Project import
diff --git a/hostap/Android.mk b/hostap/Android.mk
new file mode 100644
index 0000000..bd7a409
--- /dev/null
+++ b/hostap/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+
+ifneq ($(filter VER_0_8_X VER_2_1_DEVEL,$(WPA_SUPPLICANT_VERSION)),)
+# The order of the 2 Android.mks does matter!
+# TODO: Clean up the Android.mks, reset all the temporary variables at the
+# end of each Android.mk, so that one Android.mk doesn't depend on variables
+# set up in the other Android.mk.
+include $(LOCAL_PATH)/hostapd/Android.mk \
+        $(LOCAL_PATH)/wpa_supplicant/Android.mk
+endif
diff --git a/hostap/CONTRIBUTIONS b/hostap/CONTRIBUTIONS
new file mode 100644
index 0000000..ca09bae
--- /dev/null
+++ b/hostap/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-2015, 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/hostap/COPYING b/hostap/COPYING
new file mode 100644
index 0000000..5962e2f
--- /dev/null
+++ b/hostap/COPYING
@@ -0,0 +1,22 @@
+wpa_supplicant and hostapd
+--------------------------
+
+Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
+All Rights Reserved.
+
+
+See the README file for the current license terms.
+
+This software was previously distributed under BSD/GPL v2 dual license
+terms that allowed either of those license alternatives to be
+selected. 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. It should be noted that the BSD
+license option (the one with advertisement clause removed) is compatible
+with GPL and as such, does not prevent use of this software in projects
+that use GPL.
+
+Some of the files may still include pointers to GPL version 2 license
+terms. However, such copyright and license notifications are maintained
+only for attribution purposes and any distribution of this software
+after February 11, 2012 is no longer under the GPL v2 option.
diff --git a/hostap/LICENSE b/hostap/LICENSE
new file mode 100644
index 0000000..5962e2f
--- /dev/null
+++ b/hostap/LICENSE
@@ -0,0 +1,22 @@
+wpa_supplicant and hostapd
+--------------------------
+
+Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
+All Rights Reserved.
+
+
+See the README file for the current license terms.
+
+This software was previously distributed under BSD/GPL v2 dual license
+terms that allowed either of those license alternatives to be
+selected. 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. It should be noted that the BSD
+license option (the one with advertisement clause removed) is compatible
+with GPL and as such, does not prevent use of this software in projects
+that use GPL.
+
+Some of the files may still include pointers to GPL version 2 license
+terms. However, such copyright and license notifications are maintained
+only for attribution purposes and any distribution of this software
+after February 11, 2012 is no longer under the GPL v2 option.
diff --git a/hostap/MODULE_LICENSE_BSD b/hostap/MODULE_LICENSE_BSD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hostap/MODULE_LICENSE_BSD
diff --git a/hostap/README b/hostap/README
new file mode 100644
index 0000000..07d1d25
--- /dev/null
+++ b/hostap/README
@@ -0,0 +1,56 @@
+wpa_supplicant and hostapd
+--------------------------
+
+Copyright (c) 2002-2015, 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
+hostapd/README) for more details.
+
+Source code files were moved around in v0.6.x releases and compared to
+earlier releases, the programs are now built by first going to a
+subdirectory (wpa_supplicant or hostapd) and creating build
+configuration (.config) and running 'make' there (for Linux/BSD/cygwin
+builds).
+
+
+License
+-------
+
+This software may be distributed, used, and modified under the terms of
+BSD license:
+
+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/hostap/build_release b/hostap/build_release
new file mode 100755
index 0000000..4714553
--- /dev/null
+++ b/hostap/build_release
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+set -e
+
+if [ -z "$1" ]; then
+    echo "build_release <version>"
+    exit 1
+fi
+
+TMP=tmp.build_release
+RELDIR=`pwd`/Release
+VER=$1
+NOW=`date +%Y-%m-%d`
+
+echo "Version: $VER - $NOW"
+
+DATEw=`head -n 3 wpa_supplicant/ChangeLog | tail -n 1 | sed "s/ .*//"`
+DATEh=`head -n 3 hostapd/ChangeLog | tail -n 1 | sed "s/ .*//"`
+
+if [ "$DATEw" != "$NOW" -o "$DATEh" != "$NOW" ]; then
+    echo "NOTE! Date mismatch in ChangeLog: wpa_supplicant $DATEw hostapd $DATEh != $NOW"
+fi
+
+if [ -r $TMP ]; then
+    echo "Temporary directory '$TMP' exists. Remove it before running this."
+    exit 1
+fi
+
+mkdir $TMP
+mkdir -p $RELDIR
+
+git archive --format=tar --prefix=wpa-$VER/ HEAD \
+	README COPYING CONTRIBUTIONS patches src wpa_supplicant hostapd hs20 |
+	gzip > $RELDIR/wpa-$VER.tar.gz
+git archive --format=tar --prefix=hostapd-$VER/ HEAD \
+	README COPYING CONTRIBUTIONS patches src hostapd |
+	gzip > $RELDIR/hostapd-$VER.tar.gz
+git archive --format=tar --prefix=wpa_supplicant-$VER/ HEAD \
+	README COPYING CONTRIBUTIONS patches src wpa_supplicant hs20/client |
+	tar --directory=$TMP -xf -
+
+cd $TMP
+make -C wpa_supplicant-$VER/wpa_supplicant/doc/docbook man
+rm -f wpa_supplicant-$VER/wpa_supplicant/doc/docbook/manpage.{links,refs}
+tar czf $RELDIR/wpa_supplicant-$VER.tar.gz wpa_supplicant-$VER
+cd ..
+rm -r $TMP
diff --git a/hostap/doc/Makefile b/hostap/doc/Makefile
new file mode 100644
index 0000000..62af04a
--- /dev/null
+++ b/hostap/doc/Makefile
@@ -0,0 +1,42 @@
+all: docs
+
+%.eps: %.fig
+	fig2dev -L eps $*.fig $*.eps
+
+%.png: %.fig
+	fig2dev -L png -m 3 $*.fig | pngtopnm | pnmscale 0.4 | pnmtopng \
+		> $*.png
+
+%.png: %.dot
+	dot $*.dot -Tpng -o $*.png
+
+%.eps: %.dot
+	dot $*.dot -Tps -o $*.eps
+
+_wpa_supplicant.png: wpa_supplicant.png
+	cp $< $@
+
+_wpa_supplicant.eps: wpa_supplicant.eps
+	cp $< $@
+
+docs-pics: wpa_supplicant.png wpa_supplicant.eps hostapd.png hostapd.eps p2p_sm.png p2p_sm.eps p2p_arch.png p2p_arch.eps p2p_arch2.png p2p_arch2.eps _wpa_supplicant.png _wpa_supplicant.eps
+
+docs: docs-pics
+	(cd ..; doxygen doc/doxygen.conf; cd doc)
+	$(MAKE) -C latex
+	cp latex/refman.pdf wpa_supplicant-devel.pdf
+
+html: docs-pics
+	(cd ..; doxygen doc/doxygen.conf; cd doc)
+
+clean:
+	rm -f *~
+	rm -f wpa_supplicant.eps wpa_supplicant.png
+	rm -f _wpa_supplicant.png _wpa_supplicant.eps
+	rm -f hostapd.eps hostapd.png
+	rm -f p2p_sm.eps p2p_sm.png
+	rm -f p2p_arch.eps p2p_arch.png
+	rm -f p2p_arch2.eps p2p_arch2.png
+	rm -f doxygen.warnings
+	rm -rf html latex
+	rm -f wpa_supplicant-devel.pdf
diff --git a/hostap/doc/code_structure.doxygen b/hostap/doc/code_structure.doxygen
new file mode 100644
index 0000000..454f179
--- /dev/null
+++ b/hostap/doc/code_structure.doxygen
@@ -0,0 +1,315 @@
+/**
+\page code_structure Structure of the source code
+
+[ \ref _wpa_supplicant_core "wpa_supplicant core functionality" |
+\ref generic_helper_func "Generic helper functions" |
+\ref crypto_func "Cryptographic functions" |
+\ref tls_func "TLS library" |
+\ref configuration "Configuration" |
+\ref ctrl_iface "Control interface" |
+\ref wpa_code "WPA supplicant" |
+\ref eap_peer "EAP peer" |
+\ref eapol_supp "EAPOL supplicant" |
+\ref win_port "Windows port" |
+\ref test_programs "Test programs" ]
+
+wpa_supplicant implementation is divided into number of independent
+modules. Core code includes functionality for controlling the network
+selection, association, and configuration. Independent modules include
+WPA code (key handshake, PMKSA caching, pre-authentication), EAPOL
+state machine, and EAP state machine and methods. In addition, there
+are number of separate files for generic helper functions.
+
+Both WPA and EAPOL/EAP state machines can be used separately in other
+programs than wpa_supplicant. As an example, the included test
+programs eapol_test and preauth_test are using these modules.
+
+\ref driver_wrapper "Driver interface API" is defined in \ref driver.h and
+all hardware/driver dependent functionality is implemented in
+driver_*.c.
+
+
+\section _wpa_supplicant_core wpa_supplicant core functionality
+
+\ref wpa_supplicant.c
+	Program initialization, main control loop
+
+\ref wpa_supplicant/main.c
+	main() for UNIX-like operating systems and MinGW (Windows); this
+	uses command line arguments to configure wpa_supplicant
+
+\ref events.c
+	Driver event processing; \ref wpa_supplicant_event() and related functions
+
+\ref wpa_supplicant_i.h
+	Internal definitions for wpa_supplicant core; should not be
+	included into independent modules
+
+
+\section generic_helper_func Generic helper functions
+
+wpa_supplicant uses generic helper functions some of which are shared
+with with hostapd. The following C files are currently used:
+
+\ref eloop.c and \ref eloop.h
+	Event loop (select() loop with registerable timeouts, socket read
+	callbacks, and signal callbacks)
+
+\ref common.c and \ref common.h
+	Common helper functions
+
+\ref defs.h
+	Definitions shared by multiple files
+
+\ref l2_packet.h, \ref l2_packet_linux.c, and \ref l2_packet_pcap.c
+	Layer 2 (link) access wrapper (includes native Linux implementation
+	and wrappers for libdnet/libpcap). A new l2_packet implementation
+	may need to be added when porting to new operating systems that are
+	not supported by libdnet/libpcap. Makefile can be used to select which
+	l2_packet implementation is included. \ref l2_packet_linux.c uses Linux
+	packet sockets and \ref l2_packet_pcap.c has a more portable version using
+	libpcap and libdnet.
+
+\ref pcsc_funcs.c and \ref pcsc_funcs.h
+	Wrapper for PC/SC lite SIM and smart card readers
+
+\ref priv_netlink.h
+	Private version of netlink definitions from Linux kernel header files;
+	this could be replaced with C library header file once suitable
+	version becomes commonly available
+
+\ref version.h
+	Version number definitions
+
+
+\section crypto_func Cryptographic functions
+
+\ref md5.c and \ref md5.h
+	MD5 (replaced with a crypto library if TLS support is included)
+	HMAC-MD5 (keyed checksum for message authenticity validation)
+
+\ref rc4.c and \ref rc4.h
+	RC4 (broadcast/default key encryption)
+
+\ref sha1.c and \ref sha1.h
+	SHA-1 (replaced with a crypto library if TLS support is included)
+	HMAC-SHA-1 (keyed checksum for message authenticity validation)
+	PRF-SHA-1 (pseudorandom (key/nonce generation) function)
+	PBKDF2-SHA-1 (ASCII passphrase to shared secret)
+	T-PRF (for EAP-FAST)
+	TLS-PRF (RFC 2246)
+
+\ref sha256.c and \ref sha256.h
+	SHA-256 (replaced with a crypto library if TLS support is included)
+
+\ref aes-wrap.c, \ref aes_wrap.h, \ref aes.c
+	AES (replaced with a crypto library if TLS support is included),
+	AES Key Wrap Algorithm with 128-bit KEK, RFC3394 (broadcast/default
+	key encryption),
+	One-Key CBC MAC (OMAC1) hash with AES-128,
+	AES-128 CTR mode encryption,
+	AES-128 EAX mode encryption/decryption,
+	AES-128 CBC
+
+\ref crypto.h
+	Definition of crypto library wrapper
+
+\ref crypto_openssl.c
+	Wrapper functions for libcrypto (OpenSSL)
+
+\ref crypto_internal.c
+	Wrapper functions for internal crypto implementation
+
+\ref crypto_gnutls.c
+	Wrapper functions for libgcrypt (used by GnuTLS)
+
+\ref ms_funcs.c and \ref ms_funcs.h
+	Helper functions for MSCHAPV2 and LEAP
+
+\ref tls.h
+	Definition of TLS library wrapper
+
+\ref tls_none.c
+	Dummy implementation of TLS library wrapper for cases where TLS
+	functionality is not included.
+
+\ref tls_openssl.c
+	TLS library wrapper for openssl
+
+\ref tls_internal.c
+	TLS library for internal TLS implementation
+
+\ref tls_gnutls.c
+	TLS library wrapper for GnuTLS
+
+
+\section tls_func TLS library
+
+\ref asn1.c and \ref asn1.h
+	ASN.1 DER parsing
+
+\ref bignum.c and \ref bignum.h
+	Big number math
+
+\ref rsa.c and \ref rsa.h
+	RSA
+
+\ref x509v3.c and \ref x509v3.h
+	X.509v3 certificate parsing and processing
+
+\ref tlsv1_client.c, \ref tlsv1_client.h
+	TLSv1 client (RFC 2246)
+
+\ref tlsv1_client_i.h
+	Internal structures for TLSv1 client
+
+\ref tlsv1_client_read.c
+	TLSv1 client: read handshake messages
+
+\ref tlsv1_client_write.c
+	TLSv1 client: write handshake messages
+
+\ref tlsv1_common.c and \ref tlsv1_common.h
+	Common TLSv1 routines and definitions
+
+\ref tlsv1_cred.c and \ref tlsv1_cred.h
+	TLSv1 credentials
+
+\ref tlsv1_record.c and \ref tlsv1_record.h
+	TLSv1 record protocol
+
+
+\section configuration Configuration
+
+\ref config_ssid.h
+	Definition of per network configuration items
+
+\ref config.h
+	Definition of the wpa_supplicant configuration
+
+\ref config.c
+	Configuration parser and common functions
+
+\ref wpa_supplicant/config_file.c
+	Configuration backend for text files (e.g., wpa_supplicant.conf)
+
+\ref config_winreg.c
+	Configuration backend for Windows registry
+
+
+\section ctrl_iface Control interface
+
+wpa_supplicant has a \ref ctrl_iface_page "control interface"
+that can be used to get status
+information and manage operations from external programs. An example
+command line interface (wpa_cli) and GUI (wpa_gui) for this interface
+are included in the wpa_supplicant distribution.
+
+\ref wpa_supplicant/ctrl_iface.c and \ref wpa_supplicant/ctrl_iface.h
+	wpa_supplicant-side of the control interface
+
+\ref ctrl_iface_unix.c
+	UNIX domain sockets -based control interface backend
+
+\ref ctrl_iface_udp.c
+	UDP sockets -based control interface backend
+
+\ref ctrl_iface_named_pipe.c
+	Windows named pipes -based control interface backend
+
+\ref wpa_ctrl.c and \ref wpa_ctrl.h
+	Library functions for external programs to provide access to the
+	wpa_supplicant control interface
+
+\ref wpa_cli.c
+	Example program for using wpa_supplicant control interface
+
+
+\section wpa_code WPA supplicant
+
+\ref wpa.c and \ref wpa.h
+	WPA state machine and 4-Way/Group Key Handshake processing
+
+\ref preauth.c and \ref preauth.h
+	PMKSA caching and pre-authentication (RSN/WPA2)
+
+\ref wpa_i.h
+	Internal definitions for WPA code; not to be included to other modules.
+
+\section eap_peer EAP peer
+
+\ref eap_peer_module "EAP peer implementation" is a separate module that
+can be used by other programs than just wpa_supplicant.
+
+\ref eap.c and \ref eap.h
+	EAP state machine and method interface
+
+\ref eap_defs.h
+	Common EAP definitions
+
+\ref eap_i.h
+	Internal definitions for EAP state machine and EAP methods; not to be
+	included in other modules
+
+\ref eap_sim_common.c and \ref eap_sim_common.h
+	Common code for EAP-SIM and EAP-AKA
+
+\ref eap_tls_common.c and \ref eap_tls_common.h
+	Common code for EAP-PEAP, EAP-TTLS, and EAP-FAST
+
+\ref eap_ttls.c and \ref eap_ttls.h
+	EAP-TTLS
+
+\ref eap_pax.c, \ref eap_pax_common.h, \ref eap_pax_common.c
+	EAP-PAX
+
+\ref eap_psk.c, \ref eap_psk_common.h, \ref eap_psk_common.c
+	EAP-PSK (note: this is not needed for WPA-PSK)
+
+\ref eap_sake.c, \ref eap_sake_common.h, \ref eap_sake_common.c
+	EAP-SAKE
+
+\ref eap_gpsk.c, \ref eap_gpsk_common.h, \ref eap_gpsk_common.c
+	EAP-GPSK
+
+\ref eap_aka.c, \ref eap_fast.c, \ref eap_gtc.c, \ref eap_leap.c,
+\ref eap_md5.c, \ref eap_mschapv2.c, \ref eap_otp.c, \ref eap_peap.c,
+\ref eap_sim.c, \ref eap_tls.c
+	Other EAP method implementations
+
+
+\section eapol_supp EAPOL supplicant
+
+\ref eapol_supp_sm.c and \ref eapol_supp_sm.h
+	EAPOL supplicant state machine and IEEE 802.1X processing
+
+
+\section win_port Windows port
+
+\ref ndis_events.c
+	Code for receiving NdisMIndicateStatus() events and delivering them to
+	wpa_supplicant \ref driver_ndis.c in more easier to use form
+
+\ref win_if_list.c
+	External program for listing current network interface
+
+
+\section test_programs Test programs
+
+\ref radius_client.c and \ref radius_client.h
+	RADIUS authentication client implementation for eapol_test
+
+\ref radius.c and \ref radius.h
+	RADIUS message processing for eapol_test
+
+\ref eapol_test.c
+	Standalone EAP testing tool with integrated RADIUS authentication
+	client
+
+\ref preauth_test.c
+	Standalone RSN pre-authentication tool
+
+\ref wpa_passphrase.c
+	WPA ASCII passphrase to PSK conversion
+
+*/
diff --git a/hostap/doc/ctrl_iface.doxygen b/hostap/doc/ctrl_iface.doxygen
new file mode 100644
index 0000000..be8916f
--- /dev/null
+++ b/hostap/doc/ctrl_iface.doxygen
@@ -0,0 +1,1054 @@
+/**
+\page ctrl_iface_page wpa_supplicant control interface
+
+wpa_supplicant implements a control interface that can be used by
+external programs to control the operations of the wpa_supplicant
+daemon and to get status information and event notifications. There is
+a small C library, in a form of a single C file, \ref wpa_ctrl.c, that
+provides helper functions to facilitate the use of the control
+interface. External programs can link this file into them and then use
+the library functions documented in \ref wpa_ctrl.h to interact with
+wpa_supplicant. This library can also be used with C++. \ref wpa_cli.c and
+wpa_gui are example programs using this library.
+
+There are multiple mechanisms for inter-process communication. For
+example, Linux version of wpa_supplicant is using UNIX domain sockets
+for the control interface and Windows version UDP sockets. The use of
+the functions defined in \ref wpa_ctrl.h can be used to hide the details of
+the used IPC from external programs.
+
+
+\section using_ctrl_iface Using the control interface
+
+External programs, e.g., a GUI or a configuration utility, that need to
+communicate with wpa_supplicant should link in \ref wpa_ctrl.c. This
+allows them to use helper functions to open connection to the control
+interface with \ref wpa_ctrl_open() and to send commands with
+\ref wpa_ctrl_request().
+
+wpa_supplicant uses the control interface for two types of communication:
+commands and unsolicited event messages. Commands are a pair of
+messages, a request from the external program and a response from
+wpa_supplicant. These can be executed using \ref wpa_ctrl_request().
+Unsolicited event messages are sent by wpa_supplicant to the control
+interface connection without specific request from the external program
+for receiving each message. However, the external program needs to
+attach to the control interface with \ref wpa_ctrl_attach() to receive these
+unsolicited messages.
+
+If the control interface connection is used both for commands and
+unsolicited event messages, there is potential for receiving an
+unsolicited message between the command request and response.
+\ref wpa_ctrl_request() caller will need to supply a callback, msg_cb,
+for processing these messages. Often it is easier to open two
+control interface connections by calling \ref wpa_ctrl_open() twice and
+then use one of the connections for commands and the other one for
+unsolicited messages. This way command request/response pairs will
+not be broken by unsolicited messages. wpa_cli is an example of how
+to use only one connection for both purposes and wpa_gui demonstrates
+how to use two separate connections.
+
+Once the control interface connection is not needed anymore, it should
+be closed by calling \ref wpa_ctrl_close(). If the connection was used for
+unsolicited event messages, it should be first detached by calling
+\ref wpa_ctrl_detach().
+
+
+\section ctrl_iface_cmds Control interface commands
+
+Following commands can be used with \ref wpa_ctrl_request():
+
+\subsection ctrl_iface_PING PING
+
+This command can be used to test whether wpa_supplicant is replying
+to the control interface commands. The expected reply is \c PONG if the
+connection is open and wpa_supplicant is processing commands.
+
+
+\subsection ctrl_iface_MIB MIB
+
+Request a list of MIB variables (dot1x, dot11). The output is a text
+block with each line in \c variable=value format. For example:
+
+\verbatim
+dot11RSNAOptionImplemented=TRUE
+dot11RSNAPreauthenticationImplemented=TRUE
+dot11RSNAEnabled=FALSE
+dot11RSNAPreauthenticationEnabled=FALSE
+dot11RSNAConfigVersion=1
+dot11RSNAConfigPairwiseKeysSupported=5
+dot11RSNAConfigGroupCipherSize=128
+dot11RSNAConfigPMKLifetime=43200
+dot11RSNAConfigPMKReauthThreshold=70
+dot11RSNAConfigNumberOfPTKSAReplayCounters=1
+dot11RSNAConfigSATimeout=60
+dot11RSNAAuthenticationSuiteSelected=00-50-f2-2
+dot11RSNAPairwiseCipherSelected=00-50-f2-4
+dot11RSNAGroupCipherSelected=00-50-f2-4
+dot11RSNAPMKIDUsed=
+dot11RSNAAuthenticationSuiteRequested=00-50-f2-2
+dot11RSNAPairwiseCipherRequested=00-50-f2-4
+dot11RSNAGroupCipherRequested=00-50-f2-4
+dot11RSNAConfigNumberOfGTKSAReplayCounters=0
+dot11RSNA4WayHandshakeFailures=0
+dot1xSuppPaeState=5
+dot1xSuppHeldPeriod=60
+dot1xSuppAuthPeriod=30
+dot1xSuppStartPeriod=30
+dot1xSuppMaxStart=3
+dot1xSuppSuppControlledPortStatus=Authorized
+dot1xSuppBackendPaeState=2
+dot1xSuppEapolFramesRx=0
+dot1xSuppEapolFramesTx=440
+dot1xSuppEapolStartFramesTx=2
+dot1xSuppEapolLogoffFramesTx=0
+dot1xSuppEapolRespFramesTx=0
+dot1xSuppEapolReqIdFramesRx=0
+dot1xSuppEapolReqFramesRx=0
+dot1xSuppInvalidEapolFramesRx=0
+dot1xSuppEapLengthErrorFramesRx=0
+dot1xSuppLastEapolFrameVersion=0
+dot1xSuppLastEapolFrameSource=00:00:00:00:00:00
+\endverbatim
+
+
+\subsection ctrl_iface_STATUS STATUS
+
+Request current WPA/EAPOL/EAP status information. The output is a text
+block with each line in \c variable=value format. For example:
+
+\verbatim
+bssid=02:00:01:02:03:04
+ssid=test network
+pairwise_cipher=CCMP
+group_cipher=CCMP
+key_mgmt=WPA-PSK
+wpa_state=COMPLETED
+ip_address=192.168.1.21
+Supplicant PAE state=AUTHENTICATED
+suppPortStatus=Authorized
+EAP state=SUCCESS
+\endverbatim
+
+
+\subsection ctrl_iface_STATUS-VERBOSE STATUS-VERBOSE
+
+Same as STATUS, but with more verbosity (i.e., more \c variable=value pairs).
+
+\verbatim
+bssid=02:00:01:02:03:04
+ssid=test network
+id=0
+pairwise_cipher=CCMP
+group_cipher=CCMP
+key_mgmt=WPA-PSK
+wpa_state=COMPLETED
+ip_address=192.168.1.21
+Supplicant PAE state=AUTHENTICATED
+suppPortStatus=Authorized
+heldPeriod=60
+authPeriod=30
+startPeriod=30
+maxStart=3
+portControl=Auto
+Supplicant Backend state=IDLE
+EAP state=SUCCESS
+reqMethod=0
+methodState=NONE
+decision=COND_SUCC
+ClientTimeout=60
+\endverbatim
+
+
+\subsection ctrl_iface_PMKSA PMKSA
+
+Show PMKSA cache
+
+\verbatim
+Index / AA / PMKID / expiration (in seconds) / opportunistic
+1 / 02:00:01:02:03:04 / 000102030405060708090a0b0c0d0e0f / 41362 / 0
+2 / 02:00:01:33:55:77 / 928389281928383b34afb34ba4212345 / 362 / 1
+\endverbatim
+
+
+\subsection ctrl_iface_SET SET <variable> <value>
+
+Set variables:
+- EAPOL::heldPeriod
+- EAPOL::authPeriod
+- EAPOL::startPeriod
+- EAPOL::maxStart
+- dot11RSNAConfigPMKLifetime
+- dot11RSNAConfigPMKReauthThreshold
+- dot11RSNAConfigSATimeout
+
+Example command:
+\verbatim
+SET EAPOL::heldPeriod 45
+\endverbatim
+
+
+\subsection ctrl_iface_LOGON LOGON
+
+IEEE 802.1X EAPOL state machine logon.
+
+
+\subsection ctrl_iface_LOGOFF LOGOFF
+
+IEEE 802.1X EAPOL state machine logoff.
+
+
+\subsection ctrl_iface_REASSOCIATE REASSOCIATE
+
+Force reassociation.
+
+
+\subsection ctrl_iface_RECONNECT RECONNECT
+
+Connect if disconnected (i.e., like \c REASSOCIATE, but only connect
+if in disconnected state).
+
+
+\subsection ctrl_iface_PREAUTH PREAUTH <BSSID>
+
+Start pre-authentication with the given BSSID.
+
+
+\subsection ctrl_iface_ATTACH ATTACH
+
+Attach the connection as a monitor for unsolicited events. This can
+be done with \ref wpa_ctrl_attach().
+
+
+\subsection ctrl_iface_DETACH DETACH
+
+Detach the connection as a monitor for unsolicited events. This can
+be done with \ref wpa_ctrl_detach().
+
+
+\subsection ctrl_iface_LEVEL LEVEL <debug level>
+
+Change debug level.
+
+
+\subsection ctrl_iface_RECONFIGURE RECONFIGURE
+
+Force wpa_supplicant to re-read its configuration data.
+
+
+\subsection ctrl_iface_TERMINATE TERMINATE
+
+Terminate wpa_supplicant process.
+
+
+\subsection ctrl_iface_BSSID BSSID <network id> <BSSID>
+
+Set preferred BSSID for a network. Network id can be received from the
+\c LIST_NETWORKS command output.
+
+
+\subsection ctrl_iface_LIST_NETWORKS LIST_NETWORKS
+
+List configured networks.
+
+\verbatim
+network id / ssid / bssid / flags
+0	example network	any	[CURRENT]
+\endverbatim
+
+(note: fields are separated with tabs)
+
+
+\subsection ctrl_iface_DISCONNECT DISCONNECT
+
+Disconnect and wait for \c REASSOCIATE or \c RECONNECT command before
+connecting.
+
+
+\subsection ctrl_iface_SCAN SCAN
+
+Request a new BSS scan.
+
+
+\subsection ctrl_iface_SCAN_RESULTS SCAN_RESULTS
+
+Get the latest scan results.
+
+\verbatim
+bssid / frequency / signal level / flags / ssid
+00:09:5b:95:e0:4e	2412	208	[WPA-PSK-CCMP]	jkm private
+02:55:24:33:77:a3	2462	187	[WPA-PSK-TKIP]	testing
+00:09:5b:95:e0:4f	2412	209		jkm guest
+\endverbatim
+
+(note: fields are separated with tabs)
+
+
+\subsection ctrl_iface_BSS BSS
+
+Get detailed per-BSS scan results. \c BSS command can be used to
+iterate through scan results one BSS at a time and to fetch all
+information from the found BSSes. This provides access to the same
+data that is available through \c SCAN_RESULTS but in a way that
+avoids problems with large number of scan results not fitting in the
+ctrl_iface messages.
+
+There are two options for selecting the BSS with the \c BSS command:
+"BSS <idx>" requests information for the BSS identified by the index
+(0 .. size-1) in the scan results table and "BSS <BSSID>" requests
+information for the given BSS (based on BSSID in 00:01:02:03:04:05
+format).
+
+BSS information is presented in following format. Please note that new
+fields may be added to this field=value data, so the ctrl_iface user
+should be prepared to ignore values it does not understand.
+
+\verbatim
+bssid=00:09:5b:95:e0:4e
+freq=2412
+beacon_int=0
+capabilities=0x0011
+qual=51
+noise=161
+level=212
+tsf=0000000000000000
+ie=000b6a6b6d2070726976617465010180dd180050f20101000050f20401000050f20401000050f2020000
+ssid=jkm private
+\endverbatim
+
+
+
+\subsection ctrl_iface_SELECT_NETWORK SELECT_NETWORK <network id>
+
+Select a network (disable others). Network id can be received from the
+\c LIST_NETWORKS command output.
+
+
+\subsection ctrl_iface_ENABLE_NETWORK ENABLE_NETWORK <network id>
+
+Enable a network. Network id can be received from the
+\c LIST_NETWORKS command output. Special network id \c all can be
+used to enable all network.
+
+
+\subsection ctrl_iface_DISABLE_NETWORK DISABLE_NETWORK <network id>
+
+Disable a network. Network id can be received from the
+\c LIST_NETWORKS command output. Special network id \c all can be
+used to disable all network.
+
+
+\subsection ctrl_iface_ADD_NETWORK ADD_NETWORK
+
+Add a new network. This command creates a new network with empty
+configuration. The new network is disabled and once it has been
+configured it can be enabled with \c ENABLE_NETWORK command. \c ADD_NETWORK
+returns the network id of the new network or FAIL on failure.
+
+
+\subsection ctrl_iface_REMOVE_NETWORK REMOVE_NETWORK <network id>
+
+Remove a network. Network id can be received from the
+\c LIST_NETWORKS command output. Special network id \c all can be
+used to remove all network.
+
+
+\subsection ctrl_iface_SET_NETWORK SET_NETWORK <network id> <variable> <value>
+
+Set network variables. Network id can be received from the
+\c LIST_NETWORKS command output.
+
+This command uses the same variables and data formats as the
+configuration file. See example wpa_supplicant.conf for more details.
+
+- ssid (network name, SSID)
+- psk (WPA passphrase or pre-shared key)
+- key_mgmt (key management protocol)
+- identity (EAP identity)
+- password (EAP password)
+- ...
+
+
+\subsection ctrl_iface_GET_NETWORK GET_NETWORK <network id> <variable>
+
+Get network variables. Network id can be received from the
+\c LIST_NETWORKS command output.
+
+
+\subsection ctrl_iface_SAVE_CONFIG SAVE_CONFIG
+
+Save the current configuration.
+
+
+\subsection ctrl_iface_P2P_FIND P2P_FIND
+
+Start P2P device discovery. Optional parameter can be used to specify
+the duration for the discovery in seconds (e.g., "P2P_FIND 5"). If the
+duration is not specified, discovery will be started for indefinite
+time, i.e., until it is terminated by P2P_STOP_FIND or P2P_CONNECT (to
+start group formation with a discovered peer).
+
+The default search type is to first run a full scan of all channels
+and then continue scanning only social channels (1, 6, 11). This
+behavior can be changed by specifying a different search type: social
+(e.g., "P2P_FIND 5 type=social") will skip the initial full scan and
+only search social channels; progressive (e.g., "P2P_FIND
+type=progressive") starts with a full scan and then searches
+progressively through all channels one channel at the time with the
+social channel scans. Progressive device discovery can be used to find
+new groups (and groups that were not found during the initial scan,
+e.g., due to the GO being asleep) over time without adding
+considerable extra delay for every Search state round.
+
+
+\subsection ctrl_iface_P2P_STOP_FIND P2P_STOP_FIND
+
+Stop ongoing P2P device discovery or other operation (connect, listen
+mode).
+
+
+\subsection ctrl_iface_P2P_CONNECT P2P_CONNECT
+
+Start P2P group formation with a discovered P2P peer. This includes
+group owner negotiation, group interface setup, provisioning, and
+establishing data connection.
+
+P2P_CONNECT <peer device address> <pbc|pin|PIN#>
+[label|display|keypad] [persistent] [join|auth] [go_intent=<0..15>]
+
+Start P2P group formation with a discovered P2P peer. This includes
+optional group owner negotiation, group interface setup, provisioning,
+and establishing data connection.
+
+The <pbc|pin|PIN#> parameter specifies the WPS provisioning
+method. "pbc" string starts pushbutton method, "pin" string start PIN
+method using an automatically generated PIN (which will be returned as
+the command return code), PIN# means that a pre-selected PIN can be
+used (e.g., 12345670). [label|display|keypad] is used with PIN method
+to specify which PIN is used (label=PIN from local label,
+display=dynamically generated random PIN from local display,
+keypad=PIN entered from peer device label or display). "persistent"
+parameter can be used to request a persistent group to be formed.
+
+"join" indicates that this is a command to join an existing group as a
+client. It skips the GO Negotiation part.
+
+"auth" indicates that the WPS parameters are authorized for the peer
+device without actually starting GO Negotiation (i.e., the peer is
+expected to initiate GO Negotiation). This is mainly for testing
+purposes.
+
+The optional "go_intent" parameter can be used to override the default
+GO Intent value.
+
+
+\subsection ctrl_iface_P2P_LISTEN P2P_LISTEN
+
+Start Listen-only state. Optional parameter can be used to specify the
+duration for the Listen operation in seconds. This command may not
+be of that much use during normal operations and is mainly designed
+for testing. It can also be used to keep the device discoverable
+without having to maintain a group.
+
+
+\subsection ctrl_iface_P2P_GROUP_REMOVE P2P_GROUP_REMOVE
+
+Terminate a P2P group. If a new virtual network interface was used for
+the group, it will also be removed. The network interface name of the
+group interface is used as a parameter for this command.
+
+
+\subsection ctrl_iface_P2P_GROUP_ADD P2P_GROUP_ADD
+
+Set up a P2P group owner manually (i.e., without group owner
+negotiation with a specific peer). This is also known as autonomous
+GO. Optional persistent=<network id> can be used to specify restart of
+a persistent group.
+
+
+\subsection ctrl_iface_P2P_PROV_DISC P2P_PROV_DISC
+
+Send P2P provision discovery request to the specified peer. The
+parameters for this command are the P2P device address of the peer and
+the desired configuration method. For example, "P2P_PROV_DISC
+02:01:02:03:04:05 display" would request the peer to display a PIN for
+us and "P2P_PROV_DISC 02:01:02:03:04:05 keypad" would request the peer
+to enter a PIN that we display.
+
+
+\subsection ctrl_iface_P2P_GET_PASSPHRASE P2P_GET_PASSPHRASE
+
+Get the passphrase for a group (only available when acting as a GO).
+
+
+\subsection ctrl_iface_P2P_SERV_DISC_REQ P2P_SERV_DISC_REQ
+
+Schedule a P2P service discovery request. The parameters for this
+command are the device address of the peer device (or 00:00:00:00:00:00
+for wildcard query that is sent to every discovered P2P peer that
+supports service discovery) and P2P Service Query TLV(s) as hexdump.
+For example, "P2P_SERV_DISC_REQ 00:00:00:00:00:00 02000001" schedules
+a request for listing all supported service discovery protocols and
+requests this to be sent to all discovered peers. The pending requests
+are sent during device discovery (see \ref ctrl_iface_P2P_FIND).
+
+This command returns an identifier for the pending query (e.g.,
+"1f77628") that can be used to cancel the request. Directed requests
+will be automatically removed when the specified peer has replied to
+it.
+
+
+\subsection ctrl_iface_P2P_SERV_DISC_CANCEL_REQ P2P_SERV_DISC_CANCEL_REQ
+
+Cancel a pending P2P service discovery request. This command takes a
+single parameter: identifier for the pending query (the value returned
+by \ref ctrl_iface_P2P_SERV_DISC_REQ), e.g.,
+"P2P_SERV_DISC_CANCEL_REQ 1f77628".
+
+
+\subsection ctrl_iface_P2P_SERV_DISC_RESP P2P_SERV_DISC_RESP
+
+Reply to a service discovery query. This command takes following
+parameters: frequency in MHz, destination address, dialog token,
+response TLV(s). The first three parameters are copied from the
+request event. For example,
+"P2P_SERV_DISC_RESP 2437 02:40:61:c2:f3:b7 1 0300000101".
+
+
+\subsection ctrl_iface_P2P_SERVICE_UPDATE P2P_SERVICE_UPDATE
+
+Indicate that local services have changed. This is used to increment
+the P2P service indicator value so that peers know when previously
+cached information may have changed.
+
+
+\subsection ctrl_iface_P2P_SERV_DISC_EXTERNAL P2P_SERV_DISC_EXTERNAL
+
+Configure external processing of P2P service requests: 0 (default) =
+no external processing of requests (i.e., internal code will reject
+each request), 1 = external processing of requests (external program
+is responsible for replying to service discovery requests with
+\ref ctrl_iface_P2P_SERV_DISC_RESP).
+
+
+\subsection ctrl_iface_P2P_REJECT P2P_REJECT
+
+Reject connection attempt from a peer (specified with a device
+address). This is a mechanism to reject a pending GO Negotiation with
+a peer and request to automatically block any further connection or
+discovery of the peer.
+
+
+\subsection ctrl_iface_P2P_INVITE P2P_INVITE
+
+Invite a peer to join a group or to (re)start a persistent group.
+
+
+\subsection ctrl_iface_P2P_PEER P2P_PEER
+
+Fetch information about a discovered peer. This command takes in an
+argument specifying which peer to select: P2P Device Address of the
+peer, "FIRST" to indicate the first peer in the list, or "NEXT-<P2P
+Device Address>" to indicate the entry following the specified peer
+(to allow for iterating through the list).
+
+
+\subsection ctrl_iface_P2P_EXT_LISTEN P2P_EXT_LISTEN
+
+Enable/disable extended listen timing. Without parameters, this
+command disables extended listen timing. When enabling the feature,
+two parameters are used: availibility period and availability interval
+(both in milliseconds and with range of 1-65535).
+
+
+\section ctrl_iface_interactive Interactive requests
+
+If wpa_supplicant needs additional information during authentication
+(e.g., password), it will use a specific prefix, \c CTRL-REQ-
+(\a WPA_CTRL_REQ macro) in an unsolicited event message. An external
+program, e.g., a GUI, can provide such information by using
+\c CTRL-RSP- (\a WPA_CTRL_RSP macro) prefix in a command with matching
+field name.
+
+The following fields can be requested in this way from the user:
+- IDENTITY (EAP identity/user name)
+- PASSWORD (EAP password)
+- NEW_PASSWORD (New password if the server is requesting password change)
+- PIN (PIN code for accessing a SIM or smartcard)
+- OTP (one-time password; like password, but the value is used only once)
+- PASSPHRASE (passphrase for a private key file)
+
+\verbatim
+CTRL-REQ-<field name>-<network id>-<human readable text>
+CTRL-RSP-<field name>-<network id>-<value>
+\endverbatim
+
+For example, request from wpa_supplicant:
+\verbatim
+CTRL-REQ-PASSWORD-1-Password needed for SSID test-network
+\endverbatim
+
+And a matching reply from the GUI:
+\verbatim
+CTRL-RSP-PASSWORD-1-secret
+\endverbatim
+
+
+\subsection ctrl_iface_GET_CAPABILITY GET_CAPABILITY <option> [strict]
+
+Get list of supported functionality (eap, pairwise, group,
+proto). Supported functionality is shown as space separate lists of
+values used in the same format as in wpa_supplicant configuration.
+If optional argument, 'strict', is added, only the values that the
+driver claims to explicitly support are included. Without this, all
+available capabilities are included if the driver does not provide
+a mechanism for querying capabilities.
+
+Example request/reply pairs:
+
+\verbatim
+GET_CAPABILITY eap
+AKA FAST GTC LEAP MD5 MSCHAPV2 OTP PAX PEAP PSK SIM TLS TTLS
+\endverbatim
+
+\verbatim
+GET_CAPABILITY pairwise
+CCMP TKIP NONE
+\endverbatim
+
+\verbatim
+GET_CAPABILITY pairwise strict
+\endverbatim
+
+\verbatim
+GET_CAPABILITY group
+CCMP TKIP WEP104 WEP40
+\endverbatim
+
+\verbatim
+GET_CAPABILITY key_mgmt
+WPA-PSK WPA-EAP IEEE8021X NONE
+\endverbatim
+
+\verbatim
+GET_CAPABILITY proto
+RSN WPA
+\endverbatim
+
+\verbatim
+GET_CAPABILITY auth_alg
+OPEN SHARED LEAP
+\endverbatim
+
+
+\subsection ctrl_iface_AP_SCAN AP_SCAN <ap_scan value>
+
+Change ap_scan value:
+0 = no scanning,
+1 = wpa_supplicant requests scans and uses scan results to select the AP,
+2 = wpa_supplicant does not use scanning and just requests driver to
+associate and take care of AP selection
+
+
+\subsection ctrl_iface_INTERFACES INTERFACES
+
+List configured interfaces.
+
+\verbatim
+wlan0
+eth0
+\endverbatim
+
+
+\section ctrl_iface_events Control interface events
+
+wpa_supplicant generates number messages based on events like
+connection or a completion of a task. These are available to external
+programs that attach to receive unsolicited messages over the control
+interface with \ref wpa_ctrl_attach().
+
+The event messages will be delivered over the attach control interface
+as text strings that start with the priority level of the message and
+a fixed prefix text as defined in \ref wpa_ctrl.h. After this, optional
+additional information may be included depending on the event
+message. For example, following event message is delivered when new
+scan results are available:
+
+\verbatim
+<2>CTRL-EVENT-SCAN-RESULTS
+\endverbatim
+
+Following priority levels are used:
+- 0 = MSGDUMP
+- 1 = DEBUG
+- 2 = INFO
+- 3 = WARNING
+- 4 = ERROR
+
+By default, any priority level greater than equal to 2 (INFO) are
+delivered over the attached control interface. LEVEL command can be
+used to set the level of messages which will be delivered. It should
+be noted that there are many debug messages that do not include any
+particulat prefix and are subject to change. They may be used for
+debug information, but can usually be ignored by external programs.
+
+In most cases, the external program can skip over the priority field
+in the beginning of the event message and then compare the following
+text to the event strings from \ref wpa_ctrl.h that the program is
+interested in processing.
+
+Following subsections describe the most common event notifications
+generated by wpa_supplicant.
+
+\subsection ctrl_iface_event_CTRL_REQ CTRL-REQ-
+
+WPA_CTRL_REQ: Request information from a user. See
+\ref ctrl_iface_interactive "Interactive requests" sections for more
+details.
+
+\subsection ctrl_iface_event_CONNECTED CTRL-EVENT-CONNECTED
+
+WPA_EVENT_CONNECTED: Indicate successfully completed authentication
+and that the data connection is now enabled.
+
+\subsection ctrl_iface_event_DISCONNECTED CTRL-EVENT-DISCONNECTED
+
+WPA_EVENT_DISCONNECTED: Disconnected, data connection is not available
+
+\subsection ctrl_iface_event_TERMINATING  CTRL-EVENT-TERMINATING
+
+WPA_EVENT_TERMINATING: wpa_supplicant is exiting
+
+\subsection ctrl_iface_event_PASSWORD_CHANGED CTRL-EVENT-PASSWORD-CHANGED
+
+WPA_EVENT_PASSWORD_CHANGED: Password change was completed successfully
+
+\subsection ctrl_iface_event_EAP_NOTIFICATION CTRL-EVENT-EAP-NOTIFICATION
+
+WPA_EVENT_EAP_NOTIFICATION: EAP-Request/Notification received
+
+\subsection ctrl_iface_event_EAP_STARTED CTRL-EVENT-EAP-STARTED
+
+WPA_EVENT_EAP_STARTED: EAP authentication started (EAP-Request/Identity
+received)
+
+\subsection ctrl_iface_event_EAP_METHOD CTRL-EVENT-EAP-METHOD
+
+WPA_EVENT_EAP_METHOD: EAP method selected
+
+\subsection ctrl_iface_event_EAP_SUCCESS CTRL-EVENT-EAP-SUCCESS
+
+WPA_EVENT_EAP_SUCCESS: EAP authentication completed successfully
+
+\subsection ctrl_iface_event_EAP_FAILURE CTRL-EVENT-EAP-FAILURE
+
+WPA_EVENT_EAP_FAILURE: EAP authentication failed (EAP-Failure received)
+
+\subsection ctrl_iface_event_SCAN_RESULTS CTRL-EVENT-SCAN-RESULTS
+
+WPA_EVENT_SCAN_RESULTS: New scan results available
+
+\subsection ctrl_iface_event_BSS_ADDED CTRL-EVENT-BSS-ADDED
+
+WPA_EVENT_BSS_ADDED: A new BSS entry was added. The event prefix is
+followed by the BSS entry id and BSSID.
+
+\verbatim
+CTRL-EVENT-BSS-ADDED 34 00:11:22:33:44:55
+\endverbatim
+
+\subsection ctrl_iface_event_BSS_REMOVED CTRL-EVENT-BSS-REMOVED
+
+WPA_EVENT_BSS_REMOVED: A BSS entry was removed. The event prefix is
+followed by BSS entry id and BSSID.
+
+\verbatim
+CTRL-EVENT-BSS-REMOVED 34 00:11:22:33:44:55
+\endverbatim
+
+\subsection ctrl_iface_event_WPS_OVERLAP_DETECTED WPS-OVERLAP-DETECTED
+
+WPS_EVENT_OVERLAP: WPS overlap detected in PBC mode
+
+\subsection ctrl_iface_event_WPS_AP_AVAILABLE_PBC WPS-AP-AVAILABLE-PBC
+
+WPS_EVENT_AP_AVAILABLE_PBC: Available WPS AP with active PBC found in
+scan results.
+
+\subsection ctrl_iface_event_WPS_AP_AVAILABLE_PIN WPS-AP-AVAILABLE-PIN
+
+WPS_EVENT_AP_AVAILABLE_PIN: Available WPS AP with recently selected PIN
+registrar found in scan results.
+
+\subsection ctrl_iface_event_WPS_AP_AVAILABLE WPS-AP-AVAILABLE
+
+WPS_EVENT_AP_AVAILABLE: Available WPS AP found in scan results
+
+\subsection ctrl_iface_event_WPS_CRED_RECEIVED WPS-CRED-RECEIVED
+
+WPS_EVENT_CRED_RECEIVED: A new credential received
+
+\subsection ctrl_iface_event_WPS_M2D WPS-M2D
+
+WPS_EVENT_M2D: M2D received
+
+\subsection ctrl_iface_event_WPS_FAIL
+
+WPS_EVENT_FAIL: WPS registration failed after M2/M2D
+
+\subsection ctrl_iface_event_WPS_SUCCESS WPS-SUCCESS
+
+WPS_EVENT_SUCCESS: WPS registration completed successfully
+
+\subsection ctrl_iface_event_WPS_TIMEOUT WPS-TIMEOUT
+
+WPS_EVENT_TIMEOUT: WPS enrollment attempt timed out and was terminated
+
+\subsection ctrl_iface_event_WPS_ENROLLEE_SEEN WPS-ENROLLEE-SEEN
+
+WPS_EVENT_ENROLLEE_SEEN: WPS Enrollee was detected (used in AP mode).
+The event prefix is followed by MAC addr, UUID-E, pri dev type,
+config methods, dev passwd id, request type, [dev name].
+
+\verbatim
+WPS-ENROLLEE-SEEN 02:00:00:00:01:00
+572cf82f-c957-5653-9b16-b5cfb298abf1 1-0050F204-1 0x80 4 1
+[Wireless Client]
+\endverbatim
+
+\subsection ctrl_iface_event_WPS_ER_AP_ADD WPS-ER-AP-ADD
+
+WPS_EVENT_ER_AP_ADD: WPS ER discovered an AP
+
+\verbatim
+WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002 02:11:22:33:44:55
+pri_dev_type=6-0050F204-1 wps_state=1 |Very friendly name|Company|
+Long description of the model|WAP|http://w1.fi/|http://w1.fi/hostapd/
+\endverbatim
+
+\subsection ctrl_iface_event_WPS_ER_AP_REMOVE WPS-ER-AP-REMOVE
+
+WPS_EVENT_ER_AP_REMOVE: WPS ER removed an AP entry
+
+\verbatim
+WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002
+\endverbatim
+
+\subsection ctrl_iface_event_WPS_ER_ENROLLEE_ADD WPS-ER-ENROLLEE-ADD
+
+WPS_EVENT_ER_ENROLLEE_ADD: WPS ER discovered a new Enrollee
+
+\verbatim
+WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
+02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
+pri_dev_type=1-0050F204-1
+|Wireless Client|Company|cmodel|123|12345|
+\endverbatim
+
+\subsection ctrl_iface_event_WPS_ER_ENROLLEE_REMOVE WPS-ER-ENROLLEE-REMOVE
+
+WPS_EVENT_ER_ENROLLEE_REMOVE: WPS ER removed an Enrollee entry
+
+\verbatim
+WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
+02:66:a0:ee:17:27
+\endverbatim
+
+\subsection ctrl_iface_event_WPS_PIN_NEEDED WPS-PIN-NEEDED
+
+WPS_EVENT_PIN_NEEDED: PIN is needed to complete provisioning with an
+Enrollee. This is followed by information about the Enrollee (UUID,
+MAC address, device name, manufacturer, model name, model number,
+serial number, primary device type).
+\verbatim
+WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7 02:2a:c4:18:5b:f3
+[Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
+\endverbatim
+
+\subsection ctrl_iface_event_WPS_NEW_AP_SETTINGS WPS-NEW-AP-SETTINGS
+
+WPS_EVENT_NEW_AP_SETTINGS: New AP settings were received
+
+\subsection ctrl_iface_event_WPS_REG_SUCCESS WPS-REG-SUCCESS
+
+WPS_EVENT_REG_SUCCESS: WPS provisioning was completed successfully
+(AP/Registrar)
+
+\subsection ctrl_iface_event_WPS_AP_SETUP_LOCKED WPS-AP-SETUP-LOCKED
+
+WPS_EVENT_AP_SETUP_LOCKED: AP changed into setup locked state due to
+multiple failed configuration attempts using the AP PIN.
+
+\subsection ctrl_iface_event_AP_STA_CONNECTED AP-STA-CONNECTED
+
+AP_STA_CONNECTED: A station associated with us (AP mode event). The
+event prefix is followed by the MAC address of the station.
+
+\verbatim
+AP-STA-CONNECTED 02:2a:c4:18:5b:f3
+\endverbatim
+
+\subsection ctrl_iface_event_AP_STA_DISCONNECTED AP-STA-DISCONNECTED
+
+AP_STA_DISCONNECTED: A station disassociated (AP mode event)
+
+\verbatim
+AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_DEVICE_FOUND P2P-DEVICE-FOUND
+
+P2P_EVENT_DEVICE_FOUND: Indication of a discovered P2P device with
+information about that device.
+
+\verbatim
+P2P-DEVICE-FOUND 02:b5:64:63:30:63 p2p_dev_addr=02:b5:64:63:30:63
+pri_dev_type=1-0050f204-1 name='Wireless Client' config_methods=0x84
+dev_capab=0x21 group_capab=0x0
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_GO_NEG_REQUEST P2P-GO-NEG-REQUEST
+
+P2P_EVENT_GO_NEG_REQUEST: A P2P device requested GO negotiation, but we
+were not ready to start the negotiation.
+
+\verbatim
+P2P-GO-NEG-REQUEST 02:40:61:c2:f3:b7 dev_passwd_id=4
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_GO_NEG_SUCCESS P2P-GO-NEG-SUCCESS
+
+P2P_EVENT_GO_NEG_SUCCESS: Indication of successfully complete group
+owner negotiation.
+
+\subsection ctrl_iface_event_P2P_EVENT_GO_NEG_FAILURE P2P-GO-NEG-FAILURE
+
+P2P_EVENT_GO_NEG_FAILURE: Indication of failed group owner negotiation.
+
+\subsection ctrl_iface_event_P2P_EVENT_GROUP_FORMATION_SUCCESS P2P-GROUP-FORMATION-SUCCESS
+
+P2P_EVENT_GROUP_FORMATION_SUCCESS: Indication that P2P group formation
+has been completed successfully.
+
+\subsection ctrl_iface_event_P2P_EVENT_GROUP_FORMATION_FAILURE P2P-GROUP-FORMATION-FAILURE
+
+P2P_EVENT_GROUP_FORMATION_FAILURE: Indication that P2P group formation
+failed (e.g., due to provisioning failure or timeout).
+
+\subsection ctrl_iface_event_P2P_EVENT_GROUP_STARTED P2P-GROUP-STARTED
+
+P2P_EVENT_GROUP_STARTED: Indication of a new P2P group having been
+started. Additional parameters: network interface name for the group,
+role (GO/client), SSID. The passphrase used in the group is also
+indicated here if known (on GO) or PSK (on client). If the group is a
+persistent one, a flag indicating that is included.
+
+\verbatim
+P2P-GROUP-STARTED wlan0-p2p-0 GO ssid="DIRECT-3F Testing"
+passphrase="12345678" go_dev_addr=02:40:61:c2:f3:b7 [PERSISTENT]
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_GROUP_REMOVED P2P-GROUP-REMOVED
+
+P2P_EVENT_GROUP_REMOVED: Indication of a P2P group having been removed.
+Additional parameters: network interface name for the group, role
+(GO/client).
+
+\verbatim
+P2P-GROUP-REMOVED wlan0-p2p-0 GO
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_PROV_DISC_SHOW_PIN P2P-PROV-DISC-SHOW-PIN
+
+P2P_EVENT_PROV_DISC_SHOW_PIN: Request from the peer for us to display
+a PIN that will be entered on the peer. The following parameters are
+included after the event prefix: peer_address PIN. The PIN is a
+random PIN generated for this connection. P2P_CONNECT command can be
+used to accept the request with the same PIN configured for the
+connection.
+
+\verbatim
+P2P-PROV-DISC-SHOW-PIN 02:40:61:c2:f3:b7 12345670
+p2p_dev_addr=02:40:61:c2:f3:b7 pri_dev_type=1-0050F204-1 name='Test'
+config_methods=0x188 dev_capab=0x21 group_capab=0x0
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_PROV_DISC_ENTER_PIN P2P-PROV-DISC-ENTER-PIN
+
+P2P_EVENT_PROV_DISC_ENTER_PIN: Request from the peer for us to enter a
+PIN displayed on the peer. The following parameter is included after
+the event prefix: peer address.
+
+\verbatim
+P2P-PROV-DISC-ENTER-PIN 02:40:61:c2:f3:b7 p2p_dev_addr=02:40:61:c2:f3:b7
+pri_dev_type=1-0050F204-1 name='Test' config_methods=0x188
+dev_capab=0x21 group_capab=0x0
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_PROV_DISC_PBC_REQ P2P-PROV-DISC-PBC-REQ
+
+P2P_EVENT_PROV_DISC_PBC_REQ: Request from the peer for us to connect
+using PBC. The following parameters are included after the event prefix:
+peer_address. P2P_CONNECT command can be used to accept the request.
+
+\verbatim
+P2P-PROV-DISC-PBC-REQ 02:40:61:c2:f3:b7 p2p_dev_addr=02:40:61:c2:f3:b7
+pri_dev_type=1-0050F204-1 name='Test' config_methods=0x188
+dev_capab=0x21 group_capab=0x0
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_PROV_DISC_PBC_RESP P2P-PROV-DISC-PBC-RESP
+
+P2P_EVENT_PROV_DISC_PBC_RESP: The peer accepted our provision discovery
+request to connect using PBC. The following parameters are included
+after the event prefix: peer_address. P2P_CONNECT command can be used to
+start GO Negotiation after this.
+
+\verbatim
+P2P-PROV-DISC-PBC-RESP 02:40:61:c2:f3:b7
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_SERV_DISC_REQ P2P-SERV-DISC-REQ
+
+P2P-SERV-DISC-REQ: Indicate reception of a P2P service discovery
+request. The following parameters are included after the event prefix:
+frequency in MHz, source address, dialog token, Service Update
+Indicator, Service Query TLV(s) as hexdump.
+
+\verbatim
+P2P-SERV-DISC-REQ 2412 02:40:61:c2:f3:b7 0 0 02000001
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_SERV_DISC_RESP P2P-SERV-DISC-RESP
+
+P2P-SERV-DISC-RESP: Indicate reception of a P2P service discovery
+response. The following parameters are included after the event prefix:
+source address, Service Update Indicator, Service Response TLV(s) as
+hexdump.
+
+\verbatim
+P2P-SERV-DISC-RESP 02:40:61:c2:f3:b7 0 0300000101
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_INVITATION_RECEIVED P2P-INVITATION-RECEIVED
+
+P2P-INVITATION-RECEIVED: Indicate reception of a P2P Invitation
+Request. For persistent groups, the parameter after the event prefix
+indicates which network block includes the persistent group data.
+
+\verbatim
+P2P-INVITATION-RECEIVED sa=02:40:61:c2:f3:b7 persistent=0
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_INVITATION_RESULT P2P-INVITATION-RESULT
+
+P2P-INVITATION-RESULT: Indicate result of a P2P invitation that was
+requested with \ref ctrl_iface_P2P_INVITE. The parameter
+status=<value> shows the status code returned by the peer (or -1 on
+local failure or timeout).
+
+\verbatim
+P2P-INVITATION-RESULT status=1
+\endverbatim
+
+*/
diff --git a/hostap/doc/dbus.doxygen b/hostap/doc/dbus.doxygen
new file mode 100644
index 0000000..9430632
--- /dev/null
+++ b/hostap/doc/dbus.doxygen
@@ -0,0 +1,1878 @@
+/**
+\page dbus wpa_supplicant D-Bus API
+
+This section documents the wpa_supplicant D-Bus API. Every D-Bus
+interface implemented by wpa_supplicant is described here including
+their methods, signals, and properties with arguments, returned
+values, and possible errors.
+
+Interfaces:
+- \ref dbus_main
+- \ref dbus_interface
+- \ref dbus_wps
+- \ref dbus_p2pdevice
+- \ref dbus_bss
+- \ref dbus_network
+- \ref dbus_peer
+- \ref dbus_group
+- \ref dbus_persistent_group
+
+
+\section dbus_main fi.w1.wpa_supplicant1
+
+Interface implemented by the main wpa_supplicant D-Bus object
+registered in the bus with fi.w1.wpa_supplicant1 name.
+
+\subsection dbus_main_methods Methods
+
+<ul>
+      <li>
+	<h3>CreateInterface ( a{sv} : args ) --> o : interface</h3>
+	<p>Registers a wireless interface in wpa_supplicant.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>a{sv} : args</dt>
+	  <dd>
+	    A dictionary with arguments used to add the interface to wpa_supplicant. The dictionary may contain the following entries:
+	    <table>
+	      <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th>
+	      <tr><td>Ifname</td><td>s</td><td>Name of the network interface to control, e.g., wlan0</td><td>Yes</td>
+	      <tr><td>BridgeIfname</td><td>s</td><td>Name of the bridge interface to control, e.g., br0</td><td>No</td>
+	      <tr><td>Driver</td><td>s</td><td>Driver name which the interface uses, e.g., nl80211</td><td>No</td>
+	      <tr><td>ConfigFile</td><td>s</td><td>Configuration file path</td><td>No</td>
+	    </table>
+	  </dd>
+	</dl>
+	<h4>Returns</h4>
+	<dl>
+	  <dt>o : interface</dt>
+	  <dd>A D-Bus path to object representing created interface</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.InterfaceExists</dt>
+	  <dd>wpa_supplicant already controls this interface.</dd>
+	  <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+	  <dd>Creating interface failed for an unknown reason.</dd>
+	  <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+	  <dd>Invalid entries were found in the passed argument.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>RemoveInterface ( o : interface ) --> nothing</h3>
+	<p>Deregisters a wireless interface from wpa_supplicant.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>o : interface</dt>
+	  <dd>A D-Bus path to an object representing an interface to remove returned by CreateInterface</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.InterfaceUnknown</dt>
+	  <dd>Object pointed by the path doesn't exist or doesn't represent an interface.</dd>
+	  <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+	  <dd>Removing interface failed for an unknown reason.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>GetInterface ( s : ifname ) --> o : interface</h3>
+	<p>Returns a D-Bus path to an object related to an interface which wpa_supplicant already controls.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : ifname</dt>
+	  <dd>Name of the network interface, e.g., wlan0</dd>
+	</dl>
+	<h4>Returns</h4>
+	<dl>
+	  <dt>o : interface</dt>
+	  <dd>A D-Bus path to an object representing an interface</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.InterfaceUnknown</dt>
+	  <dd>An interface with the passed name in not controlled by wpa_supplicant.</dd>
+	  <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+	  <dd>Getting an interface object path failed for an unknown reason.</dd>
+	</dl>
+      </li>
+    </ul>
+
+\subsection dbus_main_properties Properties
+
+<ul>
+      <li>
+	<h3>DebugLevel - s - (read/write)</h3>
+	<p>Global wpa_supplicant debugging level. Possible values are
+	"msgdump" (verbose debugging), "debug" (debugging),
+	"info" (informative), "warning" (warnings), and "error" (errors).</p>
+      </li>
+
+      <li>
+	<h3>DebugTimestamp - b - (read/write)</h3>
+	<p>Global wpa_supplicant debugging parameter. Determines if timestamps are shown in debug logs.</p>
+      </li>
+
+      <li>
+	<h3>DebugShowKeys - b - (read/write)</h3>
+	<p>Global wpa_supplicant debugging parameter. Determines if secrets are shown in debug logs.</p>
+      </li>
+
+      <li>
+	<h3>Interfaces - ao - (read)</h3>
+	<p>An array with paths to D-Bus objects representing controlled interfaces each.</p>
+      </li>
+
+      <li>
+	<h3>EapMethods - as - (read)</h3>
+	<p>An array with supported EAP methods names.</p>
+      </li>
+
+      <li>
+	<h3>Capabilities - as - (read)</h3>
+	<p>An array with supported capabilities (e.g., "ap", "ibss-rsn", "p2p", "interworking").</p>
+      </li>
+
+      <li>
+	<h3>WFDIEs - ay - (read/write)</h3>
+	<p>Wi-Fi Display subelements.</p>
+      </li>
+    </ul>
+
+\subsection dbus_main_signals Signals
+
+<ul>
+      <li>
+	<h3>InterfaceAdded ( o : interface, a{sv} : properties )</h3>
+	<p>A new interface was added to wpa_supplicant.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>o : interface</dt>
+	  <dd>A D-Bus path to an object representing the added interface</dd>
+	</dl>
+	<dl>
+	  <dt>a{sv} : properties</dt>
+	  <dd>A dictionary containing properties of added interface.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>InterfaceRemoved ( o : interface )</h3>
+	<p>An interface was removed from wpa_supplicant.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>o : interface</dt>
+	  <dd>A D-Bus path to an object representing the removed interface</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>PropertiesChanged ( a{sv} : properties )</h3>
+	<p>Some properties have changed.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>a{sv} : properties</dt>
+	  <dd>A dictionary with pairs of properties names which have changed and theirs new values. Possible dictionary keys are: "DebugParams"</dd>
+	</dl>
+      </li>
+    </ul>
+
+
+\section dbus_interface fi.w1.wpa_supplicant1.Interface
+
+Interface implemented by objects related to network interface added to
+wpa_supplicant, i.e., returned by
+fi.w1.wpa_supplicant1.CreateInterface.
+
+\subsection dbus_interface_methods Methods
+
+<ul>
+      <li>
+	<h3>Scan ( a{sv} : args ) --> nothing</h3>
+	<p>Triggers a scan.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>a{sv} : args</dt>
+	  <dd>
+	    A dictionary with arguments describing scan type:
+	    <table>
+	      <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th>
+	      <tr><td>Type</td><td>s</td><td>Type of the scan. Possible values: "active", "passive"</td><td>Yes</td>
+	      <tr><td>SSIDs</td><td>aay</td><td>Array of SSIDs to scan for (applies only if scan type is active)</td><td>No</td>
+	      <tr><td>IEs</td><td>aay</td><td>Information elements to used in active scan (applies only if scan type is active)</td><td>No</td>
+	      <tr><td>Channels</td><td>a(uu)</td><td>Array of frequencies to scan in form of (center, width) in MHz.</td><td>No</td>
+	      <tr><td>AllowRoam</td><td>b</td><td>TRUE (or absent) to allow a roaming decision based on the results of this scan, FALSE to prevent a roaming decision.</td><td>No</td>
+	    </table>
+	  </dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+	  <dd>Invalid entries were found in the passed argument.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>Disconnect ( ) --> nothing</h3>
+	<p>Disassociates the interface from current network.</p>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.NotConnected</dt>
+	  <dd>Interface is not connected to any network.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>AddNetwork ( a{sv} : args ) --> o : network</h3>
+	<p>Adds a new network to the interface.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>a{sv} : args</dt>
+	  <dd>A dictionary with network configuration. Dictionary entries are equivalent to entries in the "network" block in wpa_supplicant configuration file. Entry values should be appropriate type to the entry, e.g., an entry with key "frequency" should have value type int.</dd>
+	</dl>
+	<h4>Returns</h4>
+	<dl>
+	  <dt>o : network</dt>
+	  <dd>A D-Bus path to an object representing a configured network</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+	  <dd>Invalid entries were found in the passed argument.</dd>
+	  <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+	  <dd>Adding network failed for an unknown reason.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>RemoveNetwork ( o : network ) --> nothing</h3>
+	<p>Removes a configured network from the interface.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>o : network</dt>
+	  <dd>A D-Bus path to an object representing a configured network returned by fi.w1.wpa_supplicant1.Interface.AddNetwork</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.NetworkUnknown</dt>
+	  <dd>A passed path doesn't point to any network object.</dd>
+	  <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+	  <dd>A passed path doesn't point to any network object.</dd>
+	  <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+	  <dd>Removing network failed for an unknown reason.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>RemoveAllNetworks ( ) --> nothing</h3>
+	<p>Remove all configured networks from the interface.</p>
+      </li>
+
+      <li>
+	<h3>SelectNetwork ( o : network ) --> nothing</h3>
+	<p>Attempt association with a configured network.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>o : network</dt>
+	  <dd>A D-Bus path to an object representing a configured network returned by fi.w1.wpa_supplicant1.Interface.AddNetwork</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.NetworkUnknown</dt>
+	  <dd>A passed path doesn't point to any network object.</dd>
+	  <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+	  <dd>A passed path doesn't point to any network object.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>Reassociate ( ) --> nothing</h3>
+	<p>Attempt reassociation.</p>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.InterfaceDisabled</dt>
+	  <dd>The interface is disabled.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>Reattach ( ) --> nothing</h3>
+	<p>Attempt reassociation back to the current BSS.</p>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.NotConnected</dt>
+	  <dd>Interface is not connected to any network.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>Reconnect ( ) --> nothing</h3>
+	<p>Attempt reconnection and connect if in disconnected state.</p>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.InterfaceDisabled</dt>
+	  <dd>The interface is disabled.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>AddBlob ( s : name, ay : data ) --> nothing</h3>
+	<p>Adds a blob to the interface.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : name</dt>
+	  <dd>A name of a blob</dd>
+	  <dt>ay : data</dt>
+	  <dd>A blob data</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.BlobExists</dt>
+	  <dd>A blob with the specified name already exists.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>RemoveBlob ( s : name ) --> nothing</h3>
+	<p>Removes the blob from the interface.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : name</dt>
+	  <dd>A name of the blob to remove</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.BlobUnknown</dt>
+	  <dd>A blob with the specified name doesn't exist.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>GetBlob ( s : name ) --> ay : data</h3>
+	<p>Returns the blob data of a previously added blob.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : name</dt>
+	  <dd>A name of the blob</dd>
+	</dl>
+	<h4>Returns</h4>
+	<dl>
+	  <dt>ay : data</dt>
+	  <dd>A blob data</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.BlobUnknown</dt>
+	  <dd>A blob with the specified name doesn't exist.</dd>
+	</dl>
+      </li>
+      <li>
+	<h3>AutoScan ( s : arg ) --> nothing</h3>
+	<p>Set autoscan parameters for the interface.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : arg</dt>
+	  <dd>Autoscan parameter line or empty to unset autoscan.</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.NoMemory</dt>
+	  <dd>Needed memory was not possible to get allocated.</dd>
+	  <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+	  <dd>Invalid entries were found in the passed argument.</dd>
+	</dl>
+      </li>
+      <li>
+	<h3>TDLSDiscover ( s : peer_address ) --> nothing</h3>
+	<p>Initiate a TDLS discovery for a peer.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : peer_address</dt>
+	  <dd>MAC address for the peer to perform TDLS discovery.</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+	  <dd>The "peer_address" argument is not a properly formatted MAC.</dd>
+	  <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+	  <dd>Initiating the TDLS operation failed for an unknown reason.</dd>
+	</dl>
+      </li>
+      <li>
+	<h3>TDLSSetup ( s : peer_address ) --> nothing</h3>
+	<p>Setup a TDLS session for a peer.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : peer_address</dt>
+	  <dd>MAC address for the peer to perform TDLS setup.</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+	  <dd>The "peer_address" argument is not a properly formatted MAC.</dd>
+	  <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+	  <dd>Initiating the TDLS operation failed for an unknown reason.</dd>
+	</dl>
+      </li>
+      <li>
+	<h3>TDLSStatus ( s : peer_address ) --> s</h3>
+	<p>Return TDLS status with respect to a peer.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : peer_address</dt>
+	  <dd>MAC address for the peer for which status is requested.</dd>
+	</dl>
+	<h4>Returns</h4>
+	<dl>
+	  <dt>s : status</dt>
+	  <dd>Current status of the TDLS link with the selected peer.</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+	  <dd>The "peer_address" argument is not a properly formatted MAC.</dd>
+	</dl>
+      </li>
+      <li>
+	<h3>TDLSTeardown ( s : peer_address ) --> nothing</h3>
+	<p>Tear down a TDLS session with a peer.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : peer_address</dt>
+	  <dd>MAC address for the peer to tear down TDLS connectivity with.</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+	  <dd>The "peer_address" argument is not a properly formatted MAC.</dd>
+	  <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+	  <dd>Initiating the TDLS operation failed for an unknown reason.</dd>
+	</dl>
+      </li>
+      <li>
+	<h3>EAPLogoff ( ) --> nothing</h3>
+	<p>IEEE 802.1X EAPOL state machine logoff.</p>
+      </li>
+      <li>
+	<h3>EAPLogon ( ) --> nothing</h3>
+	<p>IEEE 802.1X EAPOL state machine logon.</p>
+      </li>
+
+      <li>
+	<h3>NetworkReply ( o : network, s : field, s : value ) --> nothing</h3>
+	<p>Provide parameter requested by NetworkRequest().</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>o : network</dt>
+	  <dd>A D-Bus path to an object representing the network (copied from NetworkRequest()).</dd>
+	  <dt>s : field</dt>
+	  <dd>Requested information (copied from NetworkRequest()).</dd>
+	  <dt>s : value</dt>
+	  <dd>The requested information (e.g., password for EAP authentication).</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.NetworkUnknown</dt>
+	  <dd>A passed path doesn't point to any network object.</dd>
+	  <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+	  <dd>A passed path doesn't point to any network object.</dd>
+	  <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+	  <dd>IEEE 802.1X support was not included in the build.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>SetPKCS11EngineAndModulePath ( s : pkcs11_engine_path, s : pkcs11_module_path ) --> nothing</h3>
+	<p>Set PKCS #11 engine and module path.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : pkcs11_engine_path</dt>
+	  <dd>PKCS #11 engine path.</dd>
+	  <dt>s : pkcs11_module_path</dt>
+	  <dd>PKCS #11 module path.</dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>org.freedesktop.DBus.Error.Failed.InvalidArgs</dt>
+	  <dd>Invalid PKCS #11 engine or module path.</dd>
+	  <dt>org.freedesktop.DBus.Error.Failed</dt>
+	  <dd>Reinit of the EAPOL state machine with the new PKCS #11 engine and module path failed.</dd>
+	</dl>
+      </li>
+      <li>
+	<h3>SignalPoll ( ) --> a{sv} : properties</h3>
+	<p>Fetch signal properties for the current connection.</p>
+	<h4>Returns</h4>
+	<dl>
+	  <dt>a{sv} : properties</dt>
+	  <dd>
+	    <table>
+	      <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th>
+	      <tr><td>linkspeed</td><td>i</td><td>Link speed (Mbps)</td><td>No</td>
+	      <tr><td>noise</td><td>i</td><td>Noise (dBm)</td><td>No</td>
+	      <tr><td>width</td><td>s</td><td>Channel width</td><td>No</td>
+	      <tr><td>frequency</td><td>u</td><td>Frequency (MHz)</td><td>No</td>
+	      <tr><td>rssi</td><td>i</td><td>RSSI (dBm)</td><td>No</td>
+	      <tr><td>avg-rssi</td><td>i</td><td>Average RSSI (dBm)</td><td>No</td>
+	      <tr><td>center-frq1</td><td>i</td><td>VHT segment 1 frequency (MHz)</td><td>No</td>
+	      <tr><td>center-frq2</td><td>i</td><td>VHT segment 2 frequency (MHz)</td><td>No</td>
+	    </table>
+	  </dd>
+	</dl>
+      </li>
+      <li>
+	<h3>FlushBSS ( u : age ) --> nothing</h3>
+	<p>Flush BSS entries from the cache.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>u : age</dt>
+	  <dd>Maximum age in seconds for BSS entries to keep in cache (0 = remove all entries).</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>SubscribeProbeReq ( ) --> nothing</h3>
+	<p>Subscribe to receive Probe Request events. This is needed in addition to registering a signal handler for the ProbeRequest signal to avoid flooding D-Bus with all Probe Request indications when no application is interested in them.</p>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.SubscriptionInUse</dt>
+	  <dd>Another application is already subscribed.</dd>
+	  <dt>fi.w1.wpa_supplicant1.NoMemory</dt>
+	  <dd>Needed memory was not possible to get allocated.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>UnsubscribeProbeReq ( ) --> nothing</h3>
+	<p>Unsubscribe from receiving Probe Request events.</p>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.NoSubscription</dt>
+	  <dd>No subscription in place.</dd>
+	  <dt>fi.w1.wpa_supplicant1.SubscriptionNotYou</dt>
+	  <dd>Subscription in place, but for another process.</dd>
+	</dl>
+      </li>
+    </ul>
+
+\subsection dbus_interface_properties Properties
+
+<ul>
+      <li>
+	<h3>Capabilities - a{sv} - (read)</h3>
+	<p>Capabilities of the interface. Dictionary contains following entries:</p>
+	<table>
+	  <tr><th>Key</th><th>Value type</th><th>Description</th>
+	  <tr><td>Pairwise</td><td>as</td><td>Possible array elements: "ccmp", "tkip", "none"</td>
+	  <tr><td>Group</td><td>as</td><td>Possible array elements: "ccmp", "tkip", "wep104", "wep40"</td>
+	  <tr><td>KeyMgmt</td><td>as</td><td>Possible array elements: "wpa-psk", "wpa-ft-psk", "wpa-psk-sha256", "wpa-eap", "wpa-ft-eap", "wpa-eap-sha256", "ieee8021x", "wpa-none", "wps", "none"</td>
+	  <tr><td>Protocol</td><td>as</td><td>Possible array elements: "rsn", "wpa"</td>
+	  <tr><td>AuthAlg</td><td>as</td><td>Possible array elements: "open", "shared", "leap"</td>
+	  <tr><td>Scan</td><td>as</td><td>Possible array elements: "active", "passive", "ssid"</td>
+	  <tr><td>Modes</td><td>as</td><td>Possible array elements: "infrastructure", "ad-hoc", "ap"</td>
+	</table>
+      </li>
+
+      <li>
+	<h3>State - s - (read)</h3>
+	<p>A state of the interface. Possible values are: return "disconnected", "inactive", "scanning", "authenticating", "associating", "associated", "4way_handshake", "group_handshake", "completed","unknown".</p>
+      </li>
+
+      <li>
+	<h3>Scanning - b - (read)</h3>
+	<p>Determines if the interface is already scanning or not</p>
+      </li>
+
+      <li>
+	<h3>ApScan - u - (read/write)</h3>
+	<p>Identical to ap_scan entry in wpa_supplicant configuration file. Possible values are 0, 1 or 2.</p>
+      </li>
+
+      <li>
+	<h3>BSSExpireAge - u - (read/write)</h3>
+	<p>Identical to bss_expiration_age entry in wpa_supplicant configuration file.</p>
+      </li>
+
+      <li>
+	<h3>BSSExpireCount - u - (read/write)</h3>
+	<p>Identical to bss_expiration_scan_count entry in wpa_supplicant configuration file.</p>
+      </li>
+
+      <li>
+	<h3>Country - s - (read/write)</h3>
+	<p>Identical to country entry in wpa_supplicant configuration file.</p>
+      </li>
+
+      <li>
+	<h3>Ifname - s - (read)</h3>
+	<p>Name of network interface controlled by the interface, e.g., wlan0.</p>
+      </li>
+
+      <li>
+	<h3>BridgeIfname - s - (read)</h3>
+	<p>Name of bridge network interface controlled by the interface, e.g., br0.</p>
+      </li>
+
+      <li>
+	<h3>Driver - s - (read)</h3>
+	<p>Name of driver used by the interface, e.g., nl80211.</p>
+      </li>
+
+      <li>
+	<h3>CurrentBSS - o - (read)</h3>
+	<p>Path to D-Bus object representing BSS which wpa_supplicant is associated with, or "/" if is not associated at all.</p>
+      </li>
+
+      <li>
+	<h3>CurrentNetwork - o - (read)</h3>
+	<p>Path to D-Bus object representing configured network which wpa_supplicant uses at the moment, or "/" if doesn't use any.</p>
+      </li>
+
+      <li>
+	<h3>CurrentAuthMode - s - (read)</h3>
+	<p>Current authentication type.</p>
+      </li>
+
+      <li>
+	<h3>Blobs - as - (read)</h3>
+	<p>List of blobs names added to the Interface.</p>
+      </li>
+
+      <li>
+	<h3>BSSs - ao - (read)</h3>
+	<p>List of D-Bus objects paths representing BSSs known to the interface, i.e., scan results.</p>
+      </li>
+
+      <li>
+	<h3>Networks - ao - (read)</h3>
+	<p>List of D-Bus objects paths representing configured networks.</p>
+      </li>
+
+      <li>
+	<h3>FastReauth - b - (read/write)</h3>
+	<p>Identical to fast_reauth entry in wpa_supplicant configuration file.</p>
+      </li>
+
+      <li>
+	<h3>ScanInterval - i - (read/write)</h3>
+	<p>Time (in seconds) between scans for a suitable AP. Must be >= 0.</p>
+      </li>
+
+      <li>
+	<h3>PKCS11EnginePath - s - (read)</h3>
+	<p>PKCS #11 engine path.</p>
+      </li>
+
+      <li>
+	<h3>PKCS11ModulePath - s - (read)</h3>
+	<p>PKCS #11 module path.</p>
+      </li>
+
+      <li>
+	<h3>DisconnectReason - i - (read)</h3>
+	<p>The most recent IEEE 802.11 reason code for disconnect. Negative value indicates locally generated disconnection.</p>
+      </li>
+    </ul>
+
+\subsection dbus_interface_signals Signals
+
+<ul>
+      <li>
+	<h3>ScanDone ( b : success )</h3>
+	<p>Scanning finished. </p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : success</dt>
+	  <dd>Determines if scanning was successful. If so, results are available.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>BSSAdded ( o : BSS, a{sv} : properties )</h3>
+	<p>Interface became aware of a new BSS.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>o : BSS</dt>
+	  <dd>A D-Bus path to an object representing the new BSS.</dd>
+	</dl>
+	<dl>
+	  <dt>a{sv} : properties</dt>
+	  <dd>A dictionary containing properties of added BSS.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>BSSRemoved ( o : BSS )</h3>
+	<p>BSS disappeared.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>o : BSS</dt>
+	  <dd>A D-Bus path to an object representing the BSS.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>BlobAdded ( s : blobName )</h3>
+	<p>A new blob has been added to the interface.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : blobName</dt>
+	  <dd>A name of the added blob.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>BlobRemoved ( s : blobName )</h3>
+	<p>A blob has been removed from the interface.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : blobName</dt>
+	  <dd>A name of the removed blob.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>NetworkAdded ( o : network, a{sv} : properties )</h3>
+	<p>A new network has been added to the interface.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>o : network</dt>
+	  <dd>A D-Bus path to an object representing the added network.</dd>
+	</dl>
+	<dl>
+	  <dt>a{sv} : properties</dt>
+	  <dd>A dictionary containing properties of added network.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>NetworkRemoved ( o : network )</h3>
+	<p>The network has been removed from the interface.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>o : network</dt>
+	  <dd>A D-Bus path to an object representing the removed network.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>NetworkSelected ( o : network )</h3>
+	<p>The network has been selected.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>o : network</dt>
+	  <dd>A D-Bus path to an object representing the selected network.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>StaAuthorized ( s : mac )</h3>
+	<p>A new station has been authorized to the interface.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : mac</dt>
+	  <dd>A mac address which has been authorized.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>StaDeauthorized ( s : mac )</h3>
+	<p>A station has been deauthorized to the interface.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : mac</dt>
+	  <dd>A mac address which has been deauthorized.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>PropertiesChanged ( a{sv} : properties )</h3>
+	<p>Some properties have changed.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>a{sv} : properties</dt>
+	  <dd>A dictionary with pairs of properties names which have changed and theirs new values. Possible dictionary keys are: "ApScan", "Scanning", "State", "CurrentBSS", "CurrentNetwork"</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>Certification ( a{sv} : parameters )</h3>
+	<p>Information about server TLS certificates.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>a{sv} : parameters</dt>
+	  <dd>A dictionary with pairs of field names and their values. Possible dictionary keys are: "depth", "subject", "altsubject", "cert_hash", "cert".</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>EAP ( s : status, s : parameter )</h3>
+	<p>Information about EAP peer status.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : status</dt>
+	  <dd>Operation, e.g., "started", "accept proposed method", "remote certificate verification", "eap parameter needed", "completion".</dd>
+	  <dt>s : parameter</dt>
+	  <dd>Information about the operation, e.g., EAP method name, "success".</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>NetworkRequest ( o : network, s : field, s : txt )</h3>
+	<p>Request for network parameter. NetworkResponse() is used to provide the requested parameter.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>o : network</dt>
+	  <dd>D-Bus path to an object representing the network.</dd>
+	  <dt>s : field</dt>
+	  <dd>Requested information, e.g., "PASSWORD".</dd>
+	  <dt>txt : field</dt>
+	  <dd>Human readable information about the requested information.</dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>ProbeRequest ( a{sv} : args )</h3>
+	<p>Information about a received Probe Request frame. This signal is delivered only to a single application that has subscribed to received the events with SubscribeProbeReq().</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>a{sv} : args</dt>
+	  <dd>A dictionary with pairs of field names and their values. Possible dictionary keys are: "addr", "dst", "bssid", "ies", "signal".</dd>
+	</dl>
+      </li>
+    </ul>
+
+
+\section dbus_wps fi.w1.wpa_supplicant1.Interface.WPS
+
+Interface for performing WPS (Wi-Fi Simple Config) operations.
+
+\subsection dbus_wps_methods Methods
+
+<ul>
+      <li>
+	<h3>Start ( a{sv} : args ) --> a{sv} : output</h3>
+	<p>Starts WPS configuration. Note: When used with P2P groups, this needs to be issued on the GO group interface.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>a{sv} : args</dt>
+	  <dd>
+	    A dictionary with arguments used to start WPS configuration. The dictionary may contain the following entries:
+	    <table>
+	      <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th>
+	      <tr><td>Role</td><td>s</td><td>The device's role. Possible values are "enrollee" and "registrar".</td><td>Yes</td>
+	      <tr><td>Type</td><td>s</td><td>WPS authentication type. Applies only for enrollee role. Possible values are "pin" and "pbc".</td><td>Yes, for enrollee role; otherwise no</td>
+	      <tr><td>Pin</td><td>s</td><td>WPS Pin.</td><td>Yes, for registrar role; otherwise optional</td>
+	      <tr><td>Bssid</td><td>ay</td><td>Note: This is used to specify the peer MAC address when authorizing WPS connection in AP or P2P GO role.</td><td>No</td>
+	      <tr><td>P2PDeviceAddress</td><td>ay</td><td>P2P Device Address of a peer to authorize for PBC connection. Used only in P2P GO role.</td><td>No</td>
+	    </table>
+	  </dd>
+	</dl>
+	<h4>Returns</h4>
+	<dl>
+	  <dt>a{sv} : output</dt>
+	  <dd>
+	    <table>
+	      <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th>
+	      <tr><td>Pin</td><td>s</td><td>Newly generated PIN, if not specified for enrollee role and pin authentication type.</td><td>No</td>
+	    </table>
+	  </dd>
+	</dl>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+	  <dd>Starting WPS configuration failed for an unknown reason.</dd>
+	  <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+	  <dd>Invalid entries were found in the passed argument.</dd>
+	</dl>
+      </li>
+      <li>
+	<h3>Cancel ( nothing ) --> nothing</h3>
+	<p>Cancel ongoing WPS operation.</p>
+      </li>
+    </ul>
+
+\subsection dbus_wps_properties Properties
+
+<ul>
+      <li>
+	<h3>ProcessCredentials - b - (read/write)</h3>
+	<p>Determines if the interface will process the credentials (credentials_processed configuration file parameter).</p>
+      </li>
+      <li>
+	<h3>ConfigMethods - s - (read/write)</h3>
+	<p>The currently advertised WPS configuration methods. Available methods: usba ethernet label display ext_nfc_token int_nfc_token nfc_interface push_button keypad virtual_display physical_display virtual_push_button physical_push_button.</p>
+      </li>
+    </ul>
+
+\subsection dbus_wps_signals Signals
+
+<ul>
+      <li>
+	<h3>Event ( s : name, a{sv} : args )</h3>
+	<p>WPS event occurred.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>s : event</dt>
+	  <dd>Event type. Possible values are: "success, "fail", "m2d", and
+	  "pbc-overlap".</dd>
+	  <dt>a{sv} : args</dt>
+	  <dd>
+	    Event arguments. Empty for success and pbc-overlap events, error information ( "msg" : i, "config_error" : i, "error_indication" : i ) for fail event and following entries for m2d event:
+	    <table>
+	      <tr><th>config_methods</th><th>Value type</th>
+	      <tr><td>manufacturer</td><td>q</td>
+	      <tr><td>model_name</td><td>ay</td>
+	      <tr><td>model_number</td><td>ay</td>
+	      <tr><td>serial_number</td><td>ay</td>
+	      <tr><td>dev_name</td><td>ay</td>
+	      <tr><td>primary_dev_type</td><td>ay</td>
+	      <tr><td>config_error</td><td>q</td>
+	      <tr><td>dev_password_id</td><td>q</td>
+	    </table>
+	  </dd>
+	</dl>
+      </li>
+
+      <li>
+	<h3>Credentials ( a{sv} : credentials )</h3>
+	<p>WPS credentials. Dictionary contains:</p>
+	<table>
+	  <tr><th>Key</th><th>Value type</th><th>Description</th>
+	  <tr><td>BSSID</td><td>ay</td><td></td>
+	  <tr><td>SSID</td><td>s</td><td></td>
+	  <tr><td>AuthType</td><td>as</td><td>Possible array elements: "open", "shared", "wpa-psk", "wpa-eap", "wpa2-eap", "wpa2-psk"</td>
+	  <tr><td>EncrType</td><td>as</td><td>Possible array elements: "none", "wep", "tkip", "aes"</td>
+	  <tr><td>Key</td><td>ay</td><td>Key data</td>
+	  <tr><td>KeyIndex</td><td>u</td><td>Key index</td>
+	</table>
+      </li>
+
+      <li>
+	<h3>PropertiesChanged ( a{sv} : properties )</h3>
+	<p>Some properties have changed.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>a{sv} : properties</dt>
+	  <dd>A dictionary with pairs of properties names which have changed and theirs new values. Possible dictionary keys are: "ProcessCredentials"</dd>
+	</dl>
+      </li>
+    </ul>
+
+
+\section dbus_p2pdevice fi.w1.wpa_supplicant1.Interface.P2PDevice
+
+Interface for performing P2P (Wi-Fi Peer-to-Peer) P2P Device operations.
+
+\subsection dbus_p2pdevice_methods Methods
+
+<ul>
+  <li>
+    <h3>Find ( a{sv} : args ) --> nothing</h3>
+    <p>Start P2P find operation (i.e., alternating P2P Search and Listen states to discover peers and be discoverable).</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+	A dictionary with parameters for the P2P find operation:
+	<table>
+	<tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+	<tr><td>Timeout</td><td>i</td><td>Timeout for operating in seconds</td><td>no</td></tr>
+	<tr><td>RequestedDeviceTypes</td><td>aay</td><td>WPS Device Types to search for</td><td>no</td></tr>
+	<tr><td>DiscoveryType</td><td>s</td><td>"start_with_full" (default, if not specified), "social", "progressive"</td><td>no</td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>StopFind ( nothing ) --> nothing</h3>
+    <p>Stop P2P find operation.</p>
+  </li>
+
+  <li>
+    <h3>Listen ( i : timeout ) --> nothing</h3>
+    <p>Start P2P listen operation (i.e., be discoverable).</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>i : timeout</dt>
+      <dd>Timeout in seconds for stopping the listen operation.</dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>ExtendedListen ( a{sv} : args ) --> nothing</h3>
+    <p>Configure Extended Listen Timing. If the parameters are omitted, this feature is disabled. If the parameters are included, Listen State will be entered every interval msec for at least period msec. Both values have acceptable range of 1-65535 (with interval obviously having to be larger than or equal to duration). If the P2P module is not idle at the time the Extended Listen Timing timeout occurs, the Listen State operation will be skipped.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+	A dictionary with parameters for extended listen. Leave out all items to disable extended listen.
+	<table>
+	<tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+	<tr><td>period</td><td>i</td><td>Extended listen period in milliseconds; 1-65535.</td><td>no</td></tr>
+	<tr><td>interval</td><td>i</td><td>Extended listen interval in milliseconds; 1-65535.</td><td>no</td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>PresenceRequest ( a{sv} : args ) --> nothing</h3>
+    <p>Request a specific GO presence in a P2P group where the local device is a P2P Client. 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 interval values in microseconds. If the second pair is included, that indicates which value would be acceptable.
+    \note This needs to be issued on a P2P group interface if separate group interfaces are used.
+    \bug It would be cleaner to not require .P2PDevice methods to be issued on a group interface. In other words, args['group_object'] could be used to specify the group or this method could be moved to be a .Group PresenceRequest() method.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+	A dictionary with parameters for the presence request.
+	<table>
+	<tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+	<tr><td>duration1</td><td>i</td><td>Duration in microseconds.</td><td>no</td></tr>
+	<tr><td>interval1</td><td>i</td><td>Interval in microseconds.</td><td>no</td></tr>
+	<tr><td>duration2</td><td>i</td><td>Duration in microseconds.</td><td>no</td></tr>
+	<tr><td>interval2</td><td>i</td><td>Interval in microseconds.</td><td>no</td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryRequest ( o : peer, s : config_method ) --> nothing</h3>
+  </li>
+
+  <li>
+    <h3>Connect ( a{sv} : args ) --> s : generated_pin</h3>
+    <p>Request a P2P group to be started through GO Negotiation or by joining an already operating group.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+	A dictionary with parameters for the requested connection:
+	<table>
+	<tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+	<tr><td>peer</td><td>o</td><td></td><td>yes</td></tr>
+	<tr><td>persistent</td><td>b</td><td>Whether to form a persistent group.</td><td>no</td></tr>
+	<tr><td>join</td><td>b</td><td>Whether to join an already operating group instead of forming a new group.</td><td>no</td></tr>
+	<tr><td>authorize_only</td><td>b</td><td>Whether to authorize a peer to initiate GO Negotiation instead of initiating immediately.</td><td>no</td></tr>
+	<tr><td>frequency</td><td>i</td><td>Operating frequency in MHz</td><td>no</td></tr>
+	<tr><td>go_intent</td><td>i</td><td>GO intent 0-15</td><td>no</td></tr>
+	<tr><td>wps_method</td><td>s</td><td>"pbc", "display", "keypad", "pin" (alias for "display")</td><td>yes</td></tr>
+	<tr><td>pin</td><td>s</td><td></td><td>no</td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>GroupAdd ( a{sv} : args ) --> nothing</h3>
+    <p>Request a P2P group to be started without GO Negotiation.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+	A dictionary with parameters for the requested group:
+	<table>
+	<tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+	<tr><td>persistent</td><td>b</td><td>Whether to form a persistent group.</td><td>no</td></tr>
+	<tr><td>persistent_group_object</td><td>o</td><td></td><td>no</td></tr>
+	<tr><td>frequency</td><td>i</td><td>Operating frequency in MHz</td><td>no</td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>Cancel ( nothing ) --> nothing</h3>
+    <p>Stop ongoing P2P group formation operation.</p>
+  </li>
+
+  <li>
+    <h3>Invite ( a{sv} : args ) --> nothing</h3>
+    <p>Invite a peer to join an already operating group or to re-invoke a persistent group.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+	A dictionary with parameters for the invitation:
+	<table>
+	<tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+	<tr><td>peer</td><td>o</td><td></td><td>yes</td></tr>
+	<tr><td>persistent_group_object</td><td>o</td><td></td><td>no</td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>Disconnect ( nothing ) --> nothing</h3>
+    <p>Terminate a P2P group.
+    \note This needs to be issued on a P2P group interface if separate group interfaces are used.
+    \bug It would be cleaner to not require .P2PDevice methods to be issued on a group interface. In other words, this would either need to be Disconnect(group_object) or moved to be a .Group Disconnect() method.</p>
+  </li>
+
+  <li>
+    <h3>RejectPeer ( o : peer ) --> nothing</h3>
+    <p>Reject connection attempt from a peer (specified with a device address). This is a mechanism to reject a pending GO Negotiation with a peer and request to automatically block any further connection or discovery of the peer.</p>
+  </li>
+
+  <li>
+    <h3>RemoveClient ( a{sv} : args ) --> nothing</h3>
+    <p>Remove the client from all groups (operating and persistent) from the local GO.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+	A dictionary with parameters for removing a client:
+	<table>
+	<tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+	<tr><td>peer</td><td>o</td><td>Object path for peer's P2P Device Address</td><td>yes</td></tr>
+	<tr><td>iface</td><td>s</td><td>Interface address[MAC Address format] of the peer to be disconnected. Required if object path is not provided.</td><td>no</td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>Flush ( nothing ) --> nothing</h3>
+    <p>Flush P2P peer table and state.</p>
+  </li>
+
+  <li>
+    <h3>AddService ( a{sv} : args ) --> nothing</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+	A dictionary with parameters for the service:
+	<table>
+	<tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+	<tr><td>service_type</td><td>s</td><td>"upnp", "bonjour"</td><td>yes</td></tr>
+	<tr><td>version</td><td>u</td><td>Required for UPnP services.</td><td>no</td></tr>
+	<tr><td>service</td><td>s</td><td></td><td></td></tr>
+	<tr><td>query</td><td>ay</td><td></td><td></td></tr>
+	<tr><td>response</td><td>ay</td><td></td><td></td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>DeleteService ( a{sv} : args ) --> nothing</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+	A dictionary with parameters for the service:
+	<table>
+	<tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+	<tr><td>service_type</td><td>s</td><td>"upnp", "bonjour"</td><td>yes</td></tr>
+	<tr><td>version</td><td>u</td><td>Required for UPnP services.</td><td>no</td></tr>
+	<tr><td>service</td><td>s</td><td></td><td></td></tr>
+	<tr><td>query</td><td>ay</td><td></td><td></td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>FlushService ( nothing ) --> nothing</h3>
+  </li>
+
+  <li>
+    <h3>ServiceDiscoveryRequest ( a{sv} : args ) --> t : ref</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+	A dictionary with following parameters:
+	<table>
+	<tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+	<tr><td>peer_object</td><td>o</td><td></td><td>no</td></tr>
+	<tr><td>service_type</td><td>s</td><td>"upnp"</td><td>no</td></tr>
+	<tr><td>version</td><td>u</td><td>Required for UPnP services.</td><td>no</td></tr>
+	<tr><td>service</td><td>s</td><td></td><td></td></tr>
+	<tr><td>tlv</td><td>ay</td><td></td><td></td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>ServiceDiscoveryResponse ( a{sv} : args ) --> nothing : ref</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+	A dictionary with following parameters:
+	<table>
+	<tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+	<tr><td>peer_object</td><td>o</td><td></td><td>yes</td></tr>
+	<tr><td>frequency</td><td>i</td><td></td><td>yes</td></tr>
+	<tr><td>dialog_token</td><td>i</td><td></td><td>yes</td></tr>
+	<tr><td>tlvs</td><td>ay</td><td></td><td>yes</td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>ServiceDiscoveryCancelRequest ( t : args ) --> nothing : ref</h3>
+  </li>
+
+  <li>
+    <h3>ServiceUpdate ( nothing ) --> nothing</h3>
+  </li>
+
+  <li>
+    <h3>ServiceDiscoveryExternal ( i : arg ) --> nothing</h3>
+  </li>
+
+  <li>
+    <h3>AddPersistentGroup ( a{sv} : args ) --> o : path</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+	A dictionary with following parameters:
+	<table>
+	<tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+	<tr><td>bssid</td><td>s</td><td>P2P Device Address of the GO in the persistent group.</td><td>yes</td></tr>
+	<tr><td>ssid</td><td>s</td><td>SSID of the group</td><td>yes</td></tr>
+	<tr><td>psk</td><td>s</td><td>Passphrase (on the GO and optionally on P2P Client) or PSK (on P2P Client if passphrase ise not known)</td><td>yes</td></tr>
+	<tr><td>mode</td><td>s</td><td>"3" on GO or "0" on P2P Client</td><td>yes</td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>RemovePersistentGroup ( o : path ) --> nothing</h3>
+  </li>
+
+  <li>
+    <h3>RemoveAllPersistentGroups ( nothing ) --> nothing</h3>
+  </li>
+</ul>
+
+\subsection dbus_p2pdevice_properties Properties
+
+<ul>
+  <li>
+    <h3>P2PDeviceConfig - a{sv} - (read/write)</h3>
+    <p>Dictionary with following entries. On write, only the included values are changed.</p>
+    <table>
+    <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+    <tr><td>DeviceName</td><td>s</td><td></td></tr>
+    <tr><td>PrimaryDeviceType</td><td>ay</td><td></td></tr>
+    <tr><td>SecondaryDeviceTypes</td><td>aay</td><td></td></tr>
+    <tr><td>VendorExtension</td><td>aay</td><td></td></tr>
+    <tr><td>GOIntent</td><td>u</td><td></td></tr>
+    <tr><td>PersistentReconnect</td><td>b</td><td></td></tr>
+    <tr><td>ListenRegClass</td><td>u</td><td></td></tr>
+    <tr><td>ListenChannel</td><td>u</td><td></td></tr>
+    <tr><td>OperRegClass</td><td>u</td><td></td></tr>
+    <tr><td>OperChannel</td><td>u</td><td></td></tr>
+    <tr><td>SsidPostfix</td><td>s</td><td></td></tr>
+    <tr><td>IntraBss</td><td>b</td><td></td></tr>
+    <tr><td>GroupIdle</td><td>u</td><td></td></tr>
+    <tr><td>disassoc_low_ack</td><td>u</td><td></td></tr>
+    <tr><td>NoGroupIface</td><td>b</td><td></td></tr>
+    <tr><td>p2p_search_delay</td><td>u</td><td></td></tr>
+    </table>
+  </li>
+
+  <li>
+    <h3>Peers - ao - (read)</h3>
+  </li>
+
+  <li>
+    <h3>Role - s - (read)</h3>
+    <p>\bug What is this trying to indicate? It does not make much sense to have a P2PDevice property role since there can be multiple concurrent groups and the P2P Device role is always active anyway.</p>
+  </li>
+
+  <li>
+    <h3>Group - o - (read)</h3>
+    <p>\bug What is this trying to indicate? It does not make much sense to have a P2PDevice property Group since there can be multiple concurrent groups.</p>
+  </li>
+
+  <li>
+    <h3>PeerGO - o - (read)</h3>
+    <p>\bug What is this trying to indicate? It does not make much sense to have a P2PDevice property PeerGO since there can be multiple concurrent groups.</p>
+  </li>
+
+  <li>
+    <h3>PersistentGroups - ao - (read)</h3>
+  </li>
+</ul>
+
+\subsection dbus_p2pdevice_signals Signals
+
+<ul>
+  <li>
+    <h3>DeviceFound ( o : path )</h3>
+  </li>
+
+  <li>
+    <h3>DeviceLost ( o : path )</h3>
+  </li>
+
+  <li>
+    <h3>FindStopped ( )</h3>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryRequestDisplayPin ( o : peer_object, s : pin )</h3>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryResponseDisplayPin ( o : peer_object, s : pin )</h3>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryRequestEnterPin ( o : peer_object )</h3>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryResponseEnterPin ( o : peer_object )</h3>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryPBCRequest ( o : peer_object )</h3>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryPBCResponse ( o : peer_object )</h3>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryFailure ( o : peer_object, i : status )</h3>
+  </li>
+
+  <li>
+    <h3>GroupStarted ( a{sv} : properties )</h3>
+    <p>A new P2P group was started or joined.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : properties</dt>
+      <dd>A dictionary with following information on the added group:
+	<table>
+	  <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+	  <tr><td>interface_object</td><td>o</td><td>D-Bus path of the interface on which this group is operating on. See \ref dbus_interface.</td></tr>
+	  <tr><td>role</td><td>s</td><td>The role of the local device in the group: "GO" or "client".</td></tr>
+	  <tr><td>group_object</td><td>o</td><td>D-Bus path of the group. See \ref dbus_group.</td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>GONegotiationSuccess ( a{sv} : properties )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : properties</dt>
+      <dd>A dictionary with following information:
+	<table>
+	  <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+	  <tr><td>peer_object</td><td>o</td><td>D-Bus path of the peer. See \ref dbus_peer.</td></tr>
+	  <tr><td>status</td><td>i</td><td></td></tr>
+	  <tr><td>passphrase</td><td>s</td><td>Passphrase for the group. Included only if this device becomes the GO of the group.</td></tr>
+	  <tr><td>role_go</td><td>s</td><td>The role of the local device in the group: "GO" or "client".</td></tr>
+	  <tr><td>ssid</td><td>ay</td><td></td></tr>
+	  <tr><td>peer_device_addr</td><td>ay</td><td></td></tr>
+	  <tr><td>peer_interface_addr</td><td>ay</td><td></td></tr>
+	  <tr><td>wps_method</td><td>s</td><td></td></tr>
+	  <tr><td>frequency_list</td><td>ai</td><td></td></tr>
+	  <tr><td>persistent_group</td><td>i</td><td></td></tr>
+	  <tr><td>peer_config_timeout</td><td>u</td><td></td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>GONegotiationFailure ( a{sv} : properties )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : properties</dt>
+      <dd>A dictionary with following information:
+	<table>
+	  <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+	  <tr><td>peer_object</td><td>o</td><td>D-Bus path of the peer. See \ref dbus_peer.</td></tr>
+	  <tr><td>status</td><td>i</td><td></td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>GONegotiationRequest ( o : path, q : dev_passwd_id, y : device_go_intent )</h3>
+  </li>
+
+  <li>
+    <h3>InvitationResult ( a{sv} : invite_result )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : invite_result</dt>
+      <dd>A dictionary with following information:
+	<table>
+	  <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+	  <tr><td>status</td><td>i</td><td></td></tr>
+	  <tr><td>BSSID</td><td>ay</td><td>Optionally present</td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>GroupFinished ( a{sv} : properties )</h3>
+    <p>A P2P group was removed.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : properties</dt>
+      <dd>A dictionary with following information of the removed group:
+	<table>
+	  <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+	  <tr><td>interface_object</td><td>o</td><td>D-Bus path of the interface on which this group is operating on. See \ref dbus_interface.</td></tr>
+	  <tr><td>role</td><td>s</td><td>The role of the local device in the group: "GO" or "client".</td></tr>
+	  <tr><td>group_object</td><td>o</td><td>D-Bus path of the group. See \ref dbus_group.</td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>ServiceDiscoveryRequest ( a{sv} : sd_request )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : sd_request</dt>
+      <dd>A dictionary with following information:
+	<table>
+	  <tr><td>peer_object</td><td>o</td><td></td></tr>
+	  <tr><td>frequency</td><td>i</td><td></td></tr>
+	  <tr><td>dialog_token</td><td>i</td><td></td></tr>
+	  <tr><td>update_indicator</td><td>q</td><td></td></tr>
+	  <tr><td>tlvs</td><td>ay</td><td></td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>ServiceDiscoveryResponse ( a{sv} : sd_response )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : sd_response</dt>
+      <dd>A dictionary with following information:
+	<table>
+	  <tr><td>peer_object</td><td>o</td><td></td></tr>
+	  <tr><td>update_indicator</td><td>q</td><td></td></tr>
+	  <tr><td>tlvs</td><td>ay</td><td></td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>PersistentGroupAdded ( o : path, a{sv} : properties )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>o : path</dt>
+      <dd>D-Bus object path for the persistent group. See \ref dbus_persistent_group.</dd>
+      <dt>a{sv} : properties</dt>
+      <dd>A dictionary with following information:
+	<table>
+	<tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+	<tr><td>bssid</td><td>s</td><td>P2P Device Address of the GO in the persistent group.</td></tr>
+	<tr><td>ssid</td><td>s</td><td>SSID of the group</td></tr>
+	<tr><td>psk</td><td>s</td><td>Passphrase (on the GO and optionally on P2P Client) or PSK (on P2P Client if passphrase ise not known)</td></tr>
+	<tr><td>disabled</td><td>s</td><td>Set to "2" to indicate special network block use as a P2P persistent group information</td></tr>
+	<tr><td>mode</td><td>s</td><td>"3" on GO or "0" on P2P Client</td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>PersistentGroupRemoved ( o : path )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>o : path</dt>
+      <dd>D-Bus object path for the persistent group. See \ref dbus_persistent_group.</dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>WpsFailed ( s : name, a{sv} : args )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>s : name</dt>
+      <dd>"fail"</dd>
+      <dt>a{sv} : args</dt>
+      <dd>A dictionary with following information:
+	<table>
+	  <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+	  <tr><td>msg</td><td>i</td><td></td></tr>
+	  <tr><td>config_error</td><td>n</td><td></td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>InvitationReceived ( a{sv} : properties )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : properties</dt>
+      <dd>A dictionary with following information:
+	<table>
+	  <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+	  <tr><td>sa</td><td>ay</td><td>Optionally present</td></tr>
+	  <tr><td>go_dev_addr</td><td>ay</td><td>Optionally present</td></tr>
+	  <tr><td>bssid</td><td>ay</td><td>Optionally present</td></tr>
+	  <tr><td>persistent_id</td><td>i</td><td>Optionally present</td></tr>
+	  <tr><td>op_freq</td><td>i</td><td></td></tr>
+	</table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>GroupFormationFailure ( s : reason )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>s : reason</dt>
+      <dd>Reason for failure or empty string if not known.</dd>
+    </dl>
+  </li>
+</ul>
+
+\section dbus_bss fi.w1.wpa_supplicant1.BSS
+
+Interface implemented by objects representing a scanned BSSs, i.e.,
+scan results.
+
+\subsection dbus_bss_properties Properties
+
+<ul>
+      <li>
+	<h3>BSSID - ay - (read)</h3>
+	<p>BSSID of the BSS.</p>
+      </li>
+      <li>
+	<h3>SSID - ay - (read)</h3>
+	<p>SSID of the BSS.</p>
+      </li>
+      <li>
+	<h3>WPA - a{sv} - (read)</h3>
+	<p>WPA information of the BSS. Empty dictionary indicates no WPA support. Dictionary entries are:</p>
+	<table>
+	  <tr><td>KeyMgmt</td><td>as</td><td>Key management suite. Possible array elements: "wpa-psk", "wpa-eap", "wpa-none"</td>
+	  <tr><td>Pairwise</td><td>as</td><td>Pairwise cipher suites. Possible array elements: "ccmp", "tkip"</td>
+	  <tr><td>Group</td><td>s</td><td>Group cipher suite. Possible values are: "ccmp", "tkip", "wep104", "wep40"</td>
+	</table>
+      </li>
+      <li>
+	<h3>RSN - a{sv} - (read)</h3>
+	<p>RSN information of the BSS. Empty dictionary indicates no RSN support. Dictionary entries are:</p>
+	<table>
+	  <tr><td>KeyMgmt</td><td>as</td><td>Key management suite. Possible array elements: "wpa-psk", "wpa-eap", "wpa-ft-psk", "wpa-ft-eap", "wpa-psk-sha256", "wpa-eap-sha256",</td>
+	  <tr><td>Pairwise</td><td>as</td><td>Pairwise cipher suites. Possible array elements: "ccmp", "tkip"</td>
+	  <tr><td>Group</td><td>s</td><td>Group cipher suite. Possible values are: "ccmp", "tkip", "wep104", "wep40"</td>
+	  <tr><td>MgmtGroup</td><td>s</td><td>Mangement frames cipher suite. Possible values are: "aes128cmac"</td>
+	</table>
+      </li>
+      <li>
+	<h3>WPS - a{sv} - (read)</h3>
+	<p>WPS information of the BSS. Empty dictionary indicates no WPS support. Dictionary entries are:</p>
+	<table>
+	  <tr><td>Type</td><td>s</td><td>"pbc", "pin", ""</td>
+	</table>
+      </li>
+      <li>
+	<h3>IEs - ay - (read)</h3>
+	<p>All IEs of the BSS as a chain of TLVs</p>
+      </li>
+      <li>
+	<h3>Privacy - b - (read)</h3>
+	<p>Indicates if BSS supports privacy.</p>
+      </li>
+      <li>
+	<h3>Mode - s - (read)</h3>
+	<p>Describes mode of the BSS. Possible values are: "ad-hoc" and "infrastructure".</p>
+      </li>
+      <li>
+	<h3>Frequency - q - (read)</h3>
+	<p>Frequency of the BSS in MHz.</p>
+      </li>
+      <li>
+	<h3>Rates - au - (read)</h3>
+	<p>Descending ordered array of rates supported by the BSS in bits per second.</p>
+      </li>
+      <li>
+	<h3>Signal - n - (read)</h3>
+	<p>Signal strength of the BSS.</p>
+      </li>
+      <li>
+	<h3>Age - u - (read)</h3>
+	<p>Number of seconds since the BSS was last seen.</p>
+      </li>
+    </ul>
+
+\subsection dbus_bss_signals Signals
+
+<ul>
+      <li>
+	<h3>PropertiesChanged ( a{sv} : properties )</h3>
+	<p>Some properties have changed.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>a{sv} : properties</dt>
+	  <dd>A dictionary with pairs of properties names which have changed and theirs new values.</dd>
+	</dl>
+      </li>
+    </ul>
+
+
+\section dbus_network fi.w1.wpa_supplicant1.Network
+
+Interface implemented by objects representing configured networks,
+i.e., returned by fi.w1.wpa_supplicant1.Interface.AddNetwork.
+
+\subsection dbus_network_properties Properties
+
+<ul>
+      <li>
+	<h3>Enabled - b - (read/write)</h3>
+	<p>Determines if the configured network is enabled or not.</p>
+      </li>
+
+      <li>
+	<h3>Properties - a{sv} - (read/write)</h3>
+	<p>Properties of the configured network. Dictionary contains entries from "network" block of wpa_supplicant configuration file. All values are string type, e.g., frequency is "2437", not 2437.
+      </li>
+    </ul>
+
+\subsection dbus_network_signals Signals
+
+<ul>
+      <li>
+	<h3>PropertiesChanged ( a{sv} : properties )</h3>
+	<p>Some properties have changed.</p>
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>a{sv} : properties</dt>
+	  <dd>A dictionary with pairs of properties names which have changed and theirs new values. Possible dictionary keys are: "Enabled"</dd>
+	</dl>
+      </li>
+    </ul>
+
+\section dbus_peer fi.w1.wpa_supplicant1.Peer
+
+Interface implemented by objects representing P2P peer devices.
+
+\subsection dbus_peer_properties Properties
+
+<ul>
+  <li>
+    <h3>DeviceName - s - (read)</h3>
+  </li>
+
+  <li>
+    <h3>Manufacturer - s - (read)</h3>
+  </li>
+
+  <li>
+    <h3>ModelName - s - (read)</h3>
+  </li>
+
+  <li>
+    <h3>ModelNumber - s - (read)</h3>
+  </li>
+
+  <li>
+    <h3>SerialNumber - s - (read)</h3>
+  </li>
+
+  <li>
+    <h3>PrimaryDeviceType - ay - (read)</h3>
+  </li>
+
+  <li>
+    <h3>config_method - q - (read)</h3>
+  </li>
+
+  <li>
+    <h3>level - i - (read)</h3>
+  </li>
+
+  <li>
+    <h3>devicecapability - y - (read)</h3>
+  </li>
+
+  <li>
+    <h3>groupcapability - y - (read)</h3>
+    <p>Group Capability field from the last frame from which this peer information was updated.
+    \note This field is only for debugging purposes and must not be used to determine whether the peer happens to be operating a group as a GO at the moment.
+    </p>
+  </li>
+
+  <li>
+    <h3>SecondaryDeviceTypes - aay - (read)</h3>
+  </li>
+
+  <li>
+    <h3>VendorExtension - aay - (read)</h3>
+  </li>
+
+  <li>
+    <h3>IEs - ay - (read)</h3>
+    <p>This is a confusingly named property that includes Wi-Fi Display subelements from the peer.
+    \bug This should really be renamed since "IEs" means something completely different..
+    </p>
+  </li>
+
+  <li>
+    <h3>DeviceAddress - ay - (read)</h3>
+    <p>The P2P Device Address of the peer.</p>
+  </li>
+
+  <li>
+    <h3>Groups - ao - (read)</h3>
+    <p>The current groups in which this peer is connected.</p>
+  </li>
+</ul>
+
+\subsection dbus_peer_signals Signals
+
+<ul>
+  <li>
+    <h3>PropertiesChanged ( a{sv} : properties )</h3>
+    <p>Some properties have changed.
+    \deprecated Use org.freedesktop.DBus.Properties.PropertiesChanged instead.</p>
+    \todo Explain how ProertiesChanged signals are supposed to be of any real use with Peer objects (i.e., one signal for multiple peers).
+	<h4>Arguments</h4>
+	<dl>
+	  <dt>a{sv} : properties</dt>
+	  <dd>A dictionary with pairs of properties names which have changed and their new values.</dd>
+	</dl>
+      </li>
+    </ul>
+
+\section dbus_group fi.w1.wpa_supplicant1.Group
+
+Interface implemented by objects representing active P2P groups.
+
+\subsection dbus_group_properties Properties
+
+<ul>
+  <li>
+    <h3>Members - ao - (read)</h3>
+    <p>Array of D-Bus object paths for the peer devices that are currently connected to the group. This is valid only on the GO device. An empty array is returned in P2P Client role.
+  </li>
+
+  <li>
+    <h3>Group - o - (read)</h3>
+    <p>\todo Why is this here? This D-Bus object path is to this specific group and one needs to know it to fetching this information in the first place..
+    </p>
+  </li>
+
+  <li>
+    <h3>Role - s - (read)</h3>
+    <p>The role of this device in the group: "GO", "client".</p>
+  </li>
+
+  <li>
+    <h3>SSID - ay - (read)</h3>
+    <p>P2P Group SSID.</p>
+  </li>
+
+  <li>
+    <h3>BSSID - ay - (read)</h3>
+    <p>P2P Group BSSID (the P2P Interface Address of the GO).</p>
+  </li>
+
+  <li>
+    <h3>Frequency - q - (read)</h3>
+    <p>The frequency (in MHz) of the group operating channel.</p>
+  </li>
+
+  <li>
+    <h3>Passphrase - s - (read)</h3>
+    <p>Passphrase used in the group. This is always available on the GO. For P2P Client role, this may be available depending on whether the peer GO provided the passphrase during the WPS provisioning step. If not available, an empty string is returned.</p>
+  </li>
+
+  <li>
+    <h3>PSK - ay - (read)</h3>
+    <p>PSK used in the group.</p>
+  </li>
+
+  <li>
+    <h3>WPSVendorExtensions - aay - (read/write)</h3>
+    <p>WPS vendor extension attributes used on the GO. This is valid only the in the GO role. An empty array is returned in P2P Client role. At maximum, 10 separate vendor extension byte arrays can be configured. The GO device will include the configured attributes in WPS exchanges.</p>
+  </li>
+</ul>
+
+\subsection dbus_group_signals Signals
+
+<ul>
+  <li>
+    <h3>PeerJoined ( o : peer )</h3>
+    <p>A peer device has joined the group. This is indicated only on the GO device.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>o : peer</dt>
+      <dd>A D-Bus path to the object representing the peer. See \ref dbus_peer.</dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>PeerDisconnected ( o : peer )</h3>
+    <p>A peer device has left the group. This is indicated only on the GO device.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>o : peer</dt>
+      <dd>A D-Bus path to the object representing the peer. See \ref dbus_peer.</dd>
+    </dl>
+  </li>
+</ul>
+
+\section dbus_persistent_group fi.w1.wpa_supplicant1.PersistentGroup
+
+Interface implemented by objects representing persistent P2P groups.
+
+\subsection dbus_persistent_group_properties Properties
+
+<ul>
+  <li>
+    <h3>Properties - a{sv} - (read/write)</h3>
+    <p>Properties of the persistent group. These are same properties as in the \ref dbus_network. When writing this, only the entries to be modified are included, i.e., any item that is not included will be left at its existing value. The following entries are used for persistent groups:</p>
+    <table>
+      <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+      <tr><td>bssid</td><td>s</td><td>P2P Device Address of the GO in the persistent group.</td></tr>
+      <tr><td>ssid</td><td>s</td><td>SSID of the group</td></tr>
+      <tr><td>psk</td><td>s</td><td>Passphrase (on the GO and optionally on P2P Client) or PSK (on P2P Client if passphrase ise not known)</td></tr>
+      <tr><td>disabled</td><td>s</td><td>Set to "2" to indicate special network block use as a P2P persistent group information</td></tr>
+      <tr><td>mode</td><td>s</td><td>"3" on GO or "0" on P2P Client</td></tr>
+    </table>
+  </li>
+</ul>
+
+*/
diff --git a/hostap/doc/directories.doxygen b/hostap/doc/directories.doxygen
new file mode 100644
index 0000000..15e5bda
--- /dev/null
+++ b/hostap/doc/directories.doxygen
@@ -0,0 +1,90 @@
+/**
+
+\dir hostapd hostapd
+
+hostapd-specific code for configuration, control interface, and AP
+management.
+
+
+\dir src/common Common functionality
+
+This module includes IEEE 802.11, IEEE 802.1X, and WPA related
+functionality that is shared between AP and station modes.
+
+
+\dir src/crypto Cryptographical functionality and wrappers
+
+This module defines crypto and tls interfaces to provide portability
+layer for different crypto/TLS libraries. Wrappers for number of
+libraries are also included here. In addition, internal implementation
+of various crypto functions are provided as an alternative for an
+external library and to extend some algorithms.
+
+
+\dir src/drivers Driver wrappers
+
+This directory includes the driver interface definition and all the
+driver wrappers that can be used to interact with different drivers
+without making rest of the software dependent on which particular
+driver is used.
+
+
+\dir src/eap_common Common EAP functionality for server and peer
+
+
+\dir src/eap_peer EAP peer
+
+
+\dir src/eap_server EAP server
+
+
+\dir src/eapol_auth EAPOL authenticator
+
+
+\dir src/eapol_supp EAPOL supplicant
+
+
+\dir src/l2_packet Layer 2 packet interface
+
+This module defines an interface for layer 2 (link layer) packet
+sendinf and receiving. All the wrappers for supported mechanisms are
+also included here. This is used to port packet access for new
+operating systems without having to make rest of the source code
+depend on which OS network stack is used.
+
+
+\dir src/radius RADIUS
+
+RADIUS module includes RADIUS message building and parsing
+functionality and separate RADIUS client and server functions.
+
+
+\dir src/rsn_supp IEEE 802.11 RSN and WPA supplicant
+
+
+\dir src/tls Internal TLS server and client implementation
+
+This module can be used as an alternative to using an external TLS
+library.
+
+
+\dir src/utils Utility functions
+
+Independent set of helper functions that most other components
+use. This includes portability wrappers and helpers for common tasks.
+
+
+\dir src/wps Wi-Fi Protected Setup
+
+This directory includes Wi-Fi Protected Setup functions for Registrar
+(both internal in an AP and an External Registrar and
+Enrollee. Minimal UPnP and HTTP functionality is also provided for the
+functionality needed to implement Wi-Fi Protected Setup.
+
+
+\dir wpa_supplicant wpa_supplicant
+
+wpa_supplicant-specific code for configuration, control interface, and
+client management.
+
+*/
diff --git a/hostap/doc/doxygen.conf b/hostap/doc/doxygen.conf
new file mode 100644
index 0000000..06be6a5
--- /dev/null
+++ b/hostap/doc/doxygen.conf
@@ -0,0 +1,1547 @@
+# Doxyfile 1.6.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = "wpa_supplicant / hostapd"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         = 2.4
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE            =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           = doc/doxygen.warnings
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = \
+	doc \
+	hostapd \
+	wpa_supplicant \
+	wpa_supplicant/dbus \
+	eap_example \
+	src/ap \
+	src/common \
+	src/crypto \
+	src/drivers \
+	src/eap_common \
+	src/eapol_auth \
+	src/eapol_supp \
+	src/eap_peer \
+	src/eap_server \
+	src/l2_packet \
+	src/p2p \
+	src/pae \
+	src/radius \
+	src/rsn_supp \
+	src/tls \
+	src/utils \
+	src/wps
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS          = *.c *.h *.cpp *.m *.doxygen
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             = doc
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+# You can download the filter tool from
+# http://w1.fi/tools/kerneldoc2doxygen-hostap.pl
+INPUT_FILTER           = kerneldoc2doxygen-hostap.pl
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 3
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# When the SEARCHENGINE tag is enable doxygen will generate a search box for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP) or Qt help (GENERATE_QHP)
+# there is already a search function so this one should typically
+# be disabled.
+
+SEARCHENGINE           = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             = IEEE8021X_EAPOL CONFIG_CTRL_IFACE CONFIG_P2P
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS         = NO
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME           = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = NO
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = NO
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = NO
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = NO
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = NO
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+
+#---------------------------------------------------------------------------
+# Project additions
+#---------------------------------------------------------------------------
+
+# Disable autolink support due to wpa_supplicant getting unfortunately
+# auto-linked to struct wpa_supplicant due to having an underscore in the name.
+AUTOLINK_SUPPORT = FALSE
diff --git a/hostap/doc/driver_wrapper.doxygen b/hostap/doc/driver_wrapper.doxygen
new file mode 100644
index 0000000..a3b470a
--- /dev/null
+++ b/hostap/doc/driver_wrapper.doxygen
@@ -0,0 +1,180 @@
+/**
+\page driver_wrapper Driver wrapper implementation (driver.h, drivers.c)
+
+All hardware and driver dependent functionality is in separate C files
+that implement defined wrapper functions. Other parts
+of the wpa_supplicant are designed to be hardware, driver, and operating
+system independent.
+
+Driver wrappers need to implement whatever calls are used in the
+target operating system/driver for controlling wireless LAN
+devices. As an example, in case of Linux, these are mostly some glue
+code and ioctl() calls and netlink message parsing for Linux Wireless
+Extensions (WE). Since features required for WPA were added only recently to
+Linux Wireless Extensions (in version 18), some driver specific code is used
+in number of driver interface implementations. These driver dependent parts
+can be replaced with generic code in \ref driver_wext.c once the target driver
+includes full support for WE-18. After that, all Linux drivers, at
+least in theory, could use the same driver wrapper code.
+
+A driver wrapper needs to implement some or all of the functions
+defined in \ref driver.h. These functions are registered by filling struct
+\ref wpa_driver_ops with function pointers. Hardware independent parts of
+wpa_supplicant will call these functions to control the driver/wlan
+card. In addition, support for driver events is required. The event
+callback function, \ref wpa_supplicant_event(), and its parameters are
+documented in \ref driver.h. In addition, a pointer to the 'struct
+\ref wpa_driver_ops' needs to be registered in \ref drivers.c file.
+
+When porting to other operating systems, the driver wrapper should be
+modified to use the native interface of the target OS. It is possible
+that some extra requirements for the interface between the driver
+wrapper and generic wpa_supplicant code are discovered during porting
+to a new operating system. These will be addressed on case by case
+basis by modifying the interface and updating the other driver
+wrappers for this. The goal is to avoid changing this interface
+without very good reasons in order to limit the number of changes
+needed to other wrappers and hardware independent parts of
+wpa_supplicant. When changes are required, recommended way is to
+make them in backwards compatible way that allows existing driver
+interface implementations to be compiled without any modification.
+
+Generic Linux Wireless Extensions functions are implemented in
+\ref driver_wext.c. All Linux driver wrappers can use these when the kernel
+driver supports the generic ioctl()s and wireless events. Driver
+specific functions are implemented in separate C files, e.g.,
+\ref driver_hostap.c. These files need to define struct \ref wpa_driver_ops
+entry that will be used in \ref wpa_supplicant.c when calling driver
+functions. struct \ref wpa_driver_ops entries are registered in \ref drivers.c.
+
+In general, it is likely to be useful to first take a look at couple
+of driver interface examples before starting on implementing a new
+one. \ref driver_hostap.c and \ref driver_wext.c include a complete
+implementation for Linux drivers that use wpa_supplicant-based control
+of WPA IE and roaming. \ref driver_ndis.c (with help from \ref driver_ndis_.c)
+is an example of a complete interface for Windows NDIS interface for
+drivers that generate WPA IE themselves and decide when to roam. These
+example implementations include full support for all security modes.
+
+
+\section driver_req Driver requirements for WPA
+
+WPA introduces new requirements for the device driver. At least some
+of these need to be implemented in order to provide enough support for
+wpa_supplicant.
+
+\subsection driver_tkip_ccmp TKIP/CCMP
+
+WPA requires that the pairwise cipher suite (encryption algorithm for
+unicast data packets) is TKIP or CCMP. These are new encryption
+protocols and thus, the driver will need to be modified to support
+them. Depending on the used wlan hardware, some parts of these may be
+implemented by the hardware/firmware.
+
+Specification for both TKIP and CCMP is available from IEEE (IEEE
+802.11i amendment). Fully functional, hardware independent
+implementation of both encryption protocols is also available in Host
+AP driver (driver/modules/hostap_{tkip,ccmp}.c). In addition, Linux 2.6
+kernel tree has generic implementations for WEP, TKIP, and CCMP that can
+be used in Linux drivers.
+
+The driver will also need to provide configuration mechanism to allow
+user space programs to configure TKIP and CCMP. Linux Wireless Extensions
+v18 added support for configuring these algorithms and
+individual/non-default keys. If the target kernel does not include WE-18,
+private ioctls can be used to provide similar functionality.
+
+\subsection driver_roaming Roaming control and scanning support
+
+wpa_supplicant can optionally control AP selection based on the
+information received from Beacon and/or Probe Response frames
+(ap_scan=1 mode in configuration). This means that the driver should
+support external control for scan process. In case of Linux, use of
+new Wireless Extensions scan support (i.e., 'iwlist wlan0 scan') is
+recommended. The current driver wrapper (\ref driver_wext.c) uses this for
+scan results.
+
+Scan results must also include the WPA information element. Support for
+this was added in WE-18. With older versions, a custom event can be used
+to provide the full WPA IE (including element id and length) as a hex
+string that is included in the scan results.
+
+wpa_supplicant needs to also be able to request the driver to
+associate with a specific BSS. Current Host AP driver and matching
+\ref driver_hostap.c wrapper uses following sequence for this
+request. Similar/identical mechanism should be usable also with other
+drivers.
+
+- set WPA IE for AssocReq with private ioctl
+- set SSID with SIOCSIWESSID
+- set channel/frequency with SIOCSIWFREQ
+- set BSSID with SIOCSIWAP
+  (this last ioctl will trigger the driver to request association)
+
+\subsection driver_wpa_ie WPA IE generation
+
+wpa_supplicant selects which cipher suites and key management suites
+are used. Based on this information, it generates a WPA IE. This is
+provided to the driver interface in the associate call. This does not
+match with Windows NDIS drivers which generate the WPA IE
+themselves.
+
+wpa_supplicant allows Windows NDIS-like behavior by providing the
+selected cipher and key management suites in the associate call. If
+the driver generates its own WPA IE and that differs from the one
+generated by wpa_supplicant, the driver has to inform wpa_supplicant
+about the used WPA IE (i.e., the one it used in (Re)Associate
+Request). This notification is done using EVENT_ASSOCINFO event (see
+\ref driver.h). wpa_supplicant is normally configured to use
+ap_scan=2 mode with drivers that control WPA IE generation and roaming.
+
+\subsection driver_events Driver events
+
+wpa_supplicant needs to receive event callbacks when certain events
+occur (association, disassociation, Michael MIC failure, scan results
+available, PMKSA caching candidate). These events and the callback
+details are defined in \ref driver.h (\ref wpa_supplicant_event() function
+and enum \ref wpa_event_type).
+
+On Linux, association and disassociation can use existing Wireless
+Extensions event that is reporting new AP with SIOCGIWAP
+event. Similarly, completion of a scan can be reported with SIOCGIWSCAN
+event.
+
+Michael MIC failure event was added in WE-18. Older versions of Wireless
+Extensions will need to use a custom event. Host AP driver used a custom
+event with following contents: MLME-MICHAELMICFAILURE.indication(keyid=#
+broadcast/unicast addr=addr2). This is the recommended format until
+the driver can be moved to use WE-18 mechanism.
+
+\subsection driver_wext_summary Summary of Linux Wireless Extensions use
+
+AP selection depends on ap_scan configuration:
+
+ap_scan=1:
+
+- wpa_supplicant requests scan with SIOCSIWSCAN
+- driver reports scan complete with wireless event SIOCGIWSCAN
+- wpa_supplicant reads scan results with SIOCGIWSCAN (multiple call if
+  a larget buffer is needed)
+- wpa_supplicant decides which AP to use based on scan results
+- wpa_supplicant configures driver to associate with the selected BSS
+  (SIOCSIWMODE, SIOCSIWGENIE, SIOCSIWAUTH, SIOCSIWFREQ,
+   SIOCSIWESSID, SIOCSIWAP)
+
+ap_scan=2:
+
+- wpa_supplicant configures driver to associate with an SSID
+  (SIOCSIWMODE, SIOCSIWGENIE, SIOCSIWAUTH, SIOCSIWESSID)
+
+
+After this, both modes use similar steps:
+
+- optionally (or required for drivers that generate WPA/RSN IE for
+  (Re)AssocReq), driver reports association parameters (AssocReq IEs)
+  with wireless event IWEVASSOCREQIE (and optionally IWEVASSOCRESPIE)
+- driver reports association with wireless event SIOCGIWAP
+- wpa_supplicant takes care of EAPOL frame handling (validating
+  information from associnfo and if needed, from scan results if WPA/RSN
+  IE from the Beacon frame is not reported through associnfo)
+*/
diff --git a/hostap/doc/eap.doxygen b/hostap/doc/eap.doxygen
new file mode 100644
index 0000000..fc7ea26
--- /dev/null
+++ b/hostap/doc/eap.doxygen
@@ -0,0 +1,87 @@
+/**
+\page eap_peer_module EAP peer implementation
+
+Extensible Authentication Protocol (EAP) is an authentication framework
+defined in RFC 3748. wpa_supplicant uses a separate code module for EAP
+peer implementation. This module was designed to use only a minimal set
+of direct function calls (mainly, to debug/event functions) in order for
+it to be usable in other programs. The design of the EAP
+implementation is based loosely on RFC 4137. The state machine is
+defined in this RFC and so is the interface between the peer state
+machine and methods. As such, this RFC provides useful information for
+understanding the EAP peer implementation in wpa_supplicant.
+
+Some of the terminology used in EAP state machine is referring to
+EAPOL (IEEE 802.1X), but there is no strict requirement on the lower
+layer being IEEE 802.1X if EAP module is built for other programs than
+wpa_supplicant. These terms should be understood to refer to the
+lower layer as defined in RFC 4137.
+
+
+\section adding_eap_methods Adding EAP methods
+
+Each EAP method is implemented as a separate module, usually as one C
+file named eap_<name of the method>.c, e.g., \ref eap_md5.c. All EAP
+methods use the same interface between the peer state machine and
+method specific functions. This allows new EAP methods to be added
+without modifying the core EAP state machine implementation.
+
+New EAP methods need to be registered by adding them into the build
+(Makefile) and the EAP method registration list in the
+\ref eap_peer_register_methods() function of \ref eap_methods.c. Each EAP
+method should use a build-time configuration option, e.g., EAP_TLS, in
+order to make it possible to select which of the methods are included
+in the build.
+
+EAP methods must implement the interface defined in \ref eap_i.h. struct
+eap_method defines the needed function pointers that each EAP method
+must provide. In addition, the EAP type and name are registered using
+this structure. This interface is based on section 4.4 of RFC 4137.
+
+It is recommended that the EAP methods would use generic helper
+functions, \ref eap_msg_alloc() and \ref eap_hdr_validate() when processing
+messages. This allows code sharing and can avoid missing some of the
+needed validation steps for received packets. In addition, these
+functions make it easier to change between expanded and legacy EAP
+header, if needed.
+
+When adding an EAP method that uses a vendor specific EAP type
+(Expanded Type as defined in RFC 3748, Chapter 5.7), the new method
+must be registered by passing vendor id instead of EAP_VENDOR_IETF to
+\ref eap_peer_method_alloc(). These methods must not try to emulate
+expanded types by registering a legacy EAP method for type 254. See
+\ref eap_vendor_test.c for an example of an EAP method implementation that
+is implemented as an expanded type.
+
+
+\section used_eap_library Using EAP implementation as a library
+
+The Git repository has an eap_example directory that contains an
+example showing how EAP peer and server code from wpa_supplicant and
+hostapd can be used as a library. The example program initializes both
+an EAP server and an EAP peer entities and then runs through an
+EAP-PEAP/MSCHAPv2 authentication.
+
+\ref eap_example_peer.c shows the initialization and glue code needed to
+control the EAP peer implementation. \ref eap_example_server.c does the
+same for EAP server. \ref eap_example.c is an example that ties in both the
+EAP server and client parts to allow an EAP authentication to be
+shown.
+
+In this example, the EAP messages are passed between the server and
+the peer are passed by direct function calls within the same process.
+In practice, server and peer functionalities would likely reside in
+separate devices and the EAP messages would be transmitted between the
+devices based on an external protocol. For example, in IEEE 802.11
+uses IEEE 802.1X EAPOL state machines to control the transmission of
+EAP messages and WiMax supports optional PMK EAP authentication
+mechanism that transmits EAP messages as defined in IEEE 802.16e.
+
+The EAP library links in number of helper functions from \ref src/utils and
+\ref src/crypto directories. Most of these are suitable as-is, but it may
+be desirable to replace the debug output code in \ref src/utils/wpa_debug.c
+by dropping this file from the library and re-implementing the
+functions there in a way that better fits in with the main
+application.
+
+*/
diff --git a/hostap/doc/eap_server.doxygen b/hostap/doc/eap_server.doxygen
new file mode 100644
index 0000000..f60ac79
--- /dev/null
+++ b/hostap/doc/eap_server.doxygen
@@ -0,0 +1,56 @@
+/**
+\page eap_server_module EAP server implementation
+
+Extensible Authentication Protocol (EAP) is an authentication framework
+defined in RFC 3748. hostapd uses a separate code module for EAP server
+implementation. This module was designed to use only a minimal set of
+direct function calls (mainly, to debug/event functions) in order for
+it to be usable in other programs. The design of the EAP
+implementation is based loosely on RFC 4137. The state machine is
+defined in this RFC and so is the interface between the server state
+machine and methods. As such, this RFC provides useful information for
+understanding the EAP server implementation in hostapd.
+
+Some of the terminology used in EAP state machine is referring to
+EAPOL (IEEE 802.1X), but there is no strict requirement on the lower
+layer being IEEE 802.1X if EAP module is built for other programs than
+wpa_supplicant. These terms should be understood to refer to the
+lower layer as defined in RFC 4137.
+
+
+\section adding_eap_methods Adding EAP methods
+
+Each EAP method is implemented as a separate module, usually as one C
+file named eap_server_<name of the method>.c, e.g., \ref eap_server_md5.c. All EAP
+methods use the same interface between the server state machine and
+method specific functions. This allows new EAP methods to be added
+without modifying the core EAP state machine implementation.
+
+New EAP methods need to be registered by adding them into the build
+(Makefile) and the EAP method registration list in the
+\ref eap_server_register_methods() function of \ref eap_server_methods.c. Each EAP
+method should use a build-time configuration option, e.g., EAP_TLS, in
+order to make it possible to select which of the methods are included
+in the build.
+
+EAP methods must implement the interface defined in \ref eap_i.h. struct
+\ref eap_method defines the needed function pointers that each EAP method
+must provide. In addition, the EAP type and name are registered using
+this structure. This interface is based on section 4.4 of RFC 4137.
+
+It is recommended that the EAP methods would use generic helper
+functions, \ref eap_msg_alloc() and \ref eap_hdr_validate() when processing
+messages. This allows code sharing and can avoid missing some of the
+needed validation steps for received packets. In addition, these
+functions make it easier to change between expanded and legacy EAP
+header, if needed.
+
+When adding an EAP method that uses a vendor specific EAP type
+(Expanded Type as defined in RFC 3748, Chapter 5.7), the new method
+must be registered by passing vendor id instead of EAP_VENDOR_IETF to
+\ref eap_server_method_alloc(). These methods must not try to emulate
+expanded types by registering a legacy EAP method for type 254. See
+\ref eap_server_vendor_test.c for an example of an EAP method implementation that
+is implemented as an expanded type.
+
+*/
diff --git a/hostap/doc/hostapd.fig b/hostap/doc/hostapd.fig
new file mode 100644
index 0000000..ea4ab3a
--- /dev/null
+++ b/hostap/doc/hostapd.fig
@@ -0,0 +1,264 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+6 1875 4050 2925 4350
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 1875 4050 2925 4050 2925 4350 1875 4350 1875 4050
+4 0 0 50 -1 0 12 0.0000 4 180 735 2025 4275 l2_packet\001
+-6
+6 4725 1200 5925 1500
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 4725 1200 5925 1200 5925 1500 4725 1500 4725 1200
+4 0 0 50 -1 0 12 0.0000 4 135 1005 4800 1425 GUI frontend\001
+-6
+6 6000 2700 7200 3225
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 6000 2700 7200 2700 7200 3225 6000 3225 6000 2700
+4 0 0 50 -1 0 12 0.0000 4 135 975 6075 2925 WPA/WPA2\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 3150 state machine\001
+-6
+6 6000 4950 7200 5475
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 6000 4950 7200 4950 7200 5475 6000 5475 6000 4950
+4 0 0 50 -1 0 12 0.0000 4 135 360 6075 5175 EAP\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 5400 state machine\001
+-6
+6 4350 3900 5025 4425
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 4350 3900 5025 3900 5025 4425 4350 4425 4350 3900
+4 0 0 50 -1 0 12 0.0000 4 105 420 4500 4125 event\001
+4 0 0 50 -1 0 12 0.0000 4 180 315 4500 4350 loop\001
+-6
+6 4275 2550 5100 2850
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 4275 2550 5100 2550 5100 2850 4275 2850 4275 2550
+4 0 0 50 -1 0 12 0.0000 4 135 450 4425 2775 ctrl i/f\001
+-6
+6 6000 3900 7200 4425
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 6000 3900 7200 3900 7200 4425 6000 4425 6000 3900
+4 0 0 50 -1 0 12 0.0000 4 135 600 6075 4125 EAPOL\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 4350 state machine\001
+-6
+6 2775 3150 4050 3450
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 2775 3150 4050 3150 4050 3450 2775 3450 2775 3150
+4 0 0 50 -1 0 12 0.0000 4 180 990 2925 3375 configuration\001
+-6
+6 3450 1200 4575 1500
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 3450 1200 4575 1200 4575 1500 3450 1500 3450 1200
+4 0 0 50 -1 0 12 0.0000 4 180 870 3600 1425 hostapd_cli\001
+-6
+6 3525 7800 5775 8100
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 3525 7800 5775 7800 5775 8100 3525 8100 3525 7800
+4 0 0 50 -1 0 12 0.0000 4 135 2145 3600 8025 kernel network device driver\001
+-6
+6 4275 6000 5100 6300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 4275 6000 5100 6000 5100 6300 4275 6300 4275 6000
+4 0 0 50 -1 0 12 0.0000 4 135 630 4350 6225 driver i/f\001
+-6
+6 8175 4725 9225 5025
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8175 4725 9225 4725 9225 5025 8175 5025 8175 4725
+4 0 0 50 -1 0 12 0.0000 4 135 735 8250 4950 EAP-TLS\001
+-6
+6 9300 4725 10350 5025
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 9300 4725 10350 4725 10350 5025 9300 5025 9300 4725
+4 0 0 50 -1 0 12 0.0000 4 135 810 9375 4950 EAP-MD5\001
+-6
+6 8175 5100 9225 5400
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8175 5100 9225 5100 9225 5400 8175 5400 8175 5100
+4 0 0 50 -1 0 12 0.0000 4 135 885 8250 5325 EAP-PEAP\001
+-6
+6 9300 5100 10350 5400
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 9300 5100 10350 5100 10350 5400 9300 5400 9300 5100
+4 0 0 50 -1 0 12 0.0000 4 135 840 9375 5325 EAP-TTLS\001
+-6
+6 8175 5475 9225 5775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8175 5475 9225 5475 9225 5775 8175 5775 8175 5475
+4 0 0 50 -1 0 12 0.0000 4 135 780 8250 5700 EAP-GTC\001
+-6
+6 8175 5850 9225 6150
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8175 5850 9225 5850 9225 6150 8175 6150 8175 5850
+4 0 0 50 -1 0 12 0.0000 4 135 750 8250 6075 EAP-SIM\001
+-6
+6 8175 6225 9225 6525
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8175 6225 9225 6225 9225 6525 8175 6525 8175 6225
+4 0 0 50 -1 0 12 0.0000 4 135 765 8250 6450 EAP-PSK\001
+-6
+6 9300 5850 10350 6150
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 9300 5850 10350 5850 10350 6150 9300 6150 9300 5850
+4 0 0 50 -1 0 12 0.0000 4 135 825 9375 6075 EAP-AKA\001
+-6
+6 9300 5475 10350 5775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 9300 5475 10350 5475 10350 5775 9300 5775 9300 5475
+4 0 0 50 -1 0 12 0.0000 4 135 795 9375 5700 EAP-PAX\001
+-6
+6 8175 6600 9675 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8175 6600 9675 6600 9675 6900 8175 6900 8175 6600
+4 0 0 50 -1 0 12 0.0000 4 135 1365 8250 6825 EAP-MSCHAPv2\001
+-6
+6 8700 3450 9375 3750
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8700 3450 9375 3450 9375 3750 8700 3750 8700 3450
+4 0 0 50 -1 0 12 0.0000 4 150 480 8775 3675 crypto\001
+-6
+6 9600 3450 10275 3750
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 9600 3450 10275 3450 10275 3750 9600 3750 9600 3450
+4 0 0 50 -1 0 12 0.0000 4 135 315 9750 3675 TLS\001
+-6
+6 6000 5775 7200 6300
+6 6000 5775 7200 6300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 6000 5775 7200 5775 7200 6300 6000 6300 6000 5775
+4 0 0 50 -1 0 12 0.0000 4 135 690 6075 6000 RADIUS\001
+-6
+4 0 0 50 -1 0 12 0.0000 4 90 480 6075 6225 server\001
+-6
+6 8100 2250 8925 2775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8100 2250 8925 2250 8925 2775 8100 2775 8100 2250
+4 0 0 50 -1 0 12 0.0000 4 135 690 8175 2475 RADIUS\001
+4 0 0 50 -1 0 12 0.0000 4 135 420 8175 2700 client\001
+-6
+6 3150 5475 4425 5775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 3150 5475 4425 5475 4425 5775 3150 5775 3150 5475
+4 0 0 50 -1 0 12 0.0000 4 135 990 3300 5700 driver events\001
+-6
+6 1950 5550 2625 6075
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 1950 5550 2625 5550 2625 6075 1950 6075 1950 5550
+4 0 0 50 -1 0 12 0.0000 4 135 540 2025 5775 Station\001
+4 0 0 50 -1 0 12 0.0000 4 135 375 2025 6000 table\001
+-6
+6 1875 4725 2925 5250
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 1875 4725 2925 4725 2925 5250 1875 5250 1875 4725
+4 0 0 50 -1 0 12 0.0000 4 135 960 1950 4950 IEEE 802.11\001
+4 0 0 50 -1 0 12 0.0000 4 135 555 1950 5175 MLME\001
+-6
+2 1 1 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2
+	 1275 4200 1875 4200
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+	 4500 2550 3900 1500
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+	 4800 2550 5400 1500
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 2925 4200 4350 4200
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 5025 3900 6000 3000
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 5025 4200 6000 4200
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 4650 6000 4650 4425
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6600 4425 6600 4950
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6600 3225 6600 3900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 7200 5250 8100 5250
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 9075 4425 9075 3750
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 7200 3000 8700 3525
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 4650 3900 4650 2850
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 7200 4125 8700 3675
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6000 4350 5025 6000
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6000 3150 4875 6000
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 1500 2100 10800 2100 10800 7500 1500 7500 1500 2100
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 9900 4425 9900 3750
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 1
+	 4350 3900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 4350 3900 4050 3450
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 4350 4425 4050 5475
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+	 2250 7200 4200 7800
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+	 7200 7200 5100 7800
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 2775 6900 3675 6900 3675 7200 2775 7200 2775 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 3750 6900 4650 6900 4650 7200 3750 7200 3750 6900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+	 2250 6900 2250 6600 7200 6600 7200 6900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 3225 6900 3225 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 4200 6900 4200 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 5175 6900 5175 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6150 6900 6150 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 4650 6600 4650 6300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 1800 6900 2700 6900 2700 7200 1800 7200 1800 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 4725 6900 5625 6900 5625 7200 4725 7200 4725 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 5700 6900 6600 6900 6600 7200 5700 7200 5700 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 6675 6900 7800 6900 7800 7200 6675 7200 6675 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8100 6975 10425 6975 10425 4425 8100 4425 8100 6975
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6600 5475 6600 5775
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 5025 4425 6000 5775
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 4800 3900 5925 2550 8100 2550
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 7200 3900 8475 2775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 9450 2250 10425 2250 10425 2775 9450 2775 9450 2250
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 8925 2475 9450 2475
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 2325 5550 2325 5250
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 2925 4950 4350 4275
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 2850 4725 5775 2400 8100 2400
+4 0 0 50 -1 0 12 0.0000 4 135 915 375 3975 EAPOL and\001
+4 0 0 50 -1 0 12 0.0000 4 180 630 375 4200 pre-auth\001
+4 0 0 50 -1 0 12 0.0000 4 180 810 375 4425 ethertypes\001
+4 0 0 50 -1 0 12 0.0000 4 135 1050 375 4650 from/to kernel\001
+4 0 0 50 -1 0 12 0.0000 4 135 1920 3675 1875 frontend control interface\001
+4 0 0 50 -1 2 14 0.0000 4 195 720 1637 2371 hostapd\001
+4 0 0 50 -1 0 12 0.0000 4 180 600 3825 7125 prism54\001
+4 0 0 50 -1 0 12 0.0000 4 180 510 1875 7125 hostap\001
+4 0 0 50 -1 0 12 0.0000 4 135 600 2850 7125 nl80211\001
+4 0 0 50 -1 0 12 0.0000 4 135 270 4800 7125 bsd\001
+4 0 0 50 -1 0 12 0.0000 4 105 300 6750 7125 test\001
+4 0 0 50 -1 0 12 0.0000 4 135 420 5775 7125 wired\001
+4 0 0 50 -1 0 12 0.0000 4 135 1050 8700 4650 EAP methods\001
+4 0 0 50 -1 0 12 0.0000 4 135 690 9525 2475 RADIUS\001
+4 0 0 50 -1 0 12 0.0000 4 180 825 9525 2700 accounting\001
diff --git a/hostap/doc/hostapd_ctrl_iface.doxygen b/hostap/doc/hostapd_ctrl_iface.doxygen
new file mode 100644
index 0000000..4d2bac8
--- /dev/null
+++ b/hostap/doc/hostapd_ctrl_iface.doxygen
@@ -0,0 +1,66 @@
+/**
+\page hostapd_ctrl_iface_page hostapd control interface
+
+hostapd implements a control interface that can be used by
+external programs to control the operations of the hostapd
+daemon and to get status information and event notifications. There is
+a small C library, in a form of a single C file, \ref wpa_ctrl.c, that
+provides helper functions to facilitate the use of the control
+interface. External programs can link this file into them and then use
+the library functions documented in \ref wpa_ctrl.h to interact with
+wpa_supplicant. This library can also be used with C++. \ref hostapd_cli.c
+is an example program using this library.
+
+There are multiple mechanisms for inter-process communication. For
+example, Linux version of hostapd is using UNIX domain sockets for the
+control interface. The use of the functions defined in \ref wpa_ctrl.h can
+be used to hide the details of the used IPC from external programs.
+
+
+\section using_ctrl_iface Using the control interface
+
+External programs, e.g., a GUI or a configuration utility, that need to
+communicate with hostapd should link in \ref wpa_ctrl.c. This
+allows them to use helper functions to open connection to the control
+interface with \ref wpa_ctrl_open() and to send commands with
+\ref wpa_ctrl_request().
+
+hostapd uses the control interface for two types of communication:
+commands and unsolicited event messages. Commands are a pair of
+messages, a request from the external program and a response from
+hostapd. These can be executed using \ref wpa_ctrl_request().
+Unsolicited event messages are sent by hostapd to the control
+interface connection without specific request from the external program
+for receiving each message. However, the external program needs to
+attach to the control interface with \ref wpa_ctrl_attach() to receive these
+unsolicited messages.
+
+If the control interface connection is used both for commands and
+unsolicited event messages, there is potential for receiving an
+unsolicited message between the command request and response.
+\ref wpa_ctrl_request() caller will need to supply a callback, msg_cb,
+for processing these messages. Often it is easier to open two
+control interface connections by calling \ref wpa_ctrl_open() twice and
+then use one of the connections for commands and the other one for
+unsolicited messages. This way command request/response pairs will
+not be broken by unsolicited messages. \ref wpa_cli.c is an example of how
+to use only one connection for both purposes and wpa_gui demonstrates
+how to use two separate connections.
+
+Once the control interface connection is not needed anymore, it should
+be closed by calling \ref wpa_ctrl_close(). If the connection was used for
+unsolicited event messages, it should be first detached by calling
+\ref wpa_ctrl_detach().
+
+
+\section ctrl_iface_cmds Control interface commands
+
+Following commands can be used with \ref wpa_ctrl_request():
+
+\subsection ctrl_iface_PING PING
+
+This command can be used to test whether hostapd is replying
+to the control interface commands. The expected reply is \c PONG if the
+connection is open and hostapd is processing commands.
+
+*/
diff --git a/hostap/doc/mainpage.doxygen b/hostap/doc/mainpage.doxygen
new file mode 100644
index 0000000..329afea
--- /dev/null
+++ b/hostap/doc/mainpage.doxygen
@@ -0,0 +1,95 @@
+/**
+\mainpage Developers' documentation for wpa_supplicant and hostapd
+
+The goal of this documentation and comments in the source code is to
+give enough information for other developers to understand how
+wpa_supplicant and hostapd have been implemented, how they can be
+modified, how new drivers can be supported, and how the source code
+can be ported to other operating systems. If any information is
+missing, feel free to contact Jouni Malinen <j@w1.fi> for more
+information. Contributions as patch files are also very welcome at the
+same address. Please note that this software is licensed under the
+BSD license (the one with advertisement clause removed). All
+contributions to wpa_supplicant and hostapd are expected to use
+compatible licensing terms.
+
+The source code and read-only access to the combined wpa_supplicant
+and hostapd Git repository is available from the project home page at
+http://w1.fi/wpa_supplicant/. This developers' documentation is also
+available as a PDF file from
+http://w1.fi/wpa_supplicant/wpa_supplicant-devel.pdf .
+
+
+\section _wpa_supplicant wpa_supplicant
+
+wpa_supplicant is a WPA Supplicant for Linux, BSD and Windows with
+support for WPA and WPA2 (IEEE 802.11i / RSN). Supplicant is the IEEE
+802.1X/WPA component that is used in the client stations. It
+implements key negotiation with a WPA Authenticator and it can optionally
+control roaming and IEEE 802.11 authentication/association of the wlan
+driver.
+
+The design goal for wpa_supplicant was to use hardware, driver, and
+OS independent, portable C code for all WPA functionality. The source
+code is divided into separate C files as shown on the \ref
+code_structure "code structure page". All hardware/driver specific
+functionality is in separate files that implement a \ref
+driver_wrapper "well-defined driver API". Information about porting
+to different target boards and operating systems is available on
+the \ref porting "porting page".
+
+EAPOL (IEEE 802.1X) state machines are implemented as a separate
+module that interacts with \ref eap_peer_module "EAP peer implementation".
+In addition to programs aimed at normal production use,
+wpa_supplicant source tree includes number of \ref testing_tools
+"testing and development tools" that make it easier to test the
+programs without having to setup a full test setup with wireless
+cards. These tools can also be used to implement automatic test
+suites.
+
+wpa_supplicant implements a
+\ref ctrl_iface_page "control interface" that can be used by
+external programs to control the operations of the wpa_supplicant
+daemon and to get status information and event notifications. There is
+a small C library that provides helper functions to facilitate the use of the
+control interface. This library can also be used with C++.
+
+\image html _wpa_supplicant.png "wpa_supplicant modules"
+\image latex _wpa_supplicant.eps "wpa_supplicant modules" width=15cm
+
+
+\section _hostapd hostapd
+
+hostapd includes IEEE 802.11 access point management (authentication /
+association), IEEE 802.1X/WPA/WPA2 Authenticator, EAP server, and
+RADIUS authentication server functionality. It can be build with
+various configuration option, e.g., a standalone AP management
+solution or a RADIUS authentication server with support for number of
+EAP methods.
+
+The design goal for hostapd was to use hardware, driver, and
+OS independent, portable C code for all WPA functionality. The source
+code is divided into separate C files as shown on the \ref
+code_structure "code structure page". All hardware/driver specific
+functionality is in separate files that implement a \ref
+driver_wrapper "well-defined driver API". Information about porting
+to different target boards and operating systems is available on
+the \ref porting "porting page".
+
+EAPOL (IEEE 802.1X) state machines are implemented as a separate
+module that interacts with \ref eap_server_module "EAP server implementation".
+Similarly, RADIUS authentication server is in its own separate module.
+Both IEEE 802.1X and RADIUS authentication server can use EAP server
+functionality.
+
+hostapd implements a \ref hostapd_ctrl_iface_page "control interface"
+that can be used by external programs to control the operations of the
+hostapdt daemon and to get status information and event notifications.
+There is a small C library that provides helper functions to facilitate
+the use of the control interface. This library can also be used with
+C++.
+
+\image html hostapd.png "hostapd modules"
+\image latex hostapd.eps "hostapd modules" width=15cm
+
+*/
diff --git a/hostap/doc/p2p.doxygen b/hostap/doc/p2p.doxygen
new file mode 100644
index 0000000..c1a81db
--- /dev/null
+++ b/hostap/doc/p2p.doxygen
@@ -0,0 +1,471 @@
+/**
+\page p2p Wi-Fi Direct - P2P module
+
+Wi-Fi Direct functionality is implemented any many levels in the WLAN
+stack from low-level driver operations to high-level GUI design. This
+document covers the parts that can be user by wpa_supplicant. However,
+it should be noted that alternative designs are also possible, so some
+of the functionality may reside in other components in the system.
+
+The driver (or WLAN firmware/hardware) is expected to handle low-level
+operations related to P2P Power Management and channel scheduling. In
+addition, support for virtual network interface and data frame
+processing is done inside the driver. Configuration for these
+low-level operations is defined in the driver interface:
+src/drivers/driver.h. This defines both the commands and events used to
+interact with the driver.
+
+P2P module implements higher layer functionality for management P2P
+groups. It takes care of Device Discovery, Service Discovery, Group
+Owner Negotiation, P2P Invitation. In addition, it maintains
+information about neighboring P2P Devices. This module could be used
+in designs that do not use wpa_supplicant and it could also reside
+inside the driver/firmware component. P2P module API is defined in
+\ref src/p2p/p2p.h.
+
+Provisioning step of Group Formation is implemented using WPS
+(\ref src/wps/wps.h).
+
+wpa_supplicant includes code in interact with both the P2P module
+(\ref wpa_supplicant/p2p_supplicant.c) and WPS
+(\ref wpa_supplicant/wps_supplicant.c). The driver operations are passed
+through these files, i.e., core P2P or WPS code does not interact
+directly with the driver interface.
+
+
+\section p2p_arch P2P architecture
+
+P2P functionality affects many areas of the system architecture. This
+section shows couple of examples on the location of main P2P
+components. In the diagrams below, green arrows are used to show
+communication paths from the P2P module to upper layer management
+functionality and all the way to a GUI that user could use to manage
+P2P connections. Blue arrows show the path taken for lower layer
+operations. Glue code is used to bind the P2P module API to the rest
+of the system to provide access both towards upper and lower layer
+functionality.
+
+\subsection p2p_arch_mac80211 P2P architecture with Linux/mac80211/ath9k
+
+An architecture where the P2P module resides inside the
+wpa_supplicant process is used with Linux mac80211-based drivers,
+e.g., ath9k. The following diagram shows the main components related
+to P2P functionality in such an architecture.
+
+\image html p2p_arch.png "P2P module within wpa_supplicant"
+\image latex p2p_arch.eps "P2P module within wpa_supplicant" width=15cm
+
+\subsection p2p_arch_umac P2P architecture with UMAC
+
+The following diagram shows the main components related to P2P
+functionality in an architecture where the P2P module resides inside
+the kernel IEEE 802.11 stack (UMAC in the figure).
+
+\image html p2p_arch2.png "P2P module in kernel
+\image latex p2p_arch2.eps "P2P module in kernel" width=15cm
+
+
+\section p2p_module P2P module
+
+P2P module manages discovery and group formation with a single state
+machine, i.e., only a single operation per device can be in progress
+at any given time. The following diagram describes the P2P state
+machine. For clarity, it does not include state transitions on
+operation timeouts to the IDLE state. The states that are marked with
+dotted ellipse are listed for clarity to describe the protocol
+functionality for Device Discovery phase, but are not used in the
+implementation (the SEARCH state is used to manage the initial Scan
+and the alternating Listen and Search states within Find).
+
+\image html p2p_sm.png "P2P module state machine"
+\image latex p2p_sm.eps "P2P module state machine" width=15cm
+
+\subsection p2p_module_api P2P module API
+
+P2P module API is defined in \ref src/p2p/p2p.h. The API consists of
+functions for requesting operations and for providing event
+notifications. Similar set of callback functions are configured with
+struct p2p_config to provide callback functions that P2P module can
+use to request operations and to provide event notifications. In
+addition, there are number of generic helper functions that can be
+used for P2P related operations.
+
+These are the main functions for an upper layer management entity to
+request P2P operations:
+- \ref p2p_find()
+- \ref p2p_stop_find()
+- \ref p2p_listen()
+- \ref p2p_connect()
+- \ref p2p_reject()
+- \ref p2p_prov_disc_req()
+- \ref p2p_sd_request()
+- \ref p2p_sd_cancel_request()
+- \ref p2p_sd_response()
+- \ref p2p_sd_service_update()
+- \ref p2p_invite()
+
+These are the main callback functions for P2P module to provide event
+notifications to the upper layer management entity:
+
+- \ref p2p_config::dev_found()
+- \ref p2p_config::go_neg_req_rx()
+- \ref p2p_config::go_neg_completed()
+- \ref p2p_config::sd_request()
+- \ref p2p_config::sd_response()
+- \ref p2p_config::prov_disc_req()
+- \ref p2p_config::prov_disc_resp()
+- \ref p2p_config::invitation_process()
+- \ref p2p_config::invitation_received()
+- \ref p2p_config::invitation_result()
+
+The P2P module uses following functions to request lower layer driver
+operations:
+
+- \ref p2p_config::p2p_scan()
+- \ref p2p_config::send_probe_resp()
+- \ref p2p_config::send_action()
+- \ref p2p_config::send_action_done()
+- \ref p2p_config::start_listen()
+- \ref p2p_config::stop_listen()
+
+Events from lower layer driver operations are delivered to the P2P
+module with following functions:
+
+- \ref p2p_probe_req_rx()
+- \ref p2p_rx_action()
+- \ref p2p_scan_res_handler()
+- \ref p2p_scan_res_handled()
+- \ref p2p_send_action_cb()
+- \ref p2p_listen_cb()
+
+In addition to the per-device state, the P2P module maintains
+per-group state for group owners. This is initialized with a call to
+p2p_group_init() when a group is created and deinitialized with
+p2p_group_deinit(). The upper layer GO management entity uses
+following functions to interact with the P2P per-group state:
+
+- \ref p2p_group_notif_assoc()
+- \ref p2p_group_notif_disassoc()
+- \ref p2p_group_notif_formation_done()
+- \ref p2p_group_match_dev_type()
+
+The P2P module will use following callback function to update P2P IE
+for GO Beacon and Probe Response frames:
+
+- \ref p2p_group_config::ie_update()
+
+
+\section p2p_driver P2P driver operations (low-level interface)
+
+The following driver wrapper functions are needed for P2P in addition
+to the standard station/AP mode operations when the P2P module resides
+within wpa_supplicant:
+- \ref wpa_driver_ops::if_add()
+- \ref wpa_driver_ops::if_remove()
+- \ref wpa_driver_ops::remain_on_channel()
+- \ref wpa_driver_ops::cancel_remain_on_channel()
+- \ref wpa_driver_ops::send_action()
+- \ref wpa_driver_ops::probe_req_report()
+
+The following driver wrapper events are needed for P2P in addition to
+the standard station/AP mode events when the P2P module resides within
+wpa_supplicant:
+- \ref wpa_event_type::EVENT_RX_MGMT
+- \ref wpa_event_type::EVENT_REMAIN_ON_CHANNEL
+- \ref wpa_event_type::EVENT_CANCEL_REMAIN_ON_CHANNEL
+- \ref wpa_event_type::EVENT_RX_PROBE_REQ
+
+
+\section p2p_go_neg P2P device discovery and group formation
+
+This section shows an example sequence of operations that can be used
+to implement P2P device discovery and group formation. The function
+calls are described based on the P2P module API. The exact design for
+the glue code outside the P2P module depends on the architecture used
+in the system.
+
+An upper layer management entity starts P2P device discovery by
+calling \ref p2p_find(). The P2P module start the discovery by requesting a
+full scan to be completed by calling \ref p2p_config::p2p_scan(). Results
+from the scan will be reported by calling \ref p2p_scan_res_handler() and
+after last result, the scan result processing is terminated with a
+call to \ref p2p_scan_res_handled(). The P2P peers that are found during
+the full scan are reported with the \ref p2p_config::dev_found() callback.
+
+After the full scan, P2P module start alternating between Listen and
+Search states until the device discovery operation times out or
+terminated, e.g., with a call to \ref p2p_stop_find().
+
+When going into the Listen state, the P2P module requests the driver
+to be configured to be awake on the listen channel with a call to
+\ref p2p_config::start_listen(). The glue code using the P2P module may
+implement this, e.g., by using remain-on-channel low-level driver
+functionality for off-channel operation. Once the driver is available
+on the requested channel, notification of this is delivered by calling
+\ref p2p_listen_cb(). The Probe Request frames that are received during the
+Listen period are delivered to the P2P module by calling
+\ref p2p_config::p2p_probe_req_rx() and P2P module request a response to
+these to be sent by using \ref p2p_config::send_probe_resp() callback
+function. If a group owner negotiation from another P2P device is
+received during the device discovery phase, that is indicated to the
+upper layer code with the \ref p2p_config::go_neg_req_tx() callback.
+
+The Search state is implemented by using the normal scan interface,
+i.e., the P2P module will call \ref p2p_config::p2p_scan() just like in the
+full scan phase described. Similarly, scan results from the search
+operation will be delivered to the P2P module using the
+\ref p2p_scan_res_handler() and \ref p2p_scan_res_handled() functions.
+
+Once the upper layer management entity has found a peer with which it
+wants to connect by forming a new group, it initiates group owner
+negotiation by calling \ref p2p_connect(). Before doing this, the upper
+layer code is responsible for asking the user to provide the PIN to be
+used during the provisioning step with the peer or the push button
+press for PBC mode. The glue code will need to figure out the intended
+interface address for the group before group owner negotiation can be
+started.
+
+Optional Provision Discovery mechanism can be used to request the peer
+to display a PIN for the local device to enter (and vice versa). Upper
+layer management entity can request the specific mechanism by calling
+\ref p2p_prov_disc_req(). The response to this will be reported with the
+\ref p2p_config::prov_disc_resp() callback. If the peer device started
+Provision Discovery, an accepted request will be reported with the
+\ref p2p_config::prov_disc_req() callback. The P2P module will
+automatically accept the Provision Discovery for display and keypad
+methods, but it is up to the upper layer manegement entity to actually
+generate the PIN and to configure it with following \ref p2p_connect() call
+to actually authorize the connection.
+
+The P2P module will use \ref p2p_config::send_action() callback to request
+lower layer code to transmit an Action frame during group owner
+negotiation. \ref p2p_send_action_cb() is used to report the result of
+transmission. If the peer is not reachable, the P2P module will try to
+find it by alternating between Action frame send and Listen
+states. The Listen state for this phase will be used similarly to the
+Listen state during device discovery as described above.
+
+Once the group owner negotiation has been completed, its results will
+be reported with the \ref p2p_config::go_neg_completed() callback. The
+upper layer management code or the glue code using the P2P module API
+is responsible for creating a new group interface and starting
+provisioning step at this point by configuring WPS Registrar or
+Enrollee functionality based on the reported group owner negotiation
+results. The upper layer code is also responsible for timing out WPS
+provisioning if it cannot be completed in 15 seconds.
+
+Successful completion of the WPS provisioning is reported with a call
+to \ref p2p_wps_success_cb(). The P2P module will clear its group formation
+state at this point and allows new group formation attempts to be
+started. The upper layer management code is responsible for configuring
+the GO to accept associations from devices and the client to connect to
+the GO with the provisioned credentials. GO is also responsible for
+calling \ref p2p_group_notif_formation_done() as described below.
+
+If the WPS provisioning step fails or times out, this is reported with
+a call to \ref p2p_group_formation_failed(). The P2P module will clear its
+group formation state at this point and allows new group formation
+attempts to be started. The upper layer management code is responsible
+for removing the group interface for the failed group.
+
+
+\section p2p_sd P2P service discovery
+
+P2P protocol includes service discovery functionality that can be used
+to discover which services are provided by the peers before forming a
+group. This leverages the Generic Advertisement Service (GAS) protocol
+from IEEE 802.11u and P2P vendor-specific contents inside the Native
+GAS messages.
+
+The P2P module takes care of GAS encapsulation, fragmentation, and
+actual transmission and reception of the Action frames needed for
+service discovery. The user of the P2P module is responsible for
+providing P2P specific Service Request TLV(s) for queries and Service
+Response TLV(s) for responses.
+
+\subsection p2p_sd_query Quering services of peers
+
+Service discovery is implemented by processing pending queries as a
+part of the device discovery phase. \ref p2p_sd_request() function is used
+to schedule service discovery queries to a specific peer or to all
+discovered peers. \ref p2p_sd_cancel_request() can be used to cancel a
+scheduled query. Queries that are specific to a single peer will be
+removed automatically after the response has been received.
+
+After the service discovery queries have been queued, device discovery
+is started with a call to \ref p2p_find(). The pending service discovery
+queries are then sent whenever a peer is discovered during the find
+operation. Responses to the queries will be reported with the
+\ref p2p_config::sd_response() callback.
+
+\subsection p2p_sd_response Replying to service discovery queries from peers
+
+The received service discovery requests will be indicated with the
+\ref p2p_config::sd_request() callback. The response to the query is sent
+by calling \ref p2p_sd_response().
+
+\subsection p2p_sd_indicator Service update indicator
+
+P2P service discovery provides a mechanism to notify peers about
+changes in available services. This works by incrementing Service
+Update Indicator value whenever there is a change in the
+services. This value is included in all SD request and response
+frames. The value received from the peers will be included in the
+\ref p2p_config::sd_request() and \ref p2p_config::sd_response() callbacks. The
+value to be sent to the peers is incremented with a call to
+\ref p2p_sd_service_update() whenever availibility of the local services
+changes.
+
+
+\section p2p_go P2P group owner
+
+This section describes how P2P module can be used for managing
+per-group information in a group owner. The function calls are
+described based on the P2P module API. The exact design for the glue
+code outside the P2P module depends on the architecture used in the
+system.
+
+When a P2P group interface is created in group owner role, per-group
+data is initialized with \ref p2p_group_init(). This call provides a
+pointer to the per-device P2P module context and configures the
+per-group operation. The configured \ref p2p_group_config::ie_update()
+callback is used to set the initial P2P IE for Beacon and Probe
+Response frames in the group owner. The AP mode implementation may use
+this information to add IEs into the frames.
+
+Once the group formation has been completed (or if it is skipped in
+case of manual group setup), \ref p2p_group_notif_formation_done() is
+called. This will allow the P2P module to update the P2P IE for
+Beacon and Probe Response frames.
+
+The SME/MLME code that managements IEEE 802.11 association processing
+needs to inform P2P module whenever a P2P client associates or
+disassociates with the group. This is done by calling
+\ref p2p_group_notif_assoc() and \ref p2p_group_notif_disassoc(). The P2P module
+manages a list of group members and updates the P2P Group Information
+subelement in the P2P IE based on the information from the P2P
+clients. The \ref p2p_group_config::ie_update() callback is used whenever
+the P2P IE in Probe Response frames needs to be changed.
+
+The SME/MLME code that takes care of replying to Probe Request frames
+can use \ref p2p_group_match_dev_type() to check whether the Probe Request
+frame request a reply only from groups that include a specific device
+type in one of the clients or GO. A match will be reported if the
+Probe Request does not request a specific device type, so this
+function can be used to filter or received Probe Request frames and
+only the ones that result in non-zero return value need to be replied.
+
+When the P2P group interface for GO role is removed,
+\ref p2p_group_deinit() is used to deinitialize the per-group P2P module
+state.
+
+
+\section p2p_ctrl_iface P2P control interface
+
+wpa_supplicant \ref ctrl_iface_page "control interface" can be used
+to manage P2P functionality from an external program (e.g., a GUI or a
+system configuration manager). This interface can be used directly
+through the control interface backend mechanism (e.g., local domain
+sockets on Linux) or with help of wpa_cli (e.g., from a script).
+
+The following P2P-related commands are available:
+- \ref ctrl_iface_P2P_FIND P2P_FIND
+- \ref ctrl_iface_P2P_STOP_FIND P2P_STOP_FIND
+- \ref ctrl_iface_P2P_CONNECT P2P_CONNECT
+- \ref ctrl_iface_P2P_LISTEN P2P_LISTEN
+- \ref ctrl_iface_P2P_GROUP_REMOVE P2P_GROUP_REMOVE
+- \ref ctrl_iface_P2P_GROUP_ADD P2P_GROUP_ADD
+- \ref ctrl_iface_P2P_PROV_DISC P2P_PROV_DISC
+- \ref ctrl_iface_P2P_SERV_DISC_REQ P2P_SERV_DISC_REQ
+- \ref ctrl_iface_P2P_SERV_DISC_CANCEL_REQ P2P_SERV_DISC_CANCEL_REQ
+- \ref ctrl_iface_P2P_SERV_DISC_RESP P2P_SERV_DISC_RESP
+- \ref ctrl_iface_P2P_SERVICE_UPDATE P2P_SERVICE_UPDATE
+- \ref ctrl_iface_P2P_SERV_DISC_EXTERNAL P2P_SERV_DISC_EXTERNAL
+- \ref ctrl_iface_P2P_REJECT P2P_REJECT
+- \ref ctrl_iface_P2P_INVITE P2P_INVITE
+
+The following P2P-related events are used:
+- \ref ctrl_iface_event_P2P_EVENT_DEVICE_FOUND P2P-DEVICE-FOUND
+- \ref ctrl_iface_event_P2P_EVENT_GO_NEG_REQUEST P2P-GO-NEG-REQUEST
+- \ref ctrl_iface_event_P2P_EVENT_GO_NEG_SUCCESS P2P-GO-NEG-SUCCESS
+- \ref ctrl_iface_event_P2P_EVENT_GO_NEG_FAILURE P2P-GO-NEG-FAILURE
+- \ref ctrl_iface_event_P2P_EVENT_GROUP_FORMATION_SUCCESS P2P-GROUP-FORMATION-SUCCESS
+- \ref ctrl_iface_event_P2P_EVENT_GROUP_FORMATION_FAILURE P2P-GROUP-FORMATION-FAILURE
+- \ref ctrl_iface_event_P2P_EVENT_GROUP_STARTED P2P-GROUP-STARTED
+- \ref ctrl_iface_event_P2P_EVENT_GROUP_REMOVED P2P-GROUP-REMOVED
+- \ref ctrl_iface_event_P2P_EVENT_PROV_DISC_SHOW_PIN P2P-PROV-DISC-SHOW-PIN
+- \ref ctrl_iface_event_P2P_EVENT_PROV_DISC_ENTER_PIN P2P-PROV-DISC-ENTER-PIN
+- \ref ctrl_iface_event_P2P_EVENT_SERV_DISC_REQ P2P-SERV-DISC-REQ
+- \ref ctrl_iface_event_P2P_EVENT_SERV_DISC_RESP P2P-SERV-DISC-RESP
+- \ref ctrl_iface_event_P2P_EVENT_INVITATION_RECEIVED P2P-INVITATION-RECEIVED
+- \ref ctrl_iface_event_P2P_EVENT_INVITATION_RESULT P2P-INVITATION-RESULT
+
+
+\subsection p2p_wpa_gui GUI example (wpa_gui)
+
+wpa_gui has an example implementation of a GUI that could be used to
+manage P2P operations. The P2P related functionality is contained
+mostly in wpa_supplicant/wpa_gui-qt4/peers.cpp and it shows how the
+control interface commands and events can be used.
+
+
+\subsection p2p_wpa_cli wpa_cli example
+
+wpa_cli can be used to control wpa_supplicant in interactive
+mode. The following sessions show examples of commands used for
+device discovery and group formation. The lines starting with "> " are
+commands from the user (followed by command result indication) and
+lines starting with "<2>" are event messages from wpa_supplicant.
+
+P2P device "Wireless Client":
+
+\verbatim
+> p2p_find
+OK
+> <2>P2P-DEVICE-FOUND 02:40:61:c2:f3:b7 p2p_dev_addr=02:40:61:c2:f3:b7
+pri_dev_type=1-0050F204-1 name='Wireless Client 2' config_methods=0x18c
+dev_capab=0x1 group_capab=0x0
+<2>P2P-GO-NEG-REQUEST 02:40:61:c2:f3:b7
+<2>P2P-GO-NEG-REQUEST 02:40:61:c2:f3:b7
+> p2p_connect 02:40:61:c2:f3:b7 pbc
+OK
+<2>P2P-GO-NEG-SUCCESS 
+<2>P2P-GROUP-FORMATION-SUCCESS 
+<2>P2P-GROUP-STARTED sta0-p2p-0 client DIRECT-vM
+> interface
+Available interfaces:
+sta0-p2p-0
+sta0
+> p2p_group_remove sta0-p2p-0
+<2>P2P-GROUP-REMOVED sta0-p2p-0 client
+OK
+> term
+OK
+\endverbatim
+
+
+P2P device "Wireless Client2" (which ended up operating in GO role):
+
+\verbatim
+> p2p_find
+OK
+<2>P2P-DEVICE-FOUND 02:f0:bc:44:87:62 p2p_dev_addr=02:f0:bc:44:87:62
+pri_dev_type=1-0050F204-1 name='Wireless Client' config_methods=0x18c
+dev_capab=0x1 group_capab=0x0
+> p2p_connect 02:f0:bc:44:87:62 pbc
+OK
+<2>P2P-GO-NEG-SUCCESS 
+<2>P2P-GROUP-FORMATION-SUCCESS 
+<2>P2P-GROUP-STARTED sta1-p2p-0 GO DIRECT-vM
+> interface
+Available interfaces:
+sta1-p2p-0
+sta1
+> p2p_group_remove sta1-p2p-0
+<2>P2P-GROUP-REMOVED sta1-p2p-0 GO
+OK
+> term
+OK
+\endverbatim
+
+*/
diff --git a/hostap/doc/p2p_arch.dot b/hostap/doc/p2p_arch.dot
new file mode 100644
index 0000000..27ae0e2
--- /dev/null
+++ b/hostap/doc/p2p_arch.dot
@@ -0,0 +1,85 @@
+digraph p2p_arch {
+	ranksep=.75;
+	size = "7.5,7.5";
+
+	edge [dir=none];
+
+	subgraph cluster_wpa_gui {
+		label = "wpa_gui";
+
+		status -> Qt;
+		scan -> Qt;
+		network -> Qt;
+		Qt -> peers;
+		Qt -> WPS;
+		Qt -> gui_ctrl;
+
+		gui_ctrl [label="ctrl i/f"];
+	}
+
+	subgraph cluster_wpa_supplicant {
+		label = "wpa_supplicant"
+
+		ctrl_iface [label="ctrl i/f"];
+		authenticator [label="Authenticator"];
+		supplicant [label="Supplicant"];
+		driver_iface [label="driver i/f"];
+		p2p_module [label="P2P\nmodule"];
+		wps_registrar [label="WPS\nRegistrar"];
+		wps_enrollee [label="WPS\nEnrollee"];
+		mgmt_entity [label="Management\nentity"];
+
+		ctrl_iface -> mgmt_entity;
+		p2p_module -> mgmt_entity;
+		wps_registrar -> mgmt_entity;
+		wps_enrollee -> mgmt_entity;
+		mgmt_entity -> authenticator;
+		mgmt_entity -> supplicant;
+		mgmt_entity -> driver_iface;
+
+		{ rank = same; mgmt_entity; p2p_module; }
+	}
+
+	subgraph cluster_wpa_cli {
+		label = "wpa_cli -a"
+
+		wpa_cli_action;
+	}
+
+	subgraph cluster_dnsmasq {
+		label = "dnsmasq"
+
+		dnsmasq;
+	}
+
+	subgraph cluster_dhclient {
+		label = "dhclient"
+
+		dhclient;
+	}
+
+	subgraph cluster_kernel {
+		label = "Linux kernel"
+
+		cfg80211 -> mac80211;
+		netdev -> mac80211;
+		mac80211 -> ath9k;
+	}
+
+	gui_ctrl -> ctrl_iface;
+	wpa_cli_action -> ctrl_iface;
+
+	driver_iface -> cfg80211;
+
+	wpa_cli_action -> dnsmasq;
+	wpa_cli_action -> dhclient;
+
+	dnsmasq -> netdev;
+	dhclient -> netdev;
+
+	edge [color=blue,dir=both];
+	p2p_module -> mgmt_entity -> driver_iface -> cfg80211 -> mac80211 -> ath9k;
+
+	edge [color=green,dir=both];
+	peers -> Qt -> gui_ctrl -> ctrl_iface -> mgmt_entity -> p2p_module;
+}
diff --git a/hostap/doc/p2p_arch2.dot b/hostap/doc/p2p_arch2.dot
new file mode 100644
index 0000000..9c7b4b5
--- /dev/null
+++ b/hostap/doc/p2p_arch2.dot
@@ -0,0 +1,85 @@
+digraph p2p_arch2 {
+	ranksep=.75;
+	size = "7.5,7.5";
+
+	edge [dir=none];
+
+	subgraph cluster_wpa_gui {
+		label = "wpa_gui";
+
+		status -> Qt;
+		scan -> Qt;
+		network -> Qt;
+		Qt -> peers;
+		Qt -> WPS;
+		Qt -> gui_ctrl;
+
+		gui_ctrl [label="ctrl i/f"];
+	}
+
+	subgraph cluster_wpa_supplicant {
+		label = "wpa_supplicant"
+
+		ctrl_iface [label="ctrl i/f"];
+		authenticator [label="Authenticator"];
+		supplicant [label="Supplicant"];
+		driver_iface [label="driver i/f"];
+		wps_registrar [label="WPS\nRegistrar"];
+		wps_enrollee [label="WPS\nEnrollee"];
+		mgmt_entity [label="Management\nentity"];
+
+		ctrl_iface -> mgmt_entity;
+		wps_registrar -> mgmt_entity;
+		wps_enrollee -> mgmt_entity;
+		mgmt_entity -> authenticator;
+		mgmt_entity -> supplicant;
+		mgmt_entity -> driver_iface;
+	}
+
+	subgraph cluster_wpa_cli {
+		label = "wpa_cli -a"
+
+		wpa_cli_action;
+	}
+
+	subgraph cluster_dnsmasq {
+		label = "dnsmasq"
+
+		dnsmasq;
+	}
+
+	subgraph cluster_dhclient {
+		label = "dhclient"
+
+		dhclient;
+	}
+
+	subgraph cluster_kernel {
+		label = "Kernel"
+
+		ioctl -> umac;
+		netdev -> umac;
+		umac -> p2p_module;
+		p2p_module [label="P2P\nmodule"];
+		umac -> driver;
+
+		{ rank = same; umac; p2p_module; }
+	}
+
+	gui_ctrl -> ctrl_iface;
+	wpa_cli_action -> ctrl_iface;
+
+	driver_iface -> ioctl;
+
+	wpa_cli_action -> dnsmasq;
+	wpa_cli_action -> dhclient;
+
+	dnsmasq -> netdev;
+	dhclient -> netdev;
+
+	edge [color=blue,dir=both];
+	p2p_module -> umac -> driver;
+
+	edge [color=green,dir=both];
+	peers -> Qt -> gui_ctrl -> ctrl_iface -> mgmt_entity -> driver_iface -> ioctl -> umac -> p2p_module;
+}
diff --git a/hostap/doc/p2p_sm.dot b/hostap/doc/p2p_sm.dot
new file mode 100644
index 0000000..640caef
--- /dev/null
+++ b/hostap/doc/p2p_sm.dot
@@ -0,0 +1,62 @@
+digraph p2p {
+	ranksep=.75;
+	size = "8.5,7.5";
+
+	start -> IDLE;
+	start [label="Init",shape=none];
+
+	/* Discovery: Scan followed by Find(SEARCH,LISTEN) */
+	subgraph cluster_0 {
+		label="Discovery";
+		color=lightgrey;
+		node [color=blue];
+		/* SCAN and LISTEN currently not used in the implementation */
+		SCAN [style=dotted];
+		LISTEN [style=dotted];
+
+		SCAN -> LISTEN;
+		LISTEN -> SEARCH -> LISTEN [style=dotted];
+		SEARCH -> SD_DURING_FIND [label="Peer SD capab\nand no info", weight=100];
+		SD_DURING_FIND -> SEARCH [label="RX SD Resp\nor timeout", weight=100];
+		SEARCH -> PROV_DISC_DURING_FIND [label="Prov Disc cmd\nand no Resp", weight=100];
+		PROV_DISC_DURING_FIND -> SEARCH [label="RX Prov Disc Resp\nor timeout", weight=100];
+	}
+
+	/* Group Formation */
+	subgraph cluster_1 {
+		label="Group Formation";
+		color=lightgrey;
+		node [color=green];
+
+		CONNECT -> CONNECT_LISTEN [style=dotted,weight=100];
+		CONNECT_LISTEN -> CONNECT [style=dotted,weight=100];
+		CONNECT -> WAIT_PEER_IDLE [label="RX GO Neg Resp\n(info unavail)"];
+		WAIT_PEER_IDLE -> WAIT_PEER_CONNECT [style=dotted,weight=100];
+		WAIT_PEER_CONNECT -> WAIT_PEER_IDLE [style=dotted,weight=100];
+
+		CONNECT -> GO_NEG [label="RX GO Neg Resp\n(success)", weight=10];
+		CONNECT_LISTEN -> GO_NEG [label="RX GO Neg Req or\nTX GO Neg Resp"];
+		WAIT_PEER_CONNECT -> GO_NEG [label="RX GO Neg Req"];
+		GO_NEG -> PROVISIONING [label="TX/RX GO Neg Conf"];
+	}
+
+	PROVISIONING -> IDLE [label="WPS\nsuccess"];
+
+	/* External triggers */
+	IDLE -> SCAN [label="Find cmd",weight=20];
+	IDLE -> CONNECT [label="Connect cmd",weight=20];
+	IDLE -> LISTEN_ONLY [label="Listen cmd"];
+
+	/* Timeouts */
+/*
+	edge [color=red];
+	WAIT_PEER_IDLE -> IDLE [label="timeout", weight=0];
+	WAIT_PEER_CONNECT -> IDLE [label="timeout", weight=0];
+	CONNECT -> IDLE [label="timeout", weight=0];
+	CONNECT_LISTEN -> IDLE [label="timeout", weight=0];
+	GO_NEG -> IDLE [label="timeout", weight=0];
+	PROVISIONING -> IDLE [label="timeout", weight=0];
+	LISTEN_ONLY -> IDLE [label="timeout", weight=0];
+	SEARCH -> IDLE [label="timeout", weight=0];
+*/
+}
diff --git a/hostap/doc/porting.doxygen b/hostap/doc/porting.doxygen
new file mode 100644
index 0000000..b4b78ef
--- /dev/null
+++ b/hostap/doc/porting.doxygen
@@ -0,0 +1,209 @@
+/**
+\page porting Porting to different target boards and operating systems
+
+wpa_supplicant was designed to be easily portable to different
+hardware (board, CPU) and software (OS, drivers) targets. It is
+already used with number of operating systems and numerous wireless
+card models and drivers. The main wpa_supplicant repository includes
+support for Linux, FreeBSD, and Windows. In addition, the code has been
+ported to number of other operating systems like VxWorks, PalmOS,
+Windows CE, and Windows Mobile. On the hardware
+side, wpa_supplicant is used on various systems: desktops, laptops,
+PDAs, and embedded devices with CPUs including x86, PowerPC,
+arm/xscale, and MIPS. Both big and little endian configurations are
+supported.
+
+
+\section ansi_c_extra Extra functions on top of ANSI C
+
+wpa_supplicant is mostly using ANSI C functions that are available on
+most targets. However, couple of additional functions that are common
+on modern UNIX systems are used. Number of these are listed with
+prototypes in \ref common.h (the \verbatim #ifdef CONFIG_ANSI_C_EXTRA \endverbatim
+block). These functions may need to be implemented or at least defined
+as macros to native functions in the target OS or C library.
+
+Many of the common ANSI C functions are used through a wrapper
+definitions in \ref os.h to allow these to be replaced easily with a
+platform specific version in case standard C libraries are not
+available. In addition, \ref os.h defines couple of common platform
+specific functions that are implemented in \ref os_unix.c for UNIX like
+targets and in \ref os_win32.c for Win32 API. If the target platform does
+not support either of these examples, a new os_*.c file may need to be
+added.
+
+Unless OS_NO_C_LIB_DEFINES is defined, the standard ANSI C and POSIX
+functions are used by defining the os_*() wrappers to use them
+directly in order to avoid extra cost in size and speed. If the target
+platform needs different versions of the functions, \ref os.h can be
+modified to define the suitable macros or alternatively,
+OS_NO_C_LIB_DEFINES may be defined for the build and the wrapper
+functions can then be implemented in a new os_*.c wrapper file.
+
+\ref common.h defines number of helper macros for handling integers of
+different size and byte order. Suitable version of these definitions
+may need to be added for the target platform.
+
+
+\section configuration_backend Configuration backend
+
+wpa_supplicant implements a configuration interface that allows the
+backend to be easily replaced in order to read configuration data from
+a suitable source depending on the target platform. \ref config.c
+implements the generic code that can be shared with all configuration
+backends. Each backend is implemented in its own config_*.c file.
+
+The included \ref config_file.c backend uses a text file for configuration
+and \ref config_winreg.c uses Windows registry. These files can be used as
+an example for a new configuration backend if the target platform uses
+different mechanism for configuration parameters. In addition,
+\ref config_none.c can be used as an empty starting point for building a
+new configuration backend.
+
+
+\section driver_iface_porting Driver interface
+
+Unless the target OS and driver is already supported, most porting
+projects have to implement a driver wrapper. This may be done by
+adding a new driver interface module or modifying an existing module
+(driver_*.c) if the new target is similar to one of them. \ref
+driver_wrapper "Driver wrapper implementation" describes the details
+of the driver interface and discusses the tasks involved in porting
+this part of wpa_supplicant.
+
+
+\section l2_packet_porting l2_packet (link layer access)
+
+wpa_supplicant needs to have access to sending and receiving layer 2
+(link layer) packets with two Ethertypes: EAP-over-LAN (EAPOL) 0x888e
+and RSN pre-authentication 0x88c7. \ref l2_packet.h defines the interfaces
+used for this in the core wpa_supplicant implementation.
+
+If the target operating system supports a generic mechanism for link
+layer access, that is likely the best mechanism for providing the
+needed functionality for wpa_supplicant. Linux packet socket is an
+example of such a generic mechanism. If this is not available, a
+separate interface may need to be implemented to the network stack or
+driver. This is usually an intermediate or protocol driver that is
+operating between the device driver and the OS network stack. If such
+a mechanism is not feasible, the interface can also be implemented
+directly in the device driver.
+
+The main wpa_supplicant repository includes l2_packet implementations
+for Linux using packet sockets (\ref l2_packet_linux.c), more portable
+version using libpcap/libdnet libraries (\ref l2_packet_pcap.c; this
+supports WinPcap, too), and FreeBSD specific version of libpcap
+interface (\ref l2_packet_freebsd.c).
+
+If the target operating system is supported by libpcap (receiving) and
+libdnet (sending), \ref l2_packet_pcap.c can likely be used with minimal or
+no changes. If this is not a case or a proprietary interface for link
+layer is required, a new l2_packet module may need to be
+added. Alternatively, for hostapd,
+struct \ref wpa_driver_ops::hapd_send_eapol() handler can
+be used to override the l2_packet library if the link layer access is
+integrated with the driver interface implementation.
+
+
+\section eloop_porting Event loop
+
+wpa_supplicant uses a single process/thread model and an event loop
+to provide callbacks on events (registered timeout, received packet,
+signal). eloop.h defines the event loop interface. \ref eloop.c is an
+implementation of such an event loop using select() and sockets. This
+is suitable for most UNIX/POSIX systems. When porting to other
+operating systems, it may be necessary to replace that implementation
+with OS specific mechanisms that provide similar functionality.
+
+
+\section ctrl_iface_porting Control interface
+
+wpa_supplicant uses a \ref ctrl_iface_page "control interface"
+to allow external processed
+to get status information and to control the operations. Currently,
+this is implemented with socket based communication; both UNIX domain
+sockets and UDP sockets are supported. If the target OS does not
+support sockets, this interface will likely need to be modified to use
+another mechanism like message queues. The control interface is
+optional component, so it is also possible to run wpa_supplicant
+without porting this part.
+
+The wpa_supplicant side of the control interface is implemented in
+\ref wpa_supplicant/ctrl_iface.c. Matching client side is implemented as a control
+interface library in \ref wpa_ctrl.c.
+
+
+\section entry_point Program entry point
+
+wpa_supplicant defines a set of functions that can be used to
+initialize main supplicant processing. Each operating system has a
+mechanism for starting new processing or threads. This is usually a
+function with a specific set of arguments and calling convention. This
+function is responsible on initializing wpa_supplicant.
+
+\ref wpa_supplicant/main.c includes an entry point for UNIX-like
+operating system, i.e., main() function that uses command line arguments
+for setting parameters for wpa_supplicant. When porting to other
+operating systems, similar OS-specific entry point implementation is
+needed. It can be implemented in a new file that is then linked with
+wpa_supplicant instead of main.o. \ref wpa_supplicant/main.c is also a
+good example on how the initialization process should be done.
+
+The supplicant initialization functions are defined in
+\ref wpa_supplicant_i.h. In most cases, the entry point function should
+start by fetching configuration parameters. After this, a global
+wpa_supplicant context is initialized with a call to
+\ref wpa_supplicant_init(). After this, existing network interfaces can be
+added with \ref wpa_supplicant_add_iface(). \ref wpa_supplicant_run() is then
+used to start the main event loop. Once this returns at program
+termination time, \ref wpa_supplicant_deinit() is used to release global
+context data.
+
+\ref wpa_supplicant_add_iface() and \ref wpa_supplicant_remove_iface() can be
+used dynamically to add and remove interfaces based on when
+wpa_supplicant processing is needed for them. This can be done, e.g.,
+when hotplug network adapters are being inserted and ejected. It is
+also possible to do this when a network interface is being
+enabled/disabled if it is desirable that wpa_supplicant processing
+for the interface is fully enabled/disabled at the same time.
+
+
+\section simple_build Simple build example
+
+One way to start a porting project is to begin with a very simple
+build of wpa_supplicant with WPA-PSK support and once that is
+building correctly, start adding features.
+
+Following command can be used to build very simple version of
+wpa_supplicant:
+
+\verbatim
+cc -o wpa_supplicant config.c eloop.c common.c md5.c rc4.c sha1.c \
+	config_none.c l2_packet_none.c tls_none.c wpa.c preauth.c \
+	aes_wrap.c wpa_supplicant.c events.c main_none.c drivers.c
+\endverbatim
+
+The end result is not really very useful since it uses empty functions
+for configuration parsing and layer 2 packet access and does not
+include a driver interface. However, this is a good starting point
+since the build is complete in the sense that all functions are
+present and this is easy to configure to a build system by just
+including the listed C files.
+
+Once this version can be build successfully, the end result can be
+made functional by adding a proper program entry point (main*.c),
+driver interface (driver_*.c and matching CONFIG_DRIVER_* define for
+registration in \ref drivers.c), configuration parser/writer (config_*.c),
+and layer 2 packet access implementation (l2_packet_*.c). After these
+components have been added, the end result should be a working
+WPA/WPA2-PSK enabled supplicant.
+
+After the basic functionality has been verified to work, more features
+can be added by linking in more files and defining C pre-processor
+defines. Currently, the best source of information for what options
+are available and which files needs to be included is in the Makefile
+used for building the supplicant with make. Similar configuration will
+be needed for build systems that either use different type of make
+tool or a GUI-based project configuration.
+
+*/
diff --git a/hostap/doc/testing_tools.doxygen b/hostap/doc/testing_tools.doxygen
new file mode 100644
index 0000000..d126524
--- /dev/null
+++ b/hostap/doc/testing_tools.doxygen
@@ -0,0 +1,201 @@
+/**
+\page testing_tools Testing and development tools
+
+[ \ref eapol_test "eapol_test" |
+\ref preauth_test "preauth_test" |
+\ref unit_tests "Unit tests" |
+\ref wpa_trace "Tracing code" ]
+
+wpa_supplicant source tree includes number of testing and development
+tools that make it easier to test the programs without having to setup
+a full test setup with wireless cards. In addition, these tools can be
+used to implement automatic tests suites.
+
+\section eapol_test eapol_test - EAP peer and RADIUS client testing
+
+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.
+
+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.
+
+eapol_test uses the same build time configuration file, .config, as
+wpa_supplicant. This file is used to select which EAP methods are
+included in eapol_test. This program is not built with the default
+Makefile target, so a separate make command needs to be used to
+compile the tool:
+
+\verbatim
+make eapol_test
+\endverbatim
+
+The resulting eapol_test binary has following command like options:
+
+\verbatim
+usage:
+eapol_test [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] [-s<AS secret>] \
+           [-r<count>] [-t<timeout>] [-C<Connect-Info>] \
+           [-M<client MAC address>]
+eapol_test scard
+eapol_test sim <PIN> <num triplets> [debug]
+
+options:
+  -c<conf> = configuration file
+  -a<AS IP> = IP address of the authentication server, default 127.0.0.1
+  -p<AS port> = UDP port of the authentication server, default 1812
+  -s<AS secret> = shared secret with the authentication server, default 'radius'
+  -r<count> = number of re-authentications
+  -W = wait for a control interface monitor before starting
+  -S = save configuration after authentiation
+  -n = no MPPE keys expected
+  -t<timeout> = sets timeout in seconds (default: 30 s)
+  -C<Connect-Info> = RADIUS Connect-Info (default: CONNECT 11Mbps 802.11b)
+  -M<client MAC address> = Set own MAC address (Calling-Station-Id,
+                           default: 02:00:00:00:00:01)
+\endverbatim
+
+
+As an example,
+\verbatim
+eapol_test -ctest.conf -a127.0.0.1 -p1812 -ssecret -r1
+\endverbatim
+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.
+
+
+\section preauth_test preauth_test - WPA2 pre-authentication and EAP peer testing
+
+preauth_test is similar to eapol_test in the sense that in combines
+EAP peer implementation with something else, in this case, with WPA2
+pre-authentication. This tool can be used to test pre-authentication
+based on the code that wpa_supplicant is using. As such, it tests
+both the wpa_supplicant implementation and the functionality of an
+access point.
+
+preauth_test is built with:
+
+\verbatim
+make preauth_test
+\endverbatim
+
+and it uses following command line arguments:
+
+\verbatim
+usage: preauth_test <conf> <target MAC address> <ifname>
+\endverbatim
+
+For example,
+\verbatim
+preauth_test test.conf 02:11:22:33:44:55 eth0
+\endverbatim
+would use network configuration from test.conf to try to complete
+pre-authentication with AP using BSSID 02:11:22:33:44:55. The
+pre-authentication packets would be sent using the eth0 interface.
+
+
+\section unit_tests Unit tests
+
+Number of the components (.c files) used in wpa_supplicant define
+their own unit tests for automated validation of the basic
+functionality. Most of the tests for cryptographic algorithms are
+using standard test vectors to validate functionality. These tests can
+be useful especially when verifying port to a new CPU target.
+
+The test programs are collected in the tests subdirectory. All
+automated unit tests can be run with
+
+\verbatim
+make run-tests
+\endverbatim
+
+This make target builds and runs each test and terminates with zero
+exit code if all tests were completed successfully.
+
+
+\section wpa_trace Tracing code for developer debuggin
+
+wpa_supplicant and hostapd can be built with tracing code that will
+track and analyze memory allocations and other resource registrations
+and certain API uses. If incorrect use is detected, a backtrace of the
+call location (and/or allocation location) is shown. This can also be
+used to detect certain categories of memory leaks and report them
+automatically when the program is terminated. The report will also
+include information about forgotten eloop events.
+
+The trace code can be enabled with CONFIG_WPA_TRACE=y build
+option. More verbose backtrace information can be generated if libbfd
+is available and the binaries are not stripped of symbol
+information. This is enabled with CONFIG_WPA_TRACE_BFD=y.
+
+For example, a memory leak (forgotten os_free() call) would show up
+like this when the program is terminated:
+
+\verbatim
+MEMLEAK[0x82d200]: len 128
+WPA_TRACE: memleak - START
+[0]: ./wpa_supplicant(os_malloc+0x59) [0x41a5e9]
+     os_malloc() ../src/utils/os_unix.c:359
+[1]: ./wpa_supplicant(os_zalloc+0x16) [0x41a676]
+     os_zalloc() ../src/utils/os_unix.c:418
+[2]: ./wpa_supplicant(wpa_supplicant_init+0x38) [0x48b508]
+     wpa_supplicant_init() wpa_supplicant.c:2315
+[3]: ./wpa_supplicant(main+0x2f3) [0x491073]
+     main() main.c:252
+WPA_TRACE: memleak - END
+MEMLEAK: total 128 bytes
+\endverbatim
+
+Another type of error that can be detected is freeing of memory area
+that was registered for some use and is still be referenced:
+
+\verbatim
+WPA_TRACE: Freeing referenced memory - START
+[2]: ./wpa_supplicant(os_free+0x5c) [0x41a53c]
+     os_free() ../src/utils/os_unix.c:411
+[3]: ./wpa_supplicant(wpa_supplicant_remove_iface+0x30) [0x48b380]
+     wpa_supplicant_remove_iface() wpa_supplicant.c:2259
+[4]: ./wpa_supplicant(wpa_supplicant_deinit+0x20) [0x48b3e0]
+     wpa_supplicant_deinit() wpa_supplicant.c:2430
+[5]: ./wpa_supplicant(main+0x357) [0x4910d7]
+     main() main.c:276
+WPA_TRACE: Freeing referenced memory - END
+WPA_TRACE: Reference registration - START
+[1]: ./wpa_supplicant [0x41c040]
+     eloop_trace_sock_add_ref() ../src/utils/eloop.c:94
+[2]: ./wpa_supplicant(wpa_supplicant_ctrl_iface_deinit+0x17) [0x473247]
+     wpa_supplicant_ctrl_iface_deinit() ctrl_iface_unix.c:436
+[3]: ./wpa_supplicant [0x48b21c]
+     wpa_supplicant_cleanup() wpa_supplicant.c:378
+     wpa_supplicant_deinit_iface() wpa_supplicant.c:2155
+[4]: ./wpa_supplicant(wpa_supplicant_remove_iface+0x30) [0x48b380]
+     wpa_supplicant_remove_iface() wpa_supplicant.c:2259
+[5]: ./wpa_supplicant(wpa_supplicant_deinit+0x20) [0x48b3e0]
+     wpa_supplicant_deinit() wpa_supplicant.c:2430
+[6]: ./wpa_supplicant(main+0x357) [0x4910d7]
+     main() main.c:276
+WPA_TRACE: Reference registration - END
+Aborted
+\endverbatim
+
+This type of error results in showing backtraces for both the location
+where the incorrect freeing happened and the location where the memory
+area was marked referenced.
+
+*/
diff --git a/hostap/doc/wpa_supplicant.fig b/hostap/doc/wpa_supplicant.fig
new file mode 100644
index 0000000..d2c4306
--- /dev/null
+++ b/hostap/doc/wpa_supplicant.fig
@@ -0,0 +1,247 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+6 1875 4050 2925 4350
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 1875 4050 2925 4050 2925 4350 1875 4350 1875 4050
+4 0 0 50 -1 0 12 0.0000 4 180 735 2025 4275 l2_packet\001
+-6
+6 3450 1200 4275 1500
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 3450 1200 4275 1200 4275 1500 3450 1500 3450 1200
+4 0 0 50 -1 0 12 0.0000 4 180 585 3600 1425 wpa_cli\001
+-6
+6 4725 1200 5925 1500
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 4725 1200 5925 1200 5925 1500 4725 1500 4725 1200
+4 0 0 50 -1 0 12 0.0000 4 135 1005 4800 1425 GUI frontend\001
+-6
+6 6000 2700 7200 3225
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 6000 2700 7200 2700 7200 3225 6000 3225 6000 2700
+4 0 0 50 -1 0 12 0.0000 4 135 975 6075 2925 WPA/WPA2\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 3150 state machine\001
+-6
+6 6000 4950 7200 5475
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 6000 4950 7200 4950 7200 5475 6000 5475 6000 4950
+4 0 0 50 -1 0 12 0.0000 4 135 360 6075 5175 EAP\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 5400 state machine\001
+-6
+6 8700 3000 9375 3300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8700 3000 9375 3000 9375 3300 8700 3300 8700 3000
+4 0 0 50 -1 0 12 0.0000 4 150 480 8775 3225 crypto\001
+-6
+6 4350 3900 5025 4425
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 4350 3900 5025 3900 5025 4425 4350 4425 4350 3900
+4 0 0 50 -1 0 12 0.0000 4 105 420 4500 4125 event\001
+4 0 0 50 -1 0 12 0.0000 4 180 315 4500 4350 loop\001
+-6
+6 4275 2550 5100 2850
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 4275 2550 5100 2550 5100 2850 4275 2850 4275 2550
+4 0 0 50 -1 0 12 0.0000 4 135 450 4425 2775 ctrl i/f\001
+-6
+6 6000 3900 7200 4425
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 6000 3900 7200 3900 7200 4425 6000 4425 6000 3900
+4 0 0 50 -1 0 12 0.0000 4 135 600 6075 4125 EAPOL\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 4350 state machine\001
+-6
+6 1800 6000 7800 8100
+6 1800 6000 7800 7200
+6 1800 6900 2700 7200
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 1800 6900 2700 6900 2700 7200 1800 7200 1800 6900
+4 0 0 50 -1 0 12 0.0000 4 105 375 1875 7125 wext\001
+-6
+6 4725 6900 5625 7200
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 4725 6900 5625 6900 5625 7200 4725 7200 4725 6900
+4 0 0 50 -1 0 12 0.0000 4 135 555 4800 7125 hermes\001
+-6
+6 6675 6900 7800 7200
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 6675 6900 7800 6900 7800 7200 6675 7200 6675 6900
+4 0 0 50 -1 0 12 0.0000 4 180 930 6750 7125 ndiswrapper\001
+-6
+6 5700 6900 6600 7200
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 5700 6900 6600 6900 6600 7200 5700 7200 5700 6900
+4 0 0 50 -1 0 12 0.0000 4 135 420 5775 7125 atmel\001
+-6
+6 4275 6000 5100 6300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 4275 6000 5100 6000 5100 6300 4275 6300 4275 6000
+4 0 0 50 -1 0 12 0.0000 4 135 630 4350 6225 driver i/f\001
+-6
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 2775 6900 3675 6900 3675 7200 2775 7200 2775 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 3750 6900 4650 6900 4650 7200 3750 7200 3750 6900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+	 2250 6900 2250 6600 7200 6600 7200 6900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 3225 6900 3225 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 4200 6900 4200 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 5175 6900 5175 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6150 6900 6150 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 4650 6600 4650 6300
+4 0 0 50 -1 0 12 0.0000 4 180 510 2850 7125 hostap\001
+4 0 0 50 -1 0 12 0.0000 4 135 600 3825 7125 nl80211\001
+-6
+6 3525 7800 5775 8100
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 3525 7800 5775 7800 5775 8100 3525 8100 3525 7800
+4 0 0 50 -1 0 12 0.0000 4 135 2145 3600 8025 kernel network device driver\001
+-6
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+	 2250 7200 4200 7800
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+	 7200 7200 5100 7800
+-6
+6 9600 3000 10275 3300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 9600 3000 10275 3000 10275 3300 9600 3300 9600 3000
+4 0 0 50 -1 0 12 0.0000 4 135 315 9750 3225 TLS\001
+-6
+6 8100 4425 10425 7350
+6 8175 4725 9225 5025
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8175 4725 9225 4725 9225 5025 8175 5025 8175 4725
+4 0 0 50 -1 0 12 0.0000 4 135 735 8250 4950 EAP-TLS\001
+-6
+6 9300 4725 10350 5025
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 9300 4725 10350 4725 10350 5025 9300 5025 9300 4725
+4 0 0 50 -1 0 12 0.0000 4 135 810 9375 4950 EAP-MD5\001
+-6
+6 8175 5100 9225 5400
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8175 5100 9225 5100 9225 5400 8175 5400 8175 5100
+4 0 0 50 -1 0 12 0.0000 4 135 885 8250 5325 EAP-PEAP\001
+-6
+6 9300 5100 10350 5400
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 9300 5100 10350 5100 10350 5400 9300 5400 9300 5100
+4 0 0 50 -1 0 12 0.0000 4 135 840 9375 5325 EAP-TTLS\001
+-6
+6 8175 5475 9225 5775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8175 5475 9225 5475 9225 5775 8175 5775 8175 5475
+4 0 0 50 -1 0 12 0.0000 4 135 780 8250 5700 EAP-GTC\001
+-6
+6 9300 5475 10350 5775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 9300 5475 10350 5475 10350 5775 9300 5775 9300 5475
+4 0 0 50 -1 0 12 0.0000 4 135 765 9375 5700 EAP-OTP\001
+-6
+6 8175 5850 9225 6150
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8175 5850 9225 5850 9225 6150 8175 6150 8175 5850
+4 0 0 50 -1 0 12 0.0000 4 135 750 8250 6075 EAP-SIM\001
+-6
+6 9300 6225 10350 6525
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 9300 6225 10350 6225 10350 6525 9300 6525 9300 6225
+4 0 0 50 -1 0 12 0.0000 4 135 465 9375 6450 LEAP\001
+-6
+6 8175 6225 9225 6525
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8175 6225 9225 6225 9225 6525 8175 6525 8175 6225
+4 0 0 50 -1 0 12 0.0000 4 135 765 8250 6450 EAP-PSK\001
+-6
+6 9300 5850 10350 6150
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 9300 5850 10350 5850 10350 6150 9300 6150 9300 5850
+4 0 0 50 -1 0 12 0.0000 4 135 825 9375 6075 EAP-AKA\001
+-6
+6 8175 6975 9675 7275
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8175 6975 9675 6975 9675 7275 8175 7275 8175 6975
+4 0 0 50 -1 0 12 0.0000 4 135 1365 8250 7200 EAP-MSCHAPv2\001
+-6
+6 9300 6600 10350 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 9300 6600 10350 6600 10350 6900 9300 6900 9300 6600
+4 0 0 50 -1 0 12 0.0000 4 135 870 9375 6825 EAP-FAST\001
+-6
+6 8175 6600 9225 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8175 6600 9225 6600 9225 6900 8175 6900 8175 6600
+4 0 0 50 -1 0 12 0.0000 4 135 795 8250 6825 EAP-PAX\001
+-6
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 8100 7350 10425 7350 10425 4425 8100 4425 8100 7350
+4 0 0 50 -1 0 12 0.0000 4 135 1050 8700 4650 EAP methods\001
+-6
+6 2775 5025 4050 5325
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 2775 5025 4050 5025 4050 5325 2775 5325 2775 5025
+4 0 0 50 -1 0 12 0.0000 4 135 990 2925 5250 driver events\001
+-6
+6 2775 3150 4050 3450
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 2775 3150 4050 3150 4050 3450 2775 3450 2775 3150
+4 0 0 50 -1 0 12 0.0000 4 180 990 2925 3375 configuration\001
+-6
+2 1 1 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2
+	 1275 4200 1875 4200
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+	 4500 2550 3900 1500
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+	 4800 2550 5400 1500
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 2925 4200 4350 4200
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 5025 3900 6000 3000
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 5025 4200 6000 4200
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 4650 6000 4650 4425
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6600 4425 6600 4950
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6600 3225 6600 3900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 7200 5250 8100 5250
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 9075 4425 9075 3300
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 7200 3000 8700 3150
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 4650 3900 4650 2850
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 7200 4125 8700 3300
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6000 4350 5025 6000
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6000 3150 4875 6000
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 1500 2100 10800 2100 10800 7500 1500 7500 1500 2100
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 9900 4425 9900 3300
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 1
+	 4350 3900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 4350 3900 4050 3450
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 4350 4425 4050 5025
+4 0 0 50 -1 0 12 0.0000 4 135 915 375 3975 EAPOL and\001
+4 0 0 50 -1 0 12 0.0000 4 180 630 375 4200 pre-auth\001
+4 0 0 50 -1 0 12 0.0000 4 180 810 375 4425 ethertypes\001
+4 0 0 50 -1 0 12 0.0000 4 135 1050 375 4650 from/to kernel\001
+4 0 0 50 -1 0 12 0.0000 4 135 1920 3675 1875 frontend control interface\001
+4 0 0 50 -1 2 14 0.0000 4 210 1440 1637 2371 wpa_supplicant\001
diff --git a/hostap/eap_example/Makefile b/hostap/eap_example/Makefile
new file mode 100644
index 0000000..0cc19bd
--- /dev/null
+++ b/hostap/eap_example/Makefile
@@ -0,0 +1,152 @@
+ALL=eap_example
+
+all: $(ALL)
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef RANLIB
+RANLIB=ranlib
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+
+CFLAGS += -I.
+CFLAGS += -I../src
+CFLAGS += -I../src/utils
+
+
+OBJS_both += ../src/utils/libutils.a
+OBJS_both += ../src/crypto/libcrypto.a
+OBJS_both += ../src/tls/libtls.a
+
+OBJS_both += ../src/eap_common/eap_peap_common.o
+OBJS_both += ../src/eap_common/eap_psk_common.o
+OBJS_both += ../src/eap_common/eap_pax_common.o
+OBJS_both += ../src/eap_common/eap_sake_common.o
+OBJS_both += ../src/eap_common/eap_gpsk_common.o
+OBJS_both += ../src/eap_common/chap.o
+
+OBJS_peer += ../src/eap_peer/eap_tls.o
+OBJS_peer += ../src/eap_peer/eap_peap.o
+OBJS_peer += ../src/eap_peer/eap_ttls.o
+OBJS_peer += ../src/eap_peer/eap_md5.o
+OBJS_peer += ../src/eap_peer/eap_mschapv2.o
+OBJS_peer += ../src/eap_peer/mschapv2.o
+OBJS_peer += ../src/eap_peer/eap_otp.o
+OBJS_peer += ../src/eap_peer/eap_gtc.o
+OBJS_peer += ../src/eap_peer/eap_leap.o
+OBJS_peer += ../src/eap_peer/eap_psk.o
+OBJS_peer += ../src/eap_peer/eap_pax.o
+OBJS_peer += ../src/eap_peer/eap_sake.o
+OBJS_peer += ../src/eap_peer/eap_gpsk.o
+OBJS_peer += ../src/eap_peer/eap.o
+OBJS_peer += ../src/eap_common/eap_common.o
+OBJS_peer += ../src/eap_peer/eap_methods.o
+OBJS_peer += ../src/eap_peer/eap_tls_common.o
+
+CFLAGS += -DEAP_TLS
+CFLAGS += -DEAP_PEAP
+CFLAGS += -DEAP_TTLS
+CFLAGS += -DEAP_MD5
+CFLAGS += -DEAP_MSCHAPv2
+CFLAGS += -DEAP_GTC
+CFLAGS += -DEAP_OTP
+CFLAGS += -DEAP_LEAP
+CFLAGS += -DEAP_PSK
+CFLAGS += -DEAP_PAX
+CFLAGS += -DEAP_SAKE
+CFLAGS += -DEAP_GPSK -DEAP_GPSK_SHA256
+
+CFLAGS += -DEAP_SERVER_IDENTITY
+CFLAGS += -DEAP_SERVER_TLS
+CFLAGS += -DEAP_SERVER_PEAP
+CFLAGS += -DEAP_SERVER_TTLS
+CFLAGS += -DEAP_SERVER_MD5
+CFLAGS += -DEAP_SERVER_MSCHAPV2
+CFLAGS += -DEAP_SERVER_GTC
+CFLAGS += -DEAP_SERVER_PSK
+CFLAGS += -DEAP_SERVER_PAX
+CFLAGS += -DEAP_SERVER_SAKE
+CFLAGS += -DEAP_SERVER_GPSK -DEAP_SERVER_GPSK_SHA256
+
+CFLAGS += -DIEEE8021X_EAPOL
+
+
+# Optional components to add EAP server support
+OBJS_server += ../src/eap_server/eap_server_tls.o
+OBJS_server += ../src/eap_server/eap_server_peap.o
+OBJS_server += ../src/eap_server/eap_server_ttls.o
+OBJS_server += ../src/eap_server/eap_server_md5.o
+OBJS_server += ../src/eap_server/eap_server_mschapv2.o
+OBJS_server += ../src/eap_server/eap_server_gtc.o
+OBJS_server += ../src/eap_server/eap_server_psk.o
+OBJS_server += ../src/eap_server/eap_server_pax.o
+OBJS_server += ../src/eap_server/eap_server_sake.o
+OBJS_server += ../src/eap_server/eap_server_gpsk.o
+OBJS_server += ../src/eap_server/eap_server.o
+OBJS_server += ../src/eap_server/eap_server_identity.o
+OBJS_server += ../src/eap_server/eap_server_methods.o
+OBJS_server += ../src/eap_server/eap_server_tls_common.o
+CFLAGS += -DEAP_SERVER
+
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+
+%.o: %.c
+	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
+	@$(E) "  CC " $<
+
+
+OBJS_lib=$(OBJS_both) $(OBJS_peer) $(OBJS_server)
+
+OBJS_ex = eap_example.o eap_example_peer.o eap_example_server.o
+
+
+../src/utils/libutils.a:
+	$(MAKE) -C ../src/utils
+
+../src/crypto/libcrypto.a:
+	$(MAKE) -C ../src/crypto
+
+../src/tls/libtls.a:
+	$(MAKE) -C ../src/tls
+
+
+ifneq ($(CONFIG_SOLIB), yes)
+LIBEAP = libeap.a
+libeap.a: $(OBJS_lib)
+	$(AR) crT libeap.a $(OBJS_lib)
+	$(RANLIB) libeap.a
+
+else
+CFLAGS  += -fPIC -DPIC
+LDFLAGS += -shared
+
+LIBEAP  = libeap.so
+libeap.so: $(OBJS_lib)
+	$(LDO) $(LDFLAGS) $(OBJS_lib) -o $(LIBEAP)
+
+endif
+
+eap_example: $(OBJS_ex) $(LIBEAP)
+	$(LDO) $(LDFLAGS) -o eap_example $(OBJS_ex) -L. -leap $(LIBS)
+
+clean:
+	$(MAKE) -C ../src clean
+	rm -f core *~ *.o *.d libeap.a libeap.so $(ALL)
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/eap_example/README b/hostap/eap_example/README
new file mode 100644
index 0000000..0c2921e
--- /dev/null
+++ b/hostap/eap_example/README
@@ -0,0 +1,42 @@
+EAP peer/server library and example program
+Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+
+This software may be distributed under the terms of the BSD license.
+See the parent directory README for more details.
+
+
+The interfaces of the EAP server/peer implementation are based on RFC
+4137 (EAP State Machines). This RFC is coordinated with the state
+machines defined in IEEE 802.1X-2004. hostapd and wpa_supplicant
+include implementation of the IEEE 802.1X EAPOL state machines and the
+interface between them and EAP. However, the EAP implementation can be
+used with other protocols, too, by providing a compatible interface
+which maps the EAPOL<->EAP variables to another protocol.
+
+This directory contains an example showing how EAP peer and server
+code from wpa_supplicant and hostapd can be used as a library. The
+example program initializes both an EAP server and an EAP peer
+entities and then runs through an EAP-PEAP/MSCHAPv2 authentication.
+
+eap_example_peer.c shows the initialization and glue code needed to
+control the EAP peer implementation. eap_example_server.c does the
+same for EAP server. eap_example.c is an example that ties in both the
+EAP server and client parts to allow an EAP authentication to be
+shown.
+
+In this example, the EAP messages are passed between the server and
+the peer are passed by direct function calls within the same process.
+In practice, server and peer functionalities would likely reside in
+separate devices and the EAP messages would be transmitted between the
+devices based on an external protocol. For example, in IEEE 802.11
+uses IEEE 802.1X EAPOL state machines to control the transmission of
+EAP messages and WiMax supports optional PMK EAP authentication
+mechanism that transmits EAP messages as defined in IEEE 802.16e.
+
+
+The EAP library links in number of helper functions from src/utils and
+src/crypto directories. Most of these are suitable as-is, but it may
+be desirable to replace the debug output code in src/utils/wpa_debug.c
+by dropping this file from the library and re-implementing the
+functions there in a way that better fits in with the main
+application.
diff --git a/hostap/eap_example/ca.pem b/hostap/eap_example/ca.pem
new file mode 100644
index 0000000..bfae1cc
--- /dev/null
+++ b/hostap/eap_example/ca.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDBzCCAnCgAwIBAgIJAIb4NS4TdLXUMA0GCSqGSIb3DQEBBQUAMGExCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMQ4wDAYDVQQKEwV3MS5maTEQMA4G
+A1UEAxMHVGVzdCBDQTEbMBkGCSqGSIb3DQEJARYMdGVzdGNhQHcxLmZpMB4XDTA3
+MTIwOTAzMTQzN1oXDTE3MTIwNjAzMTQzN1owYTELMAkGA1UEBhMCVVMxEzARBgNV
+BAgTCkNhbGlmb3JuaWExDjAMBgNVBAoTBXcxLmZpMRAwDgYDVQQDEwdUZXN0IENB
+MRswGQYJKoZIhvcNAQkBFgx0ZXN0Y2FAdzEuZmkwgZ8wDQYJKoZIhvcNAQEBBQAD
+gY0AMIGJAoGBAO6GoecRclnILh9FTvqnY/yUZmeJDgC+3/PQiicpMDhAzCkWAmi+
+a1LSnqakNN/GdCy3q053TFLFEzhEHkhhRwY/zzj2vZIcFZESoUhr67CzCpcPmTGa
+AfOzsGPjaH6xYcaOR4RZMfXd/EKfAauHxj3LuCusLL5hK/FwxWhQJNJrAgMBAAGj
+gcYwgcMwHQYDVR0OBBYEFKhJuSLJ6JhcB/dRgB8j0h9mOlpKMIGTBgNVHSMEgYsw
+gYiAFKhJuSLJ6JhcB/dRgB8j0h9mOlpKoWWkYzBhMQswCQYDVQQGEwJVUzETMBEG
+A1UECBMKQ2FsaWZvcm5pYTEOMAwGA1UEChMFdzEuZmkxEDAOBgNVBAMTB1Rlc3Qg
+Q0ExGzAZBgkqhkiG9w0BCQEWDHRlc3RjYUB3MS5maYIJAIb4NS4TdLXUMAwGA1Ud
+EwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAuU+5Uerq+n8WgiIsiANT3wUoGe2Y
+cnoQi2nVjUHrivgMDufH0tgh1AVfc3wVNNREdGC136qr1KBNqalQx2rKZ76xeNqW
+sQa2LIC2wE7Q7LJsltUcUjPyZHGUhBqWjKsCvlonfNB6JHkEayTEvVvyupgzTsxW
+QuuRdZ0sNv/S8VI=
+-----END CERTIFICATE-----
diff --git a/hostap/eap_example/dh.conf b/hostap/eap_example/dh.conf
new file mode 100644
index 0000000..7bc8325
--- /dev/null
+++ b/hostap/eap_example/dh.conf
@@ -0,0 +1,5 @@
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAP3V8IHq3H2DUlYywsvjYNuS17eCdt0mJo6/os6PHqdhgkMrPxF9u4Gr
+qKXq9e6GqmZYdjta30N3FkXaV924BJ0xOqb2TntiKg4u50/l6hSUneWt6UFBaizd
+XrqjNFIme/5RXMZ7RglXliBpCepAaFLMcKhOS4ulUyYYHSy+oqRjAgEC
+-----END DH PARAMETERS-----
diff --git a/hostap/eap_example/eap_example.c b/hostap/eap_example/eap_example.c
new file mode 100644
index 0000000..8a48cd3
--- /dev/null
+++ b/hostap/eap_example/eap_example.c
@@ -0,0 +1,47 @@
+/*
+ * Example application showing how EAP peer and server code from
+ * wpa_supplicant/hostapd can be used as a library. This example program
+ * initializes both an EAP server and an EAP peer entities and then runs
+ * through an EAP-PEAP/MSCHAPv2 authentication.
+ * Copyright (c) 2007, 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"
+
+
+int eap_example_peer_init(void);
+void eap_example_peer_deinit(void);
+int eap_example_peer_step(void);
+
+int eap_example_server_init(void);
+void eap_example_server_deinit(void);
+int eap_example_server_step(void);
+
+
+int main(int argc, char *argv[])
+{
+	int res_s, res_p;
+
+	wpa_debug_level = 0;
+
+	if (eap_example_peer_init() < 0 ||
+	    eap_example_server_init() < 0)
+		return -1;
+
+	do {
+		printf("---[ server ]--------------------------------\n");
+		res_s = eap_example_server_step();
+		printf("---[ peer ]----------------------------------\n");
+		res_p = eap_example_peer_step();
+	} while (res_s || res_p);
+
+	eap_example_peer_deinit();
+	eap_example_server_deinit();
+
+	return 0;
+}
diff --git a/hostap/eap_example/eap_example_peer.c b/hostap/eap_example/eap_example_peer.c
new file mode 100644
index 0000000..2ffc9fc
--- /dev/null
+++ b/hostap/eap_example/eap_example_peer.c
@@ -0,0 +1,378 @@
+/*
+ * Example application showing how EAP peer code from wpa_supplicant can be
+ * used as a library.
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_peer/eap.h"
+#include "eap_peer/eap_config.h"
+#include "wpabuf.h"
+
+void eap_example_server_rx(const u8 *data, size_t data_len);
+
+
+struct eap_peer_ctx {
+	Boolean eapSuccess;
+	Boolean eapRestart;
+	Boolean eapFail;
+	Boolean eapResp;
+	Boolean eapNoResp;
+	Boolean eapReq;
+	Boolean portEnabled;
+	Boolean altAccept; /* for EAP */
+	Boolean altReject; /* for EAP */
+	Boolean eapTriggerStart;
+
+	struct wpabuf *eapReqData; /* for EAP */
+
+	unsigned int idleWhile; /* for EAP state machine */
+
+	struct eap_peer_config eap_config;
+	struct eap_sm *eap;
+};
+
+
+static struct eap_peer_ctx eap_ctx;
+
+
+static struct eap_peer_config * peer_get_config(void *ctx)
+{
+	struct eap_peer_ctx *peer = ctx;
+	return &peer->eap_config;
+}
+
+
+static Boolean peer_get_bool(void *ctx, enum eapol_bool_var variable)
+{
+	struct eap_peer_ctx *peer = ctx;
+	if (peer == NULL)
+		return FALSE;
+	switch (variable) {
+	case EAPOL_eapSuccess:
+		return peer->eapSuccess;
+	case EAPOL_eapRestart:
+		return peer->eapRestart;
+	case EAPOL_eapFail:
+		return peer->eapFail;
+	case EAPOL_eapResp:
+		return peer->eapResp;
+	case EAPOL_eapNoResp:
+		return peer->eapNoResp;
+	case EAPOL_eapReq:
+		return peer->eapReq;
+	case EAPOL_portEnabled:
+		return peer->portEnabled;
+	case EAPOL_altAccept:
+		return peer->altAccept;
+	case EAPOL_altReject:
+		return peer->altReject;
+	case EAPOL_eapTriggerStart:
+		return peer->eapTriggerStart;
+	}
+	return FALSE;
+}
+
+
+static void peer_set_bool(void *ctx, enum eapol_bool_var variable,
+			  Boolean value)
+{
+	struct eap_peer_ctx *peer = ctx;
+	if (peer == NULL)
+		return;
+	switch (variable) {
+	case EAPOL_eapSuccess:
+		peer->eapSuccess = value;
+		break;
+	case EAPOL_eapRestart:
+		peer->eapRestart = value;
+		break;
+	case EAPOL_eapFail:
+		peer->eapFail = value;
+		break;
+	case EAPOL_eapResp:
+		peer->eapResp = value;
+		break;
+	case EAPOL_eapNoResp:
+		peer->eapNoResp = value;
+		break;
+	case EAPOL_eapReq:
+		peer->eapReq = value;
+		break;
+	case EAPOL_portEnabled:
+		peer->portEnabled = value;
+		break;
+	case EAPOL_altAccept:
+		peer->altAccept = value;
+		break;
+	case EAPOL_altReject:
+		peer->altReject = value;
+		break;
+	case EAPOL_eapTriggerStart:
+		peer->eapTriggerStart = value;
+		break;
+	}
+}
+
+
+static unsigned int peer_get_int(void *ctx, enum eapol_int_var variable)
+{
+	struct eap_peer_ctx *peer = ctx;
+	if (peer == NULL)
+		return 0;
+	switch (variable) {
+	case EAPOL_idleWhile:
+		return peer->idleWhile;
+	}
+	return 0;
+}
+
+
+static void peer_set_int(void *ctx, enum eapol_int_var variable,
+			 unsigned int value)
+{
+	struct eap_peer_ctx *peer = ctx;
+	if (peer == NULL)
+		return;
+	switch (variable) {
+	case EAPOL_idleWhile:
+		peer->idleWhile = value;
+		break;
+	}
+}
+
+
+static struct wpabuf * peer_get_eapReqData(void *ctx)
+{
+	struct eap_peer_ctx *peer = ctx;
+	if (peer == NULL || peer->eapReqData == NULL)
+		return NULL;
+
+	return peer->eapReqData;
+}
+
+
+static void peer_set_config_blob(void *ctx, struct wpa_config_blob *blob)
+{
+	printf("TODO: %s\n", __func__);
+}
+
+
+static const struct wpa_config_blob *
+peer_get_config_blob(void *ctx, const char *name)
+{
+	printf("TODO: %s\n", __func__);
+	return NULL;
+}
+
+
+static void peer_notify_pending(void *ctx)
+{
+	printf("TODO: %s\n", __func__);
+}
+
+
+static int eap_peer_register_methods(void)
+{
+	int ret = 0;
+
+#ifdef EAP_MD5
+	if (ret == 0)
+		ret = eap_peer_md5_register();
+#endif /* EAP_MD5 */
+
+#ifdef EAP_TLS
+	if (ret == 0)
+		ret = eap_peer_tls_register();
+#endif /* EAP_TLS */
+
+#ifdef EAP_MSCHAPv2
+	if (ret == 0)
+		ret = eap_peer_mschapv2_register();
+#endif /* EAP_MSCHAPv2 */
+
+#ifdef EAP_PEAP
+	if (ret == 0)
+		ret = eap_peer_peap_register();
+#endif /* EAP_PEAP */
+
+#ifdef EAP_TTLS
+	if (ret == 0)
+		ret = eap_peer_ttls_register();
+#endif /* EAP_TTLS */
+
+#ifdef EAP_GTC
+	if (ret == 0)
+		ret = eap_peer_gtc_register();
+#endif /* EAP_GTC */
+
+#ifdef EAP_OTP
+	if (ret == 0)
+		ret = eap_peer_otp_register();
+#endif /* EAP_OTP */
+
+#ifdef EAP_SIM
+	if (ret == 0)
+		ret = eap_peer_sim_register();
+#endif /* EAP_SIM */
+
+#ifdef EAP_LEAP
+	if (ret == 0)
+		ret = eap_peer_leap_register();
+#endif /* EAP_LEAP */
+
+#ifdef EAP_PSK
+	if (ret == 0)
+		ret = eap_peer_psk_register();
+#endif /* EAP_PSK */
+
+#ifdef EAP_AKA
+	if (ret == 0)
+		ret = eap_peer_aka_register();
+#endif /* EAP_AKA */
+
+#ifdef EAP_AKA_PRIME
+	if (ret == 0)
+		ret = eap_peer_aka_prime_register();
+#endif /* EAP_AKA_PRIME */
+
+#ifdef EAP_FAST
+	if (ret == 0)
+		ret = eap_peer_fast_register();
+#endif /* EAP_FAST */
+
+#ifdef EAP_PAX
+	if (ret == 0)
+		ret = eap_peer_pax_register();
+#endif /* EAP_PAX */
+
+#ifdef EAP_SAKE
+	if (ret == 0)
+		ret = eap_peer_sake_register();
+#endif /* EAP_SAKE */
+
+#ifdef EAP_GPSK
+	if (ret == 0)
+		ret = eap_peer_gpsk_register();
+#endif /* EAP_GPSK */
+
+#ifdef EAP_WSC
+	if (ret == 0)
+		ret = eap_peer_wsc_register();
+#endif /* EAP_WSC */
+
+#ifdef EAP_IKEV2
+	if (ret == 0)
+		ret = eap_peer_ikev2_register();
+#endif /* EAP_IKEV2 */
+
+#ifdef EAP_VENDOR_TEST
+	if (ret == 0)
+		ret = eap_peer_vendor_test_register();
+#endif /* EAP_VENDOR_TEST */
+
+#ifdef EAP_TNC
+	if (ret == 0)
+		ret = eap_peer_tnc_register();
+#endif /* EAP_TNC */
+
+	return ret;
+}
+
+
+static struct eapol_callbacks eap_cb;
+static struct eap_config eap_conf;
+
+int eap_example_peer_init(void)
+{
+	if (eap_peer_register_methods() < 0)
+		return -1;
+
+	os_memset(&eap_ctx, 0, sizeof(eap_ctx));
+
+	eap_ctx.eap_config.identity = (u8 *) os_strdup("user");
+	eap_ctx.eap_config.identity_len = 4;
+	eap_ctx.eap_config.password = (u8 *) os_strdup("password");
+	eap_ctx.eap_config.password_len = 8;
+	eap_ctx.eap_config.ca_cert = (u8 *) os_strdup("ca.pem");
+	eap_ctx.eap_config.fragment_size = 1398;
+
+	os_memset(&eap_cb, 0, sizeof(eap_cb));
+	eap_cb.get_config = peer_get_config;
+	eap_cb.get_bool = peer_get_bool;
+	eap_cb.set_bool = peer_set_bool;
+	eap_cb.get_int = peer_get_int;
+	eap_cb.set_int = peer_set_int;
+	eap_cb.get_eapReqData = peer_get_eapReqData;
+	eap_cb.set_config_blob = peer_set_config_blob;
+	eap_cb.get_config_blob = peer_get_config_blob;
+	eap_cb.notify_pending = peer_notify_pending;
+
+	os_memset(&eap_conf, 0, sizeof(eap_conf));
+	eap_ctx.eap = eap_peer_sm_init(&eap_ctx, &eap_cb, &eap_ctx, &eap_conf);
+	if (eap_ctx.eap == NULL)
+		return -1;
+
+	/* Enable "port" to allow authentication */
+	eap_ctx.portEnabled = TRUE;
+
+	return 0;
+}
+
+
+void eap_example_peer_deinit(void)
+{
+	eap_peer_sm_deinit(eap_ctx.eap);
+	eap_peer_unregister_methods();
+	wpabuf_free(eap_ctx.eapReqData);
+	os_free(eap_ctx.eap_config.identity);
+	os_free(eap_ctx.eap_config.password);
+	os_free(eap_ctx.eap_config.ca_cert);
+}
+
+
+int eap_example_peer_step(void)
+{
+	int res;
+	res = eap_peer_sm_step(eap_ctx.eap);
+
+	if (eap_ctx.eapResp) {
+		struct wpabuf *resp;
+		printf("==> Response\n");
+		eap_ctx.eapResp = FALSE;
+		resp = eap_get_eapRespData(eap_ctx.eap);
+		if (resp) {
+			/* Send EAP response to the server */
+			eap_example_server_rx(wpabuf_head(resp),
+					      wpabuf_len(resp));
+			wpabuf_free(resp);
+		}
+	}
+
+	if (eap_ctx.eapSuccess) {
+		res = 0;
+		if (eap_key_available(eap_ctx.eap)) {
+			const u8 *key;
+			size_t key_len;
+			key = eap_get_eapKeyData(eap_ctx.eap, &key_len);
+			wpa_hexdump(MSG_DEBUG, "EAP keying material",
+				    key, key_len);
+		}
+	}
+
+	return res;
+}
+
+
+void eap_example_peer_rx(const u8 *data, size_t data_len)
+{
+	/* Make received EAP message available to the EAP library */
+	eap_ctx.eapReq = TRUE;
+	wpabuf_free(eap_ctx.eapReqData);
+	eap_ctx.eapReqData = wpabuf_alloc_copy(data, data_len);
+}
diff --git a/hostap/eap_example/eap_example_server.c b/hostap/eap_example/eap_example_server.c
new file mode 100644
index 0000000..a081b87
--- /dev/null
+++ b/hostap/eap_example/eap_example_server.c
@@ -0,0 +1,296 @@
+/*
+ * Example application showing how EAP server code from hostapd can be used as
+ * a library.
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/tls.h"
+#include "eap_server/eap.h"
+#include "wpabuf.h"
+
+void eap_example_peer_rx(const u8 *data, size_t data_len);
+
+
+struct eap_server_ctx {
+	struct eap_eapol_interface *eap_if;
+	struct eap_sm *eap;
+	void *tls_ctx;
+};
+
+static struct eap_server_ctx eap_ctx;
+
+
+static int server_get_eap_user(void *ctx, const u8 *identity,
+			       size_t identity_len, int phase2,
+			       struct eap_user *user)
+{
+	os_memset(user, 0, sizeof(*user));
+
+	if (!phase2) {
+		/* Only allow EAP-PEAP as the Phase 1 method */
+		user->methods[0].vendor = EAP_VENDOR_IETF;
+		user->methods[0].method = EAP_TYPE_PEAP;
+		return 0;
+	}
+
+	if (identity_len != 4 || identity == NULL ||
+	    os_memcmp(identity, "user", 4) != 0) {
+		printf("Unknown user\n");
+		return -1;
+	}
+
+	/* Only allow EAP-MSCHAPv2 as the Phase 2 method */
+	user->methods[0].vendor = EAP_VENDOR_IETF;
+	user->methods[0].method = EAP_TYPE_MSCHAPV2;
+	user->password = (u8 *) os_strdup("password");
+	user->password_len = 8;
+
+	return 0;
+}
+
+
+static const char * server_get_eap_req_id_text(void *ctx, size_t *len)
+{
+	*len = 0;
+	return NULL;
+}
+
+
+static struct eapol_callbacks eap_cb;
+static struct eap_config eap_conf;
+
+static int eap_example_server_init_tls(void)
+{
+	struct tls_config tconf;
+	struct tls_connection_params tparams;
+
+	os_memset(&tconf, 0, sizeof(tconf));
+	eap_ctx.tls_ctx = tls_init(&tconf);
+	if (eap_ctx.tls_ctx == NULL)
+		return -1;
+
+	os_memset(&tparams, 0, sizeof(tparams));
+	tparams.ca_cert = "ca.pem";
+	tparams.client_cert = "server.pem";
+	/* tparams.private_key = "server.key"; */
+	tparams.private_key = "server-key.pem";
+	/* tparams.private_key_passwd = "whatever"; */
+	tparams.dh_file = "dh.conf";
+
+	if (tls_global_set_params(eap_ctx.tls_ctx, &tparams)) {
+		printf("Failed to set TLS parameters\n");
+		return -1;
+	}
+
+	if (tls_global_set_verify(eap_ctx.tls_ctx, 0)) {
+		printf("Failed to set check_crl\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eap_server_register_methods(void)
+{
+	int ret = 0;
+
+#ifdef EAP_SERVER_IDENTITY
+	if (ret == 0)
+		ret = eap_server_identity_register();
+#endif /* EAP_SERVER_IDENTITY */
+
+#ifdef EAP_SERVER_MD5
+	if (ret == 0)
+		ret = eap_server_md5_register();
+#endif /* EAP_SERVER_MD5 */
+
+#ifdef EAP_SERVER_TLS
+	if (ret == 0)
+		ret = eap_server_tls_register();
+#endif /* EAP_SERVER_TLS */
+
+#ifdef EAP_SERVER_MSCHAPV2
+	if (ret == 0)
+		ret = eap_server_mschapv2_register();
+#endif /* EAP_SERVER_MSCHAPV2 */
+
+#ifdef EAP_SERVER_PEAP
+	if (ret == 0)
+		ret = eap_server_peap_register();
+#endif /* EAP_SERVER_PEAP */
+
+#ifdef EAP_SERVER_TLV
+	if (ret == 0)
+		ret = eap_server_tlv_register();
+#endif /* EAP_SERVER_TLV */
+
+#ifdef EAP_SERVER_GTC
+	if (ret == 0)
+		ret = eap_server_gtc_register();
+#endif /* EAP_SERVER_GTC */
+
+#ifdef EAP_SERVER_TTLS
+	if (ret == 0)
+		ret = eap_server_ttls_register();
+#endif /* EAP_SERVER_TTLS */
+
+#ifdef EAP_SERVER_SIM
+	if (ret == 0)
+		ret = eap_server_sim_register();
+#endif /* EAP_SERVER_SIM */
+
+#ifdef EAP_SERVER_AKA
+	if (ret == 0)
+		ret = eap_server_aka_register();
+#endif /* EAP_SERVER_AKA */
+
+#ifdef EAP_SERVER_AKA_PRIME
+	if (ret == 0)
+		ret = eap_server_aka_prime_register();
+#endif /* EAP_SERVER_AKA_PRIME */
+
+#ifdef EAP_SERVER_PAX
+	if (ret == 0)
+		ret = eap_server_pax_register();
+#endif /* EAP_SERVER_PAX */
+
+#ifdef EAP_SERVER_PSK
+	if (ret == 0)
+		ret = eap_server_psk_register();
+#endif /* EAP_SERVER_PSK */
+
+#ifdef EAP_SERVER_SAKE
+	if (ret == 0)
+		ret = eap_server_sake_register();
+#endif /* EAP_SERVER_SAKE */
+
+#ifdef EAP_SERVER_GPSK
+	if (ret == 0)
+		ret = eap_server_gpsk_register();
+#endif /* EAP_SERVER_GPSK */
+
+#ifdef EAP_SERVER_VENDOR_TEST
+	if (ret == 0)
+		ret = eap_server_vendor_test_register();
+#endif /* EAP_SERVER_VENDOR_TEST */
+
+#ifdef EAP_SERVER_FAST
+	if (ret == 0)
+		ret = eap_server_fast_register();
+#endif /* EAP_SERVER_FAST */
+
+#ifdef EAP_SERVER_WSC
+	if (ret == 0)
+		ret = eap_server_wsc_register();
+#endif /* EAP_SERVER_WSC */
+
+#ifdef EAP_SERVER_IKEV2
+	if (ret == 0)
+		ret = eap_server_ikev2_register();
+#endif /* EAP_SERVER_IKEV2 */
+
+#ifdef EAP_SERVER_TNC
+	if (ret == 0)
+		ret = eap_server_tnc_register();
+#endif /* EAP_SERVER_TNC */
+
+	return ret;
+}
+
+
+int eap_example_server_init(void)
+{
+	if (eap_server_register_methods() < 0)
+		return -1;
+
+	os_memset(&eap_ctx, 0, sizeof(eap_ctx));
+
+	if (eap_example_server_init_tls() < 0)
+		return -1;
+
+	os_memset(&eap_cb, 0, sizeof(eap_cb));
+	eap_cb.get_eap_user = server_get_eap_user;
+	eap_cb.get_eap_req_id_text = server_get_eap_req_id_text;
+
+	os_memset(&eap_conf, 0, sizeof(eap_conf));
+	eap_conf.eap_server = 1;
+	eap_conf.ssl_ctx = eap_ctx.tls_ctx;
+
+	eap_ctx.eap = eap_server_sm_init(&eap_ctx, &eap_cb, &eap_conf);
+	if (eap_ctx.eap == NULL)
+		return -1;
+
+	eap_ctx.eap_if = eap_get_interface(eap_ctx.eap);
+
+	/* Enable "port" and request EAP to start authentication. */
+	eap_ctx.eap_if->portEnabled = TRUE;
+	eap_ctx.eap_if->eapRestart = TRUE;
+
+	return 0;
+}
+
+
+void eap_example_server_deinit(void)
+{
+	eap_server_sm_deinit(eap_ctx.eap);
+	eap_server_unregister_methods();
+	tls_deinit(eap_ctx.tls_ctx);
+}
+
+
+int eap_example_server_step(void)
+{
+	int res, process = 0;
+
+	res = eap_server_sm_step(eap_ctx.eap);
+
+	if (eap_ctx.eap_if->eapReq) {
+		printf("==> Request\n");
+		process = 1;
+		eap_ctx.eap_if->eapReq = 0;
+	}
+
+	if (eap_ctx.eap_if->eapSuccess) {
+		printf("==> Success\n");
+		process = 1;
+		res = 0;
+		eap_ctx.eap_if->eapSuccess = 0;
+
+		if (eap_ctx.eap_if->eapKeyAvailable) {
+			wpa_hexdump(MSG_DEBUG, "EAP keying material",
+				    eap_ctx.eap_if->eapKeyData,
+				    eap_ctx.eap_if->eapKeyDataLen);
+		}
+	}
+
+	if (eap_ctx.eap_if->eapFail) {
+		printf("==> Fail\n");
+		process = 1;
+		eap_ctx.eap_if->eapFail = 0;
+	}
+
+	if (process && eap_ctx.eap_if->eapReqData) {
+		/* Send EAP response to the server */
+		eap_example_peer_rx(wpabuf_head(eap_ctx.eap_if->eapReqData),
+				    wpabuf_len(eap_ctx.eap_if->eapReqData));
+	}
+
+	return res;
+}
+
+
+void eap_example_server_rx(const u8 *data, size_t data_len)
+{
+	/* Make received EAP message available to the EAP library */
+	wpabuf_free(eap_ctx.eap_if->eapRespData);
+	eap_ctx.eap_if->eapRespData = wpabuf_alloc_copy(data, data_len);
+	if (eap_ctx.eap_if->eapRespData)
+		eap_ctx.eap_if->eapResp = TRUE;
+}
diff --git a/hostap/eap_example/server-key.pem b/hostap/eap_example/server-key.pem
new file mode 100644
index 0000000..d98c4dd
--- /dev/null
+++ b/hostap/eap_example/server-key.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDToYuDPmjEWu+/Aj0RVWTSb07sX6dAkPnrTaUjZAG5AhjRqJWz
+zD50kFmVKi+R7GgS5tlGzLUtokdwjuSUAmz8tMXwIwmVeS0HluFDVSi94XbVRczE
++nyoDigg1RGyy1mc3t5RG84bvNatq98OceJag4ngh8L8I4k1qTLRMlyBJwIDAQAB
+AoGAP+v0asDn/h8FeSkg7uJfIJyUNxsxNnRTuHnsXkMvrgTvICyOgw828hhDpqVm
+VuoUCVmG2Tatpsn0UBApBHezGRh0u1syWoGM8fiDvZmoYmhFe5FxKnftg3KNXhDf
+Agk4OxwNNPBXpQFQP+GNxh6Qs7FEkYHLRh/J7vC0+wp3UWECQQDzcTQZXqYPow5M
+uinL819HKfh1n2257w1HGvw8cMCiYbKRyR74Q18TJcxuEyEwnPrg5ZGpMPDKiIOU
+SlgAMLBXAkEA3oxBpRue1Kqb2+Fq6lhZ7PQiZC5F69upIb/wxbk8ByImEl1pUKFW
+rV+YoKujbnj77PmMq1+R0dFkT1ai3zDzsQJBAMa3CUgMMpFhEDMhYyzQJF36rI2W
+7gJwV+5K4MqVXyktho3qFhWhKOKAYDcZ9mWwPjmGKzhocqVgecd6SAsfs1ECQA7r
+xHL3eRy1G6IQaQSxS8YxUCT7XUDFB3/1yITZOIcZ6QeOL8NyLceOA0OyflCn1+w5
+hw7uZ25z5Y/UNTNVquECQEgto3zPneEW06qkEnRz9EbLtWR3nRBS/QGrjOFNUuln
+pNhVUH4RB17Kk35xveUTz4U/Iw/WRfGNjFLHrtR/5xk=
+-----END RSA PRIVATE KEY-----
diff --git a/hostap/eap_example/server.key b/hostap/eap_example/server.key
new file mode 100644
index 0000000..4f32591
--- /dev/null
+++ b/hostap/eap_example/server.key
Binary files differ
diff --git a/hostap/eap_example/server.pem b/hostap/eap_example/server.pem
new file mode 100644
index 0000000..02f6e7b
--- /dev/null
+++ b/hostap/eap_example/server.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0zCCAjygAwIBAgIJAIb4NS4TdLXVMA0GCSqGSIb3DQEBBQUAMGExCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMQ4wDAYDVQQKEwV3MS5maTEQMA4G
+A1UEAxMHVGVzdCBDQTEbMBkGCSqGSIb3DQEJARYMdGVzdGNhQHcxLmZpMB4XDTA3
+MTIwOTAzMTUwOFoXDTE3MTIwNjAzMTUwOFoweTELMAkGA1UEBhMCVVMxEzARBgNV
+BAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xDjAMBgNVBAoT
+BXcxLmZpMRAwDgYDVQQDEwdUZXN0IEFTMRswGQYJKoZIhvcNAQkBFgx0ZXN0YXNA
+dzEuZmkwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOhi4M+aMRa778CPRFV
+ZNJvTuxfp0CQ+etNpSNkAbkCGNGolbPMPnSQWZUqL5HsaBLm2UbMtS2iR3CO5JQC
+bPy0xfAjCZV5LQeW4UNVKL3hdtVFzMT6fKgOKCDVEbLLWZze3lEbzhu81q2r3w5x
+4lqDieCHwvwjiTWpMtEyXIEnAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4
+QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRb
+xGTC3mPimgyGb5vYLLV5wyc9ITAfBgNVHSMEGDAWgBSoSbkiyeiYXAf3UYAfI9If
+ZjpaSjANBgkqhkiG9w0BAQUFAAOBgQA9wVGtroz/rsx1EeALJejW01SAr4kpTxoS
+WP6zuWFb+J/lJd7DeVM6/QBYAwZb0fB6nwSpJJCj6XDRZtN/yLeaTd/rCZrfom4Z
+8gbkWMTXDn2Cea2VnCe5W0gK+4dIj5DD5CpPvgt4lYqlwN0WAih6twd7Q4x/tiiJ
+ejNQzlTHOg==
+-----END CERTIFICATE-----
diff --git a/hostap/hostapd/Android.mk b/hostap/hostapd/Android.mk
new file mode 100644
index 0000000..5885f2b
--- /dev/null
+++ b/hostap/hostapd/Android.mk
@@ -0,0 +1,976 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+WPA_BUILD_HOSTAPD := false
+ifneq ($(BOARD_HOSTAPD_DRIVER),)
+  WPA_BUILD_HOSTAPD := true
+  CONFIG_DRIVER_$(BOARD_HOSTAPD_DRIVER) := y
+endif
+
+ifeq ($(WPA_BUILD_HOSTAPD),true)
+
+include $(LOCAL_PATH)/android.config
+
+# To ignore possible wrong network configurations
+L_CFLAGS = -DWPA_IGNORE_CONFIG_ERRORS
+
+L_CFLAGS += -DVERSION_STR_POSTFIX=\"-$(PLATFORM_VERSION)\"
+
+# Set Android log name
+L_CFLAGS += -DANDROID_LOG_NAME=\"hostapd\"
+
+# Disable unused parameter warnings
+L_CFLAGS += -Wno-unused-parameter
+
+# Set Android extended P2P functionality
+L_CFLAGS += -DANDROID_P2P
+
+ifeq ($(BOARD_HOSTAPD_PRIVATE_LIB),)
+L_CFLAGS += -DANDROID_LIB_STUB
+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/hostapd\"
+
+# To force sizeof(enum) = 4
+ifeq ($(TARGET_ARCH),arm)
+L_CFLAGS += -mabi=aapcs-linux
+endif
+
+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
+ifdef CONFIG_NATIVE_WINDOWS
+CONFIG_OS=win32
+else
+CONFIG_OS=unix
+endif
+endif
+
+ifeq ($(CONFIG_OS), internal)
+L_CFLAGS += -DOS_NO_C_LIB_DEFINES
+endif
+
+ifdef CONFIG_NATIVE_WINDOWS
+L_CFLAGS += -DCONFIG_NATIVE_WINDOWS
+LIBS += -lws2_32
+endif
+
+OBJS = main.c
+OBJS += config_file.c
+
+OBJS += src/ap/hostapd.c
+OBJS += src/ap/wpa_auth_glue.c
+OBJS += src/ap/drv_callbacks.c
+OBJS += src/ap/ap_drv_ops.c
+OBJS += src/ap/utils.c
+OBJS += src/ap/authsrv.c
+OBJS += src/ap/ieee802_1x.c
+OBJS += src/ap/ap_config.c
+OBJS += src/ap/eap_user_db.c
+OBJS += src/ap/ieee802_11_auth.c
+OBJS += src/ap/sta_info.c
+OBJS += src/ap/wpa_auth.c
+OBJS += src/ap/tkip_countermeasures.c
+OBJS += src/ap/ap_mlme.c
+OBJS += src/ap/wpa_auth_ie.c
+OBJS += src/ap/preauth_auth.c
+OBJS += src/ap/pmksa_cache_auth.c
+OBJS += src/ap/ieee802_11_shared.c
+OBJS += src/ap/beacon.c
+OBJS += src/ap/bss_load.c
+OBJS_d =
+OBJS_p =
+LIBS =
+LIBS_c =
+HOBJS =
+LIBS_h =
+
+NEED_RC4=y
+NEED_AES=y
+NEED_MD5=y
+NEED_SHA1=y
+
+OBJS += src/drivers/drivers.c
+L_CFLAGS += -DHOSTAPD
+
+ifdef CONFIG_WPA_TRACE
+L_CFLAGS += -DWPA_TRACE
+OBJS += src/utils/trace.c
+HOBJS += src/utils/trace.c
+LDFLAGS += -rdynamic
+L_CFLAGS += -funwind-tables
+ifdef CONFIG_WPA_TRACE_BFD
+L_CFLAGS += -DWPA_TRACE_BFD
+LIBS += -lbfd
+LIBS_c += -lbfd
+LIBS_h += -lbfd
+endif
+endif
+
+OBJS += src/utils/eloop.c
+
+ifdef CONFIG_ELOOP_POLL
+L_CFLAGS += -DCONFIG_ELOOP_POLL
+endif
+
+ifdef CONFIG_ELOOP_EPOLL
+L_CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
+
+OBJS += src/utils/common.c
+OBJS += src/utils/wpa_debug.c
+OBJS += src/utils/wpabuf.c
+OBJS += src/utils/os_$(CONFIG_OS).c
+OBJS += src/utils/ip_addr.c
+
+OBJS += src/common/ieee802_11_common.c
+OBJS += src/common/wpa_common.c
+OBJS += src/common/hw_features_common.c
+
+OBJS += src/eapol_auth/eapol_auth_sm.c
+
+
+ifndef CONFIG_NO_DUMP_STATE
+# 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 += src/eapol_auth/eapol_auth_dump.c
+endif
+
+ifdef CONFIG_NO_RADIUS
+L_CFLAGS += -DCONFIG_NO_RADIUS
+CONFIG_NO_ACCOUNTING=y
+else
+OBJS += src/radius/radius.c
+OBJS += src/radius/radius_client.c
+OBJS += src/radius/radius_das.c
+endif
+
+ifdef CONFIG_NO_ACCOUNTING
+L_CFLAGS += -DCONFIG_NO_ACCOUNTING
+else
+OBJS += src/ap/accounting.c
+endif
+
+ifdef CONFIG_NO_VLAN
+L_CFLAGS += -DCONFIG_NO_VLAN
+else
+OBJS += src/ap/vlan_init.c
+ifdef CONFIG_VLAN_NETLINK
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+OBJS += src/ap/vlan_util.c
+endif
+L_CFLAGS += -DCONFIG_VLAN_NETLINK
+endif
+endif
+
+ifdef CONFIG_NO_CTRL_IFACE
+L_CFLAGS += -DCONFIG_NO_CTRL_IFACE
+else
+OBJS += ctrl_iface.c
+OBJS += src/ap/ctrl_iface_ap.c
+endif
+
+L_CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX
+
+ifdef CONFIG_IAPP
+L_CFLAGS += -DCONFIG_IAPP
+OBJS += src/ap/iapp.c
+endif
+
+ifdef CONFIG_RSN_PREAUTH
+L_CFLAGS += -DCONFIG_RSN_PREAUTH
+CONFIG_L2_PACKET=y
+endif
+
+ifdef CONFIG_PEERKEY
+L_CFLAGS += -DCONFIG_PEERKEY
+OBJS += src/ap/peerkey_auth.c
+endif
+
+ifdef CONFIG_HS20
+NEED_AES_OMAC1=y
+CONFIG_PROXYARP=y
+endif
+
+ifdef CONFIG_PROXYARP
+CONFIG_L2_PACKET=y
+endif
+
+ifdef CONFIG_SUITEB
+L_CFLAGS += -DCONFIG_SUITEB
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_SUITEB192
+L_CFLAGS += -DCONFIG_SUITEB192
+NEED_SHA384=y
+endif
+
+ifdef CONFIG_IEEE80211W
+L_CFLAGS += -DCONFIG_IEEE80211W
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_IEEE80211R
+L_CFLAGS += -DCONFIG_IEEE80211R
+OBJS += src/ap/wpa_auth_ft.c
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+NEED_AES_UNWRAP=y
+endif
+
+ifdef CONFIG_SAE
+L_CFLAGS += -DCONFIG_SAE
+OBJS += src/common/sae.c
+NEED_ECC=y
+NEED_DH_GROUPS=y
+endif
+
+ifdef CONFIG_WNM
+L_CFLAGS += -DCONFIG_WNM
+OBJS += src/ap/wnm_ap.c
+endif
+
+ifdef CONFIG_IEEE80211N
+L_CFLAGS += -DCONFIG_IEEE80211N
+endif
+
+ifdef CONFIG_IEEE80211AC
+L_CFLAGS += -DCONFIG_IEEE80211AC
+endif
+
+ifdef CONFIG_FST
+L_CFLAGS += -DCONFIG_FST
+OBJS += src/fst/fst.c
+OBJS += src/fst/fst_group.c
+OBJS += src/fst/fst_iface.c
+OBJS += src/fst/fst_session.c
+OBJS += src/fst/fst_ctrl_aux.c
+ifdef CONFIG_FST_TEST
+L_CFLAGS += -DCONFIG_FST_TEST
+endif
+ifndef CONFIG_NO_CTRL_IFACE
+OBJS += src/fst/fst_ctrl_iface.c
+endif
+endif
+
+
+include $(LOCAL_PATH)/src/drivers/drivers.mk
+
+OBJS += $(DRV_AP_OBJS)
+L_CFLAGS += $(DRV_AP_CFLAGS)
+LDFLAGS += $(DRV_AP_LDFLAGS)
+LIBS += $(DRV_AP_LIBS)
+
+ifdef CONFIG_L2_PACKET
+ifdef CONFIG_DNET_PCAP
+ifdef CONFIG_L2_FREEBSD
+LIBS += -lpcap
+OBJS += src/l2_packet/l2_packet_freebsd.c
+else
+LIBS += -ldnet -lpcap
+OBJS += src/l2_packet/l2_packet_pcap.c
+endif
+else
+OBJS += src/l2_packet/l2_packet_linux.c
+endif
+else
+OBJS += src/l2_packet/l2_packet_none.c
+endif
+
+
+ifdef CONFIG_EAP_MD5
+L_CFLAGS += -DEAP_SERVER_MD5
+OBJS += src/eap_server/eap_server_md5.c
+CHAP=y
+endif
+
+ifdef CONFIG_EAP_TLS
+L_CFLAGS += -DEAP_SERVER_TLS
+OBJS += src/eap_server/eap_server_tls.c
+TLS_FUNCS=y
+endif
+
+ifdef CONFIG_EAP_UNAUTH_TLS
+L_CFLAGS += -DEAP_SERVER_UNAUTH_TLS
+ifndef CONFIG_EAP_TLS
+OBJS += src/eap_server/eap_server_tls.c
+TLS_FUNCS=y
+endif
+endif
+
+ifdef CONFIG_EAP_PEAP
+L_CFLAGS += -DEAP_SERVER_PEAP
+OBJS += src/eap_server/eap_server_peap.c
+OBJS += src/eap_common/eap_peap_common.c
+TLS_FUNCS=y
+CONFIG_EAP_MSCHAPV2=y
+endif
+
+ifdef CONFIG_EAP_TTLS
+L_CFLAGS += -DEAP_SERVER_TTLS
+OBJS += src/eap_server/eap_server_ttls.c
+TLS_FUNCS=y
+CHAP=y
+endif
+
+ifdef CONFIG_EAP_MSCHAPV2
+L_CFLAGS += -DEAP_SERVER_MSCHAPV2
+OBJS += src/eap_server/eap_server_mschapv2.c
+MS_FUNCS=y
+endif
+
+ifdef CONFIG_EAP_GTC
+L_CFLAGS += -DEAP_SERVER_GTC
+OBJS += src/eap_server/eap_server_gtc.c
+endif
+
+ifdef CONFIG_EAP_SIM
+L_CFLAGS += -DEAP_SERVER_SIM
+OBJS += src/eap_server/eap_server_sim.c
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_AKA
+L_CFLAGS += -DEAP_SERVER_AKA
+OBJS += src/eap_server/eap_server_aka.c
+CONFIG_EAP_SIM_COMMON=y
+NEED_SHA256=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_AKA_PRIME
+L_CFLAGS += -DEAP_SERVER_AKA_PRIME
+endif
+
+ifdef CONFIG_EAP_SIM_COMMON
+OBJS += src/eap_common/eap_sim_common.c
+# Example EAP-SIM/AKA interface for GSM/UMTS authentication. This can be
+# replaced with another file implementating the interface specified in
+# eap_sim_db.h.
+OBJS += src/eap_server/eap_sim_db.c
+NEED_FIPS186_2_PRF=y
+endif
+
+ifdef CONFIG_EAP_PAX
+L_CFLAGS += -DEAP_SERVER_PAX
+OBJS += src/eap_server/eap_server_pax.c src/eap_common/eap_pax_common.c
+endif
+
+ifdef CONFIG_EAP_PSK
+L_CFLAGS += -DEAP_SERVER_PSK
+OBJS += src/eap_server/eap_server_psk.c src/eap_common/eap_psk_common.c
+NEED_AES_OMAC1=y
+NEED_AES_ENCBLOCK=y
+NEED_AES_EAX=y
+endif
+
+ifdef CONFIG_EAP_SAKE
+L_CFLAGS += -DEAP_SERVER_SAKE
+OBJS += src/eap_server/eap_server_sake.c src/eap_common/eap_sake_common.c
+endif
+
+ifdef CONFIG_EAP_GPSK
+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_GPSK_SHA256
+endif
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_EAP_PWD
+L_CFLAGS += -DEAP_SERVER_PWD
+OBJS += src/eap_server/eap_server_pwd.c src/eap_common/eap_pwd_common.c
+NEED_SHA256=y
+endif
+
+ifdef CONFIG_EAP_EKE
+L_CFLAGS += -DEAP_SERVER_EKE
+OBJS += src/eap_server/eap_server_eke.c src/eap_common/eap_eke_common.c
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+endif
+
+ifdef CONFIG_EAP_VENDOR_TEST
+L_CFLAGS += -DEAP_SERVER_VENDOR_TEST
+OBJS += src/eap_server/eap_server_vendor_test.c
+endif
+
+ifdef CONFIG_EAP_FAST
+L_CFLAGS += -DEAP_SERVER_FAST
+OBJS += src/eap_server/eap_server_fast.c
+OBJS += src/eap_common/eap_fast_common.c
+TLS_FUNCS=y
+NEED_T_PRF=y
+NEED_AES_UNWRAP=y
+endif
+
+ifdef CONFIG_WPS
+L_CFLAGS += -DCONFIG_WPS -DEAP_SERVER_WSC
+OBJS += src/utils/uuid.c
+OBJS += src/ap/wps_hostapd.c
+OBJS += src/eap_server/eap_server_wsc.c src/eap_common/eap_wsc_common.c
+OBJS += src/wps/wps.c
+OBJS += src/wps/wps_common.c
+OBJS += src/wps/wps_attr_parse.c
+OBJS += src/wps/wps_attr_build.c
+OBJS += src/wps/wps_attr_process.c
+OBJS += src/wps/wps_dev_attr.c
+OBJS += src/wps/wps_enrollee.c
+OBJS += src/wps/wps_registrar.c
+NEED_DH_GROUPS=y
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_AES_CBC=y
+NEED_MODEXP=y
+CONFIG_EAP=y
+
+ifdef CONFIG_WPS_NFC
+L_CFLAGS += -DCONFIG_WPS_NFC
+OBJS += src/wps/ndef.c
+NEED_WPS_OOB=y
+endif
+
+ifdef NEED_WPS_OOB
+L_CFLAGS += -DCONFIG_WPS_OOB
+endif
+
+ifdef CONFIG_WPS_UPNP
+L_CFLAGS += -DCONFIG_WPS_UPNP
+OBJS += src/wps/wps_upnp.c
+OBJS += src/wps/wps_upnp_ssdp.c
+OBJS += src/wps/wps_upnp_web.c
+OBJS += src/wps/wps_upnp_event.c
+OBJS += src/wps/wps_upnp_ap.c
+OBJS += src/wps/upnp_xml.c
+OBJS += src/wps/httpread.c
+OBJS += src/wps/http_client.c
+OBJS += src/wps/http_server.c
+endif
+
+ifdef CONFIG_WPS_STRICT
+L_CFLAGS += -DCONFIG_WPS_STRICT
+OBJS += src/wps/wps_validate.c
+endif
+
+ifdef CONFIG_WPS_TESTING
+L_CFLAGS += -DCONFIG_WPS_TESTING
+endif
+
+endif
+
+ifdef CONFIG_EAP_IKEV2
+L_CFLAGS += -DEAP_SERVER_IKEV2
+OBJS += src/eap_server/eap_server_ikev2.c src/eap_server/ikev2.c
+OBJS += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+NEED_MODEXP=y
+NEED_CIPHER=y
+endif
+
+ifdef CONFIG_EAP_TNC
+L_CFLAGS += -DEAP_SERVER_TNC
+OBJS += src/eap_server/eap_server_tnc.c
+OBJS += src/eap_server/tncs.c
+NEED_BASE64=y
+ifndef CONFIG_DRIVER_BSD
+LIBS += -ldl
+endif
+endif
+
+# Basic EAP functionality is needed for EAPOL
+OBJS += eap_register.c
+OBJS += src/eap_server/eap_server.c
+OBJS += src/eap_common/eap_common.c
+OBJS += src/eap_server/eap_server_methods.c
+OBJS += src/eap_server/eap_server_identity.c
+L_CFLAGS += -DEAP_SERVER_IDENTITY
+
+ifdef CONFIG_EAP
+L_CFLAGS += -DEAP_SERVER
+endif
+
+ifdef CONFIG_PKCS12
+L_CFLAGS += -DPKCS12_FUNCS
+endif
+
+ifdef MS_FUNCS
+OBJS += src/crypto/ms_funcs.c
+NEED_DES=y
+NEED_MD4=y
+endif
+
+ifdef CHAP
+OBJS += src/eap_common/chap.c
+endif
+
+ifdef TLS_FUNCS
+NEED_DES=y
+# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS)
+L_CFLAGS += -DEAP_TLS_FUNCS
+OBJS += src/eap_server/eap_server_tls_common.c
+NEED_TLS_PRF=y
+endif
+
+ifndef CONFIG_TLS
+CONFIG_TLS=openssl
+endif
+
+ifdef CONFIG_TLSV11
+L_CFLAGS += -DCONFIG_TLSV11
+endif
+
+ifdef CONFIG_TLSV12
+L_CFLAGS += -DCONFIG_TLSV12
+NEED_SHA256=y
+endif
+
+ifeq ($(CONFIG_TLS), openssl)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_openssl.c
+LIBS += -lssl
+endif
+OBJS += src/crypto/crypto_openssl.c
+HOBJS += src/crypto/crypto_openssl.c
+ifdef NEED_FIPS186_2_PRF
+OBJS += src/crypto/fips_prf_openssl.c
+endif
+NEED_SHA256=y
+NEED_TLS_PRF_SHA256=y
+LIBS += -lcrypto
+LIBS_h += -lcrypto
+endif
+
+ifeq ($(CONFIG_TLS), gnutls)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_gnutls.c
+LIBS += -lgnutls -lgpg-error
+endif
+OBJS += src/crypto/crypto_gnutls.c
+HOBJS += src/crypto/crypto_gnutls.c
+ifdef NEED_FIPS186_2_PRF
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
+endif
+LIBS += -lgcrypt
+LIBS_h += -lgcrypt
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+
+ifeq ($(CONFIG_TLS), internal)
+ifndef CONFIG_CRYPTO
+CONFIG_CRYPTO=internal
+endif
+ifdef TLS_FUNCS
+OBJS += src/crypto/crypto_internal-rsa.c
+OBJS += src/crypto/tls_internal.c
+OBJS += src/tls/tlsv1_common.c
+OBJS += src/tls/tlsv1_record.c
+OBJS += src/tls/tlsv1_cred.c
+OBJS += src/tls/tlsv1_server.c
+OBJS += src/tls/tlsv1_server_write.c
+OBJS += src/tls/tlsv1_server_read.c
+OBJS += src/tls/asn1.c
+OBJS += src/tls/rsa.c
+OBJS += src/tls/x509v3.c
+OBJS += src/tls/pkcs1.c
+OBJS += src/tls/pkcs5.c
+OBJS += src/tls/pkcs8.c
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+ifdef CONFIG_TLSV12
+NEED_TLS_PRF_SHA256=y
+endif
+NEED_MODEXP=y
+NEED_CIPHER=y
+L_CFLAGS += -DCONFIG_TLS_INTERNAL
+L_CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+endif
+ifdef NEED_CIPHER
+NEED_DES=y
+OBJS += src/crypto/crypto_internal-cipher.c
+endif
+ifdef NEED_MODEXP
+OBJS += src/crypto/crypto_internal-modexp.c
+OBJS += src/tls/bignum.c
+endif
+ifeq ($(CONFIG_CRYPTO), libtomcrypt)
+OBJS += src/crypto/crypto_libtomcrypt.c
+LIBS += -ltomcrypt -ltfm
+LIBS_h += -ltomcrypt -ltfm
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), internal)
+OBJS += src/crypto/crypto_internal.c
+NEED_AES_DEC=y
+L_CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+ifdef CONFIG_INTERNAL_LIBTOMMATH
+L_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST
+L_CFLAGS += -DLTM_FAST
+endif
+else
+LIBS += -ltommath
+LIBS_h += -ltommath
+endif
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_DES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD4=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), cryptoapi)
+OBJS += src/crypto/crypto_cryptoapi.c
+OBJS_p += src/crypto/crypto_cryptoapi.c
+L_CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+ifeq ($(CONFIG_TLS), none)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_none.c
+L_CFLAGS += -DEAP_TLS_NONE
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+endif
+OBJS += src/crypto/crypto_none.c
+OBJS_p += src/crypto/crypto_none.c
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+
+ifndef TLS_FUNCS
+OBJS += src/crypto/tls_none.c
+ifeq ($(CONFIG_TLS), internal)
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+AESOBJS = # none so far
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-enc.c
+endif
+
+ifneq ($(CONFIG_TLS), openssl)
+AESOBJS += src/crypto/aes-wrap.c
+endif
+ifdef NEED_AES_EAX
+AESOBJS += src/crypto/aes-eax.c
+NEED_AES_CTR=y
+endif
+ifdef NEED_AES_CTR
+AESOBJS += src/crypto/aes-ctr.c
+endif
+ifdef NEED_AES_ENCBLOCK
+AESOBJS += src/crypto/aes-encblock.c
+endif
+ifdef NEED_AES_OMAC1
+AESOBJS += src/crypto/aes-omac1.c
+endif
+ifdef NEED_AES_UNWRAP
+ifneq ($(CONFIG_TLS), openssl)
+NEED_AES_DEC=y
+AESOBJS += src/crypto/aes-unwrap.c
+endif
+endif
+ifdef NEED_AES_CBC
+NEED_AES_DEC=y
+ifneq ($(CONFIG_TLS), openssl)
+AESOBJS += src/crypto/aes-cbc.c
+endif
+endif
+ifdef NEED_AES_DEC
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += src/crypto/aes-internal-dec.c
+endif
+endif
+ifdef NEED_AES
+OBJS += $(AESOBJS)
+endif
+
+SHA1OBJS =
+ifdef NEED_SHA1
+ifneq ($(CONFIG_TLS), openssl)
+SHA1OBJS += src/crypto/sha1.c
+endif
+SHA1OBJS += src/crypto/sha1-prf.c
+ifdef CONFIG_INTERNAL_SHA1
+SHA1OBJS += src/crypto/sha1-internal.c
+ifdef NEED_FIPS186_2_PRF
+SHA1OBJS += src/crypto/fips_prf_internal.c
+endif
+endif
+ifneq ($(CONFIG_TLS), openssl)
+SHA1OBJS += src/crypto/sha1-pbkdf2.c
+endif
+ifdef NEED_T_PRF
+SHA1OBJS += src/crypto/sha1-tprf.c
+endif
+ifdef NEED_TLS_PRF
+SHA1OBJS += src/crypto/sha1-tlsprf.c
+endif
+endif
+
+ifdef NEED_SHA1
+OBJS += $(SHA1OBJS)
+endif
+
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += src/crypto/md5.c
+endif
+
+ifdef NEED_MD5
+ifdef CONFIG_INTERNAL_MD5
+OBJS += src/crypto/md5-internal.c
+HOBJS += src/crypto/md5-internal.c
+endif
+endif
+
+ifdef NEED_MD4
+ifdef CONFIG_INTERNAL_MD4
+OBJS += src/crypto/md4-internal.c
+endif
+endif
+
+ifdef NEED_DES
+ifdef CONFIG_INTERNAL_DES
+OBJS += src/crypto/des-internal.c
+endif
+endif
+
+ifdef CONFIG_NO_RC4
+L_CFLAGS += -DCONFIG_NO_RC4
+endif
+
+ifdef NEED_RC4
+ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
+OBJS += src/crypto/rc4.c
+endif
+endif
+endif
+
+ifdef NEED_SHA256
+L_CFLAGS += -DCONFIG_SHA256
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += src/crypto/sha256.c
+endif
+OBJS += src/crypto/sha256-prf.c
+ifdef CONFIG_INTERNAL_SHA256
+OBJS += src/crypto/sha256-internal.c
+endif
+ifdef NEED_TLS_PRF_SHA256
+OBJS += src/crypto/sha256-tlsprf.c
+endif
+endif
+ifdef NEED_SHA384
+L_CFLAGS += -DCONFIG_SHA384
+OBJS += src/crypto/sha384-prf.c
+endif
+
+ifdef NEED_DH_GROUPS
+OBJS += src/crypto/dh_groups.c
+endif
+ifdef NEED_DH_GROUPS_ALL
+L_CFLAGS += -DALL_DH_GROUPS
+endif
+ifdef CONFIG_INTERNAL_DH_GROUP5
+ifdef NEED_DH_GROUPS
+OBJS += src/crypto/dh_group5.c
+endif
+endif
+
+ifdef NEED_ECC
+L_CFLAGS += -DCONFIG_ECC
+endif
+
+ifdef CONFIG_NO_RANDOM_POOL
+L_CFLAGS += -DCONFIG_NO_RANDOM_POOL
+else
+OBJS += src/crypto/random.c
+HOBJS += src/crypto/random.c
+HOBJS += src/utils/eloop.c
+HOBJS += $(SHA1OBJS)
+ifneq ($(CONFIG_TLS), openssl)
+HOBJS += src/crypto/md5.c
+endif
+endif
+
+ifdef CONFIG_RADIUS_SERVER
+L_CFLAGS += -DRADIUS_SERVER
+OBJS += src/radius/radius_server.c
+endif
+
+ifdef CONFIG_IPV6
+L_CFLAGS += -DCONFIG_IPV6
+endif
+
+ifdef CONFIG_DRIVER_RADIUS_ACL
+L_CFLAGS += -DCONFIG_DRIVER_RADIUS_ACL
+endif
+
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+# define CONFIG_FULL_DYNAMIC_VLAN to have hostapd manipulate bridges
+# and vlan interfaces for the vlan feature.
+L_CFLAGS += -DCONFIG_FULL_DYNAMIC_VLAN
+endif
+
+ifdef NEED_BASE64
+OBJS += src/utils/base64.c
+endif
+
+ifdef NEED_AP_MLME
+OBJS += src/ap/wmm.c
+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
+OBJS += src/ap/ieee802_11_ht.c
+endif
+
+ifdef CONFIG_IEEE80211AC
+OBJS += src/ap/ieee802_11_vht.c
+endif
+
+ifdef CONFIG_P2P_MANAGER
+L_CFLAGS += -DCONFIG_P2P_MANAGER
+OBJS += src/ap/p2p_hostapd.c
+endif
+
+ifdef CONFIG_HS20
+L_CFLAGS += -DCONFIG_HS20
+OBJS += src/ap/hs20.c
+CONFIG_INTERWORKING=y
+endif
+
+ifdef CONFIG_INTERWORKING
+L_CFLAGS += -DCONFIG_INTERWORKING
+OBJS += src/common/gas.c
+OBJS += src/ap/gas_serv.c
+endif
+
+ifdef CONFIG_PROXYARP
+L_CFLAGS += -DCONFIG_PROXYARP
+OBJS += src/ap/x_snoop.c
+OBJS += src/ap/dhcp_snoop.c
+ifdef CONFIG_IPV6
+OBJS += src/ap/ndisc_snoop.c
+endif
+endif
+
+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
+
+ifdef CONFIG_ANDROID_LOG
+L_CFLAGS += -DCONFIG_ANDROID_LOG
+endif
+
+OBJS_c = hostapd_cli.c src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c
+OBJS_c += src/utils/eloop.c
+OBJS_c += src/utils/common.c
+ifdef CONFIG_WPA_TRACE
+OBJS_c += src/utils/trace.c
+endif
+OBJS_c += src/utils/wpa_debug.c
+ifdef CONFIG_WPA_CLI_EDIT
+OBJS_c += src/utils/edit.c
+else
+OBJS_c += src/utils/edit_simple.c
+endif
+
+########################
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := hostapd_cli
+LOCAL_MODULE_TAGS := debug
+LOCAL_SHARED_LIBRARIES := libc libcutils liblog
+LOCAL_CFLAGS := $(L_CFLAGS)
+LOCAL_SRC_FILES := $(OBJS_c)
+LOCAL_C_INCLUDES := $(INCLUDES)
+include $(BUILD_EXECUTABLE)
+
+########################
+include $(CLEAR_VARS)
+LOCAL_MODULE := hostapd
+LOCAL_MODULE_TAGS := optional
+ifdef CONFIG_DRIVER_CUSTOM
+LOCAL_STATIC_LIBRARIES := libCustomWifi
+endif
+ifneq ($(BOARD_HOSTAPD_PRIVATE_LIB),)
+LOCAL_STATIC_LIBRARIES += $(BOARD_HOSTAPD_PRIVATE_LIB)
+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)
+include $(BUILD_EXECUTABLE)
+
+endif # ifeq ($(WPA_BUILD_HOSTAPD),true)
diff --git a/hostap/hostapd/ChangeLog b/hostap/hostapd/ChangeLog
new file mode 100644
index 0000000..af54e1e
--- /dev/null
+++ b/hostap/hostapd/ChangeLog
@@ -0,0 +1,1071 @@
+ChangeLog for hostapd
+
+2015-09-27 - v2.5
+	* fixed WPS UPnP vulnerability with HTTP chunked transfer encoding
+	  [http://w1.fi/security/2015-2/] (CVE-2015-4141)
+	* fixed WMM Action frame parser
+	  [http://w1.fi/security/2015-3/] (CVE-2015-4142)
+	* fixed EAP-pwd server missing payload length validation
+	  [http://w1.fi/security/2015-4/]
+	  (CVE-2015-4143, CVE-2015-4144, CVE-2015-4145)
+	* fixed validation of WPS and P2P NFC NDEF record payload length
+	  [http://w1.fi/security/2015-5/]
+	* nl80211:
+	  - fixed vendor command handling to check OUI properly
+	* fixed hlr_auc_gw build with OpenSSL
+	* hlr_auc_gw: allow Milenage RES length to be reduced
+	* disable HT for a station that does not support WMM/QoS
+	* added support for hashed password (NtHash) in EAP-pwd server
+	* fixed and extended dynamic VLAN cases
+	* added EAP-EKE server support for deriving Session-Id
+	* set Acct-Session-Id to a random value to make it more likely to be
+	  unique even if the device does not have a proper clock
+	* added more 2.4 GHz channels for 20/40 MHz HT co-ex scan
+	* modified SAE routines to be more robust and PWE generation to be
+	  stronger against timing attacks
+	* added support for Brainpool Elliptic Curves with SAE
+	* increases maximum value accepted for cwmin/cwmax
+	* added support for CCMP-256 and GCMP-256 as group ciphers with FT
+	* added Fast Session Transfer (FST) module
+	* removed optional fields from RSNE when using FT with PMF
+	  (workaround for interoperability issues with iOS 8.4)
+	* added EAP server support for TLS session resumption
+	* fixed key derivation for Suite B 192-bit AKM (this breaks
+	  compatibility with the earlier version)
+	* added mechanism to track unconnected stations and do minimal band
+	  steering
+	* number of small fixes
+
+2015-03-15 - v2.4
+	* allow OpenSSL cipher configuration to be set for internal EAP server
+	  (openssl_ciphers parameter)
+	* fixed number of small issues based on hwsim test case failures and
+	  static analyzer reports
+	* fixed Accounting-Request to not include duplicated Acct-Session-Id
+	* add support for Acct-Multi-Session-Id in RADIUS Accounting messages
+	* add support for PMKSA caching with SAE
+	* add support for generating BSS Load element (bss_load_update_period)
+	* fixed channel switch from VHT to HT
+	* add INTERFACE-ENABLED and INTERFACE-DISABLED ctrl_iface events
+	* add support for learning STA IPv4/IPv6 addresses and configuring
+	  ProxyARP support
+	* dropped support for the madwifi driver interface
+	* add support for Suite B (128-bit and 192-bit level) key management and
+	  cipher suites
+	* fixed a regression with driver=wired
+	* extend EAPOL-Key msg 1/4 retry workaround for changing SNonce
+	* add BSS_TM_REQ ctrl_iface command to send BSS Transition Management
+	  Request frames and BSS-TM-RESP event to indicate response to such
+	  frame
+	* add support for EAP Re-Authentication Protocol (ERP)
+	* fixed AP IE in EAPOL-Key 3/4 when both WPA and FT was enabled
+	* fixed a regression in HT 20/40 coex Action frame parsing
+	* set stdout to be line-buffered
+	* add support for vendor specific VHT extension to enable 256 QAM rates
+	  (VHT-MCS 8 and 9) on 2.4 GHz band
+	* RADIUS DAS:
+	  - extend Disconnect-Request processing to allow matching of multiple
+	    sessions
+	  - support Acct-Multi-Session-Id as an identifier
+	  - allow PMKSA cache entry to be removed without association
+	* expire hostapd STA entry if kernel does not have a matching entry
+	* allow chanlist to be used to specify a subset of channels for ACS
+	* improve ACS behavior on 2.4 GHz band and allow channel bias to be
+	  configured with acs_chan_bias parameter
+	* do not reply to a Probe Request frame that includes DSS Parameter Set
+	  element in which the channel does not match the current operating
+	  channel
+	* add UPDATE_BEACON ctrl_iface command; this can be used to force Beacon
+	  frame contents to be updated and to start beaconing on an interface
+	  that used start_disabled=1
+	* fixed some RADIUS server failover cases
+
+2014-10-09 - v2.3
+	* fixed number of minor issues identified in static analyzer warnings
+	* fixed DFS and channel switch operation for multi-BSS cases
+	* started to use constant time comparison for various password and hash
+	  values to reduce possibility of any externally measurable timing
+	  differences
+	* extended explicit clearing of freed memory and expired keys to avoid
+	  keeping private data in memory longer than necessary
+	* added support for number of new RADIUS attributes from RFC 7268
+	  (Mobility-Domain-Id, WLAN-HESSID, WLAN-Pairwise-Cipher,
+	  WLAN-Group-Cipher, WLAN-AKM-Suite, WLAN-Group-Mgmt-Pairwise-Cipher)
+	* fixed GET_CONFIG wpa_pairwise_cipher value
+	* added code to clear bridge FDB entry on station disconnection
+	* fixed PMKSA cache timeout from Session-Timeout for WPA/WPA2 cases
+	* fixed OKC PMKSA cache entry fetch to avoid a possible infinite loop
+	  in case the first entry does not match
+	* fixed hostapd_cli action script execution to use more robust mechanism
+	  (CVE-2014-3686)
+
+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
+	* improved debug logging (human readable event names, interface name
+	  included in more entries)
+	* added number of small changes to make it easier for static analyzers
+	  to understand the implementation
+	* added a workaround for Windows 7 Michael MIC failure reporting and
+	  use of the Secure bit in EAPOL-Key msg 3/4
+	* fixed number of small bugs (see git logs for more details)
+	* changed OpenSSL to read full certificate chain from server_cert file
+	* nl80211: number of updates to use new cfg80211/nl80211 functionality
+	  - replace monitor interface with nl80211 commands
+	  - additional information for driver-based AP SME
+	* EAP-pwd:
+	  - fix KDF for group 21 and zero-padding
+	  - added support for fragmentation
+	  - increased maximum number of hunting-and-pecking iterations
+	* avoid excessive Probe Response retries for broadcast Probe Request
+	  frames (only with drivers using hostapd SME/MLME)
+	* added preliminary support for using TLS v1.2 (CONFIG_TLSV12=y)
+	* fixed WPS operation stopping on dual concurrent AP
+	* added wps_rf_bands configuration parameter for overriding RF Bands
+	  value for WPS
+	* added support for getting per-device PSK from RADIUS Tunnel-Password
+	* added support for libnl 3.2 and newer
+	* increased initial group key handshake retransmit timeout to 500 ms
+	* added a workaround for 4-way handshake to update SNonce even after
+	  having sent EAPOL-Key 3/4 to avoid issues with some supplicant
+	  implementations that can change SNonce for each EAP-Key 2/4
+	* added a workaround for EAPOL-Key 4/4 using incorrect type value in
+	  WPA2 mode (some deployed stations use WPA type in that message)
+	* added a WPS workaround for mixed mode AP Settings with Windows 7
+	* changed WPS AP PIN disabling mechanism to disable the PIN after 10
+	  consecutive failures in addition to using the exponential lockout
+	  period
+	* added support for WFA Hotspot 2.0
+	  - GAS/ANQP advertisement of network information
+	  - disable_dgaf parameter to disable downstream group-addressed
+	    forwarding
+	* simplified licensing terms by selecting the BSD license as the only
+	  alternative
+	* EAP-SIM: fixed re-authentication not to update pseudonym
+	* EAP-SIM: use Notification round before EAP-Failure
+	* EAP-AKA: added support for AT_COUNTER_TOO_SMALL
+	* EAP-AKA: skip AKA/Identity exchange if EAP identity is recognized
+	* EAP-AKA': fixed identity for MK derivation
+	* EAP-AKA': updated to RFC 5448 (username prefixes changed); note: this
+	  breaks interoperability with older versions
+	* EAP-SIM/AKA: allow pseudonym to be used after unknown reauth id
+	* changed ANonce to be a random number instead of Counter-based
+	* added support for canceling WPS operations with hostapd_cli wps_cancel
+	* fixed EAP/WPS to PSK transition on reassociation in cases where
+	  deauthentication is missed
+	* hlr_auc_gw enhancements:
+	  - a new command line parameter -u can be used to enable updating of
+	    SQN in Milenage file
+	  - use 5 bit IND for SQN updates
+	  - SQLite database can now be used to store Milenage information
+	* EAP-SIM/AKA DB: added optional use of SQLite database for pseudonyms
+	  and reauth data
+	* added support for Chargeable-User-Identity (RFC 4372)
+	* added radius_auth_req_attr and radius_acct_req_attr configuration
+	  parameters to allow adding/overriding of RADIUS attributes in
+	  Access-Request and Accounting-Request packets
+	* added support for RADIUS dynamic authorization server (RFC 5176)
+	* added initial support for WNM operations
+	  - BSS max idle period
+	  - WNM-Sleep Mode
+	* added new WPS NFC ctrl_iface mechanism
+	  - removed obsoleted WPS_OOB command (including support for deprecated
+	    UFD config_method)
+	* added FT support for drivers that implement MLME internally
+	* added SA Query support for drivers that implement MLME internally
+	* removed default ACM=1 from AC_VO and AC_VI
+	* changed VENDOR-TEST EAP method to use proper private enterprise number
+	  (this will not interoperate with older versions)
+	* added hostapd.conf parameter vendor_elements to allow arbitrary vendor
+	  specific elements to be added to the Beacon and Probe Response frames
+	* added support for configuring GCMP cipher for IEEE 802.11ad
+	* added support for 256-bit AES with internal TLS implementation
+	* changed EAPOL transmission to use AC_VO if WMM is active
+	* fixed EAP-TLS/PEAP/TTLS/FAST server to validate TLS Message Length
+	  correctly; invalid messages could have caused the hostapd process to
+	  terminate before this fix [CVE-2012-4445]
+	* limit number of active wildcard PINs for WPS Registrar to one to avoid
+	  confusing behavior with multiple wildcard PINs
+	* added a workaround for WPS PBC session overlap detection to avoid
+	  interop issues with deployed station implementations that do not
+	  remove active PBC indication from Probe Request frames properly
+	* added support for using SQLite for the eap_user database
+	* added Acct-Session-Id attribute into Access-Request messages
+	* fixed EAPOL frame transmission to non-QoS STAs with nl80211
+	  (do not send QoS frames if the STA did not negotiate use of QoS for
+	  this association)
+
+2012-05-10 - v1.0
+	* Add channel selection support in hostapd. See hostapd.conf.
+	* Add support for IEEE 802.11v Time Advertisement mechanism with UTC
+	  TSF offset. See hostapd.conf for config info.
+	* Delay STA entry removal until Deauth/Disassoc TX status in AP mode.
+	  This allows the driver to use PS buffering of Deauthentication and
+	  Disassociation frames when the STA is in power save sleep. Only
+	  available with drivers that provide TX status events for Deauth/
+	  Disassoc frames (nl80211).
+	* Allow PMKSA caching to be disabled on the Authenticator. See
+	  hostap.conf config parameter disable_pmksa_caching.
+	* atheros: Add support for IEEE 802.11w configuration.
+	* bsd: Add support for setting HT values in IFM_MMASK.
+	* Allow client isolation to be configured with ap_isolate. Client
+	  isolation can be used to prevent low-level bridging of frames
+	  between associated stations in the BSS. By default, this bridging
+	  is allowed.
+	* Allow coexistance of HT BSSes with WEP/TKIP BSSes.
+	* Add require_ht config parameter, which can be used to configure
+	  hostapd to reject association with any station that does not support
+	  HT PHY.
+	* Add support for writing debug log to a file using "-f" option. Also
+	  add relog CLI command to re-open the log file.
+	* Add bridge handling for WDS STA interfaces. By default they are
+	  added to the configured bridge of the AP interface (if present),
+	  but the user can also specify a separate bridge using cli command
+	  wds_bridge.
+	* hostapd_cli:
+	  - Add wds_bridge command for specifying bridge for WDS STA
+	    interfaces.
+	  - Add relog command for reopening log file.
+	  - Send AP-STA-DISCONNECTED event when an AP disconnects a station
+	    due to inactivity.
+	  - Add wps_config ctrl_interface command for configuring AP. This
+	    command can be used to configure the AP using the internal WPS
+	    registrar. It works in the same way as new AP settings received
+	    from an ER.
+	  - Many WPS/WPS ER commands - see WPS/WPS ER sections for details.
+	  - Add command get version, that returns hostapd version string.
+	* WNM: Add BSS Transition Management Request for ESS Disassoc Imminent.
+	  Use hostapd_cli ess_disassoc (STA addr) (URL) to send the
+	  notification to the STA.
+	* Allow AP mode to disconnect STAs based on low ACK condition (when
+	  the data connection is not working properly, e.g., due to the STA
+	  going outside the range of the AP). Disabled by default, enable by
+	  config option disassoc_low_ack.
+	* Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad
+	  config file.
+	* WPS:
+	  - Send AP Settings as a wrapped Credential attribute to ctrl_iface
+	    in WPS-NEW-AP-SETTINGS.
+	  - Dispatch more WPS events through hostapd ctrl_iface.
+	  - Add mechanism for indicating non-standard WPS errors.
+	  - Change concurrent radio AP to use only one WPS UPnP instance.
+	  - Add wps_check_pin command for processing PIN from user input.
+	    UIs can use this command to process a PIN entered by a user and to
+	    validate the checksum digit (if present).
+	  - Add hostap_cli get_config command to display current AP config.
+	  - Add new hostapd_cli command, wps_ap_pin, to manage AP PIN at
+	    runtime and support dynamic AP PIN management.
+	  - Disable AP PIN after 10 consecutive failures. Slow down attacks
+	    on failures up to 10.
+	  - Allow AP to start in Enrollee mode without AP PIN for probing,
+	    to be compatible with Windows 7.
+	  - Add Config Error into WPS-FAIL events to provide more info
+	    to the user on how to resolve the issue.
+	  - When controlling multiple interfaces:
+	     - apply WPS commands to all interfaces configured to use WPS
+	     - apply WPS config changes to all interfaces that use WPS
+	     - when an attack is detected on any interface, disable AP PIN on
+	       all interfaces
+	* WPS ER:
+	  - Show SetSelectedRegistrar events as ctrl_iface events.
+	  - Add special AP Setup Locked mode to allow read only ER.
+	    ap_setup_locked=2 can now be used to enable a special mode where
+	    WPS ER can learn the current AP settings, but cannot change them.
+	* WPS 2.0: Add support for WPS 2.0 (CONFIG_WPS2)
+	  - Add build option CONFIG_WPS_EXTENSIBILITY_TESTING to enable tool
+	    for testing protocol extensibility.
+	  - Add build option CONFIG_WPS_STRICT to allow disabling of WPS
+	    workarounds.
+	  - Add support for AuthorizedMACs attribute.
+	* TDLS:
+	  - Allow TDLS use or TDLS channel switching in the BSS to be
+	    prohibited in the BSS, using config params tdls_prohibit and
+	    tdls_prohibit_chan_switch.
+	* EAP server: Add support for configuring fragment size (see
+	  fragment_size in hostapd.conf).
+	* wlantest: Add a tool wlantest for IEEE802.11 protocol testing.
+	  wlantest can be used to capture frames from a monitor interface
+	  for realtime capturing or from pcap files for offline analysis.
+	* Interworking: Support added for 802.11u. Enable in .config with
+	  CONFIG_INTERWORKING. See hostapd.conf for config parameters for
+	  interworking.
+	* Android: Add build and runtime support for Android hostapd.
+	* Add a new debug message level for excessive information. Use
+	  -ddd to enable.
+	* TLS: Add support for tls_disable_time_checks=1 in client mode.
+	* Internal TLS:
+	  - Add support for TLS v1.1 (RFC 4346). Enable with build parameter
+	    CONFIG_TLSV11.
+	  - Add domainComponent parser for X.509 names
+	* Reorder some IEs to get closer to IEEE 802.11 standard. Move
+	  WMM into end of Beacon, Probe Resp and (Re)Assoc Resp frames.
+	  Move HT IEs to be later in (Re)Assoc Resp.
+	* Many bugfixes.
+
+2010-04-18 - v0.7.2
+	* fix WPS internal Registrar use when an external Registrar is also
+	  active
+	* bsd: Cleaned up driver wrapper and added various low-level
+	  configuration options
+	* TNC: fixed issues with fragmentation
+	* EAP-TNC: add Flags field into fragment acknowledgement (needed to
+	  interoperate with other implementations; may potentially breaks
+	  compatibility with older wpa_supplicant/hostapd versions)
+	* cleaned up driver wrapper API for multi-BSS operations
+	* nl80211: fix multi-BSS and VLAN operations
+	* fix number of issues with IEEE 802.11r/FT; this version is not
+	  backwards compatible with old versions
+	* add SA Query Request processing in AP mode (IEEE 802.11w)
+	* fix IGTK PN in group rekeying (IEEE 802.11w)
+	* fix WPS PBC session overlap detection to use correct attribute
+	* hostapd_notif_Assoc() can now be called with all IEs to simplify
+	  driver wrappers
+	* work around interoperability issue with some WPS External Registrar
+	  implementations
+	* nl80211: fix WPS IE update
+	* hostapd_cli: add support for action script operations (run a script
+	  on hostapd events)
+	* fix DH padding with internal crypto code (mainly, for WPS)
+	* fix WPS association with both WPS IE and WPA/RSN IE present with
+	  driver wrappers that use hostapd MLME (e.g., nl80211)
+
+2010-01-16 - v0.7.1
+	* cleaned up driver wrapper API (struct wpa_driver_ops); the new API
+	  is not fully backwards compatible, so out-of-tree driver wrappers
+	  will need modifications
+	* cleaned up various module interfaces
+	* merge hostapd and wpa_supplicant developers' documentation into a
+	  single document
+	* fixed HT Capabilities IE with nl80211 drivers
+	* moved generic AP functionality code into src/ap
+	* WPS: handle Selected Registrar as union of info from all Registrars
+	* remove obsolte Prism54.org driver wrapper
+	* added internal debugging mechanism with backtrace support and memory
+	  allocation/freeing validation, etc. tests (CONFIG_WPA_TRACE=y)
+	* EAP-FAST server: piggyback Phase 2 start with the end of Phase 1
+	* WPS: add support for dynamically selecting whether to provision the
+	  PSK as an ASCII passphrase or PSK
+	* added support for WDS (4-address frame) mode with per-station virtual
+	  interfaces (wds_sta=1 in config file; only supported with
+	  driver=nl80211 for now)
+	* fixed WPS Probe Request processing to handle missing required
+	  attribute
+	* fixed PKCS#12 use with OpenSSL 1.0.0
+	* detect bridge interface automatically so that bridge parameter in
+	  hostapd.conf becomes optional (though, it may now be used to
+	  automatically add then WLAN interface into a bridge with
+	  driver=nl80211)
+
+2009-11-21 - v0.7.0
+	* increased hostapd_cli ping interval to 5 seconds and made this
+	  configurable with a new command line options (-G<seconds>)
+	* driver_nl80211: use Linux socket filter to improve performance
+	* added support for external Registrars with WPS (UPnP transport)
+	* 802.11n: scan for overlapping BSSes before starting 20/40 MHz channel
+	* driver_nl80211: fixed STA accounting data collection (TX/RX bytes
+	  reported correctly; TX/RX packets not yet available from kernel)
+	* added support for WPS USBA out-of-band mechanism with USB Flash
+	  Drives (UFD) (CONFIG_WPS_UFD=y)
+	* fixed EAPOL/EAP reauthentication when using an external RADIUS
+	  authentication server
+	* fixed TNC with EAP-TTLS
+	* fixed IEEE 802.11r key derivation function to match with the standard
+	  (note: this breaks interoperability with previous version) [Bug 303]
+	* fixed SHA-256 based key derivation function to match with the
+	  standard when using CCMP (for IEEE 802.11r and IEEE 802.11w)
+	  (note: this breaks interoperability with previous version) [Bug 307]
+	* added number of code size optimizations to remove unnecessary
+	  functionality from the program binary based on build configuration
+	  (part of this automatic; part configurable with CONFIG_NO_* build
+	  options)
+	* use shared driver wrapper files with wpa_supplicant
+	* driver_nl80211: multiple updates to provide support for new Linux
+	  nl80211/mac80211 functionality
+	* updated management frame protection to use IEEE Std 802.11w-2009
+	* fixed number of small WPS issues and added workarounds to
+	  interoperate with common deployed broken implementations
+	* added some IEEE 802.11n co-existence rules to disable 40 MHz channels
+	  or modify primary/secondary channels if needed based on neighboring
+	  networks
+	* added support for NFC out-of-band mechanism with WPS
+	* added preliminary support for IEEE 802.11r RIC processing
+
+2009-01-06 - v0.6.7
+	* added support for Wi-Fi Protected Setup (WPS)
+	  (hostapd can now be configured to act as an integrated WPS Registrar
+	  and provision credentials for WPS Enrollees using PIN and PBC
+	  methods; external wireless Registrar can configure the AP, but
+	  external WLAN Manager Registrars are not supported); WPS support can
+	  be enabled by adding CONFIG_WPS=y into .config and setting the
+	  runtime configuration variables in hostapd.conf (see WPS section in
+	  the example configuration file); new hostapd_cli commands wps_pin and
+	  wps_pbc are used to configure WPS negotiation; see README-WPS for
+	  more details
+	* added IEEE 802.11n HT capability configuration (ht_capab)
+	* added support for generating Country IE based on nl80211 regulatory
+	  information (added if ieee80211d=1 in configuration)
+	* fixed WEP authentication (both Open System and Shared Key) with
+	  mac80211
+	* added support for EAP-AKA' (draft-arkko-eap-aka-kdf)
+	* added support for using driver_test over UDP socket
+	* changed EAP-GPSK to use the IANA assigned EAP method type 51
+	* updated management frame protection to use IEEE 802.11w/D7.0
+	* fixed retransmission of EAP requests if no response is received
+
+2008-11-23 - v0.6.6
+	* added a new configuration option, wpa_ptk_rekey, that can be used to
+	  enforce frequent PTK rekeying, e.g., to mitigate some attacks against
+	  TKIP deficiencies
+	* updated OpenSSL code for EAP-FAST to use an updated version of the
+	  session ticket overriding API that was included into the upstream
+	  OpenSSL 0.9.9 tree on 2008-11-15 (no additional OpenSSL patch is
+	  needed with that version anymore)
+	* changed channel flags configuration to read the information from
+	  the driver (e.g., via driver_nl80211 when using mac80211) instead of
+	  using hostapd as the source of the regulatory information (i.e.,
+	  information from CRDA is now used with mac80211); this allows 5 GHz
+	  channels to be used with hostapd (if allowed in the current
+	  regulatory domain)
+	* fixed EAP-TLS message processing for the last TLS message if it is
+	  large enough to require fragmentation (e.g., if a large Session
+	  Ticket data is included)
+	* fixed listen interval configuration for nl80211 drivers
+
+2008-11-01 - v0.6.5
+	* added support for SHA-256 as X.509 certificate digest when using the
+	  internal X.509/TLSv1 implementation
+	* fixed EAP-FAST PAC-Opaque padding (0.6.4 broke this for some peer
+	  identity lengths)
+	* fixed internal TLSv1 implementation for abbreviated handshake (used
+	  by EAP-FAST server)
+	* added support for setting VLAN ID for STAs based on local MAC ACL
+	  (accept_mac_file) as an alternative for RADIUS server-based
+	  configuration
+	* updated management frame protection to use IEEE 802.11w/D6.0
+	  (adds a new association ping to protect against unauthenticated
+	  authenticate or (re)associate request frames dropping association)
+	* added support for using SHA256-based stronger key derivation for WPA2
+	  (IEEE 802.11w)
+	* added new "driver wrapper" for RADIUS-only configuration
+	  (driver=none in hostapd.conf; CONFIG_DRIVER_NONE=y in .config)
+	* fixed WPA/RSN IE validation to verify that the proto (WPA vs. WPA2)
+	  is enabled in configuration
+	* changed EAP-FAST configuration to use separate fields for A-ID and
+	  A-ID-Info (eap_fast_a_id_info) to allow A-ID to be set to a fixed
+	  16-octet len binary value for better interoperability with some peer
+	  implementations; eap_fast_a_id is now configured as a hex string
+	* driver_nl80211: Updated to match the current Linux mac80211 AP mode
+	  configuration (wireless-testing.git and Linux kernel releases
+	  starting from 2.6.29)
+
+2008-08-10 - v0.6.4
+	* added peer identity into EAP-FAST PAC-Opaque and skip Phase 2
+	  Identity Request if identity is already known
+	* added support for EAP Sequences in EAP-FAST Phase 2
+	* added support for EAP-TNC (Trusted Network Connect)
+	  (this version implements the EAP-TNC method and EAP-TTLS/EAP-FAST
+	  changes needed to run two methods in sequence (IF-T) and the IF-IMV
+	  and IF-TNCCS interfaces from TNCS)
+	* added support for optional cryptobinding with PEAPv0
+	* added fragmentation support for EAP-TNC
+	* added support for fragmenting EAP-TTLS/PEAP/FAST Phase 2 (tunneled)
+	  data
+	* added support for opportunistic key caching (OKC)
+
+2008-02-22 - v0.6.3
+	* fixed Reassociation Response callback processing when using internal
+	  MLME (driver_{hostap,nl80211,test}.c)
+	* updated FT support to use the latest draft, IEEE 802.11r/D9.0
+	* copy optional Proxy-State attributes into RADIUS response when acting
+	  as a RADIUS authentication server
+	* fixed EAPOL state machine to handle a case in which no response is
+	  received from the RADIUS authentication server; previous version
+	  could have triggered a crash in some cases after a timeout
+	* fixed EAP-SIM/AKA realm processing to allow decorated usernames to
+	  be used
+	* added a workaround for EAP-SIM/AKA peers that include incorrect null
+	  termination in the username
+	* fixed EAP-SIM/AKA protected result indication to include AT_COUNTER
+	  attribute in notification messages only when using fast
+	  reauthentication
+	* fixed EAP-SIM Start response processing for fast reauthentication
+	  case
+	* added support for pending EAP processing in EAP-{PEAP,TTLS,FAST}
+	  phase 2 to allow EAP-SIM and EAP-AKA to be used as the Phase 2 method
+
+2008-01-01 - v0.6.2
+	* fixed EAP-SIM and EAP-AKA message parser to validate attribute
+	  lengths properly to avoid potential crash caused by invalid messages
+	* added data structure for storing allocated buffers (struct wpabuf);
+	  this does not affect hostapd usage, but many of the APIs changed
+	  and various interfaces (e.g., EAP) is not compatible with old
+	  versions
+	* added support for protecting EAP-AKA/Identity messages with
+	  AT_CHECKCODE (optional feature in RFC 4187)
+	* added support for protected result indication with AT_RESULT_IND for
+	  EAP-SIM and EAP-AKA (eap_sim_aka_result_ind=1)
+	* added support for configuring EAP-TTLS phase 2 non-EAP methods in
+	  EAP server configuration; previously all four were enabled for every
+	  phase 2 user, now all four are disabled by default and need to be
+	  enabled with new method names TTLS-PAP, TTLS-CHAP, TTLS-MSCHAP,
+	  TTLS-MSCHAPV2
+	* removed old debug printing mechanism and the related 'debug'
+	  parameter in the configuration file; debug verbosity is now set with
+	  -d (or -dd) command line arguments
+	* added support for EAP-IKEv2 (draft-tschofenig-eap-ikev2-15.txt);
+	  only shared key/password authentication is supported in this version
+
+2007-11-24 - v0.6.1
+	* added experimental, integrated TLSv1 server implementation with the
+	  needed X.509/ASN.1/RSA/bignum processing (this can be enabled by
+	  setting CONFIG_TLS=internal and CONFIG_INTERNAL_LIBTOMMATH=y in
+	  .config); this can be useful, e.g., if the target system does not
+	  have a suitable TLS library and a minimal code size is required
+	* added support for EAP-FAST server method to the integrated EAP
+	  server
+	* updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
+	  draft (draft-ietf-emu-eap-gpsk-07.txt)
+	* added a new configuration parameter, rsn_pairwise, to allow different
+	  pairwise cipher suites to be enabled for WPA and RSN/WPA2
+	  (note: if wpa_pairwise differs from rsn_pairwise, the driver will
+	  either need to support this or will have to use the WPA/RSN IEs from
+	  hostapd; currently, the included madwifi and bsd driver interfaces do
+	  not have support for this)
+	* updated FT support to use the latest draft, IEEE 802.11r/D8.0
+
+2007-05-28 - v0.6.0
+	* added experimental IEEE 802.11r/D6.0 support
+	* updated EAP-SAKE to RFC 4763 and the IANA-allocated EAP type 48
+	* updated EAP-PSK to use the IANA-allocated EAP type 47
+	* fixed EAP-PSK bit ordering of the Flags field
+	* fixed configuration reloading (SIGHUP) to re-initialize WPA PSKs
+	  by reading wpa_psk_file [Bug 181]
+	* fixed EAP-TTLS AVP parser processing for too short AVP lengths
+	* fixed IPv6 connection to RADIUS accounting server
+	* updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
+	  draft (draft-ietf-emu-eap-gpsk-04.txt)
+	* hlr_auc_gw: read GSM triplet file into memory and rotate through the
+	  entries instead of only using the same three triplets every time
+	  (this does not work properly with tests using multiple clients, but
+	  provides bit better triplet data for testing a single client; anyway,
+	  if a better quality triplets are needed, GSM-Milenage should be used
+	  instead of hardcoded triplet file)
+	* fixed EAP-MSCHAPv2 server to use a space between S and M parameters
+	  in Success Request [Bug 203]
+	* added support for sending EAP-AKA Notifications in error cases
+	* updated to use IEEE 802.11w/D2.0 for management frame protection
+	  (still experimental)
+	* RADIUS server: added support for processing duplicate messages
+	  (retransmissions from RADIUS client) by replying with the previous
+	  reply
+
+2006-11-24 - v0.5.6
+	* added support for configuring and controlling multiple BSSes per
+	  radio interface (bss=<ifname> in hostapd.conf); this is only
+	  available with Devicescape and test driver interfaces
+	* fixed PMKSA cache update in the end of successful RSN
+	  pre-authentication
+	* added support for dynamic VLAN configuration (i.e., selecting VLAN-ID
+	  for each STA based on RADIUS Access-Accept attributes); this requires
+	  VLAN support from the kernel driver/802.11 stack and this is
+	  currently only available with Devicescape and test driver interfaces
+	* driver_madwifi: fixed configuration of unencrypted modes (plaintext
+	  and IEEE 802.1X without WEP)
+	* removed STAKey handshake since PeerKey handshake has replaced it in
+	  IEEE 802.11ma and there are no known deployments of STAKey
+	* updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
+	  draft (draft-ietf-emu-eap-gpsk-01.txt)
+	* added preliminary implementation of IEEE 802.11w/D1.0 (management
+	  frame protection)
+	  (Note: this requires driver support to work properly.)
+	  (Note2: IEEE 802.11w is an unapproved draft and subject to change.)
+	* hlr_auc_gw: added support for GSM-Milenage (for EAP-SIM)
+	* hlr_auc_gw: added support for reading per-IMSI Milenage keys and
+	  parameters from a text file to make it possible to implement proper
+	  GSM/UMTS authentication server for multiple SIM/USIM cards using
+	  EAP-SIM/EAP-AKA
+	* fixed session timeout processing with drivers that do not use
+	  ieee802_11.c (e.g., madwifi)
+
+2006-08-27 - v0.5.5
+	* added 'hostapd_cli new_sta <addr>' command for adding a new STA into
+	  hostapd (e.g., to initialize wired network authentication based on an
+	  external signal)
+	* fixed hostapd to add PMKID KDE into 4-Way Handshake Message 1 when
+	  using WPA2 even if PMKSA caching is not used
+	* added -P<pid file> argument for hostapd to write the current process
+	  id into a file
+	* added support for RADIUS Authentication Server MIB (RFC 2619)
+
+2006-06-20 - v0.5.4
+	* fixed nt_password_hash build [Bug 144]
+	* added PeerKey handshake implementation for IEEE 802.11e
+	  direct link setup (DLS) to replace STAKey handshake
+	* added support for EAP Generalized Pre-Shared Key (EAP-GPSK,
+	  draft-clancy-emu-eap-shared-secret-00.txt)
+	* fixed a segmentation fault when RSN pre-authentication was completed
+	  successfully [Bug 152]
+
+2006-04-27 - v0.5.3
+	* do not build nt_password_hash and hlr_auc_gw by default to avoid
+	  requiring a TLS library for a successful build; these programs can be
+	  build with 'make nt_password_hash' and 'make hlr_auc_gw'
+	* added a new configuration option, eapol_version, that can be used to
+	  set EAPOL version to 1 (default is 2) to work around broken client
+	  implementations that drop EAPOL frames which use version number 2
+	  [Bug 89]
+	* added support for EAP-SAKE (no EAP method number allocated yet, so
+	  this is using the same experimental type 255 as EAP-PSK)
+	* fixed EAP-MSCHAPv2 message length validation
+
+2006-03-19 - v0.5.2
+	* fixed stdarg use in hostapd_logger(): if both stdout and syslog
+	  logging was enabled, hostapd could trigger a segmentation fault in
+	  vsyslog on some CPU -- C library combinations
+	* moved HLR/AuC gateway implementation for EAP-SIM/AKA into an external
+	  program to make it easier to use for implementing real SS7 gateway;
+	  eap_sim_db is not anymore used as a file name for GSM authentication
+	  triplets; instead, it is path to UNIX domain socket that will be used
+	  to communicate with the external gateway program (e.g., hlr_auc_gw)
+	* added example HLR/AuC gateway implementation, hlr_auc_gw, that uses
+	  local information (GSM authentication triplets from a text file and
+	  hardcoded AKA authentication data); this can be used to test EAP-SIM
+	  and EAP-AKA
+	* added Milenage algorithm (example 3GPP AKA algorithm) to hlr_auc_gw
+	  to make it possible to test EAP-AKA with real USIM cards (this is
+	  disabled by default; define AKA_USE_MILENAGE when building hlr_auc_gw
+	  to enable this)
+	* driver_madwifi: added support for getting station RSN IE from
+	  madwifi-ng svn r1453 and newer; this fixes RSN that was apparently
+	  broken with earlier change (r1357) in the driver
+	* changed EAP method registration to use a dynamic list of methods
+	  instead of a static list generated at build time
+	* fixed WPA message 3/4 not to encrypt Key Data field (WPA IE)
+	  [Bug 125]
+	* added ap_max_inactivity configuration parameter
+
+2006-01-29 - v0.5.1
+	* driver_test: added better support for multiple APs and STAs by using
+	  a directory with sockets that include MAC address for each device in
+	  the name (test_socket=DIR:/tmp/test)
+	* added support for EAP expanded type (vendor specific EAP methods)
+
+2005-12-18 - v0.5.0 (beginning of 0.5.x development releases)
+	* added experimental STAKey handshake implementation for IEEE 802.11e
+	  direct link setup (DLS); note: this is disabled by default in both
+	  build and runtime configuration (can be enabled with CONFIG_STAKEY=y
+	  and stakey=1)
+	* added support for EAP methods to use callbacks to external programs
+	  by buffering a pending request and processing it after the EAP method
+	  is ready to continue
+	* improved EAP-SIM database interface to allow external request to GSM
+	  HLR/AuC without blocking hostapd process
+	* added support for using EAP-SIM pseudonyms and fast re-authentication
+	* added support for EAP-AKA in the integrated EAP authenticator
+	* added support for matching EAP identity prefixes (e.g., "1"*) in EAP
+	  user database to allow EAP-SIM/AKA selection without extra roundtrip
+	  for EAP-Nak negotiation
+	* added support for storing EAP user password as NtPasswordHash instead
+	  of plaintext password when using MSCHAP or MSCHAPv2 for
+	  authentication (hash:<16-octet hex value>); added nt_password_hash
+	  tool for hashing password to generate NtPasswordHash
+
+2005-11-20 - v0.4.7 (beginning of 0.4.x stable releases)
+	* driver_wired: fixed EAPOL sending to optionally use PAE group address
+	  as the destination instead of supplicant MAC address; this is
+	  disabled by default, but should be enabled with use_pae_group_addr=1
+	  in configuration file if the wired interface is used by only one
+	  device at the time (common switch configuration)
+	* driver_madwifi: configure driver to use TKIP countermeasures in order
+	  to get correct behavior (IEEE 802.11 association failing; previously,
+	  association succeeded, but hostpad forced disassociation immediately)
+	* driver_madwifi: added support for madwifi-ng
+
+2005-10-27 - v0.4.6
+	* added support for replacing user identity from EAP with RADIUS
+	  User-Name attribute from Access-Accept message, if that is included,
+	  for the RADIUS accounting messages (e.g., for EAP-PEAP/TTLS to get
+	  tunneled identity into accounting messages when the RADIUS server
+	  does not support better way of doing this with Class attribute)
+	* driver_madwifi: fixed EAPOL packet receive for configuration where
+	  ath# is part of a bridge interface
+	* added a configuration file and log analyzer script for logwatch
+	* fixed EAPOL state machine step function to process all state
+	  transitions before processing new events; this resolves a race
+	  condition in which EAPOL-Start message could trigger hostapd to send
+	  two EAP-Response/Identity frames to the authentication server
+
+2005-09-25 - v0.4.5
+	* added client CA list to the TLS certificate request in order to make
+	  it easier for the client to select which certificate to use
+	* added experimental support for EAP-PSK
+	* added support for WE-19 (hostap, madwifi)
+
+2005-08-21 - v0.4.4
+	* fixed build without CONFIG_RSN_PREAUTH
+	* fixed FreeBSD build
+
+2005-06-26 - v0.4.3
+	* fixed PMKSA caching to copy User-Name and Class attributes so that
+	  RADIUS accounting gets correct information
+	* start RADIUS accounting only after successful completion of WPA
+	  4-Way Handshake if WPA-PSK is used
+	* fixed PMKSA caching for the case where STA (re)associates without
+	  first disassociating
+
+2005-06-12 - v0.4.2
+	* EAP-PAX is now registered as EAP type 46
+	* fixed EAP-PAX MAC calculation
+	* fixed EAP-PAX CK and ICK key derivation
+	* renamed eap_authenticator configuration variable to eap_server to
+	  better match with RFC 3748 (EAP) terminology
+	* driver_test: added support for testing hostapd with wpa_supplicant
+	  by using test driver interface without any kernel drivers or network
+	  cards
+
+2005-05-22 - v0.4.1
+	* fixed RADIUS server initialization when only auth or acct server
+	  is configured and the other one is left empty
+	* driver_madwifi: added support for RADIUS accounting
+	* driver_madwifi: added preliminary support for compiling against 'BSD'
+	  branch of madwifi CVS tree
+	* driver_madwifi: fixed pairwise key removal to allow WPA reauth
+	  without disassociation
+	* added support for reading additional certificates from PKCS#12 files
+	  and adding them to the certificate chain
+	* fixed RADIUS Class attribute processing to only use Access-Accept
+	  packets to update Class; previously, other RADIUS authentication
+	  packets could have cleared Class attribute
+	* added support for more than one Class attribute in RADIUS packets
+	* added support for verifying certificate revocation list (CRL) when
+	  using integrated EAP authenticator for EAP-TLS; new hostapd.conf
+	  options 'check_crl'; CRL must be included in the ca_cert file for now
+
+2005-04-25 - v0.4.0 (beginning of 0.4.x development releases)
+	* added support for including network information into
+	  EAP-Request/Identity message (ASCII-0 (nul) in eap_message)
+	  (e.g., to implement draft-adrange-eap-network-discovery-07.txt)
+	* fixed a bug which caused some RSN pre-authentication cases to use
+	  freed memory and potentially crash hostapd
+	* fixed private key loading for cases where passphrase is not set
+	* added support for sending TLS alerts and aborting authentication
+	  when receiving a TLS alert
+	* fixed WPA2 to add PMKSA cache entry when using integrated EAP
+	  authenticator
+	* fixed PMKSA caching (EAP authentication was not skipped correctly
+	  with the new state machine changes from IEEE 802.1X draft)
+	* added support for RADIUS over IPv6; own_ip_addr, auth_server_addr,
+	  and acct_server_addr can now be IPv6 addresses (CONFIG_IPV6=y needs
+	  to be added to .config to include IPv6 support); for RADIUS server,
+	  radius_server_ipv6=1 needs to be set in hostapd.conf and addresses
+	  in RADIUS clients file can then use IPv6 format
+	* added experimental support for EAP-PAX
+	* replaced hostapd control interface library (hostapd_ctrl.[ch]) with
+	  the same implementation that wpa_supplicant is using (wpa_ctrl.[ch])
+
+2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases)
+
+2005-01-23 - v0.3.5
+	* added support for configuring a forced PEAP version based on the
+	  Phase 1 identity
+	* fixed PEAPv1 to use tunneled EAP-Success/Failure instead of EAP-TLV
+	  to terminate authentication
+	* fixed EAP identifier duplicate processing with the new IEEE 802.1X
+	  draft
+	* clear accounting data in the driver when starting a new accounting
+	  session
+	* driver_madwifi: filter wireless events based on ifindex to allow more
+	  than one network interface to be used
+	* fixed WPA message 2/4 processing not to cancel timeout for TimeoutEvt
+	  setting if the packet does not pass MIC verification (e.g., due to
+	  incorrect PSK); previously, message 1/4 was not tried again if an
+	  invalid message 2/4 was received
+	* fixed reconfiguration of RADIUS client retransmission timer when
+	  adding a new message to the pending list; previously, timer was not
+	  updated at this point and if there was a pending message with long
+	  time for the next retry, the new message needed to wait that long for
+	  its first retry, too
+
+2005-01-09 - v0.3.4
+	* added support for configuring multiple allowed EAP types for Phase 2
+	  authentication (EAP-PEAP, EAP-TTLS)
+	* fixed EAPOL-Start processing to trigger WPA reauthentication
+	  (previously, only EAPOL authentication was done)
+
+2005-01-02 - v0.3.3
+	* added support for EAP-PEAP in the integrated EAP authenticator
+	* added support for EAP-GTC in the integrated EAP authenticator
+	* added support for configuring list of EAP methods for Phase 1 so that
+	  the integrated EAP authenticator can, e.g., use the wildcard entry
+	  for EAP-TLS and EAP-PEAP
+	* added support for EAP-TTLS in the integrated EAP authenticator
+	* added support for EAP-SIM in the integrated EAP authenticator
+	* added support for using hostapd as a RADIUS authentication server
+	  with the integrated EAP authenticator taking care of EAP
+	  authentication (new hostapd.conf options: radius_server_clients and
+	  radius_server_auth_port); this is not included in default build; use
+	  CONFIG_RADIUS_SERVER=y in .config to include
+
+2004-12-19 - v0.3.2
+	* removed 'daemonize' configuration file option since it has not really
+	  been used at all for more than year
+	* driver_madwifi: fixed group key setup and added get_ssid method
+	* added support for EAP-MSCHAPv2 in the integrated EAP authenticator
+
+2004-12-12 - v0.3.1
+	* added support for integrated EAP-TLS authentication (new hostapd.conf
+	  variables: ca_cert, server_cert, private_key, private_key_passwd);
+	  this enabled dynamic keying (WPA2/WPA/IEEE 802.1X/WEP) without
+	  external RADIUS server
+	* added support for reading PKCS#12 (PFX) files (as a replacement for
+	  PEM/DER) to get certificate and private key (CONFIG_PKCS12)
+
+2004-12-05 - v0.3.0 (beginning of 0.3.x development releases)
+	* added support for Acct-{Input,Output}-Gigawords
+	* added support for Event-Timestamp (in RADIUS Accounting-Requests)
+	* added support for RADIUS Authentication Client MIB (RFC2618)
+	* added support for RADIUS Accounting Client MIB (RFC2620)
+	* made EAP re-authentication period configurable (eap_reauth_period)
+	* fixed EAPOL reauthentication to trigger WPA/WPA2 reauthentication
+	* fixed EAPOL state machine to stop if STA is removed during
+	  eapol_sm_step(); this fixes at least one segfault triggering bug with
+	  IEEE 802.11i pre-authentication
+	* added support for multiple WPA pre-shared keys (e.g., one for each
+	  client MAC address or keys shared by a group of clients);
+	  new hostapd.conf field wpa_psk_file for setting path to a text file
+	  containing PSKs, see hostapd.wpa_psk for an example
+	* added support for multiple driver interfaces to allow hostapd to be
+	  used with other drivers
+	* added wired authenticator driver interface (driver=wired in
+	  hostapd.conf, see wired.conf for example configuration)
+	* added madwifi driver interface (driver=madwifi in hostapd.conf, see
+	  madwifi.conf for example configuration; Note: include files from
+	  madwifi project is needed for building and a configuration file,
+	  .config, needs to be created in hostapd directory with
+	  CONFIG_DRIVER_MADWIFI=y to include this driver interface in hostapd
+	  build)
+	* fixed an alignment issue that could cause SHA-1 to fail on some
+	  platforms (e.g., Intel ixp425 with a compiler that does not 32-bit
+	  align variables)
+	* fixed RADIUS reconnection after an error in sending interim
+	  accounting packets
+	* added hostapd control interface for external programs and an example
+	  CLI, hostapd_cli (like wpa_cli for wpa_supplicant)
+	* started adding dot11, dot1x, radius MIBs ('hostapd_cli mib',
+	  'hostapd_cli sta <addr>')
+	* finished update from IEEE 802.1X-2001 to IEEE 802.1X-REV (now d11)
+	* added support for strict GTK rekeying (wpa_strict_rekey in
+	  hostapd.conf)
+	* updated IAPP to use UDP port 3517 and multicast address 224.0.1.178
+	  (instead of broadcast) for IAPP ADD-notify (moved from draft 3 to
+	  IEEE 802.11F-2003)
+	* added Prism54 driver interface (driver=prism54 in hostapd.conf;
+	  note: .config needs to be created in hostapd directory with
+	  CONFIG_DRIVER_PRISM54=y to include this driver interface in hostapd
+	  build)
+	* dual-licensed hostapd (GPLv2 and BSD licenses)
+	* fixed RADIUS accounting to generate a new session id for cases where
+	  a station reassociates without first being complete deauthenticated
+	* fixed STA disassociation handler to mark next timeout state to
+	  deauthenticate the station, i.e., skip long wait for inactivity poll
+	  and extra disassociation, if the STA disassociates without
+	  deauthenticating
+	* added integrated EAP authenticator that can be used instead of
+	  external RADIUS authentication server; currently, only EAP-MD5 is
+	  supported, so this cannot yet be used for key distribution; the EAP
+	  method interface is generic, though, so adding new EAP methods should
+	  be straightforward; new hostapd.conf variables: 'eap_authenticator'
+	  and 'eap_user_file'; this obsoletes "minimal authentication server"
+	  ('minimal_eap' in hostapd.conf) which is now removed
+	* added support for FreeBSD and driver interface for the BSD net80211
+	  layer (driver=bsd in hostapd.conf and CONFIG_DRIVER_BSD=y in
+	  .config); please note that some of the required kernel mods have not
+	  yet been committed
+
+2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases)
+	* fixed some accounting cases where Accounting-Start was sent when
+	  IEEE 802.1X port was being deauthorized
+
+2004-06-20 - v0.2.3
+	* modified RADIUS client to re-connect the socket in case of certain
+	  error codes that are generated when a network interface state is
+	  changes (e.g., when IP address changes or the interface is set UP)
+	* fixed couple of cases where EAPOL state for a station was freed
+	  twice causing a segfault for hostapd
+	* fixed couple of bugs in processing WPA deauthentication (freed data
+	  was used)
+
+2004-05-31 - v0.2.2
+	* fixed WPA/WPA2 group rekeying to use key index correctly (GN/GM)
+	* fixed group rekeying to send zero TSC in EAPOL-Key messages to fix
+	  cases where STAs dropped multicast frames as replay attacks
+	* added support for copying RADIUS Attribute 'Class' from
+	  authentication messages into accounting messages
+	* send canned EAP failure if RADIUS server sends Access-Reject without
+	  EAP message (previously, Supplicant was not notified in this case)
+	* fixed mixed WPA-PSK and WPA-EAP mode to work with WPA-PSK (i.e., do
+	  not start EAPOL state machines if the STA selected to use WPA-PSK)
+
+2004-05-06 - v0.2.1
+	* added WPA and IEEE 802.11i/RSN (WPA2) Authenticator functionality
+	  - based on IEEE 802.11i/D10.0 but modified to interoperate with WPA
+	    (i.e., IEEE 802.11i/D3.0)
+	  - supports WPA-only, RSN-only, and mixed WPA/RSN mode
+	  - both WPA-PSK and WPA-RADIUS/EAP are supported
+	  - PMKSA caching and pre-authentication
+	  - new hostapd.conf variables: wpa, wpa_psk, wpa_passphrase,
+	    wpa_key_mgmt, wpa_pairwise, wpa_group_rekey, wpa_gmk_rekey,
+	    rsn_preauth, rsn_preauth_interfaces
+	* fixed interim accounting to remove any pending accounting messages
+	  to the STA before sending a new one
+
+2004-02-15 - v0.2.0
+	* added support for Acct-Interim-Interval:
+	  - draft-ietf-radius-acct-interim-01.txt
+	  - use Acct-Interim-Interval attribute from Access-Accept if local
+	    'radius_acct_interim_interval' is not set
+	  - allow different update intervals for each STA
+	* fixed event loop to call signal handlers only after returning from
+	  the real signal handler
+	* reset sta->timeout_next after successful association to make sure
+	  that the previously registered inactivity timer will not remove the
+	  STA immediately (e.g., if STA deauthenticates and re-associates
+	  before the timer is triggered).
+	* added new hostapd.conf variable, nas_identifier, that can be used to
+	  add an optional RADIUS Attribute, NAS-Identifier, into authentication
+	  and accounting messages
+	* added support for Accounting-On and Accounting-Off messages
+	* fixed accounting session handling to send Accounting-Start only once
+	  per session and not to send Accounting-Stop if the session was not
+	  initialized properly
+	* fixed Accounting-Stop statistics in cases where the message was
+	  previously sent after the kernel entry for the STA (and/or IEEE
+	  802.1X data) was removed
+
+
+Note:
+
+Older changes up to and including v0.1.0 are included in the ChangeLog
+of the Host AP driver.
diff --git a/hostap/hostapd/Makefile b/hostap/hostapd/Makefile
new file mode 100644
index 0000000..a812b9d
--- /dev/null
+++ b/hostap/hostapd/Makefile
@@ -0,0 +1,1044 @@
+ifndef CC
+CC=gcc
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+CFLAGS += $(EXTRA_CFLAGS)
+CFLAGS += -I$(abspath ../src)
+CFLAGS += -I$(abspath ../src/utils)
+
+export BINDIR ?= /usr/local/bin/
+
+# Uncomment following line and set the path to your kernel tree include
+# directory if your C library does not include all header files.
+# CFLAGS += -DUSE_KERNEL_HEADERS -I/usr/src/linux/include
+
+-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
+else
+CONFIG_OS=unix
+endif
+endif
+
+ifeq ($(CONFIG_OS), internal)
+CFLAGS += -DOS_NO_C_LIB_DEFINES
+endif
+
+ifdef CONFIG_NATIVE_WINDOWS
+CFLAGS += -DCONFIG_NATIVE_WINDOWS
+LIBS += -lws2_32
+endif
+
+OBJS += main.o
+OBJS += config_file.o
+
+OBJS += ../src/ap/hostapd.o
+OBJS += ../src/ap/wpa_auth_glue.o
+OBJS += ../src/ap/drv_callbacks.o
+OBJS += ../src/ap/ap_drv_ops.o
+OBJS += ../src/ap/utils.o
+OBJS += ../src/ap/authsrv.o
+OBJS += ../src/ap/ieee802_1x.o
+OBJS += ../src/ap/ap_config.o
+OBJS += ../src/ap/eap_user_db.o
+OBJS += ../src/ap/ieee802_11_auth.o
+OBJS += ../src/ap/sta_info.o
+OBJS += ../src/ap/wpa_auth.o
+OBJS += ../src/ap/tkip_countermeasures.o
+OBJS += ../src/ap/ap_mlme.o
+OBJS += ../src/ap/wpa_auth_ie.o
+OBJS += ../src/ap/preauth_auth.o
+OBJS += ../src/ap/pmksa_cache_auth.o
+OBJS += ../src/ap/ieee802_11_shared.o
+OBJS += ../src/ap/beacon.o
+OBJS += ../src/ap/bss_load.o
+
+OBJS_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o
+
+NEED_RC4=y
+NEED_AES=y
+NEED_MD5=y
+NEED_SHA1=y
+
+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
+HOBJS += ../src/utils/trace.o
+LDFLAGS += -rdynamic
+CFLAGS += -funwind-tables
+ifdef CONFIG_WPA_TRACE_BFD
+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
+
+ifndef CONFIG_ELOOP
+CONFIG_ELOOP=eloop
+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
+
+ifdef CONFIG_ELOOP_POLL
+CFLAGS += -DCONFIG_ELOOP_POLL
+endif
+
+ifdef CONFIG_ELOOP_EPOLL
+CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
+
+OBJS += ../src/utils/common.o
+OBJS_c += ../src/utils/common.o
+OBJS += ../src/utils/wpa_debug.o
+OBJS_c += ../src/utils/wpa_debug.o
+OBJS += ../src/utils/wpabuf.o
+OBJS += ../src/utils/os_$(CONFIG_OS).o
+OBJS += ../src/utils/ip_addr.o
+
+OBJS += ../src/common/ieee802_11_common.o
+OBJS += ../src/common/wpa_common.o
+OBJS += ../src/common/hw_features_common.o
+
+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 support for dumping internal state
+# through control interface commands (undefine it, if you want to save in
+# binary size)
+CFLAGS += -DHOSTAPD_DUMP_STATE
+OBJS += ../src/eapol_auth/eapol_auth_dump.o
+endif
+
+ifdef CONFIG_NO_RADIUS
+CFLAGS += -DCONFIG_NO_RADIUS
+CONFIG_NO_ACCOUNTING=y
+else
+OBJS += ../src/radius/radius.o
+OBJS += ../src/radius/radius_client.o
+OBJS += ../src/radius/radius_das.o
+endif
+
+ifdef CONFIG_NO_ACCOUNTING
+CFLAGS += -DCONFIG_NO_ACCOUNTING
+else
+OBJS += ../src/ap/accounting.o
+endif
+
+ifdef CONFIG_NO_VLAN
+CFLAGS += -DCONFIG_NO_VLAN
+else
+OBJS += ../src/ap/vlan_init.o
+ifdef CONFIG_VLAN_NETLINK
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+OBJS += ../src/ap/vlan_util.o
+endif
+CFLAGS += -DCONFIG_VLAN_NETLINK
+endif
+endif
+
+ifdef CONFIG_NO_CTRL_IFACE
+CFLAGS += -DCONFIG_NO_CTRL_IFACE
+else
+OBJS += ctrl_iface.o
+OBJS += ../src/ap/ctrl_iface_ap.o
+endif
+
+CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX
+
+ifdef CONFIG_IAPP
+CFLAGS += -DCONFIG_IAPP
+OBJS += ../src/ap/iapp.o
+endif
+
+ifdef CONFIG_RSN_PREAUTH
+CFLAGS += -DCONFIG_RSN_PREAUTH
+CONFIG_L2_PACKET=y
+endif
+
+ifdef CONFIG_PEERKEY
+CFLAGS += -DCONFIG_PEERKEY
+OBJS += ../src/ap/peerkey_auth.o
+endif
+
+ifdef CONFIG_HS20
+NEED_AES_OMAC1=y
+CONFIG_PROXYARP=y
+endif
+
+ifdef CONFIG_PROXYARP
+CONFIG_L2_PACKET=y
+endif
+
+ifdef CONFIG_SUITEB
+CFLAGS += -DCONFIG_SUITEB
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_SUITEB192
+CFLAGS += -DCONFIG_SUITEB192
+NEED_SHA384=y
+endif
+
+ifdef CONFIG_IEEE80211W
+CFLAGS += -DCONFIG_IEEE80211W
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211R
+OBJS += ../src/ap/wpa_auth_ft.o
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+NEED_AES_UNWRAP=y
+endif
+
+ifdef CONFIG_SAE
+CFLAGS += -DCONFIG_SAE
+OBJS += ../src/common/sae.o
+NEED_ECC=y
+NEED_DH_GROUPS=y
+NEED_AP_MLME=y
+endif
+
+ifdef CONFIG_WNM
+CFLAGS += -DCONFIG_WNM
+OBJS += ../src/ap/wnm_ap.o
+endif
+
+ifdef CONFIG_IEEE80211N
+CFLAGS += -DCONFIG_IEEE80211N
+endif
+
+ifdef CONFIG_IEEE80211AC
+CFLAGS += -DCONFIG_IEEE80211AC
+endif
+
+include ../src/drivers/drivers.mak
+OBJS += $(DRV_AP_OBJS)
+CFLAGS += $(DRV_AP_CFLAGS)
+LDFLAGS += $(DRV_AP_LDFLAGS)
+LIBS += $(DRV_AP_LIBS)
+
+ifdef CONFIG_L2_PACKET
+ifdef CONFIG_DNET_PCAP
+ifdef CONFIG_L2_FREEBSD
+LIBS += -lpcap
+OBJS += ../src/l2_packet/l2_packet_freebsd.o
+else
+LIBS += -ldnet -lpcap
+OBJS += ../src/l2_packet/l2_packet_pcap.o
+endif
+else
+OBJS += ../src/l2_packet/l2_packet_linux.o
+endif
+else
+OBJS += ../src/l2_packet/l2_packet_none.o
+endif
+
+
+ifdef CONFIG_ERP
+CFLAGS += -DCONFIG_ERP
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
+ifdef CONFIG_EAP_MD5
+CFLAGS += -DEAP_SERVER_MD5
+OBJS += ../src/eap_server/eap_server_md5.o
+CHAP=y
+endif
+
+ifdef CONFIG_EAP_TLS
+CFLAGS += -DEAP_SERVER_TLS
+OBJS += ../src/eap_server/eap_server_tls.o
+TLS_FUNCS=y
+endif
+
+ifdef CONFIG_EAP_UNAUTH_TLS
+CFLAGS += -DEAP_SERVER_UNAUTH_TLS
+ifndef CONFIG_EAP_TLS
+OBJS += ../src/eap_server/eap_server_tls.o
+TLS_FUNCS=y
+endif
+endif
+
+ifdef CONFIG_EAP_PEAP
+CFLAGS += -DEAP_SERVER_PEAP
+OBJS += ../src/eap_server/eap_server_peap.o
+OBJS += ../src/eap_common/eap_peap_common.o
+TLS_FUNCS=y
+CONFIG_EAP_MSCHAPV2=y
+endif
+
+ifdef CONFIG_EAP_TTLS
+CFLAGS += -DEAP_SERVER_TTLS
+OBJS += ../src/eap_server/eap_server_ttls.o
+TLS_FUNCS=y
+CHAP=y
+endif
+
+ifdef CONFIG_EAP_MSCHAPV2
+CFLAGS += -DEAP_SERVER_MSCHAPV2
+OBJS += ../src/eap_server/eap_server_mschapv2.o
+MS_FUNCS=y
+endif
+
+ifdef CONFIG_EAP_GTC
+CFLAGS += -DEAP_SERVER_GTC
+OBJS += ../src/eap_server/eap_server_gtc.o
+endif
+
+ifdef CONFIG_EAP_SIM
+CFLAGS += -DEAP_SERVER_SIM
+OBJS += ../src/eap_server/eap_server_sim.o
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_AKA
+CFLAGS += -DEAP_SERVER_AKA
+OBJS += ../src/eap_server/eap_server_aka.o
+CONFIG_EAP_SIM_COMMON=y
+NEED_SHA256=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_AKA_PRIME
+CFLAGS += -DEAP_SERVER_AKA_PRIME
+endif
+
+ifdef CONFIG_EAP_SIM_COMMON
+OBJS += ../src/eap_common/eap_sim_common.o
+# Example EAP-SIM/AKA interface for GSM/UMTS authentication. This can be
+# replaced with another file implementating the interface specified in
+# eap_sim_db.h.
+OBJS += ../src/eap_server/eap_sim_db.o
+NEED_FIPS186_2_PRF=y
+endif
+
+ifdef CONFIG_EAP_PAX
+CFLAGS += -DEAP_SERVER_PAX
+OBJS += ../src/eap_server/eap_server_pax.o ../src/eap_common/eap_pax_common.o
+endif
+
+ifdef CONFIG_EAP_PSK
+CFLAGS += -DEAP_SERVER_PSK
+OBJS += ../src/eap_server/eap_server_psk.o ../src/eap_common/eap_psk_common.o
+NEED_AES_OMAC1=y
+NEED_AES_ENCBLOCK=y
+NEED_AES_EAX=y
+endif
+
+ifdef CONFIG_EAP_SAKE
+CFLAGS += -DEAP_SERVER_SAKE
+OBJS += ../src/eap_server/eap_server_sake.o ../src/eap_common/eap_sake_common.o
+endif
+
+ifdef CONFIG_EAP_GPSK
+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_GPSK_SHA256
+endif
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_EAP_PWD
+CFLAGS += -DEAP_SERVER_PWD
+OBJS += ../src/eap_server/eap_server_pwd.o ../src/eap_common/eap_pwd_common.o
+NEED_SHA256=y
+endif
+
+ifdef CONFIG_EAP_EKE
+CFLAGS += -DEAP_SERVER_EKE
+OBJS += ../src/eap_server/eap_server_eke.o ../src/eap_common/eap_eke_common.o
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+endif
+
+ifdef CONFIG_EAP_VENDOR_TEST
+CFLAGS += -DEAP_SERVER_VENDOR_TEST
+OBJS += ../src/eap_server/eap_server_vendor_test.o
+endif
+
+ifdef CONFIG_EAP_FAST
+CFLAGS += -DEAP_SERVER_FAST
+OBJS += ../src/eap_server/eap_server_fast.o
+OBJS += ../src/eap_common/eap_fast_common.o
+TLS_FUNCS=y
+NEED_T_PRF=y
+NEED_AES_UNWRAP=y
+endif
+
+ifdef CONFIG_WPS
+CFLAGS += -DCONFIG_WPS -DEAP_SERVER_WSC
+OBJS += ../src/utils/uuid.o
+OBJS += ../src/ap/wps_hostapd.o
+OBJS += ../src/eap_server/eap_server_wsc.o ../src/eap_common/eap_wsc_common.o
+OBJS += ../src/wps/wps.o
+OBJS += ../src/wps/wps_common.o
+OBJS += ../src/wps/wps_attr_parse.o
+OBJS += ../src/wps/wps_attr_build.o
+OBJS += ../src/wps/wps_attr_process.o
+OBJS += ../src/wps/wps_dev_attr.o
+OBJS += ../src/wps/wps_enrollee.o
+OBJS += ../src/wps/wps_registrar.o
+NEED_DH_GROUPS=y
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_AES_CBC=y
+NEED_MODEXP=y
+CONFIG_EAP=y
+
+ifdef CONFIG_WPS_NFC
+CFLAGS += -DCONFIG_WPS_NFC
+OBJS += ../src/wps/ndef.o
+NEED_WPS_OOB=y
+endif
+
+ifdef NEED_WPS_OOB
+CFLAGS += -DCONFIG_WPS_OOB
+endif
+
+ifdef CONFIG_WPS_UPNP
+CFLAGS += -DCONFIG_WPS_UPNP
+OBJS += ../src/wps/wps_upnp.o
+OBJS += ../src/wps/wps_upnp_ssdp.o
+OBJS += ../src/wps/wps_upnp_web.o
+OBJS += ../src/wps/wps_upnp_event.o
+OBJS += ../src/wps/wps_upnp_ap.o
+OBJS += ../src/wps/upnp_xml.o
+OBJS += ../src/wps/httpread.o
+OBJS += ../src/wps/http_client.o
+OBJS += ../src/wps/http_server.o
+endif
+
+ifdef CONFIG_WPS_STRICT
+CFLAGS += -DCONFIG_WPS_STRICT
+OBJS += ../src/wps/wps_validate.o
+endif
+
+ifdef CONFIG_WPS_TESTING
+CFLAGS += -DCONFIG_WPS_TESTING
+endif
+
+endif
+
+ifdef CONFIG_EAP_IKEV2
+CFLAGS += -DEAP_SERVER_IKEV2
+OBJS += ../src/eap_server/eap_server_ikev2.o ../src/eap_server/ikev2.o
+OBJS += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+NEED_MODEXP=y
+NEED_CIPHER=y
+endif
+
+ifdef CONFIG_EAP_TNC
+CFLAGS += -DEAP_SERVER_TNC
+OBJS += ../src/eap_server/eap_server_tnc.o
+OBJS += ../src/eap_server/tncs.o
+NEED_BASE64=y
+ifndef CONFIG_DRIVER_BSD
+LIBS += -ldl
+endif
+endif
+
+# Basic EAP functionality is needed for EAPOL
+OBJS += eap_register.o
+OBJS += ../src/eap_server/eap_server.o
+OBJS += ../src/eap_common/eap_common.o
+OBJS += ../src/eap_server/eap_server_methods.o
+OBJS += ../src/eap_server/eap_server_identity.o
+CFLAGS += -DEAP_SERVER_IDENTITY
+
+ifdef CONFIG_EAP
+CFLAGS += -DEAP_SERVER
+endif
+
+ifdef CONFIG_PKCS12
+CFLAGS += -DPKCS12_FUNCS
+endif
+
+ifdef MS_FUNCS
+OBJS += ../src/crypto/ms_funcs.o
+NEED_DES=y
+NEED_MD4=y
+endif
+
+ifdef CHAP
+OBJS += ../src/eap_common/chap.o
+endif
+
+ifdef TLS_FUNCS
+NEED_DES=y
+# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS)
+CFLAGS += -DEAP_TLS_FUNCS
+OBJS += ../src/eap_server/eap_server_tls_common.o
+NEED_TLS_PRF=y
+endif
+
+ifndef CONFIG_TLS
+CONFIG_TLS=openssl
+endif
+
+ifdef CONFIG_TLSV11
+CFLAGS += -DCONFIG_TLSV11
+endif
+
+ifdef CONFIG_TLSV12
+CFLAGS += -DCONFIG_TLSV12
+NEED_SHA256=y
+endif
+
+ifeq ($(CONFIG_TLS), openssl)
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/tls_openssl.o
+LIBS += -lssl
+endif
+OBJS += ../src/crypto/crypto_openssl.o
+HOBJS += ../src/crypto/crypto_openssl.o
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_openssl.o
+endif
+NEED_SHA256=y
+NEED_TLS_PRF_SHA256=y
+LIBS += -lcrypto
+LIBS_h += -lcrypto
+ifdef CONFIG_TLS_ADD_DL
+LIBS += -ldl
+LIBS_h += -ldl
+endif
+endif
+
+ifeq ($(CONFIG_TLS), gnutls)
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/tls_gnutls.o
+LIBS += -lgnutls -lgpg-error
+endif
+OBJS += ../src/crypto/crypto_gnutls.o
+HOBJS += ../src/crypto/crypto_gnutls.o
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
+endif
+LIBS += -lgcrypt
+LIBS_h += -lgcrypt
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+
+ifeq ($(CONFIG_TLS), internal)
+ifndef CONFIG_CRYPTO
+CONFIG_CRYPTO=internal
+endif
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/crypto_internal-rsa.o
+OBJS += ../src/crypto/tls_internal.o
+OBJS += ../src/tls/tlsv1_common.o
+OBJS += ../src/tls/tlsv1_record.o
+OBJS += ../src/tls/tlsv1_cred.o
+OBJS += ../src/tls/tlsv1_server.o
+OBJS += ../src/tls/tlsv1_server_write.o
+OBJS += ../src/tls/tlsv1_server_read.o
+OBJS += ../src/tls/asn1.o
+OBJS += ../src/tls/rsa.o
+OBJS += ../src/tls/x509v3.o
+OBJS += ../src/tls/pkcs1.o
+OBJS += ../src/tls/pkcs5.o
+OBJS += ../src/tls/pkcs8.o
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+ifdef CONFIG_TLSV12
+NEED_TLS_PRF_SHA256=y
+endif
+NEED_MODEXP=y
+NEED_CIPHER=y
+CFLAGS += -DCONFIG_TLS_INTERNAL
+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+endif
+ifdef NEED_CIPHER
+NEED_DES=y
+OBJS += ../src/crypto/crypto_internal-cipher.o
+endif
+ifdef NEED_MODEXP
+OBJS += ../src/crypto/crypto_internal-modexp.o
+OBJS += ../src/tls/bignum.o
+endif
+ifeq ($(CONFIG_CRYPTO), libtomcrypt)
+OBJS += ../src/crypto/crypto_libtomcrypt.o
+LIBS += -ltomcrypt -ltfm
+LIBS_h += -ltomcrypt -ltfm
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), internal)
+OBJS += ../src/crypto/crypto_internal.o
+NEED_AES_DEC=y
+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+ifdef CONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST
+CFLAGS += -DLTM_FAST
+endif
+else
+LIBS += -ltommath
+LIBS_h += -ltommath
+endif
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_DES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD4=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), cryptoapi)
+OBJS += ../src/crypto/crypto_cryptoapi.o
+OBJS_p += ../src/crypto/crypto_cryptoapi.o
+CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+ifeq ($(CONFIG_TLS), none)
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/tls_none.o
+CFLAGS += -DEAP_TLS_NONE
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+endif
+OBJS += ../src/crypto/crypto_none.o
+OBJS_p += ../src/crypto/crypto_none.o
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+
+ifndef TLS_FUNCS
+OBJS += ../src/crypto/tls_none.o
+ifeq ($(CONFIG_TLS), internal)
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+AESOBJS = # none so far
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-enc.o
+endif
+
+ifneq ($(CONFIG_TLS), openssl)
+AESOBJS += ../src/crypto/aes-wrap.o
+endif
+ifdef NEED_AES_EAX
+AESOBJS += ../src/crypto/aes-eax.o
+NEED_AES_CTR=y
+endif
+ifdef NEED_AES_CTR
+AESOBJS += ../src/crypto/aes-ctr.o
+endif
+ifdef NEED_AES_ENCBLOCK
+AESOBJS += ../src/crypto/aes-encblock.o
+endif
+ifdef NEED_AES_OMAC1
+AESOBJS += ../src/crypto/aes-omac1.o
+endif
+ifdef NEED_AES_UNWRAP
+ifneq ($(CONFIG_TLS), openssl)
+NEED_AES_DEC=y
+AESOBJS += ../src/crypto/aes-unwrap.o
+endif
+endif
+ifdef NEED_AES_CBC
+NEED_AES_DEC=y
+ifneq ($(CONFIG_TLS), openssl)
+AESOBJS += ../src/crypto/aes-cbc.o
+endif
+endif
+ifdef NEED_AES_DEC
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += ../src/crypto/aes-internal-dec.o
+endif
+endif
+ifdef NEED_AES
+OBJS += $(AESOBJS)
+endif
+
+ifdef NEED_SHA1
+ifneq ($(CONFIG_TLS), openssl)
+SHA1OBJS += ../src/crypto/sha1.o
+endif
+SHA1OBJS += ../src/crypto/sha1-prf.o
+ifdef CONFIG_INTERNAL_SHA1
+SHA1OBJS += ../src/crypto/sha1-internal.o
+ifdef NEED_FIPS186_2_PRF
+SHA1OBJS += ../src/crypto/fips_prf_internal.o
+endif
+endif
+ifneq ($(CONFIG_TLS), openssl)
+SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
+endif
+ifdef NEED_T_PRF
+SHA1OBJS += ../src/crypto/sha1-tprf.o
+endif
+ifdef NEED_TLS_PRF
+SHA1OBJS += ../src/crypto/sha1-tlsprf.o
+endif
+endif
+
+ifdef NEED_SHA1
+OBJS += $(SHA1OBJS)
+endif
+
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += ../src/crypto/md5.o
+endif
+
+ifdef NEED_MD5
+ifdef CONFIG_INTERNAL_MD5
+OBJS += ../src/crypto/md5-internal.o
+HOBJS += ../src/crypto/md5-internal.o
+endif
+endif
+
+ifdef NEED_MD4
+ifdef CONFIG_INTERNAL_MD4
+OBJS += ../src/crypto/md4-internal.o
+endif
+endif
+
+ifdef NEED_DES
+ifdef CONFIG_INTERNAL_DES
+OBJS += ../src/crypto/des-internal.o
+endif
+endif
+
+ifdef CONFIG_NO_RC4
+CFLAGS += -DCONFIG_NO_RC4
+endif
+
+ifdef NEED_RC4
+ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
+OBJS += ../src/crypto/rc4.o
+endif
+endif
+endif
+
+ifdef NEED_SHA256
+CFLAGS += -DCONFIG_SHA256
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += ../src/crypto/sha256.o
+endif
+OBJS += ../src/crypto/sha256-prf.o
+ifdef CONFIG_INTERNAL_SHA256
+OBJS += ../src/crypto/sha256-internal.o
+endif
+ifdef NEED_TLS_PRF_SHA256
+OBJS += ../src/crypto/sha256-tlsprf.o
+endif
+ifdef NEED_HMAC_SHA256_KDF
+OBJS += ../src/crypto/sha256-kdf.o
+endif
+endif
+ifdef NEED_SHA384
+CFLAGS += -DCONFIG_SHA384
+OBJS += ../src/crypto/sha384-prf.o
+endif
+
+ifdef NEED_DH_GROUPS
+OBJS += ../src/crypto/dh_groups.o
+endif
+ifdef NEED_DH_GROUPS_ALL
+CFLAGS += -DALL_DH_GROUPS
+endif
+ifdef CONFIG_INTERNAL_DH_GROUP5
+ifdef NEED_DH_GROUPS
+OBJS += ../src/crypto/dh_group5.o
+endif
+endif
+
+ifdef NEED_ECC
+CFLAGS += -DCONFIG_ECC
+endif
+
+ifdef CONFIG_NO_RANDOM_POOL
+CFLAGS += -DCONFIG_NO_RANDOM_POOL
+else
+OBJS += ../src/crypto/random.o
+HOBJS += ../src/crypto/random.o
+HOBJS += ../src/utils/eloop.o
+HOBJS += $(SHA1OBJS)
+ifneq ($(CONFIG_TLS), openssl)
+HOBJS += ../src/crypto/md5.o
+endif
+endif
+
+ifdef CONFIG_RADIUS_SERVER
+CFLAGS += -DRADIUS_SERVER
+OBJS += ../src/radius/radius_server.o
+endif
+
+ifdef CONFIG_IPV6
+CFLAGS += -DCONFIG_IPV6
+endif
+
+ifdef CONFIG_DRIVER_RADIUS_ACL
+CFLAGS += -DCONFIG_DRIVER_RADIUS_ACL
+endif
+
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+# define CONFIG_FULL_DYNAMIC_VLAN to have hostapd manipulate bridges
+# and vlan interfaces for the vlan feature.
+CFLAGS += -DCONFIG_FULL_DYNAMIC_VLAN
+endif
+
+ifdef NEED_BASE64
+OBJS += ../src/utils/base64.o
+endif
+
+ifdef NEED_AP_MLME
+OBJS += ../src/ap/wmm.o
+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
+OBJS += ../src/ap/ieee802_11_ht.o
+endif
+
+ifdef CONFIG_IEEE80211AC
+OBJS += ../src/ap/ieee802_11_vht.o
+endif
+
+ifdef CONFIG_P2P_MANAGER
+CFLAGS += -DCONFIG_P2P_MANAGER
+OBJS += ../src/ap/p2p_hostapd.o
+endif
+
+ifdef CONFIG_HS20
+CFLAGS += -DCONFIG_HS20
+OBJS += ../src/ap/hs20.o
+CONFIG_INTERWORKING=y
+endif
+
+ifdef CONFIG_INTERWORKING
+CFLAGS += -DCONFIG_INTERWORKING
+OBJS += ../src/common/gas.o
+OBJS += ../src/ap/gas_serv.o
+endif
+
+ifdef CONFIG_PROXYARP
+CFLAGS += -DCONFIG_PROXYARP
+OBJS += ../src/ap/x_snoop.o
+OBJS += ../src/ap/dhcp_snoop.o
+ifdef CONFIG_IPV6
+OBJS += ../src/ap/ndisc_snoop.o
+endif
+endif
+
+OBJS += ../src/drivers/driver_common.o
+
+ifdef CONFIG_WPA_CLI_EDIT
+OBJS_c += ../src/utils/edit.o
+else
+OBJS_c += ../src/utils/edit_simple.o
+endif
+
+ifdef CONFIG_ACS
+CFLAGS += -DCONFIG_ACS
+OBJS += ../src/ap/acs.o
+LIBS += -lm
+endif
+
+ifdef CONFIG_NO_STDOUT_DEBUG
+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
+
+ifdef CONFIG_SQLITE
+CFLAGS += -DCONFIG_SQLITE
+LIBS += -lsqlite3
+LIBS_h += -lsqlite3
+endif
+
+ifdef CONFIG_FST
+CFLAGS += -DCONFIG_FST
+OBJS += ../src/fst/fst.o
+OBJS += ../src/fst/fst_group.o
+OBJS += ../src/fst/fst_iface.o
+OBJS += ../src/fst/fst_session.o
+OBJS += ../src/fst/fst_ctrl_aux.o
+ifdef CONFIG_FST_TEST
+CFLAGS += -DCONFIG_FST_TEST
+endif
+ifndef CONFIG_NO_CTRL_IFACE
+OBJS += ../src/fst/fst_ctrl_iface.o
+endif
+endif
+
+ALL=hostapd hostapd_cli
+
+all: verify_config $(ALL)
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+ifeq ($(QUIET), 1)
+Q=@
+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 \
+		echo 'Building hostapd requires a configuration file'; \
+		echo '(.config). See README for more instructions. You can'; \
+		echo 'run "cp defconfig .config" to create an example'; \
+		echo 'configuration.'; \
+		exit 1; \
+	fi
+
+$(DESTDIR)$(BINDIR)/%: %
+	install -D $(<) $(@)
+
+install: $(addprefix $(DESTDIR)$(BINDIR)/,$(ALL))
+
+../src/drivers/build.hostapd:
+	@if [ -f ../src/drivers/build.wpa_supplicant ]; then \
+		$(MAKE) -C ../src/drivers clean; \
+	fi
+	@touch ../src/drivers/build.hostapd
+
+BCHECK=../src/drivers/build.hostapd
+
+hostapd: $(BCHECK) $(OBJS)
+	$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
+	@$(E) "  LD " $@
+
+ifdef CONFIG_WPA_TRACE
+OBJS_c += ../src/utils/trace.o
+endif
+hostapd_cli: $(OBJS_c)
+	$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
+	@$(E) "  LD " $@
+
+NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS)
+NOBJS += ../src/utils/common.o
+ifdef NEED_RC4
+ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
+NOBJS += ../src/crypto/rc4.o
+endif
+endif
+endif
+ifdef CONFIG_INTERNAL_MD5
+NOBJS += ../src/crypto/md5-internal.o
+endif
+NOBJS += ../src/crypto/crypto_openssl.o ../src/utils/os_$(CONFIG_OS).o
+NOBJS += ../src/utils/wpa_debug.o
+NOBJS += ../src/utils/wpabuf.o
+ifdef CONFIG_WPA_TRACE
+NOBJS += ../src/utils/trace.o
+LIBS_n += -lbfd
+endif
+ifdef TLS_FUNCS
+LIBS_n += -lcrypto
+endif
+
+HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o
+HOBJS += ../src/crypto/aes-encblock.o
+ifdef CONFIG_INTERNAL_AES
+HOBJS += ../src/crypto/aes-internal.o
+HOBJS += ../src/crypto/aes-internal-enc.o
+endif
+
+nt_password_hash: $(NOBJS)
+	$(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
+	@$(E) "  LD " $@
+
+hlr_auc_gw: $(HOBJS)
+	$(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 *.gcno *.gcda *.gcov
+	rm -f lcov.info
+	rm -rf lcov-html
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/hostapd/README b/hostap/hostapd/README
new file mode 100644
index 0000000..366b199
--- /dev/null
+++ b/hostap/hostapd/README
@@ -0,0 +1,366 @@
+hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP
+	  Authenticator and RADIUS authentication server
+================================================================
+
+Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
+All Rights Reserved.
+
+This program is 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.
+
+
+
+License
+-------
+
+This software may be distributed, used, and modified under the terms of
+BSD license:
+
+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.
+
+
+
+Introduction
+============
+
+Originally, hostapd was an optional user space component for Host AP
+driver. It adds more features to the basic IEEE 802.11 management
+included in the kernel driver: using external RADIUS authentication
+server for MAC address based access control, IEEE 802.1X Authenticator
+and dynamic WEP keying, RADIUS accounting, WPA/WPA2 (IEEE 802.11i/RSN)
+Authenticator and dynamic TKIP/CCMP keying.
+
+The current version includes support for other drivers, an integrated
+EAP server (i.e., allow full authentication without requiring
+an external RADIUS authentication server), and RADIUS authentication
+server for EAP authentication.
+
+
+Requirements
+------------
+
+Current hardware/software requirements:
+- drivers:
+	Host AP driver for Prism2/2.5/3.
+	(http://hostap.epitest.fi/)
+	Please note that station firmware version needs to be 1.7.0 or newer
+	to work in WPA mode.
+
+	mac80211-based drivers that support AP mode (with driver=nl80211).
+	This includes drivers for Atheros (ath9k) and Broadcom (b43)
+	chipsets.
+
+	Any wired Ethernet driver for wired IEEE 802.1X authentication
+	(experimental code)
+
+	FreeBSD -current (with some kernel mods that have not yet been
+	committed when hostapd v0.3.0 was released)
+	BSD net80211 layer (e.g., Atheros driver)
+
+
+Build configuration
+-------------------
+
+In order to be able to build hostapd, you will need to create a build
+time configuration file, .config that selects which optional
+components are included. See defconfig file for example configuration
+and list of available options.
+
+
+
+IEEE 802.1X
+===========
+
+IEEE Std 802.1X-2001 is a standard for port-based network access
+control. In case of IEEE 802.11 networks, a "virtual port" is used
+between each associated station and the AP. IEEE 802.11 specifies
+minimal authentication mechanism for stations, whereas IEEE 802.1X
+introduces a extensible mechanism for authenticating and authorizing
+users.
+
+IEEE 802.1X uses elements called Supplicant, Authenticator, Port
+Access Entity, and Authentication Server. Supplicant is a component in
+a station and it performs the authentication with the Authentication
+Server. An access point includes an Authenticator that relays the packets
+between a Supplicant and an Authentication Server. In addition, it has a
+Port Access Entity (PAE) with Authenticator functionality for
+controlling the virtual port authorization, i.e., whether to accept
+packets from or to the station.
+
+IEEE 802.1X uses Extensible Authentication Protocol (EAP). The frames
+between a Supplicant and an Authenticator are sent using EAP over LAN
+(EAPOL) and the Authenticator relays these frames to the Authentication
+Server (and similarly, relays the messages from the Authentication
+Server to the Supplicant). The Authentication Server can be colocated with the
+Authenticator, in which case there is no need for additional protocol
+for EAP frame transmission. However, a more common configuration is to
+use an external Authentication Server and encapsulate EAP frame in the
+frames used by that server. RADIUS is suitable for this, but IEEE
+802.1X would also allow other mechanisms.
+
+Host AP driver includes PAE functionality in the kernel driver. It
+is a relatively simple mechanism for denying normal frames going to
+or coming from an unauthorized port. PAE allows IEEE 802.1X related
+frames to be passed between the Supplicant and the Authenticator even
+on an unauthorized port.
+
+User space daemon, hostapd, includes Authenticator functionality. It
+receives 802.1X (EAPOL) frames from the Supplicant using the wlan#ap
+device that is also used with IEEE 802.11 management frames. The
+frames to the Supplicant are sent using the same device.
+
+The normal configuration of the Authenticator would use an external
+Authentication Server. hostapd supports RADIUS encapsulation of EAP
+packets, so the Authentication Server should be a RADIUS server, like
+FreeRADIUS (http://www.freeradius.org/). The Authenticator in hostapd
+relays the frames between the Supplicant and the Authentication
+Server. It also controls the PAE functionality in the kernel driver by
+controlling virtual port authorization, i.e., station-AP
+connection, based on the IEEE 802.1X state.
+
+When a station would like to use the services of an access point, it
+will first perform IEEE 802.11 authentication. This is normally done
+with open systems authentication, so there is no security. After
+this, IEEE 802.11 association is performed. If IEEE 802.1X is
+configured to be used, the virtual port for the station is set in
+Unauthorized state and only IEEE 802.1X frames are accepted at this
+point. The Authenticator will then ask the Supplicant to authenticate
+with the Authentication Server. After this is completed successfully,
+the virtual port is set to Authorized state and frames from and to the
+station are accepted.
+
+Host AP configuration for IEEE 802.1X
+-------------------------------------
+
+The user space daemon has its own configuration file that can be used to
+define AP options. Distribution package contains an example
+configuration file (hostapd/hostapd.conf) that can be used as a basis
+for configuration. It includes examples of all supported configuration
+options and short description of each option. hostapd should be started
+with full path to the configuration file as the command line argument,
+e.g., './hostapd /etc/hostapd.conf'. If you have more that one wireless
+LAN card, you can use one hostapd process for multiple interfaces by
+giving a list of configuration files (one per interface) in the command
+line.
+
+hostapd includes a minimal co-located IEEE 802.1X server which can be
+used to test IEEE 802.1X authentication. However, it should not be
+used in normal use since it does not provide any security. This can be
+configured by setting ieee8021x and minimal_eap options in the
+configuration file.
+
+An external Authentication Server (RADIUS) is configured with
+auth_server_{addr,port,shared_secret} options. In addition,
+ieee8021x and own_ip_addr must be set for this mode. With such
+configuration, the co-located Authentication Server is not used and EAP
+frames will be relayed using EAPOL between the Supplicant and the
+Authenticator and RADIUS encapsulation between the Authenticator and
+the Authentication Server. Other than this, the functionality is similar
+to the case with the co-located Authentication Server.
+
+Authentication Server and Supplicant
+------------------------------------
+
+Any RADIUS server supporting EAP should be usable as an IEEE 802.1X
+Authentication Server with hostapd Authenticator. FreeRADIUS
+(http://www.freeradius.org/) has been successfully tested with hostapd
+Authenticator and both Xsupplicant (http://www.open1x.org) and Windows
+XP Supplicants. EAP/TLS was used with Xsupplicant and
+EAP/MD5-Challenge with Windows XP.
+
+http://www.missl.cs.umd.edu/wireless/eaptls/ has useful information
+about using EAP/TLS with FreeRADIUS and Xsupplicant (just replace
+Cisco access point with Host AP driver, hostapd daemon, and a Prism2
+card ;-). http://www.freeradius.org/doc/EAP-MD5.html has information
+about using EAP/MD5 with FreeRADIUS, including instructions for WinXP
+configuration. http://www.denobula.com/EAPTLS.pdf has a HOWTO on
+EAP/TLS use with WinXP Supplicant.
+
+Automatic WEP key configuration
+-------------------------------
+
+EAP/TLS generates a session key that can be used to send WEP keys from
+an AP to authenticated stations. The Authenticator in hostapd can be
+configured to automatically select a random default/broadcast key
+(shared by all authenticated stations) with wep_key_len_broadcast
+option (5 for 40-bit WEP or 13 for 104-bit WEP). In addition,
+wep_key_len_unicast option can be used to configure individual unicast
+keys for stations. This requires support for individual keys in the
+station driver.
+
+WEP keys can be automatically updated by configuring rekeying. This
+will improve security of the network since same WEP key will only be
+used for a limited period of time. wep_rekey_period option sets the
+interval for rekeying in seconds.
+
+
+WPA/WPA2
+========
+
+Features
+--------
+
+Supported WPA/IEEE 802.11i features:
+- WPA-PSK ("WPA-Personal")
+- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise")
+- key management for CCMP, TKIP, WEP104, WEP40
+- RSN/WPA2 (IEEE 802.11i), including PMKSA caching and pre-authentication
+
+WPA
+---
+
+The original security mechanism of IEEE 802.11 standard was not
+designed to be strong and has proved to be insufficient for most
+networks that require some kind of security. Task group I (Security)
+of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked
+to address the flaws of the base standard and has in practice
+completed its work in May 2004. The IEEE 802.11i amendment to the IEEE
+802.11 standard was approved in June 2004 and this amendment is likely
+to be published in July 2004.
+
+Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the
+IEEE 802.11i work (draft 3.0) to define a subset of the security
+enhancements that can be implemented with existing wlan hardware. This
+is called Wi-Fi Protected Access<TM> (WPA). This has now become a
+mandatory component of interoperability testing and certification done
+by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web
+site (http://www.wi-fi.org/OpenSection/protected_access.asp).
+
+IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm
+for protecting wireless networks. WEP uses RC4 with 40-bit keys,
+24-bit initialization vector (IV), and CRC32 to protect against packet
+forgery. All these choices have proven to be insufficient: key space is
+too small against current attacks, RC4 key scheduling is insufficient
+(beginning of the pseudorandom stream should be skipped), IV space is
+too small and IV reuse makes attacks easier, there is no replay
+protection, and non-keyed authentication does not protect against bit
+flipping packet data.
+
+WPA is an intermediate solution for the security issues. It uses
+Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a
+compromise on strong security and possibility to use existing
+hardware. It still uses RC4 for the encryption like WEP, but with
+per-packet RC4 keys. In addition, it implements replay protection,
+keyed packet authentication mechanism (Michael MIC).
+
+Keys can be managed using two different mechanisms. WPA can either use
+an external authentication server (e.g., RADIUS) and EAP just like
+IEEE 802.1X is using or pre-shared keys without need for additional
+servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal",
+respectively. Both mechanisms will generate a master session key for
+the Authenticator (AP) and Supplicant (client station).
+
+WPA implements a new key handshake (4-Way Handshake and Group Key
+Handshake) for generating and exchanging data encryption keys between
+the Authenticator and Supplicant. This handshake is also used to
+verify that both Authenticator and Supplicant know the master session
+key. These handshakes are identical regardless of the selected key
+management mechanism (only the method for generating master session
+key changes).
+
+
+IEEE 802.11i / WPA2
+-------------------
+
+The design for parts of IEEE 802.11i that were not included in WPA has
+finished (May 2004) and this amendment to IEEE 802.11 was approved in
+June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new
+version of WPA called WPA2. This includes, e.g., support for more
+robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC)
+to replace TKIP and optimizations for handoff (reduced number of
+messages in initial key handshake, pre-authentication, and PMKSA caching).
+
+Some wireless LAN vendors are already providing support for CCMP in
+their WPA products. There is no "official" interoperability
+certification for CCMP and/or mixed modes using both TKIP and CCMP, so
+some interoperability issues can be expected even though many
+combinations seem to be working with equipment from different vendors.
+Testing for WPA2 is likely to start during the second half of 2004.
+
+hostapd configuration for WPA/WPA2
+----------------------------------
+
+TODO
+
+# Enable WPA. Setting this variable configures the AP to require WPA (either
+# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
+# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
+# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
+# RADIUS authentication server must be configured, and WPA-EAP must be included
+# in wpa_key_mgmt.
+# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0)
+# and/or WPA2 (full IEEE 802.11i/RSN):
+# bit0 = WPA
+# bit1 = IEEE 802.11i/RSN (WPA2)
+#wpa=1
+
+# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
+# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
+# (8..63 characters) that will be converted to PSK. This conversion uses SSID
+# so the PSK changes when ASCII passphrase is used and the SSID is changed.
+#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+#wpa_passphrase=secret passphrase
+
+# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
+# entries are separated with a space.
+#wpa_key_mgmt=WPA-PSK WPA-EAP
+
+# Set of accepted cipher suites (encryption algorithms) for pairwise keys
+# (unicast packets). This is a space separated list of algorithms:
+# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i]
+# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i]
+# Group cipher suite (encryption algorithm for broadcast and multicast frames)
+# is automatically selected based on this configuration. If only CCMP is
+# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
+# TKIP will be used as the group cipher.
+#wpa_pairwise=TKIP CCMP
+
+# Time interval for rekeying GTK (broadcast/multicast encryption keys) in
+# seconds.
+#wpa_group_rekey=600
+
+# Time interval for rekeying GMK (master key used internally to generate GTKs
+# (in seconds).
+#wpa_gmk_rekey=86400
+
+# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
+# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
+# authentication and key handshake before actually associating with a new AP.
+#rsn_preauth=1
+#
+# Space separated list of interfaces from which pre-authentication frames are
+# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all
+# interface that are used for connections to other APs. This could include
+# wired interfaces and WDS links. The normal wireless data interface towards
+# associated stations (e.g., wlan0) should not be added, since
+# pre-authentication is only used with APs other than the currently associated
+# one.
+#rsn_preauth_interfaces=eth0
diff --git a/hostap/hostapd/README-WPS b/hostap/hostapd/README-WPS
new file mode 100644
index 0000000..d5f713a
--- /dev/null
+++ b/hostap/hostapd/README-WPS
@@ -0,0 +1,352 @@
+hostapd and Wi-Fi Protected Setup (WPS)
+=======================================
+
+This document describes how the WPS implementation in hostapd can be
+configured and how an external component on an AP (e.g., web UI) is
+used to enable enrollment of client devices.
+
+
+Introduction to WPS
+-------------------
+
+Wi-Fi Protected Setup (WPS) is a mechanism for easy configuration of a
+wireless network. It allows automated generation of random keys (WPA
+passphrase/PSK) and configuration of an access point and client
+devices. WPS includes number of methods for setting up connections
+with PIN method and push-button configuration (PBC) being the most
+commonly deployed options.
+
+While WPS can enable more home networks to use encryption in the
+wireless network, it should be noted that the use of the PIN and
+especially PBC mechanisms for authenticating the initial key setup is
+not very secure. As such, use of WPS may not be suitable for
+environments that require secure network access without chance for
+allowing outsiders to gain access during the setup phase.
+
+WPS uses following terms to describe the entities participating in the
+network setup:
+- access point: the WLAN access point
+- Registrar: a device that control a network and can authorize
+  addition of new devices); this may be either in the AP ("internal
+  Registrar") or in an external device, e.g., a laptop, ("external
+  Registrar")
+- Enrollee: a device that is being authorized to use the network
+
+It should also be noted that the AP and a client device may change
+roles (i.e., AP acts as an Enrollee and client device as a Registrar)
+when WPS is used to configure the access point.
+
+
+More information about WPS is available from Wi-Fi Alliance:
+http://www.wi-fi.org/wifi-protected-setup
+
+
+hostapd implementation
+----------------------
+
+hostapd includes an optional WPS component that can be used as an
+internal WPS Registrar to manage addition of new WPS enabled clients
+to the network. In addition, WPS Enrollee functionality in hostapd can
+be used to allow external WPS Registrars to configure the access
+point, e.g., for initial network setup. In addition, hostapd can proxy a
+WPS registration between a wireless Enrollee and an external Registrar
+(e.g., Microsoft Vista or Atheros JumpStart) with UPnP.
+
+
+hostapd configuration
+---------------------
+
+WPS is an optional component that needs to be enabled in hostapd build
+configuration (.config). Here is an example configuration that
+includes WPS support and uses nl80211 driver interface:
+
+CONFIG_DRIVER_NL80211=y
+CONFIG_WPS=y
+CONFIG_WPS_UPNP=y
+
+Following parameter can be used to enable support for NFC config method:
+
+CONFIG_WPS_NFC=y
+
+
+Following section shows an example runtime configuration
+(hostapd.conf) that enables WPS:
+
+# Configure the driver and network interface
+driver=nl80211
+interface=wlan0
+
+# WPA2-Personal configuration for the AP
+ssid=wps-test
+wpa=2
+wpa_key_mgmt=WPA-PSK
+wpa_pairwise=CCMP
+# Default WPA passphrase for legacy (non-WPS) clients
+wpa_passphrase=12345678
+# Enable random per-device PSK generation for WPS clients
+# Please note that the file has to exists for hostapd to start (i.e., create an
+# empty file as a starting point).
+wpa_psk_file=/etc/hostapd.psk
+
+# Enable control interface for PBC/PIN entry
+ctrl_interface=/var/run/hostapd
+
+# Enable internal EAP server for EAP-WSC (part of Wi-Fi Protected Setup)
+eap_server=1
+
+# WPS configuration (AP configured, do not allow external WPS Registrars)
+wps_state=2
+ap_setup_locked=1
+# If UUID is not configured, it will be generated based on local MAC address.
+uuid=87654321-9abc-def0-1234-56789abc0000
+wps_pin_requests=/var/run/hostapd.pin-req
+device_name=Wireless AP
+manufacturer=Company
+model_name=WAP
+model_number=123
+serial_number=12345
+device_type=6-0050F204-1
+os_version=01020300
+config_methods=label display push_button keypad
+
+# if external Registrars are allowed, UPnP support could be added:
+#upnp_iface=br0
+#friendly_name=WPS Access Point
+
+
+External operations
+-------------------
+
+WPS requires either a device PIN code (usually, 8-digit number) or a
+pushbutton event (for PBC) to allow a new WPS Enrollee to join the
+network. hostapd uses the control interface as an input channel for
+these events.
+
+The PIN value used in the commands must be processed by an UI to
+remove non-digit characters and potentially, to verify the checksum
+digit. "hostapd_cli wps_check_pin <PIN>" can be used to do such
+processing. It returns FAIL if the PIN is invalid, or FAIL-CHECKSUM if
+the checksum digit is incorrect, or the processed PIN (non-digit
+characters removed) if the PIN is valid.
+
+When a client device (WPS Enrollee) connects to hostapd (WPS
+Registrar) in order to start PIN mode negotiation for WPS, an
+identifier (Enrollee UUID) is sent. hostapd will need to be configured
+with a device password (PIN) for this Enrollee. This is an operation
+that requires user interaction (assuming there are no pre-configured
+PINs on the AP for a set of Enrollee).
+
+The PIN request with information about the device is appended to the
+wps_pin_requests file (/var/run/hostapd.pin-req in this example). In
+addition, hostapd control interface event is sent as a notification of
+a new device. The AP could use, e.g., a web UI for showing active
+Enrollees to the user and request a PIN for an Enrollee.
+
+The PIN request file has one line for every Enrollee that connected to
+the AP, but for which there was no PIN. Following information is
+provided for each Enrollee (separated with tabulators):
+- timestamp (seconds from 1970-01-01)
+- Enrollee UUID
+- MAC address
+- Device name
+- Manufacturer
+- Model Name
+- Model Number
+- Serial Number
+- Device category
+
+Example line in the /var/run/hostapd.pin-req file:
+1200188391	53b63a98-d29e-4457-a2ed-094d7e6a669c	Intel(R) Centrino(R)	Intel Corporation	Intel(R) Centrino(R)	-	-	1-0050F204-1
+
+Control interface data:
+WPS-PIN-NEEDED [UUID-E|MAC Address|Device Name|Manufacturer|Model Name|Model Number|Serial Number|Device Category]
+For example:
+<2>WPS-PIN-NEEDED [53b63a98-d29e-4457-a2ed-094d7e6a669c|02:12:34:56:78:9a|Device|Manuf|Model|Model Number|Serial Number|1-0050F204-1]
+
+When the user enters a PIN for a pending Enrollee, e.g., on the web
+UI), hostapd needs to be notified of the new PIN over the control
+interface. This can be done either by using the UNIX domain socket
+-based control interface directly (src/common/wpa_ctrl.c provides
+helper functions for using the interface) or by calling hostapd_cli.
+
+Example command to add a PIN (12345670) for an Enrollee:
+
+hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c 12345670
+
+If the UUID-E is not available (e.g., Enrollee waits for the Registrar
+to be selected before connecting), wildcard UUID may be used to allow
+the PIN to be used once with any UUID:
+
+hostapd_cli wps_pin any 12345670
+
+To reduce likelihood of PIN being used with other devices or of
+forgetting an active PIN available for potential attackers, expiration
+time in seconds can be set for the new PIN (value 0 indicates no
+expiration):
+
+hostapd_cli wps_pin any 12345670 300
+
+If the MAC address of the enrollee is known, it should be configured
+to allow the AP to advertise list of authorized enrollees:
+
+hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c \
+	12345670 300 00:11:22:33:44:55
+
+
+After this, the Enrollee can connect to the AP again and complete WPS
+negotiation. At that point, a new, random WPA PSK is generated for the
+client device and the client can then use that key to connect to the
+AP to access the network.
+
+
+If the AP includes a pushbutton, WPS PBC mode can be used. It is
+enabled by pushing a button on both the AP and the client at about the
+same time (2 minute window). hostapd needs to be notified about the AP
+button pushed event over the control interface, e.g., by calling
+hostapd_cli:
+
+hostapd_cli wps_pbc
+
+At this point, the client has two minutes to complete WPS negotiation
+which will generate a new WPA PSK in the same way as the PIN method
+described above.
+
+
+When an external Registrar is used, the AP can act as an Enrollee and
+use its AP PIN. A static AP PIN (e.g., one one a label in the AP
+device) can be configured in hostapd.conf (ap_pin parameter). A more
+secure option is to use hostapd_cli wps_ap_pin command to enable the
+AP PIN only based on user action (and even better security by using a
+random AP PIN for each session, i.e., by using "wps_ap_pin random"
+command with a timeout value). Following commands are available for
+managing the dynamic AP PIN operations:
+
+hostapd_cli wps_ap_pin disable
+- disable AP PIN (i.e., do not allow external Registrars to use it to
+  learn the current AP settings or to reconfigure the AP)
+
+hostapd_cli wps_ap_pin random [timeout]
+- generate a random AP PIN and enable it
+- if the optional timeout parameter is given, the AP PIN will be enabled
+  for the specified number of seconds
+
+hostapd_cli wps_ap_pin get
+- fetch the current AP PIN
+
+hostapd_cli wps_ap_pin set <PIN> [timeout]
+- set the AP PIN and enable it
+- if the optional timeout parameter is given, the AP PIN will be enabled
+  for the specified number of seconds
+
+hostapd_cli get_config
+- display the current configuration
+
+hostapd_cli wps_config <new SSID> <auth> <encr> <new key>
+examples:
+  hostapd_cli wps_config testing WPA2PSK CCMP 12345678
+  hostapd_cli wps_config "no security" OPEN NONE ""
+
+<auth> must be one of the following: OPEN WPAPSK WPA2PSK
+<encr> must be one of the following: NONE WEP TKIP CCMP
+
+
+Credential generation and configuration changes
+-----------------------------------------------
+
+By default, hostapd generates credentials for Enrollees and processing
+AP configuration updates internally. However, it is possible to
+control these operations from external programs, if desired.
+
+The internal credential generation can be disabled with
+skip_cred_build=1 option in the configuration. extra_cred option will
+then need to be used to provide pre-configured Credential attribute(s)
+for hostapd to use. The exact data from this binary file will be sent,
+i.e., it will have to include valid WPS attributes. extra_cred can
+also be used to add additional networks if the Registrar is used to
+configure credentials for multiple networks.
+
+Processing of received configuration updates can be disabled with
+wps_cred_processing=1 option. When this is used, an external program
+is responsible for creating hostapd configuration files and processing
+configuration updates based on messages received from hostapd over
+control interface. This will also include the initial configuration on
+first successful registration if the AP is initially set in
+unconfigured state.
+
+Following control interface messages are sent out for external programs:
+
+WPS-REG-SUCCESS <Enrollee MAC address <UUID-E>
+For example:
+<2>WPS-REG-SUCCESS 02:66:a0:ee:17:27 2b7093f1-d6fb-5108-adbb-bea66bb87333
+
+This can be used to trigger change from unconfigured to configured
+state (random configuration based on the first successful WPS
+registration). In addition, this can be used to update AP UI about the
+status of WPS registration progress.
+
+
+WPS-NEW-AP-SETTINGS <hexdump of AP Setup attributes>
+For example:
+<2>WPS-NEW-AP-SETTINGS 10260001011045000c6a6b6d2d7770732d74657374100300020020100f00020008102700403065346230343536633236366665306433396164313535346131663462663731323433376163666462376633393965353466316631623032306164343438623510200006024231cede15101e000844
+
+This can be used to update the externally stored AP configuration and
+then update hostapd configuration (followed by restarting of hostapd).
+
+
+WPS with NFC
+------------
+
+WPS can be used with NFC-based configuration method. An NFC tag
+containing a password token from the Enrollee can be used to
+authenticate the connection instead of the PIN. In addition, an NFC tag
+with a configuration token can be used to transfer AP settings without
+going through the WPS protocol.
+
+When the AP acts as an Enrollee, a local NFC tag with a password token
+can be used by touching the NFC interface of an external Registrar. The
+wps_nfc_token command is used to manage use of the NFC password token
+from the AP. "wps_nfc_token enable" enables the use of the AP's NFC
+password token (in place of AP PIN) and "wps_nfc_token disable" disables
+the NFC password token.
+
+The NFC password token that is either pre-configured in the
+configuration file (wps_nfc_dev_pw_id, wps_nfc_dh_pubkey,
+wps_nfc_dh_privkey, wps_nfc_dev_pw) or generated dynamically with
+"wps_nfc_token <WPS|NDEF>" command. The nfc_pw_token tool from
+wpa_supplicant can be used to generate NFC password tokens during
+manufacturing (each AP needs to have its own random keys).
+
+The "wps_nfc_config_token <WPS/NDEF>" command can be used to build an
+NFC configuration token. The output value from this command is a hexdump
+of the current AP configuration (WPS parameter requests this to include
+only the WPS attributes; NDEF parameter requests additional NDEF
+encapsulation to be included). This data needs to be written to an NFC
+tag with an external program. Once written, the NFC configuration token
+can be used to touch an NFC interface on a station to provision the
+credentials needed to access the network.
+
+When the NFC device on the AP reads an NFC tag with a MIME media type
+"application/vnd.wfa.wsc", the NDEF message payload (with or without
+NDEF encapsulation) can be delivered to hostapd using the
+following hostapd_cli command:
+
+wps_nfc_tag_read <hexdump of payload>
+
+If the NFC tag contains a password token, the token is added to the
+internal Registrar. This allows station Enrollee from which the password
+token was received to run through WPS protocol to provision the
+credential.
+
+"nfc_get_handover_sel <NDEF> <WPS>" command can be used to build the
+contents of a Handover Select Message for connection handover when this
+does not depend on the contents of the Handover Request Message. 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_report_handover <INIT/RESP> WPS <carrier from handover request>
+<carrier from handover select>" is used to report completed NFC
+connection handover. The first parameter indicates whether the local
+device initiated or responded to the connection handover and the carrier
+records are the selected carrier from the handover request and select
+messages as a hexdump.
diff --git a/hostap/hostapd/android.config b/hostap/hostapd/android.config
new file mode 100644
index 0000000..0b1d7c7
--- /dev/null
+++ b/hostap/hostapd/android.config
@@ -0,0 +1,193 @@
+# Example hostapd build time configuration
+#
+# This file lists the configuration options that are used when building the
+# hostapd binary. All lines starting with # are ignored. Configuration option
+# lines must be commented out complete, if they are not to be included, i.e.,
+# just setting VARIABLE=n is not disabling that variable.
+#
+# This file is included in Makefile, so variables like CFLAGS and LIBS can also
+# be modified from here. In most cass, these lines should use += in order not
+# to override previous values of the variables.
+
+# Driver interface for Host AP driver
+#CONFIG_DRIVER_HOSTAP=y
+
+# Driver interface for wired authenticator
+#CONFIG_DRIVER_WIRED=y
+
+# Driver interface for drivers using the nl80211 kernel interface
+#CONFIG_DRIVER_NL80211=y
+# driver_nl80211.c requires a rather new libnl (version 1.1) which may not be
+# shipped with your distribution yet. If that is the case, you need to build
+# newer libnl version and point the hostapd build to use it.
+#LIBNL=/usr/src/libnl
+#CFLAGS += -I$(LIBNL)/include
+#LIBS += -L$(LIBNL)/lib
+CONFIG_LIBNL20=y
+
+# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
+#CONFIG_DRIVER_BSD=y
+#CFLAGS += -I/usr/local/include
+#LIBS += -L/usr/local/lib
+#LIBS_p += -L/usr/local/lib
+#LIBS_c += -L/usr/local/lib
+
+# Driver interface for no driver (e.g., RADIUS server only)
+#CONFIG_DRIVER_NONE=y
+
+# IEEE 802.11F/IAPP
+#CONFIG_IAPP=y
+
+# WPA2/IEEE 802.11i RSN pre-authentication
+#CONFIG_RSN_PREAUTH=y
+
+# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
+#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
+
+# Integrated EAP server
+#CONFIG_EAP=y
+
+# EAP-MD5 for the integrated EAP server
+#CONFIG_EAP_MD5=y
+
+# EAP-TLS for the integrated EAP server
+#CONFIG_EAP_TLS=y
+
+# EAP-MSCHAPv2 for the integrated EAP server
+#CONFIG_EAP_MSCHAPV2=y
+
+# EAP-PEAP for the integrated EAP server
+#CONFIG_EAP_PEAP=y
+
+# EAP-GTC for the integrated EAP server
+#CONFIG_EAP_GTC=y
+
+# EAP-TTLS for the integrated EAP server
+#CONFIG_EAP_TTLS=y
+
+# EAP-SIM for the integrated EAP server
+#CONFIG_EAP_SIM=y
+
+# EAP-AKA for the integrated EAP server
+#CONFIG_EAP_AKA=y
+
+# EAP-AKA' for the integrated EAP server
+# This requires CONFIG_EAP_AKA to be enabled, too.
+#CONFIG_EAP_AKA_PRIME=y
+
+# EAP-PAX for the integrated EAP server
+#CONFIG_EAP_PAX=y
+
+# EAP-PSK for the integrated EAP server (this is _not_ needed for WPA-PSK)
+#CONFIG_EAP_PSK=y
+
+# EAP-SAKE for the integrated EAP server
+#CONFIG_EAP_SAKE=y
+
+# EAP-GPSK for the integrated EAP server
+#CONFIG_EAP_GPSK=y
+# Include support for optional SHA256 cipher suite in EAP-GPSK
+#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.
+#CONFIG_EAP_FAST=y
+
+# Wi-Fi Protected Setup (WPS)
+CONFIG_WPS=y
+# Enable UPnP support for external WPS Registrars
+#CONFIG_WPS_UPNP=y
+
+# EAP-IKEv2
+#CONFIG_EAP_IKEV2=y
+
+# Trusted Network Connect (EAP-TNC)
+#CONFIG_EAP_TNC=y
+
+# PKCS#12 (PFX) support (used to read private key and certificate file from
+# a file that usually has extension .p12 or .pfx)
+CONFIG_PKCS12=y
+
+# RADIUS authentication server. This provides access to the integrated EAP
+# server from external hosts using RADIUS.
+#CONFIG_RADIUS_SERVER=y
+
+# Build IPv6 support for RADIUS operations
+CONFIG_IPV6=y
+
+# IEEE Std 802.11r-2008 (Fast BSS Transition)
+#CONFIG_IEEE80211R=y
+
+# Use the hostapd's IEEE 802.11 authentication (ACL), but without
+# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
+#CONFIG_DRIVER_RADIUS_ACL=y
+
+# IEEE 802.11n (High Throughput) support
+CONFIG_IEEE80211N=y
+
+# Remove debugging code that is printing out debug messages to stdout.
+# This can be used to reduce the size of the hostapd considerably if debugging
+# code is not needed.
+#CONFIG_NO_STDOUT_DEBUG=y
+
+# Add support for writing debug log to Android logcat instead of standard output
+CONFIG_ANDROID_LOG=y
+
+# Remove support for RADIUS accounting
+#CONFIG_NO_ACCOUNTING=y
+
+# Remove support for RADIUS
+CONFIG_NO_RADIUS=y
+
+# Remove support for VLANs
+#CONFIG_NO_VLAN=y
+
+# 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
+
+# Select wrapper for operatins system and C library specific functions
+# unix = UNIX/POSIX like systems (default)
+# win32 = Windows systems
+# none = Empty template
+CONFIG_OS=unix
+
+# Enable tracing code for developer debugging
+# This tracks use of memory allocations and other registrations and reports
+# incorrect use with a backtrace of call (or allocation) location.
+#CONFIG_WPA_TRACE=y
+# For BSD, comment out these.
+#LIBS += -lexecinfo
+#LIBS_p += -lexecinfo
+#LIBS_c += -lexecinfo
+
+# Use libbfd to get more details for developer debugging
+# This enables use of libbfd to get more detailed symbols for the backtraces
+# generated by CONFIG_WPA_TRACE=y.
+#CONFIG_WPA_TRACE_BFD=y
+# For BSD, comment out these.
+#LIBS += -lbfd -liberty -lz
+#LIBS_p += -lbfd -liberty -lz
+#LIBS_c += -lbfd -liberty -lz
+
+# 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
+
+# Enable AP
+CONFIG_AP=y
+
+# Enable Fast Session Transfer (FST)
+#CONFIG_FST=y
diff --git a/hostap/hostapd/config_file.c b/hostap/hostapd/config_file.c
new file mode 100644
index 0000000..82ac61d
--- /dev/null
+++ b/hostap/hostapd/config_file.c
@@ -0,0 +1,3536 @@
+/*
+ * hostapd / Configuration file parser
+ * Copyright (c) 2003-2015, 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"
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <grp.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#include "utils/common.h"
+#include "utils/uuid.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "eap_server/eap.h"
+#include "radius/radius_client.h"
+#include "ap/wpa_auth.h"
+#include "ap/ap_config.h"
+#include "config_file.h"
+
+
+#ifndef CONFIG_NO_RADIUS
+#ifdef EAP_SERVER
+static struct hostapd_radius_attr *
+hostapd_parse_radius_attr(const char *value);
+#endif /* EAP_SERVER */
+#endif /* CONFIG_NO_RADIUS */
+
+
+#ifndef CONFIG_NO_VLAN
+static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss,
+					 const char *fname)
+{
+	FILE *f;
+	char buf[128], *pos, *pos2;
+	int line = 0, vlan_id;
+	struct hostapd_vlan *vlan;
+
+	f = fopen(fname, "r");
+	if (!f) {
+		wpa_printf(MSG_ERROR, "VLAN file '%s' not readable.", fname);
+		return -1;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		line++;
+
+		if (buf[0] == '#')
+			continue;
+		pos = buf;
+		while (*pos != '\0') {
+			if (*pos == '\n') {
+				*pos = '\0';
+				break;
+			}
+			pos++;
+		}
+		if (buf[0] == '\0')
+			continue;
+
+		if (buf[0] == '*') {
+			vlan_id = VLAN_ID_WILDCARD;
+			pos = buf + 1;
+		} else {
+			vlan_id = strtol(buf, &pos, 10);
+			if (buf == pos || vlan_id < 1 ||
+			    vlan_id > MAX_VLAN_ID) {
+				wpa_printf(MSG_ERROR, "Invalid VLAN ID at "
+					   "line %d in '%s'", line, fname);
+				fclose(f);
+				return -1;
+			}
+		}
+
+		while (*pos == ' ' || *pos == '\t')
+			pos++;
+		pos2 = pos;
+		while (*pos2 != ' ' && *pos2 != '\t' && *pos2 != '\0')
+			pos2++;
+		*pos2 = '\0';
+		if (*pos == '\0' || os_strlen(pos) > IFNAMSIZ) {
+			wpa_printf(MSG_ERROR, "Invalid VLAN ifname at line %d "
+				   "in '%s'", line, fname);
+			fclose(f);
+			return -1;
+		}
+
+		vlan = os_zalloc(sizeof(*vlan));
+		if (vlan == NULL) {
+			wpa_printf(MSG_ERROR, "Out of memory while reading "
+				   "VLAN interfaces from '%s'", fname);
+			fclose(f);
+			return -1;
+		}
+
+		vlan->vlan_id = vlan_id;
+		os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname));
+		vlan->next = bss->vlan;
+		bss->vlan = vlan;
+	}
+
+	fclose(f);
+
+	return 0;
+}
+#endif /* CONFIG_NO_VLAN */
+
+
+static int hostapd_acl_comp(const void *a, const void *b)
+{
+	const struct mac_acl_entry *aa = a;
+	const struct mac_acl_entry *bb = b;
+	return os_memcmp(aa->addr, bb->addr, sizeof(macaddr));
+}
+
+
+static int hostapd_config_read_maclist(const char *fname,
+				       struct mac_acl_entry **acl, int *num)
+{
+	FILE *f;
+	char buf[128], *pos;
+	int line = 0;
+	u8 addr[ETH_ALEN];
+	struct mac_acl_entry *newacl;
+	int vlan_id;
+
+	if (!fname)
+		return 0;
+
+	f = fopen(fname, "r");
+	if (!f) {
+		wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", fname);
+		return -1;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		int i, rem = 0;
+
+		line++;
+
+		if (buf[0] == '#')
+			continue;
+		pos = buf;
+		while (*pos != '\0') {
+			if (*pos == '\n') {
+				*pos = '\0';
+				break;
+			}
+			pos++;
+		}
+		if (buf[0] == '\0')
+			continue;
+		pos = buf;
+		if (buf[0] == '-') {
+			rem = 1;
+			pos++;
+		}
+
+		if (hwaddr_aton(pos, addr)) {
+			wpa_printf(MSG_ERROR, "Invalid MAC address '%s' at "
+				   "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')
+			pos++;
+		while (*pos == ' ' || *pos == '\t')
+			pos++;
+		if (*pos != '\0')
+			vlan_id = atoi(pos);
+
+		newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
+		if (newacl == NULL) {
+			wpa_printf(MSG_ERROR, "MAC list reallocation failed");
+			fclose(f);
+			return -1;
+		}
+
+		*acl = newacl;
+		os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
+		(*acl)[*num].vlan_id = vlan_id;
+		(*num)++;
+	}
+
+	fclose(f);
+
+	qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
+
+	return 0;
+}
+
+
+#ifdef EAP_SERVER
+static int hostapd_config_read_eap_user(const char *fname,
+					struct hostapd_bss_config *conf)
+{
+	FILE *f;
+	char buf[512], *pos, *start, *pos2;
+	int line = 0, ret = 0, num_methods;
+	struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL;
+
+	if (!fname)
+		return 0;
+
+	if (os_strncmp(fname, "sqlite:", 7) == 0) {
+#ifdef CONFIG_SQLITE
+		os_free(conf->eap_user_sqlite);
+		conf->eap_user_sqlite = os_strdup(fname + 7);
+		return 0;
+#else /* CONFIG_SQLITE */
+		wpa_printf(MSG_ERROR,
+			   "EAP user file in SQLite DB, but CONFIG_SQLITE was not enabled in the build.");
+		return -1;
+#endif /* CONFIG_SQLITE */
+	}
+
+	f = fopen(fname, "r");
+	if (!f) {
+		wpa_printf(MSG_ERROR, "EAP user file '%s' not found.", fname);
+		return -1;
+	}
+
+	/* Lines: "user" METHOD,METHOD2 "password" (password optional) */
+	while (fgets(buf, sizeof(buf), f)) {
+		line++;
+
+		if (buf[0] == '#')
+			continue;
+		pos = buf;
+		while (*pos != '\0') {
+			if (*pos == '\n') {
+				*pos = '\0';
+				break;
+			}
+			pos++;
+		}
+		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] != '*') {
+			wpa_printf(MSG_ERROR, "Invalid EAP identity (no \" in "
+				   "start) on line %d in '%s'", line, fname);
+			goto failed;
+		}
+
+		user = os_zalloc(sizeof(*user));
+		if (user == NULL) {
+			wpa_printf(MSG_ERROR, "EAP user allocation failed");
+			goto failed;
+		}
+		user->force_version = -1;
+
+		if (buf[0] == '*') {
+			pos = buf;
+		} else {
+			pos = buf + 1;
+			start = pos;
+			while (*pos != '"' && *pos != '\0')
+				pos++;
+			if (*pos == '\0') {
+				wpa_printf(MSG_ERROR, "Invalid EAP identity "
+					   "(no \" in end) on line %d in '%s'",
+					   line, fname);
+				goto failed;
+			}
+
+			user->identity = os_malloc(pos - start);
+			if (user->identity == NULL) {
+				wpa_printf(MSG_ERROR, "Failed to allocate "
+					   "memory for EAP identity");
+				goto failed;
+			}
+			os_memcpy(user->identity, start, pos - start);
+			user->identity_len = pos - start;
+
+			if (pos[0] == '"' && pos[1] == '*') {
+				user->wildcard_prefix = 1;
+				pos++;
+			}
+		}
+		pos++;
+		while (*pos == ' ' || *pos == '\t')
+			pos++;
+
+		if (*pos == '\0') {
+			wpa_printf(MSG_ERROR, "No EAP method on line %d in "
+				   "'%s'", line, fname);
+			goto failed;
+		}
+
+		start = pos;
+		while (*pos != ' ' && *pos != '\t' && *pos != '\0')
+			pos++;
+		if (*pos == '\0') {
+			pos = NULL;
+		} else {
+			*pos = '\0';
+			pos++;
+		}
+		num_methods = 0;
+		while (*start) {
+			char *pos3 = os_strchr(start, ',');
+			if (pos3) {
+				*pos3++ = '\0';
+			}
+			user->methods[num_methods].method =
+				eap_server_get_type(
+					start,
+					&user->methods[num_methods].vendor);
+			if (user->methods[num_methods].vendor ==
+			    EAP_VENDOR_IETF &&
+			    user->methods[num_methods].method == EAP_TYPE_NONE)
+			{
+				if (os_strcmp(start, "TTLS-PAP") == 0) {
+					user->ttls_auth |= EAP_TTLS_AUTH_PAP;
+					goto skip_eap;
+				}
+				if (os_strcmp(start, "TTLS-CHAP") == 0) {
+					user->ttls_auth |= EAP_TTLS_AUTH_CHAP;
+					goto skip_eap;
+				}
+				if (os_strcmp(start, "TTLS-MSCHAP") == 0) {
+					user->ttls_auth |=
+						EAP_TTLS_AUTH_MSCHAP;
+					goto skip_eap;
+				}
+				if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) {
+					user->ttls_auth |=
+						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);
+				goto failed;
+			}
+
+			num_methods++;
+			if (num_methods >= EAP_MAX_METHODS)
+				break;
+		skip_eap:
+			if (pos3 == NULL)
+				break;
+			start = pos3;
+		}
+		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;
+		}
+
+		if (pos == NULL)
+			goto done;
+
+		while (*pos == ' ' || *pos == '\t')
+			pos++;
+		if (*pos == '\0')
+			goto done;
+
+		if (os_strncmp(pos, "[ver=0]", 7) == 0) {
+			user->force_version = 0;
+			goto done;
+		}
+
+		if (os_strncmp(pos, "[ver=1]", 7) == 0) {
+			user->force_version = 1;
+			goto done;
+		}
+
+		if (os_strncmp(pos, "[2]", 3) == 0) {
+			user->phase2 = 1;
+			goto done;
+		}
+
+		if (*pos == '"') {
+			pos++;
+			start = pos;
+			while (*pos != '"' && *pos != '\0')
+				pos++;
+			if (*pos == '\0') {
+				wpa_printf(MSG_ERROR, "Invalid EAP password "
+					   "(no \" in end) on line %d in '%s'",
+					   line, fname);
+				goto failed;
+			}
+
+			user->password = os_malloc(pos - start);
+			if (user->password == NULL) {
+				wpa_printf(MSG_ERROR, "Failed to allocate "
+					   "memory for EAP password");
+				goto failed;
+			}
+			os_memcpy(user->password, start, pos - start);
+			user->password_len = pos - start;
+
+			pos++;
+		} else if (os_strncmp(pos, "hash:", 5) == 0) {
+			pos += 5;
+			pos2 = pos;
+			while (*pos2 != '\0' && *pos2 != ' ' &&
+			       *pos2 != '\t' && *pos2 != '#')
+				pos2++;
+			if (pos2 - pos != 32) {
+				wpa_printf(MSG_ERROR, "Invalid password hash "
+					   "on line %d in '%s'", line, fname);
+				goto failed;
+			}
+			user->password = os_malloc(16);
+			if (user->password == NULL) {
+				wpa_printf(MSG_ERROR, "Failed to allocate "
+					   "memory for EAP password hash");
+				goto failed;
+			}
+			if (hexstr2bin(pos, user->password, 16) < 0) {
+				wpa_printf(MSG_ERROR, "Invalid hash password "
+					   "on line %d in '%s'", line, fname);
+				goto failed;
+			}
+			user->password_len = 16;
+			user->password_hash = 1;
+			pos = pos2;
+		} else {
+			pos2 = pos;
+			while (*pos2 != '\0' && *pos2 != ' ' &&
+			       *pos2 != '\t' && *pos2 != '#')
+				pos2++;
+			if ((pos2 - pos) & 1) {
+				wpa_printf(MSG_ERROR, "Invalid hex password "
+					   "on line %d in '%s'", line, fname);
+				goto failed;
+			}
+			user->password = os_malloc((pos2 - pos) / 2);
+			if (user->password == NULL) {
+				wpa_printf(MSG_ERROR, "Failed to allocate "
+					   "memory for EAP password");
+				goto failed;
+			}
+			if (hexstr2bin(pos, user->password,
+				       (pos2 - pos) / 2) < 0) {
+				wpa_printf(MSG_ERROR, "Invalid hex password "
+					   "on line %d in '%s'", line, fname);
+				goto failed;
+			}
+			user->password_len = (pos2 - pos) / 2;
+			pos = pos2;
+		}
+
+		while (*pos == ' ' || *pos == '\t')
+			pos++;
+		if (os_strncmp(pos, "[2]", 3) == 0) {
+			user->phase2 = 1;
+		}
+
+	done:
+		if (tail == NULL) {
+			tail = new_user = user;
+		} else {
+			tail->next = user;
+			tail = user;
+		}
+		continue;
+
+	failed:
+		if (user)
+			hostapd_config_free_eap_user(user);
+		ret = -1;
+		break;
+	}
+
+	fclose(f);
+
+	if (ret == 0) {
+		user = conf->eap_user;
+		while (user) {
+			struct hostapd_eap_user *prev;
+
+			prev = user;
+			user = user->next;
+			hostapd_config_free_eap_user(prev);
+		}
+		conf->eap_user = new_user;
+	}
+
+	return ret;
+}
+#endif /* EAP_SERVER */
+
+
+#ifndef CONFIG_NO_RADIUS
+static int
+hostapd_config_read_radius_addr(struct hostapd_radius_server **server,
+				int *num_server, const char *val, int def_port,
+				struct hostapd_radius_server **curr_serv)
+{
+	struct hostapd_radius_server *nserv;
+	int ret;
+	static int server_index = 1;
+
+	nserv = os_realloc_array(*server, *num_server + 1, sizeof(*nserv));
+	if (nserv == NULL)
+		return -1;
+
+	*server = nserv;
+	nserv = &nserv[*num_server];
+	(*num_server)++;
+	(*curr_serv) = nserv;
+
+	os_memset(nserv, 0, sizeof(*nserv));
+	nserv->port = def_port;
+	ret = hostapd_parse_ip_addr(val, &nserv->addr);
+	nserv->index = server_index++;
+
+	return ret;
+}
+
+
+static struct hostapd_radius_attr *
+hostapd_parse_radius_attr(const char *value)
+{
+	const char *pos;
+	char syntax;
+	struct hostapd_radius_attr *attr;
+	size_t len;
+
+	attr = os_zalloc(sizeof(*attr));
+	if (attr == NULL)
+		return NULL;
+
+	attr->type = atoi(value);
+
+	pos = os_strchr(value, ':');
+	if (pos == NULL) {
+		attr->val = wpabuf_alloc(1);
+		if (attr->val == NULL) {
+			os_free(attr);
+			return NULL;
+		}
+		wpabuf_put_u8(attr->val, 0);
+		return attr;
+	}
+
+	pos++;
+	if (pos[0] == '\0' || pos[1] != ':') {
+		os_free(attr);
+		return NULL;
+	}
+	syntax = *pos++;
+	pos++;
+
+	switch (syntax) {
+	case 's':
+		attr->val = wpabuf_alloc_copy(pos, os_strlen(pos));
+		break;
+	case 'x':
+		len = os_strlen(pos);
+		if (len & 1)
+			break;
+		len /= 2;
+		attr->val = wpabuf_alloc(len);
+		if (attr->val == NULL)
+			break;
+		if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) {
+			wpabuf_free(attr->val);
+			os_free(attr);
+			return NULL;
+		}
+		break;
+	case 'd':
+		attr->val = wpabuf_alloc(4);
+		if (attr->val)
+			wpabuf_put_be32(attr->val, atoi(pos));
+		break;
+	default:
+		os_free(attr);
+		return NULL;
+	}
+
+	if (attr->val == NULL) {
+		os_free(attr);
+		return NULL;
+	}
+
+	return attr;
+}
+
+
+static int hostapd_parse_das_client(struct hostapd_bss_config *bss,
+				    const char *val)
+{
+	char *secret;
+
+	secret = os_strchr(val, ' ');
+	if (secret == NULL)
+		return -1;
+
+	secret++;
+
+	if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr))
+		return -1;
+
+	os_free(bss->radius_das_shared_secret);
+	bss->radius_das_shared_secret = (u8 *) os_strdup(secret);
+	if (bss->radius_das_shared_secret == NULL)
+		return -1;
+	bss->radius_das_shared_secret_len = os_strlen(secret);
+
+	return 0;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+static int hostapd_config_parse_key_mgmt(int line, const char *value)
+{
+	int val = 0, last;
+	char *start, *end, *buf;
+
+	buf = os_strdup(value);
+	if (buf == NULL)
+		return -1;
+	start = buf;
+
+	while (*start != '\0') {
+		while (*start == ' ' || *start == '\t')
+			start++;
+		if (*start == '\0')
+			break;
+		end = start;
+		while (*end != ' ' && *end != '\t' && *end != '\0')
+			end++;
+		last = *end == '\0';
+		*end = '\0';
+		if (os_strcmp(start, "WPA-PSK") == 0)
+			val |= WPA_KEY_MGMT_PSK;
+		else if (os_strcmp(start, "WPA-EAP") == 0)
+			val |= WPA_KEY_MGMT_IEEE8021X;
+#ifdef CONFIG_IEEE80211R
+		else if (os_strcmp(start, "FT-PSK") == 0)
+			val |= WPA_KEY_MGMT_FT_PSK;
+		else if (os_strcmp(start, "FT-EAP") == 0)
+			val |= WPA_KEY_MGMT_FT_IEEE8021X;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+		else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
+			val |= WPA_KEY_MGMT_PSK_SHA256;
+		else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
+			val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+		else if (os_strcmp(start, "SAE") == 0)
+			val |= WPA_KEY_MGMT_SAE;
+		else if (os_strcmp(start, "FT-SAE") == 0)
+			val |= WPA_KEY_MGMT_FT_SAE;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_SUITEB
+		else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0)
+			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+		else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
+			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+#endif /* CONFIG_SUITEB192 */
+		else {
+			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
+				   line, start);
+			os_free(buf);
+			return -1;
+		}
+
+		if (last)
+			break;
+		start = end + 1;
+	}
+
+	os_free(buf);
+	if (val == 0) {
+		wpa_printf(MSG_ERROR, "Line %d: no key_mgmt values "
+			   "configured.", line);
+		return -1;
+	}
+
+	return val;
+}
+
+
+static int hostapd_config_parse_cipher(int line, const char *value)
+{
+	int val = wpa_parse_cipher(value);
+	if (val < 0) {
+		wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
+			   line, value);
+		return -1;
+	}
+	if (val == 0) {
+		wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.",
+			   line);
+		return -1;
+	}
+	return val;
+}
+
+
+static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx,
+				   char *val)
+{
+	size_t len = os_strlen(val);
+
+	if (keyidx < 0 || keyidx > 3 || wep->key[keyidx] != NULL)
+		return -1;
+
+	if (val[0] == '"') {
+		if (len < 2 || val[len - 1] != '"')
+			return -1;
+		len -= 2;
+		wep->key[keyidx] = os_malloc(len);
+		if (wep->key[keyidx] == NULL)
+			return -1;
+		os_memcpy(wep->key[keyidx], val + 1, len);
+		wep->len[keyidx] = len;
+	} else {
+		if (len & 1)
+			return -1;
+		len /= 2;
+		wep->key[keyidx] = os_malloc(len);
+		if (wep->key[keyidx] == NULL)
+			return -1;
+		wep->len[keyidx] = len;
+		if (hexstr2bin(val, wep->key[keyidx], len) < 0)
+			return -1;
+	}
+
+	wep->keys_set++;
+
+	return 0;
+}
+
+
+static int hostapd_parse_chanlist(struct hostapd_config *conf, char *val)
+{
+	char *pos;
+
+	/* for backwards compatibility, translate ' ' in conf str to ',' */
+	pos = val;
+	while (pos) {
+		pos = os_strchr(pos, ' ');
+		if (pos)
+			*pos++ = ',';
+	}
+	if (freq_range_list_parse(&conf->acs_ch_list, val))
+		return -1;
+
+	return 0;
+}
+
+
+static int hostapd_parse_intlist(int **int_list, char *val)
+{
+	int *list;
+	int count;
+	char *pos, *end;
+
+	os_free(*int_list);
+	*int_list = NULL;
+
+	pos = val;
+	count = 0;
+	while (*pos != '\0') {
+		if (*pos == ' ')
+			count++;
+		pos++;
+	}
+
+	list = os_malloc(sizeof(int) * (count + 2));
+	if (list == NULL)
+		return -1;
+	pos = val;
+	count = 0;
+	while (*pos != '\0') {
+		end = os_strchr(pos, ' ');
+		if (end)
+			*end = '\0';
+
+		list[count++] = atoi(pos);
+		if (!end)
+			break;
+		pos = end + 1;
+	}
+	list[count] = -1;
+
+	*int_list = list;
+	return 0;
+}
+
+
+static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname)
+{
+	struct hostapd_bss_config **all, *bss;
+
+	if (*ifname == '\0')
+		return -1;
+
+	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 = all;
+
+	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->bss[conf->num_bss++] = bss;
+	conf->last_bss = bss;
+
+	hostapd_config_defaults_bss(bss);
+	os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
+	os_memcpy(bss->ssid.vlan, bss->iface, IFNAMSIZ + 1);
+
+	return 0;
+}
+
+
+/* convert floats with one decimal place to value*10 int, i.e.,
+ * "1.5" will return 15 */
+static int hostapd_config_read_int10(const char *value)
+{
+	int i, d;
+	char *pos;
+
+	i = atoi(value);
+	pos = os_strchr(value, '.');
+	d = 0;
+	if (pos) {
+		pos++;
+		if (*pos >= '0' && *pos <= '9')
+			d = *pos - '0';
+	}
+
+	return i * 10 + d;
+}
+
+
+static int valid_cw(int cw)
+{
+	return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 ||
+		cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023 ||
+		cw == 2047 || cw == 4095 || cw == 8191 || cw == 16383 ||
+		cw == 32767);
+}
+
+
+enum {
+	IEEE80211_TX_QUEUE_DATA0 = 0, /* used for EDCA AC_VO data */
+	IEEE80211_TX_QUEUE_DATA1 = 1, /* used for EDCA AC_VI data */
+	IEEE80211_TX_QUEUE_DATA2 = 2, /* used for EDCA AC_BE data */
+	IEEE80211_TX_QUEUE_DATA3 = 3 /* used for EDCA AC_BK data */
+};
+
+static int hostapd_config_tx_queue(struct hostapd_config *conf,
+				   const char *name, const char *val)
+{
+	int num;
+	const char *pos;
+	struct hostapd_tx_queue_params *queue;
+
+	/* skip 'tx_queue_' prefix */
+	pos = name + 9;
+	if (os_strncmp(pos, "data", 4) == 0 &&
+	    pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') {
+		num = pos[4] - '0';
+		pos += 6;
+	} else if (os_strncmp(pos, "after_beacon_", 13) == 0 ||
+		   os_strncmp(pos, "beacon_", 7) == 0) {
+		wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
+		return 0;
+	} else {
+		wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos);
+		return -1;
+	}
+
+	if (num >= NUM_TX_QUEUES) {
+		/* for backwards compatibility, do not trigger failure */
+		wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
+		return 0;
+	}
+
+	queue = &conf->tx_queue[num];
+
+	if (os_strcmp(pos, "aifs") == 0) {
+		queue->aifs = atoi(val);
+		if (queue->aifs < 0 || queue->aifs > 255) {
+			wpa_printf(MSG_ERROR, "Invalid AIFS value %d",
+				   queue->aifs);
+			return -1;
+		}
+	} else if (os_strcmp(pos, "cwmin") == 0) {
+		queue->cwmin = atoi(val);
+		if (!valid_cw(queue->cwmin)) {
+			wpa_printf(MSG_ERROR, "Invalid cwMin value %d",
+				   queue->cwmin);
+			return -1;
+		}
+	} else if (os_strcmp(pos, "cwmax") == 0) {
+		queue->cwmax = atoi(val);
+		if (!valid_cw(queue->cwmax)) {
+			wpa_printf(MSG_ERROR, "Invalid cwMax value %d",
+				   queue->cwmax);
+			return -1;
+		}
+	} else if (os_strcmp(pos, "burst") == 0) {
+		queue->burst = hostapd_config_read_int10(val);
+	} else {
+		wpa_printf(MSG_ERROR, "Unknown tx_queue field '%s'", pos);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static int add_r0kh(struct hostapd_bss_config *bss, char *value)
+{
+	struct ft_remote_r0kh *r0kh;
+	char *pos, *next;
+
+	r0kh = os_zalloc(sizeof(*r0kh));
+	if (r0kh == NULL)
+		return -1;
+
+	/* 02:01:02:03:04:05 a.example.com 000102030405060708090a0b0c0d0e0f */
+	pos = value;
+	next = os_strchr(pos, ' ');
+	if (next)
+		*next++ = '\0';
+	if (next == NULL || hwaddr_aton(pos, r0kh->addr)) {
+		wpa_printf(MSG_ERROR, "Invalid R0KH MAC address: '%s'", pos);
+		os_free(r0kh);
+		return -1;
+	}
+
+	pos = next;
+	next = os_strchr(pos, ' ');
+	if (next)
+		*next++ = '\0';
+	if (next == NULL || next - pos > FT_R0KH_ID_MAX_LEN) {
+		wpa_printf(MSG_ERROR, "Invalid R0KH-ID: '%s'", pos);
+		os_free(r0kh);
+		return -1;
+	}
+	r0kh->id_len = next - pos - 1;
+	os_memcpy(r0kh->id, pos, r0kh->id_len);
+
+	pos = next;
+	if (hexstr2bin(pos, r0kh->key, sizeof(r0kh->key))) {
+		wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos);
+		os_free(r0kh);
+		return -1;
+	}
+
+	r0kh->next = bss->r0kh_list;
+	bss->r0kh_list = r0kh;
+
+	return 0;
+}
+
+
+static int add_r1kh(struct hostapd_bss_config *bss, char *value)
+{
+	struct ft_remote_r1kh *r1kh;
+	char *pos, *next;
+
+	r1kh = os_zalloc(sizeof(*r1kh));
+	if (r1kh == NULL)
+		return -1;
+
+	/* 02:01:02:03:04:05 02:01:02:03:04:05
+	 * 000102030405060708090a0b0c0d0e0f */
+	pos = value;
+	next = os_strchr(pos, ' ');
+	if (next)
+		*next++ = '\0';
+	if (next == NULL || hwaddr_aton(pos, r1kh->addr)) {
+		wpa_printf(MSG_ERROR, "Invalid R1KH MAC address: '%s'", pos);
+		os_free(r1kh);
+		return -1;
+	}
+
+	pos = next;
+	next = os_strchr(pos, ' ');
+	if (next)
+		*next++ = '\0';
+	if (next == NULL || hwaddr_aton(pos, r1kh->id)) {
+		wpa_printf(MSG_ERROR, "Invalid R1KH-ID: '%s'", pos);
+		os_free(r1kh);
+		return -1;
+	}
+
+	pos = next;
+	if (hexstr2bin(pos, r1kh->key, sizeof(r1kh->key))) {
+		wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos);
+		os_free(r1kh);
+		return -1;
+	}
+
+	r1kh->next = bss->r1kh_list;
+	bss->r1kh_list = r1kh;
+
+	return 0;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+#ifdef CONFIG_IEEE80211N
+static int hostapd_config_ht_capab(struct hostapd_config *conf,
+				   const char *capab)
+{
+	if (os_strstr(capab, "[LDPC]"))
+		conf->ht_capab |= HT_CAP_INFO_LDPC_CODING_CAP;
+	if (os_strstr(capab, "[HT40-]")) {
+		conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+		conf->secondary_channel = -1;
+	}
+	if (os_strstr(capab, "[HT40+]")) {
+		conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+		conf->secondary_channel = 1;
+	}
+	if (os_strstr(capab, "[SMPS-STATIC]")) {
+		conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK;
+		conf->ht_capab |= HT_CAP_INFO_SMPS_STATIC;
+	}
+	if (os_strstr(capab, "[SMPS-DYNAMIC]")) {
+		conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK;
+		conf->ht_capab |= HT_CAP_INFO_SMPS_DYNAMIC;
+	}
+	if (os_strstr(capab, "[GF]"))
+		conf->ht_capab |= HT_CAP_INFO_GREEN_FIELD;
+	if (os_strstr(capab, "[SHORT-GI-20]"))
+		conf->ht_capab |= HT_CAP_INFO_SHORT_GI20MHZ;
+	if (os_strstr(capab, "[SHORT-GI-40]"))
+		conf->ht_capab |= HT_CAP_INFO_SHORT_GI40MHZ;
+	if (os_strstr(capab, "[TX-STBC]"))
+		conf->ht_capab |= HT_CAP_INFO_TX_STBC;
+	if (os_strstr(capab, "[RX-STBC1]")) {
+		conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK;
+		conf->ht_capab |= HT_CAP_INFO_RX_STBC_1;
+	}
+	if (os_strstr(capab, "[RX-STBC12]")) {
+		conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK;
+		conf->ht_capab |= HT_CAP_INFO_RX_STBC_12;
+	}
+	if (os_strstr(capab, "[RX-STBC123]")) {
+		conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK;
+		conf->ht_capab |= HT_CAP_INFO_RX_STBC_123;
+	}
+	if (os_strstr(capab, "[DELAYED-BA]"))
+		conf->ht_capab |= HT_CAP_INFO_DELAYED_BA;
+	if (os_strstr(capab, "[MAX-AMSDU-7935]"))
+		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, "[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;
+
+	return 0;
+}
+#endif /* CONFIG_IEEE80211N */
+
+
+#ifdef CONFIG_IEEE80211AC
+static int hostapd_config_vht_capab(struct hostapd_config *conf,
+				    const char *capab)
+{
+	if (os_strstr(capab, "[MAX-MPDU-7991]"))
+		conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_7991;
+	if (os_strstr(capab, "[MAX-MPDU-11454]"))
+		conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_11454;
+	if (os_strstr(capab, "[VHT160]"))
+		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, "[RXLDPC]"))
+		conf->vht_capab |= VHT_CAP_RXLDPC;
+	if (os_strstr(capab, "[SHORT-GI-80]"))
+		conf->vht_capab |= VHT_CAP_SHORT_GI_80;
+	if (os_strstr(capab, "[SHORT-GI-160]"))
+		conf->vht_capab |= VHT_CAP_SHORT_GI_160;
+	if (os_strstr(capab, "[TX-STBC-2BY1]"))
+		conf->vht_capab |= VHT_CAP_TXSTBC;
+	if (os_strstr(capab, "[RX-STBC-1]"))
+		conf->vht_capab |= VHT_CAP_RXSTBC_1;
+	if (os_strstr(capab, "[RX-STBC-12]"))
+		conf->vht_capab |= VHT_CAP_RXSTBC_2;
+	if (os_strstr(capab, "[RX-STBC-123]"))
+		conf->vht_capab |= VHT_CAP_RXSTBC_3;
+	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_SU_BEAMFORMER_CAPABLE;
+	if (os_strstr(capab, "[SU-BEAMFORMEE]"))
+		conf->vht_capab |= VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+	if (os_strstr(capab, "[BF-ANTENNA-2]") &&
+	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+		conf->vht_capab |= (1 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+	if (os_strstr(capab, "[BF-ANTENNA-3]") &&
+	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+		conf->vht_capab |= (2 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+	if (os_strstr(capab, "[BF-ANTENNA-4]") &&
+	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+		conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+	if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
+	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+		conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
+	if (os_strstr(capab, "[SOUNDING-DIMENSION-3]") &&
+	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+		conf->vht_capab |= (2 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
+	if (os_strstr(capab, "[SOUNDING-DIMENSION-4]") &&
+	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+		conf->vht_capab |= (3 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
+	if (os_strstr(capab, "[MU-BEAMFORMER]"))
+		conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE;
+	if (os_strstr(capab, "[VHT-TXOP-PS]"))
+		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-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;
+	if (os_strstr(capab, "[VHT-LINK-ADAPT3]") &&
+	    (conf->vht_capab & VHT_CAP_HTC_VHT))
+		conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
+	if (os_strstr(capab, "[RX-ANTENNA-PATTERN]"))
+		conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN;
+	if (os_strstr(capab, "[TX-ANTENNA-PATTERN]"))
+		conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN;
+	return 0;
+}
+#endif /* CONFIG_IEEE80211AC */
+
+
+#ifdef CONFIG_INTERWORKING
+static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos,
+				    int line)
+{
+	size_t len = os_strlen(pos);
+	u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
+
+	struct hostapd_roaming_consortium *rc;
+
+	if ((len & 1) || len < 2 * 3 || len / 2 > MAX_ROAMING_CONSORTIUM_LEN ||
+	    hexstr2bin(pos, oi, len / 2)) {
+		wpa_printf(MSG_ERROR, "Line %d: invalid roaming_consortium "
+			   "'%s'", line, pos);
+		return -1;
+	}
+	len /= 2;
+
+	rc = os_realloc_array(bss->roaming_consortium,
+			      bss->roaming_consortium_count + 1,
+			      sizeof(struct hostapd_roaming_consortium));
+	if (rc == NULL)
+		return -1;
+
+	os_memcpy(rc[bss->roaming_consortium_count].oi, oi, len);
+	rc[bss->roaming_consortium_count].len = len;
+
+	bss->roaming_consortium = rc;
+	bss->roaming_consortium_count++;
+
+	return 0;
+}
+
+
+static int parse_lang_string(struct hostapd_lang_string **array,
+			     unsigned int *count, char *pos)
+{
+	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)
+		goto fail;
+	*sep++ = '\0';
+
+	clen = os_strlen(pos);
+	if (clen < 2 || clen > sizeof(ls->lang))
+		goto fail;
+	nlen = os_strlen(sep);
+	if (nlen > 252)
+		goto fail;
+
+	ls = os_realloc_array(*array, *count + 1,
+			      sizeof(struct hostapd_lang_string));
+	if (ls == NULL)
+		goto fail;
+
+	*array = ls;
+	ls = &(*array)[*count];
+	(*count)++;
+
+	os_memset(ls->lang, 0, sizeof(ls->lang));
+	os_memcpy(ls->lang, pos, clen);
+	ls->name_len = nlen;
+	os_memcpy(ls->name, sep, nlen);
+
+	ret = 0;
+fail:
+	os_free(str);
+	return ret;
+}
+
+
+static int parse_venue_name(struct hostapd_bss_config *bss, char *pos,
+			    int line)
+{
+	if (parse_lang_string(&bss->venue_name, &bss->venue_name_count, pos)) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid venue_name '%s'",
+			   line, pos);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf,
+			       int line)
+{
+	size_t count;
+	char *pos;
+	u8 *info = NULL, *ipos;
+
+	/* format: <MCC1,MNC1>[;<MCC2,MNC2>][;...] */
+
+	count = 1;
+	for (pos = buf; *pos; pos++) {
+		if ((*pos < '0' || *pos > '9') && *pos != ';' && *pos != ',')
+			goto fail;
+		if (*pos == ';')
+			count++;
+	}
+	if (1 + count * 3 > 0x7f)
+		goto fail;
+
+	info = os_zalloc(2 + 3 + count * 3);
+	if (info == NULL)
+		return -1;
+
+	ipos = info;
+	*ipos++ = 0; /* GUD - Version 1 */
+	*ipos++ = 3 + count * 3; /* User Data Header Length (UDHL) */
+	*ipos++ = 0; /* PLMN List IEI */
+	/* ext(b8) | Length of PLMN List value contents(b7..1) */
+	*ipos++ = 1 + count * 3;
+	*ipos++ = count; /* Number of PLMNs */
+
+	pos = buf;
+	while (pos && *pos) {
+		char *mcc, *mnc;
+		size_t mnc_len;
+
+		mcc = pos;
+		mnc = os_strchr(pos, ',');
+		if (mnc == NULL)
+			goto fail;
+		*mnc++ = '\0';
+		pos = os_strchr(mnc, ';');
+		if (pos)
+			*pos++ = '\0';
+
+		mnc_len = os_strlen(mnc);
+		if (os_strlen(mcc) != 3 || (mnc_len != 2 && mnc_len != 3))
+			goto fail;
+
+		/* BC coded MCC,MNC */
+		/* MCC digit 2 | MCC digit 1 */
+		*ipos++ = ((mcc[1] - '0') << 4) | (mcc[0] - '0');
+		/* MNC digit 3 | MCC digit 3 */
+		*ipos++ = (((mnc_len == 2) ? 0xf0 : ((mnc[2] - '0') << 4))) |
+			(mcc[2] - '0');
+		/* MNC digit 2 | MNC digit 1 */
+		*ipos++ = ((mnc[1] - '0') << 4) | (mnc[0] - '0');
+	}
+
+	os_free(bss->anqp_3gpp_cell_net);
+	bss->anqp_3gpp_cell_net = info;
+	bss->anqp_3gpp_cell_net_len = 2 + 3 + 3 * count;
+	wpa_hexdump(MSG_MSGDUMP, "3GPP Cellular Network information",
+		    bss->anqp_3gpp_cell_net, bss->anqp_3gpp_cell_net_len);
+
+	return 0;
+
+fail:
+	wpa_printf(MSG_ERROR, "Line %d: Invalid anqp_3gpp_cell_net: %s",
+		   line, buf);
+	os_free(info);
+	return -1;
+}
+
+
+static int parse_nai_realm(struct hostapd_bss_config *bss, char *buf, int line)
+{
+	struct hostapd_nai_realm_data *realm;
+	size_t i, j, len;
+	int *offsets;
+	char *pos, *end, *rpos;
+
+	offsets = os_calloc(bss->nai_realm_count * MAX_NAI_REALMS,
+			    sizeof(int));
+	if (offsets == NULL)
+		return -1;
+
+	for (i = 0; i < bss->nai_realm_count; i++) {
+		realm = &bss->nai_realm_data[i];
+		for (j = 0; j < MAX_NAI_REALMS; j++) {
+			offsets[i * MAX_NAI_REALMS + j] =
+				realm->realm[j] ?
+				realm->realm[j] - realm->realm_buf : -1;
+		}
+	}
+
+	realm = os_realloc_array(bss->nai_realm_data, bss->nai_realm_count + 1,
+				 sizeof(struct hostapd_nai_realm_data));
+	if (realm == NULL) {
+		os_free(offsets);
+		return -1;
+	}
+	bss->nai_realm_data = realm;
+
+	/* patch the pointers after realloc */
+	for (i = 0; i < bss->nai_realm_count; i++) {
+		realm = &bss->nai_realm_data[i];
+		for (j = 0; j < MAX_NAI_REALMS; j++) {
+			int offs = offsets[i * MAX_NAI_REALMS + j];
+			if (offs >= 0)
+				realm->realm[j] = realm->realm_buf + offs;
+			else
+				realm->realm[j] = NULL;
+		}
+	}
+	os_free(offsets);
+
+	realm = &bss->nai_realm_data[bss->nai_realm_count];
+	os_memset(realm, 0, sizeof(*realm));
+
+	pos = buf;
+	realm->encoding = atoi(pos);
+	pos = os_strchr(pos, ',');
+	if (pos == NULL)
+		goto fail;
+	pos++;
+
+	end = os_strchr(pos, ',');
+	if (end) {
+		len = end - pos;
+		*end = '\0';
+	} else {
+		len = os_strlen(pos);
+	}
+
+	if (len > MAX_NAI_REALMLEN) {
+		wpa_printf(MSG_ERROR, "Too long a realm string (%d > max %d "
+			   "characters)", (int) len, MAX_NAI_REALMLEN);
+		goto fail;
+	}
+	os_memcpy(realm->realm_buf, pos, len);
+
+	if (end)
+		pos = end + 1;
+	else
+		pos = NULL;
+
+	while (pos && *pos) {
+		struct hostapd_nai_realm_eap *eap;
+
+		if (realm->eap_method_count >= MAX_NAI_EAP_METHODS) {
+			wpa_printf(MSG_ERROR, "Too many EAP methods");
+			goto fail;
+		}
+
+		eap = &realm->eap_method[realm->eap_method_count];
+		realm->eap_method_count++;
+
+		end = os_strchr(pos, ',');
+		if (end == NULL)
+			end = pos + os_strlen(pos);
+
+		eap->eap_method = atoi(pos);
+		for (;;) {
+			pos = os_strchr(pos, '[');
+			if (pos == NULL || pos > end)
+				break;
+			pos++;
+			if (eap->num_auths >= MAX_NAI_AUTH_TYPES) {
+				wpa_printf(MSG_ERROR, "Too many auth params");
+				goto fail;
+			}
+			eap->auth_id[eap->num_auths] = atoi(pos);
+			pos = os_strchr(pos, ':');
+			if (pos == NULL || pos > end)
+				goto fail;
+			pos++;
+			eap->auth_val[eap->num_auths] = atoi(pos);
+			pos = os_strchr(pos, ']');
+			if (pos == NULL || pos > end)
+				goto fail;
+			pos++;
+			eap->num_auths++;
+		}
+
+		if (*end != ',')
+			break;
+
+		pos = end + 1;
+	}
+
+	/* Split realm list into null terminated realms */
+	rpos = realm->realm_buf;
+	i = 0;
+	while (*rpos) {
+		if (i >= MAX_NAI_REALMS) {
+			wpa_printf(MSG_ERROR, "Too many realms");
+			goto fail;
+		}
+		realm->realm[i++] = rpos;
+		rpos = os_strchr(rpos, ';');
+		if (rpos == NULL)
+			break;
+		*rpos++ = '\0';
+	}
+
+	bss->nai_realm_count++;
+
+	return 0;
+
+fail:
+	wpa_printf(MSG_ERROR, "Line %d: invalid nai_realm '%s'", line, buf);
+	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 */
+
+
+#ifdef CONFIG_HS20
+static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf,
+				 int line)
+{
+	u8 *conn_cap;
+	char *pos;
+
+	if (bss->hs20_connection_capability_len >= 0xfff0)
+		return -1;
+
+	conn_cap = os_realloc(bss->hs20_connection_capability,
+			      bss->hs20_connection_capability_len + 4);
+	if (conn_cap == NULL)
+		return -1;
+
+	bss->hs20_connection_capability = conn_cap;
+	conn_cap += bss->hs20_connection_capability_len;
+	pos = buf;
+	conn_cap[0] = atoi(pos);
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		return -1;
+	pos++;
+	WPA_PUT_LE16(conn_cap + 1, atoi(pos));
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		return -1;
+	pos++;
+	conn_cap[3] = atoi(pos);
+	bss->hs20_connection_capability_len += 4;
+
+	return 0;
+}
+
+
+static int hs20_parse_wan_metrics(struct hostapd_bss_config *bss, char *buf,
+				  int line)
+{
+	u8 *wan_metrics;
+	char *pos;
+
+	/* <WAN Info>:<DL Speed>:<UL Speed>:<DL Load>:<UL Load>:<LMD> */
+
+	wan_metrics = os_zalloc(13);
+	if (wan_metrics == NULL)
+		return -1;
+
+	pos = buf;
+	/* WAN Info */
+	if (hexstr2bin(pos, wan_metrics, 1) < 0)
+		goto fail;
+	pos += 2;
+	if (*pos != ':')
+		goto fail;
+	pos++;
+
+	/* Downlink Speed */
+	WPA_PUT_LE32(wan_metrics + 1, atoi(pos));
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		goto fail;
+	pos++;
+
+	/* Uplink Speed */
+	WPA_PUT_LE32(wan_metrics + 5, atoi(pos));
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		goto fail;
+	pos++;
+
+	/* Downlink Load */
+	wan_metrics[9] = atoi(pos);
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		goto fail;
+	pos++;
+
+	/* Uplink Load */
+	wan_metrics[10] = atoi(pos);
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		goto fail;
+	pos++;
+
+	/* LMD */
+	WPA_PUT_LE16(wan_metrics + 11, atoi(pos));
+
+	os_free(bss->hs20_wan_metrics);
+	bss->hs20_wan_metrics = wan_metrics;
+
+	return 0;
+
+fail:
+	wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_wan_metrics '%s'",
+		   line, buf);
+	os_free(wan_metrics);
+	return -1;
+}
+
+
+static int hs20_parse_oper_friendly_name(struct hostapd_bss_config *bss,
+					 char *pos, int line)
+{
+	if (parse_lang_string(&bss->hs20_oper_friendly_name,
+			      &bss->hs20_oper_friendly_name_count, pos)) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid "
+			   "hs20_oper_friendly_name '%s'", line, pos);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int hs20_parse_icon(struct hostapd_bss_config *bss, char *pos)
+{
+	struct hs20_icon *icon;
+	char *end;
+
+	icon = os_realloc_array(bss->hs20_icons, bss->hs20_icons_count + 1,
+				sizeof(struct hs20_icon));
+	if (icon == NULL)
+		return -1;
+	bss->hs20_icons = icon;
+	icon = &bss->hs20_icons[bss->hs20_icons_count];
+	os_memset(icon, 0, sizeof(*icon));
+
+	icon->width = atoi(pos);
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		return -1;
+	pos++;
+
+	icon->height = atoi(pos);
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		return -1;
+	pos++;
+
+	end = os_strchr(pos, ':');
+	if (end == NULL || end - pos > 3)
+		return -1;
+	os_memcpy(icon->language, pos, end - pos);
+	pos = end + 1;
+
+	end = os_strchr(pos, ':');
+	if (end == NULL || end - pos > 255)
+		return -1;
+	os_memcpy(icon->type, pos, end - pos);
+	pos = end + 1;
+
+	end = os_strchr(pos, ':');
+	if (end == NULL || end - pos > 255)
+		return -1;
+	os_memcpy(icon->name, pos, end - pos);
+	pos = end + 1;
+
+	if (os_strlen(pos) > 255)
+		return -1;
+	os_memcpy(icon->file, pos, os_strlen(pos));
+
+	bss->hs20_icons_count++;
+
+	return 0;
+}
+
+
+static int hs20_parse_osu_ssid(struct hostapd_bss_config *bss,
+			       char *pos, int line)
+{
+	size_t slen;
+	char *str;
+
+	str = wpa_config_parse_string(pos, &slen);
+	if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos);
+		os_free(str);
+		return -1;
+	}
+
+	os_memcpy(bss->osu_ssid, str, slen);
+	bss->osu_ssid_len = slen;
+	os_free(str);
+
+	return 0;
+}
+
+
+static int hs20_parse_osu_server_uri(struct hostapd_bss_config *bss,
+				     char *pos, int line)
+{
+	struct hs20_osu_provider *p;
+
+	p = os_realloc_array(bss->hs20_osu_providers,
+			     bss->hs20_osu_providers_count + 1, sizeof(*p));
+	if (p == NULL)
+		return -1;
+
+	bss->hs20_osu_providers = p;
+	bss->last_osu = &bss->hs20_osu_providers[bss->hs20_osu_providers_count];
+	bss->hs20_osu_providers_count++;
+	os_memset(bss->last_osu, 0, sizeof(*p));
+	bss->last_osu->server_uri = os_strdup(pos);
+
+	return 0;
+}
+
+
+static int hs20_parse_osu_friendly_name(struct hostapd_bss_config *bss,
+					char *pos, int line)
+{
+	if (bss->last_osu == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+		return -1;
+	}
+
+	if (parse_lang_string(&bss->last_osu->friendly_name,
+			      &bss->last_osu->friendly_name_count, pos)) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid osu_friendly_name '%s'",
+			   line, pos);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int hs20_parse_osu_nai(struct hostapd_bss_config *bss,
+			      char *pos, int line)
+{
+	if (bss->last_osu == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+		return -1;
+	}
+
+	os_free(bss->last_osu->osu_nai);
+	bss->last_osu->osu_nai = os_strdup(pos);
+	if (bss->last_osu->osu_nai == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+static int hs20_parse_osu_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 */
+
+
+#ifdef CONFIG_WPS_NFC
+static struct wpabuf * hostapd_parse_bin(const char *buf)
+{
+	size_t len;
+	struct wpabuf *ret;
+
+	len = os_strlen(buf);
+	if (len & 0x01)
+		return NULL;
+	len /= 2;
+
+	ret = wpabuf_alloc(len);
+	if (ret == NULL)
+		return NULL;
+
+	if (hexstr2bin(buf, wpabuf_put(ret, len), len)) {
+		wpabuf_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+#endif /* CONFIG_WPS_NFC */
+
+
+#ifdef CONFIG_ACS
+static int hostapd_config_parse_acs_chan_bias(struct hostapd_config *conf,
+					      char *pos)
+{
+	struct acs_bias *bias = NULL, *tmp;
+	unsigned int num = 0;
+	char *end;
+
+	while (*pos) {
+		tmp = os_realloc_array(bias, num + 1, sizeof(*bias));
+		if (!tmp)
+			goto fail;
+		bias = tmp;
+
+		bias[num].channel = atoi(pos);
+		if (bias[num].channel <= 0)
+			goto fail;
+		pos = os_strchr(pos, ':');
+		if (!pos)
+			goto fail;
+		pos++;
+		bias[num].bias = strtod(pos, &end);
+		if (end == pos || bias[num].bias < 0.0)
+			goto fail;
+		pos = end;
+		if (*pos != ' ' && *pos != '\0')
+			goto fail;
+		num++;
+	}
+
+	os_free(conf->acs_chan_bias);
+	conf->acs_chan_bias = bias;
+	conf->num_acs_chan_bias = num;
+
+	return 0;
+fail:
+	os_free(bias);
+	return -1;
+}
+#endif /* CONFIG_ACS */
+
+
+static int hostapd_config_fill(struct hostapd_config *conf,
+			       struct hostapd_bss_config *bss,
+			       const char *buf, char *pos, int line)
+{
+	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);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "driver_params") == 0) {
+		os_free(conf->driver_params);
+		conf->driver_params = os_strdup(pos);
+	} 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 > SSID_MAX_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 > SSID_MAX_LEN) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
+				   line, pos);
+			os_free(str);
+			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))
+			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, "tls_session_lifetime") == 0) {
+		bss->tls_session_lifetime = 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, "openssl_ciphers") == 0) {
+		os_free(bss->openssl_ciphers);
+		bss->openssl_ciphers = 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);
+			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);
+#endif /* EAP_SERVER_SIM */
+#ifdef EAP_SERVER_TNC
+	} 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);
+#endif /* EAP_SERVER_PWD */
+	} else if (os_strcmp(buf, "eap_server_erp") == 0) {
+		bss->eap_server_erp = atoi(pos);
+#endif /* EAP_SERVER */
+	} 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, "erp_send_reauth_start") == 0) {
+		bss->erp_send_reauth_start = atoi(pos);
+	} else if (os_strcmp(buf, "erp_domain") == 0) {
+		os_free(bss->erp_domain);
+		bss->erp_domain = os_strdup(pos);
+	} 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));
+#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);
+			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, "radius_client_addr") == 0) {
+		if (hostapd_parse_ip_addr(pos, &bss->radius->client_addr)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid IP address '%s'",
+				   line, pos);
+			return 1;
+		}
+		bss->radius->force_client_addr = 1;
+	} 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_addr_replace") == 0) {
+		if (hostapd_parse_ip_addr(pos,
+					  &bss->radius->auth_server->addr)) {
+			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_addr_replace") == 0) {
+		if (hostapd_parse_ip_addr(pos,
+					  &bss->radius->acct_server->addr)) {
+			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);
+			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) {
+			hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
+			bss->ssid.wpa_passphrase_set = 1;
+		}
+	} else if (os_strcmp(buf, "wpa_psk") == 0) {
+		hostapd_config_clear_wpa_psk(&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);
+			hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
+			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) {
+		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);
+#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_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) {
+#ifndef CONFIG_NATIVE_WINDOWS
+		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 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_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, "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 if (os_strcmp(pos, "any") == 0)
+			conf->hw_mode = HOSTAPD_MODE_IEEE80211ANY;
+		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, "ad") == 0)
+			bss->wps_rf_bands = WPS_RF_60GHZ;
+		else 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);
+			return 1;
+#else /* CONFIG_ACS */
+			conf->acs = 1;
+			conf->channel = 0;
+#endif /* CONFIG_ACS */
+		} else {
+			conf->channel = atoi(pos);
+			conf->acs = conf->channel == 0;
+		}
+	} else if (os_strcmp(buf, "chanlist") == 0) {
+		if (hostapd_parse_chanlist(conf, pos)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid channel list",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "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);
+			return 1;
+		}
+		conf->acs_num_scans = val;
+	} else if (os_strcmp(buf, "acs_chan_bias") == 0) {
+		if (hostapd_config_parse_acs_chan_bias(conf, pos)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid acs_chan_bias",
+				   line);
+			return -1;
+		}
+#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);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "bss_load_update_period") == 0) {
+		bss->bss_load_update_period = atoi(pos);
+		if (bss->bss_load_update_period < 0 ||
+		    bss->bss_load_update_period > 100) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid bss_load_update_period %d",
+				   line, bss->bss_load_update_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);
+			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) {
+		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);
+			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, "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);
+			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);
+			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);
+	} else if (os_strcmp(buf, "vendor_vht") == 0) {
+		bss->vendor_vht = 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);
+#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);
+			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) > WPS_DEV_NAME_MAX_LEN) {
+			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);
+			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) {
+		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) {
+		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);
+#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);
+			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);
+#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);
+			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)
+			return 1;
+
+		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;
+			}
+		}
+
+		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);
+#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, "proxy_arp") == 0) {
+		bss->proxy_arp = atoi(pos);
+	} else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) {
+		bss->na_mcast_to_ucast = atoi(pos);
+	} else if (os_strcmp(buf, "osen") == 0) {
+		bss->osen = atoi(pos);
+	} else if (os_strcmp(buf, "anqp_domain_id") == 0) {
+		bss->anqp_domain_id = atoi(pos);
+	} else if (os_strcmp(buf, "hs20_deauth_req_timeout") == 0) {
+		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.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;
+	} else if (os_strcmp(buf, "radio_measurements") == 0) {
+		bss->radio_measurements = atoi(pos);
+	} else if (os_strcmp(buf, "own_ie_override") == 0) {
+		struct wpabuf *tmp;
+		size_t len = os_strlen(pos) / 2;
+
+		tmp = wpabuf_alloc(len);
+		if (!tmp)
+			return 1;
+
+		if (hexstr2bin(pos, wpabuf_put(tmp, len), len)) {
+			wpabuf_free(tmp);
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid own_ie_override '%s'",
+				   line, pos);
+			return 1;
+		}
+
+		wpabuf_free(bss->own_ie_override);
+		bss->own_ie_override = tmp;
+#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 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);
+#ifdef CONFIG_FST
+	} else if (os_strcmp(buf, "fst_group_id") == 0) {
+		size_t len = os_strlen(pos);
+
+		if (!len || len >= sizeof(conf->fst_cfg.group_id)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid fst_group_id value '%s'",
+				   line, pos);
+			return 1;
+		}
+
+		if (conf->fst_cfg.group_id[0]) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Duplicate fst_group value '%s'",
+				   line, pos);
+			return 1;
+		}
+
+		os_strlcpy(conf->fst_cfg.group_id, pos,
+			   sizeof(conf->fst_cfg.group_id));
+	} else if (os_strcmp(buf, "fst_priority") == 0) {
+		char *endp;
+		long int val;
+
+		if (!*pos) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: fst_priority value not supplied (expected 1..%u)",
+				   line, FST_MAX_PRIO_VALUE);
+			return -1;
+		}
+
+		val = strtol(pos, &endp, 0);
+		if (*endp || val < 1 || val > FST_MAX_PRIO_VALUE) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid fst_priority %ld (%s) (expected 1..%u)",
+				   line, val, pos, FST_MAX_PRIO_VALUE);
+			return 1;
+		}
+		conf->fst_cfg.priority = (u8) val;
+	} else if (os_strcmp(buf, "fst_llt") == 0) {
+		char *endp;
+		long int val;
+
+		if (!*pos) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: fst_llt value not supplied (expected 1..%u)",
+				   line, FST_MAX_LLT_MS);
+			return -1;
+		}
+		val = strtol(pos, &endp, 0);
+		if (*endp || val < 1 || val > FST_MAX_LLT_MS) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid fst_llt %ld (%s) (expected 1..%u)",
+				   line, val, pos, FST_MAX_LLT_MS);
+			return 1;
+		}
+		conf->fst_cfg.llt = (u32) val;
+#endif /* CONFIG_FST */
+	} else if (os_strcmp(buf, "track_sta_max_num") == 0) {
+		conf->track_sta_max_num = atoi(pos);
+	} else if (os_strcmp(buf, "track_sta_max_age") == 0) {
+		conf->track_sta_max_age = atoi(pos);
+	} else if (os_strcmp(buf, "no_probe_resp_if_seen_on") == 0) {
+		os_free(bss->no_probe_resp_if_seen_on);
+		bss->no_probe_resp_if_seen_on = os_strdup(pos);
+	} else if (os_strcmp(buf, "no_auth_if_seen_on") == 0) {
+		os_free(bss->no_auth_if_seen_on);
+		bss->no_auth_if_seen_on = os_strdup(pos);
+	} else {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: unknown configuration item '%s'",
+			   line, buf);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * hostapd_config_read - Read and parse a configuration file
+ * @fname: Configuration file name (including path, if needed)
+ * Returns: Allocated configuration data structure
+ */
+struct hostapd_config * hostapd_config_read(const char *fname)
+{
+	struct hostapd_config *conf;
+	FILE *f;
+	char buf[512], *pos;
+	int line = 0;
+	int errors = 0;
+	size_t i;
+
+	f = fopen(fname, "r");
+	if (f == NULL) {
+		wpa_printf(MSG_ERROR, "Could not open configuration file '%s' "
+			   "for reading.", fname);
+		return NULL;
+	}
+
+	conf = hostapd_config_defaults();
+	if (conf == NULL) {
+		fclose(f);
+		return NULL;
+	}
+
+	/* set default driver based on configuration */
+	conf->driver = wpa_drivers[0];
+	if (conf->driver == NULL) {
+		wpa_printf(MSG_ERROR, "No driver wrappers registered!");
+		hostapd_config_free(conf);
+		fclose(f);
+		return NULL;
+	}
+
+	conf->last_bss = conf->bss[0];
+
+	while (fgets(buf, sizeof(buf), f)) {
+		struct hostapd_bss_config *bss;
+
+		bss = conf->last_bss;
+		line++;
+
+		if (buf[0] == '#')
+			continue;
+		pos = buf;
+		while (*pos != '\0') {
+			if (*pos == '\n') {
+				*pos = '\0';
+				break;
+			}
+			pos++;
+		}
+		if (buf[0] == '\0')
+			continue;
+
+		pos = os_strchr(buf, '=');
+		if (pos == NULL) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid line '%s'",
+				   line, buf);
+			errors++;
+			continue;
+		}
+		*pos = '\0';
+		pos++;
+		errors += hostapd_config_fill(conf, bss, buf, pos, line);
+	}
+
+	fclose(f);
+
+	for (i = 0; i < conf->num_bss; i++)
+		hostapd_set_security_params(conf->bss[i], 1);
+
+	if (hostapd_config_check(conf, 1))
+		errors++;
+
+#ifndef WPA_IGNORE_CONFIG_ERRORS
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d errors found in configuration file "
+			   "'%s'", errors, fname);
+		hostapd_config_free(conf);
+		conf = NULL;
+	}
+#endif /* WPA_IGNORE_CONFIG_ERRORS */
+
+	return conf;
+}
+
+
+int hostapd_set_iface(struct hostapd_config *conf,
+		      struct hostapd_bss_config *bss, const char *field,
+		      char *value)
+{
+	int errors;
+	size_t i;
+
+	errors = hostapd_config_fill(conf, bss, field, value, 0);
+	if (errors) {
+		wpa_printf(MSG_INFO, "Failed to set configuration field '%s' "
+			   "to value '%s'", field, value);
+		return -1;
+	}
+
+	for (i = 0; i < conf->num_bss; i++)
+		hostapd_set_security_params(conf->bss[i], 0);
+
+	if (hostapd_config_check(conf, 0)) {
+		wpa_printf(MSG_ERROR, "Configuration check failed");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/hostap/hostapd/config_file.h b/hostap/hostapd/config_file.h
new file mode 100644
index 0000000..c98bdb6
--- /dev/null
+++ b/hostap/hostapd/config_file.h
@@ -0,0 +1,17 @@
+/*
+ * hostapd / Configuration file parser
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CONFIG_FILE_H
+#define CONFIG_FILE_H
+
+struct hostapd_config * hostapd_config_read(const char *fname);
+int hostapd_set_iface(struct hostapd_config *conf,
+		      struct hostapd_bss_config *bss, const char *field,
+		      char *value);
+
+#endif /* CONFIG_FILE_H */
diff --git a/hostap/hostapd/ctrl_iface.c b/hostap/hostapd/ctrl_iface.c
new file mode 100644
index 0000000..cb6fb17
--- /dev/null
+++ b/hostap/hostapd/ctrl_iface.c
@@ -0,0 +1,3217 @@
+/*
+ * hostapd / UNIX domain socket -based control interface
+ * Copyright (c) 2004-2015, 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"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+
+#ifdef CONFIG_TESTING_OPTIONS
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <stddef.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/version.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/tls.h"
+#include "drivers/driver.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "radius/radius_client.h"
+#include "radius/radius_server.h"
+#include "l2_packet/l2_packet.h"
+#include "ap/hostapd.h"
+#include "ap/ap_config.h"
+#include "ap/ieee802_1x.h"
+#include "ap/wpa_auth.h"
+#include "ap/ieee802_11.h"
+#include "ap/sta_info.h"
+#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 "ap/beacon.h"
+#include "wps/wps_defs.h"
+#include "wps/wps.h"
+#include "fst/fst_ctrl_iface.h"
+#include "config_file.h"
+#include "ctrl_iface.h"
+
+
+#define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
+
+struct wpa_ctrl_dst {
+	struct wpa_ctrl_dst *next;
+	struct sockaddr_un addr;
+	socklen_t addrlen;
+	int debug_level;
+	int errors;
+};
+
+
+static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
+				    enum wpa_msg_type type,
+				    const char *buf, size_t len);
+
+
+static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
+				     struct sockaddr_un *from,
+				     socklen_t fromlen)
+{
+	struct wpa_ctrl_dst *dst;
+
+	dst = os_zalloc(sizeof(*dst));
+	if (dst == NULL)
+		return -1;
+	os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
+	dst->addrlen = fromlen;
+	dst->debug_level = MSG_INFO;
+	dst->next = hapd->ctrl_dst;
+	hapd->ctrl_dst = dst;
+	wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
+		    (u8 *) from->sun_path,
+		    fromlen - offsetof(struct sockaddr_un, sun_path));
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
+				     struct sockaddr_un *from,
+				     socklen_t fromlen)
+{
+	struct wpa_ctrl_dst *dst, *prev = NULL;
+
+	dst = hapd->ctrl_dst;
+	while (dst) {
+		if (fromlen == dst->addrlen &&
+		    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);
+			return 0;
+		}
+		prev = dst;
+		dst = dst->next;
+	}
+	return -1;
+}
+
+
+static int hostapd_ctrl_iface_level(struct hostapd_data *hapd,
+				    struct sockaddr_un *from,
+				    socklen_t fromlen,
+				    char *level)
+{
+	struct wpa_ctrl_dst *dst;
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
+
+	dst = hapd->ctrl_dst;
+	while (dst) {
+		if (fromlen == dst->addrlen &&
+		    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));
+			dst->debug_level = atoi(level);
+			return 0;
+		}
+		dst = dst->next;
+	}
+
+	return -1;
+}
+
+
+static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd,
+				      const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface "
+		   "notification", MAC2STR(addr));
+	sta = ap_sta_add(hapd, addr);
+	if (sta == NULL)
+		return -1;
+
+	hostapd_new_assoc_sta(hapd, sta, 0);
+	return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+#ifdef NEED_AP_MLME
+static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
+				       const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr) ||
+	    os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0)
+		return -1;
+
+	ieee802_11_send_sa_query_req(hapd, addr, trans_id);
+
+	return 0;
+}
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211W */
+
+
+#ifdef CONFIG_WPS
+static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
+{
+	char *pin = os_strchr(txt, ' ');
+	char *timeout_txt;
+	int timeout;
+	u8 addr_buf[ETH_ALEN], *addr = NULL;
+	char *pos;
+
+	if (pin == NULL)
+		return -1;
+	*pin++ = '\0';
+
+	timeout_txt = os_strchr(pin, ' ');
+	if (timeout_txt) {
+		*timeout_txt++ = '\0';
+		timeout = atoi(timeout_txt);
+		pos = os_strchr(timeout_txt, ' ');
+		if (pos) {
+			*pos++ = '\0';
+			if (hwaddr_aton(pos, addr_buf) == 0)
+				addr = addr_buf;
+		}
+	} else
+		timeout = 0;
+
+	return hostapd_wps_add_pin(hapd, addr, txt, pin, timeout);
+}
+
+
+static int hostapd_ctrl_iface_wps_check_pin(
+	struct hostapd_data *hapd, char *cmd, char *buf, size_t buflen)
+{
+	char pin[9];
+	size_t len;
+	char *pos;
+	int ret;
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
+			      (u8 *) cmd, os_strlen(cmd));
+	for (pos = cmd, len = 0; *pos != '\0'; pos++) {
+		if (*pos < '0' || *pos > '9')
+			continue;
+		pin[len++] = *pos;
+		if (len == 9) {
+			wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
+			return -1;
+		}
+	}
+	if (len != 4 && len != 8) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
+		return -1;
+	}
+	pin[len] = '\0';
+
+	if (len == 8) {
+		unsigned int pin_val;
+		pin_val = atoi(pin);
+		if (!wps_pin_valid(pin_val)) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
+			ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
+			if (os_snprintf_error(buflen, ret))
+				return -1;
+			return ret;
+		}
+	}
+
+	ret = os_snprintf(buf, buflen, "%s", pin);
+	if (os_snprintf_error(buflen, ret))
+		return -1;
+
+	return ret;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+static int hostapd_ctrl_iface_wps_nfc_tag_read(struct hostapd_data *hapd,
+					       char *pos)
+{
+	size_t len;
+	struct wpabuf *buf;
+	int ret;
+
+	len = os_strlen(pos);
+	if (len & 0x01)
+		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;
+	}
+
+	ret = hostapd_wps_nfc_tag_read(hapd, buf);
+	wpabuf_free(buf);
+
+	return ret;
+}
+
+
+static int hostapd_ctrl_iface_wps_nfc_config_token(struct hostapd_data *hapd,
+						   char *cmd, char *reply,
+						   size_t max_len)
+{
+	int ndef;
+	struct wpabuf *buf;
+	int res;
+
+	if (os_strcmp(cmd, "WPS") == 0)
+		ndef = 0;
+	else if (os_strcmp(cmd, "NDEF") == 0)
+		ndef = 1;
+	else
+		return -1;
+
+	buf = hostapd_wps_nfc_config_token(hapd, ndef);
+	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;
+}
+
+
+static int hostapd_ctrl_iface_wps_nfc_token_gen(struct hostapd_data *hapd,
+						char *reply, size_t max_len,
+						int ndef)
+{
+	struct wpabuf *buf;
+	int res;
+
+	buf = hostapd_wps_nfc_token_gen(hapd, ndef);
+	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;
+}
+
+
+static int hostapd_ctrl_iface_wps_nfc_token(struct hostapd_data *hapd,
+					    char *cmd, char *reply,
+					    size_t max_len)
+{
+	if (os_strcmp(cmd, "WPS") == 0)
+		return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
+							    max_len, 0);
+
+	if (os_strcmp(cmd, "NDEF") == 0)
+		return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
+							    max_len, 1);
+
+	if (os_strcmp(cmd, "enable") == 0)
+		return hostapd_wps_nfc_token_enable(hapd);
+
+	if (os_strcmp(cmd, "disable") == 0) {
+		hostapd_wps_nfc_token_disable(hapd);
+		return 0;
+	}
+
+	return -1;
+}
+
+
+static int hostapd_ctrl_iface_nfc_get_handover_sel(struct hostapd_data *hapd,
+						   char *cmd, char *reply,
+						   size_t max_len)
+{
+	struct wpabuf *buf;
+	int res;
+	char *pos;
+	int ndef;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\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-CR") == 0)
+		buf = hostapd_wps_nfc_hs_cr(hapd, ndef);
+	else
+		buf = NULL;
+	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;
+}
+
+
+static int hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data *hapd,
+						  char *cmd)
+{
+	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 */
+
+
+static int hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data *hapd, char *txt,
+					 char *buf, size_t buflen)
+{
+	int timeout = 300;
+	char *pos;
+	const char *pin_txt;
+
+	pos = os_strchr(txt, ' ');
+	if (pos)
+		*pos++ = '\0';
+
+	if (os_strcmp(txt, "disable") == 0) {
+		hostapd_wps_ap_pin_disable(hapd);
+		return os_snprintf(buf, buflen, "OK\n");
+	}
+
+	if (os_strcmp(txt, "random") == 0) {
+		if (pos)
+			timeout = atoi(pos);
+		pin_txt = hostapd_wps_ap_pin_random(hapd, timeout);
+		if (pin_txt == NULL)
+			return -1;
+		return os_snprintf(buf, buflen, "%s", pin_txt);
+	}
+
+	if (os_strcmp(txt, "get") == 0) {
+		pin_txt = hostapd_wps_ap_pin_get(hapd);
+		if (pin_txt == NULL)
+			return -1;
+		return os_snprintf(buf, buflen, "%s", pin_txt);
+	}
+
+	if (os_strcmp(txt, "set") == 0) {
+		char *pin;
+		if (pos == NULL)
+			return -1;
+		pin = pos;
+		pos = os_strchr(pos, ' ');
+		if (pos) {
+			*pos++ = '\0';
+			timeout = atoi(pos);
+		}
+		if (os_strlen(pin) > buflen)
+			return -1;
+		if (hostapd_wps_ap_pin_set(hapd, pin, timeout) < 0)
+			return -1;
+		return os_snprintf(buf, buflen, "%s", pin);
+	}
+
+	return -1;
+}
+
+
+static int hostapd_ctrl_iface_wps_config(struct hostapd_data *hapd, char *txt)
+{
+	char *pos;
+	char *ssid, *auth, *encr = NULL, *key = NULL;
+
+	ssid = txt;
+	pos = os_strchr(txt, ' ');
+	if (!pos)
+		return -1;
+	*pos++ = '\0';
+
+	auth = pos;
+	pos = os_strchr(pos, ' ');
+	if (pos) {
+		*pos++ = '\0';
+		encr = pos;
+		pos = os_strchr(pos, ' ');
+		if (pos) {
+			*pos++ = '\0';
+			key = pos;
+		}
+	}
+
+	return hostapd_wps_config_ap(hapd, ssid, auth, encr, key);
+}
+
+
+static const char * pbc_status_str(enum pbc_status status)
+{
+	switch (status) {
+	case WPS_PBC_STATUS_DISABLE:
+		return "Disabled";
+	case WPS_PBC_STATUS_ACTIVE:
+		return "Active";
+	case WPS_PBC_STATUS_TIMEOUT:
+		return "Timed-out";
+	case WPS_PBC_STATUS_OVERLAP:
+		return "Overlap";
+	default:
+		return "Unknown";
+	}
+}
+
+
+static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
+					     char *buf, size_t buflen)
+{
+	int ret;
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + buflen;
+
+	ret = os_snprintf(pos, end - pos, "PBC Status: %s\n",
+			  pbc_status_str(hapd->wps_stats.pbc_status));
+
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	ret = os_snprintf(pos, end - pos, "Last WPS result: %s\n",
+			  (hapd->wps_stats.status == WPS_STATUS_SUCCESS ?
+			   "Success":
+			   (hapd->wps_stats.status == WPS_STATUS_FAILURE ?
+			    "Failed" : "None")));
+
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	/* If status == Failure - Add possible Reasons */
+	if(hapd->wps_stats.status == WPS_STATUS_FAILURE &&
+	   hapd->wps_stats.failure_reason > 0) {
+		ret = os_snprintf(pos, end - pos,
+				  "Failure Reason: %s\n",
+				  wps_ei_str(hapd->wps_stats.failure_reason));
+
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (hapd->wps_stats.status) {
+		ret = os_snprintf(pos, end - pos, "Peer Address: " MACSTR "\n",
+				  MAC2STR(hapd->wps_stats.peer_addr));
+
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+#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
+
+static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
+						const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	int disassoc_timer;
+	struct sta_info *sta;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+	if (cmd[17] != ' ')
+		return -1;
+	disassoc_timer = atoi(cmd + 17);
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for disassociation imminent message",
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer);
+}
+
+
+static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
+					   const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	const char *url, *timerstr;
+	int disassoc_timer;
+	struct sta_info *sta;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for ESS disassociation imminent message",
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	timerstr = cmd + 17;
+	if (*timerstr != ' ')
+		return -1;
+	timerstr++;
+	disassoc_timer = atoi(timerstr);
+	if (disassoc_timer < 0 || disassoc_timer > 65535)
+		return -1;
+
+	url = os_strchr(timerstr, ' ');
+	if (url == NULL)
+		return -1;
+	url++;
+
+	return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
+}
+
+
+static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+					 const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	const char *pos, *end;
+	int disassoc_timer = 0;
+	struct sta_info *sta;
+	u8 req_mode = 0, valid_int = 0x01;
+	u8 bss_term_dur[12];
+	char *url = NULL;
+	int ret;
+	u8 nei_rep[1000];
+	u8 *nei_pos = nei_rep;
+
+	if (hwaddr_aton(cmd, addr)) {
+		wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
+		return -1;
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for BSS TM Request message",
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	pos = os_strstr(cmd, " disassoc_timer=");
+	if (pos) {
+		pos += 16;
+		disassoc_timer = atoi(pos);
+		if (disassoc_timer < 0 || disassoc_timer > 65535) {
+			wpa_printf(MSG_DEBUG, "Invalid disassoc_timer");
+			return -1;
+		}
+	}
+
+	pos = os_strstr(cmd, " valid_int=");
+	if (pos) {
+		pos += 11;
+		valid_int = atoi(pos);
+	}
+
+	pos = os_strstr(cmd, " bss_term=");
+	if (pos) {
+		pos += 10;
+		req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED;
+		/* TODO: TSF configurable/learnable */
+		bss_term_dur[0] = 4; /* Subelement ID */
+		bss_term_dur[1] = 10; /* Length */
+		os_memset(bss_term_dur, 2, 8);
+		end = os_strchr(pos, ',');
+		if (end == NULL) {
+			wpa_printf(MSG_DEBUG, "Invalid bss_term data");
+			return -1;
+		}
+		end++;
+		WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
+	}
+
+
+	/*
+	 * BSS Transition Candidate List Entries - Neighbor Report elements
+	 * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
+	 * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
+	 */
+	pos = cmd;
+	while (pos) {
+		u8 *nei_start;
+		long int val;
+		char *endptr, *tmp;
+
+		pos = os_strstr(pos, " neighbor=");
+		if (!pos)
+			break;
+		if (nei_pos + 15 > nei_rep + sizeof(nei_rep)) {
+			wpa_printf(MSG_DEBUG,
+				   "Not enough room for additional neighbor");
+			return -1;
+		}
+		pos += 10;
+
+		nei_start = nei_pos;
+		*nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
+		nei_pos++; /* length to be filled in */
+
+		if (hwaddr_aton(pos, nei_pos)) {
+			wpa_printf(MSG_DEBUG, "Invalid BSSID");
+			return -1;
+		}
+		nei_pos += ETH_ALEN;
+		pos += 17;
+		if (*pos != ',') {
+			wpa_printf(MSG_DEBUG, "Missing BSSID Information");
+			return -1;
+		}
+		pos++;
+
+		val = strtol(pos, &endptr, 0);
+		WPA_PUT_LE32(nei_pos, val);
+		nei_pos += 4;
+		if (*endptr != ',') {
+			wpa_printf(MSG_DEBUG, "Missing Operating Class");
+			return -1;
+		}
+		pos = endptr + 1;
+
+		*nei_pos++ = atoi(pos); /* Operating Class */
+		pos = os_strchr(pos, ',');
+		if (pos == NULL) {
+			wpa_printf(MSG_DEBUG, "Missing Channel Number");
+			return -1;
+		}
+		pos++;
+
+		*nei_pos++ = atoi(pos); /* Channel Number */
+		pos = os_strchr(pos, ',');
+		if (pos == NULL) {
+			wpa_printf(MSG_DEBUG, "Missing PHY Type");
+			return -1;
+		}
+		pos++;
+
+		*nei_pos++ = atoi(pos); /* PHY Type */
+		end = os_strchr(pos, ' ');
+		tmp = os_strchr(pos, ',');
+		if (tmp && (!end || tmp < end)) {
+			/* Optional Subelements (hexdump) */
+			size_t len;
+
+			pos = tmp + 1;
+			end = os_strchr(pos, ' ');
+			if (end)
+				len = end - pos;
+			else
+				len = os_strlen(pos);
+			if (nei_pos + len / 2 > nei_rep + sizeof(nei_rep)) {
+				wpa_printf(MSG_DEBUG,
+					   "Not enough room for neighbor subelements");
+				return -1;
+			}
+			if (len & 0x01 ||
+			    hexstr2bin(pos, nei_pos, len / 2) < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "Invalid neighbor subelement info");
+				return -1;
+			}
+			nei_pos += len / 2;
+			pos = end;
+		}
+
+		nei_start[1] = nei_pos - nei_start - 2;
+	}
+
+	pos = os_strstr(cmd, " url=");
+	if (pos) {
+		size_t len;
+		pos += 5;
+		end = os_strchr(pos, ' ');
+		if (end)
+			len = end - pos;
+		else
+			len = os_strlen(pos);
+		url = os_malloc(len + 1);
+		if (url == NULL)
+			return -1;
+		os_memcpy(url, pos, len);
+		url[len] = '\0';
+		req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+	}
+
+	if (os_strstr(cmd, " pref=1"))
+		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+	if (os_strstr(cmd, " abridged=1"))
+		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
+	if (os_strstr(cmd, " disassoc_imminent=1"))
+		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+
+	ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
+				  valid_int, bss_term_dur, url,
+				  nei_pos > nei_rep ? nei_rep : NULL,
+				  nei_pos - nei_rep);
+	os_free(url);
+	return ret;
+}
+
+#endif /* CONFIG_WNM */
+
+
+static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
+					   char *buf, size_t buflen)
+{
+	int ret = 0;
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + buflen;
+
+	WPA_ASSERT(hapd->conf->wpa_key_mgmt);
+
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+		ret = os_snprintf(pos, end - pos, "WPA-PSK ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+		ret = os_snprintf(pos, end - pos, "WPA-EAP ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#ifdef CONFIG_IEEE80211R
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+		ret = os_snprintf(pos, end - pos, "FT-PSK ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+		ret = os_snprintf(pos, end - pos, "FT-EAP ");
+		if (os_snprintf_error(end - pos, ret))
+			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 (os_snprintf_error(end - pos, ret))
+			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) {
+		ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+		ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
+		ret = os_snprintf(pos, end - pos, "SAE ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_SAE */
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+		ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (hapd->conf->wpa_key_mgmt &
+	    WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		ret = os_snprintf(pos, end - pos,
+				  "WPA-EAP-SUITE-B-192 ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (pos > buf && *(pos - 1) == ' ') {
+		*(pos - 1) = '\0';
+		pos--;
+	}
+
+	return pos - buf;
+}
+
+
+static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
+					 char *buf, size_t buflen)
+{
+	int ret;
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + buflen;
+
+	ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n"
+			  "ssid=%s\n",
+			  MAC2STR(hapd->own_addr),
+			  wpa_ssid_txt(hapd->conf->ssid.ssid,
+				       hapd->conf->ssid.ssid_len));
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+#ifdef CONFIG_WPS
+	ret = os_snprintf(pos, end - pos, "wps_state=%s\n",
+			  hapd->conf->wps_state == 0 ? "disabled" :
+			  (hapd->conf->wps_state == 1 ? "not configured" :
+			   "configured"));
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	if (hapd->conf->wps_state && hapd->conf->wpa &&
+	    hapd->conf->ssid.wpa_passphrase) {
+		ret = os_snprintf(pos, end - pos, "passphrase=%s\n",
+				  hapd->conf->ssid.wpa_passphrase);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (hapd->conf->wps_state && hapd->conf->wpa &&
+	    hapd->conf->ssid.wpa_psk &&
+	    hapd->conf->ssid.wpa_psk->group) {
+		char hex[PMK_LEN * 2 + 1];
+		wpa_snprintf_hex(hex, sizeof(hex),
+				 hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
+		ret = os_snprintf(pos, end - pos, "psk=%s\n", hex);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_WPS */
+
+	if (hapd->conf->wpa) {
+		ret = os_snprintf(pos, end - pos, "wpa=%d\n", hapd->conf->wpa);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) {
+		ret = os_snprintf(pos, end - pos, "key_mgmt=");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+
+		pos += hostapd_ctrl_iface_get_key_mgmt(hapd, pos, end - pos);
+
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (hapd->conf->wpa) {
+		ret = os_snprintf(pos, end - pos, "group_cipher=%s\n",
+				  wpa_cipher_txt(hapd->conf->wpa_group));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->rsn_pairwise) {
+		ret = os_snprintf(pos, end - pos, "rsn_pairwise_cipher=");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+
+		ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise,
+					" ");
+		if (ret < 0)
+			return pos - buf;
+		pos += ret;
+
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if ((hapd->conf->wpa & WPA_PROTO_WPA) && hapd->conf->wpa_pairwise) {
+		ret = os_snprintf(pos, end - pos, "wpa_pairwise_cipher=");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+
+		ret = wpa_write_ciphers(pos, end, hapd->conf->wpa_pairwise,
+					" ");
+		if (ret < 0)
+			return pos - buf;
+		pos += ret;
+
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
+{
+	char *value;
+	int ret = 0;
+
+	value = os_strchr(cmd, ' ');
+	if (value == NULL)
+		return -1;
+	*value++ = '\0';
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value);
+	if (0) {
+#ifdef CONFIG_WPS_TESTING
+	} else if (os_strcasecmp(cmd, "wps_version_number") == 0) {
+		long int val;
+		val = strtol(value, NULL, 0);
+		if (val < 0 || val > 0xff) {
+			ret = -1;
+			wpa_printf(MSG_DEBUG, "WPS: Invalid "
+				   "wps_version_number %ld", val);
+		} else {
+			wps_version_number = val;
+			wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS "
+				   "version %u.%u",
+				   (wps_version_number & 0xf0) >> 4,
+				   wps_version_number & 0x0f);
+			hostapd_wps_update_ie(hapd);
+		}
+	} else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) {
+		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) {
+		int val = atoi(value);
+		if (val <= 0)
+			ret = -1;
+		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);
+	} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
+		hapd->ext_eapol_frame_io = 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;
+}
+
+
+static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
+				  char *buf, size_t buflen)
+{
+	int res;
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
+
+	if (os_strcmp(cmd, "version") == 0) {
+		res = os_snprintf(buf, buflen, "%s", VERSION_STR);
+		if (os_snprintf_error(buflen, res))
+			return -1;
+		return res;
+	} else if (os_strcmp(cmd, "tls_library") == 0) {
+		res = tls_get_library_version(buf, buflen);
+		if (os_snprintf_error(buflen, res))
+			return -1;
+		return res;
+	}
+
+	return -1;
+}
+
+
+static int hostapd_ctrl_iface_enable(struct hostapd_iface *iface)
+{
+	if (hostapd_enable_iface(iface) < 0) {
+		wpa_printf(MSG_ERROR, "Enabling of interface failed");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_reload(struct hostapd_iface *iface)
+{
+	if (hostapd_reload_iface(iface) < 0) {
+		wpa_printf(MSG_ERROR, "Reloading of interface failed");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface)
+{
+	if (hostapd_disable_iface(iface) < 0) {
+		wpa_printf(MSG_ERROR, "Disabling of interface failed");
+		return -1;
+	}
+	return 0;
+}
+
+
+#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;
+}
+
+
+static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd)
+{
+	char *pos;
+	u8 src[ETH_ALEN], *buf;
+	int used;
+	size_t len;
+
+	wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd);
+
+	pos = cmd;
+	used = hwaddr_aton2(pos, src);
+	if (used < 0)
+		return -1;
+	pos += used;
+	while (*pos == ' ')
+		pos++;
+
+	len = os_strlen(pos);
+	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;
+	}
+
+	ieee802_1x_receive(hapd, src, buf, len);
+	os_free(buf);
+
+	return 0;
+}
+
+
+static u16 ipv4_hdr_checksum(const void *buf, size_t len)
+{
+	size_t i;
+	u32 sum = 0;
+	const u16 *pos = buf;
+
+	for (i = 0; i < len / 2; i++)
+		sum += *pos++;
+
+	while (sum >> 16)
+		sum = (sum & 0xffff) + (sum >> 16);
+
+	return sum ^ 0xffff;
+}
+
+
+#define HWSIM_PACKETLEN 1500
+#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
+
+void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
+			  size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	const struct ether_header *eth;
+	struct iphdr ip;
+	const u8 *pos;
+	unsigned int i;
+
+	if (len != HWSIM_PACKETLEN)
+		return;
+
+	eth = (const struct ether_header *) buf;
+	os_memcpy(&ip, eth + 1, sizeof(ip));
+	pos = &buf[sizeof(*eth) + sizeof(ip)];
+
+	if (ip.ihl != 5 || ip.version != 4 ||
+	    ntohs(ip.tot_len) != HWSIM_IP_LEN)
+		return;
+
+	for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) {
+		if (*pos != (u8) i)
+			return;
+		pos++;
+	}
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR,
+		MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost));
+}
+
+
+static int hostapd_ctrl_iface_data_test_config(struct hostapd_data *hapd,
+					       char *cmd)
+{
+	int enabled = atoi(cmd);
+	char *pos;
+	const char *ifname;
+
+	if (!enabled) {
+		if (hapd->l2_test) {
+			l2_packet_deinit(hapd->l2_test);
+			hapd->l2_test = NULL;
+			wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+				"test data: Disabled");
+		}
+		return 0;
+	}
+
+	if (hapd->l2_test)
+		return 0;
+
+	pos = os_strstr(cmd, " ifname=");
+	if (pos)
+		ifname = pos + 8;
+	else
+		ifname = hapd->conf->iface;
+
+	hapd->l2_test = l2_packet_init(ifname, hapd->own_addr,
+					ETHERTYPE_IP, hostapd_data_test_rx,
+					hapd, 1);
+	if (hapd->l2_test == NULL)
+		return -1;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: Enabled");
+
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd)
+{
+	u8 dst[ETH_ALEN], src[ETH_ALEN];
+	char *pos;
+	int used;
+	long int val;
+	u8 tos;
+	u8 buf[2 + HWSIM_PACKETLEN];
+	struct ether_header *eth;
+	struct iphdr *ip;
+	u8 *dpos;
+	unsigned int i;
+
+	if (hapd->l2_test == NULL)
+		return -1;
+
+	/* format: <dst> <src> <tos> */
+
+	pos = cmd;
+	used = hwaddr_aton2(pos, dst);
+	if (used < 0)
+		return -1;
+	pos += used;
+	while (*pos == ' ')
+		pos++;
+	used = hwaddr_aton2(pos, src);
+	if (used < 0)
+		return -1;
+	pos += used;
+
+	val = strtol(pos, NULL, 0);
+	if (val < 0 || val > 0xff)
+		return -1;
+	tos = val;
+
+	eth = (struct ether_header *) &buf[2];
+	os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
+	os_memcpy(eth->ether_shost, src, ETH_ALEN);
+	eth->ether_type = htons(ETHERTYPE_IP);
+	ip = (struct iphdr *) (eth + 1);
+	os_memset(ip, 0, sizeof(*ip));
+	ip->ihl = 5;
+	ip->version = 4;
+	ip->ttl = 64;
+	ip->tos = tos;
+	ip->tot_len = htons(HWSIM_IP_LEN);
+	ip->protocol = 1;
+	ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
+	ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2);
+	ip->check = ipv4_hdr_checksum(ip, sizeof(*ip));
+	dpos = (u8 *) (ip + 1);
+	for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++)
+		*dpos++ = i;
+
+	if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2],
+			   HWSIM_PACKETLEN) < 0)
+		return -1;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX dst=" MACSTR
+		" src=" MACSTR " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos);
+
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_data_test_frame(struct hostapd_data *hapd,
+					      char *cmd)
+{
+	u8 *buf;
+	struct ether_header *eth;
+	struct l2_packet_data *l2 = NULL;
+	size_t len;
+	u16 ethertype;
+	int res = -1;
+	const char *ifname = hapd->conf->iface;
+
+	if (os_strncmp(cmd, "ifname=", 7) == 0) {
+		cmd += 7;
+		ifname = cmd;
+		cmd = os_strchr(cmd, ' ');
+		if (cmd == NULL)
+			return -1;
+		*cmd++ = '\0';
+	}
+
+	len = os_strlen(cmd);
+	if (len & 1 || len < ETH_HLEN * 2)
+		return -1;
+	len /= 2;
+
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+
+	if (hexstr2bin(cmd, buf, len) < 0)
+		goto done;
+
+	eth = (struct ether_header *) buf;
+	ethertype = ntohs(eth->ether_type);
+
+	l2 = l2_packet_init(ifname, hapd->own_addr, ethertype,
+			    hostapd_data_test_rx, hapd, 1);
+	if (l2 == NULL)
+		goto done;
+
+	res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len);
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX frame res=%d", res);
+done:
+	if (l2)
+		l2_packet_deinit(l2);
+	os_free(buf);
+
+	return res < 0 ? -1 : 0;
+}
+
+
+static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_fail_func[256];
+	extern unsigned int wpa_trace_fail_after;
+	char *pos;
+
+	wpa_trace_fail_after = atoi(cmd);
+	pos = os_strchr(cmd, ':');
+	if (pos) {
+		pos++;
+		os_strlcpy(wpa_trace_fail_func, pos,
+			   sizeof(wpa_trace_fail_func));
+	} else {
+		wpa_trace_fail_after = 0;
+	}
+
+	return 0;
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd,
+				       char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_fail_func[256];
+	extern unsigned int wpa_trace_fail_after;
+
+	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
+			   wpa_trace_fail_func);
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_test_fail_func[256];
+	extern unsigned int wpa_trace_test_fail_after;
+	char *pos;
+
+	wpa_trace_test_fail_after = atoi(cmd);
+	pos = os_strchr(cmd, ':');
+	if (pos) {
+		pos++;
+		os_strlcpy(wpa_trace_test_fail_func, pos,
+			   sizeof(wpa_trace_test_fail_func));
+	} else {
+		wpa_trace_test_fail_after = 0;
+	}
+
+	return 0;
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int hostapd_ctrl_get_fail(struct hostapd_data *hapd,
+				 char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_test_fail_func[256];
+	extern unsigned int wpa_trace_test_fail_after;
+
+	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
+			   wpa_trace_test_fail_func);
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+#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 int hostapd_ctrl_iface_eapol_reauth(struct hostapd_data *hapd,
+					   const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !sta->eapol_sm)
+		return -1;
+
+	eapol_auth_reauthenticate(sta->eapol_sm);
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	char *pos = cmd, *param;
+
+	if (hwaddr_aton(pos, addr) || pos[17] != ' ')
+		return -1;
+	pos += 18;
+	param = pos;
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return -1;
+	*pos++ = '\0';
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !sta->eapol_sm)
+		return -1;
+
+	return eapol_auth_set_conf(sta->eapol_sm, param, pos);
+}
+
+
+static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd,
+					char *buf, size_t buflen)
+{
+	char *pos, *end, *stamp;
+	int ret;
+
+	/* cmd: "LOG_LEVEL [<level>]" */
+	if (*cmd == '\0') {
+		pos = buf;
+		end = buf + buflen;
+		ret = os_snprintf(pos, end - pos, "Current level: %s\n"
+				  "Timestamp: %d\n",
+				  debug_level_str(wpa_debug_level),
+				  wpa_debug_timestamp);
+		if (os_snprintf_error(end - pos, ret))
+			ret = 0;
+
+		return ret;
+	}
+
+	while (*cmd == ' ')
+		cmd++;
+
+	stamp = os_strchr(cmd, ' ');
+	if (stamp) {
+		*stamp++ = '\0';
+		while (*stamp == ' ') {
+			stamp++;
+		}
+	}
+
+	if (os_strlen(cmd)) {
+		int level = str_to_debug_level(cmd);
+		if (level < 0)
+			return -1;
+		wpa_debug_level = level;
+	}
+
+	if (stamp && os_strlen(stamp))
+		wpa_debug_timestamp = atoi(stamp);
+
+	os_memcpy(buf, "OK\n", 3);
+	return 3;
+}
+
+
+#ifdef NEED_AP_MLME
+static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
+					     char *buf, size_t buflen)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	char *pos, *end;
+	struct hostapd_sta_info *info;
+	struct os_reltime now;
+
+	sta_track_expire(iface, 0);
+
+	pos = buf;
+	end = buf + buflen;
+
+	os_get_reltime(&now);
+	dl_list_for_each_reverse(info, &iface->sta_seen,
+				 struct hostapd_sta_info, list) {
+		struct os_reltime age;
+		int ret;
+
+		os_reltime_sub(&now, &info->last_seen, &age);
+		ret = os_snprintf(pos, end - pos, MACSTR " %u\n",
+				  MAC2STR(info->addr), (unsigned int) age.sec);
+		if (os_snprintf_error(end - pos, ret))
+			break;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+#endif /* NEED_AP_MLME */
+
+
+static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+					      char *buf, char *reply,
+					      int reply_size,
+					      struct sockaddr_un *from,
+					      socklen_t fromlen)
+{
+	int reply_len, res;
+
+	os_memcpy(reply, "OK\n", 3);
+	reply_len = 3;
+
+	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, "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) {
+			res = wpa_get_mib(hapd->wpa_auth, reply + reply_len,
+					  reply_size - reply_len);
+			if (res < 0)
+				reply_len = -1;
+			else
+				reply_len += res;
+		}
+		if (reply_len >= 0) {
+			res = ieee802_1x_get_mib(hapd, reply + reply_len,
+						 reply_size - reply_len);
+			if (res < 0)
+				reply_len = -1;
+			else
+				reply_len += res;
+		}
+#ifndef CONFIG_NO_RADIUS
+		if (reply_len >= 0) {
+			res = radius_client_get_mib(hapd->radius,
+						    reply + reply_len,
+						    reply_size - reply_len);
+			if (res < 0)
+				reply_len = -1;
+			else
+				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);
+	} else if (os_strncmp(buf, "STA ", 4) == 0) {
+		reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply,
+						   reply_size);
+	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
+		reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
+							reply_size);
+	} else if (os_strcmp(buf, "ATTACH") == 0) {
+		if (hostapd_ctrl_iface_attach(hapd, from, fromlen))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "DETACH") == 0) {
+		if (hostapd_ctrl_iface_detach(hapd, from, fromlen))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
+		if (hostapd_ctrl_iface_level(hapd, from, fromlen,
+						    buf + 6))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
+		if (hostapd_ctrl_iface_new_sta(hapd, buf + 8))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
+		if (hostapd_ctrl_iface_deauthenticate(hapd, buf + 15))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
+		if (hostapd_ctrl_iface_disassociate(hapd, buf + 13))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "STOP_AP") == 0) {
+		if (hostapd_ctrl_iface_stop_ap(hapd))
+			reply_len = -1;
+#ifdef CONFIG_IEEE80211W
+#ifdef NEED_AP_MLME
+	} else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) {
+		if (hostapd_ctrl_iface_sa_query(hapd, buf + 9))
+			reply_len = -1;
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_WPS
+	} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
+		if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
+		reply_len = hostapd_ctrl_iface_wps_check_pin(
+			hapd, buf + 14, reply, reply_size);
+	} else if (os_strcmp(buf, "WPS_PBC") == 0) {
+		if (hostapd_wps_button_pushed(hapd, NULL))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
+		if (hostapd_wps_cancel(hapd))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
+		reply_len = hostapd_ctrl_iface_wps_ap_pin(hapd, buf + 11,
+							  reply, reply_size);
+	} else if (os_strncmp(buf, "WPS_CONFIG ", 11) == 0) {
+		if (hostapd_ctrl_iface_wps_config(hapd, buf + 11) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_GET_STATUS", 13) == 0) {
+		reply_len = hostapd_ctrl_iface_wps_get_status(hapd, reply,
+							      reply_size);
+#ifdef CONFIG_WPS_NFC
+	} else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
+		if (hostapd_ctrl_iface_wps_nfc_tag_read(hapd, buf + 17))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) {
+		reply_len = hostapd_ctrl_iface_wps_nfc_config_token(
+			hapd, buf + 21, reply, reply_size);
+	} else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
+		reply_len = hostapd_ctrl_iface_wps_nfc_token(
+			hapd, buf + 14, reply, reply_size);
+	} else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
+		reply_len = hostapd_ctrl_iface_nfc_get_handover_sel(
+			hapd, buf + 21, reply, reply_size);
+	} else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) {
+		if (hostapd_ctrl_iface_nfc_report_handover(hapd, buf + 20))
+			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))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) {
+		if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
+		if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11))
+			reply_len = -1;
+#endif /* CONFIG_WNM */
+	} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
+		reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
+							  reply_size);
+	} else if (os_strncmp(buf, "SET ", 4) == 0) {
+		if (hostapd_ctrl_iface_set(hapd, buf + 4))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "GET ", 4) == 0) {
+		reply_len = hostapd_ctrl_iface_get(hapd, buf + 4, reply,
+						   reply_size);
+	} else if (os_strncmp(buf, "ENABLE", 6) == 0) {
+		if (hostapd_ctrl_iface_enable(hapd->iface))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "RELOAD", 6) == 0) {
+		if (hostapd_ctrl_iface_reload(hapd->iface))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DISABLE", 7) == 0) {
+		if (hostapd_ctrl_iface_disable(hapd->iface))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "UPDATE_BEACON") == 0) {
+		if (ieee802_11_set_beacon(hapd))
+			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;
+	} else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
+		if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) {
+		if (hostapd_ctrl_iface_data_test_config(hapd, buf + 17) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) {
+		if (hostapd_ctrl_iface_data_test_tx(hapd, buf + 13) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
+		if (hostapd_ctrl_iface_data_test_frame(hapd, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
+		if (hostapd_ctrl_test_alloc_fail(hapd, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
+		reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply,
+							reply_size);
+	} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
+		if (hostapd_ctrl_test_fail(hapd, buf + 10) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "GET_FAIL") == 0) {
+		reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size);
+#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 if (os_strcmp(buf, "ERP_FLUSH") == 0) {
+		ieee802_1x_erp_flush(hapd);
+#ifdef RADIUS_SERVER
+		radius_server_erp_flush(hapd->radius_srv);
+#endif /* RADIUS_SERVER */
+	} else if (os_strncmp(buf, "EAPOL_REAUTH ", 13) == 0) {
+		if (hostapd_ctrl_iface_eapol_reauth(hapd, buf + 13))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "EAPOL_SET ", 10) == 0) {
+		if (hostapd_ctrl_iface_eapol_set(hapd, buf + 10))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
+		reply_len = hostapd_ctrl_iface_log_level(
+			hapd, buf + 9, reply, reply_size);
+#ifdef NEED_AP_MLME
+	} else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) {
+		reply_len = hostapd_ctrl_iface_track_sta_list(
+			hapd, reply, reply_size);
+#endif /* NEED_AP_MLME */
+	} else {
+		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+		reply_len = 16;
+	}
+
+	if (reply_len < 0) {
+		os_memcpy(reply, "FAIL\n", 5);
+		reply_len = 5;
+	}
+
+	return reply_len;
+}
+
+
+static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
+				       void *sock_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	char buf[4096];
+	int res;
+	struct sockaddr_un from;
+	socklen_t fromlen = sizeof(from);
+	char *reply;
+	const int reply_size = 4096;
+	int reply_len;
+	int level = MSG_DEBUG;
+
+	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+		       (struct sockaddr *) &from, &fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+			   strerror(errno));
+		return;
+	}
+	buf[res] = '\0';
+	if (os_strcmp(buf, "PING") == 0)
+		level = MSG_EXCESSIVE;
+	wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res);
+
+	reply = os_malloc(reply_size);
+	if (reply == NULL) {
+		if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
+			   fromlen) < 0) {
+			wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
+				   strerror(errno));
+		}
+		return;
+	}
+
+	reply_len = hostapd_ctrl_iface_receive_process(hapd, buf,
+						       reply, reply_size,
+						       &from, fromlen);
+
+	if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+		   fromlen) < 0) {
+		wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
+			   strerror(errno));
+	}
+	os_free(reply);
+}
+
+
+static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
+{
+	char *buf;
+	size_t len;
+
+	if (hapd->conf->ctrl_interface == NULL)
+		return NULL;
+
+	len = os_strlen(hapd->conf->ctrl_interface) +
+		os_strlen(hapd->conf->iface) + 2;
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return NULL;
+
+	os_snprintf(buf, len, "%s/%s",
+		    hapd->conf->ctrl_interface, hapd->conf->iface);
+	buf[len - 1] = '\0';
+	return buf;
+}
+
+
+static void hostapd_ctrl_iface_msg_cb(void *ctx, int level,
+				      enum wpa_msg_type type,
+				      const char *txt, size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	if (hapd == NULL)
+		return;
+	hostapd_ctrl_iface_send(hapd, level, type, txt, len);
+}
+
+
+int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
+{
+	struct sockaddr_un addr;
+	int s = -1;
+	char *fname = NULL;
+
+	if (hapd->ctrl_sock > -1) {
+		wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
+		return 0;
+	}
+
+	if (hapd->conf->ctrl_interface == NULL)
+		return 0;
+
+	if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
+		if (errno == EEXIST) {
+			wpa_printf(MSG_DEBUG, "Using existing control "
+				   "interface directory.");
+		} else {
+			wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
+				   strerror(errno));
+			goto fail;
+		}
+	}
+
+	if (hapd->conf->ctrl_interface_gid_set &&
+	    chown(hapd->conf->ctrl_interface, -1,
+		  hapd->conf->ctrl_interface_gid) < 0) {
+		wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (!hapd->conf->ctrl_interface_gid_set &&
+	    hapd->iface->interfaces->ctrl_iface_group &&
+	    chown(hapd->conf->ctrl_interface, -1,
+		  hapd->iface->interfaces->ctrl_iface_group) < 0) {
+		wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+#ifdef ANDROID
+	/*
+	 * Android is using umask 0077 which would leave the control interface
+	 * directory without group access. This breaks things since Wi-Fi
+	 * framework assumes that this directory can be accessed by other
+	 * applications in the wifi group. Fix this by adding group access even
+	 * if umask value would prevent this.
+	 */
+	if (chmod(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
+		wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s",
+			   strerror(errno));
+		/* Try to continue anyway */
+	}
+#endif /* ANDROID */
+
+	if (os_strlen(hapd->conf->ctrl_interface) + 1 +
+	    os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
+		goto fail;
+
+	s = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
+		goto fail;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+#ifdef __FreeBSD__
+	addr.sun_len = sizeof(addr);
+#endif /* __FreeBSD__ */
+	addr.sun_family = AF_UNIX;
+	fname = hostapd_ctrl_iface_path(hapd);
+	if (fname == NULL)
+		goto fail;
+	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
+	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
+			   strerror(errno));
+		if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
+				   " allow connections - assuming it was left"
+				   "over from forced program termination");
+			if (unlink(fname) < 0) {
+				wpa_printf(MSG_ERROR,
+					   "Could not unlink existing ctrl_iface socket '%s': %s",
+					   fname, strerror(errno));
+				goto fail;
+			}
+			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
+			    0) {
+				wpa_printf(MSG_ERROR,
+					   "hostapd-ctrl-iface: bind(PF_UNIX): %s",
+					   strerror(errno));
+				goto fail;
+			}
+			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
+				   "ctrl_iface socket '%s'", fname);
+		} else {
+			wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
+				   "be in use - cannot override it");
+			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
+				   "not used anymore", fname);
+			os_free(fname);
+			fname = NULL;
+			goto fail;
+		}
+	}
+
+	if (hapd->conf->ctrl_interface_gid_set &&
+	    chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) {
+		wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	if (!hapd->conf->ctrl_interface_gid_set &&
+	    hapd->iface->interfaces->ctrl_iface_group &&
+	    chown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) {
+		wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
+		wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
+			   strerror(errno));
+		goto fail;
+	}
+	os_free(fname);
+
+	hapd->ctrl_sock = s;
+	if (eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
+				     NULL) < 0) {
+		hostapd_ctrl_iface_deinit(hapd);
+		return -1;
+	}
+	hapd->msg_ctx = hapd;
+	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
+
+	return 0;
+
+fail:
+	if (s >= 0)
+		close(s);
+	if (fname) {
+		unlink(fname);
+		os_free(fname);
+	}
+	return -1;
+}
+
+
+void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
+{
+	struct wpa_ctrl_dst *dst, *prev;
+
+	if (hapd->ctrl_sock > -1) {
+		char *fname;
+		eloop_unregister_read_sock(hapd->ctrl_sock);
+		close(hapd->ctrl_sock);
+		hapd->ctrl_sock = -1;
+		fname = hostapd_ctrl_iface_path(hapd);
+		if (fname)
+			unlink(fname);
+		os_free(fname);
+
+		if (hapd->conf->ctrl_interface &&
+		    rmdir(hapd->conf->ctrl_interface) < 0) {
+			if (errno == ENOTEMPTY) {
+				wpa_printf(MSG_DEBUG, "Control interface "
+					   "directory not empty - leaving it "
+					   "behind");
+			} else {
+				wpa_printf(MSG_ERROR,
+					   "rmdir[ctrl_interface=%s]: %s",
+					   hapd->conf->ctrl_interface,
+					   strerror(errno));
+			}
+		}
+	}
+
+	dst = hapd->ctrl_dst;
+	hapd->ctrl_dst = NULL;
+	while (dst) {
+		prev = dst;
+		dst = dst->next;
+		os_free(prev);
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	l2_packet_deinit(hapd->l2_test);
+	hapd->l2_test = NULL;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static int hostapd_ctrl_iface_add(struct hapd_interfaces *interfaces,
+				  char *buf)
+{
+	if (hostapd_add_iface(interfaces, buf) < 0) {
+		wpa_printf(MSG_ERROR, "Adding interface %s failed", buf);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces,
+				     char *buf)
+{
+	if (hostapd_remove_iface(interfaces, buf) < 0) {
+		wpa_printf(MSG_ERROR, "Removing interface %s failed", buf);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces,
+					    struct sockaddr_un *from,
+					    socklen_t fromlen)
+{
+	struct wpa_ctrl_dst *dst;
+
+	dst = os_zalloc(sizeof(*dst));
+	if (dst == NULL)
+		return -1;
+	os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
+	dst->addrlen = fromlen;
+	dst->debug_level = MSG_INFO;
+	dst->next = interfaces->global_ctrl_dst;
+	interfaces->global_ctrl_dst = dst;
+	wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached (global)",
+		    from->sun_path,
+		    fromlen - offsetof(struct sockaddr_un, sun_path));
+	return 0;
+}
+
+
+static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces,
+					    struct sockaddr_un *from,
+					    socklen_t fromlen)
+{
+	struct wpa_ctrl_dst *dst, *prev = NULL;
+
+	dst = interfaces->global_ctrl_dst;
+	while (dst) {
+		if (fromlen == dst->addrlen &&
+		    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 (global)",
+				    from->sun_path,
+				    fromlen -
+				    offsetof(struct sockaddr_un, sun_path));
+			if (prev == NULL)
+				interfaces->global_ctrl_dst = dst->next;
+			else
+				prev->next = dst->next;
+			os_free(dst);
+			return 0;
+		}
+		prev = dst;
+		dst = dst->next;
+	}
+	return -1;
+}
+
+
+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 */
+}
+
+
+#ifdef CONFIG_FST
+
+static int
+hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces,
+				     const char *cmd)
+{
+	char ifname[IFNAMSIZ + 1];
+	struct fst_iface_cfg cfg;
+	struct hostapd_data *hapd;
+	struct fst_wpa_obj iface_obj;
+
+	if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
+		hapd = hostapd_get_iface(interfaces, ifname);
+		if (hapd) {
+			if (hapd->iface->fst) {
+				wpa_printf(MSG_INFO, "FST: Already attached");
+				return -1;
+			}
+			fst_hostapd_fill_iface_obj(hapd, &iface_obj);
+			hapd->iface->fst = fst_attach(ifname, hapd->own_addr,
+						      &iface_obj, &cfg);
+			if (hapd->iface->fst)
+				return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+
+static int
+hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces *interfaces,
+				     const char *cmd)
+{
+	char ifname[IFNAMSIZ + 1];
+	struct hostapd_data * hapd;
+
+	if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
+		hapd = hostapd_get_iface(interfaces, ifname);
+		if (hapd) {
+			if (!fst_iface_detach(ifname)) {
+				hapd->iface->fst = NULL;
+				hapd->iface->fst_ies = NULL;
+				return 0;
+			}
+		}
+	}
+
+	return -EINVAL;
+}
+
+#endif /* CONFIG_FST */
+
+
+static struct hostapd_data *
+hostapd_interfaces_get_hapd(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;
+
+			hapd = iface->bss[j];
+			if (os_strcmp(ifname, hapd->conf->iface) == 0)
+				return hapd;
+		}
+	}
+
+	return NULL;
+}
+
+
+static int hostapd_ctrl_iface_dup_param(struct hostapd_data *src_hapd,
+					struct hostapd_data *dst_hapd,
+					const char *param)
+{
+	int res;
+	char *value;
+
+	value = os_zalloc(HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
+	if (!value) {
+		wpa_printf(MSG_ERROR,
+			   "DUP: cannot allocate buffer to stringify %s",
+			   param);
+		goto error_return;
+	}
+
+	if (os_strcmp(param, "wpa") == 0) {
+		os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%d",
+			    src_hapd->conf->wpa);
+	} else if (os_strcmp(param, "wpa_key_mgmt") == 0 &&
+		   src_hapd->conf->wpa_key_mgmt) {
+		res = hostapd_ctrl_iface_get_key_mgmt(
+			src_hapd, value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
+		if (os_snprintf_error(HOSTAPD_CLI_DUP_VALUE_MAX_LEN, res))
+			goto error_stringify;
+	} else if (os_strcmp(param, "wpa_pairwise") == 0 &&
+		   src_hapd->conf->wpa_pairwise) {
+		res = wpa_write_ciphers(value,
+					value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
+					src_hapd->conf->wpa_pairwise, " ");
+		if (res < 0)
+			goto error_stringify;
+	} else if (os_strcmp(param, "rsn_pairwise") == 0 &&
+		   src_hapd->conf->rsn_pairwise) {
+		res = wpa_write_ciphers(value,
+					value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
+					src_hapd->conf->rsn_pairwise, " ");
+		if (res < 0)
+			goto error_stringify;
+	} else if (os_strcmp(param, "wpa_passphrase") == 0 &&
+		   src_hapd->conf->ssid.wpa_passphrase) {
+		os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%s",
+			    src_hapd->conf->ssid.wpa_passphrase);
+	} else if (os_strcmp(param, "wpa_psk") == 0 &&
+		   src_hapd->conf->ssid.wpa_psk_set) {
+		wpa_snprintf_hex(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
+			src_hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
+	} else {
+		wpa_printf(MSG_WARNING, "DUP: %s cannot be duplicated", param);
+		goto error_return;
+	}
+
+	res = hostapd_set_iface(dst_hapd->iconf, dst_hapd->conf, param, value);
+	os_free(value);
+	return res;
+
+error_stringify:
+	wpa_printf(MSG_ERROR, "DUP: cannot stringify %s", param);
+error_return:
+	os_free(value);
+	return -1;
+}
+
+
+static int
+hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces,
+				      char *cmd)
+{
+	char *p_start = cmd, *p_end;
+	struct hostapd_data *src_hapd, *dst_hapd;
+
+	/* cmd: "<src ifname> <dst ifname> <variable name> */
+
+	p_end = os_strchr(p_start, ' ');
+	if (!p_end) {
+		wpa_printf(MSG_ERROR, "DUP: no src ifname found in cmd: '%s'",
+			   cmd);
+		return -1;
+	}
+
+	*p_end = '\0';
+	src_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
+	if (!src_hapd) {
+		wpa_printf(MSG_ERROR, "DUP: no src ifname found: '%s'",
+			   p_start);
+		return -1;
+	}
+
+	p_start = p_end + 1;
+	p_end = os_strchr(p_start, ' ');
+	if (!p_end) {
+		wpa_printf(MSG_ERROR, "DUP: no dst ifname found in cmd: '%s'",
+			   cmd);
+		return -1;
+	}
+
+	*p_end = '\0';
+	dst_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
+	if (!dst_hapd) {
+		wpa_printf(MSG_ERROR, "DUP: no dst ifname found: '%s'",
+			   p_start);
+		return -1;
+	}
+
+	p_start = p_end + 1;
+	return hostapd_ctrl_iface_dup_param(src_hapd, dst_hapd, p_start);
+}
+
+
+static int hostapd_global_ctrl_iface_ifname(struct hapd_interfaces *interfaces,
+					    const char *ifname,
+					    char *buf, char *reply,
+					    int reply_size,
+					    struct sockaddr_un *from,
+					    socklen_t fromlen)
+{
+	struct hostapd_data *hapd;
+
+	hapd = hostapd_interfaces_get_hapd(interfaces, ifname);
+	if (hapd == NULL) {
+		int res;
+
+		res = os_snprintf(reply, reply_size, "FAIL-NO-IFNAME-MATCH\n");
+		if (os_snprintf_error(reply_size, res))
+			return -1;
+		return res;
+	}
+
+	return hostapd_ctrl_iface_receive_process(hapd, buf, reply,reply_size,
+						  from, fromlen);
+}
+
+
+static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
+					      void *sock_ctx)
+{
+	void *interfaces = eloop_ctx;
+	char buf[256];
+	int res;
+	struct sockaddr_un from;
+	socklen_t fromlen = sizeof(from);
+	char *reply;
+	int reply_len;
+	const int reply_size = 4096;
+
+	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+		       (struct sockaddr *) &from, &fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+			   strerror(errno));
+		return;
+	}
+	buf[res] = '\0';
+	wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf);
+
+	reply = os_malloc(reply_size);
+	if (reply == NULL) {
+		if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
+			   fromlen) < 0) {
+			wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
+				   strerror(errno));
+		}
+		return;
+	}
+
+	os_memcpy(reply, "OK\n", 3);
+	reply_len = 3;
+
+	if (os_strncmp(buf, "IFNAME=", 7) == 0) {
+		char *pos = os_strchr(buf + 7, ' ');
+
+		if (pos) {
+			*pos++ = '\0';
+			reply_len = hostapd_global_ctrl_iface_ifname(
+				interfaces, buf + 7, pos, reply, reply_size,
+				&from, fromlen);
+			goto send_reply;
+		}
+	}
+
+	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;
+	} else if (os_strcmp(buf, "ATTACH") == 0) {
+		if (hostapd_global_ctrl_iface_attach(interfaces, &from,
+						     fromlen))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "DETACH") == 0) {
+		if (hostapd_global_ctrl_iface_detach(interfaces, &from,
+			fromlen))
+			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 */
+#ifdef CONFIG_FST
+	} else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
+		if (!hostapd_global_ctrl_iface_fst_attach(interfaces, buf + 11))
+			reply_len = os_snprintf(reply, reply_size, "OK\n");
+		else
+			reply_len = -1;
+	} else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
+		if (!hostapd_global_ctrl_iface_fst_detach(interfaces, buf + 11))
+			reply_len = os_snprintf(reply, reply_size, "OK\n");
+		else
+			reply_len = -1;
+	} else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
+		reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
+#endif /* CONFIG_FST */
+	} else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
+		if (!hostapd_global_ctrl_iface_dup_network(interfaces,
+							   buf + 12))
+			reply_len = os_snprintf(reply, reply_size, "OK\n");
+		else
+			reply_len = -1;
+	} else {
+		wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
+			   "ignored");
+		reply_len = -1;
+	}
+
+send_reply:
+	if (reply_len < 0) {
+		os_memcpy(reply, "FAIL\n", 5);
+		reply_len = 5;
+	}
+
+	if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+		   fromlen) < 0) {
+		wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
+			   strerror(errno));
+	}
+	os_free(reply);
+}
+
+
+static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface)
+{
+	char *buf;
+	size_t len;
+
+	if (interface->global_iface_path == NULL)
+		return NULL;
+
+	len = os_strlen(interface->global_iface_path) +
+		os_strlen(interface->global_iface_name) + 2;
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return NULL;
+
+	os_snprintf(buf, len, "%s/%s", interface->global_iface_path,
+		    interface->global_iface_name);
+	buf[len - 1] = '\0';
+	return buf;
+}
+
+
+int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
+{
+	struct sockaddr_un addr;
+	int s = -1;
+	char *fname = NULL;
+
+	if (interface->global_iface_path == NULL) {
+		wpa_printf(MSG_DEBUG, "ctrl_iface not configured!");
+		return 0;
+	}
+
+	if (mkdir(interface->global_iface_path, S_IRWXU | S_IRWXG) < 0) {
+		if (errno == EEXIST) {
+			wpa_printf(MSG_DEBUG, "Using existing control "
+				   "interface directory.");
+		} else {
+			wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
+				   strerror(errno));
+			goto fail;
+		}
+	} else if (interface->ctrl_iface_group &&
+		   chown(interface->global_iface_path, -1,
+			 interface->ctrl_iface_group) < 0) {
+		wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	if (os_strlen(interface->global_iface_path) + 1 +
+	    os_strlen(interface->global_iface_name) >= sizeof(addr.sun_path))
+		goto fail;
+
+	s = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
+		goto fail;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+#ifdef __FreeBSD__
+	addr.sun_len = sizeof(addr);
+#endif /* __FreeBSD__ */
+	addr.sun_family = AF_UNIX;
+	fname = hostapd_global_ctrl_iface_path(interface);
+	if (fname == NULL)
+		goto fail;
+	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
+	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
+			   strerror(errno));
+		if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
+				   " allow connections - assuming it was left"
+				   "over from forced program termination");
+			if (unlink(fname) < 0) {
+				wpa_printf(MSG_ERROR,
+					   "Could not unlink existing ctrl_iface socket '%s': %s",
+					   fname, strerror(errno));
+				goto fail;
+			}
+			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
+			    0) {
+				wpa_printf(MSG_ERROR, "bind(PF_UNIX): %s",
+					   strerror(errno));
+				goto fail;
+			}
+			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
+				   "ctrl_iface socket '%s'", fname);
+		} else {
+			wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
+				   "be in use - cannot override it");
+			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
+				   "not used anymore", fname);
+			os_free(fname);
+			fname = NULL;
+			goto fail;
+		}
+	}
+
+	if (interface->ctrl_iface_group &&
+	    chown(fname, -1, interface->ctrl_iface_group) < 0) {
+		wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
+		wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
+			   strerror(errno));
+		goto fail;
+	}
+	os_free(fname);
+
+	interface->global_ctrl_sock = s;
+	eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
+				 interface, NULL);
+
+	return 0;
+
+fail:
+	if (s >= 0)
+		close(s);
+	if (fname) {
+		unlink(fname);
+		os_free(fname);
+	}
+	return -1;
+}
+
+
+void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
+{
+	char *fname = NULL;
+	struct wpa_ctrl_dst *dst, *prev;
+
+	if (interfaces->global_ctrl_sock > -1) {
+		eloop_unregister_read_sock(interfaces->global_ctrl_sock);
+		close(interfaces->global_ctrl_sock);
+		interfaces->global_ctrl_sock = -1;
+		fname = hostapd_global_ctrl_iface_path(interfaces);
+		if (fname) {
+			unlink(fname);
+			os_free(fname);
+		}
+
+		if (interfaces->global_iface_path &&
+		    rmdir(interfaces->global_iface_path) < 0) {
+			if (errno == ENOTEMPTY) {
+				wpa_printf(MSG_DEBUG, "Control interface "
+					   "directory not empty - leaving it "
+					   "behind");
+			} else {
+				wpa_printf(MSG_ERROR,
+					   "rmdir[ctrl_interface=%s]: %s",
+					   interfaces->global_iface_path,
+					   strerror(errno));
+			}
+		}
+	}
+
+	os_free(interfaces->global_iface_path);
+	interfaces->global_iface_path = NULL;
+
+	dst = interfaces->global_ctrl_dst;
+	interfaces->global_ctrl_dst = NULL;
+	while (dst) {
+		prev = dst;
+		dst = dst->next;
+		os_free(prev);
+	}
+}
+
+
+static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
+				    enum wpa_msg_type type,
+				    const char *buf, size_t len)
+{
+	struct wpa_ctrl_dst *dst, *next;
+	struct msghdr msg;
+	int idx;
+	struct iovec io[2];
+	char levelstr[10];
+	int s;
+
+	if (type != WPA_MSG_ONLY_GLOBAL) {
+		s = hapd->ctrl_sock;
+		dst = hapd->ctrl_dst;
+	} else {
+		s = hapd->iface->interfaces->global_ctrl_sock;
+		dst = hapd->iface->interfaces->global_ctrl_dst;
+	}
+
+	if (s < 0 || dst == NULL)
+		return;
+
+	os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
+	io[0].iov_base = levelstr;
+	io[0].iov_len = os_strlen(levelstr);
+	io[1].iov_base = (char *) buf;
+	io[1].iov_len = len;
+	os_memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 2;
+
+	idx = 0;
+	while (dst) {
+		next = dst->next;
+		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 = &dst->addr;
+			msg.msg_namelen = dst->addrlen;
+			if (sendmsg(s, &msg, 0) < 0) {
+				int _errno = errno;
+				wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
+					   "%d - %s",
+					   idx, errno, strerror(errno));
+				dst->errors++;
+				if (dst->errors > 10 || _errno == ENOENT) {
+					if (type != WPA_MSG_ONLY_GLOBAL)
+						hostapd_ctrl_iface_detach(
+							hapd, &dst->addr,
+							dst->addrlen);
+					else
+						hostapd_global_ctrl_iface_detach(
+							hapd->iface->interfaces,
+							&dst->addr,
+							dst->addrlen);
+				}
+			} else
+				dst->errors = 0;
+		}
+		idx++;
+		dst = next;
+	}
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/hostap/hostapd/ctrl_iface.h b/hostap/hostapd/ctrl_iface.h
new file mode 100644
index 0000000..3341a66
--- /dev/null
+++ b/hostap/hostapd/ctrl_iface.h
@@ -0,0 +1,39 @@
+/*
+ * hostapd / UNIX domain socket -based control interface
+ * Copyright (c) 2004, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CTRL_IFACE_H
+#define CTRL_IFACE_H
+
+#ifndef CONFIG_NO_CTRL_IFACE
+int hostapd_ctrl_iface_init(struct hostapd_data *hapd);
+void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd);
+int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface);
+void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface);
+#else /* CONFIG_NO_CTRL_IFACE */
+static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline int
+hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
+{
+	return 0;
+}
+
+static inline void
+hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface)
+{
+}
+#endif /* CONFIG_NO_CTRL_IFACE */
+
+#endif /* CTRL_IFACE_H */
diff --git a/hostap/hostapd/defconfig b/hostap/hostapd/defconfig
new file mode 100644
index 0000000..430f758
--- /dev/null
+++ b/hostap/hostapd/defconfig
@@ -0,0 +1,328 @@
+# Example hostapd build time configuration
+#
+# This file lists the configuration options that are used when building the
+# hostapd binary. All lines starting with # are ignored. Configuration option
+# lines must be commented out complete, if they are not to be included, i.e.,
+# just setting VARIABLE=n is not disabling that variable.
+#
+# This file is included in Makefile, so variables like CFLAGS and LIBS can also
+# be modified from here. In most cass, these lines should use += in order not
+# to override previous values of the variables.
+
+# Driver interface for Host AP driver
+CONFIG_DRIVER_HOSTAP=y
+
+# Driver interface for wired authenticator
+#CONFIG_DRIVER_WIRED=y
+
+# 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
+#LIBS += -L/usr/local/lib
+#LIBS_p += -L/usr/local/lib
+#LIBS_c += -L/usr/local/lib
+
+# Driver interface for no driver (e.g., RADIUS server only)
+#CONFIG_DRIVER_NONE=y
+
+# IEEE 802.11F/IAPP
+CONFIG_IAPP=y
+
+# WPA2/IEEE 802.11i RSN pre-authentication
+CONFIG_RSN_PREAUTH=y
+
+# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
+CONFIG_PEERKEY=y
+
+# IEEE 802.11w (management frame protection)
+CONFIG_IEEE80211W=y
+
+# Integrated EAP server
+CONFIG_EAP=y
+
+# EAP Re-authentication Protocol (ERP) in integrated EAP server
+CONFIG_ERP=y
+
+# EAP-MD5 for the integrated EAP server
+CONFIG_EAP_MD5=y
+
+# EAP-TLS for the integrated EAP server
+CONFIG_EAP_TLS=y
+
+# EAP-MSCHAPv2 for the integrated EAP server
+CONFIG_EAP_MSCHAPV2=y
+
+# EAP-PEAP for the integrated EAP server
+CONFIG_EAP_PEAP=y
+
+# EAP-GTC for the integrated EAP server
+CONFIG_EAP_GTC=y
+
+# EAP-TTLS for the integrated EAP server
+CONFIG_EAP_TTLS=y
+
+# EAP-SIM for the integrated EAP server
+#CONFIG_EAP_SIM=y
+
+# EAP-AKA for the integrated EAP server
+#CONFIG_EAP_AKA=y
+
+# EAP-AKA' for the integrated EAP server
+# This requires CONFIG_EAP_AKA to be enabled, too.
+#CONFIG_EAP_AKA_PRIME=y
+
+# EAP-PAX for the integrated EAP server
+#CONFIG_EAP_PAX=y
+
+# EAP-PSK for the integrated EAP server (this is _not_ needed for WPA-PSK)
+#CONFIG_EAP_PSK=y
+
+# EAP-pwd for the integrated EAP server (secure authentication with a password)
+#CONFIG_EAP_PWD=y
+
+# EAP-SAKE for the integrated EAP server
+#CONFIG_EAP_SAKE=y
+
+# EAP-GPSK for the integrated EAP server
+#CONFIG_EAP_GPSK=y
+# Include support for optional SHA256 cipher suite in EAP-GPSK
+#CONFIG_EAP_GPSK_SHA256=y
+
+# EAP-FAST for the integrated EAP server
+# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed
+# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g.,
+# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions.
+#CONFIG_EAP_FAST=y
+
+# Wi-Fi Protected Setup (WPS)
+#CONFIG_WPS=y
+# Enable UPnP support for external WPS Registrars
+#CONFIG_WPS_UPNP=y
+# Enable WPS support with NFC config method
+#CONFIG_WPS_NFC=y
+
+# EAP-IKEv2
+#CONFIG_EAP_IKEV2=y
+
+# Trusted Network Connect (EAP-TNC)
+#CONFIG_EAP_TNC=y
+
+# EAP-EKE for the integrated EAP server
+#CONFIG_EAP_EKE=y
+
+# PKCS#12 (PFX) support (used to read private key and certificate file from
+# a file that usually has extension .p12 or .pfx)
+CONFIG_PKCS12=y
+
+# RADIUS authentication server. This provides access to the integrated EAP
+# server from external hosts using RADIUS.
+#CONFIG_RADIUS_SERVER=y
+
+# Build IPv6 support for RADIUS operations
+CONFIG_IPV6=y
+
+# IEEE Std 802.11r-2008 (Fast BSS Transition)
+#CONFIG_IEEE80211R=y
+
+# Use the hostapd's IEEE 802.11 authentication (ACL), but without
+# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
+#CONFIG_DRIVER_RADIUS_ACL=y
+
+# IEEE 802.11n (High Throughput) support
+#CONFIG_IEEE80211N=y
+
+# Wireless Network Management (IEEE Std 802.11v-2011)
+# Note: This is experimental and not complete implementation.
+#CONFIG_WNM=y
+
+# IEEE 802.11ac (Very High Throughput) support
+#CONFIG_IEEE80211AC=y
+
+# Remove debugging code that is printing out debug messages to stdout.
+# This can be used to reduce the size of the hostapd considerably if debugging
+# code is not needed.
+#CONFIG_NO_STDOUT_DEBUG=y
+
+# Add support for writing debug log to a file: -f /tmp/hostapd.log
+# 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
+
+# Remove support for RADIUS
+#CONFIG_NO_RADIUS=y
+
+# Remove support for VLANs
+#CONFIG_NO_VLAN=y
+
+# Enable support for fully dynamic VLANs. This enables hostapd to
+# automatically create bridge and VLAN interfaces if necessary.
+#CONFIG_FULL_DYNAMIC_VLAN=y
+
+# Use netlink-based kernel API for VLAN operations instead of ioctl()
+# Note: This requires libnl 3.1 or newer.
+#CONFIG_VLAN_NETLINK=y
+
+# 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
+
+# Enable tracing code for developer debugging
+# This tracks use of memory allocations and other registrations and reports
+# incorrect use with a backtrace of call (or allocation) location.
+#CONFIG_WPA_TRACE=y
+# For BSD, comment out these.
+#LIBS += -lexecinfo
+#LIBS_p += -lexecinfo
+#LIBS_c += -lexecinfo
+
+# Use libbfd to get more details for developer debugging
+# This enables use of libbfd to get more detailed symbols for the backtraces
+# generated by CONFIG_WPA_TRACE=y.
+#CONFIG_WPA_TRACE_BFD=y
+# For BSD, comment out these.
+#LIBS += -lbfd -liberty -lz
+#LIBS_p += -lbfd -liberty -lz
+#LIBS_c += -lbfd -liberty -lz
+
+# hostapd depends on strong random number generation being available from the
+# operating system. os_get_random() function is used to fetch random data when
+# needed, e.g., for key generation. On Linux and BSD systems, this works by
+# reading /dev/urandom. It should be noted that the OS entropy pool needs to be
+# properly initialized before hostapd is started. This is important especially
+# on embedded devices that do not have a hardware random number generator and
+# may by default start up with minimal entropy available for random number
+# generation.
+#
+# As a safety net, hostapd is by default trying to internally collect
+# additional entropy for generating random data to mix in with the data
+# fetched from the OS. This by itself is not considered to be very strong, but
+# it may help in cases where the system pool is not initialized properly.
+# However, it is very strongly recommended that the system pool is initialized
+# with enough entropy either by using hardware assisted random number
+# generator or by storing state over device reboots.
+#
+# hostapd can be configured to maintain its own entropy store over restarts to
+# enhance random number generation. This is not perfect, but it is much more
+# secure than using the same sequence of random numbers after every reboot.
+# This can be enabled with -e<entropy file> command line option. The specified
+# file needs to be readable and writable by hostapd.
+#
+# If the os_get_random() is known to provide strong random data (e.g., on
+# Linux/BSD, the board in question is known to have reliable source of random
+# data from /dev/urandom), the internal hostapd random pool can be disabled.
+# This will save some in binary size and CPU use. However, this should only be
+# considered for builds that are known to be used on devices that meet the
+# requirements described above.
+#CONFIG_NO_RANDOM_POOL=y
+
+# 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 TLS implementation
+# openssl = OpenSSL (default)
+# gnutls = GnuTLS
+# internal = Internal TLSv1 implementation (experimental)
+# none = Empty template
+#CONFIG_TLS=openssl
+
+# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1)
+# can be enabled to get a stronger construction of messages when block ciphers
+# are used.
+#CONFIG_TLSV11=y
+
+# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2)
+# can be enabled to enable use of stronger crypto algorithms.
+#CONFIG_TLSV12=y
+
+# If CONFIG_TLS=internal is used, additional library and include paths are
+# needed for LibTomMath. Alternatively, an integrated, minimal version of
+# LibTomMath can be used. See beginning of libtommath.c for details on benefits
+# and drawbacks of this option.
+#CONFIG_INTERNAL_LIBTOMMATH=y
+#ifndef CONFIG_INTERNAL_LIBTOMMATH
+#LTM_PATH=/usr/src/libtommath-0.39
+#CFLAGS += -I$(LTM_PATH)
+#LIBS += -L$(LTM_PATH)
+#LIBS_p += -L$(LTM_PATH)
+#endif
+# At the cost of about 4 kB of additional binary size, the internal LibTomMath
+# can be configured to include faster routines for exptmod, sqr, and div to
+# speed up DH and RSA calculation considerably
+#CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+
+# Interworking (IEEE 802.11u)
+# This can be used to enable functionality to improve interworking with
+# external networks.
+#CONFIG_INTERWORKING=y
+
+# Hotspot 2.0
+#CONFIG_HS20=y
+
+# Enable SQLite database support in hlr_auc_gw, EAP-SIM DB, and eap_user_file
+#CONFIG_SQLITE=y
+
+# Enable Fast Session Transfer (FST)
+#CONFIG_FST=y
+
+# Enable CLI commands for FST testing
+#CONFIG_FST_TEST=y
+
+# Testing options
+# This can be used to enable some testing options (see also the example
+# configuration file) that are really useful only for testing clients that
+# connect to this hostapd. These options allow, for example, to drop a
+# certain percentage of probe requests or auth/(re)assoc frames.
+#
+#CONFIG_TESTING_OPTIONS=y
+
+# Automatic Channel Selection
+# This will allow hostapd to pick the channel automatically when channel is set
+# to "acs_survey" or "0". Eventually, other ACS algorithms can be added in
+# similar way.
+#
+# Automatic selection is currently only done through initialization, later on
+# we hope to do background checks to keep us moving to more ideal channels as
+# time goes by. ACS is currently only supported through the nl80211 driver and
+# your driver must have survey dump capability that is filled by the driver
+# during scanning.
+#
+# You can customize the ACS survey algorithm with the hostapd.conf variable
+# acs_num_scans.
+#
+# Supported ACS drivers:
+# * ath9k
+# * ath5k
+# * ath10k
+#
+# For more details refer to:
+# http://wireless.kernel.org/en/users/Documentation/acs
+#
+#CONFIG_ACS=y
diff --git a/hostap/hostapd/eap_register.c b/hostap/hostapd/eap_register.c
new file mode 100644
index 0000000..8477c21
--- /dev/null
+++ b/hostap/hostapd/eap_register.c
@@ -0,0 +1,150 @@
+/*
+ * EAP method registration
+ * 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 "common.h"
+#include "eap_server/eap_methods.h"
+#include "eap_register.h"
+
+
+/**
+ * eap_server_register_methods - Register statically linked EAP server methods
+ * Returns: 0 on success, -1 or -2 on failure
+ *
+ * This function is called at program initialization to register all EAP
+ * methods that were linked in statically.
+ */
+int eap_server_register_methods(void)
+{
+	int ret = 0;
+
+#ifdef EAP_SERVER_IDENTITY
+	if (ret == 0)
+		ret = eap_server_identity_register();
+#endif /* EAP_SERVER_IDENTITY */
+
+#ifdef EAP_SERVER_MD5
+	if (ret == 0)
+		ret = eap_server_md5_register();
+#endif /* EAP_SERVER_MD5 */
+
+#ifdef EAP_SERVER_TLS
+	if (ret == 0)
+		ret = eap_server_tls_register();
+#endif /* EAP_SERVER_TLS */
+
+#ifdef EAP_SERVER_UNAUTH_TLS
+	if (ret == 0)
+		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();
+#endif /* EAP_SERVER_MSCHAPV2 */
+
+#ifdef EAP_SERVER_PEAP
+	if (ret == 0)
+		ret = eap_server_peap_register();
+#endif /* EAP_SERVER_PEAP */
+
+#ifdef EAP_SERVER_TLV
+	if (ret == 0)
+		ret = eap_server_tlv_register();
+#endif /* EAP_SERVER_TLV */
+
+#ifdef EAP_SERVER_GTC
+	if (ret == 0)
+		ret = eap_server_gtc_register();
+#endif /* EAP_SERVER_GTC */
+
+#ifdef EAP_SERVER_TTLS
+	if (ret == 0)
+		ret = eap_server_ttls_register();
+#endif /* EAP_SERVER_TTLS */
+
+#ifdef EAP_SERVER_SIM
+	if (ret == 0)
+		ret = eap_server_sim_register();
+#endif /* EAP_SERVER_SIM */
+
+#ifdef EAP_SERVER_AKA
+	if (ret == 0)
+		ret = eap_server_aka_register();
+#endif /* EAP_SERVER_AKA */
+
+#ifdef EAP_SERVER_AKA_PRIME
+	if (ret == 0)
+		ret = eap_server_aka_prime_register();
+#endif /* EAP_SERVER_AKA_PRIME */
+
+#ifdef EAP_SERVER_PAX
+	if (ret == 0)
+		ret = eap_server_pax_register();
+#endif /* EAP_SERVER_PAX */
+
+#ifdef EAP_SERVER_PSK
+	if (ret == 0)
+		ret = eap_server_psk_register();
+#endif /* EAP_SERVER_PSK */
+
+#ifdef EAP_SERVER_SAKE
+	if (ret == 0)
+		ret = eap_server_sake_register();
+#endif /* EAP_SERVER_SAKE */
+
+#ifdef EAP_SERVER_GPSK
+	if (ret == 0)
+		ret = eap_server_gpsk_register();
+#endif /* EAP_SERVER_GPSK */
+
+#ifdef EAP_SERVER_VENDOR_TEST
+	if (ret == 0)
+		ret = eap_server_vendor_test_register();
+#endif /* EAP_SERVER_VENDOR_TEST */
+
+#ifdef EAP_SERVER_FAST
+	if (ret == 0)
+		ret = eap_server_fast_register();
+#endif /* EAP_SERVER_FAST */
+
+#ifdef EAP_SERVER_WSC
+	if (ret == 0)
+		ret = eap_server_wsc_register();
+#endif /* EAP_SERVER_WSC */
+
+#ifdef EAP_SERVER_IKEV2
+	if (ret == 0)
+		ret = eap_server_ikev2_register();
+#endif /* EAP_SERVER_IKEV2 */
+
+#ifdef EAP_SERVER_TNC
+	if (ret == 0)
+		ret = eap_server_tnc_register();
+#endif /* EAP_SERVER_TNC */
+
+#ifdef EAP_SERVER_PWD
+	if (ret == 0)
+		ret = eap_server_pwd_register();
+#endif /* EAP_SERVER_PWD */
+
+#ifdef EAP_SERVER_EKE
+	if (ret == 0)
+		ret = eap_server_eke_register();
+#endif /* EAP_SERVER_EKE */
+
+	return ret;
+}
diff --git a/hostap/hostapd/eap_register.h b/hostap/hostapd/eap_register.h
new file mode 100644
index 0000000..c342351
--- /dev/null
+++ b/hostap/hostapd/eap_register.h
@@ -0,0 +1,14 @@
+/*
+ * EAP method registration
+ * 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.
+ */
+
+#ifndef EAP_REGISTER_H
+#define EAP_REGISTER_H
+
+int eap_server_register_methods(void);
+
+#endif /* EAP_REGISTER_H */
diff --git a/hostap/hostapd/eap_testing.txt b/hostap/hostapd/eap_testing.txt
new file mode 100644
index 0000000..04468c3
--- /dev/null
+++ b/hostap/hostapd/eap_testing.txt
@@ -0,0 +1,77 @@
+Interoperability testing of hostapd's IEEE 802.1X/EAPOL authentication
+
+Test matrix
+
++) tested successfully
+F) failed
+-) peer did not support
+?) not tested
+
+XSupplicant --------------------------------.
+Intel PROSet ---------------------------.   |
+Windows XP -------------------------.   |   |
+Mac OS X 10.4 ------------------.   |   |   |
+Nokia S60 ------------------.   |   |   |   |
+wpa_supplicant ---------.   |   |   |   |   |
+			|   |   |   |   |   |
+
+EAP-MD5			+   -   ?   ?   -
+EAP-GTC			+   -   ?   -   -
+EAP-MSCHAPv2		+   -   ?   -   -
+EAP-TLS			+   +   +1  +   +
+EAP-PEAPv0/MSCHAPv2	+   +   +   +   +   +
+EAP-PEAPv0/GTC		+   +   +   -   +
+EAP-PEAPv0/MD5		+   -   +   -   -
+EAP-PEAPv0/TLS		+   F   -   +   +
+EAP-PEAPv0/SIM		+   +   -   -   -
+EAP-PEAPv0/AKA		+   +   -   -   -
+EAP-PEAPv0/PSK		+   -   -   -   -
+EAP-PEAPv0/PAX		+   -   -   -   -
+EAP-PEAPv0/SAKE		+   -   -   -   -
+EAP-PEAPv0/GPSK		+   -   -   -   -
+EAP-PEAPv1/MSCHAPv2	+   +   +   -   +   +
+EAP-PEAPv1/GTC		+   +   +   -   +
+EAP-PEAPv1/MD5		+   -   +   -   -
+EAP-PEAPv1/TLS		+   F   -   -   +
+EAP-PEAPv1/SIM		+   +   -   -   -
+EAP-PEAPv1/AKA		+   +   -   -   -
+EAP-PEAPv1/PSK		+   -   -   -   -
+EAP-PEAPv1/PAX		+   -   -   -   -
+EAP-PEAPv1/SAKE		+   -   -   -   -
+EAP-PEAPv1/GPSK		+   -   -   -   -
+EAP-TTLS/CHAP		+   -   +   -   +   +
+EAP-TTLS/MSCHAP		+   -   +   -   +   +
+EAP-TTLS/MSCHAPv2	+   +   +   -   +   +
+EAP-TTLS/PAP		+   -   +   -   +   +
+EAP-TTLS/EAP-MD5	+   -   -   -   -   +
+EAP-TTLS/EAP-GTC	+   +   -   -   -
+EAP-TTLS/EAP-MSCHAPv2	+   +   -   -   -
+EAP-TTLS/EAP-TLS	+   F   -   -   -
+EAP-TTLS/EAP-SIM	+   +   -   -   -
+EAP-TTLS/EAP-AKA	+   +   -   -   -
+EAP-TTLS + TNC		+   -   -   -   -
+EAP-SIM			+   +   -   -   +
+EAP-AKA			+   +   -   -   -
+EAP-PAX			+   -   -   -   -
+EAP-SAKE		+   -   -   -   -
+EAP-GPSK		+   -   -   -   -
+EAP-FAST/MSCHAPv2(prov)	+   -   F   -   F
+EAP-FAST/GTC(auth)	+   -   +   -   +
+EAP-FAST/MSCHAPv2(aprov)+   -   F   -   F
+EAP-FAST/GTC(aprov)	+   -   F   -   F
+EAP-FAST/MD5(aprov)	+   -   -   -   -
+EAP-FAST/TLS(aprov)	+   -   -   -   -
+EAP-FAST/SIM(aprov)	+   -   -   -   -
+EAP-FAST/AKA(aprov)	+   -   -   -   -
+EAP-FAST/MSCHAPv2(auth)	+   -   +   -   +
+EAP-FAST/MD5(auth)	+   -   +   -   -
+EAP-FAST/TLS(auth)	+   -   -   -   -
+EAP-FAST/SIM(auth)	+   -   -   -   -
+EAP-FAST/AKA(auth)	+   -   -   -   -
+EAP-FAST + TNC		+   -   -   -   -
+EAP-IKEv2		+   -   -   -   -
+EAP-TNC			+   -   -   -   -
+
+1) EAP-TLS itself worked, but peer certificate validation failed at
+   least when using the internal TLS server (peer included incorrect
+   certificates in the chain?)
diff --git a/hostap/hostapd/hapd_module_tests.c b/hostap/hostapd/hapd_module_tests.c
new file mode 100644
index 0000000..f7887eb
--- /dev/null
+++ b/hostap/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/hostap/hostapd/hlr_auc_gw.c b/hostap/hostapd/hlr_auc_gw.c
new file mode 100644
index 0000000..84d0308
--- /dev/null
+++ b/hostap/hostapd/hlr_auc_gw.c
@@ -0,0 +1,1163 @@
+/*
+ * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
+ * 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.
+ *
+ * This is an example implementation of the EAP-SIM/AKA database/authentication
+ * gateway interface to HLR/AuC. It is expected to be replaced with an
+ * implementation of SS7 gateway to GSM/UMTS authentication center (HLR/AuC) or
+ * a local implementation of SIM triplet and AKA authentication data generator.
+ *
+ * hostapd will send SIM/AKA authentication queries over a UNIX domain socket
+ * to and external program, e.g., this hlr_auc_gw. This interface uses simple
+ * text-based format:
+ *
+ * EAP-SIM / GSM triplet query/response:
+ * 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>
+ * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES>
+ * AKA-RESP-AUTH <IMSI> FAILURE
+ *
+ * EAP-AKA / UMTS AUTS (re-synchronization):
+ * AKA-AUTS <IMSI> <AUTS> <RAND>
+ *
+ * IMSI and max_chal are sent as an ASCII string,
+ * Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings.
+ *
+ * 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.
+ */
+
+#include "includes.h"
+#include <sys/un.h>
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
+
+#include "common.h"
+#include "crypto/milenage.h"
+#include "crypto/random.h"
+
+static const char *default_socket_path = "/tmp/hlr_auc_gw.sock";
+static const char *socket_path;
+static int serv_sock = -1;
+static char *milenage_file = NULL;
+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 {
+	struct gsm_triplet *next;
+	char imsi[20];
+	u8 kc[8];
+	u8 sres[4];
+	u8 _rand[16];
+};
+
+static struct gsm_triplet *gsm_db = NULL, *gsm_db_pos = NULL;
+
+/* OPc and AMF parameters for Milenage (Example algorithms for AKA). */
+struct milenage_parameters {
+	struct milenage_parameters *next;
+	char imsi[20];
+	u8 ki[16];
+	u8 opc[16];
+	u8 amf[2];
+	u8 sqn[6];
+	int set;
+	size_t res_len;
+};
+
+static struct milenage_parameters *milenage_db = NULL;
+
+#define EAP_SIM_MAX_CHAL 3
+
+#define EAP_AKA_RAND_LEN 16
+#define EAP_AKA_AUTN_LEN 16
+#define EAP_AKA_AUTS_LEN 14
+#define EAP_AKA_RES_MIN_LEN 4
+#define EAP_AKA_RES_MAX_LEN 16
+#define EAP_AKA_IK_LEN 16
+#define EAP_AKA_CK_LEN 16
+
+
+#ifdef CONFIG_SQLITE
+
+static sqlite3 *sqlite_db = NULL;
+static struct milenage_parameters db_tmp_milenage;
+
+
+static int db_table_exists(sqlite3 *db, const char *name)
+{
+	char cmd[128];
+	os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
+	return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
+}
+
+
+static int db_table_create_milenage(sqlite3 *db)
+{
+	char *err = NULL;
+	const char *sql =
+		"CREATE TABLE milenage("
+		"  imsi INTEGER PRIMARY KEY NOT NULL,"
+		"  ki CHAR(32) NOT NULL,"
+		"  opc CHAR(32) NOT NULL,"
+		"  amf CHAR(4) NOT NULL,"
+		"  sqn CHAR(12) NOT NULL,"
+		"  res_len INTEGER"
+		");";
+
+	printf("Adding database table for milenage information\n");
+	if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+		printf("SQLite error: %s\n", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static sqlite3 * db_open(const char *db_file)
+{
+	sqlite3 *db;
+
+	if (sqlite3_open(db_file, &db)) {
+		printf("Failed to open database %s: %s\n",
+		       db_file, sqlite3_errmsg(db));
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	if (!db_table_exists(db, "milenage") &&
+	    db_table_create_milenage(db) < 0) {
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	return db;
+}
+
+
+static int get_milenage_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+	struct milenage_parameters *m = ctx;
+	int i;
+
+	m->set = 1;
+
+	for (i = 0; i < argc; i++) {
+		if (os_strcmp(col[i], "ki") == 0 && argv[i] &&
+		    hexstr2bin(argv[i], m->ki, sizeof(m->ki))) {
+			printf("Invalid ki value in database\n");
+			return -1;
+		}
+
+		if (os_strcmp(col[i], "opc") == 0 && argv[i] &&
+		    hexstr2bin(argv[i], m->opc, sizeof(m->opc))) {
+			printf("Invalid opcvalue in database\n");
+			return -1;
+		}
+
+		if (os_strcmp(col[i], "amf") == 0 && argv[i] &&
+		    hexstr2bin(argv[i], m->amf, sizeof(m->amf))) {
+			printf("Invalid amf value in database\n");
+			return -1;
+		}
+
+		if (os_strcmp(col[i], "sqn") == 0 && argv[i] &&
+		    hexstr2bin(argv[i], m->sqn, sizeof(m->sqn))) {
+			printf("Invalid sqn value in database\n");
+			return -1;
+		}
+
+		if (os_strcmp(col[i], "res_len") == 0 && argv[i]) {
+			m->res_len = atoi(argv[i]);
+		}
+	}
+
+	return 0;
+}
+
+
+static struct milenage_parameters * db_get_milenage(const char *imsi_txt)
+{
+	char cmd[128];
+	unsigned long long imsi;
+
+	os_memset(&db_tmp_milenage, 0, sizeof(db_tmp_milenage));
+	imsi = atoll(imsi_txt);
+	os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi),
+		    "%llu", imsi);
+	os_snprintf(cmd, sizeof(cmd),
+		    "SELECT * FROM milenage WHERE imsi=%llu;", imsi);
+	if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage,
+			 NULL) != SQLITE_OK)
+		return NULL;
+
+	if (!db_tmp_milenage.set)
+		return NULL;
+	return &db_tmp_milenage;
+}
+
+
+static int db_update_milenage_sqn(struct milenage_parameters *m)
+{
+	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';
+	os_snprintf(cmd, sizeof(cmd),
+		    "UPDATE milenage SET sqn='%s' WHERE imsi=%s;",
+		    val, m->imsi);
+	if (sqlite3_exec(sqlite_db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+		printf("Failed to update SQN in database for IMSI %s\n",
+		       m->imsi);
+		return -1;
+	}
+	return 0;
+}
+
+#endif /* CONFIG_SQLITE */
+
+
+static int open_socket(const char *path)
+{
+	struct sockaddr_un addr;
+	int s;
+
+	s = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (s < 0) {
+		perror("socket(PF_UNIX)");
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
+	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("hlr-auc-gw: bind(PF_UNIX)");
+		close(s);
+		return -1;
+	}
+
+	return s;
+}
+
+
+static int read_gsm_triplets(const char *fname)
+{
+	FILE *f;
+	char buf[200], *pos, *pos2;
+	struct gsm_triplet *g = NULL;
+	int line, ret = 0;
+
+	if (fname == NULL)
+		return -1;
+
+	f = fopen(fname, "r");
+	if (f == NULL) {
+		printf("Could not open GSM tripler data file '%s'\n", fname);
+		return -1;
+	}
+
+	line = 0;
+	while (fgets(buf, sizeof(buf), f)) {
+		line++;
+
+		/* Parse IMSI:Kc:SRES:RAND */
+		buf[sizeof(buf) - 1] = '\0';
+		if (buf[0] == '#')
+			continue;
+		pos = buf;
+		while (*pos != '\0' && *pos != '\n')
+			pos++;
+		if (*pos == '\n')
+			*pos = '\0';
+		pos = buf;
+		if (*pos == '\0')
+			continue;
+
+		g = os_zalloc(sizeof(*g));
+		if (g == NULL) {
+			ret = -1;
+			break;
+		}
+
+		/* IMSI */
+		pos2 = strchr(pos, ':');
+		if (pos2 == NULL) {
+			printf("%s:%d - Invalid IMSI (%s)\n",
+			       fname, line, pos);
+			ret = -1;
+			break;
+		}
+		*pos2 = '\0';
+		if (strlen(pos) >= sizeof(g->imsi)) {
+			printf("%s:%d - Too long IMSI (%s)\n",
+			       fname, line, pos);
+			ret = -1;
+			break;
+		}
+		os_strlcpy(g->imsi, pos, sizeof(g->imsi));
+		pos = pos2 + 1;
+
+		/* Kc */
+		pos2 = strchr(pos, ':');
+		if (pos2 == NULL) {
+			printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos);
+			ret = -1;
+			break;
+		}
+		*pos2 = '\0';
+		if (strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) {
+			printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos);
+			ret = -1;
+			break;
+		}
+		pos = pos2 + 1;
+
+		/* SRES */
+		pos2 = strchr(pos, ':');
+		if (pos2 == NULL) {
+			printf("%s:%d - Invalid SRES (%s)\n", fname, line,
+			       pos);
+			ret = -1;
+			break;
+		}
+		*pos2 = '\0';
+		if (strlen(pos) != 8 || hexstr2bin(pos, g->sres, 4)) {
+			printf("%s:%d - Invalid SRES (%s)\n", fname, line,
+			       pos);
+			ret = -1;
+			break;
+		}
+		pos = pos2 + 1;
+
+		/* RAND */
+		pos2 = strchr(pos, ':');
+		if (pos2)
+			*pos2 = '\0';
+		if (strlen(pos) != 32 || hexstr2bin(pos, g->_rand, 16)) {
+			printf("%s:%d - Invalid RAND (%s)\n", fname, line,
+			       pos);
+			ret = -1;
+			break;
+		}
+		pos = pos2 + 1;
+
+		g->next = gsm_db;
+		gsm_db = g;
+		g = NULL;
+	}
+	os_free(g);
+
+	fclose(f);
+
+	return ret;
+}
+
+
+static struct gsm_triplet * get_gsm_triplet(const char *imsi)
+{
+	struct gsm_triplet *g = gsm_db_pos;
+
+	while (g) {
+		if (strcmp(g->imsi, imsi) == 0) {
+			gsm_db_pos = g->next;
+			return g;
+		}
+		g = g->next;
+	}
+
+	g = gsm_db;
+	while (g && g != gsm_db_pos) {
+		if (strcmp(g->imsi, imsi) == 0) {
+			gsm_db_pos = g->next;
+			return g;
+		}
+		g = g->next;
+	}
+
+	return NULL;
+}
+
+
+static int read_milenage(const char *fname)
+{
+	FILE *f;
+	char buf[200], *pos, *pos2;
+	struct milenage_parameters *m = NULL;
+	int line, ret = 0;
+
+	if (fname == NULL)
+		return -1;
+
+	f = fopen(fname, "r");
+	if (f == NULL) {
+		printf("Could not open Milenage data file '%s'\n", fname);
+		return -1;
+	}
+
+	line = 0;
+	while (fgets(buf, sizeof(buf), f)) {
+		line++;
+
+		/* Parse IMSI Ki OPc AMF SQN [RES_len] */
+		buf[sizeof(buf) - 1] = '\0';
+		if (buf[0] == '#')
+			continue;
+		pos = buf;
+		while (*pos != '\0' && *pos != '\n')
+			pos++;
+		if (*pos == '\n')
+			*pos = '\0';
+		pos = buf;
+		if (*pos == '\0')
+			continue;
+
+		m = os_zalloc(sizeof(*m));
+		if (m == NULL) {
+			ret = -1;
+			break;
+		}
+
+		/* IMSI */
+		pos2 = strchr(pos, ' ');
+		if (pos2 == NULL) {
+			printf("%s:%d - Invalid IMSI (%s)\n",
+			       fname, line, pos);
+			ret = -1;
+			break;
+		}
+		*pos2 = '\0';
+		if (strlen(pos) >= sizeof(m->imsi)) {
+			printf("%s:%d - Too long IMSI (%s)\n",
+			       fname, line, pos);
+			ret = -1;
+			break;
+		}
+		os_strlcpy(m->imsi, pos, sizeof(m->imsi));
+		pos = pos2 + 1;
+
+		/* Ki */
+		pos2 = strchr(pos, ' ');
+		if (pos2 == NULL) {
+			printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
+			ret = -1;
+			break;
+		}
+		*pos2 = '\0';
+		if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) {
+			printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
+			ret = -1;
+			break;
+		}
+		pos = pos2 + 1;
+
+		/* OPc */
+		pos2 = strchr(pos, ' ');
+		if (pos2 == NULL) {
+			printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
+			ret = -1;
+			break;
+		}
+		*pos2 = '\0';
+		if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) {
+			printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
+			ret = -1;
+			break;
+		}
+		pos = pos2 + 1;
+
+		/* AMF */
+		pos2 = strchr(pos, ' ');
+		if (pos2 == NULL) {
+			printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
+			ret = -1;
+			break;
+		}
+		*pos2 = '\0';
+		if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) {
+			printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
+			ret = -1;
+			break;
+		}
+		pos = pos2 + 1;
+
+		/* SQN */
+		pos2 = strchr(pos, ' ');
+		if (pos2)
+			*pos2 = '\0';
+		if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) {
+			printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos);
+			ret = -1;
+			break;
+		}
+
+		if (pos2) {
+			pos = pos2 + 1;
+			m->res_len = atoi(pos);
+			if (m->res_len &&
+			    (m->res_len < EAP_AKA_RES_MIN_LEN ||
+			     m->res_len > EAP_AKA_RES_MAX_LEN)) {
+				printf("%s:%d - Invalid RES_len (%s)\n",
+				       fname, line, pos);
+				ret = -1;
+				break;
+			}
+		}
+
+		m->next = milenage_db;
+		milenage_db = m;
+		m = NULL;
+	}
+	os_free(m);
+
+	fclose(f);
+
+	return ret;
+}
+
+
+static void update_milenage_file(const char *fname)
+{
+	FILE *f, *f2;
+	char name[500], buf[500], *pos;
+	char *end = buf + sizeof(buf);
+	struct milenage_parameters *m;
+	size_t imsi_len;
+
+	f = fopen(fname, "r");
+	if (f == NULL) {
+		printf("Could not open Milenage data file '%s'\n", fname);
+		return;
+	}
+
+	snprintf(name, sizeof(name), "%s.new", fname);
+	f2 = fopen(name, "w");
+	if (f2 == NULL) {
+		printf("Could not write Milenage data file '%s'\n", name);
+		fclose(f);
+		return;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		/* IMSI Ki OPc AMF SQN */
+		buf[sizeof(buf) - 1] = '\0';
+
+		pos = strchr(buf, ' ');
+		if (buf[0] == '#' || pos == NULL || pos - buf >= 20)
+			goto no_update;
+
+		imsi_len = pos - buf;
+
+		for (m = milenage_db; m; m = m->next) {
+			if (strncmp(buf, m->imsi, imsi_len) == 0 &&
+			    m->imsi[imsi_len] == '\0')
+				break;
+		}
+
+		if (!m)
+			goto no_update;
+
+		pos = buf;
+		pos += snprintf(pos, end - pos, "%s ", m->imsi);
+		pos += wpa_snprintf_hex(pos, end - pos, m->ki, 16);
+		*pos++ = ' ';
+		pos += wpa_snprintf_hex(pos, end - pos, m->opc, 16);
+		*pos++ = ' ';
+		pos += wpa_snprintf_hex(pos, end - pos, m->amf, 2);
+		*pos++ = ' ';
+		pos += wpa_snprintf_hex(pos, end - pos, m->sqn, 6);
+		*pos++ = '\n';
+
+	no_update:
+		fprintf(f2, "%s", buf);
+	}
+
+	fclose(f2);
+	fclose(f);
+
+	snprintf(name, sizeof(name), "%s.bak", fname);
+	if (rename(fname, name) < 0) {
+		perror("rename");
+		return;
+	}
+
+	snprintf(name, sizeof(name), "%s.new", fname);
+	if (rename(name, fname) < 0) {
+		perror("rename");
+		return;
+	}
+
+}
+
+
+static struct milenage_parameters * get_milenage(const char *imsi)
+{
+	struct milenage_parameters *m = milenage_db;
+
+	while (m) {
+		if (strcmp(m->imsi, imsi) == 0)
+			break;
+		m = m->next;
+	}
+
+#ifdef CONFIG_SQLITE
+	if (!m)
+		m = db_get_milenage(imsi);
+#endif /* CONFIG_SQLITE */
+
+	return m;
+}
+
+
+static int sim_req_auth(char *imsi, char *resp, size_t resp_len)
+{
+	int count, max_chal, ret;
+	char *pos;
+	char *rpos, *rend;
+	struct milenage_parameters *m;
+	struct gsm_triplet *g;
+
+	resp[0] = '\0';
+
+	pos = strchr(imsi, ' ');
+	if (pos) {
+		*pos++ = '\0';
+		max_chal = atoi(pos);
+		if (max_chal < 1 || max_chal > EAP_SIM_MAX_CHAL)
+			max_chal = EAP_SIM_MAX_CHAL;
+	} else
+		max_chal = EAP_SIM_MAX_CHAL;
+
+	rend = resp + resp_len;
+	rpos = resp;
+	ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %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 < max_chal; count++) {
+			if (random_get_bytes(_rand, 16) < 0)
+				return -1;
+			gsm_milenage(m->opc, m->ki, _rand, sres, kc);
+			*rpos++ = ' ';
+			rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8);
+			*rpos++ = ':';
+			rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4);
+			*rpos++ = ':';
+			rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16);
+		}
+		*rpos = '\0';
+		return 0;
+	}
+
+	count = 0;
+	while (count < max_chal && (g = get_gsm_triplet(imsi))) {
+		if (strcmp(g->imsi, imsi) != 0)
+			continue;
+
+		if (rpos < rend)
+			*rpos++ = ' ';
+		rpos += wpa_snprintf_hex(rpos, rend - rpos, g->kc, 8);
+		if (rpos < rend)
+			*rpos++ = ':';
+		rpos += wpa_snprintf_hex(rpos, rend - rpos, g->sres, 4);
+		if (rpos < rend)
+			*rpos++ = ':';
+		rpos += wpa_snprintf_hex(rpos, rend - rpos, g->_rand, 16);
+		count++;
+	}
+
+	if (count == 0) {
+		printf("No GSM triplets found for %s\n", imsi);
+		ret = snprintf(rpos, rend - rpos, " FAILURE");
+		if (ret < 0 || ret >= rend - rpos)
+			return -1;
+		rpos += ret;
+	}
+
+	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 (os_snprintf_error(rend - rpos, ret))
+		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 (os_snprintf_error(rend - rpos, ret))
+		return -1;
+	rpos += ret;
+
+	return 0;
+}
+
+
+static void inc_sqn(u8 *sqn)
+{
+	u64 val, seq, ind;
+
+	/*
+	 * SQN = SEQ | IND = SEQ1 | SEQ2 | IND
+	 *
+	 * The mechanism used here is not time-based, so SEQ2 is void and
+	 * SQN = SEQ1 | IND. The length of IND is ind_len bits and the length
+	 * of SEQ1 is 48 - ind_len bits.
+	 */
+
+	/* Increment both SEQ and IND by one */
+	val = ((u64) WPA_GET_BE32(sqn) << 16) | ((u64) WPA_GET_BE16(sqn + 4));
+	seq = (val >> ind_len) + 1;
+	ind = (val + 1) & ((1 << ind_len) - 1);
+	val = (seq << ind_len) | ind;
+	WPA_PUT_BE32(sqn, val >> 16);
+	WPA_PUT_BE16(sqn + 4, val & 0xffff);
+}
+
+
+static int aka_req_auth(char *imsi, char *resp, size_t resp_len)
+{
+	/* AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> */
+	char *pos, *end;
+	u8 _rand[EAP_AKA_RAND_LEN];
+	u8 autn[EAP_AKA_AUTN_LEN];
+	u8 ik[EAP_AKA_IK_LEN];
+	u8 ck[EAP_AKA_CK_LEN];
+	u8 res[EAP_AKA_RES_MAX_LEN];
+	size_t res_len;
+	int ret;
+	struct milenage_parameters *m;
+	int failed = 0;
+
+	m = get_milenage(imsi);
+	if (m) {
+		if (random_get_bytes(_rand, EAP_AKA_RAND_LEN) < 0)
+			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;
+		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);
+		if (m->res_len >= EAP_AKA_RES_MIN_LEN &&
+		    m->res_len <= EAP_AKA_RES_MAX_LEN &&
+		    m->res_len < res_len)
+			res_len = m->res_len;
+	} else {
+		printf("Unknown IMSI: %s\n", imsi);
+#ifdef AKA_USE_FIXED_TEST_VALUES
+		printf("Using fixed test values for AKA\n");
+		memset(_rand, '0', EAP_AKA_RAND_LEN);
+		memset(autn, '1', EAP_AKA_AUTN_LEN);
+		memset(ik, '3', EAP_AKA_IK_LEN);
+		memset(ck, '4', EAP_AKA_CK_LEN);
+		memset(res, '2', EAP_AKA_RES_MAX_LEN);
+		res_len = EAP_AKA_RES_MAX_LEN;
+#else /* AKA_USE_FIXED_TEST_VALUES */
+		failed = 1;
+#endif /* AKA_USE_FIXED_TEST_VALUES */
+	}
+
+	pos = resp;
+	end = resp + resp_len;
+	ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi);
+	if (ret < 0 || ret >= end - pos)
+		return -1;
+	pos += ret;
+	if (failed) {
+		ret = snprintf(pos, end - pos, "FAILURE");
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+		return 0;
+	}
+	pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN);
+	*pos++ = ' ';
+	pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN);
+	*pos++ = ' ';
+	pos += wpa_snprintf_hex(pos, end - pos, ik, EAP_AKA_IK_LEN);
+	*pos++ = ' ';
+	pos += wpa_snprintf_hex(pos, end - pos, ck, EAP_AKA_CK_LEN);
+	*pos++ = ' ';
+	pos += wpa_snprintf_hex(pos, end - pos, res, res_len);
+
+	return 0;
+}
+
+
+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 -1;
+	*auts++ = '\0';
+
+	__rand = strchr(auts, ' ');
+	if (__rand == NULL)
+		return -1;
+	*__rand++ = '\0';
+
+	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 -1;
+	}
+
+	m = get_milenage(imsi);
+	if (m == NULL) {
+		printf("Unknown IMSI: %s\n", imsi);
+		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);
+		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], resp[1000];
+	struct sockaddr_un from;
+	socklen_t fromlen;
+	ssize_t res;
+
+	fromlen = sizeof(from);
+	res = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from,
+		       &fromlen);
+	if (res < 0) {
+		perror("recvfrom");
+		return -1;
+	}
+
+	if (res == 0)
+		return 0;
+
+	if ((size_t) res >= sizeof(buf))
+		res = sizeof(buf) - 1;
+	buf[res] = '\0';
+
+	printf("Received: %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;
+}
+
+
+static void cleanup(void)
+{
+	struct gsm_triplet *g, *gprev;
+	struct milenage_parameters *m, *prev;
+
+	if (update_milenage && milenage_file && sqn_changes)
+		update_milenage_file(milenage_file);
+
+	g = gsm_db;
+	while (g) {
+		gprev = g;
+		g = g->next;
+		os_free(gprev);
+	}
+
+	m = milenage_db;
+	while (m) {
+		prev = m;
+		m = m->next;
+		os_free(prev);
+	}
+
+	if (serv_sock >= 0)
+		close(serv_sock);
+	if (socket_path)
+		unlink(socket_path);
+
+#ifdef CONFIG_SQLITE
+	if (sqlite_db) {
+		sqlite3_close(sqlite_db);
+		sqlite_db = NULL;
+	}
+#endif /* CONFIG_SQLITE */
+}
+
+
+static void handle_term(int sig)
+{
+	printf("Signal %d - terminate\n", sig);
+	exit(0);
+}
+
+
+static void usage(void)
+{
+	printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
+	       "database/authenticator\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>] [command]\n"
+	       "\n"
+	       "options:\n"
+	       "  -h = show this usage help\n"
+	       "  -u = update SQN in Milenage file on exit\n"
+	       "  -s<socket path> = path for UNIX domain socket\n"
+	       "                    (default: %s)\n"
+	       "  -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"
+	       "\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);
+}
+
+
+int main(int argc, char *argv[])
+{
+	int c;
+	char *gsm_triplet_file = NULL;
+	char *sqlite_db_file = NULL;
+	int ret = 0;
+
+	if (os_program_init())
+		return -1;
+
+	socket_path = default_socket_path;
+
+	for (;;) {
+		c = getopt(argc, argv, "D:g:hi:m:s:u");
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'D':
+#ifdef CONFIG_SQLITE
+			sqlite_db_file = optarg;
+			break;
+#else /* CONFIG_SQLITE */
+			printf("No SQLite support included in the build\n");
+			return -1;
+#endif /* CONFIG_SQLITE */
+		case 'g':
+			gsm_triplet_file = optarg;
+			break;
+		case 'h':
+			usage();
+			return 0;
+		case 'i':
+			ind_len = atoi(optarg);
+			if (ind_len < 0 || ind_len > 32) {
+				printf("Invalid IND length\n");
+				return -1;
+			}
+			break;
+		case 'm':
+			milenage_file = optarg;
+			break;
+		case 's':
+			socket_path = optarg;
+			break;
+		case 'u':
+			update_milenage = 1;
+			break;
+		default:
+			usage();
+			return -1;
+		}
+	}
+
+	if (!gsm_triplet_file && !milenage_file && !sqlite_db_file) {
+		usage();
+		return -1;
+	}
+
+#ifdef CONFIG_SQLITE
+	if (sqlite_db_file && (sqlite_db = db_open(sqlite_db_file)) == NULL)
+		return -1;
+#endif /* CONFIG_SQLITE */
+
+	if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0)
+		return -1;
+
+	if (milenage_file && read_milenage(milenage_file) < 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);
+
+		atexit(cleanup);
+		signal(SIGTERM, handle_term);
+		signal(SIGINT, handle_term);
+
+		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) {
+		sqlite3_close(sqlite_db);
+		sqlite_db = NULL;
+	}
+#endif /* CONFIG_SQLITE */
+
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/hostap/hostapd/hlr_auc_gw.milenage_db b/hostap/hostapd/hlr_auc_gw.milenage_db
new file mode 100644
index 0000000..c156a29
--- /dev/null
+++ b/hostap/hostapd/hlr_auc_gw.milenage_db
@@ -0,0 +1,15 @@
+# Parameters for Milenage (Example algorithms for AKA).
+# The example Ki, OPc, and AMF values here are from 3GPP TS 35.208 v6.0.0
+# 4.3.20 Test Set 20. SQN is the last used SQN value.
+# These values can be used for both UMTS (EAP-AKA) and GSM (EAP-SIM)
+# authentication. In case of GSM/EAP-SIM, AMF and SQN values are not used, but
+# dummy values will need to be included in this file.
+
+# IMSI Ki OPc AMF SQN [RES_len]
+232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000
+# Example using truncated 32-bit RES instead of 64-bit default
+232010000000001 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 4
+
+# These values are from Test Set 19 which has the AMF separation bit set to 1
+# and as such, is suitable for EAP-AKA' test.
+555444333222111 5122250214c33e723a5dd523fc145fc0 981d464c7c52eb6e5036234984ad0bcf c3ab 16f3b3f70fc1
diff --git a/hostap/hostapd/hlr_auc_gw.txt b/hostap/hostapd/hlr_auc_gw.txt
new file mode 100644
index 0000000..097bbce
--- /dev/null
+++ b/hostap/hostapd/hlr_auc_gw.txt
@@ -0,0 +1,104 @@
+HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
+
+hlr_auc_gw is an example implementation of the EAP-SIM/AKA/AKA'
+database/authentication gateway interface to HLR/AuC. It could be
+replaced with an implementation of SS7 gateway to GSM/UMTS
+authentication center (HLR/AuC). hostapd will send SIM/AKA
+authentication queries over a UNIX domain socket to and external
+program, e.g., hlr_auc_gw.
+
+hlr_auc_gw can be configured with GSM and UMTS authentication data with
+text files: GSM triplet file (see hostapd.sim_db) and Milenage file (see
+hlr_auc_gw.milenage_db). Milenage parameters can be used to generate
+dynamic authentication data for EAP-SIM, EAP-AKA, and EAP-AKA' while the
+GSM triplet data is used for a more static configuration (e.g., triplets
+extracted from a SIM card).
+
+Alternatively, hlr_auc_gw can be built with support for an SQLite
+database for more dynamic operations. This is enabled by adding
+"CONFIG_SQLITE=y" into hostapd/.config before building hlr_auc_gw ("make
+clean; make hlr_auc_gw" in this directory).
+
+hostapd is configured to use hlr_auc_gw with the eap_sim_db parameter in
+hostapd.conf (e.g., "eap_sim_db=unix:/tmp/hlr_auc_gw.sock"). hlr_auc_gw
+is configured with command line parameters:
+
+hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] [-m<milenage file>] \
+        [-D<DB file>] [-i<IND len in bits>]
+
+options:
+  -h = show this usage help
+  -u = update SQN in Milenage file on exit
+  -s<socket path> = path for UNIX domain socket
+                    (default: /tmp/hlr_auc_gw.sock)
+  -g<triplet file> = path for GSM authentication triplets
+  -m<milenage file> = path for Milenage keys
+  -D<DB file> = path to SQLite database
+  -i<IND len in bits> = IND length for SQN (default: 5)
+
+
+The SQLite database can be initialized with sqlite, e.g., by running
+following commands in "sqlite3 /path/to/hlr_auc_gw.db":
+
+CREATE TABLE milenage(
+	imsi INTEGER PRIMARY KEY NOT NULL,
+	ki CHAR(32) NOT NULL,
+	opc CHAR(32) NOT NULL,
+	amf CHAR(4) NOT NULL,
+	sqn CHAR(12) NOT NULL
+);
+INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES(
+	232010000000000,
+	'90dca4eda45b53cf0f12d7c9c3bc6a89',
+	'cb9cccc4b9258e6dca4760379fb82581',
+	'61df',
+	'000000000000'
+);
+INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES(
+	555444333222111,
+	'5122250214c33e723a5dd523fc145fc0',
+	'981d464c7c52eb6e5036234984ad0bcf',
+	'c3ab',
+	'16f3b3f70fc1'
+);
+
+
+hostapd (EAP server) can also be configured to store the EAP-SIM/AKA
+pseudonyms and reauth information into a SQLite database. This is
+configured with the db parameter within the eap_sim_db configuration
+option.
+
+
+"hlr_auc_gw -D /path/to/hlr_auc_gw.db" can then be used to fetch
+Milenage parameters based on IMSI from the database. The database can be
+updated dynamically while hlr_auc_gw is running to add/remove/modify
+entries.
+
+
+Example configuration files for hostapd to operate as a RADIUS
+authentication server for EAP-SIM/AKA/AKA':
+
+hostapd.conf:
+
+driver=none
+radius_server_clients=hostapd.radius_clients
+eap_server=1
+eap_user_file=hostapd.eap_user
+eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/eap_sim.db
+eap_sim_aka_result_ind=1
+
+hostapd.radius_clients:
+
+0.0.0.0/0	radius
+
+hostapd.eap_user:
+
+"0"*	AKA
+"1"*	SIM
+"2"*	AKA
+"3"*	SIM
+"4"*	AKA
+"5"*	SIM
+"6"*	AKA'
+"7"*	AKA'
+"8"*	AKA'
diff --git a/hostap/hostapd/hostapd.8 b/hostap/hostapd/hostapd.8
new file mode 100644
index 0000000..d19d862
--- /dev/null
+++ b/hostap/hostapd/hostapd.8
@@ -0,0 +1,59 @@
+.TH HOSTAPD 8 "April  7, 2005" hostapd hostapd
+.SH NAME
+hostapd \- IEEE 802.11 AP, IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator
+.SH SYNOPSIS
+.B hostapd
+[\-hdBKtv] [\-P <PID file>] <configuration file(s)>
+.SH DESCRIPTION
+This manual page documents briefly the
+.B hostapd
+daemon.
+.PP
+.B hostapd
+is a user space daemon for access point and authentication servers.
+It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server.
+The current version supports Linux (Host AP, mac80211-based drivers) and FreeBSD (net80211).
+
+.B hostapd
+is designed to be a "daemon" program that runs in the background and acts as the backend component controlling authentication.
+.B hostapd
+supports separate frontend programs and an example text-based frontend,
+.BR hostapd_cli ,
+is included with
+.BR hostapd .
+.SH OPTIONS
+A summary of options is included below.
+For a complete description, run
+.BR hostapd
+from the command line.
+.TP
+.B \-h
+Show usage.
+.TP
+.B \-d
+Show more debug messages.
+.TP
+.B \-dd
+Show even more debug messages.
+.TP
+.B \-B
+Run daemon in the background.
+.TP
+.B \-P <PID file>
+Path to PID file.
+.TP
+.B \-K
+Include key data in debug messages.
+.TP
+.B \-t
+Include timestamps in some debug messages.
+.TP
+.B \-v
+Show hostapd version.
+.SH SEE ALSO
+.BR hostapd_cli (1).
+.SH AUTHOR
+hostapd was written by Jouni Malinen <j@w1.fi>. 
+.PP
+This manual page was written by Faidon Liambotis <faidon@cube.gr>,
+for the Debian project (but may be used by others).
diff --git a/hostap/hostapd/hostapd.accept b/hostap/hostapd/hostapd.accept
new file mode 100644
index 0000000..2d2a0a2
--- /dev/null
+++ b/hostap/hostapd/hostapd.accept
@@ -0,0 +1,6 @@
+# List of MAC addresses that are allowed to authenticate (IEEE 802.11)
+# with the AP. Optional VLAN ID can be assigned for clients based on the
+# MAC address if dynamic VLANs (hostapd.conf dynamic_vlan option) are used.
+00:11:22:33:44:55
+00:66:77:88:99:aa
+00:00:22:33:44:55	1
diff --git a/hostap/hostapd/hostapd.conf b/hostap/hostapd/hostapd.conf
new file mode 100644
index 0000000..a0071f7
--- /dev/null
+++ b/hostap/hostapd/hostapd.conf
@@ -0,0 +1,1885 @@
+##### hostapd configuration file ##############################################
+# Empty lines and lines starting with # are ignored
+
+# AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for
+# management frames with the Host AP driver); wlan0 with many nl80211 drivers
+interface=wlan0
+
+# In case of atheros and nl80211 driver interfaces, an additional
+# configuration parameter, bridge, may be used to notify hostapd if the
+# interface is included in a bridge. This parameter is not used with Host AP
+# driver. If the bridge parameter is not set, the drivers will automatically
+# figure out the bridge interface (assuming sysfs is enabled and mounted to
+# /sys) and this parameter may not be needed.
+#
+# For nl80211, this parameter can be used to request the AP interface to be
+# added to the bridge automatically (brctl may refuse to do this before hostapd
+# has been started to change the interface mode). If needed, the bridge
+# interface is also created.
+#bridge=br0
+
+# Driver interface type (hostap/wired/none/nl80211/bsd);
+# default: hostap). nl80211 is used with all Linux mac80211 drivers.
+# Use driver=none if building hostapd as a standalone RADIUS server that does
+# not control any wireless/wired driver.
+# driver=hostap
+
+# Driver interface parameters (mainly for development testing use)
+# driver_params=<params>
+
+# hostapd event logger configuration
+#
+# Two output method: syslog and stdout (only usable if not forking to
+# background).
+#
+# Module bitfield (ORed bitfield of modules that will be logged; -1 = all
+# modules):
+# bit 0 (1) = IEEE 802.11
+# bit 1 (2) = IEEE 802.1X
+# bit 2 (4) = RADIUS
+# bit 3 (8) = WPA
+# bit 4 (16) = driver interface
+# bit 5 (32) = IAPP
+# bit 6 (64) = MLME
+#
+# Levels (minimum value for logged events):
+#  0 = verbose debugging
+#  1 = debugging
+#  2 = informational messages
+#  3 = notification
+#  4 = warning
+#
+logger_syslog=-1
+logger_syslog_level=2
+logger_stdout=-1
+logger_stdout_level=2
+
+# 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
+# configuration. The socket file will be named based on the interface name, so
+# multiple hostapd processes/interfaces can be run at the same time if more
+# than one interface is used.
+# /var/run/hostapd is the recommended directory for sockets and by default,
+# hostapd_cli will use it when trying to connect with hostapd.
+ctrl_interface=/var/run/hostapd
+
+# Access control for the control interface can be configured by setting the
+# directory to allow only members of a group to use sockets. This way, it is
+# possible to run hostapd as root (since it needs to change network
+# configuration and open raw sockets) and still allow GUI/CLI components to be
+# run as non-root users. However, since the control interface can be used to
+# change the network configuration, this access needs to be protected in many
+# cases. By default, hostapd is configured to use gid 0 (root). If you
+# want to allow non-root users to use the contron interface, add a new group
+# and change this value to match with that group. Add users that should have
+# control interface access to this group.
+#
+# This variable can be a group name or gid.
+#ctrl_interface_group=wheel
+ctrl_interface_group=0
+
+
+##### IEEE 802.11 related configuration #######################################
+
+# SSID to be used in IEEE 802.11 management frames
+ssid=test
+# Alternative formats for configuring SSID
+# (double quoted string, hexdump, printf-escaped string)
+#ssid2="test"
+#ssid2=74657374
+#ssid2=P"hello\nthere"
+
+# UTF-8 SSID: Whether the SSID is to be interpreted using UTF-8 encoding
+#utf8_ssid=1
+
+# Country code (ISO/IEC 3166-1). Used to set regulatory domain.
+# Set as needed to indicate country in which device is operating.
+# This can limit available channels and transmit power.
+#country_code=US
+
+# Enable IEEE 802.11d. This advertises the country_code and the set of allowed
+# channels and transmit power levels based on the regulatory limits. The
+# country_code setting must be configured with the correct country for
+# IEEE 802.11d functions.
+# (default: 0 = disabled)
+#ieee80211d=1
+
+# Enable IEEE 802.11h. This enables radar detection and DFS support if
+# available. DFS support is required on outdoor 5 GHz channels in most countries
+# of the world. This can be used only with ieee80211d=1.
+# (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). When using ACS (see channel parameter), a special value "any"
+# can be used to indicate that any support band can be used. This special case
+# is currently supported only with drivers with which offloaded ACS is used.
+# Default: IEEE 802.11b
+hw_mode=g
+
+# Channel number (IEEE 802.11)
+# (default: 0, i.e., not set)
+# Please note that some drivers do not use this value from hostapd and the
+# channel will need to be configured separately with iwconfig.
+#
+# If CONFIG_ACS build option is enabled, the channel can be selected
+# automatically at run time by setting channel=acs_survey or channel=0, both of
+# which will enable the ACS survey based algorithm.
+channel=1
+
+# ACS tuning - Automatic Channel Selection
+# See: http://wireless.kernel.org/en/users/Documentation/acs
+#
+# You can customize the ACS survey algorithm with following variables:
+#
+# acs_num_scans requirement is 1..100 - number of scans to be performed that
+# are used to trigger survey data gathering of an underlying device driver.
+# Scans are passive and typically take a little over 100ms (depending on the
+# driver) on each available channel for given hw_mode. Increasing this value
+# means sacrificing startup time and gathering more data wrt channel
+# interference that may help choosing a better channel. This can also help fine
+# tune the ACS scan time in case a driver has different scan dwell times.
+#
+# acs_chan_bias is a space-separated list of <channel>:<bias> pairs. It can be
+# used to increase (or decrease) the likelihood of a specific channel to be
+# selected by the ACS algorithm. The total interference factor for each channel
+# gets multiplied by the specified bias value before finding the channel with
+# the lowest value. In other words, values between 0.0 and 1.0 can be used to
+# make a channel more likely to be picked while values larger than 1.0 make the
+# specified channel less likely to be picked. This can be used, e.g., to prefer
+# the commonly used 2.4 GHz band channels 1, 6, and 11 (which is the default
+# behavior on 2.4 GHz band if no acs_chan_bias parameter is specified).
+#
+# Defaults:
+#acs_num_scans=5
+#acs_chan_bias=1:0.8 6:0.8 11:0.8
+
+# Channel list restriction. This option allows hostapd to select one of the
+# provided channels when a channel should be automatically selected.
+# Channel list can be provided as range using hyphen ('-') or individual
+# channels can be specified by space (' ') seperated values
+# Default: all channels allowed in selected hw_mode
+#chanlist=100 104 108 112 116
+#chanlist=1 6 11-13
+
+# Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
+beacon_int=100
+
+# DTIM (delivery traffic information message) period (range 1..255):
+# number of beacons between DTIMs (1 = every beacon includes DTIM element)
+# (default: 2)
+dtim_period=2
+
+# Maximum number of stations allowed in station table. New stations will be
+# rejected after the station table is full. IEEE 802.11 has a limit of 2007
+# different association IDs, so this number should not be larger than that.
+# (default: 2007)
+max_num_sta=255
+
+# RTS/CTS threshold; 2347 = disabled (default); range 0..2347
+# If this field is not included in hostapd.conf, hostapd will not control
+# RTS threshold and 'iwconfig wlan# rts <val>' can be used to set it.
+rts_threshold=2347
+
+# Fragmentation threshold; 2346 = disabled (default); range 256..2346
+# If this field is not included in hostapd.conf, hostapd will not control
+# fragmentation threshold and 'iwconfig wlan# frag <val>' can be used to set
+# it.
+fragm_threshold=2346
+
+# Rate configuration
+# Default is to enable all rates supported by the hardware. This configuration
+# item allows this list be filtered so that only the listed rates will be left
+# in the list. If the list is empty, all rates are used. This list can have
+# entries that are not in the list of rates the hardware supports (such entries
+# are ignored). The entries in this list are in 100 kbps, i.e., 11 Mbps = 110.
+# If this item is present, at least one rate have to be matching with the rates
+# hardware supports.
+# default: use the most common supported rate setting for the selected
+# hw_mode (i.e., this line can be removed from configuration file in most
+# cases)
+#supported_rates=10 20 55 110 60 90 120 180 240 360 480 540
+
+# Basic rate set configuration
+# List of rates (in 100 kbps) that are included in the basic rate set.
+# If this item is not included, usually reasonable default set is used.
+#basic_rates=10 20
+#basic_rates=10 20 55 110
+#basic_rates=60 120 240
+
+# Short Preamble
+# This parameter can be used to enable optional use of short preamble for
+# frames sent at 2 Mbps, 5.5 Mbps, and 11 Mbps to improve network performance.
+# This applies only to IEEE 802.11b-compatible networks and this should only be
+# enabled if the local hardware supports use of short preamble. If any of the
+# associated STAs do not support short preamble, use of short preamble will be
+# disabled (and enabled when such STAs disassociate) dynamically.
+# 0 = do not allow use of short preamble (default)
+# 1 = allow use of short preamble
+#preamble=1
+
+# Station MAC address -based authentication
+# Please note that this kind of access control requires a driver that uses
+# hostapd to take care of management frame processing and as such, this can be
+# used with driver=hostap or driver=nl80211, but not with driver=atheros.
+# 0 = accept unless in deny list
+# 1 = deny unless in accept list
+# 2 = use external RADIUS server (accept/deny lists are searched first)
+macaddr_acl=0
+
+# Accept/deny lists are read from separate files (containing list of
+# MAC addresses, one per line). Use absolute path name to make sure that the
+# files can be read on SIGHUP configuration reloads.
+#accept_mac_file=/etc/hostapd.accept
+#deny_mac_file=/etc/hostapd.deny
+
+# IEEE 802.11 specifies two authentication algorithms. hostapd can be
+# configured to allow both of these or only one. Open system authentication
+# should be used with IEEE 802.1X.
+# Bit fields of allowed authentication algorithms:
+# bit 0 = Open System Authentication
+# bit 1 = Shared Key Authentication (requires WEP)
+auth_algs=3
+
+# Send empty SSID in beacons and ignore probe request frames that do not
+# specify full SSID, i.e., require stations to know SSID.
+# default: disabled (0)
+# 1 = send empty (length=0) SSID in beacon and ignore probe request for
+#     broadcast SSID
+# 2 = clear SSID (ASCII 0), but keep the original length (this may be required
+#     with some clients that do not support empty SSID) and ignore probe
+#     requests for broadcast SSID
+ignore_broadcast_ssid=0
+
+# Additional vendor specfic elements for Beacon and Probe Response frames
+# This parameter can be used to add additional vendor specific element(s) into
+# the end of the Beacon and Probe Response frames. The format for these
+# element(s) is a hexdump of the raw information elements (id+len+payload for
+# one or more elements)
+#vendor_elements=dd0411223301
+
+# TX queue parameters (EDCF / bursting)
+# tx_queue_<queue name>_<param>
+# queues: data0, data1, data2, data3, after_beacon, beacon
+#		(data0 is the highest priority queue)
+# parameters:
+#   aifs: AIFS (default 2)
+#   cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191,
+#	   16383, 32767)
+#   cwmax: cwMax (same values as cwMin, cwMax >= cwMin)
+#   burst: maximum length (in milliseconds with precision of up to 0.1 ms) for
+#          bursting
+#
+# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
+# These parameters are used by the access point when transmitting frames
+# to the clients.
+#
+# Low priority / AC_BK = background
+#tx_queue_data3_aifs=7
+#tx_queue_data3_cwmin=15
+#tx_queue_data3_cwmax=1023
+#tx_queue_data3_burst=0
+# Note: for IEEE 802.11b mode: cWmin=31 cWmax=1023 burst=0
+#
+# Normal priority / AC_BE = best effort
+#tx_queue_data2_aifs=3
+#tx_queue_data2_cwmin=15
+#tx_queue_data2_cwmax=63
+#tx_queue_data2_burst=0
+# Note: for IEEE 802.11b mode: cWmin=31 cWmax=127 burst=0
+#
+# High priority / AC_VI = video
+#tx_queue_data1_aifs=1
+#tx_queue_data1_cwmin=7
+#tx_queue_data1_cwmax=15
+#tx_queue_data1_burst=3.0
+# Note: for IEEE 802.11b mode: cWmin=15 cWmax=31 burst=6.0
+#
+# Highest priority / AC_VO = voice
+#tx_queue_data0_aifs=1
+#tx_queue_data0_cwmin=3
+#tx_queue_data0_cwmax=7
+#tx_queue_data0_burst=1.5
+# Note: for IEEE 802.11b mode: cWmin=7 cWmax=15 burst=3.3
+
+# 802.1D Tag (= UP) to AC mappings
+# WMM specifies following mapping of data frames to different ACs. This mapping
+# can be configured using Linux QoS/tc and sch_pktpri.o module.
+# 802.1D Tag	802.1D Designation	Access Category	WMM Designation
+# 1		BK			AC_BK		Background
+# 2		-			AC_BK		Background
+# 0		BE			AC_BE		Best Effort
+# 3		EE			AC_BE		Best Effort
+# 4		CL			AC_VI		Video
+# 5		VI			AC_VI		Video
+# 6		VO			AC_VO		Voice
+# 7		NC			AC_VO		Voice
+# Data frames with no priority information: AC_BE
+# Management frames: AC_VO
+# PS-Poll frames: AC_BE
+
+# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
+# for 802.11a or 802.11g networks
+# These parameters are sent to WMM clients when they associate.
+# The parameters will be used by WMM clients for frames transmitted to the
+# access point.
+#
+# note - txop_limit is in units of 32microseconds
+# note - acm is admission control mandatory flag. 0 = admission control not
+# required, 1 = mandatory
+# note - Here cwMin and cmMax are in exponent form. The actual cw value used
+# will be (2^n)-1 where n is the value given here. The allowed range for these
+# wmm_ac_??_{cwmin,cwmax} is 0..15 with cwmax >= cwmin.
+#
+wmm_enabled=1
+#
+# WMM-PS Unscheduled Automatic Power Save Delivery [U-APSD]
+# Enable this flag if U-APSD supported outside hostapd (eg., Firmware/driver)
+#uapsd_advertisement_enabled=1
+#
+# Low priority / AC_BK = background
+wmm_ac_bk_cwmin=4
+wmm_ac_bk_cwmax=10
+wmm_ac_bk_aifs=7
+wmm_ac_bk_txop_limit=0
+wmm_ac_bk_acm=0
+# Note: for IEEE 802.11b mode: cWmin=5 cWmax=10
+#
+# Normal priority / AC_BE = best effort
+wmm_ac_be_aifs=3
+wmm_ac_be_cwmin=4
+wmm_ac_be_cwmax=10
+wmm_ac_be_txop_limit=0
+wmm_ac_be_acm=0
+# Note: for IEEE 802.11b mode: cWmin=5 cWmax=7
+#
+# High priority / AC_VI = video
+wmm_ac_vi_aifs=2
+wmm_ac_vi_cwmin=3
+wmm_ac_vi_cwmax=4
+wmm_ac_vi_txop_limit=94
+wmm_ac_vi_acm=0
+# Note: for IEEE 802.11b mode: cWmin=4 cWmax=5 txop_limit=188
+#
+# Highest priority / AC_VO = voice
+wmm_ac_vo_aifs=2
+wmm_ac_vo_cwmin=2
+wmm_ac_vo_cwmax=3
+wmm_ac_vo_txop_limit=47
+wmm_ac_vo_acm=0
+# Note: for IEEE 802.11b mode: cWmin=3 cWmax=4 burst=102
+
+# Static WEP key configuration
+#
+# The key number to use when transmitting.
+# It must be between 0 and 3, and the corresponding key must be set.
+# default: not set
+#wep_default_key=0
+# The WEP keys to use.
+# A key may be a quoted string or unquoted hexadecimal digits.
+# The key length should be 5, 13, or 16 characters, or 10, 26, or 32
+# digits, depending on whether 40-bit (64-bit), 104-bit (128-bit), or
+# 128-bit (152-bit) WEP is used.
+# Only the default key must be supplied; the others are optional.
+# default: not set
+#wep_key0=123456789a
+#wep_key1="vwxyz"
+#wep_key2=0102030405060708090a0b0c0d
+#wep_key3=".2.4.6.8.0.23"
+
+# Station inactivity limit
+#
+# If a station does not send anything in ap_max_inactivity seconds, an
+# empty data frame is sent to it in order to verify whether it is
+# still in range. If this frame is not ACKed, the station will be
+# disassociated and then deauthenticated. This feature is used to
+# clear station table of old entries when the STAs move out of the
+# range.
+#
+# The station can associate again with the AP if it is still in range;
+# this inactivity poll is just used as a nicer way of verifying
+# inactivity; i.e., client will not report broken connection because
+# disassociation frame is not sent immediately without first polling
+# the STA with a data frame.
+# default: 300 (i.e., 5 minutes)
+#ap_max_inactivity=300
+#
+# The inactivity polling can be disabled to disconnect stations based on
+# inactivity timeout so that idle stations are more likely to be disconnected
+# even if they are still in range of the AP. This can be done by setting
+# skip_inactivity_poll to 1 (default 0).
+#skip_inactivity_poll=0
+
+# Disassociate stations based on excessive transmission failures or other
+# indications of connection loss. This depends on the driver capabilities and
+# may not be available with all drivers.
+#disassoc_low_ack=1
+
+# Maximum allowed Listen Interval (how many Beacon periods STAs are allowed to
+# remain asleep). Default: 65535 (no limit apart from field size)
+#max_listen_interval=100
+
+# WDS (4-address frame) mode with per-station virtual interfaces
+# (only supported with driver=nl80211)
+# This mode allows associated stations to use 4-address frames to allow layer 2
+# bridging to be used.
+#wds_sta=1
+
+# If bridge parameter is set, the WDS STA interface will be added to the same
+# bridge by default. This can be overridden with the wds_bridge parameter to
+# use a separate bridge.
+#wds_bridge=wds-br0
+
+# Start the AP with beaconing disabled by default.
+#start_disabled=0
+
+# Client isolation can be used to prevent low-level bridging of frames between
+# associated stations in the BSS. By default, this bridging is allowed.
+#ap_isolate=1
+
+# BSS Load update period (in BUs)
+# This field is used to enable and configure adding a BSS Load element into
+# Beacon and Probe Response frames.
+#bss_load_update_period=50
+
+# 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
+# 0 = disabled (default)
+# 1 = enabled
+# Note: You will also need to enable WMM for full HT functionality.
+#ieee80211n=1
+
+# ht_capab: HT capabilities (list of flags)
+# 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 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
+#	HT40- and HT40+ use per IEEE 802.11n Annex J:
+#	freq		HT40-		HT40+
+#	2.4 GHz		5-13		1-7 (1-9 in Europe/Japan)
+#	5 GHz		40,48,56,64	36,44,52,60
+#	(depending on the location, not all of these channels may be available
+#	for use)
+#	Please note that 40 MHz channels may switch their primary and secondary
+#	channels if needed or creation of 40 MHz channel maybe rejected based
+#	on overlapping BSSes. These changes are done automatically when hostapd
+#	is setting up the 40 MHz channel.
+# Spatial Multiplexing (SM) Power Save: [SMPS-STATIC] or [SMPS-DYNAMIC]
+#	(SMPS disabled if neither is set)
+# HT-greenfield: [GF] (disabled if not set)
+# Short GI for 20 MHz: [SHORT-GI-20] (disabled if not set)
+# Short GI for 40 MHz: [SHORT-GI-40] (disabled if not set)
+# Tx STBC: [TX-STBC] (disabled if not set)
+# Rx STBC: [RX-STBC1] (one spatial stream), [RX-STBC12] (one or two spatial
+#	streams), or [RX-STBC123] (one, two, or three spatial streams); Rx STBC
+#	disabled if none of these set
+# HT-delayed Block Ack: [DELAYED-BA] (disabled if not set)
+# 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)
+# 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. Setting this
+# to non-zero allows 2.4 GHz band AP to move dynamically to a 40 MHz channel if
+# no co-existence issues with neighboring devices are found.
+#obss_interval=0
+
+##### IEEE 802.11ac related configuration #####################################
+
+# ieee80211ac: Whether IEEE 802.11ac (VHT) is enabled
+# 0 = disabled (default)
+# 1 = enabled
+# Note: You will also need to enable WMM for full VHT functionality.
+#ieee80211ac=1
+
+# vht_capab: VHT capabilities (list of flags)
+#
+# vht_max_mpdu_len: [MAX-MPDU-7991] [MAX-MPDU-11454]
+# Indicates maximum MPDU length
+# 0 = 3895 octets (default)
+# 1 = 7991 octets
+# 2 = 11454 octets
+# 3 = reserved
+#
+# supported_chan_width: [VHT160] [VHT160-80PLUS80]
+# Indicates supported Channel widths
+# 0 = 160 MHz & 80+80 channel widths are not supported (default)
+# 1 = 160 MHz channel width is supported
+# 2 = 160 MHz & 80+80 channel widths are supported
+# 3 = reserved
+#
+# Rx LDPC coding capability: [RXLDPC]
+# Indicates support for receiving LDPC coded pkts
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Short GI for 80 MHz: [SHORT-GI-80]
+# Indicates short GI support for reception of packets transmitted with TXVECTOR
+# params format equal to VHT and CBW = 80Mhz
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Short GI for 160 MHz: [SHORT-GI-160]
+# Indicates short GI support for reception of packets transmitted with TXVECTOR
+# params format equal to VHT and CBW = 160Mhz
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Tx STBC: [TX-STBC-2BY1]
+# Indicates support for the transmission of at least 2x1 STBC
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Rx STBC: [RX-STBC-1] [RX-STBC-12] [RX-STBC-123] [RX-STBC-1234]
+# Indicates support for the reception of PPDUs using STBC
+# 0 = Not supported (default)
+# 1 = support of one spatial stream
+# 2 = support of one and two spatial streams
+# 3 = support of one, two and three spatial streams
+# 4 = support of one, two, three and four spatial streams
+# 5,6,7 = reserved
+#
+# SU Beamformer Capable: [SU-BEAMFORMER]
+# Indicates support for operation as a single user beamformer
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# SU Beamformee Capable: [SU-BEAMFORMEE]
+# Indicates support for operation as a single user beamformee
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Compressed Steering Number of Beamformer Antennas Supported:
+# [BF-ANTENNA-2] [BF-ANTENNA-3] [BF-ANTENNA-4]
+#   Beamformee's capability indicating the maximum number of beamformer
+#   antennas the beamformee can support when sending compressed beamforming
+#   feedback
+# If SU beamformer capable, set to maximum value minus 1
+# else reserved (default)
+#
+# Number of Sounding Dimensions:
+# [SOUNDING-DIMENSION-2] [SOUNDING-DIMENSION-3] [SOUNDING-DIMENSION-4]
+# Beamformer's capability indicating the maximum value of the NUM_STS parameter
+# in the TXVECTOR of a VHT NDP
+# If SU beamformer capable, set to maximum value minus 1
+# else reserved (default)
+#
+# MU Beamformer Capable: [MU-BEAMFORMER]
+# Indicates support for operation as an MU beamformer
+# 0 = Not supported or sent by Non-AP STA (default)
+# 1 = Supported
+#
+# VHT TXOP PS: [VHT-TXOP-PS]
+# Indicates whether or not the AP supports VHT TXOP Power Save Mode
+#  or whether or not the STA is in VHT TXOP Power Save mode
+# 0 = VHT AP doesnt support VHT TXOP PS mode (OR) VHT Sta not in VHT TXOP PS
+#  mode
+# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT Sta is in VHT TXOP power save
+#  mode
+#
+# +HTC-VHT Capable: [HTC-VHT]
+# Indicates whether or not the STA supports receiving a VHT variant HT Control
+# field.
+# 0 = Not supported (default)
+# 1 = supported
+#
+# Maximum A-MPDU Length Exponent: [MAX-A-MPDU-LEN-EXP0]..[MAX-A-MPDU-LEN-EXP7]
+# Indicates the maximum length of A-MPDU pre-EOF padding that the STA can recv
+# This field is an integer in the range of 0 to 7.
+# The length defined by this field is equal to
+# 2 pow(13 + Maximum A-MPDU Length Exponent) -1 octets
+#
+# VHT Link Adaptation Capable: [VHT-LINK-ADAPT2] [VHT-LINK-ADAPT3]
+# Indicates whether or not the STA supports link adaptation using VHT variant
+# HT Control field
+# If +HTC-VHTcapable is 1
+#  0 = (no feedback) if the STA does not provide VHT MFB (default)
+#  1 = reserved
+#  2 = (Unsolicited) if the STA provides only unsolicited VHT MFB
+#  3 = (Both) if the STA can provide VHT MFB in response to VHT MRQ and if the
+#      STA provides unsolicited VHT MFB
+# Reserved if +HTC-VHTcapable is 0
+#
+# Rx Antenna Pattern Consistency: [RX-ANTENNA-PATTERN]
+# Indicates the possibility of Rx antenna pattern change
+# 0 = Rx antenna pattern might change during the lifetime of an association
+# 1 = Rx antenna pattern does not change during the lifetime of an association
+#
+# Tx Antenna Pattern Consistency: [TX-ANTENNA-PATTERN]
+# Indicates the possibility of Tx antenna pattern change
+# 0 = Tx antenna pattern might change during the lifetime of an association
+# 1 = Tx antenna pattern does not change during the lifetime of an association
+#vht_capab=[SHORT-GI-80][HTC-VHT]
+#
+# Require stations to support VHT PHY (reject association if they do not)
+#require_vht=1
+
+# 0 = 20 or 40 MHz operating Channel width
+# 1 = 80 MHz channel width
+# 2 = 160 MHz channel width
+# 3 = 80+80 MHz channel width
+#vht_oper_chwidth=1
+#
+# center freq = 5 GHz + (5 * index)
+# So index 42 gives center freq 5.210 GHz
+# which is channel 42 in 5G band
+#
+#vht_oper_centr_freq_seg0_idx=42
+#
+# center freq = 5 GHz + (5 * index)
+# So index 159 gives center freq 5.795 GHz
+# which is channel 159 in 5G band
+#
+#vht_oper_centr_freq_seg1_idx=159
+
+##### IEEE 802.1X-2004 related configuration ##################################
+
+# Require IEEE 802.1X authorization
+#ieee8021x=1
+
+# IEEE 802.1X/EAPOL version
+# hostapd is implemented based on IEEE Std 802.1X-2004 which defines EAPOL
+# version 2. However, there are many client implementations that do not handle
+# the new version number correctly (they seem to drop the frames completely).
+# In order to make hostapd interoperate with these clients, the version number
+# can be set to the older version (1) with this configuration value.
+#eapol_version=2
+
+# Optional displayable message sent with EAP Request-Identity. The first \0
+# in this string will be converted to ASCII-0 (nul). This can be used to
+# separate network info (comma separated list of attribute=value pairs); see,
+# e.g., RFC 4284.
+#eap_message=hello
+#eap_message=hello\0networkid=netw,nasid=foo,portid=0,NAIRealms=example.com
+
+# WEP rekeying (disabled if key lengths are not set or are set to 0)
+# Key lengths for default/broadcast and individual/unicast keys:
+# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits)
+# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits)
+#wep_key_len_broadcast=5
+#wep_key_len_unicast=5
+# Rekeying period in seconds. 0 = do not rekey (i.e., set keys only once)
+#wep_rekey_period=300
+
+# EAPOL-Key index workaround (set bit7) for WinXP Supplicant (needed only if
+# only broadcast keys are used)
+eapol_key_index_workaround=0
+
+# EAP reauthentication period in seconds (default: 3600 seconds; 0 = disable
+# reauthentication).
+#eap_reauth_period=3600
+
+# Use PAE group address (01:80:c2:00:00:03) instead of individual target
+# address when sending EAPOL frames with driver=wired. This is the most common
+# mechanism used in wired authentication, but it also requires that the port
+# is only used by one station.
+#use_pae_group_addr=1
+
+# EAP Re-authentication Protocol (ERP) authenticator (RFC 6696)
+#
+# Whether to initiate EAP authentication with EAP-Initiate/Re-auth-Start before
+# EAP-Identity/Request
+#erp_send_reauth_start=1
+#
+# Domain name for EAP-Initiate/Re-auth-Start. Omitted from the message if not
+# set (no local ER server). This is also used by the integrated EAP server if
+# ERP is enabled (eap_server_erp=1).
+#erp_domain=example.com
+
+##### Integrated EAP server ###################################################
+
+# Optionally, hostapd can be configured to use an integrated EAP server
+# to process EAP authentication locally without need for an external RADIUS
+# server. This functionality can be used both as a local authentication server
+# for IEEE 802.1X/EAPOL and as a RADIUS server for other devices.
+
+# Use integrated EAP server instead of external RADIUS authentication
+# server. This is also needed if hostapd is configured to act as a RADIUS
+# authentication server.
+eap_server=0
+
+# Path for EAP server user database
+# If SQLite support is included, this can be set to "sqlite:/path/to/sqlite.db"
+# to use SQLite database instead of a text file.
+#eap_user_file=/etc/hostapd.eap_user
+
+# CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS
+#ca_cert=/etc/hostapd.ca.pem
+
+# Server certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS
+#server_cert=/etc/hostapd.server.pem
+
+# Private key matching with the server certificate for EAP-TLS/PEAP/TTLS
+# This may point to the same file as server_cert if both certificate and key
+# are included in a single file. PKCS#12 (PFX) file (.p12/.pfx) can also be
+# used by commenting out server_cert and specifying the PFX file as the
+# private_key.
+#private_key=/etc/hostapd.server.prv
+
+# Passphrase for private key
+#private_key_passwd=secret passphrase
+
+# Server identity
+# EAP methods that provide mechanism for authenticated server identity delivery
+# use this value. If not set, "hostapd" is used as a default.
+#server_id=server.example.com
+
+# Enable CRL verification.
+# Note: hostapd does not yet support CRL downloading based on CDP. Thus, a
+# valid CRL signed by the CA is required to be included in the ca_cert file.
+# This can be done by using PEM format for CA certificate and CRL and
+# concatenating these into one file. Whenever CRL changes, hostapd needs to be
+# restarted to take the new CRL into use.
+# 0 = do not verify CRLs (default)
+# 1 = check the CRL of the user certificate
+# 2 = check all CRLs in the certificate path
+#check_crl=1
+
+# TLS Session Lifetime in seconds
+# This can be used to allow TLS sessions to be cached and resumed with an
+# abbreviated handshake when using EAP-TLS/TTLS/PEAP.
+# (default: 0 = session caching and resumption disabled)
+#tls_session_lifetime=3600
+
+# Cached OCSP stapling response (DER encoded)
+# If set, this file is sent as a certificate status response by the EAP server
+# if the EAP peer requests certificate status in the ClientHello message.
+# This cache file can be updated, e.g., by running following command
+# periodically to get an update from the OCSP responder:
+# openssl ocsp \
+#	-no_nonce \
+#	-CAfile /etc/hostapd.ca.pem \
+#	-issuer /etc/hostapd.ca.pem \
+#	-cert /etc/hostapd.server.pem \
+#	-url http://ocsp.example.com:8888/ \
+#	-respout /tmp/ocsp-cache.der
+#ocsp_stapling_response=/tmp/ocsp-cache.der
+
+# dh_file: File path to DH/DSA parameters file (in PEM format)
+# This is an optional configuration file for setting parameters for an
+# ephemeral DH key exchange. In most cases, the default RSA authentication does
+# not use this configuration. However, it is possible setup RSA to use
+# ephemeral DH key exchange. In addition, ciphers with DSA keys always use
+# ephemeral DH keys. This can be used to achieve forward secrecy. If the file
+# is in DSA parameters format, it will be automatically converted into DH
+# params. This parameter is required if anonymous EAP-FAST is used.
+# You can generate DH parameters file with OpenSSL, e.g.,
+# "openssl dhparam -out /etc/hostapd.dh.pem 2048"
+#dh_file=/etc/hostapd.dh.pem
+
+# OpenSSL cipher string
+#
+# This is an OpenSSL specific configuration option for configuring the default
+# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default.
+# See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation
+# on cipher suite configuration. This is applicable only if hostapd is built to
+# use OpenSSL.
+#openssl_ciphers=DEFAULT:!EXP:!LOW
+
+# Fragment size for EAP methods
+#fragment_size=1400
+
+# Finite cyclic group for EAP-pwd. Number maps to group of domain parameters
+# using the IANA repository for IKE (RFC 2409).
+#pwd_group=19
+
+# Configuration data for EAP-SIM database/authentication gateway interface.
+# This is a text string in implementation specific format. The example
+# implementation in eap_sim_db.c uses this as the UNIX domain socket name for
+# the HLR/AuC gateway (e.g., hlr_auc_gw). In this case, the path uses "unix:"
+# prefix. If hostapd is built with SQLite support (CONFIG_SQLITE=y in .config),
+# database file can be described with an optional db=<path> parameter.
+#eap_sim_db=unix:/tmp/hlr_auc_gw.sock
+#eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db
+
+# Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret,
+# random value. It is configured as a 16-octet value in hex format. It can be
+# generated, e.g., with the following command:
+# od -tx1 -v -N16 /dev/random | colrm 1 8 | tr -d ' '
+#pac_opaque_encr_key=000102030405060708090a0b0c0d0e0f
+
+# EAP-FAST authority identity (A-ID)
+# A-ID indicates the identity of the authority that issues PACs. The A-ID
+# should be unique across all issuing servers. In theory, this is a variable
+# length field, but due to some existing implementations requiring A-ID to be
+# 16 octets in length, it is strongly recommended to use that length for the
+# field to provid interoperability with deployed peer implementations. This
+# field is configured in hex format.
+#eap_fast_a_id=101112131415161718191a1b1c1d1e1f
+
+# EAP-FAST authority identifier information (A-ID-Info)
+# This is a user-friendly name for the A-ID. For example, the enterprise name
+# and server name in a human-readable format. This field is encoded as UTF-8.
+#eap_fast_a_id_info=test server
+
+# Enable/disable different EAP-FAST provisioning modes:
+#0 = provisioning disabled
+#1 = only anonymous provisioning allowed
+#2 = only authenticated provisioning allowed
+#3 = both provisioning modes allowed (default)
+#eap_fast_prov=3
+
+# EAP-FAST PAC-Key lifetime in seconds (hard limit)
+#pac_key_lifetime=604800
+
+# EAP-FAST PAC-Key refresh time in seconds (soft limit on remaining hard
+# limit). The server will generate a new PAC-Key when this number of seconds
+# (or fewer) of the lifetime remains.
+#pac_key_refresh_time=86400
+
+# EAP-SIM and EAP-AKA protected success/failure indication using AT_RESULT_IND
+# (default: 0 = disabled).
+#eap_sim_aka_result_ind=1
+
+# Trusted Network Connect (TNC)
+# If enabled, TNC validation will be required before the peer is allowed to
+# connect. Note: This is only used with EAP-TTLS and EAP-FAST. If any other
+# EAP method is enabled, the peer will be allowed to connect without TNC.
+#tnc=1
+
+# EAP Re-authentication Protocol (ERP) - RFC 6696
+#
+# Whether to enable ERP on the EAP server.
+#eap_server_erp=1
+
+##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) #######################
+
+# Interface to be used for IAPP broadcast packets
+#iapp_interface=eth0
+
+
+##### RADIUS client configuration #############################################
+# for IEEE 802.1X with external Authentication Server, IEEE 802.11
+# authentication with external ACL for MAC addresses, and accounting
+
+# The own IP address of the access point (used as NAS-IP-Address)
+own_ip_addr=127.0.0.1
+
+# Optional NAS-Identifier string for RADIUS messages. When used, this should be
+# a unique to the NAS within the scope of the RADIUS server. For example, a
+# fully qualified domain name can be used here.
+# When using IEEE 802.11r, nas_identifier must be set and must be between 1 and
+# 48 octets long.
+#nas_identifier=ap.example.com
+
+# RADIUS client forced local IP address for the access point
+# Normally the local IP address is determined automatically based on configured
+# IP addresses, but this field can be used to force a specific address to be
+# used, e.g., when the device has multiple IP addresses.
+#radius_client_addr=127.0.0.1
+
+# RADIUS authentication server
+#auth_server_addr=127.0.0.1
+#auth_server_port=1812
+#auth_server_shared_secret=secret
+
+# RADIUS accounting server
+#acct_server_addr=127.0.0.1
+#acct_server_port=1813
+#acct_server_shared_secret=secret
+
+# Secondary RADIUS servers; to be used if primary one does not reply to
+# RADIUS packets. These are optional and there can be more than one secondary
+# server listed.
+#auth_server_addr=127.0.0.2
+#auth_server_port=1812
+#auth_server_shared_secret=secret2
+#
+#acct_server_addr=127.0.0.2
+#acct_server_port=1813
+#acct_server_shared_secret=secret2
+
+# Retry interval for trying to return to the primary RADIUS server (in
+# seconds). RADIUS client code will automatically try to use the next server
+# when the current server is not replying to requests. If this interval is set,
+# primary server will be retried after configured amount of time even if the
+# currently used secondary server is still working.
+#radius_retry_primary_interval=600
+
+
+# Interim accounting update interval
+# If this is set (larger than 0) and acct_server is configured, hostapd will
+# send interim accounting updates every N seconds. Note: if set, this overrides
+# possible Acct-Interim-Interval attribute in Access-Accept message. Thus, this
+# value should not be configured in hostapd.conf, if RADIUS server is used to
+# control the interim interval.
+# This value should not be less 600 (10 minutes) and must not be less than
+# 60 (1 minute).
+#radius_acct_interim_interval=600
+
+# Request Chargeable-User-Identity (RFC 4372)
+# This parameter can be used to configure hostapd to request CUI from the
+# RADIUS server by including Chargeable-User-Identity attribute into
+# Access-Request packets.
+#radius_request_cui=1
+
+# Dynamic VLAN mode; allow RADIUS authentication server to decide which VLAN
+# is used for the stations. This information is parsed from following RADIUS
+# attributes based on RFC 3580 and RFC 2868: Tunnel-Type (value 13 = VLAN),
+# Tunnel-Medium-Type (value 6 = IEEE 802), Tunnel-Private-Group-ID (value
+# VLANID as a string). Optionally, the local MAC ACL list (accept_mac_file) can
+# be used to set static client MAC address to VLAN ID mapping.
+# 0 = disabled (default)
+# 1 = option; use default interface if RADIUS server does not include VLAN ID
+# 2 = required; reject authentication if RADIUS server does not include VLAN ID
+#dynamic_vlan=0
+
+# VLAN interface list for dynamic VLAN mode is read from a separate text file.
+# This list is used to map VLAN ID from the RADIUS server to a network
+# interface. Each station is bound to one interface in the same way as with
+# multiple BSSIDs or SSIDs. Each line in this text file is defining a new
+# interface and the line must include VLAN ID and interface name separated by
+# white space (space or tab).
+# If no entries are provided by this file, the station is statically mapped
+# to <bss-iface>.<vlan-id> interfaces.
+#vlan_file=/etc/hostapd.vlan
+
+# Interface where 802.1q tagged packets should appear when a RADIUS server is
+# used to determine which VLAN a station is on.  hostapd creates a bridge for
+# each VLAN.  Then hostapd adds a VLAN interface (associated with the interface
+# indicated by 'vlan_tagged_interface') and the appropriate wireless interface
+# to the bridge.
+#vlan_tagged_interface=eth0
+
+# Bridge (prefix) to add the wifi and the tagged interface to. This gets the
+# VLAN ID appended. It defaults to brvlan%d if no tagged interface is given
+# and br%s.%d if a tagged interface is given, provided %s = tagged interface
+# and %d = VLAN ID.
+#vlan_bridge=brvlan
+
+# When hostapd creates a VLAN interface on vlan_tagged_interfaces, it needs
+# to know how to name it.
+# 0 = vlan<XXX>, e.g., vlan1
+# 1 = <vlan_tagged_interface>.<XXX>, e.g. eth0.1
+#vlan_naming=0
+
+# Arbitrary RADIUS attributes can be added into Access-Request and
+# Accounting-Request packets by specifying the contents of the attributes with
+# the following configuration parameters. There can be multiple of these to
+# add multiple attributes. These parameters can also be used to override some
+# of the attributes added automatically by hostapd.
+# Format: <attr_id>[:<syntax:value>]
+# attr_id: RADIUS attribute type (e.g., 26 = Vendor-Specific)
+# syntax: s = string (UTF-8), d = integer, x = octet string
+# value: attribute value in format indicated by the syntax
+# If syntax and value parts are omitted, a null value (single 0x00 octet) is
+# used.
+#
+# Additional Access-Request attributes
+# radius_auth_req_attr=<attr_id>[:<syntax:value>]
+# Examples:
+# Operator-Name = "Operator"
+#radius_auth_req_attr=126:s:Operator
+# Service-Type = Framed (2)
+#radius_auth_req_attr=6:d:2
+# Connect-Info = "testing" (this overrides the automatically generated value)
+#radius_auth_req_attr=77:s:testing
+# Same Connect-Info value set as a hexdump
+#radius_auth_req_attr=77:x:74657374696e67
+
+#
+# Additional Accounting-Request attributes
+# radius_acct_req_attr=<attr_id>[:<syntax:value>]
+# Examples:
+# Operator-Name = "Operator"
+#radius_acct_req_attr=126:s:Operator
+
+# Dynamic Authorization Extensions (RFC 5176)
+# This mechanism can be used to allow dynamic changes to user session based on
+# commands from a RADIUS server (or some other disconnect client that has the
+# needed session information). For example, Disconnect message can be used to
+# request an associated station to be disconnected.
+#
+# This is disabled by default. Set radius_das_port to non-zero UDP port
+# number to enable.
+#radius_das_port=3799
+#
+# DAS client (the host that can send Disconnect/CoA requests) and shared secret
+#radius_das_client=192.168.1.123 shared secret here
+#
+# DAS Event-Timestamp time window in seconds
+#radius_das_time_window=300
+#
+# DAS require Event-Timestamp
+#radius_das_require_event_timestamp=1
+
+##### RADIUS authentication server configuration ##############################
+
+# hostapd can be used as a RADIUS authentication server for other hosts. This
+# requires that the integrated EAP server is also enabled and both
+# authentication services are sharing the same configuration.
+
+# File name of the RADIUS clients configuration for the RADIUS server. If this
+# commented out, RADIUS server is disabled.
+#radius_server_clients=/etc/hostapd.radius_clients
+
+# 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
+
+
+##### WPA/IEEE 802.11i configuration ##########################################
+
+# Enable WPA. Setting this variable configures the AP to require WPA (either
+# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
+# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
+# Instead of wpa_psk / wpa_passphrase, wpa_psk_radius might suffice.
+# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
+# RADIUS authentication server must be configured, and WPA-EAP must be included
+# in wpa_key_mgmt.
+# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0)
+# and/or WPA2 (full IEEE 802.11i/RSN):
+# bit0 = WPA
+# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled)
+#wpa=1
+
+# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
+# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
+# (8..63 characters) that will be converted to PSK. This conversion uses SSID
+# so the PSK changes when ASCII passphrase is used and the SSID is changed.
+# wpa_psk (dot11RSNAConfigPSKValue)
+# wpa_passphrase (dot11RSNAConfigPSKPassPhrase)
+#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+#wpa_passphrase=secret passphrase
+
+# Optionally, WPA PSKs can be read from a separate text file (containing list
+# of (PSK,MAC address) pairs. This allows more than one PSK to be configured.
+# Use absolute path name to make sure that the files can be read on SIGHUP
+# configuration reloads.
+#wpa_psk_file=/etc/hostapd.wpa_psk
+
+# Optionally, WPA passphrase can be received from RADIUS authentication server
+# This requires macaddr_acl to be set to 2 (RADIUS)
+# 0 = disabled (default)
+# 1 = optional; use default passphrase/psk if RADIUS server does not include
+#	Tunnel-Password
+# 2 = required; reject authentication if RADIUS server does not include
+#	Tunnel-Password
+#wpa_psk_radius=0
+
+# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
+# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
+# added to enable SHA256-based stronger algorithms.
+# (dot11RSNAConfigAuthenticationSuitesTable)
+#wpa_key_mgmt=WPA-PSK WPA-EAP
+
+# Set of accepted cipher suites (encryption algorithms) for pairwise keys
+# (unicast packets). This is a space separated list of algorithms:
+# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
+# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
+# Group cipher suite (encryption algorithm for broadcast and multicast frames)
+# is automatically selected based on this configuration. If only CCMP is
+# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
+# TKIP will be used as the group cipher.
+# (dot11RSNAConfigPairwiseCiphersTable)
+# Pairwise cipher for WPA (v1) (default: TKIP)
+#wpa_pairwise=TKIP CCMP
+# Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value)
+#rsn_pairwise=CCMP
+
+# Time interval for rekeying GTK (broadcast/multicast encryption keys) in
+# seconds. (dot11RSNAConfigGroupRekeyTime)
+#wpa_group_rekey=600
+
+# Rekey GTK when any STA that possesses the current GTK is leaving the BSS.
+# (dot11RSNAConfigGroupRekeyStrict)
+#wpa_strict_rekey=1
+
+# Time interval for rekeying GMK (master key used internally to generate GTKs
+# (in seconds).
+#wpa_gmk_rekey=86400
+
+# Maximum lifetime for PTK in seconds. This can be used to enforce rekeying of
+# PTK to mitigate some attacks against TKIP deficiencies.
+#wpa_ptk_rekey=600
+
+# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
+# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
+# authentication and key handshake before actually associating with a new AP.
+# (dot11RSNAPreauthenticationEnabled)
+#rsn_preauth=1
+#
+# Space separated list of interfaces from which pre-authentication frames are
+# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all
+# interface that are used for connections to other APs. This could include
+# wired interfaces and WDS links. The normal wireless data interface towards
+# associated stations (e.g., wlan0) should not be added, since
+# pre-authentication is only used with APs other than the currently associated
+# one.
+#rsn_preauth_interfaces=eth0
+
+# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e) is
+# allowed. This is only used with RSN/WPA2.
+# 0 = disabled (default)
+# 1 = enabled
+#peerkey=1
+
+# ieee80211w: Whether management frame protection (MFP) is enabled
+# 0 = disabled (default)
+# 1 = optional
+# 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
+#assoc_sa_query_max_timeout=1000
+
+# Association SA Query retry timeout (in TU = 1.024 ms; for MFP)
+# (time between two subsequent SA Query requests)
+# dot11AssociationSAQueryRetryTimeout, 1...4294967295
+#assoc_sa_query_retry_timeout=201
+
+# disable_pmksa_caching: Disable PMKSA caching
+# This parameter can be used to disable caching of PMKSA created through EAP
+# authentication. RSN preauthentication may still end up using PMKSA caching if
+# it is enabled (rsn_preauth=1).
+# 0 = PMKSA caching enabled (default)
+# 1 = PMKSA caching disabled
+#disable_pmksa_caching=0
+
+# okc: Opportunistic Key Caching (aka Proactive Key Caching)
+# Allow PMK cache to be shared opportunistically among configured interfaces
+# and BSSes (i.e., all configurations within a single hostapd process).
+# 0 = disabled (default)
+# 1 = enabled
+#okc=1
+
+# SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold)
+# This parameter defines how many open SAE instances can be in progress at the
+# same time before the anti-clogging mechanism is taken into use.
+#sae_anti_clogging_threshold=5
+
+# Enabled SAE finite cyclic groups
+# SAE implementation are required to support group 19 (ECC group defined over a
+# 256-bit prime order field). All groups that are supported by the
+# implementation are enabled by default. This configuration parameter can be
+# used to specify a limited set of allowed groups. The group values are listed
+# in the IANA registry:
+# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9
+#sae_groups=19 20 21 25 26
+
+##### IEEE 802.11r configuration ##############################################
+
+# Mobility Domain identifier (dot11FTMobilityDomainID, MDID)
+# MDID is used to indicate a group of APs (within an ESS, i.e., sharing the
+# same SSID) between which a STA can use Fast BSS Transition.
+# 2-octet identifier as a hex string.
+#mobility_domain=a1b2
+
+# PMK-R0 Key Holder identifier (dot11FTR0KeyHolderID)
+# 1 to 48 octet identifier.
+# This is configured with nas_identifier (see RADIUS client section above).
+
+# Default lifetime of the PMK-RO in minutes; range 1..65535
+# (dot11FTR0KeyLifetime)
+#r0_key_lifetime=10000
+
+# PMK-R1 Key Holder identifier (dot11FTR1KeyHolderID)
+# 6-octet identifier as a hex string.
+#r1_key_holder=000102030405
+
+# Reassociation deadline in time units (TUs / 1.024 ms; range 1000..65535)
+# (dot11FTReassociationDeadline)
+#reassociation_deadline=1000
+
+# List of R0KHs in the same Mobility Domain
+# format: <MAC address> <NAS Identifier> <128-bit key as hex string>
+# This list is used to map R0KH-ID (NAS Identifier) to a destination MAC
+# address when requesting PMK-R1 key from the R0KH that the STA used during the
+# Initial Mobility Domain Association.
+#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f
+#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff
+# And so on.. One line per R0KH.
+
+# List of R1KHs in the same Mobility Domain
+# format: <MAC address> <R1KH-ID> <128-bit key as hex string>
+# This list is used to map R1KH-ID to a destination MAC address when sending
+# PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD
+# that can request PMK-R1 keys.
+#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f
+#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff
+# And so on.. One line per R1KH.
+
+# Whether PMK-R1 push is enabled at R0KH
+# 0 = do not push PMK-R1 to all configured R1KHs (default)
+# 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived
+#pmk_r1_push=1
+
+# Whether to enable FT-over-DS
+# 0 = FT-over-DS disabled
+# 1 = FT-over-DS enabled (default)
+#ft_over_ds=1
+
+##### Neighbor table ##########################################################
+# Maximum number of entries kept in AP table (either for neigbor table or for
+# detecting Overlapping Legacy BSS Condition). The oldest entry will be
+# removed when adding a new entry that would make the list grow over this
+# limit. Note! WFA certification for IEEE 802.11g requires that OLBC is
+# enabled, so this field should not be set to 0 when using IEEE 802.11g.
+# default: 255
+#ap_table_max_size=255
+
+# Number of seconds of no frames received after which entries may be deleted
+# from the AP table. Since passive scanning is not usually performed frequently
+# this should not be set to very small value. In addition, there is no
+# guarantee that every scan cycle will receive beacon frames from the
+# neighboring APs.
+# default: 60
+#ap_table_expiration_time=3600
+
+# Maximum number of stations to track on the operating channel
+# This can be used to detect dualband capable stations before they have
+# associated, e.g., to provide guidance on which colocated BSS to use.
+# Default: 0 (disabled)
+#track_sta_max_num=100
+
+# Maximum age of a station tracking entry in seconds
+# Default: 180
+#track_sta_max_age=180
+
+# Do not reply to group-addressed Probe Request from a station that was seen on
+# another radio.
+# Default: Disabled
+#
+# This can be used with enabled track_sta_max_num configuration on another
+# interface controlled by the same hostapd process to restrict Probe Request
+# frame handling from replying to group-addressed Probe Request frames from a
+# station that has been detected to be capable of operating on another band,
+# e.g., to try to reduce likelihood of the station selecting a 2.4 GHz BSS when
+# the AP operates both a 2.4 GHz and 5 GHz BSS concurrently.
+#
+# Note: Enabling this can cause connectivity issues and increase latency for
+# discovering the AP.
+#no_probe_resp_if_seen_on=wlan1
+
+# Reject authentication from a station that was seen on another radio.
+# Default: Disabled
+#
+# This can be used with enabled track_sta_max_num configuration on another
+# interface controlled by the same hostapd process to reject authentication
+# attempts from a station that has been detected to be capable of operating on
+# another band, e.g., to try to reduce likelihood of the station selecting a
+# 2.4 GHz BSS when the AP operates both a 2.4 GHz and 5 GHz BSS concurrently.
+#
+# Note: Enabling this can cause connectivity issues and increase latency for
+# connecting with the AP.
+#no_auth_if_seen_on=wlan1
+
+##### Wi-Fi Protected Setup (WPS) #############################################
+
+# WPS state
+# 0 = WPS disabled (default)
+# 1 = WPS enabled, not configured
+# 2 = WPS enabled, configured
+#wps_state=2
+
+# Whether to manage this interface independently from other WPS interfaces
+# By default, a single hostapd process applies WPS operations to all configured
+# interfaces. This parameter can be used to disable that behavior for a subset
+# of interfaces. If this is set to non-zero for an interface, WPS commands
+# issued on that interface do not apply to other interfaces and WPS operations
+# performed on other interfaces do not affect this interface.
+#wps_independent=0
+
+# AP can be configured into a locked state where new WPS Registrar are not
+# accepted, but previously authorized Registrars (including the internal one)
+# can continue to add new Enrollees.
+#ap_setup_locked=1
+
+# Universally Unique IDentifier (UUID; see RFC 4122) of the device
+# This value is used as the UUID for the internal WPS Registrar. If the AP
+# is also using UPnP, this value should be set to the device's UPnP UUID.
+# If not configured, UUID will be generated based on the local MAC address.
+#uuid=12345678-9abc-def0-1234-56789abcdef0
+
+# Note: If wpa_psk_file is set, WPS is used to generate random, per-device PSKs
+# that will be appended to the wpa_psk_file. If wpa_psk_file is not set, the
+# default PSK (wpa_psk/wpa_passphrase) will be delivered to Enrollees. Use of
+# per-device PSKs is recommended as the more secure option (i.e., make sure to
+# set wpa_psk_file when using WPS with WPA-PSK).
+
+# When an Enrollee requests access to the network with PIN method, the Enrollee
+# PIN will need to be entered for the Registrar. PIN request notifications are
+# sent to hostapd ctrl_iface monitor. In addition, they can be written to a
+# text file that could be used, e.g., to populate the AP administration UI with
+# pending PIN requests. If the following variable is set, the PIN requests will
+# be written to the configured file.
+#wps_pin_requests=/var/run/hostapd_wps_pin_requests
+
+# Device Name
+# User-friendly description of device; up to 32 octets encoded in UTF-8
+#device_name=Wireless AP
+
+# Manufacturer
+# The manufacturer of the device (up to 64 ASCII characters)
+#manufacturer=Company
+
+# Model Name
+# Model of the device (up to 32 ASCII characters)
+#model_name=WAP
+
+# Model Number
+# Additional device description (up to 32 ASCII characters)
+#model_number=123
+
+# Serial Number
+# Serial number of the device (up to 32 characters)
+#serial_number=12345
+
+# Primary Device Type
+# Used format: <categ>-<OUI>-<subcateg>
+# categ = Category as an integer value
+# OUI = OUI and type octet as a 4-octet hex-encoded value; 0050F204 for
+#       default WPS OUI
+# subcateg = OUI-specific Sub Category as an integer value
+# Examples:
+#   1-0050F204-1 (Computer / PC)
+#   1-0050F204-2 (Computer / Server)
+#   5-0050F204-1 (Storage / NAS)
+#   6-0050F204-1 (Network Infrastructure / AP)
+#device_type=6-0050F204-1
+
+# OS Version
+# 4-octet operating system version number (hex string)
+#os_version=01020300
+
+# Config Methods
+# List of the supported configuration methods
+# Available methods: usba ethernet label display ext_nfc_token int_nfc_token
+#	nfc_interface push_button keypad virtual_display physical_display
+#	virtual_push_button physical_push_button
+#config_methods=label virtual_display virtual_push_button keypad
+
+# WPS capability discovery workaround for PBC with Windows 7
+# Windows 7 uses incorrect way of figuring out AP's WPS capabilities by acting
+# as a Registrar and using M1 from the AP. The config methods attribute in that
+# message is supposed to indicate only the configuration method supported by
+# the AP in Enrollee role, i.e., to add an external Registrar. For that case,
+# PBC shall not be used and as such, the PushButton config method is removed
+# from M1 by default. If pbc_in_m1=1 is included in the configuration file,
+# the PushButton config method is left in M1 (if included in config_methods
+# parameter) to allow Windows 7 to use PBC instead of PIN (e.g., from a label
+# in the AP).
+#pbc_in_m1=1
+
+# Static access point PIN for initial configuration and adding Registrars
+# If not set, hostapd will not allow external WPS Registrars to control the
+# access point. The AP PIN can also be set at runtime with hostapd_cli
+# wps_ap_pin command. Use of temporary (enabled by user action) and random
+# AP PIN is much more secure than configuring a static AP PIN here. As such,
+# use of the ap_pin parameter is not recommended if the AP device has means for
+# displaying a random PIN.
+#ap_pin=12345670
+
+# Skip building of automatic WPS credential
+# This can be used to allow the automatically generated Credential attribute to
+# be replaced with pre-configured Credential(s).
+#skip_cred_build=1
+
+# Additional Credential attribute(s)
+# This option can be used to add pre-configured Credential attributes into M8
+# message when acting as a Registrar. If skip_cred_build=1, this data will also
+# be able to override the Credential attribute that would have otherwise been
+# automatically generated based on network configuration. This configuration
+# option points to an external file that much contain the WPS Credential
+# attribute(s) as binary data.
+#extra_cred=hostapd.cred
+
+# Credential processing
+#   0 = process received credentials internally (default)
+#   1 = do not process received credentials; just pass them over ctrl_iface to
+#	external program(s)
+#   2 = process received credentials internally and pass them over ctrl_iface
+#	to external program(s)
+# Note: With wps_cred_processing=1, skip_cred_build should be set to 1 and
+# extra_cred be used to provide the Credential data for Enrollees.
+#
+# wps_cred_processing=1 will disabled automatic updates of hostapd.conf file
+# both for Credential processing and for marking AP Setup Locked based on
+# validation failures of AP PIN. An external program is responsible on updating
+# the configuration appropriately in this case.
+#wps_cred_processing=0
+
+# AP Settings Attributes for M7
+# By default, hostapd generates the AP Settings Attributes for M7 based on the
+# current configuration. It is possible to override this by providing a file
+# with pre-configured attributes. This is similar to extra_cred file format,
+# but the AP Settings attributes are not encapsulated in a Credential
+# attribute.
+#ap_settings=hostapd.ap_settings
+
+# WPS UPnP interface
+# If set, support for external Registrars is enabled.
+#upnp_iface=br0
+
+# Friendly Name (required for UPnP)
+# Short description for end use. Should be less than 64 characters.
+#friendly_name=WPS Access Point
+
+# Manufacturer URL (optional for UPnP)
+#manufacturer_url=http://www.example.com/
+
+# Model Description (recommended for UPnP)
+# Long description for end user. Should be less than 128 characters.
+#model_description=Wireless Access Point
+
+# Model URL (optional for UPnP)
+#model_url=http://www.example.com/model/
+
+# Universal Product Code (optional for UPnP)
+# 12-digit, all-numeric code that identifies the consumer package.
+#upc=123456789012
+
+# WPS RF Bands (a = 5G, b = 2.4G, g = 2.4G, ag = dual band, ad = 60 GHz)
+# This value should be set according to RF band(s) supported by the AP if
+# hw_mode is not set. For dual band dual concurrent devices, this needs to be
+# set to ag to allow both RF bands to be advertized.
+#wps_rf_bands=ag
+
+# NFC password token for WPS
+# These parameters can be used to configure a fixed NFC password token for the
+# AP. This can be generated, e.g., with nfc_pw_token from wpa_supplicant. When
+# these parameters are used, the AP is assumed to be deployed with a NFC tag
+# that includes the matching NFC password token (e.g., written based on the
+# NDEF record from nfc_pw_token).
+#
+#wps_nfc_dev_pw_id: Device Password ID (16..65535)
+#wps_nfc_dh_pubkey: Hexdump of DH Public Key
+#wps_nfc_dh_privkey: Hexdump of DH Private Key
+#wps_nfc_dev_pw: Hexdump of Device Password
+
+##### Wi-Fi Direct (P2P) ######################################################
+
+# Enable P2P Device management
+#manage_p2p=1
+
+# Allow cross connection
+#allow_cross_connection=1
+
+#### TDLS (IEEE 802.11z-2010) #################################################
+
+# Prohibit use of TDLS in this BSS
+#tdls_prohibit=1
+
+# Prohibit use of TDLS Channel Switching in this BSS
+#tdls_prohibit_chan_switch=1
+
+##### IEEE 802.11v-2011 #######################################################
+
+# Time advertisement
+# 0 = disabled (default)
+# 2 = UTC time at which the TSF timer is 0
+#time_advertisement=2
+
+# Local time zone as specified in 8.3 of IEEE Std 1003.1-2004:
+# stdoffset[dst[offset][,start[/time],end[/time]]]
+#time_zone=EST5
+
+# WNM-Sleep Mode (extended sleep mode for stations)
+# 0 = disabled (default)
+# 1 = enabled (allow stations to use WNM-Sleep Mode)
+#wnm_sleep_mode=1
+
+# BSS Transition Management
+# 0 = disabled (default)
+# 1 = enabled
+#bss_transition=1
+
+# Proxy ARP
+# 0 = disabled (default)
+# 1 = enabled
+#proxy_arp=1
+
+# IPv6 Neighbor Advertisement multicast-to-unicast conversion
+# This can be used with Proxy ARP to allow multicast NAs to be forwarded to
+# associated STAs using link layer unicast delivery.
+# 0 = disabled (default)
+# 1 = enabled
+#na_mcast_to_ucast=0
+
+##### IEEE 802.11u-2011 #######################################################
+
+# Enable Interworking service
+#interworking=1
+
+# Access Network Type
+# 0 = Private network
+# 1 = Private network with guest access
+# 2 = Chargeable public network
+# 3 = Free public network
+# 4 = Personal device network
+# 5 = Emergency services only network
+# 14 = Test or experimental
+# 15 = Wildcard
+#access_network_type=0
+
+# Whether the network provides connectivity to the Internet
+# 0 = Unspecified
+# 1 = Network provides connectivity to the Internet
+#internet=1
+
+# Additional Step Required for Access
+# Note: This is only used with open network, i.e., ASRA shall ne set to 0 if
+# RSN is used.
+#asra=0
+
+# Emergency services reachable
+#esr=0
+
+# Unauthenticated emergency service accessible
+#uesa=0
+
+# Venue Info (optional)
+# The available values are defined in IEEE Std 802.11u-2011, 7.3.1.34.
+# Example values (group,type):
+# 0,0 = Unspecified
+# 1,7 = Convention Center
+# 1,13 = Coffee Shop
+# 2,0 = Unspecified Business
+# 7,1  Private Residence
+#venue_group=7
+#venue_type=1
+
+# Homogeneous ESS identifier (optional; dot11HESSID)
+# If set, this shall be identifical to one of the BSSIDs in the homogeneous
+# ESS and this shall be set to the same value across all BSSs in homogeneous
+# ESS.
+#hessid=02:03:04:05:06:07
+
+# Roaming Consortium List
+# Arbitrary number of Roaming Consortium OIs can be configured with each line
+# adding a new OI to the list. The first three entries are available through
+# Beacon and Probe Response frames. Any additional entry will be available only
+# through ANQP queries. Each OI is between 3 and 15 octets and is configured as
+# a hexstring.
+#roaming_consortium=021122
+#roaming_consortium=2233445566
+
+# Venue Name information
+# This parameter can be used to configure one or more Venue Name Duples for
+# Venue Name ANQP information. Each entry has a two or three character language
+# code (ISO-639) separated by colon from the venue name string.
+# Note that venue_group and venue_type have to be set for Venue Name
+# 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
+# network.
+# format: <network auth type indicator (1-octet hex str)> [redirect URL]
+# Network Authentication Type Indicator values:
+# 00 = Acceptance of terms and conditions
+# 01 = On-line enrollment supported
+# 02 = http/https redirection
+# 03 = DNS redirection
+#network_auth_type=00
+#network_auth_type=02http://www.example.com/redirect/me/here/
+
+# IP Address Type Availability
+# format: <1-octet encoded value as hex str>
+# (ipv4_type & 0x3f) << 2 | (ipv6_type & 0x3)
+# ipv4_type:
+# 0 = Address type not available
+# 1 = Public IPv4 address available
+# 2 = Port-restricted IPv4 address available
+# 3 = Single NATed private IPv4 address available
+# 4 = Double NATed private IPv4 address available
+# 5 = Port-restricted IPv4 address and single NATed IPv4 address available
+# 6 = Port-restricted IPv4 address and double NATed IPv4 address available
+# 7 = Availability of the address type is not known
+# ipv6_type:
+# 0 = Address type not available
+# 1 = Address type available
+# 2 = Availability of the address type not known
+#ipaddr_type_availability=14
+
+# Domain Name
+# format: <variable-octet str>[,<variable-octet str>]
+#domain_name=example.com,another.example.com,yet-another.example.com
+
+# 3GPP Cellular Network information
+# format: <MCC1,MNC1>[;<MCC2,MNC2>][;...]
+#anqp_3gpp_cell_net=244,91;310,026;234,56
+
+# NAI Realm information
+# One or more realm can be advertised. Each nai_realm line adds a new realm to
+# the set. These parameters provide information for stations using Interworking
+# network selection to allow automatic connection to a network based on
+# credentials.
+# format: <encoding>,<NAI Realm(s)>[,<EAP Method 1>][,<EAP Method 2>][,...]
+# encoding:
+#	0 = Realm formatted in accordance with IETF RFC 4282
+#	1 = UTF-8 formatted character string that is not formatted in
+#	    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
+# ID 3 = Inner authentication EAP Method Type
+# ID 5 = Credential Type
+#	1 = SIM, 2 = USIM, 3 = NFC Secure Element, 4 = Hardware Token,
+#	5 = Softoken, 6 = Certificate, 7 = username/password, 9 = Anonymous,
+#	10 = Vendor Specific
+#nai_realm=0,example.com;example.net
+# EAP methods EAP-TLS with certificate and EAP-TTLS/MSCHAPv2 with
+# 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
+#hs20=1
+
+# Disable Downstream Group-Addressed Forwarding (DGAF)
+# This can be used to configure a network where no group-addressed frames are
+# allowed. The AP will not forward any group-address frames to the stations and
+# random GTKs are issued for each station to prevent associated stations from
+# 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)
+# separated by colon from the operator friendly name string.
+#hs20_oper_friendly_name=eng:Example operator
+#hs20_oper_friendly_name=fin:Esimerkkioperaattori
+
+# Connection Capability
+# This can be used to advertise what type of IP traffic can be sent through the
+# hotspot (e.g., due to firewall allowing/blocking protocols/ports).
+# format: <IP Protocol>:<Port Number>:<Status>
+# IP Protocol: 1 = ICMP, 6 = TCP, 17 = UDP
+# Port Number: 0..65535
+# Status: 0 = Closed, 1 = Open, 2 = Unknown
+# Each hs20_conn_capab line is added to the list of advertised tuples.
+#hs20_conn_capab=1:0:2
+#hs20_conn_capab=6:22:1
+#hs20_conn_capab=17:5060:0
+
+# WAN Metrics
+# format: <WAN Info>:<DL Speed>:<UL Speed>:<DL Load>:<UL Load>:<LMD>
+# WAN Info: B0-B1: Link Status, B2: Symmetric Link, B3: At Capabity
+#    (encoded as two hex digits)
+#    Link Status: 1 = Link up, 2 = Link down, 3 = Link in test state
+# Downlink Speed: Estimate of WAN backhaul link current downlink speed in kbps;
+#	1..4294967295; 0 = unknown
+# Uplink Speed: Estimate of WAN backhaul link current uplink speed in kbps
+#	1..4294967295; 0 = unknown
+# Downlink Load: Current load of downlink WAN connection (scaled to 255 = 100%)
+# Uplink Load: Current load of uplink WAN connection (scaled to 255 = 100%)
+# Load Measurement Duration: Duration for measuring downlink/uplink load in
+# tenths of a second (1..65535); 0 if load cannot be determined
+#hs20_wan_metrics=01:8000:1000:80:240:3000
+
+# Operating Class Indication
+# List of operating classes the BSSes in this ESS use. The Global operating
+# classes in Table E-4 of IEEE Std 802.11-2012 Annex E define the values that
+# can be used in this.
+# format: hexdump of operating class octets
+# for example, operating classes 81 (2.4 GHz channels 1-13) and 115 (5 GHz
+# 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=...
+
+##### Fast Session Transfer (FST) support #####################################
+#
+# The options in this section are only available when the build configuration
+# option CONFIG_FST is set while compiling hostapd. They allow this interface
+# to be a part of FST setup.
+#
+# FST is the transfer of a session from a channel to another channel, in the
+# same or different frequency bands.
+#
+# For detals, see IEEE Std 802.11ad-2012.
+
+# Identifier of an FST Group the interface belongs to.
+#fst_group_id=bond0
+
+# Interface priority within the FST Group.
+# Announcing a higher priority for an interface means declaring it more
+# preferable for FST switch.
+# fst_priority is in 1..255 range with 1 being the lowest priority.
+#fst_priority=100
+
+# Default LLT value for this interface in milliseconds. The value used in case
+# no value provided during session setup. Default is 50 ms.
+# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2
+# Transitioning between states).
+#fst_llt=100
+
+##### TESTING OPTIONS #########################################################
+#
+# The options in this section are only available when the build configuration
+# option CONFIG_TESTING_OPTIONS is set while compiling hostapd. They allow
+# testing some scenarios that are otherwise difficult to reproduce.
+#
+# Ignore probe requests sent to hostapd with the given probability, must be a
+# floating point number in the range [0, 1).
+#ignore_probe_probability=0.0
+#
+# Ignore authentication frames with the given probability
+#ignore_auth_probability=0.0
+#
+# Ignore association requests with the given probability
+#ignore_assoc_probability=0.0
+#
+# Ignore reassociation requests with the given probability
+#ignore_reassoc_probability=0.0
+#
+# Corrupt Key MIC in GTK rekey EAPOL-Key frames with the given probability
+#corrupt_gtk_rekey_mic_probability=0.0
+
+##### Multiple BSSID support ##################################################
+#
+# Above configuration is using the default interface (wlan#, or multi-SSID VLAN
+# interfaces). Other BSSIDs can be added by using separator 'bss' with
+# default interface name to be allocated for the data packets of the new BSS.
+#
+# hostapd will generate BSSID mask based on the BSSIDs that are
+# configured. hostapd will verify that dev_addr & MASK == dev_addr. If this is
+# not the case, the MAC address of the radio must be changed before starting
+# hostapd (ifconfig wlan0 hw ether <MAC addr>). If a BSSID is configured for
+# every secondary BSS, this limitation is not applied at hostapd and other
+# masks may be used if the driver supports them (e.g., swap the locally
+# administered bit)
+#
+# BSSIDs are assigned in order to each BSS, unless an explicit BSSID is
+# specified using the 'bssid' parameter.
+# If an explicit BSSID is specified, it must be chosen such that it:
+# - results in a valid MASK that covers it and the dev_addr
+# - 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.
+#
+#bss=wlan0_0
+#ssid=test2
+# most of the above items can be used here (apart from radio interface specific
+# items, like channel)
+
+#bss=wlan0_1
+#bssid=00:13:10:95:fe:0b
+# ...
diff --git a/hostap/hostapd/hostapd.deny b/hostap/hostapd/hostapd.deny
new file mode 100644
index 0000000..1616678
--- /dev/null
+++ b/hostap/hostapd/hostapd.deny
@@ -0,0 +1,5 @@
+# List of MAC addresses that are not allowed to authenticate (IEEE 802.11)
+# with the AP.
+00:20:30:40:50:60
+00:ab:cd:ef:12:34
+00:00:30:40:50:60
diff --git a/hostap/hostapd/hostapd.eap_user b/hostap/hostapd/hostapd.eap_user
new file mode 100644
index 0000000..00edc95
--- /dev/null
+++ b/hostap/hostapd/hostapd.eap_user
@@ -0,0 +1,103 @@
+# hostapd user database for integrated EAP server
+
+# Each line must contain an identity, EAP method(s), and an optional password
+# separated with whitespace (space or tab). The identity and password must be
+# double quoted ("user"). Password can alternatively be stored as
+# NtPasswordHash (16-byte MD4 hash of the unicode presentation of the password
+# in unicode) if it is used for MSCHAP or MSCHAPv2 authentication. This means
+# that the plaintext password does not need to be included in the user file.
+# Password hash is stored as hash:<16-octets of hex data> without quotation
+# marks.
+
+# [2] flag in the end of the line can be used to mark users for tunneled phase
+# 2 authentication (e.g., within EAP-PEAP). In these cases, an anonymous
+# identity can be used in the unencrypted phase 1 and the real user identity
+# is transmitted only within the encrypted tunnel in phase 2. If non-anonymous
+# access is needed, two user entries is needed, one for phase 1 and another
+# with the same username for phase 2.
+#
+# EAP-TLS, EAP-PEAP, EAP-TTLS, EAP-FAST, EAP-SIM, and EAP-AKA do not use
+# password option.
+# EAP-MD5, EAP-MSCHAPV2, EAP-GTC, EAP-PAX, EAP-PSK, and EAP-SAKE require a
+# password.
+# EAP-PEAP, EAP-TTLS, and EAP-FAST require Phase 2 configuration.
+#
+# * can be used as a wildcard to match any user identity. The main purposes for
+# this are to set anonymous phase 1 identity for EAP-PEAP and EAP-TTLS and to
+# avoid having to configure every certificate for EAP-TLS authentication. The
+# first matching entry is selected, so * should be used as the last phase 1
+# user entry.
+#
+# "prefix"* can be used to match the given prefix and anything after this. The
+# main purpose for this is to be able to avoid EAP method negotiation when the
+# method is using known prefix in identities (e.g., EAP-SIM and EAP-AKA). This
+# is only allowed for phase 1 identities.
+#
+# Multiple methods can be configured to make the authenticator try them one by
+# one until the peer accepts one. The method names are separated with a
+# comma (,).
+#
+# [ver=0] and [ver=1] flags after EAP type PEAP can be used to force PEAP
+# version based on the Phase 1 identity. Without this flag, the EAP
+# authenticator advertises the highest supported version and select the version
+# based on the first PEAP packet from the supplicant.
+#
+# EAP-TTLS supports both EAP and non-EAP authentication inside the tunnel.
+# Tunneled EAP methods are configured with standard EAP method name and [2]
+# flag. Non-EAP methods can be enabled by following method names: TTLS-PAP,
+# 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"
+"test user"	MD5	"secret"
+"example user"	TLS
+"DOMAIN\user"	MSCHAPV2	"password"
+"gtc user"	GTC	"password"
+"pax user"	PAX	"unknown"
+"pax.user@example.com"	PAX	0123456789abcdef0123456789abcdef
+"psk user"	PSK	"unknown"
+"psk.user@example.com"	PSK	0123456789abcdef0123456789abcdef
+"sake.user@example.com"	SAKE	0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+"ttls"		TTLS
+"not anonymous"	PEAP
+# Default to EAP-SIM and EAP-AKA based on fixed identity prefixes
+"0"*		AKA,TTLS,TLS,PEAP,SIM
+"1"*		SIM,TTLS,TLS,PEAP,AKA
+"2"*		AKA,TTLS,TLS,PEAP,SIM
+"3"*		SIM,TTLS,TLS,PEAP,AKA
+"4"*		AKA,TTLS,TLS,PEAP,SIM
+"5"*		SIM,TTLS,TLS,PEAP,AKA
+"6"*		AKA'
+"7"*		AKA'
+"8"*		AKA'
+
+# Wildcard for all other identities
+*		PEAP,TTLS,TLS,SIM,AKA
+
+# Phase 2 (tunnelled within EAP-PEAP or EAP-TTLS) users
+"t-md5"		MD5	"password"	[2]
+"DOMAIN\t-mschapv2"	MSCHAPV2	"password"	[2]
+"t-gtc"		GTC	"password"	[2]
+"not anonymous"	MSCHAPV2	"password"	[2]
+"user"		MD5,GTC,MSCHAPV2	"password"	[2]
+"test user"	MSCHAPV2	hash:000102030405060708090a0b0c0d0e0f	[2]
+"ttls-user"	TTLS-PAP,TTLS-CHAP,TTLS-MSCHAP,TTLS-MSCHAPV2	"password"	[2]
+
+# Default to EAP-SIM and EAP-AKA based on fixed identity prefixes in phase 2
+"0"*		AKA	[2]
+"1"*		SIM	[2]
+"2"*		AKA	[2]
+"3"*		SIM	[2]
+"4"*		AKA	[2]
+"5"*		SIM	[2]
+"6"*		AKA'	[2]
+"7"*		AKA'	[2]
+"8"*		AKA'	[2]
diff --git a/hostap/hostapd/hostapd.eap_user_sqlite b/hostap/hostapd/hostapd.eap_user_sqlite
new file mode 100644
index 0000000..826db34
--- /dev/null
+++ b/hostap/hostapd/hostapd.eap_user_sqlite
@@ -0,0 +1,26 @@
+CREATE TABLE users(
+	identity TEXT PRIMARY KEY,
+	methods TEXT,
+	password TEXT,
+	remediation TEXT,
+	phase2 INTEGER
+);
+
+CREATE TABLE wildcards(
+	identity TEXT PRIMARY KEY,
+	methods TEXT
+);
+
+INSERT INTO users(identity,methods,password,phase2) VALUES ('user','TTLS-MSCHAPV2','password',1);
+INSERT INTO users(identity,methods,password,phase2) VALUES ('DOMAIN\mschapv2 user','TTLS-MSCHAPV2','password',1);
+
+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/hostap/hostapd/hostapd.radius_clients b/hostap/hostapd/hostapd.radius_clients
new file mode 100644
index 0000000..3980427
--- /dev/null
+++ b/hostap/hostapd/hostapd.radius_clients
@@ -0,0 +1,4 @@
+# RADIUS client configuration for the RADIUS server
+10.1.2.3	secret passphrase
+192.168.1.0/24	another very secret passphrase
+0.0.0.0/0	radius
diff --git a/hostap/hostapd/hostapd.sim_db b/hostap/hostapd/hostapd.sim_db
new file mode 100644
index 0000000..01c593d
--- /dev/null
+++ b/hostap/hostapd/hostapd.sim_db
@@ -0,0 +1,9 @@
+# Example GSM authentication triplet file for EAP-SIM authenticator
+# IMSI:Kc:SRES:RAND
+# IMSI: ASCII string (numbers)
+# Kc: hex, 8 octets
+# SRES: hex, 4 octets
+# RAND: hex, 16 octets
+234567898765432:A0A1A2A3A4A5A6A7:D1D2D3D4:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+234567898765432:B0B1B2B3B4B5B6B7:E1E2E3E4:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+234567898765432:C0C1C2C3C4C5C6C7:F1F2F3F4:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
diff --git a/hostap/hostapd/hostapd.vlan b/hostap/hostapd/hostapd.vlan
new file mode 100644
index 0000000..98254fa
--- /dev/null
+++ b/hostap/hostapd/hostapd.vlan
@@ -0,0 +1,9 @@
+# VLAN ID to network interface mapping
+1	vlan1
+2	vlan2
+3	vlan3
+100	guest
+# Optional wildcard entry matching all VLAN IDs. The first # in the interface
+# name will be replaced with the VLAN ID. The network interfaces are created
+# (and removed) dynamically based on the use.
+*	vlan#
diff --git a/hostap/hostapd/hostapd.wpa_psk b/hostap/hostapd/hostapd.wpa_psk
new file mode 100644
index 0000000..0a9499a
--- /dev/null
+++ b/hostap/hostapd/hostapd.wpa_psk
@@ -0,0 +1,9 @@
+# List of WPA PSKs. Each line, except for empty lines and lines starting
+# with #, must contain a MAC address and PSK separated with a space.
+# Special MAC address 00:00:00:00:00:00 can be used to configure PSKs that
+# anyone can use. PSK can be configured as an ASCII passphrase of 8..63
+# characters or as a 256-bit hex PSK (64 hex digits).
+00:00:00:00:00:00 secret passphrase
+00:11:22:33:44:55 another passphrase
+00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+00:00:00:00:00:00 another passphrase for all STAs
diff --git a/hostap/hostapd/hostapd_cli.1 b/hostap/hostapd/hostapd_cli.1
new file mode 100644
index 0000000..218ea15
--- /dev/null
+++ b/hostap/hostapd/hostapd_cli.1
@@ -0,0 +1,89 @@
+.TH HOSTAPD_CLI 1 "April  7, 2005" hostapd_cli "hostapd command-line interface"
+.SH NAME
+hostapd_cli \- hostapd command-line interface
+.SH SYNOPSIS
+.B hostapd_cli
+[\-p<path>] [\-i<ifname>] [\-a<path>] [\-hvB] [command..]
+.SH DESCRIPTION
+This manual page documents briefly the
+.B hostapd_cli
+utility.
+.PP
+.B hostapd_cli
+is a command-line interface for the
+.B hostapd
+daemon.
+
+.B hostapd
+is a user space daemon for access point and authentication servers.
+It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server.
+For more information about
+.B hostapd
+refer to the
+.BR hostapd (8)
+man page.
+.SH OPTIONS
+A summary of options is included below.
+For a complete description, run
+.BR hostapd_cli
+from the command line.
+.TP
+.B \-p<path>
+Path to find control sockets.
+
+Default: /var/run/hostapd
+.TP
+.B \-i<ifname>
+Interface to listen on.
+
+Default: first interface found in socket path.
+.TP
+.B \-a<path>
+Run in daemon mode executing the action file based on events from hostapd.
+.TP
+.B \-B
+Run a daemon in the background.
+.TP
+.B \-h
+Show usage.
+.TP
+.B \-v
+Show hostapd_cli version.
+.SH COMMANDS
+A summary of commands is included below.
+For a complete description, run
+.BR hostapd_cli
+from the command line.
+.TP
+.B mib
+Get MIB variables (dot1x, dot11, radius).
+.TP
+.B sta <addr>
+Get MIB variables for one station.
+.TP
+.B all_sta
+Get MIB variables for all stations.
+.TP
+.B help
+Get usage help.
+.TP
+.B interface [ifname] 
+Show interfaces/select interface.
+.TP
+.B level <debug level>
+Change debug level.
+.TP
+.B license
+Show full
+.B hostapd_cli
+license.
+.TP
+.B quit
+Exit hostapd_cli.
+.SH SEE ALSO
+.BR hostapd (8).
+.SH AUTHOR
+hostapd_cli was written by Jouni Malinen <j@w1.fi>. 
+.PP
+This manual page was written by Faidon Liambotis <faidon@cube.gr>,
+for the Debian project (but may be used by others).
diff --git a/hostap/hostapd/hostapd_cli.c b/hostap/hostapd/hostapd_cli.c
new file mode 100644
index 0000000..46c2f37
--- /dev/null
+++ b/hostap/hostapd/hostapd_cli.c
@@ -0,0 +1,1461 @@
+/*
+ * hostapd - command line interface for hostapd daemon
+ * Copyright (c) 2004-2015, 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 <dirent.h>
+
+#include "common/wpa_ctrl.h"
+#include "common/ieee802_11_defs.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/edit.h"
+#include "common/version.h"
+
+
+static const char *const hostapd_cli_version =
+"hostapd_cli v" VERSION_STR "\n"
+"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors";
+
+
+static const char *const hostapd_cli_license =
+"This software may be distributed under the terms of the BSD license.\n"
+"See README for more details.\n";
+
+static const char *const hostapd_cli_full_license =
+"This software may be distributed under the terms of the BSD license.\n"
+"\n"
+"Redistribution and use in source and binary forms, with or without\n"
+"modification, are permitted provided that the following conditions are\n"
+"met:\n"
+"\n"
+"1. Redistributions of source code must retain the above copyright\n"
+"   notice, this list of conditions and the following disclaimer.\n"
+"\n"
+"2. Redistributions in binary form must reproduce the above copyright\n"
+"   notice, this list of conditions and the following disclaimer in the\n"
+"   documentation and/or other materials provided with the distribution.\n"
+"\n"
+"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
+"   names of its contributors may be used to endorse or promote products\n"
+"   derived from this software without specific prior written permission.\n"
+"\n"
+"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
+"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+"\n";
+
+static const char *const commands_help =
+"Commands:\n"
+"   mib                  get MIB variables (dot1x, dot11, radius)\n"
+"   sta <addr>           get MIB variables for one station\n"
+"   all_sta              get MIB variables for all stations\n"
+"   new_sta <addr>       add a new station\n"
+"   deauthenticate <addr>  deauthenticate a station\n"
+"   disassociate <addr>  disassociate a station\n"
+#ifdef CONFIG_IEEE80211W
+"   sa_query <addr>      send SA Query to a station\n"
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_WPS
+"   wps_pin <uuid> <pin> [timeout] [addr]  add WPS Enrollee PIN\n"
+"   wps_check_pin <PIN>  verify PIN checksum\n"
+"   wps_pbc              indicate button pushed to initiate PBC\n"
+"   wps_cancel           cancel the pending WPS operation\n"
+#ifdef CONFIG_WPS_NFC
+"   wps_nfc_tag_read <hexdump>  report read NFC tag with WPS data\n"
+"   wps_nfc_config_token <WPS/NDEF>  build NFC configuration token\n"
+"   wps_nfc_token <WPS/NDEF/enable/disable>  manager NFC password token\n"
+#endif /* CONFIG_WPS_NFC */
+"   wps_ap_pin <cmd> [params..]  enable/disable AP PIN\n"
+"   wps_config <SSID> <auth> <encr> <key>  configure AP\n"
+"   wps_get_status       show current WPS status\n"
+#endif /* CONFIG_WPS */
+"   get_config           show current configuration\n"
+"   help                 show this usage help\n"
+"   interface [ifname]   show interfaces/select interface\n"
+"   level <debug level>  change debug level\n"
+"   license              show full hostapd_cli license\n"
+"   quit                 exit hostapd_cli\n";
+
+static struct wpa_ctrl *ctrl_conn;
+static int hostapd_cli_quit = 0;
+static int hostapd_cli_attached = 0;
+
+#ifndef CONFIG_CTRL_IFACE_DIR
+#define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd"
+#endif /* CONFIG_CTRL_IFACE_DIR */
+static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
+static const char *client_socket_dir = NULL;
+
+static char *ctrl_ifname = NULL;
+static const char *pid_file = NULL;
+static const char *action_file = NULL;
+static int ping_interval = 5;
+static int interactive = 0;
+
+
+static void usage(void)
+{
+	fprintf(stderr, "%s\n", hostapd_cli_version);
+	fprintf(stderr,
+		"\n"
+		"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
+		"[-a<path>] \\\n"
+		"                   [-P<pid file>] [-G<ping interval>] [command..]\n"
+		"\n"
+		"Options:\n"
+		"   -h           help (show this usage text)\n"
+		"   -v           shown version information\n"
+		"   -p<path>     path to find control sockets (default: "
+		"/var/run/hostapd)\n"
+		"   -s<dir_path> dir path to open client sockets (default: "
+		CONFIG_CTRL_IFACE_DIR ")\n"
+		"   -a<file>     run in daemon mode executing the action file "
+		"based on events\n"
+		"                from hostapd\n"
+		"   -B           run a daemon in the background\n"
+		"   -i<ifname>   Interface to listen on (default: first "
+		"interface found in the\n"
+		"                socket path)\n\n"
+		"%s",
+		commands_help);
+}
+
+
+static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
+{
+	char *cfile;
+	int flen;
+
+	if (ifname == NULL)
+		return NULL;
+
+	flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
+	cfile = malloc(flen);
+	if (cfile == NULL)
+		return NULL;
+	snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
+
+	if (client_socket_dir && client_socket_dir[0] &&
+	    access(client_socket_dir, F_OK) < 0) {
+		perror(client_socket_dir);
+		free(cfile);
+		return NULL;
+	}
+
+	ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);
+	free(cfile);
+	return ctrl_conn;
+}
+
+
+static void hostapd_cli_close_connection(void)
+{
+	if (ctrl_conn == NULL)
+		return;
+
+	if (hostapd_cli_attached) {
+		wpa_ctrl_detach(ctrl_conn);
+		hostapd_cli_attached = 0;
+	}
+	wpa_ctrl_close(ctrl_conn);
+	ctrl_conn = NULL;
+}
+
+
+static void hostapd_cli_msg_cb(char *msg, size_t len)
+{
+	printf("%s\n", msg);
+}
+
+
+static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
+{
+	char buf[4096];
+	size_t len;
+	int ret;
+
+	if (ctrl_conn == NULL) {
+		printf("Not connected to hostapd - command dropped.\n");
+		return -1;
+	}
+	len = sizeof(buf) - 1;
+	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
+			       hostapd_cli_msg_cb);
+	if (ret == -2) {
+		printf("'%s' command timed out.\n", cmd);
+		return -2;
+	} else if (ret < 0) {
+		printf("'%s' command failed.\n", cmd);
+		return -1;
+	}
+	if (print) {
+		buf[len] = '\0';
+		printf("%s", buf);
+	}
+	return 0;
+}
+
+
+static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
+{
+	return _wpa_ctrl_command(ctrl, cmd, 1);
+}
+
+
+static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "PING");
+}
+
+
+static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "RELOG");
+}
+
+
+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");
+}
+
+
+static int hostapd_cli_exec(const char *program, const char *arg1,
+			    const char *arg2)
+{
+	char *arg;
+	size_t len;
+	int res;
+
+	len = os_strlen(arg1) + os_strlen(arg2) + 2;
+	arg = os_malloc(len);
+	if (arg == NULL)
+		return -1;
+	os_snprintf(arg, len, "%s %s", arg1, arg2);
+	res = os_exec(program, arg, 1);
+	os_free(arg);
+
+	return res;
+}
+
+
+static void hostapd_cli_action_process(char *msg, size_t len)
+{
+	const char *pos;
+
+	pos = msg;
+	if (*pos == '<') {
+		pos = os_strchr(pos, '>');
+		if (pos)
+			pos++;
+		else
+			pos = msg;
+	}
+
+	hostapd_cli_exec(action_file, ctrl_ifname, pos);
+}
+
+
+static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char buf[64];
+	if (argc < 1) {
+		printf("Invalid 'sta' command - at least one argument, STA "
+		       "address, is required.\n");
+		return -1;
+	}
+	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);
+}
+
+
+static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	char buf[64];
+	if (argc != 1) {
+		printf("Invalid 'new_sta' command - exactly one argument, STA "
+		       "address, is required.\n");
+		return -1;
+	}
+	snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	char buf[64];
+	if (argc < 1) {
+		printf("Invalid 'deauthenticate' command - exactly one "
+		       "argument, STA address, is required.\n");
+		return -1;
+	}
+	if (argc > 1)
+		os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
+			    argv[0], argv[1]);
+	else
+		os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	char buf[64];
+	if (argc < 1) {
+		printf("Invalid 'disassociate' command - exactly one "
+		       "argument, STA address, is required.\n");
+		return -1;
+	}
+	if (argc > 1)
+		os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
+			    argv[0], argv[1]);
+	else
+		os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
+#ifdef CONFIG_IEEE80211W
+static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	char buf[64];
+	if (argc != 1) {
+		printf("Invalid 'sa_query' command - exactly one argument, "
+		       "STA address, is required.\n");
+		return -1;
+	}
+	snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
+	return wpa_ctrl_command(ctrl, buf);
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+#ifdef CONFIG_WPS
+static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	char buf[256];
+	if (argc < 2) {
+		printf("Invalid 'wps_pin' command - at least two arguments, "
+		       "UUID and PIN, are required.\n");
+		return -1;
+	}
+	if (argc > 3)
+		snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
+			 argv[0], argv[1], argv[2], argv[3]);
+	else if (argc > 2)
+		snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
+			 argv[0], argv[1], argv[2]);
+	else
+		snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	char cmd[256];
+	int res;
+
+	if (argc != 1 && argc != 2) {
+		printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
+		       "- PIN to be verified\n");
+		return -1;
+	}
+
+	if (argc == 2)
+		res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
+				  argv[0], argv[1]);
+	else
+		res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
+				  argv[0]);
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long WPS_CHECK_PIN command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "WPS_PBC");
+}
+
+
+static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "WPS_CANCEL");
+}
+
+
+#ifdef CONFIG_WPS_NFC
+static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
+					    char *argv[])
+{
+	int ret;
+	char *buf;
+	size_t buflen;
+
+	if (argc != 1) {
+		printf("Invalid 'wps_nfc_tag_read' command - one argument "
+		       "is required.\n");
+		return -1;
+	}
+
+	buflen = 18 + os_strlen(argv[0]);
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return -1;
+	os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
+
+	ret = wpa_ctrl_command(ctrl, buf);
+	os_free(buf);
+
+	return ret;
+}
+
+
+static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
+						int argc, char *argv[])
+{
+	char cmd[64];
+	int res;
+
+	if (argc != 1) {
+		printf("Invalid 'wps_nfc_config_token' command - one argument "
+		       "is required.\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
+			  argv[0]);
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
+					 int argc, char *argv[])
+{
+	char cmd[64];
+	int res;
+
+	if (argc != 1) {
+		printf("Invalid 'wps_nfc_token' command - one argument is "
+		       "required.\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long WPS_NFC_TOKEN command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl,
+						int argc, char *argv[])
+{
+	char cmd[64];
+	int res;
+
+	if (argc != 2) {
+		printf("Invalid 'nfc_get_handover_sel' command - two arguments "
+		       "are required.\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s",
+			  argv[0], argv[1]);
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long NFC_GET_HANDOVER_SEL command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	char buf[64];
+	if (argc < 1) {
+		printf("Invalid 'wps_ap_pin' command - at least one argument "
+		       "is required.\n");
+		return -1;
+	}
+	if (argc > 2)
+		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
+			 argv[0], argv[1], argv[2]);
+	else if (argc > 1)
+		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
+			 argv[0], argv[1]);
+	else
+		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_wps_get_status(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "WPS_GET_STATUS");
+}
+
+
+static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	char buf[256];
+	char ssid_hex[2 * SSID_MAX_LEN + 1];
+	char key_hex[2 * 64 + 1];
+	int i;
+
+	if (argc < 1) {
+		printf("Invalid 'wps_config' command - at least two arguments "
+		       "are required.\n");
+		return -1;
+	}
+
+	ssid_hex[0] = '\0';
+	for (i = 0; i < SSID_MAX_LEN; i++) {
+		if (argv[0][i] == '\0')
+			break;
+		os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
+	}
+
+	key_hex[0] = '\0';
+	if (argc > 3) {
+		for (i = 0; i < 64; i++) {
+			if (argv[3][i] == '\0')
+				break;
+			os_snprintf(&key_hex[i * 2], 3, "%02x",
+				    argv[3][i]);
+		}
+	}
+
+	if (argc > 3)
+		snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
+			 ssid_hex, argv[1], argv[2], key_hex);
+	else if (argc > 2)
+		snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
+			 ssid_hex, argv[1], argv[2]);
+	else
+		snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
+			 ssid_hex, argv[1]);
+	return wpa_ctrl_command(ctrl, buf);
+}
+#endif /* CONFIG_WPS */
+
+
+static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
+					     char *argv[])
+{
+	char buf[300];
+	int res;
+
+	if (argc < 2) {
+		printf("Invalid 'disassoc_imminent' command - two arguments "
+		       "(STA addr and Disassociation Timer) are needed\n");
+		return -1;
+	}
+
+	res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
+			  argv[0], argv[1]);
+	if (os_snprintf_error(sizeof(buf), res))
+		return -1;
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	char buf[300];
+	int res;
+
+	if (argc < 3) {
+		printf("Invalid 'ess_disassoc' command - three arguments (STA "
+		       "addr, disassoc timer, and URL) are needed\n");
+		return -1;
+	}
+
+	res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s",
+			  argv[0], argv[1], argv[2]);
+	if (os_snprintf_error(sizeof(buf), res))
+		return -1;
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_bss_tm_req(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	char buf[2000], *tmp;
+	int res, i, total;
+
+	if (argc < 1) {
+		printf("Invalid 'bss_tm_req' command - at least one argument (STA addr) is needed\n");
+		return -1;
+	}
+
+	res = os_snprintf(buf, sizeof(buf), "BSS_TM_REQ %s", argv[0]);
+	if (os_snprintf_error(sizeof(buf), res))
+		return -1;
+
+	total = res;
+	for (i = 1; i < argc; i++) {
+		tmp = &buf[total];
+		res = os_snprintf(tmp, sizeof(buf) - total, " %s", argv[i]);
+		if (os_snprintf_error(sizeof(buf) - total, res))
+			return -1;
+		total += res;
+	}
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "GET_CONFIG");
+}
+
+
+static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
+				char *addr, size_t addr_len)
+{
+	char buf[4096], *pos;
+	size_t len;
+	int ret;
+
+	if (ctrl_conn == NULL) {
+		printf("Not connected to hostapd - command dropped.\n");
+		return -1;
+	}
+	len = sizeof(buf) - 1;
+	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
+			       hostapd_cli_msg_cb);
+	if (ret == -2) {
+		printf("'%s' command timed out.\n", cmd);
+		return -2;
+	} else if (ret < 0) {
+		printf("'%s' command failed.\n", cmd);
+		return -1;
+	}
+
+	buf[len] = '\0';
+	if (memcmp(buf, "FAIL", 4) == 0)
+		return -1;
+	printf("%s", buf);
+
+	pos = buf;
+	while (*pos != '\0' && *pos != '\n')
+		pos++;
+	*pos = '\0';
+	os_strlcpy(addr, buf, addr_len);
+	return 0;
+}
+
+
+static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	char addr[32], cmd[64];
+
+	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
+		return 0;
+	do {
+		snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
+
+	return -1;
+}
+
+
+static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	printf("%s", commands_help);
+	return 0;
+}
+
+
+static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
+	return 0;
+}
+
+
+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 (os_snprintf_error(sizeof(buf), res))
+		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 (os_snprintf_error(sizeof(buf), res))
+		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 (os_snprintf_error(sizeof(buf), res))
+		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 (os_snprintf_error(sizeof(buf), res))
+		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;
+	if (interactive)
+		eloop_terminate();
+	return 0;
+}
+
+
+static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256];
+	if (argc != 1) {
+		printf("Invalid LEVEL command: needs one argument (debug "
+		       "level)\n");
+		return 0;
+	}
+	snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
+{
+	struct dirent *dent;
+	DIR *dir;
+
+	dir = opendir(ctrl_iface_dir);
+	if (dir == NULL) {
+		printf("Control interface directory '%s' could not be "
+		       "openned.\n", ctrl_iface_dir);
+		return;
+	}
+
+	printf("Available interfaces:\n");
+	while ((dent = readdir(dir))) {
+		if (strcmp(dent->d_name, ".") == 0 ||
+		    strcmp(dent->d_name, "..") == 0)
+			continue;
+		printf("%s\n", dent->d_name);
+	}
+	closedir(dir);
+}
+
+
+static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	if (argc < 1) {
+		hostapd_cli_list_interfaces(ctrl);
+		return 0;
+	}
+
+	hostapd_cli_close_connection();
+	os_free(ctrl_ifname);
+	ctrl_ifname = os_strdup(argv[0]);
+	if (ctrl_ifname == NULL)
+		return -1;
+
+	if (hostapd_cli_open_connection(ctrl_ifname)) {
+		printf("Connected to interface '%s.\n", ctrl_ifname);
+		if (wpa_ctrl_attach(ctrl_conn) == 0) {
+			hostapd_cli_attached = 1;
+		} else {
+			printf("Warning: Failed to attach to "
+			       "hostapd.\n");
+		}
+	} else {
+		printf("Could not connect to interface '%s' - re-trying\n",
+			ctrl_ifname);
+	}
+	return 0;
+}
+
+
+static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256];
+	int res;
+
+	if (argc != 2) {
+		printf("Invalid SET command: needs two arguments (variable "
+		       "name and value)\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long SET command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256];
+	int res;
+
+	if (argc != 1) {
+		printf("Invalid GET command: needs one argument (variable "
+		       "name)\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long GET command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+#ifdef CONFIG_FST
+static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256];
+	int res;
+	int i;
+	int total;
+
+	if (argc <= 0) {
+		printf("FST command: parameters are required.\n");
+		return -1;
+	}
+
+	total = os_snprintf(cmd, sizeof(cmd), "FST-MANAGER");
+
+	for (i = 0; i < argc; i++) {
+		res = os_snprintf(cmd + total, sizeof(cmd) - total, " %s",
+				  argv[i]);
+		if (os_snprintf_error(sizeof(cmd) - total, res)) {
+			printf("Too long fst command.\n");
+			return -1;
+		}
+		total += res;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+#endif /* CONFIG_FST */
+
+
+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 (os_snprintf_error(sizeof(cmd), res)) {
+		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 (os_snprintf_error(sizeof(cmd) - total, res)) {
+			printf("Too long CHAN_SWITCH command.\n");
+			return -1;
+		}
+		total += res;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "ENABLE");
+}
+
+
+static int hostapd_cli_cmd_reload(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "RELOAD");
+}
+
+
+static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "DISABLE");
+}
+
+
+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 (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long VENDOR command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "ERP_FLUSH");
+}
+
+
+static int hostapd_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	char cmd[256];
+	int res;
+
+	res = os_snprintf(cmd, sizeof(cmd), "LOG_LEVEL%s%s%s%s",
+			  argc >= 1 ? " " : "",
+			  argc >= 1 ? argv[0] : "",
+			  argc == 2 ? " " : "",
+			  argc == 2 ? argv[1] : "");
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long option\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[]);
+};
+
+static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+	{ "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 },
+	{ "deauthenticate", hostapd_cli_cmd_deauthenticate },
+	{ "disassociate", hostapd_cli_cmd_disassociate },
+#ifdef CONFIG_IEEE80211W
+	{ "sa_query", hostapd_cli_cmd_sa_query },
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_WPS
+	{ "wps_pin", hostapd_cli_cmd_wps_pin },
+	{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
+	{ "wps_pbc", hostapd_cli_cmd_wps_pbc },
+	{ "wps_cancel", hostapd_cli_cmd_wps_cancel },
+#ifdef CONFIG_WPS_NFC
+	{ "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
+	{ "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
+	{ "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
+	{ "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel },
+#endif /* CONFIG_WPS_NFC */
+	{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
+	{ "wps_config", hostapd_cli_cmd_wps_config },
+	{ "wps_get_status", hostapd_cli_cmd_wps_get_status },
+#endif /* CONFIG_WPS */
+	{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
+	{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
+	{ "bss_tm_req", hostapd_cli_cmd_bss_tm_req },
+	{ "get_config", hostapd_cli_cmd_get_config },
+	{ "help", hostapd_cli_cmd_help },
+	{ "interface", hostapd_cli_cmd_interface },
+#ifdef CONFIG_FST
+	{ "fst", hostapd_cli_cmd_fst },
+#endif /* CONFIG_FST */
+	{ "level", hostapd_cli_cmd_level },
+	{ "license", hostapd_cli_cmd_license },
+	{ "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 },
+	{ "enable", hostapd_cli_cmd_enable },
+	{ "reload", hostapd_cli_cmd_reload },
+	{ "disable", hostapd_cli_cmd_disable },
+	{ "erp_flush", hostapd_cli_cmd_erp_flush },
+	{ "log_level", hostapd_cli_cmd_log_level },
+	{ NULL, NULL }
+};
+
+
+static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	const struct hostapd_cli_cmd *cmd, *match = NULL;
+	int count;
+
+	count = 0;
+	cmd = hostapd_cli_commands;
+	while (cmd->cmd) {
+		if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
+			match = cmd;
+			if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
+				/* we have an exact match */
+				count = 1;
+				break;
+			}
+			count++;
+		}
+		cmd++;
+	}
+
+	if (count > 1) {
+		printf("Ambiguous command '%s'; possible commands:", argv[0]);
+		cmd = hostapd_cli_commands;
+		while (cmd->cmd) {
+			if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
+			    0) {
+				printf(" %s", cmd->cmd);
+			}
+			cmd++;
+		}
+		printf("\n");
+	} else if (count == 0) {
+		printf("Unknown command '%s'\n", argv[0]);
+	} else {
+		match->handler(ctrl, argc - 1, &argv[1]);
+	}
+}
+
+
+static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
+				     int action_monitor)
+{
+	int first = 1;
+	if (ctrl_conn == NULL)
+		return;
+	while (wpa_ctrl_pending(ctrl)) {
+		char buf[256];
+		size_t len = sizeof(buf) - 1;
+		if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
+			buf[len] = '\0';
+			if (action_monitor)
+				hostapd_cli_action_process(buf, len);
+			else {
+				if (in_read && first)
+					printf("\n");
+				first = 0;
+				printf("%s\n", buf);
+			}
+		} else {
+			printf("Could not read pending message.\n");
+			break;
+		}
+	}
+}
+
+
+#define max_args 10
+
+static int tokenize_cmd(char *cmd, char *argv[])
+{
+	char *pos;
+	int argc = 0;
+
+	pos = cmd;
+	for (;;) {
+		while (*pos == ' ')
+			pos++;
+		if (*pos == '\0')
+			break;
+		argv[argc] = pos;
+		argc++;
+		if (argc == max_args)
+			break;
+		if (*pos == '"') {
+			char *pos2 = os_strrchr(pos, '"');
+			if (pos2)
+				pos = pos2 + 1;
+		}
+		while (*pos != '\0' && *pos != ' ')
+			pos++;
+		if (*pos == ' ')
+			*pos++ = '\0';
+	}
+
+	return argc;
+}
+
+
+static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
+{
+	if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
+		printf("Connection to hostapd lost - trying to reconnect\n");
+		hostapd_cli_close_connection();
+	}
+	if (!ctrl_conn) {
+		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
+		if (ctrl_conn) {
+			printf("Connection to hostapd re-established\n");
+			if (wpa_ctrl_attach(ctrl_conn) == 0) {
+				hostapd_cli_attached = 1;
+			} else {
+				printf("Warning: Failed to attach to "
+				       "hostapd.\n");
+			}
+		}
+	}
+	if (ctrl_conn)
+		hostapd_cli_recv_pending(ctrl_conn, 1, 0);
+	eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
+}
+
+
+static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
+{
+	eloop_terminate();
+}
+
+
+static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
+{
+	char *argv[max_args];
+	int argc;
+	argc = tokenize_cmd(cmd, argv);
+	if (argc)
+		wpa_request(ctrl_conn, argc, argv);
+}
+
+
+static void hostapd_cli_edit_eof_cb(void *ctx)
+{
+	eloop_terminate();
+}
+
+
+static void hostapd_cli_interactive(void)
+{
+	printf("\nInteractive mode\n\n");
+
+	eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
+	edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
+		  NULL, NULL, NULL, NULL);
+	eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
+
+	eloop_run();
+
+	edit_deinit(NULL, NULL);
+	eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
+}
+
+
+static void hostapd_cli_cleanup(void)
+{
+	hostapd_cli_close_connection();
+	if (pid_file)
+		os_daemonize_terminate(pid_file);
+
+	os_program_deinit();
+}
+
+
+static void hostapd_cli_action(struct wpa_ctrl *ctrl)
+{
+	fd_set rfds;
+	int fd, res;
+	struct timeval tv;
+	char buf[256];
+	size_t len;
+
+	fd = wpa_ctrl_get_fd(ctrl);
+
+	while (!hostapd_cli_quit) {
+		FD_ZERO(&rfds);
+		FD_SET(fd, &rfds);
+		tv.tv_sec = ping_interval;
+		tv.tv_usec = 0;
+		res = select(fd + 1, &rfds, NULL, NULL, &tv);
+		if (res < 0 && errno != EINTR) {
+			perror("select");
+			break;
+		}
+
+		if (FD_ISSET(fd, &rfds))
+			hostapd_cli_recv_pending(ctrl, 0, 1);
+		else {
+			len = sizeof(buf) - 1;
+			if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
+					     hostapd_cli_action_process) < 0 ||
+			    len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
+				printf("hostapd did not reply to PING "
+				       "command - exiting\n");
+				break;
+			}
+		}
+	}
+}
+
+
+int main(int argc, char *argv[])
+{
+	int warning_displayed = 0;
+	int c;
+	int daemonize = 0;
+
+	if (os_program_init())
+		return -1;
+
+	for (;;) {
+		c = getopt(argc, argv, "a:BhG:i:p:P:s:v");
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'a':
+			action_file = optarg;
+			break;
+		case 'B':
+			daemonize = 1;
+			break;
+		case 'G':
+			ping_interval = atoi(optarg);
+			break;
+		case 'h':
+			usage();
+			return 0;
+		case 'v':
+			printf("%s\n", hostapd_cli_version);
+			return 0;
+		case 'i':
+			os_free(ctrl_ifname);
+			ctrl_ifname = os_strdup(optarg);
+			break;
+		case 'p':
+			ctrl_iface_dir = optarg;
+			break;
+		case 'P':
+			pid_file = optarg;
+			break;
+		case 's':
+			client_socket_dir = optarg;
+			break;
+		default:
+			usage();
+			return -1;
+		}
+	}
+
+	interactive = (argc == optind) && (action_file == NULL);
+
+	if (interactive) {
+		printf("%s\n\n%s\n\n", hostapd_cli_version,
+		       hostapd_cli_license);
+	}
+
+	if (eloop_init())
+		return -1;
+
+	for (;;) {
+		if (ctrl_ifname == NULL) {
+			struct dirent *dent;
+			DIR *dir = opendir(ctrl_iface_dir);
+			if (dir) {
+				while ((dent = readdir(dir))) {
+					if (os_strcmp(dent->d_name, ".") == 0
+					    ||
+					    os_strcmp(dent->d_name, "..") == 0)
+						continue;
+					printf("Selected interface '%s'\n",
+					       dent->d_name);
+					ctrl_ifname = os_strdup(dent->d_name);
+					break;
+				}
+				closedir(dir);
+			}
+		}
+		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
+		if (ctrl_conn) {
+			if (warning_displayed)
+				printf("Connection established.\n");
+			break;
+		}
+
+		if (!interactive) {
+			perror("Failed to connect to hostapd - "
+			       "wpa_ctrl_open");
+			return -1;
+		}
+
+		if (!warning_displayed) {
+			printf("Could not connect to hostapd - re-trying\n");
+			warning_displayed = 1;
+		}
+		os_sleep(1, 0);
+		continue;
+	}
+
+	if (interactive || action_file) {
+		if (wpa_ctrl_attach(ctrl_conn) == 0) {
+			hostapd_cli_attached = 1;
+		} else {
+			printf("Warning: Failed to attach to hostapd.\n");
+			if (action_file)
+				return -1;
+		}
+	}
+
+	if (daemonize && os_daemonize(pid_file))
+		return -1;
+
+	if (interactive)
+		hostapd_cli_interactive();
+	else if (action_file)
+		hostapd_cli_action(ctrl_conn);
+	else
+		wpa_request(ctrl_conn, argc - optind, &argv[optind]);
+
+	os_free(ctrl_ifname);
+	eloop_destroy();
+	hostapd_cli_cleanup();
+	return 0;
+}
diff --git a/hostap/hostapd/logwatch/README b/hostap/hostapd/logwatch/README
new file mode 100644
index 0000000..3cba511
--- /dev/null
+++ b/hostap/hostapd/logwatch/README
@@ -0,0 +1,9 @@
+Logwatch is a utility for analyzing system logs and provide a human
+readable summary. This directory has a configuration file and a log
+analyzer script for parsing hostapd system log entries for logwatch.
+These files can be installed by copying them to following locations:
+
+/etc/log.d/conf/services/hostapd.conf
+/etc/log.d/scripts/services/hostapd
+
+More information about logwatch is available from http://www.logwatch.org/
diff --git a/hostap/hostapd/logwatch/hostapd b/hostap/hostapd/logwatch/hostapd
new file mode 100755
index 0000000..97b24ef
--- /dev/null
+++ b/hostap/hostapd/logwatch/hostapd
@@ -0,0 +1,65 @@
+#!/usr/bin/perl -w
+#
+# Logwatch script for hostapd
+#
+# Copyright 2005 Henrik Brix Andersen <brix@gentoo.org>
+# Distributed under the terms of the GNU General Public License v2
+# Alternatively, this file may be distributed under the terms of the BSD License
+
+use strict;
+
+my $debug = $ENV{'LOGWATCH_DEBUG'} || 0;
+my $detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;
+my $debugcounter = 1;
+
+my %hostapd;
+my @unmatched;
+
+if ($debug >= 5) {
+	print STDERR "\n\nDEBUG: Inside HOSTAPD Filter\n\n";
+}
+
+while (defined(my $line = <STDIN>)) {
+	if ($debug >= 5) {
+		print STDERR "DEBUG($debugcounter): $line";
+		$debugcounter++;
+	}
+    chomp($line);
+
+	if (my ($iface,$mac,$layer,$details) = ($line =~ /(.*?): STA (.*?) (.*?): (.*?)$/i)) {
+		unless ($detail == 10) {
+			# collapse association events
+			$details =~ s/^(associated) .*$/$1/i;
+		}
+		$hostapd{$iface}->{$mac}->{$layer}->{$details}++;
+	} else {
+		push @unmatched, "$line\n";
+	}
+}
+
+if (keys %hostapd) {
+	foreach my $iface (sort keys %hostapd) {
+		print "Interface $iface:\n";
+		foreach my $mac (sort keys %{$hostapd{$iface}}) {
+			print "  Client MAC Address $mac:\n";
+			foreach my $layer (sort keys %{$hostapd{$iface}->{$mac}}) {
+				print "    $layer:\n";
+				foreach my $details (sort keys %{$hostapd{$iface}->{$mac}->{$layer}}) {
+					print "      $details";
+					my $count = $hostapd{$iface}->{$mac}->{$layer}->{$details};
+					if ($count > 1) {
+						print ": " . $count . " Times";
+					}
+					print "\n";
+				}
+			}
+		}
+	}
+}
+
+if ($#unmatched >= 0) {
+    print "\n**Unmatched Entries**\n";
+    print @unmatched;
+}
+
+exit(0);
diff --git a/hostap/hostapd/logwatch/hostapd.conf b/hostap/hostapd/logwatch/hostapd.conf
new file mode 100644
index 0000000..5bebe6a
--- /dev/null
+++ b/hostap/hostapd/logwatch/hostapd.conf
@@ -0,0 +1,10 @@
+# Logwatch configuration for hostapd
+#
+# Copyright 2005 Henrik Brix Andersen <brix@gentoo.org>
+# Distributed under the terms of the GNU General Public License v2
+# Alternatively, this file may be distributed under the terms of the BSD License
+
+Title = "hostapd"
+LogFile = messages
+*OnlyService = hostapd
+*RemoveHeaders
diff --git a/hostap/hostapd/main.c b/hostap/hostapd/main.c
new file mode 100644
index 0000000..6c7406a
--- /dev/null
+++ b/hostap/hostapd/main.c
@@ -0,0 +1,805 @@
+/*
+ * hostapd / main()
+ * Copyright (c) 2002-2015, 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"
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <syslog.h>
+#include <grp.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
+#include "crypto/random.h"
+#include "crypto/tls.h"
+#include "common/version.h"
+#include "drivers/driver.h"
+#include "eap_server/eap.h"
+#include "eap_server/tncs.h"
+#include "ap/hostapd.h"
+#include "ap/ap_config.h"
+#include "ap/ap_drv_ops.h"
+#include "fst/fst.h"
+#include "config_file.h"
+#include "eap_register.h"
+#include "ctrl_iface.h"
+
+
+struct hapd_global {
+	void **drv_priv;
+	size_t drv_count;
+};
+
+static struct hapd_global global;
+
+
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
+			      int level, const char *txt, size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	char *format, *module_str;
+	int maxlen;
+	int conf_syslog_level, conf_stdout_level;
+	unsigned int conf_syslog, conf_stdout;
+
+	maxlen = len + 100;
+	format = os_malloc(maxlen);
+	if (!format)
+		return;
+
+	if (hapd && hapd->conf) {
+		conf_syslog_level = hapd->conf->logger_syslog_level;
+		conf_stdout_level = hapd->conf->logger_stdout_level;
+		conf_syslog = hapd->conf->logger_syslog;
+		conf_stdout = hapd->conf->logger_stdout;
+	} else {
+		conf_syslog_level = conf_stdout_level = 0;
+		conf_syslog = conf_stdout = (unsigned int) -1;
+	}
+
+	switch (module) {
+	case HOSTAPD_MODULE_IEEE80211:
+		module_str = "IEEE 802.11";
+		break;
+	case HOSTAPD_MODULE_IEEE8021X:
+		module_str = "IEEE 802.1X";
+		break;
+	case HOSTAPD_MODULE_RADIUS:
+		module_str = "RADIUS";
+		break;
+	case HOSTAPD_MODULE_WPA:
+		module_str = "WPA";
+		break;
+	case HOSTAPD_MODULE_DRIVER:
+		module_str = "DRIVER";
+		break;
+	case HOSTAPD_MODULE_IAPP:
+		module_str = "IAPP";
+		break;
+	case HOSTAPD_MODULE_MLME:
+		module_str = "MLME";
+		break;
+	default:
+		module_str = NULL;
+		break;
+	}
+
+	if (hapd && hapd->conf && addr)
+		os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s",
+			    hapd->conf->iface, MAC2STR(addr),
+			    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 ? " " : "",
+			    module_str ? module_str : "", txt);
+	else if (addr)
+		os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s",
+			    MAC2STR(addr), module_str ? " " : "",
+			    module_str ? module_str : "", txt);
+	else
+		os_snprintf(format, maxlen, "%s%s%s",
+			    module_str ? module_str : "",
+			    module_str ? ": " : "", txt);
+
+	if ((conf_stdout & module) && level >= conf_stdout_level) {
+		wpa_debug_print_timestamp();
+		wpa_printf(MSG_INFO, "%s", format);
+	}
+
+#ifndef CONFIG_NATIVE_WINDOWS
+	if ((conf_syslog & module) && level >= conf_syslog_level) {
+		int priority;
+		switch (level) {
+		case HOSTAPD_LEVEL_DEBUG_VERBOSE:
+		case HOSTAPD_LEVEL_DEBUG:
+			priority = LOG_DEBUG;
+			break;
+		case HOSTAPD_LEVEL_INFO:
+			priority = LOG_INFO;
+			break;
+		case HOSTAPD_LEVEL_NOTICE:
+			priority = LOG_NOTICE;
+			break;
+		case HOSTAPD_LEVEL_WARNING:
+			priority = LOG_WARNING;
+			break;
+		default:
+			priority = LOG_INFO;
+			break;
+		}
+		syslog(priority, "%s", format);
+	}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	os_free(format);
+}
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+
+/**
+ * hostapd_driver_init - Preparate driver interface
+ */
+static int hostapd_driver_init(struct hostapd_iface *iface)
+{
+	struct wpa_init_params params;
+	size_t i;
+	struct hostapd_data *hapd = iface->bss[0];
+	struct hostapd_bss_config *conf = hapd->conf;
+	u8 *b = conf->bssid;
+	struct wpa_driver_capa capa;
+
+	if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
+		wpa_printf(MSG_ERROR, "No hostapd driver wrapper available");
+		return -1;
+	}
+
+	/* Initialize the driver interface */
+	if (!(b[0] | b[1] | b[2] | b[3] | b[4] | b[5]))
+		b = NULL;
+
+	os_memset(&params, 0, sizeof(params));
+	for (i = 0; wpa_drivers[i]; i++) {
+		if (wpa_drivers[i] != hapd->driver)
+			continue;
+
+		if (global.drv_priv[i] == NULL &&
+		    wpa_drivers[i]->global_init) {
+			global.drv_priv[i] = wpa_drivers[i]->global_init();
+			if (global.drv_priv[i] == NULL) {
+				wpa_printf(MSG_ERROR, "Failed to initialize "
+					   "driver '%s'",
+					   wpa_drivers[i]->name);
+				return -1;
+			}
+		}
+
+		params.global_priv = global.drv_priv[i];
+		break;
+	}
+	params.bssid = b;
+	params.ifname = hapd->conf->iface;
+	params.driver_params = hapd->iconf->driver_params;
+	params.use_pae_group_addr = hapd->conf->use_pae_group_addr;
+
+	params.num_bridge = hapd->iface->num_bss;
+	params.bridge = os_calloc(hapd->iface->num_bss, sizeof(char *));
+	if (params.bridge == NULL)
+		return -1;
+	for (i = 0; i < hapd->iface->num_bss; i++) {
+		struct hostapd_data *bss = hapd->iface->bss[i];
+		if (bss->conf->bridge[0])
+			params.bridge[i] = bss->conf->bridge;
+	}
+
+	params.own_addr = hapd->own_addr;
+
+	hapd->drv_priv = hapd->driver->hapd_init(hapd, &params);
+	os_free(params.bridge);
+	if (hapd->drv_priv == NULL) {
+		wpa_printf(MSG_ERROR, "%s driver initialization failed.",
+			   hapd->driver->name);
+		hapd->driver = NULL;
+		return -1;
+	}
+
+	if (hapd->driver->get_capa &&
+	    hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) {
+		struct wowlan_triggers *triggs;
+
+		iface->drv_flags = capa.flags;
+		iface->smps_modes = capa.smps_modes;
+		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)
+{
+	struct hostapd_iface *iface;
+	int k;
+
+	wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname);
+	iface = hostapd_init(interfaces, config_fname);
+	if (!iface)
+		return NULL;
+	iface->interfaces = interfaces;
+
+	for (k = 0; k < debug; k++) {
+		if (iface->bss[0]->conf->logger_stdout_level > 0)
+			iface->bss[0]->conf->logger_stdout_level--;
+	}
+
+	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);
+		hostapd_interface_deinit_free(iface);
+		return NULL;
+	}
+
+	return iface;
+}
+
+
+/**
+ * handle_term - SIGINT and SIGTERM handler to terminate hostapd process
+ */
+static void handle_term(int sig, void *signal_ctx)
+{
+	wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig);
+	eloop_terminate();
+}
+
+
+#ifndef CONFIG_NATIVE_WINDOWS
+
+static int handle_reload_iface(struct hostapd_iface *iface, void *ctx)
+{
+	if (hostapd_reload_config(iface) < 0) {
+		wpa_printf(MSG_WARNING, "Failed to read new configuration "
+			   "file - continuing with old.");
+	}
+	return 0;
+}
+
+
+/**
+ * handle_reload - SIGHUP handler to reload configuration
+ */
+static void handle_reload(int sig, void *signal_ctx)
+{
+	struct hapd_interfaces *interfaces = signal_ctx;
+	wpa_printf(MSG_DEBUG, "Signal %d received - reloading configuration",
+		   sig);
+	hostapd_for_each_interface(interfaces, handle_reload_iface, NULL);
+}
+
+
+static void handle_dump_state(int sig, void *signal_ctx)
+{
+	/* Not used anymore - ignore signal */
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static int hostapd_global_init(struct hapd_interfaces *interfaces,
+			       const char *entropy_file)
+{
+	int i;
+
+	os_memset(&global, 0, sizeof(global));
+
+	hostapd_logger_register_cb(hostapd_logger_cb);
+
+	if (eap_server_register_methods()) {
+		wpa_printf(MSG_ERROR, "Failed to register EAP methods");
+		return -1;
+	}
+
+	if (eloop_init()) {
+		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+		return -1;
+	}
+
+	random_init(entropy_file);
+
+#ifndef CONFIG_NATIVE_WINDOWS
+	eloop_register_signal(SIGHUP, handle_reload, interfaces);
+	eloop_register_signal(SIGUSR1, handle_dump_state, interfaces);
+#endif /* CONFIG_NATIVE_WINDOWS */
+	eloop_register_signal_terminate(handle_term, interfaces);
+
+#ifndef CONFIG_NATIVE_WINDOWS
+	openlog("hostapd", 0, LOG_DAEMON);
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	for (i = 0; wpa_drivers[i]; i++)
+		global.drv_count++;
+	if (global.drv_count == 0) {
+		wpa_printf(MSG_ERROR, "No drivers enabled");
+		return -1;
+	}
+	global.drv_priv = os_calloc(global.drv_count, sizeof(void *));
+	if (global.drv_priv == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+static void hostapd_global_deinit(const char *pid_file)
+{
+	int i;
+
+	for (i = 0; wpa_drivers[i] && global.drv_priv; i++) {
+		if (!global.drv_priv[i])
+			continue;
+		wpa_drivers[i]->global_deinit(global.drv_priv[i]);
+	}
+	os_free(global.drv_priv);
+	global.drv_priv = NULL;
+
+#ifdef EAP_SERVER_TNC
+	tncs_global_deinit();
+#endif /* EAP_SERVER_TNC */
+
+	random_deinit();
+
+	eloop_destroy();
+
+#ifndef CONFIG_NATIVE_WINDOWS
+	closelog();
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	eap_server_unregister_methods();
+
+	os_daemonize_terminate(pid_file);
+}
+
+
+static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize,
+			      const char *pid_file)
+{
+#ifdef EAP_SERVER_TNC
+	int tnc = 0;
+	size_t i, k;
+
+	for (i = 0; !tnc && i < ifaces->count; i++) {
+		for (k = 0; k < ifaces->iface[i]->num_bss; k++) {
+			if (ifaces->iface[i]->bss[0]->conf->tnc) {
+				tnc++;
+				break;
+			}
+		}
+	}
+
+	if (tnc && tncs_global_init() < 0) {
+		wpa_printf(MSG_ERROR, "Failed to initialize TNCS");
+		return -1;
+	}
+#endif /* EAP_SERVER_TNC */
+
+	if (daemonize && os_daemonize(pid_file)) {
+		wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
+		return -1;
+	}
+
+	eloop_run();
+
+	return 0;
+}
+
+
+static void show_version(void)
+{
+	fprintf(stderr,
+		"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-2015, Jouni Malinen <j@w1.fi> "
+		"and contributors\n");
+}
+
+
+static void usage(void)
+{
+	show_version();
+	fprintf(stderr,
+		"\n"
+		"usage: hostapd [-hdBKtv] [-P <PID file>] [-e <entropy file>] "
+		"\\\n"
+		"         [-g <global ctrl_iface>] [-G <group>] \\\n"
+		"         <configuration file(s)>\n"
+		"\n"
+		"options:\n"
+		"   -h   show this usage\n"
+		"   -d   show more debug messages (-dd for even more)\n"
+		"   -B   run daemon in the background\n"
+		"   -e   entropy file\n"
+		"   -g   global control interface path\n"
+		"   -G   group for control interfaces\n"
+		"   -P   PID file\n"
+		"   -K   include key data in debug messages\n"
+#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");
+
+	exit(1);
+}
+
+
+static const char * hostapd_msg_ifname_cb(void *ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	if (hapd && hapd->iconf && hapd->iconf->bss &&
+	    hapd->iconf->num_bss > 0 && hapd->iconf->bss[0])
+		return hapd->iconf->bss[0]->iface;
+	return NULL;
+}
+
+
+static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces,
+					 const char *path)
+{
+	char *pos;
+	os_free(interfaces->global_iface_path);
+	interfaces->global_iface_path = os_strdup(path);
+	if (interfaces->global_iface_path == NULL)
+		return -1;
+	pos = os_strrchr(interfaces->global_iface_path, '/');
+	if (pos == NULL) {
+		wpa_printf(MSG_ERROR, "No '/' in the global control interface "
+			   "file");
+		os_free(interfaces->global_iface_path);
+		interfaces->global_iface_path = NULL;
+		return -1;
+	}
+
+	*pos = '\0';
+	interfaces->global_iface_name = pos + 1;
+
+	return 0;
+}
+
+
+static int hostapd_get_ctrl_iface_group(struct hapd_interfaces *interfaces,
+					const char *group)
+{
+#ifndef CONFIG_NATIVE_WINDOWS
+	struct group *grp;
+	grp = getgrnam(group);
+	if (grp == NULL) {
+		wpa_printf(MSG_ERROR, "Unknown group '%s'", group);
+		return -1;
+	}
+	interfaces->ctrl_iface_group = grp->gr_gid;
+#endif /* CONFIG_NATIVE_WINDOWS */
+	return 0;
+}
+
+
+#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 */
+
+
+#ifndef HOSTAPD_CLEANUP_INTERVAL
+#define HOSTAPD_CLEANUP_INTERVAL 10
+#endif /* HOSTAPD_CLEANUP_INTERVAL */
+
+static int hostapd_periodic_call(struct hostapd_iface *iface, void *ctx)
+{
+	hostapd_periodic_iface(iface);
+	return 0;
+}
+
+
+/* Periodic cleanup tasks */
+static void hostapd_periodic(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hapd_interfaces *interfaces = eloop_ctx;
+
+	eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
+			       hostapd_periodic, interfaces, NULL);
+	hostapd_for_each_interface(interfaces, hostapd_periodic_call, NULL);
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct hapd_interfaces interfaces;
+	int ret = 1;
+	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;
+
+	os_memset(&interfaces, 0, sizeof(interfaces));
+	interfaces.reload_config = hostapd_reload_config;
+	interfaces.config_read_cb = hostapd_config_read;
+	interfaces.for_each_interface = hostapd_for_each_interface;
+	interfaces.ctrl_iface_init = hostapd_ctrl_iface_init;
+	interfaces.ctrl_iface_deinit = hostapd_ctrl_iface_deinit;
+	interfaces.driver_init = hostapd_driver_init;
+	interfaces.global_iface_path = NULL;
+	interfaces.global_iface_name = NULL;
+	interfaces.global_ctrl_sock = -1;
+	interfaces.global_ctrl_dst = NULL;
+
+	for (;;) {
+		c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:");
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'h':
+			usage();
+			break;
+		case 'd':
+			debug++;
+			if (wpa_debug_level > 0)
+				wpa_debug_level--;
+			break;
+		case 'B':
+			daemonize++;
+			break;
+		case 'e':
+			entropy_file = optarg;
+			break;
+		case 'f':
+			log_file = optarg;
+			break;
+		case 'K':
+			wpa_debug_show_keys++;
+			break;
+		case 'P':
+			os_free(pid_file);
+			pid_file = os_rel2abs_path(optarg);
+			break;
+		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);
+			break;
+		case 'g':
+			if (hostapd_get_global_ctrl_iface(&interfaces, optarg))
+				return -1;
+			break;
+		case 'G':
+			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 &&
+	    num_bss_configs == 0)
+		usage();
+
+	wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb);
+
+	if (log_file)
+		wpa_debug_open_file(log_file);
+	else
+		wpa_debug_setup_stdout();
+#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 || 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");
+			return -1;
+		}
+	}
+
+	if (hostapd_global_init(&interfaces, entropy_file)) {
+		wpa_printf(MSG_ERROR, "Failed to initialize global context");
+		return -1;
+	}
+
+	eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
+			       hostapd_periodic, &interfaces, NULL);
+
+	if (fst_global_init()) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to initialize global FST context");
+		goto out;
+	}
+
+#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
+	if (!fst_global_add_ctrl(fst_ctrl_cli))
+		wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
+#endif /* CONFIG_FST && CONFIG_CTRL_IFACE */
+
+	/* 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],
+							     debug);
+		if (!interfaces.iface[i]) {
+			wpa_printf(MSG_ERROR, "Failed to initialize interface");
+			goto out;
+		}
+	}
+
+	/* 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)) {
+		wpa_printf(MSG_ERROR, "Failed to start eloop");
+		goto out;
+	}
+
+	ret = 0;
+
+ out:
+	hostapd_global_ctrl_iface_deinit(&interfaces);
+	/* Deinitialize all interfaces */
+	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);
+
+	eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
+	hostapd_global_deinit(pid_file);
+	os_free(pid_file);
+
+	if (log_file)
+		wpa_debug_close_file();
+	wpa_debug_close_linux_tracing();
+
+	os_free(bss_config);
+
+	fst_global_deinit();
+
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/hostap/hostapd/nt_password_hash.c b/hostap/hostapd/nt_password_hash.c
new file mode 100644
index 0000000..7064b9c
--- /dev/null
+++ b/hostap/hostapd/nt_password_hash.c
@@ -0,0 +1,47 @@
+/*
+ * hostapd - Plaintext password to NtPasswordHash
+ * Copyright (c) 2005, 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/ms_funcs.h"
+
+
+int main(int argc, char *argv[])
+{
+	unsigned char password_hash[16];
+	size_t i;
+	char *password, buf[64], *pos;
+
+	if (argc > 1)
+		password = argv[1];
+	else {
+		if (fgets(buf, sizeof(buf), stdin) == NULL) {
+			printf("Failed to read password\n");
+			return 1;
+		}
+		buf[sizeof(buf) - 1] = '\0';
+		pos = buf;
+		while (*pos != '\0') {
+			if (*pos == '\r' || *pos == '\n') {
+				*pos = '\0';
+				break;
+			}
+			pos++;
+		}
+		password = buf;
+	}
+
+	if (nt_password_hash((u8 *) password, strlen(password), password_hash))
+		return -1;
+	for (i = 0; i < sizeof(password_hash); i++)
+		printf("%02x", password_hash[i]);
+	printf("\n");
+
+	return 0;
+}
diff --git a/hostap/hostapd/src b/hostap/hostapd/src
new file mode 120000
index 0000000..5cd551c
--- /dev/null
+++ b/hostap/hostapd/src
@@ -0,0 +1 @@
+../src
\ No newline at end of file
diff --git a/hostap/hostapd/wired.conf b/hostap/hostapd/wired.conf
new file mode 100644
index 0000000..956f8c5
--- /dev/null
+++ b/hostap/hostapd/wired.conf
@@ -0,0 +1,40 @@
+##### hostapd configuration file ##############################################
+# Empty lines and lines starting with # are ignored
+
+# Example configuration file for wired authenticator. See hostapd.conf for
+# more details.
+
+interface=eth0
+driver=wired
+logger_stdout=-1
+logger_stdout_level=1
+debug=2
+dump_file=/tmp/hostapd.dump
+
+ieee8021x=1
+eap_reauth_period=3600
+
+use_pae_group_addr=1
+
+
+##### RADIUS configuration ####################################################
+# for IEEE 802.1X with external Authentication Server, IEEE 802.11
+# authentication with external ACL for MAC addresses, and accounting
+
+# The own IP address of the access point (used as NAS-IP-Address)
+own_ip_addr=127.0.0.1
+
+# Optional NAS-Identifier string for RADIUS messages. When used, this should be
+# a unique to the NAS within the scope of the RADIUS server. For example, a
+# fully qualified domain name can be used here.
+nas_identifier=ap.example.com
+
+# RADIUS authentication server
+auth_server_addr=127.0.0.1
+auth_server_port=1812
+auth_server_shared_secret=radius
+
+# RADIUS accounting server
+acct_server_addr=127.0.0.1
+acct_server_port=1813
+acct_server_shared_secret=radius
diff --git a/hostap/hostapd/wps-ap-nfc.py b/hostap/hostapd/wps-ap-nfc.py
new file mode 100755
index 0000000..2fc3012
--- /dev/null
+++ b/hostap/hostapd/wps-ap-nfc.py
@@ -0,0 +1,342 @@
+#!/usr/bin/python
+#
+# Example nfcpy to hostapd wrapper for WPS 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 argparse
+
+import nfc
+import nfc.ndef
+import nfc.llcp
+import nfc.handover
+
+import logging
+
+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 = []
+    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 hostapd: ", error
+            return None
+
+    if len(ifaces) < 1:
+        print "No hostapd control interface found"
+        return None
+
+    for ctrl in ifaces:
+        try:
+            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
+    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
+    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
+    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
+    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):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    return wpas.request("NFC_REPORT_HANDOVER RESP WPS " +
+                        str(req).encode("hex") + " " +
+                        str(sel).encode("hex"))
+
+
+class HandoverServer(nfc.handover.HandoverServer):
+    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):
+        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":
+                summary("WPS carrier type match - add WPS carrier record")
+                data = wpas_get_handover_sel()
+                if data is None:
+                    summary("Could not get handover select carrier record from hostapd")
+                    continue
+                print "Handover select carrier record from hostapd:"
+                print data.encode("hex")
+                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:"
+        try:
+            print sel.pretty()
+        except Exception, e:
+            print e
+        print str(sel).encode("hex")
+
+        summary("Sending handover select")
+        self.success = True
+        return sel
+
+
+def wps_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 hostapd")
+                success = wpas_tag_read(tag.ndef.message)
+                break
+    else:
+        summary("Empty tag")
+
+    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, 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"
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
+
+
+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"
+    clf.connect(rdwr={'on-connect': rdwr_connected_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 = 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
+
+    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 not clf.open("usb"):
+            print "Could not open connection with an NFC device"
+            raise SystemExit
+
+        if args.command == "write-config":
+            wps_write_config_tag(clf, wait_remove=not args.no_wait)
+            raise SystemExit
+
+        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"
+
+            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/hostap/hs20/client/Android.mk b/hostap/hs20/client/Android.mk
new file mode 100644
index 0000000..b23ac17
--- /dev/null
+++ b/hostap/hs20/client/Android.mk
@@ -0,0 +1,81 @@
+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
+
+# We try to keep this compiling against older platform versions.
+# The new icu location (external/icu) exports its own headers, but
+# the older versions in external/icu4c don't, and we need to add those
+# headers to the include path by hand.
+ifeq ($(wildcard external/icu),)
+INCLUDES += external/icu4c/common
+else
+# The LOCAL_EXPORT_C_INCLUDE_DIRS from ICU did not seem to fully resolve the
+# build (e.g., "mm -B" failed to build, but following that with "mm" allowed
+# the build to complete). For now, add the include directory manually here for
+# Android 5.0.
+ver = $(filter 5.0%,$(PLATFORM_VERSION))
+ifneq (,$(strip $(ver)))
+INCLUDES += external/icu/icu4c/source/common
+endif
+endif
+
+
+L_CFLAGS += -DCONFIG_CTRL_IFACE
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
+
+OBJS = spp_client.c
+OBJS += oma_dm_client.c
+OBJS += osu_client.c
+OBJS += est.c
+OBJS += ../../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
+
+L_CFLAGS += -Wno-unused-parameter
+
+
+########################
+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/hostap/hs20/client/Makefile b/hostap/hs20/client/Makefile
new file mode 100644
index 0000000..94cd5f1
--- /dev/null
+++ b/hostap/hs20/client/Makefile
@@ -0,0 +1,100 @@
+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)
+
+# Allow static/custom linking of libcurl.
+ifdef CUST_CURL_LINKAGE
+LIBS += ${CUST_CURL_LINKAGE}
+else
+LIBS += -lcurl
+endif
+
+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/hostap/hs20/client/devdetail.xml b/hostap/hs20/client/devdetail.xml
new file mode 100644
index 0000000..6d0389e
--- /dev/null
+++ b/hostap/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/hostap/hs20/client/devinfo.xml b/hostap/hs20/client/devinfo.xml
new file mode 100644
index 0000000..d48a520
--- /dev/null
+++ b/hostap/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/hostap/hs20/client/est.c b/hostap/hs20/client/est.c
new file mode 100644
index 0000000..ec05bc4
--- /dev/null
+++ b/hostap/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/hostap/hs20/client/oma_dm_client.c b/hostap/hs20/client/oma_dm_client.c
new file mode 100644
index 0000000..5854b72
--- /dev/null
+++ b/hostap/hs20/client/oma_dm_client.c
@@ -0,0 +1,1392 @@
+/*
+ * 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);
+	if (data == NULL) {
+		wpa_printf(MSG_INFO, "Invalid data");
+		return DM_RESP_BAD_REQUEST;
+	}
+	wpa_printf(MSG_INFO, "Data: %s", data);
+	wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data);
+	write_summary(ctx, "Launch browser to URI '%s'", data);
+	res = hs20_web_browser(data);
+	xml_node_get_text_free(ctx->xml, data);
+	if (res > 0) {
+		wpa_printf(MSG_INFO, "User response in browser completed successfully");
+		write_summary(ctx, "User response in browser completed successfully");
+		return DM_RESP_OK;
+	} else {
+		wpa_printf(MSG_INFO, "Failed to receive user response");
+		write_summary(ctx, "Failed to receive user response");
+		return DM_RESP_COMMAND_FAILED;
+	}
+}
+
+
+static int oma_dm_exec_get_cert(struct hs20_osu_client *ctx, xml_node_t *exec)
+{
+	xml_node_t *node, *getcert;
+	char *data;
+	const char *name;
+	int res;
+
+	wpa_printf(MSG_INFO, "Client certificate enrollment");
+	write_summary(ctx, "Client certificate enrollment");
+
+	node = get_node(ctx->xml, exec, "Item/Data");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No Data node found");
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	data = xml_node_get_text(ctx->xml, node);
+	if (data == NULL) {
+		wpa_printf(MSG_INFO, "Invalid data");
+		return DM_RESP_BAD_REQUEST;
+	}
+	wpa_printf(MSG_INFO, "Data: %s", data);
+	getcert = xml_node_from_buf(ctx->xml, data);
+	xml_node_get_text_free(ctx->xml, data);
+
+	if (getcert == NULL) {
+		wpa_printf(MSG_INFO, "Could not parse Item/Data node contents");
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	debug_dump_node(ctx, "OMA-DM getCertificate", getcert);
+
+	name = xml_node_get_localname(ctx->xml, getcert);
+	if (name == NULL || os_strcasecmp(name, "getCertificate") != 0) {
+		wpa_printf(MSG_INFO, "Unexpected getCertificate node name '%s'",
+			   name);
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	res = osu_get_certificate(ctx, getcert);
+
+	xml_node_free(ctx->xml, getcert);
+
+	return res == 0 ? DM_RESP_OK : DM_RESP_COMMAND_FAILED;
+}
+
+
+static int oma_dm_exec(struct hs20_osu_client *ctx, xml_node_t *exec)
+{
+	char *locuri;
+	int ret;
+
+	locuri = oma_dm_get_target_locuri(ctx, exec);
+	if (locuri == NULL) {
+		wpa_printf(MSG_INFO, "No Target LocURI node found");
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
+
+	if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
+			  "launchBrowserToURI") == 0) {
+		ret = oma_dm_exec_browser(ctx, exec);
+	} else if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
+			  "getCertificate") == 0) {
+		ret = oma_dm_exec_get_cert(ctx, exec);
+	} else {
+		wpa_printf(MSG_INFO, "Unsupported exec Target LocURI");
+		ret = DM_RESP_NOT_FOUND;
+	}
+	os_free(locuri);
+
+	return ret;
+}
+
+
+static int oma_dm_run_add(struct hs20_osu_client *ctx, const char *locuri,
+			  xml_node_t *add, xml_node_t *pps,
+			  const char *pps_fname)
+{
+	const char *pos;
+	size_t fqdn_len;
+	xml_node_t *node, *tnds, *unode, *pps_node;
+	char *data, *uri, *upos, *end;
+	int use_tnds = 0;
+	size_t uri_len;
+
+	wpa_printf(MSG_INFO, "Add command target LocURI: %s", locuri);
+
+	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+		wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi");
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos = locuri + 8;
+
+	if (ctx->fqdn == NULL)
+		return DM_RESP_COMMAND_FAILED;
+	fqdn_len = os_strlen(ctx->fqdn);
+	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+	    pos[fqdn_len] != '/') {
+		wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi/%s",
+			   ctx->fqdn);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos += fqdn_len + 1;
+
+	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+		wpa_printf(MSG_INFO,
+			   "Do not allow Add outside ./Wi-Fi/%s/PerProviderSubscription",
+			   ctx->fqdn);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos += 24;
+
+	wpa_printf(MSG_INFO, "Add command for PPS node %s", pos);
+
+	pps_node = get_node(ctx->xml, pps, pos);
+	if (pps_node) {
+		wpa_printf(MSG_INFO, "Specified PPS node exists already");
+		return DM_RESP_ALREADY_EXISTS;
+	}
+
+	uri = os_strdup(pos);
+	if (uri == NULL)
+		return DM_RESP_COMMAND_FAILED;
+	while (!pps_node) {
+		upos = os_strrchr(uri, '/');
+		if (!upos)
+			break;
+		upos[0] = '\0';
+		pps_node = get_node(ctx->xml, pps, uri);
+		wpa_printf(MSG_INFO, "Node %s %s", uri,
+			   pps_node ? "exists" : "does not exist");
+	}
+
+	wpa_printf(MSG_INFO, "Parent URI: %s", uri);
+
+	if (!pps_node) {
+		/* Add at root of PPS MO */
+		pps_node = pps;
+	}
+
+	uri_len = os_strlen(uri);
+	os_strlcpy(uri, pos + uri_len, os_strlen(pos));
+	upos = uri;
+	while (*upos == '/')
+		upos++;
+	wpa_printf(MSG_INFO, "Nodes to add: %s", upos);
+
+	for (;;) {
+		end = os_strchr(upos, '/');
+		if (!end)
+			break;
+		*end = '\0';
+		wpa_printf(MSG_INFO, "Adding interim node %s", upos);
+		pps_node = xml_node_create(ctx->xml, pps_node, NULL, upos);
+		if (pps_node == NULL) {
+			os_free(uri);
+			return DM_RESP_COMMAND_FAILED;
+		}
+		upos = end + 1;
+	}
+
+	wpa_printf(MSG_INFO, "Adding node %s", upos);
+
+	node = get_node(ctx->xml, add, "Item/Meta/Type");
+	if (node) {
+		char *type;
+		type = xml_node_get_text(ctx->xml, node);
+		if (type == NULL) {
+			wpa_printf(MSG_ERROR, "Could not find type text");
+			os_free(uri);
+			return DM_RESP_BAD_REQUEST;
+		}
+		use_tnds = node &&
+			os_strstr(type, "application/vnd.syncml.dmtnds+xml");
+	}
+
+	node = get_node(ctx->xml, add, "Item/Data");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No Add/Item/Data found");
+		os_free(uri);
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	data = xml_node_get_text(ctx->xml, node);
+	if (data == NULL) {
+		wpa_printf(MSG_INFO, "Could not get Add/Item/Data text");
+		os_free(uri);
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	wpa_printf(MSG_DEBUG, "Add/Item/Data: %s", data);
+
+	if (use_tnds) {
+		tnds = xml_node_from_buf(ctx->xml, data);
+		xml_node_get_text_free(ctx->xml, data);
+		if (tnds == NULL) {
+			wpa_printf(MSG_INFO,
+				   "Could not parse Add/Item/Data text");
+			os_free(uri);
+			return DM_RESP_BAD_REQUEST;
+		}
+
+		unode = tnds_to_mo(ctx->xml, tnds);
+		xml_node_free(ctx->xml, tnds);
+		if (unode == NULL) {
+			wpa_printf(MSG_INFO, "Could not parse TNDS text");
+			os_free(uri);
+			return DM_RESP_BAD_REQUEST;
+		}
+
+		debug_dump_node(ctx, "Parsed TNDS", unode);
+
+		xml_node_add_child(ctx->xml, pps_node, unode);
+	} else {
+		/* TODO: What to do here? */
+		os_free(uri);
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	os_free(uri);
+
+	if (update_pps_file(ctx, pps_fname, pps) < 0)
+		return DM_RESP_COMMAND_FAILED;
+
+	ctx->pps_updated = 1;
+
+	return DM_RESP_OK;
+}
+
+
+static int oma_dm_add(struct hs20_osu_client *ctx, xml_node_t *add,
+		      xml_node_t *pps, const char *pps_fname)
+{
+	xml_node_t *node;
+	char *locuri;
+	char fname[300];
+	int ret;
+
+	node = get_node(ctx->xml, add, "Item/Target/LocURI");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No Target LocURI node found");
+		return DM_RESP_BAD_REQUEST;
+	}
+	locuri = xml_node_get_text(ctx->xml, node);
+	if (locuri == NULL) {
+		wpa_printf(MSG_ERROR, "No LocURI node text found");
+		return DM_RESP_BAD_REQUEST;
+	}
+	wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
+	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+		wpa_printf(MSG_INFO, "Unsupported Add Target LocURI");
+		xml_node_get_text_free(ctx->xml, locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+
+	node = get_node(ctx->xml, add, "Item/Data");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No Data node found");
+		xml_node_get_text_free(ctx->xml, locuri);
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	if (pps_fname && os_file_exists(pps_fname)) {
+		ret = oma_dm_run_add(ctx, locuri, add, pps, pps_fname);
+		if (ret != DM_RESP_OK) {
+			xml_node_get_text_free(ctx->xml, locuri);
+			return ret;
+		}
+		ret = 0;
+		os_strlcpy(fname, pps_fname, sizeof(fname));
+	} else
+		ret = hs20_add_pps_mo(ctx, locuri, node, fname, sizeof(fname));
+	xml_node_get_text_free(ctx->xml, locuri);
+	if (ret < 0)
+		return ret == -2 ? DM_RESP_ALREADY_EXISTS :
+			DM_RESP_COMMAND_FAILED;
+
+	if (ctx->no_reconnect == 2) {
+		os_snprintf(ctx->pps_fname, sizeof(ctx->pps_fname), "%s",
+			    fname);
+		ctx->pps_cred_set = 1;
+		return DM_RESP_OK;
+	}
+
+	wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+	cmd_set_pps(ctx, fname);
+
+	if (ctx->no_reconnect)
+		return DM_RESP_OK;
+
+	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
+		wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+
+	return DM_RESP_OK;
+}
+
+
+static int oma_dm_replace(struct hs20_osu_client *ctx, xml_node_t *replace,
+			  xml_node_t *pps, const char *pps_fname)
+{
+	char *locuri, *pos;
+	size_t fqdn_len;
+	xml_node_t *node, *tnds, *unode, *pps_node, *parent;
+	char *data;
+	int use_tnds = 0;
+
+	locuri = oma_dm_get_target_locuri(ctx, replace);
+	if (locuri == NULL)
+		return DM_RESP_BAD_REQUEST;
+
+	wpa_printf(MSG_INFO, "Replace command target LocURI: %s", locuri);
+	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+		wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi");
+		os_free(locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos = locuri + 8;
+
+	if (ctx->fqdn == NULL) {
+		os_free(locuri);
+		return DM_RESP_COMMAND_FAILED;
+	}
+	fqdn_len = os_strlen(ctx->fqdn);
+	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+	    pos[fqdn_len] != '/') {
+		wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi/%s",
+			   ctx->fqdn);
+		os_free(locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos += fqdn_len + 1;
+
+	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+		wpa_printf(MSG_INFO,
+			   "Do not allow Replace outside ./Wi-Fi/%s/PerProviderSubscription",
+			   ctx->fqdn);
+		os_free(locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos += 24;
+
+	wpa_printf(MSG_INFO, "Replace command for PPS node %s", pos);
+
+	pps_node = get_node(ctx->xml, pps, pos);
+	if (pps_node == NULL) {
+		wpa_printf(MSG_INFO, "Specified PPS node not found");
+		os_free(locuri);
+		return DM_RESP_NOT_FOUND;
+	}
+
+	node = get_node(ctx->xml, replace, "Item/Meta/Type");
+	if (node) {
+		char *type;
+		type = xml_node_get_text(ctx->xml, node);
+		if (type == NULL) {
+			wpa_printf(MSG_INFO, "Could not find type text");
+			os_free(locuri);
+			return DM_RESP_BAD_REQUEST;
+		}
+		use_tnds = node &&
+			os_strstr(type, "application/vnd.syncml.dmtnds+xml");
+	}
+
+	node = get_node(ctx->xml, replace, "Item/Data");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No Replace/Item/Data found");
+		os_free(locuri);
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	data = xml_node_get_text(ctx->xml, node);
+	if (data == NULL) {
+		wpa_printf(MSG_INFO, "Could not get Replace/Item/Data text");
+		os_free(locuri);
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	wpa_printf(MSG_DEBUG, "Replace/Item/Data: %s", data);
+
+	if (use_tnds) {
+		tnds = xml_node_from_buf(ctx->xml, data);
+		xml_node_get_text_free(ctx->xml, data);
+		if (tnds == NULL) {
+			wpa_printf(MSG_INFO,
+				   "Could not parse Replace/Item/Data text");
+			os_free(locuri);
+			return DM_RESP_BAD_REQUEST;
+		}
+
+		unode = tnds_to_mo(ctx->xml, tnds);
+		xml_node_free(ctx->xml, tnds);
+		if (unode == NULL) {
+			wpa_printf(MSG_INFO, "Could not parse TNDS text");
+			os_free(locuri);
+			return DM_RESP_BAD_REQUEST;
+		}
+
+		debug_dump_node(ctx, "Parsed TNDS", unode);
+
+		parent = xml_node_get_parent(ctx->xml, pps_node);
+		xml_node_detach(ctx->xml, pps_node);
+		xml_node_add_child(ctx->xml, parent, unode);
+	} else {
+		xml_node_set_text(ctx->xml, pps_node, data);
+		xml_node_get_text_free(ctx->xml, data);
+	}
+
+	os_free(locuri);
+
+	if (update_pps_file(ctx, pps_fname, pps) < 0)
+		return DM_RESP_COMMAND_FAILED;
+
+	ctx->pps_updated = 1;
+
+	return DM_RESP_OK;
+}
+
+
+static int oma_dm_get(struct hs20_osu_client *ctx, xml_node_t *get,
+		      xml_node_t *pps, const char *pps_fname, char **value)
+{
+	char *locuri, *pos;
+	size_t fqdn_len;
+	xml_node_t *pps_node;
+	const char *name;
+
+	*value = NULL;
+
+	locuri = oma_dm_get_target_locuri(ctx, get);
+	if (locuri == NULL)
+		return DM_RESP_BAD_REQUEST;
+
+	wpa_printf(MSG_INFO, "Get command target LocURI: %s", locuri);
+	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+		wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi");
+		os_free(locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos = locuri + 8;
+
+	if (ctx->fqdn == NULL)
+		return DM_RESP_COMMAND_FAILED;
+	fqdn_len = os_strlen(ctx->fqdn);
+	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+	    pos[fqdn_len] != '/') {
+		wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi/%s",
+			   ctx->fqdn);
+		os_free(locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos += fqdn_len + 1;
+
+	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+		wpa_printf(MSG_INFO,
+			   "Do not allow Get outside ./Wi-Fi/%s/PerProviderSubscription",
+			   ctx->fqdn);
+		os_free(locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos += 24;
+
+	wpa_printf(MSG_INFO, "Get command for PPS node %s", pos);
+
+	pps_node = get_node(ctx->xml, pps, pos);
+	if (pps_node == NULL) {
+		wpa_printf(MSG_INFO, "Specified PPS node not found");
+		os_free(locuri);
+		return DM_RESP_NOT_FOUND;
+	}
+
+	name = xml_node_get_localname(ctx->xml, pps_node);
+	wpa_printf(MSG_INFO, "Get command returned node with name '%s'", name);
+	if (os_strcasecmp(name, "Password") == 0) {
+		wpa_printf(MSG_INFO, "Do not allow Get for Password node");
+		os_free(locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+
+	/*
+	 * TODO: No support for DMTNDS, so if interior node, reply with a
+	 * list of children node names in Results element. The child list type is
+	 * defined in [DMTND].
+	 */
+
+	*value = xml_node_get_text(ctx->xml, pps_node);
+	if (*value == NULL)
+		return DM_RESP_COMMAND_FAILED;
+
+	return DM_RESP_OK;
+}
+
+
+static int oma_dm_get_cmdid(struct hs20_osu_client *ctx, xml_node_t *node)
+{
+	xml_node_t *cnode;
+	char *str;
+	int ret;
+
+	cnode = get_node(ctx->xml, node, "CmdID");
+	if (cnode == NULL)
+		return 0;
+
+	str = xml_node_get_text(ctx->xml, cnode);
+	if (str == NULL)
+		return 0;
+	ret = atoi(str);
+	xml_node_get_text_free(ctx->xml, str);
+	return ret;
+}
+
+
+static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx,
+				     const char *url, xml_node_t *syncml,
+				     const char *ext_hdr,
+				     const char *username, const char *password,
+				     const char *client_cert,
+				     const char *client_key)
+{
+	xml_node_t *resp;
+	char *str, *res;
+	char *resp_uri = NULL;
+
+	str = xml_node_to_str(ctx->xml, syncml);
+	xml_node_free(ctx->xml, syncml);
+	if (str == NULL)
+		return NULL;
+
+	wpa_printf(MSG_INFO, "Send OMA DM Package");
+	write_summary(ctx, "Send OMA DM Package");
+	os_free(ctx->server_url);
+	ctx->server_url = os_strdup(url);
+	res = http_post(ctx->http, url, str, "application/vnd.syncml.dm+xml",
+			ext_hdr, ctx->ca_fname, username, password,
+			client_cert, client_key, NULL);
+	os_free(str);
+	os_free(resp_uri);
+	resp_uri = NULL;
+
+	if (res == NULL) {
+		const char *err = http_get_err(ctx->http);
+		if (err) {
+			wpa_printf(MSG_INFO, "HTTP error: %s", err);
+			write_result(ctx, "HTTP error: %s", err);
+		} else {
+			write_summary(ctx, "Failed to send OMA DM Package");
+		}
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "Server response: %s", res);
+
+	wpa_printf(MSG_INFO, "Process OMA DM Package");
+	write_summary(ctx, "Process received OMA DM Package");
+	resp = xml_node_from_buf(ctx->xml, res);
+	os_free(res);
+	if (resp == NULL) {
+		wpa_printf(MSG_INFO, "Failed to parse OMA DM response");
+		return NULL;
+	}
+
+	debug_dump_node(ctx, "OMA DM Package", resp);
+
+	return resp;
+}
+
+
+static xml_node_t * oma_dm_process(struct hs20_osu_client *ctx, const char *url,
+				   xml_node_t *resp, int msgid,
+				   char **ret_resp_uri,
+				   xml_node_t *pps, const char *pps_fname)
+{
+	xml_node_t *syncml, *syncbody, *hdr, *body, *child;
+	const char *name;
+	char *resp_uri = NULL;
+	int server_msgid = 0;
+	int cmdid = 0;
+	int server_cmdid;
+	int resp_needed = 0;
+	char *tmp;
+	int final = 0;
+	char *locuri;
+
+	*ret_resp_uri = NULL;
+
+	name = xml_node_get_localname(ctx->xml, resp);
+	if (name == NULL || os_strcasecmp(name, "SyncML") != 0) {
+		wpa_printf(MSG_INFO, "SyncML node not found");
+		return NULL;
+	}
+
+	hdr = get_node(ctx->xml, resp, "SyncHdr");
+	body = get_node(ctx->xml, resp, "SyncBody");
+	if (hdr == NULL || body == NULL) {
+		wpa_printf(MSG_INFO, "Could not find SyncHdr or SyncBody");
+		return NULL;
+	}
+
+	xml_node_for_each_child(ctx->xml, child, hdr) {
+		xml_node_for_each_check(ctx->xml, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		wpa_printf(MSG_INFO, "SyncHdr %s", name);
+		if (os_strcasecmp(name, "RespURI") == 0) {
+			tmp = xml_node_get_text(ctx->xml, child);
+			if (tmp)
+				resp_uri = os_strdup(tmp);
+			xml_node_get_text_free(ctx->xml, tmp);
+		} else if (os_strcasecmp(name, "MsgID") == 0) {
+			tmp = xml_node_get_text(ctx->xml, child);
+			if (tmp)
+				server_msgid = atoi(tmp);
+			xml_node_get_text_free(ctx->xml, tmp);
+		}
+	}
+
+	wpa_printf(MSG_INFO, "Server MsgID: %d", server_msgid);
+	if (resp_uri)
+		wpa_printf(MSG_INFO, "RespURI: %s", resp_uri);
+
+	syncml = oma_dm_build_hdr(ctx, resp_uri ? resp_uri : url, msgid);
+	if (syncml == NULL) {
+		os_free(resp_uri);
+		return NULL;
+	}
+
+	syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
+	cmdid++;
+	add_status(ctx, syncbody, server_msgid, 0, cmdid, "SyncHdr",
+		   DM_RESP_AUTH_ACCEPTED, NULL);
+
+	xml_node_for_each_child(ctx->xml, child, body) {
+		xml_node_for_each_check(ctx->xml, child);
+		server_cmdid = oma_dm_get_cmdid(ctx, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		wpa_printf(MSG_INFO, "SyncBody CmdID=%d - %s",
+			   server_cmdid, name);
+		if (os_strcasecmp(name, "Exec") == 0) {
+			int res = oma_dm_exec(ctx, child);
+			cmdid++;
+			locuri = oma_dm_get_target_locuri(ctx, child);
+			if (locuri == NULL)
+				res = DM_RESP_BAD_REQUEST;
+			add_status(ctx, syncbody, server_msgid, server_cmdid,
+				   cmdid, name, res, locuri);
+			os_free(locuri);
+			resp_needed = 1;
+		} else if (os_strcasecmp(name, "Add") == 0) {
+			int res = oma_dm_add(ctx, child, pps, pps_fname);
+			cmdid++;
+			locuri = oma_dm_get_target_locuri(ctx, child);
+			if (locuri == NULL)
+				res = DM_RESP_BAD_REQUEST;
+			add_status(ctx, syncbody, server_msgid, server_cmdid,
+				   cmdid, name, res, locuri);
+			os_free(locuri);
+			resp_needed = 1;
+		} else if (os_strcasecmp(name, "Replace") == 0) {
+			int res;
+			res = oma_dm_replace(ctx, child, pps, pps_fname);
+			cmdid++;
+			locuri = oma_dm_get_target_locuri(ctx, child);
+			if (locuri == NULL)
+				res = DM_RESP_BAD_REQUEST;
+			add_status(ctx, syncbody, server_msgid, server_cmdid,
+				   cmdid, name, res, locuri);
+			os_free(locuri);
+			resp_needed = 1;
+		} else if (os_strcasecmp(name, "Status") == 0) {
+			/* TODO: Verify success */
+		} else if (os_strcasecmp(name, "Get") == 0) {
+			int res;
+			char *value;
+			res = oma_dm_get(ctx, child, pps, pps_fname, &value);
+			cmdid++;
+			locuri = oma_dm_get_target_locuri(ctx, child);
+			if (locuri == NULL)
+				res = DM_RESP_BAD_REQUEST;
+			add_status(ctx, syncbody, server_msgid, server_cmdid,
+				   cmdid, name, res, locuri);
+			if (res == DM_RESP_OK && value) {
+				cmdid++;
+				add_results(ctx, syncbody, server_msgid,
+					    server_cmdid, cmdid, locuri, value);
+			}
+			os_free(locuri);
+			xml_node_get_text_free(ctx->xml, value);
+			resp_needed = 1;
+#if 0 /* TODO: MUST support */
+		} else if (os_strcasecmp(name, "Delete") == 0) {
+#endif
+#if 0 /* TODO: MUST support */
+		} else if (os_strcasecmp(name, "Sequence") == 0) {
+#endif
+		} else if (os_strcasecmp(name, "Final") == 0) {
+			final = 1;
+			break;
+		} else {
+			locuri = oma_dm_get_target_locuri(ctx, child);
+			add_status(ctx, syncbody, server_msgid, server_cmdid,
+				   cmdid, name, DM_RESP_COMMAND_NOT_IMPLEMENTED,
+				   locuri);
+			os_free(locuri);
+			resp_needed = 1;
+		}
+	}
+
+	if (!final) {
+		wpa_printf(MSG_INFO, "Final node not found");
+		xml_node_free(ctx->xml, syncml);
+		os_free(resp_uri);
+		return NULL;
+	}
+
+	if (!resp_needed) {
+		wpa_printf(MSG_INFO, "Exchange completed - no response needed");
+		xml_node_free(ctx->xml, syncml);
+		os_free(resp_uri);
+		return NULL;
+	}
+
+	xml_node_create(ctx->xml, syncbody, NULL, "Final");
+
+	debug_dump_node(ctx, "OMA-DM Package 3", syncml);
+
+	*ret_resp_uri = resp_uri;
+	return syncml;
+}
+
+
+int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url)
+{
+	xml_node_t *syncml, *resp;
+	char *resp_uri = NULL;
+	int msgid = 0;
+
+	if (url == NULL) {
+		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "OMA-DM credential provisioning requested");
+	write_summary(ctx, "OMA-DM credential provisioning");
+
+	msgid++;
+	syncml = build_oma_dm_1_sub_reg(ctx, url, msgid);
+	if (syncml == NULL)
+		return -1;
+
+	while (syncml) {
+		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
+					syncml, NULL, NULL, NULL, NULL, NULL);
+		if (resp == NULL)
+			return -1;
+
+		msgid++;
+		syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
+					NULL, NULL);
+		xml_node_free(ctx->xml, resp);
+	}
+
+	os_free(resp_uri);
+
+	return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url)
+{
+	xml_node_t *syncml, *resp;
+	char *resp_uri = NULL;
+	int msgid = 0;
+
+	if (url == NULL) {
+		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "OMA-DM SIM provisioning requested");
+	ctx->no_reconnect = 2;
+
+	wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
+	write_summary(ctx, "Wait for IP address before starting SIM provisioning");
+
+	if (wait_ip_addr(ctx->ifname, 15) < 0) {
+		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+	}
+	write_summary(ctx, "OMA-DM SIM provisioning");
+
+	msgid++;
+	syncml = build_oma_dm_1_sub_prov(ctx, url, msgid);
+	if (syncml == NULL)
+		return -1;
+
+	while (syncml) {
+		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
+					syncml, NULL, NULL, NULL, NULL, NULL);
+		if (resp == NULL)
+			return -1;
+
+		msgid++;
+		syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
+					NULL, NULL);
+		xml_node_free(ctx->xml, resp);
+	}
+
+	os_free(resp_uri);
+
+	if (ctx->pps_cred_set) {
+		wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+		cmd_set_pps(ctx, ctx->pps_fname);
+
+		wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+		write_summary(ctx, "Requesting reconnection with updated configuration");
+		if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+			wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+			write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+			return -1;
+		}
+	}
+
+	return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
+		    const char *pps_fname,
+		    const char *client_cert, const char *client_key,
+		    const char *cred_username, const char *cred_password,
+		    xml_node_t *pps)
+{
+	xml_node_t *syncml, *resp;
+	char *resp_uri = NULL;
+	int msgid = 0;
+
+	wpa_printf(MSG_INFO, "OMA-DM policy update");
+	write_summary(ctx, "OMA-DM policy update");
+
+	msgid++;
+	syncml = build_oma_dm_1_pol_upd(ctx, address, msgid);
+	if (syncml == NULL)
+		return;
+
+	while (syncml) {
+		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
+					syncml, NULL, cred_username,
+					cred_password, client_cert, client_key);
+		if (resp == NULL)
+			return;
+
+		msgid++;
+		syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
+					pps, pps_fname);
+		xml_node_free(ctx->xml, resp);
+	}
+
+	os_free(resp_uri);
+
+	if (ctx->pps_updated) {
+		wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO");
+		write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request connection");
+		cmd_set_pps(ctx, pps_fname);
+		if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+			wpa_printf(MSG_INFO,
+				   "Failed to request wpa_supplicant to reconnect");
+			write_summary(ctx,
+				      "Failed to request wpa_supplicant to reconnect");
+		}
+	}
+}
+
+
+void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
+		    const char *pps_fname,
+		    const char *client_cert, const char *client_key,
+		    const char *cred_username, const char *cred_password,
+		    xml_node_t *pps)
+{
+	xml_node_t *syncml, *resp;
+	char *resp_uri = NULL;
+	int msgid = 0;
+
+	wpa_printf(MSG_INFO, "OMA-DM subscription remediation");
+	write_summary(ctx, "OMA-DM subscription remediation");
+
+	msgid++;
+	syncml = build_oma_dm_1_sub_rem(ctx, address, msgid);
+	if (syncml == NULL)
+		return;
+
+	while (syncml) {
+		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
+					syncml, NULL, cred_username,
+					cred_password, client_cert, client_key);
+		if (resp == NULL)
+			return;
+
+		msgid++;
+		syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
+					pps, pps_fname);
+		xml_node_free(ctx->xml, resp);
+	}
+
+	os_free(resp_uri);
+
+	wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
+	write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
+	cmd_set_pps(ctx, pps_fname);
+	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+		wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+		write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+	}
+}
+
+
+void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
+		    const char *add_fname)
+{
+	xml_node_t *pps, *add;
+	int res;
+
+	ctx->fqdn = os_strdup("wi-fi.org");
+
+	pps = node_from_file(ctx->xml, pps_fname);
+	if (pps == NULL) {
+		wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
+			   pps_fname);
+		return;
+	}
+
+	add = node_from_file(ctx->xml, add_fname);
+	if (add == NULL) {
+		wpa_printf(MSG_INFO, "Add file %s could not be parsed",
+			   add_fname);
+		xml_node_free(ctx->xml, pps);
+		return;
+	}
+
+	res = oma_dm_add(ctx, add, pps, pps_fname);
+	wpa_printf(MSG_INFO, "oma_dm_add --> %d", res);
+
+	xml_node_free(ctx->xml, pps);
+	xml_node_free(ctx->xml, add);
+}
+
+
+void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
+			const char *replace_fname)
+{
+	xml_node_t *pps, *replace;
+	int res;
+
+	ctx->fqdn = os_strdup("wi-fi.org");
+
+	pps = node_from_file(ctx->xml, pps_fname);
+	if (pps == NULL) {
+		wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
+			   pps_fname);
+		return;
+	}
+
+	replace = node_from_file(ctx->xml, replace_fname);
+	if (replace == NULL) {
+		wpa_printf(MSG_INFO, "Replace file %s could not be parsed",
+			   replace_fname);
+		xml_node_free(ctx->xml, pps);
+		return;
+	}
+
+	res = oma_dm_replace(ctx, replace, pps, pps_fname);
+	wpa_printf(MSG_INFO, "oma_dm_replace --> %d", res);
+
+	xml_node_free(ctx->xml, pps);
+	xml_node_free(ctx->xml, replace);
+}
diff --git a/hostap/hs20/client/osu_client.c b/hostap/hs20/client/osu_client.c
new file mode 100644
index 0000000..0315f7b
--- /dev/null
+++ b/hostap/hs20/client/osu_client.c
@@ -0,0 +1,3264 @@
+/*
+ * 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>
+#ifdef ANDROID
+#include "private/android_filesystem_config.h"
+#endif /* ANDROID */
+
+#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"
+
+const char *spp_xsd_fname = "spp.xsd";
+
+
+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,
+			      "Policy/PolicyUpdate/TrustRoot");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS");
+		xml_node_free(ctx->xml, pps);
+		return -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);
+			free(fqdn);
+			return -1;
+		}
+		*pos = '\0'; /* remove trailing slash and PPS node name */
+	}
+	wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn);
+
+	if (!server_dnsname_suffix_match(ctx, fqdn)) {
+		wpa_printf(MSG_INFO,
+			   "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values, count: %d",
+			   fqdn, (int) ctx->server_dnsname_count);
+		write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
+			     fqdn);
+		free(fqdn);
+		return -1;
+	}
+
+	if (!valid_fqdn(fqdn)) {
+		wpa_printf(MSG_INFO, "Invalid FQDN '%s'", fqdn);
+		write_result(ctx, "Invalid FQDN '%s'", fqdn);
+		free(fqdn);
+		return -1;
+	}
+
+	mkdir("SP", S_IRWXU);
+	snprintf(fname, fname_len, "SP/%s", fqdn);
+	if (mkdir(fname, S_IRWXU) < 0) {
+		if (errno != EEXIST) {
+			int err = errno;
+			wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
+				   fname, strerror(err));
+			free(fqdn);
+			return -1;
+		}
+	}
+
+#ifdef ANDROID
+	/* Allow processes running with Group ID as AID_WIFI,
+	 * to read files from SP/<fqdn> directory */
+	if (chown(fname, -1, AID_WIFI)) {
+		wpa_printf(MSG_INFO, "CTRL: Could not chown directory: %s",
+			   strerror(errno));
+		/* Try to continue anyway */
+	}
+	if (chmod(fname, S_IRWXU | S_IRGRP | S_IXGRP) < 0) {
+		wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s",
+			   strerror(errno));
+		/* Try to continue anyway */
+	}
+#endif /* ANDROID */
+
+	snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn);
+
+	if (os_file_exists(fname)) {
+		wpa_printf(MSG_INFO, "PPS file '%s' exists - reject addMO",
+			   fname);
+		write_result(ctx, "PPS file '%s' exists - reject addMO",
+			     fname);
+		free(fqdn);
+		return -2;
+	}
+	wpa_printf(MSG_INFO, "Using PPS file: %s", fname);
+
+	str = xml_node_get_text(ctx->xml, add_mo);
+	if (str == NULL) {
+		wpa_printf(MSG_INFO, "Could not extract MO text");
+		free(fqdn);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "[hs20] addMO text: '%s'", str);
+
+	tnds = xml_node_from_buf(ctx->xml, str);
+	xml_node_get_text_free(ctx->xml, str);
+	if (tnds == NULL) {
+		wpa_printf(MSG_INFO, "[hs20] Could not parse addMO text");
+		free(fqdn);
+		return -1;
+	}
+
+	mo = tnds_to_mo(ctx->xml, tnds);
+	if (mo == NULL) {
+		wpa_printf(MSG_INFO, "[hs20] Could not parse addMO TNDS text");
+		free(fqdn);
+		return -1;
+	}
+
+	debug_dump_node(ctx, "Parsed TNDS", mo);
+
+	name = xml_node_get_localname(ctx->xml, mo);
+	if (os_strcasecmp(name, "PerProviderSubscription") != 0) {
+		wpa_printf(MSG_INFO, "[hs20] Unexpected PPS MO root node name '%s'",
+			   name);
+		free(fqdn);
+		return -1;
+	}
+
+	cert = get_child_node(ctx->xml, mo,
+			      "Credential/DigitalCertificate/"
+			      "CertSHA256Fingerprint");
+	if (cert && process_est_cert(ctx, cert, fqdn) < 0) {
+		xml_node_free(ctx->xml, mo);
+		free(fqdn);
+		return -1;
+	}
+	free(fqdn);
+
+	if (node_to_file(ctx->xml, fname, mo) < 0) {
+		wpa_printf(MSG_INFO, "Could not write MO to file");
+		xml_node_free(ctx->xml, mo);
+		return -1;
+	}
+	xml_node_free(ctx->xml, mo);
+
+	wpa_printf(MSG_INFO, "A new PPS MO added as '%s'", fname);
+	write_summary(ctx, "A new PPS MO added as '%s'", fname);
+
+	ret = download_trust_roots(ctx, fname);
+	if (ret < 0) {
+		wpa_printf(MSG_INFO, "Remove invalid PPS MO file");
+		write_summary(ctx, "Remove invalid PPS MO file");
+		unlink(fname);
+	}
+
+	return ret;
+}
+
+
+int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
+		    xml_node_t *pps)
+{
+	char *str;
+	FILE *f;
+	char backup[300];
+
+	if (ctx->client_cert_present) {
+		xml_node_t *cert;
+		cert = get_child_node(ctx->xml, pps,
+				      "Credential/DigitalCertificate/"
+				      "CertSHA256Fingerprint");
+		if (cert && os_file_exists("Cert/est_cert.der") &&
+		    process_est_cert(ctx, cert, ctx->fqdn) < 0) {
+			wpa_printf(MSG_INFO, "EST certificate update processing failed on PPS MO update");
+			return -1;
+		}
+	}
+
+	wpa_printf(MSG_INFO, "Updating PPS MO %s", pps_fname);
+
+	str = xml_node_to_str(ctx->xml, pps);
+	if (str == NULL) {
+		wpa_printf(MSG_ERROR, "No node found");
+		return -1;
+	}
+	wpa_printf(MSG_MSGDUMP, "[hs20] Updated PPS: '%s'", str);
+
+	snprintf(backup, sizeof(backup), "%s.bak", pps_fname);
+	rename(pps_fname, backup);
+	f = fopen(pps_fname, "w");
+	if (f == NULL) {
+		wpa_printf(MSG_INFO, "Could not write PPS");
+		rename(backup, pps_fname);
+		free(str);
+		return -1;
+	}
+	fprintf(f, "%s\n", str);
+	fclose(f);
+
+	free(str);
+
+	return 0;
+}
+
+
+void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
+		 const char *alt_loc, char **user, char **pw)
+{
+	xml_node_t *node;
+
+	node = get_child_node(ctx->xml, pps,
+			      "Credential/UsernamePassword/Username");
+	if (node)
+		*user = xml_node_get_text(ctx->xml, node);
+
+	node = get_child_node(ctx->xml, pps,
+			      "Credential/UsernamePassword/Password");
+	if (node)
+		*pw = xml_node_get_base64_text(ctx->xml, node, NULL);
+
+	node = get_child_node(ctx->xml, pps, alt_loc);
+	if (node) {
+		xml_node_t *a;
+		a = get_node(ctx->xml, node, "Username");
+		if (a) {
+			xml_node_get_text_free(ctx->xml, *user);
+			*user = xml_node_get_text(ctx->xml, a);
+			wpa_printf(MSG_INFO, "Use OSU username '%s'", *user);
+		}
+
+		a = get_node(ctx->xml, node, "Password");
+		if (a) {
+			free(*pw);
+			*pw = xml_node_get_base64_text(ctx->xml, a, NULL);
+			wpa_printf(MSG_INFO, "Use OSU password");
+		}
+	}
+}
+
+
+/* Remove old credentials based on HomeSP/FQDN */
+static void remove_sp_creds(struct hs20_osu_client *ctx, const char *fqdn)
+{
+	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) {
+		wpa_printf(MSG_DEBUG, "Calling cmd_prov from osu_connect");
+		res = cmd_prov(ctx, url);
+	} else if (methods & 0x01) {
+		wpa_printf(MSG_DEBUG,
+			   "Calling cmd_oma_dm_prov from osu_connect");
+		res = cmd_oma_dm_prov(ctx, url);
+	}
+
+	wpa_printf(MSG_INFO, "Remove OSU network connection");
+	write_summary(ctx, "Remove OSU network connection");
+	snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
+	wpa_command(ifname, buf);
+
+	if (res < 0)
+		return -1;
+
+	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+	write_summary(ctx, "Requesting reconnection with updated configuration");
+	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+		wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+		write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir,
+			  int connect, int no_prod_assoc,
+			  const char *friendly_name)
+{
+	char fname[255];
+	FILE *f;
+	struct osu_data *osu = NULL, *last = NULL;
+	size_t osu_count, i, j;
+	int ret;
+
+	write_summary(ctx, "OSU provider selection");
+
+	if (dir == NULL) {
+		wpa_printf(MSG_INFO, "Missing dir parameter to osu_select");
+		return -1;
+	}
+
+	snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir);
+	osu = parse_osu_providers(fname, &osu_count);
+	if (osu == NULL) {
+		wpa_printf(MSG_INFO, "Could not find any OSU providers from %s",
+			   fname);
+		write_result(ctx, "No OSU providers available");
+		return -1;
+	}
+
+	if (friendly_name) {
+		for (i = 0; i < osu_count; i++) {
+			last = &osu[i];
+			for (j = 0; j < last->friendly_name_count; j++) {
+				if (os_strcmp(last->friendly_name[j].text,
+					      friendly_name) == 0)
+					break;
+			}
+			if (j < last->friendly_name_count)
+				break;
+		}
+		if (i == osu_count) {
+			wpa_printf(MSG_INFO, "Requested operator friendly name '%s' not found in the list of available providers",
+				   friendly_name);
+			write_summary(ctx, "Requested operator friendly name '%s' not found in the list of available providers",
+				      friendly_name);
+			free(osu);
+			return -1;
+		}
+
+		wpa_printf(MSG_INFO, "OSU Provider selected based on requested operator friendly name '%s'",
+			   friendly_name);
+		write_summary(ctx, "OSU Provider selected based on requested operator friendly name '%s'",
+			      friendly_name);
+		ret = i + 1;
+		goto selected;
+	}
+
+	snprintf(fname, sizeof(fname), "%s/osu-providers.html", dir);
+	f = fopen(fname, "w");
+	if (f == NULL) {
+		wpa_printf(MSG_INFO, "Could not open %s", fname);
+		free(osu);
+		return -1;
+	}
+
+	fprintf(f, "<html><head>"
+		"<meta http-equiv=\"Content-type\" content=\"text/html; "
+		"charset=utf-8\"<title>Select service operator</title>"
+		"</head><body><h1>Select service operator</h1>\n");
+
+	if (osu_count == 0)
+		fprintf(f, "No online signup available\n");
+
+	for (i = 0; i < osu_count; i++) {
+		last = &osu[i];
+#ifdef ANDROID
+		fprintf(f, "<p>\n"
+			"<a href=\"http://localhost:12345/osu/%d\">"
+			"<table><tr><td>", (int) i + 1);
+#else /* ANDROID */
+		fprintf(f, "<p>\n"
+			"<a href=\"osu://%d\">"
+			"<table><tr><td>", (int) i + 1);
+#endif /* ANDROID */
+		for (j = 0; j < last->icon_count; j++) {
+			fprintf(f, "<img src=\"osu-icon-%d.%s\">\n",
+				last->icon[j].id,
+				strcasecmp(last->icon[j].mime_type,
+					   "image/png") == 0 ? "png" : "icon");
+		}
+		fprintf(f, "<td>");
+		for (j = 0; j < last->friendly_name_count; j++) {
+			fprintf(f, "<small>[%s]</small> %s<br>\n",
+				last->friendly_name[j].lang,
+				last->friendly_name[j].text);
+		}
+		fprintf(f, "<tr><td colspan=2>");
+		for (j = 0; j < last->serv_desc_count; j++) {
+			fprintf(f, "<small>[%s]</small> %s<br>\n",
+				last->serv_desc[j].lang,
+				last->serv_desc[j].text);
+		}
+		fprintf(f, "</table></a><br><small>BSSID: %s<br>\n"
+			"SSID: %s<br>\n",
+			last->bssid, last->osu_ssid);
+		if (last->osu_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) {
+				wpa_printf(MSG_DEBUG,
+					   "Calling cmd_prov from cmd_osu_select");
+				ret = cmd_prov(ctx, last->url);
+			} else if (last->methods & 0x01) {
+				wpa_printf(MSG_DEBUG,
+					   "Calling cmd_oma_dm_prov from cmd_osu_select");
+				ret = cmd_oma_dm_prov(ctx, last->url);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "No supported OSU provisioning method");
+				ret = -1;
+			}
+		} else if (connect)
+			ret = osu_connect(ctx, last->bssid, last->osu_ssid,
+					  last->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 int cmd_sub_rem(struct hs20_osu_client *ctx, const char *address,
+		       const char *pps_fname, const char *ca_fname)
+{
+	xml_node_t *pps, *node;
+	char pps_fname_buf[300];
+	char ca_fname_buf[200];
+	char *cred_username = NULL;
+	char *cred_password = NULL;
+	char *sub_rem_uri = NULL;
+	char client_cert_buf[200];
+	char *client_cert = NULL;
+	char client_key_buf[200];
+	char *client_key = NULL;
+	int spp;
+
+	wpa_printf(MSG_INFO, "Subscription remediation requested with Server URL: %s",
+		   address);
+
+	if (!pps_fname) {
+		char buf[256];
+		wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
+		if (os_strncmp(address, "fqdn=", 5) == 0) {
+			wpa_printf(MSG_INFO, "Use requested FQDN from command line");
+			os_snprintf(buf, sizeof(buf), "%s", address + 5);
+			address = NULL;
+		} else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
+					  sizeof(buf)) < 0) {
+			wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
+			return -1;
+		}
+		os_free(ctx->fqdn);
+		ctx->fqdn = os_strdup(buf);
+		if (ctx->fqdn == NULL)
+			return -1;
+		wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
+			   buf);
+		os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
+			    "SP/%s/pps.xml", ctx->fqdn);
+		pps_fname = pps_fname_buf;
+
+		os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
+			    ctx->fqdn);
+		ca_fname = ca_fname_buf;
+	}
+
+	if (!os_file_exists(pps_fname)) {
+		wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
+			   pps_fname);
+		return -1;
+	}
+	wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
+
+	if (ca_fname && !os_file_exists(ca_fname)) {
+		wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
+			   ca_fname);
+		return -1;
+	}
+	wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
+	ctx->ca_fname = ca_fname;
+
+	pps = node_from_file(ctx->xml, pps_fname);
+	if (pps == NULL) {
+		wpa_printf(MSG_INFO, "Could not read PPS MO");
+		return -1;
+	}
+
+	if (!ctx->fqdn) {
+		char *tmp;
+		node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
+		if (node == NULL) {
+			wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
+			return -1;
+		}
+		tmp = xml_node_get_text(ctx->xml, node);
+		if (tmp == NULL) {
+			wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
+			return -1;
+		}
+		ctx->fqdn = os_strdup(tmp);
+		xml_node_get_text_free(ctx->xml, tmp);
+		if (!ctx->fqdn) {
+			wpa_printf(MSG_INFO, "No FQDN known");
+			return -1;
+		}
+	}
+
+	node = get_child_node(ctx->xml, pps,
+			      "SubscriptionUpdate/UpdateMethod");
+	if (node) {
+		char *tmp;
+		tmp = xml_node_get_text(ctx->xml, node);
+		if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
+			spp = 0;
+		else
+			spp = 1;
+	} else {
+		wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
+		spp = 1;
+	}
+
+	get_user_pw(ctx, pps, "SubscriptionUpdate/UsernamePassword",
+		    &cred_username, &cred_password);
+	if (cred_username)
+		wpa_printf(MSG_INFO, "Using username: %s", cred_username);
+	if (cred_password)
+		wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
+
+	if (cred_username == NULL && cred_password == NULL &&
+	    get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
+		wpa_printf(MSG_INFO, "Using client certificate");
+		os_snprintf(client_cert_buf, sizeof(client_cert_buf),
+			    "SP/%s/client-cert.pem", ctx->fqdn);
+		client_cert = client_cert_buf;
+		os_snprintf(client_key_buf, sizeof(client_key_buf),
+			    "SP/%s/client-key.pem", ctx->fqdn);
+		client_key = client_key_buf;
+		ctx->client_cert_present = 1;
+	}
+
+	node = get_child_node(ctx->xml, pps, "SubscriptionUpdate/URI");
+	if (node) {
+		sub_rem_uri = xml_node_get_text(ctx->xml, node);
+		if (sub_rem_uri &&
+		    (!address || os_strcmp(address, sub_rem_uri) != 0)) {
+			wpa_printf(MSG_INFO, "Override sub rem URI based on PPS: %s",
+				   sub_rem_uri);
+			address = sub_rem_uri;
+		}
+	}
+	if (!address) {
+		wpa_printf(MSG_INFO, "Server URL not known");
+		return -1;
+	}
+
+	write_summary(ctx, "Wait for IP address for subscriptiom remediation");
+	wpa_printf(MSG_INFO, "Wait for IP address before starting subscription remediation");
+
+	if (wait_ip_addr(ctx->ifname, 15) < 0) {
+		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+	}
+
+	if (spp)
+		spp_sub_rem(ctx, address, pps_fname,
+			    client_cert, client_key,
+			    cred_username, cred_password, pps);
+	else
+		oma_dm_sub_rem(ctx, address, pps_fname,
+			       client_cert, client_key,
+			       cred_username, cred_password, pps);
+
+	xml_node_get_text_free(ctx->xml, sub_rem_uri);
+	xml_node_get_text_free(ctx->xml, cred_username);
+	str_clear_free(cred_password);
+	xml_node_free(ctx->xml, pps);
+	return 0;
+}
+
+
+static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address,
+		       const char *pps_fname, const char *ca_fname)
+{
+	xml_node_t *pps;
+	xml_node_t *node;
+	char pps_fname_buf[300];
+	char ca_fname_buf[200];
+	char *uri = NULL;
+	char *cred_username = NULL;
+	char *cred_password = NULL;
+	char client_cert_buf[200];
+	char *client_cert = NULL;
+	char client_key_buf[200];
+	char *client_key = NULL;
+	int spp;
+
+	wpa_printf(MSG_INFO, "Policy update requested");
+
+	if (!pps_fname) {
+		char buf[256];
+		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))
+		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, url=%s)",
+		   !ctx->no_osu_cert_validation, ctx->server_url);
+
+	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,
+			   "[%i] Looking for icon file name '%s' match",
+			   j, name);
+		for (i = 0; i < cert->num_logo; i++) {
+			struct http_logo *logo = &cert->logo[i];
+			size_t uri_len = os_strlen(logo->uri);
+			char *pos;
+
+			wpa_printf(MSG_INFO,
+				   "[%i] Comparing to '%s' uri_len=%d name_len=%d",
+				   i, logo->uri, (int) uri_len, (int) name_len);
+			if (uri_len < 1 + name_len) {
+				wpa_printf(MSG_INFO, "URI Length is too short");
+				continue;
+			}
+			pos = &logo->uri[uri_len - name_len - 1];
+			if (*pos != '/')
+				continue;
+			pos++;
+			if (os_strcmp(pos, name) == 0) {
+				found = 1;
+				break;
+			}
+		}
+
+		if (!found) {
+			wpa_printf(MSG_INFO, "No icon filename match found for '%s'",
+				   name);
+			write_result(ctx,
+				     "No icon filename match found for '%s'",
+				     name);
+			return -1;
+		}
+	}
+
+	for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
+		int found = 0;
+
+		for (i = 0; i < cert->num_logo; i++) {
+			struct http_logo *logo = &cert->logo[i];
+
+			if (logo->hash_len != 32) {
+				wpa_printf(MSG_INFO,
+					   "[%i][%i] Icon hash length invalid (should be 32): %d",
+					   j, i, (int) logo->hash_len);
+				continue;
+			}
+			if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) {
+				found = 1;
+				break;
+			}
+
+			wpa_printf(MSG_DEBUG,
+				   "[%u][%u] Icon hash did not match", j, i);
+			wpa_hexdump_ascii(MSG_DEBUG, "logo->hash",
+					  logo->hash, 32);
+			wpa_hexdump_ascii(MSG_DEBUG, "ctx->icon_hash[j]",
+					  ctx->icon_hash[j], 32);
+		}
+
+		if (!found) {
+			wpa_printf(MSG_INFO,
+				   "No icon hash match (by hash) found");
+			write_result(ctx,
+				     "No icon hash match (by hash) found");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int init_ctx(struct hs20_osu_client *ctx)
+{
+	xml_node_t *devinfo, *devid;
+
+	os_memset(ctx, 0, sizeof(*ctx));
+	ctx->ifname = "wlan0";
+	ctx->xml = xml_node_init_ctx(ctx, NULL);
+	if (ctx->xml == NULL)
+		return -1;
+
+	devinfo = node_from_file(ctx->xml, "devinfo.xml");
+	if (!devinfo) {
+		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"
+	       "    [-x<spp.xsd file name>] \\\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:hKNO:qr:s:S:tw:x:");
+		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 'x':
+			spp_xsd_fname = 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
+			ret = cmd_sub_rem(&ctx, argv[optind + 1],
+					  argc > optind + 2 ?
+					  argv[optind + 2] : NULL,
+					  argc > optind + 3 ?
+					  argv[optind + 3] : NULL);
+	} else if (strcmp(argv[optind], "pol_upd") == 0) {
+		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];
+		wpa_printf(MSG_DEBUG, "Calling cmd_prov from main");
+		cmd_prov(&ctx, argv[optind + 1]);
+	} else if (strcmp(argv[optind], "sim_prov") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		ctx.ca_fname = argv[optind + 2];
+		cmd_sim_prov(&ctx, argv[optind + 1]);
+	} else if (strcmp(argv[optind], "dl_osu_ca") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		cmd_dl_osu_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+	} else if (strcmp(argv[optind], "dl_polupd_ca") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		cmd_dl_polupd_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+	} else if (strcmp(argv[optind], "dl_aaa_ca") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+	} else if (strcmp(argv[optind], "osu_select") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		ctx.ca_fname = argc > optind + 2 ? argv[optind + 2] : NULL;
+		cmd_osu_select(&ctx, argv[optind + 1], 2, 1, NULL);
+	} else if (strcmp(argv[optind], "signup") == 0) {
+		ctx.ca_fname = argc > optind + 1 ? argv[optind + 1] : NULL;
+		ret = cmd_signup(&ctx, no_prod_assoc, friendly_name);
+	} else if (strcmp(argv[optind], "set_pps") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			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/hostap/hs20/client/osu_client.h b/hostap/hs20/client/osu_client.h
new file mode 100644
index 0000000..9a7059e
--- /dev/null
+++ b/hostap/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/hostap/hs20/client/spp_client.c b/hostap/hs20/client/spp_client.c
new file mode 100644
index 0000000..c619541
--- /dev/null
+++ b/hostap/hs20/client/spp_client.c
@@ -0,0 +1,1004 @@
+/*
+ * Hotspot 2.0 SPP client
+ * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/stat.h>
+
+#include "common.h"
+#include "browser.h"
+#include "wpa_ctrl.h"
+#include "wpa_helpers.h"
+#include "xml-utils.h"
+#include "http-utils.h"
+#include "utils/base64.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "osu_client.h"
+
+
+extern const char *spp_xsd_fname;
+
+static int hs20_spp_update_response(struct hs20_osu_client *ctx,
+				    const char *session_id,
+				    const char *spp_status,
+				    const char *error_code);
+static void hs20_policy_update_complete(
+	struct hs20_osu_client *ctx, const char *pps_fname);
+
+
+static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+				 char *attr_name)
+{
+	return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name);
+}
+
+
+static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node,
+			     const char *expected_name)
+{
+	struct xml_node_ctx *xctx = ctx->xml;
+	const char *name;
+	char *err;
+	int ret;
+
+	if (!xml_node_is_element(xctx, node))
+		return -1;
+
+	name = xml_node_get_localname(xctx, node);
+	if (name == NULL)
+		return -1;
+
+	if (strcmp(expected_name, name) != 0) {
+		wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')",
+			   name, expected_name);
+		write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')",
+			      name, expected_name);
+		return -1;
+	}
+
+	ret = xml_validate(xctx, node, spp_xsd_fname, &err);
+	if (ret < 0) {
+		wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err);
+		write_summary(ctx, "SPP XML schema validation failed");
+		os_free(err);
+	}
+	return ret;
+}
+
+
+static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns,
+			     xml_node_t *parent, const char *urn,
+			     const char *fname)
+{
+	xml_node_t *node;
+	xml_node_t *fnode, *tnds;
+	char *str;
+
+	errno = 0;
+	fnode = node_from_file(ctx, fname);
+	if (!fnode) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to create XML node from file: %s, possible error: %s",
+			   fname, strerror(errno));
+		return;
+	}
+	tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2");
+	xml_node_free(ctx, fnode);
+	if (!tnds)
+		return;
+
+	str = xml_node_to_str(ctx, tnds);
+	xml_node_free(ctx, tnds);
+	if (str == NULL)
+		return;
+
+	node = xml_node_create_text(ctx, parent, ns, "moContainer", str);
+	if (node)
+		xml_node_add_attr(ctx, node, ns, "moURN", urn);
+	os_free(str);
+}
+
+
+static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx,
+					    xml_namespace_t **ret_ns,
+					    const char *session_id,
+					    const char *reason)
+{
+	xml_namespace_t *ns;
+	xml_node_t *spp_node;
+
+	write_summary(ctx, "Building sppPostDevData requestReason='%s'",
+		      reason);
+	spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+					"sppPostDevData");
+	if (spp_node == NULL)
+		return NULL;
+	if (ret_ns)
+		*ret_ns = ns;
+
+	xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+	xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason);
+	if (session_id)
+		xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID",
+				  session_id);
+	xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI",
+			  "http://localhost:12345/");
+
+	xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions",
+			     "1.0");
+	xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList",
+			     URN_HS20_PPS " " URN_OMA_DM_DEVINFO " "
+			     URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT);
+
+	add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO,
+			 "devinfo.xml");
+	add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL,
+			 "devdetail.xml");
+
+	return spp_node;
+}
+
+
+static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps,
+			       xml_node_t *update)
+{
+	xml_node_t *node, *parent, *tnds, *unode;
+	char *str;
+	const char *name;
+	char *uri, *pos;
+	char *cdata, *cdata_end;
+	size_t fqdn_len;
+
+	wpa_printf(MSG_INFO, "Processing updateNode");
+	debug_dump_node(ctx, "updateNode", update);
+
+	uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI");
+	if (uri == NULL) {
+		wpa_printf(MSG_INFO, "No managementTreeURI present");
+		return -1;
+	}
+	wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri);
+
+	name = os_strrchr(uri, '/');
+	if (name == NULL) {
+		wpa_printf(MSG_INFO, "Unexpected URI");
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+	name++;
+	wpa_printf(MSG_INFO, "Update interior node: '%s'", name);
+
+	str = xml_node_get_text(ctx->xml, update);
+	if (str == NULL) {
+		wpa_printf(MSG_INFO, "Could not extract MO text");
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str);
+	cdata = strstr(str, "<![CDATA[");
+	cdata_end = strstr(str, "]]>");
+	if (cdata && cdata_end && cdata_end > cdata &&
+	    cdata < strstr(str, "MgmtTree") &&
+	    cdata_end > strstr(str, "/MgmtTree")) {
+		char *tmp;
+		wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container");
+		tmp = strdup(cdata + 9);
+		if (tmp) {
+			cdata_end = strstr(tmp, "]]>");
+			if (cdata_end)
+				*cdata_end = '\0';
+			wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'",
+				   tmp);
+			tnds = xml_node_from_buf(ctx->xml, tmp);
+			free(tmp);
+		} else
+			tnds = NULL;
+	} else
+		tnds = xml_node_from_buf(ctx->xml, str);
+	xml_node_get_text_free(ctx->xml, str);
+	if (tnds == NULL) {
+		wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text");
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+
+	unode = tnds_to_mo(ctx->xml, tnds);
+	xml_node_free(ctx->xml, tnds);
+	if (unode == NULL) {
+		wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text");
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+
+	debug_dump_node(ctx, "Parsed TNDS", unode);
+
+	if (get_node_uri(ctx->xml, unode, name) == NULL) {
+		wpa_printf(MSG_INFO, "[hs20] %s node not found", name);
+		xml_node_free(ctx->xml, unode);
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+
+	if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) {
+		wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi");
+		xml_node_free(ctx->xml, unode);
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+	pos = uri + 8;
+
+	if (ctx->fqdn == NULL) {
+		wpa_printf(MSG_INFO, "FQDN not known");
+		xml_node_free(ctx->xml, unode);
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+	fqdn_len = os_strlen(ctx->fqdn);
+	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+	    pos[fqdn_len] != '/') {
+		wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s",
+			   ctx->fqdn);
+		xml_node_free(ctx->xml, unode);
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+	pos += fqdn_len + 1;
+
+	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+		wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription",
+			   ctx->fqdn);
+		xml_node_free(ctx->xml, unode);
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+	pos += 24;
+
+	wpa_printf(MSG_INFO, "Update command for PPS node %s", pos);
+
+	node = get_node(ctx->xml, pps, pos);
+	if (node) {
+		parent = xml_node_get_parent(ctx->xml, node);
+		xml_node_detach(ctx->xml, node);
+		wpa_printf(MSG_INFO, "Replace '%s' node", name);
+	} else {
+		char *pos2;
+		pos2 = os_strrchr(pos, '/');
+		if (pos2 == NULL) {
+			parent = pps;
+		} else {
+			*pos2 = '\0';
+			parent = get_node(ctx->xml, pps, pos);
+		}
+		if (parent == NULL) {
+			wpa_printf(MSG_INFO, "Could not find parent %s", pos);
+			xml_node_free(ctx->xml, unode);
+			xml_node_get_attr_value_free(ctx->xml, uri);
+			return -1;
+		}
+		wpa_printf(MSG_INFO, "Add '%s' node", name);
+	}
+	xml_node_add_child(ctx->xml, parent, unode);
+
+	xml_node_get_attr_value_free(ctx->xml, uri);
+
+	return 0;
+}
+
+
+static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update,
+		      const char *pps_fname, xml_node_t *pps)
+{
+	wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)");
+	xml_node_for_each_sibling(ctx->xml, update) {
+		xml_node_for_each_check(ctx->xml, update);
+		if (process_update_node(ctx, pps, update) < 0)
+			return -1;
+	}
+
+	return update_pps_file(ctx, pps_fname, pps);
+}
+
+
+static void hs20_sub_rem_complete(struct hs20_osu_client *ctx,
+				  const char *pps_fname)
+{
+	/*
+	 * Update wpa_supplicant credentials and reconnect using updated
+	 * information.
+	 */
+	wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+	cmd_set_pps(ctx, pps_fname);
+
+	if (ctx->no_reconnect)
+		return;
+
+	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
+		wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
+}
+
+
+static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx,
+				       xml_node_t *cmd,
+				       const char *session_id,
+				       const char *pps_fname)
+{
+	xml_namespace_t *ns;
+	xml_node_t *node, *ret_node;
+	char *urn;
+
+	urn = get_spp_attr_value(ctx->xml, cmd, "moURN");
+	if (!urn) {
+		wpa_printf(MSG_INFO, "No URN included");
+		return NULL;
+	}
+	wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn);
+	if (strcasecmp(urn, URN_HS20_PPS) != 0) {
+		wpa_printf(MSG_INFO, "Unsupported moURN");
+		xml_node_get_attr_value_free(ctx->xml, urn);
+		return NULL;
+	}
+	xml_node_get_attr_value_free(ctx->xml, urn);
+
+	if (!pps_fname) {
+		wpa_printf(MSG_INFO, "PPS file name no known");
+		return NULL;
+	}
+
+	node = build_spp_post_dev_data(ctx, &ns, session_id,
+				       "MO upload");
+	if (node == NULL)
+		return NULL;
+	add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname);
+
+	ret_node = soap_send_receive(ctx->http, node);
+	if (ret_node == NULL)
+		return NULL;
+
+	debug_dump_node(ctx, "Received response to MO upload", ret_node);
+
+	if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+		wpa_printf(MSG_INFO, "SPP validation failed");
+		xml_node_free(ctx->xml, ret_node);
+		return NULL;
+	}
+
+	return ret_node;
+}
+
+
+static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo,
+		       char *fname, size_t fname_len)
+{
+	char *uri, *urn;
+	int ret;
+
+	debug_dump_node(ctx, "Received addMO", add_mo);
+
+	urn = get_spp_attr_value(ctx->xml, add_mo, "moURN");
+	if (urn == NULL) {
+		wpa_printf(MSG_INFO, "[hs20] No moURN in addMO");
+		return -1;
+	}
+	wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn);
+	if (strcasecmp(urn, URN_HS20_PPS) != 0) {
+		wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO");
+		xml_node_get_attr_value_free(ctx->xml, urn);
+		return -1;
+	}
+	xml_node_get_attr_value_free(ctx->xml, urn);
+
+	uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI");
+	if (uri == NULL) {
+		wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO");
+		return -1;
+	}
+	wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri);
+
+	ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len);
+	xml_node_get_attr_value_free(ctx->xml, uri);
+	return ret;
+}
+
+
+static int process_spp_user_input_response(struct hs20_osu_client *ctx,
+					   const char *session_id,
+					   xml_node_t *add_mo)
+{
+	int ret;
+	char fname[300];
+
+	debug_dump_node(ctx, "addMO", add_mo);
+
+	wpa_printf(MSG_INFO, "Subscription registration completed");
+
+	if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) {
+		wpa_printf(MSG_INFO, "Could not add MO");
+		ret = hs20_spp_update_response(
+			ctx, session_id,
+			"Error occurred",
+			"MO addition or update failed");
+		return 0;
+	}
+
+	ret = hs20_spp_update_response(ctx, session_id, "OK", NULL);
+	if (ret == 0)
+		hs20_sub_rem_complete(ctx, fname);
+
+	return 0;
+}
+
+
+static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx,
+						    const char *session_id)
+{
+	xml_node_t *node, *ret_node;
+
+	node = build_spp_post_dev_data(ctx, NULL, session_id,
+				       "User input completed");
+	if (node == NULL)
+		return NULL;
+
+	ret_node = soap_send_receive(ctx->http, node);
+	if (!ret_node) {
+		if (soap_reinit_client(ctx->http) < 0)
+			return NULL;
+		wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
+		node = build_spp_post_dev_data(ctx, NULL, session_id,
+					       "User input completed");
+		if (node == NULL)
+			return NULL;
+		ret_node = soap_send_receive(ctx->http, node);
+		if (ret_node == NULL)
+			return NULL;
+		wpa_printf(MSG_INFO, "Continue with new connection");
+	}
+
+	if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+		wpa_printf(MSG_INFO, "SPP validation failed");
+		xml_node_free(ctx->xml, ret_node);
+		return NULL;
+	}
+
+	return ret_node;
+}
+
+
+static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx,
+					     xml_node_t *cmd,
+					     const char *session_id,
+					     const char *pps_fname)
+{
+	xml_namespace_t *ns;
+	xml_node_t *node, *ret_node;
+	int res;
+
+	wpa_printf(MSG_INFO, "Client certificate enrollment");
+
+	res = osu_get_certificate(ctx, cmd);
+	if (res < 0)
+		wpa_printf(MSG_INFO, "EST simpleEnroll failed");
+
+	node = build_spp_post_dev_data(ctx, &ns, session_id,
+				       res == 0 ?
+				       "Certificate enrollment completed" :
+				       "Certificate enrollment failed");
+	if (node == NULL)
+		return NULL;
+
+	ret_node = soap_send_receive(ctx->http, node);
+	if (ret_node == NULL)
+		return NULL;
+
+	debug_dump_node(ctx, "Received response to certificate enrollment "
+			"completed", ret_node);
+
+	if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+		wpa_printf(MSG_INFO, "SPP validation failed");
+		xml_node_free(ctx->xml, ret_node);
+		return NULL;
+	}
+
+	return ret_node;
+}
+
+
+static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec,
+			 const char *session_id, const char *pps_fname,
+			 xml_node_t *pps, xml_node_t **ret_node)
+{
+	xml_node_t *cmd;
+	const char *name;
+	char *uri;
+	char *id = strdup(session_id);
+
+	if (id == NULL)
+		return -1;
+
+	*ret_node = NULL;
+
+	debug_dump_node(ctx, "exec", exec);
+
+	xml_node_for_each_child(ctx->xml, cmd, exec) {
+		xml_node_for_each_check(ctx->xml, cmd);
+		break;
+	}
+	if (!cmd) {
+		wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)",
+			   cmd);
+		free(id);
+		return -1;
+	}
+
+	name = xml_node_get_localname(ctx->xml, cmd);
+
+	if (strcasecmp(name, "launchBrowserToURI") == 0) {
+		int res;
+		uri = xml_node_get_text(ctx->xml, cmd);
+		if (!uri) {
+			wpa_printf(MSG_INFO, "No URI found");
+			free(id);
+			return -1;
+		}
+		wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri);
+		write_summary(ctx, "Launch browser to URI '%s'", uri);
+		res = hs20_web_browser(uri);
+		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 - URL: %s ca_fname: %s",
+		   url, ctx->ca_fname ? ctx->ca_fname : "N/A");
+
+	os_free(ctx->server_url);
+	ctx->server_url = os_strdup(url);
+
+	if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
+			     NULL) < 0)
+		return -1;
+	spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
+			  "Subscription registration", NULL, NULL);
+
+	return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
+{
+	if (url == NULL) {
+		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "SIM provisioning requested");
+
+	os_free(ctx->server_url);
+	ctx->server_url = os_strdup(url);
+
+	wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
+
+	if (wait_ip_addr(ctx->ifname, 15) < 0) {
+		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+	}
+
+	if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
+			     NULL) < 0)
+		return -1;
+	spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
+			  "Subscription provisioning", NULL, NULL);
+
+	return ctx->pps_cred_set ? 0 : -1;
+}
diff --git a/hostap/hs20/server/Makefile b/hostap/hs20/server/Makefile
new file mode 100644
index 0000000..248ed5c
--- /dev/null
+++ b/hostap/hs20/server/Makefile
@@ -0,0 +1,46 @@
+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
+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/hostap/hs20/server/ca/clean.sh b/hostap/hs20/server/ca/clean.sh
new file mode 100755
index 0000000..c72dcbd
--- /dev/null
+++ b/hostap/hs20/server/ca/clean.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+for i in server-client server server-revoked user ocsp; do
+    rm -f $i.csr $i.key $i.pem
+done
+
+rm -f openssl.cnf.tmp
+if [ -d demoCA ]; then
+    rm -r demoCA
+fi
+rm -f ca.pem logo.asn1 logo.der server.der ocsp-server-cache.der
+rm -f my-openssl.cnf my-openssl-root.cnf
+#rm -r rootCA
diff --git a/hostap/hs20/server/ca/est-csrattrs.cnf b/hostap/hs20/server/ca/est-csrattrs.cnf
new file mode 100644
index 0000000..b50ea00
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/ca/est-csrattrs.sh b/hostap/hs20/server/ca/est-csrattrs.sh
new file mode 100755
index 0000000..0b73a04
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/ca/hs20.oid b/hostap/hs20/server/ca/hs20.oid
new file mode 100644
index 0000000..a829ff2
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/ca/ocsp-req.sh b/hostap/hs20/server/ca/ocsp-req.sh
new file mode 100755
index 0000000..931a206
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/ca/ocsp-responder-ica.sh b/hostap/hs20/server/ca/ocsp-responder-ica.sh
new file mode 100755
index 0000000..116c6e1
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/ca/ocsp-responder.sh b/hostap/hs20/server/ca/ocsp-responder.sh
new file mode 100755
index 0000000..8cebd74
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/ca/ocsp-update-cache.sh b/hostap/hs20/server/ca/ocsp-update-cache.sh
new file mode 100755
index 0000000..8ddef9b
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/ca/openssl-root.cnf b/hostap/hs20/server/ca/openssl-root.cnf
new file mode 100644
index 0000000..5bc50be
--- /dev/null
+++ b/hostap/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 = @PASSWORD@
+output_password = @PASSWORD@
+
+string_mask = utf8only
+
+[ req_distinguished_name ]
+countryName			= Country Name (2 letter code)
+countryName_default		= US
+countryName_min			= 2
+countryName_max			= 2
+
+localityName			= Locality Name (eg, city)
+localityName_default		= Tuusula
+
+0.organizationName		= Organization Name (eg, company)
+0.organizationName_default	= WFA Hotspot 2.0
+
+##organizationalUnitName		= Organizational Unit Name (eg, section)
+#organizationalUnitName_default	=
+#@OU@
+
+commonName			= Common Name (e.g. server FQDN or YOUR name)
+#@CN@
+commonName_max			= 64
+
+emailAddress			= Email Address
+emailAddress_max		= 64
+
+[ req_attributes ]
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName=DNS:example.com,DNS:another.example.com
+
+[ v3_ca ]
+
+# Hotspot 2.0 PKI requirements
+subjectKeyIdentifier=hash
+basicConstraints = critical,CA:true
+keyUsage = critical, cRLSign, keyCertSign
+
+[ crl_ext ]
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ v3_OCSP ]
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = OCSPSigning
diff --git a/hostap/hs20/server/ca/openssl.cnf b/hostap/hs20/server/ca/openssl.cnf
new file mode 100644
index 0000000..6141013
--- /dev/null
+++ b/hostap/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 = @PASSWORD@
+output_password = @PASSWORD@
+
+string_mask = utf8only
+
+[ req_distinguished_name ]
+countryName			= Country Name (2 letter code)
+countryName_default		= FI
+countryName_min			= 2
+countryName_max			= 2
+
+localityName			= Locality Name (eg, city)
+localityName_default		= Tuusula
+
+0.organizationName		= Organization Name (eg, company)
+0.organizationName_default	= @DOMAIN@
+
+##organizationalUnitName		= Organizational Unit Name (eg, section)
+#organizationalUnitName_default	=
+#@OU@
+
+commonName			= Common Name (e.g. server FQDN or YOUR name)
+#@CN@
+commonName_max			= 64
+
+emailAddress			= Email Address
+emailAddress_max		= 64
+
+[ req_attributes ]
+
+[ v3_ca ]
+
+# Hotspot 2.0 PKI requirements
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer
+basicConstraints = critical, CA:true, pathlen:0
+keyUsage = critical, cRLSign, keyCertSign
+authorityInfoAccess = OCSP;URI:@OCSP_URI@
+# For SP intermediate CA
+#subjectAltName=critical,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:engExample OSU
+#nameConstraints=permitted;DNS:.@DOMAIN@
+#1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
+
+[ v3_osu_server ]
+
+basicConstraints = critical, CA:true, pathlen:0
+keyUsage = critical, keyEncipherment
+#@ALTNAME@
+
+#logotypeoid=ASN1:SEQUENCE:LogotypeExtn
+1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
+[LogotypeExtn]
+communityLogos=EXP:0,SEQUENCE:LogotypeInfo
+[LogotypeInfo]
+# note: implicit tag converted to explicit for CHOICE
+direct=EXP:0,SEQUENCE:LogotypeData
+[LogotypeData]
+image=SEQUENCE:LogotypeImage
+[LogotypeImage]
+imageDetails=SEQUENCE:LogotypeDetails
+imageInfo=SEQUENCE:LogotypeImageInfo
+[LogotypeDetails]
+mediaType=IA5STRING:image/png
+logotypeHash=SEQUENCE:HashAlgAndValues
+logotypeURI=SEQUENCE:URI
+[HashAlgAndValues]
+value1=SEQUENCE:HashAlgAndValueSHA256
+#value2=SEQUENCE:HashAlgAndValueSHA1
+[HashAlgAndValueSHA256]
+hashAlg=SEQUENCE:sha256_alg
+hashValue=FORMAT:HEX,OCTETSTRING:@LOGO_HASH256@
+[HashAlgAndValueSHA1]
+hashAlg=SEQUENCE:sha1_alg
+hashValue=FORMAT:HEX,OCTETSTRING:@LOGO_HASH1@
+[sha256_alg]
+algorithm=OID:sha256
+[sha1_alg]
+algorithm=OID:sha1
+[URI]
+uri=IA5STRING:@LOGO_URI@
+[LogotypeImageInfo]
+# default value color(1), component optional
+#type=IMP:0,INTEGER:1
+fileSize=INTEGER:7549
+xSize=INTEGER:128
+ySize=INTEGER:80
+language=IMP:4,IA5STRING:zxx
+
+[ crl_ext ]
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ v3_OCSP ]
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = OCSPSigning
+
+[ ext_client ]
+
+basicConstraints=CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+authorityInfoAccess = OCSP;URI:@OCSP_URI@
+#@ALTNAME@
+extendedKeyUsage = clientAuth
+
+[ ext_server ]
+
+# Hotspot 2.0 PKI requirements
+basicConstraints=critical, CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+authorityInfoAccess = OCSP;URI:@OCSP_URI@
+#@ALTNAME@
+extendedKeyUsage = critical, serverAuth
+keyUsage = critical, keyEncipherment
diff --git a/hostap/hs20/server/ca/setup.sh b/hostap/hs20/server/ca/setup.sh
new file mode 100755
index 0000000..78abccc
--- /dev/null
+++ b/hostap/hs20/server/ca/setup.sh
@@ -0,0 +1,209 @@
+#!/bin/sh
+
+if [ -z "$OPENSSL" ]; then
+    OPENSSL=openssl
+fi
+export OPENSSL_CONF=$PWD/openssl.cnf
+PASS=whatever
+if [ -z "$DOMAIN" ]; then
+    DOMAIN=w1.fi
+fi
+COMPANY=w1.fi
+OPER_ENG="engw1.fi TESTING USE"
+OPER_FI="finw1.fi TESTIKÄYTTÖ"
+CNR="Hotspot 2.0 Trust Root CA - 99"
+CNO="ocsp.$DOMAIN"
+CNV="osu-revoked.$DOMAIN"
+CNOC="osu-client.$DOMAIN"
+OSU_SERVER_HOSTNAME="osu.$DOMAIN"
+DEBUG=0
+OCSP_URI="http://$CNO:8888/"
+LOGO_URI="http://osu.w1.fi/w1fi_logo.png"
+LOGO_HASH256="4532f7ec36424381617c03c6ce87b55a51d6e7177ffafda243cebf280a68954d"
+LOGO_HASH1="5e1d5085676eede6b02da14d31c523ec20ffba0b"
+
+# Command line overrides
+USAGE=$( cat <<EOF
+Usage:\n
+# -c:  Company name, used to generate Subject name CN for Intermediate CA\n
+# -C:  Subject name CN of the Root CA ($CNR)\n
+# -D:  Enable debugging (set -x, etc)\n
+# -g:  Logo sha1 hash ($LOGO_HASH1)\n
+# -G:  Logo sha256 hash ($LOGO_HASH256)\n
+# -h:  Show this help message\n
+# -l:  Logo URI ($LOGO_URI)\n
+# -m:  Domain ($DOMAIN)\n
+# -o:  Subject name CN for OSU-Client Server ($CNOC)\n
+# -O:  Subject name CN for OCSP Server ($CNO)\n
+# -p:  passphrase for private keys ($PASS)\n
+# -r:  Operator-english ($OPER_ENG)\n
+# -R:  Operator-finish ($OPER_FI)\n
+# -S:  OSU Server name ($OSU_SERVER_HOSTNAME)\n
+# -u:  OCSP-URI ($OCSP_URI)\n
+# -V:  Subject name CN for OSU-Revoked Server ($CNV)\n
+EOF
+)
+
+while getopts "c:C:Dg:G:l:m:o:O:p:r:R:S:u:V:h" flag
+  do
+  case $flag in
+      c) COMPANY=$OPTARG;;
+      C) CNR=$OPTARG;;
+      D) DEBUG=1;;
+      g) LOGO_HASH1=$OPTARG;;
+      G) LOGO_HASH256=$OPTARG;;
+      h) echo -e $USAGE; exit 0;;
+      l) LOGO_URI=$OPTARG;;
+      m) DOMAIN=$OPTARG;;
+      o) CNOC=$OPTARG;;
+      O) CNO=$OPTARG;;
+      p) PASS=$OPTARG;;
+      r) OPER_ENG=$OPTARG;;
+      R) OPER_FI=$OPTARG;;
+      S) OSU_SERVER_HOSTNAME=$OPTARG;;
+      u) OCSP_URI=$OPTARG;;
+      V) CNV=$OPTARG;;
+      *) echo "Unknown flag: $flag"; echo -e $USAGE; exit 1;;
+  esac
+done
+
+fail()
+{
+    echo "$*"
+    exit 1
+}
+
+echo
+echo "---[ Root CA ]----------------------------------------------------------"
+echo
+
+if [ $DEBUG = 1 ]
+then
+    set -x
+fi
+
+# Set the passphrase and some other common config accordingly.
+cat openssl-root.cnf | sed "s/@PASSWORD@/$PASS/" \
+ > my-openssl-root.cnf
+
+cat openssl.cnf | sed "s/@PASSWORD@/$PASS/" |
+sed "s,@OCSP_URI@,$OCSP_URI," |
+sed "s,@LOGO_URI@,$LOGO_URI," |
+sed "s,@LOGO_HASH1@,$LOGO_HASH1," |
+sed "s,@LOGO_HASH256@,$LOGO_HASH256," |
+sed "s/@DOMAIN@/$DOMAIN/" \
+ > my-openssl.cnf
+
+
+cat my-openssl-root.cnf | sed "s/#@CN@/commonName_default = $CNR/" > openssl.cnf.tmp
+mkdir -p rootCA/certs rootCA/crl rootCA/newcerts rootCA/private
+touch rootCA/index.txt
+if [ -e rootCA/private/cakey.pem ]; then
+    echo " * Use existing Root CA"
+else
+    echo " * Generate Root CA private key"
+    $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:4096 -keyout rootCA/private/cakey.pem -out rootCA/careq.pem || fail "Failed to generate Root CA private key"
+    echo " * Sign Root CA certificate"
+    $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out rootCA/cacert.pem -days 10957 -batch -keyfile rootCA/private/cakey.pem -passin pass:$PASS -selfsign -extensions v3_ca -outdir rootCA/newcerts -infiles rootCA/careq.pem || fail "Failed to sign Root CA certificate"
+    $OPENSSL x509 -in rootCA/cacert.pem -out rootCA/cacert.der -outform DER || fail "Failed to create rootCA DER"
+    sha256sum rootCA/cacert.der > rootCA/cacert.fingerprint || fail "Failed to create rootCA fingerprint"
+fi
+if [ ! -e rootCA/crlnumber ]; then
+    echo 00 > rootCA/crlnumber
+fi
+
+echo
+echo "---[ Intermediate CA ]--------------------------------------------------"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $COMPANY Hotspot 2.0 Intermediate CA/" > openssl.cnf.tmp
+mkdir -p demoCA/certs demoCA/crl demoCA/newcerts demoCA/private
+touch demoCA/index.txt
+if [ -e demoCA/private/cakey.pem ]; then
+    echo " * Use existing Intermediate CA"
+else
+    echo " * Generate Intermediate CA private key"
+    $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -keyout demoCA/private/cakey.pem -out demoCA/careq.pem || fail "Failed to generate Intermediate CA private key"
+    echo " * Sign Intermediate CA certificate"
+    $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out demoCA/cacert.pem -days 3652 -batch -keyfile rootCA/private/cakey.pem -cert rootCA/cacert.pem -passin pass:$PASS -extensions v3_ca -infiles demoCA/careq.pem || fail "Failed to sign Intermediate CA certificate"
+    # horrible from security view point, but for testing purposes since OCSP responder does not seem to support -passin
+    openssl rsa -in demoCA/private/cakey.pem -out demoCA/private/cakey-plain.pem -passin pass:$PASS
+    $OPENSSL x509 -in demoCA/cacert.pem -out demoCA/cacert.der -outform DER || fail "Failed to create demoCA DER."
+    sha256sum demoCA/cacert.der > demoCA/cacert.fingerprint || fail "Failed to create demoCA fingerprint"
+fi
+if [ ! -e demoCA/crlnumber ]; then
+    echo 00 > demoCA/crlnumber
+fi
+
+echo
+echo "OCSP responder"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNO/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out ocsp.csr -keyout ocsp.key -extensions v3_OCSP
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -keyfile demoCA/private/cakey.pem -passin pass:$PASS -in ocsp.csr -out ocsp.pem -days 730 -extensions v3_OCSP || fail "Could not generate ocsp.pem"
+
+echo
+echo "---[ Server - to be revoked ] ------------------------------------------"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNV/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-revoked.csr -keyout server-revoked.key
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-revoked.csr -out server-revoked.pem -key $PASS -days 730 -extensions ext_server
+$OPENSSL ca -revoke server-revoked.pem -key $PASS
+
+echo
+echo "---[ Server - with client ext key use ] ---------------------------------"
+echo "---[ Only used for negative-testing for OSU-client implementation ] -----"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNOC/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-client.csr -keyout server-client.key || fail "Could not create server-client.key"
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-client.csr -out server-client.pem -key $PASS -days 730 -extensions ext_client || fail "Could not create server-client.pem"
+
+echo
+echo "---[ User ]-------------------------------------------------------------"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = User/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out user.csr -keyout user.key || fail "Could not create user.key"
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in user.csr -out user.pem -key $PASS -days 730 -extensions ext_client || fail "Could not create user.pem"
+
+echo
+echo "---[ Server ]-----------------------------------------------------------"
+echo
+
+ALT="DNS:$OSU_SERVER_HOSTNAME"
+ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:$OPER_ENG"
+ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:$OPER_FI"
+
+cat my-openssl.cnf |
+	sed "s/#@CN@/commonName_default = $OSU_SERVER_HOSTNAME/" |
+	sed "s/^##organizationalUnitName/organizationalUnitName/" |
+	sed "s/#@OU@/organizationalUnitName_default = Hotspot 2.0 Online Sign Up Server/" |
+	sed "s/#@ALTNAME@/subjectAltName=critical,$ALT/" \
+	> openssl.cnf.tmp
+echo $OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server || fail "Failed to generate server request"
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server.csr -out server.pem -key $PASS -days 730 -extensions ext_server -policy policy_osu_server || fail "Failed to sign server certificate"
+
+#dump logotype details for debugging
+$OPENSSL x509 -in server.pem -out server.der -outform DER
+openssl asn1parse -in server.der -inform DER | grep HEX | tail -1 | sed 's/.*://' | xxd -r -p > logo.der
+openssl asn1parse -in logo.der -inform DER > logo.asn1
+
+
+echo
+echo "---[ CRL ]---------------------------------------------------------------"
+echo
+
+$OPENSSL ca -config $PWD/my-openssl.cnf -gencrl -md sha256 -out demoCA/crl/crl.pem -passin pass:$PASS
+
+echo
+echo "---[ Verify ]------------------------------------------------------------"
+echo
+
+$OPENSSL verify -CAfile rootCA/cacert.pem demoCA/cacert.pem
+$OPENSSL verify -CAfile rootCA/cacert.pem -untrusted demoCA/cacert.pem *.pem
+
+cat rootCA/cacert.pem demoCA/cacert.pem > ca.pem
diff --git a/hostap/hs20/server/ca/w1fi_logo.png b/hostap/hs20/server/ca/w1fi_logo.png
new file mode 100644
index 0000000..ac7c259
--- /dev/null
+++ b/hostap/hs20/server/ca/w1fi_logo.png
Binary files differ
diff --git a/hostap/hs20/server/hs20-osu-server.txt b/hostap/hs20/server/hs20-osu-server.txt
new file mode 100644
index 0000000..001d6f2
--- /dev/null
+++ b/hostap/hs20/server/hs20-osu-server.txt
@@ -0,0 +1,257 @@
+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
+
+# Build local keys and certs
+cd ca
+# Display help options.
+./setup.sh -h
+
+# Remove old keys, fill in appropriate values, and generate your keys.
+# For instance:
+./clean.sh
+rm -fr rootCA"
+old_hostname=myserver.local
+./setup.sh -C "Hotspot 2.0 Trust Root CA - CT" -d $old_hostname \
+   -I "Hotspot 2.0 Intermediate CA - CT" -o $old_hostname-osu-client \
+   -O $old_hostname-oscp -p lanforge -S $old_hostname \
+   -V $old_hostname-osu-revoked \
+   -m local -u http://$old_hostname:8888/
+
+# Configure subscription policies
+mkdir -p /home/user/hs20-server/spp/policy
+cat > /home/user/hs20-server/spp/policy/default.xml <<EOF
+<Policy>
+	<PolicyUpdate>
+		<UpdateInterval>30</UpdateInterval>
+		<UpdateMethod>ClientInitiated</UpdateMethod>
+		<Restriction>Unrestricted</Restriction>
+		<URI>https://policy-server.osu.example.com/hs20/spp.php</URI>
+	</PolicyUpdate>
+</Policy>
+EOF
+
+
+# Install Hotspot 2.0 SPP and OMA DM XML schema/DTD files
+
+# XML schema for SPP
+# Copy the latest XML schema into /home/user/hs20-server/spp/spp.xsd
+
+# OMA DM Device Description Framework DTD
+# Copy into /home/user/hs20-server/spp/dm_ddf-v1_2.dtd
+# http://www.openmobilealliance.org/tech/DTD/dm_ddf-v1_2.dtd
+
+
+# Configure RADIUS authentication service
+# Note: Change the URL to match the setup
+# Note: Install AAA server key/certificate and root CA in Key directory
+
+cat > /home/user/hs20-server/AS/as-sql.conf <<EOF
+driver=none
+radius_server_clients=as.radius_clients
+eap_server=1
+eap_user_file=sqlite:DB/eap_user.db
+ca_cert=Key/ca.pem
+server_cert=Key/server.pem
+private_key=Key/server.key
+private_key_passwd=passphrase
+eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=eap_sim.db
+subscr_remediation_url=https://subscription-server.osu.example.com/hs20/spp.php
+EOF
+
+# Set RADIUS passphrase for the APs
+# Note: Modify to match the setup
+cat > /home/user/hs20-server/AS/as.radius_clients <<EOF
+0.0.0.0/0	radius
+EOF
+
+
+Start RADIUS authentication server
+----------------------------------
+
+cd /home/user/hs20-server/AS
+./hostapd -B as-sql.conf
+
+
+OSEN RADIUS server configuration notes
+
+The OSEN RADIUS server config file should have the 'ocsp_stapling_response'
+configuration in it. For example:
+
+# hostapd-radius config for the radius used by the OSEN AP
+interface=eth0#0
+driver=none
+logger_syslog=-1
+logger_syslog_level=2
+logger_stdout=-1
+logger_stdout_level=2
+ctrl_interface=/var/run/hostapd
+ctrl_interface_group=0
+eap_server=1
+eap_user_file=/home/user/hs20-server/AS/hostapd-osen.eap_user
+server_id=ben-ota-2-osen
+radius_server_auth_port=1811
+radius_server_clients=/home/user/hs20-server/AS/hostap.radius_clients
+
+ca_cert=/home/user/hs20-server/ca/ca.pem
+server_cert=/home/user/hs20-server/ca/server.pem
+private_key=/home/user/hs20-server/ca/server.key
+private_key_passwd=whatever
+
+ocsp_stapling_response=/home/user/hs20-server/ca/ocsp-server-cache.der
+
+The /home/user/hs20-server/AS/hostapd-osen.eap_user file should look
+similar to this, and should coorelate with the osu_nai entry in
+the non-OSEN VAP config file.  For instance:
+
+# cat hostapd-osen.eap_user
+# For OSEN authentication (Hotspot 2.0 Release 2)
+"osen@w1.fi"      WFA-UNAUTH-TLS
+
+
+# Run OCSP server:
+cd /home/user/hs20-server/ca
+./ocsp-responder.sh&
+
+# Update cache (This should be run periodically)
+./ocsp-update-cache.sh
+
+
+Configure web server
+--------------------
+
+Edit /etc/apache2/sites-available/default-ssl
+
+Add following block just before "SSL Engine Switch" line":
+
+        Alias /hs20/ "/home/user/hs20-server/www/"
+        <Directory "/home/user/hs20-server/www/">
+                Options Indexes MultiViews FollowSymLinks
+                AllowOverride None
+                Order allow,deny
+                Allow from all
+        </Directory>
+
+Update SSL configuration to use the OSU server certificate/key.
+They keys and certs are called 'server.key' and 'server.pem' from
+ca/setup.sh.
+
+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/hostap/hs20/server/hs20_spp_server.c b/hostap/hs20/server/hs20_spp_server.c
new file mode 100644
index 0000000..591f66b
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/spp_server.c b/hostap/hs20/server/spp_server.c
new file mode 100644
index 0000000..33e3fa1
--- /dev/null
+++ b/hostap/hs20/server/spp_server.c
@@ -0,0 +1,2292 @@
+/*
+ * 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_update_session_machine_managed(struct hs20_svc *ctx,
+					      const char *user,
+					      const char *realm,
+					      const char *sessionid,
+					      const int pw_mm)
+{
+	char *sql;
+
+	sql = sqlite3_mprintf("UPDATE sessions SET machine_managed=%Q WHERE id=%Q AND user=%Q AND realm=%Q",
+			      pw_mm ? "1" : "0", sessionid, user, realm);
+	if (sql == NULL)
+		return;
+	debug_print(ctx, 1, "DB: %s", sql);
+	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1,
+			    "Failed to update session machine_managed: %s",
+			    sqlite3_errmsg(ctx->db));
+	}
+	sqlite3_free(sql);
+}
+
+
+static void db_add_session_pps(struct hs20_svc *ctx, const char *user,
+			       const char *realm, const char *sessionid,
+			       xml_node_t *node)
+{
+	char *str;
+	char *sql;
+
+	str = xml_node_to_str(ctx->xml, node);
+	if (str == NULL)
+		return;
+	sql = sqlite3_mprintf("UPDATE sessions SET pps=%Q WHERE id=%Q AND "
+			      "user=%Q AND realm=%Q",
+			      str, sessionid, user, realm);
+	free(str);
+	if (sql == NULL)
+		return;
+	debug_print(ctx, 1, "DB: %s", sql);
+	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1, "Failed to add session pps: %s",
+			    sqlite3_errmsg(ctx->db));
+	}
+	sqlite3_free(sql);
+}
+
+
+static void db_add_session_devinfo(struct hs20_svc *ctx, const char *sessionid,
+				   xml_node_t *node)
+{
+	char *str;
+	char *sql;
+
+	str = xml_node_to_str(ctx->xml, node);
+	if (str == NULL)
+		return;
+	sql = sqlite3_mprintf("UPDATE sessions SET devinfo=%Q WHERE id=%Q",
+			      str, sessionid);
+	free(str);
+	if (sql == NULL)
+		return;
+	debug_print(ctx, 1, "DB: %s", sql);
+	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1, "Failed to add session devinfo: %s",
+			    sqlite3_errmsg(ctx->db));
+	}
+	sqlite3_free(sql);
+}
+
+
+static void db_add_session_devdetail(struct hs20_svc *ctx,
+				     const char *sessionid,
+				     xml_node_t *node)
+{
+	char *str;
+	char *sql;
+
+	str = xml_node_to_str(ctx->xml, node);
+	if (str == NULL)
+		return;
+	sql = sqlite3_mprintf("UPDATE sessions SET devdetail=%Q WHERE id=%Q",
+			      str, sessionid);
+	free(str);
+	if (sql == NULL)
+		return;
+	debug_print(ctx, 1, "DB: %s", sql);
+	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1, "Failed to add session devdetail: %s",
+			    sqlite3_errmsg(ctx->db));
+	}
+	sqlite3_free(sql);
+}
+
+
+static void db_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");
+	if (machine_managed) {
+		db_update_session_password(ctx, user, realm, session_id, pw);
+		db_update_session_machine_managed(ctx, user, realm, session_id,
+						  machine_managed);
+	}
+	db_add_session_pps(ctx, user, realm, session_id, pps);
+
+	hs20_eventlog_node(ctx, user, realm, session_id,
+			   "new subscription", pps);
+	free(user);
+	free(pw);
+
+	tnds = mo_to_tnds(ctx->xml, pps, 0, URN_HS20_PPS, NULL);
+	xml_node_free(ctx->xml, pps);
+	if (!tnds) {
+		xml_node_free(ctx->xml, spp_node);
+		free(realm);
+		return NULL;
+	}
+
+	str = xml_node_to_str(ctx->xml, tnds);
+	xml_node_free(ctx->xml, tnds);
+	if (str == NULL) {
+		xml_node_free(ctx->xml, spp_node);
+		free(realm);
+		return NULL;
+	}
+
+	node = xml_node_create_text(ctx->xml, spp_node, ns, "addMO", str);
+	free(str);
+	snprintf(buf, sizeof(buf), "./Wi-Fi/%s/PerProviderSubscription", realm);
+	free(realm);
+	xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", buf);
+	xml_node_add_attr(ctx->xml, node, ns, "moURN", URN_HS20_PPS);
+
+	return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_free_remediation(struct hs20_svc *ctx,
+						     const char *user,
+						     const char *realm,
+						     const char *session_id)
+{
+	xml_namespace_t *ns;
+	xml_node_t *spp_node;
+	xml_node_t *cred;
+	char buf[400];
+	char *status;
+	char *free_account, *pw;
+
+	free_account = db_get_osu_config_val(ctx, realm, "free_account");
+	if (free_account == NULL)
+		return NULL;
+	pw = db_get_val(ctx, free_account, realm, "password", 0);
+	if (pw == NULL) {
+		free(free_account);
+		return NULL;
+	}
+
+	cred = build_credential_pw(ctx, free_account, realm, pw);
+	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, xsd file: %s  xml-error: %s",
+			    fname, xml_err);
+		hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+				   "SPP message failed validation", node);
+		hs20_eventlog(ctx, auth_user, auth_realm, session_id,
+			      "Validation errors", xml_err);
+		os_free(xml_err);
+		xml_node_get_attr_value_free(ctx->xml, session_id);
+		/* TODO: what to return here? */
+		ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+					   "SppValidationError");
+		return ret;
+	}
+
+	session_id = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+						"sessionID");
+	if (session_id) {
+		char *tmp;
+		debug_print(ctx, 1, "Received sessionID %s", session_id);
+		tmp = os_strdup(session_id);
+		xml_node_get_attr_value_free(ctx->xml, session_id);
+		if (tmp == NULL)
+			return NULL;
+		session_id = tmp;
+	} else {
+		session_id = gen_spp_session_id();
+		if (session_id == NULL) {
+			debug_print(ctx, 1, "Failed to generate sessionID");
+			return NULL;
+		}
+		debug_print(ctx, 1, "Generated sessionID %s", session_id);
+	}
+
+	op_name = xml_node_get_localname(ctx->xml, node);
+	if (op_name == NULL) {
+		debug_print(ctx, 1, "Could not get op_name");
+		return NULL;
+	}
+
+	if (strcmp(op_name, "sppPostDevData") == 0) {
+		hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+				   "sppPostDevData received and validated",
+				   node);
+		ret = hs20_spp_post_dev_data(ctx, node, auth_user, auth_realm,
+					     session_id, dmacc);
+	} else if (strcmp(op_name, "sppUpdateResponse") == 0) {
+		hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+				   "sppUpdateResponse received and validated",
+				   node);
+		ret = hs20_spp_update_response(ctx, node, auth_user,
+					       auth_realm, session_id, dmacc);
+	} else {
+		hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+				   "Unsupported SPP message received and "
+				   "validated", node);
+		debug_print(ctx, 1, "Unsupported operation '%s'", op_name);
+		/* TODO: what to return here? */
+		ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+					   "SppUnknownCommandError");
+	}
+	os_free(session_id);
+
+	if (ret == NULL) {
+		/* TODO: what to return here? */
+		ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+					   "SppInternalError");
+	}
+
+	return ret;
+}
+
+
+int hs20_spp_server_init(struct hs20_svc *ctx)
+{
+	char fname[200];
+	ctx->db = NULL;
+	snprintf(fname, sizeof(fname), "%s/AS/DB/eap_user.db", ctx->root_dir);
+	if (sqlite3_open(fname, &ctx->db)) {
+		printf("Failed to open sqlite database: %s\n",
+		       sqlite3_errmsg(ctx->db));
+		sqlite3_close(ctx->db);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void hs20_spp_server_deinit(struct hs20_svc *ctx)
+{
+	sqlite3_close(ctx->db);
+	ctx->db = NULL;
+}
diff --git a/hostap/hs20/server/spp_server.h b/hostap/hs20/server/spp_server.h
new file mode 100644
index 0000000..7b27be3
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/sql-example.txt b/hostap/hs20/server/sql-example.txt
new file mode 100644
index 0000000..20dcf2f
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/sql.txt b/hostap/hs20/server/sql.txt
new file mode 100644
index 0000000..6609538
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/www/add-free.php b/hostap/hs20/server/www/add-free.php
new file mode 100644
index 0000000..1efc655
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/www/add-mo.php b/hostap/hs20/server/www/add-mo.php
new file mode 100644
index 0000000..a3b4513
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/www/cert-enroll.php b/hostap/hs20/server/www/cert-enroll.php
new file mode 100644
index 0000000..f023ca5
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/www/config.php b/hostap/hs20/server/www/config.php
new file mode 100644
index 0000000..e3af435
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/www/est.php b/hostap/hs20/server/www/est.php
new file mode 100644
index 0000000..a45648b
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/www/free-remediation.php b/hostap/hs20/server/www/free-remediation.php
new file mode 100644
index 0000000..5648b30
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/www/free.php b/hostap/hs20/server/www/free.php
new file mode 100644
index 0000000..8195069
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/www/redirect.php b/hostap/hs20/server/www/redirect.php
new file mode 100644
index 0000000..8fc9cd6
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/www/remediation.php b/hostap/hs20/server/www/remediation.php
new file mode 100644
index 0000000..392a7bd
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/www/signup.php b/hostap/hs20/server/www/signup.php
new file mode 100644
index 0000000..aeb2f68
--- /dev/null
+++ b/hostap/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 for id: $id");
+}
+$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/hostap/hs20/server/www/spp.php b/hostap/hs20/server/www/spp.php
new file mode 100644
index 0000000..dde4434
--- /dev/null
+++ b/hostap/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/hostap/hs20/server/www/users.php b/hostap/hs20/server/www/users.php
new file mode 100644
index 0000000..c340a33
--- /dev/null
+++ b/hostap/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/hostap/mac80211_hwsim/tests/0001-wpa2-psk/hostapd.conf b/hostap/mac80211_hwsim/tests/0001-wpa2-psk/hostapd.conf
new file mode 100644
index 0000000..08cde7e
--- /dev/null
+++ b/hostap/mac80211_hwsim/tests/0001-wpa2-psk/hostapd.conf
@@ -0,0 +1,11 @@
+interface=wlan0
+driver=nl80211
+
+hw_mode=g
+channel=1
+ssid=mac80211 test
+
+wpa=2
+wpa_key_mgmt=WPA-PSK
+wpa_pairwise=CCMP
+wpa_passphrase=12345678
diff --git a/hostap/mac80211_hwsim/tests/0001-wpa2-psk/test.txt b/hostap/mac80211_hwsim/tests/0001-wpa2-psk/test.txt
new file mode 100644
index 0000000..05d85a0
--- /dev/null
+++ b/hostap/mac80211_hwsim/tests/0001-wpa2-psk/test.txt
@@ -0,0 +1,7 @@
+# WPA2-Personal (PSK) with CCMP, AP and single client
+
+modprobe mac80211_hwsim
+
+hostapd hostapd.conf
+
+wpa_supplicant -Dwext -iwlan1 -c wpa_supplicant.conf
diff --git a/hostap/mac80211_hwsim/tests/0001-wpa2-psk/wpa_supplicant.conf b/hostap/mac80211_hwsim/tests/0001-wpa2-psk/wpa_supplicant.conf
new file mode 100644
index 0000000..299128c
--- /dev/null
+++ b/hostap/mac80211_hwsim/tests/0001-wpa2-psk/wpa_supplicant.conf
@@ -0,0 +1,10 @@
+ctrl_interface=/var/run/wpa_supplicant
+
+network={
+	ssid="mac80211 test"
+	psk="12345678"
+	key_mgmt=WPA-PSK
+	proto=WPA2
+	pairwise=CCMP
+	group=CCMP
+}
diff --git a/hostap/mac80211_hwsim/tests/0002-vlan/hostapd.accept b/hostap/mac80211_hwsim/tests/0002-vlan/hostapd.accept
new file mode 100644
index 0000000..e97a175
--- /dev/null
+++ b/hostap/mac80211_hwsim/tests/0002-vlan/hostapd.accept
@@ -0,0 +1,2 @@
+02:00:00:00:01:00	1
+02:00:00:00:02:00	2
diff --git a/hostap/mac80211_hwsim/tests/0002-vlan/hostapd.conf b/hostap/mac80211_hwsim/tests/0002-vlan/hostapd.conf
new file mode 100644
index 0000000..8698f0e
--- /dev/null
+++ b/hostap/mac80211_hwsim/tests/0002-vlan/hostapd.conf
@@ -0,0 +1,12 @@
+interface=wlan0
+driver=nl80211
+
+hw_mode=g
+channel=1
+ssid=mac80211 test
+
+dynamic_vlan=2
+vlan_file=hostapd.vlan
+
+macaddr_acl=0
+accept_mac_file=hostapd.accept
diff --git a/hostap/mac80211_hwsim/tests/0002-vlan/hostapd.vlan b/hostap/mac80211_hwsim/tests/0002-vlan/hostapd.vlan
new file mode 100644
index 0000000..b3750b2
--- /dev/null
+++ b/hostap/mac80211_hwsim/tests/0002-vlan/hostapd.vlan
@@ -0,0 +1 @@
+*	vlan#
diff --git a/hostap/mac80211_hwsim/tests/0002-vlan/test.txt b/hostap/mac80211_hwsim/tests/0002-vlan/test.txt
new file mode 100644
index 0000000..8c92f1c
--- /dev/null
+++ b/hostap/mac80211_hwsim/tests/0002-vlan/test.txt
@@ -0,0 +1,15 @@
+# Plaintext connection, two clients, different VLANs
+
+modprobe mac80211_hwsim radios=3
+
+hostapd hostapd.conf
+
+ifconfig wlan1 up
+iwconfig wlan1 essid "mac80211 test"
+
+ifconfig wlan2 up
+iwconfig wlan2 essid "mac80211 test"
+
+# Expected results:
+# STA1(wlan1) is bound to vlan1
+# STA2(wlan2) is bound to vlan2
diff --git a/hostap/mac80211_hwsim/tools/Makefile b/hostap/mac80211_hwsim/tools/Makefile
new file mode 100644
index 0000000..fb29d8d
--- /dev/null
+++ b/hostap/mac80211_hwsim/tools/Makefile
@@ -0,0 +1,15 @@
+all: hwsim_test
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef CFLAGS
+CFLAGS = -O2 -Wall -g
+endif
+
+hwsim_test: hwsim_test.o
+
+clean:
+	rm -rf *.o
+	rm -rf hwsim_test
diff --git a/hostap/mac80211_hwsim/tools/hwsim_test.c b/hostap/mac80211_hwsim/tools/hwsim_test.c
new file mode 100644
index 0000000..c22a33f
--- /dev/null
+++ b/hostap/mac80211_hwsim/tools/hwsim_test.c
@@ -0,0 +1,306 @@
+/*
+ * hwsim_test - Data connectivity test for mac80211_hwsim
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * 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 <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+#define HWSIM_ETHERTYPE ETHERTYPE_IP
+#define HWSIM_PACKETLEN 1500
+
+static unsigned char addr1[ETH_ALEN], addr2[ETH_ALEN], bcast[ETH_ALEN];
+
+static u_int16_t checksum(const void *buf, size_t len)
+{
+	size_t i;
+	u_int32_t sum = 0;
+	const u_int16_t *pos = buf;
+
+	for (i = 0; i < len / 2; i++)
+		sum += *pos++;
+
+	while (sum >> 16)
+		sum = (sum & 0xffff) + (sum >> 16);
+
+	return sum ^ 0xffff;
+}
+
+
+static void tx(int s, const char *ifname, int ifindex,
+	       const unsigned char *src, const unsigned char *dst,
+	       u_int8_t tos)
+{
+	char buf[HWSIM_PACKETLEN], *pos;
+	struct ether_header *eth;
+	struct iphdr *ip;
+	int i;
+
+	printf("TX: %s(ifindex=%d) " MACSTR " -> " MACSTR "\n",
+	       ifname, ifindex, MAC2STR(src), MAC2STR(dst));
+
+	eth = (struct ether_header *) buf;
+	memcpy(eth->ether_dhost, dst, ETH_ALEN);
+	memcpy(eth->ether_shost, src, ETH_ALEN);
+	eth->ether_type = htons(HWSIM_ETHERTYPE);
+	ip = (struct iphdr *) (eth + 1);
+	memset(ip, 0, sizeof(*ip));
+	ip->ihl = 5;
+	ip->version = 4;
+	ip->ttl = 64;
+	ip->tos = tos;
+	ip->tot_len = htons(HWSIM_PACKETLEN - sizeof(*eth));
+	ip->protocol = 1;
+	ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1);
+	ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2);
+	ip->check = checksum(ip, sizeof(*ip));
+	pos = (char *) (ip + 1);
+	for (i = 0; i < sizeof(buf) - sizeof(*eth) - sizeof(*ip); i++)
+		*pos++ = i;
+
+	if (send(s, buf, sizeof(buf), 0) < 0)
+		perror("send");
+}
+
+
+struct rx_result {
+	unsigned int rx_unicast1:1;
+	unsigned int rx_broadcast1:1;
+	unsigned int rx_unicast2:1;
+	unsigned int rx_broadcast2:1;
+};
+
+
+static void rx(int s, int iface, const char *ifname, int ifindex,
+	       struct rx_result *res)
+{
+	char buf[HWSIM_PACKETLEN + 1], *pos;
+	struct ether_header *eth;
+	struct iphdr *ip;
+	int len, i;
+
+	len = recv(s, buf, sizeof(buf), 0);
+	if (len < 0) {
+		perror("recv");
+		return;
+	}
+	eth = (struct ether_header *) buf;
+
+	printf("RX: %s(ifindex=%d) " MACSTR " -> " MACSTR " (len=%d)\n",
+	       ifname, ifindex,
+	       MAC2STR(eth->ether_shost), MAC2STR(eth->ether_dhost), len);
+
+	if (len != HWSIM_PACKETLEN) {
+		printf("Ignore frame with unexpected RX length (%d)\n", len);
+		return;
+	}
+
+	ip = (struct iphdr *) (eth + 1);
+	pos = (char *) (ip + 1);
+	for (i = 0; i < sizeof(buf) - 1 - sizeof(*eth) - sizeof(*ip); i++) {
+		if ((unsigned char) *pos != (unsigned char) i) {
+			printf("Ignore frame with unexpected contents\n");
+			printf("i=%d received=0x%x expected=0x%x\n",
+			       i, (unsigned char) *pos, (unsigned char) i);
+			return;
+		}
+		pos++;
+	}
+
+	if (iface == 1 &&
+		   memcmp(eth->ether_dhost, addr1, ETH_ALEN) == 0 &&
+		   memcmp(eth->ether_shost, addr2, ETH_ALEN) == 0)
+		res->rx_unicast1 = 1;
+	else if (iface == 1 &&
+		   memcmp(eth->ether_dhost, bcast, ETH_ALEN) == 0 &&
+		   memcmp(eth->ether_shost, addr2, ETH_ALEN) == 0)
+		res->rx_broadcast1 = 1;
+	else if (iface == 2 &&
+		   memcmp(eth->ether_dhost, addr2, ETH_ALEN) == 0 &&
+		   memcmp(eth->ether_shost, addr1, ETH_ALEN) == 0)
+		res->rx_unicast2 = 1;
+	else if (iface == 2 &&
+		   memcmp(eth->ether_dhost, bcast, ETH_ALEN) == 0 &&
+		   memcmp(eth->ether_shost, addr1, ETH_ALEN) == 0)
+		res->rx_broadcast2 = 1;
+}
+
+
+static void usage(void)
+{
+	fprintf(stderr, "usage: hwsim_test [-D<DSCP>] [-t<tos>] <ifname1> <ifname2>\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+	int s1 = -1, s2 = -1, ret = -1, c;
+	struct ifreq ifr;
+	int ifindex1, ifindex2;
+	struct sockaddr_ll ll;
+	fd_set rfds;
+	struct timeval tv;
+	struct rx_result res;
+	char *s_ifname, *d_ifname, *end;
+	int tos = 0;
+
+	for (;;) {
+		c = getopt(argc, argv, "D:t:");
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'D':
+			tos = strtol(optarg, &end, 0) << 2;
+			if (*end) {
+				usage();
+				return -1;
+			}
+			break;
+		case 't':
+			tos = strtol(optarg, &end, 0);
+			if (*end) {
+				usage();
+				return -1;
+			}
+			break;
+		default:
+			usage();
+			return -1;
+		}
+	}
+
+	if (optind != argc - 2) {
+		usage();
+		return -1;
+	}
+
+	s_ifname = argv[optind];
+	d_ifname = argv[optind + 1];
+
+	memset(bcast, 0xff, ETH_ALEN);
+
+	s1 = socket(PF_PACKET, SOCK_RAW, htons(HWSIM_ETHERTYPE));
+	if (s1 < 0) {
+		perror("socket");
+		goto fail;
+	}
+
+	s2 = socket(PF_PACKET, SOCK_RAW, htons(HWSIM_ETHERTYPE));
+	if (s2 < 0) {
+		perror("socket");
+		goto fail;
+	}
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, s_ifname, sizeof(ifr.ifr_name));
+	if (ioctl(s1, SIOCGIFINDEX, &ifr) < 0) {
+		perror("ioctl[SIOCGIFINDEX]");
+		goto fail;
+	}
+	ifindex1 = ifr.ifr_ifindex;
+	if (ioctl(s1, SIOCGIFHWADDR, &ifr) < 0) {
+		perror("ioctl[SIOCGIFHWADDR]");
+		goto fail;
+	}
+	memcpy(addr1, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, d_ifname, sizeof(ifr.ifr_name));
+	if (ioctl(s2, SIOCGIFINDEX, &ifr) < 0) {
+		perror("ioctl[SIOCGIFINDEX]");
+		goto fail;
+	}
+	ifindex2 = ifr.ifr_ifindex;
+	if (ioctl(s2, SIOCGIFHWADDR, &ifr) < 0) {
+		perror("ioctl[SIOCGIFHWADDR]");
+		goto fail;
+	}
+	memcpy(addr2, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+	memset(&ll, 0, sizeof(ll));
+	ll.sll_family = PF_PACKET;
+	ll.sll_ifindex = ifindex1;
+	ll.sll_protocol = htons(HWSIM_ETHERTYPE);
+	if (bind(s1, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+		perror("bind");
+		goto fail;
+	}
+
+	memset(&ll, 0, sizeof(ll));
+	ll.sll_family = PF_PACKET;
+	ll.sll_ifindex = ifindex2;
+	ll.sll_protocol = htons(HWSIM_ETHERTYPE);
+	if (bind(s2, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+		perror("bind");
+		goto fail;
+	}
+
+	tx(s1, s_ifname, ifindex1, addr1, addr2, tos);
+	tx(s1, s_ifname, ifindex1, addr1, bcast, tos);
+	tx(s2, d_ifname, ifindex2, addr2, addr1, tos);
+	tx(s2, d_ifname, ifindex2, addr2, bcast, tos);
+
+	tv.tv_sec = 1;
+	tv.tv_usec = 0;
+
+	memset(&res, 0, sizeof(res));
+	for (;;) {
+		int r;
+		FD_ZERO(&rfds);
+		FD_SET(s1, &rfds);
+		FD_SET(s2, &rfds);
+
+		r = select(s2 + 1, &rfds, NULL, NULL, &tv);
+		if (r < 0) {
+			perror("select");
+			goto fail;
+		}
+
+		if (r == 0)
+			break; /* timeout */
+
+		if (FD_ISSET(s1, &rfds))
+			rx(s1, 1, s_ifname, ifindex1, &res);
+		if (FD_ISSET(s2, &rfds))
+			rx(s2, 2, d_ifname, ifindex2, &res);
+
+		if (res.rx_unicast1 && res.rx_broadcast1 &&
+		    res.rx_unicast2 && res.rx_broadcast2) {
+			ret = 0;
+			break;
+		}
+	}
+
+	if (ret) {
+		printf("Did not receive all expected frames:\n"
+		       "rx_unicast1=%u rx_broadcast1=%u "
+		       "rx_unicast2=%u rx_broadcast2=%u\n",
+		       res.rx_unicast1, res.rx_broadcast1,
+		       res.rx_unicast2, res.rx_broadcast2);
+	} else {
+		printf("Both unicast and broadcast working in both "
+		       "directions\n");
+	}
+
+fail:
+	close(s1);
+	close(s2);
+
+	return ret;
+}
diff --git a/hostap/patches/openssl-0.9.8za-tls-extensions.patch b/hostap/patches/openssl-0.9.8za-tls-extensions.patch
new file mode 100644
index 0000000..82bfe23
--- /dev/null
+++ b/hostap/patches/openssl-0.9.8za-tls-extensions.patch
@@ -0,0 +1,397 @@
+This patch adds support for TLS SessionTicket extension (RFC 5077) for
+the parts used by EAP-FAST (RFC 4851).
+
+This is based on the patch from Alexey Kobozev <akobozev@cisco.com>
+(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300).
+
+OpenSSL 0.9.8za does not enable TLS extension support by default, so it
+will need to be enabled by adding enable-tlsext to config script
+command line.
+
+
+diff -upr openssl-0.9.8za.orig/ssl/s3_clnt.c openssl-0.9.8za/ssl/s3_clnt.c
+--- openssl-0.9.8za.orig/ssl/s3_clnt.c	2014-06-05 11:09:26.000000000 +0300
++++ openssl-0.9.8za/ssl/s3_clnt.c	2014-06-05 20:37:09.221387312 +0300
+@@ -767,6 +767,22 @@ int ssl3_get_server_hello(SSL *s)
+ 		goto f_err;
+ 		}
+ 
++#ifndef OPENSSL_NO_TLSEXT
++	/* check if we want to resume the session based on external pre-shared secret */
++	if (s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++		{
++		SSL_CIPHER *pref_cipher=NULL;
++		s->session->master_key_length=sizeof(s->session->master_key);
++		if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length,
++			NULL, &pref_cipher, s->tls_session_secret_cb_arg))
++			{
++			s->session->cipher=pref_cipher ?
++				pref_cipher : ssl_get_cipher_by_char(s,p+j);
++			s->s3->flags |= SSL3_FLAGS_CCS_OK;
++			}
++		}
++#endif /* OPENSSL_NO_TLSEXT */
++
+ 	if (j != 0 && j == s->session->session_id_length
+ 	    && memcmp(p,s->session->session_id,j) == 0)
+ 	    {
+@@ -2745,11 +2760,8 @@ int ssl3_check_finished(SSL *s)
+ 	{
+ 	int ok;
+ 	long n;
+-	/* If we have no ticket or session ID is non-zero length (a match of
+-	 * a non-zero session length would never reach here) it cannot be a
+-	 * resumed session.
+-	 */
+-	if (!s->session->tlsext_tick || s->session->session_id_length)
++	/* If we have no ticket it cannot be a resumed session. */
++	if (!s->session->tlsext_tick)
+ 		return 1;
+ 	/* this function is called when we really expect a Certificate
+ 	 * message, so permit appropriate message length */
+diff -upr openssl-0.9.8za.orig/ssl/s3_srvr.c openssl-0.9.8za/ssl/s3_srvr.c
+--- openssl-0.9.8za.orig/ssl/s3_srvr.c	2014-06-05 11:09:26.000000000 +0300
++++ openssl-0.9.8za/ssl/s3_srvr.c	2014-06-05 20:37:09.225387312 +0300
+@@ -1011,6 +1011,59 @@ int ssl3_get_client_hello(SSL *s)
+ 			SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT);
+ 			goto err;
+ 		}
++
++	/* Check if we want to use external pre-shared secret for this
++	 * handshake for not reused session only. We need to generate
++	 * server_random before calling tls_session_secret_cb in order to allow
++	 * SessionTicket processing to use it in key derivation. */
++	{
++		unsigned long Time;
++		unsigned char *pos;
++		Time=(unsigned long)time(NULL);			/* Time */
++		pos=s->s3->server_random;
++		l2n(Time,pos);
++		if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0)
++			{
++			al=SSL_AD_INTERNAL_ERROR;
++			goto f_err;
++			}
++	}
++
++	if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++		{
++		SSL_CIPHER *pref_cipher=NULL;
++
++		s->session->master_key_length=sizeof(s->session->master_key);
++		if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, 
++			ciphers, &pref_cipher, s->tls_session_secret_cb_arg))
++			{
++			s->hit=1;
++			s->session->ciphers=ciphers;
++			s->session->verify_result=X509_V_OK;
++			
++			ciphers=NULL;
++			
++			/* check if some cipher was preferred by call back */
++			pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s));
++			if (pref_cipher == NULL)
++				{
++				al=SSL_AD_HANDSHAKE_FAILURE;
++				SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER);
++				goto f_err;
++				}
++
++			s->session->cipher=pref_cipher;
++
++			if (s->cipher_list)
++				sk_SSL_CIPHER_free(s->cipher_list);
++
++			if (s->cipher_list_by_id)
++				sk_SSL_CIPHER_free(s->cipher_list_by_id);
++
++			s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers);
++			s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers);
++			}
++		}
+ #endif
+ 	/* Worst case, we will use the NULL compression, but if we have other
+ 	 * options, we will now look for them.  We have i-1 compression
+@@ -1161,16 +1214,22 @@ int ssl3_send_server_hello(SSL *s)
+ 	unsigned char *buf;
+ 	unsigned char *p,*d;
+ 	int i,sl;
+-	unsigned long l,Time;
++	unsigned long l;
++#ifdef OPENSSL_NO_TLSEXT
++	unsigned long Time;
++#endif
+ 
+ 	if (s->state == SSL3_ST_SW_SRVR_HELLO_A)
+ 		{
+ 		buf=(unsigned char *)s->init_buf->data;
++#ifdef OPENSSL_NO_TLSEXT
+ 		p=s->s3->server_random;
++		/* Generate server_random if it was not needed previously */
+ 		Time=(unsigned long)time(NULL);			/* Time */
+ 		l2n(Time,p);
+ 		if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0)
+ 			return -1;
++#endif
+ 		/* Do the message type and length last */
+ 		d=p= &(buf[4]);
+ 
+diff -upr openssl-0.9.8za.orig/ssl/ssl_err.c openssl-0.9.8za/ssl/ssl_err.c
+--- openssl-0.9.8za.orig/ssl/ssl_err.c	2014-06-05 11:09:08.000000000 +0300
++++ openssl-0.9.8za/ssl/ssl_err.c	2014-06-05 20:37:09.225387312 +0300
+@@ -265,6 +265,7 @@ static ERR_STRING_DATA SSL_str_functs[]=
+ {ERR_FUNC(SSL_F_TLS1_ENC),	"TLS1_ENC"},
+ {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK),	"TLS1_SETUP_KEY_BLOCK"},
+ {ERR_FUNC(SSL_F_WRITE_PENDING),	"WRITE_PENDING"},
++{ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"},
+ {0,NULL}
+ 	};
+ 
+diff -upr openssl-0.9.8za.orig/ssl/ssl.h openssl-0.9.8za/ssl/ssl.h
+--- openssl-0.9.8za.orig/ssl/ssl.h	2014-06-05 11:09:08.000000000 +0300
++++ openssl-0.9.8za/ssl/ssl.h	2014-06-05 20:37:09.229387312 +0300
+@@ -344,6 +344,7 @@ extern "C" {
+  * 'struct ssl_st *' function parameters used to prototype callbacks
+  * in SSL_CTX. */
+ typedef struct ssl_st *ssl_crock_st;
++typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT;
+ 
+ /* used to hold info on the particular ciphers used */
+ typedef struct ssl_cipher_st
+@@ -362,6 +363,9 @@ typedef struct ssl_cipher_st
+ 
+ DECLARE_STACK_OF(SSL_CIPHER)
+ 
++typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg);
++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
++
+ /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
+ typedef struct ssl_method_st
+ 	{
+@@ -1053,6 +1057,18 @@ struct ssl_st
+ 
+ 	/* RFC4507 session ticket expected to be received or sent */
+ 	int tlsext_ticket_expected;
++
++	/* TLS Session Ticket extension override */
++	TLS_SESSION_TICKET_EXT *tlsext_session_ticket;
++
++	/* TLS Session Ticket extension callback */
++	tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb;
++	void *tls_session_ticket_ext_cb_arg;
++
++	/* TLS pre-shared secret session resumption */
++	tls_session_secret_cb_fn tls_session_secret_cb;
++	void *tls_session_secret_cb_arg;
++
+ 	SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */
+ #define session_ctx initial_ctx
+ #else
+@@ -1668,6 +1684,15 @@ void *SSL_COMP_get_compression_methods(v
+ int SSL_COMP_add_compression_method(int id,void *cm);
+ #endif
+ 
++/* TLS extensions functions */
++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len);
++
++int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
++				  void *arg);
++
++/* Pre-shared secret session resumption functions */
++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg);
++
+ /* BEGIN ERROR CODES */
+ /* The following lines are auto generated by the script mkerr.pl. Any changes
+  * made after this point may be overwritten when the script is next run.
+@@ -1872,6 +1897,7 @@ void ERR_load_SSL_strings(void);
+ #define SSL_F_TLS1_ENC					 210
+ #define SSL_F_TLS1_SETUP_KEY_BLOCK			 211
+ #define SSL_F_WRITE_PENDING				 212
++#define SSL_F_SSL_SET_SESSION_TICKET_EXT		 213
+ 
+ /* Reason codes. */
+ #define SSL_R_APP_DATA_IN_HANDSHAKE			 100
+diff -upr openssl-0.9.8za.orig/ssl/ssl_sess.c openssl-0.9.8za/ssl/ssl_sess.c
+--- openssl-0.9.8za.orig/ssl/ssl_sess.c	2014-06-05 11:09:08.000000000 +0300
++++ openssl-0.9.8za/ssl/ssl_sess.c	2014-06-05 20:37:09.229387312 +0300
+@@ -712,6 +712,61 @@ long SSL_CTX_get_timeout(const SSL_CTX *
+ 	return(s->session_timeout);
+ 	}
+ 
++#ifndef OPENSSL_NO_TLSEXT
++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len,
++	STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg)
++	{
++	if (s == NULL) return(0);
++	s->tls_session_secret_cb = tls_session_secret_cb;
++	s->tls_session_secret_cb_arg = arg;
++	return(1);
++	}
++
++int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
++				  void *arg)
++	{
++	if (s == NULL) return(0);
++	s->tls_session_ticket_ext_cb = cb;
++	s->tls_session_ticket_ext_cb_arg = arg;
++	return(1);
++	}
++
++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len)
++	{
++	if (s->version >= TLS1_VERSION)
++		{
++		if (s->tlsext_session_ticket)
++			{
++			OPENSSL_free(s->tlsext_session_ticket);
++			s->tlsext_session_ticket = NULL;
++			}
++
++		s->tlsext_session_ticket = OPENSSL_malloc(sizeof(TLS_SESSION_TICKET_EXT) + ext_len);
++		if (!s->tlsext_session_ticket)
++			{
++			SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE);
++			return 0;
++			}
++
++		if (ext_data)
++			{
++			s->tlsext_session_ticket->length = ext_len;
++			s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1;
++			memcpy(s->tlsext_session_ticket->data, ext_data, ext_len);
++			}
++		else
++			{
++			s->tlsext_session_ticket->length = 0;
++			s->tlsext_session_ticket->data = NULL;
++			}
++
++		return 1;
++		}
++
++	return 0;
++	}
++#endif /* OPENSSL_NO_TLSEXT */
++
+ typedef struct timeout_param_st
+ 	{
+ 	SSL_CTX *ctx;
+diff -upr openssl-0.9.8za.orig/ssl/t1_lib.c openssl-0.9.8za/ssl/t1_lib.c
+--- openssl-0.9.8za.orig/ssl/t1_lib.c	2014-06-05 11:09:08.000000000 +0300
++++ openssl-0.9.8za/ssl/t1_lib.c	2014-06-05 20:37:09.229387312 +0300
+@@ -106,6 +106,12 @@ int tls1_new(SSL *s)
+ 
+ void tls1_free(SSL *s)
+ 	{
++#ifndef OPENSSL_NO_TLSEXT
++	if (s->tlsext_session_ticket)
++		{
++		OPENSSL_free(s->tlsext_session_ticket);
++		}
++#endif
+ 	ssl3_free(s);
+ 	}
+ 
+@@ -206,8 +212,23 @@ unsigned char *ssl_add_clienthello_tlsex
+ 		int ticklen;
+ 		if (!s->new_session && s->session && s->session->tlsext_tick)
+ 			ticklen = s->session->tlsext_ticklen;
++		else if (s->session && s->tlsext_session_ticket &&
++			 s->tlsext_session_ticket->data)
++			{
++			ticklen = s->tlsext_session_ticket->length;
++			s->session->tlsext_tick = OPENSSL_malloc(ticklen);
++			if (!s->session->tlsext_tick)
++				return NULL;
++			memcpy(s->session->tlsext_tick,
++			       s->tlsext_session_ticket->data,
++			       ticklen);
++			s->session->tlsext_ticklen = ticklen;
++			}
+ 		else
+ 			ticklen = 0;
++		if (ticklen == 0 && s->tlsext_session_ticket &&
++		    s->tlsext_session_ticket->data == NULL)
++			goto skip_ext;
+ 		/* Check for enough room 2 for extension type, 2 for len
+  		 * rest for ticket
+   		 */
+@@ -221,6 +242,7 @@ unsigned char *ssl_add_clienthello_tlsex
+ 			ret += ticklen;
+ 			}
+ 		}
++		skip_ext:
+ 
+ 	if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp &&
+ 	    s->version != DTLS1_VERSION)
+@@ -574,6 +596,15 @@ int ssl_parse_clienthello_tlsext(SSL *s,
+ 				return 0;
+ 			renegotiate_seen = 1;
+ 			}
++		else if (type == TLSEXT_TYPE_session_ticket)
++			{
++			if (s->tls_session_ticket_ext_cb &&
++			    !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg))
++				{
++				*al = TLS1_AD_INTERNAL_ERROR;
++				return 0;
++				}
++			}
+ 		else if (type == TLSEXT_TYPE_status_request &&
+ 		         s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb)
+ 			{
+@@ -751,6 +782,12 @@ int ssl_parse_serverhello_tlsext(SSL *s,
+ 			}
+ 		else if (type == TLSEXT_TYPE_session_ticket)
+ 			{
++			if (s->tls_session_ticket_ext_cb &&
++			    !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg))
++				{
++				*al = TLS1_AD_INTERNAL_ERROR;
++				return 0;
++				}
+ 			if ((SSL_get_options(s) & SSL_OP_NO_TICKET)
+ 				|| (size > 0))
+ 				{
+@@ -1043,6 +1080,15 @@ int tls1_process_ticket(SSL *s, unsigned
+ 				s->tlsext_ticket_expected = 1;
+ 				return 0;	/* Cache miss */
+ 				}
++			if (s->tls_session_secret_cb)
++				{
++				/* Indicate cache miss here and instead of
++				 * generating the session from ticket now,
++				 * trigger abbreviated handshake based on
++				 * external mechanism to calculate the master
++				 * secret later. */
++				return 0;
++				}
+ 			return tls_decrypt_ticket(s, p, size, session_id, len,
+ 									ret);
+ 			}
+diff -upr openssl-0.9.8za.orig/ssl/tls1.h openssl-0.9.8za/ssl/tls1.h
+--- openssl-0.9.8za.orig/ssl/tls1.h	2014-06-05 11:09:08.000000000 +0300
++++ openssl-0.9.8za/ssl/tls1.h	2014-06-05 20:37:09.229387312 +0300
+@@ -415,6 +415,13 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T
+ #define TLS_MD_MASTER_SECRET_CONST    "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74"  /*master secret*/
+ #endif
+ 
++/* TLS extension struct */
++struct tls_session_ticket_ext_st
++	{
++	unsigned short length;
++	void *data;
++	};
++
+ #ifdef  __cplusplus
+ }
+ #endif
+diff -upr openssl-0.9.8za.orig/util/ssleay.num openssl-0.9.8za/util/ssleay.num
+--- openssl-0.9.8za.orig/util/ssleay.num	2014-06-05 12:38:45.000000000 +0300
++++ openssl-0.9.8za/util/ssleay.num	2014-06-05 20:37:09.229387312 +0300
+@@ -242,3 +242,5 @@ SSL_set_SSL_CTX
+ SSL_get_servername                      291	EXIST::FUNCTION:TLSEXT
+ SSL_get_servername_type                 292	EXIST::FUNCTION:TLSEXT
+ SSL_CTX_set_client_cert_engine          293	EXIST::FUNCTION:ENGINE
++SSL_set_session_ticket_ext		306	EXIST::FUNCTION:TLSEXT
++SSL_set_session_secret_cb		307	EXIST::FUNCTION:TLSEXT
diff --git a/hostap/patches/openssl-0.9.8zf-tls-extensions.patch b/hostap/patches/openssl-0.9.8zf-tls-extensions.patch
new file mode 100644
index 0000000..3a8f90e
--- /dev/null
+++ b/hostap/patches/openssl-0.9.8zf-tls-extensions.patch
@@ -0,0 +1,398 @@
+This patch adds support for TLS SessionTicket extension (RFC 5077) for
+the parts used by EAP-FAST (RFC 4851).
+
+This is based on the patch from Alexey Kobozev <akobozev@cisco.com>
+(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300).
+
+OpenSSL 0.9.8zf does not enable TLS extension support by default, so it
+will need to be enabled by adding enable-tlsext to config script
+command line.
+
+
+diff -upr openssl-0.9.8zf.orig/ssl/s3_clnt.c openssl-0.9.8zf/ssl/s3_clnt.c
+--- openssl-0.9.8zf.orig/ssl/s3_clnt.c	2015-03-19 15:46:46.000000000 +0200
++++ openssl-0.9.8zf/ssl/s3_clnt.c	2015-03-24 16:19:14.043911769 +0200
+@@ -760,6 +760,23 @@ int ssl3_get_server_hello(SSL *s)
+         goto f_err;
+     }
+ 
++#ifndef OPENSSL_NO_TLSEXT
++    /* check if we want to resume the session based on external pre-shared secret */
++    if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) {
++        SSL_CIPHER *pref_cipher = NULL;
++
++        s->session->master_key_length = sizeof(s->session->master_key);
++        if (s->tls_session_secret_cb(s, s->session->master_key,
++				     &s->session->master_key_length,
++				     NULL, &pref_cipher,
++				     s->tls_session_secret_cb_arg)) {
++            s->session->cipher = pref_cipher ?
++		    pref_cipher : ssl_get_cipher_by_char(s, p + j);
++	    s->s3->flags |= SSL3_FLAGS_CCS_OK;
++	}
++    }
++#endif /* OPENSSL_NO_TLSEXT */
++
+     if (j != 0 && j == s->session->session_id_length
+         && memcmp(p, s->session->session_id, j) == 0) {
+         if (s->sid_ctx_length != s->session->sid_ctx_length
+@@ -2684,12 +2701,8 @@ int ssl3_check_finished(SSL *s)
+ {
+     int ok;
+     long n;
+-    /*
+-     * If we have no ticket or session ID is non-zero length (a match of a
+-     * non-zero session length would never reach here) it cannot be a resumed
+-     * session.
+-     */
+-    if (!s->session->tlsext_tick || s->session->session_id_length)
++    /* If we have no ticket it cannot be a resumed session. */
++    if (!s->session->tlsext_tick)
+         return 1;
+     /*
+      * this function is called when we really expect a Certificate message,
+diff -upr openssl-0.9.8zf.orig/ssl/s3_srvr.c openssl-0.9.8zf/ssl/s3_srvr.c
+--- openssl-0.9.8zf.orig/ssl/s3_srvr.c	2015-03-19 15:46:46.000000000 +0200
++++ openssl-0.9.8zf/ssl/s3_srvr.c	2015-03-24 16:23:34.567909681 +0200
+@@ -999,6 +999,59 @@ int ssl3_get_client_hello(SSL *s)
+         SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT);
+         goto err;
+     }
++
++    /* Check if we want to use external pre-shared secret for this
++     * handshake for not reused session only. We need to generate
++     * server_random before calling tls_session_secret_cb in order to allow
++     * SessionTicket processing to use it in key derivation. */
++    {
++        unsigned long Time;
++	unsigned char *pos;
++	Time = (unsigned long)time(NULL);			/* Time */
++	pos = s->s3->server_random;
++	l2n(Time, pos);
++	if (RAND_pseudo_bytes(pos, SSL3_RANDOM_SIZE - 4) <= 0) {
++		al = SSL_AD_INTERNAL_ERROR;
++		goto f_err;
++	}
++    }
++
++    if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) {
++	SSL_CIPHER *pref_cipher = NULL;
++
++	s->session->master_key_length = sizeof(s->session->master_key);
++	if (s->tls_session_secret_cb(s, s->session->master_key,
++				     &s->session->master_key_length, 
++				     ciphers, &pref_cipher,
++				     s->tls_session_secret_cb_arg)) {
++	    s->hit = 1;
++	    s->session->ciphers = ciphers;
++	    s->session->verify_result = X509_V_OK;
++
++	    ciphers = NULL;
++
++	    /* check if some cipher was preferred by call back */
++	    pref_cipher = pref_cipher ? pref_cipher :
++		    ssl3_choose_cipher(s, s->session->ciphers,
++				       SSL_get_ciphers(s));
++	    if (pref_cipher == NULL) {
++		al = SSL_AD_HANDSHAKE_FAILURE;
++		SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_NO_SHARED_CIPHER);
++		goto f_err;
++	    }
++
++	    s->session->cipher = pref_cipher;
++
++	    if (s->cipher_list)
++		sk_SSL_CIPHER_free(s->cipher_list);
++
++	    if (s->cipher_list_by_id)
++		sk_SSL_CIPHER_free(s->cipher_list_by_id);
++
++	    s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers);
++	    s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers);
++	}
++    }
+ #endif
+     /*
+      * Worst case, we will use the NULL compression, but if we have other
+@@ -1143,15 +1196,21 @@ int ssl3_send_server_hello(SSL *s)
+     unsigned char *buf;
+     unsigned char *p, *d;
+     int i, sl;
+-    unsigned long l, Time;
++    unsigned long l;
++#ifdef OPENSSL_NO_TLSEXT
++    unsigned long Time;
++#endif
+ 
+     if (s->state == SSL3_ST_SW_SRVR_HELLO_A) {
+         buf = (unsigned char *)s->init_buf->data;
++#ifdef OPENSSL_NO_TLSEXT
+         p = s->s3->server_random;
++        /* Generate server_random if it was not needed previously */
+         Time = (unsigned long)time(NULL); /* Time */
+         l2n(Time, p);
+         if (RAND_pseudo_bytes(p, SSL3_RANDOM_SIZE - 4) <= 0)
+             return -1;
++#endif
+         /* Do the message type and length last */
+         d = p = &(buf[4]);
+ 
+diff -upr openssl-0.9.8zf.orig/ssl/ssl_err.c openssl-0.9.8zf/ssl/ssl_err.c
+--- openssl-0.9.8zf.orig/ssl/ssl_err.c	2015-03-19 15:46:46.000000000 +0200
++++ openssl-0.9.8zf/ssl/ssl_err.c	2015-03-24 16:35:58.627903717 +0200
+@@ -316,6 +316,7 @@ static ERR_STRING_DATA SSL_str_functs[]
+     {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"},
+     {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"},
+     {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"},
++    {ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"},
+     {0, NULL}
+ };
+ 
+diff -upr openssl-0.9.8zf.orig/ssl/ssl.h openssl-0.9.8zf/ssl/ssl.h
+--- openssl-0.9.8zf.orig/ssl/ssl.h	2015-03-19 15:46:46.000000000 +0200
++++ openssl-0.9.8zf/ssl/ssl.h	2015-03-24 16:25:44.339908641 +0200
+@@ -349,6 +349,7 @@ extern "C" {
+  * function parameters used to prototype callbacks in SSL_CTX.
+  */
+ typedef struct ssl_st *ssl_crock_st;
++typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT;
+ 
+ /* used to hold info on the particular ciphers used */
+ typedef struct ssl_cipher_st {
+@@ -366,6 +367,12 @@ typedef struct ssl_cipher_st {
+ 
+ DECLARE_STACK_OF(SSL_CIPHER)
+ 
++typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data,
++					    int len, void *arg);
++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len,
++					STACK_OF(SSL_CIPHER) *peer_ciphers,
++					SSL_CIPHER **cipher, void *arg);
++
+ /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
+ typedef struct ssl_method_st {
+     int version;
+@@ -1116,6 +1123,18 @@ struct ssl_st {
+     int tlsext_ocsp_resplen;
+     /* RFC4507 session ticket expected to be received or sent */
+     int tlsext_ticket_expected;
++
++    /* TLS Session Ticket extension override */
++    TLS_SESSION_TICKET_EXT *tlsext_session_ticket;
++
++    /* TLS Session Ticket extension callback */
++    tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb;
++    void *tls_session_ticket_ext_cb_arg;
++
++    /* TLS pre-shared secret session resumption */
++    tls_session_secret_cb_fn tls_session_secret_cb;
++    void *tls_session_secret_cb_arg;
++
+     SSL_CTX *initial_ctx;       /* initial ctx, used to store sessions */
+ #  define session_ctx initial_ctx
+ # else
+@@ -1772,6 +1791,17 @@ void *SSL_COMP_get_compression_methods(v
+ int SSL_COMP_add_compression_method(int id, void *cm);
+ # endif
+ 
++/* TLS extensions functions */
++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len);
++
++int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
++				  void *arg);
++
++/* Pre-shared secret session resumption functions */
++int SSL_set_session_secret_cb(SSL *s,
++			      tls_session_secret_cb_fn tls_session_secret_cb,
++			      void *arg);
++
+ /* BEGIN ERROR CODES */
+ /*
+  * The following lines are auto generated by the script mkerr.pl. Any changes
+@@ -1977,6 +2007,7 @@ void ERR_load_SSL_strings(void);
+ # define SSL_F_TLS1_ENC                                   210
+ # define SSL_F_TLS1_SETUP_KEY_BLOCK                       211
+ # define SSL_F_WRITE_PENDING                              212
++#define SSL_F_SSL_SET_SESSION_TICKET_EXT                  213
+ 
+ /* Reason codes. */
+ # define SSL_R_APP_DATA_IN_HANDSHAKE                      100
+diff -upr openssl-0.9.8zf.orig/ssl/ssl_sess.c openssl-0.9.8zf/ssl/ssl_sess.c
+--- openssl-0.9.8zf.orig/ssl/ssl_sess.c	2015-03-19 15:46:46.000000000 +0200
++++ openssl-0.9.8zf/ssl/ssl_sess.c	2015-03-24 16:28:04.819907515 +0200
+@@ -716,6 +716,61 @@ long SSL_CTX_get_timeout(const SSL_CTX *
+     return (s->session_timeout);
+ }
+ 
++#ifndef OPENSSL_NO_TLSEXT
++int SSL_set_session_secret_cb(
++	SSL *s,
++	int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len,
++				     STACK_OF(SSL_CIPHER) *peer_ciphers,
++				     SSL_CIPHER **cipher, void *arg), void *arg)
++{
++    if (s == NULL)
++	    return 0;
++    s->tls_session_secret_cb = tls_session_secret_cb;
++    s->tls_session_secret_cb_arg = arg;
++    return 1;
++}
++
++int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
++				  void *arg)
++{
++    if (s == NULL)
++	    return 0;
++    s->tls_session_ticket_ext_cb = cb;
++    s->tls_session_ticket_ext_cb_arg = arg;
++    return 1;
++}
++
++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len)
++{
++    if (s->version >= TLS1_VERSION) {
++	if (s->tlsext_session_ticket) {
++	    OPENSSL_free(s->tlsext_session_ticket);
++	    s->tlsext_session_ticket = NULL;
++	}
++
++	s->tlsext_session_ticket = OPENSSL_malloc(
++		sizeof(TLS_SESSION_TICKET_EXT) + ext_len);
++	if (!s->tlsext_session_ticket) {
++	    SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE);
++	    return 0;
++	}
++
++	if (ext_data) {
++	    s->tlsext_session_ticket->length = ext_len;
++	    s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1;
++	    memcpy(s->tlsext_session_ticket->data, ext_data, ext_len);
++	} else {
++		s->tlsext_session_ticket->length = 0;
++		s->tlsext_session_ticket->data = NULL;
++	}
++
++	return 1;
++    }
++
++    return 0;
++}
++#endif /* OPENSSL_NO_TLSEXT */
++
+ typedef struct timeout_param_st {
+     SSL_CTX *ctx;
+     long time;
+diff -upr openssl-0.9.8zf.orig/ssl/t1_lib.c openssl-0.9.8zf/ssl/t1_lib.c
+--- openssl-0.9.8zf.orig/ssl/t1_lib.c	2015-03-19 15:46:46.000000000 +0200
++++ openssl-0.9.8zf/ssl/t1_lib.c	2015-03-24 16:32:46.923905254 +0200
+@@ -108,6 +108,11 @@ int tls1_new(SSL *s)
+ 
+ void tls1_free(SSL *s)
+ {
++#ifndef OPENSSL_NO_TLSEXT
++    if (s->tlsext_session_ticket) {
++	OPENSSL_free(s->tlsext_session_ticket);
++    }
++#endif
+     ssl3_free(s);
+ }
+ 
+@@ -206,8 +211,20 @@ unsigned char *ssl_add_clienthello_tlsex
+         int ticklen;
+         if (!s->new_session && s->session && s->session->tlsext_tick)
+             ticklen = s->session->tlsext_ticklen;
+-        else
++	else if (s->session && s->tlsext_session_ticket &&
++		 s->tlsext_session_ticket->data) {
++	    ticklen = s->tlsext_session_ticket->length;
++	    s->session->tlsext_tick = OPENSSL_malloc(ticklen);
++	    if (!s->session->tlsext_tick)
++		return NULL;
++	    memcpy(s->session->tlsext_tick, s->tlsext_session_ticket->data,
++		   ticklen);
++	    s->session->tlsext_ticklen = ticklen;
++	} else
+             ticklen = 0;
++	if (ticklen == 0 && s->tlsext_session_ticket &&
++	    s->tlsext_session_ticket->data == NULL)
++	    goto skip_ext;
+         /*
+          * Check for enough room 2 for extension type, 2 for len rest for
+          * ticket
+@@ -221,6 +238,7 @@ unsigned char *ssl_add_clienthello_tlsex
+             ret += ticklen;
+         }
+     }
++skip_ext:
+ 
+     if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp &&
+         s->version != DTLS1_VERSION) {
+@@ -560,6 +578,14 @@ int ssl_parse_clienthello_tlsext(SSL *s,
+             if (!ssl_parse_clienthello_renegotiate_ext(s, data, size, al))
+                 return 0;
+             renegotiate_seen = 1;
++	} else if (type == TLSEXT_TYPE_session_ticket) {
++	    if (s->tls_session_ticket_ext_cb &&
++		!s->tls_session_ticket_ext_cb(s, data, size,
++					      s->tls_session_ticket_ext_cb_arg))
++	    {
++		*al = TLS1_AD_INTERNAL_ERROR;
++		return 0;
++	    }
+         } else if (type == TLSEXT_TYPE_status_request &&
+                    s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb) {
+ 
+@@ -710,6 +736,13 @@ int ssl_parse_serverhello_tlsext(SSL *s,
+             }
+             tlsext_servername = 1;
+         } else if (type == TLSEXT_TYPE_session_ticket) {
++	    if (s->tls_session_ticket_ext_cb &&
++		!s->tls_session_ticket_ext_cb(
++			s, data, size,
++			s->tls_session_ticket_ext_cb_arg)) {
++		*al = TLS1_AD_INTERNAL_ERROR;
++		return 0;
++	    }
+             if ((SSL_get_options(s) & SSL_OP_NO_TICKET)
+                 || (size > 0)) {
+                 *al = TLS1_AD_UNSUPPORTED_EXTENSION;
+@@ -993,6 +1026,14 @@ int tls1_process_ticket(SSL *s, unsigned
+                 s->tlsext_ticket_expected = 1;
+                 return 0;       /* Cache miss */
+             }
++	    if (s->tls_session_secret_cb) {
++		/* Indicate cache miss here and instead of
++		 * generating the session from ticket now,
++		 * trigger abbreviated handshake based on
++		 * external mechanism to calculate the master
++		 * secret later. */
++		return 0;
++	    }
+             return tls_decrypt_ticket(s, p, size, session_id, len, ret);
+         }
+         p += size;
+diff -upr openssl-0.9.8zf.orig/ssl/tls1.h openssl-0.9.8zf/ssl/tls1.h
+--- openssl-0.9.8zf.orig/ssl/tls1.h	2015-03-19 15:46:46.000000000 +0200
++++ openssl-0.9.8zf/ssl/tls1.h	2015-03-24 16:33:31.855904894 +0200
+@@ -460,6 +460,12 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T
+ #  define TLS_MD_MASTER_SECRET_CONST    "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74"
+ # endif
+ 
++/* TLS extension struct */
++struct tls_session_ticket_ext_st {
++    unsigned short length;
++    void *data;
++};
++
+ #ifdef  __cplusplus
+ }
+ #endif
+diff -upr openssl-0.9.8zf.orig/util/ssleay.num openssl-0.9.8zf/util/ssleay.num
+--- openssl-0.9.8zf.orig/util/ssleay.num	2015-03-19 15:47:15.000000000 +0200
++++ openssl-0.9.8zf/util/ssleay.num	2015-03-24 16:33:51.127904739 +0200
+@@ -242,3 +242,5 @@ SSL_set_SSL_CTX
+ SSL_get_servername                      291	EXIST::FUNCTION:TLSEXT
+ SSL_get_servername_type                 292	EXIST::FUNCTION:TLSEXT
+ SSL_CTX_set_client_cert_engine          293	EXIST::FUNCTION:ENGINE
++SSL_set_session_ticket_ext		306	EXIST::FUNCTION:TLSEXT
++SSL_set_session_secret_cb		307	EXIST::FUNCTION:TLSEXT
diff --git a/hostap/radius_example/Makefile b/hostap/radius_example/Makefile
new file mode 100644
index 0000000..883e2f2
--- /dev/null
+++ b/hostap/radius_example/Makefile
@@ -0,0 +1,46 @@
+ALL=radius_example
+
+all: $(ALL)
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+CFLAGS += -I.
+CFLAGS += -I../src
+CFLAGS += -I../src/utils
+
+LIBS = ../src/radius/libradius.a
+LIBS += ../src/crypto/libcrypto.a
+LIBS += ../src/utils/libutils.a
+LLIBS = -lrt
+
+../src/utils/libutils.a:
+	$(MAKE) -C ../src/utils
+
+../src/crypto/libcrypto.a:
+	$(MAKE) -C ../src/crypto
+
+../src/radius/libradius.a:
+	$(MAKE) -C ../src/radius
+
+#CLAGS += -DCONFIG_IPV6
+
+OBJS_ex = radius_example.o
+
+radius_example: $(OBJS_ex) $(LIBS)
+	$(LDO) $(LDFLAGS) -o radius_example $(OBJS_ex) $(LIBS) $(LLIBS)
+
+clean:
+	$(MAKE) -C ../src clean
+	rm -f core *~ *.o *.d $(ALL)
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/radius_example/README b/hostap/radius_example/README
new file mode 100644
index 0000000..ec458e3
--- /dev/null
+++ b/hostap/radius_example/README
@@ -0,0 +1,35 @@
+Example application using RADIUS client as a library
+Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+
+This software may be distributed under the terms of the BSD license.
+See the parent directory README for more details.
+
+
+This directory contains an example showing how the RADIUS client
+functionality from hostapd can be used as a library in another
+program. The example program initializes the RADIUS client and send a
+Access-Request using User-Name and User-Password attributes. A reply
+from the RADIUS authentication server will be processed and it is used
+as a trigger to terminate the example program.
+
+The RADIUS library links in couple of helper functions from src/utils and
+src/crypto directories. Most of these are suitable as-is, but it may
+be desirable to replace the debug output code in src/utils/wpa_debug.c
+by dropping this file from the library and re-implementing the
+functions there in a way that better fits in with the main
+application.
+
+RADIUS client implementation takes care of receiving messages,
+timeouts, and retransmissions of packets. Consequently, it requires
+functionality for registering timeouts and received packet
+notifications. This is implemented using the generic event loop
+implementation (see src/utils/eloop.h).
+
+The main application may either use the included event loop
+implementation or alternatively, implement eloop_* wrapper functions
+to use whatever event loop design is used in the main program. This
+would involve removing src/utils/eloop.o from the library and
+implementing following functions defines in src/utils/eloop.h:
+eloop_register_timeout(), eloop_cancel_timeout(),
+eloop_register_read_sock(), eloop_unregister_read_sock(), and
+eloop_terminated().
diff --git a/hostap/radius_example/radius_example.c b/hostap/radius_example/radius_example.c
new file mode 100644
index 0000000..e4b3678
--- /dev/null
+++ b/hostap/radius_example/radius_example.c
@@ -0,0 +1,153 @@
+/*
+ * Example application using RADIUS client as a library
+ * Copyright (c) 2007, 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 "eloop.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+
+struct radius_ctx {
+	struct radius_client_data *radius;
+	struct hostapd_radius_servers conf;
+	u8 radius_identifier;
+	struct in_addr own_ip_addr;
+};
+
+
+static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
+			      int level, const char *txt, size_t len)
+{
+	printf("%s\n", txt);
+}
+
+
+/* Process the RADIUS frames from Authentication Server */
+static RadiusRxResult receive_auth(struct radius_msg *msg,
+				   struct radius_msg *req,
+				   const u8 *shared_secret,
+				   size_t shared_secret_len,
+				   void *data)
+{
+	/* struct radius_ctx *ctx = data; */
+	printf("Received RADIUS Authentication message; code=%d\n",
+	       radius_msg_get_hdr(msg)->code);
+
+	/* We're done for this example, so request eloop to terminate. */
+	eloop_terminate();
+
+	return RADIUS_RX_PROCESSED;
+}
+
+
+static void start_example(void *eloop_ctx, void *timeout_ctx)
+{
+	struct radius_ctx *ctx = eloop_ctx;
+	struct radius_msg *msg;
+
+	printf("Sending a RADIUS authentication message\n");
+
+	ctx->radius_identifier = radius_client_get_id(ctx->radius);
+	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
+			     ctx->radius_identifier);
+	if (msg == NULL) {
+		printf("Could not create net RADIUS packet\n");
+		return;
+	}
+
+	radius_msg_make_authenticator(msg, (u8 *) ctx, sizeof(*ctx));
+
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
+				 (u8 *) "user", 4)) {
+		printf("Could not add User-Name\n");
+		radius_msg_free(msg);
+		return;
+	}
+
+	if (!radius_msg_add_attr_user_password(
+		    msg, (u8 *) "password", 8,
+		    ctx->conf.auth_server->shared_secret,
+		    ctx->conf.auth_server->shared_secret_len)) {
+		printf("Could not add User-Password\n");
+		radius_msg_free(msg);
+		return;
+	}
+
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+				 (u8 *) &ctx->own_ip_addr, 4)) {
+		printf("Could not add NAS-IP-Address\n");
+		radius_msg_free(msg);
+		return;
+	}
+
+	if (radius_client_send(ctx->radius, msg, RADIUS_AUTH, NULL) < 0)
+		radius_msg_free(msg);
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct radius_ctx ctx;
+	struct hostapd_radius_server *srv;
+
+	if (os_program_init())
+		return -1;
+
+	hostapd_logger_register_cb(hostapd_logger_cb);
+
+	os_memset(&ctx, 0, sizeof(ctx));
+	inet_aton("127.0.0.1", &ctx.own_ip_addr);
+
+	if (eloop_init()) {
+		printf("Failed to initialize event loop\n");
+		return -1;
+	}
+
+	srv = os_zalloc(sizeof(*srv));
+	if (srv == NULL)
+		return -1;
+
+	srv->addr.af = AF_INET;
+	srv->port = 1812;
+	if (hostapd_parse_ip_addr("127.0.0.1", &srv->addr) < 0) {
+		printf("Failed to parse IP address\n");
+		return -1;
+	}
+	srv->shared_secret = (u8 *) os_strdup("radius");
+	srv->shared_secret_len = 6;
+
+	ctx.conf.auth_server = ctx.conf.auth_servers = srv;
+	ctx.conf.num_auth_servers = 1;
+	ctx.conf.msg_dumps = 1;
+
+	ctx.radius = radius_client_init(&ctx, &ctx.conf);
+	if (ctx.radius == NULL) {
+		printf("Failed to initialize RADIUS client\n");
+		return -1;
+	}
+
+	if (radius_client_register(ctx.radius, RADIUS_AUTH, receive_auth,
+				   &ctx) < 0) {
+		printf("Failed to register RADIUS authentication handler\n");
+		return -1;
+	}
+
+	eloop_register_timeout(0, 0, start_example, &ctx, NULL);
+
+	eloop_run();
+
+	radius_client_deinit(ctx.radius);
+	os_free(srv->shared_secret);
+	os_free(srv);
+
+	eloop_destroy();
+	os_program_deinit();
+
+	return 0;
+}
diff --git a/hostap/src/Makefile b/hostap/src/Makefile
new file mode 100644
index 0000000..c9e84c1
--- /dev/null
+++ b/hostap/src/Makefile
@@ -0,0 +1,12 @@
+SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae radius rsn_supp tls utils wps
+SUBDIRS += fst
+
+all:
+	for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
+
+clean:
+	for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done
+	rm -f *~
+
+install:
+	for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d install; done
diff --git a/hostap/src/ap/Makefile b/hostap/src/ap/Makefile
new file mode 100644
index 0000000..98788fe
--- /dev/null
+++ b/hostap/src/ap/Makefile
@@ -0,0 +1,67 @@
+all: libap.a
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libap.a
+
+install:
+	@echo Nothing to be made.
+
+include ../lib.rules
+
+CFLAGS += -DHOSTAPD
+CFLAGS += -DNEED_AP_MLME
+CFLAGS += -DCONFIG_HS20
+CFLAGS += -DCONFIG_INTERWORKING
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211W
+CFLAGS += -DCONFIG_WPS
+CFLAGS += -DCONFIG_PROXYARP
+CFLAGS += -DCONFIG_IAPP
+
+LIB_OBJS= \
+	accounting.o \
+	ap_config.o \
+	ap_drv_ops.o \
+	ap_list.o \
+	ap_mlme.o \
+	authsrv.o \
+	beacon.o \
+	bss_load.o \
+	ctrl_iface_ap.o \
+	dfs.o \
+	dhcp_snoop.o \
+	drv_callbacks.o \
+	eap_user_db.o \
+	gas_serv.o \
+	hostapd.o \
+	hs20.o \
+	hw_features.o \
+	iapp.o \
+	ieee802_11_auth.o \
+	ieee802_11.o \
+	ieee802_11_ht.o \
+	ieee802_11_shared.o \
+	ieee802_11_vht.o \
+	ieee802_1x.o \
+	ndisc_snoop.o \
+	p2p_hostapd.o \
+	peerkey_auth.o \
+	pmksa_cache_auth.o \
+	preauth_auth.o \
+	sta_info.o \
+	tkip_countermeasures.o \
+	utils.o \
+	vlan_init.o \
+	wmm.o \
+	wnm_ap.o \
+	wpa_auth.o \
+	wpa_auth_ft.o \
+	wpa_auth_glue.o \
+	wpa_auth_ie.o \
+	wps_hostapd.o \
+	x_snoop.o
+
+libap.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/ap/accounting.c b/hostap/src/ap/accounting.c
new file mode 100644
index 0000000..a096de4
--- /dev/null
+++ b/hostap/src/ap/accounting.c
@@ -0,0 +1,488 @@
+/*
+ * hostapd / RADIUS Accounting
+ * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "accounting.h"
+
+
+/* Default interval in seconds for polling TX/RX octets from the driver if
+ * STA is not using interim accounting. This detects wrap arounds for
+ * input/output octets and updates Acct-{Input,Output}-Gigawords. */
+#define ACCT_DEFAULT_UPDATE_INTERVAL 300
+
+static void accounting_sta_interim(struct hostapd_data *hapd,
+				   struct sta_info *sta);
+
+
+static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
+					  struct sta_info *sta,
+					  int status_type)
+{
+	struct radius_msg *msg;
+	char buf[128];
+	u8 *val;
+	size_t len;
+	int i;
+	struct wpabuf *b;
+
+	msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
+			     radius_client_get_id(hapd->radius));
+	if (msg == NULL) {
+		wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
+		return NULL;
+	}
+
+	if (sta) {
+		radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
+
+		if ((hapd->conf->wpa & 2) &&
+		    !hapd->conf->disable_pmksa_caching &&
+		    sta->eapol_sm && sta->eapol_sm->acct_multi_session_id_hi) {
+			os_snprintf(buf, sizeof(buf), "%08X+%08X",
+				    sta->eapol_sm->acct_multi_session_id_hi,
+				    sta->eapol_sm->acct_multi_session_id_lo);
+			if (!radius_msg_add_attr(
+				    msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+				    (u8 *) buf, os_strlen(buf))) {
+				wpa_printf(MSG_INFO,
+					   "Could not add Acct-Multi-Session-Id");
+				goto fail;
+			}
+		}
+	} else {
+		radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd));
+	}
+
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
+				       status_type)) {
+		wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
+		goto fail;
+	}
+
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr,
+					    RADIUS_ATTR_ACCT_AUTHENTIC) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
+				       hapd->conf->ieee802_1x ?
+				       RADIUS_ACCT_AUTHENTIC_RADIUS :
+				       RADIUS_ACCT_AUTHENTIC_LOCAL)) {
+		wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
+		goto fail;
+	}
+
+	if (sta) {
+		/* Use 802.1X identity if available */
+		val = ieee802_1x_get_identity(sta->eapol_sm, &len);
+
+		/* Use RADIUS ACL identity if 802.1X provides no identity */
+		if (!val && sta->identity) {
+			val = (u8 *) sta->identity;
+			len = os_strlen(sta->identity);
+		}
+
+		/* Use STA MAC if neither 802.1X nor RADIUS ACL provided
+		 * identity */
+		if (!val) {
+			os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
+				    MAC2STR(sta->addr));
+			val = (u8 *) buf;
+			len = os_strlen(buf);
+		}
+
+		if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
+					 len)) {
+			wpa_printf(MSG_INFO, "Could not add User-Name");
+			goto fail;
+		}
+	}
+
+	if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta,
+				   msg) < 0)
+		goto fail;
+
+	if (sta) {
+		for (i = 0; ; i++) {
+			val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
+							  i);
+			if (val == NULL)
+				break;
+
+			if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
+						 val, len)) {
+				wpa_printf(MSG_INFO, "Could not add Class");
+				goto fail;
+			}
+		}
+
+		b = ieee802_1x_get_radius_cui(sta->eapol_sm);
+		if (b &&
+		    !radius_msg_add_attr(msg,
+					 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+					 wpabuf_head(b), wpabuf_len(b))) {
+			wpa_printf(MSG_ERROR, "Could not add CUI");
+			goto fail;
+		}
+
+		if (!b && sta->radius_cui &&
+		    !radius_msg_add_attr(msg,
+					 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+					 (u8 *) sta->radius_cui,
+					 os_strlen(sta->radius_cui))) {
+			wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
+			goto fail;
+		}
+	}
+
+	return msg;
+
+ fail:
+	radius_msg_free(msg);
+	return NULL;
+}
+
+
+static int accounting_sta_update_stats(struct hostapd_data *hapd,
+				       struct sta_info *sta,
+				       struct hostap_sta_driver_data *data)
+{
+	if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
+		return -1;
+
+	if (sta->last_rx_bytes > data->rx_bytes)
+		sta->acct_input_gigawords++;
+	if (sta->last_tx_bytes > data->tx_bytes)
+		sta->acct_output_gigawords++;
+	sta->last_rx_bytes = data->rx_bytes;
+	sta->last_tx_bytes = data->tx_bytes;
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: "
+		       "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u "
+		       "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u",
+		       sta->last_rx_bytes, sta->acct_input_gigawords,
+		       sta->last_tx_bytes, sta->acct_output_gigawords);
+
+	return 0;
+}
+
+
+static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	int interval;
+
+	if (sta->acct_interim_interval) {
+		accounting_sta_interim(hapd, sta);
+		interval = sta->acct_interim_interval;
+	} else {
+		struct hostap_sta_driver_data data;
+		accounting_sta_update_stats(hapd, sta, &data);
+		interval = ACCT_DEFAULT_UPDATE_INTERVAL;
+	}
+
+	eloop_register_timeout(interval, 0, accounting_interim_update,
+			       hapd, sta);
+}
+
+
+/**
+ * accounting_sta_start - Start STA accounting
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ */
+void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct radius_msg *msg;
+	int interval;
+
+	if (sta->acct_session_started)
+		return;
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_INFO,
+		       "starting accounting session %08X-%08X",
+		       sta->acct_session_id_hi, sta->acct_session_id_lo);
+
+	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);
+
+	if (!hapd->conf->radius->acct_server)
+		return;
+
+	if (sta->acct_interim_interval)
+		interval = sta->acct_interim_interval;
+	else
+		interval = ACCT_DEFAULT_UPDATE_INTERVAL;
+	eloop_register_timeout(interval, 0, accounting_interim_update,
+			       hapd, sta);
+
+	msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
+	if (msg &&
+	    radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
+		radius_msg_free(msg);
+
+	sta->acct_session_started = 1;
+}
+
+
+static void accounting_sta_report(struct hostapd_data *hapd,
+				  struct sta_info *sta, int stop)
+{
+	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;
+
+	if (!hapd->conf->radius->acct_server)
+		return;
+
+	msg = accounting_msg(hapd, sta,
+			     stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
+			     RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
+	if (!msg) {
+		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,
+				       diff.sec)) {
+		wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
+		goto fail;
+	}
+
+	if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
+		if (!radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_ACCT_INPUT_PACKETS,
+					       data.rx_packets)) {
+			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)) {
+			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)) {
+			wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
+			goto fail;
+		}
+		gigawords = sta->acct_input_gigawords;
+#if __WORDSIZE == 64
+		gigawords += data.rx_bytes >> 32;
+#endif
+		if (gigawords &&
+		    !radius_msg_add_attr_int32(
+			    msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
+			    gigawords)) {
+			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)) {
+			wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
+			goto fail;
+		}
+		gigawords = sta->acct_output_gigawords;
+#if __WORDSIZE == 64
+		gigawords += data.tx_bytes >> 32;
+#endif
+		if (gigawords &&
+		    !radius_msg_add_attr_int32(
+			    msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
+			    gigawords)) {
+			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)) {
+		wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
+		goto fail;
+	}
+
+	if (eloop_terminated())
+		cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
+
+	if (stop && cause &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
+				       cause)) {
+		wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
+		goto fail;
+	}
+
+	if (radius_client_send(hapd->radius, msg,
+			       stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
+			       sta->addr) < 0)
+		goto fail;
+	return;
+
+ fail:
+	radius_msg_free(msg);
+}
+
+
+/**
+ * accounting_sta_interim - Send a interim STA accounting report
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ */
+static void accounting_sta_interim(struct hostapd_data *hapd,
+				   struct sta_info *sta)
+{
+	if (sta->acct_session_started)
+		accounting_sta_report(hapd, sta, 0);
+}
+
+
+/**
+ * accounting_sta_stop - Stop STA accounting
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ */
+void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if (sta->acct_session_started) {
+		accounting_sta_report(hapd, sta, 1);
+		eloop_cancel_timeout(accounting_interim_update, hapd, sta);
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+			       HOSTAPD_LEVEL_INFO,
+			       "stopped accounting session %08X-%08X",
+			       sta->acct_session_id_hi,
+			       sta->acct_session_id_lo);
+		sta->acct_session_started = 0;
+	}
+}
+
+
+void accounting_sta_get_id(struct hostapd_data *hapd,
+				  struct sta_info *sta)
+{
+	sta->acct_session_id_lo = hapd->acct_session_id_lo++;
+	if (hapd->acct_session_id_lo == 0) {
+		hapd->acct_session_id_hi++;
+	}
+	sta->acct_session_id_hi = hapd->acct_session_id_hi;
+}
+
+
+/**
+ * accounting_receive - Process the RADIUS frames from Accounting Server
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: Processing status
+ */
+static RadiusRxResult
+accounting_receive(struct radius_msg *msg, struct radius_msg *req,
+		   const u8 *shared_secret, size_t shared_secret_len,
+		   void *data)
+{
+	if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
+		wpa_printf(MSG_INFO, "Unknown RADIUS message code");
+		return RADIUS_RX_UNKNOWN;
+	}
+
+	if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
+		wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
+		return RADIUS_RX_INVALID_AUTHENTICATOR;
+	}
+
+	return RADIUS_RX_PROCESSED;
+}
+
+
+static void accounting_report_state(struct hostapd_data *hapd, int on)
+{
+	struct radius_msg *msg;
+
+	if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
+		return;
+
+	/* Inform RADIUS server that accounting will start/stop so that the
+	 * server can close old accounting sessions. */
+	msg = accounting_msg(hapd, NULL,
+			     on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
+			     RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
+	if (!msg)
+		return;
+
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
+				       RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
+	{
+		wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
+		radius_msg_free(msg);
+		return;
+	}
+
+	if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
+		radius_msg_free(msg);
+}
+
+
+/**
+ * accounting_init: Initialize accounting
+ * @hapd: hostapd BSS data
+ * Returns: 0 on success, -1 on failure
+ */
+int accounting_init(struct hostapd_data *hapd)
+{
+	struct os_time now;
+
+	/* Acct-Session-Id should be unique over reboots. Using a random number
+	 * is preferred. If that is not available, take the current time. Mix
+	 * in microseconds to make this more likely to be unique. */
+	os_get_time(&now);
+	if (os_get_random((u8 *) &hapd->acct_session_id_hi,
+			  sizeof(hapd->acct_session_id_hi)) < 0)
+		hapd->acct_session_id_hi = now.sec;
+	hapd->acct_session_id_hi ^= now.usec;
+
+	if (radius_client_register(hapd->radius, RADIUS_ACCT,
+				   accounting_receive, hapd))
+		return -1;
+
+	accounting_report_state(hapd, 1);
+
+	return 0;
+}
+
+
+/**
+ * accounting_deinit: Deinitialize accounting
+ * @hapd: hostapd BSS data
+ */
+void accounting_deinit(struct hostapd_data *hapd)
+{
+	accounting_report_state(hapd, 0);
+}
diff --git a/hostap/src/ap/accounting.h b/hostap/src/ap/accounting.h
new file mode 100644
index 0000000..dcc54ee
--- /dev/null
+++ b/hostap/src/ap/accounting.h
@@ -0,0 +1,44 @@
+/*
+ * hostapd / RADIUS Accounting
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ACCOUNTING_H
+#define ACCOUNTING_H
+
+#ifdef CONFIG_NO_ACCOUNTING
+static inline void accounting_sta_get_id(struct hostapd_data *hapd,
+					 struct sta_info *sta)
+{
+}
+
+static inline void accounting_sta_start(struct hostapd_data *hapd,
+					struct sta_info *sta)
+{
+}
+
+static inline void accounting_sta_stop(struct hostapd_data *hapd,
+				       struct sta_info *sta)
+{
+}
+
+static inline int accounting_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void accounting_deinit(struct hostapd_data *hapd)
+{
+}
+#else /* CONFIG_NO_ACCOUNTING */
+void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
+void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta);
+void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta);
+int accounting_init(struct hostapd_data *hapd);
+void accounting_deinit(struct hostapd_data *hapd);
+#endif /* CONFIG_NO_ACCOUNTING */
+
+#endif /* ACCOUNTING_H */
diff --git a/hostap/src/ap/acs.c b/hostap/src/ap/acs.c
new file mode 100644
index 0000000..03d797f
--- /dev/null
+++ b/hostap/src/ap/acs.c
@@ -0,0 +1,946 @@
+/*
+ * ACS - Automatic Channel Selection module
+ * Copyright (c) 2011, Atheros Communications
+ * 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 <math.h>
+
+#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"
+#include "ap_config.h"
+#include "hw_features.h"
+#include "acs.h"
+
+/*
+ * Automatic Channel Selection
+ * ===========================
+ *
+ * More info at
+ * ------------
+ * http://wireless.kernel.org/en/users/Documentation/acs
+ *
+ * How to use
+ * ----------
+ * - make sure you have CONFIG_ACS=y in hostapd's .config
+ * - use channel=0 or channel=acs to enable ACS
+ *
+ * How does it work
+ * ----------------
+ * 1. passive scans are used to collect survey data
+ *    (it is assumed that scan trigger collection of survey data in driver)
+ * 2. interference factor is calculated for each channel
+ * 3. ideal channel is picked depending on channel width by using adjacent
+ *    channel interference factors
+ *
+ * Known limitations
+ * -----------------
+ * - Current implementation depends heavily on the amount of time willing to
+ *   spend gathering survey data during hostapd startup. Short traffic bursts
+ *   may be missed and a suboptimal channel may be picked.
+ * - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS
+ *
+ * Todo / Ideas
+ * ------------
+ * - implement other interference computation methods
+ *   - BSS/RSSI based
+ *   - spectral scan based
+ *   (should be possibly to hook this up with current ACS scans)
+ * - add wpa_supplicant support (for P2P)
+ * - collect a histogram of interference over time allowing more educated
+ *   guess about an ideal channel (perhaps CSA could be used to migrate AP to a
+ *   new "better" channel while running)
+ * - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs
+ *   when choosing the ideal channel
+ *
+ * Survey interference factor implementation details
+ * -------------------------------------------------
+ * Generic interference_factor in struct hostapd_channel_data is used.
+ *
+ * The survey interference factor is defined as the ratio of the
+ * observed busy time over the time we spent on the channel,
+ * this value is then amplified by the observed noise floor on
+ * the channel in comparison to the lowest noise floor observed
+ * on the entire band.
+ *
+ * This corresponds to:
+ * ---
+ * (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf)
+ * ---
+ *
+ * The coefficient of 2 reflects the way power in "far-field"
+ * radiation decreases as the square of distance from the antenna [1].
+ * What this does is it decreases the observed busy time ratio if the
+ * noise observed was low but increases it if the noise was high,
+ * proportionally to the way "far field" radiation changes over
+ * distance.
+ *
+ * If channel busy time is not available the fallback is to use channel RX time.
+ *
+ * Since noise floor is in dBm it is necessary to convert it into Watts so that
+ * combined channel interference (e.g., HT40, which uses two channels) can be
+ * calculated easily.
+ * ---
+ * (busy time - tx time) / (active time - tx time) *
+ *    2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * ---
+ *
+ * However to account for cases where busy/rx time is 0 (channel load is then
+ * 0%) channel noise floor signal power is combined into the equation so a
+ * channel with lower noise floor is preferred. The equation becomes:
+ * ---
+ * 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) *
+ *    2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * ---
+ *
+ * All this "interference factor" is purely subjective and only time
+ * will tell how usable this is. By using the minimum noise floor we
+ * remove any possible issues due to card calibration. The computation
+ * of the interference factor then is dependent on what the card itself
+ * picks up as the minimum noise, not an actual real possible card
+ * noise value.
+ *
+ * Total interference computation details
+ * --------------------------------------
+ * The above channel interference factor is calculated with no respect to
+ * target operational bandwidth.
+ *
+ * To find an ideal channel the above data is combined by taking into account
+ * the target operational bandwidth and selected band. E.g., on 2.4 GHz channels
+ * overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth
+ * on 5 GHz.
+ *
+ * Each valid and possible channel spec (i.e., channel + width) is taken and its
+ * interference factor is computed by summing up interferences of each channel
+ * it overlaps. The one with least total interference is picked up.
+ *
+ * Note: This implies base channel interference factor must be non-negative
+ * allowing easy summing up.
+ *
+ * Example ACS analysis printout
+ * -----------------------------
+ *
+ * ACS: Trying survey-based ACS
+ * ACS: Survey analysis for channel 1 (2412 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13
+ * ACS:  2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
+ * ACS:  3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11
+ * ACS:  4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
+ * ACS:  5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
+ * ACS:  * interference factor average: 0.0557166
+ * ACS: Survey analysis for channel 2 (2417 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
+ * ACS:  2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4
+ * ACS:  3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6
+ * ACS:  4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24
+ * ACS:  5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
+ * ACS:  * interference factor average: 0.050832
+ * ACS: Survey analysis for channel 3 (2422 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS:  2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
+ * ACS:  3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS:  4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS:  5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS:  * interference factor average: 0.0148838
+ * ACS: Survey analysis for channel 4 (2427 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
+ * ACS:  3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
+ * ACS:  5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  * interference factor average: 0.0160801
+ * ACS: Survey analysis for channel 5 (2432 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66
+ * ACS:  2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7
+ * ACS:  3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2
+ * ACS:  4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109
+ * ACS:  5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
+ * ACS:  * interference factor average: 0.232244
+ * ACS: Survey analysis for channel 6 (2437 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89
+ * ACS:  2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13
+ * ACS:  3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
+ * ACS:  4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70
+ * ACS:  5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
+ * ACS:  * interference factor average: 0.232298
+ * ACS: Survey analysis for channel 7 (2442 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71
+ * ACS:  2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62
+ * ACS:  3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS:  4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS:  5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
+ * ACS:  * interference factor average: 0.195031
+ * ACS: Survey analysis for channel 8 (2447 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8
+ * ACS:  2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8
+ * ACS:  3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS:  4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21
+ * ACS:  5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27
+ * ACS:  * interference factor average: 0.0865885
+ * ACS: Survey analysis for channel 9 (2452 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2
+ * ACS:  2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5
+ * ACS:  3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1
+ * ACS:  5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  * interference factor average: 0.00993022
+ * ACS: Survey analysis for channel 10 (2457 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8
+ * ACS:  5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  * interference factor average: 0.0136033
+ * ACS: Survey analysis for channel 11 (2462 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS:  2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
+ * ACS:  3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7
+ * ACS:  5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15
+ * ACS:  * interference factor average: 0.0271605
+ * ACS: Survey analysis for channel 12 (2467 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
+ * ACS:  2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS:  5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1
+ * ACS:  * interference factor average: 0.0148992
+ * ACS: Survey analysis for channel 13 (2472 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12
+ * ACS:  2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
+ * ACS:  3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  * interference factor average: 0.0260179
+ * ACS: Survey analysis for selected bandwidth 20MHz
+ * ACS:  * channel 1: total interference = 0.121432
+ * ACS:  * channel 2: total interference = 0.137512
+ * ACS:  * channel 3: total interference = 0.369757
+ * ACS:  * channel 4: total interference = 0.546338
+ * ACS:  * channel 5: total interference = 0.690538
+ * ACS:  * channel 6: total interference = 0.762242
+ * ACS:  * channel 7: total interference = 0.756092
+ * ACS:  * channel 8: total interference = 0.537451
+ * ACS:  * channel 9: total interference = 0.332313
+ * ACS:  * channel 10: total interference = 0.152182
+ * ACS:  * channel 11: total interference = 0.0916111
+ * ACS:  * channel 12: total interference = 0.0816809
+ * ACS:  * channel 13: total interference = 0.0680776
+ * ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776
+ *
+ * [1] http://en.wikipedia.org/wiki/Near_and_far_field
+ */
+
+
+static int acs_request_scan(struct hostapd_iface *iface);
+static int acs_survey_is_sufficient(struct freq_survey *survey);
+
+
+static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
+{
+	struct freq_survey *survey, *tmp;
+
+	if (dl_list_empty(&chan->survey_list))
+		return;
+
+	dl_list_for_each_safe(survey, tmp, &chan->survey_list,
+			      struct freq_survey, list) {
+		dl_list_del(&survey->list);
+		os_free(survey);
+	}
+}
+
+
+static void acs_cleanup(struct hostapd_iface *iface)
+{
+	int i;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < iface->current_mode->num_channels; i++) {
+		chan = &iface->current_mode->channels[i];
+
+		if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED)
+			acs_clean_chan_surveys(chan);
+
+		dl_list_init(&chan->survey_list);
+		chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
+		chan->min_nf = 0;
+	}
+
+	iface->chans_surveyed = 0;
+	iface->acs_num_completed_scans = 0;
+}
+
+
+static void acs_fail(struct hostapd_iface *iface)
+{
+	wpa_printf(MSG_ERROR, "ACS: Failed to start");
+	acs_cleanup(iface);
+	hostapd_disable_iface(iface);
+}
+
+
+static long double
+acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf)
+{
+	long double factor, busy, total;
+
+	if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY)
+		busy = survey->channel_time_busy;
+	else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX)
+		busy = survey->channel_time_rx;
+	else {
+		/* This shouldn't really happen as survey data is checked in
+		 * acs_sanity_check() */
+		wpa_printf(MSG_ERROR, "ACS: Survey data missing");
+		return 0;
+	}
+
+	total = survey->channel_time;
+
+	if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) {
+		busy -= survey->channel_time_tx;
+		total -= survey->channel_time_tx;
+	}
+
+	/* TODO: figure out the best multiplier for noise floor base */
+	factor = pow(10, survey->nf / 5.0L) +
+		(busy / total) *
+		pow(2, pow(10, (long double) survey->nf / 10.0L) -
+		    pow(10, (long double) min_nf / 10.0L));
+
+	return factor;
+}
+
+
+static void
+acs_survey_chan_interference_factor(struct hostapd_iface *iface,
+				    struct hostapd_channel_data *chan)
+{
+	struct freq_survey *survey;
+	unsigned int i = 0;
+	long double int_factor = 0;
+	unsigned count = 0;
+
+	if (dl_list_empty(&chan->survey_list))
+		return;
+
+	if (chan->flag & HOSTAPD_CHAN_DISABLED)
+		return;
+
+	chan->interference_factor = 0;
+
+	dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
+	{
+		i++;
+
+		if (!acs_survey_is_sufficient(survey)) {
+			wpa_printf(MSG_DEBUG, "ACS: %d: insufficient data", i);
+			continue;
+		}
+
+		count++;
+		int_factor = acs_survey_interference_factor(survey,
+							    iface->lowest_nf);
+		chan->interference_factor += int_factor;
+		wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu",
+			   i, chan->min_nf, int_factor,
+			   survey->nf, (unsigned long) survey->channel_time,
+			   (unsigned long) survey->channel_time_busy,
+			   (unsigned long) survey->channel_time_rx);
+	}
+
+	if (!count)
+		return;
+	chan->interference_factor /= count;
+}
+
+
+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 < 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;
+
+	return 0;
+}
+
+
+static int acs_survey_is_sufficient(struct freq_survey *survey)
+{
+	if (!(survey->filled & SURVEY_HAS_NF)) {
+		wpa_printf(MSG_INFO, "ACS: Survey is missing noise floor");
+		return 0;
+	}
+
+	if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
+		wpa_printf(MSG_INFO, "ACS: Survey is missing channel time");
+		return 0;
+	}
+
+	if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
+	    !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
+		wpa_printf(MSG_INFO,
+			   "ACS: Survey is missing RX and busy time (at least one is required)");
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
+{
+	struct freq_survey *survey;
+	int ret = -1;
+
+	dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
+	{
+		if (acs_survey_is_sufficient(survey)) {
+			ret = 1;
+			break;
+		}
+		ret = 0;
+	}
+
+	if (ret == -1)
+		ret = 1; /* no survey list entries */
+
+	if (!ret) {
+		wpa_printf(MSG_INFO,
+			   "ACS: Channel %d has insufficient survey data",
+			   chan->chan);
+	}
+
+	return ret;
+}
+
+
+static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
+{
+	int i;
+	struct hostapd_channel_data *chan;
+	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;
+
+		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;
+}
+
+
+static int is_in_chanlist(struct hostapd_iface *iface,
+			  struct hostapd_channel_data *chan)
+{
+	if (!iface->conf->acs_ch_list.num)
+		return 1;
+
+	return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
+}
+
+
+static void acs_survey_all_chans_intereference_factor(
+	struct hostapd_iface *iface)
+{
+	int i;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < iface->current_mode->num_channels; i++) {
+		chan = &iface->current_mode->channels[i];
+
+		if (!acs_usable_chan(chan))
+			continue;
+
+		if (!is_in_chanlist(iface, chan))
+			continue;
+
+		wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
+			   chan->chan, chan->freq);
+
+		acs_survey_chan_interference_factor(iface, chan);
+
+		wpa_printf(MSG_DEBUG, "ACS:  * interference factor average: %Lg",
+			   chan->interference_factor);
+	}
+}
+
+
+static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface,
+						  int freq)
+{
+	struct hostapd_channel_data *chan;
+	int i;
+
+	for (i = 0; i < iface->current_mode->num_channels; i++) {
+		chan = &iface->current_mode->channels[i];
+
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+
+		if (chan->freq == freq)
+			return chan;
+	}
+
+	return NULL;
+}
+
+
+static int is_24ghz_mode(enum hostapd_hw_mode mode)
+{
+	return mode == HOSTAPD_MODE_IEEE80211B ||
+		mode == HOSTAPD_MODE_IEEE80211G;
+}
+
+
+static int is_common_24ghz_chan(int chan)
+{
+	return chan == 1 || chan == 6 || chan == 11;
+}
+
+
+#ifndef ACS_ADJ_WEIGHT
+#define ACS_ADJ_WEIGHT 0.85
+#endif /* ACS_ADJ_WEIGHT */
+
+#ifndef ACS_NEXT_ADJ_WEIGHT
+#define ACS_NEXT_ADJ_WEIGHT 0.55
+#endif /* ACS_NEXT_ADJ_WEIGHT */
+
+#ifndef ACS_24GHZ_PREFER_1_6_11
+/*
+ * Select commonly used channels 1, 6, 11 by default even if a neighboring
+ * channel has a smaller interference factor as long as it is not better by more
+ * than this multiplier.
+ */
+#define ACS_24GHZ_PREFER_1_6_11 0.8
+#endif /* ACS_24GHZ_PREFER_1_6_11 */
+
+/*
+ * At this point it's assumed chan->interface_factor has been computed.
+ * This function should be reusable regardless of interference computation
+ * option (survey, BSS, spectral, ...). chan->interference factor must be
+ * summable (i.e., must be always greater than zero).
+ */
+static struct hostapd_channel_data *
+acs_find_ideal_chan(struct hostapd_iface *iface)
+{
+	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;
+	unsigned int k;
+
+	/* TODO: HT40- support */
+
+	if (iface->conf->ieee80211n &&
+	    iface->conf->secondary_channel == -1) {
+		wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
+		return NULL;
+	}
+
+	if (iface->conf->ieee80211n &&
+	    iface->conf->secondary_channel)
+		n_chans = 2;
+
+	if (iface->conf->ieee80211ac &&
+	    iface->conf->vht_oper_chwidth == 1)
+		n_chans = 4;
+
+	/* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */
+
+	wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz",
+		   n_chans == 1 ? 20 :
+		   n_chans == 2 ? 40 :
+		   n_chans == 4 ? 80 :
+		   -1);
+
+	for (i = 0; i < iface->current_mode->num_channels; i++) {
+		double total_weight;
+		struct acs_bias *bias, tmp_bias;
+
+		chan = &iface->current_mode->channels[i];
+
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+
+		if (!is_in_chanlist(iface, chan))
+			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 &&
+		    iface->conf->ieee80211n &&
+		    iface->conf->secondary_channel &&
+		    !acs_usable_ht40_chan(chan)) {
+			wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for HT40",
+				   chan->chan);
+			continue;
+		}
+
+		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;
+		total_weight = 1;
+
+		for (j = 1; j < n_chans; j++) {
+			adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
+			if (!adj_chan)
+				break;
+
+			if (acs_usable_chan(adj_chan)) {
+				factor += adj_chan->interference_factor;
+				total_weight += 1;
+			}
+		}
+
+		if (j != n_chans) {
+			wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
+				   chan->chan);
+			continue;
+		}
+
+		/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
+		 * channel interference factor. */
+		if (is_24ghz_mode(iface->current_mode->mode)) {
+			for (j = 0; j < n_chans; j++) {
+				adj_chan = acs_find_chan(iface, chan->freq +
+							 (j * 20) - 5);
+				if (adj_chan && acs_usable_chan(adj_chan)) {
+					factor += ACS_ADJ_WEIGHT *
+						adj_chan->interference_factor;
+					total_weight += ACS_ADJ_WEIGHT;
+				}
+
+				adj_chan = acs_find_chan(iface, chan->freq +
+							 (j * 20) - 10);
+				if (adj_chan && acs_usable_chan(adj_chan)) {
+					factor += ACS_NEXT_ADJ_WEIGHT *
+						adj_chan->interference_factor;
+					total_weight += ACS_NEXT_ADJ_WEIGHT;
+				}
+
+				adj_chan = acs_find_chan(iface, chan->freq +
+							 (j * 20) + 5);
+				if (adj_chan && acs_usable_chan(adj_chan)) {
+					factor += ACS_ADJ_WEIGHT *
+						adj_chan->interference_factor;
+					total_weight += ACS_ADJ_WEIGHT;
+				}
+
+				adj_chan = acs_find_chan(iface, chan->freq +
+							 (j * 20) + 10);
+				if (adj_chan && acs_usable_chan(adj_chan)) {
+					factor += ACS_NEXT_ADJ_WEIGHT *
+						adj_chan->interference_factor;
+					total_weight += ACS_NEXT_ADJ_WEIGHT;
+				}
+			}
+		}
+
+		factor /= total_weight;
+
+		bias = NULL;
+		if (iface->conf->acs_chan_bias) {
+			for (k = 0; k < iface->conf->num_acs_chan_bias; k++) {
+				bias = &iface->conf->acs_chan_bias[k];
+				if (bias->channel == chan->chan)
+					break;
+				bias = NULL;
+			}
+		} else if (is_24ghz_mode(iface->current_mode->mode) &&
+			   is_common_24ghz_chan(chan->chan)) {
+			tmp_bias.channel = chan->chan;
+			tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11;
+			bias = &tmp_bias;
+		}
+
+		if (bias) {
+			factor *= bias->bias;
+			wpa_printf(MSG_DEBUG,
+				   "ACS:  * channel %d: total interference = %Lg (%f bias)",
+				   chan->chan, factor, bias->bias);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "ACS:  * channel %d: total interference = %Lg",
+				   chan->chan, 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) {
+		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 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:
+		offset = 2 * iface->conf->secondary_channel;
+		break;
+	case VHT_CHANWIDTH_80MHZ:
+		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");
+		return;
+	}
+
+	iface->conf->vht_oper_centr_freq_seg0_idx =
+		iface->conf->channel + offset;
+}
+
+
+static int acs_study_survey_based(struct hostapd_iface *iface)
+{
+	wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS");
+
+	if (!iface->chans_surveyed) {
+		wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data");
+		return -1;
+	}
+
+	if (!acs_surveys_are_sufficient(iface)) {
+		wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data");
+		return -1;
+	}
+
+	acs_survey_all_chans_intereference_factor(iface);
+	return 0;
+}
+
+
+static int acs_study_options(struct hostapd_iface *iface)
+{
+	int err;
+
+	err = acs_study_survey_based(iface);
+	if (err == 0)
+		return 0;
+
+	/* TODO: If no surveys are available/sufficient this is a good
+	 * place to fallback to BSS-based ACS */
+
+	return -1;
+}
+
+
+static void acs_study(struct hostapd_iface *iface)
+{
+	struct hostapd_channel_data *ideal_chan;
+	int err;
+
+	err = acs_study_options(iface);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "ACS: All study options have failed");
+		goto fail;
+	}
+
+	ideal_chan = acs_find_ideal_chan(iface);
+	if (!ideal_chan) {
+		wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel");
+		err = -1;
+		goto fail;
+	}
+
+	iface->conf->channel = ideal_chan->chan;
+
+	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 :)
+	 */
+	if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) {
+		acs_cleanup(iface);
+		return;
+	}
+
+	/* 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);
+}
+
+
+static void acs_scan_complete(struct hostapd_iface *iface)
+{
+	int err;
+
+	iface->scan_cb = NULL;
+
+	wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)",
+		   iface->conf->acs_num_scans);
+
+	err = hostapd_drv_get_survey(iface->bss[0], 0);
+	if (err) {
+		wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
+		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");
+			goto fail;
+		}
+
+		return;
+	}
+
+	acs_study(iface);
+	return;
+fail:
+	hostapd_acs_completed(iface, 1);
+	acs_fail(iface);
+}
+
+
+static int acs_request_scan(struct hostapd_iface *iface)
+{
+	struct wpa_driver_scan_params params;
+	struct hostapd_channel_data *chan;
+	int i, *freq;
+
+	os_memset(&params, 0, sizeof(params));
+	params.freqs = os_calloc(iface->current_mode->num_channels + 1,
+				 sizeof(params.freqs[0]));
+	if (params.freqs == NULL)
+		return -1;
+
+	freq = params.freqs;
+	for (i = 0; i < iface->current_mode->num_channels; i++) {
+		chan = &iface->current_mode->channels[i];
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+
+		if (!is_in_chanlist(iface, chan))
+			continue;
+
+		*freq++ = chan->freq;
+	}
+	*freq = 0;
+
+	iface->scan_cb = acs_scan_complete;
+
+	wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
+		   iface->acs_num_completed_scans + 1,
+		   iface->conf->acs_num_scans);
+
+	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;
+	}
+
+	os_free(params.freqs);
+	return 0;
+}
+
+
+enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
+{
+	int err;
+
+	wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
+
+	if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
+		wpa_printf(MSG_INFO, "ACS: Offloading to driver");
+		err = hostapd_drv_do_acs(iface->bss[0]);
+		if (err)
+			return HOSTAPD_CHAN_INVALID;
+		return HOSTAPD_CHAN_ACS;
+	}
+
+	acs_cleanup(iface);
+
+	err = acs_request_scan(iface);
+	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/hostap/src/ap/acs.h b/hostap/src/ap/acs.h
new file mode 100644
index 0000000..fc85259
--- /dev/null
+++ b/hostap/src/ap/acs.h
@@ -0,0 +1,27 @@
+/*
+ * ACS - Automatic Channel Selection module
+ * Copyright (c) 2011, Atheros Communications
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ACS_H
+#define ACS_H
+
+#ifdef CONFIG_ACS
+
+enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
+
+#else /* CONFIG_ACS */
+
+static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
+{
+	wpa_printf(MSG_ERROR, "ACS was disabled on your build, rebuild hostapd with CONFIG_ACS=y or set channel");
+	return HOSTAPD_CHAN_INVALID;
+}
+
+#endif /* CONFIG_ACS */
+
+#endif /* ACS_H */
diff --git a/hostap/src/ap/ap_config.c b/hostap/src/ap/ap_config.c
new file mode 100644
index 0000000..9a96e50
--- /dev/null
+++ b/hostap/src/ap/ap_config.c
@@ -0,0 +1,985 @@
+/*
+ * hostapd / Configuration helper functions
+ * 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.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/sha1.h"
+#include "radius/radius_client.h"
+#include "common/ieee802_11_defs.h"
+#include "common/eapol_common.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_server/eap.h"
+#include "wpa_auth.h"
+#include "sta_info.h"
+#include "ap_config.h"
+
+
+static void hostapd_config_free_vlan(struct hostapd_bss_config *bss)
+{
+	struct hostapd_vlan *vlan, *prev;
+
+	vlan = bss->vlan;
+	prev = NULL;
+	while (vlan) {
+		prev = vlan;
+		vlan = vlan->next;
+		os_free(prev);
+	}
+
+	bss->vlan = NULL;
+}
+
+
+void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
+{
+	bss->logger_syslog_level = HOSTAPD_LEVEL_INFO;
+	bss->logger_stdout_level = HOSTAPD_LEVEL_INFO;
+	bss->logger_syslog = (unsigned int) -1;
+	bss->logger_stdout = (unsigned int) -1;
+
+	bss->auth_algs = WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED;
+
+	bss->wep_rekeying_period = 300;
+	/* use key0 in individual key and key1 in broadcast key */
+	bss->broadcast_key_idx_min = 1;
+	bss->broadcast_key_idx_max = 2;
+	bss->eap_reauth_period = 3600;
+
+	bss->wpa_group_rekey = 600;
+	bss->wpa_gmk_rekey = 86400;
+	bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+	bss->wpa_pairwise = WPA_CIPHER_TKIP;
+	bss->wpa_group = WPA_CIPHER_TKIP;
+	bss->rsn_pairwise = 0;
+
+	bss->max_num_sta = MAX_STA_COUNT;
+
+	bss->dtim_period = 2;
+
+	bss->radius_server_auth_port = 1812;
+	bss->ap_max_inactivity = AP_MAX_INACTIVITY;
+	bss->eapol_version = EAPOL_VERSION;
+
+	bss->max_listen_interval = 65535;
+
+	bss->pwd_group = 19; /* ECC: GF(p=256) */
+
+#ifdef CONFIG_IEEE80211W
+	bss->assoc_sa_query_max_timeout = 1000;
+	bss->assoc_sa_query_retry_timeout = 201;
+	bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
+#endif /* CONFIG_IEEE80211W */
+#ifdef EAP_SERVER_FAST
+	 /* both anonymous and authenticated provisioning */
+	bss->eap_fast_prov = 3;
+	bss->pac_key_lifetime = 7 * 24 * 60 * 60;
+	bss->pac_key_refresh_time = 1 * 24 * 60 * 60;
+#endif /* EAP_SERVER_FAST */
+
+	/* Set to -1 as defaults depends on HT in setup */
+	bss->wmm_enabled = -1;
+
+#ifdef CONFIG_IEEE80211R
+	bss->ft_over_ds = 1;
+#endif /* CONFIG_IEEE80211R */
+
+	bss->radius_das_time_window = 300;
+
+	bss->sae_anti_clogging_threshold = 5;
+}
+
+
+struct hostapd_config * hostapd_config_defaults(void)
+{
+#define ecw2cw(ecw) ((1 << (ecw)) - 1)
+
+	struct hostapd_config *conf;
+	struct hostapd_bss_config *bss;
+	const int aCWmin = 4, aCWmax = 10;
+	const struct hostapd_wmm_ac_params ac_bk =
+		{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
+	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, 3008 / 32, 0 };
+	const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
+		{ 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 =
+		{ 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0};
+	const struct hostapd_tx_queue_params txq_vi =
+		{ 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30};
+	const struct hostapd_tx_queue_params txq_vo =
+		{ 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
+		  (ecw2cw(aCWmin) + 1) / 2 - 1, 15};
+
+#undef ecw2cw
+
+	conf = os_zalloc(sizeof(*conf));
+	bss = os_zalloc(sizeof(*bss));
+	if (conf == NULL || bss == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to allocate memory for "
+			   "configuration data.");
+		os_free(conf);
+		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;
+	}
+
+	hostapd_config_defaults_bss(bss);
+
+	conf->num_bss = 1;
+
+	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;
+	conf->wmm_ac_params[2] = ac_vi;
+	conf->wmm_ac_params[3] = ac_vo;
+
+	conf->tx_queue[0] = txq_vo;
+	conf->tx_queue[1] = txq_vi;
+	conf->tx_queue[2] = txq_be;
+	conf->tx_queue[3] = txq_bk;
+
+	conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED;
+
+	conf->ap_table_max_size = 255;
+	conf->ap_table_expiration_time = 60;
+	conf->track_sta_max_age = 180;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	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 */
+
+	conf->acs = 0;
+	conf->acs_ch_list.num = 0;
+#ifdef CONFIG_ACS
+	conf->acs_num_scans = 5;
+#endif /* CONFIG_ACS */
+
+	return conf;
+}
+
+
+int hostapd_mac_comp(const void *a, const void *b)
+{
+	return os_memcmp(a, b, sizeof(macaddr));
+}
+
+
+int hostapd_mac_comp_empty(const void *a)
+{
+	macaddr empty = { 0 };
+	return os_memcmp(a, empty, sizeof(macaddr));
+}
+
+
+static int hostapd_config_read_wpa_psk(const char *fname,
+				       struct hostapd_ssid *ssid)
+{
+	FILE *f;
+	char buf[128], *pos;
+	int line = 0, ret = 0, len, ok;
+	u8 addr[ETH_ALEN];
+	struct hostapd_wpa_psk *psk;
+
+	if (!fname)
+		return 0;
+
+	f = fopen(fname, "r");
+	if (!f) {
+		wpa_printf(MSG_ERROR, "WPA PSK file '%s' not found.", fname);
+		return -1;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		line++;
+
+		if (buf[0] == '#')
+			continue;
+		pos = buf;
+		while (*pos != '\0') {
+			if (*pos == '\n') {
+				*pos = '\0';
+				break;
+			}
+			pos++;
+		}
+		if (buf[0] == '\0')
+			continue;
+
+		if (hwaddr_aton(buf, addr)) {
+			wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on "
+				   "line %d in '%s'", buf, line, fname);
+			ret = -1;
+			break;
+		}
+
+		psk = os_zalloc(sizeof(*psk));
+		if (psk == NULL) {
+			wpa_printf(MSG_ERROR, "WPA PSK allocation failed");
+			ret = -1;
+			break;
+		}
+		if (is_zero_ether_addr(addr))
+			psk->group = 1;
+		else
+			os_memcpy(psk->addr, addr, ETH_ALEN);
+
+		pos = buf + 17;
+		if (*pos == '\0') {
+			wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'",
+				   line, fname);
+			os_free(psk);
+			ret = -1;
+			break;
+		}
+		pos++;
+
+		ok = 0;
+		len = os_strlen(pos);
+		if (len == 64 && hexstr2bin(pos, psk->psk, PMK_LEN) == 0)
+			ok = 1;
+		else if (len >= 8 && len < 64) {
+			pbkdf2_sha1(pos, ssid->ssid, ssid->ssid_len,
+				    4096, psk->psk, PMK_LEN);
+			ok = 1;
+		}
+		if (!ok) {
+			wpa_printf(MSG_ERROR, "Invalid PSK '%s' on line %d in "
+				   "'%s'", pos, line, fname);
+			os_free(psk);
+			ret = -1;
+			break;
+		}
+
+		psk->next = ssid->wpa_psk;
+		ssid->wpa_psk = psk;
+	}
+
+	fclose(f);
+
+	return ret;
+}
+
+
+static int hostapd_derive_psk(struct hostapd_ssid *ssid)
+{
+	ssid->wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
+	if (ssid->wpa_psk == NULL) {
+		wpa_printf(MSG_ERROR, "Unable to alloc space for PSK");
+		return -1;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "SSID",
+			  (u8 *) ssid->ssid, ssid->ssid_len);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "PSK (ASCII passphrase)",
+			      (u8 *) ssid->wpa_passphrase,
+			      os_strlen(ssid->wpa_passphrase));
+	pbkdf2_sha1(ssid->wpa_passphrase,
+		    ssid->ssid, ssid->ssid_len,
+		    4096, ssid->wpa_psk->psk, PMK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "PSK (from passphrase)",
+			ssid->wpa_psk->psk, PMK_LEN);
+	return 0;
+}
+
+
+int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf)
+{
+	struct hostapd_ssid *ssid = &conf->ssid;
+
+	if (ssid->wpa_passphrase != NULL) {
+		if (ssid->wpa_psk != NULL) {
+			wpa_printf(MSG_DEBUG, "Using pre-configured WPA PSK "
+				   "instead of passphrase");
+		} else {
+			wpa_printf(MSG_DEBUG, "Deriving WPA PSK based on "
+				   "passphrase");
+			if (hostapd_derive_psk(ssid) < 0)
+				return -1;
+		}
+		ssid->wpa_psk->group = 1;
+	}
+
+	if (ssid->wpa_psk_file) {
+		if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file,
+						&conf->ssid))
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static void hostapd_config_free_radius(struct hostapd_radius_server *servers,
+				       int num_servers)
+{
+	int i;
+
+	for (i = 0; i < num_servers; i++) {
+		os_free(servers[i].shared_secret);
+	}
+	os_free(servers);
+}
+
+
+struct hostapd_radius_attr *
+hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type)
+{
+	for (; attr; attr = attr->next) {
+		if (attr->type == type)
+			return attr;
+	}
+	return NULL;
+}
+
+
+static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr)
+{
+	struct hostapd_radius_attr *prev;
+
+	while (attr) {
+		prev = attr;
+		attr = attr->next;
+		wpabuf_free(prev->val);
+		os_free(prev);
+	}
+}
+
+
+void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
+{
+	hostapd_config_free_radius_attr(user->accept_attr);
+	os_free(user->identity);
+	bin_clear_free(user->password, user->password_len);
+	os_free(user);
+}
+
+
+static void hostapd_config_free_wep(struct hostapd_wep_keys *keys)
+{
+	int i;
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		bin_clear_free(keys->key[i], keys->len[i]);
+		keys->key[i] = NULL;
+	}
+}
+
+
+void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **l)
+{
+	struct hostapd_wpa_psk *psk, *tmp;
+
+	for (psk = *l; psk;) {
+		tmp = psk;
+		psk = psk->next;
+		bin_clear_free(tmp, sizeof(*tmp));
+	}
+	*l = NULL;
+}
+
+
+void hostapd_config_free_bss(struct hostapd_bss_config *conf)
+{
+	struct hostapd_eap_user *user, *prev_user;
+
+	if (conf == NULL)
+		return;
+
+	hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk);
+
+	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
+	os_free(conf->ssid.vlan_tagged_interface);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+	user = conf->eap_user;
+	while (user) {
+		prev_user = user;
+		user = user->next;
+		hostapd_config_free_eap_user(prev_user);
+	}
+	os_free(conf->eap_user_sqlite);
+
+	os_free(conf->eap_req_id_text);
+	os_free(conf->erp_domain);
+	os_free(conf->accept_mac);
+	os_free(conf->deny_mac);
+	os_free(conf->nas_identifier);
+	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);
+	os_free(conf->ctrl_interface);
+	os_free(conf->ca_cert);
+	os_free(conf->server_cert);
+	os_free(conf->private_key);
+	os_free(conf->private_key_passwd);
+	os_free(conf->ocsp_stapling_response);
+	os_free(conf->dh_file);
+	os_free(conf->openssl_ciphers);
+	os_free(conf->pac_opaque_encr_key);
+	os_free(conf->eap_fast_a_id);
+	os_free(conf->eap_fast_a_id_info);
+	os_free(conf->eap_sim_db);
+	os_free(conf->radius_server_clients);
+	os_free(conf->radius);
+	os_free(conf->radius_das_shared_secret);
+	hostapd_config_free_vlan(conf);
+	os_free(conf->time_zone);
+
+#ifdef CONFIG_IEEE80211R
+	{
+		struct ft_remote_r0kh *r0kh, *r0kh_prev;
+		struct ft_remote_r1kh *r1kh, *r1kh_prev;
+
+		r0kh = conf->r0kh_list;
+		conf->r0kh_list = NULL;
+		while (r0kh) {
+			r0kh_prev = r0kh;
+			r0kh = r0kh->next;
+			os_free(r0kh_prev);
+		}
+
+		r1kh = conf->r1kh_list;
+		conf->r1kh_list = NULL;
+		while (r1kh) {
+			r1kh_prev = r1kh;
+			r1kh = r1kh->next;
+			os_free(r1kh_prev);
+		}
+	}
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_WPS
+	os_free(conf->wps_pin_requests);
+	os_free(conf->device_name);
+	os_free(conf->manufacturer);
+	os_free(conf->model_name);
+	os_free(conf->model_number);
+	os_free(conf->serial_number);
+	os_free(conf->config_methods);
+	os_free(conf->ap_pin);
+	os_free(conf->extra_cred);
+	os_free(conf->ap_settings);
+	os_free(conf->upnp_iface);
+	os_free(conf->friendly_name);
+	os_free(conf->manufacturer_url);
+	os_free(conf->model_description);
+	os_free(conf->model_url);
+	os_free(conf->upc);
+	{
+		unsigned int i;
+
+		for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+			wpabuf_free(conf->wps_vendor_ext[i]);
+	}
+	wpabuf_free(conf->wps_nfc_dh_pubkey);
+	wpabuf_free(conf->wps_nfc_dh_privkey);
+	wpabuf_free(conf->wps_nfc_dev_pw);
+#endif /* CONFIG_WPS */
+
+	os_free(conf->roaming_consortium);
+	os_free(conf->venue_name);
+	os_free(conf->nai_realm_data);
+	os_free(conf->network_auth_type);
+	os_free(conf->anqp_3gpp_cell_net);
+	os_free(conf->domain_name);
+
+#ifdef CONFIG_RADIUS_TEST
+	os_free(conf->dump_msk_file);
+#endif /* CONFIG_RADIUS_TEST */
+
+#ifdef CONFIG_HS20
+	os_free(conf->hs20_oper_friendly_name);
+	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);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	wpabuf_free(conf->own_ie_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	os_free(conf->no_probe_resp_if_seen_on);
+	os_free(conf->no_auth_if_seen_on);
+
+	os_free(conf);
+}
+
+
+/**
+ * hostapd_config_free - Free hostapd configuration
+ * @conf: Configuration data from hostapd_config_read().
+ */
+void hostapd_config_free(struct hostapd_config *conf)
+{
+	size_t i;
+
+	if (conf == NULL)
+		return;
+
+	for (i = 0; i < conf->num_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->acs_ch_list.range);
+	os_free(conf->driver_params);
+#ifdef CONFIG_ACS
+	os_free(conf->acs_chan_bias);
+#endif /* CONFIG_ACS */
+
+	os_free(conf);
+}
+
+
+/**
+ * hostapd_maclist_found - Find a MAC address from a list
+ * @list: MAC address list
+ * @num_entries: Number of addresses in the list
+ * @addr: Address to search for
+ * @vlan_id: Buffer for returning VLAN ID or %NULL if not needed
+ * Returns: 1 if address is in the list or 0 if not.
+ *
+ * Perform a binary search for given MAC address from a pre-sorted list.
+ */
+int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
+			  const u8 *addr, int *vlan_id)
+{
+	int start, end, middle, res;
+
+	start = 0;
+	end = num_entries - 1;
+
+	while (start <= end) {
+		middle = (start + end) / 2;
+		res = os_memcmp(list[middle].addr, addr, ETH_ALEN);
+		if (res == 0) {
+			if (vlan_id)
+				*vlan_id = list[middle].vlan_id;
+			return 1;
+		}
+		if (res < 0)
+			start = middle + 1;
+		else
+			end = middle - 1;
+	}
+
+	return 0;
+}
+
+
+int hostapd_rate_found(int *list, int rate)
+{
+	int i;
+
+	if (list == NULL)
+		return 0;
+
+	for (i = 0; list[i] >= 0; i++)
+		if (list[i] == rate)
+			return 1;
+
+	return 0;
+}
+
+
+int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id)
+{
+	struct hostapd_vlan *v = vlan;
+	while (v) {
+		if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD)
+			return 1;
+		v = v->next;
+	}
+	return 0;
+}
+
+
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
+{
+	struct hostapd_vlan *v = vlan;
+	while (v) {
+		if (v->vlan_id == vlan_id)
+			return v->ifname;
+		v = v->next;
+	}
+	return NULL;
+}
+
+
+const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
+			   const u8 *addr, const u8 *p2p_dev_addr,
+			   const u8 *prev_psk)
+{
+	struct hostapd_wpa_psk *psk;
+	int next_ok = prev_psk == NULL;
+
+	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);
+		addr = NULL; /* Use P2P Device Address for matching */
+	} else {
+		wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
+			   " prev_psk=%p",
+			   MAC2STR(addr), prev_psk);
+	}
+
+	for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) {
+		if (next_ok &&
+		    (psk->group ||
+		     (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) ||
+		     (!addr && p2p_dev_addr &&
+		      os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
+		      0)))
+			return psk->psk;
+
+		if (psk->psk == prev_psk)
+			next_ok = 1;
+	}
+
+	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 capabilities");
+	}
+
+	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_CIPHER_GCMP)))) {
+		wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
+			   "WPA2/CCMP/GCMP 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;
+}
+
+
+static int hostapd_config_check_cw(struct hostapd_config *conf, int queue)
+{
+	int tx_cwmin = conf->tx_queue[queue].cwmin;
+	int tx_cwmax = conf->tx_queue[queue].cwmax;
+	int ac_cwmin = conf->wmm_ac_params[queue].cwmin;
+	int ac_cwmax = conf->wmm_ac_params[queue].cwmax;
+
+	if (tx_cwmin > tx_cwmax) {
+		wpa_printf(MSG_ERROR,
+			   "Invalid TX queue cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)",
+			   tx_cwmin, tx_cwmax);
+		return -1;
+	}
+	if (ac_cwmin > ac_cwmax) {
+		wpa_printf(MSG_ERROR,
+			   "Invalid WMM AC cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)",
+			   ac_cwmin, ac_cwmax);
+		return -1;
+	}
+	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 < NUM_TX_QUEUES; i++) {
+		if (hostapd_config_check_cw(conf, i))
+			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 (full_config && bss->default_wep_key_len) {
+			cipher = bss->default_wep_key_len >= 13 ?
+				WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
+		} else if (full_config && bss->ssid.wep.keys_set) {
+			if (bss->ssid.wep.len[0] >= 13)
+				cipher = WPA_CIPHER_WEP104;
+			else
+				cipher = WPA_CIPHER_WEP40;
+		}
+		bss->wpa_group = cipher;
+		bss->wpa_pairwise = cipher;
+		bss->rsn_pairwise = cipher;
+		if (full_config)
+			bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+	} 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;
+		if (full_config)
+			bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
+	} else if (bss->osen) {
+		bss->ssid.security_policy = SECURITY_OSEN;
+		bss->wpa_group = WPA_CIPHER_CCMP;
+		bss->wpa_pairwise = 0;
+		bss->rsn_pairwise = WPA_CIPHER_CCMP;
+	} else {
+		bss->ssid.security_policy = SECURITY_PLAINTEXT;
+		if (full_config) {
+			bss->wpa_group = WPA_CIPHER_NONE;
+			bss->wpa_pairwise = WPA_CIPHER_NONE;
+			bss->rsn_pairwise = WPA_CIPHER_NONE;
+			bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
+		}
+	}
+}
diff --git a/hostap/src/ap/ap_config.h b/hostap/src/ap/ap_config.h
new file mode 100644
index 0000000..de470a9
--- /dev/null
+++ b/hostap/src/ap/ap_config.h
@@ -0,0 +1,692 @@
+/*
+ * hostapd / Configuration definitions and helpers functions
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HOSTAPD_CONFIG_H
+#define HOSTAPD_CONFIG_H
+
+#include "common/defs.h"
+#include "ip_addr.h"
+#include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wps/wps.h"
+#include "fst/fst.h"
+
+/**
+ * mesh_conf - local MBSS state and settings
+ */
+struct mesh_conf {
+	u8 meshid[32];
+	u8 meshid_len;
+	/* Active Path Selection Protocol Identifier */
+	u8 mesh_pp_id;
+	/* Active Path Selection Metric Identifier */
+	u8 mesh_pm_id;
+	/* Congestion Control Mode Identifier */
+	u8 mesh_cc_id;
+	/* Synchronization Protocol Identifier */
+	u8 mesh_sp_id;
+	/* Authentication Protocol Identifier */
+	u8 mesh_auth_id;
+	u8 *rsn_ie;
+	int rsn_ie_len;
+#define MESH_CONF_SEC_NONE BIT(0)
+#define MESH_CONF_SEC_AUTH BIT(1)
+#define MESH_CONF_SEC_AMPE BIT(2)
+	unsigned int security;
+	int dot11MeshMaxRetries;
+	int dot11MeshRetryTimeout; /* msec */
+	int dot11MeshConfirmTimeout; /* msec */
+	int dot11MeshHoldingTimeout; /* msec */
+};
+
+#define MAX_STA_COUNT 2007
+#define MAX_VLAN_ID 4094
+
+typedef u8 macaddr[ETH_ALEN];
+
+struct mac_acl_entry {
+	macaddr addr;
+	int vlan_id;
+};
+
+struct hostapd_radius_servers;
+struct ft_remote_r0kh;
+struct ft_remote_r1kh;
+
+#define NUM_WEP_KEYS 4
+struct hostapd_wep_keys {
+	u8 idx;
+	u8 *key[NUM_WEP_KEYS];
+	size_t len[NUM_WEP_KEYS];
+	int keys_set;
+	size_t default_len; /* key length used for dynamic key generation */
+};
+
+typedef enum hostap_security_policy {
+	SECURITY_PLAINTEXT = 0,
+	SECURITY_STATIC_WEP = 1,
+	SECURITY_IEEE_802_1X = 2,
+	SECURITY_WPA_PSK = 3,
+	SECURITY_WPA = 4,
+	SECURITY_OSEN = 5
+} secpolicy;
+
+struct hostapd_ssid {
+	u8 ssid[SSID_MAX_LEN];
+	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;
+
+	struct hostapd_wpa_psk *wpa_psk;
+	char *wpa_passphrase;
+	char *wpa_psk_file;
+
+	struct hostapd_wep_keys wep;
+
+#define DYNAMIC_VLAN_DISABLED 0
+#define DYNAMIC_VLAN_OPTIONAL 1
+#define DYNAMIC_VLAN_REQUIRED 2
+	int dynamic_vlan;
+#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
+#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
+#define DYNAMIC_VLAN_NAMING_END 2
+	int vlan_naming;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	char *vlan_tagged_interface;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+};
+
+
+#define VLAN_ID_WILDCARD -1
+
+struct hostapd_vlan {
+	struct hostapd_vlan *next;
+	int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
+	char ifname[IFNAMSIZ + 1];
+	int configured;
+	int dynamic_vlan;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+
+#define DVLAN_CLEAN_WLAN_PORT	0x8
+	int clean;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+};
+
+#define PMK_LEN 32
+struct hostapd_sta_wpa_psk_short {
+	struct hostapd_sta_wpa_psk_short *next;
+	u8 psk[PMK_LEN];
+};
+
+struct hostapd_wpa_psk {
+	struct hostapd_wpa_psk *next;
+	int group;
+	u8 psk[PMK_LEN];
+	u8 addr[ETH_ALEN];
+	u8 p2p_dev_addr[ETH_ALEN];
+};
+
+struct hostapd_eap_user {
+	struct hostapd_eap_user *next;
+	u8 *identity;
+	size_t identity_len;
+	struct {
+		int vendor;
+		u32 method;
+	} methods[EAP_MAX_METHODS];
+	u8 *password;
+	size_t password_len;
+	int phase2;
+	int force_version;
+	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 {
+	u8 type;
+	struct wpabuf *val;
+	struct hostapd_radius_attr *next;
+};
+
+
+#define NUM_TX_QUEUES 4
+
+struct hostapd_tx_queue_params {
+	int aifs;
+	int cwmin;
+	int cwmax;
+	int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */
+};
+
+
+#define MAX_ROAMING_CONSORTIUM_LEN 15
+
+struct hostapd_roaming_consortium {
+	u8 len;
+	u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
+};
+
+struct hostapd_lang_string {
+	u8 lang[3];
+	u8 name_len;
+	u8 name[252];
+};
+
+#define MAX_NAI_REALMS 10
+#define MAX_NAI_REALMLEN 255
+#define MAX_NAI_EAP_METHODS 5
+#define MAX_NAI_AUTH_TYPES 4
+struct hostapd_nai_realm_data {
+	u8 encoding;
+	char realm_buf[MAX_NAI_REALMLEN + 1];
+	char *realm[MAX_NAI_REALMS];
+	u8 eap_method_count;
+	struct hostapd_nai_realm_eap {
+		u8 eap_method;
+		u8 num_auths;
+		u8 auth_id[MAX_NAI_AUTH_TYPES];
+		u8 auth_val[MAX_NAI_AUTH_TYPES];
+	} eap_method[MAX_NAI_EAP_METHODS];
+};
+
+/**
+ * struct hostapd_bss_config - Per-BSS configuration
+ */
+struct hostapd_bss_config {
+	char iface[IFNAMSIZ + 1];
+	char bridge[IFNAMSIZ + 1];
+	char vlan_bridge[IFNAMSIZ + 1];
+	char wds_bridge[IFNAMSIZ + 1];
+
+	enum hostapd_logger_level logger_syslog_level, logger_stdout_level;
+
+	unsigned int logger_syslog; /* module bitfield */
+	unsigned int logger_stdout; /* module bitfield */
+
+	int max_num_sta; /* maximum number of STAs in station table */
+
+	int dtim_period;
+	int bss_load_update_period;
+
+	int ieee802_1x; /* use IEEE 802.1X */
+	int eapol_version;
+	int eap_server; /* Use internal EAP server instead of external
+			 * RADIUS server */
+	struct hostapd_eap_user *eap_user;
+	char *eap_user_sqlite;
+	char *eap_sim_db;
+	int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
+	struct hostapd_ip_addr own_ip_addr;
+	char *nas_identifier;
+	struct hostapd_radius_servers *radius;
+	int acct_interim_interval;
+	int radius_request_cui;
+	struct hostapd_radius_attr *radius_auth_req_attr;
+	struct hostapd_radius_attr *radius_acct_req_attr;
+	int radius_das_port;
+	unsigned int radius_das_time_window;
+	int radius_das_require_event_timestamp;
+	struct hostapd_ip_addr radius_das_client_addr;
+	u8 *radius_das_shared_secret;
+	size_t radius_das_shared_secret_len;
+
+	struct hostapd_ssid ssid;
+
+	char *eap_req_id_text; /* optional displayable message sent with
+				* EAP Request-Identity */
+	size_t eap_req_id_text_len;
+	int eapol_key_index_workaround;
+
+	size_t default_wep_key_len;
+	int individual_wep_key_len;
+	int wep_rekeying_period;
+	int broadcast_key_idx_min, broadcast_key_idx_max;
+	int eap_reauth_period;
+	int erp_send_reauth_start;
+	char *erp_domain;
+
+	int ieee802_11f; /* use IEEE 802.11f (IAPP) */
+	char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
+					* frames */
+
+	enum {
+		ACCEPT_UNLESS_DENIED = 0,
+		DENY_UNLESS_ACCEPTED = 1,
+		USE_EXTERNAL_RADIUS_AUTH = 2
+	} macaddr_acl;
+	struct mac_acl_entry *accept_mac;
+	int num_accept_mac;
+	struct mac_acl_entry *deny_mac;
+	int num_deny_mac;
+	int wds_sta;
+	int isolate;
+	int start_disabled;
+
+	int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
+			* algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
+
+	int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
+	int wpa_key_mgmt;
+#ifdef CONFIG_IEEE80211W
+	enum mfp_options ieee80211w;
+	int group_mgmt_cipher;
+	/* dot11AssociationSAQueryMaximumTimeout (in TUs) */
+	unsigned int assoc_sa_query_max_timeout;
+	/* dot11AssociationSAQueryRetryTimeout (in TUs) */
+	int assoc_sa_query_retry_timeout;
+#endif /* CONFIG_IEEE80211W */
+	enum {
+		PSK_RADIUS_IGNORED = 0,
+		PSK_RADIUS_ACCEPTED = 1,
+		PSK_RADIUS_REQUIRED = 2
+	} wpa_psk_radius;
+	int wpa_pairwise;
+	int wpa_group;
+	int wpa_group_rekey;
+	int wpa_strict_rekey;
+	int wpa_gmk_rekey;
+	int wpa_ptk_rekey;
+	int rsn_pairwise;
+	int rsn_preauth;
+	char *rsn_preauth_interfaces;
+	int peerkey;
+
+#ifdef CONFIG_IEEE80211R
+	/* IEEE 802.11r - Fast BSS Transition */
+	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+	u8 r1_key_holder[FT_R1KH_ID_LEN];
+	u32 r0_key_lifetime;
+	u32 reassociation_deadline;
+	struct ft_remote_r0kh *r0kh_list;
+	struct ft_remote_r1kh *r1kh_list;
+	int pmk_r1_push;
+	int ft_over_ds;
+#endif /* CONFIG_IEEE80211R */
+
+	char *ctrl_interface; /* directory for UNIX domain sockets */
+#ifndef CONFIG_NATIVE_WINDOWS
+	gid_t ctrl_interface_gid;
+#endif /* CONFIG_NATIVE_WINDOWS */
+	int ctrl_interface_gid_set;
+
+	char *ca_cert;
+	char *server_cert;
+	char *private_key;
+	char *private_key_passwd;
+	int check_crl;
+	unsigned int tls_session_lifetime;
+	char *ocsp_stapling_response;
+	char *dh_file;
+	char *openssl_ciphers;
+	u8 *pac_opaque_encr_key;
+	u8 *eap_fast_a_id;
+	size_t eap_fast_a_id_len;
+	char *eap_fast_a_id_info;
+	int eap_fast_prov;
+	int pac_key_lifetime;
+	int pac_key_refresh_time;
+	int eap_sim_aka_result_ind;
+	int tnc;
+	int fragment_size;
+	u16 pwd_group;
+
+	char *radius_server_clients;
+	int radius_server_auth_port;
+	int radius_server_acct_port;
+	int radius_server_ipv6;
+
+	int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group
+				 * address instead of individual address
+				 * (for driver_wired.c).
+				 */
+
+	int ap_max_inactivity;
+	int ignore_broadcast_ssid;
+
+	int wmm_enabled;
+	int wmm_uapsd;
+
+	struct hostapd_vlan *vlan;
+
+	macaddr bssid;
+
+	/*
+	 * Maximum listen interval that STAs can use when associating with this
+	 * BSS. If a STA tries to use larger value, the association will be
+	 * denied with status code 51.
+	 */
+	u16 max_listen_interval;
+
+	int disable_pmksa_caching;
+	int okc; /* Opportunistic Key Caching */
+
+	int wps_state;
+#ifdef CONFIG_WPS
+	int wps_independent;
+	int ap_setup_locked;
+	u8 uuid[16];
+	char *wps_pin_requests;
+	char *device_name;
+	char *manufacturer;
+	char *model_name;
+	char *model_number;
+	char *serial_number;
+	u8 device_type[WPS_DEV_TYPE_LEN];
+	char *config_methods;
+	u8 os_version[4];
+	char *ap_pin;
+	int skip_cred_build;
+	u8 *extra_cred;
+	size_t extra_cred_len;
+	int wps_cred_processing;
+	int force_per_enrollee_psk;
+	u8 *ap_settings;
+	size_t ap_settings_len;
+	char *upnp_iface;
+	char *friendly_name;
+	char *manufacturer_url;
+	char *model_description;
+	char *model_url;
+	char *upc;
+	struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+	int wps_nfc_pw_from_config;
+	int wps_nfc_dev_pw_id;
+	struct wpabuf *wps_nfc_dh_pubkey;
+	struct wpabuf *wps_nfc_dh_privkey;
+	struct wpabuf *wps_nfc_dev_pw;
+#endif /* CONFIG_WPS */
+	int pbc_in_m1;
+	char *server_id;
+
+#define P2P_ENABLED BIT(0)
+#define P2P_GROUP_OWNER BIT(1)
+#define P2P_GROUP_FORMATION BIT(2)
+#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;
+
+#define TDLS_PROHIBIT BIT(0)
+#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1)
+	int tdls;
+	int disable_11n;
+	int disable_11ac;
+
+	/* IEEE 802.11v */
+	int time_advertisement;
+	char *time_zone;
+	int wnm_sleep_mode;
+	int bss_transition;
+
+	/* IEEE 802.11u - Interworking */
+	int interworking;
+	int access_network_type;
+	int internet;
+	int asra;
+	int esr;
+	int uesa;
+	int venue_info_set;
+	u8 venue_group;
+	u8 venue_type;
+	u8 hessid[ETH_ALEN];
+
+	/* IEEE 802.11u - Roaming Consortium list */
+	unsigned int roaming_consortium_count;
+	struct hostapd_roaming_consortium *roaming_consortium;
+
+	/* IEEE 802.11u - Venue Name duples */
+	unsigned int venue_name_count;
+	struct hostapd_lang_string *venue_name;
+
+	/* IEEE 802.11u - Network Authentication Type */
+	u8 *network_auth_type;
+	size_t network_auth_type_len;
+
+	/* IEEE 802.11u - IP Address Type Availability */
+	u8 ipaddr_type_availability;
+	u8 ipaddr_type_configured;
+
+	/* IEEE 802.11u - 3GPP Cellular Network */
+	u8 *anqp_3gpp_cell_net;
+	size_t anqp_3gpp_cell_net_len;
+
+	/* IEEE 802.11u - Domain Name */
+	u8 *domain_name;
+	size_t domain_name_len;
+
+	unsigned int nai_realm_count;
+	struct hostapd_nai_realm_data *nai_realm_data;
+
+	u16 gas_comeback_delay;
+	int gas_frag_limit;
+
+	u8 qos_map_set[16 + 2 * 21];
+	unsigned int qos_map_set_len;
+
+	int osen;
+	int proxy_arp;
+	int na_mcast_to_ucast;
+#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;
+	u8 *hs20_connection_capability;
+	size_t hs20_connection_capability_len;
+	u8 *hs20_operating_class;
+	u8 hs20_operating_class_len;
+	struct hs20_icon {
+		u16 width;
+		u16 height;
+		char language[3];
+		char type[256];
+		char name[256];
+		char file[256];
+	} *hs20_icons;
+	size_t hs20_icons_count;
+	u8 osu_ssid[SSID_MAX_LEN];
+	size_t osu_ssid_len;
+	struct hs20_osu_provider {
+		unsigned int friendly_name_count;
+		struct hostapd_lang_string *friendly_name;
+		char *server_uri;
+		int *method_list;
+		char **icons;
+		size_t icons_count;
+		char *osu_nai;
+		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_*) */
+
+#ifdef CONFIG_RADIUS_TEST
+	char *dump_msk_file;
+#endif /* CONFIG_RADIUS_TEST */
+
+	struct wpabuf *vendor_elements;
+
+	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;
+	struct wpabuf *own_ie_override;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#define MESH_ENABLED BIT(0)
+	int mesh;
+
+	int radio_measurements;
+
+	int vendor_vht;
+
+	char *no_probe_resp_if_seen_on;
+	char *no_auth_if_seen_on;
+};
+
+
+/**
+ * struct hostapd_config - Per-radio interface configuration
+ */
+struct hostapd_config {
+	struct hostapd_bss_config **bss, *last_bss;
+	size_t num_bss;
+
+	u16 beacon_int;
+	int rts_threshold;
+	int fragm_threshold;
+	u8 send_probe_response;
+	u8 channel;
+	u8 acs;
+	struct wpa_freq_range_list acs_ch_list;
+	enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
+	enum {
+		LONG_PREAMBLE = 0,
+		SHORT_PREAMBLE = 1
+	} preamble;
+
+	int *supported_rates;
+	int *basic_rates;
+
+	const struct wpa_driver_ops *driver;
+	char *driver_params;
+
+	int ap_table_max_size;
+	int ap_table_expiration_time;
+
+	unsigned int track_sta_max_num;
+	unsigned int track_sta_max_age;
+
+	char country[3]; /* first two octets: country code as described in
+			  * ISO/IEC 3166-1. Third octet:
+			  * ' ' (ascii 32): all environments
+			  * 'O': Outdoor environemnt only
+			  * 'I': Indoor environment only
+			  */
+
+	int ieee80211d;
+
+	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];
+
+	/*
+	 * WMM AC parameters, in same order as 802.1D, i.e.
+	 * 0 = BE (best effort)
+	 * 1 = BK (background)
+	 * 2 = VI (video)
+	 * 3 = VO (voice)
+	 */
+	struct hostapd_wmm_ac_params wmm_ac_params[4];
+
+	int ht_op_mode_fixed;
+	u16 ht_capab;
+	int ieee80211n;
+	int secondary_channel;
+	int no_pri_sec_switch;
+	int require_ht;
+	int obss_interval;
+	u32 vht_capab;
+	int ieee80211ac;
+	int require_vht;
+	u8 vht_oper_chwidth;
+	u8 vht_oper_centr_freq_seg0_idx;
+	u8 vht_oper_centr_freq_seg1_idx;
+
+#ifdef CONFIG_FST
+	struct fst_iface_cfg fst_cfg;
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_P2P
+	u8 p2p_go_ctwindow;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	double ignore_probe_probability;
+	double ignore_auth_probability;
+	double ignore_assoc_probability;
+	double ignore_reassoc_probability;
+	double corrupt_gtk_rekey_mic_probability;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_ACS
+	unsigned int acs_num_scans;
+	struct acs_bias {
+		int channel;
+		double bias;
+	} *acs_chan_bias;
+	unsigned int num_acs_chan_bias;
+#endif /* CONFIG_ACS */
+};
+
+
+int hostapd_mac_comp(const void *a, const void *b);
+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_clear_wpa_psk(struct hostapd_wpa_psk **p);
+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);
+const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
+			   const u8 *addr, const u8 *p2p_dev_addr,
+			   const u8 *prev_psk);
+int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
+int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id);
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
+					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/hostap/src/ap/ap_drv_ops.c b/hostap/src/ap/ap_drv_ops.c
new file mode 100644
index 0000000..6cafcb7
--- /dev/null
+++ b/hostap/src/ap/ap_drv_ops.c
@@ -0,0 +1,842 @@
+/*
+ * hostapd - Driver operations
+ * Copyright (c) 2009-2010, 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 "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
+#include "wps/wps.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "p2p_hostapd.h"
+#include "hs20.h"
+#include "ap_drv_ops.h"
+
+
+u32 hostapd_sta_flags_to_drv(u32 flags)
+{
+	int res = 0;
+	if (flags & WLAN_STA_AUTHORIZED)
+		res |= WPA_STA_AUTHORIZED;
+	if (flags & WLAN_STA_WMM)
+		res |= WPA_STA_WMM;
+	if (flags & WLAN_STA_SHORT_PREAMBLE)
+		res |= WPA_STA_SHORT_PREAMBLE;
+	if (flags & WLAN_STA_MFP)
+		res |= WPA_STA_MFP;
+	return res;
+}
+
+
+int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
+			       struct wpabuf **beacon_ret,
+			       struct wpabuf **proberesp_ret,
+			       struct wpabuf **assocresp_ret)
+{
+	struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL;
+	u8 buf[200], *pos;
+
+	*beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
+
+	pos = buf;
+	pos = hostapd_eid_time_adv(hapd, pos);
+	if (pos != buf) {
+		if (wpabuf_resize(&beacon, pos - buf) != 0)
+			goto fail;
+		wpabuf_put_data(beacon, buf, pos - buf);
+	}
+	pos = hostapd_eid_time_zone(hapd, pos);
+	if (pos != buf) {
+		if (wpabuf_resize(&proberesp, pos - buf) != 0)
+			goto fail;
+		wpabuf_put_data(proberesp, buf, pos - buf);
+	}
+
+	pos = buf;
+	pos = hostapd_eid_ext_capab(hapd, pos);
+	if (pos != buf) {
+		if (wpabuf_resize(&assocresp, pos - buf) != 0)
+			goto fail;
+		wpabuf_put_data(assocresp, buf, pos - buf);
+	}
+	pos = hostapd_eid_interworking(hapd, pos);
+	pos = hostapd_eid_adv_proto(hapd, pos);
+	pos = hostapd_eid_roaming_consortium(hapd, pos);
+	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);
+	}
+
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies) {
+		size_t add = wpabuf_len(hapd->iface->fst_ies);
+
+		if (wpabuf_resize(&beacon, add) < 0)
+			goto fail;
+		wpabuf_put_buf(beacon, hapd->iface->fst_ies);
+		if (wpabuf_resize(&proberesp, add) < 0)
+			goto fail;
+		wpabuf_put_buf(proberesp, hapd->iface->fst_ies);
+		if (wpabuf_resize(&assocresp, add) < 0)
+			goto fail;
+		wpabuf_put_buf(assocresp, hapd->iface->fst_ies);
+	}
+#endif /* CONFIG_FST */
+
+	if (hapd->wps_beacon_ie) {
+		if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) <
+		    0)
+			goto fail;
+		wpabuf_put_buf(beacon, hapd->wps_beacon_ie);
+	}
+
+	if (hapd->wps_probe_resp_ie) {
+		if (wpabuf_resize(&proberesp,
+				  wpabuf_len(hapd->wps_probe_resp_ie)) < 0)
+			goto fail;
+		wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie);
+	}
+
+#ifdef CONFIG_P2P
+	if (hapd->p2p_beacon_ie) {
+		if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) <
+		    0)
+			goto fail;
+		wpabuf_put_buf(beacon, hapd->p2p_beacon_ie);
+	}
+
+	if (hapd->p2p_probe_resp_ie) {
+		if (wpabuf_resize(&proberesp,
+				  wpabuf_len(hapd->p2p_probe_resp_ie)) < 0)
+			goto fail;
+		wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie);
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_P2P_MANAGER
+	if (hapd->conf->p2p & P2P_MANAGE) {
+		if (wpabuf_resize(&beacon, 100) == 0) {
+			u8 *start, *p;
+			start = wpabuf_put(beacon, 0);
+			p = hostapd_eid_p2p_manage(hapd, start);
+			wpabuf_put(beacon, p - start);
+		}
+
+		if (wpabuf_resize(&proberesp, 100) == 0) {
+			u8 *start, *p;
+			start = wpabuf_put(proberesp, 0);
+			p = hostapd_eid_p2p_manage(hapd, start);
+			wpabuf_put(proberesp, p - start);
+		}
+	}
+#endif /* CONFIG_P2P_MANAGER */
+
+#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_WPS */
+
+#ifdef CONFIG_P2P_MANAGER
+	if (hapd->conf->p2p & P2P_MANAGE) {
+		if (wpabuf_resize(&assocresp, 100) == 0) {
+			u8 *start, *p;
+			start = wpabuf_put(assocresp, 0);
+			p = hostapd_eid_p2p_manage(hapd, start);
+			wpabuf_put(assocresp, p - start);
+		}
+	}
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (hapd->p2p_group) {
+		struct wpabuf *a;
+		a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS);
+		if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
+			wpabuf_put_buf(assocresp, a);
+		wpabuf_free(a);
+	}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+#ifdef CONFIG_HS20
+	pos = buf;
+	pos = hostapd_eid_hs20_indication(hapd, pos);
+	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);
+	}
+
+	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) {
+		size_t add = wpabuf_len(hapd->conf->vendor_elements);
+		if (wpabuf_resize(&beacon, add) == 0)
+			wpabuf_put_buf(beacon, hapd->conf->vendor_elements);
+		if (wpabuf_resize(&proberesp, add) == 0)
+			wpabuf_put_buf(proberesp, hapd->conf->vendor_elements);
+	}
+
+	*beacon_ret = beacon;
+	*proberesp_ret = proberesp;
+	*assocresp_ret = assocresp;
+
+	return 0;
+
+fail:
+	wpabuf_free(beacon);
+	wpabuf_free(proberesp);
+	wpabuf_free(assocresp);
+	return -1;
+}
+
+
+void hostapd_free_ap_extra_ies(struct hostapd_data *hapd,
+			       struct wpabuf *beacon,
+			       struct wpabuf *proberesp,
+			       struct wpabuf *assocresp)
+{
+	wpabuf_free(beacon);
+	wpabuf_free(proberesp);
+	wpabuf_free(assocresp);
+}
+
+
+int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd)
+{
+	if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
+		return 0;
+
+	return hapd->driver->set_ap_wps_ie(hapd->drv_priv, NULL, NULL, NULL);
+}
+
+
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
+{
+	struct wpabuf *beacon, *proberesp, *assocresp;
+	int ret;
+
+	if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
+		return 0;
+
+	if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
+	    0)
+		return -1;
+
+	ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp,
+					  assocresp);
+
+	hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
+
+	return ret;
+}
+
+
+int hostapd_set_authorized(struct hostapd_data *hapd,
+			   struct sta_info *sta, int authorized)
+{
+	if (authorized) {
+		return hostapd_sta_set_flags(hapd, sta->addr,
+					     hostapd_sta_flags_to_drv(
+						     sta->flags),
+					     WPA_STA_AUTHORIZED, ~0);
+	}
+
+	return hostapd_sta_set_flags(hapd, sta->addr,
+				     hostapd_sta_flags_to_drv(sta->flags),
+				     0, ~WPA_STA_AUTHORIZED);
+}
+
+
+int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int set_flags, total_flags, flags_and, flags_or;
+	total_flags = hostapd_sta_flags_to_drv(sta->flags);
+	set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP;
+	if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
+	     sta->auth_alg == WLAN_AUTH_FT) &&
+	    sta->flags & WLAN_STA_AUTHORIZED)
+		set_flags |= WPA_STA_AUTHORIZED;
+	flags_or = total_flags & set_flags;
+	flags_and = total_flags | ~set_flags;
+	return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
+				     flags_or, flags_and);
+}
+
+
+int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+			      int enabled)
+{
+	struct wpa_bss_params params;
+	os_memset(&params, 0, sizeof(params));
+	params.ifname = ifname;
+	params.enabled = enabled;
+	if (enabled) {
+		params.wpa = hapd->conf->wpa;
+		params.ieee802_1x = hapd->conf->ieee802_1x;
+		params.wpa_group = hapd->conf->wpa_group;
+		if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
+		    (WPA_PROTO_WPA | WPA_PROTO_RSN))
+			params.wpa_pairwise = hapd->conf->wpa_pairwise |
+				hapd->conf->rsn_pairwise;
+		else if (hapd->conf->wpa & WPA_PROTO_RSN)
+			params.wpa_pairwise = hapd->conf->rsn_pairwise;
+		else if (hapd->conf->wpa & WPA_PROTO_WPA)
+			params.wpa_pairwise = hapd->conf->wpa_pairwise;
+		params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
+		params.rsn_preauth = hapd->conf->rsn_preauth;
+#ifdef CONFIG_IEEE80211W
+		params.ieee80211w = hapd->conf->ieee80211w;
+#endif /* CONFIG_IEEE80211W */
+	}
+	return hostapd_set_ieee8021x(hapd, &params);
+}
+
+
+int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
+{
+	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, 0);
+}
+
+
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
+{
+	return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname);
+}
+
+
+int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+			const u8 *addr, int aid, int val)
+{
+	const char *bridge = NULL;
+
+	if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
+		return -1;
+	if (hapd->conf->wds_bridge[0])
+		bridge = hapd->conf->wds_bridge;
+	else if (hapd->conf->bridge[0])
+		bridge = hapd->conf->bridge;
+	return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
+					 bridge, ifname_wds);
+}
+
+
+int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
+			 u16 auth_alg)
+{
+	if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL)
+		return 0;
+	return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
+}
+
+
+int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
+		     u16 seq, u16 status, const u8 *ie, size_t len)
+{
+	if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
+		return 0;
+	return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr,
+				      seq, status, ie, len);
+}
+
+
+int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
+		      int reassoc, u16 status, const u8 *ie, size_t len)
+{
+	if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL)
+		return 0;
+	return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr,
+				       reassoc, status, ie, len);
+}
+
+
+int hostapd_sta_add(struct hostapd_data *hapd,
+		    const u8 *addr, u16 aid, u16 capability,
+		    const u8 *supp_rates, size_t supp_rates_len,
+		    u16 listen_interval,
+		    const struct ieee80211_ht_capabilities *ht_capab,
+		    const struct ieee80211_vht_capabilities *vht_capab,
+		    u32 flags, u8 qosinfo, u8 vht_opmode)
+{
+	struct hostapd_sta_add_params params;
+
+	if (hapd->driver == NULL)
+		return 0;
+	if (hapd->driver->sta_add == NULL)
+		return 0;
+
+	os_memset(&params, 0, sizeof(params));
+	params.addr = addr;
+	params.aid = aid;
+	params.capability = capability;
+	params.supp_rates = supp_rates;
+	params.supp_rates_len = supp_rates_len;
+	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);
+}
+
+
+int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
+		      u8 *tspec_ie, size_t tspec_ielen)
+{
+	if (hapd->driver == NULL || hapd->driver->add_tspec == NULL)
+		return 0;
+	return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie,
+				       tspec_ielen);
+}
+
+
+int hostapd_set_privacy(struct hostapd_data *hapd, int enabled)
+{
+	if (hapd->driver == NULL || hapd->driver->set_privacy == NULL)
+		return 0;
+	return hapd->driver->set_privacy(hapd->drv_priv, enabled);
+}
+
+
+int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
+			     size_t elem_len)
+{
+	if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL)
+		return 0;
+	return hapd->driver->set_generic_elem(hapd->drv_priv, elem, elem_len);
+}
+
+
+int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len)
+{
+	if (hapd->driver == NULL || hapd->driver->hapd_get_ssid == NULL)
+		return 0;
+	return hapd->driver->hapd_get_ssid(hapd->drv_priv, buf, len);
+}
+
+
+int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
+{
+	if (hapd->driver == NULL || hapd->driver->hapd_set_ssid == NULL)
+		return 0;
+	return hapd->driver->hapd_set_ssid(hapd->drv_priv, buf, len);
+}
+
+
+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, 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, use_existing);
+}
+
+
+int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+		      const char *ifname)
+{
+	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);
+}
+
+
+int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+			  struct wpa_bss_params *params)
+{
+	if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL)
+		return 0;
+	return hapd->driver->set_ieee8021x(hapd->drv_priv, params);
+}
+
+
+int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+		       const u8 *addr, int idx, u8 *seq)
+{
+	if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL)
+		return 0;
+	return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx,
+					seq);
+}
+
+
+int hostapd_flush(struct hostapd_data *hapd)
+{
+	if (hapd->driver == NULL || hapd->driver->flush == NULL)
+		return 0;
+	return hapd->driver->flush(hapd->drv_priv);
+}
+
+
+int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
+		     int freq, int channel, int ht_enabled, int vht_enabled,
+		     int sec_channel_offset, int vht_oper_chwidth,
+		     int 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 ?
+				    hapd->iface->current_mode->vht_capab : 0))
+		return -1;
+
+	if (hapd->driver == NULL)
+		return 0;
+	if (hapd->driver->set_freq == NULL)
+		return 0;
+	return hapd->driver->set_freq(hapd->drv_priv, &data);
+}
+
+int hostapd_set_rts(struct hostapd_data *hapd, int rts)
+{
+	if (hapd->driver == NULL || hapd->driver->set_rts == NULL)
+		return 0;
+	return hapd->driver->set_rts(hapd->drv_priv, rts);
+}
+
+
+int hostapd_set_frag(struct hostapd_data *hapd, int frag)
+{
+	if (hapd->driver == NULL || hapd->driver->set_frag == NULL)
+		return 0;
+	return hapd->driver->set_frag(hapd->drv_priv, frag);
+}
+
+
+int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
+			  int total_flags, int flags_or, int flags_and)
+{
+	if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL)
+		return 0;
+	return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags,
+					   flags_or, flags_and);
+}
+
+
+int hostapd_set_country(struct hostapd_data *hapd, const char *country)
+{
+	if (hapd->driver == NULL ||
+	    hapd->driver->set_country == NULL)
+		return 0;
+	return hapd->driver->set_country(hapd->drv_priv, country);
+}
+
+
+int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
+				int cw_min, int cw_max, int burst_time)
+{
+	if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL)
+		return 0;
+	return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs,
+						 cw_min, cw_max, burst_time);
+}
+
+
+struct hostapd_hw_modes *
+hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+			    u16 *flags)
+{
+	if (hapd->driver == NULL ||
+	    hapd->driver->get_hw_feature_data == NULL)
+		return NULL;
+	return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
+						 flags);
+}
+
+
+int hostapd_driver_commit(struct hostapd_data *hapd)
+{
+	if (hapd->driver == NULL || hapd->driver->commit == NULL)
+		return 0;
+	return hapd->driver->commit(hapd->drv_priv);
+}
+
+
+int hostapd_drv_none(struct hostapd_data *hapd)
+{
+	return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
+}
+
+
+int hostapd_driver_scan(struct hostapd_data *hapd,
+			struct wpa_driver_scan_params *params)
+{
+	if (hapd->driver && hapd->driver->scan2)
+		return hapd->driver->scan2(hapd->drv_priv, params);
+	return -1;
+}
+
+
+struct wpa_scan_results * hostapd_driver_get_scan_results(
+	struct hostapd_data *hapd)
+{
+	if (hapd->driver && hapd->driver->get_scan_results2)
+		return hapd->driver->get_scan_results2(hapd->drv_priv);
+	return NULL;
+}
+
+
+int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
+			   int duration)
+{
+	if (hapd->driver && hapd->driver->set_noa)
+		return hapd->driver->set_noa(hapd->drv_priv, count, start,
+					     duration);
+	return -1;
+}
+
+
+int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd,
+			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)
+{
+	if (hapd->driver == NULL || hapd->driver->set_key == NULL)
+		return 0;
+	return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
+				     key_idx, set_tx, seq, seq_len, key,
+				     key_len);
+}
+
+
+int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+			  const void *msg, size_t len, int noack)
+{
+	if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
+		return 0;
+	return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0);
+}
+
+
+int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+			   const u8 *addr, int reason)
+{
+	if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL)
+		return 0;
+	return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
+					reason);
+}
+
+
+int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+			     const u8 *addr, int reason)
+{
+	if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL)
+		return 0;
+	return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
+					  reason);
+}
+
+
+int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper,
+			 const u8 *peer, u8 *buf, u16 *buf_len)
+{
+	if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL)
+		return -1;
+	return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
+				      buf_len);
+}
+
+
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+			    unsigned int wait, const u8 *dst, const u8 *data,
+			    size_t len)
+{
+	if (hapd->driver == NULL || hapd->driver->send_action == NULL)
+		return 0;
+	return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
+					 hapd->own_addr, hapd->own_addr, data,
+					 len, 0);
+}
+
+
+int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+			  enum hostapd_hw_mode mode, int freq,
+			  int channel, int ht_enabled, int vht_enabled,
+			  int sec_channel_offset, int vht_oper_chwidth,
+			  int 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);
+}
+
+
+static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
+					     struct hostapd_hw_modes *mode,
+					     int acs_ch_list_all,
+					     int **freq_list)
+{
+	int i;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *chan = &mode->channels[i];
+
+		if ((acs_ch_list_all ||
+		     freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
+					      chan->chan)) &&
+		    !(chan->flag & HOSTAPD_CHAN_DISABLED))
+			int_array_add_unique(freq_list, chan->freq);
+	}
+}
+
+
+int hostapd_drv_do_acs(struct hostapd_data *hapd)
+{
+	struct drv_acs_params params;
+	int ret, i, acs_ch_list_all = 0;
+	u8 *channels = NULL;
+	unsigned int num_channels = 0;
+	struct hostapd_hw_modes *mode;
+	int *freq_list = NULL;
+
+	if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
+		return 0;
+
+	os_memset(&params, 0, sizeof(params));
+	params.hw_mode = hapd->iface->conf->hw_mode;
+
+	/*
+	 * If no chanlist config parameter is provided, include all enabled
+	 * channels of the selected hw_mode.
+	 */
+	if (!hapd->iface->conf->acs_ch_list.num)
+		acs_ch_list_all = 1;
+
+	mode = hapd->iface->current_mode;
+	if (mode) {
+		channels = os_malloc(mode->num_channels);
+		if (channels == NULL)
+			return -1;
+
+		for (i = 0; i < mode->num_channels; i++) {
+			struct hostapd_channel_data *chan = &mode->channels[i];
+			if (!acs_ch_list_all &&
+			    !freq_range_list_includes(
+				    &hapd->iface->conf->acs_ch_list,
+				    chan->chan))
+				continue;
+			if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
+				channels[num_channels++] = chan->chan;
+				int_array_add_unique(&freq_list, chan->freq);
+			}
+		}
+	} else {
+		for (i = 0; i < hapd->iface->num_hw_features; i++) {
+			mode = &hapd->iface->hw_features[i];
+			hostapd_get_hw_mode_any_channels(hapd, mode,
+							 acs_ch_list_all,
+							 &freq_list);
+		}
+	}
+
+	params.ch_list = channels;
+	params.ch_list_len = num_channels;
+	params.freq_list = freq_list;
+
+	params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
+	params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
+				 HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
+	params.vht_enabled = !!(hapd->iface->conf->ieee80211ac);
+	params.ch_width = 20;
+	if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
+		params.ch_width = 40;
+
+	/* Note: VHT20 is defined by combination of ht_capab & vht_oper_chwidth
+	 */
+	if (hapd->iface->conf->ieee80211ac && params.ht40_enabled) {
+		if (hapd->iface->conf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
+			params.ch_width = 80;
+		else if (hapd->iface->conf->vht_oper_chwidth ==
+			 VHT_CHANWIDTH_160MHZ ||
+			 hapd->iface->conf->vht_oper_chwidth ==
+			 VHT_CHANWIDTH_80P80MHZ)
+			params.ch_width = 160;
+	}
+
+	ret = hapd->driver->do_acs(hapd->drv_priv, &params);
+	os_free(channels);
+
+	return ret;
+}
diff --git a/hostap/src/ap/ap_drv_ops.h b/hostap/src/ap/ap_drv_ops.h
new file mode 100644
index 0000000..82eaf3f
--- /dev/null
+++ b/hostap/src/ap/ap_drv_ops.h
@@ -0,0 +1,340 @@
+/*
+ * hostapd - Driver operations
+ * 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.
+ */
+
+#ifndef AP_DRV_OPS
+#define AP_DRV_OPS
+
+enum wpa_driver_if_type;
+struct wpa_bss_params;
+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,
+			       struct wpabuf **beacon,
+			       struct wpabuf **proberesp,
+			       struct wpabuf **assocresp);
+void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon,
+			       struct wpabuf *proberesp,
+			       struct wpabuf *assocresp);
+int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd);
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
+int hostapd_set_authorized(struct hostapd_data *hapd,
+			   struct sta_info *sta, int authorized);
+int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta);
+int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+			      int enabled);
+int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
+int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+			const u8 *addr, int aid, int val);
+int hostapd_sta_add(struct hostapd_data *hapd,
+		    const u8 *addr, u16 aid, u16 capability,
+		    const u8 *supp_rates, size_t supp_rates_len,
+		    u16 listen_interval,
+		    const struct ieee80211_ht_capabilities *ht_capab,
+		    const struct ieee80211_vht_capabilities *vht_capab,
+		    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);
+int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len);
+int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
+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, 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,
+			  struct wpa_bss_params *params);
+int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+		       const u8 *addr, int idx, u8 *seq);
+int hostapd_flush(struct hostapd_data *hapd);
+int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
+		     int freq, int channel, int ht_enabled, int vht_enabled,
+		     int sec_channel_offset, int vht_oper_chwidth,
+		     int center_segment0, int center_segment1);
+int hostapd_set_rts(struct hostapd_data *hapd, int rts);
+int hostapd_set_frag(struct hostapd_data *hapd, int frag);
+int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
+			  int total_flags, int flags_or, int flags_and);
+int hostapd_set_country(struct hostapd_data *hapd, const char *country);
+int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
+				int cw_min, int cw_max, int burst_time);
+struct hostapd_hw_modes *
+hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+			    u16 *flags);
+int hostapd_driver_commit(struct hostapd_data *hapd);
+int hostapd_drv_none(struct hostapd_data *hapd);
+int hostapd_driver_scan(struct hostapd_data *hapd,
+			struct wpa_driver_scan_params *params);
+struct wpa_scan_results * hostapd_driver_get_scan_results(
+	struct hostapd_data *hapd);
+int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
+			   int duration);
+int hostapd_drv_set_key(const char *ifname,
+			struct hostapd_data *hapd,
+			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);
+int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+			  const void *msg, size_t len, int noack);
+int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+			   const u8 *addr, int reason);
+int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+			     const u8 *addr, int reason);
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+			    unsigned int wait, const u8 *dst, const u8 *data,
+			    size_t len);
+int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
+			 u16 auth_alg);
+int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
+		     u16 seq, u16 status, const u8 *ie, size_t len);
+int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
+		      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,
+			  enum hostapd_hw_mode mode, int freq,
+			  int channel, int ht_enabled, int vht_enabled,
+			  int sec_channel_offset, int vht_oper_chwidth,
+			  int center_segment0, int center_segment1);
+int hostapd_drv_do_acs(struct hostapd_data *hapd);
+
+
+#include "drivers/driver.h"
+
+int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
+			 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)
+{
+	if (hapd->driver == NULL ||
+	    hapd->driver->hapd_set_countermeasures == NULL)
+		return 0;
+	return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
+}
+
+static inline int hostapd_drv_set_sta_vlan(const char *ifname,
+					   struct hostapd_data *hapd,
+					   const u8 *addr, int vlan_id)
+{
+	if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
+		return 0;
+	return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
+					  vlan_id);
+}
+
+static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd,
+					    const u8 *addr)
+{
+	if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
+		return 0;
+	return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd,
+					 const u8 *addr)
+{
+	if (hapd->driver == NULL || hapd->driver->sta_remove == NULL)
+		return 0;
+	return hapd->driver->sta_remove(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd,
+					      const u8 *addr, const u8 *data,
+					      size_t data_len, int encrypt,
+					      u32 flags)
+{
+	if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
+		return 0;
+	return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
+					     data_len, encrypt,
+					     hapd->own_addr, flags);
+}
+
+static inline int hostapd_drv_read_sta_data(
+	struct hostapd_data *hapd, struct hostap_sta_driver_data *data,
+	const u8 *addr)
+{
+	if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
+		return -1;
+	return hapd->driver->read_sta_data(hapd->drv_priv, data, addr);
+}
+
+static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd,
+					      const u8 *addr)
+{
+	if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
+		return 0;
+	return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_set_acl(struct hostapd_data *hapd,
+				      struct hostapd_acl_params *params)
+{
+	if (hapd->driver == NULL || hapd->driver->set_acl == NULL)
+		return 0;
+	return hapd->driver->set_acl(hapd->drv_priv, params);
+}
+
+static inline int hostapd_drv_set_ap(struct hostapd_data *hapd,
+				     struct wpa_driver_ap_params *params)
+{
+	if (hapd->driver == NULL || hapd->driver->set_ap == NULL)
+		return 0;
+	return hapd->driver->set_ap(hapd->drv_priv, params);
+}
+
+static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd,
+						  const u8 *mac, int accepted,
+						  u32 session_timeout)
+{
+	if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL)
+		return 0;
+	return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted,
+						 session_timeout);
+}
+
+static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd,
+						    const u8 *mac)
+{
+	if (hapd->driver == NULL ||
+	    hapd->driver->set_radius_acl_expire == NULL)
+		return 0;
+	return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
+}
+
+static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd,
+					   int auth_algs)
+{
+	if (hapd->driver == NULL || hapd->driver->set_authmode == NULL)
+		return 0;
+	return hapd->driver->set_authmode(hapd->drv_priv, auth_algs);
+}
+
+static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
+					   const u8 *own_addr, const u8 *addr,
+					   int qos)
+{
+	if (hapd->driver == NULL || hapd->driver->poll_client == NULL)
+		return;
+	hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos);
+}
+
+static inline int hostapd_drv_get_survey(struct hostapd_data *hapd,
+					 unsigned int freq)
+{
+	if (hapd->driver == NULL)
+		return -1;
+	if (!hapd->driver->get_survey)
+		return -1;
+	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_br_add_ip_neigh(struct hostapd_data *hapd,
+					      int version, const u8 *ipaddr,
+					      int prefixlen, const u8 *addr)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->br_add_ip_neigh == NULL)
+		return -1;
+	return hapd->driver->br_add_ip_neigh(hapd->drv_priv, version, ipaddr,
+					     prefixlen, addr);
+}
+
+static inline int hostapd_drv_br_delete_ip_neigh(struct hostapd_data *hapd,
+						 u8 version, const u8 *ipaddr)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->br_delete_ip_neigh == NULL)
+		return -1;
+	return hapd->driver->br_delete_ip_neigh(hapd->drv_priv, version,
+						ipaddr);
+}
+
+static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
+					       enum drv_br_port_attr attr,
+					       unsigned int val)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->br_port_set_attr == NULL)
+		return -1;
+	return hapd->driver->br_port_set_attr(hapd->drv_priv, attr, val);
+}
+
+static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
+					       enum drv_br_net_param param,
+					       unsigned int val)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->br_set_net_param == NULL)
+		return -1;
+	return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
+}
+
+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);
+}
+
+static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
+{
+	if (hapd->driver == NULL || hapd->driver->stop_ap == NULL)
+		return 0;
+	return hapd->driver->stop_ap(hapd->drv_priv);
+}
+
+#endif /* AP_DRV_OPS */
diff --git a/hostap/src/ap/ap_list.c b/hostap/src/ap/ap_list.c
new file mode 100644
index 0000000..8bf6dde
--- /dev/null
+++ b/hostap/src/ap/ap_list.c
@@ -0,0 +1,312 @@
+/*
+ * hostapd / AP table
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2006, Devicescape Software, 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/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ap_list.h"
+
+
+/* AP list is a double linked list with head->prev pointing to the end of the
+ * list and tail->next = NULL. Entries are moved to the head of the list
+ * whenever a beacon has been received from the AP in question. The tail entry
+ * in this link will thus be the least recently used entry. */
+
+
+static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	int i;
+
+	if (iface->current_mode == NULL ||
+	    iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
+	    iface->conf->channel != ap->channel)
+		return 0;
+
+	if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT))
+		return 1;
+
+	for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
+		int rate = (ap->supported_rates[i] & 0x7f) * 5;
+		if (rate == 60 || rate == 90 || rate > 110)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+static struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap)
+{
+	struct ap_info *s;
+
+	s = iface->ap_hash[STA_HASH(ap)];
+	while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
+		s = s->hnext;
+	return s;
+}
+
+
+static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	if (iface->ap_list) {
+		ap->prev = iface->ap_list->prev;
+		iface->ap_list->prev = ap;
+	} else
+		ap->prev = ap;
+	ap->next = iface->ap_list;
+	iface->ap_list = ap;
+}
+
+
+static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	if (iface->ap_list == ap)
+		iface->ap_list = ap->next;
+	else
+		ap->prev->next = ap->next;
+
+	if (ap->next)
+		ap->next->prev = ap->prev;
+	else if (iface->ap_list)
+		iface->ap_list->prev = ap->prev;
+}
+
+
+static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	ap->hnext = iface->ap_hash[STA_HASH(ap->addr)];
+	iface->ap_hash[STA_HASH(ap->addr)] = ap;
+}
+
+
+static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	struct ap_info *s;
+
+	s = iface->ap_hash[STA_HASH(ap->addr)];
+	if (s == NULL) return;
+	if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
+		iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
+		return;
+	}
+
+	while (s->hnext != NULL &&
+	       os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
+		s = s->hnext;
+	if (s->hnext != NULL)
+		s->hnext = s->hnext->hnext;
+	else
+		wpa_printf(MSG_INFO, "AP: could not remove AP " MACSTR
+			   " from hash table",  MAC2STR(ap->addr));
+}
+
+
+static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	ap_ap_hash_del(iface, ap);
+	ap_ap_list_del(iface, ap);
+
+	iface->num_ap--;
+	os_free(ap);
+}
+
+
+static void hostapd_free_aps(struct hostapd_iface *iface)
+{
+	struct ap_info *ap, *prev;
+
+	ap = iface->ap_list;
+
+	while (ap) {
+		prev = ap;
+		ap = ap->next;
+		ap_free_ap(iface, prev);
+	}
+
+	iface->ap_list = NULL;
+}
+
+
+static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr)
+{
+	struct ap_info *ap;
+
+	ap = os_zalloc(sizeof(struct ap_info));
+	if (ap == NULL)
+		return NULL;
+
+	/* initialize AP info data */
+	os_memcpy(ap->addr, addr, ETH_ALEN);
+	ap_ap_list_add(iface, ap);
+	iface->num_ap++;
+	ap_ap_hash_add(iface, ap);
+
+	if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) {
+		wpa_printf(MSG_DEBUG, "Removing the least recently used AP "
+			   MACSTR " from AP table", MAC2STR(ap->prev->addr));
+		ap_free_ap(iface, ap->prev);
+	}
+
+	return ap;
+}
+
+
+void ap_list_process_beacon(struct hostapd_iface *iface,
+			    const struct ieee80211_mgmt *mgmt,
+			    struct ieee802_11_elems *elems,
+			    struct hostapd_frame_info *fi)
+{
+	struct ap_info *ap;
+	int new_ap = 0;
+	int set_beacon = 0;
+
+	if (iface->conf->ap_table_max_size < 1)
+		return;
+
+	ap = ap_get_ap(iface, mgmt->bssid);
+	if (!ap) {
+		ap = ap_ap_add(iface, mgmt->bssid);
+		if (!ap) {
+			wpa_printf(MSG_INFO,
+				   "Failed to allocate AP information entry");
+			return;
+		}
+		new_ap = 1;
+	}
+
+	merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX,
+			  elems->supp_rates, elems->supp_rates_len,
+			  elems->ext_supp_rates, elems->ext_supp_rates_len);
+
+	if (elems->erp_info)
+		ap->erp = elems->erp_info[0];
+	else
+		ap->erp = -1;
+
+	if (elems->ds_params)
+		ap->channel = elems->ds_params[0];
+	else if (elems->ht_operation)
+		ap->channel = elems->ht_operation[0];
+	else if (fi)
+		ap->channel = fi->channel;
+
+	if (elems->ht_capabilities)
+		ap->ht_support = 1;
+	else
+		ap->ht_support = 0;
+
+	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
+		 * oldest entry is always in the end of the list */
+		ap_ap_list_del(iface, ap);
+		ap_ap_list_add(iface, ap);
+	}
+
+	if (!iface->olbc &&
+	    ap_list_beacon_olbc(iface, ap)) {
+		iface->olbc = 1;
+		wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR
+			   " (channel %d) - enable protection",
+			   MAC2STR(ap->addr), ap->channel);
+		set_beacon++;
+	}
+
+#ifdef CONFIG_IEEE80211N
+	if (!iface->olbc_ht && !ap->ht_support &&
+	    (ap->channel == 0 ||
+	     ap->channel == iface->conf->channel ||
+	     ap->channel == iface->conf->channel +
+	     iface->conf->secondary_channel * 4)) {
+		iface->olbc_ht = 1;
+		hostapd_ht_operation_update(iface);
+		wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
+			   " (channel %d) - enable protection",
+			   MAC2STR(ap->addr), ap->channel);
+		set_beacon++;
+	}
+#endif /* CONFIG_IEEE80211N */
+
+	if (set_beacon)
+		ieee802_11_update_beacons(iface);
+}
+
+
+void ap_list_timer(struct hostapd_iface *iface)
+{
+	struct os_reltime now;
+	struct ap_info *ap;
+	int set_beacon = 0;
+
+	if (!iface->ap_list)
+		return;
+
+	os_get_reltime(&now);
+
+	while (iface->ap_list) {
+		ap = iface->ap_list->prev;
+		if (!os_reltime_expired(&now, &ap->last_beacon,
+					iface->conf->ap_table_expiration_time))
+			break;
+
+		ap_free_ap(iface, ap);
+	}
+
+	if (iface->olbc || iface->olbc_ht) {
+		int olbc = 0;
+		int olbc_ht = 0;
+
+		ap = iface->ap_list;
+		while (ap && (olbc == 0 || olbc_ht == 0)) {
+			if (ap_list_beacon_olbc(iface, ap))
+				olbc = 1;
+			if (!ap->ht_support)
+				olbc_ht = 1;
+			ap = ap->next;
+		}
+		if (!olbc && iface->olbc) {
+			wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
+			iface->olbc = 0;
+			set_beacon++;
+		}
+#ifdef CONFIG_IEEE80211N
+		if (!olbc_ht && iface->olbc_ht) {
+			wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
+			iface->olbc_ht = 0;
+			hostapd_ht_operation_update(iface);
+			set_beacon++;
+		}
+#endif /* CONFIG_IEEE80211N */
+	}
+
+	if (set_beacon)
+		ieee802_11_update_beacons(iface);
+}
+
+
+int ap_list_init(struct hostapd_iface *iface)
+{
+	return 0;
+}
+
+
+void ap_list_deinit(struct hostapd_iface *iface)
+{
+	hostapd_free_aps(iface);
+}
diff --git a/hostap/src/ap/ap_list.h b/hostap/src/ap/ap_list.h
new file mode 100644
index 0000000..9e0353c
--- /dev/null
+++ b/hostap/src/ap/ap_list.h
@@ -0,0 +1,58 @@
+/*
+ * hostapd / AP table
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AP_LIST_H
+#define AP_LIST_H
+
+struct ap_info {
+	/* Note: next/prev pointers are updated whenever a new beacon is
+	 * received because these are used to find the least recently used
+	 * entries. */
+	struct ap_info *next; /* next entry in AP list */
+	struct ap_info *prev; /* previous entry in AP list */
+	struct ap_info *hnext; /* next entry in hash table list */
+	u8 addr[6];
+	u8 supported_rates[WLAN_SUPP_RATES_MAX];
+	int erp; /* ERP Info or -1 if ERP info element not present */
+
+	int channel;
+
+	int ht_support;
+
+	struct os_reltime last_beacon;
+};
+
+struct ieee802_11_elems;
+struct hostapd_frame_info;
+
+void ap_list_process_beacon(struct hostapd_iface *iface,
+			    const struct ieee80211_mgmt *mgmt,
+			    struct ieee802_11_elems *elems,
+			    struct hostapd_frame_info *fi);
+#ifdef NEED_AP_MLME
+int ap_list_init(struct hostapd_iface *iface);
+void ap_list_deinit(struct hostapd_iface *iface);
+void ap_list_timer(struct hostapd_iface *iface);
+#else /* NEED_AP_MLME */
+static inline int ap_list_init(struct hostapd_iface *iface)
+{
+	return 0;
+}
+
+static inline void ap_list_deinit(struct hostapd_iface *iface)
+{
+}
+
+static inline void ap_list_timer(struct hostapd_iface *iface)
+{
+}
+#endif /* NEED_AP_MLME */
+
+#endif /* AP_LIST_H */
diff --git a/hostap/src/ap/ap_mlme.c b/hostap/src/ap/ap_mlme.c
new file mode 100644
index 0000000..13604ed
--- /dev/null
+++ b/hostap/src/ap/ap_mlme.c
@@ -0,0 +1,178 @@
+/*
+ * hostapd / IEEE 802.11 MLME
+ * Copyright 2003-2006, Jouni Malinen <j@w1.fi>
+ * Copyright 2003-2004, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, 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 "ieee802_11.h"
+#include "wpa_auth.h"
+#include "sta_info.h"
+#include "ap_mlme.h"
+#include "hostapd.h"
+
+
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+static const char * mlme_auth_alg_str(int alg)
+{
+	switch (alg) {
+	case WLAN_AUTH_OPEN:
+		return "OPEN_SYSTEM";
+	case WLAN_AUTH_SHARED_KEY:
+		return "SHARED_KEY";
+	case WLAN_AUTH_FT:
+		return "FT";
+	}
+
+	return "unknown";
+}
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+
+/**
+ * mlme_authenticate_indication - Report the establishment of an authentication
+ * relationship with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * authentication relationship with a specific peer MAC entity that
+ * resulted from an authentication procedure that was initiated by
+ * that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ * AuthenticationType = sta->auth_alg (WLAN_AUTH_OPEN / WLAN_AUTH_SHARED_KEY)
+ */
+void mlme_authenticate_indication(struct hostapd_data *hapd,
+				  struct sta_info *sta)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-AUTHENTICATE.indication(" MACSTR ", %s)",
+		       MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg));
+	if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP))
+		mlme_deletekeys_request(hapd, sta);
+}
+
+
+/**
+ * mlme_deauthenticate_indication - Report the invalidation of an
+ * authentication relationship with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: Peer STA data
+ * @reason_code: ReasonCode from Deauthentication frame
+ *
+ * MLME calls this function as a result of the invalidation of an
+ * authentication relationship with a specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_deauthenticate_indication(struct hostapd_data *hapd,
+				    struct sta_info *sta, u16 reason_code)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)",
+		       MAC2STR(sta->addr), reason_code);
+	if (!hapd->iface->driver_ap_teardown)
+		mlme_deletekeys_request(hapd, sta);
+}
+
+
+/**
+ * mlme_associate_indication - Report the establishment of an association with
+ * a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * association with a specific peer MAC entity that resulted from an
+ * association procedure that was initiated by that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-ASSOCIATE.indication(" MACSTR ")",
+		       MAC2STR(sta->addr));
+	if (sta->auth_alg != WLAN_AUTH_FT)
+		mlme_deletekeys_request(hapd, sta);
+}
+
+
+/**
+ * mlme_reassociate_indication - Report the establishment of an reassociation
+ * with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * reassociation with a specific peer MAC entity that resulted from a
+ * reassociation procedure that was initiated by that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_reassociate_indication(struct hostapd_data *hapd,
+				 struct sta_info *sta)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-REASSOCIATE.indication(" MACSTR ")",
+		       MAC2STR(sta->addr));
+	if (sta->auth_alg != WLAN_AUTH_FT)
+		mlme_deletekeys_request(hapd, sta);
+}
+
+
+/**
+ * mlme_disassociate_indication - Report disassociation with a specific peer
+ * MAC entity
+ * @hapd: BSS data
+ * @sta: Peer STA data
+ * @reason_code: ReasonCode from Disassociation frame
+ *
+ * MLME calls this function as a result of the invalidation of an association
+ * relationship with a specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_disassociate_indication(struct hostapd_data *hapd,
+				  struct sta_info *sta, u16 reason_code)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-DISASSOCIATE.indication(" MACSTR ", %d)",
+		       MAC2STR(sta->addr), reason_code);
+	mlme_deletekeys_request(hapd, sta);
+}
+
+
+void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
+				       const u8 *addr)
+{
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-MichaelMICFailure.indication(" MACSTR ")",
+		       MAC2STR(addr));
+}
+
+
+void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-DELETEKEYS.request(" MACSTR ")",
+		       MAC2STR(sta->addr));
+
+	if (sta->wpa_sm)
+		wpa_remove_ptk(sta->wpa_sm);
+}
diff --git a/hostap/src/ap/ap_mlme.h b/hostap/src/ap/ap_mlme.h
new file mode 100644
index 0000000..e7fd69d
--- /dev/null
+++ b/hostap/src/ap/ap_mlme.h
@@ -0,0 +1,34 @@
+/*
+ * hostapd / IEEE 802.11 MLME
+ * Copyright 2003, Jouni Malinen <j@w1.fi>
+ * Copyright 2003-2004, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MLME_H
+#define MLME_H
+
+void mlme_authenticate_indication(struct hostapd_data *hapd,
+				  struct sta_info *sta);
+
+void mlme_deauthenticate_indication(struct hostapd_data *hapd,
+				    struct sta_info *sta, u16 reason_code);
+
+void mlme_associate_indication(struct hostapd_data *hapd,
+			       struct sta_info *sta);
+
+void mlme_reassociate_indication(struct hostapd_data *hapd,
+				 struct sta_info *sta);
+
+void mlme_disassociate_indication(struct hostapd_data *hapd,
+				  struct sta_info *sta, u16 reason_code);
+
+void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
+				       const u8 *addr);
+
+void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta);
+
+#endif /* MLME_H */
diff --git a/hostap/src/ap/authsrv.c b/hostap/src/ap/authsrv.c
new file mode 100644
index 0000000..934dcfc
--- /dev/null
+++ b/hostap/src/ap/authsrv.c
@@ -0,0 +1,236 @@
+/*
+ * Authentication server setup
+ * 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 "utils/common.h"
+#include "crypto/tls.h"
+#include "eap_server/eap.h"
+#include "eap_server/eap_sim_db.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "radius/radius_server.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "authsrv.h"
+
+
+#if defined(EAP_SERVER_SIM) || defined(EAP_SERVER_AKA)
+#define EAP_SIM_DB
+#endif /* EAP_SERVER_SIM || EAP_SERVER_AKA */
+
+
+#ifdef EAP_SIM_DB
+static int hostapd_sim_db_cb_sta(struct hostapd_data *hapd,
+				 struct sta_info *sta, void *ctx)
+{
+	if (eapol_auth_eap_pending_cb(sta->eapol_sm, ctx) == 0)
+		return 1;
+	return 0;
+}
+
+
+static void hostapd_sim_db_cb(void *ctx, void *session_ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	if (ap_for_each_sta(hapd, hostapd_sim_db_cb_sta, session_ctx) == 0) {
+#ifdef RADIUS_SERVER
+		radius_server_eap_pending_cb(hapd->radius_srv, session_ctx);
+#endif /* RADIUS_SERVER */
+	}
+}
+#endif /* EAP_SIM_DB */
+
+
+#ifdef RADIUS_SERVER
+
+static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
+				       size_t identity_len, int phase2,
+				       struct eap_user *user)
+{
+	const struct hostapd_eap_user *eap_user;
+	int i;
+	int rv = -1;
+
+	eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2);
+	if (eap_user == NULL)
+		goto out;
+
+	if (user == NULL)
+		return 0;
+
+	os_memset(user, 0, sizeof(*user));
+	for (i = 0; i < EAP_MAX_METHODS; i++) {
+		user->methods[i].vendor = eap_user->methods[i].vendor;
+		user->methods[i].method = eap_user->methods[i].method;
+	}
+
+	if (eap_user->password) {
+		user->password = os_malloc(eap_user->password_len);
+		if (user->password == NULL)
+			goto out;
+		os_memcpy(user->password, eap_user->password,
+			  eap_user->password_len);
+		user->password_len = eap_user->password_len;
+		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;
+	rv = 0;
+
+out:
+	if (rv)
+		wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
+
+	return rv;
+}
+
+
+static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
+{
+	struct radius_server_conf srv;
+	struct hostapd_bss_config *conf = hapd->conf;
+	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;
+	srv.msg_ctx = hapd->msg_ctx;
+	srv.pac_opaque_encr_key = conf->pac_opaque_encr_key;
+	srv.eap_fast_a_id = conf->eap_fast_a_id;
+	srv.eap_fast_a_id_len = conf->eap_fast_a_id_len;
+	srv.eap_fast_a_id_info = conf->eap_fast_a_id_info;
+	srv.eap_fast_prov = conf->eap_fast_prov;
+	srv.pac_key_lifetime = conf->pac_key_lifetime;
+	srv.pac_key_refresh_time = conf->pac_key_refresh_time;
+	srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
+	srv.tnc = conf->tnc;
+	srv.wps = hapd->wps;
+	srv.ipv6 = conf->radius_server_ipv6;
+	srv.get_eap_user = hostapd_radius_get_eap_user;
+	srv.eap_req_id_text = conf->eap_req_id_text;
+	srv.eap_req_id_text_len = conf->eap_req_id_text_len;
+	srv.pwd_group = conf->pwd_group;
+	srv.server_id = conf->server_id ? conf->server_id : "hostapd";
+	srv.sqlite_file = conf->eap_user_sqlite;
+#ifdef CONFIG_RADIUS_TEST
+	srv.dump_msk_file = conf->dump_msk_file;
+#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 */
+	srv.erp = conf->eap_server_erp;
+	srv.erp_domain = conf->erp_domain;
+	srv.tls_session_lifetime = conf->tls_session_lifetime;
+
+	hapd->radius_srv = radius_server_init(&srv);
+	if (hapd->radius_srv == NULL) {
+		wpa_printf(MSG_ERROR, "RADIUS server initialization failed.");
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif /* RADIUS_SERVER */
+
+
+int authsrv_init(struct hostapd_data *hapd)
+{
+#ifdef EAP_TLS_FUNCS
+	if (hapd->conf->eap_server &&
+	    (hapd->conf->ca_cert || hapd->conf->server_cert ||
+	     hapd->conf->private_key || hapd->conf->dh_file)) {
+		struct tls_config conf;
+		struct tls_connection_params params;
+
+		os_memset(&conf, 0, sizeof(conf));
+		conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+		hapd->ssl_ctx = tls_init(&conf);
+		if (hapd->ssl_ctx == NULL) {
+			wpa_printf(MSG_ERROR, "Failed to initialize TLS");
+			authsrv_deinit(hapd);
+			return -1;
+		}
+
+		os_memset(&params, 0, sizeof(params));
+		params.ca_cert = hapd->conf->ca_cert;
+		params.client_cert = hapd->conf->server_cert;
+		params.private_key = hapd->conf->private_key;
+		params.private_key_passwd = hapd->conf->private_key_passwd;
+		params.dh_file = hapd->conf->dh_file;
+		params.openssl_ciphers = hapd->conf->openssl_ciphers;
+		params.ocsp_stapling_response =
+			hapd->conf->ocsp_stapling_response;
+
+		if (tls_global_set_params(hapd->ssl_ctx, &params)) {
+			wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
+			authsrv_deinit(hapd);
+			return -1;
+		}
+
+		if (tls_global_set_verify(hapd->ssl_ctx,
+					  hapd->conf->check_crl)) {
+			wpa_printf(MSG_ERROR, "Failed to enable check_crl");
+			authsrv_deinit(hapd);
+			return -1;
+		}
+	}
+#endif /* EAP_TLS_FUNCS */
+
+#ifdef EAP_SIM_DB
+	if (hapd->conf->eap_sim_db) {
+		hapd->eap_sim_db_priv =
+			eap_sim_db_init(hapd->conf->eap_sim_db,
+					hostapd_sim_db_cb, hapd);
+		if (hapd->eap_sim_db_priv == NULL) {
+			wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM "
+				   "database interface");
+			authsrv_deinit(hapd);
+			return -1;
+		}
+	}
+#endif /* EAP_SIM_DB */
+
+#ifdef RADIUS_SERVER
+	if (hapd->conf->radius_server_clients &&
+	    hostapd_setup_radius_srv(hapd))
+		return -1;
+#endif /* RADIUS_SERVER */
+
+	return 0;
+}
+
+
+void authsrv_deinit(struct hostapd_data *hapd)
+{
+#ifdef RADIUS_SERVER
+	radius_server_deinit(hapd->radius_srv);
+	hapd->radius_srv = NULL;
+#endif /* RADIUS_SERVER */
+
+#ifdef EAP_TLS_FUNCS
+	if (hapd->ssl_ctx) {
+		tls_deinit(hapd->ssl_ctx);
+		hapd->ssl_ctx = NULL;
+	}
+#endif /* EAP_TLS_FUNCS */
+
+#ifdef EAP_SIM_DB
+	if (hapd->eap_sim_db_priv) {
+		eap_sim_db_deinit(hapd->eap_sim_db_priv);
+		hapd->eap_sim_db_priv = NULL;
+	}
+#endif /* EAP_SIM_DB */
+}
diff --git a/hostap/src/ap/authsrv.h b/hostap/src/ap/authsrv.h
new file mode 100644
index 0000000..2f4ed34
--- /dev/null
+++ b/hostap/src/ap/authsrv.h
@@ -0,0 +1,15 @@
+/*
+ * Authentication server setup
+ * 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 AUTHSRV_H
+#define AUTHSRV_H
+
+int authsrv_init(struct hostapd_data *hapd);
+void authsrv_deinit(struct hostapd_data *hapd);
+
+#endif /* AUTHSRV_H */
diff --git a/hostap/src/ap/beacon.c b/hostap/src/ap/beacon.c
new file mode 100644
index 0000000..5fe8fd5
--- /dev/null
+++ b/hostap/src/ap/beacon.c
@@ -0,0 +1,1253 @@
+/*
+ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
+ * Copyright (c) 2002-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-2012, 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"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/hw_features_common.h"
+#include "wps/wps_defs.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "wmm.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+#include "hs20.h"
+#include "dfs.h"
+
+
+#ifdef NEED_AP_MLME
+
+static u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
+					 size_t len)
+{
+	if (!hapd->conf->radio_measurements || len < 2 + 4)
+		return eid;
+
+	*eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
+	*eid++ = 5;
+	*eid++ = (hapd->conf->radio_measurements & BIT(0)) ?
+		WLAN_RRM_CAPS_NEIGHBOR_REPORT : 0x00;
+	*eid++ = 0x00;
+	*eid++ = 0x00;
+	*eid++ = 0x00;
+	*eid++ = 0x00;
+	return eid;
+}
+
+
+static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+	if (len < 2 + 5)
+		return eid;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->conf->bss_load_test_set) {
+		*eid++ = WLAN_EID_BSS_LOAD;
+		*eid++ = 5;
+		os_memcpy(eid, hapd->conf->bss_load_test, 5);
+		eid += 5;
+		return eid;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (hapd->conf->bss_load_update_period) {
+		*eid++ = WLAN_EID_BSS_LOAD;
+		*eid++ = 5;
+		WPA_PUT_LE16(eid, hapd->num_sta);
+		eid += 2;
+		*eid++ = hapd->iface->channel_utilization;
+		WPA_PUT_LE16(eid, 0); /* no available admission capabity */
+		eid += 2;
+	}
+	return eid;
+}
+
+
+static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
+{
+	u8 erp = 0;
+
+	if (hapd->iface->current_mode == NULL ||
+	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+		return 0;
+
+	if (hapd->iface->olbc)
+		erp |= ERP_INFO_USE_PROTECTION;
+	if (hapd->iface->num_sta_non_erp > 0) {
+		erp |= ERP_INFO_NON_ERP_PRESENT |
+			ERP_INFO_USE_PROTECTION;
+	}
+	if (hapd->iface->num_sta_no_short_preamble > 0 ||
+	    hapd->iconf->preamble == LONG_PREAMBLE)
+		erp |= ERP_INFO_BARKER_PREAMBLE_MODE;
+
+	return erp;
+}
+
+
+static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid)
+{
+	*eid++ = WLAN_EID_DS_PARAMS;
+	*eid++ = 1;
+	*eid++ = hapd->iconf->channel;
+	return eid;
+}
+
+
+static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid)
+{
+	if (hapd->iface->current_mode == NULL ||
+	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+		return eid;
+
+	/* Set NonERP_present and use_protection bits if there
+	 * are any associated NonERP stations. */
+	/* TODO: use_protection bit can be set to zero even if
+	 * there are NonERP stations present. This optimization
+	 * might be useful if NonERP stations are "quiet".
+	 * See 802.11g/D6 E-1 for recommended practice.
+	 * In addition, Non ERP present might be set, if AP detects Non ERP
+	 * operation on other APs. */
+
+	/* Add ERP Information element */
+	*eid++ = WLAN_EID_ERP_INFO;
+	*eid++ = 1;
+	*eid++ = ieee802_11_erp_info(hapd);
+
+	return eid;
+}
+
+
+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;
+
+	/* Let host drivers add this IE if DFS support is offloaded */
+	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+		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)
+{
+	if (end - pos < 3)
+		return pos;
+
+	/* first channel number */
+	*pos++ = start->chan;
+	/* number of channels */
+	*pos++ = (prev->chan - start->chan) / chan_spacing + 1;
+	/* maximum transmit power level */
+	*pos++ = start->max_tx_power;
+
+	return pos;
+}
+
+
+static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
+				int max_len)
+{
+	u8 *pos = eid;
+	u8 *end = eid + max_len;
+	int i;
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *start, *prev;
+	int chan_spacing = 1;
+
+	if (!hapd->iconf->ieee80211d || max_len < 6 ||
+	    hapd->iface->current_mode == NULL)
+		return eid;
+
+	*pos++ = WLAN_EID_COUNTRY;
+	pos++; /* length will be set later */
+	os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
+	pos += 3;
+
+	mode = hapd->iface->current_mode;
+	if (mode->mode == HOSTAPD_MODE_IEEE80211A)
+		chan_spacing = 4;
+
+	start = prev = NULL;
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *chan = &mode->channels[i];
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+		if (start && prev &&
+		    prev->chan + chan_spacing == chan->chan &&
+		    start->max_tx_power == chan->max_tx_power) {
+			prev = chan;
+			continue; /* can use same entry */
+		}
+
+		if (start && prev) {
+			pos = hostapd_eid_country_add(pos, end, chan_spacing,
+						      start, prev);
+			start = NULL;
+		}
+
+		/* Start new group */
+		start = prev = chan;
+	}
+
+	if (start) {
+		pos = hostapd_eid_country_add(pos, end, chan_spacing,
+					      start, prev);
+	}
+
+	if ((pos - eid) & 1) {
+		if (end - pos < 1)
+			return eid;
+		*pos++ = 0; /* pad for 16-bit alignment */
+	}
+
+	eid[1] = (pos - eid) - 2;
+
+	return pos;
+}
+
+
+static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+	const u8 *ie;
+	size_t ielen;
+
+	ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
+	if (ie == NULL || ielen > len)
+		return eid;
+
+	os_memcpy(eid, ie, ielen);
+	return eid + ielen;
+}
+
+
+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,
+				   const struct ieee80211_mgmt *req,
+				   int is_p2p, size_t *resp_len)
+{
+	struct ieee80211_mgmt *resp;
+	u8 *pos, *epos;
+	size_t buflen;
+
+#define MAX_PROBERESP_LEN 768
+	buflen = MAX_PROBERESP_LEN;
+#ifdef CONFIG_WPS
+	if (hapd->wps_probe_resp_ie)
+		buflen += wpabuf_len(hapd->wps_probe_resp_ie);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+	if (hapd->p2p_probe_resp_ie)
+		buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies)
+		buflen += wpabuf_len(hapd->iface->fst_ies);
+#endif /* CONFIG_FST */
+	if (hapd->conf->vendor_elements)
+		buflen += wpabuf_len(hapd->conf->vendor_elements);
+	if (hapd->conf->vendor_vht) {
+		buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+			2 + sizeof(struct ieee80211_vht_operation);
+	}
+	resp = os_zalloc(buflen);
+	if (resp == NULL)
+		return NULL;
+
+	epos = ((u8 *) resp) + MAX_PROBERESP_LEN;
+
+	resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_PROBE_RESP);
+	if (req)
+		os_memcpy(resp->da, req->sa, ETH_ALEN);
+	os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+
+	os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+	resp->u.probe_resp.beacon_int =
+		host_to_le16(hapd->iconf->beacon_int);
+
+	/* hardware or low-level driver will setup seq_ctrl and timestamp */
+	resp->u.probe_resp.capab_info =
+		host_to_le16(hostapd_own_capab_info(hapd));
+
+	pos = resp->u.probe_resp.variable;
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = hapd->conf->ssid.ssid_len;
+	os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len);
+	pos += hapd->conf->ssid.ssid_len;
+
+	/* Supported rates */
+	pos = hostapd_eid_supp_rates(hapd, pos);
+
+	/* DS Params */
+	pos = hostapd_eid_ds_params(hapd, pos);
+
+	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);
+
+	/* Extended supported rates */
+	pos = hostapd_eid_ext_supp_rates(hapd, pos);
+
+	/* RSN, MDIE, WPA */
+	pos = hostapd_eid_wpa(hapd, pos, epos - pos);
+
+	pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+
+	pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
+
+#ifdef CONFIG_IEEE80211N
+	pos = hostapd_eid_ht_capabilities(hapd, pos);
+	pos = hostapd_eid_ht_operation(hapd, pos);
+#endif /* CONFIG_IEEE80211N */
+
+	pos = hostapd_eid_ext_capab(hapd, pos);
+
+	pos = hostapd_eid_time_adv(hapd, pos);
+	pos = hostapd_eid_time_zone(hapd, pos);
+
+	pos = hostapd_eid_interworking(hapd, pos);
+	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_FST
+	if (hapd->iface->fst_ies) {
+		os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies),
+			  wpabuf_len(hapd->iface->fst_ies));
+		pos += wpabuf_len(hapd->iface->fst_ies);
+	}
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+		pos = hostapd_eid_vht_capabilities(hapd, pos);
+		pos = hostapd_eid_vht_operation(hapd, pos);
+	}
+	if (hapd->conf->vendor_vht)
+		pos = hostapd_eid_vendor_vht(hapd, pos);
+#endif /* CONFIG_IEEE80211AC */
+
+	/* Wi-Fi Alliance WMM */
+	pos = hostapd_eid_wmm(hapd, pos);
+
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) {
+		os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie),
+			  wpabuf_len(hapd->wps_probe_resp_ie));
+		pos += wpabuf_len(hapd->wps_probe_resp_ie);
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_ENABLED) && is_p2p &&
+	    hapd->p2p_probe_resp_ie) {
+		os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie),
+			  wpabuf_len(hapd->p2p_probe_resp_ie));
+		pos += wpabuf_len(hapd->p2p_probe_resp_ie);
+	}
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_P2P_MANAGER
+	if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
+	    P2P_MANAGE)
+		pos = hostapd_eid_p2p_manage(hapd, pos);
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_HS20
+	pos = hostapd_eid_hs20_indication(hapd, pos);
+	pos = hostapd_eid_osen(hapd, pos);
+#endif /* CONFIG_HS20 */
+
+	if (hapd->conf->vendor_elements) {
+		os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
+			  wpabuf_len(hapd->conf->vendor_elements));
+		pos += wpabuf_len(hapd->conf->vendor_elements);
+	}
+
+	*resp_len = pos - (u8 *) resp;
+	return (u8 *) resp;
+}
+
+
+enum ssid_match_result {
+	NO_SSID_MATCH,
+	EXACT_SSID_MATCH,
+	WILDCARD_SSID_MATCH
+};
+
+static enum ssid_match_result ssid_match(struct hostapd_data *hapd,
+					 const u8 *ssid, size_t ssid_len,
+					 const u8 *ssid_list,
+					 size_t ssid_list_len)
+{
+	const u8 *pos, *end;
+	int wildcard = 0;
+
+	if (ssid_len == 0)
+		wildcard = 1;
+	if (ssid_len == hapd->conf->ssid.ssid_len &&
+	    os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0)
+		return EXACT_SSID_MATCH;
+
+	if (ssid_list == NULL)
+		return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH;
+
+	pos = ssid_list;
+	end = ssid_list + ssid_list_len;
+	while (pos + 1 <= end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		if (pos[1] == 0)
+			wildcard = 1;
+		if (pos[1] == hapd->conf->ssid.ssid_len &&
+		    os_memcmp(pos + 2, hapd->conf->ssid.ssid, pos[1]) == 0)
+			return EXACT_SSID_MATCH;
+		pos += 2 + pos[1];
+	}
+
+	return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH;
+}
+
+
+void sta_track_expire(struct hostapd_iface *iface, int force)
+{
+	struct os_reltime now;
+	struct hostapd_sta_info *info;
+
+	if (!iface->num_sta_seen)
+		return;
+
+	os_get_reltime(&now);
+	while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
+				     list))) {
+		if (!force &&
+		    !os_reltime_expired(&now, &info->last_seen,
+					iface->conf->track_sta_max_age))
+			break;
+		force = 0;
+
+		wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for "
+			   MACSTR, iface->bss[0]->conf->iface,
+			   MAC2STR(info->addr));
+		dl_list_del(&info->list);
+		iface->num_sta_seen--;
+		os_free(info);
+	}
+}
+
+
+static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface,
+					       const u8 *addr)
+{
+	struct hostapd_sta_info *info;
+
+	dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list)
+		if (os_memcmp(addr, info->addr, ETH_ALEN) == 0)
+			return info;
+
+	return NULL;
+}
+
+
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr)
+{
+	struct hostapd_sta_info *info;
+
+	info = sta_track_get(iface, addr);
+	if (info) {
+		/* Move the most recent entry to the end of the list */
+		dl_list_del(&info->list);
+		dl_list_add_tail(&iface->sta_seen, &info->list);
+		os_get_reltime(&info->last_seen);
+		return;
+	}
+
+	/* Add a new entry */
+	info = os_zalloc(sizeof(*info));
+	os_memcpy(info->addr, addr, ETH_ALEN);
+	os_get_reltime(&info->last_seen);
+
+	if (iface->num_sta_seen >= iface->conf->track_sta_max_num) {
+		/* Expire oldest entry to make room for a new one */
+		sta_track_expire(iface, 1);
+	}
+
+	wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for "
+		   MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr));
+	dl_list_add_tail(&iface->sta_seen, &info->list);
+	iface->num_sta_seen++;
+}
+
+
+struct hostapd_data *
+sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
+		  const char *ifname)
+{
+	struct hapd_interfaces *interfaces = iface->interfaces;
+	size_t i, j;
+
+	for (i = 0; i < interfaces->count; i++) {
+		struct hostapd_data *hapd = NULL;
+
+		iface = interfaces->iface[i];
+		for (j = 0; j < iface->num_bss; j++) {
+			hapd = iface->bss[j];
+			if (os_strcmp(ifname, hapd->conf->iface) == 0)
+				break;
+			hapd = NULL;
+		}
+
+		if (hapd && sta_track_get(iface, addr))
+			return hapd;
+	}
+
+	return NULL;
+}
+
+
+void handle_probe_req(struct hostapd_data *hapd,
+		      const struct ieee80211_mgmt *mgmt, size_t len,
+		      int ssi_signal)
+{
+	u8 *resp;
+	struct ieee802_11_elems elems;
+	const u8 *ie;
+	size_t ie_len;
+	size_t i, resp_len;
+	int noack;
+	enum ssid_match_result res;
+
+	ie = mgmt->u.probe_req.variable;
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+		return;
+	if (hapd->iconf->track_sta_max_num)
+		sta_track_add(hapd->iface, mgmt->sa);
+	ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+
+	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
+		if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
+					    mgmt->sa, mgmt->da, mgmt->bssid,
+					    ie, ie_len, ssi_signal) > 0)
+			return;
+
+	if (!hapd->iconf->send_probe_response)
+		return;
+
+	if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR,
+			   MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if ((!elems.ssid || !elems.supp_rates)) {
+		wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
+			   "without SSID or supported rates element",
+			   MAC2STR(mgmt->sa));
+		return;
+	}
+
+	/*
+	 * No need to reply if the Probe Request frame was sent on an adjacent
+	 * channel. IEEE Std 802.11-2012 describes this as a requirement for an
+	 * AP with dot11RadioMeasurementActivated set to true, but strictly
+	 * speaking does not allow such ignoring of Probe Request frames if
+	 * dot11RadioMeasurementActivated is false. Anyway, this can help reduce
+	 * number of unnecessary Probe Response frames for cases where the STA
+	 * is less likely to see them (Probe Request frame sent on a
+	 * neighboring, but partially overlapping, channel).
+	 */
+	if (elems.ds_params &&
+	    hapd->iface->current_mode &&
+	    (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ||
+	     hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) &&
+	    hapd->iconf->channel != elems.ds_params[0]) {
+		wpa_printf(MSG_DEBUG,
+			   "Ignore Probe Request due to DS Params mismatch: chan=%u != ds.chan=%u",
+			   hapd->iconf->channel, elems.ds_params[0]);
+		return;
+	}
+
+#ifdef CONFIG_P2P
+	if (hapd->p2p && elems.wps_ie) {
+		struct wpabuf *wps;
+		wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+		if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) {
+			wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
+				   "due to mismatch with Requested Device "
+				   "Type");
+			wpabuf_free(wps);
+			return;
+		}
+		wpabuf_free(wps);
+	}
+
+	if (hapd->p2p && elems.p2p) {
+		struct wpabuf *p2p;
+		p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE);
+		if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) {
+			wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
+				   "due to mismatch with Device ID");
+			wpabuf_free(p2p);
+			return;
+		}
+		wpabuf_free(p2p);
+	}
+#endif /* CONFIG_P2P */
+
+	if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 &&
+	    elems.ssid_list_len == 0) {
+		wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
+			   "broadcast SSID ignored", MAC2STR(mgmt->sa));
+		return;
+	}
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+	    elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
+	    os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
+		      P2P_WILDCARD_SSID_LEN) == 0) {
+		/* Process P2P Wildcard SSID like Wildcard SSID */
+		elems.ssid_len = 0;
+	}
+#endif /* CONFIG_P2P */
+
+	res = ssid_match(hapd, elems.ssid, elems.ssid_len,
+			 elems.ssid_list, elems.ssid_list_len);
+	if (res == NO_SSID_MATCH) {
+		if (!(mgmt->da[0] & 0x01)) {
+			wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+				   " for foreign SSID '%s' (DA " MACSTR ")%s",
+				   MAC2STR(mgmt->sa),
+				   wpa_ssid_txt(elems.ssid, elems.ssid_len),
+				   MAC2STR(mgmt->da),
+				   elems.ssid_list ? " (SSID list)" : "");
+		}
+		return;
+	}
+
+#ifdef CONFIG_INTERWORKING
+	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) {
+			wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+				   " for mismatching ANT %u ignored",
+				   MAC2STR(mgmt->sa), ant);
+			return;
+		}
+	}
+
+	if (hapd->conf->interworking && elems.interworking &&
+	    (elems.interworking_len == 7 || elems.interworking_len == 9)) {
+		const u8 *hessid;
+		if (elems.interworking_len == 7)
+			hessid = elems.interworking + 1;
+		else
+			hessid = elems.interworking + 1 + 2;
+		if (!is_broadcast_ether_addr(hessid) &&
+		    os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) {
+			wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+				   " for mismatching HESSID " MACSTR
+				   " ignored",
+				   MAC2STR(mgmt->sa), MAC2STR(hessid));
+			return;
+		}
+	}
+#endif /* CONFIG_INTERWORKING */
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+	    supp_rates_11b_only(&elems)) {
+		/* Indicates support for 11b rates only */
+		wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from "
+			   MACSTR " with only 802.11b rates",
+			   MAC2STR(mgmt->sa));
+		return;
+	}
+#endif /* CONFIG_P2P */
+
+	/* TODO: verify that supp_rates contains at least one matching rate
+	 * with AP configuration */
+
+	if (hapd->conf->no_probe_resp_if_seen_on &&
+	    is_multicast_ether_addr(mgmt->da) &&
+	    is_multicast_ether_addr(mgmt->bssid) &&
+	    sta_track_seen_on(hapd->iface, mgmt->sa,
+			      hapd->conf->no_probe_resp_if_seen_on)) {
+		wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
+			   " since STA has been seen on %s",
+			   hapd->conf->iface, MAC2STR(mgmt->sa),
+			   hapd->conf->no_probe_resp_if_seen_on);
+		return;
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->iconf->ignore_probe_probability > 0.0 &&
+	    drand48() < hapd->iconf->ignore_probe_probability) {
+		wpa_printf(MSG_INFO,
+			   "TESTING: ignoring probe request from " MACSTR,
+			   MAC2STR(mgmt->sa));
+		return;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
+				      &resp_len);
+	if (resp == NULL)
+		return;
+
+	/*
+	 * If this is a broadcast probe request, apply no ack policy to avoid
+	 * excessive retries.
+	 */
+	noack = !!(res == WILDCARD_SSID_MATCH &&
+		   is_broadcast_ether_addr(mgmt->da));
+
+	if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0)
+		wpa_printf(MSG_INFO, "handle_probe_req: send failed");
+
+	os_free(resp);
+
+	wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s "
+		   "SSID", MAC2STR(mgmt->sa),
+		   elems.ssid_len == 0 ? "broadcast" : "our");
+}
+
+
+static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
+					size_t *resp_len)
+{
+	/* check probe response offloading caps and print warnings */
+	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD))
+		return NULL;
+
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state && hapd->wps_probe_resp_ie &&
+	    (!(hapd->iface->probe_resp_offloads &
+	       (WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS |
+		WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2))))
+		wpa_printf(MSG_WARNING, "Device is trying to offload WPS "
+			   "Probe Response while not supporting this");
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_probe_resp_ie &&
+	    !(hapd->iface->probe_resp_offloads &
+	      WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P))
+		wpa_printf(MSG_WARNING, "Device is trying to offload P2P "
+			   "Probe Response while not supporting this");
+#endif  /* CONFIG_P2P */
+
+	if (hapd->conf->interworking &&
+	    !(hapd->iface->probe_resp_offloads &
+	      WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING))
+		wpa_printf(MSG_WARNING, "Device is trying to offload "
+			   "Interworking Probe Response while not supporting "
+			   "this");
+
+	/* Generate a Probe Response template for the non-P2P case */
+	return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len);
+}
+
+#endif /* NEED_AP_MLME */
+
+
+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;
+#ifdef NEED_AP_MLME
+	u16 capab_info;
+	u8 *pos, *tailpos;
+
+#define BEACON_HEAD_BUF_SIZE 256
+#define BEACON_TAIL_BUF_SIZE 512
+	head = os_zalloc(BEACON_HEAD_BUF_SIZE);
+	tail_len = BEACON_TAIL_BUF_SIZE;
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state && hapd->wps_beacon_ie)
+		tail_len += wpabuf_len(hapd->wps_beacon_ie);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+	if (hapd->p2p_beacon_ie)
+		tail_len += wpabuf_len(hapd->p2p_beacon_ie);
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies)
+		tail_len += wpabuf_len(hapd->iface->fst_ies);
+#endif /* CONFIG_FST */
+	if (hapd->conf->vendor_elements)
+		tail_len += wpabuf_len(hapd->conf->vendor_elements);
+
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->conf->vendor_vht) {
+		tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+			2 + sizeof(struct ieee80211_vht_operation);
+	}
+#endif /* CONFIG_IEEE80211AC */
+
+	tailpos = tail = os_malloc(tail_len);
+	if (head == NULL || tail == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to set beacon data");
+		os_free(head);
+		os_free(tail);
+		return -1;
+	}
+
+	head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_BEACON);
+	head->duration = host_to_le16(0);
+	os_memset(head->da, 0xff, ETH_ALEN);
+
+	os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
+	head->u.beacon.beacon_int =
+		host_to_le16(hapd->iconf->beacon_int);
+
+	/* hardware or low-level driver will setup seq_ctrl and timestamp */
+	capab_info = hostapd_own_capab_info(hapd);
+	head->u.beacon.capab_info = host_to_le16(capab_info);
+	pos = &head->u.beacon.variable[0];
+
+	/* SSID */
+	*pos++ = WLAN_EID_SSID;
+	if (hapd->conf->ignore_broadcast_ssid == 2) {
+		/* clear the data, but keep the correct length of the SSID */
+		*pos++ = hapd->conf->ssid.ssid_len;
+		os_memset(pos, 0, hapd->conf->ssid.ssid_len);
+		pos += hapd->conf->ssid.ssid_len;
+	} else if (hapd->conf->ignore_broadcast_ssid) {
+		*pos++ = 0; /* empty SSID */
+	} else {
+		*pos++ = hapd->conf->ssid.ssid_len;
+		os_memcpy(pos, hapd->conf->ssid.ssid,
+			  hapd->conf->ssid.ssid_len);
+		pos += hapd->conf->ssid.ssid_len;
+	}
+
+	/* Supported rates */
+	pos = hostapd_eid_supp_rates(hapd, pos);
+
+	/* DS Params */
+	pos = hostapd_eid_ds_params(hapd, pos);
+
+	head_len = pos - (u8 *) head;
+
+	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);
+
+	/* Extended supported rates */
+	tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos);
+
+	/* RSN, MDIE, WPA */
+	tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
+				  tailpos);
+
+	tailpos = hostapd_eid_rm_enabled_capab(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);
+#endif /* CONFIG_IEEE80211N */
+
+	tailpos = hostapd_eid_ext_capab(hapd, tailpos);
+
+	/*
+	 * TODO: Time Advertisement element should only be included in some
+	 * DTIM Beacon frames.
+	 */
+	tailpos = hostapd_eid_time_adv(hapd, tailpos);
+
+	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_FST
+	if (hapd->iface->fst_ies) {
+		os_memcpy(tailpos, wpabuf_head(hapd->iface->fst_ies),
+			  wpabuf_len(hapd->iface->fst_ies));
+		tailpos += wpabuf_len(hapd->iface->fst_ies);
+	}
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+		tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
+		tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+	}
+	if (hapd->conf->vendor_vht)
+		tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
+#endif /* CONFIG_IEEE80211AC */
+
+	/* Wi-Fi Alliance WMM */
+	tailpos = hostapd_eid_wmm(hapd, tailpos);
+
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state && hapd->wps_beacon_ie) {
+		os_memcpy(tailpos, wpabuf_head(hapd->wps_beacon_ie),
+			  wpabuf_len(hapd->wps_beacon_ie));
+		tailpos += wpabuf_len(hapd->wps_beacon_ie);
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) {
+		os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie),
+			  wpabuf_len(hapd->p2p_beacon_ie));
+		tailpos += wpabuf_len(hapd->p2p_beacon_ie);
+	}
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_P2P_MANAGER
+	if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
+	    P2P_MANAGE)
+		tailpos = hostapd_eid_p2p_manage(hapd, tailpos);
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_HS20
+	tailpos = hostapd_eid_hs20_indication(hapd, tailpos);
+	tailpos = hostapd_eid_osen(hapd, tailpos);
+#endif /* CONFIG_HS20 */
+
+	if (hapd->conf->vendor_elements) {
+		os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
+			  wpabuf_len(hapd->conf->vendor_elements));
+		tailpos += wpabuf_len(hapd->conf->vendor_elements);
+	}
+
+	tail_len = tailpos > tail ? tailpos - tail : 0;
+
+	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;
+	if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
+	    (WPA_PROTO_WPA | WPA_PROTO_RSN))
+		params->pairwise_ciphers = hapd->conf->wpa_pairwise |
+			hapd->conf->rsn_pairwise;
+	else if (hapd->conf->wpa & WPA_PROTO_RSN)
+		params->pairwise_ciphers = hapd->conf->rsn_pairwise;
+	else if (hapd->conf->wpa & WPA_PROTO_WPA)
+		params->pairwise_ciphers = 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 ||
+		(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;
+		break;
+	case 1:
+		params->hide_ssid = HIDDEN_SSID_ZERO_LEN;
+		break;
+	case 2:
+		params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
+		break;
+	}
+	params->isolate = hapd->conf->isolate;
+	params->smps_mode = hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_MASK;
+#ifdef NEED_AP_MLME
+	params->cts_protect = !!(ieee802_11_erp_info(hapd) &
+				ERP_INFO_USE_PROTECTION);
+	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 =
+			hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1;
+	else
+		params->short_slot_time = -1;
+	if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
+		params->ht_opmode = -1;
+	else
+		params->ht_opmode = hapd->iface->ht_op_mode;
+#endif /* NEED_AP_MLME */
+	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;
+#ifdef CONFIG_P2P
+	params->p2p_go_ctwindow = hapd->iconf->p2p_go_ctwindow;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_HS20
+	params->disable_dgaf = hapd->conf->disable_dgaf;
+	if (hapd->conf->osen) {
+		params->privacy = 1;
+		params->osen = 1;
+	}
+#endif /* CONFIG_HS20 */
+	return 0;
+}
+
+
+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;
+	params.reenable = hapd->reenable_beacon;
+	hapd->reenable_beacon = 0;
+
+	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;
+	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 */
+int ieee802_11_update_beacons(struct hostapd_iface *iface)
+{
+	size_t 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/hostap/src/ap/beacon.h b/hostap/src/ap/beacon.h
new file mode 100644
index 0000000..d98f42e
--- /dev/null
+++ b/hostap/src/ap/beacon.h
@@ -0,0 +1,30 @@
+/*
+ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
+ * Copyright (c) 2002-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BEACON_H
+#define BEACON_H
+
+struct ieee80211_mgmt;
+
+void handle_probe_req(struct hostapd_data *hapd,
+		      const struct ieee80211_mgmt *mgmt, size_t len,
+		      int ssi_signal);
+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);
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr);
+void sta_track_expire(struct hostapd_iface *iface, int force);
+struct hostapd_data *
+sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
+		  const char *ifname);
+
+#endif /* BEACON_H */
diff --git a/hostap/src/ap/bss_load.c b/hostap/src/ap/bss_load.c
new file mode 100644
index 0000000..fb63942
--- /dev/null
+++ b/hostap/src/ap/bss_load.c
@@ -0,0 +1,65 @@
+/*
+ * BSS Load Element / Channel Utilization
+ * Copyright (c) 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 "hostapd.h"
+#include "bss_load.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+
+
+static void update_channel_utilization(void *eloop_data, void *user_data)
+{
+	struct hostapd_data *hapd = eloop_data;
+	unsigned int sec, usec;
+	int err;
+
+	if (!(hapd->beacon_set_done && hapd->started))
+		return;
+
+	err = hostapd_drv_get_survey(hapd, hapd->iface->freq);
+	if (err) {
+		wpa_printf(MSG_ERROR, "BSS Load: Failed to get survey data");
+		return;
+	}
+
+	ieee802_11_set_beacon(hapd);
+
+	sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
+	usec = (hapd->bss_load_update_timeout % 1000) * 1024;
+	eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
+			       NULL);
+}
+
+
+int bss_load_update_init(struct hostapd_data *hapd)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+	struct hostapd_config *iconf = hapd->iconf;
+	unsigned int sec, usec;
+
+	if (!conf->bss_load_update_period || !iconf->beacon_int)
+		return -1;
+
+	hapd->bss_load_update_timeout = conf->bss_load_update_period *
+					iconf->beacon_int;
+	sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
+	usec = (hapd->bss_load_update_timeout % 1000) * 1024;
+	eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
+			       NULL);
+	return 0;
+}
+
+
+void bss_load_update_deinit(struct hostapd_data *hapd)
+{
+	eloop_cancel_timeout(update_channel_utilization, hapd, NULL);
+}
diff --git a/hostap/src/ap/bss_load.h b/hostap/src/ap/bss_load.h
new file mode 100644
index 0000000..ac3c793
--- /dev/null
+++ b/hostap/src/ap/bss_load.h
@@ -0,0 +1,17 @@
+/*
+ * BSS load update
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BSS_LOAD_UPDATE_H
+#define BSS_LOAD_UPDATE_H
+
+
+int bss_load_update_init(struct hostapd_data *hapd);
+void bss_load_update_deinit(struct hostapd_data *hapd);
+
+
+#endif /* BSS_LOAD_UPDATE_H */
diff --git a/hostap/src/ap/ctrl_iface_ap.c b/hostap/src/ap/ctrl_iface_ap.c
new file mode 100644
index 0000000..c98978f
--- /dev/null
+++ b/hostap/src/ap/ctrl_iface_ap.c
@@ -0,0 +1,556 @@
+/*
+ * Control interface for shared AP commands
+ * 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.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/sae.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "fst/fst_ctrl_iface.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "wps_hostapd.h"
+#include "p2p_hostapd.h"
+#include "ctrl_iface_ap.h"
+#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 (os_snprintf_error(buflen, ret))
+		return 0;
+	return ret;
+}
+
+
+static int hostapd_get_sta_conn_time(struct sta_info *sta,
+				     char *buf, size_t buflen)
+{
+	struct os_reltime age;
+	int ret;
+
+	if (!sta->connected_time.sec)
+		return 0;
+
+	os_reltime_age(&sta->connected_time, &age);
+
+	ret = os_snprintf(buf, buflen, "connected_time=%u\n",
+			  (unsigned int) age.sec);
+	if (os_snprintf_error(buflen, ret))
+		return 0;
+	return ret;
+}
+
+
+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 "?";
+}
+
+
+static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
+				      struct sta_info *sta,
+				      char *buf, size_t buflen)
+{
+	int len, res, ret, i;
+
+	if (!sta)
+		return 0;
+
+	len = 0;
+	ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=",
+			  MAC2STR(sta->addr));
+	if (os_snprintf_error(buflen - len, ret))
+		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 (os_snprintf_error(buflen - len, ret))
+		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 (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
+	ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n",
+			  timeout_next_str(sta->timeout_next));
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
+	if (res >= 0)
+		len += res;
+	res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len);
+	if (res >= 0)
+		len += res;
+	res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
+	if (res >= 0)
+		len += res;
+	res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len,
+				      buflen - len);
+	if (res >= 0)
+		len += res;
+	res = hostapd_p2p_get_mib_sta(hapd, 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);
+
+#ifdef CONFIG_SAE
+	if (sta->sae && sta->sae->state == SAE_ACCEPTED) {
+		res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n",
+				  sta->sae->group);
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+	}
+#endif /* CONFIG_SAE */
+
+	if (sta->vlan_id > 0) {
+		res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n",
+				  sta->vlan_id);
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+	}
+
+	return len;
+}
+
+
+int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
+				 char *buf, size_t buflen)
+{
+	return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
+}
+
+
+int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
+			   char *buf, size_t buflen)
+{
+	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");
+		if (os_snprintf_error(buflen, ret))
+			return 0;
+		return ret;
+	}
+
+	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;
+	}
+
+	ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
+	ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret);
+
+	return ret;
+}
+
+
+int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
+				char *buf, size_t buflen)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	int ret;
+
+	if (hwaddr_aton(txtaddr, addr) ||
+	    (sta = ap_get_sta(hapd, addr)) == NULL) {
+		ret = os_snprintf(buf, buflen, "FAIL\n");
+		if (os_snprintf_error(buflen, ret))
+			return 0;
+		return ret;
+	}
+
+	if (!sta->next)
+		return 0;
+
+	return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
+}
+
+
+#ifdef CONFIG_P2P_MANAGER
+static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
+				  u8 minor_reason_code, const u8 *addr)
+{
+	struct ieee80211_mgmt *mgmt;
+	int ret;
+	u8 *pos;
+
+	if (hapd->driver->send_frame == NULL)
+		return -1;
+
+	mgmt = os_zalloc(sizeof(*mgmt) + 100);
+	if (mgmt == NULL)
+		return -1;
+
+	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);
+	if (stype == WLAN_FC_STYPE_DEAUTH) {
+		mgmt->u.deauth.reason_code =
+			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+		pos = (u8 *) (&mgmt->u.deauth.reason_code + 1);
+	} else {
+		mgmt->u.disassoc.reason_code =
+			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+		pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1);
+	}
+
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = 4 + 3 + 1;
+	WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE);
+	pos += 4;
+
+	*pos++ = P2P_ATTR_MINOR_REASON_CODE;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	*pos++ = minor_reason_code;
+
+	ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt,
+				       pos - (u8 *) mgmt, 1);
+	os_free(mgmt);
+
+	return ret < 0 ? -1 : 0;
+}
+#endif /* CONFIG_P2P_MANAGER */
+
+
+int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
+				      const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	const char *pos;
+	u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
+		txtaddr);
+
+	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;
+		int encrypt;
+		if (hapd->driver->send_frame == NULL)
+			return -1;
+		pos += 6;
+		encrypt = atoi(pos);
+		os_memset(&mgmt, 0, sizeof(mgmt));
+		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+						  WLAN_FC_STYPE_DEAUTH);
+		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(reason);
+		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
+					     IEEE80211_HDRLEN +
+					     sizeof(mgmt.u.deauth),
+					     encrypt) < 0)
+			return -1;
+		return 0;
+	}
+
+#ifdef CONFIG_P2P_MANAGER
+	pos = os_strstr(txtaddr, " p2p=");
+	if (pos) {
+		return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH,
+					      atoi(pos + 5), addr);
+	}
+#endif /* CONFIG_P2P_MANAGER */
+
+	hostapd_drv_sta_deauth(hapd, addr, reason);
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		ap_sta_deauthenticate(hapd, sta, reason);
+	else if (addr[0] == 0xff)
+		hostapd_free_stas(hapd);
+
+	return 0;
+}
+
+
+int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
+				    const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	const char *pos;
+	u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
+		txtaddr);
+
+	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;
+		int encrypt;
+		if (hapd->driver->send_frame == NULL)
+			return -1;
+		pos += 6;
+		encrypt = atoi(pos);
+		os_memset(&mgmt, 0, sizeof(mgmt));
+		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+						  WLAN_FC_STYPE_DISASSOC);
+		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(reason);
+		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
+					     IEEE80211_HDRLEN +
+					     sizeof(mgmt.u.deauth),
+					     encrypt) < 0)
+			return -1;
+		return 0;
+	}
+
+#ifdef CONFIG_P2P_MANAGER
+	pos = os_strstr(txtaddr, " p2p=");
+	if (pos) {
+		return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC,
+					      atoi(pos + 5), addr);
+	}
+#endif /* CONFIG_P2P_MANAGER */
+
+	hostapd_drv_sta_disassoc(hapd, addr, reason);
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		ap_sta_disassociate(hapd, sta, reason);
+	else if (addr[0] == 0xff)
+		hostapd_free_stas(hapd);
+
+	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 (os_snprintf_error(buflen - len, ret))
+		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 (os_snprintf_error(buflen - len, ret))
+		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 (os_snprintf_error(buflen - len, ret))
+		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 (os_snprintf_error(buflen - len, ret))
+			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;
+}
+
+
+int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
+{
+	return hostapd_drv_stop_ap(hapd);
+}
diff --git a/hostap/src/ap/ctrl_iface_ap.h b/hostap/src/ap/ctrl_iface_ap.h
new file mode 100644
index 0000000..e5297d0
--- /dev/null
+++ b/hostap/src/ap/ctrl_iface_ap.h
@@ -0,0 +1,28 @@
+/*
+ * Control interface for shared AP commands
+ * 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.
+ */
+
+#ifndef CTRL_IFACE_AP_H
+#define CTRL_IFACE_AP_H
+
+int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
+				 char *buf, size_t buflen);
+int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
+			   char *buf, size_t buflen);
+int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
+				char *buf, size_t buflen);
+int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
+				      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);
+int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd);
+
+#endif /* CTRL_IFACE_AP_H */
diff --git a/hostap/src/ap/dfs.c b/hostap/src/ap/dfs.c
new file mode 100644
index 0000000..715f19b
--- /dev/null
+++ b/hostap/src/ap/dfs.c
@@ -0,0 +1,1072 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013-2015, 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/hw_features_common.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 *seg1)
+{
+	int n_chans = 1;
+
+	*seg1 = 0;
+
+	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;
+		case VHT_CHANWIDTH_80P80MHZ:
+			n_chans = 4;
+			*seg1 = 4;
+			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 struct hostapd_channel_data *
+dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx)
+{
+	int i;
+
+	for (i = first_chan_idx; i < mode->num_channels; i++) {
+		if (mode->channels[i].freq == freq)
+			return &mode->channels[i];
+	}
+
+	return NULL;
+}
+
+
+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 = dfs_get_chan_data(mode, first_chan->freq + i * 20,
+					 first_chan_idx);
+		if (!chan)
+			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)
+{
+	if (!iface->conf->acs_ch_list.num)
+		return 1;
+
+	return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
+}
+
+
+/*
+ * 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, n_chans1;
+
+	mode = iface->current_mode;
+	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+	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, int *seg1_start)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+	int channel_no = iface->conf->channel;
+	int res = -1, i;
+	int chan_seg1 = -1;
+
+	*seg1_start = -1;
+
+	/* 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;
+		case VHT_CHANWIDTH_80P80MHZ:
+			channel_no =
+				iface->conf->vht_oper_centr_freq_seg0_idx - 6;
+			chan_seg1 =
+				iface->conf->vht_oper_centr_freq_seg1_idx - 6;
+			break;
+		default:
+			wpa_printf(MSG_INFO,
+				   "DFS only VHT20/40/80/160/80+80 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 && chan_seg1 > -1) {
+		int found = 0;
+
+		/* Get idx for seg1 */
+		mode = iface->current_mode;
+		for (i = 0; i < mode->num_channels; i++) {
+			chan = &mode->channels[i];
+			if (chan->chan == chan_seg1) {
+				*seg1_start = i;
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			res = -1;
+	}
+
+	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;
+
+	if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+		_rand = os_random();
+	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, start_chan_idx1;
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+	int n_chans, n_chans1, 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, &start_chan_idx1);
+	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+	/* 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, n_chans1, start_chan_idx, start_chan_idx1;
+	int skip_radar = 0;
+
+	if (!iface->current_mode) {
+		/*
+		 * This can happen with drivers that do not provide mode
+		 * information and as such, cannot really use hostapd for DFS.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "DFS: No current_mode information - assume no need to perform DFS operations by hostapd");
+		return 1;
+	}
+
+	iface->cac_started = 0;
+
+	do {
+		/* Get start (first) channel for current configuration */
+		start_chan_idx = dfs_get_start_chan_idx(iface,
+							&start_chan_idx1);
+		if (start_chan_idx == -1)
+			return -1;
+
+		/* Get number of used channels, depend on width */
+		n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+		/* 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 */
+		if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+			/* Complete AP configuration for the first bring up. */
+			if (iface->state != HAPD_IFACE_ENABLED)
+				hostapd_setup_interface_complete(iface, 0);
+			else
+				iface->cac_started = 0;
+		} else {
+			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;
+
+	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);
+
+	/* Proceed only if DFS is not offloaded to the driver */
+	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+		return 0;
+
+	if (!iface->conf->ieee80211h)
+		return 0;
+
+	/* 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);
+
+	/* Proceed only if DFS is not offloaded to the driver */
+	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+		return 0;
+
+	/* 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, n_chans1, start_chan_idx, start_chan_idx1, res;
+
+	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, &start_chan_idx1);
+	if (start_chan_idx == -1)
+		return -1;
+
+	/* Get number of used channels, depend on width */
+	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+	/* Check if any of configured channels require DFS */
+	res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
+	if (res)
+		return res;
+	if (start_chan_idx1 >= 0 && n_chans1 > 0)
+		res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1);
+	return res;
+}
+
+
+int hostapd_dfs_start_cac(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_CAC_START
+		"freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
+		"seg1=%d cac_time=%ds",
+		freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, 60);
+	iface->cac_started = 1;
+	return 0;
+}
+
+
+/*
+ * Main DFS handler for offloaded case.
+ * 2 - continue channel/AP setup for non-DFS channel
+ * 1 - continue channel/AP setup for DFS channel
+ * 0 - channel/AP setup will be continued after CAC
+ * -1 - hit critical error
+ */
+int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
+{
+	wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
+		   __func__, iface->cac_started);
+
+	/*
+	 * If DFS has already been started, then we are being called from a
+	 * callback to continue AP/channel setup. Reset the CAC start flag and
+	 * return.
+	 */
+	if (iface->cac_started) {
+		wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
+			   __func__, iface->cac_started);
+		iface->cac_started = 0;
+		return 1;
+	}
+
+	if (ieee80211_is_dfs(iface->freq)) {
+		wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS",
+			   __func__, iface->freq);
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "%s: freq %d MHz does not require DFS. Continue channel/AP setup",
+		   __func__, iface->freq);
+	return 2;
+}
diff --git a/hostap/src/ap/dfs.h b/hostap/src/ap/dfs.h
new file mode 100644
index 0000000..be8c0e6
--- /dev/null
+++ b/hostap/src/ap/dfs.h
@@ -0,0 +1,30 @@
+/*
+ * 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);
+int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+			  int ht_enabled, int chan_offset, int chan_width,
+			  int cf1, int cf2);
+int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
+
+#endif /* DFS_H */
diff --git a/hostap/src/ap/dhcp_snoop.c b/hostap/src/ap/dhcp_snoop.c
new file mode 100644
index 0000000..3a77225
--- /dev/null
+++ b/hostap/src/ap/dhcp_snoop.c
@@ -0,0 +1,178 @@
+/*
+ * DHCP snooping for Proxy ARP
+ * Copyright (c) 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 <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include "utils/common.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "x_snoop.h"
+#include "dhcp_snoop.h"
+
+struct bootp_pkt {
+	struct iphdr iph;
+	struct udphdr udph;
+	u8 op;
+	u8 htype;
+	u8 hlen;
+	u8 hops;
+	be32 xid;
+	be16 secs;
+	be16 flags;
+	be32 client_ip;
+	be32 your_ip;
+	be32 server_ip;
+	be32 relay_ip;
+	u8 hw_addr[16];
+	u8 serv_name[64];
+	u8 boot_file[128];
+	u8 exten[312];
+} STRUCT_PACKED;
+
+#define DHCPACK	5
+static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 };
+
+
+static const char * ipaddr_str(u32 addr)
+{
+	static char buf[17];
+
+	os_snprintf(buf, sizeof(buf), "%u.%u.%u.%u",
+		    (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+		    (addr >> 8) & 0xff, addr & 0xff);
+	return buf;
+}
+
+
+static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
+			size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	const struct bootp_pkt *b;
+	struct sta_info *sta;
+	int exten_len;
+	const u8 *end, *pos;
+	int res, msgtype = 0, prefixlen = 32;
+	u32 subnet_mask = 0;
+	u16 tot_len;
+
+	exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten));
+	if (exten_len < 4)
+		return;
+
+	b = (const struct bootp_pkt *) &buf[ETH_HLEN];
+	tot_len = ntohs(b->iph.tot_len);
+	if (tot_len > (unsigned int) (len - ETH_HLEN))
+		return;
+
+	if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie)))
+		return;
+
+	/* Parse DHCP options */
+	end = (const u8 *) b + tot_len;
+	pos = &b->exten[4];
+	while (pos < end && *pos != 0xff) {
+		const u8 *opt = pos++;
+
+		if (*opt == 0) /* padding */
+			continue;
+
+		pos += *pos + 1;
+		if (pos >= end)
+			break;
+
+		switch (*opt) {
+		case 1:  /* subnet mask */
+			if (opt[1] == 4)
+				subnet_mask = WPA_GET_BE32(&opt[2]);
+			if (subnet_mask == 0)
+				return;
+			while (!(subnet_mask & 0x1)) {
+				subnet_mask >>= 1;
+				prefixlen--;
+			}
+			break;
+		case 53: /* message type */
+			if (opt[1])
+				msgtype = opt[2];
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (msgtype == DHCPACK) {
+		if (b->your_ip == 0)
+			return;
+
+		/* DHCPACK for DHCPREQUEST */
+		sta = ap_get_sta(hapd, b->hw_addr);
+		if (!sta)
+			return;
+
+		wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR
+			   " @ IPv4 address %s/%d",
+			   MAC2STR(sta->addr), ipaddr_str(ntohl(b->your_ip)),
+			   prefixlen);
+
+		if (sta->ipaddr == b->your_ip)
+			return;
+
+		if (sta->ipaddr != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "dhcp_snoop: Removing IPv4 address %s from the ip neigh table",
+				   ipaddr_str(be_to_host32(sta->ipaddr)));
+			hostapd_drv_br_delete_ip_neigh(hapd, 4,
+						       (u8 *) &sta->ipaddr);
+		}
+
+		res = hostapd_drv_br_add_ip_neigh(hapd, 4, (u8 *) &b->your_ip,
+						  prefixlen, sta->addr);
+		if (res) {
+			wpa_printf(MSG_DEBUG,
+				   "dhcp_snoop: Adding ip neigh table failed: %d",
+				   res);
+			return;
+		}
+		sta->ipaddr = b->your_ip;
+	}
+
+	if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (!(sta->flags & WLAN_STA_AUTHORIZED))
+				continue;
+			x_snoop_mcast_to_ucast_convert_send(hapd, sta,
+							    (u8 *) buf, len);
+		}
+	}
+}
+
+
+int dhcp_snoop_init(struct hostapd_data *hapd)
+{
+	hapd->sock_dhcp = x_snoop_get_l2_packet(hapd, handle_dhcp,
+						L2_PACKET_FILTER_DHCP);
+	if (hapd->sock_dhcp == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void dhcp_snoop_deinit(struct hostapd_data *hapd)
+{
+	l2_packet_deinit(hapd->sock_dhcp);
+}
diff --git a/hostap/src/ap/dhcp_snoop.h b/hostap/src/ap/dhcp_snoop.h
new file mode 100644
index 0000000..93d0050
--- /dev/null
+++ b/hostap/src/ap/dhcp_snoop.h
@@ -0,0 +1,30 @@
+/*
+ * DHCP snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DHCP_SNOOP_H
+#define DHCP_SNOOP_H
+
+#ifdef CONFIG_PROXYARP
+
+int dhcp_snoop_init(struct hostapd_data *hapd);
+void dhcp_snoop_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_PROXYARP */
+
+static inline int dhcp_snoop_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void dhcp_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_PROXYARP */
+
+#endif /* DHCP_SNOOP_H */
diff --git a/hostap/src/ap/drv_callbacks.c b/hostap/src/ap/drv_callbacks.c
new file mode 100644
index 0000000..ca8b75c
--- /dev/null
+++ b/hostap/src/ap/drv_callbacks.c
@@ -0,0 +1,1324 @@
+/*
+ * hostapd / Callback functions for driver wrappers
+ * 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.
+ */
+
+#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"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/random.h"
+#include "p2p/p2p.h"
+#include "wps/wps.h"
+#include "fst/fst.h"
+#include "wnm_ap.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "accounting.h"
+#include "tkip_countermeasures.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "wps_hostapd.h"
+#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,
+			const u8 *req_ies, size_t req_ies_len, int reassoc)
+{
+	struct sta_info *sta;
+	int new_assoc, res;
+	struct ieee802_11_elems elems;
+	const u8 *ie;
+	size_t ielen;
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+	u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
+	u8 *p = buf;
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+	u16 reason = WLAN_REASON_UNSPECIFIED;
+	u16 status = WLAN_STATUS_SUCCESS;
+	const u8 *p2p_dev_addr = NULL;
+
+	if (addr == NULL) {
+		/*
+		 * This could potentially happen with unexpected event from the
+		 * driver wrapper. This was seen at least in one case where the
+		 * driver ended up being set to station mode while hostapd was
+		 * running, so better make sure we stop processing such an
+		 * event here.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "hostapd_notif_assoc: Skip event with no address");
+		return -1;
+	}
+	random_add_randomness(addr, ETH_ALEN);
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "associated");
+
+	ieee802_11_parse_elems(req_ies, req_ies_len, &elems, 0);
+	if (elems.wps_ie) {
+		ie = elems.wps_ie - 2;
+		ielen = elems.wps_ie_len + 2;
+		wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)AssocReq");
+	} else if (elems.rsn_ie) {
+		ie = elems.rsn_ie - 2;
+		ielen = elems.rsn_ie_len + 2;
+		wpa_printf(MSG_DEBUG, "STA included RSN IE in (Re)AssocReq");
+	} else if (elems.wpa_ie) {
+		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;
+		wpa_printf(MSG_DEBUG,
+			   "STA did not include WPS/RSN/WPA IE in (Re)AssocReq");
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta) {
+		ap_sta_no_session_timeout(hapd, sta);
+		accounting_sta_stop(hapd, sta);
+
+		/*
+		 * Make sure that the previously registered inactivity timer
+		 * will not remove the STA immediately.
+		 */
+		sta->timeout_next = STA_NULLFUNC;
+	} else {
+		sta = ap_sta_add(hapd, addr);
+		if (sta == NULL) {
+			hostapd_drv_sta_disassoc(hapd, addr,
+						 WLAN_REASON_DISASSOC_AP_BUSY);
+			return -1;
+		}
+	}
+	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+
+#ifdef CONFIG_P2P
+	if (elems.p2p) {
+		wpabuf_free(sta->p2p_ie);
+		sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
+							  P2P_IE_VENDOR_TYPE);
+		if (sta->p2p_ie)
+			p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+	if (elems.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) {
+		sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
+						 elems.hs20_len - 4);
+	} else
+		sta->hs20_ie = NULL;
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_FST
+	wpabuf_free(sta->mb_ies);
+	if (hapd->iface->fst)
+		sta->mb_ies = mb_ies_by_info(&elems.mb_ies);
+	else
+		sta->mb_ies = NULL;
+#endif /* CONFIG_FST */
+
+	if (hapd->conf->wpa) {
+		if (ie == NULL || ielen == 0) {
+#ifdef CONFIG_WPS
+			if (hapd->conf->wps_state) {
+				wpa_printf(MSG_DEBUG,
+					   "STA did not include WPA/RSN IE in (Re)Association Request - possible WPS use");
+				sta->flags |= WLAN_STA_MAYBE_WPS;
+				goto skip_wpa_check;
+			}
+#endif /* CONFIG_WPS */
+
+			wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
+			return -1;
+		}
+#ifdef CONFIG_WPS
+		if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
+		    os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
+			struct wpabuf *wps;
+
+			sta->flags |= WLAN_STA_WPS;
+			wps = ieee802_11_vendor_ie_concat(ie, ielen,
+							  WPS_IE_VENDOR_TYPE);
+			if (wps) {
+				if (wps_is_20(wps)) {
+					wpa_printf(MSG_DEBUG,
+						   "WPS: STA supports WPS 2.0");
+					sta->flags |= WLAN_STA_WPS2;
+				}
+				wpabuf_free(wps);
+			}
+			goto skip_wpa_check;
+		}
+#endif /* CONFIG_WPS */
+
+		if (sta->wpa_sm == NULL)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr,
+							p2p_dev_addr);
+		if (sta->wpa_sm == NULL) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to initialize WPA state machine");
+			return -1;
+		}
+		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+					  ie, ielen,
+					  elems.mdie, elems.mdie_len);
+		if (res != WPA_IE_OK) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA/RSN information element rejected? (res %u)",
+				   res);
+			wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
+			if (res == WPA_INVALID_GROUP) {
+				reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+				status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+			} else if (res == WPA_INVALID_PAIRWISE) {
+				reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
+				status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+			} else if (res == WPA_INVALID_AKMP) {
+				reason = WLAN_REASON_AKMP_NOT_VALID;
+				status = WLAN_STATUS_AKMP_NOT_VALID;
+			}
+#ifdef CONFIG_IEEE80211W
+			else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) {
+				reason = WLAN_REASON_INVALID_IE;
+				status = WLAN_STATUS_INVALID_IE;
+			} else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) {
+				reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+				status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+			}
+#endif /* CONFIG_IEEE80211W */
+			else {
+				reason = WLAN_REASON_INVALID_IE;
+				status = WLAN_STATUS_INVALID_IE;
+			}
+			goto fail;
+		}
+#ifdef CONFIG_IEEE80211W
+		if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+		    sta->sa_query_count > 0)
+			ap_check_sa_query_timeout(hapd, sta);
+		if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+		    (sta->auth_alg != WLAN_AUTH_FT)) {
+			/*
+			 * STA has already been associated with MFP and SA
+			 * Query timeout has not been reached. Reject the
+			 * association attempt temporarily and start SA Query,
+			 * if one is not pending.
+			 */
+
+			if (sta->sa_query_count == 0)
+				ap_sta_start_sa_query(hapd, sta);
+
+			status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+
+			p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
+
+			hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
+					  p - buf);
+			return 0;
+		}
+
+		if (wpa_auth_uses_mfp(sta->wpa_sm))
+			sta->flags |= WLAN_STA_MFP;
+		else
+			sta->flags &= ~WLAN_STA_MFP;
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211R
+		if (sta->auth_alg == WLAN_AUTH_FT) {
+			status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
+							 req_ies_len);
+			if (status != WLAN_STATUS_SUCCESS) {
+				if (status == WLAN_STATUS_INVALID_PMKID)
+					reason = WLAN_REASON_INVALID_IE;
+				if (status == WLAN_STATUS_INVALID_MDIE)
+					reason = WLAN_REASON_INVALID_IE;
+				if (status == WLAN_STATUS_INVALID_FTIE)
+					reason = WLAN_REASON_INVALID_IE;
+				goto fail;
+			}
+		}
+#endif /* CONFIG_IEEE80211R */
+	} else if (hapd->conf->wps_state) {
+#ifdef CONFIG_WPS
+		struct wpabuf *wps;
+
+		if (req_ies)
+			wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
+							  WPS_IE_VENDOR_TYPE);
+		else
+			wps = NULL;
+#ifdef CONFIG_WPS_STRICT
+		if (wps && wps_validate_assoc_req(wps) < 0) {
+			reason = WLAN_REASON_INVALID_IE;
+			status = WLAN_STATUS_INVALID_IE;
+			wpabuf_free(wps);
+			goto fail;
+		}
+#endif /* CONFIG_WPS_STRICT */
+		if (wps) {
+			sta->flags |= WLAN_STA_WPS;
+			if (wps_is_20(wps)) {
+				wpa_printf(MSG_DEBUG,
+					   "WPS: STA supports WPS 2.0");
+				sta->flags |= WLAN_STA_WPS2;
+			}
+		} else
+			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:
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_IEEE80211R
+	p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
+					sta->auth_alg, req_ies, req_ies_len);
+
+	hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+
+	if (sta->auth_alg == WLAN_AUTH_FT)
+		ap_sta_set_authorized(hapd, sta, 1);
+#else /* CONFIG_IEEE80211R */
+	/* Keep compiler silent about unused variables */
+	if (status) {
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+
+	hostapd_set_sta_flags(hapd, sta);
+
+	if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
+	else
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+
+	hostapd_new_assoc_sta(hapd, sta, !new_assoc);
+
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+
+#ifdef CONFIG_P2P
+	if (req_ies) {
+		p2p_group_notif_assoc(hapd->p2p_group, sta->addr,
+				      req_ies, req_ies_len);
+	}
+#endif /* CONFIG_P2P */
+
+	return 0;
+
+fail:
+#ifdef CONFIG_IEEE80211R
+	hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+#endif /* CONFIG_IEEE80211R */
+	hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
+	ap_free_sta(hapd, sta);
+	return -1;
+}
+
+
+void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta;
+
+	if (addr == NULL) {
+		/*
+		 * This could potentially happen with unexpected event from the
+		 * driver wrapper. This was seen at least in one case where the
+		 * driver ended up reporting a station mode event while hostapd
+		 * was running, so better make sure we stop processing such an
+		 * event here.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "hostapd_notif_disassoc: Skip event with no address");
+		return;
+	}
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "disassociated");
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "Disassociation notification for unknown STA "
+			   MACSTR, MAC2STR(addr));
+		return;
+	}
+
+	ap_sta_set_authorized(hapd, sta, 0);
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
+	sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+	ap_free_sta(hapd, sta);
+}
+
+
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	if (!sta || !hapd->conf->disassoc_low_ack)
+		return;
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO,
+		       "disconnected due to excessive missing ACKs");
+	hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
+	if (sta)
+		ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
+}
+
+
+void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+			     int offset, int width, int cf1, int cf2)
+{
+#ifdef NEED_AP_MLME
+	int channel, chwidth, seg0_idx = 0, seg1_idx = 0, is_dfs;
+
+	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO,
+		       "driver had channel switch: freq=%d, ht=%d, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+		       freq, ht, offset, width, channel_width_to_string(width),
+		       cf1, cf2);
+
+	hapd->iface->freq = freq;
+
+	channel = hostapd_hw_get_channel(hapd, freq);
+	if (!channel) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_WARNING,
+			       "driver switched to bad channel!");
+		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;
+	if (!ht)
+		hapd->iconf->ieee80211ac = 0;
+	hapd->iconf->secondary_channel = offset;
+	hapd->iconf->vht_oper_chwidth = chwidth;
+	hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx;
+	hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx;
+
+	is_dfs = ieee80211_is_dfs(freq);
+
+	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 dfs=%d", freq, is_dfs);
+	} else if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
+			"freq=%d dfs=%d", freq, is_dfs);
+	}
+#endif /* NEED_AP_MLME */
+}
+
+
+void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
+					 const u8 *addr, int reason_code)
+{
+	switch (reason_code) {
+	case MAX_CLIENT_REACHED:
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_MAX_STA MACSTR,
+			MAC2STR(addr));
+		break;
+	case BLOCKED_CLIENT:
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_BLOCKED_STA MACSTR,
+			MAC2STR(addr));
+		break;
+	}
+}
+
+
+#ifdef CONFIG_ACS
+static void hostapd_acs_channel_selected(struct hostapd_data *hapd,
+					 struct acs_selected_channels *acs_res)
+{
+	int ret, i;
+
+	if (hapd->iconf->channel) {
+		wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
+			   hapd->iconf->channel);
+		return;
+	}
+
+	if (!hapd->iface->current_mode) {
+		for (i = 0; i < hapd->iface->num_hw_features; i++) {
+			struct hostapd_hw_modes *mode =
+				&hapd->iface->hw_features[i];
+
+			if (mode->mode == acs_res->hw_mode) {
+				hapd->iface->current_mode = mode;
+				break;
+			}
+		}
+		if (!hapd->iface->current_mode) {
+			hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_WARNING,
+				       "driver selected to bad hw_mode");
+			return;
+		}
+	}
+
+	hapd->iface->freq = hostapd_hw_get_freq(hapd, acs_res->pri_channel);
+
+	if (!acs_res->pri_channel) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_WARNING,
+			       "driver switched to bad channel");
+		return;
+	}
+
+	hapd->iconf->channel = acs_res->pri_channel;
+	hapd->iconf->acs = 1;
+
+	if (acs_res->sec_channel == 0)
+		hapd->iconf->secondary_channel = 0;
+	else if (acs_res->sec_channel < acs_res->pri_channel)
+		hapd->iconf->secondary_channel = -1;
+	else if (acs_res->sec_channel > acs_res->pri_channel)
+		hapd->iconf->secondary_channel = 1;
+	else {
+		wpa_printf(MSG_ERROR, "Invalid secondary channel!");
+		return;
+	}
+
+	if (hapd->iface->conf->ieee80211ac) {
+		/* set defaults for backwards compatibility */
+		hapd->iconf->vht_oper_centr_freq_seg1_idx = 0;
+		hapd->iconf->vht_oper_centr_freq_seg0_idx = 0;
+		hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+		if (acs_res->ch_width == 80) {
+			hapd->iconf->vht_oper_centr_freq_seg0_idx =
+				acs_res->vht_seg0_center_ch;
+			hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+		} else if (acs_res->ch_width == 160) {
+			if (acs_res->vht_seg1_center_ch == 0) {
+				hapd->iconf->vht_oper_centr_freq_seg0_idx =
+					acs_res->vht_seg0_center_ch;
+				hapd->iconf->vht_oper_chwidth =
+					VHT_CHANWIDTH_160MHZ;
+			} else {
+				hapd->iconf->vht_oper_centr_freq_seg0_idx =
+					acs_res->vht_seg0_center_ch;
+				hapd->iconf->vht_oper_centr_freq_seg1_idx =
+					acs_res->vht_seg1_center_ch;
+				hapd->iconf->vht_oper_chwidth =
+					VHT_CHANWIDTH_80P80MHZ;
+			}
+		}
+	}
+
+	ret = hostapd_acs_completed(hapd->iface, 0);
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "ACS: Possibly channel configuration is invalid");
+	}
+}
+#endif /* CONFIG_ACS */
+
+
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
+			 const u8 *bssid, const u8 *ie, size_t ie_len,
+			 int ssi_signal)
+{
+	size_t i;
+	int ret = 0;
+
+	if (sa == NULL || ie == NULL)
+		return -1;
+
+	random_add_randomness(sa, ETH_ALEN);
+	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) {
+		if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
+					    sa, da, bssid, ie, ie_len,
+					    ssi_signal) > 0) {
+			ret = 1;
+			break;
+		}
+	}
+	return ret;
+}
+
+
+#ifdef HOSTAPD
+
+#ifdef CONFIG_IEEE80211R
+static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst,
+					  const u8 *bssid,
+					  u16 auth_transaction, u16 status,
+					  const u8 *ies, size_t ies_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, dst);
+	if (sta == NULL)
+		return;
+
+	hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
+	sta->flags |= WLAN_STA_AUTH;
+
+	hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len);
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+static void hostapd_notif_auth(struct hostapd_data *hapd,
+			       struct auth_info *rx_auth)
+{
+	struct sta_info *sta;
+	u16 status = WLAN_STATUS_SUCCESS;
+	u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+	size_t resp_ies_len = 0;
+
+	sta = ap_get_sta(hapd, rx_auth->peer);
+	if (!sta) {
+		sta = ap_sta_add(hapd, rx_auth->peer);
+		if (sta == NULL) {
+			status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+			goto fail;
+		}
+	}
+	sta->flags &= ~WLAN_STA_PREAUTH;
+	ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
+#ifdef CONFIG_IEEE80211R
+	if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) {
+		sta->auth_alg = WLAN_AUTH_FT;
+		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_DEBUG,
+				   "FT: Failed to initialize WPA state machine");
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid,
+				    rx_auth->auth_transaction, rx_auth->ies,
+				    rx_auth->ies_len,
+				    hostapd_notify_auth_ft_finish, hapd);
+		return;
+	}
+#endif /* CONFIG_IEEE80211R */
+fail:
+	hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
+			 status, resp_ies, resp_ies_len);
+}
+
+
+static void hostapd_action_rx(struct hostapd_data *hapd,
+			      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",
+		   mgmt->u.action.category, (int) plen);
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
+		return;
+	}
+#ifdef CONFIG_IEEE80211R
+	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 (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 (mgmt->u.action.category == WLAN_ACTION_WNM) {
+		ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
+	}
+#endif /* CONFIG_WNM */
+#ifdef CONFIG_FST
+	if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) {
+		fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len);
+		return;
+	}
+#endif /* CONFIG_FST */
+
+}
+
+
+#ifdef NEED_AP_MLME
+
+#define HAPD_BROADCAST ((struct hostapd_data *) -1)
+
+static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+					    const u8 *bssid)
+{
+	size_t i;
+
+	if (bssid == NULL)
+		return NULL;
+	if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff &&
+	    bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff)
+		return HAPD_BROADCAST;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		if (os_memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0)
+			return iface->bss[i];
+	}
+
+	return NULL;
+}
+
+
+static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
+					const u8 *bssid, const u8 *addr,
+					int wds)
+{
+	hapd = get_hapd_bssid(hapd->iface, bssid);
+	if (hapd == NULL || hapd == HAPD_BROADCAST)
+		return;
+
+	ieee802_11_rx_from_unknown(hapd, addr, wds);
+}
+
+
+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 0;
+
+	hapd = get_hapd_bssid(iface, bssid);
+	if (hapd == NULL) {
+		u16 fc = le_to_host16(hdr->frame_control);
+
+		/*
+		 * Drop frames to unknown BSSIDs except for Beacon frames which
+		 * could be used to update neighbor information.
+		 */
+		if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+		    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
+			hapd = iface->bss[0];
+		else
+			return 0;
+	}
+
+	os_memset(&fi, 0, sizeof(fi));
+	fi.datarate = rx_mgmt->datarate;
+	fi.ssi_signal = rx_mgmt->ssi_signal;
+
+	if (hapd == HAPD_BROADCAST) {
+		size_t i;
+
+		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
+		ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len,
+				      &fi);
+
+	random_add_randomness(&fi, sizeof(fi));
+
+	return ret;
+}
+
+
+static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
+			       size_t len, u16 stype, int ok)
+{
+	struct ieee80211_hdr *hdr;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+	if (hapd == NULL || hapd == HAPD_BROADCAST)
+		return;
+	ieee802_11_mgmt_cb(hapd, buf, len, stype, ok);
+}
+
+#endif /* NEED_AP_MLME */
+
+
+static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	if (sta)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "Data frame from unknown STA " MACSTR
+		   " - adding a new STA", MAC2STR(addr));
+	sta = ap_sta_add(hapd, addr);
+	if (sta) {
+		hostapd_new_assoc_sta(hapd, sta, 0);
+	} else {
+		wpa_printf(MSG_DEBUG, "Failed to add STA entry for " MACSTR,
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
+				   const u8 *data, size_t data_len)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct sta_info *sta;
+	size_t j;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		sta = ap_get_sta(iface->bss[j], src);
+		if (sta && sta->flags & WLAN_STA_ASSOC) {
+			hapd = iface->bss[j];
+			break;
+		}
+	}
+
+	ieee802_1x_receive(hapd, src, data, data_len);
+}
+
+
+static struct hostapd_channel_data * hostapd_get_mode_channel(
+	struct hostapd_iface *iface, unsigned int freq)
+{
+	int i;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < iface->current_mode->num_channels; i++) {
+		chan = &iface->current_mode->channels[i];
+		if (!chan)
+			return NULL;
+		if ((unsigned int) chan->freq == freq)
+			return chan;
+	}
+
+	return NULL;
+}
+
+
+static void hostapd_update_nf(struct hostapd_iface *iface,
+			      struct hostapd_channel_data *chan,
+			      struct freq_survey *survey)
+{
+	if (!iface->chans_surveyed) {
+		chan->min_nf = survey->nf;
+		iface->lowest_nf = survey->nf;
+	} else {
+		if (dl_list_empty(&chan->survey_list))
+			chan->min_nf = survey->nf;
+		else if (survey->nf < chan->min_nf)
+			chan->min_nf = survey->nf;
+		if (survey->nf < iface->lowest_nf)
+			iface->lowest_nf = survey->nf;
+	}
+}
+
+
+static void hostapd_single_channel_get_survey(struct hostapd_iface *iface,
+					      struct survey_results *survey_res)
+{
+	struct hostapd_channel_data *chan;
+	struct freq_survey *survey;
+	u64 divisor, dividend;
+
+	survey = dl_list_first(&survey_res->survey_list, struct freq_survey,
+			       list);
+	if (!survey || !survey->freq)
+		return;
+
+	chan = hostapd_get_mode_channel(iface, survey->freq);
+	if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)",
+		   survey->freq,
+		   (unsigned long int) survey->channel_time,
+		   (unsigned long int) survey->channel_time_busy);
+
+	if (survey->channel_time > iface->last_channel_time &&
+	    survey->channel_time > survey->channel_time_busy) {
+		dividend = survey->channel_time_busy -
+			iface->last_channel_time_busy;
+		divisor = survey->channel_time - iface->last_channel_time;
+
+		iface->channel_utilization = dividend * 255 / divisor;
+		wpa_printf(MSG_DEBUG, "Channel Utilization: %d",
+			   iface->channel_utilization);
+	}
+	iface->last_channel_time = survey->channel_time;
+	iface->last_channel_time_busy = survey->channel_time_busy;
+}
+
+
+static void hostapd_event_get_survey(struct hostapd_data *hapd,
+				     struct survey_results *survey_results)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct freq_survey *survey, *tmp;
+	struct hostapd_channel_data *chan;
+
+	if (dl_list_empty(&survey_results->survey_list)) {
+		wpa_printf(MSG_DEBUG, "No survey data received");
+		return;
+	}
+
+	if (survey_results->freq_filter) {
+		hostapd_single_channel_get_survey(iface, survey_results);
+		return;
+	}
+
+	dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
+			      struct freq_survey, list) {
+		chan = hostapd_get_mode_channel(iface, survey->freq);
+		if (!chan)
+			continue;
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+
+		dl_list_del(&survey->list);
+		dl_list_add_tail(&chan->survey_list, &survey->list);
+
+		hostapd_update_nf(iface, chan, survey);
+
+		iface->chans_surveyed++;
+	}
+}
+
+
+#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);
+}
+
+
+static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
+					  struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS offload CAC started on %d MHz", radar->freq);
+	hostapd_dfs_start_cac(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)
+{
+	struct hostapd_data *hapd = ctx;
+#ifndef CONFIG_NO_STDOUT_DEBUG
+	int level = MSG_DEBUG;
+
+	if (event == EVENT_RX_MGMT && data->rx_mgmt.frame &&
+	    data->rx_mgmt.frame_len >= 24) {
+		const struct ieee80211_hdr *hdr;
+		u16 fc;
+
+		hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame;
+		fc = le_to_host16(hdr->frame_control);
+		if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+		    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
+			level = MSG_EXCESSIVE;
+		if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+		    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ)
+			level = MSG_EXCESSIVE;
+	}
+
+	wpa_dbg(hapd->msg_ctx, level, "Event %s (%d) received",
+		event_to_string(event), event);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+	switch (event) {
+	case EVENT_MICHAEL_MIC_FAILURE:
+		michael_mic_failure(hapd, data->michael_mic_failure.src, 1);
+		break;
+	case EVENT_SCAN_RESULTS:
+		if (hapd->iface->scan_cb)
+			hapd->iface->scan_cb(hapd->iface);
+		break;
+	case EVENT_WPS_BUTTON_PUSHED:
+		hostapd_wps_button_pushed(hapd, NULL);
+		break;
+#ifdef NEED_AP_MLME
+	case EVENT_TX_STATUS:
+		switch (data->tx_status.type) {
+		case WLAN_FC_TYPE_MGMT:
+			hostapd_mgmt_tx_cb(hapd, data->tx_status.data,
+					   data->tx_status.data_len,
+					   data->tx_status.stype,
+					   data->tx_status.ack);
+			break;
+		case WLAN_FC_TYPE_DATA:
+			hostapd_tx_status(hapd, data->tx_status.dst,
+					  data->tx_status.data,
+					  data->tx_status.data_len,
+					  data->tx_status.ack);
+			break;
+		}
+		break;
+	case EVENT_EAPOL_TX_STATUS:
+		hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst,
+					data->eapol_tx_status.data,
+					data->eapol_tx_status.data_len,
+					data->eapol_tx_status.ack);
+		break;
+	case EVENT_DRIVER_CLIENT_POLL_OK:
+		hostapd_client_poll_ok(hapd, data->client_poll.addr);
+		break;
+	case EVENT_RX_FROM_UNKNOWN:
+		hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.bssid,
+					    data->rx_from_unknown.addr,
+					    data->rx_from_unknown.wds);
+		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)
+			break;
+		hostapd_probe_req_rx(hapd, 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);
+		break;
+	case EVENT_NEW_STA:
+		hostapd_event_new_sta(hapd, data->new_sta.addr);
+		break;
+	case EVENT_EAPOL_RX:
+		hostapd_event_eapol_rx(hapd, data->eapol_rx.src,
+				       data->eapol_rx.data,
+				       data->eapol_rx.data_len);
+		break;
+	case EVENT_ASSOC:
+		if (!data)
+			return;
+		hostapd_notif_assoc(hapd, data->assoc_info.addr,
+				    data->assoc_info.req_ies,
+				    data->assoc_info.req_ies_len,
+				    data->assoc_info.reassoc);
+		break;
+	case EVENT_DISASSOC:
+		if (data)
+			hostapd_notif_disassoc(hapd, data->disassoc_info.addr);
+		break;
+	case EVENT_DEAUTH:
+		if (data)
+			hostapd_notif_disassoc(hapd, data->deauth_info.addr);
+		break;
+	case EVENT_STATION_LOW_ACK:
+		if (!data)
+			break;
+		hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
+		break;
+	case EVENT_AUTH:
+		hostapd_notif_auth(hapd, &data->auth);
+		break;
+	case EVENT_CH_SWITCH:
+		if (!data)
+			break;
+		hostapd_event_ch_switch(hapd, data->ch_switch.freq,
+					data->ch_switch.ht_enabled,
+					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)
+			break;
+		hostapd_event_connect_failed_reason(
+			hapd, data->connect_failed_reason.addr,
+			data->connect_failed_reason.code);
+		break;
+	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;
+	case EVENT_DFS_CAC_STARTED:
+		if (!data)
+			break;
+		hostapd_event_dfs_cac_started(hapd, &data->dfs_event);
+		break;
+#endif /* NEED_AP_MLME */
+	case EVENT_INTERFACE_ENABLED:
+		wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED);
+		if (hapd->disabled && hapd->started) {
+			hapd->disabled = 0;
+			/*
+			 * Try to re-enable interface if the driver stopped it
+			 * when the interface got disabled.
+			 */
+			wpa_auth_reconfig_group_keys(hapd->wpa_auth);
+			hapd->reenable_beacon = 1;
+			ieee802_11_set_beacon(hapd);
+		}
+		break;
+	case EVENT_INTERFACE_DISABLED:
+		hostapd_free_stas(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_DISABLED);
+		hapd->disabled = 1;
+		break;
+#ifdef CONFIG_ACS
+	case EVENT_ACS_CHANNEL_SELECTED:
+		hostapd_acs_channel_selected(hapd,
+					     &data->acs_selected_channels);
+		break;
+#endif /* CONFIG_ACS */
+	default:
+		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
+		break;
+	}
+}
+
+#endif /* HOSTAPD */
diff --git a/hostap/src/ap/eap_user_db.c b/hostap/src/ap/eap_user_db.c
new file mode 100644
index 0000000..082d0f5
--- /dev/null
+++ b/hostap/src/ap/eap_user_db.c
@@ -0,0 +1,282 @@
+/*
+ * hostapd / EAP user database
+ * Copyright (c) 2012, 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"
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
+
+#include "common.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_server/eap_methods.h"
+#include "eap_server/eap.h"
+#include "ap_config.h"
+#include "hostapd.h"
+
+#ifdef CONFIG_SQLITE
+
+static void set_user_methods(struct hostapd_eap_user *user, const char *methods)
+{
+	char *buf, *start;
+	int num_methods;
+
+	buf = os_strdup(methods);
+	if (buf == NULL)
+		return;
+
+	os_memset(&user->methods, 0, sizeof(user->methods));
+	num_methods = 0;
+	start = buf;
+	while (*start) {
+		char *pos3 = os_strchr(start, ',');
+		if (pos3)
+			*pos3++ = '\0';
+		user->methods[num_methods].method =
+			eap_server_get_type(start,
+					    &user->methods[num_methods].vendor);
+		if (user->methods[num_methods].vendor == EAP_VENDOR_IETF &&
+		    user->methods[num_methods].method == EAP_TYPE_NONE) {
+			if (os_strcmp(start, "TTLS-PAP") == 0) {
+				user->ttls_auth |= EAP_TTLS_AUTH_PAP;
+				goto skip_eap;
+			}
+			if (os_strcmp(start, "TTLS-CHAP") == 0) {
+				user->ttls_auth |= EAP_TTLS_AUTH_CHAP;
+				goto skip_eap;
+			}
+			if (os_strcmp(start, "TTLS-MSCHAP") == 0) {
+				user->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
+				goto skip_eap;
+			}
+			if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) {
+				user->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
+				goto skip_eap;
+			}
+			wpa_printf(MSG_INFO, "DB: Unsupported EAP type '%s'",
+				   start);
+			os_free(buf);
+			return;
+		}
+
+		num_methods++;
+		if (num_methods >= EAP_MAX_METHODS)
+			break;
+	skip_eap:
+		if (pos3 == NULL)
+			break;
+		start = pos3;
+	}
+
+	os_free(buf);
+}
+
+
+static int get_user_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+	struct hostapd_eap_user *user = ctx;
+	int i;
+
+	for (i = 0; i < argc; i++) {
+		if (os_strcmp(col[i], "password") == 0 && argv[i]) {
+			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;
+		}
+	}
+
+	return 0;
+}
+
+
+static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+	struct hostapd_eap_user *user = ctx;
+	int i, id = -1, methods = -1;
+	size_t len;
+
+	for (i = 0; i < argc; i++) {
+		if (os_strcmp(col[i], "identity") == 0 && argv[i])
+			id = i;
+		else if (os_strcmp(col[i], "methods") == 0 && argv[i])
+			methods = i;
+	}
+
+	if (id < 0 || methods < 0)
+		return 0;
+
+	len = os_strlen(argv[id]);
+	if (len <= user->identity_len &&
+	    os_memcmp(argv[id], user->identity, len) == 0 &&
+	    (user->password == NULL || len > user->password_len)) {
+		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;
+		set_user_methods(user, argv[methods]);
+	}
+
+	return 0;
+}
+
+
+static const struct hostapd_eap_user *
+eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
+		    size_t identity_len, int phase2)
+{
+	sqlite3 *db;
+	struct hostapd_eap_user *user = NULL;
+	char id_str[256], cmd[300];
+	size_t i;
+
+	if (identity_len >= sizeof(id_str)) {
+		wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d",
+			   __func__, (int) identity_len,
+			   (int) (sizeof(id_str)));
+		return NULL;
+	}
+	os_memcpy(id_str, identity, identity_len);
+	id_str[identity_len] = '\0';
+	for (i = 0; i < identity_len; i++) {
+		if (id_str[i] >= 'a' && id_str[i] <= 'z')
+			continue;
+		if (id_str[i] >= 'A' && id_str[i] <= 'Z')
+			continue;
+		if (id_str[i] >= '0' && id_str[i] <= '9')
+			continue;
+		if (id_str[i] == '-' || id_str[i] == '_' || id_str[i] == '.' ||
+		    id_str[i] == ',' || id_str[i] == '@' || id_str[i] == '\\' ||
+		    id_str[i] == '!' || id_str[i] == '#' || id_str[i] == '%' ||
+		    id_str[i] == '=' || id_str[i] == ' ')
+			continue;
+		wpa_printf(MSG_INFO, "DB: Unsupported character in identity");
+		return NULL;
+	}
+
+	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);
+	if (hapd->tmp_eap_user.identity == NULL)
+		return NULL;
+	os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len);
+
+	if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) {
+		wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s",
+			   hapd->conf->eap_user_sqlite, sqlite3_errmsg(db));
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	os_snprintf(cmd, sizeof(cmd),
+		    "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) {
+		wpa_printf(MSG_DEBUG,
+			   "DB: Failed to complete SQL operation: %s  db: %s",
+			   sqlite3_errmsg(db), hapd->conf->eap_user_sqlite);
+	} else if (hapd->tmp_eap_user.next)
+		user = &hapd->tmp_eap_user;
+
+	if (user == NULL && !phase2) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "SELECT identity,methods FROM wildcards;");
+		wpa_printf(MSG_DEBUG, "DB: %s", cmd);
+		if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user,
+				 NULL) != SQLITE_OK) {
+			wpa_printf(MSG_DEBUG,
+				   "DB: Failed to complete SQL operation: %s  db: %s",
+				   sqlite3_errmsg(db),
+				   hapd->conf->eap_user_sqlite);
+		} else if (hapd->tmp_eap_user.next) {
+			user = &hapd->tmp_eap_user;
+			os_free(user->identity);
+			user->identity = user->password;
+			user->identity_len = user->password_len;
+			user->password = NULL;
+			user->password_len = 0;
+		}
+	}
+
+	sqlite3_close(db);
+
+	return user;
+}
+
+#endif /* CONFIG_SQLITE */
+
+
+const struct hostapd_eap_user *
+hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
+		     size_t identity_len, int phase2)
+{
+	const struct hostapd_bss_config *conf = hapd->conf;
+	struct hostapd_eap_user *user = conf->eap_user;
+
+#ifdef CONFIG_WPS
+	if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN &&
+	    os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) {
+		static struct hostapd_eap_user wsc_enrollee;
+		os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee));
+		wsc_enrollee.methods[0].method = eap_server_get_type(
+			"WSC", &wsc_enrollee.methods[0].vendor);
+		return &wsc_enrollee;
+	}
+
+	if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN &&
+	    os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) {
+		static struct hostapd_eap_user wsc_registrar;
+		os_memset(&wsc_registrar, 0, sizeof(wsc_registrar));
+		wsc_registrar.methods[0].method = eap_server_get_type(
+			"WSC", &wsc_registrar.methods[0].vendor);
+		wsc_registrar.password = (u8 *) conf->ap_pin;
+		wsc_registrar.password_len = conf->ap_pin ?
+			os_strlen(conf->ap_pin) : 0;
+		return &wsc_registrar;
+	}
+#endif /* CONFIG_WPS */
+
+	while (user) {
+		if (!phase2 && user->identity == NULL) {
+			/* Wildcard match */
+			break;
+		}
+
+		if (user->phase2 == !!phase2 && user->wildcard_prefix &&
+		    identity_len >= user->identity_len &&
+		    os_memcmp(user->identity, identity, user->identity_len) ==
+		    0) {
+			/* Wildcard prefix match */
+			break;
+		}
+
+		if (user->phase2 == !!phase2 &&
+		    user->identity_len == identity_len &&
+		    os_memcmp(user->identity, identity, identity_len) == 0)
+			break;
+		user = user->next;
+	}
+
+#ifdef CONFIG_SQLITE
+	if (user == NULL && conf->eap_user_sqlite) {
+		return eap_user_sqlite_get(hapd, identity, identity_len,
+					   phase2);
+	}
+#endif /* CONFIG_SQLITE */
+
+	return user;
+}
diff --git a/hostap/src/ap/gas_serv.c b/hostap/src/ap/gas_serv.c
new file mode 100644
index 0000000..9d19f98
--- /dev/null
+++ b/hostap/src/ap/gas_serv.c
@@ -0,0 +1,1282 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2011-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 "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#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)
+{
+	struct sta_info *sta;
+	struct gas_dialog_info *dia = NULL;
+	int i, j;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		/*
+		 * We need a STA entry to be able to maintain state for
+		 * the GAS query.
+		 */
+		wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
+			   "GAS query");
+		sta = ap_sta_add(hapd, addr);
+		if (!sta) {
+			wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
+				   " for GAS query", MAC2STR(addr));
+			return NULL;
+		}
+		sta->flags |= WLAN_STA_GAS;
+		/*
+		 * The default inactivity is 300 seconds. We don't need
+		 * 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) {
+		sta->gas_dialog = os_calloc(GAS_DIALOG_MAX,
+					    sizeof(struct gas_dialog_info));
+		if (sta->gas_dialog == NULL)
+			return NULL;
+	}
+
+	for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
+		if (i == GAS_DIALOG_MAX)
+			i = 0;
+		if (sta->gas_dialog[i].valid)
+			continue;
+		dia = &sta->gas_dialog[i];
+		dia->valid = 1;
+		dia->dialog_token = dialog_token;
+		sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
+		return dia;
+	}
+
+	wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
+		MACSTR " dialog_token %u. Consider increasing "
+		"GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
+
+	return NULL;
+}
+
+
+struct gas_dialog_info *
+gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
+		     u8 dialog_token)
+{
+	struct sta_info *sta;
+	int i;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
+			   MAC2STR(addr));
+		return NULL;
+	}
+	for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
+		if (sta->gas_dialog[i].dialog_token != dialog_token ||
+		    !sta->gas_dialog[i].valid)
+			continue;
+		return &sta->gas_dialog[i];
+	}
+	wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
+		   MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
+	return NULL;
+}
+
+
+void gas_serv_dialog_clear(struct gas_dialog_info *dia)
+{
+	wpabuf_free(dia->sd_resp);
+	os_memset(dia, 0, sizeof(*dia));
+}
+
+
+static void gas_serv_free_dialogs(struct hostapd_data *hapd,
+				  const u8 *sta_addr)
+{
+	struct sta_info *sta;
+	int i;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (sta == NULL || sta->gas_dialog == NULL)
+		return;
+
+	for (i = 0; i < GAS_DIALOG_MAX; i++) {
+		if (sta->gas_dialog[i].valid)
+			return;
+	}
+
+	os_free(sta->gas_dialog);
+	sta->gas_dialog = NULL;
+}
+
+
+#ifdef CONFIG_HS20
+static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
+				   struct wpabuf *buf)
+{
+	u8 *len;
+
+	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_CAPABILITY_LIST);
+	wpabuf_put_u8(buf, 0); /* Reserved */
+	wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
+	if (hapd->conf->hs20_oper_friendly_name)
+		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+	if (hapd->conf->hs20_wan_metrics)
+		wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
+	if (hapd->conf->hs20_connection_capability)
+		wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
+	if (hapd->conf->nai_realm_data)
+		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 */
+
+
+static void anqp_add_capab_list(struct hostapd_data *hapd,
+				struct wpabuf *buf)
+{
+	u8 *len;
+
+	len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
+	wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
+	if (hapd->conf->venue_name)
+		wpabuf_put_le16(buf, ANQP_VENUE_NAME);
+	if (hapd->conf->network_auth_type)
+		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
+	if (hapd->conf->roaming_consortium)
+		wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
+	if (hapd->conf->ipaddr_type_configured)
+		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
+	if (hapd->conf->nai_realm_data)
+		wpabuf_put_le16(buf, ANQP_NAI_REALM);
+	if (hapd->conf->anqp_3gpp_cell_net)
+		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
+	if (hapd->conf->domain_name)
+		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+#ifdef CONFIG_HS20
+	anqp_add_hs_capab_list(hapd, buf);
+#endif /* CONFIG_HS20 */
+	gas_anqp_set_element_len(buf, len);
+}
+
+
+static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+	if (hapd->conf->venue_name) {
+		u8 *len;
+		unsigned int i;
+		len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
+		wpabuf_put_u8(buf, hapd->conf->venue_group);
+		wpabuf_put_u8(buf, hapd->conf->venue_type);
+		for (i = 0; i < hapd->conf->venue_name_count; i++) {
+			struct hostapd_lang_string *vn;
+			vn = &hapd->conf->venue_name[i];
+			wpabuf_put_u8(buf, 3 + vn->name_len);
+			wpabuf_put_data(buf, vn->lang, 3);
+			wpabuf_put_data(buf, vn->name, vn->name_len);
+		}
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_network_auth_type(struct hostapd_data *hapd,
+				       struct wpabuf *buf)
+{
+	if (hapd->conf->network_auth_type) {
+		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
+		wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
+		wpabuf_put_data(buf, hapd->conf->network_auth_type,
+				hapd->conf->network_auth_type_len);
+	}
+}
+
+
+static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
+					struct wpabuf *buf)
+{
+	unsigned int i;
+	u8 *len;
+
+	len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
+	for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
+		struct hostapd_roaming_consortium *rc;
+		rc = &hapd->conf->roaming_consortium[i];
+		wpabuf_put_u8(buf, rc->len);
+		wpabuf_put_data(buf, rc->oi, rc->len);
+	}
+	gas_anqp_set_element_len(buf, len);
+}
+
+
+static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
+					       struct wpabuf *buf)
+{
+	if (hapd->conf->ipaddr_type_configured) {
+		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
+		wpabuf_put_le16(buf, 1);
+		wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
+	}
+}
+
+
+static void anqp_add_nai_realm_eap(struct wpabuf *buf,
+				   struct hostapd_nai_realm_data *realm)
+{
+	unsigned int i, j;
+
+	wpabuf_put_u8(buf, realm->eap_method_count);
+
+	for (i = 0; i < realm->eap_method_count; i++) {
+		struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
+		wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
+		wpabuf_put_u8(buf, eap->eap_method);
+		wpabuf_put_u8(buf, eap->num_auths);
+		for (j = 0; j < eap->num_auths; j++) {
+			wpabuf_put_u8(buf, eap->auth_id[j]);
+			wpabuf_put_u8(buf, 1);
+			wpabuf_put_u8(buf, eap->auth_val[j]);
+		}
+	}
+}
+
+
+static void anqp_add_nai_realm_data(struct wpabuf *buf,
+				    struct hostapd_nai_realm_data *realm,
+				    unsigned int realm_idx)
+{
+	u8 *realm_data_len;
+
+	wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
+		   (int) os_strlen(realm->realm[realm_idx]));
+	realm_data_len = wpabuf_put(buf, 2);
+	wpabuf_put_u8(buf, realm->encoding);
+	wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
+	wpabuf_put_str(buf, realm->realm[realm_idx]);
+	anqp_add_nai_realm_eap(buf, realm);
+	gas_anqp_set_element_len(buf, realm_data_len);
+}
+
+
+static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
+					   struct wpabuf *buf,
+					   const u8 *home_realm,
+					   size_t home_realm_len)
+{
+	unsigned int i, j, k;
+	u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
+	struct hostapd_nai_realm_data *realm;
+	const u8 *pos, *realm_name, *end;
+	struct {
+		unsigned int realm_data_idx;
+		unsigned int realm_idx;
+	} matches[10];
+
+	pos = home_realm;
+	end = pos + home_realm_len;
+	if (pos + 1 > end) {
+		wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
+			    home_realm, home_realm_len);
+		return -1;
+	}
+	num_realms = *pos++;
+
+	for (i = 0; i < num_realms && num_matching < 10; i++) {
+		if (pos + 2 > end) {
+			wpa_hexdump(MSG_DEBUG,
+				    "Truncated NAI Home Realm Query",
+				    home_realm, home_realm_len);
+			return -1;
+		}
+		encoding = *pos++;
+		realm_len = *pos++;
+		if (pos + realm_len > end) {
+			wpa_hexdump(MSG_DEBUG,
+				    "Truncated NAI Home Realm Query",
+				    home_realm, home_realm_len);
+			return -1;
+		}
+		realm_name = pos;
+		for (j = 0; j < hapd->conf->nai_realm_count &&
+			     num_matching < 10; j++) {
+			const u8 *rpos, *rend;
+			realm = &hapd->conf->nai_realm_data[j];
+			if (encoding != realm->encoding)
+				continue;
+
+			rpos = realm_name;
+			while (rpos < realm_name + realm_len &&
+			       num_matching < 10) {
+				for (rend = rpos;
+				     rend < realm_name + realm_len; rend++) {
+					if (*rend == ';')
+						break;
+				}
+				for (k = 0; k < MAX_NAI_REALMS &&
+					     realm->realm[k] &&
+					     num_matching < 10; k++) {
+					if ((int) os_strlen(realm->realm[k]) !=
+					    rend - rpos ||
+					    os_strncmp((char *) rpos,
+						       realm->realm[k],
+						       rend - rpos) != 0)
+						continue;
+					matches[num_matching].realm_data_idx =
+						j;
+					matches[num_matching].realm_idx = k;
+					num_matching++;
+				}
+				rpos = rend + 1;
+			}
+		}
+		pos += realm_len;
+	}
+
+	realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
+	wpabuf_put_le16(buf, num_matching);
+
+	/*
+	 * There are two ways to format. 1. each realm in a NAI Realm Data unit
+	 * 2. all realms that share the same EAP methods in a NAI Realm Data
+	 * unit. The first format is likely to be bigger in size than the
+	 * second, but may be easier to parse and process by the receiver.
+	 */
+	for (i = 0; i < num_matching; i++) {
+		wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
+			   matches[i].realm_data_idx, matches[i].realm_idx);
+		realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
+		anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
+	}
+	gas_anqp_set_element_len(buf, realm_list_len);
+	return 0;
+}
+
+
+static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
+			       const u8 *home_realm, size_t home_realm_len,
+			       int nai_realm, int nai_home_realm)
+{
+	if (nai_realm && hapd->conf->nai_realm_data) {
+		u8 *len;
+		unsigned int i, j;
+		len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
+		wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
+		for (i = 0; i < hapd->conf->nai_realm_count; i++) {
+			u8 *realm_data_len, *realm_len;
+			struct hostapd_nai_realm_data *realm;
+
+			realm = &hapd->conf->nai_realm_data[i];
+			realm_data_len = wpabuf_put(buf, 2);
+			wpabuf_put_u8(buf, realm->encoding);
+			realm_len = wpabuf_put(buf, 1);
+			for (j = 0; realm->realm[j]; j++) {
+				if (j > 0)
+					wpabuf_put_u8(buf, ';');
+				wpabuf_put_str(buf, realm->realm[j]);
+			}
+			*realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
+			anqp_add_nai_realm_eap(buf, realm);
+			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 && home_realm) {
+		hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
+						home_realm_len);
+	}
+}
+
+
+static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
+					   struct wpabuf *buf)
+{
+	if (hapd->conf->anqp_3gpp_cell_net) {
+		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
+		wpabuf_put_le16(buf,
+				hapd->conf->anqp_3gpp_cell_net_len);
+		wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
+				hapd->conf->anqp_3gpp_cell_net_len);
+	}
+}
+
+
+static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+	if (hapd->conf->domain_name) {
+		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+		wpabuf_put_le16(buf, hapd->conf->domain_name_len);
+		wpabuf_put_data(buf, hapd->conf->domain_name,
+				hapd->conf->domain_name_len);
+	}
+}
+
+
+#ifdef CONFIG_HS20
+
+static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
+					    struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_oper_friendly_name) {
+		u8 *len;
+		unsigned int i;
+		len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
+		{
+			struct hostapd_lang_string *vn;
+			vn = &hapd->conf->hs20_oper_friendly_name[i];
+			wpabuf_put_u8(buf, 3 + vn->name_len);
+			wpabuf_put_data(buf, vn->lang, 3);
+			wpabuf_put_data(buf, vn->name, vn->name_len);
+		}
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_wan_metrics(struct hostapd_data *hapd,
+				 struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_wan_metrics) {
+		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_WAN_METRICS);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_connection_capability(struct hostapd_data *hapd,
+					   struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_connection_capability) {
+		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_CONNECTION_CAPABILITY);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
+				hapd->conf->hs20_connection_capability_len);
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_operating_class(struct hostapd_data *hapd,
+				     struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_operating_class) {
+		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_OPERATING_CLASS);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
+				hapd->conf->hs20_operating_class_len);
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+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,
+				const u8 *home_realm, size_t home_realm_len,
+				const u8 *icon_name, size_t icon_name_len)
+{
+	struct wpabuf *buf;
+	size_t len;
+
+	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;
+
+	if (request & ANQP_REQ_CAPABILITY_LIST)
+		anqp_add_capab_list(hapd, buf);
+	if (request & ANQP_REQ_VENUE_NAME)
+		anqp_add_venue_name(hapd, buf);
+	if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
+		anqp_add_network_auth_type(hapd, buf);
+	if (request & ANQP_REQ_ROAMING_CONSORTIUM)
+		anqp_add_roaming_consortium(hapd, buf);
+	if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
+		anqp_add_ip_addr_type_availability(hapd, buf);
+	if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
+		anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
+				   request & ANQP_REQ_NAI_REALM,
+				   request & ANQP_REQ_NAI_HOME_REALM);
+	if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
+		anqp_add_3gpp_cellular_network(hapd, buf);
+	if (request & ANQP_REQ_DOMAIN_NAME)
+		anqp_add_domain_name(hapd, buf);
+
+#ifdef CONFIG_HS20
+	if (request & ANQP_REQ_HS_CAPABILITY_LIST)
+		anqp_add_hs_capab_list(hapd, buf);
+	if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
+		anqp_add_operator_friendly_name(hapd, buf);
+	if (request & ANQP_REQ_WAN_METRICS)
+		anqp_add_wan_metrics(hapd, buf);
+	if (request & ANQP_REQ_CONNECTION_CAPABILITY)
+		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;
+}
+
+
+struct anqp_query_info {
+	unsigned int request;
+	const u8 *home_realm_query;
+	size_t home_realm_query_len;
+	const u8 *icon_name;
+	size_t icon_name_len;
+	int p2p_sd;
+};
+
+
+static void set_anqp_req(unsigned int bit, const char *name, int local,
+			 struct anqp_query_info *qi)
+{
+	qi->request |= bit;
+	if (local) {
+		wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
+	} else {
+		wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
+	}
+}
+
+
+static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
+				  struct anqp_query_info *qi)
+{
+	switch (info_id) {
+	case ANQP_CAPABILITY_LIST:
+		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, qi);
+		break;
+	case ANQP_NETWORK_AUTH_TYPE:
+		set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
+			     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, 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, qi);
+		break;
+	case ANQP_NAI_REALM:
+		set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
+			     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, qi);
+		break;
+	case ANQP_DOMAIN_NAME:
+		set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
+			     hapd->conf->domain_name != NULL, qi);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
+			   info_id);
+		break;
+	}
+}
+
+
+static void rx_anqp_query_list(struct hostapd_data *hapd,
+			       const u8 *pos, const u8 *end,
+			       struct anqp_query_info *qi)
+{
+	wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
+		   (unsigned int) (end - pos) / 2);
+
+	while (pos + 2 <= end) {
+		rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
+		pos += 2;
+	}
+}
+
+
+#ifdef CONFIG_HS20
+
+static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
+				  struct anqp_query_info *qi)
+{
+	switch (subtype) {
+	case HS20_STYPE_CAPABILITY_LIST:
+		set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
+			     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, qi);
+		break;
+	case HS20_STYPE_WAN_METRICS:
+		set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
+			     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,
+			     qi);
+		break;
+	case HS20_STYPE_OPERATING_CLASS:
+		set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
+			     hapd->conf->hs20_operating_class != NULL, qi);
+		break;
+	case HS20_STYPE_OSU_PROVIDERS_LIST:
+		set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
+			     hapd->conf->hs20_osu_providers_count, qi);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
+			   subtype);
+		break;
+	}
+}
+
+
+static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
+				      const u8 *pos, const u8 *end,
+				      struct anqp_query_info *qi)
+{
+	qi->request |= ANQP_REQ_NAI_HOME_REALM;
+	qi->home_realm_query = pos;
+	qi->home_realm_query_len = end - pos;
+	if (hapd->conf->nai_realm_data != NULL) {
+		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
+			   "(local)");
+	} else {
+		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
+			   "available");
+	}
+}
+
+
+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)
+{
+	u32 oui;
+	u8 subtype;
+
+	if (pos + 4 > end) {
+		wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
+			   "Query element");
+		return;
+	}
+
+	oui = WPA_GET_BE24(pos);
+	pos += 3;
+	if (oui != OUI_WFA) {
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
+			   oui);
+		return;
+	}
+
+#ifdef CONFIG_P2P
+	if (*pos == P2P_OUI_TYPE) {
+		/*
+		 * This is for P2P SD and will be taken care of by the P2P
+		 * implementation. This query needs to be ignored in the generic
+		 * GAS server to avoid duplicated response.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
+			   *pos);
+		qi->p2p_sd = 1;
+		return;
+	}
+#endif /* CONFIG_P2P */
+
+	if (*pos != HS20_ANQP_OUI_TYPE) {
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
+			   *pos);
+		return;
+	}
+	pos++;
+
+	if (pos + 1 >= end)
+		return;
+
+	subtype = *pos++;
+	pos++; /* Reserved */
+	switch (subtype) {
+	case HS20_STYPE_QUERY_LIST:
+		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
+		while (pos < end) {
+			rx_anqp_hs_query_list(hapd, *pos, qi);
+			pos++;
+		}
+		break;
+	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);
+		break;
+	}
+}
+
+#endif /* CONFIG_HS20 */
+
+
+static void gas_serv_req_local_processing(struct hostapd_data *hapd,
+					  const u8 *sa, u8 dialog_token,
+					  struct anqp_query_info *qi, int prot)
+{
+	struct wpabuf *buf, *tx_buf;
+
+	buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
+					      qi->home_realm_query,
+					      qi->home_realm_query_len,
+					      qi->icon_name, qi->icon_name_len);
+	wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
+			buf);
+	if (!buf)
+		return;
+#ifdef CONFIG_P2P
+	if (wpabuf_len(buf) == 0 && qi->p2p_sd) {
+		wpa_printf(MSG_DEBUG,
+			   "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)");
+		wpabuf_free(buf);
+		return;
+	}
+#endif /* CONFIG_P2P */
+
+	if (wpabuf_len(buf) > hapd->gas_frag_limit ||
+	    hapd->conf->gas_comeback_delay) {
+		struct gas_dialog_info *di;
+		u16 comeback_delay = 1;
+
+		if (hapd->conf->gas_comeback_delay) {
+			/* Testing - allow overriding of the delay value */
+			comeback_delay = hapd->conf->gas_comeback_delay;
+		}
+
+		wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
+			   "initial response - use GAS comeback");
+		di = gas_dialog_create(hapd, sa, dialog_token);
+		if (!di) {
+			wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
+				   "for " MACSTR " (dialog token %u)",
+				   MAC2STR(sa), dialog_token);
+			wpabuf_free(buf);
+			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);
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
+		tx_buf = gas_anqp_build_initial_resp_buf(
+			dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
+		wpabuf_free(buf);
+	}
+	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);
+}
+
+
+static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
+					const u8 *sa,
+					const u8 *data, size_t len, int prot)
+{
+	const u8 *pos = data;
+	const u8 *end = data + len;
+	const u8 *next;
+	u8 dialog_token;
+	u16 slen;
+	struct anqp_query_info qi;
+	const u8 *adv_proto;
+
+	if (len < 1 + 2)
+		return;
+
+	os_memset(&qi, 0, sizeof(qi));
+
+	dialog_token = *pos++;
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+		"GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
+		MAC2STR(sa), dialog_token);
+
+	if (*pos != WLAN_EID_ADV_PROTO) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Unexpected IE in GAS Initial Request: %u", *pos);
+		return;
+	}
+	adv_proto = pos++;
+
+	slen = *pos++;
+	next = pos + slen;
+	if (next > end || slen < 2) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Invalid IE in GAS Initial Request");
+		return;
+	}
+	pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+		struct wpabuf *buf;
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Unsupported GAS advertisement protocol id %u",
+			*pos);
+		if (sa[0] & 0x01)
+			return; /* Invalid source address - drop silently */
+		buf = gas_build_initial_resp(
+			dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
+			0, 2 + slen + 2);
+		if (buf == NULL)
+			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);
+		return;
+	}
+
+	pos = next;
+	/* Query Request */
+	if (pos + 2 > end)
+		return;
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	if (pos + slen > end)
+		return;
+	end = pos + slen;
+
+	/* ANQP Query Request */
+	while (pos < end) {
+		u16 info_id, elen;
+
+		if (pos + 4 > end)
+			return;
+
+		info_id = WPA_GET_LE16(pos);
+		pos += 2;
+		elen = WPA_GET_LE16(pos);
+		pos += 2;
+
+		if (pos + elen > end) {
+			wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
+			return;
+		}
+
+		switch (info_id) {
+		case ANQP_QUERY_LIST:
+			rx_anqp_query_list(hapd, pos, pos + elen, &qi);
+			break;
+#ifdef CONFIG_HS20
+		case ANQP_VENDOR_SPECIFIC:
+			rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
+			break;
+#endif /* CONFIG_HS20 */
+		default:
+			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
+				   "Request element %u", info_id);
+			break;
+		}
+
+		pos += elen;
+	}
+
+	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, int prot)
+{
+	struct gas_dialog_info *dialog;
+	struct wpabuf *buf, *tx_buf;
+	u8 dialog_token;
+	size_t frag_len;
+	int more = 0;
+
+	wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
+	if (len < 1)
+		return;
+	dialog_token = *data;
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
+		dialog_token);
+
+	dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
+	if (!dialog) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
+			"response fragment for " MACSTR " dialog token %u",
+			MAC2STR(sa), dialog_token);
+
+		if (sa[0] & 0x01)
+			return; /* Invalid source address - drop silently */
+		tx_buf = gas_anqp_build_comeback_resp_buf(
+			dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
+			0, NULL);
+		if (tx_buf == NULL)
+			return;
+		goto send_resp;
+	}
+
+	frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
+	if (frag_len > hapd->gas_frag_limit) {
+		frag_len = hapd->gas_frag_limit;
+		more = 1;
+	}
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
+		(unsigned int) frag_len);
+	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: Failed to allocate "
+			"buffer");
+		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) {
+		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);
+	dialog->sd_frag_id++;
+	dialog->sd_resp_pos += frag_len;
+
+	if (more) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
+			"to be sent",
+			(int) (wpabuf_len(dialog->sd_resp) -
+			       dialog->sd_resp_pos));
+	} else {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
+			"SD response sent");
+		gas_serv_dialog_clear(dialog);
+		gas_serv_free_dialogs(hapd, sa);
+	}
+
+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);
+}
+
+
+static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
+				      int freq)
+{
+	struct hostapd_data *hapd = ctx;
+	const struct ieee80211_mgmt *mgmt;
+	const u8 *sa, *data;
+	int prot;
+
+	mgmt = (const struct ieee80211_mgmt *) buf;
+	if (len < IEEE80211_HDRLEN + 2)
+		return;
+	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 -= 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, prot);
+		break;
+	case WLAN_PA_GAS_COMEBACK_REQ:
+		gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
+		break;
+	}
+}
+
+
+int gas_serv_init(struct hostapd_data *hapd)
+{
+	hapd->public_action_cb2 = gas_serv_rx_public_action;
+	hapd->public_action_cb2_ctx = hapd;
+	hapd->gas_frag_limit = 1400;
+	if (hapd->conf->gas_frag_limit > 0)
+		hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
+	return 0;
+}
+
+
+void gas_serv_deinit(struct hostapd_data *hapd)
+{
+}
diff --git a/hostap/src/ap/gas_serv.h b/hostap/src/ap/gas_serv.h
new file mode 100644
index 0000000..4ec3201
--- /dev/null
+++ b/hostap/src/ap/gas_serv.h
@@ -0,0 +1,64 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_SERV_H
+#define GAS_SERV_H
+
+#define ANQP_REQ_CAPABILITY_LIST \
+	(1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST))
+#define ANQP_REQ_VENUE_NAME \
+	(1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_NETWORK_AUTH_TYPE \
+	(1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST))
+#define ANQP_REQ_ROAMING_CONSORTIUM \
+	(1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST))
+#define ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY \
+	(1 << (ANQP_IP_ADDR_TYPE_AVAILABILITY - ANQP_QUERY_LIST))
+#define ANQP_REQ_NAI_REALM \
+	(1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST))
+#define ANQP_REQ_3GPP_CELLULAR_NETWORK \
+	(1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST))
+#define ANQP_REQ_DOMAIN_NAME \
+	(1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_HS_CAPABILITY_LIST \
+	(0x10000 << HS20_STYPE_CAPABILITY_LIST)
+#define ANQP_REQ_OPERATOR_FRIENDLY_NAME \
+	(0x10000 << HS20_STYPE_OPERATOR_FRIENDLY_NAME)
+#define ANQP_REQ_WAN_METRICS \
+	(0x10000 << HS20_STYPE_WAN_METRICS)
+#define ANQP_REQ_CONNECTION_CAPABILITY \
+	(0x10000 << HS20_STYPE_CONNECTION_CAPABILITY)
+#define ANQP_REQ_NAI_HOME_REALM \
+	(0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY)
+#define ANQP_REQ_OPERATING_CLASS \
+	(0x10000 << HS20_STYPE_OPERATING_CLASS)
+#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;
+	struct wpabuf *sd_resp; /* Fragmented response */
+	u8 dialog_token;
+	size_t sd_resp_pos; /* Offset in sd_resp */
+	u8 sd_frag_id;
+	int prot; /* whether Protected Dual of Public Action frame is used */
+};
+
+struct hostapd_data;
+
+struct gas_dialog_info *
+gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
+		     u8 dialog_token);
+void gas_serv_dialog_clear(struct gas_dialog_info *dialog);
+
+int gas_serv_init(struct hostapd_data *hapd);
+void gas_serv_deinit(struct hostapd_data *hapd);
+
+#endif /* GAS_SERV_H */
diff --git a/hostap/src/ap/hostapd.c b/hostap/src/ap/hostapd.c
new file mode 100644
index 0000000..c09c17a
--- /dev/null
+++ b/hostap/src/ap/hostapd.c
@@ -0,0 +1,2962 @@
+/*
+ * hostapd / Initialization and configuration
+ * 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.
+ */
+
+#include "utils/includes.h"
+
+#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 "eap_server/tncs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "fst/fst.h"
+#include "hostapd.h"
+#include "authsrv.h"
+#include "sta_info.h"
+#include "accounting.h"
+#include "ap_list.h"
+#include "beacon.h"
+#include "iapp.h"
+#include "ieee802_1x.h"
+#include "ieee802_11_auth.h"
+#include "vlan_init.h"
+#include "wpa_auth.h"
+#include "wps_hostapd.h"
+#include "hw_features.h"
+#include "wpa_auth_glue.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "p2p_hostapd.h"
+#include "gas_serv.h"
+#include "dfs.h"
+#include "ieee802_11.h"
+#include "bss_load.h"
+#include "x_snoop.h"
+#include "dhcp_snoop.h"
+#include "ndisc_snoop.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);
+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,
+			       int (*cb)(struct hostapd_iface *iface,
+					 void *ctx), void *ctx)
+{
+	size_t i;
+	int ret;
+
+	for (i = 0; i < interfaces->count; i++) {
+		ret = cb(interfaces->iface[i], ctx);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+
+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.
+		 */
+		hostapd_config_clear_wpa_psk(&hapd->conf->ssid.wpa_psk);
+	}
+	if (hostapd_setup_wpa_psk(hapd->conf)) {
+		wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK "
+			   "after reloading configuration");
+	}
+
+	if (hapd->conf->ieee802_1x || hapd->conf->wpa)
+		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1);
+	else
+		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+
+	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);
+	} else if (hapd->conf->wpa) {
+		const u8 *wpa_ie;
+		size_t wpa_ie_len;
+		hostapd_reconfig_wpa(hapd);
+		wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len);
+		if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len))
+			wpa_printf(MSG_ERROR, "Failed to configure WPA IE for "
+				   "the kernel driver.");
+	} else if (hapd->wpa_auth) {
+		wpa_deinit(hapd->wpa_auth);
+		hapd->wpa_auth = NULL;
+		hostapd_set_privacy(hapd, 0);
+		hostapd_setup_encryption(hapd->conf->iface, hapd);
+		hostapd_set_generic_elem(hapd, (u8 *) "", 0);
+	}
+
+	ieee802_11_set_beacon(hapd);
+	hostapd_update_wps(hapd);
+
+	if (hapd->conf->ssid.ssid_set &&
+	    hostapd_set_ssid(hapd, hapd->conf->ssid.ssid,
+			     hapd->conf->ssid.ssid_len)) {
+		wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
+		/* try to continue */
+	}
+	wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
+}
+
+
+static void hostapd_clear_old(struct hostapd_iface *iface)
+{
+	size_t j;
+
+	/*
+	 * Deauthenticate all stations since the new configuration may not
+	 * allow them to use the BSS anymore.
+	 */
+	for (j = 0; j < iface->num_bss; j++) {
+		hostapd_flush_old_stations(iface->bss[j],
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+		hostapd_broadcast_wep_clear(iface->bss[j]);
+
+#ifndef CONFIG_NO_RADIUS
+		/* TODO: update dynamic data based on changed configuration
+		 * items (e.g., open/close sockets, etc.) */
+		radius_client_flush(iface->bss[j]->radius, 0);
+#endif /* CONFIG_NO_RADIUS */
+	}
+}
+
+
+int hostapd_reload_config(struct hostapd_iface *iface)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	struct hostapd_config *newconf, *oldconf;
+	size_t j;
+
+	if (iface->config_fname == NULL) {
+		/* Only in-memory config in use - assume it has been updated */
+		hostapd_clear_old(iface);
+		for (j = 0; j < iface->num_bss; j++)
+			hostapd_reload_bss(iface->bss[j]);
+		return 0;
+	}
+
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->config_read_cb == NULL)
+		return -1;
+	newconf = iface->interfaces->config_read_cb(iface->config_fname);
+	if (newconf == NULL)
+		return -1;
+
+	hostapd_clear_old(iface);
+
+	oldconf = hapd->iconf;
+	iface->conf = newconf;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		hapd = iface->bss[j];
+		hapd->iconf = newconf;
+		hapd->iconf->channel = oldconf->channel;
+		hapd->iconf->acs = oldconf->acs;
+		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);
+	}
+
+	hostapd_config_free(oldconf);
+
+
+	return 0;
+}
+
+
+static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
+					      char *ifname)
+{
+	int i;
+
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i,
+					0, NULL, 0, NULL, 0)) {
+			wpa_printf(MSG_DEBUG, "Failed to clear default "
+				   "encryption keys (ifname=%s keyidx=%d)",
+				   ifname, i);
+		}
+	}
+#ifdef CONFIG_IEEE80211W
+	if (hapd->conf->ieee80211w) {
+		for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) {
+			if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE,
+						NULL, i, 0, NULL,
+						0, NULL, 0)) {
+				wpa_printf(MSG_DEBUG, "Failed to clear "
+					   "default mgmt encryption keys "
+					   "(ifname=%s keyidx=%d)", ifname, i);
+			}
+		}
+	}
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd)
+{
+	hostapd_broadcast_key_clear_iface(hapd, hapd->conf->iface);
+	return 0;
+}
+
+
+static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
+{
+	int errors = 0, idx;
+	struct hostapd_ssid *ssid = &hapd->conf->ssid;
+
+	idx = ssid->wep.idx;
+	if (ssid->wep.default_len &&
+	    hostapd_drv_set_key(hapd->conf->iface,
+				hapd, WPA_ALG_WEP, broadcast_ether_addr, idx,
+				1, NULL, 0, ssid->wep.key[idx],
+				ssid->wep.len[idx])) {
+		wpa_printf(MSG_WARNING, "Could not set WEP encryption.");
+		errors++;
+	}
+
+	return errors;
+}
+
+
+static void hostapd_free_hapd_data(struct hostapd_data *hapd)
+{
+	os_free(hapd->probereq_cb);
+	hapd->probereq_cb = NULL;
+	hapd->num_probereq_cb = 0;
+
+#ifdef CONFIG_P2P
+	wpabuf_free(hapd->p2p_beacon_ie);
+	hapd->p2p_beacon_ie = NULL;
+	wpabuf_free(hapd->p2p_probe_resp_ie);
+	hapd->p2p_probe_resp_ie = NULL;
+#endif /* CONFIG_P2P */
+
+	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);
+	hostapd_deinit_wpa(hapd);
+	vlan_deinit(hapd);
+	hostapd_acl_deinit(hapd);
+#ifndef CONFIG_NO_RADIUS
+	radius_client_deinit(hapd->radius);
+	hapd->radius = NULL;
+	radius_das_deinit(hapd->radius_das);
+	hapd->radius_das = NULL;
+#endif /* CONFIG_NO_RADIUS */
+
+	hostapd_deinit_wps(hapd);
+
+	authsrv_deinit(hapd);
+
+	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;
+		}
+	}
+
+	wpabuf_free(hapd->time_adv);
+
+#ifdef CONFIG_INTERWORKING
+	gas_serv_deinit(hapd);
+#endif /* CONFIG_INTERWORKING */
+
+	bss_load_update_deinit(hapd);
+	ndisc_snoop_deinit(hapd);
+	dhcp_snoop_deinit(hapd);
+	x_snoop_deinit(hapd);
+
+#ifdef CONFIG_SQLITE
+	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 */
+
+#ifdef CONFIG_MESH
+	wpabuf_free(hapd->mesh_pending_auth);
+	hapd->mesh_pending_auth = NULL;
+#endif /* CONFIG_MESH */
+}
+
+
+/**
+ * hostapd_cleanup - Per-BSS cleanup (deinitialization)
+ * @hapd: Pointer to BSS data
+ *
+ * This function is used to free all per-BSS data structures and resources.
+ * 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);
+	hostapd_free_hapd_data(hapd);
+}
+
+
+static void sta_track_deinit(struct hostapd_iface *iface)
+{
+	struct hostapd_sta_info *info;
+
+	if (!iface->num_sta_seen)
+		return;
+
+	while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
+				     list))) {
+		dl_list_del(&info->list);
+		iface->num_sta_seen--;
+		os_free(info);
+	}
+}
+
+
+static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
+{
+	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+	hostapd_stop_setup_timers(iface);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
+	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+	iface->hw_features = NULL;
+	os_free(iface->current_rates);
+	iface->current_rates = NULL;
+	os_free(iface->basic_rates);
+	iface->basic_rates = NULL;
+	ap_list_deinit(iface);
+	sta_track_deinit(iface);
+}
+
+
+/**
+ * hostapd_cleanup_iface - Complete per-interface cleanup
+ * @iface: Pointer to interface data
+ *
+ * This function is called after per-BSS data structures are deinitialized
+ * with hostapd_cleanup().
+ */
+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 && !hapd->iface->driver_ap_teardown) {
+		hostapd_set_privacy(hapd, 0);
+		hostapd_broadcast_wep_clear(hapd);
+	}
+}
+
+
+static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
+{
+	int i;
+
+	hostapd_broadcast_wep_set(hapd);
+
+	if (hapd->conf->ssid.wep.default_len) {
+		hostapd_set_privacy(hapd, 1);
+		return 0;
+	}
+
+	/*
+	 * When IEEE 802.1X is not enabled, the driver may need to know how to
+	 * set authentication algorithms for static WEP.
+	 */
+	hostapd_drv_set_authmode(hapd, hapd->conf->auth_algs);
+
+	for (i = 0; i < 4; i++) {
+		if (hapd->conf->ssid.wep.key[i] &&
+		    hostapd_drv_set_key(iface, hapd, WPA_ALG_WEP, NULL, i,
+					i == hapd->conf->ssid.wep.idx, NULL, 0,
+					hapd->conf->ssid.wep.key[i],
+					hapd->conf->ssid.wep.len[i])) {
+			wpa_printf(MSG_WARNING, "Could not set WEP "
+				   "encryption.");
+			return -1;
+		}
+		if (hapd->conf->ssid.wep.key[i] &&
+		    i == hapd->conf->ssid.wep.idx)
+			hostapd_set_privacy(hapd, 1);
+	}
+
+	return 0;
+}
+
+
+static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
+{
+	int ret = 0;
+	u8 addr[ETH_ALEN];
+
+	if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL)
+		return 0;
+
+	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);
+	hostapd_drv_sta_deauth(hapd, addr, reason);
+	hostapd_free_stas(hapd);
+
+	return ret;
+}
+
+
+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
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to validate that the configured BSSIDs are valid.
+ */
+static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface)
+{
+	u8 mask[ETH_ALEN] = { 0 };
+	struct hostapd_data *hapd = iface->bss[0];
+	unsigned int i = iface->conf->num_bss, bits = 0, j;
+	int auto_addr = 0;
+
+	if (hostapd_drv_none(hapd))
+		return 0;
+
+	/* Generate BSSID mask that is large enough to cover the BSSIDs. */
+
+	/* Determine the bits necessary to cover the number of BSSIDs. */
+	for (i--; i; i >>= 1)
+		bits++;
+
+	/* 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 (j)
+				auto_addr++;
+			continue;
+		}
+
+		for (i = 0; i < ETH_ALEN; i++) {
+			mask[i] |=
+				iface->conf->bss[j]->bssid[i] ^
+				hapd->own_addr[i];
+		}
+	}
+
+	if (!auto_addr)
+		goto skip_mask_ext;
+
+	for (i = 0; i < ETH_ALEN && mask[i] == 0; i++)
+		;
+	j = 0;
+	if (i < ETH_ALEN) {
+		j = (5 - i) * 8;
+
+		while (mask[i] != 0) {
+			mask[i] >>= 1;
+			j++;
+		}
+	}
+
+	if (bits < j)
+		bits = j;
+
+	if (bits > 40) {
+		wpa_printf(MSG_ERROR, "Too many bits in the BSSID mask (%u)",
+			   bits);
+		return -1;
+	}
+
+	os_memset(mask, 0xff, ETH_ALEN);
+	j = bits / 8;
+	for (i = 5; i > 5 - j; i--)
+		mask[i] = 0;
+	j = bits % 8;
+	while (j--)
+		mask[i] <<= 1;
+
+skip_mask_ext:
+	wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)",
+		   (unsigned long) iface->conf->num_bss, MAC2STR(mask), bits);
+
+	if (!auto_addr)
+		return 0;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		if ((hapd->own_addr[i] & mask[i]) != hapd->own_addr[i]) {
+			wpa_printf(MSG_ERROR, "Invalid BSSID mask " MACSTR
+				   " for start address " MACSTR ".",
+				   MAC2STR(mask), MAC2STR(hapd->own_addr));
+			wpa_printf(MSG_ERROR, "Start address must be the "
+				   "first address in the block (i.e., addr "
+				   "AND mask == addr).");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int mac_in_conf(struct hostapd_config *conf, const void *a)
+{
+	size_t i;
+
+	for (i = 0; i < conf->num_bss; i++) {
+		if (hostapd_mac_comp(conf->bss[i]->bssid, a) == 0) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+
+static int hostapd_das_nas_mismatch(struct hostapd_data *hapd,
+				    struct radius_das_attrs *attr)
+{
+	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;
+}
+
+
+static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
+					      struct radius_das_attrs *attr,
+					      int *multi)
+{
+	struct sta_info *selected, *sta;
+	char buf[128];
+	int num_attr = 0;
+	int count;
+
+	*multi = 0;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next)
+		sta->radius_das_match = 1;
+
+	if (attr->sta_addr) {
+		num_attr++;
+		sta = ap_get_sta(hapd, attr->sta_addr);
+		if (!sta) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No Calling-Station-Id match");
+			return NULL;
+		}
+
+		selected = sta;
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (sta != selected)
+				sta->radius_das_match = 0;
+		}
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: Calling-Station-Id match");
+	}
+
+	if (attr->acct_session_id) {
+		num_attr++;
+		if (attr->acct_session_id_len != 17) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: Acct-Session-Id cannot match");
+			return NULL;
+		}
+		count = 0;
+
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (!sta->radius_das_match)
+				continue;
+			os_snprintf(buf, sizeof(buf), "%08X-%08X",
+				    sta->acct_session_id_hi,
+				    sta->acct_session_id_lo);
+			if (os_memcmp(attr->acct_session_id, buf, 17) != 0)
+				sta->radius_das_match = 0;
+			else
+				count++;
+		}
+
+		if (count == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No matches remaining after Acct-Session-Id check");
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Session-Id match");
+	}
+
+	if (attr->acct_multi_session_id) {
+		num_attr++;
+		if (attr->acct_multi_session_id_len != 17) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: Acct-Multi-Session-Id cannot match");
+			return NULL;
+		}
+		count = 0;
+
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (!sta->radius_das_match)
+				continue;
+			if (!sta->eapol_sm ||
+			    !sta->eapol_sm->acct_multi_session_id_hi) {
+				sta->radius_das_match = 0;
+				continue;
+			}
+			os_snprintf(buf, sizeof(buf), "%08X+%08X",
+				    sta->eapol_sm->acct_multi_session_id_hi,
+				    sta->eapol_sm->acct_multi_session_id_lo);
+			if (os_memcmp(attr->acct_multi_session_id, buf, 17) !=
+			    0)
+				sta->radius_das_match = 0;
+			else
+				count++;
+		}
+
+		if (count == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No matches remaining after Acct-Multi-Session-Id check");
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS DAS: Acct-Multi-Session-Id match");
+	}
+
+	if (attr->cui) {
+		num_attr++;
+		count = 0;
+
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			struct wpabuf *cui;
+
+			if (!sta->radius_das_match)
+				continue;
+			cui = ieee802_1x_get_radius_cui(sta->eapol_sm);
+			if (!cui || wpabuf_len(cui) != attr->cui_len ||
+			    os_memcmp(wpabuf_head(cui), attr->cui,
+				      attr->cui_len) != 0)
+				sta->radius_das_match = 0;
+			else
+				count++;
+		}
+
+		if (count == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No matches remaining after Chargeable-User-Identity check");
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS DAS: Chargeable-User-Identity match");
+	}
+
+	if (attr->user_name) {
+		num_attr++;
+		count = 0;
+
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			u8 *identity;
+			size_t identity_len;
+
+			if (!sta->radius_das_match)
+				continue;
+			identity = ieee802_1x_get_identity(sta->eapol_sm,
+							   &identity_len);
+			if (!identity ||
+			    identity_len != attr->user_name_len ||
+			    os_memcmp(identity, attr->user_name, identity_len)
+			    != 0)
+				sta->radius_das_match = 0;
+			else
+				count++;
+		}
+
+		if (count == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No matches remaining after User-Name check");
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS DAS: User-Name match");
+	}
+
+	if (num_attr == 0) {
+		/*
+		 * In theory, we could match all current associations, but it
+		 * seems safer to just reject requests that do not include any
+		 * session identification attributes.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS DAS: No session identification attributes included");
+		return NULL;
+	}
+
+	selected = NULL;
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (sta->radius_das_match) {
+			if (selected) {
+				*multi = 1;
+				return NULL;
+			}
+			selected = sta;
+		}
+	}
+
+	return selected;
+}
+
+
+static int hostapd_das_disconnect_pmksa(struct hostapd_data *hapd,
+					struct radius_das_attrs *attr)
+{
+	if (!hapd->wpa_auth)
+		return -1;
+	return wpa_auth_radius_das_disconnect_pmksa(hapd->wpa_auth, attr);
+}
+
+
+static enum radius_das_res
+hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	int multi;
+
+	if (hostapd_das_nas_mismatch(hapd, attr))
+		return RADIUS_DAS_NAS_MISMATCH;
+
+	sta = hostapd_das_find_sta(hapd, attr, &multi);
+	if (sta == NULL) {
+		if (multi) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: Multiple sessions match - not supported");
+			return RADIUS_DAS_MULTI_SESSION_MATCH;
+		}
+		if (hostapd_das_disconnect_pmksa(hapd, attr) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: PMKSA cache entry matched");
+			return RADIUS_DAS_SUCCESS;
+		}
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
+		return RADIUS_DAS_SESSION_NOT_FOUND;
+	}
+
+	wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
+		   " - disconnecting", MAC2STR(sta->addr));
+	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);
+
+	return RADIUS_DAS_SUCCESS;
+}
+
+#endif /* CONFIG_NO_RADIUS */
+
+
+/**
+ * hostapd_setup_bss - Per-BSS setup (initialization)
+ * @hapd: Pointer to BSS data
+ * @first: Whether this BSS is the first BSS of an interface; -1 = not first,
+ *	but interface may exist
+ *
+ * This function is used to initialize all per-BSS data structures and
+ * resources. This gets called in a loop for each BSS when an interface is
+ * initialized. Most of the modules that are initialized here will be
+ * deinitialized in hostapd_cleanup().
+ */
+static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+	u8 ssid[SSID_MAX_LEN + 1];
+	int ssid_len, set_ssid;
+	char force_ifname[IFNAMSIZ];
+	u8 if_addr[ETH_ALEN];
+	int flush_old_stations = 1;
+
+	wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
+		   __func__, hapd, conf->iface, first);
+
+#ifdef EAP_SERVER_TNC
+	if (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__, conf->iface);
+		return -1;
+	}
+	hapd->started = 1;
+
+	if (!first || first == -1) {
+		if (hostapd_mac_comp_empty(conf->bssid) == 0) {
+			/* Allocate the next available BSSID. */
+			do {
+				inc_byte_array(hapd->own_addr, ETH_ALEN);
+			} while (mac_in_conf(hapd->iconf, hapd->own_addr));
+		} else {
+			/* Allocate the configured BSSID. */
+			os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN);
+
+			if (hostapd_mac_comp(hapd->own_addr,
+					     hapd->iface->bss[0]->own_addr) ==
+			    0) {
+				wpa_printf(MSG_ERROR, "BSS '%s' may not have "
+					   "BSSID set to the MAC address of "
+					   "the radio", conf->iface);
+				return -1;
+			}
+		}
+
+		hapd->interface_added = 1;
+		if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
+				   conf->iface, hapd->own_addr, hapd,
+				   &hapd->drv_priv, force_ifname, if_addr,
+				   conf->bridge[0] ? conf->bridge : NULL,
+				   first == -1)) {
+			wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
+				   MACSTR ")", MAC2STR(hapd->own_addr));
+			hapd->interface_added = 0;
+			return -1;
+		}
+	}
+
+	if (conf->wmm_enabled < 0)
+		conf->wmm_enabled = hapd->iconf->ieee80211n;
+
+#ifdef CONFIG_MESH
+	if (hapd->iface->mconf == NULL)
+		flush_old_stations = 0;
+#endif /* CONFIG_MESH */
+
+	if (flush_old_stations)
+		hostapd_flush_old_stations(hapd,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+	hostapd_set_privacy(hapd, 0);
+
+	hostapd_broadcast_wep_clear(hapd);
+	if (hostapd_setup_encryption(conf->iface, hapd))
+		return -1;
+
+	/*
+	 * Fetch the SSID from the system and use it or,
+	 * if one was specified in the config file, verify they
+	 * match.
+	 */
+	ssid_len = hostapd_get_ssid(hapd, ssid, sizeof(ssid));
+	if (ssid_len < 0) {
+		wpa_printf(MSG_ERROR, "Could not read SSID from system");
+		return -1;
+	}
+	if (conf->ssid.ssid_set) {
+		/*
+		 * If SSID is specified in the config file and it differs
+		 * from what is being used then force installation of the
+		 * new SSID.
+		 */
+		set_ssid = (conf->ssid.ssid_len != (size_t) ssid_len ||
+			    os_memcmp(conf->ssid.ssid, ssid, ssid_len) != 0);
+	} else {
+		/*
+		 * No SSID in the config file; just use the one we got
+		 * from the system.
+		 */
+		set_ssid = 0;
+		conf->ssid.ssid_len = ssid_len;
+		os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len);
+	}
+
+	if (!hostapd_drv_none(hapd)) {
+		wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR
+			   " and ssid \"%s\"",
+			   conf->iface, MAC2STR(hapd->own_addr),
+			   wpa_ssid_txt(conf->ssid.ssid, conf->ssid.ssid_len));
+	}
+
+	if (hostapd_setup_wpa_psk(conf)) {
+		wpa_printf(MSG_ERROR, "WPA-PSK setup failed.");
+		return -1;
+	}
+
+	/* Set SSID for the kernel driver (to be used in beacon and probe
+	 * response frames) */
+	if (set_ssid && hostapd_set_ssid(hapd, conf->ssid.ssid,
+					 conf->ssid.ssid_len)) {
+		wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
+		return -1;
+	}
+
+	if (wpa_debug_level <= MSG_MSGDUMP)
+		conf->radius->msg_dumps = 1;
+#ifndef CONFIG_NO_RADIUS
+	hapd->radius = radius_client_init(hapd, conf->radius);
+	if (hapd->radius == NULL) {
+		wpa_printf(MSG_ERROR, "RADIUS client initialization failed.");
+		return -1;
+	}
+
+	if (conf->radius_das_port) {
+		struct radius_das_conf das_conf;
+		os_memset(&das_conf, 0, sizeof(das_conf));
+		das_conf.port = conf->radius_das_port;
+		das_conf.shared_secret = conf->radius_das_shared_secret;
+		das_conf.shared_secret_len =
+			conf->radius_das_shared_secret_len;
+		das_conf.client_addr = &conf->radius_das_client_addr;
+		das_conf.time_window = conf->radius_das_time_window;
+		das_conf.require_event_timestamp =
+			conf->radius_das_require_event_timestamp;
+		das_conf.ctx = hapd;
+		das_conf.disconnect = hostapd_das_disconnect;
+		hapd->radius_das = radius_das_init(&das_conf);
+		if (hapd->radius_das == NULL) {
+			wpa_printf(MSG_ERROR, "RADIUS DAS initialization "
+				   "failed.");
+			return -1;
+		}
+	}
+#endif /* CONFIG_NO_RADIUS */
+
+	if (hostapd_acl_init(hapd)) {
+		wpa_printf(MSG_ERROR, "ACL initialization failed.");
+		return -1;
+	}
+	if (hostapd_init_wps(hapd, conf))
+		return -1;
+
+	if (authsrv_init(hapd) < 0)
+		return -1;
+
+	if (ieee802_1x_init(hapd)) {
+		wpa_printf(MSG_ERROR, "IEEE 802.1X initialization failed.");
+		return -1;
+	}
+
+	if ((conf->wpa || conf->osen) && hostapd_setup_wpa(hapd))
+		return -1;
+
+	if (accounting_init(hapd)) {
+		wpa_printf(MSG_ERROR, "Accounting initialization failed.");
+		return -1;
+	}
+
+	if (conf->ieee802_11f &&
+	    (hapd->iapp = iapp_init(hapd, conf->iapp_iface)) == NULL) {
+		wpa_printf(MSG_ERROR, "IEEE 802.11F (IAPP) initialization "
+			   "failed.");
+		return -1;
+	}
+
+#ifdef CONFIG_INTERWORKING
+	if (gas_serv_init(hapd)) {
+		wpa_printf(MSG_ERROR, "GAS server initialization failed");
+		return -1;
+	}
+
+	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 (conf->bss_load_update_period && bss_load_update_init(hapd)) {
+		wpa_printf(MSG_ERROR, "BSS Load initialization failed");
+		return -1;
+	}
+
+	if (conf->proxy_arp) {
+		if (x_snoop_init(hapd)) {
+			wpa_printf(MSG_ERROR,
+				   "Generic snooping infrastructure initialization failed");
+			return -1;
+		}
+
+		if (dhcp_snoop_init(hapd)) {
+			wpa_printf(MSG_ERROR,
+				   "DHCP snooping initialization failed");
+			return -1;
+		}
+
+		if (ndisc_snoop_init(hapd)) {
+			wpa_printf(MSG_ERROR,
+				   "Neighbor Discovery snooping initialization failed");
+			return -1;
+		}
+	}
+
+	if (!hostapd_drv_none(hapd) && vlan_init(hapd)) {
+		wpa_printf(MSG_ERROR, "VLAN initialization failed.");
+		return -1;
+	}
+
+	if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+		return -1;
+
+	if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
+		return -1;
+
+	if (hapd->driver && hapd->driver->set_operstate)
+		hapd->driver->set_operstate(hapd->drv_priv, 1);
+
+	return 0;
+}
+
+
+static void hostapd_tx_queue_params(struct hostapd_iface *iface)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	int i;
+	struct hostapd_tx_queue_params *p;
+
+#ifdef CONFIG_MESH
+	if (iface->mconf == NULL)
+		return;
+#endif /* CONFIG_MESH */
+
+	for (i = 0; i < NUM_TX_QUEUES; i++) {
+		p = &iface->conf->tx_queue[i];
+
+		if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin,
+						p->cwmax, p->burst)) {
+			wpa_printf(MSG_DEBUG, "Failed to set TX queue "
+				   "parameters for queue %d.", i);
+			/* Continue anyway */
+		}
+	}
+}
+
+
+static int hostapd_set_acl_list(struct hostapd_data *hapd,
+				struct mac_acl_entry *mac_acl,
+				int n_entries, u8 accept_acl)
+{
+	struct hostapd_acl_params *acl_params;
+	int i, err;
+
+	acl_params = os_zalloc(sizeof(*acl_params) +
+			       (n_entries * sizeof(acl_params->mac_acl[0])));
+	if (!acl_params)
+		return -ENOMEM;
+
+	for (i = 0; i < n_entries; i++)
+		os_memcpy(acl_params->mac_acl[i].addr, mac_acl[i].addr,
+			  ETH_ALEN);
+
+	acl_params->acl_policy = accept_acl;
+	acl_params->num_mac_acl = n_entries;
+
+	err = hostapd_drv_set_acl(hapd, acl_params);
+
+	os_free(acl_params);
+
+	return err;
+}
+
+
+static void hostapd_set_acl(struct hostapd_data *hapd)
+{
+	struct hostapd_config *conf = hapd->iconf;
+	int err;
+	u8 accept_acl;
+
+	if (hapd->iface->drv_max_acl_mac_addrs == 0)
+		return;
+
+	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[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;
+
+	/*
+	 * 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
+	 * driver interface.
+	 */
+	for (i = 1; i < iface->num_bss; i++) {
+		iface->bss[i]->driver = hapd->driver;
+		iface->bss[i]->drv_priv = hapd->drv_priv;
+	}
+
+	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. */
+	} else {
+		int ret = hostapd_select_hw_mode(iface);
+		if (ret < 0) {
+			wpa_printf(MSG_ERROR, "Could not select hw_mode and "
+				   "channel. (%d)", ret);
+			goto fail;
+		}
+		if (ret == 1) {
+			wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)");
+			return 0;
+		}
+		ret = hostapd_check_ht_capab(iface);
+		if (ret < 0)
+			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;
+}
+
+
+#ifdef CONFIG_FST
+
+static const u8 * fst_hostapd_get_bssid_cb(void *ctx)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return hapd->own_addr;
+}
+
+
+static void fst_hostapd_get_channel_info_cb(void *ctx,
+					    enum hostapd_hw_mode *hw_mode,
+					    u8 *channel)
+{
+	struct hostapd_data *hapd = ctx;
+
+	*hw_mode = ieee80211_freq_to_chan(hapd->iface->freq, channel);
+}
+
+
+static void fst_hostapd_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
+{
+	struct hostapd_data *hapd = ctx;
+
+	if (hapd->iface->fst_ies != fst_ies) {
+		hapd->iface->fst_ies = fst_ies;
+		if (ieee802_11_set_beacon(hapd))
+			wpa_printf(MSG_WARNING, "FST: Cannot set beacon");
+	}
+}
+
+
+static int fst_hostapd_send_action_cb(void *ctx, const u8 *da,
+				      struct wpabuf *buf)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da,
+				       wpabuf_head(buf), wpabuf_len(buf));
+}
+
+
+static const struct wpabuf * fst_hostapd_get_mb_ie_cb(void *ctx, const u8 *addr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	return sta ? sta->mb_ies : NULL;
+}
+
+
+static void fst_hostapd_update_mb_ie_cb(void *ctx, const u8 *addr,
+					const u8 *buf, size_t size)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	if (sta) {
+		struct mb_ies_info info;
+
+		if (!mb_ies_info_by_ies(&info, buf, size)) {
+			wpabuf_free(sta->mb_ies);
+			sta->mb_ies = mb_ies_by_info(&info);
+		}
+	}
+}
+
+
+static const u8 * fst_hostapd_get_sta(struct fst_get_peer_ctx **get_ctx,
+				      Boolean mb_only)
+{
+	struct sta_info *s = (struct sta_info *) *get_ctx;
+
+	if (mb_only) {
+		for (; s && !s->mb_ies; s = s->next)
+			;
+	}
+
+	if (s) {
+		*get_ctx = (struct fst_get_peer_ctx *) s->next;
+
+		return s->addr;
+	}
+
+	*get_ctx = NULL;
+	return NULL;
+}
+
+
+static const u8 * fst_hostapd_get_peer_first(void *ctx,
+					     struct fst_get_peer_ctx **get_ctx,
+					     Boolean mb_only)
+{
+	struct hostapd_data *hapd = ctx;
+
+	*get_ctx = (struct fst_get_peer_ctx *) hapd->sta_list;
+
+	return fst_hostapd_get_sta(get_ctx, mb_only);
+}
+
+
+static const u8 * fst_hostapd_get_peer_next(void *ctx,
+					    struct fst_get_peer_ctx **get_ctx,
+					    Boolean mb_only)
+{
+	return fst_hostapd_get_sta(get_ctx, mb_only);
+}
+
+
+void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
+				struct fst_wpa_obj *iface_obj)
+{
+	iface_obj->ctx = hapd;
+	iface_obj->get_bssid = fst_hostapd_get_bssid_cb;
+	iface_obj->get_channel_info = fst_hostapd_get_channel_info_cb;
+	iface_obj->set_ies = fst_hostapd_set_ies_cb;
+	iface_obj->send_action = fst_hostapd_send_action_cb;
+	iface_obj->get_mb_ie = fst_hostapd_get_mb_ie_cb;
+	iface_obj->update_mb_ie = fst_hostapd_update_mb_ie_cb;
+	iface_obj->get_peer_first = fst_hostapd_get_peer_first;
+	iface_obj->get_peer_next = fst_hostapd_get_peer_next;
+}
+
+#endif /* CONFIG_FST */
+
+
+/**
+ * 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;
+	int delay_apply_cfg = 0;
+	int res_dfs_offload = 0;
+
+	if (err)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "Completing interface initialization");
+	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(iface->conf->hw_mode),
+			   iface->conf->channel, iface->freq);
+
+#ifdef NEED_AP_MLME
+		/* Handle DFS only if it is not offloaded to the driver */
+		if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) {
+			/* Check DFS */
+			res = hostapd_handle_dfs(iface);
+			if (res <= 0) {
+				if (res < 0)
+					goto fail;
+				return res;
+			}
+		} else {
+			/* If DFS is offloaded to the driver */
+			res_dfs_offload = hostapd_handle_dfs_offload(iface);
+			if (res_dfs_offload <= 0) {
+				if (res_dfs_offload < 0)
+					goto fail;
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "Proceed with AP/channel setup");
+				/*
+				 * If this is a DFS channel, move to completing
+				 * AP setup.
+				 */
+				if (res_dfs_offload == 1)
+					goto dfs_offload;
+				/* Otherwise fall through. */
+			}
+		}
+#endif /* NEED_AP_MLME */
+
+#ifdef CONFIG_MESH
+		if (iface->mconf != NULL) {
+			wpa_printf(MSG_DEBUG,
+				   "%s: Mesh configuration will be applied while joining the mesh network",
+				   iface->bss[0]->conf->iface);
+			delay_apply_cfg = 1;
+		}
+#endif /* CONFIG_MESH */
+
+		if (!delay_apply_cfg &&
+		    hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+				     hapd->iconf->channel,
+				     hapd->iconf->ieee80211n,
+				     hapd->iconf->ieee80211ac,
+				     hapd->iconf->secondary_channel,
+				     hapd->iconf->vht_oper_chwidth,
+				     hapd->iconf->vht_oper_centr_freq_seg0_idx,
+				     hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
+			wpa_printf(MSG_ERROR, "Could not set channel for "
+				   "kernel driver");
+			goto fail;
+		}
+	}
+
+	if (iface->current_mode) {
+		if (hostapd_prepare_rates(iface, iface->current_mode)) {
+			wpa_printf(MSG_ERROR, "Failed to prepare rates "
+				   "table.");
+			hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_WARNING,
+				       "Failed to prepare rates table.");
+			goto fail;
+		}
+	}
+
+	if (hapd->iconf->rts_threshold > -1 &&
+	    hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) {
+		wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
+			   "kernel driver");
+		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");
+		goto fail;
+	}
+
+	prev_addr = hapd->own_addr;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		hapd = iface->bss[j];
+		if (j)
+			os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
+		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);
+
+	ap_list_init(iface);
+	dl_list_init(&iface->sta_seen);
+
+	hostapd_set_acl(hapd);
+
+	if (hostapd_driver_commit(hapd) < 0) {
+		wpa_printf(MSG_ERROR, "%s: Failed to commit driver "
+			   "configuration", __func__);
+		goto fail;
+	}
+
+	/*
+	 * WPS UPnP module can be initialized only when the "upnp_iface" is up.
+	 * If "interface" and "upnp_iface" are the same (e.g., non-bridge
+	 * mode), the interface is up only after driver_commit, so initialize
+	 * WPS after driver_commit.
+	 */
+	for (j = 0; j < iface->num_bss; j++) {
+		if (hostapd_init_wps_complete(iface->bss[j]))
+			goto fail;
+	}
+
+	if ((iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+	    !res_dfs_offload) {
+		/*
+		 * If freq is DFS, and DFS is offloaded to the driver, then wait
+		 * for CAC to complete.
+		 */
+		wpa_printf(MSG_DEBUG, "%s: Wait for CAC to complete", __func__);
+		return res_dfs_offload;
+	}
+
+#ifdef NEED_AP_MLME
+dfs_offload:
+#endif /* NEED_AP_MLME */
+
+#ifdef CONFIG_FST
+	if (hapd->iconf->fst_cfg.group_id[0]) {
+		struct fst_wpa_obj iface_obj;
+
+		fst_hostapd_fill_iface_obj(hapd, &iface_obj);
+		iface->fst = fst_attach(hapd->conf->iface, hapd->own_addr,
+					&iface_obj, &hapd->iconf->fst_cfg);
+		if (!iface->fst) {
+			wpa_printf(MSG_ERROR, "Could not attach to FST %s",
+				   hapd->iconf->fst_cfg.group_id);
+			goto fail;
+		}
+	}
+#endif /* CONFIG_FST */
+
+	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);
+#ifdef CONFIG_FST
+	if (iface->fst) {
+		fst_detach(iface->fst);
+		iface->fst = NULL;
+	}
+#endif /* CONFIG_FST */
+	if (iface->interfaces && iface->interfaces->terminate_on_error)
+		eloop_terminate();
+	return -1;
+}
+
+
+/**
+ * hostapd_setup_interface - Setup of an interface
+ * @iface: Pointer to interface data.
+ * Returns: 0 on success, -1 on failure
+ *
+ * Initializes the driver interface, validates the configuration,
+ * 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)
+{
+	int ret;
+
+	ret = setup_interface(iface);
+	if (ret) {
+		wpa_printf(MSG_ERROR, "%s: Unable to setup interface.",
+			   iface->bss[0]->conf->iface);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * hostapd_alloc_bss_data - Allocate and initialize per-BSS data
+ * @hapd_iface: Pointer to interface data
+ * @conf: Pointer to per-interface configuration
+ * @bss: Pointer to per-BSS configuration for this BSS
+ * Returns: Pointer to allocated BSS data
+ *
+ * This function is used to allocate per-BSS data structure. This data will be
+ * freed after hostapd_cleanup() is called for it during interface
+ * deinitialization.
+ */
+struct hostapd_data *
+hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+		       struct hostapd_config *conf,
+		       struct hostapd_bss_config *bss)
+{
+	struct hostapd_data *hapd;
+
+	hapd = os_zalloc(sizeof(*hapd));
+	if (hapd == NULL)
+		return NULL;
+
+	hapd->new_assoc_sta_cb = hostapd_new_assoc_sta;
+	hapd->iconf = conf;
+	hapd->conf = bss;
+	hapd->iface = hapd_iface;
+	hapd->driver = hapd->iconf->driver;
+	hapd->ctrl_sock = -1;
+
+	return hapd;
+}
+
+
+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);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+	hostapd_cleanup(hapd);
+}
+
+
+void hostapd_interface_deinit(struct hostapd_iface *iface)
+{
+	int j;
+
+	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+	if (iface == NULL)
+		return;
+
+	hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+
+#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;
+
+#ifdef CONFIG_FST
+	if (iface->fst) {
+		fst_detach(iface->fst);
+		iface->fst = NULL;
+	}
+#endif /* CONFIG_FST */
+
+	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;
+	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);
+}
+
+
+/**
+ * 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);
+	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);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "Enable interface %s",
+		   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))
+		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;
+}
+
+
+int hostapd_reload_iface(struct hostapd_iface *hapd_iface)
+{
+	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_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;
+}
+
+
+int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
+{
+	size_t j;
+	const struct wpa_driver_ops *driver;
+	void *drv_priv;
+
+	if (hapd_iface == NULL)
+		return -1;
+
+	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;
+
+	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_bss_deinit_no_free(hapd);
+		hostapd_free_hapd_data(hapd);
+	}
+
+	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);
+
+	wpa_printf(MSG_DEBUG, "Interface %s disabled",
+		   hapd_iface->bss[0]->conf->iface);
+	hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED);
+	return 0;
+}
+
+
+static struct hostapd_iface *
+hostapd_iface_alloc(struct hapd_interfaces *interfaces)
+{
+	struct hostapd_iface **iface, *hapd_iface;
+
+	iface = os_realloc_array(interfaces->iface, interfaces->count + 1,
+				 sizeof(struct hostapd_iface *));
+	if (iface == NULL)
+		return NULL;
+	interfaces->iface = iface;
+	hapd_iface = interfaces->iface[interfaces->count] =
+		os_zalloc(sizeof(*hapd_iface));
+	if (hapd_iface == NULL) {
+		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
+			   "the interface", __func__);
+		return NULL;
+	}
+	interfaces->count++;
+	hapd_iface->interfaces = interfaces;
+
+	return hapd_iface;
+}
+
+
+static struct hostapd_config *
+hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
+		     const char *ctrl_iface, const char *driver)
+{
+	struct hostapd_bss_config *bss;
+	struct hostapd_config *conf;
+
+	/* Allocates memory for bss and conf */
+	conf = hostapd_config_defaults();
+	if (conf == NULL) {
+		 wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
+				"configuration", __func__);
+		return NULL;
+	}
+
+	if (driver) {
+		int j;
+
+		for (j = 0; wpa_drivers[j]; j++) {
+			if (os_strcmp(driver, wpa_drivers[j]->name) == 0) {
+				conf->driver = wpa_drivers[j];
+				goto skip;
+			}
+		}
+
+		wpa_printf(MSG_ERROR,
+			   "Invalid/unknown driver '%s' - registering the default driver",
+			   driver);
+	}
+
+	conf->driver = wpa_drivers[0];
+	if (conf->driver == NULL) {
+		wpa_printf(MSG_ERROR, "No driver wrappers registered!");
+		hostapd_config_free(conf);
+		return NULL;
+	}
+
+skip:
+	bss = conf->last_bss = conf->bss[0];
+
+	os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
+	bss->ctrl_interface = os_strdup(ctrl_iface);
+	if (bss->ctrl_interface == NULL) {
+		hostapd_config_free(conf);
+		return NULL;
+	}
+
+	/* Reading configuration file skipped, will be done in SET!
+	 * From reading the configuration till the end has to be done in
+	 * SET
+	 */
+	return conf;
+}
+
+
+static int hostapd_data_alloc(struct hostapd_iface *hapd_iface,
+			      struct hostapd_config *conf)
+{
+	size_t i;
+	struct hostapd_data *hapd;
+
+	hapd_iface->bss = os_calloc(conf->num_bss,
+				    sizeof(struct hostapd_data *));
+	if (hapd_iface->bss == NULL)
+		return -1;
+
+	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) {
+			while (i > 0) {
+				i--;
+				os_free(hapd_iface->bss[i]);
+				hapd_iface->bss[i] = NULL;
+			}
+			os_free(hapd_iface->bss);
+			hapd_iface->bss = NULL;
+			return -1;
+		}
+		hapd->msg_ctx = hapd;
+	}
+
+	hapd_iface->conf = conf;
+	hapd_iface->num_bss = conf->num_bss;
+
+	return 0;
+}
+
+
+int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
+{
+	struct hostapd_config *conf = NULL;
+	struct hostapd_iface *hapd_iface = NULL, *new_iface = NULL;
+	struct hostapd_data *hapd;
+	char *ptr;
+	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))
+				goto fail;
+
+			if (hostapd_setup_interface(hapd_iface)) {
+				hostapd_deinit_driver(
+					hapd_iface->bss[0]->driver,
+					hapd_iface->bss[0]->drv_priv,
+					hapd_iface);
+				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);
+				hostapd_config_free_bss(hapd->conf);
+				hapd->conf = NULL;
+				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,
+			       buf)) {
+			wpa_printf(MSG_INFO, "Cannot add interface - it "
+				   "already exists");
+			return -1;
+		}
+	}
+
+	hapd_iface = hostapd_iface_alloc(interfaces);
+	if (hapd_iface == NULL) {
+		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+			   "for interface", __func__);
+		goto fail;
+	}
+	new_iface = hapd_iface;
+
+	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 {
+		char *driver = os_strchr(ptr, ' ');
+
+		if (driver)
+			*driver++ = '\0';
+		conf = hostapd_config_alloc(interfaces, buf, ptr, driver);
+	}
+
+	if (conf == NULL || conf->bss == NULL) {
+		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+			   "for configuration", __func__);
+		goto fail;
+	}
+
+	if (hostapd_data_alloc(hapd_iface, conf) < 0) {
+		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+			   "for hostapd", __func__);
+		goto fail;
+	}
+	conf = NULL;
+
+	if (start_ctrl_iface(hapd_iface) < 0)
+		goto fail;
+
+	wpa_printf(MSG_INFO, "Add interface '%s'",
+		   hapd_iface->conf->bss[0]->iface);
+
+	return 0;
+
+fail:
+	if (conf)
+		hostapd_config_free(conf);
+	if (hapd_iface) {
+		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);
+				hostapd_cleanup(hapd);
+				os_free(hapd);
+				hapd_iface->bss[i] = NULL;
+			}
+			os_free(hapd_iface->bss);
+			hapd_iface->bss = NULL;
+		}
+		if (new_iface) {
+			interfaces->count--;
+			interfaces->iface[interfaces->count] = NULL;
+		}
+		hostapd_cleanup_iface(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);
+		hapd->conf = NULL;
+		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, 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)) {
+			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)) {
+				interfaces->iface[k] =
+					interfaces->iface[k + 1];
+				k++;
+			}
+			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;
+}
+
+
+/**
+ * hostapd_new_assoc_sta - Notify that a new station associated with the AP
+ * @hapd: Pointer to BSS data
+ * @sta: Pointer to the associated STA data
+ * @reassoc: 1 to indicate this was a re-association; 0 = first association
+ *
+ * This function will be called whenever a station associates with the AP. It
+ * can be called from ieee802_11.c for drivers that export MLME to hostapd and
+ * from drv_callbacks.c based on driver events for drivers that take care of
+ * management frames (IEEE 802.11 authentication and association) internally.
+ */
+void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   int reassoc)
+{
+	if (hapd->tkip_countermeasures) {
+		hostapd_drv_sta_deauth(hapd, sta->addr,
+				       WLAN_REASON_MICHAEL_MIC_FAILURE);
+		return;
+	}
+
+	hostapd_prune_associations(hapd, sta->addr);
+
+	/* IEEE 802.11F (IAPP) */
+	if (hapd->conf->ieee802_11f)
+		iapp_new_station(hapd->iapp, sta);
+
+#ifdef CONFIG_P2P
+	if (sta->p2p_ie == NULL && !sta->no_p2p_set) {
+		sta->no_p2p_set = 1;
+		hapd->num_sta_no_p2p++;
+		if (hapd->num_sta_no_p2p == 1)
+			hostapd_p2p_non_p2p_sta_connected(hapd);
+	}
+#endif /* CONFIG_P2P */
+
+	/* Start accounting here, if IEEE 802.1X and WPA are not used.
+	 * IEEE 802.1X/WPA code will start accounting after the station has
+	 * been authorized. */
+	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) {
+		ap_sta_set_authorized(hapd, sta, 1);
+		os_get_reltime(&sta->connected_time);
+		accounting_sta_start(hapd, sta);
+	}
+
+	/* Start IEEE 802.1X authentication process for new stations */
+	ieee802_1x_new_station(hapd, sta);
+	if (reassoc) {
+		if (sta->auth_alg != WLAN_AUTH_FT &&
+		    !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)))
+			wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
+	} else
+		wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
+
+	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;
+
+	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+		wpa_printf(MSG_INFO, "CSA is not supported");
+		return -1;
+	}
+
+	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);
+}
+
+
+struct hostapd_data * hostapd_get_iface(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 hapd;
+		}
+	}
+
+	return NULL;
+}
+
+#endif /* NEED_AP_MLME */
+
+
+void hostapd_periodic_iface(struct hostapd_iface *iface)
+{
+	size_t i;
+
+	ap_list_timer(iface);
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *hapd = iface->bss[i];
+
+		if (!hapd->started)
+			continue;
+
+#ifndef CONFIG_NO_RADIUS
+		hostapd_acl_expire(hapd);
+#endif /* CONFIG_NO_RADIUS */
+	}
+}
diff --git a/hostap/src/ap/hostapd.h b/hostap/src/ap/hostapd.h
new file mode 100644
index 0000000..dcf51f0
--- /dev/null
+++ b/hostap/src/ap/hostapd.h
@@ -0,0 +1,494 @@
+/*
+ * hostapd / Initialization and configuration
+ * 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.
+ */
+
+#ifndef HOSTAPD_H
+#define HOSTAPD_H
+
+#include "common/defs.h"
+#include "utils/list.h"
+#include "ap_config.h"
+#include "drivers/driver.h"
+
+struct wpa_ctrl_dst;
+struct radius_server_data;
+struct upnp_wps_device_sm;
+struct hostapd_data;
+struct sta_info;
+struct ieee80211_ht_capabilities;
+struct full_dynamic_vlan;
+enum wps_event;
+union wps_event_data;
+#ifdef CONFIG_MESH
+struct mesh_conf;
+#endif /* CONFIG_MESH */
+
+struct hostapd_iface;
+
+struct hapd_interfaces {
+	int (*reload_config)(struct hostapd_iface *iface);
+	struct hostapd_config * (*config_read_cb)(const char *config_fname);
+	int (*ctrl_iface_init)(struct hostapd_data *hapd);
+	void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
+	int (*for_each_interface)(struct hapd_interfaces *interfaces,
+				  int (*cb)(struct hostapd_iface *iface,
+					    void *ctx), void *ctx);
+	int (*driver_init)(struct hostapd_iface *iface);
+
+	size_t count;
+	int global_ctrl_sock;
+	struct wpa_ctrl_dst *global_ctrl_dst;
+	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;
+
+	size_t terminate_on_error;
+#ifndef CONFIG_NO_VLAN
+	struct dynamic_iface *vlan_priv;
+#endif /* CONFIG_NO_VLAN */
+};
+
+enum hostapd_chan_status {
+	HOSTAPD_CHAN_VALID = 0, /* channel is ready */
+	HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */
+	HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */
+};
+
+struct hostapd_probereq_cb {
+	int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
+		  const u8 *ie, size_t ie_len, int ssi_signal);
+	void *ctx;
+};
+
+#define HOSTAPD_RATE_BASIC 0x00000001
+
+struct hostapd_rate_data {
+	int rate; /* rate in 100 kbps */
+	int flags; /* HOSTAPD_RATE_ flags */
+};
+
+struct hostapd_frame_info {
+	u32 channel;
+	u32 datarate;
+	int ssi_signal; /* dBm */
+};
+
+enum wps_status {
+	WPS_STATUS_SUCCESS = 1,
+	WPS_STATUS_FAILURE
+};
+
+enum pbc_status {
+	WPS_PBC_STATUS_DISABLE,
+	WPS_PBC_STATUS_ACTIVE,
+	WPS_PBC_STATUS_TIMEOUT,
+	WPS_PBC_STATUS_OVERLAP
+};
+
+struct wps_stat {
+	enum wps_status status;
+	enum wps_error_indication failure_reason;
+	enum pbc_status pbc_status;
+	u8 peer_addr[ETH_ALEN];
+};
+
+
+/**
+ * struct hostapd_data - hostapd per-BSS data structure
+ */
+struct hostapd_data {
+	struct hostapd_iface *iface;
+	struct hostapd_config *iconf;
+	struct hostapd_bss_config *conf;
+	int interface_added; /* virtual interface added for this BSS */
+	unsigned int started:1;
+	unsigned int disabled:1;
+	unsigned int reenable_beacon:1;
+
+	u8 own_addr[ETH_ALEN];
+
+	int num_sta; /* number of entries in sta_list */
+	struct sta_info *sta_list; /* STA info list head */
+#define STA_HASH_SIZE 256
+#define STA_HASH(sta) (sta[5])
+	struct sta_info *sta_hash[STA_HASH_SIZE];
+
+	/*
+	 * Bitfield for indicating which AIDs are allocated. Only AID values
+	 * 1-2007 are used and as such, the bit at index 0 corresponds to AID
+	 * 1.
+	 */
+#define AID_WORDS ((2008 + 31) / 32)
+	u32 sta_aid[AID_WORDS];
+
+	const struct wpa_driver_ops *driver;
+	void *drv_priv;
+
+	void (*new_assoc_sta_cb)(struct hostapd_data *hapd,
+				 struct sta_info *sta, int reassoc);
+
+	void *msg_ctx; /* ctx for wpa_msg() calls */
+	void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */
+
+	struct radius_client_data *radius;
+	u32 acct_session_id_hi, acct_session_id_lo;
+	struct radius_das_data *radius_das;
+
+	struct iapp_data *iapp;
+
+	struct hostapd_cached_radius_acl *acl_cache;
+	struct hostapd_acl_query_data *acl_queries;
+
+	struct wpa_authenticator *wpa_auth;
+	struct eapol_authenticator *eapol_auth;
+
+	struct rsn_preauth_interface *preauth_iface;
+	struct os_reltime michael_mic_failure;
+	int michael_mic_failures;
+	int tkip_countermeasures;
+
+	int ctrl_sock;
+	struct wpa_ctrl_dst *ctrl_dst;
+
+	void *ssl_ctx;
+	void *eap_sim_db_priv;
+	struct radius_server_data *radius_srv;
+	struct dl_list erp_keys; /* struct eap_server_erp_key */
+
+	int parameter_set_count;
+
+	/* Time Advertisement */
+	u8 time_update_counter;
+	struct wpabuf *time_adv;
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	struct full_dynamic_vlan *full_dynamic_vlan;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+	struct l2_packet_data *l2;
+	struct wps_context *wps;
+
+	int beacon_set_done;
+	struct wpabuf *wps_beacon_ie;
+	struct wpabuf *wps_probe_resp_ie;
+#ifdef CONFIG_WPS
+	unsigned int ap_pin_failures;
+	unsigned int ap_pin_failures_consecutive;
+	struct upnp_wps_device_sm *wps_upnp;
+	unsigned int ap_pin_lockout_time;
+
+	struct wps_stat wps_stats;
+#endif /* CONFIG_WPS */
+
+	struct hostapd_probereq_cb *probereq_cb;
+	size_t num_probereq_cb;
+
+	void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
+				 int freq);
+	void *public_action_cb_ctx;
+	void (*public_action_cb2)(void *ctx, const u8 *buf, size_t len,
+				  int freq);
+	void *public_action_cb2_ctx;
+
+	int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len,
+				int freq);
+	void *vendor_action_cb_ctx;
+
+	void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr,
+				   const u8 *uuid_e);
+	void *wps_reg_success_cb_ctx;
+
+	void (*wps_event_cb)(void *ctx, enum wps_event event,
+			     union wps_event_data *data);
+	void *wps_event_cb_ctx;
+
+	void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr,
+				  int authorized, const u8 *p2p_dev_addr);
+	void *sta_authorized_cb_ctx;
+
+	void (*setup_complete_cb)(void *ctx);
+	void *setup_complete_cb_ctx;
+
+	void (*new_psk_cb)(void *ctx, const u8 *mac_addr,
+			   const u8 *p2p_dev_addr, const u8 *psk,
+			   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;
+
+	/* BSS Load */
+	unsigned int bss_load_update_timeout;
+
+#ifdef CONFIG_P2P
+	struct p2p_data *p2p;
+	struct p2p_group *p2p_group;
+	struct wpabuf *p2p_beacon_ie;
+	struct wpabuf *p2p_probe_resp_ie;
+
+	/* Number of non-P2P association stations */
+	int num_sta_no_p2p;
+
+	/* Periodic NoA (used only when no non-P2P clients in the group) */
+	int noa_enabled;
+	int noa_start;
+	int noa_duration;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+	size_t gas_frag_limit;
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_PROXYARP
+	struct l2_packet_data *sock_dhcp;
+	struct l2_packet_data *sock_ndisc;
+#endif /* CONFIG_PROXYARP */
+#ifdef CONFIG_MESH
+	int num_plinks;
+	int max_plinks;
+	void (*mesh_sta_free_cb)(struct sta_info *sta);
+	struct wpabuf *mesh_pending_auth;
+	struct os_reltime mesh_pending_auth_time;
+#endif /* CONFIG_MESH */
+
+#ifdef CONFIG_SQLITE
+	struct hostapd_eap_user tmp_eap_user;
+#endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_SAE
+	/** Key used for generating SAE anti-clogging tokens */
+	u8 sae_token_key[8];
+	struct os_reltime last_sae_token_key_update;
+	int dot11RSNASAERetransPeriod; /* msec */
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	unsigned int ext_mgmt_frame_handling:1;
+	unsigned int ext_eapol_frame_io:1;
+
+	struct l2_packet_data *l2_test;
+#endif /* CONFIG_TESTING_OPTIONS */
+};
+
+
+struct hostapd_sta_info {
+	struct dl_list list;
+	u8 addr[ETH_ALEN];
+	struct os_reltime last_seen;
+};
+
+/**
+ * struct hostapd_iface - hostapd per-interface data structure
+ */
+struct hostapd_iface {
+	struct hapd_interfaces *interfaces;
+	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;
+
+#ifdef CONFIG_MESH
+	struct mesh_conf *mconf;
+#endif /* CONFIG_MESH */
+
+	size_t num_bss;
+	struct hostapd_data **bss;
+
+	unsigned int wait_channel_update:1;
+	unsigned int cac_started:1;
+#ifdef CONFIG_FST
+	struct fst_iface *fst;
+	const struct wpabuf *fst_ies;
+#endif /* CONFIG_FST */
+
+	/*
+	 * 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];
+
+	u64 drv_flags;
+
+	/* SMPS modes supported by the driver (WPA_DRIVER_SMPS_MODE_*) */
+	unsigned int smps_modes;
+
+	/*
+	 * A bitmap of supported protocols for probe response offload. See
+	 * struct wpa_driver_capa in driver.h
+	 */
+	unsigned int probe_resp_offloads;
+
+	/* extended capabilities supported by the driver */
+	const u8 *extended_capa, *extended_capa_mask;
+	unsigned int extended_capa_len;
+
+	unsigned int drv_max_acl_mac_addrs;
+
+	struct hostapd_hw_modes *hw_features;
+	int num_hw_features;
+	struct hostapd_hw_modes *current_mode;
+	/* Rates that are currently used (i.e., filtered copy of
+	 * current_mode->channels */
+	int num_rates;
+	struct hostapd_rate_data *current_rates;
+	int *basic_rates;
+	int freq;
+
+	u16 hw_flags;
+
+	/* Number of associated Non-ERP stations (i.e., stations using 802.11b
+	 * in 802.11g BSS) */
+	int num_sta_non_erp;
+
+	/* Number of associated stations that do not support Short Slot Time */
+	int num_sta_no_short_slot_time;
+
+	/* Number of associated stations that do not support Short Preamble */
+	int num_sta_no_short_preamble;
+
+	int olbc; /* Overlapping Legacy BSS Condition */
+
+	/* Number of HT associated stations that do not support greenfield */
+	int num_sta_ht_no_gf;
+
+	/* Number of associated non-HT stations */
+	int num_sta_no_ht;
+
+	/* 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;
+
+	u16 ht_op_mode;
+
+	/* surveying helpers */
+
+	/* number of channels surveyed */
+	unsigned int chans_surveyed;
+
+	/* lowest observed noise floor in dBm */
+	s8 lowest_nf;
+
+	/* channel utilization calculation */
+	u64 last_channel_time;
+	u64 last_channel_time_busy;
+	u8 channel_utilization;
+
+	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);
+	int num_ht40_scan_tries;
+
+	struct dl_list sta_seen; /* struct hostapd_sta_info */
+	unsigned int num_sta_seen;
+};
+
+/* hostapd.c */
+int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+			       int (*cb)(struct hostapd_iface *iface,
+					 void *ctx), void *ctx);
+int hostapd_reload_config(struct hostapd_iface *iface);
+struct hostapd_data *
+hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+		       struct hostapd_config *conf,
+		       struct hostapd_bss_config *bss);
+int hostapd_setup_interface(struct hostapd_iface *iface);
+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);
+int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
+int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
+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);
+void hostapd_periodic_iface(struct hostapd_iface *iface);
+
+/* utils.c */
+int hostapd_register_probereq_cb(struct hostapd_data *hapd,
+				 int (*cb)(void *ctx, const u8 *sa,
+					   const u8 *da, const u8 *bssid,
+					   const u8 *ie, size_t ie_len,
+					   int ssi_signal),
+				 void *ctx);
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
+
+/* drv_callbacks.c (TODO: move to somewhere else?) */
+int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+			const u8 *ie, size_t ielen, int reassoc);
+void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
+					 const u8 *addr, int reason_code);
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
+			 const u8 *bssid, const u8 *ie, size_t ie_len,
+			 int ssi_signal);
+void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+			     int offset, int width, int cf1, int cf2);
+
+const struct hostapd_eap_user *
+hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
+		     size_t identity_len, int phase2);
+
+struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
+					const char *ifname);
+
+#ifdef CONFIG_FST
+void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
+				struct fst_wpa_obj *iface_obj);
+#endif /* CONFIG_FST */
+
+#endif /* HOSTAPD_H */
diff --git a/hostap/src/ap/hs20.c b/hostap/src/ap/hs20.c
new file mode 100644
index 0000000..d7909fa
--- /dev/null
+++ b/hostap/src/ap/hs20.c
@@ -0,0 +1,177 @@
+/*
+ * Hotspot 2.0 AP ANQP processing
+ * Copyright (c) 2009, Atheros Communications, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#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++ = 7;
+	WPA_PUT_BE24(eid, OUI_WFA);
+	eid += 3;
+	*eid++ = HS20_INDICATION_OUI_TYPE;
+	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/hostap/src/ap/hs20.h b/hostap/src/ap/hs20.h
new file mode 100644
index 0000000..152439f
--- /dev/null
+++ b/hostap/src/ap/hs20.h
@@ -0,0 +1,22 @@
+/*
+ * Hotspot 2.0 AP ANQP processing
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HS20_H
+#define HS20_H
+
+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/hostap/src/ap/hw_features.c b/hostap/src/ap/hw_features.c
new file mode 100644
index 0000000..fc8786d
--- /dev/null
+++ b/hostap/src/ap/hw_features.c
@@ -0,0 +1,980 @@
+/*
+ * hostapd / Hardware feature query and different modes
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "common/hw_features_common.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"
+
+
+void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+			      size_t num_hw_features)
+{
+	size_t i;
+
+	if (hw_features == NULL)
+		return;
+
+	for (i = 0; i < num_hw_features; i++) {
+		os_free(hw_features[i].channels);
+		os_free(hw_features[i].rates);
+	}
+
+	os_free(hw_features);
+}
+
+
+#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];
+	int i, j;
+	u16 num_modes, flags;
+	struct hostapd_hw_modes *modes;
+
+	if (hostapd_drv_none(hapd))
+		return -1;
+	modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags);
+	if (modes == NULL) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Fetching hardware channel/rate support not "
+			       "supported.");
+		return -1;
+	}
+
+	iface->hw_flags = flags;
+
+	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+	iface->hw_features = modes;
+	iface->num_hw_features = num_modes;
+
+	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
+			 * to initiate radiation (a.k.a. passive scan and no
+			 * IBSS).
+			 * Use radar channels only if the driver supports DFS.
+			 */
+			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_IR)) {
+				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%s",
+				   feature->mode,
+				   feature->channels[j].chan,
+				   feature->channels[j].freq,
+				   feature->channels[j].max_tx_power,
+				   dfs ? dfs_info(&feature->channels[j]) : "");
+		}
+	}
+
+	return 0;
+}
+
+
+int hostapd_prepare_rates(struct hostapd_iface *iface,
+			  struct hostapd_hw_modes *mode)
+{
+	int i, num_basic_rates = 0;
+	int basic_rates_a[] = { 60, 120, 240, -1 };
+	int basic_rates_b[] = { 10, 20, -1 };
+	int basic_rates_g[] = { 10, 20, 55, 110, -1 };
+	int *basic_rates;
+
+	if (iface->conf->basic_rates)
+		basic_rates = iface->conf->basic_rates;
+	else switch (mode->mode) {
+	case HOSTAPD_MODE_IEEE80211A:
+		basic_rates = basic_rates_a;
+		break;
+	case HOSTAPD_MODE_IEEE80211B:
+		basic_rates = basic_rates_b;
+		break;
+	case HOSTAPD_MODE_IEEE80211G:
+		basic_rates = basic_rates_g;
+		break;
+	case HOSTAPD_MODE_IEEE80211AD:
+		return 0; /* No basic rates for 11ad */
+	default:
+		return -1;
+	}
+
+	i = 0;
+	while (basic_rates[i] >= 0)
+		i++;
+	if (i)
+		i++; /* -1 termination */
+	os_free(iface->basic_rates);
+	iface->basic_rates = os_malloc(i * sizeof(int));
+	if (iface->basic_rates)
+		os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int));
+
+	os_free(iface->current_rates);
+	iface->num_rates = 0;
+
+	iface->current_rates =
+		os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data));
+	if (!iface->current_rates) {
+		wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
+			   "table.");
+		return -1;
+	}
+
+	for (i = 0; i < mode->num_rates; i++) {
+		struct hostapd_rate_data *rate;
+
+		if (iface->conf->supported_rates &&
+		    !hostapd_rate_found(iface->conf->supported_rates,
+					mode->rates[i]))
+			continue;
+
+		rate = &iface->current_rates[iface->num_rates];
+		rate->rate = mode->rates[i];
+		if (hostapd_rate_found(basic_rates, rate->rate)) {
+			rate->flags |= HOSTAPD_RATE_BASIC;
+			num_basic_rates++;
+		}
+		wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
+			   iface->num_rates, rate->rate, rate->flags);
+		iface->num_rates++;
+	}
+
+	if ((iface->num_rates == 0 || num_basic_rates == 0) &&
+	    (!iface->conf->ieee80211n || !iface->conf->require_ht)) {
+		wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
+			   "rate sets (%d,%d).",
+			   iface->num_rates, num_basic_rates);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211N
+static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
+{
+	int pri_chan, sec_chan;
+
+	if (!iface->conf->secondary_channel)
+		return 1; /* HT40 not used */
+
+	pri_chan = iface->conf->channel;
+	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
+
+	return allowed_ht40_channel_pair(iface->current_mode, pri_chan,
+					 sec_chan);
+}
+
+
+static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
+{
+	if (iface->conf->secondary_channel > 0) {
+		iface->conf->channel += 4;
+		iface->conf->secondary_channel = -1;
+	} else {
+		iface->conf->channel -= 4;
+		iface->conf->secondary_channel = 1;
+	}
+}
+
+
+static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
+				     struct wpa_scan_results *scan_res)
+{
+	int pri_chan, sec_chan;
+	int res;
+
+	pri_chan = iface->conf->channel;
+	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
+
+	res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan);
+
+	if (res == 2) {
+		if (iface->conf->no_pri_sec_switch) {
+			wpa_printf(MSG_DEBUG,
+				   "Cannot switch PRI/SEC channels due to local constraint");
+		} else {
+			ieee80211n_switch_pri_sec(iface);
+		}
+	}
+
+	return !!res;
+}
+
+
+static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
+				      struct wpa_scan_results *scan_res)
+{
+	int pri_chan, sec_chan;
+
+	pri_chan = iface->conf->channel;
+	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
+
+	return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan,
+			       sec_chan);
+}
+
+
+static void ieee80211n_check_scan(struct hostapd_iface *iface)
+{
+	struct wpa_scan_results *scan_res;
+	int oper40;
+	int res;
+
+	/* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
+	 * allowed per IEEE Std 802.11-2012, 10.15.3.2 */
+
+	iface->scan_cb = NULL;
+
+	scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
+	if (scan_res == NULL) {
+		hostapd_setup_interface_complete(iface, 1);
+		return;
+	}
+
+	if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
+		oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
+	else
+		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",
+			   iface->conf->channel,
+			   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);
+}
+
+
+static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
+					 struct wpa_driver_scan_params *params)
+{
+	/* Scan only the affected frequency range */
+	int pri_freq, sec_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)
+		sec_freq = pri_freq + 20;
+	else
+		sec_freq = pri_freq - 20;
+	/*
+	 * Note: Need to find the PRI channel also in cases where the affected
+	 * channel is the SEC channel of a 40 MHz BSS, so need to include the
+	 * scanning coverage here to be 40 MHz from the center frequency.
+	 */
+	affected_start = (pri_freq + sec_freq) / 2 - 40;
+	affected_end = (pri_freq + sec_freq) / 2 + 40;
+	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 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);
+	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;
+	}
+
+	iface->scan_cb = ieee80211n_check_scan;
+	return 1;
+}
+
+
+static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
+{
+	u16 hw = iface->current_mode->ht_capab;
+	u16 conf = iface->conf->ht_capab;
+
+	if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
+	    !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [LDPC]");
+		return 0;
+	}
+
+	/*
+	 * Driver ACS chosen channel may not be HT40 due to internal driver
+	 * restrictions.
+	 */
+	if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+	    !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [HT40*]");
+		return 0;
+	}
+
+	switch (conf & HT_CAP_INFO_SMPS_MASK) {
+	case HT_CAP_INFO_SMPS_STATIC:
+		if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_STATIC)) {
+			wpa_printf(MSG_ERROR,
+				   "Driver does not support configured HT capability [SMPS-STATIC]");
+			return 0;
+		}
+		break;
+	case HT_CAP_INFO_SMPS_DYNAMIC:
+		if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_DYNAMIC)) {
+			wpa_printf(MSG_ERROR,
+				   "Driver does not support configured HT capability [SMPS-DYNAMIC]");
+			return 0;
+		}
+		break;
+	case HT_CAP_INFO_SMPS_DISABLED:
+	default:
+		break;
+	}
+
+	if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
+	    !(hw & HT_CAP_INFO_GREEN_FIELD)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [GF]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) &&
+	    !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [SHORT-GI-20]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) &&
+	    !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [SHORT-GI-40]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [TX-STBC]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_RX_STBC_MASK) >
+	    (hw & HT_CAP_INFO_RX_STBC_MASK)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [RX-STBC*]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_DELAYED_BA) &&
+	    !(hw & HT_CAP_INFO_DELAYED_BA)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [DELAYED-BA]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) &&
+	    !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [MAX-AMSDU-7935]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) &&
+	    !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [DSSS_CCK-40]");
+		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 "
+			   "HT capability [LSIG-TXOP-PROT]");
+		return 0;
+	}
+
+	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 mask,
+				     unsigned int shift,
+				     const char *name)
+{
+	u32 hw_max = hw & mask;
+	u32 conf_val = conf & mask;
+
+	if (conf_val > hw_max) {
+		wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)",
+			   name, conf_val >> shift, hw_max >> shift);
+		return 0;
+	}
+	return 1;
+}
+
+
+static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
+{
+	struct hostapd_hw_modes *mode = iface->current_mode;
+	u32 hw = 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);
+
+	if (mode->mode == HOSTAPD_MODE_IEEE80211G &&
+	    iface->conf->bss[0]->vendor_vht &&
+	    mode->vht_capab == 0 && iface->hw_features) {
+		int i;
+
+		for (i = 0; i < iface->num_hw_features; i++) {
+			if (iface->hw_features[i].mode ==
+			    HOSTAPD_MODE_IEEE80211A) {
+				mode = &iface->hw_features[i];
+				hw = mode->vht_capab;
+				wpa_printf(MSG_DEBUG,
+					   "update hw vht capab based on 5 GHz band: 0x%x",
+					   hw);
+				break;
+			}
+		}
+	}
+
+#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 ## _SHIFT, \
+					       #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 */
+
+
+int hostapd_check_ht_capab(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_IEEE80211N
+	int ret;
+	if (!iface->conf->ieee80211n)
+		return 0;
+
+	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B &&
+	    iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G &&
+	    (iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) {
+		wpa_printf(MSG_DEBUG,
+			   "Disable HT capability [DSSS_CCK-40] on 5 GHz band");
+		iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ;
+	}
+
+	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;
+	if (!ieee80211n_allowed_ht40_channel_pair(iface))
+		return -1;
+#endif /* CONFIG_IEEE80211N */
+
+	return 0;
+}
+
+
+static int hostapd_is_usable_chan(struct hostapd_iface *iface,
+				  int channel, int primary)
+{
+	int i;
+	struct hostapd_channel_data *chan;
+
+	if (!iface->current_mode)
+		return 0;
+
+	for (i = 0; i < iface->current_mode->num_channels; i++) {
+		chan = &iface->current_mode->channels[i];
+		if (chan->chan != channel)
+			continue;
+
+		if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
+			return 1;
+
+		wpa_printf(MSG_DEBUG,
+			   "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s",
+			   primary ? "" : "Configured HT40 secondary ",
+			   i, chan->chan, chan->flag,
+			   chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
+			   chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
+	}
+
+	return 0;
+}
+
+
+static int hostapd_is_usable_chans(struct hostapd_iface *iface)
+{
+	if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
+		return 0;
+
+	if (!iface->conf->secondary_channel)
+		return 1;
+
+	return hostapd_is_usable_chan(iface, iface->conf->channel +
+				      iface->conf->secondary_channel * 4, 0);
+}
+
+
+static enum hostapd_chan_status
+hostapd_check_chans(struct hostapd_iface *iface)
+{
+	if (iface->conf->channel) {
+		if (hostapd_is_usable_chans(iface))
+			return HOSTAPD_CHAN_VALID;
+		else
+			return HOSTAPD_CHAN_INVALID;
+	}
+
+	/*
+	 * The user set channel=0 or channel=acs_survey
+	 * which is used to trigger ACS.
+	 */
+
+	switch (acs_init(iface)) {
+	case HOSTAPD_CHAN_ACS:
+		return HOSTAPD_CHAN_ACS;
+	case HOSTAPD_CHAN_VALID:
+	case HOSTAPD_CHAN_INVALID:
+	default:
+		return HOSTAPD_CHAN_INVALID;
+	}
+}
+
+
+static void hostapd_notify_bad_chans(struct hostapd_iface *iface)
+{
+	if (!iface->current_mode) {
+		hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_WARNING,
+			       "Hardware does not support configured mode");
+		return;
+	}
+	hostapd_logger(iface->bss[0], NULL,
+		       HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_WARNING,
+		       "Configured channel (%d) not found from the "
+		       "channel list of current mode (%d) %s",
+		       iface->conf->channel,
+		       iface->current_mode->mode,
+		       hostapd_hw_mode_txt(iface->current_mode->mode));
+	hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_WARNING,
+		       "Hardware does not support configured channel");
+}
+
+
+int hostapd_acs_completed(struct hostapd_iface *iface, int err)
+{
+	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);
+		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);
+		goto out;
+	}
+
+	ret = hostapd_check_ht_capab(iface);
+	if (ret < 0)
+		goto out;
+	if (ret == 1) {
+		wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback");
+		return 0;
+	}
+
+	ret = 0;
+out:
+	return hostapd_setup_interface_complete(iface, ret);
+}
+
+
+/**
+ * hostapd_select_hw_mode - Select the hardware mode
+ * @iface: Pointer to interface data.
+ * Returns: 0 on success, < 0 on failure
+ *
+ * Sets up the hardware mode, channel, rates, and passive scanning
+ * based on the configuration.
+ */
+int hostapd_select_hw_mode(struct hostapd_iface *iface)
+{
+	int i;
+
+	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];
+		if (mode->mode == iface->conf->hw_mode) {
+			iface->current_mode = mode;
+			break;
+		}
+	}
+
+	if (iface->current_mode == NULL) {
+		if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) ||
+		    !(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY))
+		{
+			wpa_printf(MSG_ERROR,
+				   "Hardware does not support configured mode");
+			hostapd_logger(iface->bss[0], NULL,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_WARNING,
+				       "Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)",
+				       (int) iface->conf->hw_mode);
+			return -2;
+		}
+	}
+
+	switch (hostapd_check_chans(iface)) {
+	case HOSTAPD_CHAN_VALID:
+		return 0;
+	case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */
+		return 1;
+	case HOSTAPD_CHAN_INVALID:
+	default:
+		hostapd_notify_bad_chans(iface);
+		return -3;
+	}
+}
+
+
+const char * hostapd_hw_mode_txt(int mode)
+{
+	switch (mode) {
+	case HOSTAPD_MODE_IEEE80211A:
+		return "IEEE 802.11a";
+	case HOSTAPD_MODE_IEEE80211B:
+		return "IEEE 802.11b";
+	case HOSTAPD_MODE_IEEE80211G:
+		return "IEEE 802.11g";
+	case HOSTAPD_MODE_IEEE80211AD:
+		return "IEEE 802.11ad";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+
+int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
+{
+	return hw_get_freq(hapd->iface->current_mode, chan);
+}
+
+
+int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
+{
+	return hw_get_chan(hapd->iface->current_mode, freq);
+}
diff --git a/hostap/src/ap/hw_features.h b/hostap/src/ap/hw_features.h
new file mode 100644
index 0000000..ca7f22b
--- /dev/null
+++ b/hostap/src/ap/hw_features.h
@@ -0,0 +1,76 @@
+/*
+ * hostapd / Hardware feature query and different modes
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HW_FEATURES_H
+#define HW_FEATURES_H
+
+#ifdef NEED_AP_MLME
+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);
+int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
+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,
+			 size_t num_hw_features)
+{
+}
+
+static inline int hostapd_get_hw_features(struct hostapd_iface *iface)
+{
+	return -1;
+}
+
+static inline int hostapd_acs_completed(struct hostapd_iface *iface, int err)
+{
+	return -1;
+}
+
+static inline int hostapd_select_hw_mode(struct hostapd_iface *iface)
+{
+	return -100;
+}
+
+static inline const char * hostapd_hw_mode_txt(int mode)
+{
+	return NULL;
+}
+
+static inline int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
+{
+	return -1;
+}
+
+static inline int hostapd_check_ht_capab(struct hostapd_iface *iface)
+{
+	return 0;
+}
+
+static inline int hostapd_prepare_rates(struct hostapd_iface *iface,
+					struct hostapd_hw_modes *mode)
+{
+	return 0;
+}
+
+static inline void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+}
+
+#endif /* NEED_AP_MLME */
+
+#endif /* HW_FEATURES_H */
diff --git a/hostap/src/ap/iapp.c b/hostap/src/ap/iapp.c
new file mode 100644
index 0000000..99aa04d
--- /dev/null
+++ b/hostap/src/ap/iapp.c
@@ -0,0 +1,533 @@
+/*
+ * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * Note: IEEE 802.11F-2003 was a experimental use specification. It has expired
+ * and IEEE has withdrawn it. In other words, it is likely better to look at
+ * using some other mechanism for AP-to-AP communication than extending the
+ * implementation here.
+ */
+
+/* TODO:
+ * Level 1: no administrative or security support
+ *	(e.g., static BSSID to IP address mapping in each AP)
+ * Level 2: support for dynamic mapping of BSSID to IP address
+ * Level 3: support for encryption and authentication of IAPP messages
+ * - add support for MOVE-notify and MOVE-response (this requires support for
+ *   finding out IP address for previous AP using RADIUS)
+ * - add support for Send- and ACK-Security-Block to speedup IEEE 802.1X during
+ *   reassociation to another AP
+ * - implement counters etc. for IAPP MIB
+ * - verify endianness of fields in IAPP messages; are they big-endian as
+ *   used here?
+ * - RADIUS connection for AP registration and BSSID to IP address mapping
+ * - TCP connection for IAPP MOVE, CACHE
+ * - broadcast ESP for IAPP ADD-notify
+ * - ESP for IAPP MOVE messages
+ * - security block sending/processing
+ * - IEEE 802.11 context transfer
+ */
+
+#include "utils/includes.h"
+#include <net/if.h>
+#include <sys/ioctl.h>
+#ifdef USE_KERNEL_HEADERS
+#include <linux/if_packet.h>
+#else /* USE_KERNEL_HEADERS */
+#include <netpacket/packet.h>
+#endif /* USE_KERNEL_HEADERS */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "iapp.h"
+
+
+#define IAPP_MULTICAST "224.0.1.178"
+#define IAPP_UDP_PORT 3517
+#define IAPP_TCP_PORT 3517
+
+struct iapp_hdr {
+	u8 version;
+	u8 command;
+	be16 identifier;
+	be16 length;
+	/* followed by length-6 octets of data */
+} __attribute__ ((packed));
+
+#define IAPP_VERSION 0
+
+enum IAPP_COMMAND {
+	IAPP_CMD_ADD_notify = 0,
+	IAPP_CMD_MOVE_notify = 1,
+	IAPP_CMD_MOVE_response = 2,
+	IAPP_CMD_Send_Security_Block = 3,
+	IAPP_CMD_ACK_Security_Block = 4,
+	IAPP_CMD_CACHE_notify = 5,
+	IAPP_CMD_CACHE_response = 6,
+};
+
+
+/* ADD-notify - multicast UDP on the local LAN */
+struct iapp_add_notify {
+	u8 addr_len; /* ETH_ALEN */
+	u8 reserved;
+	u8 mac_addr[ETH_ALEN];
+	be16 seq_num;
+} __attribute__ ((packed));
+
+
+/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
+struct iapp_layer2_update {
+	u8 da[ETH_ALEN]; /* broadcast */
+	u8 sa[ETH_ALEN]; /* STA addr */
+	be16 len; /* 6 */
+	u8 dsap; /* null DSAP address */
+	u8 ssap; /* null SSAP address, CR=Response */
+	u8 control;
+	u8 xid_info[3];
+} __attribute__ ((packed));
+
+
+/* MOVE-notify - unicast TCP */
+struct iapp_move_notify {
+	u8 addr_len; /* ETH_ALEN */
+	u8 reserved;
+	u8 mac_addr[ETH_ALEN];
+	u16 seq_num;
+	u16 ctx_block_len;
+	/* followed by ctx_block_len bytes */
+} __attribute__ ((packed));
+
+
+/* MOVE-response - unicast TCP */
+struct iapp_move_response {
+	u8 addr_len; /* ETH_ALEN */
+	u8 status;
+	u8 mac_addr[ETH_ALEN];
+	u16 seq_num;
+	u16 ctx_block_len;
+	/* followed by ctx_block_len bytes */
+} __attribute__ ((packed));
+
+enum {
+	IAPP_MOVE_SUCCESSFUL = 0,
+	IAPP_MOVE_DENIED = 1,
+	IAPP_MOVE_STALE_MOVE = 2,
+};
+
+
+/* CACHE-notify */
+struct iapp_cache_notify {
+	u8 addr_len; /* ETH_ALEN */
+	u8 reserved;
+	u8 mac_addr[ETH_ALEN];
+	u16 seq_num;
+	u8 current_ap[ETH_ALEN];
+	u16 ctx_block_len;
+	/* ctx_block_len bytes of context block followed by 16-bit context
+	 * timeout */
+} __attribute__ ((packed));
+
+
+/* CACHE-response - unicast TCP */
+struct iapp_cache_response {
+	u8 addr_len; /* ETH_ALEN */
+	u8 status;
+	u8 mac_addr[ETH_ALEN];
+	u16 seq_num;
+} __attribute__ ((packed));
+
+enum {
+	IAPP_CACHE_SUCCESSFUL = 0,
+	IAPP_CACHE_STALE_CACHE = 1,
+};
+
+
+/* Send-Security-Block - unicast TCP */
+struct iapp_send_security_block {
+	u8 iv[8];
+	u16 sec_block_len;
+	/* followed by sec_block_len bytes of security block */
+} __attribute__ ((packed));
+
+
+/* ACK-Security-Block - unicast TCP */
+struct iapp_ack_security_block {
+	u8 iv[8];
+	u8 new_ap_ack_authenticator[48];
+} __attribute__ ((packed));
+
+
+struct iapp_data {
+	struct hostapd_data *hapd;
+	u16 identifier; /* next IAPP identifier */
+	struct in_addr own, multicast;
+	int udp_sock;
+	int packet_sock;
+};
+
+
+static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num)
+{
+	char buf[128];
+	struct iapp_hdr *hdr;
+	struct iapp_add_notify *add;
+	struct sockaddr_in addr;
+
+	/* Send IAPP ADD-notify to remove possible association from other APs
+	 */
+
+	hdr = (struct iapp_hdr *) buf;
+	hdr->version = IAPP_VERSION;
+	hdr->command = IAPP_CMD_ADD_notify;
+	hdr->identifier = host_to_be16(iapp->identifier++);
+	hdr->length = host_to_be16(sizeof(*hdr) + sizeof(*add));
+
+	add = (struct iapp_add_notify *) (hdr + 1);
+	add->addr_len = ETH_ALEN;
+	add->reserved = 0;
+	os_memcpy(add->mac_addr, mac_addr, ETH_ALEN);
+
+	add->seq_num = host_to_be16(seq_num);
+	
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = iapp->multicast.s_addr;
+	addr.sin_port = htons(IAPP_UDP_PORT);
+	if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0,
+		   (struct sockaddr *) &addr, sizeof(addr)) < 0)
+		wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno));
+}
+
+
+static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr)
+{
+	struct iapp_layer2_update msg;
+
+	/* Send Level 2 Update Frame to update forwarding tables in layer 2
+	 * bridge devices */
+
+	/* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)
+	 * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */
+
+	os_memset(msg.da, 0xff, ETH_ALEN);
+	os_memcpy(msg.sa, addr, ETH_ALEN);
+	msg.len = host_to_be16(6);
+	msg.dsap = 0; /* NULL DSAP address */
+	msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */
+	msg.control = 0xaf; /* XID response lsb.1111F101.
+			     * F=0 (no poll command; unsolicited frame) */
+	msg.xid_info[0] = 0x81; /* XID format identifier */
+	msg.xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */
+	msg.xid_info[2] = 1 << 1; /* XID sender's receive window size (RW)
+				   * FIX: what is correct RW with 802.11? */
+
+	if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0)
+		wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno));
+}
+
+
+/**
+ * iapp_new_station - IAPP processing for a new STA
+ * @iapp: IAPP data
+ * @sta: The associated station
+ */
+void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta)
+{
+	u16 seq = 0; /* TODO */
+
+	if (iapp == NULL)
+		return;
+
+	/* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */
+	hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP,
+		       HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq);
+	iapp_send_layer2_update(iapp, sta->addr);
+	iapp_send_add(iapp, sta->addr, seq);
+
+	/* TODO: If this was reassociation:
+	 * IAPP-MOVE.request(MAC Address, Sequence Number, Old AP,
+	 *                   Context Block, Timeout)
+	 * TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to
+	 * IP address */
+}
+
+
+static void iapp_process_add_notify(struct iapp_data *iapp,
+				    struct sockaddr_in *from,
+				    struct iapp_hdr *hdr, int len)
+{
+	struct iapp_add_notify *add = (struct iapp_add_notify *) (hdr + 1);
+	struct sta_info *sta;
+
+	if (len != sizeof(*add)) {
+		wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)",
+			   len, (unsigned long) sizeof(*add));
+		return;
+	}
+
+	sta = ap_get_sta(iapp->hapd, add->mac_addr);
+
+	/* IAPP-ADD.indication(MAC Address, Sequence Number) */
+	hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
+		       HOSTAPD_LEVEL_INFO,
+		       "Received IAPP ADD-notify (seq# %d) from %s:%d%s",
+		       be_to_host16(add->seq_num),
+		       inet_ntoa(from->sin_addr), ntohs(from->sin_port),
+		       sta ? "" : " (STA not found)");
+
+	if (!sta)
+		return;
+
+	/* TODO: could use seq_num to try to determine whether last association
+	 * to this AP is newer than the one advertised in IAPP-ADD. Although,
+	 * this is not really a reliable verification. */
+
+	hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "Removing STA due to IAPP ADD-notify");
+	ap_sta_disconnect(iapp->hapd, sta, NULL, 0);
+}
+
+
+/**
+ * iapp_receive_udp - Process IAPP UDP frames
+ * @sock: File descriptor for the socket
+ * @eloop_ctx: IAPP data (struct iapp_data *)
+ * @sock_ctx: Not used
+ */
+static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct iapp_data *iapp = eloop_ctx;
+	int len, hlen;
+	unsigned char buf[128];
+	struct sockaddr_in from;
+	socklen_t fromlen;
+	struct iapp_hdr *hdr;
+
+	/* Handle incoming IAPP frames (over UDP/IP) */
+
+	fromlen = sizeof(from);
+	len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0,
+		       (struct sockaddr *) &from, &fromlen);
+	if (len < 0) {
+		wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s",
+			   strerror(errno));
+		return;
+	}
+
+	if (from.sin_addr.s_addr == iapp->own.s_addr)
+		return; /* ignore own IAPP messages */
+
+	hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "Received %d byte IAPP frame from %s%s\n",
+		       len, inet_ntoa(from.sin_addr),
+		       len < (int) sizeof(*hdr) ? " (too short)" : "");
+
+	if (len < (int) sizeof(*hdr))
+		return;
+
+	hdr = (struct iapp_hdr *) buf;
+	hlen = be_to_host16(hdr->length);
+	hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "RX: version=%d command=%d id=%d len=%d\n",
+		       hdr->version, hdr->command,
+		       be_to_host16(hdr->identifier), hlen);
+	if (hdr->version != IAPP_VERSION) {
+		wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d",
+			   hdr->version);
+		return;
+	}
+	if (hlen > len) {
+		wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)",
+			   hlen, len);
+		return;
+	}
+	if (hlen < len) {
+		wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame",
+			   len - hlen);
+		len = hlen;
+	}
+
+	switch (hdr->command) {
+	case IAPP_CMD_ADD_notify:
+		iapp_process_add_notify(iapp, &from, hdr, len - sizeof(*hdr));
+		break;
+	case IAPP_CMD_MOVE_notify:
+		/* TODO: MOVE is using TCP; so move this to TCP handler once it
+		 * is implemented.. */
+		/* IAPP-MOVE.indication(MAC Address, New BSSID,
+		 * Sequence Number, AP Address, Context Block) */
+		/* TODO: process */
+		break;
+	default:
+		wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command);
+		break;
+	}
+}
+
+
+struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
+{
+	struct ifreq ifr;
+	struct sockaddr_ll addr;
+	int ifindex;
+	struct sockaddr_in *paddr, uaddr;
+	struct iapp_data *iapp;
+	struct ip_mreqn mreq;
+
+	iapp = os_zalloc(sizeof(*iapp));
+	if (iapp == NULL)
+		return NULL;
+	iapp->hapd = hapd;
+	iapp->udp_sock = iapp->packet_sock = -1;
+
+	/* TODO:
+	 * open socket for sending and receiving IAPP frames over TCP
+	 */
+
+	iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (iapp->udp_sock < 0) {
+		wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
+		iapp_deinit(iapp);
+		return NULL;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+	if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) {
+		wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFINDEX): %s",
+			   strerror(errno));
+		iapp_deinit(iapp);
+		return NULL;
+	}
+	ifindex = ifr.ifr_ifindex;
+
+	if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) {
+		wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFADDR): %s",
+			   strerror(errno));
+		iapp_deinit(iapp);
+		return NULL;
+	}
+	paddr = (struct sockaddr_in *) &ifr.ifr_addr;
+	if (paddr->sin_family != AF_INET) {
+		wpa_printf(MSG_INFO, "IAPP: Invalid address family %i (SIOCGIFADDR)",
+			   paddr->sin_family);
+		iapp_deinit(iapp);
+		return NULL;
+	}
+	iapp->own.s_addr = paddr->sin_addr.s_addr;
+
+	if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) {
+		wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFBRDADDR): %s",
+			   strerror(errno));
+		iapp_deinit(iapp);
+		return NULL;
+	}
+	paddr = (struct sockaddr_in *) &ifr.ifr_addr;
+	if (paddr->sin_family != AF_INET) {
+		wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)",
+			   paddr->sin_family);
+		iapp_deinit(iapp);
+		return NULL;
+	}
+	inet_aton(IAPP_MULTICAST, &iapp->multicast);
+
+	os_memset(&uaddr, 0, sizeof(uaddr));
+	uaddr.sin_family = AF_INET;
+	uaddr.sin_port = htons(IAPP_UDP_PORT);
+	if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
+		 sizeof(uaddr)) < 0) {
+		wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s",
+			   strerror(errno));
+		iapp_deinit(iapp);
+		return NULL;
+	}
+
+	os_memset(&mreq, 0, sizeof(mreq));
+	mreq.imr_multiaddr = iapp->multicast;
+	mreq.imr_address.s_addr = INADDR_ANY;
+	mreq.imr_ifindex = 0;
+	if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,
+		       sizeof(mreq)) < 0) {
+		wpa_printf(MSG_INFO, "iapp_init - setsockopt[UDP,IP_ADD_MEMBERSHIP]: %s",
+			   strerror(errno));
+		iapp_deinit(iapp);
+		return NULL;
+	}
+
+	iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+	if (iapp->packet_sock < 0) {
+		wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s",
+			   strerror(errno));
+		iapp_deinit(iapp);
+		return NULL;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sll_family = AF_PACKET;
+	addr.sll_ifindex = ifindex;
+	if (bind(iapp->packet_sock, (struct sockaddr *) &addr,
+		 sizeof(addr)) < 0) {
+		wpa_printf(MSG_INFO, "iapp_init - bind[PACKET]: %s",
+			   strerror(errno));
+		iapp_deinit(iapp);
+		return NULL;
+	}
+
+	if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp,
+				     iapp, NULL)) {
+		wpa_printf(MSG_INFO, "Could not register read socket for IAPP");
+		iapp_deinit(iapp);
+		return NULL;
+	}
+
+	wpa_printf(MSG_INFO, "IEEE 802.11F (IAPP) using interface %s", iface);
+
+	/* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive
+	 * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually
+	 * be openned only after receiving Initiate-Accept. If Initiate-Reject
+	 * is received, IAPP is not started. */
+
+	return iapp;
+}
+
+
+void iapp_deinit(struct iapp_data *iapp)
+{
+	struct ip_mreqn mreq;
+
+	if (iapp == NULL)
+		return;
+
+	if (iapp->udp_sock >= 0) {
+		os_memset(&mreq, 0, sizeof(mreq));
+		mreq.imr_multiaddr = iapp->multicast;
+		mreq.imr_address.s_addr = INADDR_ANY;
+		mreq.imr_ifindex = 0;
+		if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP,
+			       &mreq, sizeof(mreq)) < 0) {
+			wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s",
+				   strerror(errno));
+		}
+
+		eloop_unregister_read_sock(iapp->udp_sock);
+		close(iapp->udp_sock);
+	}
+	if (iapp->packet_sock >= 0) {
+		eloop_unregister_read_sock(iapp->packet_sock);
+		close(iapp->packet_sock);
+	}
+	os_free(iapp);
+}
diff --git a/hostap/src/ap/iapp.h b/hostap/src/ap/iapp.h
new file mode 100644
index 0000000..c221183
--- /dev/null
+++ b/hostap/src/ap/iapp.h
@@ -0,0 +1,39 @@
+/*
+ * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IAPP_H
+#define IAPP_H
+
+struct iapp_data;
+
+#ifdef CONFIG_IAPP
+
+void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta);
+struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface);
+void iapp_deinit(struct iapp_data *iapp);
+
+#else /* CONFIG_IAPP */
+
+static inline void iapp_new_station(struct iapp_data *iapp,
+				    struct sta_info *sta)
+{
+}
+
+static inline struct iapp_data * iapp_init(struct hostapd_data *hapd,
+					   const char *iface)
+{
+	return NULL;
+}
+
+static inline void iapp_deinit(struct iapp_data *iapp)
+{
+}
+
+#endif /* CONFIG_IAPP */
+
+#endif /* IAPP_H */
diff --git a/hostap/src/ap/ieee802_11.c b/hostap/src/ap/ieee802_11.c
new file mode 100644
index 0000000..7bb18c0
--- /dev/null
+++ b/hostap/src/ap/ieee802_11.c
@@ -0,0 +1,2844 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * 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.
+ */
+
+#include "utils/includes.h"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "common/sae.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "p2p/p2p.h"
+#include "wps/wps.h"
+#include "fst/fst.h"
+#include "hostapd.h"
+#include "beacon.h"
+#include "ieee802_11_auth.h"
+#include "sta_info.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
+#include "wmm.h"
+#include "ap_list.h"
+#include "accounting.h"
+#include "ap_config.h"
+#include "ap_mlme.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "wnm_ap.h"
+#include "hw_features.h"
+#include "ieee802_11.h"
+#include "dfs.h"
+
+
+u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	int i, num, count;
+
+	if (hapd->iface->current_rates == NULL)
+		return eid;
+
+	*pos++ = WLAN_EID_SUPP_RATES;
+	num = hapd->iface->num_rates;
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+		num++;
+	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
+		num++;
+	if (num > 8) {
+		/* rest of the rates are encoded in Extended supported
+		 * rates element */
+		num = 8;
+	}
+
+	*pos++ = num;
+	for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
+	     i++) {
+		count++;
+		*pos = hapd->iface->current_rates[i].rate / 5;
+		if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
+			*pos |= 0x80;
+		pos++;
+	}
+
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) {
+		count++;
+		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
+	}
+
+	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) {
+		count++;
+		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
+	}
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	int i, num, count;
+
+	if (hapd->iface->current_rates == NULL)
+		return eid;
+
+	num = hapd->iface->num_rates;
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+		num++;
+	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
+		num++;
+	if (num <= 8)
+		return eid;
+	num -= 8;
+
+	*pos++ = WLAN_EID_EXT_SUPP_RATES;
+	*pos++ = num;
+	for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8;
+	     i++) {
+		count++;
+		if (count <= 8)
+			continue; /* already in SuppRates IE */
+		*pos = hapd->iface->current_rates[i].rate / 5;
+		if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
+			*pos |= 0x80;
+		pos++;
+	}
+
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) {
+		count++;
+		if (count > 8)
+			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
+	}
+
+	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) {
+		count++;
+		if (count > 8)
+			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
+	}
+
+	return pos;
+}
+
+
+u16 hostapd_own_capab_info(struct hostapd_data *hapd)
+{
+	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)
+		capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+	privacy = hapd->conf->ssid.wep.keys_set;
+
+	if (hapd->conf->ieee802_1x &&
+	    (hapd->conf->default_wep_key_len ||
+	     hapd->conf->individual_wep_key_len))
+		privacy = 1;
+
+	if (hapd->conf->wpa)
+		privacy = 1;
+
+#ifdef CONFIG_HS20
+	if (hapd->conf->osen)
+		privacy = 1;
+#endif /* CONFIG_HS20 */
+
+	if (privacy)
+		capab |= WLAN_CAPABILITY_PRIVACY;
+
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
+	    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;
+
+	if (hapd->conf->radio_measurements)
+		capab |= IEEE80211_CAP_RRM;
+
+	return capab;
+}
+
+
+#ifndef CONFIG_NO_RC4
+static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
+			   u16 auth_transaction, const u8 *challenge,
+			   int iswep)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "authentication (shared key, transaction %d)",
+		       auth_transaction);
+
+	if (auth_transaction == 1) {
+		if (!sta->challenge) {
+			/* Generate a pseudo-random challenge */
+			u8 key[8];
+			struct os_time now;
+			int r;
+			sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN);
+			if (sta->challenge == NULL)
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+			os_get_time(&now);
+			r = os_random();
+			os_memcpy(key, &now.sec, 4);
+			os_memcpy(key + 4, &r, 4);
+			rc4_skip(key, sizeof(key), 0,
+				 sta->challenge, WLAN_AUTH_CHALLENGE_LEN);
+		}
+		return 0;
+	}
+
+	if (auth_transaction != 3)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	/* Transaction 3 */
+	if (!iswep || !sta->challenge || !challenge ||
+	    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 "
+			       "challenge-response");
+		return WLAN_STATUS_CHALLENGE_FAIL;
+	}
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "authentication OK (shared key)");
+	sta->flags |= WLAN_STA_AUTH;
+	wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+	os_free(sta->challenge);
+	sta->challenge = NULL;
+
+	return 0;
+}
+#endif /* CONFIG_NO_RC4 */
+
+
+static void send_auth_reply(struct hostapd_data *hapd,
+			    const u8 *dst, const u8 *bssid,
+			    u16 auth_alg, u16 auth_transaction, u16 resp,
+			    const u8 *ies, size_t ies_len)
+{
+	struct ieee80211_mgmt *reply;
+	u8 *buf;
+	size_t rlen;
+
+	rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len;
+	buf = os_zalloc(rlen);
+	if (buf == NULL)
+		return;
+
+	reply = (struct ieee80211_mgmt *) buf;
+	reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					    WLAN_FC_STYPE_AUTH);
+	os_memcpy(reply->da, dst, ETH_ALEN);
+	os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(reply->bssid, bssid, ETH_ALEN);
+
+	reply->u.auth.auth_alg = host_to_le16(auth_alg);
+	reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
+	reply->u.auth.status_code = host_to_le16(resp);
+
+	if (ies && ies_len)
+		os_memcpy(reply->u.auth.variable, ies, ies_len);
+
+	wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR
+		   " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)",
+		   MAC2STR(dst), auth_alg, auth_transaction,
+		   resp, (unsigned long) ies_len);
+	if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0)
+		wpa_printf(MSG_INFO, "send_auth_reply: send");
+
+	os_free(buf);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
+				  u16 auth_transaction, u16 status,
+				  const u8 *ies, size_t ies_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction,
+			status, ies, ies_len);
+
+	if (status != WLAN_STATUS_SUCCESS)
+		return;
+
+	sta = ap_get_sta(hapd, dst);
+	if (sta == NULL)
+		return;
+
+	hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
+	sta->flags |= WLAN_STA_AUTH;
+	mlme_authenticate_indication(hapd, sta);
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+#ifdef CONFIG_SAE
+
+#define dot11RSNASAESync 5		/* attempts */
+
+
+static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
+					     struct sta_info *sta, int update)
+{
+	struct wpabuf *buf;
+
+	if (hapd->conf->ssid.wpa_passphrase == NULL) {
+		wpa_printf(MSG_DEBUG, "SAE: No password available");
+		return NULL;
+	}
+
+	if (update &&
+	    sae_prepare_commit(hapd->own_addr, sta->addr,
+			       (u8 *) hapd->conf->ssid.wpa_passphrase,
+			       os_strlen(hapd->conf->ssid.wpa_passphrase),
+			       sta->sae) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
+		return NULL;
+	}
+
+	buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
+	if (buf == NULL)
+		return NULL;
+	sae_write_commit(sta->sae, buf, sta->sae->tmp ?
+			 sta->sae->tmp->anti_clogging_token : NULL);
+
+	return buf;
+}
+
+
+static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
+					      struct sta_info *sta)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN);
+	if (buf == NULL)
+		return NULL;
+
+	sae_write_confirm(sta->sae, buf);
+
+	return buf;
+}
+
+
+static int auth_sae_send_commit(struct hostapd_data *hapd,
+				struct sta_info *sta,
+				const u8 *bssid, int update)
+{
+	struct wpabuf *data;
+
+	data = auth_build_sae_commit(hapd, sta, update);
+	if (data == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	send_auth_reply(hapd, sta->addr, bssid,
+			WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS,
+			wpabuf_head(data), wpabuf_len(data));
+
+	wpabuf_free(data);
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static int auth_sae_send_confirm(struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 const u8 *bssid)
+{
+	struct wpabuf *data;
+
+	data = auth_build_sae_confirm(hapd, sta);
+	if (data == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	send_auth_reply(hapd, sta->addr, bssid,
+			WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS,
+			wpabuf_head(data), wpabuf_len(data));
+
+	wpabuf_free(data);
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static int use_sae_anti_clogging(struct hostapd_data *hapd)
+{
+	struct sta_info *sta;
+	unsigned int open = 0;
+
+	if (hapd->conf->sae_anti_clogging_threshold == 0)
+		return 1;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (!sta->sae)
+			continue;
+		if (sta->sae->state != SAE_COMMITTED &&
+		    sta->sae->state != SAE_CONFIRMED)
+			continue;
+		open++;
+		if (open >= hapd->conf->sae_anti_clogging_threshold)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int check_sae_token(struct hostapd_data *hapd, const u8 *addr,
+			   const u8 *token, size_t token_len)
+{
+	u8 mac[SHA256_MAC_LEN];
+
+	if (token_len != SHA256_MAC_LEN)
+		return -1;
+	if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+			addr, ETH_ALEN, mac) < 0 ||
+	    os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
+					    int group, const u8 *addr)
+{
+	struct wpabuf *buf;
+	u8 *token;
+	struct os_reltime now;
+
+	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 = now;
+	}
+
+	buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN);
+	if (buf == NULL)
+		return NULL;
+
+	wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
+
+	token = wpabuf_put(buf, SHA256_MAC_LEN);
+	hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+		    addr, ETH_ALEN, token);
+
+	return buf;
+}
+
+
+static int sae_check_big_sync(struct sta_info *sta)
+{
+	if (sta->sae->sync > dot11RSNASAESync) {
+		sta->sae->state = SAE_NOTHING;
+		sta->sae->sync = 0;
+		return -1;
+	}
+	return 0;
+}
+
+
+static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = eloop_data;
+	int ret;
+
+	if (sae_check_big_sync(sta))
+		return;
+	sta->sae->sync++;
+
+	switch (sta->sae->state) {
+	case SAE_COMMITTED:
+		ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
+		eloop_register_timeout(0,
+				       hapd->dot11RSNASAERetransPeriod * 1000,
+				       auth_sae_retransmit_timer, hapd, sta);
+		break;
+	case SAE_CONFIRMED:
+		ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr);
+		eloop_register_timeout(0,
+				       hapd->dot11RSNASAERetransPeriod * 1000,
+				       auth_sae_retransmit_timer, hapd, sta);
+		break;
+	default:
+		ret = -1;
+		break;
+	}
+
+	if (ret != WLAN_STATUS_SUCCESS)
+		wpa_printf(MSG_INFO, "SAE: Failed to retransmit: ret=%d", ret);
+}
+
+
+void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+}
+
+
+static void sae_set_retransmit_timer(struct hostapd_data *hapd,
+				     struct sta_info *sta)
+{
+	if (!(hapd->conf->mesh & MESH_ENABLED))
+		return;
+
+	eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+	eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000,
+			       auth_sae_retransmit_timer, hapd, sta);
+}
+
+
+static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *bssid, u8 auth_transaction)
+{
+	int ret;
+
+	if (auth_transaction != 1 && auth_transaction != 2)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	switch (sta->sae->state) {
+	case SAE_NOTHING:
+		if (auth_transaction == 1) {
+			ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+			if (ret)
+				return ret;
+			sta->sae->state = SAE_COMMITTED;
+
+			if (sae_process_commit(sta->sae) < 0)
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+			/*
+			 * In mesh case, both Commit and Confirm can be sent
+			 * immediately. In infrastructure BSS, only a single
+			 * Authentication frame (Commit) is expected from the AP
+			 * here and the second one (Confirm) will be sent once
+			 * the STA has sent its second Authentication frame
+			 * (Confirm).
+			 */
+			if (hapd->conf->mesh & MESH_ENABLED) {
+				/*
+				 * Send both Commit and Confirm immediately
+				 * based on SAE finite state machine
+				 * Nothing -> Confirm transition.
+				 */
+				ret = auth_sae_send_confirm(hapd, sta, bssid);
+				if (ret)
+					return ret;
+				sta->sae->state = SAE_CONFIRMED;
+			} else {
+				/*
+				 * For infrastructure BSS, send only the Commit
+				 * message now to get alternating sequence of
+				 * Authentication frames between the AP and STA.
+				 * Confirm will be sent in
+				 * Commited -> Confirmed/Accepted transition
+				 * when receiving Confirm from STA.
+				 */
+			}
+			sta->sae->sync = 0;
+			sae_set_retransmit_timer(hapd, sta);
+		} else {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "SAE confirm before commit");
+		}
+		break;
+	case SAE_COMMITTED:
+		sae_clear_retransmit_timer(hapd, sta);
+		if (auth_transaction == 1) {
+			if (sae_process_commit(sta->sae) < 0)
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+			ret = auth_sae_send_confirm(hapd, sta, bssid);
+			if (ret)
+				return ret;
+			sta->sae->state = SAE_CONFIRMED;
+			sta->sae->sync = 0;
+			sae_set_retransmit_timer(hapd, sta);
+		} else if (hapd->conf->mesh & MESH_ENABLED) {
+			/*
+			 * In mesh case, follow SAE finite state machine and
+			 * send Commit now, if sync count allows.
+			 */
+			if (sae_check_big_sync(sta))
+				return WLAN_STATUS_SUCCESS;
+			sta->sae->sync++;
+
+			ret = auth_sae_send_commit(hapd, sta, bssid, 0);
+			if (ret)
+				return ret;
+
+			sae_set_retransmit_timer(hapd, sta);
+		} else {
+			/*
+			 * For instructure BSS, send the postponed Confirm from
+			 * Nothing -> Confirmed transition that was reduced to
+			 * Nothing -> Committed above.
+			 */
+			ret = auth_sae_send_confirm(hapd, sta, bssid);
+			if (ret)
+				return ret;
+
+			sta->sae->state = SAE_CONFIRMED;
+
+			/*
+			 * Since this was triggered on Confirm RX, run another
+			 * step to get to Accepted without waiting for
+			 * additional events.
+			 */
+			return sae_sm_step(hapd, sta, bssid, auth_transaction);
+		}
+		break;
+	case SAE_CONFIRMED:
+		sae_clear_retransmit_timer(hapd, sta);
+		if (auth_transaction == 1) {
+			if (sae_check_big_sync(sta))
+				return WLAN_STATUS_SUCCESS;
+			sta->sae->sync++;
+
+			ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+			if (ret)
+				return ret;
+
+			if (sae_process_commit(sta->sae) < 0)
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+			ret = auth_sae_send_confirm(hapd, sta, bssid);
+			if (ret)
+				return ret;
+
+			sae_set_retransmit_timer(hapd, sta);
+		} else {
+			sta->flags |= WLAN_STA_AUTH;
+			sta->auth_alg = WLAN_AUTH_SAE;
+			mlme_authenticate_indication(hapd, sta);
+			wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+			sta->sae->state = SAE_ACCEPTED;
+			wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
+					       sta->sae->pmk);
+		}
+		break;
+	case SAE_ACCEPTED:
+		if (auth_transaction == 1) {
+			wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
+				   ") doing reauthentication",
+				   MAC2STR(sta->addr));
+			ap_free_sta(hapd, sta);
+		} else {
+			if (sae_check_big_sync(sta))
+				return WLAN_STATUS_SUCCESS;
+			sta->sae->sync++;
+
+			ret = auth_sae_send_confirm(hapd, sta, bssid);
+			sae_clear_temp_data(sta->sae);
+			if (ret)
+				return ret;
+		}
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "SAE: invalid state %d",
+			   sta->sae->state);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
+			    const struct ieee80211_mgmt *mgmt, size_t len,
+			    u16 auth_transaction, u16 status_code)
+{
+	u16 resp = WLAN_STATUS_SUCCESS;
+	struct wpabuf *data = NULL;
+
+	if (!sta->sae) {
+		if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
+			return;
+		sta->sae = os_zalloc(sizeof(*sta->sae));
+		if (sta->sae == NULL)
+			return;
+		sta->sae->state = SAE_NOTHING;
+		sta->sae->sync = 0;
+	}
+
+	if (auth_transaction == 1) {
+		const u8 *token = NULL, *pos, *end;
+		size_t token_len = 0;
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "start SAE authentication (RX commit, status=%u)",
+			       status_code);
+
+		if ((hapd->conf->mesh & MESH_ENABLED) &&
+		    status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
+		    sta->sae->tmp) {
+			pos = mgmt->u.auth.variable;
+			end = ((const u8 *) mgmt) + len;
+			if (pos + sizeof(le16) > end) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: Too short anti-clogging token request");
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto reply;
+			}
+			resp = sae_group_allowed(sta->sae,
+						 hapd->conf->sae_groups,
+						 WPA_GET_LE16(pos));
+			if (resp != WLAN_STATUS_SUCCESS) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: Invalid group in anti-clogging token request");
+				goto reply;
+			}
+			pos += sizeof(le16);
+
+			wpabuf_free(sta->sae->tmp->anti_clogging_token);
+			sta->sae->tmp->anti_clogging_token =
+				wpabuf_alloc_copy(pos, end - pos);
+			if (sta->sae->tmp->anti_clogging_token == NULL) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: Failed to alloc for anti-clogging token");
+				return;
+			}
+
+			/*
+			 * IEEE Std 802.11-2012, 11.3.8.6.4: If the Status code
+			 * is 76, a new Commit Message shall be constructed
+			 * with the Anti-Clogging Token from the received
+			 * Authentication frame, and the commit-scalar and
+			 * COMMIT-ELEMENT previously sent.
+			 */
+			if (auth_sae_send_commit(hapd, sta, mgmt->bssid, 0)) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: Failed to send commit message");
+				return;
+			}
+			sta->sae->state = SAE_COMMITTED;
+			sta->sae->sync = 0;
+			sae_set_retransmit_timer(hapd, sta);
+			return;
+		}
+
+		if (status_code != WLAN_STATUS_SUCCESS)
+			return;
+
+		resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
+					((const u8 *) mgmt) + len -
+					mgmt->u.auth.variable, &token,
+					&token_len, hapd->conf->sae_groups);
+		if (resp == SAE_SILENTLY_DISCARD) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Drop commit message from " MACSTR " due to reflection attack",
+				   MAC2STR(sta->addr));
+			return;
+		}
+		if (token && check_sae_token(hapd, sta->addr, token, token_len)
+		    < 0) {
+			wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
+				   "incorrect token from " MACSTR,
+				   MAC2STR(sta->addr));
+			return;
+		}
+
+		if (resp != WLAN_STATUS_SUCCESS)
+			goto reply;
+
+		if (!token && use_sae_anti_clogging(hapd)) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Request anti-clogging token from "
+				   MACSTR, MAC2STR(sta->addr));
+			data = auth_build_token_req(hapd, sta->sae->group,
+						    sta->addr);
+			resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
+			if (hapd->conf->mesh & MESH_ENABLED)
+				sta->sae->state = SAE_NOTHING;
+			goto reply;
+		}
+
+		resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
+	} else if (auth_transaction == 2) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "SAE authentication (RX confirm, status=%u)",
+			       status_code);
+		if (status_code != WLAN_STATUS_SUCCESS)
+			return;
+		if (sta->sae->state >= SAE_CONFIRMED ||
+		    !(hapd->conf->mesh & MESH_ENABLED)) {
+			if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
+					      ((u8 *) mgmt) + len -
+					      mgmt->u.auth.variable) < 0) {
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto reply;
+			}
+		}
+		resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
+	} else {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "unexpected SAE authentication transaction %u (status=%u)",
+			       auth_transaction, status_code);
+		if (status_code != WLAN_STATUS_SUCCESS)
+			return;
+		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+	}
+
+reply:
+	if (resp != WLAN_STATUS_SUCCESS) {
+		send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+				auth_transaction, resp,
+				data ? wpabuf_head(data) : (u8 *) "",
+				data ? wpabuf_len(data) : 0);
+	}
+	wpabuf_free(data);
+}
+
+
+/**
+ * auth_sae_init_committed - Send COMMIT and start SAE in committed state
+ * @hapd: BSS data for the device initiating the authentication
+ * @sta: the peer to which commit authentication frame is sent
+ *
+ * This function implements Init event handling (IEEE Std 802.11-2012,
+ * 11.3.8.6.3) in which initial COMMIT message is sent. Prior to calling, the
+ * sta->sae structure should be initialized appropriately via a call to
+ * sae_prepare_commit().
+ */
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int ret;
+
+	if (!sta->sae || !sta->sae->tmp)
+		return -1;
+
+	if (sta->sae->state != SAE_NOTHING)
+		return -1;
+
+	ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
+	if (ret)
+		return -1;
+
+	sta->sae->state = SAE_COMMITTED;
+	sta->sae->sync = 0;
+	sae_set_retransmit_timer(hapd, sta);
+
+	return 0;
+}
+
+#endif /* CONFIG_SAE */
+
+
+static void handle_auth(struct hostapd_data *hapd,
+			const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	u16 auth_alg, auth_transaction, status_code;
+	u16 resp = WLAN_STATUS_SUCCESS;
+	struct sta_info *sta = NULL;
+	int res;
+	u16 fc;
+	const u8 *challenge = NULL;
+	u32 session_timeout, acct_interim_interval;
+	int vlan_id = 0;
+	struct hostapd_sta_wpa_psk_short *psk = NULL;
+	u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+	size_t resp_ies_len = 0;
+	char *identity = NULL;
+	char *radius_cui = NULL;
+	u16 seq_ctrl;
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+		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.0 &&
+	    drand48() < hapd->iconf->ignore_auth_probability) {
+		wpa_printf(MSG_INFO,
+			   "TESTING: ignoring auth frame from " MACSTR,
+			   MAC2STR(mgmt->sa));
+		return;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+	auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+	status_code = le_to_host16(mgmt->u.auth.status_code);
+	fc = le_to_host16(mgmt->frame_control);
+	seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
+	if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) +
+	    2 + WLAN_AUTH_CHALLENGE_LEN &&
+	    mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE &&
+	    mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN)
+		challenge = &mgmt->u.auth.variable[2];
+
+	wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
+		   "auth_transaction=%d status_code=%d wep=%d%s "
+		   "seq_ctrl=0x%x%s",
+		   MAC2STR(mgmt->sa), auth_alg, auth_transaction,
+		   status_code, !!(fc & WLAN_FC_ISWEP),
+		   challenge ? " challenge" : "",
+		   seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
+
+#ifdef CONFIG_NO_RC4
+	if (auth_alg == WLAN_AUTH_SHARED_KEY) {
+		wpa_printf(MSG_INFO,
+			   "Unsupported authentication algorithm (%d)",
+			   auth_alg);
+		resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+		goto fail;
+	}
+#endif /* CONFIG_NO_RC4 */
+
+	if (hapd->tkip_countermeasures) {
+		resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+		goto fail;
+	}
+
+	if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) &&
+	       auth_alg == WLAN_AUTH_OPEN) ||
+#ifdef CONFIG_IEEE80211R
+	      (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
+	       auth_alg == WLAN_AUTH_FT) ||
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SAE
+	      (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+	       auth_alg == WLAN_AUTH_SAE) ||
+#endif /* CONFIG_SAE */
+	      ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
+	       auth_alg == WLAN_AUTH_SHARED_KEY))) {
+		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))) {
+		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) {
+		wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
+			   MAC2STR(mgmt->sa));
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	if (hapd->conf->no_auth_if_seen_on) {
+		struct hostapd_data *other;
+
+		other = sta_track_seen_on(hapd->iface, mgmt->sa,
+					  hapd->conf->no_auth_if_seen_on);
+		if (other) {
+			u8 *pos;
+			u32 info;
+			u8 op_class, channel, phytype;
+
+			wpa_printf(MSG_DEBUG, "%s: Reject authentication from "
+				   MACSTR " since STA has been seen on %s",
+				   hapd->conf->iface, MAC2STR(mgmt->sa),
+				   hapd->conf->no_auth_if_seen_on);
+
+			resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION;
+			pos = &resp_ies[0];
+			*pos++ = WLAN_EID_NEIGHBOR_REPORT;
+			*pos++ = 13;
+			os_memcpy(pos, other->own_addr, ETH_ALEN);
+			pos += ETH_ALEN;
+			info = 0; /* TODO: BSSID Information */
+			WPA_PUT_LE32(pos, info);
+			pos += 4;
+			if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
+				phytype = 8; /* dmg */
+			else if (other->iconf->ieee80211ac)
+				phytype = 9; /* vht */
+			else if (other->iconf->ieee80211n)
+				phytype = 7; /* ht */
+			else if (other->iconf->hw_mode ==
+				 HOSTAPD_MODE_IEEE80211A)
+				phytype = 4; /* ofdm */
+			else if (other->iconf->hw_mode ==
+				 HOSTAPD_MODE_IEEE80211G)
+				phytype = 6; /* erp */
+			else
+				phytype = 5; /* hrdsss */
+			if (ieee80211_freq_to_channel_ext(
+				    hostapd_hw_get_freq(other,
+							other->iconf->channel),
+				    other->iconf->secondary_channel,
+				    other->iconf->ieee80211ac,
+				    &op_class, &channel) == NUM_HOSTAPD_MODES) {
+				op_class = 0;
+				channel = other->iconf->channel;
+			}
+			*pos++ = op_class;
+			*pos++ = channel;
+			*pos++ = phytype;
+			resp_ies_len = pos - &resp_ies[0];
+			goto fail;
+		}
+	}
+
+	res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len,
+				      &session_timeout,
+				      &acct_interim_interval, &vlan_id,
+				      &psk, &identity, &radius_cui);
+
+	if (res == HOSTAPD_ACL_REJECT) {
+		wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
+			   MAC2STR(mgmt->sa));
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	if (res == HOSTAPD_ACL_PENDING) {
+		wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
+			   " waiting for an external authentication",
+			   MAC2STR(mgmt->sa));
+		/* Authentication code will re-send the authentication frame
+		 * after it has received (and cached) information from the
+		 * external source. */
+		return;
+	}
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+	if (sta) {
+		if ((fc & WLAN_FC_RETRY) &&
+		    sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+		    sta->last_seq_ctrl == seq_ctrl &&
+		    sta->last_subtype == WLAN_FC_STYPE_AUTH) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "Drop repeated authentication frame seq_ctrl=0x%x",
+				       seq_ctrl);
+			return;
+		}
+	} else {
+#ifdef CONFIG_MESH
+		if (hapd->conf->mesh & MESH_ENABLED) {
+			/* if the mesh peer is not available, we don't do auth.
+			 */
+			wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
+				   " not yet known - drop Authentiation frame",
+				   MAC2STR(mgmt->sa));
+			/*
+			 * Save a copy of the frame so that it can be processed
+			 * if a new peer entry is added shortly after this.
+			 */
+			wpabuf_free(hapd->mesh_pending_auth);
+			hapd->mesh_pending_auth = wpabuf_alloc_copy(mgmt, len);
+			os_get_reltime(&hapd->mesh_pending_auth_time);
+			return;
+		}
+#endif /* CONFIG_MESH */
+
+		sta = ap_sta_add(hapd, mgmt->sa);
+		if (!sta) {
+			resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+			goto fail;
+		}
+	}
+	sta->last_seq_ctrl = seq_ctrl;
+	sta->last_subtype = WLAN_FC_STYPE_AUTH;
+
+	if (vlan_id > 0) {
+		if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO, "Invalid VLAN ID "
+				       "%d received from RADIUS server",
+				       vlan_id);
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		sta->vlan_id = vlan_id;
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+			       HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
+	}
+
+	hostapd_free_psk_list(sta->psk);
+	if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) {
+		sta->psk = psk;
+		psk = NULL;
+	} else {
+		sta->psk = NULL;
+	}
+
+	sta->identity = identity;
+	identity = NULL;
+	sta->radius_cui = radius_cui;
+	radius_cui = NULL;
+
+	sta->flags &= ~WLAN_STA_PREAUTH;
+	ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
+
+	if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
+		sta->acct_interim_interval = acct_interim_interval;
+	if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+		ap_sta_session_timeout(hapd, sta, session_timeout);
+	else
+		ap_sta_no_session_timeout(hapd, sta);
+
+	switch (auth_alg) {
+	case WLAN_AUTH_OPEN:
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "authentication OK (open system)");
+		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);
+		break;
+#ifndef CONFIG_NO_RC4
+	case WLAN_AUTH_SHARED_KEY:
+		resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
+				       fc & WLAN_FC_ISWEP);
+		sta->auth_alg = WLAN_AUTH_SHARED_KEY;
+		mlme_authenticate_indication(hapd, sta);
+		if (sta->challenge && auth_transaction == 1) {
+			resp_ies[0] = WLAN_EID_CHALLENGE;
+			resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN;
+			os_memcpy(resp_ies + 2, sta->challenge,
+				  WLAN_AUTH_CHALLENGE_LEN);
+			resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN;
+		}
+		break;
+#endif /* CONFIG_NO_RC4 */
+#ifdef CONFIG_IEEE80211R
+	case WLAN_AUTH_FT:
+		sta->auth_alg = WLAN_AUTH_FT;
+		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_DEBUG, "FT: Failed to initialize WPA "
+				   "state machine");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid,
+				    auth_transaction, mgmt->u.auth.variable,
+				    len - IEEE80211_HDRLEN -
+				    sizeof(mgmt->u.auth),
+				    handle_auth_ft_finish, hapd);
+		/* handle_auth_ft_finish() callback will complete auth. */
+		return;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SAE
+	case WLAN_AUTH_SAE:
+#ifdef CONFIG_MESH
+		if (status_code == WLAN_STATUS_SUCCESS &&
+		    hapd->conf->mesh & MESH_ENABLED) {
+			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_DEBUG,
+					   "SAE: Failed to initialize WPA state machine");
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto fail;
+			}
+		}
+#endif /* CONFIG_MESH */
+		handle_auth_sae(hapd, sta, mgmt, len, auth_transaction,
+				status_code);
+		return;
+#endif /* CONFIG_SAE */
+	}
+
+ fail:
+	os_free(identity);
+	os_free(radius_cui);
+	hostapd_free_psk_list(psk);
+
+	send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
+			auth_transaction + 1, resp, resp_ies, resp_ies_len);
+}
+
+
+static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int i, j = 32, aid;
+
+	/* get a unique AID */
+	if (sta->aid > 0) {
+		wpa_printf(MSG_DEBUG, "  old AID %d", sta->aid);
+		return 0;
+	}
+
+	for (i = 0; i < AID_WORDS; i++) {
+		if (hapd->sta_aid[i] == (u32) -1)
+			continue;
+		for (j = 0; j < 32; j++) {
+			if (!(hapd->sta_aid[i] & BIT(j)))
+				break;
+		}
+		if (j < 32)
+			break;
+	}
+	if (j == 32)
+		return -1;
+	aid = i * 32 + j + 1;
+	if (aid > 2007)
+		return -1;
+
+	sta->aid = aid;
+	hapd->sta_aid[i] |= BIT(j);
+	wpa_printf(MSG_DEBUG, "  new AID %d", sta->aid);
+	return 0;
+}
+
+
+static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *ssid_ie, size_t ssid_ie_len)
+{
+	if (ssid_ie == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	if (ssid_ie_len != hapd->conf->ssid.ssid_len ||
+	    os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "Station tried to associate with unknown SSID "
+			       "'%s'", wpa_ssid_txt(ssid_ie, ssid_ie_len));
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
+		     const u8 *wmm_ie, size_t wmm_ie_len)
+{
+	sta->flags &= ~WLAN_STA_WMM;
+	sta->qosinfo = 0;
+	if (wmm_ie && hapd->conf->wmm_enabled) {
+		struct wmm_information_element *wmm;
+
+		if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "invalid WMM element in association "
+				       "request");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+
+		sta->flags |= WLAN_STA_WMM;
+		wmm = (struct wmm_information_element *) wmm_ie;
+		sta->qosinfo = wmm->qos_info;
+	}
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
+			   struct ieee802_11_elems *elems)
+{
+	if (!elems->supp_rates) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "No supported rates element in AssocReq");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (elems->supp_rates_len + elems->ext_supp_rates_len >
+	    sizeof(sta->supported_rates)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Invalid supported rates element length %d+%d",
+			       elems->supp_rates_len,
+			       elems->ext_supp_rates_len);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->supported_rates_len = merge_byte_arrays(
+		sta->supported_rates, sizeof(sta->supported_rates),
+		elems->supp_rates, elems->supp_rates_len,
+		elems->ext_supp_rates, elems->ext_supp_rates_len);
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+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)
+{
+	struct ieee802_11_elems elems;
+	u16 resp;
+	const u8 *wpa_ie;
+	size_t wpa_ie_len;
+	const u8 *p2p_dev_addr = NULL;
+
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "Station sent an invalid "
+			       "association request");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	resp = check_ssid(hapd, sta, elems.ssid, elems.ssid_len);
+	if (resp != WLAN_STATUS_SUCCESS)
+		return resp;
+	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;
+#ifdef CONFIG_IEEE80211N
+	resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities);
+	if (resp != WLAN_STATUS_SUCCESS)
+		return resp;
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
+	    !(sta->flags & WLAN_STA_HT)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "Station does not support "
+			       "mandatory HT PHY - reject association");
+		return WLAN_STATUS_ASSOC_DENIED_NO_HT;
+	}
+#endif /* CONFIG_IEEE80211N */
+
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->iconf->ieee80211ac) {
+		resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities);
+		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_ASSOC_DENIED_NO_VHT;
+	}
+
+	if (hapd->conf->vendor_vht && !elems.vht_capabilities) {
+		resp = copy_sta_vendor_vht(hapd, sta, elems.vendor_vht,
+					   elems.vendor_vht_len);
+		if (resp != WLAN_STATUS_SUCCESS)
+			return resp;
+	}
+#endif /* CONFIG_IEEE80211AC */
+
+#ifdef CONFIG_P2P
+	if (elems.p2p) {
+		wpabuf_free(sta->p2p_ie);
+		sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+							  P2P_IE_VENDOR_TYPE);
+		if (sta->p2p_ie)
+			p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+	} else {
+		wpabuf_free(sta->p2p_ie);
+		sta->p2p_ie = NULL;
+	}
+#endif /* CONFIG_P2P */
+
+	if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) {
+		wpa_ie = elems.rsn_ie;
+		wpa_ie_len = elems.rsn_ie_len;
+	} else if ((hapd->conf->wpa & WPA_PROTO_WPA) &&
+		   elems.wpa_ie) {
+		wpa_ie = elems.wpa_ie;
+		wpa_ie_len = elems.wpa_ie_len;
+	} else {
+		wpa_ie = NULL;
+		wpa_ie_len = 0;
+	}
+
+#ifdef CONFIG_WPS
+	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+	if (hapd->conf->wps_state && elems.wps_ie) {
+		wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
+			   "Request - assume WPS is used");
+		sta->flags |= WLAN_STA_WPS;
+		wpabuf_free(sta->wps_ie);
+		sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+							  WPS_IE_VENDOR_TYPE);
+		if (sta->wps_ie && wps_is_20(sta->wps_ie)) {
+			wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0");
+			sta->flags |= WLAN_STA_WPS2;
+		}
+		wpa_ie = NULL;
+		wpa_ie_len = 0;
+		if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in "
+				   "(Re)Association Request - reject");
+			return WLAN_STATUS_INVALID_IE;
+		}
+	} else if (hapd->conf->wps_state && wpa_ie == NULL) {
+		wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
+			   "(Re)Association Request - possible WPS use");
+		sta->flags |= WLAN_STA_MAYBE_WPS;
+	} else
+#endif /* CONFIG_WPS */
+	if (hapd->conf->wpa && wpa_ie == NULL) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "No WPA/RSN IE in association request");
+		return WLAN_STATUS_INVALID_IE;
+	}
+
+	if (hapd->conf->wpa && wpa_ie) {
+		int res;
+		wpa_ie -= 2;
+		wpa_ie_len += 2;
+		if (sta->wpa_sm == NULL)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr,
+							p2p_dev_addr);
+		if (sta->wpa_sm == NULL) {
+			wpa_printf(MSG_WARNING, "Failed to initialize WPA "
+				   "state machine");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+					  wpa_ie, wpa_ie_len,
+					  elems.mdie, elems.mdie_len);
+		if (res == WPA_INVALID_GROUP)
+			resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+		else if (res == WPA_INVALID_PAIRWISE)
+			resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+		else if (res == WPA_INVALID_AKMP)
+			resp = WLAN_STATUS_AKMP_NOT_VALID;
+		else if (res == WPA_ALLOC_FAIL)
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+#ifdef CONFIG_IEEE80211W
+		else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
+			resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+		else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
+			resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+#endif /* CONFIG_IEEE80211W */
+		else if (res == WPA_INVALID_MDIE)
+			resp = WLAN_STATUS_INVALID_MDIE;
+		else if (res != WPA_IE_OK)
+			resp = WLAN_STATUS_INVALID_IE;
+		if (resp != WLAN_STATUS_SUCCESS)
+			return resp;
+#ifdef CONFIG_IEEE80211W
+		if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+		    sta->sa_query_count > 0)
+			ap_check_sa_query_timeout(hapd, sta);
+		if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+		    (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
+			/*
+			 * STA has already been associated with MFP and SA
+			 * Query timeout has not been reached. Reject the
+			 * association attempt temporarily and start SA Query,
+			 * if one is not pending.
+			 */
+
+			if (sta->sa_query_count == 0)
+				ap_sta_start_sa_query(hapd, sta);
+
+			return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+		}
+
+		if (wpa_auth_uses_mfp(sta->wpa_sm))
+			sta->flags |= WLAN_STA_MFP;
+		else
+			sta->flags &= ~WLAN_STA_MFP;
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211R
+		if (sta->auth_alg == WLAN_AUTH_FT) {
+			if (!reassoc) {
+				wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
+					   "to use association (not "
+					   "re-association) with FT auth_alg",
+					   MAC2STR(sta->addr));
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+			}
+
+			resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies,
+						       ies_len);
+			if (resp != WLAN_STATUS_SUCCESS)
+				return resp;
+		}
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_SAE
+		if (wpa_auth_uses_sae(sta->wpa_sm) &&
+		    sta->auth_alg == WLAN_AUTH_OPEN) {
+			struct rsn_pmksa_cache_entry *sa;
+			sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+			if (!sa || sa->akmp != WPA_KEY_MGMT_SAE) {
+				wpa_printf(MSG_DEBUG,
+					   "SAE: No PMKSA cache entry found for "
+					   MACSTR, MAC2STR(sta->addr));
+				return WLAN_STATUS_INVALID_PMKID;
+			}
+			wpa_printf(MSG_DEBUG, "SAE: " MACSTR
+				   " using PMKSA caching", MAC2STR(sta->addr));
+		} else if (wpa_auth_uses_sae(sta->wpa_sm) &&
+			   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);
+			return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+		}
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_IEEE80211N
+		if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
+		    wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Station tried to use TKIP with HT "
+				       "association");
+			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);
+
+#ifdef CONFIG_P2P
+	p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_HS20
+	wpabuf_free(sta->hs20_ie);
+	if (elems.hs20 && elems.hs20_len > 4) {
+		sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
+						 elems.hs20_len - 4);
+	} else
+		sta->hs20_ie = NULL;
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_FST
+	wpabuf_free(sta->mb_ies);
+	if (hapd->iface->fst)
+		sta->mb_ies = mb_ies_by_info(&elems.mb_ies);
+	else
+		sta->mb_ies = NULL;
+#endif /* CONFIG_FST */
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
+			u16 reason_code)
+{
+	int send_len;
+	struct ieee80211_mgmt reply;
+
+	os_memset(&reply, 0, sizeof(reply));
+	reply.frame_control =
+		IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH);
+	os_memcpy(reply.da, addr, ETH_ALEN);
+	os_memcpy(reply.sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(reply.bssid, hapd->own_addr, ETH_ALEN);
+
+	send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth);
+	reply.u.deauth.reason_code = host_to_le16(reason_code);
+
+	if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0) < 0)
+		wpa_printf(MSG_INFO, "Failed to send deauth: %s",
+			   strerror(errno));
+}
+
+
+static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
+			    u16 status_code, int reassoc, const u8 *ies,
+			    size_t ies_len)
+{
+	int send_len;
+	u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
+	struct ieee80211_mgmt *reply;
+	u8 *p;
+
+	os_memset(buf, 0, sizeof(buf));
+	reply = (struct ieee80211_mgmt *) buf;
+	reply->frame_control =
+		IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+			     (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
+			      WLAN_FC_STYPE_ASSOC_RESP));
+	os_memcpy(reply->da, sta->addr, ETH_ALEN);
+	os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
+
+	send_len = IEEE80211_HDRLEN;
+	send_len += sizeof(reply->u.assoc_resp);
+	reply->u.assoc_resp.capab_info =
+		host_to_le16(hostapd_own_capab_info(hapd));
+	reply->u.assoc_resp.status_code = host_to_le16(status_code);
+	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 */
+	p = hostapd_eid_ext_supp_rates(hapd, p);
+
+#ifdef CONFIG_IEEE80211R
+	if (status_code == WLAN_STATUS_SUCCESS) {
+		/* IEEE 802.11r: Mobility Domain Information, Fast BSS
+		 * Transition Information, RSN, [RIC Response] */
+		p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
+						buf + sizeof(buf) - p,
+						sta->auth_alg, ies, ies_len);
+	}
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211W
+	if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
+		p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211N
+	p = hostapd_eid_ht_capabilities(hapd, p);
+	p = hostapd_eid_ht_operation(hapd, p);
+#endif /* CONFIG_IEEE80211N */
+
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+		p = hostapd_eid_vht_capabilities(hapd, p);
+		p = hostapd_eid_vht_operation(hapd, p);
+	}
+#endif /* CONFIG_IEEE80211AC */
+
+	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);
+
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies) {
+		os_memcpy(p, wpabuf_head(hapd->iface->fst_ies),
+			  wpabuf_len(hapd->iface->fst_ies));
+		p += wpabuf_len(hapd->iface->fst_ies);
+	}
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
+		p = hostapd_eid_vendor_vht(hapd, p);
+#endif /* CONFIG_IEEE80211AC */
+
+	if (sta->flags & WLAN_STA_WMM)
+		p = hostapd_eid_wmm(hapd, p);
+
+#ifdef CONFIG_WPS
+	if ((sta->flags & WLAN_STA_WPS) ||
+	    ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) {
+		struct wpabuf *wps = wps_build_assoc_resp_ie();
+		if (wps) {
+			os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
+			p += wpabuf_len(wps);
+			wpabuf_free(wps);
+		}
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+	if (sta->p2p_ie) {
+		struct wpabuf *p2p_resp_ie;
+		enum p2p_status_code status;
+		switch (status_code) {
+		case WLAN_STATUS_SUCCESS:
+			status = P2P_SC_SUCCESS;
+			break;
+		case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
+			status = P2P_SC_FAIL_LIMIT_REACHED;
+			break;
+		default:
+			status = P2P_SC_FAIL_INVALID_PARAMS;
+			break;
+		}
+		p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status);
+		if (p2p_resp_ie) {
+			os_memcpy(p, wpabuf_head(p2p_resp_ie),
+				  wpabuf_len(p2p_resp_ie));
+			p += wpabuf_len(p2p_resp_ie);
+			wpabuf_free(p2p_resp_ie);
+		}
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_P2P_MANAGER
+	if (hapd->conf->p2p & P2P_MANAGE)
+		p = hostapd_eid_p2p_manage(hapd, p);
+#endif /* CONFIG_P2P_MANAGER */
+
+	send_len += p - reply->u.assoc_resp.variable;
+
+	if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0)
+		wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
+			   strerror(errno));
+}
+
+
+static void handle_assoc(struct hostapd_data *hapd,
+			 const struct ieee80211_mgmt *mgmt, size_t len,
+			 int reassoc)
+{
+	u16 capab_info, listen_interval, seq_ctrl, fc;
+	u16 resp = WLAN_STATUS_SUCCESS;
+	const u8 *pos;
+	int left, i;
+	struct sta_info *sta;
+
+	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
+				      sizeof(mgmt->u.assoc_req))) {
+		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.0 &&
+		    drand48() < hapd->iconf->ignore_reassoc_probability) {
+			wpa_printf(MSG_INFO,
+				   "TESTING: ignoring reassoc request from "
+				   MACSTR, MAC2STR(mgmt->sa));
+			return;
+		}
+	} else {
+		if (hapd->iconf->ignore_assoc_probability > 0.0 &&
+		    drand48() < hapd->iconf->ignore_assoc_probability) {
+			wpa_printf(MSG_INFO,
+				   "TESTING: ignoring assoc request from "
+				   MACSTR, MAC2STR(mgmt->sa));
+			return;
+		}
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	fc = le_to_host16(mgmt->frame_control);
+	seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
+	if (reassoc) {
+		capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info);
+		listen_interval = le_to_host16(
+			mgmt->u.reassoc_req.listen_interval);
+		wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR
+			   " capab_info=0x%02x listen_interval=%d current_ap="
+			   MACSTR " seq_ctrl=0x%x%s",
+			   MAC2STR(mgmt->sa), capab_info, listen_interval,
+			   MAC2STR(mgmt->u.reassoc_req.current_ap),
+			   seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
+		left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
+		pos = mgmt->u.reassoc_req.variable;
+	} else {
+		capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
+		listen_interval = le_to_host16(
+			mgmt->u.assoc_req.listen_interval);
+		wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR
+			   " capab_info=0x%02x listen_interval=%d "
+			   "seq_ctrl=0x%x%s",
+			   MAC2STR(mgmt->sa), capab_info, listen_interval,
+			   seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
+		left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
+		pos = mgmt->u.assoc_req.variable;
+	}
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+#ifdef CONFIG_IEEE80211R
+	if (sta && sta->auth_alg == WLAN_AUTH_FT &&
+	    (sta->flags & WLAN_STA_AUTH) == 0) {
+		wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
+			   "prior to authentication since it is using "
+			   "over-the-DS FT", MAC2STR(mgmt->sa));
+	} else
+#endif /* CONFIG_IEEE80211R */
+	if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "Station tried to "
+			       "associate before authentication "
+			       "(aid=%d flags=0x%x)",
+			       sta ? sta->aid : -1,
+			       sta ? sta->flags : 0);
+		send_deauth(hapd, mgmt->sa,
+			    WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
+		return;
+	}
+
+	if ((fc & WLAN_FC_RETRY) &&
+	    sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+	    sta->last_seq_ctrl == seq_ctrl &&
+	    sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+	    WLAN_FC_STYPE_ASSOC_REQ) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Drop repeated association frame seq_ctrl=0x%x",
+			       seq_ctrl);
+		return;
+	}
+	sta->last_seq_ctrl = seq_ctrl;
+	sta->last_subtype = reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+		WLAN_FC_STYPE_ASSOC_REQ;
+
+	if (hapd->tkip_countermeasures) {
+		resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+		goto fail;
+	}
+
+	if (listen_interval > hapd->conf->max_listen_interval) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Too large Listen Interval (%d)",
+			       listen_interval);
+		resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE;
+		goto fail;
+	}
+
+	/* followed by SSID and Supported rates; and HT capabilities if 802.11n
+	 * is used */
+	resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
+	if (resp != WLAN_STATUS_SUCCESS)
+		goto fail;
+
+	if (hostapd_get_aid(hapd, sta) < 0) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "No room for more AIDs");
+		resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+		goto fail;
+	}
+
+	sta->capability = capab_info;
+	sta->listen_interval = listen_interval;
+
+	if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+		sta->flags |= WLAN_STA_NONERP;
+	for (i = 0; i < sta->supported_rates_len; i++) {
+		if ((sta->supported_rates[i] & 0x7f) > 22) {
+			sta->flags &= ~WLAN_STA_NONERP;
+			break;
+		}
+	}
+	if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) {
+		sta->nonerp_set = 1;
+		hapd->iface->num_sta_non_erp++;
+		if (hapd->iface->num_sta_non_erp == 1)
+			ieee802_11_set_beacons(hapd->iface);
+	}
+
+	if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) &&
+	    !sta->no_short_slot_time_set) {
+		sta->no_short_slot_time_set = 1;
+		hapd->iface->num_sta_no_short_slot_time++;
+		if (hapd->iface->current_mode->mode ==
+		    HOSTAPD_MODE_IEEE80211G &&
+		    hapd->iface->num_sta_no_short_slot_time == 1)
+			ieee802_11_set_beacons(hapd->iface);
+	}
+
+	if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+		sta->flags |= WLAN_STA_SHORT_PREAMBLE;
+	else
+		sta->flags &= ~WLAN_STA_SHORT_PREAMBLE;
+
+	if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
+	    !sta->no_short_preamble_set) {
+		sta->no_short_preamble_set = 1;
+		hapd->iface->num_sta_no_short_preamble++;
+		if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+		    && hapd->iface->num_sta_no_short_preamble == 1)
+			ieee802_11_set_beacons(hapd->iface);
+	}
+
+#ifdef CONFIG_IEEE80211N
+	update_ht_state(hapd, sta);
+#endif /* CONFIG_IEEE80211N */
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "association OK (aid %d)", sta->aid);
+	/* Station will be marked associated, after it acknowledges AssocResp
+	 */
+	sta->flags |= WLAN_STA_ASSOC_REQ_OK;
+
+#ifdef CONFIG_IEEE80211W
+	if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) {
+		wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out "
+			   "SA Query procedure", reassoc ? "re" : "");
+		/* TODO: Send a protected Disassociate frame to the STA using
+		 * the old key and Reason Code "Previous Authentication no
+		 * longer valid". Make sure this is only sent protected since
+		 * unprotected frame would be received by the STA that is now
+		 * trying to associate.
+		 */
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	/* Make sure that the previously registered inactivity timer will not
+	 * remove the STA immediately. */
+	sta->timeout_next = STA_NULLFUNC;
+
+ fail:
+	send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
+}
+
+
+static void handle_disassoc(struct hostapd_data *hapd,
+			    const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct sta_info *sta;
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
+		wpa_printf(MSG_INFO, "handle_disassoc - too short payload (len=%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d",
+		   MAC2STR(mgmt->sa),
+		   le_to_host16(mgmt->u.disassoc.reason_code));
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+	if (sta == NULL) {
+		wpa_printf(MSG_INFO, "Station " MACSTR " trying to disassociate, but it is not associated",
+			   MAC2STR(mgmt->sa));
+		return;
+	}
+
+	ap_sta_set_authorized(hapd, sta, 0);
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+	sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "disassociated");
+	sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+	/* Stop Accounting and IEEE 802.1X sessions, but leave the STA
+	 * authenticated. */
+	accounting_sta_stop(hapd, sta);
+	ieee802_1x_free_station(sta);
+	if (sta->ipaddr)
+		hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+	ap_sta_ip6addr_del(hapd, sta);
+	hostapd_drv_sta_remove(hapd, sta->addr);
+
+	if (sta->timeout_next == STA_NULLFUNC ||
+	    sta->timeout_next == STA_DISASSOC) {
+		sta->timeout_next = STA_DEAUTH;
+		eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+		eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
+				       hapd, sta);
+	}
+
+	mlme_disassociate_indication(
+		hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code));
+}
+
+
+static void handle_deauth(struct hostapd_data *hapd,
+			  const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct sta_info *sta;
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "handle_deauth - too short "
+			"payload (len=%lu)", (unsigned long) len);
+		return;
+	}
+
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "deauthentication: STA=" MACSTR
+		" reason_code=%d",
+		MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+	if (sta == NULL) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying "
+			"to deauthenticate, but it is not authenticated",
+			MAC2STR(mgmt->sa));
+		return;
+	}
+
+	ap_sta_set_authorized(hapd, sta, 0);
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+			WLAN_STA_ASSOC_REQ_OK);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "deauthenticated");
+	mlme_deauthenticate_indication(
+		hapd, sta, le_to_host16(mgmt->u.deauth.reason_code));
+	sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+	ap_free_sta(hapd, sta);
+}
+
+
+static void handle_beacon(struct hostapd_data *hapd,
+			  const struct ieee80211_mgmt *mgmt, size_t len,
+			  struct hostapd_frame_info *fi)
+{
+	struct ieee802_11_elems elems;
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) {
+		wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+	(void) ieee802_11_parse_elems(mgmt->u.beacon.variable,
+				      len - (IEEE80211_HDRLEN +
+					     sizeof(mgmt->u.beacon)), &elems,
+				      0);
+
+	ap_list_process_beacon(hapd->iface, mgmt, &elems, fi);
+}
+
+
+#ifdef CONFIG_IEEE80211W
+
+static int hostapd_sa_query_action(struct hostapd_data *hapd,
+				   const struct ieee80211_mgmt *mgmt,
+				   size_t len)
+{
+	const u8 *end;
+
+	end = mgmt->u.action.u.sa_query_resp.trans_id +
+		WLAN_SA_QUERY_TR_ID_LEN;
+	if (((u8 *) mgmt) + len < end) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action "
+			   "frame (len=%lu)", (unsigned long) len);
+		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;
+}
+
+
+static int robust_action_frame(u8 category)
+{
+	return category != WLAN_ACTION_PUBLIC &&
+		category != WLAN_ACTION_HT;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+static int handle_action(struct hostapd_data *hapd,
+			 const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct sta_info *sta;
+	sta = ap_get_sta(hapd, mgmt->sa);
+
+	if (len < IEEE80211_HDRLEN + 1) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "handle_action - too short payload (len=%lu)",
+			       (unsigned long) len);
+		return 0;
+	}
+
+	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+	    (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
+			   "frame (category=%u) from unassociated STA " MACSTR,
+			   MAC2STR(mgmt->sa), mgmt->u.action.category);
+		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)) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Dropped unprotected Robust Action frame from "
+			       "an MFP STA");
+		return 0;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	if (sta) {
+		u16 fc = le_to_host16(mgmt->frame_control);
+		u16 seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
+		if ((fc & WLAN_FC_RETRY) &&
+		    sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+		    sta->last_seq_ctrl == seq_ctrl &&
+		    sta->last_subtype == WLAN_FC_STYPE_ACTION) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "Drop repeated action frame seq_ctrl=0x%x",
+				       seq_ctrl);
+			return 1;
+		}
+
+		sta->last_seq_ctrl = seq_ctrl;
+		sta->last_subtype = WLAN_FC_STYPE_ACTION;
+	}
+
+	switch (mgmt->u.action.category) {
+#ifdef CONFIG_IEEE80211R
+	case WLAN_ACTION_FT:
+		if (!sta ||
+		    wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
+				     len - IEEE80211_HDRLEN))
+			break;
+		return 1;
+#endif /* CONFIG_IEEE80211R */
+	case WLAN_ACTION_WMM:
+		hostapd_wmm_action(hapd, mgmt, len);
+		return 1;
+#ifdef CONFIG_IEEE80211W
+	case WLAN_ACTION_SA_QUERY:
+		return hostapd_sa_query_action(hapd, mgmt, len);
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_WNM
+	case WLAN_ACTION_WNM:
+		ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
+		return 1;
+#endif /* CONFIG_WNM */
+#ifdef CONFIG_FST
+	case WLAN_ACTION_FST:
+		if (hapd->iface->fst)
+			fst_rx_action(hapd->iface->fst, mgmt, len);
+		else
+			wpa_printf(MSG_DEBUG,
+				   "FST: Ignore FST Action frame - no FST attached");
+		return 1;
+#endif /* CONFIG_FST */
+	case WLAN_ACTION_PUBLIC:
+	case WLAN_ACTION_PROTECTED_DUAL:
+#ifdef CONFIG_IEEE80211N
+		if (len >= IEEE80211_HDRLEN + 2 &&
+		    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,
+					       hapd->iface->freq);
+		}
+		if (hapd->public_action_cb2) {
+			hapd->public_action_cb2(hapd->public_action_cb2_ctx,
+						(u8 *) mgmt, len,
+						hapd->iface->freq);
+		}
+		if (hapd->public_action_cb || hapd->public_action_cb2)
+			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 1;
+		}
+		break;
+	}
+
+	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "handle_action - unknown action category %d or invalid "
+		       "frame",
+		       mgmt->u.action.category);
+	if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) &&
+	    !(mgmt->sa[0] & 0x01)) {
+		struct ieee80211_mgmt *resp;
+
+		/*
+		 * IEEE 802.11-REVma/D9.0 - 7.3.1.11
+		 * Return the Action frame to the source without change
+		 * except that MSB of the Category set to 1.
+		 */
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action "
+			   "frame back to sender");
+		resp = os_malloc(len);
+		if (resp == NULL)
+			return 0;
+		os_memcpy(resp, mgmt, len);
+		os_memcpy(resp->da, resp->sa, ETH_ALEN);
+		os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+		os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+		resp->u.action.category |= 0x80;
+
+		if (hostapd_drv_send_mlme(hapd, resp, len, 0) < 0) {
+			wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send "
+				   "Action frame");
+		}
+		os_free(resp);
+	}
+
+	return 1;
+}
+
+
+/**
+ * ieee802_11_mgmt - process incoming IEEE 802.11 management frames
+ * @hapd: hostapd BSS data structure (the BSS to which the management frame was
+ * sent to)
+ * @buf: management frame data (starting from IEEE 802.11 header)
+ * @len: length of frame data in octets
+ * @fi: meta data about received frame (signal level, etc.)
+ *
+ * Process all incoming IEEE 802.11 management frames. This will be called for
+ * each frame received from the kernel driver through wlan#ap interface. In
+ * addition, it can be called to re-inserted pending frames (e.g., when using
+ * external RADIUS server as an MAC ACL).
+ */
+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 0;
+
+	mgmt = (struct ieee80211_mgmt *) buf;
+	fc = le_to_host16(mgmt->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if (stype == WLAN_FC_STYPE_BEACON) {
+		handle_beacon(hapd, mgmt, len, fi);
+		return 1;
+	}
+
+	broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff &&
+		mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff &&
+		mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff;
+
+	if (!broadcast &&
+#ifdef CONFIG_P2P
+	    /* Invitation responses can be sent with the peer MAC as BSSID */
+	    !((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+	      stype == WLAN_FC_STYPE_ACTION) &&
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_MESH
+	    !(hapd->conf->mesh & MESH_ENABLED) &&
+#endif /* CONFIG_MESH */
+	    os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
+		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 1;
+	}
+
+	if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "MGMT: DA=" MACSTR " not our address",
+			       MAC2STR(mgmt->da));
+		return 0;
+	}
+
+	if (hapd->iconf->track_sta_max_num)
+		sta_track_add(hapd->iface, mgmt->sa);
+
+	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");
+		ret = handle_action(hapd, mgmt, len);
+		break;
+	default:
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "unknown mgmt frame subtype %d", stype);
+		break;
+	}
+
+	return ret;
+}
+
+
+static void handle_auth_cb(struct hostapd_data *hapd,
+			   const struct ieee80211_mgmt *mgmt,
+			   size_t len, int ok)
+{
+	u16 auth_alg, auth_transaction, status_code;
+	struct sta_info *sta;
+
+	if (!ok) {
+		hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_NOTICE,
+			       "did not acknowledge authentication response");
+		return;
+	}
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+		wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+	auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+	auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+	status_code = le_to_host16(mgmt->u.auth.status_code);
+
+	sta = ap_get_sta(hapd, mgmt->da);
+	if (!sta) {
+		wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found",
+			   MAC2STR(mgmt->da));
+		return;
+	}
+
+	if (status_code == WLAN_STATUS_SUCCESS &&
+	    ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
+	     (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "authenticated");
+		sta->flags |= WLAN_STA_AUTH;
+	}
+}
+
+
+static void hostapd_set_wds_encryption(struct hostapd_data *hapd,
+				       struct sta_info *sta,
+				       char *ifname_wds)
+{
+	int i;
+	struct hostapd_ssid *ssid = &hapd->conf->ssid;
+
+	if (hapd->conf->ieee802_1x || hapd->conf->wpa)
+		return;
+
+	for (i = 0; i < 4; i++) {
+		if (ssid->wep.key[i] &&
+		    hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i,
+					i == ssid->wep.idx, NULL, 0,
+					ssid->wep.key[i], ssid->wep.len[i])) {
+			wpa_printf(MSG_WARNING,
+				   "Could not set WEP keys for WDS interface; %s",
+				   ifname_wds);
+			break;
+		}
+	}
+}
+
+
+static void handle_assoc_cb(struct hostapd_data *hapd,
+			    const struct ieee80211_mgmt *mgmt,
+			    size_t len, int reassoc, int ok)
+{
+	u16 status;
+	struct sta_info *sta;
+	int new_assoc = 1;
+	struct ieee80211_ht_capabilities ht_cap;
+	struct ieee80211_vht_capabilities vht_cap;
+
+	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
+				      sizeof(mgmt->u.assoc_resp))) {
+		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) {
+		wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found",
+			   MAC2STR(mgmt->da));
+		return;
+	}
+
+	if (!ok) {
+		hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "did not acknowledge association response");
+		sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
+		return;
+	}
+
+	if (reassoc)
+		status = le_to_host16(mgmt->u.reassoc_resp.status_code);
+	else
+		status = le_to_host16(mgmt->u.assoc_resp.status_code);
+
+	if (status != WLAN_STATUS_SUCCESS)
+		return;
+
+	/* Stop previous accounting session, if one is started, and allocate
+	 * new session id for the new session. */
+	accounting_sta_stop(hapd, sta);
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO,
+		       "associated (aid %d)",
+		       sta->aid);
+
+	if (sta->flags & WLAN_STA_ASSOC)
+		new_assoc = 0;
+	sta->flags |= WLAN_STA_ASSOC;
+	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+	if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) ||
+	    sta->auth_alg == WLAN_AUTH_FT) {
+		/*
+		 * Open, static WEP, or FT protocol; no separate authorization
+		 * step.
+		 */
+		ap_sta_set_authorized(hapd, sta, 1);
+	}
+
+	if (reassoc)
+		mlme_reassociate_indication(hapd, sta);
+	else
+		mlme_associate_indication(hapd, sta);
+
+#ifdef CONFIG_IEEE80211W
+	sta->sa_query_timed_out = 0;
+#endif /* CONFIG_IEEE80211W */
+
+	/*
+	 * Remove the STA entry in order to make sure the STA PS state gets
+	 * cleared and configuration gets updated in case of reassociation back
+	 * to the same AP.
+	 */
+	hostapd_drv_sta_remove(hapd, sta->addr);
+
+#ifdef CONFIG_IEEE80211N
+	if (sta->flags & WLAN_STA_HT)
+		hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
+#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+	if (sta->flags & WLAN_STA_VHT)
+		hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
+#endif /* CONFIG_IEEE80211AC */
+
+	if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
+			    sta->supported_rates, sta->supported_rates_len,
+			    sta->listen_interval,
+			    sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
+			    sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
+			    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");
+
+		ap_sta_disconnect(hapd, sta, sta->addr,
+				  WLAN_REASON_DISASSOC_AP_BUSY);
+
+		return;
+	}
+
+	if (sta->flags & WLAN_STA_WDS) {
+		int ret;
+		char ifname_wds[IFNAMSIZ + 1];
+
+		ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
+					  sta->aid, 1);
+		if (!ret)
+			hostapd_set_wds_encryption(hapd, sta, ifname_wds);
+	}
+
+	if (sta->eapol_sm == NULL) {
+		/*
+		 * This STA does not use RADIUS server for EAP authentication,
+		 * so bind it to the selected VLAN interface now, since the
+		 * interface selection is not going to change anymore.
+		 */
+		if (ap_sta_bind_vlan(hapd, sta) < 0)
+			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)
+			return;
+	}
+
+	hostapd_set_sta_flags(hapd, sta);
+
+	if (sta->auth_alg == WLAN_AUTH_FT)
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
+	else
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+	hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
+
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+}
+
+
+static void handle_deauth_cb(struct hostapd_data *hapd,
+			     const struct ieee80211_mgmt *mgmt,
+			     size_t len, int ok)
+{
+	struct sta_info *sta;
+	if (mgmt->da[0] & 0x01)
+		return;
+	sta = ap_get_sta(hapd, mgmt->da);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR
+			   " not found", MAC2STR(mgmt->da));
+		return;
+	}
+	if (ok)
+		wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth",
+			   MAC2STR(sta->addr));
+	else
+		wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
+			   "deauth", MAC2STR(sta->addr));
+
+	ap_sta_deauth_cb(hapd, sta);
+}
+
+
+static void handle_disassoc_cb(struct hostapd_data *hapd,
+			       const struct ieee80211_mgmt *mgmt,
+			       size_t len, int ok)
+{
+	struct sta_info *sta;
+	if (mgmt->da[0] & 0x01)
+		return;
+	sta = ap_get_sta(hapd, mgmt->da);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR
+			   " not found", MAC2STR(mgmt->da));
+		return;
+	}
+	if (ok)
+		wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc",
+			   MAC2STR(sta->addr));
+	else
+		wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
+			   "disassoc", MAC2STR(sta->addr));
+
+	ap_sta_disassoc_cb(hapd, sta);
+}
+
+
+/**
+ * ieee802_11_mgmt_cb - Process management frame TX status callback
+ * @hapd: hostapd BSS data structure (the BSS from which the management frame
+ * was sent from)
+ * @buf: management frame data (starting from IEEE 802.11 header)
+ * @len: length of frame data in octets
+ * @stype: management frame subtype from frame control field
+ * @ok: Whether the frame was ACK'ed
+ */
+void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
+			u16 stype, int ok)
+{
+	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");
+		handle_auth_cb(hapd, mgmt, len, ok);
+		break;
+	case WLAN_FC_STYPE_ASSOC_RESP:
+		wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb");
+		handle_assoc_cb(hapd, mgmt, len, 0, ok);
+		break;
+	case WLAN_FC_STYPE_REASSOC_RESP:
+		wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb");
+		handle_assoc_cb(hapd, mgmt, len, 1, ok);
+		break;
+	case WLAN_FC_STYPE_PROBE_RESP:
+		wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb");
+		break;
+	case WLAN_FC_STYPE_DEAUTH:
+		wpa_printf(MSG_DEBUG, "mgmt::deauth cb");
+		handle_deauth_cb(hapd, mgmt, len, ok);
+		break;
+	case WLAN_FC_STYPE_DISASSOC:
+		wpa_printf(MSG_DEBUG, "mgmt::disassoc cb");
+		handle_disassoc_cb(hapd, mgmt, len, ok);
+		break;
+	case WLAN_FC_STYPE_ACTION:
+		wpa_printf(MSG_DEBUG, "mgmt::action cb");
+		break;
+	default:
+		wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
+		break;
+	}
+}
+
+
+int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+	/* TODO */
+	return 0;
+}
+
+
+int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   char *buf, size_t buflen)
+{
+	/* TODO */
+	return 0;
+}
+
+
+void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+		       const u8 *buf, size_t len, int ack)
+{
+	struct sta_info *sta;
+	struct hostapd_iface *iface = hapd->iface;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL && iface->num_bss > 1) {
+		size_t j;
+		for (j = 0; j < iface->num_bss; j++) {
+			hapd = iface->bss[j];
+			sta = ap_get_sta(hapd, addr);
+			if (sta)
+				break;
+		}
+	}
+	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))
+		return;
+	if (sta->flags & WLAN_STA_PENDING_POLL) {
+		wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending "
+			   "activity poll", MAC2STR(sta->addr),
+			   ack ? "ACKed" : "did not ACK");
+		if (ack)
+			sta->flags &= ~WLAN_STA_PENDING_POLL;
+	}
+
+	ieee802_1x_tx_status(hapd, sta, buf, len, ack);
+}
+
+
+void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+			     const u8 *data, size_t len, int ack)
+{
+	struct sta_info *sta;
+	struct hostapd_iface *iface = hapd->iface;
+
+	sta = ap_get_sta(hapd, dst);
+	if (sta == NULL && iface->num_bss > 1) {
+		size_t j;
+		for (j = 0; j < iface->num_bss; j++) {
+			hapd = iface->bss[j];
+			sta = ap_get_sta(hapd, dst);
+			if (sta)
+				break;
+		}
+	}
+	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+		wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
+			   MACSTR " that is not currently associated",
+			   MAC2STR(dst));
+		return;
+	}
+
+	ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
+}
+
+
+void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta;
+	struct hostapd_iface *iface = hapd->iface;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL && iface->num_bss > 1) {
+		size_t j;
+		for (j = 0; j < iface->num_bss; j++) {
+			hapd = iface->bss[j];
+			sta = ap_get_sta(hapd, addr);
+			if (sta)
+				break;
+		}
+	}
+	if (sta == NULL)
+		return;
+	if (!(sta->flags & WLAN_STA_PENDING_POLL))
+		return;
+
+	wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending "
+		   "activity poll", MAC2STR(sta->addr));
+	sta->flags &= ~WLAN_STA_PENDING_POLL;
+}
+
+
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+				int wds)
+{
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, src);
+	if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+		if (!hapd->conf->wds_sta)
+			return;
+
+		if (wds && !(sta->flags & WLAN_STA_WDS)) {
+			int ret;
+			char ifname_wds[IFNAMSIZ + 1];
+
+			wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for "
+				   "STA " MACSTR " (aid %u)",
+				   MAC2STR(sta->addr), sta->aid);
+			sta->flags |= WLAN_STA_WDS;
+			ret = hostapd_set_wds_sta(hapd, ifname_wds,
+						  sta->addr, sta->aid, 1);
+			if (!ret)
+				hostapd_set_wds_encryption(hapd, sta,
+							   ifname_wds);
+		}
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
+		   MACSTR, MAC2STR(src));
+	if (src[0] & 0x01) {
+		/* Broadcast bit set in SA?! Ignore the frame silently. */
+		return;
+	}
+
+	if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) {
+		wpa_printf(MSG_DEBUG, "Association Response to the STA has "
+			   "already been sent, but no TX status yet known - "
+			   "ignore Class 3 frame issue with " MACSTR,
+			   MAC2STR(src));
+		return;
+	}
+
+	if (sta && (sta->flags & WLAN_STA_AUTH))
+		hostapd_drv_sta_disassoc(
+			hapd, src,
+			WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+	else
+		hostapd_drv_sta_deauth(
+			hapd, src,
+			WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+}
+
+
+#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/hostap/src/ap/ieee802_11.h b/hostap/src/ap/ieee802_11.h
new file mode 100644
index 0000000..44c1bff
--- /dev/null
+++ b/hostap/src/ap/ieee802_11.h
@@ -0,0 +1,107 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * 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 IEEE802_11_H
+#define IEEE802_11_H
+
+struct hostapd_iface;
+struct hostapd_data;
+struct sta_info;
+struct hostapd_frame_info;
+struct ieee80211_ht_capabilities;
+struct ieee80211_vht_capabilities;
+struct ieee80211_mgmt;
+
+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 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,
+			   char *buf, size_t buflen);
+#else /* NEED_AP_MLME */
+static inline int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf,
+				     size_t buflen)
+{
+	return 0;
+}
+
+static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
+					 struct sta_info *sta,
+					 char *buf, size_t buflen)
+{
+	return 0;
+}
+#endif /* NEED_AP_MLME */
+u16 hostapd_own_capab_info(struct hostapd_data *hapd);
+void ap_ht2040_timeout(void *eloop_data, void *user_data);
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_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);
+u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
+int hostapd_ht_operation_update(struct hostapd_iface *iface);
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+				  const u8 *addr, const u8 *trans_id);
+void hostapd_get_ht_capab(struct hostapd_data *hapd,
+			  struct ieee80211_ht_capabilities *ht_cap,
+			  struct ieee80211_ht_capabilities *neg_ht_cap);
+void hostapd_get_vht_capab(struct hostapd_data *hapd,
+			   struct ieee80211_vht_capabilities *vht_cap,
+			   struct ieee80211_vht_capabilities *neg_vht_cap);
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *ht_capab);
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+			const u8 *ie, size_t 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);
+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,
+			     const u8 *data, size_t len, int ack);
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+				int wds);
+u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+				     struct sta_info *sta, u8 *eid);
+void ieee802_11_sa_query_action(struct hostapd_data *hapd,
+				const u8 *sa, const u8 action_type,
+				const u8 *trans_id);
+u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
+int hostapd_update_time_adv(struct hostapd_data *hapd);
+void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
+
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
+#ifdef CONFIG_SAE
+void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+				struct sta_info *sta);
+#else /* CONFIG_SAE */
+static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+					      struct sta_info *sta)
+{
+}
+#endif /* CONFIG_SAE */
+
+#endif /* IEEE802_11_H */
diff --git a/hostap/src/ap/ieee802_11_auth.c b/hostap/src/ap/ieee802_11_auth.c
new file mode 100644
index 0000000..531a67d
--- /dev/null
+++ b/hostap/src/ap/ieee802_11_auth.c
@@ -0,0 +1,648 @@
+/*
+ * hostapd / IEEE 802.11 authentication (ACL)
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * Access control list for IEEE 802.11 authentication can uses statically
+ * configured ACL from configuration files or an external RADIUS server.
+ * Results from external RADIUS queries are cached to allow faster
+ * authentication frame processing.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/sha1.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "ieee802_11.h"
+#include "ieee802_1x.h"
+#include "ieee802_11_auth.h"
+
+#define RADIUS_ACL_TIMEOUT 30
+
+
+struct hostapd_cached_radius_acl {
+	struct os_reltime timestamp;
+	macaddr addr;
+	int accepted; /* HOSTAPD_ACL_* */
+	struct hostapd_cached_radius_acl *next;
+	u32 session_timeout;
+	u32 acct_interim_interval;
+	int vlan_id;
+	struct hostapd_sta_wpa_psk_short *psk;
+	char *identity;
+	char *radius_cui;
+};
+
+
+struct hostapd_acl_query_data {
+	struct os_reltime timestamp;
+	u8 radius_id;
+	macaddr addr;
+	u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
+	size_t auth_msg_len;
+	struct hostapd_acl_query_data *next;
+};
+
+
+#ifndef CONFIG_NO_RADIUS
+static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e)
+{
+	os_free(e->identity);
+	os_free(e->radius_cui);
+	hostapd_free_psk_list(e->psk);
+	os_free(e);
+}
+
+
+static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
+{
+	struct hostapd_cached_radius_acl *prev;
+
+	while (acl_cache) {
+		prev = acl_cache;
+		acl_cache = acl_cache->next;
+		hostapd_acl_cache_free_entry(prev);
+	}
+}
+
+
+static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
+			  struct hostapd_sta_wpa_psk_short *src)
+{
+	struct hostapd_sta_wpa_psk_short **copy_to;
+	struct hostapd_sta_wpa_psk_short *copy_from;
+
+	/* Copy PSK linked list */
+	copy_to = psk;
+	copy_from = src;
+	while (copy_from && copy_to) {
+		*copy_to = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
+		if (*copy_to == NULL)
+			break;
+		os_memcpy(*copy_to, copy_from,
+			  sizeof(struct hostapd_sta_wpa_psk_short));
+		copy_from = copy_from->next;
+		copy_to = &((*copy_to)->next);
+	}
+	if (copy_to)
+		*copy_to = NULL;
+}
+
+
+static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
+				 u32 *session_timeout,
+				 u32 *acct_interim_interval, int *vlan_id,
+				 struct hostapd_sta_wpa_psk_short **psk,
+				 char **identity, char **radius_cui)
+{
+	struct hostapd_cached_radius_acl *entry;
+	struct os_reltime 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 (os_reltime_expired(&now, &entry->timestamp,
+				       RADIUS_ACL_TIMEOUT))
+			return -1; /* entry has expired */
+		if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+			if (session_timeout)
+				*session_timeout = entry->session_timeout;
+		if (acct_interim_interval)
+			*acct_interim_interval =
+				entry->acct_interim_interval;
+		if (vlan_id)
+			*vlan_id = entry->vlan_id;
+		copy_psk_list(psk, entry->psk);
+		if (identity) {
+			if (entry->identity)
+				*identity = os_strdup(entry->identity);
+			else
+				*identity = NULL;
+		}
+		if (radius_cui) {
+			if (entry->radius_cui)
+				*radius_cui = os_strdup(entry->radius_cui);
+			else
+				*radius_cui = NULL;
+		}
+		return entry->accepted;
+	}
+
+	return -1;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
+{
+	if (query == NULL)
+		return;
+	os_free(query->auth_msg);
+	os_free(query);
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
+				    struct hostapd_acl_query_data *query)
+{
+	struct radius_msg *msg;
+	char buf[128];
+
+	query->radius_id = radius_client_get_id(hapd->radius);
+	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
+	if (msg == NULL)
+		return -1;
+
+	radius_msg_make_authenticator(msg, addr, ETH_ALEN);
+
+	os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
+				 os_strlen(buf))) {
+		wpa_printf(MSG_DEBUG, "Could not add User-Name");
+		goto fail;
+	}
+
+	if (!radius_msg_add_attr_user_password(
+		    msg, (u8 *) buf, os_strlen(buf),
+		    hapd->conf->radius->auth_server->shared_secret,
+		    hapd->conf->radius->auth_server->shared_secret_len)) {
+		wpa_printf(MSG_DEBUG, "Could not add User-Password");
+		goto fail;
+	}
+
+	if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr,
+				   NULL, msg) < 0)
+		goto fail;
+
+	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+		    MAC2STR(addr));
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id");
+		goto fail;
+	}
+
+	os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_DEBUG, "Could not add Connect-Info");
+		goto fail;
+	}
+
+	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
+		goto fail;
+	return 0;
+
+ fail:
+	radius_msg_free(msg);
+	return -1;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+/**
+ * hostapd_allowed_address - Check whether a specified STA can be authenticated
+ * @hapd: hostapd BSS data
+ * @addr: MAC address of the STA
+ * @msg: Authentication message
+ * @len: Length of msg in octets
+ * @session_timeout: Buffer for returning session timeout (from RADIUS)
+ * @acct_interim_interval: Buffer for returning account interval (from RADIUS)
+ * @vlan_id: Buffer for returning VLAN ID
+ * @psk: Linked list buffer for returning WPA PSK
+ * @identity: Buffer for returning identity (from RADIUS)
+ * @radius_cui: Buffer for returning CUI (from RADIUS)
+ * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
+ *
+ * The caller is responsible for freeing the returned *identity and *radius_cui
+ * values with os_free().
+ */
+int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+			    const u8 *msg, size_t len, u32 *session_timeout,
+			    u32 *acct_interim_interval, int *vlan_id,
+			    struct hostapd_sta_wpa_psk_short **psk,
+			    char **identity, char **radius_cui)
+{
+	if (session_timeout)
+		*session_timeout = 0;
+	if (acct_interim_interval)
+		*acct_interim_interval = 0;
+	if (vlan_id)
+		*vlan_id = 0;
+	if (psk)
+		*psk = NULL;
+	if (identity)
+		*identity = NULL;
+	if (radius_cui)
+		*radius_cui = NULL;
+
+	if (hostapd_maclist_found(hapd->conf->accept_mac,
+				  hapd->conf->num_accept_mac, addr, vlan_id))
+		return HOSTAPD_ACL_ACCEPT;
+
+	if (hostapd_maclist_found(hapd->conf->deny_mac,
+				  hapd->conf->num_deny_mac, addr, vlan_id))
+		return HOSTAPD_ACL_REJECT;
+
+	if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
+		return HOSTAPD_ACL_ACCEPT;
+	if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
+		return HOSTAPD_ACL_REJECT;
+
+	if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
+#ifdef CONFIG_NO_RADIUS
+		return HOSTAPD_ACL_REJECT;
+#else /* CONFIG_NO_RADIUS */
+		struct hostapd_acl_query_data *query;
+
+		/* Check whether ACL cache has an entry for this station */
+		int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
+						acct_interim_interval,
+						vlan_id, psk,
+						identity, radius_cui);
+		if (res == HOSTAPD_ACL_ACCEPT ||
+		    res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+			return res;
+		if (res == HOSTAPD_ACL_REJECT)
+			return HOSTAPD_ACL_REJECT;
+
+		query = hapd->acl_queries;
+		while (query) {
+			if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
+				/* pending query in RADIUS retransmit queue;
+				 * do not generate a new one */
+				if (identity) {
+					os_free(*identity);
+					*identity = NULL;
+				}
+				if (radius_cui) {
+					os_free(*radius_cui);
+					*radius_cui = NULL;
+				}
+				return HOSTAPD_ACL_PENDING;
+			}
+			query = query->next;
+		}
+
+		if (!hapd->conf->radius->auth_server)
+			return HOSTAPD_ACL_REJECT;
+
+		/* No entry in the cache - query external RADIUS server */
+		query = os_zalloc(sizeof(*query));
+		if (query == NULL) {
+			wpa_printf(MSG_ERROR, "malloc for query data failed");
+			return HOSTAPD_ACL_REJECT;
+		}
+		os_get_reltime(&query->timestamp);
+		os_memcpy(query->addr, addr, ETH_ALEN);
+		if (hostapd_radius_acl_query(hapd, addr, query)) {
+			wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
+				   "for ACL query.");
+			hostapd_acl_query_free(query);
+			return HOSTAPD_ACL_REJECT;
+		}
+
+		query->auth_msg = os_malloc(len);
+		if (query->auth_msg == NULL) {
+			wpa_printf(MSG_ERROR, "Failed to allocate memory for "
+				   "auth frame.");
+			hostapd_acl_query_free(query);
+			return HOSTAPD_ACL_REJECT;
+		}
+		os_memcpy(query->auth_msg, msg, len);
+		query->auth_msg_len = len;
+		query->next = hapd->acl_queries;
+		hapd->acl_queries = query;
+
+		/* Queued data will be processed in hostapd_acl_recv_radius()
+		 * when RADIUS server replies to the sent Access-Request. */
+		return HOSTAPD_ACL_PENDING;
+#endif /* CONFIG_NO_RADIUS */
+	}
+
+	return HOSTAPD_ACL_REJECT;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void hostapd_acl_expire_cache(struct hostapd_data *hapd,
+				     struct os_reltime *now)
+{
+	struct hostapd_cached_radius_acl *prev, *entry, *tmp;
+
+	prev = NULL;
+	entry = hapd->acl_cache;
+
+	while (entry) {
+		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)
+				prev->next = entry->next;
+			else
+				hapd->acl_cache = entry->next;
+			hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
+			tmp = entry;
+			entry = entry->next;
+			hostapd_acl_cache_free_entry(tmp);
+			continue;
+		}
+
+		prev = entry;
+		entry = entry->next;
+	}
+}
+
+
+static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
+				       struct os_reltime *now)
+{
+	struct hostapd_acl_query_data *prev, *entry, *tmp;
+
+	prev = NULL;
+	entry = hapd->acl_queries;
+
+	while (entry) {
+		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)
+				prev->next = entry->next;
+			else
+				hapd->acl_queries = entry->next;
+
+			tmp = entry;
+			entry = entry->next;
+			hostapd_acl_query_free(tmp);
+			continue;
+		}
+
+		prev = entry;
+		entry = entry->next;
+	}
+}
+
+
+/**
+ * hostapd_acl_expire - ACL cache expiration callback
+ * @hapd: struct hostapd_data *
+ */
+void hostapd_acl_expire(struct hostapd_data *hapd)
+{
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+	hostapd_acl_expire_cache(hapd, &now);
+	hostapd_acl_expire_queries(hapd, &now);
+}
+
+
+static void decode_tunnel_passwords(struct hostapd_data *hapd,
+				    const u8 *shared_secret,
+				    size_t shared_secret_len,
+				    struct radius_msg *msg,
+				    struct radius_msg *req,
+				    struct hostapd_cached_radius_acl *cache)
+{
+	int passphraselen;
+	char *passphrase, *strpassphrase;
+	size_t i;
+	struct hostapd_sta_wpa_psk_short *psk;
+
+	/*
+	 * Decode all tunnel passwords as PSK and save them into a linked list.
+	 */
+	for (i = 0; ; i++) {
+		passphrase = radius_msg_get_tunnel_password(
+			msg, &passphraselen, shared_secret, shared_secret_len,
+			req, i);
+		/*
+		 * Passphrase is NULL iff there is no i-th Tunnel-Password
+		 * attribute in msg.
+		 */
+		if (passphrase == NULL)
+			break;
+		/*
+		 * passphrase does not contain the NULL termination.
+		 * Add it here as pbkdf2_sha1() requires it.
+		 */
+		strpassphrase = os_zalloc(passphraselen + 1);
+		psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
+		if (strpassphrase && psk) {
+			os_memcpy(strpassphrase, passphrase, passphraselen);
+			pbkdf2_sha1(strpassphrase,
+				    hapd->conf->ssid.ssid,
+				    hapd->conf->ssid.ssid_len, 4096,
+				    psk->psk, PMK_LEN);
+			psk->next = cache->psk;
+			cache->psk = psk;
+			psk = NULL;
+		}
+		os_free(strpassphrase);
+		os_free(psk);
+		os_free(passphrase);
+	}
+}
+
+
+/**
+ * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and
+ * was processed here) or RADIUS_RX_UNKNOWN if not.
+ */
+static RadiusRxResult
+hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
+			const u8 *shared_secret, size_t shared_secret_len,
+			void *data)
+{
+	struct hostapd_data *hapd = data;
+	struct hostapd_acl_query_data *query, *prev;
+	struct hostapd_cached_radius_acl *cache;
+	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+
+	query = hapd->acl_queries;
+	prev = NULL;
+	while (query) {
+		if (query->radius_id == hdr->identifier)
+			break;
+		prev = query;
+		query = query->next;
+	}
+	if (query == NULL)
+		return RADIUS_RX_UNKNOWN;
+
+	wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS "
+		   "message (id=%d)", query->radius_id);
+
+	if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
+		wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have "
+			   "correct authenticator - dropped\n");
+		return RADIUS_RX_INVALID_AUTHENTICATOR;
+	}
+
+	if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+	    hdr->code != RADIUS_CODE_ACCESS_REJECT) {
+		wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
+			   "query", hdr->code);
+		return RADIUS_RX_UNKNOWN;
+	}
+
+	/* Insert Accept/Reject info into ACL cache */
+	cache = os_zalloc(sizeof(*cache));
+	if (cache == NULL) {
+		wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
+		goto done;
+	}
+	os_get_reltime(&cache->timestamp);
+	os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
+	if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
+		u8 *buf;
+		size_t len;
+
+		if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
+					      &cache->session_timeout) == 0)
+			cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
+		else
+			cache->accepted = HOSTAPD_ACL_ACCEPT;
+
+		if (radius_msg_get_attr_int32(
+			    msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
+			    &cache->acct_interim_interval) == 0 &&
+		    cache->acct_interim_interval < 60) {
+			wpa_printf(MSG_DEBUG, "Ignored too small "
+				   "Acct-Interim-Interval %d for STA " MACSTR,
+				   cache->acct_interim_interval,
+				   MAC2STR(query->addr));
+			cache->acct_interim_interval = 0;
+		}
+
+		cache->vlan_id = radius_msg_get_vlanid(msg);
+
+		decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
+					msg, req, cache);
+
+		if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
+					    &buf, &len, NULL) == 0) {
+			cache->identity = os_zalloc(len + 1);
+			if (cache->identity)
+				os_memcpy(cache->identity, buf, len);
+		}
+		if (radius_msg_get_attr_ptr(
+			    msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+			    &buf, &len, NULL) == 0) {
+			cache->radius_cui = os_zalloc(len + 1);
+			if (cache->radius_cui)
+				os_memcpy(cache->radius_cui, buf, len);
+		}
+
+		if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED &&
+		    !cache->psk)
+			cache->accepted = HOSTAPD_ACL_REJECT;
+
+		if (cache->vlan_id &&
+		    !hostapd_vlan_id_valid(hapd->conf->vlan, cache->vlan_id)) {
+			hostapd_logger(hapd, query->addr,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO,
+				       "Invalid VLAN ID %d received from RADIUS server",
+				       cache->vlan_id);
+			cache->vlan_id = 0;
+		}
+		if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
+		    !cache->vlan_id)
+			cache->accepted = HOSTAPD_ACL_REJECT;
+	} else
+		cache->accepted = HOSTAPD_ACL_REJECT;
+	cache->next = hapd->acl_cache;
+	hapd->acl_cache = cache;
+
+#ifdef CONFIG_DRIVER_RADIUS_ACL
+	hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted,
+					cache->session_timeout);
+#else /* CONFIG_DRIVER_RADIUS_ACL */
+#ifdef NEED_AP_MLME
+	/* Re-send original authentication frame for 802.11 processing */
+	wpa_printf(MSG_DEBUG, "Re-sending authentication frame after "
+		   "successful RADIUS ACL query");
+	ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_DRIVER_RADIUS_ACL */
+
+ done:
+	if (prev == NULL)
+		hapd->acl_queries = query->next;
+	else
+		prev->next = query->next;
+
+	hostapd_acl_query_free(query);
+
+	return RADIUS_RX_PROCESSED;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+/**
+ * hostapd_acl_init: Initialize IEEE 802.11 ACL
+ * @hapd: hostapd BSS data
+ * Returns: 0 on success, -1 on failure
+ */
+int hostapd_acl_init(struct hostapd_data *hapd)
+{
+#ifndef CONFIG_NO_RADIUS
+	if (radius_client_register(hapd->radius, RADIUS_AUTH,
+				   hostapd_acl_recv_radius, hapd))
+		return -1;
+#endif /* CONFIG_NO_RADIUS */
+
+	return 0;
+}
+
+
+/**
+ * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL
+ * @hapd: hostapd BSS data
+ */
+void hostapd_acl_deinit(struct hostapd_data *hapd)
+{
+	struct hostapd_acl_query_data *query, *prev;
+
+#ifndef CONFIG_NO_RADIUS
+	hostapd_acl_cache_free(hapd->acl_cache);
+#endif /* CONFIG_NO_RADIUS */
+
+	query = hapd->acl_queries;
+	while (query) {
+		prev = query;
+		query = query->next;
+		hostapd_acl_query_free(prev);
+	}
+}
+
+
+void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk)
+{
+	while (psk) {
+		struct hostapd_sta_wpa_psk_short *prev = psk;
+		psk = psk->next;
+		os_free(prev);
+	}
+}
diff --git a/hostap/src/ap/ieee802_11_auth.h b/hostap/src/ap/ieee802_11_auth.h
new file mode 100644
index 0000000..b66f244
--- /dev/null
+++ b/hostap/src/ap/ieee802_11_auth.h
@@ -0,0 +1,29 @@
+/*
+ * hostapd / IEEE 802.11 authentication (ACL)
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_11_AUTH_H
+#define IEEE802_11_AUTH_H
+
+enum {
+	HOSTAPD_ACL_REJECT = 0,
+	HOSTAPD_ACL_ACCEPT = 1,
+	HOSTAPD_ACL_PENDING = 2,
+	HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
+};
+
+int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+			    const u8 *msg, size_t len, u32 *session_timeout,
+			    u32 *acct_interim_interval, int *vlan_id,
+			    struct hostapd_sta_wpa_psk_short **psk,
+			    char **identity, char **radius_cui);
+int hostapd_acl_init(struct hostapd_data *hapd);
+void hostapd_acl_deinit(struct hostapd_data *hapd);
+void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
+void hostapd_acl_expire(struct hostapd_data *hapd);
+
+#endif /* IEEE802_11_AUTH_H */
diff --git a/hostap/src/ap/ieee802_11_ht.c b/hostap/src/ap/ieee802_11_ht.c
new file mode 100644
index 0000000..11fde2a
--- /dev/null
+++ b/hostap/src/ap/ieee802_11_ht.c
@@ -0,0 +1,490 @@
+/*
+ * hostapd / IEEE 802.11n HT
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2008, Intel Corporation
+ *
+ * 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 "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)
+{
+	struct ieee80211_ht_capabilities *cap;
+	u8 *pos = eid;
+
+	if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode ||
+	    hapd->conf->disable_11n)
+		return eid;
+
+	*pos++ = WLAN_EID_HT_CAP;
+	*pos++ = sizeof(*cap);
+
+	cap = (struct ieee80211_ht_capabilities *) pos;
+	os_memset(cap, 0, sizeof(*cap));
+	cap->ht_capabilities_info = host_to_le16(hapd->iconf->ht_capab);
+	cap->a_mpdu_params = hapd->iface->current_mode->a_mpdu_params;
+	os_memcpy(cap->supported_mcs_set, hapd->iface->current_mode->mcs_set,
+		  16);
+
+	/* TODO: ht_extended_capabilities (now fully disabled) */
+	/* TODO: tx_bf_capability_info (now fully disabled) */
+	/* TODO: asel_capabilities (now fully disabled) */
+
+ 	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;
+}
+
+
+u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_ht_operation *oper;
+	u8 *pos = eid;
+
+	if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
+		return eid;
+
+	*pos++ = WLAN_EID_HT_OPERATION;
+	*pos++ = sizeof(*oper);
+
+	oper = (struct ieee80211_ht_operation *) pos;
+	os_memset(oper, 0, sizeof(*oper));
+
+	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_STA_CHNL_WIDTH;
+	if (hapd->iconf->secondary_channel == -1)
+		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
+			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+
+	pos += sizeof(*oper);
+
+	return pos;
+}
+
+
+/*
+op_mode
+Set to 0 (HT pure) under the followign conditions
+	- all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or
+	- all STAs in the BSS are 20 MHz HT in 20 MHz BSS
+Set to 1 (HT non-member protection) if there may be non-HT STAs
+	in both the primary and the secondary channel
+Set to 2 if only HT STAs are associated in BSS,
+	however and at least one 20 MHz HT STA is associated
+Set to 3 (HT mixed mode) when one or more non-HT STAs are associated
+*/
+int hostapd_ht_operation_update(struct hostapd_iface *iface)
+{
+	u16 cur_op_mode, new_op_mode;
+	int op_mode_changes = 0;
+
+	if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X",
+		   __func__, iface->ht_op_mode);
+
+	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_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
+		op_mode_changes++;
+	} else if ((iface->ht_op_mode &
+		    HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) &&
+		   iface->num_sta_ht_no_gf == 0) {
+		iface->ht_op_mode &= ~HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
+		op_mode_changes++;
+	}
+
+	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_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
+		op_mode_changes++;
+	} else if ((iface->ht_op_mode &
+		    HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
+		   (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) {
+		iface->ht_op_mode &= ~HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
+		op_mode_changes++;
+	}
+
+	if (iface->num_sta_no_ht)
+		new_op_mode = HT_PROT_NON_HT_MIXED;
+	else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz)
+		new_op_mode = HT_PROT_20MHZ_PROTECTION;
+	else if (iface->olbc_ht)
+		new_op_mode = HT_PROT_NONMEMBER_PROTECTION;
+	else
+		new_op_mode = HT_PROT_NO_PROTECTION;
+
+	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_OPER_OP_MODE_HT_PROT_MASK;
+		iface->ht_op_mode |= new_op_mode;
+		op_mode_changes++;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d",
+		   __func__, iface->ht_op_mode, op_mode_changes);
+
+	return op_mode_changes;
+}
+
+
+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_ht40_allowed = 1;
+	int i;
+	const u8 *start = (const u8 *) mgmt;
+	const u8 *data = start + IEEE80211_HDRLEN + 2;
+
+	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 + 2 + sizeof(*bc_ie))
+		return;
+
+	bc_ie = (struct ieee80211_2040_bss_coex_ie *) data;
+	if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE ||
+	    bc_ie->length < 1) {
+		wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report",
+			   bc_ie->element_id, bc_ie->length);
+		return;
+	}
+	if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length)
+		return;
+	data += 2 + bc_ie->length;
+
+	wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x",
+		   bc_ie->coex_param);
+	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_ht40_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_ht40_allowed = 0;
+	}
+
+	if (start + len - data >= 3 &&
+	    data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
+		u8 ielen = data[1];
+
+		if (ielen > start + len - data - 2)
+			return;
+		ic_report = (struct ieee80211_2040_intol_chan_report *) data;
+		wpa_printf(MSG_DEBUG,
+			   "20/40 BSS Intolerant Channel Report: Operating Class %u",
+			   ic_report->op_class);
+
+		/* Go through the channel report to find any BSS there in the
+		 * affected channel range */
+		for (i = 0; i < ielen - 1; i++) {
+			u8 chan = ic_report->variable[i];
+
+			if (is_40_allowed(iface, chan))
+				continue;
+			hostapd_logger(hapd, mgmt->sa,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "20_40_INTOLERANT channel %d reported",
+				       chan);
+			is_ht40_allowed = 0;
+		}
+	}
+	wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
+		   is_ht40_allowed, iface->num_sta_ht40_intolerant);
+
+	if (!is_ht40_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 &&
+		    iface->conf->obss_interval) {
+			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);
+			wpa_printf(MSG_DEBUG,
+				   "Reschedule HT 20/40 timeout to occur in %u seconds",
+				   delay_time);
+		}
+	}
+}
+
+
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *ht_capab)
+{
+	/*
+	 * Disable HT caps for STAs associated to no-HT BSSes, or for stations
+	 * that did not specify a valid WMM IE in the (Re)Association Request
+	 * frame.
+	 */
+	if (!ht_capab ||
+	    !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) {
+		sta->flags &= ~WLAN_STA_HT;
+		os_free(sta->ht_capabilities);
+		sta->ht_capabilities = NULL;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (sta->ht_capabilities == NULL) {
+		sta->ht_capabilities =
+			os_zalloc(sizeof(struct ieee80211_ht_capabilities));
+		if (sta->ht_capabilities == NULL)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_HT;
+	os_memcpy(sta->ht_capabilities, ht_capab,
+		  sizeof(struct ieee80211_ht_capabilities));
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+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;
+
+	ht_capab = le_to_host16(sta->ht_capabilities->ht_capabilities_info);
+	wpa_printf(MSG_DEBUG, "HT: STA " MACSTR " HT Capabilities Info: "
+		   "0x%04x", MAC2STR(sta->addr), ht_capab);
+	if ((ht_capab & HT_CAP_INFO_GREEN_FIELD) == 0) {
+		if (!sta->no_ht_gf_set) {
+			sta->no_ht_gf_set = 1;
+			hapd->iface->num_sta_ht_no_gf++;
+		}
+		wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no greenfield, num "
+			   "of non-gf stations %d",
+			   __func__, MAC2STR(sta->addr),
+			   hapd->iface->num_sta_ht_no_gf);
+	}
+	if ((ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) {
+		if (!sta->ht_20mhz_set) {
+			sta->ht_20mhz_set = 1;
+			hapd->iface->num_sta_ht_20mhz++;
+		}
+		wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, num of "
+			   "20MHz HT STAs %d",
+			   __func__, MAC2STR(sta->addr),
+			   hapd->iface->num_sta_ht_20mhz);
+	}
+
+	if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT)
+		ht40_intolerant_add(hapd->iface, sta);
+}
+
+
+static void update_sta_no_ht(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if (!sta->no_ht_set) {
+		sta->no_ht_set = 1;
+		hapd->iface->num_sta_no_ht++;
+	}
+	if (hapd->iconf->ieee80211n) {
+		wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no HT, num of "
+			   "non-HT stations %d",
+			   __func__, MAC2STR(sta->addr),
+			   hapd->iface->num_sta_no_ht);
+	}
+}
+
+
+void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities)
+		update_sta_ht(hapd, sta);
+	else
+		update_sta_no_ht(hapd, sta);
+
+	if (hostapd_ht_operation_update(hapd->iface) > 0)
+		ieee802_11_set_beacons(hapd->iface);
+}
+
+
+void hostapd_get_ht_capab(struct hostapd_data *hapd,
+			  struct ieee80211_ht_capabilities *ht_cap,
+			  struct ieee80211_ht_capabilities *neg_ht_cap)
+{
+	u16 cap;
+
+	if (ht_cap == NULL)
+		return;
+	os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap));
+	cap = le_to_host16(neg_ht_cap->ht_capabilities_info);
+
+	/*
+	 * Mask out HT features we don't support, but don't overwrite
+	 * non-symmetric features like STBC and SMPS. Just because
+	 * we're not in dynamic SMPS mode the STA might still be.
+	 */
+	cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK |
+		HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK);
+
+	/*
+	 * STBC needs to be handled specially
+	 * 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 (!(hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK))
+		cap &= ~HT_CAP_INFO_TX_STBC;
+	if (!(hapd->iconf->ht_capab & HT_CAP_INFO_TX_STBC))
+		cap &= ~HT_CAP_INFO_RX_STBC_MASK;
+
+	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/hostap/src/ap/ieee802_11_shared.c b/hostap/src/ap/ieee802_11_shared.c
new file mode 100644
index 0000000..d462ac8
--- /dev/null
+++ b/hostap/src/ap/ieee802_11_shared.c
@@ -0,0 +1,508 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2012, 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 "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "ieee802_11.h"
+
+
+#ifdef CONFIG_IEEE80211W
+
+u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+				     struct sta_info *sta, u8 *eid)
+{
+	u8 *pos = eid;
+	u32 timeout, tu;
+	struct os_reltime now, passed;
+
+	*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+	*pos++ = 5;
+	*pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
+	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;
+	else
+		timeout = 0;
+	if (timeout < hapd->conf->assoc_sa_query_max_timeout)
+		timeout++; /* add some extra time for local timers */
+	WPA_PUT_LE32(pos, timeout);
+	pos += 4;
+
+	return pos;
+}
+
+
+/* MLME-SAQuery.request */
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+				  const u8 *addr, const u8 *trans_id)
+{
+	struct ieee80211_mgmt mgmt;
+	u8 *end;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
+		   MACSTR, MAC2STR(addr));
+	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+		    trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+	os_memset(&mgmt, 0, sizeof(mgmt));
+	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_SA_QUERY;
+	mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+	os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
+		  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)
+		wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed");
+}
+
+
+static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
+					  const u8 *sa, const u8 *trans_id)
+{
+	struct sta_info *sta;
+	struct ieee80211_mgmt resp;
+	u8 *end;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
+		   MACSTR, MAC2STR(sa));
+	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+		    trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+	sta = ap_get_sta(hapd, sa);
+	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
+			   "from unassociated STA " MACSTR, MAC2STR(sa));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
+		   MACSTR, MAC2STR(sa));
+
+	os_memset(&resp, 0, sizeof(resp));
+	resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					  WLAN_FC_STYPE_ACTION);
+	os_memcpy(resp.da, sa, ETH_ALEN);
+	os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN);
+	resp.u.action.category = WLAN_ACTION_SA_QUERY;
+	resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
+	os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id,
+		  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)
+		wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed");
+}
+
+
+void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa,
+				const u8 action_type, const u8 *trans_id)
+{
+	struct sta_info *sta;
+	int i;
+
+	if (action_type == WLAN_SA_QUERY_REQUEST) {
+		ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
+		return;
+	}
+
+	if (action_type != WLAN_SA_QUERY_RESPONSE) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
+			   "Action %d", action_type);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
+		   MACSTR, MAC2STR(sa));
+	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+		    trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+	/* MLME-SAQuery.confirm */
+
+	sta = ap_get_sta(hapd, sa);
+	if (sta == NULL || sta->sa_query_trans_id == NULL) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
+			   "pending SA Query request found");
+		return;
+	}
+
+	for (i = 0; i < sta->sa_query_count; i++) {
+		if (os_memcmp(sta->sa_query_trans_id +
+			      i * WLAN_SA_QUERY_TR_ID_LEN,
+			      trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0)
+			break;
+	}
+
+	if (i >= sta->sa_query_count) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
+			   "transaction identifier found");
+		return;
+	}
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "Reply to pending SA Query received");
+	ap_sta_stop_sa_query(hapd, sta);
+}
+
+#endif /* CONFIG_IEEE80211W */
+
+
+static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
+{
+	*pos = 0x00;
+
+	switch (idx) {
+	case 0: /* Bits 0-7 */
+		if (hapd->iconf->obss_interval)
+			*pos |= 0x01; /* Bit 0 - Coexistence management */
+		break;
+	case 1: /* Bits 8-15 */
+		if (hapd->conf->proxy_arp)
+			*pos |= 0x10; /* Bit 12 - Proxy ARP */
+		break;
+	case 2: /* Bits 16-23 */
+		if (hapd->conf->wnm_sleep_mode)
+			*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+		if (hapd->conf->bss_transition)
+			*pos |= 0x08; /* Bit 19 - BSS Transition */
+		break;
+	case 3: /* Bits 24-31 */
+#ifdef CONFIG_WNM
+		*pos |= 0x02; /* Bit 25 - SSID List */
+#endif /* CONFIG_WNM */
+		if (hapd->conf->time_advertisement == 2)
+			*pos |= 0x08; /* Bit 27 - UTC TSF Offset */
+		if (hapd->conf->interworking)
+			*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) {
+			/* Bit 39 - TDLS Channel Switching Prohibited */
+			*pos |= 0x80;
+		}
+		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)
+			*pos |= 0x01; /* Bit 48 - UTF-8 SSID */
+		break;
+	}
+}
+
+
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	u8 len = 0, i;
+
+	if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH))
+		len = 5;
+	if (len < 4 && hapd->conf->interworking)
+		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)
+		return eid;
+
+	*pos++ = WLAN_EID_EXT_CAPAB;
+	*pos++ = len;
+	for (i = 0; i < len; i++, pos++) {
+		hostapd_ext_capab_byte(hapd, pos, i);
+
+		if (i < hapd->iface->extended_capa_len) {
+			*pos &= ~hapd->iface->extended_capa_mask[i];
+			*pos |= hapd->iface->extended_capa[i];
+		}
+	}
+
+	while (len > 0 && eid[1 + len] == 0) {
+		len--;
+		eid[1] = len;
+	}
+	if (len == 0)
+		return eid;
+
+	return eid + 2 + len;
+}
+
+
+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;
+#ifdef CONFIG_INTERWORKING
+	u8 *len;
+
+	if (!hapd->conf->interworking)
+		return eid;
+
+	*pos++ = WLAN_EID_INTERWORKING;
+	len = pos++;
+
+	*pos = hapd->conf->access_network_type;
+	if (hapd->conf->internet)
+		*pos |= INTERWORKING_ANO_INTERNET;
+	if (hapd->conf->asra)
+		*pos |= INTERWORKING_ANO_ASRA;
+	if (hapd->conf->esr)
+		*pos |= INTERWORKING_ANO_ESR;
+	if (hapd->conf->uesa)
+		*pos |= INTERWORKING_ANO_UESA;
+	pos++;
+
+	if (hapd->conf->venue_info_set) {
+		*pos++ = hapd->conf->venue_group;
+		*pos++ = hapd->conf->venue_type;
+	}
+
+	if (!is_zero_ether_addr(hapd->conf->hessid)) {
+		os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
+		pos += ETH_ALEN;
+	}
+
+	*len = pos - len - 1;
+#endif /* CONFIG_INTERWORKING */
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+
+	/* TODO: Separate configuration for ANQP? */
+	if (!hapd->conf->interworking)
+		return eid;
+
+	*pos++ = WLAN_EID_ADV_PROTO;
+	*pos++ = 2;
+	*pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */
+	*pos++ = ACCESS_NETWORK_QUERY_PROTOCOL;
+#endif /* CONFIG_INTERWORKING */
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+	u8 *len;
+	unsigned int i, count;
+
+	if (!hapd->conf->interworking ||
+	    hapd->conf->roaming_consortium == NULL ||
+	    hapd->conf->roaming_consortium_count == 0)
+		return eid;
+
+	*pos++ = WLAN_EID_ROAMING_CONSORTIUM;
+	len = pos++;
+
+	/* Number of ANQP OIs (in addition to the max 3 listed here) */
+	if (hapd->conf->roaming_consortium_count > 3 + 255)
+		*pos++ = 255;
+	else if (hapd->conf->roaming_consortium_count > 3)
+		*pos++ = hapd->conf->roaming_consortium_count - 3;
+	else
+		*pos++ = 0;
+
+	/* OU #1 and #2 Lengths */
+	*pos = hapd->conf->roaming_consortium[0].len;
+	if (hapd->conf->roaming_consortium_count > 1)
+		*pos |= hapd->conf->roaming_consortium[1].len << 4;
+	pos++;
+
+	if (hapd->conf->roaming_consortium_count > 3)
+		count = 3;
+	else
+		count = hapd->conf->roaming_consortium_count;
+
+	for (i = 0; i < count; i++) {
+		os_memcpy(pos, hapd->conf->roaming_consortium[i].oi,
+			  hapd->conf->roaming_consortium[i].len);
+		pos += hapd->conf->roaming_consortium[i].len;
+	}
+
+	*len = pos - len - 1;
+#endif /* CONFIG_INTERWORKING */
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid)
+{
+	if (hapd->conf->time_advertisement != 2)
+		return eid;
+
+	if (hapd->time_adv == NULL &&
+	    hostapd_update_time_adv(hapd) < 0)
+		return eid;
+
+	if (hapd->time_adv == NULL)
+		return eid;
+
+	os_memcpy(eid, wpabuf_head(hapd->time_adv),
+		  wpabuf_len(hapd->time_adv));
+	eid += wpabuf_len(hapd->time_adv);
+
+	return eid;
+}
+
+
+u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid)
+{
+	size_t len;
+
+	if (hapd->conf->time_advertisement != 2)
+		return eid;
+
+	len = os_strlen(hapd->conf->time_zone);
+
+	*eid++ = WLAN_EID_TIME_ZONE;
+	*eid++ = len;
+	os_memcpy(eid, hapd->conf->time_zone, len);
+	eid += len;
+
+	return eid;
+}
+
+
+int hostapd_update_time_adv(struct hostapd_data *hapd)
+{
+	const int elen = 2 + 1 + 10 + 5 + 1;
+	struct os_time t;
+	struct os_tm tm;
+	u8 *pos;
+
+	if (hapd->conf->time_advertisement != 2)
+		return 0;
+
+	if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0)
+		return -1;
+
+	if (!hapd->time_adv) {
+		hapd->time_adv = wpabuf_alloc(elen);
+		if (hapd->time_adv == NULL)
+			return -1;
+		pos = wpabuf_put(hapd->time_adv, elen);
+	} else
+		pos = wpabuf_mhead_u8(hapd->time_adv);
+
+	*pos++ = WLAN_EID_TIME_ADVERTISEMENT;
+	*pos++ = 1 + 10 + 5 + 1;
+
+	*pos++ = 2; /* UTC time at which the TSF timer is 0 */
+
+	/* Time Value at TSF 0 */
+	/* FIX: need to calculate this based on the current TSF value */
+	WPA_PUT_LE16(pos, tm.year); /* Year */
+	pos += 2;
+	*pos++ = tm.month; /* Month */
+	*pos++ = tm.day; /* Day of month */
+	*pos++ = tm.hour; /* Hours */
+	*pos++ = tm.min; /* Minutes */
+	*pos++ = tm.sec; /* Seconds */
+	WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */
+	pos += 2;
+	*pos++ = 0; /* Reserved */
+
+	/* Time Error */
+	/* TODO: fill in an estimate on the error */
+	*pos++ = 0;
+	*pos++ = 0;
+	*pos++ = 0;
+	*pos++ = 0;
+	*pos++ = 0;
+
+	*pos++ = hapd->time_update_counter++;
+
+	return 0;
+}
+
+
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+
+#ifdef CONFIG_WNM
+	if (hapd->conf->ap_max_inactivity > 0) {
+		unsigned int val;
+		*pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
+		*pos++ = 3;
+		val = hapd->conf->ap_max_inactivity;
+		if (val > 68000)
+			val = 68000;
+		val *= 1000;
+		val /= 1024;
+		if (val == 0)
+			val = 1;
+		if (val > 65535)
+			val = 65535;
+		WPA_PUT_LE16(pos, val);
+		pos += 2;
+		*pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
+	}
+#endif /* CONFIG_WNM */
+
+	return pos;
+}
diff --git a/hostap/src/ap/ieee802_11_vht.c b/hostap/src/ap/ieee802_11_vht.c
new file mode 100644
index 0000000..5bf1b5d
--- /dev/null
+++ b/hostap/src/ap/ieee802_11_vht.c
@@ -0,0 +1,296 @@
+/*
+ * hostapd / IEEE 802.11ac VHT
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of BSD license
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+
+
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_vht_capabilities *cap;
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+	u8 *pos = eid;
+
+	if (!mode)
+		return eid;
+
+	if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht &&
+	    mode->vht_capab == 0 && hapd->iface->hw_features) {
+		int i;
+
+		for (i = 0; i < hapd->iface->num_hw_features; i++) {
+			if (hapd->iface->hw_features[i].mode ==
+			    HOSTAPD_MODE_IEEE80211A) {
+				mode = &hapd->iface->hw_features[i];
+				break;
+			}
+		}
+	}
+
+	*pos++ = WLAN_EID_VHT_CAP;
+	*pos++ = sizeof(*cap);
+
+	cap = (struct ieee80211_vht_capabilities *) pos;
+	os_memset(cap, 0, sizeof(*cap));
+	cap->vht_capabilities_info = host_to_le32(
+		hapd->iface->conf->vht_capab);
+
+	/* Supported MCS set comes from hw */
+	os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
+
+	pos += sizeof(*cap);
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_vht_operation *oper;
+	u8 *pos = eid;
+
+	*pos++ = WLAN_EID_VHT_OPERATION;
+	*pos++ = sizeof(*oper);
+
+	oper = (struct ieee80211_vht_operation *) pos;
+	os_memset(oper, 0, sizeof(*oper));
+
+	/*
+	 * center freq = 5 GHz + (5 * index)
+	 * So index 42 gives center freq 5.210 GHz
+	 * which is channel 42 in 5G band
+	 */
+	oper->vht_op_info_chan_center_freq_seg0_idx =
+		hapd->iconf->vht_oper_centr_freq_seg0_idx;
+	oper->vht_op_info_chan_center_freq_seg1_idx =
+		hapd->iconf->vht_oper_centr_freq_seg1_idx;
+
+	oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth;
+
+	/* VHT Basic MCS set comes from hw */
+	/* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */
+	oper->vht_basic_mcs_set = host_to_le16(0xfffc);
+	pos += sizeof(*oper);
+
+	return pos;
+}
+
+
+static int check_valid_vht_mcs(struct hostapd_hw_modes *mode,
+			       const u8 *sta_vht_capab)
+{
+	const struct ieee80211_vht_capabilities *vht_cap;
+	struct ieee80211_vht_capabilities ap_vht_cap;
+	u16 sta_rx_mcs_set, ap_tx_mcs_set;
+	int i;
+
+	if (!mode)
+		return 1;
+
+	/*
+	 * Disable VHT caps for STAs for which there is not even a single
+	 * allowed MCS in any supported number of streams, i.e., STA is
+	 * advertising 3 (not supported) as VHT MCS rates for all supported
+	 * stream cases.
+	 */
+	os_memcpy(&ap_vht_cap.vht_supported_mcs_set, mode->vht_mcs_set,
+		  sizeof(ap_vht_cap.vht_supported_mcs_set));
+	vht_cap = (const struct ieee80211_vht_capabilities *) sta_vht_capab;
+
+	/* AP Tx MCS map vs. STA Rx MCS map */
+	sta_rx_mcs_set = le_to_host16(vht_cap->vht_supported_mcs_set.rx_map);
+	ap_tx_mcs_set = le_to_host16(ap_vht_cap.vht_supported_mcs_set.tx_map);
+
+	for (i = 0; i < VHT_RX_NSS_MAX_STREAMS; i++) {
+		if ((ap_tx_mcs_set & (0x3 << (i * 2))) == 3)
+			continue;
+
+		if ((sta_rx_mcs_set & (0x3 << (i * 2))) == 3)
+			continue;
+
+		return 1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "No matching VHT MCS found between AP TX and STA RX");
+	return 0;
+}
+
+
+u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *vht_capab)
+{
+	/* Disable VHT caps for STAs associated to no-VHT BSSes. */
+	if (!vht_capab ||
+	    hapd->conf->disable_11ac ||
+	    !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) {
+		sta->flags &= ~WLAN_STA_VHT;
+		os_free(sta->vht_capabilities);
+		sta->vht_capabilities = NULL;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (sta->vht_capabilities == NULL) {
+		sta->vht_capabilities =
+			os_zalloc(sizeof(struct ieee80211_vht_capabilities));
+		if (sta->vht_capabilities == NULL)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_VHT;
+	os_memcpy(sta->vht_capabilities, vht_capab,
+		  sizeof(struct ieee80211_vht_capabilities));
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+			const u8 *ie, size_t len)
+{
+	const u8 *vht_capab;
+	unsigned int vht_capab_len;
+
+	if (!ie || len < 5 + 2 + sizeof(struct ieee80211_vht_capabilities) ||
+	    hapd->conf->disable_11ac)
+		goto no_capab;
+
+	/* The VHT Capabilities element embedded in vendor VHT */
+	vht_capab = ie + 5;
+	if (vht_capab[0] != WLAN_EID_VHT_CAP)
+		goto no_capab;
+	vht_capab_len = vht_capab[1];
+	if (vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
+	    (int) vht_capab_len > ie + len - vht_capab - 2)
+		goto no_capab;
+	vht_capab += 2;
+
+	if (sta->vht_capabilities == NULL) {
+		sta->vht_capabilities =
+			os_zalloc(sizeof(struct ieee80211_vht_capabilities));
+		if (sta->vht_capabilities == NULL)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_VHT | WLAN_STA_VENDOR_VHT;
+	os_memcpy(sta->vht_capabilities, vht_capab,
+		  sizeof(struct ieee80211_vht_capabilities));
+	return WLAN_STATUS_SUCCESS;
+
+no_capab:
+	sta->flags &= ~WLAN_STA_VENDOR_VHT;
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+
+	if (!hapd->iface->current_mode)
+		return eid;
+
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = (5 +		/* The Vendor OUI, type and subtype */
+		  2 + sizeof(struct ieee80211_vht_capabilities) +
+		  2 + sizeof(struct ieee80211_vht_operation));
+
+	WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE);
+	pos += 4;
+	*pos++ = VENDOR_VHT_SUBTYPE;
+	pos = hostapd_eid_vht_capabilities(hapd, pos);
+	pos = hostapd_eid_vht_operation(hapd, pos);
+
+	return pos;
+}
+
+
+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));
+
+	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/hostap/src/ap/ieee802_1x.c b/hostap/src/ap/ieee802_1x.c
new file mode 100644
index 0000000..0f2d428
--- /dev/null
+++ b/hostap/src/ap/ieee802_1x.c
@@ -0,0 +1,2616 @@
+/*
+ * hostapd / IEEE 802.1X-2004 Authenticator
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/md5.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "eap_server/eap.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "accounting.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+#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,
+				int remediation);
+
+
+static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
+			    u8 type, const u8 *data, size_t datalen)
+{
+	u8 *buf;
+	struct ieee802_1x_hdr *xhdr;
+	size_t len;
+	int encrypt = 0;
+
+	len = sizeof(*xhdr) + datalen;
+	buf = os_zalloc(len);
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR, "malloc() failed for "
+			   "ieee802_1x_send(len=%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+	xhdr = (struct ieee802_1x_hdr *) buf;
+	xhdr->version = hapd->conf->eapol_version;
+	xhdr->type = type;
+	xhdr->length = host_to_be16(datalen);
+
+	if (datalen > 0 && data != NULL)
+		os_memcpy(xhdr + 1, data, datalen);
+
+	if (wpa_auth_pairwise_set(sta->wpa_sm))
+		encrypt = 1;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->ext_eapol_frame_io) {
+		size_t hex_len = 2 * len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex) {
+			wpa_snprintf_hex(hex, hex_len, buf, len);
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				"EAPOL-TX " MACSTR " %s",
+				MAC2STR(sta->addr), hex);
+			os_free(hex);
+		}
+	} else
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (sta->flags & WLAN_STA_PREAUTH) {
+		rsn_preauth_send(hapd, sta, buf, len);
+	} else {
+		hostapd_drv_hapd_send_eapol(
+			hapd, sta->addr, buf, len,
+			encrypt, hostapd_sta_flags_to_drv(sta->flags));
+	}
+
+	os_free(buf);
+}
+
+
+void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
+				   struct sta_info *sta, int authorized)
+{
+	int res;
+
+	if (sta->flags & WLAN_STA_PREAUTH)
+		return;
+
+	if (authorized) {
+		ap_sta_set_authorized(hapd, sta, 1);
+		res = hostapd_set_authorized(hapd, sta, 1);
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG, "authorizing port");
+	} else {
+		ap_sta_set_authorized(hapd, sta, 0);
+		res = hostapd_set_authorized(hapd, sta, 0);
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG, "unauthorizing port");
+	}
+
+	if (res && errno != ENOENT) {
+		wpa_printf(MSG_DEBUG, "Could not set station " MACSTR
+			   " flags for kernel driver (errno=%d).",
+			   MAC2STR(sta->addr), errno);
+	}
+
+	if (authorized) {
+		os_get_reltime(&sta->connected_time);
+		accounting_sta_start(hapd, sta);
+	}
+}
+
+
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_NO_RC4
+
+static void ieee802_1x_tx_key_one(struct hostapd_data *hapd,
+				  struct sta_info *sta,
+				  int idx, int broadcast,
+				  u8 *key_data, size_t key_len)
+{
+	u8 *buf, *ekey;
+	struct ieee802_1x_hdr *hdr;
+	struct ieee802_1x_eapol_key *key;
+	size_t len, ekey_len;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm == NULL)
+		return;
+
+	len = sizeof(*key) + key_len;
+	buf = os_zalloc(sizeof(*hdr) + len);
+	if (buf == NULL)
+		return;
+
+	hdr = (struct ieee802_1x_hdr *) buf;
+	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+	key->type = EAPOL_KEY_TYPE_RC4;
+	WPA_PUT_BE16(key->key_length, key_len);
+	wpa_get_ntp_timestamp(key->replay_counter);
+
+	if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) {
+		wpa_printf(MSG_ERROR, "Could not get random numbers");
+		os_free(buf);
+		return;
+	}
+
+	key->key_index = idx | (broadcast ? 0 : BIT(7));
+	if (hapd->conf->eapol_key_index_workaround) {
+		/* According to some information, WinXP Supplicant seems to
+		 * interpret bit7 as an indication whether the key is to be
+		 * activated, so make it possible to enable workaround that
+		 * sets this bit for all keys. */
+		key->key_index |= BIT(7);
+	}
+
+	/* Key is encrypted using "Key-IV + MSK[0..31]" as the RC4-key and
+	 * MSK[32..63] is used to sign the message. */
+	if (sm->eap_if->eapKeyData == NULL || sm->eap_if->eapKeyDataLen < 64) {
+		wpa_printf(MSG_ERROR, "No eapKeyData available for encrypting "
+			   "and signing EAPOL-Key");
+		os_free(buf);
+		return;
+	}
+	os_memcpy((u8 *) (key + 1), key_data, key_len);
+	ekey_len = sizeof(key->key_iv) + 32;
+	ekey = os_malloc(ekey_len);
+	if (ekey == NULL) {
+		wpa_printf(MSG_ERROR, "Could not encrypt key");
+		os_free(buf);
+		return;
+	}
+	os_memcpy(ekey, key->key_iv, sizeof(key->key_iv));
+	os_memcpy(ekey + sizeof(key->key_iv), sm->eap_if->eapKeyData, 32);
+	rc4_skip(ekey, ekey_len, 0, (u8 *) (key + 1), key_len);
+	os_free(ekey);
+
+	/* This header is needed here for HMAC-MD5, but it will be regenerated
+	 * in ieee802_1x_send() */
+	hdr->version = hapd->conf->eapol_version;
+	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+	hdr->length = host_to_be16(len);
+	hmac_md5(sm->eap_if->eapKeyData + 32, 32, buf, sizeof(*hdr) + len,
+		 key->key_signature);
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key to " MACSTR
+		   " (%s index=%d)", MAC2STR(sm->addr),
+		   broadcast ? "broadcast" : "unicast", idx);
+	ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len);
+	if (sta->eapol_sm)
+		sta->eapol_sm->dot1xAuthEapolFramesTx++;
+	os_free(buf);
+}
+
+
+static void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct eapol_authenticator *eapol = hapd->eapol_auth;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm == NULL || !sm->eap_if->eapKeyData)
+		return;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR,
+		   MAC2STR(sta->addr));
+
+#ifndef CONFIG_NO_VLAN
+	if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) {
+		wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported.");
+		return;
+	}
+#endif /* CONFIG_NO_VLAN */
+
+	if (eapol->default_wep_key) {
+		ieee802_1x_tx_key_one(hapd, sta, eapol->default_wep_key_idx, 1,
+				      eapol->default_wep_key,
+				      hapd->conf->default_wep_key_len);
+	}
+
+	if (hapd->conf->individual_wep_key_len > 0) {
+		u8 *ikey;
+		ikey = os_malloc(hapd->conf->individual_wep_key_len);
+		if (ikey == NULL ||
+		    random_get_bytes(ikey, hapd->conf->individual_wep_key_len))
+		{
+			wpa_printf(MSG_ERROR, "Could not generate random "
+				   "individual WEP key.");
+			os_free(ikey);
+			return;
+		}
+
+		wpa_hexdump_key(MSG_DEBUG, "Individual WEP key",
+				ikey, hapd->conf->individual_wep_key_len);
+
+		ieee802_1x_tx_key_one(hapd, sta, 0, 0, ikey,
+				      hapd->conf->individual_wep_key_len);
+
+		/* TODO: set encryption in TX callback, i.e., only after STA
+		 * has ACKed EAPOL-Key frame */
+		if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
+					sta->addr, 0, 1, NULL, 0, ikey,
+					hapd->conf->individual_wep_key_len)) {
+			wpa_printf(MSG_ERROR, "Could not set individual WEP "
+				   "encryption.");
+		}
+
+		os_free(ikey);
+	}
+}
+
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_FIPS */
+
+
+const char *radius_mode_txt(struct hostapd_data *hapd)
+{
+	switch (hapd->iface->conf->hw_mode) {
+	case HOSTAPD_MODE_IEEE80211AD:
+		return "802.11ad";
+	case HOSTAPD_MODE_IEEE80211A:
+		return "802.11a";
+	case HOSTAPD_MODE_IEEE80211G:
+		return "802.11g";
+	case HOSTAPD_MODE_IEEE80211B:
+	default:
+		return "802.11b";
+	}
+}
+
+
+int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int i;
+	u8 rate = 0;
+
+	for (i = 0; i < sta->supported_rates_len; i++)
+		if ((sta->supported_rates[i] & 0x7f) > rate)
+			rate = sta->supported_rates[i] & 0x7f;
+
+	return rate;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
+				      struct eapol_state_machine *sm,
+				      const u8 *eap, size_t len)
+{
+	const u8 *identity;
+	size_t identity_len;
+	const struct eap_hdr *hdr = (const struct eap_hdr *) eap;
+
+	if (len <= sizeof(struct eap_hdr) ||
+	    (hdr->code == EAP_CODE_RESPONSE &&
+	     eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) ||
+	    (hdr->code == EAP_CODE_INITIATE &&
+	     eap[sizeof(struct eap_hdr)] != EAP_ERP_TYPE_REAUTH) ||
+	    (hdr->code != EAP_CODE_RESPONSE &&
+	     hdr->code != EAP_CODE_INITIATE))
+		return;
+
+	identity = eap_get_identity(sm->eap, &identity_len);
+	if (identity == NULL)
+		return;
+
+	/* Save station identity for future RADIUS packets */
+	os_free(sm->identity);
+	sm->identity = (u8 *) dup_binstr(identity, identity_len);
+	if (sm->identity == NULL) {
+		sm->identity_len = 0;
+		return;
+	}
+
+	sm->identity_len = identity_len;
+	hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity);
+	sm->dot1xAuthEapolRespIdFramesRx++;
+}
+
+
+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) ||
+				     hapd->conf->osen) ?
+				    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,
+				      struct radius_msg *msg)
+{
+	char buf[128];
+
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_PORT) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-Port");
+		return -1;
+	}
+
+	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+		    MAC2STR(sta->addr));
+	buf[sizeof(buf) - 1] = '\0';
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_ERROR, "Could not add Calling-Station-Id");
+		return -1;
+	}
+
+	if (sta->flags & WLAN_STA_PREAUTH) {
+		os_strlcpy(buf, "IEEE 802.11i Pre-Authentication",
+			   sizeof(buf));
+	} else {
+		os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
+			    radius_sta_rate(hapd, sta) / 2,
+			    (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
+			    radius_mode_txt(hapd));
+		buf[sizeof(buf) - 1] = '\0';
+	}
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_CONNECT_INFO) &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_ERROR, "Could not add Connect-Info");
+		return -1;
+	}
+
+	if (sta->acct_session_id_hi || sta->acct_session_id_lo) {
+		os_snprintf(buf, sizeof(buf), "%08X-%08X",
+			    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))) {
+			wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
+			return -1;
+		}
+	}
+
+#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 || hapd->conf->osen) && sta->wpa_sm &&
+	    add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
+		return -1;
+
+	return 0;
+}
+
+
+int add_common_radius_attr(struct hostapd_data *hapd,
+			   struct hostapd_radius_attr *req_attr,
+			   struct sta_info *sta,
+			   struct radius_msg *msg)
+{
+	char buf[128];
+	struct hostapd_radius_attr *attr;
+
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_IP_ADDRESS) &&
+	    hapd->conf->own_ip_addr.af == AF_INET &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-IP-Address");
+		return -1;
+	}
+
+#ifdef CONFIG_IPV6
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_IPV6_ADDRESS) &&
+	    hapd->conf->own_ip_addr.af == AF_INET6 &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-IPv6-Address");
+		return -1;
+	}
+#endif /* CONFIG_IPV6 */
+
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_IDENTIFIER) &&
+	    hapd->conf->nas_identifier &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+				 (u8 *) hapd->conf->nas_identifier,
+				 os_strlen(hapd->conf->nas_identifier))) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-Identifier");
+		return -1;
+	}
+
+	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
+		    MAC2STR(hapd->own_addr),
+		    wpa_ssid_txt(hapd->conf->ssid.ssid,
+				 hapd->conf->ssid.ssid_len));
+	buf[sizeof(buf) - 1] = '\0';
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_CALLED_STATION_ID) &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_ERROR, "Could not add Called-Station-Id");
+		return -1;
+	}
+
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_PORT_TYPE) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
+				       RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-Port-Type");
+		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;
+
+	for (attr = req_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");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+					  struct sta_info *sta,
+					  const u8 *eap, size_t len)
+{
+	struct radius_msg *msg;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm == NULL)
+		return;
+
+	ieee802_1x_learn_identity(hapd, sm, eap, len);
+
+	wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS "
+		   "packet");
+
+	sm->radius_identifier = radius_client_get_id(hapd->radius);
+	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
+			     sm->radius_identifier);
+	if (msg == NULL) {
+		wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
+		return;
+	}
+
+	radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
+
+	if (sm->identity &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
+				 sm->identity, sm->identity_len)) {
+		wpa_printf(MSG_INFO, "Could not add User-Name");
+		goto fail;
+	}
+
+	if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, sta,
+				   msg) < 0)
+		goto fail;
+
+	/* TODO: should probably check MTU from driver config; 2304 is max for
+	 * IEEE 802.11, but use 1400 to avoid problems with too large packets
+	 */
+	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)) {
+		wpa_printf(MSG_INFO, "Could not add Framed-MTU");
+		goto fail;
+	}
+
+	if (!radius_msg_add_eap(msg, eap, len)) {
+		wpa_printf(MSG_INFO, "Could not add EAP-Message");
+		goto fail;
+	}
+
+	/* State attribute must be copied if and only if this packet is
+	 * Access-Request reply to the previous Access-Challenge */
+	if (sm->last_recv_radius &&
+	    radius_msg_get_hdr(sm->last_recv_radius)->code ==
+	    RADIUS_CODE_ACCESS_CHALLENGE) {
+		int res = radius_msg_copy_attr(msg, sm->last_recv_radius,
+					       RADIUS_ATTR_STATE);
+		if (res < 0) {
+			wpa_printf(MSG_INFO, "Could not copy State attribute from previous Access-Challenge");
+			goto fail;
+		}
+		if (res > 0) {
+			wpa_printf(MSG_DEBUG, "Copied RADIUS State Attribute");
+		}
+	}
+
+	if (hapd->conf->radius_request_cui) {
+		const u8 *cui;
+		size_t cui_len;
+		/* Add previously learned CUI or nul CUI to request CUI */
+		if (sm->radius_cui) {
+			cui = wpabuf_head(sm->radius_cui);
+			cui_len = wpabuf_len(sm->radius_cui);
+		} else {
+			cui = (const u8 *) "\0";
+			cui_len = 1;
+		}
+		if (!radius_msg_add_attr(msg,
+					 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+					 cui, cui_len)) {
+			wpa_printf(MSG_ERROR, "Could not add CUI");
+			goto fail;
+		}
+	}
+
+#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;
+
+	return;
+
+ fail:
+	radius_msg_free(msg);
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+static void handle_eap_response(struct hostapd_data *hapd,
+				struct sta_info *sta, struct eap_hdr *eap,
+				size_t len)
+{
+	u8 type, *data;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	if (sm == NULL)
+		return;
+
+	data = (u8 *) (eap + 1);
+
+	if (len < sizeof(*eap) + 1) {
+		wpa_printf(MSG_INFO, "handle_eap_response: too short response data");
+		return;
+	}
+
+	sm->eap_type_supp = type = data[0];
+
+	hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d "
+		       "id=%d len=%d) from STA: EAP Response-%s (%d)",
+		       eap->code, eap->identifier, be_to_host16(eap->length),
+		       eap_server_get_name(0, type), type);
+
+	sm->dot1xAuthEapolRespFramesRx++;
+
+	wpabuf_free(sm->eap_if->eapRespData);
+	sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
+	sm->eapolEap = TRUE;
+}
+
+
+static void handle_eap_initiate(struct hostapd_data *hapd,
+				struct sta_info *sta, struct eap_hdr *eap,
+				size_t len)
+{
+#ifdef CONFIG_ERP
+	u8 type, *data;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm == NULL)
+		return;
+
+	if (len < sizeof(*eap) + 1) {
+		wpa_printf(MSG_INFO,
+			   "handle_eap_initiate: too short response data");
+		return;
+	}
+
+	data = (u8 *) (eap + 1);
+	type = data[0];
+
+	hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d "
+		       "id=%d len=%d) from STA: EAP Initiate type %u",
+		       eap->code, eap->identifier, be_to_host16(eap->length),
+		       type);
+
+	wpabuf_free(sm->eap_if->eapRespData);
+	sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
+	sm->eapolEap = TRUE;
+#endif /* CONFIG_ERP */
+}
+
+
+/* Process incoming EAP packet from Supplicant */
+static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
+		       u8 *buf, size_t len)
+{
+	struct eap_hdr *eap;
+	u16 eap_len;
+
+	if (len < sizeof(*eap)) {
+		wpa_printf(MSG_INFO, "   too short EAP packet");
+		return;
+	}
+
+	eap = (struct eap_hdr *) buf;
+
+	eap_len = be_to_host16(eap->length);
+	wpa_printf(MSG_DEBUG, "EAP: code=%d identifier=%d length=%d",
+		   eap->code, eap->identifier, eap_len);
+	if (eap_len < sizeof(*eap)) {
+		wpa_printf(MSG_DEBUG, "   Invalid EAP length");
+		return;
+	} else if (eap_len > len) {
+		wpa_printf(MSG_DEBUG, "   Too short frame to contain this EAP "
+			   "packet");
+		return;
+	} else if (eap_len < len) {
+		wpa_printf(MSG_DEBUG, "   Ignoring %lu extra bytes after EAP "
+			   "packet", (unsigned long) len - eap_len);
+	}
+
+	switch (eap->code) {
+	case EAP_CODE_REQUEST:
+		wpa_printf(MSG_DEBUG, " (request)");
+		return;
+	case EAP_CODE_RESPONSE:
+		wpa_printf(MSG_DEBUG, " (response)");
+		handle_eap_response(hapd, sta, eap, eap_len);
+		break;
+	case EAP_CODE_SUCCESS:
+		wpa_printf(MSG_DEBUG, " (success)");
+		return;
+	case EAP_CODE_FAILURE:
+		wpa_printf(MSG_DEBUG, " (failure)");
+		return;
+	case EAP_CODE_INITIATE:
+		wpa_printf(MSG_DEBUG, " (initiate)");
+		handle_eap_initiate(hapd, sta, eap, eap_len);
+		break;
+	case EAP_CODE_FINISH:
+		wpa_printf(MSG_DEBUG, " (finish)");
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, " (unknown code)");
+		return;
+	}
+}
+
+
+static struct eapol_state_machine *
+ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int flags = 0;
+	if (sta->flags & WLAN_STA_PREAUTH)
+		flags |= EAPOL_SM_PREAUTH;
+	if (sta->wpa_sm) {
+		flags |= EAPOL_SM_USES_WPA;
+		if (wpa_auth_sta_get_pmksa(sta->wpa_sm))
+			flags |= EAPOL_SM_FROM_PMKSA_CACHE;
+	}
+	return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags,
+				sta->wps_ie, sta->p2p_ie, sta,
+				sta->identity, sta->radius_cui);
+}
+
+
+/**
+ * ieee802_1x_receive - Process the EAPOL frames from the Supplicant
+ * @hapd: hostapd BSS data
+ * @sa: Source address (sender of the EAPOL frame)
+ * @buf: EAPOL frame
+ * @len: Length of buf in octets
+ *
+ * This function is called for each incoming EAPOL frame from the interface
+ */
+void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
+			size_t len)
+{
+	struct sta_info *sta;
+	struct ieee802_1x_hdr *hdr;
+	struct ieee802_1x_eapol_key *key;
+	u16 datalen;
+	struct rsn_pmksa_cache_entry *pmksa;
+	int key_mgmt;
+
+	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen &&
+	    !hapd->conf->wps_state)
+		return;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR,
+		   (unsigned long) len, MAC2STR(sa));
+	sta = ap_get_sta(hapd, sa);
+	if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) &&
+		     !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not "
+			   "associated/Pre-authenticating STA");
+		return;
+	}
+
+	if (len < sizeof(*hdr)) {
+		wpa_printf(MSG_INFO, "   too short IEEE 802.1X packet");
+		return;
+	}
+
+	hdr = (struct ieee802_1x_hdr *) buf;
+	datalen = be_to_host16(hdr->length);
+	wpa_printf(MSG_DEBUG, "   IEEE 802.1X: version=%d type=%d length=%d",
+		   hdr->version, hdr->type, datalen);
+
+	if (len - sizeof(*hdr) < datalen) {
+		wpa_printf(MSG_INFO, "   frame too short for this IEEE 802.1X packet");
+		if (sta->eapol_sm)
+			sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++;
+		return;
+	}
+	if (len - sizeof(*hdr) > datalen) {
+		wpa_printf(MSG_DEBUG, "   ignoring %lu extra octets after "
+			   "IEEE 802.1X packet",
+			   (unsigned long) len - sizeof(*hdr) - datalen);
+	}
+
+	if (sta->eapol_sm) {
+		sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version;
+		sta->eapol_sm->dot1xAuthEapolFramesRx++;
+	}
+
+	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+	if (datalen >= sizeof(struct ieee802_1x_eapol_key) &&
+	    hdr->type == IEEE802_1X_TYPE_EAPOL_KEY &&
+	    (key->type == EAPOL_KEY_TYPE_WPA ||
+	     key->type == EAPOL_KEY_TYPE_RSN)) {
+		wpa_receive(hapd->wpa_auth, sta->wpa_sm, (u8 *) hdr,
+			    sizeof(*hdr) + datalen);
+		return;
+	}
+
+	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");
+		return;
+	}
+
+	key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+	if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
+			   "STA is using PSK");
+		return;
+	}
+
+	if (!sta->eapol_sm) {
+		sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
+		if (!sta->eapol_sm)
+			return;
+
+#ifdef CONFIG_WPS
+		if (!hapd->conf->ieee802_1x && hapd->conf->wps_state) {
+			u32 wflags = sta->flags & (WLAN_STA_WPS |
+						   WLAN_STA_WPS2 |
+						   WLAN_STA_MAYBE_WPS);
+			if (wflags == WLAN_STA_MAYBE_WPS ||
+			    wflags == (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) {
+				/*
+				 * Delay EAPOL frame transmission until a
+				 * possible WPS STA initiates the handshake
+				 * with EAPOL-Start. Only allow the wait to be
+				 * skipped if the STA is known to support WPS
+				 * 2.0.
+				 */
+				wpa_printf(MSG_DEBUG, "WPS: Do not start "
+					   "EAPOL until EAPOL-Start is "
+					   "received");
+				sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+			}
+		}
+#endif /* CONFIG_WPS */
+
+		sta->eapol_sm->eap_if->portEnabled = TRUE;
+	}
+
+	/* since we support version 1, we can ignore version field and proceed
+	 * as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */
+	/* TODO: actually, we are not version 1 anymore.. However, Version 2
+	 * does not change frame contents, so should be ok to process frames
+	 * more or less identically. Some changes might be needed for
+	 * verification of fields. */
+
+	switch (hdr->type) {
+	case IEEE802_1X_TYPE_EAP_PACKET:
+		handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen);
+		break;
+
+	case IEEE802_1X_TYPE_EAPOL_START:
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG, "received EAPOL-Start "
+			       "from STA");
+		sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
+		pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+		if (pmksa) {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG, "cached PMKSA "
+				       "available - ignore it since "
+				       "STA sent EAPOL-Start");
+			wpa_auth_sta_clear_pmksa(sta->wpa_sm, pmksa);
+		}
+		sta->eapol_sm->eapolStart = TRUE;
+		sta->eapol_sm->dot1xAuthEapolStartFramesRx++;
+		eap_server_clear_identity(sta->eapol_sm->eap);
+		wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
+		break;
+
+	case IEEE802_1X_TYPE_EAPOL_LOGOFF:
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG, "received EAPOL-Logoff "
+			       "from STA");
+		sta->acct_terminate_cause =
+			RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+		accounting_sta_stop(hapd, sta);
+		sta->eapol_sm->eapolLogoff = TRUE;
+		sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++;
+		eap_server_clear_identity(sta->eapol_sm->eap);
+		break;
+
+	case IEEE802_1X_TYPE_EAPOL_KEY:
+		wpa_printf(MSG_DEBUG, "   EAPOL-Key");
+		if (!ap_sta_is_authorized(sta)) {
+			wpa_printf(MSG_DEBUG, "   Dropped key data from "
+				   "unauthorized Supplicant");
+			break;
+		}
+		break;
+
+	case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT:
+		wpa_printf(MSG_DEBUG, "   EAPOL-Encapsulated-ASF-Alert");
+		/* TODO: implement support for this; show data */
+		break;
+
+	default:
+		wpa_printf(MSG_DEBUG, "   unknown IEEE 802.1X packet type");
+		sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++;
+		break;
+	}
+
+	eapol_auth_step(sta->eapol_sm);
+}
+
+
+/**
+ * ieee802_1x_new_station - Start IEEE 802.1X authentication
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ *
+ * This function is called to start IEEE 802.1X authentication when a new
+ * station completes IEEE 802.11 association.
+ */
+void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct rsn_pmksa_cache_entry *pmksa;
+	int reassoc = 1;
+	int force_1x = 0;
+	int key_mgmt;
+
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state &&
+	    ((hapd->conf->wpa && (sta->flags & WLAN_STA_MAYBE_WPS)) ||
+	     (sta->flags & WLAN_STA_WPS))) {
+		/*
+		 * Need to enable IEEE 802.1X/EAPOL state machines for possible
+		 * WPS handshake even if IEEE 802.1X/EAPOL is not used for
+		 * authentication in this BSS.
+		 */
+		force_1x = 1;
+	}
+#endif /* CONFIG_WPS */
+
+	if (!force_1x && !hapd->conf->ieee802_1x && !hapd->conf->osen) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - "
+			   "802.1X not enabled or forced for WPS");
+		/*
+		 * Clear any possible EAPOL authenticator state to support
+		 * reassociation change from WPS to PSK.
+		 */
+		ieee802_1x_free_station(sta);
+		return;
+	}
+
+	key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+	if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
+		/*
+		 * Clear any possible EAPOL authenticator state to support
+		 * reassociation change from WPA-EAP to PSK.
+		 */
+		ieee802_1x_free_station(sta);
+		return;
+	}
+
+	if (sta->eapol_sm == NULL) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG, "start authentication");
+		sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
+		if (sta->eapol_sm == NULL) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE8021X,
+				       HOSTAPD_LEVEL_INFO,
+				       "failed to allocate state machine");
+			return;
+		}
+		reassoc = 0;
+	}
+
+#ifdef CONFIG_WPS
+	sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
+	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
+		 * wait to be skipped if the STA is known to support WPS 2.0.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS: Do not start EAPOL until "
+			   "EAPOL-Start is received");
+		sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+	}
+#endif /* CONFIG_WPS */
+
+	sta->eapol_sm->eap_if->portEnabled = TRUE;
+
+#ifdef CONFIG_IEEE80211R
+	if (sta->auth_alg == WLAN_AUTH_FT) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "PMK from FT - skip IEEE 802.1X/EAP");
+		/* Setup EAPOL state machines to already authenticated state
+		 * because of existing FT information from R0KH. */
+		sta->eapol_sm->keyRun = TRUE;
+		sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
+		sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+		sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+		sta->eapol_sm->authSuccess = TRUE;
+		sta->eapol_sm->authFail = FALSE;
+		if (sta->eapol_sm->eap)
+			eap_sm_notify_cached(sta->eapol_sm->eap);
+		/* TODO: get vlan_id from R0KH using RRB message */
+		return;
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+	if (pmksa) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "PMK from PMKSA cache - skip IEEE 802.1X/EAP");
+		/* Setup EAPOL state machines to already authenticated state
+		 * because of existing PMKSA information in the cache. */
+		sta->eapol_sm->keyRun = TRUE;
+		sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
+		sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+		sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+		sta->eapol_sm->authSuccess = TRUE;
+		sta->eapol_sm->authFail = FALSE;
+		if (sta->eapol_sm->eap)
+			eap_sm_notify_cached(sta->eapol_sm->eap);
+		pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm);
+		ap_sta_bind_vlan(hapd, sta);
+	} else {
+		if (reassoc) {
+			/*
+			 * Force EAPOL state machines to start
+			 * re-authentication without having to wait for the
+			 * Supplicant to send EAPOL-Start.
+			 */
+			sta->eapol_sm->reAuthenticate = TRUE;
+		}
+		eapol_auth_step(sta->eapol_sm);
+	}
+}
+
+
+void ieee802_1x_free_station(struct sta_info *sta)
+{
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm == NULL)
+		return;
+
+	sta->eapol_sm = NULL;
+
+#ifndef CONFIG_NO_RADIUS
+	radius_msg_free(sm->last_recv_radius);
+	radius_free_class(&sm->radius_class);
+	wpabuf_free(sm->radius_cui);
+#endif /* CONFIG_NO_RADIUS */
+
+	os_free(sm->identity);
+	eapol_auth_free(sm);
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd,
+					  struct sta_info *sta)
+{
+	struct wpabuf *eap;
+	const struct eap_hdr *hdr;
+	int eap_type = -1;
+	char buf[64];
+	struct radius_msg *msg;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm == NULL || sm->last_recv_radius == NULL) {
+		if (sm)
+			sm->eap_if->aaaEapNoReq = TRUE;
+		return;
+	}
+
+	msg = sm->last_recv_radius;
+
+	eap = radius_msg_get_eap(msg);
+	if (eap == NULL) {
+		/* RFC 3579, Chap. 2.6.3:
+		 * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
+		 * attribute */
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_WARNING, "could not extract "
+			       "EAP-Message from RADIUS message");
+		sm->eap_if->aaaEapNoReq = TRUE;
+		return;
+	}
+
+	if (wpabuf_len(eap) < sizeof(*hdr)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_WARNING, "too short EAP packet "
+			       "received from authentication server");
+		wpabuf_free(eap);
+		sm->eap_if->aaaEapNoReq = TRUE;
+		return;
+	}
+
+	if (wpabuf_len(eap) > sizeof(*hdr))
+		eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)];
+
+	hdr = wpabuf_head(eap);
+	switch (hdr->code) {
+	case EAP_CODE_REQUEST:
+		if (eap_type >= 0)
+			sm->eap_type_authsrv = eap_type;
+		os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
+			    eap_server_get_name(0, eap_type), eap_type);
+		break;
+	case EAP_CODE_RESPONSE:
+		os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
+			    eap_server_get_name(0, eap_type), eap_type);
+		break;
+	case EAP_CODE_SUCCESS:
+		os_strlcpy(buf, "EAP Success", sizeof(buf));
+		break;
+	case EAP_CODE_FAILURE:
+		os_strlcpy(buf, "EAP Failure", sizeof(buf));
+		break;
+	default:
+		os_strlcpy(buf, "unknown EAP code", sizeof(buf));
+		break;
+	}
+	buf[sizeof(buf) - 1] = '\0';
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "decapsulated EAP packet (code=%d "
+		       "id=%d len=%d) from RADIUS server: %s",
+		       hdr->code, hdr->identifier, be_to_host16(hdr->length),
+		       buf);
+	sm->eap_if->aaaEapReq = TRUE;
+
+	wpabuf_free(sm->eap_if->aaaEapReqData);
+	sm->eap_if->aaaEapReqData = eap;
+}
+
+
+static void ieee802_1x_get_keys(struct hostapd_data *hapd,
+				struct sta_info *sta, struct radius_msg *msg,
+				struct radius_msg *req,
+				const u8 *shared_secret,
+				size_t shared_secret_len)
+{
+	struct radius_ms_mppe_keys *keys;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	if (sm == NULL)
+		return;
+
+	keys = radius_msg_get_ms_keys(msg, req, shared_secret,
+				      shared_secret_len);
+
+	if (keys && keys->send && keys->recv) {
+		size_t len = keys->send_len + keys->recv_len;
+		wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key",
+				keys->send, keys->send_len);
+		wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key",
+				keys->recv, keys->recv_len);
+
+		os_free(sm->eap_if->aaaEapKeyData);
+		sm->eap_if->aaaEapKeyData = os_malloc(len);
+		if (sm->eap_if->aaaEapKeyData) {
+			os_memcpy(sm->eap_if->aaaEapKeyData, keys->recv,
+				  keys->recv_len);
+			os_memcpy(sm->eap_if->aaaEapKeyData + keys->recv_len,
+				  keys->send, keys->send_len);
+			sm->eap_if->aaaEapKeyDataLen = len;
+			sm->eap_if->aaaEapKeyAvailable = TRUE;
+		}
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "MS-MPPE: 1x_get_keys, could not get keys: %p  send: %p  recv: %p",
+			   keys, keys ? keys->send : NULL,
+			   keys ? keys->recv : NULL);
+	}
+
+	if (keys) {
+		os_free(keys->send);
+		os_free(keys->recv);
+		os_free(keys);
+	}
+}
+
+
+static void ieee802_1x_store_radius_class(struct hostapd_data *hapd,
+					  struct sta_info *sta,
+					  struct radius_msg *msg)
+{
+	u8 *attr_class;
+	size_t class_len;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	int count, i;
+	struct radius_attr_data *nclass;
+	size_t nclass_count;
+
+	if (!hapd->conf->radius->acct_server || hapd->radius == NULL ||
+	    sm == NULL)
+		return;
+
+	radius_free_class(&sm->radius_class);
+	count = radius_msg_count_attr(msg, RADIUS_ATTR_CLASS, 1);
+	if (count <= 0)
+		return;
+
+	nclass = os_calloc(count, sizeof(struct radius_attr_data));
+	if (nclass == NULL)
+		return;
+
+	nclass_count = 0;
+
+	attr_class = NULL;
+	for (i = 0; i < count; i++) {
+		do {
+			if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS,
+						    &attr_class, &class_len,
+						    attr_class) < 0) {
+				i = count;
+				break;
+			}
+		} while (class_len < 1);
+
+		nclass[nclass_count].data = os_malloc(class_len);
+		if (nclass[nclass_count].data == NULL)
+			break;
+
+		os_memcpy(nclass[nclass_count].data, attr_class, class_len);
+		nclass[nclass_count].len = class_len;
+		nclass_count++;
+	}
+
+	sm->radius_class.attr = nclass;
+	sm->radius_class.count = nclass_count;
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: Stored %lu RADIUS Class "
+		   "attributes for " MACSTR,
+		   (unsigned long) sm->radius_class.count,
+		   MAC2STR(sta->addr));
+}
+
+
+/* Update sta->identity based on User-Name attribute in Access-Accept */
+static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd,
+					   struct sta_info *sta,
+					   struct radius_msg *msg)
+{
+	u8 *buf, *identity;
+	size_t len;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm == NULL)
+		return;
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len,
+				    NULL) < 0)
+		return;
+
+	identity = (u8 *) dup_binstr(buf, len);
+	if (identity == NULL)
+		return;
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "old identity '%s' updated with "
+		       "User-Name from Access-Accept '%s'",
+		       sm->identity ? (char *) sm->identity : "N/A",
+		       (char *) identity);
+
+	os_free(sm->identity);
+	sm->identity = identity;
+	sm->identity_len = len;
+}
+
+
+/* Update CUI based on Chargeable-User-Identity attribute in Access-Accept */
+static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd,
+				      struct sta_info *sta,
+				      struct radius_msg *msg)
+{
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	struct wpabuf *cui;
+	u8 *buf;
+	size_t len;
+
+	if (sm == NULL)
+		return;
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+				    &buf, &len, NULL) < 0)
+		return;
+
+	cui = wpabuf_alloc_copy(buf, len);
+	if (cui == NULL)
+		return;
+
+	wpabuf_free(sm->radius_cui);
+	sm->radius_cui = cui;
+}
+
+
+#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;
+};
+
+
+static int ieee802_1x_select_radius_identifier(struct hostapd_data *hapd,
+					       struct sta_info *sta,
+					       void *ctx)
+{
+	struct sta_id_search *id_search = ctx;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm && sm->radius_identifier >= 0 &&
+	    sm->radius_identifier == id_search->identifier) {
+		id_search->sm = sm;
+		return 1;
+	}
+	return 0;
+}
+
+
+static struct eapol_state_machine *
+ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier)
+{
+	struct sta_id_search id_search;
+	id_search.identifier = identifier;
+	id_search.sm = NULL;
+	ap_for_each_sta(hapd, ieee802_1x_select_radius_identifier, &id_search);
+	return id_search.sm;
+}
+
+
+/**
+ * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: Processing status
+ */
+static RadiusRxResult
+ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
+			const u8 *shared_secret, size_t shared_secret_len,
+			void *data)
+{
+	struct hostapd_data *hapd = data;
+	struct sta_info *sta;
+	u32 session_timeout = 0, termination_action, acct_interim_interval;
+	int session_timeout_set, vlan_id = 0;
+	struct eapol_state_machine *sm;
+	int override_eapReq = 0;
+	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+
+	sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier);
+	if (sm == NULL) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Could not find matching "
+			   "station for this RADIUS message");
+		return RADIUS_RX_UNKNOWN;
+	}
+	sta = sm->sta;
+
+	/* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be
+	 * present when packet contains an EAP-Message attribute */
+	if (hdr->code == RADIUS_CODE_ACCESS_REJECT &&
+	    radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
+				0) < 0 &&
+	    radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "Allowing RADIUS Access-Reject without "
+			   "Message-Authenticator since it does not include "
+			   "EAP-Message");
+	} else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
+				     req, 1)) {
+		wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Message-Authenticator - dropped");
+		return RADIUS_RX_INVALID_AUTHENTICATOR;
+	}
+
+	if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+	    hdr->code != RADIUS_CODE_ACCESS_REJECT &&
+	    hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) {
+		wpa_printf(MSG_INFO, "Unknown RADIUS message code");
+		return RADIUS_RX_UNKNOWN;
+	}
+
+	sm->radius_identifier = -1;
+	wpa_printf(MSG_DEBUG, "RADIUS packet matching with station " MACSTR,
+		   MAC2STR(sta->addr));
+
+	radius_msg_free(sm->last_recv_radius);
+	sm->last_recv_radius = msg;
+
+	session_timeout_set =
+		!radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
+					   &session_timeout);
+	if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_TERMINATION_ACTION,
+				      &termination_action))
+		termination_action = RADIUS_TERMINATION_ACTION_DEFAULT;
+
+	if (hapd->conf->acct_interim_interval == 0 &&
+	    hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
+	    radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
+				      &acct_interim_interval) == 0) {
+		if (acct_interim_interval < 60) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE8021X,
+				       HOSTAPD_LEVEL_INFO,
+				       "ignored too small "
+				       "Acct-Interim-Interval %d",
+				       acct_interim_interval);
+		} else
+			sta->acct_interim_interval = acct_interim_interval;
+	}
+
+
+	switch (hdr->code) {
+	case RADIUS_CODE_ACCESS_ACCEPT:
+		if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+			vlan_id = 0;
+#ifndef CONFIG_NO_VLAN
+		else
+			vlan_id = radius_msg_get_vlanid(msg);
+		if (vlan_id > 0 &&
+		    hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO,
+				       "VLAN ID %d", vlan_id);
+		} else if (vlan_id > 0) {
+			sta->eapol_sm->authFail = TRUE;
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO,
+				       "Invalid VLAN ID %d received from RADIUS server",
+				       vlan_id);
+			break;
+		} else if (hapd->conf->ssid.dynamic_vlan ==
+			   DYNAMIC_VLAN_REQUIRED) {
+			sta->eapol_sm->authFail = TRUE;
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE8021X,
+				       HOSTAPD_LEVEL_INFO, "authentication "
+				       "server did not include required VLAN "
+				       "ID in Access-Accept");
+			break;
+		}
+#endif /* CONFIG_NO_VLAN */
+
+		sta->vlan_id = vlan_id;
+		if ((sta->flags & WLAN_STA_ASSOC) &&
+		    ap_sta_bind_vlan(hapd, sta) < 0)
+			break;
+
+		sta->session_timeout_set = !!session_timeout_set;
+		sta->session_timeout = session_timeout;
+
+		/* RFC 3580, Ch. 3.17 */
+		if (session_timeout_set && termination_action ==
+		    RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) {
+			sm->reAuthPeriod = session_timeout;
+		} else if (session_timeout_set)
+			ap_sta_session_timeout(hapd, sta, session_timeout);
+
+		sm->eap_if->aaaSuccess = TRUE;
+		override_eapReq = 1;
+		ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret,
+				    shared_secret_len);
+		ieee802_1x_store_radius_class(hapd, sta, msg);
+		ieee802_1x_update_sta_identity(hapd, sta, msg);
+		ieee802_1x_update_sta_cui(hapd, sta, msg);
+		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) {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "Added PMKSA cache entry");
+		}
+		break;
+	case RADIUS_CODE_ACCESS_REJECT:
+		sm->eap_if->aaaFail = TRUE;
+		override_eapReq = 1;
+		break;
+	case RADIUS_CODE_ACCESS_CHALLENGE:
+		sm->eap_if->aaaEapReq = TRUE;
+		if (session_timeout_set) {
+			/* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */
+			sm->eap_if->aaaMethodTimeout = session_timeout;
+			hostapd_logger(hapd, sm->addr,
+				       HOSTAPD_MODULE_IEEE8021X,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "using EAP timeout of %d seconds (from "
+				       "RADIUS)",
+				       sm->eap_if->aaaMethodTimeout);
+		} else {
+			/*
+			 * Use dynamic retransmission behavior per EAP
+			 * specification.
+			 */
+			sm->eap_if->aaaMethodTimeout = 0;
+		}
+		break;
+	}
+
+	ieee802_1x_decapsulate_radius(hapd, sta);
+	if (override_eapReq)
+		sm->eap_if->aaaEapReq = FALSE;
+
+	eapol_auth_step(sm);
+
+	return RADIUS_RX_QUEUED;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	if (sm == NULL)
+		return;
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "aborting authentication");
+
+#ifndef CONFIG_NO_RADIUS
+	radius_msg_free(sm->last_recv_radius);
+	sm->last_recv_radius = NULL;
+#endif /* CONFIG_NO_RADIUS */
+
+	if (sm->eap_if->eapTimeout) {
+		/*
+		 * Disconnect the STA since it did not reply to the last EAP
+		 * request and we cannot continue EAP processing (EAP-Failure
+		 * could only be sent if the EAP peer actually replied).
+		 */
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "EAP Timeout, STA " MACSTR,
+			MAC2STR(sta->addr));
+
+		sm->eap_if->portEnabled = FALSE;
+		ap_sta_disconnect(hapd, sta, sta->addr,
+				  WLAN_REASON_PREV_AUTH_NOT_VALID);
+	}
+}
+
+
+static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd)
+{
+	struct eapol_authenticator *eapol = hapd->eapol_auth;
+
+	if (hapd->conf->default_wep_key_len < 1)
+		return 0;
+
+	os_free(eapol->default_wep_key);
+	eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len);
+	if (eapol->default_wep_key == NULL ||
+	    random_get_bytes(eapol->default_wep_key,
+			     hapd->conf->default_wep_key_len)) {
+		wpa_printf(MSG_INFO, "Could not generate random WEP key");
+		os_free(eapol->default_wep_key);
+		eapol->default_wep_key = NULL;
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "IEEE 802.1X: New default WEP key",
+			eapol->default_wep_key,
+			hapd->conf->default_wep_key_len);
+
+	return 0;
+}
+
+
+static int ieee802_1x_sta_key_available(struct hostapd_data *hapd,
+					struct sta_info *sta, void *ctx)
+{
+	if (sta->eapol_sm) {
+		sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
+		eapol_auth_step(sta->eapol_sm);
+	}
+	return 0;
+}
+
+
+static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct eapol_authenticator *eapol = hapd->eapol_auth;
+
+	if (eapol->default_wep_key_idx >= 3)
+		eapol->default_wep_key_idx =
+			hapd->conf->individual_wep_key_len > 0 ? 1 : 0;
+	else
+		eapol->default_wep_key_idx++;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d",
+		   eapol->default_wep_key_idx);
+		      
+	if (ieee802_1x_rekey_broadcast(hapd)) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_WARNING, "failed to generate a "
+			       "new broadcast key");
+		os_free(eapol->default_wep_key);
+		eapol->default_wep_key = NULL;
+		return;
+	}
+
+	/* TODO: Could setup key for RX here, but change default TX keyid only
+	 * after new broadcast key has been sent to all stations. */
+	if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
+				broadcast_ether_addr,
+				eapol->default_wep_key_idx, 1, NULL, 0,
+				eapol->default_wep_key,
+				hapd->conf->default_wep_key_len)) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_WARNING, "failed to configure a "
+			       "new broadcast key");
+		os_free(eapol->default_wep_key);
+		eapol->default_wep_key = NULL;
+		return;
+	}
+
+	ap_for_each_sta(hapd, ieee802_1x_sta_key_available, NULL);
+
+	if (hapd->conf->wep_rekeying_period > 0) {
+		eloop_register_timeout(hapd->conf->wep_rekeying_period, 0,
+				       ieee802_1x_rekey, hapd, NULL);
+	}
+}
+
+
+static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type,
+				  const u8 *data, size_t datalen)
+{
+#ifdef CONFIG_WPS
+	struct sta_info *sta = sta_ctx;
+
+	if ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
+	    WLAN_STA_MAYBE_WPS) {
+		const u8 *identity;
+		size_t identity_len;
+		struct eapol_state_machine *sm = sta->eapol_sm;
+
+		identity = eap_get_identity(sm->eap, &identity_len);
+		if (identity &&
+		    ((identity_len == WSC_ID_ENROLLEE_LEN &&
+		      os_memcmp(identity, WSC_ID_ENROLLEE,
+				WSC_ID_ENROLLEE_LEN) == 0) ||
+		     (identity_len == WSC_ID_REGISTRAR_LEN &&
+		      os_memcmp(identity, WSC_ID_REGISTRAR,
+				WSC_ID_REGISTRAR_LEN) == 0))) {
+			wpa_printf(MSG_DEBUG, "WPS: WLAN_STA_MAYBE_WPS -> "
+				   "WLAN_STA_WPS");
+			sta->flags |= WLAN_STA_WPS;
+		}
+	}
+#endif /* CONFIG_WPS */
+
+	ieee802_1x_send(ctx, sta_ctx, type, data, datalen);
+}
+
+
+static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx,
+				const u8 *data, size_t datalen)
+{
+#ifndef CONFIG_NO_RADIUS
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = sta_ctx;
+
+	ieee802_1x_encapsulate_radius(hapd, sta, data, datalen);
+#endif /* CONFIG_NO_RADIUS */
+}
+
+
+static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
+				 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, remediation);
+}
+
+
+static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
+				   size_t identity_len, int phase2,
+				   struct eap_user *user)
+{
+	struct hostapd_data *hapd = ctx;
+	const struct hostapd_eap_user *eap_user;
+	int i;
+	int rv = -1;
+
+	eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2);
+	if (eap_user == NULL)
+		goto out;
+
+	os_memset(user, 0, sizeof(*user));
+	user->phase2 = phase2;
+	for (i = 0; i < EAP_MAX_METHODS; i++) {
+		user->methods[i].vendor = eap_user->methods[i].vendor;
+		user->methods[i].method = eap_user->methods[i].method;
+	}
+
+	if (eap_user->password) {
+		user->password = os_malloc(eap_user->password_len);
+		if (user->password == NULL)
+			goto out;
+		os_memcpy(user->password, eap_user->password,
+			  eap_user->password_len);
+		user->password_len = eap_user->password_len;
+		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;
+	rv = 0;
+
+out:
+	if (rv)
+		wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
+
+	return rv;
+}
+
+
+static int ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL || sta->eapol_sm == NULL)
+		return 0;
+	return 1;
+}
+
+
+static void ieee802_1x_logger(void *ctx, const u8 *addr,
+			      eapol_logger_level level, const char *txt)
+{
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+	struct hostapd_data *hapd = ctx;
+	int hlevel;
+
+	switch (level) {
+	case EAPOL_LOGGER_WARNING:
+		hlevel = HOSTAPD_LEVEL_WARNING;
+		break;
+	case EAPOL_LOGGER_INFO:
+		hlevel = HOSTAPD_LEVEL_INFO;
+		break;
+	case EAPOL_LOGGER_DEBUG:
+	default:
+		hlevel = HOSTAPD_LEVEL_DEBUG;
+		break;
+	}
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE8021X, hlevel, "%s",
+		       txt);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+}
+
+
+static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx,
+					   int authorized)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = sta_ctx;
+	ieee802_1x_set_sta_authorized(hapd, sta, authorized);
+}
+
+
+static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = sta_ctx;
+	ieee802_1x_abort_auth(hapd, sta);
+}
+
+
+static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx)
+{
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_NO_RC4
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = sta_ctx;
+	ieee802_1x_tx_key(hapd, sta);
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_FIPS */
+}
+
+
+static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx,
+				   enum eapol_event type)
+{
+	/* struct hostapd_data *hapd = ctx; */
+	struct sta_info *sta = sta_ctx;
+	switch (type) {
+	case EAPOL_AUTH_SM_CHANGE:
+		wpa_auth_sm_notify(sta->wpa_sm);
+		break;
+	case EAPOL_AUTH_REAUTHENTICATE:
+		wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
+		break;
+	}
+}
+
+
+#ifdef CONFIG_ERP
+
+static struct eap_server_erp_key *
+ieee802_1x_erp_get_key(void *ctx, const char *keyname)
+{
+	struct hostapd_data *hapd = ctx;
+	struct eap_server_erp_key *erp;
+
+	dl_list_for_each(erp, &hapd->erp_keys, struct eap_server_erp_key,
+			 list) {
+		if (os_strcmp(erp->keyname_nai, keyname) == 0)
+			return erp;
+	}
+
+	return NULL;
+}
+
+
+static int ieee802_1x_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
+{
+	struct hostapd_data *hapd = ctx;
+
+	dl_list_add(&hapd->erp_keys, &erp->list);
+	return 0;
+}
+
+#endif /* CONFIG_ERP */
+
+
+int ieee802_1x_init(struct hostapd_data *hapd)
+{
+	int i;
+	struct eapol_auth_config conf;
+	struct eapol_auth_cb cb;
+
+	dl_list_init(&hapd->erp_keys);
+
+	os_memset(&conf, 0, sizeof(conf));
+	conf.ctx = hapd;
+	conf.eap_reauth_period = hapd->conf->eap_reauth_period;
+	conf.wpa = hapd->conf->wpa;
+	conf.individual_wep_key_len = hapd->conf->individual_wep_key_len;
+	conf.eap_server = hapd->conf->eap_server;
+	conf.ssl_ctx = hapd->ssl_ctx;
+	conf.msg_ctx = hapd->msg_ctx;
+	conf.eap_sim_db_priv = hapd->eap_sim_db_priv;
+	conf.eap_req_id_text = hapd->conf->eap_req_id_text;
+	conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len;
+	conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start;
+	conf.erp_domain = hapd->conf->erp_domain;
+	conf.erp = hapd->conf->eap_server_erp;
+	conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+	conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key;
+	conf.eap_fast_a_id = hapd->conf->eap_fast_a_id;
+	conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
+	conf.eap_fast_a_id_info = hapd->conf->eap_fast_a_id_info;
+	conf.eap_fast_prov = hapd->conf->eap_fast_prov;
+	conf.pac_key_lifetime = hapd->conf->pac_key_lifetime;
+	conf.pac_key_refresh_time = hapd->conf->pac_key_refresh_time;
+	conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
+	conf.tnc = hapd->conf->tnc;
+	conf.wps = hapd->wps;
+	conf.fragment_size = hapd->conf->fragment_size;
+	conf.pwd_group = hapd->conf->pwd_group;
+	conf.pbc_in_m1 = hapd->conf->pbc_in_m1;
+	if (hapd->conf->server_id) {
+		conf.server_id = (const u8 *) hapd->conf->server_id;
+		conf.server_id_len = os_strlen(hapd->conf->server_id);
+	} else {
+		conf.server_id = (const u8 *) "hostapd";
+		conf.server_id_len = 7;
+	}
+
+	os_memset(&cb, 0, sizeof(cb));
+	cb.eapol_send = ieee802_1x_eapol_send;
+	cb.aaa_send = ieee802_1x_aaa_send;
+	cb.finished = _ieee802_1x_finished;
+	cb.get_eap_user = ieee802_1x_get_eap_user;
+	cb.sta_entry_alive = ieee802_1x_sta_entry_alive;
+	cb.logger = ieee802_1x_logger;
+	cb.set_port_authorized = ieee802_1x_set_port_authorized;
+	cb.abort_auth = _ieee802_1x_abort_auth;
+	cb.tx_key = _ieee802_1x_tx_key;
+	cb.eapol_event = ieee802_1x_eapol_event;
+#ifdef CONFIG_ERP
+	cb.erp_get_key = ieee802_1x_erp_get_key;
+	cb.erp_add_key = ieee802_1x_erp_add_key;
+#endif /* CONFIG_ERP */
+
+	hapd->eapol_auth = eapol_auth_init(&conf, &cb);
+	if (hapd->eapol_auth == NULL)
+		return -1;
+
+	if ((hapd->conf->ieee802_1x || hapd->conf->wpa) &&
+	    hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1))
+		return -1;
+
+#ifndef CONFIG_NO_RADIUS
+	if (radius_client_register(hapd->radius, RADIUS_AUTH,
+				   ieee802_1x_receive_auth, hapd))
+		return -1;
+#endif /* CONFIG_NO_RADIUS */
+
+	if (hapd->conf->default_wep_key_len) {
+		for (i = 0; i < 4; i++)
+			hostapd_drv_set_key(hapd->conf->iface, hapd,
+					    WPA_ALG_NONE, NULL, i, 0, NULL, 0,
+					    NULL, 0);
+
+		ieee802_1x_rekey(hapd, NULL);
+
+		if (hapd->eapol_auth->default_wep_key == NULL)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+void ieee802_1x_erp_flush(struct hostapd_data *hapd)
+{
+	struct eap_server_erp_key *erp;
+
+	while ((erp = dl_list_first(&hapd->erp_keys, struct eap_server_erp_key,
+				    list)) != NULL) {
+		dl_list_del(&erp->list);
+		bin_clear_free(erp, sizeof(*erp));
+	}
+}
+
+
+void ieee802_1x_deinit(struct hostapd_data *hapd)
+{
+	eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
+
+	if (hapd->driver != NULL &&
+	    (hapd->conf->ieee802_1x || hapd->conf->wpa))
+		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+
+	eapol_auth_deinit(hapd->eapol_auth);
+	hapd->eapol_auth = NULL;
+
+	ieee802_1x_erp_flush(hapd);
+}
+
+
+int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+			 const u8 *buf, size_t len, int ack)
+{
+	struct ieee80211_hdr *hdr;
+	u8 *pos;
+	const unsigned char rfc1042_hdr[ETH_ALEN] =
+		{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+	if (sta == NULL)
+		return -1;
+	if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2)
+		return 0;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	pos = (u8 *) (hdr + 1);
+	if (os_memcmp(pos, rfc1042_hdr, sizeof(rfc1042_hdr)) != 0)
+		return 0;
+	pos += sizeof(rfc1042_hdr);
+	if (WPA_GET_BE16(pos) != ETH_P_PAE)
+		return 0;
+	pos += 2;
+
+	return ieee802_1x_eapol_tx_status(hapd, sta, pos, buf + len - pos,
+					  ack);
+}
+
+
+int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+			       const u8 *buf, int len, int ack)
+{
+	const struct ieee802_1x_hdr *xhdr =
+		(const struct ieee802_1x_hdr *) buf;
+	const u8 *pos = buf + sizeof(*xhdr);
+	struct ieee802_1x_eapol_key *key;
+
+	if (len < (int) sizeof(*xhdr))
+		return 0;
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " TX status - version=%d "
+		   "type=%d length=%d - ack=%d",
+		   MAC2STR(sta->addr), xhdr->version, xhdr->type,
+		   be_to_host16(xhdr->length), ack);
+
+	if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY)
+		return 0;
+
+	if (pos + sizeof(struct wpa_eapol_key) <= buf + len) {
+		const struct wpa_eapol_key *wpa;
+		wpa = (const struct wpa_eapol_key *) pos;
+		if (wpa->type == EAPOL_KEY_TYPE_RSN ||
+		    wpa->type == EAPOL_KEY_TYPE_WPA)
+			wpa_auth_eapol_key_tx_status(hapd->wpa_auth,
+						     sta->wpa_sm, ack);
+	}
+
+	/* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant
+	 * or Authenticator state machines, but EAPOL-Key packets are not
+	 * retransmitted in case of failure. Try to re-send failed EAPOL-Key
+	 * packets couple of times because otherwise STA keys become
+	 * unsynchronized with AP. */
+	if (!ack && pos + sizeof(*key) <= buf + len) {
+		key = (struct ieee802_1x_eapol_key *) pos;
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG, "did not Ack EAPOL-Key "
+			       "frame (%scast index=%d)",
+			       key->key_index & BIT(7) ? "uni" : "broad",
+			       key->key_index & ~BIT(7));
+		/* TODO: re-send EAPOL-Key couple of times (with short delay
+		 * between them?). If all attempt fail, report error and
+		 * deauthenticate STA so that it will get new keys when
+		 * authenticating again (e.g., after returning in range).
+		 * Separate limit/transmit state needed both for unicast and
+		 * broadcast keys(?) */
+	}
+	/* TODO: could move unicast key configuration from ieee802_1x_tx_key()
+	 * to here and change the key only if the EAPOL-Key packet was Acked.
+	 */
+
+	return 1;
+}
+
+
+u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len)
+{
+	if (sm == NULL || sm->identity == NULL)
+		return NULL;
+
+	*len = sm->identity_len;
+	return sm->identity;
+}
+
+
+u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
+				 int idx)
+{
+	if (sm == NULL || sm->radius_class.attr == NULL ||
+	    idx >= (int) sm->radius_class.count)
+		return NULL;
+
+	*len = sm->radius_class.attr[idx].len;
+	return sm->radius_class.attr[idx].data;
+}
+
+
+struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm)
+{
+	if (sm == NULL)
+		return NULL;
+	return sm->radius_cui;
+}
+
+
+const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len)
+{
+	*len = 0;
+	if (sm == NULL)
+		return NULL;
+
+	*len = sm->eap_if->eapKeyDataLen;
+	return sm->eap_if->eapKeyData;
+}
+
+
+void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
+				    int enabled)
+{
+	if (sm == NULL)
+		return;
+	sm->eap_if->portEnabled = enabled ? TRUE : FALSE;
+	eapol_auth_step(sm);
+}
+
+
+void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
+				  int valid)
+{
+	if (sm == NULL)
+		return;
+	sm->portValid = valid ? TRUE : FALSE;
+	eapol_auth_step(sm);
+}
+
+
+void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth)
+{
+	if (sm == NULL)
+		return;
+	if (pre_auth)
+		sm->flags |= EAPOL_SM_PREAUTH;
+	else
+		sm->flags &= ~EAPOL_SM_PREAUTH;
+}
+
+
+static const char * bool_txt(Boolean val)
+{
+	return val ? "TRUE" : "FALSE";
+}
+
+
+int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+	/* TODO */
+	return 0;
+}
+
+
+int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   char *buf, size_t buflen)
+{
+	int len = 0, ret;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	struct os_reltime diff;
+	const char *name1;
+	const char *name2;
+
+	if (sm == NULL)
+		return 0;
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot1xPaePortNumber=%d\n"
+			  "dot1xPaePortProtocolVersion=%d\n"
+			  "dot1xPaePortCapabilities=1\n"
+			  "dot1xPaePortInitialize=%d\n"
+			  "dot1xPaePortReauthenticate=FALSE\n",
+			  sta->aid,
+			  EAPOL_VERSION,
+			  sm->initialize);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	/* dot1xAuthConfigTable */
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot1xAuthPaeState=%d\n"
+			  "dot1xAuthBackendAuthState=%d\n"
+			  "dot1xAuthAdminControlledDirections=%d\n"
+			  "dot1xAuthOperControlledDirections=%d\n"
+			  "dot1xAuthAuthControlledPortStatus=%d\n"
+			  "dot1xAuthAuthControlledPortControl=%d\n"
+			  "dot1xAuthQuietPeriod=%u\n"
+			  "dot1xAuthServerTimeout=%u\n"
+			  "dot1xAuthReAuthPeriod=%u\n"
+			  "dot1xAuthReAuthEnabled=%s\n"
+			  "dot1xAuthKeyTxEnabled=%s\n",
+			  sm->auth_pae_state + 1,
+			  sm->be_auth_state + 1,
+			  sm->adminControlledDirections,
+			  sm->operControlledDirections,
+			  sm->authPortStatus,
+			  sm->portControl,
+			  sm->quietPeriod,
+			  sm->serverTimeout,
+			  sm->reAuthPeriod,
+			  bool_txt(sm->reAuthEnabled),
+			  bool_txt(sm->keyTxEnabled));
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	/* dot1xAuthStatsTable */
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot1xAuthEapolFramesRx=%u\n"
+			  "dot1xAuthEapolFramesTx=%u\n"
+			  "dot1xAuthEapolStartFramesRx=%u\n"
+			  "dot1xAuthEapolLogoffFramesRx=%u\n"
+			  "dot1xAuthEapolRespIdFramesRx=%u\n"
+			  "dot1xAuthEapolRespFramesRx=%u\n"
+			  "dot1xAuthEapolReqIdFramesTx=%u\n"
+			  "dot1xAuthEapolReqFramesTx=%u\n"
+			  "dot1xAuthInvalidEapolFramesRx=%u\n"
+			  "dot1xAuthEapLengthErrorFramesRx=%u\n"
+			  "dot1xAuthLastEapolFrameVersion=%u\n"
+			  "dot1xAuthLastEapolFrameSource=" MACSTR "\n",
+			  sm->dot1xAuthEapolFramesRx,
+			  sm->dot1xAuthEapolFramesTx,
+			  sm->dot1xAuthEapolStartFramesRx,
+			  sm->dot1xAuthEapolLogoffFramesRx,
+			  sm->dot1xAuthEapolRespIdFramesRx,
+			  sm->dot1xAuthEapolRespFramesRx,
+			  sm->dot1xAuthEapolReqIdFramesTx,
+			  sm->dot1xAuthEapolReqFramesTx,
+			  sm->dot1xAuthInvalidEapolFramesRx,
+			  sm->dot1xAuthEapLengthErrorFramesRx,
+			  sm->dot1xAuthLastEapolFrameVersion,
+			  MAC2STR(sm->addr));
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	/* dot1xAuthDiagTable */
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot1xAuthEntersConnecting=%u\n"
+			  "dot1xAuthEapLogoffsWhileConnecting=%u\n"
+			  "dot1xAuthEntersAuthenticating=%u\n"
+			  "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthFailWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthReauthsWhileAuthenticated=%u\n"
+			  "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n"
+			  "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n"
+			  "dot1xAuthBackendResponses=%u\n"
+			  "dot1xAuthBackendAccessChallenges=%u\n"
+			  "dot1xAuthBackendOtherRequestsToSupplicant=%u\n"
+			  "dot1xAuthBackendAuthSuccesses=%u\n"
+			  "dot1xAuthBackendAuthFails=%u\n",
+			  sm->authEntersConnecting,
+			  sm->authEapLogoffsWhileConnecting,
+			  sm->authEntersAuthenticating,
+			  sm->authAuthSuccessesWhileAuthenticating,
+			  sm->authAuthTimeoutsWhileAuthenticating,
+			  sm->authAuthFailWhileAuthenticating,
+			  sm->authAuthEapStartsWhileAuthenticating,
+			  sm->authAuthEapLogoffWhileAuthenticating,
+			  sm->authAuthReauthsWhileAuthenticated,
+			  sm->authAuthEapStartsWhileAuthenticated,
+			  sm->authAuthEapLogoffWhileAuthenticated,
+			  sm->backendResponses,
+			  sm->backendAccessChallenges,
+			  sm->backendOtherRequestsToSupplicant,
+			  sm->backendAuthSuccesses,
+			  sm->backendAuthFails);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	/* dot1xAuthSessionStatsTable */
+	os_reltime_age(&sta->acct_session_start, &diff);
+	ret = os_snprintf(buf + len, buflen - len,
+			  /* TODO: dot1xAuthSessionOctetsRx */
+			  /* TODO: dot1xAuthSessionOctetsTx */
+			  /* TODO: dot1xAuthSessionFramesRx */
+			  /* TODO: dot1xAuthSessionFramesTx */
+			  "dot1xAuthSessionId=%08X-%08X\n"
+			  "dot1xAuthSessionAuthenticMethod=%d\n"
+			  "dot1xAuthSessionTime=%u\n"
+			  "dot1xAuthSessionTerminateCause=999\n"
+			  "dot1xAuthSessionUserName=%s\n",
+			  sta->acct_session_id_hi, sta->acct_session_id_lo,
+			  (wpa_key_mgmt_wpa_ieee8021x(
+				   wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
+			  1 : 2,
+			  (unsigned int) diff.sec,
+			  sm->identity);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	if (sm->acct_multi_session_id_hi) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "authMultiSessionId=%08X+%08X\n",
+				  sm->acct_multi_session_id_hi,
+				  sm->acct_multi_session_id_lo);
+		if (os_snprintf_error(buflen - len, ret))
+			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,
+			  sm->eap_type_supp, name2);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	return len;
+}
+
+
+static void ieee802_1x_finished(struct hostapd_data *hapd,
+				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;
+	unsigned int session_timeout;
+
+#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 (sta->session_timeout_set)
+		session_timeout = sta->session_timeout;
+	else
+		session_timeout = dot11RSNAConfigPMKLifetime;
+	if (success && key && len >= PMK_LEN && !sta->remediation &&
+	    !sta->hs20_deauth_requested &&
+	    wpa_auth_pmksa_add(sta->wpa_sm, key, session_timeout,
+			       sta->eapol_sm) == 0) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Added PMKSA cache entry (IEEE 802.1X)");
+	}
+
+	if (!success) {
+		/*
+		 * Many devices require deauthentication after WPS provisioning
+		 * and some may not be be able to do that themselves, so
+		 * disconnect the client here. In addition, this may also
+		 * benefit IEEE 802.1X/EAPOL authentication cases, too since
+		 * the EAPOL PAE state machine would remain in HELD state for
+		 * considerable amount of time and some EAP methods, like
+		 * EAP-FAST with anonymous provisioning, may require another
+		 * EAPOL authentication to be started to complete connection.
+		 */
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: Force "
+			"disconnection after EAP-Failure");
+		/* Add a small sleep to increase likelihood of previously
+		 * requested EAP-Failure TX getting out before this should the
+		 * driver reorder operations.
+		 */
+		os_sleep(0, 10000);
+		ap_sta_disconnect(hapd, sta, sta->addr,
+				  WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
+		hostapd_wps_eap_completed(hapd);
+	}
+}
diff --git a/hostap/src/ap/ieee802_1x.h b/hostap/src/ap/ieee802_1x.h
new file mode 100644
index 0000000..14d6955
--- /dev/null
+++ b/hostap/src/ap/ieee802_1x.h
@@ -0,0 +1,61 @@
+/*
+ * hostapd / IEEE 802.1X-2004 Authenticator
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_H
+#define IEEE802_1X_H
+
+struct hostapd_data;
+struct sta_info;
+struct eapol_state_machine;
+struct hostapd_config;
+struct hostapd_bss_config;
+struct hostapd_radius_attr;
+struct radius_msg;
+
+
+void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
+			size_t len);
+void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_free_station(struct sta_info *sta);
+
+void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
+				   struct sta_info *sta, int authorized);
+void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta);
+int ieee802_1x_init(struct hostapd_data *hapd);
+void ieee802_1x_erp_flush(struct hostapd_data *hapd);
+void ieee802_1x_deinit(struct hostapd_data *hapd);
+int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+			 const u8 *buf, size_t len, int ack);
+int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+			       const u8 *data, int len, int ack);
+u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len);
+u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
+				 int idx);
+struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm);
+const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len);
+void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
+				    int enabled);
+void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
+				  int valid);
+void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth);
+int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
+int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   char *buf, size_t buflen);
+void hostapd_get_ntp_timestamp(u8 *buf);
+char *eap_type_text(u8 type);
+
+const char *radius_mode_txt(struct hostapd_data *hapd);
+int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta);
+
+int add_common_radius_attr(struct hostapd_data *hapd,
+			   struct hostapd_radius_attr *req_attr,
+			   struct sta_info *sta,
+			   struct radius_msg *msg);
+
+#endif /* IEEE802_1X_H */
diff --git a/hostap/src/ap/ndisc_snoop.c b/hostap/src/ap/ndisc_snoop.c
new file mode 100644
index 0000000..4a87721
--- /dev/null
+++ b/hostap/src/ap/ndisc_snoop.c
@@ -0,0 +1,184 @@
+/*
+ * Neighbor Discovery snooping for Proxy ARP
+ * Copyright (c) 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 <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include "utils/common.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "list.h"
+#include "x_snoop.h"
+
+struct ip6addr {
+	struct in6_addr addr;
+	struct dl_list list;
+};
+
+struct icmpv6_ndmsg {
+	struct ip6_hdr ipv6h;
+	struct icmp6_hdr icmp6h;
+	struct in6_addr target_addr;
+	u8 opt_type;
+	u8 len;
+	u8 opt_lladdr[0];
+} STRUCT_PACKED;
+
+#define ROUTER_ADVERTISEMENT	134
+#define NEIGHBOR_SOLICITATION	135
+#define NEIGHBOR_ADVERTISEMENT	136
+#define SOURCE_LL_ADDR		1
+
+static int sta_ip6addr_add(struct sta_info *sta, struct in6_addr *addr)
+{
+	struct ip6addr *ip6addr;
+
+	ip6addr = os_zalloc(sizeof(*ip6addr));
+	if (!ip6addr)
+		return -1;
+
+	os_memcpy(&ip6addr->addr, addr, sizeof(*addr));
+
+	dl_list_add_tail(&sta->ip6addr, &ip6addr->list);
+
+	return 0;
+}
+
+
+void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct ip6addr *ip6addr, *prev;
+
+	dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
+			      list) {
+		hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
+		os_free(ip6addr);
+	}
+}
+
+
+static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr)
+{
+	struct ip6addr *ip6addr;
+
+	dl_list_for_each(ip6addr, &sta->ip6addr, struct ip6addr, list) {
+		if (ip6addr->addr.s6_addr32[0] == addr->s6_addr32[0] &&
+		    ip6addr->addr.s6_addr32[1] == addr->s6_addr32[1] &&
+		    ip6addr->addr.s6_addr32[2] == addr->s6_addr32[2] &&
+		    ip6addr->addr.s6_addr32[3] == addr->s6_addr32[3])
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void ucast_to_stas(struct hostapd_data *hapd, const u8 *buf, size_t len)
+{
+	struct sta_info *sta;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (!(sta->flags & WLAN_STA_AUTHORIZED))
+			continue;
+		x_snoop_mcast_to_ucast_convert_send(hapd, sta, (u8 *) buf, len);
+	}
+}
+
+
+static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf,
+			 size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct icmpv6_ndmsg *msg;
+	struct in6_addr saddr;
+	struct sta_info *sta;
+	int res;
+	char addrtxt[INET6_ADDRSTRLEN + 1];
+
+	if (len < ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
+		return;
+	msg = (struct icmpv6_ndmsg *) &buf[ETH_HLEN];
+	switch (msg->icmp6h.icmp6_type) {
+	case NEIGHBOR_SOLICITATION:
+		if (len < ETH_HLEN + sizeof(*msg))
+			return;
+		if (msg->opt_type != SOURCE_LL_ADDR)
+			return;
+
+		/*
+		 * IPv6 header may not be 32-bit aligned in the buffer, so use
+		 * a local copy to avoid unaligned reads.
+		 */
+		os_memcpy(&saddr, &msg->ipv6h.ip6_src, sizeof(saddr));
+		if (!(saddr.s6_addr32[0] == 0 && saddr.s6_addr32[1] == 0 &&
+		      saddr.s6_addr32[2] == 0 && saddr.s6_addr32[3] == 0)) {
+			if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN)
+				return;
+			sta = ap_get_sta(hapd, msg->opt_lladdr);
+			if (!sta)
+				return;
+
+			if (sta_has_ip6addr(sta, &saddr))
+				return;
+
+			if (inet_ntop(AF_INET6, &saddr, addrtxt,
+				      sizeof(addrtxt)) == NULL)
+				addrtxt[0] = '\0';
+			wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for "
+				   MACSTR, addrtxt, MAC2STR(sta->addr));
+			hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &saddr);
+			res = hostapd_drv_br_add_ip_neigh(hapd, 6,
+							  (u8 *) &saddr,
+							  128, sta->addr);
+			if (res) {
+				wpa_printf(MSG_ERROR,
+					   "ndisc_snoop: Adding ip neigh failed: %d",
+					   res);
+				return;
+			}
+
+			if (sta_ip6addr_add(sta, &saddr))
+				return;
+		}
+		break;
+	case ROUTER_ADVERTISEMENT:
+		if (hapd->conf->disable_dgaf)
+			ucast_to_stas(hapd, buf, len);
+		break;
+	case NEIGHBOR_ADVERTISEMENT:
+		if (hapd->conf->na_mcast_to_ucast)
+			ucast_to_stas(hapd, buf, len);
+		break;
+	default:
+		break;
+	}
+}
+
+
+int ndisc_snoop_init(struct hostapd_data *hapd)
+{
+	hapd->sock_ndisc = x_snoop_get_l2_packet(hapd, handle_ndisc,
+						 L2_PACKET_FILTER_NDISC);
+	if (hapd->sock_ndisc == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "ndisc_snoop: Failed to initialize L2 packet processing for NDISC packets: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void ndisc_snoop_deinit(struct hostapd_data *hapd)
+{
+	l2_packet_deinit(hapd->sock_ndisc);
+}
diff --git a/hostap/src/ap/ndisc_snoop.h b/hostap/src/ap/ndisc_snoop.h
new file mode 100644
index 0000000..3cc9a55
--- /dev/null
+++ b/hostap/src/ap/ndisc_snoop.h
@@ -0,0 +1,36 @@
+/*
+ * Neighbor Discovery snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NDISC_SNOOP_H
+#define NDISC_SNOOP_H
+
+#if defined(CONFIG_PROXYARP) && defined(CONFIG_IPV6)
+
+int ndisc_snoop_init(struct hostapd_data *hapd);
+void ndisc_snoop_deinit(struct hostapd_data *hapd);
+void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
+
+#else /* CONFIG_PROXYARP && CONFIG_IPV6 */
+
+static inline int ndisc_snoop_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void ndisc_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline void sta_ip6addr_del(struct hostapd_data *hapd,
+				   struct sta_info *sta)
+{
+}
+
+#endif /* CONFIG_PROXYARP && CONFIG_IPV6 */
+
+#endif /* NDISC_SNOOP_H */
diff --git a/hostap/src/ap/p2p_hostapd.c b/hostap/src/ap/p2p_hostapd.c
new file mode 100644
index 0000000..9be640c
--- /dev/null
+++ b/hostap/src/ap/p2p_hostapd.c
@@ -0,0 +1,113 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * 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 "p2p/p2p.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "p2p_hostapd.h"
+
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			    char *buf, size_t buflen)
+{
+	if (sta->p2p_ie == NULL)
+		return 0;
+
+	return p2p_ie_text(sta->p2p_ie, buf, buf + buflen);
+}
+
+
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+			int duration)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Set NoA parameters: count=%u start=%d "
+		   "duration=%d", count, start, duration);
+
+	if (count == 0) {
+		hapd->noa_enabled = 0;
+		hapd->noa_start = 0;
+		hapd->noa_duration = 0;
+	}
+
+	if (count != 255) {
+		wpa_printf(MSG_DEBUG, "P2P: Non-periodic NoA - set "
+			   "NoA parameters");
+		return hostapd_driver_set_noa(hapd, count, start, duration);
+	}
+
+	hapd->noa_enabled = 1;
+	hapd->noa_start = start;
+	hapd->noa_duration = duration;
+
+	if (hapd->num_sta_no_p2p == 0) {
+		wpa_printf(MSG_DEBUG, "P2P: No legacy STAs connected - update "
+			   "periodic NoA parameters");
+		return hostapd_driver_set_noa(hapd, count, start, duration);
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Legacy STA(s) connected - do not enable "
+		   "periodic NoA");
+
+	return 0;
+}
+
+
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "P2P: First non-P2P device connected");
+
+	if (hapd->noa_enabled) {
+		wpa_printf(MSG_DEBUG, "P2P: Disable periodic NoA");
+		hostapd_driver_set_noa(hapd, 0, 0, 0);
+	}
+}
+
+
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Last non-P2P device disconnected");
+
+	if (hapd->noa_enabled) {
+		wpa_printf(MSG_DEBUG, "P2P: Enable periodic NoA");
+		hostapd_driver_set_noa(hapd, 255, hapd->noa_start,
+				       hapd->noa_duration);
+	}
+}
+
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_P2P_MANAGER
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 bitmap;
+	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
+	*eid++ = 4 + 3 + 1;
+	WPA_PUT_BE32(eid, P2P_IE_VENDOR_TYPE);
+	eid += 4;
+
+	*eid++ = P2P_ATTR_MANAGEABILITY;
+	WPA_PUT_LE16(eid, 1);
+	eid += 2;
+	bitmap = P2P_MAN_DEVICE_MANAGEMENT;
+	if (hapd->conf->p2p & P2P_ALLOW_CROSS_CONNECTION)
+		bitmap |= P2P_MAN_CROSS_CONNECTION_PERMITTED;
+	bitmap |= P2P_MAN_COEXISTENCE_OPTIONAL;
+	*eid++ = bitmap;
+
+	return eid;
+}
+#endif /* CONFIG_P2P_MANAGER */
diff --git a/hostap/src/ap/p2p_hostapd.h b/hostap/src/ap/p2p_hostapd.h
new file mode 100644
index 0000000..0e3921c
--- /dev/null
+++ b/hostap/src/ap/p2p_hostapd.h
@@ -0,0 +1,35 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef P2P_HOSTAPD_H
+#define P2P_HOSTAPD_H
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			    char *buf, size_t buflen);
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+			int duration);
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd);
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd);
+
+
+#else /* CONFIG_P2P */
+
+static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd,
+					  struct sta_info *sta,
+					  char *buf, size_t buflen)
+{
+	return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid);
+
+#endif /* P2P_HOSTAPD_H */
diff --git a/hostap/src/ap/peerkey_auth.c b/hostap/src/ap/peerkey_auth.c
new file mode 100644
index 0000000..efc1d7e
--- /dev/null
+++ b/hostap/src/ap/peerkey_auth.c
@@ -0,0 +1,396 @@
+/*
+ * hostapd - PeerKey for Direct Link Setup (DLS)
+ * Copyright (c) 2006-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 "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "wpa_auth.h"
+#include "wpa_auth_i.h"
+#include "wpa_auth_ie.h"
+
+#ifdef CONFIG_PEERKEY
+
+static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx)
+{
+#if 0
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct wpa_stsl_negotiation *neg = timeout_ctx;
+#endif
+
+	/* TODO: ? */
+}
+
+
+struct wpa_stsl_search {
+	const u8 *addr;
+	struct wpa_state_machine *sm;
+};
+
+
+static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx)
+{
+	struct wpa_stsl_search *search = ctx;
+	if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) {
+		search->sm = sm;
+		return 1;
+	}
+	return 0;
+}
+
+
+static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth,
+			       struct wpa_state_machine *sm, const u8 *peer,
+			       u16 mui, u16 error_type)
+{
+	u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN +
+	       2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)];
+	u8 *pos;
+	struct rsn_error_kde error;
+
+	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			"Sending SMK Error");
+
+	pos = kde;
+
+	if (peer) {
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN,
+				  NULL, 0);
+	}
+
+	error.mui = host_to_be16(mui);
+	error.error_type = host_to_be16(error_type);
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR,
+			  (u8 *) &error, sizeof(error), NULL, 0);
+
+	__wpa_send_eapol(wpa_auth, sm,
+			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+			 WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR,
+			 NULL, NULL, kde, pos - kde, 0, 0, 0);
+}
+
+
+void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
+		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+		const u8 *key_data, size_t key_data_len)
+{
+	struct wpa_eapol_ie_parse kde;
+	struct wpa_stsl_search search;
+	u8 *buf, *pos;
+	size_t buf_len;
+
+	if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
+		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1");
+		return;
+	}
+
+	if (kde.rsn_ie == NULL || kde.mac_addr == NULL ||
+	    kde.mac_addr_len < ETH_ALEN) {
+		wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in "
+			   "SMK M1");
+		return;
+	}
+
+	/* Initiator = sm->addr; Peer = kde.mac_addr */
+
+	search.addr = kde.mac_addr;
+	search.sm = NULL;
+	if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
+	    0 || search.sm == NULL) {
+		wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR
+			   " aborted - STA not associated anymore",
+			   MAC2STR(kde.mac_addr));
+		wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
+				   STK_ERR_STA_NR);
+		/* FIX: wpa_stsl_remove(wpa_auth, neg); */
+		return;
+	}
+
+	buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+	buf = os_malloc(buf_len);
+	if (buf == NULL)
+		return;
+	/* Initiator RSN IE */
+	os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len);
+	pos = buf + kde.rsn_ie_len;
+	/* Initiator MAC Address */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN,
+			  NULL, 0);
+
+	/* SMK M2:
+	 * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
+	 *           MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE)
+	 */
+
+	wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG,
+			"Sending SMK M2");
+
+	__wpa_send_eapol(wpa_auth, search.sm,
+			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+			 WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE,
+			 NULL, key->key_nonce, buf, pos - buf, 0, 0, 0);
+
+	os_free(buf);
+}
+
+
+static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth,
+			    struct wpa_state_machine *sm,
+			    struct wpa_eapol_key *key,
+			    struct wpa_eapol_ie_parse *kde,
+			    const u8 *smk)
+{
+	u8 *buf, *pos;
+	size_t buf_len;
+	u32 lifetime;
+
+	/* SMK M4:
+	 * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce,
+	 *           MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE,
+	 *           Lifetime KDE)
+	 */
+
+	buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN +
+		2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN +
+		2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN +
+		2 + RSN_SELECTOR_LEN + sizeof(lifetime);
+	pos = buf = os_malloc(buf_len);
+	if (buf == NULL)
+		return;
+
+	/* Initiator MAC Address */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN,
+			  NULL, 0);
+
+	/* Initiator Nonce */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN,
+			  NULL, 0);
+
+	/* SMK with PNonce */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN,
+			  key->key_nonce, WPA_NONCE_LEN);
+
+	/* Lifetime */
+	lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+			  (u8 *) &lifetime, sizeof(lifetime), NULL, 0);
+
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"Sending SMK M4");
+
+	__wpa_send_eapol(wpa_auth, sm,
+			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+			 WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE,
+			 NULL, key->key_nonce, buf, pos - buf, 0, 1, 0);
+
+	os_free(buf);
+}
+
+
+static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth,
+			    struct wpa_state_machine *sm,
+			    struct wpa_eapol_key *key,
+			    struct wpa_eapol_ie_parse *kde,
+			    const u8 *smk, const u8 *peer)
+{
+	u8 *buf, *pos;
+	size_t buf_len;
+	u32 lifetime;
+
+	/* SMK M5:
+	 * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
+	 *           MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE,
+	 *                             Lifetime KDE))
+	 */
+
+	buf_len = kde->rsn_ie_len +
+		2 + RSN_SELECTOR_LEN + ETH_ALEN +
+		2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN +
+		2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN +
+		2 + RSN_SELECTOR_LEN + sizeof(lifetime);
+	pos = buf = os_malloc(buf_len);
+	if (buf == NULL)
+		return;
+
+	/* Peer RSN IE */
+	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);
+
+	/* PNonce */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce,
+			  WPA_NONCE_LEN, NULL, 0);
+
+	/* SMK and INonce */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN,
+			  kde->nonce, WPA_NONCE_LEN);
+
+	/* Lifetime */
+	lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+			  (u8 *) &lifetime, sizeof(lifetime), NULL, 0);
+
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"Sending SMK M5");
+
+	__wpa_send_eapol(wpa_auth, sm,
+			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+			 WPA_KEY_INFO_SMK_MESSAGE,
+			 NULL, kde->nonce, buf, pos - buf, 0, 1, 0);
+
+	os_free(buf);
+}
+
+
+void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
+		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+		const u8 *key_data, size_t key_data_len)
+{
+	struct wpa_eapol_ie_parse kde;
+	struct wpa_stsl_search search;
+	u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos;
+
+	if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
+		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3");
+		return;
+	}
+
+	if (kde.rsn_ie == NULL ||
+	    kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
+	    kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) {
+		wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or "
+			   "Nonce KDE in SMK M3");
+		return;
+	}
+
+	/* Peer = sm->addr; Initiator = kde.mac_addr;
+	 * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */
+
+	search.addr = kde.mac_addr;
+	search.sm = NULL;
+	if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
+	    0 || search.sm == NULL) {
+		wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR
+			   " aborted - STA not associated anymore",
+			   MAC2STR(kde.mac_addr));
+		wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
+				   STK_ERR_STA_NR);
+		/* FIX: wpa_stsl_remove(wpa_auth, neg); */
+		return;
+	}
+
+	if (random_get_bytes(smk, PMK_LEN)) {
+		wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK");
+		return;
+	}
+
+	/* SMK = PRF-256(Random number, "SMK Derivation",
+	 *               AA || Time || INonce || PNonce)
+	 */
+	os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
+	pos = buf + ETH_ALEN;
+	wpa_get_ntp_timestamp(pos);
+	pos += 8;
+	os_memcpy(pos, kde.nonce, WPA_NONCE_LEN);
+	pos += WPA_NONCE_LEN;
+	os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN);
+#ifdef CONFIG_IEEE80211W
+	sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf),
+		   smk, PMK_LEN);
+#else /* CONFIG_IEEE80211W */
+	sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf),
+		 smk, PMK_LEN);
+#endif /* CONFIG_IEEE80211W */
+
+	wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN);
+
+	wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk);
+	wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr);
+
+	/* Authenticator does not need SMK anymore and it is required to forget
+	 * it. */
+	os_memset(smk, 0, sizeof(*smk));
+}
+
+
+void wpa_smk_error(struct wpa_authenticator *wpa_auth,
+		   struct wpa_state_machine *sm,
+		   const u8 *key_data, size_t key_data_len)
+{
+	struct wpa_eapol_ie_parse kde;
+	struct wpa_stsl_search search;
+	struct rsn_error_kde error;
+	u16 mui, error_type;
+
+	if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
+		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
+		return;
+	}
+
+	if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
+	    kde.error == NULL || kde.error_len < sizeof(error)) {
+		wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in "
+			   "SMK Error");
+		return;
+	}
+
+	search.addr = kde.mac_addr;
+	search.sm = NULL;
+	if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
+	    0 || search.sm == NULL) {
+		wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not "
+			   "associated for SMK Error message from " MACSTR,
+			   MAC2STR(kde.mac_addr), MAC2STR(sm->addr));
+		return;
+	}
+
+	os_memcpy(&error, kde.error, sizeof(error));
+	mui = be_to_host16(error.mui);
+	error_type = be_to_host16(error.error_type);
+	wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			 "STA reported SMK Error: Peer " MACSTR
+			 " MUI %d Error Type %d",
+			 MAC2STR(kde.mac_addr), mui, error_type);
+
+	wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type);
+}
+
+
+int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
+		    struct wpa_stsl_negotiation *neg)
+{
+	struct wpa_stsl_negotiation *pos, *prev;
+
+	if (wpa_auth == NULL)
+		return -1;
+	pos = wpa_auth->stsl_negotiations;
+	prev = NULL;
+	while (pos) {
+		if (pos == neg) {
+			if (prev)
+				prev->next = pos->next;
+			else
+				wpa_auth->stsl_negotiations = pos->next;
+
+			eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos);
+			os_free(pos);
+			return 0;
+		}
+		prev = pos;
+		pos = pos->next;
+	}
+
+	return -1;
+}
+
+#endif /* CONFIG_PEERKEY */
diff --git a/hostap/src/ap/pmksa_cache_auth.c b/hostap/src/ap/pmksa_cache_auth.c
new file mode 100644
index 0000000..877affe
--- /dev/null
+++ b/hostap/src/ap/pmksa_cache_auth.c
@@ -0,0 +1,528 @@
+/*
+ * hostapd - PMKSA cache for IEEE 802.11i RSN
+ * Copyright (c) 2004-2008, 2012-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "radius/radius_das.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "pmksa_cache_auth.h"
+
+
+static const int pmksa_cache_max_entries = 1024;
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
+struct rsn_pmksa_cache {
+#define PMKID_HASH_SIZE 128
+#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
+	struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE];
+	struct rsn_pmksa_cache_entry *pmksa;
+	int pmksa_count;
+
+	void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
+	void *ctx;
+};
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
+
+
+static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
+{
+	os_free(entry->identity);
+	wpabuf_free(entry->cui);
+#ifndef CONFIG_NO_RADIUS
+	radius_free_class(&entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+	bin_clear_free(entry, sizeof(*entry));
+}
+
+
+void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+			    struct rsn_pmksa_cache_entry *entry)
+{
+	struct rsn_pmksa_cache_entry *pos, *prev;
+	unsigned int hash;
+
+	pmksa->pmksa_count--;
+	pmksa->free_cb(entry, pmksa->ctx);
+
+	/* unlink from hash list */
+	hash = PMKID_HASH(entry->pmkid);
+	pos = pmksa->pmkid[hash];
+	prev = NULL;
+	while (pos) {
+		if (pos == entry) {
+			if (prev != NULL)
+				prev->hnext = entry->hnext;
+			else
+				pmksa->pmkid[hash] = entry->hnext;
+			break;
+		}
+		prev = pos;
+		pos = pos->hnext;
+	}
+
+	/* unlink from entry list */
+	pos = pmksa->pmksa;
+	prev = NULL;
+	while (pos) {
+		if (pos == entry) {
+			if (prev != NULL)
+				prev->next = entry->next;
+			else
+				pmksa->pmksa = entry->next;
+			break;
+		}
+		prev = pos;
+		pos = pos->next;
+	}
+
+	_pmksa_cache_free_entry(entry);
+}
+
+
+static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+{
+	struct rsn_pmksa_cache *pmksa = eloop_ctx;
+	struct os_reltime 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));
+		pmksa_cache_free_entry(pmksa, pmksa->pmksa);
+	}
+
+	pmksa_cache_set_expiration(pmksa);
+}
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
+{
+	int sec;
+	struct os_reltime now;
+
+	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+	if (pmksa->pmksa == NULL)
+		return;
+	os_get_reltime(&now);
+	sec = pmksa->pmksa->expiration - now.sec;
+	if (sec < 0)
+		sec = 0;
+	eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
+}
+
+
+static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
+					struct eapol_state_machine *eapol)
+{
+	if (eapol == NULL)
+		return;
+
+	if (eapol->identity) {
+		entry->identity = os_malloc(eapol->identity_len);
+		if (entry->identity) {
+			entry->identity_len = eapol->identity_len;
+			os_memcpy(entry->identity, eapol->identity,
+				  eapol->identity_len);
+		}
+	}
+
+	if (eapol->radius_cui)
+		entry->cui = wpabuf_dup(eapol->radius_cui);
+
+#ifndef CONFIG_NO_RADIUS
+	radius_copy_class(&entry->radius_class, &eapol->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+
+	entry->eap_type_authsrv = eapol->eap_type_authsrv;
+	entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id;
+
+	entry->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi;
+	entry->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo;
+}
+
+
+void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
+			       struct eapol_state_machine *eapol)
+{
+	if (entry == NULL || eapol == NULL)
+		return;
+
+	if (entry->identity) {
+		os_free(eapol->identity);
+		eapol->identity = os_malloc(entry->identity_len);
+		if (eapol->identity) {
+			eapol->identity_len = entry->identity_len;
+			os_memcpy(eapol->identity, entry->identity,
+				  entry->identity_len);
+		}
+		wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA",
+				  eapol->identity, eapol->identity_len);
+	}
+
+	if (entry->cui) {
+		wpabuf_free(eapol->radius_cui);
+		eapol->radius_cui = wpabuf_dup(entry->cui);
+	}
+
+#ifndef CONFIG_NO_RADIUS
+	radius_free_class(&eapol->radius_class);
+	radius_copy_class(&eapol->radius_class, &entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+	if (eapol->radius_class.attr) {
+		wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from "
+			   "PMKSA", (unsigned long) eapol->radius_class.count);
+	}
+
+	eapol->eap_type_authsrv = entry->eap_type_authsrv;
+	((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id;
+
+	eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi;
+	eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo;
+}
+
+
+static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
+				   struct rsn_pmksa_cache_entry *entry)
+{
+	struct rsn_pmksa_cache_entry *pos, *prev;
+	int hash;
+
+	/* Add the new entry; order by expiration time */
+	pos = pmksa->pmksa;
+	prev = NULL;
+	while (pos) {
+		if (pos->expiration > entry->expiration)
+			break;
+		prev = pos;
+		pos = pos->next;
+	}
+	if (prev == NULL) {
+		entry->next = pmksa->pmksa;
+		pmksa->pmksa = entry;
+	} else {
+		entry->next = prev->next;
+		prev->next = entry;
+	}
+
+	hash = PMKID_HASH(entry->pmkid);
+	entry->hnext = pmksa->pmkid[hash];
+	pmksa->pmkid[hash] = entry;
+
+	pmksa->pmksa_count++;
+	if (prev == NULL)
+		pmksa_cache_set_expiration(pmksa);
+	wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
+		   MAC2STR(entry->spa));
+	wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
+}
+
+
+/**
+ * pmksa_cache_auth_add - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @session_timeout: Session timeout
+ * @eapol: Pointer to EAPOL state machine data
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
+ *
+ * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
+ * cache. If an old entry is already in the cache for the same Supplicant,
+ * this entry will be replaced with the new entry. PMKID will be calculated
+ * based on the PMK.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
+		     const u8 *pmk, size_t pmk_len,
+		     const u8 *kck, size_t kck_len,
+		     const u8 *aa, const u8 *spa, int session_timeout,
+		     struct eapol_state_machine *eapol, int akmp)
+{
+	struct rsn_pmksa_cache_entry *entry, *pos;
+	struct os_reltime now;
+
+	if (pmk_len > PMK_LEN)
+		return NULL;
+
+	if (wpa_key_mgmt_suite_b(akmp) && !kck)
+		return NULL;
+
+	entry = os_zalloc(sizeof(*entry));
+	if (entry == NULL)
+		return NULL;
+	os_memcpy(entry->pmk, pmk, pmk_len);
+	entry->pmk_len = pmk_len;
+	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
+	else if (wpa_key_mgmt_suite_b(akmp))
+		rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
+	else
+		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
+			  wpa_key_mgmt_sha256(akmp));
+	os_get_reltime(&now);
+	entry->expiration = now.sec;
+	if (session_timeout > 0)
+		entry->expiration += session_timeout;
+	else
+		entry->expiration += dot11RSNAConfigPMKLifetime;
+	entry->akmp = akmp;
+	os_memcpy(entry->spa, spa, ETH_ALEN);
+	pmksa_cache_from_eapol_data(entry, eapol);
+
+	/* Replace an old entry for the same STA (if found) with the new entry
+	 */
+	pos = pmksa_cache_auth_get(pmksa, spa, NULL);
+	if (pos)
+		pmksa_cache_free_entry(pmksa, pos);
+
+	if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
+		/* Remove the oldest entry to make room for the new entry */
+		wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
+			   "entry (for " MACSTR ") to make room for new one",
+			   MAC2STR(pmksa->pmksa->spa));
+		pmksa_cache_free_entry(pmksa, pmksa->pmksa);
+	}
+
+	pmksa_cache_link_entry(pmksa, entry);
+
+	return entry;
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
+		    const struct rsn_pmksa_cache_entry *old_entry,
+		    const u8 *aa, const u8 *pmkid)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	entry = os_zalloc(sizeof(*entry));
+	if (entry == NULL)
+		return NULL;
+	os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
+	os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len);
+	entry->pmk_len = old_entry->pmk_len;
+	entry->expiration = old_entry->expiration;
+	entry->akmp = old_entry->akmp;
+	os_memcpy(entry->spa, old_entry->spa, ETH_ALEN);
+	entry->opportunistic = 1;
+	if (old_entry->identity) {
+		entry->identity = os_malloc(old_entry->identity_len);
+		if (entry->identity) {
+			entry->identity_len = old_entry->identity_len;
+			os_memcpy(entry->identity, old_entry->identity,
+				  old_entry->identity_len);
+		}
+	}
+	if (old_entry->cui)
+		entry->cui = wpabuf_dup(old_entry->cui);
+#ifndef CONFIG_NO_RADIUS
+	radius_copy_class(&entry->radius_class, &old_entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+	entry->eap_type_authsrv = old_entry->eap_type_authsrv;
+	entry->vlan_id = old_entry->vlan_id;
+	entry->opportunistic = 1;
+
+	pmksa_cache_link_entry(pmksa, entry);
+
+	return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_deinit - Free all entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ */
+void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
+{
+	struct rsn_pmksa_cache_entry *entry, *prev;
+	int i;
+
+	if (pmksa == NULL)
+		return;
+
+	entry = pmksa->pmksa;
+	while (entry) {
+		prev = entry;
+		entry = entry->next;
+		_pmksa_cache_free_entry(prev);
+	}
+	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+	pmksa->pmksa_count = 0;
+	pmksa->pmksa = NULL;
+	for (i = 0; i < PMKID_HASH_SIZE; i++)
+		pmksa->pmkid[i] = NULL;
+	os_free(pmksa);
+}
+
+
+/**
+ * pmksa_cache_auth_get - Fetch a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @spa: Supplicant address or %NULL to match any
+ * @pmkid: PMKID or %NULL to match any
+ * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
+		     const u8 *spa, const u8 *pmkid)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	if (pmkid) {
+		for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry;
+		     entry = entry->hnext) {
+			if ((spa == NULL ||
+			     os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
+			    os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
+				return entry;
+		}
+	} else {
+		for (entry = pmksa->pmksa; entry; entry = entry->next) {
+			if (spa == NULL ||
+			    os_memcmp(entry->spa, spa, ETH_ALEN) == 0)
+				return entry;
+		}
+	}
+
+	return NULL;
+}
+
+
+/**
+ * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: PMKID
+ * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
+ *
+ * Use opportunistic key caching (OKC) to find a PMK for a supplicant.
+ */
+struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
+	struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
+	const u8 *pmkid)
+{
+	struct rsn_pmksa_cache_entry *entry;
+	u8 new_pmkid[PMKID_LEN];
+
+	for (entry = pmksa->pmksa; entry; entry = entry->next) {
+		if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
+			continue;
+		rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
+			  wpa_key_mgmt_sha256(entry->akmp));
+		if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
+			return entry;
+	}
+	return NULL;
+}
+
+
+/**
+ * pmksa_cache_auth_init - Initialize PMKSA cache
+ * @free_cb: Callback function to be called when a PMKSA cache entry is freed
+ * @ctx: Context pointer for free_cb function
+ * Returns: Pointer to PMKSA cache data or %NULL on failure
+ */
+struct rsn_pmksa_cache *
+pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+				      void *ctx), void *ctx)
+{
+	struct rsn_pmksa_cache *pmksa;
+
+	pmksa = os_zalloc(sizeof(*pmksa));
+	if (pmksa) {
+		pmksa->free_cb = free_cb;
+		pmksa->ctx = ctx;
+	}
+
+	return pmksa;
+}
+
+
+static int das_attr_match(struct rsn_pmksa_cache_entry *entry,
+			  struct radius_das_attrs *attr)
+{
+	int match = 0;
+
+	if (attr->sta_addr) {
+		if (os_memcmp(attr->sta_addr, entry->spa, ETH_ALEN) != 0)
+			return 0;
+		match++;
+	}
+
+	if (attr->acct_multi_session_id) {
+		char buf[20];
+
+		if (attr->acct_multi_session_id_len != 17)
+			return 0;
+		os_snprintf(buf, sizeof(buf), "%08X+%08X",
+			    entry->acct_multi_session_id_hi,
+			    entry->acct_multi_session_id_lo);
+		if (os_memcmp(attr->acct_multi_session_id, buf, 17) != 0)
+			return 0;
+		match++;
+	}
+
+	if (attr->cui) {
+		if (!entry->cui ||
+		    attr->cui_len != wpabuf_len(entry->cui) ||
+		    os_memcmp(attr->cui, wpabuf_head(entry->cui),
+			      attr->cui_len) != 0)
+			return 0;
+		match++;
+	}
+
+	if (attr->user_name) {
+		if (!entry->identity ||
+		    attr->user_name_len != entry->identity_len ||
+		    os_memcmp(attr->user_name, entry->identity,
+			      attr->user_name_len) != 0)
+			return 0;
+		match++;
+	}
+
+	return match;
+}
+
+
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+					   struct radius_das_attrs *attr)
+{
+	int found = 0;
+	struct rsn_pmksa_cache_entry *entry, *prev;
+
+	if (attr->acct_session_id)
+		return -1;
+
+	entry = pmksa->pmksa;
+	while (entry) {
+		if (das_attr_match(entry, attr)) {
+			found++;
+			prev = entry;
+			entry = entry->next;
+			pmksa_cache_free_entry(pmksa, prev);
+			continue;
+		}
+		entry = entry->next;
+	}
+
+	return found ? 0 : -1;
+}
diff --git a/hostap/src/ap/pmksa_cache_auth.h b/hostap/src/ap/pmksa_cache_auth.h
new file mode 100644
index 0000000..8b7be12
--- /dev/null
+++ b/hostap/src/ap/pmksa_cache_auth.h
@@ -0,0 +1,67 @@
+/*
+ * hostapd - PMKSA cache for IEEE 802.11i RSN
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PMKSA_CACHE_H
+#define PMKSA_CACHE_H
+
+#include "radius/radius.h"
+
+/**
+ * struct rsn_pmksa_cache_entry - PMKSA cache entry
+ */
+struct rsn_pmksa_cache_entry {
+	struct rsn_pmksa_cache_entry *next, *hnext;
+	u8 pmkid[PMKID_LEN];
+	u8 pmk[PMK_LEN];
+	size_t pmk_len;
+	os_time_t expiration;
+	int akmp; /* WPA_KEY_MGMT_* */
+	u8 spa[ETH_ALEN];
+
+	u8 *identity;
+	size_t identity_len;
+	struct wpabuf *cui;
+	struct radius_class_data radius_class;
+	u8 eap_type_authsrv;
+	int vlan_id;
+	int opportunistic;
+
+	u32 acct_multi_session_id_hi;
+	u32 acct_multi_session_id_lo;
+};
+
+struct rsn_pmksa_cache;
+
+struct rsn_pmksa_cache *
+pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+				      void *ctx), void *ctx);
+void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
+		     const u8 *spa, const u8 *pmkid);
+struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
+	struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *aa,
+	const u8 *pmkid);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
+		     const u8 *pmk, size_t pmk_len,
+		     const u8 *kck, size_t kck_len,
+		     const u8 *aa, const u8 *spa, int session_timeout,
+		     struct eapol_state_machine *eapol, int akmp);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
+		    const struct rsn_pmksa_cache_entry *old_entry,
+		    const u8 *aa, const u8 *pmkid);
+void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
+			       struct eapol_state_machine *eapol);
+void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+			    struct rsn_pmksa_cache_entry *entry);
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+					   struct radius_das_attrs *attr);
+
+#endif /* PMKSA_CACHE_H */
diff --git a/hostap/src/ap/preauth_auth.c b/hostap/src/ap/preauth_auth.c
new file mode 100644
index 0000000..3e0c800
--- /dev/null
+++ b/hostap/src/ap/preauth_auth.c
@@ -0,0 +1,273 @@
+/*
+ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
+ * Copyright (c) 2004-2007, 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"
+
+#ifdef CONFIG_RSN_PREAUTH
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "common/wpa_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_1x.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+
+#ifndef ETH_P_PREAUTH
+#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
+#endif /* ETH_P_PREAUTH */
+
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
+struct rsn_preauth_interface {
+	struct rsn_preauth_interface *next;
+	struct hostapd_data *hapd;
+	struct l2_packet_data *l2;
+	char *ifname;
+	int ifindex;
+};
+
+
+static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
+				const u8 *buf, size_t len)
+{
+	struct rsn_preauth_interface *piface = ctx;
+	struct hostapd_data *hapd = piface->hapd;
+	struct ieee802_1x_hdr *hdr;
+	struct sta_info *sta;
+	struct l2_ethhdr *ethhdr;
+
+	wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet "
+		   "from interface '%s'", piface->ifname);
+	if (len < sizeof(*ethhdr) + sizeof(*hdr)) {
+		wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet "
+			   "(len=%lu)", (unsigned long) len);
+		return;
+	}
+
+	ethhdr = (struct l2_ethhdr *) buf;
+	hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
+
+	if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address "
+			   MACSTR, MAC2STR(ethhdr->h_dest));
+		return;
+	}
+
+	sta = ap_get_sta(hapd, ethhdr->h_source);
+	if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+		wpa_printf(MSG_DEBUG, "RSN: pre-auth for already association "
+			   "STA " MACSTR, MAC2STR(sta->addr));
+		return;
+	}
+	if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) {
+		sta = ap_sta_add(hapd, ethhdr->h_source);
+		if (sta == NULL)
+			return;
+		sta->flags = WLAN_STA_PREAUTH;
+
+		ieee802_1x_new_station(hapd, sta);
+		if (sta->eapol_sm == NULL) {
+			ap_free_sta(hapd, sta);
+			sta = NULL;
+		} else {
+			sta->eapol_sm->radius_identifier = -1;
+			sta->eapol_sm->portValid = TRUE;
+			sta->eapol_sm->flags |= EAPOL_SM_PREAUTH;
+		}
+	}
+	if (sta == NULL)
+		return;
+	sta->preauth_iface = piface;
+	ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1),
+			   len - sizeof(*ethhdr));
+}
+
+
+static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname)
+{
+	struct rsn_preauth_interface *piface;
+
+	wpa_printf(MSG_DEBUG, "RSN pre-auth interface '%s'", ifname);
+
+	piface = os_zalloc(sizeof(*piface));
+	if (piface == NULL)
+		return -1;
+	piface->hapd = hapd;
+
+	piface->ifname = os_strdup(ifname);
+	if (piface->ifname == NULL) {
+		goto fail1;
+	}
+
+	piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH,
+				    rsn_preauth_receive, piface, 1);
+	if (piface->l2 == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to open register layer 2 access "
+			   "to ETH_P_PREAUTH");
+		goto fail2;
+	}
+
+	piface->next = hapd->preauth_iface;
+	hapd->preauth_iface = piface;
+	return 0;
+
+fail2:
+	os_free(piface->ifname);
+fail1:
+	os_free(piface);
+	return -1;
+}
+
+
+void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
+{
+	struct rsn_preauth_interface *piface, *prev;
+
+	piface = hapd->preauth_iface;
+	hapd->preauth_iface = NULL;
+	while (piface) {
+		prev = piface;
+		piface = piface->next;
+		l2_packet_deinit(prev->l2);
+		os_free(prev->ifname);
+		os_free(prev);
+	}
+}
+
+
+int rsn_preauth_iface_init(struct hostapd_data *hapd)
+{
+	char *tmp, *start, *end;
+
+	if (hapd->conf->rsn_preauth_interfaces == NULL)
+		return 0;
+
+	tmp = os_strdup(hapd->conf->rsn_preauth_interfaces);
+	if (tmp == NULL)
+		return -1;
+	start = tmp;
+	for (;;) {
+		while (*start == ' ')
+			start++;
+		if (*start == '\0')
+			break;
+		end = os_strchr(start, ' ');
+		if (end)
+			*end = '\0';
+
+		if (rsn_preauth_iface_add(hapd, start)) {
+			rsn_preauth_iface_deinit(hapd);
+			os_free(tmp);
+			return -1;
+		}
+
+		if (end)
+			start = end + 1;
+		else
+			break;
+	}
+	os_free(tmp);
+	return 0;
+}
+
+
+static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for "
+		   MACSTR, MAC2STR(sta->addr));
+	ap_free_sta(hapd, sta);
+}
+
+
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+			  int success)
+{
+	const u8 *key;
+	size_t len;
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+		       HOSTAPD_LEVEL_INFO, "pre-authentication %s",
+		       success ? "succeeded" : "failed");
+
+	key = ieee802_1x_get_key(sta->eapol_sm, &len);
+	if (len > PMK_LEN)
+		len = PMK_LEN;
+	if (success && key) {
+		if (wpa_auth_pmksa_add_preauth(hapd->wpa_auth, key, len,
+					       sta->addr,
+					       dot11RSNAConfigPMKLifetime,
+					       sta->eapol_sm) == 0) {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "added PMKSA cache entry (pre-auth)");
+		} else {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "failed to add PMKSA cache entry "
+				       "(pre-auth)");
+		}
+	}
+
+	/*
+	 * Finish STA entry removal from timeout in order to avoid freeing
+	 * STA data before the caller has finished processing.
+	 */
+	eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta);
+}
+
+
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+		      u8 *buf, size_t len)
+{
+	struct rsn_preauth_interface *piface;
+	struct l2_ethhdr *ethhdr;
+
+	piface = hapd->preauth_iface;
+	while (piface) {
+		if (piface == sta->preauth_iface)
+			break;
+		piface = piface->next;
+	}
+
+	if (piface == NULL) {
+		wpa_printf(MSG_DEBUG, "RSN: Could not find pre-authentication "
+			   "interface for " MACSTR, MAC2STR(sta->addr));
+		return;
+	}
+
+	ethhdr = os_malloc(sizeof(*ethhdr) + len);
+	if (ethhdr == NULL)
+		return;
+
+	os_memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN);
+	os_memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN);
+	ethhdr->h_proto = host_to_be16(ETH_P_PREAUTH);
+	os_memcpy(ethhdr + 1, buf, len);
+
+	if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr,
+			   sizeof(*ethhdr) + len) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to send preauth packet using "
+			   "l2_packet_send\n");
+	}
+	os_free(ethhdr);
+}
+
+
+void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	eloop_cancel_timeout(rsn_preauth_finished_cb, hapd, sta);
+}
+
+#endif /* CONFIG_RSN_PREAUTH */
diff --git a/hostap/src/ap/preauth_auth.h b/hostap/src/ap/preauth_auth.h
new file mode 100644
index 0000000..69fb356
--- /dev/null
+++ b/hostap/src/ap/preauth_auth.h
@@ -0,0 +1,52 @@
+/*
+ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PREAUTH_H
+#define PREAUTH_H
+
+#ifdef CONFIG_RSN_PREAUTH
+
+int rsn_preauth_iface_init(struct hostapd_data *hapd);
+void rsn_preauth_iface_deinit(struct hostapd_data *hapd);
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+			  int success);
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+		      u8 *buf, size_t len);
+void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta);
+
+#else /* CONFIG_RSN_PREAUTH */
+
+static inline int rsn_preauth_iface_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline void rsn_preauth_finished(struct hostapd_data *hapd,
+					struct sta_info *sta,
+					int success)
+{
+}
+
+static inline void rsn_preauth_send(struct hostapd_data *hapd,
+				    struct sta_info *sta,
+				    u8 *buf, size_t len)
+{
+}
+
+static inline void rsn_preauth_free_station(struct hostapd_data *hapd,
+					    struct sta_info *sta)
+{
+}
+
+#endif /* CONFIG_RSN_PREAUTH */
+
+#endif /* PREAUTH_H */
diff --git a/hostap/src/ap/sta_info.c b/hostap/src/ap/sta_info.c
new file mode 100644
index 0000000..d64307c
--- /dev/null
+++ b/hostap/src/ap/sta_info.c
@@ -0,0 +1,1169 @@
+/*
+ * hostapd / Station table
+ * 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.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "common/sae.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "p2p/p2p.h"
+#include "fst/fst.h"
+#include "hostapd.h"
+#include "accounting.h"
+#include "ieee802_1x.h"
+#include "ieee802_11.h"
+#include "ieee802_11_auth.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+#include "ap_config.h"
+#include "beacon.h"
+#include "ap_mlme.h"
+#include "vlan_init.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "gas_serv.h"
+#include "wnm_ap.h"
+#include "ndisc_snoop.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
+static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx);
+#endif /* CONFIG_IEEE80211W */
+static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta);
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+		    int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+			      void *ctx),
+		    void *ctx)
+{
+	struct sta_info *sta;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (cb(hapd, sta, ctx))
+			return 1;
+	}
+
+	return 0;
+}
+
+
+struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+{
+	struct sta_info *s;
+
+	s = hapd->sta_hash[STA_HASH(sta)];
+	while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
+		s = s->hnext;
+	return s;
+}
+
+
+#ifdef CONFIG_P2P
+struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		const u8 *p2p_dev_addr;
+
+		if (sta->p2p_ie == NULL)
+			continue;
+
+		p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+		if (p2p_dev_addr == NULL)
+			continue;
+
+		if (os_memcmp(p2p_dev_addr, addr, ETH_ALEN) == 0)
+			return sta;
+	}
+
+	return NULL;
+}
+#endif /* CONFIG_P2P */
+
+
+static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct sta_info *tmp;
+
+	if (hapd->sta_list == sta) {
+		hapd->sta_list = sta->next;
+		return;
+	}
+
+	tmp = hapd->sta_list;
+	while (tmp != NULL && tmp->next != sta)
+		tmp = tmp->next;
+	if (tmp == NULL) {
+		wpa_printf(MSG_DEBUG, "Could not remove STA " MACSTR " from "
+			   "list.", MAC2STR(sta->addr));
+	} else
+		tmp->next = sta->next;
+}
+
+
+void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)];
+	hapd->sta_hash[STA_HASH(sta->addr)] = sta;
+}
+
+
+static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct sta_info *s;
+
+	s = hapd->sta_hash[STA_HASH(sta->addr)];
+	if (s == NULL) return;
+	if (os_memcmp(s->addr, sta->addr, 6) == 0) {
+		hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext;
+		return;
+	}
+
+	while (s->hnext != NULL &&
+	       os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0)
+		s = s->hnext;
+	if (s->hnext != NULL)
+		s->hnext = s->hnext->hnext;
+	else
+		wpa_printf(MSG_DEBUG, "AP: could not remove STA " MACSTR
+			   " from hash table", MAC2STR(sta->addr));
+}
+
+
+void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	sta_ip6addr_del(hapd, sta);
+}
+
+
+void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int set_beacon = 0;
+
+	accounting_sta_stop(hapd, sta);
+
+	/* just in case */
+	ap_sta_set_authorized(hapd, sta, 0);
+
+	if (sta->flags & WLAN_STA_WDS)
+		hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
+
+	if (sta->ipaddr)
+		hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+	ap_sta_ip6addr_del(hapd, sta);
+
+	if (!hapd->iface->driver_ap_teardown &&
+	    !(sta->flags & WLAN_STA_PREAUTH))
+		hostapd_drv_sta_remove(hapd, sta->addr);
+
+#ifndef CONFIG_NO_VLAN
+	if (sta->vlan_id_bound) {
+		/*
+		 * Need to remove the STA entry before potentially removing the
+		 * VLAN.
+		 */
+		if (hapd->iface->driver_ap_teardown &&
+		    !(sta->flags & WLAN_STA_PREAUTH))
+			hostapd_drv_sta_remove(hapd, sta->addr);
+		vlan_remove_dynamic(hapd, sta->vlan_id_bound);
+	}
+#endif /* CONFIG_NO_VLAN */
+
+	ap_sta_hash_del(hapd, sta);
+	ap_sta_list_del(hapd, sta);
+
+	if (sta->aid > 0)
+		hapd->sta_aid[(sta->aid - 1) / 32] &=
+			~BIT((sta->aid - 1) % 32);
+
+	hapd->num_sta--;
+	if (sta->nonerp_set) {
+		sta->nonerp_set = 0;
+		hapd->iface->num_sta_non_erp--;
+		if (hapd->iface->num_sta_non_erp == 0)
+			set_beacon++;
+	}
+
+	if (sta->no_short_slot_time_set) {
+		sta->no_short_slot_time_set = 0;
+		hapd->iface->num_sta_no_short_slot_time--;
+		if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+		    && hapd->iface->num_sta_no_short_slot_time == 0)
+			set_beacon++;
+	}
+
+	if (sta->no_short_preamble_set) {
+		sta->no_short_preamble_set = 0;
+		hapd->iface->num_sta_no_short_preamble--;
+		if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+		    && hapd->iface->num_sta_no_short_preamble == 0)
+			set_beacon++;
+	}
+
+	if (sta->no_ht_gf_set) {
+		sta->no_ht_gf_set = 0;
+		hapd->iface->num_sta_ht_no_gf--;
+	}
+
+	if (sta->no_ht_set) {
+		sta->no_ht_set = 0;
+		hapd->iface->num_sta_no_ht--;
+	}
+
+	if (sta->ht_20mhz_set) {
+		sta->ht_20mhz_set = 0;
+		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;
+		hapd->num_sta_no_p2p--;
+		if (hapd->num_sta_no_p2p == 0)
+			hostapd_p2p_non_p2p_sta_disconnected(hapd);
+	}
+#endif /* CONFIG_P2P */
+
+#if defined(NEED_AP_MLME) && defined(CONFIG_IEEE80211N)
+	if (hostapd_ht_operation_update(hapd->iface) > 0)
+		set_beacon++;
+#endif /* NEED_AP_MLME && CONFIG_IEEE80211N */
+
+#ifdef CONFIG_MESH
+	if (hapd->mesh_sta_free_cb)
+		hapd->mesh_sta_free_cb(sta);
+#endif /* CONFIG_MESH */
+
+	if (set_beacon)
+		ieee802_11_set_beacons(hapd->iface);
+
+	wpa_printf(MSG_DEBUG, "%s: cancel ap_handle_timer for " MACSTR,
+		   __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);
+	sae_clear_retransmit_timer(hapd, sta);
+
+	ieee802_1x_free_station(sta);
+	wpa_auth_sta_deinit(sta->wpa_sm);
+	rsn_preauth_free_station(hapd, sta);
+#ifndef CONFIG_NO_RADIUS
+	if (hapd->radius)
+		radius_client_flush_auth(hapd->radius, sta->addr);
+#endif /* CONFIG_NO_RADIUS */
+
+	os_free(sta->challenge);
+
+#ifdef CONFIG_IEEE80211W
+	os_free(sta->sa_query_trans_id);
+	eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_P2P
+	p2p_group_notif_disassoc(hapd->p2p_group, sta->addr);
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_INTERWORKING
+	if (sta->gas_dialog) {
+		int i;
+		for (i = 0; i < GAS_DIALOG_MAX; i++)
+			gas_serv_dialog_clear(&sta->gas_dialog[i]);
+		os_free(sta->gas_dialog);
+	}
+#endif /* CONFIG_INTERWORKING */
+
+	wpabuf_free(sta->wps_ie);
+	wpabuf_free(sta->p2p_ie);
+	wpabuf_free(sta->hs20_ie);
+#ifdef CONFIG_FST
+	wpabuf_free(sta->mb_ies);
+#endif /* CONFIG_FST */
+
+	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);
+	os_free(sta->sae);
+#endif /* CONFIG_SAE */
+
+	os_free(sta);
+}
+
+
+void hostapd_free_stas(struct hostapd_data *hapd)
+{
+	struct sta_info *sta, *prev;
+
+	sta = hapd->sta_list;
+
+	while (sta) {
+		prev = sta;
+		if (sta->flags & WLAN_STA_AUTH) {
+			mlme_deauthenticate_indication(
+				hapd, sta, WLAN_REASON_UNSPECIFIED);
+		}
+		sta = sta->next;
+		wpa_printf(MSG_DEBUG, "Removing station " MACSTR,
+			   MAC2STR(prev->addr));
+		ap_free_sta(hapd, prev);
+	}
+}
+
+
+/**
+ * ap_handle_timer - Per STA timer handler
+ * @eloop_ctx: struct hostapd_data *
+ * @timeout_ctx: struct sta_info *
+ *
+ * This function is called to check station activity and to remove inactive
+ * stations.
+ */
+void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	unsigned long next_time = 0;
+	int reason;
+
+	wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d",
+		   __func__, MAC2STR(sta->addr), sta->flags,
+		   sta->timeout_next);
+	if (sta->timeout_next == STA_REMOVE) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+			       "local deauth request");
+		ap_free_sta(hapd, sta);
+		return;
+	}
+
+	if ((sta->flags & WLAN_STA_ASSOC) &&
+	    (sta->timeout_next == STA_NULLFUNC ||
+	     sta->timeout_next == STA_DISASSOC)) {
+		int inactive_sec;
+		/*
+		 * Add random value to timeout so that we don't end up bouncing
+		 * all stations at the same time if we have lots of associated
+		 * stations that are idle (but keep re-associating).
+		 */
+		int fuzz = os_random() % 20;
+		inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr);
+		if (inactive_sec == -1) {
+			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+				"Check inactivity: Could not "
+				"get station info from kernel driver for "
+				MACSTR, MAC2STR(sta->addr));
+			/*
+			 * The driver may not support this functionality.
+			 * Anyway, try again after the next inactivity timeout,
+			 * but do not disconnect the station now.
+			 */
+			next_time = hapd->conf->ap_max_inactivity + fuzz;
+		} else if (inactive_sec == -ENOENT) {
+			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+				"Station " MACSTR " has lost its driver entry",
+				MAC2STR(sta->addr));
+
+			/* Avoid sending client probe on removed client */
+			sta->timeout_next = STA_DISASSOC;
+			goto skip_poll;
+		} else if (inactive_sec < hapd->conf->ap_max_inactivity) {
+			/* station activity detected; reset timeout state */
+			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+				"Station " MACSTR " has been active %is ago",
+				MAC2STR(sta->addr), inactive_sec);
+			sta->timeout_next = STA_NULLFUNC;
+			next_time = hapd->conf->ap_max_inactivity + fuzz -
+				inactive_sec;
+		} else {
+			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+				"Station " MACSTR " has been "
+				"inactive too long: %d sec, max allowed: %d",
+				MAC2STR(sta->addr), inactive_sec,
+				hapd->conf->ap_max_inactivity);
+
+			if (hapd->conf->skip_inactivity_poll)
+				sta->timeout_next = STA_DISASSOC;
+		}
+	}
+
+	if ((sta->flags & WLAN_STA_ASSOC) &&
+	    sta->timeout_next == STA_DISASSOC &&
+	    !(sta->flags & WLAN_STA_PENDING_POLL) &&
+	    !hapd->conf->skip_inactivity_poll) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+			" has ACKed data poll", MAC2STR(sta->addr));
+		/* data nullfunc frame poll did not produce TX errors; assume
+		 * station ACKed it */
+		sta->timeout_next = STA_NULLFUNC;
+		next_time = hapd->conf->ap_max_inactivity;
+	}
+
+skip_poll:
+	if (next_time) {
+		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+			   "for " MACSTR " (%lu seconds)",
+			   __func__, MAC2STR(sta->addr), next_time);
+		eloop_register_timeout(next_time, 0, ap_handle_timer, hapd,
+				       sta);
+		return;
+	}
+
+	if (sta->timeout_next == STA_NULLFUNC &&
+	    (sta->flags & WLAN_STA_ASSOC)) {
+		wpa_printf(MSG_DEBUG, "  Polling STA");
+		sta->flags |= WLAN_STA_PENDING_POLL;
+		hostapd_drv_poll_client(hapd, hapd->own_addr, sta->addr,
+					sta->flags & WLAN_STA_WMM);
+	} else if (sta->timeout_next != STA_REMOVE) {
+		int deauth = sta->timeout_next == STA_DEAUTH;
+
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+			"Timeout, sending %s info to STA " MACSTR,
+			deauth ? "deauthentication" : "disassociation",
+			MAC2STR(sta->addr));
+
+		if (deauth) {
+			hostapd_drv_sta_deauth(
+				hapd, sta->addr,
+				WLAN_REASON_PREV_AUTH_NOT_VALID);
+		} else {
+			reason = (sta->timeout_next == STA_DISASSOC) ?
+				WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY :
+				WLAN_REASON_PREV_AUTH_NOT_VALID;
+
+			hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
+		}
+	}
+
+	switch (sta->timeout_next) {
+	case STA_NULLFUNC:
+		sta->timeout_next = STA_DISASSOC;
+		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+			   "for " MACSTR " (%d seconds - AP_DISASSOC_DELAY)",
+			   __func__, MAC2STR(sta->addr), AP_DISASSOC_DELAY);
+		eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer,
+				       hapd, sta);
+		break;
+	case STA_DISASSOC:
+	case STA_DISASSOC_FROM_CLI:
+		ap_sta_set_authorized(hapd, sta, 0);
+		sta->flags &= ~WLAN_STA_ASSOC;
+		ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+		if (!sta->acct_terminate_cause)
+			sta->acct_terminate_cause =
+				RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
+		accounting_sta_stop(hapd, sta);
+		ieee802_1x_free_station(sta);
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "disassociated due to "
+			       "inactivity");
+		reason = (sta->timeout_next == STA_DISASSOC) ?
+			WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY :
+			WLAN_REASON_PREV_AUTH_NOT_VALID;
+		sta->timeout_next = STA_DEAUTH;
+		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+			   "for " MACSTR " (%d seconds - AP_DEAUTH_DELAY)",
+			   __func__, MAC2STR(sta->addr), AP_DEAUTH_DELAY);
+		eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
+				       hapd, sta);
+		mlme_disassociate_indication(hapd, sta, reason);
+		break;
+	case STA_DEAUTH:
+	case STA_REMOVE:
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+			       "inactivity (timer DEAUTH/REMOVE)");
+		if (!sta->acct_terminate_cause)
+			sta->acct_terminate_cause =
+				RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
+		mlme_deauthenticate_indication(
+			hapd, sta,
+			WLAN_REASON_PREV_AUTH_NOT_VALID);
+		ap_free_sta(hapd, sta);
+		break;
+	}
+}
+
+
+static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	if (!(sta->flags & WLAN_STA_AUTH)) {
+		if (sta->flags & WLAN_STA_GAS) {
+			wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA "
+				   "entry " MACSTR, MAC2STR(sta->addr));
+			ap_free_sta(hapd, sta);
+		}
+		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,
+		       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+		       "session timeout");
+	sta->acct_terminate_cause =
+		RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT;
+	ap_free_sta(hapd, sta);
+}
+
+
+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);
+	}
+}
+
+
+void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+			    u32 session_timeout)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "setting session timeout to %d "
+		       "seconds", session_timeout);
+	eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+	eloop_register_timeout(session_timeout, 0, ap_handle_session_timer,
+			       hapd, sta);
+}
+
+
+void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+}
+
+
+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;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		return sta;
+
+	wpa_printf(MSG_DEBUG, "  New STA");
+	if (hapd->num_sta >= hapd->conf->max_num_sta) {
+		/* FIX: might try to remove some old STAs first? */
+		wpa_printf(MSG_DEBUG, "no more room for new STAs (%d/%d)",
+			   hapd->num_sta, hapd->conf->max_num_sta);
+		return NULL;
+	}
+
+	sta = os_zalloc(sizeof(struct sta_info));
+	if (sta == NULL) {
+		wpa_printf(MSG_ERROR, "malloc failed");
+		return NULL;
+	}
+	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 */
+	os_memcpy(sta->addr, addr, ETH_ALEN);
+	sta->next = hapd->sta_list;
+	hapd->sta_list = sta;
+	hapd->num_sta++;
+	ap_sta_hash_add(hapd, sta);
+	ap_sta_remove_in_other_bss(hapd, sta);
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+	dl_list_init(&sta->ip6addr);
+
+	return sta;
+}
+
+
+static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+
+	if (sta->ipaddr)
+		hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+	ap_sta_ip6addr_del(hapd, sta);
+
+	wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver",
+		   MAC2STR(sta->addr));
+	if (hostapd_drv_sta_remove(hapd, sta->addr) &&
+	    sta->flags & WLAN_STA_ASSOC) {
+		wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR
+			   " from kernel driver.", MAC2STR(sta->addr));
+		return -1;
+	}
+	return 0;
+}
+
+
+static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
+				       struct sta_info *sta)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	size_t i;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *bss = iface->bss[i];
+		struct sta_info *sta2;
+		/* bss should always be set during operation, but it may be
+		 * NULL during reconfiguration. Assume the STA is not
+		 * associated to another BSS in that case to avoid NULL pointer
+		 * dereferences. */
+		if (bss == hapd || bss == NULL)
+			continue;
+		sta2 = ap_get_sta(bss, sta->addr);
+		if (!sta2)
+			continue;
+
+		ap_sta_disconnect(bss, sta2, sta2->addr,
+				  WLAN_REASON_PREV_AUTH_NOT_VALID);
+	}
+}
+
+
+static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	ap_sta_remove(hapd, sta);
+	mlme_disassociate_indication(hapd, sta, sta->disassoc_reason);
+}
+
+
+void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
+			 u16 reason)
+{
+	wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+	sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+	ap_sta_set_authorized(hapd, sta, 0);
+	sta->timeout_next = STA_DEAUTH;
+	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - "
+		   "AP_MAX_INACTIVITY_AFTER_DISASSOC)",
+		   __func__, MAC2STR(sta->addr),
+		   AP_MAX_INACTIVITY_AFTER_DISASSOC);
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
+			       ap_handle_timer, hapd, sta);
+	accounting_sta_stop(hapd, sta);
+	ieee802_1x_free_station(sta);
+
+	sta->disassoc_reason = reason;
+	sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+	eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+	eloop_register_timeout(hapd->iface->drv_flags &
+			       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+			       ap_sta_disassoc_cb_timeout, hapd, sta);
+}
+
+
+static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	ap_sta_remove(hapd, sta);
+	mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason);
+}
+
+
+void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+			   u16 reason)
+{
+	wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+	ap_sta_set_authorized(hapd, sta, 0);
+	sta->timeout_next = STA_REMOVE;
+	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - "
+		   "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
+		   __func__, MAC2STR(sta->addr),
+		   AP_MAX_INACTIVITY_AFTER_DEAUTH);
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
+			       ap_handle_timer, hapd, sta);
+	accounting_sta_stop(hapd, sta);
+	ieee802_1x_free_station(sta);
+
+	sta->deauth_reason = reason;
+	sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
+	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+	eloop_register_timeout(hapd->iface->drv_flags &
+			       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+			       ap_sta_deauth_cb_timeout, hapd, sta);
+}
+
+
+#ifdef CONFIG_WPS
+int ap_sta_wps_cancel(struct hostapd_data *hapd,
+		      struct sta_info *sta, void *ctx)
+{
+	if (sta && (sta->flags & WLAN_STA_WPS)) {
+		ap_sta_deauthenticate(hapd, sta,
+				      WLAN_REASON_PREV_AUTH_NOT_VALID);
+		wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR,
+			   __func__, MAC2STR(sta->addr));
+		return 1;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_WPS */
+
+
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
+{
+#ifndef CONFIG_NO_VLAN
+	const char *iface;
+	struct hostapd_vlan *vlan = NULL;
+	int ret;
+	int old_vlanid = sta->vlan_id_bound;
+
+	iface = hapd->conf->iface;
+	if (hapd->conf->ssid.vlan[0])
+		iface = hapd->conf->ssid.vlan;
+
+	if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+		sta->vlan_id = 0;
+	else if (sta->vlan_id > 0) {
+		struct hostapd_vlan *wildcard_vlan = NULL;
+		vlan = hapd->conf->vlan;
+		while (vlan) {
+			if (vlan->vlan_id == sta->vlan_id)
+				break;
+			if (vlan->vlan_id == VLAN_ID_WILDCARD)
+				wildcard_vlan = vlan;
+			vlan = vlan->next;
+		}
+		if (!vlan)
+			vlan = wildcard_vlan;
+		if (vlan)
+			iface = vlan->ifname;
+	}
+
+	/*
+	 * Do not increment ref counters if the VLAN ID remains same, but do
+	 * not skip hostapd_drv_set_sta_vlan() as hostapd_drv_sta_remove() might
+	 * have been called before.
+	 */
+	if (sta->vlan_id == old_vlanid)
+		goto skip_counting;
+
+	if (sta->vlan_id > 0 && vlan == NULL) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "could not find VLAN for "
+			       "binding station to (vlan_id=%d)",
+			       sta->vlan_id);
+		ret = -1;
+		goto done;
+	} else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) {
+		vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id);
+		if (vlan == NULL) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG, "could not add "
+				       "dynamic VLAN interface for vlan_id=%d",
+				       sta->vlan_id);
+			ret = -1;
+			goto done;
+		}
+
+		iface = vlan->ifname;
+		if (vlan_setup_encryption_dyn(hapd, iface) != 0) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG, "could not "
+				       "configure encryption for dynamic VLAN "
+				       "interface for vlan_id=%d",
+				       sta->vlan_id);
+		}
+
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN "
+			       "interface '%s'", iface);
+	} else if (vlan && vlan->vlan_id == sta->vlan_id) {
+		if (vlan->dynamic_vlan > 0) {
+			vlan->dynamic_vlan++;
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG, "updated existing "
+				       "dynamic VLAN interface '%s'", iface);
+		}
+
+		/*
+		 * Update encryption configuration for statically generated
+		 * VLAN interface. This is only used for static WEP
+		 * configuration for the case where hostapd did not yet know
+		 * which keys are to be used when the interface was added.
+		 */
+		if (vlan_setup_encryption_dyn(hapd, iface) != 0) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG, "could not "
+				       "configure encryption for VLAN "
+				       "interface for vlan_id=%d",
+				       sta->vlan_id);
+		}
+	}
+
+	/* ref counters have been increased, so mark the station */
+	sta->vlan_id_bound = sta->vlan_id;
+
+skip_counting:
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "binding station to interface "
+		       "'%s'", iface);
+
+	if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0)
+		wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA");
+
+	ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id);
+	if (ret < 0) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "could not bind the STA "
+			       "entry to vlan_id=%d", sta->vlan_id);
+	}
+
+	/* During 1x reauth, if the vlan id changes, then remove the old id. */
+	if (old_vlanid > 0 && old_vlanid != sta->vlan_id)
+		vlan_remove_dynamic(hapd, old_vlanid);
+done:
+
+	return ret;
+#else /* CONFIG_NO_VLAN */
+	return 0;
+#endif /* CONFIG_NO_VLAN */
+}
+
+
+#ifdef CONFIG_IEEE80211W
+
+int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	u32 tu;
+	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,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "association SA Query timed out");
+		sta->sa_query_timed_out = 1;
+		os_free(sta->sa_query_trans_id);
+		sta->sa_query_trans_id = NULL;
+		sta->sa_query_count = 0;
+		eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	unsigned int timeout, sec, usec;
+	u8 *trans_id, *nbuf;
+
+	if (sta->sa_query_count > 0 &&
+	    ap_check_sa_query_timeout(hapd, sta))
+		return;
+
+	nbuf = os_realloc_array(sta->sa_query_trans_id,
+				sta->sa_query_count + 1,
+				WLAN_SA_QUERY_TR_ID_LEN);
+	if (nbuf == NULL)
+		return;
+	if (sta->sa_query_count == 0) {
+		/* Starting a new SA Query procedure */
+		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;
+	sta->sa_query_count++;
+
+	if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) {
+		/*
+		 * We don't really care which ID is used here, so simply
+		 * hardcode this if the mostly theoretical os_get_random()
+		 * failure happens.
+		 */
+		trans_id[0] = 0x12;
+		trans_id[1] = 0x34;
+	}
+
+	timeout = hapd->conf->assoc_sa_query_retry_timeout;
+	sec = ((timeout / 1000) * 1024) / 1000;
+	usec = (timeout % 1000) * 1024;
+	eloop_register_timeout(sec, usec, ap_sa_query_timer, hapd, sta);
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "association SA Query attempt %d", sta->sa_query_count);
+
+	ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id);
+}
+
+
+void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	ap_sa_query_timer(hapd, sta);
+}
+
+
+void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
+	os_free(sta->sa_query_trans_id);
+	sta->sa_query_trans_id = NULL;
+	sta->sa_query_count = 0;
+}
+
+#endif /* CONFIG_IEEE80211W */
+
+
+void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+			   int authorized)
+{
+	const u8 *dev_addr = NULL;
+	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))
+		return;
+
+	if (authorized)
+		sta->flags |= WLAN_STA_AUTHORIZED;
+	else
+		sta->flags &= ~WLAN_STA_AUTHORIZED;
+
+#ifdef CONFIG_P2P
+	if (hapd->p2p_group == NULL) {
+		if (sta->p2p_ie != NULL &&
+		    p2p_parse_dev_addr_in_p2p_ie(sta->p2p_ie, addr) == 0)
+			dev_addr = addr;
+	} else
+		dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr);
+
+	if (dev_addr)
+		os_snprintf(buf, sizeof(buf), MACSTR " p2p_dev_addr=" MACSTR,
+			    MAC2STR(sta->addr), MAC2STR(dev_addr));
+	else
+#endif /* CONFIG_P2P */
+		os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
+
+	if (hapd->sta_authorized_cb)
+		hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx,
+					sta->addr, authorized, dev_addr);
+
+	if (authorized) {
+		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%s",
+					  buf, ip_addr);
+	} else {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
+
+		if (hapd->msg_ctx_parent &&
+		    hapd->msg_ctx_parent != hapd->msg_ctx)
+			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
+					  AP_STA_DISCONNECTED "%s", buf);
+	}
+
+#ifdef CONFIG_FST
+	if (hapd->iface->fst) {
+		if (authorized)
+			fst_notify_peer_connected(hapd->iface->fst, sta->addr);
+		else
+			fst_notify_peer_disconnected(hapd->iface->fst,
+						     sta->addr);
+	}
+#endif /* CONFIG_FST */
+}
+
+
+void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *addr, u16 reason)
+{
+
+	if (sta == NULL && addr)
+		sta = ap_get_sta(hapd, addr);
+
+	if (addr)
+		hostapd_drv_sta_deauth(hapd, addr, reason);
+
+	if (sta == NULL)
+		return;
+	ap_sta_set_authorized(hapd, sta, 0);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - "
+		   "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
+		   __func__, MAC2STR(sta->addr),
+		   AP_MAX_INACTIVITY_AFTER_DEAUTH);
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
+			       ap_handle_timer, hapd, sta);
+	sta->timeout_next = STA_REMOVE;
+
+	sta->deauth_reason = reason;
+	sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
+	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+	eloop_register_timeout(hapd->iface->drv_flags &
+			       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+			       ap_sta_deauth_cb_timeout, hapd, sta);
+}
+
+
+void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if (!(sta->flags & WLAN_STA_PENDING_DEAUTH_CB)) {
+		wpa_printf(MSG_DEBUG, "Ignore deauth cb for test frame");
+		return;
+	}
+	sta->flags &= ~WLAN_STA_PENDING_DEAUTH_CB;
+	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+	ap_sta_deauth_cb_timeout(hapd, sta);
+}
+
+
+void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if (!(sta->flags & WLAN_STA_PENDING_DISASSOC_CB)) {
+		wpa_printf(MSG_DEBUG, "Ignore disassoc cb for test frame");
+		return;
+	}
+	sta->flags &= ~WLAN_STA_PENDING_DISASSOC_CB;
+	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%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_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
+			  (flags & WLAN_STA_WNM_SLEEP_MODE ?
+			   "[WNM_SLEEP_MODE]" : ""));
+	if (os_snprintf_error(buflen, res))
+		res = -1;
+
+	return res;
+}
diff --git a/hostap/src/ap/sta_info.h b/hostap/src/ap/sta_info.h
new file mode 100644
index 0000000..420d64e
--- /dev/null
+++ b/hostap/src/ap/sta_info.h
@@ -0,0 +1,241 @@
+/*
+ * hostapd / Station table
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef STA_INFO_H
+#define STA_INFO_H
+
+#ifdef CONFIG_MESH
+/* needed for mesh_plink_state enum */
+#include "common/defs.h"
+#endif /* CONFIG_MESH */
+
+#include "list.h"
+
+/* STA flags */
+#define WLAN_STA_AUTH BIT(0)
+#define WLAN_STA_ASSOC BIT(1)
+#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)
+#define WLAN_STA_PREAUTH BIT(8)
+#define WLAN_STA_WMM BIT(9)
+#define WLAN_STA_MFP BIT(10)
+#define WLAN_STA_HT BIT(11)
+#define WLAN_STA_WPS BIT(12)
+#define WLAN_STA_MAYBE_WPS BIT(13)
+#define WLAN_STA_WDS BIT(14)
+#define WLAN_STA_ASSOC_REQ_OK BIT(15)
+#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_VENDOR_VHT BIT(21)
+#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
+#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
+#define WLAN_STA_NONERP BIT(31)
+
+/* Maximum number of supported rates (from both Supported Rates and Extended
+ * Supported Rates IEs). */
+#define WLAN_SUPP_RATES_MAX 32
+
+
+struct sta_info {
+	struct sta_info *next; /* next entry in sta list */
+	struct sta_info *hnext; /* next entry in hash table list */
+	u8 addr[6];
+	be32 ipaddr;
+	struct dl_list ip6addr; /* list head for struct ip6addr */
+	u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+	u32 flags; /* Bitfield of WLAN_STA_* */
+	u16 capability;
+	u16 listen_interval; /* or beacon_int for APs */
+	u8 supported_rates[WLAN_SUPP_RATES_MAX];
+	int supported_rates_len;
+	u8 qosinfo; /* Valid when WLAN_STA_WMM is set */
+
+#ifdef CONFIG_MESH
+	enum mesh_plink_state plink_state;
+	u16 peer_lid;
+	u16 my_lid;
+	u16 mpm_close_reason;
+	int mpm_retries;
+	u8 my_nonce[32];
+	u8 peer_nonce[32];
+	u8 aek[32];	/* SHA256 digest length */
+	u8 mtk[16];
+	u8 mgtk[16];
+	u8 sae_auth_retry;
+#endif /* CONFIG_MESH */
+
+	unsigned int nonerp_set:1;
+	unsigned int no_short_slot_time_set:1;
+	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;
+	unsigned int session_timeout_set:1;
+	unsigned int radius_das_match:1;
+
+	u16 auth_alg;
+
+	enum {
+		STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE,
+		STA_DISASSOC_FROM_CLI
+	} timeout_next;
+
+	u16 deauth_reason;
+	u16 disassoc_reason;
+
+	/* IEEE 802.1X related data */
+	struct eapol_state_machine *eapol_sm;
+
+	u32 acct_session_id_hi;
+	u32 acct_session_id_lo;
+	struct os_reltime acct_session_start;
+	int acct_session_started;
+	int acct_terminate_cause; /* Acct-Terminate-Cause */
+	int acct_interim_interval; /* Acct-Interim-Interval */
+
+	unsigned long last_rx_bytes;
+	unsigned long last_tx_bytes;
+	u32 acct_input_gigawords; /* Acct-Input-Gigawords */
+	u32 acct_output_gigawords; /* Acct-Output-Gigawords */
+
+	u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */
+
+	struct wpa_state_machine *wpa_sm;
+	struct rsn_preauth_interface *preauth_iface;
+
+	int vlan_id; /* 0: none, >0: VID */
+	int vlan_id_bound; /* updated by ap_sta_bind_vlan() */
+	 /* PSKs from RADIUS authentication server */
+	struct hostapd_sta_wpa_psk_short *psk;
+
+	char *identity; /* User-Name from RADIUS */
+	char *radius_cui; /* Chargeable-User-Identity from RADIUS */
+
+	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;
+			     * 0 = no SA Query in progress */
+	int sa_query_timed_out;
+	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_reltime sa_query_start;
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_INTERWORKING
+#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
+	struct gas_dialog_info *gas_dialog;
+	u8 gas_dialog_next;
+#endif /* CONFIG_INTERWORKING */
+
+	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;
+#ifdef CONFIG_FST
+	struct wpabuf *mb_ies; /* MB IEs from (Re)Association Request */
+#endif /* CONFIG_FST */
+
+	struct os_reltime connected_time;
+
+#ifdef CONFIG_SAE
+	struct sae_data *sae;
+#endif /* CONFIG_SAE */
+
+	u32 session_timeout; /* valid only if session_timeout_set == 1 */
+
+	/* Last Authentication/(Re)Association Request/Action frame sequence
+	 * control */
+	u16 last_seq_ctrl;
+	/* Last Authentication/(Re)Association Request/Action frame subtype */
+	u8 last_subtype;
+};
+
+
+/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has
+ * passed since last received frame from the station, a nullfunc data frame is
+ * sent to the station. If this frame is not acknowledged and no other frames
+ * have been received, the station will be disassociated after
+ * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated
+ * after AP_DEAUTH_DELAY seconds has passed after disassociation. */
+#define AP_MAX_INACTIVITY (5 * 60)
+#define AP_DISASSOC_DELAY (1)
+#define AP_DEAUTH_DELAY (1)
+/* Number of seconds to keep STA entry with Authenticated flag after it has
+ * been disassociated. */
+#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30)
+/* Number of seconds to keep STA entry after it has been deauthenticated. */
+#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
+
+
+struct hostapd_data;
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+		    int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+			      void *ctx),
+		    void *ctx);
+struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
+struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr);
+void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_ip6addr_del(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);
+void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+			   u16 reason);
+#ifdef CONFIG_WPS
+int ap_sta_wps_cancel(struct hostapd_data *hapd,
+		      struct sta_info *sta, void *ctx);
+#endif /* CONFIG_WPS */
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *addr, u16 reason);
+
+void ap_sta_set_authorized(struct hostapd_data *hapd,
+			   struct sta_info *sta, int authorized);
+static inline int ap_sta_is_authorized(struct sta_info *sta)
+{
+	return sta->flags & WLAN_STA_AUTHORIZED;
+}
+
+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/hostap/src/ap/tkip_countermeasures.c b/hostap/src/ap/tkip_countermeasures.c
new file mode 100644
index 0000000..4725e2b
--- /dev/null
+++ b/hostap/src/ap/tkip_countermeasures.c
@@ -0,0 +1,105 @@
+/*
+ * hostapd / TKIP countermeasures
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "radius/radius.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_mlme.h"
+#include "wpa_auth.h"
+#include "ap_drv_ops.h"
+#include "tkip_countermeasures.h"
+
+
+static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx,
+						void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	hapd->tkip_countermeasures = 0;
+	hostapd_drv_set_countermeasures(hapd, 0);
+	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended");
+}
+
+
+static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd)
+{
+	struct sta_info *sta;
+
+	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "TKIP countermeasures initiated");
+
+	wpa_auth_countermeasures_start(hapd->wpa_auth);
+	hapd->tkip_countermeasures = 1;
+	hostapd_drv_set_countermeasures(hapd, 1);
+	wpa_gtk_rekey(hapd->wpa_auth);
+	eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
+	eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop,
+			       hapd, NULL);
+	while ((sta = hapd->sta_list)) {
+		sta->acct_terminate_cause =
+			RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET;
+		if (sta->flags & WLAN_STA_AUTH) {
+			mlme_deauthenticate_indication(
+				hapd, sta,
+				WLAN_REASON_MICHAEL_MIC_FAILURE);
+		}
+		hostapd_drv_sta_deauth(hapd, sta->addr,
+				       WLAN_REASON_MICHAEL_MIC_FAILURE);
+		ap_free_sta(hapd, sta);
+	}
+}
+
+
+void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd)
+{
+	eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
+}
+
+
+int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
+{
+	struct os_reltime now;
+	int ret = 0;
+
+	if (addr && local) {
+		struct sta_info *sta = ap_get_sta(hapd, addr);
+		if (sta != NULL) {
+			wpa_auth_sta_local_mic_failure_report(sta->wpa_sm);
+			hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Michael MIC failure detected in "
+				       "received frame");
+			mlme_michaelmicfailure_indication(hapd, addr);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "MLME-MICHAELMICFAILURE.indication "
+				   "for not associated STA (" MACSTR
+				   ") ignored", MAC2STR(addr));
+			return ret;
+		}
+	}
+
+	os_get_reltime(&now);
+	if (os_reltime_expired(&now, &hapd->michael_mic_failure, 60)) {
+		hapd->michael_mic_failures = 1;
+	} else {
+		hapd->michael_mic_failures++;
+		if (hapd->michael_mic_failures > 1) {
+			ieee80211_tkip_countermeasures_start(hapd);
+			ret = 1;
+		}
+	}
+	hapd->michael_mic_failure = now;
+
+	return ret;
+}
diff --git a/hostap/src/ap/tkip_countermeasures.h b/hostap/src/ap/tkip_countermeasures.h
new file mode 100644
index 0000000..d3eaed3
--- /dev/null
+++ b/hostap/src/ap/tkip_countermeasures.h
@@ -0,0 +1,15 @@
+/*
+ * hostapd / TKIP countermeasures
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TKIP_COUNTERMEASURES_H
+#define TKIP_COUNTERMEASURES_H
+
+int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local);
+void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd);
+
+#endif /* TKIP_COUNTERMEASURES_H */
diff --git a/hostap/src/ap/utils.c b/hostap/src/ap/utils.c
new file mode 100644
index 0000000..fcb371b
--- /dev/null
+++ b/hostap/src/ap/utils.c
@@ -0,0 +1,96 @@
+/*
+ * AP mode helper functions
+ * 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 "common/ieee802_11_defs.h"
+#include "fst/fst.h"
+#include "sta_info.h"
+#include "hostapd.h"
+
+
+int hostapd_register_probereq_cb(struct hostapd_data *hapd,
+				 int (*cb)(void *ctx, const u8 *sa,
+					   const u8 *da, const u8 *bssid,
+					   const u8 *ie, size_t ie_len,
+					   int ssi_signal),
+				 void *ctx)
+{
+	struct hostapd_probereq_cb *n;
+
+	n = os_realloc_array(hapd->probereq_cb, hapd->num_probereq_cb + 1,
+			     sizeof(struct hostapd_probereq_cb));
+	if (n == NULL)
+		return -1;
+
+	hapd->probereq_cb = n;
+	n = &hapd->probereq_cb[hapd->num_probereq_cb];
+	hapd->num_probereq_cb++;
+
+	n->cb = cb;
+	n->ctx = ctx;
+
+	return 0;
+}
+
+
+struct prune_data {
+	struct hostapd_data *hapd;
+	const u8 *addr;
+};
+
+static int prune_associations(struct hostapd_iface *iface, void *ctx)
+{
+	struct prune_data *data = ctx;
+	struct sta_info *osta;
+	struct hostapd_data *ohapd;
+	size_t j;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		ohapd = iface->bss[j];
+		if (ohapd == data->hapd)
+			continue;
+#ifdef CONFIG_FST
+		/* Don't prune STAs belong to same FST */
+		if (ohapd->iface->fst &&
+		    data->hapd->iface->fst &&
+		    fst_are_ifaces_aggregated(ohapd->iface->fst,
+					      data->hapd->iface->fst))
+			continue;
+#endif /* CONFIG_FST */
+		osta = ap_get_sta(ohapd, data->addr);
+		if (!osta)
+			continue;
+
+		wpa_printf(MSG_INFO, "%s: Prune association for " MACSTR,
+			   ohapd->conf->iface, MAC2STR(osta->addr));
+		ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED);
+	}
+
+	return 0;
+}
+
+/**
+ * hostapd_prune_associations - Remove extraneous associations
+ * @hapd: Pointer to BSS data for the most recent association
+ * @addr: Associated STA address
+ *
+ * This function looks through all radios and BSS's for previous
+ * (stale) associations of STA. If any are found they are removed.
+ */
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct prune_data data;
+	data.hapd = hapd;
+	data.addr = addr;
+	if (hapd->iface->interfaces &&
+	    hapd->iface->interfaces->for_each_interface)
+		hapd->iface->interfaces->for_each_interface(
+			hapd->iface->interfaces, prune_associations, &data);
+}
diff --git a/hostap/src/ap/vlan_init.c b/hostap/src/ap/vlan_init.c
new file mode 100644
index 0000000..fd1c8dd
--- /dev/null
+++ b/hostap/src/ap/vlan_init.c
@@ -0,0 +1,1083 @@
+/*
+ * hostapd / VLAN initialization
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * 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 "utils/includes.h"
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#include <linux/if_vlan.h>
+#include <linux/if_bridge.h>
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "vlan_init.h"
+#include "vlan_util.h"
+
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+
+#include "drivers/priv_netlink.h"
+#include "utils/eloop.h"
+
+
+struct full_dynamic_vlan {
+	int s; /* socket on which to listen for new/removed interfaces. */
+};
+
+#define DVLAN_CLEAN_BR         0x1
+#define DVLAN_CLEAN_VLAN       0x2
+#define DVLAN_CLEAN_VLAN_PORT  0x4
+
+struct dynamic_iface {
+	char ifname[IFNAMSIZ + 1];
+	int usage;
+	int clean;
+	struct dynamic_iface *next;
+};
+
+
+/* Increment ref counter for ifname and add clean flag.
+ * If not in list, add it only if some flags are given.
+ */
+static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname,
+			  int clean)
+{
+	struct dynamic_iface *next, **dynamic_ifaces;
+	struct hapd_interfaces *interfaces;
+
+	interfaces = hapd->iface->interfaces;
+	dynamic_ifaces = &interfaces->vlan_priv;
+
+	for (next = *dynamic_ifaces; next; next = next->next) {
+		if (os_strcmp(ifname, next->ifname) == 0)
+			break;
+	}
+
+	if (next) {
+		next->usage++;
+		next->clean |= clean;
+		return;
+	}
+
+	if (!clean)
+		return;
+
+	next = os_zalloc(sizeof(*next));
+	if (!next)
+		return;
+	os_strlcpy(next->ifname, ifname, sizeof(next->ifname));
+	next->usage = 1;
+	next->clean = clean;
+	next->next = *dynamic_ifaces;
+	*dynamic_ifaces = next;
+}
+
+
+/* Decrement reference counter for given ifname.
+ * Return clean flag iff reference counter was decreased to zero, else zero
+ */
+static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
+{
+	struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces;
+	struct hapd_interfaces *interfaces;
+	int clean;
+
+	interfaces = hapd->iface->interfaces;
+	dynamic_ifaces = &interfaces->vlan_priv;
+
+	for (next = *dynamic_ifaces; next; next = next->next) {
+		if (os_strcmp(ifname, next->ifname) == 0)
+			break;
+		prev = next;
+	}
+
+	if (!next)
+		return 0;
+
+	next->usage--;
+	if (next->usage)
+		return 0;
+
+	if (prev)
+		prev->next = next->next;
+	else
+		*dynamic_ifaces = next->next;
+	clean = next->clean;
+	os_free(next);
+
+	return clean;
+}
+
+
+static int ifconfig_helper(const char *if_name, int up)
+{
+	int fd;
+	struct ifreq ifr;
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
+
+	if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
+			   "for interface %s: %s",
+			   __func__, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	if (up)
+		ifr.ifr_flags |= IFF_UP;
+	else
+		ifr.ifr_flags &= ~IFF_UP;
+
+	if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
+			   "for interface %s (up=%d): %s",
+			   __func__, if_name, up, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+static int ifconfig_up(const char *if_name)
+{
+	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
+	return ifconfig_helper(if_name, 1);
+}
+
+
+static int ifconfig_down(const char *if_name)
+{
+	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
+	return ifconfig_helper(if_name, 0);
+}
+
+
+/*
+ * These are only available in recent linux headers (without the leading
+ * underscore).
+ */
+#define _GET_VLAN_REALDEV_NAME_CMD	8
+#define _GET_VLAN_VID_CMD		9
+
+/* This value should be 256 ONLY. If it is something else, then hostapd
+ * might crash!, as this value has been hard-coded in 2.4.x kernel
+ * bridging code.
+ */
+#define MAX_BR_PORTS      		256
+
+static int br_delif(const char *br_name, const char *if_name)
+{
+	int fd;
+	struct ifreq ifr;
+	unsigned long args[2];
+	int if_index;
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	if_index = if_nametoindex(if_name);
+
+	if (if_index == 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+			   "interface index for '%s'",
+			   __func__, if_name);
+		close(fd);
+		return -1;
+	}
+
+	args[0] = BRCTL_DEL_IF;
+	args[1] = if_index;
+
+	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (__caddr_t) args;
+
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
+		/* No error if interface already removed. */
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+			   "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
+			   "%s", __func__, br_name, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+/*
+	Add interface 'if_name' to the bridge 'br_name'
+
+	returns -1 on error
+	returns 1 if the interface is already part of the bridge
+	returns 0 otherwise
+*/
+static int br_addif(const char *br_name, const char *if_name)
+{
+	int fd;
+	struct ifreq ifr;
+	unsigned long args[2];
+	int if_index;
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	if_index = if_nametoindex(if_name);
+
+	if (if_index == 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+			   "interface index for '%s'",
+			   __func__, if_name);
+		close(fd);
+		return -1;
+	}
+
+	args[0] = BRCTL_ADD_IF;
+	args[1] = if_index;
+
+	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (__caddr_t) args;
+
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+		if (errno == EBUSY) {
+			/* The interface is already added. */
+			close(fd);
+			return 1;
+		}
+
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+			   "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
+			   "%s", __func__, br_name, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+static int br_delbr(const char *br_name)
+{
+	int fd;
+	unsigned long arg[2];
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	arg[0] = BRCTL_DEL_BRIDGE;
+	arg[1] = (unsigned long) br_name;
+
+	if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
+		/* No error if bridge already removed. */
+		wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
+			   "%s: %s", __func__, br_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+/*
+	Add a bridge with the name 'br_name'.
+
+	returns -1 on error
+	returns 1 if the bridge already exists
+	returns 0 otherwise
+*/
+static int br_addbr(const char *br_name)
+{
+	int fd;
+	unsigned long arg[4];
+	struct ifreq ifr;
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	arg[0] = BRCTL_ADD_BRIDGE;
+	arg[1] = (unsigned long) br_name;
+
+	if (ioctl(fd, SIOCGIFBR, arg) < 0) {
+ 		if (errno == EEXIST) {
+			/* The bridge is already added. */
+			close(fd);
+			return 1;
+		} else {
+			wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
+				   "failed for %s: %s",
+				   __func__, br_name, strerror(errno));
+			close(fd);
+			return -1;
+		}
+	}
+
+	/* Decrease forwarding delay to avoid EAPOL timeouts. */
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
+	arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
+	arg[1] = 1;
+	arg[2] = 0;
+	arg[3] = 0;
+	ifr.ifr_data = (char *) &arg;
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: "
+			   "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
+			   "%s: %s", __func__, br_name, strerror(errno));
+		/* Continue anyway */
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+static int br_getnumports(const char *br_name)
+{
+	int fd;
+	int i;
+	int port_cnt = 0;
+	unsigned long arg[4];
+	int ifindices[MAX_BR_PORTS];
+	struct ifreq ifr;
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	arg[0] = BRCTL_GET_PORT_LIST;
+	arg[1] = (unsigned long) ifindices;
+	arg[2] = MAX_BR_PORTS;
+	arg[3] = 0;
+
+	os_memset(ifindices, 0, sizeof(ifindices));
+	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (__caddr_t) arg;
+
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
+			   "failed for %s: %s",
+			   __func__, br_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	for (i = 1; i < MAX_BR_PORTS; i++) {
+		if (ifindices[i] > 0) {
+			port_cnt++;
+		}
+	}
+
+	close(fd);
+	return port_cnt;
+}
+
+
+#ifndef CONFIG_VLAN_NETLINK
+
+int vlan_rem(const char *if_name)
+{
+	int fd;
+	struct vlan_ioctl_args if_request;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name);
+	if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+			   if_name);
+		return -1;
+	}
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&if_request, 0, sizeof(if_request));
+
+	os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
+	if_request.cmd = DEL_VLAN_CMD;
+
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: "
+			   "%s", __func__, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+/*
+	Add a vlan interface with VLAN ID 'vid' and tagged interface
+	'if_name'.
+
+	returns -1 on error
+	returns 1 if the interface already exists
+	returns 0 otherwise
+*/
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
+{
+	int fd;
+	struct vlan_ioctl_args if_request;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)",
+		   if_name, vid);
+	ifconfig_up(if_name);
+
+	if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+			   if_name);
+		return -1;
+	}
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&if_request, 0, sizeof(if_request));
+
+	/* Determine if a suitable vlan device already exists. */
+
+	os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
+		    vid);
+
+	if_request.cmd = _GET_VLAN_VID_CMD;
+
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) {
+
+		if (if_request.u.VID == vid) {
+			if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD;
+
+			if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
+			    os_strncmp(if_request.u.device2, if_name,
+				       sizeof(if_request.u.device2)) == 0) {
+				close(fd);
+				wpa_printf(MSG_DEBUG, "VLAN: vlan_add: "
+					   "if_name %s exists already",
+					   if_request.device1);
+				return 1;
+			}
+		}
+	}
+
+	/* A suitable vlan device does not already exist, add one. */
+
+	os_memset(&if_request, 0, sizeof(if_request));
+	os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
+	if_request.u.VID = vid;
+	if_request.cmd = ADD_VLAN_CMD;
+
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: "
+			   "%s",
+			   __func__, if_request.device1, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+static int vlan_set_name_type(unsigned int name_type)
+{
+	int fd;
+	struct vlan_ioctl_args if_request;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)",
+		   name_type);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&if_request, 0, sizeof(if_request));
+
+	if_request.u.name_type = name_type;
+	if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD "
+			   "name_type=%u failed: %s",
+			   __func__, name_type, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+#endif /* CONFIG_VLAN_NETLINK */
+
+
+static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
+{
+	char vlan_ifname[IFNAMSIZ];
+	char br_name[IFNAMSIZ];
+	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 clean;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
+
+	while (vlan) {
+		if (os_strcmp(ifname, vlan->ifname) == 0 && !vlan->configured) {
+			vlan->configured = 1;
+
+			if (hapd->conf->vlan_bridge[0]) {
+				os_snprintf(br_name, sizeof(br_name), "%s%d",
+					    hapd->conf->vlan_bridge,
+					    vlan->vlan_id);
+			} else if (tagged_interface) {
+				os_snprintf(br_name, sizeof(br_name),
+				            "br%s.%d", tagged_interface,
+					    vlan->vlan_id);
+			} else {
+				os_snprintf(br_name, sizeof(br_name),
+				            "brvlan%d", vlan->vlan_id);
+			}
+
+			dyn_iface_get(hapd, br_name,
+				      br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
+
+			ifconfig_up(br_name);
+
+			if (tagged_interface) {
+				if (vlan_naming ==
+				    DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+					os_snprintf(vlan_ifname,
+						    sizeof(vlan_ifname),
+						    "%s.%d", tagged_interface,
+						    vlan->vlan_id);
+				else
+					os_snprintf(vlan_ifname,
+						    sizeof(vlan_ifname),
+						    "vlan%d", vlan->vlan_id);
+
+				clean = 0;
+				ifconfig_up(tagged_interface);
+				if (!vlan_add(tagged_interface, vlan->vlan_id,
+					      vlan_ifname))
+					clean |= DVLAN_CLEAN_VLAN;
+
+				if (!br_addif(br_name, vlan_ifname))
+					clean |= DVLAN_CLEAN_VLAN_PORT;
+
+				dyn_iface_get(hapd, vlan_ifname, clean);
+
+				ifconfig_up(vlan_ifname);
+			}
+
+			if (!br_addif(br_name, ifname))
+				vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
+
+			ifconfig_up(ifname);
+
+			break;
+		}
+		vlan = vlan->next;
+	}
+}
+
+
+static void vlan_dellink(char *ifname, struct hostapd_data *hapd)
+{
+	char vlan_ifname[IFNAMSIZ];
+	char br_name[IFNAMSIZ];
+	struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
+	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+	int vlan_naming = hapd->conf->ssid.vlan_naming;
+	int clean;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
+
+	first = prev = vlan;
+
+	while (vlan) {
+		if (os_strcmp(ifname, vlan->ifname) == 0 &&
+		    vlan->configured) {
+			if (hapd->conf->vlan_bridge[0]) {
+				os_snprintf(br_name, sizeof(br_name), "%s%d",
+					    hapd->conf->vlan_bridge,
+					    vlan->vlan_id);
+			} else if (tagged_interface) {
+				os_snprintf(br_name, sizeof(br_name),
+				            "br%s.%d", tagged_interface,
+					    vlan->vlan_id);
+			} else {
+				os_snprintf(br_name, sizeof(br_name),
+				            "brvlan%d", vlan->vlan_id);
+			}
+
+			if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
+				br_delif(br_name, vlan->ifname);
+
+			if (tagged_interface) {
+				if (vlan_naming ==
+				    DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+					os_snprintf(vlan_ifname,
+						    sizeof(vlan_ifname),
+						    "%s.%d", tagged_interface,
+						    vlan->vlan_id);
+				else
+					os_snprintf(vlan_ifname,
+						    sizeof(vlan_ifname),
+						    "vlan%d", vlan->vlan_id);
+
+				clean = dyn_iface_put(hapd, vlan_ifname);
+
+				if (clean & DVLAN_CLEAN_VLAN_PORT)
+					br_delif(br_name, vlan_ifname);
+
+				if (clean & DVLAN_CLEAN_VLAN) {
+					ifconfig_down(vlan_ifname);
+					vlan_rem(vlan_ifname);
+				}
+			}
+
+			clean = dyn_iface_put(hapd, br_name);
+			if ((clean & DVLAN_CLEAN_BR) &&
+			    br_getnumports(br_name) == 0) {
+				ifconfig_down(br_name);
+				br_delbr(br_name);
+			}
+		}
+
+		if (os_strcmp(ifname, vlan->ifname) == 0) {
+			if (vlan == first) {
+				hapd->conf->vlan = vlan->next;
+			} else {
+				prev->next = vlan->next;
+			}
+			os_free(vlan);
+
+			break;
+		}
+		prev = vlan;
+		vlan = vlan->next;
+	}
+}
+
+
+static void
+vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
+		  struct hostapd_data *hapd)
+{
+	struct ifinfomsg *ifi;
+	int attrlen, nlmsg_len, rta_len;
+	struct rtattr *attr;
+	char ifname[IFNAMSIZ + 1];
+
+	if (len < sizeof(*ifi))
+		return;
+
+	ifi = NLMSG_DATA(h);
+
+	nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
+
+	attrlen = h->nlmsg_len - nlmsg_len;
+	if (attrlen < 0)
+		return;
+
+	attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
+
+	os_memset(ifname, 0, sizeof(ifname));
+	rta_len = RTA_ALIGN(sizeof(struct rtattr));
+	while (RTA_OK(attr, attrlen)) {
+		if (attr->rta_type == IFLA_IFNAME) {
+			int n = attr->rta_len - rta_len;
+			if (n < 0)
+				break;
+
+			if ((size_t) n >= sizeof(ifname))
+				n = sizeof(ifname) - 1;
+			os_memcpy(ifname, ((char *) attr) + rta_len, n);
+
+		}
+
+		attr = RTA_NEXT(attr, attrlen);
+	}
+
+	if (!ifname[0])
+		return;
+	if (del && if_nametoindex(ifname)) {
+		 /* interface still exists, race condition ->
+		  * iface has just been recreated */
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
+		   del ? "DEL" : "NEW",
+		   ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags,
+		   (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+		   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+	if (del)
+		vlan_dellink(ifname, hapd);
+	else
+		vlan_newlink(ifname, hapd);
+}
+
+
+static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	char buf[8192];
+	int left;
+	struct sockaddr_nl from;
+	socklen_t fromlen;
+	struct nlmsghdr *h;
+	struct hostapd_data *hapd = eloop_ctx;
+
+	fromlen = sizeof(from);
+	left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
+			(struct sockaddr *) &from, &fromlen);
+	if (left < 0) {
+		if (errno != EINTR && errno != EAGAIN)
+			wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
+				   __func__, strerror(errno));
+		return;
+	}
+
+	h = (struct nlmsghdr *) buf;
+	while (NLMSG_OK(h, left)) {
+		int len, plen;
+
+		len = h->nlmsg_len;
+		plen = len - sizeof(*h);
+		if (len > left || plen < 0) {
+			wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
+				   "message: len=%d left=%d plen=%d",
+				   len, left, plen);
+			break;
+		}
+
+		switch (h->nlmsg_type) {
+		case RTM_NEWLINK:
+			vlan_read_ifnames(h, plen, 0, hapd);
+			break;
+		case RTM_DELLINK:
+			vlan_read_ifnames(h, plen, 1, hapd);
+			break;
+		}
+
+		h = NLMSG_NEXT(h, left);
+	}
+
+	if (left > 0) {
+		wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
+			   "netlink message", __func__, left);
+	}
+}
+
+
+static struct full_dynamic_vlan *
+full_dynamic_vlan_init(struct hostapd_data *hapd)
+{
+	struct sockaddr_nl local;
+	struct full_dynamic_vlan *priv;
+
+	priv = os_zalloc(sizeof(*priv));
+	if (priv == NULL)
+		return NULL;
+
+#ifndef CONFIG_VLAN_NETLINK
+	vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
+			   DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
+			   VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
+			   VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
+#endif /* CONFIG_VLAN_NETLINK */
+
+	priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (priv->s < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
+			   "NETLINK_ROUTE) failed: %s",
+			   __func__, strerror(errno));
+		os_free(priv);
+		return NULL;
+	}
+
+	os_memset(&local, 0, sizeof(local));
+	local.nl_family = AF_NETLINK;
+	local.nl_groups = RTMGRP_LINK;
+	if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
+			   __func__, strerror(errno));
+		close(priv->s);
+		os_free(priv);
+		return NULL;
+	}
+
+	if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
+	{
+		close(priv->s);
+		os_free(priv);
+		return NULL;
+	}
+
+	return priv;
+}
+
+
+static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
+{
+	if (priv == NULL)
+		return;
+	eloop_unregister_read_sock(priv->s);
+	close(priv->s);
+	os_free(priv);
+}
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+
+int vlan_setup_encryption_dyn(struct hostapd_data *hapd, const char *dyn_vlan)
+{
+        int i;
+
+        if (dyn_vlan == NULL)
+		return 0;
+
+	/* Static WEP keys are set here; IEEE 802.1X and WPA uses their own
+	 * functions for setting up dynamic broadcast keys. */
+	for (i = 0; i < 4; i++) {
+		if (hapd->conf->ssid.wep.key[i] &&
+		    hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
+					i == hapd->conf->ssid.wep.idx, NULL, 0,
+					hapd->conf->ssid.wep.key[i],
+					hapd->conf->ssid.wep.len[i]))
+		{
+			wpa_printf(MSG_ERROR, "VLAN: Could not set WEP "
+				   "encryption for dynamic VLAN");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int vlan_dynamic_add(struct hostapd_data *hapd,
+			    struct hostapd_vlan *vlan)
+{
+	while (vlan) {
+		if (vlan->vlan_id != VLAN_ID_WILDCARD) {
+			if (hostapd_vlan_if_add(hapd, vlan->ifname)) {
+				if (errno != EEXIST) {
+					wpa_printf(MSG_ERROR, "VLAN: Could "
+						   "not add VLAN %s: %s",
+						   vlan->ifname,
+						   strerror(errno));
+					return -1;
+				}
+			}
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+			ifconfig_up(vlan->ifname);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+		}
+
+		vlan = vlan->next;
+	}
+
+	return 0;
+}
+
+
+static void vlan_dynamic_remove(struct hostapd_data *hapd,
+				struct hostapd_vlan *vlan)
+{
+	struct hostapd_vlan *next;
+
+	while (vlan) {
+		next = vlan->next;
+
+		if (vlan->vlan_id != VLAN_ID_WILDCARD &&
+		    hostapd_vlan_if_remove(hapd, vlan->ifname)) {
+			wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
+				   "iface: %s: %s",
+				   vlan->ifname, strerror(errno));
+		}
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+		if (vlan->clean)
+			vlan_dellink(vlan->ifname, hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+		vlan = next;
+	}
+}
+
+
+int vlan_init(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+	if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED &&
+	    !hapd->conf->vlan) {
+		/* dynamic vlans enabled but no (or empty) vlan_file given */
+		struct hostapd_vlan *vlan;
+		vlan = os_zalloc(sizeof(*vlan));
+		if (vlan == NULL) {
+			wpa_printf(MSG_ERROR, "Out of memory while assigning "
+				   "VLAN interfaces");
+			return -1;
+		}
+
+		vlan->vlan_id = VLAN_ID_WILDCARD;
+		os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
+			    hapd->conf->iface);
+		vlan->next = hapd->conf->vlan;
+		hapd->conf->vlan = vlan;
+	}
+
+	if (vlan_dynamic_add(hapd, hapd->conf->vlan))
+		return -1;
+
+        return 0;
+}
+
+
+void vlan_deinit(struct hostapd_data *hapd)
+{
+	vlan_dynamic_remove(hapd, hapd->conf->vlan);
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
+	hapd->full_dynamic_vlan = NULL;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+}
+
+
+struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+				       struct hostapd_vlan *vlan,
+				       int vlan_id)
+{
+	struct hostapd_vlan *n = NULL;
+	char *ifname, *pos;
+
+	if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
+	    vlan->vlan_id != VLAN_ID_WILDCARD)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
+		   __func__, vlan_id, vlan->ifname);
+	ifname = os_strdup(vlan->ifname);
+	if (ifname == NULL)
+		return NULL;
+	pos = os_strchr(ifname, '#');
+	if (pos == NULL)
+		goto free_ifname;
+	*pos++ = '\0';
+
+	n = os_zalloc(sizeof(*n));
+	if (n == NULL)
+		goto free_ifname;
+
+	n->vlan_id = vlan_id;
+	n->dynamic_vlan = 1;
+
+	os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
+		    pos);
+
+	if (hostapd_vlan_if_add(hapd, n->ifname)) {
+		os_free(n);
+		n = NULL;
+		goto free_ifname;
+	}
+
+	n->next = hapd->conf->vlan;
+	hapd->conf->vlan = n;
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	ifconfig_up(n->ifname);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+free_ifname:
+	os_free(ifname);
+	return n;
+}
+
+
+int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
+{
+	struct hostapd_vlan *vlan;
+
+	if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID)
+		return 1;
+
+	wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)",
+		   __func__, hapd->conf->iface, vlan_id);
+
+	vlan = hapd->conf->vlan;
+	while (vlan) {
+		if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) {
+			vlan->dynamic_vlan--;
+			break;
+		}
+		vlan = vlan->next;
+	}
+
+	if (vlan == NULL)
+		return 1;
+
+	if (vlan->dynamic_vlan == 0) {
+		hostapd_vlan_if_remove(hapd, vlan->ifname);
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+		vlan_dellink(vlan->ifname, hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+	}
+
+	return 0;
+}
diff --git a/hostap/src/ap/vlan_init.h b/hostap/src/ap/vlan_init.h
new file mode 100644
index 0000000..fc39443
--- /dev/null
+++ b/hostap/src/ap/vlan_init.h
@@ -0,0 +1,51 @@
+/*
+ * hostapd / VLAN initialization
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef VLAN_INIT_H
+#define VLAN_INIT_H
+
+#ifndef CONFIG_NO_VLAN
+int vlan_init(struct hostapd_data *hapd);
+void vlan_deinit(struct hostapd_data *hapd);
+struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+				       struct hostapd_vlan *vlan,
+				       int vlan_id);
+int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id);
+int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
+			      const char *dyn_vlan);
+#else /* CONFIG_NO_VLAN */
+static inline int vlan_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void vlan_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+						     struct hostapd_vlan *vlan,
+						     int vlan_id)
+{
+	return NULL;
+}
+
+static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
+{
+	return -1;
+}
+
+static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
+					    const char *dyn_vlan)
+{
+	return -1;
+}
+#endif /* CONFIG_NO_VLAN */
+
+#endif /* VLAN_INIT_H */
diff --git a/hostap/src/ap/vlan_util.c b/hostap/src/ap/vlan_util.c
new file mode 100644
index 0000000..d4e0efb
--- /dev/null
+++ b/hostap/src/ap/vlan_util.c
@@ -0,0 +1,192 @@
+/*
+ * hostapd / VLAN netlink api
+ * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#include <linux/if_vlan.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/vlan.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "vlan_util.h"
+
+/*
+ * Add a vlan interface with name 'vlan_if_name', VLAN ID 'vid' and
+ * tagged interface 'if_name'.
+ *
+ * returns -1 on error
+ * returns 1 if the interface already exists
+ * returns 0 otherwise
+*/
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
+{
+	int err, ret = -1;
+	struct nl_sock *handle = NULL;
+	struct nl_cache *cache = NULL;
+	struct rtnl_link *rlink = NULL;
+	int if_idx = 0;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d, "
+		   "vlan_if_name=%s)", if_name, vid, vlan_if_name);
+
+	if ((os_strlen(if_name) + 1) > IFNAMSIZ) {
+		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+			   if_name);
+		return -1;
+	}
+
+	if ((os_strlen(vlan_if_name) + 1) > IFNAMSIZ) {
+		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+			   vlan_if_name);
+		return -1;
+	}
+
+	handle = nl_socket_alloc();
+	if (!handle) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
+		goto vlan_add_error;
+	}
+
+	err = nl_connect(handle, NETLINK_ROUTE);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s",
+			   nl_geterror(err));
+		goto vlan_add_error;
+	}
+
+	err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache);
+	if (err < 0) {
+		cache = NULL;
+		wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s",
+			   nl_geterror(err));
+		goto vlan_add_error;
+	}
+
+	if (!(if_idx = rtnl_link_name2i(cache, if_name))) {
+		/* link does not exist */
+		wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist",
+			   if_name);
+		goto vlan_add_error;
+	}
+
+	if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) {
+		/* link does exist */
+		rtnl_link_put(rlink);
+		rlink = NULL;
+		wpa_printf(MSG_ERROR, "VLAN: interface %s already exists",
+			   vlan_if_name);
+		ret = 1;
+		goto vlan_add_error;
+	}
+
+	rlink = rtnl_link_alloc();
+	if (!rlink) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to allocate new link");
+		goto vlan_add_error;
+	}
+
+	err = rtnl_link_set_type(rlink, "vlan");
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to set link type: %s",
+			   nl_geterror(err));
+		goto vlan_add_error;
+	}
+
+	rtnl_link_set_link(rlink, if_idx);
+	rtnl_link_set_name(rlink, vlan_if_name);
+
+	err = rtnl_link_vlan_set_id(rlink, vid);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id: %s",
+			   nl_geterror(err));
+		goto vlan_add_error;
+	}
+
+	err = rtnl_link_add(handle, rlink, NLM_F_CREATE);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for "
+			   "vlan %d on %s (%d): %s",
+			   vlan_if_name, vid, if_name, if_idx,
+			   nl_geterror(err));
+		goto vlan_add_error;
+	}
+
+	ret = 0;
+
+vlan_add_error:
+	if (rlink)
+		rtnl_link_put(rlink);
+	if (cache)
+		nl_cache_free(cache);
+	if (handle)
+		nl_socket_free(handle);
+	return ret;
+}
+
+
+int vlan_rem(const char *if_name)
+{
+	int err, ret = -1;
+	struct nl_sock *handle = NULL;
+	struct nl_cache *cache = NULL;
+	struct rtnl_link *rlink = NULL;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name);
+
+	handle = nl_socket_alloc();
+	if (!handle) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
+		goto vlan_rem_error;
+	}
+
+	err = nl_connect(handle, NETLINK_ROUTE);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s",
+			   nl_geterror(err));
+		goto vlan_rem_error;
+	}
+
+	err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache);
+	if (err < 0) {
+		cache = NULL;
+		wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s",
+			   nl_geterror(err));
+		goto vlan_rem_error;
+	}
+
+	if (!(rlink = rtnl_link_get_by_name(cache, if_name))) {
+		/* link does not exist */
+		wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists",
+			   if_name);
+		goto vlan_rem_error;
+	}
+
+	err = rtnl_link_delete(handle, rlink);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s: %s",
+			   if_name, nl_geterror(err));
+		goto vlan_rem_error;
+	}
+
+	ret = 0;
+
+vlan_rem_error:
+	if (rlink)
+		rtnl_link_put(rlink);
+	if (cache)
+		nl_cache_free(cache);
+	if (handle)
+		nl_socket_free(handle);
+	return ret;
+}
diff --git a/hostap/src/ap/vlan_util.h b/hostap/src/ap/vlan_util.h
new file mode 100644
index 0000000..bef5a16
--- /dev/null
+++ b/hostap/src/ap/vlan_util.h
@@ -0,0 +1,15 @@
+/*
+ * hostapd / VLAN netlink api
+ * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef VLAN_UTIL_H
+#define VLAN_UTIL_H
+
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name);
+int vlan_rem(const char *if_name);
+
+#endif /* VLAN_UTIL_H */
diff --git a/hostap/src/ap/wmm.c b/hostap/src/ap/wmm.c
new file mode 100644
index 0000000..314e244
--- /dev/null
+++ b/hostap/src/ap/wmm.c
@@ -0,0 +1,327 @@
+/*
+ * hostapd / WMM (Wi-Fi Multimedia)
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "wmm.h"
+
+
+/* TODO: maintain separate sequence and fragment numbers for each AC
+ * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA
+ * if only WMM stations are receiving a certain group */
+
+
+static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
+{
+	u8 ret;
+	ret = (aifsn << WMM_AC_AIFNS_SHIFT) & WMM_AC_AIFSN_MASK;
+	if (acm)
+		ret |= WMM_AC_ACM;
+	ret |= (aci << WMM_AC_ACI_SHIFT) & WMM_AC_ACI_MASK;
+	return ret;
+}
+
+
+static inline u8 wmm_ecw(int ecwmin, int ecwmax)
+{
+	return ((ecwmin << WMM_AC_ECWMIN_SHIFT) & WMM_AC_ECWMIN_MASK) |
+		((ecwmax << WMM_AC_ECWMAX_SHIFT) & WMM_AC_ECWMAX_MASK);
+}
+
+
+/*
+ * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association
+ * Response frames.
+ */
+u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	struct wmm_parameter_element *wmm =
+		(struct wmm_parameter_element *) (pos + 2);
+	int e;
+
+	if (!hapd->conf->wmm_enabled)
+		return eid;
+	eid[0] = WLAN_EID_VENDOR_SPECIFIC;
+	wmm->oui[0] = 0x00;
+	wmm->oui[1] = 0x50;
+	wmm->oui[2] = 0xf2;
+	wmm->oui_type = WMM_OUI_TYPE;
+	wmm->oui_subtype = WMM_OUI_SUBTYPE_PARAMETER_ELEMENT;
+	wmm->version = WMM_VERSION;
+	wmm->qos_info = hapd->parameter_set_count & 0xf;
+
+	if (hapd->conf->wmm_uapsd &&
+	    (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
+		wmm->qos_info |= 0x80;
+
+	wmm->reserved = 0;
+
+	/* fill in a parameter set record for each AC */
+	for (e = 0; e < 4; e++) {
+		struct wmm_ac_parameter *ac = &wmm->ac[e];
+		struct hostapd_wmm_ac_params *acp =
+			&hapd->iconf->wmm_ac_params[e];
+
+		ac->aci_aifsn = wmm_aci_aifsn(acp->aifs,
+					      acp->admission_control_mandatory,
+					      e);
+		ac->cw = wmm_ecw(acp->cwmin, acp->cwmax);
+		ac->txop_limit = host_to_le16(acp->txop_limit);
+	}
+
+	pos = (u8 *) (wmm + 1);
+	eid[1] = pos - eid - 2; /* element length */
+
+	return pos;
+}
+
+
+/*
+ * This function is called when a station sends an association request with
+ * WMM info element. The function returns 1 on success or 0 on any error in WMM
+ * element. eid does not include Element ID and Length octets.
+ */
+int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
+{
+	struct wmm_information_element *wmm;
+
+	wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len);
+
+	if (len < sizeof(struct wmm_information_element)) {
+		wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)",
+			   (unsigned long) len);
+		return 0;
+	}
+
+	wmm = (struct wmm_information_element *) eid;
+	wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x  "
+		   "OUI type %d  OUI sub-type %d  version %d  QoS info 0x%x",
+		   wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type,
+		   wmm->oui_subtype, wmm->version, wmm->qos_info);
+	if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT ||
+	    wmm->version != WMM_VERSION) {
+		wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version");
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
+			    const struct wmm_tspec_element *tspec,
+			    u8 action_code, u8 dialogue_token, u8 status_code)
+{
+	u8 buf[256];
+	struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf;
+	struct wmm_tspec_element *t = (struct wmm_tspec_element *)
+		m->u.action.u.wmm_action.variable;
+	int len;
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "action response - reason %d", status_code);
+	os_memset(buf, 0, sizeof(buf));
+	m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					WLAN_FC_STYPE_ACTION);
+	os_memcpy(m->da, addr, ETH_ALEN);
+	os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
+	m->u.action.category = WLAN_ACTION_WMM;
+	m->u.action.u.wmm_action.action_code = action_code;
+	m->u.action.u.wmm_action.dialog_token = dialogue_token;
+	m->u.action.u.wmm_action.status_code = status_code;
+	os_memcpy(t, tspec, sizeof(struct wmm_tspec_element));
+	len = ((u8 *) (t + 1)) - buf;
+
+	if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0)
+		wpa_printf(MSG_INFO, "wmm_send_action: send failed");
+}
+
+
+int wmm_process_tspec(struct wmm_tspec_element *tspec)
+{
+	int medium_time, pps, duration;
+	int up, psb, dir, tid;
+	u16 val, surplus;
+
+	up = (tspec->ts_info[1] >> 3) & 0x07;
+	psb = (tspec->ts_info[1] >> 2) & 0x01;
+	dir = (tspec->ts_info[0] >> 5) & 0x03;
+	tid = (tspec->ts_info[0] >> 1) & 0x0f;
+	wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d",
+		   up, psb, dir, tid);
+	val = le_to_host16(tspec->nominal_msdu_size);
+	wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s",
+		   val & 0x7fff, val & 0x8000 ? " (fixed)" : "");
+	wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps",
+		   le_to_host32(tspec->mean_data_rate));
+	wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps",
+		   le_to_host32(tspec->minimum_phy_rate));
+	val = le_to_host16(tspec->surplus_bandwidth_allowance);
+	wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u",
+		   val >> 13, 10000 * (val & 0x1fff) / 0x2000);
+
+	val = le_to_host16(tspec->nominal_msdu_size);
+	if (val == 0) {
+		wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)");
+		return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+	}
+	/* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */
+	pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val;
+	wpa_printf(MSG_DEBUG, "WMM: Packets-per-second estimate for TSPEC: %d",
+		   pps);
+
+	if (le_to_host32(tspec->minimum_phy_rate) < 1000000) {
+		wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate");
+		return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+	}
+
+	duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 /
+		(le_to_host32(tspec->minimum_phy_rate) / 1000000) +
+		50 /* FIX: proper SIFS + ACK duration */;
+
+	/* unsigned binary number with an implicit binary point after the
+	 * leftmost 3 bits, i.e., 0x2000 = 1.0 */
+	surplus = le_to_host16(tspec->surplus_bandwidth_allowance);
+	if (surplus <= 0x2000) {
+		wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not "
+			   "greater than unity");
+		return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+	}
+
+	medium_time = surplus * pps * duration / 0x2000;
+	wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time);
+
+	/*
+	 * TODO: store list of granted (and still active) TSPECs and check
+	 * whether there is available medium time for this request. For now,
+	 * just refuse requests that would by themselves take very large
+	 * portion of the available bandwidth.
+	 */
+	if (medium_time > 750000) {
+		wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over "
+			   "75%% of available bandwidth");
+		return WMM_ADDTS_STATUS_REFUSED;
+	}
+
+	/* Convert to 32 microseconds per second unit */
+	tspec->medium_time = host_to_le16(medium_time / 32);
+
+	return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED;
+}
+
+
+static void wmm_addts_req(struct hostapd_data *hapd,
+			  const struct ieee80211_mgmt *mgmt,
+			  struct wmm_tspec_element *tspec, size_t len)
+{
+	const u8 *end = ((const u8 *) mgmt) + len;
+	int res;
+
+	if ((const u8 *) (tspec + 1) > end) {
+		wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC "
+		   "from " MACSTR,
+		   mgmt->u.action.u.wmm_action.dialog_token,
+		   MAC2STR(mgmt->sa));
+
+	res = wmm_process_tspec(tspec);
+	wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res);
+
+	wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP,
+			mgmt->u.action.u.wmm_action.dialog_token, res);
+}
+
+
+void hostapd_wmm_action(struct hostapd_data *hapd,
+			const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	int action_code;
+	int left = len - IEEE80211_HDRLEN - 4;
+	const u8 *pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 4;
+	struct ieee802_11_elems elems;
+	struct sta_info *sta = ap_get_sta(hapd, mgmt->sa);
+
+	/* check that the request comes from a valid station */
+	if (!sta ||
+	    (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WMM)) !=
+	    (WLAN_STA_ASSOC | WLAN_STA_WMM)) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "wmm action received is not from associated wmm"
+			       " station");
+		/* TODO: respond with action frame refused status code */
+		return;
+	}
+
+	if (left < 0)
+		return; /* not a valid WMM Action frame */
+
+	/* extract the tspec info element */
+	if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "hostapd_wmm_action - could not parse wmm "
+			       "action");
+		/* TODO: respond with action frame invalid parameters status
+		 * code */
+		return;
+	}
+
+	if (!elems.wmm_tspec ||
+	    elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "hostapd_wmm_action - missing or wrong length "
+			       "tspec");
+		/* TODO: respond with action frame invalid parameters status
+		 * code */
+		return;
+	}
+
+	/* TODO: check the request is for an AC with ACM set, if not, refuse
+	 * request */
+
+	action_code = mgmt->u.action.u.wmm_action.action_code;
+	switch (action_code) {
+	case WMM_ACTION_CODE_ADDTS_REQ:
+		wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *)
+			      (elems.wmm_tspec - 2), len);
+		return;
+#if 0
+	/* TODO: needed for client implementation */
+	case WMM_ACTION_CODE_ADDTS_RESP:
+		wmm_setup_request(hapd, mgmt, len);
+		return;
+	/* TODO: handle station teardown requests */
+	case WMM_ACTION_CODE_DELTS:
+		wmm_teardown(hapd, mgmt, len);
+		return;
+#endif
+	}
+
+	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "hostapd_wmm_action - unknown action code %d",
+		       action_code);
+}
diff --git a/hostap/src/ap/wmm.h b/hostap/src/ap/wmm.h
new file mode 100644
index 0000000..b70b863
--- /dev/null
+++ b/hostap/src/ap/wmm.h
@@ -0,0 +1,23 @@
+/*
+ * hostapd / WMM (Wi-Fi Multimedia)
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WME_H
+#define WME_H
+
+struct ieee80211_mgmt;
+struct wmm_tspec_element;
+
+u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid);
+int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid,
+			  size_t len);
+void hostapd_wmm_action(struct hostapd_data *hapd,
+			const struct ieee80211_mgmt *mgmt, size_t len);
+int wmm_process_tspec(struct wmm_tspec_element *tspec);
+
+#endif /* WME_H */
diff --git a/hostap/src/ap/wnm_ap.c b/hostap/src/ap/wnm_ap.c
new file mode 100644
index 0000000..4c8bc10
--- /dev/null
+++ b/hostap/src/ap/wnm_ap.c
@@ -0,0 +1,596 @@
+/*
+ * hostapd - WNM
+ * Copyright (c) 2011-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/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "ap/hostapd.h"
+#include "ap/sta_info.h"
+#include "ap/ap_config.h"
+#include "ap/ap_drv_ops.h"
+#include "ap/wpa_auth.h"
+#include "wnm_ap.h"
+
+#define MAX_TFS_IE_LEN  1024
+
+
+/* get the TFS IE from driver */
+static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
+				   u8 *buf, u16 *buf_len, enum wnm_oper oper)
+{
+	wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
+
+	return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
+}
+
+
+/* set the TFS IE to driver */
+static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
+				   u8 *buf, u16 *buf_len, enum wnm_oper oper)
+{
+	wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
+
+	return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
+}
+
+
+/* MLME-SLEEPMODE.response */
+static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
+					 const u8 *addr, u8 dialog_token,
+					 u8 action_type, u16 intval)
+{
+	struct ieee80211_mgmt *mgmt;
+	int res;
+	size_t len;
+	size_t gtk_elem_len = 0;
+	size_t igtk_elem_len = 0;
+	struct wnm_sleep_element wnmsleep_ie;
+	u8 *wnmtfs_ie;
+	u8 wnmsleep_ie_len;
+	u16 wnmtfs_ie_len;
+	u8 *pos;
+	struct sta_info *sta;
+	enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
+		WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
+		return -EINVAL;
+	}
+
+	/* WNM-Sleep Mode IE */
+	os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element));
+	wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
+	wnmsleep_ie.eid = WLAN_EID_WNMSLEEP;
+	wnmsleep_ie.len = wnmsleep_ie_len - 2;
+	wnmsleep_ie.action_type = action_type;
+	wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
+	wnmsleep_ie.intval = host_to_le16(intval);
+
+	/* TFS IE(s) */
+	wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
+	if (wnmtfs_ie == NULL)
+		return -1;
+	if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len,
+				    tfs_oper)) {
+		wnmtfs_ie_len = 0;
+		os_free(wnmtfs_ie);
+		wnmtfs_ie = NULL;
+	}
+
+#define MAX_GTK_SUBELEM_LEN 45
+#define MAX_IGTK_SUBELEM_LEN 26
+	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
+			 MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN);
+	if (mgmt == NULL) {
+		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
+			   "WNM-Sleep Response action frame");
+		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.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP;
+	mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token;
+	pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
+	/* add key data if MFP is enabled */
+	if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+	    action_type != WNM_SLEEP_MODE_EXIT) {
+		mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
+	} else {
+		gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos);
+		pos += gtk_elem_len;
+		wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d",
+			   (int) gtk_elem_len);
+#ifdef CONFIG_IEEE80211W
+		res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
+		if (res < 0) {
+			os_free(wnmtfs_ie);
+			os_free(mgmt);
+			return -1;
+		}
+		igtk_elem_len = res;
+		pos += igtk_elem_len;
+		wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
+			   (int) igtk_elem_len);
+#endif /* CONFIG_IEEE80211W */
+
+		WPA_PUT_LE16((u8 *)
+			     &mgmt->u.action.u.wnm_sleep_resp.keydata_len,
+			     gtk_elem_len + igtk_elem_len);
+	}
+	os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
+	/* copy TFS IE here */
+	pos += wnmsleep_ie_len;
+	if (wnmtfs_ie)
+		os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
+
+	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
+		igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len;
+
+	/* In driver, response frame should be forced to sent when STA is in
+	 * PS mode */
+	res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
+				      mgmt->da, &mgmt->u.action.category, len);
+
+	if (!res) {
+		wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response "
+			   "frame");
+
+		/* when entering wnmsleep
+		 * 1. pause the node in driver
+		 * 2. mark the node so that AP won't update GTK/IGTK during
+		 * WNM Sleep
+		 */
+		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);
+		}
+		/* when exiting wnmsleep
+		 * 1. unmark the node
+		 * 2. start GTK/IGTK update if MFP is not used
+		 * 3. unpause the node in driver
+		 */
+		if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT ||
+		     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);
+			if (!wpa_auth_uses_mfp(sta->wpa_sm))
+				wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
+		}
+	} else
+		wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame");
+
+#undef MAX_GTK_SUBELEM_LEN
+#undef MAX_IGTK_SUBELEM_LEN
+	os_free(wnmtfs_ie);
+	os_free(mgmt);
+	return res;
+}
+
+
+static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
+				       const u8 *addr, const u8 *frm, int len)
+{
+	/* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */
+	const u8 *pos = frm;
+	u8 dialog_token;
+	struct wnm_sleep_element *wnmsleep_ie = NULL;
+	/* multiple TFS Req IE (assuming consecutive) */
+	u8 *tfsreq_ie_start = NULL;
+	u8 *tfsreq_ie_end = NULL;
+	u16 tfsreq_ie_len = 0;
+
+	dialog_token = *pos++;
+	while (pos + 1 < frm + len) {
+		u8 ie_len = pos[1];
+		if (pos + 2 + ie_len > frm + len)
+			break;
+		if (*pos == WLAN_EID_WNMSLEEP)
+			wnmsleep_ie = (struct wnm_sleep_element *) pos;
+		else if (*pos == WLAN_EID_TFS_REQ) {
+			if (!tfsreq_ie_start)
+				tfsreq_ie_start = (u8 *) pos;
+			tfsreq_ie_end = (u8 *) pos;
+		} else
+			wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
+				   *pos);
+		pos += ie_len + 2;
+	}
+
+	if (!wnmsleep_ie) {
+		wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
+		return;
+	}
+
+	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
+	    tfsreq_ie_start && tfsreq_ie_end &&
+	    tfsreq_ie_end - tfsreq_ie_start >= 0) {
+		tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) -
+			tfsreq_ie_start;
+		wpa_printf(MSG_DEBUG, "TFS Req IE(s) found");
+		/* pass the TFS Req IE(s) to driver for processing */
+		if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
+					    &tfsreq_ie_len,
+					    WNM_SLEEP_TFS_REQ_IE_SET))
+			wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE");
+	}
+
+	ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
+				      wnmsleep_ie->action_type,
+				      le_to_host16(wnmsleep_ie->intval));
+
+	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
+		/* clear the tfs after sending the resp frame */
+		ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
+					&tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL);
+	}
+}
+
+
+static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+						  const u8 *addr,
+						  u8 dialog_token,
+						  const char *url)
+{
+	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));
+		wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
+			" status_code=%u bss_termination_delay=%u target_bssid="
+			MACSTR,
+			MAC2STR(addr), status_code, bss_termination_delay,
+			MAC2STR(pos));
+		pos += ETH_ALEN;
+	} else {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
+			" status_code=%u bss_termination_delay=%u",
+			MAC2STR(addr), status_code, bss_termination_delay);
+	}
+
+	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;
+
+	payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
+	action = *payload++;
+	plen = len - IEEE80211_HDRLEN - 2;
+
+	switch (action) {
+	case WNM_BSS_TRANS_MGMT_QUERY:
+		ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
+						   plen);
+		return 0;
+	case WNM_BSS_TRANS_MGMT_RESP:
+		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, mgmt->sa, payload, plen);
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
+		   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;
+}
+
+
+static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
+			       int 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);
+}
+
+
+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;
+	}
+
+	if (disassoc_timer) {
+		/* send disassociation frame after time-out */
+		set_disassoc_timer(hapd, sta, disassoc_timer);
+	}
+
+	return 0;
+}
+
+
+int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+			u8 req_mode, int disassoc_timer, u8 valid_int,
+			const u8 *bss_term_dur, const char *url,
+			const u8 *nei_rep, size_t nei_rep_len)
+{
+	u8 *buf, *pos;
+	struct ieee80211_mgmt *mgmt;
+	size_t url_len;
+
+	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
+		   MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x",
+		   MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int);
+	buf = os_zalloc(1000 + nei_rep_len);
+	if (buf == NULL)
+		return -1;
+	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 = req_mode;
+	mgmt->u.action.u.bss_tm_req.disassoc_timer =
+		host_to_le16(disassoc_timer);
+	mgmt->u.action.u.bss_tm_req.validity_interval = valid_int;
+
+	pos = mgmt->u.action.u.bss_tm_req.variable;
+
+	if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) &&
+	    bss_term_dur) {
+		os_memcpy(pos, bss_term_dur, 12);
+		pos += 12;
+	}
+
+	if (url) {
+		/* Session Information URL */
+		url_len = os_strlen(url);
+		if (url_len > 255) {
+			os_free(buf);
+			return -1;
+		}
+
+		*pos++ = url_len;
+		os_memcpy(pos, url, url_len);
+		pos += url_len;
+	}
+
+	if (nei_rep) {
+		os_memcpy(pos, nei_rep, nei_rep_len);
+		pos += nei_rep_len;
+	}
+
+	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "Failed to send BSS Transition Management Request frame");
+		os_free(buf);
+		return -1;
+	}
+	os_free(buf);
+
+	if (disassoc_timer) {
+		/* send disassociation frame after time-out */
+		set_disassoc_timer(hapd, sta, disassoc_timer);
+	}
+
+	return 0;
+}
diff --git a/hostap/src/ap/wnm_ap.h b/hostap/src/ap/wnm_ap.h
new file mode 100644
index 0000000..7789307
--- /dev/null
+++ b/hostap/src/ap/wnm_ap.h
@@ -0,0 +1,26 @@
+/*
+ * IEEE 802.11v WNM related functions and structures
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WNM_AP_H
+#define WNM_AP_H
+
+struct sta_info;
+
+int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+				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);
+int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+			u8 req_mode, int disassoc_timer, u8 valid_int,
+			const u8 *bss_term_dur, const char *url,
+			const u8 *nei_rep, size_t nei_rep_len);
+
+#endif /* WNM_AP_H */
diff --git a/hostap/src/ap/wpa_auth.c b/hostap/src/ap/wpa_auth.c
new file mode 100644
index 0000000..2760a3f
--- /dev/null
+++ b/hostap/src/ap/wpa_auth.c
@@ -0,0 +1,3500 @@
+/*
+ * IEEE 802.11 RSN / WPA Authenticator
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/state_machine.h"
+#include "utils/bitfield.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
+#include "wpa_auth_i.h"
+#include "wpa_auth_ie.h"
+
+#define STATE_MACHINE_DATA struct wpa_state_machine
+#define STATE_MACHINE_DEBUG_PREFIX "WPA"
+#define STATE_MACHINE_ADDR sm->addr
+
+
+static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
+static int wpa_sm_step(struct wpa_state_machine *sm);
+static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
+			      size_t data_len);
+static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
+static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
+			      struct wpa_group *group);
+static void wpa_request_new_ptk(struct wpa_state_machine *sm);
+static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group);
+static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
+				       struct wpa_group *group);
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+			  const u8 *pmk, struct wpa_ptk *ptk);
+static void wpa_group_free(struct wpa_authenticator *wpa_auth,
+			   struct wpa_group *group);
+static void wpa_group_get(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group);
+static void wpa_group_put(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group);
+
+static const u32 dot11RSNAConfigGroupUpdateCount = 4;
+static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
+static const u32 eapol_key_timeout_first = 100; /* ms */
+static const u32 eapol_key_timeout_subseq = 1000; /* ms */
+static const u32 eapol_key_timeout_first_group = 500; /* ms */
+
+/* TODO: make these configurable */
+static const int dot11RSNAConfigPMKLifetime = 43200;
+static const int dot11RSNAConfigPMKReauthThreshold = 70;
+static const int dot11RSNAConfigSATimeout = 60;
+
+
+static inline int wpa_auth_mic_failure_report(
+	struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+	if (wpa_auth->cb.mic_failure_report)
+		return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr);
+	return 0;
+}
+
+
+static inline void wpa_auth_psk_failure_report(
+	struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+	if (wpa_auth->cb.psk_failure_report)
+		wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr);
+}
+
+
+static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr, wpa_eapol_variable var,
+				      int value)
+{
+	if (wpa_auth->cb.set_eapol)
+		wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value);
+}
+
+
+static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
+				     const u8 *addr, wpa_eapol_variable var)
+{
+	if (wpa_auth->cb.get_eapol == NULL)
+		return -1;
+	return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var);
+}
+
+
+static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
+					  const u8 *addr,
+					  const u8 *p2p_dev_addr,
+					  const u8 *prev_psk)
+{
+	if (wpa_auth->cb.get_psk == NULL)
+		return NULL;
+	return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr,
+				    prev_psk);
+}
+
+
+static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth,
+				   const u8 *addr, u8 *msk, size_t *len)
+{
+	if (wpa_auth->cb.get_msk == NULL)
+		return -1;
+	return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len);
+}
+
+
+static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
+				   int vlan_id,
+				   enum wpa_alg alg, const u8 *addr, int idx,
+				   u8 *key, size_t key_len)
+{
+	if (wpa_auth->cb.set_key == NULL)
+		return -1;
+	return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
+				    key, key_len);
+}
+
+
+static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr, int idx, u8 *seq)
+{
+	if (wpa_auth->cb.get_seqnum == NULL)
+		return -1;
+	return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
+}
+
+
+static inline int
+wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		    const u8 *data, size_t data_len, int encrypt)
+{
+	if (wpa_auth->cb.send_eapol == NULL)
+		return -1;
+	return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len,
+				       encrypt);
+}
+
+
+#ifdef CONFIG_MESH
+static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr)
+{
+	if (wpa_auth->cb.start_ampe == NULL)
+		return -1;
+	return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr);
+}
+#endif /* CONFIG_MESH */
+
+
+int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
+			  int (*cb)(struct wpa_state_machine *sm, void *ctx),
+			  void *cb_ctx)
+{
+	if (wpa_auth->cb.for_each_sta == NULL)
+		return 0;
+	return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx);
+}
+
+
+int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
+			   int (*cb)(struct wpa_authenticator *a, void *ctx),
+			   void *cb_ctx)
+{
+	if (wpa_auth->cb.for_each_auth == NULL)
+		return 0;
+	return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx);
+}
+
+
+void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		     logger_level level, const char *txt)
+{
+	if (wpa_auth->cb.logger == NULL)
+		return;
+	wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt);
+}
+
+
+void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		      logger_level level, const char *fmt, ...)
+{
+	char *format;
+	int maxlen;
+	va_list ap;
+
+	if (wpa_auth->cb.logger == NULL)
+		return;
+
+	maxlen = os_strlen(fmt) + 100;
+	format = os_malloc(maxlen);
+	if (!format)
+		return;
+
+	va_start(ap, fmt);
+	vsnprintf(format, maxlen, fmt, ap);
+	va_end(ap);
+
+	wpa_auth_logger(wpa_auth, addr, level, format);
+
+	os_free(format);
+}
+
+
+static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
+			       const u8 *addr)
+{
+	if (wpa_auth->cb.disconnect == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr));
+	wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr,
+				WLAN_REASON_PREV_AUTH_NOT_VALID);
+}
+
+
+static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
+{
+	int ret = 0;
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+		ret = 1;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+	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;
+}
+
+
+static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+
+	if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) {
+		wpa_printf(MSG_ERROR, "Failed to get random data for WPA "
+			   "initialization.");
+	} else {
+		wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd");
+		wpa_hexdump_key(MSG_DEBUG, "GMK",
+				wpa_auth->group->GMK, WPA_GMK_LEN);
+	}
+
+	if (wpa_auth->conf.wpa_gmk_rekey) {
+		eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
+				       wpa_rekey_gmk, wpa_auth, NULL);
+	}
+}
+
+
+static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct wpa_group *group, *next;
+
+	wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
+	group = wpa_auth->group;
+	while (group) {
+		wpa_group_get(wpa_auth, group);
+
+		group->GTKReKey = TRUE;
+		do {
+			group->changed = FALSE;
+			wpa_group_sm_step(wpa_auth, group);
+		} while (group->changed);
+
+		next = group->next;
+		wpa_group_put(wpa_auth, group);
+		group = next;
+	}
+
+	if (wpa_auth->conf.wpa_group_rekey) {
+		eloop_register_timeout(wpa_auth->conf.wpa_group_rekey,
+				       0, wpa_rekey_gtk, wpa_auth, NULL);
+	}
+}
+
+
+static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct wpa_state_machine *sm = timeout_ctx;
+
+	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK");
+	wpa_request_new_ptk(sm);
+	wpa_sm_step(sm);
+}
+
+
+static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx)
+{
+	if (sm->pmksa == ctx)
+		sm->pmksa = NULL;
+	return 0;
+}
+
+
+static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
+				   void *ctx)
+{
+	struct wpa_authenticator *wpa_auth = ctx;
+	wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry);
+}
+
+
+static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
+					  struct wpa_group *group)
+{
+	u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)];
+	u8 rkey[32];
+	unsigned long ptr;
+
+	if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN);
+
+	/*
+	 * Counter = PRF-256(Random number, "Init Counter",
+	 *                   Local MAC Address || Time)
+	 */
+	os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
+	wpa_get_ntp_timestamp(buf + ETH_ALEN);
+	ptr = (unsigned long) group;
+	os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr));
+	if (random_get_bytes(rkey, sizeof(rkey)) < 0)
+		return -1;
+
+	if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf),
+		     group->Counter, WPA_NONCE_LEN) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "Key Counter",
+			group->Counter, WPA_NONCE_LEN);
+
+	return 0;
+}
+
+
+static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
+					 int vlan_id, int delay_init)
+{
+	struct wpa_group *group;
+
+	group = os_zalloc(sizeof(struct wpa_group));
+	if (group == NULL)
+		return NULL;
+
+	group->GTKAuthenticator = TRUE;
+	group->vlan_id = vlan_id;
+	group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
+
+	if (random_pool_ready() != 1) {
+		wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
+			   "for secure operations - update keys later when "
+			   "the first station connects");
+	}
+
+	/*
+	 * Set initial GMK/Counter value here. The actual values that will be
+	 * used in negotiations will be set once the first station tries to
+	 * connect. This allows more time for collecting additional randomness
+	 * on embedded devices.
+	 */
+	if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to get random data for WPA "
+			   "initialization.");
+		os_free(group);
+		return NULL;
+	}
+
+	group->GInit = TRUE;
+	if (delay_init) {
+		wpa_printf(MSG_DEBUG, "WPA: Delay group state machine start "
+			   "until Beacon frames have been configured");
+		/* Initialization is completed in wpa_init_keys(). */
+	} else {
+		wpa_group_sm_step(wpa_auth, group);
+		group->GInit = FALSE;
+		wpa_group_sm_step(wpa_auth, group);
+	}
+
+	return group;
+}
+
+
+/**
+ * wpa_init - Initialize WPA authenticator
+ * @addr: Authenticator address
+ * @conf: Configuration for WPA authenticator
+ * @cb: Callback functions for WPA authenticator
+ * Returns: Pointer to WPA authenticator data or %NULL on failure
+ */
+struct wpa_authenticator * wpa_init(const u8 *addr,
+				    struct wpa_auth_config *conf,
+				    struct wpa_auth_callbacks *cb)
+{
+	struct wpa_authenticator *wpa_auth;
+
+	wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
+	if (wpa_auth == NULL)
+		return NULL;
+	os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
+	os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+	os_memcpy(&wpa_auth->cb, cb, sizeof(*cb));
+
+	if (wpa_auth_gen_wpa_ie(wpa_auth)) {
+		wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
+		os_free(wpa_auth);
+		return NULL;
+	}
+
+	wpa_auth->group = wpa_group_init(wpa_auth, 0, 1);
+	if (wpa_auth->group == NULL) {
+		os_free(wpa_auth->wpa_ie);
+		os_free(wpa_auth);
+		return NULL;
+	}
+
+	wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb,
+						wpa_auth);
+	if (wpa_auth->pmksa == NULL) {
+		wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
+		os_free(wpa_auth->group);
+		os_free(wpa_auth->wpa_ie);
+		os_free(wpa_auth);
+		return NULL;
+	}
+
+#ifdef CONFIG_IEEE80211R
+	wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
+	if (wpa_auth->ft_pmk_cache == NULL) {
+		wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
+		os_free(wpa_auth->group);
+		os_free(wpa_auth->wpa_ie);
+		pmksa_cache_auth_deinit(wpa_auth->pmksa);
+		os_free(wpa_auth);
+		return NULL;
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	if (wpa_auth->conf.wpa_gmk_rekey) {
+		eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
+				       wpa_rekey_gmk, wpa_auth, NULL);
+	}
+
+	if (wpa_auth->conf.wpa_group_rekey) {
+		eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0,
+				       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;
+}
+
+
+int wpa_init_keys(struct wpa_authenticator *wpa_auth)
+{
+	struct wpa_group *group = wpa_auth->group;
+
+	wpa_printf(MSG_DEBUG, "WPA: Start group state machine to set initial "
+		   "keys");
+	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;
+}
+
+
+/**
+ * wpa_deinit - Deinitialize WPA authenticator
+ * @wpa_auth: Pointer to WPA authenticator data from wpa_init()
+ */
+void wpa_deinit(struct wpa_authenticator *wpa_auth)
+{
+	struct wpa_group *group, *prev;
+
+	eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
+	eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+
+#ifdef CONFIG_PEERKEY
+	while (wpa_auth->stsl_negotiations)
+		wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations);
+#endif /* CONFIG_PEERKEY */
+
+	pmksa_cache_auth_deinit(wpa_auth->pmksa);
+
+#ifdef CONFIG_IEEE80211R
+	wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
+	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;
+	while (group) {
+		prev = group;
+		group = group->next;
+		os_free(prev);
+	}
+
+	os_free(wpa_auth);
+}
+
+
+/**
+ * wpa_reconfig - Update WPA authenticator configuration
+ * @wpa_auth: Pointer to WPA authenticator data from wpa_init()
+ * @conf: Configuration for WPA authenticator
+ */
+int wpa_reconfig(struct wpa_authenticator *wpa_auth,
+		 struct wpa_auth_config *conf)
+{
+	struct wpa_group *group;
+	if (wpa_auth == NULL)
+		return 0;
+
+	os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+	if (wpa_auth_gen_wpa_ie(wpa_auth)) {
+		wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
+		return -1;
+	}
+
+	/*
+	 * Reinitialize GTK to make sure it is suitable for the new
+	 * configuration.
+	 */
+	group = wpa_auth->group;
+	group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
+	group->GInit = TRUE;
+	wpa_group_sm_step(wpa_auth, group);
+	group->GInit = FALSE;
+	wpa_group_sm_step(wpa_auth, group);
+
+	return 0;
+}
+
+
+struct wpa_state_machine *
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		  const u8 *p2p_dev_addr)
+{
+	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;
+	os_memcpy(sm->addr, addr, ETH_ALEN);
+	if (p2p_dev_addr)
+		os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
+
+	sm->wpa_auth = wpa_auth;
+	sm->group = wpa_auth->group;
+	wpa_group_get(sm->wpa_auth, sm->group);
+
+	return sm;
+}
+
+
+int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
+			    struct wpa_state_machine *sm)
+{
+	if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
+		return -1;
+
+#ifdef CONFIG_IEEE80211R
+	if (sm->ft_completed) {
+		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 */
+
+	if (sm->started) {
+		os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
+		sm->ReAuthenticationRequest = TRUE;
+		return wpa_sm_step(sm);
+	}
+
+	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			"start authentication");
+	sm->started = 1;
+
+	sm->Init = TRUE;
+	if (wpa_sm_step(sm) == 1)
+		return 1; /* should not really happen */
+	sm->Init = FALSE;
+	sm->AuthenticationRequest = TRUE;
+	return wpa_sm_step(sm);
+}
+
+
+void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
+{
+	/* WPA/RSN was not used - clear WPA state. This is needed if the STA
+	 * reassociates back to the same AP while the previous entry for the
+	 * STA has not yet been removed. */
+	if (sm == NULL)
+		return;
+
+	sm->wpa_key_mgmt = 0;
+}
+
+
+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);
+	wpa_group_put(sm->wpa_auth, sm->group);
+	os_free(sm);
+}
+
+
+void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return;
+
+	if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
+		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+				"strict rekeying - force GTK rekey since STA "
+				"is leaving");
+		eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL);
+		eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth,
+				       NULL);
+	}
+
+	eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
+	sm->pending_1_of_4_timeout = 0;
+	eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
+	eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+	if (sm->in_step_loop) {
+		/* Must not free state machine while wpa_sm_step() is running.
+		 * Freeing will be completed in the end of wpa_sm_step(). */
+		wpa_printf(MSG_DEBUG, "WPA: Registering pending STA state "
+			   "machine deinit for " MACSTR, MAC2STR(sm->addr));
+		sm->pending_deinit = 1;
+	} else
+		wpa_free_sta_sm(sm);
+}
+
+
+static void wpa_request_new_ptk(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return;
+
+	sm->PTKRequest = TRUE;
+	sm->PTK_valid = 0;
+}
+
+
+static int wpa_replay_counter_valid(struct wpa_key_replay_counter *ctr,
+				    const u8 *replay_counter)
+{
+	int i;
+	for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+		if (!ctr[i].valid)
+			break;
+		if (os_memcmp(replay_counter, ctr[i].counter,
+			      WPA_REPLAY_COUNTER_LEN) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+
+static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr,
+					    const u8 *replay_counter)
+{
+	int i;
+	for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+		if (ctr[i].valid &&
+		    (replay_counter == NULL ||
+		     os_memcmp(replay_counter, ctr[i].counter,
+			       WPA_REPLAY_COUNTER_LEN) == 0))
+			ctr[i].valid = FALSE;
+	}
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
+			       struct wpa_state_machine *sm,
+			       struct wpa_eapol_ie_parse *kde)
+{
+	struct wpa_ie_data ie;
+	struct rsn_mdie *mdie;
+
+	if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 ||
+	    ie.num_pmkid != 1 || ie.pmkid == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in "
+			   "FT 4-way handshake message 2/4");
+		return -1;
+	}
+
+	os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant",
+		    sm->sup_pmk_r1_name, PMKID_LEN);
+
+	if (!kde->mdie || !kde->ftie) {
+		wpa_printf(MSG_DEBUG, "FT: No %s in FT 4-way handshake "
+			   "message 2/4", kde->mdie ? "FTIE" : "MDIE");
+		return -1;
+	}
+
+	mdie = (struct rsn_mdie *) (kde->mdie + 2);
+	if (kde->mdie[1] < sizeof(struct rsn_mdie) ||
+	    os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain,
+		      MOBILITY_DOMAIN_ID_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: MDIE mismatch");
+		return -1;
+	}
+
+	if (sm->assoc_resp_ftie &&
+	    (kde->ftie[1] != sm->assoc_resp_ftie[1] ||
+	     os_memcmp(kde->ftie, sm->assoc_resp_ftie,
+		       2 + sm->assoc_resp_ftie[1]) != 0)) {
+		wpa_printf(MSG_DEBUG, "FT: FTIE mismatch");
+		wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4",
+			    kde->ftie, kde->ftie_len);
+		wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp",
+			    sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]);
+		return -1;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
+				    struct wpa_state_machine *sm, int group)
+{
+	/* Supplicant reported a Michael MIC error */
+	wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			 "received EAPOL-Key Error Request "
+			 "(STA detected Michael MIC failure (group=%d))",
+			 group);
+
+	if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"ignore Michael MIC failure report since "
+				"group cipher is not TKIP");
+	} else if (!group && sm->pairwise != WPA_CIPHER_TKIP) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"ignore Michael MIC failure report since "
+				"pairwise cipher is not TKIP");
+	} else {
+		if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0)
+			return 1; /* STA entry was removed */
+		sm->dot11RSNAStatsTKIPRemoteMICFailures++;
+		wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
+	}
+
+	/*
+	 * Error report is not a request for a new key handshake, but since
+	 * Authenticator may do it, let's change the keys now anyway.
+	 */
+	wpa_request_new_ptk(sm);
+	return 0;
+}
+
+
+static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
+			      size_t data_len)
+{
+	struct wpa_ptk PTK;
+	int ok = 0;
+	const u8 *pmk = NULL;
+
+	for (;;) {
+		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+			pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+					       sm->p2p_dev_addr, pmk);
+			if (pmk == NULL)
+				break;
+		} else
+			pmk = sm->PMK;
+
+		wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK);
+
+		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
+		    == 0) {
+			ok = 1;
+			break;
+		}
+
+		if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+			break;
+	}
+
+	if (!ok) {
+		wpa_printf(MSG_DEBUG,
+			   "WPA: Earlier SNonce did not result in matching MIC");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "WPA: Earlier SNonce resulted in matching MIC");
+	sm->alt_snonce_valid = 0;
+	os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
+	os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+	sm->PTK_valid = TRUE;
+
+	return 0;
+}
+
+
+void wpa_receive(struct wpa_authenticator *wpa_auth,
+		 struct wpa_state_machine *sm,
+		 u8 *data, size_t data_len)
+{
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	struct wpa_eapol_key_192 *key192;
+	u16 key_info, key_data_length;
+	enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
+	       SMK_M1, SMK_M3, SMK_ERROR } msg;
+	char *msgtxt;
+	struct wpa_eapol_ie_parse kde;
+	int ft;
+	const u8 *eapol_key_ie, *key_data;
+	size_t eapol_key_ie_len, keyhdrlen, mic_len;
+
+	if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
+		return;
+
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+	if (data_len < sizeof(*hdr) + keyhdrlen)
+		return;
+
+	hdr = (struct ieee802_1x_hdr *) data;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	key_info = WPA_GET_BE16(key->key_info);
+	if (mic_len == 24) {
+		key_data = (const u8 *) (key192 + 1);
+		key_data_length = WPA_GET_BE16(key192->key_data_length);
+	} else {
+		key_data = (const u8 *) (key + 1);
+		key_data_length = WPA_GET_BE16(key->key_data_length);
+	}
+	wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
+		   " key_info=0x%x type=%u key_data_length=%u",
+		   MAC2STR(sm->addr), key_info, key->type, key_data_length);
+	if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) {
+		wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
+			   "key_data overflow (%d > %lu)",
+			   key_data_length,
+			   (unsigned long) (data_len - sizeof(*hdr) -
+					    keyhdrlen));
+		return;
+	}
+
+	if (sm->wpa == WPA_VERSION_WPA2) {
+		if (key->type == EAPOL_KEY_TYPE_WPA) {
+			/*
+			 * Some deployed station implementations seem to send
+			 * msg 4/4 with incorrect type value in WPA2 mode.
+			 */
+			wpa_printf(MSG_DEBUG, "Workaround: Allow EAPOL-Key "
+				   "with unexpected WPA type in RSN mode");
+		} else if (key->type != EAPOL_KEY_TYPE_RSN) {
+			wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with "
+				   "unexpected type %d in RSN mode",
+				   key->type);
+			return;
+		}
+	} else {
+		if (key->type != EAPOL_KEY_TYPE_WPA) {
+			wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with "
+				   "unexpected type %d in WPA mode",
+				   key->type);
+			return;
+		}
+	}
+
+	wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce,
+		    WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter",
+		    key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
+	/* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys
+	 * are set */
+
+	if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) ==
+	    (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) {
+		if (key_info & WPA_KEY_INFO_ERROR) {
+			msg = SMK_ERROR;
+			msgtxt = "SMK Error";
+		} else {
+			msg = SMK_M1;
+			msgtxt = "SMK M1";
+		}
+	} else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+		msg = SMK_M3;
+		msgtxt = "SMK M3";
+	} else if (key_info & WPA_KEY_INFO_REQUEST) {
+		msg = REQUEST;
+		msgtxt = "Request";
+	} else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+		msg = GROUP_2;
+		msgtxt = "2/2 Group";
+	} else if (key_data_length == 0) {
+		msg = PAIRWISE_4;
+		msgtxt = "4/4 Pairwise";
+	} else {
+		msg = PAIRWISE_2;
+		msgtxt = "2/4 Pairwise";
+	}
+
+	/* TODO: key_info type validation for PeerKey */
+	if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
+	    msg == GROUP_2) {
+		u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+		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 &&
+			    !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+			    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+				wpa_auth_logger(wpa_auth, sm->addr,
+						LOGGER_WARNING,
+						"advertised support for "
+						"AES-128-CMAC, but did not "
+						"use it");
+				return;
+			}
+
+			if (!wpa_use_aes_cmac(sm) &&
+			    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+				wpa_auth_logger(wpa_auth, sm->addr,
+						LOGGER_WARNING,
+						"did not use HMAC-SHA1-AES "
+						"with CCMP/GCMP");
+				return;
+			}
+		}
+
+		if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+		    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
+					"did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
+			return;
+		}
+	}
+
+	if (key_info & WPA_KEY_INFO_REQUEST) {
+		if (sm->req_replay_counter_used &&
+		    os_memcmp(key->replay_counter, sm->req_replay_counter,
+			      WPA_REPLAY_COUNTER_LEN) <= 0) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
+					"received EAPOL-Key request with "
+					"replayed counter");
+			return;
+		}
+	}
+
+	if (!(key_info & WPA_KEY_INFO_REQUEST) &&
+	    !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) {
+		int i;
+
+		if (msg == PAIRWISE_2 &&
+		    wpa_replay_counter_valid(sm->prev_key_replay,
+					     key->replay_counter) &&
+		    sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+		    os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0)
+		{
+			/*
+			 * Some supplicant implementations (e.g., Windows XP
+			 * WZC) update SNonce for each EAPOL-Key 2/4. This
+			 * breaks the workaround on accepting any of the
+			 * pending requests, so allow the SNonce to be updated
+			 * even if we have already sent out EAPOL-Key 3/4.
+			 */
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "Process SNonce update from STA "
+					 "based on retransmitted EAPOL-Key "
+					 "1/4");
+			sm->update_snonce = 1;
+			os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
+			sm->alt_snonce_valid = TRUE;
+			os_memcpy(sm->alt_replay_counter,
+				  sm->key_replay[0].counter,
+				  WPA_REPLAY_COUNTER_LEN);
+			goto continue_processing;
+		}
+
+		if (msg == PAIRWISE_4 && sm->alt_snonce_valid &&
+		    sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+		    os_memcmp(key->replay_counter, sm->alt_replay_counter,
+			      WPA_REPLAY_COUNTER_LEN) == 0) {
+			/*
+			 * Supplicant may still be using the old SNonce since
+			 * there was two EAPOL-Key 2/4 messages and they had
+			 * different SNonce values.
+			 */
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
+			goto continue_processing;
+		}
+
+		if (msg == PAIRWISE_2 &&
+		    wpa_replay_counter_valid(sm->prev_key_replay,
+					     key->replay_counter) &&
+		    sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "ignore retransmitted EAPOL-Key %s - "
+					 "SNonce did not change", msgtxt);
+		} else {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "received EAPOL-Key %s with "
+					 "unexpected replay counter", msgtxt);
+		}
+		for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+			if (!sm->key_replay[i].valid)
+				break;
+			wpa_hexdump(MSG_DEBUG, "pending replay counter",
+				    sm->key_replay[i].counter,
+				    WPA_REPLAY_COUNTER_LEN);
+		}
+		wpa_hexdump(MSG_DEBUG, "received replay counter",
+			    key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+		return;
+	}
+
+continue_processing:
+	switch (msg) {
+	case PAIRWISE_2:
+		if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
+		    sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING &&
+		    (!sm->update_snonce ||
+		     sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+					 "received EAPOL-Key msg 2/4 in "
+					 "invalid state (%d) - dropped",
+					 sm->wpa_ptk_state);
+			return;
+		}
+		random_add_randomness(key->key_nonce, WPA_NONCE_LEN);
+		if (sm->group->reject_4way_hs_for_entropy) {
+			/*
+			 * The system did not have enough entropy to generate
+			 * strong random numbers. Reject the first 4-way
+			 * handshake(s) and collect some entropy based on the
+			 * information from it. Once enough entropy is
+			 * available, the next atempt will trigger GMK/Key
+			 * Counter update and the station will be allowed to
+			 * continue.
+			 */
+			wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to "
+				   "collect more entropy for random number "
+				   "generation");
+			random_mark_pool_ready();
+			wpa_sta_disconnect(wpa_auth, sm->addr);
+			return;
+		}
+		if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+					 "received EAPOL-Key msg 2/4 with "
+					 "invalid Key Data contents");
+			return;
+		}
+		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;
+		}
+		ft = sm->wpa == WPA_VERSION_WPA2 &&
+			wpa_key_mgmt_ft(sm->wpa_key_mgmt);
+		if (sm->wpa_ie == NULL ||
+		    wpa_compare_rsn_ie(ft,
+				       sm->wpa_ie, sm->wpa_ie_len,
+				       eapol_key_ie, eapol_key_ie_len)) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"WPA IE from (Re)AssocReq did not "
+					"match with msg 2/4");
+			if (sm->wpa_ie) {
+				wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
+					    sm->wpa_ie, sm->wpa_ie_len);
+			}
+			wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
+				    eapol_key_ie, eapol_key_ie_len);
+			/* MLME-DEAUTHENTICATE.request */
+			wpa_sta_disconnect(wpa_auth, sm->addr);
+			return;
+		}
+#ifdef CONFIG_IEEE80211R
+		if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
+			wpa_sta_disconnect(wpa_auth, sm->addr);
+			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 ||
+		    !sm->PTK_valid) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+					 "received EAPOL-Key msg 4/4 in "
+					 "invalid state (%d) - dropped",
+					 sm->wpa_ptk_state);
+			return;
+		}
+		break;
+	case GROUP_2:
+		if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING
+		    || !sm->PTK_valid) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+					 "received EAPOL-Key msg 2/2 in "
+					 "invalid state (%d) - dropped",
+					 sm->wpa_ptk_group_state);
+			return;
+		}
+		break;
+#ifdef CONFIG_PEERKEY
+	case SMK_M1:
+	case SMK_M3:
+	case SMK_ERROR:
+		if (!wpa_auth->conf.peerkey) {
+			wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but "
+				   "PeerKey use disabled - ignoring message");
+			return;
+		}
+		if (!sm->PTK_valid) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"received EAPOL-Key msg SMK in "
+					"invalid state - dropped");
+			return;
+		}
+		break;
+#else /* CONFIG_PEERKEY */
+	case SMK_M1:
+	case SMK_M3:
+	case SMK_ERROR:
+		return; /* STSL disabled - ignore SMK messages */
+#endif /* CONFIG_PEERKEY */
+	case REQUEST:
+		break;
+	}
+
+	wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			 "received EAPOL-Key frame (%s)", msgtxt);
+
+	if (key_info & WPA_KEY_INFO_ACK) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"received invalid EAPOL-Key: Key Ack set");
+		return;
+	}
+
+	if (!(key_info & WPA_KEY_INFO_MIC)) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"received invalid EAPOL-Key: Key MIC not set");
+		return;
+	}
+
+	sm->MICVerified = FALSE;
+	if (sm->PTK_valid && !sm->update_snonce) {
+		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
+				       data_len) &&
+		    (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
+		     wpa_try_alt_snonce(sm, data, data_len))) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"received EAPOL-Key with invalid MIC");
+			return;
+		}
+		sm->MICVerified = TRUE;
+		eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+		sm->pending_1_of_4_timeout = 0;
+	}
+
+	if (key_info & WPA_KEY_INFO_REQUEST) {
+		if (sm->MICVerified) {
+			sm->req_replay_counter_used = 1;
+			os_memcpy(sm->req_replay_counter, key->replay_counter,
+				  WPA_REPLAY_COUNTER_LEN);
+		} else {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"received EAPOL-Key request with "
+					"invalid MIC");
+			return;
+		}
+
+		/*
+		 * TODO: should decrypt key data field if encryption was used;
+		 * even though MAC address KDE is not normally encrypted,
+		 * supplicant is allowed to encrypt it.
+		 */
+		if (msg == SMK_ERROR) {
+#ifdef CONFIG_PEERKEY
+			wpa_smk_error(wpa_auth, sm, key_data, key_data_length);
+#endif /* CONFIG_PEERKEY */
+			return;
+		} else if (key_info & WPA_KEY_INFO_ERROR) {
+			if (wpa_receive_error_report(
+				    wpa_auth, sm,
+				    !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
+				return; /* STA entry was removed */
+		} else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"received EAPOL-Key Request for new "
+					"4-Way Handshake");
+			wpa_request_new_ptk(sm);
+#ifdef CONFIG_PEERKEY
+		} else if (msg == SMK_M1) {
+			wpa_smk_m1(wpa_auth, sm, key, key_data,
+				   key_data_length);
+#endif /* CONFIG_PEERKEY */
+		} else if (key_data_length > 0 &&
+			   wpa_parse_kde_ies(key_data, key_data_length,
+					     &kde) == 0 &&
+			   kde.mac_addr) {
+		} else {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"received EAPOL-Key Request for GTK "
+					"rekeying");
+			eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+			wpa_rekey_gtk(wpa_auth, NULL);
+		}
+	} else {
+		/* Do not allow the same key replay counter to be reused. */
+		wpa_replay_counter_mark_invalid(sm->key_replay,
+						key->replay_counter);
+
+		if (msg == PAIRWISE_2) {
+			/*
+			 * Maintain a copy of the pending EAPOL-Key frames in
+			 * case the EAPOL-Key frame was retransmitted. This is
+			 * needed to allow EAPOL-Key msg 2/4 reply to another
+			 * pending msg 1/4 to update the SNonce to work around
+			 * unexpected supplicant behavior.
+			 */
+			os_memcpy(sm->prev_key_replay, sm->key_replay,
+				  sizeof(sm->key_replay));
+		} else {
+			os_memset(sm->prev_key_replay, 0,
+				  sizeof(sm->prev_key_replay));
+		}
+
+		/*
+		 * Make sure old valid counters are not accepted anymore and
+		 * do not get copied again.
+		 */
+		wpa_replay_counter_mark_invalid(sm->key_replay, NULL);
+	}
+
+#ifdef CONFIG_PEERKEY
+	if (msg == SMK_M3) {
+		wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length);
+		return;
+	}
+#endif /* CONFIG_PEERKEY */
+
+	os_free(sm->last_rx_eapol_key);
+	sm->last_rx_eapol_key = os_malloc(data_len);
+	if (sm->last_rx_eapol_key == NULL)
+		return;
+	os_memcpy(sm->last_rx_eapol_key, data, data_len);
+	sm->last_rx_eapol_key_len = data_len;
+
+	sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
+	sm->EAPOLKeyReceived = TRUE;
+	sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
+	sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
+	os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN);
+	wpa_sm_step(sm);
+}
+
+
+static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
+			  const u8 *gnonce, u8 *gtk, size_t gtk_len)
+{
+	u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16];
+	u8 *pos;
+	int ret = 0;
+
+	/* GTK = PRF-X(GMK, "Group key expansion",
+	 *	AA || GNonce || Time || random data)
+	 * The example described in the IEEE 802.11 standard uses only AA and
+	 * GNonce as inputs here. Add some more entropy since this derivation
+	 * is done only at the Authenticator and as such, does not need to be
+	 * exactly same.
+	 */
+	os_memcpy(data, addr, ETH_ALEN);
+	os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
+	pos = data + ETH_ALEN + WPA_NONCE_LEN;
+	wpa_get_ntp_timestamp(pos);
+	pos += 8;
+	if (random_get_bytes(pos, 16) < 0)
+		ret = -1;
+
+#ifdef CONFIG_IEEE80211W
+	sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len);
+#else /* CONFIG_IEEE80211W */
+	if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len)
+	    < 0)
+		ret = -1;
+#endif /* CONFIG_IEEE80211W */
+
+	return ret;
+}
+
+
+static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct wpa_state_machine *sm = timeout_ctx;
+
+	sm->pending_1_of_4_timeout = 0;
+	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout");
+	sm->TimeoutEvt = TRUE;
+	wpa_sm_step(sm);
+}
+
+
+void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
+		      struct wpa_state_machine *sm, int key_info,
+		      const u8 *key_rsc, const u8 *nonce,
+		      const u8 *kde, size_t kde_len,
+		      int keyidx, int encr, int force_version)
+{
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	struct wpa_eapol_key_192 *key192;
+	size_t len, mic_len, keyhdrlen;
+	int alg;
+	int key_data_len, pad_len = 0;
+	u8 *buf, *pos;
+	int version, pairwise;
+	int i;
+	u8 *key_data;
+
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+	len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
+
+	if (force_version)
+		version = force_version;
+	else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+		 wpa_key_mgmt_suite_b(sm->wpa_key_mgmt))
+		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)
+		version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+	else
+		version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+	pairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
+
+	wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d "
+		   "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d "
+		   "encr=%d)",
+		   version,
+		   (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0,
+		   (key_info & WPA_KEY_INFO_MIC) ? 1 : 0,
+		   (key_info & WPA_KEY_INFO_ACK) ? 1 : 0,
+		   (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0,
+		   pairwise, (unsigned long) kde_len, keyidx, encr);
+
+	key_data_len = kde_len;
+
+	if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+	     sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+	     wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+	     version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
+		pad_len = key_data_len % 8;
+		if (pad_len)
+			pad_len = 8 - pad_len;
+		key_data_len += pad_len + 8;
+	}
+
+	len += key_data_len;
+
+	hdr = os_zalloc(len);
+	if (hdr == NULL)
+		return;
+	hdr->version = wpa_auth->conf.eapol_version;
+	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+	hdr->length = host_to_be16(len  - sizeof(*hdr));
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
+
+	key->type = sm->wpa == WPA_VERSION_WPA2 ?
+		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+	key_info |= version;
+	if (encr && sm->wpa == WPA_VERSION_WPA2)
+		key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
+	if (sm->wpa != WPA_VERSION_WPA2)
+		key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT;
+	WPA_PUT_BE16(key->key_info, key_info);
+
+	alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group;
+	WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
+	if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
+		WPA_PUT_BE16(key->key_length, 0);
+
+	/* FIX: STSL: what to use as key_replay_counter? */
+	for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
+		sm->key_replay[i].valid = sm->key_replay[i - 1].valid;
+		os_memcpy(sm->key_replay[i].counter,
+			  sm->key_replay[i - 1].counter,
+			  WPA_REPLAY_COUNTER_LEN);
+	}
+	inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN);
+	os_memcpy(key->replay_counter, sm->key_replay[0].counter,
+		  WPA_REPLAY_COUNTER_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter",
+		    key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+	sm->key_replay[0].valid = TRUE;
+
+	if (nonce)
+		os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN);
+
+	if (key_rsc)
+		os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
+
+	if (kde && !encr) {
+		os_memcpy(key_data, kde, kde_len);
+		if (mic_len == 24)
+			WPA_PUT_BE16(key192->key_data_length, kde_len);
+		else
+			WPA_PUT_BE16(key->key_data_length, kde_len);
+	} else if (encr && kde) {
+		buf = os_zalloc(key_data_len);
+		if (buf == NULL) {
+			os_free(hdr);
+			return;
+		}
+		pos = buf;
+		os_memcpy(pos, kde, kde_len);
+		pos += kde_len;
+
+		if (pad_len)
+			*pos++ = 0xdd;
+
+		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 ||
+		    wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+		    version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+			if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
+				     (key_data_len - 8) / 8, buf, key_data)) {
+				os_free(hdr);
+				os_free(buf);
+				return;
+			}
+			if (mic_len == 24)
+				WPA_PUT_BE16(key192->key_data_length,
+					     key_data_len);
+			else
+				WPA_PUT_BE16(key->key_data_length,
+					     key_data_len);
+#ifndef CONFIG_NO_RC4
+		} else if (sm->PTK.kek_len == 16) {
+			u8 ek[32];
+			os_memcpy(key->key_iv,
+				  sm->group->Counter + WPA_NONCE_LEN - 16, 16);
+			inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
+			os_memcpy(ek, key->key_iv, 16);
+			os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len);
+			os_memcpy(key_data, buf, key_data_len);
+			rc4_skip(ek, 32, 256, key_data, key_data_len);
+			if (mic_len == 24)
+				WPA_PUT_BE16(key192->key_data_length,
+					     key_data_len);
+			else
+				WPA_PUT_BE16(key->key_data_length,
+					     key_data_len);
+#endif /* CONFIG_NO_RC4 */
+		} else {
+			os_free(hdr);
+			os_free(buf);
+			return;
+		}
+		os_free(buf);
+	}
+
+	if (key_info & WPA_KEY_INFO_MIC) {
+		u8 *key_mic;
+
+		if (!sm->PTK_valid) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+					"PTK not valid when sending EAPOL-Key "
+					"frame");
+			os_free(hdr);
+			return;
+		}
+
+		key_mic = key192->key_mic; /* same offset for key and key192 */
+		wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
+				  sm->wpa_key_mgmt, version,
+				  (u8 *) hdr, len, key_mic);
+#ifdef CONFIG_TESTING_OPTIONS
+		if (!pairwise &&
+		    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,
+					"Corrupting group EAPOL-Key Key MIC");
+			key_mic[0]++;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+	}
+
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx,
+			   1);
+	wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len,
+			    sm->pairwise_set);
+	os_free(hdr);
+}
+
+
+static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
+			   struct wpa_state_machine *sm, int key_info,
+			   const u8 *key_rsc, const u8 *nonce,
+			   const u8 *kde, size_t kde_len,
+			   int keyidx, int encr)
+{
+	int timeout_ms;
+	int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
+	int ctr;
+
+	if (sm == NULL)
+		return;
+
+	__wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len,
+			 keyidx, encr, 0);
+
+	ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
+	if (ctr == 1 && wpa_auth->conf.tx_status)
+		timeout_ms = pairwise ? eapol_key_timeout_first :
+			eapol_key_timeout_first_group;
+	else
+		timeout_ms = eapol_key_timeout_subseq;
+	if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
+		sm->pending_1_of_4_timeout = 1;
+	wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry "
+		   "counter %d)", timeout_ms, ctr);
+	eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
+			       wpa_send_eapol_timeout, wpa_auth, sm);
+}
+
+
+static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
+			      size_t data_len)
+{
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	struct wpa_eapol_key_192 *key192;
+	u16 key_info;
+	int ret = 0;
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	size_t mic_len = wpa_mic_len(akmp);
+
+	if (data_len < sizeof(*hdr) + sizeof(*key))
+		return -1;
+
+	hdr = (struct ieee802_1x_hdr *) data;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	key_info = WPA_GET_BE16(key->key_info);
+	os_memcpy(mic, key192->key_mic, mic_len);
+	os_memset(key192->key_mic, 0, mic_len);
+	if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
+			      key_info & WPA_KEY_INFO_TYPE_MASK,
+			      data, data_len, key192->key_mic) ||
+	    os_memcmp_const(mic, key192->key_mic, mic_len) != 0)
+		ret = -1;
+	os_memcpy(key192->key_mic, mic, mic_len);
+	return ret;
+}
+
+
+void wpa_remove_ptk(struct wpa_state_machine *sm)
+{
+	sm->PTK_valid = FALSE;
+	os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+	wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0);
+	sm->pairwise_set = FALSE;
+	eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+}
+
+
+int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
+{
+	int remove_ptk = 1;
+
+	if (sm == NULL)
+		return -1;
+
+	wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			 "event %d notification", event);
+
+	switch (event) {
+	case WPA_AUTH:
+#ifdef CONFIG_MESH
+		/* PTKs are derived through AMPE */
+		if (wpa_auth_start_ampe(sm->wpa_auth, sm->addr)) {
+			/* not mesh */
+			break;
+		}
+		return 0;
+#endif /* CONFIG_MESH */
+	case WPA_ASSOC:
+		break;
+	case WPA_DEAUTH:
+	case WPA_DISASSOC:
+		sm->DeauthenticationRequest = TRUE;
+		break;
+	case WPA_REAUTH:
+	case WPA_REAUTH_EAPOL:
+		if (!sm->started) {
+			/*
+			 * When using WPS, we may end up here if the STA
+			 * manages to re-associate without the previous STA
+			 * entry getting removed. Consequently, we need to make
+			 * sure that the WPA state machines gets initialized
+			 * properly at this point.
+			 */
+			wpa_printf(MSG_DEBUG, "WPA state machine had not been "
+				   "started - initialize now");
+			sm->started = 1;
+			sm->Init = TRUE;
+			if (wpa_sm_step(sm) == 1)
+				return 1; /* should not really happen */
+			sm->Init = FALSE;
+			sm->AuthenticationRequest = TRUE;
+			break;
+		}
+		if (sm->GUpdateStationKeys) {
+			/*
+			 * Reauthentication cancels the pending group key
+			 * update for this STA.
+			 */
+			sm->group->GKeyDoneStations--;
+			sm->GUpdateStationKeys = FALSE;
+			sm->PtkGroupInit = TRUE;
+		}
+		sm->ReAuthenticationRequest = TRUE;
+		break;
+	case WPA_ASSOC_FT:
+#ifdef CONFIG_IEEE80211R
+		wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration "
+			   "after association");
+		wpa_ft_install_ptk(sm);
+
+		/* Using FT protocol, not WPA auth state machine */
+		sm->ft_completed = 1;
+		return 0;
+#else /* CONFIG_IEEE80211R */
+		break;
+#endif /* CONFIG_IEEE80211R */
+	}
+
+#ifdef CONFIG_IEEE80211R
+	sm->ft_completed = 0;
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211W
+	if (sm->mgmt_frame_prot && event == WPA_AUTH)
+		remove_ptk = 0;
+#endif /* CONFIG_IEEE80211W */
+
+	if (remove_ptk) {
+		sm->PTK_valid = FALSE;
+		os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+
+		if (event != WPA_REAUTH_EAPOL)
+			wpa_remove_ptk(sm);
+	}
+
+	if (sm->in_step_loop) {
+		/*
+		 * wpa_sm_step() is already running - avoid recursive call to
+		 * it by making the existing loop process the new update.
+		 */
+		sm->changed = TRUE;
+		return 0;
+	}
+	return wpa_sm_step(sm);
+}
+
+
+SM_STATE(WPA_PTK, INITIALIZE)
+{
+	SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk);
+	if (sm->Init) {
+		/* Init flag is not cleared here, so avoid busy
+		 * loop by claiming nothing changed. */
+		sm->changed = FALSE;
+	}
+
+	sm->keycount = 0;
+	if (sm->GUpdateStationKeys)
+		sm->group->GKeyDoneStations--;
+	sm->GUpdateStationKeys = FALSE;
+	if (sm->wpa == WPA_VERSION_WPA)
+		sm->PInitAKeys = FALSE;
+	if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and
+	       * Local AA > Remote AA)) */) {
+		sm->Pair = TRUE;
+	}
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0);
+	wpa_remove_ptk(sm);
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0);
+	sm->TimeoutCtr = 0;
+	if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+		wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+				   WPA_EAPOL_authorized, 0);
+	}
+}
+
+
+SM_STATE(WPA_PTK, DISCONNECT)
+{
+	SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk);
+	sm->Disconnect = FALSE;
+	wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+}
+
+
+SM_STATE(WPA_PTK, DISCONNECTED)
+{
+	SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk);
+	sm->DeauthenticationRequest = FALSE;
+}
+
+
+SM_STATE(WPA_PTK, AUTHENTICATION)
+{
+	SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk);
+	os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+	sm->PTK_valid = FALSE;
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto,
+			   1);
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1);
+	sm->AuthenticationRequest = FALSE;
+}
+
+
+static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth,
+				  struct wpa_group *group)
+{
+	if (group->first_sta_seen)
+		return;
+	/*
+	 * System has run bit further than at the time hostapd was started
+	 * potentially very early during boot up. This provides better chances
+	 * of collecting more randomness on embedded systems. Re-initialize the
+	 * GMK and Counter here to improve their strength if there was not
+	 * enough entropy available immediately after system startup.
+	 */
+	wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first "
+		   "station");
+	if (random_pool_ready() != 1) {
+		wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
+			   "to proceed - reject first 4-way handshake");
+		group->reject_4way_hs_for_entropy = TRUE;
+	} else {
+		group->first_sta_seen = TRUE;
+		group->reject_4way_hs_for_entropy = FALSE;
+	}
+
+	if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 ||
+	    wpa_gtk_update(wpa_auth, group) < 0 ||
+	    wpa_group_config_group_keys(wpa_auth, group) < 0) {
+		wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed");
+		group->first_sta_seen = FALSE;
+		group->reject_4way_hs_for_entropy = TRUE;
+	}
+}
+
+
+SM_STATE(WPA_PTK, AUTHENTICATION2)
+{
+	SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk);
+
+	wpa_group_ensure_init(sm->wpa_auth, sm->group);
+	sm->ReAuthenticationRequest = FALSE;
+
+	/*
+	 * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat
+	 * ambiguous. The Authenticator state machine uses a counter that is
+	 * incremented by one for each 4-way handshake. However, the security
+	 * analysis of 4-way handshake points out that unpredictable nonces
+	 * help in preventing precomputation attacks. Instead of the state
+	 * machine definition, use an unpredictable nonce value here to provide
+	 * stronger protection against potential precomputation attacks.
+	 */
+	if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+		wpa_printf(MSG_ERROR, "WPA: Failed to get random data for "
+			   "ANonce.");
+		sm->Disconnect = TRUE;
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
+		    WPA_NONCE_LEN);
+	/* IEEE 802.11i does not clear TimeoutCtr here, but this is more
+	 * logical place than INITIALIZE since AUTHENTICATION2 can be
+	 * re-entered on ReAuthenticationRequest without going through
+	 * INITIALIZE. */
+	sm->TimeoutCtr = 0;
+}
+
+
+SM_STATE(WPA_PTK, INITPMK)
+{
+	u8 msk[2 * PMK_LEN];
+	size_t len = 2 * PMK_LEN;
+
+	SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk);
+#ifdef CONFIG_IEEE80211R
+	sm->xxkey_len = 0;
+#endif /* CONFIG_IEEE80211R */
+	if (sm->pmksa) {
+		wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
+		os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN);
+	} else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
+		wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine "
+			   "(len=%lu)", (unsigned long) len);
+		os_memcpy(sm->PMK, msk, PMK_LEN);
+#ifdef CONFIG_IEEE80211R
+		if (len >= 2 * PMK_LEN) {
+			os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
+			sm->xxkey_len = PMK_LEN;
+		}
+#endif /* CONFIG_IEEE80211R */
+	} else {
+		wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
+			   sm->wpa_auth->cb.get_msk);
+		sm->Disconnect = TRUE;
+		return;
+	}
+	os_memset(msk, 0, sizeof(msk));
+
+	sm->req_replay_counter_used = 0;
+	/* IEEE 802.11i does not set keyRun to FALSE, but not doing this
+	 * will break reauthentication since EAPOL state machines may not be
+	 * get into AUTHENTICATING state that clears keyRun before WPA state
+	 * machine enters AUTHENTICATION2 state and goes immediately to INITPMK
+	 * state and takes PMK from the previously used AAA Key. This will
+	 * eventually fail in 4-Way Handshake because Supplicant uses PMK
+	 * derived from the new AAA Key. Setting keyRun = FALSE here seems to
+	 * be good workaround for this issue. */
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0);
+}
+
+
+SM_STATE(WPA_PTK, INITPSK)
+{
+	const u8 *psk;
+	SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
+	psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL);
+	if (psk) {
+		os_memcpy(sm->PMK, psk, PMK_LEN);
+#ifdef CONFIG_IEEE80211R
+		os_memcpy(sm->xxkey, psk, PMK_LEN);
+		sm->xxkey_len = PMK_LEN;
+#endif /* CONFIG_IEEE80211R */
+	}
+	sm->req_replay_counter_used = 0;
+}
+
+
+SM_STATE(WPA_PTK, PTKSTART)
+{
+	u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL;
+	size_t pmkid_len = 0;
+
+	SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
+	sm->PTKRequest = FALSE;
+	sm->TimeoutEvt = FALSE;
+	sm->alt_snonce_valid = FALSE;
+
+	sm->TimeoutCtr++;
+	if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+		/* No point in sending the EAPOL-Key - we will disconnect
+		 * immediately following this. */
+		return;
+	}
+
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"sending 1/4 msg of 4-Way Handshake");
+	/*
+	 * TODO: Could add PMKID even with WPA2-PSK, but only if there is only
+	 * one possible PSK for this STA.
+	 */
+	if (sm->wpa == WPA_VERSION_WPA2 &&
+	    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;
+		pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
+		RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
+		if (sm->pmksa) {
+			os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+				  sm->pmksa->pmkid, PMKID_LEN);
+		} else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) {
+			/* No KCK available to derive PMKID */
+			pmkid = NULL;
+		} else {
+			/*
+			 * Calculate PMKID since no PMKSA cache entry was
+			 * available with pre-calculated PMKID.
+			 */
+			rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr,
+				  sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
+				  wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
+		}
+	}
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
+		       sm->ANonce, pmkid, pmkid_len, 0, 0);
+}
+
+
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+			  const u8 *pmk, struct wpa_ptk *ptk)
+{
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+		return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
+#endif /* CONFIG_IEEE80211R */
+
+	return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
+			      sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
+			      ptk, sm->wpa_key_mgmt, sm->pairwise);
+}
+
+
+SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
+{
+	struct wpa_ptk PTK;
+	int ok = 0, psk_found = 0;
+	const u8 *pmk = NULL;
+
+	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
+	sm->EAPOLKeyReceived = FALSE;
+	sm->update_snonce = FALSE;
+
+	/* WPA with IEEE 802.1X: use the derived PMK from EAP
+	 * WPA-PSK: iterate through possible PSKs and select the one matching
+	 * the packet */
+	for (;;) {
+		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+			pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+					       sm->p2p_dev_addr, pmk);
+			if (pmk == NULL)
+				break;
+			psk_found = 1;
+		} else
+			pmk = sm->PMK;
+
+		wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK);
+
+		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
+				       sm->last_rx_eapol_key,
+				       sm->last_rx_eapol_key_len) == 0) {
+			ok = 1;
+			break;
+		}
+
+		if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+			break;
+	}
+
+	if (!ok) {
+		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+				"invalid MIC in msg 2/4 of 4-Way Handshake");
+		if (psk_found)
+			wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr);
+		return;
+	}
+
+#ifdef CONFIG_IEEE80211R
+	if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		/*
+		 * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
+		 * with the value we derived.
+		 */
+		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");
+			wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from "
+				    "Supplicant",
+				    sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
+			wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
+				    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+			return;
+		}
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	sm->pending_1_of_4_timeout = 0;
+	eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
+
+	if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+		/* PSK may have changed from the previous choice, so update
+		 * state machine data based on whatever PSK was selected here.
+		 */
+		os_memcpy(sm->PMK, pmk, PMK_LEN);
+	}
+
+	sm->MICVerified = TRUE;
+
+	os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+	sm->PTK_valid = TRUE;
+}
+
+
+SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2)
+{
+	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk);
+	sm->TimeoutCtr = 0;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+
+static int ieee80211w_kde_len(struct wpa_state_machine *sm)
+{
+	if (sm->mgmt_frame_prot) {
+		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;
+}
+
+
+static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
+{
+	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;
+
+	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, rsc) < 0)
+		os_memset(igtk.pn, 0, sizeof(igtk.pn));
+	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, len) < 0)
+			return pos;
+	}
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
+			  (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
+			  NULL, 0);
+
+	return pos;
+}
+
+#else /* CONFIG_IEEE80211W */
+
+static int ieee80211w_kde_len(struct wpa_state_machine *sm)
+{
+	return 0;
+}
+
+
+static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
+{
+	return pos;
+}
+
+#endif /* CONFIG_IEEE80211W */
+
+
+SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
+{
+	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32];
+	size_t gtk_len, kde_len;
+	struct wpa_group *gsm = sm->group;
+	u8 *wpa_ie;
+	int wpa_ie_len, secure, keyidx, encr = 0;
+
+	SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
+	sm->TimeoutEvt = FALSE;
+
+	sm->TimeoutCtr++;
+	if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+		/* No point in sending the EAPOL-Key - we will disconnect
+		 * immediately following this. */
+		return;
+	}
+
+	/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
+	   GTK[GN], IGTK, [FTIE], [TIE * 2])
+	 */
+	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
+	/* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
+	wpa_ie = sm->wpa_auth->wpa_ie;
+	wpa_ie_len = sm->wpa_auth->wpa_ie_len;
+	if (sm->wpa == WPA_VERSION_WPA &&
+	    (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+	    wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
+		/* WPA-only STA, remove RSN IE and possible MDIE */
+		wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
+			wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		wpa_ie_len = wpa_ie[1] + 2;
+	}
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"sending 3/4 msg of 4-Way Handshake");
+	if (sm->wpa == WPA_VERSION_WPA2) {
+		/* WPA2 send GTK in the 4-way handshake */
+		secure = 1;
+		gtk = gsm->GTK[gsm->GN - 1];
+		gtk_len = gsm->GTK_len;
+		if (sm->wpa_auth->conf.disable_gtk) {
+			/*
+			 * Provide unique random GTK to each STA to prevent use
+			 * of GTK in the BSS.
+			 */
+			if (random_get_bytes(dummy_gtk, gtk_len) < 0)
+				return;
+			gtk = dummy_gtk;
+		}
+		keyidx = gsm->GN;
+		_rsc = rsc;
+		encr = 1;
+	} else {
+		/* WPA does not include GTK in msg 3/4 */
+		secure = 0;
+		gtk = NULL;
+		gtk_len = 0;
+		keyidx = 0;
+		_rsc = NULL;
+		if (sm->rx_eapol_key_secure) {
+			/*
+			 * It looks like Windows 7 supplicant tries to use
+			 * Secure bit in msg 2/4 after having reported Michael
+			 * MIC failure and it then rejects the 4-way handshake
+			 * if msg 3/4 does not set Secure bit. Work around this
+			 * by setting the Secure bit here even in the case of
+			 * WPA if the supplicant used it first.
+			 */
+			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+					"STA used Secure bit in WPA msg 2/4 - "
+					"set Secure for 3/4 as workaround");
+			secure = 1;
+		}
+	}
+
+	kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
+	if (gtk)
+		kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
+		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;
+
+	pos = kde;
+	os_memcpy(pos, wpa_ie, wpa_ie_len);
+	pos += wpa_ie_len;
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR, "FT: Failed to insert "
+				   "PMKR1Name into RSN IE in EAPOL-Key data");
+			os_free(kde);
+			return;
+		}
+		pos += res;
+	}
+#endif /* CONFIG_IEEE80211R */
+	if (gtk) {
+		u8 hdr[2];
+		hdr[0] = keyidx & 0x03;
+		hdr[1] = 0;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+				  gtk, gtk_len);
+	}
+	pos = ieee80211w_kde_add(sm, pos);
+
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		int res;
+		struct wpa_auth_config *conf;
+
+		conf = &sm->wpa_auth->conf;
+		res = wpa_write_ftie(conf, conf->r0_key_holder,
+				     conf->r0_key_holder_len,
+				     NULL, NULL, pos, kde + kde_len - pos,
+				     NULL, 0);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
+				   "into EAPOL-Key Key Data");
+			os_free(kde);
+			return;
+		}
+		pos += res;
+
+		/* TIE[ReassociationDeadline] (TU) */
+		*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+		*pos++ = 5;
+		*pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
+		WPA_PUT_LE32(pos, conf->reassociation_deadline);
+		pos += 4;
+
+		/* TIE[KeyLifetime] (seconds) */
+		*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+		*pos++ = 5;
+		*pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
+		WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
+		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 |
+		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
+		       WPA_KEY_INFO_KEY_TYPE,
+		       _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
+	os_free(kde);
+}
+
+
+SM_STATE(WPA_PTK, PTKINITDONE)
+{
+	SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk);
+	sm->EAPOLKeyReceived = FALSE;
+	if (sm->Pair) {
+		enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise);
+		int klen = wpa_cipher_key_len(sm->pairwise);
+		if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+				     sm->PTK.tk, klen)) {
+			wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+			return;
+		}
+		/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
+		sm->pairwise_set = TRUE;
+
+		if (sm->wpa_auth->conf.wpa_ptk_rekey) {
+			eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+			eloop_register_timeout(sm->wpa_auth->conf.
+					       wpa_ptk_rekey, 0, wpa_rekey_ptk,
+					       sm->wpa_auth, sm);
+		}
+
+		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+			wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+					   WPA_EAPOL_authorized, 1);
+		}
+	}
+
+	if (0 /* IBSS == TRUE */) {
+		sm->keycount++;
+		if (sm->keycount == 2) {
+			wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+					   WPA_EAPOL_portValid, 1);
+		}
+	} else {
+		wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid,
+				   1);
+	}
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0);
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1);
+	if (sm->wpa == WPA_VERSION_WPA)
+		sm->PInitAKeys = TRUE;
+	else
+		sm->has_GTK = TRUE;
+	wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+			 "pairwise key handshake completed (%s)",
+			 sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
+
+#ifdef CONFIG_IEEE80211R
+	wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+SM_STEP(WPA_PTK)
+{
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+
+	if (sm->Init)
+		SM_ENTER(WPA_PTK, INITIALIZE);
+	else if (sm->Disconnect
+		 /* || FIX: dot11RSNAConfigSALifetime timeout */) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				"WPA_PTK: sm->Disconnect");
+		SM_ENTER(WPA_PTK, DISCONNECT);
+	}
+	else if (sm->DeauthenticationRequest)
+		SM_ENTER(WPA_PTK, DISCONNECTED);
+	else if (sm->AuthenticationRequest)
+		SM_ENTER(WPA_PTK, AUTHENTICATION);
+	else if (sm->ReAuthenticationRequest)
+		SM_ENTER(WPA_PTK, AUTHENTICATION2);
+	else if (sm->PTKRequest)
+		SM_ENTER(WPA_PTK, PTKSTART);
+	else switch (sm->wpa_ptk_state) {
+	case WPA_PTK_INITIALIZE:
+		break;
+	case WPA_PTK_DISCONNECT:
+		SM_ENTER(WPA_PTK, DISCONNECTED);
+		break;
+	case WPA_PTK_DISCONNECTED:
+		SM_ENTER(WPA_PTK, INITIALIZE);
+		break;
+	case WPA_PTK_AUTHENTICATION:
+		SM_ENTER(WPA_PTK, AUTHENTICATION2);
+		break;
+	case WPA_PTK_AUTHENTICATION2:
+		if (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+		    wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
+				       WPA_EAPOL_keyRun) > 0)
+			SM_ENTER(WPA_PTK, INITPMK);
+		else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)
+			 /* FIX: && 802.1X::keyRun */)
+			SM_ENTER(WPA_PTK, INITPSK);
+		break;
+	case WPA_PTK_INITPMK:
+		if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
+				       WPA_EAPOL_keyAvailable) > 0)
+			SM_ENTER(WPA_PTK, PTKSTART);
+		else {
+			wpa_auth->dot11RSNA4WayHandshakeFailures++;
+			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+					"INITPMK - keyAvailable = false");
+			SM_ENTER(WPA_PTK, DISCONNECT);
+		}
+		break;
+	case WPA_PTK_INITPSK:
+		if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr,
+				     NULL))
+			SM_ENTER(WPA_PTK, PTKSTART);
+		else {
+			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+					"no PSK configured for the STA");
+			wpa_auth->dot11RSNA4WayHandshakeFailures++;
+			SM_ENTER(WPA_PTK, DISCONNECT);
+		}
+		break;
+	case WPA_PTK_PTKSTART:
+		if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+		    sm->EAPOLKeyPairwise)
+			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+		else if (sm->TimeoutCtr >
+			 (int) dot11RSNAConfigPairwiseUpdateCount) {
+			wpa_auth->dot11RSNA4WayHandshakeFailures++;
+			wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "PTKSTART: Retry limit %d reached",
+					 dot11RSNAConfigPairwiseUpdateCount);
+			SM_ENTER(WPA_PTK, DISCONNECT);
+		} else if (sm->TimeoutEvt)
+			SM_ENTER(WPA_PTK, PTKSTART);
+		break;
+	case WPA_PTK_PTKCALCNEGOTIATING:
+		if (sm->MICVerified)
+			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2);
+		else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+			 sm->EAPOLKeyPairwise)
+			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+		else if (sm->TimeoutEvt)
+			SM_ENTER(WPA_PTK, PTKSTART);
+		break;
+	case WPA_PTK_PTKCALCNEGOTIATING2:
+		SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
+		break;
+	case WPA_PTK_PTKINITNEGOTIATING:
+		if (sm->update_snonce)
+			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+		else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+			 sm->EAPOLKeyPairwise && sm->MICVerified)
+			SM_ENTER(WPA_PTK, PTKINITDONE);
+		else if (sm->TimeoutCtr >
+			 (int) dot11RSNAConfigPairwiseUpdateCount) {
+			wpa_auth->dot11RSNA4WayHandshakeFailures++;
+			wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "PTKINITNEGOTIATING: Retry limit %d "
+					 "reached",
+					 dot11RSNAConfigPairwiseUpdateCount);
+			SM_ENTER(WPA_PTK, DISCONNECT);
+		} else if (sm->TimeoutEvt)
+			SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
+		break;
+	case WPA_PTK_PTKINITDONE:
+		break;
+	}
+}
+
+
+SM_STATE(WPA_PTK_GROUP, IDLE)
+{
+	SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group);
+	if (sm->Init) {
+		/* Init flag is not cleared here, so avoid busy
+		 * loop by claiming nothing changed. */
+		sm->changed = FALSE;
+	}
+	sm->GTimeoutCtr = 0;
+}
+
+
+SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
+{
+	u8 rsc[WPA_KEY_RSC_LEN];
+	struct wpa_group *gsm = sm->group;
+	const u8 *kde;
+	u8 *kde_buf = NULL, *pos, hdr[2];
+	size_t kde_len;
+	u8 *gtk, dummy_gtk[32];
+
+	SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
+
+	sm->GTimeoutCtr++;
+	if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) {
+		/* No point in sending the EAPOL-Key - we will disconnect
+		 * immediately following this. */
+		return;
+	}
+
+	if (sm->wpa == WPA_VERSION_WPA)
+		sm->PInitAKeys = FALSE;
+	sm->TimeoutEvt = FALSE;
+	/* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
+	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+	if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE)
+		wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"sending 1/2 msg of Group Key Handshake");
+
+	gtk = gsm->GTK[gsm->GN - 1];
+	if (sm->wpa_auth->conf.disable_gtk) {
+		/*
+		 * Provide unique random GTK to each STA to prevent use
+		 * of GTK in the BSS.
+		 */
+		if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0)
+			return;
+		gtk = dummy_gtk;
+	}
+	if (sm->wpa == WPA_VERSION_WPA2) {
+		kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
+			ieee80211w_kde_len(sm);
+		kde_buf = os_malloc(kde_len);
+		if (kde_buf == NULL)
+			return;
+
+		kde = pos = kde_buf;
+		hdr[0] = gsm->GN & 0x03;
+		hdr[1] = 0;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+				  gtk, gsm->GTK_len);
+		pos = ieee80211w_kde_add(sm, pos);
+		kde_len = pos - kde;
+	} else {
+		kde = gtk;
+		kde_len = gsm->GTK_len;
+	}
+
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+		       WPA_KEY_INFO_ACK |
+		       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
+		       rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1);
+
+	os_free(kde_buf);
+}
+
+
+SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
+{
+	SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group);
+	sm->EAPOLKeyReceived = FALSE;
+	if (sm->GUpdateStationKeys)
+		sm->group->GKeyDoneStations--;
+	sm->GUpdateStationKeys = FALSE;
+	sm->GTimeoutCtr = 0;
+	/* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
+	wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+			 "group key handshake completed (%s)",
+			 sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
+	sm->has_GTK = TRUE;
+}
+
+
+SM_STATE(WPA_PTK_GROUP, KEYERROR)
+{
+	SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
+	if (sm->GUpdateStationKeys)
+		sm->group->GKeyDoneStations--;
+	sm->GUpdateStationKeys = FALSE;
+	sm->Disconnect = TRUE;
+}
+
+
+SM_STEP(WPA_PTK_GROUP)
+{
+	if (sm->Init || sm->PtkGroupInit) {
+		SM_ENTER(WPA_PTK_GROUP, IDLE);
+		sm->PtkGroupInit = FALSE;
+	} else switch (sm->wpa_ptk_group_state) {
+	case WPA_PTK_GROUP_IDLE:
+		if (sm->GUpdateStationKeys ||
+		    (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys))
+			SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
+		break;
+	case WPA_PTK_GROUP_REKEYNEGOTIATING:
+		if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+		    !sm->EAPOLKeyPairwise && sm->MICVerified)
+			SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
+		else if (sm->GTimeoutCtr >
+			 (int) dot11RSNAConfigGroupUpdateCount)
+			SM_ENTER(WPA_PTK_GROUP, KEYERROR);
+		else if (sm->TimeoutEvt)
+			SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
+		break;
+	case WPA_PTK_GROUP_KEYERROR:
+		SM_ENTER(WPA_PTK_GROUP, IDLE);
+		break;
+	case WPA_PTK_GROUP_REKEYESTABLISHED:
+		SM_ENTER(WPA_PTK_GROUP, IDLE);
+		break;
+	}
+}
+
+
+static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group)
+{
+	int ret = 0;
+
+	os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+	inc_byte_array(group->Counter, WPA_NONCE_LEN);
+	if (wpa_gmk_to_gtk(group->GMK, "Group key expansion",
+			   wpa_auth->addr, group->GNonce,
+			   group->GTK[group->GN - 1], group->GTK_len) < 0)
+		ret = -1;
+	wpa_hexdump_key(MSG_DEBUG, "GTK",
+			group->GTK[group->GN - 1], group->GTK_len);
+
+#ifdef CONFIG_IEEE80211W
+	if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		size_t len;
+		len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+		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], len) < 0)
+			ret = -1;
+		wpa_hexdump_key(MSG_DEBUG, "IGTK",
+				group->IGTK[group->GN_igtk - 4], len);
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	return ret;
+}
+
+
+static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth,
+			       struct wpa_group *group)
+{
+	wpa_printf(MSG_DEBUG, "WPA: group state machine entering state "
+		   "GTK_INIT (VLAN-ID %d)", group->vlan_id);
+	group->changed = FALSE; /* GInit is not cleared here; avoid loop */
+	group->wpa_group_state = WPA_GROUP_GTK_INIT;
+
+	/* GTK[0..N] = 0 */
+	os_memset(group->GTK, 0, sizeof(group->GTK));
+	group->GN = 1;
+	group->GM = 2;
+#ifdef CONFIG_IEEE80211W
+	group->GN_igtk = 4;
+	group->GM_igtk = 5;
+#endif /* CONFIG_IEEE80211W */
+	/* GTK[GN] = CalcGTK() */
+	wpa_gtk_update(wpa_auth, group);
+}
+
+
+static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
+{
+	if (ctx != NULL && ctx != sm->group)
+		return 0;
+
+	if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) {
+		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+				"Not in PTKINITDONE; skip Group Key update");
+		sm->GUpdateStationKeys = FALSE;
+		return 0;
+	}
+	if (sm->GUpdateStationKeys) {
+		/*
+		 * This should not really happen, so add a debug log entry.
+		 * Since we clear the GKeyDoneStations before the loop, the
+		 * station needs to be counted here anyway.
+		 */
+		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+				"GUpdateStationKeys was already set when "
+				"marking station for GTK rekeying");
+	}
+
+	/* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */
+	if (sm->is_wnmsleep)
+		return 0;
+
+	sm->group->GKeyDoneStations++;
+	sm->GUpdateStationKeys = TRUE;
+
+	wpa_sm_step(sm);
+	return 0;
+}
+
+
+#ifdef CONFIG_WNM
+/* update GTK when exiting WNM-Sleep Mode */
+void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
+{
+	if (sm == NULL || sm->is_wnmsleep)
+		return;
+
+	wpa_group_update_sta(sm, NULL);
+}
+
+
+void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag)
+{
+	if (sm)
+		sm->is_wnmsleep = !!flag;
+}
+
+
+int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+	struct wpa_group *gsm = sm->group;
+	u8 *start = pos;
+
+	/*
+	 * GTK subelement:
+	 * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
+	 * Key[5..32]
+	 */
+	*pos++ = WNM_SLEEP_SUBELEM_GTK;
+	*pos++ = 11 + gsm->GTK_len;
+	/* Key ID in B0-B1 of Key Info */
+	WPA_PUT_LE16(pos, gsm->GN & 0x03);
+	pos += 2;
+	*pos++ = gsm->GTK_len;
+	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0)
+		return 0;
+	pos += 8;
+	os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+	pos += gsm->GTK_len;
+
+	wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit",
+		   gsm->GN);
+	wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit",
+			gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+
+	return pos - start;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+	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 + 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], 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], len);
+
+	return pos - start;
+}
+#endif /* CONFIG_IEEE80211W */
+#endif /* CONFIG_WNM */
+
+
+static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+			      struct wpa_group *group)
+{
+	int tmp;
+
+	wpa_printf(MSG_DEBUG, "WPA: group state machine entering state "
+		   "SETKEYS (VLAN-ID %d)", group->vlan_id);
+	group->changed = TRUE;
+	group->wpa_group_state = WPA_GROUP_SETKEYS;
+	group->GTKReKey = FALSE;
+	tmp = group->GM;
+	group->GM = group->GN;
+	group->GN = tmp;
+#ifdef CONFIG_IEEE80211W
+	tmp = group->GM_igtk;
+	group->GM_igtk = group->GN_igtk;
+	group->GN_igtk = tmp;
+#endif /* CONFIG_IEEE80211W */
+	/* "GKeyDoneStations = GNoStations" is done in more robust way by
+	 * counting the STAs that are marked with GUpdateStationKeys instead of
+	 * including all STAs that could be in not-yet-completed state. */
+	wpa_gtk_update(wpa_auth, group);
+
+	if (group->GKeyDoneStations) {
+		wpa_printf(MSG_DEBUG, "wpa_group_setkeys: Unexpected "
+			   "GKeyDoneStations=%d when starting new GTK rekey",
+			   group->GKeyDoneStations);
+		group->GKeyDoneStations = 0;
+	}
+	wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
+	wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
+		   group->GKeyDoneStations);
+}
+
+
+static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
+				       struct wpa_group *group)
+{
+	int ret = 0;
+
+	if (wpa_auth_set_key(wpa_auth, group->vlan_id,
+			     wpa_cipher_to_alg(wpa_auth->conf.wpa_group),
+			     broadcast_ether_addr, group->GN,
+			     group->GTK[group->GN - 1], group->GTK_len) < 0)
+		ret = -1;
+
+#ifdef CONFIG_IEEE80211W
+	if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		enum wpa_alg alg;
+		size_t len;
+
+		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)
+{
+	wpa_printf(MSG_DEBUG, "WPA: group state machine entering state "
+		   "SETKEYSDONE (VLAN-ID %d)", group->vlan_id);
+	group->changed = TRUE;
+	group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
+
+	if (wpa_group_config_group_keys(wpa_auth, group) < 0) {
+		wpa_group_fatal_failure(wpa_auth, group);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
+			      struct wpa_group *group)
+{
+	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);
+	} else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE &&
+		   group->GTKReKey) {
+		wpa_group_setkeys(wpa_auth, group);
+	} else if (group->wpa_group_state == WPA_GROUP_SETKEYS) {
+		if (group->GKeyDoneStations == 0)
+			wpa_group_setkeysdone(wpa_auth, group);
+		else if (group->GTKReKey)
+			wpa_group_setkeys(wpa_auth, group);
+	}
+}
+
+
+static int wpa_sm_step(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return 0;
+
+	if (sm->in_step_loop) {
+		/* This should not happen, but if it does, make sure we do not
+		 * end up freeing the state machine too early by exiting the
+		 * recursive call. */
+		wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively");
+		return 0;
+	}
+
+	sm->in_step_loop = 1;
+	do {
+		if (sm->pending_deinit)
+			break;
+
+		sm->changed = FALSE;
+		sm->wpa_auth->group->changed = FALSE;
+
+		SM_STEP_RUN(WPA_PTK);
+		if (sm->pending_deinit)
+			break;
+		SM_STEP_RUN(WPA_PTK_GROUP);
+		if (sm->pending_deinit)
+			break;
+		wpa_group_sm_step(sm->wpa_auth, sm->group);
+	} while (sm->changed || sm->wpa_auth->group->changed);
+	sm->in_step_loop = 0;
+
+	if (sm->pending_deinit) {
+		wpa_printf(MSG_DEBUG, "WPA: Completing pending STA state "
+			   "machine deinit for " MACSTR, MAC2STR(sm->addr));
+		wpa_free_sta_sm(sm);
+		return 1;
+	}
+	return 0;
+}
+
+
+static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_state_machine *sm = eloop_ctx;
+	wpa_sm_step(sm);
+}
+
+
+void wpa_auth_sm_notify(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return;
+	eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL);
+}
+
+
+void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth)
+{
+	int tmp, i;
+	struct wpa_group *group;
+
+	if (wpa_auth == NULL)
+		return;
+
+	group = wpa_auth->group;
+
+	for (i = 0; i < 2; i++) {
+		tmp = group->GM;
+		group->GM = group->GN;
+		group->GN = tmp;
+#ifdef CONFIG_IEEE80211W
+		tmp = group->GM_igtk;
+		group->GM_igtk = group->GN_igtk;
+		group->GN_igtk = tmp;
+#endif /* CONFIG_IEEE80211W */
+		wpa_gtk_update(wpa_auth, group);
+		wpa_group_config_group_keys(wpa_auth, group);
+	}
+}
+
+
+static const char * wpa_bool_txt(int val)
+{
+	return val ? "TRUE" : "FALSE";
+}
+
+
+#define RSN_SUITE "%02x-%02x-%02x-%d"
+#define RSN_SUITE_ARG(s) \
+((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
+
+int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
+{
+	int len = 0, ret;
+	char pmkid_txt[PMKID_LEN * 2 + 1];
+#ifdef CONFIG_RSN_PREAUTH
+	const int preauth = 1;
+#else /* CONFIG_RSN_PREAUTH */
+	const int preauth = 0;
+#endif /* CONFIG_RSN_PREAUTH */
+
+	if (wpa_auth == NULL)
+		return len;
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot11RSNAOptionImplemented=TRUE\n"
+			  "dot11RSNAPreauthenticationImplemented=%s\n"
+			  "dot11RSNAEnabled=%s\n"
+			  "dot11RSNAPreauthenticationEnabled=%s\n",
+			  wpa_bool_txt(preauth),
+			  wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN),
+			  wpa_bool_txt(wpa_auth->conf.rsn_preauth));
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt),
+			 wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN);
+
+	ret = os_snprintf(
+		buf + len, buflen - len,
+		"dot11RSNAConfigVersion=%u\n"
+		"dot11RSNAConfigPairwiseKeysSupported=9999\n"
+		/* FIX: dot11RSNAConfigGroupCipher */
+		/* FIX: dot11RSNAConfigGroupRekeyMethod */
+		/* FIX: dot11RSNAConfigGroupRekeyTime */
+		/* FIX: dot11RSNAConfigGroupRekeyPackets */
+		"dot11RSNAConfigGroupRekeyStrict=%u\n"
+		"dot11RSNAConfigGroupUpdateCount=%u\n"
+		"dot11RSNAConfigPairwiseUpdateCount=%u\n"
+		"dot11RSNAConfigGroupCipherSize=%u\n"
+		"dot11RSNAConfigPMKLifetime=%u\n"
+		"dot11RSNAConfigPMKReauthThreshold=%u\n"
+		"dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n"
+		"dot11RSNAConfigSATimeout=%u\n"
+		"dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
+		"dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
+		"dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
+		"dot11RSNAPMKIDUsed=%s\n"
+		"dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
+		"dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
+		"dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
+		"dot11RSNATKIPCounterMeasuresInvoked=%u\n"
+		"dot11RSNA4WayHandshakeFailures=%u\n"
+		"dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n",
+		RSN_VERSION,
+		!!wpa_auth->conf.wpa_strict_rekey,
+		dot11RSNAConfigGroupUpdateCount,
+		dot11RSNAConfigPairwiseUpdateCount,
+		wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8,
+		dot11RSNAConfigPMKLifetime,
+		dot11RSNAConfigPMKReauthThreshold,
+		dot11RSNAConfigSATimeout,
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected),
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected),
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected),
+		pmkid_txt,
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested),
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested),
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested),
+		wpa_auth->dot11RSNATKIPCounterMeasuresInvoked,
+		wpa_auth->dot11RSNA4WayHandshakeFailures);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	/* TODO: dot11RSNAConfigPairwiseCiphersTable */
+	/* TODO: dot11RSNAConfigAuthenticationSuitesTable */
+
+	/* Private MIB */
+	ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n",
+			  wpa_auth->group->wpa_group_state);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	return len;
+}
+
+
+int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
+{
+	int len = 0, ret;
+	u32 pairwise = 0;
+
+	if (sm == NULL)
+		return 0;
+
+	/* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */
+
+	/* dot11RSNAStatsEntry */
+
+	pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ?
+				       WPA_PROTO_RSN : WPA_PROTO_WPA,
+				       sm->pairwise);
+	if (pairwise == 0)
+		return 0;
+
+	ret = os_snprintf(
+		buf + len, buflen - len,
+		/* TODO: dot11RSNAStatsIndex */
+		"dot11RSNAStatsSTAAddress=" MACSTR "\n"
+		"dot11RSNAStatsVersion=1\n"
+		"dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n"
+		/* TODO: dot11RSNAStatsTKIPICVErrors */
+		"dot11RSNAStatsTKIPLocalMICFailures=%u\n"
+		"dot11RSNAStatsTKIPRemoteMICFailures=%u\n"
+		/* TODO: dot11RSNAStatsCCMPReplays */
+		/* TODO: dot11RSNAStatsCCMPDecryptErrors */
+		/* TODO: dot11RSNAStatsTKIPReplays */,
+		MAC2STR(sm->addr),
+		RSN_SUITE_ARG(pairwise),
+		sm->dot11RSNAStatsTKIPLocalMICFailures,
+		sm->dot11RSNAStatsTKIPRemoteMICFailures);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	/* Private MIB */
+	ret = os_snprintf(buf + len, buflen - len,
+			  "hostapdWPAPTKState=%d\n"
+			  "hostapdWPAPTKGroupState=%d\n",
+			  sm->wpa_ptk_state,
+			  sm->wpa_ptk_group_state);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	return len;
+}
+
+
+void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth)
+{
+	if (wpa_auth)
+		wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++;
+}
+
+
+int wpa_auth_pairwise_set(struct wpa_state_machine *sm)
+{
+	return sm && sm->pairwise_set;
+}
+
+
+int wpa_auth_get_pairwise(struct wpa_state_machine *sm)
+{
+	return sm->pairwise;
+}
+
+
+int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return -1;
+	return sm->wpa_key_mgmt;
+}
+
+
+int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return 0;
+	return sm->wpa;
+}
+
+
+int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
+			     struct rsn_pmksa_cache_entry *entry)
+{
+	if (sm == NULL || sm->pmksa != entry)
+		return -1;
+	sm->pmksa = NULL;
+	return 0;
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm)
+{
+	return sm ? sm->pmksa : NULL;
+}
+
+
+void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm)
+{
+	if (sm)
+		sm->dot11RSNAStatsTKIPLocalMICFailures++;
+}
+
+
+const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len)
+{
+	if (wpa_auth == NULL)
+		return NULL;
+	*len = wpa_auth->wpa_ie_len;
+	return wpa_auth->wpa_ie;
+}
+
+
+int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
+		       int session_timeout, struct eapol_state_machine *eapol)
+{
+	if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 ||
+	    sm->wpa_auth->conf.disable_pmksa_caching)
+		return -1;
+
+	if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN,
+				 sm->PTK.kck, sm->PTK.kck_len,
+				 sm->wpa_auth->addr, sm->addr, session_timeout,
+				 eapol, sm->wpa_key_mgmt))
+		return 0;
+
+	return -1;
+}
+
+
+int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
+			       const u8 *pmk, size_t len, const u8 *sta_addr,
+			       int session_timeout,
+			       struct eapol_state_machine *eapol)
+{
+	if (wpa_auth == NULL)
+		return -1;
+
+	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len,
+				 NULL, 0,
+				 wpa_auth->addr,
+				 sta_addr, session_timeout, eapol,
+				 WPA_KEY_MGMT_IEEE8021X))
+		return 0;
+
+	return -1;
+}
+
+
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			   const u8 *pmk)
+{
+	if (wpa_auth->conf.disable_pmksa_caching)
+		return -1;
+
+	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN,
+				 NULL, 0,
+				 wpa_auth->addr, addr, 0, NULL,
+				 WPA_KEY_MGMT_SAE))
+		return 0;
+
+	return -1;
+}
+
+
+void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
+			   const u8 *sta_addr)
+{
+	struct rsn_pmksa_cache_entry *pmksa;
+
+	if (wpa_auth == NULL || wpa_auth->pmksa == NULL)
+		return;
+	pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+	if (pmksa) {
+		wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for "
+			   MACSTR " based on request", MAC2STR(sta_addr));
+		pmksa_cache_free_entry(wpa_auth->pmksa, pmksa);
+	}
+}
+
+
+/*
+ * Remove and free the group from wpa_authenticator. This is triggered by a
+ * callback to make sure nobody is currently iterating the group list while it
+ * gets modified.
+ */
+static void wpa_group_free(struct wpa_authenticator *wpa_auth,
+			   struct wpa_group *group)
+{
+	struct wpa_group *prev = wpa_auth->group;
+
+	wpa_printf(MSG_DEBUG, "WPA: Remove group state machine for VLAN-ID %d",
+		   group->vlan_id);
+
+	while (prev) {
+		if (prev->next == group) {
+			/* This never frees the special first group as needed */
+			prev->next = group->next;
+			os_free(group);
+			break;
+		}
+		prev = prev->next;
+	}
+
+}
+
+
+/* Increase the reference counter for group */
+static void wpa_group_get(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group)
+{
+	/* Skip the special first group */
+	if (wpa_auth->group == group)
+		return;
+
+	group->references++;
+}
+
+
+/* Decrease the reference counter and maybe free the group */
+static void wpa_group_put(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group)
+{
+	/* Skip the special first group */
+	if (wpa_auth->group == group)
+		return;
+
+	group->references--;
+	if (group->references)
+		return;
+	wpa_group_free(wpa_auth, group);
+}
+
+
+/*
+ * Add a group that has its references counter set to zero. Caller needs to
+ * call wpa_group_get() on the return value to mark the entry in use.
+ */
+static struct wpa_group *
+wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+	struct wpa_group *group;
+
+	if (wpa_auth == NULL || wpa_auth->group == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d",
+		   vlan_id);
+	group = wpa_group_init(wpa_auth, vlan_id, 0);
+	if (group == NULL)
+		return NULL;
+
+	group->next = wpa_auth->group->next;
+	wpa_auth->group->next = group;
+
+	return group;
+}
+
+
+int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
+{
+	struct wpa_group *group;
+
+	if (sm == NULL || sm->wpa_auth == NULL)
+		return 0;
+
+	group = sm->wpa_auth->group;
+	while (group) {
+		if (group->vlan_id == vlan_id)
+			break;
+		group = group->next;
+	}
+
+	if (group == NULL) {
+		group = wpa_auth_add_group(sm->wpa_auth, vlan_id);
+		if (group == NULL)
+			return -1;
+	}
+
+	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);
+
+	wpa_group_get(sm->wpa_auth, group);
+	wpa_group_put(sm->wpa_auth, sm->group);
+	sm->group = group;
+
+	return 0;
+}
+
+
+void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+				  struct wpa_state_machine *sm, int ack)
+{
+	if (wpa_auth == NULL || sm == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR
+		   " ack=%d", MAC2STR(sm->addr), ack);
+	if (sm->pending_1_of_4_timeout && ack) {
+		/*
+		 * Some deployed supplicant implementations update their SNonce
+		 * for each EAPOL-Key 2/4 message even within the same 4-way
+		 * handshake and then fail to use the first SNonce when
+		 * deriving the PTK. This results in unsuccessful 4-way
+		 * handshake whenever the relatively short initial timeout is
+		 * reached and EAPOL-Key 1/4 is retransmitted. Try to work
+		 * around this by increasing the timeout now that we know that
+		 * the station has received the frame.
+		 */
+		int timeout_ms = eapol_key_timeout_subseq;
+		wpa_printf(MSG_DEBUG, "WPA: Increase initial EAPOL-Key 1/4 "
+			   "timeout by %u ms because of acknowledged frame",
+			   timeout_ms);
+		eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+		eloop_register_timeout(timeout_ms / 1000,
+				       (timeout_ms % 1000) * 1000,
+				       wpa_send_eapol_timeout, wpa_auth, sm);
+	}
+}
+
+
+int wpa_auth_uses_sae(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		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 */
+
+
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+					 struct radius_das_attrs *attr)
+{
+	return pmksa_cache_auth_radius_das_disconnect(wpa_auth->pmksa, attr);
+}
+
+
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth)
+{
+	struct wpa_group *group;
+
+	if (!wpa_auth)
+		return;
+	for (group = wpa_auth->group; group; group = group->next)
+		wpa_group_config_group_keys(wpa_auth, group);
+}
diff --git a/hostap/src/ap/wpa_auth.h b/hostap/src/ap/wpa_auth.h
new file mode 100644
index 0000000..fd04f16
--- /dev/null
+++ b/hostap/src/ap/wpa_auth.h
@@ -0,0 +1,328 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_H
+#define WPA_AUTH_H
+
+#include "common/defs.h"
+#include "common/eapol_common.h"
+#include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
+
+#define MAX_OWN_IE_OVERRIDE 256
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+/* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition
+ */
+struct ft_rrb_frame {
+	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+	u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */
+	le16 action_length; /* little endian length of action_frame */
+	u8 ap_address[ETH_ALEN];
+	/*
+	 * Followed by action_length bytes of FT Action frame (from Category
+	 * field to the end of Action Frame body.
+	 */
+} STRUCT_PACKED;
+
+#define RSN_REMOTE_FRAME_TYPE_FT_RRB 1
+
+#define FT_PACKET_REQUEST 0
+#define FT_PACKET_RESPONSE 1
+/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */
+#define FT_PACKET_R0KH_R1KH_PULL 200
+#define FT_PACKET_R0KH_R1KH_RESP 201
+#define FT_PACKET_R0KH_R1KH_PUSH 202
+
+#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 */
+	u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */
+	le16 data_length; /* little endian length of data (44) */
+	u8 ap_address[ETH_ALEN];
+
+	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];
+	u8 pad[4]; /* 8-octet boundary for AES key wrap */
+	u8 key_wrap_extra[8];
+} STRUCT_PACKED;
+
+struct ft_r0kh_r1kh_resp_frame {
+	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+	u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */
+	le16 data_length; /* little endian length of data (76) */
+	u8 ap_address[ETH_ALEN];
+
+	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];
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+	le16 pairwise;
+	u8 pad[2]; /* 8-octet boundary for AES key wrap */
+	u8 key_wrap_extra[8];
+} STRUCT_PACKED;
+
+struct ft_r0kh_r1kh_push_frame {
+	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+	u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */
+	le16 data_length; /* little endian length of data (88) */
+	u8 ap_address[ETH_ALEN];
+
+	/* Encrypted with AES key-wrap */
+	u8 timestamp[4]; /* current time in seconds since unix epoch, little
+			  * endian */
+	u8 r1kh_id[FT_R1KH_ID_LEN];
+	u8 s1kh_id[ETH_ALEN];
+	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN];
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+	le16 pairwise;
+	u8 pad[6]; /* 8-octet boundary for AES key wrap */
+	u8 key_wrap_extra[8];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+/* per STA state machine data */
+
+struct wpa_authenticator;
+struct wpa_state_machine;
+struct rsn_pmksa_cache_entry;
+struct eapol_state_machine;
+
+
+struct ft_remote_r0kh {
+	struct ft_remote_r0kh *next;
+	u8 addr[ETH_ALEN];
+	u8 id[FT_R0KH_ID_MAX_LEN];
+	size_t id_len;
+	u8 key[16];
+};
+
+
+struct ft_remote_r1kh {
+	struct ft_remote_r1kh *next;
+	u8 addr[ETH_ALEN];
+	u8 id[FT_R1KH_ID_LEN];
+	u8 key[16];
+};
+
+
+struct wpa_auth_config {
+	int wpa;
+	int wpa_key_mgmt;
+	int wpa_pairwise;
+	int wpa_group;
+	int wpa_group_rekey;
+	int wpa_strict_rekey;
+	int wpa_gmk_rekey;
+	int wpa_ptk_rekey;
+	int rsn_pairwise;
+	int rsn_preauth;
+	int eapol_version;
+	int peerkey;
+	int wmm_enabled;
+	int wmm_uapsd;
+	int disable_pmksa_caching;
+	int okc;
+	int tx_status;
+#ifdef CONFIG_IEEE80211W
+	enum mfp_options ieee80211w;
+	int group_mgmt_cipher;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+	u8 r0_key_holder[FT_R0KH_ID_MAX_LEN];
+	size_t r0_key_holder_len;
+	u8 r1_key_holder[FT_R1KH_ID_LEN];
+	u32 r0_key_lifetime;
+	u32 reassociation_deadline;
+	struct ft_remote_r0kh *r0kh_list;
+	struct ft_remote_r1kh *r1kh_list;
+	int pmk_r1_push;
+	int ft_over_ds;
+#endif /* CONFIG_IEEE80211R */
+	int disable_gtk;
+	int ap_mlme;
+#ifdef CONFIG_TESTING_OPTIONS
+	double corrupt_gtk_rekey_mic_probability;
+	u8 own_ie_override[MAX_OWN_IE_OVERRIDE];
+	size_t own_ie_override_len;
+#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 {
+	LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING
+} logger_level;
+
+typedef enum {
+	WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized,
+	WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable,
+	WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx
+} wpa_eapol_variable;
+
+struct wpa_auth_callbacks {
+	void *ctx;
+	void (*logger)(void *ctx, const u8 *addr, logger_level level,
+		       const char *txt);
+	void (*disconnect)(void *ctx, const u8 *addr, u16 reason);
+	int (*mic_failure_report)(void *ctx, const u8 *addr);
+	void (*psk_failure_report)(void *ctx, const u8 *addr);
+	void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var,
+			  int value);
+	int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
+	const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr,
+			      const u8 *prev_psk);
+	int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
+	int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
+		       const u8 *addr, int idx, u8 *key, size_t key_len);
+	int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq);
+	int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data,
+			  size_t data_len, int encrypt);
+	int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm,
+						 void *ctx), void *cb_ctx);
+	int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a,
+						  void *ctx), void *cb_ctx);
+	int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
+			  size_t data_len);
+#ifdef CONFIG_IEEE80211R
+	struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
+	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);
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_MESH
+	int (*start_ampe)(void *ctx, const u8 *sta_addr);
+#endif /* CONFIG_MESH */
+};
+
+struct wpa_authenticator * wpa_init(const u8 *addr,
+				    struct wpa_auth_config *conf,
+				    struct wpa_auth_callbacks *cb);
+int wpa_init_keys(struct wpa_authenticator *wpa_auth);
+void wpa_deinit(struct wpa_authenticator *wpa_auth);
+int wpa_reconfig(struct wpa_authenticator *wpa_auth,
+		 struct wpa_auth_config *conf);
+
+enum {
+	WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
+	WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL,
+	WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
+	WPA_INVALID_MDIE, WPA_INVALID_PROTO
+};
+	
+int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+			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,
+		  const u8 *p2p_dev_addr);
+int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
+			    struct wpa_state_machine *sm);
+void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm);
+void wpa_auth_sta_deinit(struct wpa_state_machine *sm);
+void wpa_receive(struct wpa_authenticator *wpa_auth,
+		 struct wpa_state_machine *sm,
+		 u8 *data, size_t data_len);
+enum wpa_event {
+	WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
+	WPA_REAUTH_EAPOL, WPA_ASSOC_FT
+};
+void wpa_remove_ptk(struct wpa_state_machine *sm);
+int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event);
+void wpa_auth_sm_notify(struct wpa_state_machine *sm);
+void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth);
+int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen);
+int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen);
+void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth);
+int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
+int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
+int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
+int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
+int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
+			     struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache_entry *
+wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm);
+void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm);
+const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth,
+			       size_t *len);
+int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
+		       int session_timeout, struct eapol_state_machine *eapol);
+int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
+			       const u8 *pmk, size_t len, const u8 *sta_addr,
+			       int session_timeout,
+			       struct eapol_state_machine *eapol);
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			   const u8 *pmk);
+void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
+			   const u8 *sta_addr);
+int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
+void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+				  struct wpa_state_machine *sm, int ack);
+
+#ifdef CONFIG_IEEE80211R
+u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
+				 size_t max_len, int auth_alg,
+				 const u8 *req_ies, size_t req_ies_len);
+void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
+			 u16 auth_transaction, const u8 *ies, size_t ies_len,
+			 void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
+				    u16 auth_transaction, u16 resp,
+				    const u8 *ies, size_t ies_len),
+			 void *ctx);
+u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
+			    size_t ies_len);
+int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len);
+int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+		  const u8 *data, size_t data_len);
+void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
+#endif /* CONFIG_IEEE80211R */
+
+void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
+void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
+int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos);
+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);
+
+struct radius_das_attrs;
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+					 struct radius_das_attrs *attr);
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
+
+#endif /* WPA_AUTH_H */
diff --git a/hostap/src/ap/wpa_auth_ft.c b/hostap/src/ap/wpa_auth_ft.c
new file mode 100644
index 0000000..eeaffbf
--- /dev/null
+++ b/hostap/src/ap/wpa_auth_ft.c
@@ -0,0 +1,1792 @@
+/*
+ * hostapd - IEEE 802.11r - Fast BSS Transition
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wmm.h"
+#include "wpa_auth.h"
+#include "wpa_auth_i.h"
+
+
+#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)
+{
+	if (wpa_auth->cb.send_ether == NULL)
+		return -1;
+	wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst));
+	return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB,
+				       data, data_len);
+}
+
+
+static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
+			      const u8 *dst, const u8 *data, size_t data_len)
+{
+	if (wpa_auth->cb.send_ft_action == NULL)
+		return -1;
+	return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst,
+					   data, data_len);
+}
+
+
+static struct wpa_state_machine *
+wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+{
+	if (wpa_auth->cb.add_sta == NULL)
+		return NULL;
+	return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr);
+}
+
+
+static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
+			    const u8 *sta_addr,
+			    u8 *tspec_ie, size_t tspec_ielen)
+{
+	if (wpa_auth->cb.add_tspec == NULL) {
+		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,
+				      tspec_ielen);
+}
+
+
+int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+	u8 *pos = buf;
+	u8 capab;
+	if (len < 2 + sizeof(struct rsn_mdie))
+		return -1;
+
+	*pos++ = WLAN_EID_MOBILITY_DOMAIN;
+	*pos++ = MOBILITY_DOMAIN_ID_LEN + 1;
+	os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
+	pos += MOBILITY_DOMAIN_ID_LEN;
+	capab = 0;
+	if (conf->ft_over_ds)
+		capab |= RSN_FT_CAPAB_FT_OVER_DS;
+	*pos++ = capab;
+
+	return pos - buf;
+}
+
+
+int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
+		   size_t r0kh_id_len,
+		   const u8 *anonce, const u8 *snonce,
+		   u8 *buf, size_t len, const u8 *subelem,
+		   size_t subelem_len)
+{
+	u8 *pos = buf, *ielen;
+	struct rsn_ftie *hdr;
+
+	if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
+	    subelem_len)
+		return -1;
+
+	*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
+	ielen = pos++;
+
+	hdr = (struct rsn_ftie *) pos;
+	os_memset(hdr, 0, sizeof(*hdr));
+	pos += sizeof(*hdr);
+	WPA_PUT_LE16(hdr->mic_control, 0);
+	if (anonce)
+		os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+	if (snonce)
+		os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+
+	/* Optional Parameters */
+	*pos++ = FTIE_SUBELEM_R1KH_ID;
+	*pos++ = FT_R1KH_ID_LEN;
+	os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN);
+	pos += FT_R1KH_ID_LEN;
+
+	if (r0kh_id) {
+		*pos++ = FTIE_SUBELEM_R0KH_ID;
+		*pos++ = r0kh_id_len;
+		os_memcpy(pos, r0kh_id, r0kh_id_len);
+		pos += r0kh_id_len;
+	}
+
+	if (subelem) {
+		os_memcpy(pos, subelem, subelem_len);
+		pos += subelem_len;
+	}
+
+	*ielen = pos - buf - 2;
+
+	return pos - buf;
+}
+
+
+struct wpa_ft_pmk_r0_sa {
+	struct wpa_ft_pmk_r0_sa *next;
+	u8 pmk_r0[PMK_LEN];
+	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 spa[ETH_ALEN];
+	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+	/* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
+	int pmk_r1_pushed;
+};
+
+struct wpa_ft_pmk_r1_sa {
+	struct wpa_ft_pmk_r1_sa *next;
+	u8 pmk_r1[PMK_LEN];
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+	u8 spa[ETH_ALEN];
+	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+	/* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
+};
+
+struct wpa_ft_pmk_cache {
+	struct wpa_ft_pmk_r0_sa *pmk_r0;
+	struct wpa_ft_pmk_r1_sa *pmk_r1;
+};
+
+struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void)
+{
+	struct wpa_ft_pmk_cache *cache;
+
+	cache = os_zalloc(sizeof(*cache));
+
+	return cache;
+}
+
+
+void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache)
+{
+	struct wpa_ft_pmk_r0_sa *r0, *r0prev;
+	struct wpa_ft_pmk_r1_sa *r1, *r1prev;
+
+	r0 = cache->pmk_r0;
+	while (r0) {
+		r0prev = r0;
+		r0 = r0->next;
+		os_memset(r0prev->pmk_r0, 0, PMK_LEN);
+		os_free(r0prev);
+	}
+
+	r1 = cache->pmk_r1;
+	while (r1) {
+		r1prev = r1;
+		r1 = r1->next;
+		os_memset(r1prev->pmk_r1, 0, PMK_LEN);
+		os_free(r1prev);
+	}
+
+	os_free(cache);
+}
+
+
+static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
+			       const u8 *spa, const u8 *pmk_r0,
+			       const u8 *pmk_r0_name, int pairwise)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct wpa_ft_pmk_r0_sa *r0;
+
+	/* TODO: add expiration and limit on number of entries in cache */
+
+	r0 = os_zalloc(sizeof(*r0));
+	if (r0 == NULL)
+		return -1;
+
+	os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN);
+	os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
+	os_memcpy(r0->spa, spa, ETH_ALEN);
+	r0->pairwise = pairwise;
+
+	r0->next = cache->pmk_r0;
+	cache->pmk_r0 = r0;
+
+	return 0;
+}
+
+
+static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
+			       const u8 *spa, const u8 *pmk_r0_name,
+			       u8 *pmk_r0, int *pairwise)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct wpa_ft_pmk_r0_sa *r0;
+
+	r0 = cache->pmk_r0;
+	while (r0) {
+		if (os_memcmp(r0->spa, spa, ETH_ALEN) == 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;
+			return 0;
+		}
+
+		r0 = r0->next;
+	}
+
+	return -1;
+}
+
+
+static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
+			       const u8 *spa, const u8 *pmk_r1,
+			       const u8 *pmk_r1_name, int pairwise)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct wpa_ft_pmk_r1_sa *r1;
+
+	/* TODO: add expiration and limit on number of entries in cache */
+
+	r1 = os_zalloc(sizeof(*r1));
+	if (r1 == NULL)
+		return -1;
+
+	os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN);
+	os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
+	os_memcpy(r1->spa, spa, ETH_ALEN);
+	r1->pairwise = pairwise;
+
+	r1->next = cache->pmk_r1;
+	cache->pmk_r1 = r1;
+
+	return 0;
+}
+
+
+static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
+			       const u8 *spa, const u8 *pmk_r1_name,
+			       u8 *pmk_r1, int *pairwise)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct wpa_ft_pmk_r1_sa *r1;
+
+	r1 = cache->pmk_r1;
+	while (r1) {
+		if (os_memcmp(r1->spa, spa, ETH_ALEN) == 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;
+			return 0;
+		}
+
+		r1 = r1->next;
+	}
+
+	return -1;
+}
+
+
+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 = sm->wpa_auth->conf.r0kh_list;
+	while (r0kh) {
+		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) {
+		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));
+
+	os_memset(&frame, 0, sizeof(frame));
+	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, 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, 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, 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, sizeof(r0kh->key),
+		     (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
+		     f.nonce, frame.nonce) < 0)
+		return -1;
+
+	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;
+}
+
+
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
+			   struct wpa_ptk *ptk)
+{
+	u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN];
+	u8 ptk_name[WPA_PMK_NAME_LEN];
+	const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
+	const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
+	size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len;
+	const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
+	const u8 *ssid = sm->wpa_auth->conf.ssid;
+	size_t ssid_len = sm->wpa_auth->conf.ssid_len;
+
+	if (sm->xxkey_len == 0) {
+		wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
+			   "derivation");
+		return -1;
+	}
+
+	wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
+			  r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
+	wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
+			    sm->pairwise);
+
+	wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
+			  pmk_r1, sm->pmk_r1_name);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
+		    WPA_PMK_NAME_LEN);
+	wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
+			    sm->pairwise);
+
+	return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+				 sm->wpa_auth->addr, sm->pmk_r1_name,
+				 ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise);
+}
+
+
+static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr, int idx, u8 *seq)
+{
+	if (wpa_auth->cb.get_seqnum == NULL)
+		return -1;
+	return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
+}
+
+
+static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
+{
+	u8 *subelem;
+	struct wpa_group *gsm = sm->group;
+	size_t subelem_len, pad_len;
+	const u8 *key;
+	size_t key_len;
+	u8 keybuf[32];
+
+	key_len = gsm->GTK_len;
+	if (key_len > sizeof(keybuf))
+		return NULL;
+
+	/*
+	 * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less
+	 * than 16 bytes.
+	 */
+	pad_len = key_len % 8;
+	if (pad_len)
+		pad_len = 8 - pad_len;
+	if (key_len + pad_len < 16)
+		pad_len += 8;
+	if (pad_len && key_len < sizeof(keybuf)) {
+		os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
+		os_memset(keybuf + key_len, 0, pad_len);
+		keybuf[key_len] = 0xdd;
+		key_len += pad_len;
+		key = keybuf;
+	} else
+		key = gsm->GTK[gsm->GN - 1];
+
+	/*
+	 * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
+	 * Key[5..32].
+	 */
+	subelem_len = 13 + key_len + 8;
+	subelem = os_zalloc(subelem_len);
+	if (subelem == NULL)
+		return NULL;
+
+	subelem[0] = FTIE_SUBELEM_GTK;
+	subelem[1] = 11 + key_len + 8;
+	/* Key ID in B0-B1 of Key Info */
+	WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
+	subelem[4] = gsm->GTK_len;
+	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
+	if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, key_len / 8, key,
+		     subelem + 13)) {
+		os_free(subelem);
+		return NULL;
+	}
+
+	*len = subelem_len;
+	return subelem;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
+{
+	u8 *subelem, *pos;
+	struct wpa_group *gsm = sm->group;
+	size_t subelem_len;
+
+	/* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] |
+	 * Key[16+8] */
+	subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8;
+	subelem = os_zalloc(subelem_len);
+	if (subelem == NULL)
+		return NULL;
+
+	pos = subelem;
+	*pos++ = FTIE_SUBELEM_IGTK;
+	*pos++ = subelem_len - 2;
+	WPA_PUT_LE16(pos, gsm->GN_igtk);
+	pos += 2;
+	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
+	pos += 6;
+	*pos++ = WPA_IGTK_LEN;
+	if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, WPA_IGTK_LEN / 8,
+		     gsm->IGTK[gsm->GN_igtk - 4], pos)) {
+		os_free(subelem);
+		return NULL;
+	}
+
+	*len = subelem_len;
+	return subelem;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm,
+				u8 *pos, u8 *end, u8 id, u8 descr_count,
+				const u8 *ies, size_t ies_len)
+{
+	struct ieee802_11_elems parse;
+	struct rsn_rdie *rdie;
+
+	wpa_printf(MSG_DEBUG, "FT: Resource Request: id=%d descr_count=%d",
+		   id, descr_count);
+	wpa_hexdump(MSG_MSGDUMP, "FT: Resource descriptor IE(s)",
+		    ies, ies_len);
+
+	if (end - pos < (int) sizeof(*rdie)) {
+		wpa_printf(MSG_ERROR, "FT: Not enough room for response RDIE");
+		return pos;
+	}
+
+	*pos++ = WLAN_EID_RIC_DATA;
+	*pos++ = sizeof(*rdie);
+	rdie = (struct rsn_rdie *) pos;
+	rdie->id = id;
+	rdie->descr_count = 0;
+	rdie->status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+	pos += sizeof(*rdie);
+
+	if (ieee802_11_parse_elems((u8 *) ies, ies_len, &parse, 1) ==
+	    ParseFailed) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to parse request IEs");
+		rdie->status_code =
+			host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+		return pos;
+	}
+
+	if (parse.wmm_tspec) {
+		struct wmm_tspec_element *tspec;
+
+		if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) {
+			wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE "
+				   "(%d)", (int) parse.wmm_tspec_len);
+			rdie->status_code =
+				host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+			return pos;
+		}
+		if (end - pos < (int) sizeof(*tspec)) {
+			wpa_printf(MSG_ERROR, "FT: Not enough room for "
+				   "response TSPEC");
+			rdie->status_code =
+				host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+			return pos;
+		}
+		tspec = (struct wmm_tspec_element *) pos;
+		os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
+	}
+
+#ifdef NEED_AP_MLME
+	if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) {
+		int res;
+
+		res = wmm_process_tspec((struct wmm_tspec_element *) pos);
+		wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res);
+		if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS)
+			rdie->status_code =
+				host_to_le16(WLAN_STATUS_INVALID_PARAMETERS);
+		else if (res == WMM_ADDTS_STATUS_REFUSED)
+			rdie->status_code =
+				host_to_le16(WLAN_STATUS_REQUEST_DECLINED);
+		else {
+			/* TSPEC accepted; include updated TSPEC in response */
+			rdie->descr_count = 1;
+			pos += sizeof(struct wmm_tspec_element);
+		}
+		return pos;
+	}
+#endif /* NEED_AP_MLME */
+
+	if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) {
+		int res;
+
+		res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos,
+				       sizeof(struct wmm_tspec_element));
+		if (res >= 0) {
+			if (res)
+				rdie->status_code = host_to_le16(res);
+			else {
+				/* TSPEC accepted; include updated TSPEC in
+				 * response */
+				rdie->descr_count = 1;
+				pos += sizeof(struct wmm_tspec_element);
+			}
+			return pos;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "FT: No supported resource requested");
+	rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+	return pos;
+}
+
+
+static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end,
+			       const u8 *ric, size_t ric_len)
+{
+	const u8 *rpos, *start;
+	const struct rsn_rdie *rdie;
+
+	wpa_hexdump(MSG_MSGDUMP, "FT: RIC Request", ric, ric_len);
+
+	rpos = ric;
+	while (rpos + sizeof(*rdie) < ric + ric_len) {
+		if (rpos[0] != WLAN_EID_RIC_DATA || rpos[1] < sizeof(*rdie) ||
+		    rpos + 2 + rpos[1] > ric + ric_len)
+			break;
+		rdie = (const struct rsn_rdie *) (rpos + 2);
+		rpos += 2 + rpos[1];
+		start = rpos;
+
+		while (rpos + 2 <= ric + ric_len &&
+		       rpos + 2 + rpos[1] <= ric + ric_len) {
+			if (rpos[0] == WLAN_EID_RIC_DATA)
+				break;
+			rpos += 2 + rpos[1];
+		}
+		pos = wpa_ft_process_rdie(sm, pos, end, rdie->id,
+					  rdie->descr_count,
+					  start, rpos - start);
+	}
+
+	return pos;
+}
+
+
+u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
+				 size_t max_len, int auth_alg,
+				 const u8 *req_ies, size_t req_ies_len)
+{
+	u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
+	size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
+	int res;
+	struct wpa_auth_config *conf;
+	struct rsn_ftie *_ftie;
+	struct wpa_ft_ies parse;
+	u8 *ric_start;
+	u8 *anonce, *snonce;
+
+	if (sm == NULL)
+		return pos;
+
+	conf = &sm->wpa_auth->conf;
+
+	if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+		return pos;
+
+	end = pos + max_len;
+
+	if (auth_alg == WLAN_AUTH_FT) {
+		/*
+		 * RSN (only present if this is a Reassociation Response and
+		 * part of a fast BSS transition)
+		 */
+		res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
+		if (res < 0)
+			return pos;
+		rsnie = pos;
+		rsnie_len = res;
+		pos += res;
+	}
+
+	/* Mobility Domain Information */
+	res = wpa_write_mdie(conf, pos, end - pos);
+	if (res < 0)
+		return pos;
+	mdie = pos;
+	mdie_len = res;
+	pos += res;
+
+	/* Fast BSS Transition Information */
+	if (auth_alg == WLAN_AUTH_FT) {
+		subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
+		r0kh_id = sm->r0kh_id;
+		r0kh_id_len = sm->r0kh_id_len;
+		anonce = sm->ANonce;
+		snonce = sm->SNonce;
+#ifdef CONFIG_IEEE80211W
+		if (sm->mgmt_frame_prot) {
+			u8 *igtk;
+			size_t igtk_len;
+			u8 *nbuf;
+			igtk = wpa_ft_igtk_subelem(sm, &igtk_len);
+			if (igtk == NULL) {
+				os_free(subelem);
+				return pos;
+			}
+			nbuf = os_realloc(subelem, subelem_len + igtk_len);
+			if (nbuf == NULL) {
+				os_free(subelem);
+				os_free(igtk);
+				return pos;
+			}
+			subelem = nbuf;
+			os_memcpy(subelem + subelem_len, igtk, igtk_len);
+			subelem_len += igtk_len;
+			os_free(igtk);
+		}
+#endif /* CONFIG_IEEE80211W */
+	} else {
+		r0kh_id = conf->r0_key_holder;
+		r0kh_id_len = conf->r0_key_holder_len;
+		anonce = NULL;
+		snonce = NULL;
+	}
+	res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos,
+			     end - pos, subelem, subelem_len);
+	os_free(subelem);
+	if (res < 0)
+		return pos;
+	ftie = pos;
+	ftie_len = res;
+	pos += res;
+
+	os_free(sm->assoc_resp_ftie);
+	sm->assoc_resp_ftie = os_malloc(ftie_len);
+	if (sm->assoc_resp_ftie)
+		os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
+
+	_ftie = (struct rsn_ftie *) (ftie + 2);
+	if (auth_alg == WLAN_AUTH_FT)
+		_ftie->mic_control[1] = 3; /* Information element count */
+
+	ric_start = pos;
+	if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) {
+		pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
+					 parse.ric_len);
+		if (auth_alg == WLAN_AUTH_FT)
+			_ftie->mic_control[1] +=
+				ieee802_11_ie_count(ric_start,
+						    pos - ric_start);
+	}
+	if (ric_start == pos)
+		ric_start = NULL;
+
+	if (auth_alg == WLAN_AUTH_FT &&
+	    wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
+		       sm->wpa_auth->addr, 6,
+		       mdie, mdie_len, ftie, ftie_len,
+		       rsnie, rsnie_len,
+		       ric_start, ric_start ? pos - ric_start : 0,
+		       _ftie->mic) < 0)
+		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+
+	return pos;
+}
+
+
+static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
+				   int vlan_id,
+				   enum wpa_alg alg, const u8 *addr, int idx,
+				   u8 *key, size_t key_len)
+{
+	if (wpa_auth->cb.set_key == NULL)
+		return -1;
+	return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
+				    key, key_len);
+}
+
+
+void wpa_ft_install_ptk(struct wpa_state_machine *sm)
+{
+	enum wpa_alg alg;
+	int klen;
+
+	/* MLME-SETKEYS.request(PTK) */
+	alg = wpa_cipher_to_alg(sm->pairwise);
+	klen = wpa_cipher_key_len(sm->pairwise);
+	if (!wpa_cipher_valid_pairwise(sm->pairwise)) {
+		wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip "
+			   "PTK configuration", sm->pairwise);
+		return;
+	}
+
+	/* FIX: add STA entry to kernel/driver here? The set_key will fail
+	 * most likely without this.. At the moment, STA entry is added only
+	 * after association has been completed. This function will be called
+	 * again after association to get the PTK configured, but that could be
+	 * optimized by adding the STA entry earlier.
+	 */
+	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+			     sm->PTK.tk, klen))
+		return;
+
+	/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
+	sm->pairwise_set = TRUE;
+}
+
+
+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)
+{
+	struct rsn_mdie *mdie;
+	struct rsn_ftie *ftie;
+	u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
+	u8 ptk_name[WPA_PMK_NAME_LEN];
+	struct wpa_auth_config *conf;
+	struct wpa_ft_ies parse;
+	size_t buflen;
+	int ret;
+	u8 *pos, *end;
+	int pairwise;
+
+	*resp_ies = NULL;
+	*resp_ies_len = 0;
+
+	sm->pmk_r1_name_valid = 0;
+	conf = &sm->wpa_auth->conf;
+
+	wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
+		    ies, ies_len);
+
+	if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	mdie = (struct rsn_mdie *) parse.mdie;
+	if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+	    os_memcmp(mdie->mobility_domain,
+		      sm->wpa_auth->conf.mobility_domain,
+		      MOBILITY_DOMAIN_ID_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+		return WLAN_STATUS_INVALID_MDIE;
+	}
+
+	ftie = (struct rsn_ftie *) parse.ftie;
+	if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+
+	if (parse.r0kh_id == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID",
+		    parse.r0kh_id, parse.r0kh_id_len);
+	os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
+	sm->r0kh_id_len = parse.r0kh_id_len;
+
+	if (parse.rsn_pmkid == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
+		return WLAN_STATUS_INVALID_PMKID;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
+		    parse.rsn_pmkid, WPA_PMK_NAME_LEN);
+	wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+			       sm->wpa_auth->conf.r1_key_holder, sm->addr,
+			       pmk_r1_name);
+	wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name",
+		    pmk_r1_name, WPA_PMK_NAME_LEN);
+
+	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, 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;
+		}
+
+		return -1; /* Status pending */
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN);
+	sm->pmk_r1_name_valid = 1;
+	os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
+
+	if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
+			   "ANonce");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+		    sm->SNonce, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
+		    sm->ANonce, WPA_NONCE_LEN);
+
+	if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+			      sm->wpa_auth->addr, pmk_r1_name,
+			      &sm->PTK, ptk_name, sm->wpa_key_mgmt,
+			      pairwise) < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	sm->pairwise = pairwise;
+	sm->PTK_valid = TRUE;
+	wpa_ft_install_ptk(sm);
+
+	buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
+		2 + FT_R1KH_ID_LEN + 200;
+	*resp_ies = os_zalloc(buflen);
+	if (*resp_ies == NULL) {
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	pos = *resp_ies;
+	end = *resp_ies + buflen;
+
+	ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid);
+	if (ret < 0) {
+		os_free(*resp_ies);
+		*resp_ies = NULL;
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	pos += ret;
+
+	ret = wpa_write_mdie(conf, pos, end - pos);
+	if (ret < 0) {
+		os_free(*resp_ies);
+		*resp_ies = NULL;
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	pos += ret;
+
+	ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len,
+			     sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0);
+	if (ret < 0) {
+		os_free(*resp_ies);
+		*resp_ies = NULL;
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	pos += ret;
+
+	*resp_ies_len = pos - *resp_ies;
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
+			 u16 auth_transaction, const u8 *ies, size_t ies_len,
+			 void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
+				    u16 auth_transaction, u16 status,
+				    const u8 *ies, size_t ies_len),
+			 void *ctx)
+{
+	u16 status;
+	u8 *resp_ies;
+	size_t resp_ies_len;
+	int res;
+
+	if (sm == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but "
+			   "WPA SM not available");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR
+		   " BSSID=" MACSTR " transaction=%d",
+		   MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction);
+	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",
+		   MAC2STR(sm->addr), auth_transaction + 1, status);
+	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
+	cb(ctx, sm->addr, bssid, auth_transaction + 1, status,
+	   resp_ies, resp_ies_len);
+	os_free(resp_ies);
+}
+
+
+u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
+			    size_t ies_len)
+{
+	struct wpa_ft_ies parse;
+	struct rsn_mdie *mdie;
+	struct rsn_ftie *ftie;
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	size_t mic_len = 16;
+	unsigned int count;
+
+	if (sm == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
+
+	if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (parse.rsn == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (parse.rsn_pmkid == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
+		return WLAN_STATUS_INVALID_PMKID;
+	}
+
+	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;
+	}
+
+	mdie = (struct rsn_mdie *) parse.mdie;
+	if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+	    os_memcmp(mdie->mobility_domain,
+		      sm->wpa_auth->conf.mobility_domain,
+		      MOBILITY_DOMAIN_ID_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+		return WLAN_STATUS_INVALID_MDIE;
+	}
+
+	ftie = (struct rsn_ftie *) parse.ftie;
+	if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
+		wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+			    ftie->snonce, WPA_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+			    sm->SNonce, WPA_NONCE_LEN);
+		return -1;
+	}
+
+	if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
+		wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
+			    ftie->anonce, WPA_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
+			    sm->ANonce, WPA_NONCE_LEN);
+		return -1;
+	}
+
+
+	if (parse.r0kh_id == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+		return -1;
+	}
+
+	if (parse.r0kh_id_len != sm->r0kh_id_len ||
+	    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",
+			    parse.r0kh_id, parse.r0kh_id_len);
+		wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+			    sm->r0kh_id, sm->r0kh_id_len);
+		return -1;
+	}
+
+	if (parse.r1kh_id == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+		return -1;
+	}
+
+	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",
+			    parse.r1kh_id, FT_R1KH_ID_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
+			    sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
+		return -1;
+	}
+
+	if (parse.rsn_pmkid == NULL ||
+	    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;
+	}
+
+	count = 3;
+	if (parse.ric)
+		count += ieee802_11_ie_count(parse.ric, parse.ric_len);
+	if (ftie->mic_control[1] != count) {
+		wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
+			   "Control: received %u expected %u",
+			   ftie->mic_control[1], count);
+		return -1;
+	}
+
+	if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
+		       sm->wpa_auth->addr, 5,
+		       parse.mdie - 2, parse.mdie_len + 2,
+		       parse.ftie - 2, parse.ftie_len + 2,
+		       parse.rsn - 2, parse.rsn_len + 2,
+		       parse.ric, parse.ric_len,
+		       mic) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (os_memcmp_const(mic, ftie->mic, mic_len) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
+		wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
+			   MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
+		wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+			    ftie->mic, mic_len);
+		wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
+		wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
+			    parse.mdie - 2, parse.mdie_len + 2);
+		wpa_hexdump(MSG_MSGDUMP, "FT: FTIE",
+			    parse.ftie - 2, parse.ftie_len + 2);
+		wpa_hexdump(MSG_MSGDUMP, "FT: RSN",
+			    parse.rsn - 2, parse.rsn_len + 2);
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len)
+{
+	const u8 *sta_addr, *target_ap;
+	const u8 *ies;
+	size_t ies_len;
+	u8 action;
+	struct ft_rrb_frame *frame;
+
+	if (sm == NULL)
+		return -1;
+
+	/*
+	 * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
+	 * FT Request action frame body[variable]
+	 */
+
+	if (len < 14) {
+		wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame "
+			   "(len=%lu)", (unsigned long) len);
+		return -1;
+	}
+
+	action = data[1];
+	sta_addr = data + 2;
+	target_ap = data + 8;
+	ies = data + 14;
+	ies_len = len - 14;
+
+	wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR
+		   " Target AP=" MACSTR " Action=%d)",
+		   MAC2STR(sta_addr), MAC2STR(target_ap), action);
+
+	if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: "
+			   "STA=" MACSTR " STA-Address=" MACSTR,
+			   MAC2STR(sm->addr), MAC2STR(sta_addr));
+		return -1;
+	}
+
+	/*
+	 * Do some sanity checking on the target AP address (not own and not
+	 * broadcast. This could be extended to filter based on a list of known
+	 * APs in the MD (if such a list were configured).
+	 */
+	if ((target_ap[0] & 0x01) ||
+	    os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action "
+			   "frame");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len);
+
+	/* RRB - Forward action frame to the target AP */
+	frame = os_malloc(sizeof(*frame) + len);
+	if (frame == NULL)
+		return -1;
+	frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+	frame->packet_type = FT_PACKET_REQUEST;
+	frame->action_length = host_to_le16(len);
+	os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN);
+	os_memcpy(frame + 1, data, len);
+
+	wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame,
+			sizeof(*frame) + len);
+	os_free(frame);
+
+	return 0;
+}
+
+
+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;
+	size_t resp_ies_len;
+	int res;
+
+	sm = wpa_ft_add_sta(wpa_auth, sta_addr);
+	if (sm == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on "
+			   "RRB Request");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, 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",
+		   MAC2STR(sm->addr), MAC2STR(current_ap), status);
+	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
+
+	/* RRB - Forward action frame response to the Current AP */
+
+	/*
+	 * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
+	 * Status_Code[2] FT Request action frame body[variable]
+	 */
+	rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len;
+
+	frame = os_malloc(sizeof(*frame) + rlen);
+	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);
+	os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN);
+	pos = (u8 *) (frame + 1);
+	*pos++ = WLAN_ACTION_FT;
+	*pos++ = 2; /* Action: Response */
+	os_memcpy(pos, sta_addr, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, wpa_auth->addr, ETH_ALEN);
+	pos += ETH_ALEN;
+	WPA_PUT_LE16(pos, status);
+	pos += 2;
+	if (resp_ies)
+		os_memcpy(pos, resp_ies, resp_ies_len);
+
+	wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame,
+			sizeof(*frame) + rlen);
+	os_free(frame);
+
+	return 0;
+}
+
+
+static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
+			      const u8 *src_addr,
+			      const u8 *data, size_t data_len)
+{
+	struct ft_r0kh_r1kh_pull_frame f;
+	const u8 *crypt;
+	u8 *plain;
+	struct ft_remote_r1kh *r1kh;
+	struct ft_r0kh_r1kh_resp_frame resp, r;
+	u8 pmk_r0[PMK_LEN];
+	int pairwise;
+
+	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
+
+	if (data_len < sizeof(f))
+		return -1;
+
+	r1kh = wpa_auth->conf.r1kh_list;
+	while (r1kh) {
+		if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0)
+			break;
+		r1kh = r1kh->next;
+	}
+	if (r1kh == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for "
+			   "PMK-R1 pull source address " MACSTR,
+			   MAC2STR(src_addr));
+		return -1;
+	}
+
+	crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
+	os_memset(&f, 0, sizeof(f));
+	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
+	/* aes_unwrap() does not support inplace decryption, so use a temporary
+	 * buffer for the data. */
+	if (aes_unwrap(r1kh->key, sizeof(r1kh->key),
+		       (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
+		       crypt, plain) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
+			   "request from " MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
+		    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="
+		   MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
+
+	os_memset(&resp, 0, sizeof(resp));
+	resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+	resp.packet_type = FT_PACKET_R0KH_R1KH_RESP;
+	resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN);
+	os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN);
+
+	/* aes_wrap() does not support inplace encryption, so use a temporary
+	 * buffer for the data. */
+	os_memcpy(r.nonce, f.nonce, sizeof(f.nonce));
+	os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN);
+	os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN);
+	if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0,
+				&pairwise) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for "
+			   "PMK-R1 pull");
+		return -1;
+	}
+
+	wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id,
+			  r.pmk_r1, r.pmk_r1_name);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name,
+		    WPA_PMK_NAME_LEN);
+	r.pairwise = host_to_le16(pairwise);
+	os_memset(r.pad, 0, sizeof(r.pad));
+
+	if (aes_wrap(r1kh->key, sizeof(r1kh->key),
+		     (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
+		     r.nonce, resp.nonce) < 0) {
+		os_memset(pmk_r0, 0, PMK_LEN);
+		return -1;
+	}
+
+	os_memset(pmk_r0, 0, PMK_LEN);
+
+	wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp));
+
+	return 0;
+}
+
+
+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 f;
+	const u8 *crypt;
+	u8 *plain;
+	struct ft_remote_r0kh *r0kh;
+	int pairwise, res;
+
+	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
+
+	if (data_len < sizeof(f))
+		return -1;
+
+	r0kh = wpa_auth->conf.r0kh_list;
+	while (r0kh) {
+		if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
+			break;
+		r0kh = r0kh->next;
+	}
+	if (r0kh == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
+			   "PMK-R0 pull response source address " MACSTR,
+			   MAC2STR(src_addr));
+		return -1;
+	}
+
+	crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
+	os_memset(&f, 0, sizeof(f));
+	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
+	/* aes_unwrap() does not support inplace decryption, so use a temporary
+	 * buffer for the data. */
+	if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
+		       (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
+		       crypt, plain) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
+			   "response from " MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+
+	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;
+	}
+
+	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="
+		   MACSTR " pairwise=0x%x",
+		   MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1",
+			f.pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
+			f.pmk_r1_name, WPA_PMK_NAME_LEN);
+
+	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 res ? 0 : -1;
+}
+
+
+static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
+			      const u8 *src_addr,
+			      const u8 *data, size_t data_len)
+{
+	struct ft_r0kh_r1kh_push_frame f;
+	const u8 *crypt;
+	u8 *plain;
+	struct ft_remote_r0kh *r0kh;
+	struct os_time now;
+	os_time_t tsend;
+	int pairwise;
+
+	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
+
+	if (data_len < sizeof(f))
+		return -1;
+
+	r0kh = wpa_auth->conf.r0kh_list;
+	while (r0kh) {
+		if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
+			break;
+		r0kh = r0kh->next;
+	}
+	if (r0kh == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
+			   "PMK-R0 push source address " MACSTR,
+			   MAC2STR(src_addr));
+		return -1;
+	}
+
+	crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp);
+	os_memset(&f, 0, sizeof(f));
+	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
+				       timestamp);
+	/* aes_unwrap() does not support inplace decryption, so use a temporary
+	 * buffer for the data. */
+	if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
+		       (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
+		       crypt, plain) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from "
+			   MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+
+	os_get_time(&now);
+	tsend = WPA_GET_LE32(f.timestamp);
+	if ((now.sec > tsend && now.sec - tsend > 60) ||
+	    (now.sec < tsend && tsend - now.sec > 60)) {
+		wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid "
+			   "timestamp: sender time %d own time %d\n",
+			   (int) tsend, (int) now.sec);
+		return -1;
+	}
+
+	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),
+			   MAC2STR(wpa_auth->conf.r1_key_holder));
+		return -1;
+	}
+
+	pairwise = le_to_host16(f.pairwise);
+	wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - 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 push - PMK-R1",
+			f.pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - 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);
+	os_memset(f.pmk_r1, 0, PMK_LEN);
+
+	return 0;
+}
+
+
+int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+		  const u8 *data, size_t data_len)
+{
+	struct ft_rrb_frame *frame;
+	u16 alen;
+	const u8 *pos, *end, *start;
+	u8 action;
+	const u8 *sta_addr, *target_ap_addr;
+
+	wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR,
+		   MAC2STR(src_addr));
+
+	if (data_len < sizeof(*frame)) {
+		wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)",
+			   (unsigned long) data_len);
+		return -1;
+	}
+
+	pos = data;
+	frame = (struct ft_rrb_frame *) pos;
+	pos += sizeof(*frame);
+
+	alen = le_to_host16(frame->action_length);
+	wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d "
+		   "action_length=%d ap_address=" MACSTR,
+		   frame->frame_type, frame->packet_type, alen,
+		   MAC2STR(frame->ap_address));
+
+	if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) {
+		/* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */
+		wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with "
+			   "unrecognized type %d", frame->frame_type);
+		return -1;
+	}
+
+	if (alen > data_len - sizeof(*frame)) {
+		wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action "
+			   "frame");
+		return -1;
+	}
+
+	if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL)
+		return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len);
+	if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP)
+		return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len);
+	if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH)
+		return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len);
+
+	wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen);
+
+	if (alen < 1 + 1 + 2 * ETH_ALEN) {
+		wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough "
+			   "room for Action Frame body); alen=%lu",
+			   (unsigned long) alen);
+		return -1;
+	}
+	start = pos;
+	end = pos + alen;
+
+	if (*pos != WLAN_ACTION_FT) {
+		wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category "
+			   "%d", *pos);
+		return -1;
+	}
+
+	pos++;
+	action = *pos++;
+	sta_addr = pos;
+	pos += ETH_ALEN;
+	target_ap_addr = pos;
+	pos += ETH_ALEN;
+	wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr="
+		   MACSTR " target_ap_addr=" MACSTR,
+		   action, MAC2STR(sta_addr), MAC2STR(target_ap_addr));
+
+	if (frame->packet_type == FT_PACKET_REQUEST) {
+		wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request");
+
+		if (action != 1) {
+			wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in "
+				   "RRB Request", action);
+			return -1;
+		}
+
+		if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) {
+			wpa_printf(MSG_DEBUG, "FT: Target AP address in the "
+				   "RRB Request does not match with own "
+				   "address");
+			return -1;
+		}
+
+		if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address,
+					  sta_addr, pos, end - pos) < 0)
+			return -1;
+	} else if (frame->packet_type == FT_PACKET_RESPONSE) {
+		u16 status_code;
+
+		if (end - pos < 2) {
+			wpa_printf(MSG_DEBUG, "FT: Not enough room for status "
+				   "code in RRB Response");
+			return -1;
+		}
+		status_code = WPA_GET_LE16(pos);
+		pos += 2;
+
+		wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response "
+			   "(status_code=%d)", status_code);
+
+		if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0)
+			return -1;
+	} else {
+		wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown "
+			   "packet_type %d", frame->packet_type);
+		return -1;
+	}
+
+	if (end > pos) {
+		wpa_hexdump(MSG_DEBUG, "FT: Ignore extra data in end",
+			    pos, end - pos);
+	}
+
+	return 0;
+}
+
+
+static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
+				   struct wpa_ft_pmk_r0_sa *pmk_r0,
+				   struct ft_remote_r1kh *r1kh,
+				   const u8 *s1kh_id, int pairwise)
+{
+	struct ft_r0kh_r1kh_push_frame frame, f;
+	struct os_time now;
+	const u8 *plain;
+	u8 *crypt;
+
+	os_memset(&frame, 0, sizeof(frame));
+	frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+	frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH;
+	frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN);
+	os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN);
+
+	/* aes_wrap() does not support inplace encryption, so use a temporary
+	 * buffer for the data. */
+	os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN);
+	os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
+	os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN);
+	wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id,
+			  s1kh_id, f.pmk_r1, f.pmk_r1_name);
+	wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id));
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name,
+		    WPA_PMK_NAME_LEN);
+	os_get_time(&now);
+	WPA_PUT_LE32(f.timestamp, now.sec);
+	f.pairwise = host_to_le16(pairwise);
+	os_memset(f.pad, 0, sizeof(f.pad));
+	plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
+					     timestamp);
+	crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame,
+					   timestamp);
+	if (aes_wrap(r1kh->key, sizeof(r1kh->key),
+		     (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
+		     plain, crypt) < 0)
+		return;
+
+	wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame));
+}
+
+
+void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+	struct wpa_ft_pmk_r0_sa *r0;
+	struct ft_remote_r1kh *r1kh;
+
+	if (!wpa_auth->conf.pmk_r1_push)
+		return;
+
+	r0 = wpa_auth->ft_pmk_cache->pmk_r0;
+	while (r0) {
+		if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0)
+			break;
+		r0 = r0->next;
+	}
+
+	if (r0 == NULL || r0->pmk_r1_pushed)
+		return;
+	r0->pmk_r1_pushed = 1;
+
+	wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
+		   "for STA " MACSTR, MAC2STR(addr));
+
+	r1kh = wpa_auth->conf.r1kh_list;
+	while (r1kh) {
+		wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise);
+		r1kh = r1kh->next;
+	}
+}
+
+#endif /* CONFIG_IEEE80211R */
diff --git a/hostap/src/ap/wpa_auth_glue.c b/hostap/src/ap/wpa_auth_glue.c
new file mode 100644
index 0000000..f98cc50
--- /dev/null
+++ b/hostap/src/ap/wpa_auth_glue.c
@@ -0,0 +1,695 @@
+/*
+ * hostapd / WPA authenticator glue code
+ * Copyright (c) 2002-2012, 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 "common/ieee802_11_defs.h"
+#include "common/sae.h"
+#include "common/wpa_ctrl.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "eap_server/eap.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "preauth_auth.h"
+#include "sta_info.h"
+#include "tkip_countermeasures.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "wpa_auth.h"
+#include "wpa_auth_glue.h"
+
+
+static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
+				  struct hostapd_config *iconf,
+				  struct wpa_auth_config *wconf)
+{
+	os_memset(wconf, 0, sizeof(*wconf));
+	wconf->wpa = conf->wpa;
+	wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
+	wconf->wpa_pairwise = conf->wpa_pairwise;
+	wconf->wpa_group = conf->wpa_group;
+	wconf->wpa_group_rekey = conf->wpa_group_rekey;
+	wconf->wpa_strict_rekey = conf->wpa_strict_rekey;
+	wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey;
+	wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey;
+	wconf->rsn_pairwise = conf->rsn_pairwise;
+	wconf->rsn_preauth = conf->rsn_preauth;
+	wconf->eapol_version = conf->eapol_version;
+	wconf->peerkey = conf->peerkey;
+	wconf->wmm_enabled = conf->wmm_enabled;
+	wconf->wmm_uapsd = conf->wmm_uapsd;
+	wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
+	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;
+	if (wconf->ssid_len > SSID_MAX_LEN)
+		wconf->ssid_len = SSID_MAX_LEN;
+	os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len);
+	os_memcpy(wconf->mobility_domain, conf->mobility_domain,
+		  MOBILITY_DOMAIN_ID_LEN);
+	if (conf->nas_identifier &&
+	    os_strlen(conf->nas_identifier) <= FT_R0KH_ID_MAX_LEN) {
+		wconf->r0_key_holder_len = os_strlen(conf->nas_identifier);
+		os_memcpy(wconf->r0_key_holder, conf->nas_identifier,
+			  wconf->r0_key_holder_len);
+	}
+	os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
+	wconf->r0_key_lifetime = conf->r0_key_lifetime;
+	wconf->reassociation_deadline = conf->reassociation_deadline;
+	wconf->r0kh_list = conf->r0kh_list;
+	wconf->r1kh_list = conf->r1kh_list;
+	wconf->pmk_r1_push = conf->pmk_r1_push;
+	wconf->ft_over_ds = conf->ft_over_ds;
+#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;
+	if (conf->own_ie_override &&
+	    wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) {
+		wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override);
+		os_memcpy(wconf->own_ie_override,
+			  wpabuf_head(conf->own_ie_override),
+			  wconf->own_ie_override_len);
+	}
+#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 */
+}
+
+
+static void hostapd_wpa_auth_logger(void *ctx, const u8 *addr,
+				    logger_level level, const char *txt)
+{
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+	struct hostapd_data *hapd = ctx;
+	int hlevel;
+
+	switch (level) {
+	case LOGGER_WARNING:
+		hlevel = HOSTAPD_LEVEL_WARNING;
+		break;
+	case LOGGER_INFO:
+		hlevel = HOSTAPD_LEVEL_INFO;
+		break;
+	case LOGGER_DEBUG:
+	default:
+		hlevel = HOSTAPD_LEVEL_DEBUG;
+		break;
+	}
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_WPA, hlevel, "%s", txt);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+}
+
+
+static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr,
+					u16 reason)
+{
+	struct hostapd_data *hapd = ctx;
+	wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: "
+		   "STA " MACSTR " reason %d",
+		   __func__, MAC2STR(addr), reason);
+	ap_sta_disconnect(hapd, NULL, addr, reason);
+}
+
+
+static int hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr)
+{
+	struct hostapd_data *hapd = ctx;
+	return michael_mic_failure(hapd, addr, 0);
+}
+
+
+static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr)
+{
+	struct hostapd_data *hapd = ctx;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR,
+		MAC2STR(addr));
+}
+
+
+static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr,
+				       wpa_eapol_variable var, int value)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	if (sta == NULL)
+		return;
+	switch (var) {
+	case WPA_EAPOL_portEnabled:
+		ieee802_1x_notify_port_enabled(sta->eapol_sm, value);
+		break;
+	case WPA_EAPOL_portValid:
+		ieee802_1x_notify_port_valid(sta->eapol_sm, value);
+		break;
+	case WPA_EAPOL_authorized:
+		ieee802_1x_set_sta_authorized(hapd, sta, value);
+		break;
+	case WPA_EAPOL_portControl_Auto:
+		if (sta->eapol_sm)
+			sta->eapol_sm->portControl = Auto;
+		break;
+	case WPA_EAPOL_keyRun:
+		if (sta->eapol_sm)
+			sta->eapol_sm->keyRun = value ? TRUE : FALSE;
+		break;
+	case WPA_EAPOL_keyAvailable:
+		if (sta->eapol_sm)
+			sta->eapol_sm->eap_if->eapKeyAvailable =
+				value ? TRUE : FALSE;
+		break;
+	case WPA_EAPOL_keyDone:
+		if (sta->eapol_sm)
+			sta->eapol_sm->keyDone = value ? TRUE : FALSE;
+		break;
+	case WPA_EAPOL_inc_EapolFramesTx:
+		if (sta->eapol_sm)
+			sta->eapol_sm->dot1xAuthEapolFramesTx++;
+		break;
+	}
+}
+
+
+static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr,
+				      wpa_eapol_variable var)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	if (sta == NULL || sta->eapol_sm == NULL)
+		return -1;
+	switch (var) {
+	case WPA_EAPOL_keyRun:
+		return sta->eapol_sm->keyRun;
+	case WPA_EAPOL_keyAvailable:
+		return sta->eapol_sm->eap_if->eapKeyAvailable;
+	default:
+		return -1;
+	}
+}
+
+
+static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
+					   const u8 *p2p_dev_addr,
+					   const u8 *prev_psk)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	const u8 *psk;
+
+#ifdef CONFIG_SAE
+	if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
+		if (!sta->sae || prev_psk)
+			return NULL;
+		return sta->sae->pmk;
+	}
+#endif /* CONFIG_SAE */
+
+	psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk);
+	/*
+	 * This is about to iterate over all psks, prev_psk gives the last
+	 * returned psk which should not be returned again.
+	 * logic list (all hostapd_get_psk; all sta->psk)
+	 */
+	if (sta && sta->psk && !psk) {
+		struct hostapd_sta_wpa_psk_short *pos;
+		psk = sta->psk->psk;
+		for (pos = sta->psk; pos; pos = pos->next) {
+			if (pos->psk == prev_psk) {
+				psk = pos->next ? pos->next->psk : NULL;
+				break;
+			}
+		}
+	}
+	return psk;
+}
+
+
+static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk,
+				    size_t *len)
+{
+	struct hostapd_data *hapd = ctx;
+	const u8 *key;
+	size_t keylen;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Cannot find STA");
+		return -1;
+	}
+
+	key = ieee802_1x_get_key(sta->eapol_sm, &keylen);
+	if (key == NULL) {
+		wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Key is null, eapol_sm: %p",
+			   sta->eapol_sm);
+		return -1;
+	}
+
+	if (keylen > *len)
+		keylen = *len;
+	os_memcpy(msk, key, keylen);
+	*len = keylen;
+
+	return 0;
+}
+
+
+static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
+				    const u8 *addr, int idx, u8 *key,
+				    size_t key_len)
+{
+	struct hostapd_data *hapd = ctx;
+	const char *ifname = hapd->conf->iface;
+
+	if (vlan_id > 0) {
+		ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id);
+		if (ifname == NULL)
+			return -1;
+	}
+
+	return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
+				   key, key_len);
+}
+
+
+static int hostapd_wpa_auth_get_seqnum(void *ctx, const u8 *addr, int idx,
+				       u8 *seq)
+{
+	struct hostapd_data *hapd = ctx;
+	return hostapd_get_seqnum(hapd->conf->iface, hapd, addr, idx, seq);
+}
+
+
+static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr,
+				       const u8 *data, size_t data_len,
+				       int encrypt)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	u32 flags = 0;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->ext_eapol_frame_io) {
+		size_t hex_len = 2 * data_len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex == NULL)
+			return -1;
+		wpa_snprintf_hex(hex, hex_len, data, data_len);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+			MAC2STR(addr), hex);
+		os_free(hex);
+		return 0;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		flags = hostapd_sta_flags_to_drv(sta->flags);
+
+	return hostapd_drv_hapd_send_eapol(hapd, addr, data, data_len,
+					   encrypt, flags);
+}
+
+
+static int hostapd_wpa_auth_for_each_sta(
+	void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx),
+	void *cb_ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (sta->wpa_sm && cb(sta->wpa_sm, cb_ctx))
+			return 1;
+	}
+	return 0;
+}
+
+
+struct wpa_auth_iface_iter_data {
+	int (*cb)(struct wpa_authenticator *sm, void *ctx);
+	void *cb_ctx;
+};
+
+static int wpa_auth_iface_iter(struct hostapd_iface *iface, void *ctx)
+{
+	struct wpa_auth_iface_iter_data *data = ctx;
+	size_t i;
+	for (i = 0; i < iface->num_bss; i++) {
+		if (iface->bss[i]->wpa_auth &&
+		    data->cb(iface->bss[i]->wpa_auth, data->cb_ctx))
+			return 1;
+	}
+	return 0;
+}
+
+
+static int hostapd_wpa_auth_for_each_auth(
+	void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx),
+	void *cb_ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	struct wpa_auth_iface_iter_data data;
+	if (hapd->iface->interfaces == NULL ||
+	    hapd->iface->interfaces->for_each_interface == NULL)
+		return -1;
+	data.cb = cb;
+	data.cb_ctx = cb_ctx;
+	return hapd->iface->interfaces->for_each_interface(
+		hapd->iface->interfaces, wpa_auth_iface_iter, &data);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+struct wpa_auth_ft_iface_iter_data {
+	struct hostapd_data *src_hapd;
+	const u8 *dst;
+	const u8 *data;
+	size_t data_len;
+};
+
+
+static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx)
+{
+	struct wpa_auth_ft_iface_iter_data *idata = ctx;
+	struct hostapd_data *hapd;
+	size_t j;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		hapd = iface->bss[j];
+		if (hapd == idata->src_hapd)
+			continue;
+		if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) {
+			wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to "
+				   "locally managed BSS " MACSTR "@%s -> "
+				   MACSTR "@%s",
+				   MAC2STR(idata->src_hapd->own_addr),
+				   idata->src_hapd->conf->iface,
+				   MAC2STR(hapd->own_addr), hapd->conf->iface);
+			wpa_ft_rrb_rx(hapd->wpa_auth,
+				      idata->src_hapd->own_addr,
+				      idata->data, idata->data_len);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
+				       const u8 *data, size_t data_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct l2_ethhdr *buf;
+	int ret;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->ext_eapol_frame_io && proto == ETH_P_EAPOL) {
+		size_t hex_len = 2 * data_len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex == NULL)
+			return -1;
+		wpa_snprintf_hex(hex, hex_len, data, data_len);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+			MAC2STR(dst), hex);
+		os_free(hex);
+		return 0;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_IEEE80211R
+	if (proto == ETH_P_RRB && hapd->iface->interfaces &&
+	    hapd->iface->interfaces->for_each_interface) {
+		int res;
+		struct wpa_auth_ft_iface_iter_data idata;
+		idata.src_hapd = hapd;
+		idata.dst = dst;
+		idata.data = data;
+		idata.data_len = data_len;
+		res = hapd->iface->interfaces->for_each_interface(
+			hapd->iface->interfaces, hostapd_wpa_auth_ft_iter,
+			&idata);
+		if (res == 1)
+			return data_len;
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	if (hapd->driver && hapd->driver->send_ether)
+		return hapd->driver->send_ether(hapd->drv_priv, dst,
+						hapd->own_addr, proto,
+						data, data_len);
+	if (hapd->l2 == NULL)
+		return -1;
+
+	buf = os_malloc(sizeof(*buf) + data_len);
+	if (buf == NULL)
+		return -1;
+	os_memcpy(buf->h_dest, dst, ETH_ALEN);
+	os_memcpy(buf->h_source, hapd->own_addr, ETH_ALEN);
+	buf->h_proto = host_to_be16(proto);
+	os_memcpy(buf + 1, data, data_len);
+	ret = l2_packet_send(hapd->l2, dst, proto, (u8 *) buf,
+			     sizeof(*buf) + data_len);
+	os_free(buf);
+	return ret;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
+					   const u8 *data, size_t data_len)
+{
+	struct hostapd_data *hapd = ctx;
+	int res;
+	struct ieee80211_mgmt *m;
+	size_t mlen;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, dst);
+	if (sta == NULL || sta->wpa_sm == NULL)
+		return -1;
+
+	m = os_zalloc(sizeof(*m) + data_len);
+	if (m == NULL)
+		return -1;
+	mlen = ((u8 *) &m->u - (u8 *) m) + data_len;
+	m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					WLAN_FC_STYPE_ACTION);
+	os_memcpy(m->da, dst, ETH_ALEN);
+	os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
+	os_memcpy(&m->u, data, data_len);
+
+	res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen, 0);
+	os_free(m);
+	return res;
+}
+
+
+static struct wpa_state_machine *
+hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0)
+		return NULL;
+
+	sta = ap_sta_add(hapd, sta_addr);
+	if (sta == NULL)
+		return NULL;
+	if (sta->wpa_sm) {
+		sta->auth_alg = WLAN_AUTH_FT;
+		return sta->wpa_sm;
+	}
+
+	sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL);
+	if (sta->wpa_sm == NULL) {
+		ap_free_sta(hapd, sta);
+		return NULL;
+	}
+	sta->auth_alg = WLAN_AUTH_FT;
+
+	return sta->wpa_sm;
+}
+
+
+static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+				size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct l2_ethhdr *ethhdr;
+	if (len < sizeof(*ethhdr))
+		return;
+	ethhdr = (struct l2_ethhdr *) buf;
+	wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
+		   MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest));
+	wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr),
+		      len - sizeof(*ethhdr));
+}
+
+
+static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
+				      u8 *tspec_ie, size_t tspec_ielen)
+{
+	struct hostapd_data *hapd = ctx;
+	return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+int hostapd_setup_wpa(struct hostapd_data *hapd)
+{
+	struct wpa_auth_config _conf;
+	struct wpa_auth_callbacks cb;
+	const u8 *wpa_ie;
+	size_t wpa_ie_len;
+
+	hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
+	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
+		_conf.tx_status = 1;
+	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
+		_conf.ap_mlme = 1;
+	os_memset(&cb, 0, sizeof(cb));
+	cb.ctx = hapd;
+	cb.logger = hostapd_wpa_auth_logger;
+	cb.disconnect = hostapd_wpa_auth_disconnect;
+	cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report;
+	cb.psk_failure_report = hostapd_wpa_auth_psk_failure_report;
+	cb.set_eapol = hostapd_wpa_auth_set_eapol;
+	cb.get_eapol = hostapd_wpa_auth_get_eapol;
+	cb.get_psk = hostapd_wpa_auth_get_psk;
+	cb.get_msk = hostapd_wpa_auth_get_msk;
+	cb.set_key = hostapd_wpa_auth_set_key;
+	cb.get_seqnum = hostapd_wpa_auth_get_seqnum;
+	cb.send_eapol = hostapd_wpa_auth_send_eapol;
+	cb.for_each_sta = hostapd_wpa_auth_for_each_sta;
+	cb.for_each_auth = hostapd_wpa_auth_for_each_auth;
+	cb.send_ether = hostapd_wpa_auth_send_ether;
+#ifdef CONFIG_IEEE80211R
+	cb.send_ft_action = hostapd_wpa_auth_send_ft_action;
+	cb.add_sta = hostapd_wpa_auth_add_sta;
+	cb.add_tspec = hostapd_wpa_auth_add_tspec;
+#endif /* CONFIG_IEEE80211R */
+	hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb);
+	if (hapd->wpa_auth == NULL) {
+		wpa_printf(MSG_ERROR, "WPA initialization failed.");
+		return -1;
+	}
+
+	if (hostapd_set_privacy(hapd, 1)) {
+		wpa_printf(MSG_ERROR, "Could not set PrivacyInvoked "
+			   "for interface %s", hapd->conf->iface);
+		return -1;
+	}
+
+	wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len);
+	if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) {
+		wpa_printf(MSG_ERROR, "Failed to configure WPA IE for "
+			   "the kernel driver.");
+		return -1;
+	}
+
+	if (rsn_preauth_iface_init(hapd)) {
+		wpa_printf(MSG_ERROR, "Initialization of RSN "
+			   "pre-authentication failed.");
+		return -1;
+	}
+
+#ifdef CONFIG_IEEE80211R
+	if (!hostapd_drv_none(hapd) && hapd->conf->ft_over_ds &&
+	    wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
+		hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
+					  hapd->conf->bridge :
+					  hapd->conf->iface, NULL, ETH_P_RRB,
+					  hostapd_rrb_receive, hapd, 1);
+		if (hapd->l2 == NULL &&
+		    (hapd->driver == NULL ||
+		     hapd->driver->send_ether == NULL)) {
+			wpa_printf(MSG_ERROR, "Failed to open l2_packet "
+				   "interface");
+			return -1;
+		}
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	return 0;
+
+}
+
+
+void hostapd_reconfig_wpa(struct hostapd_data *hapd)
+{
+	struct wpa_auth_config wpa_auth_conf;
+	hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &wpa_auth_conf);
+	wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf);
+}
+
+
+void hostapd_deinit_wpa(struct hostapd_data *hapd)
+{
+	ieee80211_tkip_countermeasures_deinit(hapd);
+	rsn_preauth_iface_deinit(hapd);
+	if (hapd->wpa_auth) {
+		wpa_deinit(hapd->wpa_auth);
+		hapd->wpa_auth = NULL;
+
+		if (hostapd_set_privacy(hapd, 0)) {
+			wpa_printf(MSG_DEBUG, "Could not disable "
+				   "PrivacyInvoked for interface %s",
+				   hapd->conf->iface);
+		}
+
+		if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) {
+			wpa_printf(MSG_DEBUG, "Could not remove generic "
+				   "information element from interface %s",
+				   hapd->conf->iface);
+		}
+	}
+	ieee802_1x_deinit(hapd);
+
+#ifdef CONFIG_IEEE80211R
+	l2_packet_deinit(hapd->l2);
+	hapd->l2 = NULL;
+#endif /* CONFIG_IEEE80211R */
+}
diff --git a/hostap/src/ap/wpa_auth_glue.h b/hostap/src/ap/wpa_auth_glue.h
new file mode 100644
index 0000000..1b13ae7
--- /dev/null
+++ b/hostap/src/ap/wpa_auth_glue.h
@@ -0,0 +1,16 @@
+/*
+ * hostapd / WPA authenticator glue code
+ * 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 WPA_AUTH_GLUE_H
+#define WPA_AUTH_GLUE_H
+
+int hostapd_setup_wpa(struct hostapd_data *hapd);
+void hostapd_reconfig_wpa(struct hostapd_data *hapd);
+void hostapd_deinit_wpa(struct hostapd_data *hapd);
+
+#endif /* WPA_AUTH_GLUE_H */
diff --git a/hostap/src/ap/wpa_auth_i.h b/hostap/src/ap/wpa_auth_i.h
new file mode 100644
index 0000000..57b098f
--- /dev/null
+++ b/hostap/src/ap/wpa_auth_i.h
@@ -0,0 +1,259 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_I_H
+#define WPA_AUTH_I_H
+
+/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
+#define RSNA_MAX_EAPOL_RETRIES 4
+
+struct wpa_group;
+
+struct wpa_stsl_negotiation {
+	struct wpa_stsl_negotiation *next;
+	u8 initiator[ETH_ALEN];
+	u8 peer[ETH_ALEN];
+};
+
+
+struct wpa_state_machine {
+	struct wpa_authenticator *wpa_auth;
+	struct wpa_group *group;
+
+	u8 addr[ETH_ALEN];
+	u8 p2p_dev_addr[ETH_ALEN];
+
+	enum {
+		WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED,
+		WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2,
+		WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART,
+		WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2,
+		WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE
+	} wpa_ptk_state;
+
+	enum {
+		WPA_PTK_GROUP_IDLE = 0,
+		WPA_PTK_GROUP_REKEYNEGOTIATING,
+		WPA_PTK_GROUP_REKEYESTABLISHED,
+		WPA_PTK_GROUP_KEYERROR
+	} wpa_ptk_group_state;
+
+	Boolean Init;
+	Boolean DeauthenticationRequest;
+	Boolean AuthenticationRequest;
+	Boolean ReAuthenticationRequest;
+	Boolean Disconnect;
+	int TimeoutCtr;
+	int GTimeoutCtr;
+	Boolean TimeoutEvt;
+	Boolean EAPOLKeyReceived;
+	Boolean EAPOLKeyPairwise;
+	Boolean EAPOLKeyRequest;
+	Boolean MICVerified;
+	Boolean GUpdateStationKeys;
+	u8 ANonce[WPA_NONCE_LEN];
+	u8 SNonce[WPA_NONCE_LEN];
+	u8 alt_SNonce[WPA_NONCE_LEN];
+	u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
+	u8 PMK[PMK_LEN];
+	struct wpa_ptk PTK;
+	Boolean PTK_valid;
+	Boolean pairwise_set;
+	int keycount;
+	Boolean Pair;
+	struct wpa_key_replay_counter {
+		u8 counter[WPA_REPLAY_COUNTER_LEN];
+		Boolean valid;
+	} key_replay[RSNA_MAX_EAPOL_RETRIES],
+		prev_key_replay[RSNA_MAX_EAPOL_RETRIES];
+	Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */
+	Boolean PTKRequest; /* not in IEEE 802.11i state machine */
+	Boolean has_GTK;
+	Boolean PtkGroupInit; /* init request for PTK Group state machine */
+
+	u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */
+	size_t last_rx_eapol_key_len;
+
+	unsigned int changed:1;
+	unsigned int in_step_loop:1;
+	unsigned int pending_deinit:1;
+	unsigned int started:1;
+	unsigned int mgmt_frame_prot:1;
+	unsigned int rx_eapol_key_secure:1;
+	unsigned int update_snonce:1;
+	unsigned int alt_snonce_valid:1;
+#ifdef CONFIG_IEEE80211R
+	unsigned int ft_completed:1;
+	unsigned int pmk_r1_name_valid:1;
+#endif /* CONFIG_IEEE80211R */
+	unsigned int is_wnmsleep:1;
+
+	u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
+	int req_replay_counter_used;
+
+	u8 *wpa_ie;
+	size_t wpa_ie_len;
+
+	enum {
+		WPA_VERSION_NO_WPA = 0 /* WPA not used */,
+		WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */,
+		WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */
+	} wpa;
+	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+	int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */
+	struct rsn_pmksa_cache_entry *pmksa;
+
+	u32 dot11RSNAStatsTKIPLocalMICFailures;
+	u32 dot11RSNAStatsTKIPRemoteMICFailures;
+
+#ifdef CONFIG_IEEE80211R
+	u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
+	size_t xxkey_len;
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
+					   * Request */
+	u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
+	size_t r0kh_id_len;
+	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 */
+};
+
+
+/* per group key state machine data */
+struct wpa_group {
+	struct wpa_group *next;
+	int vlan_id;
+
+	Boolean GInit;
+	int GKeyDoneStations;
+	Boolean GTKReKey;
+	int GTK_len;
+	int GN, GM;
+	Boolean GTKAuthenticator;
+	u8 Counter[WPA_NONCE_LEN];
+
+	enum {
+		WPA_GROUP_GTK_INIT = 0,
+		WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE,
+		WPA_GROUP_FATAL_FAILURE
+	} wpa_group_state;
+
+	u8 GMK[WPA_GMK_LEN];
+	u8 GTK[2][WPA_GTK_MAX_LEN];
+	u8 GNonce[WPA_NONCE_LEN];
+	Boolean changed;
+	Boolean first_sta_seen;
+	Boolean reject_4way_hs_for_entropy;
+#ifdef CONFIG_IEEE80211W
+	u8 IGTK[2][WPA_IGTK_MAX_LEN];
+	int GN_igtk, GM_igtk;
+#endif /* CONFIG_IEEE80211W */
+	/* Number of references except those in struct wpa_group->next */
+	unsigned int references;
+};
+
+
+struct wpa_ft_pmk_cache;
+
+/* per authenticator data */
+struct wpa_authenticator {
+	struct wpa_group *group;
+
+	unsigned int dot11RSNAStatsTKIPRemoteMICFailures;
+	u32 dot11RSNAAuthenticationSuiteSelected;
+	u32 dot11RSNAPairwiseCipherSelected;
+	u32 dot11RSNAGroupCipherSelected;
+	u8 dot11RSNAPMKIDUsed[PMKID_LEN];
+	u32 dot11RSNAAuthenticationSuiteRequested; /* FIX: update */
+	u32 dot11RSNAPairwiseCipherRequested; /* FIX: update */
+	u32 dot11RSNAGroupCipherRequested; /* FIX: update */
+	unsigned int dot11RSNATKIPCounterMeasuresInvoked;
+	unsigned int dot11RSNA4WayHandshakeFailures;
+
+	struct wpa_stsl_negotiation *stsl_negotiations;
+
+	struct wpa_auth_config conf;
+	struct wpa_auth_callbacks cb;
+
+	u8 *wpa_ie;
+	size_t wpa_ie_len;
+
+	u8 addr[ETH_ALEN];
+
+	struct rsn_pmksa_cache *pmksa;
+	struct wpa_ft_pmk_cache *ft_pmk_cache;
+
+#ifdef CONFIG_P2P
+	struct bitfield *ip_pool;
+#endif /* CONFIG_P2P */
+};
+
+
+int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
+		     const u8 *pmkid);
+void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		     logger_level level, const char *txt);
+void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		      logger_level level, const char *fmt, ...);
+void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
+		      struct wpa_state_machine *sm, int key_info,
+		      const u8 *key_rsc, const u8 *nonce,
+		      const u8 *kde, size_t kde_len,
+		      int keyidx, int encr, int force_version);
+int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
+			  int (*cb)(struct wpa_state_machine *sm, void *ctx),
+			  void *cb_ctx);
+int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
+			   int (*cb)(struct wpa_authenticator *a, void *ctx),
+			   void *cb_ctx);
+
+#ifdef CONFIG_PEERKEY
+int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
+		    struct wpa_stsl_negotiation *neg);
+void wpa_smk_error(struct wpa_authenticator *wpa_auth,
+		   struct wpa_state_machine *sm,
+		   const u8 *key_data, size_t key_data_len);
+void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
+		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+		const u8 *key_data, size_t key_data_len);
+void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
+		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+		const u8 *key_data, size_t key_data_len);
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211R
+int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
+int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
+		   size_t r0kh_id_len,
+		   const u8 *anonce, const u8 *snonce,
+		   u8 *buf, size_t len, const u8 *subelem,
+		   size_t subelem_len);
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
+			   struct wpa_ptk *ptk);
+struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
+void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
+void wpa_ft_install_ptk(struct wpa_state_machine *sm);
+#endif /* CONFIG_IEEE80211R */
+
+#endif /* WPA_AUTH_I_H */
diff --git a/hostap/src/ap/wpa_auth_ie.c b/hostap/src/ap/wpa_auth_ie.c
new file mode 100644
index 0000000..eafb828
--- /dev/null
+++ b/hostap/src/ap/wpa_auth_ie.c
@@ -0,0 +1,937 @@
+/*
+ * hostapd - WPA/RSN IE and KDE definitions
+ * Copyright (c) 2004-2015, 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 "common/ieee802_11_defs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
+#include "wpa_auth_ie.h"
+#include "wpa_auth_i.h"
+
+
+#ifdef CONFIG_RSN_TESTING
+int rsn_testing = 0;
+#endif /* CONFIG_RSN_TESTING */
+
+
+static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+	struct wpa_ie_hdr *hdr;
+	int num_suites;
+	u8 *pos, *count;
+	u32 suite;
+
+	hdr = (struct wpa_ie_hdr *) buf;
+	hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
+	RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
+	WPA_PUT_LE16(hdr->version, WPA_VERSION);
+	pos = (u8 *) (hdr + 1);
+
+	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, conf->wpa_group);
+	if (suite == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
+			   conf->wpa_group);
+		return -1;
+	}
+	RSN_SELECTOR_PUT(pos, suite);
+	pos += WPA_SELECTOR_LEN;
+
+	count = pos;
+	pos += 2;
+
+	num_suites = wpa_cipher_put_suites(pos, conf->wpa_pairwise);
+	if (num_suites == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
+			   conf->wpa_pairwise);
+		return -1;
+	}
+	pos += num_suites * WPA_SELECTOR_LEN;
+	WPA_PUT_LE16(count, num_suites);
+
+	num_suites = 0;
+	count = pos;
+	pos += 2;
+
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+
+	if (num_suites == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
+			   conf->wpa_key_mgmt);
+		return -1;
+	}
+	WPA_PUT_LE16(count, num_suites);
+
+	/* WPA Capabilities; use defaults, so no need to include it */
+
+	hdr->len = (pos - buf) - 2;
+
+	return pos - buf;
+}
+
+
+int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
+		     const u8 *pmkid)
+{
+	struct rsn_ie_hdr *hdr;
+	int num_suites, res;
+	u8 *pos, *count;
+	u16 capab;
+	u32 suite;
+
+	hdr = (struct rsn_ie_hdr *) buf;
+	hdr->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(hdr->version, RSN_VERSION);
+	pos = (u8 *) (hdr + 1);
+
+	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group);
+	if (suite == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
+			   conf->wpa_group);
+		return -1;
+	}
+	RSN_SELECTOR_PUT(pos, suite);
+	pos += RSN_SELECTOR_LEN;
+
+	num_suites = 0;
+	count = pos;
+	pos += 2;
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1));
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	res = rsn_cipher_put_suites(pos, conf->rsn_pairwise);
+	num_suites += res;
+	pos += res * RSN_SELECTOR_LEN;
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2));
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	if (num_suites == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
+			   conf->rsn_pairwise);
+		return -1;
+	}
+	WPA_PUT_LE16(count, num_suites);
+
+	num_suites = 0;
+	count = pos;
+	pos += 2;
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1));
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#ifdef CONFIG_IEEE80211R
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_SAE */
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2));
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	if (num_suites == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
+			   conf->wpa_key_mgmt);
+		return -1;
+	}
+	WPA_PUT_LE16(count, num_suites);
+
+	/* RSN Capabilities */
+	capab = 0;
+	if (conf->rsn_preauth)
+		capab |= WPA_CAPABILITY_PREAUTH;
+	if (conf->peerkey)
+		capab |= WPA_CAPABILITY_PEERKEY_ENABLED;
+	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 */
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing)
+		capab |= BIT(8) | BIT(14) | BIT(15);
+#endif /* CONFIG_RSN_TESTING */
+	WPA_PUT_LE16(pos, capab);
+	pos += 2;
+
+	if (pmkid) {
+		if (pos + 2 + PMKID_LEN > buf + len)
+			return -1;
+		/* PMKID Count */
+		WPA_PUT_LE16(pos, 1);
+		pos += 2;
+		os_memcpy(pos, pmkid, PMKID_LEN);
+		pos += PMKID_LEN;
+	}
+
+#ifdef CONFIG_IEEE80211W
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+	    conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
+		if (pos + 2 + 4 > buf + len)
+			return -1;
+		if (pmkid == NULL) {
+			/* PMKID Count */
+			WPA_PUT_LE16(pos, 0);
+			pos += 2;
+		}
+
+		/* Management Group Cipher Suite */
+		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 */
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		/*
+		 * Fill in any defined fields and add extra data to the end of
+		 * the element.
+		 */
+		int pmkid_count_set = pmkid != NULL;
+		if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION)
+			pmkid_count_set = 1;
+		/* PMKID Count */
+		WPA_PUT_LE16(pos, 0);
+		pos += 2;
+		if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+			/* Management Group Cipher Suite */
+			RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+			pos += RSN_SELECTOR_LEN;
+		}
+
+		os_memset(pos, 0x12, 17);
+		pos += 17;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	hdr->len = (pos - buf) - 2;
+
+	return pos - buf;
+}
+
+
+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];
+	int res;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_auth->conf.own_ie_override_len) {
+		wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing",
+			    wpa_auth->conf.own_ie_override,
+			    wpa_auth->conf.own_ie_override_len);
+		os_free(wpa_auth->wpa_ie);
+		wpa_auth->wpa_ie =
+			os_malloc(wpa_auth->conf.own_ie_override_len);
+		if (wpa_auth->wpa_ie == NULL)
+			return -1;
+		os_memcpy(wpa_auth->wpa_ie, wpa_auth->conf.own_ie_override,
+			  wpa_auth->conf.own_ie_override_len);
+		wpa_auth->wpa_ie_len = wpa_auth->conf.own_ie_override_len;
+		return 0;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	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);
+		if (res < 0)
+			return res;
+		pos += res;
+	}
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
+		res = wpa_write_mdie(&wpa_auth->conf, pos,
+				     buf + sizeof(buf) - pos);
+		if (res < 0)
+			return res;
+		pos += res;
+	}
+#endif /* CONFIG_IEEE80211R */
+	if (wpa_auth->conf.wpa & WPA_PROTO_WPA) {
+		res = wpa_write_wpa_ie(&wpa_auth->conf,
+				       pos, buf + sizeof(buf) - pos);
+		if (res < 0)
+			return res;
+		pos += res;
+	}
+
+	os_free(wpa_auth->wpa_ie);
+	wpa_auth->wpa_ie = os_malloc(pos - buf);
+	if (wpa_auth->wpa_ie == NULL)
+		return -1;
+	os_memcpy(wpa_auth->wpa_ie, buf, pos - buf);
+	wpa_auth->wpa_ie_len = pos - buf;
+
+	return 0;
+}
+
+
+u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
+		 const u8 *data2, size_t data2_len)
+{
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = RSN_SELECTOR_LEN + data_len + data2_len;
+	RSN_SELECTOR_PUT(pos, kde);
+	pos += RSN_SELECTOR_LEN;
+	os_memcpy(pos, data, data_len);
+	pos += data_len;
+	if (data2) {
+		os_memcpy(pos, data2, data2_len);
+		pos += data2_len;
+	}
+	return pos;
+}
+
+
+struct wpa_auth_okc_iter_data {
+	struct rsn_pmksa_cache_entry *pmksa;
+	const u8 *aa;
+	const u8 *spa;
+	const u8 *pmkid;
+};
+
+
+static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx)
+{
+	struct wpa_auth_okc_iter_data *data = ctx;
+	data->pmksa = pmksa_cache_get_okc(a->pmksa, data->aa, data->spa,
+					  data->pmkid);
+	if (data->pmksa)
+		return 1;
+	return 0;
+}
+
+
+int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+			struct wpa_state_machine *sm,
+			const u8 *wpa_ie, size_t wpa_ie_len,
+			const u8 *mdie, size_t mdie_len)
+{
+	struct wpa_ie_data data;
+	int ciphers, key_mgmt, res, version;
+	u32 selector;
+	size_t i;
+	const u8 *pmkid = NULL;
+
+	if (wpa_auth == NULL || sm == NULL)
+		return WPA_NOT_ENABLED;
+
+	if (wpa_ie == NULL || wpa_ie_len < 1)
+		return WPA_INVALID_IE;
+
+	if (wpa_ie[0] == WLAN_EID_RSN)
+		version = WPA_PROTO_RSN;
+	else
+		version = WPA_PROTO_WPA;
+
+	if (!(wpa_auth->conf.wpa & version)) {
+		wpa_printf(MSG_DEBUG, "Invalid WPA proto (%d) from " MACSTR,
+			   version, MAC2STR(sm->addr));
+		return WPA_INVALID_PROTO;
+	}
+
+	if (version == WPA_PROTO_RSN) {
+		res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data);
+
+		selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+		if (0) {
+		}
+		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+			selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+			selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
+#ifdef CONFIG_IEEE80211R
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+			selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK)
+			selector = RSN_AUTH_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+			selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+		else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+			selector = RSN_AUTH_KEY_MGMT_PSK_SHA256;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+		else if (data.key_mgmt & WPA_KEY_MGMT_SAE)
+			selector = RSN_AUTH_KEY_MGMT_SAE;
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE)
+			selector = RSN_AUTH_KEY_MGMT_FT_SAE;
+#endif /* CONFIG_SAE */
+		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+			selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+		else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
+			selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+		wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
+
+		selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
+					       data.pairwise_cipher);
+		if (!selector)
+			selector = RSN_CIPHER_SUITE_CCMP;
+		wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
+
+		selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
+					       data.group_cipher);
+		if (!selector)
+			selector = RSN_CIPHER_SUITE_CCMP;
+		wpa_auth->dot11RSNAGroupCipherSelected = selector;
+	} else {
+		res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data);
+
+		selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
+		if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+			selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
+		else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
+			selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+		wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
+
+		selector = wpa_cipher_to_suite(WPA_PROTO_WPA,
+					       data.pairwise_cipher);
+		if (!selector)
+			selector = RSN_CIPHER_SUITE_TKIP;
+		wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
+
+		selector = wpa_cipher_to_suite(WPA_PROTO_WPA,
+					       data.group_cipher);
+		if (!selector)
+			selector = WPA_CIPHER_SUITE_TKIP;
+		wpa_auth->dot11RSNAGroupCipherSelected = selector;
+	}
+	if (res) {
+		wpa_printf(MSG_DEBUG, "Failed to parse WPA/RSN IE from "
+			   MACSTR " (res=%d)", MAC2STR(sm->addr), res);
+		wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len);
+		return WPA_INVALID_IE;
+	}
+
+	if (data.group_cipher != wpa_auth->conf.wpa_group) {
+		wpa_printf(MSG_DEBUG, "Invalid WPA group cipher (0x%x) from "
+			   MACSTR, data.group_cipher, MAC2STR(sm->addr));
+		return WPA_INVALID_GROUP;
+	}
+
+	key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt;
+	if (!key_mgmt) {
+		wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from "
+			   MACSTR, data.key_mgmt, MAC2STR(sm->addr));
+		return WPA_INVALID_AKMP;
+	}
+	if (0) {
+	}
+	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+#ifdef CONFIG_IEEE80211R
+	else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+	else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
+	else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+	else if (key_mgmt & WPA_KEY_MGMT_SAE)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE;
+	else if (key_mgmt & WPA_KEY_MGMT_FT_SAE)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE;
+#endif /* CONFIG_SAE */
+	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+	else
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+
+	if (version == WPA_PROTO_RSN)
+		ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise;
+	else
+		ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise;
+	if (!ciphers) {
+		wpa_printf(MSG_DEBUG, "Invalid %s pairwise cipher (0x%x) "
+			   "from " MACSTR,
+			   version == WPA_PROTO_RSN ? "RSN" : "WPA",
+			   data.pairwise_cipher, MAC2STR(sm->addr));
+		return WPA_INVALID_PAIRWISE;
+	}
+
+#ifdef CONFIG_IEEE80211W
+	if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) {
+		if (!(data.capabilities & WPA_CAPABILITY_MFPC)) {
+			wpa_printf(MSG_DEBUG, "Management frame protection "
+				   "required, but client did not enable it");
+			return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+		}
+
+		if (ciphers & WPA_CIPHER_TKIP) {
+			wpa_printf(MSG_DEBUG, "Management frame protection "
+				   "cannot use TKIP");
+			return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+		}
+
+		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;
+		}
+	}
+
+	if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
+	    !(data.capabilities & WPA_CAPABILITY_MFPC))
+		sm->mgmt_frame_prot = 0;
+	else
+		sm->mgmt_frame_prot = 1;
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
+			wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but "
+				   "MDIE not included");
+			return WPA_INVALID_MDIE;
+		}
+		if (os_memcmp(mdie, wpa_auth->conf.mobility_domain,
+			      MOBILITY_DOMAIN_ID_LEN) != 0) {
+			wpa_hexdump(MSG_DEBUG, "RSN: Attempted to use unknown "
+				    "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN);
+			return WPA_INVALID_MDIE;
+		}
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
+	if (sm->pairwise < 0)
+		return WPA_INVALID_PAIRWISE;
+
+	/* TODO: clear WPA/WPA2 state if STA changes from one to another */
+	if (wpa_ie[0] == WLAN_EID_RSN)
+		sm->wpa = WPA_VERSION_WPA2;
+	else
+		sm->wpa = WPA_VERSION_WPA;
+
+	sm->pmksa = NULL;
+	for (i = 0; i < data.num_pmkid; i++) {
+		wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
+			    &data.pmkid[i * PMKID_LEN], PMKID_LEN);
+		sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr,
+						 &data.pmkid[i * PMKID_LEN]);
+		if (sm->pmksa) {
+			pmkid = sm->pmksa->pmkid;
+			break;
+		}
+	}
+	for (i = 0; sm->pmksa == NULL && wpa_auth->conf.okc &&
+		     i < data.num_pmkid; i++) {
+		struct wpa_auth_okc_iter_data idata;
+		idata.pmksa = NULL;
+		idata.aa = wpa_auth->addr;
+		idata.spa = sm->addr;
+		idata.pmkid = &data.pmkid[i * PMKID_LEN];
+		wpa_auth_for_each_auth(wpa_auth, wpa_auth_okc_iter, &idata);
+		if (idata.pmksa) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "OKC match for PMKID");
+			sm->pmksa = pmksa_cache_add_okc(wpa_auth->pmksa,
+							idata.pmksa,
+							wpa_auth->addr,
+							idata.pmkid);
+			pmkid = idata.pmkid;
+			break;
+		}
+	}
+	if (sm->pmksa && pmkid) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				 "PMKID found from PMKSA cache "
+				 "eap_type=%d vlan_id=%d",
+				 sm->pmksa->eap_type_authsrv,
+				 sm->pmksa->vlan_id);
+		os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
+	}
+
+	if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
+		os_free(sm->wpa_ie);
+		sm->wpa_ie = os_malloc(wpa_ie_len);
+		if (sm->wpa_ie == NULL)
+			return WPA_ALLOC_FAIL;
+	}
+	os_memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len);
+	sm->wpa_ie_len = wpa_ie_len;
+
+	return WPA_IE_OK;
+}
+
+
+#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
+ * @end: Pointer to the end of the Key Data buffer
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, 1 if end mark is found, -1 on failure
+ */
+static int wpa_parse_generic(const u8 *pos, const u8 *end,
+			     struct wpa_eapol_ie_parse *ie)
+{
+	if (pos[1] == 0)
+		return 1;
+
+	if (pos[1] >= 6 &&
+	    RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
+	    pos[2 + WPA_SELECTOR_LEN] == 1 &&
+	    pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
+		ie->wpa_ie = pos;
+		ie->wpa_ie_len = pos[1] + 2;
+		return 0;
+	}
+
+	if (pos[1] >= 4 && WPA_GET_BE32(pos + 2) == OSEN_IE_VENDOR_TYPE) {
+		ie->osen = pos;
+		ie->osen_len = pos[1] + 2;
+		return 0;
+	}
+
+	if (pos + 1 + RSN_SELECTOR_LEN < end &&
+	    pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
+		ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
+		ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
+		ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
+		ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
+		ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
+		return 0;
+	}
+
+#ifdef CONFIG_PEERKEY
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
+		ie->smk = pos + 2 + RSN_SELECTOR_LEN;
+		ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
+		ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
+		ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
+		ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
+		ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
+		ie->error = pos + 2 + RSN_SELECTOR_LEN;
+		ie->error_len = pos[1] - RSN_SELECTOR_LEN;
+		return 0;
+	}
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211W
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
+		ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
+		ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
+		return 0;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_P2P
+	if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
+		ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
+			    ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
+		return 0;
+	}
+
+	if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
+		ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG,
+			    "WPA: IP Address Allocation in EAPOL-Key",
+			    ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
+		return 0;
+	}
+#endif /* CONFIG_P2P */
+
+	return 0;
+}
+
+
+/**
+ * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs
+ * @buf: Pointer to the Key Data buffer
+ * @len: Key Data Length
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
+{
+	const u8 *pos, *end;
+	int ret = 0;
+
+	os_memset(ie, 0, sizeof(*ie));
+	for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
+		if (pos[0] == 0xdd &&
+		    ((pos == buf + len - 1) || pos[1] == 0)) {
+			/* Ignore padding */
+			break;
+		}
+		if (pos + 2 + pos[1] > end) {
+			wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
+				   "underflow (ie=%d len=%d pos=%d)",
+				   pos[0], pos[1], (int) (pos - buf));
+			wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data",
+					buf, len);
+			ret = -1;
+			break;
+		}
+		if (*pos == WLAN_EID_RSN) {
+			ie->rsn_ie = pos;
+			ie->rsn_ie_len = pos[1] + 2;
+#ifdef CONFIG_IEEE80211R
+		} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
+			ie->mdie = pos;
+			ie->mdie_len = pos[1] + 2;
+		} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
+			ie->ftie = pos;
+			ie->ftie_len = pos[1] + 2;
+#endif /* CONFIG_IEEE80211R */
+		} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
+			ret = wpa_parse_generic(pos, end, ie);
+			if (ret < 0)
+				break;
+			if (ret > 0) {
+				ret = 0;
+				break;
+			}
+		} else {
+			wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key "
+				    "Key Data IE", pos, 2 + pos[1]);
+		}
+	}
+
+	return ret;
+}
+
+
+int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
+{
+	return sm ? sm->mgmt_frame_prot : 0;
+}
diff --git a/hostap/src/ap/wpa_auth_ie.h b/hostap/src/ap/wpa_auth_ie.h
new file mode 100644
index 0000000..d2067ba
--- /dev/null
+++ b/hostap/src/ap/wpa_auth_ie.h
@@ -0,0 +1,57 @@
+/*
+ * hostapd - WPA/RSN IE and KDE definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_IE_H
+#define WPA_AUTH_IE_H
+
+struct wpa_eapol_ie_parse {
+	const u8 *wpa_ie;
+	size_t wpa_ie_len;
+	const u8 *rsn_ie;
+	size_t rsn_ie_len;
+	const u8 *pmkid;
+	const u8 *gtk;
+	size_t gtk_len;
+	const u8 *mac_addr;
+	size_t mac_addr_len;
+#ifdef CONFIG_PEERKEY
+	const u8 *smk;
+	size_t smk_len;
+	const u8 *nonce;
+	size_t nonce_len;
+	const u8 *lifetime;
+	size_t lifetime_len;
+	const u8 *error;
+	size_t error_len;
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_IEEE80211W
+	const u8 *igtk;
+	size_t igtk_len;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
+	const u8 *mdie;
+	size_t mdie_len;
+	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,
+		      struct wpa_eapol_ie_parse *ie);
+u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
+		 const u8 *data2, size_t data2_len);
+int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth);
+
+#endif /* WPA_AUTH_IE_H */
diff --git a/hostap/src/ap/wps_hostapd.c b/hostap/src/ap/wps_hostapd.c
new file mode 100644
index 0000000..cde31e6
--- /dev/null
+++ b/hostap/src/ap/wps_hostapd.c
@@ -0,0 +1,2014 @@
+/*
+ * hostapd / WPS integration
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
+#include "common/wpa_ctrl.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
+#include "wps/wps_dev_attr.h"
+#include "wps/wps_attr_parse.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+#include "sta_info.h"
+#include "wps_hostapd.h"
+
+
+#ifdef CONFIG_WPS_UPNP
+#include "wps/wps_upnp.h"
+static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
+				 struct wps_context *wps);
+static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd);
+#endif /* CONFIG_WPS_UPNP */
+
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
+				    const u8 *bssid,
+				    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 {
+	int (*func)(struct hostapd_data *h, void *ctx);
+	void *ctx;
+	struct hostapd_data *calling_hapd;
+};
+
+
+static int wps_for_each(struct hostapd_iface *iface, void *ctx)
+{
+	struct wps_for_each_data *data = ctx;
+	size_t j;
+
+	if (iface == NULL)
+		return 0;
+	for (j = 0; j < iface->num_bss; j++) {
+		struct hostapd_data *hapd = iface->bss[j];
+		int ret;
+
+		if (hapd != data->calling_hapd &&
+		    (hapd->conf->wps_independent ||
+		     data->calling_hapd->conf->wps_independent))
+			continue;
+
+		ret = data->func(hapd, data->ctx);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+
+static int hostapd_wps_for_each(struct hostapd_data *hapd,
+				int (*func)(struct hostapd_data *h, void *ctx),
+				void *ctx)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct wps_for_each_data data;
+	data.func = func;
+	data.ctx = ctx;
+	data.calling_hapd = hapd;
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->for_each_interface == NULL)
+		return wps_for_each(iface, &data);
+	return iface->interfaces->for_each_interface(iface->interfaces,
+						     wps_for_each, &data);
+}
+
+
+static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr,
+				  const u8 *p2p_dev_addr, const u8 *psk,
+				  size_t psk_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct hostapd_wpa_psk *p;
+	struct hostapd_ssid *ssid = &hapd->conf->ssid;
+
+	if (is_zero_ether_addr(p2p_dev_addr)) {
+		wpa_printf(MSG_DEBUG,
+			   "Received new WPA/WPA2-PSK from WPS for STA " MACSTR,
+			   MAC2STR(mac_addr));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "Received new WPA/WPA2-PSK from WPS for STA " MACSTR
+			   " P2P Device Addr " MACSTR,
+			   MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
+	}
+	wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
+
+	if (psk_len != PMK_LEN) {
+		wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu",
+			   (unsigned long) psk_len);
+		return -1;
+	}
+
+	/* Add the new PSK to runtime PSK list */
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL)
+		return -1;
+	os_memcpy(p->addr, mac_addr, ETH_ALEN);
+	os_memcpy(p->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
+	os_memcpy(p->psk, psk, PMK_LEN);
+
+	if (hapd->new_psk_cb) {
+		hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr,
+				 psk, psk_len);
+	}
+
+	p->next = ssid->wpa_psk;
+	ssid->wpa_psk = p;
+
+	if (ssid->wpa_psk_file) {
+		FILE *f;
+		char hex[PMK_LEN * 2 + 1];
+		/* Add the new PSK to PSK list file */
+		f = fopen(ssid->wpa_psk_file, "a");
+		if (f == NULL) {
+			wpa_printf(MSG_DEBUG, "Failed to add the PSK to "
+				   "'%s'", ssid->wpa_psk_file);
+			return -1;
+		}
+
+		wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len);
+		fprintf(f, MACSTR " %s\n", MAC2STR(mac_addr), hex);
+		fclose(f);
+	}
+
+	return 0;
+}
+
+
+static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie,
+				 struct wpabuf *probe_resp_ie)
+{
+	struct hostapd_data *hapd = ctx;
+	wpabuf_free(hapd->wps_beacon_ie);
+	hapd->wps_beacon_ie = beacon_ie;
+	wpabuf_free(hapd->wps_probe_resp_ie);
+	hapd->wps_probe_resp_ie = probe_resp_ie;
+	if (hapd->beacon_set_done)
+		ieee802_11_set_beacon(hapd);
+	return hostapd_set_ap_wps_ie(hapd);
+}
+
+
+static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
+				      const struct wps_device_data *dev)
+{
+	struct hostapd_data *hapd = ctx;
+	char uuid[40], txt[400];
+	int len;
+	char devtype[WPS_DEV_TYPE_BUFSIZE];
+	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+		return;
+	wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid);
+	len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED
+			  "%s " MACSTR " [%s|%s|%s|%s|%s|%s]",
+			  uuid, MAC2STR(dev->mac_addr), dev->device_name,
+			  dev->manufacturer, dev->model_name,
+			  dev->model_number, dev->serial_number,
+			  wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+					       sizeof(devtype)));
+	if (!os_snprintf_error(sizeof(txt), len))
+		wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt);
+
+	if (hapd->conf->wps_pin_requests) {
+		FILE *f;
+		struct os_time t;
+		f = fopen(hapd->conf->wps_pin_requests, "a");
+		if (f == NULL)
+			return;
+		os_get_time(&t);
+		fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s"
+			"\t%s\n",
+			t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name,
+			dev->manufacturer, dev->model_name, dev->model_number,
+			dev->serial_number,
+			wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+					     sizeof(devtype)));
+		fclose(f);
+	}
+}
+
+
+struct wps_stop_reg_data {
+	struct hostapd_data *current_hapd;
+	const u8 *uuid_e;
+	const u8 *dev_pw;
+	size_t dev_pw_len;
+};
+
+static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_stop_reg_data *data = ctx;
+	if (hapd != data->current_hapd && hapd->wps != NULL)
+		wps_registrar_complete(hapd->wps->registrar, data->uuid_e,
+				       data->dev_pw, data->dev_pw_len);
+	return 0;
+}
+
+
+static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
+				       const u8 *uuid_e, const u8 *dev_pw,
+				       size_t dev_pw_len)
+{
+	struct hostapd_data *hapd = ctx;
+	char uuid[40];
+	struct wps_stop_reg_data data;
+	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+		return;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s",
+		MAC2STR(mac_addr), uuid);
+	if (hapd->wps_reg_success_cb)
+		hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx,
+					 mac_addr, uuid_e);
+	data.current_hapd = hapd;
+	data.uuid_e = uuid_e;
+	data.dev_pw = dev_pw;
+	data.dev_pw_len = dev_pw_len;
+	hostapd_wps_for_each(hapd, wps_stop_registrar, &data);
+}
+
+
+static void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr,
+					 const u8 *uuid_e,
+					 const u8 *pri_dev_type,
+					 u16 config_methods,
+					 u16 dev_password_id, u8 request_type,
+					 const char *dev_name)
+{
+	struct hostapd_data *hapd = ctx;
+	char uuid[40];
+	char devtype[WPS_DEV_TYPE_BUFSIZE];
+	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+		return;
+	if (dev_name == NULL)
+		dev_name = "";
+	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ENROLLEE_SEEN MACSTR
+		     " %s %s 0x%x %u %u [%s]",
+		     MAC2STR(addr), uuid,
+		     wps_dev_type_bin2str(pri_dev_type, devtype,
+					  sizeof(devtype)),
+		     config_methods, dev_password_id, request_type, dev_name);
+}
+
+
+static int str_starts(const char *str, const char *start)
+{
+	return os_strncmp(str, start, os_strlen(start)) == 0;
+}
+
+
+static void wps_reload_config(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_iface *iface = eloop_data;
+
+	wpa_printf(MSG_DEBUG, "WPS: Reload configuration data");
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->reload_config(iface) < 0) {
+		wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated "
+			   "configuration");
+	}
+}
+
+
+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)
+{
+	size_t blen = attr_len * 2 + 1;
+	char *buf = os_malloc(blen);
+	if (buf) {
+		wpa_snprintf_hex(buf, blen, attr, attr_len);
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			WPS_EVENT_NEW_AP_SETTINGS "%s", buf);
+		os_free(buf);
+	}
+}
+
+
+static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
+				       const struct wps_credential *cred)
+{
+	struct hostapd_bss_config *bss = hapd->conf;
+
+	wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration");
+
+	bss->wps_state = 2;
+	if (cred->ssid_len <= SSID_MAX_LEN) {
+		os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len);
+		bss->ssid.ssid_len = cred->ssid_len;
+		bss->ssid.ssid_set = 1;
+	}
+
+	if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
+	    (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
+		bss->wpa = 3;
+	else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
+		bss->wpa = 2;
+	else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
+		bss->wpa = 1;
+	else
+		bss->wpa = 0;
+
+	if (bss->wpa) {
+		if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA))
+			bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+		if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
+			bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+
+		bss->wpa_pairwise = 0;
+		if (cred->encr_type & WPS_ENCR_AES) {
+			if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
+				bss->wpa_pairwise |= WPA_CIPHER_GCMP;
+			else
+				bss->wpa_pairwise |= WPA_CIPHER_CCMP;
+		}
+		if (cred->encr_type & WPS_ENCR_TKIP)
+			bss->wpa_pairwise |= WPA_CIPHER_TKIP;
+		bss->rsn_pairwise = bss->wpa_pairwise;
+		bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa,
+							    bss->wpa_pairwise,
+							    bss->rsn_pairwise);
+
+		if (cred->key_len >= 8 && cred->key_len < 64) {
+			os_free(bss->ssid.wpa_passphrase);
+			bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1);
+			if (bss->ssid.wpa_passphrase)
+				os_memcpy(bss->ssid.wpa_passphrase, cred->key,
+					  cred->key_len);
+			hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
+		} else if (cred->key_len == 64) {
+			hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
+			bss->ssid.wpa_psk =
+				os_zalloc(sizeof(struct hostapd_wpa_psk));
+			if (bss->ssid.wpa_psk &&
+			    hexstr2bin((const char *) cred->key,
+				       bss->ssid.wpa_psk->psk, PMK_LEN) == 0) {
+				bss->ssid.wpa_psk->group = 1;
+				os_free(bss->ssid.wpa_passphrase);
+				bss->ssid.wpa_passphrase = NULL;
+			}
+		}
+		bss->auth_algs = 1;
+	} else {
+		/*
+		 * 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
+	 * EAP-WSC to be finished.
+	 */
+	eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
+			       NULL);
+
+	return 0;
+}
+
+
+static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
+{
+	const struct wps_credential *cred = ctx;
+	FILE *oconf, *nconf;
+	size_t len, i;
+	char *tmp_fname;
+	char buf[1024];
+	int multi_bss;
+	int wpa;
+
+	if (hapd->wps == NULL)
+		return 0;
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
+			cred->cred_attr, cred->cred_attr_len);
+
+	wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings");
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len);
+	wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x",
+		   cred->auth_type);
+	wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type);
+	wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
+			cred->key, cred->key_len);
+	wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR,
+		   MAC2STR(cred->mac_addr));
+
+	if ((hapd->conf->wps_cred_processing == 1 ||
+	     hapd->conf->wps_cred_processing == 2) && cred->cred_attr) {
+		hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len);
+	} else if (hapd->conf->wps_cred_processing == 1 ||
+		   hapd->conf->wps_cred_processing == 2) {
+		struct wpabuf *attr;
+		attr = wpabuf_alloc(200);
+		if (attr && wps_build_credential_wrap(attr, cred) == 0)
+			hapd_new_ap_event(hapd, wpabuf_head_u8(attr),
+					  wpabuf_len(attr));
+		wpabuf_free(attr);
+	} else
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS);
+
+	if (hapd->conf->wps_cred_processing == 1)
+		return 0;
+
+	os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len);
+	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;
+		hapd->wps->network_key_len = 0;
+	} else if ((cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) &&
+		   (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN)) {
+		wpa_printf(MSG_INFO, "WPS: Invalid key length %lu for WPA/WPA2",
+			   (unsigned long) cred->key_len);
+		return -1;
+	} else {
+		if (hapd->wps->network_key == NULL ||
+		    hapd->wps->network_key_len < cred->key_len) {
+			hapd->wps->network_key_len = 0;
+			os_free(hapd->wps->network_key);
+			hapd->wps->network_key = os_malloc(cred->key_len);
+			if (hapd->wps->network_key == NULL)
+				return -1;
+		}
+		hapd->wps->network_key_len = cred->key_len;
+		os_memcpy(hapd->wps->network_key, cred->key, cred->key_len);
+	}
+	hapd->wps->wps_state = WPS_STATE_CONFIGURED;
+
+	if (hapd->iface->config_fname == NULL)
+		return hapd_wps_reconfig_in_memory(hapd, cred);
+	len = os_strlen(hapd->iface->config_fname) + 5;
+	tmp_fname = os_malloc(len);
+	if (tmp_fname == NULL)
+		return -1;
+	os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname);
+
+	oconf = fopen(hapd->iface->config_fname, "r");
+	if (oconf == NULL) {
+		wpa_printf(MSG_WARNING, "WPS: Could not open current "
+			   "configuration file");
+		os_free(tmp_fname);
+		return -1;
+	}
+
+	nconf = fopen(tmp_fname, "w");
+	if (nconf == NULL) {
+		wpa_printf(MSG_WARNING, "WPS: Could not write updated "
+			   "configuration file");
+		os_free(tmp_fname);
+		fclose(oconf);
+		return -1;
+	}
+
+	fprintf(nconf, "# WPS configuration - START\n");
+
+	fprintf(nconf, "wps_state=2\n");
+
+	if (is_hex(cred->ssid, cred->ssid_len)) {
+		fprintf(nconf, "ssid2=");
+		for (i = 0; i < cred->ssid_len; i++)
+			fprintf(nconf, "%02x", cred->ssid[i]);
+		fprintf(nconf, "\n");
+	} else {
+		fprintf(nconf, "ssid=");
+		for (i = 0; i < cred->ssid_len; i++)
+			fputc(cred->ssid[i], nconf);
+		fprintf(nconf, "\n");
+	}
+
+	if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
+	    (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
+		wpa = 3;
+	else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
+		wpa = 2;
+	else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
+		wpa = 1;
+	else
+		wpa = 0;
+
+	if (wpa) {
+		char *prefix;
+		fprintf(nconf, "wpa=%d\n", wpa);
+
+		fprintf(nconf, "wpa_key_mgmt=");
+		prefix = "";
+		if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) {
+			fprintf(nconf, "WPA-EAP");
+			prefix = " ";
+		}
+		if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
+			fprintf(nconf, "%sWPA-PSK", prefix);
+		fprintf(nconf, "\n");
+
+		fprintf(nconf, "wpa_pairwise=");
+		prefix = "";
+		if (cred->encr_type & WPS_ENCR_AES) {
+			if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
+				fprintf(nconf, "GCMP");
+			else
+				fprintf(nconf, "CCMP");
+
+			prefix = " ";
+		}
+		if (cred->encr_type & WPS_ENCR_TKIP) {
+			fprintf(nconf, "%sTKIP", prefix);
+		}
+		fprintf(nconf, "\n");
+
+		if (cred->key_len >= 8 && cred->key_len < 64) {
+			fprintf(nconf, "wpa_passphrase=");
+			for (i = 0; i < cred->key_len; i++)
+				fputc(cred->key[i], nconf);
+			fprintf(nconf, "\n");
+		} else if (cred->key_len == 64) {
+			fprintf(nconf, "wpa_psk=");
+			for (i = 0; i < cred->key_len; i++)
+				fputc(cred->key[i], nconf);
+			fprintf(nconf, "\n");
+		} else {
+			wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu "
+				   "for WPA/WPA2",
+				   (unsigned long) cred->key_len);
+		}
+
+		fprintf(nconf, "auth_algs=1\n");
+	} else {
+		/*
+		 * 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");
+
+	multi_bss = 0;
+	while (fgets(buf, sizeof(buf), oconf)) {
+		if (os_strncmp(buf, "bss=", 4) == 0)
+			multi_bss = 1;
+		if (!multi_bss &&
+		    (str_starts(buf, "ssid=") ||
+		     str_starts(buf, "ssid2=") ||
+		     str_starts(buf, "auth_algs=") ||
+		     str_starts(buf, "wep_default_key=") ||
+		     str_starts(buf, "wep_key") ||
+		     str_starts(buf, "wps_state=") ||
+		     str_starts(buf, "wpa=") ||
+		     str_starts(buf, "wpa_psk=") ||
+		     str_starts(buf, "wpa_pairwise=") ||
+		     str_starts(buf, "rsn_pairwise=") ||
+		     str_starts(buf, "wpa_key_mgmt=") ||
+		     str_starts(buf, "wpa_passphrase="))) {
+			fprintf(nconf, "#WPS# %s", buf);
+		} else
+			fprintf(nconf, "%s", buf);
+	}
+
+	fclose(nconf);
+	fclose(oconf);
+
+	if (rename(tmp_fname, hapd->iface->config_fname) < 0) {
+		wpa_printf(MSG_WARNING, "WPS: Failed to rename the updated "
+			   "configuration file: %s", strerror(errno));
+		os_free(tmp_fname);
+		return -1;
+	}
+
+	os_free(tmp_fname);
+
+	/* Schedule configuration reload after short period of time to allow
+	 * EAP-WSC to be finished.
+	 */
+	eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
+			       NULL);
+
+	wpa_printf(MSG_DEBUG, "WPS: AP configuration updated");
+
+	return 0;
+}
+
+
+static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
+{
+	struct hostapd_data *hapd = ctx;
+	return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred);
+}
+
+
+static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_data;
+
+	if (hapd->conf->ap_setup_locked)
+		return;
+	if (hapd->ap_pin_failures_consecutive >= 10)
+		return;
+
+	wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN");
+	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
+	hapd->wps->ap_setup_locked = 0;
+	wps_registrar_update_ie(hapd->wps->registrar);
+}
+
+
+static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_event_pwd_auth_fail *data = ctx;
+
+	if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL)
+		return 0;
+
+	/*
+	 * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup
+	 * for some time if this happens multiple times to slow down brute
+	 * force attacks.
+	 */
+	hapd->ap_pin_failures++;
+	hapd->ap_pin_failures_consecutive++;
+	wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u "
+		   "(%u consecutive)",
+		   hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
+	if (hapd->ap_pin_failures < 3)
+		return 0;
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED);
+	hapd->wps->ap_setup_locked = 1;
+
+	wps_registrar_update_ie(hapd->wps->registrar);
+
+	if (!hapd->conf->ap_setup_locked &&
+	    hapd->ap_pin_failures_consecutive >= 10) {
+		/*
+		 * In indefinite lockdown - disable automatic AP PIN
+		 * reenablement.
+		 */
+		eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+		wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
+	} else if (!hapd->conf->ap_setup_locked) {
+		if (hapd->ap_pin_lockout_time == 0)
+			hapd->ap_pin_lockout_time = 60;
+		else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
+			 (hapd->ap_pin_failures % 3) == 0)
+			hapd->ap_pin_lockout_time *= 2;
+
+		wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds",
+			   hapd->ap_pin_lockout_time);
+		eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+		eloop_register_timeout(hapd->ap_pin_lockout_time, 0,
+				       hostapd_wps_reenable_ap_pin, hapd,
+				       NULL);
+	}
+
+	return 0;
+}
+
+
+static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
+				  struct wps_event_pwd_auth_fail *data)
+{
+	/* Update WPS Status - Authentication Failure */
+	wpa_printf(MSG_DEBUG, "WPS: Authentication failure update");
+	hapd->wps_stats.status = WPS_STATUS_FAILURE;
+	hapd->wps_stats.failure_reason = WPS_EI_AUTH_FAILURE;
+	os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN);
+
+	hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data);
+}
+
+
+static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx)
+{
+	if (hapd->conf->ap_pin == NULL || hapd->wps == NULL)
+		return 0;
+
+	if (hapd->ap_pin_failures_consecutive == 0)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter "
+		   "- total validation failures %u (%u consecutive)",
+		   hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
+	hapd->ap_pin_failures_consecutive = 0;
+
+	return 0;
+}
+
+
+static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd)
+{
+	hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL);
+}
+
+
+static void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd)
+{
+	/* Update WPS Status - PBC Overlap */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP;
+}
+
+
+static void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd)
+{
+	/* Update WPS PBC Status:PBC Timeout */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT;
+}
+
+
+static void hostapd_wps_event_pbc_active(struct hostapd_data *hapd)
+{
+	/* Update WPS PBC status - Active */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE;
+}
+
+
+static void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd)
+{
+	/* Update WPS PBC status - Active */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
+}
+
+
+static void hostapd_wps_event_success(struct hostapd_data *hapd,
+				      struct wps_event_success *success)
+{
+	/* Update WPS status - Success */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
+	hapd->wps_stats.status = WPS_STATUS_SUCCESS;
+	os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN);
+}
+
+
+static void hostapd_wps_event_fail(struct hostapd_data *hapd,
+				   struct wps_event_fail *fail)
+{
+	/* Update WPS status - Failure */
+	hapd->wps_stats.status = WPS_STATUS_FAILURE;
+	os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN);
+
+	hapd->wps_stats.failure_reason = fail->error_indication;
+
+	if (fail->error_indication > 0 &&
+	    fail->error_indication < NUM_WPS_EI_VALUES) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
+			fail->msg, fail->config_error, fail->error_indication,
+			wps_ei_str(fail->error_indication));
+	} else {
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			WPS_EVENT_FAIL "msg=%d config_error=%d",
+			fail->msg, fail->config_error);
+	}
+}
+
+
+static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
+				 union wps_event_data *data)
+{
+	struct hostapd_data *hapd = ctx;
+
+	switch (event) {
+	case WPS_EV_M2D:
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D);
+		break;
+	case WPS_EV_FAIL:
+		hostapd_wps_event_fail(hapd, &data->fail);
+		break;
+	case WPS_EV_SUCCESS:
+		hostapd_wps_event_success(hapd, &data->success);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS);
+		break;
+	case WPS_EV_PWD_AUTH_FAIL:
+		hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail);
+		break;
+	case WPS_EV_PBC_OVERLAP:
+		hostapd_wps_event_pbc_overlap(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP);
+		break;
+	case WPS_EV_PBC_TIMEOUT:
+		hostapd_wps_event_pbc_timeout(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT);
+		break;
+	case WPS_EV_PBC_ACTIVE:
+		hostapd_wps_event_pbc_active(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE);
+		break;
+	case WPS_EV_PBC_DISABLE:
+		hostapd_wps_event_pbc_disable(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE);
+		break;
+	case WPS_EV_ER_AP_ADD:
+		break;
+	case WPS_EV_ER_AP_REMOVE:
+		break;
+	case WPS_EV_ER_ENROLLEE_ADD:
+		break;
+	case WPS_EV_ER_ENROLLEE_REMOVE:
+		break;
+	case WPS_EV_ER_AP_SETTINGS:
+		break;
+	case WPS_EV_ER_SET_SELECTED_REGISTRAR:
+		break;
+	case WPS_EV_AP_PIN_SUCCESS:
+		hostapd_wps_ap_pin_success(hapd);
+		break;
+	}
+	if (hapd->wps_event_cb)
+		hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data);
+}
+
+
+static int hostapd_wps_rf_band_cb(void *ctx)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
+		WPS_RF_50GHZ :
+		hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ?
+		WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+}
+
+
+static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only)
+{
+	wpabuf_free(hapd->wps_beacon_ie);
+	hapd->wps_beacon_ie = NULL;
+
+	wpabuf_free(hapd->wps_probe_resp_ie);
+	hapd->wps_probe_resp_ie = NULL;
+
+	if (deinit_only) {
+		hostapd_reset_ap_wps_ie(hapd);
+		return;
+	}
+
+	hostapd_set_ap_wps_ie(hapd);
+}
+
+
+static int get_uuid_cb(struct hostapd_iface *iface, void *ctx)
+{
+	const u8 **uuid = ctx;
+	size_t j;
+
+	if (iface == NULL)
+		return 0;
+	for (j = 0; j < iface->num_bss; j++) {
+		struct hostapd_data *hapd = iface->bss[j];
+		if (hapd->wps && !hapd->conf->wps_independent &&
+		    !is_nil_uuid(hapd->wps->uuid)) {
+			*uuid = hapd->wps->uuid;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+static const u8 * get_own_uuid(struct hostapd_iface *iface)
+{
+	const u8 *uuid;
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->for_each_interface == NULL)
+		return NULL;
+	uuid = NULL;
+	iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb,
+					      &uuid);
+	return uuid;
+}
+
+
+static int count_interface_cb(struct hostapd_iface *iface, void *ctx)
+{
+	int *count= ctx;
+	(*count)++;
+	return 0;
+}
+
+
+static int interface_count(struct hostapd_iface *iface)
+{
+	int count = 0;
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->for_each_interface == NULL)
+		return 0;
+	iface->interfaces->for_each_interface(iface->interfaces,
+					      count_interface_cb, &count);
+	return count;
+}
+
+
+static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd,
+				      struct wps_context *wps)
+{
+	int i;
+
+	for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+		wpabuf_free(wps->dev.vendor_ext[i]);
+		wps->dev.vendor_ext[i] = NULL;
+
+		if (hapd->conf->wps_vendor_ext[i] == NULL)
+			continue;
+
+		wps->dev.vendor_ext[i] =
+			wpabuf_dup(hapd->conf->wps_vendor_ext[i]);
+		if (wps->dev.vendor_ext[i] == NULL) {
+			while (--i >= 0)
+				wpabuf_free(wps->dev.vendor_ext[i]);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+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)
+{
+	struct wps_context *wps;
+	struct wps_registrar_config cfg;
+
+	if (conf->wps_state == 0) {
+		hostapd_wps_clear_ies(hapd, 0);
+		return 0;
+	}
+
+	wps = os_zalloc(sizeof(*wps));
+	if (wps == NULL)
+		return -1;
+
+	wps->cred_cb = hostapd_wps_cred_cb;
+	wps->event_cb = hostapd_wps_event_cb;
+	wps->rf_band_cb = hostapd_wps_rf_band_cb;
+	wps->cb_ctx = hapd;
+
+	os_memset(&cfg, 0, sizeof(cfg));
+	wps->wps_state = hapd->conf->wps_state;
+	wps->ap_setup_locked = hapd->conf->ap_setup_locked;
+	if (is_nil_uuid(hapd->conf->uuid)) {
+		const u8 *uuid;
+		uuid = get_own_uuid(hapd->iface);
+		if (uuid && !conf->wps_independent) {
+			os_memcpy(wps->uuid, uuid, UUID_LEN);
+			wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another "
+				    "interface", wps->uuid, UUID_LEN);
+		} else {
+			uuid_gen_mac_addr(hapd->own_addr, wps->uuid);
+			wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
+				    "address", wps->uuid, UUID_LEN);
+		}
+	} else {
+		os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN);
+		wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID",
+			    wps->uuid, UUID_LEN);
+	}
+	wps->ssid_len = hapd->conf->ssid.ssid_len;
+	os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len);
+	wps->ap = 1;
+	os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN);
+	wps->dev.device_name = hapd->conf->device_name ?
+		os_strdup(hapd->conf->device_name) : NULL;
+	wps->dev.manufacturer = hapd->conf->manufacturer ?
+		os_strdup(hapd->conf->manufacturer) : NULL;
+	wps->dev.model_name = hapd->conf->model_name ?
+		os_strdup(hapd->conf->model_name) : NULL;
+	wps->dev.model_number = hapd->conf->model_number ?
+		os_strdup(hapd->conf->model_number) : NULL;
+	wps->dev.serial_number = hapd->conf->serial_number ?
+		os_strdup(hapd->conf->serial_number) : NULL;
+	wps->config_methods =
+		wps_config_methods_str2bin(hapd->conf->config_methods);
+	if ((wps->config_methods &
+	     (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
+	      WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
+		wpa_printf(MSG_INFO, "WPS: Converting display to "
+			   "virtual_display for WPS 2.0 compliance");
+		wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY;
+	}
+	if ((wps->config_methods &
+	     (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
+	      WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
+		wpa_printf(MSG_INFO, "WPS: Converting push_button to "
+			   "virtual_push_button for WPS 2.0 compliance");
+		wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+	}
+	os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type,
+		  WPS_DEV_TYPE_LEN);
+
+	if (hostapd_wps_set_vendor_ext(hapd, wps) < 0)
+		goto fail;
+
+	wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
+
+	if (conf->wps_rf_bands) {
+		wps->dev.rf_bands = conf->wps_rf_bands;
+	} else {
+		wps->dev.rf_bands =
+			hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
+			WPS_RF_50GHZ :
+			hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ?
+			WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+	}
+
+	if (conf->wpa & WPA_PROTO_RSN) {
+		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
+			wps->auth_types |= WPS_AUTH_WPA2PSK;
+		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+			wps->auth_types |= WPS_AUTH_WPA2;
+
+		if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP))
+			wps->encr_types |= WPS_ENCR_AES;
+		if (conf->rsn_pairwise & WPA_CIPHER_TKIP)
+			wps->encr_types |= WPS_ENCR_TKIP;
+	}
+
+	if (conf->wpa & WPA_PROTO_WPA) {
+		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
+			wps->auth_types |= WPS_AUTH_WPAPSK;
+		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+			wps->auth_types |= WPS_AUTH_WPA;
+
+		if (conf->wpa_pairwise & WPA_CIPHER_CCMP)
+			wps->encr_types |= WPS_ENCR_AES;
+		if (conf->wpa_pairwise & WPA_CIPHER_TKIP)
+			wps->encr_types |= WPS_ENCR_TKIP;
+	}
+
+	if (conf->ssid.security_policy == SECURITY_PLAINTEXT) {
+		wps->encr_types |= WPS_ENCR_NONE;
+		wps->auth_types |= WPS_AUTH_OPEN;
+	}
+
+	if (conf->ssid.wpa_psk_file) {
+		/* Use per-device PSKs */
+	} else if (conf->ssid.wpa_passphrase) {
+		wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase);
+		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)
+			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)
+			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];
+	}
+
+	if (conf->ssid.wpa_psk) {
+		os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN);
+		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;
+		wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
+	}
+
+	wps->ap_settings = conf->ap_settings;
+	wps->ap_settings_len = conf->ap_settings_len;
+
+	cfg.new_psk_cb = hostapd_wps_new_psk_cb;
+	cfg.set_ie_cb = hostapd_wps_set_ie_cb;
+	cfg.pin_needed_cb = hostapd_wps_pin_needed_cb;
+	cfg.reg_success_cb = hostapd_wps_reg_success_cb;
+	cfg.enrollee_seen_cb = hostapd_wps_enrollee_seen_cb;
+	cfg.cb_ctx = hapd;
+	cfg.skip_cred_build = conf->skip_cred_build;
+	cfg.extra_cred = conf->extra_cred;
+	cfg.extra_cred_len = conf->extra_cred_len;
+	cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) &&
+		conf->skip_cred_build;
+	if (conf->ssid.security_policy == SECURITY_STATIC_WEP)
+		cfg.static_wep_only = 1;
+	cfg.dualband = interface_count(hapd->iface) > 1;
+	if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) ==
+	    (WPS_RF_50GHZ | WPS_RF_24GHZ))
+		cfg.dualband = 1;
+	if (cfg.dualband)
+		wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
+	cfg.force_per_enrollee_psk = conf->force_per_enrollee_psk;
+
+	wps->registrar = wps_registrar_init(wps, &cfg);
+	if (wps->registrar == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar");
+		goto fail;
+	}
+
+#ifdef CONFIG_WPS_UPNP
+	wps->friendly_name = hapd->conf->friendly_name;
+	wps->manufacturer_url = hapd->conf->manufacturer_url;
+	wps->model_description = hapd->conf->model_description;
+	wps->model_url = hapd->conf->model_url;
+	wps->upc = hapd->conf->upc;
+#endif /* CONFIG_WPS_UPNP */
+
+	hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
+
+	hapd->wps = wps;
+
+	return 0;
+
+fail:
+	hostapd_free_wps(wps);
+	return -1;
+}
+
+
+int hostapd_init_wps_complete(struct hostapd_data *hapd)
+{
+	struct wps_context *wps = hapd->wps;
+
+	if (wps == NULL)
+		return 0;
+
+#ifdef CONFIG_WPS_UPNP
+	if (hostapd_wps_upnp_init(hapd, wps) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
+		wps_registrar_deinit(wps->registrar);
+		hostapd_free_wps(wps);
+		hapd->wps = NULL;
+		return -1;
+	}
+#endif /* CONFIG_WPS_UPNP */
+
+	return 0;
+}
+
+
+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;
+	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_NFC */
+}
+
+
+void hostapd_deinit_wps(struct hostapd_data *hapd)
+{
+	eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+	eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, 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);
+	wps_free_pending_msgs(hapd->wps->upnp_msgs);
+	hostapd_free_wps(hapd->wps);
+	hapd->wps = NULL;
+	hostapd_wps_clear_ies(hapd, 1);
+}
+
+
+void hostapd_update_wps(struct hostapd_data *hapd)
+{
+	if (hapd->wps == NULL)
+		return;
+
+#ifdef CONFIG_WPS_UPNP
+	hapd->wps->friendly_name = hapd->conf->friendly_name;
+	hapd->wps->manufacturer_url = hapd->conf->manufacturer_url;
+	hapd->wps->model_description = hapd->conf->model_description;
+	hapd->wps->model_url = hapd->conf->model_url;
+	hapd->wps->upc = hapd->conf->upc;
+#endif /* CONFIG_WPS_UPNP */
+
+	hostapd_wps_set_vendor_ext(hapd, hapd->wps);
+
+	if (hapd->conf->wps_state)
+		wps_registrar_update_ie(hapd->wps->registrar);
+	else
+		hostapd_deinit_wps(hapd);
+}
+
+
+struct wps_add_pin_data {
+	const u8 *addr;
+	const u8 *uuid;
+	const u8 *pin;
+	size_t pin_len;
+	int timeout;
+	int added;
+};
+
+
+static int wps_add_pin(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_add_pin_data *data = ctx;
+	int ret;
+
+	if (hapd->wps == NULL)
+		return 0;
+	ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr,
+				    data->uuid, data->pin, data->pin_len,
+				    data->timeout);
+	if (ret == 0)
+		data->added++;
+	return ret;
+}
+
+
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
+			const char *uuid, const char *pin, int timeout)
+{
+	u8 u[UUID_LEN];
+	struct wps_add_pin_data data;
+
+	data.addr = addr;
+	data.uuid = u;
+	data.pin = (const u8 *) pin;
+	data.pin_len = os_strlen(pin);
+	data.timeout = timeout;
+	data.added = 0;
+
+	if (os_strcmp(uuid, "any") == 0)
+		data.uuid = NULL;
+	else {
+		if (uuid_str2bin(uuid, u))
+			return -1;
+		data.uuid = u;
+	}
+	if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0)
+		return -1;
+	return data.added ? 0 : -1;
+}
+
+
+struct wps_button_pushed_ctx {
+	const u8 *p2p_dev_addr;
+	unsigned int count;
+};
+
+static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_button_pushed_ctx *data = ctx;
+
+	if (hapd->wps) {
+		data->count++;
+		return wps_registrar_button_pushed(hapd->wps->registrar,
+						   data->p2p_dev_addr);
+	}
+
+	return 0;
+}
+
+
+int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+			      const u8 *p2p_dev_addr)
+{
+	struct wps_button_pushed_ctx ctx;
+	int ret;
+
+	os_memset(&ctx, 0, sizeof(ctx));
+	ctx.p2p_dev_addr = p2p_dev_addr;
+	ret = hostapd_wps_for_each(hapd, wps_button_pushed, &ctx);
+	if (ret == 0 && !ctx.count)
+		ret = -1;
+	return ret;
+}
+
+
+struct wps_cancel_ctx {
+	unsigned int count;
+};
+
+static int wps_cancel(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_cancel_ctx *data = ctx;
+
+	if (hapd->wps) {
+		data->count++;
+		wps_registrar_wps_cancel(hapd->wps->registrar);
+		ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+	}
+
+	return 0;
+}
+
+
+int hostapd_wps_cancel(struct hostapd_data *hapd)
+{
+	struct wps_cancel_ctx ctx;
+	int ret;
+
+	os_memset(&ctx, 0, sizeof(ctx));
+	ret = hostapd_wps_for_each(hapd, wps_cancel, &ctx);
+	if (ret == 0 && !ctx.count)
+		ret = -1;
+	return ret;
+}
+
+
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
+				    const u8 *bssid,
+				    const u8 *ie, size_t ie_len,
+				    int ssi_signal)
+{
+	struct hostapd_data *hapd = ctx;
+	struct wpabuf *wps_ie;
+	struct ieee802_11_elems elems;
+
+	if (hapd->wps == NULL)
+		return 0;
+
+	if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "WPS: Could not parse ProbeReq from "
+			   MACSTR, MAC2STR(addr));
+		return 0;
+	}
+
+	if (elems.ssid && elems.ssid_len > 0 &&
+	    (elems.ssid_len != hapd->conf->ssid.ssid_len ||
+	     os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) !=
+	     0))
+		return 0; /* Not for us */
+
+	wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+	if (wps_ie == NULL)
+		return 0;
+	if (wps_validate_probe_req(wps_ie, addr) < 0) {
+		wpabuf_free(wps_ie);
+		return 0;
+	}
+
+	if (wpabuf_len(wps_ie) > 0) {
+		int p2p_wildcard = 0;
+#ifdef CONFIG_P2P
+		if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
+		    os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
+			      P2P_WILDCARD_SSID_LEN) == 0)
+			p2p_wildcard = 1;
+#endif /* CONFIG_P2P */
+		wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie,
+					   p2p_wildcard);
+#ifdef CONFIG_WPS_UPNP
+		/* FIX: what exactly should be included in the WLANEvent?
+		 * WPS attributes? Full ProbeReq frame? */
+		if (!p2p_wildcard)
+			upnp_wps_device_send_wlan_event(
+				hapd->wps_upnp, addr,
+				UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie);
+#endif /* CONFIG_WPS_UPNP */
+	}
+
+	wpabuf_free(wps_ie);
+
+	return 0;
+}
+
+
+#ifdef CONFIG_WPS_UPNP
+
+static int hostapd_rx_req_put_wlan_response(
+	void *priv, enum upnp_wps_wlanevent_type ev_type,
+	const u8 *mac_addr, const struct wpabuf *msg,
+	enum wps_msg_type msg_type)
+{
+	struct hostapd_data *hapd = priv;
+	struct sta_info *sta;
+	struct upnp_pending_message *p;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr="
+		   MACSTR, ev_type, MAC2STR(mac_addr));
+	wpa_hexdump(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage",
+		    wpabuf_head(msg), wpabuf_len(msg));
+	if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected "
+			   "PutWLANResponse WLANEventType %d", ev_type);
+		return -1;
+	}
+
+	/*
+	 * EAP response to ongoing to WPS Registration. Send it to EAP-WSC
+	 * server implementation for delivery to the peer.
+	 */
+
+	sta = ap_get_sta(hapd, mac_addr);
+#ifndef CONFIG_WPS_STRICT
+	if (!sta) {
+		/*
+		 * Workaround - Intel wsccmd uses bogus NewWLANEventMAC:
+		 * Pick STA that is in an ongoing WPS registration without
+		 * checking the MAC address.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found based "
+			   "on NewWLANEventMAC; try wildcard match");
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (sta->eapol_sm && (sta->flags & WLAN_STA_WPS))
+				break;
+		}
+	}
+#endif /* CONFIG_WPS_STRICT */
+
+	if (!sta || !(sta->flags & WLAN_STA_WPS)) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found");
+		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;
+	os_memcpy(p->addr, sta->addr, ETH_ALEN);
+	p->msg = wpabuf_dup(msg);
+	p->type = msg_type;
+	p->next = hapd->wps->upnp_msgs;
+	hapd->wps->upnp_msgs = p;
+
+	return eapol_auth_eap_pending_cb(sta->eapol_sm, sta->eapol_sm->eap);
+}
+
+
+static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
+				 struct wps_context *wps)
+{
+	struct upnp_wps_device_ctx *ctx;
+
+	if (!hapd->conf->upnp_iface)
+		return 0;
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return -1;
+
+	ctx->rx_req_put_wlan_response = hostapd_rx_req_put_wlan_response;
+	if (hapd->conf->ap_pin)
+		ctx->ap_pin = os_strdup(hapd->conf->ap_pin);
+
+	hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd,
+					      hapd->conf->upnp_iface);
+	if (hapd->wps_upnp == NULL)
+		return -1;
+	wps->wps_upnp = hapd->wps_upnp;
+
+	return 0;
+}
+
+
+static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd)
+{
+	upnp_wps_device_deinit(hapd->wps_upnp, hapd);
+}
+
+#endif /* CONFIG_WPS_UPNP */
+
+
+int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
+			    char *buf, size_t buflen)
+{
+	if (hapd->wps == NULL)
+		return 0;
+	return wps_registrar_get_info(hapd->wps->registrar, addr, buf, buflen);
+}
+
+
+static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_data;
+	wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
+	hostapd_wps_ap_pin_disable(hapd);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED);
+}
+
+
+static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
+	hapd->ap_pin_failures = 0;
+	hapd->ap_pin_failures_consecutive = 0;
+	hapd->conf->ap_setup_locked = 0;
+	if (hapd->wps->ap_setup_locked) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
+		hapd->wps->ap_setup_locked = 0;
+		wps_registrar_update_ie(hapd->wps->registrar);
+	}
+	eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0,
+				       hostapd_wps_ap_pin_timeout, hapd, NULL);
+}
+
+
+static int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx)
+{
+	os_free(hapd->conf->ap_pin);
+	hapd->conf->ap_pin = NULL;
+#ifdef CONFIG_WPS_UPNP
+	upnp_wps_set_ap_pin(hapd->wps_upnp, NULL);
+#endif /* CONFIG_WPS_UPNP */
+	eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+	return 0;
+}
+
+
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
+	hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL);
+}
+
+
+struct wps_ap_pin_data {
+	char pin_txt[9];
+	int timeout;
+};
+
+
+static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_ap_pin_data *data = ctx;
+
+	if (!hapd->wps)
+		return 0;
+
+	os_free(hapd->conf->ap_pin);
+	hapd->conf->ap_pin = os_strdup(data->pin_txt);
+#ifdef CONFIG_WPS_UPNP
+	upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt);
+#endif /* CONFIG_WPS_UPNP */
+	hostapd_wps_ap_pin_enable(hapd, data->timeout);
+	return 0;
+}
+
+
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
+{
+	unsigned int pin;
+	struct wps_ap_pin_data data;
+
+	pin = wps_generate_pin();
+	os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin);
+	data.timeout = timeout;
+	hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
+	return hapd->conf->ap_pin;
+}
+
+
+const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd)
+{
+	return hapd->conf->ap_pin;
+}
+
+
+int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
+			   int timeout)
+{
+	struct wps_ap_pin_data data;
+	int ret;
+
+	ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin);
+	if (os_snprintf_error(sizeof(data.pin_txt), ret))
+		return -1;
+	data.timeout = timeout;
+	return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
+}
+
+
+static int wps_update_ie(struct hostapd_data *hapd, void *ctx)
+{
+	if (hapd->wps)
+		wps_registrar_update_ie(hapd->wps->registrar);
+	return 0;
+}
+
+
+void hostapd_wps_update_ie(struct hostapd_data *hapd)
+{
+	hostapd_wps_for_each(hapd, wps_update_ie, NULL);
+}
+
+
+int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
+			  const char *auth, const char *encr, const char *key)
+{
+	struct wps_credential cred;
+	size_t len;
+
+	os_memset(&cred, 0, sizeof(cred));
+
+	len = os_strlen(ssid);
+	if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
+	    hexstr2bin(ssid, cred.ssid, len / 2))
+		return -1;
+	cred.ssid_len = len / 2;
+
+	if (os_strncmp(auth, "OPEN", 4) == 0)
+		cred.auth_type = WPS_AUTH_OPEN;
+	else if (os_strncmp(auth, "WPAPSK", 6) == 0)
+		cred.auth_type = WPS_AUTH_WPAPSK;
+	else if (os_strncmp(auth, "WPA2PSK", 7) == 0)
+		cred.auth_type = WPS_AUTH_WPA2PSK;
+	else
+		return -1;
+
+	if (encr) {
+		if (os_strncmp(encr, "NONE", 4) == 0)
+			cred.encr_type = WPS_ENCR_NONE;
+		else if (os_strncmp(encr, "TKIP", 4) == 0)
+			cred.encr_type = WPS_ENCR_TKIP;
+		else if (os_strncmp(encr, "CCMP", 4) == 0)
+			cred.encr_type = WPS_ENCR_AES;
+		else
+			return -1;
+	} else
+		cred.encr_type = WPS_ENCR_NONE;
+
+	if (key) {
+		len = os_strlen(key);
+		if ((len & 1) || len > 2 * sizeof(cred.key) ||
+		    hexstr2bin(key, cred.key, len / 2))
+			return -1;
+		cred.key_len = len / 2;
+	}
+
+	return wps_registrar_config_ap(hapd->wps->registrar, &cred);
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wps_nfc_password_token_data {
+	const u8 *oob_dev_pw;
+	size_t oob_dev_pw_len;
+	int added;
+};
+
+
+static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_nfc_password_token_data *data = ctx;
+	int ret;
+
+	if (hapd->wps == NULL)
+		return 0;
+	ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar,
+						   data->oob_dev_pw,
+						   data->oob_dev_pw_len);
+	if (ret == 0)
+		data->added++;
+	return ret;
+}
+
+
+static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd,
+					      struct wps_parse_attr *attr)
+{
+	struct wps_nfc_password_token_data data;
+
+	data.oob_dev_pw = attr->oob_dev_password;
+	data.oob_dev_pw_len = attr->oob_dev_password_len;
+	data.added = 0;
+	if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0)
+		return -1;
+	return data.added ? 0 : -1;
+}
+
+
+static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd,
+				       const struct wpabuf *wps)
+{
+	struct wps_parse_attr attr;
+
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
+
+	if (wps_parse_msg(wps, &attr)) {
+		wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
+		return -1;
+	}
+
+	if (attr.oob_dev_password)
+		return hostapd_wps_add_nfc_password_token(hapd, &attr);
+
+	wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
+	return -1;
+}
+
+
+int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
+			     const struct wpabuf *data)
+{
+	const struct wpabuf *wps = data;
+	struct wpabuf *tmp = NULL;
+	int ret;
+
+	if (wpabuf_len(data) < 4)
+		return -1;
+
+	if (*wpabuf_head_u8(data) != 0x10) {
+		/* Assume this contains full NDEF record */
+		tmp = ndef_parse_wifi(data);
+		if (tmp == NULL) {
+			wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
+			return -1;
+		}
+		wps = tmp;
+	}
+
+	ret = hostapd_wps_nfc_tag_process(hapd, wps);
+	wpabuf_free(tmp);
+	return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
+					     int ndef)
+{
+	struct wpabuf *ret;
+
+	if (hapd->wps == NULL)
+		return NULL;
+
+	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);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+}
+
+
+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;
+
+	/*
+	 * 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(hapd->wps->registrar,
+					     attr.oob_dev_password,
+					     DEV_PW_NFC_CONNECTION_HANDOVER,
+					     NULL, 0, 1);
+
+out:
+	wpabuf_free(wps);
+	return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef)
+{
+	if (hapd->conf->wps_nfc_pw_from_config) {
+		return wps_nfc_token_build(ndef,
+					   hapd->conf->wps_nfc_dev_pw_id,
+					   hapd->conf->wps_nfc_dh_pubkey,
+					   hapd->conf->wps_nfc_dev_pw);
+	}
+
+	return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id,
+				 &hapd->conf->wps_nfc_dh_pubkey,
+				 &hapd->conf->wps_nfc_dh_privkey,
+				 &hapd->conf->wps_nfc_dev_pw);
+}
+
+
+int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
+{
+	struct wps_context *wps = hapd->wps;
+	struct wpabuf *pw;
+
+	if (wps == NULL)
+		return -1;
+
+	if (!hapd->conf->wps_nfc_dh_pubkey ||
+	    !hapd->conf->wps_nfc_dh_privkey ||
+	    !hapd->conf->wps_nfc_dev_pw ||
+	    !hapd->conf->wps_nfc_dev_pw_id)
+		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);
+	pw = hapd->conf->wps_nfc_dev_pw;
+	wps->ap_nfc_dev_pw = wpabuf_alloc(
+		wpabuf_len(pw) * 2 + 1);
+	if (wps->ap_nfc_dev_pw) {
+		wpa_snprintf_hex_uppercase(
+			(char *) wpabuf_put(wps->ap_nfc_dev_pw,
+					    wpabuf_len(pw) * 2),
+			wpabuf_len(pw) * 2 + 1,
+			wpabuf_head(pw), wpabuf_len(pw));
+	}
+
+	if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey ||
+	    !wps->ap_nfc_dev_pw) {
+		hostapd_wps_nfc_clear(wps);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+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);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/hostap/src/ap/wps_hostapd.h b/hostap/src/ap/wps_hostapd.h
new file mode 100644
index 0000000..204bd82
--- /dev/null
+++ b/hostap/src/ap/wps_hostapd.h
@@ -0,0 +1,92 @@
+/*
+ * hostapd / WPS integration
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_HOSTAPD_H
+#define WPS_HOSTAPD_H
+
+#ifdef CONFIG_WPS
+
+int hostapd_init_wps(struct hostapd_data *hapd,
+		     struct hostapd_bss_config *conf);
+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,
+			      const u8 *p2p_dev_addr);
+int hostapd_wps_cancel(struct hostapd_data *hapd);
+int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
+			    char *buf, size_t buflen);
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd);
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout);
+const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd);
+int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
+			   int timeout);
+void hostapd_wps_update_ie(struct hostapd_data *hapd);
+int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
+			  const char *auth, const char *encr, const char *key);
+int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
+			     const struct wpabuf *data);
+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);
+
+#else /* CONFIG_WPS */
+
+static inline int hostapd_init_wps(struct hostapd_data *hapd,
+				   struct hostapd_bss_config *conf)
+{
+	return 0;
+}
+
+static inline void hostapd_deinit_wps(struct hostapd_data *hapd)
+{
+}
+
+static inline int hostapd_init_wps_complete(struct hostapd_data *hapd)
+{
+    return 0;
+}
+
+static inline void hostapd_update_wps(struct hostapd_data *hapd)
+{
+}
+
+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)
+{
+	return 0;
+}
+
+static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+					    const u8 *p2p_dev_addr)
+{
+	return 0;
+}
+
+static inline int hostapd_wps_cancel(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+#endif /* CONFIG_WPS */
+
+#endif /* WPS_HOSTAPD_H */
diff --git a/hostap/src/ap/x_snoop.c b/hostap/src/ap/x_snoop.c
new file mode 100644
index 0000000..aef9a53
--- /dev/null
+++ b/hostap/src/ap/x_snoop.c
@@ -0,0 +1,131 @@
+/*
+ * Generic Snooping for Proxy ARP
+ * Copyright (c) 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 "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "x_snoop.h"
+
+
+int x_snoop_init(struct hostapd_data *hapd)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+
+	if (!conf->isolate) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: ap_isolate must be enabled for x_snoop");
+		return -1;
+	}
+
+	if (conf->bridge[0] == '\0') {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Bridge must be configured for x_snoop");
+		return -1;
+	}
+
+	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+					 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to enable hairpin_mode on the bridge port");
+		return -1;
+	}
+
+	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to enable proxyarp on the bridge port");
+		return -1;
+	}
+
+	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
+					 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
+		return -1;
+	}
+
+#ifdef CONFIG_IPV6
+	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to enable multicast snooping on the bridge");
+		return -1;
+	}
+#endif /* CONFIG_IPV6 */
+
+	return 0;
+}
+
+
+struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+		      void (*handler)(void *ctx, const u8 *src_addr,
+				      const u8 *buf, size_t len),
+		      enum l2_packet_filter_type type)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+	struct l2_packet_data *l2;
+
+	l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1);
+	if (l2 == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to initialize L2 packet processing %s",
+			   strerror(errno));
+		return NULL;
+	}
+
+	if (l2_packet_set_packet_filter(l2, type)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to set L2 packet filter for type: %d",
+			   type);
+		l2_packet_deinit(l2);
+		return NULL;
+	}
+
+	return l2;
+}
+
+
+void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+					 struct sta_info *sta, u8 *buf,
+					 size_t len)
+{
+	int res;
+	u8 addr[ETH_ALEN];
+	u8 *dst_addr = buf;
+
+	if (!(dst_addr[0] & 0x01))
+		return;
+
+	wpa_printf(MSG_EXCESSIVE, "x_snoop: Multicast-to-unicast conversion "
+		   MACSTR " -> " MACSTR " (len %u)",
+		   MAC2STR(dst_addr), MAC2STR(sta->addr), (unsigned int) len);
+
+	/* save the multicast destination address for restoring it later */
+	os_memcpy(addr, buf, ETH_ALEN);
+
+	os_memcpy(buf, sta->addr, ETH_ALEN);
+	res = l2_packet_send(hapd->sock_dhcp, NULL, 0, buf, len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to send mcast to ucast converted packet to "
+			   MACSTR, MAC2STR(sta->addr));
+	}
+
+	/* restore the multicast destination address */
+	os_memcpy(buf, addr, ETH_ALEN);
+}
+
+
+void x_snoop_deinit(struct hostapd_data *hapd)
+{
+	hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
+	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
+	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
+}
diff --git a/hostap/src/ap/x_snoop.h b/hostap/src/ap/x_snoop.h
new file mode 100644
index 0000000..e43a78d
--- /dev/null
+++ b/hostap/src/ap/x_snoop.h
@@ -0,0 +1,56 @@
+/*
+ * Generic Snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef X_SNOOP_H
+#define X_SNOOP_H
+
+#include "l2_packet/l2_packet.h"
+
+#ifdef CONFIG_PROXYARP
+
+int x_snoop_init(struct hostapd_data *hapd);
+struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+		      void (*handler)(void *ctx, const u8 *src_addr,
+				      const u8 *buf, size_t len),
+		      enum l2_packet_filter_type type);
+void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+					 struct sta_info *sta, u8 *buf,
+					 size_t len);
+void x_snoop_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_PROXYARP */
+
+static inline int x_snoop_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+		      void (*handler)(void *ctx, const u8 *src_addr,
+				      const u8 *buf, size_t len),
+		      enum l2_packet_filter_type type)
+{
+	return NULL;
+}
+
+static inline void
+x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+				    struct sta_info *sta, void *buf,
+				    size_t len)
+{
+}
+
+static inline void x_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_PROXYARP */
+
+#endif /* X_SNOOP_H */
diff --git a/hostap/src/common/Makefile b/hostap/src/common/Makefile
new file mode 100644
index 0000000..e703630
--- /dev/null
+++ b/hostap/src/common/Makefile
@@ -0,0 +1,28 @@
+all: libcommon.a
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcommon.a
+
+install:
+	@echo Nothing to be made.
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211W
+CFLAGS += -DCONFIG_HS20
+CFLAGS += -DCONFIG_SAE
+CFLAGS += -DCONFIG_SUITE
+CFLAGS += -DCONFIG_SUITEB
+
+LIB_OBJS= \
+	gas.o \
+	hw_features_common.o \
+	ieee802_11_common.o \
+	sae.o \
+	wpa_common.o
+
+libcommon.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/common/common_module_tests.c b/hostap/src/common/common_module_tests.c
new file mode 100644
index 0000000..d69448b
--- /dev/null
+++ b/hostap/src/common/common_module_tests.c
@@ -0,0 +1,207 @@
+/*
+ * common module tests
+ * Copyright (c) 2014-2015, 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 "ieee802_11_defs.h"
+#include "gas.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 },
+	{ (u8 *) "\x03\x00\x2a\x00\x36\x00\x37\x00\x38\x00\x2d\x00\x3d\x00\xbf\x00\xc0\x00",
+	  18, ParseOK, 9 },
+	{ (u8 *) "\x8b\x00", 2, ParseOK, 1 },
+	{ (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 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;
+}
+
+
+static int gas_tests(void)
+{
+	struct wpabuf *buf;
+
+	wpa_printf(MSG_INFO, "gas tests");
+	gas_anqp_set_len(NULL);
+
+	buf = wpabuf_alloc(1);
+	if (buf == NULL)
+		return -1;
+	gas_anqp_set_len(buf);
+	wpabuf_free(buf);
+
+	buf = wpabuf_alloc(20);
+	if (buf == NULL)
+		return -1;
+	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_REQ);
+	wpabuf_put_u8(buf, 0);
+	wpabuf_put_be32(buf, 0);
+	wpabuf_put_u8(buf, 0);
+	gas_anqp_set_len(buf);
+	wpabuf_free(buf);
+
+	return 0;
+}
+
+
+int common_module_tests(void)
+{
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "common module tests");
+
+	if (ieee802_11_parse_tests() < 0 ||
+	    gas_tests() < 0 ||
+	    rsn_ie_parse_tests() < 0)
+		ret = -1;
+
+	return ret;
+}
diff --git a/hostap/src/common/defs.h b/hostap/src/common/defs.h
new file mode 100644
index 0000000..6aea375
--- /dev/null
+++ b/hostap/src/common/defs.h
@@ -0,0 +1,337 @@
+/*
+ * WPA Supplicant - Common definitions
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DEFS_H
+#define DEFS_H
+
+#ifdef FALSE
+#undef FALSE
+#endif
+#ifdef TRUE
+#undef TRUE
+#endif
+typedef enum { FALSE = 0, TRUE = 1 } Boolean;
+
+
+#define WPA_CIPHER_NONE BIT(0)
+#define WPA_CIPHER_WEP40 BIT(1)
+#define WPA_CIPHER_WEP104 BIT(2)
+#define WPA_CIPHER_TKIP BIT(3)
+#define WPA_CIPHER_CCMP BIT(4)
+#define WPA_CIPHER_AES_128_CMAC BIT(5)
+#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)
+#define WPA_KEY_MGMT_NONE BIT(2)
+#define WPA_KEY_MGMT_IEEE8021X_NO_WPA BIT(3)
+#define WPA_KEY_MGMT_WPA_NONE BIT(4)
+#define WPA_KEY_MGMT_FT_IEEE8021X BIT(5)
+#define WPA_KEY_MGMT_FT_PSK BIT(6)
+#define WPA_KEY_MGMT_IEEE8021X_SHA256 BIT(7)
+#define WPA_KEY_MGMT_PSK_SHA256 BIT(8)
+#define WPA_KEY_MGMT_WPS BIT(9)
+#define WPA_KEY_MGMT_SAE BIT(10)
+#define WPA_KEY_MGMT_FT_SAE BIT(11)
+#define WPA_KEY_MGMT_WAPI_PSK BIT(12)
+#define WPA_KEY_MGMT_WAPI_CERT BIT(13)
+#define WPA_KEY_MGMT_CCKM BIT(14)
+#define WPA_KEY_MGMT_OSEN BIT(15)
+#define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16)
+#define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17)
+
+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 |
+			 WPA_KEY_MGMT_IEEE8021X_SUITE_B |
+			 WPA_KEY_MGMT_IEEE8021X_SUITE_B_192));
+}
+
+static inline int wpa_key_mgmt_wpa_psk(int akm)
+{
+	return !!(akm & (WPA_KEY_MGMT_PSK |
+			 WPA_KEY_MGMT_FT_PSK |
+			 WPA_KEY_MGMT_PSK_SHA256 |
+			 WPA_KEY_MGMT_SAE |
+			 WPA_KEY_MGMT_FT_SAE));
+}
+
+static inline int wpa_key_mgmt_ft(int akm)
+{
+	return !!(akm & (WPA_KEY_MGMT_FT_PSK |
+			 WPA_KEY_MGMT_FT_IEEE8021X |
+			 WPA_KEY_MGMT_FT_SAE));
+}
+
+static inline int wpa_key_mgmt_sae(int akm)
+{
+	return !!(akm & (WPA_KEY_MGMT_SAE |
+			 WPA_KEY_MGMT_FT_SAE));
+}
+
+static inline int wpa_key_mgmt_sha256(int akm)
+{
+	return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
+			 WPA_KEY_MGMT_IEEE8021X_SHA256 |
+			 WPA_KEY_MGMT_OSEN |
+			 WPA_KEY_MGMT_IEEE8021X_SUITE_B));
+}
+
+static inline int wpa_key_mgmt_sha384(int akm)
+{
+	return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192);
+}
+
+static inline int wpa_key_mgmt_suite_b(int akm)
+{
+	return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B |
+			 WPA_KEY_MGMT_IEEE8021X_SUITE_B_192));
+}
+
+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_sae(akm);
+}
+
+static inline int wpa_key_mgmt_wpa_any(int akm)
+{
+	return wpa_key_mgmt_wpa(akm) || (akm & WPA_KEY_MGMT_WPA_NONE);
+}
+
+static inline int wpa_key_mgmt_cckm(int akm)
+{
+	return akm == WPA_KEY_MGMT_CCKM;
+}
+
+
+#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)
+#define WPA_AUTH_ALG_LEAP BIT(2)
+#define WPA_AUTH_ALG_FT BIT(3)
+#define WPA_AUTH_ALG_SAE BIT(4)
+
+
+enum wpa_alg {
+	WPA_ALG_NONE,
+	WPA_ALG_WEP,
+	WPA_ALG_TKIP,
+	WPA_ALG_CCMP,
+	WPA_ALG_IGTK,
+	WPA_ALG_PMK,
+	WPA_ALG_GCMP,
+	WPA_ALG_SMS4,
+	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
+};
+
+/**
+ * enum wpa_states - wpa_supplicant state
+ *
+ * These enumeration values are used to indicate the current wpa_supplicant
+ * state (wpa_s->wpa_state). The current state can be retrieved with
+ * wpa_supplicant_get_state() function and the state can be changed by calling
+ * wpa_supplicant_set_state(). In WPA state machine (wpa.c and preauth.c), the
+ * wrapper functions wpa_sm_get_state() and wpa_sm_set_state() should be used
+ * to access the state variable.
+ */
+enum wpa_states {
+	/**
+	 * WPA_DISCONNECTED - Disconnected state
+	 *
+	 * This state indicates that client is not associated, but is likely to
+	 * start looking for an access point. This state is entered when a
+	 * connection is lost.
+	 */
+	WPA_DISCONNECTED,
+
+	/**
+	 * WPA_INTERFACE_DISABLED - Interface disabled
+	 *
+	 * This state is entered if the network interface is disabled, e.g.,
+	 * due to rfkill. wpa_supplicant refuses any new operations that would
+	 * use the radio until the interface has been enabled.
+	 */
+	WPA_INTERFACE_DISABLED,
+
+	/**
+	 * WPA_INACTIVE - Inactive state (wpa_supplicant disabled)
+	 *
+	 * This state is entered if there are no enabled networks in the
+	 * configuration. wpa_supplicant is not trying to associate with a new
+	 * network and external interaction (e.g., ctrl_iface call to add or
+	 * enable a network) is needed to start association.
+	 */
+	WPA_INACTIVE,
+
+	/**
+	 * WPA_SCANNING - Scanning for a network
+	 *
+	 * This state is entered when wpa_supplicant starts scanning for a
+	 * network.
+	 */
+	WPA_SCANNING,
+
+	/**
+	 * WPA_AUTHENTICATING - Trying to authenticate with a BSS/SSID
+	 *
+	 * This state is entered when wpa_supplicant has found a suitable BSS
+	 * to authenticate with and the driver is configured to try to
+	 * authenticate with this BSS. This state is used only with drivers
+	 * that use wpa_supplicant as the SME.
+	 */
+	WPA_AUTHENTICATING,
+
+	/**
+	 * WPA_ASSOCIATING - Trying to associate with a BSS/SSID
+	 *
+	 * This state is entered when wpa_supplicant has found a suitable BSS
+	 * to associate with and the driver is configured to try to associate
+	 * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this
+	 * state is entered when the driver is configured to try to associate
+	 * with a network using the configured SSID and security policy.
+	 */
+	WPA_ASSOCIATING,
+
+	/**
+	 * WPA_ASSOCIATED - Association completed
+	 *
+	 * This state is entered when the driver reports that association has
+	 * been successfully completed with an AP. If IEEE 802.1X is used
+	 * (with or without WPA/WPA2), wpa_supplicant remains in this state
+	 * until the IEEE 802.1X/EAPOL authentication has been completed.
+	 */
+	WPA_ASSOCIATED,
+
+	/**
+	 * WPA_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress
+	 *
+	 * This state is entered when WPA/WPA2 4-Way Handshake is started. In
+	 * case of WPA-PSK, this happens when receiving the first EAPOL-Key
+	 * frame after association. In case of WPA-EAP, this state is entered
+	 * when the IEEE 802.1X/EAPOL authentication has been completed.
+	 */
+	WPA_4WAY_HANDSHAKE,
+
+	/**
+	 * WPA_GROUP_HANDSHAKE - WPA Group Key Handshake in progress
+	 *
+	 * This state is entered when 4-Way Key Handshake has been completed
+	 * (i.e., when the supplicant sends out message 4/4) and when Group
+	 * Key rekeying is started by the AP (i.e., when supplicant receives
+	 * message 1/2).
+	 */
+	WPA_GROUP_HANDSHAKE,
+
+	/**
+	 * WPA_COMPLETED - All authentication completed
+	 *
+	 * This state is entered when the full authentication process is
+	 * completed. In case of WPA2, this happens when the 4-Way Handshake is
+	 * successfully completed. With WPA, this state is entered after the
+	 * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is
+	 * completed after dynamic keys are received (or if not used, after
+	 * the EAP authentication has been completed). With static WEP keys and
+	 * plaintext connections, this state is entered when an association
+	 * has been completed.
+	 *
+	 * This state indicates that the supplicant has completed its
+	 * processing for the association phase and that data connection is
+	 * fully configured.
+	 */
+	WPA_COMPLETED
+};
+
+#define MLME_SETPROTECTION_PROTECT_TYPE_NONE 0
+#define MLME_SETPROTECTION_PROTECT_TYPE_RX 1
+#define MLME_SETPROTECTION_PROTECT_TYPE_TX 2
+#define MLME_SETPROTECTION_PROTECT_TYPE_RX_TX 3
+
+#define MLME_SETPROTECTION_KEY_TYPE_GROUP 0
+#define MLME_SETPROTECTION_KEY_TYPE_PAIRWISE 1
+
+
+/**
+ * enum mfp_options - Management frame protection (IEEE 802.11w) options
+ */
+enum mfp_options {
+	NO_MGMT_FRAME_PROTECTION = 0,
+	MGMT_FRAME_PROTECTION_OPTIONAL = 1,
+	MGMT_FRAME_PROTECTION_REQUIRED = 2,
+};
+#define MGMT_FRAME_PROTECTION_DEFAULT 3
+
+/**
+ * enum hostapd_hw_mode - Hardware mode
+ */
+enum hostapd_hw_mode {
+	HOSTAPD_MODE_IEEE80211B,
+	HOSTAPD_MODE_IEEE80211G,
+	HOSTAPD_MODE_IEEE80211A,
+	HOSTAPD_MODE_IEEE80211AD,
+	HOSTAPD_MODE_IEEE80211ANY,
+	NUM_HOSTAPD_MODES
+};
+
+/**
+ * enum wpa_ctrl_req_type - Control interface request types
+ */
+enum wpa_ctrl_req_type {
+	WPA_CTRL_REQ_UNKNOWN,
+	WPA_CTRL_REQ_EAP_IDENTITY,
+	WPA_CTRL_REQ_EAP_PASSWORD,
+	WPA_CTRL_REQ_EAP_NEW_PASSWORD,
+	WPA_CTRL_REQ_EAP_PIN,
+	WPA_CTRL_REQ_EAP_OTP,
+	WPA_CTRL_REQ_EAP_PASSPHRASE,
+	WPA_CTRL_REQ_SIM,
+	WPA_CTRL_REQ_PSK_PASSPHRASE,
+	NUM_WPA_CTRL_REQS
+};
+
+/* Maximum number of EAP methods to store for EAP server user information */
+#define EAP_MAX_METHODS 8
+
+enum mesh_plink_state {
+	PLINK_LISTEN = 1,
+	PLINK_OPEN_SENT,
+	PLINK_OPEN_RCVD,
+	PLINK_CNF_RCVD,
+	PLINK_ESTAB,
+	PLINK_HOLDING,
+	PLINK_BLOCKED,
+};
+
+enum set_band {
+	WPA_SETBAND_AUTO,
+	WPA_SETBAND_5G,
+	WPA_SETBAND_2G
+};
+
+#endif /* DEFS_H */
diff --git a/hostap/src/common/eapol_common.h b/hostap/src/common/eapol_common.h
new file mode 100644
index 0000000..6958661
--- /dev/null
+++ b/hostap/src/common/eapol_common.h
@@ -0,0 +1,92 @@
+/*
+ * EAPOL definitions shared between hostapd and wpa_supplicant
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAPOL_COMMON_H
+#define EAPOL_COMMON_H
+
+/* IEEE Std 802.1X-2004 */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee802_1x_hdr {
+	u8 version;
+	u8 type;
+	be16 length;
+	/* 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_MKA = 5,
+};
+
+enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2,
+       EAPOL_KEY_TYPE_WPA = 254 };
+
+
+#define IEEE8021X_REPLAY_COUNTER_LEN 8
+#define IEEE8021X_KEY_SIGN_LEN 16
+#define IEEE8021X_KEY_IV_LEN 16
+
+#define IEEE8021X_KEY_INDEX_FLAG 0x80
+#define IEEE8021X_KEY_INDEX_MASK 0x03
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee802_1x_eapol_key {
+	u8 type;
+	/* Note: key_length is unaligned */
+	u8 key_length[2];
+	/* does not repeat within the life of the keying material used to
+	 * encrypt the Key field; 64-bit NTP timestamp MAY be used here */
+	u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN];
+	u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */
+	u8 key_index; /* key flag in the most significant bit:
+		       * 0 = broadcast (default key),
+		       * 1 = unicast (key mapping key); key index is in the
+		       * 7 least significant bits */
+	/* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as
+	 * the key */
+	u8 key_signature[IEEE8021X_KEY_SIGN_LEN];
+
+	/* followed by key: if packet body length = 44 + key length, then the
+	 * key field (of key_length bytes) contains the key in encrypted form;
+	 * if packet body length = 44, key field is absent and key_length
+	 * represents the number of least significant octets from
+	 * MS-MPPE-Send-Key attribute to be used as the keying material;
+	 * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#endif /* EAPOL_COMMON_H */
diff --git a/hostap/src/common/gas.c b/hostap/src/common/gas.c
new file mode 100644
index 0000000..cff9254
--- /dev/null
+++ b/hostap/src/common/gas.c
@@ -0,0 +1,273 @@
+/*
+ * Generic advertisement service (GAS) (IEEE 802.11u)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2012, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "ieee802_11_defs.h"
+#include "gas.h"
+
+
+static struct wpabuf *
+gas_build_req(u8 action, u8 dialog_token, size_t size)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(100 + size);
+	if (buf == NULL)
+		return NULL;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(buf, action);
+	wpabuf_put_u8(buf, dialog_token);
+
+	return buf;
+}
+
+
+struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size)
+{
+	return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token,
+			     size);
+}
+
+
+struct wpabuf * gas_build_comeback_req(u8 dialog_token)
+{
+	return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0);
+}
+
+
+static struct wpabuf *
+gas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id,
+	       u8 more, u16 comeback_delay, size_t size)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(100 + size);
+	if (buf == NULL)
+		return NULL;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(buf, action);
+	wpabuf_put_u8(buf, dialog_token);
+	wpabuf_put_le16(buf, status_code);
+	if (action == WLAN_PA_GAS_COMEBACK_RESP)
+		wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0));
+	wpabuf_put_le16(buf, comeback_delay);
+
+	return buf;
+}
+
+
+struct wpabuf *
+gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay,
+		       size_t size)
+{
+	return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token,
+			      status_code, 0, 0, comeback_delay, size);
+}
+
+
+static struct wpabuf *
+gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
+			u16 comeback_delay, size_t size)
+{
+	return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token,
+			      status_code, frag_id, more, comeback_delay,
+			      size);
+}
+
+
+/**
+ * gas_add_adv_proto_anqp - Add an Advertisement Protocol element
+ * @buf: Buffer to which the element is added
+ * @query_resp_len_limit: Query Response Length Limit in units of 256 octets
+ * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1)
+ *
+ *
+ * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means
+ * that the maximum limit is determined by the maximum allowable number of
+ * fragments in the GAS Query Response Fragment ID.
+ */
+static void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit,
+				   u8 pame_bi)
+{
+	/* Advertisement Protocol IE */
+	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(buf, 2); /* Length */
+	wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) |
+		      (pame_bi ? 0x80 : 0));
+	/* Advertisement Protocol */
+	wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL);
+}
+
+
+struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size)
+{
+	struct wpabuf *buf;
+
+	buf = gas_build_initial_req(dialog_token, 4 + size);
+	if (buf == NULL)
+		return NULL;
+
+	gas_add_adv_proto_anqp(buf, 0, 0);
+
+	wpabuf_put(buf, 2); /* Query Request Length to be filled */
+
+	return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
+					    u16 comeback_delay, size_t size)
+{
+	struct wpabuf *buf;
+
+	buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay,
+				     4 + size);
+	if (buf == NULL)
+		return NULL;
+
+	gas_add_adv_proto_anqp(buf, 0x7f, 0);
+
+	wpabuf_put(buf, 2); /* Query Response Length to be filled */
+
+	return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token,
+						u16 status_code,
+						u16 comeback_delay,
+						struct wpabuf *payload)
+{
+	struct wpabuf *buf;
+
+	buf = gas_anqp_build_initial_resp(dialog_token, status_code,
+					  comeback_delay,
+					  payload ? wpabuf_len(payload) : 0);
+	if (buf == NULL)
+		return NULL;
+
+	if (payload)
+		wpabuf_put_buf(buf, payload);
+
+	gas_anqp_set_len(buf);
+
+	return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code,
+					     u8 frag_id, u8 more,
+					     u16 comeback_delay, size_t size)
+{
+	struct wpabuf *buf;
+
+	buf = gas_build_comeback_resp(dialog_token, status_code,
+				      frag_id, more, comeback_delay, 4 + size);
+	if (buf == NULL)
+		return NULL;
+
+	gas_add_adv_proto_anqp(buf, 0x7f, 0);
+
+	wpabuf_put(buf, 2); /* Query Response Length to be filled */
+
+	return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token,
+						 u16 status_code,
+						 u8 frag_id, u8 more,
+						 u16 comeback_delay,
+						 struct wpabuf *payload)
+{
+	struct wpabuf *buf;
+
+	buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id,
+					   more, comeback_delay,
+					   payload ? wpabuf_len(payload) : 0);
+	if (buf == NULL)
+		return NULL;
+
+	if (payload)
+		wpabuf_put_buf(buf, payload);
+
+	gas_anqp_set_len(buf);
+
+	return buf;
+}
+
+
+/**
+ * gas_anqp_set_len - Set Query Request/Response Length
+ * @buf: GAS message
+ *
+ * This function is used to update the Query Request/Response Length field once
+ * the payload has been filled.
+ */
+void gas_anqp_set_len(struct wpabuf *buf)
+{
+	u8 action;
+	size_t offset;
+	u8 *len;
+
+	if (buf == NULL || wpabuf_len(buf) < 2)
+		return;
+
+	action = *(wpabuf_head_u8(buf) + 1);
+	switch (action) {
+	case WLAN_PA_GAS_INITIAL_REQ:
+		offset = 3 + 4;
+		break;
+	case WLAN_PA_GAS_INITIAL_RESP:
+		offset = 7 + 4;
+		break;
+	case WLAN_PA_GAS_COMEBACK_RESP:
+		offset = 8 + 4;
+		break;
+	default:
+		return;
+	}
+
+	if (wpabuf_len(buf) < offset + 2)
+		return;
+
+	len = wpabuf_mhead_u8(buf) + offset;
+	WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+/**
+ * gas_anqp_add_element - Add ANQP element header
+ * @buf: GAS message
+ * @info_id: ANQP Info ID
+ * Returns: Pointer to the Length field for gas_anqp_set_element_len()
+ */
+u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id)
+{
+	wpabuf_put_le16(buf, info_id);
+	return wpabuf_put(buf, 2); /* Length to be filled */
+}
+
+
+/**
+ * gas_anqp_set_element_len - Update ANQP element Length field
+ * @buf: GAS message
+ * @len_pos: Length field position from gas_anqp_add_element()
+ *
+ * This function is called after the ANQP element payload has been added to the
+ * buffer.
+ */
+void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos)
+{
+	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
+}
diff --git a/hostap/src/common/gas.h b/hostap/src/common/gas.h
new file mode 100644
index 0000000..306adc5
--- /dev/null
+++ b/hostap/src/common/gas.h
@@ -0,0 +1,37 @@
+/*
+ * Generic advertisement service (GAS) (IEEE 802.11u)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2012, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_H
+#define GAS_H
+
+struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size);
+struct wpabuf * gas_build_comeback_req(u8 dialog_token);
+struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code,
+				       u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size);
+struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
+					    u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token,
+						u16 status_code,
+						u16 comeback_delay,
+						struct wpabuf *payload);
+struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code,
+					     u8 frag_id, u8 more,
+					     u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token,
+						 u16 status_code,
+						 u8 frag_id, u8 more,
+						 u16 comeback_delay,
+						 struct wpabuf *payload);
+void gas_anqp_set_len(struct wpabuf *buf);
+
+u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id);
+void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos);
+
+#endif /* GAS_H */
diff --git a/hostap/src/common/hw_features_common.c b/hostap/src/common/hw_features_common.c
new file mode 100644
index 0000000..9c37ea6
--- /dev/null
+++ b/hostap/src/common/hw_features_common.c
@@ -0,0 +1,455 @@
+/*
+ * Common hostapd/wpa_supplicant HW features
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015, 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 "defs.h"
+#include "ieee802_11_defs.h"
+#include "ieee802_11_common.h"
+#include "hw_features_common.h"
+
+
+struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
+						  int chan, int *freq)
+{
+	int i;
+
+	if (freq)
+		*freq = 0;
+
+	if (!mode)
+		return NULL;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *ch = &mode->channels[i];
+		if (ch->chan == chan) {
+			if (freq)
+				*freq = ch->freq;
+			return ch;
+		}
+	}
+
+	return NULL;
+}
+
+
+struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
+						  int freq, int *chan)
+{
+	int i;
+
+	if (chan)
+		*chan = 0;
+
+	if (!mode)
+		return NULL;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *ch = &mode->channels[i];
+		if (ch->freq == freq) {
+			if (chan)
+				*chan = ch->chan;
+			return ch;
+		}
+	}
+
+	return NULL;
+}
+
+
+int hw_get_freq(struct hostapd_hw_modes *mode, int chan)
+{
+	int freq;
+
+	hw_get_channel_chan(mode, chan, &freq);
+
+	return freq;
+}
+
+
+int hw_get_chan(struct hostapd_hw_modes *mode, int freq)
+{
+	int chan;
+
+	hw_get_channel_freq(mode, freq, &chan);
+
+	return chan;
+}
+
+
+int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
+			      int sec_chan)
+{
+	int ok, j, first;
+	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
+			  149, 157, 184, 192 };
+	size_t k;
+
+	if (pri_chan == sec_chan || !sec_chan)
+		return 1; /* HT40 not used */
+
+	wpa_printf(MSG_DEBUG,
+		   "HT40: control channel: %d  secondary channel: %d",
+		   pri_chan, sec_chan);
+
+	/* Verify that HT40 secondary channel is an allowed 20 MHz
+	 * channel */
+	ok = 0;
+	for (j = 0; j < mode->num_channels; j++) {
+		struct hostapd_channel_data *chan = &mode->channels[j];
+		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		    chan->chan == sec_chan) {
+			ok = 1;
+			break;
+		}
+	}
+	if (!ok) {
+		wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
+			   sec_chan);
+		return 0;
+	}
+
+	/*
+	 * Verify that HT40 primary,secondary channel pair is allowed per
+	 * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
+	 * 2.4 GHz rules allow all cases where the secondary channel fits into
+	 * the list of allowed channels (already checked above).
+	 */
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return 1;
+
+	first = pri_chan < sec_chan ? pri_chan : sec_chan;
+
+	ok = 0;
+	for (k = 0; k < ARRAY_SIZE(allowed); k++) {
+		if (first == allowed[k]) {
+			ok = 1;
+			break;
+		}
+	}
+	if (!ok) {
+		wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
+			   pri_chan, sec_chan);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan)
+{
+	struct ieee80211_ht_operation *oper;
+	struct ieee802_11_elems elems;
+
+	*pri_chan = *sec_chan = 0;
+
+	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+	if (elems.ht_operation) {
+		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+		*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)
+				*sec_chan = *pri_chan + 4;
+			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+				*sec_chan = *pri_chan - 4;
+		}
+	}
+}
+
+
+int check_40mhz_5g(struct hostapd_hw_modes *mode,
+		   struct wpa_scan_results *scan_res, int pri_chan,
+		   int sec_chan)
+{
+	int pri_freq, sec_freq, pri_bss, sec_bss;
+	int bss_pri_chan, bss_sec_chan;
+	size_t i;
+	int match;
+
+	if (!mode || !scan_res || !pri_chan || !sec_chan ||
+	    pri_chan == sec_chan)
+		return 0;
+
+	pri_freq = hw_get_freq(mode, pri_chan);
+	sec_freq = hw_get_freq(mode, sec_chan);
+
+	/*
+	 * Switch PRI/SEC channels if Beacons were detected on selected SEC
+	 * channel, but not on selected PRI channel.
+	 */
+	pri_bss = sec_bss = 0;
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		if (bss->freq == pri_freq)
+			pri_bss++;
+		else if (bss->freq == sec_freq)
+			sec_bss++;
+	}
+	if (sec_bss && !pri_bss) {
+		wpa_printf(MSG_INFO,
+			   "Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes");
+		return 2;
+	}
+
+	/*
+	 * Match PRI/SEC channel with any existing HT40 BSS on the same
+	 * channels that we are about to use (if already mixed order in
+	 * existing BSSes, use own preference).
+	 */
+	match = 0;
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
+		if (pri_chan == bss_pri_chan &&
+		    sec_chan == bss_sec_chan) {
+			match = 1;
+			break;
+		}
+	}
+	if (!match) {
+		for (i = 0; i < scan_res->num; i++) {
+			struct wpa_scan_res *bss = scan_res->res[i];
+			get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
+			if (pri_chan == bss_sec_chan &&
+			    sec_chan == bss_pri_chan) {
+				wpa_printf(MSG_INFO, "Switch own primary and "
+					   "secondary channel due to BSS "
+					   "overlap with " MACSTR,
+					   MAC2STR(bss->bssid));
+				return 2;
+			}
+		}
+	}
+
+	return 1;
+}
+
+
+static int 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) {
+		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;
+}
+
+
+int check_40mhz_2g4(struct hostapd_hw_modes *mode,
+		    struct wpa_scan_results *scan_res, int pri_chan,
+		    int sec_chan)
+{
+	int pri_freq, sec_freq;
+	int affected_start, affected_end;
+	size_t i;
+
+	if (!mode || !scan_res || !pri_chan || !sec_chan ||
+	    pri_chan == sec_chan)
+		return 0;
+
+	pri_freq = hw_get_freq(mode, pri_chan);
+	sec_freq = hw_get_freq(mode, sec_chan);
+
+	affected_start = (pri_freq + sec_freq) / 2 - 25;
+	affected_end = (pri_freq + sec_freq) / 2 + 25;
+	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+		   affected_start, affected_end);
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		int pri = bss->freq;
+		int sec = pri;
+		struct ieee802_11_elems elems;
+
+		/* Check for overlapping 20 MHz BSS */
+		if (check_20mhz_bss(bss, pri_freq, affected_start,
+				    affected_end)) {
+			wpa_printf(MSG_DEBUG,
+				   "Overlapping 20 MHz BSS is found");
+			return 0;
+		}
+
+		get_pri_sec_chan(bss, &pri_chan, &sec_chan);
+
+		if (sec_chan) {
+			if (sec_chan < pri_chan)
+				sec = pri - 20;
+			else
+				sec = pri + 20;
+		}
+
+		if ((pri < affected_start || pri > affected_end) &&
+		    (sec < affected_start || sec > affected_end))
+			continue; /* not within affected channel range */
+
+		wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
+			   " freq=%d pri=%d sec=%d",
+			   MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
+
+		if (sec_chan) {
+			if (pri_freq != pri || sec_freq != sec) {
+				wpa_printf(MSG_DEBUG,
+					   "40 MHz pri/sec mismatch with BSS "
+					   MACSTR
+					   " <%d,%d> (chan=%d%c) vs. <%d,%d>",
+					   MAC2STR(bss->bssid),
+					   pri, sec, pri_chan,
+					   sec > pri ? '+' : '-',
+					   pri_freq, sec_freq);
+				return 0;
+			}
+		}
+
+		ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
+				       0);
+		if (elems.ht_capabilities) {
+			struct ieee80211_ht_capabilities *ht_cap =
+				(struct ieee80211_ht_capabilities *)
+				elems.ht_capabilities;
+
+			if (le_to_host16(ht_cap->ht_capabilities_info) &
+			    HT_CAP_INFO_40MHZ_INTOLERANT) {
+				wpa_printf(MSG_DEBUG,
+					   "40 MHz Intolerant is set on channel %d in BSS "
+					   MACSTR, pri, MAC2STR(bss->bssid));
+				return 0;
+			}
+		}
+	}
+
+	return 1;
+}
+
+
+int hostapd_set_freq_params(struct hostapd_freq_params *data,
+			    enum hostapd_hw_mode mode,
+			    int freq, int channel, int ht_enabled,
+			    int vht_enabled, int sec_channel_offset,
+			    int vht_oper_chwidth, int center_segment0,
+			    int center_segment1, u32 vht_caps)
+{
+	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;
+
+	if (data->vht_enabled) switch (vht_oper_chwidth) {
+	case VHT_CHANWIDTH_USE_HT:
+		if (center_segment1 ||
+		    (center_segment0 != 0 &&
+		     5000 + center_segment0 * 5 != data->center_freq1 &&
+		     2407 + center_segment0 * 5 != data->center_freq1))
+			return -1;
+		break;
+	case VHT_CHANWIDTH_80P80MHZ:
+		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;
+		/* fall through */
+	case VHT_CHANWIDTH_80MHZ:
+		data->bandwidth = 80;
+		if ((vht_oper_chwidth == 1 && center_segment1) ||
+		    (vht_oper_chwidth == 3 && !center_segment1) ||
+		    !sec_channel_offset)
+			return -1;
+		if (!center_segment0) {
+			if (channel <= 48)
+				center_segment0 = 42;
+			else if (channel <= 64)
+				center_segment0 = 58;
+			else if (channel <= 112)
+				center_segment0 = 106;
+			else if (channel <= 128)
+				center_segment0 = 122;
+			else if (channel <= 144)
+				center_segment0 = 138;
+			else if (channel <= 161)
+				center_segment0 = 155;
+			data->center_freq1 = 5000 + center_segment0 * 5;
+		} else {
+			/*
+			 * Note: HT/VHT config and params are coupled. Check if
+			 * HT40 channel band is in VHT80 Pri channel band
+			 * configuration.
+			 */
+			if (center_segment0 == channel + 6 ||
+			    center_segment0 == channel + 2 ||
+			    center_segment0 == channel - 2 ||
+			    center_segment0 == channel - 6)
+				data->center_freq1 = 5000 + center_segment0 * 5;
+			else
+				return -1;
+		}
+		break;
+	case VHT_CHANWIDTH_160MHZ:
+		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)
+			return -1;
+		/*
+		 * Note: HT/VHT config and params are coupled. Check if
+		 * HT40 channel band is in VHT160 channel band configuration.
+		 */
+		if (center_segment0 == channel + 14 ||
+		    center_segment0 == channel + 10 ||
+		    center_segment0 == channel + 6 ||
+		    center_segment0 == channel + 2 ||
+		    center_segment0 == channel - 2 ||
+		    center_segment0 == channel - 6 ||
+		    center_segment0 == channel - 10 ||
+		    center_segment0 == channel - 14)
+			data->center_freq1 = 5000 + center_segment0 * 5;
+		else
+			return -1;
+		break;
+	}
+
+	return 0;
+}
diff --git a/hostap/src/common/hw_features_common.h b/hostap/src/common/hw_features_common.h
new file mode 100644
index 0000000..7360b4e
--- /dev/null
+++ b/hostap/src/common/hw_features_common.h
@@ -0,0 +1,39 @@
+/*
+ * Common hostapd/wpa_supplicant HW features
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HW_FEATURES_COMMON_H
+#define HW_FEATURES_COMMON_H
+
+#include "drivers/driver.h"
+
+struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
+						  int chan, int *freq);
+struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
+						  int freq, int *chan);
+
+int hw_get_freq(struct hostapd_hw_modes *mode, int chan);
+int hw_get_chan(struct hostapd_hw_modes *mode, int freq);
+
+int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
+			      int sec_chan);
+void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan);
+int check_40mhz_5g(struct hostapd_hw_modes *mode,
+		   struct wpa_scan_results *scan_res, int pri_chan,
+		   int sec_chan);
+int check_40mhz_2g4(struct hostapd_hw_modes *mode,
+		    struct wpa_scan_results *scan_res, int pri_chan,
+		    int sec_chan);
+int hostapd_set_freq_params(struct hostapd_freq_params *data,
+			    enum hostapd_hw_mode mode,
+			    int freq, int channel, int ht_enabled,
+			    int vht_enabled, int sec_channel_offset,
+			    int vht_oper_chwidth, int center_segment0,
+			    int center_segment1, u32 vht_caps);
+
+#endif /* HW_FEATURES_COMMON_H */
diff --git a/hostap/src/common/ieee802_11_common.c b/hostap/src/common/ieee802_11_common.c
new file mode 100644
index 0000000..d07a316
--- /dev/null
+++ b/hostap/src/common/ieee802_11_common.c
@@ -0,0 +1,1147 @@
+/*
+ * IEEE 802.11 Common routines
+ * Copyright (c) 2002-2015, 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 "defs.h"
+#include "wpa_common.h"
+#include "qca-vendor.h"
+#include "ieee802_11_defs.h"
+#include "ieee802_11_common.h"
+
+
+static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
+					    struct ieee802_11_elems *elems,
+					    int show_errors)
+{
+	unsigned int oui;
+
+	/* first 3 bytes in vendor specific information element are the IEEE
+	 * OUI of the vendor. The following byte is used a vendor specific
+	 * sub-type. */
+	if (elen < 4) {
+		if (show_errors) {
+			wpa_printf(MSG_MSGDUMP, "short vendor specific "
+				   "information element ignored (len=%lu)",
+				   (unsigned long) elen);
+		}
+		return -1;
+	}
+
+	oui = WPA_GET_BE24(pos);
+	switch (oui) {
+	case OUI_MICROSOFT:
+		/* Microsoft/Wi-Fi information elements are further typed and
+		 * subtyped */
+		switch (pos[3]) {
+		case 1:
+			/* Microsoft OUI (00:50:F2) with OUI Type 1:
+			 * real WPA information element */
+			elems->wpa_ie = pos;
+			elems->wpa_ie_len = elen;
+			break;
+		case WMM_OUI_TYPE:
+			/* WMM information element */
+			if (elen < 5) {
+				wpa_printf(MSG_MSGDUMP, "short WMM "
+					   "information element ignored "
+					   "(len=%lu)",
+					   (unsigned long) elen);
+				return -1;
+			}
+			switch (pos[4]) {
+			case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
+			case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
+				/*
+				 * Share same pointer since only one of these
+				 * is used and they start with same data.
+				 * Length field can be used to distinguish the
+				 * IEs.
+				 */
+				elems->wmm = pos;
+				elems->wmm_len = elen;
+				break;
+			case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
+				elems->wmm_tspec = pos;
+				elems->wmm_tspec_len = elen;
+				break;
+			default:
+				wpa_printf(MSG_EXCESSIVE, "unknown WMM "
+					   "information element ignored "
+					   "(subtype=%d len=%lu)",
+					   pos[4], (unsigned long) elen);
+				return -1;
+			}
+			break;
+		case 4:
+			/* Wi-Fi Protected Setup (WPS) IE */
+			elems->wps_ie = pos;
+			elems->wps_ie_len = elen;
+			break;
+		default:
+			wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
+				   "information element ignored "
+				   "(type=%d len=%lu)",
+				   pos[3], (unsigned long) elen);
+			return -1;
+		}
+		break;
+
+	case OUI_WFA:
+		switch (pos[3]) {
+		case P2P_OUI_TYPE:
+			/* Wi-Fi Alliance - P2P IE */
+			elems->p2p = pos;
+			elems->p2p_len = elen;
+			break;
+		case WFD_OUI_TYPE:
+			/* Wi-Fi Alliance - WFD IE */
+			elems->wfd = pos;
+			elems->wfd_len = elen;
+			break;
+		case HS20_INDICATION_OUI_TYPE:
+			/* Hotspot 2.0 */
+			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)",
+				   pos[3], (unsigned long) elen);
+			return -1;
+		}
+		break;
+
+	case OUI_BROADCOM:
+		switch (pos[3]) {
+		case VENDOR_HT_CAPAB_OUI_TYPE:
+			elems->vendor_ht_cap = pos;
+			elems->vendor_ht_cap_len = elen;
+			break;
+		case VENDOR_VHT_TYPE:
+			if (elen > 4 &&
+			    (pos[4] == VENDOR_VHT_SUBTYPE ||
+			     pos[4] == VENDOR_VHT_SUBTYPE2)) {
+				elems->vendor_vht = pos;
+				elems->vendor_vht_len = elen;
+			} else
+				return -1;
+			break;
+		default:
+			wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
+				   "information element ignored "
+				   "(type=%d len=%lu)",
+				   pos[3], (unsigned long) elen);
+			return -1;
+		}
+		break;
+
+	case OUI_QCA:
+		switch (pos[3]) {
+		case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST:
+			elems->pref_freq_list = pos;
+			elems->pref_freq_list_len = elen;
+			break;
+		default:
+			wpa_printf(MSG_EXCESSIVE,
+				   "Unknown QCA information element ignored (type=%d len=%lu)",
+				   pos[3], (unsigned long) elen);
+			return -1;
+		}
+		break;
+
+	default:
+		wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
+			   "information element ignored (vendor OUI "
+			   "%02x:%02x:%02x len=%lu)",
+			   pos[0], pos[1], pos[2], (unsigned long) elen);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * ieee802_11_parse_elems - Parse information elements in management frames
+ * @start: Pointer to the start of IEs
+ * @len: Length of IE buffer in octets
+ * @elems: Data structure for parsed elements
+ * @show_errors: Whether to show parsing errors in debug log
+ * Returns: Parsing result
+ */
+ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
+				struct ieee802_11_elems *elems,
+				int show_errors)
+{
+	size_t left = len;
+	const u8 *pos = start;
+	int unknown = 0;
+
+	os_memset(elems, 0, sizeof(*elems));
+
+	while (left >= 2) {
+		u8 id, elen;
+
+		id = *pos++;
+		elen = *pos++;
+		left -= 2;
+
+		if (elen > left) {
+			if (show_errors) {
+				wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
+					   "parse failed (id=%d elen=%d "
+					   "left=%lu)",
+					   id, elen, (unsigned long) left);
+				wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
+			}
+			return ParseFailed;
+		}
+
+		switch (id) {
+		case WLAN_EID_SSID:
+			if (elen > SSID_MAX_LEN) {
+				wpa_printf(MSG_DEBUG,
+					   "Ignored too long SSID element (elen=%u)",
+					   elen);
+				break;
+			}
+			elems->ssid = pos;
+			elems->ssid_len = elen;
+			break;
+		case WLAN_EID_SUPP_RATES:
+			elems->supp_rates = pos;
+			elems->supp_rates_len = elen;
+			break;
+		case WLAN_EID_DS_PARAMS:
+			if (elen < 1)
+				break;
+			elems->ds_params = pos;
+			break;
+		case WLAN_EID_CF_PARAMS:
+		case WLAN_EID_TIM:
+			break;
+		case WLAN_EID_CHALLENGE:
+			elems->challenge = pos;
+			elems->challenge_len = elen;
+			break;
+		case WLAN_EID_ERP_INFO:
+			if (elen < 1)
+				break;
+			elems->erp_info = pos;
+			break;
+		case WLAN_EID_EXT_SUPP_RATES:
+			elems->ext_supp_rates = pos;
+			elems->ext_supp_rates_len = elen;
+			break;
+		case WLAN_EID_VENDOR_SPECIFIC:
+			if (ieee802_11_parse_vendor_specific(pos, elen,
+							     elems,
+							     show_errors))
+				unknown++;
+			break;
+		case WLAN_EID_RSN:
+			elems->rsn_ie = pos;
+			elems->rsn_ie_len = elen;
+			break;
+		case WLAN_EID_PWR_CAPABILITY:
+			break;
+		case WLAN_EID_SUPPORTED_CHANNELS:
+			elems->supp_channels = pos;
+			elems->supp_channels_len = elen;
+			break;
+		case WLAN_EID_MOBILITY_DOMAIN:
+			if (elen < sizeof(struct rsn_mdie))
+				break;
+			elems->mdie = pos;
+			elems->mdie_len = elen;
+			break;
+		case WLAN_EID_FAST_BSS_TRANSITION:
+			if (elen < sizeof(struct rsn_ftie))
+				break;
+			elems->ftie = pos;
+			elems->ftie_len = elen;
+			break;
+		case WLAN_EID_TIMEOUT_INTERVAL:
+			if (elen != 5)
+				break;
+			elems->timeout_int = pos;
+			break;
+		case WLAN_EID_HT_CAP:
+			if (elen < sizeof(struct ieee80211_ht_capabilities))
+				break;
+			elems->ht_capabilities = pos;
+			break;
+		case WLAN_EID_HT_OPERATION:
+			if (elen < sizeof(struct ieee80211_ht_operation))
+				break;
+			elems->ht_operation = pos;
+			break;
+		case WLAN_EID_MESH_CONFIG:
+			elems->mesh_config = pos;
+			elems->mesh_config_len = elen;
+			break;
+		case WLAN_EID_MESH_ID:
+			elems->mesh_id = pos;
+			elems->mesh_id_len = elen;
+			break;
+		case WLAN_EID_PEER_MGMT:
+			elems->peer_mgmt = pos;
+			elems->peer_mgmt_len = elen;
+			break;
+		case WLAN_EID_VHT_CAP:
+			if (elen < sizeof(struct ieee80211_vht_capabilities))
+				break;
+			elems->vht_capabilities = pos;
+			break;
+		case WLAN_EID_VHT_OPERATION:
+			if (elen < sizeof(struct ieee80211_vht_operation))
+				break;
+			elems->vht_operation = pos;
+			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;
+			elems->link_id = pos;
+			break;
+		case WLAN_EID_INTERWORKING:
+			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;
+			break;
+		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
+			if (elen < 3)
+				break;
+			elems->bss_max_idle_period = pos;
+			break;
+		case WLAN_EID_SSID_LIST:
+			elems->ssid_list = pos;
+			elems->ssid_list_len = elen;
+			break;
+		case WLAN_EID_AMPE:
+			elems->ampe = pos;
+			elems->ampe_len = elen;
+			break;
+		case WLAN_EID_MIC:
+			elems->mic = pos;
+			elems->mic_len = elen;
+			/* after mic everything is encrypted, so stop. */
+			left = elen;
+			break;
+		case WLAN_EID_MULTI_BAND:
+			if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) {
+				wpa_printf(MSG_MSGDUMP,
+					   "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)",
+					   id, elen);
+				break;
+			}
+
+			elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos;
+			elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen;
+			elems->mb_ies.nof_ies++;
+			break;
+		default:
+			unknown++;
+			if (!show_errors)
+				break;
+			wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
+				   "ignored unknown element (id=%d elen=%d)",
+				   id, elen);
+			break;
+		}
+
+		left -= elen;
+		pos += elen;
+	}
+
+	if (left)
+		return ParseFailed;
+
+	return unknown ? ParseUnknown : ParseOK;
+}
+
+
+int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
+{
+	int count = 0;
+	const u8 *pos, *end;
+
+	if (ies == NULL)
+		return 0;
+
+	pos = ies;
+	end = ies + ies_len;
+
+	while (pos + 2 <= end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		count++;
+		pos += 2 + pos[1];
+	}
+
+	return count;
+}
+
+
+struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
+					    u32 oui_type)
+{
+	struct wpabuf *buf;
+	const u8 *end, *pos, *ie;
+
+	pos = ies;
+	end = ies + ies_len;
+	ie = NULL;
+
+	while (pos + 1 < end) {
+		if (pos + 2 + pos[1] > end)
+			return NULL;
+		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+		    WPA_GET_BE32(&pos[2]) == oui_type) {
+			ie = pos;
+			break;
+		}
+		pos += 2 + pos[1];
+	}
+
+	if (ie == NULL)
+		return NULL; /* No specified vendor IE found */
+
+	buf = wpabuf_alloc(ies_len);
+	if (buf == NULL)
+		return NULL;
+
+	/*
+	 * There may be multiple vendor IEs in the message, so need to
+	 * concatenate their data fields.
+	 */
+	while (pos + 1 < end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+		    WPA_GET_BE32(&pos[2]) == oui_type)
+			wpabuf_put_data(buf, pos + 6, pos[1] - 4);
+		pos += 2 + pos[1];
+	}
+
+	return buf;
+}
+
+
+const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
+{
+	u16 fc, type, stype;
+
+	/*
+	 * PS-Poll frames are 16 bytes. All other frames are
+	 * 24 bytes or longer.
+	 */
+	if (len < 16)
+		return NULL;
+
+	fc = le_to_host16(hdr->frame_control);
+	type = WLAN_FC_GET_TYPE(fc);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	switch (type) {
+	case WLAN_FC_TYPE_DATA:
+		if (len < 24)
+			return NULL;
+		switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
+		case WLAN_FC_FROMDS | WLAN_FC_TODS:
+		case WLAN_FC_TODS:
+			return hdr->addr1;
+		case WLAN_FC_FROMDS:
+			return hdr->addr2;
+		default:
+			return NULL;
+		}
+	case WLAN_FC_TYPE_CTRL:
+		if (stype != WLAN_FC_STYPE_PSPOLL)
+			return NULL;
+		return hdr->addr1;
+	case WLAN_FC_TYPE_MGMT:
+		return hdr->addr3;
+	default:
+		return NULL;
+	}
+}
+
+
+int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
+			  const char *name, const char *val)
+{
+	int num, v;
+	const char *pos;
+	struct hostapd_wmm_ac_params *ac;
+
+	/* skip 'wme_ac_' or 'wmm_ac_' prefix */
+	pos = name + 7;
+	if (os_strncmp(pos, "be_", 3) == 0) {
+		num = 0;
+		pos += 3;
+	} else if (os_strncmp(pos, "bk_", 3) == 0) {
+		num = 1;
+		pos += 3;
+	} else if (os_strncmp(pos, "vi_", 3) == 0) {
+		num = 2;
+		pos += 3;
+	} else if (os_strncmp(pos, "vo_", 3) == 0) {
+		num = 3;
+		pos += 3;
+	} else {
+		wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
+		return -1;
+	}
+
+	ac = &wmm_ac_params[num];
+
+	if (os_strcmp(pos, "aifs") == 0) {
+		v = atoi(val);
+		if (v < 1 || v > 255) {
+			wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
+			return -1;
+		}
+		ac->aifs = v;
+	} else if (os_strcmp(pos, "cwmin") == 0) {
+		v = atoi(val);
+		if (v < 0 || v > 15) {
+			wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
+			return -1;
+		}
+		ac->cwmin = v;
+	} else if (os_strcmp(pos, "cwmax") == 0) {
+		v = atoi(val);
+		if (v < 0 || v > 15) {
+			wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
+			return -1;
+		}
+		ac->cwmax = v;
+	} else if (os_strcmp(pos, "txop_limit") == 0) {
+		v = atoi(val);
+		if (v < 0 || v > 0xffff) {
+			wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
+			return -1;
+		}
+		ac->txop_limit = v;
+	} else if (os_strcmp(pos, "acm") == 0) {
+		v = atoi(val);
+		if (v < 0 || v > 1) {
+			wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
+			return -1;
+		}
+		ac->admission_control_mandatory = v;
+	} else {
+		wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
+{
+	u8 op_class;
+
+	return ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, channel);
+}
+
+
+/**
+ * ieee80211_freq_to_channel_ext - Convert frequency into channel info
+ * for HT40 and VHT. DFS channels are not covered.
+ * @freq: Frequency (MHz) to convert
+ * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
+ * @vht: 0 - non-VHT, 1 - 80 MHz
+ * @op_class: Buffer for returning operating class
+ * @channel: Buffer for returning channel number
+ * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
+ */
+enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
+						   int sec_channel, int vht,
+						   u8 *op_class, u8 *channel)
+{
+	/* TODO: more operating classes */
+
+	if (sec_channel > 1 || sec_channel < -1)
+		return NUM_HOSTAPD_MODES;
+
+	if (freq >= 2412 && freq <= 2472) {
+		if ((freq - 2407) % 5)
+			return NUM_HOSTAPD_MODES;
+
+		if (vht)
+			return NUM_HOSTAPD_MODES;
+
+		/* 2.407 GHz, channels 1..13 */
+		if (sec_channel == 1)
+			*op_class = 83;
+		else if (sec_channel == -1)
+			*op_class = 84;
+		else
+			*op_class = 81;
+
+		*channel = (freq - 2407) / 5;
+
+		return HOSTAPD_MODE_IEEE80211G;
+	}
+
+	if (freq == 2484) {
+		if (sec_channel || vht)
+			return NUM_HOSTAPD_MODES;
+
+		*op_class = 82; /* channel 14 */
+		*channel = 14;
+
+		return HOSTAPD_MODE_IEEE80211B;
+	}
+
+	if (freq >= 4900 && freq < 5000) {
+		if ((freq - 4000) % 5)
+			return NUM_HOSTAPD_MODES;
+		*channel = (freq - 4000) / 5;
+		*op_class = 0; /* TODO */
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
+	/* 5 GHz, channels 36..48 */
+	if (freq >= 5180 && freq <= 5240) {
+		if ((freq - 5000) % 5)
+			return NUM_HOSTAPD_MODES;
+
+		if (sec_channel == 1)
+			*op_class = 116;
+		else if (sec_channel == -1)
+			*op_class = 117;
+		else if (vht)
+			*op_class = 128;
+		else
+			*op_class = 115;
+
+		*channel = (freq - 5000) / 5;
+
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
+	/* 5 GHz, channels 149..161 */
+	if (freq >= 5745 && freq <= 5805) {
+		if ((freq - 5000) % 5)
+			return NUM_HOSTAPD_MODES;
+
+		if (sec_channel == 1)
+			*op_class = 126;
+		else if (sec_channel == -1)
+			*op_class = 127;
+		else if (vht)
+			*op_class = 128;
+		else
+			*op_class = 124;
+
+		*channel = (freq - 5000) / 5;
+
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
+	/* 5 GHz, channels 149..169 */
+	if (freq >= 5745 && freq <= 5845) {
+		if ((freq - 5000) % 5)
+			return NUM_HOSTAPD_MODES;
+
+		*op_class = 125;
+
+		*channel = (freq - 5000) / 5;
+
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
+	if (freq >= 5000 && freq < 5900) {
+		if ((freq - 5000) % 5)
+			return NUM_HOSTAPD_MODES;
+		*channel = (freq - 5000) / 5;
+		*op_class = 0; /* TODO */
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
+	/* 56.16 GHz, channel 1..4 */
+	if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
+		if (sec_channel || vht)
+			return NUM_HOSTAPD_MODES;
+
+		*channel = (freq - 56160) / 2160;
+		*op_class = 180;
+
+		return HOSTAPD_MODE_IEEE80211AD;
+	}
+
+	return NUM_HOSTAPD_MODES;
+}
+
+
+static const char *const us_op_class_cc[] = {
+	"US", "CA", NULL
+};
+
+static const char *const eu_op_class_cc[] = {
+	"AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE",
+	"DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT",
+	"LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT",
+	"RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL
+};
+
+static const char *const jp_op_class_cc[] = {
+	"JP", NULL
+};
+
+static const char *const cn_op_class_cc[] = {
+	"CN", NULL
+};
+
+
+static int country_match(const char *const cc[], const char *const country)
+{
+	int i;
+
+	if (country == NULL)
+		return 0;
+	for (i = 0; cc[i]; i++) {
+		if (cc[i][0] == country[0] && cc[i][1] == country[1])
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
+{
+	switch (op_class) {
+	case 12: /* channels 1..11 */
+	case 32: /* channels 1..7; 40 MHz */
+	case 33: /* channels 5..11; 40 MHz */
+		if (chan < 1 || chan > 11)
+			return -1;
+		return 2407 + 5 * chan;
+	case 1: /* channels 36,40,44,48 */
+	case 2: /* channels 52,56,60,64; dfs */
+	case 22: /* channels 36,44; 40 MHz */
+	case 23: /* channels 52,60; 40 MHz */
+	case 27: /* channels 40,48; 40 MHz */
+	case 28: /* channels 56,64; 40 MHz */
+		if (chan < 36 || chan > 64)
+			return -1;
+		return 5000 + 5 * chan;
+	case 4: /* channels 100-144 */
+	case 24: /* channels 100-140; 40 MHz */
+		if (chan < 100 || chan > 144)
+			return -1;
+		return 5000 + 5 * chan;
+	case 3: /* channels 149,153,157,161 */
+	case 25: /* channels 149,157; 40 MHz */
+	case 26: /* channels 149,157; 40 MHz */
+	case 30: /* channels 153,161; 40 MHz */
+	case 31: /* channels 153,161; 40 MHz */
+		if (chan < 149 || chan > 161)
+			return -1;
+		return 5000 + 5 * chan;
+	case 5: /* channels 149,153,157,161,165 */
+		if (chan < 149 || chan > 165)
+			return -1;
+		return 5000 + 5 * chan;
+	case 34: /* 60 GHz band, channels 1..3 */
+		if (chan < 1 || chan > 3)
+			return -1;
+		return 56160 + 2160 * chan;
+	}
+	return -1;
+}
+
+
+static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan)
+{
+	switch (op_class) {
+	case 4: /* channels 1..13 */
+	case 11: /* channels 1..9; 40 MHz */
+	case 12: /* channels 5..13; 40 MHz */
+		if (chan < 1 || chan > 13)
+			return -1;
+		return 2407 + 5 * chan;
+	case 1: /* channels 36,40,44,48 */
+	case 2: /* channels 52,56,60,64; dfs */
+	case 5: /* channels 36,44; 40 MHz */
+	case 6: /* channels 52,60; 40 MHz */
+	case 8: /* channels 40,48; 40 MHz */
+	case 9: /* channels 56,64; 40 MHz */
+		if (chan < 36 || chan > 64)
+			return -1;
+		return 5000 + 5 * chan;
+	case 3: /* channels 100-140 */
+	case 7: /* channels 100-132; 40 MHz */
+	case 10: /* channels 104-136; 40 MHz */
+	case 16: /* channels 100-140 */
+		if (chan < 100 || chan > 140)
+			return -1;
+		return 5000 + 5 * chan;
+	case 17: /* channels 149,153,157,161,165,169 */
+		if (chan < 149 || chan > 169)
+			return -1;
+		return 5000 + 5 * chan;
+	case 18: /* 60 GHz band, channels 1..4 */
+		if (chan < 1 || chan > 4)
+			return -1;
+		return 56160 + 2160 * chan;
+	}
+	return -1;
+}
+
+
+static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan)
+{
+	switch (op_class) {
+	case 30: /* channels 1..13 */
+	case 56: /* channels 1..9; 40 MHz */
+	case 57: /* channels 5..13; 40 MHz */
+		if (chan < 1 || chan > 13)
+			return -1;
+		return 2407 + 5 * chan;
+	case 31: /* channel 14 */
+		if (chan != 14)
+			return -1;
+		return 2414 + 5 * chan;
+	case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */
+	case 32: /* channels 52,56,60,64 */
+	case 33: /* channels 52,56,60,64 */
+	case 36: /* channels 36,44; 40 MHz */
+	case 37: /* channels 52,60; 40 MHz */
+	case 38: /* channels 52,60; 40 MHz */
+	case 41: /* channels 40,48; 40 MHz */
+	case 42: /* channels 56,64; 40 MHz */
+	case 43: /* channels 56,64; 40 MHz */
+		if (chan < 34 || chan > 64)
+			return -1;
+		return 5000 + 5 * chan;
+	case 34: /* channels 100-140 */
+	case 35: /* channels 100-140 */
+	case 39: /* channels 100-132; 40 MHz */
+	case 40: /* channels 100-132; 40 MHz */
+	case 44: /* channels 104-136; 40 MHz */
+	case 45: /* channels 104-136; 40 MHz */
+	case 58: /* channels 100-140 */
+		if (chan < 100 || chan > 140)
+			return -1;
+		return 5000 + 5 * chan;
+	case 59: /* 60 GHz band, channels 1..4 */
+		if (chan < 1 || chan > 3)
+			return -1;
+		return 56160 + 2160 * chan;
+	}
+	return -1;
+}
+
+
+static int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan)
+{
+	switch (op_class) {
+	case 7: /* channels 1..13 */
+	case 8: /* channels 1..9; 40 MHz */
+	case 9: /* channels 5..13; 40 MHz */
+		if (chan < 1 || chan > 13)
+			return -1;
+		return 2407 + 5 * chan;
+	case 1: /* channels 36,40,44,48 */
+	case 2: /* channels 52,56,60,64; dfs */
+	case 4: /* channels 36,44; 40 MHz */
+	case 5: /* channels 52,60; 40 MHz */
+		if (chan < 36 || chan > 64)
+			return -1;
+		return 5000 + 5 * chan;
+	case 3: /* channels 149,153,157,161,165 */
+	case 6: /* channels 149,157; 40 MHz */
+		if (chan < 149 || chan > 165)
+			return -1;
+		return 5000 + 5 * chan;
+	}
+	return -1;
+}
+
+
+static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
+{
+	/* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */
+	switch (op_class) {
+	case 81:
+		/* channels 1..13 */
+		if (chan < 1 || chan > 13)
+			return -1;
+		return 2407 + 5 * chan;
+	case 82:
+		/* channel 14 */
+		if (chan != 14)
+			return -1;
+		return 2414 + 5 * chan;
+	case 83: /* channels 1..9; 40 MHz */
+	case 84: /* channels 5..13; 40 MHz */
+		if (chan < 1 || chan > 13)
+			return -1;
+		return 2407 + 5 * chan;
+	case 115: /* channels 36,40,44,48; indoor only */
+	case 116: /* channels 36,44; 40 MHz; indoor only */
+	case 117: /* channels 40,48; 40 MHz; indoor only */
+	case 118: /* channels 52,56,60,64; dfs */
+	case 119: /* channels 52,60; 40 MHz; dfs */
+	case 120: /* channels 56,64; 40 MHz; dfs */
+		if (chan < 36 || chan > 64)
+			return -1;
+		return 5000 + 5 * chan;
+	case 121: /* channels 100-140 */
+	case 122: /* channels 100-142; 40 MHz */
+	case 123: /* channels 104-136; 40 MHz */
+		if (chan < 100 || chan > 140)
+			return -1;
+		return 5000 + 5 * chan;
+	case 124: /* channels 149,153,157,161 */
+	case 126: /* channels 149,157; 40 MHz */
+	case 127: /* channels 153,161; 40 MHz */
+		if (chan < 149 || chan > 161)
+			return -1;
+		return 5000 + 5 * chan;
+	case 125: /* channels 149,153,157,161,165,169 */
+		if (chan < 149 || chan > 169)
+			return -1;
+		return 5000 + 5 * chan;
+	case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
+	case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
+		if (chan < 36 || chan > 161)
+			return -1;
+		return 5000 + 5 * chan;
+	case 129: /* center freqs 50, 114; 160 MHz */
+		if (chan < 50 || chan > 114)
+			return -1;
+		return 5000 + 5 * chan;
+	case 180: /* 60 GHz band, channels 1..4 */
+		if (chan < 1 || chan > 4)
+			return -1;
+		return 56160 + 2160 * chan;
+	}
+	return -1;
+}
+
+/**
+ * ieee80211_chan_to_freq - Convert channel info to frequency
+ * @country: Country code, if known; otherwise, global operating class is used
+ * @op_class: Operating class
+ * @chan: Channel number
+ * Returns: Frequency in MHz or -1 if the specified channel is unknown
+ */
+int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan)
+{
+	int freq;
+
+	if (country_match(us_op_class_cc, country)) {
+		freq = ieee80211_chan_to_freq_us(op_class, chan);
+		if (freq > 0)
+			return freq;
+	}
+
+	if (country_match(eu_op_class_cc, country)) {
+		freq = ieee80211_chan_to_freq_eu(op_class, chan);
+		if (freq > 0)
+			return freq;
+	}
+
+	if (country_match(jp_op_class_cc, country)) {
+		freq = ieee80211_chan_to_freq_jp(op_class, chan);
+		if (freq > 0)
+			return freq;
+	}
+
+	if (country_match(cn_op_class_cc, country)) {
+		freq = ieee80211_chan_to_freq_cn(op_class, chan);
+		if (freq > 0)
+			return freq;
+	}
+
+	return ieee80211_chan_to_freq_global(op_class, chan);
+}
+
+
+int ieee80211_is_dfs(int freq)
+{
+	/* TODO: this could be more accurate to better cover all domains */
+	return (freq >= 5260 && freq <= 5320) || (freq >= 5500 && freq <= 5700);
+}
+
+
+static int is_11b(u8 rate)
+{
+	return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
+}
+
+
+int supp_rates_11b_only(struct ieee802_11_elems *elems)
+{
+	int num_11b = 0, num_others = 0;
+	int i;
+
+	if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL)
+		return 0;
+
+	for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) {
+		if (is_11b(elems->supp_rates[i]))
+			num_11b++;
+		else
+			num_others++;
+	}
+
+	for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len;
+	     i++) {
+		if (is_11b(elems->ext_supp_rates[i]))
+			num_11b++;
+		else
+			num_others++;
+	}
+
+	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
+}
+
+
+int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
+		       size_t ies_len)
+{
+	os_memset(info, 0, sizeof(*info));
+
+	while (ies_buf && ies_len >= 2 &&
+	       info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) {
+		size_t len = 2 + ies_buf[1];
+
+		if (len > ies_len) {
+			wpa_hexdump(MSG_DEBUG, "Truncated IEs",
+				    ies_buf, ies_len);
+			return -1;
+		}
+
+		if (ies_buf[0] == WLAN_EID_MULTI_BAND) {
+			wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len);
+			info->ies[info->nof_ies].ie = ies_buf + 2;
+			info->ies[info->nof_ies].ie_len = ies_buf[1];
+			info->nof_ies++;
+		}
+
+		ies_len -= len;
+		ies_buf += len;
+	}
+
+	return 0;
+}
+
+
+struct wpabuf * mb_ies_by_info(struct mb_ies_info *info)
+{
+	struct wpabuf *mb_ies = NULL;
+
+	WPA_ASSERT(info != NULL);
+
+	if (info->nof_ies) {
+		u8 i;
+		size_t mb_ies_size = 0;
+
+		for (i = 0; i < info->nof_ies; i++)
+			mb_ies_size += 2 + info->ies[i].ie_len;
+
+		mb_ies = wpabuf_alloc(mb_ies_size);
+		if (mb_ies) {
+			for (i = 0; i < info->nof_ies; i++) {
+				wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND);
+				wpabuf_put_u8(mb_ies, info->ies[i].ie_len);
+				wpabuf_put_data(mb_ies,
+						info->ies[i].ie,
+						info->ies[i].ie_len);
+			}
+		}
+	}
+
+	return mb_ies;
+}
diff --git a/hostap/src/common/ieee802_11_common.h b/hostap/src/common/ieee802_11_common.h
new file mode 100644
index 0000000..55ce022
--- /dev/null
+++ b/hostap/src/common/ieee802_11_common.h
@@ -0,0 +1,128 @@
+/*
+ * IEEE 802.11 Common routines
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_11_COMMON_H
+#define IEEE802_11_COMMON_H
+
+#define MAX_NOF_MB_IES_SUPPORTED 5
+
+struct mb_ies_info {
+	struct {
+		const u8 *ie;
+		u8 ie_len;
+	} ies[MAX_NOF_MB_IES_SUPPORTED];
+	u8 nof_ies;
+};
+
+/* Parsed Information Elements */
+struct ieee802_11_elems {
+	const u8 *ssid;
+	const u8 *supp_rates;
+	const u8 *ds_params;
+	const u8 *challenge;
+	const u8 *erp_info;
+	const u8 *ext_supp_rates;
+	const u8 *wpa_ie;
+	const u8 *rsn_ie;
+	const u8 *wmm; /* WMM Information or Parameter Element */
+	const u8 *wmm_tspec;
+	const u8 *wps_ie;
+	const u8 *supp_channels;
+	const u8 *mdie;
+	const u8 *ftie;
+	const u8 *timeout_int;
+	const u8 *ht_capabilities;
+	const u8 *ht_operation;
+	const u8 *mesh_config;
+	const u8 *mesh_id;
+	const u8 *peer_mgmt;
+	const u8 *vht_capabilities;
+	const u8 *vht_operation;
+	const u8 *vht_opmode_notif;
+	const u8 *vendor_ht_cap;
+	const u8 *vendor_vht;
+	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;
+	const u8 *ampe;
+	const u8 *mic;
+	const u8 *pref_freq_list;
+
+	u8 ssid_len;
+	u8 supp_rates_len;
+	u8 challenge_len;
+	u8 ext_supp_rates_len;
+	u8 wpa_ie_len;
+	u8 rsn_ie_len;
+	u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */
+	u8 wmm_tspec_len;
+	u8 wps_ie_len;
+	u8 supp_channels_len;
+	u8 mdie_len;
+	u8 ftie_len;
+	u8 mesh_config_len;
+	u8 mesh_id_len;
+	u8 peer_mgmt_len;
+	u8 vendor_ht_cap_len;
+	u8 vendor_vht_len;
+	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;
+	u8 ampe_len;
+	u8 mic_len;
+	u8 pref_freq_list_len;
+	struct mb_ies_info mb_ies;
+};
+
+typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
+
+ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
+				struct ieee802_11_elems *elems,
+				int show_errors);
+int ieee802_11_ie_count(const u8 *ies, size_t ies_len);
+struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
+					    u32 oui_type);
+struct ieee80211_hdr;
+const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len);
+
+struct hostapd_wmm_ac_params {
+	int cwmin;
+	int cwmax;
+	int aifs;
+	int txop_limit; /* in units of 32us */
+	int admission_control_mandatory;
+};
+
+int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
+			  const char *name, const char *val);
+enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel);
+int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan);
+enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
+						   int sec_channel, int vht,
+						   u8 *op_class, u8 *channel);
+int ieee80211_is_dfs(int freq);
+
+int supp_rates_11b_only(struct ieee802_11_elems *elems);
+int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
+		       size_t ies_len);
+struct wpabuf * mb_ies_by_info(struct mb_ies_info *info);
+
+const char * fc2str(u16 fc);
+#endif /* IEEE802_11_COMMON_H */
diff --git a/hostap/src/common/ieee802_11_defs.h b/hostap/src/common/ieee802_11_defs.h
new file mode 100644
index 0000000..44530ce
--- /dev/null
+++ b/hostap/src/common/ieee802_11_defs.h
@@ -0,0 +1,1436 @@
+/*
+ * IEEE 802.11 Frame type definitions
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2008 Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_11_DEFS_H
+#define IEEE802_11_DEFS_H
+
+#include <utils/common.h>
+
+/* IEEE 802.11 defines */
+
+#define WLAN_FC_PVER		0x0003
+#define WLAN_FC_TODS		0x0100
+#define WLAN_FC_FROMDS		0x0200
+#define WLAN_FC_MOREFRAG	0x0400
+#define WLAN_FC_RETRY		0x0800
+#define WLAN_FC_PWRMGT		0x1000
+#define WLAN_FC_MOREDATA	0x2000
+#define WLAN_FC_ISWEP		0x4000
+#define WLAN_FC_ORDER		0x8000
+
+#define WLAN_FC_GET_TYPE(fc)	(((fc) & 0x000c) >> 2)
+#define WLAN_FC_GET_STYPE(fc)	(((fc) & 0x00f0) >> 4)
+
+#define WLAN_INVALID_MGMT_SEQ   0xFFFF
+
+#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0)))
+#define WLAN_GET_SEQ_SEQ(seq) \
+	(((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4)
+
+#define WLAN_FC_TYPE_MGMT		0
+#define WLAN_FC_TYPE_CTRL		1
+#define WLAN_FC_TYPE_DATA		2
+
+/* management */
+#define WLAN_FC_STYPE_ASSOC_REQ		0
+#define WLAN_FC_STYPE_ASSOC_RESP	1
+#define WLAN_FC_STYPE_REASSOC_REQ	2
+#define WLAN_FC_STYPE_REASSOC_RESP	3
+#define WLAN_FC_STYPE_PROBE_REQ		4
+#define WLAN_FC_STYPE_PROBE_RESP	5
+#define WLAN_FC_STYPE_BEACON		8
+#define WLAN_FC_STYPE_ATIM		9
+#define WLAN_FC_STYPE_DISASSOC		10
+#define WLAN_FC_STYPE_AUTH		11
+#define WLAN_FC_STYPE_DEAUTH		12
+#define WLAN_FC_STYPE_ACTION		13
+
+/* control */
+#define WLAN_FC_STYPE_PSPOLL		10
+#define WLAN_FC_STYPE_RTS		11
+#define WLAN_FC_STYPE_CTS		12
+#define WLAN_FC_STYPE_ACK		13
+#define WLAN_FC_STYPE_CFEND		14
+#define WLAN_FC_STYPE_CFENDACK		15
+
+/* data */
+#define WLAN_FC_STYPE_DATA		0
+#define WLAN_FC_STYPE_DATA_CFACK	1
+#define WLAN_FC_STYPE_DATA_CFPOLL	2
+#define WLAN_FC_STYPE_DATA_CFACKPOLL	3
+#define WLAN_FC_STYPE_NULLFUNC		4
+#define WLAN_FC_STYPE_CFACK		5
+#define WLAN_FC_STYPE_CFPOLL		6
+#define WLAN_FC_STYPE_CFACKPOLL		7
+#define WLAN_FC_STYPE_QOS_DATA		8
+#define WLAN_FC_STYPE_QOS_DATA_CFACK	9
+#define WLAN_FC_STYPE_QOS_DATA_CFPOLL	10
+#define WLAN_FC_STYPE_QOS_DATA_CFACKPOLL	11
+#define WLAN_FC_STYPE_QOS_NULL		12
+#define WLAN_FC_STYPE_QOS_CFPOLL	14
+#define WLAN_FC_STYPE_QOS_CFACKPOLL	15
+
+/* Authentication algorithms */
+#define WLAN_AUTH_OPEN			0
+#define WLAN_AUTH_SHARED_KEY		1
+#define WLAN_AUTH_FT			2
+#define WLAN_AUTH_SAE			3
+#define WLAN_AUTH_LEAP			128
+
+#define WLAN_AUTH_CHALLENGE_LEN 128
+
+#define WLAN_CAPABILITY_ESS BIT(0)
+#define WLAN_CAPABILITY_IBSS BIT(1)
+#define WLAN_CAPABILITY_CF_POLLABLE BIT(2)
+#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3)
+#define WLAN_CAPABILITY_PRIVACY BIT(4)
+#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5)
+#define WLAN_CAPABILITY_PBCC BIT(6)
+#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7)
+#define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8)
+#define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10)
+#define WLAN_CAPABILITY_DSSS_OFDM BIT(13)
+
+/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */
+#define WLAN_STATUS_SUCCESS 0
+#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
+#define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2
+#define WLAN_STATUS_TDLS_WAKEUP_REJECT 3
+#define WLAN_STATUS_SECURITY_DISABLED 5
+#define WLAN_STATUS_UNACCEPTABLE_LIFETIME 6
+#define WLAN_STATUS_NOT_IN_SAME_BSS 7
+#define WLAN_STATUS_CAPS_UNSUPPORTED 10
+#define WLAN_STATUS_REASSOC_NO_ASSOC 11
+#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
+#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
+#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
+#define WLAN_STATUS_CHALLENGE_FAIL 15
+#define WLAN_STATUS_AUTH_TIMEOUT 16
+#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
+#define WLAN_STATUS_ASSOC_DENIED_RATES 18
+/* IEEE 802.11b */
+#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
+#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
+#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
+/* IEEE 802.11h */
+#define WLAN_STATUS_SPEC_MGMT_REQUIRED 22
+#define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23
+#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24
+/* IEEE 802.11g */
+#define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25
+#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 26
+#define WLAN_STATUS_ASSOC_DENIED_NO_HT 27
+#define WLAN_STATUS_R0KH_UNREACHABLE 28
+#define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29
+/* IEEE 802.11w */
+#define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30
+#define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31
+#define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32
+#define WLAN_STATUS_REQUEST_DECLINED 37
+#define WLAN_STATUS_INVALID_PARAMETERS 38
+/* IEEE 802.11i */
+#define WLAN_STATUS_INVALID_IE 40
+#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41
+#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42
+#define WLAN_STATUS_AKMP_NOT_VALID 43
+#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44
+#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45
+#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46
+#define WLAN_STATUS_TS_NOT_CREATED 47
+#define WLAN_STATUS_DIRECT_LINK_NOT_ALLOWED 48
+#define WLAN_STATUS_DEST_STA_NOT_PRESENT 49
+#define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50
+#define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51
+/* IEEE 802.11r */
+#define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52
+#define WLAN_STATUS_INVALID_PMKID 53
+#define WLAN_STATUS_INVALID_MDIE 54
+#define WLAN_STATUS_INVALID_FTIE 55
+#define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59
+#define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60
+#define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61
+#define WLAN_STATUS_STA_TIMED_OUT_WAITING_FOR_GAS_RESP 62
+#define WLAN_STATUS_GAS_RESP_LARGER_THAN_LIMIT 63
+#define WLAN_STATUS_REQ_REFUSED_HOME 64
+#define WLAN_STATUS_ADV_SRV_UNREACHABLE 65
+#define WLAN_STATUS_REQ_REFUSED_SSPN 67
+#define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68
+#define WLAN_STATUS_INVALID_RSNIE 72
+#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_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82
+#define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86
+#define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95
+#define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99
+#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
+#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
+#define WLAN_REASON_DEAUTH_LEAVING 3
+#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
+#define WLAN_REASON_DISASSOC_AP_BUSY 5
+#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
+#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
+#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
+#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
+/* IEEE 802.11h */
+#define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10
+#define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11
+/* IEEE 802.11i */
+#define WLAN_REASON_INVALID_IE 13
+#define WLAN_REASON_MICHAEL_MIC_FAILURE 14
+#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15
+#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16
+#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17
+#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18
+#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19
+#define WLAN_REASON_AKMP_NOT_VALID 20
+#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21
+#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22
+#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23
+#define WLAN_REASON_CIPHER_SUITE_REJECTED 24
+#define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25
+#define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26
+/* IEEE 802.11e */
+#define WLAN_REASON_DISASSOC_LOW_ACK 34
+/* IEEE 802.11s */
+#define WLAN_REASON_MESH_PEERING_CANCELLED 52
+#define WLAN_REASON_MESH_MAX_PEERS 53
+#define WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION 54
+#define WLAN_REASON_MESH_CLOSE_RCVD 55
+#define WLAN_REASON_MESH_MAX_RETRIES 56
+#define WLAN_REASON_MESH_CONFIRM_TIMEOUT 57
+#define WLAN_REASON_MESH_INVALID_GTK 58
+#define WLAN_REASON_MESH_INCONSISTENT_PARAMS 59
+#define WLAN_REASON_MESH_INVALID_SECURITY_CAP 60
+
+
+/* Information Element IDs */
+#define WLAN_EID_SSID 0
+#define WLAN_EID_SUPP_RATES 1
+#define WLAN_EID_FH_PARAMS 2
+#define WLAN_EID_DS_PARAMS 3
+#define WLAN_EID_CF_PARAMS 4
+#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
+#define WLAN_EID_PWR_CAPABILITY 33
+#define WLAN_EID_TPC_REQUEST 34
+#define WLAN_EID_TPC_REPORT 35
+#define WLAN_EID_SUPPORTED_CHANNELS 36
+#define WLAN_EID_CHANNEL_SWITCH 37
+#define WLAN_EID_MEASURE_REQUEST 38
+#define WLAN_EID_MEASURE_REPORT 39
+#define WLAN_EID_QUITE 40
+#define WLAN_EID_IBSS_DFS 41
+/* EIDs defined by IEEE 802.11h - END */
+#define WLAN_EID_ERP_INFO 42
+#define WLAN_EID_HT_CAP 45
+#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
+#define WLAN_EID_TIME_ADVERTISEMENT 69
+#define WLAN_EID_RRM_ENABLED_CAPABILITIES 70
+#define WLAN_EID_20_40_BSS_COEXISTENCE 72
+#define WLAN_EID_20_40_BSS_INTOLERANT 73
+#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
+#define WLAN_EID_MMIE 76
+#define WLAN_EID_SSID_LIST 84
+#define WLAN_EID_BSS_MAX_IDLE_PERIOD 90
+#define WLAN_EID_TFS_REQ 91
+#define WLAN_EID_TFS_RESP 92
+#define WLAN_EID_WNMSLEEP 93
+#define WLAN_EID_TIME_ZONE 98
+#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_MESH_CONFIG 113
+#define WLAN_EID_MESH_ID 114
+#define WLAN_EID_PEER_MGMT 117
+#define WLAN_EID_EXT_CAPAB 127
+#define WLAN_EID_AMPE 139
+#define WLAN_EID_MIC 140
+#define WLAN_EID_CCKM 156
+#define WLAN_EID_MULTI_BAND 158
+#define WLAN_EID_SESSION_TRANSITION 164
+#define WLAN_EID_VHT_CAP 191
+#define WLAN_EID_VHT_OPERATION 192
+#define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193
+#define WLAN_EID_VHT_WIDE_BW_CHSWITCH  194
+#define WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE 195
+#define WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER 196
+#define WLAN_EID_VHT_AID 197
+#define WLAN_EID_VHT_QUIET_CHANNEL 198
+#define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199
+#define WLAN_EID_VENDOR_SPECIFIC 221
+
+
+/* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */
+#define WLAN_ACTION_SPECTRUM_MGMT 0
+#define WLAN_ACTION_QOS 1
+#define WLAN_ACTION_DLS 2
+#define WLAN_ACTION_BLOCK_ACK 3
+#define WLAN_ACTION_PUBLIC 4
+#define WLAN_ACTION_RADIO_MEASUREMENT 5
+#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
+#define WLAN_ACTION_SELF_PROTECTED 15
+#define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
+#define WLAN_ACTION_FST 18
+#define WLAN_ACTION_VENDOR_SPECIFIC 127
+
+/* Public action codes */
+#define WLAN_PA_20_40_BSS_COEX 0
+#define WLAN_PA_VENDOR_SPECIFIC 9
+#define WLAN_PA_GAS_INITIAL_REQ 10
+#define WLAN_PA_GAS_INITIAL_RESP 11
+#define WLAN_PA_GAS_COMEBACK_REQ 12
+#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
+
+#define WLAN_SA_QUERY_TR_ID_LEN 2
+
+/* TDLS action codes */
+#define WLAN_TDLS_SETUP_REQUEST 0
+#define WLAN_TDLS_SETUP_RESPONSE 1
+#define WLAN_TDLS_SETUP_CONFIRM 2
+#define WLAN_TDLS_TEARDOWN 3
+#define WLAN_TDLS_PEER_TRAFFIC_INDICATION 4
+#define WLAN_TDLS_CHANNEL_SWITCH_REQUEST 5
+#define WLAN_TDLS_CHANNEL_SWITCH_RESPONSE 6
+#define WLAN_TDLS_PEER_PSM_REQUEST 7
+#define WLAN_TDLS_PEER_PSM_RESPONSE 8
+#define WLAN_TDLS_PEER_TRAFFIC_RESPONSE 9
+#define WLAN_TDLS_DISCOVERY_REQUEST 10
+
+/* Radio Measurement Action codes */
+#define WLAN_RRM_RADIO_MEASUREMENT_REQUEST 0
+#define WLAN_RRM_RADIO_MEASUREMENT_REPORT 1
+#define WLAN_RRM_LINK_MEASUREMENT_REQUEST 2
+#define WLAN_RRM_LINK_MEASUREMENT_REPORT 3
+#define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4
+#define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5
+
+/* Radio Measurement capabilities (from RRM Capabilities IE) */
+/* byte 1 (out of 5) */
+#define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0)
+#define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1)
+
+/* Timeout Interval Type */
+#define WLAN_TIMEOUT_REASSOC_DEADLINE 1
+#define WLAN_TIMEOUT_KEY_LIFETIME 2
+#define WLAN_TIMEOUT_ASSOC_COMEBACK 3
+
+/* Interworking element (IEEE 802.11u) - Access Network Options */
+#define INTERWORKING_ANO_ACCESS_NETWORK_MASK 0x0f
+#define INTERWORKING_ANO_INTERNET 0x10
+#define INTERWORKING_ANO_ASRA 0x20
+#define INTERWORKING_ANO_ESR 0x40
+#define INTERWORKING_ANO_UESA 0x80
+
+#define INTERWORKING_ANT_PRIVATE 0
+#define INTERWORKING_ANT_PRIVATE_WITH_GUEST 1
+#define INTERWORKING_ANT_CHARGEABLE_PUBLIC 2
+#define INTERWORKING_ANT_FREE_PUBLIC 3
+#define INTERWORKING_ANT_PERSONAL_DEVICE 4
+#define INTERWORKING_ANT_EMERGENCY_SERVICES 5
+#define INTERWORKING_ANT_TEST 6
+#define INTERWORKING_ANT_WILDCARD 15
+
+/* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */
+enum adv_proto_id {
+	ACCESS_NETWORK_QUERY_PROTOCOL = 0,
+	MIH_INFO_SERVICE = 1,
+	MIH_CMD_AND_EVENT_DISCOVERY = 2,
+	EMERGENCY_ALERT_SYSTEM = 3,
+	ADV_PROTO_VENDOR_SPECIFIC = 221
+};
+
+/* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */
+enum anqp_info_id {
+	ANQP_QUERY_LIST = 256,
+	ANQP_CAPABILITY_LIST = 257,
+	ANQP_VENUE_NAME = 258,
+	ANQP_EMERGENCY_CALL_NUMBER = 259,
+	ANQP_NETWORK_AUTH_TYPE = 260,
+	ANQP_ROAMING_CONSORTIUM = 261,
+	ANQP_IP_ADDR_TYPE_AVAILABILITY = 262,
+	ANQP_NAI_REALM = 263,
+	ANQP_3GPP_CELLULAR_NETWORK = 264,
+	ANQP_AP_GEOSPATIAL_LOCATION = 265,
+	ANQP_AP_CIVIC_LOCATION = 266,
+	ANQP_AP_LOCATION_PUBLIC_URI = 267,
+	ANQP_DOMAIN_NAME = 268,
+	ANQP_EMERGENCY_ALERT_URI = 269,
+	ANQP_EMERGENCY_NAI = 271,
+	ANQP_VENDOR_SPECIFIC = 56797
+};
+
+/* NAI Realm list - EAP Method subfield - Authentication Parameter ID */
+enum nai_realm_eap_auth_param {
+	NAI_REALM_EAP_AUTH_EXPANDED_EAP_METHOD = 1,
+	NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH = 2,
+	NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD = 3,
+	NAI_REALM_EAP_AUTH_EXPANDED_INNER_EAP_METHOD = 4,
+	NAI_REALM_EAP_AUTH_CRED_TYPE = 5,
+	NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE = 6,
+	NAI_REALM_EAP_AUTH_VENDOR_SPECIFIC = 221
+};
+
+enum nai_realm_eap_auth_inner_non_eap {
+	NAI_REALM_INNER_NON_EAP_PAP = 1,
+	NAI_REALM_INNER_NON_EAP_CHAP = 2,
+	NAI_REALM_INNER_NON_EAP_MSCHAP = 3,
+	NAI_REALM_INNER_NON_EAP_MSCHAPV2 = 4
+};
+
+enum nai_realm_eap_cred_type {
+	NAI_REALM_CRED_TYPE_SIM = 1,
+	NAI_REALM_CRED_TYPE_USIM = 2,
+	NAI_REALM_CRED_TYPE_NFC_SECURE_ELEMENT = 3,
+	NAI_REALM_CRED_TYPE_HARDWARE_TOKEN = 4,
+	NAI_REALM_CRED_TYPE_SOFTOKEN = 5,
+	NAI_REALM_CRED_TYPE_CERTIFICATE = 6,
+	NAI_REALM_CRED_TYPE_USERNAME_PASSWORD = 7,
+	NAI_REALM_CRED_TYPE_NONE = 8,
+	NAI_REALM_CRED_TYPE_ANONYMOUS = 9,
+	NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10
+};
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee80211_hdr {
+	le16 frame_control;
+	le16 duration_id;
+	u8 addr1[6];
+	u8 addr2[6];
+	u8 addr3[6];
+	le16 seq_ctrl;
+	/* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame
+	 */
+} STRUCT_PACKED;
+
+#define IEEE80211_DA_FROMDS addr1
+#define IEEE80211_BSSID_FROMDS addr2
+#define IEEE80211_SA_FROMDS addr3
+
+#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr))
+
+#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4))
+
+struct ieee80211_mgmt {
+	le16 frame_control;
+	le16 duration;
+	u8 da[6];
+	u8 sa[6];
+	u8 bssid[6];
+	le16 seq_ctrl;
+	union {
+		struct {
+			le16 auth_alg;
+			le16 auth_transaction;
+			le16 status_code;
+			/* possibly followed by Challenge text */
+			u8 variable[];
+		} STRUCT_PACKED auth;
+		struct {
+			le16 reason_code;
+			u8 variable[];
+		} STRUCT_PACKED deauth;
+		struct {
+			le16 capab_info;
+			le16 listen_interval;
+			/* followed by SSID and Supported rates */
+			u8 variable[];
+		} STRUCT_PACKED assoc_req;
+		struct {
+			le16 capab_info;
+			le16 status_code;
+			le16 aid;
+			/* followed by Supported rates */
+			u8 variable[];
+		} STRUCT_PACKED assoc_resp, reassoc_resp;
+		struct {
+			le16 capab_info;
+			le16 listen_interval;
+			u8 current_ap[6];
+			/* followed by SSID and Supported rates */
+			u8 variable[];
+		} STRUCT_PACKED reassoc_req;
+		struct {
+			le16 reason_code;
+			u8 variable[];
+		} STRUCT_PACKED disassoc;
+		struct {
+			u8 timestamp[8];
+			le16 beacon_int;
+			le16 capab_info;
+			/* followed by some of SSID, Supported rates,
+			 * FH Params, DS Params, CF Params, IBSS Params, TIM */
+			u8 variable[];
+		} STRUCT_PACKED beacon;
+		struct {
+			/* only variable items: SSID, Supported rates */
+			u8 variable[0];
+		} STRUCT_PACKED probe_req;
+		struct {
+			u8 timestamp[8];
+			le16 beacon_int;
+			le16 capab_info;
+			/* followed by some of SSID, Supported rates,
+			 * FH Params, DS Params, CF Params, IBSS Params */
+			u8 variable[];
+		} STRUCT_PACKED probe_resp;
+		struct {
+			u8 category;
+			union {
+				struct {
+					u8 action_code;
+					u8 dialog_token;
+					u8 status_code;
+					u8 variable[];
+				} STRUCT_PACKED wmm_action;
+				struct{
+					u8 action_code;
+					u8 element_id;
+					u8 length;
+					u8 switch_mode;
+					u8 new_chan;
+					u8 switch_count;
+				} STRUCT_PACKED chan_switch;
+				struct {
+					u8 action;
+					u8 sta_addr[ETH_ALEN];
+					u8 target_ap_addr[ETH_ALEN];
+					u8 variable[]; /* FT Request */
+				} STRUCT_PACKED ft_action_req;
+				struct {
+					u8 action;
+					u8 sta_addr[ETH_ALEN];
+					u8 target_ap_addr[ETH_ALEN];
+					le16 status_code;
+					u8 variable[]; /* FT Request */
+				} STRUCT_PACKED ft_action_resp;
+				struct {
+					u8 action;
+					u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
+				} STRUCT_PACKED sa_query_req;
+				struct {
+					u8 action; /* */
+					u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
+				} STRUCT_PACKED sa_query_resp;
+				struct {
+					u8 action;
+					u8 dialogtoken;
+					u8 variable[];
+				} STRUCT_PACKED wnm_sleep_req;
+				struct {
+					u8 action;
+					u8 dialogtoken;
+					le16 keydata_len;
+					u8 variable[];
+				} STRUCT_PACKED wnm_sleep_resp;
+				struct {
+					u8 action;
+					u8 variable[];
+				} STRUCT_PACKED public_action;
+				struct {
+					u8 action; /* 9 */
+					u8 oui[3];
+					/* Vendor-specific content */
+					u8 variable[];
+				} STRUCT_PACKED vs_public_action;
+				struct {
+					u8 action; /* 7 */
+					u8 dialog_token;
+					u8 req_mode;
+					le16 disassoc_timer;
+					u8 validity_interval;
+					/* BSS Termination Duration (optional),
+					 * Session Information URL (optional),
+					 * BSS Transition Candidate List
+					 * Entries */
+					u8 variable[];
+				} STRUCT_PACKED bss_tm_req;
+				struct {
+					u8 action; /* 8 */
+					u8 dialog_token;
+					u8 status_code;
+					u8 bss_termination_delay;
+					/* Target BSSID (optional),
+					 * BSS Transition Candidate List
+					 * Entries (optional) */
+					u8 variable[];
+				} STRUCT_PACKED bss_tm_resp;
+				struct {
+					u8 action; /* 6 */
+					u8 dialog_token;
+					u8 query_reason;
+					/* BSS Transition Candidate List
+					 * Entries (optional) */
+					u8 variable[];
+				} STRUCT_PACKED bss_tm_query;
+				struct {
+					u8 action; /* 15 */
+					u8 variable[];
+				} STRUCT_PACKED slf_prot_action;
+				struct {
+					u8 action;
+					u8 variable[];
+				} STRUCT_PACKED fst_action;
+			} u;
+		} STRUCT_PACKED action;
+	} u;
+} STRUCT_PACKED;
+
+
+/* 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; /* 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;
+	u8 asel_capabilities;
+} STRUCT_PACKED;
+
+
+/* HT Operation element */
+struct ieee80211_ht_operation {
+	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;
+
+
+struct ieee80211_vht_capabilities {
+	le32 vht_capabilities_info;
+	struct {
+		le16 rx_map;
+		le16 rx_highest;
+		le16 tx_map;
+		le16 tx_highest;
+	} vht_supported_mcs_set;
+} STRUCT_PACKED;
+
+struct ieee80211_vht_operation {
+	u8 vht_op_info_chwidth;
+	u8 vht_op_info_chan_center_freq_seg0_idx;
+	u8 vht_op_info_chan_center_freq_seg1_idx;
+	le16 vht_basic_mcs_set;
+} STRUCT_PACKED;
+
+struct ieee80211_ampe_ie {
+	u8 selected_pairwise_suite[4];
+	u8 local_nonce[32];
+	u8 peer_nonce[32];
+	u8 mgtk[16];
+	u8 key_rsc[8];
+	u8 key_expiration[4];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define ERP_INFO_NON_ERP_PRESENT BIT(0)
+#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)))
+#define HT_CAP_INFO_SMPS_STATIC			((u16) 0)
+#define HT_CAP_INFO_SMPS_DYNAMIC		((u16) BIT(2))
+#define HT_CAP_INFO_SMPS_DISABLED		((u16) (BIT(2) | BIT(3)))
+#define HT_CAP_INFO_GREEN_FIELD			((u16) BIT(4))
+#define HT_CAP_INFO_SHORT_GI20MHZ		((u16) BIT(5))
+#define HT_CAP_INFO_SHORT_GI40MHZ		((u16) BIT(6))
+#define HT_CAP_INFO_TX_STBC			((u16) BIT(7))
+#define HT_CAP_INFO_RX_STBC_MASK		((u16) (BIT(8) | BIT(9)))
+#define HT_CAP_INFO_RX_STBC_1			((u16) BIT(8))
+#define HT_CAP_INFO_RX_STBC_12			((u16) BIT(9))
+#define HT_CAP_INFO_RX_STBC_123			((u16) (BIT(8) | BIT(9)))
+#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))
+/* 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_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 */
+
+/* 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 */
+
+/* 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_STA_CHNL_WIDTH			((u8) BIT(2))
+#define HT_INFO_HT_PARAM_RIFS_MODE			((u8) BIT(3))
+/* 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 */
+
+/* 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
+
+/* 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_MAX_MPDU_LENGTH_MASK_SHIFT          0
+#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))
+#define VHT_CAP_TXSTBC                              ((u32) BIT(7))
+#define VHT_CAP_RXSTBC_1                            ((u32) BIT(8))
+#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_RXSTBC_MASK_SHIFT                   8
+#define VHT_CAP_SU_BEAMFORMER_CAPABLE               ((u32) BIT(11))
+#define VHT_CAP_SU_BEAMFORMEE_CAPABLE               ((u32) BIT(12))
+#define VHT_CAP_BEAMFORMEE_STS_MAX                  ((u32) BIT(13) | \
+							   BIT(14) | BIT(15))
+#define VHT_CAP_BEAMFORMEE_STS_MAX_SHIFT            13
+#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_MAX_SHIFT        16
+#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_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_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT 23
+#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
+#define VHT_CHANWIDTH_160MHZ	2
+#define VHT_CHANWIDTH_80P80MHZ	3
+
+#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
+#define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1
+#define WMM_OUI_SUBTYPE_TSPEC_ELEMENT 2
+#define WMM_VERSION 1
+
+#define WMM_ACTION_CODE_ADDTS_REQ 0
+#define WMM_ACTION_CODE_ADDTS_RESP 1
+#define WMM_ACTION_CODE_DELTS 2
+
+#define WMM_ADDTS_STATUS_ADMISSION_ACCEPTED 0
+#define WMM_ADDTS_STATUS_INVALID_PARAMETERS 1
+/* 2 - Reserved */
+#define WMM_ADDTS_STATUS_REFUSED 3
+/* 4-255 - Reserved */
+
+/* WMM TSPEC Direction Field Values */
+#define WMM_TSPEC_DIRECTION_UPLINK 0
+#define WMM_TSPEC_DIRECTION_DOWNLINK 1
+/* 2 - Reserved */
+#define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3
+
+/*
+ * WMM Information Element (used in (Re)Association Request frames; may also be
+ * used in Beacon frames)
+ */
+struct wmm_information_element {
+	/* Element ID: 221 (0xdd); Length: 7 */
+	/* required fields for WMM version 1 */
+	u8 oui[3]; /* 00:50:f2 */
+	u8 oui_type; /* 2 */
+	u8 oui_subtype; /* 0 */
+	u8 version; /* 1 for WMM version 1.0 */
+	u8 qos_info; /* AP/STA specific QoS info */
+
+} STRUCT_PACKED;
+
+#define WMM_QOSINFO_AP_UAPSD 0x80
+
+#define WMM_QOSINFO_STA_AC_MASK 0x0f
+#define WMM_QOSINFO_STA_SP_MASK 0x03
+#define WMM_QOSINFO_STA_SP_SHIFT 5
+
+#define WMM_AC_AIFSN_MASK 0x0f
+#define WMM_AC_AIFNS_SHIFT 0
+#define WMM_AC_ACM 0x10
+#define WMM_AC_ACI_MASK 0x60
+#define WMM_AC_ACI_SHIFT 5
+
+#define WMM_AC_ECWMIN_MASK 0x0f
+#define WMM_AC_ECWMIN_SHIFT 0
+#define WMM_AC_ECWMAX_MASK 0xf0
+#define WMM_AC_ECWMAX_SHIFT 4
+
+struct wmm_ac_parameter {
+	u8 aci_aifsn; /* AIFSN, ACM, ACI */
+	u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+	le16 txop_limit;
+}  STRUCT_PACKED;
+
+/*
+ * WMM Parameter Element (used in Beacon, Probe Response, and (Re)Association
+ * Response frmaes)
+ */
+struct wmm_parameter_element {
+	/* Element ID: 221 (0xdd); Length: 24 */
+	/* required fields for WMM version 1 */
+	u8 oui[3]; /* 00:50:f2 */
+	u8 oui_type; /* 2 */
+	u8 oui_subtype; /* 1 */
+	u8 version; /* 1 for WMM version 1.0 */
+	u8 qos_info; /* AP/STA specific QoS info */
+	u8 reserved; /* 0 */
+	struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */
+
+} STRUCT_PACKED;
+
+/* WMM TSPEC Element */
+struct wmm_tspec_element {
+	u8 eid; /* 221 = 0xdd */
+	u8 length; /* 6 + 55 = 61 */
+	u8 oui[3]; /* 00:50:f2 */
+	u8 oui_type; /* 2 */
+	u8 oui_subtype; /* 2 */
+	u8 version; /* 1 */
+	/* WMM TSPEC body (55 octets): */
+	u8 ts_info[3];
+	le16 nominal_msdu_size;
+	le16 maximum_msdu_size;
+	le32 minimum_service_interval;
+	le32 maximum_service_interval;
+	le32 inactivity_interval;
+	le32 suspension_interval;
+	le32 service_start_time;
+	le32 minimum_data_rate;
+	le32 mean_data_rate;
+	le32 peak_data_rate;
+	le32 maximum_burst_size;
+	le32 delay_bound;
+	le32 minimum_phy_rate;
+	le16 surplus_bandwidth_allowance;
+	le16 medium_time;
+} STRUCT_PACKED;
+
+
+/* Access Categories / ACI to AC coding */
+enum wmm_ac {
+	WMM_AC_BE = 0 /* Best Effort */,
+	WMM_AC_BK = 1 /* Background */,
+	WMM_AC_VI = 2 /* Video */,
+	WMM_AC_VO = 3 /* Voice */,
+	WMM_AC_NUM = 4
+};
+
+
+#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
+#define HS20_STYPE_WAN_METRICS 4
+#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) */
+
+#define P2P_OUI_TYPE 9
+
+enum p2p_attr_id {
+	P2P_ATTR_STATUS = 0,
+	P2P_ATTR_MINOR_REASON_CODE = 1,
+	P2P_ATTR_CAPABILITY = 2,
+	P2P_ATTR_DEVICE_ID = 3,
+	P2P_ATTR_GROUP_OWNER_INTENT = 4,
+	P2P_ATTR_CONFIGURATION_TIMEOUT = 5,
+	P2P_ATTR_LISTEN_CHANNEL = 6,
+	P2P_ATTR_GROUP_BSSID = 7,
+	P2P_ATTR_EXT_LISTEN_TIMING = 8,
+	P2P_ATTR_INTENDED_INTERFACE_ADDR = 9,
+	P2P_ATTR_MANAGEABILITY = 10,
+	P2P_ATTR_CHANNEL_LIST = 11,
+	P2P_ATTR_NOTICE_OF_ABSENCE = 12,
+	P2P_ATTR_DEVICE_INFO = 13,
+	P2P_ATTR_GROUP_INFO = 14,
+	P2P_ATTR_GROUP_ID = 15,
+	P2P_ATTR_INTERFACE = 16,
+	P2P_ATTR_OPERATING_CHANNEL = 17,
+	P2P_ATTR_INVITATION_FLAGS = 18,
+	P2P_ATTR_OOB_GO_NEG_CHANNEL = 19,
+	P2P_ATTR_SERVICE_HASH = 21,
+	P2P_ATTR_SESSION_INFORMATION_DATA = 22,
+	P2P_ATTR_CONNECTION_CAPABILITY = 23,
+	P2P_ATTR_ADVERTISEMENT_ID = 24,
+	P2P_ATTR_ADVERTISED_SERVICE = 25,
+	P2P_ATTR_SESSION_ID = 26,
+	P2P_ATTR_FEATURE_CAPABILITY = 27,
+	P2P_ATTR_PERSISTENT_GROUP = 28,
+	P2P_ATTR_VENDOR_SPECIFIC = 221
+};
+
+#define P2P_MAX_GO_INTENT 15
+
+/* P2P Capability - Device Capability bitmap */
+#define P2P_DEV_CAPAB_SERVICE_DISCOVERY BIT(0)
+#define P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY BIT(1)
+#define P2P_DEV_CAPAB_CONCURRENT_OPER BIT(2)
+#define P2P_DEV_CAPAB_INFRA_MANAGED BIT(3)
+#define P2P_DEV_CAPAB_DEVICE_LIMIT BIT(4)
+#define P2P_DEV_CAPAB_INVITATION_PROCEDURE BIT(5)
+
+/* P2P Capability - Group Capability bitmap */
+#define P2P_GROUP_CAPAB_GROUP_OWNER BIT(0)
+#define P2P_GROUP_CAPAB_PERSISTENT_GROUP BIT(1)
+#define P2P_GROUP_CAPAB_GROUP_LIMIT BIT(2)
+#define P2P_GROUP_CAPAB_INTRA_BSS_DIST BIT(3)
+#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)
+
+/* P2PS Coordination Protocol Transport Bitmap */
+#define P2PS_FEATURE_CAPAB_UDP_TRANSPORT BIT(0)
+#define P2PS_FEATURE_CAPAB_MAC_TRANSPORT BIT(1)
+
+struct p2ps_feature_capab {
+	u8 cpt;
+	u8 reserved;
+} STRUCT_PACKED;
+
+/* Invitation Flags */
+#define P2P_INVITATION_FLAGS_TYPE BIT(0)
+
+/* P2P Manageability */
+#define P2P_MAN_DEVICE_MANAGEMENT BIT(0)
+#define P2P_MAN_CROSS_CONNECTION_PERMITTED BIT(1)
+#define P2P_MAN_COEXISTENCE_OPTIONAL BIT(2)
+
+enum p2p_status_code {
+	P2P_SC_SUCCESS = 0,
+	P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1,
+	P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2,
+	P2P_SC_FAIL_LIMIT_REACHED = 3,
+	P2P_SC_FAIL_INVALID_PARAMS = 4,
+	P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5,
+	P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6,
+	P2P_SC_FAIL_NO_COMMON_CHANNELS = 7,
+	P2P_SC_FAIL_UNKNOWN_GROUP = 8,
+	P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9,
+	P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10,
+	P2P_SC_FAIL_REJECTED_BY_USER = 11,
+	P2P_SC_SUCCESS_DEFERRED = 12,
+};
+
+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
+
+/* P2P action frames */
+enum p2p_act_frame_type {
+	P2P_NOA = 0,
+	P2P_PRESENCE_REQ = 1,
+	P2P_PRESENCE_RESP = 2,
+	P2P_GO_DISC_REQ = 3
+};
+
+/* P2P public action frames */
+enum p2p_action_frame_type {
+	P2P_GO_NEG_REQ = 0,
+	P2P_GO_NEG_RESP = 1,
+	P2P_GO_NEG_CONF = 2,
+	P2P_INVITATION_REQ = 3,
+	P2P_INVITATION_RESP = 4,
+	P2P_DEV_DISC_REQ = 5,
+	P2P_DEV_DISC_RESP = 6,
+	P2P_PROV_DISC_REQ = 7,
+	P2P_PROV_DISC_RESP = 8
+};
+
+enum p2p_service_protocol_type {
+	P2P_SERV_ALL_SERVICES = 0,
+	P2P_SERV_BONJOUR = 1,
+	P2P_SERV_UPNP = 2,
+	P2P_SERV_WS_DISCOVERY = 3,
+	P2P_SERV_WIFI_DISPLAY = 4,
+	P2P_SERV_P2PS = 11,
+	P2P_SERV_VENDOR_SPECIFIC = 255
+};
+
+enum p2p_sd_status {
+	P2P_SD_SUCCESS = 0,
+	P2P_SD_PROTO_NOT_AVAILABLE = 1,
+	P2P_SD_REQUESTED_INFO_NOT_AVAILABLE = 2,
+	P2P_SD_BAD_REQUEST = 3
+};
+
+
+enum wifi_display_subelem {
+	WFD_SUBELEM_DEVICE_INFO = 0,
+	WFD_SUBELEM_ASSOCIATED_BSSID = 1,
+	WFD_SUBELEM_AUDIO_FORMATS = 2,
+	WFD_SUBELEM_VIDEO_FORMATS = 3,
+	WFD_SUBELEM_3D_VIDEO_FORMATS = 4,
+	WFD_SUBELEM_CONTENT_PROTECTION = 5,
+	WFD_SUBELEM_COUPLED_SINK = 6,
+	WFD_SUBELEM_EXT_CAPAB = 7,
+	WFD_SUBELEM_LOCAL_IP_ADDRESS = 8,
+	WFD_SUBELEM_SESSION_INFO = 9
+};
+
+/* 802.11s */
+#define MESH_SYNC_METHOD_NEIGHBOR_OFFSET 1
+#define MESH_SYNC_METHOD_VENDOR		255
+#define MESH_PATH_PROTOCOL_HWMP		1
+#define MESH_PATH_PROTOCOL_VENDOR	255
+#define MESH_PATH_METRIC_AIRTIME	1
+#define MESH_PATH_METRIC_VENDOR		255
+
+enum plink_action_field {
+	PLINK_OPEN = 1,
+	PLINK_CONFIRM,
+	PLINK_CLOSE
+};
+
+#define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
+#define VENDOR_VHT_TYPE		0x04
+#define VENDOR_VHT_SUBTYPE	0x08
+#define VENDOR_VHT_SUBTYPE2	0x00
+
+#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
+
+/* cipher suite selectors */
+#define WLAN_CIPHER_SUITE_USE_GROUP	0x000FAC00
+#define WLAN_CIPHER_SUITE_WEP40		0x000FAC01
+#define WLAN_CIPHER_SUITE_TKIP		0x000FAC02
+/* reserved: 				0x000FAC03 */
+#define WLAN_CIPHER_SUITE_CCMP		0x000FAC04
+#define WLAN_CIPHER_SUITE_WEP104	0x000FAC05
+#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
+
+#define WLAN_CIPHER_SUITE_CKIP		0x00409600
+#define WLAN_CIPHER_SUITE_CKIP_CMIC	0x00409601
+#define WLAN_CIPHER_SUITE_CMIC		0x00409602
+#define WLAN_CIPHER_SUITE_KRK		0x004096FF /* for nl80211 use only */
+
+/* AKM suite selectors */
+#define WLAN_AKM_SUITE_8021X		0x000FAC01
+#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_8021X_SUITE_B	0x000FAC11
+#define WLAN_AKM_SUITE_8021X_SUITE_B_192	0x000FAC12
+#define WLAN_AKM_SUITE_CCKM		0x00409600
+#define WLAN_AKM_SUITE_OSEN		0x506f9a01
+
+
+/* IEEE 802.11v - WNM Action field values */
+enum wnm_action {
+	WNM_EVENT_REQ = 0,
+	WNM_EVENT_REPORT = 1,
+	WNM_DIAGNOSTIC_REQ = 2,
+	WNM_DIAGNOSTIC_REPORT = 3,
+	WNM_LOCATION_CFG_REQ = 4,
+	WNM_LOCATION_CFG_RESP = 5,
+	WNM_BSS_TRANS_MGMT_QUERY = 6,
+	WNM_BSS_TRANS_MGMT_REQ = 7,
+	WNM_BSS_TRANS_MGMT_RESP = 8,
+	WNM_FMS_REQ = 9,
+	WNM_FMS_RESP = 10,
+	WNM_COLLOCATED_INTERFERENCE_REQ = 11,
+	WNM_COLLOCATED_INTERFERENCE_REPORT = 12,
+	WNM_TFS_REQ = 13,
+	WNM_TFS_RESP = 14,
+	WNM_TFS_NOTIFY = 15,
+	WNM_SLEEP_MODE_REQ = 16,
+	WNM_SLEEP_MODE_RESP = 17,
+	WNM_TIM_BROADCAST_REQ = 18,
+	WNM_TIM_BROADCAST_RESP = 19,
+	WNM_QOS_TRAFFIC_CAPAB_UPDATE = 20,
+	WNM_CHANNEL_USAGE_REQ = 21,
+	WNM_CHANNEL_USAGE_RESP = 22,
+	WNM_DMS_REQ = 23,
+	WNM_DMS_RESP = 24,
+	WNM_TIMING_MEASUREMENT_REQ = 25,
+	WNM_NOTIFICATION_REQ = 26,
+	WNM_NOTIFICATION_RESP = 27
+};
+
+/* IEEE 802.11v - BSS Transition Management Request - Request Mode */
+#define WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED BIT(0)
+#define WNM_BSS_TM_REQ_ABRIDGED BIT(1)
+#define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2)
+#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3)
+#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4)
+
+/* IEEE Std 802.11-2012 - Table 8-253 */
+enum bss_trans_mgmt_status_code {
+	WNM_BSS_TM_ACCEPT = 0,
+	WNM_BSS_TM_REJECT_UNSPECIFIED = 1,
+	WNM_BSS_TM_REJECT_INSUFFICIENT_BEACON = 2,
+	WNM_BSS_TM_REJECT_INSUFFICIENT_CAPABITY = 3,
+	WNM_BSS_TM_REJECT_UNDESIRED = 4,
+	WNM_BSS_TM_REJECT_DELAY_REQUEST = 5,
+	WNM_BSS_TM_REJECT_STA_CANDIDATE_LIST_PROVIDED = 6,
+	WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES = 7,
+	WNM_BSS_TM_REJECT_LEAVING_ESS = 8
+};
+
+#define WNM_NEIGHBOR_TSF                         1
+#define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING    2
+#define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE    3
+#define WNM_NEIGHBOR_BSS_TERMINATION_DURATION    4
+#define WNM_NEIGHBOR_BEARING                     5
+#define WNM_NEIGHBOR_MEASUREMENT_PILOT          66
+#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)
+#define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ     BIT(2)
+#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ     BIT(3)
+#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_GRNT    BIT(4)
+
+struct ieee80211_2040_bss_coex_ie {
+	u8 element_id;
+	u8 length;
+	u8 coex_param;
+} STRUCT_PACKED;
+
+struct ieee80211_2040_intol_chan_report {
+	u8 element_id;
+	u8 length;
+	u8 op_class;
+	u8 variable[0];	/* Channel List */
+} STRUCT_PACKED;
+
+/* IEEE 802.11v - WNM-Sleep Mode element */
+struct wnm_sleep_element {
+	u8 eid;     /* WLAN_EID_WNMSLEEP */
+	u8 len;
+	u8 action_type; /* WNM_SLEEP_ENTER/WNM_SLEEP_MODE_EXIT */
+	u8 status;
+	le16 intval;
+} STRUCT_PACKED;
+
+#define WNM_SLEEP_MODE_ENTER 0
+#define WNM_SLEEP_MODE_EXIT 1
+
+enum wnm_sleep_mode_response_status {
+	WNM_STATUS_SLEEP_ACCEPT = 0,
+	WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1,
+	WNM_STATUS_DENIED_ACTION = 2,
+	WNM_STATUS_DENIED_TMP = 3,
+	WNM_STATUS_DENIED_KEY = 4,
+	WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5
+};
+
+/* WNM-Sleep Mode subelement IDs */
+enum wnm_sleep_mode_subelement_id {
+	WNM_SLEEP_SUBELEM_GTK = 0,
+	WNM_SLEEP_SUBELEM_IGTK = 1
+};
+
+/* Channel Switch modes (802.11h) */
+#define CHAN_SWITCH_MODE_ALLOW_TX	0
+#define CHAN_SWITCH_MODE_BLOCK_TX	1
+
+struct tpc_report {
+	u8 eid;
+	u8 len;
+	u8 tx_power;
+	u8 link_margin;
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */
+struct rrm_link_measurement_request {
+	u8 dialog_token;
+	s8 tx_power;
+	s8 max_tp;
+	u8 variable[0];
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11-2012, 8.5.7.5 - Link Measurement Report frame format */
+struct rrm_link_measurement_report {
+	u8 dialog_token;
+	struct tpc_report tpc;
+	u8 rx_ant_id;
+	u8 tx_ant_id;
+	u8 rcpi;
+	u8 rsni;
+	u8 variable[0];
+} STRUCT_PACKED;
+
+#define SSID_MAX_LEN 32
+
+/* IEEE Std 802.11ad-2012 - Multi-band element */
+struct multi_band_ie {
+	u8 eid; /* WLAN_EID_MULTI_BAND */
+	u8 len;
+	u8 mb_ctrl;
+	u8 band_id;
+	u8 op_class;
+	u8 chan;
+	u8 bssid[ETH_ALEN];
+	le16 beacon_int;
+	u8 tsf_offs[8];
+	u8 mb_connection_capability;
+	u8 fst_session_tmout;
+	/* Optional:
+	 *   STA MAC Address
+	 *   Pairwise Cipher Suite Count
+	 *   Pairwise Cipher Suite List
+	 */
+	u8 variable[0];
+} STRUCT_PACKED;
+
+enum mb_ctrl_sta_role {
+	MB_STA_ROLE_AP = 0,
+	MB_STA_ROLE_TDLS_STA = 1,
+	MB_STA_ROLE_IBSS_STA = 2,
+	MB_STA_ROLE_PCP = 3,
+	MB_STA_ROLE_NON_PCP_NON_AP = 4
+};
+
+#define MB_CTRL_ROLE_MASK (BIT(0) | BIT(1) | BIT(2))
+#define MB_CTRL_ROLE(ctrl) ((u8) ((ctrl) & MB_CTRL_ROLE_MASK))
+#define MB_CTRL_STA_MAC_PRESENT ((u8) (BIT(3)))
+#define MB_CTRL_PAIRWISE_CIPHER_SUITE_PRESENT ((u8) (BIT(4)))
+
+enum mb_band_id {
+	MB_BAND_ID_WIFI_2_4GHZ = 2, /* 2.4 GHz */
+	MB_BAND_ID_WIFI_5GHZ = 4, /* 4.9 and 5 GHz */
+	MB_BAND_ID_WIFI_60GHZ = 5, /* 60 GHz */
+};
+
+#define MB_CONNECTION_CAPABILITY_AP ((u8) (BIT(0)))
+#define MB_CONNECTION_CAPABILITY_PCP ((u8) (BIT(1)))
+#define MB_CONNECTION_CAPABILITY_DLS ((u8) (BIT(2)))
+#define MB_CONNECTION_CAPABILITY_TDLS ((u8) (BIT(3)))
+#define MB_CONNECTION_CAPABILITY_IBSS ((u8) (BIT(4)))
+
+/* IEEE Std 802.11ad-2014 - FST Action field */
+enum fst_action {
+	FST_ACTION_SETUP_REQUEST = 0,
+	FST_ACTION_SETUP_RESPONSE = 1,
+	FST_ACTION_TEAR_DOWN = 2,
+	FST_ACTION_ACK_REQUEST = 3,
+	FST_ACTION_ACK_RESPONSE = 4,
+	FST_ACTION_ON_CHANNEL_TUNNEL = 5,
+};
+
+#endif /* IEEE802_11_DEFS_H */
diff --git a/hostap/src/common/ieee802_1x_defs.h b/hostap/src/common/ieee802_1x_defs.h
new file mode 100644
index 0000000..cc88caa
--- /dev/null
+++ b/hostap/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/hostap/src/common/privsep_commands.h b/hostap/src/common/privsep_commands.h
new file mode 100644
index 0000000..8dff303
--- /dev/null
+++ b/hostap/src/common/privsep_commands.h
@@ -0,0 +1,104 @@
+/*
+ * WPA Supplicant - privilege separation commands
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PRIVSEP_COMMANDS_H
+#define PRIVSEP_COMMANDS_H
+
+#include "common/ieee802_11_defs.h"
+
+enum privsep_cmd {
+	PRIVSEP_CMD_REGISTER,
+	PRIVSEP_CMD_UNREGISTER,
+	PRIVSEP_CMD_SCAN,
+	PRIVSEP_CMD_GET_SCAN_RESULTS,
+	PRIVSEP_CMD_ASSOCIATE,
+	PRIVSEP_CMD_GET_BSSID,
+	PRIVSEP_CMD_GET_SSID,
+	PRIVSEP_CMD_SET_KEY,
+	PRIVSEP_CMD_GET_CAPA,
+	PRIVSEP_CMD_L2_REGISTER,
+	PRIVSEP_CMD_L2_UNREGISTER,
+	PRIVSEP_CMD_L2_NOTIFY_AUTH_START,
+	PRIVSEP_CMD_L2_SEND,
+	PRIVSEP_CMD_SET_COUNTRY,
+	PRIVSEP_CMD_AUTHENTICATE,
+};
+
+struct privsep_cmd_authenticate
+{
+	int freq;
+	u8 bssid[ETH_ALEN];
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	int auth_alg;
+	size_t ie_len;
+	u8 wep_key[4][16];
+	size_t wep_key_len[4];
+	int wep_tx_keyidx;
+	int local_state_change;
+	int p2p;
+	size_t sae_data_len;
+	/* followed by ie_len bytes of ie */
+	/* followed by sae_data_len bytes of sae_data */
+};
+
+struct privsep_cmd_associate
+{
+	u8 bssid[ETH_ALEN];
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	int hwmode;
+	int freq;
+	int channel;
+	int pairwise_suite;
+	int group_suite;
+	int key_mgmt_suite;
+	int auth_alg;
+	int mode;
+	size_t wpa_ie_len;
+	/* followed by wpa_ie_len bytes of wpa_ie */
+};
+
+struct privsep_cmd_set_key
+{
+	int alg;
+	u8 addr[ETH_ALEN];
+	int key_idx;
+	int set_tx;
+	u8 seq[8];
+	size_t seq_len;
+	u8 key[32];
+	size_t key_len;
+};
+
+enum privsep_event {
+	PRIVSEP_EVENT_SCAN_RESULTS,
+	PRIVSEP_EVENT_ASSOC,
+	PRIVSEP_EVENT_DISASSOC,
+	PRIVSEP_EVENT_ASSOCINFO,
+	PRIVSEP_EVENT_MICHAEL_MIC_FAILURE,
+	PRIVSEP_EVENT_INTERFACE_STATUS,
+	PRIVSEP_EVENT_PMKID_CANDIDATE,
+	PRIVSEP_EVENT_STKSTART,
+	PRIVSEP_EVENT_FT_RESPONSE,
+	PRIVSEP_EVENT_RX_EAPOL,
+	PRIVSEP_EVENT_SCAN_STARTED,
+	PRIVSEP_EVENT_AUTH,
+};
+
+struct privsep_event_auth {
+	u8 peer[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
+	u16 auth_type;
+	u16 auth_transaction;
+	u16 status_code;
+	size_t ies_len;
+	/* followed by ies_len bytes of ies */
+};
+
+#endif /* PRIVSEP_COMMANDS_H */
diff --git a/hostap/src/common/qca-vendor-attr.h b/hostap/src/common/qca-vendor-attr.h
new file mode 100644
index 0000000..6f51803
--- /dev/null
+++ b/hostap/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/hostap/src/common/qca-vendor.h b/hostap/src/common/qca-vendor.h
new file mode 100644
index 0000000..28985f5
--- /dev/null
+++ b/hostap/src/common/qca-vendor.h
@@ -0,0 +1,357 @@
+/*
+ * Qualcomm Atheros OUI and vendor specific assignments
+ * Copyright (c) 2014-2015, 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_ROAMING: Set roaming policy for drivers that use
+ *	internal BSS-selection. This command uses
+ *	@QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY to specify the new roaming policy
+ *	for the current connection (i.e., changes policy set by the nl80211
+ *	Connect command). @QCA_WLAN_VENDOR_ATTR_MAC_ADDR may optionally be
+ *	included to indicate which BSS to use in case roaming is disabled.
+ *
+ * @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.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be
+ *	used to configure PMK to the driver even when not connected. This can
+ *	be used to request offloading of key management operations. Only used
+ *	if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: An extended version of
+ *	NL80211_CMD_ROAM event with optional attributes including information
+ *	from offloaded key management operation. Uses
+ *	enum qca_wlan_vendor_attr_roam_auth attributes. Only used
+ *	if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DO_ACS: ACS command/event which is used to
+ *	invoke the ACS function in device and pass selected channels to
+ *	hostapd.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: Command to get the features
+ *	supported by the driver. enum qca_wlan_vendor_features defines
+ *	the possible features.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: Event used by driver,
+ *	which supports DFS offloading, to indicate a channel availability check
+ *	start.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED: Event used by driver,
+ *	which supports DFS offloading, to indicate a channel availability check
+ *	completion.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED: Event used by driver,
+ *	which supports DFS offloading, to indicate that the channel availability
+ *	check aborted, no change to the channel status.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED: Event used by
+ *	driver, which supports DFS offloading, to indicate that the
+ *	Non-Occupancy Period for this channel is over, channel becomes usable.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: Event used by driver,
+ *	which supports DFS offloading, to indicate a radar pattern has been
+ *	detected. The channel is now unusable.
+ */
+enum qca_nl80211_vendor_subcmds {
+	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
+	QCA_NL80211_VENDOR_SUBCMD_TEST = 1,
+	/* subcmds 2..8 not yet allocated */
+	QCA_NL80211_VENDOR_SUBCMD_ROAMING = 9,
+	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,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_RADIO_RESULTS = 17,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_IFACE_RESULTS = 18,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_PEERS_RESULTS = 19,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_START = 20,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_STOP = 21,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS = 22,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CAPABILITIES = 23,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS = 24,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE = 25,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT = 26,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT = 27,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND = 28,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_BSSID_HOTLIST = 29,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_BSSID_HOTLIST = 30,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE = 31,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SIGNIFICANT_CHANGE = 32,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SIGNIFICANT_CHANGE = 33,
+	QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE = 34,
+	QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE = 35,
+	QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS = 36,
+	QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE = 37,
+	QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_FEATURES = 38,
+	QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI = 39,
+	QCA_NL80211_VENDOR_SUBCMD_NO_DFS_FLAG = 40,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_LOST = 41,
+	QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX = 42,
+	/* 43..49 - reserved for QCA */
+	QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY = 50,
+	QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH = 51,
+	QCA_NL80211_VENDOR_SUBCMD_APFIND = 52,
+	/* 53 - reserved - was used by QCA, but not in use anymore */
+	QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54,
+	QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES = 55,
+	QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED = 56,
+	QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED = 57,
+	QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED = 58,
+	QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED = 59,
+	QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60,
+	/* 61-90 - reserved for QCA */
+	QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91,
+	QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92,
+	QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93,
+	QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT = 94,
+	QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT = 95,
+	QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER = 96,
+	QCA_NL80211_VENDOR_SUBCMD_DCC_GET_STATS = 97,
+	QCA_NL80211_VENDOR_SUBCMD_DCC_CLEAR_STATS = 98,
+	QCA_NL80211_VENDOR_SUBCMD_DCC_UPDATE_NDL = 99,
+	QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT = 100,
+	QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES = 101,
+	QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG = 102,
+	QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103,
+	QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104,
+	QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105,
+};
+
+
+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,
+	/* used by QCA_NL80211_VENDOR_SUBCMD_ROAMING, u32 with values defined
+	 * by enum qca_roaming_policy. */
+	QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5,
+	QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6,
+	/* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
+	QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7,
+	QCA_WLAN_VENDOR_ATTR_TEST = 8,
+	/* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
+	/* Unsigned 32-bit value. */
+	QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA = 9,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND = 10,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11,
+	/* Unsigned 32-bit value from enum qca_set_band. */
+	QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_MAX	= QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
+};
+
+
+enum qca_roaming_policy {
+	QCA_ROAMING_NOT_ALLOWED,
+	QCA_ROAMING_ALLOWED_WITHIN_ESS,
+};
+
+enum qca_wlan_vendor_attr_roam_auth {
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1
+};
+
+enum qca_wlan_vendor_attr_acs_offload {
+	QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL,
+	QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL,
+	QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE,
+	QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED,
+	QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED,
+	QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED,
+	QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
+	QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST,
+	QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL,
+	QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL,
+	QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_ACS_MAX =
+	QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST - 1
+};
+
+enum qca_wlan_vendor_acs_hw_mode {
+	QCA_ACS_MODE_IEEE80211B,
+	QCA_ACS_MODE_IEEE80211G,
+	QCA_ACS_MODE_IEEE80211A,
+	QCA_ACS_MODE_IEEE80211AD,
+	QCA_ACS_MODE_IEEE80211ANY,
+};
+
+/**
+ * enum qca_wlan_vendor_features - Vendor device/driver feature flags
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD: Device supports key
+ *	management offload, a mechanism where the station's firmware
+ *	does the exchange with the AP to establish the temporal keys
+ *	after roaming, rather than having the user space wpa_supplicant do it.
+ * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic
+ *	band selection based on channel selection results.
+ * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
+ */
+enum qca_wlan_vendor_features {
+	QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD	= 0,
+	QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY     = 1,
+	NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
+};
+
+/**
+ * enum qca_wlan_vendor_attr_data_offload_ind - Vendor Data Offload Indication
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_SESSION: Session corresponding to
+ *	the offloaded data.
+ * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_PROTOCOL: Protocol of the offloaded
+ *	data.
+ * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_EVENT: Event type for the data offload
+ *	indication.
+ */
+enum qca_wlan_vendor_attr_data_offload_ind {
+	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_SESSION,
+	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_PROTOCOL,
+	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_EVENT,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_MAX =
+	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1
+};
+
+enum qca_vendor_attr_get_preferred_freq_list {
+	QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID,
+	/* A 32-unsigned value; the interface type/mode for which the preferred
+	 * frequency list is requested (see enum qca_iface_type for possible
+	 * values); used in GET_PREFERRED_FREQ_LIST command from user-space to
+	 * kernel and in the kernel response back to user-space.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
+	/* An array of 32-unsigned values; values are frequency (MHz); sent
+	 * from kernel space to user space.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX =
+	QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST - 1
+};
+
+enum qca_vendor_attr_probable_oper_channel {
+	QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_INVALID,
+	/* 32-bit unsigned value; indicates the connection/iface type likely to
+	 * come on this channel (see enum qca_iface_type).
+	 */
+	QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE,
+	/* 32-bit unsigned value; the frequency (MHz) of the probable channel */
+	QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX =
+	QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST - 1
+};
+
+enum qca_iface_type {
+	QCA_IFACE_TYPE_STA,
+	QCA_IFACE_TYPE_AP,
+	QCA_IFACE_TYPE_P2P_CLIENT,
+	QCA_IFACE_TYPE_P2P_GO,
+	QCA_IFACE_TYPE_IBSS,
+	QCA_IFACE_TYPE_TDLS,
+};
+
+enum qca_set_band {
+	QCA_SETBAND_AUTO,
+	QCA_SETBAND_5G,
+	QCA_SETBAND_2G,
+};
+
+/* IEEE 802.11 Vendor Specific elements */
+
+/**
+ * enum qca_vendor_element_id - QCA Vendor Specific element types
+ *
+ * These values are used to identify QCA Vendor Specific elements. The
+ * payload of the element starts with the three octet OUI (OUI_QCA) and
+ * is followed by a single octet type which is defined by this enum.
+ *
+ * @QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: P2P preferred channel list.
+ *	This element can be used to specify preference order for supported
+ *	channels. The channels in this list are in preference order (the first
+ *	one has the highest preference) and are described as a pair of
+ *	(global) Operating Class and Channel Number (each one octet) fields.
+ *
+ *	This extends the standard P2P functionality by providing option to have
+ *	more than one preferred operating channel. When this element is present,
+ *	it replaces the preference indicated in the Operating Channel attribute.
+ *	For supporting other implementations, the Operating Channel attribute is
+ *	expected to be used with the highest preference channel. Similarly, all
+ *	the channels included in this Preferred channel list element are
+ *	expected to be included in the Channel List attribute.
+ *
+ *	This vendor element may be included in GO Negotiation Request, P2P
+ *	Invitation Request, and Provision Discovery Request frames.
+ */
+enum qca_vendor_element_id {
+	QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0,
+};
+
+#endif /* QCA_VENDOR_H */
diff --git a/hostap/src/common/sae.c b/hostap/src/common/sae.c
new file mode 100644
index 0000000..503fa1d
--- /dev/null
+++ b/hostap/src/common/sae.c
@@ -0,0 +1,1292 @@
+/*
+ * Simultaneous authentication of equals
+ * Copyright (c) 2012-2015, 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/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "crypto/dh_groups.h"
+#include "ieee802_11_defs.h"
+#include "sae.h"
+
+
+int sae_set_group(struct sae_data *sae, int group)
+{
+	struct sae_temporary_data *tmp;
+
+	sae_clear_data(sae);
+	tmp = sae->tmp = os_zalloc(sizeof(*tmp));
+	if (tmp == NULL)
+		return -1;
+
+	/* First, check if this is an ECC group */
+	tmp->ec = crypto_ec_init(group);
+	if (tmp->ec) {
+		sae->group = group;
+		tmp->prime_len = crypto_ec_prime_len(tmp->ec);
+		tmp->prime = crypto_ec_get_prime(tmp->ec);
+		tmp->order = crypto_ec_get_order(tmp->ec);
+		return 0;
+	}
+
+	/* Not an ECC group, check FFC */
+	tmp->dh = dh_groups_get(group);
+	if (tmp->dh) {
+		sae->group = group;
+		tmp->prime_len = tmp->dh->prime_len;
+		if (tmp->prime_len > SAE_MAX_PRIME_LEN) {
+			sae_clear_data(sae);
+			return -1;
+		}
+
+		tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime,
+							tmp->prime_len);
+		if (tmp->prime_buf == NULL) {
+			sae_clear_data(sae);
+			return -1;
+		}
+		tmp->prime = tmp->prime_buf;
+
+		tmp->order_buf = crypto_bignum_init_set(tmp->dh->order,
+							tmp->dh->order_len);
+		if (tmp->order_buf == NULL) {
+			sae_clear_data(sae);
+			return -1;
+		}
+		tmp->order = tmp->order_buf;
+
+		return 0;
+	}
+
+	/* Unsupported group */
+	return -1;
+}
+
+
+void sae_clear_temp_data(struct sae_data *sae)
+{
+	struct sae_temporary_data *tmp;
+	if (sae == NULL || sae->tmp == NULL)
+		return;
+	tmp = sae->tmp;
+	crypto_ec_deinit(tmp->ec);
+	crypto_bignum_deinit(tmp->prime_buf, 0);
+	crypto_bignum_deinit(tmp->order_buf, 0);
+	crypto_bignum_deinit(tmp->sae_rand, 1);
+	crypto_bignum_deinit(tmp->pwe_ffc, 1);
+	crypto_bignum_deinit(tmp->own_commit_scalar, 0);
+	crypto_bignum_deinit(tmp->own_commit_element_ffc, 0);
+	crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0);
+	crypto_ec_point_deinit(tmp->pwe_ecc, 1);
+	crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0);
+	crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0);
+	wpabuf_free(tmp->anti_clogging_token);
+	bin_clear_free(tmp, sizeof(*tmp));
+	sae->tmp = NULL;
+}
+
+
+void sae_clear_data(struct sae_data *sae)
+{
+	if (sae == NULL)
+		return;
+	sae_clear_temp_data(sae);
+	crypto_bignum_deinit(sae->peer_commit_scalar, 0);
+	os_memset(sae, 0, sizeof(*sae));
+}
+
+
+static void buf_shift_right(u8 *buf, size_t len, size_t bits)
+{
+	size_t i;
+	for (i = len - 1; i > 0; i--)
+		buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
+	buf[0] >>= bits;
+}
+
+
+static struct crypto_bignum * sae_get_rand(struct sae_data *sae)
+{
+	u8 val[SAE_MAX_PRIME_LEN];
+	int iter = 0;
+	struct crypto_bignum *bn = NULL;
+	int order_len_bits = crypto_bignum_bits(sae->tmp->order);
+	size_t order_len = (order_len_bits + 7) / 8;
+
+	if (order_len > sizeof(val))
+		return NULL;
+
+	for (;;) {
+		if (iter++ > 100 || random_get_bytes(val, order_len) < 0)
+			return NULL;
+		if (order_len_bits % 8)
+			buf_shift_right(val, order_len, 8 - order_len_bits % 8);
+		bn = crypto_bignum_init_set(val, order_len);
+		if (bn == NULL)
+			return NULL;
+		if (crypto_bignum_is_zero(bn) ||
+		    crypto_bignum_is_one(bn) ||
+		    crypto_bignum_cmp(bn, sae->tmp->order) >= 0) {
+			crypto_bignum_deinit(bn, 0);
+			continue;
+		}
+		break;
+	}
+
+	os_memset(val, 0, order_len);
+	return bn;
+}
+
+
+static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae)
+{
+	crypto_bignum_deinit(sae->tmp->sae_rand, 1);
+	sae->tmp->sae_rand = sae_get_rand(sae);
+	if (sae->tmp->sae_rand == NULL)
+		return NULL;
+	return sae_get_rand(sae);
+}
+
+
+static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key)
+{
+	wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR
+		   " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2));
+	if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) {
+		os_memcpy(key, addr1, ETH_ALEN);
+		os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN);
+	} else {
+		os_memcpy(key, addr2, ETH_ALEN);
+		os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN);
+	}
+}
+
+
+static struct crypto_bignum *
+get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits,
+		  int *r_odd)
+{
+	for (;;) {
+		struct crypto_bignum *r;
+		u8 tmp[SAE_MAX_ECC_PRIME_LEN];
+
+		if (random_get_bytes(tmp, prime_len) < 0)
+			break;
+		if (prime_bits % 8)
+			buf_shift_right(tmp, prime_len, 8 - prime_bits % 8);
+		if (os_memcmp(tmp, prime, prime_len) >= 0)
+			continue;
+		r = crypto_bignum_init_set(tmp, prime_len);
+		if (!r)
+			break;
+		if (crypto_bignum_is_zero(r)) {
+			crypto_bignum_deinit(r, 0);
+			continue;
+		}
+
+		*r_odd = tmp[prime_len - 1] & 0x01;
+		return r;
+	}
+
+	return NULL;
+}
+
+
+static int is_quadratic_residue_blind(struct sae_data *sae,
+				      const u8 *prime, size_t bits,
+				      const struct crypto_bignum *qr,
+				      const struct crypto_bignum *qnr,
+				      const struct crypto_bignum *y_sqr)
+{
+	struct crypto_bignum *r, *num;
+	int r_odd, check, res = -1;
+
+	/*
+	 * Use the blinding technique to mask y_sqr while determining
+	 * whether it is a quadratic residue modulo p to avoid leaking
+	 * timing information while determining the Legendre symbol.
+	 *
+	 * v = y_sqr
+	 * r = a random number between 1 and p-1, inclusive
+	 * num = (v * r * r) modulo p
+	 */
+	r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd);
+	if (!r)
+		return -1;
+
+	num = crypto_bignum_init();
+	if (!num ||
+	    crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 ||
+	    crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0)
+		goto fail;
+
+	if (r_odd) {
+		/*
+		 * num = (num * qr) module p
+		 * LGR(num, p) = 1 ==> quadratic residue
+		 */
+		if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0)
+			goto fail;
+		check = 1;
+	} else {
+		/*
+		 * num = (num * qnr) module p
+		 * LGR(num, p) = -1 ==> quadratic residue
+		 */
+		if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0)
+			goto fail;
+		check = -1;
+	}
+
+	res = crypto_bignum_legendre(num, sae->tmp->prime);
+	if (res == -2) {
+		res = -1;
+		goto fail;
+	}
+	res = res == check;
+fail:
+	crypto_bignum_deinit(num, 1);
+	crypto_bignum_deinit(r, 1);
+	return res;
+}
+
+
+static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
+				 const u8 *prime,
+				 const struct crypto_bignum *qr,
+				 const struct crypto_bignum *qnr,
+				 struct crypto_bignum **ret_x_cand)
+{
+	u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
+	struct crypto_bignum *y_sqr, *x_cand;
+	int res;
+	size_t bits;
+
+	*ret_x_cand = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
+
+	/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
+	bits = crypto_ec_prime_len_bits(sae->tmp->ec);
+	sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
+			prime, sae->tmp->prime_len, pwd_value, bits);
+	if (bits % 8)
+		buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
+			pwd_value, sae->tmp->prime_len);
+
+	if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0)
+		return 0;
+
+	x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
+	if (!x_cand)
+		return -1;
+	y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
+	if (!y_sqr) {
+		crypto_bignum_deinit(x_cand, 1);
+		return -1;
+	}
+
+	res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
+	crypto_bignum_deinit(y_sqr, 1);
+	if (res <= 0) {
+		crypto_bignum_deinit(x_cand, 1);
+		return res;
+	}
+
+	*ret_x_cand = x_cand;
+	return 1;
+}
+
+
+static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
+				 struct crypto_bignum *pwe)
+{
+	u8 pwd_value[SAE_MAX_PRIME_LEN];
+	size_t bits = sae->tmp->prime_len * 8;
+	u8 exp[1];
+	struct crypto_bignum *a, *b;
+	int res;
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
+
+	/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
+	sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
+			sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value,
+			bits);
+	if (bits % 8)
+		buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
+			sae->tmp->prime_len);
+
+	if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
+	{
+		wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
+		return 0;
+	}
+
+	/* PWE = pwd-value^((p-1)/r) modulo p */
+
+	a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
+
+	if (sae->tmp->dh->safe_prime) {
+		/*
+		 * r = (p-1)/2 for the group used here, so this becomes:
+		 * PWE = pwd-value^2 modulo p
+		 */
+		exp[0] = 2;
+		b = crypto_bignum_init_set(exp, sizeof(exp));
+	} else {
+		/* Calculate exponent: (p-1)/r */
+		exp[0] = 1;
+		b = crypto_bignum_init_set(exp, sizeof(exp));
+		if (b == NULL ||
+		    crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
+		    crypto_bignum_div(b, sae->tmp->order, b) < 0) {
+			crypto_bignum_deinit(b, 0);
+			b = NULL;
+		}
+	}
+
+	if (a == NULL || b == NULL)
+		res = -1;
+	else
+		res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
+
+	crypto_bignum_deinit(a, 0);
+	crypto_bignum_deinit(b, 0);
+
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
+		return -1;
+	}
+
+	/* if (PWE > 1) --> found */
+	if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
+		wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE: PWE found");
+	return 1;
+}
+
+
+static int get_random_qr_qnr(const u8 *prime, size_t prime_len,
+			     const struct crypto_bignum *prime_bn,
+			     size_t prime_bits, struct crypto_bignum **qr,
+			     struct crypto_bignum **qnr)
+{
+	*qr = NULL;
+	*qnr = NULL;
+
+	while (!(*qr) || !(*qnr)) {
+		u8 tmp[SAE_MAX_ECC_PRIME_LEN];
+		struct crypto_bignum *q;
+		int res;
+
+		if (random_get_bytes(tmp, prime_len) < 0)
+			break;
+		if (prime_bits % 8)
+			buf_shift_right(tmp, prime_len, 8 - prime_bits % 8);
+		if (os_memcmp(tmp, prime, prime_len) >= 0)
+			continue;
+		q = crypto_bignum_init_set(tmp, prime_len);
+		if (!q)
+			break;
+		res = crypto_bignum_legendre(q, prime_bn);
+
+		if (res == 1 && !(*qr))
+			*qr = q;
+		else if (res == -1 && !(*qnr))
+			*qnr = q;
+		else
+			crypto_bignum_deinit(q, 0);
+	}
+
+	return (*qr && *qnr) ? 0 : -1;
+}
+
+
+static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
+			      const u8 *addr2, const u8 *password,
+			      size_t password_len)
+{
+	u8 counter, k = 40;
+	u8 addrs[2 * ETH_ALEN];
+	const u8 *addr[2];
+	size_t len[2];
+	u8 dummy_password[32];
+	size_t dummy_password_len;
+	int pwd_seed_odd = 0;
+	u8 prime[SAE_MAX_ECC_PRIME_LEN];
+	size_t prime_len;
+	struct crypto_bignum *x = NULL, *qr, *qnr;
+	size_t bits;
+	int res;
+
+	dummy_password_len = password_len;
+	if (dummy_password_len > sizeof(dummy_password))
+		dummy_password_len = sizeof(dummy_password);
+	if (random_get_bytes(dummy_password, dummy_password_len) < 0)
+		return -1;
+
+	prime_len = sae->tmp->prime_len;
+	if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
+				 prime_len) < 0)
+		return -1;
+	bits = crypto_ec_prime_len_bits(sae->tmp->ec);
+
+	/*
+	 * Create a random quadratic residue (qr) and quadratic non-residue
+	 * (qnr) modulo p for blinding purposes during the loop.
+	 */
+	if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
+			      &qr, &qnr) < 0)
+		return -1;
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+			      password, password_len);
+
+	/*
+	 * H(salt, ikm) = HMAC-SHA256(salt, ikm)
+	 * base = password
+	 * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
+	 *              base || counter)
+	 */
+	sae_pwd_seed_key(addr1, addr2, addrs);
+
+	addr[0] = password;
+	len[0] = password_len;
+	addr[1] = &counter;
+	len[1] = sizeof(counter);
+
+	/*
+	 * Continue for at least k iterations to protect against side-channel
+	 * attacks that attempt to determine the number of iterations required
+	 * in the loop.
+	 */
+	for (counter = 1; counter <= k || !x; counter++) {
+		u8 pwd_seed[SHA256_MAC_LEN];
+		struct crypto_bignum *x_cand;
+
+		if (counter > 200) {
+			/* This should not happen in practice */
+			wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+		if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
+				       pwd_seed) < 0)
+			break;
+
+		res = sae_test_pwd_seed_ecc(sae, pwd_seed,
+					    prime, qr, qnr, &x_cand);
+		if (res < 0)
+			goto fail;
+		if (res > 0 && !x) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Selected pwd-seed with counter %u",
+				   counter);
+			x = x_cand;
+			pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
+			os_memset(pwd_seed, 0, sizeof(pwd_seed));
+
+			/*
+			 * Use a dummy password for the following rounds, if
+			 * any.
+			 */
+			addr[0] = dummy_password;
+			len[0] = dummy_password_len;
+		} else if (res > 0) {
+			crypto_bignum_deinit(x_cand, 1);
+		}
+	}
+
+	if (!x) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
+		res = -1;
+		goto fail;
+	}
+
+	if (!sae->tmp->pwe_ecc)
+		sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
+	if (!sae->tmp->pwe_ecc)
+		res = -1;
+	else
+		res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
+						    sae->tmp->pwe_ecc, x,
+						    pwd_seed_odd);
+	crypto_bignum_deinit(x, 1);
+	if (res < 0) {
+		/*
+		 * This should not happen since we already checked that there
+		 * is a result.
+		 */
+		wpa_printf(MSG_DEBUG, "SAE: Could not solve y");
+	}
+
+fail:
+	crypto_bignum_deinit(qr, 0);
+	crypto_bignum_deinit(qnr, 0);
+
+	return res;
+}
+
+
+static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
+			      const u8 *addr2, const u8 *password,
+			      size_t password_len)
+{
+	u8 counter;
+	u8 addrs[2 * ETH_ALEN];
+	const u8 *addr[2];
+	size_t len[2];
+	int found = 0;
+
+	if (sae->tmp->pwe_ffc == NULL) {
+		sae->tmp->pwe_ffc = crypto_bignum_init();
+		if (sae->tmp->pwe_ffc == NULL)
+			return -1;
+	}
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+			      password, password_len);
+
+	/*
+	 * H(salt, ikm) = HMAC-SHA256(salt, ikm)
+	 * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
+	 *              password || counter)
+	 */
+	sae_pwd_seed_key(addr1, addr2, addrs);
+
+	addr[0] = password;
+	len[0] = password_len;
+	addr[1] = &counter;
+	len[1] = sizeof(counter);
+
+	for (counter = 1; !found; counter++) {
+		u8 pwd_seed[SHA256_MAC_LEN];
+		int res;
+
+		if (counter > 200) {
+			/* This should not happen in practice */
+			wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+		if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
+				       pwd_seed) < 0)
+			break;
+		res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
+		if (res < 0)
+			break;
+		if (res > 0) {
+			wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
+			found = 1;
+		}
+	}
+
+	return found ? 0 : -1;
+}
+
+
+static int sae_derive_commit_element_ecc(struct sae_data *sae,
+					 struct crypto_bignum *mask)
+{
+	/* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
+	if (!sae->tmp->own_commit_element_ecc) {
+		sae->tmp->own_commit_element_ecc =
+			crypto_ec_point_init(sae->tmp->ec);
+		if (!sae->tmp->own_commit_element_ecc)
+			return -1;
+	}
+
+	if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask,
+				sae->tmp->own_commit_element_ecc) < 0 ||
+	    crypto_ec_point_invert(sae->tmp->ec,
+				   sae->tmp->own_commit_element_ecc) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int sae_derive_commit_element_ffc(struct sae_data *sae,
+					 struct crypto_bignum *mask)
+{
+	/* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
+	if (!sae->tmp->own_commit_element_ffc) {
+		sae->tmp->own_commit_element_ffc = crypto_bignum_init();
+		if (!sae->tmp->own_commit_element_ffc)
+			return -1;
+	}
+
+	if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime,
+				  sae->tmp->own_commit_element_ffc) < 0 ||
+	    crypto_bignum_inverse(sae->tmp->own_commit_element_ffc,
+				  sae->tmp->prime,
+				  sae->tmp->own_commit_element_ffc) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int sae_derive_commit(struct sae_data *sae)
+{
+	struct crypto_bignum *mask;
+	int ret = -1;
+	unsigned int counter = 0;
+
+	do {
+		counter++;
+		if (counter > 100) {
+			/*
+			 * This cannot really happen in practice if the random
+			 * number generator is working. Anyway, to avoid even a
+			 * theoretical infinite loop, break out after 100
+			 * attemps.
+			 */
+			return -1;
+		}
+
+		mask = sae_get_rand_and_mask(sae);
+		if (mask == NULL) {
+			wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask");
+			return -1;
+		}
+
+		/* commit-scalar = (rand + mask) modulo r */
+		if (!sae->tmp->own_commit_scalar) {
+			sae->tmp->own_commit_scalar = crypto_bignum_init();
+			if (!sae->tmp->own_commit_scalar)
+				goto fail;
+		}
+		crypto_bignum_add(sae->tmp->sae_rand, mask,
+				  sae->tmp->own_commit_scalar);
+		crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order,
+				  sae->tmp->own_commit_scalar);
+	} while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) ||
+		 crypto_bignum_is_one(sae->tmp->own_commit_scalar));
+
+	if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) ||
+	    (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0))
+		goto fail;
+
+	ret = 0;
+fail:
+	crypto_bignum_deinit(mask, 1);
+	return ret;
+}
+
+
+int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
+		       const u8 *password, size_t password_len,
+		       struct sae_data *sae)
+{
+	if (sae->tmp == NULL ||
+	    (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
+						password_len) < 0) ||
+	    (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
+						password_len) < 0) ||
+	    sae_derive_commit(sae) < 0)
+		return -1;
+	return 0;
+}
+
+
+static int sae_derive_k_ecc(struct sae_data *sae, u8 *k)
+{
+	struct crypto_ec_point *K;
+	int ret = -1;
+
+	K = crypto_ec_point_init(sae->tmp->ec);
+	if (K == NULL)
+		goto fail;
+
+	/*
+	 * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
+	 *                                        PEER-COMMIT-ELEMENT)))
+	 * If K is identity element (point-at-infinity), reject
+	 * k = F(K) (= x coordinate)
+	 */
+
+	if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc,
+				sae->peer_commit_scalar, K) < 0 ||
+	    crypto_ec_point_add(sae->tmp->ec, K,
+				sae->tmp->peer_commit_element_ecc, K) < 0 ||
+	    crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 ||
+	    crypto_ec_point_is_at_infinity(sae->tmp->ec, K) ||
+	    crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len);
+
+	ret = 0;
+fail:
+	crypto_ec_point_deinit(K, 1);
+	return ret;
+}
+
+
+static int sae_derive_k_ffc(struct sae_data *sae, u8 *k)
+{
+	struct crypto_bignum *K;
+	int ret = -1;
+
+	K = crypto_bignum_init();
+	if (K == NULL)
+		goto fail;
+
+	/*
+	 * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
+	 *                                        PEER-COMMIT-ELEMENT)))
+	 * If K is identity element (one), reject.
+	 * k = F(K) (= x coordinate)
+	 */
+
+	if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar,
+				  sae->tmp->prime, K) < 0 ||
+	    crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc,
+				 sae->tmp->prime, K) < 0 ||
+	    crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0
+	    ||
+	    crypto_bignum_is_one(K) ||
+	    crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) <
+	    0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len);
+
+	ret = 0;
+fail:
+	crypto_bignum_deinit(K, 1);
+	return ret;
+}
+
+
+static int sae_derive_keys(struct sae_data *sae, const u8 *k)
+{
+	u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN];
+	u8 keyseed[SHA256_MAC_LEN];
+	u8 keys[SAE_KCK_LEN + SAE_PMK_LEN];
+	struct crypto_bignum *tmp;
+	int ret = -1;
+
+	tmp = crypto_bignum_init();
+	if (tmp == NULL)
+		goto fail;
+
+	/* keyseed = H(<0>32, k)
+	 * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK",
+	 *                      (commit-scalar + peer-commit-scalar) modulo r)
+	 * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
+	 */
+
+	os_memset(null_key, 0, sizeof(null_key));
+	hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len,
+		    keyseed);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed));
+
+	crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar,
+			  tmp);
+	crypto_bignum_mod(tmp, sae->tmp->order, tmp);
+	crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len);
+	wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
+	sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
+		   val, sae->tmp->prime_len, keys, sizeof(keys));
+	os_memset(keyseed, 0, sizeof(keyseed));
+	os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN);
+	os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN);
+	os_memset(keys, 0, sizeof(keys));
+	wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
+
+	ret = 0;
+fail:
+	crypto_bignum_deinit(tmp, 0);
+	return ret;
+}
+
+
+int sae_process_commit(struct sae_data *sae)
+{
+	u8 k[SAE_MAX_PRIME_LEN];
+	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;
+	return 0;
+}
+
+
+void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
+		      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);
+		wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token",
+			    wpabuf_head(token), wpabuf_len(token));
+	}
+	pos = wpabuf_put(buf, sae->tmp->prime_len);
+	crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos,
+			     sae->tmp->prime_len, sae->tmp->prime_len);
+	wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar",
+		    pos, sae->tmp->prime_len);
+	if (sae->tmp->ec) {
+		pos = wpabuf_put(buf, 2 * sae->tmp->prime_len);
+		crypto_ec_point_to_bin(sae->tmp->ec,
+				       sae->tmp->own_commit_element_ecc,
+				       pos, pos + sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)",
+			    pos, sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)",
+			    pos + sae->tmp->prime_len, sae->tmp->prime_len);
+	} else {
+		pos = wpabuf_put(buf, sae->tmp->prime_len);
+		crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos,
+				     sae->tmp->prime_len, sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: own commit-element",
+			    pos, sae->tmp->prime_len);
+	}
+}
+
+
+u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group)
+{
+	if (allowed_groups) {
+		int i;
+		for (i = 0; allowed_groups[i] > 0; i++) {
+			if (allowed_groups[i] == group)
+				break;
+		}
+		if (allowed_groups[i] != group) {
+			wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not "
+				   "enabled in the current configuration",
+				   group);
+			return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+		}
+	}
+
+	if (sae->state == SAE_COMMITTED && group != sae->group) {
+		wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed");
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	}
+
+	if (group != sae->group && sae_set_group(sae, group) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
+			   group);
+		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);
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos,
+				   const u8 *end, const u8 **token,
+				   size_t *token_len)
+{
+	if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) {
+		size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) *
+				     sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
+		if (token)
+			*token = *pos;
+		if (token_len)
+			*token_len = tlen;
+		*pos += tlen;
+	} else {
+		if (token)
+			*token = NULL;
+		if (token_len)
+			*token_len = 0;
+	}
+}
+
+
+static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos,
+				   const u8 *end)
+{
+	struct crypto_bignum *peer_scalar;
+
+	if (*pos + sae->tmp->prime_len > end) {
+		wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len);
+	if (peer_scalar == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	/*
+	 * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for
+	 * the peer and it is in Authenticated state, the new Commit Message
+	 * shall be dropped if the peer-scalar is identical to the one used in
+	 * the existing protocol instance.
+	 */
+	if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar &&
+	    crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous "
+			   "peer-commit-scalar");
+		crypto_bignum_deinit(peer_scalar, 0);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	/* 1 < scalar < r */
+	if (crypto_bignum_is_zero(peer_scalar) ||
+	    crypto_bignum_is_one(peer_scalar) ||
+	    crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar");
+		crypto_bignum_deinit(peer_scalar, 0);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+
+	crypto_bignum_deinit(sae->peer_commit_scalar, 0);
+	sae->peer_commit_scalar = peer_scalar;
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar",
+		    *pos, sae->tmp->prime_len);
+	*pos += sae->tmp->prime_len;
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
+					const u8 *end)
+{
+	u8 prime[SAE_MAX_ECC_PRIME_LEN];
+
+	if (pos + 2 * sae->tmp->prime_len > end) {
+		wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
+			   "commit-element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
+				 sae->tmp->prime_len) < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	/* element x and y coordinates < p */
+	if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 ||
+	    os_memcmp(pos + sae->tmp->prime_len, prime,
+		      sae->tmp->prime_len) >= 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
+			   "element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)",
+		    pos, sae->tmp->prime_len);
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
+		    pos + sae->tmp->prime_len, sae->tmp->prime_len);
+
+	crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0);
+	sae->tmp->peer_commit_element_ecc =
+		crypto_ec_point_from_bin(sae->tmp->ec, pos);
+	if (sae->tmp->peer_commit_element_ecc == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	if (!crypto_ec_point_is_on_curve(sae->tmp->ec,
+					 sae->tmp->peer_commit_element_ecc)) {
+		wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos,
+					const u8 *end)
+{
+	struct crypto_bignum *res, *one;
+	const u8 one_bin[1] = { 0x01 };
+
+	if (pos + sae->tmp->prime_len > end) {
+		wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
+			   "commit-element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos,
+		    sae->tmp->prime_len);
+
+	crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0);
+	sae->tmp->peer_commit_element_ffc =
+		crypto_bignum_init_set(pos, sae->tmp->prime_len);
+	if (sae->tmp->peer_commit_element_ffc == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	/* 1 < element < p - 1 */
+	res = crypto_bignum_init();
+	one = crypto_bignum_init_set(one_bin, sizeof(one_bin));
+	if (!res || !one ||
+	    crypto_bignum_sub(sae->tmp->prime, one, res) ||
+	    crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) ||
+	    crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) ||
+	    crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) {
+		crypto_bignum_deinit(res, 0);
+		crypto_bignum_deinit(one, 0);
+		wpa_printf(MSG_DEBUG, "SAE: Invalid peer element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	crypto_bignum_deinit(one, 0);
+
+	/* scalar-op(r, ELEMENT) = 1 modulo p */
+	if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc,
+				  sae->tmp->order, sae->tmp->prime, res) < 0 ||
+	    !crypto_bignum_is_one(res)) {
+		wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)");
+		crypto_bignum_deinit(res, 0);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	crypto_bignum_deinit(res, 0);
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos,
+				    const u8 *end)
+{
+	if (sae->tmp->dh)
+		return sae_parse_commit_element_ffc(sae, pos, end);
+	return sae_parse_commit_element_ecc(sae, pos, end);
+}
+
+
+u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
+		     const u8 **token, size_t *token_len, int *allowed_groups)
+{
+	const u8 *pos = data, *end = data + len;
+	u16 res;
+
+	/* Check Finite Cyclic Group */
+	if (pos + 2 > end)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos));
+	if (res != WLAN_STATUS_SUCCESS)
+		return res;
+	pos += 2;
+
+	/* Optional Anti-Clogging Token */
+	sae_parse_commit_token(sae, &pos, end, token, token_len);
+
+	/* commit-scalar */
+	res = sae_parse_commit_scalar(sae, &pos, end);
+	if (res != WLAN_STATUS_SUCCESS)
+		return res;
+
+	/* commit-element */
+	res = sae_parse_commit_element(sae, pos, end);
+	if (res != WLAN_STATUS_SUCCESS)
+		return res;
+
+	/*
+	 * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as
+	 * the values we sent which would be evidence of a reflection attack.
+	 */
+	if (!sae->tmp->own_commit_scalar ||
+	    crypto_bignum_cmp(sae->tmp->own_commit_scalar,
+			      sae->peer_commit_scalar) != 0 ||
+	    (sae->tmp->dh &&
+	     (!sae->tmp->own_commit_element_ffc ||
+	      crypto_bignum_cmp(sae->tmp->own_commit_element_ffc,
+				sae->tmp->peer_commit_element_ffc) != 0)) ||
+	    (sae->tmp->ec &&
+	     (!sae->tmp->own_commit_element_ecc ||
+	      crypto_ec_point_cmp(sae->tmp->ec,
+				  sae->tmp->own_commit_element_ecc,
+				  sae->tmp->peer_commit_element_ecc) != 0)))
+		return WLAN_STATUS_SUCCESS; /* scalars/elements are different */
+
+	/*
+	 * This is a reflection attack - return special value to trigger caller
+	 * to silently discard the frame instead of replying with a specific
+	 * status code.
+	 */
+	return SAE_SILENTLY_DISCARD;
+}
+
+
+static void sae_cn_confirm(struct sae_data *sae, const u8 *sc,
+			   const struct crypto_bignum *scalar1,
+			   const u8 *element1, size_t element1_len,
+			   const struct crypto_bignum *scalar2,
+			   const u8 *element2, size_t element2_len,
+			   u8 *confirm)
+{
+	const u8 *addr[5];
+	size_t len[5];
+	u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN];
+
+	/* Confirm
+	 * CN(key, X, Y, Z, ...) =
+	 *    HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...)
+	 * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT,
+	 *              peer-commit-scalar, PEER-COMMIT-ELEMENT)
+	 * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar,
+	 *               PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT)
+	 */
+	addr[0] = sc;
+	len[0] = 2;
+	crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1),
+			     sae->tmp->prime_len);
+	addr[1] = scalar_b1;
+	len[1] = sae->tmp->prime_len;
+	addr[2] = element1;
+	len[2] = element1_len;
+	crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2),
+			     sae->tmp->prime_len);
+	addr[3] = scalar_b2;
+	len[3] = sae->tmp->prime_len;
+	addr[4] = element2;
+	len[4] = element2_len;
+	hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len,
+			   confirm);
+}
+
+
+static void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc,
+			       const struct crypto_bignum *scalar1,
+			       const struct crypto_ec_point *element1,
+			       const struct crypto_bignum *scalar2,
+			       const struct crypto_ec_point *element2,
+			       u8 *confirm)
+{
+	u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN];
+	u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN];
+
+	crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1,
+			       element_b1 + sae->tmp->prime_len);
+	crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2,
+			       element_b2 + sae->tmp->prime_len);
+
+	sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len,
+		       scalar2, element_b2, 2 * sae->tmp->prime_len, confirm);
+}
+
+
+static void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc,
+			       const struct crypto_bignum *scalar1,
+			       const struct crypto_bignum *element1,
+			       const struct crypto_bignum *scalar2,
+			       const struct crypto_bignum *element2,
+			       u8 *confirm)
+{
+	u8 element_b1[SAE_MAX_PRIME_LEN];
+	u8 element_b2[SAE_MAX_PRIME_LEN];
+
+	crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1),
+			     sae->tmp->prime_len);
+	crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2),
+			     sae->tmp->prime_len);
+
+	sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len,
+		       scalar2, element_b2, sae->tmp->prime_len, confirm);
+}
+
+
+void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
+{
+	const u8 *sc;
+
+	if (sae->tmp == NULL)
+		return;
+
+	/* Send-Confirm */
+	sc = wpabuf_put(buf, 0);
+	wpabuf_put_le16(buf, sae->send_confirm);
+	sae->send_confirm++;
+
+	if (sae->tmp->ec)
+		sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ecc,
+				   sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ecc,
+				   wpabuf_put(buf, SHA256_MAC_LEN));
+	else
+		sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ffc,
+				   sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ffc,
+				   wpabuf_put(buf, SHA256_MAC_LEN));
+}
+
+
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
+{
+	u8 verifier[SHA256_MAC_LEN];
+
+	if (len < 2 + SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG, "SAE: Too short confirm message");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
+
+	if (sae->tmp == 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,
+				   sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ecc,
+				   verifier);
+	else
+		sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ffc,
+				   sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ffc,
+				   verifier);
+
+	if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
+		wpa_hexdump(MSG_DEBUG, "SAE: Received confirm",
+			    data + 2, SHA256_MAC_LEN);
+		wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier",
+			    verifier, SHA256_MAC_LEN);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/hostap/src/common/sae.h b/hostap/src/common/sae.h
new file mode 100644
index 0000000..c07026c
--- /dev/null
+++ b/hostap/src/common/sae.h
@@ -0,0 +1,70 @@
+/*
+ * Simultaneous authentication of equals
+ * 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.
+ */
+
+#ifndef SAE_H
+#define SAE_H
+
+#define SAE_KCK_LEN 32
+#define SAE_PMK_LEN 32
+#define SAE_PMKID_LEN 16
+#define SAE_KEYSEED_KEY_LEN 32
+#define SAE_MAX_PRIME_LEN 512
+#define SAE_MAX_ECC_PRIME_LEN 66
+#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN)
+#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN)
+
+/* Special value returned by sae_parse_commit() */
+#define SAE_SILENTLY_DISCARD 65535
+
+struct sae_temporary_data {
+	u8 kck[SAE_KCK_LEN];
+	struct crypto_bignum *own_commit_scalar;
+	struct crypto_bignum *own_commit_element_ffc;
+	struct crypto_ec_point *own_commit_element_ecc;
+	struct crypto_bignum *peer_commit_element_ffc;
+	struct crypto_ec_point *peer_commit_element_ecc;
+	struct crypto_ec_point *pwe_ecc;
+	struct crypto_bignum *pwe_ffc;
+	struct crypto_bignum *sae_rand;
+	struct crypto_ec *ec;
+	int prime_len;
+	const struct dh_group *dh;
+	const struct crypto_bignum *prime;
+	const struct crypto_bignum *order;
+	struct crypto_bignum *prime_buf;
+	struct crypto_bignum *order_buf;
+	struct wpabuf *anti_clogging_token;
+};
+
+struct sae_data {
+	enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state;
+	u16 send_confirm;
+	u8 pmk[SAE_PMK_LEN];
+	struct crypto_bignum *peer_commit_scalar;
+	int group;
+	int sync;
+	struct sae_temporary_data *tmp;
+};
+
+int sae_set_group(struct sae_data *sae, int group);
+void sae_clear_temp_data(struct sae_data *sae);
+void sae_clear_data(struct sae_data *sae);
+
+int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
+		       const u8 *password, size_t password_len,
+		       struct sae_data *sae);
+int sae_process_commit(struct sae_data *sae);
+void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
+		      const struct wpabuf *token);
+u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
+		     const u8 **token, size_t *token_len, int *allowed_groups);
+void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
+u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
+
+#endif /* SAE_H */
diff --git a/hostap/src/common/tnc.h b/hostap/src/common/tnc.h
new file mode 100644
index 0000000..108acf9
--- /dev/null
+++ b/hostap/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/hostap/src/common/version.h b/hostap/src/common/version.h
new file mode 100644
index 0000000..9012eb3
--- /dev/null
+++ b/hostap/src/common/version.h
@@ -0,0 +1,14 @@
+#ifndef VERSION_H
+#define VERSION_H
+
+#ifndef VERSION_STR_POSTFIX
+#define VERSION_STR_POSTFIX ""
+#endif /* VERSION_STR_POSTFIX */
+
+#ifndef NEST_VERSION_STR_POSTFIX
+#define NEST_VERSION_STR_POSTFIX "-Nest_2017a"
+#endif /* NEST_VERSION_STR_POSTFIX */
+
+#define VERSION_STR "2.5" VERSION_STR_POSTFIX NEST_VERSION_STR_POSTFIX
+
+#endif /* VERSION_H */
diff --git a/hostap/src/common/wpa_common.c b/hostap/src/common/wpa_common.c
new file mode 100644
index 0000000..e9d4248
--- /dev/null
+++ b/hostap/src/common/wpa_common.c
@@ -0,0 +1,1664 @@
+/*
+ * WPA/RSN - Shared functions for supplicant and authenticator
+ * Copyright (c) 2002-2015, 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/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "ieee802_11_defs.h"
+#include "defs.h"
+#include "wpa_common.h"
+
+
+static unsigned int wpa_kck_len(int akmp)
+{
+	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		return 24;
+	return 16;
+}
+
+
+static unsigned int wpa_kek_len(int akmp)
+{
+	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		return 32;
+	return 16;
+}
+
+
+unsigned int wpa_mic_len(int akmp)
+{
+	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		return 24;
+	return 16;
+}
+
+
+/**
+ * wpa_eapol_key_mic - Calculate EAPOL-Key MIC
+ * @key: EAPOL-Key Key Confirmation Key (KCK)
+ * @key_len: KCK length in octets
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*)
+ * @buf: Pointer to the beginning of the EAPOL header (version field)
+ * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame)
+ * @mic: Pointer to the buffer to which the EAPOL-Key MIC is written
+ * Returns: 0 on success, -1 on failure
+ *
+ * Calculate EAPOL-Key MIC for an EAPOL-Key packet. The EAPOL-Key MIC field has
+ * to be cleared (all zeroes) when calling this function.
+ *
+ * Note: 'IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames' has an error in the
+ * description of the Key MIC calculation. It includes packet data from the
+ * beginning of the EAPOL-Key header, not EAPOL header. This incorrect change
+ * happened during final editing of the standard and the correct behavior is
+ * defined in the last draft (IEEE 802.11i/D10).
+ */
+int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
+		      const u8 *buf, size_t len, u8 *mic)
+{
+	u8 hash[SHA384_MAC_LEN];
+
+	switch (ver) {
+#ifndef CONFIG_FIPS
+	case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
+		return hmac_md5(key, key_len, buf, len, mic);
+#endif /* CONFIG_FIPS */
+	case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
+		if (hmac_sha1(key, key_len, buf, len, hash))
+			return -1;
+		os_memcpy(mic, hash, MD5_MAC_LEN);
+		break;
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+	case WPA_KEY_INFO_TYPE_AES_128_CMAC:
+		return omac1_aes_128(key, buf, len, mic);
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+	case WPA_KEY_INFO_TYPE_AKM_DEFINED:
+		switch (akmp) {
+#ifdef CONFIG_HS20
+		case WPA_KEY_MGMT_OSEN:
+			return omac1_aes_128(key, buf, len, mic);
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_SUITEB
+		case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+			if (hmac_sha256(key, key_len, buf, len, hash))
+				return -1;
+			os_memcpy(mic, hash, MD5_MAC_LEN);
+			break;
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+			if (hmac_sha384(key, key_len, buf, len, hash))
+				return -1;
+			os_memcpy(mic, hash, 24);
+			break;
+#endif /* CONFIG_SUITEB192 */
+		default:
+			return -1;
+		}
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces
+ * @pmk: Pairwise master key
+ * @pmk_len: Length of PMK
+ * @label: Label to use in derivation
+ * @addr1: AA or SA
+ * @addr2: SA or AA
+ * @nonce1: ANonce or SNonce
+ * @nonce2: SNonce or ANonce
+ * @ptk: Buffer for pairwise transient key
+ * @akmp: Negotiated AKM
+ * @cipher: Negotiated pairwise cipher
+ * Returns: 0 on success, -1 on failure
+ *
+ * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
+ * PTK = PRF-X(PMK, "Pairwise key expansion",
+ *             Min(AA, SA) || Max(AA, SA) ||
+ *             Min(ANonce, SNonce) || Max(ANonce, SNonce))
+ *
+ * STK = PRF-X(SMK, "Peer key expansion",
+ *             Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) ||
+ *             Min(INonce, PNonce) || Max(INonce, PNonce))
+ */
+int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
+		   const u8 *addr1, const u8 *addr2,
+		   const u8 *nonce1, const u8 *nonce2,
+		   struct wpa_ptk *ptk, int akmp, int cipher)
+{
+	u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN];
+	u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+	size_t ptk_len;
+
+	if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
+		os_memcpy(data, addr1, ETH_ALEN);
+		os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
+	} else {
+		os_memcpy(data, addr2, ETH_ALEN);
+		os_memcpy(data + ETH_ALEN, addr1, ETH_ALEN);
+	}
+
+	if (os_memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) {
+		os_memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN);
+		os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2,
+			  WPA_NONCE_LEN);
+	} else {
+		os_memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN);
+		os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1,
+			  WPA_NONCE_LEN);
+	}
+
+	ptk->kck_len = wpa_kck_len(akmp);
+	ptk->kek_len = wpa_kek_len(akmp);
+	ptk->tk_len = wpa_cipher_key_len(cipher);
+	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
+
+#ifdef CONFIG_SUITEB192
+	if (wpa_key_mgmt_sha384(akmp))
+		sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+			   tmp, ptk_len);
+	else
+#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_IEEE80211W
+	if (wpa_key_mgmt_sha256(akmp))
+		sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+			   tmp, ptk_len);
+	else
+#endif /* CONFIG_IEEE80211W */
+		sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, ptk_len);
+
+	wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
+		   MAC2STR(addr1), MAC2STR(addr2));
+	wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
+	wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", tmp, ptk_len);
+
+	os_memcpy(ptk->kck, tmp, ptk->kck_len);
+	wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", ptk->kck, ptk->kck_len);
+
+	os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
+	wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len);
+
+	os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
+	wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len);
+
+	os_memset(tmp, 0, sizeof(tmp));
+	return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+	       const u8 *ap_addr, u8 transaction_seqnum,
+	       const u8 *mdie, size_t mdie_len,
+	       const u8 *ftie, size_t ftie_len,
+	       const u8 *rsnie, size_t rsnie_len,
+	       const u8 *ric, size_t ric_len, u8 *mic)
+{
+	const u8 *addr[9];
+	size_t len[9];
+	size_t i, num_elem = 0;
+	u8 zero_mic[16];
+
+	if (kck_len != 16) {
+		wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u",
+			   (unsigned int) kck_len);
+		return -1;
+	}
+
+	addr[num_elem] = sta_addr;
+	len[num_elem] = ETH_ALEN;
+	num_elem++;
+
+	addr[num_elem] = ap_addr;
+	len[num_elem] = ETH_ALEN;
+	num_elem++;
+
+	addr[num_elem] = &transaction_seqnum;
+	len[num_elem] = 1;
+	num_elem++;
+
+	if (rsnie) {
+		addr[num_elem] = rsnie;
+		len[num_elem] = rsnie_len;
+		num_elem++;
+	}
+	if (mdie) {
+		addr[num_elem] = mdie;
+		len[num_elem] = mdie_len;
+		num_elem++;
+	}
+	if (ftie) {
+		if (ftie_len < 2 + sizeof(struct rsn_ftie))
+			return -1;
+
+		/* IE hdr and mic_control */
+		addr[num_elem] = ftie;
+		len[num_elem] = 2 + 2;
+		num_elem++;
+
+		/* MIC field with all zeros */
+		os_memset(zero_mic, 0, sizeof(zero_mic));
+		addr[num_elem] = zero_mic;
+		len[num_elem] = sizeof(zero_mic);
+		num_elem++;
+
+		/* Rest of FTIE */
+		addr[num_elem] = ftie + 2 + 2 + 16;
+		len[num_elem] = ftie_len - (2 + 2 + 16);
+		num_elem++;
+	}
+	if (ric) {
+		addr[num_elem] = ric;
+		len[num_elem] = ric_len;
+		num_elem++;
+	}
+
+	for (i = 0; i < num_elem; i++)
+		wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]);
+	if (omac1_aes_128_vector(kck, num_elem, addr, len, mic))
+		return -1;
+
+	return 0;
+}
+
+
+static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
+			     struct wpa_ft_ies *parse)
+{
+	const u8 *end, *pos;
+
+	parse->ftie = ie;
+	parse->ftie_len = ie_len;
+
+	pos = ie + sizeof(struct rsn_ftie);
+	end = ie + ie_len;
+
+	while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+		switch (pos[0]) {
+		case FTIE_SUBELEM_R1KH_ID:
+			if (pos[1] != FT_R1KH_ID_LEN) {
+				wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
+					   "length in FTIE: %d", pos[1]);
+				return -1;
+			}
+			parse->r1kh_id = pos + 2;
+			break;
+		case FTIE_SUBELEM_GTK:
+			parse->gtk = pos + 2;
+			parse->gtk_len = pos[1];
+			break;
+		case FTIE_SUBELEM_R0KH_ID:
+			if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
+				wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
+					   "length in FTIE: %d", pos[1]);
+				return -1;
+			}
+			parse->r0kh_id = pos + 2;
+			parse->r0kh_id_len = pos[1];
+			break;
+#ifdef CONFIG_IEEE80211W
+		case FTIE_SUBELEM_IGTK:
+			parse->igtk = pos + 2;
+			parse->igtk_len = pos[1];
+			break;
+#endif /* CONFIG_IEEE80211W */
+		}
+
+		pos += 2 + pos[1];
+	}
+
+	return 0;
+}
+
+
+int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
+		     struct wpa_ft_ies *parse)
+{
+	const u8 *end, *pos;
+	struct wpa_ie_data data;
+	int ret;
+	const struct rsn_ftie *ftie;
+	int prot_ie_count = 0;
+
+	os_memset(parse, 0, sizeof(*parse));
+	if (ies == NULL)
+		return 0;
+
+	pos = ies;
+	end = ies + ies_len;
+	while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+		switch (pos[0]) {
+		case WLAN_EID_RSN:
+			parse->rsn = pos + 2;
+			parse->rsn_len = pos[1];
+			ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
+						   parse->rsn_len + 2,
+						   &data);
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG, "FT: Failed to parse "
+					   "RSN IE: %d", ret);
+				return -1;
+			}
+			if (data.num_pmkid == 1 && data.pmkid)
+				parse->rsn_pmkid = data.pmkid;
+			break;
+		case WLAN_EID_MOBILITY_DOMAIN:
+			if (pos[1] < sizeof(struct rsn_mdie))
+				return -1;
+			parse->mdie = pos + 2;
+			parse->mdie_len = pos[1];
+			break;
+		case WLAN_EID_FAST_BSS_TRANSITION:
+			if (pos[1] < sizeof(*ftie))
+				return -1;
+			ftie = (const struct rsn_ftie *) (pos + 2);
+			prot_ie_count = ftie->mic_control[1];
+			if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
+				return -1;
+			break;
+		case WLAN_EID_TIMEOUT_INTERVAL:
+			if (pos[1] != 5)
+				break;
+			parse->tie = pos + 2;
+			parse->tie_len = pos[1];
+			break;
+		case WLAN_EID_RIC_DATA:
+			if (parse->ric == NULL)
+				parse->ric = pos;
+			break;
+		}
+
+		pos += 2 + pos[1];
+	}
+
+	if (prot_ie_count == 0)
+		return 0; /* no MIC */
+
+	/*
+	 * Check that the protected IE count matches with IEs included in the
+	 * frame.
+	 */
+	if (parse->rsn)
+		prot_ie_count--;
+	if (parse->mdie)
+		prot_ie_count--;
+	if (parse->ftie)
+		prot_ie_count--;
+	if (prot_ie_count < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
+			   "the protected IE count");
+		return -1;
+	}
+
+	if (prot_ie_count == 0 && parse->ric) {
+		wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
+			   "included in protected IE count");
+		return -1;
+	}
+
+	/* Determine the end of the RIC IE(s) */
+	pos = parse->ric;
+	while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
+	       prot_ie_count) {
+		prot_ie_count--;
+		pos += 2 + pos[1];
+	}
+	parse->ric_len = pos - parse->ric;
+	if (prot_ie_count) {
+		wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
+			   "frame", (int) prot_ie_count);
+		return -1;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+static int rsn_selector_to_bitfield(const u8 *s)
+{
+	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE)
+		return WPA_CIPHER_NONE;
+	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP)
+		return WPA_CIPHER_TKIP;
+	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP)
+		return WPA_CIPHER_CCMP;
+#ifdef CONFIG_IEEE80211W
+	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC)
+		return WPA_CIPHER_AES_128_CMAC;
+#endif /* CONFIG_IEEE80211W */
+	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP)
+		return WPA_CIPHER_GCMP;
+	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP_256)
+		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;
+}
+
+
+static int rsn_key_mgmt_to_bitfield(const u8 *s)
+{
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_UNSPEC_802_1X)
+		return WPA_KEY_MGMT_IEEE8021X;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X)
+		return WPA_KEY_MGMT_PSK;
+#ifdef CONFIG_IEEE80211R
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X)
+		return WPA_KEY_MGMT_FT_IEEE8021X;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK)
+		return WPA_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256)
+		return WPA_KEY_MGMT_IEEE8021X_SHA256;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256)
+		return WPA_KEY_MGMT_PSK_SHA256;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE)
+		return WPA_KEY_MGMT_SAE;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE)
+		return WPA_KEY_MGMT_FT_SAE;
+#endif /* CONFIG_SAE */
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B)
+		return WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192)
+		return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN)
+		return WPA_KEY_MGMT_OSEN;
+	return 0;
+}
+
+
+int wpa_cipher_valid_group(int cipher)
+{
+	return wpa_cipher_valid_pairwise(cipher) ||
+		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
+ * @rsn_ie_len: RSN IE buffer length (including IE number and length octets)
+ * @data: Pointer to structure that will be filled in with parsed data
+ * Returns: 0 on success, <0 on failure
+ */
+int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
+			 struct wpa_ie_data *data)
+{
+	const u8 *pos;
+	int left;
+	int i, count;
+
+	os_memset(data, 0, sizeof(*data));
+	data->proto = WPA_PROTO_RSN;
+	data->pairwise_cipher = WPA_CIPHER_CCMP;
+	data->group_cipher = WPA_CIPHER_CCMP;
+	data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+	data->capabilities = 0;
+	data->pmkid = NULL;
+	data->num_pmkid = 0;
+#ifdef CONFIG_IEEE80211W
+	data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
+#else /* CONFIG_IEEE80211W */
+	data->mgmt_group_cipher = 0;
+#endif /* CONFIG_IEEE80211W */
+
+	if (rsn_ie_len == 0) {
+		/* No RSN IE - fail silently */
+		return -1;
+	}
+
+	if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) {
+		wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
+			   __func__, (unsigned long) rsn_ie_len);
+		return -1;
+	}
+
+	if (rsn_ie_len >= 6 && rsn_ie[1] >= 4 &&
+	    rsn_ie[1] == rsn_ie_len - 2 &&
+	    WPA_GET_BE32(&rsn_ie[2]) == OSEN_IE_VENDOR_TYPE) {
+		pos = rsn_ie + 6;
+		left = rsn_ie_len - 6;
+
+		data->proto = WPA_PROTO_OSEN;
+	} else {
+		const struct rsn_ie_hdr *hdr;
+
+		hdr = (const struct rsn_ie_hdr *) rsn_ie;
+
+		if (hdr->elem_id != WLAN_EID_RSN ||
+		    hdr->len != rsn_ie_len - 2 ||
+		    WPA_GET_LE16(hdr->version) != RSN_VERSION) {
+			wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+				   __func__);
+			return -2;
+		}
+
+		pos = (const u8 *) (hdr + 1);
+		left = rsn_ie_len - sizeof(*hdr);
+	}
+
+	if (left >= RSN_SELECTOR_LEN) {
+		data->group_cipher = rsn_selector_to_bitfield(pos);
+		if (!wpa_cipher_valid_group(data->group_cipher)) {
+			wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x",
+				   __func__, data->group_cipher);
+			return -1;
+		}
+		pos += RSN_SELECTOR_LEN;
+		left -= RSN_SELECTOR_LEN;
+	} else if (left > 0) {
+		wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+			   __func__, left);
+		return -3;
+	}
+
+	if (left >= 2) {
+		data->pairwise_cipher = 0;
+		count = WPA_GET_LE16(pos);
+		pos += 2;
+		left -= 2;
+		if (count == 0 || count > left / RSN_SELECTOR_LEN) {
+			wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+				   "count %u left %u", __func__, count, left);
+			return -4;
+		}
+		for (i = 0; i < count; i++) {
+			data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
+			pos += RSN_SELECTOR_LEN;
+			left -= RSN_SELECTOR_LEN;
+		}
+#ifdef CONFIG_IEEE80211W
+		if (data->pairwise_cipher & WPA_CIPHER_AES_128_CMAC) {
+			wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as "
+				   "pairwise cipher", __func__);
+			return -1;
+		}
+#endif /* CONFIG_IEEE80211W */
+	} else if (left == 1) {
+		wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+			   __func__);
+		return -5;
+	}
+
+	if (left >= 2) {
+		data->key_mgmt = 0;
+		count = WPA_GET_LE16(pos);
+		pos += 2;
+		left -= 2;
+		if (count == 0 || count > left / RSN_SELECTOR_LEN) {
+			wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+				   "count %u left %u", __func__, count, left);
+			return -6;
+		}
+		for (i = 0; i < count; i++) {
+			data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos);
+			pos += RSN_SELECTOR_LEN;
+			left -= RSN_SELECTOR_LEN;
+		}
+	} else if (left == 1) {
+		wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+			   __func__);
+		return -7;
+	}
+
+	if (left >= 2) {
+		data->capabilities = WPA_GET_LE16(pos);
+		pos += 2;
+		left -= 2;
+	}
+
+	if (left >= 2) {
+		u16 num_pmkid = WPA_GET_LE16(pos);
+		pos += 2;
+		left -= 2;
+		if (num_pmkid > (unsigned int) left / PMKID_LEN) {
+			wpa_printf(MSG_DEBUG, "%s: PMKID underflow "
+				   "(num_pmkid=%u left=%d)",
+				   __func__, num_pmkid, left);
+			data->num_pmkid = 0;
+			return -9;
+		} else {
+			data->num_pmkid = num_pmkid;
+			data->pmkid = pos;
+			pos += data->num_pmkid * PMKID_LEN;
+			left -= data->num_pmkid * PMKID_LEN;
+		}
+	}
+
+#ifdef CONFIG_IEEE80211W
+	if (left >= 4) {
+		data->mgmt_group_cipher = rsn_selector_to_bitfield(pos);
+		if (!wpa_cipher_valid_mgmt_group(data->mgmt_group_cipher)) {
+			wpa_printf(MSG_DEBUG, "%s: Unsupported management "
+				   "group cipher 0x%x", __func__,
+				   data->mgmt_group_cipher);
+			return -10;
+		}
+		pos += RSN_SELECTOR_LEN;
+		left -= RSN_SELECTOR_LEN;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	if (left > 0) {
+		wpa_hexdump(MSG_DEBUG,
+			    "wpa_parse_wpa_ie_rsn: ignore trailing bytes",
+			    pos, left);
+	}
+
+	return 0;
+}
+
+
+static int wpa_selector_to_bitfield(const u8 *s)
+{
+	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE)
+		return WPA_CIPHER_NONE;
+	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP)
+		return WPA_CIPHER_TKIP;
+	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP)
+		return WPA_CIPHER_CCMP;
+	return 0;
+}
+
+
+static int wpa_key_mgmt_to_bitfield(const u8 *s)
+{
+	if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X)
+		return WPA_KEY_MGMT_IEEE8021X;
+	if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X)
+		return WPA_KEY_MGMT_PSK;
+	if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE)
+		return WPA_KEY_MGMT_WPA_NONE;
+	return 0;
+}
+
+
+int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
+			 struct wpa_ie_data *data)
+{
+	const struct wpa_ie_hdr *hdr;
+	const u8 *pos;
+	int left;
+	int i, count;
+
+	os_memset(data, 0, sizeof(*data));
+	data->proto = WPA_PROTO_WPA;
+	data->pairwise_cipher = WPA_CIPHER_TKIP;
+	data->group_cipher = WPA_CIPHER_TKIP;
+	data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+	data->capabilities = 0;
+	data->pmkid = NULL;
+	data->num_pmkid = 0;
+	data->mgmt_group_cipher = 0;
+
+	if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
+		wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
+			   __func__, (unsigned long) wpa_ie_len);
+		return -1;
+	}
+
+	hdr = (const struct wpa_ie_hdr *) wpa_ie;
+
+	if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC ||
+	    hdr->len != wpa_ie_len - 2 ||
+	    RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE ||
+	    WPA_GET_LE16(hdr->version) != WPA_VERSION) {
+		wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+			   __func__);
+		return -2;
+	}
+
+	pos = (const u8 *) (hdr + 1);
+	left = wpa_ie_len - sizeof(*hdr);
+
+	if (left >= WPA_SELECTOR_LEN) {
+		data->group_cipher = wpa_selector_to_bitfield(pos);
+		pos += WPA_SELECTOR_LEN;
+		left -= WPA_SELECTOR_LEN;
+	} else if (left > 0) {
+		wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+			   __func__, left);
+		return -3;
+	}
+
+	if (left >= 2) {
+		data->pairwise_cipher = 0;
+		count = WPA_GET_LE16(pos);
+		pos += 2;
+		left -= 2;
+		if (count == 0 || count > left / WPA_SELECTOR_LEN) {
+			wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+				   "count %u left %u", __func__, count, left);
+			return -4;
+		}
+		for (i = 0; i < count; i++) {
+			data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
+			pos += WPA_SELECTOR_LEN;
+			left -= WPA_SELECTOR_LEN;
+		}
+	} else if (left == 1) {
+		wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+			   __func__);
+		return -5;
+	}
+
+	if (left >= 2) {
+		data->key_mgmt = 0;
+		count = WPA_GET_LE16(pos);
+		pos += 2;
+		left -= 2;
+		if (count == 0 || count > left / WPA_SELECTOR_LEN) {
+			wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+				   "count %u left %u", __func__, count, left);
+			return -6;
+		}
+		for (i = 0; i < count; i++) {
+			data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
+			pos += WPA_SELECTOR_LEN;
+			left -= WPA_SELECTOR_LEN;
+		}
+	} else if (left == 1) {
+		wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+			   __func__);
+		return -7;
+	}
+
+	if (left >= 2) {
+		data->capabilities = WPA_GET_LE16(pos);
+		pos += 2;
+		left -= 2;
+	}
+
+	if (left > 0) {
+		wpa_hexdump(MSG_DEBUG,
+			    "wpa_parse_wpa_ie_wpa: ignore trailing bytes",
+			    pos, left);
+	}
+
+	return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+/**
+ * wpa_derive_pmk_r0 - Derive PMK-R0 and PMKR0Name
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.3
+ */
+void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
+		       const u8 *ssid, size_t ssid_len,
+		       const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
+		       const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name)
+{
+	u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
+	       FT_R0KH_ID_MAX_LEN + ETH_ALEN];
+	u8 *pos, r0_key_data[48], hash[32];
+	const u8 *addr[2];
+	size_t len[2];
+
+	/*
+	 * R0-Key-Data = KDF-384(XXKey, "FT-R0",
+	 *                       SSIDlength || SSID || MDID || R0KHlength ||
+	 *                       R0KH-ID || S0KH-ID)
+	 * XXKey is either the second 256 bits of MSK or PSK.
+	 * PMK-R0 = L(R0-Key-Data, 0, 256)
+	 * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128)
+	 */
+	if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
+		return;
+	pos = buf;
+	*pos++ = ssid_len;
+	os_memcpy(pos, ssid, ssid_len);
+	pos += ssid_len;
+	os_memcpy(pos, mdid, MOBILITY_DOMAIN_ID_LEN);
+	pos += MOBILITY_DOMAIN_ID_LEN;
+	*pos++ = r0kh_id_len;
+	os_memcpy(pos, r0kh_id, r0kh_id_len);
+	pos += r0kh_id_len;
+	os_memcpy(pos, s0kh_id, ETH_ALEN);
+	pos += ETH_ALEN;
+
+	sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+		   r0_key_data, sizeof(r0_key_data));
+	os_memcpy(pmk_r0, r0_key_data, PMK_LEN);
+
+	/*
+	 * PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt)
+	 */
+	addr[0] = (const u8 *) "FT-R0N";
+	len[0] = 6;
+	addr[1] = r0_key_data + PMK_LEN;
+	len[1] = 16;
+
+	sha256_vector(2, addr, len, hash);
+	os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
+}
+
+
+/**
+ * wpa_derive_pmk_r1_name - Derive PMKR1Name
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.4
+ */
+void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
+			    const u8 *s1kh_id, u8 *pmk_r1_name)
+{
+	u8 hash[32];
+	const u8 *addr[4];
+	size_t len[4];
+
+	/*
+	 * PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name ||
+	 *                                  R1KH-ID || S1KH-ID))
+	 */
+	addr[0] = (const u8 *) "FT-R1N";
+	len[0] = 6;
+	addr[1] = pmk_r0_name;
+	len[1] = WPA_PMK_NAME_LEN;
+	addr[2] = r1kh_id;
+	len[2] = FT_R1KH_ID_LEN;
+	addr[3] = s1kh_id;
+	len[3] = ETH_ALEN;
+
+	sha256_vector(4, addr, len, hash);
+	os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
+}
+
+
+/**
+ * wpa_derive_pmk_r1 - Derive PMK-R1 and PMKR1Name from PMK-R0
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.4
+ */
+void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
+		       const u8 *r1kh_id, const u8 *s1kh_id,
+		       u8 *pmk_r1, u8 *pmk_r1_name)
+{
+	u8 buf[FT_R1KH_ID_LEN + ETH_ALEN];
+	u8 *pos;
+
+	/* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */
+	pos = buf;
+	os_memcpy(pos, r1kh_id, FT_R1KH_ID_LEN);
+	pos += FT_R1KH_ID_LEN;
+	os_memcpy(pos, s1kh_id, ETH_ALEN);
+	pos += ETH_ALEN;
+
+	sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf, pmk_r1, PMK_LEN);
+
+	wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, pmk_r1_name);
+}
+
+
+/**
+ * wpa_pmk_r1_to_ptk - Derive PTK and PTKName from PMK-R1
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.5
+ */
+int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
+		      const u8 *sta_addr, const u8 *bssid,
+		      const u8 *pmk_r1_name,
+		      struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher)
+{
+	u8 buf[2 * WPA_NONCE_LEN + 2 * ETH_ALEN];
+	u8 *pos, hash[32];
+	const u8 *addr[6];
+	size_t len[6];
+	u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+	size_t ptk_len;
+
+	/*
+	 * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce ||
+	 *                  BSSID || STA-ADDR)
+	 */
+	pos = buf;
+	os_memcpy(pos, snonce, WPA_NONCE_LEN);
+	pos += WPA_NONCE_LEN;
+	os_memcpy(pos, anonce, WPA_NONCE_LEN);
+	pos += WPA_NONCE_LEN;
+	os_memcpy(pos, bssid, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, sta_addr, ETH_ALEN);
+	pos += ETH_ALEN;
+
+	ptk->kck_len = wpa_kck_len(akmp);
+	ptk->kek_len = wpa_kek_len(akmp);
+	ptk->tk_len = wpa_cipher_key_len(cipher);
+	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
+
+	sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, tmp, ptk_len);
+
+	/*
+	 * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce ||
+	 *                                ANonce || BSSID || STA-ADDR))
+	 */
+	addr[0] = pmk_r1_name;
+	len[0] = WPA_PMK_NAME_LEN;
+	addr[1] = (const u8 *) "FT-PTKN";
+	len[1] = 7;
+	addr[2] = snonce;
+	len[2] = WPA_NONCE_LEN;
+	addr[3] = anonce;
+	len[3] = WPA_NONCE_LEN;
+	addr[4] = bssid;
+	len[4] = ETH_ALEN;
+	addr[5] = sta_addr;
+	len[5] = ETH_ALEN;
+
+	sha256_vector(6, addr, len, hash);
+	os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN);
+
+	os_memcpy(ptk->kck, tmp, ptk->kck_len);
+	os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
+	os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
+
+	wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len);
+	wpa_hexdump_key(MSG_DEBUG, "FT: KEK", ptk->kek, ptk->kek_len);
+	wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len);
+	wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+	os_memset(tmp, 0, sizeof(tmp));
+
+	return 0;
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+/**
+ * rsn_pmkid - Calculate PMK identifier
+ * @pmk: Pairwise master key
+ * @pmk_len: Length of pmk in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: Buffer for PMKID
+ * @use_sha256: Whether to use SHA256-based KDF
+ *
+ * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
+ * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA)
+ */
+void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
+	       u8 *pmkid, int use_sha256)
+{
+	char *title = "PMK Name";
+	const u8 *addr[3];
+	const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
+	unsigned char hash[SHA256_MAC_LEN];
+
+	addr[0] = (u8 *) title;
+	addr[1] = aa;
+	addr[2] = spa;
+
+#ifdef CONFIG_IEEE80211W
+	if (use_sha256)
+		hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash);
+	else
+#endif /* CONFIG_IEEE80211W */
+		hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
+	os_memcpy(pmkid, hash, PMKID_LEN);
+}
+
+
+#ifdef CONFIG_SUITEB
+/**
+ * rsn_pmkid_suite_b - Calculate PMK identifier for Suite B AKM
+ * @kck: Key confirmation key
+ * @kck_len: Length of kck in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: Buffer for PMKID
+ * Returns: 0 on success, -1 on failure
+ *
+ * IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy
+ * PMKID = Truncate(HMAC-SHA-256(KCK, "PMK Name" || AA || SPA))
+ */
+int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
+		      const u8 *spa, u8 *pmkid)
+{
+	char *title = "PMK Name";
+	const u8 *addr[3];
+	const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
+	unsigned char hash[SHA256_MAC_LEN];
+
+	addr[0] = (u8 *) title;
+	addr[1] = aa;
+	addr[2] = spa;
+
+	if (hmac_sha256_vector(kck, kck_len, 3, addr, len, hash) < 0)
+		return -1;
+	os_memcpy(pmkid, hash, PMKID_LEN);
+	return 0;
+}
+#endif /* CONFIG_SUITEB */
+
+
+#ifdef CONFIG_SUITEB192
+/**
+ * rsn_pmkid_suite_b_192 - Calculate PMK identifier for Suite B AKM
+ * @kck: Key confirmation key
+ * @kck_len: Length of kck in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: Buffer for PMKID
+ * Returns: 0 on success, -1 on failure
+ *
+ * IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy
+ * PMKID = Truncate(HMAC-SHA-384(KCK, "PMK Name" || AA || SPA))
+ */
+int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa,
+			  const u8 *spa, u8 *pmkid)
+{
+	char *title = "PMK Name";
+	const u8 *addr[3];
+	const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
+	unsigned char hash[SHA384_MAC_LEN];
+
+	addr[0] = (u8 *) title;
+	addr[1] = aa;
+	addr[2] = spa;
+
+	if (hmac_sha384_vector(kck, kck_len, 3, addr, len, hash) < 0)
+		return -1;
+	os_memcpy(pmkid, hash, PMKID_LEN);
+	return 0;
+}
+#endif /* CONFIG_SUITEB192 */
+
+
+/**
+ * wpa_cipher_txt - Convert cipher suite to a text string
+ * @cipher: Cipher suite (WPA_CIPHER_* enum)
+ * Returns: Pointer to a text string of the cipher suite name
+ */
+const char * wpa_cipher_txt(int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_NONE:
+		return "NONE";
+	case WPA_CIPHER_WEP40:
+		return "WEP-40";
+	case WPA_CIPHER_WEP104:
+		return "WEP-104";
+	case WPA_CIPHER_TKIP:
+		return "TKIP";
+	case WPA_CIPHER_CCMP:
+		return "CCMP";
+	case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP:
+		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";
+	}
+}
+
+
+/**
+ * wpa_key_mgmt_txt - Convert key management suite to a text string
+ * @key_mgmt: Key management suite (WPA_KEY_MGMT_* enum)
+ * @proto: WPA/WPA2 version (WPA_PROTO_*)
+ * Returns: Pointer to a text string of the key management suite name
+ */
+const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
+{
+	switch (key_mgmt) {
+	case WPA_KEY_MGMT_IEEE8021X:
+		if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA))
+			return "WPA2+WPA/IEEE 802.1X/EAP";
+		return proto == WPA_PROTO_RSN ?
+			"WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP";
+	case WPA_KEY_MGMT_PSK:
+		if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA))
+			return "WPA2-PSK+WPA-PSK";
+		return proto == WPA_PROTO_RSN ?
+			"WPA2-PSK" : "WPA-PSK";
+	case WPA_KEY_MGMT_NONE:
+		return "NONE";
+	case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
+		return "IEEE 802.1X (no WPA)";
+#ifdef CONFIG_IEEE80211R
+	case WPA_KEY_MGMT_FT_IEEE8021X:
+		return "FT-EAP";
+	case WPA_KEY_MGMT_FT_PSK:
+		return "FT-PSK";
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+	case WPA_KEY_MGMT_IEEE8021X_SHA256:
+		return "WPA2-EAP-SHA256";
+	case WPA_KEY_MGMT_PSK_SHA256:
+		return "WPA2-PSK-SHA256";
+#endif /* CONFIG_IEEE80211W */
+	case WPA_KEY_MGMT_WPS:
+		return "WPS";
+	case WPA_KEY_MGMT_SAE:
+		return "SAE";
+	case WPA_KEY_MGMT_FT_SAE:
+		return "FT-SAE";
+	case WPA_KEY_MGMT_OSEN:
+		return "OSEN";
+	case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+		return "WPA2-EAP-SUITE-B";
+	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+		return "WPA2-EAP-SUITE-B-192";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+
+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;
+	if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+		return WLAN_AKM_SUITE_8021X_SUITE_B;
+	if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		return WLAN_AKM_SUITE_8021X_SUITE_B_192;
+	return 0;
+}
+
+
+int wpa_compare_rsn_ie(int ft_initial_assoc,
+		       const u8 *ie1, size_t ie1len,
+		       const u8 *ie2, size_t ie2len)
+{
+	if (ie1 == NULL || ie2 == NULL)
+		return -1;
+
+	if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0)
+		return 0; /* identical IEs */
+
+#ifdef CONFIG_IEEE80211R
+	if (ft_initial_assoc) {
+		struct wpa_ie_data ie1d, ie2d;
+		/*
+		 * The PMKID-List in RSN IE is different between Beacon/Probe
+		 * Response/(Re)Association Request frames and EAPOL-Key
+		 * messages in FT initial mobility domain association. Allow
+		 * for this, but verify that other parts of the RSN IEs are
+		 * identical.
+		 */
+		if (wpa_parse_wpa_ie_rsn(ie1, ie1len, &ie1d) < 0 ||
+		    wpa_parse_wpa_ie_rsn(ie2, ie2len, &ie2d) < 0)
+			return -1;
+		if (ie1d.proto == ie2d.proto &&
+		    ie1d.pairwise_cipher == ie2d.pairwise_cipher &&
+		    ie1d.group_cipher == ie2d.group_cipher &&
+		    ie1d.key_mgmt == ie2d.key_mgmt &&
+		    ie1d.capabilities == ie2d.capabilities &&
+		    ie1d.mgmt_group_cipher == ie2d.mgmt_group_cipher)
+			return 0;
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	return -1;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid)
+{
+	u8 *start, *end, *rpos, *rend;
+	int added = 0;
+
+	start = ies;
+	end = ies + ies_len;
+
+	while (start < end) {
+		if (*start == WLAN_EID_RSN)
+			break;
+		start += 2 + start[1];
+	}
+	if (start >= end) {
+		wpa_printf(MSG_ERROR, "FT: Could not find RSN IE in "
+			   "IEs data");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "FT: RSN IE before modification",
+		    start, 2 + start[1]);
+
+	/* Find start of PMKID-Count */
+	rpos = start + 2;
+	rend = rpos + start[1];
+
+	/* Skip Version and Group Data Cipher Suite */
+	rpos += 2 + 4;
+	/* Skip Pairwise Cipher Suite Count and List */
+	rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN;
+	/* Skip AKM Suite Count and List */
+	rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN;
+
+	if (rpos == rend) {
+		/* Add RSN Capabilities */
+		os_memmove(rpos + 2, rpos, end - rpos);
+		*rpos++ = 0;
+		*rpos++ = 0;
+		added += 2;
+		start[1] += 2;
+		rend = rpos;
+	} else {
+		/* Skip RSN Capabilities */
+		rpos += 2;
+		if (rpos > rend) {
+			wpa_printf(MSG_ERROR, "FT: Could not parse RSN IE in "
+				   "IEs data");
+			return -1;
+		}
+	}
+
+	if (rpos == rend) {
+		/* No PMKID-Count field included; add it */
+		os_memmove(rpos + 2 + PMKID_LEN, rpos, end + added - rpos);
+		WPA_PUT_LE16(rpos, 1);
+		rpos += 2;
+		os_memcpy(rpos, pmkid, PMKID_LEN);
+		added += 2 + PMKID_LEN;
+		start[1] += 2 + PMKID_LEN;
+	} else {
+		/* PMKID-Count was included; use it */
+		if (WPA_GET_LE16(rpos) != 0) {
+			wpa_printf(MSG_ERROR, "FT: Unexpected PMKID "
+				   "in RSN IE in EAPOL-Key data");
+			return -1;
+		}
+		WPA_PUT_LE16(rpos, 1);
+		rpos += 2;
+		os_memmove(rpos + PMKID_LEN, rpos, end + added - rpos);
+		os_memcpy(rpos, pmkid, PMKID_LEN);
+		added += PMKID_LEN;
+		start[1] += PMKID_LEN;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification "
+		    "(PMKID inserted)", start, 2 + start[1]);
+
+	return added;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+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;
+	}
+
+	return 0;
+}
+
+
+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:
+		return 6;
+	}
+
+	return 0;
+}
+
+
+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:
+		return WPA_ALG_GCMP;
+	case WPA_CIPHER_TKIP:
+		return WPA_ALG_TKIP;
+	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;
+}
+
+
+int wpa_cipher_valid_pairwise(int cipher)
+{
+	return cipher == WPA_CIPHER_CCMP_256 ||
+		cipher == WPA_CIPHER_GCMP_256 ||
+		cipher == WPA_CIPHER_CCMP ||
+		cipher == WPA_CIPHER_GCMP ||
+		cipher == WPA_CIPHER_TKIP;
+}
+
+
+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);
+	if (cipher & WPA_CIPHER_GCMP)
+		return RSN_CIPHER_SUITE_GCMP;
+	if (cipher & WPA_CIPHER_TKIP)
+		return (proto == WPA_PROTO_RSN ?
+			RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
+	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 *start, int ciphers)
+{
+	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;
+	}
+	if (ciphers & WPA_CIPHER_GCMP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
+		pos += RSN_SELECTOR_LEN;
+	}
+	if (ciphers & WPA_CIPHER_TKIP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+		pos += RSN_SELECTOR_LEN;
+	}
+	if (ciphers & WPA_CIPHER_NONE) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
+		pos += RSN_SELECTOR_LEN;
+	}
+
+	return (pos - start) / RSN_SELECTOR_LEN;
+}
+
+
+int wpa_cipher_put_suites(u8 *start, int ciphers)
+{
+	u8 *pos = start;
+
+	if (ciphers & WPA_CIPHER_CCMP) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+		pos += WPA_SELECTOR_LEN;
+	}
+	if (ciphers & WPA_CIPHER_TKIP) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+		pos += WPA_SELECTOR_LEN;
+	}
+	if (ciphers & WPA_CIPHER_NONE) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
+		pos += WPA_SELECTOR_LEN;
+	}
+
+	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)
+		return WPA_CIPHER_GCMP;
+	if (ciphers & WPA_CIPHER_TKIP)
+		return WPA_CIPHER_TKIP;
+	if (none_allowed && (ciphers & WPA_CIPHER_NONE))
+		return WPA_CIPHER_NONE;
+	return -1;
+}
+
+
+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;
+	return -1;
+}
+
+
+int wpa_parse_cipher(const char *value)
+{
+	int val = 0, last;
+	char *start, *end, *buf;
+
+	buf = os_strdup(value);
+	if (buf == NULL)
+		return -1;
+	start = buf;
+
+	while (*start != '\0') {
+		while (*start == ' ' || *start == '\t')
+			start++;
+		if (*start == '\0')
+			break;
+		end = start;
+		while (*end != ' ' && *end != '\t' && *end != '\0')
+			end++;
+		last = *end == '\0';
+		*end = '\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;
+		else if (os_strcmp(start, "TKIP") == 0)
+			val |= WPA_CIPHER_TKIP;
+		else if (os_strcmp(start, "WEP104") == 0)
+			val |= WPA_CIPHER_WEP104;
+		else if (os_strcmp(start, "WEP40") == 0)
+			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;
+		}
+
+		if (last)
+			break;
+		start = end + 1;
+	}
+	os_free(buf);
+
+	return val;
+}
+
+
+int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim)
+{
+	char *pos = start;
+	int ret;
+
+	if (ciphers & WPA_CIPHER_CCMP_256) {
+		ret = os_snprintf(pos, end - pos, "%sCCMP-256",
+				  pos == start ? "" : delim);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_GCMP_256) {
+		ret = os_snprintf(pos, end - pos, "%sGCMP-256",
+				  pos == start ? "" : delim);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_CCMP) {
+		ret = os_snprintf(pos, end - pos, "%sCCMP",
+				  pos == start ? "" : delim);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_GCMP) {
+		ret = os_snprintf(pos, end - pos, "%sGCMP",
+				  pos == start ? "" : delim);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_TKIP) {
+		ret = os_snprintf(pos, end - pos, "%sTKIP",
+				  pos == start ? "" : delim);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_NONE) {
+		ret = os_snprintf(pos, end - pos, "%sNONE",
+				  pos == start ? "" : delim);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+
+	return pos - start;
+}
+
+
+int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise)
+{
+	int pairwise = 0;
+
+	/* Select group cipher based on the enabled pairwise cipher suites */
+	if (wpa & 1)
+		pairwise |= wpa_pairwise;
+	if (wpa & 2)
+		pairwise |= rsn_pairwise;
+
+	if (pairwise & WPA_CIPHER_TKIP)
+		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;
+}
diff --git a/hostap/src/common/wpa_common.h b/hostap/src/common/wpa_common.h
new file mode 100644
index 0000000..21e13da
--- /dev/null
+++ b/hostap/src/common/wpa_common.h
@@ -0,0 +1,462 @@
+/*
+ * WPA definitions shared between hostapd and wpa_supplicant
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_COMMON_H
+#define WPA_COMMON_H
+
+/* IEEE 802.11i */
+#define PMKID_LEN 16
+#define PMK_LEN 32
+#define WPA_REPLAY_COUNTER_LEN 8
+#define WPA_NONCE_LEN 32
+#define WPA_KEY_RSC_LEN 8
+#define WPA_GMK_LEN 32
+#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_GCMP_256 | WPA_CIPHER_CCMP_256)
+#define WPA_ALLOWED_GROUP_CIPHERS \
+(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \
+WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
+WPA_CIPHER_GTK_NOT_USED)
+
+#define WPA_SELECTOR_LEN 4
+#define WPA_VERSION 1
+#define RSN_SELECTOR_LEN 4
+#define RSN_VERSION 1
+
+#define RSN_SELECTOR(a, b, c, d) \
+	((((u32) (a)) << 24) | (((u32) (b)) << 16) | (((u32) (c)) << 8) | \
+	 (u32) (d))
+
+#define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
+#define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
+#define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
+#define WPA_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0)
+#define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
+#define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
+#define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4)
+
+
+#define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
+#define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
+#ifdef CONFIG_IEEE80211R
+#define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
+#define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+#endif /* CONFIG_IEEE80211R */
+#define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
+#define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+#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_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
+#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_192 \
+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_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
+#if 0
+#define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
+#endif
+#define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+#define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+#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.
+ */
+#define RSN_KEY_DATA_GROUPKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
+#if 0
+#define RSN_KEY_DATA_STAKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
+#endif
+#define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
+#define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+#ifdef CONFIG_PEERKEY
+#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
+#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
+#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_IEEE80211W
+#define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
+#endif /* CONFIG_IEEE80211W */
+#define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10)
+#define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
+#define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
+
+#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))
+#define RSN_SELECTOR_GET(a) WPA_GET_BE32((const u8 *) (a))
+
+#define RSN_NUM_REPLAY_COUNTERS_1 0
+#define RSN_NUM_REPLAY_COUNTERS_2 1
+#define RSN_NUM_REPLAY_COUNTERS_4 2
+#define RSN_NUM_REPLAY_COUNTERS_16 3
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+#ifdef CONFIG_IEEE80211W
+#define WPA_IGTK_LEN 16
+#define WPA_IGTK_MAX_LEN 32
+#endif /* CONFIG_IEEE80211W */
+
+
+/* IEEE 802.11, 7.3.2.25.3 RSN Capabilities */
+#define WPA_CAPABILITY_PREAUTH BIT(0)
+#define WPA_CAPABILITY_NO_PAIRWISE BIT(1)
+/* B2-B3: PTKSA Replay Counter */
+/* B4-B5: GTKSA Replay Counter */
+#define WPA_CAPABILITY_MFPR BIT(6)
+#define WPA_CAPABILITY_MFPC BIT(7)
+/* B8: Reserved */
+#define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9)
+#define WPA_CAPABILITY_SPP_A_MSDU_CAPABLE BIT(10)
+#define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11)
+#define WPA_CAPABILITY_PBAC BIT(12)
+#define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13)
+/* B14-B15: Reserved */
+
+
+/* IEEE 802.11r */
+#define MOBILITY_DOMAIN_ID_LEN 2
+#define FT_R0KH_ID_MAX_LEN 48
+#define FT_R1KH_ID_LEN 6
+#define WPA_PMK_NAME_LEN 16
+
+
+/* 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
+#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */
+/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */
+#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5))
+#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4
+#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */
+#define WPA_KEY_INFO_TXRX BIT(6) /* group */
+#define WPA_KEY_INFO_ACK BIT(7)
+#define WPA_KEY_INFO_MIC BIT(8)
+#define WPA_KEY_INFO_SECURE BIT(9)
+#define WPA_KEY_INFO_ERROR BIT(10)
+#define WPA_KEY_INFO_REQUEST BIT(11)
+#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */
+#define WPA_KEY_INFO_SMK_MESSAGE BIT(13)
+
+
+struct wpa_eapol_key {
+	u8 type;
+	/* Note: key_info, key_length, and key_data_length are unaligned */
+	u8 key_info[2]; /* big endian */
+	u8 key_length[2]; /* big endian */
+	u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+	u8 key_nonce[WPA_NONCE_LEN];
+	u8 key_iv[16];
+	u8 key_rsc[WPA_KEY_RSC_LEN];
+	u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
+	u8 key_mic[16];
+	u8 key_data_length[2]; /* big endian */
+	/* followed by key_data_length bytes of key_data */
+} STRUCT_PACKED;
+
+struct wpa_eapol_key_192 {
+	u8 type;
+	/* Note: key_info, key_length, and key_data_length are unaligned */
+	u8 key_info[2]; /* big endian */
+	u8 key_length[2]; /* big endian */
+	u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+	u8 key_nonce[WPA_NONCE_LEN];
+	u8 key_iv[16];
+	u8 key_rsc[WPA_KEY_RSC_LEN];
+	u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
+	u8 key_mic[24];
+	u8 key_data_length[2]; /* big endian */
+	/* followed by key_data_length bytes of key_data */
+} STRUCT_PACKED;
+
+#define WPA_EAPOL_KEY_MIC_MAX_LEN 24
+#define WPA_KCK_MAX_LEN 24
+#define WPA_KEK_MAX_LEN 32
+#define WPA_TK_MAX_LEN 32
+
+/**
+ * struct wpa_ptk - WPA Pairwise Transient Key
+ * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
+ */
+struct wpa_ptk {
+	u8 kck[WPA_KCK_MAX_LEN]; /* EAPOL-Key Key Confirmation Key (KCK) */
+	u8 kek[WPA_KEK_MAX_LEN]; /* EAPOL-Key Key Encryption Key (KEK) */
+	u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */
+	size_t kck_len;
+	size_t kek_len;
+	size_t tk_len;
+};
+
+struct wpa_gtk {
+	u8 gtk[WPA_GTK_MAX_LEN];
+	size_t gtk_len;
+};
+
+#ifdef CONFIG_IEEE80211W
+struct wpa_igtk {
+	u8 igtk[WPA_IGTK_MAX_LEN];
+	size_t igtk_len;
+};
+#endif /* CONFIG_IEEE80211W */
+
+/* WPA IE version 1
+ * 00-50-f2:1 (OUI:OUI type)
+ * 0x01 0x00 (version; little endian)
+ * (all following fields are optional:)
+ * Group Suite Selector (4 octets) (default: TKIP)
+ * Pairwise Suite Count (2 octets, little endian) (default: 1)
+ * Pairwise Suite List (4 * n octets) (default: TKIP)
+ * Authenticated Key Management Suite Count (2 octets, little endian)
+ *    (default: 1)
+ * Authenticated Key Management Suite List (4 * n octets)
+ *    (default: unspec 802.1X)
+ * WPA Capabilities (2 octets, little endian) (default: 0)
+ */
+
+struct wpa_ie_hdr {
+	u8 elem_id;
+	u8 len;
+	u8 oui[4]; /* 24-bit OUI followed by 8-bit OUI type */
+	u8 version[2]; /* little endian */
+} STRUCT_PACKED;
+
+
+/* 1/4: PMKID
+ * 2/4: RSN IE
+ * 3/4: one or two RSN IEs + GTK IE (encrypted)
+ * 4/4: empty
+ * 1/2: GTK IE (encrypted)
+ * 2/2: empty
+ */
+
+/* RSN IE version 1
+ * 0x01 0x00 (version; little endian)
+ * (all following fields are optional:)
+ * Group Suite Selector (4 octets) (default: CCMP)
+ * Pairwise Suite Count (2 octets, little endian) (default: 1)
+ * Pairwise Suite List (4 * n octets) (default: CCMP)
+ * Authenticated Key Management Suite Count (2 octets, little endian)
+ *    (default: 1)
+ * Authenticated Key Management Suite List (4 * n octets)
+ *    (default: unspec 802.1X)
+ * RSN Capabilities (2 octets, little endian) (default: 0)
+ * PMKID Count (2 octets) (default: 0)
+ * PMKID List (16 * n octets)
+ * Management Group Cipher Suite (4 octets) (default: AES-128-CMAC)
+ */
+
+struct rsn_ie_hdr {
+	u8 elem_id; /* WLAN_EID_RSN */
+	u8 len;
+	u8 version[2]; /* little endian */
+} STRUCT_PACKED;
+
+
+#ifdef CONFIG_PEERKEY
+enum {
+	STK_MUI_4WAY_STA_AP = 1,
+	STK_MUI_4WAY_STAT_STA = 2,
+	STK_MUI_GTK = 3,
+	STK_MUI_SMK = 4
+};
+
+enum {
+	STK_ERR_STA_NR = 1,
+	STK_ERR_STA_NRSN = 2,
+	STK_ERR_CPHR_NS = 3,
+	STK_ERR_NO_STSL = 4
+};
+#endif /* CONFIG_PEERKEY */
+
+struct rsn_error_kde {
+	be16 mui;
+	be16 error_type;
+} STRUCT_PACKED;
+
+#ifdef CONFIG_IEEE80211W
+#define WPA_IGTK_KDE_PREFIX_LEN (2 + 6)
+struct wpa_igtk_kde {
+	u8 keyid[2];
+	u8 pn[6];
+	u8 igtk[WPA_IGTK_MAX_LEN];
+} STRUCT_PACKED;
+#endif /* CONFIG_IEEE80211W */
+
+struct rsn_mdie {
+	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+	u8 ft_capab;
+} STRUCT_PACKED;
+
+#define RSN_FT_CAPAB_FT_OVER_DS BIT(0)
+#define RSN_FT_CAPAB_FT_RESOURCE_REQ_SUPP BIT(1)
+
+struct rsn_ftie {
+	u8 mic_control[2];
+	u8 mic[16];
+	u8 anonce[WPA_NONCE_LEN];
+	u8 snonce[WPA_NONCE_LEN];
+	/* followed by optional parameters */
+} STRUCT_PACKED;
+
+#define FTIE_SUBELEM_R1KH_ID 1
+#define FTIE_SUBELEM_GTK 2
+#define FTIE_SUBELEM_R0KH_ID 3
+#define FTIE_SUBELEM_IGTK 4
+
+struct rsn_rdie {
+	u8 id;
+	u8 descr_count;
+	le16 status_code;
+} STRUCT_PACKED;
+
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
+		      const u8 *buf, size_t len, u8 *mic);
+int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
+		   const u8 *addr1, const u8 *addr2,
+		   const u8 *nonce1, const u8 *nonce2,
+		   struct wpa_ptk *ptk, int akmp, int cipher);
+
+#ifdef CONFIG_IEEE80211R
+int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+	       const u8 *ap_addr, u8 transaction_seqnum,
+	       const u8 *mdie, size_t mdie_len,
+	       const u8 *ftie, size_t ftie_len,
+	       const u8 *rsnie, size_t rsnie_len,
+	       const u8 *ric, size_t ric_len, u8 *mic);
+void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
+		       const u8 *ssid, size_t ssid_len,
+		       const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
+		       const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name);
+void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
+			    const u8 *s1kh_id, u8 *pmk_r1_name);
+void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
+		       const u8 *r1kh_id, const u8 *s1kh_id,
+		       u8 *pmk_r1, u8 *pmk_r1_name);
+int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
+		      const u8 *sta_addr, const u8 *bssid,
+		      const u8 *pmk_r1_name,
+		      struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher);
+#endif /* CONFIG_IEEE80211R */
+
+struct wpa_ie_data {
+	int proto;
+	int pairwise_cipher;
+	int group_cipher;
+	int key_mgmt;
+	int capabilities;
+	size_t num_pmkid;
+	const u8 *pmkid;
+	int mgmt_group_cipher;
+};
+
+
+int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
+			 struct wpa_ie_data *data);
+int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
+			 struct wpa_ie_data *data);
+
+void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
+	       u8 *pmkid, int use_sha256);
+#ifdef CONFIG_SUITEB
+int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
+		       const u8 *spa, u8 *pmkid);
+#else /* CONFIG_SUITEB */
+static inline int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
+				    const u8 *spa, u8 *pmkid)
+{
+	return -1;
+}
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa,
+			  const u8 *spa, u8 *pmkid);
+#else /* CONFIG_SUITEB192 */
+static inline int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len,
+					const u8 *aa, const u8 *spa, u8 *pmkid)
+{
+	return -1;
+}
+#endif /* CONFIG_SUITEB192 */
+
+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);
+int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid);
+
+struct wpa_ft_ies {
+	const u8 *mdie;
+	size_t mdie_len;
+	const u8 *ftie;
+	size_t ftie_len;
+	const u8 *r1kh_id;
+	const u8 *gtk;
+	size_t gtk_len;
+	const u8 *r0kh_id;
+	size_t r0kh_id_len;
+	const u8 *rsn;
+	size_t rsn_len;
+	const u8 *rsn_pmkid;
+	const u8 *tie;
+	size_t tie_len;
+	const u8 *igtk;
+	size_t igtk_len;
+	const u8 *ric;
+	size_t ric_len;
+};
+
+int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse);
+
+int wpa_cipher_key_len(int cipher);
+int wpa_cipher_rsc_len(int cipher);
+int wpa_cipher_to_alg(int cipher);
+int wpa_cipher_valid_group(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);
+int wpa_pick_pairwise_cipher(int ciphers, int none_allowed);
+int wpa_pick_group_cipher(int ciphers);
+int wpa_parse_cipher(const char *value);
+int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
+int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
+unsigned int wpa_mic_len(int akmp);
+
+#endif /* WPA_COMMON_H */
diff --git a/hostap/src/common/wpa_ctrl.c b/hostap/src/common/wpa_ctrl.c
new file mode 100644
index 0000000..5733aa6
--- /dev/null
+++ b/hostap/src/common/wpa_ctrl.c
@@ -0,0 +1,750 @@
+/*
+ * wpa_supplicant/hostapd control interface library
+ * Copyright (c) 2004-2007, 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"
+
+#ifdef CONFIG_CTRL_IFACE
+
+#ifdef CONFIG_CTRL_IFACE_UNIX
+#include <sys/un.h>
+#include <unistd.h>
+#include <fcntl.h>
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+#include <netdb.h>
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
+#ifdef ANDROID
+#include <dirent.h>
+#include <sys/stat.h>
+#include <cutils/sockets.h>
+#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"
+
+
+#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP)
+#define CTRL_IFACE_SOCKET
+#endif /* CONFIG_CTRL_IFACE_UNIX || CONFIG_CTRL_IFACE_UDP */
+
+
+/**
+ * struct wpa_ctrl - Internal structure for control interface library
+ *
+ * This structure is used by the wpa_supplicant/hostapd control interface
+ * library to store internal data. Programs using the library should not touch
+ * this data directly. They can only use the pointer to the data structure as
+ * an identifier for the control interface connection and use this as one of
+ * the arguments for most of the control interface library functions.
+ */
+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;
+#endif /* CONFIG_CTRL_IFACE_UDP */
+#ifdef CONFIG_CTRL_IFACE_UNIX
+	int s;
+	struct sockaddr_un local;
+	struct sockaddr_un dest;
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+	HANDLE pipe;
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+};
+
+
+#ifdef CONFIG_CTRL_IFACE_UNIX
+
+#ifndef CONFIG_CTRL_IFACE_CLIENT_DIR
+#define CONFIG_CTRL_IFACE_CLIENT_DIR "/tmp"
+#endif /* CONFIG_CTRL_IFACE_CLIENT_DIR */
+#ifndef CONFIG_CTRL_IFACE_CLIENT_PREFIX
+#define CONFIG_CTRL_IFACE_CLIENT_PREFIX "wpa_ctrl_"
+#endif /* CONFIG_CTRL_IFACE_CLIENT_PREFIX */
+
+
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
+{
+	return wpa_ctrl_open2(ctrl_path, NULL);
+}
+
+
+struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path,
+				 const char *cli_path)
+{
+	struct wpa_ctrl *ctrl;
+	static int counter = 0;
+	int ret;
+	size_t res;
+	int tries = 0;
+	int flags;
+
+	if (ctrl_path == NULL)
+		return NULL;
+
+	ctrl = os_zalloc(sizeof(*ctrl));
+	if (ctrl == NULL)
+		return NULL;
+
+	ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (ctrl->s < 0) {
+		os_free(ctrl);
+		return NULL;
+	}
+
+	ctrl->local.sun_family = AF_UNIX;
+	counter++;
+try_again:
+	if (cli_path && cli_path[0] == '/') {
+		ret = os_snprintf(ctrl->local.sun_path,
+				  sizeof(ctrl->local.sun_path),
+				  "%s/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
+				  cli_path, (int) getpid(), counter);
+	} else {
+		ret = os_snprintf(ctrl->local.sun_path,
+				  sizeof(ctrl->local.sun_path),
+				  CONFIG_CTRL_IFACE_CLIENT_DIR "/"
+				  CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
+				  (int) getpid(), counter);
+	}
+	if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) {
+		close(ctrl->s);
+		os_free(ctrl);
+		return NULL;
+	}
+	tries++;
+	if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
+		    sizeof(ctrl->local)) < 0) {
+		if (errno == EADDRINUSE && tries < 2) {
+			/*
+			 * getpid() returns unique identifier for this instance
+			 * of wpa_ctrl, so the existing socket file must have
+			 * been left by unclean termination of an earlier run.
+			 * Remove the file and try again.
+			 */
+			unlink(ctrl->local.sun_path);
+			goto try_again;
+		}
+		close(ctrl->s);
+		os_free(ctrl);
+		return NULL;
+	}
+
+#ifdef ANDROID
+	chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+	/* Set group even if we do not have privileges to change owner */
+	chown(ctrl->local.sun_path, -1, AID_WIFI);
+	chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
+
+	if (os_strncmp(ctrl_path, "@android:", 9) == 0) {
+		if (socket_local_client_connect(
+			    ctrl->s, ctrl_path + 9,
+			    ANDROID_SOCKET_NAMESPACE_RESERVED,
+			    SOCK_DGRAM) < 0) {
+			close(ctrl->s);
+			unlink(ctrl->local.sun_path);
+			os_free(ctrl);
+			return NULL;
+		}
+		return ctrl;
+	}
+
+	/*
+	 * If the ctrl_path isn't an absolute pathname, assume that
+	 * it's the name of a socket in the Android reserved namespace.
+	 * Otherwise, it's a normal UNIX domain socket appearing in the
+	 * filesystem.
+	 */
+	if (*ctrl_path != '/') {
+		char buf[21];
+		os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path);
+		if (socket_local_client_connect(
+			    ctrl->s, buf,
+			    ANDROID_SOCKET_NAMESPACE_RESERVED,
+			    SOCK_DGRAM) < 0) {
+			close(ctrl->s);
+			unlink(ctrl->local.sun_path);
+			os_free(ctrl);
+			return NULL;
+		}
+		return ctrl;
+	}
+#endif /* ANDROID */
+
+	ctrl->dest.sun_family = AF_UNIX;
+	if (os_strncmp(ctrl_path, "@abstract:", 10) == 0) {
+		ctrl->dest.sun_path[0] = '\0';
+		os_strlcpy(ctrl->dest.sun_path + 1, ctrl_path + 10,
+			   sizeof(ctrl->dest.sun_path) - 1);
+	} else {
+		res = os_strlcpy(ctrl->dest.sun_path, ctrl_path,
+				 sizeof(ctrl->dest.sun_path));
+		if (res >= sizeof(ctrl->dest.sun_path)) {
+			close(ctrl->s);
+			os_free(ctrl);
+			return NULL;
+		}
+	}
+	if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
+		    sizeof(ctrl->dest)) < 0) {
+		close(ctrl->s);
+		unlink(ctrl->local.sun_path);
+		os_free(ctrl);
+		return NULL;
+	}
+
+	/*
+	 * Make socket non-blocking so that we don't hang forever if
+	 * target dies unexpectedly.
+	 */
+	flags = fcntl(ctrl->s, F_GETFL);
+	if (flags >= 0) {
+		flags |= O_NONBLOCK;
+		if (fcntl(ctrl->s, F_SETFL, flags) < 0) {
+			perror("fcntl(ctrl->s, O_NONBLOCK)");
+			/* Not fatal, continue on.*/
+		}
+	}
+
+	return ctrl;
+}
+
+
+void wpa_ctrl_close(struct wpa_ctrl *ctrl)
+{
+	if (ctrl == NULL)
+		return;
+	unlink(ctrl->local.sun_path);
+	if (ctrl->s >= 0)
+		close(ctrl->s);
+	os_free(ctrl);
+}
+
+
+#ifdef ANDROID
+/**
+ * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
+ * may be left over from clients that were previously connected to
+ * wpa_supplicant. This keeps these files from being orphaned in the
+ * event of crashes that prevented them from being removed as part
+ * of the normal orderly shutdown.
+ */
+void wpa_ctrl_cleanup(void)
+{
+	DIR *dir;
+	struct dirent entry;
+	struct dirent *result;
+	size_t dirnamelen;
+	size_t maxcopy;
+	char pathname[PATH_MAX];
+	char *namep;
+
+	if ((dir = opendir(CONFIG_CTRL_IFACE_CLIENT_DIR)) == NULL)
+		return;
+
+	dirnamelen = (size_t) os_snprintf(pathname, sizeof(pathname), "%s/",
+					  CONFIG_CTRL_IFACE_CLIENT_DIR);
+	if (dirnamelen >= sizeof(pathname)) {
+		closedir(dir);
+		return;
+	}
+	namep = pathname + dirnamelen;
+	maxcopy = PATH_MAX - dirnamelen;
+	while (readdir_r(dir, &entry, &result) == 0 && result != NULL) {
+		if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy)
+			unlink(pathname);
+	}
+	closedir(dir);
+}
+#endif /* ANDROID */
+
+#else /* CONFIG_CTRL_IFACE_UNIX */
+
+#ifdef ANDROID
+void wpa_ctrl_cleanup(void)
+{
+}
+#endif /* ANDROID */
+
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
+{
+	struct wpa_ctrl *ctrl;
+	char buf[128];
+	size_t len;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	struct hostent *h;
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
+	ctrl = os_zalloc(sizeof(*ctrl));
+	if (ctrl == NULL)
+		return NULL;
+
+#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);
+		os_free(ctrl);
+		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) {
+			close(ctrl->s);
+			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]);
+			port[0] = '\0';
+		} 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) {
+			perror("gethostbyname");
+			close(ctrl->s);
+			os_free(ctrl->remote_ip);
+			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(&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) {
+#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);
+		return NULL;
+	}
+
+	len = sizeof(buf) - 1;
+	if (wpa_ctrl_request(ctrl, "GET_COOKIE", 10, buf, &len, NULL) == 0) {
+		buf[len] = '\0';
+		ctrl->cookie = os_strdup(buf);
+	}
+
+	if (wpa_ctrl_request(ctrl, "IFNAME", 6, buf, &len, NULL) == 0) {
+		buf[len] = '\0';
+		ctrl->remote_ifname = os_strdup(buf);
+	}
+
+	return ctrl;
+}
+
+
+char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl)
+{
+#define WPA_CTRL_MAX_PS_NAME 100
+	static char ps[WPA_CTRL_MAX_PS_NAME] = {};
+	os_snprintf(ps, WPA_CTRL_MAX_PS_NAME, "%s/%s",
+		    ctrl->remote_ip, ctrl->remote_ifname);
+	return ps;
+}
+
+
+void wpa_ctrl_close(struct wpa_ctrl *ctrl)
+{
+	close(ctrl->s);
+	os_free(ctrl->cookie);
+	os_free(ctrl->remote_ifname);
+	os_free(ctrl->remote_ip);
+	os_free(ctrl);
+}
+
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+
+#ifdef CTRL_IFACE_SOCKET
+int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+		     char *reply, size_t *reply_len,
+		     void (*msg_cb)(char *msg, size_t len))
+{
+	struct timeval tv;
+	struct os_reltime started_at;
+	int res;
+	fd_set rfds;
+	const char *_cmd;
+	char *cmd_buf = NULL;
+	size_t _cmd_len;
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+	if (ctrl->cookie) {
+		char *pos;
+		_cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len;
+		cmd_buf = os_malloc(_cmd_len);
+		if (cmd_buf == NULL)
+			return -1;
+		_cmd = cmd_buf;
+		pos = cmd_buf;
+		os_strlcpy(pos, ctrl->cookie, _cmd_len);
+		pos += os_strlen(ctrl->cookie);
+		*pos++ = ' ';
+		os_memcpy(pos, cmd, cmd_len);
+	} else
+#endif /* CONFIG_CTRL_IFACE_UDP */
+	{
+		_cmd = cmd;
+		_cmd_len = cmd_len;
+	}
+
+	errno = 0;
+	started_at.sec = 0;
+	started_at.usec = 0;
+retry_send:
+	if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) {
+		if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK)
+		{
+			/*
+			 * Must be a non-blocking socket... Try for a bit
+			 * longer before giving up.
+			 */
+			if (started_at.sec == 0)
+				os_get_reltime(&started_at);
+			else {
+				struct os_reltime n;
+				os_get_reltime(&n);
+				/* Try for a few seconds. */
+				if (os_reltime_expired(&n, &started_at, 5))
+					goto send_err;
+			}
+			os_sleep(1, 0);
+			goto retry_send;
+		}
+	send_err:
+		os_free(cmd_buf);
+		return -1;
+	}
+	os_free(cmd_buf);
+
+	for (;;) {
+		tv.tv_sec = 10;
+		tv.tv_usec = 0;
+		FD_ZERO(&rfds);
+		FD_SET(ctrl->s, &rfds);
+		res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
+		if (res < 0)
+			return res;
+		if (FD_ISSET(ctrl->s, &rfds)) {
+			res = recv(ctrl->s, reply, *reply_len, 0);
+			if (res < 0)
+				return res;
+			if (res > 0 && reply[0] == '<') {
+				/* This is an unsolicited message from
+				 * wpa_supplicant, not the reply to the
+				 * request. Use msg_cb to report this to the
+				 * caller. */
+				if (msg_cb) {
+					/* Make sure the message is nul
+					 * terminated. */
+					if ((size_t) res == *reply_len)
+						res = (*reply_len) - 1;
+					reply[res] = '\0';
+					msg_cb(reply, res);
+				}
+				continue;
+			}
+			*reply_len = res;
+			break;
+		} else {
+			return -2;
+		}
+	}
+	return 0;
+}
+#endif /* CTRL_IFACE_SOCKET */
+
+
+static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach)
+{
+	char buf[10];
+	int ret;
+	size_t len = 10;
+
+	ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6,
+			       buf, &len, NULL);
+	if (ret < 0)
+		return ret;
+	if (len == 3 && os_memcmp(buf, "OK\n", 3) == 0)
+		return 0;
+	return -1;
+}
+
+
+int wpa_ctrl_attach(struct wpa_ctrl *ctrl)
+{
+	return wpa_ctrl_attach_helper(ctrl, 1);
+}
+
+
+int wpa_ctrl_detach(struct wpa_ctrl *ctrl)
+{
+	return wpa_ctrl_attach_helper(ctrl, 0);
+}
+
+
+#ifdef CTRL_IFACE_SOCKET
+
+int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
+{
+	int res;
+
+	res = recv(ctrl->s, reply, *reply_len, 0);
+	if (res < 0)
+		return res;
+	*reply_len = res;
+	return 0;
+}
+
+
+int wpa_ctrl_pending(struct wpa_ctrl *ctrl)
+{
+	struct timeval tv;
+	fd_set rfds;
+	tv.tv_sec = 0;
+	tv.tv_usec = 0;
+	FD_ZERO(&rfds);
+	FD_SET(ctrl->s, &rfds);
+	select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
+	return FD_ISSET(ctrl->s, &rfds);
+}
+
+
+int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
+{
+	return ctrl->s;
+}
+
+#endif /* CTRL_IFACE_SOCKET */
+
+
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+
+#ifndef WPA_SUPPLICANT_NAMED_PIPE
+#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant"
+#endif
+#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE)
+
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
+{
+	struct wpa_ctrl *ctrl;
+	DWORD mode;
+	TCHAR name[256];
+	int i, ret;
+
+	ctrl = os_malloc(sizeof(*ctrl));
+	if (ctrl == NULL)
+		return NULL;
+	os_memset(ctrl, 0, sizeof(*ctrl));
+
+#ifdef UNICODE
+	if (ctrl_path == NULL)
+		ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX);
+	else
+		ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"),
+				 ctrl_path);
+#else /* UNICODE */
+	if (ctrl_path == NULL)
+		ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX);
+	else
+		ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s",
+				  ctrl_path);
+#endif /* UNICODE */
+	if (os_snprintf_error(256, ret)) {
+		os_free(ctrl);
+		return NULL;
+	}
+
+	for (i = 0; i < 10; i++) {
+		ctrl->pipe = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0,
+					NULL, OPEN_EXISTING, 0, NULL);
+		/*
+		 * Current named pipe server side in wpa_supplicant is
+		 * re-opening the pipe for new clients only after the previous
+		 * one is taken into use. This leaves a small window for race
+		 * conditions when two connections are being opened at almost
+		 * the same time. Retry if that was the case.
+		 */
+		if (ctrl->pipe != INVALID_HANDLE_VALUE ||
+		    GetLastError() != ERROR_PIPE_BUSY)
+			break;
+		WaitNamedPipe(name, 1000);
+	}
+	if (ctrl->pipe == INVALID_HANDLE_VALUE) {
+		os_free(ctrl);
+		return NULL;
+	}
+
+	mode = PIPE_READMODE_MESSAGE;
+	if (!SetNamedPipeHandleState(ctrl->pipe, &mode, NULL, NULL)) {
+		CloseHandle(ctrl->pipe);
+		os_free(ctrl);
+		return NULL;
+	}
+
+	return ctrl;
+}
+
+
+void wpa_ctrl_close(struct wpa_ctrl *ctrl)
+{
+	CloseHandle(ctrl->pipe);
+	os_free(ctrl);
+}
+
+
+int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+		     char *reply, size_t *reply_len,
+		     void (*msg_cb)(char *msg, size_t len))
+{
+	DWORD written;
+	DWORD readlen = *reply_len;
+
+	if (!WriteFile(ctrl->pipe, cmd, cmd_len, &written, NULL))
+		return -1;
+
+	if (!ReadFile(ctrl->pipe, reply, *reply_len, &readlen, NULL))
+		return -1;
+	*reply_len = readlen;
+
+	return 0;
+}
+
+
+int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
+{
+	DWORD len = *reply_len;
+	if (!ReadFile(ctrl->pipe, reply, *reply_len, &len, NULL))
+		return -1;
+	*reply_len = len;
+	return 0;
+}
+
+
+int wpa_ctrl_pending(struct wpa_ctrl *ctrl)
+{
+	DWORD left;
+
+	if (!PeekNamedPipe(ctrl->pipe, NULL, 0, NULL, &left, NULL))
+		return -1;
+	return left ? 1 : 0;
+}
+
+
+int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
+{
+	return -1;
+}
+
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+
+#endif /* CONFIG_CTRL_IFACE */
diff --git a/hostap/src/common/wpa_ctrl.h b/hostap/src/common/wpa_ctrl.h
new file mode 100644
index 0000000..3de4682
--- /dev/null
+++ b/hostap/src/common/wpa_ctrl.h
@@ -0,0 +1,472 @@
+/*
+ * wpa_supplicant/hostapd control interface library
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_CTRL_H
+#define WPA_CTRL_H
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/* wpa_supplicant control interface - fixed message prefixes */
+
+/** Interactive request for identity/password/pin */
+#define WPA_CTRL_REQ "CTRL-REQ-"
+
+/** Response to identity/password/pin request */
+#define WPA_CTRL_RSP "CTRL-RSP-"
+
+/* Event messages with fixed prefix */
+/** Authentication completed successfully and data connection enabled */
+#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED "
+/** Disconnected, data connection is not available */
+#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED "
+/** Association rejected during connection attempt */
+#define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT "
+/** Authentication rejected during connection attempt */
+#define WPA_EVENT_AUTH_REJECT "CTRL-EVENT-AUTH-REJECT "
+/** wpa_supplicant is exiting */
+#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
+/** Password change was completed successfully */
+#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED "
+/** EAP-Request/Notification received */
+#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION "
+/** EAP authentication started (EAP-Request/Identity received) */
+#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED "
+/** EAP method proposed by the server */
+#define WPA_EVENT_EAP_PROPOSED_METHOD "CTRL-EVENT-EAP-PROPOSED-METHOD "
+/** EAP method selected */
+#define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD "
+/** EAP peer certificate from TLS */
+#define WPA_EVENT_EAP_PEER_CERT "CTRL-EVENT-EAP-PEER-CERT "
+/** EAP peer certificate alternative subject name component from TLS */
+#define WPA_EVENT_EAP_PEER_ALT "CTRL-EVENT-EAP-PEER-ALT "
+/** EAP TLS certificate chain validation error */
+#define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR "
+/** EAP status */
+#define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS "
+/** EAP authentication completed successfully */
+#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
+/** EAP authentication failed (EAP-Failure received) */
+#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
+/** Network block temporarily disabled (e.g., due to authentication failure) */
+#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 "
+/** Scan command failed */
+#define WPA_EVENT_SCAN_FAILED "CTRL-EVENT-SCAN-FAILED "
+/** wpa_supplicant state change */
+#define WPA_EVENT_STATE_CHANGE "CTRL-EVENT-STATE-CHANGE "
+/** A new BSS entry was added (followed by BSS entry id and BSSID) */
+#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 "
+/** No suitable network was found */
+#define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND "
+/** 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 */
+#define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC "
+/** Available WPS AP with our address as authorized in scan results */
+#define WPS_EVENT_AP_AVAILABLE_AUTH "WPS-AP-AVAILABLE-AUTH "
+/** Available WPS AP with recently selected PIN registrar found in scan results
+ */
+#define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN "
+/** Available WPS AP found in scan results */
+#define WPS_EVENT_AP_AVAILABLE "WPS-AP-AVAILABLE "
+/** A new credential received */
+#define WPS_EVENT_CRED_RECEIVED "WPS-CRED-RECEIVED "
+/** M2D received */
+#define WPS_EVENT_M2D "WPS-M2D "
+/** WPS registration failed after M2/M2D */
+#define WPS_EVENT_FAIL "WPS-FAIL "
+/** WPS registration completed successfully */
+#define WPS_EVENT_SUCCESS "WPS-SUCCESS "
+/** WPS enrollment attempt timed out and was terminated */
+#define WPS_EVENT_TIMEOUT "WPS-TIMEOUT "
+/* PBC mode was activated */
+#define WPS_EVENT_ACTIVE "WPS-PBC-ACTIVE "
+/* PBC mode was disabled */
+#define WPS_EVENT_DISABLE "WPS-PBC-DISABLE "
+
+#define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN "
+
+#define WPS_EVENT_OPEN_NETWORK "WPS-OPEN-NETWORK "
+
+/* WPS ER events */
+#define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD "
+#define WPS_EVENT_ER_AP_REMOVE "WPS-ER-AP-REMOVE "
+#define WPS_EVENT_ER_ENROLLEE_ADD "WPS-ER-ENROLLEE-ADD "
+#define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE "
+#define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
+#define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG "
+
+/* MESH events */
+#define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
+#define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED "
+#define MESH_PEER_CONNECTED "MESH-PEER-CONNECTED "
+#define MESH_PEER_DISCONNECTED "MESH-PEER-DISCONNECTED "
+/** Mesh SAE authentication failure. Wrong password suspected. */
+#define MESH_SAE_AUTH_FAILURE "MESH-SAE-AUTH-FAILURE "
+#define MESH_SAE_AUTH_BLOCKED "MESH-SAE-AUTH-BLOCKED "
+
+/* WMM AC events */
+#define WMM_AC_EVENT_TSPEC_ADDED "TSPEC-ADDED "
+#define WMM_AC_EVENT_TSPEC_REMOVED "TSPEC-REMOVED "
+#define WMM_AC_EVENT_TSPEC_REQ_FAILED "TSPEC-REQ-FAILED "
+
+/** P2P device found */
+#define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND "
+
+/** P2P device lost */
+#define P2P_EVENT_DEVICE_LOST "P2P-DEVICE-LOST "
+
+/** A P2P device requested GO negotiation, but we were not ready to start the
+ * negotiation */
+#define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST "
+#define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS "
+#define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE "
+#define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS "
+#define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE "
+#define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED "
+#define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED "
+#define P2P_EVENT_CROSS_CONNECT_ENABLE "P2P-CROSS-CONNECT-ENABLE "
+#define P2P_EVENT_CROSS_CONNECT_DISABLE "P2P-CROSS-CONNECT-DISABLE "
+/* parameters: <peer address> <PIN> */
+#define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP "
+/* parameters: <peer address> <status> */
+#define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE"
+/* parameters: <freq> <src addr> <dialog token> <update indicator> <TLVs> */
+#define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ "
+/* parameters: <src addr> <update indicator> <TLVs> */
+#define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP "
+#define P2P_EVENT_SERV_ASP_RESP "P2P-SERV-ASP-RESP "
+#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
+#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 "
+#define P2P_EVENT_FALLBACK_TO_GO_NEG "P2P-FALLBACK-TO-GO-NEG "
+#define P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED "P2P-FALLBACK-TO-GO-NEG-ENABLED "
+
+/* 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 P2P_EVENT_P2PS_PROVISION_START "P2PS-PROV-START "
+#define P2P_EVENT_P2PS_PROVISION_DONE "P2PS-PROV-DONE "
+
+#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 "
+
+/* parameters: <addr> <result> */
+#define ANQP_QUERY_DONE "ANQP-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 "
+
+#define RRM_EVENT_NEIGHBOR_REP_RXED "RRM-NEIGHBOR-REP-RECEIVED "
+#define RRM_EVENT_NEIGHBOR_REP_FAILED "RRM-NEIGHBOR-REP-REQUEST-FAILED "
+
+/* hostapd control interface - fixed message prefixes */
+#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
+#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
+#define WPS_EVENT_REG_SUCCESS "WPS-REG-SUCCESS "
+#define WPS_EVENT_AP_SETUP_LOCKED "WPS-AP-SETUP-LOCKED "
+#define WPS_EVENT_AP_SETUP_UNLOCKED "WPS-AP-SETUP-UNLOCKED "
+#define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED "
+#define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED "
+#define AP_STA_CONNECTED "AP-STA-CONNECTED "
+#define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
+#define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH "
+
+#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 INTERFACE_ENABLED "INTERFACE-ENABLED "
+#define INTERFACE_DISABLED "INTERFACE-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 Transition Management Response frame received */
+#define BSS_TM_RESP "BSS-TM-RESP "
+
+/* BSS command information masks */
+
+#define WPA_BSS_MASK_ALL		0xFFFDFFFF
+#define WPA_BSS_MASK_ID			BIT(0)
+#define WPA_BSS_MASK_BSSID		BIT(1)
+#define WPA_BSS_MASK_FREQ		BIT(2)
+#define WPA_BSS_MASK_BEACON_INT		BIT(3)
+#define WPA_BSS_MASK_CAPABILITIES	BIT(4)
+#define WPA_BSS_MASK_QUAL		BIT(5)
+#define WPA_BSS_MASK_NOISE		BIT(6)
+#define WPA_BSS_MASK_LEVEL		BIT(7)
+#define WPA_BSS_MASK_TSF		BIT(8)
+#define WPA_BSS_MASK_AGE		BIT(9)
+#define WPA_BSS_MASK_IE			BIT(10)
+#define WPA_BSS_MASK_FLAGS		BIT(11)
+#define WPA_BSS_MASK_SSID		BIT(12)
+#define WPA_BSS_MASK_WPS_SCAN		BIT(13)
+#define WPA_BSS_MASK_P2P_SCAN		BIT(14)
+#define WPA_BSS_MASK_INTERNETW		BIT(15)
+#define WPA_BSS_MASK_WIFI_DISPLAY	BIT(16)
+#define WPA_BSS_MASK_DELIM		BIT(17)
+#define WPA_BSS_MASK_MESH_SCAN		BIT(18)
+#define WPA_BSS_MASK_SNR		BIT(19)
+#define WPA_BSS_MASK_EST_THROUGHPUT	BIT(20)
+#define WPA_BSS_MASK_FST		BIT(21)
+
+
+/* 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,
+	VENDOR_ELEM_ASSOC_REQ = 13,
+	NUM_VENDOR_ELEM_FRAMES
+};
+
+
+/* wpa_supplicant/hostapd control interface access */
+
+/**
+ * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd
+ * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
+ * Returns: Pointer to abstract control interface data or %NULL on failure
+ *
+ * This function is used to open a control interface to wpa_supplicant/hostapd.
+ * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path
+ * is configured in wpa_supplicant/hostapd and other programs using the control
+ * interface need to use matching path configuration.
+ */
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
+
+/**
+ * wpa_ctrl_open2 - Open a control interface to wpa_supplicant/hostapd
+ * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
+ * @cli_path: Path for client UNIX domain sockets; ignored if UDP socket
+ *            is used.
+ * Returns: Pointer to abstract control interface data or %NULL on failure
+ *
+ * This function is used to open a control interface to wpa_supplicant/hostapd
+ * when the socket path for client need to be specified explicitly. Default
+ * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd and client
+ * socket path is /tmp.
+ */
+struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path);
+
+
+/**
+ * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ *
+ * This function is used to close a control interface.
+ */
+void wpa_ctrl_close(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * @cmd: Command; usually, ASCII text, e.g., "PING"
+ * @cmd_len: Length of the cmd in bytes
+ * @reply: Buffer for the response
+ * @reply_len: Reply buffer length
+ * @msg_cb: Callback function for unsolicited messages or %NULL if not used
+ * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout
+ *
+ * This function is used to send commands to wpa_supplicant/hostapd. Received
+ * response will be written to reply and reply_len is set to the actual length
+ * of the reply. This function will block for up to two seconds while waiting
+ * for the reply. If unsolicited messages are received, the blocking time may
+ * be longer.
+ *
+ * msg_cb can be used to register a callback function that will be called for
+ * unsolicited messages received while waiting for the command response. These
+ * messages may be received if wpa_ctrl_request() is called at the same time as
+ * wpa_supplicant/hostapd is sending such a message. This can happen only if
+ * the program has used wpa_ctrl_attach() to register itself as a monitor for
+ * event messages. Alternatively to msg_cb, programs can register two control
+ * interface connections and use one of them for commands and the other one for
+ * receiving event messages, in other words, call wpa_ctrl_attach() only for
+ * the control interface connection that will be used for event messages.
+ */
+int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+		     char *reply, size_t *reply_len,
+		     void (*msg_cb)(char *msg, size_t len));
+
+
+/**
+ * wpa_ctrl_attach - Register as an event monitor for the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: 0 on success, -1 on failure, -2 on timeout
+ *
+ * This function registers the control interface connection as a monitor for
+ * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the
+ * control interface connection starts receiving event messages that can be
+ * read with wpa_ctrl_recv().
+ */
+int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_detach - Unregister event monitor from the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: 0 on success, -1 on failure, -2 on timeout
+ *
+ * This function unregisters the control interface connection as a monitor for
+ * wpa_supplicant/hostapd events, i.e., cancels the registration done with
+ * wpa_ctrl_attach().
+ */
+int wpa_ctrl_detach(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_recv - Receive a pending control interface message
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * @reply: Buffer for the message data
+ * @reply_len: Length of the reply buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function will receive a pending control interface message. The received
+ * response will be written to reply and reply_len is set to the actual length
+ * of the reply.
+
+ * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach()
+ * must have been used to register the control interface as an event monitor.
+ */
+int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);
+
+
+/**
+ * wpa_ctrl_pending - Check whether there are pending event messages
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: 1 if there are pending messages, 0 if no, or -1 on error
+ *
+ * This function will check whether there are any pending control interface
+ * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is
+ * only used for event messages, i.e., wpa_ctrl_attach() must have been used to
+ * register the control interface as an event monitor.
+ */
+int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_get_fd - Get file descriptor used by the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: File descriptor used for the connection
+ *
+ * This function can be used to get the file descriptor that is used for the
+ * control interface connection. The returned value can be used, e.g., with
+ * select() while waiting for multiple events.
+ *
+ * The returned file descriptor must not be used directly for sending or
+ * receiving packets; instead, the library functions wpa_ctrl_request() and
+ * wpa_ctrl_recv() must be used for this.
+ */
+int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
+
+#ifdef ANDROID
+/**
+ * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
+ * may be left over from clients that were previously connected to
+ * wpa_supplicant. This keeps these files from being orphaned in the
+ * event of crashes that prevented them from being removed as part
+ * of the normal orderly shutdown.
+ */
+void wpa_ctrl_cleanup(void);
+#endif /* ANDROID */
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+/* Port range for multiple wpa_supplicant instances and multiple VIFs */
+#define WPA_CTRL_IFACE_PORT 9877
+#define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */
+#define WPA_GLOBAL_CTRL_IFACE_PORT 9878
+#define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */
+
+char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* WPA_CTRL_H */
diff --git a/hostap/src/common/wpa_helpers.c b/hostap/src/common/wpa_helpers.c
new file mode 100644
index 0000000..28913b9
--- /dev/null
+++ b/hostap/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/hostap/src/common/wpa_helpers.h b/hostap/src/common/wpa_helpers.h
new file mode 100644
index 0000000..54c2872
--- /dev/null
+++ b/hostap/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/hostap/src/crypto/Makefile b/hostap/src/crypto/Makefile
new file mode 100644
index 0000000..3e90350
--- /dev/null
+++ b/hostap/src/crypto/Makefile
@@ -0,0 +1,64 @@
+all: libcrypto.a
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcrypto.a
+
+install:
+	@echo Nothing to be made.
+
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+#CFLAGS += -DALL_DH_GROUPS
+CFLAGS += -DCONFIG_SHA256
+
+LIB_OBJS= \
+	aes-cbc.o \
+	aes-ccm.o \
+	aes-ctr.o \
+	aes-eax.o \
+	aes-encblock.o \
+	aes-gcm.o \
+	aes-internal.o \
+	aes-internal-dec.o \
+	aes-internal-enc.o \
+	aes-omac1.o \
+	aes-siv.o \
+	aes-unwrap.o \
+	aes-wrap.o \
+	des-internal.o \
+	dh_group5.o \
+	dh_groups.o \
+	md4-internal.o \
+	md5.o \
+	md5-internal.o \
+	milenage.o \
+	ms_funcs.o \
+	rc4.o \
+	sha1.o \
+	sha1-internal.o \
+	sha1-pbkdf2.o \
+	sha1-prf.o \
+	sha1-tlsprf.o \
+	sha1-tprf.o \
+	sha256.o \
+	sha256-prf.o \
+	sha256-tlsprf.o \
+	sha256-internal.o
+
+LIB_OBJS += crypto_internal.o
+LIB_OBJS += crypto_internal-cipher.o
+LIB_OBJS += crypto_internal-modexp.o
+LIB_OBJS += crypto_internal-rsa.o
+LIB_OBJS += tls_internal.o
+LIB_OBJS += fips_prf_internal.o
+LIB_OBJS += random.o
+
+
+libcrypto.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/crypto/aes-cbc.c b/hostap/src/crypto/aes-cbc.c
new file mode 100644
index 0000000..2833cfc
--- /dev/null
+++ b/hostap/src/crypto/aes-cbc.c
@@ -0,0 +1,80 @@
+/*
+ * AES-128 CBC
+ *
+ * Copyright (c) 2003-2007, 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 "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_cbc_encrypt - AES-128 CBC encryption
+ * @key: Encryption key
+ * @iv: Encryption IV for CBC mode (16 bytes)
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes (must be divisible by 16)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	void *ctx;
+	u8 cbc[AES_BLOCK_SIZE];
+	u8 *pos = data;
+	int i, j, blocks;
+
+	ctx = aes_encrypt_init(key, 16);
+	if (ctx == NULL)
+		return -1;
+	os_memcpy(cbc, iv, AES_BLOCK_SIZE);
+
+	blocks = data_len / AES_BLOCK_SIZE;
+	for (i = 0; i < blocks; i++) {
+		for (j = 0; j < AES_BLOCK_SIZE; j++)
+			cbc[j] ^= pos[j];
+		aes_encrypt(ctx, cbc, cbc);
+		os_memcpy(pos, cbc, AES_BLOCK_SIZE);
+		pos += AES_BLOCK_SIZE;
+	}
+	aes_encrypt_deinit(ctx);
+	return 0;
+}
+
+
+/**
+ * aes_128_cbc_decrypt - AES-128 CBC decryption
+ * @key: Decryption key
+ * @iv: Decryption IV for CBC mode (16 bytes)
+ * @data: Data to decrypt in-place
+ * @data_len: Length of data in bytes (must be divisible by 16)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	void *ctx;
+	u8 cbc[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE];
+	u8 *pos = data;
+	int i, j, blocks;
+
+	ctx = aes_decrypt_init(key, 16);
+	if (ctx == NULL)
+		return -1;
+	os_memcpy(cbc, iv, AES_BLOCK_SIZE);
+
+	blocks = data_len / AES_BLOCK_SIZE;
+	for (i = 0; i < blocks; i++) {
+		os_memcpy(tmp, pos, AES_BLOCK_SIZE);
+		aes_decrypt(ctx, pos, pos);
+		for (j = 0; j < AES_BLOCK_SIZE; j++)
+			pos[j] ^= cbc[j];
+		os_memcpy(cbc, tmp, AES_BLOCK_SIZE);
+		pos += AES_BLOCK_SIZE;
+	}
+	aes_decrypt_deinit(ctx);
+	return 0;
+}
diff --git a/hostap/src/crypto/aes-ccm.c b/hostap/src/crypto/aes-ccm.c
new file mode 100644
index 0000000..cf22778
--- /dev/null
+++ b/hostap/src/crypto/aes-ccm.c
@@ -0,0 +1,212 @@
+/*
+ * Counter with CBC-MAC (CCM) with AES
+ *
+ * Copyright (c) 2010-2012, 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 "aes.h"
+#include "aes_wrap.h"
+
+
+static void xor_aes_block(u8 *dst, const u8 *src)
+{
+	u32 *d = (u32 *) dst;
+	u32 *s = (u32 *) src;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+}
+
+
+static void aes_ccm_auth_start(void *aes, size_t M, size_t L, const u8 *nonce,
+			       const u8 *aad, size_t aad_len, size_t plain_len,
+			       u8 *x)
+{
+	u8 aad_buf[2 * AES_BLOCK_SIZE];
+	u8 b[AES_BLOCK_SIZE];
+
+	/* Authentication */
+	/* B_0: Flags | Nonce N | l(m) */
+	b[0] = aad_len ? 0x40 : 0 /* Adata */;
+	b[0] |= (((M - 2) / 2) /* M' */ << 3);
+	b[0] |= (L - 1) /* L' */;
+	os_memcpy(&b[1], nonce, 15 - L);
+	WPA_PUT_BE16(&b[AES_BLOCK_SIZE - L], plain_len);
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM B_0", b, AES_BLOCK_SIZE);
+	aes_encrypt(aes, b, x); /* X_1 = E(K, B_0) */
+
+	if (!aad_len)
+		return;
+
+	WPA_PUT_BE16(aad_buf, aad_len);
+	os_memcpy(aad_buf + 2, aad, aad_len);
+	os_memset(aad_buf + 2 + aad_len, 0, sizeof(aad_buf) - 2 - aad_len);
+
+	xor_aes_block(aad_buf, x);
+	aes_encrypt(aes, aad_buf, x); /* X_2 = E(K, X_1 XOR B_1) */
+
+	if (aad_len > AES_BLOCK_SIZE - 2) {
+		xor_aes_block(&aad_buf[AES_BLOCK_SIZE], x);
+		/* X_3 = E(K, X_2 XOR B_2) */
+		aes_encrypt(aes, &aad_buf[AES_BLOCK_SIZE], x);
+	}
+}
+
+
+static void aes_ccm_auth(void *aes, const u8 *data, size_t len, u8 *x)
+{
+	size_t last = len % AES_BLOCK_SIZE;
+	size_t i;
+
+	for (i = 0; i < len / AES_BLOCK_SIZE; i++) {
+		/* X_i+1 = E(K, X_i XOR B_i) */
+		xor_aes_block(x, data);
+		data += AES_BLOCK_SIZE;
+		aes_encrypt(aes, x, x);
+	}
+	if (last) {
+		/* XOR zero-padded last block */
+		for (i = 0; i < last; i++)
+			x[i] ^= *data++;
+		aes_encrypt(aes, x, x);
+	}
+}
+
+
+static void aes_ccm_encr_start(size_t L, const u8 *nonce, u8 *a)
+{
+	/* A_i = Flags | Nonce N | Counter i */
+	a[0] = L - 1; /* Flags = L' */
+	os_memcpy(&a[1], nonce, 15 - L);
+}
+
+
+static void aes_ccm_encr(void *aes, size_t L, const u8 *in, size_t len, u8 *out,
+			 u8 *a)
+{
+	size_t last = len % AES_BLOCK_SIZE;
+	size_t i;
+
+	/* crypt = msg XOR (S_1 | S_2 | ... | S_n) */
+	for (i = 1; i <= len / AES_BLOCK_SIZE; i++) {
+		WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i);
+		/* S_i = E(K, A_i) */
+		aes_encrypt(aes, a, out);
+		xor_aes_block(out, in);
+		out += AES_BLOCK_SIZE;
+		in += AES_BLOCK_SIZE;
+	}
+	if (last) {
+		WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i);
+		aes_encrypt(aes, a, out);
+		/* XOR zero-padded last block */
+		for (i = 0; i < last; i++)
+			*out++ ^= *in++;
+	}
+}
+
+
+static void aes_ccm_encr_auth(void *aes, size_t M, u8 *x, u8 *a, u8 *auth)
+{
+	size_t i;
+	u8 tmp[AES_BLOCK_SIZE];
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", x, M);
+	/* U = T XOR S_0; S_0 = E(K, A_0) */
+	WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0);
+	aes_encrypt(aes, a, tmp);
+	for (i = 0; i < M; i++)
+		auth[i] = x[i] ^ tmp[i];
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M);
+}
+
+
+static void aes_ccm_decr_auth(void *aes, size_t M, u8 *a, const u8 *auth, u8 *t)
+{
+	size_t i;
+	u8 tmp[AES_BLOCK_SIZE];
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M);
+	/* U = T XOR S_0; S_0 = E(K, A_0) */
+	WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0);
+	aes_encrypt(aes, a, tmp);
+	for (i = 0; i < M; i++)
+		t[i] = auth[i] ^ tmp[i];
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", t, M);
+}
+
+
+/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */
+int aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce,
+	       size_t M, const u8 *plain, size_t plain_len,
+	       const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth)
+{
+	const size_t L = 2;
+	void *aes;
+	u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE];
+
+	if (aad_len > 30 || M > AES_BLOCK_SIZE)
+		return -1;
+
+	aes = aes_encrypt_init(key, key_len);
+	if (aes == NULL)
+		return -1;
+
+	aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, plain_len, x);
+	aes_ccm_auth(aes, plain, plain_len, x);
+
+	/* Encryption */
+	aes_ccm_encr_start(L, nonce, a);
+	aes_ccm_encr(aes, L, plain, plain_len, crypt, a);
+	aes_ccm_encr_auth(aes, M, x, a, auth);
+
+	aes_encrypt_deinit(aes);
+
+	return 0;
+}
+
+
+/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */
+int aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce,
+	       size_t M, const u8 *crypt, size_t crypt_len,
+	       const u8 *aad, size_t aad_len, const u8 *auth, u8 *plain)
+{
+	const size_t L = 2;
+	void *aes;
+	u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE];
+	u8 t[AES_BLOCK_SIZE];
+
+	if (aad_len > 30 || M > AES_BLOCK_SIZE)
+		return -1;
+
+	aes = aes_encrypt_init(key, key_len);
+	if (aes == NULL)
+		return -1;
+
+	/* Decryption */
+	aes_ccm_encr_start(L, nonce, a);
+	aes_ccm_decr_auth(aes, M, a, auth, t);
+
+	/* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */
+	aes_ccm_encr(aes, L, crypt, crypt_len, plain, a);
+
+	aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, crypt_len, x);
+	aes_ccm_auth(aes, plain, crypt_len, x);
+
+	aes_encrypt_deinit(aes);
+
+	if (os_memcmp_const(x, t, M) != 0) {
+		wpa_printf(MSG_EXCESSIVE, "CCM: Auth mismatch");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/hostap/src/crypto/aes-ctr.c b/hostap/src/crypto/aes-ctr.c
new file mode 100644
index 0000000..d4d874d
--- /dev/null
+++ b/hostap/src/crypto/aes-ctr.c
@@ -0,0 +1,55 @@
+/*
+ * AES-128 CTR
+ *
+ * Copyright (c) 2003-2007, 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 "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_ctr_encrypt - AES-128 CTR mode encryption
+ * @key: Key for encryption (16 bytes)
+ * @nonce: Nonce for counter mode (16 bytes)
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+			u8 *data, size_t data_len)
+{
+	void *ctx;
+	size_t j, len, left = data_len;
+	int i;
+	u8 *pos = data;
+	u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE];
+
+	ctx = aes_encrypt_init(key, 16);
+	if (ctx == NULL)
+		return -1;
+	os_memcpy(counter, nonce, AES_BLOCK_SIZE);
+
+	while (left > 0) {
+		aes_encrypt(ctx, counter, buf);
+
+		len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE;
+		for (j = 0; j < len; j++)
+			pos[j] ^= buf[j];
+		pos += len;
+		left -= len;
+
+		for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) {
+			counter[i]++;
+			if (counter[i])
+				break;
+		}
+	}
+	aes_encrypt_deinit(ctx);
+	return 0;
+}
diff --git a/hostap/src/crypto/aes-eax.c b/hostap/src/crypto/aes-eax.c
new file mode 100644
index 0000000..15a09f8
--- /dev/null
+++ b/hostap/src/crypto/aes-eax.c
@@ -0,0 +1,145 @@
+/*
+ * AES-128 EAX
+ *
+ * Copyright (c) 2003-2007, 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 "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_eax_encrypt - AES-128 EAX mode encryption
+ * @key: Key for encryption (16 bytes)
+ * @nonce: Nonce for counter mode
+ * @nonce_len: Nonce length in bytes
+ * @hdr: Header data to be authenticity protected
+ * @hdr_len: Length of the header data bytes
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * @tag: 16-byte tag value
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
+			const u8 *hdr, size_t hdr_len,
+			u8 *data, size_t data_len, u8 *tag)
+{
+	u8 *buf;
+	size_t buf_len;
+	u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE],
+		data_mac[AES_BLOCK_SIZE];
+	int i, ret = -1;
+
+	if (nonce_len > data_len)
+		buf_len = nonce_len;
+	else
+		buf_len = data_len;
+	if (hdr_len > buf_len)
+		buf_len = hdr_len;
+	buf_len += 16;
+
+	buf = os_malloc(buf_len);
+	if (buf == NULL)
+		return -1;
+
+	os_memset(buf, 0, 15);
+
+	buf[15] = 0;
+	os_memcpy(buf + 16, nonce, nonce_len);
+	if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac))
+		goto fail;
+
+	buf[15] = 1;
+	os_memcpy(buf + 16, hdr, hdr_len);
+	if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac))
+		goto fail;
+
+	if (aes_128_ctr_encrypt(key, nonce_mac, data, data_len))
+		goto fail;
+	buf[15] = 2;
+	os_memcpy(buf + 16, data, data_len);
+	if (omac1_aes_128(key, buf, 16 + data_len, data_mac))
+		goto fail;
+
+	for (i = 0; i < AES_BLOCK_SIZE; i++)
+		tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i];
+
+	ret = 0;
+fail:
+	bin_clear_free(buf, buf_len);
+
+	return ret;
+}
+
+
+/**
+ * aes_128_eax_decrypt - AES-128 EAX mode decryption
+ * @key: Key for decryption (16 bytes)
+ * @nonce: Nonce for counter mode
+ * @nonce_len: Nonce length in bytes
+ * @hdr: Header data to be authenticity protected
+ * @hdr_len: Length of the header data bytes
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * @tag: 16-byte tag value
+ * Returns: 0 on success, -1 on failure, -2 if tag does not match
+ */
+int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
+			const u8 *hdr, size_t hdr_len,
+			u8 *data, size_t data_len, const u8 *tag)
+{
+	u8 *buf;
+	size_t buf_len;
+	u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE],
+		data_mac[AES_BLOCK_SIZE];
+	int i;
+
+	if (nonce_len > data_len)
+		buf_len = nonce_len;
+	else
+		buf_len = data_len;
+	if (hdr_len > buf_len)
+		buf_len = hdr_len;
+	buf_len += 16;
+
+	buf = os_malloc(buf_len);
+	if (buf == NULL)
+		return -1;
+
+	os_memset(buf, 0, 15);
+
+	buf[15] = 0;
+	os_memcpy(buf + 16, nonce, nonce_len);
+	if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac)) {
+		os_free(buf);
+		return -1;
+	}
+
+	buf[15] = 1;
+	os_memcpy(buf + 16, hdr, hdr_len);
+	if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac)) {
+		os_free(buf);
+		return -1;
+	}
+
+	buf[15] = 2;
+	os_memcpy(buf + 16, data, data_len);
+	if (omac1_aes_128(key, buf, 16 + data_len, data_mac)) {
+		os_free(buf);
+		return -1;
+	}
+
+	os_free(buf);
+
+	for (i = 0; i < AES_BLOCK_SIZE; i++) {
+		if (tag[i] != (nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]))
+			return -2;
+	}
+
+	return aes_128_ctr_encrypt(key, nonce_mac, data, data_len);
+}
diff --git a/hostap/src/crypto/aes-encblock.c b/hostap/src/crypto/aes-encblock.c
new file mode 100644
index 0000000..a521621
--- /dev/null
+++ b/hostap/src/crypto/aes-encblock.c
@@ -0,0 +1,32 @@
+/*
+ * AES encrypt_block
+ *
+ * Copyright (c) 2003-2007, 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 "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_encrypt_block - Perform one AES 128-bit block operation
+ * @key: Key for AES
+ * @in: Input data (16 bytes)
+ * @out: Output of the AES block operation (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
+{
+	void *ctx;
+	ctx = aes_encrypt_init(key, 16);
+	if (ctx == NULL)
+		return -1;
+	aes_encrypt(ctx, in, out);
+	aes_encrypt_deinit(ctx);
+	return 0;
+}
diff --git a/hostap/src/crypto/aes-gcm.c b/hostap/src/crypto/aes-gcm.c
new file mode 100644
index 0000000..84294d2
--- /dev/null
+++ b/hostap/src/crypto/aes-gcm.c
@@ -0,0 +1,327 @@
+/*
+ * Galois/Counter Mode (GCM) and GMAC with AES
+ *
+ * Copyright (c) 2012, 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 "aes.h"
+#include "aes_wrap.h"
+
+static void inc32(u8 *block)
+{
+	u32 val;
+	val = WPA_GET_BE32(block + AES_BLOCK_SIZE - 4);
+	val++;
+	WPA_PUT_BE32(block + AES_BLOCK_SIZE - 4, val);
+}
+
+
+static void xor_block(u8 *dst, const u8 *src)
+{
+	u32 *d = (u32 *) dst;
+	u32 *s = (u32 *) src;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+}
+
+
+static void shift_right_block(u8 *v)
+{
+	u32 val;
+
+	val = WPA_GET_BE32(v + 12);
+	val >>= 1;
+	if (v[11] & 0x01)
+		val |= 0x80000000;
+	WPA_PUT_BE32(v + 12, val);
+
+	val = WPA_GET_BE32(v + 8);
+	val >>= 1;
+	if (v[7] & 0x01)
+		val |= 0x80000000;
+	WPA_PUT_BE32(v + 8, val);
+
+	val = WPA_GET_BE32(v + 4);
+	val >>= 1;
+	if (v[3] & 0x01)
+		val |= 0x80000000;
+	WPA_PUT_BE32(v + 4, val);
+
+	val = WPA_GET_BE32(v);
+	val >>= 1;
+	WPA_PUT_BE32(v, val);
+}
+
+
+/* Multiplication in GF(2^128) */
+static void gf_mult(const u8 *x, const u8 *y, u8 *z)
+{
+	u8 v[16];
+	int i, j;
+
+	os_memset(z, 0, 16); /* Z_0 = 0^128 */
+	os_memcpy(v, y, 16); /* V_0 = Y */
+
+	for (i = 0; i < 16; i++) {
+		for (j = 0; j < 8; j++) {
+			if (x[i] & BIT(7 - j)) {
+				/* Z_(i + 1) = Z_i XOR V_i */
+				xor_block(z, v);
+			} else {
+				/* Z_(i + 1) = Z_i */
+			}
+
+			if (v[15] & 0x01) {
+				/* V_(i + 1) = (V_i >> 1) XOR R */
+				shift_right_block(v);
+				/* R = 11100001 || 0^120 */
+				v[0] ^= 0xe1;
+			} else {
+				/* V_(i + 1) = V_i >> 1 */
+				shift_right_block(v);
+			}
+		}
+	}
+}
+
+
+static void ghash_start(u8 *y)
+{
+	/* Y_0 = 0^128 */
+	os_memset(y, 0, 16);
+}
+
+
+static void ghash(const u8 *h, const u8 *x, size_t xlen, u8 *y)
+{
+	size_t m, i;
+	const u8 *xpos = x;
+	u8 tmp[16];
+
+	m = xlen / 16;
+
+	for (i = 0; i < m; i++) {
+		/* Y_i = (Y^(i-1) XOR X_i) dot H */
+		xor_block(y, xpos);
+		xpos += 16;
+
+		/* dot operation:
+		 * multiplication operation for binary Galois (finite) field of
+		 * 2^128 elements */
+		gf_mult(y, h, tmp);
+		os_memcpy(y, tmp, 16);
+	}
+
+	if (x + xlen > xpos) {
+		/* Add zero padded last block */
+		size_t last = x + xlen - xpos;
+		os_memcpy(tmp, xpos, last);
+		os_memset(tmp + last, 0, sizeof(tmp) - last);
+
+		/* Y_i = (Y^(i-1) XOR X_i) dot H */
+		xor_block(y, tmp);
+
+		/* dot operation:
+		 * multiplication operation for binary Galois (finite) field of
+		 * 2^128 elements */
+		gf_mult(y, h, tmp);
+		os_memcpy(y, tmp, 16);
+	}
+
+	/* Return Y_m */
+}
+
+
+static void aes_gctr(void *aes, const u8 *icb, const u8 *x, size_t xlen, u8 *y)
+{
+	size_t i, n, last;
+	u8 cb[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE];
+	const u8 *xpos = x;
+	u8 *ypos = y;
+
+	if (xlen == 0)
+		return;
+
+	n = xlen / 16;
+
+	os_memcpy(cb, icb, AES_BLOCK_SIZE);
+	/* Full blocks */
+	for (i = 0; i < n; i++) {
+		aes_encrypt(aes, cb, ypos);
+		xor_block(ypos, xpos);
+		xpos += AES_BLOCK_SIZE;
+		ypos += AES_BLOCK_SIZE;
+		inc32(cb);
+	}
+
+	last = x + xlen - xpos;
+	if (last) {
+		/* Last, partial block */
+		aes_encrypt(aes, cb, tmp);
+		for (i = 0; i < last; i++)
+			*ypos++ = *xpos++ ^ tmp[i];
+	}
+}
+
+
+static void * aes_gcm_init_hash_subkey(const u8 *key, size_t key_len, u8 *H)
+{
+	void *aes;
+
+	aes = aes_encrypt_init(key, key_len);
+	if (aes == NULL)
+		return NULL;
+
+	/* Generate hash subkey H = AES_K(0^128) */
+	os_memset(H, 0, AES_BLOCK_SIZE);
+	aes_encrypt(aes, H, H);
+	wpa_hexdump_key(MSG_EXCESSIVE, "Hash subkey H for GHASH",
+			H, AES_BLOCK_SIZE);
+	return aes;
+}
+
+
+static void aes_gcm_prepare_j0(const u8 *iv, size_t iv_len, const u8 *H, u8 *J0)
+{
+	u8 len_buf[16];
+
+	if (iv_len == 12) {
+		/* Prepare block J_0 = IV || 0^31 || 1 [len(IV) = 96] */
+		os_memcpy(J0, iv, iv_len);
+		os_memset(J0 + iv_len, 0, AES_BLOCK_SIZE - iv_len);
+		J0[AES_BLOCK_SIZE - 1] = 0x01;
+	} else {
+		/*
+		 * s = 128 * ceil(len(IV)/128) - len(IV)
+		 * J_0 = GHASH_H(IV || 0^(s+64) || [len(IV)]_64)
+		 */
+		ghash_start(J0);
+		ghash(H, iv, iv_len, J0);
+		WPA_PUT_BE64(len_buf, 0);
+		WPA_PUT_BE64(len_buf + 8, iv_len * 8);
+		ghash(H, len_buf, sizeof(len_buf), J0);
+	}
+}
+
+
+static void aes_gcm_gctr(void *aes, const u8 *J0, const u8 *in, size_t len,
+			 u8 *out)
+{
+	u8 J0inc[AES_BLOCK_SIZE];
+
+	if (len == 0)
+		return;
+
+	os_memcpy(J0inc, J0, AES_BLOCK_SIZE);
+	inc32(J0inc);
+	aes_gctr(aes, J0inc, in, len, out);
+}
+
+
+static void aes_gcm_ghash(const u8 *H, const u8 *aad, size_t aad_len,
+			  const u8 *crypt, size_t crypt_len, u8 *S)
+{
+	u8 len_buf[16];
+
+	/*
+	 * u = 128 * ceil[len(C)/128] - len(C)
+	 * v = 128 * ceil[len(A)/128] - len(A)
+	 * S = GHASH_H(A || 0^v || C || 0^u || [len(A)]64 || [len(C)]64)
+	 * (i.e., zero padded to block size A || C and lengths of each in bits)
+	 */
+	ghash_start(S);
+	ghash(H, aad, aad_len, S);
+	ghash(H, crypt, crypt_len, S);
+	WPA_PUT_BE64(len_buf, aad_len * 8);
+	WPA_PUT_BE64(len_buf + 8, crypt_len * 8);
+	ghash(H, len_buf, sizeof(len_buf), S);
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "S = GHASH_H(...)", S, 16);
+}
+
+
+/**
+ * aes_gcm_ae - GCM-AE_K(IV, P, A)
+ */
+int aes_gcm_ae(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
+	       const u8 *plain, size_t plain_len,
+	       const u8 *aad, size_t aad_len, u8 *crypt, u8 *tag)
+{
+	u8 H[AES_BLOCK_SIZE];
+	u8 J0[AES_BLOCK_SIZE];
+	u8 S[16];
+	void *aes;
+
+	aes = aes_gcm_init_hash_subkey(key, key_len, H);
+	if (aes == NULL)
+		return -1;
+
+	aes_gcm_prepare_j0(iv, iv_len, H, J0);
+
+	/* C = GCTR_K(inc_32(J_0), P) */
+	aes_gcm_gctr(aes, J0, plain, plain_len, crypt);
+
+	aes_gcm_ghash(H, aad, aad_len, crypt, plain_len, S);
+
+	/* T = MSB_t(GCTR_K(J_0, S)) */
+	aes_gctr(aes, J0, S, sizeof(S), tag);
+
+	/* Return (C, T) */
+
+	aes_encrypt_deinit(aes);
+
+	return 0;
+}
+
+
+/**
+ * aes_gcm_ad - GCM-AD_K(IV, C, A, T)
+ */
+int aes_gcm_ad(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
+	       const u8 *crypt, size_t crypt_len,
+	       const u8 *aad, size_t aad_len, const u8 *tag, u8 *plain)
+{
+	u8 H[AES_BLOCK_SIZE];
+	u8 J0[AES_BLOCK_SIZE];
+	u8 S[16], T[16];
+	void *aes;
+
+	aes = aes_gcm_init_hash_subkey(key, key_len, H);
+	if (aes == NULL)
+		return -1;
+
+	aes_gcm_prepare_j0(iv, iv_len, H, J0);
+
+	/* P = GCTR_K(inc_32(J_0), C) */
+	aes_gcm_gctr(aes, J0, crypt, crypt_len, plain);
+
+	aes_gcm_ghash(H, aad, aad_len, crypt, crypt_len, S);
+
+	/* T' = MSB_t(GCTR_K(J_0, S)) */
+	aes_gctr(aes, J0, S, sizeof(S), T);
+
+	aes_encrypt_deinit(aes);
+
+	if (os_memcmp_const(tag, T, 16) != 0) {
+		wpa_printf(MSG_EXCESSIVE, "GCM: Tag mismatch");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int aes_gmac(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
+	     const u8 *aad, size_t aad_len, u8 *tag)
+{
+	return aes_gcm_ae(key, key_len, iv, iv_len, NULL, 0, aad, aad_len, NULL,
+			  tag);
+}
diff --git a/hostap/src/crypto/aes-internal-dec.c b/hostap/src/crypto/aes-internal-dec.c
new file mode 100644
index 0000000..720c703
--- /dev/null
+++ b/hostap/src/crypto/aes-internal-dec.c
@@ -0,0 +1,161 @@
+/*
+ * AES (Rijndael) cipher - decrypt
+ *
+ * Modifications to public domain implementation:
+ * - cleanup
+ * - use C pre-processor to make it easier to change S table access
+ * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
+ *   cost of reduced throughput (quite small difference on Pentium 4,
+ *   10-25% when using -O1 or -O2 optimization)
+ *
+ * Copyright (c) 2003-2012, 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"
+#include "aes_i.h"
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ *
+ * @return	the number of rounds for the given cipher key size.
+ */
+static int rijndaelKeySetupDec(u32 rk[], const u8 cipherKey[], int keyBits)
+{
+	int Nr, i, j;
+	u32 temp;
+
+	/* expand the cipher key: */
+	Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits);
+	if (Nr < 0)
+		return Nr;
+	/* invert the order of the round keys: */
+	for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) {
+		temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;
+		temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+		temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+		temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+	}
+	/* apply the inverse MixColumn transform to all round keys but the
+	 * first and the last: */
+	for (i = 1; i < Nr; i++) {
+		rk += 4;
+		for (j = 0; j < 4; j++) {
+			rk[j] = TD0_(TE4((rk[j] >> 24)       )) ^
+				TD1_(TE4((rk[j] >> 16) & 0xff)) ^
+				TD2_(TE4((rk[j] >>  8) & 0xff)) ^
+				TD3_(TE4((rk[j]      ) & 0xff));
+		}
+	}
+
+	return Nr;
+}
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	u32 *rk;
+	int res;
+	rk = os_malloc(AES_PRIV_SIZE);
+	if (rk == NULL)
+		return NULL;
+	res = rijndaelKeySetupDec(rk, key, len * 8);
+	if (res < 0) {
+		os_free(rk);
+		return NULL;
+	}
+	rk[AES_PRIV_NR_POS] = res;
+	return rk;
+}
+
+static void rijndaelDecrypt(const u32 rk[/*44*/], int Nr, const u8 ct[16],
+			    u8 pt[16])
+{
+	u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+	int r;
+#endif /* ?FULL_UNROLL */
+
+	/*
+	 * map byte array block to cipher state
+	 * and add initial round key:
+	 */
+	s0 = GETU32(ct     ) ^ rk[0];
+	s1 = GETU32(ct +  4) ^ rk[1];
+	s2 = GETU32(ct +  8) ^ rk[2];
+	s3 = GETU32(ct + 12) ^ rk[3];
+
+#define ROUND(i,d,s) \
+d##0 = TD0(s##0) ^ TD1(s##3) ^ TD2(s##2) ^ TD3(s##1) ^ rk[4 * i]; \
+d##1 = TD0(s##1) ^ TD1(s##0) ^ TD2(s##3) ^ TD3(s##2) ^ rk[4 * i + 1]; \
+d##2 = TD0(s##2) ^ TD1(s##1) ^ TD2(s##0) ^ TD3(s##3) ^ rk[4 * i + 2]; \
+d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3]
+
+#ifdef FULL_UNROLL
+
+	ROUND(1,t,s);
+	ROUND(2,s,t);
+	ROUND(3,t,s);
+	ROUND(4,s,t);
+	ROUND(5,t,s);
+	ROUND(6,s,t);
+	ROUND(7,t,s);
+	ROUND(8,s,t);
+	ROUND(9,t,s);
+	if (Nr > 10) {
+		ROUND(10,s,t);
+		ROUND(11,t,s);
+		if (Nr > 12) {
+			ROUND(12,s,t);
+			ROUND(13,t,s);
+		}
+	}
+
+	rk += Nr << 2;
+
+#else  /* !FULL_UNROLL */
+
+	/* Nr - 1 full rounds: */
+	r = Nr >> 1;
+	for (;;) {
+		ROUND(1,t,s);
+		rk += 8;
+		if (--r == 0)
+			break;
+		ROUND(0,s,t);
+	}
+
+#endif /* ?FULL_UNROLL */
+
+#undef ROUND
+
+	/*
+	 * apply last round and
+	 * map cipher state to byte array block:
+	 */
+	s0 = TD41(t0) ^ TD42(t3) ^ TD43(t2) ^ TD44(t1) ^ rk[0];
+	PUTU32(pt     , s0);
+	s1 = TD41(t1) ^ TD42(t0) ^ TD43(t3) ^ TD44(t2) ^ rk[1];
+	PUTU32(pt +  4, s1);
+	s2 = TD41(t2) ^ TD42(t1) ^ TD43(t0) ^ TD44(t3) ^ rk[2];
+	PUTU32(pt +  8, s2);
+	s3 = TD41(t3) ^ TD42(t2) ^ TD43(t1) ^ TD44(t0) ^ rk[3];
+	PUTU32(pt + 12, s3);
+}
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	u32 *rk = ctx;
+	rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	os_memset(ctx, 0, AES_PRIV_SIZE);
+	os_free(ctx);
+}
diff --git a/hostap/src/crypto/aes-internal-enc.c b/hostap/src/crypto/aes-internal-enc.c
new file mode 100644
index 0000000..f3c61b8
--- /dev/null
+++ b/hostap/src/crypto/aes-internal-enc.c
@@ -0,0 +1,126 @@
+/*
+ * AES (Rijndael) cipher - encrypt
+ *
+ * Modifications to public domain implementation:
+ * - cleanup
+ * - use C pre-processor to make it easier to change S table access
+ * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
+ *   cost of reduced throughput (quite small difference on Pentium 4,
+ *   10-25% when using -O1 or -O2 optimization)
+ *
+ * Copyright (c) 2003-2012, 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"
+#include "aes_i.h"
+
+static void rijndaelEncrypt(const u32 rk[], int Nr, const u8 pt[16], u8 ct[16])
+{
+	u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+	int r;
+#endif /* ?FULL_UNROLL */
+
+	/*
+	 * map byte array block to cipher state
+	 * and add initial round key:
+	 */
+	s0 = GETU32(pt     ) ^ rk[0];
+	s1 = GETU32(pt +  4) ^ rk[1];
+	s2 = GETU32(pt +  8) ^ rk[2];
+	s3 = GETU32(pt + 12) ^ rk[3];
+
+#define ROUND(i,d,s) \
+d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[4 * i]; \
+d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[4 * i + 1]; \
+d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[4 * i + 2]; \
+d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3]
+
+#ifdef FULL_UNROLL
+
+	ROUND(1,t,s);
+	ROUND(2,s,t);
+	ROUND(3,t,s);
+	ROUND(4,s,t);
+	ROUND(5,t,s);
+	ROUND(6,s,t);
+	ROUND(7,t,s);
+	ROUND(8,s,t);
+	ROUND(9,t,s);
+	if (Nr > 10) {
+		ROUND(10,s,t);
+		ROUND(11,t,s);
+		if (Nr > 12) {
+			ROUND(12,s,t);
+			ROUND(13,t,s);
+		}
+	}
+
+	rk += Nr << 2;
+
+#else  /* !FULL_UNROLL */
+
+	/* Nr - 1 full rounds: */
+	r = Nr >> 1;
+	for (;;) {
+		ROUND(1,t,s);
+		rk += 8;
+		if (--r == 0)
+			break;
+		ROUND(0,s,t);
+	}
+
+#endif /* ?FULL_UNROLL */
+
+#undef ROUND
+
+	/*
+	 * apply last round and
+	 * map cipher state to byte array block:
+	 */
+	s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0];
+	PUTU32(ct     , s0);
+	s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1];
+	PUTU32(ct +  4, s1);
+	s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2];
+	PUTU32(ct +  8, s2);
+	s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3];
+	PUTU32(ct + 12, s3);
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	u32 *rk;
+	int res;
+	rk = os_malloc(AES_PRIV_SIZE);
+	if (rk == NULL)
+		return NULL;
+	res = rijndaelKeySetupEnc(rk, key, len * 8);
+	if (res < 0) {
+		os_free(rk);
+		return NULL;
+	}
+	rk[AES_PRIV_NR_POS] = res;
+	return rk;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	u32 *rk = ctx;
+	rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	os_memset(ctx, 0, AES_PRIV_SIZE);
+	os_free(ctx);
+}
diff --git a/hostap/src/crypto/aes-internal.c b/hostap/src/crypto/aes-internal.c
new file mode 100644
index 0000000..bd4535d
--- /dev/null
+++ b/hostap/src/crypto/aes-internal.c
@@ -0,0 +1,845 @@
+/*
+ * AES (Rijndael) cipher
+ *
+ * Modifications to public domain implementation:
+ * - cleanup
+ * - use C pre-processor to make it easier to change S table access
+ * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
+ *   cost of reduced throughput (quite small difference on Pentium 4,
+ *   10-25% when using -O1 or -O2 optimization)
+ *
+ * Copyright (c) 2003-2012, 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"
+#include "aes_i.h"
+
+/*
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''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 AUTHORS 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.
+ */
+
+
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x].[01, 01, 01, 01];
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01, 01, 01, 01];
+*/
+
+const u32 Te0[256] = {
+    0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+    0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+    0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+    0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+    0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+    0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+    0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+    0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+    0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+    0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+    0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+    0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+    0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+    0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+    0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+    0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+    0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+    0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+    0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+    0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+    0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+    0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+    0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+    0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+    0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+    0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+    0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+    0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+    0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+    0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+    0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+    0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+    0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+    0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+    0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+    0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+    0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+    0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+    0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+    0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+    0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+    0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+    0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+    0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+    0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+    0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+    0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+    0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+    0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+    0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+    0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+    0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+    0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+    0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+    0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+    0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+    0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+    0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+    0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+    0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+    0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+    0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+    0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+    0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+#ifndef AES_SMALL_TABLES
+const u32 Te1[256] = {
+    0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+    0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+    0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+    0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+    0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+    0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+    0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+    0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+    0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+    0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+    0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+    0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+    0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+    0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+    0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+    0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+    0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+    0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+    0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+    0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+    0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+    0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+    0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+    0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+    0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+    0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+    0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+    0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+    0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+    0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+    0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+    0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+    0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+    0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+    0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+    0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+    0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+    0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+    0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+    0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+    0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+    0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+    0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+    0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+    0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+    0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+    0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+    0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+    0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+    0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+    0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+    0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+    0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+    0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+    0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+    0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+    0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+    0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+    0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+    0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+    0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+    0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+    0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+    0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+const u32 Te2[256] = {
+    0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+    0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+    0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+    0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+    0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+    0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+    0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+    0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+    0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+    0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+    0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+    0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+    0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+    0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+    0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+    0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+    0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+    0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+    0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+    0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+    0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+    0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+    0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+    0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+    0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+    0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+    0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+    0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+    0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+    0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+    0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+    0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+    0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+    0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+    0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+    0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+    0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+    0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+    0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+    0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+    0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+    0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+    0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+    0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+    0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+    0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+    0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+    0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+    0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+    0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+    0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+    0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+    0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+    0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+    0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+    0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+    0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+    0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+    0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+    0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+    0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+    0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+    0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+    0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+const u32 Te3[256] = {
+
+    0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+    0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+    0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+    0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+    0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+    0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+    0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+    0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+    0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+    0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+    0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+    0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+    0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+    0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+    0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+    0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+    0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+    0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+    0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+    0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+    0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+    0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+    0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+    0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+    0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+    0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+    0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+    0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+    0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+    0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+    0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+    0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+    0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+    0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+    0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+    0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+    0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+    0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+    0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+    0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+    0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+    0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+    0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+    0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+    0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+    0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+    0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+    0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+    0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+    0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+    0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+    0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+    0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+    0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+    0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+    0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+    0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+    0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+    0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+    0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+    0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+    0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+    0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+    0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+const u32 Te4[256] = {
+    0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+    0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+    0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+    0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+    0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+    0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+    0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+    0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+    0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+    0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+    0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+    0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+    0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+    0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+    0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+    0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+    0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+    0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+    0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+    0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+    0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+    0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+    0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+    0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+    0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+    0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+    0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+    0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+    0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+    0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+    0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+    0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+    0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+    0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+    0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+    0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+    0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+    0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+    0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+    0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+    0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+    0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+    0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+    0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+    0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+    0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+    0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+    0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+    0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+    0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+    0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+    0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+    0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+    0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+    0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+    0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+    0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+    0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+    0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+    0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+    0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+    0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+    0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+    0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+#endif /* AES_SMALL_TABLES */
+const u32 Td0[256] = {
+    0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+    0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+    0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+    0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+    0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+    0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+    0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+    0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+    0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+    0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+    0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+    0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+    0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+    0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+    0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+    0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+    0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+    0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+    0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+    0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+    0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+    0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+    0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+    0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+    0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+    0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+    0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+    0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+    0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+    0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+    0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+    0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+    0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+    0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+    0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+    0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+    0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+    0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+    0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+    0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+    0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+    0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+    0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+    0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+    0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+    0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+    0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+    0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+    0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+    0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+    0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+    0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+    0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+    0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+    0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+    0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+    0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+    0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+    0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+    0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+    0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+    0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+    0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+    0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+#ifndef AES_SMALL_TABLES
+const u32 Td1[256] = {
+    0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+    0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+    0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+    0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+    0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+    0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+    0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+    0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+    0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+    0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+    0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+    0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+    0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+    0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+    0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+    0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+    0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+    0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+    0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+    0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+    0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+    0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+    0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+    0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+    0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+    0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+    0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+    0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+    0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+    0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+    0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+    0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+    0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+    0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+    0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+    0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+    0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+    0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+    0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+    0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+    0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+    0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+    0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+    0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+    0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+    0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+    0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+    0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+    0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+    0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+    0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+    0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+    0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+    0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+    0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+    0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+    0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+    0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+    0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+    0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+    0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+    0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+    0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+    0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+const u32 Td2[256] = {
+    0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+    0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+    0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+    0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+    0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+    0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+    0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+    0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+    0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+    0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+    0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+    0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+    0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+    0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+    0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+    0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+    0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+    0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+    0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+    0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+    0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+    0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+    0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+    0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+    0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+    0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+    0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+    0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+    0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+    0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+    0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+    0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+    0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+    0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+    0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+    0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+    0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+    0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+    0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+    0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+    0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+    0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+    0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+    0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+    0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+    0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+    0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+    0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+    0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+    0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+    0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+    0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+    0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+    0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+    0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+    0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+    0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+    0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+    0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+    0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+    0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+    0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+    0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+    0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+const u32 Td3[256] = {
+    0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+    0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+    0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+    0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+    0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+    0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+    0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+    0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+    0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+    0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+    0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+    0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+    0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+    0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+    0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+    0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+    0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+    0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+    0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+    0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+    0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+    0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+    0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+    0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+    0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+    0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+    0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+    0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+    0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+    0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+    0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+    0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+    0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+    0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+    0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+    0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+    0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+    0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+    0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+    0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+    0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+    0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+    0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+    0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+    0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+    0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+    0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+    0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+    0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+    0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+    0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+    0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+    0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+    0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+    0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+    0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+    0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+    0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+    0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+    0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+    0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+    0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+    0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+    0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+const u32 Td4[256] = {
+    0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,
+    0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,
+    0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,
+    0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,
+    0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,
+    0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,
+    0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,
+    0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,
+    0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,
+    0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,
+    0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,
+    0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,
+    0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,
+    0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,
+    0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,
+    0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,
+    0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,
+    0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,
+    0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,
+    0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,
+    0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,
+    0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,
+    0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,
+    0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,
+    0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,
+    0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,
+    0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,
+    0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,
+    0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,
+    0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,
+    0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,
+    0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,
+    0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,
+    0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,
+    0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,
+    0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,
+    0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,
+    0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,
+    0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,
+    0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,
+    0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,
+    0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,
+    0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,
+    0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,
+    0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,
+    0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,
+    0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,
+    0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,
+    0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,
+    0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,
+    0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,
+    0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,
+    0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,
+    0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,
+    0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,
+    0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,
+    0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,
+    0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,
+    0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,
+    0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,
+    0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,
+    0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,
+    0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,
+    0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,
+};
+const u32 rcon[] = {
+	0x01000000, 0x02000000, 0x04000000, 0x08000000,
+	0x10000000, 0x20000000, 0x40000000, 0x80000000,
+	0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+#else /* AES_SMALL_TABLES */
+const u8 Td4s[256] = {
+    0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U,
+    0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU,
+    0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U,
+    0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU,
+    0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU,
+    0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU,
+    0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U,
+    0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U,
+    0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U,
+    0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U,
+    0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU,
+    0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U,
+    0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU,
+    0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U,
+    0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U,
+    0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU,
+    0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU,
+    0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U,
+    0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U,
+    0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU,
+    0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U,
+    0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU,
+    0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U,
+    0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U,
+    0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U,
+    0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU,
+    0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU,
+    0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU,
+    0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U,
+    0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U,
+    0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U,
+    0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU,
+};
+const u8 rcons[] = {
+	0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
+	/* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+#endif /* AES_SMALL_TABLES */
+/**
+ * Expand the cipher key into the encryption key schedule.
+ *
+ * @return	the number of rounds for the given cipher key size.
+ */
+int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits)
+{
+	int i;
+	u32 temp;
+
+	rk[0] = GETU32(cipherKey     );
+	rk[1] = GETU32(cipherKey +  4);
+	rk[2] = GETU32(cipherKey +  8);
+	rk[3] = GETU32(cipherKey + 12);
+
+	if (keyBits == 128) {
+		for (i = 0; i < 10; i++) {
+			temp  = rk[3];
+			rk[4] = rk[0] ^ TE421(temp) ^ TE432(temp) ^
+				TE443(temp) ^ TE414(temp) ^ RCON(i);
+			rk[5] = rk[1] ^ rk[4];
+			rk[6] = rk[2] ^ rk[5];
+			rk[7] = rk[3] ^ rk[6];
+			rk += 4;
+		}
+		return 10;
+	}
+
+	rk[4] = GETU32(cipherKey + 16);
+	rk[5] = GETU32(cipherKey + 20);
+
+	if (keyBits == 192) {
+		for (i = 0; i < 8; i++) {
+			temp  = rk[5];
+			rk[6] = rk[0] ^ TE421(temp) ^ TE432(temp) ^
+				TE443(temp) ^ TE414(temp) ^ RCON(i);
+			rk[7] = rk[1] ^ rk[6];
+			rk[8] = rk[2] ^ rk[7];
+			rk[9] = rk[3] ^ rk[8];
+			if (i == 7)
+				return 12;
+			rk[10] = rk[4] ^ rk[9];
+			rk[11] = rk[5] ^ rk[10];
+			rk += 6;
+		}
+	}
+
+	rk[6] = GETU32(cipherKey + 24);
+	rk[7] = GETU32(cipherKey + 28);
+
+	if (keyBits == 256) {
+		for (i = 0; i < 7; i++) {
+			temp  = rk[7];
+			rk[8] = rk[0] ^ TE421(temp) ^ TE432(temp) ^
+				TE443(temp) ^ TE414(temp) ^ RCON(i);
+			rk[9] = rk[1] ^ rk[8];
+			rk[10] = rk[2] ^ rk[9];
+			rk[11] = rk[3] ^ rk[10];
+			if (i == 6)
+				return 14;
+			temp  = rk[11];
+			rk[12] = rk[4] ^ TE411(temp) ^ TE422(temp) ^
+				TE433(temp) ^ TE444(temp);
+			rk[13] = rk[5] ^ rk[12];
+			rk[14] = rk[6] ^ rk[13];
+			rk[15] = rk[7] ^ rk[14];
+			rk += 8;
+		}
+	}
+
+	return -1;
+}
diff --git a/hostap/src/crypto/aes-omac1.c b/hostap/src/crypto/aes-omac1.c
new file mode 100644
index 0000000..375db57
--- /dev/null
+++ b/hostap/src/crypto/aes-omac1.c
@@ -0,0 +1,170 @@
+/*
+ * One-key CBC MAC (OMAC1) hash with AES
+ *
+ * Copyright (c) 2003-2007, 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 "aes.h"
+#include "aes_wrap.h"
+
+static void gf_mulx(u8 *pad)
+{
+	int i, carry;
+
+	carry = pad[0] & 0x80;
+	for (i = 0; i < AES_BLOCK_SIZE - 1; i++)
+		pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
+	pad[AES_BLOCK_SIZE - 1] <<= 1;
+	if (carry)
+		pad[AES_BLOCK_SIZE - 1] ^= 0x87;
+}
+
+
+/**
+ * omac1_aes_vector - One-Key CBC MAC (OMAC1) hash with AES
+ * @key: Key for the hash operation
+ * @key_len: Key length in octets
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	void *ctx;
+	u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE];
+	const u8 *pos, *end;
+	size_t i, e, left, total_len;
+
+	ctx = aes_encrypt_init(key, key_len);
+	if (ctx == NULL)
+		return -1;
+	os_memset(cbc, 0, AES_BLOCK_SIZE);
+
+	total_len = 0;
+	for (e = 0; e < num_elem; e++)
+		total_len += len[e];
+	left = total_len;
+
+	e = 0;
+	pos = addr[0];
+	end = pos + len[0];
+
+	while (left >= AES_BLOCK_SIZE) {
+		for (i = 0; i < AES_BLOCK_SIZE; i++) {
+			cbc[i] ^= *pos++;
+			if (pos >= end) {
+				/*
+				 * Stop if there are no more bytes to process
+				 * since there are no more entries in the array.
+				 */
+				if (i + 1 == AES_BLOCK_SIZE &&
+				    left == AES_BLOCK_SIZE)
+					break;
+				e++;
+				pos = addr[e];
+				end = pos + len[e];
+			}
+		}
+		if (left > AES_BLOCK_SIZE)
+			aes_encrypt(ctx, cbc, cbc);
+		left -= AES_BLOCK_SIZE;
+	}
+
+	os_memset(pad, 0, AES_BLOCK_SIZE);
+	aes_encrypt(ctx, pad, pad);
+	gf_mulx(pad);
+
+	if (left || total_len == 0) {
+		for (i = 0; i < left; i++) {
+			cbc[i] ^= *pos++;
+			if (pos >= end) {
+				/*
+				 * Stop if there are no more bytes to process
+				 * since there are no more entries in the array.
+				 */
+				if (i + 1 == left)
+					break;
+				e++;
+				pos = addr[e];
+				end = pos + len[e];
+			}
+		}
+		cbc[left] ^= 0x80;
+		gf_mulx(pad);
+	}
+
+	for (i = 0; i < AES_BLOCK_SIZE; i++)
+		pad[i] ^= cbc[i];
+	aes_encrypt(ctx, pad, mac);
+	aes_encrypt_deinit(ctx);
+	return 0;
+}
+
+
+/**
+ * omac1_aes_128_vector - One-Key CBC MAC (OMAC1) hash with AES-128
+ * @key: 128-bit key for the hash operation
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+			 const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+/**
+ * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 (aka AES-CMAC)
+ * @key: 128-bit key for the hash operation
+ * @data: Data buffer for which a MAC is determined
+ * @data_len: Length of data buffer in bytes
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+/**
+ * omac1_aes_256 - One-Key CBC MAC (OMAC1) hash with AES-256 (aka AES-CMAC)
+ * @key: 256-bit key for the hash operation
+ * @data: Data buffer for which a MAC is determined
+ * @data_len: Length of data buffer in bytes
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
diff --git a/hostap/src/crypto/aes-siv.c b/hostap/src/crypto/aes-siv.c
new file mode 100644
index 0000000..5ac82c2
--- /dev/null
+++ b/hostap/src/crypto/aes-siv.c
@@ -0,0 +1,188 @@
+/*
+ * AES SIV (RFC 5297)
+ * Copyright (c) 2013 Cozybit, 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 "aes.h"
+#include "aes_wrap.h"
+#include "aes_siv.h"
+
+
+static const u8 zero[AES_BLOCK_SIZE];
+
+
+static void dbl(u8 *pad)
+{
+	int i, carry;
+
+	carry = pad[0] & 0x80;
+	for (i = 0; i < AES_BLOCK_SIZE - 1; i++)
+		pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
+	pad[AES_BLOCK_SIZE - 1] <<= 1;
+	if (carry)
+		pad[AES_BLOCK_SIZE - 1] ^= 0x87;
+}
+
+
+static void xor(u8 *a, const u8 *b)
+{
+	int i;
+
+	for (i = 0; i < AES_BLOCK_SIZE; i++)
+		*a++ ^= *b++;
+}
+
+
+static void xorend(u8 *a, int alen, const u8 *b, int blen)
+{
+	int i;
+
+	if (alen < blen)
+		return;
+
+	for (i = 0; i < blen; i++)
+		a[alen - blen + i] ^= b[i];
+}
+
+
+static void pad_block(u8 *pad, const u8 *addr, size_t len)
+{
+	os_memset(pad, 0, AES_BLOCK_SIZE);
+	os_memcpy(pad, addr, len);
+
+	if (len < AES_BLOCK_SIZE)
+		pad[len] = 0x80;
+}
+
+
+static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[],
+		   size_t *len, u8 *mac)
+{
+	u8 tmp[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
+	u8 *buf = NULL;
+	int ret;
+	size_t i;
+
+	if (!num_elem) {
+		os_memcpy(tmp, zero, sizeof(zero));
+		tmp[AES_BLOCK_SIZE - 1] = 1;
+		return omac1_aes_128(key, tmp, sizeof(tmp), mac);
+	}
+
+	ret = omac1_aes_128(key, zero, sizeof(zero), tmp);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < num_elem - 1; i++) {
+		ret = omac1_aes_128(key, addr[i], len[i], tmp2);
+		if (ret)
+			return ret;
+
+		dbl(tmp);
+		xor(tmp, tmp2);
+	}
+	if (len[i] >= AES_BLOCK_SIZE) {
+		buf = os_malloc(len[i]);
+		if (!buf)
+			return -ENOMEM;
+
+		os_memcpy(buf, addr[i], len[i]);
+		xorend(buf, len[i], tmp, AES_BLOCK_SIZE);
+		ret = omac1_aes_128(key, buf, len[i], mac);
+		bin_clear_free(buf, len[i]);
+		return ret;
+	}
+
+	dbl(tmp);
+	pad_block(tmp2, addr[i], len[i]);
+	xor(tmp, tmp2);
+
+	return omac1_aes_128(key, tmp, sizeof(tmp), mac);
+}
+
+
+int aes_siv_encrypt(const u8 *key, const u8 *pw,
+		    size_t pwlen, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *out)
+{
+	const u8 *_addr[6];
+	size_t _len[6];
+	const u8 *k1 = key, *k2 = key + 16;
+	u8 v[AES_BLOCK_SIZE];
+	size_t i;
+	u8 *iv, *crypt_pw;
+
+	if (num_elem > ARRAY_SIZE(_addr) - 1)
+		return -1;
+
+	for (i = 0; i < num_elem; i++) {
+		_addr[i] = addr[i];
+		_len[i] = len[i];
+	}
+	_addr[num_elem] = pw;
+	_len[num_elem] = pwlen;
+
+	if (aes_s2v(k1, num_elem + 1, _addr, _len, v))
+		return -1;
+
+	iv = out;
+	crypt_pw = out + AES_BLOCK_SIZE;
+
+	os_memcpy(iv, v, AES_BLOCK_SIZE);
+	os_memcpy(crypt_pw, pw, pwlen);
+
+	/* zero out 63rd and 31st bits of ctr (from right) */
+	v[8] &= 0x7f;
+	v[12] &= 0x7f;
+	return aes_128_ctr_encrypt(k2, v, crypt_pw, pwlen);
+}
+
+
+int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len,
+		    size_t num_elem, const u8 *addr[], const size_t *len,
+		    u8 *out)
+{
+	const u8 *_addr[6];
+	size_t _len[6];
+	const u8 *k1 = key, *k2 = key + 16;
+	size_t crypt_len;
+	size_t i;
+	int ret;
+	u8 iv[AES_BLOCK_SIZE];
+	u8 check[AES_BLOCK_SIZE];
+
+	if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1)
+		return -1;
+	crypt_len = iv_c_len - AES_BLOCK_SIZE;
+
+	for (i = 0; i < num_elem; i++) {
+		_addr[i] = addr[i];
+		_len[i] = len[i];
+	}
+	_addr[num_elem] = out;
+	_len[num_elem] = crypt_len;
+
+	os_memcpy(iv, iv_crypt, AES_BLOCK_SIZE);
+	os_memcpy(out, iv_crypt + AES_BLOCK_SIZE, crypt_len);
+
+	iv[8] &= 0x7f;
+	iv[12] &= 0x7f;
+
+	ret = aes_128_ctr_encrypt(k2, iv, out, crypt_len);
+	if (ret)
+		return ret;
+
+	ret = aes_s2v(k1, num_elem + 1, _addr, _len, check);
+	if (ret)
+		return ret;
+	if (os_memcmp(check, iv_crypt, AES_BLOCK_SIZE) == 0)
+		return 0;
+
+	return -1;
+}
diff --git a/hostap/src/crypto/aes-unwrap.c b/hostap/src/crypto/aes-unwrap.c
new file mode 100644
index 0000000..ec793d9
--- /dev/null
+++ b/hostap/src/crypto/aes-unwrap.c
@@ -0,0 +1,80 @@
+/*
+ * AES key unwrap (RFC3394)
+ *
+ * Copyright (c) 2003-2007, 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 "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (RFC3394)
+ * @kek: Key encryption key (KEK)
+ * @kek_len: Length of KEK in octets
+ * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16
+ * bytes
+ * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits
+ * @plain: Plaintext key, n * 64 bits
+ * Returns: 0 on success, -1 on failure (e.g., integrity verification failed)
+ */
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+	       u8 *plain)
+{
+	u8 a[8], *r, b[AES_BLOCK_SIZE];
+	int i, j;
+	void *ctx;
+	unsigned int t;
+
+	/* 1) Initialize variables. */
+	os_memcpy(a, cipher, 8);
+	r = plain;
+	os_memcpy(r, cipher + 8, 8 * n);
+
+	ctx = aes_decrypt_init(kek, kek_len);
+	if (ctx == NULL)
+		return -1;
+
+	/* 2) Compute intermediate values.
+	 * For j = 5 to 0
+	 *     For i = n to 1
+	 *         B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i
+	 *         A = MSB(64, B)
+	 *         R[i] = LSB(64, B)
+	 */
+	for (j = 5; j >= 0; j--) {
+		r = plain + (n - 1) * 8;
+		for (i = n; i >= 1; i--) {
+			os_memcpy(b, a, 8);
+			t = n * j + i;
+			b[7] ^= t;
+			b[6] ^= t >> 8;
+			b[5] ^= t >> 16;
+			b[4] ^= t >> 24;
+
+			os_memcpy(b + 8, r, 8);
+			aes_decrypt(ctx, b, b);
+			os_memcpy(a, b, 8);
+			os_memcpy(r, b + 8, 8);
+			r -= 8;
+		}
+	}
+	aes_decrypt_deinit(ctx);
+
+	/* 3) Output results.
+	 *
+	 * These are already in @plain due to the location of temporary
+	 * variables. Just verify that the IV matches with the expected value.
+	 */
+	for (i = 0; i < 8; i++) {
+		if (a[i] != 0xa6)
+			return -1;
+	}
+
+	return 0;
+}
diff --git a/hostap/src/crypto/aes-wrap.c b/hostap/src/crypto/aes-wrap.c
new file mode 100644
index 0000000..7ed34e8
--- /dev/null
+++ b/hostap/src/crypto/aes-wrap.c
@@ -0,0 +1,76 @@
+/*
+ * AES Key Wrap Algorithm (RFC3394)
+ *
+ * Copyright (c) 2003-2007, 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 "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_wrap - Wrap keys with AES Key Wrap Algorithm (RFC3394)
+ * @kek: Key encryption key (KEK)
+ * @kek_len: Length of KEK in octets
+ * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16
+ * bytes
+ * @plain: Plaintext key to be wrapped, n * 64 bits
+ * @cipher: Wrapped key, (n + 1) * 64 bits
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
+{
+	u8 *a, *r, b[AES_BLOCK_SIZE];
+	int i, j;
+	void *ctx;
+	unsigned int t;
+
+	a = cipher;
+	r = cipher + 8;
+
+	/* 1) Initialize variables. */
+	os_memset(a, 0xa6, 8);
+	os_memcpy(r, plain, 8 * n);
+
+	ctx = aes_encrypt_init(kek, kek_len);
+	if (ctx == NULL)
+		return -1;
+
+	/* 2) Calculate intermediate values.
+	 * For j = 0 to 5
+	 *     For i=1 to n
+	 *         B = AES(K, A | R[i])
+	 *         A = MSB(64, B) ^ t where t = (n*j)+i
+	 *         R[i] = LSB(64, B)
+	 */
+	for (j = 0; j <= 5; j++) {
+		r = cipher + 8;
+		for (i = 1; i <= n; i++) {
+			os_memcpy(b, a, 8);
+			os_memcpy(b + 8, r, 8);
+			aes_encrypt(ctx, b, b);
+			os_memcpy(a, b, 8);
+			t = n * j + i;
+			a[7] ^= t;
+			a[6] ^= t >> 8;
+			a[5] ^= t >> 16;
+			a[4] ^= t >> 24;
+			os_memcpy(r, b + 8, 8);
+			r += 8;
+		}
+	}
+	aes_encrypt_deinit(ctx);
+
+	/* 3) Output the results.
+	 *
+	 * These are already in @cipher due to the location of temporary
+	 * variables.
+	 */
+
+	return 0;
+}
diff --git a/hostap/src/crypto/aes.h b/hostap/src/crypto/aes.h
new file mode 100644
index 0000000..2de59e0
--- /dev/null
+++ b/hostap/src/crypto/aes.h
@@ -0,0 +1,21 @@
+/*
+ * AES functions
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AES_H
+#define AES_H
+
+#define AES_BLOCK_SIZE 16
+
+void * aes_encrypt_init(const u8 *key, size_t len);
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+void aes_encrypt_deinit(void *ctx);
+void * aes_decrypt_init(const u8 *key, size_t len);
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+void aes_decrypt_deinit(void *ctx);
+
+#endif /* AES_H */
diff --git a/hostap/src/crypto/aes_i.h b/hostap/src/crypto/aes_i.h
new file mode 100644
index 0000000..54375cf
--- /dev/null
+++ b/hostap/src/crypto/aes_i.h
@@ -0,0 +1,125 @@
+/*
+ * AES (Rijndael) cipher
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AES_I_H
+#define AES_I_H
+
+#include "aes.h"
+
+/* #define FULL_UNROLL */
+#define AES_SMALL_TABLES
+
+extern const u32 Te0[256];
+extern const u32 Te1[256];
+extern const u32 Te2[256];
+extern const u32 Te3[256];
+extern const u32 Te4[256];
+extern const u32 Td0[256];
+extern const u32 Td1[256];
+extern const u32 Td2[256];
+extern const u32 Td3[256];
+extern const u32 Td4[256];
+extern const u32 rcon[10];
+extern const u8 Td4s[256];
+extern const u8 rcons[10];
+
+#ifndef AES_SMALL_TABLES
+
+#define RCON(i) rcon[(i)]
+
+#define TE0(i) Te0[((i) >> 24) & 0xff]
+#define TE1(i) Te1[((i) >> 16) & 0xff]
+#define TE2(i) Te2[((i) >> 8) & 0xff]
+#define TE3(i) Te3[(i) & 0xff]
+#define TE41(i) (Te4[((i) >> 24) & 0xff] & 0xff000000)
+#define TE42(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE43(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE44(i) (Te4[(i) & 0xff] & 0x000000ff)
+#define TE421(i) (Te4[((i) >> 16) & 0xff] & 0xff000000)
+#define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000)
+#define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00)
+#define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff)
+#define TE411(i) (Te4[((i) >> 24) & 0xff] & 0xff000000)
+#define TE422(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE433(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE444(i) (Te4[(i) & 0xff] & 0x000000ff)
+#define TE4(i) (Te4[(i)] & 0x000000ff)
+
+#define TD0(i) Td0[((i) >> 24) & 0xff]
+#define TD1(i) Td1[((i) >> 16) & 0xff]
+#define TD2(i) Td2[((i) >> 8) & 0xff]
+#define TD3(i) Td3[(i) & 0xff]
+#define TD41(i) (Td4[((i) >> 24) & 0xff] & 0xff000000)
+#define TD42(i) (Td4[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TD43(i) (Td4[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TD44(i) (Td4[(i) & 0xff] & 0x000000ff)
+#define TD0_(i) Td0[(i) & 0xff]
+#define TD1_(i) Td1[(i) & 0xff]
+#define TD2_(i) Td2[(i) & 0xff]
+#define TD3_(i) Td3[(i) & 0xff]
+
+#else /* AES_SMALL_TABLES */
+
+#define RCON(i) (rcons[(i)] << 24)
+
+static inline u32 rotr(u32 val, int bits)
+{
+	return (val >> bits) | (val << (32 - bits));
+}
+
+#define TE0(i) Te0[((i) >> 24) & 0xff]
+#define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8)
+#define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16)
+#define TE3(i) rotr(Te0[(i) & 0xff], 24)
+#define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000)
+#define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff)
+#define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000)
+#define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000)
+#define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00)
+#define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff)
+#define TE411(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000)
+#define TE422(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE433(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE444(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff)
+#define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff)
+
+#define TD0(i) Td0[((i) >> 24) & 0xff]
+#define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8)
+#define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16)
+#define TD3(i) rotr(Td0[(i) & 0xff], 24)
+#define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24)
+#define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16)
+#define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8)
+#define TD44(i) (Td4s[(i) & 0xff])
+#define TD0_(i) Td0[(i) & 0xff]
+#define TD1_(i) rotr(Td0[(i) & 0xff], 8)
+#define TD2_(i) rotr(Td0[(i) & 0xff], 16)
+#define TD3_(i) rotr(Td0[(i) & 0xff], 24)
+
+#endif /* AES_SMALL_TABLES */
+
+#ifdef _MSC_VER
+#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)
+#define GETU32(p) SWAP(*((u32 *)(p)))
+#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); }
+#else
+#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \
+((u32)(pt)[2] <<  8) ^ ((u32)(pt)[3]))
+#define PUTU32(ct, st) { \
+(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \
+(ct)[2] = (u8)((st) >>  8); (ct)[3] = (u8)(st); }
+#endif
+
+#define AES_PRIV_SIZE (4 * 4 * 15 + 4)
+#define AES_PRIV_NR_POS (4 * 15)
+
+int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits);
+
+#endif /* AES_I_H */
diff --git a/hostap/src/crypto/aes_siv.h b/hostap/src/crypto/aes_siv.h
new file mode 100644
index 0000000..463cf65
--- /dev/null
+++ b/hostap/src/crypto/aes_siv.h
@@ -0,0 +1,19 @@
+/*
+ * AES SIV (RFC 5297)
+ * Copyright (c) 2013 Cozybit, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AES_SIV_H
+#define AES_SIV_H
+
+int aes_siv_encrypt(const u8 *key, const u8 *pw,
+		    size_t pwlen, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *out);
+int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len,
+		    size_t num_elem, const u8 *addr[], const size_t *len,
+		    u8 *out);
+
+#endif /* AES_SIV_H */
diff --git a/hostap/src/crypto/aes_wrap.h b/hostap/src/crypto/aes_wrap.h
new file mode 100644
index 0000000..4a14209
--- /dev/null
+++ b/hostap/src/crypto/aes_wrap.h
@@ -0,0 +1,71 @@
+/*
+ * AES-based functions
+ *
+ * - AES Key Wrap Algorithm (RFC3394)
+ * - One-Key CBC MAC (OMAC1) hash with AES-128 and AES-256
+ * - AES-128 CTR mode encryption
+ * - AES-128 EAX mode encryption/decryption
+ * - AES-128 CBC
+ * - AES-GCM
+ * - AES-CCM
+ *
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AES_WRAP_H
+#define AES_WRAP_H
+
+int __must_check aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain,
+			  u8 *cipher);
+int __must_check aes_unwrap(const u8 *kek, size_t kek_len, int n,
+			    const u8 *cipher, u8 *plain);
+int __must_check omac1_aes_vector(const u8 *key, size_t key_len,
+				  size_t num_elem, const u8 *addr[],
+				  const size_t *len, u8 *mac);
+int __must_check omac1_aes_128_vector(const u8 *key, size_t num_elem,
+				      const u8 *addr[], const size_t *len,
+				      u8 *mac);
+int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len,
+			       u8 *mac);
+int __must_check omac1_aes_256(const u8 *key, const u8 *data, size_t data_len,
+			       u8 *mac);
+int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out);
+int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+				     u8 *data, size_t data_len);
+int __must_check aes_128_eax_encrypt(const u8 *key,
+				     const u8 *nonce, size_t nonce_len,
+				     const u8 *hdr, size_t hdr_len,
+				     u8 *data, size_t data_len, u8 *tag);
+int __must_check aes_128_eax_decrypt(const u8 *key,
+				     const u8 *nonce, size_t nonce_len,
+				     const u8 *hdr, size_t hdr_len,
+				     u8 *data, size_t data_len, const u8 *tag);
+int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data,
+				     size_t data_len);
+int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data,
+				     size_t data_len);
+int __must_check aes_gcm_ae(const u8 *key, size_t key_len,
+			    const u8 *iv, size_t iv_len,
+			    const u8 *plain, size_t plain_len,
+			    const u8 *aad, size_t aad_len,
+			    u8 *crypt, u8 *tag);
+int __must_check aes_gcm_ad(const u8 *key, size_t key_len,
+			    const u8 *iv, size_t iv_len,
+			    const u8 *crypt, size_t crypt_len,
+			    const u8 *aad, size_t aad_len, const u8 *tag,
+			    u8 *plain);
+int __must_check aes_gmac(const u8 *key, size_t key_len,
+			  const u8 *iv, size_t iv_len,
+			  const u8 *aad, size_t aad_len, u8 *tag);
+int __must_check aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce,
+			    size_t M, const u8 *plain, size_t plain_len,
+			    const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth);
+int __must_check aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce,
+			    size_t M, const u8 *crypt, size_t crypt_len,
+			    const u8 *aad, size_t aad_len, const u8 *auth,
+			    u8 *plain);
+
+#endif /* AES_WRAP_H */
diff --git a/hostap/src/crypto/crypto.h b/hostap/src/crypto/crypto.h
new file mode 100644
index 0000000..534c4bd
--- /dev/null
+++ b/hostap/src/crypto/crypto.h
@@ -0,0 +1,809 @@
+/*
+ * Wrapper functions for crypto libraries
+ * 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.
+ *
+ * This file defines the cryptographic functions that need to be implemented
+ * for wpa_supplicant and hostapd. When TLS is not used, internal
+ * implementation of MD5, SHA1, and AES is used and no external libraries are
+ * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the
+ * crypto library used by the TLS implementation is expected to be used for
+ * non-TLS needs, too, in order to save space by not implementing these
+ * functions twice.
+ *
+ * Wrapper code for using each crypto library is in its own file (crypto*.c)
+ * and one of these files is build and linked in to provide the functions
+ * defined here.
+ */
+
+#ifndef CRYPTO_H
+#define CRYPTO_H
+
+/**
+ * md4_vector - MD4 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+
+/**
+ * sha1_vector - SHA-1 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		u8 *mac);
+
+/**
+ * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF
+ * @seed: Seed/key for the PRF
+ * @seed_len: Seed length in bytes
+ * @x: Buffer for PRF output
+ * @xlen: Output length in bytes
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function implements random number generation specified in NIST FIPS
+ * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to
+ * SHA-1, but has different message padding.
+ */
+int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x,
+			       size_t xlen);
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac);
+
+/**
+ * des_encrypt - Encrypt one block with DES
+ * @clear: 8 octets (in)
+ * @key: 7 octets (in) (no parity bits included)
+ * @cypher: 8 octets (out)
+ */
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
+
+/**
+ * aes_encrypt_init - Initialize AES for encryption
+ * @key: Encryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_encrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_encrypt - Encrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @plain: Plaintext data to be encrypted (16 bytes)
+ * @crypt: Buffer for the encrypted data (16 bytes)
+ */
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+
+/**
+ * aes_encrypt_deinit - Deinitialize AES encryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_encrypt_deinit(void *ctx);
+
+/**
+ * aes_decrypt_init - Initialize AES for decryption
+ * @key: Decryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_decrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_decrypt - Decrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @crypt: Encrypted data (16 bytes)
+ * @plain: Buffer for the decrypted data (16 bytes)
+ */
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+
+/**
+ * aes_decrypt_deinit - Deinitialize AES decryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_decrypt_deinit(void *ctx);
+
+
+enum crypto_hash_alg {
+	CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
+	CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1,
+	CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256
+};
+
+struct crypto_hash;
+
+/**
+ * crypto_hash_init - Initialize hash/HMAC function
+ * @alg: Hash algorithm
+ * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed
+ * @key_len: Length of the key in bytes
+ * Returns: Pointer to hash context to use with other hash functions or %NULL
+ * on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len);
+
+/**
+ * crypto_hash_update - Add data to hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @data: Data buffer to add
+ * @len: Length of the buffer
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len);
+
+/**
+ * crypto_hash_finish - Complete hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @hash: Buffer for hash value or %NULL if caller is just freeing the hash
+ * context
+ * @len: Pointer to length of the buffer or %NULL if caller is just freeing the
+ * hash context; on return, this is set to the actual length of the hash value
+ * Returns: 0 on success, -1 if buffer is too small (len set to needed length),
+ * or -2 on other failures (including failed crypto_hash_update() operations)
+ *
+ * This function calculates the hash value and frees the context buffer that
+ * was used for hash calculation.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len);
+
+
+enum crypto_cipher_alg {
+	CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES,
+	CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4
+};
+
+struct crypto_cipher;
+
+/**
+ * crypto_cipher_init - Initialize block/stream cipher function
+ * @alg: Cipher algorithm
+ * @iv: Initialization vector for block ciphers or %NULL for stream ciphers
+ * @key: Cipher key
+ * @key_len: Length of key in bytes
+ * Returns: Pointer to cipher context to use with other cipher functions or
+ * %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len);
+
+/**
+ * crypto_cipher_encrypt - Cipher encrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @plain: Plaintext to cipher
+ * @crypt: Resulting ciphertext
+ * @len: Length of the plaintext
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx,
+				       const u8 *plain, u8 *crypt, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Cipher decrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @crypt: Ciphertext to decrypt
+ * @plain: Resulting plaintext
+ * @len: Length of the cipher text
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx,
+				       const u8 *crypt, u8 *plain, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Free cipher context
+ * @ctx: Context pointer from crypto_cipher_init()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_cipher_deinit(struct crypto_cipher *ctx);
+
+
+struct crypto_public_key;
+struct crypto_private_key;
+
+/**
+ * crypto_public_key_import - Import an RSA public key
+ * @key: Key buffer (DER encoded RSA public key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library supports X.509
+ * parsing. In that case, crypto_public_key_from_cert() is used to import the
+ * public key from a certificate.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+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)
+ * @len: Key buffer length in bytes
+ * @passwd: Key encryption password or %NULL if key is not encrypted
+ * Returns: Pointer to the private key or %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+						      size_t len,
+						      const char *passwd);
+
+/**
+ * crypto_public_key_from_cert - Import an RSA public key from a certificate
+ * @buf: DER encoded X.509 certificate
+ * @len: Certificate buffer length in bytes
+ * Returns: Pointer to public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library does not support
+ * X.509 parsing. In that case, internal code will be used to parse the
+ * certificate and public key is imported using crypto_public_key_import().
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+						       size_t len);
+
+/**
+ * crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5)
+ * @key: Public key
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_public_key_encrypt_pkcs1_v15(
+	struct crypto_public_key *key, const u8 *in, size_t inlen,
+	u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5)
+ * @key: Private key
+ * @in: Encrypted buffer
+ * @inlen: Length of encrypted buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_decrypt_pkcs1_v15(
+	struct crypto_private_key *key, const u8 *in, size_t inlen,
+	u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1)
+ * @key: Private key from crypto_private_key_import()
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted (signed) data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+					       const u8 *in, size_t inlen,
+					       u8 *out, size_t *outlen);
+
+/**
+ * crypto_public_key_free - Free public key
+ * @key: Public key
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_public_key_free(struct crypto_public_key *key);
+
+/**
+ * crypto_private_key_free - Free private key
+ * @key: Private key from crypto_private_key_import()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_private_key_free(struct crypto_private_key *key);
+
+/**
+ * crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature
+ * @key: Public key
+ * @crypt: Encrypted signature data (using the private key)
+ * @crypt_len: Encrypted signature data length
+ * @plain: Buffer for plaintext (at least crypt_len bytes)
+ * @plain_len: Plaintext length (max buffer size on input, real len on output);
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check crypto_public_key_decrypt_pkcs1(
+	struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
+	u8 *plain, size_t *plain_len);
+
+/**
+ * crypto_global_init - Initialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_global_init(void);
+
+/**
+ * crypto_global_deinit - Deinitialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_global_deinit(void);
+
+/**
+ * crypto_mod_exp - Modular exponentiation of large integers
+ * @base: Base integer (big endian byte array)
+ * @base_len: Length of base integer in bytes
+ * @power: Power integer (big endian byte array)
+ * @power_len: Length of power integer in bytes
+ * @modulus: Modulus integer (big endian byte array)
+ * @modulus_len: Length of modulus integer in bytes
+ * @result: Buffer for the result
+ * @result_len: Result length (max buffer size on input, real len on output)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function calculates result = base ^ power mod modulus. modules_len is
+ * used as the maximum size of modulus buffer. It is set to the used size on
+ * success.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_mod_exp(const u8 *base, size_t base_len,
+				const u8 *power, size_t power_len,
+				const u8 *modulus, size_t modulus_len,
+				u8 *result, size_t *result_len);
+
+/**
+ * rc4_skip - XOR RC4 stream to given data with skip-stream-start
+ * @key: RC4 key
+ * @keylen: RC4 key length
+ * @skip: number of bytes to skip from the beginning of the RC4 stream
+ * @data: data to be XOR'ed with RC4 stream
+ * @data_len: buf length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Generate RC4 pseudo random stream for the given key, skip beginning of the
+ * stream, and XOR the end result with the data buffer to perform RC4
+ * encryption/decryption.
+ */
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+	     u8 *data, size_t data_len);
+
+/**
+ * crypto_get_random - Generate cryptographically strong pseudy-random bytes
+ * @buf: Buffer for data
+ * @len: Number of bytes to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * If the PRNG does not have enough entropy to ensure unpredictable byte
+ * sequence, this functions must return -1.
+ */
+int crypto_get_random(void *buf, size_t len);
+
+
+/**
+ * struct crypto_bignum - bignum
+ *
+ * Internal data structure for bignum implementation. The contents is specific
+ * to the used crypto library.
+ */
+struct crypto_bignum;
+
+/**
+ * crypto_bignum_init - Allocate memory for bignum
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct crypto_bignum * crypto_bignum_init(void);
+
+/**
+ * crypto_bignum_init_set - Allocate memory for bignum and set the value
+ * @buf: Buffer with unsigned binary value
+ * @len: Length of buf in octets
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len);
+
+/**
+ * crypto_bignum_deinit - Free bignum
+ * @n: Bignum from crypto_bignum_init() or crypto_bignum_init_set()
+ * @clear: Whether to clear the value from memory
+ */
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear);
+
+/**
+ * crypto_bignum_to_bin - Set binary buffer to unsigned bignum
+ * @a: Bignum
+ * @buf: Buffer for the binary number
+ * @len: Length of @buf in octets
+ * @padlen: Length in octets to pad the result to or 0 to indicate no padding
+ * Returns: Number of octets written on success, -1 on failure
+ */
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+			 u8 *buf, size_t buflen, size_t padlen);
+
+/**
+ * crypto_bignum_add - c = a + b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_add(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_mod - c = a % b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a % b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_mod(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_exptmod - Modular exponentiation: d = a^b (mod c)
+ * @a: Bignum; base
+ * @b: Bignum; exponent
+ * @c: Bignum; modulus
+ * @d: Bignum; used to store the result of a^b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_exptmod(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  const struct crypto_bignum *c,
+			  struct crypto_bignum *d);
+
+/**
+ * crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_sub - c = a - b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a - b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_sub(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_div - c = a / b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a / b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_div(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_mulmod - d = a * b (mod c)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum
+ * @d: Bignum; used to store the result of (a * b) % c
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d);
+
+/**
+ * crypto_bignum_cmp - Compare two bignums
+ * @a: Bignum
+ * @b: Bignum
+ * Returns: -1 if a < b, 0 if a == b, or 1 if a > b
+ */
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b);
+
+/**
+ * crypto_bignum_bits - Get size of a bignum in bits
+ * @a: Bignum
+ * Returns: Number of bits in the bignum
+ */
+int crypto_bignum_bits(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_is_zero - Is the given bignum zero
+ * @a: Bignum
+ * Returns: 1 if @a is zero or 0 if not
+ */
+int crypto_bignum_is_zero(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_is_one - Is the given bignum one
+ * @a: Bignum
+ * Returns: 1 if @a is one or 0 if not
+ */
+int crypto_bignum_is_one(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_legendre - Compute the Legendre symbol (a/p)
+ * @a: Bignum
+ * @p: Bignum
+ * Returns: Legendre symbol -1,0,1 on success; -2 on calculation failure
+ */
+int crypto_bignum_legendre(const struct crypto_bignum *a,
+			   const struct crypto_bignum *p);
+
+/**
+ * struct crypto_ec - Elliptic curve context
+ *
+ * Internal data structure for EC implementation. The contents is specific
+ * to the used crypto library.
+ */
+struct crypto_ec;
+
+/**
+ * crypto_ec_init - Initialize elliptic curve context
+ * @group: Identifying number for the ECC group (IANA "Group Description"
+ *	attribute registrty for RFC 2409)
+ * Returns: Pointer to EC context or %NULL on failure
+ */
+struct crypto_ec * crypto_ec_init(int group);
+
+/**
+ * crypto_ec_deinit - Deinitialize elliptic curve context
+ * @e: EC context from crypto_ec_init()
+ */
+void crypto_ec_deinit(struct crypto_ec *e);
+
+/**
+ * crypto_ec_prime_len - Get length of the prime in octets
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the prime defining the group
+ */
+size_t crypto_ec_prime_len(struct crypto_ec *e);
+
+/**
+ * crypto_ec_prime_len_bits - Get length of the prime in bits
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the prime defining the group in bits
+ */
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_prime - Get prime defining an EC group
+ * @e: EC context from crypto_ec_init()
+ * Returns: Prime (bignum) defining the group
+ */
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_order - Get order of an EC group
+ * @e: EC context from crypto_ec_init()
+ * Returns: Order (bignum) of the group
+ */
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e);
+
+/**
+ * struct crypto_ec_point - Elliptic curve point
+ *
+ * Internal data structure for EC implementation to represent a point. The
+ * contents is specific to the used crypto library.
+ */
+struct crypto_ec_point;
+
+/**
+ * crypto_ec_point_init - Initialize data for an EC point
+ * @e: EC context from crypto_ec_init()
+ * Returns: Pointer to EC point data or %NULL on failure
+ */
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e);
+
+/**
+ * crypto_ec_point_deinit - Deinitialize EC point data
+ * @p: EC point data from crypto_ec_point_init()
+ * @clear: Whether to clear the EC point value from memory
+ */
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear);
+
+/**
+ * crypto_ec_point_to_bin - Write EC point value as binary data
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point data from crypto_ec_point_init()
+ * @x: Buffer for writing the binary data for x coordinate or %NULL if not used
+ * @y: Buffer for writing the binary data for y coordinate or %NULL if not used
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to write an EC point as binary data in a format
+ * that has the x and y coordinates in big endian byte order fields padded to
+ * the length of the prime defining the group.
+ */
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+			   const struct crypto_ec_point *point, u8 *x, u8 *y);
+
+/**
+ * crypto_ec_point_from_bin - Create EC point from binary data
+ * @e: EC context from crypto_ec_init()
+ * @val: Binary data to read the EC point from
+ * Returns: Pointer to EC point data or %NULL on failure
+ *
+ * This function readers x and y coordinates of the EC point from the provided
+ * buffer assuming the values are in big endian byte order with fields padded to
+ * the length of the prime defining the group.
+ */
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+						  const u8 *val);
+
+/**
+ * crypto_bignum_add - c = a + b
+ * @e: EC context from crypto_ec_init()
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b,
+			struct crypto_ec_point *c);
+
+/**
+ * crypto_bignum_mul - res = b * p
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * @b: Bignum
+ * @res: EC point; used to store the result of b * p
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+			const struct crypto_bignum *b,
+			struct crypto_ec_point *res);
+
+/**
+ * crypto_ec_point_invert - Compute inverse of an EC point
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point to invert (and result of the operation)
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_solve_y_coord - Solve y coordinate for an x coordinate
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point to use for the returning the result
+ * @x: x coordinate
+ * @y_bit: y-bit (0 or 1) for selecting the y value to use
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+				  struct crypto_ec_point *p,
+				  const struct crypto_bignum *x, int y_bit);
+
+/**
+ * crypto_ec_point_compute_y_sqr - Compute y^2 = x^3 + ax + b
+ * @e: EC context from crypto_ec_init()
+ * @x: x coordinate
+ * Returns: y^2 on success, %NULL failure
+ */
+struct crypto_bignum *
+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
+			      const struct crypto_bignum *x);
+
+/**
+ * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * Returns: 1 if the specified EC point is the neutral element of the group or
+ *	0 if not
+ */
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+				   const struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_is_on_curve - Check whether EC point is on curve
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * Returns: 1 if the specified EC point is on the curve or 0 if not
+ */
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+				const struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_cmp - Compare two EC points
+ * @e: EC context from crypto_ec_init()
+ * @a: EC point
+ * @b: EC point
+ * Returns: 0 on equal, non-zero otherwise
+ */
+int crypto_ec_point_cmp(const struct crypto_ec *e,
+			const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b);
+
+#endif /* CRYPTO_H */
diff --git a/hostap/src/crypto/crypto_gnutls.c b/hostap/src/crypto/crypto_gnutls.c
new file mode 100644
index 0000000..0dfd54d
--- /dev/null
+++ b/hostap/src/crypto/crypto_gnutls.c
@@ -0,0 +1,299 @@
+/*
+ * WPA Supplicant / wrapper functions 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 md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	gcry_md_hd_t hd;
+	unsigned char *p;
+	size_t i;
+
+	if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR)
+		return -1;
+	for (i = 0; i < num_elem; i++)
+		gcry_md_write(hd, addr[i], len[i]);
+	p = gcry_md_read(hd, GCRY_MD_MD4);
+	if (p)
+		memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4));
+	gcry_md_close(hd);
+	return 0;
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	gcry_cipher_hd_t hd;
+	u8 pkey[8], next, tmp;
+	int i;
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	gcry_cipher_open(&hd, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
+	gcry_err_code(gcry_cipher_setkey(hd, pkey, 8));
+	gcry_cipher_encrypt(hd, cypher, 8, clear, 8);
+	gcry_cipher_close(hd);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	gcry_md_hd_t hd;
+	unsigned char *p;
+	size_t i;
+
+	if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR)
+		return -1;
+	for (i = 0; i < num_elem; i++)
+		gcry_md_write(hd, addr[i], len[i]);
+	p = gcry_md_read(hd, GCRY_MD_MD5);
+	if (p)
+		memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5));
+	gcry_md_close(hd);
+	return 0;
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	gcry_md_hd_t hd;
+	unsigned char *p;
+	size_t i;
+
+	if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR)
+		return -1;
+	for (i = 0; i < num_elem; i++)
+		gcry_md_write(hd, addr[i], len[i]);
+	p = gcry_md_read(hd, GCRY_MD_SHA1);
+	if (p)
+		memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1));
+	gcry_md_close(hd);
+	return 0;
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	gcry_cipher_hd_t hd;
+
+	if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
+	    GPG_ERR_NO_ERROR) {
+		printf("cipher open failed\n");
+		return NULL;
+	}
+	if (gcry_cipher_setkey(hd, key, len) != GPG_ERR_NO_ERROR) {
+		printf("setkey failed\n");
+		gcry_cipher_close(hd);
+		return NULL;
+	}
+
+	return hd;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	gcry_cipher_hd_t hd = ctx;
+	gcry_cipher_encrypt(hd, crypt, 16, plain, 16);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	gcry_cipher_hd_t hd = ctx;
+	gcry_cipher_close(hd);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	gcry_cipher_hd_t hd;
+
+	if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
+	    GPG_ERR_NO_ERROR)
+		return NULL;
+	if (gcry_cipher_setkey(hd, key, len) != GPG_ERR_NO_ERROR) {
+		gcry_cipher_close(hd);
+		return NULL;
+	}
+
+	return hd;
+}
+
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	gcry_cipher_hd_t hd = ctx;
+	gcry_cipher_decrypt(hd, plain, 16, crypt, 16);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	gcry_cipher_hd_t hd = ctx;
+	gcry_cipher_close(hd);
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+		   const u8 *power, size_t power_len,
+		   const u8 *modulus, size_t modulus_len,
+		   u8 *result, size_t *result_len)
+{
+	gcry_mpi_t bn_base = NULL, bn_exp = NULL, bn_modulus = NULL,
+		bn_result = NULL;
+	int ret = -1;
+
+	if (gcry_mpi_scan(&bn_base, GCRYMPI_FMT_USG, base, base_len, NULL) !=
+	    GPG_ERR_NO_ERROR ||
+	    gcry_mpi_scan(&bn_exp, GCRYMPI_FMT_USG, power, power_len, NULL) !=
+	    GPG_ERR_NO_ERROR ||
+	    gcry_mpi_scan(&bn_modulus, GCRYMPI_FMT_USG, modulus, modulus_len,
+			  NULL) != GPG_ERR_NO_ERROR)
+		goto error;
+	bn_result = gcry_mpi_new(modulus_len * 8);
+
+	gcry_mpi_powm(bn_result, bn_base, bn_exp, bn_modulus);
+
+	if (gcry_mpi_print(GCRYMPI_FMT_USG, result, *result_len, result_len,
+			   bn_result) != GPG_ERR_NO_ERROR)
+		goto error;
+
+	ret = 0;
+
+error:
+	gcry_mpi_release(bn_base);
+	gcry_mpi_release(bn_exp);
+	gcry_mpi_release(bn_modulus);
+	gcry_mpi_release(bn_result);
+	return ret;
+}
+
+
+struct crypto_cipher {
+	gcry_cipher_hd_t enc;
+	gcry_cipher_hd_t dec;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+	gcry_error_t res;
+	enum gcry_cipher_algos a;
+	int ivlen;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	switch (alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		a = GCRY_CIPHER_ARCFOUR;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_STREAM,
+				       0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_STREAM, 0);
+		break;
+	case CRYPTO_CIPHER_ALG_AES:
+		if (key_len == 24)
+			a = GCRY_CIPHER_AES192;
+		else if (key_len == 32)
+			a = GCRY_CIPHER_AES256;
+		else
+			a = GCRY_CIPHER_AES;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		a = GCRY_CIPHER_3DES;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		a = GCRY_CIPHER_DES;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+		break;
+	case CRYPTO_CIPHER_ALG_RC2:
+		if (key_len == 5)
+			a = GCRY_CIPHER_RFC2268_40;
+		else
+			a = GCRY_CIPHER_RFC2268_128;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+		break;
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (res != GPG_ERR_NO_ERROR) {
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (gcry_cipher_setkey(ctx->enc, key, key_len) != GPG_ERR_NO_ERROR ||
+	    gcry_cipher_setkey(ctx->dec, key, key_len) != GPG_ERR_NO_ERROR) {
+		gcry_cipher_close(ctx->enc);
+		gcry_cipher_close(ctx->dec);
+		os_free(ctx);
+		return NULL;
+	}
+
+	ivlen = gcry_cipher_get_algo_blklen(a);
+	if (gcry_cipher_setiv(ctx->enc, iv, ivlen) != GPG_ERR_NO_ERROR ||
+	    gcry_cipher_setiv(ctx->dec, iv, ivlen) != GPG_ERR_NO_ERROR) {
+		gcry_cipher_close(ctx->enc);
+		gcry_cipher_close(ctx->dec);
+		os_free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	if (gcry_cipher_encrypt(ctx->enc, crypt, len, plain, len) !=
+	    GPG_ERR_NO_ERROR)
+		return -1;
+	return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	if (gcry_cipher_decrypt(ctx->dec, plain, len, crypt, len) !=
+	    GPG_ERR_NO_ERROR)
+		return -1;
+	return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	gcry_cipher_close(ctx->enc);
+	gcry_cipher_close(ctx->dec);
+	os_free(ctx);
+}
diff --git a/hostap/src/crypto/crypto_internal-cipher.c b/hostap/src/crypto/crypto_internal-cipher.c
new file mode 100644
index 0000000..ad0930a
--- /dev/null
+++ b/hostap/src/crypto/crypto_internal-cipher.c
@@ -0,0 +1,243 @@
+/*
+ * Crypto wrapper for internal crypto implementation - Cipher wrappers
+ * Copyright (c) 2006-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"
+#include "aes.h"
+#include "des_i.h"
+
+
+struct crypto_cipher {
+	enum crypto_cipher_alg alg;
+	union {
+		struct {
+			size_t used_bytes;
+			u8 key[16];
+			size_t keylen;
+		} rc4;
+		struct {
+			u8 cbc[32];
+			void *ctx_enc;
+			void *ctx_dec;
+		} aes;
+		struct {
+			struct des3_key_s key;
+			u8 cbc[8];
+		} des3;
+		struct {
+			u32 ek[32];
+			u32 dk[32];
+			u8 cbc[8];
+		} des;
+	} u;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	ctx->alg = alg;
+
+	switch (alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		if (key_len > sizeof(ctx->u.rc4.key)) {
+			os_free(ctx);
+			return NULL;
+		}
+		ctx->u.rc4.keylen = key_len;
+		os_memcpy(ctx->u.rc4.key, key, key_len);
+		break;
+	case CRYPTO_CIPHER_ALG_AES:
+		ctx->u.aes.ctx_enc = aes_encrypt_init(key, key_len);
+		if (ctx->u.aes.ctx_enc == NULL) {
+			os_free(ctx);
+			return NULL;
+		}
+		ctx->u.aes.ctx_dec = aes_decrypt_init(key, key_len);
+		if (ctx->u.aes.ctx_dec == NULL) {
+			aes_encrypt_deinit(ctx->u.aes.ctx_enc);
+			os_free(ctx);
+			return NULL;
+		}
+		os_memcpy(ctx->u.aes.cbc, iv, AES_BLOCK_SIZE);
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		if (key_len != 24) {
+			os_free(ctx);
+			return NULL;
+		}
+		des3_key_setup(key, &ctx->u.des3.key);
+		os_memcpy(ctx->u.des3.cbc, iv, 8);
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		if (key_len != 8) {
+			os_free(ctx);
+			return NULL;
+		}
+		des_key_setup(key, ctx->u.des.ek, ctx->u.des.dk);
+		os_memcpy(ctx->u.des.cbc, iv, 8);
+		break;
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	size_t i, j, blocks;
+
+	switch (ctx->alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		if (plain != crypt)
+			os_memcpy(crypt, plain, len);
+		rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+			 ctx->u.rc4.used_bytes, crypt, len);
+		ctx->u.rc4.used_bytes += len;
+		break;
+	case CRYPTO_CIPHER_ALG_AES:
+		if (len % AES_BLOCK_SIZE)
+			return -1;
+		blocks = len / AES_BLOCK_SIZE;
+		for (i = 0; i < blocks; i++) {
+			for (j = 0; j < AES_BLOCK_SIZE; j++)
+				ctx->u.aes.cbc[j] ^= plain[j];
+			aes_encrypt(ctx->u.aes.ctx_enc, ctx->u.aes.cbc,
+				    ctx->u.aes.cbc);
+			os_memcpy(crypt, ctx->u.aes.cbc, AES_BLOCK_SIZE);
+			plain += AES_BLOCK_SIZE;
+			crypt += AES_BLOCK_SIZE;
+		}
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		if (len % 8)
+			return -1;
+		blocks = len / 8;
+		for (i = 0; i < blocks; i++) {
+			for (j = 0; j < 8; j++)
+				ctx->u.des3.cbc[j] ^= plain[j];
+			des3_encrypt(ctx->u.des3.cbc, &ctx->u.des3.key,
+				     ctx->u.des3.cbc);
+			os_memcpy(crypt, ctx->u.des3.cbc, 8);
+			plain += 8;
+			crypt += 8;
+		}
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		if (len % 8)
+			return -1;
+		blocks = len / 8;
+		for (i = 0; i < blocks; i++) {
+			for (j = 0; j < 8; j++)
+				ctx->u.des3.cbc[j] ^= plain[j];
+			des_block_encrypt(ctx->u.des.cbc, ctx->u.des.ek,
+					  ctx->u.des.cbc);
+			os_memcpy(crypt, ctx->u.des.cbc, 8);
+			plain += 8;
+			crypt += 8;
+		}
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	size_t i, j, blocks;
+	u8 tmp[32];
+
+	switch (ctx->alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		if (plain != crypt)
+			os_memcpy(plain, crypt, len);
+		rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+			 ctx->u.rc4.used_bytes, plain, len);
+		ctx->u.rc4.used_bytes += len;
+		break;
+	case CRYPTO_CIPHER_ALG_AES:
+		if (len % AES_BLOCK_SIZE)
+			return -1;
+		blocks = len / AES_BLOCK_SIZE;
+		for (i = 0; i < blocks; i++) {
+			os_memcpy(tmp, crypt, AES_BLOCK_SIZE);
+			aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain);
+			for (j = 0; j < AES_BLOCK_SIZE; j++)
+				plain[j] ^= ctx->u.aes.cbc[j];
+			os_memcpy(ctx->u.aes.cbc, tmp, AES_BLOCK_SIZE);
+			plain += AES_BLOCK_SIZE;
+			crypt += AES_BLOCK_SIZE;
+		}
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		if (len % 8)
+			return -1;
+		blocks = len / 8;
+		for (i = 0; i < blocks; i++) {
+			os_memcpy(tmp, crypt, 8);
+			des3_decrypt(crypt, &ctx->u.des3.key, plain);
+			for (j = 0; j < 8; j++)
+				plain[j] ^= ctx->u.des3.cbc[j];
+			os_memcpy(ctx->u.des3.cbc, tmp, 8);
+			plain += 8;
+			crypt += 8;
+		}
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		if (len % 8)
+			return -1;
+		blocks = len / 8;
+		for (i = 0; i < blocks; i++) {
+			os_memcpy(tmp, crypt, 8);
+			des_block_decrypt(crypt, ctx->u.des.dk, plain);
+			for (j = 0; j < 8; j++)
+				plain[j] ^= ctx->u.des.cbc[j];
+			os_memcpy(ctx->u.des.cbc, tmp, 8);
+			plain += 8;
+			crypt += 8;
+		}
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	switch (ctx->alg) {
+	case CRYPTO_CIPHER_ALG_AES:
+		aes_encrypt_deinit(ctx->u.aes.ctx_enc);
+		aes_decrypt_deinit(ctx->u.aes.ctx_dec);
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		break;
+	default:
+		break;
+	}
+	os_free(ctx);
+}
diff --git a/hostap/src/crypto/crypto_internal-modexp.c b/hostap/src/crypto/crypto_internal-modexp.c
new file mode 100644
index 0000000..9dcabb9
--- /dev/null
+++ b/hostap/src/crypto/crypto_internal-modexp.c
@@ -0,0 +1,49 @@
+/*
+ * Crypto wrapper for internal crypto implementation - modexp
+ * Copyright (c) 2006-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 "tls/bignum.h"
+#include "crypto.h"
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+		   const u8 *power, size_t power_len,
+		   const u8 *modulus, size_t modulus_len,
+		   u8 *result, size_t *result_len)
+{
+	struct bignum *bn_base, *bn_exp, *bn_modulus, *bn_result;
+	int ret = -1;
+
+	bn_base = bignum_init();
+	bn_exp = bignum_init();
+	bn_modulus = bignum_init();
+	bn_result = bignum_init();
+
+	if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL ||
+	    bn_result == NULL)
+		goto error;
+
+	if (bignum_set_unsigned_bin(bn_base, base, base_len) < 0 ||
+	    bignum_set_unsigned_bin(bn_exp, power, power_len) < 0 ||
+	    bignum_set_unsigned_bin(bn_modulus, modulus, modulus_len) < 0)
+		goto error;
+
+	if (bignum_exptmod(bn_base, bn_exp, bn_modulus, bn_result) < 0)
+		goto error;
+
+	ret = bignum_get_unsigned_bin(bn_result, result, result_len);
+
+error:
+	bignum_deinit(bn_base);
+	bignum_deinit(bn_exp);
+	bignum_deinit(bn_modulus);
+	bignum_deinit(bn_result);
+	return ret;
+}
diff --git a/hostap/src/crypto/crypto_internal-rsa.c b/hostap/src/crypto/crypto_internal-rsa.c
new file mode 100644
index 0000000..dc7f350
--- /dev/null
+++ b/hostap/src/crypto/crypto_internal-rsa.c
@@ -0,0 +1,117 @@
+/*
+ * Crypto wrapper for internal crypto implementation - RSA parts
+ * Copyright (c) 2006-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"
+#include "tls/rsa.h"
+#include "tls/pkcs1.h"
+#include "tls/pkcs8.h"
+
+/* Dummy structures; these are just typecast to struct crypto_rsa_key */
+struct crypto_public_key;
+struct crypto_private_key;
+
+
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len)
+{
+	return (struct crypto_public_key *)
+		crypto_rsa_import_public_key(key, len);
+}
+
+
+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)
+{
+	struct crypto_private_key *res;
+
+	/* First, check for possible PKCS #8 encoding */
+	res = pkcs8_key_import(key, len);
+	if (res)
+		return res;
+
+	if (passwd) {
+		/* Try to parse as encrypted PKCS #8 */
+		res = pkcs8_enc_key_import(key, len, passwd);
+		if (res)
+			return res;
+	}
+
+	/* Not PKCS#8, so try to import PKCS #1 encoded RSA private key */
+	wpa_printf(MSG_DEBUG, "Trying to parse PKCS #1 encoded RSA private "
+		   "key");
+	return (struct crypto_private_key *)
+		crypto_rsa_import_private_key(key, len);
+}
+
+
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+						       size_t len)
+{
+	/* No X.509 support in crypto_internal.c */
+	return NULL;
+}
+
+
+int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key,
+					const u8 *in, size_t inlen,
+					u8 *out, size_t *outlen)
+{
+	return pkcs1_encrypt(2, (struct crypto_rsa_key *) key,
+			     0, in, inlen, out, outlen);
+}
+
+
+int crypto_private_key_decrypt_pkcs1_v15(struct crypto_private_key *key,
+					 const u8 *in, size_t inlen,
+					 u8 *out, size_t *outlen)
+{
+	return pkcs1_v15_private_key_decrypt((struct crypto_rsa_key *) key,
+					     in, inlen, out, outlen);
+}
+
+
+int crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+				  const u8 *in, size_t inlen,
+				  u8 *out, size_t *outlen)
+{
+	return pkcs1_encrypt(1, (struct crypto_rsa_key *) key,
+			     1, in, inlen, out, outlen);
+}
+
+
+void crypto_public_key_free(struct crypto_public_key *key)
+{
+	crypto_rsa_free((struct crypto_rsa_key *) key);
+}
+
+
+void crypto_private_key_free(struct crypto_private_key *key)
+{
+	crypto_rsa_free((struct crypto_rsa_key *) key);
+}
+
+
+int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key,
+				    const u8 *crypt, size_t crypt_len,
+				    u8 *plain, size_t *plain_len)
+{
+	return pkcs1_decrypt_public_key((struct crypto_rsa_key *) key,
+					crypt, crypt_len, plain, plain_len);
+}
diff --git a/hostap/src/crypto/crypto_internal.c b/hostap/src/crypto/crypto_internal.c
new file mode 100644
index 0000000..f3602da
--- /dev/null
+++ b/hostap/src/crypto/crypto_internal.c
@@ -0,0 +1,275 @@
+/*
+ * Crypto wrapper for internal crypto implementation
+ * Copyright (c) 2006-2011, 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"
+#include "sha256_i.h"
+#include "sha1_i.h"
+#include "md5_i.h"
+
+struct crypto_hash {
+	enum crypto_hash_alg alg;
+	union {
+		struct MD5Context md5;
+		struct SHA1Context sha1;
+#ifdef CONFIG_SHA256
+		struct sha256_state sha256;
+#endif /* CONFIG_SHA256 */
+	} u;
+	u8 key[64];
+	size_t key_len;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len)
+{
+	struct crypto_hash *ctx;
+	u8 k_pad[64];
+	u8 tk[32];
+	size_t i;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	ctx->alg = alg;
+
+	switch (alg) {
+	case CRYPTO_HASH_ALG_MD5:
+		MD5Init(&ctx->u.md5);
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+		SHA1Init(&ctx->u.sha1);
+		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_SHA256:
+		sha256_init(&ctx->u.sha256);
+		break;
+#endif /* CONFIG_SHA256 */
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		if (key_len > sizeof(k_pad)) {
+			MD5Init(&ctx->u.md5);
+			MD5Update(&ctx->u.md5, key, key_len);
+			MD5Final(tk, &ctx->u.md5);
+			key = tk;
+			key_len = 16;
+		}
+		os_memcpy(ctx->key, key, key_len);
+		ctx->key_len = key_len;
+
+		os_memcpy(k_pad, key, key_len);
+		if (key_len < sizeof(k_pad))
+			os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+		for (i = 0; i < sizeof(k_pad); i++)
+			k_pad[i] ^= 0x36;
+		MD5Init(&ctx->u.md5);
+		MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad));
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		if (key_len > sizeof(k_pad)) {
+			SHA1Init(&ctx->u.sha1);
+			SHA1Update(&ctx->u.sha1, key, key_len);
+			SHA1Final(tk, &ctx->u.sha1);
+			key = tk;
+			key_len = 20;
+		}
+		os_memcpy(ctx->key, key, key_len);
+		ctx->key_len = key_len;
+
+		os_memcpy(k_pad, key, key_len);
+		if (key_len < sizeof(k_pad))
+			os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+		for (i = 0; i < sizeof(k_pad); i++)
+			k_pad[i] ^= 0x36;
+		SHA1Init(&ctx->u.sha1);
+		SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad));
+		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		if (key_len > sizeof(k_pad)) {
+			sha256_init(&ctx->u.sha256);
+			sha256_process(&ctx->u.sha256, key, key_len);
+			sha256_done(&ctx->u.sha256, tk);
+			key = tk;
+			key_len = 32;
+		}
+		os_memcpy(ctx->key, key, key_len);
+		ctx->key_len = key_len;
+
+		os_memcpy(k_pad, key, key_len);
+		if (key_len < sizeof(k_pad))
+			os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+		for (i = 0; i < sizeof(k_pad); i++)
+			k_pad[i] ^= 0x36;
+		sha256_init(&ctx->u.sha256);
+		sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad));
+		break;
+#endif /* CONFIG_SHA256 */
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+	if (ctx == NULL)
+		return;
+
+	switch (ctx->alg) {
+	case CRYPTO_HASH_ALG_MD5:
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		MD5Update(&ctx->u.md5, data, len);
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		SHA1Update(&ctx->u.sha1, data, len);
+		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_SHA256:
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		sha256_process(&ctx->u.sha256, data, len);
+		break;
+#endif /* CONFIG_SHA256 */
+	default:
+		break;
+	}
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+	u8 k_pad[64];
+	size_t i;
+
+	if (ctx == NULL)
+		return -2;
+
+	if (mac == NULL || len == NULL) {
+		os_free(ctx);
+		return 0;
+	}
+
+	switch (ctx->alg) {
+	case CRYPTO_HASH_ALG_MD5:
+		if (*len < 16) {
+			*len = 16;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 16;
+		MD5Final(mac, &ctx->u.md5);
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+		if (*len < 20) {
+			*len = 20;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 20;
+		SHA1Final(mac, &ctx->u.sha1);
+		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_SHA256:
+		if (*len < 32) {
+			*len = 32;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 32;
+		sha256_done(&ctx->u.sha256, mac);
+		break;
+#endif /* CONFIG_SHA256 */
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		if (*len < 16) {
+			*len = 16;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 16;
+
+		MD5Final(mac, &ctx->u.md5);
+
+		os_memcpy(k_pad, ctx->key, ctx->key_len);
+		os_memset(k_pad + ctx->key_len, 0,
+			  sizeof(k_pad) - ctx->key_len);
+		for (i = 0; i < sizeof(k_pad); i++)
+			k_pad[i] ^= 0x5c;
+		MD5Init(&ctx->u.md5);
+		MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad));
+		MD5Update(&ctx->u.md5, mac, 16);
+		MD5Final(mac, &ctx->u.md5);
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		if (*len < 20) {
+			*len = 20;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 20;
+
+		SHA1Final(mac, &ctx->u.sha1);
+
+		os_memcpy(k_pad, ctx->key, ctx->key_len);
+		os_memset(k_pad + ctx->key_len, 0,
+			  sizeof(k_pad) - ctx->key_len);
+		for (i = 0; i < sizeof(k_pad); i++)
+			k_pad[i] ^= 0x5c;
+		SHA1Init(&ctx->u.sha1);
+		SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad));
+		SHA1Update(&ctx->u.sha1, mac, 20);
+		SHA1Final(mac, &ctx->u.sha1);
+		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		if (*len < 32) {
+			*len = 32;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 32;
+
+		sha256_done(&ctx->u.sha256, mac);
+
+		os_memcpy(k_pad, ctx->key, ctx->key_len);
+		os_memset(k_pad + ctx->key_len, 0,
+			  sizeof(k_pad) - ctx->key_len);
+		for (i = 0; i < sizeof(k_pad); i++)
+			k_pad[i] ^= 0x5c;
+		sha256_init(&ctx->u.sha256);
+		sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad));
+		sha256_process(&ctx->u.sha256, mac, 32);
+		sha256_done(&ctx->u.sha256, mac);
+		break;
+#endif /* CONFIG_SHA256 */
+	default:
+		os_free(ctx);
+		return -1;
+	}
+
+	os_free(ctx);
+
+	return 0;
+}
+
+
+int crypto_global_init(void)
+{
+	return 0;
+}
+
+
+void crypto_global_deinit(void)
+{
+}
diff --git a/hostap/src/crypto/crypto_libtomcrypt.c b/hostap/src/crypto/crypto_libtomcrypt.c
new file mode 100644
index 0000000..a55edd1
--- /dev/null
+++ b/hostap/src/crypto/crypto_libtomcrypt.c
@@ -0,0 +1,726 @@
+/*
+ * WPA Supplicant / Crypto wrapper for LibTomCrypt (for internal TLSv1)
+ * Copyright (c) 2005-2006, 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 <tomcrypt.h>
+
+#include "common.h"
+#include "crypto.h"
+
+#ifndef mp_init_multi
+#define mp_init_multi                ltc_init_multi
+#define mp_clear_multi               ltc_deinit_multi
+#define mp_unsigned_bin_size(a)      ltc_mp.unsigned_size(a)
+#define mp_to_unsigned_bin(a, b)     ltc_mp.unsigned_write(a, b)
+#define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c)
+#define mp_exptmod(a,b,c,d)          ltc_mp.exptmod(a,b,c,d)
+#endif
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	hash_state md;
+	size_t i;
+
+	md4_init(&md);
+	for (i = 0; i < num_elem; i++)
+		md4_process(&md, addr[i], len[i]);
+	md4_done(&md, mac);
+	return 0;
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	u8 pkey[8], next, tmp;
+	int i;
+	symmetric_key skey;
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	des_setup(pkey, 8, 0, &skey);
+	des_ecb_encrypt(clear, cypher, &skey);
+	des_done(&skey);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	hash_state md;
+	size_t i;
+
+	md5_init(&md);
+	for (i = 0; i < num_elem; i++)
+		md5_process(&md, addr[i], len[i]);
+	md5_done(&md, mac);
+	return 0;
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	hash_state md;
+	size_t i;
+
+	sha1_init(&md);
+	for (i = 0; i < num_elem; i++)
+		sha1_process(&md, addr[i], len[i]);
+	sha1_done(&md, mac);
+	return 0;
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	symmetric_key *skey;
+	skey = os_malloc(sizeof(*skey));
+	if (skey == NULL)
+		return NULL;
+	if (aes_setup(key, len, 0, skey) != CRYPT_OK) {
+		os_free(skey);
+		return NULL;
+	}
+	return skey;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	symmetric_key *skey = ctx;
+	aes_ecb_encrypt(plain, crypt, skey);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	symmetric_key *skey = ctx;
+	aes_done(skey);
+	os_free(skey);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	symmetric_key *skey;
+	skey = os_malloc(sizeof(*skey));
+	if (skey == NULL)
+		return NULL;
+	if (aes_setup(key, len, 0, skey) != CRYPT_OK) {
+		os_free(skey);
+		return NULL;
+	}
+	return skey;
+}
+
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	symmetric_key *skey = ctx;
+	aes_ecb_encrypt(plain, (u8 *) crypt, skey);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	symmetric_key *skey = ctx;
+	aes_done(skey);
+	os_free(skey);
+}
+
+
+struct crypto_hash {
+	enum crypto_hash_alg alg;
+	int error;
+	union {
+		hash_state md;
+		hmac_state hmac;
+	} u;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len)
+{
+	struct crypto_hash *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	ctx->alg = alg;
+
+	switch (alg) {
+	case CRYPTO_HASH_ALG_MD5:
+		if (md5_init(&ctx->u.md) != CRYPT_OK)
+			goto fail;
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+		if (sha1_init(&ctx->u.md) != CRYPT_OK)
+			goto fail;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		if (hmac_init(&ctx->u.hmac, find_hash("md5"), key, key_len) !=
+		    CRYPT_OK)
+			goto fail;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		if (hmac_init(&ctx->u.hmac, find_hash("sha1"), key, key_len) !=
+		    CRYPT_OK)
+			goto fail;
+		break;
+	default:
+		goto fail;
+	}
+
+	return ctx;
+
+fail:
+	os_free(ctx);
+	return NULL;
+}
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+	if (ctx == NULL || ctx->error)
+		return;
+
+	switch (ctx->alg) {
+	case CRYPTO_HASH_ALG_MD5:
+		ctx->error = md5_process(&ctx->u.md, data, len) != CRYPT_OK;
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+		ctx->error = sha1_process(&ctx->u.md, data, len) != CRYPT_OK;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		ctx->error = hmac_process(&ctx->u.hmac, data, len) != CRYPT_OK;
+		break;
+	}
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+	int ret = 0;
+	unsigned long clen;
+
+	if (ctx == NULL)
+		return -2;
+
+	if (mac == NULL || len == NULL) {
+		os_free(ctx);
+		return 0;
+	}
+
+	if (ctx->error) {
+		os_free(ctx);
+		return -2;
+	}
+
+	switch (ctx->alg) {
+	case CRYPTO_HASH_ALG_MD5:
+		if (*len < 16) {
+			*len = 16;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 16;
+		if (md5_done(&ctx->u.md, mac) != CRYPT_OK)
+			ret = -2;
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+		if (*len < 20) {
+			*len = 20;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 20;
+		if (sha1_done(&ctx->u.md, mac) != CRYPT_OK)
+			ret = -2;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		if (*len < 20) {
+			*len = 20;
+			os_free(ctx);
+			return -1;
+		}
+		/* continue */
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		if (*len < 16) {
+			*len = 16;
+			os_free(ctx);
+			return -1;
+		}
+		clen = *len;
+		if (hmac_done(&ctx->u.hmac, mac, &clen) != CRYPT_OK) {
+			os_free(ctx);
+			return -1;
+		}
+		*len = clen;
+		break;
+	default:
+		ret = -2;
+		break;
+	}
+
+	os_free(ctx);
+
+	return ret;
+}
+
+
+struct crypto_cipher {
+	int rc4;
+	union {
+		symmetric_CBC cbc;
+		struct {
+			size_t used_bytes;
+			u8 key[16];
+			size_t keylen;
+		} rc4;
+	} u;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{	
+	struct crypto_cipher *ctx;
+	int idx, res, rc4 = 0;
+
+	switch (alg) {
+	case CRYPTO_CIPHER_ALG_AES:
+		idx = find_cipher("aes");
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		idx = find_cipher("3des");
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		idx = find_cipher("des");
+		break;
+	case CRYPTO_CIPHER_ALG_RC2:
+		idx = find_cipher("rc2");
+		break;
+	case CRYPTO_CIPHER_ALG_RC4:
+		idx = -1;
+		rc4 = 1;
+		break;
+	default:
+		return NULL;
+	}
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	if (rc4) {
+		ctx->rc4 = 1;
+		if (key_len > sizeof(ctx->u.rc4.key)) {
+			os_free(ctx);
+			return NULL;
+		}
+		ctx->u.rc4.keylen = key_len;
+		os_memcpy(ctx->u.rc4.key, key, key_len);
+	} else {
+		res = cbc_start(idx, iv, key, key_len, 0, &ctx->u.cbc);
+		if (res != CRYPT_OK) {
+			wpa_printf(MSG_DEBUG, "LibTomCrypt: Cipher start "
+				   "failed: %s", error_to_string(res));
+			os_free(ctx);
+			return NULL;
+		}
+	}
+
+	return ctx;
+}
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	int res;
+
+	if (ctx->rc4) {
+		if (plain != crypt)
+			os_memcpy(crypt, plain, len);
+		rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+			 ctx->u.rc4.used_bytes, crypt, len);
+		ctx->u.rc4.used_bytes += len;
+		return 0;
+	}
+
+	res = cbc_encrypt(plain, crypt, len, &ctx->u.cbc);
+	if (res != CRYPT_OK) {
+		wpa_printf(MSG_DEBUG, "LibTomCrypt: CBC encryption "
+			   "failed: %s", error_to_string(res));
+		return -1;
+	}
+	return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	int res;
+
+	if (ctx->rc4) {
+		if (plain != crypt)
+			os_memcpy(plain, crypt, len);
+		rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+			 ctx->u.rc4.used_bytes, plain, len);
+		ctx->u.rc4.used_bytes += len;
+		return 0;
+	}
+
+	res = cbc_decrypt(crypt, plain, len, &ctx->u.cbc);
+	if (res != CRYPT_OK) {
+		wpa_printf(MSG_DEBUG, "LibTomCrypt: CBC decryption "
+			   "failed: %s", error_to_string(res));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	if (!ctx->rc4)
+		cbc_done(&ctx->u.cbc);
+	os_free(ctx);
+}
+
+
+struct crypto_public_key {
+	rsa_key rsa;
+};
+
+struct crypto_private_key {
+	rsa_key rsa;
+};
+
+
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len)
+{
+	int res;
+	struct crypto_public_key *pk;
+
+	pk = os_zalloc(sizeof(*pk));
+	if (pk == NULL)
+		return NULL;
+
+	res = rsa_import(key, len, &pk->rsa);
+	if (res != CRYPT_OK) {
+		wpa_printf(MSG_ERROR, "LibTomCrypt: Failed to import "
+			   "public key (res=%d '%s')",
+			   res, error_to_string(res));
+		os_free(pk);
+		return NULL;
+	}
+
+	if (pk->rsa.type != PK_PUBLIC) {
+		wpa_printf(MSG_ERROR, "LibTomCrypt: Public key was not of "
+			   "correct type");
+		rsa_free(&pk->rsa);
+		os_free(pk);
+		return NULL;
+	}
+
+	return pk;
+}
+
+
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+						      size_t len,
+						      const char *passwd)
+{
+	int res;
+	struct crypto_private_key *pk;
+
+	pk = os_zalloc(sizeof(*pk));
+	if (pk == NULL)
+		return NULL;
+
+	res = rsa_import(key, len, &pk->rsa);
+	if (res != CRYPT_OK) {
+		wpa_printf(MSG_ERROR, "LibTomCrypt: Failed to import "
+			   "private key (res=%d '%s')",
+			   res, error_to_string(res));
+		os_free(pk);
+		return NULL;
+	}
+
+	if (pk->rsa.type != PK_PRIVATE) {
+		wpa_printf(MSG_ERROR, "LibTomCrypt: Private key was not of "
+			   "correct type");
+		rsa_free(&pk->rsa);
+		os_free(pk);
+		return NULL;
+	}
+
+	return pk;
+}
+
+
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+						       size_t len)
+{
+	/* No X.509 support in LibTomCrypt */
+	return NULL;
+}
+
+
+static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen,
+					   const u8 *in, size_t inlen,
+					   u8 *out, size_t *outlen)
+{
+	size_t ps_len;
+	u8 *pos;
+
+	/*
+	 * PKCS #1 v1.5, 8.1:
+	 *
+	 * EB = 00 || BT || PS || 00 || D
+	 * BT = 00 or 01 for private-key operation; 02 for public-key operation
+	 * PS = k-3-||D||; at least eight octets
+	 * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero)
+	 * k = length of modulus in octets (modlen)
+	 */
+
+	if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) {
+		wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer "
+			   "lengths (modlen=%lu outlen=%lu inlen=%lu)",
+			   __func__, (unsigned long) modlen,
+			   (unsigned long) *outlen,
+			   (unsigned long) inlen);
+		return -1;
+	}
+
+	pos = out;
+	*pos++ = 0x00;
+	*pos++ = block_type; /* BT */
+	ps_len = modlen - inlen - 3;
+	switch (block_type) {
+	case 0:
+		os_memset(pos, 0x00, ps_len);
+		pos += ps_len;
+		break;
+	case 1:
+		os_memset(pos, 0xff, ps_len);
+		pos += ps_len;
+		break;
+	case 2:
+		if (os_get_random(pos, ps_len) < 0) {
+			wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get "
+				   "random data for PS", __func__);
+			return -1;
+		}
+		while (ps_len--) {
+			if (*pos == 0x00)
+				*pos = 0x01;
+			pos++;
+		}
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type "
+			   "%d", __func__, block_type);
+		return -1;
+	}
+	*pos++ = 0x00;
+	os_memcpy(pos, in, inlen); /* D */
+
+	return 0;
+}
+
+
+static int crypto_rsa_encrypt_pkcs1(int block_type, rsa_key *key, int key_type,
+				    const u8 *in, size_t inlen,
+				    u8 *out, size_t *outlen)
+{
+	unsigned long len, modlen;
+	int res;
+
+	modlen = mp_unsigned_bin_size(key->N);
+
+	if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen,
+					    out, outlen) < 0)
+		return -1;
+
+	len = *outlen;
+	res = rsa_exptmod(out, modlen, out, &len, key_type, key);
+	if (res != CRYPT_OK) {
+		wpa_printf(MSG_DEBUG, "LibTomCrypt: rsa_exptmod failed: %s",
+			   error_to_string(res));
+		return -1;
+	}
+	*outlen = len;
+
+	return 0;
+}
+
+
+int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key,
+					const u8 *in, size_t inlen,
+					u8 *out, size_t *outlen)
+{
+	return crypto_rsa_encrypt_pkcs1(2, &key->rsa, PK_PUBLIC, in, inlen,
+					out, outlen);
+}
+
+
+int crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+				  const u8 *in, size_t inlen,
+				  u8 *out, size_t *outlen)
+{
+	return crypto_rsa_encrypt_pkcs1(1, &key->rsa, PK_PRIVATE, in, inlen,
+					out, outlen);
+}
+
+
+void crypto_public_key_free(struct crypto_public_key *key)
+{
+	if (key) {
+		rsa_free(&key->rsa);
+		os_free(key);
+	}
+}
+
+
+void crypto_private_key_free(struct crypto_private_key *key)
+{
+	if (key) {
+		rsa_free(&key->rsa);
+		os_free(key);
+	}
+}
+
+
+int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key,
+				    const u8 *crypt, size_t crypt_len,
+				    u8 *plain, size_t *plain_len)
+{
+	int res;
+	unsigned long len;
+	u8 *pos;
+
+	len = *plain_len;
+	res = rsa_exptmod(crypt, crypt_len, plain, &len, PK_PUBLIC,
+			  &key->rsa);
+	if (res != CRYPT_OK) {
+		wpa_printf(MSG_DEBUG, "LibTomCrypt: rsa_exptmod failed: %s",
+			   error_to_string(res));
+		return -1;
+	}
+
+	/*
+	 * PKCS #1 v1.5, 8.1:
+	 *
+	 * EB = 00 || BT || PS || 00 || D
+	 * BT = 01
+	 * PS = k-3-||D|| times FF
+	 * k = length of modulus in octets
+	 */
+
+	if (len < 3 + 8 + 16 /* min hash len */ ||
+	    plain[0] != 0x00 || plain[1] != 0x01 || plain[2] != 0xff) {
+		wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+			   "structure");
+		return -1;
+	}
+
+	pos = plain + 3;
+	while (pos < plain + len && *pos == 0xff)
+		pos++;
+	if (pos - plain - 2 < 8) {
+		/* PKCS #1 v1.5, 8.1: At least eight octets long PS */
+		wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature "
+			   "padding");
+		return -1;
+	}
+
+	if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) {
+		wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+			   "structure (2)");
+		return -1;
+	}
+	pos++;
+	len -= pos - plain;
+
+	/* Strip PKCS #1 header */
+	os_memmove(plain, pos, len);
+	*plain_len = len;
+
+	return 0;
+}
+
+
+int crypto_global_init(void)
+{
+	ltc_mp = tfm_desc;
+	/* TODO: only register algorithms that are really needed */
+	if (register_hash(&md4_desc) < 0 ||
+	    register_hash(&md5_desc) < 0 ||
+	    register_hash(&sha1_desc) < 0 ||
+	    register_cipher(&aes_desc) < 0 ||
+	    register_cipher(&des_desc) < 0 ||
+	    register_cipher(&des3_desc) < 0) {
+		wpa_printf(MSG_ERROR, "TLSv1: Failed to register "
+			   "hash/cipher functions");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void crypto_global_deinit(void)
+{
+}
+
+
+#ifdef CONFIG_MODEXP
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+		   const u8 *power, size_t power_len,
+		   const u8 *modulus, size_t modulus_len,
+		   u8 *result, size_t *result_len)
+{
+	void *b, *p, *m, *r;
+
+	if (mp_init_multi(&b, &p, &m, &r, NULL) != CRYPT_OK)
+		return -1;
+
+	if (mp_read_unsigned_bin(b, (u8 *) base, base_len) != CRYPT_OK ||
+	    mp_read_unsigned_bin(p, (u8 *) power, power_len) != CRYPT_OK ||
+	    mp_read_unsigned_bin(m, (u8 *) modulus, modulus_len) != CRYPT_OK)
+		goto fail;
+
+	if (mp_exptmod(b, p, m, r) != CRYPT_OK)
+		goto fail;
+
+	*result_len = mp_unsigned_bin_size(r);
+	if (mp_to_unsigned_bin(r, result) != CRYPT_OK)
+		goto fail;
+
+	mp_clear_multi(b, p, m, r, NULL);
+	return 0;
+
+fail:
+	mp_clear_multi(b, p, m, r, NULL);
+	return -1;
+}
+
+#endif /* CONFIG_MODEXP */
diff --git a/hostap/src/crypto/crypto_module_tests.c b/hostap/src/crypto/crypto_module_tests.c
new file mode 100644
index 0000000..581005d
--- /dev/null
+++ b/hostap/src/crypto/crypto_module_tests.c
@@ -0,0 +1,1702 @@
+/*
+ * crypto module tests
+ * Copyright (c) 2014-2015, 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 "crypto/aes_siv.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/aes.h"
+#include "crypto/ms_funcs.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+
+
+static int test_siv(void)
+{
+#ifdef CONFIG_MESH
+	/* RFC 5297, A.1. Deterministic Authenticated Encryption Example */
+	u8 key[] = {
+		0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
+		0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+	};
+	u8 ad[] = {
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
+	};
+	u8 plaintext[] = {
+		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+		0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee
+	};
+	u8 iv_c[] = {
+		0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f,
+		0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93,
+		0x40, 0xc0, 0x2b, 0x96, 0x90, 0xc4, 0xdc, 0x04,
+		0xda, 0xef, 0x7f, 0x6a, 0xfe, 0x5c
+	};
+	/* RFC 5297, A.2. Nonce-Based Authenticated Encryption Example */
+	u8 key_2[] = {
+		0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78,
+		0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70,
+		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+		0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+	};
+	u8 ad1_2[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+		0xde, 0xad, 0xda, 0xda, 0xde, 0xad, 0xda, 0xda,
+		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
+		0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00
+	};
+	u8 ad2_2[] = {
+		0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+		0x90, 0xa0
+	};
+	u8 nonce_2[] = {
+		0x09, 0xf9, 0x11, 0x02, 0x9d, 0x74, 0xe3, 0x5b,
+		0xd8, 0x41, 0x56, 0xc5, 0x63, 0x56, 0x88, 0xc0
+	};
+	u8 plaintext_2[] = {
+		0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
+		0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x6c, 0x61,
+		0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x74,
+		0x6f, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70,
+		0x74, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20,
+		0x53, 0x49, 0x56, 0x2d, 0x41, 0x45, 0x53
+	};
+	u8 iv_c_2[] = {
+		0x7b, 0xdb, 0x6e, 0x3b, 0x43, 0x26, 0x67, 0xeb,
+		0x06, 0xf4, 0xd1, 0x4b, 0xff, 0x2f, 0xbd, 0x0f,
+		0xcb, 0x90, 0x0f, 0x2f, 0xdd, 0xbe, 0x40, 0x43,
+		0x26, 0x60, 0x19, 0x65, 0xc8, 0x89, 0xbf, 0x17,
+		0xdb, 0xa7, 0x7c, 0xeb, 0x09, 0x4f, 0xa6, 0x63,
+		0xb7, 0xa3, 0xf7, 0x48, 0xba, 0x8a, 0xf8, 0x29,
+		0xea, 0x64, 0xad, 0x54, 0x4a, 0x27, 0x2e, 0x9c,
+		0x48, 0x5b, 0x62, 0xa3, 0xfd, 0x5c, 0x0d
+	};
+	u8 out[2 * AES_BLOCK_SIZE + sizeof(plaintext_2)];
+	const u8 *addr[3];
+	size_t len[3];
+
+	/* RFC 5297, A.1. Deterministic Authenticated Encryption Example */
+	addr[0] = ad;
+	len[0] = sizeof(ad);
+
+	if (aes_siv_encrypt(key, plaintext, sizeof(plaintext),
+			    1, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, iv_c, sizeof(iv_c)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode encryption returned invalid cipher text");
+		return 1;
+	}
+
+	if (aes_siv_decrypt(key, iv_c, sizeof(iv_c), 1, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, plaintext, sizeof(plaintext)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode decryption returned invalid plain text");
+		return 1;
+	}
+
+	/* RFC 5297, A.2. Nonce-Based Authenticated Encryption Example */
+	addr[0] = ad1_2;
+	len[0] = sizeof(ad1_2);
+	addr[1] = ad2_2;
+	len[1] = sizeof(ad2_2);
+	addr[2] = nonce_2;
+	len[2] = sizeof(nonce_2);
+
+	if (aes_siv_encrypt(key_2, plaintext_2, sizeof(plaintext_2),
+			    3, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, iv_c_2, sizeof(iv_c_2)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode encryption returned invalid cipher text");
+		return 1;
+	}
+
+	if (aes_siv_decrypt(key_2, iv_c_2, sizeof(iv_c_2), 3, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, plaintext_2, sizeof(plaintext_2)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode decryption returned invalid plain text");
+		return 1;
+	}
+
+	wpa_printf(MSG_INFO, "AES-SIV test cases passed");
+#endif /* CONFIG_MESH */
+
+	return 0;
+}
+
+
+/* OMAC1 AES-128 test vectors from
+ * http://csrc.nist.gov/CryptoToolkit/modes/proposedmodes/omac/omac-ad.pdf
+ * which are same as the examples from NIST SP800-38B
+ * http://csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf
+ */
+
+struct omac1_test_vector {
+	u8 k[16];
+	u8 msg[64];
+	int msg_len;
+	u8 tag[16];
+};
+
+static const struct omac1_test_vector omac1_test_vectors[] =
+{
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ },
+		0,
+		{ 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28,
+		  0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 }
+	},
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		  0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a},
+		16,
+		{ 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44,
+		  0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c }
+	},
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		  0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+		  0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+		  0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+		  0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 },
+		40,
+		{ 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30,
+		  0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 }
+	},
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		  0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+		  0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+		  0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+		  0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+		  0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+		  0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+		  0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
+		64,
+		{ 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92,
+		  0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe }
+	},
+};
+
+
+static int test_omac1_vector(const struct omac1_test_vector *tv,
+			     unsigned int i)
+{
+	u8 key[] = {
+		0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+	};
+	u8 msg[] = { 0x12, 0x34, 0x56 };
+	u8 result[24], result2[24];
+	const u8 *addr[3];
+	size_t len[3];
+
+	if (omac1_aes_128(tv->k, tv->msg, tv->msg_len, result) ||
+	    os_memcmp(result, tv->tag, 16) != 0) {
+		wpa_printf(MSG_ERROR, "OMAC1-AES-128 test vector %u failed", i);
+		return 1;
+	}
+
+	if (tv->msg_len > 1) {
+
+		addr[0] = tv->msg;
+		len[0] = 1;
+		addr[1] = tv->msg + 1;
+		len[1] = tv->msg_len - 1;
+
+		if (omac1_aes_128_vector(tv->k, 2, addr, len, result) ||
+		    os_memcmp(result, tv->tag, 16) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "OMAC1-AES-128(vector) test vector %u failed",
+				   i);
+			return 1;
+		}
+
+		addr[0] = tv->msg;
+		len[0] = tv->msg_len - 2;
+		addr[1] = tv->msg + tv->msg_len - 2;
+		len[1] = 1;
+		addr[2] = tv->msg + tv->msg_len - 1;
+		len[2] = 1;
+
+		if (omac1_aes_128_vector(tv->k, 3, addr, len, result) ||
+		    os_memcmp(result, tv->tag, 16) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "OMAC1-AES-128(vector2) test vector %u failed",
+				   i);
+			return 1;
+		}
+	}
+
+	addr[0] = &msg[0];
+	len[0] = 1;
+	addr[1] = &msg[1];
+	len[1] = 1;
+	addr[2] = &msg[2];
+	len[2] = 1;
+	if (omac1_aes_128(key, msg, sizeof(msg), result) ||
+	    omac1_aes_128_vector(key, 3, addr, len, result2) ||
+	    os_memcmp(result, result2, 16) != 0) {
+		wpa_printf(MSG_ERROR, "OMAC1-AES-128 short test mismatch");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int test_omac1(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(omac1_test_vectors); i++) {
+		if (test_omac1_vector(&omac1_test_vectors[i], i))
+			return 1;
+	}
+
+	wpa_printf(MSG_INFO, "OMAC1-AES-128 test cases passed");
+
+	return 0;
+}
+
+
+static int test_eax(void)
+{
+#ifdef EAP_PSK
+	u8 msg[] = { 0xF7, 0xFB };
+	u8 key[] = { 0x91, 0x94, 0x5D, 0x3F, 0x4D, 0xCB, 0xEE, 0x0B,
+		     0xF4, 0x5E, 0xF5, 0x22, 0x55, 0xF0, 0x95, 0xA4 };
+	u8 nonce[] = { 0xBE, 0xCA, 0xF0, 0x43, 0xB0, 0xA2, 0x3D, 0x84,
+		       0x31, 0x94, 0xBA, 0x97, 0x2C, 0x66, 0xDE, 0xBD };
+	u8 hdr[] = { 0xFA, 0x3B, 0xFD, 0x48, 0x06, 0xEB, 0x53, 0xFA };
+	u8 cipher[] = { 0x19, 0xDD, 0x5C, 0x4C, 0x93, 0x31, 0x04, 0x9D,
+			0x0B, 0xDA, 0xB0, 0x27, 0x74, 0x08, 0xF6, 0x79,
+			0x67, 0xE5 };
+	u8 data[sizeof(msg)], tag[AES_BLOCK_SIZE];
+
+	os_memcpy(data, msg, sizeof(msg));
+	if (aes_128_eax_encrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr),
+				data, sizeof(data), tag)) {
+		wpa_printf(MSG_ERROR, "AES-128 EAX mode encryption failed");
+		return 1;
+	}
+	if (os_memcmp(data, cipher, sizeof(data)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-128 EAX mode encryption returned invalid cipher text");
+		return 1;
+	}
+	if (os_memcmp(tag, cipher + sizeof(data), AES_BLOCK_SIZE) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-128 EAX mode encryption returned invalid tag");
+		return 1;
+	}
+
+	if (aes_128_eax_decrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr),
+				data, sizeof(data), tag)) {
+		wpa_printf(MSG_ERROR, "AES-128 EAX mode decryption failed");
+		return 1;
+	}
+	if (os_memcmp(data, msg, sizeof(data)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-128 EAX mode decryption returned invalid plain text");
+		return 1;
+	}
+
+	wpa_printf(MSG_INFO, "AES-128 EAX mode test cases passed");
+#endif /* EAP_PSK */
+
+	return 0;
+}
+
+
+static int test_cbc(void)
+{
+	struct cbc_test_vector {
+		u8 key[16];
+		u8 iv[16];
+		u8 plain[32];
+		u8 cipher[32];
+		size_t len;
+	} vectors[] = {
+		{
+			{ 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b,
+			  0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 },
+			{ 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30,
+			  0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 },
+			"Single block msg",
+			{ 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8,
+			  0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a },
+			16
+		},
+		{
+			{ 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0,
+			  0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a },
+			{ 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28,
+			  0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 },
+			{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+			  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+			  0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f },
+			{ 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a,
+			  0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a,
+			  0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9,
+			  0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1 },
+			32
+		}
+	};
+	int ret = 0;
+	u8 *buf;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vectors); i++) {
+		struct cbc_test_vector *tv = &vectors[i];
+
+		buf = os_malloc(tv->len);
+		if (buf == NULL) {
+			ret++;
+			break;
+		}
+
+		os_memcpy(buf, tv->plain, tv->len);
+		if (aes_128_cbc_encrypt(tv->key, tv->iv, buf, tv->len) ||
+		    os_memcmp(buf, tv->cipher, tv->len) != 0) {
+			wpa_printf(MSG_ERROR, "AES-CBC encrypt %d failed", i);
+			ret++;
+		}
+
+		os_memcpy(buf, tv->cipher, tv->len);
+		if (aes_128_cbc_decrypt(tv->key, tv->iv, buf, tv->len) ||
+		    os_memcmp(buf, tv->plain, tv->len) != 0) {
+			wpa_printf(MSG_ERROR, "AES-CBC decrypt %d failed", i);
+			ret++;
+		}
+
+		os_free(buf);
+	}
+
+	return ret;
+}
+
+
+static int test_ecb(void)
+{
+#ifdef EAP_PSK
+	struct ecb_test_vector {
+		char *key;
+		char *plaintext;
+		char *ciphertext;
+	} vectors[] = {
+		/* CAVS 11.1 - ECBGFSbox128.rsp */
+		{
+			"00000000000000000000000000000000",
+			"f34481ec3cc627bacd5dc3fb08f273e6",
+			"0336763e966d92595a567cc9ce537f5e"
+		},
+		{
+			"00000000000000000000000000000000",
+			"9798c4640bad75c7c3227db910174e72",
+			"a9a1631bf4996954ebc093957b234589"
+		},
+		{
+			"00000000000000000000000000000000",
+			"96ab5c2ff612d9dfaae8c31f30c42168",
+			"ff4f8391a6a40ca5b25d23bedd44a597"
+		},
+		{
+			"00000000000000000000000000000000",
+			"6a118a874519e64e9963798a503f1d35",
+			"dc43be40be0e53712f7e2bf5ca707209"
+		},
+		{
+			"00000000000000000000000000000000",
+			"cb9fceec81286ca3e989bd979b0cb284",
+			"92beedab1895a94faa69b632e5cc47ce"
+		},
+		{
+			"00000000000000000000000000000000",
+			"b26aeb1874e47ca8358ff22378f09144",
+			"459264f4798f6a78bacb89c15ed3d601"
+		},
+		{
+			"00000000000000000000000000000000",
+			"58c8e00b2631686d54eab84b91f0aca1",
+			"08a4e2efec8a8e3312ca7460b9040bbf"
+		},
+		/* CAVS 11.1 - ECBKeySbox128.rsp */
+		{
+			"10a58869d74be5a374cf867cfb473859",
+			"00000000000000000000000000000000",
+			"6d251e6944b051e04eaa6fb4dbf78465"
+		},
+		{
+			"caea65cdbb75e9169ecd22ebe6e54675",
+			"00000000000000000000000000000000",
+			"6e29201190152df4ee058139def610bb",
+		}
+	};
+	int ret = 0;
+	unsigned int i;
+	u8 key[16], plain[16], cipher[16], out[16];
+
+	for (i = 0; i < ARRAY_SIZE(vectors); i++) {
+		struct ecb_test_vector *tv = &vectors[i];
+
+		if (hexstr2bin(tv->key, key, sizeof(key)) ||
+		    hexstr2bin(tv->plaintext, plain, sizeof(plain)) ||
+		    hexstr2bin(tv->ciphertext, cipher, sizeof(cipher))) {
+			wpa_printf(MSG_ERROR, "Invalid AES-ECB test vector %u",
+				   i);
+			ret++;
+			continue;
+		}
+
+		if (aes_128_encrypt_block(key, plain, out) < 0 ||
+		    os_memcmp(out, cipher, 16) != 0) {
+			wpa_printf(MSG_ERROR, "AES-ECB encrypt %u failed", i);
+			ret++;
+		}
+	}
+
+	if (!ret)
+		wpa_printf(MSG_INFO, "AES ECB mode test cases passed");
+
+	return ret;
+#endif /* EAP_PSK */
+
+	return 0;
+}
+
+
+static int test_key_wrap(void)
+{
+	int ret = 0;
+
+	/* RFC 3394 - Test vector 4.1 */
+	u8 kek41[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+	};
+	u8 plain41[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+	};
+	u8 crypt41[] = {
+		0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47,
+		0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82,
+		0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5
+	};
+#ifndef CONFIG_BORINGSSL
+	/* RFC 3394 - Test vector 4.2 */
+	u8 kek42[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
+	};
+	u8 plain42[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+	};
+	u8 crypt42[] = {
+		0x96, 0x77, 0x8B, 0x25, 0xAE, 0x6C, 0xA4, 0x35,
+		0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2,
+		0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D
+	};
+#endif /* CONFIG_BORINGSSL */
+	/* RFC 3394 - Test vector 4.3 */
+	u8 kek43[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+	};
+	u8 plain43[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+	};
+	u8 crypt43[] = {
+		0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2,
+		0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A,
+		0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7,
+	};
+#ifndef CONFIG_BORINGSSL
+	/* RFC 3394 - Test vector 4.4 */
+	u8 kek44[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
+	};
+	u8 plain44[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+	};
+	u8 crypt44[] = {
+		0x03, 0x1D, 0x33, 0x26, 0x4E, 0x15, 0xD3, 0x32,
+		0x68, 0xF2, 0x4E, 0xC2, 0x60, 0x74, 0x3E, 0xDC,
+		0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93,
+		0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2
+	};
+#endif /* CONFIG_BORINGSSL */
+	/* RFC 3394 - Test vector 4.5 */
+	u8 kek45[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+	};
+	u8 plain45[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+	};
+	u8 crypt45[] = {
+		0xA8, 0xF9, 0xBC, 0x16, 0x12, 0xC6, 0x8B, 0x3F,
+		0xF6, 0xE6, 0xF4, 0xFB, 0xE3, 0x0E, 0x71, 0xE4,
+		0x76, 0x9C, 0x8B, 0x80, 0xA3, 0x2C, 0xB8, 0x95,
+		0x8C, 0xD5, 0xD1, 0x7D, 0x6B, 0x25, 0x4D, 0xA1,
+	};
+	/* RFC 3394 - Test vector 4.6 */
+	u8 kek46[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+	};
+	u8 plain46[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
+	};
+	u8 crypt46[] = {
+		0x28, 0xC9, 0xF4, 0x04, 0xC4, 0xB8, 0x10, 0xF4,
+		0xCB, 0xCC, 0xB3, 0x5C, 0xFB, 0x87, 0xF8, 0x26,
+		0x3F, 0x57, 0x86, 0xE2, 0xD8, 0x0E, 0xD3, 0x26,
+		0xCB, 0xC7, 0xF0, 0xE7, 0x1A, 0x99, 0xF4, 0x3B,
+		0xFB, 0x98, 0x8B, 0x9B, 0x7A, 0x02, 0xDD, 0x21
+	};
+	u8 result[40];
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.1");
+	if (aes_wrap(kek41, sizeof(kek41), sizeof(plain41) / 8, plain41,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-128 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt41, sizeof(crypt41)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-128 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek41, sizeof(kek41), sizeof(plain41) / 8, crypt41,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-128 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain41, sizeof(plain41)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-128 failed");
+		ret++;
+	}
+
+#ifndef CONFIG_BORINGSSL
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.2");
+	if (aes_wrap(kek42, sizeof(kek42), sizeof(plain42) / 8, plain42,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt42, sizeof(crypt42)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek42, sizeof(kek42), sizeof(plain42) / 8, crypt42,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain42, sizeof(plain42)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed");
+		ret++;
+	}
+#endif /* CONFIG_BORINGSSL */
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.3");
+	if (aes_wrap(kek43, sizeof(kek43), sizeof(plain43) / 8, plain43,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt43, sizeof(crypt43)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek43, sizeof(kek43), sizeof(plain43) / 8, crypt43,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain43, sizeof(plain43)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
+		ret++;
+	}
+
+#ifndef CONFIG_BORINGSSL
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.4");
+	if (aes_wrap(kek44, sizeof(kek44), sizeof(plain44) / 8, plain44,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt44, sizeof(crypt44)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek44, sizeof(kek44), sizeof(plain44) / 8, crypt44,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain44, sizeof(plain44)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed");
+		ret++;
+	}
+#endif /* CONFIG_BORINGSSL */
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.5");
+	if (aes_wrap(kek45, sizeof(kek45), sizeof(plain45) / 8, plain45,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt45, sizeof(crypt45)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek45, sizeof(kek45), sizeof(plain45) / 8, crypt45,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain45, sizeof(plain45)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
+		ret++;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.6");
+	if (aes_wrap(kek46, sizeof(kek46), sizeof(plain46) / 8, plain46,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt46, sizeof(crypt46)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek46, sizeof(kek46), sizeof(plain46) / 8, crypt46,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain46, sizeof(plain46)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
+		ret++;
+	}
+
+	if (!ret)
+		wpa_printf(MSG_INFO, "AES key wrap/unwrap test cases passed");
+
+	return ret;
+}
+
+
+static int test_md5(void)
+{
+#ifndef CONFIG_FIPS
+	struct {
+		char *data;
+		char *hash;
+	} tests[] = {
+		{
+			"",
+			"\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04"
+			"\xe9\x80\x09\x98\xec\xf8\x42\x7e"
+		},
+		{
+			"a",
+			"\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8"
+			"\x31\xc3\x99\xe2\x69\x77\x26\x61"
+		},
+		{
+			"abc",
+			"\x90\x01\x50\x98\x3c\xd2\x4f\xb0"
+			"\xd6\x96\x3f\x7d\x28\xe1\x7f\x72"
+		},
+		{
+			"message digest",
+			"\xf9\x6b\x69\x7d\x7c\xb7\x93\x8d"
+			"\x52\x5a\x2f\x31\xaa\xf1\x61\xd0"
+		},
+		{
+			"abcdefghijklmnopqrstuvwxyz",
+			"\xc3\xfc\xd3\xd7\x61\x92\xe4\x00"
+			"\x7d\xfb\x49\x6c\xca\x67\xe1\x3b"
+		},
+		{
+			"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+			"0123456789",
+			"\xd1\x74\xab\x98\xd2\x77\xd9\xf5"
+			"\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f"
+		},
+		{
+			"12345678901234567890123456789012345678901234567890"
+			"123456789012345678901234567890",
+			"\x57\xed\xf4\xa2\x2b\xe3\xc9\x55"
+			"\xac\x49\xda\x2e\x21\x07\xb6\x7a"
+		}
+	};
+	unsigned int i;
+	u8 hash[16];
+	const u8 *addr[2];
+	size_t len[2];
+	int errors = 0;
+
+	for (i = 0; i < ARRAY_SIZE(tests); i++) {
+		wpa_printf(MSG_INFO, "MD5 test case %d", i);
+
+		addr[0] = (u8 *) tests[i].data;
+		len[0] = strlen(tests[i].data);
+		if (md5_vector(1, addr, len, hash) < 0 ||
+		    os_memcmp(hash, tests[i].hash, 16) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		if (len[0]) {
+			addr[0] = (u8 *) tests[i].data;
+			len[0] = strlen(tests[i].data);
+			addr[1] = (u8 *) tests[i].data + 1;
+			len[1] = strlen(tests[i].data) - 1;
+			if (md5_vector(1, addr, len, hash) < 0 ||
+			    os_memcmp(hash, tests[i].hash, 16) != 0) {
+				wpa_printf(MSG_INFO, " FAIL");
+				errors++;
+			} else
+				wpa_printf(MSG_INFO, " OK");
+		}
+	}
+
+	if (!errors)
+		wpa_printf(MSG_INFO, "MD5 test cases passed");
+
+	return errors;
+#else /* CONFIG_FIPS */
+	wpa_printf(MSG_INFO, "MD5 test cases skipped due to CONFIG_FIPS");
+	return 0;
+#endif /* CONFIG_FIPS */
+}
+
+
+static int test_eap_fast(void)
+{
+#ifdef EAP_FAST
+	/* RFC 4851, Appendix B.1 */
+	const u8 pac_key[] = {
+		0x0B, 0x97, 0x39, 0x0F, 0x37, 0x51, 0x78, 0x09,
+		0x81, 0x1E, 0xFD, 0x9C, 0x6E, 0x65, 0x94, 0x2B,
+		0x63, 0x2C, 0xE9, 0x53, 0x89, 0x38, 0x08, 0xBA,
+		0x36, 0x0B, 0x03, 0x7C, 0xD1, 0x85, 0xE4, 0x14
+	};
+	const u8 seed[] = {
+		0x3F, 0xFB, 0x11, 0xC4, 0x6C, 0xBF, 0xA5, 0x7A,
+		0x54, 0x40, 0xDA, 0xE8, 0x22, 0xD3, 0x11, 0xD3,
+		0xF7, 0x6D, 0xE4, 0x1D, 0xD9, 0x33, 0xE5, 0x93,
+		0x70, 0x97, 0xEB, 0xA9, 0xB3, 0x66, 0xF4, 0x2A,
+		0x00, 0x00, 0x00, 0x02, 0x6A, 0x66, 0x43, 0x2A,
+		0x8D, 0x14, 0x43, 0x2C, 0xEC, 0x58, 0x2D, 0x2F,
+		0xC7, 0x9C, 0x33, 0x64, 0xBA, 0x04, 0xAD, 0x3A,
+		0x52, 0x54, 0xD6, 0xA5, 0x79, 0xAD, 0x1E, 0x00
+	};
+	const u8 master_secret[] = {
+		0x4A, 0x1A, 0x51, 0x2C, 0x01, 0x60, 0xBC, 0x02,
+		0x3C, 0xCF, 0xBC, 0x83, 0x3F, 0x03, 0xBC, 0x64,
+		0x88, 0xC1, 0x31, 0x2F, 0x0B, 0xA9, 0xA2, 0x77,
+		0x16, 0xA8, 0xD8, 0xE8, 0xBD, 0xC9, 0xD2, 0x29,
+		0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27,
+		0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2
+	};
+#ifndef CONFIG_FIPS
+	const u8 key_block[] = {
+		0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74,
+		0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35,
+		0xDF, 0xFB, 0xC8, 0x1E, 0x9C, 0x24, 0x9C, 0x8B,
+		0x0E, 0xC3, 0x1D, 0x72, 0xC8, 0x84, 0x9D, 0x57,
+		0x48, 0x51, 0x2E, 0x45, 0x97, 0x6C, 0x88, 0x70,
+		0xBE, 0x5F, 0x01, 0xD3, 0x64, 0xE7, 0x4C, 0xBB,
+		0x11, 0x24, 0xE3, 0x49, 0xE2, 0x3B, 0xCD, 0xEF,
+		0x7A, 0xB3, 0x05, 0x39, 0x5D, 0x64, 0x8A, 0x44,
+		0x11, 0xB6, 0x69, 0x88, 0x34, 0x2E, 0x8E, 0x29,
+		0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05,
+		0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96,
+		0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84,
+		0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98,
+		0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71
+	};
+#endif /* CONFIG_FIPS */
+	const u8 sks[] = {
+		0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05,
+		0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96,
+		0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84,
+		0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98,
+		0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71
+	};
+	const u8 isk[] = {
+		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
+	};
+	const u8 imck[] = {
+		0x16, 0x15, 0x3C, 0x3F, 0x21, 0x55, 0xEF, 0xD9,
+		0x7F, 0x34, 0xAE, 0xC8, 0x1A, 0x4E, 0x66, 0x80,
+		0x4C, 0xC3, 0x76, 0xF2, 0x8A, 0xA9, 0x6F, 0x96,
+		0xC2, 0x54, 0x5F, 0x8C, 0xAB, 0x65, 0x02, 0xE1,
+		0x18, 0x40, 0x7B, 0x56, 0xBE, 0xEA, 0xA7, 0xC5,
+		0x76, 0x5D, 0x8F, 0x0B, 0xC5, 0x07, 0xC6, 0xB9,
+		0x04, 0xD0, 0x69, 0x56, 0x72, 0x8B, 0x6B, 0xB8,
+		0x15, 0xEC, 0x57, 0x7B
+	};
+	const u8 msk[] = {
+		0x4D, 0x83, 0xA9, 0xBE, 0x6F, 0x8A, 0x74, 0xED,
+		0x6A, 0x02, 0x66, 0x0A, 0x63, 0x4D, 0x2C, 0x33,
+		0xC2, 0xDA, 0x60, 0x15, 0xC6, 0x37, 0x04, 0x51,
+		0x90, 0x38, 0x63, 0xDA, 0x54, 0x3E, 0x14, 0xB9,
+		0x27, 0x99, 0x18, 0x1E, 0x07, 0xBF, 0x0F, 0x5A,
+		0x5E, 0x3C, 0x32, 0x93, 0x80, 0x8C, 0x6C, 0x49,
+		0x67, 0xED, 0x24, 0xFE, 0x45, 0x40, 0xA0, 0x59,
+		0x5E, 0x37, 0xC2, 0xE9, 0xD0, 0x5D, 0x0A, 0xE3
+	};
+	const u8 emsk[] = {
+		0x3A, 0xD4, 0xAB, 0xDB, 0x76, 0xB2, 0x7F, 0x3B,
+		0xEA, 0x32, 0x2C, 0x2B, 0x74, 0xF4, 0x28, 0x55,
+		0xEF, 0x2D, 0xBA, 0x78, 0xC9, 0x57, 0x2F, 0x0D,
+		0x06, 0xCD, 0x51, 0x7C, 0x20, 0x93, 0x98, 0xA9,
+		0x76, 0xEA, 0x70, 0x21, 0xD7, 0x0E, 0x25, 0x54,
+		0x97, 0xED, 0xB2, 0x8A, 0xF6, 0xED, 0xFD, 0x0A,
+		0x2A, 0xE7, 0xA1, 0x58, 0x90, 0x10, 0x50, 0x44,
+		0xB3, 0x82, 0x85, 0xDB, 0x06, 0x14, 0xD2, 0xF9
+	};
+	/* RFC 4851, Appendix B.2 */
+	u8 tlv[] = {
+		0x80, 0x0C, 0x00, 0x38, 0x00, 0x01, 0x01, 0x00,
+		0xD8, 0x6A, 0x8C, 0x68, 0x3C, 0x32, 0x31, 0xA8,
+		0x56, 0x63, 0xB6, 0x40, 0x21, 0xFE, 0x21, 0x14,
+		0x4E, 0xE7, 0x54, 0x20, 0x79, 0x2D, 0x42, 0x62,
+		0xC9, 0xBF, 0x53, 0x7F, 0x54, 0xFD, 0xAC, 0x58,
+		0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF,
+		0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC,
+		0x05, 0xC5, 0x5B, 0xB7
+	};
+	const u8 compound_mac[] = {
+		0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF,
+		0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC,
+		0x05, 0xC5, 0x5B, 0xB7
+	};
+	u8 buf[512];
+	const u8 *simck, *cmk;
+	int errors = 0;
+
+	wpa_printf(MSG_INFO, "EAP-FAST test cases");
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / master_secret");
+	if (sha1_t_prf(pac_key, sizeof(pac_key),
+		       "PAC to master secret label hash",
+		       seed, sizeof(seed), buf, sizeof(master_secret)) < 0 ||
+	    os_memcmp(master_secret, buf, sizeof(master_secret)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+#ifndef CONFIG_FIPS
+	wpa_printf(MSG_INFO, "- PRF (TLS, SHA1/MD5) test case / key_block");
+	if (tls_prf_sha1_md5(master_secret, sizeof(master_secret),
+			     "key expansion", seed, sizeof(seed),
+			     buf, sizeof(key_block)) ||
+	    os_memcmp(key_block, buf, sizeof(key_block)) != 0) {
+		wpa_printf(MSG_INFO, "PRF test - FAILED!");
+		errors++;
+	}
+#endif /* CONFIG_FIPS */
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / IMCK");
+	if (sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys",
+		       isk, sizeof(isk), buf, sizeof(imck)) < 0 ||
+	    os_memcmp(imck, buf, sizeof(imck)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+	simck = imck;
+	cmk = imck + 40;
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / MSK");
+	if (sha1_t_prf(simck, 40, "Session Key Generating Function",
+		       (u8 *) "", 0, buf, sizeof(msk)) < 0 ||
+	    os_memcmp(msk, buf, sizeof(msk)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / EMSK");
+	if (sha1_t_prf(simck, 40, "Extended Session Key Generating Function",
+		       (u8 *) "", 0, buf, sizeof(msk)) < 0 ||
+	    os_memcmp(emsk, buf, sizeof(emsk)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+	wpa_printf(MSG_INFO, "- Compound MAC test case");
+	os_memset(tlv + sizeof(tlv) - 20, 0, 20);
+	if (hmac_sha1(cmk, 20, tlv, sizeof(tlv), tlv + sizeof(tlv) - 20) < 0 ||
+	    os_memcmp(tlv + sizeof(tlv) - 20, compound_mac,
+		      sizeof(compound_mac)) != 0) {
+		wpa_printf(MSG_INFO, "Compound MAC test - FAILED!");
+		errors++;
+	}
+
+	return errors;
+#else /* EAP_FAST */
+	return 0;
+#endif /* EAP_FAST */
+}
+
+
+static const u8 key0[] =
+{
+	0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+	0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+	0x0b, 0x0b, 0x0b, 0x0b
+};
+static const u8 data0[] = "Hi There";
+static const u8 prf0[] =
+{
+	0xbc, 0xd4, 0xc6, 0x50, 0xb3, 0x0b, 0x96, 0x84,
+	0x95, 0x18, 0x29, 0xe0, 0xd7, 0x5f, 0x9d, 0x54,
+	0xb8, 0x62, 0x17, 0x5e, 0xd9, 0xf0, 0x06, 0x06,
+	0xe1, 0x7d, 0x8d, 0xa3, 0x54, 0x02, 0xff, 0xee,
+	0x75, 0xdf, 0x78, 0xc3, 0xd3, 0x1e, 0x0f, 0x88,
+	0x9f, 0x01, 0x21, 0x20, 0xc0, 0x86, 0x2b, 0xeb,
+	0x67, 0x75, 0x3e, 0x74, 0x39, 0xae, 0x24, 0x2e,
+	0xdb, 0x83, 0x73, 0x69, 0x83, 0x56, 0xcf, 0x5a
+};
+
+static const u8 key1[] = "Jefe";
+static const u8 data1[] = "what do ya want for nothing?";
+static const u8 prf1[] =
+{
+	0x51, 0xf4, 0xde, 0x5b, 0x33, 0xf2, 0x49, 0xad,
+	0xf8, 0x1a, 0xeb, 0x71, 0x3a, 0x3c, 0x20, 0xf4,
+	0xfe, 0x63, 0x14, 0x46, 0xfa, 0xbd, 0xfa, 0x58,
+	0x24, 0x47, 0x59, 0xae, 0x58, 0xef, 0x90, 0x09,
+	0xa9, 0x9a, 0xbf, 0x4e, 0xac, 0x2c, 0xa5, 0xfa,
+	0x87, 0xe6, 0x92, 0xc4, 0x40, 0xeb, 0x40, 0x02,
+	0x3e, 0x7b, 0xab, 0xb2, 0x06, 0xd6, 0x1d, 0xe7,
+	0xb9, 0x2f, 0x41, 0x52, 0x90, 0x92, 0xb8, 0xfc
+};
+
+
+static const u8 key2[] =
+{
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa
+};
+static const u8 data2[] =
+{
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd
+};
+static const u8 prf2[] =
+{
+	0xe1, 0xac, 0x54, 0x6e, 0xc4, 0xcb, 0x63, 0x6f,
+	0x99, 0x76, 0x48, 0x7b, 0xe5, 0xc8, 0x6b, 0xe1,
+	0x7a, 0x02, 0x52, 0xca, 0x5d, 0x8d, 0x8d, 0xf1,
+	0x2c, 0xfb, 0x04, 0x73, 0x52, 0x52, 0x49, 0xce,
+	0x9d, 0xd8, 0xd1, 0x77, 0xea, 0xd7, 0x10, 0xbc,
+	0x9b, 0x59, 0x05, 0x47, 0x23, 0x91, 0x07, 0xae,
+	0xf7, 0xb4, 0xab, 0xd4, 0x3d, 0x87, 0xf0, 0xa6,
+	0x8f, 0x1c, 0xbd, 0x9e, 0x2b, 0x6f, 0x76, 0x07
+};
+
+
+struct passphrase_test {
+	char *passphrase;
+	char *ssid;
+	char psk[32];
+};
+
+static const struct passphrase_test passphrase_tests[] =
+{
+	{
+		"password",
+		"IEEE",
+		{
+			0xf4, 0x2c, 0x6f, 0xc5, 0x2d, 0xf0, 0xeb, 0xef,
+			0x9e, 0xbb, 0x4b, 0x90, 0xb3, 0x8a, 0x5f, 0x90,
+			0x2e, 0x83, 0xfe, 0x1b, 0x13, 0x5a, 0x70, 0xe2,
+			0x3a, 0xed, 0x76, 0x2e, 0x97, 0x10, 0xa1, 0x2e
+		}
+	},
+	{
+		"ThisIsAPassword",
+		"ThisIsASSID",
+		{
+			0x0d, 0xc0, 0xd6, 0xeb, 0x90, 0x55, 0x5e, 0xd6,
+			0x41, 0x97, 0x56, 0xb9, 0xa1, 0x5e, 0xc3, 0xe3,
+			0x20, 0x9b, 0x63, 0xdf, 0x70, 0x7d, 0xd5, 0x08,
+			0xd1, 0x45, 0x81, 0xf8, 0x98, 0x27, 0x21, 0xaf
+		}
+	},
+	{
+		"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+		"ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
+		{
+			0xbe, 0xcb, 0x93, 0x86, 0x6b, 0xb8, 0xc3, 0x83,
+			0x2c, 0xb7, 0x77, 0xc2, 0xf5, 0x59, 0x80, 0x7c,
+			0x8c, 0x59, 0xaf, 0xcb, 0x6e, 0xae, 0x73, 0x48,
+			0x85, 0x00, 0x13, 0x00, 0xa9, 0x81, 0xcc, 0x62
+		}
+	},
+};
+
+#define NUM_PASSPHRASE_TESTS ARRAY_SIZE(passphrase_tests)
+
+
+struct rfc6070_test {
+	char *p;
+	char *s;
+	int c;
+	char dk[32];
+	size_t dk_len;
+};
+
+static const struct rfc6070_test rfc6070_tests[] =
+{
+	{
+		"password",
+		"salt",
+		1,
+		{
+			0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71,
+			0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06,
+			0x2f, 0xe0, 0x37, 0xa6
+		},
+		20
+	},
+	{
+		"password",
+		"salt",
+		2,
+		{
+			0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c,
+			0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0,
+			0xd8, 0xde, 0x89, 0x57
+		},
+		20
+	},
+	{
+		"password",
+		"salt",
+		4096,
+		{
+			0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a,
+			0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0,
+			0x65, 0xa4, 0x29, 0xc1
+		},
+		20
+	},
+#if 0 /* This takes quite long to derive.. */
+	{
+		"password",
+		"salt",
+		16777216,
+		{
+			0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4,
+			0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c,
+			0x26, 0x34, 0xe9, 0x84
+		},
+		20
+	},
+#endif
+	{
+		"passwordPASSWORDpassword",
+		"saltSALTsaltSALTsaltSALTsaltSALTsalt",
+		4096,
+		{
+			0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b,
+			0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a,
+			0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70,
+			0x38
+		},
+		25
+	},
+#if 0 /* \0 not currently supported in passphrase parameters.. */
+	{
+		"pass\0word",
+		"sa\0lt",
+		4096,
+		{
+			0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d,
+			0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3
+		},
+		16
+	},
+#endif
+};
+
+#define NUM_RFC6070_TESTS ARRAY_SIZE(rfc6070_tests)
+
+
+static int test_sha1(void)
+{
+	u8 res[512];
+	int ret = 0;
+	unsigned int i;
+
+	wpa_printf(MSG_INFO, "PRF-SHA1 test cases:");
+
+	if (sha1_prf(key0, sizeof(key0), "prefix", data0, sizeof(data0) - 1,
+		     res, sizeof(prf0)) == 0 &&
+	    os_memcmp(res, prf0, sizeof(prf0)) == 0)
+		wpa_printf(MSG_INFO, "Test case 0 - OK");
+	else {
+		wpa_printf(MSG_INFO, "Test case 0 - FAILED!");
+		ret++;
+	}
+
+	if (sha1_prf(key1, sizeof(key1) - 1, "prefix", data1, sizeof(data1) - 1,
+		     res, sizeof(prf1)) == 0 &&
+	    os_memcmp(res, prf1, sizeof(prf1)) == 0)
+		wpa_printf(MSG_INFO, "Test case 1 - OK");
+	else {
+		wpa_printf(MSG_INFO, "Test case 1 - FAILED!");
+		ret++;
+	}
+
+	if (sha1_prf(key2, sizeof(key2), "prefix", data2, sizeof(data2),
+		     res, sizeof(prf2)) == 0 &&
+	    os_memcmp(res, prf2, sizeof(prf2)) == 0)
+		wpa_printf(MSG_INFO, "Test case 2 - OK");
+	else {
+		wpa_printf(MSG_INFO, "Test case 2 - FAILED!");
+		ret++;
+	}
+
+	ret += test_eap_fast();
+
+	wpa_printf(MSG_INFO, "PBKDF2-SHA1 Passphrase test cases:");
+	for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) {
+		u8 psk[32];
+		const struct passphrase_test *test = &passphrase_tests[i];
+
+		if (pbkdf2_sha1(test->passphrase,
+				(const u8 *) test->ssid, strlen(test->ssid),
+				4096, psk, 32) == 0 &&
+		    os_memcmp(psk, test->psk, 32) == 0)
+			wpa_printf(MSG_INFO, "Test case %d - OK", i);
+		else {
+			wpa_printf(MSG_INFO, "Test case %d - FAILED!", i);
+			ret++;
+		}
+	}
+
+	wpa_printf(MSG_INFO, "PBKDF2-SHA1 test cases (RFC 6070):");
+	for (i = 0; i < NUM_RFC6070_TESTS; i++) {
+		u8 dk[25];
+		const struct rfc6070_test *test = &rfc6070_tests[i];
+
+		if (pbkdf2_sha1(test->p, (const u8 *) test->s, strlen(test->s),
+				test->c, dk, test->dk_len) == 0 &&
+		    os_memcmp(dk, test->dk, test->dk_len) == 0)
+			wpa_printf(MSG_INFO, "Test case %d - OK", i);
+		else {
+			wpa_printf(MSG_INFO, "Test case %d - FAILED!", i);
+			ret++;
+		}
+	}
+
+	if (!ret)
+		wpa_printf(MSG_INFO, "SHA1 test cases passed");
+	return ret;
+}
+
+
+const struct {
+	char *data;
+	u8 hash[32];
+} tests[] = {
+	{
+		"abc",
+		{
+			0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
+			0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+			0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+			0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
+		}
+	},
+	{
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+		{
+			0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
+			0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
+			0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
+			0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1
+		}
+	}
+};
+
+const struct hmac_test {
+	u8 key[80];
+	size_t key_len;
+	u8 data[128];
+	size_t data_len;
+	u8 hash[32];
+} hmac_tests[] = {
+	/* draft-ietf-ipsec-ciph-sha-256-01.txt */
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
+		},
+		32,
+		"abc", 3,
+		{
+			0xa2, 0x1b, 0x1f, 0x5d, 0x4c, 0xf4, 0xf7, 0x3a,
+			0x4d, 0xd9, 0x39, 0x75, 0x0f, 0x7a, 0x06, 0x6a,
+			0x7f, 0x98, 0xcc, 0x13, 0x1c, 0xb1, 0x6a, 0x66,
+			0x92, 0x75, 0x90, 0x21, 0xcf, 0xab, 0x81, 0x81
+		}
+	},
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
+		},
+		32,
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+		56,
+		{
+			0x10, 0x4f, 0xdc, 0x12, 0x57, 0x32, 0x8f, 0x08,
+			0x18, 0x4b, 0xa7, 0x31, 0x31, 0xc5, 0x3c, 0xae,
+			0xe6, 0x98, 0xe3, 0x61, 0x19, 0x42, 0x11, 0x49,
+			0xea, 0x8c, 0x71, 0x24, 0x56, 0x69, 0x7d, 0x30
+		}
+	},
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
+		},
+		32,
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+		112,
+		{
+			0x47, 0x03, 0x05, 0xfc, 0x7e, 0x40, 0xfe, 0x34,
+			0xd3, 0xee, 0xb3, 0xe7, 0x73, 0xd9, 0x5a, 0xab,
+			0x73, 0xac, 0xf0, 0xfd, 0x06, 0x04, 0x47, 0xa5,
+			0xeb, 0x45, 0x95, 0xbf, 0x33, 0xa9, 0xd1, 0xa3
+		}
+	},
+	{
+		{
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
+		},
+		32,
+		"Hi There",
+		8,
+		{
+			0x19, 0x8a, 0x60, 0x7e, 0xb4, 0x4b, 0xfb, 0xc6,
+			0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5,
+			0xba, 0x0a, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c,
+			0x7a, 0x3b, 0x16, 0x96, 0xa0, 0xb6, 0x8c, 0xf7
+		}
+	},
+	{
+		"Jefe",
+		4,
+		"what do ya want for nothing?",
+		28,
+		{
+			0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e,
+			0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7,
+			0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83,
+			0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43
+		}
+	},
+	{
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+		},
+		32,
+		{
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd
+		},
+		50,
+		{
+			0xcd, 0xcb, 0x12, 0x20, 0xd1, 0xec, 0xcc, 0xea,
+			0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62,
+			0xe5, 0x49, 0xfe, 0x6c, 0xe9, 0xed, 0x7f, 0xdc,
+			0x43, 0x19, 0x1f, 0xbd, 0xe4, 0x5c, 0x30, 0xb0
+		}
+	},
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+			0x21, 0x22, 0x23, 0x24, 0x25
+		},
+		37,
+		{
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd
+		},
+		50,
+		{
+			0xd4, 0x63, 0x3c, 0x17, 0xf6, 0xfb, 0x8d, 0x74,
+			0x4c, 0x66, 0xde, 0xe0, 0xf8, 0xf0, 0x74, 0x55,
+			0x6e, 0xc4, 0xaf, 0x55, 0xef, 0x07, 0x99, 0x85,
+			0x41, 0x46, 0x8e, 0xb4, 0x9b, 0xd2, 0xe9, 0x17
+		}
+	},
+	{
+		{
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c
+		},
+		32,
+		"Test With Truncation",
+		20,
+		{
+			0x75, 0x46, 0xaf, 0x01, 0x84, 0x1f, 0xc0, 0x9b,
+			0x1a, 0xb9, 0xc3, 0x74, 0x9a, 0x5f, 0x1c, 0x17,
+			0xd4, 0xf5, 0x89, 0x66, 0x8a, 0x58, 0x7b, 0x27,
+			0x00, 0xa9, 0xc9, 0x7c, 0x11, 0x93, 0xcf, 0x42
+		}
+	},
+	{
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+		},
+		80,
+		"Test Using Larger Than Block-Size Key - Hash Key First",
+		54,
+		{
+			0x69, 0x53, 0x02, 0x5e, 0xd9, 0x6f, 0x0c, 0x09,
+			0xf8, 0x0a, 0x96, 0xf7, 0x8e, 0x65, 0x38, 0xdb,
+			0xe2, 0xe7, 0xb8, 0x20, 0xe3, 0xdd, 0x97, 0x0e,
+			0x7d, 0xdd, 0x39, 0x09, 0x1b, 0x32, 0x35, 0x2f
+		}
+	},
+	{
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+		},
+		80,
+		"Test Using Larger Than Block-Size Key and Larger Than One "
+		"Block-Size Data",
+		73,
+		{
+			0x63, 0x55, 0xac, 0x22, 0xe8, 0x90, 0xd0, 0xa3,
+			0xc8, 0x48, 0x1a, 0x5c, 0xa4, 0x82, 0x5b, 0xc8,
+			0x84, 0xd3, 0xe7, 0xa1, 0xff, 0x98, 0xa2, 0xfc,
+			0x2a, 0xc7, 0xd8, 0xe0, 0x64, 0xc3, 0xb2, 0xe6
+		}
+	}
+};
+
+
+static int test_sha256(void)
+{
+	unsigned int i;
+	u8 hash[32];
+	const u8 *addr[2];
+	size_t len[2];
+	int errors = 0;
+
+	for (i = 0; i < ARRAY_SIZE(tests); i++) {
+		wpa_printf(MSG_INFO, "SHA256 test case %d:", i + 1);
+
+		addr[0] = (u8 *) tests[i].data;
+		len[0] = strlen(tests[i].data);
+		sha256_vector(1, addr, len, hash);
+		if (memcmp(hash, tests[i].hash, 32) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		if (len[0]) {
+			addr[0] = (u8 *) tests[i].data;
+			len[0] = 1;
+			addr[1] = (u8 *) tests[i].data + 1;
+			len[1] = strlen(tests[i].data) - 1;
+			sha256_vector(2, addr, len, hash);
+			if (memcmp(hash, tests[i].hash, 32) != 0) {
+				wpa_printf(MSG_INFO, " FAIL");
+				errors++;
+			} else
+				wpa_printf(MSG_INFO, " OK");
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) {
+		const struct hmac_test *t = &hmac_tests[i];
+
+		wpa_printf(MSG_INFO, "HMAC-SHA256 test case %d:", i + 1);
+
+		if (hmac_sha256(t->key, t->key_len, t->data, t->data_len,
+				hash) < 0 ||
+		    os_memcmp(hash, t->hash, 32) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		addr[0] = t->data;
+		len[0] = t->data_len;
+		if (hmac_sha256_vector(t->key, t->key_len, 1, addr, len,
+				       hash) < 0 ||
+		    os_memcmp(hash, t->hash, 32) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		if (len[0]) {
+			addr[0] = t->data;
+			len[0] = 1;
+			addr[1] = t->data + 1;
+			len[1] = t->data_len - 1;
+			if (hmac_sha256_vector(t->key, t->key_len, 2, addr, len,
+					       hash) < 0 ||
+			    os_memcmp(hash, t->hash, 32) != 0) {
+				wpa_printf(MSG_INFO, " FAIL");
+				errors++;
+			} else
+				wpa_printf(MSG_INFO, " OK");
+		}
+	}
+
+	wpa_printf(MSG_INFO, "Test IEEE 802.11r KDF");
+	sha256_prf((u8 *) "abc", 3, "KDF test", (u8 *) "data", 4,
+		   hash, sizeof(hash));
+	/* TODO: add proper test case for this */
+
+	if (!errors)
+		wpa_printf(MSG_INFO, "SHA256 test cases passed");
+	return errors;
+}
+
+
+static int test_ms_funcs(void)
+{
+#ifndef CONFIG_FIPS
+	/* Test vector from RFC2759 example */
+	char *username = "User";
+	char *password = "clientPass";
+	u8 auth_challenge[] = {
+		0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
+		0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28
+	};
+	u8 peer_challenge[] = {
+		0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
+		0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E
+	};
+	u8 password_hash[] = {
+		0x44, 0xEB, 0xBA, 0x8D, 0x53, 0x12, 0xB8, 0xD6,
+		0x11, 0x47, 0x44, 0x11, 0xF5, 0x69, 0x89, 0xAE
+	};
+	u8 nt_response[] = {
+		0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E,
+		0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54,
+		0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF
+	};
+	u8 password_hash_hash[] = {
+		0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C,
+		0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F
+	};
+	u8 authenticator_response[] = {
+		0x40, 0x7A, 0x55, 0x89, 0x11, 0x5F, 0xD0, 0xD6,
+		0x20, 0x9F, 0x51, 0x0F, 0xE9, 0xC0, 0x45, 0x66,
+		0x93, 0x2C, 0xDA, 0x56
+	};
+	u8 master_key[] = {
+		0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C,
+		0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31
+	};
+	u8 send_start_key[] = {
+		0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B,
+		0xA1, 0x18, 0xCB, 0x15, 0x3F, 0x56, 0xDC, 0xCB
+	};
+	u8 buf[32];
+	int errors = 0;
+
+	if (nt_password_hash((u8 *) password, os_strlen(password), buf) ||
+	    os_memcmp(password_hash, buf, sizeof(password_hash)) != 0) {
+		wpa_printf(MSG_ERROR, "nt_password_hash failed");
+		errors++;
+	}
+
+	if (generate_nt_response(auth_challenge, peer_challenge,
+				 (u8 *) username, os_strlen(username),
+				 (u8 *) password, os_strlen(password), buf) ||
+	    os_memcmp(nt_response, buf, sizeof(nt_response)) != 0) {
+		wpa_printf(MSG_ERROR, "generate_nt_response failed");
+		errors++;
+	}
+
+	if (hash_nt_password_hash(password_hash, buf) ||
+	    os_memcmp(password_hash_hash, buf,
+		      sizeof(password_hash_hash)) != 0) {
+		wpa_printf(MSG_ERROR, "hash_nt_password_hash failed");
+		errors++;
+	}
+
+	if (generate_authenticator_response((u8 *) password,
+					    os_strlen(password),
+					    peer_challenge, auth_challenge,
+					    (u8 *) username,
+					    os_strlen(username),
+					    nt_response, buf) ||
+	    os_memcmp(authenticator_response, buf,
+		      sizeof(authenticator_response)) != 0) {
+		wpa_printf(MSG_ERROR, "generate_authenticator_response failed");
+		errors++;
+	}
+
+	if (get_master_key(password_hash_hash, nt_response, buf) ||
+	    os_memcmp(master_key, buf, sizeof(master_key)) != 0) {
+		wpa_printf(MSG_ERROR, "get_master_key failed");
+		errors++;
+	}
+
+	if (get_asymetric_start_key(master_key, buf, sizeof(send_start_key),
+				    1, 1) ||
+	    os_memcmp(send_start_key, buf, sizeof(send_start_key)) != 0) {
+		wpa_printf(MSG_ERROR, "get_asymetric_start_key failed");
+		errors++;
+	}
+
+	if (errors)
+		wpa_printf(MSG_ERROR, "ms_funcs: %d errors", errors);
+	else
+		wpa_printf(MSG_INFO, "ms_funcs test cases passed");
+
+	return errors;
+#else /* CONFIG_FIPS */
+	wpa_printf(MSG_INFO, "ms_funcs test cases skipped due to CONFIG_FIPS");
+	return 0;
+#endif /* CONFIG_FIPS */
+}
+
+
+int crypto_module_tests(void)
+{
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "crypto module tests");
+	if (test_siv() ||
+	    test_omac1() ||
+	    test_eax() ||
+	    test_cbc() ||
+	    test_ecb() ||
+	    test_key_wrap() ||
+	    test_md5() ||
+	    test_sha1() ||
+	    test_sha256() ||
+	    test_ms_funcs())
+		ret = -1;
+
+	return ret;
+}
diff --git a/hostap/src/crypto/crypto_none.c b/hostap/src/crypto/crypto_none.c
new file mode 100644
index 0000000..011f3f3
--- /dev/null
+++ b/hostap/src/crypto/crypto_none.c
@@ -0,0 +1,23 @@
+/*
+ * WPA Supplicant / Empty template functions for crypto wrapper
+ * Copyright (c) 2005, 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 md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return 0;
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+}
diff --git a/hostap/src/crypto/crypto_openssl.c b/hostap/src/crypto/crypto_openssl.c
new file mode 100644
index 0000000..6cff75c
--- /dev/null
+++ b/hostap/src/crypto/crypto_openssl.c
@@ -0,0 +1,1442 @@
+/*
+ * Wrapper functions for OpenSSL libcrypto
+ * Copyright (c) 2004-2015, 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/opensslv.h>
+#include <openssl/err.h>
+#include <openssl/des.h>
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/dh.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+#ifdef CONFIG_OPENSSL_CMAC
+#include <openssl/cmac.h>
+#endif /* CONFIG_OPENSSL_CMAC */
+#ifdef CONFIG_ECC
+#include <openssl/ec.h>
+#endif /* CONFIG_ECC */
+
+#include "common.h"
+#include "wpabuf.h"
+#include "dh_group5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "crypto.h"
+
+static BIGNUM * get_group5_prime(void)
+{
+#ifdef OPENSSL_IS_BORINGSSL
+	static const unsigned char RFC3526_PRIME_1536[] = {
+		0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,
+		0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,
+		0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6,
+		0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD,
+		0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,
+		0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,
+		0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9,
+		0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED,
+		0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,
+		0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D,
+		0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36,
+		0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F,
+		0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,
+		0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,
+		0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08,
+		0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+	};
+        return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL);
+#else /* OPENSSL_IS_BORINGSSL */
+	return get_rfc3526_prime_1536(NULL);
+#endif /* OPENSSL_IS_BORINGSSL */
+}
+
+#ifdef OPENSSL_NO_SHA256
+#define NO_SHA256_WRAPPER
+#endif
+
+static int openssl_digest_vector(const EVP_MD *type, size_t num_elem,
+				 const u8 *addr[], const size_t *len, u8 *mac)
+{
+	EVP_MD_CTX ctx;
+	size_t i;
+	unsigned int mac_len;
+
+	EVP_MD_CTX_init(&ctx);
+	if (!EVP_DigestInit_ex(&ctx, type, NULL)) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+	for (i = 0; i < num_elem; i++) {
+		if (!EVP_DigestUpdate(&ctx, addr[i], len[i])) {
+			wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate "
+				   "failed: %s",
+				   ERR_error_string(ERR_get_error(), NULL));
+			return -1;
+		}
+	}
+	if (!EVP_DigestFinal(&ctx, mac, &mac_len)) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+#ifndef CONFIG_FIPS
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
+}
+#endif /* CONFIG_FIPS */
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	u8 pkey[8], next, tmp;
+	int i;
+	DES_key_schedule ks;
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	DES_set_key((DES_cblock *) &pkey, &ks);
+	DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks,
+			DES_ENCRYPT);
+}
+
+
+#ifndef CONFIG_NO_RC4
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+	     u8 *data, size_t data_len)
+{
+#ifdef OPENSSL_NO_RC4
+	return -1;
+#else /* OPENSSL_NO_RC4 */
+	EVP_CIPHER_CTX ctx;
+	int outl;
+	int res = -1;
+	unsigned char skip_buf[16];
+
+	EVP_CIPHER_CTX_init(&ctx);
+	if (!EVP_CIPHER_CTX_set_padding(&ctx, 0) ||
+	    !EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, NULL, NULL, 1) ||
+	    !EVP_CIPHER_CTX_set_key_length(&ctx, keylen) ||
+	    !EVP_CipherInit_ex(&ctx, NULL, NULL, key, NULL, 1))
+		goto out;
+
+	while (skip >= sizeof(skip_buf)) {
+		size_t len = skip;
+		if (len > sizeof(skip_buf))
+			len = sizeof(skip_buf);
+		if (!EVP_CipherUpdate(&ctx, skip_buf, &outl, skip_buf, len))
+			goto out;
+		skip -= len;
+	}
+
+	if (EVP_CipherUpdate(&ctx, data, &outl, data, data_len))
+		res = 0;
+
+out:
+	EVP_CIPHER_CTX_cleanup(&ctx);
+	return res;
+#endif /* OPENSSL_NO_RC4 */
+}
+#endif /* CONFIG_NO_RC4 */
+
+
+#ifndef CONFIG_FIPS
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac);
+}
+#endif /* CONFIG_FIPS */
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_digest_vector(EVP_sha1(), num_elem, addr, len, mac);
+}
+
+
+#ifndef NO_SHA256_WRAPPER
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return openssl_digest_vector(EVP_sha256(), num_elem, addr, len, mac);
+}
+#endif /* NO_SHA256_WRAPPER */
+
+
+static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen)
+{
+	switch (keylen) {
+	case 16:
+		return EVP_aes_128_ecb();
+#ifndef OPENSSL_IS_BORINGSSL
+	case 24:
+		return EVP_aes_192_ecb();
+#endif /* OPENSSL_IS_BORINGSSL */
+	case 32:
+		return EVP_aes_256_ecb();
+	}
+
+	return NULL;
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	EVP_CIPHER_CTX *ctx;
+	const EVP_CIPHER *type;
+
+	type = aes_get_evp_cipher(len);
+	if (type == NULL)
+		return NULL;
+
+	ctx = os_malloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	EVP_CIPHER_CTX_init(ctx);
+	if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
+		os_free(ctx);
+		return NULL;
+	}
+	EVP_CIPHER_CTX_set_padding(ctx, 0);
+	return ctx;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	EVP_CIPHER_CTX *c = ctx;
+	int clen = 16;
+	if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+	}
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	EVP_CIPHER_CTX *c = ctx;
+	u8 buf[16];
+	int len = sizeof(buf);
+	if (EVP_EncryptFinal_ex(c, buf, &len) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptFinal_ex failed: "
+			   "%s", ERR_error_string(ERR_get_error(), NULL));
+	}
+	if (len != 0) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
+			   "in AES encrypt", len);
+	}
+	EVP_CIPHER_CTX_cleanup(c);
+	bin_clear_free(c, sizeof(*c));
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	EVP_CIPHER_CTX *ctx;
+	const EVP_CIPHER *type;
+
+	type = aes_get_evp_cipher(len);
+	if (type == NULL)
+		return NULL;
+
+	ctx = os_malloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	EVP_CIPHER_CTX_init(ctx);
+	if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
+		os_free(ctx);
+		return NULL;
+	}
+	EVP_CIPHER_CTX_set_padding(ctx, 0);
+	return ctx;
+}
+
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	EVP_CIPHER_CTX *c = ctx;
+	int plen = 16;
+	if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+	}
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	EVP_CIPHER_CTX *c = ctx;
+	u8 buf[16];
+	int len = sizeof(buf);
+	if (EVP_DecryptFinal_ex(c, buf, &len) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptFinal_ex failed: "
+			   "%s", ERR_error_string(ERR_get_error(), NULL));
+	}
+	if (len != 0) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
+			   "in AES decrypt", len);
+	}
+	EVP_CIPHER_CTX_cleanup(c);
+	bin_clear_free(c, sizeof(*c));
+}
+
+
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+
+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
+{
+	AES_KEY actx;
+	int res;
+
+	if (AES_set_encrypt_key(kek, kek_len << 3, &actx))
+		return -1;
+	res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8);
+	OPENSSL_cleanse(&actx, sizeof(actx));
+	return res <= 0 ? -1 : 0;
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+	       u8 *plain)
+{
+	AES_KEY actx;
+	int res;
+
+	if (AES_set_decrypt_key(kek, kek_len << 3, &actx))
+		return -1;
+	res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8);
+	OPENSSL_cleanse(&actx, sizeof(actx));
+	return res <= 0 ? -1 : 0;
+}
+
+#endif /* CONFIG_OPENSSL_INTERNAL_AES_WRAP */
+#endif /* CONFIG_FIPS */
+
+
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	EVP_CIPHER_CTX ctx;
+	int clen, len;
+	u8 buf[16];
+
+	EVP_CIPHER_CTX_init(&ctx);
+	if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
+		return -1;
+	EVP_CIPHER_CTX_set_padding(&ctx, 0);
+
+	clen = data_len;
+	if (EVP_EncryptUpdate(&ctx, data, &clen, data, data_len) != 1 ||
+	    clen != (int) data_len)
+		return -1;
+
+	len = sizeof(buf);
+	if (EVP_EncryptFinal_ex(&ctx, buf, &len) != 1 || len != 0)
+		return -1;
+	EVP_CIPHER_CTX_cleanup(&ctx);
+
+	return 0;
+}
+
+
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	EVP_CIPHER_CTX ctx;
+	int plen, len;
+	u8 buf[16];
+
+	EVP_CIPHER_CTX_init(&ctx);
+	if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
+		return -1;
+	EVP_CIPHER_CTX_set_padding(&ctx, 0);
+
+	plen = data_len;
+	if (EVP_DecryptUpdate(&ctx, data, &plen, data, data_len) != 1 ||
+	    plen != (int) data_len)
+		return -1;
+
+	len = sizeof(buf);
+	if (EVP_DecryptFinal_ex(&ctx, buf, &len) != 1 || len != 0)
+		return -1;
+	EVP_CIPHER_CTX_cleanup(&ctx);
+
+	return 0;
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+		   const u8 *power, size_t power_len,
+		   const u8 *modulus, size_t modulus_len,
+		   u8 *result, size_t *result_len)
+{
+	BIGNUM *bn_base, *bn_exp, *bn_modulus, *bn_result;
+	int ret = -1;
+	BN_CTX *ctx;
+
+	ctx = BN_CTX_new();
+	if (ctx == NULL)
+		return -1;
+
+	bn_base = BN_bin2bn(base, base_len, NULL);
+	bn_exp = BN_bin2bn(power, power_len, NULL);
+	bn_modulus = BN_bin2bn(modulus, modulus_len, NULL);
+	bn_result = BN_new();
+
+	if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL ||
+	    bn_result == NULL)
+		goto error;
+
+	if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1)
+		goto error;
+
+	*result_len = BN_bn2bin(bn_result, result);
+	ret = 0;
+
+error:
+	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;
+}
+
+
+struct crypto_cipher {
+	EVP_CIPHER_CTX enc;
+	EVP_CIPHER_CTX dec;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+	const EVP_CIPHER *cipher;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	switch (alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef OPENSSL_NO_RC4
+	case CRYPTO_CIPHER_ALG_RC4:
+		cipher = EVP_rc4();
+		break;
+#endif /* OPENSSL_NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef OPENSSL_NO_AES
+	case CRYPTO_CIPHER_ALG_AES:
+		switch (key_len) {
+		case 16:
+			cipher = EVP_aes_128_cbc();
+			break;
+#ifndef OPENSSL_IS_BORINGSSL
+		case 24:
+			cipher = EVP_aes_192_cbc();
+			break;
+#endif /* OPENSSL_IS_BORINGSSL */
+		case 32:
+			cipher = EVP_aes_256_cbc();
+			break;
+		default:
+			os_free(ctx);
+			return NULL;
+		}
+		break;
+#endif /* OPENSSL_NO_AES */
+#ifndef OPENSSL_NO_DES
+	case CRYPTO_CIPHER_ALG_3DES:
+		cipher = EVP_des_ede3_cbc();
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		cipher = EVP_des_cbc();
+		break;
+#endif /* OPENSSL_NO_DES */
+#ifndef OPENSSL_NO_RC2
+	case CRYPTO_CIPHER_ALG_RC2:
+		cipher = EVP_rc2_ecb();
+		break;
+#endif /* OPENSSL_NO_RC2 */
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	EVP_CIPHER_CTX_init(&ctx->enc);
+	EVP_CIPHER_CTX_set_padding(&ctx->enc, 0);
+	if (!EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, NULL, NULL) ||
+	    !EVP_CIPHER_CTX_set_key_length(&ctx->enc, key_len) ||
+	    !EVP_EncryptInit_ex(&ctx->enc, NULL, NULL, key, iv)) {
+		EVP_CIPHER_CTX_cleanup(&ctx->enc);
+		os_free(ctx);
+		return NULL;
+	}
+
+	EVP_CIPHER_CTX_init(&ctx->dec);
+	EVP_CIPHER_CTX_set_padding(&ctx->dec, 0);
+	if (!EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, NULL, NULL) ||
+	    !EVP_CIPHER_CTX_set_key_length(&ctx->dec, key_len) ||
+	    !EVP_DecryptInit_ex(&ctx->dec, NULL, NULL, key, iv)) {
+		EVP_CIPHER_CTX_cleanup(&ctx->enc);
+		EVP_CIPHER_CTX_cleanup(&ctx->dec);
+		os_free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	int outl;
+	if (!EVP_EncryptUpdate(&ctx->enc, crypt, &outl, plain, len))
+		return -1;
+	return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	int outl;
+	outl = len;
+	if (!EVP_DecryptUpdate(&ctx->dec, plain, &outl, crypt, len))
+		return -1;
+	return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	EVP_CIPHER_CTX_cleanup(&ctx->enc);
+	EVP_CIPHER_CTX_cleanup(&ctx->dec);
+	os_free(ctx);
+}
+
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+{
+	DH *dh;
+	struct wpabuf *pubkey = NULL, *privkey = NULL;
+	size_t publen, privlen;
+
+	*priv = NULL;
+	*publ = NULL;
+
+	dh = DH_new();
+	if (dh == NULL)
+		return NULL;
+
+	dh->g = BN_new();
+	if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
+		goto err;
+
+	dh->p = get_group5_prime();
+	if (dh->p == NULL)
+		goto err;
+
+	if (DH_generate_key(dh) != 1)
+		goto err;
+
+	publen = BN_num_bytes(dh->pub_key);
+	pubkey = wpabuf_alloc(publen);
+	if (pubkey == NULL)
+		goto err;
+	privlen = BN_num_bytes(dh->priv_key);
+	privkey = wpabuf_alloc(privlen);
+	if (privkey == NULL)
+		goto err;
+
+	BN_bn2bin(dh->pub_key, wpabuf_put(pubkey, publen));
+	BN_bn2bin(dh->priv_key, wpabuf_put(privkey, privlen));
+
+	*priv = privkey;
+	*publ = pubkey;
+	return dh;
+
+err:
+	wpabuf_clear_free(pubkey);
+	wpabuf_clear_free(privkey);
+	DH_free(dh);
+	return NULL;
+}
+
+
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+	DH *dh;
+
+	dh = DH_new();
+	if (dh == NULL)
+		return NULL;
+
+	dh->g = BN_new();
+	if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
+		goto err;
+
+	dh->p = get_group5_prime();
+	if (dh->p == NULL)
+		goto err;
+
+	dh->priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL);
+	if (dh->priv_key == NULL)
+		goto err;
+
+	dh->pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL);
+	if (dh->pub_key == NULL)
+		goto err;
+
+	if (DH_generate_key(dh) != 1)
+		goto err;
+
+	return dh;
+
+err:
+	DH_free(dh);
+	return NULL;
+}
+
+
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+				  const struct wpabuf *own_private)
+{
+	BIGNUM *pub_key;
+	struct wpabuf *res = NULL;
+	size_t rlen;
+	DH *dh = ctx;
+	int keylen;
+
+	if (ctx == NULL)
+		return NULL;
+
+	pub_key = BN_bin2bn(wpabuf_head(peer_public), wpabuf_len(peer_public),
+			    NULL);
+	if (pub_key == NULL)
+		return NULL;
+
+	rlen = DH_size(dh);
+	res = wpabuf_alloc(rlen);
+	if (res == NULL)
+		goto err;
+
+	keylen = DH_compute_key(wpabuf_mhead(res), pub_key, dh);
+	if (keylen < 0)
+		goto err;
+	wpabuf_put(res, keylen);
+	BN_clear_free(pub_key);
+
+	return res;
+
+err:
+	BN_clear_free(pub_key);
+	wpabuf_clear_free(res);
+	return NULL;
+}
+
+
+void dh5_free(void *ctx)
+{
+	DH *dh;
+	if (ctx == NULL)
+		return;
+	dh = ctx;
+	DH_free(dh);
+}
+
+
+struct crypto_hash {
+	HMAC_CTX ctx;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len)
+{
+	struct crypto_hash *ctx;
+	const EVP_MD *md;
+
+	switch (alg) {
+#ifndef OPENSSL_NO_MD5
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		md = EVP_md5();
+		break;
+#endif /* OPENSSL_NO_MD5 */
+#ifndef OPENSSL_NO_SHA
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		md = EVP_sha1();
+		break;
+#endif /* OPENSSL_NO_SHA */
+#ifndef OPENSSL_NO_SHA256
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		md = EVP_sha256();
+		break;
+#endif /* CONFIG_SHA256 */
+#endif /* OPENSSL_NO_SHA256 */
+	default:
+		return NULL;
+	}
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	HMAC_CTX_init(&ctx->ctx);
+
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+	HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL);
+#else /* openssl < 0.9.9 */
+	if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) {
+		bin_clear_free(ctx, sizeof(*ctx));
+		return NULL;
+	}
+#endif /* openssl < 0.9.9 */
+
+	return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+	if (ctx == NULL)
+		return;
+	HMAC_Update(&ctx->ctx, data, len);
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+	unsigned int mdlen;
+	int res;
+
+	if (ctx == NULL)
+		return -2;
+
+	if (mac == NULL || len == NULL) {
+		bin_clear_free(ctx, sizeof(*ctx));
+		return 0;
+	}
+
+	mdlen = *len;
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+	HMAC_Final(&ctx->ctx, mac, &mdlen);
+	res = 1;
+#else /* openssl < 0.9.9 */
+	res = HMAC_Final(&ctx->ctx, mac, &mdlen);
+#endif /* openssl < 0.9.9 */
+	HMAC_CTX_cleanup(&ctx->ctx);
+	bin_clear_free(ctx, sizeof(*ctx));
+
+	if (res == 1) {
+		*len = mdlen;
+		return 0;
+	}
+
+	return -1;
+}
+
+
+static int openssl_hmac_vector(const EVP_MD *type, const u8 *key,
+			       size_t key_len, size_t num_elem,
+			       const u8 *addr[], const size_t *len, u8 *mac,
+			       unsigned int mdlen)
+{
+	HMAC_CTX ctx;
+	size_t i;
+	int res;
+
+	HMAC_CTX_init(&ctx);
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+	HMAC_Init_ex(&ctx, key, key_len, type, NULL);
+#else /* openssl < 0.9.9 */
+	if (HMAC_Init_ex(&ctx, key, key_len, type, NULL) != 1)
+		return -1;
+#endif /* openssl < 0.9.9 */
+
+	for (i = 0; i < num_elem; i++)
+		HMAC_Update(&ctx, addr[i], len[i]);
+
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+	HMAC_Final(&ctx, mac, &mdlen);
+	res = 1;
+#else /* openssl < 0.9.9 */
+	res = HMAC_Final(&ctx, mac, &mdlen);
+#endif /* openssl < 0.9.9 */
+	HMAC_CTX_cleanup(&ctx);
+
+	return res == 1 ? 0 : -1;
+}
+
+
+#ifndef CONFIG_FIPS
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector(EVP_md5(), key ,key_len, num_elem, addr, len,
+				   mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+		int iterations, u8 *buf, size_t buflen)
+{
+	if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
+				   ssid_len, iterations, buflen, buf) != 1)
+		return -1;
+	return 0;
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector(EVP_sha1(), key, key_len, num_elem, addr,
+				   len, mac, 20);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	       u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector(EVP_sha256(), key, key_len, num_elem, addr,
+				   len, mac, 32);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr,
+				   len, mac, 32);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+int crypto_get_random(void *buf, size_t len)
+{
+	if (RAND_bytes(buf, len) != 1)
+		return -1;
+	return 0;
+}
+
+
+#ifdef CONFIG_OPENSSL_CMAC
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	CMAC_CTX *ctx;
+	int ret = -1;
+	size_t outlen, i;
+
+	ctx = CMAC_CTX_new();
+	if (ctx == NULL)
+		return -1;
+
+	if (key_len == 32) {
+		if (!CMAC_Init(ctx, key, 32, EVP_aes_256_cbc(), NULL))
+			goto fail;
+	} else if (key_len == 16) {
+		if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL))
+			goto fail;
+	} else {
+		goto fail;
+	}
+	for (i = 0; i < num_elem; i++) {
+		if (!CMAC_Update(ctx, addr[i], len[i]))
+			goto fail;
+	}
+	if (!CMAC_Final(ctx, mac, &outlen) || outlen != 16)
+		goto fail;
+
+	ret = 0;
+fail:
+	CMAC_CTX_free(ctx);
+	return ret;
+}
+
+
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+			 const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
+#endif /* CONFIG_OPENSSL_CMAC */
+
+
+struct crypto_bignum * crypto_bignum_init(void)
+{
+	return (struct crypto_bignum *) BN_new();
+}
+
+
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
+{
+	BIGNUM *bn = BN_bin2bn(buf, len, NULL);
+	return (struct crypto_bignum *) bn;
+}
+
+
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
+{
+	if (clear)
+		BN_clear_free((BIGNUM *) n);
+	else
+		BN_free((BIGNUM *) n);
+}
+
+
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+			 u8 *buf, size_t buflen, size_t padlen)
+{
+	int num_bytes, offset;
+
+	if (padlen > buflen)
+		return -1;
+
+	num_bytes = BN_num_bytes((const BIGNUM *) a);
+	if ((size_t) num_bytes > buflen)
+		return -1;
+	if (padlen > (size_t) num_bytes)
+		offset = padlen - num_bytes;
+	else
+		offset = 0;
+
+	os_memset(buf, 0, offset);
+	BN_bn2bin((const BIGNUM *) a, buf + offset);
+
+	return num_bytes + offset;
+}
+
+
+int crypto_bignum_add(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	return BN_add((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
+		0 : -1;
+}
+
+
+int crypto_bignum_mod(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	int res;
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b,
+		     bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_exptmod(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  const struct crypto_bignum *c,
+			  struct crypto_bignum *d)
+{
+	int res;
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
+			 (const BIGNUM *) c, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  struct crypto_bignum *c)
+{
+	BIGNUM *res;
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a,
+			     (const BIGNUM *) b, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_sub(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
+		0 : -1;
+}
+
+
+int crypto_bignum_div(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	int res;
+
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a,
+		     (const BIGNUM *) b, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d)
+{
+	int res;
+
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod_mul((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
+			 (const BIGNUM *) c, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b)
+{
+	return BN_cmp((const BIGNUM *) a, (const BIGNUM *) b);
+}
+
+
+int crypto_bignum_bits(const struct crypto_bignum *a)
+{
+	return BN_num_bits((const BIGNUM *) a);
+}
+
+
+int crypto_bignum_is_zero(const struct crypto_bignum *a)
+{
+	return BN_is_zero((const BIGNUM *) a);
+}
+
+
+int crypto_bignum_is_one(const struct crypto_bignum *a)
+{
+	return BN_is_one((const BIGNUM *) a);
+}
+
+
+int crypto_bignum_legendre(const struct crypto_bignum *a,
+			   const struct crypto_bignum *p)
+{
+	BN_CTX *bnctx;
+	BIGNUM *exp = NULL, *tmp = NULL;
+	int res = -2;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -2;
+
+	exp = BN_new();
+	tmp = BN_new();
+	if (!exp || !tmp ||
+	    /* exp = (p-1) / 2 */
+	    !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) ||
+	    !BN_rshift1(exp, exp) ||
+	    !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p,
+			bnctx))
+		goto fail;
+
+	if (BN_is_word(tmp, 1))
+		res = 1;
+	else if (BN_is_zero(tmp))
+		res = 0;
+	else
+		res = -1;
+
+fail:
+	BN_clear_free(tmp);
+	BN_clear_free(exp);
+	BN_CTX_free(bnctx);
+	return res;
+}
+
+
+#ifdef CONFIG_ECC
+
+struct crypto_ec {
+	EC_GROUP *group;
+	BN_CTX *bnctx;
+	BIGNUM *prime;
+	BIGNUM *order;
+	BIGNUM *a;
+	BIGNUM *b;
+};
+
+struct crypto_ec * crypto_ec_init(int group)
+{
+	struct crypto_ec *e;
+	int nid;
+
+	/* Map from IANA registry for IKE D-H groups to OpenSSL NID */
+	switch (group) {
+	case 19:
+		nid = NID_X9_62_prime256v1;
+		break;
+	case 20:
+		nid = NID_secp384r1;
+		break;
+	case 21:
+		nid = NID_secp521r1;
+		break;
+	case 25:
+		nid = NID_X9_62_prime192v1;
+		break;
+	case 26:
+		nid = NID_secp224r1;
+		break;
+#ifdef NID_brainpoolP224r1
+	case 27:
+		nid = NID_brainpoolP224r1;
+		break;
+#endif /* NID_brainpoolP224r1 */
+#ifdef NID_brainpoolP256r1
+	case 28:
+		nid = NID_brainpoolP256r1;
+		break;
+#endif /* NID_brainpoolP256r1 */
+#ifdef NID_brainpoolP384r1
+	case 29:
+		nid = NID_brainpoolP384r1;
+		break;
+#endif /* NID_brainpoolP384r1 */
+#ifdef NID_brainpoolP512r1
+	case 30:
+		nid = NID_brainpoolP512r1;
+		break;
+#endif /* NID_brainpoolP512r1 */
+	default:
+		return NULL;
+	}
+
+	e = os_zalloc(sizeof(*e));
+	if (e == NULL)
+		return NULL;
+
+	e->bnctx = BN_CTX_new();
+	e->group = EC_GROUP_new_by_curve_name(nid);
+	e->prime = BN_new();
+	e->order = BN_new();
+	e->a = BN_new();
+	e->b = BN_new();
+	if (e->group == NULL || e->bnctx == NULL || e->prime == NULL ||
+	    e->order == NULL || e->a == NULL || e->b == NULL ||
+	    !EC_GROUP_get_curve_GFp(e->group, e->prime, e->a, e->b, e->bnctx) ||
+	    !EC_GROUP_get_order(e->group, e->order, e->bnctx)) {
+		crypto_ec_deinit(e);
+		e = NULL;
+	}
+
+	return e;
+}
+
+
+void crypto_ec_deinit(struct crypto_ec *e)
+{
+	if (e == NULL)
+		return;
+	BN_clear_free(e->b);
+	BN_clear_free(e->a);
+	BN_clear_free(e->order);
+	BN_clear_free(e->prime);
+	EC_GROUP_free(e->group);
+	BN_CTX_free(e->bnctx);
+	os_free(e);
+}
+
+
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
+{
+	if (e == NULL)
+		return NULL;
+	return (struct crypto_ec_point *) EC_POINT_new(e->group);
+}
+
+
+size_t crypto_ec_prime_len(struct crypto_ec *e)
+{
+	return BN_num_bytes(e->prime);
+}
+
+
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
+{
+	return BN_num_bits(e->prime);
+}
+
+
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) e->prime;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) e->order;
+}
+
+
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
+{
+	if (clear)
+		EC_POINT_clear_free((EC_POINT *) p);
+	else
+		EC_POINT_free((EC_POINT *) p);
+}
+
+
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+			   const struct crypto_ec_point *point, u8 *x, u8 *y)
+{
+	BIGNUM *x_bn, *y_bn;
+	int ret = -1;
+	int len = BN_num_bytes(e->prime);
+
+	x_bn = BN_new();
+	y_bn = BN_new();
+
+	if (x_bn && y_bn &&
+	    EC_POINT_get_affine_coordinates_GFp(e->group, (EC_POINT *) point,
+						x_bn, y_bn, e->bnctx)) {
+		if (x) {
+			crypto_bignum_to_bin((struct crypto_bignum *) x_bn,
+					     x, len, len);
+		}
+		if (y) {
+			crypto_bignum_to_bin((struct crypto_bignum *) y_bn,
+					     y, len, len);
+		}
+		ret = 0;
+	}
+
+	BN_clear_free(x_bn);
+	BN_clear_free(y_bn);
+	return ret;
+}
+
+
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+						  const u8 *val)
+{
+	BIGNUM *x, *y;
+	EC_POINT *elem;
+	int len = BN_num_bytes(e->prime);
+
+	x = BN_bin2bn(val, len, NULL);
+	y = BN_bin2bn(val + len, len, NULL);
+	elem = EC_POINT_new(e->group);
+	if (x == NULL || y == NULL || elem == NULL) {
+		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_clear_free(elem);
+		elem = NULL;
+	}
+
+	BN_clear_free(x);
+	BN_clear_free(y);
+
+	return (struct crypto_ec_point *) elem;
+}
+
+
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b,
+			struct crypto_ec_point *c)
+{
+	return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a,
+			    (const EC_POINT *) b, e->bnctx) ? 0 : -1;
+}
+
+
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+			const struct crypto_bignum *b,
+			struct crypto_ec_point *res)
+{
+	return EC_POINT_mul(e->group, (EC_POINT *) res, NULL,
+			    (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx)
+		? 0 : -1;
+}
+
+
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
+{
+	return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1;
+}
+
+
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+				  struct crypto_ec_point *p,
+				  const struct crypto_bignum *x, int y_bit)
+{
+	if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p,
+						     (const BIGNUM *) x, y_bit,
+						     e->bnctx) ||
+	    !EC_POINT_is_on_curve(e->group, (EC_POINT *) p, e->bnctx))
+		return -1;
+	return 0;
+}
+
+
+struct crypto_bignum *
+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
+			      const struct crypto_bignum *x)
+{
+	BIGNUM *tmp, *tmp2, *y_sqr = NULL;
+
+	tmp = BN_new();
+	tmp2 = BN_new();
+
+	/* y^2 = x^3 + ax + b */
+	if (tmp && tmp2 &&
+	    BN_mod_sqr(tmp, (const BIGNUM *) x, e->prime, e->bnctx) &&
+	    BN_mod_mul(tmp, tmp, (const BIGNUM *) x, e->prime, e->bnctx) &&
+	    BN_mod_mul(tmp2, e->a, (const BIGNUM *) x, e->prime, e->bnctx) &&
+	    BN_mod_add_quick(tmp2, tmp2, tmp, e->prime) &&
+	    BN_mod_add_quick(tmp2, tmp2, e->b, e->prime)) {
+		y_sqr = tmp2;
+		tmp2 = NULL;
+	}
+
+	BN_clear_free(tmp);
+	BN_clear_free(tmp2);
+
+	return (struct crypto_bignum *) y_sqr;
+}
+
+
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+				   const struct crypto_ec_point *p)
+{
+	return EC_POINT_is_at_infinity(e->group, (const EC_POINT *) p);
+}
+
+
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+				const struct crypto_ec_point *p)
+{
+	return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p,
+				    e->bnctx) == 1;
+}
+
+
+int crypto_ec_point_cmp(const struct crypto_ec *e,
+			const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b)
+{
+	return EC_POINT_cmp(e->group, (const EC_POINT *) a,
+			    (const EC_POINT *) b, e->bnctx);
+}
+
+#endif /* CONFIG_ECC */
diff --git a/hostap/src/crypto/des-internal.c b/hostap/src/crypto/des-internal.c
new file mode 100644
index 0000000..dec39ef
--- /dev/null
+++ b/hostap/src/crypto/des-internal.c
@@ -0,0 +1,493 @@
+/*
+ * DES and 3DES-EDE ciphers
+ *
+ * Modifications to LibTomCrypt implementation:
+ * Copyright (c) 2006-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"
+#include "des_i.h"
+
+/*
+ * This implementation is based on a DES implementation included in
+ * LibTomCrypt. The version here is modified to fit in wpa_supplicant/hostapd
+ * coding style.
+ */
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com
+ */
+
+/**
+  DES code submitted by Dobes Vandermeer
+*/
+
+#define ROLc(x, y) \
+	((((unsigned long) (x) << (unsigned long) ((y) & 31)) | \
+	  (((unsigned long) (x) & 0xFFFFFFFFUL) >> \
+	   (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+#define RORc(x, y) \
+	(((((unsigned long) (x) & 0xFFFFFFFFUL) >> \
+	   (unsigned long) ((y) & 31)) | \
+	  ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & \
+	 0xFFFFFFFFUL)
+
+
+static const u32 bytebit[8] =
+{
+	0200, 0100, 040, 020, 010, 04, 02, 01 
+};
+
+static const u32 bigbyte[24] =
+{
+	0x800000UL,  0x400000UL,  0x200000UL,  0x100000UL,
+	0x80000UL,   0x40000UL,   0x20000UL,   0x10000UL,
+	0x8000UL,    0x4000UL,    0x2000UL,    0x1000UL,
+	0x800UL,     0x400UL,     0x200UL,     0x100UL,
+	0x80UL,      0x40UL,      0x20UL,      0x10UL,
+	0x8UL,       0x4UL,       0x2UL,       0x1L 
+};
+
+/* Use the key schedule specific in the standard (ANSI X3.92-1981) */
+
+static const u8 pc1[56] = {
+	56, 48, 40, 32, 24, 16,  8,  0, 57, 49, 41, 33, 25, 17,  
+	 9,  1, 58, 50, 42, 34, 26, 18, 10,  2, 59, 51, 43, 35, 
+	62, 54, 46, 38, 30, 22, 14,  6, 61, 53, 45, 37, 29, 21,
+	13,  5, 60, 52, 44, 36, 28, 20, 12,  4, 27, 19, 11,  3 
+};
+
+static const u8 totrot[16] = {
+	1,   2,  4,  6,
+	8,  10, 12, 14, 
+	15, 17, 19, 21, 
+	23, 25, 27, 28
+};
+
+static const u8 pc2[48] = {
+	13, 16, 10, 23,  0,  4,      2, 27, 14,  5, 20,  9,
+	22, 18, 11,  3, 25,  7,     15,  6, 26, 19, 12,  1,
+	40, 51, 30, 36, 46, 54,     29, 39, 50, 44, 32, 47,
+	43, 48, 38, 55, 33, 52,     45, 41, 49, 35, 28, 31
+};
+
+
+static const u32 SP1[64] =
+{
+	0x01010400UL, 0x00000000UL, 0x00010000UL, 0x01010404UL,
+	0x01010004UL, 0x00010404UL, 0x00000004UL, 0x00010000UL,
+	0x00000400UL, 0x01010400UL, 0x01010404UL, 0x00000400UL,
+	0x01000404UL, 0x01010004UL, 0x01000000UL, 0x00000004UL,
+	0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00010400UL,
+	0x00010400UL, 0x01010000UL, 0x01010000UL, 0x01000404UL,
+	0x00010004UL, 0x01000004UL, 0x01000004UL, 0x00010004UL,
+	0x00000000UL, 0x00000404UL, 0x00010404UL, 0x01000000UL,
+	0x00010000UL, 0x01010404UL, 0x00000004UL, 0x01010000UL,
+	0x01010400UL, 0x01000000UL, 0x01000000UL, 0x00000400UL,
+	0x01010004UL, 0x00010000UL, 0x00010400UL, 0x01000004UL,
+	0x00000400UL, 0x00000004UL, 0x01000404UL, 0x00010404UL,
+	0x01010404UL, 0x00010004UL, 0x01010000UL, 0x01000404UL,
+	0x01000004UL, 0x00000404UL, 0x00010404UL, 0x01010400UL,
+	0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00000000UL,
+	0x00010004UL, 0x00010400UL, 0x00000000UL, 0x01010004UL
+};
+
+static const u32 SP2[64] =
+{
+	0x80108020UL, 0x80008000UL, 0x00008000UL, 0x00108020UL,
+	0x00100000UL, 0x00000020UL, 0x80100020UL, 0x80008020UL,
+	0x80000020UL, 0x80108020UL, 0x80108000UL, 0x80000000UL,
+	0x80008000UL, 0x00100000UL, 0x00000020UL, 0x80100020UL,
+	0x00108000UL, 0x00100020UL, 0x80008020UL, 0x00000000UL,
+	0x80000000UL, 0x00008000UL, 0x00108020UL, 0x80100000UL,
+	0x00100020UL, 0x80000020UL, 0x00000000UL, 0x00108000UL,
+	0x00008020UL, 0x80108000UL, 0x80100000UL, 0x00008020UL,
+	0x00000000UL, 0x00108020UL, 0x80100020UL, 0x00100000UL,
+	0x80008020UL, 0x80100000UL, 0x80108000UL, 0x00008000UL,
+	0x80100000UL, 0x80008000UL, 0x00000020UL, 0x80108020UL,
+	0x00108020UL, 0x00000020UL, 0x00008000UL, 0x80000000UL,
+	0x00008020UL, 0x80108000UL, 0x00100000UL, 0x80000020UL,
+	0x00100020UL, 0x80008020UL, 0x80000020UL, 0x00100020UL,
+	0x00108000UL, 0x00000000UL, 0x80008000UL, 0x00008020UL,
+	0x80000000UL, 0x80100020UL, 0x80108020UL, 0x00108000UL
+};
+
+static const u32 SP3[64] =
+{
+	0x00000208UL, 0x08020200UL, 0x00000000UL, 0x08020008UL,
+	0x08000200UL, 0x00000000UL, 0x00020208UL, 0x08000200UL,
+	0x00020008UL, 0x08000008UL, 0x08000008UL, 0x00020000UL,
+	0x08020208UL, 0x00020008UL, 0x08020000UL, 0x00000208UL,
+	0x08000000UL, 0x00000008UL, 0x08020200UL, 0x00000200UL,
+	0x00020200UL, 0x08020000UL, 0x08020008UL, 0x00020208UL,
+	0x08000208UL, 0x00020200UL, 0x00020000UL, 0x08000208UL,
+	0x00000008UL, 0x08020208UL, 0x00000200UL, 0x08000000UL,
+	0x08020200UL, 0x08000000UL, 0x00020008UL, 0x00000208UL,
+	0x00020000UL, 0x08020200UL, 0x08000200UL, 0x00000000UL,
+	0x00000200UL, 0x00020008UL, 0x08020208UL, 0x08000200UL,
+	0x08000008UL, 0x00000200UL, 0x00000000UL, 0x08020008UL,
+	0x08000208UL, 0x00020000UL, 0x08000000UL, 0x08020208UL,
+	0x00000008UL, 0x00020208UL, 0x00020200UL, 0x08000008UL,
+	0x08020000UL, 0x08000208UL, 0x00000208UL, 0x08020000UL,
+	0x00020208UL, 0x00000008UL, 0x08020008UL, 0x00020200UL
+};
+
+static const u32 SP4[64] =
+{
+	0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL,
+	0x00802080UL, 0x00800081UL, 0x00800001UL, 0x00002001UL,
+	0x00000000UL, 0x00802000UL, 0x00802000UL, 0x00802081UL,
+	0x00000081UL, 0x00000000UL, 0x00800080UL, 0x00800001UL,
+	0x00000001UL, 0x00002000UL, 0x00800000UL, 0x00802001UL,
+	0x00000080UL, 0x00800000UL, 0x00002001UL, 0x00002080UL,
+	0x00800081UL, 0x00000001UL, 0x00002080UL, 0x00800080UL,
+	0x00002000UL, 0x00802080UL, 0x00802081UL, 0x00000081UL,
+	0x00800080UL, 0x00800001UL, 0x00802000UL, 0x00802081UL,
+	0x00000081UL, 0x00000000UL, 0x00000000UL, 0x00802000UL,
+	0x00002080UL, 0x00800080UL, 0x00800081UL, 0x00000001UL,
+	0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL,
+	0x00802081UL, 0x00000081UL, 0x00000001UL, 0x00002000UL,
+	0x00800001UL, 0x00002001UL, 0x00802080UL, 0x00800081UL,
+	0x00002001UL, 0x00002080UL, 0x00800000UL, 0x00802001UL,
+	0x00000080UL, 0x00800000UL, 0x00002000UL, 0x00802080UL
+};
+
+static const u32 SP5[64] =
+{
+	0x00000100UL, 0x02080100UL, 0x02080000UL, 0x42000100UL,
+	0x00080000UL, 0x00000100UL, 0x40000000UL, 0x02080000UL,
+	0x40080100UL, 0x00080000UL, 0x02000100UL, 0x40080100UL,
+	0x42000100UL, 0x42080000UL, 0x00080100UL, 0x40000000UL,
+	0x02000000UL, 0x40080000UL, 0x40080000UL, 0x00000000UL,
+	0x40000100UL, 0x42080100UL, 0x42080100UL, 0x02000100UL,
+	0x42080000UL, 0x40000100UL, 0x00000000UL, 0x42000000UL,
+	0x02080100UL, 0x02000000UL, 0x42000000UL, 0x00080100UL,
+	0x00080000UL, 0x42000100UL, 0x00000100UL, 0x02000000UL,
+	0x40000000UL, 0x02080000UL, 0x42000100UL, 0x40080100UL,
+	0x02000100UL, 0x40000000UL, 0x42080000UL, 0x02080100UL,
+	0x40080100UL, 0x00000100UL, 0x02000000UL, 0x42080000UL,
+	0x42080100UL, 0x00080100UL, 0x42000000UL, 0x42080100UL,
+	0x02080000UL, 0x00000000UL, 0x40080000UL, 0x42000000UL,
+	0x00080100UL, 0x02000100UL, 0x40000100UL, 0x00080000UL,
+	0x00000000UL, 0x40080000UL, 0x02080100UL, 0x40000100UL
+};
+
+static const u32 SP6[64] =
+{
+	0x20000010UL, 0x20400000UL, 0x00004000UL, 0x20404010UL,
+	0x20400000UL, 0x00000010UL, 0x20404010UL, 0x00400000UL,
+	0x20004000UL, 0x00404010UL, 0x00400000UL, 0x20000010UL,
+	0x00400010UL, 0x20004000UL, 0x20000000UL, 0x00004010UL,
+	0x00000000UL, 0x00400010UL, 0x20004010UL, 0x00004000UL,
+	0x00404000UL, 0x20004010UL, 0x00000010UL, 0x20400010UL,
+	0x20400010UL, 0x00000000UL, 0x00404010UL, 0x20404000UL,
+	0x00004010UL, 0x00404000UL, 0x20404000UL, 0x20000000UL,
+	0x20004000UL, 0x00000010UL, 0x20400010UL, 0x00404000UL,
+	0x20404010UL, 0x00400000UL, 0x00004010UL, 0x20000010UL,
+	0x00400000UL, 0x20004000UL, 0x20000000UL, 0x00004010UL,
+	0x20000010UL, 0x20404010UL, 0x00404000UL, 0x20400000UL,
+	0x00404010UL, 0x20404000UL, 0x00000000UL, 0x20400010UL,
+	0x00000010UL, 0x00004000UL, 0x20400000UL, 0x00404010UL,
+	0x00004000UL, 0x00400010UL, 0x20004010UL, 0x00000000UL,
+	0x20404000UL, 0x20000000UL, 0x00400010UL, 0x20004010UL
+};
+
+static const u32 SP7[64] =
+{
+	0x00200000UL, 0x04200002UL, 0x04000802UL, 0x00000000UL,
+	0x00000800UL, 0x04000802UL, 0x00200802UL, 0x04200800UL,
+	0x04200802UL, 0x00200000UL, 0x00000000UL, 0x04000002UL,
+	0x00000002UL, 0x04000000UL, 0x04200002UL, 0x00000802UL,
+	0x04000800UL, 0x00200802UL, 0x00200002UL, 0x04000800UL,
+	0x04000002UL, 0x04200000UL, 0x04200800UL, 0x00200002UL,
+	0x04200000UL, 0x00000800UL, 0x00000802UL, 0x04200802UL,
+	0x00200800UL, 0x00000002UL, 0x04000000UL, 0x00200800UL,
+	0x04000000UL, 0x00200800UL, 0x00200000UL, 0x04000802UL,
+	0x04000802UL, 0x04200002UL, 0x04200002UL, 0x00000002UL,
+	0x00200002UL, 0x04000000UL, 0x04000800UL, 0x00200000UL,
+	0x04200800UL, 0x00000802UL, 0x00200802UL, 0x04200800UL,
+	0x00000802UL, 0x04000002UL, 0x04200802UL, 0x04200000UL,
+	0x00200800UL, 0x00000000UL, 0x00000002UL, 0x04200802UL,
+	0x00000000UL, 0x00200802UL, 0x04200000UL, 0x00000800UL,
+	0x04000002UL, 0x04000800UL, 0x00000800UL, 0x00200002UL
+};
+
+static const u32 SP8[64] =
+{
+	0x10001040UL, 0x00001000UL, 0x00040000UL, 0x10041040UL,
+	0x10000000UL, 0x10001040UL, 0x00000040UL, 0x10000000UL,
+	0x00040040UL, 0x10040000UL, 0x10041040UL, 0x00041000UL,
+	0x10041000UL, 0x00041040UL, 0x00001000UL, 0x00000040UL,
+	0x10040000UL, 0x10000040UL, 0x10001000UL, 0x00001040UL,
+	0x00041000UL, 0x00040040UL, 0x10040040UL, 0x10041000UL,
+	0x00001040UL, 0x00000000UL, 0x00000000UL, 0x10040040UL,
+	0x10000040UL, 0x10001000UL, 0x00041040UL, 0x00040000UL,
+	0x00041040UL, 0x00040000UL, 0x10041000UL, 0x00001000UL,
+	0x00000040UL, 0x10040040UL, 0x00001000UL, 0x00041040UL,
+	0x10001000UL, 0x00000040UL, 0x10000040UL, 0x10040000UL,
+	0x10040040UL, 0x10000000UL, 0x00040000UL, 0x10001040UL,
+	0x00000000UL, 0x10041040UL, 0x00040040UL, 0x10000040UL,
+	0x10040000UL, 0x10001000UL, 0x10001040UL, 0x00000000UL,
+	0x10041040UL, 0x00041000UL, 0x00041000UL, 0x00001040UL,
+	0x00001040UL, 0x00040040UL, 0x10000000UL, 0x10041000UL
+};
+
+
+static void cookey(const u32 *raw1, u32 *keyout)
+{
+	u32 *cook;
+	const u32 *raw0;
+	u32 dough[32];
+	int i;
+
+	cook = dough;
+	for (i = 0; i < 16; i++, raw1++) {
+		raw0 = raw1++;
+		*cook    = (*raw0 & 0x00fc0000L) << 6;
+		*cook   |= (*raw0 & 0x00000fc0L) << 10;
+		*cook   |= (*raw1 & 0x00fc0000L) >> 10;
+		*cook++ |= (*raw1 & 0x00000fc0L) >> 6;
+		*cook    = (*raw0 & 0x0003f000L) << 12;
+		*cook   |= (*raw0 & 0x0000003fL) << 16;
+		*cook   |= (*raw1 & 0x0003f000L) >> 4;
+		*cook++ |= (*raw1 & 0x0000003fL);
+	}
+
+	os_memcpy(keyout, dough, sizeof(dough));
+}
+
+
+static void deskey(const u8 *key, int decrypt, u32 *keyout)
+{
+	u32 i, j, l, m, n, kn[32];
+	u8 pc1m[56], pcr[56];
+
+	for (j = 0; j < 56; j++) {
+		l = (u32) pc1[j];
+		m = l & 7;
+		pc1m[j] = (u8)
+			((key[l >> 3U] & bytebit[m]) == bytebit[m] ? 1 : 0);
+	}
+
+	for (i = 0; i < 16; i++) {
+		if (decrypt)
+			m = (15 - i) << 1;
+		else
+			m = i << 1;
+		n = m + 1;
+		kn[m] = kn[n] = 0L;
+		for (j = 0; j < 28; j++) {
+			l = j + (u32) totrot[i];
+			if (l < 28)
+				pcr[j] = pc1m[l];
+			else
+				pcr[j] = pc1m[l - 28];
+		}
+		for (/* j = 28 */; j < 56; j++) {
+			l = j + (u32) totrot[i];
+			if (l < 56)
+				pcr[j] = pc1m[l];
+			else
+				pcr[j] = pc1m[l - 28];
+		}
+		for (j = 0; j < 24; j++) {
+			if ((int) pcr[(int) pc2[j]] != 0)
+				kn[m] |= bigbyte[j];
+			if ((int) pcr[(int) pc2[j + 24]] != 0)
+				kn[n] |= bigbyte[j];
+		}
+	}
+
+	cookey(kn, keyout);
+}
+
+
+static void desfunc(u32 *block, const u32 *keys)
+{
+	u32 work, right, leftt;
+	int cur_round;
+
+	leftt = block[0];
+	right = block[1];
+
+	work = ((leftt >> 4)  ^ right) & 0x0f0f0f0fL;
+	right ^= work;
+	leftt ^= (work << 4);
+
+	work = ((leftt >> 16) ^ right) & 0x0000ffffL;
+	right ^= work;
+	leftt ^= (work << 16);
+
+	work = ((right >> 2)  ^ leftt) & 0x33333333L;
+	leftt ^= work;
+	right ^= (work << 2);
+
+	work = ((right >> 8)  ^ leftt) & 0x00ff00ffL;
+	leftt ^= work;
+	right ^= (work << 8);
+
+	right = ROLc(right, 1);
+	work = (leftt ^ right) & 0xaaaaaaaaL;
+
+	leftt ^= work;
+	right ^= work;
+	leftt = ROLc(leftt, 1);
+
+	for (cur_round = 0; cur_round < 8; cur_round++) {
+		work  = RORc(right, 4) ^ *keys++;
+		leftt ^= SP7[work        & 0x3fL]
+			^ SP5[(work >>  8) & 0x3fL]
+			^ SP3[(work >> 16) & 0x3fL]
+			^ SP1[(work >> 24) & 0x3fL];
+		work  = right ^ *keys++;
+		leftt ^= SP8[ work        & 0x3fL]
+			^  SP6[(work >>  8) & 0x3fL]
+			^  SP4[(work >> 16) & 0x3fL]
+			^  SP2[(work >> 24) & 0x3fL];
+
+		work = RORc(leftt, 4) ^ *keys++;
+		right ^= SP7[ work        & 0x3fL]
+			^  SP5[(work >>  8) & 0x3fL]
+			^  SP3[(work >> 16) & 0x3fL]
+			^  SP1[(work >> 24) & 0x3fL];
+		work  = leftt ^ *keys++;
+		right ^= SP8[ work        & 0x3fL]
+			^  SP6[(work >>  8) & 0x3fL]
+			^  SP4[(work >> 16) & 0x3fL]
+			^  SP2[(work >> 24) & 0x3fL];
+	}
+
+	right = RORc(right, 1);
+	work = (leftt ^ right) & 0xaaaaaaaaL;
+	leftt ^= work;
+	right ^= work;
+	leftt = RORc(leftt, 1);
+	work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
+	right ^= work;
+	leftt ^= (work << 8);
+	/* -- */
+	work = ((leftt >> 2) ^ right) & 0x33333333L;
+	right ^= work;
+	leftt ^= (work << 2);
+	work = ((right >> 16) ^ leftt) & 0x0000ffffL;
+	leftt ^= work;
+	right ^= (work << 16);
+	work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
+	leftt ^= work;
+	right ^= (work << 4);
+
+	block[0] = right;
+	block[1] = leftt;
+}
+
+
+/* wpa_supplicant/hostapd specific wrapper */
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	u8 pkey[8], next, tmp;
+	int i;
+	u32 ek[32], work[2];
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	deskey(pkey, 0, ek);
+
+	work[0] = WPA_GET_BE32(clear);
+	work[1] = WPA_GET_BE32(clear + 4);
+	desfunc(work, ek);
+	WPA_PUT_BE32(cypher, work[0]);
+	WPA_PUT_BE32(cypher + 4, work[1]);
+
+	os_memset(pkey, 0, sizeof(pkey));
+	os_memset(ek, 0, sizeof(ek));
+}
+
+
+void des_key_setup(const u8 *key, u32 *ek, u32 *dk)
+{
+	deskey(key, 0, ek);
+	deskey(key, 1, dk);
+}
+
+
+void des_block_encrypt(const u8 *plain, const u32 *ek, u8 *crypt)
+{
+	u32 work[2];
+	work[0] = WPA_GET_BE32(plain);
+	work[1] = WPA_GET_BE32(plain + 4);
+	desfunc(work, ek);
+	WPA_PUT_BE32(crypt, work[0]);
+	WPA_PUT_BE32(crypt + 4, work[1]);
+}
+
+
+void des_block_decrypt(const u8 *crypt, const u32 *dk, u8 *plain)
+{
+	u32 work[2];
+	work[0] = WPA_GET_BE32(crypt);
+	work[1] = WPA_GET_BE32(crypt + 4);
+	desfunc(work, dk);
+	WPA_PUT_BE32(plain, work[0]);
+	WPA_PUT_BE32(plain + 4, work[1]);
+}
+
+
+void des3_key_setup(const u8 *key, struct des3_key_s *dkey)
+{
+	deskey(key, 0, dkey->ek[0]);
+	deskey(key + 8, 1, dkey->ek[1]);
+	deskey(key + 16, 0, dkey->ek[2]);
+
+	deskey(key, 1, dkey->dk[2]);
+	deskey(key + 8, 0, dkey->dk[1]);
+	deskey(key + 16, 1, dkey->dk[0]);
+}
+
+
+void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt)
+{
+	u32 work[2];
+
+	work[0] = WPA_GET_BE32(plain);
+	work[1] = WPA_GET_BE32(plain + 4);
+	desfunc(work, key->ek[0]);
+	desfunc(work, key->ek[1]);
+	desfunc(work, key->ek[2]);
+	WPA_PUT_BE32(crypt, work[0]);
+	WPA_PUT_BE32(crypt + 4, work[1]);
+}
+
+
+void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain)
+{
+	u32 work[2];
+
+	work[0] = WPA_GET_BE32(crypt);
+	work[1] = WPA_GET_BE32(crypt + 4);
+	desfunc(work, key->dk[0]);
+	desfunc(work, key->dk[1]);
+	desfunc(work, key->dk[2]);
+	WPA_PUT_BE32(plain, work[0]);
+	WPA_PUT_BE32(plain + 4, work[1]);
+}
diff --git a/hostap/src/crypto/des_i.h b/hostap/src/crypto/des_i.h
new file mode 100644
index 0000000..c9563d2
--- /dev/null
+++ b/hostap/src/crypto/des_i.h
@@ -0,0 +1,25 @@
+/*
+ * DES and 3DES-EDE ciphers
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DES_I_H
+#define DES_I_H
+
+struct des3_key_s {
+	u32 ek[3][32];
+	u32 dk[3][32];
+};
+
+void des_key_setup(const u8 *key, u32 *ek, u32 *dk);
+void des_block_encrypt(const u8 *plain, const u32 *ek, u8 *crypt);
+void des_block_decrypt(const u8 *crypt, const u32 *dk, u8 *plain);
+
+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 /* DES_I_H */
diff --git a/hostap/src/crypto/dh_group5.c b/hostap/src/crypto/dh_group5.c
new file mode 100644
index 0000000..ccdbfc8
--- /dev/null
+++ b/hostap/src/crypto/dh_group5.c
@@ -0,0 +1,40 @@
+/*
+ * Diffie-Hellman group 5 operations
+ * Copyright (c) 2009, 2012, 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 "dh_groups.h"
+#include "dh_group5.h"
+
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+{
+	*publ = dh_init(dh_groups_get(5), priv);
+	if (*publ == NULL)
+		return NULL;
+	return (void *) 1;
+}
+
+
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+	return (void *) 1;
+}
+
+
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+				  const struct wpabuf *own_private)
+{
+	return dh_derive_shared(peer_public, own_private, dh_groups_get(5));
+}
+
+
+void dh5_free(void *ctx)
+{
+}
diff --git a/hostap/src/crypto/dh_group5.h b/hostap/src/crypto/dh_group5.h
new file mode 100644
index 0000000..abee8ea
--- /dev/null
+++ b/hostap/src/crypto/dh_group5.h
@@ -0,0 +1,18 @@
+/*
+ * Diffie-Hellman group 5 operations
+ * Copyright (c) 2009, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DH_GROUP5_H
+#define DH_GROUP5_H
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ);
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ);
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+				  const struct wpabuf *own_private);
+void dh5_free(void *ctx);
+
+#endif /* DH_GROUP5_H */
diff --git a/hostap/src/crypto/dh_groups.c b/hostap/src/crypto/dh_groups.c
new file mode 100644
index 0000000..3aeb2bb
--- /dev/null
+++ b/hostap/src/crypto/dh_groups.c
@@ -0,0 +1,1271 @@
+/*
+ * Diffie-Hellman groups
+ * Copyright (c) 2007, 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"
+#include "random.h"
+#include "dh_groups.h"
+
+
+#ifdef ALL_DH_GROUPS
+
+/* RFC 4306, B.1. Group 1 - 768 Bit MODP
+ * Generator: 2
+ * Prime: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 }
+ */
+static const u8 dh_group1_generator[1] = { 0x02 };
+static const u8 dh_group1_prime[96] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group1_order[96] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1D, 0x1B, 0x10,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 4306, B.2. Group 2 - 1024 Bit MODP
+ * Generator: 2
+ * Prime: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }
+ */
+static const u8 dh_group2_generator[1] = { 0x02 };
+static const u8 dh_group2_prime[128] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+	0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+	0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group2_order[128] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x73, 0x29, 0xC0,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+#endif /* ALL_DH_GROUPS */
+
+/* RFC 3526, 2. Group 5 - 1536 Bit MODP
+ * Generator: 2
+ * Prime: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }
+ */
+static const u8 dh_group5_generator[1] = { 0x02 };
+static const u8 dh_group5_prime[192] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+	0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+	0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+	0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+	0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+	0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+	0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+	0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group5_order[192] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x11, 0xB9, 0x93,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+#ifdef ALL_DH_GROUPS
+
+/* RFC 3526, 3. Group 14 - 2048 Bit MODP
+ * Generator: 2
+ * Prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
+ */
+static const u8 dh_group14_generator[1] = { 0x02 };
+static const u8 dh_group14_prime[256] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+	0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+	0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+	0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+	0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+	0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+	0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+	0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+	0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+	0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+	0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+	0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+	0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+	0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+	0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+	0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group14_order[256] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x56, 0x55, 0x34,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 4. Group 15 - 3072 Bit MODP
+ * Generator: 2
+ * Prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }
+ */
+static const u8 dh_group15_generator[1] = { 0x02 };
+static const u8 dh_group15_prime[384] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+	0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+	0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+	0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+	0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+	0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+	0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+	0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+	0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+	0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+	0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+	0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+	0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+	0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+	0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+	0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+	0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+	0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+	0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+	0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+	0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+	0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+	0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+	0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+	0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+	0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+	0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+	0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+	0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+	0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+	0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+	0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group15_order[384] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x9D, 0x69, 0x65,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 5. Group 16 - 4096 Bit MODP
+ * Generator: 2
+ * Prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }
+ */
+static const u8 dh_group16_generator[1] = { 0x02 };
+static const u8 dh_group16_prime[512] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+	0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+	0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+	0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+	0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+	0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+	0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+	0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+	0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+	0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+	0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+	0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+	0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+	0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+	0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+	0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+	0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+	0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+	0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+	0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+	0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+	0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+	0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+	0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+	0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+	0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+	0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+	0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+	0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+	0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+	0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+	0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+	0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+	0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+	0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+	0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+	0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+	0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+	0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+	0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+	0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+	0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+	0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+	0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+	0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+	0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+	0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+	0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group16_order[512] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x03, 0x18, 0xCC,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 6. Group 17 - 6144 Bit MODP
+ * Generator: 2
+ * Prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }
+ */
+static const u8 dh_group17_generator[1] = { 0x02 };
+static const u8 dh_group17_prime[768] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+	0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+	0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+	0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+	0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+	0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+	0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+	0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+	0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+	0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+	0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+	0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+	0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+	0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+	0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+	0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+	0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+	0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+	0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+	0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+	0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+	0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+	0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+	0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+	0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+	0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+	0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+	0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+	0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+	0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+	0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+	0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+	0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+	0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+	0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+	0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+	0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+	0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+	0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+	0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+	0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+	0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+	0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+	0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+	0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+	0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+	0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+	0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
+	0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26,
+	0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE,
+	0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
+	0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E,
+	0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE,
+	0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
+	0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18,
+	0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED,
+	0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
+	0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B,
+	0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42,
+	0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
+	0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC,
+	0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03,
+	0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
+	0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82,
+	0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E,
+	0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
+	0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE,
+	0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5,
+	0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
+	0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8,
+	0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0,
+	0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
+	0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76,
+	0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0,
+	0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
+	0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32,
+	0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68,
+	0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
+	0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6,
+	0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group17_order[768] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49,
+	0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13,
+	0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F,
+	0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE,
+	0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87,
+	0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7,
+	0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18,
+	0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C,
+	0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76,
+	0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D,
+	0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5,
+	0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1,
+	0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F,
+	0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76,
+	0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81,
+	0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B,
+	0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41,
+	0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F,
+	0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79,
+	0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F,
+	0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62,
+	0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55,
+	0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC,
+	0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0,
+	0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94,
+	0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B,
+	0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8,
+	0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE,
+	0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19,
+	0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34,
+	0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77,
+	0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B,
+	0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xE6, 0x20, 0x12,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 7. Group 18 - 8192 Bit MODP
+ * Generator: 2
+ * Prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }
+ */
+static const u8 dh_group18_generator[1] = { 0x02 };
+static const u8 dh_group18_prime[1024] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+	0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+	0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+	0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+	0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+	0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+	0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+	0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+	0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+	0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+	0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+	0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+	0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+	0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+	0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+	0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+	0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+	0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+	0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+	0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+	0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+	0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+	0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+	0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+	0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+	0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+	0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+	0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+	0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+	0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+	0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+	0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+	0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+	0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+	0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+	0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+	0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+	0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+	0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+	0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+	0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+	0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+	0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+	0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+	0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+	0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+	0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+	0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
+	0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26,
+	0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE,
+	0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
+	0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E,
+	0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE,
+	0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
+	0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18,
+	0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED,
+	0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
+	0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B,
+	0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42,
+	0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
+	0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC,
+	0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03,
+	0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
+	0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82,
+	0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E,
+	0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
+	0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE,
+	0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5,
+	0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
+	0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8,
+	0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0,
+	0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
+	0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76,
+	0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0,
+	0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
+	0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32,
+	0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68,
+	0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
+	0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6,
+	0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xBE, 0x11, 0x59,
+	0x74, 0xA3, 0x92, 0x6F, 0x12, 0xFE, 0xE5, 0xE4,
+	0x38, 0x77, 0x7C, 0xB6, 0xA9, 0x32, 0xDF, 0x8C,
+	0xD8, 0xBE, 0xC4, 0xD0, 0x73, 0xB9, 0x31, 0xBA,
+	0x3B, 0xC8, 0x32, 0xB6, 0x8D, 0x9D, 0xD3, 0x00,
+	0x74, 0x1F, 0xA7, 0xBF, 0x8A, 0xFC, 0x47, 0xED,
+	0x25, 0x76, 0xF6, 0x93, 0x6B, 0xA4, 0x24, 0x66,
+	0x3A, 0xAB, 0x63, 0x9C, 0x5A, 0xE4, 0xF5, 0x68,
+	0x34, 0x23, 0xB4, 0x74, 0x2B, 0xF1, 0xC9, 0x78,
+	0x23, 0x8F, 0x16, 0xCB, 0xE3, 0x9D, 0x65, 0x2D,
+	0xE3, 0xFD, 0xB8, 0xBE, 0xFC, 0x84, 0x8A, 0xD9,
+	0x22, 0x22, 0x2E, 0x04, 0xA4, 0x03, 0x7C, 0x07,
+	0x13, 0xEB, 0x57, 0xA8, 0x1A, 0x23, 0xF0, 0xC7,
+	0x34, 0x73, 0xFC, 0x64, 0x6C, 0xEA, 0x30, 0x6B,
+	0x4B, 0xCB, 0xC8, 0x86, 0x2F, 0x83, 0x85, 0xDD,
+	0xFA, 0x9D, 0x4B, 0x7F, 0xA2, 0xC0, 0x87, 0xE8,
+	0x79, 0x68, 0x33, 0x03, 0xED, 0x5B, 0xDD, 0x3A,
+	0x06, 0x2B, 0x3C, 0xF5, 0xB3, 0xA2, 0x78, 0xA6,
+	0x6D, 0x2A, 0x13, 0xF8, 0x3F, 0x44, 0xF8, 0x2D,
+	0xDF, 0x31, 0x0E, 0xE0, 0x74, 0xAB, 0x6A, 0x36,
+	0x45, 0x97, 0xE8, 0x99, 0xA0, 0x25, 0x5D, 0xC1,
+	0x64, 0xF3, 0x1C, 0xC5, 0x08, 0x46, 0x85, 0x1D,
+	0xF9, 0xAB, 0x48, 0x19, 0x5D, 0xED, 0x7E, 0xA1,
+	0xB1, 0xD5, 0x10, 0xBD, 0x7E, 0xE7, 0x4D, 0x73,
+	0xFA, 0xF3, 0x6B, 0xC3, 0x1E, 0xCF, 0xA2, 0x68,
+	0x35, 0x90, 0x46, 0xF4, 0xEB, 0x87, 0x9F, 0x92,
+	0x40, 0x09, 0x43, 0x8B, 0x48, 0x1C, 0x6C, 0xD7,
+	0x88, 0x9A, 0x00, 0x2E, 0xD5, 0xEE, 0x38, 0x2B,
+	0xC9, 0x19, 0x0D, 0xA6, 0xFC, 0x02, 0x6E, 0x47,
+	0x95, 0x58, 0xE4, 0x47, 0x56, 0x77, 0xE9, 0xAA,
+	0x9E, 0x30, 0x50, 0xE2, 0x76, 0x56, 0x94, 0xDF,
+	0xC8, 0x1F, 0x56, 0xE8, 0x80, 0xB9, 0x6E, 0x71,
+	0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group18_order[1024] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49,
+	0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13,
+	0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F,
+	0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE,
+	0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87,
+	0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7,
+	0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18,
+	0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C,
+	0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76,
+	0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D,
+	0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5,
+	0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1,
+	0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F,
+	0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76,
+	0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81,
+	0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B,
+	0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41,
+	0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F,
+	0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79,
+	0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F,
+	0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62,
+	0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55,
+	0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC,
+	0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0,
+	0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94,
+	0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B,
+	0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8,
+	0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE,
+	0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19,
+	0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34,
+	0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77,
+	0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B,
+	0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xDF, 0x08, 0xAC,
+	0xBA, 0x51, 0xC9, 0x37, 0x89, 0x7F, 0x72, 0xF2,
+	0x1C, 0x3B, 0xBE, 0x5B, 0x54, 0x99, 0x6F, 0xC6,
+	0x6C, 0x5F, 0x62, 0x68, 0x39, 0xDC, 0x98, 0xDD,
+	0x1D, 0xE4, 0x19, 0x5B, 0x46, 0xCE, 0xE9, 0x80,
+	0x3A, 0x0F, 0xD3, 0xDF, 0xC5, 0x7E, 0x23, 0xF6,
+	0x92, 0xBB, 0x7B, 0x49, 0xB5, 0xD2, 0x12, 0x33,
+	0x1D, 0x55, 0xB1, 0xCE, 0x2D, 0x72, 0x7A, 0xB4,
+	0x1A, 0x11, 0xDA, 0x3A, 0x15, 0xF8, 0xE4, 0xBC,
+	0x11, 0xC7, 0x8B, 0x65, 0xF1, 0xCE, 0xB2, 0x96,
+	0xF1, 0xFE, 0xDC, 0x5F, 0x7E, 0x42, 0x45, 0x6C,
+	0x91, 0x11, 0x17, 0x02, 0x52, 0x01, 0xBE, 0x03,
+	0x89, 0xF5, 0xAB, 0xD4, 0x0D, 0x11, 0xF8, 0x63,
+	0x9A, 0x39, 0xFE, 0x32, 0x36, 0x75, 0x18, 0x35,
+	0xA5, 0xE5, 0xE4, 0x43, 0x17, 0xC1, 0xC2, 0xEE,
+	0xFD, 0x4E, 0xA5, 0xBF, 0xD1, 0x60, 0x43, 0xF4,
+	0x3C, 0xB4, 0x19, 0x81, 0xF6, 0xAD, 0xEE, 0x9D,
+	0x03, 0x15, 0x9E, 0x7A, 0xD9, 0xD1, 0x3C, 0x53,
+	0x36, 0x95, 0x09, 0xFC, 0x1F, 0xA2, 0x7C, 0x16,
+	0xEF, 0x98, 0x87, 0x70, 0x3A, 0x55, 0xB5, 0x1B,
+	0x22, 0xCB, 0xF4, 0x4C, 0xD0, 0x12, 0xAE, 0xE0,
+	0xB2, 0x79, 0x8E, 0x62, 0x84, 0x23, 0x42, 0x8E,
+	0xFC, 0xD5, 0xA4, 0x0C, 0xAE, 0xF6, 0xBF, 0x50,
+	0xD8, 0xEA, 0x88, 0x5E, 0xBF, 0x73, 0xA6, 0xB9,
+	0xFD, 0x79, 0xB5, 0xE1, 0x8F, 0x67, 0xD1, 0x34,
+	0x1A, 0xC8, 0x23, 0x7A, 0x75, 0xC3, 0xCF, 0xC9,
+	0x20, 0x04, 0xA1, 0xC5, 0xA4, 0x0E, 0x36, 0x6B,
+	0xC4, 0x4D, 0x00, 0x17, 0x6A, 0xF7, 0x1C, 0x15,
+	0xE4, 0x8C, 0x86, 0xD3, 0x7E, 0x01, 0x37, 0x23,
+	0xCA, 0xAC, 0x72, 0x23, 0xAB, 0x3B, 0xF4, 0xD5,
+	0x4F, 0x18, 0x28, 0x71, 0x3B, 0x2B, 0x4A, 0x6F,
+	0xE4, 0x0F, 0xAB, 0x74, 0x40, 0x5C, 0xB7, 0x38,
+	0xB0, 0x64, 0xC0, 0x6E, 0xCC, 0x76, 0xE9, 0xEF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/*
+ * RFC 5114, 2.1.
+ * Group 22 - 1024-bit MODP Group with 160-bit Prime Order Subgroup
+ */
+static const u8 dh_group22_generator[] = {
+	0xA4, 0xD1, 0xCB, 0xD5, 0xC3, 0xFD, 0x34, 0x12,
+	0x67, 0x65, 0xA4, 0x42, 0xEF, 0xB9, 0x99, 0x05,
+	0xF8, 0x10, 0x4D, 0xD2, 0x58, 0xAC, 0x50, 0x7F,
+	0xD6, 0x40, 0x6C, 0xFF, 0x14, 0x26, 0x6D, 0x31,
+	0x26, 0x6F, 0xEA, 0x1E, 0x5C, 0x41, 0x56, 0x4B,
+	0x77, 0x7E, 0x69, 0x0F, 0x55, 0x04, 0xF2, 0x13,
+	0x16, 0x02, 0x17, 0xB4, 0xB0, 0x1B, 0x88, 0x6A,
+	0x5E, 0x91, 0x54, 0x7F, 0x9E, 0x27, 0x49, 0xF4,
+	0xD7, 0xFB, 0xD7, 0xD3, 0xB9, 0xA9, 0x2E, 0xE1,
+	0x90, 0x9D, 0x0D, 0x22, 0x63, 0xF8, 0x0A, 0x76,
+	0xA6, 0xA2, 0x4C, 0x08, 0x7A, 0x09, 0x1F, 0x53,
+	0x1D, 0xBF, 0x0A, 0x01, 0x69, 0xB6, 0xA2, 0x8A,
+	0xD6, 0x62, 0xA4, 0xD1, 0x8E, 0x73, 0xAF, 0xA3,
+	0x2D, 0x77, 0x9D, 0x59, 0x18, 0xD0, 0x8B, 0xC8,
+	0x85, 0x8F, 0x4D, 0xCE, 0xF9, 0x7C, 0x2A, 0x24,
+	0x85, 0x5E, 0x6E, 0xEB, 0x22, 0xB3, 0xB2, 0xE5
+};
+static const u8 dh_group22_prime[] = {
+	0xB1, 0x0B, 0x8F, 0x96, 0xA0, 0x80, 0xE0, 0x1D,
+	0xDE, 0x92, 0xDE, 0x5E, 0xAE, 0x5D, 0x54, 0xEC,
+	0x52, 0xC9, 0x9F, 0xBC, 0xFB, 0x06, 0xA3, 0xC6,
+	0x9A, 0x6A, 0x9D, 0xCA, 0x52, 0xD2, 0x3B, 0x61,
+	0x60, 0x73, 0xE2, 0x86, 0x75, 0xA2, 0x3D, 0x18,
+	0x98, 0x38, 0xEF, 0x1E, 0x2E, 0xE6, 0x52, 0xC0,
+	0x13, 0xEC, 0xB4, 0xAE, 0xA9, 0x06, 0x11, 0x23,
+	0x24, 0x97, 0x5C, 0x3C, 0xD4, 0x9B, 0x83, 0xBF,
+	0xAC, 0xCB, 0xDD, 0x7D, 0x90, 0xC4, 0xBD, 0x70,
+	0x98, 0x48, 0x8E, 0x9C, 0x21, 0x9A, 0x73, 0x72,
+	0x4E, 0xFF, 0xD6, 0xFA, 0xE5, 0x64, 0x47, 0x38,
+	0xFA, 0xA3, 0x1A, 0x4F, 0xF5, 0x5B, 0xCC, 0xC0,
+	0xA1, 0x51, 0xAF, 0x5F, 0x0D, 0xC8, 0xB4, 0xBD,
+	0x45, 0xBF, 0x37, 0xDF, 0x36, 0x5C, 0x1A, 0x65,
+	0xE6, 0x8C, 0xFD, 0xA7, 0x6D, 0x4D, 0xA7, 0x08,
+	0xDF, 0x1F, 0xB2, 0xBC, 0x2E, 0x4A, 0x43, 0x71
+};
+static const u8 dh_group22_order[] = {
+	0xF5, 0x18, 0xAA, 0x87, 0x81, 0xA8, 0xDF, 0x27,
+	0x8A, 0xBA, 0x4E, 0x7D, 0x64, 0xB7, 0xCB, 0x9D,
+	0x49, 0x46, 0x23, 0x53
+};
+
+/*
+ * RFC 5114, 2.2.
+ * Group 23 - 2048-bit MODP Group with 224-bit Prime Order Subgroup
+ */
+static const u8 dh_group23_generator[] = {
+	0xAC, 0x40, 0x32, 0xEF, 0x4F, 0x2D, 0x9A, 0xE3,
+	0x9D, 0xF3, 0x0B, 0x5C, 0x8F, 0xFD, 0xAC, 0x50,
+	0x6C, 0xDE, 0xBE, 0x7B, 0x89, 0x99, 0x8C, 0xAF,
+	0x74, 0x86, 0x6A, 0x08, 0xCF, 0xE4, 0xFF, 0xE3,
+	0xA6, 0x82, 0x4A, 0x4E, 0x10, 0xB9, 0xA6, 0xF0,
+	0xDD, 0x92, 0x1F, 0x01, 0xA7, 0x0C, 0x4A, 0xFA,
+	0xAB, 0x73, 0x9D, 0x77, 0x00, 0xC2, 0x9F, 0x52,
+	0xC5, 0x7D, 0xB1, 0x7C, 0x62, 0x0A, 0x86, 0x52,
+	0xBE, 0x5E, 0x90, 0x01, 0xA8, 0xD6, 0x6A, 0xD7,
+	0xC1, 0x76, 0x69, 0x10, 0x19, 0x99, 0x02, 0x4A,
+	0xF4, 0xD0, 0x27, 0x27, 0x5A, 0xC1, 0x34, 0x8B,
+	0xB8, 0xA7, 0x62, 0xD0, 0x52, 0x1B, 0xC9, 0x8A,
+	0xE2, 0x47, 0x15, 0x04, 0x22, 0xEA, 0x1E, 0xD4,
+	0x09, 0x93, 0x9D, 0x54, 0xDA, 0x74, 0x60, 0xCD,
+	0xB5, 0xF6, 0xC6, 0xB2, 0x50, 0x71, 0x7C, 0xBE,
+	0xF1, 0x80, 0xEB, 0x34, 0x11, 0x8E, 0x98, 0xD1,
+	0x19, 0x52, 0x9A, 0x45, 0xD6, 0xF8, 0x34, 0x56,
+	0x6E, 0x30, 0x25, 0xE3, 0x16, 0xA3, 0x30, 0xEF,
+	0xBB, 0x77, 0xA8, 0x6F, 0x0C, 0x1A, 0xB1, 0x5B,
+	0x05, 0x1A, 0xE3, 0xD4, 0x28, 0xC8, 0xF8, 0xAC,
+	0xB7, 0x0A, 0x81, 0x37, 0x15, 0x0B, 0x8E, 0xEB,
+	0x10, 0xE1, 0x83, 0xED, 0xD1, 0x99, 0x63, 0xDD,
+	0xD9, 0xE2, 0x63, 0xE4, 0x77, 0x05, 0x89, 0xEF,
+	0x6A, 0xA2, 0x1E, 0x7F, 0x5F, 0x2F, 0xF3, 0x81,
+	0xB5, 0x39, 0xCC, 0xE3, 0x40, 0x9D, 0x13, 0xCD,
+	0x56, 0x6A, 0xFB, 0xB4, 0x8D, 0x6C, 0x01, 0x91,
+	0x81, 0xE1, 0xBC, 0xFE, 0x94, 0xB3, 0x02, 0x69,
+	0xED, 0xFE, 0x72, 0xFE, 0x9B, 0x6A, 0xA4, 0xBD,
+	0x7B, 0x5A, 0x0F, 0x1C, 0x71, 0xCF, 0xFF, 0x4C,
+	0x19, 0xC4, 0x18, 0xE1, 0xF6, 0xEC, 0x01, 0x79,
+	0x81, 0xBC, 0x08, 0x7F, 0x2A, 0x70, 0x65, 0xB3,
+	0x84, 0xB8, 0x90, 0xD3, 0x19, 0x1F, 0x2B, 0xFA
+};
+static const u8 dh_group23_prime[] = {
+	0xAD, 0x10, 0x7E, 0x1E, 0x91, 0x23, 0xA9, 0xD0,
+	0xD6, 0x60, 0xFA, 0xA7, 0x95, 0x59, 0xC5, 0x1F,
+	0xA2, 0x0D, 0x64, 0xE5, 0x68, 0x3B, 0x9F, 0xD1,
+	0xB5, 0x4B, 0x15, 0x97, 0xB6, 0x1D, 0x0A, 0x75,
+	0xE6, 0xFA, 0x14, 0x1D, 0xF9, 0x5A, 0x56, 0xDB,
+	0xAF, 0x9A, 0x3C, 0x40, 0x7B, 0xA1, 0xDF, 0x15,
+	0xEB, 0x3D, 0x68, 0x8A, 0x30, 0x9C, 0x18, 0x0E,
+	0x1D, 0xE6, 0xB8, 0x5A, 0x12, 0x74, 0xA0, 0xA6,
+	0x6D, 0x3F, 0x81, 0x52, 0xAD, 0x6A, 0xC2, 0x12,
+	0x90, 0x37, 0xC9, 0xED, 0xEF, 0xDA, 0x4D, 0xF8,
+	0xD9, 0x1E, 0x8F, 0xEF, 0x55, 0xB7, 0x39, 0x4B,
+	0x7A, 0xD5, 0xB7, 0xD0, 0xB6, 0xC1, 0x22, 0x07,
+	0xC9, 0xF9, 0x8D, 0x11, 0xED, 0x34, 0xDB, 0xF6,
+	0xC6, 0xBA, 0x0B, 0x2C, 0x8B, 0xBC, 0x27, 0xBE,
+	0x6A, 0x00, 0xE0, 0xA0, 0xB9, 0xC4, 0x97, 0x08,
+	0xB3, 0xBF, 0x8A, 0x31, 0x70, 0x91, 0x88, 0x36,
+	0x81, 0x28, 0x61, 0x30, 0xBC, 0x89, 0x85, 0xDB,
+	0x16, 0x02, 0xE7, 0x14, 0x41, 0x5D, 0x93, 0x30,
+	0x27, 0x82, 0x73, 0xC7, 0xDE, 0x31, 0xEF, 0xDC,
+	0x73, 0x10, 0xF7, 0x12, 0x1F, 0xD5, 0xA0, 0x74,
+	0x15, 0x98, 0x7D, 0x9A, 0xDC, 0x0A, 0x48, 0x6D,
+	0xCD, 0xF9, 0x3A, 0xCC, 0x44, 0x32, 0x83, 0x87,
+	0x31, 0x5D, 0x75, 0xE1, 0x98, 0xC6, 0x41, 0xA4,
+	0x80, 0xCD, 0x86, 0xA1, 0xB9, 0xE5, 0x87, 0xE8,
+	0xBE, 0x60, 0xE6, 0x9C, 0xC9, 0x28, 0xB2, 0xB9,
+	0xC5, 0x21, 0x72, 0xE4, 0x13, 0x04, 0x2E, 0x9B,
+	0x23, 0xF1, 0x0B, 0x0E, 0x16, 0xE7, 0x97, 0x63,
+	0xC9, 0xB5, 0x3D, 0xCF, 0x4B, 0xA8, 0x0A, 0x29,
+	0xE3, 0xFB, 0x73, 0xC1, 0x6B, 0x8E, 0x75, 0xB9,
+	0x7E, 0xF3, 0x63, 0xE2, 0xFF, 0xA3, 0x1F, 0x71,
+	0xCF, 0x9D, 0xE5, 0x38, 0x4E, 0x71, 0xB8, 0x1C,
+	0x0A, 0xC4, 0xDF, 0xFE, 0x0C, 0x10, 0xE6, 0x4F
+};
+static const u8 dh_group23_order[] = {
+	0x80, 0x1C, 0x0D, 0x34, 0xC5, 0x8D, 0x93, 0xFE,
+	0x99, 0x71, 0x77, 0x10, 0x1F, 0x80, 0x53, 0x5A,
+	0x47, 0x38, 0xCE, 0xBC, 0xBF, 0x38, 0x9A, 0x99,
+	0xB3, 0x63, 0x71, 0xEB
+};
+
+/*
+ * RFC 5114, 2.3.
+ * Group 24 - 2048-bit MODP Group with 256-bit Prime Order Subgroup
+ */
+static const u8 dh_group24_generator[] = {
+	0x3F, 0xB3, 0x2C, 0x9B, 0x73, 0x13, 0x4D, 0x0B,
+	0x2E, 0x77, 0x50, 0x66, 0x60, 0xED, 0xBD, 0x48,
+	0x4C, 0xA7, 0xB1, 0x8F, 0x21, 0xEF, 0x20, 0x54,
+	0x07, 0xF4, 0x79, 0x3A, 0x1A, 0x0B, 0xA1, 0x25,
+	0x10, 0xDB, 0xC1, 0x50, 0x77, 0xBE, 0x46, 0x3F,
+	0xFF, 0x4F, 0xED, 0x4A, 0xAC, 0x0B, 0xB5, 0x55,
+	0xBE, 0x3A, 0x6C, 0x1B, 0x0C, 0x6B, 0x47, 0xB1,
+	0xBC, 0x37, 0x73, 0xBF, 0x7E, 0x8C, 0x6F, 0x62,
+	0x90, 0x12, 0x28, 0xF8, 0xC2, 0x8C, 0xBB, 0x18,
+	0xA5, 0x5A, 0xE3, 0x13, 0x41, 0x00, 0x0A, 0x65,
+	0x01, 0x96, 0xF9, 0x31, 0xC7, 0x7A, 0x57, 0xF2,
+	0xDD, 0xF4, 0x63, 0xE5, 0xE9, 0xEC, 0x14, 0x4B,
+	0x77, 0x7D, 0xE6, 0x2A, 0xAA, 0xB8, 0xA8, 0x62,
+	0x8A, 0xC3, 0x76, 0xD2, 0x82, 0xD6, 0xED, 0x38,
+	0x64, 0xE6, 0x79, 0x82, 0x42, 0x8E, 0xBC, 0x83,
+	0x1D, 0x14, 0x34, 0x8F, 0x6F, 0x2F, 0x91, 0x93,
+	0xB5, 0x04, 0x5A, 0xF2, 0x76, 0x71, 0x64, 0xE1,
+	0xDF, 0xC9, 0x67, 0xC1, 0xFB, 0x3F, 0x2E, 0x55,
+	0xA4, 0xBD, 0x1B, 0xFF, 0xE8, 0x3B, 0x9C, 0x80,
+	0xD0, 0x52, 0xB9, 0x85, 0xD1, 0x82, 0xEA, 0x0A,
+	0xDB, 0x2A, 0x3B, 0x73, 0x13, 0xD3, 0xFE, 0x14,
+	0xC8, 0x48, 0x4B, 0x1E, 0x05, 0x25, 0x88, 0xB9,
+	0xB7, 0xD2, 0xBB, 0xD2, 0xDF, 0x01, 0x61, 0x99,
+	0xEC, 0xD0, 0x6E, 0x15, 0x57, 0xCD, 0x09, 0x15,
+	0xB3, 0x35, 0x3B, 0xBB, 0x64, 0xE0, 0xEC, 0x37,
+	0x7F, 0xD0, 0x28, 0x37, 0x0D, 0xF9, 0x2B, 0x52,
+	0xC7, 0x89, 0x14, 0x28, 0xCD, 0xC6, 0x7E, 0xB6,
+	0x18, 0x4B, 0x52, 0x3D, 0x1D, 0xB2, 0x46, 0xC3,
+	0x2F, 0x63, 0x07, 0x84, 0x90, 0xF0, 0x0E, 0xF8,
+	0xD6, 0x47, 0xD1, 0x48, 0xD4, 0x79, 0x54, 0x51,
+	0x5E, 0x23, 0x27, 0xCF, 0xEF, 0x98, 0xC5, 0x82,
+	0x66, 0x4B, 0x4C, 0x0F, 0x6C, 0xC4, 0x16, 0x59
+};
+static const u8 dh_group24_prime[] = {
+	0x87, 0xA8, 0xE6, 0x1D, 0xB4, 0xB6, 0x66, 0x3C,
+	0xFF, 0xBB, 0xD1, 0x9C, 0x65, 0x19, 0x59, 0x99,
+	0x8C, 0xEE, 0xF6, 0x08, 0x66, 0x0D, 0xD0, 0xF2,
+	0x5D, 0x2C, 0xEE, 0xD4, 0x43, 0x5E, 0x3B, 0x00,
+	0xE0, 0x0D, 0xF8, 0xF1, 0xD6, 0x19, 0x57, 0xD4,
+	0xFA, 0xF7, 0xDF, 0x45, 0x61, 0xB2, 0xAA, 0x30,
+	0x16, 0xC3, 0xD9, 0x11, 0x34, 0x09, 0x6F, 0xAA,
+	0x3B, 0xF4, 0x29, 0x6D, 0x83, 0x0E, 0x9A, 0x7C,
+	0x20, 0x9E, 0x0C, 0x64, 0x97, 0x51, 0x7A, 0xBD,
+	0x5A, 0x8A, 0x9D, 0x30, 0x6B, 0xCF, 0x67, 0xED,
+	0x91, 0xF9, 0xE6, 0x72, 0x5B, 0x47, 0x58, 0xC0,
+	0x22, 0xE0, 0xB1, 0xEF, 0x42, 0x75, 0xBF, 0x7B,
+	0x6C, 0x5B, 0xFC, 0x11, 0xD4, 0x5F, 0x90, 0x88,
+	0xB9, 0x41, 0xF5, 0x4E, 0xB1, 0xE5, 0x9B, 0xB8,
+	0xBC, 0x39, 0xA0, 0xBF, 0x12, 0x30, 0x7F, 0x5C,
+	0x4F, 0xDB, 0x70, 0xC5, 0x81, 0xB2, 0x3F, 0x76,
+	0xB6, 0x3A, 0xCA, 0xE1, 0xCA, 0xA6, 0xB7, 0x90,
+	0x2D, 0x52, 0x52, 0x67, 0x35, 0x48, 0x8A, 0x0E,
+	0xF1, 0x3C, 0x6D, 0x9A, 0x51, 0xBF, 0xA4, 0xAB,
+	0x3A, 0xD8, 0x34, 0x77, 0x96, 0x52, 0x4D, 0x8E,
+	0xF6, 0xA1, 0x67, 0xB5, 0xA4, 0x18, 0x25, 0xD9,
+	0x67, 0xE1, 0x44, 0xE5, 0x14, 0x05, 0x64, 0x25,
+	0x1C, 0xCA, 0xCB, 0x83, 0xE6, 0xB4, 0x86, 0xF6,
+	0xB3, 0xCA, 0x3F, 0x79, 0x71, 0x50, 0x60, 0x26,
+	0xC0, 0xB8, 0x57, 0xF6, 0x89, 0x96, 0x28, 0x56,
+	0xDE, 0xD4, 0x01, 0x0A, 0xBD, 0x0B, 0xE6, 0x21,
+	0xC3, 0xA3, 0x96, 0x0A, 0x54, 0xE7, 0x10, 0xC3,
+	0x75, 0xF2, 0x63, 0x75, 0xD7, 0x01, 0x41, 0x03,
+	0xA4, 0xB5, 0x43, 0x30, 0xC1, 0x98, 0xAF, 0x12,
+	0x61, 0x16, 0xD2, 0x27, 0x6E, 0x11, 0x71, 0x5F,
+	0x69, 0x38, 0x77, 0xFA, 0xD7, 0xEF, 0x09, 0xCA,
+	0xDB, 0x09, 0x4A, 0xE9, 0x1E, 0x1A, 0x15, 0x97
+};
+static const u8 dh_group24_order[] = {
+	0x8C, 0xF8, 0x36, 0x42, 0xA7, 0x09, 0xA0, 0x97,
+	0xB4, 0x47, 0x99, 0x76, 0x40, 0x12, 0x9D, 0xA2,
+	0x99, 0xB1, 0xA4, 0x7D, 0x1E, 0xB3, 0x75, 0x0B,
+	0xA3, 0x08, 0xB0, 0xFE, 0x64, 0xF5, 0xFB, 0xD3
+};
+
+#endif /* ALL_DH_GROUPS */
+
+
+#define DH_GROUP(id,safe) \
+{ id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \
+dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \
+dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe }
+		
+
+static const struct dh_group dh_groups[] = {
+	DH_GROUP(5, 1),
+#ifdef ALL_DH_GROUPS
+	DH_GROUP(1, 1),
+	DH_GROUP(2, 1),
+	DH_GROUP(14, 1),
+	DH_GROUP(15, 1),
+	DH_GROUP(16, 1),
+	DH_GROUP(17, 1),
+	DH_GROUP(18, 1),
+	DH_GROUP(22, 0),
+	DH_GROUP(23, 0),
+	DH_GROUP(24, 0)
+#endif /* ALL_DH_GROUPS */
+};
+
+#define NUM_DH_GROUPS ARRAY_SIZE(dh_groups)
+
+
+const struct dh_group * dh_groups_get(int id)
+{
+	size_t i;
+
+	for (i = 0; i < NUM_DH_GROUPS; i++) {
+		if (dh_groups[i].id == id)
+			return &dh_groups[i];
+	}
+	return NULL;
+}
+
+
+/**
+ * dh_init - Initialize Diffie-Hellman handshake
+ * @dh: Selected Diffie-Hellman group
+ * @priv: Pointer for returning Diffie-Hellman private key
+ * Returns: Diffie-Hellman public value
+ */
+struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
+{
+	struct wpabuf *pv;
+	size_t pv_len;
+
+	if (dh == NULL)
+		return NULL;
+
+	wpabuf_clear_free(*priv);
+	*priv = wpabuf_alloc(dh->prime_len);
+	if (*priv == NULL)
+		return NULL;
+
+	if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len))
+	{
+		wpabuf_clear_free(*priv);
+		*priv = NULL;
+		return NULL;
+	}
+
+	if (os_memcmp(wpabuf_head(*priv), dh->prime, dh->prime_len) > 0) {
+		/* Make sure private value is smaller than prime */
+		*(wpabuf_mhead_u8(*priv)) = 0;
+	}
+	wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv);
+
+	pv_len = dh->prime_len;
+	pv = wpabuf_alloc(pv_len);
+	if (pv == NULL)
+		return NULL;
+	if (crypto_mod_exp(dh->generator, dh->generator_len,
+			   wpabuf_head(*priv), wpabuf_len(*priv),
+			   dh->prime, dh->prime_len, wpabuf_mhead(pv),
+			   &pv_len) < 0) {
+		wpabuf_clear_free(pv);
+		wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
+		return NULL;
+	}
+	wpabuf_put(pv, pv_len);
+	wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv);
+
+	return pv;
+}
+
+
+/**
+ * dh_derive_shared - Derive shared Diffie-Hellman key
+ * @peer_public: Diffie-Hellman public value from peer
+ * @own_private: Diffie-Hellman private key from dh_init()
+ * @dh: Selected Diffie-Hellman group
+ * Returns: Diffie-Hellman shared key
+ */
+struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public,
+				 const struct wpabuf *own_private,
+				 const struct dh_group *dh)
+{
+	struct wpabuf *shared;
+	size_t shared_len;
+
+	if (dh == NULL || peer_public == NULL || own_private == NULL)
+		return NULL;
+
+	shared_len = dh->prime_len;
+	shared = wpabuf_alloc(shared_len);
+	if (shared == NULL)
+		return NULL;
+	if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public),
+			   wpabuf_head(own_private), wpabuf_len(own_private),
+			   dh->prime, dh->prime_len,
+			   wpabuf_mhead(shared), &shared_len) < 0) {
+		wpabuf_clear_free(shared);
+		wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
+		return NULL;
+	}
+	wpabuf_put(shared, shared_len);
+	wpa_hexdump_buf_key(MSG_DEBUG, "DH: shared key", shared);
+
+	return shared;
+}
diff --git a/hostap/src/crypto/dh_groups.h b/hostap/src/crypto/dh_groups.h
new file mode 100644
index 0000000..d0e74b9
--- /dev/null
+++ b/hostap/src/crypto/dh_groups.h
@@ -0,0 +1,29 @@
+/*
+ * Diffie-Hellman groups
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DH_GROUPS_H
+#define DH_GROUPS_H
+
+struct dh_group {
+	int id;
+	const u8 *generator;
+	size_t generator_len;
+	const u8 *prime;
+	size_t prime_len;
+	const u8 *order;
+	size_t order_len;
+	unsigned int safe_prime:1;
+};
+
+const struct dh_group * dh_groups_get(int id);
+struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv);
+struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public,
+				 const struct wpabuf *own_private,
+				 const struct dh_group *dh);
+
+#endif /* DH_GROUPS_H */
diff --git a/hostap/src/crypto/fips_prf_internal.c b/hostap/src/crypto/fips_prf_internal.c
new file mode 100644
index 0000000..a4bf50a
--- /dev/null
+++ b/hostap/src/crypto/fips_prf_internal.c
@@ -0,0 +1,69 @@
+/*
+ * FIPS 186-2 PRF for internal crypto implementation
+ * Copyright (c) 2006-2007, 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 "sha1.h"
+#include "sha1_i.h"
+#include "crypto.h"
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+	u8 xkey[64];
+	u32 t[5], _t[5];
+	int i, j, m, k;
+	u8 *xpos = x;
+	u32 carry;
+
+	if (seed_len < sizeof(xkey))
+		os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
+	else
+		seed_len = sizeof(xkey);
+
+	/* FIPS 186-2 + change notice 1 */
+
+	os_memcpy(xkey, seed, seed_len);
+	t[0] = 0x67452301;
+	t[1] = 0xEFCDAB89;
+	t[2] = 0x98BADCFE;
+	t[3] = 0x10325476;
+	t[4] = 0xC3D2E1F0;
+
+	m = xlen / 40;
+	for (j = 0; j < m; j++) {
+		/* XSEED_j = 0 */
+		for (i = 0; i < 2; i++) {
+			/* XVAL = (XKEY + XSEED_j) mod 2^b */
+
+			/* w_i = G(t, XVAL) */
+			os_memcpy(_t, t, 20);
+			SHA1Transform(_t, xkey);
+			_t[0] = host_to_be32(_t[0]);
+			_t[1] = host_to_be32(_t[1]);
+			_t[2] = host_to_be32(_t[2]);
+			_t[3] = host_to_be32(_t[3]);
+			_t[4] = host_to_be32(_t[4]);
+			os_memcpy(xpos, _t, 20);
+
+			/* XKEY = (1 + XKEY + w_i) mod 2^b */
+			carry = 1;
+			for (k = 19; k >= 0; k--) {
+				carry += xkey[k] + xpos[k];
+				xkey[k] = carry & 0xff;
+				carry >>= 8;
+			}
+
+			xpos += SHA1_MAC_LEN;
+		}
+		/* x_j = w_0|w_1 */
+	}
+
+	return 0;
+}
diff --git a/hostap/src/crypto/fips_prf_openssl.c b/hostap/src/crypto/fips_prf_openssl.c
new file mode 100644
index 0000000..fb03efc
--- /dev/null
+++ b/hostap/src/crypto/fips_prf_openssl.c
@@ -0,0 +1,86 @@
+/*
+ * FIPS 186-2 PRF for libcrypto
+ * Copyright (c) 2004-2005, 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"
+
+
+static void sha1_transform(u32 *state, const u8 data[64])
+{
+	SHA_CTX context;
+	os_memset(&context, 0, sizeof(context));
+	context.h0 = state[0];
+	context.h1 = state[1];
+	context.h2 = state[2];
+	context.h3 = state[3];
+	context.h4 = state[4];
+	SHA1_Transform(&context, data);
+	state[0] = context.h0;
+	state[1] = context.h1;
+	state[2] = context.h2;
+	state[3] = context.h3;
+	state[4] = context.h4;
+}
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+	u8 xkey[64];
+	u32 t[5], _t[5];
+	int i, j, m, k;
+	u8 *xpos = x;
+	u32 carry;
+
+	if (seed_len < sizeof(xkey))
+		os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
+	else
+		seed_len = sizeof(xkey);
+
+	/* FIPS 186-2 + change notice 1 */
+
+	os_memcpy(xkey, seed, seed_len);
+	t[0] = 0x67452301;
+	t[1] = 0xEFCDAB89;
+	t[2] = 0x98BADCFE;
+	t[3] = 0x10325476;
+	t[4] = 0xC3D2E1F0;
+
+	m = xlen / 40;
+	for (j = 0; j < m; j++) {
+		/* XSEED_j = 0 */
+		for (i = 0; i < 2; i++) {
+			/* XVAL = (XKEY + XSEED_j) mod 2^b */
+
+			/* w_i = G(t, XVAL) */
+			os_memcpy(_t, t, 20);
+			sha1_transform(_t, xkey);
+			_t[0] = host_to_be32(_t[0]);
+			_t[1] = host_to_be32(_t[1]);
+			_t[2] = host_to_be32(_t[2]);
+			_t[3] = host_to_be32(_t[3]);
+			_t[4] = host_to_be32(_t[4]);
+			os_memcpy(xpos, _t, 20);
+
+			/* XKEY = (1 + XKEY + w_i) mod 2^b */
+			carry = 1;
+			for (k = 19; k >= 0; k--) {
+				carry += xkey[k] + xpos[k];
+				xkey[k] = carry & 0xff;
+				carry >>= 8;
+			}
+
+			xpos += 20;
+		}
+		/* x_j = w_0|w_1 */
+	}
+
+	return 0;
+}
diff --git a/hostap/src/crypto/md4-internal.c b/hostap/src/crypto/md4-internal.c
new file mode 100644
index 0000000..cd5e6ca
--- /dev/null
+++ b/hostap/src/crypto/md4-internal.c
@@ -0,0 +1,272 @@
+/*
+ * MD4 hash implementation
+ * Copyright (c) 2006, 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"
+
+#define	MD4_BLOCK_LENGTH		64
+#define	MD4_DIGEST_LENGTH		16
+
+typedef struct MD4Context {
+	u32 state[4];			/* state */
+	u64 count;			/* number of bits, mod 2^64 */
+	u8 buffer[MD4_BLOCK_LENGTH];	/* input buffer */
+} MD4_CTX;
+
+
+static void MD4Init(MD4_CTX *ctx);
+static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len);
+static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx);
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	MD4_CTX ctx;
+	size_t i;
+
+	MD4Init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		MD4Update(&ctx, addr[i], len[i]);
+	MD4Final(mac, &ctx);
+	return 0;
+}
+
+
+/* ===== start - public domain MD4 implementation ===== */
+/*	$OpenBSD: md4.c,v 1.7 2005/08/08 08:05:35 espie Exp $	*/
+
+/*
+ * This code implements the MD4 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.	This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ * Todd C. Miller modified the MD5 code to do MD4 based on RFC 1186.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD4Context structure, pass it to MD4Init, call MD4Update as
+ * needed on buffers full of bytes, and then call MD4Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#define	MD4_DIGEST_STRING_LENGTH	(MD4_DIGEST_LENGTH * 2 + 1)
+
+
+static void
+MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]);
+
+#define PUT_64BIT_LE(cp, value) do {					\
+	(cp)[7] = (value) >> 56;					\
+	(cp)[6] = (value) >> 48;					\
+	(cp)[5] = (value) >> 40;					\
+	(cp)[4] = (value) >> 32;					\
+	(cp)[3] = (value) >> 24;					\
+	(cp)[2] = (value) >> 16;					\
+	(cp)[1] = (value) >> 8;						\
+	(cp)[0] = (value); } while (0)
+
+#define PUT_32BIT_LE(cp, value) do {					\
+	(cp)[3] = (value) >> 24;					\
+	(cp)[2] = (value) >> 16;					\
+	(cp)[1] = (value) >> 8;						\
+	(cp)[0] = (value); } while (0)
+
+static u8 PADDING[MD4_BLOCK_LENGTH] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * Start MD4 accumulation.
+ * Set bit count to 0 and buffer to mysterious initialization constants.
+ */
+static void MD4Init(MD4_CTX *ctx)
+{
+	ctx->count = 0;
+	ctx->state[0] = 0x67452301;
+	ctx->state[1] = 0xefcdab89;
+	ctx->state[2] = 0x98badcfe;
+	ctx->state[3] = 0x10325476;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len)
+{
+	size_t have, need;
+
+	/* Check how many bytes we already have and how many more we need. */
+	have = (size_t)((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1));
+	need = MD4_BLOCK_LENGTH - have;
+
+	/* Update bitcount */
+	ctx->count += (u64)len << 3;
+
+	if (len >= need) {
+		if (have != 0) {
+			os_memcpy(ctx->buffer + have, input, need);
+			MD4Transform(ctx->state, ctx->buffer);
+			input += need;
+			len -= need;
+			have = 0;
+		}
+
+		/* Process data in MD4_BLOCK_LENGTH-byte chunks. */
+		while (len >= MD4_BLOCK_LENGTH) {
+			MD4Transform(ctx->state, input);
+			input += MD4_BLOCK_LENGTH;
+			len -= MD4_BLOCK_LENGTH;
+		}
+	}
+
+	/* Handle any remaining bytes of data. */
+	if (len != 0)
+		os_memcpy(ctx->buffer + have, input, len);
+}
+
+/*
+ * Pad pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+static void MD4Pad(MD4_CTX *ctx)
+{
+	u8 count[8];
+	size_t padlen;
+
+	/* Convert count to 8 bytes in little endian order. */
+	PUT_64BIT_LE(count, ctx->count);
+
+	/* Pad out to 56 mod 64. */
+	padlen = MD4_BLOCK_LENGTH -
+	    ((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1));
+	if (padlen < 1 + 8)
+		padlen += MD4_BLOCK_LENGTH;
+	MD4Update(ctx, PADDING, padlen - 8);		/* padlen - 8 <= 64 */
+	MD4Update(ctx, count, 8);
+}
+
+/*
+ * Final wrapup--call MD4Pad, fill in digest and zero out ctx.
+ */
+static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx)
+{
+	int i;
+
+	MD4Pad(ctx);
+	if (digest != NULL) {
+		for (i = 0; i < 4; i++)
+			PUT_32BIT_LE(digest + i * 4, ctx->state[i]);
+		os_memset(ctx, 0, sizeof(*ctx));
+	}
+}
+
+
+/* The three core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) ((x & y) | (x & z) | (y & z))
+#define F3(x, y, z) (x ^ y ^ z)
+
+/* This is the central step in the MD4 algorithm. */
+#define MD4STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s) )
+
+/*
+ * The core of the MD4 algorithm, this alters an existing MD4 hash to
+ * reflect the addition of 16 longwords of new data.  MD4Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH])
+{
+	u32 a, b, c, d, in[MD4_BLOCK_LENGTH / 4];
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+	os_memcpy(in, block, sizeof(in));
+#else
+	for (a = 0; a < MD4_BLOCK_LENGTH / 4; a++) {
+		in[a] = (u32)(
+		    (u32)(block[a * 4 + 0]) |
+		    (u32)(block[a * 4 + 1]) <<  8 |
+		    (u32)(block[a * 4 + 2]) << 16 |
+		    (u32)(block[a * 4 + 3]) << 24);
+	}
+#endif
+
+	a = state[0];
+	b = state[1];
+	c = state[2];
+	d = state[3];
+
+	MD4STEP(F1, a, b, c, d, in[ 0],  3);
+	MD4STEP(F1, d, a, b, c, in[ 1],  7);
+	MD4STEP(F1, c, d, a, b, in[ 2], 11);
+	MD4STEP(F1, b, c, d, a, in[ 3], 19);
+	MD4STEP(F1, a, b, c, d, in[ 4],  3);
+	MD4STEP(F1, d, a, b, c, in[ 5],  7);
+	MD4STEP(F1, c, d, a, b, in[ 6], 11);
+	MD4STEP(F1, b, c, d, a, in[ 7], 19);
+	MD4STEP(F1, a, b, c, d, in[ 8],  3);
+	MD4STEP(F1, d, a, b, c, in[ 9],  7);
+	MD4STEP(F1, c, d, a, b, in[10], 11);
+	MD4STEP(F1, b, c, d, a, in[11], 19);
+	MD4STEP(F1, a, b, c, d, in[12],  3);
+	MD4STEP(F1, d, a, b, c, in[13],  7);
+	MD4STEP(F1, c, d, a, b, in[14], 11);
+	MD4STEP(F1, b, c, d, a, in[15], 19);
+
+	MD4STEP(F2, a, b, c, d, in[ 0] + 0x5a827999,  3);
+	MD4STEP(F2, d, a, b, c, in[ 4] + 0x5a827999,  5);
+	MD4STEP(F2, c, d, a, b, in[ 8] + 0x5a827999,  9);
+	MD4STEP(F2, b, c, d, a, in[12] + 0x5a827999, 13);
+	MD4STEP(F2, a, b, c, d, in[ 1] + 0x5a827999,  3);
+	MD4STEP(F2, d, a, b, c, in[ 5] + 0x5a827999,  5);
+	MD4STEP(F2, c, d, a, b, in[ 9] + 0x5a827999,  9);
+	MD4STEP(F2, b, c, d, a, in[13] + 0x5a827999, 13);
+	MD4STEP(F2, a, b, c, d, in[ 2] + 0x5a827999,  3);
+	MD4STEP(F2, d, a, b, c, in[ 6] + 0x5a827999,  5);
+	MD4STEP(F2, c, d, a, b, in[10] + 0x5a827999,  9);
+	MD4STEP(F2, b, c, d, a, in[14] + 0x5a827999, 13);
+	MD4STEP(F2, a, b, c, d, in[ 3] + 0x5a827999,  3);
+	MD4STEP(F2, d, a, b, c, in[ 7] + 0x5a827999,  5);
+	MD4STEP(F2, c, d, a, b, in[11] + 0x5a827999,  9);
+	MD4STEP(F2, b, c, d, a, in[15] + 0x5a827999, 13);
+
+	MD4STEP(F3, a, b, c, d, in[ 0] + 0x6ed9eba1,  3);
+	MD4STEP(F3, d, a, b, c, in[ 8] + 0x6ed9eba1,  9);
+	MD4STEP(F3, c, d, a, b, in[ 4] + 0x6ed9eba1, 11);
+	MD4STEP(F3, b, c, d, a, in[12] + 0x6ed9eba1, 15);
+	MD4STEP(F3, a, b, c, d, in[ 2] + 0x6ed9eba1,  3);
+	MD4STEP(F3, d, a, b, c, in[10] + 0x6ed9eba1,  9);
+	MD4STEP(F3, c, d, a, b, in[ 6] + 0x6ed9eba1, 11);
+	MD4STEP(F3, b, c, d, a, in[14] + 0x6ed9eba1, 15);
+	MD4STEP(F3, a, b, c, d, in[ 1] + 0x6ed9eba1,  3);
+	MD4STEP(F3, d, a, b, c, in[ 9] + 0x6ed9eba1,  9);
+	MD4STEP(F3, c, d, a, b, in[ 5] + 0x6ed9eba1, 11);
+	MD4STEP(F3, b, c, d, a, in[13] + 0x6ed9eba1, 15);
+	MD4STEP(F3, a, b, c, d, in[ 3] + 0x6ed9eba1,  3);
+	MD4STEP(F3, d, a, b, c, in[11] + 0x6ed9eba1,  9);
+	MD4STEP(F3, c, d, a, b, in[ 7] + 0x6ed9eba1, 11);
+	MD4STEP(F3, b, c, d, a, in[15] + 0x6ed9eba1, 15);
+
+	state[0] += a;
+	state[1] += b;
+	state[2] += c;
+	state[3] += d;
+}
+/* ===== end - public domain MD4 implementation ===== */
diff --git a/hostap/src/crypto/md5-internal.c b/hostap/src/crypto/md5-internal.c
new file mode 100644
index 0000000..f0a2a5d
--- /dev/null
+++ b/hostap/src/crypto/md5-internal.c
@@ -0,0 +1,287 @@
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2005, 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 "md5.h"
+#include "md5_i.h"
+#include "crypto.h"
+
+
+static void MD5Transform(u32 buf[4], u32 const in[16]);
+
+
+typedef struct MD5Context MD5_CTX;
+
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	MD5_CTX ctx;
+	size_t i;
+
+	MD5Init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		MD5Update(&ctx, addr[i], len[i]);
+	MD5Final(mac, &ctx);
+	return 0;
+}
+
+
+/* ===== start - public domain MD5 implementation ===== */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#ifndef WORDS_BIGENDIAN
+#define byteReverse(buf, len)	/* Nothing */
+#else
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+    u32 t;
+    do {
+	t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+	    ((unsigned) buf[1] << 8 | buf[0]);
+	*(u32 *) buf = t;
+	buf += 4;
+    } while (--longs);
+}
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    u32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((u32) len << 3)) < t)
+	ctx->bits[1]++;		/* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+	unsigned char *p = (unsigned char *) ctx->in + t;
+
+	t = 64 - t;
+	if (len < t) {
+	    os_memcpy(p, buf, len);
+	    return;
+	}
+	os_memcpy(p, buf, t);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+	buf += t;
+	len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+	os_memcpy(ctx->in, buf, 64);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+	buf += 64;
+	len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    os_memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+	/* Two lots of padding:  Pad the first block to 64 bytes */
+	os_memset(p, 0, count);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+
+	/* Now fill the next block with 56 bytes */
+	os_memset(ctx->in, 0, 56);
+    } else {
+	/* Pad block to 56 bytes */
+	os_memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((u32 *) aliasing_hide_typecast(ctx->in, u32))[14] = ctx->bits[0];
+    ((u32 *) aliasing_hide_typecast(ctx->in, u32))[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (u32 *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    os_memcpy(digest, ctx->buf, 16);
+    os_memset(ctx, 0, sizeof(*ctx));	/* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(u32 buf[4], u32 const in[16])
+{
+    register u32 a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+/* ===== end - public domain MD5 implementation ===== */
diff --git a/hostap/src/crypto/md5.c b/hostap/src/crypto/md5.c
new file mode 100644
index 0000000..f64dfd3
--- /dev/null
+++ b/hostap/src/crypto/md5.c
@@ -0,0 +1,109 @@
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2005, 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 "md5.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_md5_vector - HMAC-MD5 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	u8 k_pad[64]; /* padding - key XORd with ipad/opad */
+	u8 tk[16];
+	const u8 *_addr[6];
+	size_t i, _len[6];
+	int res;
+
+	if (num_elem > 5) {
+		/*
+		 * Fixed limit on the number of fragments to avoid having to
+		 * allocate memory (which could fail).
+		 */
+		return -1;
+	}
+
+        /* if key is longer than 64 bytes reset it to key = MD5(key) */
+        if (key_len > 64) {
+		if (md5_vector(1, &key, &key_len, tk))
+			return -1;
+		key = tk;
+		key_len = 16;
+        }
+
+	/* the HMAC_MD5 transform looks like:
+	 *
+	 * MD5(K XOR opad, MD5(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected */
+
+	/* start out by storing key in ipad */
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+
+	/* XOR key with ipad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x36;
+
+	/* perform inner MD5 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	for (i = 0; i < num_elem; i++) {
+		_addr[i + 1] = addr[i];
+		_len[i + 1] = len[i];
+	}
+	if (md5_vector(1 + num_elem, _addr, _len, mac))
+		return -1;
+
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with opad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x5c;
+
+	/* perform outer MD5 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	_addr[1] = mac;
+	_len[1] = MD5_MAC_LEN;
+	res = md5_vector(2, _addr, _len, mac);
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memset(tk, 0, sizeof(tk));
+	return res;
+}
+
+
+/**
+ * hmac_md5 - HMAC-MD5 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	      u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
diff --git a/hostap/src/crypto/md5.h b/hostap/src/crypto/md5.h
new file mode 100644
index 0000000..33f8426
--- /dev/null
+++ b/hostap/src/crypto/md5.h
@@ -0,0 +1,19 @@
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+#define MD5_MAC_LEN 16
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac);
+
+#endif /* MD5_H */
diff --git a/hostap/src/crypto/md5_i.h b/hostap/src/crypto/md5_i.h
new file mode 100644
index 0000000..7dfc100
--- /dev/null
+++ b/hostap/src/crypto/md5_i.h
@@ -0,0 +1,23 @@
+/*
+ * MD5 internal definitions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MD5_I_H
+#define MD5_I_H
+
+struct MD5Context {
+	u32 buf[4];
+	u32 bits[2];
+	u8 in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+	       unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+
+#endif /* MD5_I_H */
diff --git a/hostap/src/crypto/milenage.c b/hostap/src/crypto/milenage.c
new file mode 100644
index 0000000..6edea57
--- /dev/null
+++ b/hostap/src/crypto/milenage.c
@@ -0,0 +1,323 @@
+/*
+ * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
+ * Copyright (c) 2006-2007 <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements an example authentication algorithm defined for 3GPP
+ * AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow
+ * EAP-AKA to be tested properly with real USIM cards.
+ *
+ * This implementations assumes that the r1..r5 and c1..c5 constants defined in
+ * TS 35.206 are used, i.e., r1=64, r2=0, r3=32, r4=64, r5=96, c1=00..00,
+ * c2=00..01, c3=00..02, c4=00..04, c5=00..08. The block cipher is assumed to
+ * be AES (Rijndael).
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "milenage.h"
+
+
+/**
+ * milenage_f1 - Milenage f1 and f1* algorithms
+ * @opc: OPc = 128-bit value derived from OP and K
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @sqn: SQN = 48-bit sequence number
+ * @amf: AMF = 16-bit authentication management field
+ * @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL
+ * @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL
+ * Returns: 0 on success, -1 on failure
+ */
+int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand,
+		const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s)
+{
+	u8 tmp1[16], tmp2[16], tmp3[16];
+	int i;
+
+	/* tmp1 = TEMP = E_K(RAND XOR OP_C) */
+	for (i = 0; i < 16; i++)
+		tmp1[i] = _rand[i] ^ opc[i];
+	if (aes_128_encrypt_block(k, tmp1, tmp1))
+		return -1;
+
+	/* tmp2 = IN1 = SQN || AMF || SQN || AMF */
+	os_memcpy(tmp2, sqn, 6);
+	os_memcpy(tmp2 + 6, amf, 2);
+	os_memcpy(tmp2 + 8, tmp2, 8);
+
+	/* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */
+
+	/* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */
+	for (i = 0; i < 16; i++)
+		tmp3[(i + 8) % 16] = tmp2[i] ^ opc[i];
+	/* XOR with TEMP = E_K(RAND XOR OP_C) */
+	for (i = 0; i < 16; i++)
+		tmp3[i] ^= tmp1[i];
+	/* XOR with c1 (= ..00, i.e., NOP) */
+
+	/* f1 || f1* = E_K(tmp3) XOR OP_c */
+	if (aes_128_encrypt_block(k, tmp3, tmp1))
+		return -1;
+	for (i = 0; i < 16; i++)
+		tmp1[i] ^= opc[i];
+	if (mac_a)
+		os_memcpy(mac_a, tmp1, 8); /* f1 */
+	if (mac_s)
+		os_memcpy(mac_s, tmp1 + 8, 8); /* f1* */
+	return 0;
+}
+
+
+/**
+ * milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms
+ * @opc: OPc = 128-bit value derived from OP and K
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL
+ * @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL
+ * Returns: 0 on success, -1 on failure
+ */
+int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand,
+		   u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar)
+{
+	u8 tmp1[16], tmp2[16], tmp3[16];
+	int i;
+
+	/* tmp2 = TEMP = E_K(RAND XOR OP_C) */
+	for (i = 0; i < 16; i++)
+		tmp1[i] = _rand[i] ^ opc[i];
+	if (aes_128_encrypt_block(k, tmp1, tmp2))
+		return -1;
+
+	/* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */
+	/* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */
+	/* OUT4 = E_K(rot(TEMP XOR OP_C, r4) XOR c4) XOR OP_C */
+	/* OUT5 = E_K(rot(TEMP XOR OP_C, r5) XOR c5) XOR OP_C */
+
+	/* f2 and f5 */
+	/* rotate by r2 (= 0, i.e., NOP) */
+	for (i = 0; i < 16; i++)
+		tmp1[i] = tmp2[i] ^ opc[i];
+	tmp1[15] ^= 1; /* XOR c2 (= ..01) */
+	/* f5 || f2 = E_K(tmp1) XOR OP_c */
+	if (aes_128_encrypt_block(k, tmp1, tmp3))
+		return -1;
+	for (i = 0; i < 16; i++)
+		tmp3[i] ^= opc[i];
+	if (res)
+		os_memcpy(res, tmp3 + 8, 8); /* f2 */
+	if (ak)
+		os_memcpy(ak, tmp3, 6); /* f5 */
+
+	/* f3 */
+	if (ck) {
+		/* rotate by r3 = 0x20 = 4 bytes */
+		for (i = 0; i < 16; i++)
+			tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i];
+		tmp1[15] ^= 2; /* XOR c3 (= ..02) */
+		if (aes_128_encrypt_block(k, tmp1, ck))
+			return -1;
+		for (i = 0; i < 16; i++)
+			ck[i] ^= opc[i];
+	}
+
+	/* f4 */
+	if (ik) {
+		/* rotate by r4 = 0x40 = 8 bytes */
+		for (i = 0; i < 16; i++)
+			tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i];
+		tmp1[15] ^= 4; /* XOR c4 (= ..04) */
+		if (aes_128_encrypt_block(k, tmp1, ik))
+			return -1;
+		for (i = 0; i < 16; i++)
+			ik[i] ^= opc[i];
+	}
+
+	/* f5* */
+	if (akstar) {
+		/* rotate by r5 = 0x60 = 12 bytes */
+		for (i = 0; i < 16; i++)
+			tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i];
+		tmp1[15] ^= 8; /* XOR c5 (= ..08) */
+		if (aes_128_encrypt_block(k, tmp1, tmp1))
+			return -1;
+		for (i = 0; i < 6; i++)
+			akstar[i] = tmp1[i] ^ opc[i];
+	}
+
+	return 0;
+}
+
+
+/**
+ * milenage_generate - Generate AKA AUTN,IK,CK,RES
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @amf: AMF = 16-bit authentication management field
+ * @k: K = 128-bit subscriber key
+ * @sqn: SQN = 48-bit sequence number
+ * @_rand: RAND = 128-bit random challenge
+ * @autn: Buffer for AUTN = 128-bit authentication token
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @res_len: Max length for res; set to used length or 0 on failure
+ */
+void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k,
+		       const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik,
+		       u8 *ck, u8 *res, size_t *res_len)
+{
+	int i;
+	u8 mac_a[8], ak[6];
+
+	if (*res_len < 8) {
+		*res_len = 0;
+		return;
+	}
+	if (milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL) ||
+	    milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) {
+		*res_len = 0;
+		return;
+	}
+	*res_len = 8;
+
+	/* AUTN = (SQN ^ AK) || AMF || MAC */
+	for (i = 0; i < 6; i++)
+		autn[i] = sqn[i] ^ ak[i];
+	os_memcpy(autn + 6, amf, 2);
+	os_memcpy(autn + 8, mac_a, 8);
+}
+
+
+/**
+ * milenage_auts - Milenage AUTS validation
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @auts: AUTS = 112-bit authentication token from client
+ * @sqn: Buffer for SQN = 48-bit sequence number
+ * Returns: 0 = success (sqn filled), -1 on failure
+ */
+int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts,
+		  u8 *sqn)
+{
+	u8 amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
+	u8 ak[6], mac_s[8];
+	int i;
+
+	if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
+		return -1;
+	for (i = 0; i < 6; i++)
+		sqn[i] = auts[i] ^ ak[i];
+	if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) ||
+	    os_memcmp_const(mac_s, auts + 6, 8) != 0)
+		return -1;
+	return 0;
+}
+
+
+/**
+ * gsm_milenage - Generate GSM-Milenage (3GPP TS 55.205) authentication triplet
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @sres: Buffer for SRES = 32-bit SRES
+ * @kc: Buffer for Kc = 64-bit Kc
+ * Returns: 0 on success, -1 on failure
+ */
+int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc)
+{
+	u8 res[8], ck[16], ik[16];
+	int i;
+
+	if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL))
+		return -1;
+
+	for (i = 0; i < 8; i++)
+		kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8];
+
+#ifdef GSM_MILENAGE_ALT_SRES
+	os_memcpy(sres, res, 4);
+#else /* GSM_MILENAGE_ALT_SRES */
+	for (i = 0; i < 4; i++)
+		sres[i] = res[i] ^ res[i + 4];
+#endif /* GSM_MILENAGE_ALT_SRES */
+	return 0;
+}
+
+
+/**
+ * milenage_generate - Generate AKA AUTN,IK,CK,RES
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @sqn: SQN = 48-bit sequence number
+ * @_rand: RAND = 128-bit random challenge
+ * @autn: AUTN = 128-bit authentication token
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @res_len: Variable that will be set to RES length
+ * @auts: 112-bit buffer for AUTS
+ * Returns: 0 on success, -1 on failure, or -2 on synchronization failure
+ */
+int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand,
+		   const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len,
+		   u8 *auts)
+{
+	int i;
+	u8 mac_a[8], ak[6], rx_sqn[6];
+	const u8 *amf;
+
+	wpa_hexdump(MSG_DEBUG, "Milenage: AUTN", autn, 16);
+	wpa_hexdump(MSG_DEBUG, "Milenage: RAND", _rand, 16);
+
+	if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL))
+		return -1;
+
+	*res_len = 8;
+	wpa_hexdump_key(MSG_DEBUG, "Milenage: RES", res, *res_len);
+	wpa_hexdump_key(MSG_DEBUG, "Milenage: CK", ck, 16);
+	wpa_hexdump_key(MSG_DEBUG, "Milenage: IK", ik, 16);
+	wpa_hexdump_key(MSG_DEBUG, "Milenage: AK", ak, 6);
+
+	/* AUTN = (SQN ^ AK) || AMF || MAC */
+	for (i = 0; i < 6; i++)
+		rx_sqn[i] = autn[i] ^ ak[i];
+	wpa_hexdump(MSG_DEBUG, "Milenage: SQN", rx_sqn, 6);
+
+	if (os_memcmp(rx_sqn, sqn, 6) <= 0) {
+		u8 auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
+		if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
+			return -1;
+		wpa_hexdump_key(MSG_DEBUG, "Milenage: AK*", ak, 6);
+		for (i = 0; i < 6; i++)
+			auts[i] = sqn[i] ^ ak[i];
+		if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6))
+			return -1;
+		wpa_hexdump(MSG_DEBUG, "Milenage: AUTS", auts, 14);
+		return -2;
+	}
+
+	amf = autn + 6;
+	wpa_hexdump(MSG_DEBUG, "Milenage: AMF", amf, 2);
+	if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL))
+		return -1;
+
+	wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8);
+
+	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);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/hostap/src/crypto/milenage.h b/hostap/src/crypto/milenage.h
new file mode 100644
index 0000000..62137d9
--- /dev/null
+++ b/hostap/src/crypto/milenage.h
@@ -0,0 +1,27 @@
+/*
+ * UMTS AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
+ * Copyright (c) 2006-2007 <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MILENAGE_H
+#define MILENAGE_H
+
+void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k,
+		       const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik,
+		       u8 *ck, u8 *res, size_t *res_len);
+int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts,
+		  u8 *sqn);
+int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres,
+		 u8 *kc);
+int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand,
+		   const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len,
+		   u8 *auts);
+int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand,
+		const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s);
+int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand,
+		   u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar);
+
+#endif /* MILENAGE_H */
diff --git a/hostap/src/crypto/ms_funcs.c b/hostap/src/crypto/ms_funcs.c
new file mode 100644
index 0000000..053d203
--- /dev/null
+++ b/hostap/src/crypto/ms_funcs.c
@@ -0,0 +1,524 @@
+/*
+ * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
+ * Copyright (c) 2004-2012, 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 "sha1.h"
+#include "ms_funcs.h"
+#include "crypto.h"
+
+/**
+ * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding
+ * @utf8_string: UTF-8 string (IN)
+ * @utf8_string_len: Length of utf8_string (IN)
+ * @ucs2_buffer: UCS-2 buffer (OUT)
+ * @ucs2_buffer_size: Length of UCS-2 buffer (IN)
+ * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string
+ * Returns: 0 on success, -1 on failure
+ */
+static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len,
+                        u8 *ucs2_buffer, size_t ucs2_buffer_size,
+                        size_t *ucs2_string_size)
+{
+	size_t i, j;
+
+	for (i = 0, j = 0; i < utf8_string_len; i++) {
+		u8 c = utf8_string[i];
+		if (j >= ucs2_buffer_size) {
+			/* input too long */
+			return -1;
+		}
+		if (c <= 0x7F) {
+			WPA_PUT_LE16(ucs2_buffer + j, c);
+			j += 2;
+		} else if (i == utf8_string_len - 1 ||
+			   j >= ucs2_buffer_size - 1) {
+			/* incomplete surrogate */
+			return -1;
+		} else {
+			u8 c2 = utf8_string[++i];
+			if ((c & 0xE0) == 0xC0) {
+				/* two-byte encoding */
+				WPA_PUT_LE16(ucs2_buffer + j,
+					     ((c & 0x1F) << 6) | (c2 & 0x3F));
+				j += 2;
+			} else if (i == utf8_string_len ||
+				   j >= ucs2_buffer_size - 1) {
+				/* incomplete surrogate */
+				return -1;
+			} else {
+				/* three-byte encoding */
+				u8 c3 = utf8_string[++i];
+				WPA_PUT_LE16(ucs2_buffer + j,
+					     ((c & 0xF) << 12) |
+					     ((c2 & 0x3F) << 6) | (c3 & 0x3F));
+				j += 2;
+			}
+		}
+	}
+
+	if (ucs2_string_size)
+		*ucs2_string_size = j / 2;
+	return 0;
+}
+
+
+/**
+ * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @challenge: 8-octet Challenge (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
+		   const u8 *username, size_t username_len, u8 *challenge)
+{
+	u8 hash[SHA1_MAC_LEN];
+	const unsigned char *addr[3];
+	size_t len[3];
+
+	addr[0] = peer_challenge;
+	len[0] = 16;
+	addr[1] = auth_challenge;
+	len[1] = 16;
+	addr[2] = username;
+	len[2] = username_len;
+
+	if (sha1_vector(3, addr, len, hash))
+		return -1;
+	os_memcpy(challenge, hash, 8);
+	return 0;
+}
+
+
+/**
+ * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @password_hash: 16-octet PasswordHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int nt_password_hash(const u8 *password, size_t password_len,
+		      u8 *password_hash)
+{
+	u8 buf[512], *pos;
+	size_t len, max_len;
+
+	max_len = sizeof(buf);
+	if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0)
+		return -1;
+
+	len *= 2;
+	pos = buf;
+	return md4_vector(1, (const u8 **) &pos, &len, password_hash);
+}
+
+
+/**
+ * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @password_hash_hash: 16-octet PasswordHashHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash)
+{
+	size_t len = 16;
+	return md4_vector(1, &password_hash, &len, password_hash_hash);
+}
+
+
+/**
+ * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5
+ * @challenge: 8-octet Challenge (IN)
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @response: 24-octet Response (OUT)
+ */
+void challenge_response(const u8 *challenge, const u8 *password_hash,
+			u8 *response)
+{
+	u8 zpwd[7];
+	des_encrypt(challenge, password_hash, response);
+	des_encrypt(challenge, password_hash + 7, response + 8);
+	zpwd[0] = password_hash[14];
+	zpwd[1] = password_hash[15];
+	os_memset(zpwd + 2, 0, 5);
+	des_encrypt(challenge, zpwd, response + 16);
+}
+
+
+/**
+ * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
+			 const u8 *username, size_t username_len,
+			 const u8 *password, size_t password_len,
+			 u8 *response)
+{
+	u8 challenge[8];
+	u8 password_hash[16];
+
+	if (challenge_hash(peer_challenge, auth_challenge, username,
+			   username_len, challenge) ||
+	    nt_password_hash(password, password_len, password_hash))
+		return -1;
+	challenge_response(challenge, password_hash, response);
+	return 0;
+}
+
+
+/**
+ * generate_nt_response_pwhash - GenerateNTResponse() - RFC 2759, Sect. 8.1
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_nt_response_pwhash(const u8 *auth_challenge,
+				const u8 *peer_challenge,
+				const u8 *username, size_t username_len,
+				const u8 *password_hash,
+				u8 *response)
+{
+	u8 challenge[8];
+
+	if (challenge_hash(peer_challenge, auth_challenge,
+			   username, username_len,
+			   challenge))
+		return -1;
+	challenge_response(challenge, password_hash, response);
+	return 0;
+}
+
+
+/**
+ * generate_authenticator_response_pwhash - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @nt_response: 24-octet NT-Response (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
+ * encoded as a 42-octet ASCII string (S=hexdump_of_response)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_authenticator_response_pwhash(
+	const u8 *password_hash,
+	const u8 *peer_challenge, const u8 *auth_challenge,
+	const u8 *username, size_t username_len,
+	const u8 *nt_response, u8 *response)
+{
+	static const u8 magic1[39] = {
+		0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+		0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+		0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+		0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
+	};
+	static const u8 magic2[41] = {
+		0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+		0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+		0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+		0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+		0x6E
+	};
+
+	u8 password_hash_hash[16], challenge[8];
+	const unsigned char *addr1[3];
+	const size_t len1[3] = { 16, 24, sizeof(magic1) };
+	const unsigned char *addr2[3];
+	const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) };
+
+	addr1[0] = password_hash_hash;
+	addr1[1] = nt_response;
+	addr1[2] = magic1;
+
+	addr2[0] = response;
+	addr2[1] = challenge;
+	addr2[2] = magic2;
+
+	if (hash_nt_password_hash(password_hash, password_hash_hash) ||
+	    sha1_vector(3, addr1, len1, response) ||
+	    challenge_hash(peer_challenge, auth_challenge, username,
+			   username_len, challenge))
+		return -1;
+	return sha1_vector(3, addr2, len2, response);
+}
+
+
+/**
+ * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @nt_response: 24-octet NT-Response (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
+ * encoded as a 42-octet ASCII string (S=hexdump_of_response)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_authenticator_response(const u8 *password, size_t password_len,
+				    const u8 *peer_challenge,
+				    const u8 *auth_challenge,
+				    const u8 *username, size_t username_len,
+				    const u8 *nt_response, u8 *response)
+{
+	u8 password_hash[16];
+	if (nt_password_hash(password, password_len, password_hash))
+		return -1;
+	return generate_authenticator_response_pwhash(
+		password_hash, peer_challenge, auth_challenge,
+		username, username_len, nt_response, response);
+}
+
+
+/**
+ * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5
+ * @challenge: 8-octet Challenge (IN)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int nt_challenge_response(const u8 *challenge, const u8 *password,
+			  size_t password_len, u8 *response)
+{
+	u8 password_hash[16];
+	if (nt_password_hash(password, password_len, password_hash))
+		return -1;
+	challenge_response(challenge, password_hash, response);
+	return 0;
+}
+
+
+/**
+ * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4
+ * @password_hash_hash: 16-octet PasswordHashHash (IN)
+ * @nt_response: 24-octet NTResponse (IN)
+ * @master_key: 16-octet MasterKey (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
+		   u8 *master_key)
+{
+	static const u8 magic1[27] = {
+		0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+		0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+		0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
+	};
+	const unsigned char *addr[3];
+	const size_t len[3] = { 16, 24, sizeof(magic1) };
+	u8 hash[SHA1_MAC_LEN];
+
+	addr[0] = password_hash_hash;
+	addr[1] = nt_response;
+	addr[2] = magic1;
+
+	if (sha1_vector(3, addr, len, hash))
+		return -1;
+	os_memcpy(master_key, hash, 16);
+	return 0;
+}
+
+
+/**
+ * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4
+ * @master_key: 16-octet MasterKey (IN)
+ * @session_key: 8-to-16 octet SessionKey (OUT)
+ * @session_key_len: SessionKeyLength (Length of session_key) (IN)
+ * @is_send: IsSend (IN, BOOLEAN)
+ * @is_server: IsServer (IN, BOOLEAN)
+ * Returns: 0 on success, -1 on failure
+ */
+int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
+			    size_t session_key_len, int is_send,
+			    int is_server)
+{
+	static const u8 magic2[84] = {
+		0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+		0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+		0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+		0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+		0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+		0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+		0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+		0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+		0x6b, 0x65, 0x79, 0x2e
+	};
+	static const u8 magic3[84] = {
+		0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+		0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+		0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+		0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+		0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+		0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+		0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+		0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+		0x6b, 0x65, 0x79, 0x2e
+	};
+	static const u8 shs_pad1[40] = {
+		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
+	};
+
+	static const u8 shs_pad2[40] = {
+		0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+		0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+		0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+		0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2
+	};
+	u8 digest[SHA1_MAC_LEN];
+	const unsigned char *addr[4];
+	const size_t len[4] = { 16, 40, 84, 40 };
+
+	addr[0] = master_key;
+	addr[1] = shs_pad1;
+	if (is_send) {
+		addr[2] = is_server ? magic3 : magic2;
+	} else {
+		addr[2] = is_server ? magic2 : magic3;
+	}
+	addr[3] = shs_pad2;
+
+	if (sha1_vector(4, addr, len, digest))
+		return -1;
+
+	if (session_key_len > SHA1_MAC_LEN)
+		session_key_len = SHA1_MAC_LEN;
+	os_memcpy(session_key, digest, session_key_len);
+	return 0;
+}
+
+
+#ifndef CONFIG_NO_RC4
+
+#define PWBLOCK_LEN 516
+
+/**
+ * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @pw_block: 516-byte PwBlock (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int encrypt_pw_block_with_password_hash(
+	const u8 *password, size_t password_len,
+	const u8 *password_hash, u8 *pw_block)
+{
+	size_t ucs2_len, offset;
+	u8 *pos;
+
+	os_memset(pw_block, 0, PWBLOCK_LEN);
+
+	if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0
+	    || ucs2_len > 256)
+		return -1;
+
+	offset = (256 - ucs2_len) * 2;
+	if (offset != 0) {
+		os_memmove(pw_block + offset, pw_block, ucs2_len * 2);
+		if (os_get_random(pw_block, offset) < 0)
+			return -1;
+	}
+	/*
+	 * PasswordLength is 4 octets, but since the maximum password length is
+	 * 256, only first two (in little endian byte order) can be non-zero.
+	 */
+	pos = &pw_block[2 * 256];
+	WPA_PUT_LE16(pos, password_len * 2);
+	rc4_skip(password_hash, 16, 0, pw_block, PWBLOCK_LEN);
+	return 0;
+}
+
+
+/**
+ * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
+ * @new_password_len: Length of new_password
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
+ * @old_password_len: Length of old_password
+ * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int new_password_encrypted_with_old_nt_password_hash(
+	const u8 *new_password, size_t new_password_len,
+	const u8 *old_password, size_t old_password_len,
+	u8 *encrypted_pw_block)
+{
+	u8 password_hash[16];
+
+	if (nt_password_hash(old_password, old_password_len, password_hash))
+		return -1;
+	if (encrypt_pw_block_with_password_hash(new_password, new_password_len,
+						password_hash,
+						encrypted_pw_block))
+		return -1;
+	return 0;
+}
+
+#endif /* CONFIG_NO_RC4 */
+
+
+/**
+ * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13
+ * @password_hash: 16-octer PasswordHash (IN)
+ * @block: 16-octet Block (IN)
+ * @cypher: 16-octer Cypher (OUT)
+ */
+void nt_password_hash_encrypted_with_block(const u8 *password_hash,
+					   const u8 *block, u8 *cypher)
+{
+	des_encrypt(password_hash, block, cypher);
+	des_encrypt(password_hash + 8, block + 7, cypher + 8);
+}
+
+
+/**
+ * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
+ * @new_password_len: Length of new_password
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
+ * @old_password_len: Length of old_password
+ * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int old_nt_password_hash_encrypted_with_new_nt_password_hash(
+	const u8 *new_password, size_t new_password_len,
+	const u8 *old_password, size_t old_password_len,
+	u8 *encrypted_password_hash)
+{
+	u8 old_password_hash[16], new_password_hash[16];
+
+	if (nt_password_hash(old_password, old_password_len,
+			     old_password_hash) ||
+	    nt_password_hash(new_password, new_password_len,
+			     new_password_hash))
+		return -1;
+	nt_password_hash_encrypted_with_block(old_password_hash,
+					      new_password_hash,
+					      encrypted_password_hash);
+	return 0;
+}
diff --git a/hostap/src/crypto/ms_funcs.h b/hostap/src/crypto/ms_funcs.h
new file mode 100644
index 0000000..b5b5918
--- /dev/null
+++ b/hostap/src/crypto/ms_funcs.h
@@ -0,0 +1,60 @@
+/*
+ * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
+ * 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.
+ */
+
+#ifndef MS_FUNCS_H
+#define MS_FUNCS_H
+
+int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
+			 const u8 *username, size_t username_len,
+			 const u8 *password, size_t password_len,
+			 u8 *response);
+int generate_nt_response_pwhash(const u8 *auth_challenge,
+				const u8 *peer_challenge,
+				const u8 *username, size_t username_len,
+				const u8 *password_hash,
+				u8 *response);
+int generate_authenticator_response(const u8 *password, size_t password_len,
+				    const u8 *peer_challenge,
+				    const u8 *auth_challenge,
+				    const u8 *username, size_t username_len,
+				    const u8 *nt_response, u8 *response);
+int generate_authenticator_response_pwhash(
+	const u8 *password_hash,
+	const u8 *peer_challenge, const u8 *auth_challenge,
+	const u8 *username, size_t username_len,
+	const u8 *nt_response, u8 *response);
+int nt_challenge_response(const u8 *challenge, const u8 *password,
+			  size_t password_len, u8 *response);
+
+void challenge_response(const u8 *challenge, const u8 *password_hash,
+			u8 *response);
+int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
+		   const u8 *username, size_t username_len, u8 *challenge);
+int nt_password_hash(const u8 *password, size_t password_len,
+		     u8 *password_hash);
+int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash);
+int get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
+		   u8 *master_key);
+int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
+			    size_t session_key_len, int is_send,
+			    int is_server);
+int __must_check encrypt_pw_block_with_password_hash(
+	const u8 *password, size_t password_len,
+	const u8 *password_hash, u8 *pw_block);
+int __must_check new_password_encrypted_with_old_nt_password_hash(
+	const u8 *new_password, size_t new_password_len,
+	const u8 *old_password, size_t old_password_len,
+	u8 *encrypted_pw_block);
+void nt_password_hash_encrypted_with_block(const u8 *password_hash,
+					   const u8 *block, u8 *cypher);
+int old_nt_password_hash_encrypted_with_new_nt_password_hash(
+	const u8 *new_password, size_t new_password_len,
+	const u8 *old_password, size_t old_password_len,
+	u8 *encrypted_password_hash);
+
+#endif /* MS_FUNCS_H */
diff --git a/hostap/src/crypto/random.c b/hostap/src/crypto/random.c
new file mode 100644
index 0000000..3a86a93
--- /dev/null
+++ b/hostap/src/crypto/random.c
@@ -0,0 +1,439 @@
+/*
+ * Random number generator
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This random number generator is used to provide additional entropy to the
+ * one provided by the operating system (os_get_random()) for session key
+ * generation. The os_get_random() output is expected to be secure and the
+ * implementation here is expected to provide only limited protection against
+ * cases where os_get_random() cannot provide strong randomness. This
+ * implementation shall not be assumed to be secure as the sole source of
+ * randomness. The random_get_bytes() function mixes in randomness from
+ * os_get_random() and as such, calls to os_get_random() can be replaced with
+ * calls to random_get_bytes() without reducing security.
+ *
+ * The design here follows partially the design used in the Linux
+ * drivers/char/random.c, but the implementation here is simpler and not as
+ * strong. This is a compromise to reduce duplicated CPU effort and to avoid
+ * extra code/memory size. As pointed out above, os_get_random() needs to be
+ * guaranteed to be secure for any of the security assumptions to hold.
+ */
+
+#include "utils/includes.h"
+#ifdef __linux__
+#include <fcntl.h>
+#endif /* __linux__ */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "sha1.h"
+#include "random.h"
+
+#define POOL_WORDS 32
+#define POOL_WORDS_MASK (POOL_WORDS - 1)
+#define POOL_TAP1 26
+#define POOL_TAP2 20
+#define POOL_TAP3 14
+#define POOL_TAP4 7
+#define POOL_TAP5 1
+#define EXTRACT_LEN 16
+#define MIN_READY_MARK 2
+
+static u32 pool[POOL_WORDS];
+static unsigned int input_rotate = 0;
+static unsigned int pool_pos = 0;
+static u8 dummy_key[20];
+#ifdef __linux__
+static size_t dummy_key_avail = 0;
+static int random_fd = -1;
+#endif /* __linux__ */
+static unsigned int own_pool_ready = 0;
+#define RANDOM_ENTROPY_SIZE 20
+static char *random_entropy_file = NULL;
+static int random_entropy_file_read = 0;
+
+#define MIN_COLLECT_ENTROPY 1000
+static unsigned int entropy = 0;
+static unsigned int total_collected = 0;
+
+
+static void random_write_entropy(void);
+
+
+static u32 __ROL32(u32 x, u32 y)
+{
+	return (x << (y & 31)) | (x >> (32 - (y & 31)));
+}
+
+
+static void random_mix_pool(const void *buf, size_t len)
+{
+	static const u32 twist[8] = {
+		0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
+		0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278
+	};
+	const u8 *pos = buf;
+	u32 w;
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "random_mix_pool", buf, len);
+
+	while (len--) {
+		w = __ROL32(*pos++, input_rotate & 31);
+		input_rotate += pool_pos ? 7 : 14;
+		pool_pos = (pool_pos - 1) & POOL_WORDS_MASK;
+		w ^= pool[pool_pos];
+		w ^= pool[(pool_pos + POOL_TAP1) & POOL_WORDS_MASK];
+		w ^= pool[(pool_pos + POOL_TAP2) & POOL_WORDS_MASK];
+		w ^= pool[(pool_pos + POOL_TAP3) & POOL_WORDS_MASK];
+		w ^= pool[(pool_pos + POOL_TAP4) & POOL_WORDS_MASK];
+		w ^= pool[(pool_pos + POOL_TAP5) & POOL_WORDS_MASK];
+		pool[pool_pos] = (w >> 3) ^ twist[w & 7];
+	}
+}
+
+
+static void random_extract(u8 *out)
+{
+	unsigned int i;
+	u8 hash[SHA1_MAC_LEN];
+	u32 *hash_ptr;
+	u32 buf[POOL_WORDS / 2];
+
+	/* First, add hash back to pool to make backtracking more difficult. */
+	hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) pool,
+		  sizeof(pool), hash);
+	random_mix_pool(hash, sizeof(hash));
+	/* Hash half the pool to extra data */
+	for (i = 0; i < POOL_WORDS / 2; i++)
+		buf[i] = pool[(pool_pos - i) & POOL_WORDS_MASK];
+	hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) buf,
+		  sizeof(buf), hash);
+	/*
+	 * Fold the hash to further reduce any potential output pattern.
+	 * Though, compromise this to reduce CPU use for the most common output
+	 * length (32) and return 16 bytes from instead of only half.
+	 */
+	hash_ptr = (u32 *) hash;
+	hash_ptr[0] ^= hash_ptr[4];
+	os_memcpy(out, hash, EXTRACT_LEN);
+}
+
+
+void random_add_randomness(const void *buf, size_t len)
+{
+	struct os_time t;
+	static unsigned int count = 0;
+
+	count++;
+	if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) {
+		/*
+		 * No need to add more entropy at this point, so save CPU and
+		 * skip the update.
+		 */
+		return;
+	}
+	wpa_printf(MSG_EXCESSIVE, "Add randomness: count=%u entropy=%u",
+		   count, entropy);
+
+	os_get_time(&t);
+	wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
+			(const u8 *) pool, sizeof(pool));
+	random_mix_pool(&t, sizeof(t));
+	random_mix_pool(buf, len);
+	wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
+			(const u8 *) pool, sizeof(pool));
+	entropy++;
+	total_collected++;
+}
+
+
+int random_get_bytes(void *buf, size_t len)
+{
+	int ret;
+	u8 *bytes = buf;
+	size_t left;
+
+	wpa_printf(MSG_MSGDUMP, "Get randomness: len=%u entropy=%u",
+		   (unsigned int) len, entropy);
+
+	/* Start with assumed strong randomness from OS */
+	ret = os_get_random(buf, len);
+	wpa_hexdump_key(MSG_EXCESSIVE, "random from os_get_random",
+			buf, len);
+
+	/* Mix in additional entropy extracted from the internal pool */
+	left = len;
+	while (left) {
+		size_t siz, i;
+		u8 tmp[EXTRACT_LEN];
+		random_extract(tmp);
+		wpa_hexdump_key(MSG_EXCESSIVE, "random from internal pool",
+				tmp, sizeof(tmp));
+		siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
+		for (i = 0; i < siz; i++)
+			*bytes++ ^= tmp[i];
+		left -= siz;
+	}
+
+#ifdef CONFIG_FIPS
+	/* Mix in additional entropy from the crypto module */
+	bytes = buf;
+	left = len;
+	while (left) {
+		size_t siz, i;
+		u8 tmp[EXTRACT_LEN];
+		if (crypto_get_random(tmp, sizeof(tmp)) < 0) {
+			wpa_printf(MSG_ERROR, "random: No entropy available "
+				   "for generating strong random bytes");
+			return -1;
+		}
+		wpa_hexdump_key(MSG_EXCESSIVE, "random from crypto module",
+				tmp, sizeof(tmp));
+		siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
+		for (i = 0; i < siz; i++)
+			*bytes++ ^= tmp[i];
+		left -= siz;
+	}
+#endif /* CONFIG_FIPS */
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len);
+
+	if (entropy < len)
+		entropy = 0;
+	else
+		entropy -= len;
+
+	return ret;
+}
+
+
+int random_pool_ready(void)
+{
+#ifdef __linux__
+	int fd;
+	ssize_t res;
+
+	/*
+	 * Make sure that there is reasonable entropy available before allowing
+	 * some key derivation operations to proceed.
+	 */
+
+	if (dummy_key_avail == sizeof(dummy_key))
+		return 1; /* Already initialized - good to continue */
+
+	/*
+	 * Try to fetch some more data from the kernel high quality
+	 * /dev/random. There may not be enough data available at this point,
+	 * so use non-blocking read to avoid blocking the application
+	 * completely.
+	 */
+	fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+	if (fd < 0) {
+		wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	res = read(fd, dummy_key + dummy_key_avail,
+		   sizeof(dummy_key) - dummy_key_avail);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
+			   "%s", strerror(errno));
+		res = 0;
+	}
+	wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from "
+		   "/dev/random", (unsigned) res,
+		   (unsigned) (sizeof(dummy_key) - dummy_key_avail));
+	dummy_key_avail += res;
+	close(fd);
+
+	if (dummy_key_avail == sizeof(dummy_key)) {
+		if (own_pool_ready < MIN_READY_MARK)
+			own_pool_ready = MIN_READY_MARK;
+		random_write_entropy();
+		return 1;
+	}
+
+	wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong "
+		   "random data available from /dev/random",
+		   (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key));
+
+	if (own_pool_ready >= MIN_READY_MARK ||
+	    total_collected + 10 * own_pool_ready > MIN_COLLECT_ENTROPY) {
+		wpa_printf(MSG_INFO, "random: Allow operation to proceed "
+			   "based on internal entropy");
+		return 1;
+	}
+
+	wpa_printf(MSG_INFO, "random: Not enough entropy pool available for "
+		   "secure operations");
+	return 0;
+#else /* __linux__ */
+	/* TODO: could do similar checks on non-Linux platforms */
+	return 1;
+#endif /* __linux__ */
+}
+
+
+void random_mark_pool_ready(void)
+{
+	own_pool_ready++;
+	wpa_printf(MSG_DEBUG, "random: Mark internal entropy pool to be "
+		   "ready (count=%u/%u)", own_pool_ready, MIN_READY_MARK);
+	random_write_entropy();
+}
+
+
+#ifdef __linux__
+
+static void random_close_fd(void)
+{
+	if (random_fd >= 0) {
+		eloop_unregister_read_sock(random_fd);
+		close(random_fd);
+		random_fd = -1;
+	}
+}
+
+
+static void random_read_fd(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	ssize_t res;
+
+	if (dummy_key_avail == sizeof(dummy_key)) {
+		random_close_fd();
+		return;
+	}
+
+	res = read(sock, dummy_key + dummy_key_avail,
+		   sizeof(dummy_key) - dummy_key_avail);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
+			   "%s", strerror(errno));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from /dev/random",
+		   (unsigned) res,
+		   (unsigned) (sizeof(dummy_key) - dummy_key_avail));
+	dummy_key_avail += res;
+
+	if (dummy_key_avail == sizeof(dummy_key)) {
+		random_close_fd();
+		if (own_pool_ready < MIN_READY_MARK)
+			own_pool_ready = MIN_READY_MARK;
+		random_write_entropy();
+	}
+}
+
+#endif /* __linux__ */
+
+
+static void random_read_entropy(void)
+{
+	char *buf;
+	size_t len;
+
+	if (!random_entropy_file)
+		return;
+
+	buf = os_readfile(random_entropy_file, &len);
+	if (buf == NULL)
+		return; /* entropy file not yet available */
+
+	if (len != 1 + RANDOM_ENTROPY_SIZE) {
+		wpa_printf(MSG_DEBUG, "random: Invalid entropy file %s",
+			   random_entropy_file);
+		os_free(buf);
+		return;
+	}
+
+	own_pool_ready = (u8) buf[0];
+	random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE);
+	random_entropy_file_read = 1;
+	os_free(buf);
+	wpa_printf(MSG_DEBUG, "random: Added entropy from %s "
+		   "(own_pool_ready=%u)",
+		   random_entropy_file, own_pool_ready);
+}
+
+
+static void random_write_entropy(void)
+{
+	char buf[RANDOM_ENTROPY_SIZE];
+	FILE *f;
+	u8 opr;
+	int fail = 0;
+
+	if (!random_entropy_file)
+		return;
+
+	if (random_get_bytes(buf, RANDOM_ENTROPY_SIZE) < 0)
+		return;
+
+	f = fopen(random_entropy_file, "wb");
+	if (f == NULL) {
+		wpa_printf(MSG_ERROR, "random: Could not open entropy file %s "
+			   "for writing", random_entropy_file);
+		return;
+	}
+
+	opr = own_pool_ready > 0xff ? 0xff : own_pool_ready;
+	if (fwrite(&opr, 1, 1, f) != 1 ||
+	    fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f) != 1)
+		fail = 1;
+	fclose(f);
+	if (fail) {
+		wpa_printf(MSG_ERROR, "random: Could not write entropy data "
+			   "to %s", random_entropy_file);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "random: Updated entropy file %s "
+		   "(own_pool_ready=%u)",
+		   random_entropy_file, own_pool_ready);
+}
+
+
+void random_init(const char *entropy_file)
+{
+	os_free(random_entropy_file);
+	if (entropy_file)
+		random_entropy_file = os_strdup(entropy_file);
+	else
+		random_entropy_file = NULL;
+	random_read_entropy();
+
+#ifdef __linux__
+	if (random_fd >= 0)
+		return;
+
+	random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+	if (random_fd < 0) {
+		wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
+			   strerror(errno));
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "random: Trying to read entropy from "
+		   "/dev/random");
+
+	eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL);
+#endif /* __linux__ */
+
+	random_write_entropy();
+}
+
+
+void random_deinit(void)
+{
+#ifdef __linux__
+	random_close_fd();
+#endif /* __linux__ */
+	random_write_entropy();
+	os_free(random_entropy_file);
+	random_entropy_file = NULL;
+}
diff --git a/hostap/src/crypto/random.h b/hostap/src/crypto/random.h
new file mode 100644
index 0000000..d13e1c4
--- /dev/null
+++ b/hostap/src/crypto/random.h
@@ -0,0 +1,28 @@
+/*
+ * Random number generator
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RANDOM_H
+#define RANDOM_H
+
+#ifdef CONFIG_NO_RANDOM_POOL
+#define random_init(e) do { } while (0)
+#define random_deinit() do { } while (0)
+#define random_add_randomness(b, l) do { } while (0)
+#define random_get_bytes(b, l) os_get_random((b), (l))
+#define random_pool_ready() 1
+#define random_mark_pool_ready() do { } while (0)
+#else /* CONFIG_NO_RANDOM_POOL */
+void random_init(const char *entropy_file);
+void random_deinit(void);
+void random_add_randomness(const void *buf, size_t len);
+int random_get_bytes(void *buf, size_t len);
+int random_pool_ready(void);
+void random_mark_pool_ready(void);
+#endif /* CONFIG_NO_RANDOM_POOL */
+
+#endif /* RANDOM_H */
diff --git a/hostap/src/crypto/rc4.c b/hostap/src/crypto/rc4.c
new file mode 100644
index 0000000..98ae269
--- /dev/null
+++ b/hostap/src/crypto/rc4.c
@@ -0,0 +1,54 @@
+/*
+ * RC4 stream cipher
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+
+#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+	     u8 *data, size_t data_len)
+{
+	u32 i, j, k;
+	u8 S[256], *pos;
+	size_t kpos;
+
+	/* Setup RC4 state */
+	for (i = 0; i < 256; i++)
+		S[i] = i;
+	j = 0;
+	kpos = 0;
+	for (i = 0; i < 256; i++) {
+		j = (j + S[i] + key[kpos]) & 0xff;
+		kpos++;
+		if (kpos >= keylen)
+			kpos = 0;
+		S_SWAP(i, j);
+	}
+
+	/* Skip the start of the stream */
+	i = j = 0;
+	for (k = 0; k < skip; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		S_SWAP(i, j);
+	}
+
+	/* Apply RC4 to data */
+	pos = data;
+	for (k = 0; k < data_len; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		S_SWAP(i, j);
+		*pos++ ^= S[(S[i] + S[j]) & 0xff];
+	}
+
+	return 0;
+}
diff --git a/hostap/src/crypto/sha1-internal.c b/hostap/src/crypto/sha1-internal.c
new file mode 100644
index 0000000..24bc3ff
--- /dev/null
+++ b/hostap/src/crypto/sha1-internal.c
@@ -0,0 +1,304 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2005, 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 "sha1.h"
+#include "sha1_i.h"
+#include "md5.h"
+#include "crypto.h"
+
+typedef struct SHA1Context SHA1_CTX;
+
+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
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	SHA1_CTX ctx;
+	size_t i;
+
+	SHA1Init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		SHA1Update(&ctx, addr[i], len[i]);
+	SHA1Final(mac, &ctx);
+	return 0;
+}
+#endif /* CONFIG_CRYPTO_INTERNAL */
+
+
+/* ===== start - public domain SHA1 implementation ===== */
+
+/*
+SHA-1 in C
+By Steve Reid <sreid@sea-to-sky.net>
+100% Public Domain
+
+-----------------
+Modified 7/98 
+By James H. Brown <jbrown@burgoyne.com>
+Still 100% Public Domain
+
+Corrected a problem which generated improper hash values on 16 bit machines
+Routine SHA1Update changed from
+	void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
+len)
+to
+	void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
+long len)
+
+The 'len' parameter was declared an int which works fine on 32 bit machines.
+However, on 16 bit machines an int is too small for the shifts being done
+against
+it.  This caused the hash function to generate incorrect values if len was
+greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or larger would
+be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+"a"s).
+
+I also changed the declaration of variables i & j in SHA1Update to 
+unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit implementations since
+an
+int and a long are the same size in those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments containing 'JHB'
+-----------------
+Modified 8/98
+By Steve Reid <sreid@sea-to-sky.net>
+Still 100% public domain
+
+1- Removed #include <process.h> and used return() instead of exit()
+2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
+3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
+
+-----------------
+Modified 4/01
+By Saul Kravitz <Saul.Kravitz@celera.com>
+Still 100% PD
+Modified to run on Compaq Alpha hardware.  
+
+-----------------
+Modified 4/01
+By Jouni Malinen <j@w1.fi>
+Minor changes to match the coding style used in Dynamics.
+
+Modified September 24, 2004
+By Jouni Malinen <j@w1.fi>
+Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined.
+
+*/
+
+/*
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+#define SHA1HANDSOFF
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifndef WORDS_BIGENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \
+	(rol(block->l[i], 8) & 0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \
+	block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) \
+	z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
+	w = rol(w, 30);
+#define R1(v,w,x,y,z,i) \
+	z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
+	w = rol(w, 30);
+#define R2(v,w,x,y,z,i) \
+	z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30);
+#define R3(v,w,x,y,z,i) \
+	z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
+	w = rol(w, 30);
+#define R4(v,w,x,y,z,i) \
+	z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
+	w=rol(w, 30);
+
+
+#ifdef VERBOSE  /* SAK */
+void SHAPrintContext(SHA1_CTX *context, char *msg)
+{
+	printf("%s (%d,%d) %x %x %x %x %x\n",
+	       msg,
+	       context->count[0], context->count[1], 
+	       context->state[0],
+	       context->state[1],
+	       context->state[2],
+	       context->state[3],
+	       context->state[4]);
+}
+#endif
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(u32 state[5], const unsigned char buffer[64])
+{
+	u32 a, b, c, d, e;
+	typedef union {
+		unsigned char c[64];
+		u32 l[16];
+	} CHAR64LONG16;
+	CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+	CHAR64LONG16 workspace;
+	block = &workspace;
+	os_memcpy(block, buffer, 64);
+#else
+	block = (CHAR64LONG16 *) buffer;
+#endif
+	/* Copy context->state[] to working vars */
+	a = state[0];
+	b = state[1];
+	c = state[2];
+	d = state[3];
+	e = state[4];
+	/* 4 rounds of 20 operations each. Loop unrolled. */
+	R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+	R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+	R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+	R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+	R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+	R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+	R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+	R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+	R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+	R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+	R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+	R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+	R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+	R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+	R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+	R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+	R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+	R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+	R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+	R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+	/* Add the working vars back into context.state[] */
+	state[0] += a;
+	state[1] += b;
+	state[2] += c;
+	state[3] += d;
+	state[4] += e;
+	/* Wipe variables */
+	a = b = c = d = e = 0;
+#ifdef SHA1HANDSOFF
+	os_memset(block, 0, 64);
+#endif
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+	/* SHA1 initialization constants */
+	context->state[0] = 0x67452301;
+	context->state[1] = 0xEFCDAB89;
+	context->state[2] = 0x98BADCFE;
+	context->state[3] = 0x10325476;
+	context->state[4] = 0xC3D2E1F0;
+	context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const void *_data, u32 len)
+{
+	u32 i, j;
+	const unsigned char *data = _data;
+
+#ifdef VERBOSE
+	SHAPrintContext(context, "before");
+#endif
+	j = (context->count[0] >> 3) & 63;
+	if ((context->count[0] += len << 3) < (len << 3))
+		context->count[1]++;
+	context->count[1] += (len >> 29);
+	if ((j + len) > 63) {
+		os_memcpy(&context->buffer[j], data, (i = 64-j));
+		SHA1Transform(context->state, context->buffer);
+		for ( ; i + 63 < len; i += 64) {
+			SHA1Transform(context->state, &data[i]);
+		}
+		j = 0;
+	}
+	else i = 0;
+	os_memcpy(&context->buffer[j], &data[i], len - i);
+#ifdef VERBOSE
+	SHAPrintContext(context, "after ");
+#endif
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
+{
+	u32 i;
+	unsigned char finalcount[8];
+
+	for (i = 0; i < 8; i++) {
+		finalcount[i] = (unsigned char)
+			((context->count[(i >= 4 ? 0 : 1)] >>
+			  ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+	}
+	SHA1Update(context, (unsigned char *) "\200", 1);
+	while ((context->count[0] & 504) != 448) {
+		SHA1Update(context, (unsigned char *) "\0", 1);
+	}
+	SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform()
+					      */
+	for (i = 0; i < 20; i++) {
+		digest[i] = (unsigned char)
+			((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) &
+			 255);
+	}
+	/* Wipe variables */
+	i = 0;
+	os_memset(context->buffer, 0, 64);
+	os_memset(context->state, 0, 20);
+	os_memset(context->count, 0, 8);
+	os_memset(finalcount, 0, 8);
+}
+
+/* ===== end - public domain SHA1 implementation ===== */
diff --git a/hostap/src/crypto/sha1-pbkdf2.c b/hostap/src/crypto/sha1-pbkdf2.c
new file mode 100644
index 0000000..8effe2f
--- /dev/null
+++ b/hostap/src/crypto/sha1-pbkdf2.c
@@ -0,0 +1,92 @@
+/*
+ * SHA1-based key derivation function (PBKDF2) for IEEE 802.11i
+ * Copyright (c) 2003-2005, 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 "sha1.h"
+
+static int pbkdf2_sha1_f(const char *passphrase, const u8 *ssid,
+			 size_t ssid_len, int iterations, unsigned int count,
+			 u8 *digest)
+{
+	unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN];
+	int i, j;
+	unsigned char count_buf[4];
+	const u8 *addr[2];
+	size_t len[2];
+	size_t passphrase_len = os_strlen(passphrase);
+
+	addr[0] = ssid;
+	len[0] = ssid_len;
+	addr[1] = count_buf;
+	len[1] = 4;
+
+	/* F(P, S, c, i) = U1 xor U2 xor ... Uc
+	 * U1 = PRF(P, S || i)
+	 * U2 = PRF(P, U1)
+	 * Uc = PRF(P, Uc-1)
+	 */
+
+	count_buf[0] = (count >> 24) & 0xff;
+	count_buf[1] = (count >> 16) & 0xff;
+	count_buf[2] = (count >> 8) & 0xff;
+	count_buf[3] = count & 0xff;
+	if (hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len,
+			     tmp))
+		return -1;
+	os_memcpy(digest, tmp, SHA1_MAC_LEN);
+
+	for (i = 1; i < iterations; i++) {
+		if (hmac_sha1((u8 *) passphrase, passphrase_len, tmp,
+			      SHA1_MAC_LEN, tmp2))
+			return -1;
+		os_memcpy(tmp, tmp2, SHA1_MAC_LEN);
+		for (j = 0; j < SHA1_MAC_LEN; j++)
+			digest[j] ^= tmp2[j];
+	}
+
+	return 0;
+}
+
+
+/**
+ * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i
+ * @passphrase: ASCII passphrase
+ * @ssid: SSID
+ * @ssid_len: SSID length in bytes
+ * @iterations: Number of iterations to run
+ * @buf: Buffer for the generated key
+ * @buflen: Length of the buffer in bytes
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive PSK for WPA-PSK. For this protocol,
+ * iterations is set to 4096 and buflen to 32. This function is described in
+ * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0.
+ */
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+		int iterations, u8 *buf, size_t buflen)
+{
+	unsigned int count = 0;
+	unsigned char *pos = buf;
+	size_t left = buflen, plen;
+	unsigned char digest[SHA1_MAC_LEN];
+
+	while (left > 0) {
+		count++;
+		if (pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations,
+				  count, digest))
+			return -1;
+		plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left;
+		os_memcpy(pos, digest, plen);
+		pos += plen;
+		left -= plen;
+	}
+
+	return 0;
+}
diff --git a/hostap/src/crypto/sha1-prf.c b/hostap/src/crypto/sha1-prf.c
new file mode 100644
index 0000000..4b2d137
--- /dev/null
+++ b/hostap/src/crypto/sha1-prf.c
@@ -0,0 +1,67 @@
+/*
+ * SHA1-based PRF
+ * Copyright (c) 2003-2005, 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 "sha1.h"
+#include "crypto.h"
+
+
+/**
+ * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key (e.g., PMK in IEEE 802.11i).
+ */
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	u8 counter = 0;
+	size_t pos, plen;
+	u8 hash[SHA1_MAC_LEN];
+	size_t label_len = os_strlen(label) + 1;
+	const unsigned char *addr[3];
+	size_t len[3];
+
+	addr[0] = (u8 *) label;
+	len[0] = label_len;
+	addr[1] = data;
+	len[1] = data_len;
+	addr[2] = &counter;
+	len[2] = 1;
+
+	pos = 0;
+	while (pos < buf_len) {
+		plen = buf_len - pos;
+		if (plen >= SHA1_MAC_LEN) {
+			if (hmac_sha1_vector(key, key_len, 3, addr, len,
+					     &buf[pos]))
+				return -1;
+			pos += SHA1_MAC_LEN;
+		} else {
+			if (hmac_sha1_vector(key, key_len, 3, addr, len,
+					     hash))
+				return -1;
+			os_memcpy(&buf[pos], hash, plen);
+			break;
+		}
+		counter++;
+	}
+	os_memset(hash, 0, sizeof(hash));
+
+	return 0;
+}
diff --git a/hostap/src/crypto/sha1-tlsprf.c b/hostap/src/crypto/sha1-tlsprf.c
new file mode 100644
index 0000000..f9bc0eb
--- /dev/null
+++ b/hostap/src/crypto/sha1-tlsprf.c
@@ -0,0 +1,104 @@
+/*
+ * TLS PRF (SHA1 + MD5)
+ * Copyright (c) 2003-2005, 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 "sha1.h"
+#include "md5.h"
+
+
+/**
+ * tls_prf_sha1_md5 - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246)
+ * @secret: Key for PRF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
+ */
+int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
+		     const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+	size_t L_S1, L_S2, i;
+	const u8 *S1, *S2;
+	u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN];
+	u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN];
+	int MD5_pos, SHA1_pos;
+	const u8 *MD5_addr[3];
+	size_t MD5_len[3];
+	const unsigned char *SHA1_addr[3];
+	size_t SHA1_len[3];
+
+	if (secret_len & 1)
+		return -1;
+
+	MD5_addr[0] = A_MD5;
+	MD5_len[0] = MD5_MAC_LEN;
+	MD5_addr[1] = (unsigned char *) label;
+	MD5_len[1] = os_strlen(label);
+	MD5_addr[2] = seed;
+	MD5_len[2] = seed_len;
+
+	SHA1_addr[0] = A_SHA1;
+	SHA1_len[0] = SHA1_MAC_LEN;
+	SHA1_addr[1] = (unsigned char *) label;
+	SHA1_len[1] = os_strlen(label);
+	SHA1_addr[2] = seed;
+	SHA1_len[2] = seed_len;
+
+	/* RFC 2246, Chapter 5
+	 * A(0) = seed, A(i) = HMAC(secret, A(i-1))
+	 * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
+	 * PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed)
+	 */
+
+	L_S1 = L_S2 = (secret_len + 1) / 2;
+	S1 = secret;
+	S2 = secret + L_S1;
+	if (secret_len & 1) {
+		/* The last byte of S1 will be shared with S2 */
+		S2--;
+	}
+
+	hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5);
+	hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1);
+
+	MD5_pos = MD5_MAC_LEN;
+	SHA1_pos = SHA1_MAC_LEN;
+	for (i = 0; i < outlen; i++) {
+		if (MD5_pos == MD5_MAC_LEN) {
+			hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5);
+			MD5_pos = 0;
+			hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5);
+		}
+		if (SHA1_pos == SHA1_MAC_LEN) {
+			hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len,
+					 P_SHA1);
+			SHA1_pos = 0;
+			hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1);
+		}
+
+		out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos];
+
+		MD5_pos++;
+		SHA1_pos++;
+	}
+
+	os_memset(A_MD5, 0, MD5_MAC_LEN);
+	os_memset(P_MD5, 0, MD5_MAC_LEN);
+	os_memset(A_SHA1, 0, SHA1_MAC_LEN);
+	os_memset(P_SHA1, 0, SHA1_MAC_LEN);
+
+	return 0;
+}
diff --git a/hostap/src/crypto/sha1-tprf.c b/hostap/src/crypto/sha1-tprf.c
new file mode 100644
index 0000000..562510f
--- /dev/null
+++ b/hostap/src/crypto/sha1-tprf.c
@@ -0,0 +1,72 @@
+/*
+ * SHA1 T-PRF for EAP-FAST
+ * Copyright (c) 2003-2005, 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 "sha1.h"
+#include "crypto.h"
+
+/**
+ * sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key for EAP-FAST. T-PRF is defined in RFC 4851, Section 5.5.
+ */
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
+{
+	unsigned char counter = 0;
+	size_t pos, plen;
+	u8 hash[SHA1_MAC_LEN];
+	size_t label_len = os_strlen(label);
+	u8 output_len[2];
+	const unsigned char *addr[5];
+	size_t len[5];
+
+	addr[0] = hash;
+	len[0] = 0;
+	addr[1] = (unsigned char *) label;
+	len[1] = label_len + 1;
+	addr[2] = seed;
+	len[2] = seed_len;
+	addr[3] = output_len;
+	len[3] = 2;
+	addr[4] = &counter;
+	len[4] = 1;
+
+	output_len[0] = (buf_len >> 8) & 0xff;
+	output_len[1] = buf_len & 0xff;
+	pos = 0;
+	while (pos < buf_len) {
+		counter++;
+		plen = buf_len - pos;
+		if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
+			return -1;
+		if (plen >= SHA1_MAC_LEN) {
+			os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
+			pos += SHA1_MAC_LEN;
+		} else {
+			os_memcpy(&buf[pos], hash, plen);
+			break;
+		}
+		len[0] = SHA1_MAC_LEN;
+	}
+
+	os_memset(hash, 0, SHA1_MAC_LEN);
+
+	return 0;
+}
diff --git a/hostap/src/crypto/sha1.c b/hostap/src/crypto/sha1.c
new file mode 100644
index 0000000..8fce139
--- /dev/null
+++ b/hostap/src/crypto/sha1.c
@@ -0,0 +1,107 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2005, 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 "sha1.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
+	unsigned char tk[20];
+	const u8 *_addr[6];
+	size_t _len[6], i;
+	int ret;
+
+	if (num_elem > 5) {
+		/*
+		 * Fixed limit on the number of fragments to avoid having to
+		 * allocate memory (which could fail).
+		 */
+		return -1;
+	}
+
+        /* if key is longer than 64 bytes reset it to key = SHA1(key) */
+        if (key_len > 64) {
+		if (sha1_vector(1, &key, &key_len, tk))
+			return -1;
+		key = tk;
+		key_len = 20;
+        }
+
+	/* the HMAC_SHA1 transform looks like:
+	 *
+	 * SHA1(K XOR opad, SHA1(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected */
+
+	/* start out by storing key in ipad */
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with ipad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x36;
+
+	/* perform inner SHA1 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	for (i = 0; i < num_elem; i++) {
+		_addr[i + 1] = addr[i];
+		_len[i + 1] = len[i];
+	}
+	if (sha1_vector(1 + num_elem, _addr, _len, mac))
+		return -1;
+
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with opad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x5c;
+
+	/* perform outer SHA1 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	_addr[1] = mac;
+	_len[1] = SHA1_MAC_LEN;
+	ret = sha1_vector(2, _addr, _len, mac);
+	os_memset(k_pad, 0, sizeof(k_pad));
+	return ret;
+}
+
+
+/**
+ * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 of failure
+ */
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	       u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
diff --git a/hostap/src/crypto/sha1.h b/hostap/src/crypto/sha1.h
new file mode 100644
index 0000000..933cd81
--- /dev/null
+++ b/hostap/src/crypto/sha1.h
@@ -0,0 +1,27 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA1_H
+#define SHA1_H
+
+#define SHA1_MAC_LEN 20
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	       u8 *mac);
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len);
+int __must_check tls_prf_sha1_md5(const u8 *secret, size_t secret_len,
+				  const char *label, const u8 *seed,
+				  size_t seed_len, u8 *out, size_t outlen);
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+		int iterations, u8 *buf, size_t buflen);
+#endif /* SHA1_H */
diff --git a/hostap/src/crypto/sha1_i.h b/hostap/src/crypto/sha1_i.h
new file mode 100644
index 0000000..344387e
--- /dev/null
+++ b/hostap/src/crypto/sha1_i.h
@@ -0,0 +1,23 @@
+/*
+ * SHA1 internal definitions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA1_I_H
+#define SHA1_I_H
+
+struct SHA1Context {
+	u32 state[5];
+	u32 count[2];
+	unsigned char buffer[64];
+};
+
+void SHA1Init(struct SHA1Context *context);
+void SHA1Update(struct SHA1Context *context, const void *data, u32 len);
+void SHA1Final(unsigned char digest[20], struct SHA1Context *context);
+void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
+
+#endif /* SHA1_I_H */
diff --git a/hostap/src/crypto/sha256-internal.c b/hostap/src/crypto/sha256-internal.c
new file mode 100644
index 0000000..35299b0
--- /dev/null
+++ b/hostap/src/crypto/sha256-internal.c
@@ -0,0 +1,226 @@
+/*
+ * SHA-256 hash implementation and interface functions
+ * Copyright (c) 2003-2011, 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 "sha256.h"
+#include "sha256_i.h"
+#include "crypto.h"
+
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	struct sha256_state ctx;
+	size_t i;
+
+	sha256_init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		if (sha256_process(&ctx, addr[i], len[i]))
+			return -1;
+	if (sha256_done(&ctx, mac))
+		return -1;
+	return 0;
+}
+
+
+/* ===== start - public domain SHA256 implementation ===== */
+
+/* This is based on SHA256 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+/* the K array */
+static const unsigned long K[64] = {
+	0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+	0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+	0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+	0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+	0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+	0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+	0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+	0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+	0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+	0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+	0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+	0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+	0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+
+/* Various logical functions */
+#define RORc(x, y) \
+( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \
+   ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+#define Ch(x,y,z)       (z ^ (x & (y ^ z)))
+#define Maj(x,y,z)      (((x | y) & z) | (x & y)) 
+#define S(x, n)         RORc((x), (n))
+#define R(x, n)         (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x)       (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x)       (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x)       (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x)       (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+/* compress 512-bits */
+static int sha256_compress(struct sha256_state *md, unsigned char *buf)
+{
+	u32 S[8], W[64], t0, t1;
+	u32 t;
+	int i;
+
+	/* copy state into S */
+	for (i = 0; i < 8; i++) {
+		S[i] = md->state[i];
+	}
+
+	/* copy the state into 512-bits into W[0..15] */
+	for (i = 0; i < 16; i++)
+		W[i] = WPA_GET_BE32(buf + (4 * i));
+
+	/* fill W[16..63] */
+	for (i = 16; i < 64; i++) {
+		W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
+			W[i - 16];
+	}        
+
+	/* Compress */
+#define RND(a,b,c,d,e,f,g,h,i)                          \
+	t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];	\
+	t1 = Sigma0(a) + Maj(a, b, c);			\
+	d += t0;					\
+	h  = t0 + t1;
+
+	for (i = 0; i < 64; ++i) {
+		RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i);
+		t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; 
+		S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
+	}
+
+	/* feedback */
+	for (i = 0; i < 8; i++) {
+		md->state[i] = md->state[i] + S[i];
+	}
+	return 0;
+}
+
+
+/* Initialize the hash state */
+void sha256_init(struct sha256_state *md)
+{
+	md->curlen = 0;
+	md->length = 0;
+	md->state[0] = 0x6A09E667UL;
+	md->state[1] = 0xBB67AE85UL;
+	md->state[2] = 0x3C6EF372UL;
+	md->state[3] = 0xA54FF53AUL;
+	md->state[4] = 0x510E527FUL;
+	md->state[5] = 0x9B05688CUL;
+	md->state[6] = 0x1F83D9ABUL;
+	md->state[7] = 0x5BE0CD19UL;
+}
+
+/**
+   Process a block of memory though the hash
+   @param md     The hash state
+   @param in     The data to hash
+   @param inlen  The length of the data (octets)
+   @return CRYPT_OK if successful
+*/
+int sha256_process(struct sha256_state *md, const unsigned char *in,
+		   unsigned long inlen)
+{
+	unsigned long n;
+
+	if (md->curlen >= sizeof(md->buf))
+		return -1;
+
+	while (inlen > 0) {
+		if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) {
+			if (sha256_compress(md, (unsigned char *) in) < 0)
+				return -1;
+			md->length += SHA256_BLOCK_SIZE * 8;
+			in += SHA256_BLOCK_SIZE;
+			inlen -= SHA256_BLOCK_SIZE;
+		} else {
+			n = MIN(inlen, (SHA256_BLOCK_SIZE - md->curlen));
+			os_memcpy(md->buf + md->curlen, in, n);
+			md->curlen += n;
+			in += n;
+			inlen -= n;
+			if (md->curlen == SHA256_BLOCK_SIZE) {
+				if (sha256_compress(md, md->buf) < 0)
+					return -1;
+				md->length += 8 * SHA256_BLOCK_SIZE;
+				md->curlen = 0;
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+   Terminate the hash to get the digest
+   @param md  The hash state
+   @param out [out] The destination of the hash (32 bytes)
+   @return CRYPT_OK if successful
+*/
+int sha256_done(struct sha256_state *md, unsigned char *out)
+{
+	int i;
+
+	if (md->curlen >= sizeof(md->buf))
+		return -1;
+
+	/* increase the length of the message */
+	md->length += md->curlen * 8;
+
+	/* append the '1' bit */
+	md->buf[md->curlen++] = (unsigned char) 0x80;
+
+	/* if the length is currently above 56 bytes we append zeros
+	 * then compress.  Then we can fall back to padding zeros and length
+	 * encoding like normal.
+	 */
+	if (md->curlen > 56) {
+		while (md->curlen < SHA256_BLOCK_SIZE) {
+			md->buf[md->curlen++] = (unsigned char) 0;
+		}
+		sha256_compress(md, md->buf);
+		md->curlen = 0;
+	}
+
+	/* pad up to 56 bytes of zeroes */
+	while (md->curlen < 56) {
+		md->buf[md->curlen++] = (unsigned char) 0;
+	}
+
+	/* store length */
+	WPA_PUT_BE64(md->buf + 56, md->length);
+	sha256_compress(md, md->buf);
+
+	/* copy output */
+	for (i = 0; i < 8; i++)
+		WPA_PUT_BE32(out + (4 * i), md->state[i]);
+
+	return 0;
+}
+
+/* ===== end - public domain SHA256 implementation ===== */
diff --git a/hostap/src/crypto/sha256-kdf.c b/hostap/src/crypto/sha256-kdf.c
new file mode 100644
index 0000000..e7509ce
--- /dev/null
+++ b/hostap/src/crypto/sha256-kdf.c
@@ -0,0 +1,79 @@
+/*
+ * HMAC-SHA256 KDF (RFC 5295)
+ * 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 "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+
+
+/**
+ * hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295)
+ * @secret: Key for KDF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2.
+ */
+int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen)
+{
+	u8 T[SHA256_MAC_LEN];
+	u8 iter = 1;
+	const unsigned char *addr[4];
+	size_t len[4];
+	size_t pos, clen;
+
+	addr[0] = T;
+	len[0] = SHA256_MAC_LEN;
+	addr[1] = (const unsigned char *) label;
+	len[1] = os_strlen(label) + 1;
+	addr[2] = seed;
+	len[2] = seed_len;
+	addr[3] = &iter;
+	len[3] = 1;
+
+	if (hmac_sha256_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
+		return -1;
+
+	pos = 0;
+	for (;;) {
+		clen = outlen - pos;
+		if (clen > SHA256_MAC_LEN)
+			clen = SHA256_MAC_LEN;
+		os_memcpy(out + pos, T, clen);
+		pos += clen;
+
+		if (pos == outlen)
+			break;
+
+		if (iter == 255) {
+			os_memset(out, 0, outlen);
+			os_memset(T, 0, SHA256_MAC_LEN);
+			return -1;
+		}
+		iter++;
+
+		if (hmac_sha256_vector(secret, secret_len, 4, addr, len, T) < 0)
+		{
+			os_memset(out, 0, outlen);
+			os_memset(T, 0, SHA256_MAC_LEN);
+			return -1;
+		}
+	}
+
+	os_memset(T, 0, SHA256_MAC_LEN);
+	return 0;
+}
diff --git a/hostap/src/crypto/sha256-prf.c b/hostap/src/crypto/sha256-prf.c
new file mode 100644
index 0000000..79791c0
--- /dev/null
+++ b/hostap/src/crypto/sha256-prf.c
@@ -0,0 +1,100 @@
+/*
+ * SHA256-based PRF (IEEE 802.11r)
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+#include "crypto.h"
+
+
+/**
+ * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+void sha256_prf(const u8 *key, size_t key_len, const char *label,
+		const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	sha256_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8);
+}
+
+
+/**
+ * sha256_prf_bits - IEEE Std 802.11-2012, 11.6.1.7.2 Key derivation function
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+void sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+		     const u8 *data, size_t data_len, u8 *buf,
+		     size_t buf_len_bits)
+{
+	u16 counter = 1;
+	size_t pos, plen;
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+	u8 counter_le[2], length_le[2];
+	size_t buf_len = (buf_len_bits + 7) / 8;
+
+	addr[0] = counter_le;
+	len[0] = 2;
+	addr[1] = (u8 *) label;
+	len[1] = os_strlen(label);
+	addr[2] = data;
+	len[2] = data_len;
+	addr[3] = length_le;
+	len[3] = sizeof(length_le);
+
+	WPA_PUT_LE16(length_le, buf_len_bits);
+	pos = 0;
+	while (pos < buf_len) {
+		plen = buf_len - pos;
+		WPA_PUT_LE16(counter_le, counter);
+		if (plen >= SHA256_MAC_LEN) {
+			hmac_sha256_vector(key, key_len, 4, addr, len,
+					   &buf[pos]);
+			pos += SHA256_MAC_LEN;
+		} else {
+			hmac_sha256_vector(key, key_len, 4, addr, len, hash);
+			os_memcpy(&buf[pos], hash, plen);
+			pos += plen;
+			break;
+		}
+		counter++;
+	}
+
+	/*
+	 * Mask out unused bits in the last octet if it does not use all the
+	 * bits.
+	 */
+	if (buf_len_bits % 8) {
+		u8 mask = 0xff << (8 - buf_len_bits % 8);
+		buf[pos - 1] &= mask;
+	}
+
+	os_memset(hash, 0, sizeof(hash));
+}
diff --git a/hostap/src/crypto/sha256-tlsprf.c b/hostap/src/crypto/sha256-tlsprf.c
new file mode 100644
index 0000000..0528dad
--- /dev/null
+++ b/hostap/src/crypto/sha256-tlsprf.c
@@ -0,0 +1,66 @@
+/*
+ * TLS PRF P_SHA256
+ * Copyright (c) 2011, 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 "sha256.h"
+
+
+/**
+ * tls_prf_sha256 - Pseudo-Random Function for TLS v1.2 (P_SHA256, RFC 5246)
+ * @secret: Key for PRF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
+ */
+void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
+		    const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+	size_t clen;
+	u8 A[SHA256_MAC_LEN];
+	u8 P[SHA256_MAC_LEN];
+	size_t pos;
+	const unsigned char *addr[3];
+	size_t len[3];
+
+	addr[0] = A;
+	len[0] = SHA256_MAC_LEN;
+	addr[1] = (unsigned char *) label;
+	len[1] = os_strlen(label);
+	addr[2] = seed;
+	len[2] = seed_len;
+
+	/*
+	 * RFC 5246, Chapter 5
+	 * A(0) = seed, A(i) = HMAC(secret, A(i-1))
+	 * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
+	 * PRF(secret, label, seed) = P_SHA256(secret, label + seed)
+	 */
+
+	hmac_sha256_vector(secret, secret_len, 2, &addr[1], &len[1], A);
+
+	pos = 0;
+	while (pos < outlen) {
+		hmac_sha256_vector(secret, secret_len, 3, addr, len, P);
+		hmac_sha256(secret, secret_len, A, SHA256_MAC_LEN, A);
+
+		clen = outlen - pos;
+		if (clen > SHA256_MAC_LEN)
+			clen = SHA256_MAC_LEN;
+		os_memcpy(out + pos, P, clen);
+		pos += clen;
+	}
+}
diff --git a/hostap/src/crypto/sha256.c b/hostap/src/crypto/sha256.c
new file mode 100644
index 0000000..b55e976
--- /dev/null
+++ b/hostap/src/crypto/sha256.c
@@ -0,0 +1,104 @@
+/*
+ * SHA-256 hash implementation and interface functions
+ * Copyright (c) 2003-2012, 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 "sha256.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (32 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
+	unsigned char tk[32];
+	const u8 *_addr[6];
+	size_t _len[6], i;
+
+	if (num_elem > 5) {
+		/*
+		 * Fixed limit on the number of fragments to avoid having to
+		 * allocate memory (which could fail).
+		 */
+		return -1;
+	}
+
+        /* if key is longer than 64 bytes reset it to key = SHA256(key) */
+        if (key_len > 64) {
+		if (sha256_vector(1, &key, &key_len, tk) < 0)
+			return -1;
+		key = tk;
+		key_len = 32;
+        }
+
+	/* the HMAC_SHA256 transform looks like:
+	 *
+	 * SHA256(K XOR opad, SHA256(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected */
+
+	/* start out by storing key in ipad */
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with ipad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x36;
+
+	/* perform inner SHA256 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	for (i = 0; i < num_elem; i++) {
+		_addr[i + 1] = addr[i];
+		_len[i + 1] = len[i];
+	}
+	if (sha256_vector(1 + num_elem, _addr, _len, mac) < 0)
+		return -1;
+
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with opad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x5c;
+
+	/* perform outer SHA256 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	_addr[1] = mac;
+	_len[1] = SHA256_MAC_LEN;
+	return sha256_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_sha256 - HMAC-SHA256 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (32 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
diff --git a/hostap/src/crypto/sha256.h b/hostap/src/crypto/sha256.h
new file mode 100644
index 0000000..b15f511
--- /dev/null
+++ b/hostap/src/crypto/sha256.h
@@ -0,0 +1,30 @@
+/*
+ * SHA256 hash implementation and interface functions
+ * 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.
+ */
+
+#ifndef SHA256_H
+#define SHA256_H
+
+#define SHA256_MAC_LEN 32
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac);
+void sha256_prf(const u8 *key, size_t key_len, const char *label,
+	      const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+void sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+		     const u8 *data, size_t data_len, u8 *buf,
+		     size_t buf_len_bits);
+void tls_prf_sha256(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen);
+int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen);
+
+#endif /* SHA256_H */
diff --git a/hostap/src/crypto/sha256_i.h b/hostap/src/crypto/sha256_i.h
new file mode 100644
index 0000000..a502d2b
--- /dev/null
+++ b/hostap/src/crypto/sha256_i.h
@@ -0,0 +1,25 @@
+/*
+ * SHA-256 internal definitions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA256_I_H
+#define SHA256_I_H
+
+#define SHA256_BLOCK_SIZE 64
+
+struct sha256_state {
+	u64 length;
+	u32 state[8], curlen;
+	u8 buf[SHA256_BLOCK_SIZE];
+};
+
+void sha256_init(struct sha256_state *md);
+int sha256_process(struct sha256_state *md, const unsigned char *in,
+		   unsigned long inlen);
+int sha256_done(struct sha256_state *md, unsigned char *out);
+
+#endif /* SHA256_I_H */
diff --git a/hostap/src/crypto/sha384-prf.c b/hostap/src/crypto/sha384-prf.c
new file mode 100644
index 0000000..653920b
--- /dev/null
+++ b/hostap/src/crypto/sha384-prf.c
@@ -0,0 +1,100 @@
+/*
+ * SHA384-based KDF (IEEE 802.11ac)
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha384.h"
+#include "crypto.h"
+
+
+/**
+ * sha384_prf - SHA384-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2)
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+void sha384_prf(const u8 *key, size_t key_len, const char *label,
+		const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	sha384_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8);
+}
+
+
+/**
+ * sha384_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+		     const u8 *data, size_t data_len, u8 *buf,
+		     size_t buf_len_bits)
+{
+	u16 counter = 1;
+	size_t pos, plen;
+	u8 hash[SHA384_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+	u8 counter_le[2], length_le[2];
+	size_t buf_len = (buf_len_bits + 7) / 8;
+
+	addr[0] = counter_le;
+	len[0] = 2;
+	addr[1] = (u8 *) label;
+	len[1] = os_strlen(label);
+	addr[2] = data;
+	len[2] = data_len;
+	addr[3] = length_le;
+	len[3] = sizeof(length_le);
+
+	WPA_PUT_LE16(length_le, buf_len_bits);
+	pos = 0;
+	while (pos < buf_len) {
+		plen = buf_len - pos;
+		WPA_PUT_LE16(counter_le, counter);
+		if (plen >= SHA384_MAC_LEN) {
+			hmac_sha384_vector(key, key_len, 4, addr, len,
+					   &buf[pos]);
+			pos += SHA384_MAC_LEN;
+		} else {
+			hmac_sha384_vector(key, key_len, 4, addr, len, hash);
+			os_memcpy(&buf[pos], hash, plen);
+			pos += plen;
+			break;
+		}
+		counter++;
+	}
+
+	/*
+	 * Mask out unused bits in the last octet if it does not use all the
+	 * bits.
+	 */
+	if (buf_len_bits % 8) {
+		u8 mask = 0xff << (8 - buf_len_bits % 8);
+		buf[pos - 1] &= mask;
+	}
+
+	os_memset(hash, 0, sizeof(hash));
+}
diff --git a/hostap/src/crypto/sha384.h b/hostap/src/crypto/sha384.h
new file mode 100644
index 0000000..3deafa5
--- /dev/null
+++ b/hostap/src/crypto/sha384.h
@@ -0,0 +1,24 @@
+/*
+ * SHA384 hash implementation and interface functions
+ * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA384_H
+#define SHA384_H
+
+#define SHA384_MAC_LEN 48
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac);
+void sha384_prf(const u8 *key, size_t key_len, const char *label,
+		const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+		     const u8 *data, size_t data_len, u8 *buf,
+		     size_t buf_len_bits);
+
+#endif /* SHA384_H */
diff --git a/hostap/src/crypto/tls.h b/hostap/src/crypto/tls.h
new file mode 100644
index 0000000..2e56233
--- /dev/null
+++ b/hostap/src/crypto/tls.h
@@ -0,0 +1,588 @@
+/*
+ * SSL/TLS interface definition
+ * 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.
+ */
+
+#ifndef TLS_H
+#define TLS_H
+
+struct tls_connection;
+
+struct tls_random {
+	const u8 *client_random;
+	size_t client_random_len;
+	const u8 *server_random;
+	size_t server_random_len;
+};
+
+enum tls_event {
+	TLS_CERT_CHAIN_SUCCESS,
+	TLS_CERT_CHAIN_FAILURE,
+	TLS_PEER_CERTIFICATE,
+	TLS_ALERT
+};
+
+/*
+ * Note: These are used as identifier with external programs and as such, the
+ * values must not be changed.
+ */
+enum tls_fail_reason {
+	TLS_FAIL_UNSPECIFIED = 0,
+	TLS_FAIL_UNTRUSTED = 1,
+	TLS_FAIL_REVOKED = 2,
+	TLS_FAIL_NOT_YET_VALID = 3,
+	TLS_FAIL_EXPIRED = 4,
+	TLS_FAIL_SUBJECT_MISMATCH = 5,
+	TLS_FAIL_ALTSUBJECT_MISMATCH = 6,
+	TLS_FAIL_BAD_CERTIFICATE = 7,
+	TLS_FAIL_SERVER_CHAIN_PROBE = 8,
+	TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9,
+	TLS_FAIL_DOMAIN_MISMATCH = 10,
+};
+
+
+#define TLS_MAX_ALT_SUBJECT 10
+
+union tls_event_data {
+	struct {
+		int depth;
+		const char *subject;
+		enum tls_fail_reason reason;
+		const char *reason_txt;
+		const struct wpabuf *cert;
+	} cert_fail;
+
+	struct {
+		int depth;
+		const char *subject;
+		const struct wpabuf *cert;
+		const u8 *hash;
+		size_t hash_len;
+		const char *altsubject[TLS_MAX_ALT_SUBJECT];
+		int num_altsubject;
+	} peer_cert;
+
+	struct {
+		int is_local;
+		const char *type;
+		const char *description;
+	} alert;
+};
+
+struct tls_config {
+	const char *opensc_engine_path;
+	const char *pkcs11_engine_path;
+	const char *pkcs11_module_path;
+	int fips_mode;
+	int cert_in_cb;
+	const char *openssl_ciphers;
+	unsigned int tls_session_lifetime;
+
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+};
+
+#define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0)
+#define TLS_CONN_DISABLE_TIME_CHECKS BIT(1)
+#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)
+#define TLS_CONN_EAP_FAST BIT(7)
+#define TLS_CONN_DISABLE_TLSv1_0 BIT(8)
+
+/**
+ * struct tls_connection_params - Parameters for TLS connection
+ * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER
+ * format
+ * @ca_cert_blob: ca_cert as inlined data or %NULL if not used
+ * @ca_cert_blob_len: ca_cert_blob length
+ * @ca_path: Path to CA certificates (OpenSSL specific)
+ * @subject_match: String to match in the subject of the peer certificate or
+ * %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. This may allow subdomains an
+ * wildcard certificates. Each domain name label must have a full match.
+ * @domain_match: String to match in the dNSName or CN of the peer
+ * certificate or %NULL to allow all domain names. This requires a full,
+ * case-insensitive match.
+ * @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
+ * @client_cert_blob_len: client_cert_blob length
+ * @private_key: File or reference name for client private key in PEM or DER
+ * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY)
+ * @private_key_blob: private_key as inlined data or %NULL if not used
+ * @private_key_blob_len: private_key_blob length
+ * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
+ * passphrase is used.
+ * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used
+ * @dh_blob: dh_file as inlined data or %NULL if not used
+ * @dh_blob_len: dh_blob length
+ * @engine: 1 = use engine (e.g., a smartcard) for private key operations
+ * (this is OpenSSL specific for now)
+ * @engine_id: engine id string (this is OpenSSL specific for now)
+ * @ppin: pointer to the pin variable in the configuration
+ * (this is OpenSSL specific for now)
+ * @key_id: the private key's id when using engine (this is OpenSSL
+ * specific for now)
+ * @cert_id: the certificate's id when using engine
+ * @ca_cert_id: the CA certificate's id when using engine
+ * @openssl_ciphers: OpenSSL cipher configuration
+ * @flags: Parameter options (TLS_CONN_*)
+ * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response
+ *	or %NULL if OCSP is not enabled
+ *
+ * TLS connection parameters to be configured with tls_connection_set_params()
+ * and tls_global_set_params().
+ *
+ * Certificates and private key can be configured either as a reference name
+ * (file path or reference to certificate store) or by providing the same data
+ * as a pointer to the data in memory. Only one option will be used for each
+ * field.
+ */
+struct tls_connection_params {
+	const char *ca_cert;
+	const u8 *ca_cert_blob;
+	size_t ca_cert_blob_len;
+	const char *ca_path;
+	const char *subject_match;
+	const char *altsubject_match;
+	const char *suffix_match;
+	const char *domain_match;
+	const char *client_cert;
+	const u8 *client_cert_blob;
+	size_t client_cert_blob_len;
+	const char *private_key;
+	const u8 *private_key_blob;
+	size_t private_key_blob_len;
+	const char *private_key_passwd;
+	const char *dh_file;
+	const u8 *dh_blob;
+	size_t dh_blob_len;
+
+	/* OpenSSL specific variables */
+	int engine;
+	const char *engine_id;
+	const char *pin;
+	const char *key_id;
+	const char *cert_id;
+	const char *ca_cert_id;
+	const char *openssl_ciphers;
+
+	unsigned int flags;
+	const char *ocsp_stapling_response;
+};
+
+
+/**
+ * tls_init - Initialize TLS library
+ * @conf: Configuration data for TLS library
+ * Returns: Context data to be used as tls_ctx in calls to other functions,
+ * or %NULL on failure.
+ *
+ * Called once during program startup and once for each RSN pre-authentication
+ * session. In other words, there can be two concurrent TLS contexts. If global
+ * library initialization is needed (i.e., one that is shared between both
+ * authentication types), the TLS library wrapper should maintain a reference
+ * counter and do global initialization only when moving from 0 to 1 reference.
+ */
+void * tls_init(const struct tls_config *conf);
+
+/**
+ * tls_deinit - Deinitialize TLS library
+ * @tls_ctx: TLS context data from tls_init()
+ *
+ * Called once during program shutdown and once for each RSN pre-authentication
+ * session. If global library deinitialization is needed (i.e., one that is
+ * shared between both authentication types), the TLS library wrapper should
+ * maintain a reference counter and do global deinitialization only when moving
+ * from 1 to 0 references.
+ */
+void tls_deinit(void *tls_ctx);
+
+/**
+ * tls_get_errors - Process pending errors
+ * @tls_ctx: TLS context data from tls_init()
+ * Returns: Number of found error, 0 if no errors detected.
+ *
+ * Process all pending TLS errors.
+ */
+int tls_get_errors(void *tls_ctx);
+
+/**
+ * tls_connection_init - Initialize a new TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * Returns: Connection context data, conn for other function calls
+ */
+struct tls_connection * tls_connection_init(void *tls_ctx);
+
+/**
+ * tls_connection_deinit - Free TLS connection data
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Release all resources allocated for TLS connection.
+ */
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_established - Has the TLS connection been completed?
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 1 if TLS connection has been completed, 0 if not.
+ */
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_shutdown - Shutdown TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * Shutdown current TLS connection without releasing all resources. New
+ * connection can be started by using the same conn without having to call
+ * tls_connection_init() or setting certificates etc. again. The new
+ * connection should try to use session resumption.
+ */
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn);
+
+enum {
+	TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN = -4,
+	TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3,
+	TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2
+};
+
+/**
+ * tls_connection_set_params - Set TLS connection parameters
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @params: Connection parameters
+ * Returns: 0 on success, -1 on failure,
+ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine
+ * failure, or
+ * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
+ * PKCS#11 engine private key, or
+ * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine
+ * failure.
+ */
+int __must_check
+tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+			  const struct tls_connection_params *params);
+
+/**
+ * tls_global_set_params - Set TLS parameters for all TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @params: Global TLS parameters
+ * Returns: 0 on success, -1 on failure,
+ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine
+ * failure, or
+ * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
+ * PKCS#11 engine private key, or
+ * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine
+ * failure.
+ */
+int __must_check tls_global_set_params(
+	void *tls_ctx, const struct tls_connection_params *params);
+
+/**
+ * tls_global_set_verify - Set global certificate verification options
+ * @tls_ctx: TLS context data from tls_init()
+ * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate,
+ * 2 = verify CRL for all certificates
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_global_set_verify(void *tls_ctx, int check_crl);
+
+/**
+ * tls_connection_set_verify - Set certificate verification options
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @verify_peer: 1 = verify peer certificate
+ * @flags: Connection flags (TLS_CONN_*)
+ * @session_ctx: Session caching context or %NULL to use default
+ * @session_ctx_len: Length of @session_ctx in bytes.
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_set_verify(void *tls_ctx,
+					   struct tls_connection *conn,
+					   int verify_peer,
+					   unsigned int flags,
+					   const u8 *session_ctx,
+					   size_t session_ctx_len);
+
+/**
+ * tls_connection_get_random - Get random data from TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @data: Structure of client/server random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_get_random(void *tls_ctx,
+					 struct tls_connection *conn,
+					 struct tls_random *data);
+
+/**
+ * tls_connection_prf - Use TLS-PRF to derive keying material
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @label: Label (e.g., description of the key) for PRF
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @skip_keyblock: Skip TLS key block from the beginning of PRF output
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * tls_connection_prf() is required so that further keying material can be
+ * derived from the master secret. Example implementation of this function is in
+ * tls_prf_sha1_md5() when it is called with seed set to
+ * client_random|server_random (or server_random|client_random). For TLSv1.2 and
+ * newer, a different PRF is needed, though.
+ */
+int __must_check  tls_connection_prf(void *tls_ctx,
+				     struct tls_connection *conn,
+				     const char *label,
+				     int server_random_first,
+				     int skip_keyblock,
+				     u8 *out, size_t out_len);
+
+/**
+ * tls_connection_handshake - Process TLS handshake (client side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Input data from TLS server
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * Returns: Output data, %NULL on failure
+ *
+ * The caller is responsible for freeing the returned output data. If the final
+ * handshake message includes application data, this is decrypted and
+ * appl_data (if not %NULL) is set to point this data. The caller is
+ * responsible for freeing appl_data.
+ *
+ * This function is used during TLS handshake. The first call is done with
+ * in_data == %NULL and the library is expected to return ClientHello packet.
+ * This packet is then send to the server and a response from server is given
+ * to TLS library by calling this function again with in_data pointing to the
+ * TLS message from the server.
+ *
+ * If the TLS handshake fails, this function may return %NULL. However, if the
+ * TLS library has a TLS alert to send out, that should be returned as the
+ * output data. In this case, tls_connection_get_failed() must return failure
+ * (> 0).
+ *
+ * tls_connection_established() should return 1 once the TLS handshake has been
+ * completed successfully.
+ */
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+					 struct tls_connection *conn,
+					 const struct wpabuf *in_data,
+					 struct wpabuf **appl_data);
+
+struct wpabuf * tls_connection_handshake2(void *tls_ctx,
+					  struct tls_connection *conn,
+					  const struct wpabuf *in_data,
+					  struct wpabuf **appl_data,
+					  int *more_data_needed);
+
+/**
+ * tls_connection_server_handshake - Process TLS handshake (server side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Input data from TLS peer
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * Returns: Output data, %NULL on failure
+ *
+ * The caller is responsible for freeing the returned output data.
+ */
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+						struct tls_connection *conn,
+						const struct wpabuf *in_data,
+						struct wpabuf **appl_data);
+
+/**
+ * tls_connection_encrypt - Encrypt data into TLS tunnel
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Plaintext data to be encrypted
+ * Returns: Encrypted TLS data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel. The caller is responsible for freeing the
+ * returned output data.
+ */
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data);
+
+/**
+ * tls_connection_decrypt - Decrypt data from TLS tunnel
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Encrypted TLS data
+ * Returns: Decrypted TLS data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel. The caller is responsible for
+ * freeing the returned output data.
+ */
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data);
+
+struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
+					struct tls_connection *conn,
+					const struct wpabuf *in_data,
+					int *more_data_needed);
+
+/**
+ * tls_connection_resumed - Was session resumption used
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn);
+
+enum {
+	TLS_CIPHER_NONE,
+	TLS_CIPHER_RC4_SHA /* 0x0005 */,
+	TLS_CIPHER_AES128_SHA /* 0x002f */,
+	TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */,
+	TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */
+};
+
+/**
+ * tls_connection_set_cipher_list - Configure acceptable cipher suites
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_set_cipher_list(void *tls_ctx,
+						struct tls_connection *conn,
+						u8 *ciphers);
+
+/**
+ * tls_get_version - Get the current TLS version number
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @buf: Buffer for returning the TLS version number
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the currently used TLS version number.
+ */
+int __must_check tls_get_version(void *tls_ctx, struct tls_connection *conn,
+				 char *buf, size_t buflen);
+
+/**
+ * tls_get_cipher - Get current cipher name
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int __must_check tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+				char *buf, size_t buflen);
+
+/**
+ * tls_connection_enable_workaround - Enable TLS workaround options
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to enable connection-specific workaround options for
+ * buffer SSL/TLS implementations.
+ */
+int __must_check tls_connection_enable_workaround(void *tls_ctx,
+						  struct tls_connection *conn);
+
+/**
+ * tls_connection_client_hello_ext - Set TLS extension for ClientHello
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @ext_type: Extension type
+ * @data: Extension payload (%NULL to remove extension)
+ * @data_len: Extension payload length
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_client_hello_ext(void *tls_ctx,
+						 struct tls_connection *conn,
+						 int ext_type, const u8 *data,
+						 size_t data_len);
+
+/**
+ * tls_connection_get_failed - Get connection failure status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Returns >0 if connection has failed, 0 if not.
+ */
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_get_read_alerts - Get connection read alert status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Number of times a fatal read (remote end reported error) has
+ * happened during this connection.
+ */
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_get_write_alerts - Get connection write alert status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Number of times a fatal write (locally detected error) has happened
+ * during this connection.
+ */
+int tls_connection_get_write_alerts(void *tls_ctx,
+				    struct tls_connection *conn);
+
+typedef int (*tls_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+int __must_check  tls_connection_set_session_ticket_cb(
+	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);
+
+int tls_get_library_version(char *buf, size_t buf_len);
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data);
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn);
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn);
+
+void tls_connection_remove_session(struct tls_connection *conn);
+
+#endif /* TLS_H */
diff --git a/hostap/src/crypto/tls_gnutls.c b/hostap/src/crypto/tls_gnutls.c
new file mode 100644
index 0000000..f994379
--- /dev/null
+++ b/hostap/src/crypto/tls_gnutls.c
@@ -0,0 +1,1523 @@
+/*
+ * SSL/TLS interface functions for GnuTLS
+ * Copyright (c) 2004-2011, 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 <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#ifdef PKCS12_FUNCS
+#include <gnutls/pkcs12.h>
+#endif /* PKCS12_FUNCS */
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+#include <gnutls/ocsp.h>
+#endif /* 3.1.3 */
+
+#include "common.h"
+#include "crypto/crypto.h"
+#include "tls.h"
+
+
+static int tls_gnutls_ref_count = 0;
+
+struct tls_global {
+	/* Data for session resumption */
+	void *session_data;
+	size_t session_data_size;
+
+	int server;
+
+	int params_set;
+	gnutls_certificate_credentials_t xcred;
+
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+	int cert_in_cb;
+};
+
+struct tls_connection {
+	struct tls_global *global;
+	gnutls_session_t session;
+	int read_alerts, write_alerts, failed;
+
+	u8 *pre_shared_secret;
+	size_t pre_shared_secret_len;
+	int established;
+	int verify_peer;
+	unsigned int disable_time_checks:1;
+
+	struct wpabuf *push_buf;
+	struct wpabuf *pull_buf;
+	const u8 *pull_buf_offset;
+
+	int params_set;
+	gnutls_certificate_credentials_t xcred;
+
+	char *suffix_match;
+	char *domain_match;
+	unsigned int flags;
+};
+
+
+static int tls_connection_verify_peer(gnutls_session_t session);
+
+
+static void tls_log_func(int level, const char *msg)
+{
+	char *s, *pos;
+	if (level == 6 || level == 7) {
+		/* These levels seem to be mostly I/O debug and msg dumps */
+		return;
+	}
+
+	s = os_strdup(msg);
+	if (s == NULL)
+		return;
+
+	pos = s;
+	while (*pos != '\0') {
+		if (*pos == '\n') {
+			*pos = '\0';
+			break;
+		}
+		pos++;
+	}
+	wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG,
+		   "gnutls<%d> %s", level, s);
+	os_free(s);
+}
+
+
+void * tls_init(const struct tls_config *conf)
+{
+	struct tls_global *global;
+
+	if (tls_gnutls_ref_count == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: Library version %s (runtime) - %s (build)",
+			   gnutls_check_version(NULL), GNUTLS_VERSION);
+	}
+
+	global = os_zalloc(sizeof(*global));
+	if (global == NULL)
+		return NULL;
+
+	if (tls_gnutls_ref_count == 0 && gnutls_global_init() < 0) {
+		os_free(global);
+		return NULL;
+	}
+	tls_gnutls_ref_count++;
+
+	gnutls_global_set_log_function(tls_log_func);
+	if (wpa_debug_show_keys)
+		gnutls_global_set_log_level(11);
+
+	if (conf) {
+		global->event_cb = conf->event_cb;
+		global->cb_ctx = conf->cb_ctx;
+		global->cert_in_cb = conf->cert_in_cb;
+	}
+
+	return global;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+	struct tls_global *global = ssl_ctx;
+	if (global) {
+		if (global->params_set)
+			gnutls_certificate_free_credentials(global->xcred);
+		os_free(global->session_data);
+		os_free(global);
+	}
+
+	tls_gnutls_ref_count--;
+	if (tls_gnutls_ref_count == 0)
+		gnutls_global_deinit();
+}
+
+
+int tls_get_errors(void *ssl_ctx)
+{
+	return 0;
+}
+
+
+static ssize_t tls_pull_func(gnutls_transport_ptr_t ptr, void *buf,
+			     size_t len)
+{
+	struct tls_connection *conn = (struct tls_connection *) ptr;
+	const u8 *end;
+	if (conn->pull_buf == NULL) {
+		errno = EWOULDBLOCK;
+		return -1;
+	}
+
+	end = wpabuf_head_u8(conn->pull_buf) + wpabuf_len(conn->pull_buf);
+	if ((size_t) (end - conn->pull_buf_offset) < len)
+		len = end - conn->pull_buf_offset;
+	os_memcpy(buf, conn->pull_buf_offset, len);
+	conn->pull_buf_offset += len;
+	if (conn->pull_buf_offset == end) {
+		wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__);
+		wpabuf_free(conn->pull_buf);
+		conn->pull_buf = NULL;
+		conn->pull_buf_offset = NULL;
+	} else {
+		wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf",
+			   __func__,
+			   (unsigned long) (end - conn->pull_buf_offset));
+	}
+	return len;
+}
+
+
+static ssize_t tls_push_func(gnutls_transport_ptr_t ptr, const void *buf,
+			     size_t len)
+{
+	struct tls_connection *conn = (struct tls_connection *) ptr;
+
+	if (wpabuf_resize(&conn->push_buf, len) < 0) {
+		errno = ENOMEM;
+		return -1;
+	}
+	wpabuf_put_data(conn->push_buf, buf, len);
+
+	return len;
+}
+
+
+static int tls_gnutls_init_session(struct tls_global *global,
+				   struct tls_connection *conn)
+{
+	const char *err;
+	int ret;
+
+	ret = gnutls_init(&conn->session,
+			  global->server ? GNUTLS_SERVER : GNUTLS_CLIENT);
+	if (ret < 0) {
+		wpa_printf(MSG_INFO, "TLS: Failed to initialize new TLS "
+			   "connection: %s", gnutls_strerror(ret));
+		return -1;
+	}
+
+	ret = gnutls_set_default_priority(conn->session);
+	if (ret < 0)
+		goto fail;
+
+	ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0",
+					 &err);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at "
+			   "'%s'", err);
+		goto fail;
+	}
+
+	gnutls_transport_set_pull_function(conn->session, tls_pull_func);
+	gnutls_transport_set_push_function(conn->session, tls_push_func);
+	gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr_t) conn);
+	gnutls_session_set_ptr(conn->session, conn);
+
+	return 0;
+
+fail:
+	wpa_printf(MSG_INFO, "TLS: Failed to setup new TLS connection: %s",
+		   gnutls_strerror(ret));
+	gnutls_deinit(conn->session);
+	return -1;
+}
+
+
+struct tls_connection * tls_connection_init(void *ssl_ctx)
+{
+	struct tls_global *global = ssl_ctx;
+	struct tls_connection *conn;
+	int ret;
+
+	conn = os_zalloc(sizeof(*conn));
+	if (conn == NULL)
+		return NULL;
+	conn->global = global;
+
+	if (tls_gnutls_init_session(global, conn)) {
+		os_free(conn);
+		return NULL;
+	}
+
+	if (global->params_set) {
+		ret = gnutls_credentials_set(conn->session,
+					     GNUTLS_CRD_CERTIFICATE,
+					     global->xcred);
+		if (ret < 0) {
+			wpa_printf(MSG_INFO, "Failed to configure "
+				   "credentials: %s", gnutls_strerror(ret));
+			os_free(conn);
+			return NULL;
+		}
+	}
+
+	if (gnutls_certificate_allocate_credentials(&conn->xcred)) {
+		os_free(conn);
+		return NULL;
+	}
+
+	return conn;
+}
+
+
+void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return;
+
+	gnutls_certificate_free_credentials(conn->xcred);
+	gnutls_deinit(conn->session);
+	os_free(conn->pre_shared_secret);
+	wpabuf_free(conn->push_buf);
+	wpabuf_free(conn->pull_buf);
+	os_free(conn->suffix_match);
+	os_free(conn->domain_match);
+	os_free(conn);
+}
+
+
+int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
+{
+	return conn ? conn->established : 0;
+}
+
+
+int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
+{
+	struct tls_global *global = ssl_ctx;
+	int ret;
+
+	if (conn == NULL)
+		return -1;
+
+	/* Shutdown previous TLS connection without notifying the peer
+	 * because the connection was already terminated in practice
+	 * and "close notify" shutdown alert would confuse AS. */
+	gnutls_bye(conn->session, GNUTLS_SHUT_RDWR);
+	wpabuf_free(conn->push_buf);
+	conn->push_buf = NULL;
+	conn->established = 0;
+
+	gnutls_deinit(conn->session);
+	if (tls_gnutls_init_session(global, conn)) {
+		wpa_printf(MSG_INFO, "GnuTLS: Failed to preparare new session "
+			   "for session resumption use");
+		return -1;
+	}
+
+	ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
+				     conn->params_set ? conn->xcred :
+				     global->xcred);
+	if (ret < 0) {
+		wpa_printf(MSG_INFO, "GnuTLS: Failed to configure credentials "
+			   "for session resumption: %s", gnutls_strerror(ret));
+		return -1;
+	}
+
+	if (global->session_data) {
+		ret = gnutls_session_set_data(conn->session,
+					      global->session_data,
+					      global->session_data_size);
+		if (ret < 0) {
+			wpa_printf(MSG_INFO, "GnuTLS: Failed to set session "
+				   "data: %s", gnutls_strerror(ret));
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+			      const struct tls_connection_params *params)
+{
+	int ret;
+
+	if (conn == NULL || params == NULL)
+		return -1;
+
+	if (params->subject_match) {
+		wpa_printf(MSG_INFO, "GnuTLS: subject_match not supported");
+		return -1;
+	}
+
+	if (params->altsubject_match) {
+		wpa_printf(MSG_INFO, "GnuTLS: altsubject_match not supported");
+		return -1;
+	}
+
+	os_free(conn->suffix_match);
+	conn->suffix_match = NULL;
+	if (params->suffix_match) {
+		conn->suffix_match = os_strdup(params->suffix_match);
+		if (conn->suffix_match == NULL)
+			return -1;
+	}
+
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+	os_free(conn->domain_match);
+	conn->domain_match = NULL;
+	if (params->domain_match) {
+		conn->domain_match = os_strdup(params->domain_match);
+		if (conn->domain_match == NULL)
+			return -1;
+	}
+#else /* < 3.3.0 */
+	if (params->domain_match) {
+		wpa_printf(MSG_INFO, "GnuTLS: domain_match not supported");
+		return -1;
+	}
+#endif /* >= 3.3.0 */
+
+	conn->flags = params->flags;
+
+	if (params->openssl_ciphers) {
+		wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
+		return -1;
+	}
+
+	/* TODO: gnutls_certificate_set_verify_flags(xcred, flags); 
+	 * to force peer validation(?) */
+
+	if (params->ca_cert) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: Try to parse %s in DER format",
+			   params->ca_cert);
+		ret = gnutls_certificate_set_x509_trust_file(
+			conn->xcred, params->ca_cert, GNUTLS_X509_FMT_DER);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Failed to read CA cert '%s' in DER format (%s) - try in PEM format",
+				   params->ca_cert,
+				   gnutls_strerror(ret));
+			ret = gnutls_certificate_set_x509_trust_file(
+				conn->xcred, params->ca_cert,
+				GNUTLS_X509_FMT_PEM);
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "Failed to read CA cert '%s' in PEM format: %s",
+					   params->ca_cert,
+					   gnutls_strerror(ret));
+				return -1;
+			}
+		}
+	} else if (params->ca_cert_blob) {
+		gnutls_datum_t ca;
+
+		ca.data = (unsigned char *) params->ca_cert_blob;
+		ca.size = params->ca_cert_blob_len;
+
+		ret = gnutls_certificate_set_x509_trust_mem(
+			conn->xcred, &ca, GNUTLS_X509_FMT_DER);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "Failed to parse CA cert in DER format: %s",
+				   gnutls_strerror(ret));
+			ret = gnutls_certificate_set_x509_trust_mem(
+				conn->xcred, &ca, GNUTLS_X509_FMT_PEM);
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "Failed to parse CA cert in PEM format: %s",
+					   gnutls_strerror(ret));
+				return -1;
+			}
+		}
+	} else if (params->ca_path) {
+		wpa_printf(MSG_INFO, "GnuTLS: ca_path not supported");
+		return -1;
+	}
+
+	conn->disable_time_checks = 0;
+	if (params->ca_cert || params->ca_cert_blob) {
+		conn->verify_peer = 1;
+		gnutls_certificate_set_verify_function(
+			conn->xcred, tls_connection_verify_peer);
+
+		if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
+			gnutls_certificate_set_verify_flags(
+				conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
+		}
+
+		if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
+			conn->disable_time_checks = 1;
+			gnutls_certificate_set_verify_flags(
+				conn->xcred,
+				GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
+		}
+	}
+
+	if (params->client_cert && params->private_key) {
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+		ret = gnutls_certificate_set_x509_key_file2(
+			conn->xcred, params->client_cert, params->private_key,
+			GNUTLS_X509_FMT_DER, params->private_key_passwd, 0);
+#else
+		/* private_key_passwd not (easily) supported here */
+		ret = gnutls_certificate_set_x509_key_file(
+			conn->xcred, params->client_cert, params->private_key,
+			GNUTLS_X509_FMT_DER);
+#endif
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
+				   "in DER format: %s", gnutls_strerror(ret));
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+			ret = gnutls_certificate_set_x509_key_file2(
+				conn->xcred, params->client_cert,
+				params->private_key, GNUTLS_X509_FMT_PEM,
+				params->private_key_passwd, 0);
+#else
+			ret = gnutls_certificate_set_x509_key_file(
+				conn->xcred, params->client_cert,
+				params->private_key, GNUTLS_X509_FMT_PEM);
+#endif
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG, "Failed to read client "
+					   "cert/key in PEM format: %s",
+					   gnutls_strerror(ret));
+				return ret;
+			}
+		}
+	} else if (params->private_key) {
+		int pkcs12_ok = 0;
+#ifdef PKCS12_FUNCS
+		/* Try to load in PKCS#12 format */
+		ret = gnutls_certificate_set_x509_simple_pkcs12_file(
+			conn->xcred, params->private_key, GNUTLS_X509_FMT_DER,
+			params->private_key_passwd);
+		if (ret != 0) {
+			wpa_printf(MSG_DEBUG, "Failed to load private_key in "
+				   "PKCS#12 format: %s", gnutls_strerror(ret));
+			return -1;
+		} else
+			pkcs12_ok = 1;
+#endif /* PKCS12_FUNCS */
+
+		if (!pkcs12_ok) {
+			wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
+				   "included");
+			return -1;
+		}
+	} else if (params->client_cert_blob && params->private_key_blob) {
+		gnutls_datum_t cert, key;
+
+		cert.data = (unsigned char *) params->client_cert_blob;
+		cert.size = params->client_cert_blob_len;
+		key.data = (unsigned char *) params->private_key_blob;
+		key.size = params->private_key_blob_len;
+
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+		ret = gnutls_certificate_set_x509_key_mem2(
+			conn->xcred, &cert, &key, GNUTLS_X509_FMT_DER,
+			params->private_key_passwd, 0);
+#else
+		/* private_key_passwd not (easily) supported here */
+		ret = gnutls_certificate_set_x509_key_mem(
+			conn->xcred, &cert, &key, GNUTLS_X509_FMT_DER);
+#endif
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
+				   "in DER format: %s", gnutls_strerror(ret));
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+			ret = gnutls_certificate_set_x509_key_mem2(
+				conn->xcred, &cert, &key, GNUTLS_X509_FMT_PEM,
+				params->private_key_passwd, 0);
+#else
+			/* private_key_passwd not (easily) supported here */
+			ret = gnutls_certificate_set_x509_key_mem(
+				conn->xcred, &cert, &key, GNUTLS_X509_FMT_PEM);
+#endif
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG, "Failed to read client "
+					   "cert/key in PEM format: %s",
+					   gnutls_strerror(ret));
+				return ret;
+			}
+		}
+	} else if (params->private_key_blob) {
+#ifdef PKCS12_FUNCS
+		gnutls_datum_t key;
+
+		key.data = (unsigned char *) params->private_key_blob;
+		key.size = params->private_key_blob_len;
+
+		/* Try to load in PKCS#12 format */
+		ret = gnutls_certificate_set_x509_simple_pkcs12_mem(
+			conn->xcred, &key, GNUTLS_X509_FMT_DER,
+			params->private_key_passwd);
+		if (ret != 0) {
+			wpa_printf(MSG_DEBUG, "Failed to load private_key in "
+				   "PKCS#12 format: %s", gnutls_strerror(ret));
+			return -1;
+		}
+#else /* PKCS12_FUNCS */
+		wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not included");
+		return -1;
+#endif /* PKCS12_FUNCS */
+	}
+
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+	if (params->flags & (TLS_CONN_REQUEST_OCSP | TLS_CONN_REQUIRE_OCSP)) {
+		ret = gnutls_ocsp_status_request_enable_client(conn->session,
+							       NULL, 0, NULL);
+		if (ret != GNUTLS_E_SUCCESS) {
+			wpa_printf(MSG_INFO,
+				   "GnuTLS: Failed to enable OCSP client");
+			return -1;
+		}
+	}
+#else /* 3.1.3 */
+	if (params->flags & TLS_CONN_REQUIRE_OCSP) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: OCSP not supported by this version of GnuTLS");
+		return -1;
+	}
+#endif /* 3.1.3 */
+
+	conn->params_set = 1;
+
+	ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
+				     conn->xcred);
+	if (ret < 0) {
+		wpa_printf(MSG_INFO, "Failed to configure credentials: %s",
+			   gnutls_strerror(ret));
+	}
+
+	return ret;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+			  const struct tls_connection_params *params)
+{
+	struct tls_global *global = tls_ctx;
+	int ret;
+
+	/* Currently, global parameters are only set when running in server
+	 * mode. */
+	global->server = 1;
+
+	if (global->params_set) {
+		gnutls_certificate_free_credentials(global->xcred);
+		global->params_set = 0;
+	}
+
+	ret = gnutls_certificate_allocate_credentials(&global->xcred);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "Failed to allocate global credentials "
+			   "%s", gnutls_strerror(ret));
+		return -1;
+	}
+
+	if (params->ca_cert) {
+		ret = gnutls_certificate_set_x509_trust_file(
+			global->xcred, params->ca_cert, GNUTLS_X509_FMT_DER);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
+				   "in DER format: %s", params->ca_cert,
+				   gnutls_strerror(ret));
+			ret = gnutls_certificate_set_x509_trust_file(
+				global->xcred, params->ca_cert,
+				GNUTLS_X509_FMT_PEM);
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG, "Failed to read CA cert "
+					   "'%s' in PEM format: %s",
+					   params->ca_cert,
+					   gnutls_strerror(ret));
+				goto fail;
+			}
+		}
+
+		if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
+			gnutls_certificate_set_verify_flags(
+				global->xcred,
+				GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
+		}
+
+		if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
+			gnutls_certificate_set_verify_flags(
+				global->xcred,
+				GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
+		}
+	}
+
+	if (params->client_cert && params->private_key) {
+		/* TODO: private_key_passwd? */
+		ret = gnutls_certificate_set_x509_key_file(
+			global->xcred, params->client_cert,
+			params->private_key, GNUTLS_X509_FMT_DER);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
+				   "in DER format: %s", gnutls_strerror(ret));
+			ret = gnutls_certificate_set_x509_key_file(
+				global->xcred, params->client_cert,
+				params->private_key, GNUTLS_X509_FMT_PEM);
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG, "Failed to read client "
+					   "cert/key in PEM format: %s",
+					   gnutls_strerror(ret));
+				goto fail;
+			}
+		}
+	} else if (params->private_key) {
+		int pkcs12_ok = 0;
+#ifdef PKCS12_FUNCS
+		/* Try to load in PKCS#12 format */
+		ret = gnutls_certificate_set_x509_simple_pkcs12_file(
+			global->xcred, params->private_key,
+			GNUTLS_X509_FMT_DER, params->private_key_passwd);
+		if (ret != 0) {
+			wpa_printf(MSG_DEBUG, "Failed to load private_key in "
+				   "PKCS#12 format: %s", gnutls_strerror(ret));
+			goto fail;
+		} else
+			pkcs12_ok = 1;
+#endif /* PKCS12_FUNCS */
+
+		if (!pkcs12_ok) {
+			wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
+				   "included");
+			goto fail;
+		}
+	}
+
+	global->params_set = 1;
+
+	return 0;
+
+fail:
+	gnutls_certificate_free_credentials(global->xcred);
+	return -1;
+}
+
+
+int tls_global_set_verify(void *ssl_ctx, int check_crl)
+{
+	/* TODO */
+	return 0;
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
+{
+	if (conn == NULL || conn->session == NULL)
+		return -1;
+
+	conn->verify_peer = verify_peer;
+	gnutls_certificate_server_set_request(conn->session,
+					      verify_peer ? GNUTLS_CERT_REQUIRE
+					      : GNUTLS_CERT_REQUEST);
+
+	return 0;
+}
+
+
+int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
+			    struct tls_random *keys)
+{
+#if GNUTLS_VERSION_NUMBER >= 0x030012
+	gnutls_datum_t client, server;
+
+	if (conn == NULL || conn->session == NULL || keys == NULL)
+		return -1;
+
+	os_memset(keys, 0, sizeof(*keys));
+	gnutls_session_get_random(conn->session, &client, &server);
+	keys->client_random = client.data;
+	keys->server_random = server.data;
+	keys->client_random_len = client.size;
+	keys->server_random_len = client.size;
+
+	return 0;
+#else /* 3.0.18 */
+	return -1;
+#endif /* 3.0.18 */
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+		       const char *label, int server_random_first,
+		       int skip_keyblock, u8 *out, size_t out_len)
+{
+	if (conn == NULL || conn->session == NULL || skip_keyblock)
+		return -1;
+
+	return gnutls_prf(conn->session, os_strlen(label), label,
+			  server_random_first, 0, NULL, out_len, (char *) out);
+}
+
+
+static void gnutls_tls_fail_event(struct tls_connection *conn,
+				  const gnutls_datum_t *cert, int depth,
+				  const char *subject, const char *err_str,
+				  enum tls_fail_reason reason)
+{
+	union tls_event_data ev;
+	struct tls_global *global = conn->global;
+	struct wpabuf *cert_buf = NULL;
+
+	if (global->event_cb == NULL)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	ev.cert_fail.depth = depth;
+	ev.cert_fail.subject = subject ? subject : "";
+	ev.cert_fail.reason = reason;
+	ev.cert_fail.reason_txt = err_str;
+	if (cert) {
+		cert_buf = wpabuf_alloc_copy(cert->data, cert->size);
+		ev.cert_fail.cert = cert_buf;
+	}
+	global->event_cb(global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+	wpabuf_free(cert_buf);
+}
+
+
+#if GNUTLS_VERSION_NUMBER < 0x030300
+static int server_eku_purpose(gnutls_x509_crt_t cert)
+{
+	unsigned int i;
+
+	for (i = 0; ; i++) {
+		char oid[128];
+		size_t oid_size = sizeof(oid);
+		int res;
+
+		res = gnutls_x509_crt_get_key_purpose_oid(cert, i, oid,
+							  &oid_size, NULL);
+		if (res == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+			if (i == 0) {
+				/* No EKU - assume any use allowed */
+				return 1;
+			}
+			break;
+		}
+
+		if (res < 0) {
+			wpa_printf(MSG_INFO, "GnuTLS: Failed to get EKU");
+			return 0;
+		}
+
+		wpa_printf(MSG_DEBUG, "GnuTLS: Certificate purpose: %s", oid);
+		if (os_strcmp(oid, GNUTLS_KP_TLS_WWW_SERVER) == 0 ||
+		    os_strcmp(oid, GNUTLS_KP_ANY) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+#endif /* < 3.3.0 */
+
+
+static int check_ocsp(struct tls_connection *conn, gnutls_session_t session,
+		      gnutls_alert_description_t *err)
+{
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+	gnutls_datum_t response, buf;
+	gnutls_ocsp_resp_t resp;
+	unsigned int cert_status;
+	int res;
+
+	if (!(conn->flags & (TLS_CONN_REQUEST_OCSP | TLS_CONN_REQUIRE_OCSP)))
+		return 0;
+
+	if (!gnutls_ocsp_status_request_is_checked(session, 0)) {
+		if (conn->flags & TLS_CONN_REQUIRE_OCSP) {
+			wpa_printf(MSG_INFO,
+				   "GnuTLS: No valid OCSP response received");
+			goto ocsp_error;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: Valid OCSP response was not received - continue since OCSP was not required");
+		return 0;
+	}
+
+	/*
+	 * GnuTLS has already verified the OCSP response in
+	 * check_ocsp_response() and rejected handshake if the certificate was
+	 * found to be revoked. However, if the response indicates that the
+	 * status is unknown, handshake continues and reaches here. We need to
+	 * re-import the OCSP response to check for unknown certificate status,
+	 * but we do not need to repeat gnutls_ocsp_resp_check_crt() and
+	 * gnutls_ocsp_resp_verify_direct() calls.
+	 */
+
+	res = gnutls_ocsp_status_request_get(session, &response);
+	if (res != GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: OCSP response was received, but it was not valid");
+		goto ocsp_error;
+	}
+
+	if (gnutls_ocsp_resp_init(&resp) != GNUTLS_E_SUCCESS)
+		goto ocsp_error;
+
+	res = gnutls_ocsp_resp_import(resp, &response);
+	if (res != GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: Could not parse received OCSP response: %s",
+			   gnutls_strerror(res));
+		gnutls_ocsp_resp_deinit(resp);
+		goto ocsp_error;
+	}
+
+	res = gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &buf);
+	if (res == GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: %s", buf.data);
+		gnutls_free(buf.data);
+	}
+
+	res = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL,
+					  NULL, &cert_status, NULL,
+					  NULL, NULL, NULL);
+	gnutls_ocsp_resp_deinit(resp);
+	if (res != GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: Failed to extract OCSP information: %s",
+			   gnutls_strerror(res));
+		goto ocsp_error;
+	}
+
+	if (cert_status == GNUTLS_OCSP_CERT_GOOD) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: OCSP cert status: good");
+	} else if (cert_status == GNUTLS_OCSP_CERT_REVOKED) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP cert status: revoked");
+		goto ocsp_error;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP cert status: unknown");
+		if (conn->flags & TLS_CONN_REQUIRE_OCSP)
+			goto ocsp_error;
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP was not required, so allow connection to continue");
+	}
+
+	return 0;
+
+ocsp_error:
+	gnutls_tls_fail_event(conn, NULL, 0, NULL,
+			      "bad certificate status response",
+			      TLS_FAIL_REVOKED);
+	*err = GNUTLS_A_CERTIFICATE_REVOKED;
+	return -1;
+#else /* GnuTLS 3.1.3 or newer */
+	return 0;
+#endif /* GnuTLS 3.1.3 or newer */
+}
+
+
+static int tls_connection_verify_peer(gnutls_session_t session)
+{
+	struct tls_connection *conn;
+	unsigned int status, num_certs, i;
+	struct os_time now;
+	const gnutls_datum_t *certs;
+	gnutls_x509_crt_t cert;
+	gnutls_alert_description_t err;
+	int res;
+
+	conn = gnutls_session_get_ptr(session);
+	if (!conn->verify_peer) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: No peer certificate verification enabled");
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "GnuTSL: Verifying peer certificate");
+
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+	{
+		gnutls_typed_vdata_st data[1];
+		unsigned int elements = 0;
+
+		os_memset(data, 0, sizeof(data));
+		if (!conn->global->server) {
+			data[elements].type = GNUTLS_DT_KEY_PURPOSE_OID;
+			data[elements].data = (void *) GNUTLS_KP_TLS_WWW_SERVER;
+			elements++;
+		}
+		res = gnutls_certificate_verify_peers(session, data, 1,
+						      &status);
+	}
+#else /* < 3.3.0 */
+	res = gnutls_certificate_verify_peers2(session, &status);
+#endif
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "TLS: Failed to verify peer "
+			   "certificate chain");
+		err = GNUTLS_A_INTERNAL_ERROR;
+		goto out;
+	}
+
+#if GNUTLS_VERSION_NUMBER >= 0x030104
+	{
+		gnutls_datum_t info;
+		int ret, type;
+
+		type = gnutls_certificate_type_get(session);
+		ret = gnutls_certificate_verification_status_print(status, type,
+								   &info, 0);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Failed to print verification status");
+			err = GNUTLS_A_INTERNAL_ERROR;
+			goto out;
+		}
+		wpa_printf(MSG_DEBUG, "GnuTLS: %s", info.data);
+		gnutls_free(info.data);
+	}
+#endif /* GnuTLS 3.1.4 or newer */
+
+	certs = gnutls_certificate_get_peers(session, &num_certs);
+	if (certs == NULL || num_certs == 0) {
+		wpa_printf(MSG_INFO, "TLS: No peer certificate chain received");
+		err = GNUTLS_A_UNKNOWN_CA;
+		goto out;
+	}
+
+	if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
+		wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
+		if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
+			wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
+				   "algorithm");
+			gnutls_tls_fail_event(conn, NULL, 0, NULL,
+					      "certificate uses insecure algorithm",
+					      TLS_FAIL_BAD_CERTIFICATE);
+			err = GNUTLS_A_INSUFFICIENT_SECURITY;
+			goto out;
+		}
+		if (status & GNUTLS_CERT_NOT_ACTIVATED) {
+			wpa_printf(MSG_INFO, "TLS: Certificate not yet "
+				   "activated");
+			gnutls_tls_fail_event(conn, NULL, 0, NULL,
+					      "certificate not yet valid",
+					      TLS_FAIL_NOT_YET_VALID);
+			err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			goto out;
+		}
+		if (status & GNUTLS_CERT_EXPIRED) {
+			wpa_printf(MSG_INFO, "TLS: Certificate expired");
+			gnutls_tls_fail_event(conn, NULL, 0, NULL,
+					      "certificate has expired",
+					      TLS_FAIL_EXPIRED);
+			err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			goto out;
+		}
+		gnutls_tls_fail_event(conn, NULL, 0, NULL,
+				      "untrusted certificate",
+				      TLS_FAIL_UNTRUSTED);
+		err = GNUTLS_A_INTERNAL_ERROR;
+		goto out;
+	}
+
+	if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
+		wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a "
+			   "known issuer");
+		gnutls_tls_fail_event(conn, NULL, 0, NULL, "signed not found",
+				      TLS_FAIL_UNTRUSTED);
+		err = GNUTLS_A_UNKNOWN_CA;
+		goto out;
+	}
+
+	if (status & GNUTLS_CERT_REVOKED) {
+		wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked");
+		gnutls_tls_fail_event(conn, NULL, 0, NULL,
+				      "certificate revoked",
+				      TLS_FAIL_REVOKED);
+		err = GNUTLS_A_CERTIFICATE_REVOKED;
+		goto out;
+	}
+
+	if (status != 0) {
+		wpa_printf(MSG_INFO, "TLS: Unknown verification status: %d",
+			   status);
+		err = GNUTLS_A_INTERNAL_ERROR;
+		goto out;
+	}
+
+	if (check_ocsp(conn, session, &err))
+		goto out;
+
+	os_get_time(&now);
+
+	for (i = 0; i < num_certs; i++) {
+		char *buf;
+		size_t len;
+		if (gnutls_x509_crt_init(&cert) < 0) {
+			wpa_printf(MSG_INFO, "TLS: Certificate initialization "
+				   "failed");
+			err = GNUTLS_A_BAD_CERTIFICATE;
+			goto out;
+		}
+
+		if (gnutls_x509_crt_import(cert, &certs[i],
+					   GNUTLS_X509_FMT_DER) < 0) {
+			wpa_printf(MSG_INFO, "TLS: Could not parse peer "
+				   "certificate %d/%d", i + 1, num_certs);
+			gnutls_x509_crt_deinit(cert);
+			err = GNUTLS_A_BAD_CERTIFICATE;
+			goto out;
+		}
+
+		gnutls_x509_crt_get_dn(cert, NULL, &len);
+		len++;
+		buf = os_malloc(len + 1);
+		if (buf) {
+			buf[0] = buf[len] = '\0';
+			gnutls_x509_crt_get_dn(cert, buf, &len);
+		}
+		wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s",
+			   i + 1, num_certs, buf);
+
+		if (conn->global->event_cb) {
+			struct wpabuf *cert_buf = NULL;
+			union tls_event_data ev;
+#ifdef CONFIG_SHA256
+			u8 hash[32];
+			const u8 *_addr[1];
+			size_t _len[1];
+#endif /* CONFIG_SHA256 */
+
+			os_memset(&ev, 0, sizeof(ev));
+			if (conn->global->cert_in_cb) {
+				cert_buf = wpabuf_alloc_copy(certs[i].data,
+							     certs[i].size);
+				ev.peer_cert.cert = cert_buf;
+			}
+#ifdef CONFIG_SHA256
+			_addr[0] = certs[i].data;
+			_len[0] = certs[i].size;
+			if (sha256_vector(1, _addr, _len, hash) == 0) {
+				ev.peer_cert.hash = hash;
+				ev.peer_cert.hash_len = sizeof(hash);
+			}
+#endif /* CONFIG_SHA256 */
+			ev.peer_cert.depth = i;
+			ev.peer_cert.subject = buf;
+			conn->global->event_cb(conn->global->cb_ctx,
+					       TLS_PEER_CERTIFICATE, &ev);
+			wpabuf_free(cert_buf);
+		}
+
+		if (i == 0) {
+			if (conn->suffix_match &&
+			    !gnutls_x509_crt_check_hostname(
+				    cert, conn->suffix_match)) {
+				wpa_printf(MSG_WARNING,
+					   "TLS: Domain suffix match '%s' not found",
+					   conn->suffix_match);
+				gnutls_tls_fail_event(
+					conn, &certs[i], i, buf,
+					"Domain suffix mismatch",
+					TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
+				err = GNUTLS_A_BAD_CERTIFICATE;
+				gnutls_x509_crt_deinit(cert);
+				os_free(buf);
+				goto out;
+			}
+
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+			if (conn->domain_match &&
+			    !gnutls_x509_crt_check_hostname2(
+				    cert, conn->domain_match,
+				    GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) {
+				wpa_printf(MSG_WARNING,
+					   "TLS: Domain match '%s' not found",
+					   conn->domain_match);
+				gnutls_tls_fail_event(
+					conn, &certs[i], i, buf,
+					"Domain mismatch",
+					TLS_FAIL_DOMAIN_MISMATCH);
+				err = GNUTLS_A_BAD_CERTIFICATE;
+				gnutls_x509_crt_deinit(cert);
+				os_free(buf);
+				goto out;
+			}
+#endif /* >= 3.3.0 */
+
+			/* TODO: validate altsubject_match.
+			 * For now, any such configuration is rejected in
+			 * tls_connection_set_params() */
+
+#if GNUTLS_VERSION_NUMBER < 0x030300
+			/*
+			 * gnutls_certificate_verify_peers() not available, so
+			 * need to check EKU separately.
+			 */
+			if (!conn->global->server &&
+			    !server_eku_purpose(cert)) {
+				wpa_printf(MSG_WARNING,
+					   "GnuTLS: No server EKU");
+				gnutls_tls_fail_event(
+					conn, &certs[i], i, buf,
+					"No server EKU",
+					TLS_FAIL_BAD_CERTIFICATE);
+				err = GNUTLS_A_BAD_CERTIFICATE;
+				gnutls_x509_crt_deinit(cert);
+				os_free(buf);
+				goto out;
+			}
+#endif /* < 3.3.0 */
+		}
+
+		if (!conn->disable_time_checks &&
+		    (gnutls_x509_crt_get_expiration_time(cert) < now.sec ||
+		     gnutls_x509_crt_get_activation_time(cert) > now.sec)) {
+			wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is "
+				   "not valid at this time",
+				   i + 1, num_certs);
+			gnutls_tls_fail_event(
+				conn, &certs[i], i, buf,
+				"Certificate is not valid at this time",
+				TLS_FAIL_EXPIRED);
+			gnutls_x509_crt_deinit(cert);
+			os_free(buf);
+			err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			goto out;
+		}
+
+		os_free(buf);
+
+		gnutls_x509_crt_deinit(cert);
+	}
+
+	if (conn->global->event_cb != NULL)
+		conn->global->event_cb(conn->global->cb_ctx,
+				       TLS_CERT_CHAIN_SUCCESS, NULL);
+
+	return 0;
+
+out:
+	conn->failed++;
+	gnutls_alert_send(session, GNUTLS_AL_FATAL, err);
+	return GNUTLS_E_CERTIFICATE_ERROR;
+}
+
+
+static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn)
+{
+	int res;
+	struct wpabuf *ad;
+	wpa_printf(MSG_DEBUG, "GnuTLS: Check for possible Application Data");
+	ad = wpabuf_alloc((wpabuf_len(conn->pull_buf) + 500) * 3);
+	if (ad == NULL)
+		return NULL;
+
+	res = gnutls_record_recv(conn->session, wpabuf_mhead(ad),
+				 wpabuf_size(ad));
+	wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
+			   "(%s)", __func__, (int) res,
+			   gnutls_strerror(res));
+		wpabuf_free(ad);
+		return NULL;
+	}
+
+	wpabuf_put(ad, res);
+	wpa_printf(MSG_DEBUG, "GnuTLS: Received %d bytes of Application Data",
+		   res);
+	return ad;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+					 struct tls_connection *conn,
+					 const struct wpabuf *in_data,
+					 struct wpabuf **appl_data)
+{
+	struct tls_global *global = tls_ctx;
+	struct wpabuf *out_data;
+	int ret;
+
+	if (appl_data)
+		*appl_data = NULL;
+
+	if (in_data && wpabuf_len(in_data) > 0) {
+		if (conn->pull_buf) {
+			wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
+				   "pull_buf", __func__,
+				   (unsigned long) wpabuf_len(conn->pull_buf));
+			wpabuf_free(conn->pull_buf);
+		}
+		conn->pull_buf = wpabuf_dup(in_data);
+		if (conn->pull_buf == NULL)
+			return NULL;
+		conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
+	}
+
+	ret = gnutls_handshake(conn->session);
+	if (ret < 0) {
+		gnutls_alert_description_t alert;
+
+		switch (ret) {
+		case GNUTLS_E_AGAIN:
+			if (global->server && conn->established &&
+			    conn->push_buf == NULL) {
+				/* Need to return something to trigger
+				 * completion of EAP-TLS. */
+				conn->push_buf = wpabuf_alloc(0);
+			}
+			break;
+		case GNUTLS_E_FATAL_ALERT_RECEIVED:
+			alert = gnutls_alert_get(conn->session);
+			wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
+				   __func__, gnutls_alert_get_name(alert));
+			conn->read_alerts++;
+			if (conn->global->event_cb != NULL) {
+				union tls_event_data ev;
+
+				os_memset(&ev, 0, sizeof(ev));
+				ev.alert.is_local = 0;
+				ev.alert.type = gnutls_alert_get_name(alert);
+				ev.alert.description = ev.alert.type;
+				conn->global->event_cb(conn->global->cb_ctx,
+						       TLS_ALERT, &ev);
+			}
+			/* continue */
+		default:
+			wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
+				   "-> %s", __func__, gnutls_strerror(ret));
+			conn->failed++;
+		}
+	} else {
+		size_t size;
+
+		wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
+
+#if GNUTLS_VERSION_NUMBER >= 0x03010a
+		{
+			char *desc;
+
+			desc = gnutls_session_get_desc(conn->session);
+			if (desc) {
+				wpa_printf(MSG_DEBUG, "GnuTLS: %s", desc);
+				gnutls_free(desc);
+			}
+		}
+#endif /* GnuTLS 3.1.10 or newer */
+
+		conn->established = 1;
+		if (conn->push_buf == NULL) {
+			/* Need to return something to get final TLS ACK. */
+			conn->push_buf = wpabuf_alloc(0);
+		}
+
+		gnutls_session_get_data(conn->session, NULL, &size);
+		if (global->session_data == NULL ||
+		    global->session_data_size < size) {
+			os_free(global->session_data);
+			global->session_data = os_malloc(size);
+		}
+		if (global->session_data) {
+			global->session_data_size = size;
+			gnutls_session_get_data(conn->session,
+						global->session_data,
+						&global->session_data_size);
+		}
+
+		if (conn->pull_buf && appl_data)
+			*appl_data = gnutls_get_appl_data(conn);
+	}
+
+	out_data = conn->push_buf;
+	conn->push_buf = NULL;
+	return out_data;
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+						struct tls_connection *conn,
+						const struct wpabuf *in_data,
+						struct wpabuf **appl_data)
+{
+	return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	ssize_t res;
+	struct wpabuf *buf;
+
+	res = gnutls_record_send(conn->session, wpabuf_head(in_data),
+				 wpabuf_len(in_data));
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "%s: Encryption failed: %s",
+			   __func__, gnutls_strerror(res));
+		return NULL;
+	}
+
+	buf = conn->push_buf;
+	conn->push_buf = NULL;
+	return buf;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	ssize_t res;
+	struct wpabuf *out;
+
+	if (conn->pull_buf) {
+		wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
+			   "pull_buf", __func__,
+			   (unsigned long) wpabuf_len(conn->pull_buf));
+		wpabuf_free(conn->pull_buf);
+	}
+	conn->pull_buf = wpabuf_dup(in_data);
+	if (conn->pull_buf == NULL)
+		return NULL;
+	conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
+
+	/*
+	 * Even though we try to disable TLS compression, it is possible that
+	 * this cannot be done with all TLS libraries. Add extra buffer space
+	 * to handle the possibility of the decrypted data being longer than
+	 * input data.
+	 */
+	out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+	if (out == NULL)
+		return NULL;
+
+	res = gnutls_record_recv(conn->session, wpabuf_mhead(out),
+				 wpabuf_size(out));
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
+			   "(%s)", __func__, (int) res, gnutls_strerror(res));
+		wpabuf_free(out);
+		return NULL;
+	}
+	wpabuf_put(out, res);
+
+	return out;
+}
+
+
+int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return 0;
+	return gnutls_session_is_resumed(conn->session);
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+				   u8 *ciphers)
+{
+	/* TODO */
+	return -1;
+}
+
+
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	/* TODO */
+	return -1;
+}
+
+
+int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
+		   char *buf, size_t buflen)
+{
+	/* TODO */
+	buf[0] = '\0';
+	return 0;
+}
+
+
+int tls_connection_enable_workaround(void *ssl_ctx,
+				     struct tls_connection *conn)
+{
+	gnutls_record_disable_padding(conn->session);
+	return 0;
+}
+
+
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+				    int ext_type, const u8 *data,
+				    size_t data_len)
+{
+	/* TODO */
+	return -1;
+}
+
+
+int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return -1;
+	return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return -1;
+	return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return -1;
+	return conn->write_alerts;
+}
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+					 struct tls_connection *conn,
+					 tls_session_ticket_cb cb, void *ctx)
+{
+	return -1;
+}
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s",
+			   GNUTLS_VERSION, gnutls_check_version(NULL));
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	return NULL;
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+}
diff --git a/hostap/src/crypto/tls_internal.c b/hostap/src/crypto/tls_internal.c
new file mode 100644
index 0000000..704751d
--- /dev/null
+++ b/hostap/src/crypto/tls_internal.c
@@ -0,0 +1,733 @@
+/*
+ * TLS interface functions and an internal TLS implementation
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file interface functions for hostapd/wpa_supplicant to use the
+ * integrated TLSv1 implementation.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "tls.h"
+#include "tls/tlsv1_client.h"
+#include "tls/tlsv1_server.h"
+
+
+static int tls_ref_count = 0;
+
+struct tls_global {
+	int server;
+	struct tlsv1_credentials *server_cred;
+	int check_crl;
+};
+
+struct tls_connection {
+	struct tlsv1_client *client;
+	struct tlsv1_server *server;
+	struct tls_global *global;
+};
+
+
+void * tls_init(const struct tls_config *conf)
+{
+	struct tls_global *global;
+
+	if (tls_ref_count == 0) {
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+		if (tlsv1_client_global_init())
+			return NULL;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+		if (tlsv1_server_global_init())
+			return NULL;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	}
+	tls_ref_count++;
+
+	global = os_zalloc(sizeof(*global));
+	if (global == NULL)
+		return NULL;
+
+	return global;
+}
+
+void tls_deinit(void *ssl_ctx)
+{
+	struct tls_global *global = ssl_ctx;
+	tls_ref_count--;
+	if (tls_ref_count == 0) {
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+		tlsv1_client_global_deinit();
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+		tlsv1_cred_free(global->server_cred);
+		tlsv1_server_global_deinit();
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	}
+	os_free(global);
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+	return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+	struct tls_connection *conn;
+	struct tls_global *global = tls_ctx;
+
+	conn = os_zalloc(sizeof(*conn));
+	if (conn == NULL)
+		return NULL;
+	conn->global = global;
+
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (!global->server) {
+		conn->client = tlsv1_client_init();
+		if (conn->client == NULL) {
+			os_free(conn);
+			return NULL;
+		}
+	}
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (global->server) {
+		conn->server = tlsv1_server_init(global->server_cred);
+		if (conn->server == NULL) {
+			os_free(conn);
+			return NULL;
+		}
+	}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+
+	return conn;
+}
+
+
+#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)
+		return;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		tlsv1_client_deinit(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		tlsv1_server_deinit(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	os_free(conn);
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_established(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_established(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return 0;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_shutdown(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_shutdown(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+			      const struct tls_connection_params *params)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	struct tlsv1_credentials *cred;
+
+	if (conn->client == NULL)
+		return -1;
+
+	cred = tlsv1_cred_alloc();
+	if (cred == NULL)
+		return -1;
+
+	if (params->subject_match) {
+		wpa_printf(MSG_INFO, "TLS: subject_match not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (params->altsubject_match) {
+		wpa_printf(MSG_INFO, "TLS: altsubject_match not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (params->suffix_match) {
+		wpa_printf(MSG_INFO, "TLS: suffix_match not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (params->domain_match) {
+		wpa_printf(MSG_INFO, "TLS: domain_match not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (params->openssl_ciphers) {
+		wpa_printf(MSG_INFO, "TLS: openssl_ciphers not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (tlsv1_set_ca_cert(cred, params->ca_cert,
+			      params->ca_cert_blob, params->ca_cert_blob_len,
+			      params->ca_path)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
+			   "certificates");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (tlsv1_set_cert(cred, params->client_cert,
+			   params->client_cert_blob,
+			   params->client_cert_blob_len)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to configure client "
+			   "certificate");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (tlsv1_set_private_key(cred, params->private_key,
+				  params->private_key_passwd,
+				  params->private_key_blob,
+				  params->private_key_blob_len)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob,
+			       params->dh_blob_len)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (tlsv1_client_set_cred(conn->client, cred) < 0) {
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	tlsv1_client_set_time_checks(
+		conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS));
+
+	return 0;
+#else /* CONFIG_TLS_INTERNAL_CLIENT */
+	return -1;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+			  const struct tls_connection_params *params)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	struct tls_global *global = tls_ctx;
+	struct tlsv1_credentials *cred;
+
+	/* Currently, global parameters are only set when running in server
+	 * mode. */
+	global->server = 1;
+	tlsv1_cred_free(global->server_cred);
+	global->server_cred = cred = tlsv1_cred_alloc();
+	if (cred == NULL)
+		return -1;
+
+	if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob,
+			      params->ca_cert_blob_len, params->ca_path)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
+			   "certificates");
+		return -1;
+	}
+
+	if (tlsv1_set_cert(cred, params->client_cert, params->client_cert_blob,
+			   params->client_cert_blob_len)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to configure server "
+			   "certificate");
+		return -1;
+	}
+
+	if (tlsv1_set_private_key(cred, params->private_key,
+				  params->private_key_passwd,
+				  params->private_key_blob,
+				  params->private_key_blob_len)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+		return -1;
+	}
+
+	if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob,
+			       params->dh_blob_len)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
+		return -1;
+	}
+
+	return 0;
+#else /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl)
+{
+	struct tls_global *global = tls_ctx;
+	global->check_crl = check_crl;
+	return 0;
+}
+
+
+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_set_verify(conn->server, verify_peer);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
+			      struct tls_random *data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_get_random(conn->client, data);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_get_random(conn->server, data);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+static int tls_get_keyblock_size(struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_get_keyblock_size(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_get_keyblock_size(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+		       const char *label, int server_random_first,
+		       int skip_keyblock, u8 *out, size_t out_len)
+{
+	int ret = -1, skip = 0;
+	u8 *tmp_out = NULL;
+	u8 *_out = out;
+
+	if (skip_keyblock) {
+		skip = tls_get_keyblock_size(conn);
+		if (skip < 0)
+			return -1;
+		tmp_out = os_malloc(skip + out_len);
+		if (!tmp_out)
+			return -1;
+		_out = tmp_out;
+	}
+
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client) {
+		ret = tlsv1_client_prf(conn->client, label,
+				       server_random_first,
+				       _out, out_len);
+	}
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server) {
+		ret = tlsv1_server_prf(conn->server, label,
+				       server_random_first,
+				       _out, out_len);
+	}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	if (ret == 0 && skip_keyblock)
+		os_memcpy(out, _out + skip, out_len);
+	bin_clear_free(tmp_out, skip);
+
+	return ret;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+					 struct tls_connection *conn,
+					 const struct wpabuf *in_data,
+					 struct wpabuf **appl_data)
+{
+	return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data,
+					 NULL);
+}
+
+
+struct wpabuf * tls_connection_handshake2(void *tls_ctx,
+					  struct tls_connection *conn,
+					  const struct wpabuf *in_data,
+					  struct wpabuf **appl_data,
+					  int *need_more_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	u8 *res, *ad;
+	size_t res_len, ad_len;
+	struct wpabuf *out;
+
+	if (conn->client == NULL)
+		return NULL;
+
+	ad = NULL;
+	res = tlsv1_client_handshake(conn->client,
+				     in_data ? wpabuf_head(in_data) : NULL,
+				     in_data ? wpabuf_len(in_data) : 0,
+				     &res_len, &ad, &ad_len, need_more_data);
+	if (res == NULL)
+		return NULL;
+	out = wpabuf_alloc_ext_data(res, res_len);
+	if (out == NULL) {
+		os_free(res);
+		os_free(ad);
+		return NULL;
+	}
+	if (appl_data) {
+		if (ad) {
+			*appl_data = wpabuf_alloc_ext_data(ad, ad_len);
+			if (*appl_data == NULL)
+				os_free(ad);
+		} else
+			*appl_data = NULL;
+	} else
+		os_free(ad);
+
+	return out;
+#else /* CONFIG_TLS_INTERNAL_CLIENT */
+	return NULL;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+						struct tls_connection *conn,
+						const struct wpabuf *in_data,
+						struct wpabuf **appl_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	u8 *res;
+	size_t res_len;
+	struct wpabuf *out;
+
+	if (conn->server == NULL)
+		return NULL;
+
+	if (appl_data)
+		*appl_data = NULL;
+
+	res = tlsv1_server_handshake(conn->server, wpabuf_head(in_data),
+				     wpabuf_len(in_data), &res_len);
+	if (res == NULL && tlsv1_server_established(conn->server))
+		return wpabuf_alloc(0);
+	if (res == NULL)
+		return NULL;
+	out = wpabuf_alloc_ext_data(res, res_len);
+	if (out == NULL) {
+		os_free(res);
+		return NULL;
+	}
+
+	return out;
+#else /* CONFIG_TLS_INTERNAL_SERVER */
+	return NULL;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client) {
+		struct wpabuf *buf;
+		int res;
+		buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+		if (buf == NULL)
+			return NULL;
+		res = tlsv1_client_encrypt(conn->client, wpabuf_head(in_data),
+					   wpabuf_len(in_data),
+					   wpabuf_mhead(buf),
+					   wpabuf_size(buf));
+		if (res < 0) {
+			wpabuf_free(buf);
+			return NULL;
+		}
+		wpabuf_put(buf, res);
+		return buf;
+	}
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server) {
+		struct wpabuf *buf;
+		int res;
+		buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+		if (buf == NULL)
+			return NULL;
+		res = tlsv1_server_encrypt(conn->server, wpabuf_head(in_data),
+					   wpabuf_len(in_data),
+					   wpabuf_mhead(buf),
+					   wpabuf_size(buf));
+		if (res < 0) {
+			wpabuf_free(buf);
+			return NULL;
+		}
+		wpabuf_put(buf, res);
+		return buf;
+	}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return NULL;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL);
+}
+
+
+struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
+					struct tls_connection *conn,
+					const struct wpabuf *in_data,
+					int *need_more_data)
+{
+	if (need_more_data)
+		*need_more_data = 0;
+
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client) {
+		return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
+					    wpabuf_len(in_data),
+					    need_more_data);
+	}
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server) {
+		struct wpabuf *buf;
+		int res;
+		buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+		if (buf == NULL)
+			return NULL;
+		res = tlsv1_server_decrypt(conn->server, wpabuf_head(in_data),
+					   wpabuf_len(in_data),
+					   wpabuf_mhead(buf),
+					   wpabuf_size(buf));
+		if (res < 0) {
+			wpabuf_free(buf);
+			return NULL;
+		}
+		wpabuf_put(buf, res);
+		return buf;
+	}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return NULL;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_resumed(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_resumed(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+				   u8 *ciphers)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_set_cipher_list(conn->client, ciphers);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_set_cipher_list(conn->server, ciphers);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	/* TODO */
+	return -1;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+		   char *buf, size_t buflen)
+{
+	if (conn == NULL)
+		return -1;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_get_cipher(conn->client, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_get_cipher(conn->server, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+				     struct tls_connection *conn)
+{
+	return -1;
+}
+
+
+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
+				    int ext_type, const u8 *data,
+				    size_t data_len)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client) {
+		return tlsv1_client_hello_ext(conn->client, ext_type,
+					      data, data_len);
+	}
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+	return -1;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+	return 0;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+	return 0;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+				    struct tls_connection *conn)
+{
+	return 0;
+}
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+					 struct tls_connection *conn,
+					 tls_session_ticket_cb cb,
+					 void *ctx)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client) {
+		tlsv1_client_set_session_ticket_cb(conn->client, cb, ctx);
+		return 0;
+	}
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server) {
+		tlsv1_server_set_session_ticket_cb(conn->server, cb, ctx);
+		return 0;
+	}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "internal");
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	return NULL;
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+}
diff --git a/hostap/src/crypto/tls_none.c b/hostap/src/crypto/tls_none.c
new file mode 100644
index 0000000..ae392ad
--- /dev/null
+++ b/hostap/src/crypto/tls_none.c
@@ -0,0 +1,218 @@
+/*
+ * SSL/TLS interface functions for no TLS case
+ * 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 "common.h"
+#include "tls.h"
+
+void * tls_init(const struct tls_config *conf)
+{
+	return (void *) 1;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+	return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+	return NULL;
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+	return -1;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+	return -1;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+			      const struct tls_connection_params *params)
+{
+	return -1;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+			  const struct tls_connection_params *params)
+{
+	return -1;
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl)
+{
+	return -1;
+}
+
+
+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
+{
+	return -1;
+}
+
+
+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
+			      struct tls_random *data)
+{
+	return -1;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+		       const char *label, int server_random_first,
+		       int skip_keyblock, u8 *out, size_t out_len)
+{
+	return -1;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+					 struct tls_connection *conn,
+					 const struct wpabuf *in_data,
+					 struct wpabuf **appl_data)
+{
+	return NULL;
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+						struct tls_connection *conn,
+						const struct wpabuf *in_data,
+						struct wpabuf **appl_data)
+{
+	return NULL;
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	return NULL;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	return NULL;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+	return 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+				   u8 *ciphers)
+{
+	return -1;
+}
+
+
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	return -1;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+		   char *buf, size_t buflen)
+{
+	return -1;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+				     struct tls_connection *conn)
+{
+	return -1;
+}
+
+
+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
+				    int ext_type, const u8 *data,
+				    size_t data_len)
+{
+	return -1;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+	return 0;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+	return 0;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+				    struct tls_connection *conn)
+{
+	return 0;
+}
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "none");
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	return NULL;
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+}
diff --git a/hostap/src/crypto/tls_openssl.c b/hostap/src/crypto/tls_openssl.c
new file mode 100644
index 0000000..8b7b47b
--- /dev/null
+++ b/hostap/src/crypto/tls_openssl.c
@@ -0,0 +1,4145 @@
+/*
+ * SSL/TLS interface functions for OpenSSL
+ * Copyright (c) 2004-2015, 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"
+
+#ifndef CONFIG_SMARTCARD
+#ifndef OPENSSL_NO_ENGINE
+#ifndef ANDROID
+#define OPENSSL_NO_ENGINE
+#endif
+#endif
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509v3.h>
+#ifndef OPENSSL_NO_ENGINE
+#include <openssl/engine.h>
+#endif /* OPENSSL_NO_ENGINE */
+#ifndef OPENSSL_NO_DSA
+#include <openssl/dsa.h>
+#endif
+#ifndef OPENSSL_NO_DH
+#include <openssl/dh.h>
+#endif
+
+#include "common.h"
+#include "crypto.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "tls.h"
+
+#if OPENSSL_VERSION_NUMBER < 0x10000000L
+/* ERR_remove_thread_state replaces ERR_remove_state and the latter is
+ * deprecated. However, OpenSSL 0.9.8 doesn't include
+ * ERR_remove_thread_state. */
+#define ERR_remove_thread_state(tid) ERR_remove_state(0)
+#endif
+
+#if defined(OPENSSL_IS_BORINGSSL)
+/* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */
+typedef size_t stack_index_t;
+#else
+typedef int stack_index_t;
+#endif
+
+#ifdef SSL_set_tlsext_status_type
+#ifndef OPENSSL_NO_TLSEXT
+#define HAVE_OCSP
+#include <openssl/ocsp.h>
+#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;
+static int tls_ex_idx_session = -1;
+
+struct tls_context {
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+	int cert_in_cb;
+	char *ocsp_stapling_response;
+};
+
+static struct tls_context *tls_global = NULL;
+
+
+struct tls_data {
+	SSL_CTX *ssl;
+	unsigned int tls_session_lifetime;
+};
+
+struct tls_connection {
+	struct tls_context *context;
+	SSL_CTX *ssl_ctx;
+	SSL *ssl;
+	BIO *ssl_in, *ssl_out;
+#ifndef OPENSSL_NO_ENGINE
+	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, *suffix_match, *domain_match;
+	int read_alerts, write_alerts, failed;
+
+	tls_session_ticket_cb session_ticket_cb;
+	void *session_ticket_cb_ctx;
+
+	/* SessionTicket received from OpenSSL hello_extension_cb (server) */
+	u8 *session_ticket;
+	size_t session_ticket_len;
+
+	unsigned int ca_cert_verify:1;
+	unsigned int cert_probe:1;
+	unsigned int server_cert_only:1;
+	unsigned int invalid_hb_used:1;
+	unsigned int success_data:1;
+
+	u8 srv_cert_hash[32];
+
+	unsigned int flags;
+
+	X509 *peer_cert;
+	X509 *peer_issuer;
+	X509 *peer_issuer_issuer;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	unsigned char client_random[SSL3_RANDOM_SIZE];
+	unsigned char server_random[SSL3_RANDOM_SIZE];
+#endif
+};
+
+
+static struct tls_context * tls_context_new(const struct tls_config *conf)
+{
+	struct tls_context *context = os_zalloc(sizeof(*context));
+	if (context == NULL)
+		return NULL;
+	if (conf) {
+		context->event_cb = conf->event_cb;
+		context->cb_ctx = conf->cb_ctx;
+		context->cert_in_cb = conf->cert_in_cb;
+	}
+	return context;
+}
+
+
+#ifdef CONFIG_NO_STDOUT_DEBUG
+
+static void _tls_show_errors(void)
+{
+	unsigned long err;
+
+	while ((err = ERR_get_error())) {
+		/* Just ignore the errors, since stdout is disabled */
+	}
+}
+#define tls_show_errors(l, f, t) _tls_show_errors()
+
+#else /* CONFIG_NO_STDOUT_DEBUG */
+
+static void tls_show_errors(int level, const char *func, const char *txt)
+{
+	unsigned long err;
+
+	wpa_printf(level, "OpenSSL: %s - %s %s",
+		   func, txt, ERR_error_string(ERR_get_error(), NULL));
+
+	while ((err = ERR_get_error())) {
+		wpa_printf(MSG_INFO, "OpenSSL: pending error: %s",
+			   ERR_error_string(err, NULL));
+	}
+}
+
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+
+/* Windows CryptoAPI and access to certificate stores */
+#include <wincrypt.h>
+
+#ifdef __MINGW32_VERSION
+/*
+ * MinGW does not yet include all the needed definitions for CryptoAPI, so
+ * define here whatever extra is needed.
+ */
+#define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16)
+#define CERT_STORE_READONLY_FLAG 0x00008000
+#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000
+
+#endif /* __MINGW32_VERSION */
+
+
+struct cryptoapi_rsa_data {
+	const CERT_CONTEXT *cert;
+	HCRYPTPROV crypt_prov;
+	DWORD key_spec;
+	BOOL free_crypt_prov;
+};
+
+
+static void cryptoapi_error(const char *msg)
+{
+	wpa_printf(MSG_INFO, "CryptoAPI: %s; err=%u",
+		   msg, (unsigned int) GetLastError());
+}
+
+
+static int cryptoapi_rsa_pub_enc(int flen, const unsigned char *from,
+				 unsigned char *to, RSA *rsa, int padding)
+{
+	wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+	return 0;
+}
+
+
+static int cryptoapi_rsa_pub_dec(int flen, const unsigned char *from,
+				 unsigned char *to, RSA *rsa, int padding)
+{
+	wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+	return 0;
+}
+
+
+static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from,
+				  unsigned char *to, RSA *rsa, int padding)
+{
+	struct cryptoapi_rsa_data *priv =
+		(struct cryptoapi_rsa_data *) rsa->meth->app_data;
+	HCRYPTHASH hash;
+	DWORD hash_size, len, i;
+	unsigned char *buf = NULL;
+	int ret = 0;
+
+	if (priv == NULL) {
+		RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+		       ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (padding != RSA_PKCS1_PADDING) {
+		RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+		       RSA_R_UNKNOWN_PADDING_TYPE);
+		return 0;
+	}
+
+	if (flen != 16 /* MD5 */ + 20 /* SHA-1 */) {
+		wpa_printf(MSG_INFO, "%s - only MD5-SHA1 hash supported",
+			   __func__);
+		RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+		       RSA_R_INVALID_MESSAGE_LENGTH);
+		return 0;
+	}
+
+	if (!CryptCreateHash(priv->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash))
+	{
+		cryptoapi_error("CryptCreateHash failed");
+		return 0;
+	}
+
+	len = sizeof(hash_size);
+	if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len,
+			       0)) {
+		cryptoapi_error("CryptGetHashParam failed");
+		goto err;
+	}
+
+	if ((int) hash_size != flen) {
+		wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)",
+			   (unsigned) hash_size, flen);
+		RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+		       RSA_R_INVALID_MESSAGE_LENGTH);
+		goto err;
+	}
+	if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) {
+		cryptoapi_error("CryptSetHashParam failed");
+		goto err;
+	}
+
+	len = RSA_size(rsa);
+	buf = os_malloc(len);
+	if (buf == NULL) {
+		RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE);
+		goto err;
+	}
+
+	if (!CryptSignHash(hash, priv->key_spec, NULL, 0, buf, &len)) {
+		cryptoapi_error("CryptSignHash failed");
+		goto err;
+	}
+
+	for (i = 0; i < len; i++)
+		to[i] = buf[len - i - 1];
+	ret = len;
+
+err:
+	os_free(buf);
+	CryptDestroyHash(hash);
+
+	return ret;
+}
+
+
+static int cryptoapi_rsa_priv_dec(int flen, const unsigned char *from,
+				  unsigned char *to, RSA *rsa, int padding)
+{
+	wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+	return 0;
+}
+
+
+static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv)
+{
+	if (priv == NULL)
+		return;
+	if (priv->crypt_prov && priv->free_crypt_prov)
+		CryptReleaseContext(priv->crypt_prov, 0);
+	if (priv->cert)
+		CertFreeCertificateContext(priv->cert);
+	os_free(priv);
+}
+
+
+static int cryptoapi_finish(RSA *rsa)
+{
+	cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data);
+	os_free((void *) rsa->meth);
+	rsa->meth = NULL;
+	return 1;
+}
+
+
+static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store)
+{
+	HCERTSTORE cs;
+	const CERT_CONTEXT *ret = NULL;
+
+	cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0,
+			   store | CERT_STORE_OPEN_EXISTING_FLAG |
+			   CERT_STORE_READONLY_FLAG, L"MY");
+	if (cs == NULL) {
+		cryptoapi_error("Failed to open 'My system store'");
+		return NULL;
+	}
+
+	if (strncmp(name, "cert://", 7) == 0) {
+		unsigned short wbuf[255];
+		MultiByteToWideChar(CP_ACP, 0, name + 7, -1, wbuf, 255);
+		ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING |
+						 PKCS_7_ASN_ENCODING,
+						 0, CERT_FIND_SUBJECT_STR,
+						 wbuf, NULL);
+	} else if (strncmp(name, "hash://", 7) == 0) {
+		CRYPT_HASH_BLOB blob;
+		int len;
+		const char *hash = name + 7;
+		unsigned char *buf;
+
+		len = os_strlen(hash) / 2;
+		buf = os_malloc(len);
+		if (buf && hexstr2bin(hash, buf, len) == 0) {
+			blob.cbData = len;
+			blob.pbData = buf;
+			ret = CertFindCertificateInStore(cs,
+							 X509_ASN_ENCODING |
+							 PKCS_7_ASN_ENCODING,
+							 0, CERT_FIND_HASH,
+							 &blob, NULL);
+		}
+		os_free(buf);
+	}
+
+	CertCloseStore(cs, 0);
+
+	return ret;
+}
+
+
+static int tls_cryptoapi_cert(SSL *ssl, const char *name)
+{
+	X509 *cert = NULL;
+	RSA *rsa = NULL, *pub_rsa;
+	struct cryptoapi_rsa_data *priv;
+	RSA_METHOD *rsa_meth;
+
+	if (name == NULL ||
+	    (strncmp(name, "cert://", 7) != 0 &&
+	     strncmp(name, "hash://", 7) != 0))
+		return -1;
+
+	priv = os_zalloc(sizeof(*priv));
+	rsa_meth = os_zalloc(sizeof(*rsa_meth));
+	if (priv == NULL || rsa_meth == NULL) {
+		wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory "
+			   "for CryptoAPI RSA method");
+		os_free(priv);
+		os_free(rsa_meth);
+		return -1;
+	}
+
+	priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER);
+	if (priv->cert == NULL) {
+		priv->cert = cryptoapi_find_cert(
+			name, CERT_SYSTEM_STORE_LOCAL_MACHINE);
+	}
+	if (priv->cert == NULL) {
+		wpa_printf(MSG_INFO, "CryptoAPI: Could not find certificate "
+			   "'%s'", name);
+		goto err;
+	}
+
+	cert = d2i_X509(NULL,
+			(const unsigned char **) &priv->cert->pbCertEncoded,
+			priv->cert->cbCertEncoded);
+	if (cert == NULL) {
+		wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER "
+			   "encoding");
+		goto err;
+	}
+
+	if (!CryptAcquireCertificatePrivateKey(priv->cert,
+					       CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
+					       NULL, &priv->crypt_prov,
+					       &priv->key_spec,
+					       &priv->free_crypt_prov)) {
+		cryptoapi_error("Failed to acquire a private key for the "
+				"certificate");
+		goto err;
+	}
+
+	rsa_meth->name = "Microsoft CryptoAPI RSA Method";
+	rsa_meth->rsa_pub_enc = cryptoapi_rsa_pub_enc;
+	rsa_meth->rsa_pub_dec = cryptoapi_rsa_pub_dec;
+	rsa_meth->rsa_priv_enc = cryptoapi_rsa_priv_enc;
+	rsa_meth->rsa_priv_dec = cryptoapi_rsa_priv_dec;
+	rsa_meth->finish = cryptoapi_finish;
+	rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK;
+	rsa_meth->app_data = (char *) priv;
+
+	rsa = RSA_new();
+	if (rsa == NULL) {
+		SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,
+		       ERR_R_MALLOC_FAILURE);
+		goto err;
+	}
+
+	if (!SSL_use_certificate(ssl, cert)) {
+		RSA_free(rsa);
+		rsa = NULL;
+		goto err;
+	}
+	pub_rsa = cert->cert_info->key->pkey->pkey.rsa;
+	X509_free(cert);
+	cert = NULL;
+
+	rsa->n = BN_dup(pub_rsa->n);
+	rsa->e = BN_dup(pub_rsa->e);
+	if (!RSA_set_method(rsa, rsa_meth))
+		goto err;
+
+	if (!SSL_use_RSAPrivateKey(ssl, rsa))
+		goto err;
+	RSA_free(rsa);
+
+	return 0;
+
+err:
+	if (cert)
+		X509_free(cert);
+	if (rsa)
+		RSA_free(rsa);
+	else {
+		os_free(rsa_meth);
+		cryptoapi_free_data(priv);
+	}
+	return -1;
+}
+
+
+static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name)
+{
+	HCERTSTORE cs;
+	PCCERT_CONTEXT ctx = NULL;
+	X509 *cert;
+	char buf[128];
+	const char *store;
+#ifdef UNICODE
+	WCHAR *wstore;
+#endif /* UNICODE */
+
+	if (name == NULL || strncmp(name, "cert_store://", 13) != 0)
+		return -1;
+
+	store = name + 13;
+#ifdef UNICODE
+	wstore = os_malloc((os_strlen(store) + 1) * sizeof(WCHAR));
+	if (wstore == NULL)
+		return -1;
+	wsprintf(wstore, L"%S", store);
+	cs = CertOpenSystemStore(0, wstore);
+	os_free(wstore);
+#else /* UNICODE */
+	cs = CertOpenSystemStore(0, store);
+#endif /* UNICODE */
+	if (cs == NULL) {
+		wpa_printf(MSG_DEBUG, "%s: failed to open system cert store "
+			   "'%s': error=%d", __func__, store,
+			   (int) GetLastError());
+		return -1;
+	}
+
+	while ((ctx = CertEnumCertificatesInStore(cs, ctx))) {
+		cert = d2i_X509(NULL,
+				(const unsigned char **) &ctx->pbCertEncoded,
+				ctx->cbCertEncoded);
+		if (cert == NULL) {
+			wpa_printf(MSG_INFO, "CryptoAPI: Could not process "
+				   "X509 DER encoding for CA cert");
+			continue;
+		}
+
+		X509_NAME_oneline(X509_get_subject_name(cert), buf,
+				  sizeof(buf));
+		wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for "
+			   "system certificate store: subject='%s'", buf);
+
+		if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+			tls_show_errors(MSG_WARNING, __func__,
+					"Failed to add ca_cert to OpenSSL "
+					"certificate store");
+		}
+
+		X509_free(cert);
+	}
+
+	if (!CertCloseStore(cs, 0)) {
+		wpa_printf(MSG_DEBUG, "%s: failed to close system cert store "
+			   "'%s': error=%d", __func__, name + 13,
+			   (int) GetLastError());
+	}
+
+	return 0;
+}
+
+
+#else /* CONFIG_NATIVE_WINDOWS */
+
+static int tls_cryptoapi_cert(SSL *ssl, const char *name)
+{
+	return -1;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static void ssl_info_cb(const SSL *ssl, int where, int ret)
+{
+	const char *str;
+	int w;
+
+	wpa_printf(MSG_DEBUG, "SSL: (where=0x%x ret=0x%x)", where, ret);
+	w = where & ~SSL_ST_MASK;
+	if (w & SSL_ST_CONNECT)
+		str = "SSL_connect";
+	else if (w & SSL_ST_ACCEPT)
+		str = "SSL_accept";
+	else
+		str = "undefined";
+
+	if (where & SSL_CB_LOOP) {
+		wpa_printf(MSG_DEBUG, "SSL: %s:%s",
+			   str, SSL_state_string_long(ssl));
+	} else if (where & SSL_CB_ALERT) {
+		struct tls_connection *conn = SSL_get_app_data((SSL *) ssl);
+		wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s",
+			   where & SSL_CB_READ ?
+			   "read (remote end reported an error)" :
+			   "write (local SSL3 detected an error)",
+			   SSL_alert_type_string_long(ret),
+			   SSL_alert_desc_string_long(ret));
+		if ((ret >> 8) == SSL3_AL_FATAL) {
+			if (where & SSL_CB_READ)
+				conn->read_alerts++;
+			else
+				conn->write_alerts++;
+		}
+		if (conn->context->event_cb != NULL) {
+			union tls_event_data ev;
+			struct tls_context *context = conn->context;
+			os_memset(&ev, 0, sizeof(ev));
+			ev.alert.is_local = !(where & SSL_CB_READ);
+			ev.alert.type = SSL_alert_type_string_long(ret);
+			ev.alert.description = SSL_alert_desc_string_long(ret);
+			context->event_cb(context->cb_ctx, TLS_ALERT, &ev);
+		}
+	} else if (where & SSL_CB_EXIT && ret <= 0) {
+		wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s",
+			   str, ret == 0 ? "failed" : "error",
+			   SSL_state_string_long(ssl));
+	}
+}
+
+
+#ifndef OPENSSL_NO_ENGINE
+/**
+ * tls_engine_load_dynamic_generic - load any openssl engine
+ * @pre: an array of commands and values that load an engine initialized
+ *       in the engine specific function
+ * @post: an array of commands and values that initialize an already loaded
+ *        engine (or %NULL if not required)
+ * @id: the engine id of the engine to load (only required if post is not %NULL
+ *
+ * This function is a generic function that loads any openssl engine.
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+static int tls_engine_load_dynamic_generic(const char *pre[],
+					   const char *post[], const char *id)
+{
+	ENGINE *engine;
+	const char *dynamic_id = "dynamic";
+
+	engine = ENGINE_by_id(id);
+	if (engine) {
+		ENGINE_free(engine);
+		wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already "
+			   "available", id);
+		return 0;
+	}
+	ERR_clear_error();
+
+	engine = ENGINE_by_id(dynamic_id);
+	if (engine == NULL) {
+		wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]",
+			   dynamic_id,
+			   ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+
+	/* Perform the pre commands. This will load the engine. */
+	while (pre && pre[0]) {
+		wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", pre[0], pre[1]);
+		if (ENGINE_ctrl_cmd_string(engine, pre[0], pre[1], 0) == 0) {
+			wpa_printf(MSG_INFO, "ENGINE: ctrl cmd_string failed: "
+				   "%s %s [%s]", pre[0], pre[1],
+				   ERR_error_string(ERR_get_error(), NULL));
+			ENGINE_free(engine);
+			return -1;
+		}
+		pre += 2;
+	}
+
+	/*
+	 * Free the reference to the "dynamic" engine. The loaded engine can
+	 * now be looked up using ENGINE_by_id().
+	 */
+	ENGINE_free(engine);
+
+	engine = ENGINE_by_id(id);
+	if (engine == NULL) {
+		wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]",
+			   id, ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+
+	while (post && post[0]) {
+		wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]);
+		if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) {
+			wpa_printf(MSG_DEBUG, "ENGINE: ctrl cmd_string failed:"
+				" %s %s [%s]", post[0], post[1],
+				   ERR_error_string(ERR_get_error(), NULL));
+			ENGINE_remove(engine);
+			ENGINE_free(engine);
+			return -1;
+		}
+		post += 2;
+	}
+	ENGINE_free(engine);
+
+	return 0;
+}
+
+
+/**
+ * tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc
+ * @pkcs11_so_path: pksc11_so_path from the configuration
+ * @pcks11_module_path: pkcs11_module_path from the configuration
+ */
+static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path,
+					  const char *pkcs11_module_path)
+{
+	char *engine_id = "pkcs11";
+	const char *pre_cmd[] = {
+		"SO_PATH", NULL /* pkcs11_so_path */,
+		"ID", NULL /* engine_id */,
+		"LIST_ADD", "1",
+		/* "NO_VCHECK", "1", */
+		"LOAD", NULL,
+		NULL, NULL
+	};
+	const char *post_cmd[] = {
+		"MODULE_PATH", NULL /* pkcs11_module_path */,
+		NULL, NULL
+	};
+
+	if (!pkcs11_so_path)
+		return 0;
+
+	pre_cmd[1] = pkcs11_so_path;
+	pre_cmd[3] = engine_id;
+	if (pkcs11_module_path)
+		post_cmd[1] = pkcs11_module_path;
+	else
+		post_cmd[0] = NULL;
+
+	wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s",
+		   pkcs11_so_path);
+
+	return tls_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id);
+}
+
+
+/**
+ * tls_engine_load_dynamic_opensc - load the opensc engine provided by opensc
+ * @opensc_so_path: opensc_so_path from the configuration
+ */
+static int tls_engine_load_dynamic_opensc(const char *opensc_so_path)
+{
+	char *engine_id = "opensc";
+	const char *pre_cmd[] = {
+		"SO_PATH", NULL /* opensc_so_path */,
+		"ID", NULL /* engine_id */,
+		"LIST_ADD", "1",
+		"LOAD", NULL,
+		NULL, NULL
+	};
+
+	if (!opensc_so_path)
+		return 0;
+
+	pre_cmd[1] = opensc_so_path;
+	pre_cmd[3] = engine_id;
+
+	wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s",
+		   opensc_so_path);
+
+	return tls_engine_load_dynamic_generic(pre_cmd, NULL, engine_id);
+}
+#endif /* OPENSSL_NO_ENGINE */
+
+
+static void remove_session_cb(SSL_CTX *ctx, SSL_SESSION *sess)
+{
+	struct wpabuf *buf;
+
+	if (tls_ex_idx_session < 0)
+		return;
+	buf = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+	if (!buf)
+		return;
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Free application session data %p (sess %p)",
+		   buf, sess);
+	wpabuf_free(buf);
+
+	SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
+}
+
+
+void * tls_init(const struct tls_config *conf)
+{
+	struct tls_data *data;
+	SSL_CTX *ssl;
+	struct tls_context *context;
+	const char *ciphers;
+
+	if (tls_openssl_ref_count == 0) {
+		tls_global = context = tls_context_new(conf);
+		if (context == NULL)
+			return NULL;
+#ifdef CONFIG_FIPS
+#ifdef OPENSSL_FIPS
+		if (conf && conf->fips_mode) {
+			static int fips_enabled = 0;
+
+			if (!fips_enabled && !FIPS_mode_set(1)) {
+				wpa_printf(MSG_ERROR, "Failed to enable FIPS "
+					   "mode");
+				ERR_load_crypto_strings();
+				ERR_print_errors_fp(stderr);
+				os_free(tls_global);
+				tls_global = NULL;
+				return NULL;
+			} else {
+				wpa_printf(MSG_INFO, "Running in FIPS mode");
+				fips_enabled = 1;
+			}
+		}
+#else /* OPENSSL_FIPS */
+		if (conf && conf->fips_mode) {
+			wpa_printf(MSG_ERROR, "FIPS mode requested, but not "
+				   "supported");
+			os_free(tls_global);
+			tls_global = NULL;
+			return NULL;
+		}
+#endif /* OPENSSL_FIPS */
+#endif /* CONFIG_FIPS */
+		SSL_load_error_strings();
+		SSL_library_init();
+#ifndef OPENSSL_NO_SHA256
+		EVP_add_digest(EVP_sha256());
+#endif /* OPENSSL_NO_SHA256 */
+		/* TODO: if /dev/urandom is available, PRNG is seeded
+		 * automatically. If this is not the case, random data should
+		 * be added here. */
+
+#ifdef PKCS12_FUNCS
+#ifndef OPENSSL_NO_RC2
+		/*
+		 * 40-bit RC2 is commonly used in PKCS#12 files, so enable it.
+		 * This is enabled by PKCS12_PBE_add() in OpenSSL 0.9.8
+		 * versions, but it looks like OpenSSL 1.0.0 does not do that
+		 * anymore.
+		 */
+		EVP_add_cipher(EVP_rc2_40_cbc());
+#endif /* OPENSSL_NO_RC2 */
+		PKCS12_PBE_add();
+#endif  /* PKCS12_FUNCS */
+	} else {
+		context = tls_context_new(conf);
+		if (context == NULL)
+			return NULL;
+	}
+	tls_openssl_ref_count++;
+
+	data = os_zalloc(sizeof(*data));
+	if (data)
+		ssl = SSL_CTX_new(SSLv23_method());
+	else
+		ssl = NULL;
+	if (ssl == NULL) {
+		tls_openssl_ref_count--;
+		if (context != tls_global)
+			os_free(context);
+		if (tls_openssl_ref_count == 0) {
+			os_free(tls_global);
+			tls_global = NULL;
+		}
+		return NULL;
+	}
+	data->ssl = ssl;
+	if (conf)
+		data->tls_session_lifetime = conf->tls_session_lifetime;
+
+	SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2);
+	SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3);
+
+	SSL_CTX_set_info_callback(ssl, ssl_info_cb);
+	SSL_CTX_set_app_data(ssl, context);
+	if (data->tls_session_lifetime > 0) {
+		SSL_CTX_set_quiet_shutdown(ssl, 1);
+		/*
+		 * Set default context here. In practice, this will be replaced
+		 * by the per-EAP method context in tls_connection_set_verify().
+		 */
+		SSL_CTX_set_session_id_context(ssl, (u8 *) "hostapd", 7);
+		SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_SERVER);
+		SSL_CTX_set_timeout(ssl, data->tls_session_lifetime);
+		SSL_CTX_sess_set_remove_cb(ssl, remove_session_cb);
+	} else {
+		SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_OFF);
+	}
+
+	if (tls_ex_idx_session < 0) {
+		tls_ex_idx_session = SSL_SESSION_get_ex_new_index(
+			0, NULL, NULL, NULL, NULL);
+		if (tls_ex_idx_session < 0) {
+			tls_deinit(data);
+			return NULL;
+		}
+	}
+
+#ifndef OPENSSL_NO_ENGINE
+	wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
+	ERR_load_ENGINE_strings();
+	ENGINE_load_dynamic();
+
+	if (conf &&
+	    (conf->opensc_engine_path || conf->pkcs11_engine_path ||
+	     conf->pkcs11_module_path)) {
+		if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) ||
+		    tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path,
+						   conf->pkcs11_module_path)) {
+			tls_deinit(data);
+			return NULL;
+		}
+	}
+#endif /* OPENSSL_NO_ENGINE */
+
+	if (conf && conf->openssl_ciphers)
+		ciphers = conf->openssl_ciphers;
+	else
+		ciphers = "DEFAULT:!EXP:!LOW";
+	if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to set cipher string '%s'",
+			   ciphers);
+		tls_deinit(data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+	struct tls_data *data = ssl_ctx;
+	SSL_CTX *ssl = data->ssl;
+	struct tls_context *context = SSL_CTX_get_app_data(ssl);
+	if (context != tls_global)
+		os_free(context);
+	if (data->tls_session_lifetime > 0)
+		SSL_CTX_flush_sessions(ssl, 0);
+	SSL_CTX_free(ssl);
+
+	tls_openssl_ref_count--;
+	if (tls_openssl_ref_count == 0) {
+#ifndef OPENSSL_NO_ENGINE
+		ENGINE_cleanup();
+#endif /* OPENSSL_NO_ENGINE */
+		CRYPTO_cleanup_all_ex_data();
+		ERR_remove_thread_state(NULL);
+		ERR_free_strings();
+		EVP_cleanup();
+		os_free(tls_global->ocsp_stapling_response);
+		tls_global->ocsp_stapling_response = NULL;
+		os_free(tls_global);
+		tls_global = NULL;
+	}
+
+	os_free(data);
+}
+
+
+#ifndef OPENSSL_NO_ENGINE
+
+/* Cryptoki return values */
+#define CKR_PIN_INCORRECT 0x000000a0
+#define CKR_PIN_INVALID 0x000000a1
+#define CKR_PIN_LEN_RANGE 0x000000a2
+
+/* libp11 */
+#define ERR_LIB_PKCS11	ERR_LIB_USER
+
+static int tls_is_pin_error(unsigned int err)
+{
+	return ERR_GET_LIB(err) == ERR_LIB_PKCS11 &&
+		(ERR_GET_REASON(err) == CKR_PIN_INCORRECT ||
+		 ERR_GET_REASON(err) == CKR_PIN_INVALID ||
+		 ERR_GET_REASON(err) == CKR_PIN_LEN_RANGE);
+}
+
+#endif /* OPENSSL_NO_ENGINE */
+
+
+static int tls_engine_init(struct tls_connection *conn, const char *engine_id,
+			   const char *pin, const char *key_id,
+			   const char *cert_id, const char *ca_cert_id)
+{
+#ifndef OPENSSL_NO_ENGINE
+	int ret = -1;
+	if (engine_id == NULL) {
+		wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set");
+		return -1;
+	}
+
+	ERR_clear_error();
+#ifdef ANDROID
+	ENGINE_load_dynamic();
+#endif
+	conn->engine = ENGINE_by_id(engine_id);
+	if (!conn->engine) {
+		wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]",
+			   engine_id, ERR_error_string(ERR_get_error(), NULL));
+		goto err;
+	}
+	if (ENGINE_init(conn->engine) != 1) {
+		wpa_printf(MSG_ERROR, "ENGINE: engine init failed "
+			   "(engine: %s) [%s]", engine_id,
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto err;
+	}
+	wpa_printf(MSG_DEBUG, "ENGINE: engine initialized");
+
+#ifndef ANDROID
+	if (pin && ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) {
+		wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto err;
+	}
+#endif
+	if (key_id) {
+		/*
+		 * Ensure that the ENGINE does not attempt to use the OpenSSL
+		 * UI system to obtain a PIN, if we didn't provide one.
+		 */
+		struct {
+			const void *password;
+			const char *prompt_info;
+		} key_cb = { "", NULL };
+
+		/* load private key first in-case PIN is required for cert */
+		conn->private_key = ENGINE_load_private_key(conn->engine,
+							    key_id, NULL,
+							    &key_cb);
+		if (!conn->private_key) {
+			unsigned long err = ERR_get_error();
+
+			wpa_printf(MSG_ERROR,
+				   "ENGINE: cannot load private key with id '%s' [%s]",
+				   key_id,
+				   ERR_error_string(err, NULL));
+			if (tls_is_pin_error(err))
+				ret = TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN;
+			else
+				ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+			goto err;
+		}
+	}
+
+	/* handle a certificate and/or CA certificate */
+	if (cert_id || ca_cert_id) {
+		const char *cmd_name = "LOAD_CERT_CTRL";
+
+		/* test if the engine supports a LOAD_CERT_CTRL */
+		if (!ENGINE_ctrl(conn->engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
+				 0, (void *)cmd_name, NULL)) {
+			wpa_printf(MSG_ERROR, "ENGINE: engine does not support"
+				   " loading certificates");
+			ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+			goto err;
+		}
+	}
+
+	return 0;
+
+err:
+	if (conn->engine) {
+		ENGINE_free(conn->engine);
+		conn->engine = NULL;
+	}
+
+	if (conn->private_key) {
+		EVP_PKEY_free(conn->private_key);
+		conn->private_key = NULL;
+	}
+
+	return ret;
+#else /* OPENSSL_NO_ENGINE */
+	return 0;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static void tls_engine_deinit(struct tls_connection *conn)
+{
+#ifndef OPENSSL_NO_ENGINE
+	wpa_printf(MSG_DEBUG, "ENGINE: engine deinit");
+	if (conn->private_key) {
+		EVP_PKEY_free(conn->private_key);
+		conn->private_key = NULL;
+	}
+	if (conn->engine) {
+		ENGINE_finish(conn->engine);
+		conn->engine = NULL;
+	}
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+int tls_get_errors(void *ssl_ctx)
+{
+	int count = 0;
+	unsigned long err;
+
+	while ((err = ERR_get_error())) {
+		wpa_printf(MSG_INFO, "TLS - SSL error: %s",
+			   ERR_error_string(err, NULL));
+		count++;
+	}
+
+	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)
+{
+	struct tls_data *data = ssl_ctx;
+	SSL_CTX *ssl = data->ssl;
+	struct tls_connection *conn;
+	long options;
+	struct tls_context *context = SSL_CTX_get_app_data(ssl);
+
+	conn = os_zalloc(sizeof(*conn));
+	if (conn == NULL)
+		return NULL;
+	conn->ssl_ctx = ssl;
+	conn->ssl = SSL_new(ssl);
+	if (conn->ssl == NULL) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to initialize new SSL connection");
+		os_free(conn);
+		return NULL;
+	}
+
+	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 */
+	SSL_set_options(conn->ssl, options);
+
+	conn->ssl_in = BIO_new(BIO_s_mem());
+	if (!conn->ssl_in) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to create a new BIO for ssl_in");
+		SSL_free(conn->ssl);
+		os_free(conn);
+		return NULL;
+	}
+
+	conn->ssl_out = BIO_new(BIO_s_mem());
+	if (!conn->ssl_out) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to create a new BIO for ssl_out");
+		SSL_free(conn->ssl);
+		BIO_free(conn->ssl_in);
+		os_free(conn);
+		return NULL;
+	}
+
+	SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out);
+
+	return conn;
+}
+
+
+void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return;
+	if (conn->success_data) {
+		/*
+		 * Make sure ssl_clear_bad_session() does not remove this
+		 * session.
+		 */
+		SSL_set_quiet_shutdown(conn->ssl, 1);
+		SSL_shutdown(conn->ssl);
+	}
+	SSL_free(conn->ssl);
+	tls_engine_deinit(conn);
+	os_free(conn->subject_match);
+	os_free(conn->altsubject_match);
+	os_free(conn->suffix_match);
+	os_free(conn->domain_match);
+	os_free(conn->session_ticket);
+	os_free(conn);
+}
+
+
+int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
+{
+	return conn ? SSL_is_init_finished(conn->ssl) : 0;
+}
+
+
+int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return -1;
+
+	/* Shutdown previous TLS connection without notifying the peer
+	 * because the connection was already terminated in practice
+	 * and "close notify" shutdown alert would confuse AS. */
+	SSL_set_quiet_shutdown(conn->ssl, 1);
+	SSL_shutdown(conn->ssl);
+	return SSL_clear(conn->ssl) == 1 ? 0 : -1;
+}
+
+
+static int tls_match_altsubject_component(X509 *cert, int type,
+					  const char *value, size_t len)
+{
+	GENERAL_NAME *gen;
+	void *ext;
+	int found = 0;
+	stack_index_t i;
+
+	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 != type)
+			continue;
+		if (os_strlen((char *) gen->d.ia5->data) == len &&
+		    os_memcmp(value, gen->d.ia5->data, len) == 0)
+			found++;
+	}
+
+	return found;
+}
+
+
+static int tls_match_altsubject(X509 *cert, const char *match)
+{
+	int type;
+	const char *pos, *end;
+	size_t len;
+
+	pos = match;
+	do {
+		if (os_strncmp(pos, "EMAIL:", 6) == 0) {
+			type = GEN_EMAIL;
+			pos += 6;
+		} else if (os_strncmp(pos, "DNS:", 4) == 0) {
+			type = GEN_DNS;
+			pos += 4;
+		} else if (os_strncmp(pos, "URI:", 4) == 0) {
+			type = GEN_URI;
+			pos += 4;
+		} else {
+			wpa_printf(MSG_INFO, "TLS: Invalid altSubjectName "
+				   "match '%s'", pos);
+			return 0;
+		}
+		end = os_strchr(pos, ';');
+		while (end) {
+			if (os_strncmp(end + 1, "EMAIL:", 6) == 0 ||
+			    os_strncmp(end + 1, "DNS:", 4) == 0 ||
+			    os_strncmp(end + 1, "URI:", 4) == 0)
+				break;
+			end = os_strchr(end + 1, ';');
+		}
+		if (end)
+			len = end - pos;
+		else
+			len = os_strlen(pos);
+		if (tls_match_altsubject_component(cert, type, pos, len) > 0)
+			return 1;
+		pos = end + 1;
+	} while (end);
+
+	return 0;
+}
+
+
+#ifndef CONFIG_NATIVE_WINDOWS
+static int domain_suffix_match(const u8 *val, size_t len, const char *match,
+			       int full)
+{
+	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 || (full && 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, int full)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+	/* wincrypt.h has conflicting X509_NAME definition */
+	return -1;
+#else /* CONFIG_NATIVE_WINDOWS */
+	GENERAL_NAME *gen;
+	void *ext;
+	int i;
+	stack_index_t j;
+	int dns_name = 0;
+	X509_NAME *name;
+
+	wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s",
+		   full ? "": "suffix ", match);
+
+	ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+	for (j = 0; ext && j < sk_GENERAL_NAME_num(ext); j++) {
+		gen = sk_GENERAL_NAME_value(ext, j);
+		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, full) ==
+		    1) {
+			wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
+				   full ? "Match" : "Suffix match");
+			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, full) == 1)
+		{
+			wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
+				   full ? "Match" : "Suffix match");
+			return 1;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
+		   full ? "": "suffix ");
+	return 0;
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
+static enum tls_fail_reason openssl_tls_fail_reason(int err)
+{
+	switch (err) {
+	case X509_V_ERR_CERT_REVOKED:
+		return TLS_FAIL_REVOKED;
+	case X509_V_ERR_CERT_NOT_YET_VALID:
+	case X509_V_ERR_CRL_NOT_YET_VALID:
+		return TLS_FAIL_NOT_YET_VALID;
+	case X509_V_ERR_CERT_HAS_EXPIRED:
+	case X509_V_ERR_CRL_HAS_EXPIRED:
+		return TLS_FAIL_EXPIRED;
+	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+	case X509_V_ERR_UNABLE_TO_GET_CRL:
+	case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+	case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+	case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+	case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+	case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+	case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+	case X509_V_ERR_INVALID_CA:
+		return TLS_FAIL_UNTRUSTED;
+	case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+	case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+	case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+	case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+	case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+	case X509_V_ERR_CERT_UNTRUSTED:
+	case X509_V_ERR_CERT_REJECTED:
+		return TLS_FAIL_BAD_CERTIFICATE;
+	default:
+		return TLS_FAIL_UNSPECIFIED;
+	}
+}
+
+
+static struct wpabuf * get_x509_cert(X509 *cert)
+{
+	struct wpabuf *buf;
+	u8 *tmp;
+
+	int cert_len = i2d_X509(cert, NULL);
+	if (cert_len <= 0)
+		return NULL;
+
+	buf = wpabuf_alloc(cert_len);
+	if (buf == NULL)
+		return NULL;
+
+	tmp = wpabuf_put(buf, cert_len);
+	i2d_X509(cert, &tmp);
+	return buf;
+}
+
+
+static void openssl_tls_fail_event(struct tls_connection *conn,
+				   X509 *err_cert, int err, int depth,
+				   const char *subject, const char *err_str,
+				   enum tls_fail_reason reason)
+{
+	union tls_event_data ev;
+	struct wpabuf *cert = NULL;
+	struct tls_context *context = conn->context;
+
+	if (context->event_cb == NULL)
+		return;
+
+	cert = get_x509_cert(err_cert);
+	os_memset(&ev, 0, sizeof(ev));
+	ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
+		reason : openssl_tls_fail_reason(err);
+	ev.cert_fail.depth = depth;
+	ev.cert_fail.subject = subject;
+	ev.cert_fail.reason_txt = err_str;
+	ev.cert_fail.cert = cert;
+	context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+	wpabuf_free(cert);
+}
+
+
+static void openssl_tls_cert_event(struct tls_connection *conn,
+				   X509 *err_cert, int depth,
+				   const char *subject)
+{
+	struct wpabuf *cert = NULL;
+	union tls_event_data ev;
+	struct tls_context *context = conn->context;
+	char *altsubject[TLS_MAX_ALT_SUBJECT];
+	int alt, num_altsubject = 0;
+	GENERAL_NAME *gen;
+	void *ext;
+	stack_index_t i;
+#ifdef CONFIG_SHA256
+	u8 hash[32];
+#endif /* CONFIG_SHA256 */
+
+	if (context->event_cb == NULL)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	if (conn->cert_probe || context->cert_in_cb) {
+		cert = get_x509_cert(err_cert);
+		ev.peer_cert.cert = cert;
+	}
+#ifdef CONFIG_SHA256
+	if (cert) {
+		const u8 *addr[1];
+		size_t len[1];
+		addr[0] = wpabuf_head(cert);
+		len[0] = wpabuf_len(cert);
+		if (sha256_vector(1, addr, len, hash) == 0) {
+			ev.peer_cert.hash = hash;
+			ev.peer_cert.hash_len = sizeof(hash);
+		}
+	}
+#endif /* CONFIG_SHA256 */
+	ev.peer_cert.depth = depth;
+	ev.peer_cert.subject = subject;
+
+	ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL);
+	for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+		char *pos;
+
+		if (num_altsubject == TLS_MAX_ALT_SUBJECT)
+			break;
+		gen = sk_GENERAL_NAME_value(ext, i);
+		if (gen->type != GEN_EMAIL &&
+		    gen->type != GEN_DNS &&
+		    gen->type != GEN_URI)
+			continue;
+
+		pos = os_malloc(10 + gen->d.ia5->length + 1);
+		if (pos == NULL)
+			break;
+		altsubject[num_altsubject++] = pos;
+
+		switch (gen->type) {
+		case GEN_EMAIL:
+			os_memcpy(pos, "EMAIL:", 6);
+			pos += 6;
+			break;
+		case GEN_DNS:
+			os_memcpy(pos, "DNS:", 4);
+			pos += 4;
+			break;
+		case GEN_URI:
+			os_memcpy(pos, "URI:", 4);
+			pos += 4;
+			break;
+		}
+
+		os_memcpy(pos, gen->d.ia5->data, gen->d.ia5->length);
+		pos += gen->d.ia5->length;
+		*pos = '\0';
+	}
+
+	for (alt = 0; alt < num_altsubject; alt++)
+		ev.peer_cert.altsubject[alt] = altsubject[alt];
+	ev.peer_cert.num_altsubject = num_altsubject;
+
+	context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+	wpabuf_free(cert);
+	for (alt = 0; alt < num_altsubject; alt++)
+		os_free(altsubject[alt]);
+}
+
+
+static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+	char buf[256];
+	X509 *err_cert;
+	int err, depth;
+	SSL *ssl;
+	struct tls_connection *conn;
+	struct tls_context *context;
+	char *match, *altmatch, *suffix_match, *domain_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,
+					 SSL_get_ex_data_X509_STORE_CTX_idx());
+	X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
+
+	conn = SSL_get_app_data(ssl);
+	if (conn == NULL)
+		return 0;
+
+	if (depth == 0)
+		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;
+	domain_match = conn->domain_match;
+
+	if (!preverify_ok && !conn->ca_cert_verify)
+		preverify_ok = 1;
+	if (!preverify_ok && depth > 0 && conn->server_cert_only)
+		preverify_ok = 1;
+	if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) &&
+	    (err == X509_V_ERR_CERT_HAS_EXPIRED ||
+	     err == X509_V_ERR_CERT_NOT_YET_VALID)) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Ignore certificate validity "
+			   "time mismatch");
+		preverify_ok = 1;
+	}
+
+	err_str = X509_verify_cert_error_string(err);
+
+#ifdef CONFIG_SHA256
+	/*
+	 * Do not require preverify_ok so we can explicity allow otherwise
+	 * invalid pinned server certificates.
+	 */
+	if (depth == 0 && conn->server_cert_only) {
+		struct wpabuf *cert;
+		cert = get_x509_cert(err_cert);
+		if (!cert) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch "
+				   "server certificate data");
+			preverify_ok = 0;
+		} else {
+			u8 hash[32];
+			const u8 *addr[1];
+			size_t len[1];
+			addr[0] = wpabuf_head(cert);
+			len[0] = wpabuf_len(cert);
+			if (sha256_vector(1, addr, len, hash) < 0 ||
+			    os_memcmp(conn->srv_cert_hash, hash, 32) != 0) {
+				err_str = "Server certificate mismatch";
+				err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+				preverify_ok = 0;
+			} else if (!preverify_ok) {
+				/*
+				 * Certificate matches pinned certificate, allow
+				 * regardless of other problems.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "OpenSSL: Ignore validation issues for a pinned server certificate");
+				preverify_ok = 1;
+			}
+			wpabuf_free(cert);
+		}
+	}
+#endif /* CONFIG_SHA256 */
+
+	if (!preverify_ok) {
+		wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
+			   " error %d (%s) depth %d for '%s'", err, err_str,
+			   depth, buf);
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       err_str, TLS_FAIL_UNSPECIFIED);
+		return preverify_ok;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - preverify_ok=%d "
+		   "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
+		   preverify_ok, err, err_str,
+		   conn->ca_cert_verify, depth, buf);
+	if (depth == 0 && match && os_strstr(buf, match) == NULL) {
+		wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
+			   "match with '%s'", buf, match);
+		preverify_ok = 0;
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Subject mismatch",
+				       TLS_FAIL_SUBJECT_MISMATCH);
+	} else if (depth == 0 && altmatch &&
+		   !tls_match_altsubject(err_cert, altmatch)) {
+		wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
+			   "'%s' not found", altmatch);
+		preverify_ok = 0;
+		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, 0)) {
+		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 if (depth == 0 && domain_match &&
+		   !tls_match_suffix(err_cert, domain_match, 1)) {
+		wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found",
+			   domain_match);
+		preverify_ok = 0;
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Domain mismatch",
+				       TLS_FAIL_DOMAIN_MISMATCH);
+	} else
+		openssl_tls_cert_event(conn, err_cert, depth, buf);
+
+	if (conn->cert_probe && preverify_ok && depth == 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate "
+			   "on probe-only run");
+		preverify_ok = 0;
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Server certificate chain probe",
+				       TLS_FAIL_SERVER_CHAIN_PROBE);
+	}
+
+	if (preverify_ok && context->event_cb != NULL)
+		context->event_cb(context->cb_ctx,
+				  TLS_CERT_CHAIN_SUCCESS, NULL);
+
+	return preverify_ok;
+}
+
+
+#ifndef OPENSSL_NO_STDIO
+static int tls_load_ca_der(struct tls_data *data, const char *ca_cert)
+{
+	SSL_CTX *ssl_ctx = data->ssl;
+	X509_LOOKUP *lookup;
+	int ret = 0;
+
+	lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(ssl_ctx),
+				       X509_LOOKUP_file());
+	if (lookup == NULL) {
+		tls_show_errors(MSG_WARNING, __func__,
+				"Failed add lookup for X509 store");
+		return -1;
+	}
+
+	if (!X509_LOOKUP_load_file(lookup, ca_cert, X509_FILETYPE_ASN1)) {
+		unsigned long err = ERR_peek_error();
+		tls_show_errors(MSG_WARNING, __func__,
+				"Failed load CA in DER format");
+		if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+		    ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring "
+				   "cert already in hash table error",
+				   __func__);
+		} else
+			ret = -1;
+	}
+
+	return ret;
+}
+#endif /* OPENSSL_NO_STDIO */
+
+
+static int tls_connection_ca_cert(struct tls_data *data,
+				  struct tls_connection *conn,
+				  const char *ca_cert, const u8 *ca_cert_blob,
+				  size_t ca_cert_blob_len, const char *ca_path)
+{
+	SSL_CTX *ssl_ctx = data->ssl;
+	X509_STORE *store;
+
+	/*
+	 * Remove previously configured trusted CA certificates before adding
+	 * new ones.
+	 */
+	store = X509_STORE_new();
+	if (store == NULL) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
+			   "certificate store", __func__);
+		return -1;
+	}
+	SSL_CTX_set_cert_store(ssl_ctx, store);
+
+	SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+	conn->ca_cert_verify = 1;
+
+	if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Probe for server certificate "
+			   "chain");
+		conn->cert_probe = 1;
+		conn->ca_cert_verify = 0;
+		return 0;
+	}
+
+	if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) {
+#ifdef CONFIG_SHA256
+		const char *pos = ca_cert + 7;
+		if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Unsupported ca_cert "
+				   "hash value '%s'", ca_cert);
+			return -1;
+		}
+		pos += 14;
+		if (os_strlen(pos) != 32 * 2) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Unexpected SHA256 "
+				   "hash length in ca_cert '%s'", ca_cert);
+			return -1;
+		}
+		if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Invalid SHA256 hash "
+				   "value in ca_cert '%s'", ca_cert);
+			return -1;
+		}
+		conn->server_cert_only = 1;
+		wpa_printf(MSG_DEBUG, "OpenSSL: Checking only server "
+			   "certificate match");
+		return 0;
+#else /* CONFIG_SHA256 */
+		wpa_printf(MSG_INFO, "No SHA256 included in the build - "
+			   "cannot validate server certificate hash");
+		return -1;
+#endif /* CONFIG_SHA256 */
+	}
+
+	if (ca_cert_blob) {
+		X509 *cert = d2i_X509(NULL,
+				      (const unsigned char **) &ca_cert_blob,
+				      ca_cert_blob_len);
+		if (cert == NULL) {
+			tls_show_errors(MSG_WARNING, __func__,
+					"Failed to parse ca_cert_blob");
+			return -1;
+		}
+
+		if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
+					 cert)) {
+			unsigned long err = ERR_peek_error();
+			tls_show_errors(MSG_WARNING, __func__,
+					"Failed to add ca_cert_blob to "
+					"certificate store");
+			if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+			    ERR_GET_REASON(err) ==
+			    X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+				wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring "
+					   "cert already in hash table error",
+					   __func__);
+			} else {
+				X509_free(cert);
+				return -1;
+			}
+		}
+		X509_free(cert);
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob "
+			   "to certificate store", __func__);
+		return 0;
+	}
+
+#ifdef ANDROID
+	if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) {
+		BIO *bio = BIO_from_keystore(&ca_cert[11]);
+		STACK_OF(X509_INFO) *stack = NULL;
+		stack_index_t i;
+
+		if (bio) {
+			stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
+			BIO_free(bio);
+		}
+		if (!stack)
+			return -1;
+
+		for (i = 0; i < sk_X509_INFO_num(stack); ++i) {
+			X509_INFO *info = sk_X509_INFO_value(stack, i);
+			if (info->x509) {
+				X509_STORE_add_cert(ssl_ctx->cert_store,
+						    info->x509);
+			}
+			if (info->crl) {
+				X509_STORE_add_crl(ssl_ctx->cert_store,
+						   info->crl);
+			}
+		}
+		sk_X509_INFO_pop_free(stack, X509_INFO_free);
+		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+		return 0;
+	}
+#endif /* ANDROID */
+
+#ifdef CONFIG_NATIVE_WINDOWS
+	if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) ==
+	    0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Added CA certificates from "
+			   "system certificate store");
+		return 0;
+	}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	if (ca_cert || ca_path) {
+#ifndef OPENSSL_NO_STDIO
+		if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, ca_path) !=
+		    1) {
+			tls_show_errors(MSG_WARNING, __func__,
+					"Failed to load root certificates");
+			if (ca_cert &&
+			    tls_load_ca_der(data, ca_cert) == 0) {
+				wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded "
+					   "DER format CA certificate",
+					   __func__);
+			} else
+				return -1;
+		} else {
+			wpa_printf(MSG_DEBUG, "TLS: Trusted root "
+				   "certificate(s) loaded");
+			tls_get_errors(data);
+		}
+#else /* OPENSSL_NO_STDIO */
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
+			   __func__);
+		return -1;
+#endif /* OPENSSL_NO_STDIO */
+	} else {
+		/* No ca_cert configured - do not try to verify server
+		 * certificate */
+		conn->ca_cert_verify = 0;
+	}
+
+	return 0;
+}
+
+
+static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert)
+{
+	SSL_CTX *ssl_ctx = data->ssl;
+
+	if (ca_cert) {
+		if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1)
+		{
+			tls_show_errors(MSG_WARNING, __func__,
+					"Failed to load root certificates");
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "TLS: Trusted root "
+			   "certificate(s) loaded");
+
+#ifndef OPENSSL_NO_STDIO
+		/* Add the same CAs to the client certificate requests */
+		SSL_CTX_set_client_CA_list(ssl_ctx,
+					   SSL_load_client_CA_file(ca_cert));
+#endif /* OPENSSL_NO_STDIO */
+	}
+
+	return 0;
+}
+
+
+int tls_global_set_verify(void *ssl_ctx, int check_crl)
+{
+	int flags;
+
+	if (check_crl) {
+		struct tls_data *data = ssl_ctx;
+		X509_STORE *cs = SSL_CTX_get_cert_store(data->ssl);
+		if (cs == NULL) {
+			tls_show_errors(MSG_INFO, __func__, "Failed to get "
+					"certificate store when enabling "
+					"check_crl");
+			return -1;
+		}
+		flags = X509_V_FLAG_CRL_CHECK;
+		if (check_crl == 2)
+			flags |= X509_V_FLAG_CRL_CHECK_ALL;
+		X509_STORE_set_flags(cs, flags);
+	}
+	return 0;
+}
+
+
+static int tls_connection_set_subject_match(struct tls_connection *conn,
+					    const char *subject_match,
+					    const char *altsubject_match,
+					    const char *suffix_match,
+					    const char *domain_match)
+{
+	os_free(conn->subject_match);
+	conn->subject_match = NULL;
+	if (subject_match) {
+		conn->subject_match = os_strdup(subject_match);
+		if (conn->subject_match == NULL)
+			return -1;
+	}
+
+	os_free(conn->altsubject_match);
+	conn->altsubject_match = NULL;
+	if (altsubject_match) {
+		conn->altsubject_match = os_strdup(altsubject_match);
+		if (conn->altsubject_match == NULL)
+			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;
+	}
+
+	os_free(conn->domain_match);
+	conn->domain_match = NULL;
+	if (domain_match) {
+		conn->domain_match = os_strdup(domain_match);
+		if (conn->domain_match == NULL)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static void tls_set_conn_flags(SSL *ssl, unsigned int flags)
+{
+#ifdef SSL_OP_NO_TICKET
+	if (flags & TLS_CONN_DISABLE_SESSION_TICKET)
+		SSL_set_options(ssl, SSL_OP_NO_TICKET);
+#ifdef SSL_clear_options
+	else
+		SSL_clear_options(ssl, SSL_OP_NO_TICKET);
+#endif /* SSL_clear_options */
+#endif /* SSL_OP_NO_TICKET */
+
+#ifdef SSL_OP_NO_TLSv1
+	if (flags & TLS_CONN_DISABLE_TLSv1_0)
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1);
+	else
+		SSL_clear_options(ssl, SSL_OP_NO_TLSv1);
+#endif /* SSL_OP_NO_TLSv1 */
+#ifdef SSL_OP_NO_TLSv1_1
+	if (flags & TLS_CONN_DISABLE_TLSv1_1)
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+	else
+		SSL_clear_options(ssl, SSL_OP_NO_TLSv1_1);
+#endif /* SSL_OP_NO_TLSv1_1 */
+#ifdef SSL_OP_NO_TLSv1_2
+	if (flags & TLS_CONN_DISABLE_TLSv1_2)
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
+	else
+		SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2);
+#endif /* SSL_OP_NO_TLSv1_2 */
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
+{
+	static int counter = 0;
+	struct tls_data *data = ssl_ctx;
+
+	if (conn == NULL)
+		return -1;
+
+	if (verify_peer) {
+		conn->ca_cert_verify = 1;
+		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
+			       SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
+			       SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
+	} else {
+		conn->ca_cert_verify = 0;
+		SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
+	}
+
+	tls_set_conn_flags(conn->ssl, flags);
+	conn->flags = flags;
+
+	SSL_set_accept_state(conn->ssl);
+
+	if (data->tls_session_lifetime == 0) {
+		/*
+		 * Set session id context to a unique value to make sure
+		 * session resumption cannot be used either through session
+		 * caching or TLS ticket extension.
+		 */
+		counter++;
+		SSL_set_session_id_context(conn->ssl,
+					   (const unsigned char *) &counter,
+					   sizeof(counter));
+	} else if (session_ctx) {
+		SSL_set_session_id_context(conn->ssl, session_ctx,
+					   session_ctx_len);
+	}
+
+	return 0;
+}
+
+
+static int tls_connection_client_cert(struct tls_connection *conn,
+				      const char *client_cert,
+				      const u8 *client_cert_blob,
+				      size_t client_cert_blob_len)
+{
+	if (client_cert == NULL && client_cert_blob == NULL)
+		return 0;
+
+	if (client_cert_blob &&
+	    SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob,
+				     client_cert_blob_len) == 1) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_ASN1 --> "
+			   "OK");
+		return 0;
+	} else if (client_cert_blob) {
+		tls_show_errors(MSG_DEBUG, __func__,
+				"SSL_use_certificate_ASN1 failed");
+	}
+
+	if (client_cert == NULL)
+		return -1;
+
+#ifdef ANDROID
+	if (os_strncmp("keystore://", client_cert, 11) == 0) {
+		BIO *bio = BIO_from_keystore(&client_cert[11]);
+		X509 *x509 = NULL;
+		int ret = -1;
+		if (bio) {
+			x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+			BIO_free(bio);
+		}
+		if (x509) {
+			if (SSL_use_certificate(conn->ssl, x509) == 1)
+				ret = 0;
+			X509_free(x509);
+		}
+		return ret;
+	}
+#endif /* ANDROID */
+
+#ifndef OPENSSL_NO_STDIO
+	if (SSL_use_certificate_file(conn->ssl, client_cert,
+				     SSL_FILETYPE_ASN1) == 1) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)"
+			   " --> OK");
+		return 0;
+	}
+
+	if (SSL_use_certificate_file(conn->ssl, client_cert,
+				     SSL_FILETYPE_PEM) == 1) {
+		ERR_clear_error();
+		wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)"
+			   " --> OK");
+		return 0;
+	}
+
+	tls_show_errors(MSG_DEBUG, __func__,
+			"SSL_use_certificate_file failed");
+#else /* OPENSSL_NO_STDIO */
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
+#endif /* OPENSSL_NO_STDIO */
+
+	return -1;
+}
+
+
+static int tls_global_client_cert(struct tls_data *data,
+				  const char *client_cert)
+{
+#ifndef OPENSSL_NO_STDIO
+	SSL_CTX *ssl_ctx = data->ssl;
+
+	if (client_cert == NULL)
+		return 0;
+
+	if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
+					 SSL_FILETYPE_ASN1) != 1 &&
+	    SSL_CTX_use_certificate_chain_file(ssl_ctx, client_cert) != 1 &&
+	    SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
+					 SSL_FILETYPE_PEM) != 1) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to load client certificate");
+		return -1;
+	}
+	return 0;
+#else /* OPENSSL_NO_STDIO */
+	if (client_cert == NULL)
+		return 0;
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
+	return -1;
+#endif /* OPENSSL_NO_STDIO */
+}
+
+
+static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
+{
+	if (password == NULL) {
+		return 0;
+	}
+	os_strlcpy(buf, (char *) password, size);
+	return os_strlen(buf);
+}
+
+
+#ifdef PKCS12_FUNCS
+static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12,
+			    const char *passwd)
+{
+	EVP_PKEY *pkey;
+	X509 *cert;
+	STACK_OF(X509) *certs;
+	int res = 0;
+	char buf[256];
+
+	pkey = NULL;
+	cert = NULL;
+	certs = NULL;
+	if (!passwd)
+		passwd = "";
+	if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) {
+		tls_show_errors(MSG_DEBUG, __func__,
+				"Failed to parse PKCS12 file");
+		PKCS12_free(p12);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data");
+
+	if (cert) {
+		X509_NAME_oneline(X509_get_subject_name(cert), buf,
+				  sizeof(buf));
+		wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12: "
+			   "subject='%s'", buf);
+		if (ssl) {
+			if (SSL_use_certificate(ssl, cert) != 1)
+				res = -1;
+		} else {
+			if (SSL_CTX_use_certificate(data->ssl, cert) != 1)
+				res = -1;
+		}
+		X509_free(cert);
+	}
+
+	if (pkey) {
+		wpa_printf(MSG_DEBUG, "TLS: Got private key from PKCS12");
+		if (ssl) {
+			if (SSL_use_PrivateKey(ssl, pkey) != 1)
+				res = -1;
+		} else {
+			if (SSL_CTX_use_PrivateKey(data->ssl, pkey) != 1)
+				res = -1;
+		}
+		EVP_PKEY_free(pkey);
+	}
+
+	if (certs) {
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+		SSL_clear_chain_certs(ssl);
+		while ((cert = sk_X509_pop(certs)) != NULL) {
+			X509_NAME_oneline(X509_get_subject_name(cert), buf,
+					  sizeof(buf));
+			wpa_printf(MSG_DEBUG, "TLS: additional certificate"
+				   " from PKCS12: subject='%s'", buf);
+			if (SSL_add1_chain_cert(ssl, cert) != 1) {
+				tls_show_errors(MSG_DEBUG, __func__,
+						"Failed to add additional certificate");
+				res = -1;
+				break;
+			}
+		}
+		if (!res) {
+			/* Try to continue anyway */
+		}
+		sk_X509_free(certs);
+#ifndef OPENSSL_IS_BORINGSSL
+		res = SSL_build_cert_chain(ssl,
+					   SSL_BUILD_CHAIN_FLAG_CHECK |
+					   SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
+		if (!res) {
+			tls_show_errors(MSG_DEBUG, __func__,
+					"Failed to build certificate chain");
+		} else if (res == 2) {
+			wpa_printf(MSG_DEBUG,
+				   "TLS: Ignore certificate chain verification error when building chain with PKCS#12 extra certificates");
+		}
+#endif /* OPENSSL_IS_BORINGSSL */
+		/*
+		 * Try to continue regardless of result since it is possible for
+		 * the extra certificates not to be required.
+		 */
+		res = 0;
+#else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+		SSL_CTX_clear_extra_chain_certs(data->ssl);
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10001000L */
+		while ((cert = sk_X509_pop(certs)) != NULL) {
+			X509_NAME_oneline(X509_get_subject_name(cert), buf,
+					  sizeof(buf));
+			wpa_printf(MSG_DEBUG, "TLS: additional certificate"
+				   " from PKCS12: subject='%s'", buf);
+			/*
+			 * There is no SSL equivalent for the chain cert - so
+			 * always add it to the context...
+			 */
+			if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1)
+			{
+				res = -1;
+				break;
+			}
+		}
+		sk_X509_free(certs);
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+	}
+
+	PKCS12_free(p12);
+
+	if (res < 0)
+		tls_get_errors(data);
+
+	return res;
+}
+#endif  /* PKCS12_FUNCS */
+
+
+static int tls_read_pkcs12(struct tls_data *data, SSL *ssl,
+			   const char *private_key, const char *passwd)
+{
+#ifdef PKCS12_FUNCS
+	FILE *f;
+	PKCS12 *p12;
+
+	f = fopen(private_key, "rb");
+	if (f == NULL)
+		return -1;
+
+	p12 = d2i_PKCS12_fp(f, NULL);
+	fclose(f);
+
+	if (p12 == NULL) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to use PKCS#12 file");
+		return -1;
+	}
+
+	return tls_parse_pkcs12(data, ssl, p12, passwd);
+
+#else /* PKCS12_FUNCS */
+	wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read "
+		   "p12/pfx files");
+	return -1;
+#endif  /* PKCS12_FUNCS */
+}
+
+
+static int tls_read_pkcs12_blob(struct tls_data *data, SSL *ssl,
+				const u8 *blob, size_t len, const char *passwd)
+{
+#ifdef PKCS12_FUNCS
+	PKCS12 *p12;
+
+	p12 = d2i_PKCS12(NULL, (const unsigned char **) &blob, len);
+	if (p12 == NULL) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to use PKCS#12 blob");
+		return -1;
+	}
+
+	return tls_parse_pkcs12(data, ssl, p12, passwd);
+
+#else /* PKCS12_FUNCS */
+	wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse "
+		   "p12/pfx blobs");
+	return -1;
+#endif  /* PKCS12_FUNCS */
+}
+
+
+#ifndef OPENSSL_NO_ENGINE
+static int tls_engine_get_cert(struct tls_connection *conn,
+			       const char *cert_id,
+			       X509 **cert)
+{
+	/* this runs after the private key is loaded so no PIN is required */
+	struct {
+		const char *cert_id;
+		X509 *cert;
+	} params;
+	params.cert_id = cert_id;
+	params.cert = NULL;
+
+	if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL",
+			     0, &params, NULL, 1)) {
+		unsigned long err = ERR_get_error();
+
+		wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id"
+			   " '%s' [%s]", cert_id,
+			   ERR_error_string(err, NULL));
+		if (tls_is_pin_error(err))
+			return TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN;
+		return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+	}
+	if (!params.cert) {
+		wpa_printf(MSG_ERROR, "ENGINE: did not properly cert with id"
+			   " '%s'", cert_id);
+		return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+	}
+	*cert = params.cert;
+	return 0;
+}
+#endif /* OPENSSL_NO_ENGINE */
+
+
+static int tls_connection_engine_client_cert(struct tls_connection *conn,
+					     const char *cert_id)
+{
+#ifndef OPENSSL_NO_ENGINE
+	X509 *cert;
+
+	if (tls_engine_get_cert(conn, cert_id, &cert))
+		return -1;
+
+	if (!SSL_use_certificate(conn->ssl, cert)) {
+		tls_show_errors(MSG_ERROR, __func__,
+				"SSL_use_certificate failed");
+                X509_free(cert);
+		return -1;
+	}
+	X509_free(cert);
+	wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> "
+		   "OK");
+	return 0;
+
+#else /* OPENSSL_NO_ENGINE */
+	return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static int tls_connection_engine_ca_cert(struct tls_data *data,
+					 struct tls_connection *conn,
+					 const char *ca_cert_id)
+{
+#ifndef OPENSSL_NO_ENGINE
+	X509 *cert;
+	SSL_CTX *ssl_ctx = data->ssl;
+	X509_STORE *store;
+
+	if (tls_engine_get_cert(conn, ca_cert_id, &cert))
+		return -1;
+
+	/* start off the same as tls_connection_ca_cert */
+	store = X509_STORE_new();
+	if (store == NULL) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
+			   "certificate store", __func__);
+		X509_free(cert);
+		return -1;
+	}
+	SSL_CTX_set_cert_store(ssl_ctx, store);
+	if (!X509_STORE_add_cert(store, cert)) {
+		unsigned long err = ERR_peek_error();
+		tls_show_errors(MSG_WARNING, __func__,
+				"Failed to add CA certificate from engine "
+				"to certificate store");
+		if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+		    ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring cert"
+				   " already in hash table error",
+				   __func__);
+		} else {
+			X509_free(cert);
+			return -1;
+		}
+	}
+	X509_free(cert);
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine "
+		   "to certificate store", __func__);
+	SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+	conn->ca_cert_verify = 1;
+
+	return 0;
+
+#else /* OPENSSL_NO_ENGINE */
+	return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static int tls_connection_engine_private_key(struct tls_connection *conn)
+{
+#ifndef OPENSSL_NO_ENGINE
+	if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) {
+		tls_show_errors(MSG_ERROR, __func__,
+				"ENGINE: cannot use private key for TLS");
+		return -1;
+	}
+	if (!SSL_check_private_key(conn->ssl)) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Private key failed verification");
+		return -1;
+	}
+	return 0;
+#else /* OPENSSL_NO_ENGINE */
+	wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but "
+		   "engine support was not compiled in");
+	return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static int tls_connection_private_key(struct tls_data *data,
+				      struct tls_connection *conn,
+				      const char *private_key,
+				      const char *private_key_passwd,
+				      const u8 *private_key_blob,
+				      size_t private_key_blob_len)
+{
+	SSL_CTX *ssl_ctx = data->ssl;
+	char *passwd;
+	int ok;
+
+	if (private_key == NULL && private_key_blob == NULL)
+		return 0;
+
+	if (private_key_passwd) {
+		passwd = os_strdup(private_key_passwd);
+		if (passwd == NULL)
+			return -1;
+	} else
+		passwd = NULL;
+
+	SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
+	SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
+
+	ok = 0;
+	while (private_key_blob) {
+		if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl,
+					    (u8 *) private_key_blob,
+					    private_key_blob_len) == 1) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_"
+				   "ASN1(EVP_PKEY_RSA) --> OK");
+			ok = 1;
+			break;
+		}
+
+		if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl,
+					    (u8 *) private_key_blob,
+					    private_key_blob_len) == 1) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_"
+				   "ASN1(EVP_PKEY_DSA) --> OK");
+			ok = 1;
+			break;
+		}
+
+		if (SSL_use_RSAPrivateKey_ASN1(conn->ssl,
+					       (u8 *) private_key_blob,
+					       private_key_blob_len) == 1) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: "
+				   "SSL_use_RSAPrivateKey_ASN1 --> OK");
+			ok = 1;
+			break;
+		}
+
+		if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob,
+					 private_key_blob_len, passwd) == 0) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> "
+				   "OK");
+			ok = 1;
+			break;
+		}
+
+		break;
+	}
+
+	while (!ok && private_key) {
+#ifndef OPENSSL_NO_STDIO
+		if (SSL_use_PrivateKey_file(conn->ssl, private_key,
+					    SSL_FILETYPE_ASN1) == 1) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: "
+				   "SSL_use_PrivateKey_File (DER) --> OK");
+			ok = 1;
+			break;
+		}
+
+		if (SSL_use_PrivateKey_file(conn->ssl, private_key,
+					    SSL_FILETYPE_PEM) == 1) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: "
+				   "SSL_use_PrivateKey_File (PEM) --> OK");
+			ok = 1;
+			break;
+		}
+#else /* OPENSSL_NO_STDIO */
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
+			   __func__);
+#endif /* OPENSSL_NO_STDIO */
+
+		if (tls_read_pkcs12(data, conn->ssl, private_key, passwd)
+		    == 0) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file "
+				   "--> OK");
+			ok = 1;
+			break;
+		}
+
+		if (tls_cryptoapi_cert(conn->ssl, private_key) == 0) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Using CryptoAPI to "
+				   "access certificate store --> OK");
+			ok = 1;
+			break;
+		}
+
+		break;
+	}
+
+	if (!ok) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to load private key");
+		os_free(passwd);
+		return -1;
+	}
+	ERR_clear_error();
+	SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
+	os_free(passwd);
+
+	if (!SSL_check_private_key(conn->ssl)) {
+		tls_show_errors(MSG_INFO, __func__, "Private key failed "
+				"verification");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "SSL: Private key loaded successfully");
+	return 0;
+}
+
+
+static int tls_global_private_key(struct tls_data *data,
+				  const char *private_key,
+				  const char *private_key_passwd)
+{
+	SSL_CTX *ssl_ctx = data->ssl;
+	char *passwd;
+
+	if (private_key == NULL)
+		return 0;
+
+	if (private_key_passwd) {
+		passwd = os_strdup(private_key_passwd);
+		if (passwd == NULL)
+			return -1;
+	} else
+		passwd = NULL;
+
+	SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
+	SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
+	if (
+#ifndef OPENSSL_NO_STDIO
+	    SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
+					SSL_FILETYPE_ASN1) != 1 &&
+	    SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
+					SSL_FILETYPE_PEM) != 1 &&
+#endif /* OPENSSL_NO_STDIO */
+	    tls_read_pkcs12(data, NULL, private_key, passwd)) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to load private key");
+		os_free(passwd);
+		ERR_clear_error();
+		return -1;
+	}
+	os_free(passwd);
+	ERR_clear_error();
+	SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
+
+	if (!SSL_CTX_check_private_key(ssl_ctx)) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Private key failed verification");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int tls_connection_dh(struct tls_connection *conn, const char *dh_file)
+{
+#ifdef OPENSSL_NO_DH
+	if (dh_file == NULL)
+		return 0;
+	wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
+		   "dh_file specified");
+	return -1;
+#else /* OPENSSL_NO_DH */
+	DH *dh;
+	BIO *bio;
+
+	/* TODO: add support for dh_blob */
+	if (dh_file == NULL)
+		return 0;
+	if (conn == NULL)
+		return -1;
+
+	bio = BIO_new_file(dh_file, "r");
+	if (bio == NULL) {
+		wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
+			   dh_file, ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+	dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+	BIO_free(bio);
+#ifndef OPENSSL_NO_DSA
+	while (dh == NULL) {
+		DSA *dsa;
+		wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
+			   " trying to parse as DSA params", dh_file,
+			   ERR_error_string(ERR_get_error(), NULL));
+		bio = BIO_new_file(dh_file, "r");
+		if (bio == NULL)
+			break;
+		dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
+		BIO_free(bio);
+		if (!dsa) {
+			wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
+				   "'%s': %s", dh_file,
+				   ERR_error_string(ERR_get_error(), NULL));
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
+		dh = DSA_dup_DH(dsa);
+		DSA_free(dsa);
+		if (dh == NULL) {
+			wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
+				   "params into DH params");
+			break;
+		}
+		break;
+	}
+#endif /* !OPENSSL_NO_DSA */
+	if (dh == NULL) {
+		wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
+			   "'%s'", dh_file);
+		return -1;
+	}
+
+	if (SSL_set_tmp_dh(conn->ssl, dh) != 1) {
+		wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
+			   "%s", dh_file,
+			   ERR_error_string(ERR_get_error(), NULL));
+		DH_free(dh);
+		return -1;
+	}
+	DH_free(dh);
+	return 0;
+#endif /* OPENSSL_NO_DH */
+}
+
+
+static int tls_global_dh(struct tls_data *data, const char *dh_file)
+{
+#ifdef OPENSSL_NO_DH
+	if (dh_file == NULL)
+		return 0;
+	wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
+		   "dh_file specified");
+	return -1;
+#else /* OPENSSL_NO_DH */
+	SSL_CTX *ssl_ctx = data->ssl;
+	DH *dh;
+	BIO *bio;
+
+	/* TODO: add support for dh_blob */
+	if (dh_file == NULL)
+		return 0;
+	if (ssl_ctx == NULL)
+		return -1;
+
+	bio = BIO_new_file(dh_file, "r");
+	if (bio == NULL) {
+		wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
+			   dh_file, ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+	dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+	BIO_free(bio);
+#ifndef OPENSSL_NO_DSA
+	while (dh == NULL) {
+		DSA *dsa;
+		wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
+			   " trying to parse as DSA params", dh_file,
+			   ERR_error_string(ERR_get_error(), NULL));
+		bio = BIO_new_file(dh_file, "r");
+		if (bio == NULL)
+			break;
+		dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
+		BIO_free(bio);
+		if (!dsa) {
+			wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
+				   "'%s': %s", dh_file,
+				   ERR_error_string(ERR_get_error(), NULL));
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
+		dh = DSA_dup_DH(dsa);
+		DSA_free(dsa);
+		if (dh == NULL) {
+			wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
+				   "params into DH params");
+			break;
+		}
+		break;
+	}
+#endif /* !OPENSSL_NO_DSA */
+	if (dh == NULL) {
+		wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
+			   "'%s'", dh_file);
+		return -1;
+	}
+
+	if (SSL_CTX_set_tmp_dh(ssl_ctx, dh) != 1) {
+		wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
+			   "%s", dh_file,
+			   ERR_error_string(ERR_get_error(), NULL));
+		DH_free(dh);
+		return -1;
+	}
+	DH_free(dh);
+	return 0;
+#endif /* OPENSSL_NO_DH */
+}
+
+
+int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
+			      struct tls_random *keys)
+{
+	SSL *ssl;
+
+	if (conn == NULL || keys == NULL)
+		return -1;
+	ssl = conn->ssl;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL)
+		return -1;
+
+	os_memset(keys, 0, sizeof(*keys));
+	keys->client_random = ssl->s3->client_random;
+	keys->client_random_len = SSL3_RANDOM_SIZE;
+	keys->server_random = ssl->s3->server_random;
+	keys->server_random_len = SSL3_RANDOM_SIZE;
+#else
+	if (ssl == NULL)
+		return -1;
+
+	os_memset(keys, 0, sizeof(*keys));
+	keys->client_random = conn->client_random;
+	keys->client_random_len = SSL_get_client_random(
+		ssl, conn->client_random, sizeof(conn->client_random));
+	keys->server_random = conn->server_random;
+	keys->server_random_len = SSL_get_server_random(
+		ssl, conn->server_random, sizeof(conn->server_random));
+#endif
+
+	return 0;
+}
+
+
+#ifndef CONFIG_FIPS
+static int openssl_get_keyblock_size(SSL *ssl)
+{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	const EVP_CIPHER *c;
+	const EVP_MD *h;
+	int md_size;
+
+	if (ssl->enc_read_ctx == NULL || ssl->enc_read_ctx->cipher == NULL ||
+	    ssl->read_hash == NULL)
+		return -1;
+
+	c = ssl->enc_read_ctx->cipher;
+#if OPENSSL_VERSION_NUMBER >= 0x00909000L
+	h = EVP_MD_CTX_md(ssl->read_hash);
+#else
+	h = ssl->read_hash;
+#endif
+	if (h)
+		md_size = EVP_MD_size(h);
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+	else if (ssl->s3)
+		md_size = ssl->s3->tmp.new_mac_secret_size;
+#endif
+	else
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d "
+		   "IV_len=%d", EVP_CIPHER_key_length(c), md_size,
+		   EVP_CIPHER_iv_length(c));
+	return 2 * (EVP_CIPHER_key_length(c) +
+		    md_size +
+		    EVP_CIPHER_iv_length(c));
+#else
+	const SSL_CIPHER *ssl_cipher;
+	int cipher, digest;
+	const EVP_CIPHER *c;
+	const EVP_MD *h;
+
+	ssl_cipher = SSL_get_current_cipher(ssl);
+	if (!ssl_cipher)
+		return -1;
+	cipher = SSL_CIPHER_get_cipher_nid(ssl_cipher);
+	digest = SSL_CIPHER_get_digest_nid(ssl_cipher);
+	wpa_printf(MSG_DEBUG, "OpenSSL: cipher nid %d digest nid %d",
+		   cipher, digest);
+	if (cipher < 0 || digest < 0)
+		return -1;
+	c = EVP_get_cipherbynid(cipher);
+	h = EVP_get_digestbynid(digest);
+	if (!c || !h)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: keyblock size: key_len=%d MD_size=%d IV_len=%d",
+		   EVP_CIPHER_key_length(c), EVP_MD_size(h),
+		   EVP_CIPHER_iv_length(c));
+	return 2 * (EVP_CIPHER_key_length(c) + EVP_MD_size(h) +
+		    EVP_CIPHER_iv_length(c));
+#endif
+}
+#endif /* CONFIG_FIPS */
+
+
+static int openssl_tls_prf(struct tls_connection *conn,
+			   const char *label, int server_random_first,
+			   int skip_keyblock, u8 *out, size_t out_len)
+{
+#ifdef CONFIG_FIPS
+	wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
+		   "mode");
+	return -1;
+#else /* CONFIG_FIPS */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	SSL *ssl;
+	u8 *rnd;
+	int ret = -1;
+	int skip = 0;
+	u8 *tmp_out = NULL;
+	u8 *_out = out;
+	const char *ver;
+
+	/*
+	 * TLS library did not support key generation, so get the needed TLS
+	 * session parameters and use an internal implementation of TLS PRF to
+	 * derive the key.
+	 */
+
+	if (conn == NULL)
+		return -1;
+	ssl = conn->ssl;
+	if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL ||
+	    ssl->session->master_key_length <= 0)
+		return -1;
+	ver = SSL_get_version(ssl);
+
+	if (skip_keyblock) {
+		skip = openssl_get_keyblock_size(ssl);
+		if (skip < 0)
+			return -1;
+		tmp_out = os_malloc(skip + out_len);
+		if (!tmp_out)
+			return -1;
+		_out = tmp_out;
+	}
+
+	rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
+	if (!rnd) {
+		os_free(tmp_out);
+		return -1;
+	}
+
+	if (server_random_first) {
+		os_memcpy(rnd, ssl->s3->server_random, SSL3_RANDOM_SIZE);
+		os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->client_random,
+			SSL3_RANDOM_SIZE);
+	} else {
+		os_memcpy(rnd, ssl->s3->client_random, SSL3_RANDOM_SIZE);
+		os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->server_random,
+			SSL3_RANDOM_SIZE);
+	}
+
+	if (os_strcmp(ver, "TLSv1.2") == 0) {
+		tls_prf_sha256(ssl->session->master_key,
+			       ssl->session->master_key_length,
+			       label, rnd, 2 * SSL3_RANDOM_SIZE,
+			       _out, skip + out_len);
+		ret = 0;
+	} else if (tls_prf_sha1_md5(ssl->session->master_key,
+				    ssl->session->master_key_length,
+				    label, rnd, 2 * SSL3_RANDOM_SIZE,
+				    _out, skip + out_len) == 0) {
+		ret = 0;
+	}
+	os_free(rnd);
+	if (ret == 0 && skip_keyblock)
+		os_memcpy(out, _out + skip, out_len);
+	bin_clear_free(tmp_out, skip);
+
+	return ret;
+#else
+	SSL *ssl;
+	SSL_SESSION *sess;
+	u8 *rnd;
+	int ret = -1;
+	int skip = 0;
+	u8 *tmp_out = NULL;
+	u8 *_out = out;
+	unsigned char client_random[SSL3_RANDOM_SIZE];
+	unsigned char server_random[SSL3_RANDOM_SIZE];
+	unsigned char master_key[64];
+	size_t master_key_len;
+	const char *ver;
+
+	/*
+	 * TLS library did not support key generation, so get the needed TLS
+	 * session parameters and use an internal implementation of TLS PRF to
+	 * derive the key.
+	 */
+
+	if (conn == NULL)
+		return -1;
+	ssl = conn->ssl;
+	if (ssl == NULL)
+		return -1;
+	ver = SSL_get_version(ssl);
+	sess = SSL_get_session(ssl);
+	if (!ver || !sess)
+		return -1;
+
+	if (skip_keyblock) {
+		skip = openssl_get_keyblock_size(ssl);
+		if (skip < 0)
+			return -1;
+		tmp_out = os_malloc(skip + out_len);
+		if (!tmp_out)
+			return -1;
+		_out = tmp_out;
+	}
+
+	rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
+	if (!rnd) {
+		os_free(tmp_out);
+		return -1;
+	}
+
+	SSL_get_client_random(ssl, client_random, sizeof(client_random));
+	SSL_get_server_random(ssl, server_random, sizeof(server_random));
+	master_key_len = SSL_SESSION_get_master_key(sess, master_key,
+						    sizeof(master_key));
+
+	if (server_random_first) {
+		os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
+		os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random,
+			  SSL3_RANDOM_SIZE);
+	} else {
+		os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE);
+		os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random,
+			  SSL3_RANDOM_SIZE);
+	}
+
+	if (os_strcmp(ver, "TLSv1.2") == 0) {
+		tls_prf_sha256(master_key, master_key_len,
+			       label, rnd, 2 * SSL3_RANDOM_SIZE,
+			       _out, skip + out_len);
+		ret = 0;
+	} else if (tls_prf_sha1_md5(master_key, master_key_len,
+				    label, rnd, 2 * SSL3_RANDOM_SIZE,
+				    _out, skip + out_len) == 0) {
+		ret = 0;
+	}
+	os_memset(master_key, 0, sizeof(master_key));
+	os_free(rnd);
+	if (ret == 0 && skip_keyblock)
+		os_memcpy(out, _out + skip, out_len);
+	bin_clear_free(tmp_out, skip);
+
+	return ret;
+#endif
+#endif /* CONFIG_FIPS */
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+		       const char *label, int server_random_first,
+		       int skip_keyblock, u8 *out, size_t out_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+	SSL *ssl;
+	if (conn == NULL)
+		return -1;
+	if (server_random_first || skip_keyblock)
+		return openssl_tls_prf(conn, label,
+				       server_random_first, skip_keyblock,
+				       out, out_len);
+	ssl = conn->ssl;
+	if (SSL_export_keying_material(ssl, out, out_len, label,
+				       os_strlen(label), NULL, 0, 0) == 1) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF");
+		return 0;
+	}
+#endif
+	return openssl_tls_prf(conn, label, server_random_first,
+			       skip_keyblock, out, out_len);
+}
+
+
+static struct wpabuf *
+openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data,
+		  int server)
+{
+	int res;
+	struct wpabuf *out_data;
+
+	/*
+	 * Give TLS handshake data from the server (if available) to OpenSSL
+	 * for processing.
+	 */
+	if (in_data && wpabuf_len(in_data) > 0 &&
+	    BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data))
+	    < 0) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Handshake failed - BIO_write");
+		return NULL;
+	}
+
+	/* Initiate TLS handshake or continue the existing handshake */
+	if (server)
+		res = SSL_accept(conn->ssl);
+	else
+		res = SSL_connect(conn->ssl);
+	if (res != 1) {
+		int err = SSL_get_error(conn->ssl, res);
+		if (err == SSL_ERROR_WANT_READ)
+			wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want "
+				   "more data");
+		else if (err == SSL_ERROR_WANT_WRITE)
+			wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to "
+				   "write");
+		else {
+			tls_show_errors(MSG_INFO, __func__, "SSL_connect");
+			conn->failed++;
+		}
+	}
+
+	/* Get the TLS handshake data to be sent to the server */
+	res = BIO_ctrl_pending(conn->ssl_out);
+	wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res);
+	out_data = wpabuf_alloc(res);
+	if (out_data == NULL) {
+		wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for "
+			   "handshake output (%d bytes)", res);
+		if (BIO_reset(conn->ssl_out) < 0) {
+			tls_show_errors(MSG_INFO, __func__,
+					"BIO_reset failed");
+		}
+		return NULL;
+	}
+	res = res == 0 ? 0 : BIO_read(conn->ssl_out, wpabuf_mhead(out_data),
+				      res);
+	if (res < 0) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Handshake failed - BIO_read");
+		if (BIO_reset(conn->ssl_out) < 0) {
+			tls_show_errors(MSG_INFO, __func__,
+					"BIO_reset failed");
+		}
+		wpabuf_free(out_data);
+		return NULL;
+	}
+	wpabuf_put(out_data, res);
+
+	return out_data;
+}
+
+
+static struct wpabuf *
+openssl_get_appl_data(struct tls_connection *conn, size_t max_len)
+{
+	struct wpabuf *appl_data;
+	int res;
+
+	appl_data = wpabuf_alloc(max_len + 100);
+	if (appl_data == NULL)
+		return NULL;
+
+	res = SSL_read(conn->ssl, wpabuf_mhead(appl_data),
+		       wpabuf_size(appl_data));
+	if (res < 0) {
+		int err = SSL_get_error(conn->ssl, res);
+		if (err == SSL_ERROR_WANT_READ ||
+		    err == SSL_ERROR_WANT_WRITE) {
+			wpa_printf(MSG_DEBUG, "SSL: No Application Data "
+				   "included");
+		} else {
+			tls_show_errors(MSG_INFO, __func__,
+					"Failed to read possible "
+					"Application Data");
+		}
+		wpabuf_free(appl_data);
+		return NULL;
+	}
+
+	wpabuf_put(appl_data, res);
+	wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application Data in Finished "
+			    "message", appl_data);
+
+	return appl_data;
+}
+
+
+static struct wpabuf *
+openssl_connection_handshake(struct tls_connection *conn,
+			     const struct wpabuf *in_data,
+			     struct wpabuf **appl_data, int server)
+{
+	struct wpabuf *out_data;
+
+	if (appl_data)
+		*appl_data = NULL;
+
+	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)) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Handshake finished - resumed=%d",
+			   tls_connection_resumed(conn->ssl_ctx, conn));
+		if (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;
+}
+
+
+struct wpabuf *
+tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn,
+			 const struct wpabuf *in_data,
+			 struct wpabuf **appl_data)
+{
+	return openssl_connection_handshake(conn, in_data, appl_data, 0);
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+						struct tls_connection *conn,
+						const struct wpabuf *in_data,
+						struct wpabuf **appl_data)
+{
+	return openssl_connection_handshake(conn, in_data, appl_data, 1);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	int res;
+	struct wpabuf *buf;
+
+	if (conn == NULL)
+		return NULL;
+
+	/* Give plaintext data for OpenSSL to encrypt into the TLS tunnel. */
+	if ((res = BIO_reset(conn->ssl_in)) < 0 ||
+	    (res = BIO_reset(conn->ssl_out)) < 0) {
+		tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
+		return NULL;
+	}
+	res = SSL_write(conn->ssl, wpabuf_head(in_data), wpabuf_len(in_data));
+	if (res < 0) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Encryption failed - SSL_write");
+		return NULL;
+	}
+
+	/* Read encrypted data to be sent to the server */
+	buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+	if (buf == NULL)
+		return NULL;
+	res = BIO_read(conn->ssl_out, wpabuf_mhead(buf), wpabuf_size(buf));
+	if (res < 0) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Encryption failed - BIO_read");
+		wpabuf_free(buf);
+		return NULL;
+	}
+	wpabuf_put(buf, res);
+
+	return buf;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	int res;
+	struct wpabuf *buf;
+
+	/* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */
+	res = BIO_write(conn->ssl_in, wpabuf_head(in_data),
+			wpabuf_len(in_data));
+	if (res < 0) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Decryption failed - BIO_write");
+		return NULL;
+	}
+	if (BIO_reset(conn->ssl_out) < 0) {
+		tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
+		return NULL;
+	}
+
+	/* Read decrypted data for further processing */
+	/*
+	 * Even though we try to disable TLS compression, it is possible that
+	 * this cannot be done with all TLS libraries. Add extra buffer space
+	 * to handle the possibility of the decrypted data being longer than
+	 * input data.
+	 */
+	buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+	if (buf == NULL)
+		return NULL;
+	res = SSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf));
+	if (res < 0) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Decryption failed - SSL_read");
+		wpabuf_free(buf);
+		return NULL;
+	}
+	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;
+}
+
+
+int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+	return conn ? SSL_cache_hit(conn->ssl) : 0;
+#else
+	return conn ? conn->ssl->hit : 0;
+#endif
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+				   u8 *ciphers)
+{
+	char buf[100], *pos, *end;
+	u8 *c;
+	int ret;
+
+	if (conn == NULL || conn->ssl == NULL || ciphers == NULL)
+		return -1;
+
+	buf[0] = '\0';
+	pos = buf;
+	end = pos + sizeof(buf);
+
+	c = ciphers;
+	while (*c != TLS_CIPHER_NONE) {
+		const char *suite;
+
+		switch (*c) {
+		case TLS_CIPHER_RC4_SHA:
+			suite = "RC4-SHA";
+			break;
+		case TLS_CIPHER_AES128_SHA:
+			suite = "AES128-SHA";
+			break;
+		case TLS_CIPHER_RSA_DHE_AES128_SHA:
+			suite = "DHE-RSA-AES128-SHA";
+			break;
+		case TLS_CIPHER_ANON_DH_AES128_SHA:
+			suite = "ADH-AES128-SHA";
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "TLS: Unsupported "
+				   "cipher selection: %d", *c);
+			return -1;
+		}
+		ret = os_snprintf(pos, end - pos, ":%s", suite);
+		if (os_snprintf_error(end - pos, ret))
+			break;
+		pos += ret;
+
+		c++;
+	}
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+	if (os_strstr(buf, ":ADH-")) {
+		/*
+		 * Need to drop to security level 0 to allow anonymous
+		 * cipher suites for EAP-FAST.
+		 */
+		SSL_set_security_level(conn->ssl, 0);
+	} else if (SSL_get_security_level(conn->ssl) == 0) {
+		/* Force at least security level 1 */
+		SSL_set_security_level(conn->ssl, 1);
+	}
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+#endif
+
+	if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Cipher suite configuration failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	const char *name;
+	if (conn == NULL || conn->ssl == NULL)
+		return -1;
+
+	name = SSL_get_version(conn->ssl);
+	if (name == NULL)
+		return -1;
+
+	os_strlcpy(buf, name, buflen);
+	return 0;
+}
+
+
+int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
+		   char *buf, size_t buflen)
+{
+	const char *name;
+	if (conn == NULL || conn->ssl == NULL)
+		return -1;
+
+	name = SSL_get_cipher(conn->ssl);
+	if (name == NULL)
+		return -1;
+
+	os_strlcpy(buf, name, buflen);
+	return 0;
+}
+
+
+int tls_connection_enable_workaround(void *ssl_ctx,
+				     struct tls_connection *conn)
+{
+	SSL_set_options(conn->ssl, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
+
+	return 0;
+}
+
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+/* ClientHello TLS extensions require a patch to openssl, so this function is
+ * commented out unless explicitly needed for EAP-FAST in order to be able to
+ * build this file with unmodified openssl. */
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+				    int ext_type, const u8 *data,
+				    size_t data_len)
+{
+	if (conn == NULL || conn->ssl == NULL || ext_type != 35)
+		return -1;
+
+	if (SSL_set_session_ticket_ext(conn->ssl, (void *) data,
+				       data_len) != 1)
+		return -1;
+
+	return 0;
+}
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+
+int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return -1;
+	return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return -1;
+	return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return -1;
+	return conn->write_alerts;
+}
+
+
+#ifdef HAVE_OCSP
+
+static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
+{
+#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;
+
+	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_DEBUG, "OpenSSL: OCSP Response\n%s", txt);
+	}
+	os_free(txt);
+	BIO_free(out);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+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;
+	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");
+		return (conn->flags & TLS_CONN_REQUIRE_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");
+		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));
+		return 0;
+	}
+
+	basic = OCSP_response_get1_basic(rsp);
+	if (!basic) {
+		wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse");
+		return 0;
+	}
+
+	store = SSL_CTX_get_cert_store(conn->ssl_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");
+		}
+		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");
+				X509_free(cert);
+				sk_X509_free(certs);
+				certs = NULL;
+			}
+			if (certs && 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's issuer to OCSP responder trust store");
+					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");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded");
+
+	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;
+	}
+
+	id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
+	if (!id) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		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",
+			   (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" :
+			   " (OCSP not required)");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
+	}
+
+	if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
+		tls_show_errors(MSG_INFO, __func__,
+				"OpenSSL: OCSP status times invalid");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		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)
+		return 0;
+	if (conn->flags & TLS_CONN_REQUIRE_OCSP) {
+		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");
+	return 1;
+}
+
+
+static int ocsp_status_cb(SSL *s, void *arg)
+{
+	char *tmp;
+	char *resp;
+	size_t len;
+
+	if (tls_global->ocsp_stapling_response == NULL) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - no response configured");
+		return SSL_TLSEXT_ERR_OK;
+	}
+
+	resp = os_readfile(tls_global->ocsp_stapling_response, &len);
+	if (resp == NULL) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - could not read response file");
+		/* TODO: Build OCSPResponse with responseStatus = internalError
+		 */
+		return SSL_TLSEXT_ERR_OK;
+	}
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - send cached response");
+	tmp = OPENSSL_malloc(len);
+	if (tmp == NULL) {
+		os_free(resp);
+		return SSL_TLSEXT_ERR_ALERT_FATAL;
+	}
+
+	os_memcpy(tmp, resp, len);
+	os_free(resp);
+	SSL_set_tlsext_status_ocsp_resp(s, tmp, len);
+
+	return SSL_TLSEXT_ERR_OK;
+}
+
+#endif /* HAVE_OCSP */
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+			      const struct tls_connection_params *params)
+{
+	struct tls_data *data = tls_ctx;
+	int ret;
+	unsigned long err;
+	int can_pkcs11 = 0;
+	const char *key_id = params->key_id;
+	const char *cert_id = params->cert_id;
+	const char *ca_cert_id = params->ca_cert_id;
+	const char *engine_id = params->engine ? params->engine_id : NULL;
+
+	if (conn == NULL)
+		return -1;
+
+	/*
+	 * If the engine isn't explicitly configured, and any of the
+	 * cert/key fields are actually PKCS#11 URIs, then automatically
+	 * use the PKCS#11 ENGINE.
+	 */
+	if (!engine_id || os_strcmp(engine_id, "pkcs11") == 0)
+		can_pkcs11 = 1;
+
+	if (!key_id && params->private_key && can_pkcs11 &&
+	    os_strncmp(params->private_key, "pkcs11:", 7) == 0) {
+		can_pkcs11 = 2;
+		key_id = params->private_key;
+	}
+
+	if (!cert_id && params->client_cert && can_pkcs11 &&
+	    os_strncmp(params->client_cert, "pkcs11:", 7) == 0) {
+		can_pkcs11 = 2;
+		cert_id = params->client_cert;
+	}
+
+	if (!ca_cert_id && params->ca_cert && can_pkcs11 &&
+	    os_strncmp(params->ca_cert, "pkcs11:", 7) == 0) {
+		can_pkcs11 = 2;
+		ca_cert_id = params->ca_cert;
+	}
+
+	/* If we need to automatically enable the PKCS#11 ENGINE, do so. */
+	if (can_pkcs11 == 2 && !engine_id)
+		engine_id = "pkcs11";
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	if (params->flags & TLS_CONN_EAP_FAST) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Use TLSv1_method() for EAP-FAST");
+		if (SSL_set_ssl_method(conn->ssl, TLSv1_method()) != 1) {
+			tls_show_errors(MSG_INFO, __func__,
+					"Failed to set TLSv1_method() for EAP-FAST");
+			return -1;
+		}
+	}
+#endif
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+	while ((err = ERR_get_error())) {
+		wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
+			   __func__, ERR_error_string(err, NULL));
+	}
+
+	if (engine_id) {
+		wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine");
+		ret = tls_engine_init(conn, engine_id, params->pin,
+				      key_id, cert_id, ca_cert_id);
+		if (ret)
+			return ret;
+	}
+	if (tls_connection_set_subject_match(conn,
+					     params->subject_match,
+					     params->altsubject_match,
+					     params->suffix_match,
+					     params->domain_match))
+		return -1;
+
+	if (engine_id && ca_cert_id) {
+		if (tls_connection_engine_ca_cert(data, conn, ca_cert_id))
+			return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
+	} else if (tls_connection_ca_cert(data, conn, params->ca_cert,
+					  params->ca_cert_blob,
+					  params->ca_cert_blob_len,
+					  params->ca_path))
+		return -1;
+
+	if (engine_id && cert_id) {
+		if (tls_connection_engine_client_cert(conn, cert_id))
+			return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
+	} else if (tls_connection_client_cert(conn, params->client_cert,
+					      params->client_cert_blob,
+					      params->client_cert_blob_len))
+		return -1;
+
+	if (engine_id && key_id) {
+		wpa_printf(MSG_DEBUG, "TLS: Using private key from engine");
+		if (tls_connection_engine_private_key(conn))
+			return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
+	} else if (tls_connection_private_key(data, conn,
+					      params->private_key,
+					      params->private_key_passwd,
+					      params->private_key_blob,
+					      params->private_key_blob_len)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'",
+			   params->private_key);
+		return -1;
+	}
+
+	if (tls_connection_dh(conn, params->dh_file)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
+			   params->dh_file);
+		return -1;
+	}
+
+	if (params->openssl_ciphers &&
+	    SSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Failed to set cipher string '%s'",
+			   params->openssl_ciphers);
+		return -1;
+	}
+
+	tls_set_conn_flags(conn->ssl, params->flags);
+
+#ifdef HAVE_OCSP
+	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		SSL_CTX *ssl_ctx = data->ssl;
+		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);
+	}
+#else /* HAVE_OCSP */
+	if (params->flags & TLS_CONN_REQUIRE_OCSP) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: No OCSP support included - reject configuration");
+		return -1;
+	}
+	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: No OCSP support included - allow optional OCSP case to continue");
+	}
+#endif /* HAVE_OCSP */
+
+	conn->flags = params->flags;
+
+	tls_get_errors(data);
+
+	return 0;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+			  const struct tls_connection_params *params)
+{
+	struct tls_data *data = tls_ctx;
+	SSL_CTX *ssl_ctx = data->ssl;
+	unsigned long err;
+
+	while ((err = ERR_get_error())) {
+		wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
+			   __func__, ERR_error_string(err, NULL));
+	}
+
+	if (tls_global_ca_cert(data, params->ca_cert) ||
+	    tls_global_client_cert(data, params->client_cert) ||
+	    tls_global_private_key(data, params->private_key,
+				   params->private_key_passwd) ||
+	    tls_global_dh(data, params->dh_file)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to set global parameters");
+		return -1;
+	}
+
+	if (params->openssl_ciphers &&
+	    SSL_CTX_set_cipher_list(ssl_ctx, params->openssl_ciphers) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Failed to set cipher string '%s'",
+			   params->openssl_ciphers);
+		return -1;
+	}
+
+#ifdef SSL_OP_NO_TICKET
+	if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
+		SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
+#ifdef SSL_CTX_clear_options
+	else
+		SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET);
+#endif /* SSL_clear_options */
+#endif /*  SSL_OP_NO_TICKET */
+
+#ifdef HAVE_OCSP
+	SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_status_cb);
+	SSL_CTX_set_tlsext_status_arg(ssl_ctx, ssl_ctx);
+	os_free(tls_global->ocsp_stapling_response);
+	if (params->ocsp_stapling_response)
+		tls_global->ocsp_stapling_response =
+			os_strdup(params->ocsp_stapling_response);
+	else
+		tls_global->ocsp_stapling_response = NULL;
+#endif /* HAVE_OCSP */
+
+	return 0;
+}
+
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+/* Pre-shared secred requires a patch to openssl, so this function is
+ * commented out unless explicitly needed for EAP-FAST in order to be able to
+ * build this file with unmodified openssl. */
+
+#ifdef OPENSSL_IS_BORINGSSL
+static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
+			   STACK_OF(SSL_CIPHER) *peer_ciphers,
+			   const SSL_CIPHER **cipher, void *arg)
+#else /* OPENSSL_IS_BORINGSSL */
+static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
+			   STACK_OF(SSL_CIPHER) *peer_ciphers,
+			   SSL_CIPHER **cipher, void *arg)
+#endif /* OPENSSL_IS_BORINGSSL */
+{
+	struct tls_connection *conn = arg;
+	int ret;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	if (conn == NULL || conn->session_ticket_cb == NULL)
+		return 0;
+
+	ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
+				      conn->session_ticket,
+				      conn->session_ticket_len,
+				      s->s3->client_random,
+				      s->s3->server_random, secret);
+#else
+	unsigned char client_random[SSL3_RANDOM_SIZE];
+	unsigned char server_random[SSL3_RANDOM_SIZE];
+
+	if (conn == NULL || conn->session_ticket_cb == NULL)
+		return 0;
+
+	SSL_get_client_random(s, client_random, sizeof(client_random));
+	SSL_get_server_random(s, server_random, sizeof(server_random));
+
+	ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
+				      conn->session_ticket,
+				      conn->session_ticket_len,
+				      client_random,
+				      server_random, secret);
+#endif
+
+	os_free(conn->session_ticket);
+	conn->session_ticket = NULL;
+
+	if (ret <= 0)
+		return 0;
+
+	*secret_len = SSL_MAX_MASTER_KEY_LENGTH;
+	return 1;
+}
+
+
+static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data,
+				     int len, void *arg)
+{
+	struct tls_connection *conn = arg;
+
+	if (conn == NULL || conn->session_ticket_cb == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s: length=%d", __func__, len);
+
+	os_free(conn->session_ticket);
+	conn->session_ticket = NULL;
+
+	wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
+		    "extension", data, len);
+
+	conn->session_ticket = os_malloc(len);
+	if (conn->session_ticket == NULL)
+		return 0;
+
+	os_memcpy(conn->session_ticket, data, len);
+	conn->session_ticket_len = len;
+
+	return 1;
+}
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+					 struct tls_connection *conn,
+					 tls_session_ticket_cb cb,
+					 void *ctx)
+{
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+	conn->session_ticket_cb = cb;
+	conn->session_ticket_cb_ctx = ctx;
+
+	if (cb) {
+		if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
+					      conn) != 1)
+			return -1;
+		SSL_set_session_ticket_ext_cb(conn->ssl,
+					      tls_session_ticket_ext_cb, conn);
+	} else {
+		if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
+			return -1;
+		SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL);
+	}
+
+	return 0;
+#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+	return -1;
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+}
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
+			   OPENSSL_VERSION_TEXT,
+			   SSLeay_version(SSLEAY_VERSION));
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+	SSL_SESSION *sess;
+	struct wpabuf *old;
+
+	if (tls_ex_idx_session < 0)
+		goto fail;
+	sess = SSL_get_session(conn->ssl);
+	if (!sess)
+		goto fail;
+	old = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+	if (old) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Replacing old success data %p",
+			   old);
+		wpabuf_free(old);
+	}
+	if (SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: Stored success data %p", data);
+	conn->success_data = 1;
+	return;
+
+fail:
+	wpa_printf(MSG_INFO, "OpenSSL: Failed to store success data");
+	wpabuf_free(data);
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Success data accepted for resumed session");
+	conn->success_data = 1;
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	SSL_SESSION *sess;
+
+	if (tls_ex_idx_session < 0 ||
+	    !(sess = SSL_get_session(conn->ssl)))
+		return NULL;
+	return SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+	SSL_SESSION *sess;
+
+	sess = SSL_get_session(conn->ssl);
+	if (!sess)
+		return;
+
+	if (SSL_CTX_remove_session(conn->ssl_ctx, sess) != 1)
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Session was not cached");
+	else
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Removed cached session to disable session resumption");
+}
diff --git a/hostap/src/drivers/Makefile b/hostap/src/drivers/Makefile
new file mode 100644
index 0000000..5721154
--- /dev/null
+++ b/hostap/src/drivers/Makefile
@@ -0,0 +1,9 @@
+all:
+	@echo Nothing to be made.
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
+	rm -f build.wpa_supplicant build.hostapd
+
+install:
+	@echo Nothing to be made.
diff --git a/hostap/src/drivers/android_drv.h b/hostap/src/drivers/android_drv.h
new file mode 100644
index 0000000..31d9440
--- /dev/null
+++ b/hostap/src/drivers/android_drv.h
@@ -0,0 +1,56 @@
+/*
+ * Android driver interface
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ANDROID_DRV_H
+#define ANDROID_DRV_H
+
+#define WPA_EVENT_DRIVER_STATE "CTRL-EVENT-DRIVER-STATE "
+
+#define MAX_SSID_LEN 32
+
+#define MAX_DRV_CMD_SIZE		248
+#define DRV_NUMBER_SEQUENTIAL_ERRORS	4
+
+#define WEXT_PNOSETUP_HEADER		"PNOSETUP "
+#define WEXT_PNOSETUP_HEADER_SIZE	9
+#define WEXT_PNO_TLV_PREFIX		'S'
+#define WEXT_PNO_TLV_VERSION		'1'
+#define WEXT_PNO_TLV_SUBVERSION		'2'
+#define WEXT_PNO_TLV_RESERVED		'0'
+#define WEXT_PNO_VERSION_SIZE		4
+#define WEXT_PNO_AMOUNT			16
+#define WEXT_PNO_SSID_SECTION		'S'
+/* SSID header size is SSID section type above + SSID length */
+#define WEXT_PNO_SSID_HEADER_SIZE	2
+#define WEXT_PNO_SCAN_INTERVAL_SECTION	'T'
+#define WEXT_PNO_SCAN_INTERVAL_LENGTH	2
+#define WEXT_PNO_SCAN_INTERVAL		30
+/* Scan interval size is scan interval section type + scan interval length
+ * above */
+#define WEXT_PNO_SCAN_INTERVAL_SIZE	(1 + WEXT_PNO_SCAN_INTERVAL_LENGTH)
+#define WEXT_PNO_REPEAT_SECTION		'R'
+#define WEXT_PNO_REPEAT_LENGTH		1
+#define WEXT_PNO_REPEAT			4
+/* Repeat section size is Repeat section type + Repeat value length above */
+#define WEXT_PNO_REPEAT_SIZE		(1 + WEXT_PNO_REPEAT_LENGTH)
+#define WEXT_PNO_MAX_REPEAT_SECTION	'M'
+#define WEXT_PNO_MAX_REPEAT_LENGTH	1
+#define WEXT_PNO_MAX_REPEAT		3
+/* Max Repeat section size is Max Repeat section type + Max Repeat value length
+ * above */
+#define WEXT_PNO_MAX_REPEAT_SIZE	(1 + WEXT_PNO_MAX_REPEAT_LENGTH)
+/* This corresponds to the size of all sections expect SSIDs */
+#define WEXT_PNO_NONSSID_SECTIONS_SIZE \
+(WEXT_PNO_SCAN_INTERVAL_SIZE + WEXT_PNO_REPEAT_SIZE + WEXT_PNO_MAX_REPEAT_SIZE)
+/* PNO Max command size is total of header, version, ssid and other sections +
+ * Null termination */
+#define WEXT_PNO_MAX_COMMAND_SIZE \
+	(WEXT_PNOSETUP_HEADER_SIZE + WEXT_PNO_VERSION_SIZE \
+	 + WEXT_PNO_AMOUNT * (WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN) \
+	 + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1)
+
+#endif /* ANDROID_DRV_H */
diff --git a/hostap/src/drivers/driver.h b/hostap/src/drivers/driver.h
new file mode 100644
index 0000000..3cdab5a
--- /dev/null
+++ b/hostap/src/drivers/driver.h
@@ -0,0 +1,4704 @@
+/*
+ * Driver interface definition
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file defines a driver interface used by both %wpa_supplicant and
+ * hostapd. The first part of the file defines data structures used in various
+ * driver operations. This is followed by the struct wpa_driver_ops that each
+ * driver wrapper will beed to define with callback functions for requesting
+ * driver operations. After this, there are definitions for driver event
+ * reporting with wpa_supplicant_event() and some convenience helper functions
+ * that can be used to report events.
+ */
+
+#ifndef DRIVER_H
+#define DRIVER_H
+
+#define WPA_SUPPLICANT_DRIVER_VERSION 4
+
+#include "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "utils/list.h"
+
+#define HOSTAPD_CHAN_DISABLED 0x00000001
+#define HOSTAPD_CHAN_NO_IR 0x00000002
+#define HOSTAPD_CHAN_RADAR 0x00000008
+#define HOSTAPD_CHAN_HT40PLUS 0x00000010
+#define HOSTAPD_CHAN_HT40MINUS 0x00000020
+#define HOSTAPD_CHAN_HT40 0x00000040
+#define HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED 0x00000080
+
+#define HOSTAPD_CHAN_DFS_UNKNOWN 0x00000000
+#define HOSTAPD_CHAN_DFS_USABLE 0x00000100
+#define HOSTAPD_CHAN_DFS_UNAVAILABLE 0x00000200
+#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
+
+#define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
+#define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
+
+/**
+ * enum reg_change_initiator - Regulatory change initiator
+ */
+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 - Regulatory change types
+ */
+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
+ */
+struct hostapd_channel_data {
+	/**
+	 * chan - Channel number (IEEE 802.11)
+	 */
+	short chan;
+
+	/**
+	 * freq - Frequency in MHz
+	 */
+	int freq;
+
+	/**
+	 * flag - Channel flags (HOSTAPD_CHAN_*)
+	 */
+	int flag;
+
+	/**
+	 * max_tx_power - Regulatory transmit power limit in dBm
+	 */
+	u8 max_tx_power;
+
+	/**
+	 * survey_list - Linked list of surveys (struct freq_survey)
+	 */
+	struct dl_list survey_list;
+
+	/**
+	 * min_nf - Minimum observed noise floor, in dBm, based on all
+	 * surveyed channel data
+	 */
+	s8 min_nf;
+
+#ifdef CONFIG_ACS
+	/**
+	 * interference_factor - Computed interference factor on this
+	 * channel (used internally in src/ap/acs.c; driver wrappers do not
+	 * need to set this)
+	 */
+	long double interference_factor;
+#endif /* CONFIG_ACS */
+
+	/**
+	 * dfs_cac_ms - DFS CAC time in milliseconds
+	 */
+	unsigned int dfs_cac_ms;
+};
+
+#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
+#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
+
+/**
+ * struct hostapd_hw_modes - Supported hardware mode information
+ */
+struct hostapd_hw_modes {
+	/**
+	 * mode - Hardware mode
+	 */
+	enum hostapd_hw_mode mode;
+
+	/**
+	 * num_channels - Number of entries in the channels array
+	 */
+	int num_channels;
+
+	/**
+	 * channels - Array of supported channels
+	 */
+	struct hostapd_channel_data *channels;
+
+	/**
+	 * num_rates - Number of entries in the rates array
+	 */
+	int num_rates;
+
+	/**
+	 * rates - Array of supported rates in 100 kbps units
+	 */
+	int *rates;
+
+	/**
+	 * ht_capab - HT (IEEE 802.11n) capabilities
+	 */
+	u16 ht_capab;
+
+	/**
+	 * mcs_set - MCS (IEEE 802.11n) rate parameters
+	 */
+	u8 mcs_set[16];
+
+	/**
+	 * a_mpdu_params - A-MPDU (IEEE 802.11n) parameters
+	 */
+	u8 a_mpdu_params;
+
+	/**
+	 * vht_capab - VHT (IEEE 802.11ac) capabilities
+	 */
+	u32 vht_capab;
+
+	/**
+	 * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters
+	 */
+	u8 vht_mcs_set[8];
+
+	unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
+};
+
+
+#define IEEE80211_MODE_INFRA	0
+#define IEEE80211_MODE_IBSS	1
+#define IEEE80211_MODE_AP	2
+#define IEEE80211_MODE_MESH	5
+
+#define IEEE80211_CAP_ESS	0x0001
+#define IEEE80211_CAP_IBSS	0x0002
+#define IEEE80211_CAP_PRIVACY	0x0010
+#define IEEE80211_CAP_RRM	0x1000
+
+/* DMG (60 GHz) IEEE 802.11ad */
+/* type - bits 0..1 */
+#define IEEE80211_CAP_DMG_MASK	0x0003
+#define IEEE80211_CAP_DMG_IBSS	0x0001 /* Tx by: STA */
+#define IEEE80211_CAP_DMG_PBSS	0x0002 /* Tx by: PCP */
+#define IEEE80211_CAP_DMG_AP	0x0003 /* Tx by: AP */
+
+#define WPA_SCAN_QUAL_INVALID		BIT(0)
+#define WPA_SCAN_NOISE_INVALID		BIT(1)
+#define WPA_SCAN_LEVEL_INVALID		BIT(2)
+#define WPA_SCAN_LEVEL_DBM		BIT(3)
+#define WPA_SCAN_ASSOCIATED		BIT(5)
+
+/**
+ * struct wpa_scan_res - Scan result for an BSS/IBSS
+ * @flags: information flags about the BSS/IBSS (WPA_SCAN_*)
+ * @bssid: BSSID
+ * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
+ * @beacon_int: beacon interval in TUs (host byte order)
+ * @caps: capability information field in host byte order
+ * @qual: signal quality
+ * @noise: noise level
+ * @level: signal level
+ * @tsf: Timestamp
+ * @age: Age of the information in milliseconds (i.e., how many milliseconds
+ * ago the last Beacon or Probe Response frame was received)
+ * @est_throughput: Estimated throughput in kbps (this is calculated during
+ * scan result processing if left zero by the driver wrapper)
+ * @snr: Signal-to-noise ratio in dB (calculated during scan result processing)
+ * @ie_len: length of the following IE field in octets
+ * @beacon_ie_len: length of the following Beacon IE field in octets
+ *
+ * This structure is used as a generic format for scan results from the
+ * driver. Each driver interface implementation is responsible for converting
+ * the driver or OS specific scan results into this format.
+ *
+ * If the driver does not support reporting all IEs, the IE data structure is
+ * constructed of the IEs that are available. This field will also need to
+ * include SSID in IE format. All drivers are encouraged to be extended to
+ * report all IEs to make it easier to support future additions.
+ *
+ * This structure data is followed by ie_len octets of IEs from Probe Response
+ * frame (or if the driver does not indicate source of IEs, these may also be
+ * from Beacon frame). After the first set of IEs, another set of IEs may follow
+ * (with beacon_ie_len octets of data) if the driver provides both IE sets.
+ */
+struct wpa_scan_res {
+	unsigned int flags;
+	u8 bssid[ETH_ALEN];
+	int freq;
+	u16 beacon_int;
+	u16 caps;
+	int qual;
+	int noise;
+	int level;
+	u64 tsf;
+	unsigned int age;
+	unsigned int est_throughput;
+	int snr;
+	size_t ie_len;
+	size_t beacon_ie_len;
+	/* Followed by ie_len + beacon_ie_len octets of IE data */
+};
+
+/**
+ * struct wpa_scan_results - Scan results
+ * @res: Array of pointers to allocated variable length scan result entries
+ * @num: Number of entries in the scan result array
+ * @fetch_time: Time when the results were fetched from the driver
+ */
+struct wpa_scan_results {
+	struct wpa_scan_res **res;
+	size_t num;
+	struct os_reltime fetch_time;
+};
+
+/**
+ * struct wpa_interface_info - Network interface information
+ * @next: Pointer to the next interface or NULL if this is the last one
+ * @ifname: Interface name that can be used with init() or init2()
+ * @desc: Human readable adapter description (e.g., vendor/model) or NULL if
+ *	not available
+ * @drv_name: struct wpa_driver_ops::name (note: unlike other strings, this one
+ *	is not an allocated copy, i.e., get_interfaces() caller will not free
+ *	this)
+ */
+struct wpa_interface_info {
+	struct wpa_interface_info *next;
+	char *ifname;
+	char *desc;
+	const char *drv_name;
+};
+
+#define WPAS_MAX_SCAN_SSIDS 16
+
+/**
+ * struct wpa_driver_scan_params - Scan parameters
+ * Data for struct wpa_driver_ops::scan2().
+ */
+struct wpa_driver_scan_params {
+	/**
+	 * ssids - SSIDs to scan for
+	 */
+	struct wpa_driver_scan_ssid {
+		/**
+		 * ssid - specific SSID to scan for (ProbeReq)
+		 * %NULL or zero-length SSID is used to indicate active scan
+		 * with wildcard SSID.
+		 */
+		const u8 *ssid;
+		/**
+		 * ssid_len: Length of the SSID in octets
+		 */
+		size_t ssid_len;
+	} ssids[WPAS_MAX_SCAN_SSIDS];
+
+	/**
+	 * num_ssids - Number of entries in ssids array
+	 * Zero indicates a request for a passive scan.
+	 */
+	size_t num_ssids;
+
+	/**
+	 * extra_ies - Extra IE(s) to add into Probe Request or %NULL
+	 */
+	const u8 *extra_ies;
+
+	/**
+	 * extra_ies_len - Length of extra_ies in octets
+	 */
+	size_t extra_ies_len;
+
+	/**
+	 * freqs - Array of frequencies to scan or %NULL for all frequencies
+	 *
+	 * The frequency is set in MHz. The array is zero-terminated.
+	 */
+	int *freqs;
+
+	/**
+	 * filter_ssids - Filter for reporting SSIDs
+	 *
+	 * This optional parameter can be used to request the driver wrapper to
+	 * filter scan results to include only the specified SSIDs. %NULL
+	 * indicates that no filtering is to be done. This can be used to
+	 * reduce memory needs for scan results in environments that have large
+	 * number of APs with different SSIDs.
+	 *
+	 * The driver wrapper is allowed to take this allocated buffer into its
+	 * own use by setting the pointer to %NULL. In that case, the driver
+	 * wrapper is responsible for freeing the buffer with os_free() once it
+	 * is not needed anymore.
+	 */
+	struct wpa_driver_scan_filter {
+		u8 ssid[SSID_MAX_LEN];
+		size_t ssid_len;
+	} *filter_ssids;
+
+	/**
+	 * num_filter_ssids - Number of entries in filter_ssids array
+	 */
+	size_t num_filter_ssids;
+
+	/**
+	 * filter_rssi - Filter by RSSI
+	 *
+	 * The driver may filter scan results in firmware to reduce host
+	 * wakeups and thereby save power. Specify the RSSI threshold in s32
+	 * dBm.
+	 */
+	s32 filter_rssi;
+
+	/**
+	 * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes
+	 *
+	 * When set, the driver is expected to remove rates 1, 2, 5.5, and 11
+	 * Mbps from the support rates element(s) in the Probe Request frames
+	 * and not to transmit the frames at any of those rates.
+	 */
+	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;
+
+	/**
+	 * mac_addr_rand - Requests driver to randomize MAC address
+	 */
+	unsigned int mac_addr_rand:1;
+
+	/**
+	 * mac_addr - MAC address used with randomization. The address cannot be
+	 * a multicast one, i.e., bit 0 of byte 0 should not be set.
+	 */
+	const u8 *mac_addr;
+
+	/**
+	 * mac_addr_mask - MAC address mask used with randomization.
+	 *
+	 * Bits that are 0 in the mask should be randomized. Bits that are 1 in
+	 * the mask should be taken as is from mac_addr. The mask should not
+	 * allow the generation of a multicast address, i.e., bit 0 of byte 0
+	 * must be set.
+	 */
+	const u8 *mac_addr_mask;
+
+	/*
+	 * NOTE: Whenever adding new parameters here, please make sure
+	 * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
+	 * matching changes.
+	 */
+};
+
+/**
+ * struct wpa_driver_auth_params - Authentication parameters
+ * Data for struct wpa_driver_ops::authenticate().
+ */
+struct wpa_driver_auth_params {
+	int freq;
+	const u8 *bssid;
+	const u8 *ssid;
+	size_t ssid_len;
+	int auth_alg;
+	const u8 *ie;
+	size_t ie_len;
+	const u8 *wep_key[4];
+	size_t wep_key_len[4];
+	int wep_tx_keyidx;
+	int local_state_change;
+
+	/**
+	 * p2p - Whether this connection is a P2P group
+	 */
+	int p2p;
+
+	/**
+	 * sae_data - SAE elements for Authentication frame
+	 *
+	 * This buffer starts with the Authentication transaction sequence
+	 * number field. If SAE is not used, this pointer is %NULL.
+	 */
+	const u8 *sae_data;
+
+	/**
+	 * sae_data_len - Length of sae_data buffer in octets
+	 */
+	size_t sae_data_len;
+};
+
+/**
+ * enum wps_mode - WPS mode
+ */
+enum wps_mode {
+	/**
+	 * WPS_MODE_NONE - No WPS provisioning being used
+	 */
+	WPS_MODE_NONE,
+
+	/**
+	 * WPS_MODE_OPEN - WPS provisioning with AP that is in open mode
+	 */
+	WPS_MODE_OPEN,
+
+	/**
+	 * WPS_MODE_PRIVACY - WPS provisioning with AP that is using protection
+	 */
+	WPS_MODE_PRIVACY
+};
+
+/**
+ * struct hostapd_freq_params - Channel parameters
+ */
+struct hostapd_freq_params {
+	/**
+	 * mode - Mode/band (HOSTAPD_MODE_IEEE80211A, ..)
+	 */
+	enum hostapd_hw_mode mode;
+
+	/**
+	 * freq - Primary channel center frequency in MHz
+	 */
+	int freq;
+
+	/**
+	 * channel - Channel number
+	 */
+	int channel;
+
+	/**
+	 * ht_enabled - Whether HT is enabled
+	 */
+	int ht_enabled;
+
+	/**
+	 * sec_channel_offset - Secondary channel offset for HT40
+	 *
+	 * 0 = HT40 disabled,
+	 * -1 = HT40 enabled, secondary channel below primary,
+	 * 1 = HT40 enabled, secondary channel above primary
+	 */
+	int sec_channel_offset;
+
+	/**
+	 * vht_enabled - Whether VHT is enabled
+	 */
+	int vht_enabled;
+
+	/**
+	 * center_freq1 - Segment 0 center frequency in MHz
+	 *
+	 * Valid for both HT and VHT.
+	 */
+	int center_freq1;
+
+	/**
+	 * center_freq2 - Segment 1 center frequency in MHz
+	 *
+	 * Non-zero only for bandwidth 80 and an 80+80 channel
+	 */
+	int center_freq2;
+
+	/**
+	 * bandwidth - Channel bandwidth in MHz (20, 40, 80, 160)
+	 */
+	int bandwidth;
+};
+
+/**
+ * struct wpa_driver_associate_params - Association parameters
+ * Data for struct wpa_driver_ops::associate().
+ */
+struct wpa_driver_associate_params {
+	/**
+	 * bssid - BSSID of the selected AP
+	 * This can be %NULL, if ap_scan=2 mode is used and the driver is
+	 * responsible for selecting with which BSS to associate. */
+	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;
+
+	/**
+	 * ssid_len - Length of the SSID (1..32)
+	 */
+	size_t ssid_len;
+
+	/**
+	 * freq - channel parameters
+	 */
+	struct hostapd_freq_params 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
+	 */
+	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
+	 * of this WPA IE is optional. If the driver generates the WPA
+	 * IE, it can use pairwise_suite, group_suite, and
+	 * key_mgmt_suite to select proper algorithms. In this case,
+	 * the driver has to notify wpa_supplicant about the used WPA
+	 * IE by generating an event that the interface code will
+	 * convert into EVENT_ASSOCINFO data (see below).
+	 *
+	 * When using WPA2/IEEE 802.11i, wpa_ie is used for RSN IE
+	 * instead. The driver can determine which version is used by
+	 * looking at the first byte of the IE (0xdd for WPA, 0x30 for
+	 * WPA2/RSN).
+	 *
+	 * When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE.
+	 */
+	const u8 *wpa_ie;
+
+	/**
+	 * wpa_ie_len - length of the wpa_ie
+	 */
+	size_t wpa_ie_len;
+
+	/**
+	 * wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2
+	 */
+	unsigned int wpa_proto;
+
+	/**
+	 * pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*)
+	 *
+	 * This is usually ignored if @wpa_ie is used.
+	 */
+	unsigned int pairwise_suite;
+
+	/**
+	 * group_suite - Selected group cipher suite (WPA_CIPHER_*)
+	 *
+	 * This is usually ignored if @wpa_ie is used.
+	 */
+	unsigned int group_suite;
+
+	/**
+	 * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*)
+	 *
+	 * This is usually ignored if @wpa_ie is used.
+	 */
+	unsigned int key_mgmt_suite;
+
+	/**
+	 * auth_alg - Allowed authentication algorithms
+	 * Bit field of WPA_AUTH_ALG_*
+	 */
+	int auth_alg;
+
+	/**
+	 * mode - Operation mode (infra/ibss) IEEE80211_MODE_*
+	 */
+	int mode;
+
+	/**
+	 * wep_key - WEP keys for static WEP configuration
+	 */
+	const u8 *wep_key[4];
+
+	/**
+	 * wep_key_len - WEP key length for static WEP configuration
+	 */
+	size_t wep_key_len[4];
+
+	/**
+	 * wep_tx_keyidx - WEP TX key index for static WEP configuration
+	 */
+	int wep_tx_keyidx;
+
+	/**
+	 * mgmt_frame_protection - IEEE 802.11w management frame protection
+	 */
+	enum mfp_options mgmt_frame_protection;
+
+	/**
+	 * ft_ies - IEEE 802.11r / FT information elements
+	 * If the supplicant is using IEEE 802.11r (FT) and has the needed keys
+	 * for fast transition, this parameter is set to include the IEs that
+	 * are to be sent in the next FT Authentication Request message.
+	 * update_ft_ies() handler is called to update the IEs for further
+	 * FT messages in the sequence.
+	 *
+	 * The driver should use these IEs only if the target AP is advertising
+	 * the same mobility domain as the one included in the MDIE here.
+	 *
+	 * In ap_scan=2 mode, the driver can use these IEs when moving to a new
+	 * AP after the initial association. These IEs can only be used if the
+	 * target AP is advertising support for FT and is using the same MDIE
+	 * and SSID as the current AP.
+	 *
+	 * The driver is responsible for reporting the FT IEs received from the
+	 * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE
+	 * type. update_ft_ies() handler will then be called with the FT IEs to
+	 * include in the next frame in the authentication sequence.
+	 */
+	const u8 *ft_ies;
+
+	/**
+	 * ft_ies_len - Length of ft_ies in bytes
+	 */
+	size_t ft_ies_len;
+
+	/**
+	 * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies)
+	 *
+	 * This value is provided to allow the driver interface easier access
+	 * to the current mobility domain. This value is set to %NULL if no
+	 * mobility domain is currently active.
+	 */
+	const u8 *ft_md;
+
+	/**
+	 * passphrase - RSN passphrase for PSK
+	 *
+	 * This value is made available only for WPA/WPA2-Personal (PSK) and
+	 * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
+	 * the 8..63 character ASCII passphrase, if available. Please note that
+	 * this can be %NULL if passphrase was not used to generate the PSK. In
+	 * that case, the psk field must be used to fetch the PSK.
+	 */
+	const char *passphrase;
+
+	/**
+	 * psk - RSN PSK (alternative for passphrase for PSK)
+	 *
+	 * This value is made available only for WPA/WPA2-Personal (PSK) and
+	 * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
+	 * the 32-octet (256-bit) PSK, if available. The driver wrapper should
+	 * be prepared to handle %NULL value as an error.
+	 */
+	const u8 *psk;
+
+	/**
+	 * drop_unencrypted - Enable/disable unencrypted frame filtering
+	 *
+	 * Configure the driver to drop all non-EAPOL frames (both receive and
+	 * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must
+	 * still be allowed for key negotiation.
+	 */
+	int drop_unencrypted;
+
+	/**
+	 * prev_bssid - Previously used BSSID in this ESS
+	 *
+	 * When not %NULL, this is a request to use reassociation instead of
+	 * association.
+	 */
+	const u8 *prev_bssid;
+
+	/**
+	 * wps - WPS mode
+	 *
+	 * If the driver needs to do special configuration for WPS association,
+	 * this variable provides more information on what type of association
+	 * is being requested. Most drivers should not need ot use this.
+	 */
+	enum wps_mode wps;
+
+	/**
+	 * p2p - Whether this connection is a P2P group
+	 */
+	int p2p;
+
+	/**
+	 * uapsd - UAPSD parameters for the network
+	 * -1 = do not change defaults
+	 * AP mode: 1 = enabled, 0 = disabled
+	 * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE
+	 */
+	int uapsd;
+
+	/**
+	 * fixed_bssid - Whether to force this BSSID in IBSS mode
+	 * 1 = Fix this BSSID and prevent merges.
+	 * 0 = Do not fix BSSID.
+	 */
+	int fixed_bssid;
+
+	/**
+	 * fixed_freq - Fix control channel in IBSS mode
+	 * 0 = don't fix control channel (default)
+	 * 1 = fix control channel; this prevents IBSS merging with another
+	 *	channel
+	 */
+	int fixed_freq;
+
+	/**
+	 * disable_ht - Disable HT (IEEE 802.11n) for this connection
+	 */
+	int disable_ht;
+
+	/**
+	 * htcaps - HT Capabilities over-rides
+	 *
+	 * Only bits set in the mask will be used, and not all values are used
+	 * by the kernel anyway. Currently, MCS, MPDU and MSDU fields are used.
+	 *
+	 * Pointer to struct ieee80211_ht_capabilities.
+	 */
+	const u8 *htcaps;
+
+	/**
+	 * htcaps_mask - HT Capabilities over-rides mask
+	 *
+	 * Pointer to struct ieee80211_ht_capabilities.
+	 */
+	const u8 *htcaps_mask;
+
+#ifdef CONFIG_VHT_OVERRIDES
+	/**
+	 * disable_vht - Disable VHT for this connection
+	 */
+	int disable_vht;
+
+	/**
+	 * VHT capability overrides.
+	 */
+	const struct ieee80211_vht_capabilities *vhtcaps;
+	const struct ieee80211_vht_capabilities *vhtcaps_mask;
+#endif /* CONFIG_VHT_OVERRIDES */
+
+	/**
+	 * req_key_mgmt_offload - Request key management offload for connection
+	 *
+	 * Request key management offload for this connection if the device
+	 * supports it.
+	 */
+	int req_key_mgmt_offload;
+
+	/**
+	 * Flag for indicating whether this association includes support for
+	 * RRM (Radio Resource Measurements)
+	 */
+	int rrm_used;
+};
+
+enum hide_ssid {
+	NO_SSID_HIDING,
+	HIDDEN_SSID_ZERO_LEN,
+	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
+	 */
+	u8 *head;
+
+	/**
+	 * head_len - Length of the head buffer in octets
+	 */
+	size_t head_len;
+
+	/**
+	 * tail - Beacon tail following TIM IE
+	 */
+	u8 *tail;
+
+	/**
+	 * tail_len - Length of the tail buffer in octets
+	 */
+	size_t tail_len;
+
+	/**
+	 * dtim_period - DTIM period
+	 */
+	int dtim_period;
+
+	/**
+	 * beacon_int - Beacon interval
+	 */
+	int beacon_int;
+
+	/**
+	 * basic_rates: -1 terminated array of basic rates in 100 kbps
+	 *
+	 * This parameter can be used to set a specific basic rate set for the
+	 * BSS. If %NULL, default basic rate set is used.
+	 */
+	int *basic_rates;
+
+	/**
+	 * proberesp - Probe Response template
+	 *
+	 * This is used by drivers that reply to Probe Requests internally in
+	 * AP mode and require the full Probe Response template.
+	 */
+	u8 *proberesp;
+
+	/**
+	 * proberesp_len - Length of the proberesp buffer in octets
+	 */
+	size_t proberesp_len;
+
+	/**
+	 * ssid - The SSID to use in Beacon/Probe Response frames
+	 */
+	const u8 *ssid;
+
+	/**
+	 * ssid_len - Length of the SSID (1..32)
+	 */
+	size_t ssid_len;
+
+	/**
+	 * hide_ssid - Whether to hide the SSID
+	 */
+	enum hide_ssid hide_ssid;
+
+	/**
+	 * pairwise_ciphers - WPA_CIPHER_* bitfield
+	 */
+	unsigned int pairwise_ciphers;
+
+	/**
+	 * group_cipher - WPA_CIPHER_*
+	 */
+	unsigned int group_cipher;
+
+	/**
+	 * key_mgmt_suites - WPA_KEY_MGMT_* bitfield
+	 */
+	unsigned int key_mgmt_suites;
+
+	/**
+	 * auth_algs - WPA_AUTH_ALG_* bitfield
+	 */
+	unsigned int auth_algs;
+
+	/**
+	 * wpa_version - WPA_PROTO_* bitfield
+	 */
+	unsigned int wpa_version;
+
+	/**
+	 * privacy - Whether privacy is used in the BSS
+	 */
+	int privacy;
+
+	/**
+	 * beacon_ies - WPS/P2P IE(s) for Beacon frames
+	 *
+	 * This is used to add IEs like WPS IE and P2P IE by drivers that do
+	 * not use the full Beacon template.
+	 */
+	const struct wpabuf *beacon_ies;
+
+	/**
+	 * proberesp_ies - P2P/WPS IE(s) for Probe Response frames
+	 *
+	 * This is used to add IEs like WPS IE and P2P IE by drivers that
+	 * reply to Probe Request frames internally.
+	 */
+	const struct wpabuf *proberesp_ies;
+
+	/**
+	 * assocresp_ies - WPS IE(s) for (Re)Association Response frames
+	 *
+	 * This is used to add IEs like WPS IE by drivers that reply to
+	 * (Re)Association Request frames internally.
+	 */
+	const struct wpabuf *assocresp_ies;
+
+	/**
+	 * isolate - Whether to isolate frames between associated stations
+	 *
+	 * If this is non-zero, the AP is requested to disable forwarding of
+	 * frames between associated stations.
+	 */
+	int isolate;
+
+	/**
+	 * cts_protect - Whether CTS protection is enabled
+	 */
+	int cts_protect;
+
+	/**
+	 * preamble - Whether short preamble is enabled
+	 */
+	int preamble;
+
+	/**
+	 * short_slot_time - Whether short slot time is enabled
+	 *
+	 * 0 = short slot time disable, 1 = short slot time enabled, -1 = do
+	 * not set (e.g., when 802.11g mode is not in use)
+	 */
+	int short_slot_time;
+
+	/**
+	 * ht_opmode - HT operation mode or -1 if HT not in use
+	 */
+	int ht_opmode;
+
+	/**
+	 * interworking - Whether Interworking is enabled
+	 */
+	int interworking;
+
+	/**
+	 * hessid - Homogeneous ESS identifier or %NULL if not set
+	 */
+	const u8 *hessid;
+
+	/**
+	 * access_network_type - Access Network Type (0..15)
+	 *
+	 * This is used for filtering Probe Request frames when Interworking is
+	 * enabled.
+	 */
+	u8 access_network_type;
+
+	/**
+	 * ap_max_inactivity - Timeout in seconds to detect STA's inactivity
+	 *
+	 * This is used by driver which advertises this capability.
+	 */
+	int ap_max_inactivity;
+
+	/**
+	 * ctwindow - Client Traffic Window (in TUs)
+	 */
+	u8 p2p_go_ctwindow;
+
+	/**
+	 * smps_mode - SMPS mode
+	 *
+	 * SMPS mode to be used by the AP, specified as the relevant bits of
+	 * ht_capab (i.e. HT_CAP_INFO_SMPS_*).
+	 */
+	unsigned int smps_mode;
+
+	/**
+	 * 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;
+
+	/**
+	 * reenable - Whether this is to re-enable beaconing
+	 */
+	int reenable;
+};
+
+struct wpa_driver_mesh_bss_params {
+#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS	0x00000001
+	/*
+	 * TODO: Other mesh configuration parameters would go here.
+	 * See NL80211_MESHCONF_* for all the mesh config parameters.
+	 */
+	unsigned int flags;
+	int peer_link_timeout;
+};
+
+struct wpa_driver_mesh_join_params {
+	const u8 *meshid;
+	int meshid_len;
+	const int *basic_rates;
+	const u8 *ies;
+	int ie_len;
+	struct hostapd_freq_params freq;
+	int beacon_int;
+	int max_peer_links;
+	struct wpa_driver_mesh_bss_params conf;
+#define WPA_DRIVER_MESH_FLAG_USER_MPM	0x00000001
+#define WPA_DRIVER_MESH_FLAG_DRIVER_MPM	0x00000002
+#define WPA_DRIVER_MESH_FLAG_SAE_AUTH	0x00000004
+#define WPA_DRIVER_MESH_FLAG_AMPE	0x00000008
+	unsigned int flags;
+};
+
+/**
+ * struct wpa_driver_capa - Driver capability information
+ */
+struct wpa_driver_capa {
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA		0x00000001
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2		0x00000002
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK	0x00000004
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK	0x00000008
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE	0x00000010
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT		0x00000020
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK		0x00000040
+#define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK	0x00000080
+#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B	0x00000100
+#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192	0x00000200
+	/** Bitfield of supported key management suites */
+	unsigned int key_mgmt;
+
+#define WPA_DRIVER_CAPA_ENC_WEP40	0x00000001
+#define WPA_DRIVER_CAPA_ENC_WEP104	0x00000002
+#define WPA_DRIVER_CAPA_ENC_TKIP	0x00000004
+#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
+	/** Bitfield of supported cipher suites */
+	unsigned int enc;
+
+#define WPA_DRIVER_AUTH_OPEN		0x00000001
+#define WPA_DRIVER_AUTH_SHARED		0x00000002
+#define WPA_DRIVER_AUTH_LEAP		0x00000004
+	/** Bitfield of supported IEEE 802.11 authentication algorithms */
+	unsigned int auth;
+
+/** Driver generated WPA/RSN IE */
+#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
+/** 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
+/** Driver is for a wired Ethernet interface */
+#define WPA_DRIVER_FLAGS_WIRED		0x00000010
+/** Driver provides separate commands for authentication and association (SME in
+ * wpa_supplicant). */
+#define WPA_DRIVER_FLAGS_SME		0x00000020
+/** Driver supports AP mode */
+#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 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
+/**
+ * Driver uses the initial interface as a dedicated management interface, i.e.,
+ * it cannot be used for P2P group operations or non-P2P purposes.
+ */
+#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
+/** 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
+ * P2P group operations.
+ */
+#define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P		0x00002000
+/**
+ * Driver is known to use sane error codes, i.e., when it indicates that
+ * something (e.g., association) fails, there was indeed a failure and the
+ * operation does not end up getting completed successfully later.
+ */
+#define WPA_DRIVER_FLAGS_SANE_ERROR_CODES		0x00004000
+/** Driver supports off-channel TX */
+#define WPA_DRIVER_FLAGS_OFFCHANNEL_TX			0x00008000
+/** Driver indicates TX status events for EAPOL Data frames */
+#define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS		0x00010000
+/** Driver indicates TX status events for Deauth/Disassoc frames */
+#define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS		0x00020000
+/** Driver supports roaming (BSS selection) in firmware */
+#define WPA_DRIVER_FLAGS_BSS_SELECTION			0x00040000
+/** Driver supports operating as a TDLS peer */
+#define WPA_DRIVER_FLAGS_TDLS_SUPPORT			0x00080000
+/** Driver requires external TDLS setup/teardown/discovery */
+#define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP		0x00100000
+/** Driver indicates support for Probe Response offloading in AP mode */
+#define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD		0x00200000
+/** Driver supports U-APSD in AP mode */
+#define WPA_DRIVER_FLAGS_AP_UAPSD			0x00400000
+/** Driver supports inactivity timer in AP mode */
+#define WPA_DRIVER_FLAGS_INACTIVITY_TIMER		0x00800000
+/** Driver expects user space implementation of MLME in AP mode */
+#define WPA_DRIVER_FLAGS_AP_MLME			0x01000000
+/** Driver supports SAE with user space SME */
+#define WPA_DRIVER_FLAGS_SAE				0x02000000
+/** Driver makes use of OBSS scan mechanism in wpa_supplicant */
+#define WPA_DRIVER_FLAGS_OBSS_SCAN			0x04000000
+/** Driver supports IBSS (Ad-hoc) mode */
+#define WPA_DRIVER_FLAGS_IBSS				0x08000000
+/** Driver supports radar detection */
+#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
+/** Driver supports mesh */
+#define WPA_DRIVER_FLAGS_MESH			0x0000000100000000ULL
+/** Driver support ACS offload */
+#define WPA_DRIVER_FLAGS_ACS_OFFLOAD		0x0000000200000000ULL
+/** Driver supports key management offload */
+#define WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD	0x0000000400000000ULL
+/** Driver supports TDLS channel switching */
+#define WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH	0x0000000800000000ULL
+/** Driver supports IBSS with HT datarates */
+#define WPA_DRIVER_FLAGS_HT_IBSS		0x0000001000000000ULL
+/** Driver supports IBSS with VHT datarates */
+#define WPA_DRIVER_FLAGS_VHT_IBSS		0x0000002000000000ULL
+/** Driver supports automatic band selection */
+#define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY	0x0000004000000000ULL
+	u64 flags;
+
+#define WPA_DRIVER_SMPS_MODE_STATIC			0x00000001
+#define WPA_DRIVER_SMPS_MODE_DYNAMIC			0x00000002
+	unsigned int smps_modes;
+
+	unsigned int wmm_ac_supported:1;
+
+	unsigned int mac_addr_rand_scan_supported:1;
+	unsigned int mac_addr_rand_sched_scan_supported:1;
+
+	/** Maximum number of supported active probe SSIDs */
+	int max_scan_ssids;
+
+	/** Maximum number of supported active probe SSIDs for sched_scan */
+	int max_sched_scan_ssids;
+
+	/** Whether sched_scan (offloaded scanning) is supported */
+	int sched_scan_supported;
+
+	/** Maximum number of supported match sets for sched_scan */
+	int max_match_sets;
+
+	/**
+	 * max_remain_on_chan - Maximum remain-on-channel duration in msec
+	 */
+	unsigned int max_remain_on_chan;
+
+	/**
+	 * max_stations - Maximum number of associated stations the driver
+	 * supports in AP mode
+	 */
+	unsigned int max_stations;
+
+	/**
+	 * probe_resp_offloads - Bitmap of supported protocols by the driver
+	 * for Probe Response offloading.
+	 */
+/** Driver Probe Response offloading support for WPS ver. 1 */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS		0x00000001
+/** Driver Probe Response offloading support for WPS ver. 2 */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2		0x00000002
+/** Driver Probe Response offloading support for P2P */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P		0x00000004
+/** Driver Probe Response offloading support for IEEE 802.11u (Interworking) */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING	0x00000008
+	unsigned int probe_resp_offloads;
+
+	unsigned int max_acl_mac_addrs;
+
+	/**
+	 * Number of supported concurrent channels
+	 */
+	unsigned int num_multichan_concurrent;
+
+	/**
+	 * extended_capa - extended capabilities in driver/device
+	 *
+	 * Must be allocated and freed by driver and the pointers must be
+	 * valid for the lifetime of the driver, i.e., freed in deinit()
+	 */
+	const u8 *extended_capa, *extended_capa_mask;
+	unsigned int extended_capa_len;
+
+	struct wowlan_triggers wowlan_triggers;
+
+/** Driver adds the DS Params Set IE in Probe Request frames */
+#define WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES	0x00000001
+/** Driver adds the WFA TPC IE in Probe Request frames */
+#define WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES		0x00000002
+/** Driver handles quiet period requests */
+#define WPA_DRIVER_FLAGS_QUIET				0x00000004
+/**
+ * Driver is capable of inserting the current TX power value into the body of
+ * transmitted frames.
+ * Background: Some Action frames include a TPC Report IE. This IE contains a
+ * TX power field, which has to be updated by lower layers. One such Action
+ * frame is Link Measurement Report (part of RRM). Another is TPC Report (part
+ * of spectrum management). Note that this insertion takes place at a fixed
+ * offset, namely the 6th byte in the Action frame body.
+ */
+#define WPA_DRIVER_FLAGS_TX_POWER_INSERTION		0x00000008
+	u32 rrm_flags;
+
+	/* Driver concurrency capabilities */
+	unsigned int conc_capab;
+	/* Maximum number of concurrent channels on 2.4 GHz */
+	unsigned int max_conc_chan_2_4;
+	/* Maximum number of concurrent channels on 5 GHz */
+	unsigned int max_conc_chan_5_0;
+};
+
+
+struct hostapd_data;
+
+struct hostap_sta_driver_data {
+	unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes;
+	unsigned long current_tx_rate;
+	unsigned long inactive_msec;
+	unsigned long flags;
+	unsigned long num_ps_buf_frames;
+	unsigned long tx_retry_failed;
+	unsigned long tx_retry_count;
+	int last_rssi;
+	int last_ack_rssi;
+};
+
+struct hostapd_sta_add_params {
+	const u8 *addr;
+	u16 aid;
+	u16 capability;
+	const u8 *supp_rates;
+	size_t supp_rates_len;
+	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 */
+	u32 flags_mask; /* unset bits in flags */
+#ifdef CONFIG_MESH
+	enum mesh_plink_state plink_state;
+#endif /* CONFIG_MESH */
+	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 mac_address {
+	u8 addr[ETH_ALEN];
+};
+
+struct hostapd_acl_params {
+	u8 acl_policy;
+	unsigned int num_mac_acl;
+	struct mac_address mac_acl[0];
+};
+
+enum wpa_driver_if_type {
+	/**
+	 * WPA_IF_STATION - Station mode interface
+	 */
+	WPA_IF_STATION,
+
+	/**
+	 * WPA_IF_AP_VLAN - AP mode VLAN interface
+	 *
+	 * This interface shares its address and Beacon frame with the main
+	 * BSS.
+	 */
+	WPA_IF_AP_VLAN,
+
+	/**
+	 * WPA_IF_AP_BSS - AP mode BSS interface
+	 *
+	 * This interface has its own address and Beacon frame.
+	 */
+	WPA_IF_AP_BSS,
+
+	/**
+	 * WPA_IF_P2P_GO - P2P Group Owner
+	 */
+	WPA_IF_P2P_GO,
+
+	/**
+	 * WPA_IF_P2P_CLIENT - P2P Client
+	 */
+	WPA_IF_P2P_CLIENT,
+
+	/**
+	 * WPA_IF_P2P_GROUP - P2P Group interface (will become either
+	 * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known)
+	 */
+	WPA_IF_P2P_GROUP,
+
+	/**
+	 * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the
+	 * abstracted P2P Device function in the driver
+	 */
+	WPA_IF_P2P_DEVICE,
+
+	/*
+	 * WPA_IF_MESH - Mesh interface
+	 */
+	WPA_IF_MESH,
+
+	/*
+	 * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
+	 */
+	WPA_IF_TDLS,
+
+	/*
+	 * WPA_IF_IBSS - IBSS interface (used for pref freq only)
+	 */
+	WPA_IF_IBSS,
+};
+
+struct wpa_init_params {
+	void *global_priv;
+	const u8 *bssid;
+	const char *ifname;
+	const char *driver_params;
+	int use_pae_group_addr;
+	char **bridge;
+	size_t num_bridge;
+
+	u8 *own_addr; /* buffer for writing own MAC address */
+};
+
+
+struct wpa_bss_params {
+	/** Interface name (for multi-SSID/VLAN support) */
+	const char *ifname;
+	/** Whether IEEE 802.1X or WPA/WPA2 is enabled */
+	int enabled;
+
+	int wpa;
+	int ieee802_1x;
+	int wpa_group;
+	int wpa_pairwise;
+	int wpa_key_mgmt;
+	int rsn_preauth;
+	enum mfp_options ieee80211w;
+};
+
+#define WPA_STA_AUTHORIZED BIT(0)
+#define WPA_STA_WMM BIT(1)
+#define WPA_STA_SHORT_PREAMBLE BIT(2)
+#define WPA_STA_MFP BIT(3)
+#define WPA_STA_TDLS_PEER BIT(4)
+#define WPA_STA_AUTHENTICATED BIT(5)
+
+enum tdls_oper {
+	TDLS_DISCOVERY_REQ,
+	TDLS_SETUP,
+	TDLS_TEARDOWN,
+	TDLS_ENABLE_LINK,
+	TDLS_DISABLE_LINK,
+	TDLS_ENABLE,
+	TDLS_DISABLE
+};
+
+enum wnm_oper {
+	WNM_SLEEP_ENTER_CONFIRM,
+	WNM_SLEEP_ENTER_FAIL,
+	WNM_SLEEP_EXIT_CONFIRM,
+	WNM_SLEEP_EXIT_FAIL,
+	WNM_SLEEP_TFS_REQ_IE_ADD,   /* STA requests driver to add TFS req IE */
+	WNM_SLEEP_TFS_REQ_IE_NONE,  /* STA requests empty TFS req IE */
+	WNM_SLEEP_TFS_REQ_IE_SET,   /* AP requests driver to set TFS req IE for
+				     * a STA */
+	WNM_SLEEP_TFS_RESP_IE_ADD,  /* AP requests driver to add TFS resp IE
+				     * for a STA */
+	WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */
+	WNM_SLEEP_TFS_RESP_IE_SET,  /* AP requests driver to set TFS resp IE
+				     * for a STA */
+	WNM_SLEEP_TFS_IE_DEL        /* AP delete the TFS IE */
+};
+
+/* enum chan_width - Channel width definitions */
+enum chan_width {
+	CHAN_WIDTH_20_NOHT,
+	CHAN_WIDTH_20,
+	CHAN_WIDTH_40,
+	CHAN_WIDTH_80,
+	CHAN_WIDTH_80P80,
+	CHAN_WIDTH_160,
+	CHAN_WIDTH_UNKNOWN
+};
+
+/**
+ * struct wpa_signal_info - Information about channel signal quality
+ */
+struct wpa_signal_info {
+	u32 frequency;
+	int above_threshold;
+	int current_signal;
+	int avg_signal;
+	int avg_beacon_signal;
+	int current_noise;
+	int current_txrate;
+	enum chan_width chanwidth;
+	int center_frq1;
+	int center_frq2;
+};
+
+/**
+ * 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),
+};
+
+/* valid info in the wmm_params struct */
+enum wmm_params_valid_info {
+	WMM_PARAMS_UAPSD_QUEUES_INFO = BIT(0),
+};
+
+/**
+ * struct wmm_params - WMM parameterss configured for this association
+ * @info_bitmap: Bitmap of valid wmm_params info; indicates what fields
+ *	of the struct contain valid information.
+ * @uapsd_queues: Bitmap of ACs configured for uapsd (valid only if
+ *	%WMM_PARAMS_UAPSD_QUEUES_INFO is set)
+ */
+struct wmm_params {
+	u8 info_bitmap;
+	u8 uapsd_queues;
+};
+
+#ifdef CONFIG_MACSEC
+struct macsec_init_params {
+	Boolean always_include_sci;
+	Boolean use_es;
+	Boolean use_scb;
+};
+#endif /* CONFIG_MACSEC */
+
+enum drv_br_port_attr {
+	DRV_BR_PORT_ATTR_PROXYARP,
+	DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+};
+
+enum drv_br_net_param {
+	DRV_BR_NET_PARAM_GARP_ACCEPT,
+	DRV_BR_MULTICAST_SNOOPING,
+};
+
+struct drv_acs_params {
+	/* Selected mode (HOSTAPD_MODE_*) */
+	enum hostapd_hw_mode hw_mode;
+
+	/* Indicates whether HT is enabled */
+	int ht_enabled;
+
+	/* Indicates whether HT40 is enabled */
+	int ht40_enabled;
+
+	/* Indicates whether VHT is enabled */
+	int vht_enabled;
+
+	/* Configured ACS channel width */
+	u16 ch_width;
+
+	/* ACS channel list info */
+	unsigned int ch_list_len;
+	const u8 *ch_list;
+	const int *freq_list;
+};
+
+
+/**
+ * struct wpa_driver_ops - Driver interface API definition
+ *
+ * This structure defines the API that each driver interface needs to implement
+ * for core wpa_supplicant code. All driver specific functionality is captured
+ * in this wrapper.
+ */
+struct wpa_driver_ops {
+	/** Name of the driver interface */
+	const char *name;
+	/** One line description of the driver interface */
+	const char *desc;
+
+	/**
+	 * get_bssid - Get the current BSSID
+	 * @priv: private driver interface data
+	 * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes)
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Query kernel driver for the current BSSID and copy it to bssid.
+	 * Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not
+	 * associated.
+	 */
+	int (*get_bssid)(void *priv, u8 *bssid);
+
+	/**
+	 * get_ssid - Get the current SSID
+	 * @priv: private driver interface data
+	 * @ssid: buffer for SSID (at least 32 bytes)
+	 *
+	 * Returns: Length of the SSID on success, -1 on failure
+	 *
+	 * Query kernel driver for the current SSID and copy it to ssid.
+	 * Returning zero is recommended if the STA is not associated.
+	 *
+	 * Note: SSID is an array of octets, i.e., it is not nul terminated and
+	 * can, at least in theory, contain control characters (including nul)
+	 * and as such, should be processed as binary data, not a printable
+	 * string.
+	 */
+	int (*get_ssid)(void *priv, u8 *ssid);
+
+	/**
+	 * set_key - Configure encryption key
+	 * @ifname: Interface name (for multi-SSID/VLAN support)
+	 * @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_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
+	 *	broadcast keys, %NULL for default keys that are used both for
+	 *	broadcast and unicast; when clearing keys, %NULL is used to
+	 *	indicate that both the broadcast-only and default key of the
+	 *	specified key index is to be cleared
+	 * @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for
+	 *	IGTK
+	 * @set_tx: configure this key as the default Tx key (only used when
+	 *	driver does not support separate unicast/individual key
+	 * @seq: sequence number/packet number, seq_len octets, the next
+	 *	packet number to be used for in replay protection; configured
+	 *	for Rx keys (in most cases, this is only used with broadcast
+	 *	keys and set to zero for unicast keys); %NULL if not set
+	 * @seq_len: length of the seq, depends on the algorithm:
+	 *	TKIP: 6 octets, CCMP/GCMP: 6 octets, IGTK: 6 octets
+	 * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
+	 *	8-byte Rx Mic Key
+	 * @key_len: length of the key buffer in octets (WEP: 5 or 13,
+	 *	TKIP: 32, CCMP/GCMP: 16, IGTK: 16)
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Configure the given key for the kernel driver. If the driver
+	 * supports separate individual keys (4 default keys + 1 individual),
+	 * addr can be used to determine whether the key is default or
+	 * individual. If only 4 keys are supported, the default key with key
+	 * index 0 is used as the individual key. STA must be configured to use
+	 * it as the default Tx key (set_tx is set) and accept Rx for all the
+	 * key indexes. In most cases, WPA uses only key indexes 1 and 2 for
+	 * broadcast keys, so key index 0 is available for this kind of
+	 * configuration.
+	 *
+	 * Please note that TKIP keys include separate TX and RX MIC keys and
+	 * some drivers may expect them in different order than wpa_supplicant
+	 * is using. If the TX/RX keys are swapped, all TKIP encrypted packets
+	 * will trigger Michael MIC errors. This can be fixed by changing the
+	 * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key
+	 * in driver_*.c set_key() implementation, see driver_ndis.c for an
+	 * example on how this can be done.
+	 */
+	int (*set_key)(const char *ifname, void *priv, 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);
+
+	/**
+	 * init - Initialize driver interface
+	 * @ctx: context to be used when calling wpa_supplicant functions,
+	 * e.g., wpa_supplicant_event()
+	 * @ifname: interface name, e.g., wlan0
+	 *
+	 * Returns: Pointer to private data, %NULL on failure
+	 *
+	 * Initialize driver interface, including event processing for kernel
+	 * driver events (e.g., associated, scan results, Michael MIC failure).
+	 * This function can allocate a private configuration data area for
+	 * @ctx, file descriptor, interface name, etc. information that may be
+	 * needed in future driver operations. If this is not used, non-NULL
+	 * value will need to be returned because %NULL is used to indicate
+	 * failure. The returned value will be used as 'void *priv' data for
+	 * all other driver_ops functions.
+	 *
+	 * The main event loop (eloop.c) of wpa_supplicant can be used to
+	 * register callback for read sockets (eloop_register_read_sock()).
+	 *
+	 * See below for more information about events and
+	 * wpa_supplicant_event() function.
+	 */
+	void * (*init)(void *ctx, const char *ifname);
+
+	/**
+	 * deinit - Deinitialize driver interface
+	 * @priv: private driver interface data from init()
+	 *
+	 * Shut down driver interface and processing of driver events. Free
+	 * private data buffer if one was allocated in init() handler.
+	 */
+	void (*deinit)(void *priv);
+
+	/**
+	 * set_param - Set driver configuration parameters
+	 * @priv: private driver interface data from init()
+	 * @param: driver specific configuration parameters
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Optional handler for notifying driver interface about configuration
+	 * parameters (driver_param).
+	 */
+	int (*set_param)(void *priv, const char *param);
+
+	/**
+	 * set_countermeasures - Enable/disable TKIP countermeasures
+	 * @priv: private driver interface data
+	 * @enabled: 1 = countermeasures enabled, 0 = disabled
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Configure TKIP countermeasures. When these are enabled, the driver
+	 * should drop all received and queued frames that are using TKIP.
+	 */
+	int (*set_countermeasures)(void *priv, int enabled);
+
+	/**
+	 * deauthenticate - Request driver to deauthenticate
+	 * @priv: private driver interface data
+	 * @addr: peer address (BSSID of the AP)
+	 * @reason_code: 16-bit reason code to be sent in the deauthentication
+	 *	frame
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*deauthenticate)(void *priv, const u8 *addr, int reason_code);
+
+	/**
+	 * associate - Request driver to associate
+	 * @priv: private driver interface data
+	 * @params: association parameters
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*associate)(void *priv,
+			 struct wpa_driver_associate_params *params);
+
+	/**
+	 * add_pmkid - Add PMKSA cache entry to the driver
+	 * @priv: private driver interface data
+	 * @bssid: BSSID for the PMKSA cache entry
+	 * @pmkid: PMKID for the PMKSA cache entry
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is called when a new PMK is received, as a result of
+	 * either normal authentication or RSN pre-authentication.
+	 *
+	 * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+	 * associate(), add_pmkid() can be used to add new PMKSA cache entries
+	 * in the driver. If the driver uses wpa_ie from wpa_supplicant, this
+	 * driver_ops function does not need to be implemented. Likewise, if
+	 * the driver does not support WPA, this function is not needed.
+	 */
+	int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+
+	/**
+	 * remove_pmkid - Remove PMKSA cache entry to the driver
+	 * @priv: private driver interface data
+	 * @bssid: BSSID for the PMKSA cache entry
+	 * @pmkid: PMKID for the PMKSA cache entry
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is called when the supplicant drops a PMKSA cache
+	 * entry for any reason.
+	 *
+	 * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+	 * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+	 * between the driver and wpa_supplicant. If the driver uses wpa_ie
+	 * from wpa_supplicant, this driver_ops function does not need to be
+	 * implemented. Likewise, if the driver does not support WPA, this
+	 * function is not needed.
+	 */
+	int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+
+	/**
+	 * flush_pmkid - Flush PMKSA cache
+	 * @priv: private driver interface data
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is called when the supplicant drops all PMKSA cache
+	 * entries for any reason.
+	 *
+	 * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+	 * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+	 * between the driver and wpa_supplicant. If the driver uses wpa_ie
+	 * from wpa_supplicant, this driver_ops function does not need to be
+	 * implemented. Likewise, if the driver does not support WPA, this
+	 * function is not needed.
+	 */
+	int (*flush_pmkid)(void *priv);
+
+	/**
+	 * get_capa - Get driver capabilities
+	 * @priv: private driver interface data
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Get driver/firmware/hardware capabilities.
+	 */
+	int (*get_capa)(void *priv, struct wpa_driver_capa *capa);
+
+	/**
+	 * poll - Poll driver for association information
+	 * @priv: private driver interface data
+	 *
+	 * This is an option callback that can be used when the driver does not
+	 * provide event mechanism for association events. This is called when
+	 * receiving WPA EAPOL-Key messages that require association
+	 * information. The driver interface is supposed to generate associnfo
+	 * event before returning from this callback function. In addition, the
+	 * driver interface should generate an association event after having
+	 * sent out associnfo.
+	 */
+	void (*poll)(void *priv);
+
+	/**
+	 * get_ifname - Get interface name
+	 * @priv: private driver interface data
+	 *
+	 * Returns: Pointer to the interface name. This can differ from the
+	 * interface name used in init() call. Init() is called first.
+	 *
+	 * This optional function can be used to allow the driver interface to
+	 * replace the interface name with something else, e.g., based on an
+	 * interface mapping from a more descriptive name.
+	 */
+	const char * (*get_ifname)(void *priv);
+
+	/**
+	 * get_mac_addr - Get own MAC address
+	 * @priv: private driver interface data
+	 *
+	 * Returns: Pointer to own MAC address or %NULL on failure
+	 *
+	 * This optional function can be used to get the own MAC address of the
+	 * device from the driver interface code. This is only needed if the
+	 * l2_packet implementation for the OS does not provide easy access to
+	 * a MAC address. */
+	const u8 * (*get_mac_addr)(void *priv);
+
+	/**
+	 * set_operstate - Sets device operating state to DORMANT or UP
+	 * @priv: private driver interface data
+	 * @state: 0 = dormant, 1 = up
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function that can be used on operating systems
+	 * that support a concept of controlling network device state from user
+	 * space applications. This function, if set, gets called with
+	 * state = 1 when authentication has been completed and with state = 0
+	 * when connection is lost.
+	 */
+	int (*set_operstate)(void *priv, int state);
+
+	/**
+	 * mlme_setprotection - MLME-SETPROTECTION.request primitive
+	 * @priv: Private driver interface data
+	 * @addr: Address of the station for which to set protection (may be
+	 * %NULL for group keys)
+	 * @protect_type: MLME_SETPROTECTION_PROTECT_TYPE_*
+	 * @key_type: MLME_SETPROTECTION_KEY_TYPE_*
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function that can be used to set the driver to
+	 * require protection for Tx and/or Rx frames. This uses the layer
+	 * interface defined in IEEE 802.11i-2004 clause 10.3.22.1
+	 * (MLME-SETPROTECTION.request). Many drivers do not use explicit
+	 * set protection operation; instead, they set protection implicitly
+	 * based on configured keys.
+	 */
+	int (*mlme_setprotection)(void *priv, const u8 *addr, int protect_type,
+				  int key_type);
+
+	/**
+	 * get_hw_feature_data - Get hardware support data (channels and rates)
+	 * @priv: Private driver interface data
+	 * @num_modes: Variable for returning the number of returned modes
+	 * flags: Variable for returning hardware feature flags
+	 * Returns: Pointer to allocated hardware data on success or %NULL on
+	 * failure. Caller is responsible for freeing this.
+	 */
+	struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
+							 u16 *num_modes,
+							 u16 *flags);
+
+	/**
+	 * send_mlme - Send management frame from MLME
+	 * @priv: Private driver interface data
+	 * @data: IEEE 802.11 management frame with IEEE 802.11 header
+	 * @data_len: Size of the management frame
+	 * @noack: Do not wait for this frame to be acked (disable retries)
+	 * @freq: Frequency (in MHz) to send the frame on, or 0 to let the
+	 * driver decide
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
+			 int noack, unsigned int freq);
+
+	/**
+	 * update_ft_ies - Update FT (IEEE 802.11r) IEs
+	 * @priv: Private driver interface data
+	 * @md: Mobility domain (2 octets) (also included inside ies)
+	 * @ies: FT IEs (MDIE, FTIE, ...) or %NULL to remove IEs
+	 * @ies_len: Length of FT IEs in bytes
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * The supplicant uses this callback to let the driver know that keying
+	 * material for FT is available and that the driver can use the
+	 * provided IEs in the next message in FT authentication sequence.
+	 *
+	 * This function is only needed for driver that support IEEE 802.11r
+	 * (Fast BSS Transition).
+	 */
+	int (*update_ft_ies)(void *priv, const u8 *md, const u8 *ies,
+			     size_t ies_len);
+
+	/**
+	 * get_scan_results2 - Fetch the latest scan results
+	 * @priv: private driver interface data
+	 *
+	 * Returns: Allocated buffer of scan results (caller is responsible for
+	 * freeing the data structure) on success, NULL on failure
+	 */
+	 struct wpa_scan_results * (*get_scan_results2)(void *priv);
+
+	/**
+	 * set_country - Set country
+	 * @priv: Private driver interface data
+	 * @alpha2: country to which to switch to
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is for drivers which support some form
+	 * of setting a regulatory domain.
+	 */
+	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
+	 *
+	 * This optional function is called to initialize the driver wrapper
+	 * for global data, i.e., data that applies to all interfaces. If this
+	 * function is implemented, global_deinit() will also need to be
+	 * implemented to free the private data. The driver will also likely
+	 * use init2() function instead of init() to get the pointer to global
+	 * data available to per-interface initializer.
+	 */
+	void * (*global_init)(void);
+
+	/**
+	 * global_deinit - Global driver deinitialization
+	 * @priv: private driver global data from global_init()
+	 *
+	 * Terminate any global driver related functionality and free the
+	 * global data structure.
+	 */
+	void (*global_deinit)(void *priv);
+
+	/**
+	 * init2 - Initialize driver interface (with global data)
+	 * @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
+	 *
+	 * This function can be used instead of init() if the driver wrapper
+	 * uses global data.
+	 */
+	void * (*init2)(void *ctx, const char *ifname, void *global_priv);
+
+	/**
+	 * get_interfaces - Get information about available interfaces
+	 * @global_priv: private driver global data from global_init()
+	 * Returns: Allocated buffer of interface information (caller is
+	 * responsible for freeing the data structure) on success, NULL on
+	 * failure
+	 */
+	struct wpa_interface_info * (*get_interfaces)(void *global_priv);
+
+	/**
+	 * scan2 - Request the driver to initiate scan
+	 * @priv: private driver interface data
+	 * @params: Scan parameters
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Once the scan results are ready, the driver should report scan
+	 * results event for wpa_supplicant which will eventually request the
+	 * results with wpa_driver_get_scan_results2().
+	 */
+	int (*scan2)(void *priv, struct wpa_driver_scan_params *params);
+
+	/**
+	 * authenticate - Request driver to authenticate
+	 * @priv: private driver interface data
+	 * @params: authentication parameters
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function that can be used with drivers that
+	 * support separate authentication and association steps, i.e., when
+	 * wpa_supplicant can act as the SME. If not implemented, associate()
+	 * function is expected to take care of IEEE 802.11 authentication,
+	 * too.
+	 */
+	int (*authenticate)(void *priv,
+			    struct wpa_driver_auth_params *params);
+
+	/**
+	 * set_ap - Set Beacon and Probe Response information for AP mode
+	 * @priv: Private driver interface data
+	 * @params: Parameters to use in AP mode
+	 *
+	 * This function is used to configure Beacon template and/or extra IEs
+	 * to add for Beacon and Probe Response frames for the driver in
+	 * AP mode. The driver is responsible for building the full Beacon
+	 * frame by concatenating the head part with TIM IE generated by the
+	 * driver/firmware and finishing with the tail part. Depending on the
+	 * driver architectue, this can be done either by using the full
+	 * template or the set of additional IEs (e.g., WPS and P2P IE).
+	 * Similarly, Probe Response processing depends on the driver design.
+	 * If the driver (or firmware) takes care of replying to Probe Request
+	 * frames, the extra IEs provided here needs to be added to the Probe
+	 * Response frames.
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_ap)(void *priv, struct wpa_driver_ap_params *params);
+
+	/**
+	 * set_acl - Set ACL in AP mode
+	 * @priv: Private driver interface data
+	 * @params: Parameters to configure ACL
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is used only for the drivers which support MAC address ACL.
+	 */
+	int (*set_acl)(void *priv, struct hostapd_acl_params *params);
+
+	/**
+	 * hapd_init - Initialize driver interface (hostapd only)
+	 * @hapd: Pointer to hostapd context
+	 * @params: Configuration for the driver wrapper
+	 * Returns: Pointer to private data, %NULL on failure
+	 *
+	 * This function is used instead of init() or init2() when the driver
+	 * wrapper is used with hostapd.
+	 */
+	void * (*hapd_init)(struct hostapd_data *hapd,
+			    struct wpa_init_params *params);
+
+	/**
+	 * hapd_deinit - Deinitialize driver interface (hostapd only)
+	 * @priv: Private driver interface data from hapd_init()
+	 */
+	void (*hapd_deinit)(void *priv);
+
+	/**
+	 * set_ieee8021x - Enable/disable IEEE 802.1X support (AP only)
+	 * @priv: Private driver interface data
+	 * @params: BSS parameters
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function to configure the kernel driver to
+	 * enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This
+	 * can be left undefined (set to %NULL) if IEEE 802.1X support is
+	 * always enabled and the driver uses set_ap() to set WPA/RSN IE
+	 * for Beacon frames.
+	 *
+	 * DEPRECATED - use set_ap() instead
+	 */
+	int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params);
+
+	/**
+	 * set_privacy - Enable/disable privacy (AP only)
+	 * @priv: Private driver interface data
+	 * @enabled: 1 = privacy enabled, 0 = disabled
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function to configure privacy field in the
+	 * kernel driver for Beacon frames. This can be left undefined (set to
+	 * %NULL) if the driver uses the Beacon template from set_ap().
+	 *
+	 * DEPRECATED - use set_ap() instead
+	 */
+	int (*set_privacy)(void *priv, int enabled);
+
+	/**
+	 * get_seqnum - Fetch the current TSC/packet number (AP only)
+	 * @ifname: The interface name (main or virtual)
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the station or %NULL for group keys
+	 * @idx: Key index
+	 * @seq: Buffer for returning the latest used TSC/packet number
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is used to fetch the last used TSC/packet number for
+	 * a TKIP, CCMP, GCMP, or BIP/IGTK key. It is mainly used with group
+	 * keys, so there is no strict requirement on implementing support for
+	 * unicast keys (i.e., addr != %NULL).
+	 */
+	int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr,
+			  int idx, u8 *seq);
+
+	/**
+	 * flush - Flush all association stations (AP only)
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function requests the driver to disassociate all associated
+	 * stations. This function does not need to be implemented if the
+	 * driver does not process association frames internally.
+	 */
+	int (*flush)(void *priv);
+
+	/**
+	 * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP)
+	 * @priv: Private driver interface data
+	 * @elem: Information elements
+	 * @elem_len: Length of the elem buffer in octets
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function to add information elements in the
+	 * kernel driver for Beacon and Probe Response frames. This can be left
+	 * undefined (set to %NULL) if the driver uses the Beacon template from
+	 * set_ap().
+	 *
+	 * DEPRECATED - use set_ap() instead
+	 */
+	int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len);
+
+	/**
+	 * read_sta_data - Fetch station data
+	 * @priv: Private driver interface data
+	 * @data: Buffer for returning station information
+	 * @addr: MAC address of the station
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data,
+			     const u8 *addr);
+
+	/**
+	 * hapd_send_eapol - Send an EAPOL packet (AP only)
+	 * @priv: private driver interface data
+	 * @addr: Destination MAC address
+	 * @data: EAPOL packet starting with IEEE 802.1X header
+	 * @data_len: Length of the EAPOL packet in octets
+	 * @encrypt: Whether the frame should be encrypted
+	 * @own_addr: Source MAC address
+	 * @flags: WPA_STA_* flags for the destination station
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data,
+			       size_t data_len, int encrypt,
+			       const u8 *own_addr, u32 flags);
+
+	/**
+	 * sta_deauth - Deauthenticate a station (AP only)
+	 * @priv: Private driver interface data
+	 * @own_addr: Source address and BSSID for the Deauthentication frame
+	 * @addr: MAC address of the station to deauthenticate
+	 * @reason: Reason code for the Deauthentiation frame
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function requests a specific station to be deauthenticated and
+	 * a Deauthentication frame to be sent to it.
+	 */
+	int (*sta_deauth)(void *priv, const u8 *own_addr, const u8 *addr,
+			  int reason);
+
+	/**
+	 * sta_disassoc - Disassociate a station (AP only)
+	 * @priv: Private driver interface data
+	 * @own_addr: Source address and BSSID for the Disassociation frame
+	 * @addr: MAC address of the station to disassociate
+	 * @reason: Reason code for the Disassociation frame
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function requests a specific station to be disassociated and
+	 * a Disassociation frame to be sent to it.
+	 */
+	int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr,
+			    int reason);
+
+	/**
+	 * sta_remove - Remove a station entry (AP only)
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the station to be removed
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*sta_remove)(void *priv, const u8 *addr);
+
+	/**
+	 * hapd_get_ssid - Get the current SSID (AP only)
+	 * @priv: Private driver interface data
+	 * @buf: Buffer for returning the SSID
+	 * @len: Maximum length of the buffer
+	 * Returns: Length of the SSID on success, -1 on failure
+	 *
+	 * This function need not be implemented if the driver uses Beacon
+	 * template from set_ap() and does not reply to Probe Request frames.
+	 */
+	int (*hapd_get_ssid)(void *priv, u8 *buf, int len);
+
+	/**
+	 * hapd_set_ssid - Set SSID (AP only)
+	 * @priv: Private driver interface data
+	 * @buf: SSID
+	 * @len: Length of the SSID in octets
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * DEPRECATED - use set_ap() instead
+	 */
+	int (*hapd_set_ssid)(void *priv, const u8 *buf, int len);
+
+	/**
+	 * hapd_set_countermeasures - Enable/disable TKIP countermeasures (AP)
+	 * @priv: Private driver interface data
+	 * @enabled: 1 = countermeasures enabled, 0 = disabled
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This need not be implemented if the driver does not take care of
+	 * association processing.
+	 */
+	int (*hapd_set_countermeasures)(void *priv, int enabled);
+
+	/**
+	 * sta_add - Add a station entry
+	 * @priv: Private driver interface data
+	 * @params: Station parameters
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is used to add a station entry to the driver once the
+	 * station has completed association. This is only used if the driver
+	 * does not take care of association processing.
+	 *
+	 * With TDLS, this function is also used to add or set (params->set 1)
+	 * TDLS peer entries.
+	 */
+	int (*sta_add)(void *priv, struct hostapd_sta_add_params *params);
+
+	/**
+	 * get_inact_sec - Get station inactivity duration (AP only)
+	 * @priv: Private driver interface data
+	 * @addr: Station address
+	 * Returns: Number of seconds station has been inactive, -1 on failure
+	 */
+	int (*get_inact_sec)(void *priv, const u8 *addr);
+
+	/**
+	 * sta_clear_stats - Clear station statistics (AP only)
+	 * @priv: Private driver interface data
+	 * @addr: Station address
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*sta_clear_stats)(void *priv, const u8 *addr);
+
+	/**
+	 * set_freq - Set channel/frequency (AP only)
+	 * @priv: Private driver interface data
+	 * @freq: Channel parameters
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_freq)(void *priv, struct hostapd_freq_params *freq);
+
+	/**
+	 * set_rts - Set RTS threshold
+	 * @priv: Private driver interface data
+	 * @rts: RTS threshold in octets
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_rts)(void *priv, int rts);
+
+	/**
+	 * set_frag - Set fragmentation threshold
+	 * @priv: Private driver interface data
+	 * @frag: Fragmentation threshold in octets
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_frag)(void *priv, int frag);
+
+	/**
+	 * sta_set_flags - Set station flags (AP only)
+	 * @priv: Private driver interface data
+	 * @addr: Station address
+	 * @total_flags: Bitmap of all WPA_STA_* flags currently set
+	 * @flags_or: Bitmap of WPA_STA_* flags to add
+	 * @flags_and: Bitmap of WPA_STA_* flags to us as a mask
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*sta_set_flags)(void *priv, const u8 *addr,
+			     unsigned int total_flags, unsigned int flags_or,
+			     unsigned int flags_and);
+
+	/**
+	 * set_tx_queue_params - Set TX queue parameters
+	 * @priv: Private driver interface data
+	 * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK)
+	 * @aifs: AIFS
+	 * @cw_min: cwMin
+	 * @cw_max: cwMax
+	 * @burst_time: Maximum length for bursting in 0.1 msec units
+	 */
+	int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min,
+				   int cw_max, int burst_time);
+
+	/**
+	 * if_add - Add a virtual interface
+	 * @priv: Private driver interface data
+	 * @type: Interface type
+	 * @ifname: Interface name for the new virtual interface
+	 * @addr: Local address to use for the interface or %NULL to use the
+	 *	parent interface address
+	 * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
+	 * @drv_priv: Pointer for overwriting the driver context or %NULL if
+	 *	not allowed (applies only to %WPA_IF_AP_BSS type)
+	 * @force_ifname: Buffer for returning an interface name that the
+	 *	driver ended up using if it differs from the requested ifname
+	 * @if_addr: Buffer for returning the allocated interface address
+	 *	(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, int use_existing);
+
+	/**
+	 * if_remove - Remove a virtual interface
+	 * @priv: Private driver interface data
+	 * @type: Interface type
+	 * @ifname: Interface name of the virtual interface to be removed
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*if_remove)(void *priv, enum wpa_driver_if_type type,
+			 const char *ifname);
+
+	/**
+	 * set_sta_vlan - Bind a station into a specific interface (AP only)
+	 * @priv: Private driver interface data
+	 * @ifname: Interface (main or virtual BSS or VLAN)
+	 * @addr: MAC address of the associated station
+	 * @vlan_id: VLAN ID
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is used to bind a station to a specific virtual
+	 * interface. It is only used if when virtual interfaces are supported,
+	 * e.g., to assign stations to different VLAN interfaces based on
+	 * information from a RADIUS server. This allows separate broadcast
+	 * domains to be used with a single BSS.
+	 */
+	int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname,
+			    int vlan_id);
+
+	/**
+	 * commit - Optional commit changes handler (AP only)
+	 * @priv: driver private data
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This optional handler function can be registered if the driver
+	 * interface implementation needs to commit changes (e.g., by setting
+	 * network interface up) at the end of initial configuration. If set,
+	 * this handler will be called after initial setup has been completed.
+	 */
+	int (*commit)(void *priv);
+
+	/**
+	 * send_ether - Send an ethernet packet (AP only)
+	 * @priv: private driver interface data
+	 * @dst: Destination MAC address
+	 * @src: Source MAC address
+	 * @proto: Ethertype
+	 * @data: EAPOL packet starting with IEEE 802.1X header
+	 * @data_len: Length of the EAPOL packet in octets
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*send_ether)(void *priv, const u8 *dst, const u8 *src, u16 proto,
+			  const u8 *data, size_t data_len);
+
+	/**
+	 * set_radius_acl_auth - Notification of RADIUS ACL change
+	 * @priv: Private driver interface data
+	 * @mac: MAC address of the station
+	 * @accepted: Whether the station was accepted
+	 * @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,
+				   u32 session_timeout);
+
+	/**
+	 * set_radius_acl_expire - Notification of RADIUS ACL expiration
+	 * @priv: Private driver interface data
+	 * @mac: MAC address of the station
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_radius_acl_expire)(void *priv, const u8 *mac);
+
+	/**
+	 * set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP)
+	 * @priv: Private driver interface data
+	 * @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s)
+	 * @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove
+	 *	extra IE(s)
+	 * @assocresp: WPS IE(s) for (Re)Association Response frames or %NULL
+	 *	to remove extra IE(s)
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function to add WPS IE in the kernel driver for
+	 * Beacon and Probe Response frames. This can be left undefined (set
+	 * to %NULL) if the driver uses the Beacon template from set_ap()
+	 * and does not process Probe Request frames. If the driver takes care
+	 * of (Re)Association frame processing, the assocresp buffer includes
+	 * WPS IE(s) that need to be added to (Re)Association Response frames
+	 * whenever a (Re)Association Request frame indicated use of WPS.
+	 *
+	 * This will also be used to add P2P IE(s) into Beacon/Probe Response
+	 * frames when operating as a GO. The driver is responsible for adding
+	 * timing related attributes (e.g., NoA) in addition to the IEs
+	 * included here by appending them after these buffers. This call is
+	 * also used to provide Probe Response IEs for P2P Listen state
+	 * operations for drivers that generate the Probe Response frames
+	 * internally.
+	 *
+	 * DEPRECATED - use set_ap() instead
+	 */
+	int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon,
+			     const struct wpabuf *proberesp,
+			     const struct wpabuf *assocresp);
+
+	/**
+	 * set_supp_port - Set IEEE 802.1X Supplicant Port status
+	 * @priv: Private driver interface data
+	 * @authorized: Whether the port is authorized
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_supp_port)(void *priv, int authorized);
+
+	/**
+	 * set_wds_sta - Bind a station into a 4-address WDS (AP only)
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the associated station
+	 * @aid: Association ID
+	 * @val: 1 = bind to 4-address WDS; 0 = unbind
+	 * @bridge_ifname: Bridge interface to use for the WDS station or %NULL
+	 *	to indicate that bridge is not to be used
+	 * @ifname_wds: Buffer to return the interface name for the new WDS
+	 *	station or %NULL to indicate name is not returned.
+	 * 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);
+
+	/**
+	 * send_action - Transmit an Action frame
+	 * @priv: Private driver interface data
+	 * @freq: Frequency (in MHz) of the channel
+	 * @wait: Time to wait off-channel for a response (in ms), or zero
+	 * @dst: Destination MAC address (Address 1)
+	 * @src: Source MAC address (Address 2)
+	 * @bssid: BSSID (Address 3)
+	 * @data: Frame body
+	 * @data_len: data length in octets
+	 @ @no_cck: Whether CCK rates must not be used to transmit this frame
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This command can be used to request the driver to transmit an action
+	 * frame to the specified destination.
+	 *
+	 * If the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will
+	 * be transmitted on the given channel and the device will wait for a
+	 * response on that channel for the given wait time.
+	 *
+	 * If the flag is not set, the wait time will be ignored. In this case,
+	 * if a remain-on-channel duration is in progress, the frame must be
+	 * transmitted on that channel; alternatively the frame may be sent on
+	 * the current operational channel (if in associated state in station
+	 * mode or while operating as an AP.)
+	 */
+	int (*send_action)(void *priv, unsigned int freq, unsigned int wait,
+			   const u8 *dst, const u8 *src, const u8 *bssid,
+			   const u8 *data, size_t data_len, int no_cck);
+
+	/**
+	 * send_action_cancel_wait - Cancel action frame TX wait
+	 * @priv: Private driver interface data
+	 *
+	 * This command cancels the wait time associated with sending an action
+	 * frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is
+	 * set in the driver flags.
+	 */
+	void (*send_action_cancel_wait)(void *priv);
+
+	/**
+	 * remain_on_channel - Remain awake on a channel
+	 * @priv: Private driver interface data
+	 * @freq: Frequency (in MHz) of the channel
+	 * @duration: Duration in milliseconds
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * 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_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.
+	 *
+	 * The driver may not be at the requested channel when this function
+	 * returns, i.e., the return code is only indicating whether the
+	 * request was accepted. The caller will need to wait until the
+	 * EVENT_REMAIN_ON_CHANNEL event indicates that the driver has
+	 * completed the channel change. This may take some time due to other
+	 * need for the radio and the caller should be prepared to timing out
+	 * its wait since there are no guarantees on when this request can be
+	 * executed.
+	 */
+	int (*remain_on_channel)(void *priv, unsigned int freq,
+				 unsigned int duration);
+
+	/**
+	 * cancel_remain_on_channel - Cancel remain-on-channel operation
+	 * @priv: Private driver interface data
+	 *
+	 * This command can be used to cancel a remain-on-channel operation
+	 * before its originally requested duration has passed. This could be
+	 * used, e.g., when remain_on_channel() is used to request extra time
+	 * to receive a response to an Action frame and the response is
+	 * received when there is still unneeded time remaining on the
+	 * remain-on-channel operation.
+	 */
+	int (*cancel_remain_on_channel)(void *priv);
+
+	/**
+	 * probe_req_report - Request Probe Request frames to be indicated
+	 * @priv: Private driver interface data
+	 * @report: Whether to report received Probe Request frames
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 *
+	 * This command can be used to request the driver to indicate when
+	 * Probe Request frames are received with EVENT_RX_PROBE_REQ events.
+	 * Since this operation may require extra resources, e.g., due to less
+	 * optimal hardware/firmware RX filtering, many drivers may disable
+	 * Probe Request reporting at least in station mode. This command is
+	 * used to notify the driver when the Probe Request frames need to be
+	 * reported, e.g., during remain-on-channel operations.
+	 */
+	int (*probe_req_report)(void *priv, int report);
+
+	/**
+	 * deinit_ap - Deinitialize AP mode
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 *
+	 * This optional function can be used to disable AP mode related
+	 * configuration. If the interface was not dynamically added,
+	 * change the driver mode to station mode to allow normal station
+	 * operations like scanning to be completed.
+	 */
+	int (*deinit_ap)(void *priv);
+
+	/**
+	 * deinit_p2p_cli - Deinitialize P2P client mode
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 *
+	 * This optional function can be used to disable P2P client mode. If the
+	 * interface was not dynamically added, change the interface type back
+	 * to station mode.
+	 */
+	int (*deinit_p2p_cli)(void *priv);
+
+	/**
+	 * suspend - Notification on system suspend/hibernate event
+	 * @priv: Private driver interface data
+	 */
+	void (*suspend)(void *priv);
+
+	/**
+	 * resume - Notification on system resume/thaw event
+	 * @priv: Private driver interface data
+	 */
+	void (*resume)(void *priv);
+
+	/**
+	 * signal_monitor - Set signal monitoring parameters
+	 * @priv: Private driver interface data
+	 * @threshold: Threshold value for signal change events; 0 = disabled
+	 * @hysteresis: Minimum change in signal strength before indicating a
+	 *	new event
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 *
+	 * This function can be used to configure monitoring of signal strength
+	 * with the current AP. Whenever signal strength drops below the
+	 * %threshold value or increases above it, EVENT_SIGNAL_CHANGE event
+	 * should be generated assuming the signal strength has changed at
+	 * least %hysteresis from the previously indicated signal change event.
+	 */
+	int (*signal_monitor)(void *priv, int threshold, int hysteresis);
+
+	/**
+	 * send_frame - Send IEEE 802.11 frame (testing use only)
+	 * @priv: Private driver interface data
+	 * @data: IEEE 802.11 frame with IEEE 802.11 header
+	 * @data_len: Size of the frame
+	 * @encrypt: Whether to encrypt the frame (if keys are set)
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is only used for debugging purposes and is not
+	 * required to be implemented for normal operations.
+	 */
+	int (*send_frame)(void *priv, const u8 *data, size_t data_len,
+			  int encrypt);
+
+	/**
+	 * get_noa - Get current Notice of Absence attribute payload
+	 * @priv: Private driver interface data
+	 * @buf: Buffer for returning NoA
+	 * @buf_len: Buffer length in octets
+	 * Returns: Number of octets used in buf, 0 to indicate no NoA is being
+	 * advertized, or -1 on failure
+	 *
+	 * This function is used to fetch the current Notice of Absence
+	 * attribute value from GO.
+	 */
+	int (*get_noa)(void *priv, u8 *buf, size_t buf_len);
+
+	/**
+	 * set_noa - Set Notice of Absence parameters for GO (testing)
+	 * @priv: Private driver interface data
+	 * @count: Count
+	 * @start: Start time in ms from next TBTT
+	 * @duration: Duration in ms
+	 * Returns: 0 on success or -1 on failure
+	 *
+	 * This function is used to set Notice of Absence parameters for GO. It
+	 * is used only for testing. To disable NoA, all parameters are set to
+	 * 0.
+	 */
+	int (*set_noa)(void *priv, u8 count, int start, int duration);
+
+	/**
+	 * set_p2p_powersave - Set P2P power save options
+	 * @priv: Private driver interface data
+	 * @legacy_ps: 0 = disable, 1 = enable, 2 = maximum PS, -1 = no change
+	 * @opp_ps: 0 = disable, 1 = enable, -1 = no change
+	 * @ctwindow: 0.. = change (msec), -1 = no change
+	 * Returns: 0 on success or -1 on failure
+	 */
+	int (*set_p2p_powersave)(void *priv, int legacy_ps, int opp_ps,
+				 int ctwindow);
+
+	/**
+	 * ampdu - Enable/disable aggregation
+	 * @priv: Private driver interface data
+	 * @ampdu: 1/0 = enable/disable A-MPDU aggregation
+	 * Returns: 0 on success or -1 on failure
+	 */
+	int (*ampdu)(void *priv, int ampdu);
+
+	/**
+	 * get_radio_name - Get physical radio name for the device
+	 * @priv: Private driver interface data
+	 * Returns: Radio name or %NULL if not known
+	 *
+	 * The returned data must not be modified by the caller. It is assumed
+	 * that any interface that has the same radio name as another is
+	 * sharing the same physical radio. This information can be used to
+	 * share scan results etc. information between the virtual interfaces
+	 * to speed up various operations.
+	 */
+	const char * (*get_radio_name)(void *priv);
+
+	/**
+	 * 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)
+	 * @initiator: Is the current end the TDLS link initiator
+	 * @buf: TDLS IEs to add to the message
+	 * @len: Length of buf in octets
+	 * Returns: 0 on success, negative (<0) on failure
+	 *
+	 * This optional function can be used to send packet to driver which is
+	 * 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, u32 peer_capab,
+			      int initiator, const u8 *buf, size_t len);
+
+	/**
+	 * tdls_oper - Ask the driver to perform high-level TDLS operations
+	 * @priv: Private driver interface data
+	 * @oper: TDLS high-level operation. See %enum tdls_oper
+	 * @peer: Destination (peer) MAC address
+	 * Returns: 0 on success, negative (<0) on failure
+	 *
+	 * This optional function can be used to send high-level TDLS commands
+	 * to the driver.
+	 */
+	int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer);
+
+	/**
+	 * wnm_oper - Notify driver of the WNM frame reception
+	 * @priv: Private driver interface data
+	 * @oper: WNM operation. See %enum wnm_oper
+	 * @peer: Destination (peer) MAC address
+	 * @buf: Buffer for the driver to fill in (for getting IE)
+	 * @buf_len: Return the len of buf
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer,
+			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);
+
+	/**
+	 * br_add_ip_neigh - Add a neigh to the bridge ip neigh table
+	 * @priv: Private driver interface data
+	 * @version: IP version of the IP address, 4 or 6
+	 * @ipaddr: IP address for the neigh entry
+	 * @prefixlen: IP address prefix length
+	 * @addr: Corresponding MAC address
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*br_add_ip_neigh)(void *priv, u8 version, const u8 *ipaddr,
+			       int prefixlen, const u8 *addr);
+
+	/**
+	 * br_delete_ip_neigh - Remove a neigh from the bridge ip neigh table
+	 * @priv: Private driver interface data
+	 * @version: IP version of the IP address, 4 or 6
+	 * @ipaddr: IP address for the neigh entry
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*br_delete_ip_neigh)(void *priv, u8 version, const u8 *ipaddr);
+
+	/**
+	 * br_port_set_attr - Set a bridge port attribute
+	 * @attr: Bridge port attribute to set
+	 * @val: Value to be set
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*br_port_set_attr)(void *priv, enum drv_br_port_attr attr,
+				unsigned int val);
+
+	/**
+	 * br_port_set_attr - Set a bridge network parameter
+	 * @param: Bridge parameter to set
+	 * @val: Value to be set
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
+				unsigned int val);
+
+	/**
+	 * 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);
+
+	/**
+	 * set_authmode - Set authentication algorithm(s) for static WEP
+	 * @priv: Private driver interface data
+	 * @authmode: 1=Open System, 2=Shared Key, 3=both
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function can be used to set authentication algorithms for AP
+	 * mode when static WEP is used. If the driver uses user space MLME/SME
+	 * implementation, there is no need to implement this function.
+	 *
+	 * 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
+	 * Returns: 0 on success, -1 on failure
+	 */
+	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
+	 * @kek: Current KEK
+	 * @kek_len: KEK length in octets
+	 * @kck: Current KCK
+	 * @kck_len: KCK length in octets
+	 * @replay_ctr: Current EAPOL-Key Replay Counter
+	 *
+	 * This optional function can be used to provide information for the
+	 * driver/firmware to process EAPOL-Key frames in Group Key Handshake
+	 * while the host (including wpa_supplicant) is sleeping.
+	 */
+	void (*set_rekey_info)(void *priv, const u8 *kek, size_t kek_len,
+			       const u8 *kck, size_t kck_len,
+			       const u8 *replay_ctr);
+
+	/**
+	 * sta_assoc - Station association indication
+	 * @priv: Private driver interface data
+	 * @own_addr: Source address and BSSID for association frame
+	 * @addr: MAC address of the station to associate
+	 * @reassoc: flag to indicate re-association
+	 * @status: association response status code
+	 * @ie: assoc response ie buffer
+	 * @len: ie buffer length
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function indicates the driver to send (Re)Association
+	 * Response frame to the station.
+	 */
+	 int (*sta_assoc)(void *priv, const u8 *own_addr, const u8 *addr,
+			  int reassoc, u16 status, const u8 *ie, size_t len);
+
+	/**
+	 * sta_auth - Station authentication indication
+	 * @priv: Private driver interface data
+	 * @own_addr: Source address and BSSID for authentication frame
+	 * @addr: MAC address of the station to associate
+	 * @seq: authentication sequence number
+	 * @status: authentication response status code
+	 * @ie: authentication frame ie buffer
+	 * @len: ie buffer length
+	 *
+	 * This function indicates the driver to send Authentication frame
+	 * to the station.
+	 */
+	 int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr,
+			 u16 seq, u16 status, const u8 *ie, size_t len);
+
+	/**
+	 * add_tspec - Add traffic stream
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the station to associate
+	 * @tspec_ie: tspec ie buffer
+	 * @tspec_ielen: tspec ie length
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function adds the traffic steam for the station
+	 * and fills the medium_time in tspec_ie.
+	 */
+	 int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie,
+			  size_t tspec_ielen);
+
+	/**
+	 * add_sta_node - Add a station node in the driver
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the station to add
+	 * @auth_alg: authentication algorithm used by the station
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function adds the station node in the driver, when
+	 * the station gets added by FT-over-DS.
+	 */
+	int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg);
+
+	/**
+	 * sched_scan - Request the driver to initiate scheduled scan
+	 * @priv: Private driver interface data
+	 * @params: Scan parameters
+	 * @interval: Interval between scan cycles in milliseconds
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This operation should be used for scheduled scan offload to
+	 * the hardware. Every time scan results are available, the
+	 * driver should report scan results event for wpa_supplicant
+	 * which will eventually request the results with
+	 * wpa_driver_get_scan_results2(). This operation is optional
+	 * and if not provided or if it returns -1, we fall back to
+	 * normal host-scheduled scans.
+	 */
+	int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params,
+			  u32 interval);
+
+	/**
+	 * stop_sched_scan - Request the driver to stop a scheduled scan
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This should cause the scheduled scan to be stopped and
+	 * results should stop being sent. Must be supported if
+	 * sched_scan is supported.
+	 */
+	int (*stop_sched_scan)(void *priv);
+
+	/**
+	 * poll_client - Probe (null data or such) the given station
+	 * @priv: Private driver interface data
+	 * @own_addr: MAC address of sending interface
+	 * @addr: MAC address of the station to probe
+	 * @qos: Indicates whether station is QoS station
+	 *
+	 * This function is used to verify whether an associated station is
+	 * still present. This function does not need to be implemented if the
+	 * driver provides such inactivity polling mechanism.
+	 */
+	void (*poll_client)(void *priv, const u8 *own_addr,
+			    const u8 *addr, int qos);
+
+	/**
+	 * radio_disable - Disable/enable radio
+	 * @priv: Private driver interface data
+	 * @disabled: 1=disable 0=enable radio
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This optional command is for testing purposes. It can be used to
+	 * disable the radio on a testbed device to simulate out-of-radio-range
+	 * conditions.
+	 */
+	int (*radio_disable)(void *priv, int disabled);
+
+	/**
+	 * switch_channel - Announce channel switch and migrate the GO to the
+	 * given frequency
+	 * @priv: Private driver interface data
+	 * @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, struct csa_settings *settings);
+
+	/**
+	 * add_tx_ts - Add traffic stream
+	 * @priv: Private driver interface data
+	 * @tsid: Traffic stream ID
+	 * @addr: Receiver address
+	 * @user_prio: User priority of the traffic stream
+	 * @admitted_time: Admitted time for this TS in units of
+	 *	32 microsecond periods (per second).
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*add_tx_ts)(void *priv, u8 tsid, const u8 *addr, u8 user_prio,
+			 u16 admitted_time);
+
+	/**
+	 * del_tx_ts - Delete traffic stream
+	 * @priv: Private driver interface data
+	 * @tsid: Traffic stream ID
+	 * @addr: Receiver address
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*del_tx_ts)(void *priv, u8 tsid, const u8 *addr);
+
+	/**
+	 * Enable channel-switching with TDLS peer
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the TDLS peer
+	 * @oper_class: Operating class of the switch channel
+	 * @params: Channel specification
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * The function indicates to driver that it can start switching to a
+	 * different channel with a specified TDLS peer. The switching is
+	 * assumed on until canceled with tdls_disable_channel_switch().
+	 */
+	int (*tdls_enable_channel_switch)(
+		void *priv, const u8 *addr, u8 oper_class,
+		const struct hostapd_freq_params *params);
+
+	/**
+	 * Disable channel switching with TDLS peer
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the TDLS peer
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function indicates to the driver that it should stop switching
+	 * with a given TDLS peer.
+	 */
+	int (*tdls_disable_channel_switch)(void *priv, const u8 *addr);
+
+	/**
+	 * start_dfs_cac - Listen for radar interference on the channel
+	 * @priv: Private driver interface data
+	 * @freq: Channel parameters
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq);
+
+	/**
+	 * stop_ap - Removes beacon from AP
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 *
+	 * This optional function can be used to disable AP mode related
+	 * configuration. Unlike deinit_ap, it does not change to station
+	 * mode.
+	 */
+	int (*stop_ap)(void *priv);
+
+	/**
+	 * get_survey - Retrieve survey data
+	 * @priv: Private driver interface data
+	 * @freq: If set, survey data for the specified frequency is only
+	 *	being requested. If not set, all survey data is requested.
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Use this to retrieve:
+	 *
+	 * - the observed channel noise floor
+	 * - the amount of time we have spent on the channel
+	 * - the amount of time during which we have spent on the channel that
+	 *   the radio has determined the medium is busy and we cannot
+	 *   transmit
+	 * - the amount of time we have spent receiving data
+	 * - the amount of time we have spent transmitting data
+	 *
+	 * This data can be used for spectrum heuristics. One example is
+	 * Automatic Channel Selection (ACS). The channel survey data is
+	 * kept on a linked list on the channel data, one entry is added
+	 * for each survey. The min_nf of the channel is updated for each
+	 * 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);
+
+	/**
+	 * roaming - Set roaming policy for driver-based BSS selection
+	 * @priv: Private driver interface data
+	 * @allowed: Whether roaming within ESS is allowed
+	 * @bssid: Forced BSSID if roaming is disabled or %NULL if not set
+	 * Returns: Length of written status information or -1 on failure
+	 *
+	 * This optional callback can be used to update roaming policy from the
+	 * associate() command (bssid being set there indicates that the driver
+	 * should not roam before getting this roaming() call to allow roaming.
+	 * If the driver does not indicate WPA_DRIVER_FLAGS_BSS_SELECTION
+	 * capability, roaming policy is handled within wpa_supplicant and there
+	 * is no need to implement or react to this callback.
+	 */
+	int (*roaming)(void *priv, int allowed, const u8 *bssid);
+
+	/**
+	 * set_mac_addr - Set MAC address
+	 * @priv: Private driver interface data
+	 * @addr: MAC address to use or %NULL for setting back to permanent
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_mac_addr)(void *priv, const u8 *addr);
+
+#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 */
+
+	/**
+	 * init_mesh - Driver specific initialization for mesh
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*init_mesh)(void *priv);
+
+	/**
+	 * join_mesh - Join a mesh network
+	 * @priv: Private driver interface data
+	 * @params: Mesh configuration parameters
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*join_mesh)(void *priv,
+			 struct wpa_driver_mesh_join_params *params);
+
+	/**
+	 * leave_mesh - Leave a mesh network
+	 * @priv: Private driver interface data
+	 * Returns 0 on success, -1 on failure
+	 */
+	int (*leave_mesh)(void *priv);
+
+	/**
+	 * do_acs - Automatically select channel
+	 * @priv: Private driver interface data
+	 * @params: Parameters for ACS
+	 * Returns 0 on success, -1 on failure
+	 *
+	 * This command can be used to offload ACS to the driver if the driver
+	 * indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD).
+	 */
+	int (*do_acs)(void *priv, struct drv_acs_params *params);
+
+	/**
+	 * set_band - Notify driver of band selection
+	 * @priv: Private driver interface data
+	 * @band: The selected band(s)
+	 * Returns 0 on success, -1 on failure
+	 */
+	int (*set_band)(void *priv, enum set_band band);
+
+	/**
+	 * get_pref_freq_list - Get preferred frequency list for an interface
+	 * @priv: Private driver interface data
+	 * @if_type: Interface type
+	 * @num: Number of channels
+	 * @freq_list: Preferred channel frequency list encoded in MHz values
+	 * Returns 0 on success, -1 on failure
+	 *
+	 * This command can be used to query the preferred frequency list from
+	 * the driver specific to a particular interface type.
+	 */
+	int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type,
+				  unsigned int *num, unsigned int *freq_list);
+
+	/**
+	 * set_prob_oper_freq - Indicate probable P2P operating channel
+	 * @priv: Private driver interface data
+	 * @freq: Channel frequency in MHz
+	 * Returns 0 on success, -1 on failure
+	 *
+	 * This command can be used to inform the driver of the operating
+	 * frequency that an ongoing P2P group formation is likely to come up
+	 * on. Local device is assuming P2P Client role.
+	 */
+	int (*set_prob_oper_freq)(void *priv, unsigned int freq);
+};
+
+
+/**
+ * enum wpa_event_type - Event type for wpa_supplicant_event() calls
+ */
+enum wpa_event_type {
+	/**
+	 * EVENT_ASSOC - Association completed
+	 *
+	 * This event needs to be delivered when the driver completes IEEE
+	 * 802.11 association or reassociation successfully.
+	 * wpa_driver_ops::get_bssid() is expected to provide the current BSSID
+	 * after this event has been generated. In addition, optional
+	 * EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide
+	 * more information about the association. If the driver interface gets
+	 * both of these events at the same time, it can also include the
+	 * assoc_info data in EVENT_ASSOC call.
+	 */
+	EVENT_ASSOC,
+
+	/**
+	 * EVENT_DISASSOC - Association lost
+	 *
+	 * This event should be called when association is lost either due to
+	 * receiving deauthenticate or disassociate frame from the AP or when
+	 * sending either of these frames to the current AP. If the driver
+	 * supports separate deauthentication event, EVENT_DISASSOC should only
+	 * be used for disassociation and EVENT_DEAUTH for deauthentication.
+	 * In AP mode, union wpa_event_data::disassoc_info is required.
+	 */
+	EVENT_DISASSOC,
+
+	/**
+	 * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected
+	 *
+	 * This event must be delivered when a Michael MIC error is detected by
+	 * the local driver. Additional data for event processing is
+	 * provided with union wpa_event_data::michael_mic_failure. This
+	 * information is used to request new encyption key and to initiate
+	 * TKIP countermeasures if needed.
+	 */
+	EVENT_MICHAEL_MIC_FAILURE,
+
+	/**
+	 * EVENT_SCAN_RESULTS - Scan results available
+	 *
+	 * This event must be called whenever scan results are available to be
+	 * fetched with struct wpa_driver_ops::get_scan_results(). This event
+	 * is expected to be used some time after struct wpa_driver_ops::scan()
+	 * is called. If the driver provides an unsolicited event when the scan
+	 * has been completed, this event can be used to trigger
+	 * EVENT_SCAN_RESULTS call. If such event is not available from the
+	 * driver, the driver wrapper code is expected to use a registered
+	 * timeout to generate EVENT_SCAN_RESULTS call after the time that the
+	 * scan is expected to be completed. Optional information about
+	 * completed scan can be provided with union wpa_event_data::scan_info.
+	 */
+	EVENT_SCAN_RESULTS,
+
+	/**
+	 * EVENT_ASSOCINFO - Report optional extra information for association
+	 *
+	 * This event can be used to report extra association information for
+	 * EVENT_ASSOC processing. This extra information includes IEs from
+	 * association frames and Beacon/Probe Response frames in union
+	 * wpa_event_data::assoc_info. EVENT_ASSOCINFO must be send just before
+	 * EVENT_ASSOC. Alternatively, the driver interface can include
+	 * assoc_info data in the EVENT_ASSOC call if it has all the
+	 * information available at the same point.
+	 */
+	EVENT_ASSOCINFO,
+
+	/**
+	 * EVENT_INTERFACE_STATUS - Report interface status changes
+	 *
+	 * This optional event can be used to report changes in interface
+	 * status (interface added/removed) using union
+	 * wpa_event_data::interface_status. This can be used to trigger
+	 * wpa_supplicant to stop and re-start processing for the interface,
+	 * e.g., when a cardbus card is ejected/inserted.
+	 */
+	EVENT_INTERFACE_STATUS,
+
+	/**
+	 * EVENT_PMKID_CANDIDATE - Report a candidate AP for pre-authentication
+	 *
+	 * This event can be used to inform wpa_supplicant about candidates for
+	 * RSN (WPA2) pre-authentication. If wpa_supplicant is not responsible
+	 * for scan request (ap_scan=2 mode), this event is required for
+	 * pre-authentication. If wpa_supplicant is performing scan request
+	 * (ap_scan=1), this event is optional since scan results can be used
+	 * to add pre-authentication candidates. union
+	 * wpa_event_data::pmkid_candidate is used to report the BSSID of the
+	 * candidate and priority of the candidate, e.g., based on the signal
+	 * strength, in order to try to pre-authenticate first with candidates
+	 * that are most likely targets for re-association.
+	 *
+	 * EVENT_PMKID_CANDIDATE can be called whenever the driver has updates
+	 * on the candidate list. In addition, it can be called for the current
+	 * AP and APs that have existing PMKSA cache entries. wpa_supplicant
+	 * will automatically skip pre-authentication in cases where a valid
+	 * PMKSA exists. When more than one candidate exists, this event should
+	 * be generated once for each candidate.
+	 *
+	 * Driver will be notified about successful pre-authentication with
+	 * struct wpa_driver_ops::add_pmkid() calls.
+	 */
+	EVENT_PMKID_CANDIDATE,
+
+	/**
+	 * EVENT_STKSTART - Request STK handshake (MLME-STKSTART.request)
+	 *
+	 * This event can be used to inform wpa_supplicant about desire to set
+	 * up secure direct link connection between two stations as defined in
+	 * IEEE 802.11e with a new PeerKey mechanism that replaced the original
+	 * STAKey negotiation. The caller will need to set peer address for the
+	 * event.
+	 */
+	EVENT_STKSTART,
+
+	/**
+	 * EVENT_TDLS - Request TDLS operation
+	 *
+	 * This event can be used to request a TDLS operation to be performed.
+	 */
+	EVENT_TDLS,
+
+	/**
+	 * EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs
+	 *
+	 * The driver is expected to report the received FT IEs from
+	 * FT authentication sequence from the AP. The FT IEs are included in
+	 * the extra information in union wpa_event_data::ft_ies.
+	 */
+	EVENT_FT_RESPONSE,
+
+	/**
+	 * EVENT_IBSS_RSN_START - Request RSN authentication in IBSS
+	 *
+	 * The driver can use this event to inform wpa_supplicant about a STA
+	 * in an IBSS with which protected frames could be exchanged. This
+	 * event starts RSN authentication with the other STA to authenticate
+	 * the STA and set up encryption keys with it.
+	 */
+	EVENT_IBSS_RSN_START,
+
+	/**
+	 * EVENT_AUTH - Authentication result
+	 *
+	 * This event should be called when authentication attempt has been
+	 * completed. This is only used if the driver supports separate
+	 * authentication step (struct wpa_driver_ops::authenticate).
+	 * Information about authentication result is included in
+	 * union wpa_event_data::auth.
+	 */
+	EVENT_AUTH,
+
+	/**
+	 * EVENT_DEAUTH - Authentication lost
+	 *
+	 * This event should be called when authentication is lost either due
+	 * to receiving deauthenticate frame from the AP or when sending that
+	 * frame to the current AP.
+	 * In AP mode, union wpa_event_data::deauth_info is required.
+	 */
+	EVENT_DEAUTH,
+
+	/**
+	 * EVENT_ASSOC_REJECT - Association rejected
+	 *
+	 * This event should be called when (re)association attempt has been
+	 * rejected by the AP. Information about the association response is
+	 * included in union wpa_event_data::assoc_reject.
+	 */
+	EVENT_ASSOC_REJECT,
+
+	/**
+	 * EVENT_AUTH_TIMED_OUT - Authentication timed out
+	 */
+	EVENT_AUTH_TIMED_OUT,
+
+	/**
+	 * EVENT_ASSOC_TIMED_OUT - Association timed out
+	 */
+	EVENT_ASSOC_TIMED_OUT,
+
+	/**
+	 * EVENT_WPS_BUTTON_PUSHED - Report hardware push button press for WPS
+	 */
+	EVENT_WPS_BUTTON_PUSHED,
+
+	/**
+	 * EVENT_TX_STATUS - Report TX status
+	 */
+	EVENT_TX_STATUS,
+
+	/**
+	 * EVENT_RX_FROM_UNKNOWN - Report RX from unknown STA
+	 */
+	EVENT_RX_FROM_UNKNOWN,
+
+	/**
+	 * EVENT_RX_MGMT - Report RX of a management frame
+	 */
+	EVENT_RX_MGMT,
+
+	/**
+	 * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started
+	 *
+	 * This event is used to indicate when the driver has started the
+	 * requested remain-on-channel duration. Information about the
+	 * operation is included in union wpa_event_data::remain_on_channel.
+	 */
+	EVENT_REMAIN_ON_CHANNEL,
+
+	/**
+	 * EVENT_CANCEL_REMAIN_ON_CHANNEL - Remain-on-channel timed out
+	 *
+	 * This event is used to indicate when the driver has completed
+	 * remain-on-channel duration, i.e., may noot be available on the
+	 * requested channel anymore. Information about the
+	 * operation is included in union wpa_event_data::remain_on_channel.
+	 */
+	EVENT_CANCEL_REMAIN_ON_CHANNEL,
+
+	/**
+	 * EVENT_RX_PROBE_REQ - Indicate received Probe Request frame
+	 *
+	 * This event is used to indicate when a Probe Request frame has been
+	 * received. Information about the received frame is included in
+	 * union wpa_event_data::rx_probe_req. The driver is required to report
+	 * these events only after successfully completed probe_req_report()
+	 * commands to request the events (i.e., report parameter is non-zero)
+	 * in station mode. In AP mode, Probe Request frames should always be
+	 * reported.
+	 */
+	EVENT_RX_PROBE_REQ,
+
+	/**
+	 * EVENT_NEW_STA - New wired device noticed
+	 *
+	 * This event is used to indicate that a new device has been detected
+	 * in a network that does not use association-like functionality (i.e.,
+	 * mainly wired Ethernet). This can be used to start EAPOL
+	 * authenticator when receiving a frame from a device. The address of
+	 * the device is included in union wpa_event_data::new_sta.
+	 */
+	EVENT_NEW_STA,
+
+	/**
+	 * EVENT_EAPOL_RX - Report received EAPOL frame
+	 *
+	 * When in AP mode with hostapd, this event is required to be used to
+	 * deliver the receive EAPOL frames from the driver.
+	 */
+	EVENT_EAPOL_RX,
+
+	/**
+	 * EVENT_SIGNAL_CHANGE - Indicate change in signal strength
+	 *
+	 * This event is used to indicate changes in the signal strength
+	 * observed in frames received from the current AP if signal strength
+	 * monitoring has been enabled with signal_monitor().
+	 */
+	EVENT_SIGNAL_CHANGE,
+
+	/**
+	 * EVENT_INTERFACE_ENABLED - Notify that interface was enabled
+	 *
+	 * This event is used to indicate that the interface was enabled after
+	 * having been previously disabled, e.g., due to rfkill.
+	 */
+	EVENT_INTERFACE_ENABLED,
+
+	/**
+	 * EVENT_INTERFACE_DISABLED - Notify that interface was disabled
+	 *
+	 * This event is used to indicate that the interface was disabled,
+	 * e.g., due to rfkill.
+	 */
+	EVENT_INTERFACE_DISABLED,
+
+	/**
+	 * EVENT_CHANNEL_LIST_CHANGED - Channel list changed
+	 *
+	 * This event is used to indicate that the channel list has changed,
+	 * e.g., because of a regulatory domain change triggered by scan
+	 * results including an AP advertising a country code.
+	 */
+	EVENT_CHANNEL_LIST_CHANGED,
+
+	/**
+	 * EVENT_INTERFACE_UNAVAILABLE - Notify that interface is unavailable
+	 *
+	 * This event is used to indicate that the driver cannot maintain this
+	 * interface in its operation mode anymore. The most likely use for
+	 * this is to indicate that AP mode operation is not available due to
+	 * operating channel would need to be changed to a DFS channel when
+	 * 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. This event can be propagated when channel switching
+	 * fails.
+	 */
+	EVENT_INTERFACE_UNAVAILABLE,
+
+	/**
+	 * EVENT_BEST_CHANNEL
+	 *
+	 * Driver generates this event whenever it detects a better channel
+	 * (e.g., based on RSSI or channel use). This information can be used
+	 * to improve channel selection for a new AP/P2P group.
+	 */
+	EVENT_BEST_CHANNEL,
+
+	/**
+	 * EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received
+	 *
+	 * This event should be called when a Deauthentication frame is dropped
+	 * due to it not being protected (MFP/IEEE 802.11w).
+	 * union wpa_event_data::unprot_deauth is required to provide more
+	 * details of the frame.
+	 */
+	EVENT_UNPROT_DEAUTH,
+
+	/**
+	 * EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received
+	 *
+	 * This event should be called when a Disassociation frame is dropped
+	 * due to it not being protected (MFP/IEEE 802.11w).
+	 * union wpa_event_data::unprot_disassoc is required to provide more
+	 * details of the frame.
+	 */
+	EVENT_UNPROT_DISASSOC,
+
+	/**
+	 * EVENT_STATION_LOW_ACK
+	 *
+	 * Driver generates this event whenever it detected that a particular
+	 * station was lost. Detection can be through massive transmission
+	 * failures for example.
+	 */
+	EVENT_STATION_LOW_ACK,
+
+	/**
+	 * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore
+	 */
+	EVENT_IBSS_PEER_LOST,
+
+	/**
+	 * EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey
+	 *
+	 * This event carries the new replay counter to notify wpa_supplicant
+	 * of the current EAPOL-Key Replay Counter in case the driver/firmware
+	 * completed Group Key Handshake while the host (including
+	 * wpa_supplicant was sleeping).
+	 */
+	EVENT_DRIVER_GTK_REKEY,
+
+	/**
+	 * EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped
+	 */
+	EVENT_SCHED_SCAN_STOPPED,
+
+	/**
+	 * EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll
+	 *
+	 * This event indicates that the station responded to the poll
+	 * initiated with @poll_client.
+	 */
+	EVENT_DRIVER_CLIENT_POLL_OK,
+
+	/**
+	 * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status
+	 */
+	EVENT_EAPOL_TX_STATUS,
+
+	/**
+	 * EVENT_CH_SWITCH - AP or GO decided to switch channels
+	 *
+	 * Described in wpa_event_data.ch_switch
+	 * */
+	EVENT_CH_SWITCH,
+
+	/**
+	 * EVENT_WNM - Request WNM operation
+	 *
+	 * This event can be used to request a WNM operation to be performed.
+	 */
+	EVENT_WNM,
+
+	/**
+	 * EVENT_CONNECT_FAILED_REASON - Connection failure reason in AP mode
+	 *
+	 * This event indicates that the driver reported a connection failure
+	 * with the specified client (for example, max client reached, etc.) in
+	 * AP mode.
+	 */
+	EVENT_CONNECT_FAILED_REASON,
+
+	/**
+	 * EVENT_DFS_RADAR_DETECTED - Notify of radar detection
+	 *
+	 * A radar has been detected on the supplied frequency, hostapd should
+	 * react accordingly (e.g., change channel).
+	 */
+	EVENT_DFS_RADAR_DETECTED,
+
+	/**
+	 * EVENT_DFS_CAC_FINISHED - Notify that channel availability check has been completed
+	 *
+	 * After a successful CAC, the channel can be marked clear and used.
+	 */
+	EVENT_DFS_CAC_FINISHED,
+
+	/**
+	 * EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted
+	 *
+	 * The CAC was not successful, and the channel remains in the previous
+	 * state. This may happen due to a radar beeing detected or other
+	 * external influences.
+	 */
+	EVENT_DFS_CAC_ABORTED,
+
+	/**
+	 * EVENT_DFS_NOP_FINISHED - Notify that non-occupancy period is over
+	 *
+	 * The channel which was previously unavailable is now available again.
+	 */
+	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_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,
+
+	/**
+	 * EVENT_NEW_PEER_CANDIDATE - new (unknown) mesh peer notification
+	 */
+	EVENT_NEW_PEER_CANDIDATE,
+
+	/**
+	 * EVENT_ACS_CHANNEL_SELECTED - Received selected channels by ACS
+	 *
+	 * Indicates a pair of primary and secondary channels chosen by ACS
+	 * in device.
+	 */
+	EVENT_ACS_CHANNEL_SELECTED,
+
+	/**
+	 * EVENT_DFS_CAC_STARTED - Notify that channel availability check has
+	 * been started.
+	 *
+	 * This event indicates that channel availability check has been started
+	 * on a DFS frequency by a driver that supports DFS Offload.
+	 */
+	EVENT_DFS_CAC_STARTED,
+};
+
+
+/**
+ * struct freq_survey - Channel survey info
+ *
+ * @ifidx: Interface index in which this survey was observed
+ * @freq: Center of frequency of the surveyed channel
+ * @nf: Channel noise floor in dBm
+ * @channel_time: Amount of time in ms the radio spent on the channel
+ * @channel_time_busy: Amount of time in ms the radio detected some signal
+ *     that indicated to the radio the channel was not clear
+ * @channel_time_rx: Amount of time the radio spent receiving data
+ * @channel_time_tx: Amount of time the radio spent transmitting data
+ * @filled: bitmask indicating which fields have been reported, see
+ *     SURVEY_HAS_* defines.
+ * @list: Internal list pointers
+ */
+struct freq_survey {
+	u32 ifidx;
+	unsigned int freq;
+	s8 nf;
+	u64 channel_time;
+	u64 channel_time_busy;
+	u64 channel_time_rx;
+	u64 channel_time_tx;
+	unsigned int filled;
+	struct dl_list list;
+};
+
+#define SURVEY_HAS_NF BIT(0)
+#define SURVEY_HAS_CHAN_TIME BIT(1)
+#define SURVEY_HAS_CHAN_TIME_BUSY BIT(2)
+#define SURVEY_HAS_CHAN_TIME_RX BIT(3)
+#define SURVEY_HAS_CHAN_TIME_TX BIT(4)
+
+
+/**
+ * union wpa_event_data - Additional data for wpa_supplicant_event() calls
+ */
+union wpa_event_data {
+	/**
+	 * struct assoc_info - Data for EVENT_ASSOC and EVENT_ASSOCINFO events
+	 *
+	 * This structure is optional for EVENT_ASSOC calls and required for
+	 * EVENT_ASSOCINFO calls. By using EVENT_ASSOC with this data, the
+	 * driver interface does not need to generate separate EVENT_ASSOCINFO
+	 * calls.
+	 */
+	struct assoc_info {
+		/**
+		 * reassoc - Flag to indicate association or reassociation
+		 */
+		int reassoc;
+
+		/**
+		 * req_ies - (Re)Association Request IEs
+		 *
+		 * If the driver generates WPA/RSN IE, this event data must be
+		 * returned for WPA handshake to have needed information. If
+		 * wpa_supplicant-generated WPA/RSN IE is used, this
+		 * information event is optional.
+		 *
+		 * This should start with the first IE (fixed fields before IEs
+		 * are not included).
+		 */
+		const u8 *req_ies;
+
+		/**
+		 * req_ies_len - Length of req_ies in bytes
+		 */
+		size_t req_ies_len;
+
+		/**
+		 * resp_ies - (Re)Association Response IEs
+		 *
+		 * Optional association data from the driver. This data is not
+		 * required WPA, but may be useful for some protocols and as
+		 * such, should be reported if this is available to the driver
+		 * interface.
+		 *
+		 * This should start with the first IE (fixed fields before IEs
+		 * are not included).
+		 */
+		const u8 *resp_ies;
+
+		/**
+		 * resp_ies_len - Length of resp_ies in bytes
+		 */
+		size_t resp_ies_len;
+
+		/**
+		 * beacon_ies - Beacon or Probe Response IEs
+		 *
+		 * Optional Beacon/ProbeResp data: IEs included in Beacon or
+		 * Probe Response frames from the current AP (i.e., the one
+		 * that the client just associated with). This information is
+		 * used to update WPA/RSN IE for the AP. If this field is not
+		 * set, the results from previous scan will be used. If no
+		 * data for the new AP is found, scan results will be requested
+		 * again (without scan request). At this point, the driver is
+		 * expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is
+		 * used).
+		 *
+		 * This should start with the first IE (fixed fields before IEs
+		 * are not included).
+		 */
+		const u8 *beacon_ies;
+
+		/**
+		 * beacon_ies_len - Length of beacon_ies */
+		size_t beacon_ies_len;
+
+		/**
+		 * freq - Frequency of the operational channel in MHz
+		 */
+		unsigned int freq;
+
+		/**
+		 * wmm_params - WMM parameters used in this association.
+		 */
+		struct wmm_params wmm_params;
+
+		/**
+		 * addr - Station address (for AP mode)
+		 */
+		const u8 *addr;
+
+		/**
+		 * The following is the key management offload information
+		 * @authorized
+		 * @key_replay_ctr
+		 * @key_replay_ctr_len
+		 * @ptk_kck
+		 * @ptk_kek_len
+		 * @ptk_kek
+		 * @ptk_kek_len
+		 */
+
+		/**
+		 * authorized - Status of key management offload,
+		 * 1 = successful
+		 */
+		int authorized;
+
+		/**
+		 * key_replay_ctr - Key replay counter value last used
+		 * in a valid EAPOL-Key frame
+		 */
+		const u8 *key_replay_ctr;
+
+		/**
+		 * key_replay_ctr_len - The length of key_replay_ctr
+		 */
+		size_t key_replay_ctr_len;
+
+		/**
+		 * ptk_kck - The derived PTK KCK
+		 */
+		const u8 *ptk_kck;
+
+		/**
+		 * ptk_kek_len - The length of ptk_kck
+		 */
+		size_t ptk_kck_len;
+
+		/**
+		 * ptk_kek - The derived PTK KEK
+		 */
+		const u8 *ptk_kek;
+
+		/**
+		 * ptk_kek_len - The length of ptk_kek
+		 */
+		size_t ptk_kek_len;
+	} assoc_info;
+
+	/**
+	 * struct disassoc_info - Data for EVENT_DISASSOC events
+	 */
+	struct disassoc_info {
+		/**
+		 * addr - Station address (for AP mode)
+		 */
+		const u8 *addr;
+
+		/**
+		 * reason_code - Reason Code (host byte order) used in
+		 *	Deauthentication frame
+		 */
+		u16 reason_code;
+
+		/**
+		 * ie - Optional IE(s) in Disassociation frame
+		 */
+		const u8 *ie;
+
+		/**
+		 * ie_len - Length of ie buffer in octets
+		 */
+		size_t ie_len;
+
+		/**
+		 * locally_generated - Whether the frame was locally generated
+		 */
+		int locally_generated;
+	} disassoc_info;
+
+	/**
+	 * struct deauth_info - Data for EVENT_DEAUTH events
+	 */
+	struct deauth_info {
+		/**
+		 * addr - Station address (for AP mode)
+		 */
+		const u8 *addr;
+
+		/**
+		 * reason_code - Reason Code (host byte order) used in
+		 *	Deauthentication frame
+		 */
+		u16 reason_code;
+
+		/**
+		 * ie - Optional IE(s) in Deauthentication frame
+		 */
+		const u8 *ie;
+
+		/**
+		 * ie_len - Length of ie buffer in octets
+		 */
+		size_t ie_len;
+
+		/**
+		 * locally_generated - Whether the frame was locally generated
+		 */
+		int locally_generated;
+	} deauth_info;
+
+	/**
+	 * struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE
+	 */
+	struct michael_mic_failure {
+		int unicast;
+		const u8 *src;
+	} michael_mic_failure;
+
+	/**
+	 * struct interface_status - Data for EVENT_INTERFACE_STATUS
+	 */
+	struct interface_status {
+		char ifname[100];
+		enum {
+			EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED
+		} ievent;
+	} interface_status;
+
+	/**
+	 * struct pmkid_candidate - Data for EVENT_PMKID_CANDIDATE
+	 */
+	struct pmkid_candidate {
+		/** BSSID of the PMKID candidate */
+		u8 bssid[ETH_ALEN];
+		/** Smaller the index, higher the priority */
+		int index;
+		/** Whether RSN IE includes pre-authenticate flag */
+		int preauth;
+	} pmkid_candidate;
+
+	/**
+	 * struct stkstart - Data for EVENT_STKSTART
+	 */
+	struct stkstart {
+		u8 peer[ETH_ALEN];
+	} stkstart;
+
+	/**
+	 * struct tdls - Data for EVENT_TDLS
+	 */
+	struct tdls {
+		u8 peer[ETH_ALEN];
+		enum {
+			TDLS_REQUEST_SETUP,
+			TDLS_REQUEST_TEARDOWN,
+			TDLS_REQUEST_DISCOVER,
+		} oper;
+		u16 reason_code; /* for teardown */
+	} tdls;
+
+	/**
+	 * struct wnm - Data for EVENT_WNM
+	 */
+	struct wnm {
+		u8 addr[ETH_ALEN];
+		enum {
+			WNM_OPER_SLEEP,
+		} oper;
+		enum {
+			WNM_SLEEP_ENTER,
+			WNM_SLEEP_EXIT
+		} sleep_action;
+		int sleep_intval;
+		u16 reason_code;
+		u8 *buf;
+		u16 buf_len;
+	} wnm;
+
+	/**
+	 * struct ft_ies - FT information elements (EVENT_FT_RESPONSE)
+	 *
+	 * During FT (IEEE 802.11r) authentication sequence, the driver is
+	 * expected to use this event to report received FT IEs (MDIE, FTIE,
+	 * RSN IE, TIE, possible resource request) to the supplicant. The FT
+	 * IEs for the next message will be delivered through the
+	 * struct wpa_driver_ops::update_ft_ies() callback.
+	 */
+	struct ft_ies {
+		const u8 *ies;
+		size_t ies_len;
+		int ft_action;
+		u8 target_ap[ETH_ALEN];
+		/** Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request */
+		const u8 *ric_ies;
+		/** Length of ric_ies buffer in octets */
+		size_t ric_ies_len;
+	} ft_ies;
+
+	/**
+	 * struct ibss_rsn_start - Data for EVENT_IBSS_RSN_START
+	 */
+	struct ibss_rsn_start {
+		u8 peer[ETH_ALEN];
+	} ibss_rsn_start;
+
+	/**
+	 * struct auth_info - Data for EVENT_AUTH events
+	 */
+	struct auth_info {
+		u8 peer[ETH_ALEN];
+		u8 bssid[ETH_ALEN];
+		u16 auth_type;
+		u16 auth_transaction;
+		u16 status_code;
+		const u8 *ies;
+		size_t ies_len;
+	} auth;
+
+	/**
+	 * struct assoc_reject - Data for EVENT_ASSOC_REJECT events
+	 */
+	struct assoc_reject {
+		/**
+		 * bssid - BSSID of the AP that rejected association
+		 */
+		const u8 *bssid;
+
+		/**
+		 * resp_ies - (Re)Association Response IEs
+		 *
+		 * Optional association data from the driver. This data is not
+		 * required WPA, but may be useful for some protocols and as
+		 * such, should be reported if this is available to the driver
+		 * interface.
+		 *
+		 * This should start with the first IE (fixed fields before IEs
+		 * are not included).
+		 */
+		const u8 *resp_ies;
+
+		/**
+		 * resp_ies_len - Length of resp_ies in bytes
+		 */
+		size_t resp_ies_len;
+
+		/**
+		 * status_code - Status Code from (Re)association Response
+		 */
+		u16 status_code;
+	} assoc_reject;
+
+	struct timeout_event {
+		u8 addr[ETH_ALEN];
+	} timeout_event;
+
+	/**
+	 * struct tx_status - Data for EVENT_TX_STATUS events
+	 */
+	struct tx_status {
+		u16 type;
+		u16 stype;
+		const u8 *dst;
+		const u8 *data;
+		size_t data_len;
+		int ack;
+	} tx_status;
+
+	/**
+	 * struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events
+	 */
+	struct rx_from_unknown {
+		const u8 *bssid;
+		const u8 *addr;
+		int wds;
+	} rx_from_unknown;
+
+	/**
+	 * struct rx_mgmt - Data for EVENT_RX_MGMT events
+	 */
+	struct rx_mgmt {
+		const u8 *frame;
+		size_t frame_len;
+		u32 datarate;
+
+		/**
+		 * 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.
+		 */
+		void *drv_priv;
+
+		/**
+		 * freq - Frequency (in MHz) on which the frame was received
+		 */
+		int freq;
+
+		/**
+		 * 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
+	 *
+	 * This is also used with EVENT_CANCEL_REMAIN_ON_CHANNEL events.
+	 */
+	struct remain_on_channel {
+		/**
+		 * freq - Channel frequency in MHz
+		 */
+		unsigned int freq;
+
+		/**
+		 * duration - Duration to remain on the channel in milliseconds
+		 */
+		unsigned int duration;
+	} remain_on_channel;
+
+	/**
+	 * struct scan_info - Optional data for EVENT_SCAN_RESULTS events
+	 * @aborted: Whether the scan was aborted
+	 * @freqs: Scanned frequencies in MHz (%NULL = all channels scanned)
+	 * @num_freqs: Number of entries in freqs array
+	 * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
+	 *	SSID)
+	 * @num_ssids: Number of entries in ssids array
+	 */
+	struct scan_info {
+		int aborted;
+		const int *freqs;
+		size_t num_freqs;
+		struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
+		size_t num_ssids;
+	} scan_info;
+
+	/**
+	 * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events
+	 */
+	struct rx_probe_req {
+		/**
+		 * sa - Source address of the received Probe Request frame
+		 */
+		const u8 *sa;
+
+		/**
+		 * da - Destination address of the received Probe Request frame
+		 *	or %NULL if not available
+		 */
+		const u8 *da;
+
+		/**
+		 * bssid - BSSID of the received Probe Request frame or %NULL
+		 *	if not available
+		 */
+		const u8 *bssid;
+
+		/**
+		 * ie - IEs from the Probe Request body
+		 */
+		const u8 *ie;
+
+		/**
+		 * ie_len - Length of ie buffer in octets
+		 */
+		size_t ie_len;
+
+		/**
+		 * signal - signal strength in dBm (or 0 if not available)
+		 */
+		int ssi_signal;
+	} rx_probe_req;
+
+	/**
+	 * struct new_sta - Data for EVENT_NEW_STA events
+	 */
+	struct new_sta {
+		const u8 *addr;
+	} new_sta;
+
+	/**
+	 * struct eapol_rx - Data for EVENT_EAPOL_RX events
+	 */
+	struct eapol_rx {
+		const u8 *src;
+		const u8 *data;
+		size_t data_len;
+	} eapol_rx;
+
+	/**
+	 * signal_change - Data for EVENT_SIGNAL_CHANGE events
+	 */
+	struct wpa_signal_info signal_change;
+
+	/**
+	 * struct best_channel - Data for EVENT_BEST_CHANNEL events
+	 * @freq_24: Best 2.4 GHz band channel frequency in MHz
+	 * @freq_5: Best 5 GHz band channel frequency in MHz
+	 * @freq_overall: Best channel frequency in MHz
+	 *
+	 * 0 can be used to indicate no preference in either band.
+	 */
+	struct best_channel {
+		int freq_24;
+		int freq_5;
+		int freq_overall;
+	} best_chan;
+
+	struct unprot_deauth {
+		const u8 *sa;
+		const u8 *da;
+		u16 reason_code;
+	} unprot_deauth;
+
+	struct unprot_disassoc {
+		const u8 *sa;
+		const u8 *da;
+		u16 reason_code;
+	} unprot_disassoc;
+
+	/**
+	 * struct low_ack - Data for EVENT_STATION_LOW_ACK events
+	 * @addr: station address
+	 */
+	struct low_ack {
+		u8 addr[ETH_ALEN];
+	} low_ack;
+
+	/**
+	 * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST
+	 */
+	struct ibss_peer_lost {
+		u8 peer[ETH_ALEN];
+	} ibss_peer_lost;
+
+	/**
+	 * struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY
+	 */
+	struct driver_gtk_rekey {
+		const u8 *bssid;
+		const u8 *replay_ctr;
+	} driver_gtk_rekey;
+
+	/**
+	 * struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events
+	 * @addr: station address
+	 */
+	struct client_poll {
+		u8 addr[ETH_ALEN];
+	} client_poll;
+
+	/**
+	 * struct eapol_tx_status
+	 * @dst: Original destination
+	 * @data: Data starting with IEEE 802.1X header (!)
+	 * @data_len: Length of data
+	 * @ack: Indicates ack or lost frame
+	 *
+	 * This corresponds to hapd_send_eapol if the frame sent
+	 * there isn't just reported as EVENT_TX_STATUS.
+	 */
+	struct eapol_tx_status {
+		const u8 *dst;
+		const u8 *data;
+		int data_len;
+		int ack;
+	} eapol_tx_status;
+
+	/**
+	 * struct ch_switch
+	 * @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;
+
+	/**
+	 * struct connect_failed - Data for EVENT_CONNECT_FAILED_REASON
+	 * @addr: Remote client address
+	 * @code: Reason code for connection failure
+	 */
+	struct connect_failed_reason {
+		u8 addr[ETH_ALEN];
+		enum {
+			MAX_CLIENT_REACHED,
+			BLOCKED_CLIENT
+		} code;
+	} connect_failed_reason;
+
+	/**
+	 * struct dfs_event - Data for radar detected events
+	 * @freq: Frequency of the channel in MHz
+	 */
+	struct dfs_event {
+		int freq;
+		int ht_enabled;
+		int chan_offset;
+		enum chan_width chan_width;
+		int cf1;
+		int cf2;
+	} dfs_event;
+
+	/**
+	 * survey_results - Survey result data for EVENT_SURVEY
+	 * @freq_filter: Requested frequency survey filter, 0 if request
+	 *	was for all survey data
+	 * @survey_list: Linked list of survey data (struct freq_survey)
+	 */
+	struct survey_results {
+		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;
+
+	/**
+	 * struct mesh_peer
+	 *
+	 * @peer: Peer address
+	 * @ies: Beacon IEs
+	 * @ie_len: Length of @ies
+	 *
+	 * Notification of new candidate mesh peer.
+	 */
+	struct mesh_peer {
+		const u8 *peer;
+		const u8 *ies;
+		size_t ie_len;
+	} mesh_peer;
+
+	/**
+	 * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED
+	 * @pri_channel: Selected primary channel
+	 * @sec_channel: Selected secondary channel
+	 * @vht_seg0_center_ch: VHT mode Segment0 center channel
+	 * @vht_seg1_center_ch: VHT mode Segment1 center channel
+	 * @ch_width: Selected Channel width by driver. Driver may choose to
+	 *	change hostapd configured ACS channel width due driver internal
+	 *	channel restrictions.
+	 * hw_mode: Selected band (used with hw_mode=any)
+	 */
+	struct acs_selected_channels {
+		u8 pri_channel;
+		u8 sec_channel;
+		u8 vht_seg0_center_ch;
+		u8 vht_seg1_center_ch;
+		u16 ch_width;
+		enum hostapd_hw_mode hw_mode;
+	} acs_selected_channels;
+};
+
+/**
+ * wpa_supplicant_event - Report a driver event for wpa_supplicant
+ * @ctx: Context pointer (wpa_s); this is the ctx variable registered
+ *	with struct wpa_driver_ops::init()
+ * @event: event type (defined above)
+ * @data: possible extra data for the event
+ *
+ * Driver wrapper code should call this function whenever an event is received
+ * from the driver.
+ */
+void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+			  union wpa_event_data *data);
+
+
+/*
+ * The following inline functions are provided for convenience to simplify
+ * event indication for some of the common events.
+ */
+
+static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie,
+				   size_t ielen, int reassoc)
+{
+	union wpa_event_data event;
+	os_memset(&event, 0, sizeof(event));
+	event.assoc_info.reassoc = reassoc;
+	event.assoc_info.req_ies = ie;
+	event.assoc_info.req_ies_len = ielen;
+	event.assoc_info.addr = addr;
+	wpa_supplicant_event(ctx, EVENT_ASSOC, &event);
+}
+
+static inline void drv_event_disassoc(void *ctx, const u8 *addr)
+{
+	union wpa_event_data event;
+	os_memset(&event, 0, sizeof(event));
+	event.disassoc_info.addr = addr;
+	wpa_supplicant_event(ctx, EVENT_DISASSOC, &event);
+}
+
+static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data,
+				      size_t data_len)
+{
+	union wpa_event_data event;
+	os_memset(&event, 0, sizeof(event));
+	event.eapol_rx.src = src;
+	event.eapol_rx.data = data;
+	event.eapol_rx.data_len = data_len;
+	wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event);
+}
+
+/* driver_common.c */
+void wpa_scan_results_free(struct wpa_scan_results *res);
+
+/* Convert wpa_event_type to a string for logging */
+const char * event_to_string(enum wpa_event_type event);
+
+/* Convert chan_width to a string for logging and control interfaces */
+const char * channel_width_to_string(enum chan_width width);
+
+int ht_supported(const struct hostapd_hw_modes *mode);
+int vht_supported(const struct hostapd_hw_modes *mode);
+
+struct wowlan_triggers *
+wpa_get_wowlan_triggers(const char *wowlan_triggers,
+			const struct wpa_driver_capa *capa);
+
+/* NULL terminated array of linked in driver wrappers */
+extern const struct wpa_driver_ops *const wpa_drivers[];
+
+#endif /* DRIVER_H */
diff --git a/hostap/src/drivers/driver_atheros.c b/hostap/src/drivers/driver_atheros.c
new file mode 100644
index 0000000..ef14093
--- /dev/null
+++ b/hostap/src/drivers/driver_atheros.c
@@ -0,0 +1,2171 @@
+/*
+ * hostapd / Driver interaction with Atheros driver
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Copyright (c) 2004, Video54 Technologies
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "l2_packet/l2_packet.h"
+#include "p2p/p2p.h"
+
+#include "common.h"
+#ifndef _BYTE_ORDER
+#ifdef WORDS_BIGENDIAN
+#define _BYTE_ORDER _BIG_ENDIAN
+#else
+#define _BYTE_ORDER _LITTLE_ENDIAN
+#endif
+#endif /* _BYTE_ORDER */
+
+/*
+ * Note, the ATH_WPS_IE setting must match with the driver build.. If the
+ * driver does not include this, the IEEE80211_IOCTL_GETWPAIE ioctl will fail.
+ */
+#define ATH_WPS_IE
+
+#include "ieee80211_external.h"
+
+
+#ifdef CONFIG_WPS
+#include <netpacket/packet.h>
+#endif /* CONFIG_WPS */
+
+#ifndef ETH_P_80211_RAW
+#define ETH_P_80211_RAW 0x0019
+#endif
+
+#include "linux_wext.h"
+
+#include "driver.h"
+#include "eloop.h"
+#include "priv_netlink.h"
+#include "l2_packet/l2_packet.h"
+#include "common/ieee802_11_defs.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS)
+#define ATHEROS_USE_RAW_RECEIVE
+#endif
+
+
+struct atheros_driver_data {
+	struct hostapd_data *hapd;		/* back pointer */
+
+	char	iface[IFNAMSIZ + 1];
+	int     ifindex;
+	struct l2_packet_data *sock_xmit;	/* raw packet xmit socket */
+	struct l2_packet_data *sock_recv;	/* raw packet recv socket */
+	int	ioctl_sock;			/* socket for ioctl() use */
+	struct netlink_data *netlink;
+	int	we_version;
+	u8	acct_mac[ETH_ALEN];
+	struct hostap_sta_driver_data acct_data;
+
+	struct l2_packet_data *sock_raw; /* raw 802.11 management frames */
+	struct wpabuf *wpa_ie;
+	struct wpabuf *wps_beacon_ie;
+	struct wpabuf *wps_probe_resp_ie;
+	u8	own_addr[ETH_ALEN];
+};
+
+static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+			      int reason_code);
+static int atheros_set_privacy(void *priv, int enabled);
+
+static const char * athr_get_ioctl_name(int op)
+{
+	switch (op) {
+	case IEEE80211_IOCTL_SETPARAM:
+		return "SETPARAM";
+	case IEEE80211_IOCTL_GETPARAM:
+		return "GETPARAM";
+	case IEEE80211_IOCTL_SETKEY:
+		return "SETKEY";
+	case IEEE80211_IOCTL_SETWMMPARAMS:
+		return "SETWMMPARAMS";
+	case IEEE80211_IOCTL_DELKEY:
+		return "DELKEY";
+	case IEEE80211_IOCTL_GETWMMPARAMS:
+		return "GETWMMPARAMS";
+	case IEEE80211_IOCTL_SETMLME:
+		return "SETMLME";
+	case IEEE80211_IOCTL_GETCHANINFO:
+		return "GETCHANINFO";
+	case IEEE80211_IOCTL_SETOPTIE:
+		return "SETOPTIE";
+	case IEEE80211_IOCTL_GETOPTIE:
+		return "GETOPTIE";
+	case IEEE80211_IOCTL_ADDMAC:
+		return "ADDMAC";
+	case IEEE80211_IOCTL_DELMAC:
+		return "DELMAC";
+	case IEEE80211_IOCTL_GETCHANLIST:
+		return "GETCHANLIST";
+	case IEEE80211_IOCTL_SETCHANLIST:
+		return "SETCHANLIST";
+	case IEEE80211_IOCTL_KICKMAC:
+		return "KICKMAC";
+	case IEEE80211_IOCTL_CHANSWITCH:
+		return "CHANSWITCH";
+	case IEEE80211_IOCTL_GETMODE:
+		return "GETMODE";
+	case IEEE80211_IOCTL_SETMODE:
+		return "SETMODE";
+	case IEEE80211_IOCTL_GET_APPIEBUF:
+		return "GET_APPIEBUF";
+	case IEEE80211_IOCTL_SET_APPIEBUF:
+		return "SET_APPIEBUF";
+	case IEEE80211_IOCTL_SET_ACPARAMS:
+		return "SET_ACPARAMS";
+	case IEEE80211_IOCTL_FILTERFRAME:
+		return "FILTERFRAME";
+	case IEEE80211_IOCTL_SET_RTPARAMS:
+		return "SET_RTPARAMS";
+	case IEEE80211_IOCTL_SET_MEDENYENTRY:
+		return "SET_MEDENYENTRY";
+	case IEEE80211_IOCTL_GET_MACADDR:
+		return "GET_MACADDR";
+	case IEEE80211_IOCTL_SET_HBRPARAMS:
+		return "SET_HBRPARAMS";
+	case IEEE80211_IOCTL_SET_RXTIMEOUT:
+		return "SET_RXTIMEOUT";
+	case IEEE80211_IOCTL_STA_STATS:
+		return "STA_STATS";
+	case IEEE80211_IOCTL_GETWPAIE:
+		return "GETWPAIE";
+	default:
+		return "??";
+	}
+}
+
+
+static const char * athr_get_param_name(int op)
+{
+	switch (op) {
+	case IEEE80211_IOC_MCASTCIPHER:
+		return "MCASTCIPHER";
+	case IEEE80211_PARAM_MCASTKEYLEN:
+		return "MCASTKEYLEN";
+	case IEEE80211_PARAM_UCASTCIPHERS:
+		return "UCASTCIPHERS";
+	case IEEE80211_PARAM_KEYMGTALGS:
+		return "KEYMGTALGS";
+	case IEEE80211_PARAM_RSNCAPS:
+		return "RSNCAPS";
+	case IEEE80211_PARAM_WPA:
+		return "WPA";
+	case IEEE80211_PARAM_AUTHMODE:
+		return "AUTHMODE";
+	case IEEE80211_PARAM_PRIVACY:
+		return "PRIVACY";
+	case IEEE80211_PARAM_COUNTERMEASURES:
+		return "COUNTERMEASURES";
+	default:
+		return "??";
+	}
+}
+
+
+static int
+set80211priv(struct atheros_driver_data *drv, int op, void *data, int len)
+{
+	struct iwreq iwr;
+	int do_inline = len < IFNAMSIZ;
+
+	/* Certain ioctls must use the non-inlined method */
+	if (op == IEEE80211_IOCTL_SET_APPIEBUF ||
+	    op == IEEE80211_IOCTL_FILTERFRAME)
+		do_inline = 0;
+
+	memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	if (do_inline) {
+		/*
+		 * Argument data fits inline; put it there.
+		 */
+		memcpy(iwr.u.name, data, len);
+	} else {
+		/*
+		 * Argument data too big for inline transfer; setup a
+		 * parameter block instead; the kernel will transfer
+		 * the data for the driver.
+		 */
+		iwr.u.data.pointer = data;
+		iwr.u.data.length = len;
+	}
+
+	if (ioctl(drv->ioctl_sock, op, &iwr) < 0) {
+		wpa_printf(MSG_DEBUG, "atheros: %s: %s: ioctl op=0x%x "
+			   "(%s) len=%d failed: %d (%s)",
+			   __func__, drv->iface, op,
+			   athr_get_ioctl_name(op),
+			   len, errno, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+static int
+set80211param(struct atheros_driver_data *drv, int op, int arg)
+{
+	struct iwreq iwr;
+
+	memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.mode = op;
+	memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg));
+
+	if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) {
+		wpa_printf(MSG_INFO,
+			   "%s: %s: Failed to set parameter (op %d (%s) arg %d): ioctl[IEEE80211_IOCTL_SETPARAM]: %s",
+			   __func__, drv->iface, op, athr_get_param_name(op),
+			   arg, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char *
+ether_sprintf(const u8 *addr)
+{
+	static char buf[sizeof(MACSTR)];
+
+	if (addr != NULL)
+		snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+	else
+		snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
+	return buf;
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+/*
+ * Configure WPA parameters.
+ */
+static int
+atheros_configure_wpa(struct atheros_driver_data *drv,
+		      struct wpa_bss_params *params)
+{
+	int v;
+
+	switch (params->wpa_group) {
+	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;
+	case WPA_CIPHER_WEP104:
+		v = IEEE80211_CIPHER_WEP;
+		break;
+	case WPA_CIPHER_WEP40:
+		v = IEEE80211_CIPHER_WEP;
+		break;
+	case WPA_CIPHER_NONE:
+		v = IEEE80211_CIPHER_NONE;
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "Unknown group key cipher %u",
+			   params->wpa_group);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v);
+	if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) {
+		wpa_printf(MSG_INFO, "Unable to set group key cipher to %u", v);
+		return -1;
+	}
+	if (v == IEEE80211_CIPHER_WEP) {
+		/* key length is done only for specific ciphers */
+		v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
+		if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) {
+			wpa_printf(MSG_INFO,
+				   "Unable to set group key length to %u", v);
+			return -1;
+		}
+	}
+
+	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)
+		v |= 1<<IEEE80211_CIPHER_NONE;
+	wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
+	if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) {
+		wpa_printf(MSG_INFO,
+			   "Unable to set pairwise key ciphers to 0x%x", v);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
+		   __func__, params->wpa_key_mgmt);
+	if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS,
+			  params->wpa_key_mgmt)) {
+		wpa_printf(MSG_INFO,
+			   "Unable to set key management algorithms to 0x%x",
+			   params->wpa_key_mgmt);
+		return -1;
+	}
+
+	v = 0;
+	if (params->rsn_preauth)
+		v |= BIT(0);
+#ifdef CONFIG_IEEE80211W
+	if (params->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		v |= BIT(7);
+		if (params->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+			v |= BIT(6);
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v);
+	if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
+		wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x",
+			   v);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa);
+	if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) {
+		wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+atheros_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+	struct atheros_driver_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
+
+	if (!params->enabled) {
+		/* XXX restore state */
+		if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
+				  IEEE80211_AUTH_AUTO) < 0)
+			return -1;
+		/* IEEE80211_AUTH_AUTO ends up enabling Privacy; clear that */
+		return atheros_set_privacy(drv, 0);
+	}
+	if (!params->wpa && !params->ieee802_1x) {
+		wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!");
+		return -1;
+	}
+	if (params->wpa && atheros_configure_wpa(drv, params) != 0) {
+		wpa_printf(MSG_WARNING, "Error configuring WPA state!");
+		return -1;
+	}
+	if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
+		(params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
+		wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+atheros_set_privacy(void *priv, int enabled)
+{
+	struct atheros_driver_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+	return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled);
+}
+
+static int
+atheros_set_sta_authorized(void *priv, const u8 *addr, int authorized)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d",
+		   __func__, ether_sprintf(addr), authorized);
+
+	if (authorized)
+		mlme.im_op = IEEE80211_MLME_AUTHORIZE;
+	else
+		mlme.im_op = IEEE80211_MLME_UNAUTHORIZE;
+	mlme.im_reason = 0;
+	memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR,
+			   __func__, authorized ? "" : "un", MAC2STR(addr));
+	}
+
+	return ret;
+}
+
+static int
+atheros_sta_set_flags(void *priv, const u8 *addr,
+		      unsigned int total_flags, unsigned int flags_or,
+		      unsigned int flags_and)
+{
+	/* For now, only support setting Authorized flag */
+	if (flags_or & WPA_STA_AUTHORIZED)
+		return atheros_set_sta_authorized(priv, addr, 1);
+	if (!(flags_and & WPA_STA_AUTHORIZED))
+		return atheros_set_sta_authorized(priv, addr, 0);
+	return 0;
+}
+
+static int
+atheros_del_key(void *priv, const u8 *addr, int key_idx)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_del_key wk;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d",
+		   __func__, ether_sprintf(addr), key_idx);
+
+	memset(&wk, 0, sizeof(wk));
+	if (addr != NULL) {
+		memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+		wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE;
+	} else {
+		wk.idk_keyix = key_idx;
+	}
+
+	ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s"
+			   " key_idx %d)", __func__, ether_sprintf(addr),
+			   key_idx);
+	}
+
+	return ret;
+}
+
+static int
+atheros_set_key(const char *ifname, void *priv, 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)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_key wk;
+	u_int8_t cipher;
+	int ret;
+
+	if (alg == WPA_ALG_NONE)
+		return atheros_del_key(drv, addr, key_idx);
+
+	wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d",
+		   __func__, alg, ether_sprintf(addr), key_idx);
+
+	switch (alg) {
+	case WPA_ALG_WEP:
+		cipher = IEEE80211_CIPHER_WEP;
+		break;
+	case WPA_ALG_TKIP:
+		cipher = IEEE80211_CIPHER_TKIP;
+		break;
+	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:
+		wpa_printf(MSG_INFO, "%s: unknown/unsupported algorithm %d",
+			   __func__, alg);
+		return -1;
+	}
+
+	if (key_len > sizeof(wk.ik_keydata)) {
+		wpa_printf(MSG_INFO, "%s: key length %lu too big", __func__,
+			   (unsigned long) key_len);
+		return -3;
+	}
+
+	memset(&wk, 0, sizeof(wk));
+	wk.ik_type = cipher;
+	wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT;
+	if (addr == NULL || is_broadcast_ether_addr(addr)) {
+		memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+		wk.ik_keyix = key_idx;
+		if (set_tx)
+			wk.ik_flags |= IEEE80211_KEY_DEFAULT;
+	} else {
+		memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+		wk.ik_keyix = IEEE80211_KEYIX_NONE;
+	}
+	wk.ik_keylen = key_len;
+	memcpy(wk.ik_keydata, key, key_len);
+
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s"
+			   " key_idx %d alg %d key_len %lu set_tx %d)",
+			   __func__, ether_sprintf(wk.ik_macaddr), key_idx,
+			   alg, (unsigned long) key_len, set_tx);
+	}
+
+	return ret;
+}
+
+
+static int
+atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+		   u8 *seq)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_key wk;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
+		   __func__, ether_sprintf(addr), idx);
+
+	memset(&wk, 0, sizeof(wk));
+	if (addr == NULL)
+		memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+	else
+		memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+	wk.ik_keyix = idx;
+
+	if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data "
+			   "(addr " MACSTR " key_idx %d)",
+			   __func__, MAC2STR(wk.ik_macaddr), idx);
+		return -1;
+	}
+
+#ifdef WORDS_BIGENDIAN
+	{
+		/*
+		 * wk.ik_keytsc is in host byte order (big endian), need to
+		 * swap it to match with the byte order used in WPA.
+		 */
+		int i;
+#ifndef WPA_KEY_RSC_LEN
+#define WPA_KEY_RSC_LEN 8
+#endif
+		u8 tmp[WPA_KEY_RSC_LEN];
+		memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+		for (i = 0; i < WPA_KEY_RSC_LEN; i++) {
+			seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1];
+		}
+	}
+#else /* WORDS_BIGENDIAN */
+	memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+#endif /* WORDS_BIGENDIAN */
+	return 0;
+}
+
+
+static int
+atheros_flush(void *priv)
+{
+	u8 allsta[IEEE80211_ADDR_LEN];
+	memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+	return atheros_sta_deauth(priv, NULL, allsta,
+				  IEEE80211_REASON_AUTH_LEAVE);
+}
+
+
+static int
+atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
+			     const u8 *addr)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_sta_stats stats;
+
+	memset(data, 0, sizeof(*data));
+
+	/*
+	 * Fetch statistics for station from the system.
+	 */
+	memset(&stats, 0, sizeof(stats));
+	memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
+	if (set80211priv(drv, IEEE80211_IOCTL_STA_STATS,
+			 &stats, sizeof(stats))) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr "
+			   MACSTR ")", __func__, MAC2STR(addr));
+		if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
+			memcpy(data, &drv->acct_data, sizeof(*data));
+			return 0;
+		}
+
+		wpa_printf(MSG_INFO,
+			   "Failed to get station stats information element");
+		return -1;
+	}
+
+	data->rx_packets = stats.is_stats.ns_rx_data;
+	data->rx_bytes = stats.is_stats.ns_rx_bytes;
+	data->tx_packets = stats.is_stats.ns_tx_data;
+	data->tx_bytes = stats.is_stats.ns_tx_bytes;
+	return 0;
+}
+
+
+static int
+atheros_sta_clear_stats(void *priv, const u8 *addr)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr));
+
+	mlme.im_op = IEEE80211_MLME_CLEAR_STATS;
+	memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme,
+			   sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr "
+			   MACSTR ")", __func__, MAC2STR(addr));
+	}
+
+	return ret;
+}
+
+
+static int
+atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
+{
+	struct atheros_driver_data *drv = priv;
+	u8 buf[512];
+	struct ieee80211req_getset_appiebuf *app_ie;
+
+	wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__,
+		   (unsigned long) ie_len);
+	wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len);
+
+	wpabuf_free(drv->wpa_ie);
+	drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len);
+
+	app_ie = (struct ieee80211req_getset_appiebuf *) buf;
+	os_memcpy(&(app_ie->app_buf[0]), ie, ie_len);
+	app_ie->app_buflen = ie_len;
+
+	app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON;
+
+	/* append WPS IE for Beacon */
+	if (drv->wps_beacon_ie != NULL) {
+		os_memcpy(&(app_ie->app_buf[ie_len]),
+			  wpabuf_head(drv->wps_beacon_ie),
+			  wpabuf_len(drv->wps_beacon_ie));
+		app_ie->app_buflen = ie_len + wpabuf_len(drv->wps_beacon_ie);
+	}
+	wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(Beacon)",
+		    app_ie->app_buf, app_ie->app_buflen);
+	set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie,
+		     sizeof(struct ieee80211req_getset_appiebuf) +
+		     app_ie->app_buflen);
+
+	/* append WPS IE for Probe Response */
+	app_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_RESP;
+	if (drv->wps_probe_resp_ie != NULL) {
+		os_memcpy(&(app_ie->app_buf[ie_len]),
+			  wpabuf_head(drv->wps_probe_resp_ie),
+			  wpabuf_len(drv->wps_probe_resp_ie));
+		app_ie->app_buflen = ie_len +
+			wpabuf_len(drv->wps_probe_resp_ie);
+	} else
+		app_ie->app_buflen = ie_len;
+	wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(ProbeResp)",
+		    app_ie->app_buf, app_ie->app_buflen);
+	set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie,
+		     sizeof(struct ieee80211req_getset_appiebuf) +
+		     app_ie->app_buflen);
+	return 0;
+}
+
+static int
+atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+		   int reason_code)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d",
+		   __func__, ether_sprintf(addr), reason_code);
+
+	mlme.im_op = IEEE80211_MLME_DEAUTH;
+	mlme.im_reason = reason_code;
+	memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR
+			   " reason %d)",
+			   __func__, MAC2STR(addr), reason_code);
+	}
+
+	return ret;
+}
+
+static int
+atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+		     int reason_code)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d",
+		   __func__, ether_sprintf(addr), reason_code);
+
+	mlme.im_op = IEEE80211_MLME_DISASSOC;
+	mlme.im_reason = reason_code;
+	memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr "
+			   MACSTR " reason %d)",
+			   __func__, MAC2STR(addr), reason_code);
+	}
+
+	return ret;
+}
+
+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) {
+		wpa_printf(MSG_ERROR,
+			   "%s: %s: Failed to set QoS Map: ioctl[IEEE80211_IOCTL_DBGREQ]: %s",
+			   __func__, drv->iface, strerror(errno));
+		return -1;
+	}
+#endif /* CONFIG_ATHEROS_QOS_MAP */
+
+	return 0;
+}
+
+#ifdef ATHEROS_USE_RAW_RECEIVE
+static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+				size_t len)
+{
+	struct atheros_driver_data *drv = ctx;
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	u16 fc, stype;
+	int ielen;
+	const u8 *iebuf;
+
+	if (len < IEEE80211_HDRLEN)
+		return;
+
+	mgmt = (const struct ieee80211_mgmt *) buf;
+
+	fc = le_to_host16(mgmt->frame_control);
+
+	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
+		return;
+
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype,
+		   (int) len);
+
+	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
+		if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+			return;
+
+		os_memset(&event, 0, sizeof(event));
+		event.rx_probe_req.sa = mgmt->sa;
+		event.rx_probe_req.da = mgmt->da;
+		event.rx_probe_req.bssid = mgmt->bssid;
+		event.rx_probe_req.ie = mgmt->u.probe_req.variable;
+		event.rx_probe_req.ie_len =
+			len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+		wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event);
+		return;
+	}
+
+	if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore",
+			   __func__);
+		return;
+	}
+
+	switch (stype) {
+	case WLAN_FC_STYPE_ASSOC_REQ:
+		if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req))
+			break;
+		ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
+		iebuf = mgmt->u.assoc_req.variable;
+		drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0);
+		break;
+	case WLAN_FC_STYPE_REASSOC_REQ:
+		if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req))
+			break;
+		ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
+		iebuf = mgmt->u.reassoc_req.variable;
+		drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1);
+		break;
+	case WLAN_FC_STYPE_ACTION:
+		os_memset(&event, 0, sizeof(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))
+			break;
+		os_memset(&event, 0, sizeof(event));
+		os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
+		os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN);
+		event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+		event.auth.status_code =
+			le_to_host16(mgmt->u.auth.status_code);
+		event.auth.auth_transaction =
+			le_to_host16(mgmt->u.auth.auth_transaction);
+		event.auth.ies = mgmt->u.auth.variable;
+		event.auth.ies_len = len - IEEE80211_HDRLEN -
+			sizeof(mgmt->u.auth);
+		wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event);
+		break;
+	default:
+		break;
+	}
+}
+#endif /* ATHEROS_USE_RAW_RECEIVE */
+
+static int atheros_receive_pkt(struct atheros_driver_data *drv)
+{
+	int ret = 0;
+	struct ieee80211req_set_filter filt;
+
+	wpa_printf(MSG_DEBUG, "%s Enter", __func__);
+	filt.app_filterype = 0;
+#ifdef CONFIG_WPS
+	filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ;
+#endif /* CONFIG_WPS */
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R)
+	filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ |
+			       IEEE80211_FILTER_TYPE_AUTH |
+			       IEEE80211_FILTER_TYPE_ACTION);
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#ifdef CONFIG_WNM
+	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
+#endif /* CONFIG_WNM */
+#ifdef CONFIG_HS20
+	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
+#endif /* CONFIG_HS20 */
+	if (filt.app_filterype) {
+		ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
+				   sizeof(struct ieee80211req_set_filter));
+		if (ret)
+			return ret;
+	}
+
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R)
+	drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
+				       atheros_raw_receive, drv, 1);
+	if (drv->sock_raw == NULL)
+		return -1;
+#endif /* CONFIG_WPS || CONFIG_IEEE80211R */
+	return ret;
+}
+
+static int atheros_reset_appfilter(struct atheros_driver_data *drv)
+{
+	struct ieee80211req_set_filter filt;
+	filt.app_filterype = 0;
+	return set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
+			    sizeof(struct ieee80211req_set_filter));
+}
+
+#ifdef CONFIG_WPS
+static int
+atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
+{
+	struct atheros_driver_data *drv = priv;
+	u8 buf[512];
+	struct ieee80211req_getset_appiebuf *beac_ie;
+
+	wpa_printf(MSG_DEBUG, "%s buflen = %lu frametype=%u", __func__,
+		   (unsigned long) len, frametype);
+	wpa_hexdump(MSG_DEBUG, "atheros: IE", ie, len);
+
+	beac_ie = (struct ieee80211req_getset_appiebuf *) buf;
+	beac_ie->app_frmtype = frametype;
+	beac_ie->app_buflen = len;
+	os_memcpy(&(beac_ie->app_buf[0]), ie, len);
+
+	/* append the WPA/RSN IE if it is set already */
+	if (((frametype == IEEE80211_APPIE_FRAME_BEACON) ||
+	     (frametype == IEEE80211_APPIE_FRAME_PROBE_RESP)) &&
+	    (drv->wpa_ie != NULL)) {
+		wpa_hexdump_buf(MSG_DEBUG, "atheros: Append WPA/RSN IE",
+				drv->wpa_ie);
+		os_memcpy(&(beac_ie->app_buf[len]), wpabuf_head(drv->wpa_ie),
+			  wpabuf_len(drv->wpa_ie));
+		beac_ie->app_buflen += wpabuf_len(drv->wpa_ie);
+	}
+
+	wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF",
+		    beac_ie->app_buf, beac_ie->app_buflen);
+	return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie,
+			    sizeof(struct ieee80211req_getset_appiebuf) +
+			    beac_ie->app_buflen);
+}
+
+static int
+atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
+		      const struct wpabuf *proberesp,
+		      const struct wpabuf *assocresp)
+{
+	struct atheros_driver_data *drv = priv;
+
+	wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - beacon", beacon);
+	wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - proberesp",
+			proberesp);
+	wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - assocresp",
+			assocresp);
+	wpabuf_free(drv->wps_beacon_ie);
+	drv->wps_beacon_ie = beacon ? wpabuf_dup(beacon) : NULL;
+	wpabuf_free(drv->wps_probe_resp_ie);
+	drv->wps_probe_resp_ie = proberesp ? wpabuf_dup(proberesp) : NULL;
+
+	atheros_set_wps_ie(priv, assocresp ? wpabuf_head(assocresp) : NULL,
+			   assocresp ? wpabuf_len(assocresp) : 0,
+			   IEEE80211_APPIE_FRAME_ASSOC_RESP);
+	if (atheros_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL,
+			       beacon ? wpabuf_len(beacon) : 0,
+			       IEEE80211_APPIE_FRAME_BEACON))
+		return -1;
+	return atheros_set_wps_ie(priv,
+				  proberesp ? wpabuf_head(proberesp) : NULL,
+				  proberesp ? wpabuf_len(proberesp): 0,
+				  IEEE80211_APPIE_FRAME_PROBE_RESP);
+}
+#else /* CONFIG_WPS */
+#define atheros_set_ap_wps_ie NULL
+#endif /* CONFIG_WPS */
+
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+static int
+atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq,
+		 u16 status_code, const u8 *ie, size_t len)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d",
+		   __func__, ether_sprintf(addr), status_code);
+
+	mlme.im_op = IEEE80211_MLME_AUTH;
+	mlme.im_reason = status_code;
+	mlme.im_seq = seq;
+	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	mlme.im_optie_len = len;
+	if (len) {
+		if (len < IEEE80211_MAX_OPT_IE) {
+			os_memcpy(mlme.im_optie, ie, len);
+		} else {
+			wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
+				   "opt_ie STA (addr " MACSTR " reason %d, "
+				   "ie_len %d)",
+				   __func__, MAC2STR(addr), status_code,
+				   (int) len);
+			return -1;
+		}
+	}
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR
+			   " reason %d)",
+			   __func__, MAC2STR(addr), status_code);
+	}
+	return ret;
+}
+
+static int
+atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr,
+		  int reassoc, u16 status_code, const u8 *ie, size_t len)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d reassoc %d",
+		   __func__, ether_sprintf(addr), status_code, reassoc);
+
+	if (reassoc)
+		mlme.im_op = IEEE80211_MLME_REASSOC;
+	else
+		mlme.im_op = IEEE80211_MLME_ASSOC;
+	mlme.im_reason = status_code;
+	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	mlme.im_optie_len = len;
+	if (len) {
+		if (len < IEEE80211_MAX_OPT_IE) {
+			os_memcpy(mlme.im_optie, ie, len);
+		} else {
+			wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
+				   "opt_ie STA (addr " MACSTR " reason %d, "
+				   "ie_len %d)",
+				   __func__, MAC2STR(addr), status_code,
+				   (int) len);
+			return -1;
+		}
+	}
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to assoc STA (addr " MACSTR
+			   " reason %d)",
+			   __func__, MAC2STR(addr), status_code);
+	}
+	return ret;
+}
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+
+static void
+atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
+{
+	struct hostapd_data *hapd = drv->hapd;
+	struct ieee80211req_wpaie ie;
+	int ielen = 0;
+	u8 *iebuf = NULL;
+
+	/*
+	 * Fetch negotiated WPA/RSN parameters from the system.
+	 */
+	memset(&ie, 0, sizeof(ie));
+	memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
+	if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) {
+		/*
+		 * See ATH_WPS_IE comment in the beginning of the file for a
+		 * possible cause for the failure..
+		 */
+		wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE: %s",
+			   __func__, strerror(errno));
+		goto no_ie;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "atheros req WPA IE",
+		    ie.wpa_ie, IEEE80211_MAX_OPT_IE);
+	wpa_hexdump(MSG_MSGDUMP, "atheros req RSN IE",
+		    ie.rsn_ie, IEEE80211_MAX_OPT_IE);
+#ifdef ATH_WPS_IE
+	wpa_hexdump(MSG_MSGDUMP, "atheros req WPS IE",
+		    ie.wps_ie, IEEE80211_MAX_OPT_IE);
+#endif /* ATH_WPS_IE */
+	iebuf = ie.wpa_ie;
+	/* atheros seems to return some random data if WPA/RSN IE is not set.
+	 * Assume the IE was not included if the IE type is unknown. */
+	if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC)
+		iebuf[1] = 0;
+	if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) {
+		/* atheros-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not
+		 * set. This is needed for WPA2. */
+		iebuf = ie.rsn_ie;
+		if (iebuf[0] != WLAN_EID_RSN)
+			iebuf[1] = 0;
+	}
+
+	ielen = iebuf[1];
+
+#ifdef ATH_WPS_IE
+	/* if WPS IE is present, preference is given to WPS */
+	if (ie.wps_ie &&
+	    (ie.wps_ie[1] > 0 && (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC))) {
+		iebuf = ie.wps_ie;
+		ielen = ie.wps_ie[1];
+	}
+#endif /* ATH_WPS_IE */
+
+	if (ielen == 0)
+		iebuf = NULL;
+	else
+		ielen += 2;
+
+no_ie:
+	drv_event_assoc(hapd, addr, iebuf, ielen, 0);
+
+	if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
+		/* Cached accounting data is not valid anymore. */
+		memset(drv->acct_mac, 0, ETH_ALEN);
+		memset(&drv->acct_data, 0, sizeof(drv->acct_data));
+	}
+}
+
+static void
+atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv,
+				       char *custom, char *end)
+{
+#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */
+	wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
+
+	if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+		char *pos;
+		u8 addr[ETH_ALEN];
+		pos = strstr(custom, "addr=");
+		if (pos == NULL) {
+			wpa_printf(MSG_DEBUG,
+				   "MLME-MICHAELMICFAILURE.indication "
+				   "without sender address ignored");
+			return;
+		}
+		pos += 5;
+		if (hwaddr_aton(pos, addr) == 0) {
+			union wpa_event_data data;
+			os_memset(&data, 0, sizeof(data));
+			data.michael_mic_failure.unicast = 1;
+			data.michael_mic_failure.src = addr;
+			wpa_supplicant_event(drv->hapd,
+					     EVENT_MICHAEL_MIC_FAILURE, &data);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "MLME-MICHAELMICFAILURE.indication "
+				   "with invalid MAC address");
+		}
+	} else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) {
+		char *key, *value;
+		u32 val;
+		key = custom;
+		while ((key = strchr(key, '\n')) != NULL) {
+			key++;
+			value = strchr(key, '=');
+			if (value == NULL)
+				continue;
+			*value++ = '\0';
+			val = strtoul(value, NULL, 10);
+			if (strcmp(key, "mac") == 0)
+				hwaddr_aton(value, drv->acct_mac);
+			else if (strcmp(key, "rx_packets") == 0)
+				drv->acct_data.rx_packets = val;
+			else if (strcmp(key, "tx_packets") == 0)
+				drv->acct_data.tx_packets = val;
+			else if (strcmp(key, "rx_bytes") == 0)
+				drv->acct_data.rx_bytes = val;
+			else if (strcmp(key, "tx_bytes") == 0)
+				drv->acct_data.tx_bytes = val;
+			key = value;
+		}
+#ifdef CONFIG_WPS
+	} else if (strncmp(custom, "PUSH-BUTTON.indication", 22) == 0) {
+		/* Some atheros kernels send push button as a wireless event */
+		/* PROBLEM! this event is received for ALL BSSs ...
+		 * so all are enabled for WPS... ugh.
+		 */
+		wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL);
+	} else if (strncmp(custom, "Manage.prob_req ", 16) == 0) {
+		/*
+		 * Atheros driver uses a hack to pass Probe Request frames as a
+		 * binary data in the custom wireless event. The old way (using
+		 * packet sniffing) didn't work when bridging.
+		 * Format: "Manage.prob_req <frame len>" | zero padding | frame
+		 */
+		int len = atoi(custom + 16);
+		if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
+			wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event "
+				   "length %d", len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+#endif /* CONFIG_WPS */
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+	} else if (strncmp(custom, "Manage.assoc_req ", 17) == 0) {
+		/* Format: "Manage.assoc_req <frame len>" | zero padding |
+		 * frame */
+		int len = atoi(custom + 17);
+		if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid Manage.assoc_req event length %d",
+				   len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+		} else if (strncmp(custom, "Manage.auth ", 12) == 0) {
+		/* Format: "Manage.auth <frame len>" | zero padding | frame */
+		int len = atoi(custom + 12);
+			if (len < 0 ||
+			    custom + MGMT_FRAM_TAG_SIZE + len > end) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid Manage.auth event length %d", len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */
+#ifdef ATHEROS_USE_RAW_RECEIVE
+		} else if (strncmp(custom, "Manage.action ", 14) == 0) {
+		/* Format: "Manage.assoc_req <frame len>" | zero padding | frame
+		 */
+		int len = atoi(custom + 14);
+		if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid Manage.action event length %d",
+				   len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+#endif /* ATHEROS_USE_RAW_RECEIVE */
+	}
+}
+
+/*
+* Handle size of data problem. WEXT only allows data of 256 bytes for custom
+* events, and p2p data can be much bigger. So the athr driver sends a small
+* event telling me to collect the big data with an ioctl.
+* On the first event, send all pending events to supplicant.
+*/
+static void fetch_pending_big_events(struct atheros_driver_data *drv)
+{
+	union wpa_event_data event;
+	const struct ieee80211_mgmt *mgmt;
+	u8 tbuf[IW_PRIV_SIZE_MASK]; /* max size is 2047 bytes */
+	u16 fc, stype;
+	struct iwreq iwr;
+	size_t data_len;
+	u32 freq, frame_type;
+
+	while (1) {
+		os_memset(&iwr, 0, sizeof(iwr));
+		os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+
+		iwr.u.data.pointer = (void *) tbuf;
+		iwr.u.data.length = sizeof(tbuf);
+		iwr.u.data.flags = IEEE80211_IOC_P2P_FETCH_FRAME;
+
+		if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr)
+		    < 0) {
+			if (errno == ENOSPC) {
+				wpa_printf(MSG_DEBUG, "%s:%d exit",
+					   __func__, __LINE__);
+				return;
+			}
+			wpa_printf(MSG_DEBUG, "athr: %s: P2P_BIG_PARAM["
+				   "P2P_FETCH_FRAME] failed: %s",
+				   __func__, strerror(errno));
+			return;
+		}
+		data_len = iwr.u.data.length;
+		wpa_hexdump(MSG_DEBUG, "athr: P2P_FETCH_FRAME data",
+			    (u8 *) tbuf, data_len);
+		if (data_len < sizeof(freq) + sizeof(frame_type) + 24) {
+			wpa_printf(MSG_DEBUG, "athr: frame too short");
+			continue;
+		}
+		os_memcpy(&freq, tbuf, sizeof(freq));
+		os_memcpy(&frame_type, &tbuf[sizeof(freq)],
+			  sizeof(frame_type));
+		mgmt = (void *) &tbuf[sizeof(freq) + sizeof(frame_type)];
+		data_len -= sizeof(freq) + sizeof(frame_type);
+
+		if (frame_type == IEEE80211_EV_RX_MGMT) {
+			fc = le_to_host16(mgmt->frame_control);
+			stype = WLAN_FC_GET_STYPE(fc);
+
+			wpa_printf(MSG_DEBUG, "athr: EV_RX_MGMT stype=%u "
+				"freq=%u len=%u", stype, freq, (int) data_len);
+
+			if (stype == WLAN_FC_STYPE_ACTION) {
+				os_memset(&event, 0, sizeof(event));
+				event.rx_mgmt.frame = (const u8 *) mgmt;
+				event.rx_mgmt.frame_len = data_len;
+				wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT,
+						     &event);
+				continue;
+			}
+		} else {
+			wpa_printf(MSG_DEBUG, "athr: %s unknown type %d",
+				   __func__, frame_type);
+			continue;
+		}
+	}
+}
+
+static void
+atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv,
+				      int opcode, char *buf, int len)
+{
+	switch (opcode) {
+	case IEEE80211_EV_RX_MGMT:
+		wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT");
+		fetch_pending_big_events(drv);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+atheros_wireless_event_wireless(struct atheros_driver_data *drv,
+				char *data, int len)
+{
+	struct iw_event iwe_buf, *iwe = &iwe_buf;
+	char *pos, *end, *custom, *buf;
+
+	pos = data;
+	end = data + len;
+
+	while (pos + IW_EV_LCP_LEN <= end) {
+		/* Event data may be unaligned, so make a local, aligned copy
+		 * before processing. */
+		memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+		wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d",
+			   iwe->cmd, iwe->len);
+		if (iwe->len <= IW_EV_LCP_LEN)
+			return;
+
+		custom = pos + IW_EV_POINT_LEN;
+		if (drv->we_version > 18 &&
+		    (iwe->cmd == IWEVMICHAELMICFAILURE ||
+		     iwe->cmd == IWEVASSOCREQIE ||
+		     iwe->cmd == IWEVCUSTOM)) {
+			/* WE-19 removed the pointer from struct iw_point */
+			char *dpos = (char *) &iwe_buf.u.data.length;
+			int dlen = dpos - (char *) &iwe_buf;
+			memcpy(dpos, pos + IW_EV_LCP_LEN,
+			       sizeof(struct iw_event) - dlen);
+		} else {
+			memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+			custom += IW_EV_POINT_OFF;
+		}
+
+		switch (iwe->cmd) {
+		case IWEVEXPIRED:
+			drv_event_disassoc(drv->hapd,
+					   (u8 *) iwe->u.addr.sa_data);
+			break;
+		case IWEVREGISTERED:
+			atheros_new_sta(drv, (u8 *) iwe->u.addr.sa_data);
+			break;
+		case IWEVASSOCREQIE:
+			/* Driver hack.. Use IWEVASSOCREQIE to bypass
+			 * IWEVCUSTOM size limitations. Need to handle this
+			 * just like IWEVCUSTOM.
+			 */
+		case IWEVCUSTOM:
+			if (custom + iwe->u.data.length > end)
+				return;
+			buf = malloc(iwe->u.data.length + 1);
+			if (buf == NULL)
+				return;		/* XXX */
+			memcpy(buf, custom, iwe->u.data.length);
+			buf[iwe->u.data.length] = '\0';
+
+			if (iwe->u.data.flags != 0) {
+				atheros_wireless_event_atheros_custom(
+					drv, (int) iwe->u.data.flags,
+					buf, len);
+			} else {
+				atheros_wireless_event_wireless_custom(
+					drv, buf, buf + iwe->u.data.length);
+			}
+			free(buf);
+			break;
+		}
+
+		pos += iwe->len;
+	}
+}
+
+
+static void
+atheros_wireless_event_rtm_newlink(void *ctx,
+				   struct ifinfomsg *ifi, u8 *buf, size_t len)
+{
+	struct atheros_driver_data *drv = ctx;
+	int attrlen, rta_len;
+	struct rtattr *attr;
+
+	if (ifi->ifi_index != drv->ifindex)
+		return;
+
+	attrlen = len;
+	attr = (struct rtattr *) buf;
+
+	rta_len = RTA_ALIGN(sizeof(struct rtattr));
+	while (RTA_OK(attr, attrlen)) {
+		if (attr->rta_type == IFLA_WIRELESS) {
+			atheros_wireless_event_wireless(
+				drv, ((char *) attr) + rta_len,
+				attr->rta_len - rta_len);
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+}
+
+
+static int
+atheros_get_we_version(struct atheros_driver_data *drv)
+{
+	struct iw_range *range;
+	struct iwreq iwr;
+	int minlen;
+	size_t buflen;
+
+	drv->we_version = 0;
+
+	/*
+	 * Use larger buffer than struct iw_range in order to allow the
+	 * structure to grow in the future.
+	 */
+	buflen = sizeof(struct iw_range) + 500;
+	range = os_zalloc(buflen);
+	if (range == NULL)
+		return -1;
+
+	memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.data.pointer = (caddr_t) range;
+	iwr.u.data.length = buflen;
+
+	minlen = ((char *) &range->enc_capa) - (char *) range +
+		sizeof(range->enc_capa);
+
+	if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+			   strerror(errno));
+		os_free(range);
+		return -1;
+	} else if (iwr.u.data.length >= minlen &&
+		   range->we_version_compiled >= 18) {
+		wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
+			   "WE(source)=%d enc_capa=0x%x",
+			   range->we_version_compiled,
+			   range->we_version_source,
+			   range->enc_capa);
+		drv->we_version = range->we_version_compiled;
+	}
+
+	os_free(range);
+	return 0;
+}
+
+
+static int
+atheros_wireless_event_init(struct atheros_driver_data *drv)
+{
+	struct netlink_config *cfg;
+
+	atheros_get_we_version(drv);
+
+	cfg = os_zalloc(sizeof(*cfg));
+	if (cfg == NULL)
+		return -1;
+	cfg->ctx = drv;
+	cfg->newlink_cb = atheros_wireless_event_rtm_newlink;
+	drv->netlink = netlink_init(cfg);
+	if (drv->netlink == NULL) {
+		os_free(cfg);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int
+atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
+		   int encrypt, const u8 *own_addr, u32 flags)
+{
+	struct atheros_driver_data *drv = priv;
+	unsigned char buf[3000];
+	unsigned char *bp = buf;
+	struct l2_ethhdr *eth;
+	size_t len;
+	int status;
+
+	/*
+	 * Prepend the Ethernet header.  If the caller left us
+	 * space at the front we could just insert it but since
+	 * we don't know we copy to a local buffer.  Given the frequency
+	 * and size of frames this probably doesn't matter.
+	 */
+	len = data_len + sizeof(struct l2_ethhdr);
+	if (len > sizeof(buf)) {
+		bp = malloc(len);
+		if (bp == NULL) {
+			wpa_printf(MSG_INFO,
+				   "EAPOL frame discarded, cannot malloc temp buffer of size %lu!",
+				   (unsigned long) len);
+			return -1;
+		}
+	}
+	eth = (struct l2_ethhdr *) bp;
+	memcpy(eth->h_dest, addr, ETH_ALEN);
+	memcpy(eth->h_source, own_addr, ETH_ALEN);
+	eth->h_proto = host_to_be16(ETH_P_EAPOL);
+	memcpy(eth+1, data, data_len);
+
+	wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len);
+
+	status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len);
+
+	if (bp != buf)
+		free(bp);
+	return status;
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+	struct atheros_driver_data *drv = ctx;
+	drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr),
+			   len - sizeof(struct l2_ethhdr));
+}
+
+static void *
+atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params)
+{
+	struct atheros_driver_data *drv;
+	struct ifreq ifr;
+	struct iwreq iwr;
+	char brname[IFNAMSIZ];
+
+	drv = os_zalloc(sizeof(struct atheros_driver_data));
+	if (drv == NULL) {
+		wpa_printf(MSG_INFO,
+			   "Could not allocate memory for atheros driver data");
+		return NULL;
+	}
+
+	drv->hapd = hapd;
+	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (drv->ioctl_sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
+		goto bad;
+	}
+	memcpy(drv->iface, params->ifname, sizeof(drv->iface));
+
+	memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name));
+	if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+			   strerror(errno));
+		goto bad;
+	}
+	drv->ifindex = ifr.ifr_ifindex;
+
+	drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL,
+					handle_read, drv, 1);
+	if (drv->sock_xmit == NULL)
+		goto bad;
+	if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr))
+		goto bad;
+	os_memcpy(drv->own_addr, params->own_addr, ETH_ALEN);
+	if (params->bridge[0]) {
+		wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.",
+			   params->bridge[0]);
+		drv->sock_recv = l2_packet_init(params->bridge[0], NULL,
+						ETH_P_EAPOL, handle_read, drv,
+						1);
+		if (drv->sock_recv == NULL)
+			goto bad;
+	} else if (linux_br_get(brname, drv->iface) == 0) {
+		wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for "
+			   "EAPOL receive", brname);
+		drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL,
+						handle_read, drv, 1);
+		if (drv->sock_recv == NULL)
+			goto bad;
+	} else
+		drv->sock_recv = drv->sock_xmit;
+
+	memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+
+	iwr.u.mode = IW_MODE_MASTER;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "Could not set interface to master mode! ioctl[SIOCSIWMODE]: %s",
+			   strerror(errno));
+		goto bad;
+	}
+
+	/* mark down during setup */
+	linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
+	atheros_set_privacy(drv, 0); /* default to no privacy */
+
+	if (atheros_receive_pkt(drv))
+		goto bad;
+
+	if (atheros_wireless_event_init(drv))
+		goto bad;
+
+	return drv;
+bad:
+	atheros_reset_appfilter(drv);
+	if (drv->sock_raw)
+		l2_packet_deinit(drv->sock_raw);
+	if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit)
+		l2_packet_deinit(drv->sock_recv);
+	if (drv->sock_xmit != NULL)
+		l2_packet_deinit(drv->sock_xmit);
+	if (drv->ioctl_sock >= 0)
+		close(drv->ioctl_sock);
+	os_free(drv);
+	return NULL;
+}
+
+
+static void
+atheros_deinit(void *priv)
+{
+	struct atheros_driver_data *drv = priv;
+
+	atheros_reset_appfilter(drv);
+
+	if (drv->wpa_ie || drv->wps_beacon_ie || drv->wps_probe_resp_ie) {
+		wpabuf_free(drv->wpa_ie);
+		wpabuf_free(drv->wps_beacon_ie);
+		wpabuf_free(drv->wps_probe_resp_ie);
+		atheros_set_opt_ie(priv, NULL, 0);
+	}
+	netlink_deinit(drv->netlink);
+	(void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
+	if (drv->ioctl_sock >= 0)
+		close(drv->ioctl_sock);
+	if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit)
+		l2_packet_deinit(drv->sock_recv);
+	if (drv->sock_xmit != NULL)
+		l2_packet_deinit(drv->sock_xmit);
+	if (drv->sock_raw)
+		l2_packet_deinit(drv->sock_raw);
+	os_free(drv);
+}
+
+static int
+atheros_set_ssid(void *priv, const u8 *buf, int len)
+{
+	struct atheros_driver_data *drv = priv;
+	struct iwreq iwr;
+
+	memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.essid.flags = 1; /* SSID active */
+	iwr.u.essid.pointer = (caddr_t) buf;
+	iwr.u.essid.length = len + 1;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s",
+			   len, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+static int
+atheros_get_ssid(void *priv, u8 *buf, int len)
+{
+	struct atheros_driver_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+
+	memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.essid.pointer = (caddr_t) buf;
+	iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ?
+		IW_ESSID_MAX_SIZE : len;
+
+	if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s",
+			   strerror(errno));
+		ret = -1;
+	} else
+		ret = iwr.u.essid.length;
+
+	return ret;
+}
+
+static int
+atheros_set_countermeasures(void *priv, int enabled)
+{
+	struct atheros_driver_data *drv = priv;
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+	return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled);
+}
+
+static int
+atheros_commit(void *priv)
+{
+	struct atheros_driver_data *drv = priv;
+	return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1);
+}
+
+static int atheros_set_authmode(void *priv, int auth_algs)
+{
+	int authmode;
+
+	if ((auth_algs & WPA_AUTH_ALG_OPEN) &&
+	    (auth_algs & WPA_AUTH_ALG_SHARED))
+		authmode = IEEE80211_AUTH_AUTO;
+	else if (auth_algs & WPA_AUTH_ALG_OPEN)
+		authmode = IEEE80211_AUTH_OPEN;
+	else if (auth_algs & WPA_AUTH_ALG_SHARED)
+		authmode = IEEE80211_AUTH_SHARED;
+	else
+		return -1;
+
+	return set80211param(priv, IEEE80211_PARAM_AUTHMODE, authmode);
+}
+
+static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params)
+{
+	/*
+	 * TODO: Use this to replace set_authmode, set_privacy, set_ieee8021x,
+	 * set_generic_elem, and hapd_set_ssid.
+	 */
+
+	wpa_printf(MSG_DEBUG, "atheros: set_ap - pairwise_ciphers=0x%x "
+		   "group_cipher=0x%x key_mgmt_suites=0x%x auth_algs=0x%x "
+		   "wpa_version=0x%x privacy=%d interworking=%d",
+		   params->pairwise_ciphers, params->group_cipher,
+		   params->key_mgmt_suites, params->auth_algs,
+		   params->wpa_version, params->privacy, params->interworking);
+	wpa_hexdump_ascii(MSG_DEBUG, "atheros: SSID",
+			  params->ssid, params->ssid_len);
+	if (params->hessid)
+		wpa_printf(MSG_DEBUG, "atheros: HESSID " MACSTR,
+			   MAC2STR(params->hessid));
+	wpa_hexdump_buf(MSG_DEBUG, "atheros: beacon_ies",
+			params->beacon_ies);
+	wpa_hexdump_buf(MSG_DEBUG, "atheros: proberesp_ies",
+			params->proberesp_ies);
+	wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies",
+			params->assocresp_ies);
+
+#if defined(CONFIG_HS20) && (defined(IEEE80211_PARAM_OSEN) || defined(CONFIG_ATHEROS_OSEN))
+	if (params->osen) {
+		struct wpa_bss_params bss_params;
+
+		os_memset(&bss_params, 0, sizeof(struct wpa_bss_params));
+		bss_params.enabled = 1;
+		bss_params.wpa = 2;
+		bss_params.wpa_pairwise = WPA_CIPHER_CCMP;
+		bss_params.wpa_group = WPA_CIPHER_CCMP;
+		bss_params.ieee802_1x = 1;
+
+		if (atheros_set_privacy(priv, 1) ||
+		    set80211param(priv, IEEE80211_PARAM_OSEN, 1))
+			return -1;
+
+		return atheros_set_ieee8021x(priv, &bss_params);
+	}
+#endif /* CONFIG_HS20 && IEEE80211_PARAM_OSEN */
+
+	return 0;
+}
+
+
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+
+static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len,
+			     int noack, unsigned int freq)
+{
+	struct atheros_driver_data *drv = priv;
+	u8 buf[1510];
+	const struct ieee80211_mgmt *mgmt;
+	struct ieee80211req_mgmtbuf *mgmt_frm;
+
+	mgmt = (const struct ieee80211_mgmt *) frm;
+	wpa_printf(MSG_DEBUG, "%s frmlen = %lu " MACSTR, __func__,
+		   (unsigned long) data_len, MAC2STR(mgmt->da));
+	mgmt_frm = (struct ieee80211req_mgmtbuf *) buf;
+	memcpy(mgmt_frm->macaddr, (u8 *)mgmt->da, IEEE80211_ADDR_LEN);
+	mgmt_frm->buflen = data_len;
+	if (&mgmt_frm->buf[0] + data_len > buf + sizeof(buf)) {
+		wpa_printf(MSG_INFO, "atheros: Too long frame for "
+			   "atheros_send_mgmt (%u)", (unsigned int) data_len);
+		return -1;
+	}
+	os_memcpy(&mgmt_frm->buf[0], frm, data_len);
+	return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm,
+			    sizeof(struct ieee80211req_mgmtbuf) + data_len);
+}
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+
+
+#ifdef CONFIG_IEEE80211R
+
+static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie,
+			     size_t tspec_ielen)
+{
+	struct atheros_driver_data *drv = priv;
+	int retv;
+	struct ieee80211req_res req;
+	struct ieee80211req_res_addts *addts = &req.u.addts;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+	req.type = IEEE80211_RESREQ_ADDTS;
+	os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN);
+	os_memcpy(addts->tspecie, tspec_ie, tspec_ielen);
+	retv = set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req,
+			    sizeof(struct ieee80211req_res));
+	if (retv < 0) {
+		wpa_printf(MSG_DEBUG, "%s IEEE80211_IOCTL_RES_REQ FAILED "
+			   "retv = %d", __func__, retv);
+		return -1;
+	}
+	os_memcpy(tspec_ie, addts->tspecie, tspec_ielen);
+	return addts->status;
+}
+
+
+static int atheros_add_sta_node(void *priv, const u8 *addr, u16 auth_alg)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_res req;
+	struct ieee80211req_res_addnode *addnode = &req.u.addnode;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+	req.type = IEEE80211_RESREQ_ADDNODE;
+	os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN);
+	addnode->auth_alg = auth_alg;
+	return set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req,
+			    sizeof(struct ieee80211req_res));
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+/* Use only to set a big param, get will not work. */
+static int
+set80211big(struct atheros_driver_data *drv, int op, const void *data, int len)
+{
+	struct iwreq iwr;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+
+	iwr.u.data.pointer = (void *) data;
+	iwr.u.data.length = len;
+	iwr.u.data.flags = op;
+	wpa_printf(MSG_DEBUG, "%s: op=0x%x=%d (%s) len=0x%x",
+		   __func__, op, op, athr_get_param_name(op), len);
+
+	if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) < 0) {
+		wpa_printf(MSG_DEBUG, "%s: op=0x%x (%s) subop=0x%x=%d "
+			   "value=0x%x,0x%x failed: %d (%s)",
+			   __func__, op, athr_get_ioctl_name(op), iwr.u.mode,
+			   iwr.u.mode, iwr.u.data.length,
+			   iwr.u.data.flags, errno, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+
+static int atheros_send_action(void *priv, unsigned int freq,
+			       unsigned int wait,
+			       const u8 *dst, const u8 *src,
+			       const u8 *bssid,
+			       const u8 *data, size_t data_len, int no_cck)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211_p2p_send_action *act;
+	int res;
+
+	act = os_zalloc(sizeof(*act) + data_len);
+	if (act == NULL)
+		return -1;
+	act->freq = freq;
+	os_memcpy(act->dst_addr, dst, ETH_ALEN);
+	os_memcpy(act->src_addr, src, ETH_ALEN);
+	os_memcpy(act->bssid, bssid, ETH_ALEN);
+	os_memcpy(act + 1, data, data_len);
+	wpa_printf(MSG_DEBUG, "%s: freq=%d, wait=%u, dst=" MACSTR ", src="
+		   MACSTR ", bssid=" MACSTR,
+		   __func__, act->freq, wait, MAC2STR(act->dst_addr),
+		   MAC2STR(act->src_addr), MAC2STR(act->bssid));
+	wpa_hexdump(MSG_MSGDUMP, "athr: act", (u8 *) act, sizeof(*act));
+	wpa_hexdump(MSG_MSGDUMP, "athr: data", data, data_len);
+
+	res = set80211big(drv, IEEE80211_IOC_P2P_SEND_ACTION,
+			  act, sizeof(*act) + data_len);
+	os_free(act);
+	return res;
+}
+
+
+#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)
+{
+#define IEEE80211_APPIE_MAX    1024 /* max appie buffer size */
+	u8 buf[IEEE80211_APPIE_MAX];
+	struct ieee80211req_getset_appiebuf *tfs_ie;
+	u16 val;
+
+	wpa_printf(MSG_DEBUG, "atheros: ifname=%s, WNM TFS IE oper=%d " MACSTR,
+		   drv->iface, oper, MAC2STR(peer));
+
+	switch (oper) {
+	case WNM_SLEEP_TFS_REQ_IE_SET:
+		if (*len > IEEE80211_APPIE_MAX -
+		    sizeof(struct ieee80211req_getset_appiebuf)) {
+			wpa_printf(MSG_DEBUG, "TFS Req IE(s) too large");
+			return -1;
+		}
+		tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
+		tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
+		tfs_ie->app_buflen = ETH_ALEN + 2 + 2 + *len;
+
+		/* Command header for driver */
+		os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
+		val = oper;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
+		val = *len;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
+
+		/* copy the ie */
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2 + 2, ie, *len);
+
+		if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie,
+				 IEEE80211_APPIE_MAX)) {
+			wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: "
+				   "%s", __func__, strerror(errno));
+			return -1;
+		}
+		break;
+	case WNM_SLEEP_TFS_RESP_IE_ADD:
+		tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
+		tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
+		tfs_ie->app_buflen = IEEE80211_APPIE_MAX -
+			sizeof(struct ieee80211req_getset_appiebuf);
+		/* Command header for driver */
+		os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
+		val = oper;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
+		val = 0;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
+
+		if (set80211priv(drv, IEEE80211_IOCTL_GET_APPIEBUF, tfs_ie,
+				 IEEE80211_APPIE_MAX)) {
+			wpa_printf(MSG_DEBUG, "%s: Failed to get WNM TFS IE: "
+				   "%s", __func__, strerror(errno));
+			return -1;
+		}
+
+		*len = tfs_ie->app_buflen;
+		os_memcpy(ie, &(tfs_ie->app_buf[0]), *len);
+		wpa_printf(MSG_DEBUG, "atheros: %c len=%d", tfs_ie->app_buf[0],
+			   *len);
+		break;
+	case WNM_SLEEP_TFS_RESP_IE_NONE:
+		*len = 0;
+		break;
+	case WNM_SLEEP_TFS_IE_DEL:
+		tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
+		tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
+		tfs_ie->app_buflen = IEEE80211_APPIE_MAX -
+			sizeof(struct ieee80211req_getset_appiebuf);
+		/* Command header for driver */
+		os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
+		val = oper;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
+		val = 0;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
+
+		if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie,
+				 IEEE80211_APPIE_MAX)) {
+			wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: "
+				   "%s", __func__, strerror(errno));
+			return -1;
+		}
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "Unsupported TFS oper %d", oper);
+		break;
+	}
+
+	return 0;
+}
+
+
+static int atheros_wnm_sleep(struct atheros_driver_data *drv,
+			     const u8 *peer, enum wnm_oper oper)
+{
+	u8 *data, *pos;
+	size_t dlen;
+	int ret;
+	u16 val;
+
+	wpa_printf(MSG_DEBUG, "atheros: WNM-Sleep Oper %d, " MACSTR,
+		   oper, MAC2STR(peer));
+
+	dlen = ETH_ALEN + 2 + 2;
+	data = os_malloc(dlen);
+	if (data == NULL)
+		return -1;
+
+	/* Command header for driver */
+	pos = data;
+	os_memcpy(pos, peer, ETH_ALEN);
+	pos += ETH_ALEN;
+
+	val = oper;
+	os_memcpy(pos, &val, 2);
+	pos += 2;
+
+	val = 0;
+	os_memcpy(pos, &val, 2);
+
+	ret = atheros_set_wps_ie(drv, data, dlen, IEEE80211_APPIE_FRAME_WNM);
+
+	os_free(data);
+
+	return ret;
+}
+
+
+static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer,
+			    u8 *buf, u16 *buf_len)
+{
+	struct atheros_driver_data *drv = priv;
+
+	switch (oper) {
+	case WNM_SLEEP_ENTER_CONFIRM:
+	case WNM_SLEEP_ENTER_FAIL:
+	case WNM_SLEEP_EXIT_CONFIRM:
+	case WNM_SLEEP_EXIT_FAIL:
+		return atheros_wnm_sleep(drv, peer, oper);
+	case WNM_SLEEP_TFS_REQ_IE_SET:
+	case WNM_SLEEP_TFS_RESP_IE_ADD:
+	case WNM_SLEEP_TFS_RESP_IE_NONE:
+	case WNM_SLEEP_TFS_IE_DEL:
+		return athr_wnm_tfs(drv, peer, buf, buf_len, oper);
+	default:
+		wpa_printf(MSG_DEBUG, "atheros: Unsupported WNM operation %d",
+			   oper);
+		return -1;
+	}
+}
+#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
+
+
+const struct wpa_driver_ops wpa_driver_atheros_ops = {
+	.name			= "atheros",
+	.hapd_init		= atheros_init,
+	.hapd_deinit		= atheros_deinit,
+	.set_ieee8021x		= atheros_set_ieee8021x,
+	.set_privacy		= atheros_set_privacy,
+	.set_key		= atheros_set_key,
+	.get_seqnum		= atheros_get_seqnum,
+	.flush			= atheros_flush,
+	.set_generic_elem	= atheros_set_opt_ie,
+	.sta_set_flags		= atheros_sta_set_flags,
+	.read_sta_data		= atheros_read_sta_driver_data,
+	.hapd_send_eapol	= atheros_send_eapol,
+	.sta_disassoc		= atheros_sta_disassoc,
+	.sta_deauth		= atheros_sta_deauth,
+	.hapd_set_ssid		= atheros_set_ssid,
+	.hapd_get_ssid		= atheros_get_ssid,
+	.set_countermeasures	= atheros_set_countermeasures,
+	.sta_clear_stats	= atheros_sta_clear_stats,
+	.commit			= atheros_commit,
+	.set_ap_wps_ie		= atheros_set_ap_wps_ie,
+	.set_authmode		= atheros_set_authmode,
+	.set_ap			= atheros_set_ap,
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+	.sta_assoc              = atheros_sta_assoc,
+	.sta_auth               = atheros_sta_auth,
+	.send_mlme       	= atheros_send_mgmt,
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
+	.add_tspec      	= atheros_add_tspec,
+	.add_sta_node    	= atheros_add_sta_node,
+#endif /* CONFIG_IEEE80211R */
+	.send_action		= atheros_send_action,
+#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM)
+	.wnm_oper		= atheros_wnm_oper,
+#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
+	.set_qos_map		= atheros_set_qos_map,
+};
diff --git a/hostap/src/drivers/driver_bsd.c b/hostap/src/drivers/driver_bsd.c
new file mode 100644
index 0000000..bab1f03
--- /dev/null
+++ b/hostap/src/drivers/driver_bsd.c
@@ -0,0 +1,1646 @@
+/*
+ * WPA Supplicant - driver interaction with BSD net80211 layer
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Copyright (c) 2004, 2Wire, 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 <sys/sysctl.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#ifdef __NetBSD__
+#include <net/if_ether.h>
+#else
+#include <net/ethernet.h>
+#endif
+#include <net/route.h>
+
+#ifdef __DragonFly__
+#include <netproto/802_11/ieee80211_ioctl.h>
+#include <netproto/802_11/ieee80211_dragonfly.h>
+#else /* __DragonFly__ */
+#ifdef __GLIBC__
+#include <netinet/ether.h>
+#endif /* __GLIBC__ */
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_ioctl.h>
+#include <net80211/ieee80211_crypto.h>
+#endif /* __DragonFly__ || __GLIBC__ */
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <net80211/ieee80211_freebsd.h>
+#endif
+#if __NetBSD__
+#include <net80211/ieee80211_netbsd.h>
+#endif
+
+#include "l2_packet/l2_packet.h"
+
+struct bsd_driver_data {
+	struct hostapd_data *hapd;	/* back pointer */
+
+	int	sock;			/* open socket for 802.11 ioctls */
+	struct l2_packet_data *sock_xmit;/* raw packet xmit socket */
+	int	route;			/* routing socket for events */
+	char	ifname[IFNAMSIZ+1];	/* interface name */
+	unsigned int ifindex;		/* interface index */
+	void	*ctx;
+	struct wpa_driver_capa capa;	/* driver capability */
+	int	is_ap;			/* Access point mode */
+	int	prev_roaming;	/* roaming state to restore on deinit */
+	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 int
+bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len)
+{
+	struct bsd_driver_data *drv = priv;
+	struct ieee80211req ireq;
+
+	os_memset(&ireq, 0, sizeof(ireq));
+	os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name));
+	ireq.i_type = op;
+	ireq.i_val = val;
+	ireq.i_data = (void *) arg;
+	ireq.i_len = arg_len;
+
+	if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, "
+			   "arg_len=%u]: %s", op, val, arg_len,
+			   strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+static int
+bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg,
+	     int arg_len)
+{
+	struct bsd_driver_data *drv = priv;
+
+	os_memset(ireq, 0, sizeof(*ireq));
+	os_strlcpy(ireq->i_name, drv->ifname, sizeof(ireq->i_name));
+	ireq->i_type = op;
+	ireq->i_len = arg_len;
+	ireq->i_data = arg;
+
+	if (ioctl(drv->sock, SIOCG80211, ireq) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, "
+			   "arg_len=%u]: %s", op, arg_len, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+static int
+get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len)
+{
+	struct ieee80211req ireq;
+
+	if (bsd_get80211(drv, &ireq, op, arg, arg_len) < 0)
+		return -1;
+	return ireq.i_len;
+}
+
+static int
+set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len)
+{
+	return bsd_set80211(drv, op, 0, arg, arg_len);
+}
+
+static int
+set80211param(struct bsd_driver_data *drv, int op, int arg)
+{
+	return bsd_set80211(drv, op, arg, NULL, 0);
+}
+
+static int
+bsd_get_ssid(void *priv, u8 *ssid, int len)
+{
+	struct bsd_driver_data *drv = priv;
+#ifdef SIOCG80211NWID
+	struct ieee80211_nwid nwid;
+	struct ifreq ifr;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *)&nwid;
+	if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 ||
+	    nwid.i_len > IEEE80211_NWID_LEN)
+		return -1;
+	os_memcpy(ssid, nwid.i_nwid, nwid.i_len);
+	return nwid.i_len;
+#else
+	return get80211var(drv, IEEE80211_IOC_SSID, ssid, IEEE80211_NWID_LEN);
+#endif
+}
+
+static int
+bsd_set_ssid(void *priv, const u8 *ssid, int ssid_len)
+{
+	struct bsd_driver_data *drv = priv;
+#ifdef SIOCS80211NWID
+	struct ieee80211_nwid nwid;
+	struct ifreq ifr;
+
+	os_memcpy(nwid.i_nwid, ssid, ssid_len);
+	nwid.i_len = ssid_len;
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *)&nwid;
+	return ioctl(drv->sock, SIOCS80211NWID, &ifr);
+#else
+	return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len);
+#endif
+}
+
+static int
+bsd_get_if_media(void *priv)
+{
+	struct bsd_driver_data *drv = priv;
+	struct ifmediareq ifmr;
+
+	os_memset(&ifmr, 0, sizeof(ifmr));
+	os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+	if (ioctl(drv->sock, SIOCGIFMEDIA, &ifmr) < 0) {
+		wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__,
+			   strerror(errno));
+		return -1;
+	}
+
+	return ifmr.ifm_current;
+}
+
+static int
+bsd_set_if_media(void *priv, int media)
+{
+	struct bsd_driver_data *drv = priv;
+	struct ifreq ifr;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+	ifr.ifr_media = media;
+
+	if (ioctl(drv->sock, SIOCSIFMEDIA, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__,
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+bsd_set_mediaopt(void *priv, uint32_t mask, uint32_t mode)
+{
+	int media = bsd_get_if_media(priv);
+
+	if (media < 0)
+		return -1;
+	media &= ~mask;
+	media |= mode;
+	if (bsd_set_if_media(priv, media) < 0)
+		return -1;
+	return 0;
+}
+
+static int
+bsd_del_key(void *priv, const u8 *addr, int key_idx)
+{
+	struct ieee80211req_del_key wk;
+
+	os_memset(&wk, 0, sizeof(wk));
+	if (addr == NULL) {
+		wpa_printf(MSG_DEBUG, "%s: key_idx=%d", __func__, key_idx);
+		wk.idk_keyix = key_idx;
+	} else {
+		wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__,
+			   MAC2STR(addr));
+		os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+		wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE;	/* XXX */
+	}
+
+	return set80211var(priv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk));
+}
+
+static int
+bsd_send_mlme_param(void *priv, const u8 op, const u16 reason, const u8 *addr)
+{
+	struct ieee80211req_mlme mlme;
+
+	os_memset(&mlme, 0, sizeof(mlme));
+	mlme.im_op = op;
+	mlme.im_reason = reason;
+	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
+}
+
+static int
+bsd_ctrl_iface(void *priv, int enable)
+{
+	struct bsd_driver_data *drv = priv;
+	struct ifreq ifr;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+
+	if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (enable) {
+		if (ifr.ifr_flags & IFF_UP)
+			return 0;
+		ifr.ifr_flags |= IFF_UP;
+	} else {
+		if (!(ifr.ifr_flags & IFF_UP))
+			return 0;
+		ifr.ifr_flags &= ~IFF_UP;
+	}
+
+	if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+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,
+		   set_tx, seq_len, key_len);
+
+	if (alg == WPA_ALG_NONE) {
+#ifndef HOSTAPD
+		if (addr == NULL || is_broadcast_ether_addr(addr))
+			return bsd_del_key(priv, NULL, key_idx);
+		else
+#endif /* HOSTAPD */
+			return bsd_del_key(priv, addr, key_idx);
+	}
+
+	os_memset(&wk, 0, sizeof(wk));
+	switch (alg) {
+	case WPA_ALG_WEP:
+		wk.ik_type = IEEE80211_CIPHER_WEP;
+		break;
+	case WPA_ALG_TKIP:
+		wk.ik_type = IEEE80211_CIPHER_TKIP;
+		break;
+	case WPA_ALG_CCMP:
+		wk.ik_type = IEEE80211_CIPHER_AES_CCM;
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "%s: unknown alg=%d", __func__, alg);
+		return -1;
+	}
+
+	wk.ik_flags = IEEE80211_KEY_RECV;
+	if (set_tx)
+		wk.ik_flags |= IEEE80211_KEY_XMIT;
+
+	if (addr == NULL) {
+		os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+		wk.ik_keyix = key_idx;
+	} else {
+		os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+		/*
+		 * Deduce whether group/global or unicast key by checking
+		 * the address (yech).  Note also that we can only mark global
+		 * keys default; doing this for a unicast key is an error.
+		 */
+		if (is_broadcast_ether_addr(addr)) {
+			wk.ik_flags |= IEEE80211_KEY_GROUP;
+			wk.ik_keyix = key_idx;
+		} else {
+			wk.ik_keyix = key_idx == 0 ? IEEE80211_KEYIX_NONE :
+				key_idx;
+		}
+	}
+	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) {
+#ifdef WORDS_BIGENDIAN
+		/*
+		 * wk.ik_keyrsc is in host byte order (big endian), need to
+		 * swap it to match with the byte order used in WPA.
+		 */
+		int i;
+		u8 *keyrsc = (u8 *) &wk.ik_keyrsc;
+		for (i = 0; i < seq_len; i++)
+			keyrsc[WPA_KEY_RSC_LEN - i - 1] = seq[i];
+#else /* WORDS_BIGENDIAN */
+		os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+#endif /* WORDS_BIGENDIAN */
+	}
+	os_memcpy(wk.ik_keydata, key, key_len);
+
+	return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk));
+}
+
+static int
+bsd_configure_wpa(void *priv, struct wpa_bss_params *params)
+{
+#ifndef IEEE80211_IOC_APPIE
+	static const char *ciphernames[] =
+		{ "WEP", "TKIP", "AES-OCB", "AES-CCM", "CKIP", "NONE" };
+	int v;
+
+	switch (params->wpa_group) {
+	case WPA_CIPHER_CCMP:
+		v = IEEE80211_CIPHER_AES_CCM;
+		break;
+	case WPA_CIPHER_TKIP:
+		v = IEEE80211_CIPHER_TKIP;
+		break;
+	case WPA_CIPHER_WEP104:
+		v = IEEE80211_CIPHER_WEP;
+		break;
+	case WPA_CIPHER_WEP40:
+		v = IEEE80211_CIPHER_WEP;
+		break;
+	case WPA_CIPHER_NONE:
+		v = IEEE80211_CIPHER_NONE;
+		break;
+	default:
+		wpa_printf(MSG_INFO, "Unknown group key cipher %u",
+			   params->wpa_group);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)",
+		   __func__, ciphernames[v], v);
+	if (set80211param(priv, IEEE80211_IOC_MCASTCIPHER, v)) {
+		wpa_printf(MSG_INFO,
+			   "Unable to set group key cipher to %u (%s)",
+			   v, ciphernames[v]);
+		return -1;
+	}
+	if (v == IEEE80211_CIPHER_WEP) {
+		/* key length is done only for specific ciphers */
+		v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
+		if (set80211param(priv, IEEE80211_IOC_MCASTKEYLEN, v)) {
+			wpa_printf(MSG_INFO,
+				   "Unable to set group key length to %u", v);
+			return -1;
+		}
+	}
+
+	v = 0;
+	if (params->wpa_pairwise & WPA_CIPHER_CCMP)
+		v |= 1<<IEEE80211_CIPHER_AES_CCM;
+	if (params->wpa_pairwise & WPA_CIPHER_TKIP)
+		v |= 1<<IEEE80211_CIPHER_TKIP;
+	if (params->wpa_pairwise & WPA_CIPHER_NONE)
+		v |= 1<<IEEE80211_CIPHER_NONE;
+	wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
+	if (set80211param(priv, IEEE80211_IOC_UCASTCIPHERS, v)) {
+		wpa_printf(MSG_INFO,
+			   "Unable to set pairwise key ciphers to 0x%x", v);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
+		   __func__, params->wpa_key_mgmt);
+	if (set80211param(priv, IEEE80211_IOC_KEYMGTALGS,
+			  params->wpa_key_mgmt)) {
+		wpa_printf(MSG_INFO,
+			   "Unable to set key management algorithms to 0x%x",
+			   params->wpa_key_mgmt);
+		return -1;
+	}
+
+	v = 0;
+	if (params->rsn_preauth)
+		v |= BIT(0);
+	wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
+		   __func__, params->rsn_preauth);
+	if (set80211param(priv, IEEE80211_IOC_RSNCAPS, v)) {
+		wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x",
+			   v);
+		return -1;
+	}
+#endif /* IEEE80211_IOC_APPIE */
+
+	wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, params->wpa);
+	if (set80211param(priv, IEEE80211_IOC_WPA, params->wpa)) {
+		wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+bsd_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
+
+	if (!params->enabled) {
+		/* XXX restore state */
+		return set80211param(priv, IEEE80211_IOC_AUTHMODE,
+				     IEEE80211_AUTH_AUTO);
+	}
+	if (!params->wpa && !params->ieee802_1x) {
+		wpa_printf(MSG_ERROR, "%s: No 802.1X or WPA enabled",
+			   __func__);
+		return -1;
+	}
+	if (params->wpa && bsd_configure_wpa(priv, params) != 0) {
+		wpa_printf(MSG_ERROR, "%s: Failed to configure WPA state",
+			   __func__);
+		return -1;
+	}
+	if (set80211param(priv, IEEE80211_IOC_AUTHMODE,
+		(params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
+		wpa_printf(MSG_ERROR, "%s: Failed to enable WPA/802.1X",
+			   __func__);
+		return -1;
+	}
+	return bsd_ctrl_iface(priv, 1);
+}
+
+static void
+bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
+{
+	struct ieee80211req_wpaie ie;
+	int ielen = 0;
+	u8 *iebuf = NULL;
+
+	/*
+	 * Fetch and validate any negotiated WPA/RSN parameters.
+	 */
+	memset(&ie, 0, sizeof(ie));
+	memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
+	if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) {
+		wpa_printf(MSG_INFO,
+			   "Failed to get WPA/RSN information element");
+		goto no_ie;
+	}
+	iebuf = ie.wpa_ie;
+	ielen = ie.wpa_ie[1];
+	if (ielen == 0)
+		iebuf = NULL;
+	else
+		ielen += 2;
+
+no_ie:
+	drv_event_assoc(ctx, addr, iebuf, ielen, 0);
+}
+
+static int
+bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
+	       int encrypt, const u8 *own_addr, u32 flags)
+{
+	struct bsd_driver_data *drv = priv;
+
+	wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", data, data_len);
+
+	return l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, data,
+			      data_len);
+}
+
+static int
+bsd_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+	struct bsd_driver_data *drv = priv;
+#ifdef SIOCS80211CHANNEL
+	struct ieee80211chanreq creq;
+#endif /* SIOCS80211CHANNEL */
+	u32 mode;
+	int channel = freq->channel;
+
+	if (channel < 14) {
+		mode =
+#ifdef CONFIG_IEEE80211N
+			freq->ht_enabled ? IFM_IEEE80211_11NG :
+#endif /* CONFIG_IEEE80211N */
+		        IFM_IEEE80211_11G;
+	} else if (channel == 14) {
+		mode = IFM_IEEE80211_11B;
+	} else {
+		mode =
+#ifdef CONFIG_IEEE80211N
+			freq->ht_enabled ? IFM_IEEE80211_11NA :
+#endif /* CONFIG_IEEE80211N */
+			IFM_IEEE80211_11A;
+	}
+	if (bsd_set_mediaopt(drv, IFM_MMASK, mode) < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to set modulation mode",
+			   __func__);
+		return -1;
+	}
+
+#ifdef SIOCS80211CHANNEL
+	os_memset(&creq, 0, sizeof(creq));
+	os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name));
+	creq.i_channel = (u_int16_t)channel;
+	return ioctl(drv->sock, SIOCS80211CHANNEL, &creq);
+#else /* SIOCS80211CHANNEL */
+	return set80211param(priv, IEEE80211_IOC_CHANNEL, channel);
+#endif /* SIOCS80211CHANNEL */
+}
+
+static int
+bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
+{
+#ifdef IEEE80211_IOC_APPIE
+	wpa_printf(MSG_DEBUG, "%s: set WPA+RSN ie (len %lu)", __func__,
+		   (unsigned long)ie_len);
+	return bsd_set80211(priv, IEEE80211_IOC_APPIE, IEEE80211_APPIE_WPA,
+			    ie, ie_len);
+#endif /* IEEE80211_IOC_APPIE */
+	return 0;
+}
+
+static size_t
+rtbuf_len(void)
+{
+	size_t len;
+
+	int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0};
+
+	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
+		wpa_printf(MSG_WARNING, "%s failed: %s", __func__,
+			   strerror(errno));
+		len = 2048;
+	}
+
+	return len;
+}
+
+#ifdef HOSTAPD
+
+/*
+ * Avoid conflicts with hostapd definitions by undefining couple of defines
+ * from net80211 header files.
+ */
+#undef RSN_VERSION
+#undef WPA_VERSION
+#undef WPA_OUI_TYPE
+
+static int bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+			  int reason_code);
+
+static const char *
+ether_sprintf(const u8 *addr)
+{
+	static char buf[sizeof(MACSTR)];
+
+	if (addr != NULL)
+		snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+	else
+		snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
+	return buf;
+}
+
+static int
+bsd_set_privacy(void *priv, int enabled)
+{
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+	return set80211param(priv, IEEE80211_IOC_PRIVACY, enabled);
+}
+
+static int
+bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+	       u8 *seq)
+{
+	struct ieee80211req_key wk;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
+		   __func__, ether_sprintf(addr), idx);
+
+	memset(&wk, 0, sizeof(wk));
+	if (addr == NULL)
+		memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+	else
+		memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+	wk.ik_keyix = idx;
+
+	if (get80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) {
+		wpa_printf(MSG_INFO, "Failed to get encryption");
+		return -1;
+	}
+
+#ifdef WORDS_BIGENDIAN
+	{
+		/*
+		 * wk.ik_keytsc is in host byte order (big endian), need to
+		 * swap it to match with the byte order used in WPA.
+		 */
+		int i;
+		u8 tmp[WPA_KEY_RSC_LEN];
+		memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+		for (i = 0; i < WPA_KEY_RSC_LEN; i++) {
+			seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1];
+		}
+	}
+#else /* WORDS_BIGENDIAN */
+	memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+#endif /* WORDS_BIGENDIAN */
+	return 0;
+}
+
+
+static int 
+bsd_flush(void *priv)
+{
+	u8 allsta[IEEE80211_ADDR_LEN];
+
+	memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+	return bsd_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE);
+}
+
+
+static int
+bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
+			 const u8 *addr)
+{
+	struct ieee80211req_sta_stats stats;
+
+	memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
+	if (get80211var(priv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats))
+	    > 0) {
+		/* XXX? do packets counts include non-data frames? */
+		data->rx_packets = stats.is_stats.ns_rx_data;
+		data->rx_bytes = stats.is_stats.ns_rx_bytes;
+		data->tx_packets = stats.is_stats.ns_tx_data;
+		data->tx_bytes = stats.is_stats.ns_tx_bytes;
+	}
+	return 0;
+}
+
+static int
+bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code)
+{
+	return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
+				   addr);
+}
+
+static int
+bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+		 int reason_code)
+{
+	return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code,
+				   addr);
+}
+
+static void
+bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
+{
+	struct bsd_driver_data *drv = ctx;
+	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;
+	union wpa_event_data data;
+
+	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",
+				   __func__, strerror(errno));
+		return;
+	}
+
+	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);
+		return;
+	}
+	ifan = (struct if_announcemsghdr *) rtm;
+	switch (rtm->rtm_type) {
+	case RTM_IEEE80211:
+		switch (ifan->ifan_what) {
+		case RTM_IEEE80211_ASSOC:
+		case RTM_IEEE80211_REASSOC:
+		case RTM_IEEE80211_DISASSOC:
+		case RTM_IEEE80211_SCAN:
+			break;
+		case RTM_IEEE80211_LEAVE:
+			leave = (struct ieee80211_leave_event *) &ifan[1];
+			drv_event_disassoc(drv->hapd, leave->iev_addr);
+			break;
+		case RTM_IEEE80211_JOIN:
+#ifdef RTM_IEEE80211_REJOIN
+		case RTM_IEEE80211_REJOIN:
+#endif
+			join = (struct ieee80211_join_event *) &ifan[1];
+			bsd_new_sta(drv, drv->hapd, join->iev_addr);
+			break;
+		case RTM_IEEE80211_REPLAY:
+			/* ignore */
+			break;
+		case RTM_IEEE80211_MICHAEL:
+			mic = (struct ieee80211_michael_event *) &ifan[1];
+			wpa_printf(MSG_DEBUG,
+				"Michael MIC failure wireless event: "
+				"keyix=%u src_addr=" MACSTR, mic->iev_keyix,
+				MAC2STR(mic->iev_src));
+			os_memset(&data, 0, sizeof(data));
+			data.michael_mic_failure.unicast = 1;
+			data.michael_mic_failure.src = mic->iev_src;
+			wpa_supplicant_event(drv->hapd,
+					     EVENT_MICHAEL_MIC_FAILURE, &data);
+			break;
+		}
+		break;
+	}
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+	struct bsd_driver_data *drv = ctx;
+	drv_event_eapol_rx(drv->hapd, src_addr, buf, len);
+}
+
+static void *
+bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params)
+{
+	struct bsd_driver_data *drv;
+
+	drv = os_zalloc(sizeof(struct bsd_driver_data));
+	if (drv == NULL) {
+		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;
+	}
+
+	drv->hapd = hapd;
+	drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (drv->sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
+		goto bad;
+	}
+	os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
+
+	drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
+					handle_read, drv, 0);
+	if (drv->sock_xmit == NULL)
+		goto bad;
+	if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr))
+		goto bad;
+
+	/* mark down during setup */
+	if (bsd_ctrl_iface(drv, 0) < 0)
+		goto bad;
+
+	drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
+	if (drv->route < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_ROUTE,SOCK_RAW): %s",
+			   strerror(errno));
+		goto bad;
+	}
+	eloop_register_read_sock(drv->route, bsd_wireless_event_receive, drv,
+				 NULL);
+
+	if (bsd_set_mediaopt(drv, IFM_OMASK, IFM_IEEE80211_HOSTAP) < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+			   __func__);
+		goto bad;
+	}
+
+	return drv;
+bad:
+	if (drv->sock_xmit != NULL)
+		l2_packet_deinit(drv->sock_xmit);
+	if (drv->sock >= 0)
+		close(drv->sock);
+	os_free(drv->event_buf);
+	os_free(drv);
+	return NULL;
+}
+
+
+static void
+bsd_deinit(void *priv)
+{
+	struct bsd_driver_data *drv = priv;
+
+	if (drv->route >= 0) {
+		eloop_unregister_read_sock(drv->route);
+		close(drv->route);
+	}
+	bsd_ctrl_iface(drv, 0);
+	if (drv->sock >= 0)
+		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,
+		       unsigned int total_flags, unsigned int flags_or,
+		       unsigned 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
+get80211param(struct bsd_driver_data *drv, int op)
+{
+	struct ieee80211req ireq;
+
+	if (bsd_get80211(drv, &ireq, op, NULL, 0) < 0)
+		return -1;
+	return ireq.i_val;
+}
+
+static int
+wpa_driver_bsd_get_bssid(void *priv, u8 *bssid)
+{
+	struct bsd_driver_data *drv = priv;
+#ifdef SIOCG80211BSSID
+	struct ieee80211_bssid bs;
+
+	os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name));
+	if (ioctl(drv->sock, SIOCG80211BSSID, &bs) < 0)
+		return -1;
+	os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid));
+	return 0;
+#else
+	return get80211var(drv, IEEE80211_IOC_BSSID,
+		bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0;
+#endif
+}
+
+static int
+wpa_driver_bsd_get_ssid(void *priv, u8 *ssid)
+{
+	struct bsd_driver_data *drv = priv;
+	return bsd_get_ssid(drv, ssid, 0);
+}
+
+static int
+wpa_driver_bsd_set_wpa_ie(struct bsd_driver_data *drv, const u8 *wpa_ie,
+			  size_t wpa_ie_len)
+{
+#ifdef IEEE80211_IOC_APPIE
+	return bsd_set_opt_ie(drv, wpa_ie, wpa_ie_len);
+#else /* IEEE80211_IOC_APPIE */
+	return set80211var(drv, IEEE80211_IOC_OPTIE, wpa_ie, wpa_ie_len);
+#endif /* IEEE80211_IOC_APPIE */
+}
+
+static int
+wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy)
+{
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d",
+		__FUNCTION__, wpa, privacy);
+
+	if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0)
+		ret = -1;
+	if (set80211param(priv, IEEE80211_IOC_PRIVACY, privacy) < 0)
+		ret = -1;
+	if (set80211param(priv, IEEE80211_IOC_WPA, wpa) < 0)
+		ret = -1;
+
+	return ret;
+}
+
+static int
+wpa_driver_bsd_set_wpa(void *priv, int enabled)
+{
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+
+	return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled);
+}
+
+static int
+wpa_driver_bsd_set_countermeasures(void *priv, int enabled)
+{
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+	return set80211param(priv, IEEE80211_IOC_COUNTERMEASURES, enabled);
+}
+
+
+static int
+wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled)
+{
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+	return set80211param(priv, IEEE80211_IOC_DROPUNENCRYPTED, enabled);
+}
+
+static int
+wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code)
+{
+	return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
+				   addr);
+}
+
+static int
+wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg)
+{
+	int authmode;
+
+	if ((auth_alg & WPA_AUTH_ALG_OPEN) &&
+	    (auth_alg & WPA_AUTH_ALG_SHARED))
+		authmode = IEEE80211_AUTH_AUTO;
+	else if (auth_alg & WPA_AUTH_ALG_SHARED)
+		authmode = IEEE80211_AUTH_SHARED;
+	else
+		authmode = IEEE80211_AUTH_OPEN;
+
+	return set80211param(priv, IEEE80211_IOC_AUTHMODE, authmode);
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+	struct bsd_driver_data *drv = ctx;
+
+	drv_event_eapol_rx(drv->ctx, src_addr, buf, len);
+}
+
+static int
+wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params)
+{
+	struct bsd_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	u32 mode;
+	int privacy;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG,
+		"%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u"
+		, __func__
+		   , (unsigned int) params->ssid_len, params->ssid
+		, (unsigned int) params->wpa_ie_len
+		, params->pairwise_suite
+		, params->group_suite
+		, params->key_mgmt_suite
+	);
+
+	switch (params->mode) {
+	case IEEE80211_MODE_INFRA:
+		mode = 0 /* STA */;
+		break;
+	case IEEE80211_MODE_IBSS:
+		mode = IFM_IEEE80211_IBSS;
+		break;
+	case IEEE80211_MODE_AP:
+		mode = IFM_IEEE80211_HOSTAP;
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "%s: unknown operation mode", __func__);
+		return -1;
+	}
+	if (bsd_set_mediaopt(drv, IFM_OMASK, mode) < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+			   __func__);
+		return -1;
+	}
+
+	if (params->mode == IEEE80211_MODE_AP) {
+		drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
+						handle_read, drv, 0);
+		if (drv->sock_xmit == NULL)
+			return -1;
+		drv->is_ap = 1;
+		return 0;
+	}
+
+	if (wpa_driver_bsd_set_drop_unencrypted(drv, params->drop_unencrypted)
+	    < 0)
+		ret = -1;
+	if (wpa_driver_bsd_set_auth_alg(drv, params->auth_alg) < 0)
+		ret = -1;
+	/* XXX error handling is wrong but unclear what to do... */
+	if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
+		return -1;
+
+	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);
+
+	if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0)
+		return -1;
+
+	if (params->wpa_ie_len &&
+	    set80211param(drv, IEEE80211_IOC_WPA,
+			  params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0)
+		return -1;
+
+	os_memset(&mlme, 0, sizeof(mlme));
+	mlme.im_op = IEEE80211_MLME_ASSOC;
+	if (params->ssid != NULL)
+		os_memcpy(mlme.im_ssid, params->ssid, params->ssid_len);
+	mlme.im_ssid_len = params->ssid_len;
+	if (params->bssid != NULL)
+		os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN);
+	if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0)
+		return -1;
+	return ret;
+}
+
+static int
+wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params)
+{
+	struct bsd_driver_data *drv = priv;
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+	struct ieee80211_scan_req sr;
+	int i;
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+
+	if (bsd_set_mediaopt(drv, IFM_OMASK, 0 /* STA */) < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+			   __func__);
+		return -1;
+	}
+
+	if (set80211param(drv, IEEE80211_IOC_ROAMING,
+			  IEEE80211_ROAMING_MANUAL) < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to set "
+			   "wpa_supplicant-based roaming: %s", __func__,
+			   strerror(errno));
+		return -1;
+	}
+
+	if (wpa_driver_bsd_set_wpa(drv, 1) < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to set wpa: %s", __func__,
+			   strerror(errno));
+		return -1;
+	}
+
+	/* NB: interface must be marked UP to do a scan */
+	if (bsd_ctrl_iface(drv, 1) < 0)
+		return -1;
+
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+	os_memset(&sr, 0, sizeof(sr));
+	sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE |
+		IEEE80211_IOC_SCAN_NOJOIN;
+	sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
+	if (params->num_ssids > 0) {
+		sr.sr_nssid = params->num_ssids;
+#if 0
+		/* Boundary check is done by upper layer */
+		if (sr.sr_nssid > IEEE80211_IOC_SCAN_MAX_SSID)
+			sr.sr_nssid = IEEE80211_IOC_SCAN_MAX_SSID;
+#endif
+
+		/* NB: check scan cache first */
+		sr.sr_flags |= IEEE80211_IOC_SCAN_CHECK;
+	}
+	for (i = 0; i < sr.sr_nssid; i++) {
+		sr.sr_ssid[i].len = params->ssids[i].ssid_len;
+		os_memcpy(sr.sr_ssid[i].ssid, params->ssids[i].ssid,
+			  sr.sr_ssid[i].len);
+	}
+
+	/* NB: net80211 delivers a scan complete event so no need to poll */
+	return set80211var(drv, IEEE80211_IOC_SCAN_REQ, &sr, sizeof(sr));
+#else /* IEEE80211_IOC_SCAN_MAX_SSID */
+	/* set desired ssid before scan */
+	if (bsd_set_ssid(drv, params->ssids[0].ssid,
+			 params->ssids[0].ssid_len) < 0)
+		return -1;
+
+	/* NB: net80211 delivers a scan complete event so no need to poll */
+	return set80211param(drv, IEEE80211_IOC_SCAN_REQ, 0);
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+}
+
+static void
+wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
+{
+	struct bsd_driver_data *drv = sock_ctx;
+	struct if_announcemsghdr *ifan;
+	struct if_msghdr *ifm;
+	struct rt_msghdr *rtm;
+	union wpa_event_data event;
+	struct ieee80211_michael_event *mic;
+	struct ieee80211_leave_event *leave;
+	struct ieee80211_join_event *join;
+	int n;
+
+	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",
+				   __func__, strerror(errno));
+		return;
+	}
+
+	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);
+		return;
+	}
+	os_memset(&event, 0, sizeof(event));
+	switch (rtm->rtm_type) {
+	case RTM_IFANNOUNCE:
+		ifan = (struct if_announcemsghdr *) rtm;
+		if (ifan->ifan_index != drv->ifindex)
+			break;
+		os_strlcpy(event.interface_status.ifname, drv->ifname,
+			   sizeof(event.interface_status.ifname));
+		switch (ifan->ifan_what) {
+		case IFAN_DEPARTURE:
+			event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+		default:
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
+			   event.interface_status.ifname,
+			   ifan->ifan_what == IFAN_DEPARTURE ?
+				"removed" : "added");
+		wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+		break;
+	case RTM_IEEE80211:
+		ifan = (struct if_announcemsghdr *) rtm;
+		if (ifan->ifan_index != drv->ifindex)
+			break;
+		switch (ifan->ifan_what) {
+		case RTM_IEEE80211_ASSOC:
+		case RTM_IEEE80211_REASSOC:
+			if (drv->is_ap)
+				break;
+			wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
+			break;
+		case RTM_IEEE80211_DISASSOC:
+			if (drv->is_ap)
+				break;
+			wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
+			break;
+		case RTM_IEEE80211_SCAN:
+			if (drv->is_ap)
+				break;
+			wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL);
+			break;
+		case RTM_IEEE80211_LEAVE:
+			leave = (struct ieee80211_leave_event *) &ifan[1];
+			drv_event_disassoc(ctx, leave->iev_addr);
+			break;
+		case RTM_IEEE80211_JOIN:
+#ifdef RTM_IEEE80211_REJOIN
+		case RTM_IEEE80211_REJOIN:
+#endif
+			join = (struct ieee80211_join_event *) &ifan[1];
+			bsd_new_sta(drv, ctx, join->iev_addr);
+			break;
+		case RTM_IEEE80211_REPLAY:
+			/* ignore */
+			break;
+		case RTM_IEEE80211_MICHAEL:
+			mic = (struct ieee80211_michael_event *) &ifan[1];
+			wpa_printf(MSG_DEBUG,
+				"Michael MIC failure wireless event: "
+				"keyix=%u src_addr=" MACSTR, mic->iev_keyix,
+				MAC2STR(mic->iev_src));
+
+			os_memset(&event, 0, sizeof(event));
+			event.michael_mic_failure.unicast =
+				!IEEE80211_IS_MULTICAST(mic->iev_dst);
+			wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE,
+				&event);
+			break;
+		}
+		break;
+	case RTM_IFINFO:
+		ifm = (struct if_msghdr *) rtm;
+		if (ifm->ifm_index != drv->ifindex)
+			break;
+		if ((rtm->rtm_flags & RTF_UP) == 0) {
+			os_strlcpy(event.interface_status.ifname, drv->ifname,
+				   sizeof(event.interface_status.ifname));
+			event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+			wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
+				   event.interface_status.ifname);
+			wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+		}
+		break;
+	}
+}
+
+static void
+wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res,
+			      struct ieee80211req_scan_result *sr)
+{
+	struct wpa_scan_res *result, **tmp;
+	size_t extra_len;
+	u8 *pos;
+
+	extra_len = 2 + sr->isr_ssid_len;
+	extra_len += 2 + sr->isr_nrates;
+	extra_len += 3; /* ERP IE */
+	extra_len += sr->isr_ie_len;
+
+	result = os_zalloc(sizeof(*result) + extra_len);
+	if (result == NULL)
+		return;
+	os_memcpy(result->bssid, sr->isr_bssid, ETH_ALEN);
+	result->freq = sr->isr_freq;
+	result->beacon_int = sr->isr_intval;
+	result->caps = sr->isr_capinfo;
+	result->qual = sr->isr_rssi;
+	result->noise = sr->isr_noise;
+	/*
+	 * the rssi value reported by the kernel is in 0.5dB steps relative to
+	 * the reported noise floor. see ieee80211_node.h for details.
+	 */
+	result->level = sr->isr_rssi / 2 + sr->isr_noise;
+
+	pos = (u8 *)(result + 1);
+
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = sr->isr_ssid_len;
+	os_memcpy(pos, sr + 1, sr->isr_ssid_len);
+	pos += sr->isr_ssid_len;
+
+	/*
+	 * Deal all rates as supported rate.
+	 * Because net80211 doesn't report extended supported rate or not.
+	 */
+	*pos++ = WLAN_EID_SUPP_RATES;
+	*pos++ = sr->isr_nrates;
+	os_memcpy(pos, sr->isr_rates, sr->isr_nrates);
+	pos += sr->isr_nrates;
+
+	*pos++ = WLAN_EID_ERP_INFO;
+	*pos++ = 1;
+	*pos++ = sr->isr_erp;
+
+#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+	os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len + sr->isr_meshid_len,
+		  sr->isr_ie_len);
+#else
+	os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len);
+#endif
+	pos += sr->isr_ie_len;
+
+	result->ie_len = pos - (u8 *)(result + 1);
+
+	tmp = os_realloc_array(res->res, res->num + 1,
+			       sizeof(struct wpa_scan_res *));
+	if (tmp == NULL) {
+		os_free(result);
+		return;
+	}
+	tmp[res->num++] = result;
+	res->res = tmp;
+}
+
+struct wpa_scan_results *
+wpa_driver_bsd_get_scan_results2(void *priv)
+{
+	struct ieee80211req_scan_result *sr;
+	struct wpa_scan_results *res;
+	int len, rest;
+	uint8_t buf[24*1024], *pos;
+
+	len = get80211var(priv, IEEE80211_IOC_SCAN_RESULTS, buf, 24*1024);
+	if (len < 0)
+		return NULL;
+
+	res = os_zalloc(sizeof(*res));
+	if (res == NULL)
+		return NULL;
+
+	pos = buf;
+	rest = len;
+	while (rest >= sizeof(struct ieee80211req_scan_result)) {
+		sr = (struct ieee80211req_scan_result *)pos;
+		wpa_driver_bsd_add_scan_entry(res, sr);
+		pos += sr->isr_len;
+		rest -= sr->isr_len;
+	}
+
+	wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)",
+		   len, (unsigned long)res->num);
+
+	return res;
+}
+
+static int wpa_driver_bsd_capa(struct bsd_driver_data *drv)
+{
+#ifdef IEEE80211_IOC_DEVCAPS
+/* kernel definitions copied from net80211/ieee80211_var.h */
+#define IEEE80211_CIPHER_WEP            0
+#define IEEE80211_CIPHER_TKIP           1
+#define IEEE80211_CIPHER_AES_CCM        3
+#define IEEE80211_CRYPTO_WEP            (1<<IEEE80211_CIPHER_WEP)
+#define IEEE80211_CRYPTO_TKIP           (1<<IEEE80211_CIPHER_TKIP)
+#define IEEE80211_CRYPTO_AES_CCM        (1<<IEEE80211_CIPHER_AES_CCM)
+#define IEEE80211_C_HOSTAP      0x00000400      /* CAPABILITY: HOSTAP avail */
+#define IEEE80211_C_WPA1        0x00800000      /* CAPABILITY: WPA1 avail */
+#define IEEE80211_C_WPA2        0x01000000      /* CAPABILITY: WPA2 avail */
+	struct ieee80211_devcaps_req devcaps;
+
+	if (get80211var(drv, IEEE80211_IOC_DEVCAPS, &devcaps,
+			sizeof(devcaps)) < 0) {
+		wpa_printf(MSG_ERROR, "failed to IEEE80211_IOC_DEVCAPS: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: drivercaps=0x%08x,cryptocaps=0x%08x",
+		   __func__, devcaps.dc_drivercaps, devcaps.dc_cryptocaps);
+
+	if (devcaps.dc_drivercaps & IEEE80211_C_WPA1)
+		drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+			WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+	if (devcaps.dc_drivercaps & IEEE80211_C_WPA2)
+		drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+			WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+
+	if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_WEP)
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
+			WPA_DRIVER_CAPA_ENC_WEP104;
+	if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_TKIP)
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+	if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_AES_CCM)
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+
+	if (devcaps.dc_drivercaps & IEEE80211_C_HOSTAP)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+#undef IEEE80211_CIPHER_WEP
+#undef IEEE80211_CIPHER_TKIP
+#undef IEEE80211_CIPHER_AES_CCM
+#undef IEEE80211_CRYPTO_WEP
+#undef IEEE80211_CRYPTO_TKIP
+#undef IEEE80211_CRYPTO_AES_CCM
+#undef IEEE80211_C_HOSTAP
+#undef IEEE80211_C_WPA1
+#undef IEEE80211_C_WPA2
+#else /* IEEE80211_IOC_DEVCAPS */
+	/* 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.flags |= WPA_DRIVER_FLAGS_AP;
+#endif /* IEEE80211_IOC_DEVCAPS */
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+	drv->capa.max_scan_ssids = IEEE80211_IOC_SCAN_MAX_SSID;
+#else /* IEEE80211_IOC_SCAN_MAX_SSID */
+	drv->capa.max_scan_ssids = 1;
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+	drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+		WPA_DRIVER_AUTH_SHARED |
+		WPA_DRIVER_AUTH_LEAP;
+	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)
+{
+#define	GETPARAM(drv, param, v) \
+	(((v) = get80211param(drv, param)) != -1)
+	struct bsd_driver_data *drv;
+
+	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
+	 *     wait for an interface to appear.  This seems ok; that
+	 *     doesn't belong here; it's really the job of devd.
+	 */
+	drv->ifindex = if_nametoindex(ifname);
+	if (drv->ifindex == 0) {
+		wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
+			   __func__, ifname);
+		goto fail1;
+	}
+	drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (drv->sock < 0)
+		goto fail1;
+
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+	/* Down interface during setup. */
+	if (bsd_ctrl_iface(drv, 0) < 0)
+		goto fail;
+
+	drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
+	if (drv->route < 0)
+		goto fail;
+	eloop_register_read_sock(drv->route,
+		wpa_driver_bsd_event_receive, ctx, drv);
+
+	drv->ctx = ctx;
+
+	if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
+		wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
+			__func__, strerror(errno));
+		goto fail;
+	}
+	if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) {
+		wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s",
+			__func__, strerror(errno));
+		goto fail;
+	}
+	if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) {
+		wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s",
+			__func__, strerror(errno));
+		goto fail;
+	}
+
+	if (wpa_driver_bsd_capa(drv))
+		goto fail;
+
+	drv->opmode = get80211opmode(drv);
+
+	return drv;
+fail:
+	close(drv->sock);
+fail1:
+	os_free(drv->event_buf);
+	os_free(drv);
+	return NULL;
+#undef GETPARAM
+}
+
+static void
+wpa_driver_bsd_deinit(void *priv)
+{
+	struct bsd_driver_data *drv = priv;
+
+	wpa_driver_bsd_set_wpa(drv, 0);
+	eloop_unregister_read_sock(drv->route);
+
+	/* NB: mark interface down */
+	bsd_ctrl_iface(drv, 0);
+
+	wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy);
+	if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0)
+		wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state",
+			__func__);
+
+	if (drv->sock_xmit != NULL)
+		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);
+}
+
+static int
+wpa_driver_bsd_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+	struct bsd_driver_data *drv = priv;
+
+	os_memcpy(capa, &drv->capa, sizeof(*capa));
+	return 0;
+}
+#endif /* HOSTAPD */
+
+
+const struct wpa_driver_ops wpa_driver_bsd_ops = {
+	.name			= "bsd",
+	.desc			= "BSD 802.11 support",
+#ifdef HOSTAPD
+	.hapd_init		= bsd_init,
+	.hapd_deinit		= bsd_deinit,
+	.set_privacy		= bsd_set_privacy,
+	.get_seqnum		= bsd_get_seqnum,
+	.flush			= bsd_flush,
+	.read_sta_data		= bsd_read_sta_driver_data,
+	.sta_disassoc		= bsd_sta_disassoc,
+	.sta_deauth		= bsd_sta_deauth,
+	.sta_set_flags		= bsd_set_sta_authorized,
+	.commit			= bsd_commit,
+#else /* HOSTAPD */
+	.init			= wpa_driver_bsd_init,
+	.deinit			= wpa_driver_bsd_deinit,
+	.get_bssid		= wpa_driver_bsd_get_bssid,
+	.get_ssid		= wpa_driver_bsd_get_ssid,
+	.set_countermeasures	= wpa_driver_bsd_set_countermeasures,
+	.scan2			= wpa_driver_bsd_scan,
+	.get_scan_results2	= wpa_driver_bsd_get_scan_results2,
+	.deauthenticate		= wpa_driver_bsd_deauthenticate,
+	.associate		= wpa_driver_bsd_associate,
+	.get_capa		= wpa_driver_bsd_get_capa,
+#endif /* HOSTAPD */
+	.set_freq		= bsd_set_freq,
+	.set_key		= bsd_set_key,
+	.set_ieee8021x		= bsd_set_ieee8021x,
+	.hapd_set_ssid		= bsd_set_ssid,
+	.hapd_get_ssid		= bsd_get_ssid,
+	.hapd_send_eapol	= bsd_send_eapol,
+	.set_generic_elem	= bsd_set_opt_ie,
+};
diff --git a/hostap/src/drivers/driver_common.c b/hostap/src/drivers/driver_common.c
new file mode 100644
index 0000000..aebea8c
--- /dev/null
+++ b/hostap/src/drivers/driver_common.c
@@ -0,0 +1,220 @@
+/*
+ * Common driver-related functions
+ * Copyright (c) 2003-2011, 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 "utils/common.h"
+#include "driver.h"
+
+void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+	size_t i;
+
+	if (res == NULL)
+		return;
+
+	for (i = 0; i < res->num; i++)
+		os_free(res->res[i]);
+	os_free(res->res);
+	os_free(res);
+}
+
+
+const char * event_to_string(enum wpa_event_type event)
+{
+#define E2S(n) case EVENT_ ## n: return #n
+	switch (event) {
+	E2S(ASSOC);
+	E2S(DISASSOC);
+	E2S(MICHAEL_MIC_FAILURE);
+	E2S(SCAN_RESULTS);
+	E2S(ASSOCINFO);
+	E2S(INTERFACE_STATUS);
+	E2S(PMKID_CANDIDATE);
+	E2S(STKSTART);
+	E2S(TDLS);
+	E2S(FT_RESPONSE);
+	E2S(IBSS_RSN_START);
+	E2S(AUTH);
+	E2S(DEAUTH);
+	E2S(ASSOC_REJECT);
+	E2S(AUTH_TIMED_OUT);
+	E2S(ASSOC_TIMED_OUT);
+	E2S(WPS_BUTTON_PUSHED);
+	E2S(TX_STATUS);
+	E2S(RX_FROM_UNKNOWN);
+	E2S(RX_MGMT);
+	E2S(REMAIN_ON_CHANNEL);
+	E2S(CANCEL_REMAIN_ON_CHANNEL);
+	E2S(RX_PROBE_REQ);
+	E2S(NEW_STA);
+	E2S(EAPOL_RX);
+	E2S(SIGNAL_CHANGE);
+	E2S(INTERFACE_ENABLED);
+	E2S(INTERFACE_DISABLED);
+	E2S(CHANNEL_LIST_CHANGED);
+	E2S(INTERFACE_UNAVAILABLE);
+	E2S(BEST_CHANNEL);
+	E2S(UNPROT_DEAUTH);
+	E2S(UNPROT_DISASSOC);
+	E2S(STATION_LOW_ACK);
+	E2S(IBSS_PEER_LOST);
+	E2S(DRIVER_GTK_REKEY);
+	E2S(SCHED_SCAN_STOPPED);
+	E2S(DRIVER_CLIENT_POLL_OK);
+	E2S(EAPOL_TX_STATUS);
+	E2S(CH_SWITCH);
+	E2S(WNM);
+	E2S(CONNECT_FAILED_REASON);
+	E2S(DFS_RADAR_DETECTED);
+	E2S(DFS_CAC_FINISHED);
+	E2S(DFS_CAC_ABORTED);
+	E2S(DFS_NOP_FINISHED);
+	E2S(SURVEY);
+	E2S(SCAN_STARTED);
+	E2S(AVOID_FREQUENCIES);
+	E2S(NEW_PEER_CANDIDATE);
+	E2S(ACS_CHANNEL_SELECTED);
+	E2S(DFS_CAC_STARTED);
+	}
+
+	return "UNKNOWN";
+#undef E2S
+}
+
+
+const char * channel_width_to_string(enum chan_width width)
+{
+	switch (width) {
+	case CHAN_WIDTH_20_NOHT:
+		return "20 MHz (no HT)";
+	case CHAN_WIDTH_20:
+		return "20 MHz";
+	case CHAN_WIDTH_40:
+		return "40 MHz";
+	case CHAN_WIDTH_80:
+		return "80 MHz";
+	case CHAN_WIDTH_80P80:
+		return "80+80 MHz";
+	case CHAN_WIDTH_160:
+		return "160 MHz";
+	default:
+		return "unknown";
+	}
+}
+
+
+int ht_supported(const struct hostapd_hw_modes *mode)
+{
+	if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) {
+		/*
+		 * The driver did not indicate whether it supports HT. Assume
+		 * it does to avoid connection issues.
+		 */
+		return 1;
+	}
+
+	/*
+	 * IEEE Std 802.11n-2009 20.1.1:
+	 * An HT non-AP STA shall support all EQM rates for one spatial stream.
+	 */
+	return mode->mcs_set[0] == 0xff;
+}
+
+
+int vht_supported(const struct hostapd_hw_modes *mode)
+{
+	if (!(mode->flags & HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN)) {
+		/*
+		 * The driver did not indicate whether it supports VHT. Assume
+		 * it does to avoid connection issues.
+		 */
+		return 1;
+	}
+
+	/*
+	 * A VHT non-AP STA shall support MCS 0-7 for one spatial stream.
+	 * TODO: Verify if this complies with the standard
+	 */
+	return (mode->vht_mcs_set[0] & 0x3) != 3;
+}
+
+
+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,
+			const 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/hostap/src/drivers/driver_hostap.c b/hostap/src/drivers/driver_hostap.c
new file mode 100644
index 0000000..a7aa5ef
--- /dev/null
+++ b/hostap/src/drivers/driver_hostap.c
@@ -0,0 +1,1194 @@
+/*
+ * Driver interaction with Linux Host AP driver
+ * Copyright (c) 2003-2005, 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 <sys/ioctl.h>
+
+#include "linux_wext.h"
+#include "common.h"
+#include "driver.h"
+#include "driver_wext.h"
+#include "eloop.h"
+#include "driver_hostap.h"
+
+
+#include <net/if_arp.h>
+#include <netpacket/packet.h>
+
+#include "priv_netlink.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+
+
+/* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X
+ * frames that might be longer than normal default MTU and they are not
+ * fragmented */
+#define HOSTAPD_MTU 2290
+
+static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+struct hostap_driver_data {
+	struct hostapd_data *hapd;
+
+	char iface[IFNAMSIZ + 1];
+	int sock; /* raw packet socket for driver access */
+	int ioctl_sock; /* socket for ioctl() use */
+	struct netlink_data *netlink;
+
+	int we_version;
+
+	u8 *generic_ie;
+	size_t generic_ie_len;
+	u8 *wps_ie;
+	size_t wps_ie_len;
+};
+
+
+static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param,
+			 int len);
+static int hostap_set_iface_flags(void *priv, int dev_up);
+
+static void handle_data(struct hostap_driver_data *drv, u8 *buf, size_t len,
+			u16 stype)
+{
+	struct ieee80211_hdr *hdr;
+	u16 fc, ethertype;
+	u8 *pos, *sa;
+	size_t left;
+	union wpa_event_data event;
+
+	if (len < sizeof(struct ieee80211_hdr))
+		return;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	fc = le_to_host16(hdr->frame_control);
+
+	if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) {
+		printf("Not ToDS data frame (fc=0x%04x)\n", fc);
+		return;
+	}
+
+	sa = hdr->addr2;
+	os_memset(&event, 0, sizeof(event));
+	event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
+	event.rx_from_unknown.addr = sa;
+	wpa_supplicant_event(drv->hapd, EVENT_RX_FROM_UNKNOWN, &event);
+
+	pos = (u8 *) (hdr + 1);
+	left = len - sizeof(*hdr);
+
+	if (left < sizeof(rfc1042_header)) {
+		printf("Too short data frame\n");
+		return;
+	}
+
+	if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) {
+		printf("Data frame with no RFC1042 header\n");
+		return;
+	}
+	pos += sizeof(rfc1042_header);
+	left -= sizeof(rfc1042_header);
+
+	if (left < 2) {
+		printf("No ethertype in data frame\n");
+		return;
+	}
+
+	ethertype = WPA_GET_BE16(pos);
+	pos += 2;
+	left -= 2;
+	switch (ethertype) {
+	case ETH_P_PAE:
+		drv_event_eapol_rx(drv->hapd, sa, pos, left);
+		break;
+
+	default:
+		printf("Unknown ethertype 0x%04x in data frame\n", ethertype);
+		break;
+	}
+}
+
+
+static void handle_tx_callback(struct hostap_driver_data *drv, u8 *buf,
+			       size_t len, int ok)
+{
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	union wpa_event_data event;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	fc = le_to_host16(hdr->frame_control);
+
+	os_memset(&event, 0, sizeof(event));
+	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+	event.tx_status.dst = hdr->addr1;
+	event.tx_status.data = buf;
+	event.tx_status.data_len = len;
+	event.tx_status.ack = ok;
+	wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event);
+}
+
+
+static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len)
+{
+	struct ieee80211_hdr *hdr;
+	u16 fc, type, stype;
+	size_t data_len = len;
+	int ver;
+	union wpa_event_data event;
+
+	/* PSPOLL is only 16 bytes, but driver does not (at least yet) pass
+	 * these to user space */
+	if (len < 24) {
+		wpa_printf(MSG_MSGDUMP, "handle_frame: too short (%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+	hdr = (struct ieee80211_hdr *) buf;
+	fc = le_to_host16(hdr->frame_control);
+	type = WLAN_FC_GET_TYPE(fc);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if (type != WLAN_FC_TYPE_MGMT || stype != WLAN_FC_STYPE_BEACON) {
+		wpa_hexdump(MSG_MSGDUMP, "Received management frame",
+			    buf, len);
+	}
+
+	ver = fc & WLAN_FC_PVER;
+
+	/* protocol version 2 is reserved for indicating ACKed frame (TX
+	 * callbacks), and version 1 for indicating failed frame (no ACK, TX
+	 * callbacks) */
+	if (ver == 1 || ver == 2) {
+		handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0);
+		return;
+	} else if (ver != 0) {
+		printf("unknown protocol version %d\n", ver);
+		return;
+	}
+
+	switch (type) {
+	case WLAN_FC_TYPE_MGMT:
+		os_memset(&event, 0, sizeof(event));
+		event.rx_mgmt.frame = buf;
+		event.rx_mgmt.frame_len = data_len;
+		wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
+		break;
+	case WLAN_FC_TYPE_CTRL:
+		wpa_printf(MSG_DEBUG, "CTRL");
+		break;
+	case WLAN_FC_TYPE_DATA:
+		wpa_printf(MSG_DEBUG, "DATA");
+		handle_data(drv, buf, data_len, stype);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "unknown frame type %d", type);
+		break;
+	}
+}
+
+
+static void handle_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct hostap_driver_data *drv = eloop_ctx;
+	int len;
+	unsigned char buf[3000];
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+		return;
+	}
+
+	handle_frame(drv, buf, len);
+}
+
+
+static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr)
+{
+	struct ifreq ifr;
+	struct sockaddr_ll addr;
+
+	drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+	if (drv->sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) {
+		wpa_printf(MSG_ERROR, "Could not register read socket");
+		return -1;
+	}
+
+        memset(&ifr, 0, sizeof(ifr));
+        snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface);
+        if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+			   strerror(errno));
+		return -1;
+        }
+
+	if (hostap_set_iface_flags(drv, 1)) {
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sll_family = AF_PACKET;
+	addr.sll_ifindex = ifr.ifr_ifindex;
+	wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
+		   addr.sll_ifindex);
+
+	if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+		return -1;
+	}
+
+	return linux_get_ifhwaddr(drv->sock, drv->iface, own_addr);
+}
+
+
+static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack,
+			    unsigned int freq)
+{
+	struct hostap_driver_data *drv = priv;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg;
+	int res;
+
+	/* Request TX callback */
+	hdr->frame_control |= host_to_le16(BIT(1));
+	res = send(drv->sock, msg, len, 0);
+	hdr->frame_control &= ~host_to_le16(BIT(1));
+
+	return res;
+}
+
+
+static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data,
+			     size_t data_len, int encrypt, const u8 *own_addr,
+			     u32 flags)
+{
+	struct hostap_driver_data *drv = priv;
+	struct ieee80211_hdr *hdr;
+	size_t len;
+	u8 *pos;
+	int res;
+
+	len = sizeof(*hdr) + sizeof(rfc1042_header) + 2 + data_len;
+	hdr = os_zalloc(len);
+	if (hdr == NULL) {
+		printf("malloc() failed for hostapd_send_data(len=%lu)\n",
+		       (unsigned long) len);
+		return -1;
+	}
+
+	hdr->frame_control =
+		IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
+	hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
+	if (encrypt)
+		hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+	memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+	memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+	memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+	pos = (u8 *) (hdr + 1);
+	memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
+	pos += sizeof(rfc1042_header);
+	*((u16 *) pos) = htons(ETH_P_PAE);
+	pos += 2;
+	memcpy(pos, data, data_len);
+
+	res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - "
+			   "failed: %d (%s)",
+			   (unsigned long) len, errno, strerror(errno));
+	}
+	os_free(hdr);
+
+	return res;
+}
+
+
+static int hostap_sta_set_flags(void *priv, const u8 *addr,
+				unsigned int total_flags, unsigned int flags_or,
+				unsigned int flags_and)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param param;
+
+	if (flags_or & WPA_STA_AUTHORIZED)
+		flags_or = BIT(5); /* WLAN_STA_AUTHORIZED */
+	if (!(flags_and & WPA_STA_AUTHORIZED))
+		flags_and = ~BIT(5);
+	else
+		flags_and = ~0;
+	memset(&param, 0, sizeof(param));
+	param.cmd = PRISM2_HOSTAPD_SET_FLAGS_STA;
+	memcpy(param.sta_addr, addr, ETH_ALEN);
+	param.u.set_flags_sta.flags_or = flags_or;
+	param.u.set_flags_sta.flags_and = flags_and;
+	return hostapd_ioctl(drv, &param, sizeof(param));
+}
+
+
+static int hostap_set_iface_flags(void *priv, int dev_up)
+{
+	struct hostap_driver_data *drv = priv;
+	struct ifreq ifr;
+	char ifname[IFNAMSIZ];
+
+	os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface);
+	if (linux_set_iface_flags(drv->ioctl_sock, ifname, dev_up) < 0)
+		return -1;
+
+	if (dev_up) {
+		memset(&ifr, 0, sizeof(ifr));
+		os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+		ifr.ifr_mtu = HOSTAPD_MTU;
+		if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) {
+			wpa_printf(MSG_INFO,
+				   "Setting MTU failed - trying to survive with current value: ioctl[SIOCSIFMTU]: %s",
+				   strerror(errno));
+		}
+	}
+
+	return 0;
+}
+
+
+static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param,
+			 int len)
+{
+	struct hostap_driver_data *drv = priv;
+	struct iwreq iwr;
+
+	memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.data.pointer = (caddr_t) param;
+	iwr.u.data.length = len;
+
+	if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_HOSTAPD]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_hostap_set_key(const char *ifname, void *priv,
+				     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)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param *param;
+	u8 *buf;
+	size_t blen;
+	int ret = 0;
+
+	blen = sizeof(*param) + key_len;
+	buf = os_zalloc(blen);
+	if (buf == NULL)
+		return -1;
+
+	param = (struct prism2_hostapd_param *) buf;
+	param->cmd = PRISM2_SET_ENCRYPTION;
+	if (addr == NULL)
+		memset(param->sta_addr, 0xff, ETH_ALEN);
+	else
+		memcpy(param->sta_addr, addr, ETH_ALEN);
+	switch (alg) {
+	case WPA_ALG_NONE:
+		os_strlcpy((char *) param->u.crypt.alg, "NONE",
+			   HOSTAP_CRYPT_ALG_NAME_LEN);
+		break;
+	case WPA_ALG_WEP:
+		os_strlcpy((char *) param->u.crypt.alg, "WEP",
+			   HOSTAP_CRYPT_ALG_NAME_LEN);
+		break;
+	case WPA_ALG_TKIP:
+		os_strlcpy((char *) param->u.crypt.alg, "TKIP",
+			   HOSTAP_CRYPT_ALG_NAME_LEN);
+		break;
+	case WPA_ALG_CCMP:
+		os_strlcpy((char *) param->u.crypt.alg, "CCMP",
+			   HOSTAP_CRYPT_ALG_NAME_LEN);
+		break;
+	default:
+		os_free(buf);
+		return -1;
+	}
+	param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0;
+	param->u.crypt.idx = key_idx;
+	param->u.crypt.key_len = key_len;
+	memcpy((u8 *) (param + 1), key, key_len);
+
+	if (hostapd_ioctl(drv, param, blen)) {
+		printf("Failed to set encryption.\n");
+		ret = -1;
+	}
+	free(buf);
+
+	return ret;
+}
+
+
+static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr,
+			     int idx, u8 *seq)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param *param;
+	u8 *buf;
+	size_t blen;
+	int ret = 0;
+
+	blen = sizeof(*param) + 32;
+	buf = os_zalloc(blen);
+	if (buf == NULL)
+		return -1;
+
+	param = (struct prism2_hostapd_param *) buf;
+	param->cmd = PRISM2_GET_ENCRYPTION;
+	if (addr == NULL)
+		os_memset(param->sta_addr, 0xff, ETH_ALEN);
+	else
+		os_memcpy(param->sta_addr, addr, ETH_ALEN);
+	param->u.crypt.idx = idx;
+
+	if (hostapd_ioctl(drv, param, blen)) {
+		printf("Failed to get encryption.\n");
+		ret = -1;
+	} else {
+		os_memcpy(seq, param->u.crypt.seq, 8);
+	}
+	os_free(buf);
+
+	return ret;
+}
+
+
+static int hostap_ioctl_prism2param(void *priv, int param, int value)
+{
+	struct hostap_driver_data *drv = priv;
+	struct iwreq iwr;
+	int *i;
+
+	memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	i = (int *) iwr.u.name;
+	*i++ = param;
+	*i++ = value;
+
+	if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_PRISM2_PARAM]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int hostap_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+	struct hostap_driver_data *drv = priv;
+	int enabled = params->enabled;
+
+	/* enable kernel driver support for IEEE 802.1X */
+	if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_IEEE_802_1X, enabled)) {
+		printf("Could not setup IEEE 802.1X support in kernel driver."
+		       "\n");
+		return -1;
+	}
+
+	if (!enabled)
+		return 0;
+
+	/* use host driver implementation of encryption to allow
+	 * individual keys and passing plaintext EAPOL frames */
+	if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_DECRYPT, 1) ||
+	    hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_ENCRYPT, 1)) {
+		printf("Could not setup host-based encryption in kernel "
+		       "driver.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int hostap_set_privacy(void *priv, int enabled)
+{
+	struct hostap_drvier_data *drv = priv;
+
+	return hostap_ioctl_prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED,
+					enabled);
+}
+
+
+static int hostap_set_ssid(void *priv, const u8 *buf, int len)
+{
+	struct hostap_driver_data *drv = priv;
+	struct iwreq iwr;
+
+	memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.essid.flags = 1; /* SSID active */
+	iwr.u.essid.pointer = (caddr_t) buf;
+	iwr.u.essid.length = len + 1;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s",
+			   len, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int hostap_flush(void *priv)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param param;
+
+	memset(&param, 0, sizeof(param));
+	param.cmd = PRISM2_HOSTAPD_FLUSH;
+	return hostapd_ioctl(drv, &param, sizeof(param));
+}
+
+
+static int hostap_read_sta_data(void *priv,
+				struct hostap_sta_driver_data *data,
+				const u8 *addr)
+{
+	struct hostap_driver_data *drv = priv;
+	char buf[1024], line[128], *pos;
+	FILE *f;
+	unsigned long val;
+
+	memset(data, 0, sizeof(*data));
+	snprintf(buf, sizeof(buf), "/proc/net/hostap/%s/" MACSTR,
+		 drv->iface, MAC2STR(addr));
+
+	f = fopen(buf, "r");
+	if (!f)
+		return -1;
+	/* Need to read proc file with in one piece, so use large enough
+	 * buffer. */
+	setbuffer(f, buf, sizeof(buf));
+
+	while (fgets(line, sizeof(line), f)) {
+		pos = strchr(line, '=');
+		if (!pos)
+			continue;
+		*pos++ = '\0';
+		val = strtoul(pos, NULL, 10);
+		if (strcmp(line, "rx_packets") == 0)
+			data->rx_packets = val;
+		else if (strcmp(line, "tx_packets") == 0)
+			data->tx_packets = val;
+		else if (strcmp(line, "rx_bytes") == 0)
+			data->rx_bytes = val;
+		else if (strcmp(line, "tx_bytes") == 0)
+			data->tx_bytes = val;
+	}
+
+	fclose(f);
+
+	return 0;
+}
+
+
+static int hostap_sta_add(void *priv, struct hostapd_sta_add_params *params)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param param;
+	int tx_supp_rates = 0;
+	size_t i;
+
+#define WLAN_RATE_1M BIT(0)
+#define WLAN_RATE_2M BIT(1)
+#define WLAN_RATE_5M5 BIT(2)
+#define WLAN_RATE_11M BIT(3)
+
+	for (i = 0; i < params->supp_rates_len; i++) {
+		if ((params->supp_rates[i] & 0x7f) == 2)
+			tx_supp_rates |= WLAN_RATE_1M;
+		if ((params->supp_rates[i] & 0x7f) == 4)
+			tx_supp_rates |= WLAN_RATE_2M;
+		if ((params->supp_rates[i] & 0x7f) == 11)
+			tx_supp_rates |= WLAN_RATE_5M5;
+		if ((params->supp_rates[i] & 0x7f) == 22)
+			tx_supp_rates |= WLAN_RATE_11M;
+	}
+
+	memset(&param, 0, sizeof(param));
+	param.cmd = PRISM2_HOSTAPD_ADD_STA;
+	memcpy(param.sta_addr, params->addr, ETH_ALEN);
+	param.u.add_sta.aid = params->aid;
+	param.u.add_sta.capability = params->capability;
+	param.u.add_sta.tx_supp_rates = tx_supp_rates;
+	return hostapd_ioctl(drv, &param, sizeof(param));
+}
+
+
+static int hostap_sta_remove(void *priv, const u8 *addr)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param param;
+
+	hostap_sta_set_flags(drv, addr, 0, 0, ~WPA_STA_AUTHORIZED);
+
+	memset(&param, 0, sizeof(param));
+	param.cmd = PRISM2_HOSTAPD_REMOVE_STA;
+	memcpy(param.sta_addr, addr, ETH_ALEN);
+	if (hostapd_ioctl(drv, &param, sizeof(param))) {
+		printf("Could not remove station from kernel driver.\n");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int hostap_get_inact_sec(void *priv, const u8 *addr)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param param;
+
+	memset(&param, 0, sizeof(param));
+	param.cmd = PRISM2_HOSTAPD_GET_INFO_STA;
+	memcpy(param.sta_addr, addr, ETH_ALEN);
+	if (hostapd_ioctl(drv, &param, sizeof(param))) {
+		return -1;
+	}
+
+	return param.u.get_info_sta.inactive_sec;
+}
+
+
+static int hostap_sta_clear_stats(void *priv, const u8 *addr)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param param;
+
+	memset(&param, 0, sizeof(param));
+	param.cmd = PRISM2_HOSTAPD_STA_CLEAR_STATS;
+	memcpy(param.sta_addr, addr, ETH_ALEN);
+	if (hostapd_ioctl(drv, &param, sizeof(param))) {
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv)
+{
+	struct prism2_hostapd_param *param;
+	int res;
+	size_t blen, elem_len;
+
+	elem_len = drv->generic_ie_len + drv->wps_ie_len;
+	blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + elem_len;
+	if (blen < sizeof(*param))
+		blen = sizeof(*param);
+
+	param = os_zalloc(blen);
+	if (param == NULL)
+		return -1;
+
+	param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT;
+	param->u.generic_elem.len = elem_len;
+	if (drv->generic_ie) {
+		os_memcpy(param->u.generic_elem.data, drv->generic_ie,
+			  drv->generic_ie_len);
+	}
+	if (drv->wps_ie) {
+		os_memcpy(&param->u.generic_elem.data[drv->generic_ie_len],
+			  drv->wps_ie, drv->wps_ie_len);
+	}
+	wpa_hexdump(MSG_DEBUG, "hostap: Set generic IE",
+		    param->u.generic_elem.data, elem_len);
+	res = hostapd_ioctl(drv, param, blen);
+
+	os_free(param);
+
+	return res;
+}
+
+
+static int hostap_set_generic_elem(void *priv,
+				   const u8 *elem, size_t elem_len)
+{
+	struct hostap_driver_data *drv = priv;
+
+	os_free(drv->generic_ie);
+	drv->generic_ie = NULL;
+	drv->generic_ie_len = 0;
+	if (elem) {
+		drv->generic_ie = os_malloc(elem_len);
+		if (drv->generic_ie == NULL)
+			return -1;
+		os_memcpy(drv->generic_ie, elem, elem_len);
+		drv->generic_ie_len = elem_len;
+	}
+
+	return hostapd_ioctl_set_generic_elem(drv);
+}
+
+
+static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
+				const struct wpabuf *proberesp,
+				const struct wpabuf *assocresp)
+{
+	struct hostap_driver_data *drv = priv;
+
+	/*
+	 * Host AP driver supports only one set of extra IEs, so we need to
+	 * use the Probe Response IEs also for Beacon frames since they include
+	 * more information.
+	 */
+
+	os_free(drv->wps_ie);
+	drv->wps_ie = NULL;
+	drv->wps_ie_len = 0;
+	if (proberesp) {
+		drv->wps_ie = os_malloc(wpabuf_len(proberesp));
+		if (drv->wps_ie == NULL)
+			return -1;
+		os_memcpy(drv->wps_ie, wpabuf_head(proberesp),
+			  wpabuf_len(proberesp));
+		drv->wps_ie_len = wpabuf_len(proberesp);
+	}
+
+	return hostapd_ioctl_set_generic_elem(drv);
+}
+
+
+static void
+hostapd_wireless_event_wireless_custom(struct hostap_driver_data *drv,
+				       char *custom)
+{
+	wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
+
+	if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+		char *pos;
+		u8 addr[ETH_ALEN];
+		pos = strstr(custom, "addr=");
+		if (pos == NULL) {
+			wpa_printf(MSG_DEBUG,
+				   "MLME-MICHAELMICFAILURE.indication "
+				   "without sender address ignored");
+			return;
+		}
+		pos += 5;
+		if (hwaddr_aton(pos, addr) == 0) {
+			union wpa_event_data data;
+			os_memset(&data, 0, sizeof(data));
+			data.michael_mic_failure.unicast = 1;
+			data.michael_mic_failure.src = addr;
+			wpa_supplicant_event(drv->hapd,
+					     EVENT_MICHAEL_MIC_FAILURE, &data);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "MLME-MICHAELMICFAILURE.indication "
+				   "with invalid MAC address");
+		}
+	}
+}
+
+
+static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv,
+					    char *data, int len)
+{
+	struct iw_event iwe_buf, *iwe = &iwe_buf;
+	char *pos, *end, *custom, *buf;
+
+	pos = data;
+	end = data + len;
+
+	while (pos + IW_EV_LCP_LEN <= end) {
+		/* Event data may be unaligned, so make a local, aligned copy
+		 * before processing. */
+		memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+		wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
+			   iwe->cmd, iwe->len);
+		if (iwe->len <= IW_EV_LCP_LEN)
+			return;
+
+		custom = pos + IW_EV_POINT_LEN;
+		if (drv->we_version > 18 &&
+		    (iwe->cmd == IWEVMICHAELMICFAILURE ||
+		     iwe->cmd == IWEVCUSTOM)) {
+			/* WE-19 removed the pointer from struct iw_point */
+			char *dpos = (char *) &iwe_buf.u.data.length;
+			int dlen = dpos - (char *) &iwe_buf;
+			memcpy(dpos, pos + IW_EV_LCP_LEN,
+			       sizeof(struct iw_event) - dlen);
+		} else {
+			memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+			custom += IW_EV_POINT_OFF;
+		}
+
+		switch (iwe->cmd) {
+		case IWEVCUSTOM:
+			if (custom + iwe->u.data.length > end)
+				return;
+			buf = malloc(iwe->u.data.length + 1);
+			if (buf == NULL)
+				return;
+			memcpy(buf, custom, iwe->u.data.length);
+			buf[iwe->u.data.length] = '\0';
+			hostapd_wireless_event_wireless_custom(drv, buf);
+			free(buf);
+			break;
+		}
+
+		pos += iwe->len;
+	}
+}
+
+
+static void hostapd_wireless_event_rtm_newlink(void *ctx,
+					       struct ifinfomsg *ifi,
+					       u8 *buf, size_t len)
+{
+	struct hostap_driver_data *drv = ctx;
+	int attrlen, rta_len;
+	struct rtattr *attr;
+
+	/* TODO: use ifi->ifi_index to filter out wireless events from other
+	 * interfaces */
+
+	attrlen = len;
+	attr = (struct rtattr *) buf;
+
+	rta_len = RTA_ALIGN(sizeof(struct rtattr));
+	while (RTA_OK(attr, attrlen)) {
+		if (attr->rta_type == IFLA_WIRELESS) {
+			hostapd_wireless_event_wireless(
+				drv, ((char *) attr) + rta_len,
+				attr->rta_len - rta_len);
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+}
+
+
+static int hostap_get_we_version(struct hostap_driver_data *drv)
+{
+	struct iw_range *range;
+	struct iwreq iwr;
+	int minlen;
+	size_t buflen;
+
+	drv->we_version = 0;
+
+	/*
+	 * Use larger buffer than struct iw_range in order to allow the
+	 * structure to grow in the future.
+	 */
+	buflen = sizeof(struct iw_range) + 500;
+	range = os_zalloc(buflen);
+	if (range == NULL)
+		return -1;
+
+	memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.data.pointer = (caddr_t) range;
+	iwr.u.data.length = buflen;
+
+	minlen = ((char *) &range->enc_capa) - (char *) range +
+		sizeof(range->enc_capa);
+
+	if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+			   strerror(errno));
+		os_free(range);
+		return -1;
+	} else if (iwr.u.data.length >= minlen &&
+		   range->we_version_compiled >= 18) {
+		wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
+			   "WE(source)=%d enc_capa=0x%x",
+			   range->we_version_compiled,
+			   range->we_version_source,
+			   range->enc_capa);
+		drv->we_version = range->we_version_compiled;
+	}
+
+	free(range);
+	return 0;
+}
+
+
+static int hostap_wireless_event_init(struct hostap_driver_data *drv)
+{
+	struct netlink_config *cfg;
+
+	hostap_get_we_version(drv);
+
+	cfg = os_zalloc(sizeof(*cfg));
+	if (cfg == NULL)
+		return -1;
+	cfg->ctx = drv;
+	cfg->newlink_cb = hostapd_wireless_event_rtm_newlink;
+	drv->netlink = netlink_init(cfg);
+	if (drv->netlink == NULL) {
+		os_free(cfg);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void * hostap_init(struct hostapd_data *hapd,
+			  struct wpa_init_params *params)
+{
+	struct hostap_driver_data *drv;
+
+	drv = os_zalloc(sizeof(struct hostap_driver_data));
+	if (drv == NULL) {
+		printf("Could not allocate memory for hostapd driver data\n");
+		return NULL;
+	}
+
+	drv->hapd = hapd;
+	drv->ioctl_sock = drv->sock = -1;
+	memcpy(drv->iface, params->ifname, sizeof(drv->iface));
+
+	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (drv->ioctl_sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
+		os_free(drv);
+		return NULL;
+	}
+
+	if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) {
+		wpa_printf(MSG_ERROR,
+			   "Could not enable hostapd mode for interface %s",
+			   drv->iface);
+		close(drv->ioctl_sock);
+		os_free(drv);
+		return NULL;
+	}
+
+	if (hostap_init_sockets(drv, params->own_addr) ||
+	    hostap_wireless_event_init(drv)) {
+		close(drv->ioctl_sock);
+		os_free(drv);
+		return NULL;
+	}
+
+	return drv;
+}
+
+
+static void hostap_driver_deinit(void *priv)
+{
+	struct hostap_driver_data *drv = priv;
+
+	netlink_deinit(drv->netlink);
+	(void) hostap_set_iface_flags(drv, 0);
+	(void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 0);
+	(void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD_STA, 0);
+
+	if (drv->ioctl_sock >= 0)
+		close(drv->ioctl_sock);
+
+	if (drv->sock >= 0)
+		close(drv->sock);
+
+	os_free(drv->generic_ie);
+	os_free(drv->wps_ie);
+
+	free(drv);
+}
+
+
+static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+			     int reason)
+{
+	struct hostap_driver_data *drv = priv;
+	struct ieee80211_mgmt mgmt;
+
+	if (is_broadcast_ether_addr(addr)) {
+		/*
+		 * New Prism2.5/3 STA firmware versions seem to have issues
+		 * with this broadcast deauth frame. This gets the firmware in
+		 * odd state where nothing works correctly, so let's skip
+		 * sending this for the hostap driver.
+		 */
+		return 0;
+	}
+
+	memset(&mgmt, 0, sizeof(mgmt));
+	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					  WLAN_FC_STYPE_DEAUTH);
+	memcpy(mgmt.da, addr, ETH_ALEN);
+	memcpy(mgmt.sa, own_addr, ETH_ALEN);
+	memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+	mgmt.u.deauth.reason_code = host_to_le16(reason);
+	return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
+				sizeof(mgmt.u.deauth), 0, 0);
+}
+
+
+static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+	struct hostap_driver_data *drv = priv;
+	struct iwreq iwr;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.freq.m = freq->channel;
+	iwr.u.freq.e = 0;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+			       int reason)
+{
+	struct hostap_driver_data *drv = priv;
+	struct ieee80211_mgmt mgmt;
+
+	memset(&mgmt, 0, sizeof(mgmt));
+	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					  WLAN_FC_STYPE_DISASSOC);
+	memcpy(mgmt.da, addr, ETH_ALEN);
+	memcpy(mgmt.sa, own_addr, ETH_ALEN);
+	memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+	mgmt.u.disassoc.reason_code = host_to_le16(reason);
+	return  hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
+				 sizeof(mgmt.u.disassoc), 0, 0);
+}
+
+
+static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv,
+							    u16 *num_modes,
+							    u16 *flags)
+{
+	struct hostapd_hw_modes *mode;
+	int i, clen, rlen;
+	const short chan2freq[14] = {
+		2412, 2417, 2422, 2427, 2432, 2437, 2442,
+		2447, 2452, 2457, 2462, 2467, 2472, 2484
+	};
+
+	mode = os_zalloc(sizeof(struct hostapd_hw_modes));
+	if (mode == NULL)
+		return NULL;
+
+	*num_modes = 1;
+	*flags = 0;
+
+	mode->mode = HOSTAPD_MODE_IEEE80211B;
+	mode->num_channels = 14;
+	mode->num_rates = 4;
+
+	clen = mode->num_channels * sizeof(struct hostapd_channel_data);
+	rlen = mode->num_rates * sizeof(int);
+
+	mode->channels = os_zalloc(clen);
+	mode->rates = os_zalloc(rlen);
+	if (mode->channels == NULL || mode->rates == NULL) {
+		os_free(mode->channels);
+		os_free(mode->rates);
+		os_free(mode);
+		return NULL;
+	}
+
+	for (i = 0; i < 14; i++) {
+		mode->channels[i].chan = i + 1;
+		mode->channels[i].freq = chan2freq[i];
+		/* TODO: Get allowed channel list from the driver */
+		if (i >= 11)
+			mode->channels[i].flag = HOSTAPD_CHAN_DISABLED;
+	}
+
+	mode->rates[0] = 10;
+	mode->rates[1] = 20;
+	mode->rates[2] = 55;
+	mode->rates[3] = 110;
+
+	return mode;
+}
+
+
+static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr,
+					  const u8 *addr, int qos)
+{
+	struct ieee80211_hdr hdr;
+
+	os_memset(&hdr, 0, sizeof(hdr));
+
+	/*
+	 * WLAN_FC_STYPE_NULLFUNC would be more appropriate,
+	 * but it is apparently not retried so TX Exc events
+	 * are not received for it.
+	 * This is the reason the driver overrides the default
+	 * handling.
+	 */
+	hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA,
+					 WLAN_FC_STYPE_DATA);
+
+	hdr.frame_control |=
+		host_to_le16(WLAN_FC_FROMDS);
+	os_memcpy(hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+	os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+	os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+	hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0);
+}
+
+
+const struct wpa_driver_ops wpa_driver_hostap_ops = {
+	.name = "hostap",
+	.desc = "Host AP driver (Intersil Prism2/2.5/3)",
+	.set_key = wpa_driver_hostap_set_key,
+	.hapd_init = hostap_init,
+	.hapd_deinit = hostap_driver_deinit,
+	.set_ieee8021x = hostap_set_ieee8021x,
+	.set_privacy = hostap_set_privacy,
+	.get_seqnum = hostap_get_seqnum,
+	.flush = hostap_flush,
+	.set_generic_elem = hostap_set_generic_elem,
+	.read_sta_data = hostap_read_sta_data,
+	.hapd_send_eapol = hostap_send_eapol,
+	.sta_set_flags = hostap_sta_set_flags,
+	.sta_deauth = hostap_sta_deauth,
+	.sta_disassoc = hostap_sta_disassoc,
+	.sta_remove = hostap_sta_remove,
+	.hapd_set_ssid = hostap_set_ssid,
+	.send_mlme = hostap_send_mlme,
+	.sta_add = hostap_sta_add,
+	.get_inact_sec = hostap_get_inact_sec,
+	.sta_clear_stats = hostap_sta_clear_stats,
+	.get_hw_feature_data = hostap_get_hw_feature_data,
+	.set_ap_wps_ie = hostap_set_ap_wps_ie,
+	.set_freq = hostap_set_freq,
+	.poll_client = wpa_driver_hostap_poll_client,
+};
diff --git a/hostap/src/drivers/driver_hostap.h b/hostap/src/drivers/driver_hostap.h
new file mode 100644
index 0000000..4c1e6d6
--- /dev/null
+++ b/hostap/src/drivers/driver_hostap.h
@@ -0,0 +1,210 @@
+/*
+ * Driver interaction with Linux Host AP driver
+ * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HOSTAP_DRIVER_H
+#define HOSTAP_DRIVER_H
+
+/* netdevice private ioctls (used, e.g., with iwpriv from user space) */
+
+/* New wireless extensions API - SET/GET convention (even ioctl numbers are
+ * root only)
+ */
+#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0)
+#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1)
+#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2)
+#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3)
+#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4)
+#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6)
+#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8)
+#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10)
+#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12)
+#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14)
+#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16)
+#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18)
+#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20)
+#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22)
+
+/* following are not in SIOCGIWPRIV list; check permission in the driver code
+ */
+#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13)
+#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14)
+
+
+/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */
+enum {
+	/* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */
+	PRISM2_PARAM_TXRATECTRL = 2,
+	PRISM2_PARAM_BEACON_INT = 3,
+	PRISM2_PARAM_PSEUDO_IBSS = 4,
+	PRISM2_PARAM_ALC = 5,
+	/* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */
+	PRISM2_PARAM_DUMP = 7,
+	PRISM2_PARAM_OTHER_AP_POLICY = 8,
+	PRISM2_PARAM_AP_MAX_INACTIVITY = 9,
+	PRISM2_PARAM_AP_BRIDGE_PACKETS = 10,
+	PRISM2_PARAM_DTIM_PERIOD = 11,
+	PRISM2_PARAM_AP_NULLFUNC_ACK = 12,
+	PRISM2_PARAM_MAX_WDS = 13,
+	PRISM2_PARAM_AP_AUTOM_AP_WDS = 14,
+	PRISM2_PARAM_AP_AUTH_ALGS = 15,
+	PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16,
+	PRISM2_PARAM_HOST_ENCRYPT = 17,
+	PRISM2_PARAM_HOST_DECRYPT = 18,
+	PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19,
+	PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20,
+	PRISM2_PARAM_HOST_ROAMING = 21,
+	PRISM2_PARAM_BCRX_STA_KEY = 22,
+	PRISM2_PARAM_IEEE_802_1X = 23,
+	PRISM2_PARAM_ANTSEL_TX = 24,
+	PRISM2_PARAM_ANTSEL_RX = 25,
+	PRISM2_PARAM_MONITOR_TYPE = 26,
+	PRISM2_PARAM_WDS_TYPE = 27,
+	PRISM2_PARAM_HOSTSCAN = 28,
+	PRISM2_PARAM_AP_SCAN = 29,
+	PRISM2_PARAM_ENH_SEC = 30,
+	PRISM2_PARAM_IO_DEBUG = 31,
+	PRISM2_PARAM_BASIC_RATES = 32,
+	PRISM2_PARAM_OPER_RATES = 33,
+	PRISM2_PARAM_HOSTAPD = 34,
+	PRISM2_PARAM_HOSTAPD_STA = 35,
+	PRISM2_PARAM_WPA = 36,
+	PRISM2_PARAM_PRIVACY_INVOKED = 37,
+	PRISM2_PARAM_TKIP_COUNTERMEASURES = 38,
+	PRISM2_PARAM_DROP_UNENCRYPTED = 39,
+	PRISM2_PARAM_SCAN_CHANNEL_MASK = 40,
+};
+
+enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1,
+       HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 };
+
+
+/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */
+enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1,
+       AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3,
+       AP_MAC_CMD_KICKALL = 4 };
+
+
+/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */
+enum {
+	PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */,
+	/* Note! Old versions of prism2_srec have a fatal error in CRC-16
+	 * calculation, which will corrupt all non-volatile downloads.
+	 * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to
+	 * prevent use of old versions of prism2_srec for non-volatile
+	 * download. */
+	PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */,
+	PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */,
+	/* Persistent versions of volatile download commands (keep firmware
+	 * data in memory and automatically re-download after hw_reset */
+	PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5,
+	PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6,
+};
+
+struct prism2_download_param {
+	u32 dl_cmd;
+	u32 start_addr;
+	u32 num_areas;
+	struct prism2_download_area {
+		u32 addr; /* wlan card address */
+		u32 len;
+		caddr_t ptr; /* pointer to data in user space */
+	} data[0];
+};
+
+#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072
+#define PRISM2_MAX_DOWNLOAD_LEN 262144
+
+
+/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */
+enum {
+	PRISM2_HOSTAPD_FLUSH = 1,
+	PRISM2_HOSTAPD_ADD_STA = 2,
+	PRISM2_HOSTAPD_REMOVE_STA = 3,
+	PRISM2_HOSTAPD_GET_INFO_STA = 4,
+	/* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */
+	PRISM2_SET_ENCRYPTION = 6,
+	PRISM2_GET_ENCRYPTION = 7,
+	PRISM2_HOSTAPD_SET_FLAGS_STA = 8,
+	PRISM2_HOSTAPD_GET_RID = 9,
+	PRISM2_HOSTAPD_SET_RID = 10,
+	PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11,
+	PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12,
+	PRISM2_HOSTAPD_MLME = 13,
+	PRISM2_HOSTAPD_SCAN_REQ = 14,
+	PRISM2_HOSTAPD_STA_CLEAR_STATS = 15,
+};
+
+#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024
+#define PRISM2_HOSTAPD_RID_HDR_LEN \
+((size_t) (&((struct prism2_hostapd_param *) 0)->u.rid.data))
+#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \
+((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data))
+
+/* Maximum length for algorithm names (-1 for nul termination) used in ioctl()
+ */
+#define HOSTAP_CRYPT_ALG_NAME_LEN 16
+
+
+struct prism2_hostapd_param {
+	u32 cmd;
+	u8 sta_addr[ETH_ALEN];
+	union {
+		struct {
+			u16 aid;
+			u16 capability;
+			u8 tx_supp_rates;
+		} add_sta;
+		struct {
+			u32 inactive_sec;
+		} get_info_sta;
+		struct {
+			u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN];
+			u32 flags;
+			u32 err;
+			u8 idx;
+			u8 seq[8]; /* sequence counter (set: RX, get: TX) */
+			u16 key_len;
+			u8 key[0];
+		} crypt;
+		struct {
+			u32 flags_and;
+			u32 flags_or;
+		} set_flags_sta;
+		struct {
+			u16 rid;
+			u16 len;
+			u8 data[0];
+		} rid;
+		struct {
+			u8 len;
+			u8 data[0];
+		} generic_elem;
+		struct {
+#define MLME_STA_DEAUTH 0
+#define MLME_STA_DISASSOC 1
+			u16 cmd;
+			u16 reason_code;
+		} mlme;
+		struct {
+			u8 ssid_len;
+			u8 ssid[SSID_MAX_LEN];
+		} scan_req;
+	} u;
+};
+
+#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0)
+#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1)
+
+#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2
+#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3
+#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4
+#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5
+#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6
+#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7
+
+#endif /* HOSTAP_DRIVER_H */
diff --git a/hostap/src/drivers/driver_macsec_qca.c b/hostap/src/drivers/driver_macsec_qca.c
new file mode 100644
index 0000000..3eae2f8
--- /dev/null
+++ b/hostap/src/drivers/driver_macsec_qca.c
@@ -0,0 +1,891 @@
+/*
+ * 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) {
+		wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
+		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) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+	if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+			   strerror(errno));
+		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) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		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) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+			   strerror(errno));
+		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) {
+		wpa_print(MSG_ERROR, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifmr, 0, sizeof(ifmr));
+	os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
+	if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
+			   strerror(errno));
+		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) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		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) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
+			   strerror(errno));
+		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)
+		wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
+#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/hostap/src/drivers/driver_ndis.c b/hostap/src/drivers/driver_ndis.c
new file mode 100644
index 0000000..669f1b8
--- /dev/null
+++ b/hostap/src/drivers/driver_ndis.c
@@ -0,0 +1,3218 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifdef __CYGWIN__
+/* Avoid some header file conflicts by not including standard headers for
+ * cygwin builds when Packet32.h is included. */
+#include "build_config.h"
+int close(int fd);
+#else /* __CYGWIN__ */
+#include "includes.h"
+#endif /* __CYGWIN__ */
+#ifdef CONFIG_USE_NDISUIO
+#include <winsock2.h>
+#else /* CONFIG_USE_NDISUIO */
+#include <Packet32.h>
+#endif /* CONFIG_USE_NDISUIO */
+#ifdef __MINGW32_VERSION
+#include <ddk/ntddndis.h>
+#else /* __MINGW32_VERSION */
+#include <ntddndis.h>
+#endif /* __MINGW32_VERSION */
+
+#ifdef _WIN32_WCE
+#include <winioctl.h>
+#include <nuiouser.h>
+#include <devload.h>
+#endif /* _WIN32_WCE */
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "driver_ndis.h"
+
+int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv);
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+static void wpa_driver_ndis_deinit(void *priv);
+static void wpa_driver_ndis_poll(void *drv);
+static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx);
+static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv);
+static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv);
+static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv);
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+
+/* FIX: to be removed once this can be compiled with the complete NDIS
+ * header files */
+#ifndef OID_802_11_BSSID
+#define OID_802_11_BSSID 			0x0d010101
+#define OID_802_11_SSID 			0x0d010102
+#define OID_802_11_INFRASTRUCTURE_MODE		0x0d010108
+#define OID_802_11_ADD_WEP			0x0D010113
+#define OID_802_11_REMOVE_WEP			0x0D010114
+#define OID_802_11_DISASSOCIATE			0x0D010115
+#define OID_802_11_BSSID_LIST 			0x0d010217
+#define OID_802_11_AUTHENTICATION_MODE		0x0d010118
+#define OID_802_11_PRIVACY_FILTER		0x0d010119
+#define OID_802_11_BSSID_LIST_SCAN 		0x0d01011A
+#define OID_802_11_WEP_STATUS	 		0x0d01011B
+#define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS
+#define OID_802_11_ADD_KEY 			0x0d01011D
+#define OID_802_11_REMOVE_KEY 			0x0d01011E
+#define OID_802_11_ASSOCIATION_INFORMATION	0x0d01011F
+#define OID_802_11_TEST 			0x0d010120
+#define OID_802_11_CAPABILITY 			0x0d010122
+#define OID_802_11_PMKID 			0x0d010123
+
+#define NDIS_802_11_LENGTH_SSID 32
+#define NDIS_802_11_LENGTH_RATES 8
+#define NDIS_802_11_LENGTH_RATES_EX 16
+
+typedef UCHAR NDIS_802_11_MAC_ADDRESS[6];
+
+typedef struct NDIS_802_11_SSID {
+	ULONG SsidLength;
+	UCHAR Ssid[NDIS_802_11_LENGTH_SSID];
+} NDIS_802_11_SSID;
+
+typedef LONG NDIS_802_11_RSSI;
+
+typedef enum NDIS_802_11_NETWORK_TYPE {
+	Ndis802_11FH,
+	Ndis802_11DS,
+	Ndis802_11OFDM5,
+	Ndis802_11OFDM24,
+	Ndis802_11NetworkTypeMax
+} NDIS_802_11_NETWORK_TYPE;
+
+typedef struct NDIS_802_11_CONFIGURATION_FH {
+	ULONG Length;
+	ULONG HopPattern;
+	ULONG HopSet;
+	ULONG DwellTime;
+} NDIS_802_11_CONFIGURATION_FH;
+
+typedef struct NDIS_802_11_CONFIGURATION {
+	ULONG Length;
+	ULONG BeaconPeriod;
+	ULONG ATIMWindow;
+	ULONG DSConfig;
+	NDIS_802_11_CONFIGURATION_FH FHConfig;
+} NDIS_802_11_CONFIGURATION;
+
+typedef enum NDIS_802_11_NETWORK_INFRASTRUCTURE {
+	Ndis802_11IBSS,
+	Ndis802_11Infrastructure,
+	Ndis802_11AutoUnknown,
+	Ndis802_11InfrastructureMax
+} NDIS_802_11_NETWORK_INFRASTRUCTURE;
+
+typedef enum NDIS_802_11_AUTHENTICATION_MODE {
+	Ndis802_11AuthModeOpen,
+	Ndis802_11AuthModeShared,
+	Ndis802_11AuthModeAutoSwitch,
+	Ndis802_11AuthModeWPA,
+	Ndis802_11AuthModeWPAPSK,
+	Ndis802_11AuthModeWPANone,
+	Ndis802_11AuthModeWPA2,
+	Ndis802_11AuthModeWPA2PSK,
+	Ndis802_11AuthModeMax
+} NDIS_802_11_AUTHENTICATION_MODE;
+
+typedef enum NDIS_802_11_WEP_STATUS {
+	Ndis802_11WEPEnabled,
+	Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+	Ndis802_11WEPDisabled,
+	Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+	Ndis802_11WEPKeyAbsent,
+	Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+	Ndis802_11WEPNotSupported,
+	Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+	Ndis802_11Encryption2Enabled,
+	Ndis802_11Encryption2KeyAbsent,
+	Ndis802_11Encryption3Enabled,
+	Ndis802_11Encryption3KeyAbsent
+} NDIS_802_11_WEP_STATUS, NDIS_802_11_ENCRYPTION_STATUS;
+
+typedef enum NDIS_802_11_PRIVACY_FILTER {
+	Ndis802_11PrivFilterAcceptAll,
+	Ndis802_11PrivFilter8021xWEP
+} NDIS_802_11_PRIVACY_FILTER;
+
+typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES];
+typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX];
+
+typedef struct NDIS_WLAN_BSSID_EX {
+	ULONG Length;
+	NDIS_802_11_MAC_ADDRESS MacAddress; /* BSSID */
+	UCHAR Reserved[2];
+	NDIS_802_11_SSID Ssid;
+	ULONG Privacy;
+	NDIS_802_11_RSSI Rssi;
+	NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+	NDIS_802_11_CONFIGURATION Configuration;
+	NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+	NDIS_802_11_RATES_EX SupportedRates;
+	ULONG IELength;
+	UCHAR IEs[1];
+} NDIS_WLAN_BSSID_EX;
+
+typedef struct NDIS_802_11_BSSID_LIST_EX {
+	ULONG NumberOfItems;
+	NDIS_WLAN_BSSID_EX Bssid[1];
+} NDIS_802_11_BSSID_LIST_EX;
+
+typedef struct NDIS_802_11_FIXED_IEs {
+	UCHAR Timestamp[8];
+	USHORT BeaconInterval;
+	USHORT Capabilities;
+} NDIS_802_11_FIXED_IEs;
+
+typedef struct NDIS_802_11_WEP {
+	ULONG Length;
+	ULONG KeyIndex;
+	ULONG KeyLength;
+	UCHAR KeyMaterial[1];
+} NDIS_802_11_WEP;
+
+typedef ULONG NDIS_802_11_KEY_INDEX;
+typedef ULONGLONG NDIS_802_11_KEY_RSC;
+
+typedef struct NDIS_802_11_KEY {
+	ULONG Length;
+	ULONG KeyIndex;
+	ULONG KeyLength;
+	NDIS_802_11_MAC_ADDRESS BSSID;
+	NDIS_802_11_KEY_RSC KeyRSC;
+	UCHAR KeyMaterial[1];
+} NDIS_802_11_KEY;
+
+typedef struct NDIS_802_11_REMOVE_KEY {
+	ULONG Length;
+	ULONG KeyIndex;
+	NDIS_802_11_MAC_ADDRESS BSSID;
+} NDIS_802_11_REMOVE_KEY;
+
+typedef struct NDIS_802_11_AI_REQFI {
+	USHORT Capabilities;
+	USHORT ListenInterval;
+	NDIS_802_11_MAC_ADDRESS CurrentAPAddress;
+} NDIS_802_11_AI_REQFI;
+
+typedef struct NDIS_802_11_AI_RESFI {
+	USHORT Capabilities;
+	USHORT StatusCode;
+	USHORT AssociationId;
+} NDIS_802_11_AI_RESFI;
+
+typedef struct NDIS_802_11_ASSOCIATION_INFORMATION {
+	ULONG Length;
+	USHORT AvailableRequestFixedIEs;
+	NDIS_802_11_AI_REQFI RequestFixedIEs;
+	ULONG RequestIELength;
+	ULONG OffsetRequestIEs;
+	USHORT AvailableResponseFixedIEs;
+	NDIS_802_11_AI_RESFI ResponseFixedIEs;
+	ULONG ResponseIELength;
+	ULONG OffsetResponseIEs;
+} NDIS_802_11_ASSOCIATION_INFORMATION;
+
+typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
+	NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
+	NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
+} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
+
+typedef struct NDIS_802_11_CAPABILITY {
+	ULONG Length;
+	ULONG Version;
+	ULONG NoOfPMKIDs;
+	ULONG NoOfAuthEncryptPairsSupported;
+	NDIS_802_11_AUTHENTICATION_ENCRYPTION
+		AuthenticationEncryptionSupported[1];
+} NDIS_802_11_CAPABILITY;
+
+typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct BSSID_INFO {
+	NDIS_802_11_MAC_ADDRESS BSSID;
+	NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO;
+
+typedef struct NDIS_802_11_PMKID {
+	ULONG Length;
+	ULONG BSSIDInfoCount;
+	BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID;
+
+typedef enum NDIS_802_11_STATUS_TYPE {
+	Ndis802_11StatusType_Authentication,
+	Ndis802_11StatusType_PMKID_CandidateList = 2,
+	Ndis802_11StatusTypeMax
+} NDIS_802_11_STATUS_TYPE;
+
+typedef struct NDIS_802_11_STATUS_INDICATION {
+	NDIS_802_11_STATUS_TYPE StatusType;
+} NDIS_802_11_STATUS_INDICATION;
+
+typedef struct PMKID_CANDIDATE {
+	NDIS_802_11_MAC_ADDRESS BSSID;
+	ULONG Flags;
+} PMKID_CANDIDATE;
+
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
+
+typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
+	ULONG Version;
+	ULONG NumCandidates;
+	PMKID_CANDIDATE CandidateList[1];
+} NDIS_802_11_PMKID_CANDIDATE_LIST;
+
+typedef struct NDIS_802_11_AUTHENTICATION_REQUEST {
+	ULONG Length;
+	NDIS_802_11_MAC_ADDRESS Bssid;
+	ULONG Flags;
+} NDIS_802_11_AUTHENTICATION_REQUEST;
+
+#define NDIS_802_11_AUTH_REQUEST_REAUTH			0x01
+#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE		0x02
+#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR		0x06
+#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR		0x0E
+
+#endif /* OID_802_11_BSSID */
+
+
+#ifndef OID_802_11_PMKID
+/* Platform SDK for XP did not include WPA2, so add needed definitions */
+
+#define OID_802_11_CAPABILITY 			0x0d010122
+#define OID_802_11_PMKID 			0x0d010123
+
+#define Ndis802_11AuthModeWPA2 6
+#define Ndis802_11AuthModeWPA2PSK 7
+
+#define Ndis802_11StatusType_PMKID_CandidateList 2
+
+typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
+	NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
+	NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
+} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
+
+typedef struct NDIS_802_11_CAPABILITY {
+	ULONG Length;
+	ULONG Version;
+	ULONG NoOfPMKIDs;
+	ULONG NoOfAuthEncryptPairsSupported;
+	NDIS_802_11_AUTHENTICATION_ENCRYPTION
+		AuthenticationEncryptionSupported[1];
+} NDIS_802_11_CAPABILITY;
+
+typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct BSSID_INFO {
+	NDIS_802_11_MAC_ADDRESS BSSID;
+	NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO;
+
+typedef struct NDIS_802_11_PMKID {
+	ULONG Length;
+	ULONG BSSIDInfoCount;
+	BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID;
+
+typedef struct PMKID_CANDIDATE {
+	NDIS_802_11_MAC_ADDRESS BSSID;
+	ULONG Flags;
+} PMKID_CANDIDATE;
+
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
+
+typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
+	ULONG Version;
+	ULONG NumCandidates;
+	PMKID_CANDIDATE CandidateList[1];
+} NDIS_802_11_PMKID_CANDIDATE_LIST;
+
+#endif /* OID_802_11_CAPABILITY */
+
+
+#ifndef OID_DOT11_CURRENT_OPERATION_MODE
+/* Native 802.11 OIDs */
+#define OID_DOT11_NDIS_START 0x0D010300
+#define OID_DOT11_CURRENT_OPERATION_MODE (OID_DOT11_NDIS_START + 8)
+#define OID_DOT11_SCAN_REQUEST (OID_DOT11_NDIS_START + 11)
+
+typedef enum _DOT11_BSS_TYPE {
+	dot11_BSS_type_infrastructure = 1,
+	dot11_BSS_type_independent = 2,
+	dot11_BSS_type_any = 3
+} DOT11_BSS_TYPE, * PDOT11_BSS_TYPE;
+
+typedef UCHAR DOT11_MAC_ADDRESS[6];
+typedef DOT11_MAC_ADDRESS * PDOT11_MAC_ADDRESS;
+
+typedef enum _DOT11_SCAN_TYPE {
+	dot11_scan_type_active = 1,
+	dot11_scan_type_passive = 2,
+	dot11_scan_type_auto = 3,
+	dot11_scan_type_forced = 0x80000000
+} DOT11_SCAN_TYPE, * PDOT11_SCAN_TYPE;
+
+typedef struct _DOT11_SCAN_REQUEST_V2 {
+	DOT11_BSS_TYPE dot11BSSType;
+	DOT11_MAC_ADDRESS dot11BSSID;
+	DOT11_SCAN_TYPE dot11ScanType;
+	BOOLEAN bRestrictedScan;
+	ULONG udot11SSIDsOffset;
+	ULONG uNumOfdot11SSIDs;
+	BOOLEAN bUseRequestIE;
+	ULONG uRequestIDsOffset;
+	ULONG uNumOfRequestIDs;
+	ULONG uPhyTypeInfosOffset;
+	ULONG uNumOfPhyTypeInfos;
+	ULONG uIEsOffset;
+	ULONG uIEsLength;
+	UCHAR ucBuffer[1];
+} DOT11_SCAN_REQUEST_V2, * PDOT11_SCAN_REQUEST_V2;
+
+#endif /* OID_DOT11_CURRENT_OPERATION_MODE */
+
+#ifdef CONFIG_USE_NDISUIO
+#ifndef _WIN32_WCE
+#ifdef __MINGW32_VERSION
+typedef ULONG NDIS_OID;
+#endif /* __MINGW32_VERSION */
+/* from nuiouser.h */
+#define FSCTL_NDISUIO_BASE      FILE_DEVICE_NETWORK
+
+#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
+	CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
+
+#define IOCTL_NDISUIO_OPEN_DEVICE \
+	_NDISUIO_CTL_CODE(0x200, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_QUERY_OID_VALUE \
+	_NDISUIO_CTL_CODE(0x201, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_SET_OID_VALUE \
+	_NDISUIO_CTL_CODE(0x205, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_SET_ETHER_TYPE \
+	_NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_QUERY_BINDING \
+	_NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_BIND_WAIT \
+	_NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+typedef struct _NDISUIO_QUERY_OID
+{
+    NDIS_OID Oid;
+    UCHAR Data[sizeof(ULONG)];
+} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
+
+typedef struct _NDISUIO_SET_OID
+{
+    NDIS_OID Oid;
+    UCHAR Data[sizeof(ULONG)];
+} NDISUIO_SET_OID, *PNDISUIO_SET_OID;
+
+typedef struct _NDISUIO_QUERY_BINDING
+{
+	ULONG BindingIndex;
+	ULONG DeviceNameOffset;
+	ULONG DeviceNameLength;
+	ULONG DeviceDescrOffset;
+	ULONG DeviceDescrLength;
+} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING;
+#endif /* _WIN32_WCE */
+#endif /* CONFIG_USE_NDISUIO */
+
+
+static int ndis_get_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
+			char *data, size_t len)
+{
+#ifdef CONFIG_USE_NDISUIO
+	NDISUIO_QUERY_OID *o;
+	size_t buflen = sizeof(*o) + len;
+	DWORD written;
+	int ret;
+	size_t hdrlen;
+
+	o = os_zalloc(buflen);
+	if (o == NULL)
+		return -1;
+	o->Oid = oid;
+#ifdef _WIN32_WCE
+	o->ptcDeviceName = drv->adapter_name;
+#endif /* _WIN32_WCE */
+	if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_OID_VALUE,
+			     o, sizeof(NDISUIO_QUERY_OID), o, buflen, &written,
+			     NULL)) {
+		wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_QUERY_OID_VALUE "
+			   "failed (oid=%08x): %d", oid, (int) GetLastError());
+		os_free(o);
+		return -1;
+	}
+	hdrlen = sizeof(NDISUIO_QUERY_OID) - sizeof(o->Data);
+	if (written < hdrlen) {
+		wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d); "
+			   "too short", oid, (unsigned int) written);
+		os_free(o);
+		return -1;
+	}
+	written -= hdrlen;
+	if (written > len) {
+		wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d) > "
+			   "len (%d)",oid, (unsigned int) written, len);
+		os_free(o);
+		return -1;
+	}
+	os_memcpy(data, o->Data, written);
+	ret = written;
+	os_free(o);
+	return ret;
+#else /* CONFIG_USE_NDISUIO */
+	char *buf;
+	PACKET_OID_DATA *o;
+	int ret;
+
+	buf = os_zalloc(sizeof(*o) + len);
+	if (buf == NULL)
+		return -1;
+	o = (PACKET_OID_DATA *) buf;
+	o->Oid = oid;
+	o->Length = len;
+
+	if (!PacketRequest(drv->adapter, FALSE, o)) {
+		wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+			   __func__, oid, len);
+		os_free(buf);
+		return -1;
+	}
+	if (o->Length > len) {
+		wpa_printf(MSG_DEBUG, "%s: oid=0x%x Length (%d) > len (%d)",
+			   __func__, oid, (unsigned int) o->Length, len);
+		os_free(buf);
+		return -1;
+	}
+	os_memcpy(data, o->Data, o->Length);
+	ret = o->Length;
+	os_free(buf);
+	return ret;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_set_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
+			const char *data, size_t len)
+{
+#ifdef CONFIG_USE_NDISUIO
+	NDISUIO_SET_OID *o;
+	size_t buflen, reallen;
+	DWORD written;
+	char txt[50];
+
+	os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
+	wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
+
+	buflen = sizeof(*o) + len;
+	reallen = buflen - sizeof(o->Data);
+	o = os_zalloc(buflen);
+	if (o == NULL)
+		return -1;
+	o->Oid = oid;
+#ifdef _WIN32_WCE
+	o->ptcDeviceName = drv->adapter_name;
+#endif /* _WIN32_WCE */
+	if (data)
+		os_memcpy(o->Data, data, len);
+	if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_SET_OID_VALUE,
+			     o, reallen, NULL, 0, &written, NULL)) {
+		wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_SET_OID_VALUE "
+			   "(oid=%08x) failed: %d", oid, (int) GetLastError());
+		os_free(o);
+		return -1;
+	}
+	os_free(o);
+	return 0;
+#else /* CONFIG_USE_NDISUIO */
+	char *buf;
+	PACKET_OID_DATA *o;
+	char txt[50];
+
+	os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
+	wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
+
+	buf = os_zalloc(sizeof(*o) + len);
+	if (buf == NULL)
+		return -1;
+	o = (PACKET_OID_DATA *) buf;
+	o->Oid = oid;
+	o->Length = len;
+	if (data)
+		os_memcpy(o->Data, data, len);
+
+	if (!PacketRequest(drv->adapter, TRUE, o)) {
+		wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+			   __func__, oid, len);
+		os_free(buf);
+		return -1;
+	}
+	os_free(buf);
+	return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_set_auth_mode(struct wpa_driver_ndis_data *drv, int mode)
+{
+	u32 auth_mode = mode;
+	if (ndis_set_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+			 (char *) &auth_mode, sizeof(auth_mode)) < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+			   "OID_802_11_AUTHENTICATION_MODE (%d)",
+			   (int) auth_mode);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int ndis_get_auth_mode(struct wpa_driver_ndis_data *drv)
+{
+	u32 auth_mode;
+	int res;
+	res = ndis_get_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+			   (char *) &auth_mode, sizeof(auth_mode));
+	if (res != sizeof(auth_mode)) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
+			   "OID_802_11_AUTHENTICATION_MODE");
+		return -1;
+	}
+	return auth_mode;
+}
+
+
+static int ndis_set_encr_status(struct wpa_driver_ndis_data *drv, int encr)
+{
+	u32 encr_status = encr;
+	if (ndis_set_oid(drv, OID_802_11_ENCRYPTION_STATUS,
+			 (char *) &encr_status, sizeof(encr_status)) < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+			   "OID_802_11_ENCRYPTION_STATUS (%d)", encr);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int ndis_get_encr_status(struct wpa_driver_ndis_data *drv)
+{
+	u32 encr;
+	int res;
+	res = ndis_get_oid(drv, OID_802_11_ENCRYPTION_STATUS,
+			   (char *) &encr, sizeof(encr));
+	if (res != sizeof(encr)) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
+			   "OID_802_11_ENCRYPTION_STATUS");
+		return -1;
+	}
+	return encr;
+}
+
+
+static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+
+	if (drv->wired) {
+		/*
+		 * Report PAE group address as the "BSSID" for wired
+		 * connection.
+		 */
+		os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+		return 0;
+	}
+
+	return ndis_get_oid(drv, OID_802_11_BSSID, (char *) bssid, ETH_ALEN) <
+		0 ? -1 : 0;
+}
+
+
+static int wpa_driver_ndis_get_ssid(void *priv, u8 *ssid)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	NDIS_802_11_SSID buf;
+	int res;
+
+	res = ndis_get_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
+	if (res < 4) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to get SSID");
+		if (drv->wired) {
+			wpa_printf(MSG_DEBUG, "NDIS: Allow get_ssid failure "
+				   "with a wired interface");
+			return 0;
+		}
+		return -1;
+	}
+	os_memcpy(ssid, buf.Ssid, buf.SsidLength);
+	return buf.SsidLength;
+}
+
+
+static int wpa_driver_ndis_set_ssid(struct wpa_driver_ndis_data *drv,
+				    const u8 *ssid, size_t ssid_len)
+{
+	NDIS_802_11_SSID buf;
+
+	os_memset(&buf, 0, sizeof(buf));
+	buf.SsidLength = ssid_len;
+	os_memcpy(buf.Ssid, ssid, ssid_len);
+	/*
+	 * Make sure radio is marked enabled here so that scan request will not
+	 * force SSID to be changed to a random one in order to enable radio at
+	 * that point.
+	 */
+	drv->radio_enabled = 1;
+	return ndis_set_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
+}
+
+
+/* Disconnect using OID_802_11_DISASSOCIATE. This will also turn the radio off.
+ */
+static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv)
+{
+	drv->radio_enabled = 0;
+	return ndis_set_oid(drv, OID_802_11_DISASSOCIATE, "    ", 4);
+}
+
+
+/* Disconnect by setting SSID to random (i.e., likely not used). */
+static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv)
+{
+	char ssid[SSID_MAX_LEN];
+	int i;
+	for (i = 0; i < SSID_MAX_LEN; i++)
+		ssid[i] = rand() & 0xff;
+	return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, SSID_MAX_LEN);
+}
+
+
+static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr,
+					  int reason_code)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	return wpa_driver_ndis_disconnect(drv);
+}
+
+
+static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static int wpa_driver_ndis_scan_native80211(
+	struct wpa_driver_ndis_data *drv,
+	struct wpa_driver_scan_params *params)
+{
+	DOT11_SCAN_REQUEST_V2 req;
+	int res;
+
+	os_memset(&req, 0, sizeof(req));
+	req.dot11BSSType = dot11_BSS_type_any;
+	os_memset(req.dot11BSSID, 0xff, ETH_ALEN);
+	req.dot11ScanType = dot11_scan_type_auto;
+	res = ndis_set_oid(drv, OID_DOT11_SCAN_REQUEST, (char *) &req,
+			   sizeof(req));
+	eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+	eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv,
+			       drv->ctx);
+	return res;
+}
+
+
+static int wpa_driver_ndis_scan(void *priv,
+				struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	int res;
+
+	if (drv->native80211)
+		return wpa_driver_ndis_scan_native80211(drv, params);
+
+	if (!drv->radio_enabled) {
+		wpa_printf(MSG_DEBUG, "NDIS: turning radio on before the first"
+			   " scan");
+		if (wpa_driver_ndis_disconnect(drv) < 0) {
+			wpa_printf(MSG_DEBUG, "NDIS: failed to enable radio");
+		}
+		drv->radio_enabled = 1;
+	}
+
+	res = ndis_set_oid(drv, OID_802_11_BSSID_LIST_SCAN, "    ", 4);
+	eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+	eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv,
+			       drv->ctx);
+	return res;
+}
+
+
+static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
+{
+	const u8 *end, *pos;
+
+	pos = (const u8 *) (res + 1);
+	end = pos + res->ie_len;
+
+	while (pos + 1 < end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		if (pos[0] == ie)
+			return pos;
+		pos += 2 + pos[1];
+	}
+
+	return NULL;
+}
+
+
+static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid(
+	struct wpa_scan_res *r, NDIS_802_11_SSID *ssid)
+{
+	struct wpa_scan_res *nr;
+	u8 *pos;
+
+	if (wpa_scan_get_ie(r, WLAN_EID_SSID))
+		return r; /* SSID IE already present */
+
+	if (ssid->SsidLength == 0 || ssid->SsidLength > SSID_MAX_LEN)
+		return r; /* No valid SSID inside scan data */
+
+	nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength);
+	if (nr == NULL)
+		return r;
+
+	pos = ((u8 *) (nr + 1)) + nr->ie_len;
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = ssid->SsidLength;
+	os_memcpy(pos, ssid->Ssid, ssid->SsidLength);
+	nr->ie_len += 2 + ssid->SsidLength;
+
+	return nr;
+}
+
+
+static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	NDIS_802_11_BSSID_LIST_EX *b;
+	size_t blen, count, i;
+	int len;
+	char *pos;
+	struct wpa_scan_results *results;
+	struct wpa_scan_res *r;
+
+	blen = 65535;
+	b = os_zalloc(blen);
+	if (b == NULL)
+		return NULL;
+	len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
+	if (len < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
+		os_free(b);
+		return NULL;
+	}
+	count = b->NumberOfItems;
+
+	results = os_zalloc(sizeof(*results));
+	if (results == NULL) {
+		os_free(b);
+		return NULL;
+	}
+	results->res = os_calloc(count, sizeof(struct wpa_scan_res *));
+	if (results->res == NULL) {
+		os_free(results);
+		os_free(b);
+		return NULL;
+	}
+
+	pos = (char *) &b->Bssid[0];
+	for (i = 0; i < count; i++) {
+		NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
+		NDIS_802_11_FIXED_IEs *fixed;
+
+		if (bss->IELength < sizeof(NDIS_802_11_FIXED_IEs)) {
+			wpa_printf(MSG_DEBUG, "NDIS: too small IELength=%d",
+				   (int) bss->IELength);
+			break;
+		}
+		if (((char *) bss->IEs) + bss->IELength  > (char *) b + blen) {
+			/*
+			 * Some NDIS drivers have been reported to include an
+			 * entry with an invalid IELength in scan results and
+			 * this has crashed wpa_supplicant, so validate the
+			 * returned value before using it.
+			 */
+			wpa_printf(MSG_DEBUG, "NDIS: skipped invalid scan "
+				   "result IE (BSSID=" MACSTR ") IELength=%d",
+				   MAC2STR(bss->MacAddress),
+				   (int) bss->IELength);
+			break;
+		}
+
+		r = os_zalloc(sizeof(*r) + bss->IELength -
+			      sizeof(NDIS_802_11_FIXED_IEs));
+		if (r == NULL)
+			break;
+
+		os_memcpy(r->bssid, bss->MacAddress, ETH_ALEN);
+		r->level = (int) bss->Rssi;
+		r->freq = bss->Configuration.DSConfig / 1000;
+		fixed = (NDIS_802_11_FIXED_IEs *) bss->IEs;
+		r->beacon_int = WPA_GET_LE16((u8 *) &fixed->BeaconInterval);
+		r->caps = WPA_GET_LE16((u8 *) &fixed->Capabilities);
+		r->tsf = WPA_GET_LE64(fixed->Timestamp);
+		os_memcpy(r + 1, bss->IEs + sizeof(NDIS_802_11_FIXED_IEs),
+			  bss->IELength - sizeof(NDIS_802_11_FIXED_IEs));
+		r->ie_len = bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
+		r = wpa_driver_ndis_add_scan_ssid(r, &bss->Ssid);
+
+		results->res[results->num++] = r;
+
+		pos += bss->Length;
+		if (pos > (char *) b + blen)
+			break;
+	}
+
+	os_free(b);
+
+	return results;
+}
+
+
+static int wpa_driver_ndis_remove_key(struct wpa_driver_ndis_data *drv,
+				      int key_idx, const u8 *addr,
+				      const u8 *bssid, int pairwise)
+{
+	NDIS_802_11_REMOVE_KEY rkey;
+	NDIS_802_11_KEY_INDEX index;
+	int res, res2;
+
+	os_memset(&rkey, 0, sizeof(rkey));
+
+	rkey.Length = sizeof(rkey);
+	rkey.KeyIndex = key_idx;
+	if (pairwise)
+		rkey.KeyIndex |= 1 << 30;
+	os_memcpy(rkey.BSSID, bssid, ETH_ALEN);
+
+	res = ndis_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey,
+			   sizeof(rkey));
+	if (!pairwise) {
+		index = key_idx;
+		res2 = ndis_set_oid(drv, OID_802_11_REMOVE_WEP,
+				    (char *) &index, sizeof(index));
+	} else
+		res2 = 0;
+
+	if (res < 0 && res2 < 0)
+		return -1;
+	return 0;
+}
+
+
+static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv,
+				   int pairwise, int key_idx, int set_tx,
+				   const u8 *key, size_t key_len)
+{
+	NDIS_802_11_WEP *wep;
+	size_t len;
+	int res;
+
+	len = 12 + key_len;
+	wep = os_zalloc(len);
+	if (wep == NULL)
+		return -1;
+	wep->Length = len;
+	wep->KeyIndex = key_idx;
+	if (set_tx)
+		wep->KeyIndex |= 1 << 31;
+#if 0 /* Setting bit30 does not seem to work with some NDIS drivers */
+	if (pairwise)
+		wep->KeyIndex |= 1 << 30;
+#endif
+	wep->KeyLength = key_len;
+	os_memcpy(wep->KeyMaterial, key, key_len);
+
+	wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_WEP",
+			(u8 *) wep, len);
+	res = ndis_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len);
+
+	os_free(wep);
+
+	return res;
+}
+
+
+static int wpa_driver_ndis_set_key(const char *ifname, void *priv,
+				   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)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	size_t len, i;
+	NDIS_802_11_KEY *nkey;
+	int res, pairwise;
+	u8 bssid[ETH_ALEN];
+
+	if (addr == NULL || is_broadcast_ether_addr(addr)) {
+		/* Group Key */
+		pairwise = 0;
+		if (wpa_driver_ndis_get_bssid(drv, bssid) < 0)
+			os_memset(bssid, 0xff, ETH_ALEN);
+	} else {
+		/* Pairwise Key */
+		pairwise = 1;
+		os_memcpy(bssid, addr, ETH_ALEN);
+	}
+
+	if (alg == WPA_ALG_NONE || key_len == 0) {
+		return wpa_driver_ndis_remove_key(drv, key_idx, addr, bssid,
+						  pairwise);
+	}
+
+	if (alg == WPA_ALG_WEP) {
+		return wpa_driver_ndis_add_wep(drv, pairwise, key_idx, set_tx,
+					       key, key_len);
+	}
+
+	len = 12 + 6 + 6 + 8 + key_len;
+
+	nkey = os_zalloc(len);
+	if (nkey == NULL)
+		return -1;
+
+	nkey->Length = len;
+	nkey->KeyIndex = key_idx;
+	if (set_tx)
+		nkey->KeyIndex |= 1 << 31;
+	if (pairwise)
+		nkey->KeyIndex |= 1 << 30;
+	if (seq && seq_len)
+		nkey->KeyIndex |= 1 << 29;
+	nkey->KeyLength = key_len;
+	os_memcpy(nkey->BSSID, bssid, ETH_ALEN);
+	if (seq && seq_len) {
+		for (i = 0; i < seq_len; i++)
+			nkey->KeyRSC |= (ULONGLONG) seq[i] << (i * 8);
+	}
+	if (alg == WPA_ALG_TKIP && key_len == 32) {
+		os_memcpy(nkey->KeyMaterial, key, 16);
+		os_memcpy(nkey->KeyMaterial + 16, key + 24, 8);
+		os_memcpy(nkey->KeyMaterial + 24, key + 16, 8);
+	} else {
+		os_memcpy(nkey->KeyMaterial, key, key_len);
+	}
+
+	wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_KEY",
+			(u8 *) nkey, len);
+	res = ndis_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len);
+	os_free(nkey);
+
+	return res;
+}
+
+
+static int
+wpa_driver_ndis_associate(void *priv,
+			  struct wpa_driver_associate_params *params)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	u32 auth_mode, encr, priv_mode, mode;
+	u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+	drv->mode = params->mode;
+
+	/* Note: Setting OID_802_11_INFRASTRUCTURE_MODE clears current keys,
+	 * so static WEP keys needs to be set again after this. */
+	if (params->mode == IEEE80211_MODE_IBSS) {
+		mode = Ndis802_11IBSS;
+		/* Need to make sure that BSSID polling is enabled for
+		 * IBSS mode. */
+		eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+		eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout,
+				       drv, NULL);
+	} else
+		mode = Ndis802_11Infrastructure;
+	if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
+			 (char *) &mode, sizeof(mode)) < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+			   "OID_802_11_INFRASTRUCTURE_MODE (%d)",
+			   (int) mode);
+		/* Try to continue anyway */
+	}
+
+	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++) {
+			if (!params->wep_key[i])
+				continue;
+			wpa_printf(MSG_DEBUG, "NDIS: Re-setting static WEP "
+				   "key %d", i);
+			wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
+						bcast, i,
+						i == params->wep_tx_keyidx,
+						NULL, 0, params->wep_key[i],
+						params->wep_key_len[i]);
+		}
+	}
+
+	if (params->wpa_ie == NULL || params->wpa_ie_len == 0) {
+		if (params->auth_alg & WPA_AUTH_ALG_SHARED) {
+			if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+				auth_mode = Ndis802_11AuthModeAutoSwitch;
+			else
+				auth_mode = Ndis802_11AuthModeShared;
+		} else
+			auth_mode = Ndis802_11AuthModeOpen;
+		priv_mode = Ndis802_11PrivFilterAcceptAll;
+	} else if (params->wpa_ie[0] == WLAN_EID_RSN) {
+		priv_mode = Ndis802_11PrivFilter8021xWEP;
+		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 == WPA_KEY_MGMT_WPS) {
+		auth_mode = Ndis802_11AuthModeOpen;
+		priv_mode = Ndis802_11PrivFilterAcceptAll;
+		if (params->wps == WPS_MODE_PRIVACY) {
+			u8 dummy_key[5] = { 0x11, 0x22, 0x33, 0x44, 0x55 };
+			/*
+			 * Some NDIS drivers refuse to associate in open mode
+			 * configuration due to Privacy field mismatch, so use
+			 * a workaround to make the configuration look like
+			 * matching one for WPS provisioning.
+			 */
+			wpa_printf(MSG_DEBUG, "NDIS: Set dummy WEP key as a "
+				   "workaround to allow driver to associate "
+				   "for WPS");
+			wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
+						bcast, 0, 1,
+						NULL, 0, dummy_key,
+						sizeof(dummy_key));
+		}
+#endif /* CONFIG_WPS */
+	} else {
+		priv_mode = Ndis802_11PrivFilter8021xWEP;
+		if (params->key_mgmt_suite == WPA_KEY_MGMT_WPA_NONE)
+			auth_mode = Ndis802_11AuthModeWPANone;
+		else if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
+			auth_mode = Ndis802_11AuthModeWPAPSK;
+		else
+			auth_mode = Ndis802_11AuthModeWPA;
+	}
+
+	switch (params->pairwise_suite) {
+	case WPA_CIPHER_CCMP:
+		encr = Ndis802_11Encryption3Enabled;
+		break;
+	case WPA_CIPHER_TKIP:
+		encr = Ndis802_11Encryption2Enabled;
+		break;
+	case WPA_CIPHER_WEP40:
+	case WPA_CIPHER_WEP104:
+		encr = Ndis802_11Encryption1Enabled;
+		break;
+	case WPA_CIPHER_NONE:
+#ifdef CONFIG_WPS
+		if (params->wps == WPS_MODE_PRIVACY) {
+			encr = Ndis802_11Encryption1Enabled;
+			break;
+		}
+#endif /* CONFIG_WPS */
+		if (params->group_suite == WPA_CIPHER_CCMP)
+			encr = Ndis802_11Encryption3Enabled;
+		else if (params->group_suite == WPA_CIPHER_TKIP)
+			encr = Ndis802_11Encryption2Enabled;
+		else
+			encr = Ndis802_11EncryptionDisabled;
+		break;
+	default:
+#ifdef CONFIG_WPS
+		if (params->wps == WPS_MODE_PRIVACY) {
+			encr = Ndis802_11Encryption1Enabled;
+			break;
+		}
+#endif /* CONFIG_WPS */
+		encr = Ndis802_11EncryptionDisabled;
+		break;
+	};
+
+	if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER,
+			 (char *) &priv_mode, sizeof(priv_mode)) < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+			   "OID_802_11_PRIVACY_FILTER (%d)",
+			   (int) priv_mode);
+		/* Try to continue anyway */
+	}
+
+	ndis_set_auth_mode(drv, auth_mode);
+	ndis_set_encr_status(drv, encr);
+
+	if (params->bssid) {
+		ndis_set_oid(drv, OID_802_11_BSSID, (char *) params->bssid,
+			     ETH_ALEN);
+		drv->oid_bssid_set = 1;
+	} else if (drv->oid_bssid_set) {
+		ndis_set_oid(drv, OID_802_11_BSSID, "\xff\xff\xff\xff\xff\xff",
+			     ETH_ALEN);
+		drv->oid_bssid_set = 0;
+	}
+
+	return wpa_driver_ndis_set_ssid(drv, params->ssid, params->ssid_len);
+}
+
+
+static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv)
+{
+	int len, count, i, ret;
+	struct ndis_pmkid_entry *entry;
+	NDIS_802_11_PMKID *p;
+
+	count = 0;
+	entry = drv->pmkid;
+	while (entry) {
+		count++;
+		if (count >= drv->no_of_pmkid)
+			break;
+		entry = entry->next;
+	}
+	len = 8 + count * sizeof(BSSID_INFO);
+	p = os_zalloc(len);
+	if (p == NULL)
+		return -1;
+
+	p->Length = len;
+	p->BSSIDInfoCount = count;
+	entry = drv->pmkid;
+	for (i = 0; i < count; i++) {
+		os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN);
+		os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16);
+		entry = entry->next;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", (u8 *) p, len);
+	ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) p, len);
+	os_free(p);
+	return ret;
+}
+
+
+static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid,
+				     const u8 *pmkid)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	struct ndis_pmkid_entry *entry, *prev;
+
+	if (drv->no_of_pmkid == 0)
+		return 0;
+
+	prev = NULL;
+	entry = drv->pmkid;
+	while (entry) {
+		if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0)
+			break;
+		prev = entry;
+		entry = entry->next;
+	}
+
+	if (entry) {
+		/* Replace existing entry for this BSSID and move it into the
+		 * beginning of the list. */
+		os_memcpy(entry->pmkid, pmkid, 16);
+		if (prev) {
+			prev->next = entry->next;
+			entry->next = drv->pmkid;
+			drv->pmkid = entry;
+		}
+	} else {
+		entry = os_malloc(sizeof(*entry));
+		if (entry) {
+			os_memcpy(entry->bssid, bssid, ETH_ALEN);
+			os_memcpy(entry->pmkid, pmkid, 16);
+			entry->next = drv->pmkid;
+			drv->pmkid = entry;
+		}
+	}
+
+	return wpa_driver_ndis_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid,
+		 			const u8 *pmkid)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	struct ndis_pmkid_entry *entry, *prev;
+
+	if (drv->no_of_pmkid == 0)
+		return 0;
+
+	entry = drv->pmkid;
+	prev = NULL;
+	while (entry) {
+		if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 &&
+		    os_memcmp(entry->pmkid, pmkid, 16) == 0) {
+			if (prev)
+				prev->next = entry->next;
+			else
+				drv->pmkid = entry->next;
+			os_free(entry);
+			break;
+		}
+		prev = entry;
+		entry = entry->next;
+	}
+	return wpa_driver_ndis_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ndis_flush_pmkid(void *priv)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	NDIS_802_11_PMKID p;
+	struct ndis_pmkid_entry *pmkid, *prev;
+	int prev_authmode, ret;
+
+	if (drv->no_of_pmkid == 0)
+		return 0;
+
+	pmkid = drv->pmkid;
+	drv->pmkid = NULL;
+	while (pmkid) {
+		prev = pmkid;
+		pmkid = pmkid->next;
+		os_free(prev);
+	}
+
+	/*
+	 * Some drivers may refuse OID_802_11_PMKID if authMode is not set to
+	 * WPA2, so change authMode temporarily, if needed.
+	 */
+	prev_authmode = ndis_get_auth_mode(drv);
+	if (prev_authmode != Ndis802_11AuthModeWPA2)
+		ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA2);
+
+	os_memset(&p, 0, sizeof(p));
+	p.Length = 8;
+	p.BSSIDInfoCount = 0;
+	wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)",
+		    (u8 *) &p, 8);
+	ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8);
+
+	if (prev_authmode != Ndis802_11AuthModeWPA2)
+		ndis_set_auth_mode(drv, prev_authmode);
+
+	return ret;
+}
+
+
+static int wpa_driver_ndis_get_associnfo(struct wpa_driver_ndis_data *drv)
+{
+	char buf[512], *pos;
+	NDIS_802_11_ASSOCIATION_INFORMATION *ai;
+	int len;
+	union wpa_event_data data;
+	NDIS_802_11_BSSID_LIST_EX *b;
+	size_t blen, i;
+
+	len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, buf,
+			   sizeof(buf));
+	if (len < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: failed to get association "
+			   "information");
+		return -1;
+	}
+	if (len > sizeof(buf)) {
+		/* Some drivers seem to be producing incorrect length for this
+		 * data. Limit the length to the current buffer size to avoid
+		 * crashing in hexdump. The data seems to be otherwise valid,
+		 * so better try to use it. */
+		wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association "
+			   "information length %d", len);
+		len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION,
+				   buf, sizeof(buf));
+		if (len < -1) {
+			wpa_printf(MSG_DEBUG, "NDIS: re-reading association "
+				   "information failed");
+			return -1;
+		}
+		if (len > sizeof(buf)) {
+			wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association"
+				   " information length %d (re-read)", len);
+			len = sizeof(buf);
+		}
+	}
+	wpa_hexdump(MSG_MSGDUMP, "NDIS: association information",
+		    (u8 *) buf, len);
+	if (len < sizeof(*ai)) {
+		wpa_printf(MSG_DEBUG, "NDIS: too short association "
+			   "information");
+		return -1;
+	}
+	ai = (NDIS_802_11_ASSOCIATION_INFORMATION *) buf;
+	wpa_printf(MSG_DEBUG, "NDIS: ReqFixed=0x%x RespFixed=0x%x off_req=%d "
+		   "off_resp=%d len_req=%d len_resp=%d",
+		   ai->AvailableRequestFixedIEs, ai->AvailableResponseFixedIEs,
+		   (int) ai->OffsetRequestIEs, (int) ai->OffsetResponseIEs,
+		   (int) ai->RequestIELength, (int) ai->ResponseIELength);
+
+	if (ai->OffsetRequestIEs + ai->RequestIELength > (unsigned) len ||
+	    ai->OffsetResponseIEs + ai->ResponseIELength > (unsigned) len) {
+		wpa_printf(MSG_DEBUG, "NDIS: association information - "
+			   "IE overflow");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "NDIS: Request IEs",
+		    (u8 *) buf + ai->OffsetRequestIEs, ai->RequestIELength);
+	wpa_hexdump(MSG_MSGDUMP, "NDIS: Response IEs",
+		    (u8 *) buf + ai->OffsetResponseIEs, ai->ResponseIELength);
+
+	os_memset(&data, 0, sizeof(data));
+	data.assoc_info.req_ies = (u8 *) buf + ai->OffsetRequestIEs;
+	data.assoc_info.req_ies_len = ai->RequestIELength;
+	data.assoc_info.resp_ies = (u8 *) buf + ai->OffsetResponseIEs;
+	data.assoc_info.resp_ies_len = ai->ResponseIELength;
+
+	blen = 65535;
+	b = os_zalloc(blen);
+	if (b == NULL)
+		goto skip_scan_results;
+	len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
+	if (len < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
+		os_free(b);
+		b = NULL;
+		goto skip_scan_results;
+	}
+	wpa_printf(MSG_DEBUG, "NDIS: %d BSSID items to process for AssocInfo",
+		   (unsigned int) b->NumberOfItems);
+
+	pos = (char *) &b->Bssid[0];
+	for (i = 0; i < b->NumberOfItems; i++) {
+		NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
+		if (os_memcmp(drv->bssid, bss->MacAddress, ETH_ALEN) == 0 &&
+		    bss->IELength > sizeof(NDIS_802_11_FIXED_IEs)) {
+			data.assoc_info.beacon_ies =
+				((u8 *) bss->IEs) +
+				sizeof(NDIS_802_11_FIXED_IEs);
+			data.assoc_info.beacon_ies_len =
+				bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
+			wpa_hexdump(MSG_MSGDUMP, "NDIS: Beacon IEs",
+				    data.assoc_info.beacon_ies,
+				    data.assoc_info.beacon_ies_len);
+			break;
+		}
+		pos += bss->Length;
+		if (pos > (char *) b + blen)
+			break;
+	}
+
+skip_scan_results:
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
+
+	os_free(b);
+
+	return 0;
+}
+
+
+static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_driver_ndis_data *drv = eloop_ctx;
+	u8 bssid[ETH_ALEN];
+	int poll;
+
+	if (drv->wired)
+		return;
+
+	if (wpa_driver_ndis_get_bssid(drv, bssid)) {
+		/* Disconnected */
+		if (!is_zero_ether_addr(drv->bssid)) {
+			os_memset(drv->bssid, 0, ETH_ALEN);
+			wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+		}
+	} else {
+		/* Connected */
+		if (os_memcmp(drv->bssid, bssid, ETH_ALEN) != 0) {
+			os_memcpy(drv->bssid, bssid, ETH_ALEN);
+			wpa_driver_ndis_get_associnfo(drv);
+			wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+		}
+	}
+
+	/* When using integrated NDIS event receiver, we can skip BSSID
+	 * polling when using infrastructure network. However, when using
+	 * IBSS mode, many driver do not seem to generate connection event,
+	 * so we need to enable BSSID polling to figure out when IBSS network
+	 * has been formed.
+	 */
+	poll = drv->mode == IEEE80211_MODE_IBSS;
+#ifndef CONFIG_NDIS_EVENTS_INTEGRATED
+#ifndef _WIN32_WCE
+	poll = 1;
+#endif /* _WIN32_WCE */
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+	if (poll) {
+		eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout,
+					drv, NULL);
+	}
+}
+
+
+static void wpa_driver_ndis_poll(void *priv)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+	wpa_driver_ndis_poll_timeout(drv, NULL);
+}
+
+
+/* Called when driver generates Media Connect Event by calling
+ * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_CONNECT */
+void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv)
+{
+	wpa_printf(MSG_DEBUG, "NDIS: Media Connect Event");
+	if (wpa_driver_ndis_get_bssid(drv, drv->bssid) == 0) {
+		wpa_driver_ndis_get_associnfo(drv);
+		wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+	}
+}
+
+
+/* Called when driver generates Media Disconnect Event by calling
+ * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_DISCONNECT */
+void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv)
+{
+	wpa_printf(MSG_DEBUG, "NDIS: Media Disconnect Event");
+	os_memset(drv->bssid, 0, ETH_ALEN);
+	wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+}
+
+
+static void wpa_driver_ndis_event_auth(struct wpa_driver_ndis_data *drv,
+				       const u8 *data, size_t data_len)
+{
+	NDIS_802_11_AUTHENTICATION_REQUEST *req;
+	int pairwise = 0, group = 0;
+	union wpa_event_data event;
+
+	if (data_len < sizeof(*req)) {
+		wpa_printf(MSG_DEBUG, "NDIS: Too short Authentication Request "
+			   "Event (len=%d)", data_len);
+		return;
+	}
+	req = (NDIS_802_11_AUTHENTICATION_REQUEST *) data;
+
+	wpa_printf(MSG_DEBUG, "NDIS: Authentication Request Event: "
+		   "Bssid " MACSTR " Flags 0x%x",
+		   MAC2STR(req->Bssid), (int) req->Flags);
+
+	if ((req->Flags & NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) ==
+	    NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR)
+		pairwise = 1;
+	else if ((req->Flags & NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) ==
+	    NDIS_802_11_AUTH_REQUEST_GROUP_ERROR)
+		group = 1;
+
+	if (pairwise || group) {
+		os_memset(&event, 0, sizeof(event));
+		event.michael_mic_failure.unicast = pairwise;
+		wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE,
+				     &event);
+	}
+}
+
+
+static void wpa_driver_ndis_event_pmkid(struct wpa_driver_ndis_data *drv,
+					const u8 *data, size_t data_len)
+{
+	NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid;
+	size_t i;
+	union wpa_event_data event;
+
+	if (data_len < 8) {
+		wpa_printf(MSG_DEBUG, "NDIS: Too short PMKID Candidate List "
+			   "Event (len=%d)", data_len);
+		return;
+	}
+	pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data;
+	wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List Event - Version %d "
+		   "NumCandidates %d",
+		   (int) pmkid->Version, (int) pmkid->NumCandidates);
+
+	if (pmkid->Version != 1) {
+		wpa_printf(MSG_DEBUG, "NDIS: Unsupported PMKID Candidate List "
+			   "Version %d", (int) pmkid->Version);
+		return;
+	}
+
+	if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) {
+		wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List underflow");
+		return;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	for (i = 0; i < pmkid->NumCandidates; i++) {
+		PMKID_CANDIDATE *p = &pmkid->CandidateList[i];
+		wpa_printf(MSG_DEBUG, "NDIS: %d: " MACSTR " Flags 0x%x",
+			   i, MAC2STR(p->BSSID), (int) p->Flags);
+		os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN);
+		event.pmkid_candidate.index = i;
+		event.pmkid_candidate.preauth =
+			p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED;
+		wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE,
+				     &event);
+	}
+}
+
+
+/* Called when driver calls NdisMIndicateStatus() with
+ * NDIS_STATUS_MEDIA_SPECIFIC_INDICATION */
+void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv,
+					  const u8 *data, size_t data_len)
+{
+	NDIS_802_11_STATUS_INDICATION *status;
+
+	if (data == NULL || data_len < sizeof(*status))
+		return;
+
+	wpa_hexdump(MSG_DEBUG, "NDIS: Media Specific Indication",
+		    data, data_len);
+
+	status = (NDIS_802_11_STATUS_INDICATION *) data;
+	data += sizeof(status);
+	data_len -= sizeof(status);
+
+	switch (status->StatusType) {
+	case Ndis802_11StatusType_Authentication:
+		wpa_driver_ndis_event_auth(drv, data, data_len);
+		break;
+	case Ndis802_11StatusType_PMKID_CandidateList:
+		wpa_driver_ndis_event_pmkid(drv, data, data_len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "NDIS: Unknown StatusType %d",
+			   (int) status->StatusType);
+		break;
+	}
+}
+
+
+/* Called when an adapter is added */
+void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv)
+{
+	union wpa_event_data event;
+	int i;
+
+	wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Arrival");
+
+	for (i = 0; i < 30; i++) {
+		/* Re-open Packet32/NDISUIO connection */
+		wpa_driver_ndis_adapter_close(drv);
+		if (wpa_driver_ndis_adapter_init(drv) < 0 ||
+		    wpa_driver_ndis_adapter_open(drv) < 0) {
+			wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialization "
+				   "(%d) failed", i);
+			os_sleep(1, 0);
+		} else {
+			wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialized");
+			break;
+		}
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	os_strlcpy(event.interface_status.ifname, drv->ifname,
+		   sizeof(event.interface_status.ifname));
+	event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+/* Called when an adapter is removed */
+void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv)
+{
+	union wpa_event_data event;
+
+	wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Removal");
+	os_memset(&event, 0, sizeof(event));
+	os_strlcpy(event.interface_status.ifname, drv->ifname,
+		   sizeof(event.interface_status.ifname));
+	event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static void
+wpa_driver_ndis_get_wpa_capability(struct wpa_driver_ndis_data *drv)
+{
+	wpa_printf(MSG_DEBUG, "NDIS: verifying driver WPA capability");
+
+	if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA) == 0 &&
+	    ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPA) {
+		wpa_printf(MSG_DEBUG, "NDIS: WPA key management supported");
+		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA;
+	}
+
+	if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPAPSK) == 0 &&
+	    ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPAPSK) {
+		wpa_printf(MSG_DEBUG, "NDIS: WPA-PSK key management "
+			   "supported");
+		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+	}
+
+	if (ndis_set_encr_status(drv, Ndis802_11Encryption3Enabled) == 0 &&
+	    ndis_get_encr_status(drv) == Ndis802_11Encryption3KeyAbsent) {
+		wpa_printf(MSG_DEBUG, "NDIS: CCMP encryption supported");
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+	}
+
+	if (ndis_set_encr_status(drv, Ndis802_11Encryption2Enabled) == 0 &&
+	    ndis_get_encr_status(drv) == Ndis802_11Encryption2KeyAbsent) {
+		wpa_printf(MSG_DEBUG, "NDIS: TKIP encryption supported");
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+	}
+
+	if (ndis_set_encr_status(drv, Ndis802_11Encryption1Enabled) == 0 &&
+	    ndis_get_encr_status(drv) == Ndis802_11Encryption1KeyAbsent) {
+		wpa_printf(MSG_DEBUG, "NDIS: WEP encryption supported");
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
+			WPA_DRIVER_CAPA_ENC_WEP104;
+	}
+
+	if (ndis_set_auth_mode(drv, Ndis802_11AuthModeShared) == 0 &&
+	    ndis_get_auth_mode(drv) == Ndis802_11AuthModeShared) {
+		drv->capa.auth |= WPA_DRIVER_AUTH_SHARED;
+	}
+
+	if (ndis_set_auth_mode(drv, Ndis802_11AuthModeOpen) == 0 &&
+	    ndis_get_auth_mode(drv) == Ndis802_11AuthModeOpen) {
+		drv->capa.auth |= WPA_DRIVER_AUTH_OPEN;
+	}
+
+	ndis_set_encr_status(drv, Ndis802_11EncryptionDisabled);
+
+	/* Could also verify OID_802_11_ADD_KEY error reporting and
+	 * support for OID_802_11_ASSOCIATION_INFORMATION. */
+
+	if (drv->capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA &&
+	    drv->capa.enc & (WPA_DRIVER_CAPA_ENC_TKIP |
+			     WPA_DRIVER_CAPA_ENC_CCMP)) {
+		wpa_printf(MSG_DEBUG, "NDIS: driver supports WPA");
+		drv->has_capability = 1;
+	} else {
+		wpa_printf(MSG_DEBUG, "NDIS: no WPA support found");
+	}
+
+	wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x "
+		   "enc 0x%x auth 0x%x",
+		   drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth);
+}
+
+
+static void wpa_driver_ndis_get_capability(struct wpa_driver_ndis_data *drv)
+{
+	char buf[512];
+	int len;
+	size_t i;
+	NDIS_802_11_CAPABILITY *c;
+
+	drv->capa.flags = WPA_DRIVER_FLAGS_DRIVER_IE;
+
+	len = ndis_get_oid(drv, OID_802_11_CAPABILITY, buf, sizeof(buf));
+	if (len < 0) {
+		wpa_driver_ndis_get_wpa_capability(drv);
+		return;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "OID_802_11_CAPABILITY", (u8 *) buf, len);
+	c = (NDIS_802_11_CAPABILITY *) buf;
+	if (len < sizeof(*c) || c->Version != 2) {
+		wpa_printf(MSG_DEBUG, "NDIS: unsupported "
+			   "OID_802_11_CAPABILITY data");
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "NDIS: Driver supports OID_802_11_CAPABILITY - "
+		   "NoOfPMKIDs %d NoOfAuthEncrPairs %d",
+		   (int) c->NoOfPMKIDs,
+		   (int) c->NoOfAuthEncryptPairsSupported);
+	drv->has_capability = 1;
+	drv->no_of_pmkid = c->NoOfPMKIDs;
+	for (i = 0; i < c->NoOfAuthEncryptPairsSupported; i++) {
+		NDIS_802_11_AUTHENTICATION_ENCRYPTION *ae;
+		ae = &c->AuthenticationEncryptionSupported[i];
+		if ((char *) (ae + 1) > buf + len) {
+			wpa_printf(MSG_DEBUG, "NDIS: auth/encr pair list "
+				   "overflow");
+			break;
+		}
+		wpa_printf(MSG_MSGDUMP, "NDIS: %d - auth %d encr %d",
+			   i, (int) ae->AuthModeSupported,
+			   (int) ae->EncryptStatusSupported);
+		switch (ae->AuthModeSupported) {
+		case Ndis802_11AuthModeOpen:
+			drv->capa.auth |= WPA_DRIVER_AUTH_OPEN;
+			break;
+		case Ndis802_11AuthModeShared:
+			drv->capa.auth |= WPA_DRIVER_AUTH_SHARED;
+			break;
+		case Ndis802_11AuthModeWPA:
+			drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA;
+			break;
+		case Ndis802_11AuthModeWPAPSK:
+			drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+			break;
+		case Ndis802_11AuthModeWPA2:
+			drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
+			break;
+		case Ndis802_11AuthModeWPA2PSK:
+			drv->capa.key_mgmt |=
+				WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+			break;
+		case Ndis802_11AuthModeWPANone:
+			drv->capa.key_mgmt |=
+				WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE;
+			break;
+		default:
+			break;
+		}
+		switch (ae->EncryptStatusSupported) {
+		case Ndis802_11Encryption1Enabled:
+			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40;
+			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP104;
+			break;
+		case Ndis802_11Encryption2Enabled:
+			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+			break;
+		case Ndis802_11Encryption3Enabled:
+			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+			break;
+		default:
+			break;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x "
+		   "enc 0x%x auth 0x%x",
+		   drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth);
+}
+
+
+static int wpa_driver_ndis_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	if (!drv->has_capability)
+		return -1;
+	os_memcpy(capa, &drv->capa, sizeof(*capa));
+	return 0;
+}
+
+
+static const char * wpa_driver_ndis_get_ifname(void *priv)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	return drv->ifname;
+}
+
+
+static const u8 * wpa_driver_ndis_get_mac_addr(void *priv)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	return drv->own_addr;
+}
+
+
+#ifdef _WIN32_WCE
+
+#define NDISUIO_MSG_SIZE (sizeof(NDISUIO_DEVICE_NOTIFICATION) + 512)
+
+static void ndisuio_notification_receive(void *eloop_data, void *user_ctx)
+{
+	struct wpa_driver_ndis_data *drv = eloop_data;
+	NDISUIO_DEVICE_NOTIFICATION *hdr;
+	u8 buf[NDISUIO_MSG_SIZE];
+	DWORD len, flags;
+
+	if (!ReadMsgQueue(drv->event_queue, buf, NDISUIO_MSG_SIZE, &len, 0,
+			  &flags)) {
+		wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: "
+			   "ReadMsgQueue failed: %d", (int) GetLastError());
+		return;
+	}
+
+	if (len < sizeof(NDISUIO_DEVICE_NOTIFICATION)) {
+		wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: "
+			   "Too short message (len=%d)", (int) len);
+		return;
+	}
+
+	hdr = (NDISUIO_DEVICE_NOTIFICATION *) buf;
+	wpa_printf(MSG_DEBUG, "NDIS: Notification received: len=%d type=0x%x",
+		   (int) len, hdr->dwNotificationType);
+
+	switch (hdr->dwNotificationType) {
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL
+	case NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL:
+		wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_ARRIVAL");
+		wpa_driver_ndis_event_adapter_arrival(drv);
+		break;
+#endif
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL
+	case NDISUIO_NOTIFICATION_ADAPTER_REMOVAL:
+		wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_REMOVAL");
+		wpa_driver_ndis_event_adapter_removal(drv);
+		break;
+#endif
+	case NDISUIO_NOTIFICATION_MEDIA_CONNECT:
+		wpa_printf(MSG_DEBUG, "NDIS: MEDIA_CONNECT");
+		SetEvent(drv->connected_event);
+		wpa_driver_ndis_event_connect(drv);
+		break;
+	case NDISUIO_NOTIFICATION_MEDIA_DISCONNECT:
+		ResetEvent(drv->connected_event);
+		wpa_printf(MSG_DEBUG, "NDIS: MEDIA_DISCONNECT");
+		wpa_driver_ndis_event_disconnect(drv);
+		break;
+	case NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION:
+		wpa_printf(MSG_DEBUG, "NDIS: MEDIA_SPECIFIC_NOTIFICATION");
+#if _WIN32_WCE == 420 || _WIN32_WCE == 0x420
+		wpa_driver_ndis_event_media_specific(
+			drv, hdr->pvStatusBuffer, hdr->uiStatusBufferSize);
+#else
+		wpa_driver_ndis_event_media_specific(
+			drv, ((const u8 *) hdr) + hdr->uiOffsetToStatusBuffer,
+			(size_t) hdr->uiStatusBufferSize);
+#endif
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "NDIS: Unknown notification type 0x%x",
+			   hdr->dwNotificationType);
+		break;
+	}
+}
+
+
+static void ndisuio_notification_deinit(struct wpa_driver_ndis_data *drv)
+{
+	NDISUIO_REQUEST_NOTIFICATION req;
+
+	memset(&req, 0, sizeof(req));
+	req.hMsgQueue = drv->event_queue;
+	req.dwNotificationTypes = 0;
+
+	if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION,
+			     &req, sizeof(req), NULL, 0, NULL, NULL)) {
+		wpa_printf(MSG_INFO, "ndisuio_notification_deinit: "
+			   "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d",
+			   (int) GetLastError());
+	}
+
+	if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_CANCEL_NOTIFICATION,
+			     NULL, 0, NULL, 0, NULL, NULL)) {
+		wpa_printf(MSG_INFO, "ndisuio_notification_deinit: "
+			   "IOCTL_NDISUIO_CANCEL_NOTIFICATION failed: %d",
+			   (int) GetLastError());
+	}
+
+	if (drv->event_queue) {
+		eloop_unregister_event(drv->event_queue,
+				       sizeof(drv->event_queue));
+		CloseHandle(drv->event_queue);
+		drv->event_queue = NULL;
+	}
+
+	if (drv->connected_event) {
+		CloseHandle(drv->connected_event);
+		drv->connected_event = NULL;
+	}
+}
+
+
+static int ndisuio_notification_init(struct wpa_driver_ndis_data *drv)
+{
+	MSGQUEUEOPTIONS opt;
+	NDISUIO_REQUEST_NOTIFICATION req;
+
+	drv->connected_event =
+		CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
+	if (drv->connected_event == NULL) {
+		wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+			   "CreateEvent failed: %d",
+			   (int) GetLastError());
+		return -1;
+	}
+
+	memset(&opt, 0, sizeof(opt));
+	opt.dwSize = sizeof(opt);
+	opt.dwMaxMessages = 5;
+	opt.cbMaxMessage = NDISUIO_MSG_SIZE;
+	opt.bReadAccess = TRUE;
+
+	drv->event_queue = CreateMsgQueue(NULL, &opt);
+	if (drv->event_queue == NULL) {
+		wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+			   "CreateMsgQueue failed: %d",
+			   (int) GetLastError());
+		ndisuio_notification_deinit(drv);
+		return -1;
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.hMsgQueue = drv->event_queue;
+	req.dwNotificationTypes =
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL
+		NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL |
+#endif
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL
+		NDISUIO_NOTIFICATION_ADAPTER_REMOVAL |
+#endif
+		NDISUIO_NOTIFICATION_MEDIA_CONNECT |
+		NDISUIO_NOTIFICATION_MEDIA_DISCONNECT |
+		NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION;
+
+	if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION,
+			     &req, sizeof(req), NULL, 0, NULL, NULL)) {
+		wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+			   "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d",
+			   (int) GetLastError());
+		ndisuio_notification_deinit(drv);
+		return -1;
+	}
+
+	eloop_register_event(drv->event_queue, sizeof(drv->event_queue),
+			     ndisuio_notification_receive, drv, NULL);
+
+	return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+	NDISUIO_QUERY_BINDING *b;
+	size_t blen = sizeof(*b) + 1024;
+	int i, error, found = 0;
+	DWORD written;
+	char name[256], desc[256], *dpos;
+	WCHAR *pos;
+	size_t j, len, dlen;
+
+	b = os_malloc(blen);
+	if (b == NULL)
+		return -1;
+
+	for (i = 0; ; i++) {
+		os_memset(b, 0, blen);
+		b->BindingIndex = i;
+		if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
+				     b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
+				     &written, NULL)) {
+			error = (int) GetLastError();
+			if (error == ERROR_NO_MORE_ITEMS)
+				break;
+			wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
+				   "failed: %d", error);
+			break;
+		}
+
+		pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
+		len = b->DeviceNameLength;
+		if (len >= sizeof(name))
+			len = sizeof(name) - 1;
+		for (j = 0; j < len; j++)
+			name[j] = (char) pos[j];
+		name[len] = '\0';
+
+		pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
+		len = b->DeviceDescrLength;
+		if (len >= sizeof(desc))
+			len = sizeof(desc) - 1;
+		for (j = 0; j < len; j++)
+			desc[j] = (char) pos[j];
+		desc[len] = '\0';
+
+		wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
+
+		if (os_strstr(name, drv->ifname)) {
+			wpa_printf(MSG_DEBUG, "NDIS: Interface name match");
+			found = 1;
+			break;
+		}
+
+		if (os_strncmp(desc, drv->ifname, os_strlen(drv->ifname)) == 0)
+		{
+			wpa_printf(MSG_DEBUG, "NDIS: Interface description "
+				   "match");
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found) {
+		wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
+			   drv->ifname);
+		os_free(b);
+		return -1;
+	}
+
+	os_strlcpy(drv->ifname,
+		   os_strncmp(name, "\\DEVICE\\", 8) == 0 ? name + 8 : name,
+		   sizeof(drv->ifname));
+#ifdef _WIN32_WCE
+	drv->adapter_name = wpa_strdup_tchar(drv->ifname);
+	if (drv->adapter_name == NULL) {
+		wpa_printf(MSG_ERROR, "NDIS: Failed to allocate memory for "
+			   "adapter name");
+		os_free(b);
+		return -1;
+	}
+#endif /* _WIN32_WCE */
+
+	dpos = os_strstr(desc, " - ");
+	if (dpos)
+		dlen = dpos - desc;
+	else
+		dlen = os_strlen(desc);
+	drv->adapter_desc = dup_binstr(desc, dlen);
+	os_free(b);
+	if (drv->adapter_desc == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
+		   drv->adapter_desc);
+
+	return 0;
+#else /* CONFIG_USE_NDISUIO */
+	PTSTR _names;
+	char *names, *pos, *pos2;
+	ULONG len;
+	BOOLEAN res;
+#define MAX_ADAPTERS 32
+	char *name[MAX_ADAPTERS];
+	char *desc[MAX_ADAPTERS];
+	int num_name, num_desc, i, found_name, found_desc;
+	size_t dlen;
+
+	wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
+		   PacketGetVersion());
+
+	len = 8192;
+	_names = os_zalloc(len);
+	if (_names == NULL)
+		return -1;
+
+	res = PacketGetAdapterNames(_names, &len);
+	if (!res && len > 8192) {
+		os_free(_names);
+		_names = os_zalloc(len);
+		if (_names == NULL)
+			return -1;
+		res = PacketGetAdapterNames(_names, &len);
+	}
+
+	if (!res) {
+		wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
+			   "(PacketGetAdapterNames)");
+		os_free(_names);
+		return -1;
+	}
+
+	names = (char *) _names;
+	if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
+		wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
+			   "UNICODE");
+		/* Convert to ASCII */
+		pos2 = pos = names;
+		while (pos2 < names + len) {
+			if (pos2[0] == '\0' && pos2[1] == '\0' &&
+			    pos2[2] == '\0' && pos2[3] == '\0') {
+				pos2 += 4;
+				break;
+			}
+			*pos++ = pos2[0];
+			pos2 += 2;
+		}
+		os_memcpy(pos + 2, names, pos - names);
+		pos += 2;
+	} else
+		pos = names;
+
+	num_name = 0;
+	while (pos < names + len) {
+		name[num_name] = pos;
+		while (*pos && pos < names + len)
+			pos++;
+		if (pos + 1 >= names + len) {
+			os_free(names);
+			return -1;
+		}
+		pos++;
+		num_name++;
+		if (num_name >= MAX_ADAPTERS) {
+			wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
+			os_free(names);
+			return -1;
+		}
+		if (*pos == '\0') {
+			wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
+				   num_name);
+			pos++;
+			break;
+		}
+	}
+
+	num_desc = 0;
+	while (pos < names + len) {
+		desc[num_desc] = pos;
+		while (*pos && pos < names + len)
+			pos++;
+		if (pos + 1 >= names + len) {
+			os_free(names);
+			return -1;
+		}
+		pos++;
+		num_desc++;
+		if (num_desc >= MAX_ADAPTERS) {
+			wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
+				   "descriptions");
+			os_free(names);
+			return -1;
+		}
+		if (*pos == '\0') {
+			wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
+				   "found", num_name);
+			pos++;
+			break;
+		}
+	}
+
+	/*
+	 * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
+	 * descriptions. Fill in dummy descriptors to work around this.
+	 */
+	while (num_desc < num_name)
+		desc[num_desc++] = "dummy description";
+
+	if (num_name != num_desc) {
+		wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
+			   "description counts (%d != %d)",
+			   num_name, num_desc);
+		os_free(names);
+		return -1;
+	}
+
+	found_name = found_desc = -1;
+	for (i = 0; i < num_name; i++) {
+		wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s",
+			   i, name[i], desc[i]);
+		if (found_name == -1 && os_strstr(name[i], drv->ifname))
+			found_name = i;
+		if (found_desc == -1 &&
+		    os_strncmp(desc[i], drv->ifname, os_strlen(drv->ifname)) ==
+		    0)
+			found_desc = i;
+	}
+
+	if (found_name < 0 && found_desc >= 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Matched interface '%s' based on "
+			   "description '%s'",
+			   name[found_desc], desc[found_desc]);
+		found_name = found_desc;
+		os_strlcpy(drv->ifname,
+			   os_strncmp(name[found_desc], "\\Device\\NPF_", 12)
+			   == 0 ? name[found_desc] + 12 : name[found_desc],
+			   sizeof(drv->ifname));
+	}
+
+	if (found_name < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
+			   drv->ifname);
+		os_free(names);
+		return -1;
+	}
+
+	i = found_name;
+	pos = os_strrchr(desc[i], '(');
+	if (pos) {
+		dlen = pos - desc[i];
+		pos--;
+		if (pos > desc[i] && *pos == ' ')
+			dlen--;
+	} else {
+		dlen = os_strlen(desc[i]);
+	}
+	drv->adapter_desc = dup_binstr(desc[i], dlen);
+	os_free(names);
+	if (drv->adapter_desc == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
+		   drv->adapter_desc);
+
+	return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+#if defined(CONFIG_NATIVE_WINDOWS) || defined(__CYGWIN__)
+#ifndef _WIN32_WCE
+/*
+ * These structures are undocumented for WinXP; only WinCE version is
+ * documented. These would be included wzcsapi.h if it were available. Some
+ * changes here have been needed to make the structures match with WinXP SP2.
+ * It is unclear whether these work with any other version.
+ */
+
+typedef struct {
+	LPWSTR wszGuid;
+} INTF_KEY_ENTRY, *PINTF_KEY_ENTRY;
+
+typedef struct {
+	DWORD dwNumIntfs;
+	PINTF_KEY_ENTRY pIntfs;
+} INTFS_KEY_TABLE, *PINTFS_KEY_TABLE;
+
+typedef struct {
+	DWORD dwDataLen;
+	LPBYTE pData;
+} RAW_DATA, *PRAW_DATA;
+
+typedef struct {
+	LPWSTR wszGuid;
+	LPWSTR wszDescr;
+	ULONG ulMediaState;
+	ULONG ulMediaType;
+	ULONG ulPhysicalMediaType;
+	INT nInfraMode;
+	INT nAuthMode;
+	INT nWepStatus;
+#ifndef _WIN32_WCE
+	u8 pad[2]; /* why is this needed? */
+#endif /* _WIN32_WCE */
+	DWORD dwCtlFlags;
+	DWORD dwCapabilities; /* something added for WinXP SP2(?) */
+	RAW_DATA rdSSID;
+	RAW_DATA rdBSSID;
+	RAW_DATA rdBSSIDList;
+	RAW_DATA rdStSSIDList;
+	RAW_DATA rdCtrlData;
+#ifdef UNDER_CE
+	BOOL bInitialized;
+#endif
+	DWORD nWPAMCastCipher;
+	/* add some extra buffer for later additions since this interface is
+	 * far from stable */
+	u8 later_additions[100];
+} INTF_ENTRY, *PINTF_ENTRY;
+
+#define INTF_ALL 0xffffffff
+#define INTF_ALL_FLAGS 0x0000ffff
+#define INTF_CTLFLAGS 0x00000010
+#define INTFCTL_ENABLED 0x8000
+#endif /* _WIN32_WCE */
+
+
+#ifdef _WIN32_WCE
+static int wpa_driver_ndis_rebind_adapter(struct wpa_driver_ndis_data *drv)
+{
+	HANDLE ndis;
+	TCHAR multi[100];
+	int len;
+
+	len = _tcslen(drv->adapter_name);
+	if (len > 80)
+		return -1;
+
+	ndis = CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
+			  0, NULL, OPEN_EXISTING, 0, NULL);
+	if (ndis == INVALID_HANDLE_VALUE) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to open file to NDIS "
+			   "device: %d", (int) GetLastError());
+		return -1;
+	}
+
+	len++;
+	memcpy(multi, drv->adapter_name, len * sizeof(TCHAR));
+	memcpy(&multi[len], TEXT("NDISUIO\0"), 9 * sizeof(TCHAR));
+	len += 9;
+
+	if (!DeviceIoControl(ndis, IOCTL_NDIS_REBIND_ADAPTER,
+			     multi, len * sizeof(TCHAR), NULL, 0, NULL, NULL))
+	{
+		wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDIS_REBIND_ADAPTER "
+			   "failed: 0x%x", (int) GetLastError());
+		wpa_hexdump_ascii(MSG_DEBUG, "NDIS: rebind multi_sz",
+				  (u8 *) multi, len * sizeof(TCHAR));
+		CloseHandle(ndis);
+		return -1;
+	}
+
+	CloseHandle(ndis);
+
+	wpa_printf(MSG_DEBUG, "NDIS: Requested NDIS rebind of NDISUIO "
+		   "protocol");
+
+	return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
+				   int enable)
+{
+#ifdef _WIN32_WCE
+	HKEY hk, hk2;
+	LONG ret;
+	DWORD i, hnd, len;
+	TCHAR keyname[256], devname[256];
+
+#define WZC_DRIVER TEXT("Drivers\\BuiltIn\\ZeroConfig")
+
+	if (enable) {
+		HANDLE h;
+		h = ActivateDeviceEx(WZC_DRIVER, NULL, 0, NULL);
+		if (h == INVALID_HANDLE_VALUE || h == 0) {
+			wpa_printf(MSG_DEBUG, "NDIS: Failed to re-enable WZC "
+				   "- ActivateDeviceEx failed: %d",
+				   (int) GetLastError());
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "NDIS: WZC re-enabled");
+		return wpa_driver_ndis_rebind_adapter(drv);
+	}
+
+	/*
+	 * Unfortunately, just disabling the WZC for an interface is not enough
+	 * to free NDISUIO for us, so need to disable and unload WZC completely
+	 * for now when using WinCE with NDISUIO. In addition, must request
+	 * NDISUIO protocol to be rebound to the adapter in order to free the
+	 * NDISUIO binding that WZC hold before us.
+	 */
+
+	/* Enumerate HKLM\Drivers\Active\* to find a handle to WZC. */
+	ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVLOAD_ACTIVE_KEY, 0, 0, &hk);
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(DEVLOAD_ACTIVE_KEY) "
+			   "failed: %d %d", (int) ret, (int) GetLastError());
+		return -1;
+	}
+
+	for (i = 0; ; i++) {
+		len = sizeof(keyname);
+		ret = RegEnumKeyEx(hk, i, keyname, &len, NULL, NULL, NULL,
+				   NULL);
+		if (ret != ERROR_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "NDIS: Could not find active "
+				   "WZC - assuming it is not running.");
+			RegCloseKey(hk);
+			return -1;
+		}
+
+		ret = RegOpenKeyEx(hk, keyname, 0, 0, &hk2);
+		if (ret != ERROR_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(active dev) "
+				   "failed: %d %d",
+				   (int) ret, (int) GetLastError());
+			continue;
+		}
+
+		len = sizeof(devname);
+		ret = RegQueryValueEx(hk2, DEVLOAD_DEVKEY_VALNAME, NULL, NULL,
+				      (LPBYTE) devname, &len);
+		if (ret != ERROR_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx("
+				   "DEVKEY_VALNAME) failed: %d %d",
+				   (int) ret, (int) GetLastError());
+			RegCloseKey(hk2);
+			continue;
+		}
+
+		if (_tcscmp(devname, WZC_DRIVER) == 0)
+			break;
+
+		RegCloseKey(hk2);
+	}
+
+	RegCloseKey(hk);
+
+	/* Found WZC - get handle to it. */
+	len = sizeof(hnd);
+	ret = RegQueryValueEx(hk2, DEVLOAD_HANDLE_VALNAME, NULL, NULL,
+			      (PUCHAR) &hnd, &len);
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(HANDLE_VALNAME) "
+			   "failed: %d %d", (int) ret, (int) GetLastError());
+		RegCloseKey(hk2);
+		return -1;
+	}
+
+	RegCloseKey(hk2);
+
+	/* Deactivate WZC */
+	if (!DeactivateDevice((HANDLE) hnd)) {
+		wpa_printf(MSG_DEBUG, "NDIS: DeactivateDevice failed: %d",
+			   (int) GetLastError());
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily");
+	drv->wzc_disabled = 1;
+	return wpa_driver_ndis_rebind_adapter(drv);
+
+#else /* _WIN32_WCE */
+
+	HMODULE hm;
+	DWORD (WINAPI *wzc_enum_interf)(LPWSTR pSrvAddr,
+					PINTFS_KEY_TABLE pIntfs);
+	DWORD (WINAPI *wzc_query_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
+					 PINTF_ENTRY pIntf,
+					 LPDWORD pdwOutFlags);
+	DWORD (WINAPI *wzc_set_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
+				       PINTF_ENTRY pIntf, LPDWORD pdwOutFlags);
+	int ret = -1, j;
+	DWORD res;
+	INTFS_KEY_TABLE guids;
+	INTF_ENTRY intf;
+	char guid[128];
+	WCHAR *pos;
+	DWORD flags, i;
+
+	hm = LoadLibrary(TEXT("wzcsapi.dll"));
+	if (hm == NULL) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to load wzcsapi.dll (%u) "
+			   "- WZC probably not running",
+			   (unsigned int) GetLastError());
+		return -1;
+	}
+
+#ifdef _WIN32_WCE
+	wzc_enum_interf = (void *) GetProcAddressA(hm, "WZCEnumInterfaces");
+	wzc_query_interf = (void *) GetProcAddressA(hm, "WZCQueryInterface");
+	wzc_set_interf = (void *) GetProcAddressA(hm, "WZCSetInterface");
+#else /* _WIN32_WCE */
+	wzc_enum_interf = (void *) GetProcAddress(hm, "WZCEnumInterfaces");
+	wzc_query_interf = (void *) GetProcAddress(hm, "WZCQueryInterface");
+	wzc_set_interf = (void *) GetProcAddress(hm, "WZCSetInterface");
+#endif /* _WIN32_WCE */
+
+	if (wzc_enum_interf == NULL || wzc_query_interf == NULL ||
+	    wzc_set_interf == NULL) {
+		wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces, "
+			   "WZCQueryInterface, or WZCSetInterface not found "
+			   "in wzcsapi.dll");
+		goto fail;
+	}
+
+	os_memset(&guids, 0, sizeof(guids));
+	res = wzc_enum_interf(NULL, &guids);
+	if (res != 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces failed: %d; "
+			   "WZC service is apparently not running",
+			   (int) res);
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces: %d interfaces",
+		   (int) guids.dwNumIntfs);
+
+	for (i = 0; i < guids.dwNumIntfs; i++) {
+		pos = guids.pIntfs[i].wszGuid;
+		for (j = 0; j < sizeof(guid); j++) {
+			guid[j] = (char) *pos;
+			if (*pos == 0)
+				break;
+			pos++;
+		}
+		guid[sizeof(guid) - 1] = '\0';
+		wpa_printf(MSG_DEBUG, "NDIS: intfs %d GUID '%s'",
+			   (int) i, guid);
+		if (os_strstr(drv->ifname, guid) == NULL)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "NDIS: Current interface found from "
+			   "WZC");
+		break;
+	}
+
+	if (i >= guids.dwNumIntfs) {
+		wpa_printf(MSG_DEBUG, "NDIS: Current interface not found from "
+			   "WZC");
+		goto fail;
+	}
+
+	os_memset(&intf, 0, sizeof(intf));
+	intf.wszGuid = guids.pIntfs[i].wszGuid;
+	/* Set flags to verify that the structure has not changed. */
+	intf.dwCtlFlags = -1;
+	flags = 0;
+	res = wzc_query_interf(NULL, INTFCTL_ENABLED, &intf, &flags);
+	if (res != 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Could not query flags for the "
+			   "WZC interface: %d (0x%x)",
+			   (int) res, (int) res);
+		wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+			   (unsigned int) GetLastError());
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "NDIS: WZC interface flags 0x%x dwCtlFlags 0x%x",
+		   (int) flags, (int) intf.dwCtlFlags);
+
+	if (intf.dwCtlFlags == -1) {
+		wpa_printf(MSG_DEBUG, "NDIS: Looks like wzcsapi has changed "
+			   "again - could not disable WZC");
+		wpa_hexdump(MSG_MSGDUMP, "NDIS: intf",
+			    (u8 *) &intf, sizeof(intf));
+		goto fail;
+	}
+
+	if (enable) {
+		if (!(intf.dwCtlFlags & INTFCTL_ENABLED)) {
+			wpa_printf(MSG_DEBUG, "NDIS: Enabling WZC for this "
+				   "interface");
+			intf.dwCtlFlags |= INTFCTL_ENABLED;
+			res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
+					     &flags);
+			if (res != 0) {
+				wpa_printf(MSG_DEBUG, "NDIS: Failed to enable "
+					   "WZC: %d (0x%x)",
+					   (int) res, (int) res);
+				wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+					   (unsigned int) GetLastError());
+				goto fail;
+			}
+			wpa_printf(MSG_DEBUG, "NDIS: Re-enabled WZC for this "
+				   "interface");
+			drv->wzc_disabled = 0;
+		}
+	} else {
+		if (intf.dwCtlFlags & INTFCTL_ENABLED) {
+			wpa_printf(MSG_DEBUG, "NDIS: Disabling WZC for this "
+				   "interface");
+			intf.dwCtlFlags &= ~INTFCTL_ENABLED;
+			res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
+					     &flags);
+			if (res != 0) {
+				wpa_printf(MSG_DEBUG, "NDIS: Failed to "
+					   "disable WZC: %d (0x%x)",
+					   (int) res, (int) res);
+				wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+					   (unsigned int) GetLastError());
+				goto fail;
+			}
+			wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily "
+				   "for this interface");
+			drv->wzc_disabled = 1;
+		} else {
+			wpa_printf(MSG_DEBUG, "NDIS: WZC was not enabled for "
+				   "this interface");
+		}
+	}
+
+	ret = 0;
+
+fail:
+	FreeLibrary(hm);
+
+	return ret;
+#endif /* _WIN32_WCE */
+}
+
+#else /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
+
+static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
+				   int enable)
+{
+	return 0;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
+
+
+#ifdef CONFIG_USE_NDISUIO
+/*
+ * l2_packet_ndis.c is sharing the same handle to NDISUIO, so we must be able
+ * to export this handle. This is somewhat ugly, but there is no better
+ * mechanism available to pass data from driver interface to l2_packet wrapper.
+ */
+static HANDLE driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
+
+HANDLE driver_ndis_get_ndisuio_handle(void)
+{
+	return driver_ndis_ndisuio_handle;
+}
+#endif /* CONFIG_USE_NDISUIO */
+
+
+static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+#ifndef _WIN32_WCE
+#define NDISUIO_DEVICE_NAME TEXT("\\\\.\\\\Ndisuio")
+	DWORD written;
+#endif /* _WIN32_WCE */
+	drv->ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
+				  GENERIC_READ | GENERIC_WRITE, 0, NULL,
+				  OPEN_EXISTING,
+				  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+				  INVALID_HANDLE_VALUE);
+	if (drv->ndisuio == INVALID_HANDLE_VALUE) {
+		wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
+			   "NDISUIO: %d", (int) GetLastError());
+		return -1;
+	}
+	driver_ndis_ndisuio_handle = drv->ndisuio;
+
+#ifndef _WIN32_WCE
+	if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
+			     NULL, 0, &written, NULL)) {
+		wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
+			   "%d", (int) GetLastError());
+		CloseHandle(drv->ndisuio);
+		drv->ndisuio = INVALID_HANDLE_VALUE;
+		return -1;
+	}
+#endif /* _WIN32_WCE */
+
+	return 0;
+#else /* CONFIG_USE_NDISUIO */
+	return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+	DWORD written;
+#define MAX_NDIS_DEVICE_NAME_LEN 256
+	WCHAR ifname[MAX_NDIS_DEVICE_NAME_LEN];
+	size_t len, i, pos;
+	const char *prefix = "\\DEVICE\\";
+
+#ifdef _WIN32_WCE
+	pos = 0;
+#else /* _WIN32_WCE */
+	pos = 8;
+#endif /* _WIN32_WCE */
+	len = pos + os_strlen(drv->ifname);
+	if (len >= MAX_NDIS_DEVICE_NAME_LEN)
+		return -1;
+	for (i = 0; i < pos; i++)
+		ifname[i] = (WCHAR) prefix[i];
+	for (i = pos; i < len; i++)
+		ifname[i] = (WCHAR) drv->ifname[i - pos];
+	ifname[i] = L'\0';
+
+	if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_OPEN_DEVICE,
+			     ifname, len * sizeof(WCHAR), NULL, 0, &written,
+			     NULL)) {
+		wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_OPEN_DEVICE "
+			   "failed: %d", (int) GetLastError());
+		wpa_hexdump_ascii(MSG_DEBUG, "NDIS: ifname",
+				  (const u8 *) ifname, len * sizeof(WCHAR));
+		CloseHandle(drv->ndisuio);
+		drv->ndisuio = INVALID_HANDLE_VALUE;
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "NDIS: Opened NDISUIO device successfully");
+
+	return 0;
+#else /* CONFIG_USE_NDISUIO */
+	char ifname[128];
+	os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", drv->ifname);
+	drv->adapter = PacketOpenAdapter(ifname);
+	if (drv->adapter == NULL) {
+		wpa_printf(MSG_DEBUG, "NDIS: PacketOpenAdapter failed for "
+			   "'%s'", ifname);
+		return -1;
+	}
+	return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+	driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
+	if (drv->ndisuio != INVALID_HANDLE_VALUE)
+		CloseHandle(drv->ndisuio);
+#else /* CONFIG_USE_NDISUIO */
+	if (drv->adapter)
+		PacketCloseAdapter(drv->adapter);
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_add_multicast(struct wpa_driver_ndis_data *drv)
+{
+	if (ndis_set_oid(drv, OID_802_3_MULTICAST_LIST,
+			 (const char *) pae_group_addr, ETH_ALEN) < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to add PAE group address "
+			   "to the multicast list");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void * wpa_driver_ndis_init(void *ctx, const char *ifname)
+{
+	struct wpa_driver_ndis_data *drv;
+	u32 mode;
+
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL)
+		return NULL;
+	drv->ctx = ctx;
+	/*
+	 * Compatibility code to strip possible prefix from the GUID. Previous
+	 * versions include \Device\NPF_ prefix for all names, but the internal
+	 * interface name is now only the GUI. Both Packet32 and NDISUIO
+	 * prefixes are supported.
+	 */
+	if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0)
+		ifname += 12;
+	else if (os_strncmp(ifname, "\\DEVICE\\", 8) == 0)
+		ifname += 8;
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+	if (wpa_driver_ndis_adapter_init(drv) < 0) {
+		os_free(drv);
+		return NULL;
+	}
+
+	if (wpa_driver_ndis_get_names(drv) < 0) {
+		wpa_driver_ndis_adapter_close(drv);
+		os_free(drv);
+		return NULL;
+	}
+
+	wpa_driver_ndis_set_wzc(drv, 0);
+
+	if (wpa_driver_ndis_adapter_open(drv) < 0) {
+		wpa_driver_ndis_adapter_close(drv);
+		os_free(drv);
+		return NULL;
+	}
+
+	if (ndis_get_oid(drv, OID_802_3_CURRENT_ADDRESS,
+			 (char *) drv->own_addr, ETH_ALEN) < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Get OID_802_3_CURRENT_ADDRESS "
+			   "failed");
+		wpa_driver_ndis_adapter_close(drv);
+		os_free(drv);
+		return NULL;
+	}
+	wpa_driver_ndis_get_capability(drv);
+
+	/* Make sure that the driver does not have any obsolete PMKID entries.
+	 */
+	wpa_driver_ndis_flush_pmkid(drv);
+
+	/*
+	 * Disconnect to make sure that driver re-associates if it was
+	 * connected.
+	 */
+	wpa_driver_ndis_disconnect(drv);
+
+	eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL);
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+	drv->events = ndis_events_init(&drv->events_pipe, &drv->event_avail,
+				       drv->ifname, drv->adapter_desc);
+	if (drv->events == NULL) {
+		wpa_driver_ndis_deinit(drv);
+		return NULL;
+	}
+	eloop_register_event(drv->event_avail, sizeof(drv->event_avail),
+			     wpa_driver_ndis_event_pipe_cb, drv, NULL);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+#ifdef _WIN32_WCE
+	if (ndisuio_notification_init(drv) < 0) {
+		wpa_driver_ndis_deinit(drv);
+		return NULL;
+	}
+#endif /* _WIN32_WCE */
+
+	/* Set mode here in case card was configured for ad-hoc mode
+	 * previously. */
+	mode = Ndis802_11Infrastructure;
+	if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
+			 (char *) &mode, sizeof(mode)) < 0) {
+		char buf[8];
+		int res;
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+			   "OID_802_11_INFRASTRUCTURE_MODE (%d)",
+			   (int) mode);
+		/* Try to continue anyway */
+
+		res = ndis_get_oid(drv, OID_DOT11_CURRENT_OPERATION_MODE, buf,
+				   sizeof(buf));
+		if (res > 0) {
+			wpa_printf(MSG_INFO, "NDIS: The driver seems to use "
+				   "Native 802.11 OIDs. These are not yet "
+				   "fully supported.");
+			drv->native80211 = 1;
+		} else if (!drv->has_capability || drv->capa.enc == 0) {
+			/*
+			 * Note: This will also happen with NDIS 6 drivers with
+			 * Vista.
+			 */
+			wpa_printf(MSG_DEBUG, "NDIS: Driver did not provide "
+				   "any wireless capabilities - assume it is "
+				   "a wired interface");
+			drv->wired = 1;
+			drv->capa.flags |= WPA_DRIVER_FLAGS_WIRED;
+			drv->has_capability = 1;
+			ndis_add_multicast(drv);
+		}
+	}
+
+	return drv;
+}
+
+
+static void wpa_driver_ndis_deinit(void *priv)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+	if (drv->events) {
+		eloop_unregister_event(drv->event_avail,
+				       sizeof(drv->event_avail));
+		ndis_events_deinit(drv->events);
+	}
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+#ifdef _WIN32_WCE
+	ndisuio_notification_deinit(drv);
+#endif /* _WIN32_WCE */
+
+	eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+	eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+	wpa_driver_ndis_flush_pmkid(drv);
+	wpa_driver_ndis_disconnect(drv);
+	if (wpa_driver_ndis_radio_off(drv) < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: failed to disassociate and turn "
+			   "radio off");
+	}
+
+	wpa_driver_ndis_adapter_close(drv);
+
+	if (drv->wzc_disabled)
+		wpa_driver_ndis_set_wzc(drv, 1);
+
+#ifdef _WIN32_WCE
+	os_free(drv->adapter_name);
+#endif /* _WIN32_WCE */
+	os_free(drv->adapter_desc);
+	os_free(drv);
+}
+
+
+static struct wpa_interface_info *
+wpa_driver_ndis_get_interfaces(void *global_priv)
+{
+	struct wpa_interface_info *iface = NULL, *niface;
+
+#ifdef CONFIG_USE_NDISUIO
+	NDISUIO_QUERY_BINDING *b;
+	size_t blen = sizeof(*b) + 1024;
+	int i, error;
+	DWORD written;
+	char name[256], desc[256];
+	WCHAR *pos;
+	size_t j, len;
+	HANDLE ndisuio;
+
+	ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
+			     GENERIC_READ | GENERIC_WRITE, 0, NULL,
+			     OPEN_EXISTING,
+			     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+			     INVALID_HANDLE_VALUE);
+	if (ndisuio == INVALID_HANDLE_VALUE) {
+		wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
+			   "NDISUIO: %d", (int) GetLastError());
+		return NULL;
+	}
+
+#ifndef _WIN32_WCE
+	if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
+			     NULL, 0, &written, NULL)) {
+		wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
+			   "%d", (int) GetLastError());
+		CloseHandle(ndisuio);
+		return NULL;
+	}
+#endif /* _WIN32_WCE */
+
+	b = os_malloc(blen);
+	if (b == NULL) {
+		CloseHandle(ndisuio);
+		return NULL;
+	}
+
+	for (i = 0; ; i++) {
+		os_memset(b, 0, blen);
+		b->BindingIndex = i;
+		if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
+				     b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
+				     &written, NULL)) {
+			error = (int) GetLastError();
+			if (error == ERROR_NO_MORE_ITEMS)
+				break;
+			wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
+				   "failed: %d", error);
+			break;
+		}
+
+		pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
+		len = b->DeviceNameLength;
+		if (len >= sizeof(name))
+			len = sizeof(name) - 1;
+		for (j = 0; j < len; j++)
+			name[j] = (char) pos[j];
+		name[len] = '\0';
+
+		pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
+		len = b->DeviceDescrLength;
+		if (len >= sizeof(desc))
+			len = sizeof(desc) - 1;
+		for (j = 0; j < len; j++)
+			desc[j] = (char) pos[j];
+		desc[len] = '\0';
+
+		wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
+
+		niface = os_zalloc(sizeof(*niface));
+		if (niface == NULL)
+			break;
+		niface->drv_name = "ndis";
+		if (os_strncmp(name, "\\DEVICE\\", 8) == 0)
+			niface->ifname = os_strdup(name + 8);
+		else
+			niface->ifname = os_strdup(name);
+		if (niface->ifname == NULL) {
+			os_free(niface);
+			break;
+		}
+		niface->desc = os_strdup(desc);
+		niface->next = iface;
+		iface = niface;
+	}
+
+	os_free(b);
+	CloseHandle(ndisuio);
+#else /* CONFIG_USE_NDISUIO */
+	PTSTR _names;
+	char *names, *pos, *pos2;
+	ULONG len;
+	BOOLEAN res;
+	char *name[MAX_ADAPTERS];
+	char *desc[MAX_ADAPTERS];
+	int num_name, num_desc, i;
+
+	wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
+		   PacketGetVersion());
+
+	len = 8192;
+	_names = os_zalloc(len);
+	if (_names == NULL)
+		return NULL;
+
+	res = PacketGetAdapterNames(_names, &len);
+	if (!res && len > 8192) {
+		os_free(_names);
+		_names = os_zalloc(len);
+		if (_names == NULL)
+			return NULL;
+		res = PacketGetAdapterNames(_names, &len);
+	}
+
+	if (!res) {
+		wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
+			   "(PacketGetAdapterNames)");
+		os_free(_names);
+		return NULL;
+	}
+
+	names = (char *) _names;
+	if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
+		wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
+			   "UNICODE");
+		/* Convert to ASCII */
+		pos2 = pos = names;
+		while (pos2 < names + len) {
+			if (pos2[0] == '\0' && pos2[1] == '\0' &&
+			    pos2[2] == '\0' && pos2[3] == '\0') {
+				pos2 += 4;
+				break;
+			}
+			*pos++ = pos2[0];
+			pos2 += 2;
+		}
+		os_memcpy(pos + 2, names, pos - names);
+		pos += 2;
+	} else
+		pos = names;
+
+	num_name = 0;
+	while (pos < names + len) {
+		name[num_name] = pos;
+		while (*pos && pos < names + len)
+			pos++;
+		if (pos + 1 >= names + len) {
+			os_free(names);
+			return NULL;
+		}
+		pos++;
+		num_name++;
+		if (num_name >= MAX_ADAPTERS) {
+			wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
+			os_free(names);
+			return NULL;
+		}
+		if (*pos == '\0') {
+			wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
+				   num_name);
+			pos++;
+			break;
+		}
+	}
+
+	num_desc = 0;
+	while (pos < names + len) {
+		desc[num_desc] = pos;
+		while (*pos && pos < names + len)
+			pos++;
+		if (pos + 1 >= names + len) {
+			os_free(names);
+			return NULL;
+		}
+		pos++;
+		num_desc++;
+		if (num_desc >= MAX_ADAPTERS) {
+			wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
+				   "descriptions");
+			os_free(names);
+			return NULL;
+		}
+		if (*pos == '\0') {
+			wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
+				   "found", num_name);
+			pos++;
+			break;
+		}
+	}
+
+	/*
+	 * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
+	 * descriptions. Fill in dummy descriptors to work around this.
+	 */
+	while (num_desc < num_name)
+		desc[num_desc++] = "dummy description";
+
+	if (num_name != num_desc) {
+		wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
+			   "description counts (%d != %d)",
+			   num_name, num_desc);
+		os_free(names);
+		return NULL;
+	}
+
+	for (i = 0; i < num_name; i++) {
+		niface = os_zalloc(sizeof(*niface));
+		if (niface == NULL)
+			break;
+		niface->drv_name = "ndis";
+		if (os_strncmp(name[i], "\\Device\\NPF_", 12) == 0)
+			niface->ifname = os_strdup(name[i] + 12);
+		else
+			niface->ifname = os_strdup(name[i]);
+		if (niface->ifname == NULL) {
+			os_free(niface);
+			break;
+		}
+		niface->desc = os_strdup(desc[i]);
+		niface->next = iface;
+		iface = niface;
+	}
+
+#endif /* CONFIG_USE_NDISUIO */
+
+	return iface;
+}
+
+
+static const char *ndis_drv_name = "ndis";
+static const char *ndis_drv_desc = "Windows NDIS driver";
+
+struct wpa_driver_ops wpa_driver_ndis_ops;
+
+void driver_ndis_init_ops(void)
+{
+	os_memset(&wpa_driver_ndis_ops, 0, sizeof(wpa_driver_ndis_ops));
+	wpa_driver_ndis_ops.name = ndis_drv_name;
+	wpa_driver_ndis_ops.desc = ndis_drv_desc;
+	wpa_driver_ndis_ops.get_bssid = wpa_driver_ndis_get_bssid;
+	wpa_driver_ndis_ops.get_ssid = wpa_driver_ndis_get_ssid;
+	wpa_driver_ndis_ops.set_key = wpa_driver_ndis_set_key;
+	wpa_driver_ndis_ops.init = wpa_driver_ndis_init;
+	wpa_driver_ndis_ops.deinit = wpa_driver_ndis_deinit;
+	wpa_driver_ndis_ops.deauthenticate = wpa_driver_ndis_deauthenticate;
+	wpa_driver_ndis_ops.associate = wpa_driver_ndis_associate;
+	wpa_driver_ndis_ops.add_pmkid = wpa_driver_ndis_add_pmkid;
+	wpa_driver_ndis_ops.remove_pmkid = wpa_driver_ndis_remove_pmkid;
+	wpa_driver_ndis_ops.flush_pmkid = wpa_driver_ndis_flush_pmkid;
+	wpa_driver_ndis_ops.get_capa = wpa_driver_ndis_get_capa;
+	wpa_driver_ndis_ops.poll = wpa_driver_ndis_poll;
+	wpa_driver_ndis_ops.get_ifname = wpa_driver_ndis_get_ifname;
+	wpa_driver_ndis_ops.get_mac_addr = wpa_driver_ndis_get_mac_addr;
+	wpa_driver_ndis_ops.get_scan_results2 =
+		wpa_driver_ndis_get_scan_results;
+	wpa_driver_ndis_ops.get_interfaces = wpa_driver_ndis_get_interfaces;
+	wpa_driver_ndis_ops.scan2 = wpa_driver_ndis_scan;
+}
diff --git a/hostap/src/drivers/driver_ndis.h b/hostap/src/drivers/driver_ndis.h
new file mode 100644
index 0000000..89d136d
--- /dev/null
+++ b/hostap/src/drivers/driver_ndis.h
@@ -0,0 +1,59 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_NDIS_H
+#define DRIVER_NDIS_H
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+struct ndis_events_data;
+struct ndis_events_data * ndis_events_init(HANDLE *read_pipe, HANDLE *event,
+					   const char *ifname,
+					   const char *desc);
+void ndis_events_deinit(struct ndis_events_data *events);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+struct ndis_pmkid_entry {
+	struct ndis_pmkid_entry *next;
+	u8 bssid[ETH_ALEN];
+	u8 pmkid[16];
+};
+
+struct wpa_driver_ndis_data {
+	void *ctx;
+	char ifname[100]; /* GUID: {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D} */
+#ifdef _WIN32_WCE
+	TCHAR *adapter_name;
+	HANDLE event_queue; /* NDISUIO notifier MsgQueue */
+	HANDLE connected_event; /* WpaSupplicantConnected event */
+#endif /* _WIN32_WCE */
+	u8 own_addr[ETH_ALEN];
+#ifdef CONFIG_USE_NDISUIO
+	HANDLE ndisuio;
+#else /* CONFIG_USE_NDISUIO */
+	LPADAPTER adapter;
+#endif /* CONFIG_USE_NDISUIO */
+	u8 bssid[ETH_ALEN];
+
+	int has_capability;
+	int no_of_pmkid;
+	int radio_enabled;
+	struct wpa_driver_capa capa;
+	struct ndis_pmkid_entry *pmkid;
+	char *adapter_desc;
+	int wired;
+	int native80211;
+	int mode;
+	int wzc_disabled;
+	int oid_bssid_set;
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+	HANDLE events_pipe, event_avail;
+	struct ndis_events_data *events;
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+};
+
+#endif /* DRIVER_NDIS_H */
diff --git a/hostap/src/drivers/driver_ndis_.c b/hostap/src/drivers/driver_ndis_.c
new file mode 100644
index 0000000..4d23001
--- /dev/null
+++ b/hostap/src/drivers/driver_ndis_.c
@@ -0,0 +1,99 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface - event processing
+ * Copyright (c) 2004-2005, 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 "driver.h"
+#include "eloop.h"
+
+/* Keep this event processing in a separate file and without WinPcap headers to
+ * avoid conflicts with some of the header files. */
+struct _ADAPTER;
+typedef struct _ADAPTER * LPADAPTER;
+#include "driver_ndis.h"
+
+
+void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv,
+					  const u8 *data, size_t data_len);
+void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv);
+
+
+enum event_types { EVENT_CONNECT, EVENT_DISCONNECT,
+		   EVENT_MEDIA_SPECIFIC, EVENT_ADAPTER_ARRIVAL,
+		   EVENT_ADAPTER_REMOVAL };
+
+/* Event data:
+ * enum event_types (as int, i.e., 4 octets)
+ * data length (2 octets (big endian), optional)
+ * data (variable len, optional)
+ */
+
+
+static void wpa_driver_ndis_event_process(struct wpa_driver_ndis_data *drv,
+					  u8 *buf, size_t len)
+{
+	u8 *pos, *data = NULL;
+	enum event_types type;
+	size_t data_len = 0;
+
+	wpa_hexdump(MSG_MSGDUMP, "NDIS: received event data", buf, len);
+	if (len < sizeof(int))
+		return;
+	type = *((int *) buf);
+	pos = buf + sizeof(int);
+	wpa_printf(MSG_DEBUG, "NDIS: event - type %d", type);
+
+	if (buf + len - pos > 2) {
+		data_len = (int) *pos++ << 8;
+		data_len += *pos++;
+		if (data_len > (size_t) (buf + len - pos)) {
+			wpa_printf(MSG_DEBUG, "NDIS: event data overflow");
+			return;
+		}
+		data = pos;
+		wpa_hexdump(MSG_MSGDUMP, "NDIS: event data", data, data_len);
+	}
+
+	switch (type) {
+	case EVENT_CONNECT:
+		wpa_driver_ndis_event_connect(drv);
+		break;
+	case EVENT_DISCONNECT:
+		wpa_driver_ndis_event_disconnect(drv);
+		break;
+	case EVENT_MEDIA_SPECIFIC:
+		wpa_driver_ndis_event_media_specific(drv, data, data_len);
+		break;
+	case EVENT_ADAPTER_ARRIVAL:
+		wpa_driver_ndis_event_adapter_arrival(drv);
+		break;
+	case EVENT_ADAPTER_REMOVAL:
+		wpa_driver_ndis_event_adapter_removal(drv);
+		break;
+	}
+}
+
+
+void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data)
+{
+	struct wpa_driver_ndis_data *drv = eloop_data;
+	u8 buf[512];
+	DWORD len;
+
+	ResetEvent(drv->event_avail);
+	if (ReadFile(drv->events_pipe, buf, sizeof(buf), &len, NULL))
+		wpa_driver_ndis_event_process(drv, buf, len);
+	else {
+		wpa_printf(MSG_DEBUG, "%s: ReadFile() failed: %d", __func__,
+			   (int) GetLastError());
+	}
+}
diff --git a/hostap/src/drivers/driver_nl80211.c b/hostap/src/drivers/driver_nl80211.c
new file mode 100644
index 0000000..00b173f
--- /dev/null
+++ b/hostap/src/drivers/driver_nl80211.c
@@ -0,0 +1,8813 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211
+ * Copyright (c) 2002-2015, 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>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/types.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#ifdef CONFIG_LIBNL3_ROUTE
+#include <netlink/route/neighbour.h>
+#endif /* CONFIG_LIBNL3_ROUTE */
+#include <linux/rtnetlink.h>
+#include <netpacket/packet.h>
+#include <linux/errqueue.h>
+
+#include "common.h"
+#include "eloop.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"
+#include "netlink.h"
+#include "linux_defines.h"
+#include "linux_ioctl.h"
+#include "radiotap.h"
+#include "radiotap_iter.h"
+#include "rfkill.h"
+#include "driver_nl80211.h"
+
+
+#ifndef CONFIG_LIBNL20
+/*
+ * libnl 1.1 has a bug, it tries to allocate socket numbers densely
+ * but when you free a socket again it will mess up its bitmap and
+ * and use the wrong number the next time it needs a socket ID.
+ * Therefore, we wrap the handle alloc/destroy and add our own pid
+ * accounting.
+ */
+static uint32_t port_bitmap[32] = { 0 };
+
+static struct nl_handle *nl80211_handle_alloc(void *cb)
+{
+	struct nl_handle *handle;
+	uint32_t pid = getpid() & 0x3FFFFF;
+	int i;
+
+	handle = nl_handle_alloc_cb(cb);
+
+	for (i = 0; i < 1024; i++) {
+		if (port_bitmap[i / 32] & (1 << (i % 32)))
+			continue;
+		port_bitmap[i / 32] |= 1 << (i % 32);
+		pid += i << 22;
+		break;
+	}
+
+	nl_socket_set_local_port(handle, pid);
+
+	return handle;
+}
+
+static void nl80211_handle_destroy(struct nl_handle *handle)
+{
+	uint32_t port = nl_socket_get_local_port(handle);
+
+	port >>= 22;
+	port_bitmap[port / 32] &= ~(1 << (port % 32));
+
+	nl_handle_destroy(handle);
+}
+#endif /* CONFIG_LIBNL20 */
+
+
+#ifdef ANDROID
+/* system/core/libnl_2 does not include nl_socket_set_nonblocking() */
+#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;
+
+	handle = nl80211_handle_alloc(cb);
+	if (handle == NULL) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+			   "callbacks (%s)", dbg);
+		return NULL;
+	}
+
+	if (genl_connect(handle)) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
+			   "netlink (%s)", dbg);
+		nl80211_handle_destroy(handle);
+		return NULL;
+	}
+
+	return handle;
+}
+
+
+static void nl_destroy_handles(struct nl_handle **handle)
+{
+	if (*handle == NULL)
+		return;
+	nl80211_handle_destroy(*handle);
+	*handle = NULL;
+}
+
+
+#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)
+{
+#ifdef CONFIG_LIBNL20
+	/*
+	 * libnl uses a pretty small buffer (32 kB that gets converted to 64 kB)
+	 * by default. It is possible to hit that limit in some cases where
+	 * operations are blocked, e.g., with a burst of Deauthentication frames
+	 * to hostapd and STA entry deletion. Try to increase the buffer to make
+	 * this less likely to occur.
+	 */
+	if (nl_socket_set_buffer_size(*handle, 262144, 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Could not set nl_socket RX buffer size: %s",
+			   strerror(errno));
+		/* continue anyway with the default (smaller) buffer */
+	}
+#endif /* CONFIG_LIBNL20 */
+
+	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);
+}
+
+
+static void nl80211_global_deinit(void *priv);
+static void nl80211_check_global(struct nl80211_global *global);
+
+static void wpa_driver_nl80211_deinit(struct i802_bss *bss);
+static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
+					    struct hostapd_freq_params *freq);
+
+static int
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
+				   const u8 *set_addr, int first,
+				   const char *driver_params);
+static int nl80211_send_frame_cmd(struct i802_bss *bss,
+				  unsigned int freq, unsigned int wait,
+				  const u8 *buf, size_t buf_len, u64 *cookie,
+				  int no_cck, int no_ack, int offchanok);
+static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
+					       int report);
+
+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 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);
+
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
+			      int reset_mode);
+
+static int i802_set_iface_flags(struct i802_bss *bss, int up);
+static int nl80211_set_param(void *priv, const char *param);
+
+
+/* Converts nl80211_chan_width to a common format */
+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;
+}
+
+
+int is_ap_interface(enum nl80211_iftype nlmode)
+{
+	return nlmode == NL80211_IFTYPE_AP ||
+		nlmode == NL80211_IFTYPE_P2P_GO;
+}
+
+
+int is_sta_interface(enum nl80211_iftype nlmode)
+{
+	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;
+}
+
+
+struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
+				  int ifindex)
+{
+	struct i802_bss *bss;
+
+	for (bss = drv->first_bss; bss; bss = bss->next) {
+		if (bss->ifindex == ifindex)
+			return bss;
+	}
+
+	return NULL;
+}
+
+
+static int is_mesh_interface(enum nl80211_iftype nlmode)
+{
+	return nlmode == NL80211_IFTYPE_MESH_POINT;
+}
+
+
+void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv)
+{
+	if (drv->associated)
+		os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
+	drv->associated = 0;
+	os_memset(drv->bssid, 0, ETH_ALEN);
+}
+
+
+/* nl80211 code */
+static int ack_handler(struct nl_msg *msg, void *arg)
+{
+	int *err = arg;
+	*err = 0;
+	return NL_STOP;
+}
+
+static int finish_handler(struct nl_msg *msg, void *arg)
+{
+	int *ret = arg;
+	*ret = 0;
+	return NL_SKIP;
+}
+
+static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
+			 void *arg)
+{
+	int *ret = arg;
+	*ret = err->error;
+	return NL_SKIP;
+}
+
+
+static int no_seq_check(struct nl_msg *msg, void *arg)
+{
+	return NL_OK;
+}
+
+
+static void nl80211_nlmsg_clear(struct nl_msg *msg)
+{
+	/*
+	 * Clear nlmsg data, e.g., to make sure key material is not left in
+	 * heap memory for unnecessarily long time.
+	 */
+	if (msg) {
+		struct nlmsghdr *hdr = nlmsg_hdr(msg);
+		void *data = nlmsg_data(hdr);
+		/*
+		 * This would use nlmsg_datalen() or the older nlmsg_len() if
+		 * only libnl were to maintain a stable API.. Neither will work
+		 * with all released versions, so just calculate the length
+		 * here.
+		 */
+		int len = hdr->nlmsg_len - NLMSG_HDRLEN;
+
+		os_memset(data, 0, len);
+	}
+}
+
+
+static int send_and_recv(struct nl80211_global *global,
+			 struct nl_handle *nl_handle, struct nl_msg *msg,
+			 int (*valid_handler)(struct nl_msg *, void *),
+			 void *valid_data)
+{
+	struct nl_cb *cb;
+	int err = -ENOMEM;
+
+	if (!msg)
+		return -ENOMEM;
+
+	cb = nl_cb_clone(global->nl_cb);
+	if (!cb)
+		goto out;
+
+	err = nl_send_auto_complete(nl_handle, msg);
+	if (err < 0)
+		goto out;
+
+	err = 1;
+
+	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
+	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
+	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
+
+	if (valid_handler)
+		nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
+			  valid_handler, valid_data);
+
+	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);
+	if (!valid_handler && valid_data == (void *) -1)
+		nl80211_nlmsg_clear(msg);
+	nlmsg_free(msg);
+	return err;
+}
+
+
+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)
+{
+	return send_and_recv(drv->global, drv->global->nl, msg,
+			     valid_handler, valid_data);
+}
+
+
+struct family_data {
+	const char *group;
+	int id;
+};
+
+
+static int family_handler(struct nl_msg *msg, void *arg)
+{
+	struct family_data *res = arg;
+	struct nlattr *tb[CTRL_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *mcgrp;
+	int i;
+
+	nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb[CTRL_ATTR_MCAST_GROUPS])
+		return NL_SKIP;
+
+	nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) {
+		struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1];
+		nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp),
+			  nla_len(mcgrp), NULL);
+		if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] ||
+		    !tb2[CTRL_ATTR_MCAST_GRP_ID] ||
+		    os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]),
+			       res->group,
+			       nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
+			continue;
+		res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]);
+		break;
+	};
+
+	return NL_SKIP;
+}
+
+
+static int nl_get_multicast_id(struct nl80211_global *global,
+			       const char *family, const char *group)
+{
+	struct nl_msg *msg;
+	int ret;
+	struct family_data res = { group, -ENOENT };
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+	if (!genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"),
+			 0, 0, CTRL_CMD_GETFAMILY, 0) ||
+	    nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	ret = send_and_recv(global, global->nl, msg, family_handler, &res);
+	if (ret == 0)
+		ret = res.id;
+	return ret;
+}
+
+
+void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
+		   struct nl_msg *msg, int flags, uint8_t cmd)
+{
+	return genlmsg_put(msg, 0, 0, drv->global->nl80211_id,
+			   0, flags, cmd, 0);
+}
+
+
+static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss)
+{
+	if (bss->wdev_id_set)
+		return nla_put_u64(msg, NL80211_ATTR_WDEV, bss->wdev_id);
+	return nla_put_u32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+}
+
+
+struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd)
+{
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return NULL;
+
+	if (!nl80211_cmd(bss->drv, msg, flags, cmd) ||
+	    nl80211_set_iface_id(msg, bss) < 0) {
+		nlmsg_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+static struct nl_msg *
+nl80211_ifindex_msg(struct wpa_driver_nl80211_data *drv, int ifindex,
+		    int flags, uint8_t cmd)
+{
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return NULL;
+
+	if (!nl80211_cmd(drv, msg, flags, cmd) ||
+	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex)) {
+		nlmsg_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags,
+				uint8_t cmd)
+{
+	return nl80211_ifindex_msg(drv, drv->ifindex, flags, cmd);
+}
+
+
+struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd)
+{
+	return nl80211_ifindex_msg(bss->drv, bss->ifindex, flags, cmd);
+}
+
+
+struct wiphy_idx_data {
+	int wiphy_idx;
+	enum nl80211_iftype nlmode;
+	u8 *macaddr;
+};
+
+
+static int netdev_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wiphy_idx_data *info = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL80211_ATTR_WIPHY])
+		info->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
+	if (tb[NL80211_ATTR_IFTYPE])
+		info->nlmode = nla_get_u32(tb[NL80211_ATTR_IFTYPE]);
+
+	if (tb[NL80211_ATTR_MAC] && info->macaddr)
+		os_memcpy(info->macaddr, nla_data(tb[NL80211_ATTR_MAC]),
+			  ETH_ALEN);
+
+	return NL_SKIP;
+}
+
+
+int nl80211_get_wiphy_index(struct i802_bss *bss)
+{
+	struct nl_msg *msg;
+	struct wiphy_idx_data data = {
+		.wiphy_idx = -1,
+		.macaddr = NULL,
+	};
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+		return -1;
+
+	if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0)
+		return data.wiphy_idx;
+	return -1;
+}
+
+
+static enum nl80211_iftype nl80211_get_ifmode(struct i802_bss *bss)
+{
+	struct nl_msg *msg;
+	struct wiphy_idx_data data = {
+		.nlmode = NL80211_IFTYPE_UNSPECIFIED,
+		.macaddr = NULL,
+	};
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+		return NL80211_IFTYPE_UNSPECIFIED;
+
+	if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0)
+		return data.nlmode;
+	return NL80211_IFTYPE_UNSPECIFIED;
+}
+
+
+static int nl80211_get_macaddr(struct i802_bss *bss)
+{
+	struct nl_msg *msg;
+	struct wiphy_idx_data data = {
+		.macaddr = bss->addr,
+	};
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+		return -1;
+
+	return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data);
+}
+
+
+static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv,
+				    struct nl80211_wiphy_data *w)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -1;
+
+	if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS) ||
+	    nla_put_u32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Register beacons command "
+			   "failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	}
+	return ret;
+}
+
+
+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");
+
+	res = nl_recvmsgs(handle, w->nl_cb);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d",
+			   __func__, res);
+	}
+}
+
+
+static int process_beacon_event(struct nl_msg *msg, void *arg)
+{
+	struct nl80211_wiphy_data *w = arg;
+	struct wpa_driver_nl80211_data *drv;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	union wpa_event_data event;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (gnlh->cmd != NL80211_CMD_FRAME) {
+		wpa_printf(MSG_DEBUG, "nl80211: Unexpected beacon event? (%d)",
+			   gnlh->cmd);
+		return NL_SKIP;
+	}
+
+	if (!tb[NL80211_ATTR_FRAME])
+		return NL_SKIP;
+
+	dl_list_for_each(drv, &w->drvs, struct wpa_driver_nl80211_data,
+			 wiphy_list) {
+		os_memset(&event, 0, sizeof(event));
+		event.rx_mgmt.frame = nla_data(tb[NL80211_ATTR_FRAME]);
+		event.rx_mgmt.frame_len = nla_len(tb[NL80211_ATTR_FRAME]);
+		wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+	}
+
+	return NL_SKIP;
+}
+
+
+static struct nl80211_wiphy_data *
+nl80211_get_wiphy_data_ap(struct i802_bss *bss)
+{
+	static DEFINE_DL_LIST(nl80211_wiphys);
+	struct nl80211_wiphy_data *w;
+	int wiphy_idx, found = 0;
+	struct i802_bss *tmp_bss;
+
+	if (bss->wiphy_data != NULL)
+		return bss->wiphy_data;
+
+	wiphy_idx = nl80211_get_wiphy_index(bss);
+
+	dl_list_for_each(w, &nl80211_wiphys, struct nl80211_wiphy_data, list) {
+		if (w->wiphy_idx == wiphy_idx)
+			goto add;
+	}
+
+	/* alloc new one */
+	w = os_zalloc(sizeof(*w));
+	if (w == NULL)
+		return NULL;
+	w->wiphy_idx = wiphy_idx;
+	dl_list_init(&w->bsss);
+	dl_list_init(&w->drvs);
+
+	w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+	if (!w->nl_cb) {
+		os_free(w);
+		return NULL;
+	}
+	nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
+	nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_beacon_event,
+		  w);
+
+	w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb,
+					 "wiphy beacons");
+	if (w->nl_beacons == NULL) {
+		os_free(w);
+		return NULL;
+	}
+
+	if (nl80211_register_beacons(bss->drv, w)) {
+		nl_destroy_handles(&w->nl_beacons);
+		os_free(w);
+		return NULL;
+	}
+
+	nl80211_register_eloop_read(&w->nl_beacons, nl80211_recv_beacons, w);
+
+	dl_list_add(&nl80211_wiphys, &w->list);
+
+add:
+	/* drv entry for this bss already there? */
+	dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) {
+		if (tmp_bss->drv == bss->drv) {
+			found = 1;
+			break;
+		}
+	}
+	/* if not add it */
+	if (!found)
+		dl_list_add(&w->drvs, &bss->drv->wiphy_list);
+
+	dl_list_add(&w->bsss, &bss->wiphy_list);
+	bss->wiphy_data = w;
+	return w;
+}
+
+
+static void nl80211_put_wiphy_data_ap(struct i802_bss *bss)
+{
+	struct nl80211_wiphy_data *w = bss->wiphy_data;
+	struct i802_bss *tmp_bss;
+	int found = 0;
+
+	if (w == NULL)
+		return;
+	bss->wiphy_data = NULL;
+	dl_list_del(&bss->wiphy_list);
+
+	/* still any for this drv present? */
+	dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) {
+		if (tmp_bss->drv == bss->drv) {
+			found = 1;
+			break;
+		}
+	}
+	/* if not remove it */
+	if (!found)
+		dl_list_del(&bss->drv->wiphy_list);
+
+	if (!dl_list_empty(&w->bsss))
+		return;
+
+	nl80211_destroy_eloop_handle(&w->nl_beacons);
+
+	nl_cb_put(w->nl_cb);
+	dl_list_del(&w->list);
+	os_free(w);
+}
+
+
+static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	if (!drv->associated)
+		return -1;
+	os_memcpy(bssid, drv->bssid, ETH_ALEN);
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	if (!drv->associated)
+		return -1;
+	os_memcpy(ssid, drv->ssid, drv->ssid_len);
+	return drv->ssid_len;
+}
+
+
+static void wpa_driver_nl80211_event_newlink(
+	struct wpa_driver_nl80211_data *drv, const char *ifname)
+{
+	union wpa_event_data event;
+
+	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, const 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);
+}
+
+
+static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv,
+					 u8 *buf, size_t len)
+{
+	int attrlen, rta_len;
+	struct rtattr *attr;
+
+	attrlen = len;
+	attr = (struct rtattr *) buf;
+
+	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)
+				return 1;
+			else
+				break;
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
+					  int ifindex, u8 *buf, size_t len)
+{
+	if (drv->ifindex == ifindex)
+		return 1;
+
+	if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) {
+		nl80211_check_global(drv->global);
+		wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
+			   "interface");
+		wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static struct wpa_driver_nl80211_data *
+nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len)
+{
+	struct wpa_driver_nl80211_data *drv;
+	dl_list_for_each(drv, &global->interfaces,
+			 struct wpa_driver_nl80211_data, list) {
+		if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) ||
+		    have_ifidx(drv, idx))
+			return drv;
+	}
+	return NULL;
+}
+
+
+static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+						 struct ifinfomsg *ifi,
+						 u8 *buf, size_t len)
+{
+	struct nl80211_global *global = ctx;
+	struct wpa_driver_nl80211_data *drv;
+	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 RTM_NEWLINK event for foreign ifindex %d",
+			   ifi->ifi_index);
+		return;
+	}
+
+	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_family=%d ifi_flags=0x%x (%s%s%s%s)",
+		   ifi->ifi_index, ifname, extra, ifi->ifi_family,
+		   ifi->ifi_flags,
+		   (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+		   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+	if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+		namebuf[0] = '\0';
+		if (if_indextoname(ifi->ifi_index, namebuf) &&
+		    linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
+				   "event since interface %s is up", namebuf);
+			drv->ignore_if_down_event = 0;
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
+			   namebuf, ifname);
+		if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Not the main interface (%s) - do not indicate interface down",
+				   drv->first_bss->ifname);
+		} else if (drv->ignore_if_down_event) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
+				   "event generated by mode change");
+			drv->ignore_if_down_event = 0;
+		} else {
+			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, namebuf) == 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) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+				   "event since interface %s does not exist",
+				   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);
+		} else {
+			struct i802_bss *bss;
+			u8 addr[ETH_ALEN];
+
+			/* Re-read MAC address as it may have changed */
+			bss = get_bss_ifindex(drv, ifi->ifi_index);
+			if (bss &&
+			    linux_get_ifhwaddr(drv->global->ioctl_sock,
+					       bss->ifname, addr) < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: %s: failed to re-read MAC address",
+					   bss->ifname);
+			} else if (bss &&
+				   os_memcmp(addr, bss->addr, ETH_ALEN) != 0) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Own MAC address on ifindex %d (%s) changed from "
+					   MACSTR " to " MACSTR,
+					   ifi->ifi_index, bss->ifname,
+					   MAC2STR(bss->addr),
+					   MAC2STR(addr));
+				os_memcpy(bss->addr, addr, ETH_ALEN);
+			}
+
+			wpa_printf(MSG_DEBUG, "nl80211: Interface up");
+			drv->if_disabled = 0;
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+					     NULL);
+		}
+	}
+
+	/*
+	 * Some drivers send the association event before the operup event--in
+	 * this case, lifting operstate in wpa_driver_nl80211_set_operstate()
+	 * fails. This will hit us when wpa_supplicant does not need to do
+	 * IEEE 802.1X authentication
+	 */
+	if (drv->operstate == 1 &&
+	    (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
+	    !(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);
+	}
+
+	if (ifname[0])
+		wpa_driver_nl80211_event_newlink(drv, ifname);
+
+	if (ifi->ifi_family == AF_BRIDGE && brid) {
+		struct i802_bss *bss;
+
+		/* device has been added to bridge */
+		if (!if_indextoname(brid, namebuf)) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Could not find bridge ifname for ifindex %u",
+				   brid);
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
+			   brid, namebuf);
+		add_ifidx(drv, brid);
+
+		for (bss = drv->first_bss; bss; bss = bss->next) {
+			if (os_strcmp(ifname, bss->ifname) == 0) {
+				os_strlcpy(bss->brname, namebuf, IFNAMSIZ);
+				break;
+			}
+		}
+	}
+}
+
+
+static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
+						 struct ifinfomsg *ifi,
+						 u8 *buf, size_t len)
+{
+	struct nl80211_global *global = ctx;
+	struct wpa_driver_nl80211_data *drv;
+	int attrlen;
+	struct rtattr *attr;
+	u32 brid = 0;
+	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 RTM_DELLINK event for foreign ifindex %d",
+			   ifi->ifi_index);
+		return;
+	}
+
+	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_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_DELLINK: ifi_index=%d ifname=%s%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
+		   ifi->ifi_index, ifname, extra, ifi->ifi_family,
+		   ifi->ifi_flags,
+		   (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+		   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+	if (ifname[0] && (ifi->ifi_family != AF_BRIDGE || !brid))
+		wpa_driver_nl80211_event_dellink(drv, ifname);
+
+	if (ifi->ifi_family == AF_BRIDGE && brid) {
+		/* device has been removed from bridge */
+		char namebuf[IFNAMSIZ];
+
+		if (!if_indextoname(brid, namebuf)) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Could not find bridge ifname for ifindex %u",
+				   brid);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Remove ifindex %u for bridge %s",
+				   brid, namebuf);
+		}
+		del_ifidx(drv, brid);
+	}
+}
+
+
+unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	int ret;
+	struct nl80211_bss_info_arg arg;
+
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+	os_memset(&arg, 0, sizeof(arg));
+	arg.drv = drv;
+	ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+	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", freq);
+		if (freq)
+			drv->assoc_freq = freq;
+		return drv->assoc_freq;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+		   "(%s)", ret, strerror(-ret));
+	return drv->assoc_freq;
+}
+
+
+static int get_link_signal(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
+	static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
+		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+		[NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
+		[NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8 },
+	};
+	struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
+	static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+		[NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+		[NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
+		[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+	};
+	struct wpa_signal_info *sig_change = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb[NL80211_ATTR_STA_INFO] ||
+	    nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
+			     tb[NL80211_ATTR_STA_INFO], policy))
+		return NL_SKIP;
+	if (!sinfo[NL80211_STA_INFO_SIGNAL])
+		return NL_SKIP;
+
+	sig_change->current_signal =
+		(s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
+
+	if (sinfo[NL80211_STA_INFO_SIGNAL_AVG])
+		sig_change->avg_signal =
+			(s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]);
+	else
+		sig_change->avg_signal = 0;
+
+	if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
+		sig_change->avg_beacon_signal =
+			(s8)
+			nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]);
+	else
+		sig_change->avg_beacon_signal = 0;
+
+	if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
+		if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
+				     sinfo[NL80211_STA_INFO_TX_BITRATE],
+				     rate_policy)) {
+			sig_change->current_txrate = 0;
+		} else {
+			if (rinfo[NL80211_RATE_INFO_BITRATE]) {
+				sig_change->current_txrate =
+					nla_get_u16(rinfo[
+					     NL80211_RATE_INFO_BITRATE]) * 100;
+			}
+		}
+	}
+
+	return NL_SKIP;
+}
+
+
+int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+			    struct wpa_signal_info *sig)
+{
+	struct nl_msg *msg;
+
+	sig->current_signal = -9999;
+	sig->current_txrate = 0;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	return send_and_recv_msgs(drv, msg, get_link_signal, sig);
+}
+
+
+static int get_link_noise(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+	static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+		[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+	};
+	struct wpa_signal_info *sig_change = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+		wpa_printf(MSG_DEBUG, "nl80211: survey data missing!");
+		return NL_SKIP;
+	}
+
+	if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+			     tb[NL80211_ATTR_SURVEY_INFO],
+			     survey_policy)) {
+		wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested "
+			   "attributes!");
+		return NL_SKIP;
+	}
+
+	if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+		return NL_SKIP;
+
+	if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+	    sig_change->frequency)
+		return NL_SKIP;
+
+	if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+		return NL_SKIP;
+
+	sig_change->current_noise =
+		(s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+
+	return NL_SKIP;
+}
+
+
+int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+			   struct wpa_signal_info *sig_change)
+{
+	struct nl_msg *msg;
+
+	sig_change->current_noise = 9999;
+	sig_change->frequency = drv->assoc_freq;
+
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+	return send_and_recv_msgs(drv, msg, get_link_noise, sig_change);
+}
+
+
+static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
+					     void *handle)
+{
+	struct nl_cb *cb = eloop_ctx;
+	int res;
+
+	wpa_printf(MSG_MSGDUMP, "nl80211: Event message available");
+
+	res = nl_recvmsgs(handle, cb);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d",
+			   __func__, res);
+	}
+}
+
+
+/**
+ * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain
+ * @priv: driver_nl80211 private data
+ * @alpha2_arg: country to which to switch to
+ * Returns: 0 on success, -1 on failure
+ *
+ * This asks nl80211 to set the regulatory domain for given
+ * country ISO / IEC alpha2.
+ */
+static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	char alpha2[3];
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	alpha2[0] = alpha2_arg[0];
+	alpha2[1] = alpha2_arg[1];
+	alpha2[2] = '\0';
+
+	if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG) ||
+	    nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, alpha2)) {
+		nlmsg_free(msg);
+		return -EINVAL;
+	}
+	if (send_and_recv_msgs(drv, msg, NULL, NULL))
+		return -EINVAL;
+	return 0;
+}
+
+
+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 wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
+{
+	int ret;
+
+	global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+	if (global->nl_cb == NULL) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+			   "callbacks");
+		return -1;
+	}
+
+	global->nl = nl_create_handle(global->nl_cb, "nl");
+	if (global->nl == NULL)
+		goto err;
+
+	global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211");
+	if (global->nl80211_id < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
+			   "found");
+		goto err;
+	}
+
+	global->nl_event = nl_create_handle(global->nl_cb, "event");
+	if (global->nl_event == NULL)
+		goto err;
+
+	ret = nl_get_multicast_id(global, "nl80211", "scan");
+	if (ret >= 0)
+		ret = nl_socket_add_membership(global->nl_event, ret);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
+			   "membership for scan events: %d (%s)",
+			   ret, strerror(-ret));
+		goto err;
+	}
+
+	ret = nl_get_multicast_id(global, "nl80211", "mlme");
+	if (ret >= 0)
+		ret = nl_socket_add_membership(global->nl_event, ret);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
+			   "membership for mlme events: %d (%s)",
+			   ret, strerror(-ret));
+		goto err;
+	}
+
+	ret = nl_get_multicast_id(global, "nl80211", "regulatory");
+	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 regulatory events: %d (%s)",
+			   ret, strerror(-ret));
+		/* 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);
+
+	nl80211_register_eloop_read(&global->nl_event,
+				    wpa_driver_nl80211_event_receive,
+				    global->nl_cb);
+
+	return 0;
+
+err:
+	nl_destroy_handles(&global->nl_event);
+	nl_destroy_handles(&global->nl);
+	nl_cb_put(global->nl_cb);
+	global->nl_cb = NULL;
+	return -1;
+}
+
+
+static void nl80211_check_global(struct nl80211_global *global)
+{
+	struct nl_handle *handle;
+	const char *groups[] = { "scan", "mlme", "regulatory", "vendor", NULL };
+	int ret;
+	unsigned int i;
+
+	/*
+	 * Try to re-add memberships to handle case of cfg80211 getting reloaded
+	 * and all registration having been cleared.
+	 */
+	handle = (void *) (((intptr_t) global->nl_event) ^
+			   ELOOP_SOCKET_INVALID);
+
+	for (i = 0; groups[i]; i++) {
+		ret = nl_get_multicast_id(global, "nl80211", groups[i]);
+		if (ret >= 0)
+			ret = nl_socket_add_membership(handle, ret);
+		if (ret < 0) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Could not re-add multicast membership for %s events: %d (%s)",
+				   groups[i], ret, strerror(-ret));
+		}
+	}
+}
+
+
+static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
+{
+	wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
+	/*
+	 * This may be for any interface; use ifdown event to disable
+	 * interface.
+	 */
+}
+
+
+static void wpa_driver_nl80211_rfkill_unblocked(void *ctx)
+{
+	struct wpa_driver_nl80211_data *drv = ctx;
+	wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
+	if (i802_set_iface_flags(drv->first_bss, 1)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
+			   "after rfkill unblock");
+		return;
+	}
+	/* rtnetlink ifup handler will report interface as enabled */
+}
+
+
+static void wpa_driver_nl80211_handle_eapol_tx_status(int sock,
+						      void *eloop_ctx,
+						      void *handle)
+{
+	struct wpa_driver_nl80211_data *drv = eloop_ctx;
+	u8 data[2048];
+	struct msghdr msg;
+	struct iovec entry;
+	u8 control[512];
+	struct cmsghdr *cmsg;
+	int res, found_ee = 0, found_wifi = 0, acked = 0;
+	union wpa_event_data event;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = &entry;
+	msg.msg_iovlen = 1;
+	entry.iov_base = data;
+	entry.iov_len = sizeof(data);
+	msg.msg_control = &control;
+	msg.msg_controllen = sizeof(control);
+
+	res = recvmsg(sock, &msg, MSG_ERRQUEUE);
+	/* if error or not fitting 802.3 header, return */
+	if (res < 14)
+		return;
+
+	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
+	{
+		if (cmsg->cmsg_level == SOL_SOCKET &&
+		    cmsg->cmsg_type == SCM_WIFI_STATUS) {
+			int *ack;
+
+			found_wifi = 1;
+			ack = (void *)CMSG_DATA(cmsg);
+			acked = *ack;
+		}
+
+		if (cmsg->cmsg_level == SOL_PACKET &&
+		    cmsg->cmsg_type == PACKET_TX_TIMESTAMP) {
+			struct sock_extended_err *err =
+				(struct sock_extended_err *)CMSG_DATA(cmsg);
+
+			if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS)
+				found_ee = 1;
+		}
+	}
+
+	if (!found_ee || !found_wifi)
+		return;
+
+	memset(&event, 0, sizeof(event));
+	event.eapol_tx_status.dst = data;
+	event.eapol_tx_status.data = data + 14;
+	event.eapol_tx_status.data_len = res - 14;
+	event.eapol_tx_status.ack = acked;
+	wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
+}
+
+
+static int nl80211_init_bss(struct i802_bss *bss)
+{
+	bss->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+	if (!bss->nl_cb)
+		return -1;
+
+	nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+		  no_seq_check, NULL);
+	nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+		  process_bss_event, bss);
+
+	return 0;
+}
+
+
+static void nl80211_destroy_bss(struct i802_bss *bss)
+{
+	nl_cb_put(bss->nl_cb);
+	bss->nl_cb = NULL;
+}
+
+
+static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
+					  void *global_priv, int hostapd,
+					  const u8 *set_addr,
+					  const char *driver_params)
+{
+	struct wpa_driver_nl80211_data *drv;
+	struct rfkill_config *rcfg;
+	struct i802_bss *bss;
+
+	if (global_priv == NULL)
+		return NULL;
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL)
+		return NULL;
+	drv->global = global_priv;
+	drv->ctx = ctx;
+	drv->hostapd = !!hostapd;
+	drv->eapol_sock = -1;
+
+	/*
+	 * There is no driver capability flag for this, so assume it is
+	 * supported and disable this on first attempt to use if the driver
+	 * rejects the command due to missing support.
+	 */
+	drv->set_rekey_offload = 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;
+
+	os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
+	drv->monitor_ifidx = -1;
+	drv->monitor_sock = -1;
+	drv->eapol_tx_sock = -1;
+	drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+
+	if (nl80211_init_bss(bss))
+		goto failed;
+
+	rcfg = os_zalloc(sizeof(*rcfg));
+	if (rcfg == NULL)
+		goto failed;
+	rcfg->ctx = drv;
+	os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+	rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
+	rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
+	drv->rfkill = rfkill_init(rcfg);
+	if (drv->rfkill == NULL) {
+		wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
+		os_free(rcfg);
+	}
+
+	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, driver_params))
+		goto failed;
+
+	drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+	if (drv->eapol_tx_sock < 0)
+		goto failed;
+
+	if (drv->data_tx_status) {
+		int enabled = 1;
+
+		if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS,
+			       &enabled, sizeof(enabled)) < 0) {
+			wpa_printf(MSG_DEBUG,
+				"nl80211: wifi status sockopt failed\n");
+			drv->data_tx_status = 0;
+			if (!drv->use_monitor)
+				drv->capa.flags &=
+					~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+		} else {
+			eloop_register_read_sock(drv->eapol_tx_sock,
+				wpa_driver_nl80211_handle_eapol_tx_status,
+				drv, NULL);
+		}
+	}
+
+	if (drv->global) {
+		nl80211_check_global(drv->global);
+		dl_list_add(&drv->global->interfaces, &drv->list);
+		drv->in_interface_list = 1;
+	}
+
+	return bss;
+
+failed:
+	wpa_driver_nl80211_deinit(bss);
+	return NULL;
+}
+
+
+/**
+ * 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,
+					   NULL);
+}
+
+
+static int nl80211_register_frame(struct i802_bss *bss,
+				  struct nl_handle *nl_handle,
+				  u16 type, const u8 *match, size_t match_len)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	char buf[30];
+
+	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);
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REGISTER_ACTION)) ||
+	    nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, type) ||
+	    nla_put(msg, NL80211_ATTR_FRAME_MATCH, match_len, match)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
+			   "failed (type=%u): ret=%d (%s)",
+			   type, ret, strerror(-ret));
+		wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
+			    match, match_len);
+	}
+	return ret;
+}
+
+
+static int nl80211_alloc_mgmt_handle(struct i802_bss *bss)
+{
+	if (bss->nl_mgmt) {
+		wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting "
+			   "already on! (nl_mgmt=%p)", bss->nl_mgmt);
+		return -1;
+	}
+
+	bss->nl_mgmt = nl_create_handle(bss->nl_cb, "mgmt");
+	if (bss->nl_mgmt == NULL)
+		return -1;
+
+	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)
+{
+	u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
+	return nl80211_register_frame(bss, bss->nl_mgmt,
+				      type, match, match_len);
+}
+
+
+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)
+		ret = -1;
+	/* GAS Initial Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0)
+		ret = -1;
+	/* GAS Comeback Request */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0)
+		ret = -1;
+	/* GAS Comeback Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0)
+		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)
+		ret = -1;
+	/* P2P Action */
+	if (nl80211_register_action_frame(bss,
+					  (u8 *) "\x7f\x50\x6f\x9a\x09",
+					  5) < 0)
+		ret = -1;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_IEEE80211W
+	/* SA Query Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
+		ret = -1;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_TDLS
+	if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) {
+		/* TDLS Discovery Response */
+		if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) <
+		    0)
+			ret = -1;
+	}
+#endif /* CONFIG_TDLS */
+#ifdef CONFIG_FST
+	/* FST Action frames */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
+		ret = -1;
+#endif /* CONFIG_FST */
+
+	/* FT Action frames */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
+		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)
+		ret = -1;
+	/* WNM-Sleep Mode Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0)
+		ret = -1;
+
+#ifdef CONFIG_HS20
+	/* WNM-Notification */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x1a", 2) < 0)
+		ret = -1;
+#endif /* CONFIG_HS20 */
+
+	/* WMM-AC ADDTS Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x11\x01", 2) < 0)
+		ret = -1;
+
+	/* WMM-AC DELTS */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x11\x02", 2) < 0)
+		ret = -1;
+
+	/* Radio Measurement - Neighbor Report Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x05\x05", 2) < 0)
+		ret = -1;
+
+	/* Radio Measurement - Link Measurement Request */
+	if ((drv->capa.rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) &&
+	    (nl80211_register_action_frame(bss, (u8 *) "\x05\x02", 2) < 0))
+		ret = -1;
+
+	nl80211_mgmt_handle_register_eloop(bss);
+
+	return ret;
+}
+
+
+static int nl80211_mgmt_subscribe_mesh(struct i802_bss *bss)
+{
+	int ret = 0;
+
+	if (nl80211_alloc_mgmt_handle(bss))
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Subscribe to mgmt frames with mesh handle %p",
+		   bss->nl_mgmt);
+
+	/* Auth frames for mesh SAE */
+	if (nl80211_register_frame(bss, bss->nl_mgmt,
+				   (WLAN_FC_TYPE_MGMT << 2) |
+				   (WLAN_FC_STYPE_AUTH << 4),
+				   NULL, 0) < 0)
+		ret = -1;
+
+	/* Mesh peering open */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x01", 2) < 0)
+		ret = -1;
+	/* Mesh peering confirm */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x02", 2) < 0)
+		ret = -1;
+	/* Mesh peering close */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x03", 2) < 0)
+		ret = -1;
+
+	nl80211_mgmt_handle_register_eloop(bss);
+
+	return ret;
+}
+
+
+static int nl80211_register_spurious_class3(struct i802_bss *bss)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME);
+	ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
+			   "failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	}
+	return ret;
+}
+
+
+static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss)
+{
+	static const int stypes[] = {
+		WLAN_FC_STYPE_AUTH,
+		WLAN_FC_STYPE_ASSOC_REQ,
+		WLAN_FC_STYPE_REASSOC_REQ,
+		WLAN_FC_STYPE_DISASSOC,
+		WLAN_FC_STYPE_DEAUTH,
+		WLAN_FC_STYPE_ACTION,
+		WLAN_FC_STYPE_PROBE_REQ,
+/* Beacon doesn't work as mac80211 doesn't currently allow
+ * it, but it wouldn't really be the right thing anyway as
+ * it isn't per interface ... maybe just dump the scan
+ * results periodically for OLBC?
+ */
+		/* WLAN_FC_STYPE_BEACON, */
+	};
+	unsigned int i;
+
+	if (nl80211_alloc_mgmt_handle(bss))
+		return -1;
+	wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
+		   "handle %p", bss->nl_mgmt);
+
+	for (i = 0; i < ARRAY_SIZE(stypes); i++) {
+		if (nl80211_register_frame(bss, bss->nl_mgmt,
+					   (WLAN_FC_TYPE_MGMT << 2) |
+					   (stypes[i] << 4),
+					   NULL, 0) < 0) {
+			goto out_err;
+		}
+	}
+
+	if (nl80211_register_spurious_class3(bss))
+		goto out_err;
+
+	if (nl80211_get_wiphy_data_ap(bss) == NULL)
+		goto out_err;
+
+	nl80211_mgmt_handle_register_eloop(bss);
+	return 0;
+
+out_err:
+	nl_destroy_handles(&bss->nl_mgmt);
+	return -1;
+}
+
+
+static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss)
+{
+	if (nl80211_alloc_mgmt_handle(bss))
+		return -1;
+	wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
+		   "handle %p (device SME)", bss->nl_mgmt);
+
+	if (nl80211_register_frame(bss, bss->nl_mgmt,
+				   (WLAN_FC_TYPE_MGMT << 2) |
+				   (WLAN_FC_STYPE_ACTION << 4),
+				   NULL, 0) < 0)
+		goto out_err;
+
+	nl80211_mgmt_handle_register_eloop(bss);
+	return 0;
+
+out_err:
+	nl_destroy_handles(&bss->nl_mgmt);
+	return -1;
+}
+
+
+static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason)
+{
+	if (bss->nl_mgmt == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p "
+		   "(%s)", bss->nl_mgmt, reason);
+	nl80211_destroy_eloop_handle(&bss->nl_mgmt);
+
+	nl80211_put_wiphy_data_ap(bss);
+}
+
+
+static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+	wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
+static void nl80211_del_p2pdev(struct i802_bss *bss)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_INTERFACE);
+	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+
+	wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s",
+		   bss->ifname, (long long unsigned int) bss->wdev_id,
+		   strerror(-ret));
+}
+
+
+static int nl80211_set_p2pdev(struct i802_bss *bss, int start)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_cmd_msg(bss, 0, start ? NL80211_CMD_START_P2P_DEVICE :
+			      NL80211_CMD_STOP_P2P_DEVICE);
+	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+
+	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));
+	return ret;
+}
+
+
+static int i802_set_iface_flags(struct i802_bss *bss, int up)
+{
+	enum nl80211_iftype nlmode;
+
+	nlmode = nl80211_get_ifmode(bss);
+	if (nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+		return linux_set_iface_flags(bss->drv->global->ioctl_sock,
+					     bss->ifname, up);
+	}
+
+	/* P2P Device has start/stop which is equivalent */
+	return nl80211_set_p2pdev(bss, up);
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+static int qca_vendor_test_cmd_handler(struct nl_msg *msg, void *arg)
+{
+	/* struct wpa_driver_nl80211_data *drv = arg; */
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: QCA vendor test command response received");
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb[NL80211_ATTR_VENDOR_DATA]) {
+		wpa_printf(MSG_DEBUG, "nl80211: No vendor data attribute");
+		return NL_SKIP;
+	}
+
+	wpa_hexdump(MSG_DEBUG,
+		    "nl80211: Received QCA vendor test command response",
+		    nla_data(tb[NL80211_ATTR_VENDOR_DATA]),
+		    nla_len(tb[NL80211_ATTR_VENDOR_DATA]));
+
+	return NL_SKIP;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static void qca_vendor_test(struct wpa_driver_nl80211_data *drv)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	struct nl_msg *msg;
+	struct nlattr *params;
+	int ret;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_TEST) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TEST, 123)) {
+		nlmsg_free(msg);
+		return;
+	}
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv);
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: QCA vendor test command returned %d (%s)",
+		   ret, strerror(-ret));
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static int
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
+				   const u8 *set_addr, int first,
+				   const char *driver_params)
+{
+	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;
+	bss->wdev_id = drv->global->if_add_wdevid;
+	bss->wdev_id_set = drv->global->if_add_wdevid_set;
+
+	bss->if_dynamic = drv->ifindex == drv->global->if_add_ifindex;
+	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;
+
+	if (driver_params && nl80211_set_param(bss, driver_params) < 0)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s",
+		   bss->ifname, drv->phyname);
+
+	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;
+
+	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 mode");
+		return -1;
+	}
+
+	if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+		nl80211_get_macaddr(bss);
+
+	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 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;
+	}
+
+	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))
+		return -1;
+	os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN);
+
+	if (send_rfkill_event) {
+		eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
+				       drv, drv->ctx);
+	}
+
+	if (drv->vendor_cmd_test_avail)
+		qca_vendor_test(drv);
+
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
+		   drv->ifindex);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+
+/**
+ * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface
+ * @bss: Pointer to private nl80211 data from wpa_driver_nl80211_init()
+ *
+ * Shut down driver interface and processing of driver events. Free
+ * private data buffer if one was allocated in wpa_driver_nl80211_init().
+ */
+static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	wpa_printf(MSG_INFO, "nl80211: deinit ifname=%s disabled_11b_rates=%d",
+		   bss->ifname, drv->disabled_11b_rates);
+
+	bss->in_deinit = 1;
+	if (drv->data_tx_status)
+		eloop_unregister_read_sock(drv->eapol_tx_sock);
+	if (drv->eapol_tx_sock >= 0)
+		close(drv->eapol_tx_sock);
+
+	if (bss->nl_preq)
+		wpa_driver_nl80211_probe_req_report(bss, 0);
+	if (bss->added_if_into_bridge) {
+		if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+				    bss->ifname) < 0)
+			wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+				   "interface %s from bridge %s: %s",
+				   bss->ifname, bss->brname, strerror(errno));
+		if (drv->rtnl_sk)
+			nl80211_handle_destroy(drv->rtnl_sk);
+	}
+	if (bss->added_bridge) {
+		if (linux_set_iface_flags(drv->global->ioctl_sock, bss->brname,
+					  0) < 0)
+			wpa_printf(MSG_INFO,
+				   "nl80211: Could not set bridge %s down",
+				   bss->brname);
+		if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
+			wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+				   "bridge %s: %s",
+				   bss->brname, strerror(errno));
+	}
+
+	nl80211_remove_monitor_interface(drv);
+
+	if (is_ap_interface(drv->nlmode))
+		wpa_driver_nl80211_del_beacon(drv);
+
+	if (drv->eapol_sock >= 0) {
+		eloop_unregister_read_sock(drv->eapol_sock);
+		close(drv->eapol_sock);
+	}
+
+	if (drv->if_indices != drv->default_if_indices)
+		os_free(drv->if_indices);
+
+	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);
+
+	if (!drv->start_iface_up)
+		(void) i802_set_iface_flags(bss, 0);
+
+	if (drv->addr_changed) {
+		if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname,
+					  0) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Could not set interface down to restore permanent MAC address");
+		}
+		if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+				       drv->perm_addr) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Could not restore permanent MAC address");
+		}
+	}
+
+	if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+		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");
+		nl80211_del_p2pdev(bss);
+	}
+
+	nl80211_destroy_bss(drv->first_bss);
+
+	os_free(drv->filter_ssids);
+
+	os_free(drv->auth_ie);
+
+	if (drv->in_interface_list)
+		dl_list_del(&drv->list);
+
+	os_free(drv->extended_capa);
+	os_free(drv->extended_capa_mask);
+	os_free(drv->first_bss);
+	os_free(drv);
+}
+
+
+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 issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
+				  const u8 *key, size_t key_len)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
+		return 0;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY) ||
+	    nla_put(msg, NL80211_ATTR_VENDOR_DATA, key_len, key)) {
+		nl80211_nlmsg_clear(msg);
+		nlmsg_free(msg);
+		return -1;
+	}
+	ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Key management set key failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	}
+
+	return ret;
+}
+
+
+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,
+				      const u8 *seq, size_t seq_len,
+				      const u8 *key, size_t key_len)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ifindex;
+	struct nl_msg *msg = NULL;
+	int ret;
+	int tdls = 0;
+
+	/* Ignore for P2P Device */
+	if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+		return 0;
+
+	ifindex = if_nametoindex(ifname);
+	wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d "
+		   "set_tx=%d seq_len=%lu key_len=%lu",
+		   __func__, ifindex, ifname, alg, addr, key_idx, set_tx,
+		   (unsigned long) seq_len, (unsigned long) key_len);
+#ifdef CONFIG_TDLS
+	if (key_idx == -1) {
+		key_idx = 0;
+		tdls = 1;
+	}
+#endif /* CONFIG_TDLS */
+
+	if (alg == WPA_ALG_PMK &&
+	    (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
+		wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key",
+			   __func__);
+		ret = issue_key_mgmt_set_key(drv, key, key_len);
+		return ret;
+	}
+
+	if (alg == WPA_ALG_NONE) {
+		msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
+		if (!msg)
+			return -ENOBUFS;
+	} else {
+		u32 suite;
+
+		suite = wpa_alg_to_cipher_suite(alg, key_len);
+		if (!suite)
+			goto fail;
+		msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_NEW_KEY);
+		if (!msg ||
+		    nla_put(msg, NL80211_ATTR_KEY_DATA, key_len, key) ||
+		    nla_put_u32(msg, NL80211_ATTR_KEY_CIPHER, suite))
+			goto fail;
+		wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_DATA", key, key_len);
+	}
+
+	if (seq && seq_len) {
+		if (nla_put(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq))
+			goto fail;
+		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));
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+			goto fail;
+
+		if (alg != WPA_ALG_WEP && key_idx && !set_tx) {
+			wpa_printf(MSG_DEBUG, "   RSN IBSS RX GTK");
+			if (nla_put_u32(msg, NL80211_ATTR_KEY_TYPE,
+					NL80211_KEYTYPE_GROUP))
+				goto fail;
+		}
+	} else if (addr && is_broadcast_ether_addr(addr)) {
+		struct nlattr *types;
+
+		wpa_printf(MSG_DEBUG, "   broadcast key");
+
+		types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
+		if (!types ||
+		    nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST))
+			goto fail;
+		nla_nest_end(msg, types);
+	}
+	if (nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx))
+		goto fail;
+
+	ret = send_and_recv_msgs(drv, msg, NULL, key ? (void *) -1 : NULL);
+	if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
+		ret = 0;
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d %s)",
+			   ret, strerror(-ret));
+
+	/*
+	 * If we failed or don't need to set the default TX key (below),
+	 * we're done here.
+	 */
+	if (ret || !set_tx || alg == WPA_ALG_NONE || tdls)
+		return ret;
+	if (is_ap_interface(drv->nlmode) && addr &&
+	    !is_broadcast_ether_addr(addr))
+		return ret;
+
+	msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
+	if (!msg ||
+	    nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx) ||
+	    nla_put_flag(msg, (alg == WPA_ALG_IGTK ||
+			       alg == WPA_ALG_BIP_GMAC_128 ||
+			       alg == WPA_ALG_BIP_GMAC_256 ||
+			       alg == WPA_ALG_BIP_CMAC_256) ?
+			 NL80211_ATTR_KEY_DEFAULT_MGMT :
+			 NL80211_ATTR_KEY_DEFAULT))
+		goto fail;
+	if (addr && is_broadcast_ether_addr(addr)) {
+		struct nlattr *types;
+
+		types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
+		if (!types ||
+		    nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST))
+			goto fail;
+		nla_nest_end(msg, types);
+	} else if (addr) {
+		struct nlattr *types;
+
+		types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
+		if (!types ||
+		    nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_UNICAST))
+			goto fail;
+		nla_nest_end(msg, types);
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret == -ENOENT)
+		ret = 0;
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: set_key default failed; "
+			   "err=%d %s)", ret, strerror(-ret));
+	return ret;
+
+fail:
+	nl80211_nlmsg_clear(msg);
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
+static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg,
+		      int key_idx, int defkey,
+		      const u8 *seq, size_t seq_len,
+		      const u8 *key, size_t key_len)
+{
+	struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY);
+	u32 suite;
+
+	if (!key_attr)
+		return -1;
+
+	suite = wpa_alg_to_cipher_suite(alg, key_len);
+	if (!suite)
+		return -1;
+
+	if (defkey && alg == WPA_ALG_IGTK) {
+		if (nla_put_flag(msg, NL80211_KEY_DEFAULT_MGMT))
+			return -1;
+	} else if (defkey) {
+		if (nla_put_flag(msg, NL80211_KEY_DEFAULT))
+			return -1;
+	}
+
+	if (nla_put_u8(msg, NL80211_KEY_IDX, key_idx) ||
+	    nla_put_u32(msg, NL80211_KEY_CIPHER, suite) ||
+	    (seq && seq_len &&
+	     nla_put(msg, NL80211_KEY_SEQ, seq_len, seq)) ||
+	    nla_put(msg, NL80211_KEY_DATA, key_len, key))
+		return -1;
+
+	nla_nest_end(msg, key_attr);
+
+	return 0;
+}
+
+
+static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params,
+				 struct nl_msg *msg)
+{
+	int i, privacy = 0;
+	struct nlattr *nl_keys, *nl_key;
+
+	for (i = 0; i < 4; i++) {
+		if (!params->wep_key[i])
+			continue;
+		privacy = 1;
+		break;
+	}
+	if (params->wps == WPS_MODE_PRIVACY)
+		privacy = 1;
+	if (params->pairwise_suite &&
+	    params->pairwise_suite != WPA_CIPHER_NONE)
+		privacy = 1;
+
+	if (!privacy)
+		return 0;
+
+	if (nla_put_flag(msg, NL80211_ATTR_PRIVACY))
+		return -ENOBUFS;
+
+	nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS);
+	if (!nl_keys)
+		return -ENOBUFS;
+
+	for (i = 0; i < 4; i++) {
+		if (!params->wep_key[i])
+			continue;
+
+		nl_key = nla_nest_start(msg, i);
+		if (!nl_key ||
+		    nla_put(msg, NL80211_KEY_DATA, params->wep_key_len[i],
+			    params->wep_key[i]) ||
+		    nla_put_u32(msg, NL80211_KEY_CIPHER,
+				params->wep_key_len[i] == 5 ?
+				WLAN_CIPHER_SUITE_WEP40 :
+				WLAN_CIPHER_SUITE_WEP104) ||
+		    nla_put_u8(msg, NL80211_KEY_IDX, i) ||
+		    (i == params->wep_tx_keyidx &&
+		     nla_put_flag(msg, NL80211_KEY_DEFAULT)))
+			return -ENOBUFS;
+
+		nla_nest_end(msg, nl_key);
+	}
+	nla_nest_end(msg, nl_keys);
+
+	return 0;
+}
+
+
+int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+			    const u8 *addr, int cmd, u16 reason_code,
+			    int local_state_change)
+{
+	int ret;
+	struct nl_msg *msg;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, cmd)) ||
+	    nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code) ||
+	    (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
+	    (local_state_change &&
+	     nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE))) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_dbg(drv->ctx, MSG_DEBUG,
+			"nl80211: MLME command failed: reason=%u ret=%d (%s)",
+			reason_code, ret, strerror(-ret));
+	}
+	return ret;
+}
+
+
+static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
+					 int reason_code)
+{
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code);
+	nl80211_mark_disconnected(drv);
+	/* Disconnect command doesn't need BSSID - it uses cached value */
+	ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT,
+				      reason_code, 0);
+	/*
+	 * For locally generated disconnect, supplicant already generates a
+	 * DEAUTH event, so ignore the event from NL80211.
+	 */
+	drv->ignore_next_local_disconnect = ret == 0;
+
+	return ret;
+}
+
+
+static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss,
+					     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, 1);
+	}
+	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);
+	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;
+}
+
+
+static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv,
+				     struct wpa_driver_auth_params *params)
+{
+	int i;
+
+	drv->auth_freq = params->freq;
+	drv->auth_alg = params->auth_alg;
+	drv->auth_wep_tx_keyidx = params->wep_tx_keyidx;
+	drv->auth_local_state_change = params->local_state_change;
+	drv->auth_p2p = params->p2p;
+
+	if (params->bssid)
+		os_memcpy(drv->auth_bssid_, params->bssid, ETH_ALEN);
+	else
+		os_memset(drv->auth_bssid_, 0, ETH_ALEN);
+
+	if (params->ssid) {
+		os_memcpy(drv->auth_ssid, params->ssid, params->ssid_len);
+		drv->auth_ssid_len = params->ssid_len;
+	} else
+		drv->auth_ssid_len = 0;
+
+
+	os_free(drv->auth_ie);
+	drv->auth_ie = NULL;
+	drv->auth_ie_len = 0;
+	if (params->ie) {
+		drv->auth_ie = os_malloc(params->ie_len);
+		if (drv->auth_ie) {
+			os_memcpy(drv->auth_ie, params->ie, params->ie_len);
+			drv->auth_ie_len = params->ie_len;
+		}
+	}
+
+	for (i = 0; i < 4; i++) {
+		if (params->wep_key[i] && params->wep_key_len[i] &&
+		    params->wep_key_len[i] <= 16) {
+			os_memcpy(drv->auth_wep_key[i], params->wep_key[i],
+				  params->wep_key_len[i]);
+			drv->auth_wep_key_len[i] = params->wep_key_len[i];
+		} else
+			drv->auth_wep_key_len[i] = 0;
+	}
+}
+
+
+static void nl80211_unmask_11b_rates(struct i802_bss *bss)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (is_p2p_net_interface(drv->nlmode) || !drv->disabled_11b_rates)
+		return;
+
+	/*
+	 * Looks like we failed to unmask 11b rates previously. This could
+	 * happen, e.g., if the interface was down at the point in time when a
+	 * P2P group was terminated.
+	 */
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Interface %s mode is for non-P2P, but 11b rates were disabled - re-enable them",
+		   bss->ifname);
+	nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+}
+
+
+static int wpa_driver_nl80211_authenticate(
+	struct i802_bss *bss, struct wpa_driver_auth_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret = -1, i;
+	struct nl_msg *msg;
+	enum nl80211_auth_type type;
+	enum nl80211_iftype nlmode;
+	int count = 0;
+	int is_retry;
+
+	nl80211_unmask_11b_rates(bss);
+
+	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);
+	if (params->bssid)
+		os_memcpy(drv->auth_attempt_bssid, params->bssid, ETH_ALEN);
+	else
+		os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
+	/* FIX: IBSS mode */
+	nlmode = params->p2p ?
+		NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+	if (drv->nlmode != nlmode &&
+	    wpa_driver_nl80211_set_mode(bss, nlmode) < 0)
+		return -1;
+
+retry:
+	wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
+		   drv->ifindex);
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_AUTHENTICATE);
+	if (!msg)
+		goto fail;
+
+	for (i = 0; i < 4; i++) {
+		if (!params->wep_key[i])
+			continue;
+		wpa_driver_nl80211_set_key(bss->ifname, bss, WPA_ALG_WEP,
+					   NULL, i,
+					   i == params->wep_tx_keyidx, NULL, 0,
+					   params->wep_key[i],
+					   params->wep_key_len[i]);
+		if (params->wep_tx_keyidx != i)
+			continue;
+		if (nl_add_key(msg, WPA_ALG_WEP, i, 1, NULL, 0,
+			       params->wep_key[i], params->wep_key_len[i]))
+			goto fail;
+	}
+
+	if (params->bssid) {
+		wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
+			   MAC2STR(params->bssid));
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+			goto fail;
+	}
+	if (params->freq) {
+		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq))
+			goto fail;
+	}
+	if (params->ssid) {
+		wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
+				  params->ssid, params->ssid_len);
+		if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
+			    params->ssid))
+			goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "  * IEs", params->ie, params->ie_len);
+	if (params->ie &&
+	    nla_put(msg, NL80211_ATTR_IE, params->ie_len, params->ie))
+		goto fail;
+	if (params->sae_data) {
+		wpa_hexdump(MSG_DEBUG, "  * SAE data", params->sae_data,
+			    params->sae_data_len);
+		if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len,
+			    params->sae_data))
+			goto fail;
+	}
+	if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+		type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+	else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+		type = NL80211_AUTHTYPE_SHARED_KEY;
+	else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+		type = NL80211_AUTHTYPE_NETWORK_EAP;
+	else if (params->auth_alg & WPA_AUTH_ALG_FT)
+		type = NL80211_AUTHTYPE_FT;
+	else if (params->auth_alg & WPA_AUTH_ALG_SAE)
+		type = NL80211_AUTHTYPE_SAE;
+	else
+		goto fail;
+	wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
+	if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+		goto fail;
+	if (params->local_state_change) {
+		wpa_printf(MSG_DEBUG, "  * Local state change only");
+		if (nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE))
+			goto fail;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_dbg(drv->ctx, MSG_DEBUG,
+			"nl80211: MLME command failed (auth): ret=%d (%s)",
+			ret, strerror(-ret));
+		count++;
+		if (ret == -EALREADY && count == 1 && params->bssid &&
+		    !params->local_state_change) {
+			/*
+			 * mac80211 does not currently accept new
+			 * authentication if we are already authenticated. As a
+			 * workaround, force deauthentication and try again.
+			 */
+			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);
+			nlmsg_free(msg);
+			goto retry;
+		}
+
+		if (ret == -ENOENT && params->freq && !is_retry) {
+			/*
+			 * cfg80211 has likely expired the BSS entry even
+			 * though it was previously available in our internal
+			 * BSS table. To recover quickly, start a single
+			 * channel scan on the specified channel.
+			 */
+			struct wpa_driver_scan_params scan;
+			int freqs[2];
+
+			os_memset(&scan, 0, sizeof(scan));
+			scan.num_ssids = 1;
+			if (params->ssid) {
+				scan.ssids[0].ssid = params->ssid;
+				scan.ssids[0].ssid_len = params->ssid_len;
+			}
+			freqs[0] = params->freq;
+			freqs[1] = 0;
+			scan.freqs = freqs;
+			wpa_printf(MSG_DEBUG, "nl80211: Trigger single "
+				   "channel scan to refresh cfg80211 BSS "
+				   "entry");
+			ret = wpa_driver_nl80211_scan(bss, &scan);
+			if (ret == 0) {
+				nl80211_copy_auth_params(drv, params);
+				drv->scan_for_auth = 1;
+			}
+		} else if (is_retry) {
+			/*
+			 * Need to indicate this with an event since the return
+			 * value from the retry is not delivered to core code.
+			 */
+			union wpa_event_data event;
+			wpa_printf(MSG_DEBUG, "nl80211: Authentication retry "
+				   "failed");
+			os_memset(&event, 0, sizeof(event));
+			os_memcpy(event.timeout_event.addr, drv->auth_bssid_,
+				  ETH_ALEN);
+			wpa_supplicant_event(drv->ctx, EVENT_AUTH_TIMED_OUT,
+					     &event);
+		}
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Authentication request send successfully");
+	}
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv)
+{
+	struct wpa_driver_auth_params params;
+	struct i802_bss *bss = drv->first_bss;
+	int i;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again");
+
+	os_memset(&params, 0, sizeof(params));
+	params.freq = drv->auth_freq;
+	params.auth_alg = drv->auth_alg;
+	params.wep_tx_keyidx = drv->auth_wep_tx_keyidx;
+	params.local_state_change = drv->auth_local_state_change;
+	params.p2p = drv->auth_p2p;
+
+	if (!is_zero_ether_addr(drv->auth_bssid_))
+		params.bssid = drv->auth_bssid_;
+
+	if (drv->auth_ssid_len) {
+		params.ssid = drv->auth_ssid;
+		params.ssid_len = drv->auth_ssid_len;
+	}
+
+	params.ie = drv->auth_ie;
+	params.ie_len = drv->auth_ie_len;
+
+	for (i = 0; i < 4; i++) {
+		if (drv->auth_wep_key_len[i]) {
+			params.wep_key[i] = drv->auth_wep_key[i];
+			params.wep_key_len[i] = drv->auth_wep_key_len[i];
+		}
+	}
+
+	drv->retry_auth = 1;
+	return wpa_driver_nl80211_authenticate(bss, &params);
+}
+
+
+static int wpa_driver_nl80211_send_frame(struct i802_bss *bss,
+					 const void *data, size_t len,
+					 int encrypt, int noack,
+					 unsigned int freq, int no_cck,
+					 int offchanok, unsigned int wait_time)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	u64 cookie;
+	int res;
+
+	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) {
+		wpa_printf(MSG_DEBUG, "nl80211: send_frame(freq=%u bss->freq=%u) -> send_monitor",
+			   freq, bss->freq);
+		return nl80211_send_monitor(drv, data, len, encrypt, noack);
+	}
+
+	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;
+}
+
+
+static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
+					size_t data_len, int noack,
+					unsigned int freq, int no_cck,
+					int offchanok,
+					unsigned int wait_time)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ieee80211_mgmt *mgmt;
+	int encrypt = 1;
+	u16 fc;
+
+	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) &&
+	    WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+	    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) {
+		/*
+		 * The use of last_mgmt_freq is a bit of a hack,
+		 * but it works due to the single-threaded nature
+		 * of wpa_supplicant.
+		 */
+		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) {
+			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,
+					      data, data_len,
+					      &drv->send_action_cookie,
+					      no_cck, noack, offchanok);
+	}
+
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+	    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
+		/*
+		 * Only one of the authentication frame types is encrypted.
+		 * In order for static WEP encryption to work properly (i.e.,
+		 * to not encrypt the frame), we need to tell mac80211 about
+		 * the frames that must not be encrypted.
+		 */
+		u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+		u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction);
+		if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3)
+			encrypt = 0;
+	}
+
+	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);
+}
+
+
+static int nl80211_put_basic_rates(struct nl_msg *msg, const int *basic_rates)
+{
+	u8 rates[NL80211_MAX_SUPP_RATES];
+	u8 rates_len = 0;
+	int i;
+
+	if (!basic_rates)
+		return 0;
+
+	for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++)
+		rates[rates_len++] = basic_rates[i] / 5;
+
+	return nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
+}
+
+
+static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble,
+			   int slot, int ht_opmode, int ap_isolate,
+			   const int *basic_rates)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_BSS)) ||
+	    (cts >= 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_BSS_CTS_PROT, cts)) ||
+	    (preamble >= 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble)) ||
+	    (slot >= 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot)) ||
+	    (ht_opmode >= 0 &&
+	     nla_put_u16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode)) ||
+	    (ap_isolate >= 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate)) ||
+	    nl80211_put_basic_rates(msg, basic_rates)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+
+static int wpa_driver_nl80211_set_acl(void *priv,
+				      struct hostapd_acl_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nl_msg *acl;
+	unsigned int i;
+	int ret;
+
+	if (!(drv->capa.max_acl_mac_addrs))
+		return -ENOTSUP;
+
+	if (params->num_mac_acl > drv->capa.max_acl_mac_addrs)
+		return -ENOTSUP;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)",
+		   params->acl_policy ? "Accept" : "Deny", params->num_mac_acl);
+
+	acl = nlmsg_alloc();
+	if (!acl)
+		return -ENOMEM;
+	for (i = 0; i < params->num_mac_acl; i++) {
+		if (nla_put(acl, i + 1, ETH_ALEN, params->mac_acl[i].addr)) {
+			nlmsg_free(acl);
+			return -ENOMEM;
+		}
+	}
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MAC_ACL)) ||
+	    nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ?
+			NL80211_ACL_POLICY_DENY_UNLESS_LISTED :
+			NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) ||
+	    nla_put_nested(msg, NL80211_ATTR_MAC_ADDRS, acl)) {
+		nlmsg_free(msg);
+		nlmsg_free(acl);
+		return -ENOMEM;
+	}
+	nlmsg_free(acl);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)",
+			   ret, strerror(-ret));
+	}
+
+	return ret;
+}
+
+
+static int nl80211_put_beacon_int(struct nl_msg *msg, int beacon_int)
+{
+	if (beacon_int > 0) {
+		wpa_printf(MSG_DEBUG, "  * beacon_int=%d", beacon_int);
+		return nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
+				   beacon_int);
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_set_ap(void *priv,
+				     struct wpa_driver_ap_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	u8 cmd = NL80211_CMD_NEW_BEACON;
+	int ret;
+	int beacon_set;
+	int num_suites;
+	int smps_mode;
+	u32 suites[10], suite;
+	u32 ver;
+
+	beacon_set = params->reenable ? 0 : bss->beacon_set;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
+		   beacon_set);
+	if (beacon_set)
+		cmd = NL80211_CMD_SET_BEACON;
+
+	wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head",
+		    params->head, params->head_len);
+	wpa_hexdump(MSG_DEBUG, "nl80211: Beacon tail",
+		    params->tail, params->tail_len);
+	wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", bss->ifindex);
+	wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int);
+	wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period);
+	wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid",
+			  params->ssid, params->ssid_len);
+	if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
+	    nla_put(msg, NL80211_ATTR_BEACON_HEAD, params->head_len,
+		    params->head) ||
+	    nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len,
+		    params->tail) ||
+	    nl80211_put_beacon_int(msg, params->beacon_int) ||
+	    nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period) ||
+	    nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
+		goto fail;
+	if (params->proberesp && params->proberesp_len) {
+		wpa_hexdump(MSG_DEBUG, "nl80211: proberesp (offload)",
+			    params->proberesp, params->proberesp_len);
+		if (nla_put(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len,
+			    params->proberesp))
+			goto fail;
+	}
+	switch (params->hide_ssid) {
+	case NO_SSID_HIDING:
+		wpa_printf(MSG_DEBUG, "nl80211: hidden SSID not in use");
+		if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+				NL80211_HIDDEN_SSID_NOT_IN_USE))
+			goto fail;
+		break;
+	case HIDDEN_SSID_ZERO_LEN:
+		wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero len");
+		if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+				NL80211_HIDDEN_SSID_ZERO_LEN))
+			goto fail;
+		break;
+	case HIDDEN_SSID_ZERO_CONTENTS:
+		wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero contents");
+		if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+				NL80211_HIDDEN_SSID_ZERO_CONTENTS))
+			goto fail;
+		break;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: privacy=%d", params->privacy);
+	if (params->privacy &&
+	    nla_put_flag(msg, NL80211_ATTR_PRIVACY))
+		goto fail;
+	wpa_printf(MSG_DEBUG, "nl80211: auth_algs=0x%x", params->auth_algs);
+	if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) ==
+	    (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) {
+		/* Leave out the attribute */
+	} else if (params->auth_algs & WPA_AUTH_ALG_SHARED) {
+		if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE,
+				NL80211_AUTHTYPE_SHARED_KEY))
+			goto fail;
+	} else {
+		if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE,
+				NL80211_AUTHTYPE_OPEN_SYSTEM))
+			goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", params->wpa_version);
+	ver = 0;
+	if (params->wpa_version & WPA_PROTO_WPA)
+		ver |= NL80211_WPA_VERSION_1;
+	if (params->wpa_version & WPA_PROTO_RSN)
+		ver |= NL80211_WPA_VERSION_2;
+	if (ver &&
+	    nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "nl80211: key_mgmt_suites=0x%x",
+		   params->key_mgmt_suites);
+	num_suites = 0;
+	if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X)
+		suites[num_suites++] = WLAN_AKM_SUITE_8021X;
+	if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK)
+		suites[num_suites++] = WLAN_AKM_SUITE_PSK;
+	if (num_suites &&
+	    nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32),
+		    suites))
+		goto fail;
+
+	if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+	    params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40) &&
+	    nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x",
+		   params->pairwise_ciphers);
+	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))
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x",
+		   params->group_cipher);
+	suite = wpa_cipher_to_cipher_suite(params->group_cipher);
+	if (suite &&
+	    nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite))
+		goto fail;
+
+	switch (params->smps_mode) {
+	case HT_CAP_INFO_SMPS_DYNAMIC:
+		wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - dynamic");
+		smps_mode = NL80211_SMPS_DYNAMIC;
+		break;
+	case HT_CAP_INFO_SMPS_STATIC:
+		wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - static");
+		smps_mode = NL80211_SMPS_STATIC;
+		break;
+	default:
+		/* invalid - fallback to smps off */
+	case HT_CAP_INFO_SMPS_DISABLED:
+		wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - off");
+		smps_mode = NL80211_SMPS_OFF;
+		break;
+	}
+	if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode))
+		goto fail;
+
+	if (params->beacon_ies) {
+		wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies",
+				params->beacon_ies);
+		if (nla_put(msg, NL80211_ATTR_IE,
+			    wpabuf_len(params->beacon_ies),
+			    wpabuf_head(params->beacon_ies)))
+			goto fail;
+	}
+	if (params->proberesp_ies) {
+		wpa_hexdump_buf(MSG_DEBUG, "nl80211: proberesp_ies",
+				params->proberesp_ies);
+		if (nla_put(msg, NL80211_ATTR_IE_PROBE_RESP,
+			    wpabuf_len(params->proberesp_ies),
+			    wpabuf_head(params->proberesp_ies)))
+			goto fail;
+	}
+	if (params->assocresp_ies) {
+		wpa_hexdump_buf(MSG_DEBUG, "nl80211: assocresp_ies",
+				params->assocresp_ies);
+		if (nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP,
+			    wpabuf_len(params->assocresp_ies),
+			    wpabuf_head(params->assocresp_ies)))
+			goto fail;
+	}
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)  {
+		wpa_printf(MSG_DEBUG, "nl80211: ap_max_inactivity=%d",
+			   params->ap_max_inactivity);
+		if (nla_put_u16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT,
+				params->ap_max_inactivity))
+			goto fail;
+	}
+
+#ifdef CONFIG_P2P
+	if (params->p2p_go_ctwindow > 0) {
+		if (drv->p2p_go_ctwindow_supported) {
+			wpa_printf(MSG_DEBUG, "nl80211: P2P GO ctwindow=%d",
+				   params->p2p_go_ctwindow);
+			if (nla_put_u8(msg, NL80211_ATTR_P2P_CTWINDOW,
+				       params->p2p_go_ctwindow))
+				goto fail;
+		} else {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Driver does not support CTWindow configuration - ignore this parameter");
+		}
+	}
+#endif /* CONFIG_P2P */
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
+			   ret, strerror(-ret));
+	} else {
+		bss->beacon_set = 1;
+		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;
+fail:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
+static int nl80211_put_freq_params(struct nl_msg *msg,
+				   const struct hostapd_freq_params *freq)
+{
+	wpa_printf(MSG_DEBUG, "  * freq=%d", freq->freq);
+	if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq))
+		return -ENOBUFS;
+
+	wpa_printf(MSG_DEBUG, "  * vht_enabled=%d", freq->vht_enabled);
+	wpa_printf(MSG_DEBUG, "  * ht_enabled=%d", freq->ht_enabled);
+
+	if (freq->vht_enabled) {
+		enum nl80211_chan_width cw;
+
+		wpa_printf(MSG_DEBUG, "  * bandwidth=%d", freq->bandwidth);
+		switch (freq->bandwidth) {
+		case 20:
+			cw = NL80211_CHAN_WIDTH_20;
+			break;
+		case 40:
+			cw = NL80211_CHAN_WIDTH_40;
+			break;
+		case 80:
+			if (freq->center_freq2)
+				cw = NL80211_CHAN_WIDTH_80P80;
+			else
+				cw = NL80211_CHAN_WIDTH_80;
+			break;
+		case 160:
+			cw = NL80211_CHAN_WIDTH_160;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		wpa_printf(MSG_DEBUG, "  * channel_width=%d", cw);
+		wpa_printf(MSG_DEBUG, "  * center_freq1=%d",
+			   freq->center_freq1);
+		wpa_printf(MSG_DEBUG, "  * center_freq2=%d",
+			   freq->center_freq2);
+		if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) ||
+		    nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1,
+				freq->center_freq1) ||
+		    (freq->center_freq2 &&
+		     nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2,
+				 freq->center_freq2)))
+			return -ENOBUFS;
+	} else if (freq->ht_enabled) {
+		enum nl80211_channel_type ct;
+
+		wpa_printf(MSG_DEBUG, "  * sec_channel_offset=%d",
+			   freq->sec_channel_offset);
+		switch (freq->sec_channel_offset) {
+		case -1:
+			ct = NL80211_CHAN_HT40MINUS;
+			break;
+		case 1:
+			ct = NL80211_CHAN_HT40PLUS;
+			break;
+		default:
+			ct = NL80211_CHAN_HT20;
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "  * channel_type=%d", ct);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct))
+			return -ENOBUFS;
+	}
+	return 0;
+}
+
+
+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 = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
+			      NL80211_CMD_SET_WIPHY);
+	if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret == 0) {
+		bss->freq = freq->freq;
+		return 0;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
+		   "%d (%s)", freq->freq, ret, strerror(-ret));
+	return -1;
+}
+
+
+static u32 sta_flags_nl80211(int flags)
+{
+	u32 f = 0;
+
+	if (flags & WPA_STA_AUTHORIZED)
+		f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
+	if (flags & WPA_STA_WMM)
+		f |= BIT(NL80211_STA_FLAG_WME);
+	if (flags & WPA_STA_SHORT_PREAMBLE)
+		f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
+	if (flags & WPA_STA_MFP)
+		f |= BIT(NL80211_STA_FLAG_MFP);
+	if (flags & WPA_STA_TDLS_PEER)
+		f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+	if (flags & WPA_STA_AUTHENTICATED)
+		f |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+
+	return f;
+}
+
+
+#ifdef CONFIG_MESH
+static u32 sta_plink_state_nl80211(enum mesh_plink_state state)
+{
+	switch (state) {
+	case PLINK_LISTEN:
+		return NL80211_PLINK_LISTEN;
+	case PLINK_OPEN_SENT:
+		return NL80211_PLINK_OPN_SNT;
+	case PLINK_OPEN_RCVD:
+		return NL80211_PLINK_OPN_RCVD;
+	case PLINK_CNF_RCVD:
+		return NL80211_PLINK_CNF_RCVD;
+	case PLINK_ESTAB:
+		return NL80211_PLINK_ESTAB;
+	case PLINK_HOLDING:
+		return NL80211_PLINK_HOLDING;
+	case PLINK_BLOCKED:
+		return NL80211_PLINK_BLOCKED;
+	default:
+		wpa_printf(MSG_ERROR, "nl80211: Invalid mesh plink state %d",
+			   state);
+	}
+	return -1;
+}
+#endif /* CONFIG_MESH */
+
+
+static int wpa_driver_nl80211_sta_add(void *priv,
+				      struct hostapd_sta_add_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nl80211_sta_flag_update upd;
+	int ret = -ENOBUFS;
+
+	if ((params->flags & WPA_STA_TDLS_PEER) &&
+	    !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+		return -EOPNOTSUPP;
+
+	wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR,
+		   params->set ? "Set" : "Add", MAC2STR(params->addr));
+	msg = nl80211_bss_msg(bss, 0, params->set ? NL80211_CMD_SET_STATION :
+			      NL80211_CMD_NEW_STATION);
+	if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr))
+		goto fail;
+
+	if (!params->set || (params->flags & WPA_STA_TDLS_PEER)) {
+		wpa_hexdump(MSG_DEBUG, "  * supported rates",
+			    params->supp_rates, params->supp_rates_len);
+		wpa_printf(MSG_DEBUG, "  * capability=0x%x",
+			   params->capability);
+		if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_RATES,
+			    params->supp_rates_len, params->supp_rates) ||
+		    nla_put_u16(msg, NL80211_ATTR_STA_CAPABILITY,
+				params->capability))
+			goto fail;
+
+		if (params->ht_capabilities) {
+			wpa_hexdump(MSG_DEBUG, "  * ht_capabilities",
+				    (u8 *) params->ht_capabilities,
+				    sizeof(*params->ht_capabilities));
+			if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY,
+				    sizeof(*params->ht_capabilities),
+				    params->ht_capabilities))
+				goto fail;
+		}
+
+		if (params->vht_capabilities) {
+			wpa_hexdump(MSG_DEBUG, "  * vht_capabilities",
+				    (u8 *) params->vht_capabilities,
+				    sizeof(*params->vht_capabilities));
+			if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY,
+				    sizeof(*params->vht_capabilities),
+				    params->vht_capabilities))
+				goto fail;
+		}
+
+		if (params->ext_capab) {
+			wpa_hexdump(MSG_DEBUG, "  * ext_capab",
+				    params->ext_capab, params->ext_capab_len);
+			if (nla_put(msg, NL80211_ATTR_STA_EXT_CAPABILITY,
+				    params->ext_capab_len, params->ext_capab))
+				goto fail;
+		}
+	}
+	if (!params->set) {
+		if (params->aid) {
+			wpa_printf(MSG_DEBUG, "  * aid=%u", params->aid);
+			if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid))
+				goto fail;
+		} else {
+			/*
+			 * cfg80211 validates that AID is non-zero, so we have
+			 * to make this a non-zero value for the TDLS case where
+			 * a dummy STA entry is used for now.
+			 */
+			wpa_printf(MSG_DEBUG, "  * aid=1 (TDLS workaround)");
+			if (nla_put_u16(msg, NL80211_ATTR_STA_AID, 1))
+				goto fail;
+		}
+		wpa_printf(MSG_DEBUG, "  * listen_interval=%u",
+			   params->listen_interval);
+		if (nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+				params->listen_interval))
+			goto fail;
+	} else if (params->aid && (params->flags & WPA_STA_TDLS_PEER)) {
+		wpa_printf(MSG_DEBUG, "  * peer_aid=%u", params->aid);
+		if (nla_put_u16(msg, NL80211_ATTR_PEER_AID, params->aid))
+			goto fail;
+	}
+
+	if (params->vht_opmode_enabled) {
+		wpa_printf(MSG_DEBUG, "  * opmode=%u", params->vht_opmode);
+		if (nla_put_u8(msg, NL80211_ATTR_OPMODE_NOTIF,
+			       params->vht_opmode))
+			goto fail;
+	}
+
+	if (params->supp_channels) {
+		wpa_hexdump(MSG_DEBUG, "  * supported channels",
+			    params->supp_channels, params->supp_channels_len);
+		if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+			    params->supp_channels_len, params->supp_channels))
+			goto fail;
+	}
+
+	if (params->supp_oper_classes) {
+		wpa_hexdump(MSG_DEBUG, "  * supported operating classes",
+			    params->supp_oper_classes,
+			    params->supp_oper_classes_len);
+		if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+			    params->supp_oper_classes_len,
+			    params->supp_oper_classes))
+			goto fail;
+	}
+
+	os_memset(&upd, 0, sizeof(upd));
+	upd.set = sta_flags_nl80211(params->flags);
+	upd.mask = upd.set | sta_flags_nl80211(params->flags_mask);
+	wpa_printf(MSG_DEBUG, "  * flags set=0x%x mask=0x%x",
+		   upd.set, upd.mask);
+	if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
+		goto fail;
+
+#ifdef CONFIG_MESH
+	if (params->plink_state &&
+	    nla_put_u8(msg, NL80211_ATTR_STA_PLINK_STATE,
+		       sta_plink_state_nl80211(params->plink_state)))
+		goto fail;
+#endif /* CONFIG_MESH */
+
+	if (params->flags & WPA_STA_WMM) {
+		struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME);
+
+		wpa_printf(MSG_DEBUG, "  * qosinfo=0x%x", params->qosinfo);
+		if (!wme ||
+		    nla_put_u8(msg, NL80211_STA_WME_UAPSD_QUEUES,
+			       params->qosinfo & WMM_QOSINFO_STA_AC_MASK) ||
+		    nla_put_u8(msg, NL80211_STA_WME_MAX_SP,
+			       (params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) &
+			       WMM_QOSINFO_STA_SP_MASK))
+			goto fail;
+		nla_nest_end(msg, wme);
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION "
+			   "result: %d (%s)", params->set ? "SET" : "NEW", ret,
+			   strerror(-ret));
+	if (ret == -EEXIST)
+		ret = 0;
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
+{
+#ifdef CONFIG_LIBNL3_ROUTE
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct rtnl_neigh *rn;
+	struct nl_addr *nl_addr;
+	int err;
+
+	rn = rtnl_neigh_alloc();
+	if (!rn)
+		return;
+
+	rtnl_neigh_set_family(rn, AF_BRIDGE);
+	rtnl_neigh_set_ifindex(rn, bss->ifindex);
+	nl_addr = nl_addr_build(AF_BRIDGE, (void *) addr, ETH_ALEN);
+	if (!nl_addr) {
+		rtnl_neigh_put(rn);
+		return;
+	}
+	rtnl_neigh_set_lladdr(rn, nl_addr);
+
+	err = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
+	if (err < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for "
+			   MACSTR " ifindex=%d failed: %s", MAC2STR(addr),
+			   bss->ifindex, nl_geterror(err));
+	} else {
+		wpa_printf(MSG_DEBUG, "nl80211: deleted bridge FDB entry for "
+			   MACSTR, MAC2STR(addr));
+	}
+
+	nl_addr_put(nl_addr);
+	rtnl_neigh_put(rn);
+#endif /* CONFIG_LIBNL3_ROUTE */
+}
+
+
+static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr,
+					 int deauth, u16 reason_code)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    (deauth == 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE,
+			WLAN_FC_STYPE_DISASSOC)) ||
+	    (deauth == 1 &&
+	     nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE,
+			WLAN_FC_STYPE_DEAUTH)) ||
+	    (reason_code &&
+	     nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR
+		   " --> %d (%s)",
+		   bss->ifname, MAC2STR(addr), ret, strerror(-ret));
+
+	if (drv->rtnl_sk)
+		rtnl_neigh_delete_fdb_entry(bss, addr);
+
+	if (ret == -ENOENT)
+		return 0;
+	return ret;
+}
+
+
+void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, 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 */
+	dl_list_for_each(drv2, &drv->global->interfaces,
+			 struct wpa_driver_nl80211_data, list)
+		del_ifidx(drv2, ifidx);
+
+	msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE);
+	if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+		return;
+	wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
+}
+
+
+static const char * nl80211_iftype_str(enum nl80211_iftype mode)
+{
+	switch (mode) {
+	case NL80211_IFTYPE_ADHOC:
+		return "ADHOC";
+	case NL80211_IFTYPE_STATION:
+		return "STATION";
+	case NL80211_IFTYPE_AP:
+		return "AP";
+	case NL80211_IFTYPE_AP_VLAN:
+		return "AP_VLAN";
+	case NL80211_IFTYPE_WDS:
+		return "WDS";
+	case NL80211_IFTYPE_MONITOR:
+		return "MONITOR";
+	case NL80211_IFTYPE_MESH_POINT:
+		return "MESH_POINT";
+	case NL80211_IFTYPE_P2P_CLIENT:
+		return "P2P_CLIENT";
+	case NL80211_IFTYPE_P2P_GO:
+		return "P2P_GO";
+	case NL80211_IFTYPE_P2P_DEVICE:
+		return "P2P_DEVICE";
+	default:
+		return "unknown";
+	}
+}
+
+
+static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
+				     const char *ifname,
+				     enum nl80211_iftype iftype,
+				     const u8 *addr, int wds,
+				     int (*handler)(struct nl_msg *, void *),
+				     void *arg)
+{
+	struct nl_msg *msg;
+	int ifidx;
+	int ret = -ENOBUFS;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)",
+		   iftype, nl80211_iftype_str(iftype));
+
+	msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_NEW_INTERFACE);
+	if (!msg ||
+	    nla_put_string(msg, NL80211_ATTR_IFNAME, ifname) ||
+	    nla_put_u32(msg, NL80211_ATTR_IFTYPE, iftype))
+		goto fail;
+
+	if (iftype == NL80211_IFTYPE_MONITOR) {
+		struct nlattr *flags;
+
+		flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS);
+		if (!flags ||
+		    nla_put_flag(msg, NL80211_MNTR_FLAG_COOK_FRAMES))
+			goto fail;
+
+		nla_nest_end(msg, flags);
+	} else if (wds) {
+		if (nla_put_u8(msg, NL80211_ATTR_4ADDR, wds))
+			goto fail;
+	}
+
+	/*
+	 * Tell cfg80211 that the interface belongs to the socket that created
+	 * it, and the interface should be deleted when the socket is closed.
+	 */
+	if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
+		goto fail;
+
+	ret = send_and_recv_msgs(drv, msg, handler, arg);
+	msg = NULL;
+	if (ret) {
+	fail:
+		nlmsg_free(msg);
+		wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)",
+			   ifname, ret, strerror(-ret));
+		return ret;
+	}
+
+	if (iftype == NL80211_IFTYPE_P2P_DEVICE)
+		return 0;
+
+	ifidx = if_nametoindex(ifname);
+	wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d",
+		   ifname, ifidx);
+
+	if (ifidx <= 0)
+		return -1;
+
+	/*
+	 * 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)) {
+		nl80211_remove_iface(drv, ifidx);
+		return -1;
+	}
+
+	return ifidx;
+}
+
+
+int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+			 const char *ifname, enum nl80211_iftype iftype,
+			 const u8 *addr, int wds,
+			 int (*handler)(struct nl_msg *, void *),
+			 void *arg, int use_existing)
+{
+	int ret;
+
+	ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds, handler,
+					arg);
+
+	/* 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. */
+		nl80211_remove_iface(drv, if_nametoindex(ifname));
+
+		/* Try to create the interface again */
+		ret = nl80211_create_iface_once(drv, ifname, iftype, addr,
+						wds, handler, arg);
+	}
+
+	if (ret >= 0 && is_p2p_net_interface(iftype)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Interface %s created for P2P - disable 11b rates",
+			   ifname);
+		nl80211_disable_11b_rates(drv, ret, 1);
+	}
+
+	return ret;
+}
+
+
+static int nl80211_setup_ap(struct i802_bss *bss)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d use_monitor=%d",
+		   bss->ifname, drv->device_ap_sme, drv->use_monitor);
+
+	/*
+	 * Disable Probe Request reporting unless we need it in this way for
+	 * devices that include the AP SME, in the other case (unless using
+	 * monitor iface) we'll get it through the nl_mgmt socket instead.
+	 */
+	if (!drv->device_ap_sme)
+		wpa_driver_nl80211_probe_req_report(bss, 0);
+
+	if (!drv->device_ap_sme && !drv->use_monitor)
+		if (nl80211_mgmt_subscribe_ap(bss))
+			return -1;
+
+	if (drv->device_ap_sme && !drv->use_monitor)
+		if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
+			return -1;
+
+	if (!drv->device_ap_sme && drv->use_monitor &&
+	    nl80211_create_monitor_interface(drv) &&
+	    !drv->device_ap_sme)
+		return -1;
+
+	if (drv->device_ap_sme &&
+	    wpa_driver_nl80211_probe_req_report(bss, 1) < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
+			   "Probe Request frame reporting in AP mode");
+		/* Try to survive without this */
+	}
+
+	return 0;
+}
+
+
+static void nl80211_teardown_ap(struct i802_bss *bss)
+{
+	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)
+			nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)");
+	} else if (drv->use_monitor)
+		nl80211_remove_monitor_interface(drv);
+	else
+		nl80211_mgmt_unsubscribe(bss, "AP teardown");
+
+	bss->beacon_set = 0;
+}
+
+
+static int nl80211_send_eapol_data(struct i802_bss *bss,
+				   const u8 *addr, const u8 *data,
+				   size_t data_len)
+{
+	struct sockaddr_ll ll;
+	int ret;
+
+	if (bss->drv->eapol_tx_sock < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: No socket to send EAPOL");
+		return -1;
+	}
+
+	os_memset(&ll, 0, sizeof(ll));
+	ll.sll_family = AF_PACKET;
+	ll.sll_ifindex = bss->ifindex;
+	ll.sll_protocol = htons(ETH_P_PAE);
+	ll.sll_halen = ETH_ALEN;
+	os_memcpy(ll.sll_addr, addr, ETH_ALEN);
+	ret = sendto(bss->drv->eapol_tx_sock, data, data_len, 0,
+		     (struct sockaddr *) &ll, sizeof(ll));
+	if (ret < 0)
+		wpa_printf(MSG_ERROR, "nl80211: EAPOL TX: %s",
+			   strerror(errno));
+
+	return ret;
+}
+
+
+static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+static int wpa_driver_nl80211_hapd_send_eapol(
+	void *priv, const u8 *addr, const u8 *data,
+	size_t data_len, int encrypt, const u8 *own_addr, u32 flags)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ieee80211_hdr *hdr;
+	size_t len;
+	u8 *pos;
+	int res;
+	int qos = flags & WPA_STA_WMM;
+
+	if (drv->device_ap_sme || !drv->use_monitor)
+		return nl80211_send_eapol_data(bss, addr, data, data_len);
+
+	len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
+		data_len;
+	hdr = os_zalloc(len);
+	if (hdr == NULL) {
+		wpa_printf(MSG_INFO, "nl80211: Failed to allocate EAPOL buffer(len=%lu)",
+			   (unsigned long) len);
+		return -1;
+	}
+
+	hdr->frame_control =
+		IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
+	hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
+	if (encrypt)
+		hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+	if (qos) {
+		hdr->frame_control |=
+			host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
+	}
+
+	memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+	memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+	memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+	pos = (u8 *) (hdr + 1);
+
+	if (qos) {
+		/* Set highest priority in QoS header */
+		pos[0] = 7;
+		pos[1] = 0;
+		pos += 2;
+	}
+
+	memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
+	pos += sizeof(rfc1042_header);
+	WPA_PUT_BE16(pos, ETH_P_PAE);
+	pos += 2;
+	memcpy(pos, data, data_len);
+
+	res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0,
+					    0, 0, 0, 0);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - "
+			   "failed: %d (%s)",
+			   (unsigned long) len, errno, strerror(errno));
+	}
+	os_free(hdr);
+
+	return res;
+}
+
+
+static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
+					    unsigned int total_flags,
+					    unsigned int flags_or,
+					    unsigned int flags_and)
+{
+	struct i802_bss *bss = priv;
+	struct nl_msg *msg;
+	struct nlattr *flags;
+	struct nl80211_sta_flag_update upd;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set STA flags - ifname=%s addr=" MACSTR
+		   " total_flags=0x%x flags_or=0x%x flags_and=0x%x authorized=%d",
+		   bss->ifname, MAC2STR(addr), total_flags, flags_or, flags_and,
+		   !!(total_flags & WPA_STA_AUTHORIZED));
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+		goto fail;
+
+	/*
+	 * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This
+	 * can be removed eventually.
+	 */
+	flags = nla_nest_start(msg, NL80211_ATTR_STA_FLAGS);
+	if (!flags ||
+	    ((total_flags & WPA_STA_AUTHORIZED) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_AUTHORIZED)) ||
+	    ((total_flags & WPA_STA_WMM) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_WME)) ||
+	    ((total_flags & WPA_STA_SHORT_PREAMBLE) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_SHORT_PREAMBLE)) ||
+	    ((total_flags & WPA_STA_MFP) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_MFP)) ||
+	    ((total_flags & WPA_STA_TDLS_PEER) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_TDLS_PEER)))
+		goto fail;
+
+	nla_nest_end(msg, flags);
+
+	os_memset(&upd, 0, sizeof(upd));
+	upd.mask = sta_flags_nl80211(flags_or | ~flags_and);
+	upd.set = sta_flags_nl80211(flags_or);
+	if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
+		goto fail;
+
+	return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+fail:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
+static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
+				 struct wpa_driver_associate_params *params)
+{
+	enum nl80211_iftype nlmode, old_mode;
+
+	if (params->p2p) {
+		wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
+			   "group (GO)");
+		nlmode = NL80211_IFTYPE_P2P_GO;
+	} else
+		nlmode = NL80211_IFTYPE_AP;
+
+	old_mode = drv->nlmode;
+	if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode)) {
+		nl80211_remove_monitor_interface(drv);
+		return -1;
+	}
+
+	if (params->freq.freq &&
+	    nl80211_set_channel(drv->first_bss, &params->freq, 0)) {
+		if (old_mode != nlmode)
+			wpa_driver_nl80211_set_mode(drv->first_bss, old_mode);
+		nl80211_remove_monitor_interface(drv);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
+			      int reset_mode)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Leave IBSS request sent successfully");
+	}
+
+	if (reset_mode &&
+	    wpa_driver_nl80211_set_mode(drv->first_bss,
+					NL80211_IFTYPE_STATION)) {
+		wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
+			   "station mode");
+	}
+
+	return ret;
+}
+
+
+static int nl80211_ht_vht_overrides(struct nl_msg *msg,
+				    struct wpa_driver_associate_params *params)
+{
+	if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT))
+		return -1;
+
+	if (params->htcaps && params->htcaps_mask) {
+		int sz = sizeof(struct ieee80211_ht_capabilities);
+		wpa_hexdump(MSG_DEBUG, "  * htcaps", params->htcaps, sz);
+		wpa_hexdump(MSG_DEBUG, "  * htcaps_mask",
+			    params->htcaps_mask, sz);
+		if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz,
+			    params->htcaps) ||
+		    nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+			    params->htcaps_mask))
+			return -1;
+	}
+
+#ifdef CONFIG_VHT_OVERRIDES
+	if (params->disable_vht) {
+		wpa_printf(MSG_DEBUG, "  * VHT disabled");
+		if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT))
+			return -1;
+	}
+
+	if (params->vhtcaps && params->vhtcaps_mask) {
+		int sz = sizeof(struct ieee80211_vht_capabilities);
+		wpa_hexdump(MSG_DEBUG, "  * vhtcaps", params->vhtcaps, sz);
+		wpa_hexdump(MSG_DEBUG, "  * vhtcaps_mask",
+			    params->vhtcaps_mask, sz);
+		if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz,
+			    params->vhtcaps) ||
+		    nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
+			    params->vhtcaps_mask))
+			return -1;
+	}
+#endif /* CONFIG_VHT_OVERRIDES */
+
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
+				   struct wpa_driver_associate_params *params)
+{
+	struct nl_msg *msg;
+	int ret = -1;
+	int count = 0;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
+
+	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;
+	}
+
+retry:
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_IBSS)) ||
+	    params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
+		goto fail;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
+			  params->ssid, params->ssid_len);
+	if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
+		goto fail;
+	os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+	drv->ssid_len = params->ssid_len;
+
+	if (nl80211_put_freq_params(msg, &params->freq) < 0 ||
+	    nl80211_put_beacon_int(msg, params->beacon_int))
+		goto fail;
+
+	ret = nl80211_set_conn_keys(params, msg);
+	if (ret)
+		goto fail;
+
+	if (params->bssid && params->fixed_bssid) {
+		wpa_printf(MSG_DEBUG, "  * BSSID=" MACSTR,
+			   MAC2STR(params->bssid));
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+			goto fail;
+	}
+
+	if (params->fixed_freq) {
+		wpa_printf(MSG_DEBUG, "  * fixed_freq");
+		if (nla_put_flag(msg, NL80211_ATTR_FREQ_FIXED))
+			goto fail;
+	}
+
+	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");
+		if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
+			goto fail;
+	}
+
+	if (params->wpa_ie) {
+		wpa_hexdump(MSG_DEBUG,
+			    "  * Extra IEs for Beacon/Probe Response frames",
+			    params->wpa_ie, params->wpa_ie_len);
+		if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+			    params->wpa_ie))
+			goto fail;
+	}
+
+	if (nl80211_ht_vht_overrides(msg, params) < 0)
+		return -1;
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		count++;
+		if (ret == -EALREADY && count == 1) {
+			wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after "
+				   "forced leave");
+			nl80211_leave_ibss(drv, 0);
+			nlmsg_free(msg);
+			goto retry;
+		}
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Join IBSS request sent successfully");
+	}
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
+				  struct wpa_driver_associate_params *params,
+				  struct nl_msg *msg)
+{
+	if (params->bssid) {
+		wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
+			   MAC2STR(params->bssid));
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+			return -1;
+	}
+
+	if (params->bssid_hint) {
+		wpa_printf(MSG_DEBUG, "  * bssid_hint=" MACSTR,
+			   MAC2STR(params->bssid_hint));
+		if (nla_put(msg, NL80211_ATTR_MAC_HINT, ETH_ALEN,
+			    params->bssid_hint))
+			return -1;
+	}
+
+	if (params->freq.freq) {
+		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq.freq);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+				params->freq.freq))
+			return -1;
+		drv->assoc_freq = params->freq.freq;
+	} else
+		drv->assoc_freq = 0;
+
+	if (params->freq_hint) {
+		wpa_printf(MSG_DEBUG, "  * freq_hint=%d", params->freq_hint);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ_HINT,
+				params->freq_hint))
+			return -1;
+	}
+
+	if (params->bg_scan_period >= 0) {
+		wpa_printf(MSG_DEBUG, "  * bg scan period=%d",
+			   params->bg_scan_period);
+		if (nla_put_u16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
+				params->bg_scan_period))
+			return -1;
+	}
+
+	if (params->ssid) {
+		wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
+				  params->ssid, params->ssid_len);
+		if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
+			    params->ssid))
+			return -1;
+		if (params->ssid_len > sizeof(drv->ssid))
+			return -1;
+		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))
+		return -1;
+
+	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);
+		if (nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
+			return -1;
+	}
+
+	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);
+		if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+				cipher))
+			return -1;
+	}
+
+	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);
+		if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher))
+			return -1;
+	}
+
+	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 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		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_IEEE8021X_SUITE_B:
+			mgmt = WLAN_AKM_SUITE_8021X_SUITE_B;
+			break;
+		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+			mgmt = WLAN_AKM_SUITE_8021X_SUITE_B_192;
+			break;
+		case WPA_KEY_MGMT_PSK:
+		default:
+			mgmt = WLAN_AKM_SUITE_PSK;
+			break;
+		}
+		wpa_printf(MSG_DEBUG, "  * akm=0x%x", mgmt);
+		if (nla_put_u32(msg, NL80211_ATTR_AKM_SUITES, mgmt))
+			return -1;
+	}
+
+	if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
+		return -1;
+
+	if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
+	    nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
+		return -1;
+
+	if (params->rrm_used) {
+		u32 drv_rrm_flags = drv->capa.rrm_flags;
+		if (!(drv_rrm_flags &
+		      WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) ||
+		    !(drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET) ||
+		    nla_put_flag(msg, NL80211_ATTR_USE_RRM))
+			return -1;
+	}
+
+	if (nl80211_ht_vht_overrides(msg, params) < 0)
+		return -1;
+
+	if (params->p2p)
+		wpa_printf(MSG_DEBUG, "  * P2P group");
+
+	return 0;
+}
+
+
+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;
+
+	if (params->req_key_mgmt_offload && params->psk &&
+	    (params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+	     params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+	     params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK");
+		ret = issue_key_mgmt_set_key(drv, params->psk, 32);
+		if (ret)
+			return ret;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT);
+	if (!msg)
+		return -1;
+
+	ret = nl80211_connect_common(drv, params, msg);
+	if (ret)
+		goto fail;
+
+	algs = 0;
+	if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+		algs++;
+	if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+		algs++;
+	if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+		algs++;
+	if (algs > 1) {
+		wpa_printf(MSG_DEBUG, "  * Leave out Auth Type for automatic "
+			   "selection");
+		goto skip_auth_type;
+	}
+
+	if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+		type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+	else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+		type = NL80211_AUTHTYPE_SHARED_KEY;
+	else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+		type = NL80211_AUTHTYPE_NETWORK_EAP;
+	else if (params->auth_alg & WPA_AUTH_ALG_FT)
+		type = NL80211_AUTHTYPE_FT;
+	else
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
+	if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+		goto fail;
+
+skip_auth_type:
+	ret = nl80211_set_conn_keys(params, msg);
+	if (ret)
+		goto fail;
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Connect request send successfully");
+	}
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+
+}
+
+
+static int wpa_driver_nl80211_connect(
+	struct wpa_driver_nl80211_data *drv,
+	struct wpa_driver_associate_params *params)
+{
+	int ret;
+
+	/* Store the connection attempted bssid for future use */
+	if (params->bssid)
+		os_memcpy(drv->auth_attempt_bssid, params->bssid, ETH_ALEN);
+	else
+		os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
+
+	ret = wpa_driver_nl80211_try_connect(drv, params);
+	if (ret == -EALREADY) {
+		/*
+		 * cfg80211 does not currently accept new connections if
+		 * we are already connected. As a workaround, force
+		 * disconnection and try again.
+		 */
+		wpa_printf(MSG_DEBUG, "nl80211: Explicitly "
+			   "disconnecting before reassociation "
+			   "attempt");
+		if (wpa_driver_nl80211_disconnect(
+			    drv, WLAN_REASON_PREV_AUTH_NOT_VALID))
+			return -1;
+		ret = wpa_driver_nl80211_try_connect(drv, params);
+	}
+	return ret;
+}
+
+
+static int wpa_driver_nl80211_associate(
+	void *priv, struct wpa_driver_associate_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret = -1;
+	struct nl_msg *msg;
+
+	nl80211_unmask_11b_rates(bss);
+
+	if (params->mode == IEEE80211_MODE_AP)
+		return wpa_driver_nl80211_ap(drv, params);
+
+	if (params->mode == IEEE80211_MODE_IBSS)
+		return wpa_driver_nl80211_ibss(drv, params);
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+		enum nl80211_iftype nlmode = params->p2p ?
+			NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+
+		if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
+			return -1;
+		return wpa_driver_nl80211_connect(drv, params);
+	}
+
+	nl80211_mark_disconnected(drv);
+
+	wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
+		   drv->ifindex);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE);
+	if (!msg)
+		return -1;
+
+	ret = nl80211_connect_common(drv, params, msg);
+	if (ret)
+		goto fail;
+
+	if (params->prev_bssid) {
+		wpa_printf(MSG_DEBUG, "  * prev_bssid=" MACSTR,
+			   MAC2STR(params->prev_bssid));
+		if (nla_put(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
+			    params->prev_bssid))
+			goto fail;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_dbg(drv->ctx, MSG_DEBUG,
+			"nl80211: MLME command failed (assoc): ret=%d (%s)",
+			ret, strerror(-ret));
+		nl80211_dump_scan(drv);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Association request send successfully");
+	}
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
+			    int ifindex, enum nl80211_iftype mode)
+{
+	struct nl_msg *msg;
+	int ret = -ENOBUFS;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)",
+		   ifindex, mode, nl80211_iftype_str(mode));
+
+	msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE);
+	if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, mode))
+		goto fail;
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (!ret)
+		return 0;
+fail:
+	nlmsg_free(msg);
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
+		   " %d (%s)", ifindex, mode, ret, strerror(-ret));
+	return ret;
+}
+
+
+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;
+
+	mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+	if (mode_switch_res && nlmode == nl80211_get_ifmode(bss))
+		mode_switch_res = 0;
+
+	if (mode_switch_res == 0) {
+		drv->nlmode = nlmode;
+		ret = 0;
+		goto done;
+	}
+
+	if (mode_switch_res == -ENODEV)
+		return -1;
+
+	if (nlmode == drv->nlmode) {
+		wpa_printf(MSG_DEBUG, "nl80211: Interface already in "
+			   "requested mode - ignore error");
+		ret = 0;
+		goto done; /* Already in the requested mode */
+	}
+
+	/* mac80211 doesn't allow mode changes while the device is up, so
+	 * take the device down, try to set the mode again, and bring the
+	 * device back up.
+	 */
+	wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting "
+		   "interface down");
+	for (i = 0; i < 10; i++) {
+		res = i802_set_iface_flags(bss, 0);
+		if (res == -EACCES || res == -ENODEV)
+			break;
+		if (res != 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
+				   "interface down");
+			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 = nl80211_set_channel(bss, desired_freq_params, 0);
+			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) {
+		wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while "
+			   "interface is down");
+		drv->nlmode = nlmode;
+		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 "
+			   "from %d failed", nlmode, drv->nlmode);
+		return ret;
+	}
+
+	if (is_p2p_net_interface(nlmode)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Interface %s mode change to P2P - disable 11b rates",
+			   bss->ifname);
+		nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+	} else if (drv->disabled_11b_rates) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Interface %s mode changed to non-P2P - re-enable 11b rates",
+			   bss->ifname);
+		nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+	}
+
+	if (is_ap_interface(nlmode)) {
+		nl80211_mgmt_unsubscribe(bss, "start AP");
+		/* Setup additional AP mode functionality if needed */
+		if (nl80211_setup_ap(bss))
+			return -1;
+	} else if (was_ap) {
+		/* Remove additional AP mode functionality */
+		nl80211_teardown_ap(bss);
+	} else {
+		nl80211_mgmt_unsubscribe(bss, "mode change");
+	}
+
+	if (is_mesh_interface(nlmode) &&
+	    nl80211_mgmt_subscribe_mesh(bss))
+		return -1;
+
+	if (!bss->in_deinit && !is_ap_interface(nlmode) &&
+	    !is_mesh_interface(nlmode) &&
+	    nl80211_mgmt_subscribe_non_ap(bss) < 0)
+		wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
+			   "frame processing - ignore for now");
+
+	return 0;
+}
+
+
+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,
+					    struct hostapd_freq_params *freq)
+{
+	return wpa_driver_nl80211_set_mode_impl(bss, NL80211_IFTYPE_ADHOC,
+						freq);
+}
+
+
+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;
+
+	if (!drv->has_capability)
+		return -1;
+	os_memcpy(capa, &drv->capa, sizeof(*capa));
+	if (drv->extended_capa && drv->extended_capa_mask) {
+		capa->extended_capa = drv->extended_capa;
+		capa->extended_capa_mask = drv->extended_capa_mask;
+		capa->extended_capa_len = drv->extended_capa_len;
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_set_operstate(void *priv, int state)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	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);
+}
+
+
+static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nl80211_sta_flag_update upd;
+	int ret;
+
+	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));
+
+	os_memset(&upd, 0, sizeof(upd));
+	upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED);
+	if (authorized)
+		upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid) ||
+	    nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (!ret)
+		return 0;
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)",
+		   ret, strerror(-ret));
+	return ret;
+}
+
+
+/* Set kernel driver on given frequency (MHz) */
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+	struct i802_bss *bss = priv;
+	return nl80211_set_channel(bss, freq, 0);
+}
+
+
+static inline int min_int(int a, int b)
+{
+	if (a < b)
+		return a;
+	return b;
+}
+
+
+static int get_key_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	/*
+	 * TODO: validate the key index and mac address!
+	 * Otherwise, there's a race condition as soon as
+	 * the kernel starts sending key notifications.
+	 */
+
+	if (tb[NL80211_ATTR_KEY_SEQ])
+		memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]),
+		       min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6));
+	return NL_SKIP;
+}
+
+
+static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
+			   int idx, u8 *seq)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+
+	msg = nl80211_ifindex_msg(drv, if_nametoindex(iface), 0,
+				  NL80211_CMD_GET_KEY);
+	if (!msg ||
+	    (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
+	    nla_put_u8(msg, NL80211_ATTR_KEY_IDX, idx)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	memset(seq, 0, 6);
+
+	return send_and_recv_msgs(drv, msg, get_key_handler, seq);
+}
+
+
+static int i802_set_rts(void *priv, int rts)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	u32 val;
+
+	if (rts >= 2347)
+		val = (u32) -1;
+	else
+		val = rts;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+	    nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (!ret)
+		return 0;
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
+		   "%d (%s)", rts, ret, strerror(-ret));
+	return ret;
+}
+
+
+static int i802_set_frag(void *priv, int frag)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	u32 val;
+
+	if (frag >= 2346)
+		val = (u32) -1;
+	else
+		val = frag;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+	    nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (!ret)
+		return 0;
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
+		   "%d: %d (%s)", frag, ret, strerror(-ret));
+	return ret;
+}
+
+
+static int i802_flush(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct nl_msg *msg;
+	int res;
+
+	wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
+		   bss->ifname);
+
+	/*
+	 * XXX: FIX! this needs to flush all VLANs too
+	 */
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
+	res = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+	if (res) {
+		wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
+			   "(%s)", res, strerror(-res));
+	}
+	return res;
+}
+
+
+static int get_sta_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct hostap_sta_driver_data *data = arg;
+	struct nlattr *stats[NL80211_STA_INFO_MAX + 1];
+	static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
+		[NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
+	};
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	/*
+	 * TODO: validate the interface and mac address!
+	 * Otherwise, there's a race condition as soon as
+	 * the kernel starts sending station notifications.
+	 */
+
+	if (!tb[NL80211_ATTR_STA_INFO]) {
+		wpa_printf(MSG_DEBUG, "sta stats missing!");
+		return NL_SKIP;
+	}
+	if (nla_parse_nested(stats, NL80211_STA_INFO_MAX,
+			     tb[NL80211_ATTR_STA_INFO],
+			     stats_policy)) {
+		wpa_printf(MSG_DEBUG, "failed to parse nested attributes!");
+		return NL_SKIP;
+	}
+
+	if (stats[NL80211_STA_INFO_INACTIVE_TIME])
+		data->inactive_msec =
+			nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]);
+	if (stats[NL80211_STA_INFO_RX_BYTES])
+		data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]);
+	if (stats[NL80211_STA_INFO_TX_BYTES])
+		data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]);
+	if (stats[NL80211_STA_INFO_RX_PACKETS])
+		data->rx_packets =
+			nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]);
+	if (stats[NL80211_STA_INFO_TX_PACKETS])
+		data->tx_packets =
+			nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]);
+	if (stats[NL80211_STA_INFO_TX_FAILED])
+		data->tx_retry_failed =
+			nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
+
+	return NL_SKIP;
+}
+
+static int i802_read_sta_data(struct i802_bss *bss,
+			      struct hostap_sta_driver_data *data,
+			      const u8 *addr)
+{
+	struct nl_msg *msg;
+
+	os_memset(data, 0, sizeof(*data));
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	return send_and_recv_msgs(bss->drv, msg, get_sta_handler, data);
+}
+
+
+static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
+				    int cw_min, int cw_max, int burst_time)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *txq, *params;
+
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY);
+	if (!msg)
+		return -1;
+
+	txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS);
+	if (!txq)
+		goto fail;
+
+	/* We are only sending parameters for a single TXQ at a time */
+	params = nla_nest_start(msg, 1);
+	if (!params)
+		goto fail;
+
+	switch (queue) {
+	case 0:
+		if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO))
+			goto fail;
+		break;
+	case 1:
+		if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI))
+			goto fail;
+		break;
+	case 2:
+		if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE))
+			goto fail;
+		break;
+	case 3:
+		if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK))
+			goto fail;
+		break;
+	}
+	/* Burst time is configured in units of 0.1 msec and TXOP parameter in
+	 * 32 usec, so need to convert the value here. */
+	if (nla_put_u16(msg, NL80211_TXQ_ATTR_TXOP,
+			(burst_time * 100 + 16) / 32) ||
+	    nla_put_u16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min) ||
+	    nla_put_u16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max) ||
+	    nla_put_u8(msg, NL80211_TXQ_ATTR_AIFS, aifs))
+		goto fail;
+
+	nla_nest_end(msg, params);
+
+	nla_nest_end(msg, txq);
+
+	if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+		return 0;
+	msg = NULL;
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
+
+static int i802_set_sta_vlan(struct i802_bss *bss, const u8 *addr,
+			     const char *ifname, int vlan_id)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	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);
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    nla_put_u32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
+			   MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
+			   MAC2STR(addr), ifname, vlan_id, ret,
+			   strerror(-ret));
+	}
+	return ret;
+}
+
+
+static int i802_get_inact_sec(void *priv, const u8 *addr)
+{
+	struct hostap_sta_driver_data data;
+	int ret;
+
+	data.inactive_msec = (unsigned long) -1;
+	ret = i802_read_sta_data(priv, &data, addr);
+	if (ret == -ENOENT)
+		return -ENOENT;
+	if (ret || data.inactive_msec == (unsigned long) -1)
+		return -1;
+	return data.inactive_msec / 1000;
+}
+
+
+static int i802_sta_clear_stats(void *priv, const u8 *addr)
+{
+#if 0
+	/* TODO */
+#endif
+	return 0;
+}
+
+
+static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+			   int reason)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ieee80211_mgmt mgmt;
+
+	if (is_mesh_interface(drv->nlmode))
+		return -1;
+
+	if (drv->device_ap_sme)
+		return wpa_driver_nl80211_sta_remove(bss, addr, 1, reason);
+
+	memset(&mgmt, 0, sizeof(mgmt));
+	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					  WLAN_FC_STYPE_DEAUTH);
+	memcpy(mgmt.da, addr, ETH_ALEN);
+	memcpy(mgmt.sa, own_addr, ETH_ALEN);
+	memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+	mgmt.u.deauth.reason_code = host_to_le16(reason);
+	return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
+					    IEEE80211_HDRLEN +
+					    sizeof(mgmt.u.deauth), 0, 0, 0, 0,
+					    0);
+}
+
+
+static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+			     int reason)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ieee80211_mgmt mgmt;
+
+	if (is_mesh_interface(drv->nlmode))
+		return -1;
+
+	if (drv->device_ap_sme)
+		return wpa_driver_nl80211_sta_remove(bss, addr, 0, reason);
+
+	memset(&mgmt, 0, sizeof(mgmt));
+	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					  WLAN_FC_STYPE_DISASSOC);
+	memcpy(mgmt.da, addr, ETH_ALEN);
+	memcpy(mgmt.sa, own_addr, ETH_ALEN);
+	memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+	mgmt.u.disassoc.reason_code = host_to_le16(reason);
+	return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
+					    IEEE80211_HDRLEN +
+					    sizeof(mgmt.u.disassoc), 0, 0, 0, 0,
+					    0);
+}
+
+
+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 (os_snprintf_error(end - pos, res))
+			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)
+{
+	int i;
+	int *old;
+
+	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;
+		}
+	}
+
+	if (drv->if_indices != drv->default_if_indices)
+		old = drv->if_indices;
+	else
+		old = NULL;
+
+	drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1,
+					   sizeof(int));
+	if (!drv->if_indices) {
+		if (!old)
+			drv->if_indices = drv->default_if_indices;
+		else
+			drv->if_indices = old;
+		wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
+			   "interfaces");
+		wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
+		return;
+	} else if (!old)
+		os_memcpy(drv->if_indices, drv->default_if_indices,
+			  sizeof(drv->default_if_indices));
+	drv->if_indices[drv->num_if_indices] = ifidx;
+	drv->num_if_indices++;
+	dump_ifidx(drv);
+}
+
+
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+	int i;
+
+	for (i = 0; i < drv->num_if_indices; i++) {
+		if (drv->if_indices[i] == ifidx) {
+			drv->if_indices[i] = 0;
+			break;
+		}
+	}
+	dump_ifidx(drv);
+}
+
+
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+	int i;
+
+	for (i = 0; i < drv->num_if_indices; i++)
+		if (drv->if_indices[i] == ifidx)
+			return 1;
+
+	return 0;
+}
+
+
+static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
+			    const char *bridge_ifname, char *ifname_wds)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	char name[IFNAMSIZ + 1];
+
+	os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+	if (ifname_wds)
+		os_strlcpy(ifname_wds, name, IFNAMSIZ + 1);
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+		   " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+	if (val) {
+		if (!if_nametoindex(name)) {
+			if (nl80211_create_iface(drv, name,
+						 NL80211_IFTYPE_AP_VLAN,
+						 bss->addr, 1, NULL, NULL, 0) <
+			    0)
+				return -1;
+			if (bridge_ifname &&
+			    linux_br_add_if(drv->global->ioctl_sock,
+					    bridge_ifname, name) < 0)
+				return -1;
+		}
+		if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA "
+				   "interface %s up", name);
+		}
+		return i802_set_sta_vlan(priv, addr, name, 0);
+	} else {
+		if (bridge_ifname)
+			linux_br_del_if(drv->global->ioctl_sock, bridge_ifname,
+					name);
+
+		i802_set_sta_vlan(priv, addr, bss->ifname, 0);
+		nl80211_remove_iface(drv, if_nametoindex(name));
+		return 0;
+	}
+}
+
+
+static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_driver_nl80211_data *drv = eloop_ctx;
+	struct sockaddr_ll lladdr;
+	unsigned char buf[3000];
+	int len;
+	socklen_t fromlen = sizeof(lladdr);
+
+	len = recvfrom(sock, buf, sizeof(buf), 0,
+		       (struct sockaddr *)&lladdr, &fromlen);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: EAPOL recv failed: %s",
+			   strerror(errno));
+		return;
+	}
+
+	if (have_ifidx(drv, lladdr.sll_ifindex))
+		drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+}
+
+
+static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
+			     struct i802_bss *bss,
+			     const char *brname, const char *ifname)
+{
+	int br_ifindex;
+	char in_br[IFNAMSIZ];
+
+	os_strlcpy(bss->brname, brname, IFNAMSIZ);
+	br_ifindex = if_nametoindex(brname);
+	if (br_ifindex == 0) {
+		/*
+		 * Bridge was configured, but the bridge device does
+		 * not exist. Try to add it now.
+		 */
+		if (linux_br_add(drv->global->ioctl_sock, brname) < 0) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to add the "
+				   "bridge interface %s: %s",
+				   brname, strerror(errno));
+			return -1;
+		}
+		bss->added_bridge = 1;
+		br_ifindex = if_nametoindex(brname);
+		add_ifidx(drv, br_ifindex);
+	}
+	bss->br_ifindex = br_ifindex;
+
+	if (linux_br_get(in_br, ifname) == 0) {
+		if (os_strcmp(in_br, brname) == 0)
+			return 0; /* already in the bridge */
+
+		wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from "
+			   "bridge %s", ifname, in_br);
+		if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) <
+		    0) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to "
+				   "remove interface %s from bridge "
+				   "%s: %s",
+				   ifname, brname, strerror(errno));
+			return -1;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
+		   ifname, brname);
+	if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s "
+			   "into bridge %s: %s",
+			   ifname, brname, strerror(errno));
+		return -1;
+	}
+	bss->added_if_into_bridge = 1;
+
+	return 0;
+}
+
+
+static void *i802_init(struct hostapd_data *hapd,
+		       struct wpa_init_params *params)
+{
+	struct wpa_driver_nl80211_data *drv;
+	struct i802_bss *bss;
+	size_t i;
+	char master_ifname[IFNAMSIZ];
+	int ifindex, br_ifindex = 0;
+	int br_added = 0;
+
+	bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
+					  params->global_priv, 1,
+					  params->bssid, params->driver_params);
+	if (bss == NULL)
+		return NULL;
+
+	drv = bss->drv;
+
+	if (linux_br_get(master_ifname, params->ifname) == 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
+			   params->ifname, master_ifname);
+		br_ifindex = if_nametoindex(master_ifname);
+		os_strlcpy(bss->brname, master_ifname, IFNAMSIZ);
+	} else if ((params->num_bridge == 0 || !params->bridge[0]) &&
+		   linux_master_get(master_ifname, params->ifname) == 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in master %s",
+			params->ifname, master_ifname);
+		/* start listening for EAPOL on the master interface */
+		add_ifidx(drv, if_nametoindex(master_ifname));
+	} else {
+		master_ifname[0] = '\0';
+	}
+
+	bss->br_ifindex = br_ifindex;
+
+	for (i = 0; i < params->num_bridge; i++) {
+		if (params->bridge[i]) {
+			ifindex = if_nametoindex(params->bridge[i]);
+			if (ifindex)
+				add_ifidx(drv, ifindex);
+			if (ifindex == br_ifindex)
+				br_added = 1;
+		}
+	}
+
+	/* start listening for EAPOL on the default AP interface */
+	add_ifidx(drv, drv->ifindex);
+
+	if (params->num_bridge && params->bridge[0]) {
+		if (i802_check_bridge(drv, bss, params->bridge[0],
+				      params->ifname) < 0)
+			goto failed;
+		if (os_strcmp(params->bridge[0], master_ifname) != 0)
+			br_added = 1;
+	}
+
+	if (!br_added && br_ifindex &&
+	    (params->num_bridge == 0 || !params->bridge[0]))
+		add_ifidx(drv, br_ifindex);
+
+#ifdef CONFIG_LIBNL3_ROUTE
+	if (bss->added_if_into_bridge) {
+		drv->rtnl_sk = nl_socket_alloc();
+		if (drv->rtnl_sk == NULL) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
+			goto failed;
+		}
+
+		if (nl_connect(drv->rtnl_sk, NETLINK_ROUTE)) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
+				   strerror(errno));
+			goto failed;
+		}
+	}
+#endif /* CONFIG_LIBNL3_ROUTE */
+
+	drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
+	if (drv->eapol_sock < 0) {
+		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))
+	{
+		wpa_printf(MSG_INFO, "nl80211: Could not register read socket for eapol");
+		goto failed;
+	}
+
+	if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+			       params->own_addr))
+		goto failed;
+	os_memcpy(drv->perm_addr, params->own_addr, ETH_ALEN);
+
+	memcpy(bss->addr, params->own_addr, ETH_ALEN);
+
+	return bss;
+
+failed:
+	wpa_driver_nl80211_deinit(bss);
+	return NULL;
+}
+
+
+static void i802_deinit(void *priv)
+{
+	struct i802_bss *bss = priv;
+	wpa_driver_nl80211_deinit(bss);
+}
+
+
+static enum nl80211_iftype wpa_driver_nl80211_if_type(
+	enum wpa_driver_if_type type)
+{
+	switch (type) {
+	case WPA_IF_STATION:
+		return NL80211_IFTYPE_STATION;
+	case WPA_IF_P2P_CLIENT:
+	case WPA_IF_P2P_GROUP:
+		return NL80211_IFTYPE_P2P_CLIENT;
+	case WPA_IF_AP_VLAN:
+		return NL80211_IFTYPE_AP_VLAN;
+	case WPA_IF_AP_BSS:
+		return NL80211_IFTYPE_AP;
+	case WPA_IF_P2P_GO:
+		return NL80211_IFTYPE_P2P_GO;
+	case WPA_IF_P2P_DEVICE:
+		return NL80211_IFTYPE_P2P_DEVICE;
+	case WPA_IF_MESH:
+		return NL80211_IFTYPE_MESH_POINT;
+	default:
+		return -1;
+	}
+}
+
+
+static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
+{
+	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)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr)
+{
+	unsigned int idx;
+
+	if (!drv->global)
+		return -1;
+
+	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] ^= idx << 2;
+		if (!nl80211_addr_in_use(drv->global, new_addr))
+			break;
+	}
+	if (idx == 64)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Assigned new virtual interface address "
+		   MACSTR, MAC2STR(new_addr));
+
+	return 0;
+}
+
+
+struct wdev_info {
+	u64 wdev_id;
+	int wdev_id_set;
+	u8 macaddr[ETH_ALEN];
+};
+
+static int nl80211_wdev_handler(struct nl_msg *msg, void *arg)
+{
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct wdev_info *wi = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (tb[NL80211_ATTR_WDEV]) {
+		wi->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
+		wi->wdev_id_set = 1;
+	}
+
+	if (tb[NL80211_ATTR_MAC])
+		os_memcpy(wi->macaddr, nla_data(tb[NL80211_ATTR_MAC]),
+			  ETH_ALEN);
+
+	return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_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, int use_existing)
+{
+	enum nl80211_iftype nlmode;
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ifidx;
+	int added = 1;
+
+	if (addr)
+		os_memcpy(if_addr, addr, ETH_ALEN);
+	nlmode = wpa_driver_nl80211_if_type(type);
+	if (nlmode == NL80211_IFTYPE_P2P_DEVICE) {
+		struct wdev_info p2pdev_info;
+
+		os_memset(&p2pdev_info, 0, sizeof(p2pdev_info));
+		ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
+					     0, nl80211_wdev_handler,
+					     &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);
+			return -1;
+		}
+
+		drv->global->if_add_wdevid = p2pdev_info.wdev_id;
+		drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set;
+		if (!is_zero_ether_addr(p2pdev_info.macaddr))
+			os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN);
+		wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created",
+			   ifname,
+			   (long long unsigned int) p2pdev_info.wdev_id);
+	} else {
+		ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
+					     0, NULL, NULL, use_existing);
+		if (use_existing && ifidx == -ENFILE) {
+			added = 0;
+			ifidx = if_nametoindex(ifname);
+		} else if (ifidx < 0) {
+			return -1;
+		}
+	}
+
+	if (!addr) {
+		if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+			os_memcpy(if_addr, bss->addr, ETH_ALEN);
+		else if (linux_get_ifhwaddr(drv->global->ioctl_sock,
+					    ifname, if_addr) < 0) {
+			if (added)
+				nl80211_remove_iface(drv, ifidx);
+			return -1;
+		}
+	}
+
+	if (!addr &&
+	    (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
+	     type == WPA_IF_P2P_GO || type == WPA_IF_MESH ||
+	     type == WPA_IF_STATION)) {
+		/* Enforce unique address */
+		u8 new_addr[ETH_ALEN];
+
+		if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
+				       new_addr) < 0) {
+			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 interface %s type %d", ifname, type);
+			if (nl80211_vif_addr(drv, new_addr) < 0) {
+				if (added)
+					nl80211_remove_iface(drv, ifidx);
+				return -1;
+			}
+			if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+					       new_addr) < 0) {
+				if (added)
+					nl80211_remove_iface(drv, ifidx);
+				return -1;
+			}
+		}
+		os_memcpy(if_addr, new_addr, ETH_ALEN);
+	}
+
+	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))
+		{
+			if (added)
+				nl80211_remove_iface(drv, ifidx);
+			os_free(new_bss);
+			return -1;
+		}
+		os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ);
+		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->ctx = bss_ctx;
+		new_bss->added_if = added;
+		drv->first_bss->next = new_bss;
+		if (drv_priv)
+			*drv_priv = new_bss;
+		nl80211_init_bss(new_bss);
+
+		/* Subscribe management frames for this WPA_IF_AP_BSS */
+		if (nl80211_setup_ap(new_bss))
+			return -1;
+	}
+
+	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;
+}
+
+
+static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
+					enum wpa_driver_if_type type,
+					const char *ifname)
+{
+	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 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);
+	}
+
+	if (type != WPA_IF_AP_BSS)
+		return 0;
+
+	if (bss->added_if_into_bridge) {
+		if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+				    bss->ifname) < 0)
+			wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+				   "interface %s from bridge %s: %s",
+				   bss->ifname, bss->brname, strerror(errno));
+	}
+	if (bss->added_bridge) {
+		if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
+			wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+				   "bridge %s: %s",
+				   bss->brname, strerror(errno));
+	}
+
+	if (bss != drv->first_bss) {
+		struct i802_bss *tbss;
+
+		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;
+			}
+		}
+		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");
+		}
+	}
+
+	return 0;
+}
+
+
+static int cookie_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	u64 *cookie = arg;
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (tb[NL80211_ATTR_COOKIE])
+		*cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+	return NL_SKIP;
+}
+
+
+static int nl80211_send_frame_cmd(struct i802_bss *bss,
+				  unsigned int freq, unsigned int wait,
+				  const u8 *buf, size_t buf_len,
+				  u64 *cookie_out, int no_cck, int no_ack,
+				  int offchanok)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	u64 cookie;
+	int ret = -1;
+
+	wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d "
+		   "no_ack=%d offchanok=%d",
+		   freq, wait, no_cck, no_ack, offchanok);
+	wpa_hexdump(MSG_MSGDUMP, "CMD_FRAME", buf, buf_len);
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME)) ||
+	    (freq && nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
+	    (wait && nla_put_u32(msg, NL80211_ATTR_DURATION, wait)) ||
+	    (offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
+			   drv->test_use_roc_tx) &&
+	     nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) ||
+	    (no_cck && nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) ||
+	    (no_ack && nla_put_flag(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK)) ||
+	    nla_put(msg, NL80211_ATTR_FRAME, buf_len, buf))
+		goto fail;
+
+	cookie = 0;
+	ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
+			   "(%s) (freq=%u wait=%u)", ret, strerror(-ret),
+			   freq, wait);
+	} else {
+		wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; "
+			   "cookie 0x%llx", no_ack ? " (no ACK)" : "",
+			   (long long unsigned int) cookie);
+
+		if (cookie_out)
+			*cookie_out = no_ack ? (u64) -1 : cookie;
+	}
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int wpa_driver_nl80211_send_action(struct i802_bss *bss,
+					  unsigned int freq,
+					  unsigned int wait_time,
+					  const u8 *dst, const u8 *src,
+					  const u8 *bssid,
+					  const u8 *data, size_t data_len,
+					  int no_cck)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret = -1;
+	u8 *buf;
+	struct ieee80211_hdr *hdr;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
+		   "freq=%u MHz wait=%d ms no_cck=%d)",
+		   drv->ifindex, freq, wait_time, no_cck);
+
+	buf = os_zalloc(24 + data_len);
+	if (buf == NULL)
+		return ret;
+	os_memcpy(buf + 24, data, data_len);
+	hdr = (struct ieee80211_hdr *) buf;
+	hdr->frame_control =
+		IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION);
+	os_memcpy(hdr->addr1, dst, ETH_ALEN);
+	os_memcpy(hdr->addr2, src, ETH_ALEN);
+	os_memcpy(hdr->addr3, bssid, ETH_ALEN);
+
+	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);
+	else
+		ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
+					     24 + data_len,
+					     &drv->send_action_cookie,
+					     no_cck, 0, 1);
+
+	os_free(buf);
+	return ret;
+}
+
+
+static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx",
+		   (long long unsigned int) drv->send_action_cookie);
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) ||
+	    nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie)) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+}
+
+
+static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
+						unsigned int duration)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	u64 cookie;
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REMAIN_ON_CHANNEL)) ||
+	    nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
+	    nla_put_u32(msg, NL80211_ATTR_DURATION, duration)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	cookie = 0;
+	ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+	if (ret == 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
+			   "0x%llx for freq=%u MHz duration=%u",
+			   (long long unsigned int) cookie, freq, duration);
+		drv->remain_on_chan_cookie = cookie;
+		drv->pending_remain_on_chan = 1;
+		return 0;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
+		   "(freq=%d duration=%u): %d (%s)",
+		   freq, duration, ret, strerror(-ret));
+	return -1;
+}
+
+
+static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	if (!drv->pending_remain_on_chan) {
+		wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel "
+			   "to cancel");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie "
+		   "0x%llx",
+		   (long long unsigned int) drv->remain_on_chan_cookie);
+
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
+	if (!msg ||
+	    nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret == 0)
+		return 0;
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
+		   "%d (%s)", ret, strerror(-ret));
+	return -1;
+}
+
+
+static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (!report) {
+		if (bss->nl_preq && drv->device_ap_sme &&
+		    is_ap_interface(drv->nlmode) && !bss->in_deinit &&
+		    !bss->static_ap) {
+			/*
+			 * Do not disable Probe Request reporting that was
+			 * enabled in nl80211_setup_ap().
+			 */
+			wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of "
+				   "Probe Request reporting nl_preq=%p while "
+				   "in AP mode", bss->nl_preq);
+		} else if (bss->nl_preq) {
+			wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
+				   "reporting nl_preq=%p", bss->nl_preq);
+			nl80211_destroy_eloop_handle(&bss->nl_preq);
+		}
+		return 0;
+	}
+
+	if (bss->nl_preq) {
+		wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
+			   "already on! nl_preq=%p", bss->nl_preq);
+		return 0;
+	}
+
+	bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq");
+	if (bss->nl_preq == NULL)
+		return -1;
+	wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request "
+		   "reporting nl_preq=%p", bss->nl_preq);
+
+	if (nl80211_register_frame(bss, bss->nl_preq,
+				   (WLAN_FC_TYPE_MGMT << 2) |
+				   (WLAN_FC_STYPE_PROBE_REQ << 4),
+				   NULL, 0) < 0)
+		goto out_err;
+
+	nl80211_register_eloop_read(&bss->nl_preq,
+				    wpa_driver_nl80211_event_receive,
+				    bss->nl_cb);
+
+	return 0;
+
+ out_err:
+	nl_destroy_handles(&bss->nl_preq);
+	return -1;
+}
+
+
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+				     int ifindex, int disabled)
+{
+	struct nl_msg *msg;
+	struct nlattr *bands, *band;
+	int ret;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: NL80211_CMD_SET_TX_BITRATE_MASK (ifindex=%d %s)",
+		   ifindex, disabled ? "NL80211_TXRATE_LEGACY=OFDM-only" :
+		   "no NL80211_TXRATE_LEGACY constraint");
+
+	msg = nl80211_ifindex_msg(drv, ifindex, 0,
+				  NL80211_CMD_SET_TX_BITRATE_MASK);
+	if (!msg)
+		return -1;
+
+	bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+	if (!bands)
+		goto fail;
+
+	/*
+	 * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
+	 * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS
+	 * rates. All 5 GHz rates are left enabled.
+	 */
+	band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+	if (!band ||
+	    (disabled && nla_put(msg, NL80211_TXRATE_LEGACY, 8,
+				 "\x0c\x12\x18\x24\x30\x48\x60\x6c")))
+		goto fail;
+	nla_nest_end(msg, band);
+
+	nla_nest_end(msg, bands);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+	} else
+		drv->disabled_11b_rates = disabled;
+
+	return ret;
+
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
+
+static int wpa_driver_nl80211_deinit_ap(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	if (!is_ap_interface(drv->nlmode))
+		return -1;
+	wpa_driver_nl80211_del_beacon(drv);
+	bss->beacon_set = 0;
+
+	/*
+	 * If the P2P GO interface was dynamically added, then it is
+	 * possible that the interface change to station is not possible.
+	 */
+	if (drv->nlmode == NL80211_IFTYPE_P2P_GO && bss->if_dynamic)
+		return 0;
+
+	return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+}
+
+
+static int wpa_driver_nl80211_stop_ap(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	if (!is_ap_interface(drv->nlmode))
+		return -1;
+	wpa_driver_nl80211_del_beacon(drv);
+	bss->beacon_set = 0;
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_deinit_p2p_cli(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
+		return -1;
+
+	/*
+	 * If the P2P Client interface was dynamically added, then it is
+	 * possible that the interface change to station is not possible.
+	 */
+	if (bss->if_dynamic)
+		return 0;
+
+	return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+}
+
+
+static void wpa_driver_nl80211_resume(void *priv)
+{
+	struct i802_bss *bss = priv;
+
+	if (i802_set_iface_flags(bss, 1))
+		wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event");
+}
+
+
+static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *cqm;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
+		   "hysteresis=%d", threshold, hysteresis);
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_CQM)) ||
+	    !(cqm = nla_nest_start(msg, NL80211_ATTR_CQM)) ||
+	    nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold) ||
+	    nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_nest_end(msg, cqm);
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+
+static int get_channel_width(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wpa_signal_info *sig_change = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	sig_change->center_frq1 = -1;
+	sig_change->center_frq2 = -1;
+	sig_change->chanwidth = CHAN_WIDTH_UNKNOWN;
+
+	if (tb[NL80211_ATTR_CHANNEL_WIDTH]) {
+		sig_change->chanwidth = convert2width(
+			nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]));
+		if (tb[NL80211_ATTR_CENTER_FREQ1])
+			sig_change->center_frq1 =
+				nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+		if (tb[NL80211_ATTR_CENTER_FREQ2])
+			sig_change->center_frq2 =
+				nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv,
+				     struct wpa_signal_info *sig)
+{
+	struct nl_msg *msg;
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+	return send_and_recv_msgs(drv, msg, get_channel_width, sig);
+}
+
+
+static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int res;
+
+	os_memset(si, 0, sizeof(*si));
+	res = nl80211_get_link_signal(drv, si);
+	if (res != 0)
+		return res;
+
+	res = nl80211_get_channel_width(drv, si);
+	if (res != 0)
+		return res;
+
+	return nl80211_get_link_noise(drv, si);
+}
+
+
+static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
+			      int encrypt)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0,
+					     0, 0, 0, 0);
+}
+
+
+static int nl80211_set_param(void *priv, const char *param)
+{
+	wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
+	if (param == NULL)
+		return 0;
+
+#ifdef CONFIG_P2P
+	if (os_strstr(param, "use_p2p_group_interface=1")) {
+		struct i802_bss *bss = priv;
+		struct wpa_driver_nl80211_data *drv = bss->drv;
+
+		wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+			   "interface");
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+	}
+#endif /* CONFIG_P2P */
+
+	if (os_strstr(param, "use_monitor=1")) {
+		struct i802_bss *bss = priv;
+		struct wpa_driver_nl80211_data *drv = bss->drv;
+		drv->use_monitor = 1;
+	}
+
+	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;
+		drv->force_connect_cmd = 1;
+	}
+
+	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;
+}
+
+
+static void * nl80211_global_init(void)
+{
+	struct nl80211_global *global;
+	struct netlink_config *cfg;
+
+	global = os_zalloc(sizeof(*global));
+	if (global == NULL)
+		return NULL;
+	global->ioctl_sock = -1;
+	dl_list_init(&global->interfaces);
+	global->if_add_ifindex = -1;
+
+	cfg = os_zalloc(sizeof(*cfg));
+	if (cfg == NULL)
+		goto err;
+
+	cfg->ctx = global;
+	cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
+	cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
+	global->netlink = netlink_init(cfg);
+	if (global->netlink == NULL) {
+		os_free(cfg);
+		goto err;
+	}
+
+	if (wpa_driver_nl80211_init_nl_global(global) < 0)
+		goto err;
+
+	global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (global->ioctl_sock < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s",
+			   strerror(errno));
+		goto err;
+	}
+
+	return global;
+
+err:
+	nl80211_global_deinit(global);
+	return NULL;
+}
+
+
+static void nl80211_global_deinit(void *priv)
+{
+	struct nl80211_global *global = priv;
+	if (global == NULL)
+		return;
+	if (!dl_list_empty(&global->interfaces)) {
+		wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at "
+			   "nl80211_global_deinit",
+			   dl_list_len(&global->interfaces));
+	}
+
+	if (global->netlink)
+		netlink_deinit(global->netlink);
+
+	nl_destroy_handles(&global->nl);
+
+	if (global->nl_event)
+		nl80211_destroy_eloop_handle(&global->nl_event);
+
+	nl_cb_put(global->nl_cb);
+
+	if (global->ioctl_sock >= 0)
+		close(global->ioctl_sock);
+
+	os_free(global);
+}
+
+
+static const char * nl80211_get_radio_name(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	return drv->phyname;
+}
+
+
+static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
+			 const u8 *pmkid)
+{
+	struct nl_msg *msg;
+
+	if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
+	    (pmkid && nla_put(msg, NL80211_ATTR_PMKID, 16, pmkid)) ||
+	    (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+}
+
+
+static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+{
+	struct i802_bss *bss = priv;
+	wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid));
+	return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid);
+}
+
+
+static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+{
+	struct i802_bss *bss = priv;
+	wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
+		   MAC2STR(bssid));
+	return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid);
+}
+
+
+static int nl80211_flush_pmkid(void *priv)
+{
+	struct i802_bss *bss = priv;
+	wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
+	return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL);
+}
+
+
+static void clean_survey_results(struct survey_results *survey_results)
+{
+	struct freq_survey *survey, *tmp;
+
+	if (dl_list_empty(&survey_results->survey_list))
+		return;
+
+	dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
+			      struct freq_survey, list) {
+		dl_list_del(&survey->list);
+		os_free(survey);
+	}
+}
+
+
+static void add_survey(struct nlattr **sinfo, u32 ifidx,
+		       struct dl_list *survey_list)
+{
+	struct freq_survey *survey;
+
+	survey = os_zalloc(sizeof(struct freq_survey));
+	if  (!survey)
+		return;
+
+	survey->ifidx = ifidx;
+	survey->freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+	survey->filled = 0;
+
+	if (sinfo[NL80211_SURVEY_INFO_NOISE]) {
+		survey->nf = (int8_t)
+			nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+		survey->filled |= SURVEY_HAS_NF;
+	}
+
+	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]) {
+		survey->channel_time =
+			nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]);
+		survey->filled |= SURVEY_HAS_CHAN_TIME;
+	}
+
+	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) {
+		survey->channel_time_busy =
+			nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]);
+		survey->filled |= SURVEY_HAS_CHAN_TIME_BUSY;
+	}
+
+	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]) {
+		survey->channel_time_rx =
+			nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]);
+		survey->filled |= SURVEY_HAS_CHAN_TIME_RX;
+	}
+
+	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]) {
+		survey->channel_time_tx =
+			nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]);
+		survey->filled |= SURVEY_HAS_CHAN_TIME_TX;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Freq survey dump event (freq=%d MHz noise=%d channel_time=%ld busy_time=%ld tx_time=%ld rx_time=%ld filled=%04x)",
+		   survey->freq,
+		   survey->nf,
+		   (unsigned long int) survey->channel_time,
+		   (unsigned long int) survey->channel_time_busy,
+		   (unsigned long int) survey->channel_time_tx,
+		   (unsigned long int) survey->channel_time_rx,
+		   survey->filled);
+
+	dl_list_add_tail(survey_list, &survey->list);
+}
+
+
+static int check_survey_ok(struct nlattr **sinfo, u32 surveyed_freq,
+			   unsigned int freq_filter)
+{
+	if (!freq_filter)
+		return 1;
+
+	return freq_filter == surveyed_freq;
+}
+
+
+static int survey_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+	struct survey_results *survey_results;
+	u32 surveyed_freq = 0;
+	u32 ifidx;
+
+	static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+		[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+	};
+
+	survey_results = (struct survey_results *) arg;
+
+	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])
+		return NL_SKIP;
+
+	if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+			     tb[NL80211_ATTR_SURVEY_INFO],
+			     survey_policy))
+		return NL_SKIP;
+
+	if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) {
+		wpa_printf(MSG_ERROR, "nl80211: Invalid survey data");
+		return NL_SKIP;
+	}
+
+	surveyed_freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+
+	if (!check_survey_ok(sinfo, surveyed_freq,
+			     survey_results->freq_filter))
+		return NL_SKIP;
+
+	if (survey_results->freq_filter &&
+	    survey_results->freq_filter != surveyed_freq) {
+		wpa_printf(MSG_EXCESSIVE, "nl80211: Ignoring survey data for freq %d MHz",
+			   surveyed_freq);
+		return NL_SKIP;
+	}
+
+	add_survey(sinfo, ifidx, &survey_results->survey_list);
+
+	return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int err;
+	union wpa_event_data data;
+	struct survey_results *survey_results;
+
+	os_memset(&data, 0, sizeof(data));
+	survey_results = &data.survey_results;
+
+	dl_list_init(&survey_results->survey_list);
+
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+	if (!msg)
+		return -ENOBUFS;
+
+	if (freq)
+		data.survey_results.freq_filter = freq;
+
+	do {
+		wpa_printf(MSG_DEBUG, "nl80211: Fetch survey data");
+		err = send_and_recv_msgs(drv, msg, survey_handler,
+					 survey_results);
+	} while (err > 0);
+
+	if (err)
+		wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data");
+	else
+		wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data);
+
+	clean_survey_results(survey_results);
+	return err;
+}
+
+
+static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len,
+				   const u8 *kck, size_t kck_len,
+				   const u8 *replay_ctr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nlattr *replay_nested;
+	struct nl_msg *msg;
+	int ret;
+
+	if (!drv->set_rekey_offload)
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set rekey offload");
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
+	    !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
+	    nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) ||
+	    nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck) ||
+	    nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
+		    replay_ctr)) {
+		nl80211_nlmsg_clear(msg);
+		nlmsg_free(msg);
+		return;
+	}
+
+	nla_nest_end(msg, replay_nested);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+	if (ret == -EOPNOTSUPP) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Driver does not support rekey offload");
+		drv->set_rekey_offload = 0;
+	}
+}
+
+
+static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr,
+				    const u8 *addr, int qos)
+{
+	/* send data frame to poll STA and check whether
+	 * this frame is ACKed */
+	struct {
+		struct ieee80211_hdr hdr;
+		u16 qos_ctl;
+	} STRUCT_PACKED nulldata;
+	size_t size;
+
+	/* Send data frame to poll STA and check whether this frame is ACKed */
+
+	os_memset(&nulldata, 0, sizeof(nulldata));
+
+	if (qos) {
+		nulldata.hdr.frame_control =
+			IEEE80211_FC(WLAN_FC_TYPE_DATA,
+				     WLAN_FC_STYPE_QOS_NULL);
+		size = sizeof(nulldata);
+	} else {
+		nulldata.hdr.frame_control =
+			IEEE80211_FC(WLAN_FC_TYPE_DATA,
+				     WLAN_FC_STYPE_NULLFUNC);
+		size = sizeof(struct ieee80211_hdr);
+	}
+
+	nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
+	os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+	os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+	os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+	if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0,
+					 0, 0) < 0)
+		wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
+			   "send poll frame");
+}
+
+static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
+				int qos)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	if (!drv->poll_command_supported) {
+		nl80211_send_null_frame(bss, own_addr, addr, qos);
+		return;
+	}
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_PROBE_CLIENT)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Client probe request for "
+			   MACSTR " failed: ret=%d (%s)",
+			   MAC2STR(addr), ret, strerror(-ret));
+	}
+}
+
+
+static int nl80211_set_power_save(struct i802_bss *bss, int enabled)
+{
+	struct nl_msg *msg;
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_POWER_SAVE)) ||
+	    nla_put_u32(msg, NL80211_ATTR_PS_STATE,
+			enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+	return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+}
+
+
+static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps,
+				     int ctwindow)
+{
+	struct i802_bss *bss = priv;
+
+	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) {
+#ifdef ANDROID_P2P
+		wpa_driver_set_p2p_ps(priv, legacy_ps, opp_ps, ctwindow);
+#else /* ANDROID_P2P */
+		return -1; /* Not yet supported */
+#endif /* ANDROID_P2P */
+	}
+
+	if (legacy_ps == -1)
+		return 0;
+	if (legacy_ps != 0 && legacy_ps != 1)
+		return -1; /* Not yet supported */
+
+	return nl80211_set_power_save(bss, legacy_ps);
+}
+
+
+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) %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");
+		return -1;
+	}
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_RADAR_DETECT)) ||
+	    nl80211_put_freq_params(msg, freq) < 0) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret == 0)
+		return 0;
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
+		   "%d (%s)", ret, strerror(-ret));
+	return -1;
+}
+
+#ifdef CONFIG_TDLS
+
+static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
+				  u8 dialog_token, u16 status_code,
+				  u32 peer_capab, int initiator, const u8 *buf,
+				  size_t len)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+		return -EOPNOTSUPP;
+
+	if (!dst)
+		return -EINVAL;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_MGMT)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
+	    nla_put_u8(msg, NL80211_ATTR_TDLS_ACTION, action_code) ||
+	    nla_put_u8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token) ||
+	    nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status_code))
+		goto fail;
+	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.
+		 */
+		if (nla_put_u32(msg, NL80211_ATTR_TDLS_PEER_CAPABILITY,
+				peer_capab))
+			goto fail;
+	}
+	if ((initiator &&
+	     nla_put_flag(msg, NL80211_ATTR_TDLS_INITIATOR)) ||
+	    nla_put(msg, NL80211_ATTR_IE, len, buf))
+		goto fail;
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
+
+fail:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
+static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	enum nl80211_tdls_operation nl80211_oper;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+		return -EOPNOTSUPP;
+
+	switch (oper) {
+	case TDLS_DISCOVERY_REQ:
+		nl80211_oper = NL80211_TDLS_DISCOVERY_REQ;
+		break;
+	case TDLS_SETUP:
+		nl80211_oper = NL80211_TDLS_SETUP;
+		break;
+	case TDLS_TEARDOWN:
+		nl80211_oper = NL80211_TDLS_TEARDOWN;
+		break;
+	case TDLS_ENABLE_LINK:
+		nl80211_oper = NL80211_TDLS_ENABLE_LINK;
+		break;
+	case TDLS_DISABLE_LINK:
+		nl80211_oper = NL80211_TDLS_DISABLE_LINK;
+		break;
+	case TDLS_ENABLE:
+		return 0;
+	case TDLS_DISABLE:
+		return 0;
+	default:
+		return -EINVAL;
+	}
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_OPER)) ||
+	    nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+
+static int
+nl80211_tdls_enable_channel_switch(void *priv, const u8 *addr, u8 oper_class,
+				   const struct hostapd_freq_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret = -ENOBUFS;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
+	    !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
+		return -EOPNOTSUPP;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Enable TDLS channel switch " MACSTR
+		   " oper_class=%u freq=%u",
+		   MAC2STR(addr), oper_class, params->freq);
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CHANNEL_SWITCH);
+	if (!msg ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    nla_put_u8(msg, NL80211_ATTR_OPER_CLASS, oper_class) ||
+	    (ret = nl80211_put_freq_params(msg, params))) {
+		nlmsg_free(msg);
+		wpa_printf(MSG_DEBUG, "nl80211: Could not build TDLS chan switch");
+		return ret;
+	}
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+
+static int
+nl80211_tdls_disable_channel_switch(void *priv, const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
+	    !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
+		return -EOPNOTSUPP;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Disable TDLS channel switch " MACSTR,
+		   MAC2STR(addr));
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH);
+	if (!msg ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+		nlmsg_free(msg);
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Could not build TDLS cancel chan switch");
+		return -ENOBUFS;
+	}
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+#endif /* CONFIG TDLS */
+
+
+static int driver_nl80211_set_key(const char *ifname, void *priv,
+				  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)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_set_key(ifname, bss, alg, addr, key_idx,
+					  set_tx, seq, seq_len, key, key_len);
+}
+
+
+static int driver_nl80211_scan2(void *priv,
+				struct wpa_driver_scan_params *params)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_scan(bss, params);
+}
+
+
+static int driver_nl80211_deauthenticate(void *priv, const u8 *addr,
+					 int reason_code)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_deauthenticate(bss, addr, reason_code);
+}
+
+
+static int driver_nl80211_authenticate(void *priv,
+				       struct wpa_driver_auth_params *params)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_authenticate(bss, params);
+}
+
+
+static void driver_nl80211_deinit(void *priv)
+{
+	struct i802_bss *bss = priv;
+	wpa_driver_nl80211_deinit(bss);
+}
+
+
+static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type,
+				    const char *ifname)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_if_remove(bss, type, ifname);
+}
+
+
+static int driver_nl80211_send_mlme(void *priv, const u8 *data,
+				    size_t data_len, int noack,
+				    unsigned int freq)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack,
+					    freq, 0, 0, 0);
+}
+
+
+static int driver_nl80211_sta_remove(void *priv, const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_sta_remove(bss, addr, -1, 0);
+}
+
+
+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);
+}
+
+
+static int driver_nl80211_read_sta_data(void *priv,
+					struct hostap_sta_driver_data *data,
+					const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	return i802_read_sta_data(bss, data, addr);
+}
+
+
+static int driver_nl80211_send_action(void *priv, unsigned int freq,
+				      unsigned int wait_time,
+				      const u8 *dst, const u8 *src,
+				      const u8 *bssid,
+				      const u8 *data, size_t data_len,
+				      int no_cck)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_send_action(bss, freq, wait_time, dst, src,
+					      bssid, data, data_len, no_cck);
+}
+
+
+static int driver_nl80211_probe_req_report(void *priv, int report)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_probe_req_report(bss, report);
+}
+
+
+static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md,
+					    const u8 *ies, size_t ies_len)
+{
+	int ret;
+	struct nl_msg *msg;
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	u16 mdid = WPA_GET_LE16(md);
+
+	wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs");
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_FT_IES)) ||
+	    nla_put(msg, NL80211_ATTR_IE, ies_len, ies) ||
+	    nla_put_u16(msg, NL80211_ATTR_MDID, mdid)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: update_ft_ies failed "
+			   "err=%d (%s)", ret, strerror(-ret));
+	}
+
+	return ret;
+}
+
+
+const u8 * wpa_driver_nl80211_get_macaddr(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE)
+		return NULL;
+
+	return bss->addr;
+}
+
+
+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 (os_snprintf_error(end - pos, res))
+		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 (os_snprintf_error(end - pos, res))
+			return pos - buf;
+		pos += res;
+	}
+
+	res = os_snprintf(pos, end - pos,
+			  "phyname=%s\n"
+			  "perm_addr=" MACSTR "\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",
+			  drv->phyname,
+			  MAC2STR(drv->perm_addr),
+			  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" : "");
+	if (os_snprintf_error(end - pos, res))
+		return pos - buf;
+	pos += res;
+
+	if (drv->has_capability) {
+		res = os_snprintf(pos, end - pos,
+				  "capa.key_mgmt=0x%x\n"
+				  "capa.enc=0x%x\n"
+				  "capa.auth=0x%x\n"
+				  "capa.flags=0x%llx\n"
+				  "capa.rrm_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"
+				  "capa.mac_addr_rand_sched_scan_supported=%d\n"
+				  "capa.mac_addr_rand_scan_supported=%d\n"
+				  "capa.conc_capab=%u\n"
+				  "capa.max_conc_chan_2_4=%u\n"
+				  "capa.max_conc_chan_5_0=%u\n",
+				  drv->capa.key_mgmt,
+				  drv->capa.enc,
+				  drv->capa.auth,
+				  (unsigned long long) drv->capa.flags,
+				  drv->capa.rrm_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,
+				  drv->capa.mac_addr_rand_sched_scan_supported,
+				  drv->capa.mac_addr_rand_scan_supported,
+				  drv->capa.conc_capab,
+				  drv->capa.max_conc_chan_2_4,
+				  drv->capa.max_conc_chan_5_0);
+		if (os_snprintf_error(end - pos, res))
+			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)) ||
+	    (settings->tail &&
+	     nla_put(msg, NL80211_ATTR_BEACON_TAIL,
+		     settings->tail_len, settings->tail)) ||
+	    (settings->beacon_ies &&
+	     nla_put(msg, NL80211_ATTR_IE,
+		     settings->beacon_ies_len, settings->beacon_ies)) ||
+	    (settings->proberesp_ies &&
+	     nla_put(msg, NL80211_ATTR_IE_PROBE_RESP,
+		     settings->proberesp_ies_len, settings->proberesp_ies)) ||
+	    (settings->assocresp_ies &&
+	     nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP,
+		     settings->assocresp_ies_len, settings->assocresp_ies)) ||
+	    (settings->probe_resp &&
+	     nla_put(msg, NL80211_ATTR_PROBE_RESP,
+		     settings->probe_resp_len, settings->probe_resp)))
+		return -ENOBUFS;
+
+	return 0;
+}
+
+
+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;
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CHANNEL_SWITCH)) ||
+	    nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT,
+			settings->cs_count) ||
+	    (ret = nl80211_put_freq_params(msg, &settings->freq_params)) ||
+	    (settings->block_tx &&
+	     nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)))
+		goto error;
+
+	/* 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 fail;
+
+	ret = set_beacon_data(msg, &settings->beacon_csa);
+	if (ret)
+		goto error;
+
+	if (nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
+			settings->counter_offset_beacon) ||
+	    (settings->beacon_csa.probe_resp &&
+	     nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
+			 settings->counter_offset_presp)))
+		goto fail;
+
+	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;
+
+fail:
+	ret = -ENOBUFS;
+error:
+	nlmsg_free(msg);
+	wpa_printf(MSG_DEBUG, "nl80211: Could not build channel switch request");
+	return ret;
+}
+
+
+static int nl80211_add_ts(void *priv, u8 tsid, const u8 *addr,
+			  u8 user_priority, u16 admitted_time)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: add_ts request: tsid=%u admitted_time=%u up=%d",
+		   tsid, admitted_time, user_priority);
+
+	if (!is_sta_interface(drv->nlmode))
+		return -ENOTSUP;
+
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ADD_TX_TS);
+	if (!msg ||
+	    nla_put_u8(msg, NL80211_ATTR_TSID, tsid) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    nla_put_u8(msg, NL80211_ATTR_USER_PRIO, user_priority) ||
+	    nla_put_u16(msg, NL80211_ATTR_ADMITTED_TIME, admitted_time)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: add_ts failed err=%d (%s)",
+			   ret, strerror(-ret));
+	return ret;
+}
+
+
+static int nl80211_del_ts(void *priv, u8 tsid, const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: del_ts request: tsid=%u", tsid);
+
+	if (!is_sta_interface(drv->nlmode))
+		return -ENOTSUP;
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_TX_TS)) ||
+	    nla_put_u8(msg, NL80211_ATTR_TSID, tsid) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: del_ts failed err=%d (%s)",
+			   ret, strerror(-ret));
+	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;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (vendor_id == 0xffffffff) {
+		msg = nlmsg_alloc();
+		if (!msg)
+			return -ENOMEM;
+
+		nl80211_cmd(drv, msg, 0, subcmd);
+		if (nlmsg_append(msg, (void *) data, data_len, NLMSG_ALIGNTO) <
+		    0)
+			goto fail;
+		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 */
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, vendor_id) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd) ||
+	    (data &&
+	     nla_put(msg, NL80211_ATTR_VENDOR_DATA, data_len, data)))
+		goto fail;
+
+	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;
+
+fail:
+	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;
+
+	wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map",
+		    qos_map_set, qos_map_set_len);
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_QOS_MAP)) ||
+	    nla_put(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed");
+
+	return ret;
+}
+
+
+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;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan");
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_WOWLAN)) ||
+	    !(wowlan_triggers = nla_nest_start(msg,
+					       NL80211_ATTR_WOWLAN_TRIGGERS)) ||
+	    (triggers->any &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
+	    (triggers->disconnect &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
+	    (triggers->magic_pkt &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
+	    (triggers->gtk_rekey_failure &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
+	    (triggers->eap_identity_req &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
+	    (triggers->four_way_handshake &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
+	    (triggers->rfkill_release &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	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;
+}
+
+
+static int nl80211_roaming(void *priv, int allowed, const u8 *bssid)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *params;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Roaming policy: allowed=%d", allowed);
+
+	if (!drv->roaming_vendor_cmd_avail) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore roaming policy change since driver does not provide command for setting it");
+		return -1;
+	}
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_ROAMING) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY,
+			allowed ? QCA_ROAMING_ALLOWED_WITHIN_ESS :
+			QCA_ROAMING_NOT_ALLOWED) ||
+	    (bssid &&
+	     nla_put(msg, QCA_WLAN_VENDOR_ATTR_MAC_ADDR, ETH_ALEN, bssid))) {
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_nest_end(msg, params);
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+
+static int nl80211_set_mac_addr(void *priv, const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int new_addr = addr != NULL;
+
+	if (!addr)
+		addr = drv->perm_addr;
+
+	if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) < 0)
+		return -1;
+
+	if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, addr) < 0)
+	{
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: failed to set_mac_addr for %s to " MACSTR,
+			   bss->ifname, MAC2STR(addr));
+		if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname,
+					  1) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Could not restore interface UP after failed set_mac_addr");
+		}
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR,
+		   bss->ifname, MAC2STR(addr));
+	drv->addr_changed = new_addr;
+	os_memcpy(bss->addr, addr, ETH_ALEN);
+
+	if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0)
+	{
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Could not restore interface UP after set_mac_addr");
+	}
+
+	return 0;
+}
+
+
+#ifdef CONFIG_MESH
+
+static int wpa_driver_nl80211_init_mesh(void *priv)
+{
+	if (wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_MESH_POINT)) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: Failed to set interface into mesh mode");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
+			       size_t mesh_id_len)
+{
+	if (mesh_id) {
+		wpa_hexdump_ascii(MSG_DEBUG, "  * Mesh ID (SSID)",
+				  mesh_id, mesh_id_len);
+		return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id);
+	}
+
+	return 0;
+}
+
+
+static int nl80211_join_mesh(struct i802_bss *bss,
+			     struct wpa_driver_mesh_join_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *container;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH);
+	if (!msg ||
+	    nl80211_put_freq_params(msg, &params->freq) ||
+	    nl80211_put_basic_rates(msg, params->basic_rates) ||
+	    nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
+	    nl80211_put_beacon_int(msg, params->beacon_int))
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "  * flags=%08X", params->flags);
+
+	container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP);
+	if (!container)
+		goto fail;
+
+	if (params->ies) {
+		wpa_hexdump(MSG_DEBUG, "  * IEs", params->ies, params->ie_len);
+		if (nla_put(msg, NL80211_MESH_SETUP_IE, params->ie_len,
+			    params->ies))
+			goto fail;
+	}
+	/* WPA_DRIVER_MESH_FLAG_OPEN_AUTH is treated as default by nl80211 */
+	if (params->flags & WPA_DRIVER_MESH_FLAG_SAE_AUTH) {
+		if (nla_put_u8(msg, NL80211_MESH_SETUP_AUTH_PROTOCOL, 0x1) ||
+		    nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AUTH))
+			goto fail;
+	}
+	if ((params->flags & WPA_DRIVER_MESH_FLAG_AMPE) &&
+	    nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AMPE))
+		goto fail;
+	if ((params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM) &&
+	    nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_MPM))
+		goto fail;
+	nla_nest_end(msg, container);
+
+	container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
+	if (!container)
+		goto fail;
+
+	if (!(params->conf.flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) &&
+	    nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, 0))
+		goto fail;
+	if ((params->conf.flags & WPA_DRIVER_MESH_FLAG_DRIVER_MPM) &&
+	    nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
+			params->max_peer_links))
+		goto fail;
+
+	/*
+	 * Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because
+	 * the timer could disconnect stations even in that case.
+	 */
+	if (nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
+			params->conf.peer_link_timeout)) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT");
+		goto fail;
+	}
+
+	nla_nest_end(msg, container);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+	ret = 0;
+	bss->freq = params->freq.freq;
+	wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully");
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int
+wpa_driver_nl80211_join_mesh(void *priv,
+			     struct wpa_driver_mesh_join_params *params)
+{
+	struct i802_bss *bss = priv;
+	int ret, timeout;
+
+	timeout = params->conf.peer_link_timeout;
+
+	/* Disable kernel inactivity timer */
+	if (params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM)
+		params->conf.peer_link_timeout = 0;
+
+	ret = nl80211_join_mesh(bss, params);
+	if (ret == -EINVAL && params->conf.peer_link_timeout == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Mesh join retry for peer_link_timeout");
+		/*
+		 * Old kernel does not support setting
+		 * NL80211_MESHCONF_PLINK_TIMEOUT to zero, so set 60 seconds
+		 * into future from peer_link_timeout.
+		 */
+		params->conf.peer_link_timeout = timeout + 60;
+		ret = nl80211_join_mesh(priv, params);
+	}
+
+	params->conf.peer_link_timeout = timeout;
+	return ret;
+}
+
+
+static int wpa_driver_nl80211_leave_mesh(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: mesh leave request send successfully");
+	}
+
+	if (wpa_driver_nl80211_set_mode(drv->first_bss,
+					NL80211_IFTYPE_STATION)) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: Failed to set interface into station mode");
+	}
+	return ret;
+}
+
+#endif /* CONFIG_MESH */
+
+
+static int wpa_driver_br_add_ip_neigh(void *priv, u8 version,
+				      const u8 *ipaddr, int prefixlen,
+				      const u8 *addr)
+{
+#ifdef CONFIG_LIBNL3_ROUTE
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct rtnl_neigh *rn;
+	struct nl_addr *nl_ipaddr = NULL;
+	struct nl_addr *nl_lladdr = NULL;
+	int family, addrsize;
+	int res;
+
+	if (!ipaddr || prefixlen == 0 || !addr)
+		return -EINVAL;
+
+	if (bss->br_ifindex == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: bridge must be set before adding an ip neigh to it");
+		return -1;
+	}
+
+	if (!drv->rtnl_sk) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: nl_sock for NETLINK_ROUTE is not initialized");
+		return -1;
+	}
+
+	if (version == 4) {
+		family = AF_INET;
+		addrsize = 4;
+	} else if (version == 6) {
+		family = AF_INET6;
+		addrsize = 16;
+	} else {
+		return -EINVAL;
+	}
+
+	rn = rtnl_neigh_alloc();
+	if (rn == NULL)
+		return -ENOMEM;
+
+	/* set the destination ip address for neigh */
+	nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
+	if (nl_ipaddr == NULL) {
+		wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
+		res = -ENOMEM;
+		goto errout;
+	}
+	nl_addr_set_prefixlen(nl_ipaddr, prefixlen);
+	res = rtnl_neigh_set_dst(rn, nl_ipaddr);
+	if (res) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: neigh set destination addr failed");
+		goto errout;
+	}
+
+	/* set the corresponding lladdr for neigh */
+	nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN);
+	if (nl_lladdr == NULL) {
+		wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed");
+		res = -ENOMEM;
+		goto errout;
+	}
+	rtnl_neigh_set_lladdr(rn, nl_lladdr);
+
+	rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
+	rtnl_neigh_set_state(rn, NUD_PERMANENT);
+
+	res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE);
+	if (res) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Adding bridge ip neigh failed: %s",
+			   strerror(errno));
+	}
+errout:
+	if (nl_lladdr)
+		nl_addr_put(nl_lladdr);
+	if (nl_ipaddr)
+		nl_addr_put(nl_ipaddr);
+	if (rn)
+		rtnl_neigh_put(rn);
+	return res;
+#else /* CONFIG_LIBNL3_ROUTE */
+	return -1;
+#endif /* CONFIG_LIBNL3_ROUTE */
+}
+
+
+static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
+					 const u8 *ipaddr)
+{
+#ifdef CONFIG_LIBNL3_ROUTE
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct rtnl_neigh *rn;
+	struct nl_addr *nl_ipaddr;
+	int family, addrsize;
+	int res;
+
+	if (!ipaddr)
+		return -EINVAL;
+
+	if (version == 4) {
+		family = AF_INET;
+		addrsize = 4;
+	} else if (version == 6) {
+		family = AF_INET6;
+		addrsize = 16;
+	} else {
+		return -EINVAL;
+	}
+
+	if (bss->br_ifindex == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: bridge must be set to delete an ip neigh");
+		return -1;
+	}
+
+	if (!drv->rtnl_sk) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: nl_sock for NETLINK_ROUTE is not initialized");
+		return -1;
+	}
+
+	rn = rtnl_neigh_alloc();
+	if (rn == NULL)
+		return -ENOMEM;
+
+	/* set the destination ip address for neigh */
+	nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
+	if (nl_ipaddr == NULL) {
+		wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
+		res = -ENOMEM;
+		goto errout;
+	}
+	res = rtnl_neigh_set_dst(rn, nl_ipaddr);
+	if (res) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: neigh set destination addr failed");
+		goto errout;
+	}
+
+	rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
+
+	res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
+	if (res) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Deleting bridge ip neigh failed: %s",
+			   strerror(errno));
+	}
+errout:
+	if (nl_ipaddr)
+		nl_addr_put(nl_ipaddr);
+	if (rn)
+		rtnl_neigh_put(rn);
+	return res;
+#else /* CONFIG_LIBNL3_ROUTE */
+	return -1;
+#endif /* CONFIG_LIBNL3_ROUTE */
+}
+
+
+static int linux_write_system_file(const char *path, unsigned int val)
+{
+	char buf[50];
+	int fd, len;
+
+	len = os_snprintf(buf, sizeof(buf), "%u\n", val);
+	if (os_snprintf_error(sizeof(buf), len))
+		return -1;
+
+	fd = open(path, O_WRONLY);
+	if (fd < 0)
+		return -1;
+
+	if (write(fd, buf, len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Failed to write Linux system file: %s with the value of %d",
+			   path, val);
+		close(fd);
+		return -1;
+	}
+	close(fd);
+
+	return 0;
+}
+
+
+static const char * drv_br_port_attr_str(enum drv_br_port_attr attr)
+{
+	switch (attr) {
+	case DRV_BR_PORT_ATTR_PROXYARP:
+		return "proxyarp_wifi";
+	case DRV_BR_PORT_ATTR_HAIRPIN_MODE:
+		return "hairpin_mode";
+	}
+
+	return NULL;
+}
+
+
+static int wpa_driver_br_port_set_attr(void *priv, enum drv_br_port_attr attr,
+				       unsigned int val)
+{
+	struct i802_bss *bss = priv;
+	char path[128];
+	const char *attr_txt;
+
+	attr_txt = drv_br_port_attr_str(attr);
+	if (attr_txt == NULL)
+		return -EINVAL;
+
+	os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/%s",
+		    bss->ifname, attr_txt);
+
+	if (linux_write_system_file(path, val))
+		return -1;
+
+	return 0;
+}
+
+
+static const char * drv_br_net_param_str(enum drv_br_net_param param)
+{
+	switch (param) {
+	case DRV_BR_NET_PARAM_GARP_ACCEPT:
+		return "arp_accept";
+	default:
+		return NULL;
+	}
+}
+
+
+static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
+				       unsigned int val)
+{
+	struct i802_bss *bss = priv;
+	char path[128];
+	const char *param_txt;
+	int ip_version = 4;
+
+	if (param == DRV_BR_MULTICAST_SNOOPING) {
+		os_snprintf(path, sizeof(path),
+			    "/sys/devices/virtual/net/%s/bridge/multicast_snooping",
+			    bss->brname);
+		goto set_val;
+	}
+
+	param_txt = drv_br_net_param_str(param);
+	if (param_txt == NULL)
+		return -EINVAL;
+
+	switch (param) {
+		case DRV_BR_NET_PARAM_GARP_ACCEPT:
+			ip_version = 4;
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
+		    ip_version, bss->brname, param_txt);
+
+set_val:
+	if (linux_write_system_file(path, val))
+		return -1;
+
+	return 0;
+}
+
+
+static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode)
+{
+	switch (hw_mode) {
+	case HOSTAPD_MODE_IEEE80211B:
+		return QCA_ACS_MODE_IEEE80211B;
+	case HOSTAPD_MODE_IEEE80211G:
+		return QCA_ACS_MODE_IEEE80211G;
+	case HOSTAPD_MODE_IEEE80211A:
+		return QCA_ACS_MODE_IEEE80211A;
+	case HOSTAPD_MODE_IEEE80211AD:
+		return QCA_ACS_MODE_IEEE80211AD;
+	case HOSTAPD_MODE_IEEE80211ANY:
+		return QCA_ACS_MODE_IEEE80211ANY;
+	default:
+		return -1;
+	}
+}
+
+
+static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list)
+{
+	int i, len, ret;
+	u32 *freqs;
+
+	if (!freq_list)
+		return 0;
+	len = int_array_len(freq_list);
+	freqs = os_malloc(sizeof(u32) * len);
+	if (!freqs)
+		return -1;
+	for (i = 0; i < len; i++)
+		freqs[i] = freq_list[i];
+	ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
+		      sizeof(u32) * len, freqs);
+	os_free(freqs);
+	return ret;
+}
+
+
+static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *data;
+	int ret;
+	int mode;
+
+	mode = hw_mode_to_qca_acs(params->hw_mode);
+	if (mode < 0)
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_DO_ACS) ||
+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, mode) ||
+	    (params->ht_enabled &&
+	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED)) ||
+	    (params->ht40_enabled &&
+	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED)) ||
+	    (params->vht_enabled &&
+	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED)) ||
+	    nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
+			params->ch_width) ||
+	    (params->ch_list_len &&
+	     nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, params->ch_list_len,
+		     params->ch_list)) ||
+	    add_acs_freq_list(msg, params->freq_list)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+	nla_nest_end(msg, data);
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d CH_LIST_LEN: %u",
+		   params->hw_mode, params->ht_enabled, params->ht40_enabled,
+		   params->vht_enabled, params->ch_width, params->ch_list_len);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Failed to invoke driver ACS function: %s",
+			   strerror(errno));
+	}
+	return ret;
+}
+
+
+static int nl80211_set_band(void *priv, enum set_band band)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *data;
+	int ret;
+	enum qca_set_band qca_band;
+
+	if (!drv->setband_vendor_cmd_avail)
+		return -1;
+
+	switch (band) {
+	case WPA_SETBAND_AUTO:
+		qca_band = QCA_SETBAND_AUTO;
+		break;
+	case WPA_SETBAND_5G:
+		qca_band = QCA_SETBAND_5G;
+		break;
+	case WPA_SETBAND_2G:
+		qca_band = QCA_SETBAND_2G;
+		break;
+	default:
+		return -1;
+	}
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SETBAND) ||
+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE, qca_band)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+	nla_nest_end(msg, data);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Driver setband function failed: %s",
+			   strerror(errno));
+	}
+	return ret;
+}
+
+
+struct nl80211_pcl {
+	unsigned int num;
+	unsigned int *freq_list;
+};
+
+static int preferred_freq_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nl80211_pcl *param = arg;
+	struct nlattr *nl_vend, *attr;
+	enum qca_iface_type iface_type;
+	struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+	unsigned int num, max_num;
+	u32 *freqs;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+	if (!nl_vend)
+		return NL_SKIP;
+
+	nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+		  nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+	attr = tb_vendor[
+		QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE];
+	if (!attr) {
+		wpa_printf(MSG_ERROR, "nl80211: iface_type couldn't be found");
+		param->num = 0;
+		return NL_SKIP;
+	}
+
+	iface_type = (enum qca_iface_type) nla_get_u32(attr);
+	wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d",
+		   iface_type);
+
+	attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST];
+	if (!attr) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: preferred_freq_list couldn't be found");
+		param->num = 0;
+		return NL_SKIP;
+	}
+
+	/*
+	 * param->num has the maximum number of entries for which there
+	 * is room in the freq_list provided by the caller.
+	 */
+	freqs = nla_data(attr);
+	max_num = nla_len(attr) / sizeof(u32);
+	if (max_num > param->num)
+		max_num = param->num;
+	for (num = 0; num < max_num; num++)
+		param->freq_list[num] = freqs[num];
+	param->num = num;
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_pref_freq_list(void *priv,
+				      enum wpa_driver_if_type if_type,
+				      unsigned int *num,
+				      unsigned int *freq_list)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	unsigned int i;
+	struct nlattr *params;
+	struct nl80211_pcl param;
+	enum qca_iface_type iface_type;
+
+	if (!drv->get_pref_freq_list)
+		return -1;
+
+	switch (if_type) {
+	case WPA_IF_STATION:
+		iface_type = QCA_IFACE_TYPE_STA;
+		break;
+	case WPA_IF_AP_BSS:
+		iface_type = QCA_IFACE_TYPE_AP;
+		break;
+	case WPA_IF_P2P_GO:
+		iface_type = QCA_IFACE_TYPE_P2P_GO;
+		break;
+	case WPA_IF_P2P_CLIENT:
+		iface_type = QCA_IFACE_TYPE_P2P_CLIENT;
+		break;
+	case WPA_IF_IBSS:
+		iface_type = QCA_IFACE_TYPE_IBSS;
+		break;
+	case WPA_IF_TDLS:
+		iface_type = QCA_IFACE_TYPE_TDLS;
+		break;
+	default:
+		return -1;
+	}
+
+	param.num = *num;
+	param.freq_list = freq_list;
+
+	if (!(msg = nl80211_drv_msg(drv, 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_GET_PREFERRED_FREQ_LIST) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg,
+			QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
+			iface_type)) {
+		wpa_printf(MSG_ERROR,
+			   "%s: err in adding vendor_cmd and vendor_data",
+			   __func__);
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_nest_end(msg, params);
+
+	os_memset(freq_list, 0, *num * sizeof(freq_list[0]));
+	ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, &param);
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "%s: err in send_and_recv_msgs", __func__);
+		return ret;
+	}
+
+	*num = param.num;
+
+	for (i = 0; i < *num; i++) {
+		wpa_printf(MSG_DEBUG, "nl80211: preferred_channel_list[%d]=%d",
+			   i, freq_list[i]);
+	}
+
+	return 0;
+}
+
+
+static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	struct nlattr *params;
+
+	if (!drv->set_prob_oper_freq)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Set P2P probable operating freq %u for ifindex %d",
+		   freq, bss->ifindex);
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg,
+			QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE,
+			QCA_IFACE_TYPE_P2P_CLIENT) ||
+	    nla_put_u32(msg,
+			QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ,
+			freq)) {
+		wpa_printf(MSG_ERROR,
+			   "%s: err in adding vendor_cmd and vendor_data",
+			   __func__);
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_ERROR, "%s: err in send_and_recv_msgs",
+			   __func__);
+		return ret;
+	}
+	nlmsg_free(msg);
+	return 0;
+}
+
+
+const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+	.name = "nl80211",
+	.desc = "Linux nl80211/cfg80211",
+	.get_bssid = wpa_driver_nl80211_get_bssid,
+	.get_ssid = wpa_driver_nl80211_get_ssid,
+	.set_key = driver_nl80211_set_key,
+	.scan2 = driver_nl80211_scan2,
+	.sched_scan = wpa_driver_nl80211_sched_scan,
+	.stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
+	.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
+	.deauthenticate = driver_nl80211_deauthenticate,
+	.authenticate = driver_nl80211_authenticate,
+	.associate = wpa_driver_nl80211_associate,
+	.global_init = nl80211_global_init,
+	.global_deinit = nl80211_global_deinit,
+	.init2 = wpa_driver_nl80211_init,
+	.deinit = driver_nl80211_deinit,
+	.get_capa = wpa_driver_nl80211_get_capa,
+	.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,
+	.if_remove = driver_nl80211_if_remove,
+	.send_mlme = driver_nl80211_send_mlme,
+	.get_hw_feature_data = nl80211_get_hw_feature_data,
+	.sta_add = wpa_driver_nl80211_sta_add,
+	.sta_remove = driver_nl80211_sta_remove,
+	.hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
+	.sta_set_flags = wpa_driver_nl80211_sta_set_flags,
+	.hapd_init = i802_init,
+	.hapd_deinit = i802_deinit,
+	.set_wds_sta = i802_set_wds_sta,
+	.get_seqnum = i802_get_seqnum,
+	.flush = i802_flush,
+	.get_inact_sec = i802_get_inact_sec,
+	.sta_clear_stats = i802_sta_clear_stats,
+	.set_rts = i802_set_rts,
+	.set_frag = i802_set_frag,
+	.set_tx_queue_params = i802_set_tx_queue_params,
+	.set_sta_vlan = driver_nl80211_set_sta_vlan,
+	.sta_deauth = i802_sta_deauth,
+	.sta_disassoc = i802_sta_disassoc,
+	.read_sta_data = driver_nl80211_read_sta_data,
+	.set_freq = i802_set_freq,
+	.send_action = driver_nl80211_send_action,
+	.send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait,
+	.remain_on_channel = wpa_driver_nl80211_remain_on_channel,
+	.cancel_remain_on_channel =
+	wpa_driver_nl80211_cancel_remain_on_channel,
+	.probe_req_report = driver_nl80211_probe_req_report,
+	.deinit_ap = wpa_driver_nl80211_deinit_ap,
+	.deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli,
+	.resume = wpa_driver_nl80211_resume,
+	.signal_monitor = nl80211_signal_monitor,
+	.signal_poll = nl80211_signal_poll,
+	.send_frame = nl80211_send_frame,
+	.set_param = nl80211_set_param,
+	.get_radio_name = nl80211_get_radio_name,
+	.add_pmkid = nl80211_add_pmkid,
+	.remove_pmkid = nl80211_remove_pmkid,
+	.flush_pmkid = nl80211_flush_pmkid,
+	.set_rekey_info = nl80211_set_rekey_info,
+	.poll_client = nl80211_poll_client,
+	.set_p2p_powersave = nl80211_set_p2p_powersave,
+	.start_dfs_cac = nl80211_start_radar_detection,
+	.stop_ap = wpa_driver_nl80211_stop_ap,
+#ifdef CONFIG_TDLS
+	.send_tdls_mgmt = nl80211_send_tdls_mgmt,
+	.tdls_oper = nl80211_tdls_oper,
+	.tdls_enable_channel_switch = nl80211_tdls_enable_channel_switch,
+	.tdls_disable_channel_switch = nl80211_tdls_disable_channel_switch,
+#endif /* CONFIG_TDLS */
+	.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 /* ANDROID_P2P */
+#ifdef ANDROID
+#ifndef ANDROID_LIB_STUB
+	.driver_cmd = wpa_driver_nl80211_driver_cmd,
+#endif /* !ANDROID_LIB_STUB */
+#endif /* ANDROID */
+	.vendor_cmd = nl80211_vendor_cmd,
+	.set_qos_map = nl80211_set_qos_map,
+	.set_wowlan = nl80211_set_wowlan,
+	.roaming = nl80211_roaming,
+	.set_mac_addr = nl80211_set_mac_addr,
+#ifdef CONFIG_MESH
+	.init_mesh = wpa_driver_nl80211_init_mesh,
+	.join_mesh = wpa_driver_nl80211_join_mesh,
+	.leave_mesh = wpa_driver_nl80211_leave_mesh,
+#endif /* CONFIG_MESH */
+	.br_add_ip_neigh = wpa_driver_br_add_ip_neigh,
+	.br_delete_ip_neigh = wpa_driver_br_delete_ip_neigh,
+	.br_port_set_attr = wpa_driver_br_port_set_attr,
+	.br_set_net_param = wpa_driver_br_set_net_param,
+	.add_tx_ts = nl80211_add_ts,
+	.del_tx_ts = nl80211_del_ts,
+	.do_acs = wpa_driver_do_acs,
+	.set_band = nl80211_set_band,
+	.get_pref_freq_list = nl80211_get_pref_freq_list,
+	.set_prob_oper_freq = nl80211_set_prob_oper_freq,
+};
diff --git a/hostap/src/drivers/driver_nl80211.h b/hostap/src/drivers/driver_nl80211.h
new file mode 100644
index 0000000..5c21e0f
--- /dev/null
+++ b/hostap/src/drivers/driver_nl80211.h
@@ -0,0 +1,277 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - definitions
+ * 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>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_NL80211_H
+#define DRIVER_NL80211_H
+
+#include "nl80211_copy.h"
+#include "utils/list.h"
+#include "driver.h"
+
+#ifdef CONFIG_LIBNL20
+/* libnl 2.0 compatibility code */
+#define nl_handle nl_sock
+#define nl80211_handle_alloc nl_socket_alloc_cb
+#define nl80211_handle_destroy nl_socket_free
+#endif /* CONFIG_LIBNL20 */
+
+struct nl80211_global {
+	struct dl_list interfaces;
+	int if_add_ifindex;
+	u64 if_add_wdevid;
+	int if_add_wdevid_set;
+	struct netlink_data *netlink;
+	struct nl_cb *nl_cb;
+	struct nl_handle *nl;
+	int nl80211_id;
+	int ioctl_sock; /* socket for ioctl() use */
+
+	struct nl_handle *nl_event;
+};
+
+struct nl80211_wiphy_data {
+	struct dl_list list;
+	struct dl_list bsss;
+	struct dl_list drvs;
+
+	struct nl_handle *nl_beacons;
+	struct nl_cb *nl_cb;
+
+	int wiphy_idx;
+};
+
+struct i802_bss {
+	struct wpa_driver_nl80211_data *drv;
+	struct i802_bss *next;
+	int ifindex;
+	int br_ifindex;
+	u64 wdev_id;
+	char ifname[IFNAMSIZ + 1];
+	char brname[IFNAMSIZ];
+	unsigned int beacon_set:1;
+	unsigned int added_if_into_bridge:1;
+	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;
+	struct nl_handle *nl_preq, *nl_mgmt;
+	struct nl_cb *nl_cb;
+
+	struct nl80211_wiphy_data *wiphy_data;
+	struct dl_list wiphy_list;
+};
+
+struct wpa_driver_nl80211_data {
+	struct nl80211_global *global;
+	struct dl_list list;
+	struct dl_list wiphy_list;
+	char phyname[32];
+	u8 perm_addr[ETH_ALEN];
+	void *ctx;
+	int ifindex;
+	int if_removed;
+	int if_disabled;
+	int ignore_if_down_event;
+	struct rfkill_data *rfkill;
+	struct wpa_driver_capa capa;
+	u8 *extended_capa, *extended_capa_mask;
+	unsigned int extended_capa_len;
+	int has_capability;
+
+	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;
+
+	u8 auth_bssid[ETH_ALEN];
+	u8 auth_attempt_bssid[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
+	u8 prev_bssid[ETH_ALEN];
+	int associated;
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	enum nl80211_iftype nlmode;
+	enum nl80211_iftype ap_scan_as_station;
+	unsigned int assoc_freq;
+
+	int monitor_sock;
+	int monitor_ifidx;
+	int monitor_refcount;
+
+	unsigned int disabled_11b_rates:1;
+	unsigned int pending_remain_on_chan:1;
+	unsigned int in_interface_list:1;
+	unsigned int device_ap_sme:1;
+	unsigned int poll_command_supported:1;
+	unsigned int data_tx_status:1;
+	unsigned int scan_for_auth:1;
+	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 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 vendor_cmd_test_avail:1;
+	unsigned int roaming_vendor_cmd_avail:1;
+	unsigned int dfs_vendor_cmd_avail:1;
+	unsigned int have_low_prio_scan:1;
+	unsigned int force_connect_cmd:1;
+	unsigned int addr_changed:1;
+	unsigned int get_features_vendor_cmd_avail:1;
+	unsigned int set_rekey_offload:1;
+	unsigned int p2p_go_ctwindow_supported:1;
+	unsigned int setband_vendor_cmd_avail:1;
+	unsigned int get_pref_freq_list:1;
+	unsigned int set_prob_oper_freq:1;
+
+	u64 remain_on_chan_cookie;
+	u64 send_action_cookie;
+
+	unsigned int last_mgmt_freq;
+
+	struct wpa_driver_scan_filter *filter_ssids;
+	size_t num_filter_ssids;
+
+	struct i802_bss *first_bss;
+
+	int eapol_tx_sock;
+
+	int eapol_sock; /* socket for EAPOL frames */
+
+	struct nl_handle *rtnl_sk; /* nl_sock for NETLINK_ROUTE */
+
+	int default_if_indices[16];
+	int *if_indices;
+	int num_if_indices;
+
+	/* From failed authentication command */
+	int auth_freq;
+	u8 auth_bssid_[ETH_ALEN];
+	u8 auth_ssid[SSID_MAX_LEN];
+	size_t auth_ssid_len;
+	int auth_alg;
+	u8 *auth_ie;
+	size_t auth_ie_len;
+	u8 auth_wep_key[4][16];
+	size_t auth_wep_key_len[4];
+	int auth_wep_tx_keyidx;
+	int auth_local_state_change;
+	int auth_p2p;
+};
+
+struct nl_msg;
+
+void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
+		   struct nl_msg *msg, int flags, uint8_t cmd);
+struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd);
+struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags,
+				uint8_t cmd);
+struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd);
+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);
+int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+			 const char *ifname, enum nl80211_iftype iftype,
+			 const u8 *addr, int wds,
+			 int (*handler)(struct nl_msg *, void *),
+			 void *arg, int use_existing);
+void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx);
+unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv);
+enum chan_width convert2width(int width);
+void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv);
+struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
+				  int ifindex);
+int is_ap_interface(enum nl80211_iftype nlmode);
+int is_sta_interface(enum nl80211_iftype nlmode);
+int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv);
+int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+			    struct wpa_signal_info *sig);
+int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+			   struct wpa_signal_info *sig_change);
+int nl80211_get_wiphy_index(struct i802_bss *bss);
+int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+				enum nl80211_iftype nlmode);
+int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+			    const u8 *addr, int cmd, u16 reason_code,
+			    int local_state_change);
+
+int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv);
+void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv);
+int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
+			 const void *data, size_t len,
+			 int encrypt, int noack);
+
+int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv);
+struct hostapd_hw_modes *
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags);
+
+int process_global_event(struct nl_msg *msg, void *arg);
+int process_bss_event(struct nl_msg *msg, void *arg);
+
+#ifdef ANDROID
+int android_nl_socket_set_nonblocking(struct nl_handle *handle);
+int android_pno_start(struct i802_bss *bss,
+		      struct wpa_driver_scan_params *params);
+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);
+
+#ifdef ANDROID_P2P
+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);
+#endif /* ANDROID_P2P */
+#endif /* ANDROID */
+
+
+/* driver_nl80211_scan.c */
+
+struct nl80211_bss_info_arg {
+	struct wpa_driver_nl80211_data *drv;
+	struct wpa_scan_results *res;
+	unsigned int assoc_freq;
+	unsigned int ibss_freq;
+	u8 assoc_bssid[ETH_ALEN];
+};
+
+int bss_info_handler(struct nl_msg *msg, void *arg);
+void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+int wpa_driver_nl80211_scan(struct i802_bss *bss,
+			    struct wpa_driver_scan_params *params);
+int wpa_driver_nl80211_sched_scan(void *priv,
+				  struct wpa_driver_scan_params *params,
+				  u32 interval);
+int wpa_driver_nl80211_stop_sched_scan(void *priv);
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
+void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
+const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie);
+
+#endif /* DRIVER_NL80211_H */
diff --git a/hostap/src/drivers/driver_nl80211_android.c b/hostap/src/drivers/driver_nl80211_android.c
new file mode 100644
index 0000000..ba47888
--- /dev/null
+++ b/hostap/src/drivers/driver_nl80211_android.c
@@ -0,0 +1,190 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Android specific
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * 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>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <fcntl.h>
+
+#include "utils/common.h"
+#include "driver_nl80211.h"
+#include "android_drv.h"
+
+
+typedef struct android_wifi_priv_cmd {
+	char *buf;
+	int used_len;
+	int total_len;
+} android_wifi_priv_cmd;
+
+static int drv_errors = 0;
+
+static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv)
+{
+	drv_errors++;
+	if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
+		drv_errors = 0;
+		wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
+	}
+}
+
+
+static int android_priv_cmd(struct i802_bss *bss, const char *cmd)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ifreq ifr;
+	android_wifi_priv_cmd priv_cmd;
+	char buf[MAX_DRV_CMD_SIZE];
+	int ret;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_memset(&priv_cmd, 0, sizeof(priv_cmd));
+	os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+
+	os_memset(buf, 0, sizeof(buf));
+	os_strlcpy(buf, cmd, sizeof(buf));
+
+	priv_cmd.buf = buf;
+	priv_cmd.used_len = sizeof(buf);
+	priv_cmd.total_len = sizeof(buf);
+	ifr.ifr_data = &priv_cmd;
+
+	ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to issue private commands",
+			   __func__);
+		wpa_driver_send_hang_msg(drv);
+		return ret;
+	}
+
+	drv_errors = 0;
+	return 0;
+}
+
+
+int android_pno_start(struct i802_bss *bss,
+		      struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ifreq ifr;
+	android_wifi_priv_cmd priv_cmd;
+	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 + MAX_SSID_LEN +
+		     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;
+	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;
+
+	memset(&ifr, 0, sizeof(ifr));
+	memset(&priv_cmd, 0, sizeof(priv_cmd));
+	os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+
+	priv_cmd.buf = buf;
+	priv_cmd.used_len = bp;
+	priv_cmd.total_len = bp;
+	ifr.ifr_data = &priv_cmd;
+
+	ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
+
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d",
+			   ret);
+		wpa_driver_send_hang_msg(drv);
+		return ret;
+	}
+
+	drv_errors = 0;
+
+	return android_priv_cmd(bss, "PNOFORCE 1");
+}
+
+
+int android_pno_stop(struct i802_bss *bss)
+{
+	return android_priv_cmd(bss, "PNOFORCE 0");
+}
+
+
+#ifdef ANDROID_P2P
+#ifdef ANDROID_LIB_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;
+}
+
+#endif /* ANDROID_LIB_STUB */
+#endif /* ANDROID_P2P */
+
+
+int android_nl_socket_set_nonblocking(struct nl_handle *handle)
+{
+	return fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK);
+}
+
+
diff --git a/hostap/src/drivers/driver_nl80211_capa.c b/hostap/src/drivers/driver_nl80211_capa.c
new file mode 100644
index 0000000..4cf3123
--- /dev/null
+++ b/hostap/src/drivers/driver_nl80211_capa.c
@@ -0,0 +1,1590 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Capabilities
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/qca-vendor.h"
+#include "common/qca-vendor-attr.h"
+#include "driver_nl80211.h"
+
+
+static int protocol_feature_handler(struct nl_msg *msg, void *arg)
+{
+	u32 *feat = 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_PROTOCOL_FEATURES])
+		*feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]);
+
+	return NL_SKIP;
+}
+
+
+static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
+{
+	u32 feat = 0;
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return 0;
+
+	if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES)) {
+		nlmsg_free(msg);
+		return 0;
+	}
+
+	if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0)
+		return feat;
+
+	return 0;
+}
+
+
+struct wiphy_info_data {
+	struct wpa_driver_nl80211_data *drv;
+	struct wpa_driver_capa *capa;
+
+	unsigned int num_multichan_concurrent;
+
+	unsigned int error:1;
+	unsigned int device_ap_sme:1;
+	unsigned int poll_command_supported:1;
+	unsigned int data_tx_status:1;
+	unsigned int monitor_supported:1;
+	unsigned int auth_supported:1;
+	unsigned int connect_supported:1;
+	unsigned int p2p_go_supported:1;
+	unsigned int p2p_client_supported:1;
+	unsigned int p2p_go_ctwindow_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;
+	unsigned int wmm_ac_supported:1;
+	unsigned int mac_addr_rand_scan_supported:1;
+	unsigned int mac_addr_rand_sched_scan_supported:1;
+};
+
+
+static unsigned int probe_resp_offload_support(int supp_protocols)
+{
+	unsigned int prot = 0;
+
+	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS)
+		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS;
+	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2)
+		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2;
+	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P)
+		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P;
+	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U)
+		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING;
+
+	return prot;
+}
+
+
+static void wiphy_info_supported_iftypes(struct wiphy_info_data *info,
+					 struct nlattr *tb)
+{
+	struct nlattr *nl_mode;
+	int i;
+
+	if (tb == NULL)
+		return;
+
+	nla_for_each_nested(nl_mode, tb, i) {
+		switch (nla_type(nl_mode)) {
+		case NL80211_IFTYPE_AP:
+			info->capa->flags |= WPA_DRIVER_FLAGS_AP;
+			break;
+		case NL80211_IFTYPE_MESH_POINT:
+			info->capa->flags |= WPA_DRIVER_FLAGS_MESH;
+			break;
+		case NL80211_IFTYPE_ADHOC:
+			info->capa->flags |= WPA_DRIVER_FLAGS_IBSS;
+			break;
+		case NL80211_IFTYPE_P2P_DEVICE:
+			info->capa->flags |=
+				WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE;
+			break;
+		case NL80211_IFTYPE_P2P_GO:
+			info->p2p_go_supported = 1;
+			break;
+		case NL80211_IFTYPE_P2P_CLIENT:
+			info->p2p_client_supported = 1;
+			break;
+		case NL80211_IFTYPE_MONITOR:
+			info->monitor_supported = 1;
+			break;
+		}
+	}
+}
+
+
+static int wiphy_info_iface_comb_process(struct wiphy_info_data *info,
+					 struct nlattr *nl_combi)
+{
+	struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
+	struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT];
+	struct nlattr *nl_limit, *nl_mode;
+	int err, rem_limit, rem_mode;
+	int combination_has_p2p = 0, combination_has_mgd = 0;
+	static struct nla_policy
+	iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
+		[NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
+		[NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
+		[NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG },
+		[NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 },
+		[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 },
+	},
+	iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
+		[NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
+		[NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
+	};
+
+	err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB,
+			       nl_combi, iface_combination_policy);
+	if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] ||
+	    !tb_comb[NL80211_IFACE_COMB_MAXNUM] ||
+	    !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS])
+		return 0; /* broken combination */
+
+	if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS])
+		info->capa->flags |= WPA_DRIVER_FLAGS_RADAR;
+
+	nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS],
+			    rem_limit) {
+		err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT,
+				       nl_limit, iface_limit_policy);
+		if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES])
+			return 0; /* broken combination */
+
+		nla_for_each_nested(nl_mode,
+				    tb_limit[NL80211_IFACE_LIMIT_TYPES],
+				    rem_mode) {
+			int ift = nla_type(nl_mode);
+			if (ift == NL80211_IFTYPE_P2P_GO ||
+			    ift == NL80211_IFTYPE_P2P_CLIENT)
+				combination_has_p2p = 1;
+			if (ift == NL80211_IFTYPE_STATION)
+				combination_has_mgd = 1;
+		}
+		if (combination_has_p2p && combination_has_mgd)
+			break;
+	}
+
+	if (combination_has_p2p && combination_has_mgd) {
+		unsigned int num_channels =
+			nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]);
+
+		info->p2p_concurrent = 1;
+		if (info->num_multichan_concurrent < num_channels)
+			info->num_multichan_concurrent = num_channels;
+	}
+
+	return 0;
+}
+
+
+static void wiphy_info_iface_comb(struct wiphy_info_data *info,
+				  struct nlattr *tb)
+{
+	struct nlattr *nl_combi;
+	int rem_combi;
+
+	if (tb == NULL)
+		return;
+
+	nla_for_each_nested(nl_combi, tb, rem_combi) {
+		if (wiphy_info_iface_comb_process(info, nl_combi) > 0)
+			break;
+	}
+}
+
+
+static void wiphy_info_supp_cmds(struct wiphy_info_data *info,
+				 struct nlattr *tb)
+{
+	struct nlattr *nl_cmd;
+	int i;
+
+	if (tb == NULL)
+		return;
+
+	nla_for_each_nested(nl_cmd, tb, i) {
+		switch (nla_get_u32(nl_cmd)) {
+		case NL80211_CMD_AUTHENTICATE:
+			info->auth_supported = 1;
+			break;
+		case NL80211_CMD_CONNECT:
+			info->connect_supported = 1;
+			break;
+		case NL80211_CMD_START_SCHED_SCAN:
+			info->capa->sched_scan_supported = 1;
+			break;
+		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;
+		}
+	}
+}
+
+
+static void wiphy_info_max_roc(struct wpa_driver_capa *capa,
+			       struct nlattr *tb)
+{
+	if (tb)
+		capa->max_remain_on_chan = nla_get_u32(tb);
+}
+
+
+static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls,
+			    struct nlattr *ext_setup)
+{
+	if (tdls == NULL)
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
+	capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
+
+	if (ext_setup) {
+		wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
+		capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
+	}
+}
+
+
+static int ext_feature_isset(const u8 *ext_features, int ext_features_len,
+			     enum nl80211_ext_feature_index ftidx)
+{
+	u8 ft_byte;
+
+	if ((int) ftidx / 8 >= ext_features_len)
+		return 0;
+
+	ft_byte = ext_features[ftidx / 8];
+	return (ft_byte & BIT(ftidx % 8)) != 0;
+}
+
+
+static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
+					 struct nlattr *tb)
+{
+	struct wpa_driver_capa *capa = info->capa;
+
+	if (tb == NULL)
+		return;
+
+	if (ext_feature_isset(nla_data(tb), nla_len(tb),
+			      NL80211_EXT_FEATURE_VHT_IBSS))
+		capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS;
+}
+
+
+static void wiphy_info_feature_flags(struct wiphy_info_data *info,
+				     struct nlattr *tb)
+{
+	u32 flags;
+	struct wpa_driver_capa *capa = info->capa;
+
+	if (tb == NULL)
+		return;
+
+	flags = nla_get_u32(tb);
+
+	if (flags & NL80211_FEATURE_SK_TX_STATUS)
+		info->data_tx_status = 1;
+
+	if (flags & NL80211_FEATURE_INACTIVITY_TIMER)
+		capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER;
+
+	if (flags & NL80211_FEATURE_SAE)
+		capa->flags |= WPA_DRIVER_FLAGS_SAE;
+
+	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_TDLS_CHANNEL_SWITCH) {
+		wpa_printf(MSG_DEBUG, "nl80211: TDLS channel switch");
+		capa->flags |= WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH;
+	}
+
+	if (flags & NL80211_FEATURE_P2P_GO_CTWIN)
+		info->p2p_go_ctwindow_supported = 1;
+
+	if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN)
+		info->have_low_prio_scan = 1;
+
+	if (flags & NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)
+		info->mac_addr_rand_scan_supported = 1;
+
+	if (flags & NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR)
+		info->mac_addr_rand_sched_scan_supported = 1;
+
+	if (flags & NL80211_FEATURE_STATIC_SMPS)
+		capa->smps_modes |= WPA_DRIVER_SMPS_MODE_STATIC;
+
+	if (flags & NL80211_FEATURE_DYNAMIC_SMPS)
+		capa->smps_modes |= WPA_DRIVER_SMPS_MODE_DYNAMIC;
+
+	if (flags & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
+		info->wmm_ac_supported = 1;
+
+	if (flags & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES)
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES;
+
+	if (flags & NL80211_FEATURE_WFA_TPC_IE_IN_PROBES)
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES;
+
+	if (flags & NL80211_FEATURE_QUIET)
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_QUIET;
+
+	if (flags & NL80211_FEATURE_TX_POWER_INSERTION)
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_TX_POWER_INSERTION;
+
+	if (flags & NL80211_FEATURE_HT_IBSS)
+		capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS;
+}
+
+
+static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa,
+					  struct nlattr *tb)
+{
+	u32 protocols;
+
+	if (tb == NULL)
+		return;
+
+	protocols = nla_get_u32(tb);
+	wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP "
+		   "mode");
+	capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD;
+	capa->probe_resp_offloads = probe_resp_offload_support(protocols);
+}
+
+
+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];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wiphy_info_data *info = arg;
+	struct wpa_driver_capa *capa = info->capa;
+	struct wpa_driver_nl80211_data *drv = info->drv;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL80211_ATTR_WIPHY_NAME])
+		os_strlcpy(drv->phyname,
+			   nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
+			   sizeof(drv->phyname));
+	if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
+		capa->max_scan_ssids =
+			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
+
+	if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
+		capa->max_sched_scan_ssids =
+			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
+
+	if (tb[NL80211_ATTR_MAX_MATCH_SETS])
+		capa->max_match_sets =
+			nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
+
+	if (tb[NL80211_ATTR_MAC_ACL_MAX])
+		capa->max_acl_mac_addrs =
+			nla_get_u8(tb[NL80211_ATTR_MAC_ACL_MAX]);
+
+	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 "
+			   "off-channel TX");
+		capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+	}
+
+	if (tb[NL80211_ATTR_ROAM_SUPPORT]) {
+		wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming");
+		capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
+	}
+
+	wiphy_info_max_roc(capa,
+			   tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
+
+	if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
+		capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
+
+	wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT],
+			tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]);
+
+	if (tb[NL80211_ATTR_DEVICE_AP_SME])
+		info->device_ap_sme = 1;
+
+	wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]);
+	wiphy_info_ext_feature_flags(info, tb[NL80211_ATTR_EXT_FEATURES]);
+	wiphy_info_probe_resp_offload(capa,
+				      tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
+
+	if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] &&
+	    drv->extended_capa == NULL) {
+		drv->extended_capa =
+			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+		if (drv->extended_capa) {
+			os_memcpy(drv->extended_capa,
+				  nla_data(tb[NL80211_ATTR_EXT_CAPA]),
+				  nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+			drv->extended_capa_len =
+				nla_len(tb[NL80211_ATTR_EXT_CAPA]);
+		}
+		drv->extended_capa_mask =
+			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
+		if (drv->extended_capa_mask) {
+			os_memcpy(drv->extended_capa_mask,
+				  nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]),
+				  nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
+		} else {
+			os_free(drv->extended_capa);
+			drv->extended_capa = NULL;
+			drv->extended_capa_len = 0;
+		}
+	}
+
+	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->vendor_id == OUI_QCA) {
+				switch (vinfo->subcmd) {
+				case QCA_NL80211_VENDOR_SUBCMD_TEST:
+					drv->vendor_cmd_test_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_ROAMING:
+					drv->roaming_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY:
+					drv->dfs_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
+					drv->get_features_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST:
+					drv->get_pref_freq_list = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL:
+					drv->set_prob_oper_freq = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
+					drv->capa.flags |=
+						WPA_DRIVER_FLAGS_ACS_OFFLOAD;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_SETBAND:
+					drv->setband_vendor_cmd_avail = 1;
+					break;
+				}
+			}
+
+			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;
+}
+
+
+static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
+				       struct wiphy_info_data *info)
+{
+	u32 feat;
+	struct nl_msg *msg;
+	int flags = 0;
+
+	os_memset(info, 0, sizeof(*info));
+	info->capa = &drv->capa;
+	info->drv = drv;
+
+	feat = get_nl80211_protocol_features(drv);
+	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+		flags = NLM_F_DUMP;
+	msg = nl80211_cmd_msg(drv->first_bss, flags, NL80211_CMD_GET_WIPHY);
+	if (!msg || nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info))
+		return -1;
+
+	if (info->auth_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
+	else if (!info->connect_supported) {
+		wpa_printf(MSG_INFO, "nl80211: Driver does not support "
+			   "authentication/association or connect commands");
+		info->error = 1;
+	}
+
+	if (info->p2p_go_supported && info->p2p_client_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+	if (info->p2p_concurrent) {
+		wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+			   "interface (driver advertised support)");
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+	}
+	if (info->num_multichan_concurrent > 1) {
+		wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel "
+			   "concurrent (driver advertised support)");
+		drv->capa.num_multichan_concurrent =
+			info->num_multichan_concurrent;
+	}
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)
+		wpa_printf(MSG_DEBUG, "nl80211: use P2P_DEVICE support");
+
+	/* default to 5000 since early versions of mac80211 don't set it */
+	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;
+	drv->capa.wmm_ac_supported = info->wmm_ac_supported;
+
+	drv->capa.mac_addr_rand_sched_scan_supported =
+		info->mac_addr_rand_sched_scan_supported;
+	drv->capa.mac_addr_rand_scan_supported =
+		info->mac_addr_rand_scan_supported;
+
+	return 0;
+}
+
+
+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 void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	int dfs_capability = 0;
+	int ret;
+
+	if (!drv->dfs_vendor_cmd_avail)
+		return;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY)) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability);
+	if (!ret && dfs_capability)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
+}
+
+
+struct features_info {
+	u8 *flags;
+	size_t flags_len;
+	struct wpa_driver_capa *capa;
+};
+
+
+static int features_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct features_info *info = arg;
+	struct nlattr *nl_vend, *attr;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+	if (nl_vend) {
+		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);
+
+		attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS];
+		if (attr) {
+			info->flags = nla_data(attr);
+			info->flags_len = nla_len(attr);
+		}
+		attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA];
+		if (attr)
+			info->capa->conc_capab = nla_get_u32(attr);
+
+		attr = tb_vendor[
+			QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND];
+		if (attr)
+			info->capa->max_conc_chan_2_4 = nla_get_u32(attr);
+
+		attr = tb_vendor[
+			QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND];
+		if (attr)
+			info->capa->max_conc_chan_5_0 = nla_get_u32(attr);
+	}
+
+	return NL_SKIP;
+}
+
+
+static int check_feature(enum qca_wlan_vendor_features feature,
+			 struct features_info *info)
+{
+	size_t idx = feature / 8;
+
+	return (idx < info->flags_len) &&
+		(info->flags[idx] & BIT(feature % 8));
+}
+
+
+static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	struct features_info info;
+	int ret;
+
+	if (!drv->get_features_vendor_cmd_avail)
+		return;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES)) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	os_memset(&info, 0, sizeof(info));
+	info.capa = &drv->capa;
+	ret = send_and_recv_msgs(drv, msg, features_info_handler, &info);
+	if (ret || !info.flags)
+		return;
+
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD;
+
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY;
+}
+
+
+int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
+{
+	struct wiphy_info_data info;
+	if (wpa_driver_nl80211_get_info(drv, &info))
+		return -1;
+
+	if (info.error)
+		return -1;
+
+	drv->has_capability = 1;
+	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 |
+		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
+		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+	drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+		WPA_DRIVER_AUTH_SHARED |
+		WPA_DRIVER_AUTH_LEAP;
+
+	drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
+	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;
+
+		/*
+		 * No AP SME is currently assumed to also indicate no AP MLME
+		 * in the driver/firmware.
+		 */
+		drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME;
+	}
+
+	drv->device_ap_sme = info.device_ap_sme;
+	drv->poll_command_supported = info.poll_command_supported;
+	drv->data_tx_status = info.data_tx_status;
+	drv->p2p_go_ctwindow_supported = info.p2p_go_ctwindow_supported;
+	if (info.set_qos_map_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING;
+	drv->have_low_prio_scan = info.have_low_prio_scan;
+
+	/*
+	 * If poll command and tx status are supported, mac80211 is new enough
+	 * to have everything we need to not need monitor interfaces.
+	 */
+	drv->use_monitor = !info.poll_command_supported || !info.data_tx_status;
+
+	if (drv->device_ap_sme && drv->use_monitor) {
+		/*
+		 * Non-mac80211 drivers may not support monitor interface.
+		 * Make sure we do not get stuck with incorrect capability here
+		 * by explicitly testing this.
+		 */
+		if (!info.monitor_supported) {
+			wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor "
+				   "with device_ap_sme since no monitor mode "
+				   "support detected");
+			drv->use_monitor = 0;
+		}
+	}
+
+	/*
+	 * If we aren't going to use monitor interfaces, but the
+	 * driver doesn't support data TX status, we won't get TX
+	 * status for EAPOL frames.
+	 */
+	if (!drv->use_monitor && !info.data_tx_status)
+		drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+
+	qca_nl80211_check_dfs_capa(drv);
+	qca_nl80211_get_features(drv);
+
+	return 0;
+}
+
+
+struct phy_info_arg {
+	u16 *num_modes;
+	struct hostapd_hw_modes *modes;
+	int last_mode, last_chan_idx;
+};
+
+static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
+			     struct nlattr *ampdu_factor,
+			     struct nlattr *ampdu_density,
+			     struct nlattr *mcs_set)
+{
+	if (capa)
+		mode->ht_capab = nla_get_u16(capa);
+
+	if (ampdu_factor)
+		mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03;
+
+	if (ampdu_density)
+		mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2;
+
+	if (mcs_set && nla_len(mcs_set) >= 16) {
+		u8 *mcs;
+		mcs = nla_data(mcs_set);
+		os_memcpy(mode->mcs_set, mcs, 16);
+	}
+}
+
+
+static void phy_info_vht_capa(struct hostapd_hw_modes *mode,
+			      struct nlattr *capa,
+			      struct nlattr *mcs_set)
+{
+	if (capa)
+		mode->vht_capab = nla_get_u32(capa);
+
+	if (mcs_set && nla_len(mcs_set) >= 8) {
+		u8 *mcs;
+		mcs = nla_data(mcs_set);
+		os_memcpy(mode->vht_mcs_set, mcs, 8);
+	}
+}
+
+
+static void phy_info_freq(struct hostapd_hw_modes *mode,
+			  struct hostapd_channel_data *chan,
+			  struct nlattr *tb_freq[])
+{
+	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_NO_IR])
+		chan->flag |= HOSTAPD_CHAN_NO_IR;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
+		chan->flag |= HOSTAPD_CHAN_RADAR;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_INDOOR_ONLY])
+		chan->flag |= HOSTAPD_CHAN_INDOOR_ONLY;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_GO_CONCURRENT])
+		chan->flag |= HOSTAPD_CHAN_GO_CONCURRENT;
+
+	if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
+		enum nl80211_dfs_state state =
+			nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
+
+		switch (state) {
+		case NL80211_DFS_USABLE:
+			chan->flag |= HOSTAPD_CHAN_DFS_USABLE;
+			break;
+		case NL80211_DFS_AVAILABLE:
+			chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE;
+			break;
+		case NL80211_DFS_UNAVAILABLE:
+			chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE;
+			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]);
+	}
+}
+
+
+static int phy_info_freqs(struct phy_info_arg *phy_info,
+			  struct hostapd_hw_modes *mode, struct nlattr *tb)
+{
+	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_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 },
+	};
+	int new_channels = 0;
+	struct hostapd_channel_data *channel;
+	struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
+	struct nlattr *nl_freq;
+	int rem_freq, idx;
+
+	if (tb == NULL)
+		return NL_OK;
+
+	nla_for_each_nested(nl_freq, tb, rem_freq) {
+		nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
+			  nla_data(nl_freq), nla_len(nl_freq), freq_policy);
+		if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+			continue;
+		new_channels++;
+	}
+
+	channel = os_realloc_array(mode->channels,
+				   mode->num_channels + new_channels,
+				   sizeof(struct hostapd_channel_data));
+	if (!channel)
+		return NL_SKIP;
+
+	mode->channels = channel;
+	mode->num_channels += new_channels;
+
+	idx = phy_info->last_chan_idx;
+
+	nla_for_each_nested(nl_freq, tb, rem_freq) {
+		nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
+			  nla_data(nl_freq), nla_len(nl_freq), freq_policy);
+		if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+			continue;
+		phy_info_freq(mode, &mode->channels[idx], tb_freq);
+		idx++;
+	}
+	phy_info->last_chan_idx = idx;
+
+	return NL_OK;
+}
+
+
+static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb)
+{
+	static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
+		[NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
+		[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] =
+		{ .type = NLA_FLAG },
+	};
+	struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
+	struct nlattr *nl_rate;
+	int rem_rate, idx;
+
+	if (tb == NULL)
+		return NL_OK;
+
+	nla_for_each_nested(nl_rate, tb, rem_rate) {
+		nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
+			  nla_data(nl_rate), nla_len(nl_rate),
+			  rate_policy);
+		if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+			continue;
+		mode->num_rates++;
+	}
+
+	mode->rates = os_calloc(mode->num_rates, sizeof(int));
+	if (!mode->rates)
+		return NL_SKIP;
+
+	idx = 0;
+
+	nla_for_each_nested(nl_rate, tb, rem_rate) {
+		nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
+			  nla_data(nl_rate), nla_len(nl_rate),
+			  rate_policy);
+		if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+			continue;
+		mode->rates[idx] = nla_get_u32(
+			tb_rate[NL80211_BITRATE_ATTR_RATE]);
+		idx++;
+	}
+
+	return NL_OK;
+}
+
+
+static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
+{
+	struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
+	struct hostapd_hw_modes *mode;
+	int ret;
+
+	if (phy_info->last_mode != nl_band->nla_type) {
+		mode = os_realloc_array(phy_info->modes,
+					*phy_info->num_modes + 1,
+					sizeof(*mode));
+		if (!mode)
+			return NL_SKIP;
+		phy_info->modes = mode;
+
+		mode = &phy_info->modes[*(phy_info->num_modes)];
+		os_memset(mode, 0, sizeof(*mode));
+		mode->mode = NUM_HOSTAPD_MODES;
+		mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN |
+			HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN;
+
+		/*
+		 * Unsupported VHT MCS stream is defined as value 3, so the VHT
+		 * MCS RX/TX map must be initialized with 0xffff to mark all 8
+		 * possible streams as unsupported. This will be overridden if
+		 * driver advertises VHT support.
+		 */
+		mode->vht_mcs_set[0] = 0xff;
+		mode->vht_mcs_set[1] = 0xff;
+		mode->vht_mcs_set[4] = 0xff;
+		mode->vht_mcs_set[5] = 0xff;
+
+		*(phy_info->num_modes) += 1;
+		phy_info->last_mode = nl_band->nla_type;
+		phy_info->last_chan_idx = 0;
+	} else
+		mode = &phy_info->modes[*(phy_info->num_modes) - 1];
+
+	nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
+		  nla_len(nl_band), NULL);
+
+	phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA],
+			 tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR],
+			 tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY],
+			 tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
+	phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
+			  tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
+	ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]);
+	if (ret != NL_OK)
+		return ret;
+	ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
+	if (ret != NL_OK)
+		return ret;
+
+	return NL_OK;
+}
+
+
+static int phy_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct phy_info_arg *phy_info = arg;
+	struct nlattr *nl_band;
+	int rem_band;
+
+	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
+		return NL_SKIP;
+
+	nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band)
+	{
+		int res = phy_info_band(phy_info, nl_band);
+		if (res != NL_OK)
+			return res;
+	}
+
+	return NL_SKIP;
+}
+
+
+static struct hostapd_hw_modes *
+wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes,
+				     u16 *num_modes)
+{
+	u16 m;
+	struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode;
+	int i, mode11g_idx = -1;
+
+	/* heuristic to set up modes */
+	for (m = 0; m < *num_modes; m++) {
+		if (!modes[m].num_channels)
+			continue;
+		if (modes[m].channels[0].freq < 4000) {
+			modes[m].mode = HOSTAPD_MODE_IEEE80211B;
+			for (i = 0; i < modes[m].num_rates; i++) {
+				if (modes[m].rates[i] > 200) {
+					modes[m].mode = HOSTAPD_MODE_IEEE80211G;
+					break;
+				}
+			}
+		} else if (modes[m].channels[0].freq > 50000)
+			modes[m].mode = HOSTAPD_MODE_IEEE80211AD;
+		else
+			modes[m].mode = HOSTAPD_MODE_IEEE80211A;
+	}
+
+	/* If only 802.11g mode is included, use it to construct matching
+	 * 802.11b mode data. */
+
+	for (m = 0; m < *num_modes; m++) {
+		if (modes[m].mode == HOSTAPD_MODE_IEEE80211B)
+			return modes; /* 802.11b already included */
+		if (modes[m].mode == HOSTAPD_MODE_IEEE80211G)
+			mode11g_idx = m;
+	}
+
+	if (mode11g_idx < 0)
+		return modes; /* 2.4 GHz band not supported at all */
+
+	nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes));
+	if (nmodes == NULL)
+		return modes; /* Could not add 802.11b mode */
+
+	mode = &nmodes[*num_modes];
+	os_memset(mode, 0, sizeof(*mode));
+	(*num_modes)++;
+	modes = nmodes;
+
+	mode->mode = HOSTAPD_MODE_IEEE80211B;
+
+	mode11g = &modes[mode11g_idx];
+	mode->num_channels = mode11g->num_channels;
+	mode->channels = os_malloc(mode11g->num_channels *
+				   sizeof(struct hostapd_channel_data));
+	if (mode->channels == NULL) {
+		(*num_modes)--;
+		return modes; /* Could not add 802.11b mode */
+	}
+	os_memcpy(mode->channels, mode11g->channels,
+		  mode11g->num_channels * sizeof(struct hostapd_channel_data));
+
+	mode->num_rates = 0;
+	mode->rates = os_malloc(4 * sizeof(int));
+	if (mode->rates == NULL) {
+		os_free(mode->channels);
+		(*num_modes)--;
+		return modes; /* Could not add 802.11b mode */
+	}
+
+	for (i = 0; i < mode11g->num_rates; i++) {
+		if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 &&
+		    mode11g->rates[i] != 55 && mode11g->rates[i] != 110)
+			continue;
+		mode->rates[mode->num_rates] = mode11g->rates[i];
+		mode->num_rates++;
+		if (mode->num_rates == 4)
+			break;
+	}
+
+	if (mode->num_rates == 0) {
+		os_free(mode->channels);
+		os_free(mode->rates);
+		(*num_modes)--;
+		return modes; /* No 802.11b rates */
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g "
+		   "information");
+
+	return modes;
+}
+
+
+static void nl80211_set_ht40_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 + 10 <= end)
+			chan->flag |= HOSTAPD_CHAN_HT40;
+	}
+}
+
+
+static void nl80211_set_ht40_mode_sec(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->flag & HOSTAPD_CHAN_HT40))
+			continue;
+		if (chan->freq - 30 >= start && chan->freq - 10 <= end)
+			chan->flag |= HOSTAPD_CHAN_HT40MINUS;
+		if (chan->freq + 10 >= start && chan->freq + 30 <= end)
+			chan->flag |= HOSTAPD_CHAN_HT40PLUS;
+	}
+}
+
+
+static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp,
+				      struct phy_info_arg *results)
+{
+	u16 m;
+
+	for (m = 0; m < *results->num_modes; m++) {
+		int c;
+		struct hostapd_hw_modes *mode = &results->modes[m];
+
+		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;
+		}
+	}
+}
+
+
+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 &
+		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+			continue;
+		nl80211_set_ht40_mode(&results->modes[m], start, end);
+	}
+}
+
+
+static void nl80211_reg_rule_sec(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 < 20)
+		return;
+
+	for (m = 0; m < *results->num_modes; m++) {
+		if (!(results->modes[m].ht_capab &
+		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+			continue;
+		nl80211_set_ht40_mode_sec(&results->modes[m], start, end);
+	}
+}
+
+
+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;
+	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *nl_rule;
+	struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1];
+	int rem_rule;
+	static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+		[NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
+		[NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
+		[NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
+		[NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
+		[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
+		[NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
+	};
+
+	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb_msg[NL80211_ATTR_REG_ALPHA2] ||
+	    !tb_msg[NL80211_ATTR_REG_RULES]) {
+		wpa_printf(MSG_DEBUG, "nl80211: No regulatory information "
+			   "available");
+		return NL_SKIP;
+	}
+
+	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);
+		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)
+	{
+		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+		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_regulatory_flags(struct wpa_driver_nl80211_data *drv,
+					struct phy_info_arg *results)
+{
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
+	return send_and_recv_msgs(drv, msg, nl80211_get_reg, results);
+}
+
+
+struct hostapd_hw_modes *
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
+{
+	u32 feat;
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int nl_flags = 0;
+	struct nl_msg *msg;
+	struct phy_info_arg result = {
+		.num_modes = num_modes,
+		.modes = NULL,
+		.last_mode = -1,
+	};
+
+	*num_modes = 0;
+	*flags = 0;
+
+	feat = get_nl80211_protocol_features(drv);
+	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+		nl_flags = NLM_F_DUMP;
+	if (!(msg = nl80211_cmd_msg(bss, nl_flags, NL80211_CMD_GET_WIPHY)) ||
+	    nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+		nlmsg_free(msg);
+		return NULL;
+	}
+
+	if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
+		nl80211_set_regulatory_flags(drv, &result);
+		return wpa_driver_nl80211_postprocess_modes(result.modes,
+							    num_modes);
+	}
+
+	return NULL;
+}
diff --git a/hostap/src/drivers/driver_nl80211_event.c b/hostap/src/drivers/driver_nl80211_event.c
new file mode 100644
index 0000000..7b0f721
--- /dev/null
+++ b/hostap/src/drivers/driver_nl80211_event.c
@@ -0,0 +1,2092 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Event processing
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.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 "driver_nl80211.h"
+
+
+static const char * nl80211_command_to_string(enum nl80211_commands cmd)
+{
+#define C2S(x) case x: return #x;
+	switch (cmd) {
+	C2S(NL80211_CMD_UNSPEC)
+	C2S(NL80211_CMD_GET_WIPHY)
+	C2S(NL80211_CMD_SET_WIPHY)
+	C2S(NL80211_CMD_NEW_WIPHY)
+	C2S(NL80211_CMD_DEL_WIPHY)
+	C2S(NL80211_CMD_GET_INTERFACE)
+	C2S(NL80211_CMD_SET_INTERFACE)
+	C2S(NL80211_CMD_NEW_INTERFACE)
+	C2S(NL80211_CMD_DEL_INTERFACE)
+	C2S(NL80211_CMD_GET_KEY)
+	C2S(NL80211_CMD_SET_KEY)
+	C2S(NL80211_CMD_NEW_KEY)
+	C2S(NL80211_CMD_DEL_KEY)
+	C2S(NL80211_CMD_GET_BEACON)
+	C2S(NL80211_CMD_SET_BEACON)
+	C2S(NL80211_CMD_START_AP)
+	C2S(NL80211_CMD_STOP_AP)
+	C2S(NL80211_CMD_GET_STATION)
+	C2S(NL80211_CMD_SET_STATION)
+	C2S(NL80211_CMD_NEW_STATION)
+	C2S(NL80211_CMD_DEL_STATION)
+	C2S(NL80211_CMD_GET_MPATH)
+	C2S(NL80211_CMD_SET_MPATH)
+	C2S(NL80211_CMD_NEW_MPATH)
+	C2S(NL80211_CMD_DEL_MPATH)
+	C2S(NL80211_CMD_SET_BSS)
+	C2S(NL80211_CMD_SET_REG)
+	C2S(NL80211_CMD_REQ_SET_REG)
+	C2S(NL80211_CMD_GET_MESH_CONFIG)
+	C2S(NL80211_CMD_SET_MESH_CONFIG)
+	C2S(NL80211_CMD_SET_MGMT_EXTRA_IE)
+	C2S(NL80211_CMD_GET_REG)
+	C2S(NL80211_CMD_GET_SCAN)
+	C2S(NL80211_CMD_TRIGGER_SCAN)
+	C2S(NL80211_CMD_NEW_SCAN_RESULTS)
+	C2S(NL80211_CMD_SCAN_ABORTED)
+	C2S(NL80211_CMD_REG_CHANGE)
+	C2S(NL80211_CMD_AUTHENTICATE)
+	C2S(NL80211_CMD_ASSOCIATE)
+	C2S(NL80211_CMD_DEAUTHENTICATE)
+	C2S(NL80211_CMD_DISASSOCIATE)
+	C2S(NL80211_CMD_MICHAEL_MIC_FAILURE)
+	C2S(NL80211_CMD_REG_BEACON_HINT)
+	C2S(NL80211_CMD_JOIN_IBSS)
+	C2S(NL80211_CMD_LEAVE_IBSS)
+	C2S(NL80211_CMD_TESTMODE)
+	C2S(NL80211_CMD_CONNECT)
+	C2S(NL80211_CMD_ROAM)
+	C2S(NL80211_CMD_DISCONNECT)
+	C2S(NL80211_CMD_SET_WIPHY_NETNS)
+	C2S(NL80211_CMD_GET_SURVEY)
+	C2S(NL80211_CMD_NEW_SURVEY_RESULTS)
+	C2S(NL80211_CMD_SET_PMKSA)
+	C2S(NL80211_CMD_DEL_PMKSA)
+	C2S(NL80211_CMD_FLUSH_PMKSA)
+	C2S(NL80211_CMD_REMAIN_ON_CHANNEL)
+	C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL)
+	C2S(NL80211_CMD_SET_TX_BITRATE_MASK)
+	C2S(NL80211_CMD_REGISTER_FRAME)
+	C2S(NL80211_CMD_FRAME)
+	C2S(NL80211_CMD_FRAME_TX_STATUS)
+	C2S(NL80211_CMD_SET_POWER_SAVE)
+	C2S(NL80211_CMD_GET_POWER_SAVE)
+	C2S(NL80211_CMD_SET_CQM)
+	C2S(NL80211_CMD_NOTIFY_CQM)
+	C2S(NL80211_CMD_SET_CHANNEL)
+	C2S(NL80211_CMD_SET_WDS_PEER)
+	C2S(NL80211_CMD_FRAME_WAIT_CANCEL)
+	C2S(NL80211_CMD_JOIN_MESH)
+	C2S(NL80211_CMD_LEAVE_MESH)
+	C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE)
+	C2S(NL80211_CMD_UNPROT_DISASSOCIATE)
+	C2S(NL80211_CMD_NEW_PEER_CANDIDATE)
+	C2S(NL80211_CMD_GET_WOWLAN)
+	C2S(NL80211_CMD_SET_WOWLAN)
+	C2S(NL80211_CMD_START_SCHED_SCAN)
+	C2S(NL80211_CMD_STOP_SCHED_SCAN)
+	C2S(NL80211_CMD_SCHED_SCAN_RESULTS)
+	C2S(NL80211_CMD_SCHED_SCAN_STOPPED)
+	C2S(NL80211_CMD_SET_REKEY_OFFLOAD)
+	C2S(NL80211_CMD_PMKSA_CANDIDATE)
+	C2S(NL80211_CMD_TDLS_OPER)
+	C2S(NL80211_CMD_TDLS_MGMT)
+	C2S(NL80211_CMD_UNEXPECTED_FRAME)
+	C2S(NL80211_CMD_PROBE_CLIENT)
+	C2S(NL80211_CMD_REGISTER_BEACONS)
+	C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME)
+	C2S(NL80211_CMD_SET_NOACK_MAP)
+	C2S(NL80211_CMD_CH_SWITCH_NOTIFY)
+	C2S(NL80211_CMD_START_P2P_DEVICE)
+	C2S(NL80211_CMD_STOP_P2P_DEVICE)
+	C2S(NL80211_CMD_CONN_FAILED)
+	C2S(NL80211_CMD_SET_MCAST_RATE)
+	C2S(NL80211_CMD_SET_MAC_ACL)
+	C2S(NL80211_CMD_RADAR_DETECT)
+	C2S(NL80211_CMD_GET_PROTOCOL_FEATURES)
+	C2S(NL80211_CMD_UPDATE_FT_IES)
+	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)
+	C2S(NL80211_CMD_ADD_TX_TS)
+	C2S(NL80211_CMD_DEL_TX_TS)
+	default:
+		return "NL80211_CMD_UNKNOWN";
+	}
+#undef C2S
+}
+
+
+static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
+			    const u8 *frame, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+	    drv->force_connect_cmd) {
+		/*
+		 * Avoid reporting two association events that would confuse
+		 * the core code.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore auth event when using driver SME");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Authenticate event");
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len < 24 + sizeof(mgmt->u.auth)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+			   "frame");
+		return;
+	}
+
+	os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN);
+	os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
+	os_memset(&event, 0, sizeof(event));
+	os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
+	event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+	event.auth.auth_transaction =
+		le_to_host16(mgmt->u.auth.auth_transaction);
+	event.auth.status_code = le_to_host16(mgmt->u.auth.status_code);
+	if (len > 24 + sizeof(mgmt->u.auth)) {
+		event.auth.ies = mgmt->u.auth.variable;
+		event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event);
+}
+
+
+static void nl80211_parse_wmm_params(struct nlattr *wmm_attr,
+				     struct wmm_params *wmm_params)
+{
+	struct nlattr *wmm_info[NL80211_STA_WME_MAX + 1];
+	static struct nla_policy wme_policy[NL80211_STA_WME_MAX + 1] = {
+		[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
+	};
+
+	if (!wmm_attr ||
+	    nla_parse_nested(wmm_info, NL80211_STA_WME_MAX, wmm_attr,
+			     wme_policy) ||
+	    !wmm_info[NL80211_STA_WME_UAPSD_QUEUES])
+		return;
+
+	wmm_params->uapsd_queues =
+		nla_get_u8(wmm_info[NL80211_STA_WME_UAPSD_QUEUES]);
+	wmm_params->info_bitmap |= WMM_PARAMS_UAPSD_QUEUES_INFO;
+}
+
+
+static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
+			    const u8 *frame, size_t len, struct nlattr *wmm)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	u16 status;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+	    drv->force_connect_cmd) {
+		/*
+		 * Avoid reporting two association events that would confuse
+		 * the core code.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore assoc event when using driver SME");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Associate event");
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len < 24 + sizeof(mgmt->u.assoc_resp)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+			   "frame");
+		return;
+	}
+
+	status = le_to_host16(mgmt->u.assoc_resp.status_code);
+	if (status != WLAN_STATUS_SUCCESS) {
+		os_memset(&event, 0, sizeof(event));
+		event.assoc_reject.bssid = mgmt->bssid;
+		if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
+			event.assoc_reject.resp_ies =
+				(u8 *) mgmt->u.assoc_resp.variable;
+			event.assoc_reject.resp_ies_len =
+				len - 24 - sizeof(mgmt->u.assoc_resp);
+		}
+		event.assoc_reject.status_code = status;
+
+		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+		return;
+	}
+
+	drv->associated = 1;
+	os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN);
+	os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN);
+
+	os_memset(&event, 0, sizeof(event));
+	if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
+		event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
+		event.assoc_info.resp_ies_len =
+			len - 24 - sizeof(mgmt->u.assoc_resp);
+	}
+
+	event.assoc_info.freq = drv->assoc_freq;
+
+	nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params);
+
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
+			       enum nl80211_commands cmd, struct nlattr *status,
+			       struct nlattr *addr, struct nlattr *req_ie,
+			       struct nlattr *resp_ie,
+			       struct nlattr *authorized,
+			       struct nlattr *key_replay_ctr,
+			       struct nlattr *ptk_kck,
+			       struct nlattr *ptk_kek)
+{
+	union wpa_event_data event;
+	const u8 *ssid;
+	u16 status_code;
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+		/*
+		 * Avoid reporting two association events that would confuse
+		 * the core code.
+		 */
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) "
+			   "when using userspace SME", cmd);
+		return;
+	}
+
+	status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS;
+
+	if (cmd == NL80211_CMD_CONNECT) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Connect event (status=%u ignore_next_local_disconnect=%d)",
+			   status_code, drv->ignore_next_local_disconnect);
+	} else if (cmd == NL80211_CMD_ROAM) {
+		wpa_printf(MSG_DEBUG, "nl80211: Roam event");
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	if (cmd == NL80211_CMD_CONNECT && status_code != WLAN_STATUS_SUCCESS) {
+		if (addr)
+			event.assoc_reject.bssid = nla_data(addr);
+		if (drv->ignore_next_local_disconnect) {
+			drv->ignore_next_local_disconnect = 0;
+			if (!event.assoc_reject.bssid ||
+			    (os_memcmp(event.assoc_reject.bssid,
+				       drv->auth_attempt_bssid,
+				       ETH_ALEN) != 0)) {
+				/*
+				 * Ignore the event that came without a BSSID or
+				 * for the old connection since this is likely
+				 * not relevant to the new Connect command.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Ignore connection failure event triggered during reassociation");
+				return;
+			}
+		}
+		if (resp_ie) {
+			event.assoc_reject.resp_ies = nla_data(resp_ie);
+			event.assoc_reject.resp_ies_len = nla_len(resp_ie);
+		}
+		event.assoc_reject.status_code = status_code;
+		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+		return;
+	}
+
+	drv->associated = 1;
+	if (addr) {
+		os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
+		os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
+	}
+
+	if (req_ie) {
+		event.assoc_info.req_ies = nla_data(req_ie);
+		event.assoc_info.req_ies_len = nla_len(req_ie);
+
+		if (cmd == NL80211_CMD_ROAM) {
+			ssid = nl80211_get_ie(event.assoc_info.req_ies,
+					      event.assoc_info.req_ies_len,
+					      WLAN_EID_SSID);
+			if (ssid && ssid[1] > 0 && ssid[1] <= 32) {
+				drv->ssid_len = ssid[1];
+				os_memcpy(drv->ssid, ssid + 2, ssid[1]);
+			}
+		}
+	}
+	if (resp_ie) {
+		event.assoc_info.resp_ies = nla_data(resp_ie);
+		event.assoc_info.resp_ies_len = nla_len(resp_ie);
+	}
+
+	event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+
+	if (authorized && nla_get_u8(authorized)) {
+		event.assoc_info.authorized = 1;
+		wpa_printf(MSG_DEBUG, "nl80211: connection authorized");
+	}
+	if (key_replay_ctr) {
+		event.assoc_info.key_replay_ctr = nla_data(key_replay_ctr);
+		event.assoc_info.key_replay_ctr_len = nla_len(key_replay_ctr);
+	}
+	if (ptk_kck) {
+		event.assoc_info.ptk_kck = nla_data(ptk_kck);
+		event.assoc_info.ptk_kck_len = nla_len(ptk_kck);
+	}
+	if (ptk_kek) {
+		event.assoc_info.ptk_kek = nla_data(ptk_kek);
+		event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
+				  struct nlattr *reason, struct nlattr *addr,
+				  struct nlattr *by_ap)
+{
+	union wpa_event_data data;
+	unsigned int locally_generated = by_ap == NULL;
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+		/*
+		 * Avoid reporting two disassociation events that could
+		 * confuse the core code.
+		 */
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+			   "event when using userspace SME");
+		return;
+	}
+
+	if (drv->ignore_next_local_disconnect) {
+		drv->ignore_next_local_disconnect = 0;
+		if (locally_generated) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+				   "event triggered during reassociation");
+			return;
+		}
+		wpa_printf(MSG_WARNING, "nl80211: Was expecting local "
+			   "disconnect but got another disconnect "
+			   "event first");
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Disconnect event");
+	nl80211_mark_disconnected(drv);
+	os_memset(&data, 0, sizeof(data));
+	if (reason)
+		data.deauth_info.reason_code = nla_get_u16(reason);
+	data.deauth_info.locally_generated = by_ap == NULL;
+	wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data);
+}
+
+
+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)
+		return;
+
+	ifidx = nla_get_u32(ifindex);
+	bss = get_bss_ifindex(drv, ifidx);
+	if (bss == NULL) {
+		wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring",
+			   ifidx);
+		return;
+	}
+
+	if (type) {
+		enum nl80211_channel_type ch_type = nla_get_u32(type);
+
+		wpa_printf(MSG_DEBUG, "nl80211: Channel type: %d", ch_type);
+		switch (ch_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);
+
+	bss->freq = data.ch_switch.freq;
+
+	wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data);
+}
+
+
+static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
+			       enum nl80211_commands cmd, struct nlattr *addr)
+{
+	union wpa_event_data event;
+	enum wpa_event_type ev;
+
+	if (nla_len(addr) != ETH_ALEN)
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR,
+		   cmd, MAC2STR((u8 *) nla_data(addr)));
+
+	if (cmd == NL80211_CMD_AUTHENTICATE)
+		ev = EVENT_AUTH_TIMED_OUT;
+	else if (cmd == NL80211_CMD_ASSOCIATE)
+		ev = EVENT_ASSOC_TIMED_OUT;
+	else
+		return;
+
+	os_memset(&event, 0, sizeof(event));
+	os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN);
+	wpa_supplicant_event(drv->ctx, ev, &event);
+}
+
+
+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 management frame");
+		return;
+	}
+
+	fc = le_to_host16(mgmt->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if (sig)
+		ssi_signal = (s32) nla_get_u32(sig);
+
+	os_memset(&event, 0, sizeof(event));
+	if (freq) {
+		event.rx_mgmt.freq = nla_get_u32(freq);
+		rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
+	}
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: RX frame sa=" MACSTR
+		   " freq=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u",
+		   MAC2STR(mgmt->sa), rx_freq, ssi_signal, fc,
+		   le_to_host16(mgmt->seq_ctrl), 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);
+}
+
+
+static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
+				      struct nlattr *cookie, const u8 *frame,
+				      size_t len, struct nlattr *ack)
+{
+	union wpa_event_data event;
+	const struct ieee80211_hdr *hdr;
+	u16 fc;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event");
+	if (!is_ap_interface(drv->nlmode)) {
+		u64 cookie_val;
+
+		if (!cookie)
+			return;
+
+		cookie_val = nla_get_u64(cookie);
+		wpa_printf(MSG_DEBUG, "nl80211: Action TX status:"
+			   " cookie=0%llx%s (ack=%d)",
+			   (long long unsigned int) cookie_val,
+			   cookie_val == drv->send_action_cookie ?
+			   " (match)" : " (unknown)", ack != NULL);
+		if (cookie_val != drv->send_action_cookie)
+			return;
+	}
+
+	hdr = (const struct ieee80211_hdr *) frame;
+	fc = le_to_host16(hdr->frame_control);
+
+	os_memset(&event, 0, sizeof(event));
+	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+	event.tx_status.dst = hdr->addr1;
+	event.tx_status.data = frame;
+	event.tx_status.data_len = len;
+	event.tx_status.ack = ack != NULL;
+	wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
+}
+
+
+static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
+				       enum wpa_event_type type,
+				       const u8 *frame, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	const u8 *bssid = NULL;
+	u16 reason_code = 0;
+
+	if (type == EVENT_DEAUTH)
+		wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event");
+	else
+		wpa_printf(MSG_DEBUG, "nl80211: Disassociate event");
+
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len >= 24) {
+		bssid = mgmt->bssid;
+
+		if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+		    !drv->associated &&
+		    os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 &&
+		    os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 &&
+		    os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) {
+			/*
+			 * Avoid issues with some roaming cases where
+			 * disconnection event for the old AP may show up after
+			 * we have started connection with the new AP.
+			 */
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR,
+				   MAC2STR(bssid),
+				   MAC2STR(drv->auth_attempt_bssid));
+			return;
+		}
+
+		if (drv->associated != 0 &&
+		    os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 &&
+		    os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) {
+			/*
+			 * We have presumably received this deauth as a
+			 * response to a clear_state_mismatch() outgoing
+			 * deauth.  Don't let it take us offline!
+			 */
+			wpa_printf(MSG_DEBUG, "nl80211: Deauth received "
+				   "from Unknown BSSID " MACSTR " -- ignoring",
+				   MAC2STR(bssid));
+			return;
+		}
+	}
+
+	nl80211_mark_disconnected(drv);
+	os_memset(&event, 0, sizeof(event));
+
+	/* Note: Same offset for Reason Code in both frame subtypes */
+	if (len >= 24 + sizeof(mgmt->u.deauth))
+		reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+	if (type == EVENT_DISASSOC) {
+		event.disassoc_info.locally_generated =
+			!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) {
+			event.disassoc_info.ie = mgmt->u.disassoc.variable;
+			event.disassoc_info.ie_len = frame + len -
+				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);
+		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) {
+			event.deauth_info.ie = mgmt->u.deauth.variable;
+			event.deauth_info.ie_len = frame + len -
+				mgmt->u.deauth.variable;
+		}
+	}
+
+	wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
+static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv,
+					 enum wpa_event_type type,
+					 const u8 *frame, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	u16 reason_code = 0;
+
+	if (type == EVENT_UNPROT_DEAUTH)
+		wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event");
+	else
+		wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event");
+
+	if (len < 24)
+		return;
+
+	mgmt = (const struct ieee80211_mgmt *) frame;
+
+	os_memset(&event, 0, sizeof(event));
+	/* Note: Same offset for Reason Code in both frame subtypes */
+	if (len >= 24 + sizeof(mgmt->u.deauth))
+		reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+	if (type == EVENT_UNPROT_DISASSOC) {
+		event.unprot_disassoc.sa = mgmt->sa;
+		event.unprot_disassoc.da = mgmt->da;
+		event.unprot_disassoc.reason_code = reason_code;
+	} else {
+		event.unprot_deauth.sa = mgmt->sa;
+		event.unprot_deauth.da = mgmt->da;
+		event.unprot_deauth.reason_code = reason_code;
+	}
+
+	wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
+static void mlme_event(struct i802_bss *bss,
+		       enum nl80211_commands cmd, struct nlattr *frame,
+		       struct nlattr *addr, struct nlattr *timed_out,
+		       struct nlattr *freq, struct nlattr *ack,
+		       struct nlattr *cookie, struct nlattr *sig,
+		       struct nlattr *wmm)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	const u8 *data;
+	size_t len;
+
+	if (timed_out && addr) {
+		mlme_timeout_event(drv, cmd, addr);
+		return;
+	}
+
+	if (frame == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: MLME event %d (%s) without frame data",
+			   cmd, nl80211_command_to_string(cmd));
+		return;
+	}
+
+	data = nla_data(frame);
+	len = nla_len(frame);
+	if (len < 4 + 2 * ETH_ALEN) {
+		wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s("
+			   MACSTR ") - too short",
+			   cmd, nl80211_command_to_string(cmd), bss->ifname,
+			   MAC2STR(bss->addr));
+		return;
+	}
+	wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR
+		   ") A1=" MACSTR " A2=" MACSTR, cmd,
+		   nl80211_command_to_string(cmd), bss->ifname,
+		   MAC2STR(bss->addr), MAC2STR(data + 4),
+		   MAC2STR(data + 4 + ETH_ALEN));
+	if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
+	    os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
+	    os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
+		wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
+			   "for foreign address", bss->ifname);
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame",
+		    nla_data(frame), nla_len(frame));
+
+	switch (cmd) {
+	case NL80211_CMD_AUTHENTICATE:
+		mlme_event_auth(drv, nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_ASSOCIATE:
+		mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm);
+		break;
+	case NL80211_CMD_DEAUTHENTICATE:
+		mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
+					   nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_DISASSOCIATE:
+		mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
+					   nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_FRAME:
+		mlme_event_mgmt(bss, freq, sig, nla_data(frame),
+				nla_len(frame));
+		break;
+	case NL80211_CMD_FRAME_TX_STATUS:
+		mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
+					  nla_len(frame), ack);
+		break;
+	case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+		mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH,
+					     nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_UNPROT_DISASSOCIATE:
+		mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC,
+					     nla_data(frame), nla_len(frame));
+		break;
+	default:
+		break;
+	}
+}
+
+
+static void mlme_event_michael_mic_failure(struct i802_bss *bss,
+					   struct nlattr *tb[])
+{
+	union wpa_event_data data;
+
+	wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure");
+	os_memset(&data, 0, sizeof(data));
+	if (tb[NL80211_ATTR_MAC]) {
+		wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address",
+			    nla_data(tb[NL80211_ATTR_MAC]),
+			    nla_len(tb[NL80211_ATTR_MAC]));
+		data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]);
+	}
+	if (tb[NL80211_ATTR_KEY_SEQ]) {
+		wpa_hexdump(MSG_DEBUG, "nl80211: TSC",
+			    nla_data(tb[NL80211_ATTR_KEY_SEQ]),
+			    nla_len(tb[NL80211_ATTR_KEY_SEQ]));
+	}
+	if (tb[NL80211_ATTR_KEY_TYPE]) {
+		enum nl80211_key_type key_type =
+			nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]);
+		wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type);
+		if (key_type == NL80211_KEYTYPE_PAIRWISE)
+			data.michael_mic_failure.unicast = 1;
+	} else
+		data.michael_mic_failure.unicast = 1;
+
+	if (tb[NL80211_ATTR_KEY_IDX]) {
+		u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]);
+		wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id);
+	}
+
+	wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+}
+
+
+static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
+				 struct nlattr *tb[])
+{
+	unsigned int freq;
+
+	if (tb[NL80211_ATTR_MAC] == NULL) {
+		wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined "
+			   "event");
+		return;
+	}
+	os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+	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);
+}
+
+
+static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
+					 int cancel_event, struct nlattr *tb[])
+{
+	unsigned int freq, chan_type, duration;
+	union wpa_event_data data;
+	u64 cookie;
+
+	if (tb[NL80211_ATTR_WIPHY_FREQ])
+		freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+	else
+		freq = 0;
+
+	if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
+		chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+	else
+		chan_type = 0;
+
+	if (tb[NL80211_ATTR_DURATION])
+		duration = nla_get_u32(tb[NL80211_ATTR_DURATION]);
+	else
+		duration = 0;
+
+	if (tb[NL80211_ATTR_COOKIE])
+		cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+	else
+		cookie = 0;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d "
+		   "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))",
+		   cancel_event, freq, chan_type, duration,
+		   (long long unsigned int) cookie,
+		   cookie == drv->remain_on_chan_cookie ? "match" : "unknown");
+
+	if (cookie != drv->remain_on_chan_cookie)
+		return; /* not for us */
+
+	if (cancel_event)
+		drv->pending_remain_on_chan = 0;
+
+	os_memset(&data, 0, sizeof(data));
+	data.remain_on_channel.freq = freq;
+	data.remain_on_channel.duration = duration;
+	wpa_supplicant_event(drv->ctx, cancel_event ?
+			     EVENT_CANCEL_REMAIN_ON_CHANNEL :
+			     EVENT_REMAIN_ON_CHANNEL, &data);
+}
+
+
+static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv,
+				struct nlattr *tb[])
+{
+	union wpa_event_data data;
+
+	os_memset(&data, 0, sizeof(data));
+
+	if (tb[NL80211_ATTR_IE]) {
+		data.ft_ies.ies = nla_data(tb[NL80211_ATTR_IE]);
+		data.ft_ies.ies_len = nla_len(tb[NL80211_ATTR_IE]);
+	}
+
+	if (tb[NL80211_ATTR_IE_RIC]) {
+		data.ft_ies.ric_ies = nla_data(tb[NL80211_ATTR_IE_RIC]);
+		data.ft_ies.ric_ies_len = nla_len(tb[NL80211_ATTR_IE_RIC]);
+	}
+
+	if (tb[NL80211_ATTR_MAC])
+		os_memcpy(data.ft_ies.target_ap,
+			  nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+	wpa_printf(MSG_DEBUG, "nl80211: FT event target_ap " MACSTR,
+		   MAC2STR(data.ft_ies.target_ap));
+
+	wpa_supplicant_event(drv->ctx, EVENT_FT_RESPONSE, &data);
+}
+
+
+static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
+			    struct nlattr *tb[])
+{
+	union wpa_event_data event;
+	struct nlattr *nl;
+	int rem;
+	struct scan_info *info;
+#define MAX_REPORT_FREQS 50
+	int freqs[MAX_REPORT_FREQS];
+	int num_freqs = 0;
+
+	if (drv->scan_for_auth) {
+		drv->scan_for_auth = 0;
+		wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
+			   "cfg80211 BSS entry");
+		wpa_driver_nl80211_authenticate_retry(drv);
+		return;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	info = &event.scan_info;
+	info->aborted = aborted;
+
+	if (tb[NL80211_ATTR_SCAN_SSIDS]) {
+		nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
+			struct wpa_driver_scan_ssid *s =
+				&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 (!os_snprintf_error(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);
+}
+
+
+static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
+			      struct nlattr *tb[])
+{
+	static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
+		[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
+		[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
+		[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+		[NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 },
+	};
+	struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
+	enum nl80211_cqm_rssi_threshold_event event;
+	union wpa_event_data ed;
+	struct wpa_signal_info sig;
+	int res;
+
+	if (tb[NL80211_ATTR_CQM] == NULL ||
+	    nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM],
+			     cqm_policy)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event");
+		return;
+	}
+
+	os_memset(&ed, 0, sizeof(ed));
+
+	if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) {
+		if (!tb[NL80211_ATTR_MAC])
+			return;
+		os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]),
+			  ETH_ALEN);
+		wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed);
+		return;
+	}
+
+	if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
+		return;
+	event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
+
+	if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
+		wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+			   "event: RSSI high");
+		ed.signal_change.above_threshold = 1;
+	} else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) {
+		wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+			   "event: RSSI low");
+		ed.signal_change.above_threshold = 0;
+	} else
+		return;
+
+	res = nl80211_get_link_signal(drv, &sig);
+	if (res == 0) {
+		ed.signal_change.current_signal = sig.current_signal;
+		ed.signal_change.current_txrate = sig.current_txrate;
+		wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm  txrate: %d",
+			   sig.current_signal, sig.current_txrate);
+	}
+
+	res = nl80211_get_link_noise(drv, &sig);
+	if (res == 0) {
+		ed.signal_change.current_noise = sig.current_noise;
+		wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm",
+			   sig.current_noise);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
+}
+
+
+static void nl80211_new_peer_candidate(struct wpa_driver_nl80211_data *drv,
+				       struct nlattr **tb)
+{
+	const u8 *addr;
+	union wpa_event_data data;
+
+	if (drv->nlmode != NL80211_IFTYPE_MESH_POINT ||
+	    !tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE])
+		return;
+
+	addr = nla_data(tb[NL80211_ATTR_MAC]);
+	wpa_printf(MSG_DEBUG, "nl80211: New peer candidate" MACSTR,
+		   MAC2STR(addr));
+
+	os_memset(&data, 0, sizeof(data));
+	data.mesh_peer.peer = addr;
+	data.mesh_peer.ies = nla_data(tb[NL80211_ATTR_IE]);
+	data.mesh_peer.ie_len = nla_len(tb[NL80211_ATTR_IE]);
+	wpa_supplicant_event(drv->ctx, EVENT_NEW_PEER_CANDIDATE, &data);
+}
+
+
+static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
+				      struct i802_bss *bss,
+				      struct nlattr **tb)
+{
+	u8 *addr;
+	union wpa_event_data data;
+
+	if (tb[NL80211_ATTR_MAC] == NULL)
+		return;
+	addr = nla_data(tb[NL80211_ATTR_MAC]);
+	wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr));
+
+	if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
+		u8 *ies = NULL;
+		size_t ies_len = 0;
+		if (tb[NL80211_ATTR_IE]) {
+			ies = nla_data(tb[NL80211_ATTR_IE]);
+			ies_len = nla_len(tb[NL80211_ATTR_IE]);
+		}
+		wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len);
+		drv_event_assoc(bss->ctx, addr, ies, ies_len, 0);
+		return;
+	}
+
+	if (drv->nlmode != NL80211_IFTYPE_ADHOC)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN);
+	wpa_supplicant_event(bss->ctx, EVENT_IBSS_RSN_START, &data);
+}
+
+
+static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
+				      struct nlattr **tb)
+{
+	u8 *addr;
+	union wpa_event_data data;
+
+	if (tb[NL80211_ATTR_MAC] == NULL)
+		return;
+	addr = nla_data(tb[NL80211_ATTR_MAC]);
+	wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR,
+		   MAC2STR(addr));
+
+	if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
+		drv_event_disassoc(drv->ctx, addr);
+		return;
+	}
+
+	if (drv->nlmode != NL80211_IFTYPE_ADHOC)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN);
+	wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data);
+}
+
+
+static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv,
+					struct nlattr **tb)
+{
+	struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA];
+	static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = {
+		[NL80211_REKEY_DATA_KEK] = {
+			.minlen = NL80211_KEK_LEN,
+			.maxlen = NL80211_KEK_LEN,
+		},
+		[NL80211_REKEY_DATA_KCK] = {
+			.minlen = NL80211_KCK_LEN,
+			.maxlen = NL80211_KCK_LEN,
+		},
+		[NL80211_REKEY_DATA_REPLAY_CTR] = {
+			.minlen = NL80211_REPLAY_CTR_LEN,
+			.maxlen = NL80211_REPLAY_CTR_LEN,
+		},
+	};
+	union wpa_event_data data;
+
+	if (!tb[NL80211_ATTR_MAC] ||
+	    !tb[NL80211_ATTR_REKEY_DATA] ||
+	    nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA,
+			     tb[NL80211_ATTR_REKEY_DATA], rekey_policy) ||
+	    !rekey_info[NL80211_REKEY_DATA_REPLAY_CTR])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]);
+	wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR,
+		   MAC2STR(data.driver_gtk_rekey.bssid));
+	data.driver_gtk_rekey.replay_ctr =
+		nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]);
+	wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter",
+		    data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN);
+	wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data);
+}
+
+
+static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv,
+					  struct nlattr **tb)
+{
+	struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE];
+	static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = {
+		[NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 },
+		[NL80211_PMKSA_CANDIDATE_BSSID] = {
+			.minlen = ETH_ALEN,
+			.maxlen = ETH_ALEN,
+		},
+		[NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG },
+	};
+	union wpa_event_data data;
+
+	wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event");
+
+	if (!tb[NL80211_ATTR_PMKSA_CANDIDATE] ||
+	    nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE,
+			     tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy) ||
+	    !cand[NL80211_PMKSA_CANDIDATE_INDEX] ||
+	    !cand[NL80211_PMKSA_CANDIDATE_BSSID])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.pmkid_candidate.bssid,
+		  nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN);
+	data.pmkid_candidate.index =
+		nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]);
+	data.pmkid_candidate.preauth =
+		cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL;
+	wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data);
+}
+
+
+static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv,
+				       struct nlattr **tb)
+{
+	union wpa_event_data data;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Probe client event");
+
+	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.client_poll.addr,
+		  nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+	wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data);
+}
+
+
+static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv,
+				    struct nlattr **tb)
+{
+	union wpa_event_data data;
+
+	wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event");
+
+	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+	switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) {
+	case NL80211_TDLS_SETUP:
+		wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer "
+			   MACSTR, MAC2STR(data.tdls.peer));
+		data.tdls.oper = TDLS_REQUEST_SETUP;
+		break;
+	case NL80211_TDLS_TEARDOWN:
+		wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer "
+			   MACSTR, MAC2STR(data.tdls.peer));
+		data.tdls.oper = TDLS_REQUEST_TEARDOWN;
+		break;
+	case NL80211_TDLS_DISCOVERY_REQ:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: TDLS discovery request for peer " MACSTR,
+			   MAC2STR(data.tdls.peer));
+		data.tdls.oper = TDLS_REQUEST_DISCOVER;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione "
+			   "event");
+		return;
+	}
+	if (tb[NL80211_ATTR_REASON_CODE]) {
+		data.tdls.reason_code =
+			nla_get_u16(tb[NL80211_ATTR_REASON_CODE]);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data);
+}
+
+
+static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv,
+			    struct nlattr **tb)
+{
+	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL);
+}
+
+
+static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv,
+					 struct nlattr **tb)
+{
+	union wpa_event_data data;
+	u32 reason;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Connect failed event");
+
+	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.connect_failed_reason.addr,
+		  nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+	reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]);
+	switch (reason) {
+	case NL80211_CONN_FAIL_MAX_CLIENTS:
+		wpa_printf(MSG_DEBUG, "nl80211: Max client reached");
+		data.connect_failed_reason.code = MAX_CLIENT_REACHED;
+		break;
+	case NL80211_CONN_FAIL_BLOCKED_CLIENT:
+		wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR
+			   " tried to connect",
+			   MAC2STR(data.connect_failed_reason.addr));
+		data.connect_failed_reason.code = BLOCKED_CLIENT;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason "
+			   "%u", reason);
+		return;
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data);
+}
+
+
+static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+				struct nlattr **tb)
+{
+	union wpa_event_data data;
+	enum nl80211_radar_event event_type;
+
+	if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+	event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
+
+	/* 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:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
+		break;
+	case NL80211_RADAR_CAC_FINISHED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
+		break;
+	case NL80211_RADAR_CAC_ABORTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
+		break;
+	case NL80211_RADAR_NOP_FINISHED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
+			   "received", event_type);
+		break;
+	}
+}
+
+
+static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
+				   int wds)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	union wpa_event_data event;
+
+	if (!tb[NL80211_ATTR_MAC])
+		return;
+
+	os_memset(&event, 0, sizeof(event));
+	event.rx_from_unknown.bssid = bss->addr;
+	event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]);
+	event.rx_from_unknown.wds = wds;
+
+	wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+}
+
+
+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 enum hostapd_hw_mode get_qca_hw_mode(u8 hw_mode)
+{
+	switch (hw_mode) {
+	case QCA_ACS_MODE_IEEE80211B:
+		return HOSTAPD_MODE_IEEE80211B;
+	case QCA_ACS_MODE_IEEE80211G:
+		return HOSTAPD_MODE_IEEE80211G;
+	case QCA_ACS_MODE_IEEE80211A:
+		return HOSTAPD_MODE_IEEE80211A;
+	case QCA_ACS_MODE_IEEE80211AD:
+		return HOSTAPD_MODE_IEEE80211AD;
+	case QCA_ACS_MODE_IEEE80211ANY:
+		return HOSTAPD_MODE_IEEE80211ANY;
+	default:
+		return NUM_HOSTAPD_MODES;
+	}
+}
+
+
+static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
+				   const u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1];
+	union wpa_event_data event;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: ACS channel selection vendor event received");
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL] ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL])
+		return;
+
+	os_memset(&event, 0, sizeof(event));
+	event.acs_selected_channels.pri_channel =
+		nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]);
+	event.acs_selected_channels.sec_channel =
+		nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
+		event.acs_selected_channels.vht_seg0_center_ch =
+			nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
+		event.acs_selected_channels.vht_seg1_center_ch =
+			nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH])
+		event.acs_selected_channels.ch_width =
+			nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) {
+		u8 hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]);
+
+		event.acs_selected_channels.hw_mode = get_qca_hw_mode(hw_mode);
+		if (event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES ||
+		    event.acs_selected_channels.hw_mode ==
+		    HOSTAPD_MODE_IEEE80211ANY) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Invalid hw_mode %d in ACS selection event",
+				   hw_mode);
+			return;
+		}
+	}
+
+	wpa_printf(MSG_INFO,
+		   "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d",
+		   event.acs_selected_channels.pri_channel,
+		   event.acs_selected_channels.sec_channel,
+		   event.acs_selected_channels.ch_width,
+		   event.acs_selected_channels.vht_seg0_center_ch,
+		   event.acs_selected_channels.vht_seg1_center_ch,
+		   event.acs_selected_channels.hw_mode);
+
+	/* Ignore ACS channel list check for backwards compatibility */
+
+	wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event);
+}
+
+
+static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv,
+				      const u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX + 1];
+	u8 *bssid;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Key management roam+auth vendor event received");
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID] ||
+	    nla_len(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]) != ETH_ALEN ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE] ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE] ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED])
+		return;
+
+	bssid = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]);
+	wpa_printf(MSG_DEBUG, "  * roam BSSID " MACSTR, MAC2STR(bssid));
+
+	mlme_event_connect(drv, NL80211_CMD_ROAM, NULL,
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK]);
+}
+
+
+static void qca_nl80211_dfs_offload_radar_event(
+	struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *msg, int length)
+{
+	union wpa_event_data data;
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: DFS offload radar vendor event received");
+
+	if (nla_parse(tb, NL80211_ATTR_MAX,
+		      (struct nlattr *) msg, length, NULL))
+		return;
+
+	if (!tb[NL80211_ATTR_WIPHY_FREQ]) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: Error parsing WIPHY_FREQ in FS offload radar vendor event");
+		return;
+	}
+
+	os_memset(&data, 0, sizeof(data));
+	data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+
+	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 (subcmd) {
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Unknown DFS offload radar event %d received",
+			   subcmd);
+		break;
+	}
+}
+
+
+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_TEST:
+		wpa_hexdump(MSG_DEBUG, "nl80211: QCA test event", data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
+		qca_nl80211_avoid_freq(drv, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH:
+		qca_nl80211_key_mgmt_auth(drv, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
+		qca_nl80211_acs_select_ch(drv, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED:
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED:
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED:
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED:
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
+		qca_nl80211_dfs_offload_radar_event(drv, subcmd, 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);
+
+	if (cmd == NL80211_CMD_ROAM &&
+	    (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
+		/*
+		 * Device will use roam+auth vendor event to indicate
+		 * roaming, so ignore the regular roam event.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore roam event (cmd=%d), device will use vendor event roam+auth",
+			   cmd);
+		return;
+	}
+
+	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,
+					    drv->ap_scan_as_station);
+		drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+	}
+
+	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);
+		send_scan_event(drv, 0, tb);
+		break;
+	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.
+		 */
+		eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+				     drv->ctx);
+		send_scan_event(drv, 1, tb);
+		break;
+	case NL80211_CMD_AUTHENTICATE:
+	case NL80211_CMD_ASSOCIATE:
+	case NL80211_CMD_DEAUTHENTICATE:
+	case NL80211_CMD_DISASSOCIATE:
+	case NL80211_CMD_FRAME_TX_STATUS:
+	case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+	case NL80211_CMD_UNPROT_DISASSOCIATE:
+		mlme_event(bss, cmd, tb[NL80211_ATTR_FRAME],
+			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
+			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
+			   tb[NL80211_ATTR_COOKIE],
+			   tb[NL80211_ATTR_RX_SIGNAL_DBM],
+			   tb[NL80211_ATTR_STA_WME]);
+		break;
+	case NL80211_CMD_CONNECT:
+	case NL80211_CMD_ROAM:
+		mlme_event_connect(drv, cmd,
+				   tb[NL80211_ATTR_STATUS_CODE],
+				   tb[NL80211_ATTR_MAC],
+				   tb[NL80211_ATTR_REQ_IE],
+				   tb[NL80211_ATTR_RESP_IE],
+				   NULL, NULL, NULL, NULL);
+		break;
+	case NL80211_CMD_CH_SWITCH_NOTIFY:
+		mlme_event_ch_switch(drv,
+				     tb[NL80211_ATTR_IFINDEX],
+				     tb[NL80211_ATTR_WIPHY_FREQ],
+				     tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
+				     tb[NL80211_ATTR_CHANNEL_WIDTH],
+				     tb[NL80211_ATTR_CENTER_FREQ1],
+				     tb[NL80211_ATTR_CENTER_FREQ2]);
+		break;
+	case NL80211_CMD_DISCONNECT:
+		mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
+				      tb[NL80211_ATTR_MAC],
+				      tb[NL80211_ATTR_DISCONNECTED_BY_AP]);
+		break;
+	case NL80211_CMD_MICHAEL_MIC_FAILURE:
+		mlme_event_michael_mic_failure(bss, tb);
+		break;
+	case NL80211_CMD_JOIN_IBSS:
+		mlme_event_join_ibss(drv, tb);
+		break;
+	case NL80211_CMD_REMAIN_ON_CHANNEL:
+		mlme_event_remain_on_channel(drv, 0, tb);
+		break;
+	case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
+		mlme_event_remain_on_channel(drv, 1, tb);
+		break;
+	case NL80211_CMD_NOTIFY_CQM:
+		nl80211_cqm_event(drv, tb);
+		break;
+	case NL80211_CMD_REG_CHANGE:
+		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,
+				     &data);
+		break;
+	case NL80211_CMD_NEW_STATION:
+		nl80211_new_station_event(drv, bss, tb);
+		break;
+	case NL80211_CMD_DEL_STATION:
+		nl80211_del_station_event(drv, tb);
+		break;
+	case NL80211_CMD_SET_REKEY_OFFLOAD:
+		nl80211_rekey_offload_event(drv, tb);
+		break;
+	case NL80211_CMD_PMKSA_CANDIDATE:
+		nl80211_pmksa_candidate_event(drv, tb);
+		break;
+	case NL80211_CMD_PROBE_CLIENT:
+		nl80211_client_probe_event(drv, tb);
+		break;
+	case NL80211_CMD_TDLS_OPER:
+		nl80211_tdls_oper_event(drv, tb);
+		break;
+	case NL80211_CMD_CONN_FAILED:
+		nl80211_connect_failed_event(drv, tb);
+		break;
+	case NL80211_CMD_FT_EVENT:
+		mlme_event_ft_event(drv, tb);
+		break;
+	case NL80211_CMD_RADAR_DETECT:
+		nl80211_radar_event(drv, tb);
+		break;
+	case NL80211_CMD_STOP_AP:
+		nl80211_stop_ap(drv, tb);
+		break;
+	case NL80211_CMD_VENDOR:
+		nl80211_vendor_event(drv, tb);
+		break;
+	case NL80211_CMD_NEW_PEER_CANDIDATE:
+		nl80211_new_peer_candidate(drv, tb);
+		break;
+	default:
+		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
+			"(cmd=%d)", cmd);
+		break;
+	}
+}
+
+
+int process_global_event(struct nl_msg *msg, void *arg)
+{
+	struct nl80211_global *global = arg;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct wpa_driver_nl80211_data *drv, *tmp;
+	int ifidx = -1;
+	struct i802_bss *bss;
+	u64 wdev_id = 0;
+	int wdev_id_set = 0;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL80211_ATTR_IFINDEX])
+		ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+	else if (tb[NL80211_ATTR_WDEV]) {
+		wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
+		wdev_id_set = 1;
+	}
+
+	dl_list_for_each_safe(drv, tmp, &global->interfaces,
+			      struct wpa_driver_nl80211_data, list) {
+		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 &&
+			     wdev_id == bss->wdev_id)) {
+				do_process_drv_event(bss, gnlh->cmd, tb);
+				return NL_SKIP;
+			}
+		}
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d wdev 0x%llx)",
+			   gnlh->cmd, ifidx, (long long unsigned int) wdev_id);
+	}
+
+	return NL_SKIP;
+}
+
+
+int process_bss_event(struct nl_msg *msg, void *arg)
+{
+	struct i802_bss *bss = arg;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s",
+		   gnlh->cmd, nl80211_command_to_string(gnlh->cmd),
+		   bss->ifname);
+
+	switch (gnlh->cmd) {
+	case NL80211_CMD_FRAME:
+	case NL80211_CMD_FRAME_TX_STATUS:
+		mlme_event(bss, gnlh->cmd, tb[NL80211_ATTR_FRAME],
+			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
+			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
+			   tb[NL80211_ATTR_COOKIE],
+			   tb[NL80211_ATTR_RX_SIGNAL_DBM],
+			   tb[NL80211_ATTR_STA_WME]);
+		break;
+	case NL80211_CMD_UNEXPECTED_FRAME:
+		nl80211_spurious_frame(bss, tb, 0);
+		break;
+	case NL80211_CMD_UNEXPECTED_4ADDR_FRAME:
+		nl80211_spurious_frame(bss, tb, 1);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
+			   "(cmd=%d)", gnlh->cmd);
+		break;
+	}
+
+	return NL_SKIP;
+}
diff --git a/hostap/src/drivers/driver_nl80211_monitor.c b/hostap/src/drivers/driver_nl80211_monitor.c
new file mode 100644
index 0000000..45385da
--- /dev/null
+++ b/hostap/src/drivers/driver_nl80211_monitor.c
@@ -0,0 +1,491 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - AP monitor interface
+ * 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>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netpacket/packet.h>
+#include <linux/filter.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "linux_ioctl.h"
+#include "radiotap_iter.h"
+#include "driver_nl80211.h"
+
+
+static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
+{
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	union wpa_event_data event;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	fc = le_to_host16(hdr->frame_control);
+
+	os_memset(&event, 0, sizeof(event));
+	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+	event.tx_status.dst = hdr->addr1;
+	event.tx_status.data = buf;
+	event.tx_status.data_len = len;
+	event.tx_status.ack = ok;
+	wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event);
+}
+
+
+static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
+			     u8 *buf, size_t len)
+{
+	struct ieee80211_hdr *hdr = (void *)buf;
+	u16 fc;
+	union wpa_event_data event;
+
+	if (len < sizeof(*hdr))
+		return;
+
+	fc = le_to_host16(hdr->frame_control);
+
+	os_memset(&event, 0, sizeof(event));
+	event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
+	event.rx_from_unknown.addr = hdr->addr2;
+	event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) ==
+		(WLAN_FC_FROMDS | WLAN_FC_TODS);
+	wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+}
+
+
+static void handle_frame(struct wpa_driver_nl80211_data *drv,
+			 u8 *buf, size_t len, int datarate, int ssi_signal)
+{
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	union wpa_event_data event;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	fc = le_to_host16(hdr->frame_control);
+
+	switch (WLAN_FC_GET_TYPE(fc)) {
+	case WLAN_FC_TYPE_MGMT:
+		os_memset(&event, 0, sizeof(event));
+		event.rx_mgmt.frame = buf;
+		event.rx_mgmt.frame_len = len;
+		event.rx_mgmt.datarate = datarate;
+		event.rx_mgmt.ssi_signal = ssi_signal;
+		wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+		break;
+	case WLAN_FC_TYPE_CTRL:
+		/* can only get here with PS-Poll frames */
+		wpa_printf(MSG_DEBUG, "CTRL");
+		from_unknown_sta(drv, buf, len);
+		break;
+	case WLAN_FC_TYPE_DATA:
+		from_unknown_sta(drv, buf, len);
+		break;
+	}
+}
+
+
+static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_driver_nl80211_data *drv = eloop_ctx;
+	int len;
+	unsigned char buf[3000];
+	struct ieee80211_radiotap_iterator iter;
+	int ret;
+	int datarate = 0, ssi_signal = 0;
+	int injected = 0, failed = 0, rxflags = 0;
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s",
+			   strerror(errno));
+		return;
+	}
+
+	if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len, NULL)) {
+		wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame");
+		return;
+	}
+
+	while (1) {
+		ret = ieee80211_radiotap_iterator_next(&iter);
+		if (ret == -ENOENT)
+			break;
+		if (ret) {
+			wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)",
+				   ret);
+			return;
+		}
+		switch (iter.this_arg_index) {
+		case IEEE80211_RADIOTAP_FLAGS:
+			if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
+				len -= 4;
+			break;
+		case IEEE80211_RADIOTAP_RX_FLAGS:
+			rxflags = 1;
+			break;
+		case IEEE80211_RADIOTAP_TX_FLAGS:
+			injected = 1;
+			failed = le_to_host16((*(uint16_t *) iter.this_arg)) &
+					IEEE80211_RADIOTAP_F_TX_FAIL;
+			break;
+		case IEEE80211_RADIOTAP_DATA_RETRIES:
+			break;
+		case IEEE80211_RADIOTAP_CHANNEL:
+			/* TODO: convert from freq/flags to channel number */
+			break;
+		case IEEE80211_RADIOTAP_RATE:
+			datarate = *iter.this_arg * 5;
+			break;
+		case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
+			ssi_signal = (s8) *iter.this_arg;
+			break;
+		}
+	}
+
+	if (rxflags && injected)
+		return;
+
+	if (!injected)
+		handle_frame(drv, buf + iter._max_length,
+			     len - iter._max_length, datarate, ssi_signal);
+	else
+		handle_tx_callback(drv->ctx, buf + iter._max_length,
+				   len - iter._max_length, !failed);
+}
+
+
+/*
+ * we post-process the filter code later and rewrite
+ * this to the offset to the last instruction
+ */
+#define PASS	0xFF
+#define FAIL	0xFE
+
+static struct sock_filter msock_filter_insns[] = {
+	/*
+	 * do a little-endian load of the radiotap length field
+	 */
+	/* load lower byte into A */
+	BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 2),
+	/* put it into X (== index register) */
+	BPF_STMT(BPF_MISC| BPF_TAX, 0),
+	/* load upper byte into A */
+	BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 3),
+	/* left-shift it by 8 */
+	BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8),
+	/* or with X */
+	BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0),
+	/* put result into X */
+	BPF_STMT(BPF_MISC| BPF_TAX, 0),
+
+	/*
+	 * Allow management frames through, this also gives us those
+	 * management frames that we sent ourselves with status
+	 */
+	/* load the lower byte of the IEEE 802.11 frame control field */
+	BPF_STMT(BPF_LD  | BPF_B | BPF_IND, 0),
+	/* mask off frame type and version */
+	BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF),
+	/* accept frame if it's both 0, fall through otherwise */
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0),
+
+	/*
+	 * TODO: add a bit to radiotap RX flags that indicates
+	 * that the sending station is not associated, then
+	 * add a filter here that filters on our DA and that flag
+	 * to allow us to deauth frames to that bad station.
+	 *
+	 * For now allow all To DS data frames through.
+	 */
+	/* load the IEEE 802.11 frame control field */
+	BPF_STMT(BPF_LD  | BPF_H | BPF_IND, 0),
+	/* mask off frame type, version and DS status */
+	BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03),
+	/* accept frame if version 0, type 2 and To DS, fall through otherwise
+	 */
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0),
+
+#if 0
+	/*
+	 * drop non-data frames
+	 */
+	/* load the lower byte of the frame control field */
+	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
+	/* mask off QoS bit */
+	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x0c),
+	/* drop non-data frames */
+	BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 8, 0, FAIL),
+#endif
+	/* load the upper byte of the frame control field */
+	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 1),
+	/* mask off toDS/fromDS */
+	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x03),
+	/* accept WDS frames */
+	BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 3, PASS, 0),
+
+	/*
+	 * add header length to index
+	 */
+	/* load the lower byte of the frame control field */
+	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
+	/* mask off QoS bit */
+	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x80),
+	/* right shift it by 6 to give 0 or 2 */
+	BPF_STMT(BPF_ALU  | BPF_RSH | BPF_K, 6),
+	/* add data frame header length */
+	BPF_STMT(BPF_ALU  | BPF_ADD | BPF_K, 24),
+	/* add index, was start of 802.11 header */
+	BPF_STMT(BPF_ALU  | BPF_ADD | BPF_X, 0),
+	/* move to index, now start of LL header */
+	BPF_STMT(BPF_MISC | BPF_TAX, 0),
+
+	/*
+	 * Accept empty data frames, we use those for
+	 * polling activity.
+	 */
+	BPF_STMT(BPF_LD  | BPF_W | BPF_LEN, 0),
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0),
+
+	/*
+	 * Accept EAPOL frames
+	 */
+	BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 0),
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL),
+	BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 4),
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL),
+
+	/* keep these last two statements or change the code below */
+	/* return 0 == "DROP" */
+	BPF_STMT(BPF_RET | BPF_K, 0),
+	/* return ~0 == "keep all" */
+	BPF_STMT(BPF_RET | BPF_K, ~0),
+};
+
+static struct sock_fprog msock_filter = {
+	.len = ARRAY_SIZE(msock_filter_insns),
+	.filter = msock_filter_insns,
+};
+
+
+static int add_monitor_filter(int s)
+{
+	int idx;
+
+	/* rewrite all PASS/FAIL jump offsets */
+	for (idx = 0; idx < msock_filter.len; idx++) {
+		struct sock_filter *insn = &msock_filter_insns[idx];
+
+		if (BPF_CLASS(insn->code) == BPF_JMP) {
+			if (insn->code == (BPF_JMP|BPF_JA)) {
+				if (insn->k == PASS)
+					insn->k = msock_filter.len - idx - 2;
+				else if (insn->k == FAIL)
+					insn->k = msock_filter.len - idx - 3;
+			}
+
+			if (insn->jt == PASS)
+				insn->jt = msock_filter.len - idx - 2;
+			else if (insn->jt == FAIL)
+				insn->jt = msock_filter.len - idx - 3;
+
+			if (insn->jf == PASS)
+				insn->jf = msock_filter.len - idx - 2;
+			else if (insn->jf == FAIL)
+				insn->jf = msock_filter.len - idx - 3;
+		}
+	}
+
+	if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
+		       &msock_filter, sizeof(msock_filter))) {
+		wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv)
+{
+	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;
+
+	if (drv->monitor_ifidx >= 0) {
+		nl80211_remove_iface(drv, drv->monitor_ifidx);
+		drv->monitor_ifidx = -1;
+	}
+	if (drv->monitor_sock >= 0) {
+		eloop_unregister_read_sock(drv->monitor_sock);
+		close(drv->monitor_sock);
+		drv->monitor_sock = -1;
+	}
+}
+
+
+int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
+{
+	char buf[IFNAMSIZ];
+	struct sockaddr_ll ll;
+	int optval;
+	socklen_t optlen;
+
+	if (drv->monitor_ifidx >= 0) {
+		drv->monitor_refcount++;
+		wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d",
+			   drv->monitor_refcount);
+		return 0;
+	}
+
+	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);
+	} else {
+		/* Non-P2P interface with AP functionality. */
+		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);
+
+	if (drv->monitor_ifidx == -EOPNOTSUPP) {
+		/*
+		 * This is backward compatibility for a few versions of
+		 * the kernel only that didn't advertise the right
+		 * attributes for the only driver that then supported
+		 * AP mode w/o monitor -- ath6kl.
+		 */
+		wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
+			   "monitor interface type - try to run without it");
+		drv->device_ap_sme = 1;
+	}
+
+	if (drv->monitor_ifidx < 0)
+		return -1;
+
+	if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1))
+		goto error;
+
+	memset(&ll, 0, sizeof(ll));
+	ll.sll_family = AF_PACKET;
+	ll.sll_ifindex = drv->monitor_ifidx;
+	drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+	if (drv->monitor_sock < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s",
+			   strerror(errno));
+		goto error;
+	}
+
+	if (add_monitor_filter(drv->monitor_sock)) {
+		wpa_printf(MSG_INFO, "Failed to set socket filter for monitor "
+			   "interface; do filtering in user space");
+		/* This works, but will cost in performance. */
+	}
+
+	if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s",
+			   strerror(errno));
+		goto error;
+	}
+
+	optlen = sizeof(optval);
+	optval = 20;
+	if (setsockopt
+	    (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s",
+			   strerror(errno));
+		goto error;
+	}
+
+	if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read,
+				     drv, NULL)) {
+		wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket");
+		goto error;
+	}
+
+	drv->monitor_refcount++;
+	return 0;
+ error:
+	nl80211_remove_monitor_interface(drv);
+	return -1;
+}
+
+
+int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
+			 const void *data, size_t len,
+			 int encrypt, int noack)
+{
+	__u8 rtap_hdr[] = {
+		0x00, 0x00, /* radiotap version */
+		0x0e, 0x00, /* radiotap length */
+		0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
+		IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
+		0x00,       /* padding */
+		0x00, 0x00, /* RX and TX flags to indicate that */
+		0x00, 0x00, /* this is the injected frame directly */
+	};
+	struct iovec iov[2] = {
+		{
+			.iov_base = &rtap_hdr,
+			.iov_len = sizeof(rtap_hdr),
+		},
+		{
+			.iov_base = (void *) data,
+			.iov_len = len,
+		}
+	};
+	struct msghdr msg = {
+		.msg_name = NULL,
+		.msg_namelen = 0,
+		.msg_iov = iov,
+		.msg_iovlen = 2,
+		.msg_control = NULL,
+		.msg_controllen = 0,
+		.msg_flags = 0,
+	};
+	int res;
+	u16 txflags = 0;
+
+	if (encrypt)
+		rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
+
+	if (drv->monitor_sock < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available "
+			   "for %s", __func__);
+		return -1;
+	}
+
+	if (noack)
+		txflags |= IEEE80211_RADIOTAP_F_TX_NOACK;
+	WPA_PUT_LE16(&rtap_hdr[12], txflags);
+
+	res = sendmsg(drv->monitor_sock, &msg, 0);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
+		return -1;
+	}
+	return 0;
+}
diff --git a/hostap/src/drivers/driver_nl80211_scan.c b/hostap/src/drivers/driver_nl80211_scan.c
new file mode 100644
index 0000000..4b762ea
--- /dev/null
+++ b/hostap/src/drivers/driver_nl80211_scan.c
@@ -0,0 +1,783 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Scanning
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "driver_nl80211.h"
+
+
+static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+	static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+		[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+	};
+	struct wpa_scan_results *scan_results = arg;
+	struct wpa_scan_res *scan_res;
+	size_t i;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+		wpa_printf(MSG_DEBUG, "nl80211: Survey data missing");
+		return NL_SKIP;
+	}
+
+	if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+			     tb[NL80211_ATTR_SURVEY_INFO],
+			     survey_policy)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested "
+			   "attributes");
+		return NL_SKIP;
+	}
+
+	if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+		return NL_SKIP;
+
+	if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+		return NL_SKIP;
+
+	for (i = 0; i < scan_results->num; ++i) {
+		scan_res = scan_results->res[i];
+		if (!scan_res)
+			continue;
+		if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+		    scan_res->freq)
+			continue;
+		if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID))
+			continue;
+		scan_res->noise = (s8)
+			nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+		scan_res->flags &= ~WPA_SCAN_NOISE_INVALID;
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_noise_for_scan_results(
+	struct wpa_driver_nl80211_data *drv,
+	struct wpa_scan_results *scan_res)
+{
+	struct nl_msg *msg;
+
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+	return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
+				  scan_res);
+}
+
+
+/**
+ * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion
+ * @eloop_ctx: Driver private data
+ * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init()
+ *
+ * This function can be used as registered timeout when starting a scan to
+ * generate a scan completed event if the driver does not report this.
+ */
+void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	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,
+					    drv->ap_scan_as_station);
+		drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+	}
+	wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static struct nl_msg *
+nl80211_scan_common(struct i802_bss *bss, u8 cmd,
+		    struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	size_t i;
+	u32 scan_flags = 0;
+
+	msg = nl80211_cmd_msg(bss, 0, cmd);
+	if (!msg)
+		return NULL;
+
+	if (params->num_ssids) {
+		struct nlattr *ssids;
+
+		ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
+		if (ssids == NULL)
+			goto fail;
+		for (i = 0; i < params->num_ssids; i++) {
+			wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
+					  params->ssids[i].ssid,
+					  params->ssids[i].ssid_len);
+			if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
+				    params->ssids[i].ssid))
+				goto fail;
+		}
+		nla_nest_end(msg, ssids);
+	}
+
+	if (params->extra_ies) {
+		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+			    params->extra_ies, params->extra_ies_len);
+		if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
+			    params->extra_ies))
+			goto fail;
+	}
+
+	if (params->freqs) {
+		struct nlattr *freqs;
+		freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
+		if (freqs == NULL)
+			goto fail;
+		for (i = 0; params->freqs[i]; i++) {
+			wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
+				   "MHz", params->freqs[i]);
+			if (nla_put_u32(msg, i + 1, params->freqs[i]))
+				goto fail;
+		}
+		nla_nest_end(msg, freqs);
+	}
+
+	os_free(drv->filter_ssids);
+	drv->filter_ssids = params->filter_ssids;
+	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 (params->mac_addr_rand) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR");
+		scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
+
+		if (params->mac_addr) {
+			wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR,
+				   MAC2STR(params->mac_addr));
+			if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+				    params->mac_addr))
+				goto fail;
+		}
+
+		if (params->mac_addr_mask) {
+			wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: "
+				   MACSTR, MAC2STR(params->mac_addr_mask));
+			if (nla_put(msg, NL80211_ATTR_MAC_MASK, ETH_ALEN,
+				    params->mac_addr_mask))
+				goto fail;
+		}
+	}
+
+	if (scan_flags &&
+	    nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags))
+		goto fail;
+
+	return msg;
+
+fail:
+	nlmsg_free(msg);
+	return NULL;
+}
+
+
+/**
+ * wpa_driver_nl80211_scan - Request the driver to initiate scan
+ * @bss: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_nl80211_scan(struct i802_bss *bss,
+			    struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret = -1, timeout;
+	struct nl_msg *msg = NULL;
+
+	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request");
+	drv->scan_for_auth = 0;
+
+	if (TEST_FAIL())
+		return -1;
+
+	msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params);
+	if (!msg)
+		return -1;
+
+	if (params->p2p_probe) {
+		struct nlattr *rates;
+
+		wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
+		rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES);
+		if (rates == NULL)
+			goto fail;
+
+		/*
+		 * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
+		 * by masking out everything else apart from the OFDM rates 6,
+		 * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
+		 * rates are left enabled.
+		 */
+		if (nla_put(msg, NL80211_BAND_2GHZ, 8,
+			    "\x0c\x12\x18\x24\x30\x48\x60\x6c"))
+			goto fail;
+		nla_nest_end(msg, rates);
+
+		if (nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE))
+			goto fail;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+		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.
+			 */
+			if (wpa_driver_nl80211_set_mode(
+				    bss, NL80211_IFTYPE_STATION))
+				goto fail;
+
+			if (wpa_driver_nl80211_scan(bss, params)) {
+				wpa_driver_nl80211_set_mode(bss, old_mode);
+				goto fail;
+			}
+
+			/* Restore AP mode when processing scan results */
+			drv->ap_scan_as_station = old_mode;
+			ret = 0;
+		} else
+			goto fail;
+	}
+
+	drv->scan_state = SCAN_REQUESTED;
+	/* Not all drivers generate "scan completed" wireless event, so try to
+	 * read results after a timeout. */
+	timeout = 10;
+	if (drv->scan_complete_events) {
+		/*
+		 * The driver seems to deliver events to notify when scan is
+		 * complete, so use longer timeout to avoid race conditions
+		 * with scanning and following association request.
+		 */
+		timeout = 30;
+	}
+	wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
+		   "seconds", ret, timeout);
+	eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+	eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
+			       drv, drv->ctx);
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * @interval: Interval between scan cycles in milliseconds
+ * Returns: 0 on success, -1 on failure or if not supported
+ */
+int wpa_driver_nl80211_sched_scan(void *priv,
+				  struct wpa_driver_scan_params *params,
+				  u32 interval)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret = -1;
+	struct nl_msg *msg;
+	size_t i;
+
+	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request");
+
+#ifdef ANDROID
+	if (!drv->capa.sched_scan_supported)
+		return android_pno_start(bss, params);
+#endif /* ANDROID */
+
+	msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params);
+	if (!msg ||
+	    nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval))
+		goto fail;
+
+	if ((drv->num_filter_ssids &&
+	    (int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
+	    params->filter_rssi) {
+		struct nlattr *match_sets;
+		match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
+		if (match_sets == NULL)
+			goto fail;
+
+		for (i = 0; i < drv->num_filter_ssids; i++) {
+			struct nlattr *match_set_ssid;
+			wpa_hexdump_ascii(MSG_MSGDUMP,
+					  "nl80211: Sched scan filter SSID",
+					  drv->filter_ssids[i].ssid,
+					  drv->filter_ssids[i].ssid_len);
+
+			match_set_ssid = nla_nest_start(msg, i + 1);
+			if (match_set_ssid == NULL ||
+			    nla_put(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
+				    drv->filter_ssids[i].ssid_len,
+				    drv->filter_ssids[i].ssid) ||
+			    (params->filter_rssi &&
+			     nla_put_u32(msg,
+					 NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+					 params->filter_rssi)))
+				goto fail;
+
+			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);
+			if (match_set_rssi == NULL ||
+			    nla_put_u32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+					params->filter_rssi))
+				goto fail;
+			wpa_printf(MSG_MSGDUMP,
+				   "nl80211: Sched scan RSSI filter %d dBm",
+				   params->filter_rssi);
+			nla_nest_end(msg, match_set_rssi);
+		}
+
+		nla_nest_end(msg, match_sets);
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+
+	/* TODO: if we get an error here, we should fall back to normal scan */
+
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: "
+			   "ret=%d (%s)", ret, strerror(-ret));
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
+		   "scan interval %d msec", ret, interval);
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * Returns: 0 on success, -1 on failure or if not supported
+ */
+int wpa_driver_nl80211_stop_sched_scan(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret;
+	struct nl_msg *msg;
+
+#ifdef ANDROID
+	if (!drv->capa.sched_scan_supported)
+		return android_pno_stop(bss);
+#endif /* ANDROID */
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Sched scan stop failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Sched scan stop sent");
+	}
+
+	return ret;
+}
+
+
+const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie)
+{
+	const u8 *end, *pos;
+
+	if (ies == NULL)
+		return NULL;
+
+	pos = ies;
+	end = ies + ies_len;
+
+	while (pos + 1 < end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		if (pos[0] == ie)
+			return pos;
+		pos += 2 + pos[1];
+	}
+
+	return NULL;
+}
+
+
+static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv,
+				 const u8 *ie, size_t ie_len)
+{
+	const u8 *ssid;
+	size_t i;
+
+	if (drv->filter_ssids == NULL)
+		return 0;
+
+	ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID);
+	if (ssid == NULL)
+		return 1;
+
+	for (i = 0; i < drv->num_filter_ssids; i++) {
+		if (ssid[1] == drv->filter_ssids[i].ssid_len &&
+		    os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) ==
+		    0)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+int bss_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *bss[NL80211_BSS_MAX + 1];
+	static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+		[NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_BSS_TSF] = { .type = NLA_U64 },
+		[NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
+		[NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
+		[NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
+		[NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
+		[NL80211_BSS_STATUS] = { .type = NLA_U32 },
+		[NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
+		[NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
+	};
+	struct nl80211_bss_info_arg *_arg = arg;
+	struct wpa_scan_results *res = _arg->res;
+	struct wpa_scan_res **tmp;
+	struct wpa_scan_res *r;
+	const u8 *ie, *beacon_ie;
+	size_t ie_len, beacon_ie_len;
+	u8 *pos;
+	size_t i;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb[NL80211_ATTR_BSS])
+		return NL_SKIP;
+	if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
+			     bss_policy))
+		return NL_SKIP;
+	if (bss[NL80211_BSS_STATUS]) {
+		enum nl80211_bss_status status;
+		status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+		if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+		    bss[NL80211_BSS_FREQUENCY]) {
+			_arg->assoc_freq =
+				nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+			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,
+				  nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
+			wpa_printf(MSG_DEBUG, "nl80211: Associated with "
+				   MACSTR, MAC2STR(_arg->assoc_bssid));
+		}
+	}
+	if (!res)
+		return NL_SKIP;
+	if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+		ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+		ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+	} else {
+		ie = NULL;
+		ie_len = 0;
+	}
+	if (bss[NL80211_BSS_BEACON_IES]) {
+		beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]);
+		beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]);
+	} else {
+		beacon_ie = NULL;
+		beacon_ie_len = 0;
+	}
+
+	if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie,
+				  ie ? ie_len : beacon_ie_len))
+		return NL_SKIP;
+
+	r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
+	if (r == NULL)
+		return NL_SKIP;
+	if (bss[NL80211_BSS_BSSID])
+		os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]),
+			  ETH_ALEN);
+	if (bss[NL80211_BSS_FREQUENCY])
+		r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+	if (bss[NL80211_BSS_BEACON_INTERVAL])
+		r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]);
+	if (bss[NL80211_BSS_CAPABILITY])
+		r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
+	r->flags |= WPA_SCAN_NOISE_INVALID;
+	if (bss[NL80211_BSS_SIGNAL_MBM]) {
+		r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
+		r->level /= 100; /* mBm to dBm */
+		r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID;
+	} else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
+		r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
+		r->flags |= WPA_SCAN_QUAL_INVALID;
+	} else
+		r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
+	if (bss[NL80211_BSS_TSF])
+		r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]);
+	if (bss[NL80211_BSS_BEACON_TSF]) {
+		u64 tsf = nla_get_u64(bss[NL80211_BSS_BEACON_TSF]);
+		if (tsf > r->tsf)
+			r->tsf = tsf;
+	}
+	if (bss[NL80211_BSS_SEEN_MS_AGO])
+		r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
+	r->ie_len = ie_len;
+	pos = (u8 *) (r + 1);
+	if (ie) {
+		os_memcpy(pos, ie, ie_len);
+		pos += ie_len;
+	}
+	r->beacon_ie_len = beacon_ie_len;
+	if (beacon_ie)
+		os_memcpy(pos, beacon_ie, beacon_ie_len);
+
+	if (bss[NL80211_BSS_STATUS]) {
+		enum nl80211_bss_status status;
+		status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+		switch (status) {
+		case NL80211_BSS_STATUS_ASSOCIATED:
+			r->flags |= WPA_SCAN_ASSOCIATED;
+			break;
+		default:
+			break;
+		}
+	}
+
+	/*
+	 * cfg80211 maintains separate BSS table entries for APs if the same
+	 * 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. Similarly,
+	 * prefer newer entries over older.
+	 */
+	for (i = 0; i < res->num; i++) {
+		const u8 *s1, *s2;
+		if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0)
+			continue;
+
+		s1 = nl80211_get_ie((u8 *) (res->res[i] + 1),
+				    res->res[i]->ie_len, WLAN_EID_SSID);
+		s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID);
+		if (s1 == NULL || s2 == NULL || s1[1] != s2[1] ||
+		    os_memcmp(s1, s2, 2 + s1[1]) != 0)
+			continue;
+
+		/* Same BSSID,SSID was already included in scan results */
+		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)) ||
+		    r->age < res->res[i]->age) {
+			os_free(res->res[i]);
+			res->res[i] = r;
+		} else
+			os_free(r);
+		return NL_SKIP;
+	}
+
+	tmp = os_realloc_array(res->res, res->num + 1,
+			       sizeof(struct wpa_scan_res *));
+	if (tmp == NULL) {
+		os_free(r);
+		return NL_SKIP;
+	}
+	tmp[res->num++] = r;
+	res->res = tmp;
+
+	return NL_SKIP;
+}
+
+
+static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv,
+				 const u8 *addr)
+{
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+		wpa_printf(MSG_DEBUG, "nl80211: Clear possible state "
+			   "mismatch (" MACSTR ")", MAC2STR(addr));
+		wpa_driver_nl80211_mlme(drv, addr,
+					NL80211_CMD_DEAUTHENTICATE,
+					WLAN_REASON_PREV_AUTH_NOT_VALID, 1);
+	}
+}
+
+
+static void wpa_driver_nl80211_check_bss_status(
+	struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res)
+{
+	size_t i;
+
+	for (i = 0; i < res->num; i++) {
+		struct wpa_scan_res *r = res->res[i];
+
+		if (r->flags & WPA_SCAN_ASSOCIATED) {
+			wpa_printf(MSG_DEBUG, "nl80211: Scan results "
+				   "indicate BSS status with " MACSTR
+				   " as associated",
+				   MAC2STR(r->bssid));
+			if (is_sta_interface(drv->nlmode) &&
+			    !drv->associated) {
+				wpa_printf(MSG_DEBUG, "nl80211: Local state "
+					   "(not associated) does not match "
+					   "with BSS state");
+				clear_state_mismatch(drv, r->bssid);
+			} else if (is_sta_interface(drv->nlmode) &&
+				   os_memcmp(drv->bssid, r->bssid, ETH_ALEN) !=
+				   0) {
+				wpa_printf(MSG_DEBUG, "nl80211: Local state "
+					   "(associated with " MACSTR ") does "
+					   "not match with BSS state",
+					   MAC2STR(drv->bssid));
+				clear_state_mismatch(drv, r->bssid);
+				clear_state_mismatch(drv, drv->bssid);
+			}
+		}
+	}
+}
+
+
+static struct wpa_scan_results *
+nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	struct wpa_scan_results *res;
+	int ret;
+	struct nl80211_bss_info_arg arg;
+
+	res = os_zalloc(sizeof(*res));
+	if (res == NULL)
+		return NULL;
+	if (!(msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP,
+				    NL80211_CMD_GET_SCAN))) {
+		wpa_scan_results_free(res);
+		return NULL;
+	}
+
+	arg.drv = drv;
+	arg.res = res;
+	ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+	if (ret == 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
+			   "BSSes)", (unsigned long) res->num);
+		nl80211_get_noise_for_scan_results(drv, res);
+		return res;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+		   "(%s)", ret, strerror(-ret));
+	wpa_scan_results_free(res);
+	return NULL;
+}
+
+
+/**
+ * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
+ * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
+ * Returns: Scan results on success, -1 on failure
+ */
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct wpa_scan_results *res;
+
+	res = nl80211_get_scan_results(drv);
+	if (res)
+		wpa_driver_nl80211_check_bss_status(drv, res);
+	return res;
+}
+
+
+void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
+{
+	struct wpa_scan_results *res;
+	size_t i;
+
+	res = nl80211_get_scan_results(drv);
+	if (res == NULL) {
+		wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
+	for (i = 0; i < res->num; i++) {
+		struct wpa_scan_res *r = res->res[i];
+		wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s",
+			   (int) i, (int) res->num, MAC2STR(r->bssid),
+			   r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
+	}
+
+	wpa_scan_results_free(res);
+}
diff --git a/hostap/src/drivers/driver_none.c b/hostap/src/drivers/driver_none.c
new file mode 100644
index 0000000..6ff3eae
--- /dev/null
+++ b/hostap/src/drivers/driver_none.c
@@ -0,0 +1,85 @@
+/*
+ * Driver interface for RADIUS server or WPS ER only (no driver)
+ * Copyright (c) 2008, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "driver.h"
+
+
+struct none_driver_data {
+	struct hostapd_data *hapd;
+	void *ctx;
+};
+
+
+static void * none_driver_hapd_init(struct hostapd_data *hapd,
+				    struct wpa_init_params *params)
+{
+	struct none_driver_data *drv;
+
+	drv = os_zalloc(sizeof(struct none_driver_data));
+	if (drv == NULL) {
+		wpa_printf(MSG_ERROR, "Could not allocate memory for none "
+			   "driver data");
+		return NULL;
+	}
+	drv->hapd = hapd;
+
+	return drv;
+}
+
+
+static void none_driver_hapd_deinit(void *priv)
+{
+	struct none_driver_data *drv = priv;
+
+	os_free(drv);
+}
+
+
+static int none_driver_send_ether(void *priv, const u8 *dst, const u8 *src,
+				  u16 proto, const u8 *data, size_t data_len)
+{
+	return 0;
+}
+
+
+static void * none_driver_init(void *ctx, const char *ifname)
+{
+	struct none_driver_data *drv;
+
+	drv = os_zalloc(sizeof(struct none_driver_data));
+	if (drv == NULL) {
+		wpa_printf(MSG_ERROR, "Could not allocate memory for none "
+			   "driver data");
+		return NULL;
+	}
+	drv->ctx = ctx;
+
+	return drv;
+}
+
+
+static void none_driver_deinit(void *priv)
+{
+	struct none_driver_data *drv = priv;
+
+	os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_none_ops = {
+	.name = "none",
+	.desc = "no driver (RADIUS server/WPS ER)",
+	.hapd_init = none_driver_hapd_init,
+	.hapd_deinit = none_driver_hapd_deinit,
+	.send_ether = none_driver_send_ether,
+	.init = none_driver_init,
+	.deinit = none_driver_deinit,
+};
diff --git a/hostap/src/drivers/driver_openbsd.c b/hostap/src/drivers/driver_openbsd.c
new file mode 100644
index 0000000..e94eda0
--- /dev/null
+++ b/hostap/src/drivers/driver_openbsd.c
@@ -0,0 +1,136 @@
+/*
+ * Driver interaction with OpenBSD net80211 layer
+ * Copyright (c) 2013, Mark Kettenis
+ *
+ * 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>
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_crypto.h>
+#include <net80211/ieee80211_ioctl.h>
+
+#include "common.h"
+#include "driver.h"
+
+struct openbsd_driver_data {
+	char ifname[IFNAMSIZ + 1];
+	void *ctx;
+
+	int sock;			/* open socket for 802.11 ioctls */
+};
+
+
+static int
+wpa_driver_openbsd_get_ssid(void *priv, u8 *ssid)
+{
+	struct openbsd_driver_data *drv = priv;
+	struct ieee80211_nwid nwid;
+	struct ifreq ifr;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *)&nwid;
+	if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 ||
+	    nwid.i_len > IEEE80211_NWID_LEN)
+		return -1;
+
+	os_memcpy(ssid, nwid.i_nwid, nwid.i_len);
+	return nwid.i_len;
+}
+
+static int
+wpa_driver_openbsd_get_bssid(void *priv, u8 *bssid)
+{
+	struct openbsd_driver_data *drv = priv;
+	struct ieee80211_bssid id;
+
+	os_strlcpy(id.i_name, drv->ifname, sizeof(id.i_name));
+	if (ioctl(drv->sock, SIOCG80211BSSID, &id) < 0)
+		return -1;
+
+	os_memcpy(bssid, id.i_bssid, IEEE80211_ADDR_LEN);
+	return 0;
+}
+
+
+static int
+wpa_driver_openbsd_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+	os_memset(capa, 0, sizeof(*capa));
+	capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+	return 0;
+}
+
+
+static int
+wpa_driver_openbsd_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 openbsd_driver_data *drv = priv;
+	struct ieee80211_keyavail keyavail;
+
+	if (alg != WPA_ALG_PMK || key_len > IEEE80211_PMK_LEN)
+		return -1;
+
+	memset(&keyavail, 0, sizeof(keyavail));
+	os_strlcpy(keyavail.i_name, drv->ifname, sizeof(keyavail.i_name));
+	if (wpa_driver_openbsd_get_bssid(priv, keyavail.i_macaddr) < 0)
+		return -1;
+	memcpy(keyavail.i_key, key, key_len);
+
+	if (ioctl(drv->sock, SIOCS80211KEYAVAIL, &keyavail) < 0)
+		return -1;
+
+	return 0;
+}
+
+static void *
+wpa_driver_openbsd_init(void *ctx, const char *ifname)
+{
+	struct openbsd_driver_data *drv;
+
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL)
+		return NULL;
+
+	drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (drv->sock < 0)
+		goto fail;
+
+	drv->ctx = ctx;
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+	return drv;
+
+fail:
+	os_free(drv);
+	return NULL;
+}
+
+
+static void
+wpa_driver_openbsd_deinit(void *priv)
+{
+	struct openbsd_driver_data *drv = priv;
+
+	close(drv->sock);
+	os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_openbsd_ops = {
+	.name = "openbsd",
+	.desc = "OpenBSD 802.11 support",
+	.get_ssid = wpa_driver_openbsd_get_ssid,
+	.get_bssid = wpa_driver_openbsd_get_bssid,
+	.get_capa = wpa_driver_openbsd_get_capa,
+	.set_key = wpa_driver_openbsd_set_key,
+	.init = wpa_driver_openbsd_init,
+	.deinit = wpa_driver_openbsd_deinit,
+};
diff --git a/hostap/src/drivers/driver_privsep.c b/hostap/src/drivers/driver_privsep.c
new file mode 100644
index 0000000..1f1676a
--- /dev/null
+++ b/hostap/src/drivers/driver_privsep.c
@@ -0,0 +1,837 @@
+/*
+ * WPA Supplicant - privilege separated driver interface
+ * Copyright (c) 2007-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 <sys/un.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/privsep_commands.h"
+
+
+struct wpa_driver_privsep_data {
+	void *ctx;
+	u8 own_addr[ETH_ALEN];
+	int priv_socket;
+	char *own_socket_path;
+	int cmd_socket;
+	char *own_cmd_path;
+	struct sockaddr_un priv_addr;
+	char ifname[16];
+};
+
+
+static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd)
+{
+	int res;
+
+	res = sendto(drv->priv_socket, &cmd, sizeof(cmd), 0,
+		     (struct sockaddr *) &drv->priv_addr,
+		     sizeof(drv->priv_addr));
+	if (res < 0)
+		wpa_printf(MSG_ERROR, "sendto: %s", strerror(errno));
+	return res < 0 ? -1 : 0;
+}
+
+
+static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd,
+			const void *data, size_t data_len,
+			void *reply, size_t *reply_len)
+{
+	struct msghdr msg;
+	struct iovec io[2];
+
+	io[0].iov_base = &cmd;
+	io[0].iov_len = sizeof(cmd);
+	io[1].iov_base = (u8 *) data;
+	io[1].iov_len = data_len;
+
+	os_memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = io;
+	msg.msg_iovlen = data ? 2 : 1;
+	msg.msg_name = &drv->priv_addr;
+	msg.msg_namelen = sizeof(drv->priv_addr);
+
+	if (sendmsg(drv->cmd_socket, &msg, 0) < 0) {
+		wpa_printf(MSG_ERROR, "sendmsg(cmd_socket): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (reply) {
+		fd_set rfds;
+		struct timeval tv;
+		int res;
+
+		FD_ZERO(&rfds);
+		FD_SET(drv->cmd_socket, &rfds);
+		tv.tv_sec = 5;
+		tv.tv_usec = 0;
+		res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv);
+		if (res < 0 && errno != EINTR) {
+			wpa_printf(MSG_ERROR, "select: %s", strerror(errno));
+			return -1;
+		}
+
+		if (FD_ISSET(drv->cmd_socket, &rfds)) {
+			res = recv(drv->cmd_socket, reply, *reply_len, 0);
+			if (res < 0) {
+				wpa_printf(MSG_ERROR, "recv: %s",
+					   strerror(errno));
+				return -1;
+			}
+			*reply_len = res;
+		} else {
+			wpa_printf(MSG_DEBUG, "PRIVSEP: Timeout while waiting "
+				   "for reply (cmd=%d)", cmd);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+			     
+static int wpa_driver_privsep_scan(void *priv,
+				   struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	const u8 *ssid = params->ssids[0].ssid;
+	size_t ssid_len = params->ssids[0].ssid_len;
+	wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv);
+	return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len,
+			    NULL, NULL);
+}
+
+
+static struct wpa_scan_results *
+wpa_driver_privsep_get_scan_results2(void *priv)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	int res, num;
+	u8 *buf, *pos, *end;
+	size_t reply_len = 60000;
+	struct wpa_scan_results *results;
+	struct wpa_scan_res *r;
+
+	buf = os_malloc(reply_len);
+	if (buf == NULL)
+		return NULL;
+	res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SCAN_RESULTS,
+			   NULL, 0, buf, &reply_len);
+	if (res < 0) {
+		os_free(buf);
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "privsep: Received %lu bytes of scan results",
+		   (unsigned long) reply_len);
+	if (reply_len < sizeof(int)) {
+		wpa_printf(MSG_DEBUG, "privsep: Invalid scan result len %lu",
+			   (unsigned long) reply_len);
+		os_free(buf);
+		return NULL;
+	}
+
+	pos = buf;
+	end = buf + reply_len;
+	os_memcpy(&num, pos, sizeof(int));
+	if (num < 0 || num > 1000) {
+		os_free(buf);
+		return NULL;
+	}
+	pos += sizeof(int);
+
+	results = os_zalloc(sizeof(*results));
+	if (results == NULL) {
+		os_free(buf);
+		return NULL;
+	}
+
+	results->res = os_calloc(num, sizeof(struct wpa_scan_res *));
+	if (results->res == NULL) {
+		os_free(results);
+		os_free(buf);
+		return NULL;
+	}
+
+	while (results->num < (size_t) num && pos + sizeof(int) < end) {
+		int len;
+		os_memcpy(&len, pos, sizeof(int));
+		pos += sizeof(int);
+		if (len < 0 || len > 10000 || pos + len > end)
+			break;
+
+		r = os_malloc(len);
+		if (r == NULL)
+			break;
+		os_memcpy(r, pos, len);
+		pos += len;
+		if (sizeof(*r) + r->ie_len > (size_t) len) {
+			os_free(r);
+			break;
+		}
+
+		results->res[results->num++] = r;
+	}
+
+	os_free(buf);
+	return results;
+}
+
+
+static int wpa_driver_privsep_set_key(const char *ifname, void *priv,
+				      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)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	struct privsep_cmd_set_key cmd;
+
+	wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d",
+		   __func__, priv, alg, key_idx, set_tx);
+
+	os_memset(&cmd, 0, sizeof(cmd));
+	cmd.alg = alg;
+	if (addr)
+		os_memcpy(cmd.addr, addr, ETH_ALEN);
+	else
+		os_memset(cmd.addr, 0xff, ETH_ALEN);
+	cmd.key_idx = key_idx;
+	cmd.set_tx = set_tx;
+	if (seq && seq_len > 0 && seq_len < sizeof(cmd.seq)) {
+		os_memcpy(cmd.seq, seq, seq_len);
+		cmd.seq_len = seq_len;
+	}
+	if (key && key_len > 0 && key_len < sizeof(cmd.key)) {
+		os_memcpy(cmd.key, key, key_len);
+		cmd.key_len = key_len;
+	}
+
+	return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_KEY, &cmd, sizeof(cmd),
+			    NULL, NULL);
+}
+
+
+static int wpa_driver_privsep_authenticate(
+	void *priv, struct wpa_driver_auth_params *params)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	struct privsep_cmd_authenticate *data;
+	int i, res;
+	size_t buflen;
+	u8 *pos;
+
+	wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d bssid=" MACSTR
+		   " auth_alg=%d local_state_change=%d p2p=%d",
+		   __func__, priv, params->freq, MAC2STR(params->bssid),
+		   params->auth_alg, params->local_state_change, params->p2p);
+
+	buflen = sizeof(*data) + params->ie_len + params->sae_data_len;
+	data = os_zalloc(buflen);
+	if (data == NULL)
+		return -1;
+
+	data->freq = params->freq;
+	os_memcpy(data->bssid, params->bssid, ETH_ALEN);
+	os_memcpy(data->ssid, params->ssid, params->ssid_len);
+	data->ssid_len = params->ssid_len;
+	data->auth_alg = params->auth_alg;
+	data->ie_len = params->ie_len;
+	for (i = 0; i < 4; i++) {
+		if (params->wep_key[i])
+			os_memcpy(data->wep_key[i], params->wep_key[i],
+				  params->wep_key_len[i]);
+		data->wep_key_len[i] = params->wep_key_len[i];
+	}
+	data->wep_tx_keyidx = params->wep_tx_keyidx;
+	data->local_state_change = params->local_state_change;
+	data->p2p = params->p2p;
+	pos = (u8 *) (data + 1);
+	if (params->ie_len) {
+		os_memcpy(pos, params->ie, params->ie_len);
+		pos += params->ie_len;
+	}
+	if (params->sae_data_len)
+		os_memcpy(pos, params->sae_data, params->sae_data_len);
+
+	res = wpa_priv_cmd(drv, PRIVSEP_CMD_AUTHENTICATE, data, buflen,
+			   NULL, NULL);
+	os_free(data);
+
+	return res;
+}
+
+
+static int wpa_driver_privsep_associate(
+	void *priv, struct wpa_driver_associate_params *params)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	struct privsep_cmd_associate *data;
+	int res;
+	size_t buflen;
+
+	wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d "
+		   "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d",
+		   __func__, priv, params->freq.freq, params->pairwise_suite,
+		   params->group_suite, params->key_mgmt_suite,
+		   params->auth_alg, params->mode);
+
+	buflen = sizeof(*data) + params->wpa_ie_len;
+	data = os_zalloc(buflen);
+	if (data == NULL)
+		return -1;
+
+	if (params->bssid)
+		os_memcpy(data->bssid, params->bssid, ETH_ALEN);
+	os_memcpy(data->ssid, params->ssid, params->ssid_len);
+	data->ssid_len = params->ssid_len;
+	data->hwmode = params->freq.mode;
+	data->freq = params->freq.freq;
+	data->channel = params->freq.channel;
+	data->pairwise_suite = params->pairwise_suite;
+	data->group_suite = params->group_suite;
+	data->key_mgmt_suite = params->key_mgmt_suite;
+	data->auth_alg = params->auth_alg;
+	data->mode = params->mode;
+	data->wpa_ie_len = params->wpa_ie_len;
+	if (params->wpa_ie)
+		os_memcpy(data + 1, params->wpa_ie, params->wpa_ie_len);
+	/* TODO: add support for other assoc parameters */
+
+	res = wpa_priv_cmd(drv, PRIVSEP_CMD_ASSOCIATE, data, buflen,
+			   NULL, NULL);
+	os_free(data);
+
+	return res;
+}
+
+
+static int wpa_driver_privsep_get_bssid(void *priv, u8 *bssid)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	int res;
+	size_t len = ETH_ALEN;
+
+	res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_BSSID, NULL, 0, bssid, &len);
+	if (res < 0 || len != ETH_ALEN)
+		return -1;
+	return 0;
+}
+
+
+static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	int res, ssid_len;
+	u8 reply[sizeof(int) + SSID_MAX_LEN];
+	size_t len = sizeof(reply);
+
+	res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len);
+	if (res < 0 || len < sizeof(int))
+		return -1;
+	os_memcpy(&ssid_len, reply, sizeof(int));
+	if (ssid_len < 0 || ssid_len > SSID_MAX_LEN ||
+	    sizeof(int) + ssid_len > len) {
+		wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply");
+		return -1;
+	}
+	os_memcpy(ssid, &reply[sizeof(int)], ssid_len);
+	return ssid_len;
+}
+
+
+static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr,
+					  int reason_code)
+{
+	//struct wpa_driver_privsep_data *drv = priv;
+	wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
+		   __func__, MAC2STR(addr), reason_code);
+	wpa_printf(MSG_DEBUG, "%s - TODO", __func__);
+	return 0;
+}
+
+
+static void wpa_driver_privsep_event_auth(void *ctx, u8 *buf, size_t len)
+{
+	union wpa_event_data data;
+	struct privsep_event_auth *auth;
+
+	os_memset(&data, 0, sizeof(data));
+	if (len < sizeof(*auth))
+		return;
+	auth = (struct privsep_event_auth *) buf;
+	if (len < sizeof(*auth) + auth->ies_len)
+		return;
+
+	os_memcpy(data.auth.peer, auth->peer, ETH_ALEN);
+	os_memcpy(data.auth.bssid, auth->bssid, ETH_ALEN);
+	data.auth.auth_type = auth->auth_type;
+	data.auth.auth_transaction = auth->auth_transaction;
+	data.auth.status_code = auth->status_code;
+	if (auth->ies_len) {
+		data.auth.ies = (u8 *) (auth + 1);
+		data.auth.ies_len = auth->ies_len;
+	}
+
+	wpa_supplicant_event(ctx, EVENT_AUTH, &data);
+}
+
+
+static void wpa_driver_privsep_event_assoc(void *ctx,
+					   enum wpa_event_type event,
+					   u8 *buf, size_t len)
+{
+	union wpa_event_data data;
+	int inc_data = 0;
+	u8 *pos, *end;
+	int ie_len;
+
+	os_memset(&data, 0, sizeof(data));
+
+	pos = buf;
+	end = buf + len;
+
+	if (end - pos < (int) sizeof(int))
+		return;
+	os_memcpy(&ie_len, pos, sizeof(int));
+	pos += sizeof(int);
+	if (ie_len < 0 || ie_len > end - pos)
+		return;
+	if (ie_len) {
+		data.assoc_info.req_ies = pos;
+		data.assoc_info.req_ies_len = ie_len;
+		pos += ie_len;
+		inc_data = 1;
+	}
+
+	wpa_supplicant_event(ctx, event, inc_data ? &data : NULL);
+}
+
+
+static void wpa_driver_privsep_event_interface_status(void *ctx, u8 *buf,
+						      size_t len)
+{
+	union wpa_event_data data;
+	int ievent;
+
+	if (len < sizeof(int) ||
+	    len - sizeof(int) > sizeof(data.interface_status.ifname))
+		return;
+
+	os_memcpy(&ievent, buf, sizeof(int));
+
+	os_memset(&data, 0, sizeof(data));
+	data.interface_status.ievent = ievent;
+	os_memcpy(data.interface_status.ifname, buf + sizeof(int),
+		  len - sizeof(int));
+	wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &data);
+}
+
+
+static void wpa_driver_privsep_event_michael_mic_failure(
+	void *ctx, u8 *buf, size_t len)
+{
+	union wpa_event_data data;
+
+	if (len != sizeof(int))
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(&data.michael_mic_failure.unicast, buf, sizeof(int));
+	wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+}
+
+
+static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf,
+						     size_t len)
+{
+	union wpa_event_data data;
+
+	if (len != sizeof(struct pmkid_candidate))
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(&data.pmkid_candidate, buf, len);
+	wpa_supplicant_event(ctx, EVENT_PMKID_CANDIDATE, &data);
+}
+
+
+static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len)
+{
+	union wpa_event_data data;
+
+	if (len != ETH_ALEN)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.stkstart.peer, buf, ETH_ALEN);
+	wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
+}
+
+
+static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf,
+						 size_t len)
+{
+	union wpa_event_data data;
+
+	if (len < sizeof(int) + ETH_ALEN)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(&data.ft_ies.ft_action, buf, sizeof(int));
+	os_memcpy(data.ft_ies.target_ap, buf + sizeof(int), ETH_ALEN);
+	data.ft_ies.ies = buf + sizeof(int) + ETH_ALEN;
+	data.ft_ies.ies_len = len - sizeof(int) - ETH_ALEN;
+	wpa_supplicant_event(ctx, EVENT_FT_RESPONSE, &data);
+}
+
+
+static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len)
+{
+	if (len < ETH_ALEN)
+		return;
+	drv_event_eapol_rx(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN);
+}
+
+
+static void wpa_driver_privsep_receive(int sock, void *eloop_ctx,
+				       void *sock_ctx)
+{
+	struct wpa_driver_privsep_data *drv = eloop_ctx;
+	u8 *buf, *event_buf;
+	size_t event_len;
+	int res, event;
+	enum privsep_event e;
+	struct sockaddr_un from;
+	socklen_t fromlen = sizeof(from);
+	const size_t buflen = 2000;
+
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return;
+	res = recvfrom(sock, buf, buflen, 0,
+		       (struct sockaddr *) &from, &fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "recvfrom(priv_socket): %s",
+			   strerror(errno));
+		os_free(buf);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "privsep_driver: received %u bytes", res);
+
+	if (res < (int) sizeof(int)) {
+		wpa_printf(MSG_DEBUG, "Too short event message (len=%d)", res);
+		return;
+	}
+
+	os_memcpy(&event, buf, sizeof(int));
+	event_buf = &buf[sizeof(int)];
+	event_len = res - sizeof(int);
+	wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%lu)",
+		   event, (unsigned long) event_len);
+
+	e = event;
+	switch (e) {
+	case PRIVSEP_EVENT_SCAN_RESULTS:
+		wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
+		break;
+	case PRIVSEP_EVENT_SCAN_STARTED:
+		wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL);
+		break;
+	case PRIVSEP_EVENT_ASSOC:
+		wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC,
+					       event_buf, event_len);
+		break;
+	case PRIVSEP_EVENT_DISASSOC:
+		wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+		break;
+	case PRIVSEP_EVENT_ASSOCINFO:
+		wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOCINFO,
+					       event_buf, event_len);
+		break;
+	case PRIVSEP_EVENT_MICHAEL_MIC_FAILURE:
+		wpa_driver_privsep_event_michael_mic_failure(
+			drv->ctx, event_buf, event_len);
+		break;
+	case PRIVSEP_EVENT_INTERFACE_STATUS:
+		wpa_driver_privsep_event_interface_status(drv->ctx, event_buf,
+							  event_len);
+		break;
+	case PRIVSEP_EVENT_PMKID_CANDIDATE:
+		wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf,
+							 event_len);
+		break;
+	case PRIVSEP_EVENT_STKSTART:
+		wpa_driver_privsep_event_stkstart(drv->ctx, event_buf,
+						  event_len);
+		break;
+	case PRIVSEP_EVENT_FT_RESPONSE:
+		wpa_driver_privsep_event_ft_response(drv->ctx, event_buf,
+						     event_len);
+		break;
+	case PRIVSEP_EVENT_RX_EAPOL:
+		wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf,
+						  event_len);
+		break;
+	case PRIVSEP_EVENT_AUTH:
+		wpa_driver_privsep_event_auth(drv->ctx, event_buf, event_len);
+		break;
+	}
+
+	os_free(buf);
+}
+
+
+static void * wpa_driver_privsep_init(void *ctx, const char *ifname)
+{
+	struct wpa_driver_privsep_data *drv;
+
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL)
+		return NULL;
+	drv->ctx = ctx;
+	drv->priv_socket = -1;
+	drv->cmd_socket = -1;
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+	return drv;
+}
+
+
+static void wpa_driver_privsep_deinit(void *priv)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+
+	if (drv->priv_socket >= 0) {
+		wpa_priv_reg_cmd(drv, PRIVSEP_CMD_UNREGISTER);
+		eloop_unregister_read_sock(drv->priv_socket);
+		close(drv->priv_socket);
+	}
+
+	if (drv->own_socket_path) {
+		unlink(drv->own_socket_path);
+		os_free(drv->own_socket_path);
+	}
+
+	if (drv->cmd_socket >= 0) {
+		eloop_unregister_read_sock(drv->cmd_socket);
+		close(drv->cmd_socket);
+	}
+
+	if (drv->own_cmd_path) {
+		unlink(drv->own_cmd_path);
+		os_free(drv->own_cmd_path);
+	}
+
+	os_free(drv);
+}
+
+
+static int wpa_driver_privsep_set_param(void *priv, const char *param)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	const char *pos;
+	char *own_dir, *priv_dir;
+	static unsigned int counter = 0;
+	size_t len;
+	struct sockaddr_un addr;
+
+	wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param);
+	if (param == NULL)
+		pos = NULL;
+	else
+		pos = os_strstr(param, "own_dir=");
+	if (pos) {
+		char *end;
+		own_dir = os_strdup(pos + 8);
+		if (own_dir == NULL)
+			return -1;
+		end = os_strchr(own_dir, ' ');
+		if (end)
+			*end = '\0';
+	} else {
+		own_dir = os_strdup("/tmp");
+		if (own_dir == NULL)
+			return -1;
+	}
+
+	if (param == NULL)
+		pos = NULL;
+	else
+		pos = os_strstr(param, "priv_dir=");
+	if (pos) {
+		char *end;
+		priv_dir = os_strdup(pos + 9);
+		if (priv_dir == NULL) {
+			os_free(own_dir);
+			return -1;
+		}
+		end = os_strchr(priv_dir, ' ');
+		if (end)
+			*end = '\0';
+	} else {
+		priv_dir = os_strdup("/var/run/wpa_priv");
+		if (priv_dir == NULL) {
+			os_free(own_dir);
+			return -1;
+		}
+	}
+
+	len = os_strlen(own_dir) + 50;
+	drv->own_socket_path = os_malloc(len);
+	if (drv->own_socket_path == NULL) {
+		os_free(priv_dir);
+		os_free(own_dir);
+		return -1;
+	}
+	os_snprintf(drv->own_socket_path, len, "%s/wpa_privsep-%d-%d",
+		    own_dir, getpid(), counter++);
+
+	len = os_strlen(own_dir) + 50;
+	drv->own_cmd_path = os_malloc(len);
+	if (drv->own_cmd_path == NULL) {
+		os_free(drv->own_socket_path);
+		drv->own_socket_path = NULL;
+		os_free(priv_dir);
+		os_free(own_dir);
+		return -1;
+	}
+	os_snprintf(drv->own_cmd_path, len, "%s/wpa_privsep-%d-%d",
+		    own_dir, getpid(), counter++);
+
+	os_free(own_dir);
+
+	drv->priv_addr.sun_family = AF_UNIX;
+	os_snprintf(drv->priv_addr.sun_path, sizeof(drv->priv_addr.sun_path),
+		    "%s/%s", priv_dir, drv->ifname);
+	os_free(priv_dir);
+
+	drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (drv->priv_socket < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
+		os_free(drv->own_socket_path);
+		drv->own_socket_path = NULL;
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
+	if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) <
+	    0) {
+		wpa_printf(MSG_ERROR,
+			   "privsep-set-params priv-sock: bind(PF_UNIX): %s",
+			   strerror(errno));
+		close(drv->priv_socket);
+		drv->priv_socket = -1;
+		unlink(drv->own_socket_path);
+		os_free(drv->own_socket_path);
+		drv->own_socket_path = NULL;
+		return -1;
+	}
+
+	eloop_register_read_sock(drv->priv_socket, wpa_driver_privsep_receive,
+				 drv, NULL);
+
+	drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (drv->cmd_socket < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
+		os_free(drv->own_cmd_path);
+		drv->own_cmd_path = NULL;
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path));
+	if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+	{
+		wpa_printf(MSG_ERROR,
+			   "privsep-set-params cmd-sock: bind(PF_UNIX): %s",
+			   strerror(errno));
+		close(drv->cmd_socket);
+		drv->cmd_socket = -1;
+		unlink(drv->own_cmd_path);
+		os_free(drv->own_cmd_path);
+		drv->own_cmd_path = NULL;
+		return -1;
+	}
+
+	if (wpa_priv_reg_cmd(drv, PRIVSEP_CMD_REGISTER) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to register with wpa_priv");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_privsep_get_capa(void *priv,
+				       struct wpa_driver_capa *capa)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	int res;
+	size_t len = sizeof(*capa);
+
+	res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len);
+	if (res < 0 || len != sizeof(*capa))
+		return -1;
+	/* For now, no support for passing extended_capa pointers */
+	capa->extended_capa = NULL;
+	capa->extended_capa_mask = NULL;
+	capa->extended_capa_len = 0;
+	return 0;
+}
+
+
+static const u8 * wpa_driver_privsep_get_mac_addr(void *priv)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+	return drv->own_addr;
+}
+
+
+static int wpa_driver_privsep_set_country(void *priv, const char *alpha2)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	wpa_printf(MSG_DEBUG, "%s country='%s'", __func__, alpha2);
+	return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_COUNTRY, alpha2,
+			    os_strlen(alpha2), NULL, NULL);
+}
+
+
+struct wpa_driver_ops wpa_driver_privsep_ops = {
+	"privsep",
+	"wpa_supplicant privilege separated driver",
+	.get_bssid = wpa_driver_privsep_get_bssid,
+	.get_ssid = wpa_driver_privsep_get_ssid,
+	.set_key = wpa_driver_privsep_set_key,
+	.init = wpa_driver_privsep_init,
+	.deinit = wpa_driver_privsep_deinit,
+	.set_param = wpa_driver_privsep_set_param,
+	.scan2 = wpa_driver_privsep_scan,
+	.deauthenticate = wpa_driver_privsep_deauthenticate,
+	.authenticate = wpa_driver_privsep_authenticate,
+	.associate = wpa_driver_privsep_associate,
+	.get_capa = wpa_driver_privsep_get_capa,
+	.get_mac_addr = wpa_driver_privsep_get_mac_addr,
+	.get_scan_results2 = wpa_driver_privsep_get_scan_results2,
+	.set_country = wpa_driver_privsep_set_country,
+};
+
+
+const struct wpa_driver_ops *const wpa_drivers[] =
+{
+	&wpa_driver_privsep_ops,
+	NULL
+};
diff --git a/hostap/src/drivers/driver_roboswitch.c b/hostap/src/drivers/driver_roboswitch.c
new file mode 100644
index 0000000..d3e0595
--- /dev/null
+++ b/hostap/src/drivers/driver_roboswitch.c
@@ -0,0 +1,477 @@
+/*
+ * WPA Supplicant - roboswitch driver interface
+ * Copyright (c) 2008-2009 Jouke Witteveen
+ *
+ * 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 <linux/sockios.h>
+#include <linux/if_ether.h>
+#include <linux/mii.h>
+#include <net/if.h>
+
+#include "common.h"
+#include "driver.h"
+#include "l2_packet/l2_packet.h"
+
+#define ROBO_PHY_ADDR		0x1e	/* RoboSwitch PHY address */
+
+/* MII access registers */
+#define ROBO_MII_PAGE		0x10	/* MII page register */
+#define ROBO_MII_ADDR		0x11	/* MII address register */
+#define ROBO_MII_DATA_OFFSET	0x18	/* Start of MII data registers */
+
+#define ROBO_MII_PAGE_ENABLE	0x01	/* MII page op code */
+#define ROBO_MII_ADDR_WRITE	0x01	/* MII address write op code */
+#define ROBO_MII_ADDR_READ	0x02	/* MII address read op code */
+#define ROBO_MII_DATA_MAX	   4	/* Consecutive MII data registers */
+#define ROBO_MII_RETRY_MAX	  10	/* Read attempts before giving up */
+
+/* Page numbers */
+#define ROBO_ARLCTRL_PAGE	0x04	/* ARL control page */
+#define ROBO_VLAN_PAGE		0x34	/* VLAN page */
+
+/* ARL control page registers */
+#define ROBO_ARLCTRL_CONF	0x00	/* ARL configuration register */
+#define ROBO_ARLCTRL_ADDR_1	0x10	/* Multiport address 1 */
+#define ROBO_ARLCTRL_VEC_1	0x16	/* Multiport vector 1 */
+#define ROBO_ARLCTRL_ADDR_2	0x20	/* Multiport address 2 */
+#define ROBO_ARLCTRL_VEC_2	0x26	/* Multiport vector 2 */
+
+/* VLAN page registers */
+#define ROBO_VLAN_ACCESS	0x08	/* VLAN table access register */
+#define ROBO_VLAN_ACCESS_5350	0x06	/* VLAN table access register (5350) */
+#define ROBO_VLAN_READ		0x0c	/* VLAN read register */
+#define ROBO_VLAN_MAX		0xff	/* Maximum number of VLANs */
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+
+struct wpa_driver_roboswitch_data {
+	void *ctx;
+	struct l2_packet_data *l2;
+	char ifname[IFNAMSIZ + 1];
+	u8 own_addr[ETH_ALEN];
+	struct ifreq ifr;
+	int fd, is_5350;
+	u16 ports;
+};
+
+
+/* Copied from the kernel-only part of mii.h. */
+static inline struct mii_ioctl_data *if_mii(struct ifreq *rq)
+{
+	return (struct mii_ioctl_data *) &rq->ifr_ifru;
+}
+
+
+/*
+ * RoboSwitch uses 16-bit Big Endian addresses.
+ * The ordering of the words is reversed in the MII registers.
+ */
+static void wpa_driver_roboswitch_addr_be16(const u8 addr[ETH_ALEN], u16 *be)
+{
+	int i;
+	for (i = 0; i < ETH_ALEN; i += 2)
+		be[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i);
+}
+
+
+static u16 wpa_driver_roboswitch_mdio_read(
+	struct wpa_driver_roboswitch_data *drv, u8 reg)
+{
+	struct mii_ioctl_data *mii = if_mii(&drv->ifr);
+
+	mii->phy_id = ROBO_PHY_ADDR;
+	mii->reg_num = reg;
+
+	if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIREG]: %s",
+			   strerror(errno));
+		return 0x00;
+	}
+	return mii->val_out;
+}
+
+
+static void wpa_driver_roboswitch_mdio_write(
+	struct wpa_driver_roboswitch_data *drv, u8 reg, u16 val)
+{
+	struct mii_ioctl_data *mii = if_mii(&drv->ifr);
+
+	mii->phy_id = ROBO_PHY_ADDR;
+	mii->reg_num = reg;
+	mii->val_in = val;
+
+	if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSMIIREG]: %s",
+			   strerror(errno));
+	}
+}
+
+
+static int wpa_driver_roboswitch_reg(struct wpa_driver_roboswitch_data *drv,
+				     u8 page, u8 reg, u8 op)
+{
+	int i;
+
+	/* set page number */
+	wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_PAGE,
+					 (page << 8) | ROBO_MII_PAGE_ENABLE);
+	/* set register address */
+	wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_ADDR, (reg << 8) | op);
+
+	/* check if operation completed */
+	for (i = 0; i < ROBO_MII_RETRY_MAX; ++i) {
+		if ((wpa_driver_roboswitch_mdio_read(drv, ROBO_MII_ADDR) & 3)
+		    == 0)
+			return 0;
+	}
+	/* timeout */
+	return -1;
+}
+
+
+static int wpa_driver_roboswitch_read(struct wpa_driver_roboswitch_data *drv,
+				      u8 page, u8 reg, u16 *val, int len)
+{
+	int i;
+
+	if (len > ROBO_MII_DATA_MAX ||
+	    wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_READ) < 0)
+		return -1;
+
+	for (i = 0; i < len; ++i) {
+		val[i] = wpa_driver_roboswitch_mdio_read(
+			drv, ROBO_MII_DATA_OFFSET + i);
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_roboswitch_write(struct wpa_driver_roboswitch_data *drv,
+				       u8 page, u8 reg, u16 *val, int len)
+{
+	int i;
+
+	if (len > ROBO_MII_DATA_MAX) return -1;
+	for (i = 0; i < len; ++i) {
+		wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_DATA_OFFSET + i,
+						 val[i]);
+	}
+	return wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_WRITE);
+}
+
+
+static void wpa_driver_roboswitch_receive(void *priv, const u8 *src_addr,
+					  const u8 *buf, size_t len)
+{
+	struct wpa_driver_roboswitch_data *drv = priv;
+
+	if (len > 14 && WPA_GET_BE16(buf + 12) == ETH_P_EAPOL &&
+	    os_memcmp(buf, drv->own_addr, ETH_ALEN) == 0)
+		drv_event_eapol_rx(drv->ctx, src_addr, buf + 14, len - 14);
+}
+
+
+static int wpa_driver_roboswitch_get_ssid(void *priv, u8 *ssid)
+{
+	ssid[0] = 0;
+	return 0;
+}
+
+
+static int wpa_driver_roboswitch_get_bssid(void *priv, u8 *bssid)
+{
+	/* Report PAE group address as the "BSSID" for wired connection. */
+	os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+	return 0;
+}
+
+
+static int wpa_driver_roboswitch_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 wpa_driver_roboswitch_set_param(void *priv, const char *param)
+{
+	struct wpa_driver_roboswitch_data *drv = priv;
+	char *sep;
+
+	if (param == NULL || os_strstr(param, "multicast_only=1") == NULL) {
+		sep = drv->ifname + os_strlen(drv->ifname);
+		*sep = '.';
+		drv->l2 = l2_packet_init(drv->ifname, NULL, ETH_P_ALL,
+					 wpa_driver_roboswitch_receive, drv,
+					 1);
+		if (drv->l2 == NULL) {
+			wpa_printf(MSG_INFO, "%s: Unable to listen on %s",
+				   __func__, drv->ifname);
+			return -1;
+		}
+		*sep = '\0';
+		l2_packet_get_own_addr(drv->l2, drv->own_addr);
+	} else {
+		wpa_printf(MSG_DEBUG, "%s: Ignoring unicast frames", __func__);
+		drv->l2 = NULL;
+	}
+	return 0;
+}
+
+
+static const char * wpa_driver_roboswitch_get_ifname(void *priv)
+{
+	struct wpa_driver_roboswitch_data *drv = priv;
+	return drv->ifname;
+}
+
+
+static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv,
+				      u16 ports, const u8 *addr)
+{
+	u16 read1[3], read2[3], addr_be16[3];
+
+	wpa_driver_roboswitch_addr_be16(addr, addr_be16);
+
+	if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+				       ROBO_ARLCTRL_CONF, read1, 1) < 0)
+		return -1;
+	if (!(read1[0] & (1 << 4))) {
+		/* multiport addresses are not yet enabled */
+		read1[0] |= 1 << 4;
+		wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+					    ROBO_ARLCTRL_ADDR_1, addr_be16, 3);
+		wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+					    ROBO_ARLCTRL_VEC_1, &ports, 1);
+		wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+					    ROBO_ARLCTRL_ADDR_2, addr_be16, 3);
+		wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+					    ROBO_ARLCTRL_VEC_2, &ports, 1);
+		wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+					    ROBO_ARLCTRL_CONF, read1, 1);
+	} else {
+		/* if both multiport addresses are the same we can add */
+		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;
+		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);
+		wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+					    ROBO_ARLCTRL_VEC_1, &ports, 1);
+	}
+	return 0;
+}
+
+
+static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv,
+				       u16 ports, const u8 *addr)
+{
+	u16 _read, addr_be16[3], addr_read[3], ports_read;
+
+	wpa_driver_roboswitch_addr_be16(addr, addr_be16);
+
+	wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF,
+				   &_read, 1);
+	/* If ARL control is disabled, there is nothing to leave. */
+	if (!(_read & (1 << 4))) return -1;
+
+	wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+				   ROBO_ARLCTRL_ADDR_1, addr_read, 3);
+	wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1,
+				   &ports_read, 1);
+	/* check if we occupy multiport address 1 */
+	if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) {
+		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					   ROBO_ARLCTRL_ADDR_2, addr_read, 3);
+		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					   ROBO_ARLCTRL_VEC_2, &ports_read, 1);
+		/* and multiport address 2 */
+		if (os_memcmp(addr_read, addr_be16, 6) == 0 &&
+		    ports_read == ports) {
+			_read &= ~(1 << 4);
+			wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+						    ROBO_ARLCTRL_CONF, &_read,
+						    1);
+		} else {
+			wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+						   ROBO_ARLCTRL_ADDR_1,
+						   addr_read, 3);
+			wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+						   ROBO_ARLCTRL_VEC_1,
+						   &ports_read, 1);
+			wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+						    ROBO_ARLCTRL_ADDR_2,
+						    addr_read, 3);
+			wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+						    ROBO_ARLCTRL_VEC_2,
+						    &ports_read, 1);
+		}
+	} else {
+		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					   ROBO_ARLCTRL_ADDR_2, addr_read, 3);
+		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					   ROBO_ARLCTRL_VEC_2, &ports_read, 1);
+		/* or multiport address 2 */
+		if (os_memcmp(addr_read, addr_be16, 6) == 0 &&
+		    ports_read == ports) {
+			wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+						    ROBO_ARLCTRL_ADDR_1,
+						    addr_read, 3);
+			wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+						    ROBO_ARLCTRL_VEC_1,
+						    &ports_read, 1);
+		} else return -1;
+	}
+	return 0;
+}
+
+
+static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname)
+{
+	struct wpa_driver_roboswitch_data *drv;
+	char *sep;
+	u16 vlan = 0, _read[2];
+
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL) return NULL;
+	drv->ctx = ctx;
+	drv->own_addr[0] = '\0';
+
+	/* copy ifname and take a pointer to the second to last character */
+	sep = drv->ifname +
+	      os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2;
+	/* find the '.' separating <interface> and <vlan> */
+	while (sep > drv->ifname && *sep != '.') sep--;
+	if (sep <= drv->ifname) {
+		wpa_printf(MSG_INFO, "%s: No <interface>.<vlan> pair in "
+			   "interface name %s", __func__, drv->ifname);
+		os_free(drv);
+		return NULL;
+	}
+	*sep = '\0';
+	while (*++sep) {
+		if (*sep < '0' || *sep > '9') {
+			wpa_printf(MSG_INFO, "%s: Invalid vlan specification "
+				   "in interface name %s", __func__, ifname);
+			os_free(drv);
+			return NULL;
+		}
+		vlan *= 10;
+		vlan += *sep - '0';
+		if (vlan > ROBO_VLAN_MAX) {
+			wpa_printf(MSG_INFO, "%s: VLAN out of range in "
+				   "interface name %s", __func__, ifname);
+			os_free(drv);
+			return NULL;
+		}
+	}
+
+	drv->fd = socket(PF_INET, SOCK_DGRAM, 0);
+	if (drv->fd < 0) {
+		wpa_printf(MSG_INFO, "%s: Unable to create socket", __func__);
+		os_free(drv);
+		return NULL;
+	}
+
+	os_memset(&drv->ifr, 0, sizeof(drv->ifr));
+	os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ);
+	if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIPHY]: %s",
+			   strerror(errno));
+		os_free(drv);
+		return NULL;
+	}
+	if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR) {
+		wpa_printf(MSG_INFO, "%s: Invalid phy address (not a "
+			   "RoboSwitch?)", __func__);
+		os_free(drv);
+		return NULL;
+	}
+
+	/* set and read back to see if the register can be used */
+	_read[0] = ROBO_VLAN_MAX;
+	wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350,
+				    _read, 1);
+	wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350,
+				   _read + 1, 1);
+	drv->is_5350 = _read[0] == _read[1];
+
+	/* set the read bit */
+	vlan |= 1 << 13;
+	wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE,
+				    drv->is_5350 ? ROBO_VLAN_ACCESS_5350
+						 : ROBO_VLAN_ACCESS,
+				    &vlan, 1);
+	wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, _read,
+				   drv->is_5350 ? 2 : 1);
+	if (!(drv->is_5350 ? _read[1] & (1 << 4) : _read[0] & (1 << 14))) {
+		wpa_printf(MSG_INFO, "%s: Could not get port information for "
+				     "VLAN %d", __func__, vlan & ~(1 << 13));
+		os_free(drv);
+		return NULL;
+	}
+	drv->ports = _read[0] & 0x001F;
+	/* add the MII port */
+	drv->ports |= 1 << 8;
+	if (wpa_driver_roboswitch_join(drv, drv->ports, pae_group_addr) < 0) {
+		wpa_printf(MSG_INFO, "%s: Unable to join PAE group", __func__);
+		os_free(drv);
+		return NULL;
+	} else {
+		wpa_printf(MSG_DEBUG, "%s: Added PAE group address to "
+			   "RoboSwitch ARL", __func__);
+	}
+
+	return drv;
+}
+
+
+static void wpa_driver_roboswitch_deinit(void *priv)
+{
+	struct wpa_driver_roboswitch_data *drv = priv;
+
+	if (drv->l2) {
+		l2_packet_deinit(drv->l2);
+		drv->l2 = NULL;
+	}
+	if (wpa_driver_roboswitch_leave(drv, drv->ports, pae_group_addr) < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Unable to leave PAE group",
+			   __func__);
+	}
+
+	close(drv->fd);
+	os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_roboswitch_ops = {
+	.name = "roboswitch",
+	.desc = "wpa_supplicant roboswitch driver",
+	.get_ssid = wpa_driver_roboswitch_get_ssid,
+	.get_bssid = wpa_driver_roboswitch_get_bssid,
+	.get_capa = wpa_driver_roboswitch_get_capa,
+	.init = wpa_driver_roboswitch_init,
+	.deinit = wpa_driver_roboswitch_deinit,
+	.set_param = wpa_driver_roboswitch_set_param,
+	.get_ifname = wpa_driver_roboswitch_get_ifname,
+};
diff --git a/hostap/src/drivers/driver_wext.c b/hostap/src/drivers/driver_wext.c
new file mode 100644
index 0000000..01defdf
--- /dev/null
+++ b/hostap/src/drivers/driver_wext.c
@@ -0,0 +1,2511 @@
+/*
+ * Driver interaction with generic Linux Wireless Extensions
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements a driver interface for the Linux Wireless Extensions.
+ * When used with WE-18 or newer, this interface can be used as-is with number
+ * of drivers. In addition to this, some of the common functions in this file
+ * can be used by other driver interface implementations that use generic WE
+ * ioctls, but require private ioctls for some of the functionality.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+#include <dirent.h>
+
+#include "linux_wext.h"
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
+#include "priv_netlink.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "rfkill.h"
+#include "driver.h"
+#include "driver_wext.h"
+
+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);
+static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv);
+static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg);
+
+
+int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv,
+				   int idx, u32 value)
+{
+	struct iwreq iwr;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.param.flags = idx & IW_AUTH_INDEX;
+	iwr.u.param.value = value;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) {
+		if (errno != EOPNOTSUPP) {
+			wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d "
+				   "value 0x%x) failed: %s)",
+				   idx, value, strerror(errno));
+		}
+		ret = errno == EOPNOTSUPP ? -2 : -1;
+	}
+
+	return ret;
+}
+
+
+/**
+ * wpa_driver_wext_get_bssid - Get BSSID, SIOCGIWAP
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @bssid: Buffer for BSSID
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_get_bssid(void *priv, u8 *bssid)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+	if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWAP]: %s", strerror(errno));
+		ret = -1;
+	}
+	os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN);
+
+	return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_bssid - Set BSSID, SIOCSIWAP
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @bssid: BSSID
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.ap_addr.sa_family = ARPHRD_ETHER;
+	if (bssid)
+		os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN);
+	else
+		os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN);
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWAP]: %s", strerror(errno));
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+/**
+ * wpa_driver_wext_get_ssid - Get SSID, SIOCGIWESSID
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @ssid: Buffer for the SSID; must be at least 32 bytes long
+ * Returns: SSID length on success, -1 on failure
+ */
+int wpa_driver_wext_get_ssid(void *priv, u8 *ssid)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.essid.pointer = (caddr_t) ssid;
+	iwr.u.essid.length = SSID_MAX_LEN;
+
+	if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s",
+			   strerror(errno));
+		ret = -1;
+	} else {
+		ret = iwr.u.essid.length;
+		if (ret > SSID_MAX_LEN)
+			ret = SSID_MAX_LEN;
+		/* Some drivers include nul termination in the SSID, so let's
+		 * remove it here before further processing. WE-21 changes this
+		 * to explicitly require the length _not_ to include nul
+		 * termination. */
+		if (ret > 0 && ssid[ret - 1] == '\0' &&
+		    drv->we_version_compiled < 21)
+			ret--;
+	}
+
+	return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_ssid - Set SSID, SIOCSIWESSID
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @ssid: SSID
+ * @ssid_len: Length of SSID (0..32)
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+	char buf[33];
+
+	if (ssid_len > SSID_MAX_LEN)
+		return -1;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	/* flags: 1 = ESSID is active, 0 = not (promiscuous) */
+	iwr.u.essid.flags = (ssid_len != 0);
+	os_memset(buf, 0, sizeof(buf));
+	os_memcpy(buf, ssid, ssid_len);
+	iwr.u.essid.pointer = (caddr_t) buf;
+	if (drv->we_version_compiled < 21) {
+		/* For historic reasons, set SSID length to include one extra
+		 * character, C string nul termination, even though SSID is
+		 * really an octet string that should not be presented as a C
+		 * string. Some Linux drivers decrement the length by one and
+		 * can thus end up missing the last octet of the SSID if the
+		 * length is not incremented here. WE-21 changes this to
+		 * explicitly require the length _not_ to include nul
+		 * termination. */
+		if (ssid_len)
+			ssid_len++;
+	}
+	iwr.u.essid.length = ssid_len;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID]: %s",
+			   strerror(errno));
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_freq - Set frequency/channel, SIOCSIWFREQ
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @freq: Frequency in MHz
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_freq(void *priv, int freq)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.freq.m = freq * 100000;
+	iwr.u.freq.e = 1;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s",
+			   strerror(errno));
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+static void
+wpa_driver_wext_event_wireless_custom(void *ctx, char *custom)
+{
+	union wpa_event_data data;
+
+	wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'",
+		   custom);
+
+	os_memset(&data, 0, sizeof(data));
+	/* Host AP driver */
+	if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+		data.michael_mic_failure.unicast =
+			os_strstr(custom, " unicast ") != NULL;
+		/* TODO: parse parameters(?) */
+		wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+	} else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) {
+		char *spos;
+		int bytes;
+		u8 *req_ies = NULL, *resp_ies = NULL;
+
+		spos = custom + 17;
+
+		bytes = strspn(spos, "0123456789abcdefABCDEF");
+		if (!bytes || (bytes & 1))
+			return;
+		bytes /= 2;
+
+		req_ies = os_malloc(bytes);
+		if (req_ies == NULL ||
+		    hexstr2bin(spos, req_ies, bytes) < 0)
+			goto done;
+		data.assoc_info.req_ies = req_ies;
+		data.assoc_info.req_ies_len = bytes;
+
+		spos += bytes * 2;
+
+		data.assoc_info.resp_ies = NULL;
+		data.assoc_info.resp_ies_len = 0;
+
+		if (os_strncmp(spos, " RespIEs=", 9) == 0) {
+			spos += 9;
+
+			bytes = strspn(spos, "0123456789abcdefABCDEF");
+			if (!bytes || (bytes & 1))
+				goto done;
+			bytes /= 2;
+
+			resp_ies = os_malloc(bytes);
+			if (resp_ies == NULL ||
+			    hexstr2bin(spos, resp_ies, bytes) < 0)
+				goto done;
+			data.assoc_info.resp_ies = resp_ies;
+			data.assoc_info.resp_ies_len = bytes;
+		}
+
+		wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data);
+
+	done:
+		os_free(resp_ies);
+		os_free(req_ies);
+#ifdef CONFIG_PEERKEY
+	} else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) {
+		if (hwaddr_aton(custom + 17, data.stkstart.peer)) {
+			wpa_printf(MSG_DEBUG, "WEXT: unrecognized "
+				   "STKSTART.request '%s'", custom + 17);
+			return;
+		}
+		wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
+#endif /* CONFIG_PEERKEY */
+	}
+}
+
+
+static int wpa_driver_wext_event_wireless_michaelmicfailure(
+	void *ctx, const char *ev, size_t len)
+{
+	const struct iw_michaelmicfailure *mic;
+	union wpa_event_data data;
+
+	if (len < sizeof(*mic))
+		return -1;
+
+	mic = (const struct iw_michaelmicfailure *) ev;
+
+	wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: "
+		   "flags=0x%x src_addr=" MACSTR, mic->flags,
+		   MAC2STR(mic->src_addr.sa_data));
+
+	os_memset(&data, 0, sizeof(data));
+	data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP);
+	wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+
+	return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_pmkidcand(
+	struct wpa_driver_wext_data *drv, const char *ev, size_t len)
+{
+	const struct iw_pmkid_cand *cand;
+	union wpa_event_data data;
+	const u8 *addr;
+
+	if (len < sizeof(*cand))
+		return -1;
+
+	cand = (const struct iw_pmkid_cand *) ev;
+	addr = (const u8 *) cand->bssid.sa_data;
+
+	wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: "
+		   "flags=0x%x index=%d bssid=" MACSTR, cand->flags,
+		   cand->index, MAC2STR(addr));
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN);
+	data.pmkid_candidate.index = cand->index;
+	data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH;
+	wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data);
+
+	return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_assocreqie(
+	struct wpa_driver_wext_data *drv, const char *ev, int len)
+{
+	if (len < 0)
+		return -1;
+
+	wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev,
+		    len);
+	os_free(drv->assoc_req_ies);
+	drv->assoc_req_ies = os_malloc(len);
+	if (drv->assoc_req_ies == NULL) {
+		drv->assoc_req_ies_len = 0;
+		return -1;
+	}
+	os_memcpy(drv->assoc_req_ies, ev, len);
+	drv->assoc_req_ies_len = len;
+
+	return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_assocrespie(
+	struct wpa_driver_wext_data *drv, const char *ev, int len)
+{
+	if (len < 0)
+		return -1;
+
+	wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev,
+		    len);
+	os_free(drv->assoc_resp_ies);
+	drv->assoc_resp_ies = os_malloc(len);
+	if (drv->assoc_resp_ies == NULL) {
+		drv->assoc_resp_ies_len = 0;
+		return -1;
+	}
+	os_memcpy(drv->assoc_resp_ies, ev, len);
+	drv->assoc_resp_ies_len = len;
+
+	return 0;
+}
+
+
+static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv)
+{
+	union wpa_event_data data;
+
+	if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	if (drv->assoc_req_ies) {
+		data.assoc_info.req_ies = drv->assoc_req_ies;
+		data.assoc_info.req_ies_len = drv->assoc_req_ies_len;
+	}
+	if (drv->assoc_resp_ies) {
+		data.assoc_info.resp_ies = drv->assoc_resp_ies;
+		data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len;
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
+
+	os_free(drv->assoc_req_ies);
+	drv->assoc_req_ies = NULL;
+	os_free(drv->assoc_resp_ies);
+	drv->assoc_resp_ies = NULL;
+}
+
+
+static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
+					   char *data, int len)
+{
+	struct iw_event iwe_buf, *iwe = &iwe_buf;
+	char *pos, *end, *custom, *buf;
+
+	pos = data;
+	end = data + len;
+
+	while (pos + IW_EV_LCP_LEN <= end) {
+		/* Event data may be unaligned, so make a local, aligned copy
+		 * before processing. */
+		os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+		wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
+			   iwe->cmd, iwe->len);
+		if (iwe->len <= IW_EV_LCP_LEN)
+			return;
+
+		custom = pos + IW_EV_POINT_LEN;
+		if (drv->we_version_compiled > 18 &&
+		    (iwe->cmd == IWEVMICHAELMICFAILURE ||
+		     iwe->cmd == IWEVCUSTOM ||
+		     iwe->cmd == IWEVASSOCREQIE ||
+		     iwe->cmd == IWEVASSOCRESPIE ||
+		     iwe->cmd == IWEVPMKIDCAND)) {
+			/* WE-19 removed the pointer from struct iw_point */
+			char *dpos = (char *) &iwe_buf.u.data.length;
+			int dlen = dpos - (char *) &iwe_buf;
+			os_memcpy(dpos, pos + IW_EV_LCP_LEN,
+				  sizeof(struct iw_event) - dlen);
+		} else {
+			os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+			custom += IW_EV_POINT_OFF;
+		}
+
+		switch (iwe->cmd) {
+		case SIOCGIWAP:
+			wpa_printf(MSG_DEBUG, "Wireless event: new AP: "
+				   MACSTR,
+				   MAC2STR((u8 *) iwe->u.ap_addr.sa_data));
+			if (is_zero_ether_addr(
+				    (const u8 *) iwe->u.ap_addr.sa_data) ||
+			    os_memcmp(iwe->u.ap_addr.sa_data,
+				      "\x44\x44\x44\x44\x44\x44", ETH_ALEN) ==
+			    0) {
+				os_free(drv->assoc_req_ies);
+				drv->assoc_req_ies = NULL;
+				os_free(drv->assoc_resp_ies);
+				drv->assoc_resp_ies = NULL;
+				wpa_supplicant_event(drv->ctx, EVENT_DISASSOC,
+						     NULL);
+			
+			} else {
+				wpa_driver_wext_event_assoc_ies(drv);
+				wpa_supplicant_event(drv->ctx, EVENT_ASSOC,
+						     NULL);
+			}
+			break;
+		case IWEVMICHAELMICFAILURE:
+			if (custom + iwe->u.data.length > end) {
+				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+					   "IWEVMICHAELMICFAILURE length");
+				return;
+			}
+			wpa_driver_wext_event_wireless_michaelmicfailure(
+				drv->ctx, custom, iwe->u.data.length);
+			break;
+		case IWEVCUSTOM:
+			if (custom + iwe->u.data.length > end) {
+				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+					   "IWEVCUSTOM length");
+				return;
+			}
+			buf = dup_binstr(custom, iwe->u.data.length);
+			if (buf == NULL)
+				return;
+			wpa_driver_wext_event_wireless_custom(drv->ctx, buf);
+			os_free(buf);
+			break;
+		case SIOCGIWSCAN:
+			drv->scan_complete_events = 1;
+			eloop_cancel_timeout(wpa_driver_wext_scan_timeout,
+					     drv, drv->ctx);
+			wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS,
+					     NULL);
+			break;
+		case IWEVASSOCREQIE:
+			if (custom + iwe->u.data.length > end) {
+				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+					   "IWEVASSOCREQIE length");
+				return;
+			}
+			wpa_driver_wext_event_wireless_assocreqie(
+				drv, custom, iwe->u.data.length);
+			break;
+		case IWEVASSOCRESPIE:
+			if (custom + iwe->u.data.length > end) {
+				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+					   "IWEVASSOCRESPIE length");
+				return;
+			}
+			wpa_driver_wext_event_wireless_assocrespie(
+				drv, custom, iwe->u.data.length);
+			break;
+		case IWEVPMKIDCAND:
+			if (custom + iwe->u.data.length > end) {
+				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+					   "IWEVPMKIDCAND length");
+				return;
+			}
+			wpa_driver_wext_event_wireless_pmkidcand(
+				drv, custom, iwe->u.data.length);
+			break;
+		}
+
+		pos += iwe->len;
+	}
+}
+
+
+static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv,
+				       char *buf, size_t len, int del)
+{
+	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->ifname, event.interface_status.ifname) == 0) {
+		if (del) {
+			if (drv->if_removed) {
+				wpa_printf(MSG_DEBUG, "WEXT: if_removed "
+					   "already set - ignore event");
+				return;
+			}
+			drv->if_removed = 1;
+		} else {
+			if (if_nametoindex(drv->ifname) == 0) {
+				wpa_printf(MSG_DEBUG, "WEXT: Interface %s "
+					   "does not exist - ignore "
+					   "RTM_NEWLINK",
+					   drv->ifname);
+				return;
+			}
+			if (!drv->if_removed) {
+				wpa_printf(MSG_DEBUG, "WEXT: if_removed "
+					   "already cleared - ignore event");
+				return;
+			}
+			drv->if_removed = 0;
+		}
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv,
+				      u8 *buf, size_t len)
+{
+	int attrlen, rta_len;
+	struct rtattr *attr;
+
+	attrlen = len;
+	attr = (struct rtattr *) buf;
+
+	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->ifname)
+			    == 0)
+				return 1;
+			else
+				break;
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv,
+				       int ifindex, u8 *buf, size_t len)
+{
+	if (drv->ifindex == ifindex || drv->ifindex2 == ifindex)
+		return 1;
+
+	if (drv->if_removed && wpa_driver_wext_own_ifname(drv, buf, len)) {
+		drv->ifindex = if_nametoindex(drv->ifname);
+		wpa_printf(MSG_DEBUG, "WEXT: Update ifindex for a removed "
+			   "interface");
+		wpa_driver_wext_finish_drv_init(drv);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi,
+					      u8 *buf, size_t len)
+{
+	struct wpa_driver_wext_data *drv = ctx;
+	int attrlen, rta_len;
+	struct rtattr *attr;
+	char namebuf[IFNAMSIZ];
+
+	if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) {
+		wpa_printf(MSG_DEBUG, "Ignore 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,
+		   (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+		   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+	if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+		wpa_printf(MSG_DEBUG, "WEXT: Interface down");
+		drv->if_disabled = 1;
+		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
+	}
+
+	if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+		if (if_indextoname(ifi->ifi_index, namebuf) &&
+		    linux_iface_up(drv->ioctl_sock, drv->ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+				   "event since interface %s is down",
+				   namebuf);
+		} else if (if_nametoindex(drv->ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+				   "event since interface %s does not exist",
+				   drv->ifname);
+		} else if (drv->if_removed) {
+			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+				   "event since interface %s is marked "
+				   "removed", drv->ifname);
+		} else {
+			wpa_printf(MSG_DEBUG, "WEXT: Interface up");
+			drv->if_disabled = 0;
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+					     NULL);
+		}
+	}
+
+	/*
+	 * Some drivers send the association event before the operup event--in
+	 * this case, lifting operstate in wpa_driver_wext_set_operstate()
+	 * fails. This will hit us when wpa_supplicant does not need to do
+	 * IEEE 802.1X authentication
+	 */
+	if (drv->operstate == 1 &&
+	    (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
+	    !(ifi->ifi_flags & IFF_RUNNING))
+		netlink_send_oper_ifla(drv->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_WIRELESS) {
+			wpa_driver_wext_event_wireless(
+				drv, ((char *) attr) + rta_len,
+				attr->rta_len - rta_len);
+		} else if (attr->rta_type == IFLA_IFNAME) {
+			wpa_driver_wext_event_link(drv,
+						   ((char *) attr) + rta_len,
+						   attr->rta_len - rta_len, 0);
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+}
+
+
+static void wpa_driver_wext_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi,
+					      u8 *buf, size_t len)
+{
+	struct wpa_driver_wext_data *drv = ctx;
+	int attrlen, rta_len;
+	struct rtattr *attr;
+
+	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_wext_event_link(drv,
+						   ((char *) attr) + rta_len,
+						   attr->rta_len - rta_len, 1);
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+}
+
+
+static void wpa_driver_wext_rfkill_blocked(void *ctx)
+{
+	wpa_printf(MSG_DEBUG, "WEXT: RFKILL blocked");
+	/*
+	 * This may be for any interface; use ifdown event to disable
+	 * interface.
+	 */
+}
+
+
+static void wpa_driver_wext_rfkill_unblocked(void *ctx)
+{
+	struct wpa_driver_wext_data *drv = ctx;
+	wpa_printf(MSG_DEBUG, "WEXT: RFKILL unblocked");
+	if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) {
+		wpa_printf(MSG_DEBUG, "WEXT: Could not set interface UP "
+			   "after rfkill unblock");
+		return;
+	}
+	/* rtnetlink ifup handler will report interface as enabled */
+}
+
+
+static void wext_get_phy_name(struct wpa_driver_wext_data *drv)
+{
+	/* Find phy (radio) to which this interface belongs */
+	char buf[90], *pos;
+	int f, rv;
+
+	drv->phyname[0] = '\0';
+	snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name",
+		 drv->ifname);
+	f = open(buf, O_RDONLY);
+	if (f < 0) {
+		wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
+			   buf, strerror(errno));
+		return;
+	}
+
+	rv = read(f, drv->phyname, sizeof(drv->phyname) - 1);
+	close(f);
+	if (rv < 0) {
+		wpa_printf(MSG_DEBUG, "Could not read file %s: %s",
+			   buf, strerror(errno));
+		return;
+	}
+
+	drv->phyname[rv] = '\0';
+	pos = os_strchr(drv->phyname, '\n');
+	if (pos)
+		*pos = '\0';
+	wpa_printf(MSG_DEBUG, "wext: interface %s phy: %s",
+		   drv->ifname, drv->phyname);
+}
+
+
+/**
+ * wpa_driver_wext_init - Initialize WE driver interface
+ * @ctx: context to be used when calling wpa_supplicant functions,
+ * e.g., wpa_supplicant_event()
+ * @ifname: interface name, e.g., wlan0
+ * Returns: Pointer to private data, %NULL on failure
+ */
+void * wpa_driver_wext_init(void *ctx, const char *ifname)
+{
+	struct wpa_driver_wext_data *drv;
+	struct netlink_config *cfg;
+	struct rfkill_config *rcfg;
+	char path[128];
+	struct stat buf;
+
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL)
+		return NULL;
+	drv->ctx = ctx;
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+	os_snprintf(path, sizeof(path), "/sys/class/net/%s/phy80211", ifname);
+	if (stat(path, &buf) == 0) {
+		wpa_printf(MSG_DEBUG, "WEXT: cfg80211-based driver detected");
+		drv->cfg80211 = 1;
+		wext_get_phy_name(drv);
+	}
+
+	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (drv->ioctl_sock < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_INET,SOCK_DGRAM): %s",
+			   strerror(errno));
+		goto err1;
+	}
+
+	cfg = os_zalloc(sizeof(*cfg));
+	if (cfg == NULL)
+		goto err1;
+	cfg->ctx = drv;
+	cfg->newlink_cb = wpa_driver_wext_event_rtm_newlink;
+	cfg->dellink_cb = wpa_driver_wext_event_rtm_dellink;
+	drv->netlink = netlink_init(cfg);
+	if (drv->netlink == NULL) {
+		os_free(cfg);
+		goto err2;
+	}
+
+	rcfg = os_zalloc(sizeof(*rcfg));
+	if (rcfg == NULL)
+		goto err3;
+	rcfg->ctx = drv;
+	os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+	rcfg->blocked_cb = wpa_driver_wext_rfkill_blocked;
+	rcfg->unblocked_cb = wpa_driver_wext_rfkill_unblocked;
+	drv->rfkill = rfkill_init(rcfg);
+	if (drv->rfkill == NULL) {
+		wpa_printf(MSG_DEBUG, "WEXT: RFKILL status not available");
+		os_free(rcfg);
+	}
+
+	drv->mlme_sock = -1;
+
+	if (wpa_driver_wext_finish_drv_init(drv) < 0)
+		goto err3;
+
+	wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 1);
+
+	return drv;
+
+err3:
+	rfkill_deinit(drv->rfkill);
+	netlink_deinit(drv->netlink);
+err2:
+	close(drv->ioctl_sock);
+err1:
+	os_free(drv);
+	return NULL;
+}
+
+
+static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+	wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
+static int wext_hostap_ifname(struct wpa_driver_wext_data *drv,
+			      const char *ifname)
+{
+	char buf[200], *res;
+	int type;
+	FILE *f;
+
+	if (strcmp(ifname, ".") == 0 || strcmp(ifname, "..") == 0)
+		return -1;
+
+	snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net/%s/type",
+		 drv->ifname, ifname);
+
+	f = fopen(buf, "r");
+	if (!f)
+		return -1;
+	res = fgets(buf, sizeof(buf), f);
+	fclose(f);
+
+	type = res ? atoi(res) : -1;
+	wpa_printf(MSG_DEBUG, "WEXT: hostap ifname %s type %d", ifname, type);
+
+	if (type == ARPHRD_IEEE80211) {
+		wpa_printf(MSG_DEBUG,
+			   "WEXT: Found hostap driver wifi# interface (%s)",
+			   ifname);
+		wpa_driver_wext_alternative_ifindex(drv, ifname);
+		return 0;
+	}
+	return -1;
+}
+
+
+static int wext_add_hostap(struct wpa_driver_wext_data *drv)
+{
+	char buf[200];
+	int n;
+	struct dirent **names;
+	int ret = -1;
+
+	snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net", drv->ifname);
+	n = scandir(buf, &names, NULL, alphasort);
+	if (n < 0)
+		return -1;
+
+	while (n--) {
+		if (ret < 0 && wext_hostap_ifname(drv, names[n]->d_name) == 0)
+			ret = 0;
+		free(names[n]);
+	}
+	free(names);
+
+	return ret;
+}
+
+
+static void wext_check_hostap(struct wpa_driver_wext_data *drv)
+{
+	char buf[200], *pos;
+	ssize_t res;
+
+	/*
+	 * Host AP driver may use both wlan# and wifi# interface in wireless
+	 * events. Since some of the versions included WE-18 support, let's add
+	 * the alternative ifindex also from driver_wext.c for the time being.
+	 * This may be removed at some point once it is believed that old
+	 * versions of the driver are not in use anymore. However, it looks like
+	 * the wifi# interface is still used in the current kernel tree, so it
+	 * may not really be possible to remove this before the Host AP driver
+	 * gets removed from the kernel.
+	 */
+
+	/* First, try to see if driver information is available from sysfs */
+	snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/driver",
+		 drv->ifname);
+	res = readlink(buf, buf, sizeof(buf) - 1);
+	if (res > 0) {
+		buf[res] = '\0';
+		pos = strrchr(buf, '/');
+		if (pos)
+			pos++;
+		else
+			pos = buf;
+		wpa_printf(MSG_DEBUG, "WEXT: Driver: %s", pos);
+		if (os_strncmp(pos, "hostap", 6) == 0 &&
+		    wext_add_hostap(drv) == 0)
+			return;
+	}
+
+	/* Second, use the old design with hardcoded ifname */
+	if (os_strncmp(drv->ifname, "wlan", 4) == 0) {
+		char ifname2[IFNAMSIZ + 1];
+		os_strlcpy(ifname2, drv->ifname, sizeof(ifname2));
+		os_memcpy(ifname2, "wifi", 4);
+		wpa_driver_wext_alternative_ifindex(drv, ifname2);
+	}
+}
+
+
+static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
+{
+	int send_rfkill_event = 0;
+
+	if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) {
+		if (rfkill_is_blocked(drv->rfkill)) {
+			wpa_printf(MSG_DEBUG, "WEXT: Could not yet enable "
+				   "interface '%s' due to rfkill",
+				   drv->ifname);
+			drv->if_disabled = 1;
+			send_rfkill_event = 1;
+		} else {
+			wpa_printf(MSG_ERROR, "WEXT: Could not set "
+				   "interface '%s' UP", drv->ifname);
+			return -1;
+		}
+	}
+
+	/*
+	 * Make sure that the driver does not have any obsolete PMKID entries.
+	 */
+	wpa_driver_wext_flush_pmkid(drv);
+
+	if (wpa_driver_wext_set_mode(drv, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "Could not configure driver to use "
+			   "managed mode");
+		/* Try to use it anyway */
+	}
+
+	wpa_driver_wext_get_range(drv);
+
+	/*
+	 * Unlock the driver's BSSID and force to a random SSID to clear any
+	 * previous association the driver might have when the supplicant
+	 * starts up.
+	 */
+	wpa_driver_wext_disconnect(drv);
+
+	drv->ifindex = if_nametoindex(drv->ifname);
+
+	wext_check_hostap(drv);
+
+	netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+			       1, IF_OPER_DORMANT);
+
+	if (send_rfkill_event) {
+		eloop_register_timeout(0, 0, wpa_driver_wext_send_rfkill,
+				       drv, drv->ctx);
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_driver_wext_deinit - Deinitialize WE driver interface
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ *
+ * Shut down driver interface and processing of driver events. Free
+ * private data buffer if one was allocated in wpa_driver_wext_init().
+ */
+void wpa_driver_wext_deinit(void *priv)
+{
+	struct wpa_driver_wext_data *drv = priv;
+
+	wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 0);
+
+	eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
+
+	/*
+	 * Clear possibly configured driver parameters in order to make it
+	 * easier to use the driver after wpa_supplicant has been terminated.
+	 */
+	wpa_driver_wext_disconnect(drv);
+
+	netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
+	netlink_deinit(drv->netlink);
+	rfkill_deinit(drv->rfkill);
+
+	if (drv->mlme_sock >= 0)
+		eloop_unregister_read_sock(drv->mlme_sock);
+
+	(void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0);
+
+	close(drv->ioctl_sock);
+	if (drv->mlme_sock >= 0)
+		close(drv->mlme_sock);
+	os_free(drv->assoc_req_ies);
+	os_free(drv->assoc_resp_ies);
+	os_free(drv);
+}
+
+
+/**
+ * wpa_driver_wext_scan_timeout - Scan timeout to report scan completion
+ * @eloop_ctx: Unused
+ * @timeout_ctx: ctx argument given to wpa_driver_wext_init()
+ *
+ * This function can be used as registered timeout when starting a scan to
+ * generate a scan completed event if the driver does not report this.
+ */
+void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+/**
+ * wpa_driver_wext_scan - Request the driver to initiate scan
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @param: Scan parameters (specific SSID to scan for (ProbeReq), etc.)
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0, timeout;
+	struct iw_scan_req req;
+	const u8 *ssid = params->ssids[0].ssid;
+	size_t ssid_len = params->ssids[0].ssid_len;
+
+	if (ssid_len > IW_ESSID_MAX_SIZE) {
+		wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)",
+			   __FUNCTION__, (unsigned long) ssid_len);
+		return -1;
+	}
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+	if (ssid && ssid_len) {
+		os_memset(&req, 0, sizeof(req));
+		req.essid_len = ssid_len;
+		req.bssid.sa_family = ARPHRD_ETHER;
+		os_memset(req.bssid.sa_data, 0xff, ETH_ALEN);
+		os_memcpy(req.essid, ssid, ssid_len);
+		iwr.u.data.pointer = (caddr_t) &req;
+		iwr.u.data.length = sizeof(req);
+		iwr.u.data.flags = IW_SCAN_THIS_ESSID;
+	}
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWSCAN]: %s",
+			   strerror(errno));
+		ret = -1;
+	}
+
+	/* Not all drivers generate "scan completed" wireless event, so try to
+	 * read results after a timeout. */
+	timeout = 10;
+	if (drv->scan_complete_events) {
+		/*
+		 * The driver seems to deliver SIOCGIWSCAN events to notify
+		 * when scan is complete, so use longer timeout to avoid race
+		 * conditions with scanning and following association request.
+		 */
+		timeout = 30;
+	}
+	wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
+		   "seconds", ret, timeout);
+	eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
+	eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv,
+			       drv->ctx);
+
+	return ret;
+}
+
+
+static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv,
+				    size_t *len)
+{
+	struct iwreq iwr;
+	u8 *res_buf;
+	size_t res_buf_len;
+
+	res_buf_len = IW_SCAN_MAX_DATA;
+	for (;;) {
+		res_buf = os_malloc(res_buf_len);
+		if (res_buf == NULL)
+			return NULL;
+		os_memset(&iwr, 0, sizeof(iwr));
+		os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+		iwr.u.data.pointer = res_buf;
+		iwr.u.data.length = res_buf_len;
+
+		if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0)
+			break;
+
+		if (errno == E2BIG && res_buf_len < 65535) {
+			os_free(res_buf);
+			res_buf = NULL;
+			res_buf_len *= 2;
+			if (res_buf_len > 65535)
+				res_buf_len = 65535; /* 16-bit length field */
+			wpa_printf(MSG_DEBUG, "Scan results did not fit - "
+				   "trying larger buffer (%lu bytes)",
+				   (unsigned long) res_buf_len);
+		} else {
+			wpa_printf(MSG_ERROR, "ioctl[SIOCGIWSCAN]: %s",
+				   strerror(errno));
+			os_free(res_buf);
+			return NULL;
+		}
+	}
+
+	if (iwr.u.data.length > res_buf_len) {
+		os_free(res_buf);
+		return NULL;
+	}
+	*len = iwr.u.data.length;
+
+	return res_buf;
+}
+
+
+/*
+ * Data structure for collecting WEXT scan results. This is needed to allow
+ * the various methods of reporting IEs to be combined into a single IE buffer.
+ */
+struct wext_scan_data {
+	struct wpa_scan_res res;
+	u8 *ie;
+	size_t ie_len;
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	int maxrate;
+};
+
+
+static void wext_get_scan_mode(struct iw_event *iwe,
+			       struct wext_scan_data *res)
+{
+	if (iwe->u.mode == IW_MODE_ADHOC)
+		res->res.caps |= IEEE80211_CAP_IBSS;
+	else if (iwe->u.mode == IW_MODE_MASTER || iwe->u.mode == IW_MODE_INFRA)
+		res->res.caps |= IEEE80211_CAP_ESS;
+}
+
+
+static void wext_get_scan_ssid(struct iw_event *iwe,
+			       struct wext_scan_data *res, char *custom,
+			       char *end)
+{
+	int ssid_len = iwe->u.essid.length;
+	if (custom + ssid_len > end)
+		return;
+	if (iwe->u.essid.flags &&
+	    ssid_len > 0 &&
+	    ssid_len <= IW_ESSID_MAX_SIZE) {
+		os_memcpy(res->ssid, custom, ssid_len);
+		res->ssid_len = ssid_len;
+	}
+}
+
+
+static void wext_get_scan_freq(struct iw_event *iwe,
+			       struct wext_scan_data *res)
+{
+	int divi = 1000000, i;
+
+	if (iwe->u.freq.e == 0) {
+		/*
+		 * Some drivers do not report frequency, but a channel.
+		 * Try to map this to frequency by assuming they are using
+		 * IEEE 802.11b/g.  But don't overwrite a previously parsed
+		 * frequency if the driver sends both frequency and channel,
+		 * since the driver may be sending an A-band channel that we
+		 * don't handle here.
+		 */
+
+		if (res->res.freq)
+			return;
+
+		if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) {
+			res->res.freq = 2407 + 5 * iwe->u.freq.m;
+			return;
+		} else if (iwe->u.freq.m == 14) {
+			res->res.freq = 2484;
+			return;
+		}
+	}
+
+	if (iwe->u.freq.e > 6) {
+		wpa_printf(MSG_DEBUG, "Invalid freq in scan results (BSSID="
+			   MACSTR " m=%d e=%d)",
+			   MAC2STR(res->res.bssid), iwe->u.freq.m,
+			   iwe->u.freq.e);
+		return;
+	}
+
+	for (i = 0; i < iwe->u.freq.e; i++)
+		divi /= 10;
+	res->res.freq = iwe->u.freq.m / divi;
+}
+
+
+static void wext_get_scan_qual(struct wpa_driver_wext_data *drv,
+			       struct iw_event *iwe,
+			       struct wext_scan_data *res)
+{
+	res->res.qual = iwe->u.qual.qual;
+	res->res.noise = iwe->u.qual.noise;
+	res->res.level = iwe->u.qual.level;
+	if (iwe->u.qual.updated & IW_QUAL_QUAL_INVALID)
+		res->res.flags |= WPA_SCAN_QUAL_INVALID;
+	if (iwe->u.qual.updated & IW_QUAL_LEVEL_INVALID)
+		res->res.flags |= WPA_SCAN_LEVEL_INVALID;
+	if (iwe->u.qual.updated & IW_QUAL_NOISE_INVALID)
+		res->res.flags |= WPA_SCAN_NOISE_INVALID;
+	if (iwe->u.qual.updated & IW_QUAL_DBM)
+		res->res.flags |= WPA_SCAN_LEVEL_DBM;
+	if ((iwe->u.qual.updated & IW_QUAL_DBM) ||
+	    ((iwe->u.qual.level != 0) &&
+	     (iwe->u.qual.level > drv->max_level))) {
+		if (iwe->u.qual.level >= 64)
+			res->res.level -= 0x100;
+		if (iwe->u.qual.noise >= 64)
+			res->res.noise -= 0x100;
+	}
+}
+
+
+static void wext_get_scan_encode(struct iw_event *iwe,
+				 struct wext_scan_data *res)
+{
+	if (!(iwe->u.data.flags & IW_ENCODE_DISABLED))
+		res->res.caps |= IEEE80211_CAP_PRIVACY;
+}
+
+
+static void wext_get_scan_rate(struct iw_event *iwe,
+			       struct wext_scan_data *res, char *pos,
+			       char *end)
+{
+	int maxrate;
+	char *custom = pos + IW_EV_LCP_LEN;
+	struct iw_param p;
+	size_t clen;
+
+	clen = iwe->len;
+	if (custom + clen > end)
+		return;
+	maxrate = 0;
+	while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) {
+		/* Note: may be misaligned, make a local, aligned copy */
+		os_memcpy(&p, custom, sizeof(struct iw_param));
+		if (p.value > maxrate)
+			maxrate = p.value;
+		clen -= sizeof(struct iw_param);
+		custom += sizeof(struct iw_param);
+	}
+
+	/* Convert the maxrate from WE-style (b/s units) to
+	 * 802.11 rates (500000 b/s units).
+	 */
+	res->maxrate = maxrate / 500000;
+}
+
+
+static void wext_get_scan_iwevgenie(struct iw_event *iwe,
+				    struct wext_scan_data *res, char *custom,
+				    char *end)
+{
+	char *genie, *gpos, *gend;
+	u8 *tmp;
+
+	if (iwe->u.data.length == 0)
+		return;
+
+	gpos = genie = custom;
+	gend = genie + iwe->u.data.length;
+	if (gend > end) {
+		wpa_printf(MSG_INFO, "IWEVGENIE overflow");
+		return;
+	}
+
+	tmp = os_realloc(res->ie, res->ie_len + gend - gpos);
+	if (tmp == NULL)
+		return;
+	os_memcpy(tmp + res->ie_len, gpos, gend - gpos);
+	res->ie = tmp;
+	res->ie_len += gend - gpos;
+}
+
+
+static void wext_get_scan_custom(struct iw_event *iwe,
+				 struct wext_scan_data *res, char *custom,
+				 char *end)
+{
+	size_t clen;
+	u8 *tmp;
+
+	clen = iwe->u.data.length;
+	if (custom + clen > end)
+		return;
+
+	if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) {
+		char *spos;
+		int bytes;
+		spos = custom + 7;
+		bytes = custom + clen - spos;
+		if (bytes & 1 || bytes == 0)
+			return;
+		bytes /= 2;
+		tmp = os_realloc(res->ie, res->ie_len + bytes);
+		if (tmp == NULL)
+			return;
+		res->ie = tmp;
+		if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0)
+			return;
+		res->ie_len += bytes;
+	} else if (clen > 7 && os_strncmp(custom, "rsn_ie=", 7) == 0) {
+		char *spos;
+		int bytes;
+		spos = custom + 7;
+		bytes = custom + clen - spos;
+		if (bytes & 1 || bytes == 0)
+			return;
+		bytes /= 2;
+		tmp = os_realloc(res->ie, res->ie_len + bytes);
+		if (tmp == NULL)
+			return;
+		res->ie = tmp;
+		if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0)
+			return;
+		res->ie_len += bytes;
+	} else if (clen > 4 && os_strncmp(custom, "tsf=", 4) == 0) {
+		char *spos;
+		int bytes;
+		u8 bin[8];
+		spos = custom + 4;
+		bytes = custom + clen - spos;
+		if (bytes != 16) {
+			wpa_printf(MSG_INFO, "Invalid TSF length (%d)", bytes);
+			return;
+		}
+		bytes /= 2;
+		if (hexstr2bin(spos, bin, bytes) < 0) {
+			wpa_printf(MSG_DEBUG, "WEXT: Invalid TSF value");
+			return;
+		}
+		res->res.tsf += WPA_GET_BE64(bin);
+	}
+}
+
+
+static int wext_19_iw_point(struct wpa_driver_wext_data *drv, u16 cmd)
+{
+	return drv->we_version_compiled > 18 &&
+		(cmd == SIOCGIWESSID || cmd == SIOCGIWENCODE ||
+		 cmd == IWEVGENIE || cmd == IWEVCUSTOM);
+}
+
+
+static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res,
+					   struct wext_scan_data *data)
+{
+	struct wpa_scan_res **tmp;
+	struct wpa_scan_res *r;
+	size_t extra_len;
+	u8 *pos, *end, *ssid_ie = NULL, *rate_ie = NULL;
+
+	/* Figure out whether we need to fake any IEs */
+	pos = data->ie;
+	end = pos + data->ie_len;
+	while (pos && pos + 1 < end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		if (pos[0] == WLAN_EID_SSID)
+			ssid_ie = pos;
+		else if (pos[0] == WLAN_EID_SUPP_RATES)
+			rate_ie = pos;
+		else if (pos[0] == WLAN_EID_EXT_SUPP_RATES)
+			rate_ie = pos;
+		pos += 2 + pos[1];
+	}
+
+	extra_len = 0;
+	if (ssid_ie == NULL)
+		extra_len += 2 + data->ssid_len;
+	if (rate_ie == NULL && data->maxrate)
+		extra_len += 3;
+
+	r = os_zalloc(sizeof(*r) + extra_len + data->ie_len);
+	if (r == NULL)
+		return;
+	os_memcpy(r, &data->res, sizeof(*r));
+	r->ie_len = extra_len + data->ie_len;
+	pos = (u8 *) (r + 1);
+	if (ssid_ie == NULL) {
+		/*
+		 * Generate a fake SSID IE since the driver did not report
+		 * a full IE list.
+		 */
+		*pos++ = WLAN_EID_SSID;
+		*pos++ = data->ssid_len;
+		os_memcpy(pos, data->ssid, data->ssid_len);
+		pos += data->ssid_len;
+	}
+	if (rate_ie == NULL && data->maxrate) {
+		/*
+		 * Generate a fake Supported Rates IE since the driver did not
+		 * report a full IE list.
+		 */
+		*pos++ = WLAN_EID_SUPP_RATES;
+		*pos++ = 1;
+		*pos++ = data->maxrate;
+	}
+	if (data->ie)
+		os_memcpy(pos, data->ie, data->ie_len);
+
+	tmp = os_realloc_array(res->res, res->num + 1,
+			       sizeof(struct wpa_scan_res *));
+	if (tmp == NULL) {
+		os_free(r);
+		return;
+	}
+	tmp[res->num++] = r;
+	res->res = tmp;
+}
+
+
+/**
+ * wpa_driver_wext_get_scan_results - Fetch the latest scan results
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * Returns: Scan results on success, -1 on failure
+ */
+struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	size_t len;
+	int first;
+	u8 *res_buf;
+	struct iw_event iwe_buf, *iwe = &iwe_buf;
+	char *pos, *end, *custom;
+	struct wpa_scan_results *res;
+	struct wext_scan_data data;
+
+	res_buf = wpa_driver_wext_giwscan(drv, &len);
+	if (res_buf == NULL)
+		return NULL;
+
+	first = 1;
+
+	res = os_zalloc(sizeof(*res));
+	if (res == NULL) {
+		os_free(res_buf);
+		return NULL;
+	}
+
+	pos = (char *) res_buf;
+	end = (char *) res_buf + len;
+	os_memset(&data, 0, sizeof(data));
+
+	while (pos + IW_EV_LCP_LEN <= end) {
+		/* Event data may be unaligned, so make a local, aligned copy
+		 * before processing. */
+		os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+		if (iwe->len <= IW_EV_LCP_LEN)
+			break;
+
+		custom = pos + IW_EV_POINT_LEN;
+		if (wext_19_iw_point(drv, iwe->cmd)) {
+			/* WE-19 removed the pointer from struct iw_point */
+			char *dpos = (char *) &iwe_buf.u.data.length;
+			int dlen = dpos - (char *) &iwe_buf;
+			os_memcpy(dpos, pos + IW_EV_LCP_LEN,
+				  sizeof(struct iw_event) - dlen);
+		} else {
+			os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+			custom += IW_EV_POINT_OFF;
+		}
+
+		switch (iwe->cmd) {
+		case SIOCGIWAP:
+			if (!first)
+				wpa_driver_wext_add_scan_entry(res, &data);
+			first = 0;
+			os_free(data.ie);
+			os_memset(&data, 0, sizeof(data));
+			os_memcpy(data.res.bssid,
+				  iwe->u.ap_addr.sa_data, ETH_ALEN);
+			break;
+		case SIOCGIWMODE:
+			wext_get_scan_mode(iwe, &data);
+			break;
+		case SIOCGIWESSID:
+			wext_get_scan_ssid(iwe, &data, custom, end);
+			break;
+		case SIOCGIWFREQ:
+			wext_get_scan_freq(iwe, &data);
+			break;
+		case IWEVQUAL:
+			wext_get_scan_qual(drv, iwe, &data);
+			break;
+		case SIOCGIWENCODE:
+			wext_get_scan_encode(iwe, &data);
+			break;
+		case SIOCGIWRATE:
+			wext_get_scan_rate(iwe, &data, pos, end);
+			break;
+		case IWEVGENIE:
+			wext_get_scan_iwevgenie(iwe, &data, custom, end);
+			break;
+		case IWEVCUSTOM:
+			wext_get_scan_custom(iwe, &data, custom, end);
+			break;
+		}
+
+		pos += iwe->len;
+	}
+	os_free(res_buf);
+	res_buf = NULL;
+	if (!first)
+		wpa_driver_wext_add_scan_entry(res, &data);
+	os_free(data.ie);
+
+	wpa_printf(MSG_DEBUG, "Received %lu bytes of scan results (%lu BSSes)",
+		   (unsigned long) len, (unsigned long) res->num);
+
+	return res;
+}
+
+
+static int wpa_driver_wext_get_range(void *priv)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iw_range *range;
+	struct iwreq iwr;
+	int minlen;
+	size_t buflen;
+
+	/*
+	 * Use larger buffer than struct iw_range in order to allow the
+	 * structure to grow in the future.
+	 */
+	buflen = sizeof(struct iw_range) + 500;
+	range = os_zalloc(buflen);
+	if (range == NULL)
+		return -1;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.data.pointer = (caddr_t) range;
+	iwr.u.data.length = buflen;
+
+	minlen = ((char *) &range->enc_capa) - (char *) range +
+		sizeof(range->enc_capa);
+
+	if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+			   strerror(errno));
+		os_free(range);
+		return -1;
+	} else if (iwr.u.data.length >= minlen &&
+		   range->we_version_compiled >= 18) {
+		wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
+			   "WE(source)=%d enc_capa=0x%x",
+			   range->we_version_compiled,
+			   range->we_version_source,
+			   range->enc_capa);
+		drv->has_capability = 1;
+		drv->we_version_compiled = range->we_version_compiled;
+		if (range->enc_capa & IW_ENC_CAPA_WPA) {
+			drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+				WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+		}
+		if (range->enc_capa & IW_ENC_CAPA_WPA2) {
+			drv->capa.key_mgmt |= 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;
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP128;
+		if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP)
+			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+		if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
+			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+		if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE)
+			drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+		drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+			WPA_DRIVER_AUTH_SHARED |
+			WPA_DRIVER_AUTH_LEAP;
+		drv->capa.max_scan_ssids = 1;
+
+		wpa_printf(MSG_DEBUG, "  capabilities: key_mgmt 0x%x enc 0x%x "
+			   "flags 0x%llx",
+			   drv->capa.key_mgmt, drv->capa.enc,
+			   (unsigned long long) drv->capa.flags);
+	} else {
+		wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - "
+			   "assuming WPA is not supported");
+	}
+
+	drv->max_level = range->max_qual.level;
+
+	os_free(range);
+	return 0;
+}
+
+
+static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv,
+				   const u8 *psk)
+{
+	struct iw_encode_ext *ext;
+	struct iwreq iwr;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+		return 0;
+
+	if (!psk)
+		return 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+	ext = os_zalloc(sizeof(*ext) + PMK_LEN);
+	if (ext == NULL)
+		return -1;
+
+	iwr.u.encoding.pointer = (caddr_t) ext;
+	iwr.u.encoding.length = sizeof(*ext) + PMK_LEN;
+	ext->key_len = PMK_LEN;
+	os_memcpy(&ext->key, psk, ext->key_len);
+	ext->alg = IW_ENCODE_ALG_PMK;
+
+	ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr);
+	if (ret < 0)
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT] PMK: %s",
+			   strerror(errno));
+	os_free(ext);
+
+	return ret;
+}
+
+
+static int wpa_driver_wext_set_key_ext(void *priv, 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)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+	struct iw_encode_ext *ext;
+
+	if (seq_len > IW_ENCODE_SEQ_MAX_SIZE) {
+		wpa_printf(MSG_DEBUG, "%s: Invalid seq_len %lu",
+			   __FUNCTION__, (unsigned long) seq_len);
+		return -1;
+	}
+
+	ext = os_zalloc(sizeof(*ext) + key_len);
+	if (ext == NULL)
+		return -1;
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.encoding.flags = key_idx + 1;
+	iwr.u.encoding.flags |= IW_ENCODE_TEMP;
+	if (alg == WPA_ALG_NONE)
+		iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
+	iwr.u.encoding.pointer = (caddr_t) ext;
+	iwr.u.encoding.length = sizeof(*ext) + key_len;
+
+	if (addr == NULL || is_broadcast_ether_addr(addr))
+		ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY;
+	if (set_tx)
+		ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY;
+
+	ext->addr.sa_family = ARPHRD_ETHER;
+	if (addr)
+		os_memcpy(ext->addr.sa_data, addr, ETH_ALEN);
+	else
+		os_memset(ext->addr.sa_data, 0xff, ETH_ALEN);
+	if (key && key_len) {
+		os_memcpy(ext + 1, key, key_len);
+		ext->key_len = key_len;
+	}
+	switch (alg) {
+	case WPA_ALG_NONE:
+		ext->alg = IW_ENCODE_ALG_NONE;
+		break;
+	case WPA_ALG_WEP:
+		ext->alg = IW_ENCODE_ALG_WEP;
+		break;
+	case WPA_ALG_TKIP:
+		ext->alg = IW_ENCODE_ALG_TKIP;
+		break;
+	case WPA_ALG_CCMP:
+		ext->alg = IW_ENCODE_ALG_CCMP;
+		break;
+	case WPA_ALG_PMK:
+		ext->alg = IW_ENCODE_ALG_PMK;
+		break;
+#ifdef CONFIG_IEEE80211W
+	case WPA_ALG_IGTK:
+		ext->alg = IW_ENCODE_ALG_AES_CMAC;
+		break;
+#endif /* CONFIG_IEEE80211W */
+	default:
+		wpa_printf(MSG_DEBUG, "%s: Unknown algorithm %d",
+			   __FUNCTION__, alg);
+		os_free(ext);
+		return -1;
+	}
+
+	if (seq && seq_len) {
+		ext->ext_flags |= IW_ENCODE_EXT_RX_SEQ_VALID;
+		os_memcpy(ext->rx_seq, seq, seq_len);
+	}
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr) < 0) {
+		ret = errno == EOPNOTSUPP ? -2 : -1;
+		if (errno == ENODEV) {
+			/*
+			 * ndiswrapper seems to be returning incorrect error
+			 * code.. */
+			ret = -2;
+		}
+
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT]: %s",
+			   strerror(errno));
+	}
+
+	os_free(ext);
+	return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_key - Configure encryption key
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @priv: Private driver interface data
+ * @alg: Encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
+ *	%WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key.
+ * @addr: Address of the peer STA or ff:ff:ff:ff:ff:ff for
+ *	broadcast/default keys
+ * @key_idx: key index (0..3), usually 0 for unicast keys
+ * @set_tx: Configure this key as the default Tx key (only used when
+ *	driver does not support separate unicast/individual key
+ * @seq: Sequence number/packet number, seq_len octets, the next
+ *	packet number to be used for in replay protection; configured
+ *	for Rx keys (in most cases, this is only used with broadcast
+ *	keys and set to zero for unicast keys)
+ * @seq_len: Length of the seq, depends on the algorithm:
+ *	TKIP: 6 octets, CCMP: 6 octets
+ * @key: Key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
+ *	8-byte Rx Mic Key
+ * @key_len: Length of the key buffer in octets (WEP: 5 or 13,
+ *	TKIP: 32, CCMP: 16)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function uses SIOCSIWENCODEEXT by default, but tries to use
+ * SIOCSIWENCODE if the extended ioctl fails when configuring a WEP key.
+ */
+int wpa_driver_wext_set_key(const char *ifname, void *priv, 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)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu "
+		   "key_len=%lu",
+		   __FUNCTION__, alg, key_idx, set_tx,
+		   (unsigned long) seq_len, (unsigned long) key_len);
+
+	ret = wpa_driver_wext_set_key_ext(drv, alg, addr, key_idx, set_tx,
+					  seq, seq_len, key, key_len);
+	if (ret == 0)
+		return 0;
+
+	if (ret == -2 &&
+	    (alg == WPA_ALG_NONE || alg == WPA_ALG_WEP)) {
+		wpa_printf(MSG_DEBUG, "Driver did not support "
+			   "SIOCSIWENCODEEXT, trying SIOCSIWENCODE");
+		ret = 0;
+	} else {
+		wpa_printf(MSG_DEBUG, "Driver did not support "
+			   "SIOCSIWENCODEEXT");
+		return ret;
+	}
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.encoding.flags = key_idx + 1;
+	iwr.u.encoding.flags |= IW_ENCODE_TEMP;
+	if (alg == WPA_ALG_NONE)
+		iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
+	iwr.u.encoding.pointer = (caddr_t) key;
+	iwr.u.encoding.length = key_len;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s",
+			   strerror(errno));
+		ret = -1;
+	}
+
+	if (set_tx && alg != WPA_ALG_NONE) {
+		os_memset(&iwr, 0, sizeof(iwr));
+		os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+		iwr.u.encoding.flags = key_idx + 1;
+		iwr.u.encoding.flags |= IW_ENCODE_TEMP;
+		iwr.u.encoding.pointer = (caddr_t) NULL;
+		iwr.u.encoding.length = 0;
+		if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
+			wpa_printf(MSG_ERROR,
+				   "ioctl[SIOCSIWENCODE] (set_tx): %s",
+				   strerror(errno));
+			ret = -1;
+		}
+	}
+
+	return ret;
+}
+
+
+static int wpa_driver_wext_set_countermeasures(void *priv,
+					       int enabled)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+	return wpa_driver_wext_set_auth_param(drv,
+					      IW_AUTH_TKIP_COUNTERMEASURES,
+					      enabled);
+}
+
+
+static int wpa_driver_wext_set_drop_unencrypted(void *priv,
+						int enabled)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+	drv->use_crypt = enabled;
+	return wpa_driver_wext_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED,
+					      enabled);
+}
+
+
+static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv,
+				const u8 *addr, int cmd, int reason_code)
+{
+	struct iwreq iwr;
+	struct iw_mlme mlme;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	os_memset(&mlme, 0, sizeof(mlme));
+	mlme.cmd = cmd;
+	mlme.reason_code = reason_code;
+	mlme.addr.sa_family = ARPHRD_ETHER;
+	os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN);
+	iwr.u.data.pointer = (caddr_t) &mlme;
+	iwr.u.data.length = sizeof(mlme);
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMLME]: %s",
+			   strerror(errno));
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv)
+{
+	struct iwreq iwr;
+	const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+	u8 ssid[SSID_MAX_LEN];
+	int i;
+
+	/*
+	 * Only force-disconnect when the card is in infrastructure mode,
+	 * otherwise the driver might interpret the cleared BSSID and random
+	 * SSID as an attempt to create a new ad-hoc network.
+	 */
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s",
+			   strerror(errno));
+		iwr.u.mode = IW_MODE_INFRA;
+	}
+
+	if (iwr.u.mode == IW_MODE_INFRA) {
+		/* Clear the BSSID selection */
+		if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0) {
+			wpa_printf(MSG_DEBUG, "WEXT: Failed to clear BSSID "
+				   "selection on disconnect");
+		}
+
+		if (drv->cfg80211) {
+			/*
+			 * cfg80211 supports SIOCSIWMLME commands, so there is
+			 * no need for the random SSID hack, but clear the
+			 * SSID.
+			 */
+			if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
+				wpa_printf(MSG_DEBUG, "WEXT: Failed to clear "
+					   "SSID on disconnect");
+			}
+			return;
+		}
+
+		/*
+		 * Set a random SSID to make sure the driver will not be trying
+		 * to associate with something even if it does not understand
+		 * SIOCSIWMLME commands (or tries to associate automatically
+		 * after deauth/disassoc).
+		 */
+		for (i = 0; i < SSID_MAX_LEN; i++)
+			ssid[i] = rand() & 0xFF;
+		if (wpa_driver_wext_set_ssid(drv, ssid, SSID_MAX_LEN) < 0) {
+			wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus "
+				   "SSID to disconnect");
+		}
+	}
+}
+
+
+static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr,
+					  int reason_code)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	int ret;
+	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+	ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code);
+	wpa_driver_wext_disconnect(drv);
+	return ret;
+}
+
+
+static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie,
+				      size_t ie_len)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.data.pointer = (caddr_t) ie;
+	iwr.u.data.length = ie_len;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWGENIE]: %s",
+			   strerror(errno));
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+int wpa_driver_wext_cipher2wext(int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_NONE:
+		return IW_AUTH_CIPHER_NONE;
+	case WPA_CIPHER_WEP40:
+		return IW_AUTH_CIPHER_WEP40;
+	case WPA_CIPHER_TKIP:
+		return IW_AUTH_CIPHER_TKIP;
+	case WPA_CIPHER_CCMP:
+		return IW_AUTH_CIPHER_CCMP;
+	case WPA_CIPHER_WEP104:
+		return IW_AUTH_CIPHER_WEP104;
+	default:
+		return 0;
+	}
+}
+
+
+int wpa_driver_wext_keymgmt2wext(int keymgmt)
+{
+	switch (keymgmt) {
+	case WPA_KEY_MGMT_IEEE8021X:
+	case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
+		return IW_AUTH_KEY_MGMT_802_1X;
+	case WPA_KEY_MGMT_PSK:
+		return IW_AUTH_KEY_MGMT_PSK;
+	default:
+		return 0;
+	}
+}
+
+
+static int
+wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv,
+				  struct wpa_driver_associate_params *params)
+{
+	struct iwreq iwr;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "WEXT: Driver did not support "
+		   "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE");
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	/* Just changing mode, not actual keys */
+	iwr.u.encoding.flags = 0;
+	iwr.u.encoding.pointer = (caddr_t) NULL;
+	iwr.u.encoding.length = 0;
+
+	/*
+	 * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two
+	 * different things. Here they are used to indicate Open System vs.
+	 * Shared Key authentication algorithm. However, some drivers may use
+	 * them to select between open/restricted WEP encrypted (open = allow
+	 * both unencrypted and encrypted frames; restricted = only allow
+	 * encrypted frames).
+	 */
+
+	if (!drv->use_crypt) {
+		iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
+	} else {
+		if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+			iwr.u.encoding.flags |= IW_ENCODE_OPEN;
+		if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+			iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED;
+	}
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s",
+			   strerror(errno));
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+int wpa_driver_wext_associate(void *priv,
+			      struct wpa_driver_associate_params *params)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	int ret = 0;
+	int allow_unencrypted_eapol;
+	int value;
+
+	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+	if (drv->cfg80211) {
+		/*
+		 * Stop cfg80211 from trying to associate before we are done
+		 * with all parameters.
+		 */
+		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)
+	    < 0)
+		ret = -1;
+	if (wpa_driver_wext_set_auth_alg(drv, params->auth_alg) < 0)
+		ret = -1;
+	if (wpa_driver_wext_set_mode(drv, params->mode) < 0)
+		ret = -1;
+
+	/*
+	 * If the driver did not support SIOCSIWAUTH, fallback to
+	 * SIOCSIWENCODE here.
+	 */
+	if (drv->auth_alg_fallback &&
+	    wpa_driver_wext_auth_alg_fallback(drv, params) < 0)
+		ret = -1;
+
+	if (!params->bssid &&
+	    wpa_driver_wext_set_bssid(drv, NULL) < 0)
+		ret = -1;
+
+	/* TODO: should consider getting wpa version and cipher/key_mgmt suites
+	 * from configuration, not from here, where only the selected suite is
+	 * available */
+	if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len)
+	    < 0)
+		ret = -1;
+	if (params->wpa_proto & WPA_PROTO_RSN)
+		value = IW_AUTH_WPA_VERSION_WPA2;
+	else if (params->wpa_proto & WPA_PROTO_WPA)
+		value = IW_AUTH_WPA_VERSION_WPA;
+	else
+		value = IW_AUTH_WPA_VERSION_DISABLED;
+	if (wpa_driver_wext_set_auth_param(drv,
+					   IW_AUTH_WPA_VERSION, value) < 0)
+		ret = -1;
+	value = wpa_driver_wext_cipher2wext(params->pairwise_suite);
+	if (wpa_driver_wext_set_auth_param(drv,
+					   IW_AUTH_CIPHER_PAIRWISE, value) < 0)
+		ret = -1;
+	value = wpa_driver_wext_cipher2wext(params->group_suite);
+	if (wpa_driver_wext_set_auth_param(drv,
+					   IW_AUTH_CIPHER_GROUP, value) < 0)
+		ret = -1;
+	value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite);
+	if (wpa_driver_wext_set_auth_param(drv,
+					   IW_AUTH_KEY_MGMT, value) < 0)
+		ret = -1;
+	value = params->key_mgmt_suite != WPA_KEY_MGMT_NONE ||
+		params->pairwise_suite != WPA_CIPHER_NONE ||
+		params->group_suite != WPA_CIPHER_NONE ||
+		(params->wpa_proto & (WPA_PROTO_RSN | WPA_PROTO_WPA));
+	if (wpa_driver_wext_set_auth_param(drv,
+					   IW_AUTH_PRIVACY_INVOKED, value) < 0)
+		ret = -1;
+
+	/* 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 == WPA_KEY_MGMT_IEEE8021X ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
+		allow_unencrypted_eapol = 0;
+	else
+		allow_unencrypted_eapol = 1;
+
+	if (wpa_driver_wext_set_psk(drv, params->psk) < 0)
+		ret = -1;
+	if (wpa_driver_wext_set_auth_param(drv,
+					   IW_AUTH_RX_UNENCRYPTED_EAPOL,
+					   allow_unencrypted_eapol) < 0)
+		ret = -1;
+#ifdef CONFIG_IEEE80211W
+	switch (params->mgmt_frame_protection) {
+	case NO_MGMT_FRAME_PROTECTION:
+		value = IW_AUTH_MFP_DISABLED;
+		break;
+	case MGMT_FRAME_PROTECTION_OPTIONAL:
+		value = IW_AUTH_MFP_OPTIONAL;
+		break;
+	case MGMT_FRAME_PROTECTION_REQUIRED:
+		value = IW_AUTH_MFP_REQUIRED;
+		break;
+	};
+	if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0)
+		ret = -1;
+#endif /* CONFIG_IEEE80211W */
+	if (params->freq.freq &&
+	    wpa_driver_wext_set_freq(drv, params->freq.freq) < 0)
+		ret = -1;
+	if (!drv->cfg80211 &&
+	    wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
+		ret = -1;
+	if (params->bssid &&
+	    wpa_driver_wext_set_bssid(drv, params->bssid) < 0)
+		ret = -1;
+	if (drv->cfg80211 &&
+	    wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
+		ret = -1;
+
+	return ret;
+}
+
+
+static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	int algs = 0, res;
+
+	if (auth_alg & WPA_AUTH_ALG_OPEN)
+		algs |= IW_AUTH_ALG_OPEN_SYSTEM;
+	if (auth_alg & WPA_AUTH_ALG_SHARED)
+		algs |= IW_AUTH_ALG_SHARED_KEY;
+	if (auth_alg & WPA_AUTH_ALG_LEAP)
+		algs |= IW_AUTH_ALG_LEAP;
+	if (algs == 0) {
+		/* at least one algorithm should be set */
+		algs = IW_AUTH_ALG_OPEN_SYSTEM;
+	}
+
+	res = wpa_driver_wext_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG,
+					     algs);
+	drv->auth_alg_fallback = res == -2;
+	return res;
+}
+
+
+/**
+ * wpa_driver_wext_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_mode(void *priv, int mode)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = -1;
+	unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.mode = new_mode;
+	if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) == 0) {
+		ret = 0;
+		goto done;
+	}
+
+	if (errno != EBUSY) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s",
+			   strerror(errno));
+		goto done;
+	}
+
+	/* mac80211 doesn't allow mode changes while the device is up, so if
+	 * the device isn't in the mode we're about to change to, take device
+	 * down, try to set the mode again, and bring it back up.
+	 */
+	if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s",
+			   strerror(errno));
+		goto done;
+	}
+
+	if (iwr.u.mode == new_mode) {
+		ret = 0;
+		goto done;
+	}
+
+	if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0) == 0) {
+		/* Try to set the mode again while the interface is down */
+		iwr.u.mode = new_mode;
+		if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0)
+			wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s",
+				   strerror(errno));
+		else
+			ret = 0;
+
+		(void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1);
+	}
+
+done:
+	return ret;
+}
+
+
+static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv,
+				 u32 cmd, const u8 *bssid, const u8 *pmkid)
+{
+	struct iwreq iwr;
+	struct iw_pmksa pmksa;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	os_memset(&pmksa, 0, sizeof(pmksa));
+	pmksa.cmd = cmd;
+	pmksa.bssid.sa_family = ARPHRD_ETHER;
+	if (bssid)
+		os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN);
+	if (pmkid)
+		os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN);
+	iwr.u.data.pointer = (caddr_t) &pmksa;
+	iwr.u.data.length = sizeof(pmksa);
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) {
+		if (errno != EOPNOTSUPP)
+			wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPMKSA]: %s",
+				   strerror(errno));
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid,
+				     const u8 *pmkid)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid);
+}
+
+
+static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid,
+		 			const u8 *pmkid)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid);
+}
+
+
+static int wpa_driver_wext_flush_pmkid(void *priv)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	return wpa_driver_wext_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL);
+}
+
+
+int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	if (!drv->has_capability)
+		return -1;
+	os_memcpy(capa, &drv->capa, sizeof(*capa));
+	return 0;
+}
+
+
+int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv,
+					const char *ifname)
+{
+	if (ifname == NULL) {
+		drv->ifindex2 = -1;
+		return 0;
+	}
+
+	drv->ifindex2 = if_nametoindex(ifname);
+	if (drv->ifindex2 <= 0)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "Added alternative ifindex %d (%s) for "
+		   "wireless events", drv->ifindex2, ifname);
+
+	return 0;
+}
+
+
+int wpa_driver_wext_set_operstate(void *priv, int state)
+{
+	struct wpa_driver_wext_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
+		   __func__, drv->operstate, state, state ? "UP" : "DORMANT");
+	drv->operstate = state;
+	return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
+				      state ? IF_OPER_UP : IF_OPER_DORMANT);
+}
+
+
+int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv)
+{
+	return drv->we_version_compiled;
+}
+
+
+static const char * wext_get_radio_name(void *priv)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	return drv->phyname;
+}
+
+
+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;
+
+	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;
+
+	if (ioctl(drv->ioctl_sock, SIOCGIWSTATS, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "WEXT: SIOCGIWSTATS: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	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 wpa_driver_wext_status(void *priv, char *buf, size_t buflen)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	int res;
+	char *pos, *end;
+	unsigned char addr[ETH_ALEN];
+
+	pos = buf;
+	end = buf + buflen;
+
+	if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, addr))
+		return -1;
+
+	res = os_snprintf(pos, end - pos,
+			  "ifindex=%d\n"
+			  "ifname=%s\n"
+			  "addr=" MACSTR "\n",
+			  drv->ifindex,
+			  drv->ifname,
+			  MAC2STR(addr));
+	if (os_snprintf_error(end - pos, res))
+		return pos - buf;
+	pos += res;
+
+	return pos - buf;
+}
+
+const struct wpa_driver_ops wpa_driver_wext_ops = {
+	.name = "wext",
+	.desc = "Linux wireless extensions (generic)",
+	.get_bssid = wpa_driver_wext_get_bssid,
+	.get_ssid = wpa_driver_wext_get_ssid,
+	.set_key = wpa_driver_wext_set_key,
+	.set_countermeasures = wpa_driver_wext_set_countermeasures,
+	.scan2 = wpa_driver_wext_scan,
+	.get_scan_results2 = wpa_driver_wext_get_scan_results,
+	.deauthenticate = wpa_driver_wext_deauthenticate,
+	.associate = wpa_driver_wext_associate,
+	.init = wpa_driver_wext_init,
+	.deinit = wpa_driver_wext_deinit,
+	.add_pmkid = wpa_driver_wext_add_pmkid,
+	.remove_pmkid = wpa_driver_wext_remove_pmkid,
+	.flush_pmkid = wpa_driver_wext_flush_pmkid,
+	.get_capa = wpa_driver_wext_get_capa,
+	.set_operstate = wpa_driver_wext_set_operstate,
+	.get_radio_name = wext_get_radio_name,
+	.signal_poll = wpa_driver_wext_signal_poll,
+	.status = wpa_driver_wext_status,
+};
diff --git a/hostap/src/drivers/driver_wext.h b/hostap/src/drivers/driver_wext.h
new file mode 100644
index 0000000..b4b5960
--- /dev/null
+++ b/hostap/src/drivers/driver_wext.h
@@ -0,0 +1,81 @@
+/*
+ * WPA Supplicant - driver_wext exported functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_WEXT_H
+#define DRIVER_WEXT_H
+
+#include <net/if.h>
+
+struct wpa_driver_wext_data {
+	void *ctx;
+	struct netlink_data *netlink;
+	int ioctl_sock;
+	int mlme_sock;
+	char ifname[IFNAMSIZ + 1];
+	char phyname[32];
+	int ifindex;
+	int ifindex2;
+	int if_removed;
+	int if_disabled;
+	struct rfkill_data *rfkill;
+	u8 *assoc_req_ies;
+	size_t assoc_req_ies_len;
+	u8 *assoc_resp_ies;
+	size_t assoc_resp_ies_len;
+	struct wpa_driver_capa capa;
+	int has_capability;
+	int we_version_compiled;
+
+	/* for set_auth_alg fallback */
+	int use_crypt;
+	int auth_alg_fallback;
+
+	int operstate;
+
+	char mlmedev[IFNAMSIZ + 1];
+
+	int scan_complete_events;
+
+	int cfg80211; /* whether driver is using cfg80211 */
+
+	u8 max_level;
+};
+
+int wpa_driver_wext_get_bssid(void *priv, u8 *bssid);
+int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid);
+int wpa_driver_wext_get_ssid(void *priv, u8 *ssid);
+int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len);
+int wpa_driver_wext_set_freq(void *priv, int freq);
+int wpa_driver_wext_set_mode(void *priv, int mode);
+int wpa_driver_wext_set_key(const char *ifname, void *priv, 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);
+int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params);
+struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv);
+
+void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+
+int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv,
+					const char *ifname);
+
+void * wpa_driver_wext_init(void *ctx, const char *ifname);
+void wpa_driver_wext_deinit(void *priv);
+
+int wpa_driver_wext_set_operstate(void *priv, int state);
+int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv);
+
+int wpa_driver_wext_associate(void *priv,
+			      struct wpa_driver_associate_params *params);
+int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa);
+int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv,
+				   int idx, u32 value);
+int wpa_driver_wext_cipher2wext(int cipher);
+int wpa_driver_wext_keymgmt2wext(int keymgmt);
+
+#endif /* DRIVER_WEXT_H */
diff --git a/hostap/src/drivers/driver_wired.c b/hostap/src/drivers/driver_wired.c
new file mode 100644
index 0000000..f95f3cc
--- /dev/null
+++ b/hostap/src/drivers/driver_wired.c
@@ -0,0 +1,680 @@
+/*
+ * Wired Ethernet driver interface
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * 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 "common.h"
+#include "eloop.h"
+#include "driver.h"
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee8023_hdr {
+	u8 dest[6];
+	u8 src[6];
+	u16 ethertype;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+
+struct wpa_driver_wired_data {
+	char ifname[IFNAMSIZ + 1];
+	void *ctx;
+
+	int sock; /* raw packet socket for driver access */
+	int dhcp_sock; /* socket for dhcp packets */
+	int use_pae_group_addr;
+
+	int pf_sock;
+	int membership, multi, iff_allmulti, iff_up;
+};
+
+
+/* TODO: detecting new devices should eventually be changed from using DHCP
+ * snooping to trigger on any packet from a new layer 2 MAC address, e.g.,
+ * based on ebtables, etc. */
+
+struct dhcp_message {
+	u_int8_t op;
+	u_int8_t htype;
+	u_int8_t hlen;
+	u_int8_t hops;
+	u_int32_t xid;
+	u_int16_t secs;
+	u_int16_t flags;
+	u_int32_t ciaddr;
+	u_int32_t yiaddr;
+	u_int32_t siaddr;
+	u_int32_t giaddr;
+	u_int8_t chaddr[16];
+	u_int8_t sname[64];
+	u_int8_t file[128];
+	u_int32_t cookie;
+	u_int8_t options[308]; /* 312 - cookie */
+};
+
+
+static int wired_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) {
+		wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
+		return -1;
+	}
+	return 0;
+#else /* __linux__ */
+	return -1;
+#endif /* __linux__ */
+}
+
+
+#ifdef __linux__
+static void handle_data(void *ctx, unsigned char *buf, size_t len)
+{
+#ifdef HOSTAPD
+	struct ieee8023_hdr *hdr;
+	u8 *pos, *sa;
+	size_t left;
+	union wpa_event_data event;
+
+	/* must contain at least ieee8023_hdr 6 byte source, 6 byte dest,
+	 * 2 byte ethertype */
+	if (len < 14) {
+		wpa_printf(MSG_MSGDUMP, "handle_data: too short (%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+	hdr = (struct ieee8023_hdr *) buf;
+
+	switch (ntohs(hdr->ethertype)) {
+		case ETH_P_PAE:
+			wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
+			sa = hdr->src;
+			os_memset(&event, 0, sizeof(event));
+			event.new_sta.addr = sa;
+			wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
+
+			pos = (u8 *) (hdr + 1);
+			left = len - sizeof(*hdr);
+			drv_event_eapol_rx(ctx, sa, pos, left);
+		break;
+
+	default:
+		wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame",
+			   ntohs(hdr->ethertype));
+		break;
+	}
+#endif /* HOSTAPD */
+}
+
+
+static void handle_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	int len;
+	unsigned char buf[3000];
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+		return;
+	}
+
+	handle_data(eloop_ctx, buf, len);
+}
+
+
+static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	int len;
+	unsigned char buf[3000];
+	struct dhcp_message *msg;
+	u8 *mac_address;
+	union wpa_event_data event;
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+		return;
+	}
+
+	/* must contain at least dhcp_message->chaddr */
+	if (len < 44) {
+		wpa_printf(MSG_MSGDUMP, "handle_dhcp: too short (%d)", len);
+		return;
+	}
+
+	msg = (struct dhcp_message *) buf;
+	mac_address = (u8 *) &(msg->chaddr);
+
+	wpa_printf(MSG_MSGDUMP, "Got DHCP broadcast packet from " MACSTR,
+		   MAC2STR(mac_address));
+
+	os_memset(&event, 0, sizeof(event));
+	event.new_sta.addr = mac_address;
+	wpa_supplicant_event(eloop_ctx, EVENT_NEW_STA, &event);
+}
+#endif /* __linux__ */
+
+
+static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
+{
+#ifdef __linux__
+	struct ifreq ifr;
+	struct sockaddr_ll addr;
+	struct sockaddr_in addr2;
+	int n = 1;
+
+	drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
+	if (drv->sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) {
+		wpa_printf(MSG_INFO, "Could not register read socket");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+	if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sll_family = AF_PACKET;
+	addr.sll_ifindex = ifr.ifr_ifindex;
+	wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
+		   addr.sll_ifindex);
+
+	if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+		return -1;
+	}
+
+	/* filter multicast address */
+	if (wired_multicast_membership(drv->sock, ifr.ifr_ifindex,
+				       pae_group_addr, 1) < 0) {
+		wpa_printf(MSG_ERROR, "wired: Failed to add multicast group "
+			   "membership");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+	if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+		wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
+			   ifr.ifr_hwaddr.sa_family);
+		return -1;
+	}
+	os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+	/* setup dhcp listen socket for sta detection */
+	if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+		wpa_printf(MSG_ERROR, "socket call failed for dhcp: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx,
+				     NULL)) {
+		wpa_printf(MSG_INFO, "Could not register read socket");
+		return -1;
+	}
+
+	os_memset(&addr2, 0, sizeof(addr2));
+	addr2.sin_family = AF_INET;
+	addr2.sin_port = htons(67);
+	addr2.sin_addr.s_addr = INADDR_ANY;
+
+	if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n,
+		       sizeof(n)) == -1) {
+		wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_REUSEADDR]: %s",
+			   strerror(errno));
+		return -1;
+	}
+	if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n,
+		       sizeof(n)) == -1) {
+		wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_BROADCAST]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ);
+	if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE,
+		       (char *) &ifr, sizeof(ifr)) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "setsockopt[SOL_SOCKET,SO_BINDTODEVICE]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2,
+		 sizeof(struct sockaddr)) == -1) {
+		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+		return -1;
+	}
+
+	return 0;
+#else /* __linux__ */
+	return -1;
+#endif /* __linux__ */
+}
+
+
+static int wired_send_eapol(void *priv, const u8 *addr,
+			    const u8 *data, size_t data_len, int encrypt,
+			    const u8 *own_addr, u32 flags)
+{
+	struct wpa_driver_wired_data *drv = priv;
+	struct ieee8023_hdr *hdr;
+	size_t len;
+	u8 *pos;
+	int res;
+
+	len = sizeof(*hdr) + data_len;
+	hdr = os_zalloc(len);
+	if (hdr == NULL) {
+		wpa_printf(MSG_INFO,
+			   "malloc() failed for wired_send_eapol(len=%lu)",
+			   (unsigned long) len);
+		return -1;
+	}
+
+	os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr,
+		  ETH_ALEN);
+	os_memcpy(hdr->src, own_addr, ETH_ALEN);
+	hdr->ethertype = htons(ETH_P_PAE);
+
+	pos = (u8 *) (hdr + 1);
+	os_memcpy(pos, data, data_len);
+
+	res = send(drv->sock, (u8 *) hdr, len, 0);
+	os_free(hdr);
+
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "wired_send_eapol - packet len: %lu - failed: send: %s",
+			   (unsigned long) len, strerror(errno));
+	}
+
+	return res;
+}
+
+
+static void * wired_driver_hapd_init(struct hostapd_data *hapd,
+				     struct wpa_init_params *params)
+{
+	struct wpa_driver_wired_data *drv;
+
+	drv = os_zalloc(sizeof(struct wpa_driver_wired_data));
+	if (drv == NULL) {
+		wpa_printf(MSG_INFO,
+			   "Could not allocate memory for wired driver data");
+		return NULL;
+	}
+
+	drv->ctx = hapd;
+	os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
+	drv->use_pae_group_addr = params->use_pae_group_addr;
+
+	if (wired_init_sockets(drv, params->own_addr)) {
+		os_free(drv);
+		return NULL;
+	}
+
+	return drv;
+}
+
+
+static void wired_driver_hapd_deinit(void *priv)
+{
+	struct wpa_driver_wired_data *drv = priv;
+
+	if (drv->sock >= 0) {
+		eloop_unregister_read_sock(drv->sock);
+		close(drv->sock);
+	}
+
+	if (drv->dhcp_sock >= 0) {
+		eloop_unregister_read_sock(drv->dhcp_sock);
+		close(drv->dhcp_sock);
+	}
+
+	os_free(drv);
+}
+
+
+static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid)
+{
+	ssid[0] = 0;
+	return 0;
+}
+
+
+static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid)
+{
+	/* Report PAE group address as the "BSSID" for wired connection. */
+	os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+	return 0;
+}
+
+
+static int wpa_driver_wired_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 wpa_driver_wired_get_ifflags(const char *ifname, int *flags)
+{
+	struct ifreq ifr;
+	int s;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+	if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+			   strerror(errno));
+		close(s);
+		return -1;
+	}
+	close(s);
+	*flags = ifr.ifr_flags & 0xffff;
+	return 0;
+}
+
+
+static int wpa_driver_wired_set_ifflags(const char *ifname, int flags)
+{
+	struct ifreq ifr;
+	int s;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		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) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+			   strerror(errno));
+		close(s);
+		return -1;
+	}
+	close(s);
+	return 0;
+}
+
+
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+static int wpa_driver_wired_get_ifstatus(const char *ifname, int *status)
+{
+	struct ifmediareq ifmr;
+	int s;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifmr, 0, sizeof(ifmr));
+	os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
+	if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
+			   strerror(errno));
+		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 wpa_driver_wired_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) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		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) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
+			   strerror(errno));
+		close(s);
+		return -1;
+	}
+	close(s);
+	return 0;
+}
+
+
+static void * wpa_driver_wired_init(void *ctx, const char *ifname)
+{
+	struct wpa_driver_wired_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;
+
+#ifdef __linux__
+	drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+	if (drv->pf_sock < 0)
+		wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
+#else /* __linux__ */
+	drv->pf_sock = -1;
+#endif /* __linux__ */
+
+	if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 &&
+	    !(flags & IFF_UP) &&
+	    wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) {
+		drv->iff_up = 1;
+	}
+
+	if (wired_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 (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) {
+		wpa_printf(MSG_DEBUG, "%s: Added multicast membership with "
+			   "SIOCADDMULTI", __func__);
+		drv->multi = 1;
+	} else if (wpa_driver_wired_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 (wpa_driver_wired_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 (wpa_driver_wired_get_ifstatus(ifname, &status) == 0 &&
+		       status == 0)
+			sleep(1);
+	}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+	return drv;
+}
+
+
+static void wpa_driver_wired_deinit(void *priv)
+{
+	struct wpa_driver_wired_data *drv = priv;
+	int flags;
+
+	if (drv->membership &&
+	    wired_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 &&
+	    wpa_driver_wired_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 &&
+	    (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 ||
+	     wpa_driver_wired_set_ifflags(drv->ifname,
+					  flags & ~IFF_ALLMULTI) < 0)) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
+			   __func__);
+	}
+
+	if (drv->iff_up &&
+	    wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 &&
+	    (flags & IFF_UP) &&
+	    wpa_driver_wired_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);
+}
+
+
+const struct wpa_driver_ops wpa_driver_wired_ops = {
+	.name = "wired",
+	.desc = "Wired Ethernet driver",
+	.hapd_init = wired_driver_hapd_init,
+	.hapd_deinit = wired_driver_hapd_deinit,
+	.hapd_send_eapol = wired_send_eapol,
+	.get_ssid = wpa_driver_wired_get_ssid,
+	.get_bssid = wpa_driver_wired_get_bssid,
+	.get_capa = wpa_driver_wired_get_capa,
+	.init = wpa_driver_wired_init,
+	.deinit = wpa_driver_wired_deinit,
+};
diff --git a/hostap/src/drivers/drivers.c b/hostap/src/drivers/drivers.c
new file mode 100644
index 0000000..a98af9a
--- /dev/null
+++ b/hostap/src/drivers/drivers.c
@@ -0,0 +1,86 @@
+/*
+ * Driver interface list
+ * Copyright (c) 2004-2005, 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 "driver.h"
+
+#ifdef CONFIG_DRIVER_WEXT
+extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_NL80211
+extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_HOSTAP
+extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_BSD
+extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_OPENBSD
+extern struct wpa_driver_ops wpa_driver_openbsd_ops; /* driver_openbsd.c */
+#endif /* CONFIG_DRIVER_OPENBSD */
+#ifdef CONFIG_DRIVER_NDIS
+extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */
+#endif /* CONFIG_DRIVER_NDIS */
+#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_ROBOSWITCH
+/* driver_roboswitch.c */
+extern struct wpa_driver_ops wpa_driver_roboswitch_ops;
+#endif /* CONFIG_DRIVER_ROBOSWITCH */
+#ifdef CONFIG_DRIVER_ATHEROS
+extern struct wpa_driver_ops wpa_driver_atheros_ops; /* driver_atheros.c */
+#endif /* CONFIG_DRIVER_ATHEROS */
+#ifdef CONFIG_DRIVER_NONE
+extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */
+#endif /* CONFIG_DRIVER_NONE */
+
+
+const struct wpa_driver_ops *const wpa_drivers[] =
+{
+#ifdef CONFIG_DRIVER_NL80211
+	&wpa_driver_nl80211_ops,
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_WEXT
+	&wpa_driver_wext_ops,
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_HOSTAP
+	&wpa_driver_hostap_ops,
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_BSD
+	&wpa_driver_bsd_ops,
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_OPENBSD
+	&wpa_driver_openbsd_ops,
+#endif /* CONFIG_DRIVER_OPENBSD */
+#ifdef CONFIG_DRIVER_NDIS
+	&wpa_driver_ndis_ops,
+#endif /* CONFIG_DRIVER_NDIS */
+#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_ROBOSWITCH
+	&wpa_driver_roboswitch_ops,
+#endif /* CONFIG_DRIVER_ROBOSWITCH */
+#ifdef CONFIG_DRIVER_ATHEROS
+	&wpa_driver_atheros_ops,
+#endif /* CONFIG_DRIVER_ATHEROS */
+#ifdef CONFIG_DRIVER_NONE
+	&wpa_driver_none_ops,
+#endif /* CONFIG_DRIVER_NONE */
+	NULL
+};
diff --git a/hostap/src/drivers/drivers.mak b/hostap/src/drivers/drivers.mak
new file mode 100644
index 0000000..3dd43c7
--- /dev/null
+++ b/hostap/src/drivers/drivers.mak
@@ -0,0 +1,200 @@
+##### CLEAR VARS
+
+DRV_CFLAGS =
+DRV_WPA_CFLAGS =
+DRV_AP_CFLAGS =
+DRV_OBJS =
+DRV_WPA_OBJS =
+DRV_AP_OBJS =
+DRV_LIBS =
+DRV_WPA_LIBS =
+DRV_AP_LIBS =
+
+##### COMMON DRIVERS
+
+ifdef CONFIG_DRIVER_WIRED
+DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
+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
+DRV_OBJS += ../src/drivers/driver_nl80211_capa.o
+DRV_OBJS += ../src/drivers/driver_nl80211_event.o
+DRV_OBJS += ../src/drivers/driver_nl80211_monitor.o
+DRV_OBJS += ../src/drivers/driver_nl80211_scan.o
+DRV_OBJS += ../src/utils/radiotap.o
+NEED_SME=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
+
+ifdef CONFIG_LIBNL32
+  DRV_LIBS += -lnl-3
+  DRV_LIBS += -lnl-genl-3
+  DRV_CFLAGS += -DCONFIG_LIBNL20
+  ifdef LIBNL_INC
+    DRV_CFLAGS += -I$(LIBNL_INC)
+  else
+    PKG_CONFIG ?= pkg-config
+    DRV_CFLAGS += $(shell $(PKG_CONFIG) --cflags libnl-3.0)
+  endif
+ifdef CONFIG_LIBNL3_ROUTE
+  DRV_LIBS += -lnl-route-3
+  DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE
+endif
+else
+  ifdef CONFIG_LIBNL_TINY
+    DRV_LIBS += -lnl-tiny
+  else
+    ifndef CONFIG_OSX
+      DRV_LIBS += -lnl
+    endif
+  endif
+
+  ifdef CONFIG_LIBNL20
+    DRV_LIBS += -lnl-genl
+    DRV_CFLAGS += -DCONFIG_LIBNL20
+  endif
+endif
+endif
+
+ifdef CONFIG_DRIVER_BSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_BSD
+DRV_OBJS += ../src/drivers/driver_bsd.o
+CONFIG_L2_FREEBSD=y
+CONFIG_DNET_PCAP=y
+endif
+
+ifdef CONFIG_DRIVER_OPENBSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD
+DRV_OBJS += ../src/drivers/driver_openbsd.o
+endif
+
+ifdef CONFIG_DRIVER_NONE
+DRV_CFLAGS += -DCONFIG_DRIVER_NONE
+DRV_OBJS += ../src/drivers/driver_none.o
+endif
+
+##### PURE AP DRIVERS
+
+ifdef CONFIG_DRIVER_HOSTAP
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP
+DRV_AP_OBJS += ../src/drivers/driver_hostap.o
+CONFIG_WIRELESS_EXTENSION=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_ATHEROS
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS
+DRV_AP_OBJS += ../src/drivers/driver_atheros.o
+CONFIG_L2_PACKET=linux
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+ifdef ATH_GCM_SUPPORT
+CFLAGS += -DATH_GCM_SUPPORT
+endif
+endif
+
+##### PURE CLIENT DRIVERS
+
+ifdef CONFIG_DRIVER_WEXT
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
+CONFIG_WIRELESS_EXTENSION=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
+endif
+
+ifdef CONFIG_DRIVER_NDIS
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS
+DRV_WPA_OBJS += ../src/drivers/driver_ndis.o
+ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+DRV_WPA_OBJS += ../src/drivers/driver_ndis_.o
+endif
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=pcap
+endif
+CONFIG_WINPCAP=y
+ifdef CONFIG_USE_NDISUIO
+DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO
+endif
+endif
+
+ifdef CONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_OBJS += ../src/drivers/driver_roboswitch.o
+endif
+
+ifdef CONFIG_WIRELESS_EXTENSION
+DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+DRV_WPA_OBJS += ../src/drivers/driver_wext.o
+NEED_RFKILL=y
+endif
+
+ifdef NEED_NETLINK
+DRV_OBJS += ../src/drivers/netlink.o
+endif
+
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += ../src/drivers/linux_ioctl.o
+endif
+
+ifdef NEED_RFKILL
+DRV_OBJS += ../src/drivers/rfkill.o
+endif
+
+ifdef CONFIG_VLAN_NETLINK
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+ifdef CONFIG_LIBNL32
+  DRV_LIBS += -lnl-3
+  DRV_LIBS += -lnl-genl-3
+  DRV_LIBS += -lnl-route-3
+  DRV_CFLAGS += -DCONFIG_LIBNL20
+else
+  ifdef CONFIG_LIBNL_TINY
+    DRV_LIBS += -lnl-tiny
+  else
+    DRV_LIBS += -lnl
+  endif
+
+  ifdef CONFIG_LIBNL20
+    DRV_LIBS += -lnl-genl
+    DRV_LIBS += -lnl-route
+    DRV_CFLAGS += -DCONFIG_LIBNL20
+  endif
+endif
+endif
+endif
+
+##### COMMON VARS
+DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
+DRV_WPA_CFLAGS += $(DRV_CFLAGS)
+DRV_AP_CFLAGS += $(DRV_CFLAGS)
+
+DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS)
+DRV_WPA_LIBS += $(DRV_LIBS)
+DRV_AP_LIBS += $(DRV_LIBS)
+
+DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS)
+DRV_WPA_OBJS += $(DRV_OBJS)
+DRV_AP_OBJS += $(DRV_OBJS)
+
+DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS)
+DRV_WPA_LDFLAGS += $(DRV_LDFLAGS)
+DRV_AP_LDFLAGS += $(DRV_LDFLAGS)
diff --git a/hostap/src/drivers/drivers.mk b/hostap/src/drivers/drivers.mk
new file mode 100644
index 0000000..8da4c53
--- /dev/null
+++ b/hostap/src/drivers/drivers.mk
@@ -0,0 +1,189 @@
+##### CLEAR VARS
+
+DRV_CFLAGS =
+DRV_WPA_CFLAGS =
+DRV_AP_CFLAGS =
+DRV_OBJS =
+DRV_WPA_OBJS =
+DRV_AP_OBJS =
+DRV_LIBS =
+DRV_WPA_LIBS =
+DRV_AP_LIBS =
+
+##### COMMON DRIVERS
+
+ifdef CONFIG_DRIVER_WIRED
+DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
+DRV_OBJS += src/drivers/driver_wired.c
+endif
+
+ifdef CONFIG_DRIVER_NL80211
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
+DRV_OBJS += src/drivers/driver_nl80211.c
+DRV_OBJS += src/drivers/driver_nl80211_android.c
+DRV_OBJS += src/drivers/driver_nl80211_capa.c
+DRV_OBJS += src/drivers/driver_nl80211_event.c
+DRV_OBJS += src/drivers/driver_nl80211_monitor.c
+DRV_OBJS += src/drivers/driver_nl80211_scan.c
+DRV_OBJS += src/utils/radiotap.c
+NEED_SME=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
+
+ifdef CONFIG_LIBNL32
+  DRV_LIBS += -lnl-3
+  DRV_LIBS += -lnl-genl-3
+  DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3
+ifdef CONFIG_LIBNL3_ROUTE
+  DRV_LIBS += -lnl-route-3
+  DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE
+endif
+else
+  ifdef CONFIG_LIBNL_TINY
+    DRV_LIBS += -lnl-tiny
+  else
+    DRV_LIBS += -lnl
+  endif
+
+  ifdef CONFIG_LIBNL20
+    DRV_LIBS += -lnl-genl
+    DRV_CFLAGS += -DCONFIG_LIBNL20
+  endif
+endif
+endif
+
+ifdef CONFIG_DRIVER_BSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_BSD
+DRV_OBJS += src/drivers/driver_bsd.c
+CONFIG_L2_FREEBSD=y
+CONFIG_DNET_PCAP=y
+endif
+
+ifdef CONFIG_DRIVER_OPENBSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD
+DRV_OBJS += src/drivers/driver_openbsd.c
+endif
+
+ifdef CONFIG_DRIVER_NONE
+DRV_CFLAGS += -DCONFIG_DRIVER_NONE
+DRV_OBJS += src/drivers/driver_none.c
+endif
+
+##### PURE AP DRIVERS
+
+ifdef CONFIG_DRIVER_HOSTAP
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP
+DRV_AP_OBJS += src/drivers/driver_hostap.c
+CONFIG_WIRELESS_EXTENSION=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_ATHEROS
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS
+DRV_AP_OBJS += src/drivers/driver_atheros.c
+CONFIG_L2_PACKET=linux
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+##### PURE CLIENT DRIVERS
+
+ifdef CONFIG_DRIVER_WEXT
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
+CONFIG_WIRELESS_EXTENSION=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
+endif
+
+ifdef CONFIG_DRIVER_NDIS
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS
+DRV_WPA_OBJS += src/drivers/driver_ndis.c
+ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+DRV_WPA_OBJS += src/drivers/driver_ndis_.c
+endif
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=pcap
+endif
+CONFIG_WINPCAP=y
+ifdef CONFIG_USE_NDISUIO
+DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO
+endif
+endif
+
+ifdef CONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_OBJS += src/drivers/driver_roboswitch.c
+endif
+
+ifdef CONFIG_WIRELESS_EXTENSION
+DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+DRV_WPA_OBJS += src/drivers/driver_wext.c
+NEED_RFKILL=y
+endif
+
+ifdef NEED_NETLINK
+DRV_OBJS += src/drivers/netlink.c
+endif
+
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += src/drivers/linux_ioctl.c
+endif
+
+ifdef NEED_RFKILL
+DRV_OBJS += src/drivers/rfkill.c
+endif
+
+ifdef CONFIG_DRIVER_CUSTOM
+DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM
+endif
+
+ifdef CONFIG_VLAN_NETLINK
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+ifdef CONFIG_LIBNL32
+  DRV_LIBS += -lnl-3
+  DRV_LIBS += -lnl-genl-3
+  DRV_LIBS += -lnl-route-3
+  DRV_CFLAGS += -DCONFIG_LIBNL20
+else
+  ifdef CONFIG_LIBNL_TINY
+    DRV_LIBS += -lnl-tiny
+  else
+    DRV_LIBS += -lnl
+  endif
+
+  ifdef CONFIG_LIBNL20
+    DRV_LIBS += -lnl-genl
+    DRV_LIBS += -lnl-route
+    DRV_CFLAGS += -DCONFIG_LIBNL20
+  endif
+endif
+endif
+endif
+
+##### COMMON VARS
+DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
+DRV_WPA_CFLAGS += $(DRV_CFLAGS)
+DRV_AP_CFLAGS += $(DRV_CFLAGS)
+
+DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS)
+DRV_WPA_LIBS += $(DRV_LIBS)
+DRV_AP_LIBS += $(DRV_LIBS)
+
+DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS)
+DRV_WPA_OBJS += $(DRV_OBJS)
+DRV_AP_OBJS += $(DRV_OBJS)
+
+DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS)
+DRV_WPA_LDFLAGS += $(DRV_LDFLAGS)
+DRV_AP_LDFLAGS += $(DRV_LDFLAGS)
diff --git a/hostap/src/drivers/linux_defines.h b/hostap/src/drivers/linux_defines.h
new file mode 100644
index 0000000..a107479
--- /dev/null
+++ b/hostap/src/drivers/linux_defines.h
@@ -0,0 +1,46 @@
+/*
+ * Linux defines for values that are not yet included in common C libraries
+ * 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.
+ */
+
+#ifndef LINUX_DEFINES_H
+#define LINUX_DEFINES_H
+
+#ifndef SO_WIFI_STATUS
+# if defined(__sparc__)
+#  define SO_WIFI_STATUS	0x0025
+# elif defined(__parisc__)
+#  define SO_WIFI_STATUS	0x4022
+# else
+#  define SO_WIFI_STATUS	41
+# endif
+
+# define SCM_WIFI_STATUS	SO_WIFI_STATUS
+#endif
+
+#ifndef SO_EE_ORIGIN_TXSTATUS
+#define SO_EE_ORIGIN_TXSTATUS	4
+#endif
+
+#ifndef PACKET_TX_TIMESTAMP
+#define PACKET_TX_TIMESTAMP	16
+#endif
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP   0x10000         /* driver signals L1 up         */
+#endif
+#ifndef IFF_DORMANT
+#define IFF_DORMANT    0x20000         /* driver signals dormant       */
+#endif
+
+#ifndef IF_OPER_DORMANT
+#define IF_OPER_DORMANT 5
+#endif
+#ifndef IF_OPER_UP
+#define IF_OPER_UP 6
+#endif
+
+#endif /* LINUX_DEFINES_H */
diff --git a/hostap/src/drivers/linux_ioctl.c b/hostap/src/drivers/linux_ioctl.c
new file mode 100644
index 0000000..e21147a
--- /dev/null
+++ b/hostap/src/drivers/linux_ioctl.c
@@ -0,0 +1,244 @@
+/*
+ * Linux ioctl helper functions for driver wrappers
+ * Copyright (c) 2002-2010, 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 <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include "utils/common.h"
+#include "linux_ioctl.h"
+
+
+int linux_set_iface_flags(int sock, const char *ifname, int dev_up)
+{
+	struct ifreq ifr;
+	int ret;
+
+	if (sock < 0)
+		return -1;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+	if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
+		ret = errno ? -errno : -999;
+		wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s",
+			   ifname, strerror(errno));
+		return ret;
+	}
+
+	if (dev_up) {
+		if (ifr.ifr_flags & IFF_UP)
+			return 0;
+		ifr.ifr_flags |= IFF_UP;
+	} else {
+		if (!(ifr.ifr_flags & IFF_UP))
+			return 0;
+		ifr.ifr_flags &= ~IFF_UP;
+	}
+
+	if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) {
+		ret = errno ? -errno : -999;
+		wpa_printf(MSG_ERROR, "Could not set interface %s flags (%s): "
+			   "%s",
+			   ifname, dev_up ? "UP" : "DOWN", strerror(errno));
+		return ret;
+	}
+
+	return 0;
+}
+
+
+int linux_iface_up(int sock, const char *ifname)
+{
+	struct ifreq ifr;
+	int ret;
+
+	if (sock < 0)
+		return -1;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+	if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
+		ret = errno ? -errno : -999;
+		wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s",
+			   ifname, strerror(errno));
+		return ret;
+	}
+
+	return !!(ifr.ifr_flags & IFF_UP);
+}
+
+
+int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr)
+{
+	struct ifreq ifr;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+	if (ioctl(sock, SIOCGIFHWADDR, &ifr)) {
+		wpa_printf(MSG_ERROR, "Could not get interface %s hwaddr: %s",
+			   ifname, strerror(errno));
+		return -1;
+	}
+
+	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+		wpa_printf(MSG_ERROR, "%s: Invalid HW-addr family 0x%04x",
+			   ifname, ifr.ifr_hwaddr.sa_family);
+		return -1;
+	}
+	os_memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+	return 0;
+}
+
+
+int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr)
+{
+	struct ifreq ifr;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+	os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
+	ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
+
+	if (ioctl(sock, SIOCSIFHWADDR, &ifr)) {
+		wpa_printf(MSG_DEBUG, "Could not set interface %s hwaddr: %s",
+			   ifname, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+#ifndef SIOCBRADDBR
+#define SIOCBRADDBR 0x89a0
+#endif
+#ifndef SIOCBRDELBR
+#define SIOCBRDELBR 0x89a1
+#endif
+#ifndef SIOCBRADDIF
+#define SIOCBRADDIF 0x89a2
+#endif
+#ifndef SIOCBRDELIF
+#define SIOCBRDELIF 0x89a3
+#endif
+
+
+int linux_br_add(int sock, const char *brname)
+{
+	if (ioctl(sock, SIOCBRADDBR, brname) < 0) {
+		wpa_printf(MSG_DEBUG, "Could not add bridge %s: %s",
+			   brname, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int linux_br_del(int sock, const char *brname)
+{
+	if (ioctl(sock, SIOCBRDELBR, brname) < 0) {
+		wpa_printf(MSG_DEBUG, "Could not remove bridge %s: %s",
+			   brname, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int linux_br_add_if(int sock, const char *brname, const char *ifname)
+{
+	struct ifreq ifr;
+	int ifindex;
+
+	ifindex = if_nametoindex(ifname);
+	if (ifindex == 0)
+		return -1;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ);
+	ifr.ifr_ifindex = ifindex;
+	if (ioctl(sock, SIOCBRADDIF, &ifr) < 0) {
+		wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
+			   "%s: %s", ifname, brname, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int linux_br_del_if(int sock, const char *brname, const char *ifname)
+{
+	struct ifreq ifr;
+	int ifindex;
+
+	ifindex = if_nametoindex(ifname);
+	if (ifindex == 0)
+		return -1;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ);
+	ifr.ifr_ifindex = ifindex;
+	if (ioctl(sock, SIOCBRDELIF, &ifr) < 0) {
+		wpa_printf(MSG_DEBUG, "Could not remove interface %s from "
+			   "bridge %s: %s", ifname, brname, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+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);
+	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;
+	pos++;
+	os_strlcpy(brname, pos, IFNAMSIZ);
+	return 0;
+}
+
+
+int linux_master_get(char *master_ifname, const char *ifname)
+{
+	char buf[128], masterlink[128], *pos;
+	ssize_t res;
+
+	/* check whether there is a master */
+	os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/master", ifname);
+
+	res = readlink(buf, masterlink, sizeof(masterlink));
+	if (res < 0 || (size_t) res >= sizeof(masterlink))
+		return -1;
+
+	masterlink[res] = '\0';
+
+	pos = os_strrchr(masterlink, '/');
+	if (pos == NULL)
+		return -1;
+	pos++;
+	os_strlcpy(master_ifname, pos, IFNAMSIZ);
+	return 0;
+}
diff --git a/hostap/src/drivers/linux_ioctl.h b/hostap/src/drivers/linux_ioctl.h
new file mode 100644
index 0000000..6de4d9b
--- /dev/null
+++ b/hostap/src/drivers/linux_ioctl.h
@@ -0,0 +1,23 @@
+/*
+ * Linux ioctl helper functions for driver wrappers
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LINUX_IOCTL_H
+#define LINUX_IOCTL_H
+
+int linux_set_iface_flags(int sock, const char *ifname, int dev_up);
+int linux_iface_up(int sock, const char *ifname);
+int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr);
+int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr);
+int linux_br_add(int sock, const char *brname);
+int linux_br_del(int sock, const char *brname);
+int linux_br_add_if(int sock, const char *brname, const char *ifname);
+int linux_br_del_if(int sock, const char *brname, const char *ifname);
+int linux_br_get(char *brname, const char *ifname);
+int linux_master_get(char *master_ifname, const char *ifname);
+
+#endif /* LINUX_IOCTL_H */
diff --git a/hostap/src/drivers/linux_wext.h b/hostap/src/drivers/linux_wext.h
new file mode 100644
index 0000000..e7c7001
--- /dev/null
+++ b/hostap/src/drivers/linux_wext.h
@@ -0,0 +1,45 @@
+/*
+ * Driver interaction with generic Linux Wireless Extensions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LINUX_WEXT_H
+#define LINUX_WEXT_H
+
+#ifndef ANDROID
+
+/*
+ * Avoid including other kernel header to avoid conflicts with C library
+ * headers.
+ */
+#define _LINUX_TYPES_H
+#define _LINUX_SOCKET_H
+#define _LINUX_IF_H
+
+#include <stdint.h>
+#include <net/if.h>
+typedef uint32_t __u32;
+typedef int32_t __s32;
+typedef uint16_t __u16;
+typedef int16_t __s16;
+typedef uint8_t __u8;
+#ifndef __user
+#define __user
+#endif /* __user */
+
+#endif /* ANDROID */
+
+#include <linux/wireless.h>
+
+#ifndef IW_ENCODE_ALG_PMK
+#define IW_ENCODE_ALG_PMK 4
+#endif
+
+#ifndef IW_ENC_CAPA_4WAY_HANDSHAKE
+#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010
+#endif
+
+#endif /* LINUX_WEXT_H */
diff --git a/hostap/src/drivers/ndis_events.c b/hostap/src/drivers/ndis_events.c
new file mode 100644
index 0000000..93673a3
--- /dev/null
+++ b/hostap/src/drivers/ndis_events.c
@@ -0,0 +1,802 @@
+/*
+ * ndis_events - Receive NdisMIndicateStatus() events using WMI
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#define _WIN32_WINNT    0x0400
+
+#include "includes.h"
+
+#ifndef COBJMACROS
+#define COBJMACROS
+#endif /* COBJMACROS */
+#include <wbemidl.h>
+
+#include "common.h"
+
+
+static int wmi_refcnt = 0;
+static int wmi_first = 1;
+
+struct ndis_events_data {
+	IWbemObjectSink sink;
+	IWbemObjectSinkVtbl sink_vtbl;
+
+	IWbemServices *pSvc;
+	IWbemLocator *pLoc;
+
+	HANDLE read_pipe, write_pipe, event_avail;
+	UINT ref;
+	int terminating;
+	char *ifname; /* {GUID..} */
+	WCHAR *adapter_desc;
+};
+
+#define BstrAlloc(x) (x) ? SysAllocString(x) : NULL
+#define BstrFree(x) if (x) SysFreeString(x)
+
+/* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to
+ * BSTRs */
+HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery(
+	IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
+	long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum)
+{
+	BSTR bsQueryLanguage, bsQuery;
+	HRESULT hr;
+
+	bsQueryLanguage = BstrAlloc(strQueryLanguage);
+	bsQuery = BstrAlloc(strQuery);
+
+	hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags,
+				     pCtx, ppEnum);
+
+	BstrFree(bsQueryLanguage);
+	BstrFree(bsQuery);
+
+	return hr;
+}
+
+
+HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync(
+	IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
+	long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
+{
+	BSTR bsQueryLanguage, bsQuery;
+	HRESULT hr;
+
+	bsQueryLanguage = BstrAlloc(strQueryLanguage);
+	bsQuery = BstrAlloc(strQuery);
+
+	hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage,
+						      bsQuery, lFlags, pCtx,
+						      pResponseHandler);
+
+	BstrFree(bsQueryLanguage);
+	BstrFree(bsQuery);
+
+	return hr;
+}
+
+
+HRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer(
+	IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser,
+	LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags,
+	LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace)
+{
+	BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority;
+	HRESULT hr;
+
+	bsNetworkResource = BstrAlloc(strNetworkResource);
+	bsUser = BstrAlloc(strUser);
+	bsPassword = BstrAlloc(strPassword);
+	bsLocale = BstrAlloc(strLocale);
+	bsAuthority = BstrAlloc(strAuthority);
+
+	hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser,
+					bsPassword, bsLocale, lSecurityFlags,
+					bsAuthority, pCtx, ppNamespace);
+
+	BstrFree(bsNetworkResource);
+	BstrFree(bsUser);
+	BstrFree(bsPassword);
+	BstrFree(bsLocale);
+	BstrFree(bsAuthority);
+
+	return hr;
+}
+
+
+enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
+		   EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
+
+static int ndis_events_get_adapter(struct ndis_events_data *events,
+				   const char *ifname, const char *desc);
+
+
+static int ndis_events_constructor(struct ndis_events_data *events)
+{
+	events->ref = 1;
+
+	if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) {
+		wpa_printf(MSG_ERROR, "CreatePipe() failed: %d",
+			   (int) GetLastError());
+		return -1;
+	}
+	events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if (events->event_avail == NULL) {
+		wpa_printf(MSG_ERROR, "CreateEvent() failed: %d",
+			   (int) GetLastError());
+		CloseHandle(events->read_pipe);
+		CloseHandle(events->write_pipe);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void ndis_events_destructor(struct ndis_events_data *events)
+{
+	CloseHandle(events->read_pipe);
+	CloseHandle(events->write_pipe);
+	CloseHandle(events->event_avail);
+	IWbemServices_Release(events->pSvc);
+	IWbemLocator_Release(events->pLoc);
+	if (--wmi_refcnt == 0)
+		CoUninitialize();
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj)
+{
+	*obj = NULL;
+
+	if (IsEqualIID(riid, &IID_IUnknown) ||
+	    IsEqualIID(riid, &IID_IWbemObjectSink)) {
+		*obj = this;
+		IWbemObjectSink_AddRef(this);
+		return NOERROR;
+	}
+
+	return E_NOINTERFACE;
+}
+
+
+static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this)
+{
+	struct ndis_events_data *events = (struct ndis_events_data *) this;
+	return ++events->ref;
+}
+
+
+static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this)
+{
+	struct ndis_events_data *events = (struct ndis_events_data *) this;
+
+	if (--events->ref != 0)
+		return events->ref;
+
+	ndis_events_destructor(events);
+	wpa_printf(MSG_DEBUG, "ndis_events: terminated");
+	os_free(events->adapter_desc);
+	os_free(events->ifname);
+	os_free(events);
+	return 0;
+}
+
+
+static int ndis_events_send_event(struct ndis_events_data *events,
+				  enum event_types type,
+				  char *data, size_t data_len)
+{
+	char buf[512], *pos, *end;
+	int _type;
+	DWORD written;
+
+	end = buf + sizeof(buf);
+	_type = (int) type;
+	os_memcpy(buf, &_type, sizeof(_type));
+	pos = buf + sizeof(_type);
+
+	if (data) {
+		if (2 + data_len > (size_t) (end - pos)) {
+			wpa_printf(MSG_DEBUG, "Not enough room for send_event "
+				   "data (%d)", data_len);
+			return -1;
+		}
+		*pos++ = data_len >> 8;
+		*pos++ = data_len & 0xff;
+		os_memcpy(pos, data, data_len);
+		pos += data_len;
+	}
+
+	if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) {
+		SetEvent(events->event_avail);
+		return 0;
+	}
+	wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError());
+	return -1;
+}
+
+
+static void ndis_events_media_connect(struct ndis_events_data *events)
+{
+	wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect");
+	ndis_events_send_event(events, EVENT_CONNECT, NULL, 0);
+}
+
+
+static void ndis_events_media_disconnect(struct ndis_events_data *events)
+{
+	wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect");
+	ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0);
+}
+
+
+static void ndis_events_media_specific(struct ndis_events_data *events,
+				       IWbemClassObject *pObj)
+{
+	VARIANT vt;
+	HRESULT hr;
+	LONG lower, upper, k;
+	UCHAR ch;
+	char *data, *pos;
+	size_t data_len;
+
+	wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication");
+
+	/* This is the StatusBuffer from NdisMIndicateStatus() call */
+	hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication",
+				  0, &vt, NULL, NULL);
+	if (FAILED(hr)) {
+		wpa_printf(MSG_DEBUG, "Could not get "
+			   "NdisStatusMediaSpecificIndication from "
+			   "the object?!");
+		return;
+	}
+
+	SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower);
+	SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper);
+	data_len = upper - lower + 1;
+	data = os_malloc(data_len);
+	if (data == NULL) {
+		wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event "
+			   "data");
+		VariantClear(&vt);
+		return;
+	}
+
+	pos = data;
+	for (k = lower; k <= upper; k++) {
+		SafeArrayGetElement(V_ARRAY(&vt), &k, &ch);
+		*pos++ = ch;
+	}
+	wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len);
+
+	VariantClear(&vt);
+
+	ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len);
+
+	os_free(data);
+}
+
+
+static void ndis_events_adapter_arrival(struct ndis_events_data *events)
+{
+	wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival");
+	ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0);
+}
+
+
+static void ndis_events_adapter_removal(struct ndis_events_data *events)
+{
+	wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval");
+	ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0);
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_indicate(IWbemObjectSink *this, long lObjectCount,
+		     IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray)
+{
+	struct ndis_events_data *events = (struct ndis_events_data *) this;
+	long i;
+
+	if (events->terminating) {
+		wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
+			   "indication - terminating");
+		return WBEM_NO_ERROR;
+	}
+	/* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)",
+	   lObjectCount); */
+
+	for (i = 0; i < lObjectCount; i++) {
+		IWbemClassObject *pObj = ppObjArray[i];
+		HRESULT hr;
+		VARIANT vtClass, vt;
+
+		hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL,
+					  NULL);
+		if (FAILED(hr)) {
+			wpa_printf(MSG_DEBUG, "Failed to get __CLASS from "
+				   "event.");
+			break;
+		}
+		/* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */
+
+		hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL,
+					  NULL);
+		if (FAILED(hr)) {
+			wpa_printf(MSG_DEBUG, "Failed to get InstanceName "
+				   "from event.");
+			VariantClear(&vtClass);
+			break;
+		}
+
+		if (wcscmp(vtClass.bstrVal,
+			   L"MSNdis_NotifyAdapterArrival") == 0) {
+			wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to "
+				   "update adapter description since it may "
+				   "have changed with new adapter instance");
+			ndis_events_get_adapter(events, events->ifname, NULL);
+		}
+
+		if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) {
+			wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
+				   "indication for foreign adapter: "
+				   "InstanceName: '%S' __CLASS: '%S'",
+				   vt.bstrVal, vtClass.bstrVal);
+			VariantClear(&vtClass);
+			VariantClear(&vt);
+			continue;
+		}
+		VariantClear(&vt);
+
+		if (wcscmp(vtClass.bstrVal,
+			   L"MSNdis_StatusMediaSpecificIndication") == 0) {
+			ndis_events_media_specific(events, pObj);
+		} else if (wcscmp(vtClass.bstrVal,
+				  L"MSNdis_StatusMediaConnect") == 0) {
+			ndis_events_media_connect(events);
+		} else if (wcscmp(vtClass.bstrVal,
+				  L"MSNdis_StatusMediaDisconnect") == 0) {
+			ndis_events_media_disconnect(events);
+		} else if (wcscmp(vtClass.bstrVal,
+				  L"MSNdis_NotifyAdapterArrival") == 0) {
+			ndis_events_adapter_arrival(events);
+		} else if (wcscmp(vtClass.bstrVal,
+				  L"MSNdis_NotifyAdapterRemoval") == 0) {
+			ndis_events_adapter_removal(events);
+		} else {
+			wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: "
+				   "'%S'", vtClass.bstrVal);
+		}
+
+		VariantClear(&vtClass);
+	}
+
+	return WBEM_NO_ERROR;
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
+		       BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
+{
+	return WBEM_NO_ERROR;
+}
+
+
+static int notification_query(IWbemObjectSink *pDestSink,
+			      IWbemServices *pSvc, const char *class_name)
+{
+	HRESULT hr;
+	WCHAR query[256];
+
+	_snwprintf(query, 256,
+		  L"SELECT * FROM %S", class_name);
+	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+	hr = call_IWbemServices_ExecNotificationQueryAsync(
+		pSvc, L"WQL", query, 0, 0, pDestSink);
+	if (FAILED(hr)) {
+		wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
+			   "failed with hresult of 0x%x",
+			   class_name, (int) hr);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int register_async_notification(IWbemObjectSink *pDestSink,
+				       IWbemServices *pSvc)
+{
+	int i;
+	const char *class_list[] = {
+		"MSNdis_StatusMediaConnect",
+		"MSNdis_StatusMediaDisconnect",
+		"MSNdis_StatusMediaSpecificIndication",
+		"MSNdis_NotifyAdapterArrival",
+		"MSNdis_NotifyAdapterRemoval",
+		NULL
+	};
+
+	for (i = 0; class_list[i]; i++) {
+		if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+void ndis_events_deinit(struct ndis_events_data *events)
+{
+	events->terminating = 1;
+	IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
+	IWbemObjectSink_Release(&events->sink);
+	/*
+	 * Rest of deinitialization is done in ndis_events_destructor() once
+	 * all reference count drops to zero.
+	 */
+}
+
+
+static int ndis_events_use_desc(struct ndis_events_data *events,
+				const char *desc)
+{
+	char *tmp, *pos;
+	size_t len;
+
+	if (desc == NULL) {
+		if (events->adapter_desc == NULL)
+			return -1;
+		/* Continue using old description */
+		return 0;
+	}
+
+	tmp = os_strdup(desc);
+	if (tmp == NULL)
+		return -1;
+
+	pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
+	if (pos)
+		*pos = '\0';
+
+	len = os_strlen(tmp);
+	events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR));
+	if (events->adapter_desc == NULL) {
+		os_free(tmp);
+		return -1;
+	}
+	_snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
+	os_free(tmp);
+	return 0;
+}
+
+
+static int ndis_events_get_adapter(struct ndis_events_data *events,
+				   const char *ifname, const char *desc)
+{
+	HRESULT hr;
+	IWbemServices *pSvc;
+#define MAX_QUERY_LEN 256
+	WCHAR query[MAX_QUERY_LEN];
+	IEnumWbemClassObject *pEnumerator;
+	IWbemClassObject *pObj;
+	ULONG uReturned;
+	VARIANT vt;
+	int len, pos;
+
+	/*
+	 * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter
+	 * to have better probability of matching with InstanceName from
+	 * MSNdis events. If this fails, use the provided description.
+	 */
+
+	os_free(events->adapter_desc);
+	events->adapter_desc = NULL;
+
+	hr = call_IWbemLocator_ConnectServer(
+		events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc);
+	if (FAILED(hr)) {
+		wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI "
+			   "server (ROOT\\CIMV2) - error 0x%x", (int) hr);
+		return ndis_events_use_desc(events, desc);
+	}
+	wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
+
+	_snwprintf(query, MAX_QUERY_LEN,
+		  L"SELECT Index FROM Win32_NetworkAdapterConfiguration "
+		  L"WHERE SettingID='%S'", ifname);
+	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+
+	hr = call_IWbemServices_ExecQuery(
+		pSvc, L"WQL", query,
+		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+		NULL, &pEnumerator);
+	if (!SUCCEEDED(hr)) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+			   "GUID from Win32_NetworkAdapterConfiguration: "
+			   "0x%x", (int) hr);
+		IWbemServices_Release(pSvc);
+		return ndis_events_use_desc(events, desc);
+	}
+
+	uReturned = 0;
+	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+				       &pObj, &uReturned);
+	if (!SUCCEEDED(hr) || uReturned == 0) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+			   "GUID from Win32_NetworkAdapterConfiguration: "
+			   "0x%x", (int) hr);
+		IEnumWbemClassObject_Release(pEnumerator);
+		IWbemServices_Release(pSvc);
+		return ndis_events_use_desc(events, desc);
+	}
+	IEnumWbemClassObject_Release(pEnumerator);
+
+	VariantInit(&vt);
+	hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL);
+	if (!SUCCEEDED(hr)) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from "
+			   "Win32_NetworkAdapterConfiguration: 0x%x",
+			   (int) hr);
+		IWbemServices_Release(pSvc);
+		return ndis_events_use_desc(events, desc);
+	}
+
+	_snwprintf(query, MAX_QUERY_LEN,
+		  L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE "
+		  L"Index=%d",
+		  vt.uintVal);
+	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+	VariantClear(&vt);
+	IWbemClassObject_Release(pObj);
+
+	hr = call_IWbemServices_ExecQuery(
+		pSvc, L"WQL", query,
+		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+		NULL, &pEnumerator);
+	if (!SUCCEEDED(hr)) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
+		IWbemServices_Release(pSvc);
+		return ndis_events_use_desc(events, desc);
+	}
+
+	uReturned = 0;
+	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+				       &pObj, &uReturned);
+	if (!SUCCEEDED(hr) || uReturned == 0) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
+		IEnumWbemClassObject_Release(pEnumerator);
+		IWbemServices_Release(pSvc);
+		return ndis_events_use_desc(events, desc);
+	}
+	IEnumWbemClassObject_Release(pEnumerator);
+
+	hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
+	if (!SUCCEEDED(hr)) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
+			   "Win32_NetworkAdapter: 0x%x", (int) hr);
+		IWbemClassObject_Release(pObj);
+		IWbemServices_Release(pSvc);
+		return ndis_events_use_desc(events, desc);
+	}
+
+	wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
+		   vt.bstrVal);
+	events->adapter_desc = _wcsdup(vt.bstrVal);
+	VariantClear(&vt);
+
+	/*
+	 * Try to get even better candidate for matching with InstanceName
+	 * from Win32_PnPEntity. This is needed at least for some USB cards
+	 * that can change the InstanceName whenever being unplugged and
+	 * plugged again.
+	 */
+
+	hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL);
+	if (!SUCCEEDED(hr)) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID "
+			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
+		IWbemClassObject_Release(pObj);
+		IWbemServices_Release(pSvc);
+		if (events->adapter_desc == NULL)
+			return ndis_events_use_desc(events, desc);
+		return 0; /* use Win32_NetworkAdapter::Name */
+	}
+
+	wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
+		   "'%S'", vt.bstrVal);
+
+	len = _snwprintf(query, MAX_QUERY_LEN,
+			L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='");
+	if (len < 0 || len >= MAX_QUERY_LEN - 1) {
+		VariantClear(&vt);
+		IWbemClassObject_Release(pObj);
+		IWbemServices_Release(pSvc);
+		if (events->adapter_desc == NULL)
+			return ndis_events_use_desc(events, desc);
+		return 0; /* use Win32_NetworkAdapter::Name */
+	}
+
+	/* Escape \ as \\ */
+	for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) {
+		if (vt.bstrVal[pos] == '\\') {
+			if (len >= MAX_QUERY_LEN - 3)
+				break;
+			query[len++] = '\\';
+		}
+		query[len++] = vt.bstrVal[pos];
+	}
+	query[len++] = L'\'';
+	query[len] = L'\0';
+	VariantClear(&vt);
+	IWbemClassObject_Release(pObj);
+	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+
+	hr = call_IWbemServices_ExecQuery(
+		pSvc, L"WQL", query,
+		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+		NULL, &pEnumerator);
+	if (!SUCCEEDED(hr)) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+			   "Name from Win32_PnPEntity: 0x%x", (int) hr);
+		IWbemServices_Release(pSvc);
+		if (events->adapter_desc == NULL)
+			return ndis_events_use_desc(events, desc);
+		return 0; /* use Win32_NetworkAdapter::Name */
+	}
+
+	uReturned = 0;
+	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+				       &pObj, &uReturned);
+	if (!SUCCEEDED(hr) || uReturned == 0) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+			   "from Win32_PnPEntity: 0x%x", (int) hr);
+		IEnumWbemClassObject_Release(pEnumerator);
+		IWbemServices_Release(pSvc);
+		if (events->adapter_desc == NULL)
+			return ndis_events_use_desc(events, desc);
+		return 0; /* use Win32_NetworkAdapter::Name */
+	}
+	IEnumWbemClassObject_Release(pEnumerator);
+
+	hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
+	if (!SUCCEEDED(hr)) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
+			   "Win32_PnPEntity: 0x%x", (int) hr);
+		IWbemClassObject_Release(pObj);
+		IWbemServices_Release(pSvc);
+		if (events->adapter_desc == NULL)
+			return ndis_events_use_desc(events, desc);
+		return 0; /* use Win32_NetworkAdapter::Name */
+	}
+
+	wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'",
+		   vt.bstrVal);
+	os_free(events->adapter_desc);
+	events->adapter_desc = _wcsdup(vt.bstrVal);
+	VariantClear(&vt);
+
+	IWbemClassObject_Release(pObj);
+
+	IWbemServices_Release(pSvc);
+
+	if (events->adapter_desc == NULL)
+		return ndis_events_use_desc(events, desc);
+
+	return 0;
+}
+
+
+struct ndis_events_data *
+ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
+		 const char *ifname, const char *desc)
+{
+	HRESULT hr;
+	IWbemObjectSink *pSink;
+	struct ndis_events_data *events;
+
+	events = os_zalloc(sizeof(*events));
+	if (events == NULL) {
+		wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
+		return NULL;
+	}
+	events->ifname = os_strdup(ifname);
+	if (events->ifname == NULL) {
+		os_free(events);
+		return NULL;
+	}
+
+	if (wmi_refcnt++ == 0) {
+		hr = CoInitializeEx(0, COINIT_MULTITHREADED);
+		if (FAILED(hr)) {
+			wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
+				   "returned 0x%x", (int) hr);
+			os_free(events);
+			return NULL;
+		}
+	}
+
+	if (wmi_first) {
+		/* CoInitializeSecurity() must be called once and only once
+		 * per process, so let's use wmi_first flag to protect against
+		 * multiple calls. */
+		wmi_first = 0;
+
+		hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
+					  RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
+					  RPC_C_IMP_LEVEL_IMPERSONATE,
+					  NULL, EOAC_SECURE_REFS, NULL);
+		if (FAILED(hr)) {
+			wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
+				   "- returned 0x%x", (int) hr);
+			os_free(events);
+			return NULL;
+		}
+	}
+
+	hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
+			      &IID_IWbemLocator,
+			      (LPVOID *) (void *) &events->pLoc);
+	if (FAILED(hr)) {
+		wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
+			   "0x%x", (int) hr);
+		CoUninitialize();
+		os_free(events);
+		return NULL;
+	}
+
+	if (ndis_events_get_adapter(events, ifname, desc) < 0) {
+		CoUninitialize();
+		os_free(events);
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
+		   events->adapter_desc);
+
+	hr = call_IWbemLocator_ConnectServer(
+		events->pLoc, L"ROOT\\WMI", NULL, NULL,
+		0, 0, 0, 0, &events->pSvc);
+	if (FAILED(hr)) {
+		wpa_printf(MSG_ERROR, "Could not connect to server - error "
+			   "0x%x", (int) hr);
+		CoUninitialize();
+		os_free(events->adapter_desc);
+		os_free(events);
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
+
+	ndis_events_constructor(events);
+	pSink = &events->sink;
+	pSink->lpVtbl = &events->sink_vtbl;
+	events->sink_vtbl.QueryInterface = ndis_events_query_interface;
+	events->sink_vtbl.AddRef = ndis_events_add_ref;
+	events->sink_vtbl.Release = ndis_events_release;
+	events->sink_vtbl.Indicate = ndis_events_indicate;
+	events->sink_vtbl.SetStatus = ndis_events_set_status;
+
+	if (register_async_notification(pSink, events->pSvc) < 0) {
+		wpa_printf(MSG_DEBUG, "Failed to register async "
+			   "notifications");
+		ndis_events_destructor(events);
+		os_free(events->adapter_desc);
+		os_free(events);
+		return NULL;
+	}
+
+	*read_pipe = events->read_pipe;
+	*event_avail = events->event_avail;
+
+	return events;
+}
diff --git a/hostap/src/drivers/netlink.c b/hostap/src/drivers/netlink.c
new file mode 100644
index 0000000..0e960f4
--- /dev/null
+++ b/hostap/src/drivers/netlink.c
@@ -0,0 +1,226 @@
+/*
+ * Netlink helper functions for driver wrappers
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "priv_netlink.h"
+#include "netlink.h"
+
+
+struct netlink_data {
+	struct netlink_config *cfg;
+	int sock;
+};
+
+
+static void netlink_receive_link(struct netlink_data *netlink,
+				 void (*cb)(void *ctx, struct ifinfomsg *ifi,
+					    u8 *buf, size_t len),
+				 struct nlmsghdr *h)
+{
+	if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg))
+		return;
+	cb(netlink->cfg->ctx, NLMSG_DATA(h),
+	   (u8 *) NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)),
+	   NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg)));
+}
+
+
+static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct netlink_data *netlink = eloop_ctx;
+	char buf[8192];
+	int left;
+	struct sockaddr_nl from;
+	socklen_t fromlen;
+	struct nlmsghdr *h;
+	int max_events = 10;
+
+try_again:
+	fromlen = sizeof(from);
+	left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
+			(struct sockaddr *) &from, &fromlen);
+	if (left < 0) {
+		if (errno != EINTR && errno != EAGAIN)
+			wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s",
+				   strerror(errno));
+		return;
+	}
+
+	h = (struct nlmsghdr *) buf;
+	while (NLMSG_OK(h, left)) {
+		switch (h->nlmsg_type) {
+		case RTM_NEWLINK:
+			netlink_receive_link(netlink, netlink->cfg->newlink_cb,
+					     h);
+			break;
+		case RTM_DELLINK:
+			netlink_receive_link(netlink, netlink->cfg->dellink_cb,
+					     h);
+			break;
+		}
+
+		h = NLMSG_NEXT(h, left);
+	}
+
+	if (left > 0) {
+		wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of "
+			   "netlink message", left);
+	}
+
+	if (--max_events > 0) {
+		/*
+		 * Try to receive all events in one eloop call in order to
+		 * limit race condition on cases where AssocInfo event, Assoc
+		 * event, and EAPOL frames are received more or less at the
+		 * same time. We want to process the event messages first
+		 * before starting EAPOL processing.
+		 */
+		goto try_again;
+	}
+}
+
+
+struct netlink_data * netlink_init(struct netlink_config *cfg)
+{
+	struct netlink_data *netlink;
+	struct sockaddr_nl local;
+
+	netlink = os_zalloc(sizeof(*netlink));
+	if (netlink == NULL)
+		return NULL;
+
+	netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (netlink->sock < 0) {
+		wpa_printf(MSG_ERROR, "netlink: Failed to open netlink "
+			   "socket: %s", strerror(errno));
+		netlink_deinit(netlink);
+		return NULL;
+	}
+
+	os_memset(&local, 0, sizeof(local));
+	local.nl_family = AF_NETLINK;
+	local.nl_groups = RTMGRP_LINK;
+	if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0)
+	{
+		wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink "
+			   "socket: %s", strerror(errno));
+		netlink_deinit(netlink);
+		return NULL;
+	}
+
+	eloop_register_read_sock(netlink->sock, netlink_receive, netlink,
+				 NULL);
+
+	netlink->cfg = cfg;
+
+	return netlink;
+}
+
+
+void netlink_deinit(struct netlink_data *netlink)
+{
+	if (netlink == NULL)
+		return;
+	if (netlink->sock >= 0) {
+		eloop_unregister_read_sock(netlink->sock);
+		close(netlink->sock);
+	}
+	os_free(netlink->cfg);
+	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)
+{
+	struct {
+		struct nlmsghdr hdr;
+		struct ifinfomsg ifinfo;
+		char opts[16];
+	} req;
+	struct rtattr *rta;
+	static int nl_seq;
+	ssize_t ret;
+
+	os_memset(&req, 0, sizeof(req));
+
+	req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.hdr.nlmsg_type = RTM_SETLINK;
+	req.hdr.nlmsg_flags = NLM_F_REQUEST;
+	req.hdr.nlmsg_seq = ++nl_seq;
+	req.hdr.nlmsg_pid = 0;
+
+	req.ifinfo.ifi_family = AF_UNSPEC;
+	req.ifinfo.ifi_type = 0;
+	req.ifinfo.ifi_index = ifindex;
+	req.ifinfo.ifi_flags = 0;
+	req.ifinfo.ifi_change = 0;
+
+	if (linkmode != -1) {
+		rta = aliasing_hide_typecast(
+			((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
+			struct rtattr);
+		rta->rta_type = IFLA_LINKMODE;
+		rta->rta_len = RTA_LENGTH(sizeof(char));
+		*((char *) RTA_DATA(rta)) = linkmode;
+		req.hdr.nlmsg_len += RTA_SPACE(sizeof(char));
+	}
+	if (operstate != -1) {
+		rta = aliasing_hide_typecast(
+			((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
+			struct rtattr);
+		rta->rta_type = IFLA_OPERSTATE;
+		rta->rta_len = RTA_LENGTH(sizeof(char));
+		*((char *) RTA_DATA(rta)) = operstate;
+		req.hdr.nlmsg_len += RTA_SPACE(sizeof(char));
+	}
+
+	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) {
+		wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA "
+			   "failed: %s (assume operstate is not supported)",
+			   strerror(errno));
+	}
+
+	return ret < 0 ? -1 : 0;
+}
diff --git a/hostap/src/drivers/netlink.h b/hostap/src/drivers/netlink.h
new file mode 100644
index 0000000..3a7340e
--- /dev/null
+++ b/hostap/src/drivers/netlink.h
@@ -0,0 +1,28 @@
+/*
+ * Netlink helper functions for driver wrappers
+ * 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 NETLINK_H
+#define NETLINK_H
+
+struct netlink_data;
+struct ifinfomsg;
+
+struct netlink_config {
+	void *ctx;
+	void (*newlink_cb)(void *ctx, struct ifinfomsg *ifi, u8 *buf,
+			   size_t len);
+	void (*dellink_cb)(void *ctx, struct ifinfomsg *ifi, u8 *buf,
+			   size_t len);
+};
+
+struct netlink_data * netlink_init(struct netlink_config *cfg);
+void netlink_deinit(struct netlink_data *netlink);
+int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
+			   int linkmode, int operstate);
+
+#endif /* NETLINK_H */
diff --git a/hostap/src/drivers/nl80211_copy.h b/hostap/src/drivers/nl80211_copy.h
new file mode 100644
index 0000000..c70a161
--- /dev/null
+++ b/hostap/src/drivers/nl80211_copy.h
@@ -0,0 +1,4601 @@
+#ifndef __LINUX_NL80211_H
+#define __LINUX_NL80211_H
+/*
+ * 802.11 netlink interface public header
+ *
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2008 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com>
+ * Copyright 2008 Michael Buesch <m@bues.ch>
+ * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
+ * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
+ * Copyright 2008 Colin McCabe <colin@cozybit.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/*
+ * This header file defines the userspace API to the wireless stack. Please
+ * be careful not to break things - i.e. don't move anything around or so
+ * unless you can demonstrate that it breaks neither API nor ABI.
+ *
+ * Additions to the API should be accompanied by actual implementations in
+ * an upstream driver, so that example implementations exist in case there
+ * are ever concerns about the precise semantics of the API or changes are
+ * needed, and to ensure that code for dead (no longer implemented) API
+ * can actually be identified and removed.
+ * Nonetheless, semantics should also be documented carefully in this file.
+ */
+
+#include <linux/types.h>
+
+#define NL80211_GENL_NAME "nl80211"
+
+#define NL80211_MULTICAST_GROUP_CONFIG		"config"
+#define NL80211_MULTICAST_GROUP_SCAN		"scan"
+#define NL80211_MULTICAST_GROUP_REG		"regulatory"
+#define NL80211_MULTICAST_GROUP_MLME		"mlme"
+#define NL80211_MULTICAST_GROUP_VENDOR		"vendor"
+#define NL80211_MULTICAST_GROUP_TESTMODE	"testmode"
+
+/**
+ * DOC: Station handling
+ *
+ * Stations are added per interface, but a special case exists with VLAN
+ * interfaces. When a station is bound to an AP interface, it may be moved
+ * into a VLAN identified by a VLAN interface index (%NL80211_ATTR_STA_VLAN).
+ * The station is still assumed to belong to the AP interface it was added
+ * to.
+ *
+ * Station handling varies per interface type and depending on the driver's
+ * capabilities.
+ *
+ * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS
+ * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows:
+ *  - a setup station entry is added, not yet authorized, without any rate
+ *    or capability information, this just exists to avoid race conditions
+ *  - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid
+ *    to add rate and capability information to the station and at the same
+ *    time mark it authorized.
+ *  - %NL80211_TDLS_ENABLE_LINK is then used
+ *  - after this, the only valid operation is to remove it by tearing down
+ *    the TDLS link (%NL80211_TDLS_DISABLE_LINK)
+ *
+ * TODO: need more info for other interface types
+ */
+
+/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * management entities such as wpa_supplicant react to management frames
+ * that are not being handled by the kernel. This includes, for example,
+ * certain classes of action frames that cannot be handled in the kernel
+ * for various reasons.
+ *
+ * Frame registration is done on a per-interface basis and registrations
+ * cannot be removed other than by closing the socket. It is possible to
+ * specify a registration filter to register, for example, only for a
+ * certain type of action frame. In particular with action frames, those
+ * that userspace registers for will not be returned as unhandled by the
+ * driver, so that the registered application has to take responsibility
+ * for doing that.
+ *
+ * The type of frame that can be registered for is also dependent on the
+ * driver and interface type. The frame types are advertised in wiphy
+ * attributes so applications know what to expect.
+ *
+ * NOTE: When an interface changes type while registrations are active,
+ *       these registrations are ignored until the interface type is
+ *       changed again. This means that changing the interface type can
+ *       lead to a situation that couldn't otherwise be produced, but
+ *       any such registrations will be dormant in the sense that they
+ *       will not be serviced, i.e. they will not receive any frames.
+ *
+ * Frame transmission allows userspace to send for example the required
+ * responses to action frames. It is subject to some sanity checking,
+ * but many frames can be transmitted. When a frame was transmitted, its
+ * status is indicated to the sending socket.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * DOC: Virtual interface / concurrency capabilities
+ *
+ * Some devices are able to operate with virtual MACs, they can have
+ * more than one virtual interface. The capability handling for this
+ * is a bit complex though, as there may be a number of restrictions
+ * on the types of concurrency that are supported.
+ *
+ * To start with, each device supports the interface types listed in
+ * the %NL80211_ATTR_SUPPORTED_IFTYPES attribute, but by listing the
+ * types there no concurrency is implied.
+ *
+ * Once concurrency is desired, more attributes must be observed:
+ * To start with, since some interface types are purely managed in
+ * software, like the AP-VLAN type in mac80211 for example, there's
+ * an additional list of these, they can be added at any time and
+ * are only restricted by some semantic restrictions (e.g. AP-VLAN
+ * cannot be added without a corresponding AP interface). This list
+ * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute.
+ *
+ * Further, the list of supported combinations is exported. This is
+ * in the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute. Basically,
+ * it exports a list of "groups", and at any point in time the
+ * interfaces that are currently active must fall into any one of
+ * the advertised groups. Within each group, there are restrictions
+ * on the number of interfaces of different types that are supported
+ * and also the number of different channels, along with potentially
+ * some other restrictions. See &enum nl80211_if_combination_attrs.
+ *
+ * All together, these attributes define the concurrency of virtual
+ * interfaces that a given device supports.
+ */
+
+/**
+ * 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
+ *
+ * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request
+ *	to get a list of all present wiphys.
+ * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or
+ *	%NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME,
+ *	%NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ (and the
+ *	attributes determining the channel width; this is used for setting
+ *	monitor mode channel),  %NL80211_ATTR_WIPHY_RETRY_SHORT,
+ *	%NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+ *	and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD.
+ *	However, for setting the channel, see %NL80211_CMD_SET_CHANNEL
+ *	instead, the support here is for backward compatibility only.
+ * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
+ *	or rename notification. Has attributes %NL80211_ATTR_WIPHY and
+ *	%NL80211_ATTR_WIPHY_NAME.
+ * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes
+ *	%NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME.
+ *
+ * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration;
+ *	either a dump request for all interfaces or a specific get with a
+ *	single %NL80211_ATTR_IFINDEX is supported.
+ * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires
+ *	%NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE.
+ * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response
+ *	to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX,
+ *	%NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also
+ *	be sent from userspace to request creation of a new virtual interface,
+ *	then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and
+ *	%NL80211_ATTR_IFNAME.
+ * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes
+ *	%NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from
+ *	userspace to request deletion of a virtual interface, then requires
+ *	attribute %NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified
+ *	by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC.
+ * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT,
+ *	%NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD.
+ * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA,
+ *	%NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER,
+ *	and %NL80211_ATTR_KEY_SEQ attributes.
+ * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
+ *	or %NL80211_ATTR_MAC.
+ *
+ * @NL80211_CMD_GET_BEACON: (not used)
+ * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface
+ *	using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL
+ *	attributes. For drivers that generate the beacon and probe responses
+ *	internally, the following attributes must be provided: %NL80211_ATTR_IE,
+ *	%NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP.
+ * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters
+ *	are like for %NL80211_CMD_SET_BEACON, and additionally parameters that
+ *	do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL,
+ *	%NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID,
+ *	%NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
+ *	%NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
+ *	%NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
+ *	%NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT,
+ *	%NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS.
+ *	The channel to use can be set on the interface or be given using the
+ *	%NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width.
+ * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
+ * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface
+ * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP
+ *
+ * @NL80211_CMD_GET_STATION: Get station attributes for station identified by
+ *	%NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_STATION: Set station attributes for station identified by
+ *	%NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_NEW_STATION: Add a station with given attributes to the
+ *	the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC
+ *	or, if no MAC address given, all stations, on the interface identified
+ *	by %NL80211_ATTR_IFINDEX. %NL80211_ATTR_MGMT_SUBTYPE and
+ *	%NL80211_ATTR_REASON_CODE can optionally be used to specify which type
+ *	of disconnection indication should be sent to the station
+ *	(Deauthentication or Disassociation frame and reason code for that
+ *	frame).
+ *
+ * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
+ * 	destination %NL80211_ATTR_MAC on the interface identified by
+ * 	%NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_MPATH:  Set mesh path attributes for mesh path to
+ * 	destination %NL80211_ATTR_MAC on the interface identified by
+ * 	%NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by
+ *	%NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP.
+ * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by
+ *	%NL80211_ATTR_MAC.
+ * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the
+ *	the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC
+ *	or, if no MAC address given, all mesh paths, on the interface identified
+ *	by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by
+ *	%NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set
+ *	regulatory domain. If %NL80211_ATTR_WIPHY is specified and the device
+ *	has a private regulatory domain, it will be returned. Otherwise, the
+ *	global regdomain will be returned.
+ *	A device will have a private regulatory domain if it uses the
+ *	regulatory_hint() API. Even when a private regdomain is used the channel
+ *	information will still be mended according to further hints from
+ *	the regulatory core to help with compliance. A dump version of this API
+ *	is now available which will returns the global regdomain as well as
+ *	all private regdomains of present wiphys (for those that have it).
+ *	If a wiphy is self-managed (%NL80211_ATTR_WIPHY_SELF_MANAGED_REG), then
+ *	its private regdomain is the only valid one for it. The regulatory
+ *	core is not used to help with compliance in this case.
+ * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command
+ *	after being queried by the kernel. CRDA replies by sending a regulatory
+ *	domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
+ *	current alpha2 if it found a match. It also provides
+ * 	NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each
+ * 	regulatory rule is a nested set of attributes  given by
+ * 	%NL80211_ATTR_REG_RULE_FREQ_[START|END] and
+ * 	%NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by
+ * 	%NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and
+ * 	%NL80211_ATTR_REG_RULE_POWER_MAX_EIRP.
+ * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain
+ * 	to the specified ISO/IEC 3166-1 alpha2 country code. The core will
+ * 	store this as a valid request and then query userspace for it.
+ *
+ * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the
+ *	interface identified by %NL80211_ATTR_IFINDEX
+ *
+ * @NL80211_CMD_SET_MESH_CONFIG: Set mesh networking properties for the
+ *      interface identified by %NL80211_ATTR_IFINDEX
+ *
+ * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The
+ *	interface is identified with %NL80211_ATTR_IFINDEX and the management
+ *	frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be
+ *	added to the end of the specified management frame is specified with
+ *	%NL80211_ATTR_IE. If the command succeeds, the requested data will be
+ *	added to all specified management frames generated by
+ *	kernel/firmware/driver.
+ *	Note: This command has been removed and it is only reserved at this
+ *	point to avoid re-using existing command number. The functionality this
+ *	command was planned for has been provided with cleaner design with the
+ *	option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN,
+ *	NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE,
+ *	NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE.
+ *
+ * @NL80211_CMD_GET_SCAN: get scan results
+ * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
+ *	%NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
+ *	probe requests at CCK rate or not.
+ * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
+ *	NL80211_CMD_GET_SCAN and on the "scan" multicast group)
+ * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
+ *	partial scan results may be available
+ *
+ * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain
+ *	intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL.
+ *	Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS)
+ *	are passed, they are used in the probe requests.  For
+ *	broadcast, a broadcast SSID must be passed (ie. an empty
+ *	string).  If no SSID is passed, no probe requests are sent and
+ *	a passive scan is performed.  %NL80211_ATTR_SCAN_FREQUENCIES,
+ *	if passed, define which channels should be scanned; if not
+ *	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.  The first cycle of the
+ *	scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY
+ *	is supplied.
+ * @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
+ *	stopped.  The driver may issue this event at any time during a
+ *	scheduled scan.  One reason for stopping the scan is if the hardware
+ *	does not support starting an association or a normal scan while running
+ *	a scheduled scan.  This event is also sent when the
+ *	%NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface
+ *	is brought down while a scheduled scan was running.
+ *
+ * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation
+ *      or noise level
+ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
+ *	NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
+ *
+ * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC
+ *	(for the BSSID) and %NL80211_ATTR_PMKID.
+ * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC
+ *	(for the BSSID) and %NL80211_ATTR_PMKID.
+ * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
+ *
+ * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
+ * 	has been changed and provides details of the request information
+ * 	that caused the change such as who initiated the regulatory request
+ * 	(%NL80211_ATTR_REG_INITIATOR), the wiphy_idx
+ * 	(%NL80211_ATTR_REG_ALPHA2) on which the request was made from if
+ * 	the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or
+ * 	%NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain
+ * 	set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is
+ * 	%NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on
+ * 	to (%NL80211_ATTR_REG_ALPHA2).
+ * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon
+ * 	has been found while world roaming thus enabling active scan or
+ * 	any mode of operation that initiates TX (beacons) on a channel
+ * 	where we would not have been able to do either before. As an example
+ * 	if you are world roaming (regulatory domain set to world or if your
+ * 	driver is using a custom world roaming regulatory domain) and while
+ * 	doing a passive scan on the 5 GHz band you find an AP there (if not
+ * 	on a DFS channel) you will now be able to actively scan for that AP
+ * 	or use AP mode on your card on that same channel. Note that this will
+ * 	never be used for channels 1-11 on the 2 GHz band as they are always
+ * 	enabled world wide. This beacon hint is only sent if your device had
+ * 	either disabled active scanning or beaconing on a channel. We send to
+ * 	userspace the wiphy on which we removed a restriction from
+ * 	(%NL80211_ATTR_WIPHY) and the channel on which this occurred
+ * 	before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER)
+ * 	the beacon hint was processed.
+ *
+ * @NL80211_CMD_AUTHENTICATE: authentication request and notification.
+ *	This command is used both as a command (request to authenticate) and
+ *	as an event on the "mlme" multicast group indicating completion of the
+ *	authentication process.
+ *	When used as a command, %NL80211_ATTR_IFINDEX is used to identify the
+ *	interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and
+ *	BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify
+ *	the SSID (mainly for association, but is included in authentication
+ *	request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used
+ *	to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE
+ *	is used to specify the authentication type. %NL80211_ATTR_IE is used to
+ *	define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs)
+ *	to be added to the frame.
+ *	When used as an event, this reports reception of an Authentication
+ *	frame in station and IBSS modes when the local MLME processed the
+ *	frame, i.e., it was for the local STA and was received in correct
+ *	state. This is similar to MLME-AUTHENTICATE.confirm primitive in the
+ *	MLME SAP interface (kernel providing MLME, userspace SME). The
+ *	included %NL80211_ATTR_FRAME attribute contains the management frame
+ *	(including both the header and frame body, but not FCS). This event is
+ *	also used to indicate if the authentication attempt timed out. In that
+ *	case the %NL80211_ATTR_FRAME attribute is replaced with a
+ *	%NL80211_ATTR_TIMED_OUT flag (and %NL80211_ATTR_MAC to indicate which
+ *	pending authentication timed out).
+ * @NL80211_CMD_ASSOCIATE: association request and notification; like
+ *	NL80211_CMD_AUTHENTICATE but for Association and Reassociation
+ *	(similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request,
+ *	MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives).
+ * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like
+ *	NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to
+ *	MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication
+ *	primitives).
+ * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like
+ *	NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to
+ *	MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives).
+ *
+ * @NL80211_CMD_MICHAEL_MIC_FAILURE: notification of a locally detected Michael
+ *	MIC (part of TKIP) failure; sent on the "mlme" multicast group; the
+ *	event includes %NL80211_ATTR_MAC to describe the source MAC address of
+ *	the frame with invalid MIC, %NL80211_ATTR_KEY_TYPE to show the key
+ *	type, %NL80211_ATTR_KEY_IDX to indicate the key identifier, and
+ *	%NL80211_ATTR_KEY_SEQ to indicate the TSC value of the frame; this
+ *	event matches with MLME-MICHAELMICFAILURE.indication() primitive
+ *
+ * @NL80211_CMD_JOIN_IBSS: Join a new IBSS -- given at least an SSID and a
+ *	FREQ attribute (for the initial frequency if no peer can be found)
+ *	and optionally a MAC (as BSSID) and FREQ_FIXED attribute if those
+ *	should be fixed rather than automatically determined. Can only be
+ *	executed on a network interface that is UP, and fixed BSSID/FREQ
+ *	may be rejected. Another optional parameter is the beacon interval,
+ *	given in the %NL80211_ATTR_BEACON_INTERVAL attribute, which if not
+ *	given defaults to 100 TU (102.4ms).
+ * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is
+ *	determined by the network interface.
+ *
+ * @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute
+ *	to identify the device, and the TESTDATA blob attribute to pass through
+ *	to the driver.
+ *
+ * @NL80211_CMD_CONNECT: connection request and notification; this command
+ *	requests to connect to a specified network but without separating
+ *	auth and assoc steps. For this, you need to specify the SSID in a
+ *	%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,
+ *	%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
+ *	in driver is used and if period value is 0, bg scan will be disabled.
+ *	This attribute is ignored if driver does not support roam scan.
+ *	It is also sent as an event, with the BSSID and response IEs when the
+ *	connection is established or failed to be established. This can be
+ *	determined by the STATUS_CODE attribute.
+ * @NL80211_CMD_ROAM: request that the card roam (currently not implemented),
+ *	sent as an event when the card/driver roamed by itself.
+ * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify
+ *	userspace that a connection was dropped by the AP or due to other
+ *	reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and
+ *	%NL80211_ATTR_REASON_CODE attributes are used.
+ *
+ * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices
+ *	associated with this wiphy must be down and will follow.
+ *
+ * @NL80211_CMD_REMAIN_ON_CHANNEL: Request to remain awake on the specified
+ *	channel for the specified amount of time. This can be used to do
+ *	off-channel operations like transmit a Public Action frame and wait for
+ *	a response while being associated to an AP on another channel.
+ *	%NL80211_ATTR_IFINDEX is used to specify which interface (and thus
+ *	radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the
+ *	frequency for the operation.
+ *	%NL80211_ATTR_DURATION is used to specify the duration in milliseconds
+ *	to remain on the channel. This command is also used as an event to
+ *	notify when the requested duration starts (it may take a while for the
+ *	driver to schedule this time due to other concurrent needs for the
+ *	radio).
+ *	When called, this operation returns a cookie (%NL80211_ATTR_COOKIE)
+ *	that will be included with any events pertaining to this request;
+ *	the cookie is also used to cancel the request.
+ * @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a
+ *	pending remain-on-channel duration if the desired operation has been
+ *	completed prior to expiration of the originally requested duration.
+ *	%NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the
+ *	radio. The %NL80211_ATTR_COOKIE attribute must be given as well to
+ *	uniquely identify the request.
+ *	This command is also used as an event to notify when a requested
+ *	remain-on-channel duration has expired.
+ *
+ * @NL80211_CMD_SET_TX_BITRATE_MASK: Set the mask of rates to be used in TX
+ *	rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface
+ *	and @NL80211_ATTR_TX_RATES the set of allowed rates.
+ *
+ * @NL80211_CMD_REGISTER_FRAME: Register for receiving certain mgmt frames
+ *	(via @NL80211_CMD_FRAME) for processing in userspace. This command
+ *	requires an interface index, a frame type attribute (optional for
+ *	backward compatibility reasons, if not given assumes action frames)
+ *	and a match attribute containing the first few bytes of the frame
+ *	that should match, e.g. a single byte for only a category match or
+ *	four bytes for vendor frames including the OUI. The registration
+ *	cannot be dropped, but is removed automatically when the netlink
+ *	socket is closed. Multiple registrations can be made.
+ * @NL80211_CMD_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for
+ *	backward compatibility
+ * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This
+ *	command is used both as a request to transmit a management frame and
+ *	as an event indicating reception of a frame that was not processed in
+ *	kernel code, but is for us (i.e., which may need to be processed in a
+ *	user space application). %NL80211_ATTR_FRAME is used to specify the
+ *	frame contents (including header). %NL80211_ATTR_WIPHY_FREQ is used
+ *	to indicate on which channel the frame is to be transmitted or was
+ *	received. If this channel is not the current channel (remain-on-channel
+ *	or the operational channel) the device will switch to the given channel
+ *	and transmit the frame, optionally waiting for a response for the time
+ *	specified using %NL80211_ATTR_DURATION. When called, this operation
+ *	returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the
+ *	TX status event pertaining to the TX request.
+ *	%NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
+ *	management frames at CCK rate or not in 2GHz band.
+ *	%NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA
+ *	counters which will be updated to the current value. This attribute
+ *	is used during CSA period.
+ * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
+ *	command may be used with the corresponding cookie to cancel the wait
+ *	time if it is known that it is no longer necessary.
+ * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility.
+ * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame
+ *	transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies
+ *	the TX command and %NL80211_ATTR_FRAME includes the contents of the
+ *	frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
+ *	the frame.
+ * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for
+ *	backward compatibility.
+ *
+ * @NL80211_CMD_SET_POWER_SAVE: Set powersave, using %NL80211_ATTR_PS_STATE
+ * @NL80211_CMD_GET_POWER_SAVE: Get powersave status in %NL80211_ATTR_PS_STATE
+ *
+ * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command
+ *	is used to configure connection quality monitoring notification trigger
+ *	levels.
+ * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This
+ *	command is used as an event to indicate the that a trigger level was
+ *	reached.
+ * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ
+ *	and the attributes determining channel width) the given interface
+ *	(identifed by %NL80211_ATTR_IFINDEX) shall operate on.
+ *	In case multiple channels are supported by the device, the mechanism
+ *	with which it switches channels is implementation-defined.
+ *	When a monitor interface is given, it can only switch channel while
+ *	no other interfaces are operating to avoid disturbing the operation
+ *	of any other interfaces, and other interfaces will again take
+ *	precedence when they are used.
+ *
+ * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface.
+ *
+ * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial
+ *	mesh config parameters may be given.
+ * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the
+ *	network is determined by the network interface.
+ *
+ * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame
+ *	notification. This event is used to indicate that an unprotected
+ *	deauthentication frame was dropped when MFP is in use.
+ * @NL80211_CMD_UNPROT_DISASSOCIATE: Unprotected disassociation frame
+ *	notification. This event is used to indicate that an unprotected
+ *	disassociation frame was dropped when MFP is in use.
+ *
+ * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a
+ *      beacon or probe response from a compatible mesh peer.  This is only
+ *      sent while no station information (sta_info) exists for the new peer
+ *      candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH,
+ *      @NL80211_MESH_SETUP_USERSPACE_AMPE, or
+ *      @NL80211_MESH_SETUP_USERSPACE_MPM is set.  On reception of this
+ *      notification, userspace may decide to create a new station
+ *      (@NL80211_CMD_NEW_STATION).  To stop this notification from
+ *      reoccurring, the userspace authentication daemon may want to create the
+ *      new station with the AUTHENTICATED flag unset and maybe change it later
+ *      depending on the authentication result.
+ *
+ * @NL80211_CMD_GET_WOWLAN: get Wake-on-Wireless-LAN (WoWLAN) settings.
+ * @NL80211_CMD_SET_WOWLAN: set Wake-on-Wireless-LAN (WoWLAN) settings.
+ *	Since wireless is more complex than wired ethernet, it supports
+ *	various triggers. These triggers can be configured through this
+ *	command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For
+ *	more background information, see
+ *	http://wireless.kernel.org/en/users/Documentation/WoWLAN.
+ *	The @NL80211_CMD_SET_WOWLAN command can also be used as a notification
+ *	from the driver reporting the wakeup reason. In this case, the
+ *	@NL80211_ATTR_WOWLAN_TRIGGERS attribute will contain the reason
+ *	for the wakeup, if it was caused by wireless. If it is not present
+ *	in the wakeup notification, the wireless device didn't cause the
+ *	wakeup but reports that it was woken up.
+ *
+ * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver
+ *	the necessary information for supporting GTK rekey offload. This
+ *	feature is typically used during WoWLAN. The configuration data
+ *	is contained in %NL80211_ATTR_REKEY_DATA (which is nested and
+ *	contains the data in sub-attributes). After rekeying happened,
+ *	this command may also be sent by the driver as an MLME event to
+ *	inform userspace of the new replay counter.
+ *
+ * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace
+ *	of PMKSA caching dandidates.
+ *
+ * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup).
+ *	In addition, this can be used as an event to request userspace to take
+ *	actions on TDLS links (set up a new link or tear down an existing one).
+ *	In such events, %NL80211_ATTR_TDLS_OPERATION indicates the requested
+ *	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. 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
+ *	implement sending deauth to stations that send unexpected class 3
+ *	frames. Also used as the event sent by the kernel when such a frame
+ *	is received.
+ *	For the event, the %NL80211_ATTR_MAC attribute carries the TA and
+ *	other attributes like the interface index are present.
+ *	If used as the command it must have an interface index and you can
+ *	only unsubscribe from the event by closing the socket. Subscription
+ *	is also for %NL80211_CMD_UNEXPECTED_4ADDR_FRAME events.
+ *
+ * @NL80211_CMD_UNEXPECTED_4ADDR_FRAME: Sent as an event indicating that the
+ *	associated station identified by %NL80211_ATTR_MAC sent a 4addr frame
+ *	and wasn't already in a 4-addr VLAN. The event will be sent similarly
+ *	to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener.
+ *
+ * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface
+ *	by sending a null data frame to it and reporting when the frame is
+ *	acknowleged. This is used to allow timing out inactive clients. Uses
+ *	%NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a
+ *	direct reply with an %NL80211_ATTR_COOKIE that is later used to match
+ *	up the event with the request. The event includes the same data and
+ *	has %NL80211_ATTR_ACK set if the frame was ACKed.
+ *
+ * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from
+ *	other BSSes when any interfaces are in AP mode. This helps implement
+ *	OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME
+ *	messages. Note that per PHY only one application may register.
+ *
+ * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether
+ *      No Acknowledgement Policy should be applied.
+ *
+ * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels
+ *	independently of the userspace SME, send this event indicating
+ *	%NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the
+ *	attributes determining channel width.  This indication may also be
+ *	sent when a remotely-initiated switch (e.g., when a STA receives a CSA
+ *	from the remote AP) is completed;
+ *
+ * @NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: Notify that a channel switch
+ *	has been started on an interface, regardless of the initiator
+ *	(ie. whether it was requested from a remote device or
+ *	initiated on our own).  It indicates that
+ *	%NL80211_ATTR_IFINDEX will be on %NL80211_ATTR_WIPHY_FREQ
+ *	after %NL80211_ATTR_CH_SWITCH_COUNT TBTT's.  The userspace may
+ *	decide to react to this indication by requesting other
+ *	interfaces to change channel as well.
+ *
+ * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by
+ *	its %NL80211_ATTR_WDEV identifier. It must have been created with
+ *	%NL80211_CMD_NEW_INTERFACE previously. After it has been started, the
+ *	P2P Device can be used for P2P operations, e.g. remain-on-channel and
+ *	public action frame TX.
+ * @NL80211_CMD_STOP_P2P_DEVICE: Stop the given P2P Device, identified by
+ *	its %NL80211_ATTR_WDEV identifier.
+ *
+ * @NL80211_CMD_CONN_FAILED: connection request to an AP failed; used to
+ *	notify userspace that AP has rejected the connection request from a
+ *	station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON
+ *	is used for this.
+ *
+ * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames
+ *	for IBSS or MESH vif.
+ *
+ * @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control.
+ *	This is to be used with the drivers advertising the support of MAC
+ *	address based access control. List of MAC addresses is passed in
+ *	%NL80211_ATTR_MAC_ADDRS and ACL policy is passed in
+ *	%NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it
+ *	is not already done. The new list will replace any existing list. Driver
+ *	will clear its ACL when the list of MAC addresses passed is empty. This
+ *	command is used in AP/P2P GO mode. Driver has to make sure to clear its
+ *	ACL list during %NL80211_CMD_STOP_AP.
+ *
+ * @NL80211_CMD_RADAR_DETECT: Start a Channel availability check (CAC). Once
+ *	a radar is detected or the channel availability scan (CAC) has finished
+ *	or was aborted, or a radar was detected, usermode will be notified with
+ *	this event. This command is also used to notify userspace about radars
+ *	while operating on this channel.
+ *	%NL80211_ATTR_RADAR_EVENT is used to inform about the type of the
+ *	event.
+ *
+ * @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features,
+ *	i.e. features for the nl80211 protocol rather than device features.
+ *	Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap.
+ *
+ * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition
+ *	Information Element to the WLAN driver
+ *
+ * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver
+ *	to the supplicant. This will carry the target AP's MAC address along
+ *	with the relevant Information Elements. This event is used to report
+ *	received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE).
+ *
+ * @NL80211_CMD_CRIT_PROTOCOL_START: Indicates user-space will start running
+ *	a critical protocol that needs more reliability in the connection to
+ *	complete.
+ *
+ * @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_ADD_TX_TS: Ask the kernel to add a traffic stream for the given
+ *	%NL80211_ATTR_TSID and %NL80211_ATTR_MAC with %NL80211_ATTR_USER_PRIO
+ *	and %NL80211_ATTR_ADMITTED_TIME parameters.
+ *	Note that the action frame handshake with the AP shall be handled by
+ *	userspace via the normal management RX/TX framework, this only sets
+ *	up the TX TS in the driver/device.
+ *	If the admitted time attribute is not added then the request just checks
+ *	if a subsequent setup could be successful, the intent is to use this to
+ *	avoid setting up a session with the AP when local restrictions would
+ *	make that impossible. However, the subsequent "real" setup may still
+ *	fail even if the check was successful.
+ * @NL80211_CMD_DEL_TX_TS: Remove an existing TS with the %NL80211_ATTR_TSID
+ *	and %NL80211_ATTR_MAC parameters. It isn't necessary to call this
+ *	before removing a station entry entirely, or before disassociating
+ *	or similar, cleanup will happen in the driver/device in this case.
+ *
+ * @NL80211_CMD_GET_MPP: Get mesh path attributes for mesh proxy path to
+ *	destination %NL80211_ATTR_MAC on the interface identified by
+ *	%NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_JOIN_OCB: Join the OCB network. The center frequency and
+ *	bandwidth of a channel must be given.
+ * @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the
+ *	network is determined by the network interface.
+ *
+ * @NL80211_CMD_TDLS_CHANNEL_SWITCH: Start channel-switching with a TDLS peer,
+ *	identified by the %NL80211_ATTR_MAC parameter. A target channel is
+ *	provided via %NL80211_ATTR_WIPHY_FREQ and other attributes determining
+ *	channel width/type. The target operating class is given via
+ *	%NL80211_ATTR_OPER_CLASS.
+ *	The driver is responsible for continually initiating channel-switching
+ *	operations and returning to the base channel for communication with the
+ *	AP.
+ * @NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH: Stop channel-switching with a TDLS
+ *	peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel
+ *	when this command completes.
+ *
+ * @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used
+ *	as an event to indicate changes for devices with wiphy-specific regdom
+ *	management.
+ *
+ * @NL80211_CMD_MAX: highest used command number
+ * @__NL80211_CMD_AFTER_LAST: internal use
+ */
+enum nl80211_commands {
+/* don't change the order or add anything between, this is ABI! */
+	NL80211_CMD_UNSPEC,
+
+	NL80211_CMD_GET_WIPHY,		/* can dump */
+	NL80211_CMD_SET_WIPHY,
+	NL80211_CMD_NEW_WIPHY,
+	NL80211_CMD_DEL_WIPHY,
+
+	NL80211_CMD_GET_INTERFACE,	/* can dump */
+	NL80211_CMD_SET_INTERFACE,
+	NL80211_CMD_NEW_INTERFACE,
+	NL80211_CMD_DEL_INTERFACE,
+
+	NL80211_CMD_GET_KEY,
+	NL80211_CMD_SET_KEY,
+	NL80211_CMD_NEW_KEY,
+	NL80211_CMD_DEL_KEY,
+
+	NL80211_CMD_GET_BEACON,
+	NL80211_CMD_SET_BEACON,
+	NL80211_CMD_START_AP,
+	NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP,
+	NL80211_CMD_STOP_AP,
+	NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP,
+
+	NL80211_CMD_GET_STATION,
+	NL80211_CMD_SET_STATION,
+	NL80211_CMD_NEW_STATION,
+	NL80211_CMD_DEL_STATION,
+
+	NL80211_CMD_GET_MPATH,
+	NL80211_CMD_SET_MPATH,
+	NL80211_CMD_NEW_MPATH,
+	NL80211_CMD_DEL_MPATH,
+
+	NL80211_CMD_SET_BSS,
+
+	NL80211_CMD_SET_REG,
+	NL80211_CMD_REQ_SET_REG,
+
+	NL80211_CMD_GET_MESH_CONFIG,
+	NL80211_CMD_SET_MESH_CONFIG,
+
+	NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */,
+
+	NL80211_CMD_GET_REG,
+
+	NL80211_CMD_GET_SCAN,
+	NL80211_CMD_TRIGGER_SCAN,
+	NL80211_CMD_NEW_SCAN_RESULTS,
+	NL80211_CMD_SCAN_ABORTED,
+
+	NL80211_CMD_REG_CHANGE,
+
+	NL80211_CMD_AUTHENTICATE,
+	NL80211_CMD_ASSOCIATE,
+	NL80211_CMD_DEAUTHENTICATE,
+	NL80211_CMD_DISASSOCIATE,
+
+	NL80211_CMD_MICHAEL_MIC_FAILURE,
+
+	NL80211_CMD_REG_BEACON_HINT,
+
+	NL80211_CMD_JOIN_IBSS,
+	NL80211_CMD_LEAVE_IBSS,
+
+	NL80211_CMD_TESTMODE,
+
+	NL80211_CMD_CONNECT,
+	NL80211_CMD_ROAM,
+	NL80211_CMD_DISCONNECT,
+
+	NL80211_CMD_SET_WIPHY_NETNS,
+
+	NL80211_CMD_GET_SURVEY,
+	NL80211_CMD_NEW_SURVEY_RESULTS,
+
+	NL80211_CMD_SET_PMKSA,
+	NL80211_CMD_DEL_PMKSA,
+	NL80211_CMD_FLUSH_PMKSA,
+
+	NL80211_CMD_REMAIN_ON_CHANNEL,
+	NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
+
+	NL80211_CMD_SET_TX_BITRATE_MASK,
+
+	NL80211_CMD_REGISTER_FRAME,
+	NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME,
+	NL80211_CMD_FRAME,
+	NL80211_CMD_ACTION = NL80211_CMD_FRAME,
+	NL80211_CMD_FRAME_TX_STATUS,
+	NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS,
+
+	NL80211_CMD_SET_POWER_SAVE,
+	NL80211_CMD_GET_POWER_SAVE,
+
+	NL80211_CMD_SET_CQM,
+	NL80211_CMD_NOTIFY_CQM,
+
+	NL80211_CMD_SET_CHANNEL,
+	NL80211_CMD_SET_WDS_PEER,
+
+	NL80211_CMD_FRAME_WAIT_CANCEL,
+
+	NL80211_CMD_JOIN_MESH,
+	NL80211_CMD_LEAVE_MESH,
+
+	NL80211_CMD_UNPROT_DEAUTHENTICATE,
+	NL80211_CMD_UNPROT_DISASSOCIATE,
+
+	NL80211_CMD_NEW_PEER_CANDIDATE,
+
+	NL80211_CMD_GET_WOWLAN,
+	NL80211_CMD_SET_WOWLAN,
+
+	NL80211_CMD_START_SCHED_SCAN,
+	NL80211_CMD_STOP_SCHED_SCAN,
+	NL80211_CMD_SCHED_SCAN_RESULTS,
+	NL80211_CMD_SCHED_SCAN_STOPPED,
+
+	NL80211_CMD_SET_REKEY_OFFLOAD,
+
+#ifdef BUILD_FEATURE_SCPM
+	NL80211_CMD_GET_KEEPALIVE,
+	NL80211_CMD_SET_KEEPALIVE,
+#endif
+
+	NL80211_CMD_PMKSA_CANDIDATE,
+
+	NL80211_CMD_TDLS_OPER,
+	NL80211_CMD_TDLS_MGMT,
+
+	NL80211_CMD_UNEXPECTED_FRAME,
+
+	NL80211_CMD_PROBE_CLIENT,
+
+	NL80211_CMD_REGISTER_BEACONS,
+
+	NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
+
+	NL80211_CMD_SET_NOACK_MAP,
+
+	NL80211_CMD_CH_SWITCH_NOTIFY,
+
+	NL80211_CMD_START_P2P_DEVICE,
+	NL80211_CMD_STOP_P2P_DEVICE,
+
+	NL80211_CMD_CONN_FAILED,
+
+	NL80211_CMD_SET_MCAST_RATE,
+
+	NL80211_CMD_SET_MAC_ACL,
+
+	NL80211_CMD_RADAR_DETECT,
+
+	NL80211_CMD_GET_PROTOCOL_FEATURES,
+
+	NL80211_CMD_UPDATE_FT_IES,
+	NL80211_CMD_FT_EVENT,
+
+	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,
+
+	NL80211_CMD_ADD_TX_TS,
+	NL80211_CMD_DEL_TX_TS,
+
+	NL80211_CMD_GET_MPP,
+
+	NL80211_CMD_JOIN_OCB,
+	NL80211_CMD_LEAVE_OCB,
+
+	NL80211_CMD_CH_SWITCH_STARTED_NOTIFY,
+
+	NL80211_CMD_TDLS_CHANNEL_SWITCH,
+	NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
+
+	NL80211_CMD_WIPHY_REG_CHANGE,
+
+	/* add new commands above here */
+
+	/* used to define NL80211_CMD_MAX below */
+	__NL80211_CMD_AFTER_LAST,
+	NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
+};
+
+/*
+ * Allow user space programs to use #ifdef on new commands by defining them
+ * here
+ */
+#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS
+#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE
+#define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE
+#define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE
+#define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE
+#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE
+#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE
+#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT
+
+#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
+
+/* source-level API compatibility */
+#define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG
+#define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG
+#define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE
+
+/**
+ * enum nl80211_attrs - nl80211 netlink attributes
+ *
+ * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf.
+ *	/sys/class/ieee80211/<phyname>/index
+ * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming)
+ * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters
+ * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz,
+ *	defines the channel together with the (deprecated)
+ *	%NL80211_ATTR_WIPHY_CHANNEL_TYPE attribute or the attributes
+ *	%NL80211_ATTR_CHANNEL_WIDTH and if needed %NL80211_ATTR_CENTER_FREQ1
+ *	and %NL80211_ATTR_CENTER_FREQ2
+ * @NL80211_ATTR_CHANNEL_WIDTH: u32 attribute containing one of the values
+ *	of &enum nl80211_chan_width, describing the channel width. See the
+ *	documentation of the enum for more information.
+ * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the
+ *	channel, used for anything but 20 MHz bandwidth
+ * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the
+ *	channel, used only for 80+80 MHz bandwidth
+ * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ
+ *	if HT20 or HT40 are to be used (i.e., HT disabled if not included):
+ *	NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including
+ *		this attribute)
+ *	NL80211_CHAN_HT20 = HT20 only
+ *	NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel
+ *	NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel
+ *	This attribute is now deprecated.
+ * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is
+ *	less than or equal to the RTS threshold; allowed range: 1..255;
+ *	dot11ShortRetryLimit; u8
+ * @NL80211_ATTR_WIPHY_RETRY_LONG: TX retry limit for frames whose length is
+ *	greater than the RTS threshold; allowed range: 1..255;
+ *	dot11ShortLongLimit; u8
+ * @NL80211_ATTR_WIPHY_FRAG_THRESHOLD: fragmentation threshold, i.e., maximum
+ *	length in octets for frames; allowed range: 256..8000, disable
+ *	fragmentation with (u32)-1; dot11FragmentationThreshold; u32
+ * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length
+ *	larger than or equal to this use RTS/CTS handshake); allowed range:
+ *	0..65536, disable with (u32)-1; dot11RTSThreshold; u32
+ * @NL80211_ATTR_WIPHY_COVERAGE_CLASS: Coverage Class as defined by IEEE 802.11
+ *	section 7.3.2.9; dot11CoverageClass; u8
+ *
+ * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on
+ * @NL80211_ATTR_IFNAME: network interface name
+ * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype
+ *
+ * @NL80211_ATTR_WDEV: wireless device identifier, used for pseudo-devices
+ *	that don't have a netdev (u64)
+ *
+ * @NL80211_ATTR_MAC: MAC address (various uses)
+ *
+ * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of
+ *	16 bytes encryption key followed by 8 bytes each for TX and RX MIC
+ *	keys
+ * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3)
+ * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11
+ *	section 7.3.2.25.1, e.g. 0x000FAC04)
+ * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and
+ *	CCMP keys, each six bytes in little endian
+ * @NL80211_ATTR_KEY_DEFAULT: Flag attribute indicating the key is default key
+ * @NL80211_ATTR_KEY_DEFAULT_MGMT: Flag attribute indicating the key is the
+ *	default management key
+ * @NL80211_ATTR_CIPHER_SUITES_PAIRWISE: For crypto settings for connect or
+ *	other commands, indicates which pairwise cipher suites are used
+ * @NL80211_ATTR_CIPHER_SUITE_GROUP: For crypto settings for connect or
+ *	other commands, indicates which group cipher suite is used
+ *
+ * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU
+ * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing
+ * @NL80211_ATTR_BEACON_HEAD: portion of the beacon before the TIM IE
+ * @NL80211_ATTR_BEACON_TAIL: portion of the beacon after the TIM IE
+ *
+ * @NL80211_ATTR_STA_AID: Association ID for the station (u16)
+ * @NL80211_ATTR_STA_FLAGS: flags, nested element with NLA_FLAG attributes of
+ *	&enum nl80211_sta_flags (deprecated, use %NL80211_ATTR_STA_FLAGS2)
+ * @NL80211_ATTR_STA_LISTEN_INTERVAL: listen interval as defined by
+ *	IEEE 802.11 7.3.1.6 (u16).
+ * @NL80211_ATTR_STA_SUPPORTED_RATES: supported rates, array of supported
+ *	rates as defined by IEEE 802.11 7.3.2.2 but without the length
+ *	restriction (at most %NL80211_MAX_SUPP_RATES).
+ * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station
+ *	to, or the AP interface the station was originally added to to.
+ * @NL80211_ATTR_STA_INFO: information about a station, part of station info
+ *	given for %NL80211_CMD_GET_STATION, nested attribute containing
+ *	info as possible, see &enum nl80211_sta_info.
+ *
+ * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands,
+ *	consisting of a nested array.
+ *
+ * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes).
+ * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link
+ *	(see &enum nl80211_plink_action).
+ * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
+ * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
+ * 	info given for %NL80211_CMD_GET_MPATH, nested attribute described at
+ *	&enum nl80211_mpath_info.
+ *
+ * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of
+ *      &enum nl80211_mntr_flags.
+ *
+ * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the
+ * 	current regulatory domain should be set to or is already set to.
+ * 	For example, 'CR', for Costa Rica. This attribute is used by the kernel
+ * 	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.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
+ *	rules.
+ *
+ * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1)
+ * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled
+ *	(u8, 0 or 1)
+ * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled
+ *	(u8, 0 or 1)
+ * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic
+ *	rates in format defined by IEEE 802.11 7.3.2.2 but without the length
+ *	restriction (at most %NL80211_MAX_SUPP_RATES).
+ *
+ * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from
+ *	association request when used with NL80211_CMD_NEW_STATION)
+ *
+ * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all
+ *	supported interface types, each a flag attribute with the number
+ *	of the interface mode.
+ *
+ * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for
+ *	%NL80211_CMD_SET_MGMT_EXTRA_IE.
+ *
+ * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with
+ *	%NL80211_CMD_SET_MGMT_EXTRA_IE).
+ *
+ * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with
+ *	a single scan request, a wiphy attribute.
+ * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: number of SSIDs you can
+ *	scan with a single scheduled scan request, a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements
+ *	that can be added to a scan request
+ * @NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: maximum length of information
+ *	elements that can be added to a scheduled scan request
+ * @NL80211_ATTR_MAX_MATCH_SETS: maximum number of sets that can be
+ *	used with @NL80211_ATTR_SCHED_SCAN_MATCH, a wiphy attribute.
+ *
+ * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz)
+ * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive
+ *	scanning and include a zero-length SSID (wildcard) for wildcard scan
+ * @NL80211_ATTR_BSS: scan result BSS
+ *
+ * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain
+ * 	currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_*
+ * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently
+ * 	set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*)
+ *
+ * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies
+ *	an array of command numbers (i.e. a mapping index to command number)
+ *	that the driver for the given wiphy supports.
+ *
+ * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header
+ *	and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and
+ *	NL80211_CMD_ASSOCIATE events
+ * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets)
+ * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type,
+ *	represented as a u32
+ * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and
+ *	%NL80211_CMD_DISASSOCIATE, u16
+ *
+ * @NL80211_ATTR_KEY_TYPE: Key Type, see &enum nl80211_key_type, represented as
+ *	a u32
+ *
+ * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change
+ * 	due to considerations from a beacon hint. This attribute reflects
+ * 	the state of the channel _before_ the beacon hint processing. This
+ * 	attributes consists of a nested attribute containing
+ * 	NL80211_FREQUENCY_ATTR_*
+ * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change
+ * 	due to considerations from a beacon hint. This attribute reflects
+ * 	the state of the channel _after_ the beacon hint processing. This
+ * 	attributes consists of a nested attribute containing
+ * 	NL80211_FREQUENCY_ATTR_*
+ *
+ * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported
+ *	cipher suites
+ *
+ * @NL80211_ATTR_FREQ_FIXED: a flag indicating the IBSS should not try to look
+ *	for other networks on different channels
+ *
+ * @NL80211_ATTR_TIMED_OUT: a flag indicating than an operation timed out; this
+ *	is used, e.g., with %NL80211_CMD_AUTHENTICATE event
+ *
+ * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is
+ *	used for the association (&enum nl80211_mfp, represented as a u32);
+ *	this attribute can be used
+ *	with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests
+ *
+ * @NL80211_ATTR_STA_FLAGS2: Attribute containing a
+ *	&struct nl80211_sta_flag_update.
+ *
+ * @NL80211_ATTR_CONTROL_PORT: A flag indicating whether user space controls
+ *	IEEE 802.1X port, i.e., sets/clears %NL80211_STA_FLAG_AUTHORIZED, in
+ *	station mode. If the flag is included in %NL80211_CMD_ASSOCIATE
+ *	request, the driver will assume that the port is unauthorized until
+ *	authorized by user space. Otherwise, port is marked authorized by
+ *	default in station mode.
+ * @NL80211_ATTR_CONTROL_PORT_ETHERTYPE: A 16-bit value indicating the
+ *	ethertype that will be used for key negotiation. It can be
+ *	specified with the associate and connect commands. If it is not
+ *	specified, the value defaults to 0x888E (PAE, 802.1X). This
+ *	attribute is also used as a flag in the wiphy information to
+ *	indicate that protocols other than PAE are supported.
+ * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with
+ *	%NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom
+ *	ethertype frames used for key negotiation must not be encrypted.
+ *
+ * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver.
+ *	We recommend using nested, driver-specific attributes within this.
+ *
+ * @NL80211_ATTR_DISCONNECTED_BY_AP: A flag indicating that the DISCONNECT
+ *	event was due to the AP disconnecting the station, and not due to
+ *	a local disconnect request.
+ * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT
+ *	event (u16)
+ * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating
+ *	that protected APs should be used. This is also used with NEW_BEACON to
+ *	indicate that the BSS is to use protection.
+ *
+ * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON
+ *	to indicate which unicast key ciphers will be used with the connection
+ *	(an array of u32).
+ * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ *	indicate which group key cipher will be used with the connection (a
+ *	u32).
+ * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ *	indicate which WPA version(s) the AP we want to associate with is using
+ *	(a u32 with flags from &enum nl80211_wpa_versions).
+ * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ *	indicate which key management algorithm(s) to use (an array of u32).
+ *
+ * @NL80211_ATTR_REQ_IE: (Re)association request information elements as
+ *	sent out by the card, for ROAM and successful CONNECT events.
+ * @NL80211_ATTR_RESP_IE: (Re)association response information elements as
+ *	sent by peer, for ROAM and successful CONNECT events.
+ *
+ * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE
+ *	commands to specify using a reassociate frame
+ *
+ * @NL80211_ATTR_KEY: key information in a nested attribute with
+ *	%NL80211_KEY_* sub-attributes
+ * @NL80211_ATTR_KEYS: array of keys for static WEP keys for connect()
+ *	and join_ibss(), key information is in a nested attribute each
+ *	with %NL80211_KEY_* sub-attributes
+ *
+ * @NL80211_ATTR_PID: Process ID of a network namespace.
+ *
+ * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for
+ *	dumps. This number increases whenever the object list being
+ *	dumped changes, and as such userspace can verify that it has
+ *	obtained a complete and consistent snapshot by verifying that
+ *	all dump messages contain the same generation number. If it
+ *	changed then the list changed and the dump should be repeated
+ *	completely from scratch.
+ *
+ * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface
+ *
+ * @NL80211_ATTR_SURVEY_INFO: survey information about a channel, part of
+ *      the survey response for %NL80211_CMD_GET_SURVEY, nested attribute
+ *      containing info as possible, see &enum survey_info.
+ *
+ * @NL80211_ATTR_PMKID: PMK material for PMKSA caching.
+ * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can
+ *	cache, a wiphy attribute.
+ *
+ * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32.
+ * @NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION: Device attribute that
+ *	specifies the maximum duration that can be requested with the
+ *	remain-on-channel operation, in milliseconds, u32.
+ *
+ * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects.
+ *
+ * @NL80211_ATTR_TX_RATES: Nested set of attributes
+ *	(enum nl80211_tx_rate_attributes) describing TX rates per band. The
+ *	enum nl80211_band value is used as the index (nla_type() of the nested
+ *	data. If a band is not included, it will be configured to allow all
+ *	rates based on negotiated supported rates information. This attribute
+ *	is used with %NL80211_CMD_SET_TX_BITRATE_MASK.
+ *
+ * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain
+ *	at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME.
+ * @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the
+ *	@NL80211_CMD_REGISTER_FRAME command.
+ * @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a
+ *	nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing
+ *	information about which frame types can be transmitted with
+ *	%NL80211_CMD_FRAME.
+ * @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a
+ *	nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing
+ *	information about which frame types can be registered for RX.
+ *
+ * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
+ *	acknowledged by the recipient.
+ *
+ * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values.
+ *
+ * @NL80211_ATTR_CQM: connection quality monitor configuration in a
+ *	nested attribute with %NL80211_ATTR_CQM_* sub-attributes.
+ *
+ * @NL80211_ATTR_LOCAL_STATE_CHANGE: Flag attribute to indicate that a command
+ *	is requesting a local authentication/association state change without
+ *	invoking actual management frame exchange. This can be used with
+ *	NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE,
+ *	NL80211_CMD_DISASSOCIATE.
+ *
+ * @NL80211_ATTR_AP_ISOLATE: (AP mode) Do not forward traffic between stations
+ *	connected to this BSS.
+ *
+ * @NL80211_ATTR_WIPHY_TX_POWER_SETTING: Transmit power setting type. See
+ *      &enum nl80211_tx_power_setting for possible values.
+ * @NL80211_ATTR_WIPHY_TX_POWER_LEVEL: Transmit power level in signed mBm units.
+ *      This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING
+ *      for non-automatic settings.
+ *
+ * @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly
+ *	means support for per-station GTKs.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_TX: Bitmap of allowed antennas for transmitting.
+ *	This can be used to mask out antennas which are not attached or should
+ *	not be used for transmitting. If an antenna is not selected in this
+ *	bitmap the hardware is not allowed to transmit on this antenna.
+ *
+ *	Each bit represents one antenna, starting with antenna 1 at the first
+ *	bit. Depending on which antennas are selected in the bitmap, 802.11n
+ *	drivers can derive which chainmasks to use (if all antennas belonging to
+ *	a particular chain are disabled this chain should be disabled) and if
+ *	a chain has diversity antennas wether diversity should be used or not.
+ *	HT capabilities (STBC, TX Beamforming, Antenna selection) can be
+ *	derived from the available chains after applying the antenna mask.
+ *	Non-802.11n drivers can derive wether to use diversity or not.
+ *	Drivers may reject configurations or RX/TX mask combinations they cannot
+ *	support by returning -EINVAL.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_RX: Bitmap of allowed antennas for receiving.
+ *	This can be used to mask out antennas which are not attached or should
+ *	not be used for receiving. If an antenna is not selected in this bitmap
+ *	the hardware should not be configured to receive on this antenna.
+ *	For a more detailed description see @NL80211_ATTR_WIPHY_ANTENNA_TX.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: Bitmap of antennas which are available
+ *	for configuration as TX antennas via the above parameters.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available
+ *	for configuration as RX antennas via the above parameters.
+ *
+ * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS
+ *
+ * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be
+ *	transmitted on another channel when the channel given doesn't match
+ *	the current channel. If the current channel doesn't match and this
+ *	flag isn't set, the frame will be rejected. This is also used as an
+ *	nl80211 capability flag.
+ *
+ * @NL80211_ATTR_BSS_HT_OPMODE: HT operation mode (u16)
+ *
+ * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags
+ *	attributes, specifying what a key should be set as default as.
+ *	See &enum nl80211_key_default_types.
+ *
+ * @NL80211_ATTR_MESH_SETUP: Optional mesh setup parameters.  These cannot be
+ *	changed once the mesh is active.
+ * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute
+ *	containing attributes from &enum nl80211_meshconf_params.
+ * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver
+ *	allows auth frames in a mesh to be passed to userspace for processing via
+ *	the @NL80211_MESH_SETUP_USERSPACE_AUTH flag.
+ * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as defined in
+ *	&enum nl80211_plink_state. Used when userspace is driving the peer link
+ *	management state machine.  @NL80211_MESH_SETUP_USERSPACE_AMPE or
+ *	@NL80211_MESH_SETUP_USERSPACE_MPM must be enabled.
+ *
+ * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy
+ *	capabilities, the supported WoWLAN triggers
+ * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to
+ *	indicate which WoW triggers should be enabled. This is also
+ *	used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN
+ *	triggers.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan
+ *	cycles, in msecs.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more
+ *	sets of attributes to match during scheduled scans.  Only BSSs
+ *	that match any of the sets will be reported.  These are
+ *	pass-thru filter rules.
+ *	For a match to succeed, the BSS must match all attributes of a
+ *	set.  Since not every hardware supports matching all types of
+ *	attributes, there is no guarantee that the reported BSSs are
+ *	fully complying with the match sets and userspace needs to be
+ *	able to ignore them by itself.
+ *	Thus, the implementation is somewhat hardware-dependent, but
+ *	this is only an optimization and the userspace application
+ *	needs to handle all the non-filtered results anyway.
+ *	If the match attributes don't make sense when combined with
+ *	the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID
+ *	is included in the probe request, but the match attributes
+ *	will never let it go through), -EINVAL may be returned.
+ *	If ommited, no filtering is done.
+ *
+ * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
+ *	interface combinations. In each nested item, it contains attributes
+ *	defined in &enum nl80211_if_combination_attrs.
+ * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like
+ *	%NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that
+ *	are managed in software: interfaces of these types aren't subject to
+ *	any restrictions in their number or combinations.
+ *
+ * @NL80211_ATTR_REKEY_DATA: nested attribute containing the information
+ *	necessary for GTK rekeying in the device, see &enum nl80211_rekey_data.
+ *
+ * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan,
+ *	nested array attribute containing an entry for each band, with the entry
+ *	being a list of supported rates as defined by IEEE 802.11 7.3.2.2 but
+ *	without the length restriction (at most %NL80211_MAX_SUPP_RATES).
+ *
+ * @NL80211_ATTR_HIDDEN_SSID: indicates whether SSID is to be hidden from Beacon
+ *	and Probe Response (when response to wildcard Probe Request); see
+ *	&enum nl80211_hidden_ssid, represented as a u32
+ *
+ * @NL80211_ATTR_IE_PROBE_RESP: Information element(s) for Probe Response frame.
+ *	This is used with %NL80211_CMD_NEW_BEACON and %NL80211_CMD_SET_BEACON to
+ *	provide extra IEs (e.g., WPS/P2P IE) into Probe Response frames when the
+ *	driver (or firmware) replies to Probe Request frames.
+ * @NL80211_ATTR_IE_ASSOC_RESP: Information element(s) for (Re)Association
+ *	Response frames. This is used with %NL80211_CMD_NEW_BEACON and
+ *	%NL80211_CMD_SET_BEACON to provide extra IEs (e.g., WPS/P2P IE) into
+ *	(Re)Association Response frames when the driver (or firmware) replies to
+ *	(Re)Association Request frames.
+ *
+ * @NL80211_ATTR_STA_WME: Nested attribute containing the wme configuration
+ *	of the station, see &enum nl80211_sta_wme_attr.
+ * @NL80211_ATTR_SUPPORT_AP_UAPSD: the device supports uapsd when working
+ *	as AP.
+ *
+ * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of
+ *	roaming to another AP in the same ESS if the signal lever is low.
+ *
+ * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching
+ *	candidate information, see &enum nl80211_pmksa_candidate_attr.
+ *
+ * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not
+ *	for management frames transmission. In order to avoid p2p probe/action
+ *	frames are being transmitted at CCK rate in 2GHz band, the user space
+ *	applications use this attribute.
+ *	This attribute is used with %NL80211_CMD_TRIGGER_SCAN and
+ *	%NL80211_CMD_FRAME commands.
+ *
+ * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup
+ *	request, link setup confirm, link teardown, etc.). Values are
+ *	described in the TDLS (802.11z) specification.
+ * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a
+ *	TDLS conversation between two devices.
+ * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see
+ *	&enum nl80211_tdls_operation, represented as a u8.
+ * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate
+ *	as a TDLS peer sta.
+ * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown
+ *	procedures should be performed by sending TDLS packets via
+ *	%NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be
+ *	used for asking the driver to perform a TDLS operation.
+ *
+ * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices
+ *	that have AP support to indicate that they have the AP SME integrated
+ *	with support for the features listed in this attribute, see
+ *	&enum nl80211_ap_sme_features.
+ *
+ * @NL80211_ATTR_DONT_WAIT_FOR_ACK: Used with %NL80211_CMD_FRAME, this tells
+ *	the driver to not wait for an acknowledgement. Note that due to this,
+ *	it will also not give a status callback nor return a cookie. This is
+ *	mostly useful for probe responses to save airtime.
+ *
+ * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from
+ *	&enum nl80211_feature_flags and is advertised in wiphy information.
+ * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe
+ *	requests while operating in AP-mode.
+ *	This attribute holds a bitmap of the supported protocols for
+ *	offloading (see &enum nl80211_probe_resp_offload_support_attr).
+ *
+ * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire
+ *	probe-response frame. The DA field in the 802.11 header is zero-ed out,
+ *	to be filled by the FW.
+ * @NL80211_ATTR_DISABLE_HT:  Force HT capable interfaces to disable
+ *      this feature.  Currently, only supported in mac80211 drivers.
+ * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the
+ *      ATTR_HT_CAPABILITY to which attention should be paid.
+ *      Currently, only mac80211 NICs support this feature.
+ *      The values that may be configured are:
+ *       MCS rates, MAX-AMSDU, HT-20-40 and HT_CAP_SGI_40
+ *       AMPDU density and AMPDU factor.
+ *      All values are treated as suggestions and may be ignored
+ *      by the driver as required.  The actual values may be seen in
+ *      the station debugfs ht_caps file.
+ *
+ * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country
+ *    abides to when initiating radiation on DFS channels. A country maps
+ *    to one DFS region.
+ *
+ * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of
+ *      up to 16 TIDs.
+ *
+ * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be
+ *	used by the drivers which has MLME in firmware and does not have support
+ *	to report per station tx/rx activity to free up the staion entry from
+ *	the list. This needs to be used when the driver advertises the
+ *	capability to timeout the stations.
+ *
+ * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int);
+ *	this attribute is (depending on the driver capabilities) added to
+ *	received frames indicated with %NL80211_CMD_FRAME.
+ *
+ * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds
+ *      or 0 to disable background scan.
+ *
+ * @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from
+ *	userspace. If unset it is assumed the hint comes directly from
+ *	a user. If set code could specify exactly what type of source
+ *	was used to provide the hint. For the different types of
+ *	allowed user regulatory hints see nl80211_user_reg_hint_type.
+ *
+ * @NL80211_ATTR_CONN_FAILED_REASON: The reason for which AP has rejected
+ *	the connection request from a station. nl80211_connect_failed_reason
+ *	enum has different reasons of connection failure.
+ *
+ * @NL80211_ATTR_SAE_DATA: SAE elements in Authentication frames. This starts
+ *	with the Authentication transaction sequence number field.
+ *
+ * @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from
+ *	association request when used with NL80211_CMD_NEW_STATION)
+ *
+ * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32)
+ *
+ * @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with
+ *	the START_AP and SET_BSS commands
+ * @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the
+ *	START_AP and SET_BSS commands. This can have the values 0 or 1;
+ *	if not given in START_AP 0 is assumed, if not given in SET_BSS
+ *	no change is made.
+ *
+ * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode
+ *	defined in &enum nl80211_mesh_power_mode.
+ *
+ * @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy,
+ *	carried in a u32 attribute
+ *
+ * @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for
+ *	MAC ACL.
+ *
+ * @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum
+ *	number of MAC addresses that a device can support for MAC
+ *	ACL.
+ *
+ * @NL80211_ATTR_RADAR_EVENT: Type of radar event for notification to userspace,
+ *	contains a value of enum nl80211_radar_event (u32).
+ *
+ * @NL80211_ATTR_EXT_CAPA: 802.11 extended capabilities that the kernel driver
+ *	has and handles. The format is the same as the IE contents. See
+ *	802.11-2012 8.4.2.29 for more information.
+ * @NL80211_ATTR_EXT_CAPA_MASK: Extended capabilities that the kernel driver
+ *	has set in the %NL80211_ATTR_EXT_CAPA value, for multibit fields.
+ *
+ * @NL80211_ATTR_STA_CAPABILITY: Station capabilities (u16) are advertised to
+ *	the driver, e.g., to enable TDLS power save (PU-APSD).
+ *
+ * @NL80211_ATTR_STA_EXT_CAPABILITY: Station extended capabilities are
+ *	advertised to the driver, e.g., to enable TDLS off channel operations
+ *	and PU-APSD.
+ *
+ * @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see
+ *	&enum nl80211_protocol_features, the attribute is a u32.
+ *
+ * @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports
+ *	receiving the data for a single wiphy split across multiple
+ *	messages, given with wiphy dump message
+ *
+ * @NL80211_ATTR_MDID: Mobility Domain Identifier
+ *
+ * @NL80211_ATTR_IE_RIC: Resource Information Container Information
+ *	Element
+ *
+ * @NL80211_ATTR_CRIT_PROT_ID: critical protocol identifier requiring increased
+ *	reliability, see &enum nl80211_crit_proto_id (u16).
+ * @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which
+ *      the connection should have increased reliability (u16).
+ *
+ * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16).
+ *	This is similar to @NL80211_ATTR_STA_AID but with a difference of being
+ *	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: An array of offsets (u16) to the channel
+ *	switch counters in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CSA_C_OFF_PRESP: An array of offsets (u16) to the channel
+ *	switch counters 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_CSA_C_OFFSETS_TX: An array of csa counter offsets (u16) which
+ *	should be updated when the frame is transmitted.
+ * @NL80211_ATTR_MAX_CSA_COUNTERS: U8 attribute used to advertise the maximum
+ *	supported number of csa counters.
+ *
+ * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
+ *	As specified in the &enum nl80211_tdls_peer_capability.
+ *
+ * @NL80211_ATTR_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.
+ *	If set during scheduled scan start then the new scan req will be
+ *	owned by the netlink socket that created it and the scheduled scan will
+ *	be stopped when the socket is closed.
+ *	If set during configuration of regulatory indoor operation then the
+ *	regulatory indoor configuration would be owned by the netlink socket
+ *	that configured the indoor setting, and the indoor operation would be
+ *	cleared when the socket is closed.
+ *
+ * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
+ *	the TDLS link initiator.
+ *
+ * @NL80211_ATTR_USE_RRM: flag for indicating whether the current connection
+ *	shall support Radio Resource Measurements (11k). This attribute can be
+ *	used with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests.
+ *	User space applications are expected to use this flag only if the
+ *	underlying device supports these minimal RRM features:
+ *		%NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES,
+ *		%NL80211_FEATURE_QUIET,
+ *	If this flag is used, driver must add the Power Capabilities IE to the
+ *	association request. In addition, it must also set the RRM capability
+ *	flag in the association request's Capability Info field.
+ *
+ * @NL80211_ATTR_WIPHY_DYN_ACK: flag attribute used to enable ACK timeout
+ *	estimation algorithm (dynack). In order to activate dynack
+ *	%NL80211_FEATURE_ACKTO_ESTIMATION feature flag must be set by lower
+ *	drivers to indicate dynack capability. Dynack is automatically disabled
+ *	setting valid value for coverage class.
+ *
+ * @NL80211_ATTR_TSID: a TSID value (u8 attribute)
+ * @NL80211_ATTR_USER_PRIO: user priority value (u8 attribute)
+ * @NL80211_ATTR_ADMITTED_TIME: admitted time in units of 32 microseconds
+ *	(per second) (u16 attribute)
+ *
+ * @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see
+ *	&enum nl80211_smps_mode.
+ *
+ * @NL80211_ATTR_OPER_CLASS: operating class
+ *
+ * @NL80211_ATTR_MAC_MASK: MAC address mask
+ *
+ * @NL80211_ATTR_WIPHY_SELF_MANAGED_REG: flag attribute indicating this device
+ *	is self-managing its regulatory information and any regulatory domain
+ *	obtained from it is coming from the device's wiphy and not the global
+ *	cfg80211 regdomain.
+ *
+ * @NL80211_ATTR_EXT_FEATURES: extended feature flags contained in a byte
+ *	array. The feature flags are identified by their bit index (see &enum
+ *	nl80211_ext_feature_index). The bit index is ordered starting at the
+ *	least-significant bit of the first byte in the array, ie. bit index 0
+ *	is located at bit 0 of byte 0. bit index 25 would be located at bit 1
+ *	of byte 3 (u8 array).
+ *
+ * @NL80211_ATTR_SURVEY_RADIO_STATS: Request overall radio statistics to be
+ *	returned along with other survey data. If set, @NL80211_CMD_GET_SURVEY
+ *	may return a survey entry without a channel indicating global radio
+ *	statistics (only some values are valid and make sense.)
+ *	For devices that don't return such an entry even then, the information
+ *	should be contained in the result as the sum of the respective counters
+ *	over all channels.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before a scheduled scan (or a
+ *	WoWLAN net-detect scan) is started, u32 in seconds.
+
+ * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
+ *      is operating in an indoor environment.
+ *
+ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+ * @NL80211_ATTR_MAX: highest attribute number currently defined
+ * @__NL80211_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_attrs {
+/* don't change the order or add anything between, this is ABI! */
+	NL80211_ATTR_UNSPEC,
+
+	NL80211_ATTR_WIPHY,
+	NL80211_ATTR_WIPHY_NAME,
+
+	NL80211_ATTR_IFINDEX,
+	NL80211_ATTR_IFNAME,
+	NL80211_ATTR_IFTYPE,
+
+	NL80211_ATTR_MAC,
+
+	NL80211_ATTR_KEY_DATA,
+	NL80211_ATTR_KEY_IDX,
+	NL80211_ATTR_KEY_CIPHER,
+	NL80211_ATTR_KEY_SEQ,
+	NL80211_ATTR_KEY_DEFAULT,
+
+	NL80211_ATTR_BEACON_INTERVAL,
+	NL80211_ATTR_DTIM_PERIOD,
+	NL80211_ATTR_BEACON_HEAD,
+	NL80211_ATTR_BEACON_TAIL,
+
+	NL80211_ATTR_STA_AID,
+	NL80211_ATTR_STA_FLAGS,
+	NL80211_ATTR_STA_LISTEN_INTERVAL,
+	NL80211_ATTR_STA_SUPPORTED_RATES,
+	NL80211_ATTR_STA_VLAN,
+	NL80211_ATTR_STA_INFO,
+
+	NL80211_ATTR_WIPHY_BANDS,
+
+	NL80211_ATTR_MNTR_FLAGS,
+
+	NL80211_ATTR_MESH_ID,
+	NL80211_ATTR_STA_PLINK_ACTION,
+	NL80211_ATTR_MPATH_NEXT_HOP,
+	NL80211_ATTR_MPATH_INFO,
+
+	NL80211_ATTR_BSS_CTS_PROT,
+	NL80211_ATTR_BSS_SHORT_PREAMBLE,
+	NL80211_ATTR_BSS_SHORT_SLOT_TIME,
+
+	NL80211_ATTR_HT_CAPABILITY,
+
+	NL80211_ATTR_SUPPORTED_IFTYPES,
+
+	NL80211_ATTR_REG_ALPHA2,
+	NL80211_ATTR_REG_RULES,
+
+	NL80211_ATTR_MESH_CONFIG,
+
+	NL80211_ATTR_BSS_BASIC_RATES,
+
+	NL80211_ATTR_WIPHY_TXQ_PARAMS,
+	NL80211_ATTR_WIPHY_FREQ,
+	NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+
+	NL80211_ATTR_KEY_DEFAULT_MGMT,
+
+	NL80211_ATTR_MGMT_SUBTYPE,
+	NL80211_ATTR_IE,
+
+	NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
+
+	NL80211_ATTR_SCAN_FREQUENCIES,
+	NL80211_ATTR_SCAN_SSIDS,
+	NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */
+	NL80211_ATTR_BSS,
+
+	NL80211_ATTR_REG_INITIATOR,
+	NL80211_ATTR_REG_TYPE,
+
+	NL80211_ATTR_SUPPORTED_COMMANDS,
+
+	NL80211_ATTR_FRAME,
+	NL80211_ATTR_SSID,
+	NL80211_ATTR_AUTH_TYPE,
+	NL80211_ATTR_REASON_CODE,
+
+	NL80211_ATTR_KEY_TYPE,
+
+	NL80211_ATTR_MAX_SCAN_IE_LEN,
+	NL80211_ATTR_CIPHER_SUITES,
+
+	NL80211_ATTR_FREQ_BEFORE,
+	NL80211_ATTR_FREQ_AFTER,
+
+	NL80211_ATTR_FREQ_FIXED,
+
+
+	NL80211_ATTR_WIPHY_RETRY_SHORT,
+	NL80211_ATTR_WIPHY_RETRY_LONG,
+	NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+	NL80211_ATTR_WIPHY_RTS_THRESHOLD,
+
+	NL80211_ATTR_TIMED_OUT,
+
+	NL80211_ATTR_USE_MFP,
+
+	NL80211_ATTR_STA_FLAGS2,
+
+	NL80211_ATTR_CONTROL_PORT,
+
+	NL80211_ATTR_TESTDATA,
+
+	NL80211_ATTR_PRIVACY,
+
+	NL80211_ATTR_DISCONNECTED_BY_AP,
+	NL80211_ATTR_STATUS_CODE,
+
+	NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+	NL80211_ATTR_CIPHER_SUITE_GROUP,
+	NL80211_ATTR_WPA_VERSIONS,
+	NL80211_ATTR_AKM_SUITES,
+
+	NL80211_ATTR_REQ_IE,
+	NL80211_ATTR_RESP_IE,
+
+	NL80211_ATTR_PREV_BSSID,
+
+	NL80211_ATTR_KEY,
+	NL80211_ATTR_KEYS,
+
+	NL80211_ATTR_PID,
+
+	NL80211_ATTR_4ADDR,
+
+	NL80211_ATTR_SURVEY_INFO,
+
+	NL80211_ATTR_PMKID,
+	NL80211_ATTR_MAX_NUM_PMKIDS,
+
+	NL80211_ATTR_DURATION,
+
+	NL80211_ATTR_COOKIE,
+
+	NL80211_ATTR_WIPHY_COVERAGE_CLASS,
+
+	NL80211_ATTR_TX_RATES,
+
+	NL80211_ATTR_FRAME_MATCH,
+
+	NL80211_ATTR_ACK,
+
+	NL80211_ATTR_PS_STATE,
+
+	NL80211_ATTR_CQM,
+
+	NL80211_ATTR_LOCAL_STATE_CHANGE,
+
+	NL80211_ATTR_AP_ISOLATE,
+
+	NL80211_ATTR_WIPHY_TX_POWER_SETTING,
+	NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
+
+	NL80211_ATTR_TX_FRAME_TYPES,
+	NL80211_ATTR_RX_FRAME_TYPES,
+	NL80211_ATTR_FRAME_TYPE,
+
+	NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+	NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT,
+
+	NL80211_ATTR_SUPPORT_IBSS_RSN,
+
+	NL80211_ATTR_WIPHY_ANTENNA_TX,
+	NL80211_ATTR_WIPHY_ANTENNA_RX,
+
+	NL80211_ATTR_MCAST_RATE,
+
+	NL80211_ATTR_OFFCHANNEL_TX_OK,
+
+	NL80211_ATTR_BSS_HT_OPMODE,
+
+	NL80211_ATTR_KEY_DEFAULT_TYPES,
+
+	NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
+
+	NL80211_ATTR_MESH_SETUP,
+
+	NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
+	NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
+
+	NL80211_ATTR_SUPPORT_MESH_AUTH,
+	NL80211_ATTR_STA_PLINK_STATE,
+
+	NL80211_ATTR_WOWLAN_TRIGGERS,
+	NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED,
+
+	NL80211_ATTR_SCHED_SCAN_INTERVAL,
+
+	NL80211_ATTR_INTERFACE_COMBINATIONS,
+	NL80211_ATTR_SOFTWARE_IFTYPES,
+
+	NL80211_ATTR_REKEY_DATA,
+
+	NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
+	NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
+
+	NL80211_ATTR_SCAN_SUPP_RATES,
+
+	NL80211_ATTR_HIDDEN_SSID,
+
+	NL80211_ATTR_IE_PROBE_RESP,
+	NL80211_ATTR_IE_ASSOC_RESP,
+
+	NL80211_ATTR_STA_WME,
+	NL80211_ATTR_SUPPORT_AP_UAPSD,
+
+	NL80211_ATTR_ROAM_SUPPORT,
+
+	NL80211_ATTR_SCHED_SCAN_MATCH,
+	NL80211_ATTR_MAX_MATCH_SETS,
+
+	NL80211_ATTR_PMKSA_CANDIDATE,
+
+	NL80211_ATTR_TX_NO_CCK_RATE,
+
+	NL80211_ATTR_TDLS_ACTION,
+	NL80211_ATTR_TDLS_DIALOG_TOKEN,
+	NL80211_ATTR_TDLS_OPERATION,
+	NL80211_ATTR_TDLS_SUPPORT,
+	NL80211_ATTR_TDLS_EXTERNAL_SETUP,
+
+	NL80211_ATTR_DEVICE_AP_SME,
+
+	NL80211_ATTR_DONT_WAIT_FOR_ACK,
+
+	NL80211_ATTR_FEATURE_FLAGS,
+
+	NL80211_ATTR_PROBE_RESP_OFFLOAD,
+
+	NL80211_ATTR_PROBE_RESP,
+
+	NL80211_ATTR_DFS_REGION,
+
+	NL80211_ATTR_DISABLE_HT,
+	NL80211_ATTR_HT_CAPABILITY_MASK,
+
+	NL80211_ATTR_NOACK_MAP,
+
+	NL80211_ATTR_INACTIVITY_TIMEOUT,
+
+	NL80211_ATTR_RX_SIGNAL_DBM,
+
+	NL80211_ATTR_BG_SCAN_PERIOD,
+
+	NL80211_ATTR_WDEV,
+
+	NL80211_ATTR_USER_REG_HINT_TYPE,
+
+	NL80211_ATTR_CONN_FAILED_REASON,
+
+	NL80211_ATTR_SAE_DATA,
+
+	NL80211_ATTR_VHT_CAPABILITY,
+
+	NL80211_ATTR_SCAN_FLAGS,
+
+	NL80211_ATTR_CHANNEL_WIDTH,
+	NL80211_ATTR_CENTER_FREQ1,
+	NL80211_ATTR_CENTER_FREQ2,
+
+	NL80211_ATTR_P2P_CTWINDOW,
+	NL80211_ATTR_P2P_OPPPS,
+
+	NL80211_ATTR_LOCAL_MESH_POWER_MODE,
+
+	NL80211_ATTR_ACL_POLICY,
+
+	NL80211_ATTR_MAC_ADDRS,
+
+	NL80211_ATTR_MAC_ACL_MAX,
+
+	NL80211_ATTR_RADAR_EVENT,
+
+	NL80211_ATTR_EXT_CAPA,
+	NL80211_ATTR_EXT_CAPA_MASK,
+
+	NL80211_ATTR_STA_CAPABILITY,
+	NL80211_ATTR_STA_EXT_CAPABILITY,
+
+	NL80211_ATTR_PROTOCOL_FEATURES,
+	NL80211_ATTR_SPLIT_WIPHY_DUMP,
+
+	NL80211_ATTR_DISABLE_VHT,
+	NL80211_ATTR_VHT_CAPABILITY_MASK,
+
+	NL80211_ATTR_MDID,
+	NL80211_ATTR_IE_RIC,
+
+	NL80211_ATTR_CRIT_PROT_ID,
+	NL80211_ATTR_MAX_CRIT_PROT_DURATION,
+
+#ifdef BUILD_FEATURE_SCPM
+	NL80211_ATTR_KLVDATA,
+	NL80211_ATTR_KLV_TYPE,
+	NL80211_ATTR_KLV_INTVL,
+	NL80211_ATTR_KLV_INDEX,
+	NL80211_ATTR_KLV_TRIG,
+	NL80211_ATTR_KLV_PAYLOAD,
+#endif
+
+	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_SOCKET_OWNER,
+
+	NL80211_ATTR_CSA_C_OFFSETS_TX,
+	NL80211_ATTR_MAX_CSA_COUNTERS,
+
+	NL80211_ATTR_TDLS_INITIATOR,
+
+	NL80211_ATTR_USE_RRM,
+
+	NL80211_ATTR_WIPHY_DYN_ACK,
+
+	NL80211_ATTR_TSID,
+	NL80211_ATTR_USER_PRIO,
+	NL80211_ATTR_ADMITTED_TIME,
+
+	NL80211_ATTR_SMPS_MODE,
+
+	NL80211_ATTR_OPER_CLASS,
+
+	NL80211_ATTR_MAC_MASK,
+
+	NL80211_ATTR_WIPHY_SELF_MANAGED_REG,
+
+	NL80211_ATTR_EXT_FEATURES,
+
+	NL80211_ATTR_SURVEY_RADIO_STATS,
+
+	NL80211_ATTR_NETNS_FD,
+
+	NL80211_ATTR_SCHED_SCAN_DELAY,
+
+	NL80211_ATTR_REG_INDOOR,
+
+	/* add attributes here, update the policy in nl80211.c */
+
+	__NL80211_ATTR_AFTER_LAST,
+	NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
+	NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
+};
+
+/* source-level API compatibility */
+#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
+#define	NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG
+#define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER
+
+/*
+ * Allow user space programs to use #ifdef on new attributes by defining them
+ * here
+ */
+#define NL80211_CMD_CONNECT NL80211_CMD_CONNECT
+#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY
+#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES
+#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS
+#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ
+#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE
+#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE
+#define NL80211_ATTR_IE NL80211_ATTR_IE
+#define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR
+#define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE
+#define NL80211_ATTR_FRAME NL80211_ATTR_FRAME
+#define NL80211_ATTR_SSID NL80211_ATTR_SSID
+#define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE
+#define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE
+#define NL80211_ATTR_CIPHER_SUITES_PAIRWISE NL80211_ATTR_CIPHER_SUITES_PAIRWISE
+#define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP
+#define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS
+#define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES
+#define NL80211_ATTR_KEY NL80211_ATTR_KEY
+#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS
+#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
+
+#define NL80211_MAX_SUPP_RATES			32
+#define NL80211_MAX_SUPP_HT_RATES		77
+#define NL80211_MAX_SUPP_REG_RULES		64
+#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY	0
+#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY	16
+#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY	24
+#define NL80211_HT_CAPABILITY_LEN		26
+#define NL80211_VHT_CAPABILITY_LEN		12
+
+#define NL80211_MAX_NR_CIPHER_SUITES		5
+#define NL80211_MAX_NR_AKM_SUITES		2
+
+#define NL80211_MIN_REMAIN_ON_CHANNEL_TIME	10
+
+/* default RSSI threshold for scan results if none specified. */
+#define NL80211_SCAN_RSSI_THOLD_OFF		-300
+
+#define NL80211_CQM_TXE_MAX_INTVL		1800
+
+/**
+ * enum nl80211_iftype - (virtual) interface types
+ *
+ * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides
+ * @NL80211_IFTYPE_ADHOC: independent BSS member
+ * @NL80211_IFTYPE_STATION: managed BSS member
+ * @NL80211_IFTYPE_AP: access point
+ * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points; VLAN interfaces
+ *	are a bit special in that they must always be tied to a pre-existing
+ *	AP type interface.
+ * @NL80211_IFTYPE_WDS: wireless distribution interface
+ * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
+ * @NL80211_IFTYPE_MESH_POINT: mesh point
+ * @NL80211_IFTYPE_P2P_CLIENT: P2P client
+ * @NL80211_IFTYPE_P2P_GO: P2P group owner
+ * @NL80211_IFTYPE_P2P_DEVICE: P2P device interface type, this is not a netdev
+ *	and therefore can't be created in the normal ways, use the
+ *	%NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE
+ *	commands to create and destroy one
+ * @NL80211_IF_TYPE_OCB: Outside Context of a BSS
+ *	This mode corresponds to the MIB variable dot11OCBActivated=true
+ * @NL80211_IFTYPE_MAX: highest interface type number currently defined
+ * @NUM_NL80211_IFTYPES: number of defined interface types
+ *
+ * These values are used with the %NL80211_ATTR_IFTYPE
+ * to set the type of an interface.
+ *
+ */
+enum nl80211_iftype {
+	NL80211_IFTYPE_UNSPECIFIED,
+	NL80211_IFTYPE_ADHOC,
+	NL80211_IFTYPE_STATION,
+	NL80211_IFTYPE_AP,
+	NL80211_IFTYPE_AP_VLAN,
+	NL80211_IFTYPE_WDS,
+	NL80211_IFTYPE_MONITOR,
+	NL80211_IFTYPE_MESH_POINT,
+	NL80211_IFTYPE_P2P_CLIENT,
+	NL80211_IFTYPE_P2P_GO,
+	NL80211_IFTYPE_P2P_DEVICE,
+	NL80211_IFTYPE_OCB,
+
+	/* keep last */
+	NUM_NL80211_IFTYPES,
+	NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1
+};
+
+/**
+ * enum nl80211_sta_flags - station flags
+ *
+ * Station flags. When a station is added to an AP interface, it is
+ * assumed to be already associated (and hence authenticated.)
+ *
+ * @__NL80211_STA_FLAG_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X)
+ * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames
+ *	with short barker preamble
+ * @NL80211_STA_FLAG_WME: station is WME/QoS capable
+ * @NL80211_STA_FLAG_MFP: station uses management frame protection
+ * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated
+ * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer -- this flag should
+ *	only be used in managed mode (even in the flags mask). Note that the
+ *	flag can't be changed, it is only valid while adding a station, and
+ *	attempts to change it will silently be ignored (rather than rejected
+ *	as errors.)
+ * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers
+ *	that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a
+ *	previously added station into associated state
+ * @NL80211_STA_FLAG_MAX: highest station flag number currently defined
+ * @__NL80211_STA_FLAG_AFTER_LAST: internal use
+ */
+enum nl80211_sta_flags {
+	__NL80211_STA_FLAG_INVALID,
+	NL80211_STA_FLAG_AUTHORIZED,
+	NL80211_STA_FLAG_SHORT_PREAMBLE,
+	NL80211_STA_FLAG_WME,
+	NL80211_STA_FLAG_MFP,
+	NL80211_STA_FLAG_AUTHENTICATED,
+	NL80211_STA_FLAG_TDLS_PEER,
+	NL80211_STA_FLAG_ASSOCIATED,
+
+	/* keep last */
+	__NL80211_STA_FLAG_AFTER_LAST,
+	NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1
+};
+
+#define NL80211_STA_FLAG_MAX_OLD_API	NL80211_STA_FLAG_TDLS_PEER
+
+/**
+ * struct nl80211_sta_flag_update - station flags mask/set
+ * @mask: mask of station flags to set
+ * @set: which values to set them to
+ *
+ * Both mask and set contain bits as per &enum nl80211_sta_flags.
+ */
+struct nl80211_sta_flag_update {
+	__u32 mask;
+	__u32 set;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_rate_info - bitrate information
+ *
+ * These attribute types are used with %NL80211_STA_INFO_TXRATE
+ * when getting information about the bitrate of a station.
+ * There are 2 attributes for bitrate, a legacy one that represents
+ * a 16-bit value, and new one that represents a 32-bit value.
+ * If the rate value fits into 16 bit, both attributes are reported
+ * with the same value. If the rate is too high to fit into 16 bits
+ * (>6.5535Gbps) only 32-bit attribute is included.
+ * User space tools encouraged to use the 32-bit attribute and fall
+ * back to the 16-bit one for compatibility with older kernels.
+ *
+ * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s)
+ * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8)
+ * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 MHz dualchannel bitrate
+ * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval
+ * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s)
+ * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined
+ * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8)
+ * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8)
+ * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate
+ * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: unused - 80+80 is treated the
+ *	same as 160 for purposes of the bitrates
+ * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate
+ * @NL80211_RATE_INFO_10_MHZ_WIDTH: 10 MHz width - note that this is
+ *	a legacy rate and will be reported as the actual bitrate, i.e.
+ *	half the base (20 MHz) rate
+ * @NL80211_RATE_INFO_5_MHZ_WIDTH: 5 MHz width - note that this is
+ *	a legacy rate and will be reported as the actual bitrate, i.e.
+ *	a quarter of the base (20 MHz) rate
+ * @__NL80211_RATE_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_rate_info {
+	__NL80211_RATE_INFO_INVALID,
+	NL80211_RATE_INFO_BITRATE,
+	NL80211_RATE_INFO_MCS,
+	NL80211_RATE_INFO_40_MHZ_WIDTH,
+	NL80211_RATE_INFO_SHORT_GI,
+	NL80211_RATE_INFO_BITRATE32,
+	NL80211_RATE_INFO_VHT_MCS,
+	NL80211_RATE_INFO_VHT_NSS,
+	NL80211_RATE_INFO_80_MHZ_WIDTH,
+	NL80211_RATE_INFO_80P80_MHZ_WIDTH,
+	NL80211_RATE_INFO_160_MHZ_WIDTH,
+	NL80211_RATE_INFO_10_MHZ_WIDTH,
+	NL80211_RATE_INFO_5_MHZ_WIDTH,
+
+	/* keep last */
+	__NL80211_RATE_INFO_AFTER_LAST,
+	NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sta_bss_param - BSS information collected by STA
+ *
+ * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM
+ * when getting information about the bitrate of a station.
+ *
+ * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag)
+ * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE:  whether short preamble is enabled
+ *	(flag)
+ * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME:  whether short slot time is enabled
+ *	(flag)
+ * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8)
+ * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16)
+ * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined
+ * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use
+ */
+enum nl80211_sta_bss_param {
+	__NL80211_STA_BSS_PARAM_INVALID,
+	NL80211_STA_BSS_PARAM_CTS_PROT,
+	NL80211_STA_BSS_PARAM_SHORT_PREAMBLE,
+	NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME,
+	NL80211_STA_BSS_PARAM_DTIM_PERIOD,
+	NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
+
+	/* keep last */
+	__NL80211_STA_BSS_PARAM_AFTER_LAST,
+	NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sta_info - station information
+ *
+ * These attribute types are used with %NL80211_ATTR_STA_INFO
+ * when getting information about a station.
+ *
+ * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
+ * @NL80211_STA_INFO_RX_BYTES: total received bytes (MPDU length)
+ *	(u32, from this station)
+ * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (MPDU length)
+ *	(u32, to this station)
+ * @NL80211_STA_INFO_RX_BYTES64: total received bytes (MPDU length)
+ *	(u64, from this station)
+ * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (MPDU length)
+ *	(u64, to this station)
+ * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
+ * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
+ * 	containing info as possible, see &enum nl80211_rate_info
+ * @NL80211_STA_INFO_RX_PACKETS: total received packet (MSDUs and MMPDUs)
+ *	(u32, from this station)
+ * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (MSDUs and MMPDUs)
+ *	(u32, to this station)
+ * @NL80211_STA_INFO_TX_RETRIES: total retries (MPDUs) (u32, to this station)
+ * @NL80211_STA_INFO_TX_FAILED: total failed packets (MPDUs)
+ *	(u32, to this station)
+ * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm)
+ * @NL80211_STA_INFO_LLID: the station's mesh LLID
+ * @NL80211_STA_INFO_PLID: the station's mesh PLID
+ * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station
+ *	(see %enum nl80211_plink_state)
+ * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested
+ *	attribute, like NL80211_STA_INFO_TX_BITRATE.
+ * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute
+ *     containing info as possible, see &enum nl80211_sta_bss_param
+ * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected
+ * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update.
+ * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32)
+ * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64)
+ * @NL80211_STA_INFO_LOCAL_PM: local mesh STA link-specific power mode
+ * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode
+ * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards
+ *	non-peer STA
+ * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU
+ *	Contains a nested array of signal strength attributes (u8, dBm)
+ * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average
+ *	Same format as NL80211_STA_INFO_CHAIN_SIGNAL.
+ * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the
+ *	802.11 header (u32, kbps)
+ * @NL80211_STA_INFO_RX_DROP_MISC: RX packets dropped for unspecified reasons
+ *	(u64)
+ * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64)
+ * @NL80211_STA_INFO_BEACON_SIGNAL_AVG: signal strength average
+ *	for beacons only (u8, dBm)
+ * @NL80211_STA_INFO_TID_STATS: per-TID statistics (see &enum nl80211_tid_stats)
+ *	This is a nested attribute where each the inner attribute number is the
+ *	TID+1 and the special TID 16 (i.e. value 17) is used for non-QoS frames;
+ *	each one of those is again nested with &enum nl80211_tid_stats
+ *	attributes carrying the actual values.
+ * @__NL80211_STA_INFO_AFTER_LAST: internal
+ * @NL80211_STA_INFO_MAX: highest possible station info attribute
+ */
+enum nl80211_sta_info {
+	__NL80211_STA_INFO_INVALID,
+	NL80211_STA_INFO_INACTIVE_TIME,
+	NL80211_STA_INFO_RX_BYTES,
+	NL80211_STA_INFO_TX_BYTES,
+	NL80211_STA_INFO_LLID,
+	NL80211_STA_INFO_PLID,
+	NL80211_STA_INFO_PLINK_STATE,
+	NL80211_STA_INFO_SIGNAL,
+	NL80211_STA_INFO_TX_BITRATE,
+	NL80211_STA_INFO_RX_PACKETS,
+	NL80211_STA_INFO_TX_PACKETS,
+	NL80211_STA_INFO_TX_RETRIES,
+	NL80211_STA_INFO_TX_FAILED,
+	NL80211_STA_INFO_SIGNAL_AVG,
+	NL80211_STA_INFO_RX_BITRATE,
+	NL80211_STA_INFO_BSS_PARAM,
+	NL80211_STA_INFO_CONNECTED_TIME,
+	NL80211_STA_INFO_STA_FLAGS,
+	NL80211_STA_INFO_BEACON_LOSS,
+	NL80211_STA_INFO_T_OFFSET,
+	NL80211_STA_INFO_LOCAL_PM,
+	NL80211_STA_INFO_PEER_PM,
+	NL80211_STA_INFO_NONPEER_PM,
+	NL80211_STA_INFO_RX_BYTES64,
+	NL80211_STA_INFO_TX_BYTES64,
+	NL80211_STA_INFO_CHAIN_SIGNAL,
+	NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
+	NL80211_STA_INFO_EXPECTED_THROUGHPUT,
+	NL80211_STA_INFO_RX_DROP_MISC,
+	NL80211_STA_INFO_BEACON_RX,
+	NL80211_STA_INFO_BEACON_SIGNAL_AVG,
+	NL80211_STA_INFO_TID_STATS,
+
+	/* keep last */
+	__NL80211_STA_INFO_AFTER_LAST,
+	NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_tid_stats - per TID statistics attributes
+ * @__NL80211_TID_STATS_INVALID: attribute number 0 is reserved
+ * @NL80211_TID_STATS_RX_MSDU: number of MSDUs received (u64)
+ * @NL80211_TID_STATS_TX_MSDU: number of MSDUs transmitted (or
+ *	attempted to transmit; u64)
+ * @NL80211_TID_STATS_TX_MSDU_RETRIES: number of retries for
+ *	transmitted MSDUs (not counting the first attempt; u64)
+ * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted
+ *	MSDUs (u64)
+ * @NUM_NL80211_TID_STATS: number of attributes here
+ * @NL80211_TID_STATS_MAX: highest numbered attribute here
+ */
+enum nl80211_tid_stats {
+	__NL80211_TID_STATS_INVALID,
+	NL80211_TID_STATS_RX_MSDU,
+	NL80211_TID_STATS_TX_MSDU,
+	NL80211_TID_STATS_TX_MSDU_RETRIES,
+	NL80211_TID_STATS_TX_MSDU_FAILED,
+
+	/* keep last */
+	NUM_NL80211_TID_STATS,
+	NL80211_TID_STATS_MAX = NUM_NL80211_TID_STATS - 1
+};
+
+/**
+ * enum nl80211_mpath_flags - nl80211 mesh path flags
+ *
+ * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active
+ * @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running
+ * @NL80211_MPATH_FLAG_SN_VALID: the mesh path contains a valid SN
+ * @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set
+ * @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded
+ */
+enum nl80211_mpath_flags {
+	NL80211_MPATH_FLAG_ACTIVE =	1<<0,
+	NL80211_MPATH_FLAG_RESOLVING =	1<<1,
+	NL80211_MPATH_FLAG_SN_VALID =	1<<2,
+	NL80211_MPATH_FLAG_FIXED =	1<<3,
+	NL80211_MPATH_FLAG_RESOLVED =	1<<4,
+};
+
+/**
+ * enum nl80211_mpath_info - mesh path information
+ *
+ * These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting
+ * information about a mesh path.
+ *
+ * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_MPATH_INFO_FRAME_QLEN: number of queued frames for this destination
+ * @NL80211_MPATH_INFO_SN: destination sequence number
+ * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path
+ * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now
+ * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in
+ * 	&enum nl80211_mpath_flags;
+ * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
+ * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries
+ * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number
+ *	currently defind
+ * @__NL80211_MPATH_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_mpath_info {
+	__NL80211_MPATH_INFO_INVALID,
+	NL80211_MPATH_INFO_FRAME_QLEN,
+	NL80211_MPATH_INFO_SN,
+	NL80211_MPATH_INFO_METRIC,
+	NL80211_MPATH_INFO_EXPTIME,
+	NL80211_MPATH_INFO_FLAGS,
+	NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
+	NL80211_MPATH_INFO_DISCOVERY_RETRIES,
+
+	/* keep last */
+	__NL80211_MPATH_INFO_AFTER_LAST,
+	NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_band_attr - band attributes
+ * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band,
+ *	an array of nested frequency attributes
+ * @NL80211_BAND_ATTR_RATES: supported bitrates in this band,
+ *	an array of nested bitrate attributes
+ * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as
+ *	defined in 802.11n
+ * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE
+ * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n
+ * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n
+ * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as
+ *	defined in 802.11ac
+ * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE
+ * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
+ * @__NL80211_BAND_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_band_attr {
+	__NL80211_BAND_ATTR_INVALID,
+	NL80211_BAND_ATTR_FREQS,
+	NL80211_BAND_ATTR_RATES,
+
+	NL80211_BAND_ATTR_HT_MCS_SET,
+	NL80211_BAND_ATTR_HT_CAPA,
+	NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
+	NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
+
+	NL80211_BAND_ATTR_VHT_MCS_SET,
+	NL80211_BAND_ATTR_VHT_CAPA,
+
+	/* keep last */
+	__NL80211_BAND_ATTR_AFTER_LAST,
+	NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
+};
+
+#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA
+
+/**
+ * enum nl80211_frequency_attr - frequency attributes
+ * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz
+ * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled 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
+ *	(100 * dBm).
+ * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS
+ *	(enum nl80211_dfs_state)
+ * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long
+ *	this channel is in this DFS state.
+ * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this
+ *	channel as the control channel
+ * @NL80211_FREQUENCY_ATTR_NO_HT40_PLUS: HT40+ isn't possible with this
+ *	channel as the control channel
+ * @NL80211_FREQUENCY_ATTR_NO_80MHZ: any 80 MHz channel using this channel
+ *	as the primary or any of the secondary channels isn't possible,
+ *	this includes 80+80 channels
+ * @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_NO_IR,
+	__NL80211_FREQUENCY_ATTR_NO_IBSS,
+	NL80211_FREQUENCY_ATTR_RADAR,
+	NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+	NL80211_FREQUENCY_ATTR_DFS_STATE,
+	NL80211_FREQUENCY_ATTR_DFS_TIME,
+	NL80211_FREQUENCY_ATTR_NO_HT40_MINUS,
+	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,
+	NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1
+};
+
+#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
+ * @__NL80211_BITRATE_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps
+ * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported
+ *	in 2.4 GHz band.
+ * @NL80211_BITRATE_ATTR_MAX: highest bitrate attribute number
+ *	currently defined
+ * @__NL80211_BITRATE_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_bitrate_attr {
+	__NL80211_BITRATE_ATTR_INVALID,
+	NL80211_BITRATE_ATTR_RATE,
+	NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE,
+
+	/* keep last */
+	__NL80211_BITRATE_ATTR_AFTER_LAST,
+	NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_initiator - Indicates the initiator of a reg domain request
+ * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world
+ * 	regulatory domain.
+ * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the
+ * 	regulatory domain.
+ * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the
+ * 	wireless core it thinks its knows the regulatory domain we should be in.
+ * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an
+ * 	802.11 country information element with regulatory information it
+ * 	thinks we should consider. cfg80211 only processes the country
+ *	code from the IE, and relies on the regulatory domain information
+ *	structure passed by userspace (CRDA) from our wireless-regdb.
+ *	If a channel is enabled but the country code indicates it should
+ *	be disabled we disable the channel and re-enable it upon disassociation.
+ */
+enum nl80211_reg_initiator {
+	NL80211_REGDOM_SET_BY_CORE,
+	NL80211_REGDOM_SET_BY_USER,
+	NL80211_REGDOM_SET_BY_DRIVER,
+	NL80211_REGDOM_SET_BY_COUNTRY_IE,
+};
+
+/**
+ * enum nl80211_reg_type - specifies the type of regulatory domain
+ * @NL80211_REGDOM_TYPE_COUNTRY: the regulatory domain set is one that pertains
+ *	to a specific country. When this is set you can count on the
+ *	ISO / IEC 3166 alpha2 country code being valid.
+ * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory
+ * 	domain.
+ * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom
+ * 	driver specific world regulatory domain. These do not apply system-wide
+ * 	and are only applicable to the individual devices which have requested
+ * 	them to be applied.
+ * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product
+ *	of an intersection between two regulatory domains -- the previously
+ *	set regulatory domain on the system and the last accepted regulatory
+ *	domain request to be processed.
+ */
+enum nl80211_reg_type {
+	NL80211_REGDOM_TYPE_COUNTRY,
+	NL80211_REGDOM_TYPE_WORLD,
+	NL80211_REGDOM_TYPE_CUSTOM_WORLD,
+	NL80211_REGDOM_TYPE_INTERSECTION,
+};
+
+/**
+ * enum nl80211_reg_rule_attr - regulatory rule attributes
+ * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional
+ * 	considerations for a given frequency range. These are the
+ * 	&enum nl80211_reg_rule_flags.
+ * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory
+ * 	rule in KHz. This is not a center of frequency but an actual regulatory
+ * 	band edge.
+ * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule
+ * 	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.
+ * @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
+ */
+enum nl80211_reg_rule_attr {
+	__NL80211_REG_RULE_ATTR_INVALID,
+	NL80211_ATTR_REG_RULE_FLAGS,
+
+	NL80211_ATTR_FREQ_RANGE_START,
+	NL80211_ATTR_FREQ_RANGE_END,
+	NL80211_ATTR_FREQ_RANGE_MAX_BW,
+
+	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
+};
+
+/**
+ * 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.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a
+ *	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
+ */
+enum nl80211_sched_scan_match_attr {
+	__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID,
+
+	NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
+	NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+
+	/* keep last */
+	__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
+	NL80211_SCHED_SCAN_MATCH_ATTR_MAX =
+		__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1
+};
+
+/* only for backward compatibility */
+#define NL80211_ATTR_SCHED_SCAN_MATCH_SSID NL80211_SCHED_SCAN_MATCH_ATTR_SSID
+
+/**
+ * enum nl80211_reg_rule_flags - regulatory rule flags
+ *
+ * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed
+ * @NL80211_RRF_NO_CCK: CCK modulation not allowed
+ * @NL80211_RRF_NO_INDOOR: indoor operation not allowed
+ * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed
+ * @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_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.
+ * @NL80211_RRF_GO_CONCURRENT: See &NL80211_FREQUENCY_ATTR_GO_CONCURRENT
+ * @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation
+ * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation
+ * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed
+ * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed
+ */
+enum nl80211_reg_rule_flags {
+	NL80211_RRF_NO_OFDM		= 1<<0,
+	NL80211_RRF_NO_CCK		= 1<<1,
+	NL80211_RRF_NO_INDOOR		= 1<<2,
+	NL80211_RRF_NO_OUTDOOR		= 1<<3,
+	NL80211_RRF_DFS			= 1<<4,
+	NL80211_RRF_PTP_ONLY		= 1<<5,
+	NL80211_RRF_PTMP_ONLY		= 1<<6,
+	NL80211_RRF_NO_IR		= 1<<7,
+	__NL80211_RRF_NO_IBSS		= 1<<8,
+	NL80211_RRF_AUTO_BW		= 1<<11,
+	NL80211_RRF_GO_CONCURRENT	= 1<<12,
+	NL80211_RRF_NO_HT40MINUS	= 1<<13,
+	NL80211_RRF_NO_HT40PLUS		= 1<<14,
+	NL80211_RRF_NO_80MHZ		= 1<<15,
+	NL80211_RRF_NO_160MHZ		= 1<<16,
+};
+
+#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
+#define NL80211_RRF_NO_HT40		(NL80211_RRF_NO_HT40MINUS |\
+					 NL80211_RRF_NO_HT40PLUS)
+
+/* 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
+ *
+ * @NL80211_DFS_UNSET: Country has no DFS master region specified
+ * @NL80211_DFS_FCC: Country follows DFS master rules from FCC
+ * @NL80211_DFS_ETSI: Country follows DFS master rules from ETSI
+ * @NL80211_DFS_JP: Country follows DFS master rules from JP/MKK/Telec
+ */
+enum nl80211_dfs_regions {
+	NL80211_DFS_UNSET	= 0,
+	NL80211_DFS_FCC		= 1,
+	NL80211_DFS_ETSI	= 2,
+	NL80211_DFS_JP		= 3,
+};
+
+/**
+ * enum nl80211_user_reg_hint_type - type of user regulatory hint
+ *
+ * @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always
+ *	assumed if the attribute is not set.
+ * @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular
+ *	base station. Device drivers that have been tested to work
+ *	properly to support this type of hint can enable these hints
+ *	by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature
+ *	capability on the struct wiphy. The wireless core will
+ *	ignore all cell base station hints until at least one device
+ *	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,
+};
+
+/**
+ * enum nl80211_survey_info - survey information
+ *
+ * These attribute types are used with %NL80211_ATTR_SURVEY_INFO
+ * when getting information about a survey.
+ *
+ * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel
+ * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm)
+ * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used
+ * @NL80211_SURVEY_INFO_TIME: amount of time (in ms) that the radio
+ *	was turned on (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_BUSY: amount of the time the primary
+ *	channel was sensed busy (either due to activity or energy detect)
+ * @NL80211_SURVEY_INFO_TIME_EXT_BUSY: amount of time the extension
+ *	channel was sensed busy
+ * @NL80211_SURVEY_INFO_TIME_RX: amount of time the radio spent
+ *	receiving data (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_TX: amount of time the radio spent
+ *	transmitting data (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_SCAN: time the radio spent for scan
+ *	(on this channel or globally)
+ * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
+ *	currently defined
+ * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_survey_info {
+	__NL80211_SURVEY_INFO_INVALID,
+	NL80211_SURVEY_INFO_FREQUENCY,
+	NL80211_SURVEY_INFO_NOISE,
+	NL80211_SURVEY_INFO_IN_USE,
+	NL80211_SURVEY_INFO_TIME,
+	NL80211_SURVEY_INFO_TIME_BUSY,
+	NL80211_SURVEY_INFO_TIME_EXT_BUSY,
+	NL80211_SURVEY_INFO_TIME_RX,
+	NL80211_SURVEY_INFO_TIME_TX,
+	NL80211_SURVEY_INFO_TIME_SCAN,
+
+	/* keep last */
+	__NL80211_SURVEY_INFO_AFTER_LAST,
+	NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1
+};
+
+/* keep old names for compatibility */
+#define NL80211_SURVEY_INFO_CHANNEL_TIME		NL80211_SURVEY_INFO_TIME
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY		NL80211_SURVEY_INFO_TIME_BUSY
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY	NL80211_SURVEY_INFO_TIME_EXT_BUSY
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_RX		NL80211_SURVEY_INFO_TIME_RX
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_TX		NL80211_SURVEY_INFO_TIME_TX
+
+/**
+ * enum nl80211_mntr_flags - monitor configuration flags
+ *
+ * Monitor configuration flags.
+ *
+ * @__NL80211_MNTR_FLAG_INVALID: reserved
+ *
+ * @NL80211_MNTR_FLAG_FCSFAIL: pass frames with bad FCS
+ * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP
+ * @NL80211_MNTR_FLAG_CONTROL: pass control frames
+ * @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
+ */
+enum nl80211_mntr_flags {
+	__NL80211_MNTR_FLAG_INVALID,
+	NL80211_MNTR_FLAG_FCSFAIL,
+	NL80211_MNTR_FLAG_PLCPFAIL,
+	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,
+	NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_mesh_power_mode - mesh power save modes
+ *
+ * @NL80211_MESH_POWER_UNKNOWN: The mesh power mode of the mesh STA is
+ *	not known or has not been set yet.
+ * @NL80211_MESH_POWER_ACTIVE: Active mesh power mode. The mesh STA is
+ *	in Awake state all the time.
+ * @NL80211_MESH_POWER_LIGHT_SLEEP: Light sleep mode. The mesh STA will
+ *	alternate between Active and Doze states, but will wake up for
+ *	neighbor's beacons.
+ * @NL80211_MESH_POWER_DEEP_SLEEP: Deep sleep mode. The mesh STA will
+ *	alternate between Active and Doze states, but may not wake up
+ *	for neighbor's beacons.
+ *
+ * @__NL80211_MESH_POWER_AFTER_LAST - internal use
+ * @NL80211_MESH_POWER_MAX - highest possible power save level
+ */
+
+enum nl80211_mesh_power_mode {
+	NL80211_MESH_POWER_UNKNOWN,
+	NL80211_MESH_POWER_ACTIVE,
+	NL80211_MESH_POWER_LIGHT_SLEEP,
+	NL80211_MESH_POWER_DEEP_SLEEP,
+
+	__NL80211_MESH_POWER_AFTER_LAST,
+	NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_meshconf_params - mesh configuration parameters
+ *
+ * Mesh configuration parameters. These can be changed while the mesh is
+ * active.
+ *
+ * @__NL80211_MESHCONF_INVALID: internal use
+ *
+ * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in
+ *	millisecond units, used by the Peer Link Open message
+ *
+ * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in
+ *	millisecond units, used by the peer link management to close a peer link
+ *
+ * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in
+ *	millisecond units
+ *
+ * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed
+ *	on this mesh interface
+ *
+ * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link
+ *	open retries that can be sent to establish a new peer link instance in a
+ *	mesh
+ *
+ * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
+ *	point.
+ *
+ * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically open
+ *	peer links when we detect compatible mesh peers. Disabled if
+ *	@NL80211_MESH_SETUP_USERSPACE_MPM or @NL80211_MESH_SETUP_USERSPACE_AMPE are
+ *	set.
+ *
+ * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames
+ *	containing a PREQ that an MP can send to a particular destination (path
+ *	target)
+ *
+ * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths
+ *	(in milliseconds)
+ *
+ * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait
+ *	until giving up on a path discovery (in milliseconds)
+ *
+ * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh
+ *	points receiving a PREQ shall consider the forwarding information from
+ *	the root to be valid. (TU = time unit)
+ *
+ * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in
+ *	TUs) during which an MP can send only one action frame containing a PREQ
+ *	reference element
+ *
+ * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs)
+ *	that it takes for an HWMP information element to propagate across the
+ *	mesh
+ *
+ * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not
+ *
+ * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a
+ *	source mesh point for path selection elements.
+ *
+ * @NL80211_MESHCONF_HWMP_RANN_INTERVAL:  The interval of time (in TUs) between
+ *	root announcements are transmitted.
+ *
+ * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has
+ *	access to a broader network beyond the MBSS.  This is done via Root
+ *	Announcement frames.
+ *
+ * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in
+ *	TUs) during which a mesh STA can send only one Action frame containing a
+ *	PERR element.
+ *
+ * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding
+ *	or forwarding entity (default is TRUE - forwarding entity)
+ *
+ * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the
+ *	threshold for average signal strength of candidate station to establish
+ *	a peer link.
+ *
+ * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors
+ *	to synchronize to for 11s default synchronization method
+ *	(see 11C.12.2.2)
+ *
+ * @NL80211_MESHCONF_HT_OPMODE: set mesh HT protection mode.
+ *
+ * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
+ *
+ * @NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: The time (in TUs) for
+ *	which mesh STAs receiving a proactive PREQ shall consider the forwarding
+ *	information to the root mesh STA to be valid.
+ *
+ * @NL80211_MESHCONF_HWMP_ROOT_INTERVAL: The interval of time (in TUs) between
+ *	proactive PREQs are transmitted.
+ *
+ * @NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: The minimum interval of time
+ *	(in TUs) during which a mesh STA can send only one Action frame
+ *	containing a PREQ element for root path confirmation.
+ *
+ * @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links.
+ *	type &enum nl80211_mesh_power_mode (u32)
+ *
+ * @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. You may set this to 0 to disable
+ *	the removal of the STA. Default is 30 minutes.
+ *
+ * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_meshconf_params {
+	__NL80211_MESHCONF_INVALID,
+	NL80211_MESHCONF_RETRY_TIMEOUT,
+	NL80211_MESHCONF_CONFIRM_TIMEOUT,
+	NL80211_MESHCONF_HOLDING_TIMEOUT,
+	NL80211_MESHCONF_MAX_PEER_LINKS,
+	NL80211_MESHCONF_MAX_RETRIES,
+	NL80211_MESHCONF_TTL,
+	NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+	NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+	NL80211_MESHCONF_PATH_REFRESH_TIME,
+	NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+	NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+	NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+	NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+	NL80211_MESHCONF_HWMP_ROOTMODE,
+	NL80211_MESHCONF_ELEMENT_TTL,
+	NL80211_MESHCONF_HWMP_RANN_INTERVAL,
+	NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
+	NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
+	NL80211_MESHCONF_FORWARDING,
+	NL80211_MESHCONF_RSSI_THRESHOLD,
+	NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
+	NL80211_MESHCONF_HT_OPMODE,
+	NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
+	NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
+	NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
+	NL80211_MESHCONF_POWER_MODE,
+	NL80211_MESHCONF_AWAKE_WINDOW,
+	NL80211_MESHCONF_PLINK_TIMEOUT,
+
+	/* keep last */
+	__NL80211_MESHCONF_ATTR_AFTER_LAST,
+	NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_mesh_setup_params - mesh setup parameters
+ *
+ * Mesh setup parameters.  These are used to start/join a mesh and cannot be
+ * changed while the mesh is active.
+ *
+ * @__NL80211_MESH_SETUP_INVALID: Internal use
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a
+ *	vendor specific path selection algorithm or disable it to use the
+ *	default HWMP.
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a
+ *	vendor specific path metric or disable it to use the default Airtime
+ *	metric.
+ *
+ * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a
+ *	robust security network ie, or a vendor specific information element
+ *	that vendors will use to identify the path selection methods and
+ *	metrics in use.
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication
+ *	daemon will be authenticating mesh candidates.
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication
+ *	daemon will be securing peer link frames.  AMPE is a secured version of
+ *	Mesh Peering Management (MPM) and is implemented with the assistance of
+ *	a userspace daemon.  When this flag is set, the kernel will send peer
+ *	management frames to a userspace daemon that will implement AMPE
+ *	functionality (security capabilities selection, key confirmation, and
+ *	key management).  When the flag is unset (default), the kernel can
+ *	autonomously complete (unsecured) mesh peering without the need of a
+ *	userspace daemon.
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a
+ *	vendor specific synchronization method or disable it to use the default
+ *	neighbor offset synchronization
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will
+ *	implement an MPM which handles peer allocation and state.
+ *
+ * @NL80211_MESH_SETUP_AUTH_PROTOCOL: Inform the kernel of the authentication
+ *	method (u8, as defined in IEEE 8.4.2.100.6, e.g. 0x1 for SAE).
+ *	Default is no authentication method required.
+ *
+ * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
+ *
+ * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
+ */
+enum nl80211_mesh_setup_params {
+	__NL80211_MESH_SETUP_INVALID,
+	NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL,
+	NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC,
+	NL80211_MESH_SETUP_IE,
+	NL80211_MESH_SETUP_USERSPACE_AUTH,
+	NL80211_MESH_SETUP_USERSPACE_AMPE,
+	NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC,
+	NL80211_MESH_SETUP_USERSPACE_MPM,
+	NL80211_MESH_SETUP_AUTH_PROTOCOL,
+
+	/* keep last */
+	__NL80211_MESH_SETUP_ATTR_AFTER_LAST,
+	NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_txq_attr - TX queue parameter attributes
+ * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
+ * @NL80211_TXQ_ATTR_AC: AC identifier (NL80211_AC_*)
+ * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning
+ *	disabled
+ * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form
+ *	2^n-1 in the range 1..32767]
+ * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form
+ *	2^n-1 in the range 1..32767]
+ * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255]
+ * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal
+ * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number
+ */
+enum nl80211_txq_attr {
+	__NL80211_TXQ_ATTR_INVALID,
+	NL80211_TXQ_ATTR_AC,
+	NL80211_TXQ_ATTR_TXOP,
+	NL80211_TXQ_ATTR_CWMIN,
+	NL80211_TXQ_ATTR_CWMAX,
+	NL80211_TXQ_ATTR_AIFS,
+
+	/* keep last */
+	__NL80211_TXQ_ATTR_AFTER_LAST,
+	NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1
+};
+
+enum nl80211_ac {
+	NL80211_AC_VO,
+	NL80211_AC_VI,
+	NL80211_AC_BE,
+	NL80211_AC_BK,
+	NL80211_NUM_ACS
+};
+
+/* backward compat */
+#define NL80211_TXQ_ATTR_QUEUE	NL80211_TXQ_ATTR_AC
+#define NL80211_TXQ_Q_VO	NL80211_AC_VO
+#define NL80211_TXQ_Q_VI	NL80211_AC_VI
+#define NL80211_TXQ_Q_BE	NL80211_AC_BE
+#define NL80211_TXQ_Q_BK	NL80211_AC_BK
+
+/**
+ * enum nl80211_channel_type - channel type
+ * @NL80211_CHAN_NO_HT: 20 MHz, non-HT channel
+ * @NL80211_CHAN_HT20: 20 MHz HT channel
+ * @NL80211_CHAN_HT40MINUS: HT40 channel, secondary channel
+ *	below the control channel
+ * @NL80211_CHAN_HT40PLUS: HT40 channel, secondary channel
+ *	above the control channel
+ */
+enum nl80211_channel_type {
+	NL80211_CHAN_NO_HT,
+	NL80211_CHAN_HT20,
+	NL80211_CHAN_HT40MINUS,
+	NL80211_CHAN_HT40PLUS
+};
+
+/**
+ * enum nl80211_chan_width - channel width definitions
+ *
+ * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH
+ * attribute.
+ *
+ * @NL80211_CHAN_WIDTH_20_NOHT: 20 MHz, non-HT channel
+ * @NL80211_CHAN_WIDTH_20: 20 MHz HT channel
+ * @NL80211_CHAN_WIDTH_40: 40 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_80: 80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_80P80: 80+80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	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,
+	NL80211_CHAN_WIDTH_20,
+	NL80211_CHAN_WIDTH_40,
+	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,
+};
+
+/**
+ * enum nl80211_bss - netlink attributes for a BSS
+ *
+ * @__NL80211_BSS_INVALID: invalid
+ * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets)
+ * @NL80211_BSS_FREQUENCY: frequency in MHz (u32)
+ * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64)
+ *	(if @NL80211_BSS_PRESP_DATA is present then this is known to be
+ *	from a probe response, otherwise it may be from the same beacon
+ *	that the NL80211_BSS_BEACON_TSF will be from)
+ * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16)
+ * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16)
+ * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the
+ *	raw information elements from the probe response/beacon (bin);
+ *	if the %NL80211_BSS_BEACON_IES attribute is present and the data is
+ *	different then the IEs here are from a Probe Response frame; otherwise
+ *	they are from a Beacon frame.
+ *	However, if the driver does not indicate the source of the IEs, these
+ *	IEs may be from either frame subtype.
+ *	If present, the @NL80211_BSS_PRESP_DATA attribute indicates that the
+ *	data here is known to be from a probe response, without any heuristics.
+ * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon
+ *	in mBm (100 * dBm) (s32)
+ * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon
+ *	in unspecified units, scaled to 0..100 (u8)
+ * @NL80211_BSS_STATUS: status, if this BSS is "used"
+ * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms
+ * @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_BEACON_TSF: TSF of the last received beacon (u64)
+ *	(not present if no beacon frame has been received yet)
+ * @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and
+ *	@NL80211_BSS_TSF is known to be from a probe response (flag attribute)
+ * @__NL80211_BSS_AFTER_LAST: internal
+ * @NL80211_BSS_MAX: highest BSS attribute
+ */
+enum nl80211_bss {
+	__NL80211_BSS_INVALID,
+	NL80211_BSS_BSSID,
+	NL80211_BSS_FREQUENCY,
+	NL80211_BSS_TSF,
+	NL80211_BSS_BEACON_INTERVAL,
+	NL80211_BSS_CAPABILITY,
+	NL80211_BSS_INFORMATION_ELEMENTS,
+	NL80211_BSS_SIGNAL_MBM,
+	NL80211_BSS_SIGNAL_UNSPEC,
+	NL80211_BSS_STATUS,
+	NL80211_BSS_SEEN_MS_AGO,
+	NL80211_BSS_BEACON_IES,
+	NL80211_BSS_CHAN_WIDTH,
+	NL80211_BSS_BEACON_TSF,
+	NL80211_BSS_PRESP_DATA,
+
+	/* keep last */
+	__NL80211_BSS_AFTER_LAST,
+	NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_bss_status - BSS "status"
+ * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS.
+ *	Note that this is no longer used since cfg80211 no longer
+ *	keeps track of whether or not authentication was done with
+ *	a given BSS.
+ * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS.
+ * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS.
+ *
+ * The BSS status is a BSS attribute in scan dumps, which
+ * indicates the status the interface has wrt. this BSS.
+ */
+enum nl80211_bss_status {
+	NL80211_BSS_STATUS_AUTHENTICATED,
+	NL80211_BSS_STATUS_ASSOCIATED,
+	NL80211_BSS_STATUS_IBSS_JOINED,
+};
+
+/**
+ * enum nl80211_auth_type - AuthenticationType
+ *
+ * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication
+ * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only)
+ * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r)
+ * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP)
+ * @NL80211_AUTHTYPE_SAE: Simultaneous authentication of equals
+ * @__NL80211_AUTHTYPE_NUM: internal
+ * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm
+ * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by
+ *	trying multiple times); this is invalid in netlink -- leave out
+ *	the attribute for this on CONNECT commands.
+ */
+enum nl80211_auth_type {
+	NL80211_AUTHTYPE_OPEN_SYSTEM,
+	NL80211_AUTHTYPE_SHARED_KEY,
+	NL80211_AUTHTYPE_FT,
+	NL80211_AUTHTYPE_NETWORK_EAP,
+	NL80211_AUTHTYPE_SAE,
+
+	/* keep last */
+	__NL80211_AUTHTYPE_NUM,
+	NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1,
+	NL80211_AUTHTYPE_AUTOMATIC
+};
+
+/**
+ * enum nl80211_key_type - Key Type
+ * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key
+ * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key
+ * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS)
+ * @NUM_NL80211_KEYTYPES: number of defined key types
+ */
+enum nl80211_key_type {
+	NL80211_KEYTYPE_GROUP,
+	NL80211_KEYTYPE_PAIRWISE,
+	NL80211_KEYTYPE_PEERKEY,
+
+	NUM_NL80211_KEYTYPES
+};
+
+/**
+ * enum nl80211_mfp - Management frame protection state
+ * @NL80211_MFP_NO: Management frame protection not used
+ * @NL80211_MFP_REQUIRED: Management frame protection required
+ */
+enum nl80211_mfp {
+	NL80211_MFP_NO,
+	NL80211_MFP_REQUIRED,
+};
+
+enum nl80211_wpa_versions {
+	NL80211_WPA_VERSION_1 = 1 << 0,
+	NL80211_WPA_VERSION_2 = 1 << 1,
+};
+
+/**
+ * enum nl80211_key_default_types - key default types
+ * @__NL80211_KEY_DEFAULT_TYPE_INVALID: invalid
+ * @NL80211_KEY_DEFAULT_TYPE_UNICAST: key should be used as default
+ *	unicast key
+ * @NL80211_KEY_DEFAULT_TYPE_MULTICAST: key should be used as default
+ *	multicast key
+ * @NUM_NL80211_KEY_DEFAULT_TYPES: number of default types
+ */
+enum nl80211_key_default_types {
+	__NL80211_KEY_DEFAULT_TYPE_INVALID,
+	NL80211_KEY_DEFAULT_TYPE_UNICAST,
+	NL80211_KEY_DEFAULT_TYPE_MULTICAST,
+
+	NUM_NL80211_KEY_DEFAULT_TYPES
+};
+
+/**
+ * enum nl80211_key_attributes - key attributes
+ * @__NL80211_KEY_INVALID: invalid
+ * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of
+ *	16 bytes encryption key followed by 8 bytes each for TX and RX MIC
+ *	keys
+ * @NL80211_KEY_IDX: key ID (u8, 0-3)
+ * @NL80211_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11
+ *	section 7.3.2.25.1, e.g. 0x000FAC04)
+ * @NL80211_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and
+ *	CCMP keys, each six bytes in little endian
+ * @NL80211_KEY_DEFAULT: flag indicating default key
+ * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key
+ * @NL80211_KEY_TYPE: the key type from enum nl80211_key_type, if not
+ *	specified the default depends on whether a MAC address was
+ *	given with the command using the key or not (u32)
+ * @NL80211_KEY_DEFAULT_TYPES: A nested attribute containing flags
+ *	attributes, specifying what a key should be set as default as.
+ *	See &enum nl80211_key_default_types.
+ * @__NL80211_KEY_AFTER_LAST: internal
+ * @NL80211_KEY_MAX: highest key attribute
+ */
+enum nl80211_key_attributes {
+	__NL80211_KEY_INVALID,
+	NL80211_KEY_DATA,
+	NL80211_KEY_IDX,
+	NL80211_KEY_CIPHER,
+	NL80211_KEY_SEQ,
+	NL80211_KEY_DEFAULT,
+	NL80211_KEY_DEFAULT_MGMT,
+	NL80211_KEY_TYPE,
+	NL80211_KEY_DEFAULT_TYPES,
+
+	/* keep last */
+	__NL80211_KEY_AFTER_LAST,
+	NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_tx_rate_attributes - TX rate set attributes
+ * @__NL80211_TXRATE_INVALID: invalid
+ * @NL80211_TXRATE_LEGACY: Legacy (non-MCS) rates allowed for TX rate selection
+ *	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_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_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
+ * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
+ * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz)
+ */
+enum nl80211_band {
+	NL80211_BAND_2GHZ,
+	NL80211_BAND_5GHZ,
+	NL80211_BAND_60GHZ,
+};
+
+/**
+ * enum nl80211_ps_state - powersave state
+ * @NL80211_PS_DISABLED: powersave is disabled
+ * @NL80211_PS_ENABLED: powersave is enabled
+ */
+enum nl80211_ps_state {
+	NL80211_PS_DISABLED,
+	NL80211_PS_ENABLED,
+};
+
+/**
+ * enum nl80211_attr_cqm - connection quality monitor attributes
+ * @__NL80211_ATTR_CQM_INVALID: invalid
+ * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies
+ *	the threshold for the RSSI level at which an event will be sent. Zero
+ *	to disable.
+ * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies
+ *	the minimum amount the RSSI level must change after an event before a
+ *	new event may be issued (to reduce effects of RSSI oscillation).
+ * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event
+ * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many
+ *	consecutive packets were not acknowledged by the peer
+ * @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures
+ *	during the given %NL80211_ATTR_CQM_TXE_INTVL before an
+ *	%NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and
+ *	%NL80211_ATTR_CQM_TXE_PKTS is generated.
+ * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given
+ *	%NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is
+ *	checked.
+ * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic
+ *	interval in which %NL80211_ATTR_CQM_TXE_PKTS and
+ *	%NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an
+ *	%NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
+ * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon
+ *	loss event
+ * @__NL80211_ATTR_CQM_AFTER_LAST: internal
+ * @NL80211_ATTR_CQM_MAX: highest key attribute
+ */
+enum nl80211_attr_cqm {
+	__NL80211_ATTR_CQM_INVALID,
+	NL80211_ATTR_CQM_RSSI_THOLD,
+	NL80211_ATTR_CQM_RSSI_HYST,
+	NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+	NL80211_ATTR_CQM_PKT_LOSS_EVENT,
+	NL80211_ATTR_CQM_TXE_RATE,
+	NL80211_ATTR_CQM_TXE_PKTS,
+	NL80211_ATTR_CQM_TXE_INTVL,
+	NL80211_ATTR_CQM_BEACON_LOSS_EVENT,
+
+	/* keep last */
+	__NL80211_ATTR_CQM_AFTER_LAST,
+	NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: The RSSI level is lower than the
+ *      configured threshold
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the
+ *      configured threshold
+ * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: (reserved, never sent)
+ */
+enum nl80211_cqm_rssi_threshold_event {
+	NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+	NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+	NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
+};
+
+
+/**
+ * enum nl80211_tx_power_setting - TX power adjustment
+ * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power
+ * @NL80211_TX_POWER_LIMITED: limit TX power by the mBm parameter
+ * @NL80211_TX_POWER_FIXED: fix TX power to the mBm parameter
+ */
+enum nl80211_tx_power_setting {
+	NL80211_TX_POWER_AUTOMATIC,
+	NL80211_TX_POWER_LIMITED,
+	NL80211_TX_POWER_FIXED,
+};
+
+/**
+ * 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_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
+ *	corresponds to the lowest-order bit in the second byte of the mask.
+ *	For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where
+ *	xx indicates "don't care") would be represented by a pattern of
+ *	twelve zero bytes, and a mask of "0xed,0x01".
+ *	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_PKTPAT_OFFSET: packet offset, pattern is matched after
+ *	these fixed number of bytes of received packet
+ * @NUM_NL80211_PKTPAT: number of attributes
+ * @MAX_NL80211_PKTPAT: max attribute number
+ */
+enum nl80211_packet_pattern_attr {
+	__NL80211_PKTPAT_INVALID,
+	NL80211_PKTPAT_MASK,
+	NL80211_PKTPAT_PATTERN,
+	NL80211_PKTPAT_OFFSET,
+
+	NUM_NL80211_PKTPAT,
+	MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1,
+};
+
+/**
+ * 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 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_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
+ * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put
+ *	the chip into a special state -- works best with chips that have
+ *	support for low-power operation already (flag)
+ *	Note that this mode is incompatible with all of the others, if
+ *	any others are even supported by the device.
+ * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect
+ *	is detected is implementation-specific (flag)
+ * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed
+ *	by 16 repetitions of MAC addr, anywhere in payload) (flag)
+ * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns
+ *	which are passed in an array of nested attributes, each nested attribute
+ *	defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern.
+ *	Each pattern defines a wakeup packet. Packet offset is associated with
+ *	each pattern which is used while matching the pattern. The matching is
+ *	done on the MSDU, i.e. as though the packet was an 802.3 packet, so the
+ *	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_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
+ *	to the kernel when configuring.
+ * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be
+ *	used when setting, used only to indicate that GTK rekeying is supported
+ *	by the device (flag)
+ * @NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: wake up on GTK rekey failure (if
+ *	done by the device) (flag)
+ * @NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: wake up on EAP Identity Request
+ *	packet (flag)
+ * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag)
+ * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released
+ *	(on devices that have rfkill in the device) (flag)
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: For wakeup reporting only, contains
+ *	the 802.11 packet that caused the wakeup, e.g. a deauth frame. The frame
+ *	may be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN
+ *	attribute contains the original length.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: Original length of the 802.11
+ *	packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211
+ *	attribute if the packet was truncated somewhere.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: For wakeup reporting only, contains the
+ *	802.11 packet that caused the wakeup, e.g. a magic packet. The frame may
+ *	be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN attribute
+ *	contains the original length.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: Original length of the 802.3
+ *	packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023
+ *	attribute if the packet was truncated somewhere.
+ * @NL80211_WOWLAN_TRIG_TCP_CONNECTION: TCP connection wake, see DOC section
+ *	"TCP connection wakeup" for more details. This is a nested attribute
+ *	containing the exact information for establishing and keeping alive
+ *	the TCP connection.
+ * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the
+ *	wakeup packet was received on the TCP connection
+ * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the
+ *	TCP connection was lost or failed to be established
+ * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only,
+ *	the TCP connection ran out of tokens to use for data to send to the
+ *	service
+ * @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network
+ *	is detected.  This is a nested attribute that contains the
+ *	same attributes used with @NL80211_CMD_START_SCHED_SCAN.  It
+ *	specifies how the scan is performed (e.g. the interval, the
+ *	channels to scan and the initial delay) as well as the scan
+ *	results that will trigger a wake (i.e. the matchsets).  This
+ *	attribute is also sent in a response to
+ *	@NL80211_CMD_GET_WIPHY, indicating the number of match sets
+ *	supported by the driver (u32).
+ * @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute
+ *	containing an array with information about what triggered the
+ *	wake up.  If no elements are present in the array, it means
+ *	that the information is not available.  If more than one
+ *	element is present, it means that more than one match
+ *	occurred.
+ *	Each element in the array is a nested attribute that contains
+ *	one optional %NL80211_ATTR_SSID attribute and one optional
+ *	%NL80211_ATTR_SCAN_FREQUENCIES attribute.  At least one of
+ *	these attributes must be present.  If
+ *	%NL80211_ATTR_SCAN_FREQUENCIES contains more than one
+ *	frequency, it means that the match occurred in more than one
+ *	channel.
+ * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
+ * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
+ *
+ * These nested attributes are used to configure the wakeup triggers and
+ * to report the wakeup reason(s).
+ */
+enum nl80211_wowlan_triggers {
+	__NL80211_WOWLAN_TRIG_INVALID,
+	NL80211_WOWLAN_TRIG_ANY,
+	NL80211_WOWLAN_TRIG_DISCONNECT,
+	NL80211_WOWLAN_TRIG_MAGIC_PKT,
+	NL80211_WOWLAN_TRIG_PKT_PATTERN,
+	NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED,
+	NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE,
+	NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST,
+	NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE,
+	NL80211_WOWLAN_TRIG_RFKILL_RELEASE,
+	NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211,
+	NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN,
+	NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023,
+	NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN,
+	NL80211_WOWLAN_TRIG_TCP_CONNECTION,
+	NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH,
+	NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST,
+	NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS,
+	NL80211_WOWLAN_TRIG_NET_DETECT,
+	NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS,
+
+	/* keep last */
+	NUM_NL80211_WOWLAN_TRIG,
+	MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1
+};
+
+/**
+ * DOC: TCP connection wakeup
+ *
+ * Some devices can establish a TCP connection in order to be woken up by a
+ * packet coming in from outside their network segment, or behind NAT. If
+ * configured, the device will establish a TCP connection to the given
+ * service, and periodically send data to that service. The first data
+ * packet is usually transmitted after SYN/ACK, also ACKing the SYN/ACK.
+ * The data packets can optionally include a (little endian) sequence
+ * number (in the TCP payload!) that is generated by the device, and, also
+ * optionally, a token from a list of tokens. This serves as a keep-alive
+ * with the service, and for NATed connections, etc.
+ *
+ * During this keep-alive period, the server doesn't send any data to the
+ * client. When receiving data, it is compared against the wakeup pattern
+ * (and mask) and if it matches, the host is woken up. Similarly, if the
+ * connection breaks or cannot be established to start with, the host is
+ * also woken up.
+ *
+ * Developer's note: ARP offload is required for this, otherwise TCP
+ * response packets might not go through correctly.
+ */
+
+/**
+ * struct nl80211_wowlan_tcp_data_seq - WoWLAN TCP data sequence
+ * @start: starting value
+ * @offset: offset of sequence number in packet
+ * @len: length of the sequence value to write, 1 through 4
+ *
+ * Note: don't confuse with the TCP sequence number(s), this is for the
+ * keepalive packet payload. The actual value is written into the packet
+ * in little endian.
+ */
+struct nl80211_wowlan_tcp_data_seq {
+	__u32 start, offset, len;
+};
+
+/**
+ * struct nl80211_wowlan_tcp_data_token - WoWLAN TCP data token config
+ * @offset: offset of token in packet
+ * @len: length of each token
+ * @token_stream: stream of data to be used for the tokens, the length must
+ *	be a multiple of @len for this to make sense
+ */
+struct nl80211_wowlan_tcp_data_token {
+	__u32 offset, len;
+	__u8 token_stream[];
+};
+
+/**
+ * struct nl80211_wowlan_tcp_data_token_feature - data token features
+ * @min_len: minimum token length
+ * @max_len: maximum token length
+ * @bufsize: total available token buffer size (max size of @token_stream)
+ */
+struct nl80211_wowlan_tcp_data_token_feature {
+	__u32 min_len, max_len, bufsize;
+};
+
+/**
+ * enum nl80211_wowlan_tcp_attrs - WoWLAN TCP connection parameters
+ * @__NL80211_WOWLAN_TCP_INVALID: invalid number for nested attributes
+ * @NL80211_WOWLAN_TCP_SRC_IPV4: source IPv4 address (in network byte order)
+ * @NL80211_WOWLAN_TCP_DST_IPV4: destination IPv4 address
+ *	(in network byte order)
+ * @NL80211_WOWLAN_TCP_DST_MAC: destination MAC address, this is given because
+ *	route lookup when configured might be invalid by the time we suspend,
+ *	and doing a route lookup when suspending is no longer possible as it
+ *	might require ARP querying.
+ * @NL80211_WOWLAN_TCP_SRC_PORT: source port (u16); optional, if not given a
+ *	socket and port will be allocated
+ * @NL80211_WOWLAN_TCP_DST_PORT: destination port (u16)
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD: data packet payload, at least one byte.
+ *	For feature advertising, a u32 attribute holding the maximum length
+ *	of the data payload.
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ: data packet sequence configuration
+ *	(if desired), a &struct nl80211_wowlan_tcp_data_seq. For feature
+ *	advertising it is just a flag
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN: data packet token configuration,
+ *	see &struct nl80211_wowlan_tcp_data_token and for advertising see
+ *	&struct nl80211_wowlan_tcp_data_token_feature.
+ * @NL80211_WOWLAN_TCP_DATA_INTERVAL: data interval in seconds, maximum
+ *	interval in feature advertising (u32)
+ * @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_PKTPAT_MASK
+ *	but on the TCP payload only.
+ * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes
+ * @MAX_NL80211_WOWLAN_TCP: highest attribute number
+ */
+enum nl80211_wowlan_tcp_attrs {
+	__NL80211_WOWLAN_TCP_INVALID,
+	NL80211_WOWLAN_TCP_SRC_IPV4,
+	NL80211_WOWLAN_TCP_DST_IPV4,
+	NL80211_WOWLAN_TCP_DST_MAC,
+	NL80211_WOWLAN_TCP_SRC_PORT,
+	NL80211_WOWLAN_TCP_DST_PORT,
+	NL80211_WOWLAN_TCP_DATA_PAYLOAD,
+	NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ,
+	NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
+	NL80211_WOWLAN_TCP_DATA_INTERVAL,
+	NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
+	NL80211_WOWLAN_TCP_WAKE_MASK,
+
+	/* keep last */
+	NUM_NL80211_WOWLAN_TCP,
+	MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1
+};
+
+/**
+ * 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
+ *	can be chosen from this set of interface types (u32)
+ * @NL80211_IFACE_LIMIT_TYPES: nested attribute containing a
+ *	flag attribute for each interface type in this set
+ * @NUM_NL80211_IFACE_LIMIT: number of attributes
+ * @MAX_NL80211_IFACE_LIMIT: highest attribute number
+ */
+enum nl80211_iface_limit_attrs {
+	NL80211_IFACE_LIMIT_UNSPEC,
+	NL80211_IFACE_LIMIT_MAX,
+	NL80211_IFACE_LIMIT_TYPES,
+
+	/* keep last */
+	NUM_NL80211_IFACE_LIMIT,
+	MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1
+};
+
+/**
+ * enum nl80211_if_combination_attrs -- interface combination attributes
+ *
+ * @NL80211_IFACE_COMB_UNSPEC: (reserved)
+ * @NL80211_IFACE_COMB_LIMITS: Nested attributes containing the limits
+ *	for given interface types, see &enum nl80211_iface_limit_attrs.
+ * @NL80211_IFACE_COMB_MAXNUM: u32 attribute giving the total number of
+ *	interfaces that can be created in this group. This number doesn't
+ *	apply to interfaces purely managed in software, which are listed
+ *	in a separate attribute %NL80211_ATTR_INTERFACES_SOFTWARE.
+ * @NL80211_IFACE_COMB_STA_AP_BI_MATCH: flag attribute specifying that
+ *	beacon intervals within this group must be all the same even for
+ *	infrastructure and AP/GO combinations, i.e. the GO(s) must adopt
+ *	the infrastructure network's beacon interval.
+ * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many
+ *	different channels may be used within this group.
+ * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap
+ *	of supported channel widths for radar detection.
+ * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap
+ *	of supported regulatory regions for radar detection.
+ * @NUM_NL80211_IFACE_COMB: number of attributes
+ * @MAX_NL80211_IFACE_COMB: highest attribute number
+ *
+ * Examples:
+ *	limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2
+ *	=> allows an AP and a STA that must match BIs
+ *
+ *	numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8
+ *	=> allows 8 of AP/GO
+ *
+ *	numbers = [ #{STA} <= 2 ], channels = 2, max = 2
+ *	=> allows two STAs on different channels
+ *
+ *	numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4
+ *	=> allows a STA plus three P2P interfaces
+ *
+ * The list of these four possiblities could completely be contained
+ * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate
+ * that any of these groups must match.
+ *
+ * "Combinations" of just a single interface will not be listed here,
+ * a single interface of any valid interface type is assumed to always
+ * be possible by itself. This means that implicitly, for each valid
+ * interface type, the following group always exists:
+ *	numbers = [ #{<type>} <= 1 ], channels = 1, max = 1
+ */
+enum nl80211_if_combination_attrs {
+	NL80211_IFACE_COMB_UNSPEC,
+	NL80211_IFACE_COMB_LIMITS,
+	NL80211_IFACE_COMB_MAXNUM,
+	NL80211_IFACE_COMB_STA_AP_BI_MATCH,
+	NL80211_IFACE_COMB_NUM_CHANNELS,
+	NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+	NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
+
+	/* keep last */
+	NUM_NL80211_IFACE_COMB,
+	MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1
+};
+
+
+/**
+ * enum nl80211_plink_state - state of a mesh peer link finite state machine
+ *
+ * @NL80211_PLINK_LISTEN: initial state, considered the implicit
+ *	state of non existant mesh peer links
+ * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to
+ *	this mesh peer
+ * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received
+ *	from this mesh peer
+ * @NL80211_PLINK_CNF_RCVD: mesh plink confirm frame has been
+ *	received from this mesh peer
+ * @NL80211_PLINK_ESTAB: mesh peer link is established
+ * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled
+ * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh
+ *	plink are discarded
+ * @NUM_NL80211_PLINK_STATES: number of peer link states
+ * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states
+ */
+enum nl80211_plink_state {
+	NL80211_PLINK_LISTEN,
+	NL80211_PLINK_OPN_SNT,
+	NL80211_PLINK_OPN_RCVD,
+	NL80211_PLINK_CNF_RCVD,
+	NL80211_PLINK_ESTAB,
+	NL80211_PLINK_HOLDING,
+	NL80211_PLINK_BLOCKED,
+
+	/* keep last */
+	NUM_NL80211_PLINK_STATES,
+	MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1
+};
+
+/**
+ * enum nl80211_plink_action - actions to perform in mesh peers
+ *
+ * @NL80211_PLINK_ACTION_NO_ACTION: perform no action
+ * @NL80211_PLINK_ACTION_OPEN: start mesh peer link establishment
+ * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer
+ * @NUM_NL80211_PLINK_ACTIONS: number of possible actions
+ */
+enum plink_actions {
+	NL80211_PLINK_ACTION_NO_ACTION,
+	NL80211_PLINK_ACTION_OPEN,
+	NL80211_PLINK_ACTION_BLOCK,
+
+	NUM_NL80211_PLINK_ACTIONS,
+};
+
+
+#define NL80211_KCK_LEN			16
+#define NL80211_KEK_LEN			16
+#define NL80211_REPLAY_CTR_LEN		8
+
+/**
+ * enum nl80211_rekey_data - attributes for GTK rekey offload
+ * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes
+ * @NL80211_REKEY_DATA_KEK: key encryption key (binary)
+ * @NL80211_REKEY_DATA_KCK: key confirmation key (binary)
+ * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary)
+ * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal)
+ * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal)
+ */
+enum nl80211_rekey_data {
+	__NL80211_REKEY_DATA_INVALID,
+	NL80211_REKEY_DATA_KEK,
+	NL80211_REKEY_DATA_KCK,
+	NL80211_REKEY_DATA_REPLAY_CTR,
+
+	/* keep last */
+	NUM_NL80211_REKEY_DATA,
+	MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1
+};
+
+/**
+ * enum nl80211_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID
+ * @NL80211_HIDDEN_SSID_NOT_IN_USE: do not hide SSID (i.e., broadcast it in
+ *	Beacon frames)
+ * @NL80211_HIDDEN_SSID_ZERO_LEN: hide SSID by using zero-length SSID element
+ *	in Beacon frames
+ * @NL80211_HIDDEN_SSID_ZERO_CONTENTS: hide SSID by using correct length of SSID
+ *	element in Beacon frames but zero out each byte in the SSID
+ */
+enum nl80211_hidden_ssid {
+	NL80211_HIDDEN_SSID_NOT_IN_USE,
+	NL80211_HIDDEN_SSID_ZERO_LEN,
+	NL80211_HIDDEN_SSID_ZERO_CONTENTS
+};
+
+/**
+ * enum nl80211_sta_wme_attr - station WME attributes
+ * @__NL80211_STA_WME_INVALID: invalid number for nested attribute
+ * @NL80211_STA_WME_UAPSD_QUEUES: bitmap of uapsd queues. the format
+ *	is the same as the AC bitmap in the QoS info field.
+ * @NL80211_STA_WME_MAX_SP: max service period. the format is the same
+ *	as the MAX_SP field in the QoS info field (but already shifted down).
+ * @__NL80211_STA_WME_AFTER_LAST: internal
+ * @NL80211_STA_WME_MAX: highest station WME attribute
+ */
+enum nl80211_sta_wme_attr {
+	__NL80211_STA_WME_INVALID,
+	NL80211_STA_WME_UAPSD_QUEUES,
+	NL80211_STA_WME_MAX_SP,
+
+	/* keep last */
+	__NL80211_STA_WME_AFTER_LAST,
+	NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates
+ * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes
+ * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher
+ *	priority)
+ * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets)
+ * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag)
+ * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes
+ *	(internal)
+ * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute
+ *	(internal)
+ */
+enum nl80211_pmksa_candidate_attr {
+	__NL80211_PMKSA_CANDIDATE_INVALID,
+	NL80211_PMKSA_CANDIDATE_INDEX,
+	NL80211_PMKSA_CANDIDATE_BSSID,
+	NL80211_PMKSA_CANDIDATE_PREAUTH,
+
+	/* keep last */
+	NUM_NL80211_PMKSA_CANDIDATE,
+	MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1
+};
+
+/**
+ * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION
+ * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request
+ * @NL80211_TDLS_SETUP: Setup TDLS link
+ * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established
+ * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link
+ * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link
+ */
+enum nl80211_tdls_operation {
+	NL80211_TDLS_DISCOVERY_REQ,
+	NL80211_TDLS_SETUP,
+	NL80211_TDLS_TEARDOWN,
+	NL80211_TDLS_ENABLE_LINK,
+	NL80211_TDLS_DISABLE_LINK,
+};
+
+/*
+ * enum nl80211_ap_sme_features - device-integrated AP features
+ * Reserved for future use, no bits are defined in
+ * NL80211_ATTR_DEVICE_AP_SME yet.
+enum nl80211_ap_sme_features {
+};
+ */
+
+/**
+ * enum nl80211_feature_flags - device/driver features
+ * @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back
+ *	TX status to the socket error queue when requested with the
+ *	socket option.
+ * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates.
+ * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up
+ *	the connected inactive stations in AP mode.
+ * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
+ *	to work properly to suppport receiving regulatory hints from
+ *	cellular base stations.
+ * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: (no longer available, only
+ *	here to reserve the value for API/ABI compatibility)
+ * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of
+ *	equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station
+ *	mode
+ * @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan
+ * @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported
+ * @NL80211_FEATURE_AP_SCAN: Support scanning using an AP vif
+ * @NL80211_FEATURE_VIF_TXPOWER: The driver supports per-vif TX power setting
+ * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform
+ *	OBSS scans and generate 20/40 BSS coex reports. This flag is used only
+ *	for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied.
+ * @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window
+ *	setting
+ * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic
+ *	powersave
+ * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state
+ *	transitions for AP clients. Without this flag (and if the driver
+ *	doesn't have the AP SME in the device) the driver supports adding
+ *	stations only when they're associated and adds them in associated
+ *	state (to later be transitioned into authorized), with this flag
+ *	they should be added before even sending the authentication reply
+ *	and then transitioned into authenticated, associated and authorized
+ *	states using station flags.
+ *	Note that even for drivers that support this, the default is to add
+ *	stations in authenticated/associated state, so to add unauthenticated
+ *	stations the authenticated/associated bits have to be set in the mask.
+ * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits
+ *	(HT40, VHT 80/160 MHz) if this flag is set
+ * @NL80211_FEATURE_USERSPACE_MPM: This driver supports a userspace Mesh
+ *	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.
+ * @NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES: This device adds a DS Parameter
+ *	Set IE to probe requests.
+ * @NL80211_FEATURE_WFA_TPC_IE_IN_PROBES: This device adds a WFA TPC Report IE
+ *	to probe requests.
+ * @NL80211_FEATURE_QUIET: This device, in client mode, supports Quiet Period
+ *	requests sent to it by an AP.
+ * @NL80211_FEATURE_TX_POWER_INSERTION: This device is capable of inserting the
+ *	current tx power value into the TPC Report IE in the spectrum
+ *	management TPC Report action frame, and in the Radio Measurement Link
+ *	Measurement Report action frame.
+ * @NL80211_FEATURE_ACKTO_ESTIMATION: This driver supports dynamic ACK timeout
+ *	estimation (dynack). %NL80211_ATTR_WIPHY_DYN_ACK flag attribute is used
+ *	to enable dynack.
+ * @NL80211_FEATURE_STATIC_SMPS: Device supports static spatial
+ *	multiplexing powersave, ie. can turn off all but one chain
+ *	even on HT connections that should be using more chains.
+ * @NL80211_FEATURE_DYNAMIC_SMPS: Device supports dynamic spatial
+ *	multiplexing powersave, ie. can turn off all but one chain
+ *	and then wake the rest up as required after, for example,
+ *	rts/cts handshake.
+ * @NL80211_FEATURE_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM
+ *	TSPEC sessions (TID aka TSID 0-7) with the %NL80211_CMD_ADD_TX_TS
+ *	command. Standard IEEE 802.11 TSPEC setup is not yet supported, it
+ *	needs to be able to handle Block-Ack agreements and other things.
+ * @NL80211_FEATURE_MAC_ON_CREATE: Device supports configuring
+ *	the vif's MAC address upon creation.
+ *	See 'macaddr' field in the vif_params (cfg80211.h).
+ * @NL80211_FEATURE_TDLS_CHANNEL_SWITCH: Driver supports channel switching when
+ *	operating as a TDLS peer.
+ * @NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR: This device/driver supports using a
+ *	random MAC address during scan (if the device is unassociated); the
+ *	%NL80211_SCAN_FLAG_RANDOM_ADDR flag may be set for scans and the MAC
+ *	address mask/value will be used.
+ * @NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR: This device/driver supports
+ *	using a random MAC address for every scan iteration during scheduled
+ *	scan (while not associated), the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ *	be set for scheduled scan and the MAC address mask/value will be used.
+ * @NL80211_FEATURE_ND_RANDOM_MAC_ADDR: This device/driver supports using a
+ *	random MAC address for every scan iteration during "net detect", i.e.
+ *	scan in unassociated WoWLAN, the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ *	be set for scheduled scan and the MAC address mask/value will be used.
+ */
+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,
+	NL80211_FEATURE_SAE				= 1 << 5,
+	NL80211_FEATURE_LOW_PRIORITY_SCAN		= 1 << 6,
+	NL80211_FEATURE_SCAN_FLUSH			= 1 << 7,
+	NL80211_FEATURE_AP_SCAN				= 1 << 8,
+	NL80211_FEATURE_VIF_TXPOWER			= 1 << 9,
+	NL80211_FEATURE_NEED_OBSS_SCAN			= 1 << 10,
+	NL80211_FEATURE_P2P_GO_CTWIN			= 1 << 11,
+	NL80211_FEATURE_P2P_GO_OPPPS			= 1 << 12,
+	/* bit 13 is reserved */
+	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,
+	NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES	= 1 << 19,
+	NL80211_FEATURE_WFA_TPC_IE_IN_PROBES		= 1 << 20,
+	NL80211_FEATURE_QUIET				= 1 << 21,
+	NL80211_FEATURE_TX_POWER_INSERTION		= 1 << 22,
+	NL80211_FEATURE_ACKTO_ESTIMATION		= 1 << 23,
+	NL80211_FEATURE_STATIC_SMPS			= 1 << 24,
+	NL80211_FEATURE_DYNAMIC_SMPS			= 1 << 25,
+	NL80211_FEATURE_SUPPORTS_WMM_ADMISSION		= 1 << 26,
+	NL80211_FEATURE_MAC_ON_CREATE			= 1 << 27,
+	NL80211_FEATURE_TDLS_CHANNEL_SWITCH		= 1 << 28,
+	NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR		= 1 << 29,
+	NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR	= 1 << 30,
+	NL80211_FEATURE_ND_RANDOM_MAC_ADDR		= 1 << 31,
+};
+
+/**
+ * enum nl80211_ext_feature_index - bit index of extended features.
+ * @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates.
+ *
+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+ */
+enum nl80211_ext_feature_index {
+	NL80211_EXT_FEATURE_VHT_IBSS,
+
+	/* add new features before the definition below */
+	NUM_NL80211_EXT_FEATURES,
+	MAX_NL80211_EXT_FEATURES = NUM_NL80211_EXT_FEATURES - 1
+};
+
+/**
+ * enum nl80211_probe_resp_offload_support_attr - optional supported
+ *	protocols for probe-response offloading by the driver/FW.
+ *	To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute.
+ *	Each enum value represents a bit in the bitmap of supported
+ *	protocols. Typically a subset of probe-requests belonging to a
+ *	supported protocol will be excluded from offload and uploaded
+ *	to the host.
+ *
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: Support for WPS ver. 1
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: Support for WPS ver. 2
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: Support for P2P
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: Support for 802.11u
+ */
+enum nl80211_probe_resp_offload_support_attr {
+	NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS =	1<<0,
+	NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 =	1<<1,
+	NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P =	1<<2,
+	NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U =	1<<3,
+};
+
+/**
+ * enum nl80211_connect_failed_reason - connection request failed reasons
+ * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be
+ *	handled by the AP is reached.
+ * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL.
+ */
+enum nl80211_connect_failed_reason {
+	NL80211_CONN_FAIL_MAX_CLIENTS,
+	NL80211_CONN_FAIL_BLOCKED_CLIENT,
+};
+
+/**
+ * enum nl80211_scan_flags -  scan request control flags
+ *
+ * Scan request control flags are used to control the handling
+ * of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN
+ * requests.
+ *
+ * @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority
+ * @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning
+ * @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured
+ *	as AP and the beaconing has already been configured. This attribute is
+ *	dangerous because will destroy stations performance as a lot of frames
+ *	will be lost while scanning off-channel, therefore it must be used only
+ *	when really needed
+ * @NL80211_SCAN_FLAG_RANDOM_ADDR: use a random MAC address for this scan (or
+ *	for scheduled scan: a different one for every scan iteration). When the
+ *	flag is set, depending on device capabilities the @NL80211_ATTR_MAC and
+ *	@NL80211_ATTR_MAC_MASK attributes may also be given in which case only
+ *	the masked bits will be preserved from the MAC address and the remainder
+ *	randomised. If the attributes are not given full randomisation (46 bits,
+ *	locally administered 1, multicast 0) is assumed.
+ *	This flag must not be requested when the feature isn't supported, check
+ *	the nl80211 feature flags for the device.
+ */
+enum nl80211_scan_flags {
+	NL80211_SCAN_FLAG_LOW_PRIORITY			= 1<<0,
+	NL80211_SCAN_FLAG_FLUSH				= 1<<1,
+	NL80211_SCAN_FLAG_AP				= 1<<2,
+	NL80211_SCAN_FLAG_RANDOM_ADDR			= 1<<3,
+};
+
+/**
+ * enum nl80211_acl_policy - access control policy
+ *
+ * Access control policy is applied on a MAC list set by
+ * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to
+ * be used with %NL80211_ATTR_ACL_POLICY.
+ *
+ * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are
+ *	listed in ACL, i.e. allow all the stations which are not listed
+ *	in ACL to authenticate.
+ * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed
+ *	in ACL, i.e. deny all the stations which are not listed in ACL.
+ */
+enum nl80211_acl_policy {
+	NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED,
+	NL80211_ACL_POLICY_DENY_UNLESS_LISTED,
+};
+
+/**
+ * enum nl80211_smps_mode - SMPS mode
+ *
+ * Requested SMPS mode (for AP mode)
+ *
+ * @NL80211_SMPS_OFF: SMPS off (use all antennas).
+ * @NL80211_SMPS_STATIC: static SMPS (use a single antenna)
+ * @NL80211_SMPS_DYNAMIC: dynamic smps (start with a single antenna and
+ *	turn on other antennas after CTS/RTS).
+ */
+enum nl80211_smps_mode {
+	NL80211_SMPS_OFF,
+	NL80211_SMPS_STATIC,
+	NL80211_SMPS_DYNAMIC,
+
+	__NL80211_SMPS_AFTER_LAST,
+	NL80211_SMPS_MAX = __NL80211_SMPS_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_radar_event - type of radar event for DFS operation
+ *
+ * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace
+ * about detected radars or success of the channel available check (CAC)
+ *
+ * @NL80211_RADAR_DETECTED: A radar pattern has been detected. The channel is
+ *	now unusable.
+ * @NL80211_RADAR_CAC_FINISHED: Channel Availability Check has been finished,
+ *	the channel is now available.
+ * @NL80211_RADAR_CAC_ABORTED: Channel Availability Check has been aborted, no
+ *	change to the channel status.
+ * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is
+ *	over, channel becomes usable.
+ */
+enum nl80211_radar_event {
+	NL80211_RADAR_DETECTED,
+	NL80211_RADAR_CAC_FINISHED,
+	NL80211_RADAR_CAC_ABORTED,
+	NL80211_RADAR_NOP_FINISHED,
+};
+
+/**
+ * enum nl80211_dfs_state - DFS states for channels
+ *
+ * Channel states used by the DFS code.
+ *
+ * @NL80211_DFS_USABLE: The channel can be used, but channel availability
+ *	check (CAC) must be performed before using it for AP or IBSS.
+ * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
+ *	is therefore marked as not available.
+ * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
+ */
+enum nl80211_dfs_state {
+	NL80211_DFS_USABLE,
+	NL80211_DFS_UNAVAILABLE,
+	NL80211_DFS_AVAILABLE,
+};
+
+/**
+ * enum enum nl80211_protocol_features - nl80211 protocol features
+ * @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting
+ *	wiphy dumps (if requested by the application with the attribute
+ *	%NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the
+ *	wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or
+ *	%NL80211_ATTR_WDEV.
+ */
+enum nl80211_protocol_features {
+	NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP =	1 << 0,
+};
+
+/**
+ * enum nl80211_crit_proto_id - nl80211 critical protocol identifiers
+ *
+ * @NL80211_CRIT_PROTO_UNSPEC: protocol unspecified.
+ * @NL80211_CRIT_PROTO_DHCP: BOOTP or DHCPv6 protocol.
+ * @NL80211_CRIT_PROTO_EAPOL: EAPOL protocol.
+ * @NL80211_CRIT_PROTO_APIPA: APIPA protocol.
+ * @NUM_NL80211_CRIT_PROTO: must be kept last.
+ */
+enum nl80211_crit_proto_id {
+	NL80211_CRIT_PROTO_UNSPEC,
+	NL80211_CRIT_PROTO_DHCP,
+	NL80211_CRIT_PROTO_EAPOL,
+	NL80211_CRIT_PROTO_APIPA,
+	/* add other protocols before this one */
+	NUM_NL80211_CRIT_PROTO
+};
+
+/* 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/hostap/src/drivers/priv_netlink.h b/hostap/src/drivers/priv_netlink.h
new file mode 100644
index 0000000..d3f091c
--- /dev/null
+++ b/hostap/src/drivers/priv_netlink.h
@@ -0,0 +1,109 @@
+/*
+ * wpa_supplicant - Private copy of Linux netlink/rtnetlink definitions.
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PRIV_NETLINK_H
+#define PRIV_NETLINK_H
+
+/*
+ * This should be replaced with user space header once one is available with C
+ * library, etc..
+ */
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP   0x10000         /* driver signals L1 up         */
+#endif
+#ifndef IFF_DORMANT
+#define IFF_DORMANT    0x20000         /* driver signals dormant       */
+#endif
+
+#ifndef IFLA_IFNAME
+#define IFLA_IFNAME 3
+#endif
+#ifndef IFLA_WIRELESS
+#define IFLA_WIRELESS 11
+#endif
+#ifndef IFLA_OPERSTATE
+#define IFLA_OPERSTATE 16
+#endif
+#ifndef IFLA_LINKMODE
+#define IFLA_LINKMODE 17
+#define IF_OPER_DORMANT 5
+#define IF_OPER_UP 6
+#endif
+
+#define NLM_F_REQUEST 1
+
+#define NETLINK_ROUTE 0
+#define RTMGRP_LINK 1
+#define RTM_BASE 0x10
+#define RTM_NEWLINK (RTM_BASE + 0)
+#define RTM_DELLINK (RTM_BASE + 1)
+#define RTM_SETLINK (RTM_BASE + 3)
+
+#define NLMSG_ALIGNTO 4
+#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1))
+#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
+#define NLMSG_DATA(nlh) ((void*) (((char*) nlh) + NLMSG_LENGTH(0)))
+#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
+			     (struct nlmsghdr *) \
+			     (((char *)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
+#define NLMSG_OK(nlh,len) ((len) >= (int) sizeof(struct nlmsghdr) && \
+			   (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
+			   (int) (nlh)->nlmsg_len <= (len))
+#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
+
+#define RTA_ALIGNTO 4
+#define RTA_ALIGN(len) (((len) + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1))
+#define RTA_OK(rta,len) \
+((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \
+(rta)->rta_len <= (len))
+#define RTA_NEXT(rta,attrlen) \
+((attrlen) -= RTA_ALIGN((rta)->rta_len), \
+(struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len)))
+#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
+#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(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
+{
+	sa_family_t nl_family;
+	unsigned short nl_pad;
+	u32 nl_pid;
+	u32 nl_groups;
+};
+
+struct nlmsghdr
+{
+	u32 nlmsg_len;
+	u16 nlmsg_type;
+	u16 nlmsg_flags;
+	u32 nlmsg_seq;
+	u32 nlmsg_pid;
+};
+
+struct ifinfomsg
+{
+	unsigned char ifi_family;
+	unsigned char __ifi_pad;
+	unsigned short ifi_type;
+	int ifi_index;
+	unsigned ifi_flags;
+	unsigned ifi_change;
+};
+
+struct rtattr
+{
+	unsigned short rta_len;
+	unsigned short rta_type;
+};
+
+#endif /* PRIV_NETLINK_H */
diff --git a/hostap/src/drivers/rfkill.c b/hostap/src/drivers/rfkill.c
new file mode 100644
index 0000000..45b26c4
--- /dev/null
+++ b/hostap/src/drivers/rfkill.c
@@ -0,0 +1,188 @@
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 2010, 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 <fcntl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "rfkill.h"
+
+#define RFKILL_EVENT_SIZE_V1 8
+
+struct rfkill_event {
+	u32 idx;
+	u8 type;
+	u8 op;
+	u8 soft;
+	u8 hard;
+} STRUCT_PACKED;
+
+enum rfkill_operation {
+	RFKILL_OP_ADD = 0,
+	RFKILL_OP_DEL,
+	RFKILL_OP_CHANGE,
+	RFKILL_OP_CHANGE_ALL,
+};
+
+enum rfkill_type {
+	RFKILL_TYPE_ALL = 0,
+	RFKILL_TYPE_WLAN,
+	RFKILL_TYPE_BLUETOOTH,
+	RFKILL_TYPE_UWB,
+	RFKILL_TYPE_WIMAX,
+	RFKILL_TYPE_WWAN,
+	RFKILL_TYPE_GPS,
+	RFKILL_TYPE_FM,
+	NUM_RFKILL_TYPES,
+};
+
+
+struct rfkill_data {
+	struct rfkill_config *cfg;
+	int fd;
+	int blocked;
+};
+
+
+static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct rfkill_data *rfkill = eloop_ctx;
+	struct rfkill_event event;
+	ssize_t len;
+	int new_blocked;
+
+	len = read(rfkill->fd, &event, sizeof(event));
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
+			   strerror(errno));
+		return;
+	}
+	if (len != RFKILL_EVENT_SIZE_V1) {
+		wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
+			   "%d (expected %d)",
+			   (int) len, RFKILL_EVENT_SIZE_V1);
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
+		   "op=%u soft=%u hard=%u",
+		   event.idx, event.type, event.op, event.soft,
+		   event.hard);
+	if (event.op != RFKILL_OP_CHANGE || event.type != RFKILL_TYPE_WLAN)
+		return;
+
+	if (event.hard) {
+		wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
+		new_blocked = 1;
+	} else if (event.soft) {
+		wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
+		new_blocked = 1;
+	} else {
+		wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
+		new_blocked = 0;
+	}
+
+	if (new_blocked != rfkill->blocked) {
+		rfkill->blocked = new_blocked;
+		if (new_blocked)
+			rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
+		else
+			rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
+	}
+}
+
+
+struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
+{
+	struct rfkill_data *rfkill;
+	struct rfkill_event event;
+	ssize_t len;
+
+	rfkill = os_zalloc(sizeof(*rfkill));
+	if (rfkill == NULL)
+		return NULL;
+
+	rfkill->cfg = cfg;
+	rfkill->fd = open("/dev/rfkill", O_RDONLY);
+	if (rfkill->fd < 0) {
+		wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
+			   "device");
+		goto fail;
+	}
+
+	if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
+		wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
+			   "%s", strerror(errno));
+		goto fail2;
+	}
+
+	for (;;) {
+		len = read(rfkill->fd, &event, sizeof(event));
+		if (len < 0) {
+			if (errno == EAGAIN)
+				break; /* No more entries */
+			wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
+				   strerror(errno));
+			break;
+		}
+		if (len != RFKILL_EVENT_SIZE_V1) {
+			wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
+				   "%d (expected %d)",
+				   (int) len, RFKILL_EVENT_SIZE_V1);
+			continue;
+		}
+		wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
+			   "op=%u soft=%u hard=%u",
+			   event.idx, event.type, event.op, event.soft,
+			   event.hard);
+		if (event.op != RFKILL_OP_ADD ||
+		    event.type != RFKILL_TYPE_WLAN)
+			continue;
+		if (event.hard) {
+			wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
+			rfkill->blocked = 1;
+		} else if (event.soft) {
+			wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
+			rfkill->blocked = 1;
+		}
+	}
+
+	eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
+
+	return rfkill;
+
+fail2:
+	close(rfkill->fd);
+fail:
+	os_free(rfkill);
+	return NULL;
+}
+
+
+void rfkill_deinit(struct rfkill_data *rfkill)
+{
+	if (rfkill == NULL)
+		return;
+
+	if (rfkill->fd >= 0) {
+		eloop_unregister_read_sock(rfkill->fd);
+		close(rfkill->fd);
+	}
+
+	os_free(rfkill->cfg);
+	os_free(rfkill);
+}
+
+
+int rfkill_is_blocked(struct rfkill_data *rfkill)
+{
+	if (rfkill == NULL)
+		return 0;
+
+	return rfkill->blocked;
+}
diff --git a/hostap/src/drivers/rfkill.h b/hostap/src/drivers/rfkill.h
new file mode 100644
index 0000000..0412ac3
--- /dev/null
+++ b/hostap/src/drivers/rfkill.h
@@ -0,0 +1,25 @@
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RFKILL_H
+#define RFKILL_H
+
+struct rfkill_data;
+
+struct rfkill_config {
+	void *ctx;
+	char ifname[IFNAMSIZ];
+	void (*blocked_cb)(void *ctx);
+	void (*unblocked_cb)(void *ctx);
+};
+
+struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
+void rfkill_deinit(struct rfkill_data *rfkill);
+int rfkill_is_blocked(struct rfkill_data *rfkill);
+
+#endif /* RFKILL_H */
diff --git a/hostap/src/eap_common/Makefile b/hostap/src/eap_common/Makefile
new file mode 100644
index 0000000..f00b438
--- /dev/null
+++ b/hostap/src/eap_common/Makefile
@@ -0,0 +1,31 @@
+all: libeap_common.a
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeap_common.a
+
+install:
+	@echo Nothing to be made.
+
+include ../lib.rules
+
+LIB_OBJS= \
+	chap.o \
+	eap_common.o \
+	eap_eke_common.o \
+	eap_eke_common.o \
+	eap_fast_common.o \
+	eap_gpsk_common.o \
+	eap_ikev2_common.o \
+	eap_pax_common.o \
+	eap_peap_common.o \
+	eap_psk_common.o \
+	eap_pwd_common.o \
+	eap_sake_common.o \
+	eap_sim_common.o \
+	eap_wsc_common.o \
+	ikev2_common.o
+
+libeap_common.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/eap_common/chap.c b/hostap/src/eap_common/chap.c
new file mode 100644
index 0000000..820d18a
--- /dev/null
+++ b/hostap/src/eap_common/chap.c
@@ -0,0 +1,28 @@
+/*
+ * CHAP-MD5 (RFC 1994)
+ * Copyright (c) 2007-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/crypto.h"
+#include "chap.h"
+
+int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge,
+	      size_t challenge_len, u8 *response)
+{
+	const u8 *addr[3];
+	size_t len[3];
+
+	addr[0] = &id;
+	len[0] = 1;
+	addr[1] = secret;
+	len[1] = secret_len;
+	addr[2] = challenge;
+	len[2] = challenge_len;
+	return md5_vector(3, addr, len, response);
+}
diff --git a/hostap/src/eap_common/chap.h b/hostap/src/eap_common/chap.h
new file mode 100644
index 0000000..a791505
--- /dev/null
+++ b/hostap/src/eap_common/chap.h
@@ -0,0 +1,17 @@
+/*
+ * CHAP-MD5 (RFC 1994)
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CHAP_H
+#define CHAP_H
+
+#define CHAP_MD5_LEN 16
+
+int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge,
+	     size_t challenge_len, u8 *response);
+
+#endif /* CHAP_H */
diff --git a/hostap/src/eap_common/eap_common.c b/hostap/src/eap_common/eap_common.c
new file mode 100644
index 0000000..51a15d7
--- /dev/null
+++ b/hostap/src/eap_common/eap_common.c
@@ -0,0 +1,288 @@
+/*
+ * EAP common peer/server definitions
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_common.h"
+
+/**
+ * eap_hdr_len_valid - Validate EAP header length field
+ * @msg: EAP frame (starting with EAP header)
+ * @min_payload: Minimum payload length needed
+ * Returns: 1 for valid header, 0 for invalid
+ *
+ * This is a helper function that does minimal validation of EAP messages. The
+ * length field is verified to be large enough to include the header and not
+ * too large to go beyond the end of the buffer.
+ */
+int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
+{
+	const struct eap_hdr *hdr;
+	size_t len;
+
+	if (msg == NULL)
+		return 0;
+
+	hdr = wpabuf_head(msg);
+
+	if (wpabuf_len(msg) < sizeof(*hdr)) {
+		wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
+		return 0;
+	}
+
+	len = be_to_host16(hdr->length);
+	if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
+		wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
+		return 0;
+	}
+
+	return 1;
+}
+
+
+/**
+ * eap_hdr_validate - Validate EAP header
+ * @vendor: Expected EAP Vendor-Id (0 = IETF)
+ * @eap_type: Expected EAP type number
+ * @msg: EAP frame (starting with EAP header)
+ * @plen: Pointer to variable to contain the returned payload length
+ * Returns: Pointer to EAP payload (after type field), or %NULL on failure
+ *
+ * This is a helper function for EAP method implementations. This is usually
+ * called in the beginning of struct eap_method::process() function to verify
+ * that the received EAP request packet has a valid header. This function is
+ * able to process both legacy and expanded EAP headers and in most cases, the
+ * caller can just use the returned payload pointer (into *plen) for processing
+ * the payload regardless of whether the packet used the expanded EAP header or
+ * not.
+ */
+const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+			    const struct wpabuf *msg, size_t *plen)
+{
+	const struct eap_hdr *hdr;
+	const u8 *pos;
+	size_t len;
+
+	if (!eap_hdr_len_valid(msg, 1))
+		return NULL;
+
+	hdr = wpabuf_head(msg);
+	len = be_to_host16(hdr->length);
+	pos = (const u8 *) (hdr + 1);
+
+	if (*pos == EAP_TYPE_EXPANDED) {
+		int exp_vendor;
+		u32 exp_type;
+		if (len < sizeof(*hdr) + 8) {
+			wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
+				   "length");
+			return NULL;
+		}
+		pos++;
+		exp_vendor = WPA_GET_BE24(pos);
+		pos += 3;
+		exp_type = WPA_GET_BE32(pos);
+		pos += 4;
+		if (exp_vendor != vendor || exp_type != (u32) eap_type) {
+			wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
+				   "type");
+			return NULL;
+		}
+
+		*plen = len - sizeof(*hdr) - 8;
+		return pos;
+	} else {
+		if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
+			wpa_printf(MSG_INFO, "EAP: Invalid frame type");
+			return NULL;
+		}
+		*plen = len - sizeof(*hdr) - 1;
+		return pos + 1;
+	}
+}
+
+
+/**
+ * eap_msg_alloc - Allocate a buffer for an EAP message
+ * @vendor: Vendor-Id (0 = IETF)
+ * @type: EAP type
+ * @payload_len: Payload length in bytes (data after Type)
+ * @code: Message Code (EAP_CODE_*)
+ * @identifier: Identifier
+ * Returns: Pointer to the allocated message buffer or %NULL on error
+ *
+ * This function can be used to allocate a buffer for an EAP message and fill
+ * in the EAP header. This function is automatically using expanded EAP header
+ * if the selected Vendor-Id is not IETF. In other words, most EAP methods do
+ * not need to separately select which header type to use when using this
+ * function to allocate the message buffers. The returned buffer has room for
+ * payload_len bytes and has the EAP header and Type field already filled in.
+ */
+struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
+			      u8 code, u8 identifier)
+{
+	struct wpabuf *buf;
+	struct eap_hdr *hdr;
+	size_t len;
+
+	len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
+		payload_len;
+	buf = wpabuf_alloc(len);
+	if (buf == NULL)
+		return NULL;
+
+	hdr = wpabuf_put(buf, sizeof(*hdr));
+	hdr->code = code;
+	hdr->identifier = identifier;
+	hdr->length = host_to_be16(len);
+
+	if (vendor == EAP_VENDOR_IETF) {
+		wpabuf_put_u8(buf, type);
+	} else {
+		wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
+		wpabuf_put_be24(buf, vendor);
+		wpabuf_put_be32(buf, type);
+	}
+
+	return buf;
+}
+
+
+/**
+ * eap_update_len - Update EAP header length
+ * @msg: EAP message from eap_msg_alloc
+ *
+ * This function updates the length field in the EAP header to match with the
+ * current length for the buffer. This allows eap_msg_alloc() to be used to
+ * allocate a larger buffer than the exact message length (e.g., if exact
+ * message length is not yet known).
+ */
+void eap_update_len(struct wpabuf *msg)
+{
+	struct eap_hdr *hdr;
+	hdr = wpabuf_mhead(msg);
+	if (wpabuf_len(msg) < sizeof(*hdr))
+		return;
+	hdr->length = host_to_be16(wpabuf_len(msg));
+}
+
+
+/**
+ * eap_get_id - Get EAP Identifier from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The Identifier field from the EAP header
+ */
+u8 eap_get_id(const struct wpabuf *msg)
+{
+	const struct eap_hdr *eap;
+
+	if (wpabuf_len(msg) < sizeof(*eap))
+		return 0;
+
+	eap = wpabuf_head(msg);
+	return eap->identifier;
+}
+
+
+/**
+ * eap_get_type - Get EAP Type from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The EAP Type after the EAP header
+ */
+EapType eap_get_type(const struct wpabuf *msg)
+{
+	if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
+		return EAP_TYPE_NONE;
+
+	return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
+}
+
+
+#ifdef CONFIG_ERP
+int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs,
+		   int stop_at_keyname)
+{
+	os_memset(tlvs, 0, sizeof(*tlvs));
+
+	while (pos < end) {
+		u8 tlv_type, tlv_len;
+
+		tlv_type = *pos++;
+		switch (tlv_type) {
+		case EAP_ERP_TV_RRK_LIFETIME:
+		case EAP_ERP_TV_RMSK_LIFETIME:
+			/* 4-octet TV */
+			if (pos + 4 > end) {
+				wpa_printf(MSG_DEBUG, "EAP: Too short TV");
+				return -1;
+			}
+			pos += 4;
+			break;
+		case EAP_ERP_TLV_DOMAIN_NAME:
+		case EAP_ERP_TLV_KEYNAME_NAI:
+		case EAP_ERP_TLV_CRYPTOSUITES:
+		case EAP_ERP_TLV_AUTHORIZATION_INDICATION:
+		case EAP_ERP_TLV_CALLED_STATION_ID:
+		case EAP_ERP_TLV_CALLING_STATION_ID:
+		case EAP_ERP_TLV_NAS_IDENTIFIER:
+		case EAP_ERP_TLV_NAS_IP_ADDRESS:
+		case EAP_ERP_TLV_NAS_IPV6_ADDRESS:
+			if (pos >= end) {
+				wpa_printf(MSG_DEBUG, "EAP: Too short TLV");
+				return -1;
+			}
+			tlv_len = *pos++;
+			if (tlv_len > (unsigned) (end - pos)) {
+				wpa_printf(MSG_DEBUG, "EAP: Truncated TLV");
+				return -1;
+			}
+			if (tlv_type == EAP_ERP_TLV_KEYNAME_NAI) {
+				if (tlvs->keyname) {
+					wpa_printf(MSG_DEBUG,
+						   "EAP: More than one keyName-NAI");
+					return -1;
+				}
+				tlvs->keyname = pos;
+				tlvs->keyname_len = tlv_len;
+				if (stop_at_keyname)
+					return 0;
+			} else if (tlv_type == EAP_ERP_TLV_DOMAIN_NAME) {
+				tlvs->domain = pos;
+				tlvs->domain_len = tlv_len;
+			}
+			pos += tlv_len;
+			break;
+		default:
+			if (tlv_type >= 128 && tlv_type <= 191) {
+				/* Undefined TLV */
+				if (pos >= end) {
+					wpa_printf(MSG_DEBUG,
+						   "EAP: Too short TLV");
+					return -1;
+				}
+				tlv_len = *pos++;
+				if (tlv_len > (unsigned) (end - pos)) {
+					wpa_printf(MSG_DEBUG,
+						   "EAP: Truncated TLV");
+					return -1;
+				}
+				pos += tlv_len;
+				break;
+			}
+			wpa_printf(MSG_DEBUG, "EAP: Unknown TV/TLV type %u",
+				   tlv_type);
+			pos = end;
+			break;
+		}
+	}
+
+	return 0;
+}
+#endif /* CONFIG_ERP */
diff --git a/hostap/src/eap_common/eap_common.h b/hostap/src/eap_common/eap_common.h
new file mode 100644
index 0000000..e62f167
--- /dev/null
+++ b/hostap/src/eap_common/eap_common.h
@@ -0,0 +1,33 @@
+/*
+ * EAP common peer/server definitions
+ * 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.
+ */
+
+#ifndef EAP_COMMON_H
+#define EAP_COMMON_H
+
+#include "wpabuf.h"
+
+struct erp_tlvs {
+	const u8 *keyname;
+	const u8 *domain;
+
+	u8 keyname_len;
+	u8 domain_len;
+};
+
+int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload);
+const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+			    const struct wpabuf *msg, size_t *plen);
+struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
+			      u8 code, u8 identifier);
+void eap_update_len(struct wpabuf *msg);
+u8 eap_get_id(const struct wpabuf *msg);
+EapType eap_get_type(const struct wpabuf *msg);
+int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs,
+		   int stop_at_keyname);
+
+#endif /* EAP_COMMON_H */
diff --git a/hostap/src/eap_common/eap_defs.h b/hostap/src/eap_common/eap_defs.h
new file mode 100644
index 0000000..54f26ca
--- /dev/null
+++ b/hostap/src/eap_common/eap_defs.h
@@ -0,0 +1,118 @@
+/*
+ * EAP server/peer: Shared EAP definitions
+ * 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.
+ */
+
+#ifndef EAP_DEFS_H
+#define EAP_DEFS_H
+
+/* RFC 3748 - Extensible Authentication Protocol (EAP) */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_hdr {
+	u8 code;
+	u8 identifier;
+	be16 length; /* including code and identifier; network byte order */
+	/* followed by length-4 octets of data */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3,
+       EAP_CODE_FAILURE = 4, EAP_CODE_INITIATE = 5, EAP_CODE_FINISH = 6 };
+
+/* EAP Request and Response data begins with one octet Type. Success and
+ * Failure do not have additional data. */
+
+/* Type field in EAP-Initiate and EAP-Finish messages */
+enum eap_erp_type {
+	EAP_ERP_TYPE_REAUTH_START = 1,
+	EAP_ERP_TYPE_REAUTH = 2,
+};
+
+/* ERP TV/TLV types */
+enum eap_erp_tlv_type {
+	EAP_ERP_TLV_KEYNAME_NAI = 1,
+	EAP_ERP_TV_RRK_LIFETIME = 2,
+	EAP_ERP_TV_RMSK_LIFETIME = 3,
+	EAP_ERP_TLV_DOMAIN_NAME = 4,
+	EAP_ERP_TLV_CRYPTOSUITES = 5,
+	EAP_ERP_TLV_AUTHORIZATION_INDICATION = 6,
+	EAP_ERP_TLV_CALLED_STATION_ID = 128,
+	EAP_ERP_TLV_CALLING_STATION_ID = 129,
+	EAP_ERP_TLV_NAS_IDENTIFIER = 130,
+	EAP_ERP_TLV_NAS_IP_ADDRESS = 131,
+	EAP_ERP_TLV_NAS_IPV6_ADDRESS = 132,
+};
+
+/* ERP Cryptosuite */
+enum eap_erp_cryptosuite {
+	EAP_ERP_CS_HMAC_SHA256_64 = 1,
+	EAP_ERP_CS_HMAC_SHA256_128 = 2,
+	EAP_ERP_CS_HMAC_SHA256_256 = 3,
+};
+
+/*
+ * EAP Method Types as allocated by IANA:
+ * http://www.iana.org/assignments/eap-numbers
+ */
+typedef enum {
+	EAP_TYPE_NONE = 0,
+	EAP_TYPE_IDENTITY = 1 /* RFC 3748 */,
+	EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */,
+	EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */,
+	EAP_TYPE_MD5 = 4, /* RFC 3748 */
+	EAP_TYPE_OTP = 5 /* RFC 3748 */,
+	EAP_TYPE_GTC = 6, /* RFC 3748 */
+	EAP_TYPE_TLS = 13 /* RFC 2716 */,
+	EAP_TYPE_LEAP = 17 /* Cisco proprietary */,
+	EAP_TYPE_SIM = 18 /* RFC 4186 */,
+	EAP_TYPE_TTLS = 21 /* RFC 5281 */,
+	EAP_TYPE_AKA = 23 /* RFC 4187 */,
+	EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */,
+	EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */,
+	EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */,
+	EAP_TYPE_TNC = 38 /* TNC IF-T v1.0-r3; note: tentative assignment;
+			   * type 38 has previously been allocated for
+			   * EAP-HTTP Digest, (funk.com) */,
+	EAP_TYPE_FAST = 43 /* RFC 4851 */,
+	EAP_TYPE_PAX = 46 /* RFC 4746 */,
+	EAP_TYPE_PSK = 47 /* RFC 4764 */,
+	EAP_TYPE_SAKE = 48 /* RFC 4763 */,
+	EAP_TYPE_IKEV2 = 49 /* RFC 5106 */,
+	EAP_TYPE_AKA_PRIME = 50 /* RFC 5448 */,
+	EAP_TYPE_GPSK = 51 /* RFC 5433 */,
+	EAP_TYPE_PWD = 52 /* RFC 5931 */,
+	EAP_TYPE_EKE = 53 /* RFC 6124 */,
+	EAP_TYPE_EXPANDED = 254 /* RFC 3748 */
+} EapType;
+
+
+/* SMI Network Management Private Enterprise Code for vendor specific types */
+enum {
+	EAP_VENDOR_IETF = 0,
+	EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */,
+	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
+#define EAP_EMSK_NAME_LEN 8
+#define ERP_MAX_KEY_LEN 64
+
+#endif /* EAP_DEFS_H */
diff --git a/hostap/src/eap_common/eap_eke_common.c b/hostap/src/eap_common/eap_eke_common.c
new file mode 100644
index 0000000..4dfdb3f
--- /dev/null
+++ b/hostap/src/eap_common/eap_eke_common.c
@@ -0,0 +1,768 @@
+/*
+ * EAP server/peer: EAP-EKE shared routines
+ * Copyright (c) 2011-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_groups.h"
+#include "crypto/random.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "eap_common/eap_defs.h"
+#include "eap_eke_common.h"
+
+
+static int eap_eke_dh_len(u8 group)
+{
+	switch (group) {
+	case EAP_EKE_DHGROUP_EKE_2:
+		return 128;
+	case EAP_EKE_DHGROUP_EKE_5:
+		return 192;
+	case EAP_EKE_DHGROUP_EKE_14:
+		return 256;
+	case EAP_EKE_DHGROUP_EKE_15:
+		return 384;
+	case EAP_EKE_DHGROUP_EKE_16:
+		return 512;
+	}
+
+	return -1;
+}
+
+
+static int eap_eke_dhcomp_len(u8 dhgroup, u8 encr)
+{
+	int dhlen;
+
+	dhlen = eap_eke_dh_len(dhgroup);
+	if (dhlen < 0)
+		return -1;
+	if (encr != EAP_EKE_ENCR_AES128_CBC)
+		return -1;
+	return AES_BLOCK_SIZE + dhlen;
+}
+
+
+static const struct dh_group * eap_eke_dh_group(u8 group)
+{
+	switch (group) {
+	case EAP_EKE_DHGROUP_EKE_2:
+		return dh_groups_get(2);
+	case EAP_EKE_DHGROUP_EKE_5:
+		return dh_groups_get(5);
+	case EAP_EKE_DHGROUP_EKE_14:
+		return dh_groups_get(14);
+	case EAP_EKE_DHGROUP_EKE_15:
+		return dh_groups_get(15);
+	case EAP_EKE_DHGROUP_EKE_16:
+		return dh_groups_get(16);
+	}
+
+	return NULL;
+}
+
+
+static int eap_eke_dh_generator(u8 group)
+{
+	switch (group) {
+	case EAP_EKE_DHGROUP_EKE_2:
+		return 5;
+	case EAP_EKE_DHGROUP_EKE_5:
+		return 31;
+	case EAP_EKE_DHGROUP_EKE_14:
+		return 11;
+	case EAP_EKE_DHGROUP_EKE_15:
+		return 5;
+	case EAP_EKE_DHGROUP_EKE_16:
+		return 5;
+	}
+
+	return -1;
+}
+
+
+static int eap_eke_pnonce_len(u8 mac)
+{
+	int mac_len;
+
+	if (mac == EAP_EKE_MAC_HMAC_SHA1)
+		mac_len = SHA1_MAC_LEN;
+	else if (mac == EAP_EKE_MAC_HMAC_SHA2_256)
+		mac_len = SHA256_MAC_LEN;
+	else
+		return -1;
+
+	return AES_BLOCK_SIZE + 16 + mac_len;
+}
+
+
+static int eap_eke_pnonce_ps_len(u8 mac)
+{
+	int mac_len;
+
+	if (mac == EAP_EKE_MAC_HMAC_SHA1)
+		mac_len = SHA1_MAC_LEN;
+	else if (mac == EAP_EKE_MAC_HMAC_SHA2_256)
+		mac_len = SHA256_MAC_LEN;
+	else
+		return -1;
+
+	return AES_BLOCK_SIZE + 2 * 16 + mac_len;
+}
+
+
+static int eap_eke_prf_len(u8 prf)
+{
+	if (prf == EAP_EKE_PRF_HMAC_SHA1)
+		return 20;
+	if (prf == EAP_EKE_PRF_HMAC_SHA2_256)
+		return 32;
+	return -1;
+}
+
+
+static int eap_eke_nonce_len(u8 prf)
+{
+	int prf_len;
+
+	prf_len = eap_eke_prf_len(prf);
+	if (prf_len < 0)
+		return -1;
+
+	if (prf_len > 2 * 16)
+		return (prf_len + 1) / 2;
+
+	return 16;
+}
+
+
+static int eap_eke_auth_len(u8 prf)
+{
+	switch (prf) {
+	case EAP_EKE_PRF_HMAC_SHA1:
+		return SHA1_MAC_LEN;
+	case EAP_EKE_PRF_HMAC_SHA2_256:
+		return SHA256_MAC_LEN;
+	}
+
+	return -1;
+}
+
+
+int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub)
+{
+	int generator;
+	u8 gen;
+	const struct dh_group *dh;
+	size_t pub_len, i;
+
+	generator = eap_eke_dh_generator(group);
+	if (generator < 0 || generator > 255)
+		return -1;
+	gen = generator;
+
+	dh = eap_eke_dh_group(group);
+	if (dh == NULL)
+		return -1;
+
+	/* x = random number 2 .. p-1 */
+	if (random_get_bytes(ret_priv, dh->prime_len))
+		return -1;
+	if (os_memcmp(ret_priv, dh->prime, dh->prime_len) > 0) {
+		/* Make sure private value is smaller than prime */
+		ret_priv[0] = 0;
+	}
+	for (i = 0; i < dh->prime_len - 1; i++) {
+		if (ret_priv[i])
+			break;
+	}
+	if (i == dh->prime_len - 1 && (ret_priv[i] == 0 || ret_priv[i] == 1))
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: DH private value",
+			ret_priv, dh->prime_len);
+
+	/* y = g ^ x (mod p) */
+	pub_len = dh->prime_len;
+	if (crypto_mod_exp(&gen, 1, ret_priv, dh->prime_len,
+			   dh->prime, dh->prime_len, ret_pub, &pub_len) < 0)
+		return -1;
+	if (pub_len < dh->prime_len) {
+		size_t pad = dh->prime_len - pub_len;
+		os_memmove(ret_pub + pad, ret_pub, pub_len);
+		os_memset(ret_pub, 0, pad);
+	}
+
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: DH public value",
+		    ret_pub, dh->prime_len);
+
+	return 0;
+}
+
+
+static int eap_eke_prf(u8 prf, const u8 *key, size_t key_len, const u8 *data,
+		       size_t data_len, const u8 *data2, size_t data2_len,
+		       u8 *res)
+{
+	const u8 *addr[2];
+	size_t len[2];
+	size_t num_elem = 1;
+
+	addr[0] = data;
+	len[0] = data_len;
+	if (data2) {
+		num_elem++;
+		addr[1] = data2;
+		len[1] = data2_len;
+	}
+
+	if (prf == EAP_EKE_PRF_HMAC_SHA1)
+		return hmac_sha1_vector(key, key_len, num_elem, addr, len, res);
+	if (prf == EAP_EKE_PRF_HMAC_SHA2_256)
+		return hmac_sha256_vector(key, key_len, num_elem, addr, len,
+					  res);
+	return -1;
+}
+
+
+static int eap_eke_prf_hmac_sha1(const u8 *key, size_t key_len, const u8 *data,
+				 size_t data_len, u8 *res, size_t len)
+{
+	u8 hash[SHA1_MAC_LEN];
+	u8 idx;
+	const u8 *addr[3];
+	size_t vlen[3];
+	int ret;
+
+	idx = 0;
+	addr[0] = hash;
+	vlen[0] = SHA1_MAC_LEN;
+	addr[1] = data;
+	vlen[1] = data_len;
+	addr[2] = &idx;
+	vlen[2] = 1;
+
+	while (len > 0) {
+		idx++;
+		if (idx == 1)
+			ret = hmac_sha1_vector(key, key_len, 2, &addr[1],
+					       &vlen[1], hash);
+		else
+			ret = hmac_sha1_vector(key, key_len, 3, addr, vlen,
+					       hash);
+		if (ret < 0)
+			return -1;
+		if (len > SHA1_MAC_LEN) {
+			os_memcpy(res, hash, SHA1_MAC_LEN);
+			res += SHA1_MAC_LEN;
+			len -= SHA1_MAC_LEN;
+		} else {
+			os_memcpy(res, hash, len);
+			len = 0;
+		}
+	}
+
+	return 0;
+}
+
+
+static int eap_eke_prf_hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+				   size_t data_len, u8 *res, size_t len)
+{
+	u8 hash[SHA256_MAC_LEN];
+	u8 idx;
+	const u8 *addr[3];
+	size_t vlen[3];
+	int ret;
+
+	idx = 0;
+	addr[0] = hash;
+	vlen[0] = SHA256_MAC_LEN;
+	addr[1] = data;
+	vlen[1] = data_len;
+	addr[2] = &idx;
+	vlen[2] = 1;
+
+	while (len > 0) {
+		idx++;
+		if (idx == 1)
+			ret = hmac_sha256_vector(key, key_len, 2, &addr[1],
+						 &vlen[1], hash);
+		else
+			ret = hmac_sha256_vector(key, key_len, 3, addr, vlen,
+						 hash);
+		if (ret < 0)
+			return -1;
+		if (len > SHA256_MAC_LEN) {
+			os_memcpy(res, hash, SHA256_MAC_LEN);
+			res += SHA256_MAC_LEN;
+			len -= SHA256_MAC_LEN;
+		} else {
+			os_memcpy(res, hash, len);
+			len = 0;
+		}
+	}
+
+	return 0;
+}
+
+
+static int eap_eke_prfplus(u8 prf, const u8 *key, size_t key_len,
+			   const u8 *data, size_t data_len, u8 *res, size_t len)
+{
+	if (prf == EAP_EKE_PRF_HMAC_SHA1)
+		return eap_eke_prf_hmac_sha1(key, key_len, data, data_len, res,
+					     len);
+	if (prf == EAP_EKE_PRF_HMAC_SHA2_256)
+		return eap_eke_prf_hmac_sha256(key, key_len, data, data_len,
+					       res, len);
+	return -1;
+}
+
+
+int eap_eke_derive_key(struct eap_eke_session *sess,
+		       const u8 *password, size_t password_len,
+		       const u8 *id_s, size_t id_s_len, const u8 *id_p,
+		       size_t id_p_len, u8 *key)
+{
+	u8 zeros[EAP_EKE_MAX_HASH_LEN];
+	u8 temp[EAP_EKE_MAX_HASH_LEN];
+	size_t key_len = 16; /* Only AES-128-CBC is used here */
+	u8 *id;
+
+	/* temp = prf(0+, password) */
+	os_memset(zeros, 0, sess->prf_len);
+	if (eap_eke_prf(sess->prf, zeros, sess->prf_len,
+			password, password_len, NULL, 0, temp) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: temp = prf(0+, password)",
+			temp, sess->prf_len);
+
+	/* key = prf+(temp, ID_S | ID_P) */
+	id = os_malloc(id_s_len + id_p_len);
+	if (id == NULL)
+		return -1;
+	os_memcpy(id, id_s, id_s_len);
+	os_memcpy(id + id_s_len, id_p, id_p_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: ID_S | ID_P",
+			  id, id_s_len + id_p_len);
+	if (eap_eke_prfplus(sess->prf, temp, sess->prf_len,
+			    id, id_s_len + id_p_len, key, key_len) < 0) {
+		os_free(id);
+		return -1;
+	}
+	os_free(id);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: key = prf+(temp, ID_S | ID_P)",
+			key, key_len);
+
+	return 0;
+}
+
+
+int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub,
+		   u8 *ret_dhcomp)
+{
+	u8 pub[EAP_EKE_MAX_DH_LEN];
+	int dh_len;
+	u8 iv[AES_BLOCK_SIZE];
+
+	dh_len = eap_eke_dh_len(sess->dhgroup);
+	if (dh_len < 0)
+		return -1;
+
+	/*
+	 * DHComponent = Encr(key, y)
+	 *
+	 * All defined DH groups use primes that have length devisible by 16, so
+	 * no need to do extra padding for y (= pub).
+	 */
+	if (sess->encr != EAP_EKE_ENCR_AES128_CBC)
+		return -1;
+	if (random_get_bytes(iv, AES_BLOCK_SIZE))
+		return -1;
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: IV for Encr(key, y)",
+		    iv, AES_BLOCK_SIZE);
+	os_memcpy(pub, dhpub, dh_len);
+	if (aes_128_cbc_encrypt(key, iv, pub, dh_len) < 0)
+		return -1;
+	os_memcpy(ret_dhcomp, iv, AES_BLOCK_SIZE);
+	os_memcpy(ret_dhcomp + AES_BLOCK_SIZE, pub, dh_len);
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent = Encr(key, y)",
+		    ret_dhcomp, AES_BLOCK_SIZE + dh_len);
+
+	return 0;
+}
+
+
+int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key,
+			  const u8 *dhpriv, const u8 *peer_dhcomp)
+{
+	u8 zeros[EAP_EKE_MAX_HASH_LEN];
+	u8 peer_pub[EAP_EKE_MAX_DH_LEN];
+	u8 modexp[EAP_EKE_MAX_DH_LEN];
+	size_t len;
+	const struct dh_group *dh;
+
+	if (sess->encr != EAP_EKE_ENCR_AES128_CBC)
+		return -1;
+
+	dh = eap_eke_dh_group(sess->dhgroup);
+	if (dh == NULL)
+		return -1;
+
+	/* Decrypt peer DHComponent */
+	os_memcpy(peer_pub, peer_dhcomp + AES_BLOCK_SIZE, dh->prime_len);
+	if (aes_128_cbc_decrypt(key, peer_dhcomp, peer_pub, dh->prime_len) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt DHComponent");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted peer DH pubkey",
+			peer_pub, dh->prime_len);
+
+	/* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */
+	len = dh->prime_len;
+	if (crypto_mod_exp(peer_pub, dh->prime_len, dhpriv, dh->prime_len,
+			   dh->prime, dh->prime_len, modexp, &len) < 0)
+		return -1;
+	if (len < dh->prime_len) {
+		size_t pad = dh->prime_len - len;
+		os_memmove(modexp + pad, modexp, len);
+		os_memset(modexp, 0, pad);
+	}
+
+	os_memset(zeros, 0, sess->auth_len);
+	if (eap_eke_prf(sess->prf, zeros, sess->auth_len, modexp, dh->prime_len,
+			NULL, 0, sess->shared_secret) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: SharedSecret",
+			sess->shared_secret, sess->auth_len);
+
+	return 0;
+}
+
+
+int eap_eke_derive_ke_ki(struct eap_eke_session *sess,
+			 const u8 *id_s, size_t id_s_len,
+			 const u8 *id_p, size_t id_p_len)
+{
+	u8 buf[EAP_EKE_MAX_KE_LEN + EAP_EKE_MAX_KI_LEN];
+	size_t ke_len, ki_len;
+	u8 *data;
+	size_t data_len;
+	const char *label = "EAP-EKE Keys";
+	size_t label_len;
+
+	/*
+	 * Ke | Ki = prf+(SharedSecret, "EAP-EKE Keys" | ID_S | ID_P)
+	 * Ke = encryption key
+	 * Ki = integrity protection key
+	 * Length of each key depends on the selected algorithms.
+	 */
+
+	if (sess->encr == EAP_EKE_ENCR_AES128_CBC)
+		ke_len = 16;
+	else
+		return -1;
+
+	if (sess->mac == EAP_EKE_PRF_HMAC_SHA1)
+		ki_len = 20;
+	else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256)
+		ki_len = 32;
+	else
+		return -1;
+
+	label_len = os_strlen(label);
+	data_len = label_len + id_s_len + id_p_len;
+	data = os_malloc(data_len);
+	if (data == NULL)
+		return -1;
+	os_memcpy(data, label, label_len);
+	os_memcpy(data + label_len, id_s, id_s_len);
+	os_memcpy(data + label_len + id_s_len, id_p, id_p_len);
+	if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len,
+			    data, data_len, buf, ke_len + ki_len) < 0) {
+		os_free(data);
+		return -1;
+	}
+
+	os_memcpy(sess->ke, buf, ke_len);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ke", sess->ke, ke_len);
+	os_memcpy(sess->ki, buf + ke_len, ki_len);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ki", sess->ki, ki_len);
+
+	os_free(data);
+	return 0;
+}
+
+
+int eap_eke_derive_ka(struct eap_eke_session *sess,
+		      const u8 *id_s, size_t id_s_len,
+		      const u8 *id_p, size_t id_p_len,
+		      const u8 *nonce_p, const u8 *nonce_s)
+{
+	u8 *data, *pos;
+	size_t data_len;
+	const char *label = "EAP-EKE Ka";
+	size_t label_len;
+
+	/*
+	 * Ka = prf+(SharedSecret, "EAP-EKE Ka" | ID_S | ID_P | Nonce_P |
+	 *	     Nonce_S)
+	 * Ka = authentication key
+	 * Length of the key depends on the selected algorithms.
+	 */
+
+	label_len = os_strlen(label);
+	data_len = label_len + id_s_len + id_p_len + 2 * sess->nonce_len;
+	data = os_malloc(data_len);
+	if (data == NULL)
+		return -1;
+	pos = data;
+	os_memcpy(pos, label, label_len);
+	pos += label_len;
+	os_memcpy(pos, id_s, id_s_len);
+	pos += id_s_len;
+	os_memcpy(pos, id_p, id_p_len);
+	pos += id_p_len;
+	os_memcpy(pos, nonce_p, sess->nonce_len);
+	pos += sess->nonce_len;
+	os_memcpy(pos, nonce_s, sess->nonce_len);
+	if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len,
+			    data, data_len, sess->ka, sess->prf_len) < 0) {
+		os_free(data);
+		return -1;
+	}
+	os_free(data);
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ka", sess->ka, sess->prf_len);
+
+	return 0;
+}
+
+
+int eap_eke_derive_msk(struct eap_eke_session *sess,
+		       const u8 *id_s, size_t id_s_len,
+		       const u8 *id_p, size_t id_p_len,
+		       const u8 *nonce_p, const u8 *nonce_s,
+		       u8 *msk, u8 *emsk)
+{
+	u8 *data, *pos;
+	size_t data_len;
+	const char *label = "EAP-EKE Exported Keys";
+	size_t label_len;
+	u8 buf[EAP_MSK_LEN + EAP_EMSK_LEN];
+
+	/*
+	 * MSK | EMSK = prf+(SharedSecret, "EAP-EKE Exported Keys" | ID_S |
+	 *		     ID_P | Nonce_P | Nonce_S)
+	 */
+
+	label_len = os_strlen(label);
+	data_len = label_len + id_s_len + id_p_len + 2 * sess->nonce_len;
+	data = os_malloc(data_len);
+	if (data == NULL)
+		return -1;
+	pos = data;
+	os_memcpy(pos, label, label_len);
+	pos += label_len;
+	os_memcpy(pos, id_s, id_s_len);
+	pos += id_s_len;
+	os_memcpy(pos, id_p, id_p_len);
+	pos += id_p_len;
+	os_memcpy(pos, nonce_p, sess->nonce_len);
+	pos += sess->nonce_len;
+	os_memcpy(pos, nonce_s, sess->nonce_len);
+	if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len,
+			    data, data_len, buf, EAP_MSK_LEN + EAP_EMSK_LEN) <
+	    0) {
+		os_free(data);
+		return -1;
+	}
+	os_free(data);
+
+	os_memcpy(msk, buf, EAP_MSK_LEN);
+	os_memcpy(emsk, buf + EAP_MSK_LEN, EAP_EMSK_LEN);
+	os_memset(buf, 0, sizeof(buf));
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: MSK", msk, EAP_MSK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: EMSK", msk, EAP_EMSK_LEN);
+
+	return 0;
+}
+
+
+static int eap_eke_mac(u8 mac, const u8 *key, const u8 *data, size_t data_len,
+		       u8 *res)
+{
+	if (mac == EAP_EKE_MAC_HMAC_SHA1)
+		return hmac_sha1(key, SHA1_MAC_LEN, data, data_len, res);
+	if (mac == EAP_EKE_MAC_HMAC_SHA2_256)
+		return hmac_sha256(key, SHA256_MAC_LEN, data, data_len, res);
+	return -1;
+}
+
+
+int eap_eke_prot(struct eap_eke_session *sess,
+		 const u8 *data, size_t data_len,
+		 u8 *prot, size_t *prot_len)
+{
+	size_t block_size, icv_len, pad;
+	u8 *pos, *iv, *e;
+
+	if (sess->encr == EAP_EKE_ENCR_AES128_CBC)
+		block_size = AES_BLOCK_SIZE;
+	else
+		return -1;
+
+	if (sess->mac == EAP_EKE_PRF_HMAC_SHA1)
+		icv_len = SHA1_MAC_LEN;
+	else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256)
+		icv_len = SHA256_MAC_LEN;
+	else
+		return -1;
+
+	pad = data_len % block_size;
+	if (pad)
+		pad = block_size - pad;
+
+	if (*prot_len < block_size + data_len + pad + icv_len) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for Prot() data");
+	}
+	pos = prot;
+
+	if (random_get_bytes(pos, block_size))
+		return -1;
+	iv = pos;
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: IV for Prot()", iv, block_size);
+	pos += block_size;
+
+	e = pos;
+	os_memcpy(pos, data, data_len);
+	pos += data_len;
+	if (pad) {
+		if (random_get_bytes(pos, pad))
+			return -1;
+		pos += pad;
+	}
+
+	if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0)
+		return -1;
+
+	if (eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0)
+		return -1;
+	pos += icv_len;
+
+	*prot_len = pos - prot;
+	return 0;
+}
+
+
+int eap_eke_decrypt_prot(struct eap_eke_session *sess,
+			 const u8 *prot, size_t prot_len,
+			 u8 *data, size_t *data_len)
+{
+	size_t block_size, icv_len;
+	u8 icv[EAP_EKE_MAX_HASH_LEN];
+
+	if (sess->encr == EAP_EKE_ENCR_AES128_CBC)
+		block_size = AES_BLOCK_SIZE;
+	else
+		return -1;
+
+	if (sess->mac == EAP_EKE_PRF_HMAC_SHA1)
+		icv_len = SHA1_MAC_LEN;
+	else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256)
+		icv_len = SHA256_MAC_LEN;
+	else
+		return -1;
+
+	if (prot_len < 2 * block_size + icv_len)
+		return -1;
+	if ((prot_len - icv_len) % block_size)
+		return -1;
+
+	if (eap_eke_mac(sess->mac, sess->ki, prot + block_size,
+			prot_len - block_size - icv_len, icv) < 0)
+		return -1;
+	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;
+	}
+
+	if (*data_len < prot_len - block_size - icv_len) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for decrypted Prot() data");
+		return -1;
+	}
+
+	*data_len = prot_len - block_size - icv_len;
+	os_memcpy(data, prot + block_size, *data_len);
+	if (aes_128_cbc_decrypt(sess->ke, prot, data, *data_len) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt Prot() data");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted Prot() data",
+			data, *data_len);
+
+	return 0;
+}
+
+
+int eap_eke_auth(struct eap_eke_session *sess, const char *label,
+		 const struct wpabuf *msgs, u8 *auth)
+{
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Auth(%s)", label);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ka for Auth",
+			sess->ka, sess->auth_len);
+	wpa_hexdump_buf(MSG_MSGDUMP, "EAP-EKE: Messages for Auth", msgs);
+	return eap_eke_prf(sess->prf, sess->ka, sess->auth_len,
+			   (const u8 *) label, os_strlen(label),
+			   wpabuf_head(msgs), wpabuf_len(msgs), auth);
+}
+
+
+int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr,
+			 u8 prf, u8 mac)
+{
+	sess->dhgroup = dhgroup;
+	sess->encr = encr;
+	sess->prf = prf;
+	sess->mac = mac;
+
+	sess->prf_len = eap_eke_prf_len(prf);
+	if (sess->prf_len < 0)
+		return -1;
+	sess->nonce_len = eap_eke_nonce_len(prf);
+	if (sess->nonce_len < 0)
+		return -1;
+	sess->auth_len = eap_eke_auth_len(prf);
+	if (sess->auth_len < 0)
+		return -1;
+	sess->dhcomp_len = eap_eke_dhcomp_len(sess->dhgroup, sess->encr);
+	if (sess->dhcomp_len < 0)
+		return -1;
+	sess->pnonce_len = eap_eke_pnonce_len(sess->mac);
+	if (sess->pnonce_len < 0)
+		return -1;
+	sess->pnonce_ps_len = eap_eke_pnonce_ps_len(sess->mac);
+	if (sess->pnonce_ps_len < 0)
+		return -1;
+
+	return 0;
+}
+
+
+void eap_eke_session_clean(struct eap_eke_session *sess)
+{
+	os_memset(sess->shared_secret, 0, EAP_EKE_MAX_HASH_LEN);
+	os_memset(sess->ke, 0, EAP_EKE_MAX_KE_LEN);
+	os_memset(sess->ki, 0, EAP_EKE_MAX_KI_LEN);
+	os_memset(sess->ka, 0, EAP_EKE_MAX_KA_LEN);
+}
diff --git a/hostap/src/eap_common/eap_eke_common.h b/hostap/src/eap_common/eap_eke_common.h
new file mode 100644
index 0000000..a4c0422
--- /dev/null
+++ b/hostap/src/eap_common/eap_eke_common.h
@@ -0,0 +1,114 @@
+/*
+ * EAP server/peer: EAP-EKE shared routines
+ * Copyright (c) 2011-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_EKE_COMMON_H
+#define EAP_EKE_COMMON_H
+
+/* EKE Exchange */
+#define EAP_EKE_ID 1
+#define EAP_EKE_COMMIT 2
+#define EAP_EKE_CONFIRM 3
+#define EAP_EKE_FAILURE 4
+
+/* Diffie-Hellman Group Registry */
+#define EAP_EKE_DHGROUP_EKE_2 1
+#define EAP_EKE_DHGROUP_EKE_5 2
+#define EAP_EKE_DHGROUP_EKE_14 3 /* mandatory to implement */
+#define EAP_EKE_DHGROUP_EKE_15 4
+#define EAP_EKE_DHGROUP_EKE_16 5
+
+/* Encryption Algorithm Registry */
+#define EAP_EKE_ENCR_AES128_CBC 1 /* mandatory to implement */
+
+/* Pseudo Random Function Registry */
+#define EAP_EKE_PRF_HMAC_SHA1 1 /* mandatory to implement */
+#define EAP_EKE_PRF_HMAC_SHA2_256 2
+
+/* Keyed Message Digest (MAC) Registry */
+#define EAP_EKE_MAC_HMAC_SHA1 1 /* mandatory to implement */
+#define EAP_EKE_MAC_HMAC_SHA2_256 2
+
+/* Identity Type Registry */
+#define EAP_EKE_ID_OPAQUE 1
+#define EAP_EKE_ID_NAI 2
+#define EAP_EKE_ID_IPv4 3
+#define EAP_EKE_ID_IPv6 4
+#define EAP_EKE_ID_FQDN 5
+#define EAP_EKE_ID_DN 6
+
+/* Failure-Code */
+#define EAP_EKE_FAIL_NO_ERROR 1
+#define EAP_EKE_FAIL_PROTO_ERROR 2
+#define EAP_EKE_FAIL_PASSWD_NOT_FOUND 3
+#define EAP_EKE_FAIL_AUTHENTICATION_FAIL 4
+#define EAP_EKE_FAIL_AUTHORIZATION_FAIL 5
+#define EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN 6
+#define EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR 0xffffffff
+
+#define EAP_EKE_MAX_DH_LEN 512
+#define EAP_EKE_MAX_HASH_LEN 32
+#define EAP_EKE_MAX_KEY_LEN 16
+#define EAP_EKE_MAX_KE_LEN 16
+#define EAP_EKE_MAX_KI_LEN 32
+#define EAP_EKE_MAX_KA_LEN 32
+#define EAP_EKE_MAX_NONCE_LEN 16
+
+struct eap_eke_session {
+	/* Selected proposal */
+	u8 dhgroup;
+	u8 encr;
+	u8 prf;
+	u8 mac;
+
+	u8 shared_secret[EAP_EKE_MAX_HASH_LEN];
+	u8 ke[EAP_EKE_MAX_KE_LEN];
+	u8 ki[EAP_EKE_MAX_KI_LEN];
+	u8 ka[EAP_EKE_MAX_KA_LEN];
+
+	int prf_len;
+	int nonce_len;
+	int auth_len;
+	int dhcomp_len;
+	int pnonce_len;
+	int pnonce_ps_len;
+};
+
+int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr,
+			 u8 prf, u8 mac);
+void eap_eke_session_clean(struct eap_eke_session *sess);
+int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub);
+int eap_eke_derive_key(struct eap_eke_session *sess,
+		       const u8 *password, size_t password_len,
+		       const u8 *id_s, size_t id_s_len, const u8 *id_p,
+		       size_t id_p_len, u8 *key);
+int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub,
+		   u8 *ret_dhcomp);
+int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key,
+			  const u8 *dhpriv, const u8 *peer_dhcomp);
+int eap_eke_derive_ke_ki(struct eap_eke_session *sess,
+			 const u8 *id_s, size_t id_s_len,
+			 const u8 *id_p, size_t id_p_len);
+int eap_eke_derive_ka(struct eap_eke_session *sess,
+		      const u8 *id_s, size_t id_s_len,
+		      const u8 *id_p, size_t id_p_len,
+		      const u8 *nonce_p, const u8 *nonce_s);
+int eap_eke_derive_msk(struct eap_eke_session *sess,
+		       const u8 *id_s, size_t id_s_len,
+		       const u8 *id_p, size_t id_p_len,
+		       const u8 *nonce_p, const u8 *nonce_s,
+		       u8 *msk, u8 *emsk);
+int eap_eke_prot(struct eap_eke_session *sess,
+		 const u8 *data, size_t data_len,
+		 u8 *prot, size_t *prot_len);
+int eap_eke_decrypt_prot(struct eap_eke_session *sess,
+			 const u8 *prot, size_t prot_len,
+			 u8 *data, size_t *data_len);
+int eap_eke_auth(struct eap_eke_session *sess, const char *label,
+		 const struct wpabuf *msgs, u8 *auth);
+
+#endif /* EAP_EKE_COMMON_H */
diff --git a/hostap/src/eap_common/eap_fast_common.c b/hostap/src/eap_common/eap_fast_common.c
new file mode 100644
index 0000000..151cc78
--- /dev/null
+++ b/hostap/src/eap_common/eap_fast_common.c
@@ -0,0 +1,267 @@
+/*
+ * EAP-FAST common helper functions (RFC 4851)
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_defs.h"
+#include "eap_tlv_common.h"
+#include "eap_fast_common.h"
+
+
+void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len)
+{
+	struct pac_tlv_hdr hdr;
+	hdr.type = host_to_be16(type);
+	hdr.len = host_to_be16(len);
+	wpabuf_put_data(buf, &hdr, sizeof(hdr));
+}
+
+
+void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
+			     u16 len)
+{
+	eap_fast_put_tlv_hdr(buf, type, len);
+	wpabuf_put_data(buf, data, len);
+}
+
+
+void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
+				 const struct wpabuf *data)
+{
+	eap_fast_put_tlv_hdr(buf, type, wpabuf_len(data));
+	wpabuf_put_buf(buf, data);
+}
+
+
+struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf)
+{
+	struct wpabuf *e;
+
+	if (buf == NULL)
+		return NULL;
+
+	/* Encapsulate EAP packet in EAP-Payload TLV */
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Add EAP-Payload TLV");
+	e = wpabuf_alloc(sizeof(struct pac_tlv_hdr) + wpabuf_len(buf));
+	if (e == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory "
+			   "for TLV encapsulation");
+		wpabuf_free(buf);
+		return NULL;
+	}
+	eap_fast_put_tlv_buf(e,
+			     EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV,
+			     buf);
+	wpabuf_free(buf);
+	return e;
+}
+
+
+void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
+				   const u8 *client_random, u8 *master_secret)
+{
+#define TLS_RANDOM_LEN 32
+#define TLS_MASTER_SECRET_LEN 48
+	u8 seed[2 * TLS_RANDOM_LEN];
+
+	wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random",
+		    client_random, TLS_RANDOM_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random",
+		    server_random, TLS_RANDOM_LEN);
+
+	/*
+	 * RFC 4851, Section 5.1:
+	 * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash", 
+	 *                       server_random + client_random, 48)
+	 */
+	os_memcpy(seed, server_random, TLS_RANDOM_LEN);
+	os_memcpy(seed + TLS_RANDOM_LEN, client_random, TLS_RANDOM_LEN);
+	sha1_t_prf(pac_key, EAP_FAST_PAC_KEY_LEN,
+		   "PAC to master secret label hash",
+		   seed, sizeof(seed), master_secret, TLS_MASTER_SECRET_LEN);
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: master_secret",
+			master_secret, TLS_MASTER_SECRET_LEN);
+}
+
+
+u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
+			 const char *label, size_t len)
+{
+	u8 *out;
+
+	out = os_malloc(len);
+	if (out == NULL)
+		return NULL;
+
+	if (tls_connection_prf(ssl_ctx, conn, label, 1, 1, out, len)) {
+		os_free(out);
+		return NULL;
+	}
+
+	return out;
+}
+
+
+void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk)
+{
+	/*
+	 * RFC 4851, Section 5.4: EAP Master Session Key Generation
+	 * MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64)
+	 */
+
+	sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
+		   "Session Key Generating Function", (u8 *) "", 0,
+		   msk, EAP_FAST_KEY_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)",
+			msk, EAP_FAST_KEY_LEN);
+}
+
+
+void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk)
+{
+	/*
+	 * RFC 4851, Section 5.4: EAP Master Session Key Genreration
+	 * EMSK = T-PRF(S-IMCK[j],
+	 *        "Extended Session Key Generating Function", 64)
+	 */
+
+	sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
+		   "Extended Session Key Generating Function", (u8 *) "", 0,
+		   emsk, EAP_EMSK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)",
+			emsk, EAP_EMSK_LEN);
+}
+
+
+int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
+		       int tlv_type, u8 *pos, size_t len)
+{
+	switch (tlv_type) {
+	case EAP_TLV_EAP_PAYLOAD_TLV:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP-Payload TLV",
+			    pos, len);
+		if (tlv->eap_payload_tlv) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+				   "EAP-Payload TLV in the message");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			return -2;
+		}
+		tlv->eap_payload_tlv = pos;
+		tlv->eap_payload_tlv_len = len;
+		break;
+	case EAP_TLV_RESULT_TLV:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", pos, len);
+		if (tlv->result) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+				   "Result TLV in the message");
+			tlv->result = EAP_TLV_RESULT_FAILURE;
+			return -2;
+		}
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+				   "Result TLV");
+			tlv->result = EAP_TLV_RESULT_FAILURE;
+			break;
+		}
+		tlv->result = WPA_GET_BE16(pos);
+		if (tlv->result != EAP_TLV_RESULT_SUCCESS &&
+		    tlv->result != EAP_TLV_RESULT_FAILURE) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Result %d",
+				   tlv->result);
+			tlv->result = EAP_TLV_RESULT_FAILURE;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s",
+			   tlv->result == EAP_TLV_RESULT_SUCCESS ?
+			   "Success" : "Failure");
+		break;
+	case EAP_TLV_INTERMEDIATE_RESULT_TLV:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate Result TLV",
+			    pos, len);
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+				   "Intermediate-Result TLV");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			break;
+		}
+		if (tlv->iresult) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+				   "Intermediate-Result TLV in the message");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			return -2;
+		}
+		tlv->iresult = WPA_GET_BE16(pos);
+		if (tlv->iresult != EAP_TLV_RESULT_SUCCESS &&
+		    tlv->iresult != EAP_TLV_RESULT_FAILURE) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Intermediate "
+				   "Result %d", tlv->iresult);
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Intermediate Result: %s",
+			   tlv->iresult == EAP_TLV_RESULT_SUCCESS ?
+			   "Success" : "Failure");
+		break;
+	case EAP_TLV_CRYPTO_BINDING_TLV:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV",
+			    pos, len);
+		if (tlv->crypto_binding) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+				   "Crypto-Binding TLV in the message");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			return -2;
+		}
+		tlv->crypto_binding_len = sizeof(struct eap_tlv_hdr) + len;
+		if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+				   "Crypto-Binding TLV");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			return -2;
+		}
+		tlv->crypto_binding = (struct eap_tlv_crypto_binding_tlv *)
+			(pos - sizeof(struct eap_tlv_hdr));
+		break;
+	case EAP_TLV_REQUEST_ACTION_TLV:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Request-Action TLV",
+			    pos, len);
+		if (tlv->request_action) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+				   "Request-Action TLV in the message");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			return -2;
+		}
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+				   "Request-Action TLV");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			break;
+		}
+		tlv->request_action = WPA_GET_BE16(pos);
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Request-Action: %d",
+			   tlv->request_action);
+		break;
+	case EAP_TLV_PAC_TLV:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", pos, len);
+		if (tlv->pac) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+				   "PAC TLV in the message");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			return -2;
+		}
+		tlv->pac = pos;
+		tlv->pac_len = len;
+		break;
+	default:
+		/* Unknown TLV */
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/hostap/src/eap_common/eap_fast_common.h b/hostap/src/eap_common/eap_fast_common.h
new file mode 100644
index 0000000..d59a845
--- /dev/null
+++ b/hostap/src/eap_common/eap_fast_common.h
@@ -0,0 +1,107 @@
+/*
+ * EAP-FAST definitions (RFC 4851)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_FAST_H
+#define EAP_FAST_H
+
+#define EAP_FAST_VERSION 1
+#define EAP_FAST_KEY_LEN 64
+#define EAP_FAST_SIMCK_LEN 40
+#define EAP_FAST_SKS_LEN 40
+#define EAP_FAST_CMK_LEN 20
+
+#define TLS_EXT_PAC_OPAQUE 35
+
+/*
+ * RFC 5422: Section 4.2.1 - Formats for PAC TLV Attributes / Type Field
+ * Note: bit 0x8000 (Mandatory) and bit 0x4000 (Reserved) are also defined
+ * in the general PAC TLV format (Section 4.2).
+ */
+#define PAC_TYPE_PAC_KEY 1
+#define PAC_TYPE_PAC_OPAQUE 2
+#define PAC_TYPE_CRED_LIFETIME 3
+#define PAC_TYPE_A_ID 4
+#define PAC_TYPE_I_ID 5
+/*
+ * 6 was previous assigned for SERVER_PROTECTED_DATA, but
+ * draft-cam-winget-eap-fast-provisioning-02.txt changed this to Reserved.
+ */
+#define PAC_TYPE_A_ID_INFO 7
+#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8
+#define PAC_TYPE_PAC_INFO 9
+#define PAC_TYPE_PAC_TYPE 10
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct pac_tlv_hdr {
+	be16 type;
+	be16 len;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+#define EAP_FAST_PAC_KEY_LEN 32
+
+/* RFC 5422: 4.2.6 PAC-Type TLV */
+#define PAC_TYPE_TUNNEL_PAC 1
+/* Application Specific Short Lived PACs (only in volatile storage) */
+/* User Authorization PAC */
+#define PAC_TYPE_USER_AUTHORIZATION 3
+/* Application Specific Long Lived PACs */
+/* Machine Authentication PAC */
+#define PAC_TYPE_MACHINE_AUTHENTICATION 2
+
+
+/*
+ * RFC 5422:
+ * Section 3.3 - Key Derivations Used in the EAP-FAST Provisioning Exchange
+ */
+struct eap_fast_key_block_provisioning {
+	/* Extra key material after TLS key_block */
+	u8 session_key_seed[EAP_FAST_SKS_LEN];
+	u8 server_challenge[16]; /* MSCHAPv2 ServerChallenge */
+	u8 client_challenge[16]; /* MSCHAPv2 ClientChallenge */
+};
+
+
+struct wpabuf;
+struct tls_connection;
+
+struct eap_fast_tlv_parse {
+	u8 *eap_payload_tlv;
+	size_t eap_payload_tlv_len;
+	struct eap_tlv_crypto_binding_tlv *crypto_binding;
+	size_t crypto_binding_len;
+	int iresult;
+	int result;
+	int request_action;
+	u8 *pac;
+	size_t pac_len;
+};
+
+void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len);
+void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
+		      u16 len);
+void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
+			  const struct wpabuf *data);
+struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf);
+void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
+				   const u8 *client_random, u8 *master_secret);
+u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
+			 const char *label, size_t len);
+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, size_t len);
+
+#endif /* EAP_FAST_H */
diff --git a/hostap/src/eap_common/eap_gpsk_common.c b/hostap/src/eap_common/eap_gpsk_common.c
new file mode 100644
index 0000000..8c7ae27
--- /dev/null
+++ b/hostap/src/eap_common/eap_gpsk_common.c
@@ -0,0 +1,549 @@
+/*
+ * EAP server/peer: EAP-GPSK shared routines
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/sha256.h"
+#include "eap_defs.h"
+#include "eap_gpsk_common.h"
+
+
+/**
+ * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * Returns: 1 if ciphersuite is support, or 0 if not
+ */
+int eap_gpsk_supported_ciphersuite(int vendor, int specifier)
+{
+	if (vendor == EAP_GPSK_VENDOR_IETF &&
+	    specifier == EAP_GPSK_CIPHER_AES)
+		return 1;
+#ifdef EAP_GPSK_SHA256
+	if (vendor == EAP_GPSK_VENDOR_IETF &&
+	    specifier == EAP_GPSK_CIPHER_SHA256)
+		return 1;
+#endif /* EAP_GPSK_SHA256 */
+	return 0;
+}
+
+
+static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */,
+			      const u8 *data /* Z */, size_t data_len,
+			      u8 *buf, size_t len /* X */)
+{
+	u8 *opos;
+	size_t i, n, hashlen, left, clen;
+	u8 ibuf[2], hash[16];
+	const u8 *addr[2];
+	size_t vlen[2];
+
+	hashlen = sizeof(hash);
+	/* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */
+	addr[0] = ibuf;
+	vlen[0] = sizeof(ibuf);
+	addr[1] = data;
+	vlen[1] = data_len;
+
+	opos = buf;
+	left = len;
+	n = (len + hashlen - 1) / hashlen;
+	for (i = 1; i <= n; i++) {
+		WPA_PUT_BE16(ibuf, i);
+		if (omac1_aes_128_vector(psk, 2, addr, vlen, hash))
+			return -1;
+		clen = left > hashlen ? hashlen : left;
+		os_memcpy(opos, hash, clen);
+		opos += clen;
+		left -= clen;
+	}
+
+	return 0;
+}
+
+
+#ifdef EAP_GPSK_SHA256
+static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */,
+				const u8 *data /* Z */, size_t data_len,
+				u8 *buf, size_t len /* X */)
+{
+	u8 *opos;
+	size_t i, n, hashlen, left, clen;
+	u8 ibuf[2], hash[SHA256_MAC_LEN];
+	const u8 *addr[2];
+	size_t vlen[2];
+
+	hashlen = SHA256_MAC_LEN;
+	/* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */
+	addr[0] = ibuf;
+	vlen[0] = sizeof(ibuf);
+	addr[1] = data;
+	vlen[1] = data_len;
+
+	opos = buf;
+	left = len;
+	n = (len + hashlen - 1) / hashlen;
+	for (i = 1; i <= n; i++) {
+		WPA_PUT_BE16(ibuf, i);
+		hmac_sha256_vector(psk, 32, 2, addr, vlen, hash);
+		clen = left > hashlen ? hashlen : left;
+		os_memcpy(opos, hash, clen);
+		opos += clen;
+		left -= clen;
+	}
+
+	return 0;
+}
+#endif /* EAP_GPSK_SHA256 */
+
+
+static int eap_gpsk_derive_keys_helper(u32 csuite_specifier,
+				       u8 *kdf_out, size_t kdf_out_len,
+				       const u8 *psk, size_t psk_len,
+				       const u8 *seed, size_t seed_len,
+				       u8 *msk, u8 *emsk,
+				       u8 *sk, size_t sk_len,
+				       u8 *pk, size_t pk_len)
+{
+	u8 mk[32], *pos, *data;
+	size_t data_len, mk_len;
+	int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
+		    u8 *buf, size_t len);
+
+	gkdf = NULL;
+	switch (csuite_specifier) {
+	case EAP_GPSK_CIPHER_AES:
+		gkdf = eap_gpsk_gkdf_cmac;
+		mk_len = 16;
+		break;
+#ifdef EAP_GPSK_SHA256
+	case EAP_GPSK_CIPHER_SHA256:
+		gkdf = eap_gpsk_gkdf_sha256;
+		mk_len = SHA256_MAC_LEN;
+		break;
+#endif /* EAP_GPSK_SHA256 */
+	default:
+		return -1;
+	}
+
+	if (psk_len < mk_len)
+		return -1;
+
+	data_len = 2 + psk_len + 6 + seed_len;
+	data = os_malloc(data_len);
+	if (data == NULL)
+		return -1;
+	pos = data;
+	WPA_PUT_BE16(pos, psk_len);
+	pos += 2;
+	os_memcpy(pos, psk, psk_len);
+	pos += psk_len;
+	WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
+	pos += 4;
+	WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
+	pos += 2;
+	os_memcpy(pos, seed, seed_len); /* inputString */
+	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation",
+			data, data_len);
+
+	if (gkdf(psk, data, data_len, mk, mk_len) < 0) {
+		os_free(data);
+		return -1;
+	}
+	os_free(data);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len);
+
+	if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0)
+		return -1;
+
+	pos = kdf_out;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN);
+	os_memcpy(msk, pos, EAP_MSK_LEN);
+	pos += EAP_MSK_LEN;
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN);
+	os_memcpy(emsk, pos, EAP_EMSK_LEN);
+	pos += EAP_EMSK_LEN;
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len);
+	os_memcpy(sk, pos, sk_len);
+	pos += sk_len;
+
+	if (pk) {
+		wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len);
+		os_memcpy(pk, pos, pk_len);
+	}
+
+	return 0;
+}
+
+
+static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len,
+				    const u8 *seed, size_t seed_len,
+				    u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
+				    u8 *pk, size_t *pk_len)
+{
+#define EAP_GPSK_SK_LEN_AES 16
+#define EAP_GPSK_PK_LEN_AES 16
+	u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES +
+		   EAP_GPSK_PK_LEN_AES];
+
+	/*
+	 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
+	 *            (= seed)
+	 * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001
+	 * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString)
+	 * MSK = GKDF-160 (MK, inputString)[0..63]
+	 * EMSK = GKDF-160 (MK, inputString)[64..127]
+	 * SK = GKDF-160 (MK, inputString)[128..143]
+	 * PK = GKDF-160 (MK, inputString)[144..159]
+	 * zero = 0x00 || 0x00 || ... || 0x00 (16 times)
+	 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
+	 *                      CSuite_Sel || inputString)
+	 */
+
+	*sk_len = EAP_GPSK_SK_LEN_AES;
+	*pk_len = EAP_GPSK_PK_LEN_AES;
+
+	return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES,
+					   kdf_out, sizeof(kdf_out),
+					   psk, psk_len, seed, seed_len,
+					   msk, emsk, sk, *sk_len,
+					   pk, *pk_len);
+}
+
+
+#ifdef EAP_GPSK_SHA256
+static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len,
+				       const u8 *seed, size_t seed_len,
+				       u8 *msk, u8 *emsk,
+				       u8 *sk, size_t *sk_len)
+{
+#define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN
+#define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN
+	u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 +
+		   EAP_GPSK_PK_LEN_SHA256];
+
+	/*
+	 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
+	 *            (= seed)
+	 * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002
+	 * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString)
+	 * MSK = GKDF-160 (MK, inputString)[0..63]
+	 * EMSK = GKDF-160 (MK, inputString)[64..127]
+	 * SK = GKDF-160 (MK, inputString)[128..159]
+	 * zero = 0x00 || 0x00 || ... || 0x00 (32 times)
+	 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
+	 *                      CSuite_Sel || inputString)
+	 */
+
+	*sk_len = EAP_GPSK_SK_LEN_SHA256;
+
+	return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256,
+					   kdf_out, sizeof(kdf_out),
+					   psk, psk_len, seed, seed_len,
+					   msk, emsk, sk, *sk_len,
+					   NULL, 0);
+}
+#endif /* EAP_GPSK_SHA256 */
+
+
+/**
+ * eap_gpsk_derive_keys - Derive EAP-GPSK keys
+ * @psk: Pre-shared key
+ * @psk_len: Length of psk in bytes
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * @rand_peer: 32-byte RAND_Peer
+ * @rand_server: 32-byte RAND_Server
+ * @id_peer: ID_Peer
+ * @id_peer_len: Length of ID_Peer
+ * @id_server: ID_Server
+ * @id_server_len: Length of ID_Server
+ * @msk: Buffer for 64-byte MSK
+ * @emsk: Buffer for 64-byte EMSK
+ * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes)
+ * @sk_len: Buffer for returning length of SK
+ * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes)
+ * @pk_len: Buffer for returning length of PK
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
+			 int specifier,
+			 const u8 *rand_peer, const u8 *rand_server,
+			 const u8 *id_peer, size_t id_peer_len,
+			 const u8 *id_server, size_t id_server_len,
+			 u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
+			 u8 *pk, size_t *pk_len)
+{
+	u8 *seed, *pos;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)",
+		   vendor, specifier);
+
+	if (vendor != EAP_GPSK_VENDOR_IETF)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
+
+	/* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */
+	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");
+		return -1;
+	}
+
+	pos = seed;
+	os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
+	pos += EAP_GPSK_RAND_LEN;
+	os_memcpy(pos, id_peer, id_peer_len);
+	pos += id_peer_len;
+	os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
+	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, pos - seed);
+
+	switch (specifier) {
+	case EAP_GPSK_CIPHER_AES:
+		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,
+						  pos - seed,
+						  msk, emsk, sk, sk_len);
+		break;
+#endif /* EAP_GPSK_SHA256 */
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
+			   "key derivation", vendor, specifier);
+		ret = -1;
+		break;
+	}
+
+	os_free(seed);
+
+	return ret;
+}
+
+
+static int eap_gpsk_derive_mid_helper(u32 csuite_specifier,
+				      u8 *kdf_out, size_t kdf_out_len,
+				      const u8 *psk, const u8 *seed,
+				      size_t seed_len, u8 method_type)
+{
+	u8 *pos, *data;
+	size_t data_len;
+	int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
+		    u8 *buf, size_t len);
+
+	gkdf = NULL;
+	switch (csuite_specifier) {
+	case EAP_GPSK_CIPHER_AES:
+		gkdf = eap_gpsk_gkdf_cmac;
+		break;
+#ifdef EAP_GPSK_SHA256
+	case EAP_GPSK_CIPHER_SHA256:
+		gkdf = eap_gpsk_gkdf_sha256;
+		break;
+#endif /* EAP_GPSK_SHA256 */
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d used in "
+			   "Session-Id derivation", csuite_specifier);
+		return -1;
+	}
+
+#define SID_LABEL "Method ID"
+	/* "Method ID" || EAP_Method_Type || CSuite_Sel || inputString */
+	data_len = strlen(SID_LABEL) + 1 + 6 + seed_len;
+	data = os_malloc(data_len);
+	if (data == NULL)
+		return -1;
+	pos = data;
+	os_memcpy(pos, SID_LABEL, strlen(SID_LABEL));
+	pos += strlen(SID_LABEL);
+#undef SID_LABEL
+	os_memcpy(pos, &method_type, 1);
+	pos += 1;
+	WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
+	pos += 4;
+	WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
+	pos += 2;
+	os_memcpy(pos, seed, seed_len); /* inputString */
+	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Data to Method ID derivation",
+		    data, data_len);
+
+	if (gkdf(psk, data, data_len, kdf_out, kdf_out_len) < 0) {
+		os_free(data);
+		return -1;
+	}
+	os_free(data);
+	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Method ID", kdf_out, kdf_out_len);
+
+	return 0;
+}
+
+
+/**
+ * eap_gpsk_session_id - Derive EAP-GPSK Session ID
+ * @psk: Pre-shared key
+ * @psk_len: Length of psk in bytes
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * @rand_peer: 32-byte RAND_Peer
+ * @rand_server: 32-byte RAND_Server
+ * @id_peer: ID_Peer
+ * @id_peer_len: Length of ID_Peer
+ * @id_server: ID_Server
+ * @id_server_len: Length of ID_Server
+ * @method_type: EAP Authentication Method Type
+ * @sid: Buffer for 17-byte Session ID
+ * @sid_len: Buffer for returning length of Session ID
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor,
+			       int specifier,
+			       const u8 *rand_peer, const u8 *rand_server,
+			       const u8 *id_peer, size_t id_peer_len,
+			       const u8 *id_server, size_t id_server_len,
+			       u8 method_type, u8 *sid, size_t *sid_len)
+{
+	u8 *seed, *pos;
+	u8 kdf_out[16];
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)",
+		   vendor, specifier);
+
+	if (vendor != EAP_GPSK_VENDOR_IETF)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
+
+	/*
+	 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
+	 *            (= seed)
+	 * KS = 16, CSuite_Sel = 0x00000000 0x0001
+	 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
+	 *                      CSuite_Sel || inputString)
+	 */
+	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");
+		return -1;
+	}
+
+	pos = seed;
+	os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
+	pos += EAP_GPSK_RAND_LEN;
+	os_memcpy(pos, id_peer, id_peer_len);
+	pos += id_peer_len;
+	os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
+	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, pos - seed);
+
+	ret = eap_gpsk_derive_mid_helper(specifier,
+					 kdf_out, sizeof(kdf_out),
+					 psk, seed, pos - seed,
+					 method_type);
+
+	sid[0] = method_type;
+	os_memcpy(sid + 1, kdf_out, sizeof(kdf_out));
+	*sid_len = 1 + sizeof(kdf_out);
+
+	os_free(seed);
+
+	return ret;
+}
+
+
+/**
+ * eap_gpsk_mic_len - Get the length of the MIC
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * Returns: MIC length in bytes
+ */
+size_t eap_gpsk_mic_len(int vendor, int specifier)
+{
+	if (vendor != EAP_GPSK_VENDOR_IETF)
+		return 0;
+
+	switch (specifier) {
+	case EAP_GPSK_CIPHER_AES:
+		return 16;
+#ifdef EAP_GPSK_SHA256
+	case EAP_GPSK_CIPHER_SHA256:
+		return 32;
+#endif /* EAP_GPSK_SHA256 */
+	default:
+		return 0;
+	}
+}
+
+
+static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len,
+				    const u8 *data, size_t len, u8 *mic)
+{
+	if (sk_len != 16) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for "
+			   "AES-CMAC MIC", (unsigned long) sk_len);
+		return -1;
+	}
+
+	return omac1_aes_128(sk, data, len, mic);
+}
+
+
+/**
+ * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet
+ * @sk: Session key SK from eap_gpsk_derive_keys()
+ * @sk_len: SK length in bytes from eap_gpsk_derive_keys()
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * @data: Input data to MIC
+ * @len: Input data length in bytes
+ * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
+			 int specifier, const u8 *data, size_t len, u8 *mic)
+{
+	int ret;
+
+	if (vendor != EAP_GPSK_VENDOR_IETF)
+		return -1;
+
+	switch (specifier) {
+	case EAP_GPSK_CIPHER_AES:
+		ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic);
+		break;
+#ifdef EAP_GPSK_SHA256
+	case EAP_GPSK_CIPHER_SHA256:
+		hmac_sha256(sk, sk_len, data, len, mic);
+		ret = 0;
+		break;
+#endif /* EAP_GPSK_SHA256 */
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
+			   "MIC computation", vendor, specifier);
+		ret = -1;
+		break;
+	}
+
+	return ret;
+}
diff --git a/hostap/src/eap_common/eap_gpsk_common.h b/hostap/src/eap_common/eap_gpsk_common.h
new file mode 100644
index 0000000..fbcd547
--- /dev/null
+++ b/hostap/src/eap_common/eap_gpsk_common.h
@@ -0,0 +1,66 @@
+/*
+ * EAP server/peer: EAP-GPSK shared routines
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_GPSK_COMMON_H
+#define EAP_GPSK_COMMON_H
+
+#define EAP_GPSK_OPCODE_GPSK_1 1
+#define EAP_GPSK_OPCODE_GPSK_2 2
+#define EAP_GPSK_OPCODE_GPSK_3 3
+#define EAP_GPSK_OPCODE_GPSK_4 4
+#define EAP_GPSK_OPCODE_FAIL 5
+#define EAP_GPSK_OPCODE_PROTECTED_FAIL 6
+
+/* Failure-Code in GPSK-Fail and GPSK-Protected-Fail */
+#define EAP_GPSK_FAIL_PSK_NOT_FOUND 0x00000001
+#define EAP_GPSK_FAIL_AUTHENTICATION_FAILURE 0x00000002
+#define EAP_GPSK_FAIL_AUTHORIZATION_FAILURE 0x00000003
+
+#define EAP_GPSK_RAND_LEN 32
+#define EAP_GPSK_MAX_SK_LEN 32
+#define EAP_GPSK_MAX_PK_LEN 32
+#define EAP_GPSK_MAX_MIC_LEN 32
+
+#define EAP_GPSK_VENDOR_IETF		0x00000000
+#define EAP_GPSK_CIPHER_RESERVED	0x000000
+#define EAP_GPSK_CIPHER_AES		0x000001
+#define EAP_GPSK_CIPHER_SHA256		0x000002
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_gpsk_csuite {
+	u8 vendor[4];
+	u8 specifier[2];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+int eap_gpsk_supported_ciphersuite(int vendor, int specifier);
+int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
+			 int specifier,
+			 const u8 *rand_client, const u8 *rand_server,
+			 const u8 *id_client, size_t id_client_len,
+			 const u8 *id_server, size_t id_server_len,
+			 u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
+			 u8 *pk, size_t *pk_len);
+int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor,
+			       int specifier,
+			       const u8 *rand_peer, const u8 *rand_server,
+			       const u8 *id_peer, size_t id_peer_len,
+			       const u8 *id_server, size_t id_server_len,
+			       u8 method_type, u8 *sid, size_t *sid_len);
+size_t eap_gpsk_mic_len(int vendor, int specifier);
+int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
+			 int specifier, const u8 *data, size_t len, u8 *mic);
+
+#endif /* EAP_GPSK_COMMON_H */
diff --git a/hostap/src/eap_common/eap_ikev2_common.c b/hostap/src/eap_common/eap_ikev2_common.c
new file mode 100644
index 0000000..585c79c
--- /dev/null
+++ b/hostap/src/eap_common/eap_ikev2_common.c
@@ -0,0 +1,116 @@
+/*
+ * EAP-IKEv2 common routines
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_common.h"
+#include "ikev2_common.h"
+#include "eap_ikev2_common.h"
+
+
+int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys,
+			    const u8 *i_nonce, size_t i_nonce_len,
+			    const u8 *r_nonce, size_t r_nonce_len,
+			    u8 *keymat)
+{
+	u8 *nonces;
+	size_t nlen;
+
+	/* KEYMAT = prf+(SK_d, Ni | Nr) */
+	if (keys->SK_d == NULL || i_nonce == NULL || r_nonce == NULL)
+		return -1;
+
+	nlen = i_nonce_len + r_nonce_len;
+	nonces = os_malloc(nlen);
+	if (nonces == NULL)
+		return -1;
+	os_memcpy(nonces, i_nonce, i_nonce_len);
+	os_memcpy(nonces + i_nonce_len, r_nonce, r_nonce_len);
+
+	if (ikev2_prf_plus(prf, keys->SK_d, keys->SK_d_len, nonces, nlen,
+			   keymat, EAP_MSK_LEN + EAP_EMSK_LEN)) {
+		os_free(nonces);
+		return -1;
+	}
+	os_free(nonces);
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-IKEV2: KEYMAT",
+			keymat, EAP_MSK_LEN + EAP_EMSK_LEN);
+
+	return 0;
+}
+
+
+struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code)
+{
+	struct wpabuf *msg;
+
+	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;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Send fragment ack");
+
+	return msg;
+}
+
+
+int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys,
+			   int initiator, const struct wpabuf *msg,
+			   const u8 *pos, const u8 *end)
+{
+	const struct ikev2_integ_alg *integ;
+	size_t icv_len;
+	u8 icv[IKEV2_MAX_HASH_LEN];
+	const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
+
+	integ = ikev2_get_integ(integ_alg);
+	if (integ == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
+			   "transform / cannot validate ICV");
+		return -1;
+	}
+	icv_len = integ->hash_len;
+
+	if (end - pos < (int) icv_len) {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Not enough room in the "
+			   "message for Integrity Checksum Data");
+		return -1;
+	}
+
+	if (SK_a == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No SK_a for ICV validation");
+		return -1;
+	}
+
+	if (ikev2_integ_hash(integ_alg, SK_a, keys->SK_integ_len,
+			     wpabuf_head(msg),
+			     wpabuf_len(msg) - icv_len, icv) < 0) {
+		wpa_printf(MSG_INFO, "EAP-IKEV2: Could not calculate ICV");
+		return -1;
+	}
+
+	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);
+		wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Received ICV",
+			    end - icv_len, icv_len);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Valid Integrity Checksum Data in "
+		   "the received message");
+
+	return icv_len;
+}
diff --git a/hostap/src/eap_common/eap_ikev2_common.h b/hostap/src/eap_common/eap_ikev2_common.h
new file mode 100644
index 0000000..e7502d7
--- /dev/null
+++ b/hostap/src/eap_common/eap_ikev2_common.h
@@ -0,0 +1,29 @@
+/*
+ * EAP-IKEv2 definitions
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_IKEV2_COMMON_H
+#define EAP_IKEV2_COMMON_H
+
+#define IKEV2_FLAGS_LENGTH_INCLUDED 0x80
+#define IKEV2_FLAGS_MORE_FRAGMENTS 0x40
+#define IKEV2_FLAGS_ICV_INCLUDED 0x20
+
+#define IKEV2_FRAGMENT_SIZE 1400
+
+struct ikev2_keys;
+
+int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys,
+			    const u8 *i_nonce, size_t i_nonce_len,
+			    const u8 *r_nonce, size_t r_nonce_len,
+			    u8 *keymat);
+struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code);
+int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys,
+			   int initiator, const struct wpabuf *msg,
+			   const u8 *pos, const u8 *end);
+
+#endif /* EAP_IKEV2_COMMON_H */
diff --git a/hostap/src/eap_common/eap_pax_common.c b/hostap/src/eap_common/eap_pax_common.c
new file mode 100644
index 0000000..0e80ef5
--- /dev/null
+++ b/hostap/src/eap_common/eap_pax_common.c
@@ -0,0 +1,148 @@
+/*
+ * EAP server/peer: EAP-PAX shared routines
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "eap_pax_common.h"
+
+
+/**
+ * eap_pax_kdf - PAX Key Derivation Function
+ * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported
+ * @key: Secret key (X)
+ * @key_len: Length of the secret key in bytes
+ * @identifier: Public identifier for the key (Y)
+ * @entropy: Exchanged entropy to seed the KDF (Z)
+ * @entropy_len: Length of the entropy in bytes
+ * @output_len: Output len in bytes (W)
+ * @output: Buffer for the derived key
+ * Returns: 0 on success, -1 failed
+ *
+ * RFC 4746, Section 2.6: PAX-KDF-W(X, Y, Z)
+ */
+int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len,
+		const char *identifier,
+		const u8 *entropy, size_t entropy_len,
+		size_t output_len, u8 *output)
+{
+	u8 mac[SHA1_MAC_LEN];
+	u8 counter, *pos;
+	const u8 *addr[3];
+	size_t len[3];
+	size_t num_blocks, left;
+
+	num_blocks = (output_len + EAP_PAX_MAC_LEN - 1) / EAP_PAX_MAC_LEN;
+	if (identifier == NULL || num_blocks >= 255)
+		return -1;
+
+	/* TODO: add support for EAP_PAX_HMAC_SHA256_128 */
+	if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128)
+		return -1;
+
+	addr[0] = (const u8 *) identifier;
+	len[0] = os_strlen(identifier);
+	addr[1] = entropy;
+	len[1] = entropy_len;
+	addr[2] = &counter;
+	len[2] = 1;
+
+	pos = output;
+	left = output_len;
+	for (counter = 1; counter <= (u8) num_blocks; counter++) {
+		size_t clen = left > EAP_PAX_MAC_LEN ? EAP_PAX_MAC_LEN : left;
+		hmac_sha1_vector(key, key_len, 3, addr, len, mac);
+		os_memcpy(pos, mac, clen);
+		pos += clen;
+		left -= clen;
+	}
+
+	return 0;
+}
+
+
+/**
+ * eap_pax_mac - EAP-PAX MAC
+ * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported
+ * @key: Secret key
+ * @key_len: Length of the secret key in bytes
+ * @data1: Optional data, first block; %NULL if not used
+ * @data1_len: Length of data1 in bytes
+ * @data2: Optional data, second block; %NULL if not used
+ * @data2_len: Length of data2 in bytes
+ * @data3: Optional data, third block; %NULL if not used
+ * @data3_len: Length of data3 in bytes
+ * @mac: Buffer for the MAC value (EAP_PAX_MAC_LEN = 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Wrapper function to calculate EAP-PAX MAC.
+ */
+int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len,
+		const u8 *data1, size_t data1_len,
+		const u8 *data2, size_t data2_len,
+		const u8 *data3, size_t data3_len,
+		u8 *mac)
+{
+	u8 hash[SHA1_MAC_LEN];
+	const u8 *addr[3];
+	size_t len[3];
+	size_t count;
+
+	/* TODO: add support for EAP_PAX_HMAC_SHA256_128 */
+	if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128)
+		return -1;
+
+	addr[0] = data1;
+	len[0] = data1_len;
+	addr[1] = data2;
+	len[1] = data2_len;
+	addr[2] = data3;
+	len[2] = data3_len;
+
+	count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0);
+	hmac_sha1_vector(key, key_len, count, addr, len, hash);
+	os_memcpy(mac, hash, EAP_PAX_MAC_LEN);
+
+	return 0;
+}
+
+
+/**
+ * eap_pax_initial_key_derivation - EAP-PAX initial key derivation
+ * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported
+ * @ak: Authentication Key
+ * @e: Entropy
+ * @mk: Buffer for the derived Master Key
+ * @ck: Buffer for the derived Confirmation Key
+ * @ick: Buffer for the derived Integrity Check Key
+ * @mid: Buffer for the derived Method ID
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e,
+				   u8 *mk, u8 *ck, u8 *ick, u8 *mid)
+{
+	wpa_printf(MSG_DEBUG, "EAP-PAX: initial key derivation");
+	if (eap_pax_kdf(mac_id, ak, EAP_PAX_AK_LEN, "Master Key",
+			e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MK_LEN, mk) ||
+	    eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Confirmation Key",
+			e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_CK_LEN, ck) ||
+	    eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Integrity Check Key",
+			e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick) ||
+	    eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Method ID",
+			e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MID_LEN, mid))
+		return -1;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: AK", ak, EAP_PAX_AK_LEN);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MK", mk, EAP_PAX_MK_LEN);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: CK", ck, EAP_PAX_CK_LEN);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: ICK", ick, EAP_PAX_ICK_LEN);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MID", mid, EAP_PAX_MID_LEN);
+
+	return 0;
+}
diff --git a/hostap/src/eap_common/eap_pax_common.h b/hostap/src/eap_common/eap_pax_common.h
new file mode 100644
index 0000000..e6cdf4d
--- /dev/null
+++ b/hostap/src/eap_common/eap_pax_common.h
@@ -0,0 +1,92 @@
+/*
+ * EAP server/peer: EAP-PAX shared routines
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_PAX_COMMON_H
+#define EAP_PAX_COMMON_H
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_pax_hdr {
+	u8 op_code;
+	u8 flags;
+	u8 mac_id;
+	u8 dh_group_id;
+	u8 public_key_id;
+	/* Followed by variable length payload and ICV */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+/* op_code: */
+enum {
+	EAP_PAX_OP_STD_1 = 0x01,
+	EAP_PAX_OP_STD_2 = 0x02,
+	EAP_PAX_OP_STD_3 = 0x03,
+	EAP_PAX_OP_SEC_1 = 0x11,
+	EAP_PAX_OP_SEC_2 = 0x12,
+	EAP_PAX_OP_SEC_3 = 0x13,
+	EAP_PAX_OP_SEC_4 = 0x14,
+	EAP_PAX_OP_SEC_5 = 0x15,
+	EAP_PAX_OP_ACK = 0x21
+};
+
+/* flags: */
+#define EAP_PAX_FLAGS_MF			0x01
+#define EAP_PAX_FLAGS_CE			0x02
+#define EAP_PAX_FLAGS_AI			0x04
+
+/* mac_id: */
+#define EAP_PAX_MAC_HMAC_SHA1_128		0x01
+#define EAP_PAX_HMAC_SHA256_128			0x02
+
+/* dh_group_id: */
+#define EAP_PAX_DH_GROUP_NONE			0x00
+#define EAP_PAX_DH_GROUP_2048_MODP		0x01
+#define EAP_PAX_DH_GROUP_3072_MODP		0x02
+#define EAP_PAX_DH_GROUP_NIST_ECC_P_256		0x03
+
+/* public_key_id: */
+#define EAP_PAX_PUBLIC_KEY_NONE			0x00
+#define EAP_PAX_PUBLIC_KEY_RSAES_OAEP		0x01
+#define EAP_PAX_PUBLIC_KEY_RSA_PKCS1_V1_5	0x02
+#define EAP_PAX_PUBLIC_KEY_EL_GAMAL_NIST_ECC	0x03
+
+/* ADE type: */
+#define EAP_PAX_ADE_VENDOR_SPECIFIC		0x01
+#define EAP_PAX_ADE_CLIENT_CHANNEL_BINDING	0x02
+#define EAP_PAX_ADE_SERVER_CHANNEL_BINDING	0x03
+
+
+#define EAP_PAX_RAND_LEN 32
+#define EAP_PAX_MAC_LEN 16
+#define EAP_PAX_ICV_LEN 16
+#define EAP_PAX_AK_LEN 16
+#define EAP_PAX_MK_LEN 16
+#define EAP_PAX_CK_LEN 16
+#define EAP_PAX_ICK_LEN 16
+#define EAP_PAX_MID_LEN 16
+
+
+int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len,
+		const char *identifier,
+		const u8 *entropy, size_t entropy_len,
+		size_t output_len, u8 *output);
+int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len,
+		const u8 *data1, size_t data1_len,
+		const u8 *data2, size_t data2_len,
+		const u8 *data3, size_t data3_len,
+		u8 *mac);
+int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e,
+				   u8 *mk, u8 *ck, u8 *ick, u8 *mid);
+
+#endif /* EAP_PAX_COMMON_H */
diff --git a/hostap/src/eap_common/eap_peap_common.c b/hostap/src/eap_common/eap_peap_common.c
new file mode 100644
index 0000000..68b8878
--- /dev/null
+++ b/hostap/src/eap_common/eap_peap_common.c
@@ -0,0 +1,85 @@
+/*
+ * EAP-PEAP common routines
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "eap_peap_common.h"
+
+int peap_prfplus(int version, const u8 *key, size_t key_len,
+		 const char *label, const u8 *seed, size_t seed_len,
+		 u8 *buf, size_t buf_len)
+{
+	unsigned char counter = 0;
+	size_t pos, plen;
+	u8 hash[SHA1_MAC_LEN];
+	size_t label_len = os_strlen(label);
+	u8 extra[2];
+	const unsigned char *addr[5];
+	size_t len[5];
+
+	addr[0] = hash;
+	len[0] = 0;
+	addr[1] = (unsigned char *) label;
+	len[1] = label_len;
+	addr[2] = seed;
+	len[2] = seed_len;
+
+	if (version == 0) {
+		/*
+		 * PRF+(K, S, LEN) = T1 | T2 | ... | Tn
+		 * T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00)
+		 * T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00)
+		 * ...
+		 * Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00)
+		 */
+
+		extra[0] = 0;
+		extra[1] = 0;
+
+		addr[3] = &counter;
+		len[3] = 1;
+		addr[4] = extra;
+		len[4] = 2;
+	} else {
+		/*
+		 * PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where:
+		 * T1 = HMAC-SHA1(K, S | LEN | 0x01)
+		 * T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02)
+		 * T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03)
+		 * T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04)
+		 *   ...
+		 */
+
+		extra[0] = buf_len & 0xff;
+
+		addr[3] = extra;
+		len[3] = 1;
+		addr[4] = &counter;
+		len[4] = 1;
+	}
+
+	pos = 0;
+	while (pos < buf_len) {
+		counter++;
+		plen = buf_len - pos;
+		if (hmac_sha1_vector(key, key_len, 5, addr, len, hash) < 0)
+			return -1;
+		if (plen >= SHA1_MAC_LEN) {
+			os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
+			pos += SHA1_MAC_LEN;
+		} else {
+			os_memcpy(&buf[pos], hash, plen);
+			break;
+		}
+		len[0] = SHA1_MAC_LEN;
+	}
+
+	return 0;
+}
diff --git a/hostap/src/eap_common/eap_peap_common.h b/hostap/src/eap_common/eap_peap_common.h
new file mode 100644
index 0000000..7aad0df
--- /dev/null
+++ b/hostap/src/eap_common/eap_peap_common.h
@@ -0,0 +1,16 @@
+/*
+ * EAP-PEAP common routines
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_PEAP_COMMON_H
+#define EAP_PEAP_COMMON_H
+
+int peap_prfplus(int version, const u8 *key, size_t key_len,
+		 const char *label, const u8 *seed, size_t seed_len,
+		 u8 *buf, size_t buf_len);
+
+#endif /* EAP_PEAP_COMMON_H */
diff --git a/hostap/src/eap_common/eap_psk_common.c b/hostap/src/eap_common/eap_psk_common.c
new file mode 100644
index 0000000..638102f
--- /dev/null
+++ b/hostap/src/eap_common/eap_psk_common.c
@@ -0,0 +1,68 @@
+/*
+ * EAP server/peer: EAP-PSK shared routines
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "eap_defs.h"
+#include "eap_psk_common.h"
+
+#define aes_block_size 16
+
+
+int eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk)
+{
+	os_memset(ak, 0, aes_block_size);
+	if (aes_128_encrypt_block(psk, ak, ak))
+		return -1;
+	os_memcpy(kdk, ak, aes_block_size);
+	ak[aes_block_size - 1] ^= 0x01;
+	kdk[aes_block_size - 1] ^= 0x02;
+	if (aes_128_encrypt_block(psk, ak, ak) ||
+	    aes_128_encrypt_block(psk, kdk, kdk))
+		return -1;
+	return 0;
+}
+
+
+int eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk,
+			u8 *emsk)
+{
+	u8 hash[aes_block_size];
+	u8 counter = 1;
+	int i;
+
+	if (aes_128_encrypt_block(kdk, rand_p, hash))
+		return -1;
+
+	hash[aes_block_size - 1] ^= counter;
+	if (aes_128_encrypt_block(kdk, hash, tek))
+		return -1;
+	hash[aes_block_size - 1] ^= counter;
+	counter++;
+
+	for (i = 0; i < EAP_MSK_LEN / aes_block_size; i++) {
+		hash[aes_block_size - 1] ^= counter;
+		if (aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size]))
+			return -1;
+		hash[aes_block_size - 1] ^= counter;
+		counter++;
+	}
+
+	for (i = 0; i < EAP_EMSK_LEN / aes_block_size; i++) {
+		hash[aes_block_size - 1] ^= counter;
+		if (aes_128_encrypt_block(kdk, hash,
+					  &emsk[i * aes_block_size]))
+			return -1;
+		hash[aes_block_size - 1] ^= counter;
+		counter++;
+	}
+
+	return 0;
+}
diff --git a/hostap/src/eap_common/eap_psk_common.h b/hostap/src/eap_common/eap_psk_common.h
new file mode 100644
index 0000000..8bc2c3c
--- /dev/null
+++ b/hostap/src/eap_common/eap_psk_common.h
@@ -0,0 +1,72 @@
+/*
+ * EAP server/peer: EAP-PSK shared routines
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_PSK_COMMON_H
+#define EAP_PSK_COMMON_H
+
+
+#define EAP_PSK_RAND_LEN 16
+#define EAP_PSK_MAC_LEN 16
+#define EAP_PSK_TEK_LEN 16
+#define EAP_PSK_PSK_LEN 16
+#define EAP_PSK_AK_LEN 16
+#define EAP_PSK_KDK_LEN 16
+
+#define EAP_PSK_R_FLAG_CONT 1
+#define EAP_PSK_R_FLAG_DONE_SUCCESS 2
+#define EAP_PSK_R_FLAG_DONE_FAILURE 3
+#define EAP_PSK_E_FLAG 0x20
+
+#define EAP_PSK_FLAGS_GET_T(flags) (((flags) & 0xc0) >> 6)
+#define EAP_PSK_FLAGS_SET_T(t) ((u8) (t) << 6)
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+/* EAP-PSK First Message (AS -> Supplicant) */
+struct eap_psk_hdr_1 {
+	u8 flags;
+	u8 rand_s[EAP_PSK_RAND_LEN];
+	/* Followed by variable length ID_S */
+} STRUCT_PACKED;
+
+/* EAP-PSK Second Message (Supplicant -> AS) */
+struct eap_psk_hdr_2 {
+	u8 flags;
+	u8 rand_s[EAP_PSK_RAND_LEN];
+	u8 rand_p[EAP_PSK_RAND_LEN];
+	u8 mac_p[EAP_PSK_MAC_LEN];
+	/* Followed by variable length ID_P */
+} STRUCT_PACKED;
+
+/* EAP-PSK Third Message (AS -> Supplicant) */
+struct eap_psk_hdr_3 {
+	u8 flags;
+	u8 rand_s[EAP_PSK_RAND_LEN];
+	u8 mac_s[EAP_PSK_MAC_LEN];
+	/* Followed by variable length PCHANNEL */
+} STRUCT_PACKED;
+
+/* EAP-PSK Fourth Message (Supplicant -> AS) */
+struct eap_psk_hdr_4 {
+	u8 flags;
+	u8 rand_s[EAP_PSK_RAND_LEN];
+	/* Followed by variable length PCHANNEL */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+int __must_check eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk);
+int __must_check eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek,
+				     u8 *msk, u8 *emsk);
+
+#endif /* EAP_PSK_COMMON_H */
diff --git a/hostap/src/eap_common/eap_pwd_common.c b/hostap/src/eap_common/eap_pwd_common.c
new file mode 100644
index 0000000..4d27623
--- /dev/null
+++ b/hostap/src/eap_common/eap_pwd_common.c
@@ -0,0 +1,347 @@
+/*
+ * EAP server/peer: EAP-pwd shared routines
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * 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/sha256.h"
+#include "crypto/crypto.h"
+#include "eap_defs.h"
+#include "eap_pwd_common.h"
+
+/* The random function H(x) = HMAC-SHA256(0^32, x) */
+struct crypto_hash * eap_pwd_h_init(void)
+{
+	u8 allzero[SHA256_MAC_LEN];
+	os_memset(allzero, 0, SHA256_MAC_LEN);
+	return crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, allzero,
+				SHA256_MAC_LEN);
+}
+
+
+void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len)
+{
+	crypto_hash_update(hash, data, len);
+}
+
+
+void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest)
+{
+	size_t len = SHA256_MAC_LEN;
+	crypto_hash_finish(hash, digest, &len);
+}
+
+
+/* a counter-based KDF based on NIST SP800-108 */
+static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label,
+		       size_t labellen, u8 *result, size_t resultbitlen)
+{
+	struct crypto_hash *hash;
+	u8 digest[SHA256_MAC_LEN];
+	u16 i, ctr, L;
+	size_t resultbytelen, len = 0, mdlen;
+
+	resultbytelen = (resultbitlen + 7) / 8;
+	ctr = 0;
+	L = htons(resultbitlen);
+	while (len < resultbytelen) {
+		ctr++;
+		i = htons(ctr);
+		hash = crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256,
+					key, keylen);
+		if (hash == NULL)
+			return -1;
+		if (ctr > 1)
+			crypto_hash_update(hash, digest, SHA256_MAC_LEN);
+		crypto_hash_update(hash, (u8 *) &i, sizeof(u16));
+		crypto_hash_update(hash, label, labellen);
+		crypto_hash_update(hash, (u8 *) &L, sizeof(u16));
+		mdlen = SHA256_MAC_LEN;
+		if (crypto_hash_finish(hash, digest, &mdlen) < 0)
+			return -1;
+		if ((len + mdlen) > resultbytelen)
+			os_memcpy(result + len, digest, resultbytelen - len);
+		else
+			os_memcpy(result + len, digest, mdlen);
+		len += mdlen;
+	}
+
+	/* since we're expanding to a bit length, mask off the excess */
+	if (resultbitlen % 8) {
+		u8 mask = 0xff;
+		mask <<= (8 - (resultbitlen % 8));
+		result[resultbytelen - 1] &= mask;
+	}
+
+	return 0;
+}
+
+
+/*
+ * compute a "random" secret point on an elliptic curve based
+ * on the password and identities.
+ */
+int compute_password_element(EAP_PWD_group *grp, u16 num,
+			     const u8 *password, size_t password_len,
+			     const u8 *id_server, size_t id_server_len,
+			     const u8 *id_peer, size_t id_peer_len,
+			     const u8 *token)
+{
+	BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
+	struct crypto_hash *hash;
+	unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
+	int nid, is_odd, ret = 0;
+	size_t primebytelen, primebitlen;
+
+	switch (num) { /* from IANA registry for IKE D-H groups */
+        case 19:
+		nid = NID_X9_62_prime256v1;
+		break;
+        case 20:
+		nid = NID_secp384r1;
+		break;
+        case 21:
+		nid = NID_secp521r1;
+		break;
+#ifndef OPENSSL_IS_BORINGSSL
+        case 25:
+		nid = NID_X9_62_prime192v1;
+		break;
+#endif /* OPENSSL_IS_BORINGSSL */
+        case 26:
+		nid = NID_secp224r1;
+		break;
+        default:
+		wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num);
+		return -1;
+	}
+
+	grp->pwe = NULL;
+	grp->order = NULL;
+	grp->prime = NULL;
+
+	if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP");
+		goto fail;
+	}
+
+	if (((rnd = BN_new()) == NULL) ||
+	    ((cofactor = BN_new()) == NULL) ||
+	    ((grp->pwe = EC_POINT_new(grp->group)) == NULL) ||
+	    ((grp->order = BN_new()) == NULL) ||
+	    ((grp->prime = BN_new()) == NULL) ||
+	    ((x_candidate = BN_new()) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
+		goto fail;
+	}
+
+	if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL))
+	{
+		wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp "
+			   "curve");
+		goto fail;
+	}
+	if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) {
+		wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve");
+		goto fail;
+	}
+	if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) {
+		wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for "
+			   "curve");
+		goto fail;
+	}
+	primebitlen = BN_num_bits(grp->prime);
+	primebytelen = BN_num_bytes(grp->prime);
+	if ((prfbuf = os_malloc(primebytelen)) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
+			   "buffer");
+		goto fail;
+	}
+	os_memset(prfbuf, 0, primebytelen);
+	ctr = 0;
+	while (1) {
+		if (ctr > 30) {
+			wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
+				   "point on curve for group %d, something's "
+				   "fishy", num);
+			goto fail;
+		}
+		ctr++;
+
+		/*
+		 * compute counter-mode password value and stretch to prime
+		 *    pwd-seed = H(token | peer-id | server-id | password |
+		 *		   counter)
+		 */
+		hash = eap_pwd_h_init();
+		if (hash == NULL)
+			goto fail;
+		eap_pwd_h_update(hash, token, sizeof(u32));
+		eap_pwd_h_update(hash, id_peer, id_peer_len);
+		eap_pwd_h_update(hash, id_server, id_server_len);
+		eap_pwd_h_update(hash, password, password_len);
+		eap_pwd_h_update(hash, &ctr, sizeof(ctr));
+		eap_pwd_h_final(hash, pwe_digest);
+
+		BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd);
+
+		if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
+				(u8 *) "EAP-pwd Hunting And Pecking",
+				os_strlen("EAP-pwd Hunting And Pecking"),
+				prfbuf, primebitlen) < 0)
+			goto fail;
+
+		BN_bin2bn(prfbuf, primebytelen, x_candidate);
+
+		/*
+		 * eap_pwd_kdf() returns a string of bits 0..primebitlen but
+		 * BN_bin2bn will treat that string of bits as a big endian
+		 * number. If the primebitlen is not an even multiple of 8
+		 * then excessive bits-- those _after_ primebitlen-- so now
+		 * we have to shift right the amount we masked off.
+		 */
+		if (primebitlen % 8)
+			BN_rshift(x_candidate, x_candidate,
+				  (8 - (primebitlen % 8)));
+
+		if (BN_ucmp(x_candidate, grp->prime) >= 0)
+			continue;
+
+		wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
+			    prfbuf, primebytelen);
+
+		/*
+		 * need to unambiguously identify the solution, if there is
+		 * one...
+		 */
+		if (BN_is_odd(rnd))
+			is_odd = 1;
+		else
+			is_odd = 0;
+
+		/*
+		 * solve the quadratic equation, if it's not solvable then we
+		 * don't have a point
+		 */
+		if (!EC_POINT_set_compressed_coordinates_GFp(grp->group,
+							     grp->pwe,
+							     x_candidate,
+							     is_odd, NULL))
+			continue;
+		/*
+		 * If there's a solution to the equation then the point must be
+		 * on the curve so why check again explicitly? OpenSSL code
+		 * says this is required by X9.62. We're not X9.62 but it can't
+		 * hurt just to be sure.
+		 */
+		if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) {
+			wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
+			continue;
+		}
+
+		if (BN_cmp(cofactor, BN_value_one())) {
+			/* make sure the point is not in a small sub-group */
+			if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe,
+					  cofactor, NULL)) {
+				wpa_printf(MSG_INFO, "EAP-pwd: cannot "
+					   "multiply generator by order");
+				continue;
+			}
+			if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) {
+				wpa_printf(MSG_INFO, "EAP-pwd: point is at "
+					   "infinity");
+				continue;
+			}
+		}
+		/* if we got here then we have a new generator. */
+		break;
+	}
+	wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr);
+	grp->group_num = num;
+	if (0) {
+ fail:
+		EC_GROUP_free(grp->group);
+		grp->group = NULL;
+		EC_POINT_clear_free(grp->pwe);
+		grp->pwe = NULL;
+		BN_clear_free(grp->order);
+		grp->order = NULL;
+		BN_clear_free(grp->prime);
+		grp->prime = NULL;
+		ret = 1;
+	}
+	/* cleanliness and order.... */
+	BN_clear_free(cofactor);
+	BN_clear_free(x_candidate);
+	BN_clear_free(rnd);
+	os_free(prfbuf);
+
+	return ret;
+}
+
+
+int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
+		 const BIGNUM *peer_scalar, const BIGNUM *server_scalar,
+		 const u8 *confirm_peer, const u8 *confirm_server,
+		 const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id)
+{
+	struct crypto_hash *hash;
+	u8 mk[SHA256_MAC_LEN], *cruft;
+	u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
+	int offset;
+
+	if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL)
+		return -1;
+
+	/*
+	 * first compute the session-id = TypeCode | H(ciphersuite | scal_p |
+	 *	scal_s)
+	 */
+	session_id[0] = EAP_TYPE_PWD;
+	hash = eap_pwd_h_init();
+	if (hash == NULL) {
+		os_free(cruft);
+		return -1;
+	}
+	eap_pwd_h_update(hash, (const u8 *) ciphersuite, sizeof(u32));
+	offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar);
+	os_memset(cruft, 0, BN_num_bytes(grp->prime));
+	BN_bn2bin(peer_scalar, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
+	offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar);
+	os_memset(cruft, 0, BN_num_bytes(grp->prime));
+	BN_bn2bin(server_scalar, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
+	eap_pwd_h_final(hash, &session_id[1]);
+
+	/* then compute MK = H(k | confirm-peer | confirm-server) */
+	hash = eap_pwd_h_init();
+	if (hash == NULL) {
+		os_free(cruft);
+		return -1;
+	}
+	offset = BN_num_bytes(grp->prime) - BN_num_bytes(k);
+	os_memset(cruft, 0, BN_num_bytes(grp->prime));
+	BN_bn2bin(k, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime));
+	os_free(cruft);
+	eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN);
+	eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN);
+	eap_pwd_h_final(hash, mk);
+
+	/* stretch the mk with the session-id to get MSK | EMSK */
+	if (eap_pwd_kdf(mk, SHA256_MAC_LEN,
+			session_id, SHA256_MAC_LEN + 1,
+			msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8) < 0) {
+		return -1;
+	}
+
+	os_memcpy(msk, msk_emsk, EAP_MSK_LEN);
+	os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN);
+
+	return 1;
+}
diff --git a/hostap/src/eap_common/eap_pwd_common.h b/hostap/src/eap_common/eap_pwd_common.h
new file mode 100644
index 0000000..a0d717e
--- /dev/null
+++ b/hostap/src/eap_common/eap_pwd_common.h
@@ -0,0 +1,72 @@
+/*
+ * EAP server/peer: EAP-pwd shared definitions
+ * Copyright (c) 2009, Dan Harkins <dharkins@lounge.org>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_PWD_COMMON_H
+#define EAP_PWD_COMMON_H
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+
+/*
+ * definition of a finite cyclic group
+ * TODO: support one based on a prime field
+ */
+typedef struct group_definition_ {
+	u16 group_num;
+	EC_GROUP *group;
+	EC_POINT *pwe;
+	BIGNUM *order;
+	BIGNUM *prime;
+} EAP_PWD_group;
+
+/*
+ * EAP-pwd header, included on all payloads
+ * L(1 bit) | M(1 bit) | exch(6 bits) | total_length(if L is set)
+ */
+#define EAP_PWD_HDR_SIZE                1
+
+#define EAP_PWD_OPCODE_ID_EXCH          1
+#define EAP_PWD_OPCODE_COMMIT_EXCH      2
+#define EAP_PWD_OPCODE_CONFIRM_EXCH     3
+#define EAP_PWD_GET_LENGTH_BIT(x)       ((x) & 0x80)
+#define EAP_PWD_SET_LENGTH_BIT(x)       ((x) |= 0x80)
+#define EAP_PWD_GET_MORE_BIT(x)         ((x) & 0x40)
+#define EAP_PWD_SET_MORE_BIT(x)         ((x) |= 0x40)
+#define EAP_PWD_GET_EXCHANGE(x)         ((x) & 0x3f)
+#define EAP_PWD_SET_EXCHANGE(x,y)       ((x) |= (y))
+
+/* EAP-pwd-ID payload */
+struct eap_pwd_id {
+	be16 group_num;
+	u8 random_function;
+#define EAP_PWD_DEFAULT_RAND_FUNC       1
+	u8 prf;
+#define EAP_PWD_DEFAULT_PRF             1
+	u8 token[4];
+	u8 prep;
+#define EAP_PWD_PREP_NONE               0
+#define EAP_PWD_PREP_MS                 1
+	u8 identity[0];     /* length inferred from payload */
+} STRUCT_PACKED;
+
+/* common routines */
+int compute_password_element(EAP_PWD_group *grp, u16 num,
+			     const u8 *password, size_t password_len,
+			     const u8 *id_server, size_t id_server_len,
+			     const u8 *id_peer, size_t id_peer_len,
+			     const u8 *token);
+int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
+		 const BIGNUM *peer_scalar, const BIGNUM *server_scalar,
+		 const u8 *confirm_peer, const u8 *confirm_server,
+		 const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id);
+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);
+
+#endif  /* EAP_PWD_COMMON_H */
diff --git a/hostap/src/eap_common/eap_sake_common.c b/hostap/src/eap_common/eap_sake_common.c
new file mode 100644
index 0000000..c22e43e
--- /dev/null
+++ b/hostap/src/eap_common/eap_sake_common.c
@@ -0,0 +1,387 @@
+/*
+ * EAP server/peer: EAP-SAKE shared routines
+ * Copyright (c) 2006-2007, 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 "wpabuf.h"
+#include "crypto/sha1.h"
+#include "eap_defs.h"
+#include "eap_sake_common.h"
+
+
+static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr,
+				   u8 attr_id, u8 len, const u8 *data)
+{
+	size_t i;
+
+	switch (attr_id) {
+	case EAP_SAKE_AT_RAND_S:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_S");
+		if (len != EAP_SAKE_RAND_LEN) {
+			wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_S with "
+				   "invalid payload length %d", len);
+			return -1;
+		}
+		attr->rand_s = data;
+		break;
+	case EAP_SAKE_AT_RAND_P:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_P");
+		if (len != EAP_SAKE_RAND_LEN) {
+			wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_P with "
+				   "invalid payload length %d", len);
+			return -1;
+		}
+		attr->rand_p = data;
+		break;
+	case EAP_SAKE_AT_MIC_S:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_S");
+		if (len != EAP_SAKE_MIC_LEN) {
+			wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_S with "
+				   "invalid payload length %d", len);
+			return -1;
+		}
+		attr->mic_s = data;
+		break;
+	case EAP_SAKE_AT_MIC_P:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_P");
+		if (len != EAP_SAKE_MIC_LEN) {
+			wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_P with "
+				   "invalid payload length %d", len);
+			return -1;
+		}
+		attr->mic_p = data;
+		break;
+	case EAP_SAKE_AT_SERVERID:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SERVERID");
+		attr->serverid = data;
+		attr->serverid_len = len;
+		break;
+	case EAP_SAKE_AT_PEERID:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PEERID");
+		attr->peerid = data;
+		attr->peerid_len = len;
+		break;
+	case EAP_SAKE_AT_SPI_S:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_S");
+		attr->spi_s = data;
+		attr->spi_s_len = len;
+		break;
+	case EAP_SAKE_AT_SPI_P:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_P");
+		attr->spi_p = data;
+		attr->spi_p_len = len;
+		break;
+	case EAP_SAKE_AT_ANY_ID_REQ:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ANY_ID_REQ");
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid AT_ANY_ID_REQ"
+				   " payload length %d", len);
+			return -1;
+		}
+		attr->any_id_req = data;
+		break;
+	case EAP_SAKE_AT_PERM_ID_REQ:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PERM_ID_REQ");
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
+				   "AT_PERM_ID_REQ payload length %d", len);
+			return -1;
+		}
+		attr->perm_id_req = data;
+		break;
+	case EAP_SAKE_AT_ENCR_DATA:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ENCR_DATA");
+		attr->encr_data = data;
+		attr->encr_data_len = len;
+		break;
+	case EAP_SAKE_AT_IV:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
+		attr->iv = data;
+		attr->iv_len = len;
+		break;
+	case EAP_SAKE_AT_PADDING:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PADDING");
+		for (i = 0; i < len; i++) {
+			if (data[i]) {
+				wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_PADDING "
+					   "with non-zero pad byte");
+				return -1;
+			}
+		}
+		break;
+	case EAP_SAKE_AT_NEXT_TMPID:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_NEXT_TMPID");
+		attr->next_tmpid = data;
+		attr->next_tmpid_len = len;
+		break;
+	case EAP_SAKE_AT_MSK_LIFE:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
+		if (len != 4) {
+			wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
+				   "AT_MSK_LIFE payload length %d", len);
+			return -1;
+		}
+		attr->msk_life = data;
+		break;
+	default:
+		if (attr_id < 128) {
+			wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown non-skippable"
+				   " attribute %d", attr_id);
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring unknown skippable "
+			   "attribute %d", attr_id);
+		break;
+	}
+
+	if (attr->iv && !attr->encr_data) {
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_IV included without "
+			   "AT_ENCR_DATA");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * eap_sake_parse_attributes - Parse EAP-SAKE attributes
+ * @buf: Packet payload (starting with the first attribute)
+ * @len: Payload length
+ * @attr: Structure to be filled with found attributes
+ * Returns: 0 on success or -1 on failure
+ */
+int eap_sake_parse_attributes(const u8 *buf, size_t len,
+			      struct eap_sake_parse_attr *attr)
+{
+	const u8 *pos = buf, *end = buf + len;
+
+	os_memset(attr, 0, sizeof(*attr));
+	while (pos < end) {
+		if (end - pos < 2) {
+			wpa_printf(MSG_DEBUG, "EAP-SAKE: Too short attribute");
+			return -1;
+		}
+
+		if (pos[1] < 2) {
+			wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid attribute "
+				   "length (%d)", pos[1]);
+			return -1;
+		}
+
+		if (pos + pos[1] > end) {
+			wpa_printf(MSG_DEBUG, "EAP-SAKE: Attribute underflow");
+			return -1;
+		}
+
+		if (eap_sake_parse_add_attr(attr, pos[0], pos[1] - 2, pos + 2))
+			return -1;
+
+		pos += pos[1];
+	}
+
+	return 0;
+}
+
+
+/**
+ * eap_sake_kdf - EAP-SAKE Key Derivation Function (KDF)
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF
+ * @data: Extra data (start) to bind into the key
+ * @data_len: Length of the data
+ * @data2: Extra data (end) to bind into the key
+ * @data2_len: Length of the data2
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key (e.g., SMS). This is identical to the PRF used in IEEE 802.11i.
+ */
+static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label,
+			 const u8 *data, size_t data_len,
+			 const u8 *data2, size_t data2_len,
+			 u8 *buf, size_t buf_len)
+{
+	u8 counter = 0;
+	size_t pos, plen;
+	u8 hash[SHA1_MAC_LEN];
+	size_t label_len = os_strlen(label) + 1;
+	const unsigned char *addr[4];
+	size_t len[4];
+
+	addr[0] = (u8 *) label; /* Label | Y */
+	len[0] = label_len;
+	addr[1] = data; /* Msg[start] */
+	len[1] = data_len;
+	addr[2] = data2; /* Msg[end] */
+	len[2] = data2_len;
+	addr[3] = &counter; /* Length */
+	len[3] = 1;
+
+	pos = 0;
+	while (pos < buf_len) {
+		plen = buf_len - pos;
+		if (plen >= SHA1_MAC_LEN) {
+			hmac_sha1_vector(key, key_len, 4, addr, len,
+					 &buf[pos]);
+			pos += SHA1_MAC_LEN;
+		} else {
+			hmac_sha1_vector(key, key_len, 4, addr, len,
+					 hash);
+			os_memcpy(&buf[pos], hash, plen);
+			break;
+		}
+		counter++;
+	}
+}
+
+
+/**
+ * eap_sake_derive_keys - Derive EAP-SAKE keys
+ * @root_secret_a: 16-byte Root-Secret-A
+ * @root_secret_b: 16-byte Root-Secret-B
+ * @rand_s: 16-byte RAND_S
+ * @rand_p: 16-byte RAND_P
+ * @tek: Buffer for Temporary EAK Keys (TEK-Auth[16] | TEK-Cipher[16])
+ * @msk: Buffer for 64-byte MSK
+ * @emsk: Buffer for 64-byte EMSK
+ *
+ * This function derives EAP-SAKE keys as defined in RFC 4763, section 3.2.6.
+ */
+void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
+			  const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk,
+			  u8 *emsk)
+{
+	u8 sms_a[EAP_SAKE_SMS_LEN];
+	u8 sms_b[EAP_SAKE_SMS_LEN];
+	u8 key_buf[EAP_MSK_LEN + EAP_EMSK_LEN];
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Deriving keys");
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-A",
+			root_secret_a, EAP_SAKE_ROOT_SECRET_LEN);
+	eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN,
+		     "SAKE Master Secret A",
+		     rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
+		     sms_a, EAP_SAKE_SMS_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-A", sms_a, EAP_SAKE_SMS_LEN);
+	eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key",
+		     rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
+		     tek, EAP_SAKE_TEK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Auth",
+			tek, EAP_SAKE_TEK_AUTH_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Cipher",
+			tek + EAP_SAKE_TEK_AUTH_LEN, EAP_SAKE_TEK_CIPHER_LEN);
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-B",
+			root_secret_b, EAP_SAKE_ROOT_SECRET_LEN);
+	eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN,
+		     "SAKE Master Secret B",
+		     rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
+		     sms_b, EAP_SAKE_SMS_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-B", sms_b, EAP_SAKE_SMS_LEN);
+	eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key",
+		     rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
+		     key_buf, sizeof(key_buf));
+	os_memcpy(msk, key_buf, EAP_MSK_LEN);
+	os_memcpy(emsk, key_buf + EAP_MSK_LEN, EAP_EMSK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: MSK", msk, EAP_MSK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: EMSK", emsk, EAP_EMSK_LEN);
+}
+
+
+/**
+ * eap_sake_compute_mic - Compute EAP-SAKE MIC for an EAP packet
+ * @tek_auth: 16-byte TEK-Auth
+ * @rand_s: 16-byte RAND_S
+ * @rand_p: 16-byte RAND_P
+ * @serverid: SERVERID
+ * @serverid_len: SERVERID length
+ * @peerid: PEERID
+ * @peerid_len: PEERID length
+ * @peer: MIC calculation for 0 = Server, 1 = Peer message
+ * @eap: EAP packet
+ * @eap_len: EAP packet length
+ * @mic_pos: MIC position in the EAP packet (must be [eap .. eap + eap_len])
+ * @mic: Buffer for the computed 16-byte MIC
+ */
+int eap_sake_compute_mic(const u8 *tek_auth,
+			 const u8 *rand_s, const u8 *rand_p,
+			 const u8 *serverid, size_t serverid_len,
+			 const u8 *peerid, size_t peerid_len,
+			 int peer, const u8 *eap, size_t eap_len,
+			 const u8 *mic_pos, u8 *mic)
+{
+	u8 _rand[2 * EAP_SAKE_RAND_LEN];
+	u8 *tmp, *pos;
+	size_t tmplen;
+
+	tmplen = serverid_len + 1 + peerid_len + 1 + eap_len;
+	tmp = os_malloc(tmplen);
+	if (tmp == NULL)
+		return -1;
+	pos = tmp;
+	if (peer) {
+		if (peerid) {
+			os_memcpy(pos, peerid, peerid_len);
+			pos += peerid_len;
+		}
+		*pos++ = 0x00;
+		if (serverid) {
+			os_memcpy(pos, serverid, serverid_len);
+			pos += serverid_len;
+		}
+		*pos++ = 0x00;
+
+		os_memcpy(_rand, rand_s, EAP_SAKE_RAND_LEN);
+		os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_p,
+			  EAP_SAKE_RAND_LEN);
+	} else {
+		if (serverid) {
+			os_memcpy(pos, serverid, serverid_len);
+			pos += serverid_len;
+		}
+		*pos++ = 0x00;
+		if (peerid) {
+			os_memcpy(pos, peerid, peerid_len);
+			pos += peerid_len;
+		}
+		*pos++ = 0x00;
+
+		os_memcpy(_rand, rand_p, EAP_SAKE_RAND_LEN);
+		os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_s,
+			  EAP_SAKE_RAND_LEN);
+	}
+
+	os_memcpy(pos, eap, eap_len);
+	os_memset(pos + (mic_pos - eap), 0, EAP_SAKE_MIC_LEN);
+
+	eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN,
+		     peer ? "Peer MIC" : "Server MIC",
+		     _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen,
+		     mic, EAP_SAKE_MIC_LEN);
+
+	os_free(tmp);
+
+	return 0;
+}
+
+
+void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data,
+		       size_t len)
+{
+	wpabuf_put_u8(buf, type);
+	wpabuf_put_u8(buf, 2 + len); /* Length; including attr header */
+	if (data)
+		wpabuf_put_data(buf, data, len);
+	else
+		os_memset(wpabuf_put(buf, len), 0, len);
+}
diff --git a/hostap/src/eap_common/eap_sake_common.h b/hostap/src/eap_common/eap_sake_common.h
new file mode 100644
index 0000000..9e1e757
--- /dev/null
+++ b/hostap/src/eap_common/eap_sake_common.h
@@ -0,0 +1,96 @@
+/*
+ * EAP server/peer: EAP-SAKE shared routines
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_SAKE_COMMON_H
+#define EAP_SAKE_COMMON_H
+
+#define EAP_SAKE_VERSION 2
+
+#define EAP_SAKE_SUBTYPE_CHALLENGE 1
+#define EAP_SAKE_SUBTYPE_CONFIRM 2
+#define EAP_SAKE_SUBTYPE_AUTH_REJECT 3
+#define EAP_SAKE_SUBTYPE_IDENTITY 4
+
+#define EAP_SAKE_AT_RAND_S 1
+#define EAP_SAKE_AT_RAND_P 2
+#define EAP_SAKE_AT_MIC_S 3
+#define EAP_SAKE_AT_MIC_P 4
+#define EAP_SAKE_AT_SERVERID 5
+#define EAP_SAKE_AT_PEERID 6
+#define EAP_SAKE_AT_SPI_S 7
+#define EAP_SAKE_AT_SPI_P 8
+#define EAP_SAKE_AT_ANY_ID_REQ 9
+#define EAP_SAKE_AT_PERM_ID_REQ 10
+#define EAP_SAKE_AT_ENCR_DATA 128
+#define EAP_SAKE_AT_IV 129
+#define EAP_SAKE_AT_PADDING 130
+#define EAP_SAKE_AT_NEXT_TMPID 131
+#define EAP_SAKE_AT_MSK_LIFE 132
+
+#define EAP_SAKE_RAND_LEN 16
+#define EAP_SAKE_MIC_LEN 16
+#define EAP_SAKE_ROOT_SECRET_LEN 16
+#define EAP_SAKE_SMS_LEN 16
+#define EAP_SAKE_TEK_AUTH_LEN 16
+#define EAP_SAKE_TEK_CIPHER_LEN 16
+#define EAP_SAKE_TEK_LEN (EAP_SAKE_TEK_AUTH_LEN + EAP_SAKE_TEK_CIPHER_LEN)
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_sake_hdr {
+	u8 version; /* EAP_SAKE_VERSION */
+	u8 session_id;
+	u8 subtype;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+struct eap_sake_parse_attr {
+	const u8 *rand_s;
+	const u8 *rand_p;
+	const u8 *mic_s;
+	const u8 *mic_p;
+	const u8 *serverid;
+	size_t serverid_len;
+	const u8 *peerid;
+	size_t peerid_len;
+	const u8 *spi_s;
+	size_t spi_s_len;
+	const u8 *spi_p;
+	size_t spi_p_len;
+	const u8 *any_id_req;
+	const u8 *perm_id_req;
+	const u8 *encr_data;
+	size_t encr_data_len;
+	const u8 *iv;
+	size_t iv_len;
+	const u8 *next_tmpid;
+	size_t next_tmpid_len;
+	const u8 *msk_life;
+};
+
+int eap_sake_parse_attributes(const u8 *buf, size_t len,
+			      struct eap_sake_parse_attr *attr);
+void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
+			  const u8 *rand_s, const u8 *rand_p,
+			  u8 *tek, u8 *msk, u8 *emsk);
+int eap_sake_compute_mic(const u8 *tek_auth,
+			 const u8 *rand_s, const u8 *rand_p,
+			 const u8 *serverid, size_t serverid_len,
+			 const u8 *peerid, size_t peerid_len,
+			 int peer, const u8 *eap, size_t eap_len,
+			 const u8 *mic_pos, u8 *mic);
+void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data,
+		       size_t len);
+
+#endif /* EAP_SAKE_COMMON_H */
diff --git a/hostap/src/eap_common/eap_sim_common.c b/hostap/src/eap_common/eap_sim_common.c
new file mode 100644
index 0000000..2adc3b3
--- /dev/null
+++ b/hostap/src/eap_common/eap_sim_common.c
@@ -0,0 +1,1208 @@
+/*
+ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines
+ * Copyright (c) 2004-2008, 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 "wpabuf.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "eap_common/eap_defs.h"
+#include "eap_common/eap_sim_common.h"
+
+
+static int eap_sim_prf(const u8 *key, u8 *x, size_t xlen)
+{
+	return fips186_2_prf(key, EAP_SIM_MK_LEN, x, xlen);
+}
+
+
+void eap_sim_derive_mk(const u8 *identity, size_t identity_len,
+		       const u8 *nonce_mt, u16 selected_version,
+		       const u8 *ver_list, size_t ver_list_len,
+		       int num_chal, const u8 *kc, u8 *mk)
+{
+	u8 sel_ver[2];
+	const unsigned char *addr[5];
+	size_t len[5];
+
+	addr[0] = identity;
+	len[0] = identity_len;
+	addr[1] = kc;
+	len[1] = num_chal * EAP_SIM_KC_LEN;
+	addr[2] = nonce_mt;
+	len[2] = EAP_SIM_NONCE_MT_LEN;
+	addr[3] = ver_list;
+	len[3] = ver_list_len;
+	addr[4] = sel_ver;
+	len[4] = 2;
+
+	WPA_PUT_BE16(sel_ver, selected_version);
+
+	/* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
+	sha1_vector(5, addr, len, mk);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
+}
+
+
+void eap_aka_derive_mk(const u8 *identity, size_t identity_len,
+		       const u8 *ik, const u8 *ck, u8 *mk)
+{
+	const u8 *addr[3];
+	size_t len[3];
+
+	addr[0] = identity;
+	len[0] = identity_len;
+	addr[1] = ik;
+	len[1] = EAP_AKA_IK_LEN;
+	addr[2] = ck;
+	len[2] = EAP_AKA_CK_LEN;
+
+	/* MK = SHA1(Identity|IK|CK) */
+	sha1_vector(3, addr, len, mk);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", ik, EAP_AKA_IK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", ck, EAP_AKA_CK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: MK", mk, EAP_SIM_MK_LEN);
+}
+
+
+int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk, u8 *emsk)
+{
+	u8 buf[EAP_SIM_K_ENCR_LEN + EAP_SIM_K_AUT_LEN +
+	       EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN], *pos;
+	if (eap_sim_prf(mk, buf, sizeof(buf)) < 0) {
+		wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys");
+		return -1;
+	}
+	pos = buf;
+	os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
+	pos += EAP_SIM_K_ENCR_LEN;
+	os_memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN);
+	pos += EAP_SIM_K_AUT_LEN;
+	os_memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN);
+	pos += EAP_SIM_KEYING_DATA_LEN;
+	os_memcpy(emsk, pos, EAP_EMSK_LEN);
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr",
+			k_encr, EAP_SIM_K_ENCR_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut",
+			k_aut, EAP_SIM_K_AUT_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material (MSK)",
+			msk, EAP_SIM_KEYING_DATA_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN);
+	os_memset(buf, 0, sizeof(buf));
+
+	return 0;
+}
+
+
+int eap_sim_derive_keys_reauth(u16 _counter,
+			       const u8 *identity, size_t identity_len,
+			       const u8 *nonce_s, const u8 *mk, u8 *msk,
+			       u8 *emsk)
+{
+	u8 xkey[SHA1_MAC_LEN];
+	u8 buf[EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN + 32];
+	u8 counter[2];
+	const u8 *addr[4];
+	size_t len[4];
+
+	while (identity_len > 0 && identity[identity_len - 1] == 0) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop null "
+			   "character from the end of identity");
+		identity_len--;
+	}
+	addr[0] = identity;
+	len[0] = identity_len;
+	addr[1] = counter;
+	len[1] = 2;
+	addr[2] = nonce_s;
+	len[2] = EAP_SIM_NONCE_S_LEN;
+	addr[3] = mk;
+	len[3] = EAP_SIM_MK_LEN;
+
+	WPA_PUT_BE16(counter, _counter);
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth");
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
+			  identity, identity_len);
+	wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2);
+	wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s,
+		    EAP_SIM_NONCE_S_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
+
+	/* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */
+	sha1_vector(4, addr, len, xkey);
+	wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN);
+
+	if (eap_sim_prf(xkey, buf, sizeof(buf)) < 0) {
+		wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys");
+		return -1;
+	}
+	if (msk) {
+		os_memcpy(msk, buf, EAP_SIM_KEYING_DATA_LEN);
+		wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material (MSK)",
+			    msk, EAP_SIM_KEYING_DATA_LEN);
+	}
+	if (emsk) {
+		os_memcpy(emsk, buf + EAP_SIM_KEYING_DATA_LEN, EAP_EMSK_LEN);
+		wpa_hexdump(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN);
+	}
+	os_memset(buf, 0, sizeof(buf));
+
+	return 0;
+}
+
+
+int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
+		       const u8 *mac, const u8 *extra, size_t extra_len)
+{
+	unsigned char hmac[SHA1_MAC_LEN];
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *tmp;
+
+	if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN ||
+	    mac < wpabuf_head_u8(req) ||
+	    mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
+		return -1;
+
+	tmp = os_malloc(wpabuf_len(req));
+	if (tmp == NULL)
+		return -1;
+
+	addr[0] = tmp;
+	len[0] = wpabuf_len(req);
+	addr[1] = extra;
+	len[1] = extra_len;
+
+	/* HMAC-SHA1-128 */
+	os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
+	os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg",
+		    tmp, wpabuf_len(req));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - extra data",
+		    extra, extra_len);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Verify MAC - K_aut",
+			k_aut, EAP_SIM_K_AUT_LEN);
+	hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC: MAC",
+		    hmac, EAP_SIM_MAC_LEN);
+	os_free(tmp);
+
+	return (os_memcmp_const(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
+}
+
+
+void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac,
+		     const u8 *extra, size_t extra_len)
+{
+	unsigned char hmac[SHA1_MAC_LEN];
+	const u8 *addr[2];
+	size_t len[2];
+
+	addr[0] = msg;
+	len[0] = msg_len;
+	addr[1] = extra;
+	len[1] = extra_len;
+
+	/* HMAC-SHA1-128 */
+	os_memset(mac, 0, EAP_SIM_MAC_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - msg", msg, msg_len);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - extra data",
+		    extra, extra_len);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Add MAC - K_aut",
+			k_aut, EAP_SIM_K_AUT_LEN);
+	hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
+	os_memcpy(mac, hmac, EAP_SIM_MAC_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC: MAC",
+		    mac, EAP_SIM_MAC_LEN);
+}
+
+
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+static void prf_prime(const u8 *k, const char *seed1,
+		      const u8 *seed2, size_t seed2_len,
+		      const u8 *seed3, size_t seed3_len,
+		      u8 *res, size_t res_len)
+{
+	const u8 *addr[5];
+	size_t len[5];
+	u8 hash[SHA256_MAC_LEN];
+	u8 iter;
+
+	/*
+	 * PRF'(K,S) = T1 | T2 | T3 | T4 | ...
+	 * T1 = HMAC-SHA-256 (K, S | 0x01)
+	 * T2 = HMAC-SHA-256 (K, T1 | S | 0x02)
+	 * T3 = HMAC-SHA-256 (K, T2 | S | 0x03)
+	 * T4 = HMAC-SHA-256 (K, T3 | S | 0x04)
+	 * ...
+	 */
+
+	addr[0] = hash;
+	len[0] = 0;
+	addr[1] = (const u8 *) seed1;
+	len[1] = os_strlen(seed1);
+	addr[2] = seed2;
+	len[2] = seed2_len;
+	addr[3] = seed3;
+	len[3] = seed3_len;
+	addr[4] = &iter;
+	len[4] = 1;
+
+	iter = 0;
+	while (res_len) {
+		size_t hlen;
+		iter++;
+		hmac_sha256_vector(k, 32, 5, addr, len, hash);
+		len[0] = SHA256_MAC_LEN;
+		hlen = res_len > SHA256_MAC_LEN ? SHA256_MAC_LEN : res_len;
+		os_memcpy(res, hash, hlen);
+		res += hlen;
+		res_len -= hlen;
+	}
+}
+
+
+void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len,
+			       const u8 *ik, const u8 *ck, u8 *k_encr,
+			       u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk)
+{
+	u8 key[EAP_AKA_IK_LEN + EAP_AKA_CK_LEN];
+	u8 keys[EAP_SIM_K_ENCR_LEN + EAP_AKA_PRIME_K_AUT_LEN +
+		EAP_AKA_PRIME_K_RE_LEN + EAP_MSK_LEN + EAP_EMSK_LEN];
+	u8 *pos;
+
+	/*
+	 * MK = PRF'(IK'|CK',"EAP-AKA'"|Identity)
+	 * K_encr = MK[0..127]
+	 * K_aut  = MK[128..383]
+	 * K_re   = MK[384..639]
+	 * MSK    = MK[640..1151]
+	 * EMSK   = MK[1152..1663]
+	 */
+
+	os_memcpy(key, ik, EAP_AKA_IK_LEN);
+	os_memcpy(key + EAP_AKA_IK_LEN, ck, EAP_AKA_CK_LEN);
+
+	prf_prime(key, "EAP-AKA'", identity, identity_len, NULL, 0,
+		  keys, sizeof(keys));
+
+	pos = keys;
+	os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_encr",
+			k_encr, EAP_SIM_K_ENCR_LEN);
+	pos += EAP_SIM_K_ENCR_LEN;
+
+	os_memcpy(k_aut, pos, EAP_AKA_PRIME_K_AUT_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_aut",
+			k_aut, EAP_AKA_PRIME_K_AUT_LEN);
+	pos += EAP_AKA_PRIME_K_AUT_LEN;
+
+	os_memcpy(k_re, pos, EAP_AKA_PRIME_K_RE_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_re",
+			k_re, EAP_AKA_PRIME_K_RE_LEN);
+	pos += EAP_AKA_PRIME_K_RE_LEN;
+
+	os_memcpy(msk, pos, EAP_MSK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN);
+	pos += EAP_MSK_LEN;
+
+	os_memcpy(emsk, pos, EAP_EMSK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN);
+}
+
+
+int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
+				     const u8 *identity, size_t identity_len,
+				     const u8 *nonce_s, u8 *msk, u8 *emsk)
+{
+	u8 seed3[2 + EAP_SIM_NONCE_S_LEN];
+	u8 keys[EAP_MSK_LEN + EAP_EMSK_LEN];
+	u8 *pos;
+
+	/*
+	 * MK = PRF'(K_re,"EAP-AKA' re-auth"|Identity|counter|NONCE_S)
+	 * MSK  = MK[0..511]
+	 * EMSK = MK[512..1023]
+	 */
+
+	WPA_PUT_BE16(seed3, counter);
+	os_memcpy(seed3 + 2, nonce_s, EAP_SIM_NONCE_S_LEN);
+
+	prf_prime(k_re, "EAP-AKA' re-auth", identity, identity_len,
+		  seed3, sizeof(seed3),
+		  keys, sizeof(keys));
+
+	pos = keys;
+	os_memcpy(msk, pos, EAP_MSK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN);
+	pos += EAP_MSK_LEN;
+
+	os_memcpy(emsk, pos, EAP_EMSK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN);
+
+	os_memset(keys, 0, sizeof(keys));
+
+	return 0;
+}
+
+
+int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
+			      const u8 *mac, const u8 *extra, size_t extra_len)
+{
+	unsigned char hmac[SHA256_MAC_LEN];
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *tmp;
+
+	if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN ||
+	    mac < wpabuf_head_u8(req) ||
+	    mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
+		return -1;
+
+	tmp = os_malloc(wpabuf_len(req));
+	if (tmp == NULL)
+		return -1;
+
+	addr[0] = tmp;
+	len[0] = wpabuf_len(req);
+	addr[1] = extra;
+	len[1] = extra_len;
+
+	/* HMAC-SHA-256-128 */
+	os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
+	os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg",
+		    tmp, wpabuf_len(req));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - extra data",
+		    extra, extra_len);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Verify MAC - K_aut",
+			k_aut, EAP_AKA_PRIME_K_AUT_LEN);
+	hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC: MAC",
+		    hmac, EAP_SIM_MAC_LEN);
+	os_free(tmp);
+
+	return (os_memcmp_const(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
+}
+
+
+void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
+			    u8 *mac, const u8 *extra, size_t extra_len)
+{
+	unsigned char hmac[SHA256_MAC_LEN];
+	const u8 *addr[2];
+	size_t len[2];
+
+	addr[0] = msg;
+	len[0] = msg_len;
+	addr[1] = extra;
+	len[1] = extra_len;
+
+	/* HMAC-SHA-256-128 */
+	os_memset(mac, 0, EAP_SIM_MAC_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - msg", msg, msg_len);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - extra data",
+		    extra, extra_len);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Add MAC - K_aut",
+			k_aut, EAP_AKA_PRIME_K_AUT_LEN);
+	hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac);
+	os_memcpy(mac, hmac, EAP_SIM_MAC_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC: MAC",
+		    mac, EAP_SIM_MAC_LEN);
+}
+
+
+void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak,
+				      const u8 *network_name,
+				      size_t network_name_len)
+{
+	u8 key[EAP_AKA_CK_LEN + EAP_AKA_IK_LEN];
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[5];
+	size_t len[5];
+	u8 fc;
+	u8 l0[2], l1[2];
+
+	/* 3GPP TS 33.402 V8.0.0
+	 * (CK', IK') = F(CK, IK, <access network identity>)
+	 */
+	/* TODO: CK', IK' generation should really be moved into the actual
+	 * AKA procedure with network name passed in there and option to use
+	 * AMF separation bit = 1 (3GPP TS 33.401). */
+
+	/* Change Request 33.402 CR 0033 to version 8.1.1 from
+	 * 3GPP TSG-SA WG3 Meeting #53 in September 2008:
+	 *
+	 * CK' || IK' = HMAC-SHA-256(Key, S)
+	 * S = FC || P0 || L0 || P1 || L1 || ... || Pn || Ln
+	 * Key = CK || IK
+	 * FC = 0x20
+	 * P0 = access network identity (3GPP TS 24.302)
+	 * L0 = length of acceess network identity (2 octets, big endian)
+	 * P1 = SQN xor AK (if AK is not used, AK is treaded as 000..0
+	 * L1 = 0x00 0x06
+	 */
+
+	fc = 0x20;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA': Derive (CK',IK') from (CK,IK)");
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK", ck, EAP_AKA_CK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK", ik, EAP_AKA_IK_LEN);
+	wpa_printf(MSG_DEBUG, "EAP-AKA': FC = 0x%x", fc);
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': P0 = Access network identity",
+			  network_name, network_name_len);
+	wpa_hexdump(MSG_DEBUG, "EAP-AKA': P1 = SQN xor AK", sqn_ak, 6);
+
+	os_memcpy(key, ck, EAP_AKA_CK_LEN);
+	os_memcpy(key + EAP_AKA_CK_LEN, ik, EAP_AKA_IK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': Key = CK || IK",
+			key, sizeof(key));
+
+	addr[0] = &fc;
+	len[0] = 1;
+	addr[1] = network_name;
+	len[1] = network_name_len;
+	WPA_PUT_BE16(l0, network_name_len);
+	addr[2] = l0;
+	len[2] = 2;
+	addr[3] = sqn_ak;
+	len[3] = 6;
+	WPA_PUT_BE16(l1, 6);
+	addr[4] = l1;
+	len[4] = 2;
+
+	hmac_sha256_vector(key, sizeof(key), 5, addr, len, hash);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': KDF output (CK' || IK')",
+			hash, sizeof(hash));
+
+	os_memcpy(ck, hash, EAP_AKA_CK_LEN);
+	os_memcpy(ik, hash + EAP_AKA_CK_LEN, EAP_AKA_IK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK'", ck, EAP_AKA_CK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK'", ik, EAP_AKA_IK_LEN);
+}
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+
+
+int eap_sim_parse_attr(const u8 *start, const u8 *end,
+		       struct eap_sim_attrs *attr, int aka, int encr)
+{
+	const u8 *pos = start, *apos;
+	size_t alen, plen, i, list_len;
+
+	os_memset(attr, 0, sizeof(*attr));
+	attr->id_req = NO_ID_REQ;
+	attr->notification = -1;
+	attr->counter = -1;
+	attr->selected_version = -1;
+	attr->client_error_code = -1;
+
+	while (pos < end) {
+		if (pos + 2 > end) {
+			wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)");
+			return -1;
+		}
+		wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d",
+			   pos[0], pos[1] * 4);
+		if (pos + pos[1] * 4 > end) {
+			wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow "
+				   "(pos=%p len=%d end=%p)",
+				   pos, pos[1] * 4, end);
+			return -1;
+		}
+		if (pos[1] == 0) {
+			wpa_printf(MSG_INFO, "EAP-SIM: Attribute underflow");
+			return -1;
+		}
+		apos = pos + 2;
+		alen = pos[1] * 4 - 2;
+		wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data",
+			    apos, alen);
+
+		switch (pos[0]) {
+		case EAP_SIM_AT_RAND:
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND");
+			apos += 2;
+			alen -= 2;
+			if ((!aka && (alen % GSM_RAND_LEN)) ||
+			    (aka && alen != EAP_AKA_RAND_LEN)) {
+				wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND"
+					   " (len %lu)",
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->rand = apos;
+			attr->num_chal = alen / GSM_RAND_LEN;
+			break;
+		case EAP_SIM_AT_AUTN:
+			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN");
+			if (!aka) {
+				wpa_printf(MSG_DEBUG, "EAP-SIM: "
+					   "Unexpected AT_AUTN");
+				return -1;
+			}
+			apos += 2;
+			alen -= 2;
+			if (alen != EAP_AKA_AUTN_LEN) {
+				wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN"
+					   " (len %lu)",
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->autn = apos;
+			break;
+		case EAP_SIM_AT_PADDING:
+			if (!encr) {
+				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+					   "AT_PADDING");
+				return -1;
+			}
+			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING");
+			for (i = 2; i < alen; i++) {
+				if (apos[i] != 0) {
+					wpa_printf(MSG_INFO, "EAP-SIM: (encr) "
+						   "AT_PADDING used a non-zero"
+						   " padding byte");
+					wpa_hexdump(MSG_DEBUG, "EAP-SIM: "
+						    "(encr) padding bytes",
+						    apos + 2, alen - 2);
+					return -1;
+				}
+			}
+			break;
+		case EAP_SIM_AT_NONCE_MT:
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT");
+			if (alen != 2 + EAP_SIM_NONCE_MT_LEN) {
+				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+					   "AT_NONCE_MT length");
+				return -1;
+			}
+			attr->nonce_mt = apos + 2;
+			break;
+		case EAP_SIM_AT_PERMANENT_ID_REQ:
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ");
+			attr->id_req = PERMANENT_ID;
+			break;
+		case EAP_SIM_AT_MAC:
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC");
+			if (alen != 2 + EAP_SIM_MAC_LEN) {
+				wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC "
+					   "length");
+				return -1;
+			}
+			attr->mac = apos + 2;
+			break;
+		case EAP_SIM_AT_NOTIFICATION:
+			if (alen != 2) {
+				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+					   "AT_NOTIFICATION length %lu",
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->notification = apos[0] * 256 + apos[1];
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d",
+				   attr->notification);
+			break;
+		case EAP_SIM_AT_ANY_ID_REQ:
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ");
+			attr->id_req = ANY_ID;
+			break;
+		case EAP_SIM_AT_IDENTITY:
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY");
+			plen = WPA_GET_BE16(apos);
+			apos += 2;
+			alen -= 2;
+			if (plen > alen) {
+				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+					   "AT_IDENTITY (Actual Length %lu, "
+					   "remaining length %lu)",
+					   (unsigned long) plen,
+					   (unsigned long) alen);
+				return -1;
+			}
+
+			attr->identity = apos;
+			attr->identity_len = plen;
+			break;
+		case EAP_SIM_AT_VERSION_LIST:
+			if (aka) {
+				wpa_printf(MSG_DEBUG, "EAP-AKA: "
+					   "Unexpected AT_VERSION_LIST");
+				return -1;
+			}
+			list_len = apos[0] * 256 + apos[1];
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST");
+			if (list_len < 2 || list_len > alen - 2) {
+				wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
+					   "AT_VERSION_LIST (list_len=%lu "
+					   "attr_len=%lu)",
+					   (unsigned long) list_len,
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->version_list = apos + 2;
+			attr->version_list_len = list_len;
+			break;
+		case EAP_SIM_AT_SELECTED_VERSION:
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION");
+			if (alen != 2) {
+				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+					   "AT_SELECTED_VERSION length %lu",
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->selected_version = apos[0] * 256 + apos[1];
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION "
+				   "%d", attr->selected_version);
+			break;
+		case EAP_SIM_AT_FULLAUTH_ID_REQ:
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ");
+			attr->id_req = FULLAUTH_ID;
+			break;
+		case EAP_SIM_AT_COUNTER:
+			if (!encr) {
+				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+					   "AT_COUNTER");
+				return -1;
+			}
+			if (alen != 2) {
+				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
+					   "AT_COUNTER (alen=%lu)",
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->counter = apos[0] * 256 + apos[1];
+			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d",
+				   attr->counter);
+			break;
+		case EAP_SIM_AT_COUNTER_TOO_SMALL:
+			if (!encr) {
+				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+					   "AT_COUNTER_TOO_SMALL");
+				return -1;
+			}
+			if (alen != 2) {
+				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
+					   "AT_COUNTER_TOO_SMALL (alen=%lu)",
+					   (unsigned long) alen);
+				return -1;
+			}
+			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+				   "AT_COUNTER_TOO_SMALL");
+			attr->counter_too_small = 1;
+			break;
+		case EAP_SIM_AT_NONCE_S:
+			if (!encr) {
+				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+					   "AT_NONCE_S");
+				return -1;
+			}
+			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+				   "AT_NONCE_S");
+			if (alen != 2 + EAP_SIM_NONCE_S_LEN) {
+				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
+					   "AT_NONCE_S (alen=%lu)",
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->nonce_s = apos + 2;
+			break;
+		case EAP_SIM_AT_CLIENT_ERROR_CODE:
+			if (alen != 2) {
+				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+					   "AT_CLIENT_ERROR_CODE length %lu",
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->client_error_code = apos[0] * 256 + apos[1];
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE "
+				   "%d", attr->client_error_code);
+			break;
+		case EAP_SIM_AT_IV:
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV");
+			if (alen != 2 + EAP_SIM_MAC_LEN) {
+				wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV "
+					   "length %lu", (unsigned long) alen);
+				return -1;
+			}
+			attr->iv = apos + 2;
+			break;
+		case EAP_SIM_AT_ENCR_DATA:
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA");
+			attr->encr_data = apos + 2;
+			attr->encr_data_len = alen - 2;
+			if (attr->encr_data_len % 16) {
+				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+					   "AT_ENCR_DATA length %lu",
+					   (unsigned long)
+					   attr->encr_data_len);
+				return -1;
+			}
+			break;
+		case EAP_SIM_AT_NEXT_PSEUDONYM:
+			if (!encr) {
+				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+					   "AT_NEXT_PSEUDONYM");
+				return -1;
+			}
+			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+				   "AT_NEXT_PSEUDONYM");
+			plen = apos[0] * 256 + apos[1];
+			if (plen > alen - 2) {
+				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
+					   " AT_NEXT_PSEUDONYM (actual"
+					   " len %lu, attr len %lu)",
+					   (unsigned long) plen,
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->next_pseudonym = pos + 4;
+			attr->next_pseudonym_len = plen;
+			break;
+		case EAP_SIM_AT_NEXT_REAUTH_ID:
+			if (!encr) {
+				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+					   "AT_NEXT_REAUTH_ID");
+				return -1;
+			}
+			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+				   "AT_NEXT_REAUTH_ID");
+			plen = apos[0] * 256 + apos[1];
+			if (plen > alen - 2) {
+				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
+					   " AT_NEXT_REAUTH_ID (actual"
+					   " len %lu, attr len %lu)",
+					   (unsigned long) plen,
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->next_reauth_id = pos + 4;
+			attr->next_reauth_id_len = plen;
+			break;
+		case EAP_SIM_AT_RES:
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RES");
+			attr->res_len_bits = WPA_GET_BE16(apos);
+			apos += 2;
+			alen -= 2;
+			if (!aka || alen < EAP_AKA_MIN_RES_LEN ||
+			    alen > EAP_AKA_MAX_RES_LEN) {
+				wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RES "
+					   "(len %lu)",
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->res = apos;
+			attr->res_len = alen;
+			break;
+		case EAP_SIM_AT_AUTS:
+			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTS");
+			if (!aka) {
+				wpa_printf(MSG_DEBUG, "EAP-SIM: "
+					   "Unexpected AT_AUTS");
+				return -1;
+			}
+			if (alen != EAP_AKA_AUTS_LEN) {
+				wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTS"
+					   " (len %lu)",
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->auts = apos;
+			break;
+		case EAP_SIM_AT_CHECKCODE:
+			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_CHECKCODE");
+			if (!aka) {
+				wpa_printf(MSG_DEBUG, "EAP-SIM: "
+					   "Unexpected AT_CHECKCODE");
+				return -1;
+			}
+			apos += 2;
+			alen -= 2;
+			if (alen != 0 && alen != EAP_AKA_CHECKCODE_LEN &&
+			    alen != EAP_AKA_PRIME_CHECKCODE_LEN) {
+				wpa_printf(MSG_INFO, "EAP-AKA: Invalid "
+					   "AT_CHECKCODE (len %lu)",
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->checkcode = apos;
+			attr->checkcode_len = alen;
+			break;
+		case EAP_SIM_AT_RESULT_IND:
+			if (encr) {
+				wpa_printf(MSG_ERROR, "EAP-SIM: Encrypted "
+					   "AT_RESULT_IND");
+				return -1;
+			}
+			if (alen != 2) {
+				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+					   "AT_RESULT_IND (alen=%lu)",
+					   (unsigned long) alen);
+				return -1;
+			}
+			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND");
+			attr->result_ind = 1;
+			break;
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+		case EAP_SIM_AT_KDF_INPUT:
+			if (aka != 2) {
+				wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
+					   "AT_KDF_INPUT");
+				return -1;
+			}
+
+			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF_INPUT");
+			plen = WPA_GET_BE16(apos);
+			apos += 2;
+			alen -= 2;
+			if (plen > alen) {
+				wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
+					   "AT_KDF_INPUT (Actual Length %lu, "
+					   "remaining length %lu)",
+					   (unsigned long) plen,
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->kdf_input = apos;
+			attr->kdf_input_len = plen;
+			break;
+		case EAP_SIM_AT_KDF:
+			if (aka != 2) {
+				wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
+					   "AT_KDF");
+				return -1;
+			}
+
+			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF");
+			if (alen != 2) {
+				wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
+					   "AT_KDF (len %lu)",
+					   (unsigned long) alen);
+				return -1;
+			}
+			if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) {
+				wpa_printf(MSG_DEBUG, "EAP-AKA': Too many "
+					   "AT_KDF attributes - ignore this");
+				break;
+			}
+			attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos);
+			attr->kdf_count++;
+			break;
+		case EAP_SIM_AT_BIDDING:
+			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_BIDDING");
+			if (alen != 2) {
+				wpa_printf(MSG_INFO, "EAP-AKA: Invalid "
+					   "AT_BIDDING (len %lu)",
+					   (unsigned long) alen);
+				return -1;
+			}
+			attr->bidding = apos;
+			break;
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+		default:
+			if (pos[0] < 128) {
+				wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized "
+					   "non-skippable attribute %d",
+					   pos[0]);
+				return -1;
+			}
+
+			wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable"
+				   " attribute %d ignored", pos[0]);
+			break;
+		}
+
+		pos += pos[1] * 4;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully "
+		   "(aka=%d encr=%d)", aka, encr);
+
+	return 0;
+}
+
+
+u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
+			size_t encr_data_len, const u8 *iv,
+			struct eap_sim_attrs *attr, int aka)
+{
+	u8 *decrypted;
+
+	if (!iv) {
+		wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV");
+		return NULL;
+	}
+
+	decrypted = os_malloc(encr_data_len);
+	if (decrypted == NULL)
+		return NULL;
+	os_memcpy(decrypted, encr_data, encr_data_len);
+
+	if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) {
+		os_free(decrypted);
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA",
+		    decrypted, encr_data_len);
+
+	if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr,
+			       aka, 1)) {
+		wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse "
+			   "decrypted AT_ENCR_DATA");
+		os_free(decrypted);
+		return NULL;
+	}
+
+	return decrypted;
+}
+
+
+#define EAP_SIM_INIT_LEN 128
+
+struct eap_sim_msg {
+	struct wpabuf *buf;
+	size_t mac, iv, encr; /* index from buf */
+};
+
+
+struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype)
+{
+	struct eap_sim_msg *msg;
+	struct eap_hdr *eap;
+	u8 *pos;
+
+	msg = os_zalloc(sizeof(*msg));
+	if (msg == NULL)
+		return NULL;
+
+	msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN);
+	if (msg->buf == NULL) {
+		os_free(msg);
+		return NULL;
+	}
+	eap = wpabuf_put(msg->buf, sizeof(*eap));
+	eap->code = code;
+	eap->identifier = id;
+
+	pos = wpabuf_put(msg->buf, 4);
+	*pos++ = type;
+	*pos++ = subtype;
+	*pos++ = 0; /* Reserved */
+	*pos++ = 0; /* Reserved */
+
+	return msg;
+}
+
+
+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;
+	struct wpabuf *buf;
+
+	if (msg == NULL)
+		return NULL;
+
+	eap = wpabuf_mhead(msg->buf);
+	eap->length = host_to_be16(wpabuf_len(msg->buf));
+
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_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) +
+				       msg->mac, extra, extra_len);
+	} else
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+	if (k_aut && msg->mac) {
+		eap_sim_add_mac(k_aut, (u8 *) wpabuf_head(msg->buf),
+				wpabuf_len(msg->buf),
+				(u8 *) wpabuf_mhead(msg->buf) + msg->mac,
+				extra, extra_len);
+	}
+
+	buf = msg->buf;
+	os_free(msg);
+	return buf;
+}
+
+
+void eap_sim_msg_free(struct eap_sim_msg *msg)
+{
+	if (msg) {
+		wpabuf_free(msg->buf);
+		os_free(msg);
+	}
+}
+
+
+u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
+			  const u8 *data, size_t len)
+{
+	int attr_len = 2 + len;
+	int pad_len;
+	u8 *start;
+
+	if (msg == NULL)
+		return NULL;
+
+	pad_len = (4 - attr_len % 4) % 4;
+	attr_len += pad_len;
+	if (wpabuf_resize(&msg->buf, attr_len))
+		return NULL;
+	start = wpabuf_put(msg->buf, 0);
+	wpabuf_put_u8(msg->buf, attr);
+	wpabuf_put_u8(msg->buf, attr_len / 4);
+	wpabuf_put_data(msg->buf, data, len);
+	if (pad_len)
+		os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len);
+	return start;
+}
+
+
+u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value,
+		     const u8 *data, size_t len)
+{
+	int attr_len = 4 + len;
+	int pad_len;
+	u8 *start;
+
+	if (msg == NULL)
+		return NULL;
+
+	pad_len = (4 - attr_len % 4) % 4;
+	attr_len += pad_len;
+	if (wpabuf_resize(&msg->buf, attr_len))
+		return NULL;
+	start = wpabuf_put(msg->buf, 0);
+	wpabuf_put_u8(msg->buf, attr);
+	wpabuf_put_u8(msg->buf, attr_len / 4);
+	wpabuf_put_be16(msg->buf, value);
+	if (data)
+		wpabuf_put_data(msg->buf, data, len);
+	else
+		wpabuf_put(msg->buf, len);
+	if (pad_len)
+		os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len);
+	return start;
+}
+
+
+u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr)
+{
+	u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN);
+	if (pos)
+		msg->mac = (pos - wpabuf_head_u8(msg->buf)) + 4;
+	return pos;
+}
+
+
+int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
+			       u8 attr_encr)
+{
+	u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN);
+	if (pos == NULL)
+		return -1;
+	msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4;
+	if (random_get_bytes(wpabuf_mhead_u8(msg->buf) + msg->iv,
+			     EAP_SIM_IV_LEN)) {
+		msg->iv = 0;
+		return -1;
+	}
+
+	pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0);
+	if (pos == NULL) {
+		msg->iv = 0;
+		return -1;
+	}
+	msg->encr = pos - wpabuf_head_u8(msg->buf);
+
+	return 0;
+}
+
+
+int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad)
+{
+	size_t encr_len;
+
+	if (msg == NULL || k_encr == NULL || msg->iv == 0 || msg->encr == 0)
+		return -1;
+
+	encr_len = wpabuf_len(msg->buf) - msg->encr - 4;
+	if (encr_len % 16) {
+		u8 *pos;
+		int pad_len = 16 - (encr_len % 16);
+		if (pad_len < 4) {
+			wpa_printf(MSG_WARNING, "EAP-SIM: "
+				   "eap_sim_msg_add_encr_end - invalid pad_len"
+				   " %d", pad_len);
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "   *AT_PADDING");
+		pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4);
+		if (pos == NULL)
+			return -1;
+		os_memset(pos + 4, 0, pad_len - 4);
+		encr_len += pad_len;
+	}
+	wpa_printf(MSG_DEBUG, "   (AT_ENCR_DATA data len %lu)",
+		   (unsigned long) encr_len);
+	wpabuf_mhead_u8(msg->buf)[msg->encr + 1] = encr_len / 4 + 1;
+	return aes_128_cbc_encrypt(k_encr, wpabuf_head_u8(msg->buf) + msg->iv,
+				   wpabuf_mhead_u8(msg->buf) + msg->encr + 4,
+				   encr_len);
+}
+
+
+void eap_sim_report_notification(void *msg_ctx, int notification, int aka)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+	const char *type = aka ? "AKA" : "SIM";
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+	switch (notification) {
+	case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH:
+		wpa_printf(MSG_WARNING, "EAP-%s: General failure "
+			   "notification (after authentication)", type);
+		break;
+	case EAP_SIM_TEMPORARILY_DENIED:
+		wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
+			   "User has been temporarily denied access to the "
+			   "requested service", type);
+		break;
+	case EAP_SIM_NOT_SUBSCRIBED:
+		wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
+			   "User has not subscribed to the requested service",
+			   type);
+		break;
+	case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH:
+		wpa_printf(MSG_WARNING, "EAP-%s: General failure "
+			   "notification (before authentication)", type);
+		break;
+	case EAP_SIM_SUCCESS:
+		wpa_printf(MSG_INFO, "EAP-%s: Successful authentication "
+			   "notification", type);
+		break;
+	default:
+		if (notification >= 32768) {
+			wpa_printf(MSG_INFO, "EAP-%s: Unrecognized "
+				   "non-failure notification %d",
+				   type, notification);
+		} else {
+			wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized "
+				   "failure notification %d",
+				   type, notification);
+		}
+	}
+}
diff --git a/hostap/src/eap_common/eap_sim_common.h b/hostap/src/eap_common/eap_sim_common.h
new file mode 100644
index 0000000..daeb0e2
--- /dev/null
+++ b/hostap/src/eap_common/eap_sim_common.h
@@ -0,0 +1,230 @@
+/*
+ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_SIM_COMMON_H
+#define EAP_SIM_COMMON_H
+
+#define EAP_SIM_NONCE_S_LEN 16
+#define EAP_SIM_NONCE_MT_LEN 16
+#define EAP_SIM_MAC_LEN 16
+#define EAP_SIM_MK_LEN 20
+#define EAP_SIM_K_AUT_LEN 16
+#define EAP_SIM_K_ENCR_LEN 16
+#define EAP_SIM_KEYING_DATA_LEN 64
+#define EAP_SIM_IV_LEN 16
+#define EAP_SIM_KC_LEN 8
+#define EAP_SIM_SRES_LEN 4
+
+#define GSM_RAND_LEN 16
+
+#define EAP_SIM_VERSION 1
+
+/* EAP-SIM Subtypes */
+#define EAP_SIM_SUBTYPE_START 10
+#define EAP_SIM_SUBTYPE_CHALLENGE 11
+#define EAP_SIM_SUBTYPE_NOTIFICATION 12
+#define EAP_SIM_SUBTYPE_REAUTHENTICATION 13
+#define EAP_SIM_SUBTYPE_CLIENT_ERROR 14
+
+/* AT_CLIENT_ERROR_CODE error codes */
+#define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0
+#define EAP_SIM_UNSUPPORTED_VERSION 1
+#define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2
+#define EAP_SIM_RAND_NOT_FRESH 3
+
+#define EAP_SIM_MAX_FAST_REAUTHS 1000
+
+#define EAP_SIM_MAX_CHAL 3
+
+
+/* EAP-AKA Subtypes */
+#define EAP_AKA_SUBTYPE_CHALLENGE 1
+#define EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT 2
+#define EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE 4
+#define EAP_AKA_SUBTYPE_IDENTITY 5
+#define EAP_AKA_SUBTYPE_NOTIFICATION 12
+#define EAP_AKA_SUBTYPE_REAUTHENTICATION 13
+#define EAP_AKA_SUBTYPE_CLIENT_ERROR 14
+
+/* AT_CLIENT_ERROR_CODE error codes */
+#define EAP_AKA_UNABLE_TO_PROCESS_PACKET 0
+
+#define EAP_AKA_RAND_LEN 16
+#define EAP_AKA_AUTN_LEN 16
+#define EAP_AKA_AUTS_LEN 14
+#define EAP_AKA_RES_MAX_LEN 16
+#define EAP_AKA_IK_LEN 16
+#define EAP_AKA_CK_LEN 16
+#define EAP_AKA_MAX_FAST_REAUTHS 1000
+#define EAP_AKA_MIN_RES_LEN 4
+#define EAP_AKA_MAX_RES_LEN 16
+#define EAP_AKA_CHECKCODE_LEN 20
+
+#define EAP_AKA_PRIME_K_AUT_LEN 32
+#define EAP_AKA_PRIME_CHECKCODE_LEN 32
+#define EAP_AKA_PRIME_K_RE_LEN 32
+
+struct wpabuf;
+
+void eap_sim_derive_mk(const u8 *identity, size_t identity_len,
+		       const u8 *nonce_mt, u16 selected_version,
+		       const u8 *ver_list, size_t ver_list_len,
+		       int num_chal, const u8 *kc, u8 *mk);
+void eap_aka_derive_mk(const u8 *identity, size_t identity_len,
+		       const u8 *ik, const u8 *ck, u8 *mk);
+int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk,
+			u8 *emsk);
+int eap_sim_derive_keys_reauth(u16 _counter,
+			       const u8 *identity, size_t identity_len,
+			       const u8 *nonce_s, const u8 *mk, u8 *msk,
+			       u8 *emsk);
+int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
+		       const u8 *mac, const u8 *extra, size_t extra_len);
+void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac,
+		     const u8 *extra, size_t extra_len);
+
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len,
+			       const u8 *ik, const u8 *ck, u8 *k_encr,
+			       u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk);
+int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
+				     const u8 *identity, size_t identity_len,
+				     const u8 *nonce_s, u8 *msk, u8 *emsk);
+int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
+			      const u8 *mac, const u8 *extra,
+			      size_t extra_len);
+void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
+			    u8 *mac, const u8 *extra, size_t extra_len);
+
+void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak,
+				      const u8 *network_name,
+				      size_t network_name_len);
+#else /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+static inline void eap_aka_prime_derive_keys(const u8 *identity,
+					     size_t identity_len,
+					     const u8 *ik, const u8 *ck,
+					     u8 *k_encr, u8 *k_aut, u8 *k_re,
+					     u8 *msk, u8 *emsk)
+{
+}
+
+static inline int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
+						   const u8 *identity,
+						   size_t identity_len,
+						   const u8 *nonce_s, u8 *msk,
+						   u8 *emsk)
+{
+	return -1;
+}
+
+static inline int eap_sim_verify_mac_sha256(const u8 *k_aut,
+					    const struct wpabuf *req,
+					    const u8 *mac, const u8 *extra,
+					    size_t extra_len)
+{
+	return -1;
+}
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+
+
+/* EAP-SIM/AKA Attributes (0..127 non-skippable) */
+#define EAP_SIM_AT_RAND 1
+#define EAP_SIM_AT_AUTN 2 /* only AKA */
+#define EAP_SIM_AT_RES 3 /* only AKA, only peer->server */
+#define EAP_SIM_AT_AUTS 4 /* only AKA, only peer->server */
+#define EAP_SIM_AT_PADDING 6 /* only encrypted */
+#define EAP_SIM_AT_NONCE_MT 7 /* only SIM, only send */
+#define EAP_SIM_AT_PERMANENT_ID_REQ 10
+#define EAP_SIM_AT_MAC 11
+#define EAP_SIM_AT_NOTIFICATION 12
+#define EAP_SIM_AT_ANY_ID_REQ 13
+#define EAP_SIM_AT_IDENTITY 14 /* only send */
+#define EAP_SIM_AT_VERSION_LIST 15 /* only SIM */
+#define EAP_SIM_AT_SELECTED_VERSION 16 /* only SIM */
+#define EAP_SIM_AT_FULLAUTH_ID_REQ 17
+#define EAP_SIM_AT_COUNTER 19 /* only encrypted */
+#define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */
+#define EAP_SIM_AT_NONCE_S 21 /* only encrypted */
+#define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */
+#define EAP_SIM_AT_KDF_INPUT 23 /* only AKA' */
+#define EAP_SIM_AT_KDF 24 /* only AKA' */
+#define EAP_SIM_AT_IV 129
+#define EAP_SIM_AT_ENCR_DATA 130
+#define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */
+#define EAP_SIM_AT_NEXT_REAUTH_ID 133 /* only encrypted */
+#define EAP_SIM_AT_CHECKCODE 134 /* only AKA */
+#define EAP_SIM_AT_RESULT_IND 135
+#define EAP_SIM_AT_BIDDING 136
+
+/* AT_NOTIFICATION notification code values */
+#define EAP_SIM_GENERAL_FAILURE_AFTER_AUTH 0
+#define EAP_SIM_TEMPORARILY_DENIED 1026
+#define EAP_SIM_NOT_SUBSCRIBED 1031
+#define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384
+#define EAP_SIM_SUCCESS 32768
+
+/* EAP-AKA' AT_KDF Key Derivation Function values */
+#define EAP_AKA_PRIME_KDF 1
+
+/* AT_BIDDING flags */
+#define EAP_AKA_BIDDING_FLAG_D 0x8000
+
+
+enum eap_sim_id_req {
+	NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID
+};
+
+
+struct eap_sim_attrs {
+	const u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s;
+	const u8 *next_pseudonym, *next_reauth_id;
+	const u8 *nonce_mt, *identity, *res, *auts;
+	const u8 *checkcode;
+	const u8 *kdf_input;
+	const u8 *bidding;
+	size_t num_chal, version_list_len, encr_data_len;
+	size_t next_pseudonym_len, next_reauth_id_len, identity_len, res_len;
+	size_t res_len_bits;
+	size_t checkcode_len;
+	size_t kdf_input_len;
+	enum eap_sim_id_req id_req;
+	int notification, counter, selected_version, client_error_code;
+	int counter_too_small;
+	int result_ind;
+#define EAP_AKA_PRIME_KDF_MAX 10
+	u16 kdf[EAP_AKA_PRIME_KDF_MAX];
+	size_t kdf_count;
+};
+
+int eap_sim_parse_attr(const u8 *start, const u8 *end,
+		       struct eap_sim_attrs *attr, int aka, int encr);
+u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
+			size_t encr_data_len, const u8 *iv,
+			struct eap_sim_attrs *attr, int aka);
+
+
+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, 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,
+			  const u8 *data, size_t len);
+u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr,
+		     u16 value, const u8 *data, size_t len);
+u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr);
+int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
+			       u8 attr_encr);
+int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr,
+			     int attr_pad);
+
+void eap_sim_report_notification(void *msg_ctx, int notification, int aka);
+
+#endif /* EAP_SIM_COMMON_H */
diff --git a/hostap/src/eap_common/eap_tlv_common.h b/hostap/src/eap_common/eap_tlv_common.h
new file mode 100644
index 0000000..3286055
--- /dev/null
+++ b/hostap/src/eap_common/eap_tlv_common.h
@@ -0,0 +1,112 @@
+/*
+ * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_TLV_COMMON_H
+#define EAP_TLV_COMMON_H
+
+/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-10.txt) */
+#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */
+#define EAP_TLV_NAK_TLV 4
+#define EAP_TLV_ERROR_CODE_TLV 5
+#define EAP_TLV_CONNECTION_BINDING_TLV 6
+#define EAP_TLV_VENDOR_SPECIFIC_TLV 7
+#define EAP_TLV_URI_TLV 8
+#define EAP_TLV_EAP_PAYLOAD_TLV 9
+#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10
+#define EAP_TLV_PAC_TLV 11 /* RFC 5422, Section 4.2 */
+#define EAP_TLV_CRYPTO_BINDING_TLV 12
+#define EAP_TLV_CALLING_STATION_ID_TLV 13
+#define EAP_TLV_CALLED_STATION_ID_TLV 14
+#define EAP_TLV_NAS_PORT_TYPE_TLV 15
+#define EAP_TLV_SERVER_IDENTIFIER_TLV 16
+#define EAP_TLV_IDENTITY_TYPE_TLV 17
+#define EAP_TLV_SERVER_TRUSTED_ROOT_TLV 18
+#define EAP_TLV_REQUEST_ACTION_TLV 19
+#define EAP_TLV_PKCS7_TLV 20
+
+#define EAP_TLV_RESULT_SUCCESS 1
+#define EAP_TLV_RESULT_FAILURE 2
+
+#define EAP_TLV_TYPE_MANDATORY 0x8000
+#define EAP_TLV_TYPE_MASK 0x3fff
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_tlv_hdr {
+	be16 tlv_type;
+	be16 length;
+} STRUCT_PACKED;
+
+struct eap_tlv_nak_tlv {
+	be16 tlv_type;
+	be16 length;
+	be32 vendor_id;
+	be16 nak_type;
+} STRUCT_PACKED;
+
+struct eap_tlv_result_tlv {
+	be16 tlv_type;
+	be16 length;
+	be16 status;
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.7 - Intermediate-Result TLV */
+struct eap_tlv_intermediate_result_tlv {
+	be16 tlv_type;
+	be16 length;
+	be16 status;
+	/* Followed by optional TLVs */
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.8 - Crypto-Binding TLV */
+struct eap_tlv_crypto_binding_tlv {
+	be16 tlv_type;
+	be16 length;
+	u8 reserved;
+	u8 version;
+	u8 received_version;
+	u8 subtype;
+	u8 nonce[32];
+	u8 compound_mac[20];
+} STRUCT_PACKED;
+
+struct eap_tlv_pac_ack_tlv {
+	be16 tlv_type;
+	be16 length;
+	be16 pac_type;
+	be16 pac_len;
+	be16 result;
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.9 - Request-Action TLV */
+struct eap_tlv_request_action_tlv {
+	be16 tlv_type;
+	be16 length;
+	be16 action;
+} STRUCT_PACKED;
+
+/* RFC 5422, Section 4.2.6 - PAC-Type TLV */
+struct eap_tlv_pac_type_tlv {
+	be16 tlv_type; /* PAC_TYPE_PAC_TYPE */
+	be16 length;
+	be16 pac_type;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1
+
+#define EAP_TLV_ACTION_PROCESS_TLV 1
+#define EAP_TLV_ACTION_NEGOTIATE_EAP 2
+
+#endif /* EAP_TLV_COMMON_H */
diff --git a/hostap/src/eap_common/eap_ttls.h b/hostap/src/eap_common/eap_ttls.h
new file mode 100644
index 0000000..17901d4
--- /dev/null
+++ b/hostap/src/eap_common/eap_ttls.h
@@ -0,0 +1,65 @@
+/*
+ * EAP server/peer: EAP-TTLS (RFC 5281)
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_TTLS_H
+#define EAP_TTLS_H
+
+struct ttls_avp {
+	be32 avp_code;
+	be32 avp_length; /* 8-bit flags, 24-bit length;
+			  * length includes AVP header */
+	/* optional 32-bit Vendor-ID */
+	/* Data */
+};
+
+struct ttls_avp_vendor {
+	be32 avp_code;
+	be32 avp_length; /* 8-bit flags, 24-bit length;
+			  * length includes AVP header */
+	be32 vendor_id;
+	/* Data */
+};
+
+#define AVP_FLAGS_VENDOR 0x80
+#define AVP_FLAGS_MANDATORY 0x40
+
+#define AVP_PAD(start, pos) \
+do { \
+	int __pad; \
+	__pad = (4 - (((pos) - (start)) & 3)) & 3; \
+	os_memset((pos), 0, __pad); \
+	pos += __pad; \
+} while (0)
+
+
+/* RFC 2865 */
+#define RADIUS_ATTR_USER_NAME 1
+#define RADIUS_ATTR_USER_PASSWORD 2
+#define RADIUS_ATTR_CHAP_PASSWORD 3
+#define RADIUS_ATTR_REPLY_MESSAGE 18
+#define RADIUS_ATTR_CHAP_CHALLENGE 60
+#define RADIUS_ATTR_EAP_MESSAGE 79
+
+/* RFC 2548 */
+#define RADIUS_VENDOR_ID_MICROSOFT 311
+#define RADIUS_ATTR_MS_CHAP_RESPONSE 1
+#define RADIUS_ATTR_MS_CHAP_ERROR 2
+#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6
+#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11
+#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25
+#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26
+#define RADIUS_ATTR_MS_CHAP2_CPW 27
+
+#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16
+#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50
+#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8
+#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50
+#define EAP_TTLS_CHAP_CHALLENGE_LEN 16
+#define EAP_TTLS_CHAP_PASSWORD_LEN 16
+
+#endif /* EAP_TTLS_H */
diff --git a/hostap/src/eap_common/eap_wsc_common.c b/hostap/src/eap_common/eap_wsc_common.c
new file mode 100644
index 0000000..7c1496e
--- /dev/null
+++ b/hostap/src/eap_common/eap_wsc_common.c
@@ -0,0 +1,33 @@
+/*
+ * EAP-WSC common routines for Wi-Fi Protected Setup
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_common.h"
+#include "wps/wps.h"
+#include "eap_wsc_common.h"
+
+struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code)
+{
+	struct wpabuf *msg;
+
+	msg = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2, code, id);
+	if (msg == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
+			   "FRAG_ACK");
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/FRAG_ACK");
+	wpabuf_put_u8(msg, WSC_FRAG_ACK); /* Op-Code */
+	wpabuf_put_u8(msg, 0); /* Flags */
+
+	return msg;
+}
diff --git a/hostap/src/eap_common/eap_wsc_common.h b/hostap/src/eap_common/eap_wsc_common.h
new file mode 100644
index 0000000..0e7b653
--- /dev/null
+++ b/hostap/src/eap_common/eap_wsc_common.h
@@ -0,0 +1,27 @@
+/*
+ * EAP-WSC definitions for Wi-Fi Protected Setup
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_WSC_COMMON_H
+#define EAP_WSC_COMMON_H
+
+#define EAP_VENDOR_TYPE_WSC 1
+
+#define WSC_FLAGS_MF 0x01
+#define WSC_FLAGS_LF 0x02
+
+#define WSC_ID_REGISTRAR "WFA-SimpleConfig-Registrar-1-0"
+#define WSC_ID_REGISTRAR_LEN 30
+#define WSC_ID_ENROLLEE "WFA-SimpleConfig-Enrollee-1-0"
+#define WSC_ID_ENROLLEE_LEN 29
+
+#define WSC_FRAGMENT_SIZE 1400
+
+
+struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code);
+
+#endif /* EAP_WSC_COMMON_H */
diff --git a/hostap/src/eap_common/ikev2_common.c b/hostap/src/eap_common/ikev2_common.c
new file mode 100644
index 0000000..d60358c
--- /dev/null
+++ b/hostap/src/eap_common/ikev2_common.c
@@ -0,0 +1,726 @@
+/*
+ * IKEv2 common routines for initiator and responder
+ * Copyright (c) 2007, 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/crypto.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/random.h"
+#include "ikev2_common.h"
+
+
+static const struct ikev2_integ_alg ikev2_integ_algs[] = {
+	{ AUTH_HMAC_SHA1_96, 20, 12 },
+	{ AUTH_HMAC_MD5_96, 16, 12 }
+};
+
+#define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs)
+
+
+static const struct ikev2_prf_alg ikev2_prf_algs[] = {
+	{ PRF_HMAC_SHA1, 20, 20 },
+	{ PRF_HMAC_MD5, 16, 16 }
+};
+
+#define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs)
+
+
+static const struct ikev2_encr_alg ikev2_encr_algs[] = {
+	{ ENCR_AES_CBC, 16, 16 }, /* only 128-bit keys supported for now */
+	{ ENCR_3DES, 24, 8 }
+};
+
+#define NUM_ENCR_ALGS ARRAY_SIZE(ikev2_encr_algs)
+
+
+const struct ikev2_integ_alg * ikev2_get_integ(int id)
+{
+	size_t i;
+
+	for (i = 0; i < NUM_INTEG_ALGS; i++) {
+		if (ikev2_integ_algs[i].id == id)
+			return &ikev2_integ_algs[i];
+	}
+
+	return NULL;
+}
+
+
+int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data,
+		     size_t data_len, u8 *hash)
+{
+	u8 tmphash[IKEV2_MAX_HASH_LEN];
+
+	switch (alg) {
+	case AUTH_HMAC_SHA1_96:
+		if (key_len != 20)
+			return -1;
+		hmac_sha1(key, key_len, data, data_len, tmphash);
+		os_memcpy(hash, tmphash, 12);
+		break;
+	case AUTH_HMAC_MD5_96:
+		if (key_len != 16)
+			return -1;
+		hmac_md5(key, key_len, data, data_len, tmphash);
+		os_memcpy(hash, tmphash, 12);
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+const struct ikev2_prf_alg * ikev2_get_prf(int id)
+{
+	size_t i;
+
+	for (i = 0; i < NUM_PRF_ALGS; i++) {
+		if (ikev2_prf_algs[i].id == id)
+			return &ikev2_prf_algs[i];
+	}
+
+	return NULL;
+}
+
+
+int ikev2_prf_hash(int alg, const u8 *key, size_t key_len,
+		   size_t num_elem, const u8 *addr[], const size_t *len,
+		   u8 *hash)
+{
+	switch (alg) {
+	case PRF_HMAC_SHA1:
+		hmac_sha1_vector(key, key_len, num_elem, addr, len, hash);
+		break;
+	case PRF_HMAC_MD5:
+		hmac_md5_vector(key, key_len, num_elem, addr, len, hash);
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int ikev2_prf_plus(int alg, const u8 *key, size_t key_len,
+		   const u8 *data, size_t data_len,
+		   u8 *out, size_t out_len)
+{
+	u8 hash[IKEV2_MAX_HASH_LEN];
+	size_t hash_len;
+	u8 iter, *pos, *end;
+	const u8 *addr[3];
+	size_t len[3];
+	const struct ikev2_prf_alg *prf;
+	int res;
+
+	prf = ikev2_get_prf(alg);
+	if (prf == NULL)
+		return -1;
+	hash_len = prf->hash_len;
+
+	addr[0] = hash;
+	len[0] = hash_len;
+	addr[1] = data;
+	len[1] = data_len;
+	addr[2] = &iter;
+	len[2] = 1;
+
+	pos = out;
+	end = out + out_len;
+	iter = 1;
+	while (pos < end) {
+		size_t clen;
+		if (iter == 1)
+			res = ikev2_prf_hash(alg, key, key_len, 2, &addr[1],
+					     &len[1], hash);
+		else
+			res = ikev2_prf_hash(alg, key, key_len, 3, addr, len,
+					     hash);
+		if (res < 0)
+			return -1;
+		clen = hash_len;
+		if ((int) clen > end - pos)
+			clen = end - pos;
+		os_memcpy(pos, hash, clen);
+		pos += clen;
+		iter++;
+	}
+
+	return 0;
+}
+
+
+const struct ikev2_encr_alg * ikev2_get_encr(int id)
+{
+	size_t i;
+
+	for (i = 0; i < NUM_ENCR_ALGS; i++) {
+		if (ikev2_encr_algs[i].id == id)
+			return &ikev2_encr_algs[i];
+	}
+
+	return NULL;
+}
+
+
+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;
+
+	switch (alg) {
+	case ENCR_3DES:
+		encr_alg = CRYPTO_CIPHER_ALG_3DES;
+		break;
+	case ENCR_AES_CBC:
+		encr_alg = CRYPTO_CIPHER_ALG_AES;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg);
+		return -1;
+	}
+
+	cipher = crypto_cipher_init(encr_alg, iv, key, key_len);
+	if (cipher == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher");
+		return -1;
+	}
+
+	if (crypto_cipher_encrypt(cipher, plain, crypt, len) < 0) {
+		wpa_printf(MSG_INFO, "IKEV2: Encryption failed");
+		crypto_cipher_deinit(cipher);
+		return -1;
+	}
+	crypto_cipher_deinit(cipher);
+
+	return 0;
+}
+
+
+int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+		       const u8 *crypt, u8 *plain, size_t len)
+{
+	struct crypto_cipher *cipher;
+	int encr_alg;
+
+	switch (alg) {
+	case ENCR_3DES:
+		encr_alg = CRYPTO_CIPHER_ALG_3DES;
+		break;
+	case ENCR_AES_CBC:
+		encr_alg = CRYPTO_CIPHER_ALG_AES;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg);
+		return -1;
+	}
+
+	cipher = crypto_cipher_init(encr_alg, iv, key, key_len);
+	if (cipher == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher");
+		return -1;
+	}
+
+	if (crypto_cipher_decrypt(cipher, crypt, plain, len) < 0) {
+		wpa_printf(MSG_INFO, "IKEV2: Decryption failed");
+		crypto_cipher_deinit(cipher);
+		return -1;
+	}
+	crypto_cipher_deinit(cipher);
+
+	return 0;
+}
+
+
+int ikev2_parse_payloads(struct ikev2_payloads *payloads,
+			 u8 next_payload, const u8 *pos, const u8 *end)
+{
+	const struct ikev2_payload_hdr *phdr;
+
+	os_memset(payloads, 0, sizeof(*payloads));
+
+	while (next_payload != IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) {
+		unsigned int plen, pdatalen, left;
+		const u8 *pdata;
+		wpa_printf(MSG_DEBUG, "IKEV2: Processing payload %u",
+			   next_payload);
+		if (end < pos)
+			return -1;
+		left = end - pos;
+		if (left < sizeof(*phdr)) {
+			wpa_printf(MSG_INFO, "IKEV2:   Too short message for "
+				   "payload header (left=%ld)",
+				   (long) (end - pos));
+			return -1;
+		}
+		phdr = (const struct ikev2_payload_hdr *) pos;
+		plen = WPA_GET_BE16(phdr->payload_length);
+		if (plen < sizeof(*phdr) || plen > left) {
+			wpa_printf(MSG_INFO, "IKEV2:   Invalid payload header "
+				   "length %d", plen);
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "IKEV2:   Next Payload: %u  Flags: 0x%x"
+			   "  Payload Length: %u",
+			   phdr->next_payload, phdr->flags, plen);
+
+		pdata = (const u8 *) (phdr + 1);
+		pdatalen = plen - sizeof(*phdr);
+
+		switch (next_payload) {
+		case IKEV2_PAYLOAD_SA:
+			wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Security "
+				   "Association");
+			payloads->sa = pdata;
+			payloads->sa_len = pdatalen;
+			break;
+		case IKEV2_PAYLOAD_KEY_EXCHANGE:
+			wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Key "
+				   "Exchange");
+			payloads->ke = pdata;
+			payloads->ke_len = pdatalen;
+			break;
+		case IKEV2_PAYLOAD_IDi:
+			wpa_printf(MSG_DEBUG, "IKEV2:   Payload: IDi");
+			payloads->idi = pdata;
+			payloads->idi_len = pdatalen;
+			break;
+		case IKEV2_PAYLOAD_IDr:
+			wpa_printf(MSG_DEBUG, "IKEV2:   Payload: IDr");
+			payloads->idr = pdata;
+			payloads->idr_len = pdatalen;
+			break;
+		case IKEV2_PAYLOAD_CERTIFICATE:
+			wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Certificate");
+			payloads->cert = pdata;
+			payloads->cert_len = pdatalen;
+			break;
+		case IKEV2_PAYLOAD_AUTHENTICATION:
+			wpa_printf(MSG_DEBUG, "IKEV2:   Payload: "
+				   "Authentication");
+			payloads->auth = pdata;
+			payloads->auth_len = pdatalen;
+			break;
+		case IKEV2_PAYLOAD_NONCE:
+			wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Nonce");
+			payloads->nonce = pdata;
+			payloads->nonce_len = pdatalen;
+			break;
+		case IKEV2_PAYLOAD_ENCRYPTED:
+			wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Encrypted");
+			payloads->encrypted = pdata;
+			payloads->encrypted_len = pdatalen;
+			break;
+		case IKEV2_PAYLOAD_NOTIFICATION:
+			wpa_printf(MSG_DEBUG, "IKEV2:   Payload: "
+				   "Notification");
+			payloads->notification = pdata;
+			payloads->notification_len = pdatalen;
+			break;
+		default:
+			if (phdr->flags & IKEV2_PAYLOAD_FLAGS_CRITICAL) {
+				wpa_printf(MSG_INFO, "IKEV2:   Unsupported "
+					   "critical payload %u - reject the "
+					   "entire message", next_payload);
+				return -1;
+			} else {
+				wpa_printf(MSG_DEBUG, "IKEV2:   Skipped "
+					   "unsupported payload %u",
+					   next_payload);
+			}
+		}
+
+		if (next_payload == IKEV2_PAYLOAD_ENCRYPTED &&
+		    pos + plen == end) {
+			/*
+			 * Next Payload in the case of Encrypted Payload is
+			 * actually the payload type for the first embedded
+			 * payload.
+			 */
+			payloads->encr_next_payload = phdr->next_payload;
+			next_payload = IKEV2_PAYLOAD_NO_NEXT_PAYLOAD;
+		} else
+			next_payload = phdr->next_payload;
+
+		pos += plen;
+	}
+
+	if (pos != end) {
+		wpa_printf(MSG_INFO, "IKEV2: Unexpected extra data after "
+			   "payloads");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg,
+			   const u8 *ID, size_t ID_len, u8 ID_type,
+			   struct ikev2_keys *keys, int initiator,
+			   const u8 *shared_secret, size_t shared_secret_len,
+			   const u8 *nonce, size_t nonce_len,
+			   const u8 *key_pad, size_t key_pad_len,
+			   u8 *auth_data)
+{
+	size_t sign_len, buf_len;
+	u8 *sign_data, *pos, *buf, hash[IKEV2_MAX_HASH_LEN];
+	const struct ikev2_prf_alg *prf;
+	const u8 *SK_p = initiator ? keys->SK_pi : keys->SK_pr;
+
+	prf = ikev2_get_prf(prf_alg);
+	if (sign_msg == NULL || ID == NULL || SK_p == NULL ||
+	    shared_secret == NULL || nonce == NULL || prf == NULL)
+		return -1;
+
+	/* prf(SK_pi/r,IDi/r') */
+	buf_len = 4 + ID_len;
+	buf = os_zalloc(buf_len);
+	if (buf == NULL)
+		return -1;
+	buf[0] = ID_type;
+	os_memcpy(buf + 4, ID, ID_len);
+	if (ikev2_prf_hash(prf->id, SK_p, keys->SK_prf_len,
+			   1, (const u8 **) &buf, &buf_len, hash) < 0) {
+		os_free(buf);
+		return -1;
+	}
+	os_free(buf);
+
+	/* sign_data = msg | Nr/i | prf(SK_pi/r,IDi/r') */
+	sign_len = wpabuf_len(sign_msg) + nonce_len + prf->hash_len;
+	sign_data = os_malloc(sign_len);
+	if (sign_data == NULL)
+		return -1;
+	pos = sign_data;
+	os_memcpy(pos, wpabuf_head(sign_msg), wpabuf_len(sign_msg));
+	pos += wpabuf_len(sign_msg);
+	os_memcpy(pos, nonce, nonce_len);
+	pos += nonce_len;
+	os_memcpy(pos, hash, prf->hash_len);
+
+	/* AUTH = prf(prf(Shared Secret, key pad, sign_data) */
+	if (ikev2_prf_hash(prf->id, shared_secret, shared_secret_len, 1,
+			   &key_pad, &key_pad_len, hash) < 0 ||
+	    ikev2_prf_hash(prf->id, hash, prf->hash_len, 1,
+			   (const u8 **) &sign_data, &sign_len, auth_data) < 0)
+	{
+		os_free(sign_data);
+		return -1;
+	}
+	os_free(sign_data);
+
+	return 0;
+}
+
+
+u8 * ikev2_decrypt_payload(int encr_id, int integ_id,
+			   struct ikev2_keys *keys, int initiator,
+			   const struct ikev2_hdr *hdr,
+			   const u8 *encrypted, size_t encrypted_len,
+			   size_t *res_len)
+{
+	size_t iv_len;
+	const u8 *pos, *end, *iv, *integ;
+	u8 hash[IKEV2_MAX_HASH_LEN], *decrypted;
+	size_t decrypted_len, pad_len;
+	const struct ikev2_integ_alg *integ_alg;
+	const struct ikev2_encr_alg *encr_alg;
+	const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er;
+	const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
+
+	if (encrypted == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: No Encrypted payload in SA_AUTH");
+		return NULL;
+	}
+
+	encr_alg = ikev2_get_encr(encr_id);
+	if (encr_alg == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type");
+		return NULL;
+	}
+	iv_len = encr_alg->block_size;
+
+	integ_alg = ikev2_get_integ(integ_id);
+	if (integ_alg == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type");
+		return NULL;
+	}
+
+	if (encrypted_len < iv_len + 1 + integ_alg->hash_len) {
+		wpa_printf(MSG_INFO, "IKEV2: No room for IV or Integrity "
+			  "Checksum");
+		return NULL;
+	}
+
+	iv = encrypted;
+	pos = iv + iv_len;
+	end = encrypted + encrypted_len;
+	integ = end - integ_alg->hash_len;
+
+	if (SK_a == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: No SK_a available");
+		return NULL;
+	}
+	if (ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len,
+			     (const u8 *) hdr,
+			     integ - (const u8 *) hdr, hash) < 0) {
+		wpa_printf(MSG_INFO, "IKEV2: Failed to calculate integrity "
+			   "hash");
+		return NULL;
+	}
+	if (os_memcmp_const(integ, hash, integ_alg->hash_len) != 0) {
+		wpa_printf(MSG_INFO, "IKEV2: Incorrect Integrity Checksum "
+			   "Data");
+		return NULL;
+	}
+
+	if (SK_e == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: No SK_e available");
+		return NULL;
+	}
+
+	decrypted_len = integ - pos;
+	decrypted = os_malloc(decrypted_len);
+	if (decrypted == NULL)
+		return NULL;
+
+	if (ikev2_encr_decrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv, pos,
+			       decrypted, decrypted_len) < 0) {
+		os_free(decrypted);
+		return NULL;
+	}
+
+	pad_len = decrypted[decrypted_len - 1];
+	if (decrypted_len < pad_len + 1) {
+		wpa_printf(MSG_INFO, "IKEV2: Invalid padding in encrypted "
+			   "payload");
+		os_free(decrypted);
+		return NULL;
+	}
+
+	decrypted_len -= pad_len + 1;
+
+	*res_len = decrypted_len;
+	return decrypted;
+}
+
+
+void ikev2_update_hdr(struct wpabuf *msg)
+{
+	struct ikev2_hdr *hdr;
+
+	/* Update lenth field in HDR */
+	hdr = wpabuf_mhead(msg);
+	WPA_PUT_BE32(hdr->length, wpabuf_len(msg));
+}
+
+
+int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys,
+			  int initiator, struct wpabuf *msg,
+			  struct wpabuf *plain, u8 next_payload)
+{
+	struct ikev2_payload_hdr *phdr;
+	size_t plen;
+	size_t iv_len, pad_len;
+	u8 *icv, *iv;
+	const struct ikev2_integ_alg *integ_alg;
+	const struct ikev2_encr_alg *encr_alg;
+	const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er;
+	const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Adding Encrypted payload");
+
+	/* Encr - RFC 4306, Sect. 3.14 */
+
+	encr_alg = ikev2_get_encr(encr_id);
+	if (encr_alg == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type");
+		return -1;
+	}
+	iv_len = encr_alg->block_size;
+
+	integ_alg = ikev2_get_integ(integ_id);
+	if (integ_alg == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type");
+		return -1;
+	}
+
+	if (SK_e == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: No SK_e available");
+		return -1;
+	}
+
+	if (SK_a == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: No SK_a available");
+		return -1;
+	}
+
+	phdr = wpabuf_put(msg, sizeof(*phdr));
+	phdr->next_payload = next_payload;
+	phdr->flags = 0;
+
+	iv = wpabuf_put(msg, iv_len);
+	if (random_get_bytes(iv, iv_len)) {
+		wpa_printf(MSG_INFO, "IKEV2: Could not generate IV");
+		return -1;
+	}
+
+	pad_len = iv_len - (wpabuf_len(plain) + 1) % iv_len;
+	if (pad_len == iv_len)
+		pad_len = 0;
+	wpabuf_put(plain, pad_len);
+	wpabuf_put_u8(plain, pad_len);
+
+	if (ikev2_encr_encrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv,
+			       wpabuf_head(plain), wpabuf_mhead(plain),
+			       wpabuf_len(plain)) < 0)
+		return -1;
+
+	wpabuf_put_buf(msg, plain);
+
+	/* Need to update all headers (Length fields) prior to hash func */
+	icv = wpabuf_put(msg, integ_alg->hash_len);
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+	WPA_PUT_BE16(phdr->payload_length, plen);
+
+	ikev2_update_hdr(msg);
+
+	return ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len,
+				wpabuf_head(msg),
+				wpabuf_len(msg) - integ_alg->hash_len, icv);
+
+	return 0;
+}
+
+
+int ikev2_keys_set(struct ikev2_keys *keys)
+{
+	return keys->SK_d && keys->SK_ai && keys->SK_ar && keys->SK_ei &&
+		keys->SK_er && keys->SK_pi && keys->SK_pr;
+}
+
+
+void ikev2_free_keys(struct ikev2_keys *keys)
+{
+	os_free(keys->SK_d);
+	os_free(keys->SK_ai);
+	os_free(keys->SK_ar);
+	os_free(keys->SK_ei);
+	os_free(keys->SK_er);
+	os_free(keys->SK_pi);
+	os_free(keys->SK_pr);
+	keys->SK_d = keys->SK_ai = keys->SK_ar = keys->SK_ei = keys->SK_er =
+		keys->SK_pi = keys->SK_pr = NULL;
+}
+
+
+int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf,
+			 const struct ikev2_integ_alg *integ,
+			 const struct ikev2_encr_alg *encr,
+			 const u8 *skeyseed, const u8 *data, size_t data_len,
+			 struct ikev2_keys *keys)
+{
+	u8 *keybuf, *pos;
+	size_t keybuf_len;
+
+	/*
+	 * {SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr } =
+	 *	prf+(SKEYSEED, Ni | Nr | SPIi | SPIr )
+	 */
+	ikev2_free_keys(keys);
+	keys->SK_d_len = prf->key_len;
+	keys->SK_integ_len = integ->key_len;
+	keys->SK_encr_len = encr->key_len;
+	keys->SK_prf_len = prf->key_len;
+
+	keybuf_len = keys->SK_d_len + 2 * keys->SK_integ_len +
+		2 * keys->SK_encr_len + 2 * keys->SK_prf_len;
+	keybuf = os_malloc(keybuf_len);
+	if (keybuf == NULL)
+		return -1;
+
+	if (ikev2_prf_plus(prf->id, skeyseed, prf->hash_len,
+			   data, data_len, keybuf, keybuf_len)) {
+		os_free(keybuf);
+		return -1;
+	}
+
+	pos = keybuf;
+
+	keys->SK_d = os_malloc(keys->SK_d_len);
+	if (keys->SK_d) {
+		os_memcpy(keys->SK_d, pos, keys->SK_d_len);
+		wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_d",
+				keys->SK_d, keys->SK_d_len);
+	}
+	pos += keys->SK_d_len;
+
+	keys->SK_ai = os_malloc(keys->SK_integ_len);
+	if (keys->SK_ai) {
+		os_memcpy(keys->SK_ai, pos, keys->SK_integ_len);
+		wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ai",
+				keys->SK_ai, keys->SK_integ_len);
+	}
+	pos += keys->SK_integ_len;
+
+	keys->SK_ar = os_malloc(keys->SK_integ_len);
+	if (keys->SK_ar) {
+		os_memcpy(keys->SK_ar, pos, keys->SK_integ_len);
+		wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ar",
+				keys->SK_ar, keys->SK_integ_len);
+	}
+	pos += keys->SK_integ_len;
+
+	keys->SK_ei = os_malloc(keys->SK_encr_len);
+	if (keys->SK_ei) {
+		os_memcpy(keys->SK_ei, pos, keys->SK_encr_len);
+		wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ei",
+				keys->SK_ei, keys->SK_encr_len);
+	}
+	pos += keys->SK_encr_len;
+
+	keys->SK_er = os_malloc(keys->SK_encr_len);
+	if (keys->SK_er) {
+		os_memcpy(keys->SK_er, pos, keys->SK_encr_len);
+		wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_er",
+				keys->SK_er, keys->SK_encr_len);
+	}
+	pos += keys->SK_encr_len;
+
+	keys->SK_pi = os_malloc(keys->SK_prf_len);
+	if (keys->SK_pi) {
+		os_memcpy(keys->SK_pi, pos, keys->SK_prf_len);
+		wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_pi",
+				keys->SK_pi, keys->SK_prf_len);
+	}
+	pos += keys->SK_prf_len;
+
+	keys->SK_pr = os_malloc(keys->SK_prf_len);
+	if (keys->SK_pr) {
+		os_memcpy(keys->SK_pr, pos, keys->SK_prf_len);
+		wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_pr",
+				keys->SK_pr, keys->SK_prf_len);
+	}
+
+	os_free(keybuf);
+
+	if (!ikev2_keys_set(keys)) {
+		ikev2_free_keys(keys);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/hostap/src/eap_common/ikev2_common.h b/hostap/src/eap_common/ikev2_common.h
new file mode 100644
index 0000000..8a7982a
--- /dev/null
+++ b/hostap/src/eap_common/ikev2_common.h
@@ -0,0 +1,334 @@
+/*
+ * IKEv2 definitions
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IKEV2_COMMON_H
+#define IKEV2_COMMON_H
+
+/*
+ * Nonce length must be at least 16 octets. It must also be at least half the
+ * key size of the negotiated PRF.
+ */
+#define IKEV2_NONCE_MIN_LEN 16
+#define IKEV2_NONCE_MAX_LEN 256
+
+/* IKE Header - RFC 4306, Sect. 3.1 */
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+#define IKEV2_SPI_LEN 8
+
+struct ikev2_hdr {
+	u8 i_spi[IKEV2_SPI_LEN]; /* IKE_SA Initiator's SPI */
+	u8 r_spi[IKEV2_SPI_LEN]; /* IKE_SA Responder's SPI */
+	u8 next_payload;
+	u8 version; /* MjVer | MnVer */
+	u8 exchange_type;
+	u8 flags;
+	u8 message_id[4];
+	u8 length[4]; /* total length of HDR + payloads */
+} STRUCT_PACKED;
+
+struct ikev2_payload_hdr {
+	u8 next_payload;
+	u8 flags;
+	u8 payload_length[2]; /* this payload, including the payload header */
+} STRUCT_PACKED;
+
+struct ikev2_proposal {
+	u8 type; /* 0 (last) or 2 (more) */
+	u8 reserved;
+	u8 proposal_length[2]; /* including all transform and attributes */
+	u8 proposal_num;
+	u8 protocol_id; /* IKEV2_PROTOCOL_* */
+	u8 spi_size;
+	u8 num_transforms;
+	/* SPI of spi_size octets */
+	/* Transforms */
+} STRUCT_PACKED;
+
+struct ikev2_transform {
+	u8 type; /* 0 (last) or 3 (more) */
+	u8 reserved;
+	u8 transform_length[2]; /* including Header and Attributes */
+	u8 transform_type;
+	u8 reserved2;
+	u8 transform_id[2];
+	/* Transform Attributes */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+/* Current IKEv2 version from RFC 4306 */
+#define IKEV2_MjVer 2
+#define IKEV2_MnVer 0
+#define IKEV2_VERSION (((IKEV2_MjVer) << 4) | (IKEV2_MnVer))
+
+/* IKEv2 Exchange Types */
+enum {
+	/* 0-33 RESERVED */
+	IKE_SA_INIT = 34,
+	IKE_SA_AUTH = 35,
+	CREATE_CHILD_SA = 36,
+	INFORMATION = 37
+	/* 38-239 RESERVED TO IANA */
+	/* 240-255 Reserved for private use */
+};
+
+/* IKEv2 Flags */
+#define IKEV2_HDR_INITIATOR	0x08
+#define IKEV2_HDR_VERSION	0x10
+#define IKEV2_HDR_RESPONSE	0x20
+
+/* Payload Header Flags */
+#define IKEV2_PAYLOAD_FLAGS_CRITICAL 0x01
+
+
+/* EAP-IKEv2 Payload Types (in Next Payload Type field)
+ * http://www.iana.org/assignments/eap-ikev2-payloads */
+enum {
+	IKEV2_PAYLOAD_NO_NEXT_PAYLOAD = 0,
+	IKEV2_PAYLOAD_SA = 33,
+	IKEV2_PAYLOAD_KEY_EXCHANGE = 34,
+	IKEV2_PAYLOAD_IDi = 35,
+	IKEV2_PAYLOAD_IDr = 36,
+	IKEV2_PAYLOAD_CERTIFICATE = 37,
+	IKEV2_PAYLOAD_CERT_REQ = 38,
+	IKEV2_PAYLOAD_AUTHENTICATION = 39,
+	IKEV2_PAYLOAD_NONCE = 40,
+	IKEV2_PAYLOAD_NOTIFICATION = 41,
+	IKEV2_PAYLOAD_VENDOD_ID = 43,
+	IKEV2_PAYLOAD_ENCRYPTED = 46,
+	IKEV2_PAYLOAD_NEXT_FAST_ID = 121
+};
+
+
+/* IKEv2 Proposal - Protocol ID */
+enum {
+	IKEV2_PROTOCOL_RESERVED = 0,
+	IKEV2_PROTOCOL_IKE = 1, /* IKE is the only one allowed for EAP-IKEv2 */
+	IKEV2_PROTOCOL_AH = 2,
+	IKEV2_PROTOCOL_ESP = 3
+};
+
+
+/* IKEv2 Transform Types */
+enum {
+	IKEV2_TRANSFORM_ENCR = 1,
+	IKEV2_TRANSFORM_PRF = 2,
+	IKEV2_TRANSFORM_INTEG = 3,
+	IKEV2_TRANSFORM_DH = 4,
+	IKEV2_TRANSFORM_ESN = 5
+};
+
+/* IKEv2 Transform Type 1 (Encryption Algorithm) */
+enum {
+	ENCR_DES_IV64 = 1,
+	ENCR_DES = 2,
+	ENCR_3DES = 3,
+	ENCR_RC5 = 4,
+	ENCR_IDEA = 5,
+	ENCR_CAST = 6,
+	ENCR_BLOWFISH = 7,
+	ENCR_3IDEA = 8,
+	ENCR_DES_IV32 = 9,
+	ENCR_NULL = 11,
+	ENCR_AES_CBC = 12,
+	ENCR_AES_CTR = 13
+};
+
+/* IKEv2 Transform Type 2 (Pseudo-random Function) */
+enum {
+	PRF_HMAC_MD5 = 1,
+	PRF_HMAC_SHA1 = 2,
+	PRF_HMAC_TIGER = 3,
+	PRF_AES128_XCBC = 4
+};
+
+/* IKEv2 Transform Type 3 (Integrity Algorithm) */
+enum {
+	AUTH_HMAC_MD5_96 = 1,
+	AUTH_HMAC_SHA1_96 = 2,
+	AUTH_DES_MAC = 3,
+	AUTH_KPDK_MD5 = 4,
+	AUTH_AES_XCBC_96 = 5
+};
+
+/* IKEv2 Transform Type 4 (Diffie-Hellman Group) */
+enum {
+	DH_GROUP1_768BIT_MODP = 1, /* RFC 4306 */
+	DH_GROUP2_1024BIT_MODP = 2, /* RFC 4306 */
+	DH_GROUP5_1536BIT_MODP = 5, /* RFC 3526 */
+	DH_GROUP5_2048BIT_MODP = 14, /* RFC 3526 */
+	DH_GROUP5_3072BIT_MODP = 15, /* RFC 3526 */
+	DH_GROUP5_4096BIT_MODP = 16, /* RFC 3526 */
+	DH_GROUP5_6144BIT_MODP = 17, /* RFC 3526 */
+	DH_GROUP5_8192BIT_MODP = 18 /* RFC 3526 */
+};
+
+
+/* Identification Data Types (RFC 4306, Sect. 3.5) */
+enum {
+	ID_IPV4_ADDR = 1,
+	ID_FQDN = 2,
+	ID_RFC822_ADDR = 3,
+	ID_IPV6_ADDR = 5,
+	ID_DER_ASN1_DN = 9,
+	ID_DER_ASN1_GN= 10,
+	ID_KEY_ID = 11
+};
+
+
+/* Certificate Encoding (RFC 4306, Sect. 3.6) */
+enum {
+	CERT_ENCODING_PKCS7_X509 = 1,
+	CERT_ENCODING_PGP_CERT = 2,
+	CERT_ENCODING_DNS_SIGNED_KEY = 3,
+	/* X.509 Certificate - Signature: DER encoded X.509 certificate whose
+	 * public key is used to validate the sender's AUTH payload */
+	CERT_ENCODING_X509_CERT_SIGN = 4,
+	CERT_ENCODING_KERBEROS_TOKEN = 6,
+	/* DER encoded X.509 certificate revocation list */
+	CERT_ENCODING_CRL = 7,
+	CERT_ENCODING_ARL = 8,
+	CERT_ENCODING_SPKI_CERT = 9,
+	CERT_ENCODING_X509_CERT_ATTR = 10,
+	/* PKCS #1 encoded RSA key */
+	CERT_ENCODING_RAW_RSA_KEY = 11,
+	CERT_ENCODING_HASH_AND_URL_X509_CERT = 12,
+	CERT_ENCODING_HASH_AND_URL_X509_BUNDLE = 13
+};
+
+
+/* Authentication Method (RFC 4306, Sect. 3.8) */
+enum {
+	AUTH_RSA_SIGN = 1,
+	AUTH_SHARED_KEY_MIC = 2,
+	AUTH_DSS_SIGN = 3
+};
+
+
+/* Notify Message Types (RFC 4306, Sect. 3.10.1) */
+enum {
+	UNSUPPORTED_CRITICAL_PAYLOAD = 1,
+	INVALID_IKE_SPI = 4,
+	INVALID_MAJOR_VERSION = 5,
+	INVALID_SYNTAX = 7,
+	INVALID_MESSAGE_ID = 9,
+	INVALID_SPI = 11,
+	NO_PROPOSAL_CHOSEN = 14,
+	INVALID_KE_PAYLOAD = 17,
+	AUTHENTICATION_FAILED = 24,
+	SINGLE_PAIR_REQUIRED = 34,
+	NO_ADDITIONAL_SAS = 35,
+	INTERNAL_ADDRESS_FAILURE = 36,
+	FAILED_CP_REQUIRED = 37,
+	TS_UNACCEPTABLE = 38,
+	INVALID_SELECTORS = 39
+};
+
+
+struct ikev2_keys {
+	u8 *SK_d, *SK_ai, *SK_ar, *SK_ei, *SK_er, *SK_pi, *SK_pr;
+	size_t SK_d_len, SK_integ_len, SK_encr_len, SK_prf_len;
+};
+
+
+int ikev2_keys_set(struct ikev2_keys *keys);
+void ikev2_free_keys(struct ikev2_keys *keys);
+
+
+/* Maximum hash length for supported hash algorithms */
+#define IKEV2_MAX_HASH_LEN 20
+
+struct ikev2_integ_alg {
+	int id;
+	size_t key_len;
+	size_t hash_len;
+};
+
+struct ikev2_prf_alg {
+	int id;
+	size_t key_len;
+	size_t hash_len;
+};
+
+struct ikev2_encr_alg {
+	int id;
+	size_t key_len;
+	size_t block_size;
+};
+
+const struct ikev2_integ_alg * ikev2_get_integ(int id);
+int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data,
+		     size_t data_len, u8 *hash);
+const struct ikev2_prf_alg * ikev2_get_prf(int id);
+int ikev2_prf_hash(int alg, const u8 *key, size_t key_len,
+		   size_t num_elem, const u8 *addr[], const size_t *len,
+		   u8 *hash);
+int ikev2_prf_plus(int alg, const u8 *key, size_t key_len,
+		   const u8 *data, size_t data_len,
+		   u8 *out, size_t out_len);
+const struct ikev2_encr_alg * ikev2_get_encr(int id);
+int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+		       const u8 *plain, u8 *crypt, size_t len);
+int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+		       const u8 *crypt, u8 *plain, size_t len);
+
+int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg,
+			   const u8 *ID, size_t ID_len, u8 ID_type,
+			   struct ikev2_keys *keys, int initiator,
+			   const u8 *shared_secret, size_t shared_secret_len,
+			   const u8 *nonce, size_t nonce_len,
+			   const u8 *key_pad, size_t key_pad_len,
+			   u8 *auth_data);
+
+
+struct ikev2_payloads {
+	const u8 *sa;
+	size_t sa_len;
+	const u8 *ke;
+	size_t ke_len;
+	const u8 *idi;
+	size_t idi_len;
+	const u8 *idr;
+	size_t idr_len;
+	const u8 *cert;
+	size_t cert_len;
+	const u8 *auth;
+	size_t auth_len;
+	const u8 *nonce;
+	size_t nonce_len;
+	const u8 *encrypted;
+	size_t encrypted_len;
+	u8 encr_next_payload;
+	const u8 *notification;
+	size_t notification_len;
+};
+
+int ikev2_parse_payloads(struct ikev2_payloads *payloads,
+			 u8 next_payload, const u8 *pos, const u8 *end);
+
+u8 * ikev2_decrypt_payload(int encr_id, int integ_id, struct ikev2_keys *keys,
+			   int initiator, const struct ikev2_hdr *hdr,
+			   const u8 *encrypted, size_t encrypted_len,
+			   size_t *res_len);
+void ikev2_update_hdr(struct wpabuf *msg);
+int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys,
+			  int initiator, struct wpabuf *msg,
+			  struct wpabuf *plain, u8 next_payload);
+int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf,
+			 const struct ikev2_integ_alg *integ,
+			 const struct ikev2_encr_alg *encr,
+			 const u8 *skeyseed, const u8 *data, size_t data_len,
+			 struct ikev2_keys *keys);
+
+#endif /* IKEV2_COMMON_H */
diff --git a/hostap/src/eap_peer/Makefile b/hostap/src/eap_peer/Makefile
new file mode 100644
index 0000000..6531ccd
--- /dev/null
+++ b/hostap/src/eap_peer/Makefile
@@ -0,0 +1,23 @@
+all: libeap_peer.a
+
+clean:
+	rm -f *~ *.o *.so *.d *.gcno *.gcda *.gcov libeap_peer.a
+
+install:
+	if ls *.so >/dev/null 2>&1; then \
+		install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \
+		cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \
+	; fi
+
+include ../lib.rules
+
+CFLAGS += -DIEEE8021X_EAPOL
+
+LIB_OBJS= \
+	eap.o \
+	eap_methods.o
+
+libeap_peer.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/eap_peer/eap.c b/hostap/src/eap_peer/eap.c
new file mode 100644
index 0000000..56c24b5
--- /dev/null
+++ b/hostap/src/eap_peer/eap.c
@@ -0,0 +1,2950 @@
+/*
+ * EAP peer state machines (RFC 4137)
+ * 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.
+ *
+ * This file implements the Peer State Machine as defined in RFC 4137. The used
+ * states and state transitions match mostly with the RFC. However, there are
+ * couple of additional transitions for working around small issues noticed
+ * during testing. These exceptions are explained in comments within the
+ * functions in this file. The method functions, m.func(), are similar to the
+ * ones used in RFC 4137, but some small changes have used here to optimize
+ * operations and to add functionality needed for fast re-authentication
+ * (session resumption).
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "pcsc_funcs.h"
+#include "state_machine.h"
+#include "ext_password.h"
+#include "crypto/crypto.h"
+#include "crypto/tls.h"
+#include "crypto/sha256.h"
+#include "common/wpa_ctrl.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_i.h"
+#include "eap_config.h"
+
+#define STATE_MACHINE_DATA struct eap_sm
+#define STATE_MACHINE_DEBUG_PREFIX "EAP"
+
+#define EAP_MAX_AUTH_ROUNDS 50
+#define EAP_CLIENT_TIMEOUT_DEFAULT 60
+
+
+static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
+				  EapType method);
+static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id);
+static void eap_sm_processIdentity(struct eap_sm *sm,
+				   const struct wpabuf *req);
+static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req);
+static struct wpabuf * eap_sm_buildNotify(int id);
+static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req);
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static const char * eap_sm_method_state_txt(EapMethodState state);
+static const char * eap_sm_decision_txt(EapDecision decision);
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+
+static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var)
+{
+	return sm->eapol_cb->get_bool(sm->eapol_ctx, var);
+}
+
+
+static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var,
+			   Boolean value)
+{
+	sm->eapol_cb->set_bool(sm->eapol_ctx, var, value);
+}
+
+
+static unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var)
+{
+	return sm->eapol_cb->get_int(sm->eapol_ctx, var);
+}
+
+
+static void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var,
+			  unsigned int value)
+{
+	sm->eapol_cb->set_int(sm->eapol_ctx, var, value);
+}
+
+
+static struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm)
+{
+	return sm->eapol_cb->get_eapReqData(sm->eapol_ctx);
+}
+
+
+static void eap_notify_status(struct eap_sm *sm, const char *status,
+				      const char *parameter)
+{
+	wpa_printf(MSG_DEBUG, "EAP: Status notification: %s (param=%s)",
+		   status, parameter);
+	if (sm->eapol_cb->notify_status)
+		sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter);
+}
+
+
+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);
+	sm->ext_pw_buf = NULL;
+
+	if (sm->m == NULL || sm->eap_method_priv == NULL)
+		return;
+
+	wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method "
+		   "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt);
+	sm->m->deinit(sm, sm->eap_method_priv);
+	sm->eap_method_priv = NULL;
+	sm->m = NULL;
+}
+
+
+/**
+ * eap_allowed_method - Check whether EAP method is allowed
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types
+ * @method: EAP type
+ * Returns: 1 = allowed EAP method, 0 = not allowed
+ */
+int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	int i;
+	struct eap_method_type *m;
+
+	if (config == NULL || config->eap_methods == NULL)
+		return 1;
+
+	m = config->eap_methods;
+	for (i = 0; m[i].vendor != EAP_VENDOR_IETF ||
+		     m[i].method != EAP_TYPE_NONE; i++) {
+		if (m[i].vendor == vendor && m[i].method == method)
+			return 1;
+	}
+	return 0;
+}
+
+
+/*
+ * This state initializes state machine variables when the machine is
+ * activated (portEnabled = TRUE). This is also used when re-starting
+ * authentication (eapRestart == TRUE).
+ */
+SM_STATE(EAP, INITIALIZE)
+{
+	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->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;
+	sm->methodState = METHOD_NONE;
+	sm->allowNotifications = TRUE;
+	sm->decision = DECISION_FAIL;
+	sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
+	eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
+	eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
+	eapol_set_bool(sm, EAPOL_eapFail, FALSE);
+	eap_sm_free_key(sm);
+	os_free(sm->eapSessionId);
+	sm->eapSessionId = NULL;
+	sm->eapKeyAvailable = FALSE;
+	eapol_set_bool(sm, EAPOL_eapRestart, FALSE);
+	sm->lastId = -1; /* new session - make sure this does not match with
+			  * the first EAP-Packet */
+	/*
+	 * RFC 4137 does not reset eapResp and eapNoResp here. However, this
+	 * seemed to be able to trigger cases where both were set and if EAPOL
+	 * state machine uses eapNoResp first, it may end up not sending a real
+	 * reply correctly. This occurred when the workaround in FAIL state set
+	 * eapNoResp = TRUE.. Maybe that workaround needs to be fixed to do
+	 * something else(?)
+	 */
+	eapol_set_bool(sm, EAPOL_eapResp, FALSE);
+	eapol_set_bool(sm, EAPOL_eapNoResp, FALSE);
+	sm->num_rounds = 0;
+	sm->prev_failure = 0;
+	sm->expected_failure = 0;
+	sm->reauthInit = FALSE;
+	sm->erp_seq = (u32) -1;
+}
+
+
+/*
+ * This state is reached whenever service from the lower layer is interrupted
+ * or unavailable (portEnabled == FALSE). Immediate transition to INITIALIZE
+ * occurs when the port becomes enabled.
+ */
+SM_STATE(EAP, DISABLED)
+{
+	SM_ENTRY(EAP, DISABLED);
+	sm->num_rounds = 0;
+	/*
+	 * RFC 4137 does not describe clearing of idleWhile here, but doing so
+	 * allows the timer tick to be stopped more quickly when EAP is not in
+	 * use.
+	 */
+	eapol_set_int(sm, EAPOL_idleWhile, 0);
+}
+
+
+/*
+ * The state machine spends most of its time here, waiting for something to
+ * happen. This state is entered unconditionally from INITIALIZE, DISCARD, and
+ * SEND_RESPONSE states.
+ */
+SM_STATE(EAP, IDLE)
+{
+	SM_ENTRY(EAP, IDLE);
+}
+
+
+/*
+ * This state is entered when an EAP packet is received (eapReq == TRUE) to
+ * parse the packet header.
+ */
+SM_STATE(EAP, RECEIVED)
+{
+	const struct wpabuf *eapReqData;
+
+	SM_ENTRY(EAP, RECEIVED);
+	eapReqData = eapol_get_eapReqData(sm);
+	/* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */
+	eap_sm_parseEapReq(sm, eapReqData);
+	sm->num_rounds++;
+}
+
+
+/*
+ * This state is entered when a request for a new type comes in. Either the
+ * correct method is started, or a Nak response is built.
+ */
+SM_STATE(EAP, GET_METHOD)
+{
+	int reinit;
+	EapType method;
+	const struct eap_method *eap_method;
+
+	SM_ENTRY(EAP, GET_METHOD);
+
+	if (sm->reqMethod == EAP_TYPE_EXPANDED)
+		method = sm->reqVendorMethod;
+	else
+		method = sm->reqMethod;
+
+	eap_method = eap_peer_get_eap_method(sm->reqVendor, method);
+
+	if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) {
+		wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed",
+			   sm->reqVendor, method);
+		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+			"vendor=%u method=%u -> NAK",
+			sm->reqVendor, method);
+		eap_notify_status(sm, "refuse proposed method",
+				  eap_method ?  eap_method->name : "unknown");
+		goto nak;
+	}
+
+	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+		"vendor=%u method=%u", sm->reqVendor, method);
+
+	eap_notify_status(sm, "accept proposed method",
+			  eap_method ?  eap_method->name : "unknown");
+	/*
+	 * RFC 4137 does not define specific operation for fast
+	 * re-authentication (session resumption). The design here is to allow
+	 * the previously used method data to be maintained for
+	 * re-authentication if the method support session resumption.
+	 * Otherwise, the previously used method data is freed and a new method
+	 * is allocated here.
+	 */
+	if (sm->fast_reauth &&
+	    sm->m && sm->m->vendor == sm->reqVendor &&
+	    sm->m->method == method &&
+	    sm->m->has_reauth_data &&
+	    sm->m->has_reauth_data(sm, sm->eap_method_priv)) {
+		wpa_printf(MSG_DEBUG, "EAP: Using previous method data"
+			   " for fast re-authentication");
+		reinit = 1;
+	} else {
+		eap_deinit_prev_method(sm, "GET_METHOD");
+		reinit = 0;
+	}
+
+	sm->selectedMethod = sm->reqMethod;
+	if (sm->m == NULL)
+		sm->m = eap_method;
+	if (!sm->m) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: "
+			   "vendor %d method %d",
+			   sm->reqVendor, method);
+		goto nak;
+	}
+
+	sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
+
+	wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: "
+		   "vendor %u method %u (%s)",
+		   sm->reqVendor, method, sm->m->name);
+	if (reinit)
+		sm->eap_method_priv = sm->m->init_for_reauth(
+			sm, sm->eap_method_priv);
+	else
+		sm->eap_method_priv = sm->m->init(sm);
+
+	if (sm->eap_method_priv == NULL) {
+		struct eap_peer_config *config = eap_get_config(sm);
+		wpa_msg(sm->msg_ctx, MSG_INFO,
+			"EAP: Failed to initialize EAP method: vendor %u "
+			"method %u (%s)",
+			sm->reqVendor, method, sm->m->name);
+		sm->m = NULL;
+		sm->methodState = METHOD_NONE;
+		sm->selectedMethod = EAP_TYPE_NONE;
+		if (sm->reqMethod == EAP_TYPE_TLS && config &&
+		    (config->pending_req_pin ||
+		     config->pending_req_passphrase)) {
+			/*
+			 * Return without generating Nak in order to allow
+			 * entering of PIN code or passphrase to retry the
+			 * current EAP packet.
+			 */
+			wpa_printf(MSG_DEBUG, "EAP: Pending PIN/passphrase "
+				   "request - skip Nak");
+			return;
+		}
+
+		goto nak;
+	}
+
+	sm->methodState = METHOD_INIT;
+	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_METHOD
+		"EAP vendor %u method %u (%s) selected",
+		sm->reqVendor, method, sm->m->name);
+	return;
+
+nak:
+	wpabuf_free(sm->eapRespData);
+	sm->eapRespData = NULL;
+	sm->eapRespData = eap_sm_buildNak(sm, sm->reqId);
+}
+
+
+#ifdef CONFIG_ERP
+
+static char * eap_home_realm(struct eap_sm *sm)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	char *realm;
+	size_t i, realm_len;
+
+	if (!config)
+		return NULL;
+
+	if (config->identity) {
+		for (i = 0; i < config->identity_len; i++) {
+			if (config->identity[i] == '@')
+				break;
+		}
+		if (i < config->identity_len) {
+			realm_len = config->identity_len - i - 1;
+			realm = os_malloc(realm_len + 1);
+			if (realm == NULL)
+				return NULL;
+			os_memcpy(realm, &config->identity[i + 1], realm_len);
+			realm[realm_len] = '\0';
+			return realm;
+		}
+	}
+
+	if (config->anonymous_identity) {
+		for (i = 0; i < config->anonymous_identity_len; i++) {
+			if (config->anonymous_identity[i] == '@')
+				break;
+		}
+		if (i < config->anonymous_identity_len) {
+			realm_len = config->anonymous_identity_len - i - 1;
+			realm = os_malloc(realm_len + 1);
+			if (realm == NULL)
+				return NULL;
+			os_memcpy(realm, &config->anonymous_identity[i + 1],
+				  realm_len);
+			realm[realm_len] = '\0';
+			return realm;
+		}
+	}
+
+	return os_strdup("");
+}
+
+
+static struct eap_erp_key *
+eap_erp_get_key(struct eap_sm *sm, const char *realm)
+{
+	struct eap_erp_key *erp;
+
+	dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) {
+		char *pos;
+
+		pos = os_strchr(erp->keyname_nai, '@');
+		if (!pos)
+			continue;
+		pos++;
+		if (os_strcmp(pos, realm) == 0)
+			return erp;
+	}
+
+	return NULL;
+}
+
+
+static struct eap_erp_key *
+eap_erp_get_key_nai(struct eap_sm *sm, const char *nai)
+{
+	struct eap_erp_key *erp;
+
+	dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) {
+		if (os_strcmp(erp->keyname_nai, nai) == 0)
+			return erp;
+	}
+
+	return NULL;
+}
+
+
+static void eap_peer_erp_free_key(struct eap_erp_key *erp)
+{
+	dl_list_del(&erp->list);
+	bin_clear_free(erp, sizeof(*erp));
+}
+
+
+static void eap_erp_remove_keys_realm(struct eap_sm *sm, const char *realm)
+{
+	struct eap_erp_key *erp;
+
+	while ((erp = eap_erp_get_key(sm, realm)) != NULL) {
+		wpa_printf(MSG_DEBUG, "EAP: Delete old ERP key %s",
+			   erp->keyname_nai);
+		eap_peer_erp_free_key(erp);
+	}
+}
+
+#endif /* CONFIG_ERP */
+
+
+void eap_peer_erp_free_keys(struct eap_sm *sm)
+{
+#ifdef CONFIG_ERP
+	struct eap_erp_key *erp, *tmp;
+
+	dl_list_for_each_safe(erp, tmp, &sm->erp_keys, struct eap_erp_key, list)
+		eap_peer_erp_free_key(erp);
+#endif /* CONFIG_ERP */
+}
+
+
+static void eap_peer_erp_init(struct eap_sm *sm)
+{
+#ifdef CONFIG_ERP
+	u8 *emsk = NULL;
+	size_t emsk_len = 0;
+	u8 EMSKname[EAP_EMSK_NAME_LEN];
+	u8 len[2];
+	char *realm;
+	size_t realm_len, nai_buf_len;
+	struct eap_erp_key *erp = NULL;
+	int pos;
+
+	realm = eap_home_realm(sm);
+	if (!realm)
+		return;
+	realm_len = os_strlen(realm);
+	wpa_printf(MSG_DEBUG, "EAP: Realm for ERP keyName-NAI: %s", realm);
+	eap_erp_remove_keys_realm(sm, realm);
+
+	nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + realm_len;
+	if (nai_buf_len > 253) {
+		/*
+		 * keyName-NAI has a maximum length of 253 octet to fit in
+		 * RADIUS attributes.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Too long realm for ERP keyName-NAI maximum length");
+		goto fail;
+	}
+	nai_buf_len++; /* null termination */
+	erp = os_zalloc(sizeof(*erp) + nai_buf_len);
+	if (erp == NULL)
+		goto fail;
+
+	emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
+	if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: No suitable EMSK available for ERP");
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
+
+	WPA_PUT_BE16(len, 8);
+	if (hmac_sha256_kdf(sm->eapSessionId, sm->eapSessionIdLen, "EMSK",
+			    len, sizeof(len),
+			    EMSKname, EAP_EMSK_NAME_LEN) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN);
+
+	pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len,
+			       EMSKname, EAP_EMSK_NAME_LEN);
+	erp->keyname_nai[pos] = '@';
+	os_memcpy(&erp->keyname_nai[pos + 1], realm, realm_len);
+
+	WPA_PUT_BE16(len, emsk_len);
+	if (hmac_sha256_kdf(emsk, emsk_len,
+			    "EAP Re-authentication Root Key@ietf.org",
+			    len, sizeof(len), erp->rRK, emsk_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP");
+		goto fail;
+	}
+	erp->rRK_len = emsk_len;
+	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len);
+
+	if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+			    "EAP Re-authentication Integrity Key@ietf.org",
+			    len, sizeof(len), erp->rIK, erp->rRK_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP");
+		goto fail;
+	}
+	erp->rIK_len = erp->rRK_len;
+	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len);
+
+	wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s", erp->keyname_nai);
+	dl_list_add(&sm->erp_keys, &erp->list);
+	erp = NULL;
+fail:
+	bin_clear_free(emsk, emsk_len);
+	bin_clear_free(erp, sizeof(*erp));
+	os_free(realm);
+#endif /* CONFIG_ERP */
+}
+
+
+#ifdef CONFIG_ERP
+static int eap_peer_erp_reauth_start(struct eap_sm *sm,
+				     const struct eap_hdr *hdr, size_t len)
+{
+	char *realm;
+	struct eap_erp_key *erp;
+	struct wpabuf *msg;
+	u8 hash[SHA256_MAC_LEN];
+
+	realm = eap_home_realm(sm);
+	if (!realm)
+		return -1;
+
+	erp = eap_erp_get_key(sm, realm);
+	os_free(realm);
+	realm = NULL;
+	if (!erp)
+		return -1;
+
+	if (erp->next_seq >= 65536)
+		return -1; /* SEQ has range of 0..65535 */
+
+	/* TODO: check rRK lifetime expiration */
+
+	wpa_printf(MSG_DEBUG, "EAP: Valid ERP key found %s (SEQ=%u)",
+		   erp->keyname_nai, erp->next_seq);
+
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
+			    1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16,
+			    EAP_CODE_INITIATE, hdr->identifier);
+	if (msg == NULL)
+		return -1;
+
+	wpabuf_put_u8(msg, 0x20); /* Flags: R=0 B=0 L=1 */
+	wpabuf_put_be16(msg, erp->next_seq);
+
+	wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI);
+	wpabuf_put_u8(msg, os_strlen(erp->keyname_nai));
+	wpabuf_put_str(msg, erp->keyname_nai);
+
+	wpabuf_put_u8(msg, EAP_ERP_CS_HMAC_SHA256_128); /* Cryptosuite */
+
+	if (hmac_sha256(erp->rIK, erp->rIK_len,
+			wpabuf_head(msg), wpabuf_len(msg), hash) < 0) {
+		wpabuf_free(msg);
+		return -1;
+	}
+	wpabuf_put_data(msg, hash, 16);
+
+	wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth");
+	sm->erp_seq = erp->next_seq;
+	erp->next_seq++;
+	wpabuf_free(sm->eapRespData);
+	sm->eapRespData = msg;
+	sm->reauthInit = TRUE;
+	return 0;
+}
+#endif /* CONFIG_ERP */
+
+
+/*
+ * The method processing happens here. The request from the authenticator is
+ * processed, and an appropriate response packet is built.
+ */
+SM_STATE(EAP, METHOD)
+{
+	struct wpabuf *eapReqData;
+	struct eap_method_ret ret;
+	int min_len = 1;
+
+	SM_ENTRY(EAP, METHOD);
+	if (sm->m == NULL) {
+		wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected");
+		return;
+	}
+
+	eapReqData = eapol_get_eapReqData(sm);
+	if (sm->m->vendor == EAP_VENDOR_IETF && sm->m->method == EAP_TYPE_LEAP)
+		min_len = 0; /* LEAP uses EAP-Success without payload */
+	if (!eap_hdr_len_valid(eapReqData, min_len))
+		return;
+
+	/*
+	 * Get ignore, methodState, decision, allowNotifications, and
+	 * eapRespData. RFC 4137 uses three separate method procedure (check,
+	 * process, and buildResp) in this state. These have been combined into
+	 * a single function call to m->process() in order to optimize EAP
+	 * method implementation interface a bit. These procedures are only
+	 * used from within this METHOD state, so there is no need to keep
+	 * these as separate C functions.
+	 *
+	 * The RFC 4137 procedures return values as follows:
+	 * ignore = m.check(eapReqData)
+	 * (methodState, decision, allowNotifications) = m.process(eapReqData)
+	 * eapRespData = m.buildResp(reqId)
+	 */
+	os_memset(&ret, 0, sizeof(ret));
+	ret.ignore = sm->ignore;
+	ret.methodState = sm->methodState;
+	ret.decision = sm->decision;
+	ret.allowNotifications = sm->allowNotifications;
+	wpabuf_free(sm->eapRespData);
+	sm->eapRespData = NULL;
+	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 eapRespData=%p",
+		   ret.ignore ? "TRUE" : "FALSE",
+		   eap_sm_method_state_txt(ret.methodState),
+		   eap_sm_decision_txt(ret.decision),
+		   sm->eapRespData);
+
+	sm->ignore = ret.ignore;
+	if (sm->ignore)
+		return;
+	sm->methodState = ret.methodState;
+	sm->decision = ret.decision;
+	sm->allowNotifications = ret.allowNotifications;
+
+	if (sm->m->isKeyAvailable && sm->m->getKey &&
+	    sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
+		struct eap_peer_config *config = eap_get_config(sm);
+
+		eap_sm_free_key(sm);
+		sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
+					       &sm->eapKeyDataLen);
+		os_free(sm->eapSessionId);
+		sm->eapSessionId = NULL;
+		if (sm->m->getSessionId) {
+			sm->eapSessionId = sm->m->getSessionId(
+				sm, sm->eap_method_priv,
+				&sm->eapSessionIdLen);
+			wpa_hexdump(MSG_DEBUG, "EAP: Session-Id",
+				    sm->eapSessionId, sm->eapSessionIdLen);
+		}
+		if (config->erp && sm->m->get_emsk && sm->eapSessionId)
+			eap_peer_erp_init(sm);
+	}
+}
+
+
+/*
+ * This state signals the lower layer that a response packet is ready to be
+ * sent.
+ */
+SM_STATE(EAP, SEND_RESPONSE)
+{
+	SM_ENTRY(EAP, SEND_RESPONSE);
+	wpabuf_free(sm->lastRespData);
+	if (sm->eapRespData) {
+		if (sm->workaround)
+			os_memcpy(sm->last_sha1, sm->req_sha1, 20);
+		sm->lastId = sm->reqId;
+		sm->lastRespData = wpabuf_dup(sm->eapRespData);
+		eapol_set_bool(sm, EAPOL_eapResp, TRUE);
+	} 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);
+	sm->reauthInit = FALSE;
+}
+
+
+/*
+ * This state signals the lower layer that the request was discarded, and no
+ * response packet will be sent at this time.
+ */
+SM_STATE(EAP, DISCARD)
+{
+	SM_ENTRY(EAP, DISCARD);
+	eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+	eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+}
+
+
+/*
+ * Handles requests for Identity method and builds a response.
+ */
+SM_STATE(EAP, IDENTITY)
+{
+	const struct wpabuf *eapReqData;
+
+	SM_ENTRY(EAP, IDENTITY);
+	eapReqData = eapol_get_eapReqData(sm);
+	if (!eap_hdr_len_valid(eapReqData, 1))
+		return;
+	eap_sm_processIdentity(sm, eapReqData);
+	wpabuf_free(sm->eapRespData);
+	sm->eapRespData = NULL;
+	sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, 0);
+}
+
+
+/*
+ * Handles requests for Notification method and builds a response.
+ */
+SM_STATE(EAP, NOTIFICATION)
+{
+	const struct wpabuf *eapReqData;
+
+	SM_ENTRY(EAP, NOTIFICATION);
+	eapReqData = eapol_get_eapReqData(sm);
+	if (!eap_hdr_len_valid(eapReqData, 1))
+		return;
+	eap_sm_processNotify(sm, eapReqData);
+	wpabuf_free(sm->eapRespData);
+	sm->eapRespData = NULL;
+	sm->eapRespData = eap_sm_buildNotify(sm->reqId);
+}
+
+
+/*
+ * This state retransmits the previous response packet.
+ */
+SM_STATE(EAP, RETRANSMIT)
+{
+	SM_ENTRY(EAP, RETRANSMIT);
+	wpabuf_free(sm->eapRespData);
+	if (sm->lastRespData)
+		sm->eapRespData = wpabuf_dup(sm->lastRespData);
+	else
+		sm->eapRespData = NULL;
+}
+
+
+/*
+ * This state is entered in case of a successful completion of authentication
+ * and state machine waits here until port is disabled or EAP authentication is
+ * restarted.
+ */
+SM_STATE(EAP, SUCCESS)
+{
+	SM_ENTRY(EAP, SUCCESS);
+	if (sm->eapKeyData != NULL)
+		sm->eapKeyAvailable = TRUE;
+	eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+
+	/*
+	 * RFC 4137 does not clear eapReq here, but this seems to be required
+	 * to avoid processing the same request twice when state machine is
+	 * initialized.
+	 */
+	eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+
+	/*
+	 * RFC 4137 does not set eapNoResp here, but this seems to be required
+	 * to get EAPOL Supplicant backend state machine into SUCCESS state. In
+	 * addition, either eapResp or eapNoResp is required to be set after
+	 * processing the received EAP frame.
+	 */
+	eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+
+	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+		"EAP authentication completed successfully");
+}
+
+
+/*
+ * This state is entered in case of a failure and state machine waits here
+ * until port is disabled or EAP authentication is restarted.
+ */
+SM_STATE(EAP, FAILURE)
+{
+	SM_ENTRY(EAP, FAILURE);
+	eapol_set_bool(sm, EAPOL_eapFail, TRUE);
+
+	/*
+	 * RFC 4137 does not clear eapReq here, but this seems to be required
+	 * to avoid processing the same request twice when state machine is
+	 * initialized.
+	 */
+	eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+
+	/*
+	 * RFC 4137 does not set eapNoResp here. However, either eapResp or
+	 * eapNoResp is required to be set after processing the received EAP
+	 * frame.
+	 */
+	eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+
+	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+		"EAP authentication failed");
+
+	sm->prev_failure = 1;
+}
+
+
+static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId)
+{
+	/*
+	 * At least Microsoft IAS and Meetinghouse Aegis seem to be sending
+	 * EAP-Success/Failure with lastId + 1 even though RFC 3748 and
+	 * RFC 4137 require that reqId == lastId. In addition, it looks like
+	 * Ringmaster v2.1.2.0 would be using lastId + 2 in EAP-Success.
+	 *
+	 * Accept this kind of Id if EAP workarounds are enabled. These are
+	 * unauthenticated plaintext messages, so this should have minimal
+	 * security implications (bit easier to fake EAP-Success/Failure).
+	 */
+	if (sm->workaround && (reqId == ((lastId + 1) & 0xff) ||
+			       reqId == ((lastId + 2) & 0xff))) {
+		wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected "
+			   "identifier field in EAP Success: "
+			   "reqId=%d lastId=%d (these are supposed to be "
+			   "same)", reqId, lastId);
+		return 1;
+	}
+	wpa_printf(MSG_DEBUG, "EAP: EAP-Success Id mismatch - reqId=%d "
+		   "lastId=%d", reqId, lastId);
+	return 0;
+}
+
+
+/*
+ * RFC 4137 - Appendix A.1: EAP Peer State Machine - State transitions
+ */
+
+static void eap_peer_sm_step_idle(struct eap_sm *sm)
+{
+	/*
+	 * The first three transitions are from RFC 4137. The last two are
+	 * local additions to handle special cases with LEAP and PEAP server
+	 * not sending EAP-Success in some cases.
+	 */
+	if (eapol_get_bool(sm, EAPOL_eapReq))
+		SM_ENTER(EAP, RECEIVED);
+	else if ((eapol_get_bool(sm, EAPOL_altAccept) &&
+		  sm->decision != DECISION_FAIL) ||
+		 (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
+		  sm->decision == DECISION_UNCOND_SUCC))
+		SM_ENTER(EAP, SUCCESS);
+	else if (eapol_get_bool(sm, EAPOL_altReject) ||
+		 (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
+		  sm->decision != DECISION_UNCOND_SUCC) ||
+		 (eapol_get_bool(sm, EAPOL_altAccept) &&
+		  sm->methodState != METHOD_CONT &&
+		  sm->decision == DECISION_FAIL))
+		SM_ENTER(EAP, FAILURE);
+	else if (sm->selectedMethod == EAP_TYPE_LEAP &&
+		 sm->leap_done && sm->decision != DECISION_FAIL &&
+		 sm->methodState == METHOD_DONE)
+		SM_ENTER(EAP, SUCCESS);
+	else if (sm->selectedMethod == EAP_TYPE_PEAP &&
+		 sm->peap_done && sm->decision != DECISION_FAIL &&
+		 sm->methodState == METHOD_DONE)
+		SM_ENTER(EAP, SUCCESS);
+}
+
+
+static int eap_peer_req_is_duplicate(struct eap_sm *sm)
+{
+	int duplicate;
+
+	duplicate = (sm->reqId == sm->lastId) && sm->rxReq;
+	if (sm->workaround && duplicate &&
+	    os_memcmp(sm->req_sha1, sm->last_sha1, 20) != 0) {
+		/*
+		 * RFC 4137 uses (reqId == lastId) as the only verification for
+		 * duplicate EAP requests. However, this misses cases where the
+		 * AS is incorrectly using the same id again; and
+		 * unfortunately, such implementations exist. Use SHA1 hash as
+		 * an extra verification for the packets being duplicate to
+		 * workaround these issues.
+		 */
+		wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again, but "
+			   "EAP packets were not identical");
+		wpa_printf(MSG_DEBUG, "EAP: workaround - assume this is not a "
+			   "duplicate packet");
+		duplicate = 0;
+	}
+
+	return duplicate;
+}
+
+
+static int eap_peer_sm_allow_canned(struct eap_sm *sm)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+
+	return config && config->phase1 &&
+		os_strstr(config->phase1, "allow_canned_success=1");
+}
+
+
+static void eap_peer_sm_step_received(struct eap_sm *sm)
+{
+	int duplicate = eap_peer_req_is_duplicate(sm);
+
+	/*
+	 * Two special cases below for LEAP are local additions to work around
+	 * odd LEAP behavior (EAP-Success in the middle of authentication and
+	 * then swapped roles). Other transitions are based on RFC 4137.
+	 */
+	if (sm->rxSuccess && sm->decision != DECISION_FAIL &&
+	    (sm->reqId == sm->lastId ||
+	     eap_success_workaround(sm, sm->reqId, sm->lastId)))
+		SM_ENTER(EAP, SUCCESS);
+	else if (sm->workaround && sm->lastId == -1 && sm->rxSuccess &&
+		 !sm->rxFailure && !sm->rxReq && eap_peer_sm_allow_canned(sm))
+		SM_ENTER(EAP, SUCCESS); /* EAP-Success prior any EAP method */
+	else if (sm->workaround && sm->lastId == -1 && sm->rxFailure &&
+		 !sm->rxReq && sm->methodState != METHOD_CONT &&
+		 eap_peer_sm_allow_canned(sm))
+		SM_ENTER(EAP, FAILURE); /* EAP-Failure prior any EAP method */
+	else if (sm->workaround && sm->rxSuccess && !sm->rxFailure &&
+		 !sm->rxReq && sm->methodState != METHOD_CONT &&
+		 eap_peer_sm_allow_canned(sm))
+		SM_ENTER(EAP, SUCCESS); /* EAP-Success after Identity */
+	else if (sm->methodState != METHOD_CONT &&
+		 ((sm->rxFailure &&
+		   sm->decision != DECISION_UNCOND_SUCC) ||
+		  (sm->rxSuccess && sm->decision == DECISION_FAIL &&
+		   (sm->selectedMethod != EAP_TYPE_LEAP ||
+		    sm->methodState != METHOD_MAY_CONT))) &&
+		 (sm->reqId == sm->lastId ||
+		  eap_success_workaround(sm, sm->reqId, sm->lastId)))
+		SM_ENTER(EAP, FAILURE);
+	else if (sm->rxReq && duplicate)
+		SM_ENTER(EAP, RETRANSMIT);
+	else if (sm->rxReq && !duplicate &&
+		 sm->reqMethod == EAP_TYPE_NOTIFICATION &&
+		 sm->allowNotifications)
+		SM_ENTER(EAP, NOTIFICATION);
+	else if (sm->rxReq && !duplicate &&
+		 sm->selectedMethod == EAP_TYPE_NONE &&
+		 sm->reqMethod == EAP_TYPE_IDENTITY)
+		SM_ENTER(EAP, IDENTITY);
+	else if (sm->rxReq && !duplicate &&
+		 sm->selectedMethod == EAP_TYPE_NONE &&
+		 sm->reqMethod != EAP_TYPE_IDENTITY &&
+		 sm->reqMethod != EAP_TYPE_NOTIFICATION)
+		SM_ENTER(EAP, GET_METHOD);
+	else if (sm->rxReq && !duplicate &&
+		 sm->reqMethod == sm->selectedMethod &&
+		 sm->methodState != METHOD_DONE)
+		SM_ENTER(EAP, METHOD);
+	else if (sm->selectedMethod == EAP_TYPE_LEAP &&
+		 (sm->rxSuccess || sm->rxResp))
+		SM_ENTER(EAP, METHOD);
+	else if (sm->reauthInit)
+		SM_ENTER(EAP, SEND_RESPONSE);
+	else
+		SM_ENTER(EAP, DISCARD);
+}
+
+
+static void eap_peer_sm_step_local(struct eap_sm *sm)
+{
+	switch (sm->EAP_state) {
+	case EAP_INITIALIZE:
+		SM_ENTER(EAP, IDLE);
+		break;
+	case EAP_DISABLED:
+		if (eapol_get_bool(sm, EAPOL_portEnabled) &&
+		    !sm->force_disabled)
+			SM_ENTER(EAP, INITIALIZE);
+		break;
+	case EAP_IDLE:
+		eap_peer_sm_step_idle(sm);
+		break;
+	case EAP_RECEIVED:
+		eap_peer_sm_step_received(sm);
+		break;
+	case EAP_GET_METHOD:
+		if (sm->selectedMethod == sm->reqMethod)
+			SM_ENTER(EAP, METHOD);
+		else
+			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;
+	case EAP_SEND_RESPONSE:
+		SM_ENTER(EAP, IDLE);
+		break;
+	case EAP_DISCARD:
+		SM_ENTER(EAP, IDLE);
+		break;
+	case EAP_IDENTITY:
+		SM_ENTER(EAP, SEND_RESPONSE);
+		break;
+	case EAP_NOTIFICATION:
+		SM_ENTER(EAP, SEND_RESPONSE);
+		break;
+	case EAP_RETRANSMIT:
+		SM_ENTER(EAP, SEND_RESPONSE);
+		break;
+	case EAP_SUCCESS:
+		break;
+	case EAP_FAILURE:
+		break;
+	}
+}
+
+
+SM_STEP(EAP)
+{
+	/* Global transitions */
+	if (eapol_get_bool(sm, EAPOL_eapRestart) &&
+	    eapol_get_bool(sm, EAPOL_portEnabled))
+		SM_ENTER_GLOBAL(EAP, INITIALIZE);
+	else if (!eapol_get_bool(sm, EAPOL_portEnabled) || sm->force_disabled)
+		SM_ENTER_GLOBAL(EAP, DISABLED);
+	else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
+		/* RFC 4137 does not place any limit on number of EAP messages
+		 * in an authentication session. However, some error cases have
+		 * ended up in a state were EAP messages were sent between the
+		 * peer and server in a loop (e.g., TLS ACK frame in both
+		 * direction). Since this is quite undesired outcome, limit the
+		 * total number of EAP round-trips and abort authentication if
+		 * this limit is exceeded.
+		 */
+		if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
+			wpa_msg(sm->msg_ctx, MSG_INFO, "EAP: more than %d "
+				"authentication rounds - abort",
+				EAP_MAX_AUTH_ROUNDS);
+			sm->num_rounds++;
+			SM_ENTER_GLOBAL(EAP, FAILURE);
+		}
+	} else {
+		/* Local transitions */
+		eap_peer_sm_step_local(sm);
+	}
+}
+
+
+static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
+				  EapType method)
+{
+	if (!eap_allowed_method(sm, vendor, method)) {
+		wpa_printf(MSG_DEBUG, "EAP: configuration does not allow: "
+			   "vendor %u method %u", vendor, method);
+		return FALSE;
+	}
+	if (eap_peer_get_eap_method(vendor, method))
+		return TRUE;
+	wpa_printf(MSG_DEBUG, "EAP: not included in build: "
+		   "vendor %u method %u", vendor, method);
+	return FALSE;
+}
+
+
+static struct wpabuf * eap_sm_build_expanded_nak(
+	struct eap_sm *sm, int id, const struct eap_method *methods,
+	size_t count)
+{
+	struct wpabuf *resp;
+	int found = 0;
+	const struct eap_method *m;
+
+	wpa_printf(MSG_DEBUG, "EAP: Building expanded EAP-Nak");
+
+	/* RFC 3748 - 5.3.2: Expanded Nak */
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED,
+			     8 + 8 * (count + 1), EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+
+	wpabuf_put_be24(resp, EAP_VENDOR_IETF);
+	wpabuf_put_be32(resp, EAP_TYPE_NAK);
+
+	for (m = methods; m; m = m->next) {
+		if (sm->reqVendor == m->vendor &&
+		    sm->reqVendorMethod == m->method)
+			continue; /* do not allow the current method again */
+		if (eap_allowed_method(sm, m->vendor, m->method)) {
+			wpa_printf(MSG_DEBUG, "EAP: allowed type: "
+				   "vendor=%u method=%u",
+				   m->vendor, m->method);
+			wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+			wpabuf_put_be24(resp, m->vendor);
+			wpabuf_put_be32(resp, m->method);
+
+			found++;
+		}
+	}
+	if (!found) {
+		wpa_printf(MSG_DEBUG, "EAP: no more allowed methods");
+		wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+		wpabuf_put_be24(resp, EAP_VENDOR_IETF);
+		wpabuf_put_be32(resp, EAP_TYPE_NONE);
+	}
+
+	eap_update_len(resp);
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id)
+{
+	struct wpabuf *resp;
+	u8 *start;
+	int found = 0, expanded_found = 0;
+	size_t count;
+	const struct eap_method *methods, *m;
+
+	wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %u "
+		   "vendor=%u method=%u not allowed)", sm->reqMethod,
+		   sm->reqVendor, sm->reqVendorMethod);
+	methods = eap_peer_get_methods(&count);
+	if (methods == NULL)
+		return NULL;
+	if (sm->reqMethod == EAP_TYPE_EXPANDED)
+		return eap_sm_build_expanded_nak(sm, id, methods, count);
+
+	/* RFC 3748 - 5.3.1: Legacy Nak */
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK,
+			     sizeof(struct eap_hdr) + 1 + count + 1,
+			     EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+
+	start = wpabuf_put(resp, 0);
+	for (m = methods; m; m = m->next) {
+		if (m->vendor == EAP_VENDOR_IETF && m->method == sm->reqMethod)
+			continue; /* do not allow the current method again */
+		if (eap_allowed_method(sm, m->vendor, m->method)) {
+			if (m->vendor != EAP_VENDOR_IETF) {
+				if (expanded_found)
+					continue;
+				expanded_found = 1;
+				wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+			} else
+				wpabuf_put_u8(resp, m->method);
+			found++;
+		}
+	}
+	if (!found)
+		wpabuf_put_u8(resp, EAP_TYPE_NONE);
+	wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", start, found);
+
+	eap_update_len(resp);
+
+	return resp;
+}
+
+
+static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req)
+{
+	const u8 *pos;
+	size_t msg_len;
+
+	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
+		"EAP authentication started");
+	eap_notify_status(sm, "started", "");
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req,
+			       &msg_len);
+	if (pos == NULL)
+		return;
+
+	/*
+	 * RFC 3748 - 5.1: Identity
+	 * Data field may contain a displayable message in UTF-8. If this
+	 * includes NUL-character, only the data before that should be
+	 * displayed. Some EAP implementasitons may piggy-back additional
+	 * options after the NUL.
+	 */
+	/* TODO: could save displayable message so that it can be shown to the
+	 * user in case of interaction is required */
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data",
+			  pos, msg_len);
+}
+
+
+#ifdef PCSC_FUNCS
+
+/*
+ * Rules for figuring out MNC length based on IMSI for SIM cards that do not
+ * include MNC length field.
+ */
+static int mnc_len_from_imsi(const char *imsi)
+{
+	char mcc_str[4];
+	unsigned int mcc;
+
+	os_memcpy(mcc_str, imsi, 3);
+	mcc_str[3] = '\0';
+	mcc = atoi(mcc_str);
+
+	if (mcc == 228)
+		return 2; /* Networks in Switzerland use 2-digit MNC */
+	if (mcc == 244)
+		return 2; /* Networks in Finland use 2-digit MNC */
+
+	return -1;
+}
+
+
+static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi,
+				    size_t max_len, size_t *imsi_len)
+{
+	int mnc_len;
+	char *pos, mnc[4];
+
+	if (*imsi_len + 36 > max_len) {
+		wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer");
+		return -1;
+	}
+
+	/* MNC (2 or 3 digits) */
+	mnc_len = scard_get_mnc_len(sm->scard_ctx);
+	if (mnc_len < 0)
+		mnc_len = mnc_len_from_imsi(imsi);
+	if (mnc_len < 0) {
+		wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM "
+			   "assuming 3");
+		mnc_len = 3;
+	}
+
+	if (mnc_len == 2) {
+		mnc[0] = '0';
+		mnc[1] = imsi[3];
+		mnc[2] = imsi[4];
+	} else if (mnc_len == 3) {
+		mnc[0] = imsi[3];
+		mnc[1] = imsi[4];
+		mnc[2] = imsi[5];
+	}
+	mnc[3] = '\0';
+
+	pos = imsi + *imsi_len;
+	pos += os_snprintf(pos, imsi + max_len - pos,
+			   "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org",
+			   mnc, imsi[0], imsi[1], imsi[2]);
+	*imsi_len = pos - imsi;
+
+	return 0;
+}
+
+
+static int eap_sm_imsi_identity(struct eap_sm *sm,
+				struct eap_peer_config *conf)
+{
+	enum { EAP_SM_SIM, EAP_SM_AKA, EAP_SM_AKA_PRIME } method = EAP_SM_SIM;
+	char imsi[100];
+	size_t imsi_len;
+	struct eap_method_type *m = conf->eap_methods;
+	int i;
+
+	imsi_len = sizeof(imsi);
+	if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) {
+		wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM");
+		return -1;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len);
+
+	if (imsi_len < 7) {
+		wpa_printf(MSG_WARNING, "Too short IMSI for SIM identity");
+		return -1;
+	}
+
+	if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) {
+		wpa_printf(MSG_WARNING, "Could not add realm to SIM identity");
+		return -1;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "IMSI + realm", (u8 *) imsi, imsi_len);
+
+	for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF ||
+			  m[i].method != EAP_TYPE_NONE); i++) {
+		if (m[i].vendor == EAP_VENDOR_IETF &&
+		    m[i].method == EAP_TYPE_AKA_PRIME) {
+			method = EAP_SM_AKA_PRIME;
+			break;
+		}
+
+		if (m[i].vendor == EAP_VENDOR_IETF &&
+		    m[i].method == EAP_TYPE_AKA) {
+			method = EAP_SM_AKA;
+			break;
+		}
+	}
+
+	os_free(conf->identity);
+	conf->identity = os_malloc(1 + imsi_len);
+	if (conf->identity == NULL) {
+		wpa_printf(MSG_WARNING, "Failed to allocate buffer for "
+			   "IMSI-based identity");
+		return -1;
+	}
+
+	switch (method) {
+	case EAP_SM_SIM:
+		conf->identity[0] = '1';
+		break;
+	case EAP_SM_AKA:
+		conf->identity[0] = '0';
+		break;
+	case EAP_SM_AKA_PRIME:
+		conf->identity[0] = '6';
+		break;
+	}
+	os_memcpy(conf->identity + 1, imsi, imsi_len);
+	conf->identity_len = 1 + imsi_len;
+
+	return 0;
+}
+
+#endif /* PCSC_FUNCS */
+
+
+static int eap_sm_set_scard_pin(struct eap_sm *sm,
+				struct eap_peer_config *conf)
+{
+#ifdef PCSC_FUNCS
+	if (scard_set_pin(sm->scard_ctx, conf->pin)) {
+		/*
+		 * Make sure the same PIN is not tried again in order to avoid
+		 * blocking SIM.
+		 */
+		os_free(conf->pin);
+		conf->pin = NULL;
+
+		wpa_printf(MSG_WARNING, "PIN validation failed");
+		eap_sm_request_pin(sm);
+		return -1;
+	}
+	return 0;
+#else /* PCSC_FUNCS */
+	return -1;
+#endif /* PCSC_FUNCS */
+}
+
+static int eap_sm_get_scard_identity(struct eap_sm *sm,
+				     struct eap_peer_config *conf)
+{
+#ifdef PCSC_FUNCS
+	if (eap_sm_set_scard_pin(sm, conf))
+		return -1;
+
+	return eap_sm_imsi_identity(sm, conf);
+#else /* PCSC_FUNCS */
+	return -1;
+#endif /* PCSC_FUNCS */
+}
+
+
+/**
+ * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @id: EAP identifier for the packet
+ * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2)
+ * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on
+ * failure
+ *
+ * This function allocates and builds an EAP-Identity/Response packet for the
+ * current network. The caller is responsible for freeing the returned data.
+ */
+struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	struct wpabuf *resp;
+	const u8 *identity;
+	size_t identity_len;
+
+	if (config == NULL) {
+		wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration "
+			   "was not available");
+		return NULL;
+	}
+
+	if (sm->m && sm->m->get_identity &&
+	    (identity = sm->m->get_identity(sm, sm->eap_method_priv,
+					    &identity_len)) != NULL) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth "
+				  "identity", identity, identity_len);
+	} else if (!encrypted && config->anonymous_identity) {
+		identity = config->anonymous_identity;
+		identity_len = config->anonymous_identity_len;
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity",
+				  identity, identity_len);
+	} else {
+		identity = config->identity;
+		identity_len = config->identity_len;
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity",
+				  identity, identity_len);
+	}
+
+	if (identity == NULL) {
+		wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity "
+			   "configuration was not available");
+		if (config->pcsc) {
+			if (eap_sm_get_scard_identity(sm, config) < 0)
+				return NULL;
+			identity = config->identity;
+			identity_len = config->identity_len;
+			wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from "
+					  "IMSI", identity, identity_len);
+		} else {
+			eap_sm_request_identity(sm);
+			return NULL;
+		}
+	} else if (config->pcsc) {
+		if (eap_sm_set_scard_pin(sm, config) < 0)
+			return NULL;
+	}
+
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len,
+			     EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+
+	wpabuf_put_data(resp, identity, identity_len);
+
+	return resp;
+}
+
+
+static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req)
+{
+	const u8 *pos;
+	char *msg;
+	size_t i, msg_len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, req,
+			       &msg_len);
+	if (pos == NULL)
+		return;
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data",
+			  pos, msg_len);
+
+	msg = os_malloc(msg_len + 1);
+	if (msg == NULL)
+		return;
+	for (i = 0; i < msg_len; i++)
+		msg[i] = isprint(pos[i]) ? (char) pos[i] : '_';
+	msg[msg_len] = '\0';
+	wpa_msg(sm->msg_ctx, MSG_INFO, "%s%s",
+		WPA_EVENT_EAP_NOTIFICATION, msg);
+	os_free(msg);
+}
+
+
+static struct wpabuf * eap_sm_buildNotify(int id)
+{
+	struct wpabuf *resp;
+
+	wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification");
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0,
+			     EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+
+	return resp;
+}
+
+
+static void eap_peer_initiate(struct eap_sm *sm, const struct eap_hdr *hdr,
+			      size_t len)
+{
+#ifdef CONFIG_ERP
+	const u8 *pos = (const u8 *) (hdr + 1);
+	const u8 *end = ((const u8 *) hdr) + len;
+	struct erp_tlvs parse;
+
+	if (len < sizeof(*hdr) + 1) {
+		wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Initiate");
+		return;
+	}
+
+	if (*pos != EAP_ERP_TYPE_REAUTH_START) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Ignored unexpected EAP-Initiate Type=%u",
+			   *pos);
+		return;
+	}
+
+	pos++;
+	if (pos >= end) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Too short EAP-Initiate/Re-auth-Start");
+		return;
+	}
+	pos++; /* Reserved */
+	wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth-Start TVs/TLVs",
+		    pos, end - pos);
+
+	if (erp_parse_tlvs(pos, end, &parse, 0) < 0)
+		goto invalid;
+
+	if (parse.domain) {
+		wpa_hexdump_ascii(MSG_DEBUG,
+				  "EAP: EAP-Initiate/Re-auth-Start - Domain name",
+				  parse.domain, parse.domain_len);
+		/* TODO: Derivation of domain specific keys for local ER */
+	}
+
+	if (eap_peer_erp_reauth_start(sm, hdr, len) == 0)
+		return;
+
+invalid:
+#endif /* CONFIG_ERP */
+	wpa_printf(MSG_DEBUG,
+		   "EAP: EAP-Initiate/Re-auth-Start - No suitable ERP keys available - try to start full EAP authentication");
+	eapol_set_bool(sm, EAPOL_eapTriggerStart, TRUE);
+}
+
+
+static void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr,
+			    size_t len)
+{
+#ifdef CONFIG_ERP
+	const u8 *pos = (const u8 *) (hdr + 1);
+	const u8 *end = ((const u8 *) hdr) + len;
+	const u8 *start;
+	struct erp_tlvs parse;
+	u8 flags;
+	u16 seq;
+	u8 hash[SHA256_MAC_LEN];
+	size_t hash_len;
+	struct eap_erp_key *erp;
+	int max_len;
+	char nai[254];
+	u8 seed[4];
+	int auth_tag_ok = 0;
+
+	if (len < sizeof(*hdr) + 1) {
+		wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Finish");
+		return;
+	}
+
+	if (*pos != EAP_ERP_TYPE_REAUTH) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Ignored unexpected EAP-Finish Type=%u", *pos);
+		return;
+	}
+
+	if (len < sizeof(*hdr) + 4) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Ignored too short EAP-Finish/Re-auth");
+		return;
+	}
+
+	pos++;
+	flags = *pos++;
+	seq = WPA_GET_BE16(pos);
+	pos += 2;
+	wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq);
+
+	if (seq != sm->erp_seq) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Unexpected EAP-Finish/Re-auth SEQ=%u", seq);
+		return;
+	}
+
+	/*
+	 * Parse TVs/TLVs. Since we do not yet know the length of the
+	 * Authentication Tag, stop parsing if an unknown TV/TLV is seen and
+	 * just try to find the keyName-NAI first so that we can check the
+	 * Authentication Tag.
+	 */
+	if (erp_parse_tlvs(pos, end, &parse, 1) < 0)
+		return;
+
+	if (!parse.keyname) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: No keyName-NAI in EAP-Finish/Re-auth Packet");
+		return;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Finish/Re-auth - keyName-NAI",
+			  parse.keyname, parse.keyname_len);
+	if (parse.keyname_len > 253) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Too long keyName-NAI in EAP-Finish/Re-auth");
+		return;
+	}
+	os_memcpy(nai, parse.keyname, parse.keyname_len);
+	nai[parse.keyname_len] = '\0';
+
+	erp = eap_erp_get_key_nai(sm, nai);
+	if (!erp) {
+		wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s",
+			   nai);
+		return;
+	}
+
+	/* Is there enough room for Cryptosuite and Authentication Tag? */
+	start = parse.keyname + parse.keyname_len;
+	max_len = end - start;
+	hash_len = 16;
+	if (max_len < 1 + (int) hash_len) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Not enough room for Authentication Tag");
+		if (flags & 0x80)
+			goto no_auth_tag;
+		return;
+	}
+	if (end[-17] != EAP_ERP_CS_HMAC_SHA256_128) {
+		wpa_printf(MSG_DEBUG, "EAP: Different Cryptosuite used");
+		if (flags & 0x80)
+			goto no_auth_tag;
+		return;
+	}
+
+	if (hmac_sha256(erp->rIK, erp->rIK_len, (const u8 *) hdr,
+			end - ((const u8 *) hdr) - hash_len, hash) < 0)
+		return;
+	if (os_memcmp(end - hash_len, hash, hash_len) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Authentication Tag mismatch");
+		return;
+	}
+	auth_tag_ok = 1;
+	end -= 1 + hash_len;
+
+no_auth_tag:
+	/*
+	 * Parse TVs/TLVs again now that we know the exact part of the buffer
+	 * that contains them.
+	 */
+	wpa_hexdump(MSG_DEBUG, "EAP: EAP-Finish/Re-Auth TVs/TLVs",
+		    pos, end - pos);
+	if (erp_parse_tlvs(pos, end, &parse, 0) < 0)
+		return;
+
+	if (flags & 0x80 || !auth_tag_ok) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: EAP-Finish/Re-auth indicated failure");
+		eapol_set_bool(sm, EAPOL_eapFail, TRUE);
+		eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+		eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+			"EAP authentication failed");
+		sm->prev_failure = 1;
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Drop ERP key to try full authentication on next attempt");
+		eap_peer_erp_free_key(erp);
+		return;
+	}
+
+	eap_sm_free_key(sm);
+	sm->eapKeyDataLen = 0;
+	sm->eapKeyData = os_malloc(erp->rRK_len);
+	if (!sm->eapKeyData)
+		return;
+	sm->eapKeyDataLen = erp->rRK_len;
+
+	WPA_PUT_BE16(seed, seq);
+	WPA_PUT_BE16(&seed[2], erp->rRK_len);
+	if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+			    "Re-authentication Master Session Key@ietf.org",
+			    seed, sizeof(seed),
+			    sm->eapKeyData, erp->rRK_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP");
+		eap_sm_free_key(sm);
+		return;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK",
+			sm->eapKeyData, sm->eapKeyDataLen);
+	sm->eapKeyAvailable = TRUE;
+	eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+	eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+	eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+		"EAP re-authentication completed successfully");
+#endif /* CONFIG_ERP */
+}
+
+
+static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req)
+{
+	const struct eap_hdr *hdr;
+	size_t plen;
+	const u8 *pos;
+
+	sm->rxReq = sm->rxResp = sm->rxSuccess = sm->rxFailure = FALSE;
+	sm->reqId = 0;
+	sm->reqMethod = EAP_TYPE_NONE;
+	sm->reqVendor = EAP_VENDOR_IETF;
+	sm->reqVendorMethod = EAP_TYPE_NONE;
+
+	if (req == NULL || wpabuf_len(req) < sizeof(*hdr))
+		return;
+
+	hdr = wpabuf_head(req);
+	plen = be_to_host16(hdr->length);
+	if (plen > wpabuf_len(req)) {
+		wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
+			   "(len=%lu plen=%lu)",
+			   (unsigned long) wpabuf_len(req),
+			   (unsigned long) plen);
+		return;
+	}
+
+	sm->reqId = hdr->identifier;
+
+	if (sm->workaround) {
+		const u8 *addr[1];
+		addr[0] = wpabuf_head(req);
+		sha1_vector(1, addr, &plen, sm->req_sha1);
+	}
+
+	switch (hdr->code) {
+	case EAP_CODE_REQUEST:
+		if (plen < sizeof(*hdr) + 1) {
+			wpa_printf(MSG_DEBUG, "EAP: Too short EAP-Request - "
+				   "no Type field");
+			return;
+		}
+		sm->rxReq = TRUE;
+		pos = (const u8 *) (hdr + 1);
+		sm->reqMethod = *pos++;
+		if (sm->reqMethod == EAP_TYPE_EXPANDED) {
+			if (plen < sizeof(*hdr) + 8) {
+				wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
+					   "expanded EAP-Packet (plen=%lu)",
+					   (unsigned long) plen);
+				return;
+			}
+			sm->reqVendor = WPA_GET_BE24(pos);
+			pos += 3;
+			sm->reqVendorMethod = WPA_GET_BE32(pos);
+		}
+		wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request id=%d "
+			   "method=%u vendor=%u vendorMethod=%u",
+			   sm->reqId, sm->reqMethod, sm->reqVendor,
+			   sm->reqVendorMethod);
+		break;
+	case EAP_CODE_RESPONSE:
+		if (sm->selectedMethod == EAP_TYPE_LEAP) {
+			/*
+			 * LEAP differs from RFC 4137 by using reversed roles
+			 * for mutual authentication and because of this, we
+			 * need to accept EAP-Response frames if LEAP is used.
+			 */
+			if (plen < sizeof(*hdr) + 1) {
+				wpa_printf(MSG_DEBUG, "EAP: Too short "
+					   "EAP-Response - no Type field");
+				return;
+			}
+			sm->rxResp = TRUE;
+			pos = (const u8 *) (hdr + 1);
+			sm->reqMethod = *pos;
+			wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for "
+				   "LEAP method=%d id=%d",
+				   sm->reqMethod, sm->reqId);
+			break;
+		}
+		wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response");
+		break;
+	case EAP_CODE_SUCCESS:
+		wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success");
+		eap_notify_status(sm, "completion", "success");
+		sm->rxSuccess = TRUE;
+		break;
+	case EAP_CODE_FAILURE:
+		wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure");
+		eap_notify_status(sm, "completion", "failure");
+		sm->rxFailure = TRUE;
+		break;
+	case EAP_CODE_INITIATE:
+		eap_peer_initiate(sm, hdr, plen);
+		break;
+	case EAP_CODE_FINISH:
+		eap_peer_finish(sm, hdr, plen);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown "
+			   "code %d", hdr->code);
+		break;
+	}
+}
+
+
+static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev,
+				  union tls_event_data *data)
+{
+	struct eap_sm *sm = ctx;
+	char *hash_hex = NULL;
+
+	switch (ev) {
+	case TLS_CERT_CHAIN_SUCCESS:
+		eap_notify_status(sm, "remote certificate verification",
+				  "success");
+		break;
+	case TLS_CERT_CHAIN_FAILURE:
+		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR
+			"reason=%d depth=%d subject='%s' err='%s'",
+			data->cert_fail.reason,
+			data->cert_fail.depth,
+			data->cert_fail.subject,
+			data->cert_fail.reason_txt);
+		eap_notify_status(sm, "remote certificate verification",
+				  data->cert_fail.reason_txt);
+		break;
+	case TLS_PEER_CERTIFICATE:
+		if (!sm->eapol_cb->notify_cert)
+			break;
+
+		if (data->peer_cert.hash) {
+			size_t len = data->peer_cert.hash_len * 2 + 1;
+			hash_hex = os_malloc(len);
+			if (hash_hex) {
+				wpa_snprintf_hex(hash_hex, len,
+						 data->peer_cert.hash,
+						 data->peer_cert.hash_len);
+			}
+		}
+
+		sm->eapol_cb->notify_cert(sm->eapol_ctx,
+					  data->peer_cert.depth,
+					  data->peer_cert.subject,
+					  data->peer_cert.altsubject,
+					  data->peer_cert.num_altsubject,
+					  hash_hex, data->peer_cert.cert);
+		break;
+	case TLS_ALERT:
+		if (data->alert.is_local)
+			eap_notify_status(sm, "local TLS alert",
+					  data->alert.description);
+		else
+			eap_notify_status(sm, "remote TLS alert",
+					  data->alert.description);
+		break;
+	}
+
+	os_free(hash_hex);
+}
+
+
+/**
+ * eap_peer_sm_init - Allocate and initialize EAP peer state machine
+ * @eapol_ctx: Context data to be used with eapol_cb calls
+ * @eapol_cb: Pointer to EAPOL callback functions
+ * @msg_ctx: Context data for wpa_msg() calls
+ * @conf: EAP configuration
+ * Returns: Pointer to the allocated EAP state machine or %NULL on failure
+ *
+ * This function allocates and initializes an EAP state machine. In addition,
+ * this initializes TLS library for the new EAP state machine. eapol_cb pointer
+ * will be in use until eap_peer_sm_deinit() is used to deinitialize this EAP
+ * state machine. Consequently, the caller must make sure that this data
+ * structure remains alive while the EAP state machine is active.
+ */
+struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
+				 const struct eapol_callbacks *eapol_cb,
+				 void *msg_ctx, struct eap_config *conf)
+{
+	struct eap_sm *sm;
+	struct tls_config tlsconf;
+
+	sm = os_zalloc(sizeof(*sm));
+	if (sm == NULL)
+		return NULL;
+	sm->eapol_ctx = eapol_ctx;
+	sm->eapol_cb = eapol_cb;
+	sm->msg_ctx = msg_ctx;
+	sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
+	sm->wps = conf->wps;
+	dl_list_init(&sm->erp_keys);
+
+	os_memset(&tlsconf, 0, sizeof(tlsconf));
+	tlsconf.opensc_engine_path = conf->opensc_engine_path;
+	tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path;
+	tlsconf.pkcs11_module_path = conf->pkcs11_module_path;
+	tlsconf.openssl_ciphers = conf->openssl_ciphers;
+#ifdef CONFIG_FIPS
+	tlsconf.fips_mode = 1;
+#endif /* CONFIG_FIPS */
+	tlsconf.event_cb = eap_peer_sm_tls_event;
+	tlsconf.cb_ctx = sm;
+	tlsconf.cert_in_cb = conf->cert_in_cb;
+	sm->ssl_ctx = tls_init(&tlsconf);
+	if (sm->ssl_ctx == NULL) {
+		wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS "
+			   "context.");
+		os_free(sm);
+		return NULL;
+	}
+
+	sm->ssl_ctx2 = tls_init(&tlsconf);
+	if (sm->ssl_ctx2 == NULL) {
+		wpa_printf(MSG_INFO, "SSL: Failed to initialize TLS "
+			   "context (2).");
+		/* Run without separate TLS context within TLS tunnel */
+	}
+
+	return sm;
+}
+
+
+/**
+ * eap_peer_sm_deinit - Deinitialize and free an EAP peer state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * This function deinitializes EAP state machine and frees all allocated
+ * resources.
+ */
+void eap_peer_sm_deinit(struct eap_sm *sm)
+{
+	if (sm == NULL)
+		return;
+	eap_deinit_prev_method(sm, "EAP deinit");
+	eap_sm_abort(sm);
+	if (sm->ssl_ctx2)
+		tls_deinit(sm->ssl_ctx2);
+	tls_deinit(sm->ssl_ctx);
+	eap_peer_erp_free_keys(sm);
+	os_free(sm);
+}
+
+
+/**
+ * eap_peer_sm_step - Step EAP peer state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: 1 if EAP state was changed or 0 if not
+ *
+ * This function advances EAP state machine to a new state to match with the
+ * current variables. This should be called whenever variables used by the EAP
+ * state machine have changed.
+ */
+int eap_peer_sm_step(struct eap_sm *sm)
+{
+	int res = 0;
+	do {
+		sm->changed = FALSE;
+		SM_STEP_RUN(EAP);
+		if (sm->changed)
+			res = 1;
+	} while (sm->changed);
+	return res;
+}
+
+
+/**
+ * eap_sm_abort - Abort EAP authentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * Release system resources that have been allocated for the authentication
+ * session without fully deinitializing the EAP state machine.
+ */
+void eap_sm_abort(struct eap_sm *sm)
+{
+	wpabuf_free(sm->lastRespData);
+	sm->lastRespData = NULL;
+	wpabuf_free(sm->eapRespData);
+	sm->eapRespData = NULL;
+	eap_sm_free_key(sm);
+	os_free(sm->eapSessionId);
+	sm->eapSessionId = NULL;
+
+	/* This is not clearly specified in the EAP statemachines draft, but
+	 * it seems necessary to make sure that some of the EAPOL variables get
+	 * cleared for the next authentication. */
+	eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+static const char * eap_sm_state_txt(int state)
+{
+	switch (state) {
+	case EAP_INITIALIZE:
+		return "INITIALIZE";
+	case EAP_DISABLED:
+		return "DISABLED";
+	case EAP_IDLE:
+		return "IDLE";
+	case EAP_RECEIVED:
+		return "RECEIVED";
+	case EAP_GET_METHOD:
+		return "GET_METHOD";
+	case EAP_METHOD:
+		return "METHOD";
+	case EAP_SEND_RESPONSE:
+		return "SEND_RESPONSE";
+	case EAP_DISCARD:
+		return "DISCARD";
+	case EAP_IDENTITY:
+		return "IDENTITY";
+	case EAP_NOTIFICATION:
+		return "NOTIFICATION";
+	case EAP_RETRANSMIT:
+		return "RETRANSMIT";
+	case EAP_SUCCESS:
+		return "SUCCESS";
+	case EAP_FAILURE:
+		return "FAILURE";
+	default:
+		return "UNKNOWN";
+	}
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static const char * eap_sm_method_state_txt(EapMethodState state)
+{
+	switch (state) {
+	case METHOD_NONE:
+		return "NONE";
+	case METHOD_INIT:
+		return "INIT";
+	case METHOD_CONT:
+		return "CONT";
+	case METHOD_MAY_CONT:
+		return "MAY_CONT";
+	case METHOD_DONE:
+		return "DONE";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+
+static const char * eap_sm_decision_txt(EapDecision decision)
+{
+	switch (decision) {
+	case DECISION_FAIL:
+		return "FAIL";
+	case DECISION_COND_SUCC:
+		return "COND_SUCC";
+	case DECISION_UNCOND_SUCC:
+		return "UNCOND_SUCC";
+	default:
+		return "UNKNOWN";
+	}
+}
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_CTRL_IFACE
+
+/**
+ * eap_sm_get_status - Get EAP state machine status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query EAP state machine for status information. This function fills in a
+ * text area with current status information from the EAPOL state machine. If
+ * the buffer (buf) is not large enough, status information will be truncated
+ * to fit the buffer.
+ */
+int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose)
+{
+	int len, ret;
+
+	if (sm == NULL)
+		return 0;
+
+	len = os_snprintf(buf, buflen,
+			  "EAP state=%s\n",
+			  eap_sm_state_txt(sm->EAP_state));
+	if (os_snprintf_error(buflen, len))
+		return 0;
+
+	if (sm->selectedMethod != EAP_TYPE_NONE) {
+		const char *name;
+		if (sm->m) {
+			name = sm->m->name;
+		} else {
+			const struct eap_method *m =
+				eap_peer_get_eap_method(EAP_VENDOR_IETF,
+							sm->selectedMethod);
+			if (m)
+				name = m->name;
+			else
+				name = "?";
+		}
+		ret = os_snprintf(buf + len, buflen - len,
+				  "selectedMethod=%d (EAP-%s)\n",
+				  sm->selectedMethod, name);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+
+		if (sm->m && sm->m->get_status) {
+			len += sm->m->get_status(sm, sm->eap_method_priv,
+						 buf + len, buflen - len,
+						 verbose);
+		}
+	}
+
+	if (verbose) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "reqMethod=%d\n"
+				  "methodState=%s\n"
+				  "decision=%s\n"
+				  "ClientTimeout=%d\n",
+				  sm->reqMethod,
+				  eap_sm_method_state_txt(sm->methodState),
+				  eap_sm_decision_txt(sm->decision),
+				  sm->ClientTimeout);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
+	return len;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field,
+			   const char *msg, size_t msglen)
+{
+	struct eap_peer_config *config;
+	const char *txt = NULL;
+	char *tmp;
+
+	if (sm == NULL)
+		return;
+	config = eap_get_config(sm);
+	if (config == NULL)
+		return;
+
+	switch (field) {
+	case WPA_CTRL_REQ_EAP_IDENTITY:
+		config->pending_req_identity++;
+		break;
+	case WPA_CTRL_REQ_EAP_PASSWORD:
+		config->pending_req_password++;
+		break;
+	case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
+		config->pending_req_new_password++;
+		break;
+	case WPA_CTRL_REQ_EAP_PIN:
+		config->pending_req_pin++;
+		break;
+	case WPA_CTRL_REQ_EAP_OTP:
+		if (msg) {
+			tmp = os_malloc(msglen + 3);
+			if (tmp == NULL)
+				return;
+			tmp[0] = '[';
+			os_memcpy(tmp + 1, msg, msglen);
+			tmp[msglen + 1] = ']';
+			tmp[msglen + 2] = '\0';
+			txt = tmp;
+			os_free(config->pending_req_otp);
+			config->pending_req_otp = tmp;
+			config->pending_req_otp_len = msglen + 3;
+		} else {
+			if (config->pending_req_otp == NULL)
+				return;
+			txt = config->pending_req_otp;
+		}
+		break;
+	case WPA_CTRL_REQ_EAP_PASSPHRASE:
+		config->pending_req_passphrase++;
+		break;
+	case WPA_CTRL_REQ_SIM:
+		txt = msg;
+		break;
+	default:
+		return;
+	}
+
+	if (sm->eapol_cb->eap_param_needed)
+		sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define eap_sm_request(sm, type, msg, msglen) do { } while (0)
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+const char * eap_sm_get_method_name(struct eap_sm *sm)
+{
+	if (sm->m == NULL)
+		return "UNKNOWN";
+	return sm->m->name;
+}
+
+
+/**
+ * eap_sm_request_identity - Request identity from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request identity information for the
+ * current network. This is normally called when the identity is not included
+ * in the network configuration. The request will be sent to monitor programs
+ * through the control interface.
+ */
+void eap_sm_request_identity(struct eap_sm *sm)
+{
+	eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_password - Request password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request password information for the
+ * current network. This is normally called when the password is not included
+ * in the network configuration. The request will be sent to monitor programs
+ * through the control interface.
+ */
+void eap_sm_request_password(struct eap_sm *sm)
+{
+	eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_new_password - Request new password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request new password information for
+ * the current network. This is normally called when the EAP method indicates
+ * that the current password has expired and password change is required. The
+ * request will be sent to monitor programs through the control interface.
+ */
+void eap_sm_request_new_password(struct eap_sm *sm)
+{
+	eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_pin - Request SIM or smart card PIN from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request SIM or smart card PIN
+ * information for the current network. This is normally called when the PIN is
+ * not included in the network configuration. The request will be sent to
+ * monitor programs through the control interface.
+ */
+void eap_sm_request_pin(struct eap_sm *sm)
+{
+	eap_sm_request(sm, WPA_CTRL_REQ_EAP_PIN, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_otp - Request one time password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @msg: Message to be displayed to the user when asking for OTP
+ * @msg_len: Length of the user displayable message
+ *
+ * EAP methods can call this function to request open time password (OTP) for
+ * the current network. The request will be sent to monitor programs through
+ * the control interface.
+ */
+void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len)
+{
+	eap_sm_request(sm, WPA_CTRL_REQ_EAP_OTP, msg, msg_len);
+}
+
+
+/**
+ * eap_sm_request_passphrase - Request passphrase from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request passphrase for a private key
+ * for the current network. This is normally called when the passphrase is not
+ * included in the network configuration. The request will be sent to monitor
+ * programs through the control interface.
+ */
+void eap_sm_request_passphrase(struct eap_sm *sm)
+{
+	eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSPHRASE, NULL, 0);
+}
+
+
+/**
+ * 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()
+ *
+ * Notify EAP state machines that a monitor was attached to the control
+ * interface to trigger re-sending of pending requests for user input.
+ */
+void eap_sm_notify_ctrl_attached(struct eap_sm *sm)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+
+	if (config == NULL)
+		return;
+
+	/* Re-send any pending requests for user data since a new control
+	 * interface was added. This handles cases where the EAP authentication
+	 * starts immediately after system startup when the user interface is
+	 * not yet running. */
+	if (config->pending_req_identity)
+		eap_sm_request_identity(sm);
+	if (config->pending_req_password)
+		eap_sm_request_password(sm);
+	if (config->pending_req_new_password)
+		eap_sm_request_new_password(sm);
+	if (config->pending_req_otp)
+		eap_sm_request_otp(sm, NULL, 0);
+	if (config->pending_req_pin)
+		eap_sm_request_pin(sm);
+	if (config->pending_req_passphrase)
+		eap_sm_request_passphrase(sm);
+}
+
+
+static int eap_allowed_phase2_type(int vendor, int type)
+{
+	if (vendor != EAP_VENDOR_IETF)
+		return 0;
+	return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS &&
+		type != EAP_TYPE_FAST;
+}
+
+
+/**
+ * eap_get_phase2_type - Get EAP type for the given EAP phase 2 method name
+ * @name: EAP method name, e.g., MD5
+ * @vendor: Buffer for returning EAP Vendor-Id
+ * Returns: EAP method type or %EAP_TYPE_NONE if not found
+ *
+ * This function maps EAP type names into EAP type numbers that are allowed for
+ * Phase 2, i.e., for tunneled authentication. Phase 2 is used, e.g., with
+ * EAP-PEAP, EAP-TTLS, and EAP-FAST.
+ */
+u32 eap_get_phase2_type(const char *name, int *vendor)
+{
+	int v;
+	u32 type = eap_peer_get_type(name, &v);
+	if (eap_allowed_phase2_type(v, type)) {
+		*vendor = v;
+		return type;
+	}
+	*vendor = EAP_VENDOR_IETF;
+	return EAP_TYPE_NONE;
+}
+
+
+/**
+ * eap_get_phase2_types - Get list of allowed EAP phase 2 types
+ * @config: Pointer to a network configuration
+ * @count: Pointer to a variable to be filled with number of returned EAP types
+ * Returns: Pointer to allocated type list or %NULL on failure
+ *
+ * This function generates an array of allowed EAP phase 2 (tunneled) types for
+ * the given network configuration.
+ */
+struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
+					      size_t *count)
+{
+	struct eap_method_type *buf;
+	u32 method;
+	int vendor;
+	size_t mcount;
+	const struct eap_method *methods, *m;
+
+	methods = eap_peer_get_methods(&mcount);
+	if (methods == NULL)
+		return NULL;
+	*count = 0;
+	buf = os_malloc(mcount * sizeof(struct eap_method_type));
+	if (buf == NULL)
+		return NULL;
+
+	for (m = methods; m; m = m->next) {
+		vendor = m->vendor;
+		method = m->method;
+		if (eap_allowed_phase2_type(vendor, method)) {
+			if (vendor == EAP_VENDOR_IETF &&
+			    method == EAP_TYPE_TLS && config &&
+			    config->private_key2 == NULL)
+				continue;
+			buf[*count].vendor = vendor;
+			buf[*count].method = method;
+			(*count)++;
+		}
+	}
+
+	return buf;
+}
+
+
+/**
+ * eap_set_fast_reauth - Update fast_reauth setting
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @enabled: 1 = Fast reauthentication is enabled, 0 = Disabled
+ */
+void eap_set_fast_reauth(struct eap_sm *sm, int enabled)
+{
+	sm->fast_reauth = enabled;
+}
+
+
+/**
+ * eap_set_workaround - Update EAP workarounds setting
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @workaround: 1 = Enable EAP workarounds, 0 = Disable EAP workarounds
+ */
+void eap_set_workaround(struct eap_sm *sm, unsigned int workaround)
+{
+	sm->workaround = workaround;
+}
+
+
+/**
+ * eap_get_config - Get current network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the current network configuration or %NULL if not found
+ *
+ * EAP peer methods should avoid using this function if they can use other
+ * access functions, like eap_get_config_identity() and
+ * eap_get_config_password(), that do not require direct access to
+ * struct eap_peer_config.
+ */
+struct eap_peer_config * eap_get_config(struct eap_sm *sm)
+{
+	return sm->eapol_cb->get_config(sm->eapol_ctx);
+}
+
+
+/**
+ * eap_get_config_identity - Get identity from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the identity
+ * Returns: Pointer to the identity or %NULL if not found
+ */
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	if (config == NULL)
+		return NULL;
+	*len = config->identity_len;
+	return config->identity;
+}
+
+
+static int eap_get_ext_password(struct eap_sm *sm,
+				struct eap_peer_config *config)
+{
+	char *name;
+
+	if (config->password == NULL)
+		return -1;
+
+	name = os_zalloc(config->password_len + 1);
+	if (name == NULL)
+		return -1;
+	os_memcpy(name, config->password, config->password_len);
+
+	ext_password_free(sm->ext_pw_buf);
+	sm->ext_pw_buf = ext_password_get(sm->ext_pw, name);
+	os_free(name);
+
+	return sm->ext_pw_buf == NULL ? -1 : 0;
+}
+
+
+/**
+ * eap_get_config_password - Get password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the password
+ * Returns: Pointer to the password or %NULL if not found
+ */
+const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	if (config == NULL)
+		return NULL;
+
+	if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+		if (eap_get_ext_password(sm, config) < 0)
+			return NULL;
+		*len = wpabuf_len(sm->ext_pw_buf);
+		return wpabuf_head(sm->ext_pw_buf);
+	}
+
+	*len = config->password_len;
+	return config->password;
+}
+
+
+/**
+ * eap_get_config_password2 - Get password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the password
+ * @hash: Buffer for returning whether the password is stored as a
+ * NtPasswordHash instead of plaintext password; can be %NULL if this
+ * information is not needed
+ * Returns: Pointer to the password or %NULL if not found
+ */
+const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	if (config == NULL)
+		return NULL;
+
+	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);
+	}
+
+	*len = config->password_len;
+	if (hash)
+		*hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH);
+	return config->password;
+}
+
+
+/**
+ * eap_get_config_new_password - Get new password from network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the new password
+ * Returns: Pointer to the new password or %NULL if not found
+ */
+const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	if (config == NULL)
+		return NULL;
+	*len = config->new_password_len;
+	return config->new_password;
+}
+
+
+/**
+ * eap_get_config_otp - Get one-time password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the one-time password
+ * Returns: Pointer to the one-time password or %NULL if not found
+ */
+const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	if (config == NULL)
+		return NULL;
+	*len = config->otp_len;
+	return config->otp;
+}
+
+
+/**
+ * eap_clear_config_otp - Clear used one-time password
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * This function clears a used one-time password (OTP) from the current network
+ * configuration. This should be called when the OTP has been used and is not
+ * needed anymore.
+ */
+void eap_clear_config_otp(struct eap_sm *sm)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	if (config == NULL)
+		return;
+	os_memset(config->otp, 0, config->otp_len);
+	os_free(config->otp);
+	config->otp = NULL;
+	config->otp_len = 0;
+}
+
+
+/**
+ * eap_get_config_phase1 - Get phase1 data from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the phase1 data or %NULL if not found
+ */
+const char * eap_get_config_phase1(struct eap_sm *sm)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	if (config == NULL)
+		return NULL;
+	return config->phase1;
+}
+
+
+/**
+ * eap_get_config_phase2 - Get phase2 data from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the phase1 data or %NULL if not found
+ */
+const char * eap_get_config_phase2(struct eap_sm *sm)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	if (config == NULL)
+		return NULL;
+	return config->phase2;
+}
+
+
+int eap_get_config_fragment_size(struct eap_sm *sm)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	if (config == NULL)
+		return -1;
+	return config->fragment_size;
+}
+
+
+/**
+ * eap_key_available - Get key availability (eapKeyAvailable variable)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: 1 if EAP keying material is available, 0 if not
+ */
+int eap_key_available(struct eap_sm *sm)
+{
+	return sm ? sm->eapKeyAvailable : 0;
+}
+
+
+/**
+ * eap_notify_success - Notify EAP state machine about external success trigger
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * This function is called when external event, e.g., successful completion of
+ * WPA-PSK key handshake, is indicating that EAP state machine should move to
+ * success state. This is mainly used with security modes that do not use EAP
+ * state machine (e.g., WPA-PSK).
+ */
+void eap_notify_success(struct eap_sm *sm)
+{
+	if (sm) {
+		sm->decision = DECISION_COND_SUCC;
+		sm->EAP_state = EAP_SUCCESS;
+	}
+}
+
+
+/**
+ * eap_notify_lower_layer_success - Notification of lower layer success
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * Notify EAP state machines that a lower layer has detected a successful
+ * authentication. This is used to recover from dropped EAP-Success messages.
+ */
+void eap_notify_lower_layer_success(struct eap_sm *sm)
+{
+	if (sm == NULL)
+		return;
+
+	if (eapol_get_bool(sm, EAPOL_eapSuccess) ||
+	    sm->decision == DECISION_FAIL ||
+	    (sm->methodState != METHOD_MAY_CONT &&
+	     sm->methodState != METHOD_DONE))
+		return;
+
+	if (sm->eapKeyData != NULL)
+		sm->eapKeyAvailable = TRUE;
+	eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+		"EAP authentication completed successfully (based on lower "
+		"layer success)");
+}
+
+
+/**
+ * eap_get_eapSessionId - Get Session-Id from EAP state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_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
+ *
+ * Fetch EAP Session-Id from the EAP state machine. The Session-Id is available
+ * only after a successful authentication. EAP state machine continues to manage
+ * the Session-Id and the caller must not change or free the returned data.
+ */
+const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len)
+{
+	if (sm == NULL || sm->eapSessionId == NULL) {
+		*len = 0;
+		return NULL;
+	}
+
+	*len = sm->eapSessionIdLen;
+	return sm->eapSessionId;
+}
+
+
+/**
+ * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Pointer to variable that will be set to number of bytes in the key
+ * Returns: Pointer to the EAP keying data or %NULL on failure
+ *
+ * Fetch EAP keying material (MSK, eapKeyData) from the EAP state machine. The
+ * key is available only after a successful authentication. EAP state machine
+ * continues to manage the key data and the caller must not change or free the
+ * returned data.
+ */
+const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len)
+{
+	if (sm == NULL || sm->eapKeyData == NULL) {
+		*len = 0;
+		return NULL;
+	}
+
+	*len = sm->eapKeyDataLen;
+	return sm->eapKeyData;
+}
+
+
+/**
+ * eap_get_eapKeyData - Get EAP response data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the EAP response (eapRespData) or %NULL on failure
+ *
+ * Fetch EAP response (eapRespData) from the EAP state machine. This data is
+ * available when EAP state machine has processed an incoming EAP request. The
+ * EAP state machine does not maintain a reference to the response after this
+ * function is called and the caller is responsible for freeing the data.
+ */
+struct wpabuf * eap_get_eapRespData(struct eap_sm *sm)
+{
+	struct wpabuf *resp;
+
+	if (sm == NULL || sm->eapRespData == NULL)
+		return NULL;
+
+	resp = sm->eapRespData;
+	sm->eapRespData = NULL;
+
+	return resp;
+}
+
+
+/**
+ * eap_sm_register_scard_ctx - Notification of smart card context
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @ctx: Context data for smart card operations
+ *
+ * Notify EAP state machines of context data for smart card operations. This
+ * context data will be used as a parameter for scard_*() functions.
+ */
+void eap_register_scard_ctx(struct eap_sm *sm, void *ctx)
+{
+	if (sm)
+		sm->scard_ctx = ctx;
+}
+
+
+/**
+ * eap_set_config_blob - Set or add a named configuration blob
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an existing
+ * blob.
+ */
+void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+	sm->eapol_cb->set_config_blob(sm->eapol_ctx, blob);
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+/**
+ * eap_get_config_blob - Get a named configuration blob
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm,
+						   const char *name)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+	return sm->eapol_cb->get_config_blob(sm->eapol_ctx, name);
+#else /* CONFIG_NO_CONFIG_BLOBS */
+	return NULL;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+/**
+ * eap_set_force_disabled - Set force_disabled flag
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @disabled: 1 = EAP disabled, 0 = EAP enabled
+ *
+ * This function is used to force EAP state machine to be disabled when it is
+ * not in use (e.g., with WPA-PSK or plaintext connections).
+ */
+void eap_set_force_disabled(struct eap_sm *sm, int disabled)
+{
+	sm->force_disabled = disabled;
+}
+
+
+/**
+ * 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()
+ *
+ * An EAP method can perform a pending operation (e.g., to get a response from
+ * an external process). Once the response is available, this function can be
+ * used to request EAPOL state machine to retry delivering the previously
+ * received (and still unanswered) EAP request to EAP state machine.
+ */
+void eap_notify_pending(struct eap_sm *sm)
+{
+	sm->eapol_cb->notify_pending(sm->eapol_ctx);
+}
+
+
+/**
+ * eap_invalidate_cached_session - Mark cached session data invalid
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ */
+void eap_invalidate_cached_session(struct eap_sm *sm)
+{
+	if (sm)
+		eap_deinit_prev_method(sm, "invalidate");
+}
+
+
+int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf)
+{
+	if (conf->identity_len != WSC_ID_ENROLLEE_LEN ||
+	    os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN))
+		return 0; /* Not a WPS Enrollee */
+
+	if (conf->phase1 == NULL || os_strstr(conf->phase1, "pbc=1") == NULL)
+		return 0; /* Not using PBC */
+
+	return 1;
+}
+
+
+int eap_is_wps_pin_enrollee(struct eap_peer_config *conf)
+{
+	if (conf->identity_len != WSC_ID_ENROLLEE_LEN ||
+	    os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN))
+		return 0; /* Not a WPS Enrollee */
+
+	if (conf->phase1 == NULL || os_strstr(conf->phase1, "pin=") == NULL)
+		return 0; /* Not using PIN */
+
+	return 1;
+}
+
+
+void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext)
+{
+	ext_password_free(sm->ext_pw_buf);
+	sm->ext_pw_buf = NULL;
+	sm->ext_pw = ext;
+}
+
+
+/**
+ * eap_set_anon_id - Set or add anonymous identity
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear
+ * @len: Length of anonymous identity in octets
+ */
+void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len)
+{
+	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/hostap/src/eap_peer/eap.h b/hostap/src/eap_peer/eap.h
new file mode 100644
index 0000000..1a645af
--- /dev/null
+++ b/hostap/src/eap_peer/eap.h
@@ -0,0 +1,354 @@
+/*
+ * EAP peer state machine functions (RFC 4137)
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_H
+#define EAP_H
+
+#include "common/defs.h"
+#include "eap_common/eap_defs.h"
+#include "eap_peer/eap_methods.h"
+
+struct eap_sm;
+struct wpa_config_blob;
+struct wpabuf;
+
+struct eap_method_type {
+	int vendor;
+	u32 method;
+};
+
+#ifdef IEEE8021X_EAPOL
+
+/**
+ * enum eapol_bool_var - EAPOL boolean state variables for EAP state machine
+ *
+ * These variables are used in the interface between EAP peer state machine and
+ * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
+ * expected to maintain these variables and register a callback functions for
+ * EAP state machine to get and set the variables.
+ */
+enum eapol_bool_var {
+	/**
+	 * EAPOL_eapSuccess - EAP SUCCESS state reached
+	 *
+	 * EAP state machine reads and writes this value.
+	 */
+	EAPOL_eapSuccess,
+
+	/**
+	 * EAPOL_eapRestart - Lower layer request to restart authentication
+	 *
+	 * Set to TRUE in lower layer, FALSE in EAP state machine.
+	 */
+	EAPOL_eapRestart,
+
+	/**
+	 * EAPOL_eapFail - EAP FAILURE state reached
+	 *
+	 * EAP state machine writes this value.
+	 */
+	EAPOL_eapFail,
+
+	/**
+	 * EAPOL_eapResp - Response to send
+	 *
+	 * Set to TRUE in EAP state machine, FALSE in lower layer.
+	 */
+	EAPOL_eapResp,
+
+	/**
+	 * EAPOL_eapNoResp - Request has been process; no response to send
+	 *
+	 * Set to TRUE in EAP state machine, FALSE in lower layer.
+	 */
+	EAPOL_eapNoResp,
+
+	/**
+	 * EAPOL_eapReq - EAP request available from lower layer
+	 *
+	 * Set to TRUE in lower layer, FALSE in EAP state machine.
+	 */
+	EAPOL_eapReq,
+
+	/**
+	 * EAPOL_portEnabled - Lower layer is ready for communication
+	 *
+	 * EAP state machines reads this value.
+	 */
+	EAPOL_portEnabled,
+
+	/**
+	 * EAPOL_altAccept - Alternate indication of success (RFC3748)
+	 *
+	 * EAP state machines reads this value.
+	 */
+	EAPOL_altAccept,
+
+	/**
+	 * EAPOL_altReject - Alternate indication of failure (RFC3748)
+	 *
+	 * EAP state machines reads this value.
+	 */
+	EAPOL_altReject,
+
+	/**
+	 * EAPOL_eapTriggerStart - EAP-based trigger to send EAPOL-Start
+	 *
+	 * EAP state machine writes this value.
+	 */
+	EAPOL_eapTriggerStart
+};
+
+/**
+ * enum eapol_int_var - EAPOL integer state variables for EAP state machine
+ *
+ * These variables are used in the interface between EAP peer state machine and
+ * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
+ * expected to maintain these variables and register a callback functions for
+ * EAP state machine to get and set the variables.
+ */
+enum eapol_int_var {
+	/**
+	 * EAPOL_idleWhile - Outside time for EAP peer timeout
+	 *
+	 * This integer variable is used to provide an outside timer that the
+	 * external (to EAP state machine) code must decrement by one every
+	 * second until the value reaches zero. This is used in the same way as
+	 * EAPOL state machine timers. EAP state machine reads and writes this
+	 * value.
+	 */
+	EAPOL_idleWhile
+};
+
+/**
+ * struct eapol_callbacks - Callback functions from EAP to lower layer
+ *
+ * This structure defines the callback functions that EAP state machine
+ * requires from the lower layer (usually EAPOL state machine) for updating
+ * state variables and requesting information. eapol_ctx from
+ * eap_peer_sm_init() call will be used as the ctx parameter for these
+ * callback functions.
+ */
+struct eapol_callbacks {
+	/**
+	 * get_config - Get pointer to the current network configuration
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 */
+	struct eap_peer_config * (*get_config)(void *ctx);
+
+	/**
+	 * get_bool - Get a boolean EAPOL state variable
+	 * @variable: EAPOL boolean variable to get
+	 * Returns: Value of the EAPOL variable
+	 */
+	Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable);
+
+	/**
+	 * set_bool - Set a boolean EAPOL state variable
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @variable: EAPOL boolean variable to set
+	 * @value: Value for the EAPOL variable
+	 */
+	void (*set_bool)(void *ctx, enum eapol_bool_var variable,
+			 Boolean value);
+
+	/**
+	 * get_int - Get an integer EAPOL state variable
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @variable: EAPOL integer variable to get
+	 * Returns: Value of the EAPOL variable
+	 */
+	unsigned int (*get_int)(void *ctx, enum eapol_int_var variable);
+
+	/**
+	 * set_int - Set an integer EAPOL state variable
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @variable: EAPOL integer variable to set
+	 * @value: Value for the EAPOL variable
+	 */
+	void (*set_int)(void *ctx, enum eapol_int_var variable,
+			unsigned int value);
+
+	/**
+	 * get_eapReqData - Get EAP-Request data
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @len: Pointer to variable that will be set to eapReqDataLen
+	 * Returns: Reference to eapReqData (EAP state machine will not free
+	 * this) or %NULL if eapReqData not available.
+	 */
+	struct wpabuf * (*get_eapReqData)(void *ctx);
+
+	/**
+	 * set_config_blob - Set named configuration blob
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @blob: New value for the blob
+	 *
+	 * Adds a new configuration blob or replaces the current value of an
+	 * existing blob.
+	 */
+	void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+
+	/**
+	 * get_config_blob - Get a named configuration blob
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @name: Name of the blob
+	 * Returns: Pointer to blob data or %NULL if not found
+	 */
+	const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+							  const char *name);
+
+	/**
+	 * notify_pending - Notify that a pending request can be retried
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 *
+	 * An EAP method can perform a pending operation (e.g., to get a
+	 * response from an external process). Once the response is available,
+	 * this callback function can be used to request EAPOL state machine to
+	 * retry delivering the previously received (and still unanswered) EAP
+	 * request to EAP state machine.
+	 */
+	void (*notify_pending)(void *ctx);
+
+	/**
+	 * eap_param_needed - Notify that EAP parameter is needed
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY)
+	 * @txt: User readable text describing the required parameter
+	 */
+	void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field,
+				 const char *txt);
+
+	/**
+	 * notify_cert - Notification of a peer certificate
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @depth: Depth in certificate chain (0 = server)
+	 * @subject: Subject of the peer certificate
+	 * @altsubject: Select fields from AltSubject of the peer certificate
+	 * @num_altsubject: Number of altsubject values
+	 * @cert_hash: SHA-256 hash of the certificate
+	 * @cert: Peer certificate
+	 */
+	void (*notify_cert)(void *ctx, int depth, const char *subject,
+			    const char *altsubject[], int num_altsubject,
+			    const char *cert_hash, const struct wpabuf *cert);
+
+	/**
+	 * notify_status - Notification of the current EAP state
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @status: Step in the process of EAP authentication
+	 * @parameter: Step-specific parameter, e.g., EAP method name
+	 */
+	void (*notify_status)(void *ctx, const char *status,
+			      const char *parameter);
+
+#ifdef CONFIG_EAP_PROXY
+	/**
+	 * eap_proxy_cb - Callback signifying any updates from eap_proxy
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 */
+	void (*eap_proxy_cb)(void *ctx);
+#endif /* CONFIG_EAP_PROXY */
+
+	/**
+	 * set_anon_id - Set or add anonymous identity
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear
+	 * @len: Length of anonymous identity in octets
+	 */
+	void (*set_anon_id)(void *ctx, const u8 *id, size_t len);
+};
+
+/**
+ * struct eap_config - Configuration for EAP state machine
+ */
+struct eap_config {
+	/**
+	 * opensc_engine_path - OpenSC engine for OpenSSL engine support
+	 *
+	 * Usually, path to engine_opensc.so.
+	 */
+	const char *opensc_engine_path;
+	/**
+	 * pkcs11_engine_path - PKCS#11 engine for OpenSSL engine support
+	 *
+	 * Usually, path to engine_pkcs11.so.
+	 */
+	const char *pkcs11_engine_path;
+	/**
+	 * pkcs11_module_path - OpenSC PKCS#11 module for OpenSSL engine
+	 *
+	 * Usually, path to opensc-pkcs11.so.
+	 */
+	const char *pkcs11_module_path;
+	/**
+	 * openssl_ciphers - OpenSSL cipher string
+	 *
+	 * This is an OpenSSL specific configuration option for configuring the
+	 * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the
+	 * default.
+	 */
+	const char *openssl_ciphers;
+	/**
+	 * wps - WPS context data
+	 *
+	 * This is only used by EAP-WSC and can be left %NULL if not available.
+	 */
+	struct wps_context *wps;
+
+	/**
+	 * cert_in_cb - Include server certificates in callback
+	 */
+	int cert_in_cb;
+};
+
+struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
+				 const struct eapol_callbacks *eapol_cb,
+				 void *msg_ctx, struct eap_config *conf);
+void eap_peer_sm_deinit(struct eap_sm *sm);
+int eap_peer_sm_step(struct eap_sm *sm);
+void eap_sm_abort(struct eap_sm *sm);
+int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen,
+		      int verbose);
+const char * eap_sm_get_method_name(struct eap_sm *sm);
+struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted);
+void eap_sm_request_identity(struct eap_sm *sm);
+void eap_sm_request_password(struct eap_sm *sm);
+void eap_sm_request_new_password(struct eap_sm *sm);
+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,
+					      size_t *count);
+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);
+const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
+struct wpabuf * eap_get_eapRespData(struct eap_sm *sm);
+void eap_register_scard_ctx(struct eap_sm *sm, void *ctx);
+void eap_invalidate_cached_session(struct eap_sm *sm);
+
+int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf);
+int eap_is_wps_pin_enrollee(struct eap_peer_config *conf);
+
+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);
+void eap_peer_erp_free_keys(struct eap_sm *sm);
+
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* EAP_H */
diff --git a/hostap/src/eap_peer/eap_aka.c b/hostap/src/eap_peer/eap_aka.c
new file mode 100644
index 0000000..dc9e8cc
--- /dev/null
+++ b/hostap/src/eap_peer/eap_aka.c
@@ -0,0 +1,1551 @@
+/*
+ * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448)
+ * Copyright (c) 2004-2012, 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 "pcsc_funcs.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/milenage.h"
+#include "eap_common/eap_sim_common.h"
+#include "eap_config.h"
+#include "eap_i.h"
+
+
+struct eap_aka_data {
+	u8 ik[EAP_AKA_IK_LEN], ck[EAP_AKA_CK_LEN], res[EAP_AKA_RES_MAX_LEN];
+	size_t res_len;
+	u8 nonce_s[EAP_SIM_NONCE_S_LEN];
+	u8 mk[EAP_SIM_MK_LEN];
+	u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
+	u8 k_encr[EAP_SIM_K_ENCR_LEN];
+	u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */
+	u8 msk[EAP_SIM_KEYING_DATA_LEN];
+	u8 emsk[EAP_EMSK_LEN];
+	u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN];
+	u8 auts[EAP_AKA_AUTS_LEN];
+
+	int num_id_req, num_notification;
+	u8 *pseudonym;
+	size_t pseudonym_len;
+	u8 *reauth_id;
+	size_t reauth_id_len;
+	int reauth;
+	unsigned int counter, counter_too_small;
+	u8 *last_eap_identity;
+	size_t last_eap_identity_len;
+	enum {
+		CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
+	} state;
+
+	struct wpabuf *id_msgs;
+	int prev_id;
+	int result_ind, use_result_ind;
+	u8 eap_method;
+	u8 *network_name;
+	size_t network_name_len;
+	u16 kdf;
+	int kdf_negotiation;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_aka_state_txt(int state)
+{
+	switch (state) {
+	case CONTINUE:
+		return "CONTINUE";
+	case RESULT_SUCCESS:
+		return "RESULT_SUCCESS";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "?";
+	}
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_aka_state(struct eap_aka_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s",
+		   eap_aka_state_txt(data->state),
+		   eap_aka_state_txt(state));
+	data->state = state;
+}
+
+
+static void * eap_aka_init(struct eap_sm *sm)
+{
+	struct eap_aka_data *data;
+	const char *phase1 = eap_get_config_phase1(sm);
+	struct eap_peer_config *config = eap_get_config(sm);
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	data->eap_method = EAP_TYPE_AKA;
+
+	eap_aka_state(data, CONTINUE);
+	data->prev_id = -1;
+
+	data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL;
+
+	if (config && config->anonymous_identity) {
+		data->pseudonym = os_malloc(config->anonymous_identity_len);
+		if (data->pseudonym) {
+			os_memcpy(data->pseudonym, config->anonymous_identity,
+				  config->anonymous_identity_len);
+			data->pseudonym_len = config->anonymous_identity_len;
+		}
+	}
+
+	return data;
+}
+
+
+#ifdef EAP_AKA_PRIME
+static void * eap_aka_prime_init(struct eap_sm *sm)
+{
+	struct eap_aka_data *data = eap_aka_init(sm);
+	if (data == NULL)
+		return NULL;
+	data->eap_method = EAP_TYPE_AKA_PRIME;
+	return data;
+}
+#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;
+	if (data) {
+		os_free(data->pseudonym);
+		os_free(data->reauth_id);
+		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;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm");
+
+	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,
+				       data->ik, data->ck, data->auts);
+	}
+
+#ifdef CONFIG_USIM_SIMULATOR
+	if (conf->password) {
+		u8 opc[16], k[16], sqn[6];
+		const char *pos;
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Use internal Milenage "
+			   "implementation for UMTS authentication");
+		if (conf->password_len < 78) {
+			wpa_printf(MSG_DEBUG, "EAP-AKA: invalid Milenage "
+				   "password");
+			return -1;
+		}
+		pos = (const char *) conf->password;
+		if (hexstr2bin(pos, k, 16))
+			return -1;
+		pos += 32;
+		if (*pos != ':')
+			return -1;
+		pos++;
+
+		if (hexstr2bin(pos, opc, 16))
+			return -1;
+		pos += 32;
+		if (*pos != ':')
+			return -1;
+		pos++;
+
+		if (hexstr2bin(pos, sqn, 6))
+			return -1;
+
+		return milenage_check(opc, k, sqn, data->rand, data->autn,
+				      data->ik, data->ck,
+				      data->res, &data->res_len, data->auts);
+	}
+#endif /* CONFIG_USIM_SIMULATOR */
+
+#ifdef CONFIG_USIM_HARDCODED
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Use hardcoded Kc and SRES values for "
+		   "testing");
+
+	/* These hardcoded Kc and SRES values are used for testing.
+	 * Could consider making them configurable. */
+	os_memset(data->res, '2', EAP_AKA_RES_MAX_LEN);
+	data->res_len = EAP_AKA_RES_MAX_LEN;
+	os_memset(data->ik, '3', EAP_AKA_IK_LEN);
+	os_memset(data->ck, '4', EAP_AKA_CK_LEN);
+	{
+		u8 autn[EAP_AKA_AUTN_LEN];
+		os_memset(autn, '1', EAP_AKA_AUTN_LEN);
+		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;
+		}
+	}
+#if 0
+	{
+		static int test_resync = 1;
+		if (test_resync) {
+			/* Test Resynchronization */
+			test_resync = 0;
+			return -2;
+		}
+	}
+#endif
+	return 0;
+
+#else /* CONFIG_USIM_HARDCODED */
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorithm "
+		   "enabled");
+	return -1;
+
+#endif /* CONFIG_USIM_HARDCODED */
+}
+
+
+#define CLEAR_PSEUDONYM	0x01
+#define CLEAR_REAUTH_ID	0x02
+#define CLEAR_EAP_ID	0x04
+
+static void eap_aka_clear_identities(struct eap_sm *sm,
+				     struct eap_aka_data *data, int id)
+{
+	if ((id & CLEAR_PSEUDONYM) && data->pseudonym) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym");
+		os_free(data->pseudonym);
+		data->pseudonym = NULL;
+		data->pseudonym_len = 0;
+		eap_set_anon_id(sm, NULL, 0);
+	}
+	if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id");
+		os_free(data->reauth_id);
+		data->reauth_id = NULL;
+		data->reauth_id_len = 0;
+	}
+	if ((id & CLEAR_EAP_ID) && data->last_eap_identity) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id");
+		os_free(data->last_eap_identity);
+		data->last_eap_identity = NULL;
+		data->last_eap_identity_len = 0;
+	}
+}
+
+
+static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data,
+			     struct eap_sim_attrs *attr)
+{
+	if (attr->next_pseudonym) {
+		const u8 *identity = NULL;
+		size_t identity_len = 0;
+		const u8 *realm = NULL;
+		size_t realm_len = 0;
+
+		wpa_hexdump_ascii(MSG_DEBUG,
+				  "EAP-AKA: (encr) AT_NEXT_PSEUDONYM",
+				  attr->next_pseudonym,
+				  attr->next_pseudonym_len);
+		os_free(data->pseudonym);
+		/* Look for the realm of the permanent identity */
+		identity = eap_get_config_identity(sm, &identity_len);
+		if (identity) {
+			for (realm = identity, realm_len = identity_len;
+			     realm_len > 0; realm_len--, realm++) {
+				if (*realm == '@')
+					break;
+			}
+		}
+		data->pseudonym = os_malloc(attr->next_pseudonym_len +
+					    realm_len);
+		if (data->pseudonym == NULL) {
+			wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
+				   "next pseudonym");
+			data->pseudonym_len = 0;
+			return -1;
+		}
+		os_memcpy(data->pseudonym, attr->next_pseudonym,
+			  attr->next_pseudonym_len);
+		if (realm_len) {
+			os_memcpy(data->pseudonym + attr->next_pseudonym_len,
+				  realm, realm_len);
+		}
+		data->pseudonym_len = attr->next_pseudonym_len + realm_len;
+		eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len);
+	}
+
+	if (attr->next_reauth_id) {
+		os_free(data->reauth_id);
+		data->reauth_id = os_malloc(attr->next_reauth_id_len);
+		if (data->reauth_id == NULL) {
+			wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
+				   "next reauth_id");
+			data->reauth_id_len = 0;
+			return -1;
+		}
+		os_memcpy(data->reauth_id, attr->next_reauth_id,
+			  attr->next_reauth_id_len);
+		data->reauth_id_len = attr->next_reauth_id_len;
+		wpa_hexdump_ascii(MSG_DEBUG,
+				  "EAP-AKA: (encr) AT_NEXT_REAUTH_ID",
+				  data->reauth_id,
+				  data->reauth_id_len);
+	}
+
+	return 0;
+}
+
+
+static int eap_aka_add_id_msg(struct eap_aka_data *data,
+			      const struct wpabuf *msg)
+{
+	if (msg == NULL)
+		return -1;
+
+	if (data->id_msgs == NULL) {
+		data->id_msgs = wpabuf_dup(msg);
+		return data->id_msgs == NULL ? -1 : 0;
+	}
+
+	if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0)
+		return -1;
+	wpabuf_put_buf(data->id_msgs, msg);
+
+	return 0;
+}
+
+
+static void eap_aka_add_checkcode(struct eap_aka_data *data,
+				  struct eap_sim_msg *msg)
+{
+	const u8 *addr;
+	size_t len;
+	u8 hash[SHA256_MAC_LEN];
+
+	wpa_printf(MSG_DEBUG, "   AT_CHECKCODE");
+
+	if (data->id_msgs == NULL) {
+		/*
+		 * No EAP-AKA/Identity packets were exchanged - send empty
+		 * checkcode.
+		 */
+		eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0);
+		return;
+	}
+
+	/* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */
+	addr = wpabuf_head(data->id_msgs);
+	len = wpabuf_len(data->id_msgs);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len);
+#ifdef EAP_AKA_PRIME
+	if (data->eap_method == EAP_TYPE_AKA_PRIME)
+		sha256_vector(1, &addr, &len, hash);
+	else
+#endif /* EAP_AKA_PRIME */
+		sha1_vector(1, &addr, &len, hash);
+
+	eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash,
+			data->eap_method == EAP_TYPE_AKA_PRIME ?
+			EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN);
+}
+
+
+static int eap_aka_verify_checkcode(struct eap_aka_data *data,
+				    const u8 *checkcode, size_t checkcode_len)
+{
+	const u8 *addr;
+	size_t len;
+	u8 hash[SHA256_MAC_LEN];
+	size_t hash_len;
+
+	if (checkcode == NULL)
+		return -1;
+
+	if (data->id_msgs == NULL) {
+		if (checkcode_len != 0) {
+			wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server "
+				   "indicates that AKA/Identity messages were "
+				   "used, but they were not");
+			return -1;
+		}
+		return 0;
+	}
+
+	hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ?
+		EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN;
+
+	if (checkcode_len != hash_len) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server "
+			   "indicates that AKA/Identity message were not "
+			   "used, but they were");
+		return -1;
+	}
+
+	/* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */
+	addr = wpabuf_head(data->id_msgs);
+	len = wpabuf_len(data->id_msgs);
+#ifdef EAP_AKA_PRIME
+	if (data->eap_method == EAP_TYPE_AKA_PRIME)
+		sha256_vector(1, &addr, &len, hash);
+	else
+#endif /* EAP_AKA_PRIME */
+		sha1_vector(1, &addr, &len, hash);
+
+	if (os_memcmp_const(hash, checkcode, hash_len) != 0) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id,
+					    int err)
+{
+	struct eap_sim_msg *msg;
+
+	eap_aka_state(data, FAILURE);
+	data->num_id_req = 0;
+	data->num_notification = 0;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Send Client-Error (error code %d)",
+		   err);
+	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, data->eap_method, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data,
+						     u8 id)
+{
+	struct eap_sim_msg *msg;
+
+	eap_aka_state(data, FAILURE);
+	data->num_id_req = 0;
+	data->num_notification = 0;
+
+	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject "
+		   "(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, data->eap_method, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_synchronization_failure(
+	struct eap_aka_data *data, u8 id)
+{
+	struct eap_sim_msg *msg;
+
+	data->num_id_req = 0;
+	data->num_notification = 0;
+
+	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure "
+		   "(id=%d)", id);
+	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+			       EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE);
+	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, data->eap_method, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm,
+						 struct eap_aka_data *data,
+						 u8 id,
+						 enum eap_sim_id_req id_req)
+{
+	const u8 *identity = NULL;
+	size_t identity_len = 0;
+	struct eap_sim_msg *msg;
+
+	data->reauth = 0;
+	if (id_req == ANY_ID && data->reauth_id) {
+		identity = data->reauth_id;
+		identity_len = data->reauth_id_len;
+		data->reauth = 1;
+	} else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) &&
+		   data->pseudonym) {
+		identity = data->pseudonym;
+		identity_len = data->pseudonym_len;
+		eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID);
+	} else if (id_req != NO_ID_REQ) {
+		identity = eap_get_config_identity(sm, &identity_len);
+		if (identity) {
+			eap_aka_clear_identities(sm, data, CLEAR_PSEUDONYM |
+						 CLEAR_REAUTH_ID);
+		}
+	}
+	if (id_req != NO_ID_REQ)
+		eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
+
+	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id);
+	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+			       EAP_AKA_SUBTYPE_IDENTITY);
+
+	if (identity) {
+		wpa_hexdump_ascii(MSG_DEBUG, "   AT_IDENTITY",
+				  identity, identity_len);
+		eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
+				identity, identity_len);
+	}
+
+	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data,
+						  u8 id)
+{
+	struct eap_sim_msg *msg;
+
+	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id);
+	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+			       EAP_AKA_SUBTYPE_CHALLENGE);
+	wpa_printf(MSG_DEBUG, "   AT_RES");
+	eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len * 8,
+			data->res, data->res_len);
+	eap_aka_add_checkcode(data, msg);
+	if (data->use_result_ind) {
+		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+	}
+	wpa_printf(MSG_DEBUG, "   AT_MAC");
+	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, (u8 *) "",
+				  0);
+}
+
+
+static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data,
+					       u8 id, int counter_too_small,
+					       const u8 *nonce_s)
+{
+	struct eap_sim_msg *msg;
+	unsigned int counter;
+
+	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)",
+		   id);
+	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+			       EAP_AKA_SUBTYPE_REAUTHENTICATION);
+	wpa_printf(MSG_DEBUG, "   AT_IV");
+	wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+	eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+	if (counter_too_small) {
+		wpa_printf(MSG_DEBUG, "   *AT_COUNTER_TOO_SMALL");
+		eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0);
+		counter = data->counter_too_small;
+	} else
+		counter = data->counter;
+
+	wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", counter);
+	eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+
+	if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
+			   "AT_ENCR_DATA");
+		eap_sim_msg_free(msg);
+		return NULL;
+	}
+	eap_aka_add_checkcode(data, msg);
+	if (data->use_result_ind) {
+		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+	}
+	wpa_printf(MSG_DEBUG, "   AT_MAC");
+	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, nonce_s,
+				  EAP_SIM_NONCE_S_LEN);
+}
+
+
+static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data,
+						     u8 id, u16 notification)
+{
+	struct eap_sim_msg *msg;
+	u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL;
+
+	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id);
+	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+			       EAP_AKA_SUBTYPE_NOTIFICATION);
+	if (k_aut && data->reauth) {
+		wpa_printf(MSG_DEBUG, "   AT_IV");
+		wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+		eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
+					   EAP_SIM_AT_ENCR_DATA);
+		wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", data->counter);
+		eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
+				NULL, 0);
+		if (eap_sim_msg_add_encr_end(msg, data->k_encr,
+					     EAP_SIM_AT_PADDING)) {
+			wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
+				   "AT_ENCR_DATA");
+			eap_sim_msg_free(msg);
+			return NULL;
+		}
+	}
+	if (k_aut) {
+		wpa_printf(MSG_DEBUG, "   AT_MAC");
+		eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+	}
+	return eap_sim_msg_finish(msg, data->eap_method, k_aut, (u8 *) "", 0);
+}
+
+
+static struct wpabuf * eap_aka_process_identity(struct eap_sm *sm,
+						struct eap_aka_data *data,
+						u8 id,
+						const struct wpabuf *reqData,
+						struct eap_sim_attrs *attr)
+{
+	int id_error;
+	struct wpabuf *buf;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity");
+
+	id_error = 0;
+	switch (attr->id_req) {
+	case NO_ID_REQ:
+		break;
+	case ANY_ID:
+		if (data->num_id_req > 0)
+			id_error++;
+		data->num_id_req++;
+		break;
+	case FULLAUTH_ID:
+		if (data->num_id_req > 1)
+			id_error++;
+		data->num_id_req++;
+		break;
+	case PERMANENT_ID:
+		if (data->num_id_req > 2)
+			id_error++;
+		data->num_id_req++;
+		break;
+	}
+	if (id_error) {
+		wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests "
+			   "used within one authentication");
+		return eap_aka_client_error(data, id,
+					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	buf = eap_aka_response_identity(sm, data, id, attr->id_req);
+
+	if (data->prev_id != id) {
+		eap_aka_add_id_msg(data, reqData);
+		eap_aka_add_id_msg(data, buf);
+		data->prev_id = id;
+	}
+
+	return buf;
+}
+
+
+static int eap_aka_verify_mac(struct eap_aka_data *data,
+			      const struct wpabuf *req,
+			      const u8 *mac, const u8 *extra,
+			      size_t extra_len)
+{
+	if (data->eap_method == EAP_TYPE_AKA_PRIME)
+		return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra,
+						 extra_len);
+	return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len);
+}
+
+
+#ifdef EAP_AKA_PRIME
+static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data,
+						u8 id, u16 kdf)
+{
+	struct eap_sim_msg *msg;
+
+	data->kdf_negotiation = 1;
+	data->kdf = kdf;
+	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF "
+		   "select)", id);
+	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+			       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, data->eap_method, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data,
+					     u8 id, struct eap_sim_attrs *attr)
+{
+	size_t i;
+
+	for (i = 0; i < attr->kdf_count; i++) {
+		if (attr->kdf[i] == EAP_AKA_PRIME_KDF)
+			return eap_aka_prime_kdf_select(data, id,
+							EAP_AKA_PRIME_KDF);
+	}
+
+	/* No matching KDF found - fail authentication as if AUTN had been
+	 * incorrect */
+	return eap_aka_authentication_reject(data, id);
+}
+
+
+static int eap_aka_prime_kdf_valid(struct eap_aka_data *data,
+				   struct eap_sim_attrs *attr)
+{
+	size_t i, j;
+
+	if (attr->kdf_count == 0)
+		return 0;
+
+	/* The only allowed (and required) duplication of a KDF is the addition
+	 * of the selected KDF into the beginning of the list. */
+
+	if (data->kdf_negotiation) {
+		if (attr->kdf[0] != data->kdf) {
+			wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
+				   "accept the selected KDF");
+			return 0;
+		}
+
+		for (i = 1; i < attr->kdf_count; i++) {
+			if (attr->kdf[i] == data->kdf)
+				break;
+		}
+		if (i == attr->kdf_count &&
+		    attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) {
+			wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
+				   "duplicate the selected KDF");
+			return 0;
+		}
+
+		/* TODO: should check that the list is identical to the one
+		 * used in the previous Challenge message apart from the added
+		 * entry in the beginning. */
+	}
+
+	for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) {
+		for (j = i + 1; j < attr->kdf_count; j++) {
+			if (attr->kdf[i] == attr->kdf[j]) {
+				wpa_printf(MSG_WARNING, "EAP-AKA': The server "
+					   "included a duplicated KDF");
+				return 0;
+			}
+		}
+	}
+
+	return 1;
+}
+#endif /* EAP_AKA_PRIME */
+
+
+static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
+						 struct eap_aka_data *data,
+						 u8 id,
+						 const struct wpabuf *reqData,
+						 struct eap_sim_attrs *attr)
+{
+	const u8 *identity;
+	size_t identity_len;
+	int res;
+	struct eap_sim_attrs eattr;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge");
+
+	if (attr->checkcode &&
+	    eap_aka_verify_checkcode(data, attr->checkcode,
+				     attr->checkcode_len)) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
+			   "message");
+		return eap_aka_client_error(data, id,
+					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+	}
+
+#ifdef EAP_AKA_PRIME
+	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+		if (!attr->kdf_input || attr->kdf_input_len == 0) {
+			wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message "
+				   "did not include non-empty AT_KDF_INPUT");
+			/* Fail authentication as if AUTN had been incorrect */
+			return eap_aka_authentication_reject(data, id);
+		}
+		os_free(data->network_name);
+		data->network_name = os_malloc(attr->kdf_input_len);
+		if (data->network_name == NULL) {
+			wpa_printf(MSG_WARNING, "EAP-AKA': No memory for "
+				   "storing Network Name");
+			return eap_aka_authentication_reject(data, id);
+		}
+		os_memcpy(data->network_name, attr->kdf_input,
+			  attr->kdf_input_len);
+		data->network_name_len = attr->kdf_input_len;
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name "
+				  "(AT_KDF_INPUT)",
+				  data->network_name, data->network_name_len);
+		/* TODO: check Network Name per 3GPP.33.402 */
+
+		if (!eap_aka_prime_kdf_valid(data, attr))
+			return eap_aka_authentication_reject(data, id);
+
+		if (attr->kdf[0] != EAP_AKA_PRIME_KDF)
+			return eap_aka_prime_kdf_neg(data, id, attr);
+
+		data->kdf = EAP_AKA_PRIME_KDF;
+		wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf);
+	}
+
+	if (data->eap_method == EAP_TYPE_AKA && attr->bidding) {
+		u16 flags = WPA_GET_BE16(attr->bidding);
+		if ((flags & EAP_AKA_BIDDING_FLAG_D) &&
+		    eap_allowed_method(sm, EAP_VENDOR_IETF,
+				       EAP_TYPE_AKA_PRIME)) {
+			wpa_printf(MSG_WARNING, "EAP-AKA: Bidding down from "
+				   "AKA' to AKA detected");
+			/* Fail authentication as if AUTN had been incorrect */
+			return eap_aka_authentication_reject(data, id);
+		}
+	}
+#endif /* EAP_AKA_PRIME */
+
+	data->reauth = 0;
+	if (!attr->mac || !attr->rand || !attr->autn) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
+			   "did not include%s%s%s",
+			   !attr->mac ? " AT_MAC" : "",
+			   !attr->rand ? " AT_RAND" : "",
+			   !attr->autn ? " AT_AUTN" : "");
+		return eap_aka_client_error(data, id,
+					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+	}
+	os_memcpy(data->rand, attr->rand, EAP_AKA_RAND_LEN);
+	os_memcpy(data->autn, attr->autn, EAP_AKA_AUTN_LEN);
+
+	res = eap_aka_umts_auth(sm, data);
+	if (res == -1) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
+			   "failed (AUTN)");
+		return eap_aka_authentication_reject(data, id);
+	} else if (res == -2) {
+		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,
+					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+	}
+#ifdef EAP_AKA_PRIME
+	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+		/* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the
+		 * needed 6-octet SQN ^ AK for CK',IK' derivation */
+		u16 amf = WPA_GET_BE16(data->autn + 6);
+		if (!(amf & 0x8000)) {
+			wpa_printf(MSG_WARNING, "EAP-AKA': AMF separation bit "
+				   "not set (AMF=0x%4x)", amf);
+			return eap_aka_authentication_reject(data, id);
+		}
+		eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik,
+						 data->autn,
+						 data->network_name,
+						 data->network_name_len);
+	}
+#endif /* EAP_AKA_PRIME */
+	if (data->last_eap_identity) {
+		identity = data->last_eap_identity;
+		identity_len = data->last_eap_identity_len;
+	} else if (data->pseudonym) {
+		identity = data->pseudonym;
+		identity_len = data->pseudonym_len;
+	} else
+		identity = eap_get_config_identity(sm, &identity_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK "
+			  "derivation", identity, identity_len);
+	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+		eap_aka_prime_derive_keys(identity, identity_len, data->ik,
+					  data->ck, data->k_encr, data->k_aut,
+					  data->k_re, data->msk, data->emsk);
+	} else {
+		eap_aka_derive_mk(identity, identity_len, data->ik, data->ck,
+				  data->mk);
+		eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
+				    data->msk, data->emsk);
+	}
+	if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
+			   "used invalid AT_MAC");
+		return eap_aka_client_error(data, id,
+					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	/* Old reauthentication identity must not be used anymore. In
+	 * other words, if no new identities are received, full
+	 * authentication will be used on next reauthentication (using
+	 * pseudonym identity or permanent identity). */
+	eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+
+	if (attr->encr_data) {
+		u8 *decrypted;
+		decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+					       attr->encr_data_len, attr->iv,
+					       &eattr, 0);
+		if (decrypted == NULL) {
+			return eap_aka_client_error(
+				data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+		}
+		eap_aka_learn_ids(sm, data, &eattr);
+		os_free(decrypted);
+	}
+
+	if (data->result_ind && attr->result_ind)
+		data->use_result_ind = 1;
+
+	if (data->state != FAILURE) {
+		eap_aka_state(data, data->use_result_ind ?
+			      RESULT_SUCCESS : SUCCESS);
+	}
+
+	data->num_id_req = 0;
+	data->num_notification = 0;
+	/* RFC 4187 specifies that counter is initialized to one after
+	 * fullauth, but initializing it to zero makes it easier to implement
+	 * reauth verification. */
+	data->counter = 0;
+	return eap_aka_response_challenge(data, id);
+}
+
+
+static int eap_aka_process_notification_reauth(struct eap_aka_data *data,
+					       struct eap_sim_attrs *attr)
+{
+	struct eap_sim_attrs eattr;
+	u8 *decrypted;
+
+	if (attr->encr_data == NULL || attr->iv == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after "
+			   "reauth did not include encrypted data");
+		return -1;
+	}
+
+	decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+				       attr->encr_data_len, attr->iv, &eattr,
+				       0);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
+			   "data from notification message");
+		return -1;
+	}
+
+	if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification "
+			   "message does not match with counter in reauth "
+			   "message");
+		os_free(decrypted);
+		return -1;
+	}
+
+	os_free(decrypted);
+	return 0;
+}
+
+
+static int eap_aka_process_notification_auth(struct eap_aka_data *data,
+					     const struct wpabuf *reqData,
+					     struct eap_sim_attrs *attr)
+{
+	if (attr->mac == NULL) {
+		wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth "
+			   "Notification message");
+		return -1;
+	}
+
+	if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Notification message "
+			   "used invalid AT_MAC");
+		return -1;
+	}
+
+	if (data->reauth &&
+	    eap_aka_process_notification_reauth(data, attr)) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification "
+			   "message after reauth");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_aka_process_notification(
+	struct eap_sm *sm, struct eap_aka_data *data, u8 id,
+	const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+	wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification");
+	if (data->num_notification > 0) {
+		wpa_printf(MSG_INFO, "EAP-AKA: too many notification "
+			   "rounds (only one allowed)");
+		return eap_aka_client_error(data, id,
+					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+	}
+	data->num_notification++;
+	if (attr->notification == -1) {
+		wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in "
+			   "Notification message");
+		return eap_aka_client_error(data, id,
+					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	if ((attr->notification & 0x4000) == 0 &&
+	    eap_aka_process_notification_auth(data, reqData, attr)) {
+		return eap_aka_client_error(data, id,
+					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	eap_sim_report_notification(sm->msg_ctx, attr->notification, 1);
+	if (attr->notification >= 0 && attr->notification < 32768) {
+		eap_aka_state(data, FAILURE);
+	} else if (attr->notification == EAP_SIM_SUCCESS &&
+		   data->state == RESULT_SUCCESS)
+		eap_aka_state(data, SUCCESS);
+	return eap_aka_response_notification(data, id, attr->notification);
+}
+
+
+static struct wpabuf * eap_aka_process_reauthentication(
+	struct eap_sm *sm, struct eap_aka_data *data, u8 id,
+	const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+	struct eap_sim_attrs eattr;
+	u8 *decrypted;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication");
+
+	if (attr->checkcode &&
+	    eap_aka_verify_checkcode(data, attr->checkcode,
+				     attr->checkcode_len)) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
+			   "message");
+		return eap_aka_client_error(data, id,
+					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	if (data->reauth_id == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying "
+			   "reauthentication, but no reauth_id available");
+		return eap_aka_client_error(data, id,
+					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	data->reauth = 1;
+	if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
+			   "did not have valid AT_MAC");
+		return eap_aka_client_error(data, id,
+					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	if (attr->encr_data == NULL || attr->iv == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
+			   "message did not include encrypted data");
+		return eap_aka_client_error(data, id,
+					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+				       attr->encr_data_len, attr->iv, &eattr,
+				       0);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
+			   "data from reauthentication message");
+		return eap_aka_client_error(data, id,
+					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	if (eattr.nonce_s == NULL || eattr.counter < 0) {
+		wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet",
+			   !eattr.nonce_s ? " AT_NONCE_S" : "",
+			   eattr.counter < 0 ? " AT_COUNTER" : "");
+		os_free(decrypted);
+		return eap_aka_client_error(data, id,
+					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) {
+		struct wpabuf *res;
+		wpa_printf(MSG_INFO, "EAP-AKA: (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
+		 * packet, it has to saved for the following fullauth to be
+		 * used in MK derivation. */
+		os_free(data->last_eap_identity);
+		data->last_eap_identity = data->reauth_id;
+		data->last_eap_identity_len = data->reauth_id_len;
+		data->reauth_id = NULL;
+		data->reauth_id_len = 0;
+
+		res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s);
+		os_free(decrypted);
+
+		return res;
+	}
+	data->counter = eattr.counter;
+
+	os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S",
+		    data->nonce_s, EAP_SIM_NONCE_S_LEN);
+
+	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+		eap_aka_prime_derive_keys_reauth(data->k_re, data->counter,
+						 data->reauth_id,
+						 data->reauth_id_len,
+						 data->nonce_s,
+						 data->msk, data->emsk);
+	} else {
+		eap_sim_derive_keys_reauth(data->counter, data->reauth_id,
+					   data->reauth_id_len,
+					   data->nonce_s, data->mk,
+					   data->msk, data->emsk);
+	}
+	eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+	eap_aka_learn_ids(sm, data, &eattr);
+
+	if (data->result_ind && attr->result_ind)
+		data->use_result_ind = 1;
+
+	if (data->state != FAILURE) {
+		eap_aka_state(data, data->use_result_ind ?
+			      RESULT_SUCCESS : SUCCESS);
+	}
+
+	data->num_id_req = 0;
+	data->num_notification = 0;
+	if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of "
+			   "fast reauths performed - force fullauth");
+		eap_aka_clear_identities(sm, data,
+					 CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+	}
+	os_free(decrypted);
+	return eap_aka_response_reauth(data, id, 0, data->nonce_s);
+}
+
+
+static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv,
+				       struct eap_method_ret *ret,
+				       const struct wpabuf *reqData)
+{
+	struct eap_aka_data *data = priv;
+	const struct eap_hdr *req;
+	u8 subtype, id;
+	struct wpabuf *res;
+	const u8 *pos;
+	struct eap_sim_attrs attr;
+	size_t len;
+
+	wpa_hexdump_buf(MSG_DEBUG, "EAP-AKA: EAP data", reqData);
+	if (eap_get_config_identity(sm, &len) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured");
+		eap_sm_request_identity(sm);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData,
+			       &len);
+	if (pos == NULL || len < 3) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	req = wpabuf_head(reqData);
+	id = req->identifier;
+	len = be_to_host16(req->length);
+
+	ret->ignore = FALSE;
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_FAIL;
+	ret->allowNotifications = TRUE;
+
+	subtype = *pos++;
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype);
+	pos += 2; /* Reserved */
+
+	if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr,
+			       data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1,
+			       0)) {
+		res = eap_aka_client_error(data, id,
+					   EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+		goto done;
+	}
+
+	switch (subtype) {
+	case EAP_AKA_SUBTYPE_IDENTITY:
+		res = eap_aka_process_identity(sm, data, id, reqData, &attr);
+		break;
+	case EAP_AKA_SUBTYPE_CHALLENGE:
+		res = eap_aka_process_challenge(sm, data, id, reqData, &attr);
+		break;
+	case EAP_AKA_SUBTYPE_NOTIFICATION:
+		res = eap_aka_process_notification(sm, data, id, reqData,
+						   &attr);
+		break;
+	case EAP_AKA_SUBTYPE_REAUTHENTICATION:
+		res = eap_aka_process_reauthentication(sm, data, id, reqData,
+						       &attr);
+		break;
+	case EAP_AKA_SUBTYPE_CLIENT_ERROR:
+		wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error");
+		res = eap_aka_client_error(data, id,
+					   EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype);
+		res = eap_aka_client_error(data, id,
+					   EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+		break;
+	}
+
+done:
+	if (data->state == FAILURE) {
+		ret->decision = DECISION_FAIL;
+		ret->methodState = METHOD_DONE;
+	} else if (data->state == SUCCESS) {
+		ret->decision = data->use_result_ind ?
+			DECISION_UNCOND_SUCC : DECISION_COND_SUCC;
+		/*
+		 * It is possible for the server to reply with AKA
+		 * Notification, so we must allow the method to continue and
+		 * not only accept EAP-Success at this point.
+		 */
+		ret->methodState = data->use_result_ind ?
+			METHOD_DONE : METHOD_MAY_CONT;
+	} else if (data->state == RESULT_SUCCESS)
+		ret->methodState = METHOD_CONT;
+
+	if (ret->methodState == METHOD_DONE) {
+		ret->allowNotifications = FALSE;
+	}
+
+	return res;
+}
+
+
+static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+	struct eap_aka_data *data = priv;
+	return data->pseudonym || data->reauth_id;
+}
+
+
+static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+	struct eap_aka_data *data = priv;
+	eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
+	data->prev_id = -1;
+	wpabuf_free(data->id_msgs);
+	data->id_msgs = NULL;
+	data->use_result_ind = 0;
+	data->kdf_negotiation = 0;
+	eap_aka_clear_keys(data, 1);
+}
+
+
+static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+	struct eap_aka_data *data = priv;
+	data->num_id_req = 0;
+	data->num_notification = 0;
+	eap_aka_state(data, CONTINUE);
+	return priv;
+}
+
+
+static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv,
+				       size_t *len)
+{
+	struct eap_aka_data *data = priv;
+
+	if (data->reauth_id) {
+		*len = data->reauth_id_len;
+		return data->reauth_id;
+	}
+
+	if (data->pseudonym) {
+		*len = data->pseudonym_len;
+		return data->pseudonym;
+	}
+
+	return NULL;
+}
+
+
+static Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_aka_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_aka_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_SIM_KEYING_DATA_LEN;
+	os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+
+	return key;
+}
+
+
+static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_aka_data *data = priv;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	*len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN;
+	id = os_malloc(*len);
+	if (id == NULL)
+		return NULL;
+
+	id[0] = data->eap_method;
+	os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN);
+	os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len);
+
+	return id;
+}
+
+
+static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_aka_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_EMSK_LEN;
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+	return key;
+}
+
+
+int eap_peer_aka_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_aka_init;
+	eap->deinit = eap_aka_deinit;
+	eap->process = eap_aka_process;
+	eap->isKeyAvailable = eap_aka_isKeyAvailable;
+	eap->getKey = eap_aka_getKey;
+	eap->getSessionId = eap_aka_get_session_id;
+	eap->has_reauth_data = eap_aka_has_reauth_data;
+	eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
+	eap->init_for_reauth = eap_aka_init_for_reauth;
+	eap->get_identity = eap_aka_get_identity;
+	eap->get_emsk = eap_aka_get_emsk;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
+
+
+#ifdef EAP_AKA_PRIME
+int eap_peer_aka_prime_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME,
+				    "AKA'");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_aka_prime_init;
+	eap->deinit = eap_aka_deinit;
+	eap->process = eap_aka_process;
+	eap->isKeyAvailable = eap_aka_isKeyAvailable;
+	eap->getKey = eap_aka_getKey;
+	eap->getSessionId = eap_aka_get_session_id;
+	eap->has_reauth_data = eap_aka_has_reauth_data;
+	eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
+	eap->init_for_reauth = eap_aka_init_for_reauth;
+	eap->get_identity = eap_aka_get_identity;
+	eap->get_emsk = eap_aka_get_emsk;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+
+	return ret;
+}
+#endif /* EAP_AKA_PRIME */
diff --git a/hostap/src/eap_peer/eap_config.h b/hostap/src/eap_peer/eap_config.h
new file mode 100644
index 0000000..2b1a1d5
--- /dev/null
+++ b/hostap/src/eap_peer/eap_config.h
@@ -0,0 +1,774 @@
+/*
+ * EAP peer configuration data
+ * 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.
+ */
+
+#ifndef EAP_CONFIG_H
+#define EAP_CONFIG_H
+
+/**
+ * struct eap_peer_config - EAP peer configuration/credentials
+ */
+struct eap_peer_config {
+	/**
+	 * identity - EAP Identity
+	 *
+	 * This field is used to set the real user identity or NAI (for
+	 * EAP-PSK/PAX/SAKE/GPSK).
+	 */
+	u8 *identity;
+
+	/**
+	 * identity_len - EAP Identity length
+	 */
+	size_t identity_len;
+
+	/**
+	 * anonymous_identity -  Anonymous EAP Identity
+	 *
+	 * This field is used for unencrypted use with EAP types that support
+	 * different tunnelled identity, e.g., EAP-TTLS, in order to reveal the
+	 * real identity (identity field) only to the authentication server.
+	 *
+	 * If not set, the identity field will be used for both unencrypted and
+	 * protected fields.
+	 *
+	 * This field can also be used with EAP-SIM/AKA/AKA' to store the
+	 * pseudonym identity.
+	 */
+	u8 *anonymous_identity;
+
+	/**
+	 * anonymous_identity_len - Length of anonymous_identity
+	 */
+	size_t anonymous_identity_len;
+
+	/**
+	 * password - Password string for EAP
+	 *
+	 * This field can include either the plaintext password (default
+	 * option) or a NtPasswordHash (16-byte MD4 hash of the unicode
+	 * presentation of the password) if flags field has
+	 * EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can
+	 * only be used with authentication mechanism that use this hash as the
+	 * starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2,
+	 * EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
+	 *
+	 * In addition, this field is used to configure a pre-shared key for
+	 * EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK
+	 * and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length
+	 * PSK.
+	 */
+	u8 *password;
+
+	/**
+	 * password_len - Length of password field
+	 */
+	size_t password_len;
+
+	/**
+	 * ca_cert - File path to CA certificate file (PEM/DER)
+	 *
+	 * This file can have one or more trusted CA certificates. If ca_cert
+	 * and ca_path are not included, server certificate will not be
+	 * verified. This is insecure and a trusted CA certificate should
+	 * always be configured when using EAP-TLS/TTLS/PEAP. Full path to the
+	 * file should be used since working directory may change when
+	 * wpa_supplicant is run in the background.
+	 *
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 *
+	 * Alternatively, this can be used to only perform matching of the
+	 * server certificate (SHA-256 hash of the DER encoded X.509
+	 * certificate). In this case, the possible CA certificates in the
+	 * server certificate chain are ignored and only the server certificate
+	 * is verified. This is configured with the following format:
+	 * hash:://server/sha256/cert_hash_in_hex
+	 * For example: "hash://server/sha256/
+	 * 5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a"
+	 *
+	 * On Windows, trusted CA certificates can be loaded from the system
+	 * certificate store by setting this to cert_store://name, e.g.,
+	 * ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT".
+	 * Note that when running wpa_supplicant as an application, the user
+	 * certificate store (My user account) is used, whereas computer store
+	 * (Computer account) is used when running wpasvc as a service.
+	 */
+	u8 *ca_cert;
+
+	/**
+	 * ca_path - Directory path for CA certificate files (PEM)
+	 *
+	 * This path may contain multiple CA certificates in OpenSSL format.
+	 * Common use for this is to point to system trusted CA list which is
+	 * often installed into directory like /etc/ssl/certs. If configured,
+	 * these certificates are added to the list of trusted CAs. ca_cert
+	 * may also be included in that case, but it is not required.
+	 */
+	u8 *ca_path;
+
+	/**
+	 * client_cert - File path to client certificate file (PEM/DER)
+	 *
+	 * This field is used with EAP method that use TLS authentication.
+	 * Usually, this is only configured for EAP-TLS, even though this could
+	 * in theory be used with EAP-TTLS and EAP-PEAP, too. Full path to the
+	 * file should be used since working directory may change when
+	 * wpa_supplicant is run in the background.
+	 *
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
+	u8 *client_cert;
+
+	/**
+	 * private_key - File path to client private key file (PEM/DER/PFX)
+	 *
+	 * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+	 * commented out. Both the private key and certificate will be read
+	 * from the PKCS#12 file in this case. Full path to the file should be
+	 * used since working directory may change when wpa_supplicant is run
+	 * in the background.
+	 *
+	 * Windows certificate store can be used by leaving client_cert out and
+	 * configuring private_key in one of the following formats:
+	 *
+	 * cert://substring_to_match
+	 *
+	 * hash://certificate_thumbprint_in_hex
+	 *
+	 * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+	 *
+	 * Note that when running wpa_supplicant as an application, the user
+	 * certificate store (My user account) is used, whereas computer store
+	 * (Computer account) is used when running wpasvc as a service.
+	 *
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
+	u8 *private_key;
+
+	/**
+	 * private_key_passwd - Password for private key file
+	 *
+	 * If left out, this will be asked through control interface.
+	 */
+	char *private_key_passwd;
+
+	/**
+	 * dh_file - File path to DH/DSA parameters file (in PEM format)
+	 *
+	 * This is an optional configuration file for setting parameters for an
+	 * ephemeral DH key exchange. In most cases, the default RSA
+	 * authentication does not use this configuration. However, it is
+	 * possible setup RSA to use ephemeral DH key exchange. In addition,
+	 * ciphers with DSA keys always use ephemeral DH keys. This can be used
+	 * to achieve forward secrecy. If the file is in DSA parameters format,
+	 * it will be automatically converted into DH params. Full path to the
+	 * file should be used since working directory may change when
+	 * wpa_supplicant is run in the background.
+	 *
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
+	u8 *dh_file;
+
+	/**
+	 * subject_match - Constraint for server certificate subject
+	 *
+	 * This substring is matched against the subject of the authentication
+	 * server certificate. If this string is set, the server sertificate is
+	 * only accepted if it contains this string in the subject. The subject
+	 * string is in following format:
+	 *
+	 * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com
+	 *
+	 * Note: Since this is a substring match, this cannot be used securily
+	 * to do a suffix match against a possible domain name in the CN entry.
+	 * For such a use case, domain_suffix_match should be used instead.
+	 */
+	u8 *subject_match;
+
+	/**
+	 * altsubject_match - Constraint for server certificate alt. subject
+	 *
+	 * Semicolon separated string of entries to be matched against the
+	 * alternative subject name of the authentication server certificate.
+	 * If this string is set, the server sertificate is only accepted if it
+	 * contains one of the entries in an alternative subject name
+	 * extension.
+	 *
+	 * altSubjectName string is in following format: TYPE:VALUE
+	 *
+	 * Example: EMAIL:server@example.com
+	 * Example: DNS:server.example.com;DNS:server2.example.com
+	 *
+	 * Following types are supported: EMAIL, DNS, URI
+	 */
+	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 SubjectName 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_match - Constraint for server domain name
+	 *
+	 * If set, this FQDN is used as a full 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 SubjectName CN
+	 * using same full match comparison. This behavior is similar to
+	 * domain_suffix_match, but has the requirement of a full match, i.e.,
+	 * no subdomains or wildcard matches are allowed. Case-insensitive
+	 * comparison is used, so "Example.com" matches "example.com", but would
+	 * not match "test.Example.com".
+	 */
+	char *domain_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
+	 * and ca_path2 are not included, server certificate will not be
+	 * verified. This is insecure and a trusted CA certificate should
+	 * always be configured. Full path to the file should be used since
+	 * working directory may change when wpa_supplicant is run in the
+	 * background.
+	 *
+	 * This field is like ca_cert, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 *
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
+	u8 *ca_cert2;
+
+	/**
+	 * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2)
+	 *
+	 * This path may contain multiple CA certificates in OpenSSL format.
+	 * Common use for this is to point to system trusted CA list which is
+	 * often installed into directory like /etc/ssl/certs. If configured,
+	 * these certificates are added to the list of trusted CAs. ca_cert
+	 * may also be included in that case, but it is not required.
+	 *
+	 * This field is like ca_path, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 */
+	u8 *ca_path2;
+
+	/**
+	 * client_cert2 - File path to client certificate file
+	 *
+	 * This field is like client_cert, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+	 * file should be used since working directory may change when
+	 * wpa_supplicant is run in the background.
+	 *
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
+	u8 *client_cert2;
+
+	/**
+	 * private_key2 - File path to client private key file
+	 *
+	 * This field is like private_key, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+	 * file should be used since working directory may change when
+	 * wpa_supplicant is run in the background.
+	 *
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
+	u8 *private_key2;
+
+	/**
+	 * private_key2_passwd -  Password for private key file
+	 *
+	 * This field is like private_key_passwd, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 */
+	char *private_key2_passwd;
+
+	/**
+	 * dh_file2 - File path to DH/DSA parameters file (in PEM format)
+	 *
+	 * This field is like dh_file, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+	 * file should be used since working directory may change when
+	 * wpa_supplicant is run in the background.
+	 *
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
+	u8 *dh_file2;
+
+	/**
+	 * subject_match2 - Constraint for server certificate subject
+	 *
+	 * This field is like subject_match, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 */
+	u8 *subject_match2;
+
+	/**
+	 * altsubject_match2 - Constraint for server certificate alt. subject
+	 *
+	 * This field is like altsubject_match, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 */
+	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;
+
+	/**
+	 * domain_match2 - Constraint for server domain name
+	 *
+	 * This field is like domain_match, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 */
+	char *domain_match2;
+
+	/**
+	 * eap_methods - Allowed EAP methods
+	 *
+	 * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of
+	 * allowed EAP methods or %NULL if all methods are accepted.
+	 */
+	struct eap_method_type *eap_methods;
+
+	/**
+	 * phase1 - Phase 1 (outer authentication) parameters
+	 *
+	 * String with field-value pairs, e.g., "peapver=0" or
+	 * "peapver=1 peaplabel=1".
+	 *
+	 * 'peapver' can be used to force which PEAP version (0 or 1) is used.
+	 *
+	 * 'peaplabel=1' can be used to force new label, "client PEAP
+	 * encryption",	to be used during key derivation when PEAPv1 or newer.
+	 *
+	 * Most existing PEAPv1 implementation seem to be using the old label,
+	 * "client EAP encryption", and wpa_supplicant is now using that as the
+	 * default value.
+	 *
+	 * Some servers, e.g., Radiator, may require peaplabel=1 configuration
+	 * to interoperate with PEAPv1; see eap_testing.txt for more details.
+	 *
+	 * 'peap_outer_success=0' can be used to terminate PEAP authentication
+	 * on tunneled EAP-Success. This is required with some RADIUS servers
+	 * that implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g.,
+	 * Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode).
+	 *
+	 * include_tls_length=1 can be used to force wpa_supplicant to include
+	 * TLS Message Length field in all TLS messages even if they are not
+	 * fragmented.
+	 *
+	 * sim_min_num_chal=3 can be used to configure EAP-SIM to require three
+	 * challenges (by default, it accepts 2 or 3).
+	 *
+	 * result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use
+	 * protected result indication.
+	 *
+	 * fast_provisioning option can be used to enable in-line provisioning
+	 * of EAP-FAST credentials (PAC):
+	 * 0 = disabled,
+	 * 1 = allow unauthenticated provisioning,
+	 * 2 = allow authenticated provisioning,
+	 * 3 = allow both unauthenticated and authenticated provisioning
+	 *
+	 * fast_max_pac_list_len=num option can be used to set the maximum
+	 * number of PAC entries to store in a PAC list (default: 10).
+	 *
+	 * fast_pac_format=binary option can be used to select binary format
+	 * for storing PAC entries in order to save some space (the default
+	 * text format uses about 2.5 times the size of minimal binary format).
+	 *
+	 * crypto_binding option can be used to control PEAPv0 cryptobinding
+	 * behavior:
+	 * 0 = do not use cryptobinding (default)
+	 * 1 = use cryptobinding if server supports it
+	 * 2 = require cryptobinding
+	 *
+	 * EAP-WSC (WPS) uses following options: pin=Device_Password and
+	 * uuid=Device_UUID
+	 *
+	 * For wired IEEE 802.1X authentication, "allow_canned_success=1" can be
+	 * used to configure a mode that allows EAP-Success (and EAP-Failure)
+	 * without going through authentication step. Some switches use such
+	 * sequence when forcing the port to be authorized/unauthorized or as a
+	 * fallback option if the authentication server is unreachable. By
+	 * default, wpa_supplicant discards such frames to protect against
+	 * potential attacks by rogue devices, but this option can be used to
+	 * disable that protection for cases where the server/authenticator does
+	 * not need to be authenticated.
+	 */
+	char *phase1;
+
+	/**
+	 * phase2 - Phase2 (inner authentication with TLS tunnel) parameters
+	 *
+	 * String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
+	 * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS. "mschapv2_retry=0" can
+	 * be used to disable MSCHAPv2 password retry in authentication failure
+	 * cases.
+	 */
+	char *phase2;
+
+	/**
+	 * pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM
+	 *
+	 * This field is used to configure PC/SC smartcard interface.
+	 * Currently, the only configuration is whether this field is %NULL (do
+	 * not use PC/SC) or non-NULL (e.g., "") to enable PC/SC.
+	 *
+	 * This field is used for EAP-SIM and EAP-AKA.
+	 */
+	char *pcsc;
+
+	/**
+	 * pin - PIN for USIM, GSM SIM, and smartcards
+	 *
+	 * This field is used to configure PIN for SIM and smartcards for
+	 * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
+	 * smartcard is used for private key operations.
+	 *
+	 * If left out, this will be asked through control interface.
+	 */
+	char *pin;
+
+	/**
+	 * engine - Enable OpenSSL engine (e.g., for smartcard access)
+	 *
+	 * This is used if private key operations for EAP-TLS are performed
+	 * using a smartcard.
+	 */
+	int engine;
+
+	/**
+	 * engine_id - Engine ID for OpenSSL engine
+	 *
+	 * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
+	 * engine.
+	 *
+	 * This is used if private key operations for EAP-TLS are performed
+	 * using a smartcard.
+	 */
+	char *engine_id;
+
+	/**
+	 * engine2 - Enable OpenSSL engine (e.g., for smartcard) (Phase 2)
+	 *
+	 * This is used if private key operations for EAP-TLS are performed
+	 * using a smartcard.
+	 *
+	 * This field is like engine, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 */
+	int engine2;
+
+
+	/**
+	 * pin2 - PIN for USIM, GSM SIM, and smartcards (Phase 2)
+	 *
+	 * This field is used to configure PIN for SIM and smartcards for
+	 * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
+	 * smartcard is used for private key operations.
+	 *
+	 * This field is like pin2, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 *
+	 * If left out, this will be asked through control interface.
+	 */
+	char *pin2;
+
+	/**
+	 * engine2_id - Engine ID for OpenSSL engine (Phase 2)
+	 *
+	 * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
+	 * engine.
+	 *
+	 * This is used if private key operations for EAP-TLS are performed
+	 * using a smartcard.
+	 *
+	 * This field is like engine_id, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 */
+	char *engine2_id;
+
+
+	/**
+	 * key_id - Key ID for OpenSSL engine
+	 *
+	 * This is used if private key operations for EAP-TLS are performed
+	 * using a smartcard.
+	 */
+	char *key_id;
+
+	/**
+	 * cert_id - Cert ID for OpenSSL engine
+	 *
+	 * This is used if the certificate operations for EAP-TLS are performed
+	 * using a smartcard.
+	 */
+	char *cert_id;
+
+	/**
+	 * ca_cert_id - CA Cert ID for OpenSSL engine
+	 *
+	 * This is used if the CA certificate for EAP-TLS is on a smartcard.
+	 */
+	char *ca_cert_id;
+
+	/**
+	 * key2_id - Key ID for OpenSSL engine (phase2)
+	 *
+	 * This is used if private key operations for EAP-TLS are performed
+	 * using a smartcard.
+	 */
+	char *key2_id;
+
+	/**
+	 * cert2_id - Cert ID for OpenSSL engine (phase2)
+	 *
+	 * This is used if the certificate operations for EAP-TLS are performed
+	 * using a smartcard.
+	 */
+	char *cert2_id;
+
+	/**
+	 * ca_cert2_id - CA Cert ID for OpenSSL engine (phase2)
+	 *
+	 * This is used if the CA certificate for EAP-TLS is on a smartcard.
+	 */
+	char *ca_cert2_id;
+
+	/**
+	 * otp - One-time-password
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when OTP is entered through the control interface.
+	 */
+	u8 *otp;
+
+	/**
+	 * otp_len - Length of the otp field
+	 */
+	size_t otp_len;
+
+	/**
+	 * pending_req_identity - Whether there is a pending identity request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_identity;
+
+	/**
+	 * pending_req_password - Whether there is a pending password request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_password;
+
+	/**
+	 * pending_req_pin - Whether there is a pending PIN request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_pin;
+
+	/**
+	 * pending_req_new_password - Pending password update request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_new_password;
+
+	/**
+	 * pending_req_passphrase - Pending passphrase request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_passphrase;
+
+	/**
+	 * pending_req_otp - Whether there is a pending OTP request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	char *pending_req_otp;
+
+	/**
+	 * pending_req_otp_len - Length of the pending OTP request
+	 */
+	size_t pending_req_otp_len;
+
+	/**
+	 * pac_file - File path or blob name for the PAC entries (EAP-FAST)
+	 *
+	 * wpa_supplicant will need to be able to create this file and write
+	 * updates to it when PAC is being provisioned or refreshed. Full path
+	 * to the file should be used since working directory may change when
+	 * wpa_supplicant is run in the background.
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
+	char *pac_file;
+
+	/**
+	 * mschapv2_retry - MSCHAPv2 retry in progress
+	 *
+	 * This field is used internally by EAP-MSCHAPv2 and should not be set
+	 * as part of configuration.
+	 */
+	int mschapv2_retry;
+
+	/**
+	 * new_password - New password for password update
+	 *
+	 * This field is used during MSCHAPv2 password update. This is normally
+	 * requested from the user through the control interface and not set
+	 * from configuration.
+	 */
+	u8 *new_password;
+
+	/**
+	 * new_password_len - Length of new_password field
+	 */
+	size_t new_password_len;
+
+	/**
+	 * fragment_size - Maximum EAP fragment size in bytes (default 1398)
+	 *
+	 * This value limits the fragment size for EAP methods that support
+	 * fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set
+	 * small enough to make the EAP messages fit in MTU of the network
+	 * interface used for EAPOL. The default value is suitable for most
+	 * cases.
+	 */
+	int fragment_size;
+
+#define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0)
+#define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1)
+	/**
+	 * flags - Network configuration flags (bitfield)
+	 *
+	 * This variable is used for internal flags to describe further details
+	 * for the network parameters.
+	 * bit 0 = password is represented as a 16-byte NtPasswordHash value
+	 *         instead of plaintext password
+	 * bit 1 = password is stored in external storage; the value in the
+	 *         password field is the name of that external entry
+	 */
+	u32 flags;
+
+	/**
+	 * ocsp - Whether to use/require OCSP to check server certificate
+	 *
+	 * 0 = do not use OCSP stapling (TLS certificate status extension)
+	 * 1 = try to use OCSP stapling, but not require response
+	 * 2 = require valid OCSP stapling response
+	 */
+	int ocsp;
+
+	/**
+	 * external_sim_resp - Response from external SIM processing
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * 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;
+
+	/**
+	 * openssl_ciphers - OpenSSL cipher string
+	 *
+	 * This is an OpenSSL specific configuration option for configuring the
+	 * ciphers for this connection. If not set, the default cipher suite
+	 * list is used.
+	 */
+	char *openssl_ciphers;
+
+	/**
+	 * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
+	 */
+	int erp;
+};
+
+
+/**
+ * struct wpa_config_blob - Named configuration blob
+ *
+ * This data structure is used to provide storage for binary objects to store
+ * abstract information like certificates and private keys inlined with the
+ * configuration data.
+ */
+struct wpa_config_blob {
+	/**
+	 * name - Blob name
+	 */
+	char *name;
+
+	/**
+	 * data - Pointer to binary data
+	 */
+	u8 *data;
+
+	/**
+	 * len - Length of binary data
+	 */
+	size_t len;
+
+	/**
+	 * next - Pointer to next blob in the configuration
+	 */
+	struct wpa_config_blob *next;
+};
+
+#endif /* EAP_CONFIG_H */
diff --git a/hostap/src/eap_peer/eap_eke.c b/hostap/src/eap_peer/eap_eke.c
new file mode 100644
index 0000000..dfbda56
--- /dev/null
+++ b/hostap/src/eap_peer/eap_eke.c
@@ -0,0 +1,792 @@
+/*
+ * EAP peer method: EAP-EKE (RFC 6124)
+ * Copyright (c) 2013, 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/random.h"
+#include "eap_peer/eap_i.h"
+#include "eap_common/eap_eke_common.h"
+
+struct eap_eke_data {
+	enum {
+		IDENTITY, COMMIT, CONFIRM, SUCCESS, FAILURE
+	} state;
+	u8 msk[EAP_MSK_LEN];
+	u8 emsk[EAP_EMSK_LEN];
+	u8 *peerid;
+	size_t peerid_len;
+	u8 *serverid;
+	size_t serverid_len;
+	u8 dh_priv[EAP_EKE_MAX_DH_LEN];
+	struct eap_eke_session sess;
+	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 */
+};
+
+
+static const char * eap_eke_state_txt(int state)
+{
+	switch (state) {
+	case IDENTITY:
+		return "IDENTITY";
+	case COMMIT:
+		return "COMMIT";
+	case CONFIRM:
+		return "CONFIRM";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "?";
+	}
+}
+
+
+static void eap_eke_state(struct eap_eke_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s",
+		   eap_eke_state_txt(data->state), eap_eke_state_txt(state));
+	data->state = state;
+}
+
+
+static void eap_eke_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_eke_init(struct eap_sm *sm)
+{
+	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) {
+		wpa_printf(MSG_INFO, "EAP-EKE: No password configured");
+		return NULL;
+	}
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	eap_eke_state(data, IDENTITY);
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	if (identity) {
+		data->peerid = os_malloc(identity_len);
+		if (data->peerid == NULL) {
+			eap_eke_deinit(sm, data);
+			return NULL;
+		}
+		os_memcpy(data->peerid, identity, identity_len);
+		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;
+}
+
+
+static void eap_eke_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_eke_data *data = priv;
+	eap_eke_session_clean(&data->sess);
+	os_free(data->serverid);
+	os_free(data->peerid);
+	wpabuf_free(data->msgs);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data, int id,
+					 size_t length, u8 eke_exch)
+{
+	struct wpabuf *msg;
+	size_t plen;
+
+	plen = 1 + length;
+
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen,
+			    EAP_CODE_RESPONSE, id);
+	if (msg == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory");
+		return NULL;
+	}
+
+	wpabuf_put_u8(msg, eke_exch);
+
+	return msg;
+}
+
+
+static int eap_eke_supp_dhgroup(u8 dhgroup)
+{
+	return dhgroup == EAP_EKE_DHGROUP_EKE_2 ||
+		dhgroup == EAP_EKE_DHGROUP_EKE_5 ||
+		dhgroup == EAP_EKE_DHGROUP_EKE_14 ||
+		dhgroup == EAP_EKE_DHGROUP_EKE_15 ||
+		dhgroup == EAP_EKE_DHGROUP_EKE_16;
+}
+
+
+static int eap_eke_supp_encr(u8 encr)
+{
+	return encr == EAP_EKE_ENCR_AES128_CBC;
+}
+
+
+static int eap_eke_supp_prf(u8 prf)
+{
+	return prf == EAP_EKE_PRF_HMAC_SHA1 ||
+		prf == EAP_EKE_PRF_HMAC_SHA2_256;
+}
+
+
+static int eap_eke_supp_mac(u8 mac)
+{
+	return mac == EAP_EKE_MAC_HMAC_SHA1 ||
+		mac == EAP_EKE_MAC_HMAC_SHA2_256;
+}
+
+
+static struct wpabuf * eap_eke_build_fail(struct eap_eke_data *data,
+					  struct eap_method_ret *ret,
+					  u8 id, u32 failure_code)
+{
+	struct wpabuf *resp;
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Failure/Response - code=0x%x",
+		   failure_code);
+
+	resp = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE);
+	if (resp)
+		wpabuf_put_be32(resp, failure_code);
+
+	os_memset(data->dh_priv, 0, sizeof(data->dh_priv));
+	eap_eke_session_clean(&data->sess);
+
+	eap_eke_state(data, FAILURE);
+	ret->methodState = METHOD_DONE;
+	ret->decision = DECISION_FAIL;
+	ret->allowNotifications = FALSE;
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data,
+					  struct eap_method_ret *ret,
+					  const struct wpabuf *reqData,
+					  const u8 *payload,
+					  size_t payload_len)
+{
+	struct wpabuf *resp;
+	unsigned num_prop, i;
+	const u8 *pos, *end;
+	const u8 *prop = NULL;
+	u8 idtype;
+	u8 id = eap_get_id(reqData);
+
+	if (data->state != IDENTITY) {
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PROTO_ERROR);
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-ID/Request");
+
+	if (payload_len < 2 + 4) {
+		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data");
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PROTO_ERROR);
+	}
+
+	pos = payload;
+	end = payload + payload_len;
+
+	num_prop = *pos++;
+	pos++; /* Ignore Reserved field */
+
+	if (pos + num_prop * 4 > end) {
+		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data (num_prop=%u)",
+			   num_prop);
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PROTO_ERROR);
+	}
+
+	for (i = 0; i < num_prop; i++) {
+		const u8 *tmp = pos;
+
+		wpa_printf(MSG_DEBUG, "EAP-EKE: Proposal #%u: dh=%u encr=%u prf=%u mac=%u",
+			   i, pos[0], pos[1], pos[2], pos[3]);
+		pos += 4;
+
+		if ((data->dhgroup && data->dhgroup != *tmp) ||
+		    !eap_eke_supp_dhgroup(*tmp))
+			continue;
+		tmp++;
+		if ((data->encr && data->encr != *tmp) ||
+		    !eap_eke_supp_encr(*tmp))
+			continue;
+		tmp++;
+		if ((data->prf && data->prf != *tmp) ||
+		    !eap_eke_supp_prf(*tmp))
+			continue;
+		tmp++;
+		if ((data->mac && data->mac != *tmp) ||
+		    !eap_eke_supp_mac(*tmp))
+			continue;
+
+		prop = tmp - 3;
+		if (eap_eke_session_init(&data->sess, prop[0], prop[1], prop[2],
+					 prop[3]) < 0) {
+			prop = NULL;
+			continue;
+		}
+
+		wpa_printf(MSG_DEBUG, "EAP-EKE: Selected proposal");
+		break;
+	}
+
+	if (prop == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-EKE: No acceptable proposal found");
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN);
+	}
+
+	pos += (num_prop - i - 1) * 4;
+
+	if (pos == end) {
+		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data to include IDType/Identity");
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PROTO_ERROR);
+	}
+
+	idtype = *pos++;
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Server IDType %u", idtype);
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Server Identity",
+			  pos, end - pos);
+	os_free(data->serverid);
+	data->serverid = os_malloc(end - pos);
+	if (data->serverid == NULL) {
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+	os_memcpy(data->serverid, pos, end - pos);
+	data->serverid_len = end - pos;
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response");
+
+	resp = eap_eke_build_msg(data, id,
+				 2 + 4 + 1 + data->peerid_len,
+				 EAP_EKE_ID);
+	if (resp == NULL) {
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+
+	wpabuf_put_u8(resp, 1); /* NumProposals */
+	wpabuf_put_u8(resp, 0); /* Reserved */
+	wpabuf_put_data(resp, prop, 4); /* Selected Proposal */
+	wpabuf_put_u8(resp, EAP_EKE_ID_NAI);
+	if (data->peerid)
+		wpabuf_put_data(resp, data->peerid, data->peerid_len);
+
+	wpabuf_free(data->msgs);
+	data->msgs = wpabuf_alloc(wpabuf_len(reqData) + wpabuf_len(resp));
+	if (data->msgs == NULL) {
+		wpabuf_free(resp);
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+	wpabuf_put_buf(data->msgs, reqData);
+	wpabuf_put_buf(data->msgs, resp);
+
+	eap_eke_state(data, COMMIT);
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm,
+					      struct eap_eke_data *data,
+					      struct eap_method_ret *ret,
+					      const struct wpabuf *reqData,
+					      const u8 *payload,
+					      size_t payload_len)
+{
+	struct wpabuf *resp;
+	const u8 *pos, *end, *dhcomp;
+	size_t prot_len;
+	u8 *rpos;
+	u8 key[EAP_EKE_MAX_KEY_LEN];
+	u8 pub[EAP_EKE_MAX_DH_LEN];
+	const u8 *password;
+	size_t password_len;
+	u8 id = eap_get_id(reqData);
+
+	if (data->state != COMMIT) {
+		wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Commit/Request received in unexpected state (%d)", data->state);
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PROTO_ERROR);
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Commit/Request");
+
+	password = eap_get_config_password(sm, &password_len);
+	if (password == NULL) {
+		wpa_printf(MSG_INFO, "EAP-EKE: No password configured!");
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PASSWD_NOT_FOUND);
+	}
+
+	pos = payload;
+	end = payload + payload_len;
+
+	if (pos + data->sess.dhcomp_len > end) {
+		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit");
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PROTO_ERROR);
+	}
+
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_S",
+		    pos, data->sess.dhcomp_len);
+	dhcomp = pos;
+	pos += data->sess.dhcomp_len;
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos);
+
+	/*
+	 * temp = prf(0+, password)
+	 * key = prf+(temp, ID_S | ID_P)
+	 */
+	if (eap_eke_derive_key(&data->sess, password, password_len,
+			       data->serverid, data->serverid_len,
+			       data->peerid, data->peerid_len, key) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key");
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+
+	/*
+	 * y_p = g ^ x_p (mod p)
+	 * x_p = random number 2 .. p-1
+	 */
+	if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH");
+		os_memset(key, 0, sizeof(key));
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+
+	if (eap_eke_shared_secret(&data->sess, key, data->dh_priv, dhcomp) < 0)
+	{
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret");
+		os_memset(key, 0, sizeof(key));
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+
+	if (eap_eke_derive_ke_ki(&data->sess,
+				 data->serverid, data->serverid_len,
+				 data->peerid, data->peerid_len) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki");
+		os_memset(key, 0, sizeof(key));
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Commit/Response");
+
+	resp = eap_eke_build_msg(data, id,
+				 data->sess.dhcomp_len + data->sess.pnonce_len,
+				 EAP_EKE_COMMIT);
+	if (resp == NULL) {
+		os_memset(key, 0, sizeof(key));
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+
+	/* 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_P");
+		os_memset(key, 0, sizeof(key));
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+	os_memset(key, 0, sizeof(key));
+
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P",
+		    rpos, data->sess.dhcomp_len);
+
+	if (random_get_bytes(data->nonce_p, data->sess.nonce_len)) {
+		wpabuf_free(resp);
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P",
+			data->nonce_p, data->sess.nonce_len);
+	prot_len = wpabuf_tailroom(resp);
+	if (eap_eke_prot(&data->sess, data->nonce_p, data->sess.nonce_len,
+			 wpabuf_put(resp, 0), &prot_len) < 0) {
+		wpabuf_free(resp);
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P",
+		    wpabuf_put(resp, 0), prot_len);
+	wpabuf_put(resp, prot_len);
+
+	/* TODO: CBValue */
+
+	if (wpabuf_resize(&data->msgs, wpabuf_len(reqData) + wpabuf_len(resp))
+	    < 0) {
+		wpabuf_free(resp);
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+	wpabuf_put_buf(data->msgs, reqData);
+	wpabuf_put_buf(data->msgs, resp);
+
+	eap_eke_state(data, CONFIRM);
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data,
+					       struct eap_method_ret *ret,
+					       const struct wpabuf *reqData,
+					       const u8 *payload,
+					       size_t payload_len)
+{
+	struct wpabuf *resp;
+	const u8 *pos, *end;
+	size_t prot_len;
+	u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN];
+	u8 auth_s[EAP_EKE_MAX_HASH_LEN];
+	size_t decrypt_len;
+	u8 *auth;
+	u8 id = eap_get_id(reqData);
+
+	if (data->state != CONFIRM) {
+		wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Confirm/Request received in unexpected state (%d)",
+			   data->state);
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PROTO_ERROR);
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Confirm/Request");
+
+	pos = payload;
+	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-Confirm");
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PROTO_ERROR);
+	}
+
+	decrypt_len = sizeof(nonces);
+	if (eap_eke_decrypt_prot(&data->sess, pos, data->sess.pnonce_ps_len,
+				 nonces, &decrypt_len) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_PS");
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+	}
+	if (decrypt_len != (size_t) 2 * data->sess.nonce_len) {
+		wpa_printf(MSG_INFO, "EAP-EKE: PNonce_PS protected data length does not match length of Nonce_P and Nonce_S");
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+	}
+	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 transmitted Nonce_P");
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+	}
+
+	os_memcpy(data->nonce_s, nonces + data->sess.nonce_len,
+		  data->sess.nonce_len);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S",
+			data->nonce_s, data->sess.nonce_len);
+
+	if (eap_eke_derive_ka(&data->sess, data->serverid, data->serverid_len,
+			      data->peerid, data->peerid_len,
+			      data->nonce_p, data->nonce_s) < 0) {
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+
+	if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth_s) < 0)
+	{
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth_s, data->sess.prf_len);
+	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, id,
+					  EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Confirm/Response");
+
+	resp = eap_eke_build_msg(data, id,
+				 data->sess.pnonce_len + data->sess.prf_len,
+				 EAP_EKE_CONFIRM);
+	if (resp == NULL) {
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+
+	prot_len = wpabuf_tailroom(resp);
+	if (eap_eke_prot(&data->sess, data->nonce_s, data->sess.nonce_len,
+			 wpabuf_put(resp, 0), &prot_len) < 0) {
+		wpabuf_free(resp);
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+	wpabuf_put(resp, prot_len);
+
+	auth = wpabuf_put(resp, data->sess.prf_len);
+	if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth) < 0) {
+		wpabuf_free(resp);
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth, data->sess.prf_len);
+
+	if (eap_eke_derive_msk(&data->sess, data->serverid, data->serverid_len,
+			       data->peerid, data->peerid_len,
+			       data->nonce_s, data->nonce_p,
+			       data->msk, data->emsk) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK");
+		wpabuf_free(resp);
+		return eap_eke_build_fail(data, ret, id,
+					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+	}
+
+	os_memset(data->dh_priv, 0, sizeof(data->dh_priv));
+	eap_eke_session_clean(&data->sess);
+
+	eap_eke_state(data, SUCCESS);
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_COND_SUCC;
+	ret->allowNotifications = FALSE;
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_eke_process_failure(struct eap_eke_data *data,
+					       struct eap_method_ret *ret,
+					       const struct wpabuf *reqData,
+					       const u8 *payload,
+					       size_t payload_len)
+{
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Failure/Request");
+
+	if (payload_len < 4) {
+		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure");
+	} else {
+		u32 code;
+		code = WPA_GET_BE32(payload);
+		wpa_printf(MSG_INFO, "EAP-EKE: Failure-Code 0x%x", code);
+	}
+
+	return eap_eke_build_fail(data, ret, eap_get_id(reqData),
+				  EAP_EKE_FAIL_NO_ERROR);
+}
+
+
+static struct wpabuf * eap_eke_process(struct eap_sm *sm, void *priv,
+				       struct eap_method_ret *ret,
+				       const struct wpabuf *reqData)
+{
+	struct eap_eke_data *data = priv;
+	struct wpabuf *resp;
+	const u8 *pos, *end;
+	size_t len;
+	u8 eke_exch;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, reqData, &len);
+	if (pos == NULL || len < 1) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	end = pos + len;
+	eke_exch = *pos++;
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: exch %d", eke_exch);
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received Data", pos, end - pos);
+
+	ret->ignore = FALSE;
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_FAIL;
+	ret->allowNotifications = TRUE;
+
+	switch (eke_exch) {
+	case EAP_EKE_ID:
+		resp = eap_eke_process_id(data, ret, reqData, pos, end - pos);
+		break;
+	case EAP_EKE_COMMIT:
+		resp = eap_eke_process_commit(sm, data, ret, reqData,
+					      pos, end - pos);
+		break;
+	case EAP_EKE_CONFIRM:
+		resp = eap_eke_process_confirm(data, ret, reqData,
+					       pos, end - pos);
+		break;
+	case EAP_EKE_FAILURE:
+		resp = eap_eke_process_failure(data, ret, reqData,
+					       pos, end - pos);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-EKE: Ignoring message with unknown EKE-Exch %d", eke_exch);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (ret->methodState == METHOD_DONE)
+		ret->allowNotifications = FALSE;
+
+	return resp;
+}
+
+
+static Boolean eap_eke_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_eke_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_eke_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_MSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->msk, EAP_MSK_LEN);
+	*len = EAP_MSK_LEN;
+
+	return key;
+}
+
+
+static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_eke_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+	*len = EAP_EMSK_LEN;
+
+	return key;
+}
+
+
+static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_eke_data *data = priv;
+	u8 *sid;
+	size_t sid_len;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	sid_len = 1 + 2 * data->sess.nonce_len;
+	sid = os_malloc(sid_len);
+	if (sid == NULL)
+		return NULL;
+	sid[0] = EAP_TYPE_EKE;
+	os_memcpy(sid + 1, data->nonce_p, data->sess.nonce_len);
+	os_memcpy(sid + 1 + data->sess.nonce_len, data->nonce_s,
+		  data->sess.nonce_len);
+	*len = sid_len;
+
+	return sid;
+}
+
+
+int eap_peer_eke_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_eke_init;
+	eap->deinit = eap_eke_deinit;
+	eap->process = eap_eke_process;
+	eap->isKeyAvailable = eap_eke_isKeyAvailable;
+	eap->getKey = eap_eke_getKey;
+	eap->get_emsk = eap_eke_get_emsk;
+	eap->getSessionId = eap_eke_get_session_id;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_fast.c b/hostap/src/eap_peer/eap_fast.c
new file mode 100644
index 0000000..4cbe3ba
--- /dev/null
+++ b/hostap/src/eap_peer/eap_fast.c
@@ -0,0 +1,1785 @@
+/*
+ * EAP peer method: EAP-FAST (RFC 4851)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/tls.h"
+#include "crypto/sha1.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+#include "eap_fast_pac.h"
+
+#ifdef EAP_FAST_DYNAMIC
+#include "eap_fast_pac.c"
+#endif /* EAP_FAST_DYNAMIC */
+
+/* TODO:
+ * - test session resumption and enable it if it interoperates
+ * - password change (pending mschapv2 packet; replay decrypted packet)
+ */
+
+
+static void eap_fast_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_fast_data {
+	struct eap_ssl_data ssl;
+
+	int fast_version;
+
+	const struct eap_method *phase2_method;
+	void *phase2_priv;
+	int phase2_success;
+
+	struct eap_method_type phase2_type;
+	struct eap_method_type *phase2_types;
+	size_t num_phase2_types;
+	int resuming; /* starting a resumed session */
+	struct eap_fast_key_block_provisioning *key_block_p;
+#define EAP_FAST_PROV_UNAUTH 1
+#define EAP_FAST_PROV_AUTH 2
+	int provisioning_allowed; /* Allowed PAC provisioning modes */
+	int provisioning; /* doing PAC provisioning (not the normal auth) */
+	int anon_provisioning; /* doing anonymous (unauthenticated)
+				* provisioning */
+	int session_ticket_used;
+
+	u8 key_data[EAP_FAST_KEY_LEN];
+	u8 *session_id;
+	size_t id_len;
+	u8 emsk[EAP_EMSK_LEN];
+	int success;
+
+	struct eap_fast_pac *pac;
+	struct eap_fast_pac *current_pac;
+	size_t max_pac_list_len;
+	int use_pac_binary_format;
+
+	u8 simck[EAP_FAST_SIMCK_LEN];
+	int simck_idx;
+
+	struct wpabuf *pending_phase2_req;
+};
+
+
+static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
+				      const u8 *client_random,
+				      const u8 *server_random,
+				      u8 *master_secret)
+{
+	struct eap_fast_data *data = ctx;
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback");
+
+	if (client_random == NULL || server_random == NULL ||
+	    master_secret == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket failed - fall "
+			   "back to full TLS handshake");
+		data->session_ticket_used = 0;
+		if (data->provisioning_allowed) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Try to provision a "
+				   "new PAC-Key");
+			data->provisioning = 1;
+			data->current_pac = NULL;
+		}
+		return 0;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket", ticket, len);
+
+	if (data->current_pac == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key available for "
+			   "using SessionTicket");
+		data->session_ticket_used = 0;
+		return 0;
+	}
+
+	eap_fast_derive_master_secret(data->current_pac->pac_key,
+				      server_random, client_random,
+				      master_secret);
+
+	data->session_ticket_used = 1;
+
+	return 1;
+}
+
+
+static int eap_fast_parse_phase1(struct eap_fast_data *data,
+				 const char *phase1)
+{
+	const char *pos;
+
+	pos = os_strstr(phase1, "fast_provisioning=");
+	if (pos) {
+		data->provisioning_allowed = atoi(pos + 18);
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Automatic PAC provisioning "
+			   "mode: %d", data->provisioning_allowed);
+	}
+
+	pos = os_strstr(phase1, "fast_max_pac_list_len=");
+	if (pos) {
+		data->max_pac_list_len = atoi(pos + 22);
+		if (data->max_pac_list_len == 0)
+			data->max_pac_list_len = 1;
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Maximum PAC list length: %lu",
+			   (unsigned long) data->max_pac_list_len);
+	}
+
+	pos = os_strstr(phase1, "fast_pac_format=binary");
+	if (pos) {
+		data->use_pac_binary_format = 1;
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Using binary format for PAC "
+			   "list");
+	}
+
+	return 0;
+}
+
+
+static void * eap_fast_init(struct eap_sm *sm)
+{
+	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->phase1 && eap_fast_parse_phase1(data, config->phase1) < 0) {
+		eap_fast_deinit(sm, data);
+		return NULL;
+	}
+
+	if (eap_peer_select_phase2_methods(config, "auth=",
+					   &data->phase2_types,
+					   &data->num_phase2_types) < 0) {
+		eap_fast_deinit(sm, data);
+		return NULL;
+	}
+
+	data->phase2_type.vendor = EAP_VENDOR_IETF;
+	data->phase2_type.method = EAP_TYPE_NONE;
+
+	if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_FAST)) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
+		eap_fast_deinit(sm, data);
+		return NULL;
+	}
+
+	if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
+						 eap_fast_session_ticket_cb,
+						 data) < 0) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket "
+			   "callback");
+		eap_fast_deinit(sm, data);
+		return NULL;
+	}
+
+	/*
+	 * The local RADIUS server in a Cisco AP does not seem to like empty
+	 * fragments before data, so disable that workaround for CBC.
+	 * TODO: consider making this configurable
+	 */
+	if (tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn)) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to enable TLS "
+			   "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;
+	}
+	eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len);
+
+	if (data->pac == NULL && !data->provisioning_allowed) {
+		wpa_printf(MSG_INFO, "EAP-FAST: No PAC configured and "
+			   "provisioning disabled");
+		eap_fast_deinit(sm, data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+static void eap_fast_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_fast_data *data = priv;
+	struct eap_fast_pac *pac, *prev;
+
+	if (data == NULL)
+		return;
+	if (data->phase2_priv && data->phase2_method)
+		data->phase2_method->deinit(sm, data->phase2_priv);
+	os_free(data->phase2_types);
+	os_free(data->key_block_p);
+	eap_peer_tls_ssl_deinit(sm, &data->ssl);
+
+	pac = data->pac;
+	prev = NULL;
+	while (pac) {
+		prev = pac;
+		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);
+}
+
+
+static int eap_fast_derive_msk(struct eap_fast_data *data)
+{
+	eap_fast_derive_eap_msk(data->simck, data->key_data);
+	eap_fast_derive_eap_emsk(data->simck, data->emsk);
+	data->success = 1;
+	return 0;
+}
+
+
+static int eap_fast_derive_key_auth(struct eap_sm *sm,
+				    struct eap_fast_data *data)
+{
+	u8 *sks;
+
+	/* RFC 4851, Section 5.1:
+	 * Extra key material after TLS key_block: session_key_seed[40]
+	 */
+
+	sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion",
+				  EAP_FAST_SKS_LEN);
+	if (sks == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
+			   "session_key_seed");
+		return -1;
+	}
+
+	/*
+	 * RFC 4851, Section 5.2:
+	 * S-IMCK[0] = session_key_seed
+	 */
+	wpa_hexdump_key(MSG_DEBUG,
+			"EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+			sks, EAP_FAST_SKS_LEN);
+	data->simck_idx = 0;
+	os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN);
+	os_free(sks);
+	return 0;
+}
+
+
+static int eap_fast_derive_key_provisioning(struct eap_sm *sm,
+					    struct eap_fast_data *data)
+{
+	os_free(data->key_block_p);
+	data->key_block_p = (struct eap_fast_key_block_provisioning *)
+		eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
+				    "key expansion",
+				    sizeof(*data->key_block_p));
+	if (data->key_block_p == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
+		return -1;
+	}
+	/*
+	 * RFC 4851, Section 5.2:
+	 * S-IMCK[0] = session_key_seed
+	 */
+	wpa_hexdump_key(MSG_DEBUG,
+			"EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+			data->key_block_p->session_key_seed,
+			sizeof(data->key_block_p->session_key_seed));
+	data->simck_idx = 0;
+	os_memcpy(data->simck, data->key_block_p->session_key_seed,
+		  EAP_FAST_SIMCK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge",
+			data->key_block_p->server_challenge,
+			sizeof(data->key_block_p->server_challenge));
+	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge",
+			data->key_block_p->client_challenge,
+			sizeof(data->key_block_p->client_challenge));
+	return 0;
+}
+
+
+static int eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data)
+{
+	int res;
+
+	if (data->anon_provisioning)
+		res = eap_fast_derive_key_provisioning(sm, data);
+	else
+		res = eap_fast_derive_key_auth(sm, data);
+	return res;
+}
+
+
+static int eap_fast_init_phase2_method(struct eap_sm *sm,
+				       struct eap_fast_data *data)
+{
+	data->phase2_method =
+		eap_peer_get_eap_method(data->phase2_type.vendor,
+					data->phase2_type.method);
+	if (data->phase2_method == NULL)
+		return -1;
+
+	if (data->key_block_p) {
+		sm->auth_challenge = data->key_block_p->server_challenge;
+		sm->peer_challenge = data->key_block_p->client_challenge;
+	}
+	sm->init_phase2 = 1;
+	data->phase2_priv = data->phase2_method->init(sm);
+	sm->init_phase2 = 0;
+	sm->auth_challenge = NULL;
+	sm->peer_challenge = NULL;
+
+	return data->phase2_priv == NULL ? -1 : 0;
+}
+
+
+static int eap_fast_select_phase2_method(struct eap_fast_data *data, u8 type)
+{
+	size_t i;
+
+	/* TODO: TNC with anonymous provisioning; need to require both
+	 * completed MSCHAPv2 and TNC */
+
+	if (data->anon_provisioning && type != EAP_TYPE_MSCHAPV2) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Only EAP-MSCHAPv2 is allowed "
+			   "during unauthenticated provisioning; reject phase2"
+			   " type %d", type);
+		return -1;
+	}
+
+#ifdef EAP_TNC
+	if (type == EAP_TYPE_TNC) {
+		data->phase2_type.vendor = EAP_VENDOR_IETF;
+		data->phase2_type.method = EAP_TYPE_TNC;
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP "
+			   "vendor %d method %d for TNC",
+			   data->phase2_type.vendor,
+			   data->phase2_type.method);
+		return 0;
+	}
+#endif /* EAP_TNC */
+
+	for (i = 0; i < data->num_phase2_types; i++) {
+		if (data->phase2_types[i].vendor != EAP_VENDOR_IETF ||
+		    data->phase2_types[i].method != type)
+			continue;
+
+		data->phase2_type.vendor = data->phase2_types[i].vendor;
+		data->phase2_type.method = data->phase2_types[i].method;
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP "
+			   "vendor %d method %d",
+			   data->phase2_type.vendor,
+			   data->phase2_type.method);
+		break;
+	}
+
+	if (type != data->phase2_type.method || type == EAP_TYPE_NONE)
+		return -1;
+
+	return 0;
+}
+
+
+static int eap_fast_phase2_request(struct eap_sm *sm,
+				   struct eap_fast_data *data,
+				   struct eap_method_ret *ret,
+				   struct eap_hdr *hdr,
+				   struct wpabuf **resp)
+{
+	size_t len = be_to_host16(hdr->length);
+	u8 *pos;
+	struct eap_method_ret iret;
+	struct eap_peer_config *config = eap_get_config(sm);
+	struct wpabuf msg;
+
+	if (len <= sizeof(struct eap_hdr)) {
+		wpa_printf(MSG_INFO, "EAP-FAST: too short "
+			   "Phase 2 request (len=%lu)", (unsigned long) len);
+		return -1;
+	}
+	pos = (u8 *) (hdr + 1);
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%d", *pos);
+	if (*pos == EAP_TYPE_IDENTITY) {
+		*resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+		return 0;
+	}
+
+	if (data->phase2_priv && data->phase2_method &&
+	    *pos != data->phase2_type.method) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 EAP sequence - "
+			   "deinitialize previous method");
+		data->phase2_method->deinit(sm, data->phase2_priv);
+		data->phase2_method = NULL;
+		data->phase2_priv = NULL;
+		data->phase2_type.vendor = EAP_VENDOR_IETF;
+		data->phase2_type.method = EAP_TYPE_NONE;
+	}
+
+	if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
+	    data->phase2_type.method == EAP_TYPE_NONE &&
+	    eap_fast_select_phase2_method(data, *pos) < 0) {
+		if (eap_peer_tls_phase2_nak(data->phase2_types,
+					    data->num_phase2_types,
+					    hdr, resp))
+			return -1;
+		return 0;
+	}
+
+	if ((data->phase2_priv == NULL &&
+	     eap_fast_init_phase2_method(sm, data) < 0) ||
+	    data->phase2_method == NULL) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize "
+			   "Phase 2 EAP method %d", *pos);
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		return -1;
+	}
+
+	os_memset(&iret, 0, sizeof(iret));
+	wpabuf_set(&msg, hdr, len);
+	*resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
+					     &msg);
+	if (*resp == NULL ||
+	    (iret.methodState == METHOD_DONE &&
+	     iret.decision == DECISION_FAIL)) {
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+	} else if ((iret.methodState == METHOD_DONE ||
+		    iret.methodState == METHOD_MAY_CONT) &&
+		   (iret.decision == DECISION_UNCOND_SUCC ||
+		    iret.decision == DECISION_COND_SUCC)) {
+		data->phase2_success = 1;
+	}
+
+	if (*resp == NULL && config &&
+	    (config->pending_req_identity || config->pending_req_password ||
+	     config->pending_req_otp || config->pending_req_new_password)) {
+		wpabuf_free(data->pending_phase2_req);
+		data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
+	} else if (*resp == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_fast_tlv_nak(int vendor_id, int tlv_type)
+{
+	struct wpabuf *buf;
+	struct eap_tlv_nak_tlv *nak;
+	buf = wpabuf_alloc(sizeof(*nak));
+	if (buf == NULL)
+		return NULL;
+	nak = wpabuf_put(buf, sizeof(*nak));
+	nak->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_NAK_TLV);
+	nak->length = host_to_be16(6);
+	nak->vendor_id = host_to_be32(vendor_id);
+	nak->nak_type = host_to_be16(tlv_type);
+	return buf;
+}
+
+
+static struct wpabuf * eap_fast_tlv_result(int status, int intermediate)
+{
+	struct wpabuf *buf;
+	struct eap_tlv_intermediate_result_tlv *result;
+	buf = wpabuf_alloc(sizeof(*result));
+	if (buf == NULL)
+		return NULL;
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Add %sResult TLV(status=%d)",
+		   intermediate ? "Intermediate " : "", status);
+	result = wpabuf_put(buf, sizeof(*result));
+	result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+					(intermediate ?
+					 EAP_TLV_INTERMEDIATE_RESULT_TLV :
+					 EAP_TLV_RESULT_TLV));
+	result->length = host_to_be16(2);
+	result->status = host_to_be16(status);
+	return buf;
+}
+
+
+static struct wpabuf * eap_fast_tlv_pac_ack(void)
+{
+	struct wpabuf *buf;
+	struct eap_tlv_result_tlv *res;
+	struct eap_tlv_pac_ack_tlv *ack;
+
+	buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack));
+	if (buf == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV (ack)");
+	ack = wpabuf_put(buf, sizeof(*ack));
+	ack->tlv_type = host_to_be16(EAP_TLV_PAC_TLV |
+				     EAP_TLV_TYPE_MANDATORY);
+	ack->length = host_to_be16(sizeof(*ack) - sizeof(struct eap_tlv_hdr));
+	ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT);
+	ack->pac_len = host_to_be16(2);
+	ack->result = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+
+	return buf;
+}
+
+
+static struct wpabuf * eap_fast_process_eap_payload_tlv(
+	struct eap_sm *sm, struct eap_fast_data *data,
+	struct eap_method_ret *ret,
+	u8 *eap_payload_tlv, size_t eap_payload_tlv_len)
+{
+	struct eap_hdr *hdr;
+	struct wpabuf *resp = NULL;
+
+	if (eap_payload_tlv_len < sizeof(*hdr)) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: too short EAP "
+			   "Payload TLV (len=%lu)",
+			   (unsigned long) eap_payload_tlv_len);
+		return NULL;
+	}
+
+	hdr = (struct eap_hdr *) eap_payload_tlv;
+	if (be_to_host16(hdr->length) > eap_payload_tlv_len) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: EAP packet overflow in "
+			   "EAP Payload TLV");
+		return NULL;
+	}
+
+	if (hdr->code != EAP_CODE_REQUEST) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in "
+			   "Phase 2 EAP header", hdr->code);
+		return NULL;
+	}
+
+	if (eap_fast_phase2_request(sm, data, ret, hdr, &resp)) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Phase2 Request processing "
+			   "failed");
+		return NULL;
+	}
+
+	return eap_fast_tlv_eap_payload(resp);
+}
+
+
+static int eap_fast_validate_crypto_binding(
+	struct eap_tlv_crypto_binding_tlv *_bind)
+{
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV: Version %d "
+		   "Received Version %d SubType %d",
+		   _bind->version, _bind->received_version, _bind->subtype);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+		    _bind->nonce, sizeof(_bind->nonce));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+		    _bind->compound_mac, sizeof(_bind->compound_mac));
+
+	if (_bind->version != EAP_FAST_VERSION ||
+	    _bind->received_version != EAP_FAST_VERSION ||
+	    _bind->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Invalid version/subtype in "
+			   "Crypto-Binding TLV: Version %d "
+			   "Received Version %d SubType %d",
+			   _bind->version, _bind->received_version,
+			   _bind->subtype);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void eap_fast_write_crypto_binding(
+	struct eap_tlv_crypto_binding_tlv *rbind,
+	struct eap_tlv_crypto_binding_tlv *_bind, const u8 *cmk)
+{
+	rbind->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+				       EAP_TLV_CRYPTO_BINDING_TLV);
+	rbind->length = host_to_be16(sizeof(*rbind) -
+				     sizeof(struct eap_tlv_hdr));
+	rbind->version = EAP_FAST_VERSION;
+	rbind->received_version = _bind->version;
+	rbind->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE;
+	os_memcpy(rbind->nonce, _bind->nonce, sizeof(_bind->nonce));
+	inc_byte_array(rbind->nonce, sizeof(rbind->nonce));
+	hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) rbind, sizeof(*rbind),
+		  rbind->compound_mac);
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: Version %d "
+		   "Received Version %d SubType %d",
+		   rbind->version, rbind->received_version, rbind->subtype);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+		    rbind->nonce, sizeof(rbind->nonce));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+		    rbind->compound_mac, sizeof(rbind->compound_mac));
+}
+
+
+static int eap_fast_get_phase2_key(struct eap_sm *sm,
+				   struct eap_fast_data *data,
+				   u8 *isk, size_t isk_len)
+{
+	u8 *key;
+	size_t key_len;
+
+	os_memset(isk, 0, isk_len);
+
+	if (data->phase2_method == NULL || data->phase2_priv == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not "
+			   "available");
+		return -1;
+	}
+
+	if (data->phase2_method->isKeyAvailable == NULL ||
+	    data->phase2_method->getKey == NULL)
+		return 0;
+
+	if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||
+	    (key = data->phase2_method->getKey(sm, data->phase2_priv,
+					       &key_len)) == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material "
+			   "from Phase 2");
+		return -1;
+	}
+
+	if (key_len > isk_len)
+		key_len = isk_len;
+	if (key_len == 32 &&
+	    data->phase2_method->vendor == EAP_VENDOR_IETF &&
+	    data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
+		/*
+		 * EAP-FAST uses reverse order for MS-MPPE keys when deriving
+		 * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
+		 * ISK for EAP-FAST cryptobinding.
+		 */
+		os_memcpy(isk, key + 16, 16);
+		os_memcpy(isk + 16, key, 16);
+	} else
+		os_memcpy(isk, key, key_len);
+	os_free(key);
+
+	return 0;
+}
+
+
+static int eap_fast_get_cmk(struct eap_sm *sm, struct eap_fast_data *data,
+			    u8 *cmk)
+{
+	u8 isk[32], imck[60];
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Determining CMK[%d] for Compound MIC "
+		   "calculation", data->simck_idx + 1);
+
+	/*
+	 * RFC 4851, Section 5.2:
+	 * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys",
+	 *                 MSK[j], 60)
+	 * S-IMCK[j] = first 40 octets of IMCK[j]
+	 * CMK[j] = last 20 octets of IMCK[j]
+	 */
+
+	if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk));
+	sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
+		   "Inner Methods Compound Keys",
+		   isk, sizeof(isk), imck, sizeof(imck));
+	data->simck_idx++;
+	os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]",
+			data->simck, EAP_FAST_SIMCK_LEN);
+	os_memcpy(cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]",
+			cmk, EAP_FAST_CMK_LEN);
+
+	return 0;
+}
+
+
+static u8 * eap_fast_write_pac_request(u8 *pos, u16 pac_type)
+{
+	struct eap_tlv_hdr *pac;
+	struct eap_tlv_request_action_tlv *act;
+	struct eap_tlv_pac_type_tlv *type;
+
+	act = (struct eap_tlv_request_action_tlv *) pos;
+	act->tlv_type = host_to_be16(EAP_TLV_REQUEST_ACTION_TLV);
+	act->length = host_to_be16(2);
+	act->action = host_to_be16(EAP_TLV_ACTION_PROCESS_TLV);
+
+	pac = (struct eap_tlv_hdr *) (act + 1);
+	pac->tlv_type = host_to_be16(EAP_TLV_PAC_TLV);
+	pac->length = host_to_be16(sizeof(*type));
+
+	type = (struct eap_tlv_pac_type_tlv *) (pac + 1);
+	type->tlv_type = host_to_be16(PAC_TYPE_PAC_TYPE);
+	type->length = host_to_be16(2);
+	type->pac_type = host_to_be16(pac_type);
+
+	return (u8 *) (type + 1);
+}
+
+
+static struct wpabuf * eap_fast_process_crypto_binding(
+	struct eap_sm *sm, struct eap_fast_data *data,
+	struct eap_method_ret *ret,
+	struct eap_tlv_crypto_binding_tlv *_bind, size_t bind_len)
+{
+	struct wpabuf *resp;
+	u8 *pos;
+	u8 cmk[EAP_FAST_CMK_LEN], cmac[SHA1_MAC_LEN];
+	int res;
+	size_t len;
+
+	if (eap_fast_validate_crypto_binding(_bind) < 0)
+		return NULL;
+
+	if (eap_fast_get_cmk(sm, data, cmk) < 0)
+		return NULL;
+
+	/* Validate received Compound MAC */
+	os_memcpy(cmac, _bind->compound_mac, sizeof(cmac));
+	os_memset(_bind->compound_mac, 0, sizeof(cmac));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for Compound "
+		    "MAC calculation", (u8 *) _bind, bind_len);
+	hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) _bind, bind_len,
+		  _bind->compound_mac);
+	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",
+		    _bind->compound_mac, sizeof(cmac));
+	if (res != 0) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match");
+		os_memcpy(_bind->compound_mac, cmac, sizeof(cmac));
+		return NULL;
+	}
+
+	/*
+	 * Compound MAC was valid, so authentication succeeded. Reply with
+	 * crypto binding to allow server to complete authentication.
+	 */
+
+	len = sizeof(struct eap_tlv_crypto_binding_tlv);
+	resp = wpabuf_alloc(len);
+	if (resp == NULL)
+		return NULL;
+
+	if (!data->anon_provisioning && data->phase2_success &&
+	    eap_fast_derive_msk(data) < 0) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK");
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		data->phase2_success = 0;
+		wpabuf_free(resp);
+		return NULL;
+	}
+
+	if (!data->anon_provisioning && data->phase2_success) {
+		os_free(data->session_id);
+		data->session_id = eap_peer_tls_derive_session_id(
+			sm, &data->ssl, EAP_TYPE_FAST, &data->id_len);
+		if (data->session_id) {
+			wpa_hexdump(MSG_DEBUG, "EAP-FAST: Derived Session-Id",
+				    data->session_id, data->id_len);
+		} else {
+			wpa_printf(MSG_ERROR, "EAP-FAST: Failed to derive "
+				   "Session-Id");
+			wpabuf_free(resp);
+			return NULL;
+		}
+	}
+
+	pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding_tlv));
+	eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding_tlv *)
+				      pos, _bind, cmk);
+
+	return resp;
+}
+
+
+static void eap_fast_parse_pac_tlv(struct eap_fast_pac *entry, int type,
+				   u8 *pos, size_t len, int *pac_key_found)
+{
+	switch (type & 0x7fff) {
+	case PAC_TYPE_PAC_KEY:
+		wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key", pos, len);
+		if (len != EAP_FAST_PAC_KEY_LEN) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid PAC-Key "
+				   "length %lu", (unsigned long) len);
+			break;
+		}
+		*pac_key_found = 1;
+		os_memcpy(entry->pac_key, pos, len);
+		break;
+	case PAC_TYPE_PAC_OPAQUE:
+		wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque", pos, len);
+		entry->pac_opaque = pos;
+		entry->pac_opaque_len = len;
+		break;
+	case PAC_TYPE_PAC_INFO:
+		wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info", pos, len);
+		entry->pac_info = pos;
+		entry->pac_info_len = len;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC type %d",
+			   type);
+		break;
+	}
+}
+
+
+static int eap_fast_process_pac_tlv(struct eap_fast_pac *entry,
+				    u8 *pac, size_t pac_len)
+{
+	struct pac_tlv_hdr *hdr;
+	u8 *pos;
+	size_t left, len;
+	int type, pac_key_found = 0;
+
+	pos = pac;
+	left = pac_len;
+
+	while (left > sizeof(*hdr)) {
+		hdr = (struct pac_tlv_hdr *) pos;
+		type = be_to_host16(hdr->type);
+		len = be_to_host16(hdr->len);
+		pos += sizeof(*hdr);
+		left -= sizeof(*hdr);
+		if (len > left) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV overrun "
+				   "(type=%d len=%lu left=%lu)",
+				   type, (unsigned long) len,
+				   (unsigned long) left);
+			return -1;
+		}
+
+		eap_fast_parse_pac_tlv(entry, type, pos, len, &pac_key_found);
+
+		pos += len;
+		left -= len;
+	}
+
+	if (!pac_key_found || !entry->pac_opaque || !entry->pac_info) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV does not include "
+			   "all the required fields");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eap_fast_parse_pac_info(struct eap_fast_pac *entry, int type,
+				   u8 *pos, size_t len)
+{
+	u16 pac_type;
+	u32 lifetime;
+	struct os_time now;
+
+	switch (type & 0x7fff) {
+	case PAC_TYPE_CRED_LIFETIME:
+		if (len != 4) {
+			wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info - "
+				    "Invalid CRED_LIFETIME length - ignored",
+				    pos, len);
+			return 0;
+		}
+
+		/*
+		 * This is not currently saved separately in PAC files since
+		 * the server can automatically initiate PAC update when
+		 * needed. Anyway, the information is available from PAC-Info
+		 * dump if it is needed for something in the future.
+		 */
+		lifetime = WPA_GET_BE32(pos);
+		os_get_time(&now);
+		wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - CRED_LIFETIME %d "
+			   "(%d days)",
+			   lifetime, (lifetime - (u32) now.sec) / 86400);
+		break;
+	case PAC_TYPE_A_ID:
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID",
+				  pos, len);
+		entry->a_id = pos;
+		entry->a_id_len = len;
+		break;
+	case PAC_TYPE_I_ID:
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - I-ID",
+				  pos, len);
+		entry->i_id = pos;
+		entry->i_id_len = len;
+		break;
+	case PAC_TYPE_A_ID_INFO:
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID-Info",
+				  pos, len);
+		entry->a_id_info = pos;
+		entry->a_id_info_len = len;
+		break;
+	case PAC_TYPE_PAC_TYPE:
+		/* RFC 5422, Section 4.2.6 - PAC-Type TLV */
+		if (len != 2) {
+			wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC-Type "
+				   "length %lu (expected 2)",
+				   (unsigned long) len);
+			wpa_hexdump_ascii(MSG_DEBUG,
+					  "EAP-FAST: PAC-Info - PAC-Type",
+					  pos, len);
+			return -1;
+		}
+		pac_type = WPA_GET_BE16(pos);
+		if (pac_type != PAC_TYPE_TUNNEL_PAC &&
+		    pac_type != PAC_TYPE_USER_AUTHORIZATION &&
+		    pac_type != PAC_TYPE_MACHINE_AUTHENTICATION) {
+			wpa_printf(MSG_INFO, "EAP-FAST: Unsupported PAC Type "
+				   "%d", pac_type);
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - PAC-Type %d",
+			   pac_type);
+		entry->pac_type = pac_type;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC-Info "
+			   "type %d", type);
+		break;
+	}
+
+	return 0;
+}
+
+
+static int eap_fast_process_pac_info(struct eap_fast_pac *entry)
+{
+	struct pac_tlv_hdr *hdr;
+	u8 *pos;
+	size_t left, len;
+	int type;
+
+	/* RFC 5422, Section 4.2.4 */
+
+	/* PAC-Type defaults to Tunnel PAC (Type 1) */
+	entry->pac_type = PAC_TYPE_TUNNEL_PAC;
+
+	pos = entry->pac_info;
+	left = entry->pac_info_len;
+	while (left > sizeof(*hdr)) {
+		hdr = (struct pac_tlv_hdr *) pos;
+		type = be_to_host16(hdr->type);
+		len = be_to_host16(hdr->len);
+		pos += sizeof(*hdr);
+		left -= sizeof(*hdr);
+		if (len > left) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info overrun "
+				   "(type=%d len=%lu left=%lu)",
+				   type, (unsigned long) len,
+				   (unsigned long) left);
+			return -1;
+		}
+
+		if (eap_fast_parse_pac_info(entry, type, pos, len) < 0)
+			return -1;
+
+		pos += len;
+		left -= len;
+	}
+
+	if (entry->a_id == NULL || entry->a_id_info == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info does not include "
+			   "all the required fields");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm,
+					    struct eap_fast_data *data,
+					    struct eap_method_ret *ret,
+					    u8 *pac, size_t pac_len)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	struct eap_fast_pac entry;
+
+	os_memset(&entry, 0, sizeof(entry));
+	if (eap_fast_process_pac_tlv(&entry, pac, pac_len) ||
+	    eap_fast_process_pac_info(&entry))
+		return NULL;
+
+	eap_fast_add_pac(&data->pac, &data->current_pac, &entry);
+	eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len);
+	if (data->use_pac_binary_format)
+		eap_fast_save_pac_bin(sm, data->pac, config->pac_file);
+	else
+		eap_fast_save_pac(sm, data->pac, config->pac_file);
+
+	if (data->provisioning) {
+		if (data->anon_provisioning) {
+			/*
+			 * Unauthenticated provisioning does not provide keying
+			 * material and must end with an EAP-Failure.
+			 * Authentication will be done separately after this.
+			 */
+			data->success = 0;
+			ret->decision = DECISION_FAIL;
+		} else {
+			/*
+			 * Server may or may not allow authenticated
+			 * provisioning also for key generation.
+			 */
+			ret->decision = DECISION_COND_SUCC;
+		}
+		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
+		 * expected to be completed with an EAP-Success. However,
+		 * RFC 5422, Section 3.5 allows EAP-Failure to be sent even
+		 * after protected success exchange in case of EAP-Fast
+		 * provisioning, so we better use DECISION_COND_SUCC here
+		 * instead of DECISION_UNCOND_SUCC.
+		 */
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
+			   "- PAC refreshing completed successfully");
+		ret->decision = DECISION_COND_SUCC;
+	}
+	ret->methodState = METHOD_DONE;
+	return eap_fast_tlv_pac_ack();
+}
+
+
+static int eap_fast_parse_decrypted(struct wpabuf *decrypted,
+				    struct eap_fast_tlv_parse *tlv,
+				    struct wpabuf **resp)
+{
+	int mandatory, tlv_type, res;
+	size_t len;
+	u8 *pos, *end;
+
+	os_memset(tlv, 0, sizeof(*tlv));
+
+	/* Parse TLVs from the decrypted Phase 2 data */
+	pos = wpabuf_mhead(decrypted);
+	end = pos + wpabuf_len(decrypted);
+	while (pos + 4 < end) {
+		mandatory = pos[0] & 0x80;
+		tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+		pos += 2;
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (len > (size_t) (end - pos)) {
+			wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: "
+			   "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)
+			break;
+		if (res < 0) {
+			if (mandatory) {
+				wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown "
+					   "mandatory TLV type %d", tlv_type);
+				*resp = eap_fast_tlv_nak(0, tlv_type);
+				break;
+			} else {
+				wpa_printf(MSG_DEBUG, "EAP-FAST: ignored "
+					   "unknown optional TLV type %d",
+					   tlv_type);
+			}
+		}
+
+		pos += len;
+	}
+
+	return 0;
+}
+
+
+static int eap_fast_encrypt_response(struct eap_sm *sm,
+				     struct eap_fast_data *data,
+				     struct wpabuf *resp,
+				     u8 identifier, struct wpabuf **out_data)
+{
+	if (resp == NULL)
+		return 0;
+
+	wpa_hexdump_buf(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 data",
+			resp);
+	if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST,
+				 data->fast_version, identifier,
+				 resp, out_data)) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 "
+			   "frame");
+	}
+	wpabuf_free(resp);
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_fast_pac_request(void)
+{
+	struct wpabuf *tmp;
+	u8 *pos, *pos2;
+
+	tmp = wpabuf_alloc(sizeof(struct eap_tlv_hdr) +
+			   sizeof(struct eap_tlv_request_action_tlv) +
+			   sizeof(struct eap_tlv_pac_type_tlv));
+	if (tmp == NULL)
+		return NULL;
+
+	pos = wpabuf_put(tmp, 0);
+	pos2 = eap_fast_write_pac_request(pos, PAC_TYPE_TUNNEL_PAC);
+	wpabuf_put(tmp, pos2 - pos);
+	return tmp;
+}
+
+
+static int eap_fast_process_decrypted(struct eap_sm *sm,
+				      struct eap_fast_data *data,
+				      struct eap_method_ret *ret,
+				      u8 identifier,
+				      struct wpabuf *decrypted,
+				      struct wpabuf **out_data)
+{
+	struct wpabuf *resp = NULL, *tmp;
+	struct eap_fast_tlv_parse tlv;
+	int failed = 0;
+
+	if (eap_fast_parse_decrypted(decrypted, &tlv, &resp) < 0)
+		return 0;
+	if (resp)
+		return eap_fast_encrypt_response(sm, data, resp,
+						 identifier, out_data);
+
+	if (tlv.result == EAP_TLV_RESULT_FAILURE) {
+		resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
+		return eap_fast_encrypt_response(sm, data, resp,
+						 identifier, out_data);
+	}
+
+	if (tlv.iresult == EAP_TLV_RESULT_FAILURE) {
+		resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1);
+		return eap_fast_encrypt_response(sm, data, resp,
+						 identifier, out_data);
+	}
+
+	if (tlv.crypto_binding) {
+		tmp = eap_fast_process_crypto_binding(sm, data, ret,
+						      tlv.crypto_binding,
+						      tlv.crypto_binding_len);
+		if (tmp == NULL)
+			failed = 1;
+		else
+			resp = wpabuf_concat(resp, tmp);
+	}
+
+	if (tlv.iresult == EAP_TLV_RESULT_SUCCESS) {
+		tmp = eap_fast_tlv_result(failed ? EAP_TLV_RESULT_FAILURE :
+					  EAP_TLV_RESULT_SUCCESS, 1);
+		resp = wpabuf_concat(resp, tmp);
+	}
+
+	if (tlv.eap_payload_tlv) {
+		tmp = eap_fast_process_eap_payload_tlv(
+			sm, data, ret, tlv.eap_payload_tlv,
+			tlv.eap_payload_tlv_len);
+		resp = wpabuf_concat(resp, tmp);
+	}
+
+	if (tlv.pac && tlv.result != EAP_TLV_RESULT_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV "
+			   "acknowledging success");
+		failed = 1;
+	} else if (tlv.pac && tlv.result == EAP_TLV_RESULT_SUCCESS) {
+		tmp = eap_fast_process_pac(sm, data, ret, tlv.pac,
+					   tlv.pac_len);
+		resp = wpabuf_concat(resp, tmp);
+	}
+
+	if (data->current_pac == NULL && data->provisioning &&
+	    !data->anon_provisioning && !tlv.pac &&
+	    (tlv.iresult == EAP_TLV_RESULT_SUCCESS ||
+	     tlv.result == EAP_TLV_RESULT_SUCCESS)) {
+		/*
+		 * Need to request Tunnel PAC when using authenticated
+		 * provisioning.
+		 */
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Request Tunnel PAC");
+		tmp = eap_fast_pac_request();
+		resp = wpabuf_concat(resp, tmp);
+	}
+
+	if (tlv.result == EAP_TLV_RESULT_SUCCESS && !failed) {
+		tmp = eap_fast_tlv_result(EAP_TLV_RESULT_SUCCESS, 0);
+		resp = wpabuf_concat(tmp, resp);
+	} else if (failed) {
+		tmp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
+		resp = wpabuf_concat(tmp, resp);
+	}
+
+	if (resp && tlv.result == EAP_TLV_RESULT_SUCCESS && !failed &&
+	    tlv.crypto_binding && data->phase2_success) {
+		if (data->anon_provisioning) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Unauthenticated "
+				   "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.");
+			if (data->provisioning)
+				ret->methodState = METHOD_MAY_CONT;
+			else
+				ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_UNCOND_SUCC;
+		}
+	}
+
+	if (resp == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send "
+			   "empty response packet");
+		resp = wpabuf_alloc(1);
+	}
+
+	return eap_fast_encrypt_response(sm, data, resp, identifier,
+					 out_data);
+}
+
+
+static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
+			    struct eap_method_ret *ret, u8 identifier,
+			    const struct wpabuf *in_data,
+			    struct wpabuf **out_data)
+{
+	struct wpabuf *in_decrypted;
+	int res;
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for"
+		   " Phase 2", (unsigned long) wpabuf_len(in_data));
+
+	if (data->pending_phase2_req) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Pending Phase 2 request - "
+			   "skip decryption and use old data");
+		/* Clear TLS reassembly state. */
+		eap_peer_tls_reset_input(&data->ssl);
+
+		in_decrypted = data->pending_phase2_req;
+		data->pending_phase2_req = NULL;
+		goto continue_req;
+	}
+
+	if (wpabuf_len(in_data) == 0) {
+		/* Received TLS ACK - requesting more fragments */
+		return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST,
+					    data->fast_version,
+					    identifier, NULL, out_data);
+	}
+
+	res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+	if (res)
+		return res;
+
+continue_req:
+	wpa_hexdump_buf(MSG_MSGDUMP, "EAP-FAST: Decrypted Phase 2 TLV(s)",
+			in_decrypted);
+
+	if (wpabuf_len(in_decrypted) < 4) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 "
+			   "TLV frame (len=%lu)",
+			   (unsigned long) wpabuf_len(in_decrypted));
+		wpabuf_free(in_decrypted);
+		return -1;
+	}
+
+	res = eap_fast_process_decrypted(sm, data, ret, identifier,
+					 in_decrypted, out_data);
+
+	wpabuf_free(in_decrypted);
+
+	return res;
+}
+
+
+static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len)
+{
+	const u8 *a_id;
+	const struct pac_tlv_hdr *hdr;
+
+	/*
+	 * Parse authority identity (A-ID) from the EAP-FAST/Start. This
+	 * supports both raw A-ID and one inside an A-ID TLV.
+	 */
+	a_id = buf;
+	*id_len = len;
+	if (len > sizeof(*hdr)) {
+		int tlen;
+		hdr = (const struct pac_tlv_hdr *) buf;
+		tlen = be_to_host16(hdr->len);
+		if (be_to_host16(hdr->type) == PAC_TYPE_A_ID &&
+		    sizeof(*hdr) + tlen <= len) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: A-ID was in TLV "
+				   "(Start)");
+			a_id = (const u8 *) (hdr + 1);
+			*id_len = tlen;
+		}
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: A-ID", a_id, *id_len);
+
+	return a_id;
+}
+
+
+static void eap_fast_select_pac(struct eap_fast_data *data,
+				const u8 *a_id, size_t a_id_len)
+{
+	data->current_pac = eap_fast_get_pac(data->pac, a_id, a_id_len,
+					     PAC_TYPE_TUNNEL_PAC);
+	if (data->current_pac == NULL) {
+		/*
+		 * Tunnel PAC was not available for this A-ID. Try to use
+		 * Machine Authentication PAC, if one is available.
+		 */
+		data->current_pac = eap_fast_get_pac(
+			data->pac, a_id, a_id_len,
+			PAC_TYPE_MACHINE_AUTHENTICATION);
+	}
+
+	if (data->current_pac) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: PAC found for this A-ID "
+			   "(PAC-Type %d)", data->current_pac->pac_type);
+		wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-FAST: A-ID-Info",
+				  data->current_pac->a_id_info,
+				  data->current_pac->a_id_info_len);
+	}
+}
+
+
+static int eap_fast_use_pac_opaque(struct eap_sm *sm,
+				   struct eap_fast_data *data,
+				   struct eap_fast_pac *pac)
+{
+	u8 *tlv;
+	size_t tlv_len, olen;
+	struct eap_tlv_hdr *ehdr;
+
+	olen = pac->pac_opaque_len;
+	tlv_len = sizeof(*ehdr) + olen;
+	tlv = os_malloc(tlv_len);
+	if (tlv) {
+		ehdr = (struct eap_tlv_hdr *) tlv;
+		ehdr->tlv_type = host_to_be16(PAC_TYPE_PAC_OPAQUE);
+		ehdr->length = host_to_be16(olen);
+		os_memcpy(ehdr + 1, pac->pac_opaque, olen);
+	}
+	if (tlv == NULL ||
+	    tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
+					    TLS_EXT_PAC_OPAQUE,
+					    tlv, tlv_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to add PAC-Opaque TLS "
+			   "extension");
+		os_free(tlv);
+		return -1;
+	}
+	os_free(tlv);
+
+	return 0;
+}
+
+
+static int eap_fast_clear_pac_opaque_ext(struct eap_sm *sm,
+					 struct eap_fast_data *data)
+{
+	if (tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
+					    TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to remove PAC-Opaque "
+			   "TLS extension");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int eap_fast_set_provisioning_ciphers(struct eap_sm *sm,
+					     struct eap_fast_data *data)
+{
+	u8 ciphers[5];
+	int count = 0;
+
+	if (data->provisioning_allowed & EAP_FAST_PROV_UNAUTH) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling unauthenticated "
+			   "provisioning TLS cipher suites");
+		ciphers[count++] = TLS_CIPHER_ANON_DH_AES128_SHA;
+	}
+
+	if (data->provisioning_allowed & EAP_FAST_PROV_AUTH) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling authenticated "
+			   "provisioning TLS cipher suites");
+		ciphers[count++] = TLS_CIPHER_RSA_DHE_AES128_SHA;
+		ciphers[count++] = TLS_CIPHER_AES128_SHA;
+		ciphers[count++] = TLS_CIPHER_RC4_SHA;
+	}
+
+	ciphers[count++] = TLS_CIPHER_NONE;
+
+	if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn,
+					   ciphers)) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Could not configure TLS "
+			   "cipher suites for provisioning");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eap_fast_process_start(struct eap_sm *sm,
+				  struct eap_fast_data *data, u8 flags,
+				  const u8 *pos, size_t left)
+{
+	const u8 *a_id;
+	size_t a_id_len;
+
+	/* EAP-FAST Version negotiation (section 3.1) */
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own ver=%d)",
+		   flags & EAP_TLS_VERSION_MASK, data->fast_version);
+	if ((flags & EAP_TLS_VERSION_MASK) < data->fast_version)
+		data->fast_version = flags & EAP_TLS_VERSION_MASK;
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Using FAST version %d",
+		   data->fast_version);
+
+	a_id = eap_fast_get_a_id(pos, left, &a_id_len);
+	eap_fast_select_pac(data, a_id, a_id_len);
+
+	if (data->resuming && data->current_pac) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Trying to resume session - "
+			   "do not add PAC-Opaque to TLS ClientHello");
+		if (eap_fast_clear_pac_opaque_ext(sm, data) < 0)
+			return -1;
+	} else if (data->current_pac) {
+		/*
+		 * PAC found for the A-ID and we are not resuming an old
+		 * session, so add PAC-Opaque extension to ClientHello.
+		 */
+		if (eap_fast_use_pac_opaque(sm, data, data->current_pac) < 0)
+			return -1;
+	} else {
+		/* No PAC found, so we must provision one. */
+		if (!data->provisioning_allowed) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found and "
+				   "provisioning disabled");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found - "
+			   "starting provisioning");
+		if (eap_fast_set_provisioning_ciphers(sm, data) < 0 ||
+		    eap_fast_clear_pac_opaque_ext(sm, data) < 0)
+			return -1;
+		data->provisioning = 1;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv,
+					struct eap_method_ret *ret,
+					const struct wpabuf *reqData)
+{
+	const struct eap_hdr *req;
+	size_t left;
+	int res;
+	u8 flags, id;
+	struct wpabuf *resp;
+	const u8 *pos;
+	struct eap_fast_data *data = priv;
+	struct wpabuf msg;
+
+	pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret,
+					reqData, &left, &flags);
+	if (pos == NULL)
+		return NULL;
+
+	req = wpabuf_head(reqData);
+	id = req->identifier;
+
+	if (flags & EAP_TLS_FLAGS_START) {
+		if (eap_fast_process_start(sm, data, flags, pos, left) < 0)
+			return NULL;
+
+		left = 0; /* A-ID is not used in further packet processing */
+	}
+
+	wpabuf_set(&msg, pos, left);
+
+	resp = NULL;
+	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+	    !data->resuming) {
+		/* Process tunneled (encrypted) phase 2 data. */
+		res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp);
+		if (res < 0) {
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			/*
+			 * Ack possible Alert that may have caused failure in
+			 * decryption.
+			 */
+			res = 1;
+		}
+	} else {
+		/* Continue processing TLS handshake (phase 1). */
+		res = eap_peer_tls_process_helper(sm, &data->ssl,
+						  EAP_TYPE_FAST,
+						  data->fast_version, id, &msg,
+						  &resp);
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-FAST: TLS processing failed");
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			return resp;
+		}
+
+		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+			char cipher[80];
+			wpa_printf(MSG_DEBUG,
+				   "EAP-FAST: TLS done, proceed to Phase 2");
+			if (data->provisioning &&
+			    (!(data->provisioning_allowed &
+			       EAP_FAST_PROV_AUTH) ||
+			     tls_get_cipher(sm->ssl_ctx, data->ssl.conn,
+					    cipher, sizeof(cipher)) < 0 ||
+			     os_strstr(cipher, "ADH-") ||
+			     os_strstr(cipher, "anon"))) {
+				wpa_printf(MSG_DEBUG, "EAP-FAST: Using "
+					   "anonymous (unauthenticated) "
+					   "provisioning");
+				data->anon_provisioning = 1;
+			} else
+				data->anon_provisioning = 0;
+			data->resuming = 0;
+			if (eap_fast_derive_keys(sm, data) < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-FAST: Could not derive keys");
+				ret->methodState = METHOD_DONE;
+				ret->decision = DECISION_FAIL;
+				wpabuf_free(resp);
+				return NULL;
+			}
+		}
+
+		if (res == 2) {
+			/*
+			 * Application data included in the handshake message.
+			 */
+			wpabuf_free(data->pending_phase2_req);
+			data->pending_phase2_req = resp;
+			resp = NULL;
+			res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp);
+		}
+	}
+
+	if (res == 1) {
+		wpabuf_free(resp);
+		return eap_peer_tls_build_ack(id, EAP_TYPE_FAST,
+					      data->fast_version);
+	}
+
+	return resp;
+}
+
+
+#if 0 /* FIX */
+static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+	struct eap_fast_data *data = priv;
+	return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+}
+
+
+static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+	struct eap_fast_data *data = priv;
+	os_free(data->key_block_p);
+	data->key_block_p = NULL;
+	wpabuf_free(data->pending_phase2_req);
+	data->pending_phase2_req = NULL;
+}
+
+
+static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+	struct eap_fast_data *data = priv;
+	if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+		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 &&
+	    data->phase2_method->init_for_reauth)
+		data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+	data->phase2_success = 0;
+	data->resuming = 1;
+	data->provisioning = 0;
+	data->anon_provisioning = 0;
+	data->simck_idx = 0;
+	return priv;
+}
+#endif
+
+
+static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf,
+			       size_t buflen, int verbose)
+{
+	struct eap_fast_data *data = priv;
+	int len, ret;
+
+	len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+	if (data->phase2_method) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "EAP-FAST Phase2 method=%s\n",
+				  data->phase2_method->name);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+	return len;
+}
+
+
+static Boolean eap_fast_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_fast_data *data = priv;
+	return data->success;
+}
+
+
+static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_fast_data *data = priv;
+	u8 *key;
+
+	if (!data->success)
+		return NULL;
+
+	key = os_malloc(EAP_FAST_KEY_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_FAST_KEY_LEN;
+	os_memcpy(key, data->key_data, EAP_FAST_KEY_LEN);
+
+	return key;
+}
+
+
+static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_fast_data *data = priv;
+	u8 *id;
+
+	if (!data->success)
+		return NULL;
+
+	id = os_malloc(data->id_len);
+	if (id == NULL)
+		return NULL;
+
+	*len = data->id_len;
+	os_memcpy(id, data->session_id, data->id_len);
+
+	return id;
+}
+
+
+static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_fast_data *data = priv;
+	u8 *key;
+
+	if (!data->success)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_EMSK_LEN;
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+	return key;
+}
+
+
+int eap_peer_fast_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_fast_init;
+	eap->deinit = eap_fast_deinit;
+	eap->process = eap_fast_process;
+	eap->isKeyAvailable = eap_fast_isKeyAvailable;
+	eap->getKey = eap_fast_getKey;
+	eap->getSessionId = eap_fast_get_session_id;
+	eap->get_status = eap_fast_get_status;
+#if 0
+	eap->has_reauth_data = eap_fast_has_reauth_data;
+	eap->deinit_for_reauth = eap_fast_deinit_for_reauth;
+	eap->init_for_reauth = eap_fast_init_for_reauth;
+#endif
+	eap->get_emsk = eap_fast_get_emsk;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_fast_pac.c b/hostap/src/eap_peer/eap_fast_pac.c
new file mode 100644
index 0000000..89e604e
--- /dev/null
+++ b/hostap/src/eap_peer/eap_fast_pac.c
@@ -0,0 +1,929 @@
+/*
+ * EAP peer method: EAP-FAST PAC file processing
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_config.h"
+#include "eap_i.h"
+#include "eap_fast_pac.h"
+
+/* TODO: encrypt PAC-Key in the PAC file */
+
+
+/* Text data format */
+static const char *pac_file_hdr =
+	"wpa_supplicant EAP-FAST PAC file - version 1";
+
+/*
+ * Binary data format
+ * 4-octet magic value: 6A E4 92 0C
+ * 2-octet version (big endian)
+ * <version specific data>
+ *
+ * version=0:
+ * Sequence of PAC entries:
+ *   2-octet PAC-Type (big endian)
+ *   32-octet PAC-Key
+ *   2-octet PAC-Opaque length (big endian)
+ *   <variable len> PAC-Opaque data (length bytes)
+ *   2-octet PAC-Info length (big endian)
+ *   <variable len> PAC-Info data (length bytes)
+ */
+
+#define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c
+#define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0
+
+
+/**
+ * eap_fast_free_pac - Free PAC data
+ * @pac: Pointer to the PAC entry
+ *
+ * Note that the PAC entry must not be in a list since this function does not
+ * remove the list links.
+ */
+void eap_fast_free_pac(struct eap_fast_pac *pac)
+{
+	os_free(pac->pac_opaque);
+	os_free(pac->pac_info);
+	os_free(pac->a_id);
+	os_free(pac->i_id);
+	os_free(pac->a_id_info);
+	os_free(pac);
+}
+
+
+/**
+ * eap_fast_get_pac - Get a PAC entry based on A-ID
+ * @pac_root: Pointer to root of the PAC list
+ * @a_id: A-ID to search for
+ * @a_id_len: Length of A-ID
+ * @pac_type: PAC-Type to search for
+ * Returns: Pointer to the PAC entry, or %NULL if A-ID not found
+ */
+struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
+				       const u8 *a_id, size_t a_id_len,
+				       u16 pac_type)
+{
+	struct eap_fast_pac *pac = pac_root;
+
+	while (pac) {
+		if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
+		    os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
+			return pac;
+		}
+		pac = pac->next;
+	}
+	return NULL;
+}
+
+
+static void eap_fast_remove_pac(struct eap_fast_pac **pac_root,
+				struct eap_fast_pac **pac_current,
+				const u8 *a_id, size_t a_id_len, u16 pac_type)
+{
+	struct eap_fast_pac *pac, *prev;
+
+	pac = *pac_root;
+	prev = NULL;
+
+	while (pac) {
+		if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
+		    os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
+			if (prev == NULL)
+				*pac_root = pac->next;
+			else
+				prev->next = pac->next;
+			if (*pac_current == pac)
+				*pac_current = NULL;
+			eap_fast_free_pac(pac);
+			break;
+		}
+		prev = pac;
+		pac = pac->next;
+	}
+}
+
+
+static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
+			     const u8 *src, size_t src_len)
+{
+	if (src) {
+		*dst = os_malloc(src_len);
+		if (*dst == NULL)
+			return -1;
+		os_memcpy(*dst, src, src_len);
+		*dst_len = src_len;
+	}
+	return 0;
+}
+
+
+/**
+ * eap_fast_add_pac - Add a copy of a PAC entry to a list
+ * @pac_root: Pointer to PAC list root pointer
+ * @pac_current: Pointer to the current PAC pointer
+ * @entry: New entry to clone and add to the list
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function makes a clone of the given PAC entry and adds this copied
+ * entry to the list (pac_root). If an old entry for the same A-ID is found,
+ * it will be removed from the PAC list and in this case, pac_current entry
+ * is set to %NULL if it was the removed entry.
+ */
+int eap_fast_add_pac(struct eap_fast_pac **pac_root,
+		     struct eap_fast_pac **pac_current,
+		     struct eap_fast_pac *entry)
+{
+	struct eap_fast_pac *pac;
+
+	if (entry == NULL || entry->a_id == NULL)
+		return -1;
+
+	/* Remove a possible old entry for the matching A-ID. */
+	eap_fast_remove_pac(pac_root, pac_current,
+			    entry->a_id, entry->a_id_len, entry->pac_type);
+
+	/* Allocate a new entry and add it to the list of PACs. */
+	pac = os_zalloc(sizeof(*pac));
+	if (pac == NULL)
+		return -1;
+
+	pac->pac_type = entry->pac_type;
+	os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
+	if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
+			      entry->pac_opaque, entry->pac_opaque_len) < 0 ||
+	    eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len,
+			      entry->pac_info, entry->pac_info_len) < 0 ||
+	    eap_fast_copy_buf(&pac->a_id, &pac->a_id_len,
+			      entry->a_id, entry->a_id_len) < 0 ||
+	    eap_fast_copy_buf(&pac->i_id, &pac->i_id_len,
+			      entry->i_id, entry->i_id_len) < 0 ||
+	    eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
+			      entry->a_id_info, entry->a_id_info_len) < 0) {
+		eap_fast_free_pac(pac);
+		return -1;
+	}
+
+	pac->next = *pac_root;
+	*pac_root = pac;
+
+	return 0;
+}
+
+
+struct eap_fast_read_ctx {
+	FILE *f;
+	const char *pos;
+	const char *end;
+	int line;
+	char *buf;
+	size_t buf_len;
+};
+
+static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value)
+{
+	char *pos;
+
+	rc->line++;
+	if (rc->f) {
+		if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
+			return -1;
+	} else {
+		const char *l_end;
+		size_t len;
+		if (rc->pos >= rc->end)
+			return -1;
+		l_end = rc->pos;
+		while (l_end < rc->end && *l_end != '\n')
+			l_end++;
+		len = l_end - rc->pos;
+		if (len >= rc->buf_len)
+			len = rc->buf_len - 1;
+		os_memcpy(rc->buf, rc->pos, len);
+		rc->buf[len] = '\0';
+		rc->pos = l_end + 1;
+	}
+
+	rc->buf[rc->buf_len - 1] = '\0';
+	pos = rc->buf;
+	while (*pos != '\0') {
+		if (*pos == '\n' || *pos == '\r') {
+			*pos = '\0';
+			break;
+		}
+		pos++;
+	}
+
+	pos = os_strchr(rc->buf, '=');
+	if (pos)
+		*pos++ = '\0';
+	*value = pos;
+
+	return 0;
+}
+
+
+static u8 * eap_fast_parse_hex(const char *value, size_t *len)
+{
+	int hlen;
+	u8 *buf;
+
+	if (value == NULL)
+		return NULL;
+	hlen = os_strlen(value);
+	if (hlen & 1)
+		return NULL;
+	*len = hlen / 2;
+	buf = os_malloc(*len);
+	if (buf == NULL)
+		return NULL;
+	if (hexstr2bin(value, buf, *len)) {
+		os_free(buf);
+		return NULL;
+	}
+	return buf;
+}
+
+
+static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file,
+				  struct eap_fast_read_ctx *rc)
+{
+	os_memset(rc, 0, sizeof(*rc));
+
+	rc->buf_len = 2048;
+	rc->buf = os_malloc(rc->buf_len);
+	if (rc->buf == NULL)
+		return -1;
+
+	if (os_strncmp(pac_file, "blob://", 7) == 0) {
+		const struct wpa_config_blob *blob;
+		blob = eap_get_config_blob(sm, pac_file + 7);
+		if (blob == NULL) {
+			wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
+				   "assume no PAC entries have been "
+				   "provisioned", pac_file + 7);
+			os_free(rc->buf);
+			return -1;
+		}
+		rc->pos = (char *) blob->data;
+		rc->end = (char *) blob->data + blob->len;
+	} else {
+		rc->f = fopen(pac_file, "rb");
+		if (rc->f == NULL) {
+			wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
+				   "assume no PAC entries have been "
+				   "provisioned", pac_file);
+			os_free(rc->buf);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc)
+{
+	os_free(rc->buf);
+	if (rc->f)
+		fclose(rc->f);
+}
+
+
+static const char * eap_fast_parse_start(struct eap_fast_pac **pac)
+{
+	if (*pac)
+		return "START line without END";
+
+	*pac = os_zalloc(sizeof(struct eap_fast_pac));
+	if (*pac == NULL)
+		return "No memory for PAC entry";
+	(*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
+	return NULL;
+}
+
+
+static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
+				       struct eap_fast_pac **pac)
+{
+	if (*pac == NULL)
+		return "END line without START";
+	if (*pac_root) {
+		struct eap_fast_pac *end = *pac_root;
+		while (end->next)
+			end = end->next;
+		end->next = *pac;
+	} else
+		*pac_root = *pac;
+
+	*pac = NULL;
+	return NULL;
+}
+
+
+static const char * eap_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 &&
+	    pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION)
+		return "Unrecognized PAC-Type";
+
+	return NULL;
+}
+
+
+static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos)
+{
+	u8 *key;
+	size_t key_len;
+
+	key = eap_fast_parse_hex(pos, &key_len);
+	if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
+		os_free(key);
+		return "Invalid PAC-Key";
+	}
+
+	os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
+	os_free(key);
+
+	return NULL;
+}
+
+
+static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac,
+					      char *pos)
+{
+	os_free(pac->pac_opaque);
+	pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len);
+	if (pac->pac_opaque == NULL)
+		return "Invalid PAC-Opaque";
+	return NULL;
+}
+
+
+static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos)
+{
+	os_free(pac->a_id);
+	pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
+	if (pac->a_id == NULL)
+		return "Invalid A-ID";
+	return NULL;
+}
+
+
+static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos)
+{
+	os_free(pac->i_id);
+	pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
+	if (pac->i_id == NULL)
+		return "Invalid I-ID";
+	return NULL;
+}
+
+
+static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac,
+					     char *pos)
+{
+	os_free(pac->a_id_info);
+	pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len);
+	if (pac->a_id_info == NULL)
+		return "Invalid A-ID-Info";
+	return NULL;
+}
+
+
+/**
+ * eap_fast_load_pac - Load PAC entries (text format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Pointer to root of the PAC list (to be filled)
+ * @pac_file: Name of the PAC file/blob to load
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+		      const char *pac_file)
+{
+	struct eap_fast_read_ctx rc;
+	struct eap_fast_pac *pac = NULL;
+	int count = 0;
+	char *pos;
+	const char *err = NULL;
+
+	if (pac_file == NULL)
+		return -1;
+
+	if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
+		return 0;
+
+	if (eap_fast_read_line(&rc, &pos) < 0) {
+		/* empty file - assume it is fine to overwrite */
+		eap_fast_deinit_pac_data(&rc);
+		return 0;
+	}
+	if (os_strcmp(pac_file_hdr, rc.buf) != 0)
+		err = "Unrecognized header line";
+
+	while (!err && eap_fast_read_line(&rc, &pos) == 0) {
+		if (os_strcmp(rc.buf, "START") == 0)
+			err = eap_fast_parse_start(&pac);
+		else if (os_strcmp(rc.buf, "END") == 0) {
+			err = eap_fast_parse_end(pac_root, &pac);
+			count++;
+		} else if (!pac)
+			err = "Unexpected line outside START/END block";
+		else if (os_strcmp(rc.buf, "PAC-Type") == 0)
+			err = eap_fast_parse_pac_type(pac, pos);
+		else if (os_strcmp(rc.buf, "PAC-Key") == 0)
+			err = eap_fast_parse_pac_key(pac, pos);
+		else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
+			err = eap_fast_parse_pac_opaque(pac, pos);
+		else if (os_strcmp(rc.buf, "A-ID") == 0)
+			err = eap_fast_parse_a_id(pac, pos);
+		else if (os_strcmp(rc.buf, "I-ID") == 0)
+			err = eap_fast_parse_i_id(pac, pos);
+		else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
+			err = eap_fast_parse_a_id_info(pac, pos);
+	}
+
+	if (pac) {
+		err = "PAC block not terminated with END";
+		eap_fast_free_pac(pac);
+	}
+
+	eap_fast_deinit_pac_data(&rc);
+
+	if (err) {
+		wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'",
+			   err, pac_file, rc.line);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'",
+		   count, pac_file);
+
+	return 0;
+}
+
+
+static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
+			   const char *field, const u8 *data,
+			   size_t len, int txt)
+{
+	size_t i, need;
+	int ret;
+	char *end;
+
+	if (data == NULL || buf == NULL || *buf == NULL ||
+	    pos == NULL || *pos == NULL || *pos < *buf)
+		return;
+
+	need = os_strlen(field) + len * 2 + 30;
+	if (txt)
+		need += os_strlen(field) + len + 20;
+
+	if (*pos - *buf + need > *buf_len) {
+		char *nbuf = os_realloc(*buf, *buf_len + need);
+		if (nbuf == NULL) {
+			os_free(*buf);
+			*buf = NULL;
+			return;
+		}
+		*pos = nbuf + (*pos - *buf);
+		*buf = nbuf;
+		*buf_len += need;
+	}
+	end = *buf + *buf_len;
+
+	ret = os_snprintf(*pos, end - *pos, "%s=", field);
+	if (os_snprintf_error(end - *pos, ret))
+		return;
+	*pos += ret;
+	*pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
+	ret = os_snprintf(*pos, end - *pos, "\n");
+	if (os_snprintf_error(end - *pos, ret))
+		return;
+	*pos += ret;
+
+	if (txt) {
+		ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
+		if (os_snprintf_error(end - *pos, ret))
+			return;
+		*pos += ret;
+		for (i = 0; i < len; i++) {
+			ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
+			if (os_snprintf_error(end - *pos, ret))
+				return;
+			*pos += ret;
+		}
+		ret = os_snprintf(*pos, end - *pos, "\n");
+		if (os_snprintf_error(end - *pos, ret))
+			return;
+		*pos += ret;
+	}
+}
+
+
+static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file,
+			      char *buf, size_t len)
+{
+	if (os_strncmp(pac_file, "blob://", 7) == 0) {
+		struct wpa_config_blob *blob;
+		blob = os_zalloc(sizeof(*blob));
+		if (blob == NULL)
+			return -1;
+		blob->data = (u8 *) buf;
+		blob->len = len;
+		buf = NULL;
+		blob->name = os_strdup(pac_file + 7);
+		if (blob->name == NULL) {
+			os_free(blob);
+			return -1;
+		}
+		eap_set_config_blob(sm, blob);
+	} else {
+		FILE *f;
+		f = fopen(pac_file, "wb");
+		if (f == NULL) {
+			wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC "
+				   "file '%s' for writing", pac_file);
+			return -1;
+		}
+		if (fwrite(buf, 1, len, f) != len) {
+			wpa_printf(MSG_INFO, "EAP-FAST: Failed to write all "
+				   "PACs into '%s'", pac_file);
+			fclose(f);
+			return -1;
+		}
+		os_free(buf);
+		fclose(f);
+	}
+
+	return 0;
+}
+
+
+static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf,
+				 char **pos, size_t *buf_len)
+{
+	int ret;
+
+	ret = os_snprintf(*pos, *buf + *buf_len - *pos,
+			  "START\nPAC-Type=%d\n", pac->pac_type);
+	if (os_snprintf_error(*buf + *buf_len - *pos, ret))
+		return -1;
+
+	*pos += ret;
+	eap_fast_write(buf, pos, buf_len, "PAC-Key",
+		       pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0);
+	eap_fast_write(buf, pos, buf_len, "PAC-Opaque",
+		       pac->pac_opaque, pac->pac_opaque_len, 0);
+	eap_fast_write(buf, pos, buf_len, "PAC-Info",
+		       pac->pac_info, pac->pac_info_len, 0);
+	eap_fast_write(buf, pos, buf_len, "A-ID",
+		       pac->a_id, pac->a_id_len, 0);
+	eap_fast_write(buf, pos, buf_len, "I-ID",
+		       pac->i_id, pac->i_id_len, 1);
+	eap_fast_write(buf, pos, buf_len, "A-ID-Info",
+		       pac->a_id_info, pac->a_id_info_len, 1);
+	if (*buf == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
+			   "data");
+		return -1;
+	}
+	ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
+	if (os_snprintf_error(*buf + *buf_len - *pos, ret))
+		return -1;
+	*pos += ret;
+
+	return 0;
+}
+
+
+/**
+ * eap_fast_save_pac - Save PAC entries (text format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Root of the PAC list
+ * @pac_file: Name of the PAC file/blob
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+		      const char *pac_file)
+{
+	struct eap_fast_pac *pac;
+	int ret, count = 0;
+	char *buf, *pos;
+	size_t buf_len;
+
+	if (pac_file == NULL)
+		return -1;
+
+	buf_len = 1024;
+	pos = buf = os_malloc(buf_len);
+	if (buf == NULL)
+		return -1;
+
+	ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
+	if (os_snprintf_error(buf + buf_len - pos, ret)) {
+		os_free(buf);
+		return -1;
+	}
+	pos += ret;
+
+	pac = pac_root;
+	while (pac) {
+		if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) {
+			os_free(buf);
+			return -1;
+		}
+		count++;
+		pac = pac->next;
+	}
+
+	if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) {
+		os_free(buf);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'",
+		   count, pac_file);
+
+	return 0;
+}
+
+
+/**
+ * eap_fast_pac_list_truncate - Truncate a PAC list to the given length
+ * @pac_root: Root of the PAC list
+ * @max_len: Maximum length of the list (>= 1)
+ * Returns: Number of PAC entries removed
+ */
+size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
+				  size_t max_len)
+{
+	struct eap_fast_pac *pac, *prev;
+	size_t count;
+
+	pac = pac_root;
+	prev = NULL;
+	count = 0;
+
+	while (pac) {
+		count++;
+		if (count > max_len)
+			break;
+		prev = pac;
+		pac = pac->next;
+	}
+
+	if (count <= max_len || prev == NULL)
+		return 0;
+
+	count = 0;
+	prev->next = NULL;
+
+	while (pac) {
+		prev = pac;
+		pac = pac->next;
+		eap_fast_free_pac(prev);
+		count++;
+	}
+
+	return count;
+}
+
+
+static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac)
+{
+	u8 *pos, *end;
+	u16 type, len;
+
+	pos = pac->pac_info;
+	end = pos + pac->pac_info_len;
+
+	while (pos + 4 < end) {
+		type = WPA_GET_BE16(pos);
+		pos += 2;
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (len > (unsigned int) (end - pos))
+			break;
+
+		if (type == PAC_TYPE_A_ID) {
+			os_free(pac->a_id);
+			pac->a_id = os_malloc(len);
+			if (pac->a_id == NULL)
+				break;
+			os_memcpy(pac->a_id, pos, len);
+			pac->a_id_len = len;
+		}
+
+		if (type == PAC_TYPE_A_ID_INFO) {
+			os_free(pac->a_id_info);
+			pac->a_id_info = os_malloc(len);
+			if (pac->a_id_info == NULL)
+				break;
+			os_memcpy(pac->a_id_info, pos, len);
+			pac->a_id_info_len = len;
+		}
+
+		pos += len;
+	}
+}
+
+
+/**
+ * eap_fast_load_pac_bin - Load PAC entries (binary format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Pointer to root of the PAC list (to be filled)
+ * @pac_file: Name of the PAC file/blob to load
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+			  const char *pac_file)
+{
+	const struct wpa_config_blob *blob = NULL;
+	u8 *buf, *end, *pos;
+	size_t len, count = 0;
+	struct eap_fast_pac *pac, *prev;
+
+	*pac_root = NULL;
+
+	if (pac_file == NULL)
+		return -1;
+
+	if (os_strncmp(pac_file, "blob://", 7) == 0) {
+		blob = eap_get_config_blob(sm, pac_file + 7);
+		if (blob == NULL) {
+			wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
+				   "assume no PAC entries have been "
+				   "provisioned", pac_file + 7);
+			return 0;
+		}
+		buf = blob->data;
+		len = blob->len;
+	} else {
+		buf = (u8 *) os_readfile(pac_file, &len);
+		if (buf == NULL) {
+			wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
+				   "assume no PAC entries have been "
+				   "provisioned", pac_file);
+			return 0;
+		}
+	}
+
+	if (len == 0) {
+		if (blob == NULL)
+			os_free(buf);
+		return 0;
+	}
+
+	if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC ||
+	    WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)",
+			   pac_file);
+		if (blob == NULL)
+			os_free(buf);
+		return -1;
+	}
+
+	pac = prev = NULL;
+	pos = buf + 6;
+	end = buf + len;
+	while (pos < end) {
+		u16 val;
+
+		if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2)
+			goto parse_fail;
+
+		pac = os_zalloc(sizeof(*pac));
+		if (pac == NULL)
+			goto parse_fail;
+
+		pac->pac_type = WPA_GET_BE16(pos);
+		pos += 2;
+		os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
+		pos += EAP_FAST_PAC_KEY_LEN;
+		val = WPA_GET_BE16(pos);
+		pos += 2;
+		if (val > end - pos)
+			goto parse_fail;
+		pac->pac_opaque_len = val;
+		pac->pac_opaque = os_malloc(pac->pac_opaque_len);
+		if (pac->pac_opaque == NULL)
+			goto parse_fail;
+		os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len);
+		pos += pac->pac_opaque_len;
+		if (2 > end - pos)
+			goto parse_fail;
+		val = WPA_GET_BE16(pos);
+		pos += 2;
+		if (val > end - pos)
+			goto parse_fail;
+		pac->pac_info_len = val;
+		pac->pac_info = os_malloc(pac->pac_info_len);
+		if (pac->pac_info == NULL)
+			goto parse_fail;
+		os_memcpy(pac->pac_info, pos, pac->pac_info_len);
+		pos += pac->pac_info_len;
+		eap_fast_pac_get_a_id(pac);
+
+		count++;
+		if (prev)
+			prev->next = pac;
+		else
+			*pac_root = pac;
+		prev = pac;
+	}
+
+	if (blob == NULL)
+		os_free(buf);
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)",
+		   (unsigned long) count, pac_file);
+
+	return 0;
+
+parse_fail:
+	wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)",
+		   pac_file);
+	if (blob == NULL)
+		os_free(buf);
+	if (pac)
+		eap_fast_free_pac(pac);
+	return -1;
+}
+
+
+/**
+ * eap_fast_save_pac_bin - Save PAC entries (binary format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Root of the PAC list
+ * @pac_file: Name of the PAC file/blob
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+			  const char *pac_file)
+{
+	size_t len, count = 0;
+	struct eap_fast_pac *pac;
+	u8 *buf, *pos;
+
+	len = 6;
+	pac = pac_root;
+	while (pac) {
+		if (pac->pac_opaque_len > 65535 ||
+		    pac->pac_info_len > 65535)
+			return -1;
+		len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
+			2 + pac->pac_info_len;
+		pac = pac->next;
+	}
+
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+
+	pos = buf;
+	WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC);
+	pos += 4;
+	WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION);
+	pos += 2;
+
+	pac = pac_root;
+	while (pac) {
+		WPA_PUT_BE16(pos, pac->pac_type);
+		pos += 2;
+		os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN);
+		pos += EAP_FAST_PAC_KEY_LEN;
+		WPA_PUT_BE16(pos, pac->pac_opaque_len);
+		pos += 2;
+		os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
+		pos += pac->pac_opaque_len;
+		WPA_PUT_BE16(pos, pac->pac_info_len);
+		pos += 2;
+		os_memcpy(pos, pac->pac_info, pac->pac_info_len);
+		pos += pac->pac_info_len;
+
+		pac = pac->next;
+		count++;
+	}
+
+	if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) {
+		os_free(buf);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' "
+		   "(bin)", (unsigned long) count, pac_file);
+
+	return 0;
+}
diff --git a/hostap/src/eap_peer/eap_fast_pac.h b/hostap/src/eap_peer/eap_fast_pac.h
new file mode 100644
index 0000000..8815d91
--- /dev/null
+++ b/hostap/src/eap_peer/eap_fast_pac.h
@@ -0,0 +1,50 @@
+/*
+ * EAP peer method: EAP-FAST PAC file processing
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_FAST_PAC_H
+#define EAP_FAST_PAC_H
+
+#include "eap_common/eap_fast_common.h"
+
+struct eap_fast_pac {
+	struct eap_fast_pac *next;
+
+	u8 pac_key[EAP_FAST_PAC_KEY_LEN];
+	u8 *pac_opaque;
+	size_t pac_opaque_len;
+	u8 *pac_info;
+	size_t pac_info_len;
+	u8 *a_id;
+	size_t a_id_len;
+	u8 *i_id;
+	size_t i_id_len;
+	u8 *a_id_info;
+	size_t a_id_info_len;
+	u16 pac_type;
+};
+
+
+void eap_fast_free_pac(struct eap_fast_pac *pac);
+struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
+				       const u8 *a_id, size_t a_id_len,
+				       u16 pac_type);
+int eap_fast_add_pac(struct eap_fast_pac **pac_root,
+		     struct eap_fast_pac **pac_current,
+		     struct eap_fast_pac *entry);
+int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+		      const char *pac_file);
+int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+		      const char *pac_file);
+size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
+				  size_t max_len);
+int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+			  const char *pac_file);
+int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+			  const char *pac_file);
+
+#endif /* EAP_FAST_PAC_H */
diff --git a/hostap/src/eap_peer/eap_gpsk.c b/hostap/src/eap_peer/eap_gpsk.c
new file mode 100644
index 0000000..902b4ba
--- /dev/null
+++ b/hostap/src/eap_peer/eap_gpsk.c
@@ -0,0 +1,793 @@
+/*
+ * EAP peer method: EAP-GPSK (RFC 5433)
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/random.h"
+#include "eap_peer/eap_i.h"
+#include "eap_common/eap_gpsk_common.h"
+
+struct eap_gpsk_data {
+	enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state;
+	u8 rand_server[EAP_GPSK_RAND_LEN];
+	u8 rand_peer[EAP_GPSK_RAND_LEN];
+	u8 msk[EAP_MSK_LEN];
+	u8 emsk[EAP_EMSK_LEN];
+	u8 sk[EAP_GPSK_MAX_SK_LEN];
+	size_t sk_len;
+	u8 pk[EAP_GPSK_MAX_PK_LEN];
+	size_t pk_len;
+	u8 session_id[128];
+	size_t id_len;
+	u8 *id_peer;
+	size_t id_peer_len;
+	u8 *id_server;
+	size_t id_server_len;
+	int vendor; /* CSuite/Specifier */
+	int specifier; /* CSuite/Specifier */
+	u8 *psk;
+	size_t psk_len;
+	u16 forced_cipher; /* force cipher or 0 to allow all supported */
+};
+
+
+static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
+					    u8 identifier,
+					    const u8 *csuite_list,
+					    size_t csuite_list_len);
+static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
+					    u8 identifier);
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_gpsk_state_txt(int state)
+{
+	switch (state) {
+	case GPSK_1:
+		return "GPSK-1";
+	case GPSK_3:
+		return "GPSK-3";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "?";
+	}
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_gpsk_state(struct eap_gpsk_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s",
+		   eap_gpsk_state_txt(data->state),
+		   eap_gpsk_state_txt(state));
+	data->state = state;
+}
+
+
+static void eap_gpsk_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_gpsk_init(struct eap_sm *sm)
+{
+	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) {
+		wpa_printf(MSG_INFO, "EAP-GPSK: No key (password) configured");
+		return NULL;
+	}
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = GPSK_1;
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	if (identity) {
+		data->id_peer = os_malloc(identity_len);
+		if (data->id_peer == NULL) {
+			eap_gpsk_deinit(sm, data);
+			return NULL;
+		}
+		os_memcpy(data->id_peer, identity, identity_len);
+		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);
+		return NULL;
+	}
+	os_memcpy(data->psk, password, password_len);
+	data->psk_len = password_len;
+
+	return data;
+}
+
+
+static void eap_gpsk_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_gpsk_data *data = priv;
+	os_free(data->id_server);
+	os_free(data->id_peer);
+	if (data->psk) {
+		os_memset(data->psk, 0, data->psk_len);
+		os_free(data->psk);
+	}
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data,
+					     const u8 *pos, const u8 *end)
+{
+	u16 alen;
+
+	if (end - pos < 2) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
+		return NULL;
+	}
+	alen = WPA_GET_BE16(pos);
+	pos += 2;
+	if (end - pos < alen) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow");
+		return NULL;
+	}
+	os_free(data->id_server);
+	data->id_server = os_malloc(alen);
+	if (data->id_server == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server");
+		return NULL;
+	}
+	os_memcpy(data->id_server, pos, alen);
+	data->id_server_len = alen;
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server",
+			  data->id_server, data->id_server_len);
+	pos += alen;
+
+	return pos;
+}
+
+
+static const u8 * eap_gpsk_process_rand_server(struct eap_gpsk_data *data,
+					       const u8 *pos, const u8 *end)
+{
+	if (pos == NULL)
+		return NULL;
+
+	if (end - pos < EAP_GPSK_RAND_LEN) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow");
+		return NULL;
+	}
+	os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server",
+		    data->rand_server, EAP_GPSK_RAND_LEN);
+	pos += EAP_GPSK_RAND_LEN;
+
+	return pos;
+}
+
+
+static int eap_gpsk_select_csuite(struct eap_sm *sm,
+				  struct eap_gpsk_data *data,
+				  const u8 *csuite_list,
+				  size_t csuite_list_len)
+{
+	struct eap_gpsk_csuite *csuite;
+	int i, count;
+
+	count = csuite_list_len / sizeof(struct eap_gpsk_csuite);
+	data->vendor = EAP_GPSK_VENDOR_IETF;
+	data->specifier = EAP_GPSK_CIPHER_RESERVED;
+	csuite = (struct eap_gpsk_csuite *) csuite_list;
+	for (i = 0; i < count; i++) {
+		int vendor, specifier;
+		vendor = WPA_GET_BE32(csuite->vendor);
+		specifier = WPA_GET_BE16(csuite->specifier);
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d",
+			   i, vendor, specifier);
+		if (data->vendor == EAP_GPSK_VENDOR_IETF &&
+		    data->specifier == EAP_GPSK_CIPHER_RESERVED &&
+		    eap_gpsk_supported_ciphersuite(vendor, specifier) &&
+		    (!data->forced_cipher || data->forced_cipher == specifier))
+		{
+			data->vendor = vendor;
+			data->specifier = specifier;
+		}
+		csuite++;
+	}
+	if (data->vendor == EAP_GPSK_VENDOR_IETF &&
+	    data->specifier == EAP_GPSK_CIPHER_RESERVED) {
+		wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported "
+			"ciphersuite found");
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d",
+		   data->vendor, data->specifier);
+
+	return 0;
+}
+
+
+static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm,
+					       struct eap_gpsk_data *data,
+					       const u8 **list,
+					       size_t *list_len,
+					       const u8 *pos, const u8 *end)
+{
+	size_t len;
+
+	if (pos == NULL)
+		return NULL;
+
+	if (end - pos < 2) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
+		return NULL;
+	}
+	len = WPA_GET_BE16(pos);
+	pos += 2;
+	if (len > (size_t) (end - pos)) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow");
+		return NULL;
+	}
+	if (len == 0 || (len % sizeof(struct eap_gpsk_csuite))) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu",
+			   (unsigned long) len);
+		return NULL;
+	}
+
+	if (eap_gpsk_select_csuite(sm, data, pos, len) < 0)
+		return NULL;
+
+	*list = pos;
+	*list_len = len;
+	pos += len;
+
+	return pos;
+}
+
+
+static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm,
+					       struct eap_gpsk_data *data,
+					       struct eap_method_ret *ret,
+					       u8 identifier,
+					       const u8 *payload,
+					       size_t payload_len)
+{
+	size_t csuite_list_len;
+	const u8 *csuite_list, *pos, *end;
+	struct wpabuf *resp;
+
+	if (data->state != GPSK_1) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1");
+
+	end = payload + payload_len;
+
+	pos = eap_gpsk_process_id_server(data, payload, end);
+	pos = eap_gpsk_process_rand_server(data, pos, end);
+	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;
+	}
+
+	resp = eap_gpsk_send_gpsk_2(data, identifier,
+				    csuite_list, csuite_list_len);
+	if (resp == NULL)
+		return NULL;
+
+	eap_gpsk_state(data, GPSK_3);
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
+					    u8 identifier,
+					    const u8 *csuite_list,
+					    size_t csuite_list_len)
+{
+	struct wpabuf *resp;
+	size_t len, miclen;
+	u8 *rpos, *start;
+	struct eap_gpsk_csuite *csuite;
+
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2");
+
+	miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+	len = 1 + 2 + data->id_peer_len + 2 + data->id_server_len +
+		2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len +
+		sizeof(struct eap_gpsk_csuite) + 2 + miclen;
+
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
+			     EAP_CODE_RESPONSE, identifier);
+	if (resp == NULL)
+		return NULL;
+
+	wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_2);
+	start = wpabuf_put(resp, 0);
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
+			  data->id_peer, data->id_peer_len);
+	wpabuf_put_be16(resp, data->id_peer_len);
+	wpabuf_put_data(resp, data->id_peer, data->id_peer_len);
+
+	wpabuf_put_be16(resp, data->id_server_len);
+	wpabuf_put_data(resp, data->id_server, data->id_server_len);
+
+	if (random_get_bytes(data->rand_peer, EAP_GPSK_RAND_LEN)) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data "
+			   "for RAND_Peer");
+		eap_gpsk_state(data, FAILURE);
+		wpabuf_free(resp);
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer",
+		    data->rand_peer, EAP_GPSK_RAND_LEN);
+	wpabuf_put_data(resp, data->rand_peer, EAP_GPSK_RAND_LEN);
+	wpabuf_put_data(resp, data->rand_server, EAP_GPSK_RAND_LEN);
+
+	wpabuf_put_be16(resp, csuite_list_len);
+	wpabuf_put_data(resp, csuite_list, csuite_list_len);
+
+	csuite = wpabuf_put(resp, sizeof(*csuite));
+	WPA_PUT_BE32(csuite->vendor, data->vendor);
+	WPA_PUT_BE16(csuite->specifier, data->specifier);
+
+	if (eap_gpsk_derive_keys(data->psk, data->psk_len,
+				 data->vendor, data->specifier,
+				 data->rand_peer, data->rand_server,
+				 data->id_peer, data->id_peer_len,
+				 data->id_server, data->id_server_len,
+				 data->msk, data->emsk,
+				 data->sk, &data->sk_len,
+				 data->pk, &data->pk_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys");
+		eap_gpsk_state(data, FAILURE);
+		wpabuf_free(resp);
+		return NULL;
+	}
+
+	if (eap_gpsk_derive_session_id(data->psk, data->psk_len,
+				       data->vendor, data->specifier,
+				       data->rand_peer, data->rand_server,
+				       data->id_peer, data->id_peer_len,
+				       data->id_server, data->id_server_len,
+				       EAP_TYPE_GPSK,
+				       data->session_id, &data->id_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id");
+		eap_gpsk_state(data, FAILURE);
+		wpabuf_free(resp);
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id",
+		    data->session_id, data->id_len);
+
+	/* No PD_Payload_1 */
+	wpabuf_put_be16(resp, 0);
+
+	rpos = wpabuf_put(resp, miclen);
+	if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+				 data->specifier, start, rpos - start, rpos) <
+	    0) {
+		eap_gpsk_state(data, FAILURE);
+		wpabuf_free(resp);
+		return NULL;
+	}
+
+	return resp;
+}
+
+
+static const u8 * eap_gpsk_validate_rand(struct eap_gpsk_data *data,
+					 const u8 *pos, const u8 *end)
+{
+	if (end - pos < EAP_GPSK_RAND_LEN) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+			   "RAND_Peer");
+		return NULL;
+	}
+	if (os_memcmp(pos, data->rand_peer, EAP_GPSK_RAND_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2 and "
+			   "GPSK-3 did not match");
+		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2",
+			    data->rand_peer, EAP_GPSK_RAND_LEN);
+		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-3",
+			    pos, EAP_GPSK_RAND_LEN);
+		return NULL;
+	}
+	pos += EAP_GPSK_RAND_LEN;
+
+	if (end - pos < EAP_GPSK_RAND_LEN) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+			   "RAND_Server");
+		return NULL;
+	}
+	if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and "
+			   "GPSK-3 did not match");
+		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1",
+			    data->rand_server, EAP_GPSK_RAND_LEN);
+		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-3",
+			    pos, EAP_GPSK_RAND_LEN);
+		return NULL;
+	}
+	pos += EAP_GPSK_RAND_LEN;
+
+	return pos;
+}
+
+
+static const u8 * eap_gpsk_validate_id_server(struct eap_gpsk_data *data,
+					      const u8 *pos, const u8 *end)
+{
+	size_t len;
+
+	if (pos == NULL)
+		return NULL;
+
+	if (end - pos < (int) 2) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+			   "length(ID_Server)");
+		return NULL;
+	}
+
+	len = WPA_GET_BE16(pos);
+	pos += 2;
+
+	if (end - pos < (int) len) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+			   "ID_Server");
+		return NULL;
+	}
+
+	if (len != data->id_server_len ||
+	    os_memcmp(pos, data->id_server, len) != 0) {
+		wpa_printf(MSG_INFO, "EAP-GPSK: ID_Server did not match with "
+			   "the one used in GPSK-1");
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1",
+				  data->id_server, data->id_server_len);
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-3",
+				  pos, len);
+		return NULL;
+	}
+
+	pos += len;
+
+	return pos;
+}
+
+
+static const u8 * eap_gpsk_validate_csuite(struct eap_gpsk_data *data,
+					   const u8 *pos, const u8 *end)
+{
+	int vendor, specifier;
+	const struct eap_gpsk_csuite *csuite;
+
+	if (pos == NULL)
+		return NULL;
+
+	if (end - pos < (int) sizeof(*csuite)) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+			   "CSuite_Sel");
+		return NULL;
+	}
+	csuite = (const struct eap_gpsk_csuite *) pos;
+	vendor = WPA_GET_BE32(csuite->vendor);
+	specifier = WPA_GET_BE16(csuite->specifier);
+	pos += sizeof(*csuite);
+	if (vendor != data->vendor || specifier != data->specifier) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not "
+			   "match with the one sent in GPSK-2 (%d:%d)",
+			   vendor, specifier, data->vendor, data->specifier);
+		return NULL;
+	}
+
+	return pos;
+}
+
+
+static const u8 * eap_gpsk_validate_pd_payload_2(struct eap_gpsk_data *data,
+						 const u8 *pos, const u8 *end)
+{
+	u16 alen;
+
+	if (pos == NULL)
+		return NULL;
+
+	if (end - pos < 2) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+			   "PD_Payload_2 length");
+		return NULL;
+	}
+	alen = WPA_GET_BE16(pos);
+	pos += 2;
+	if (end - pos < alen) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+			   "%d-octet PD_Payload_2", alen);
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen);
+	pos += alen;
+
+	return pos;
+}
+
+
+static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data,
+					       const u8 *payload,
+					       const u8 *pos, const u8 *end)
+{
+	size_t miclen;
+	u8 mic[EAP_GPSK_MAX_MIC_LEN];
+
+	if (pos == NULL)
+		return NULL;
+
+	miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+	if (end - pos < (int) miclen) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
+			   "(left=%lu miclen=%lu)",
+			   (unsigned long) (end - pos),
+			   (unsigned long) miclen);
+		return NULL;
+	}
+	if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+				 data->specifier, payload, pos - payload, mic)
+	    < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
+		return NULL;
+	}
+	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);
+		return NULL;
+	}
+	pos += miclen;
+
+	return pos;
+}
+
+
+static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm,
+					       struct eap_gpsk_data *data,
+					       struct eap_method_ret *ret,
+					       u8 identifier,
+					       const u8 *payload,
+					       size_t payload_len)
+{
+	struct wpabuf *resp;
+	const u8 *pos, *end;
+
+	if (data->state != GPSK_3) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3");
+
+	end = payload + payload_len;
+
+	pos = eap_gpsk_validate_rand(data, payload, end);
+	pos = eap_gpsk_validate_id_server(data, pos, end);
+	pos = eap_gpsk_validate_csuite(data, pos, end);
+	pos = eap_gpsk_validate_pd_payload_2(data, pos, end);
+	pos = eap_gpsk_validate_gpsk_3_mic(data, payload, pos, end);
+
+	if (pos == NULL) {
+		eap_gpsk_state(data, FAILURE);
+		return NULL;
+	}
+	if (pos != end) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
+			   "data in the end of GPSK-2",
+			   (unsigned long) (end - pos));
+	}
+
+	resp = eap_gpsk_send_gpsk_4(data, identifier);
+	if (resp == NULL)
+		return NULL;
+
+	eap_gpsk_state(data, SUCCESS);
+	ret->methodState = METHOD_DONE;
+	ret->decision = DECISION_UNCOND_SUCC;
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
+					    u8 identifier)
+{
+	struct wpabuf *resp;
+	u8 *rpos, *start;
+	size_t mlen;
+
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4");
+
+	mlen = eap_gpsk_mic_len(data->vendor, data->specifier);
+
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, 1 + 2 + mlen,
+			     EAP_CODE_RESPONSE, identifier);
+	if (resp == NULL)
+		return NULL;
+
+	wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_4);
+	start = wpabuf_put(resp, 0);
+
+	/* No PD_Payload_3 */
+	wpabuf_put_be16(resp, 0);
+
+	rpos = wpabuf_put(resp, mlen);
+	if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+				 data->specifier, start, rpos - start, rpos) <
+	    0) {
+		eap_gpsk_state(data, FAILURE);
+		wpabuf_free(resp);
+		return NULL;
+	}
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv,
+					struct eap_method_ret *ret,
+					const struct wpabuf *reqData)
+{
+	struct eap_gpsk_data *data = priv;
+	struct wpabuf *resp;
+	const u8 *pos;
+	size_t len;
+	u8 opcode, id;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len);
+	if (pos == NULL || len < 1) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	id = eap_get_id(reqData);
+	opcode = *pos++;
+	len--;
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", opcode);
+
+	ret->ignore = FALSE;
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_FAIL;
+	ret->allowNotifications = FALSE;
+
+	switch (opcode) {
+	case EAP_GPSK_OPCODE_GPSK_1:
+		resp = eap_gpsk_process_gpsk_1(sm, data, ret, id, pos, len);
+		break;
+	case EAP_GPSK_OPCODE_GPSK_3:
+		resp = eap_gpsk_process_gpsk_3(sm, data, ret, id, pos, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "EAP-GPSK: Ignoring message with unknown opcode %d",
+			   opcode);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	return resp;
+}
+
+
+static Boolean eap_gpsk_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_gpsk_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_gpsk_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_MSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->msk, EAP_MSK_LEN);
+	*len = EAP_MSK_LEN;
+
+	return key;
+}
+
+
+static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_gpsk_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+	*len = EAP_EMSK_LEN;
+
+	return key;
+}
+
+
+static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_gpsk_data *data = priv;
+	u8 *sid;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	sid = os_malloc(data->id_len);
+	if (sid == NULL)
+		return NULL;
+	os_memcpy(sid, data->session_id, data->id_len);
+	*len = data->id_len;
+
+	return sid;
+}
+
+
+int eap_peer_gpsk_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_gpsk_init;
+	eap->deinit = eap_gpsk_deinit;
+	eap->process = eap_gpsk_process;
+	eap->isKeyAvailable = eap_gpsk_isKeyAvailable;
+	eap->getKey = eap_gpsk_getKey;
+	eap->get_emsk = eap_gpsk_get_emsk;
+	eap->getSessionId = eap_gpsk_get_session_id;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_gtc.c b/hostap/src/eap_peer/eap_gtc.c
new file mode 100644
index 0000000..9f3cfbd
--- /dev/null
+++ b/hostap/src/eap_peer/eap_gtc.c
@@ -0,0 +1,145 @@
+/*
+ * EAP peer method: EAP-GTC (RFC 3748)
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+struct eap_gtc_data {
+	int prefix;
+};
+
+
+static void * eap_gtc_init(struct eap_sm *sm)
+{
+	struct eap_gtc_data *data;
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	if (sm->m && sm->m->vendor == EAP_VENDOR_IETF &&
+	    sm->m->method == EAP_TYPE_FAST) {
+		wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix "
+			   "with challenge/response");
+		data->prefix = 1;
+	}
+	return data;
+}
+
+
+static void eap_gtc_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_gtc_data *data = priv;
+	os_free(data);
+}
+
+
+static struct wpabuf * eap_gtc_process(struct eap_sm *sm, void *priv,
+				       struct eap_method_ret *ret,
+				       const struct wpabuf *reqData)
+{
+	struct eap_gtc_data *data = priv;
+	struct wpabuf *resp;
+	const u8 *pos, *password, *identity;
+	size_t password_len, identity_len, len, plen;
+	int otp;
+	u8 id;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, reqData, &len);
+	if (pos == NULL) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	id = eap_get_id(reqData);
+
+	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message", pos, len);
+	if (data->prefix &&
+	    (len < 10 || os_memcmp(pos, "CHALLENGE=", 10) != 0)) {
+		wpa_printf(MSG_DEBUG, "EAP-GTC: Challenge did not start with "
+			   "expected prefix");
+
+		/* Send an empty response in order to allow tunneled
+		 * acknowledgement of the failure. This will also cover the
+		 * error case which seems to use EAP-MSCHAPv2 like error
+		 * reporting with EAP-GTC inside EAP-FAST tunnel. */
+		resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC,
+				     0, EAP_CODE_RESPONSE, id);
+		return resp;
+	}
+
+	password = eap_get_config_otp(sm, &password_len);
+	if (password)
+		otp = 1;
+	else {
+		password = eap_get_config_password(sm, &password_len);
+		otp = 0;
+	}
+
+	if (password == NULL) {
+		wpa_printf(MSG_INFO, "EAP-GTC: Password not configured");
+		eap_sm_request_otp(sm, (const char *) pos, len);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	ret->ignore = FALSE;
+
+	ret->methodState = data->prefix ? METHOD_MAY_CONT : METHOD_DONE;
+	ret->decision = DECISION_COND_SUCC;
+	ret->allowNotifications = FALSE;
+
+	plen = password_len;
+	identity = eap_get_config_identity(sm, &identity_len);
+	if (identity == NULL)
+		return NULL;
+	if (data->prefix)
+		plen += 9 + identity_len + 1;
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, plen,
+			     EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+	if (data->prefix) {
+		wpabuf_put_data(resp, "RESPONSE=", 9);
+		wpabuf_put_data(resp, identity, identity_len);
+		wpabuf_put_u8(resp, '\0');
+	}
+	wpabuf_put_data(resp, password, password_len);
+	wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response",
+			      wpabuf_head_u8(resp) + sizeof(struct eap_hdr) +
+			      1, plen);
+
+	if (otp) {
+		wpa_printf(MSG_DEBUG, "EAP-GTC: Forgetting used password");
+		eap_clear_config_otp(sm);
+	}
+
+	return resp;
+}
+
+
+int eap_peer_gtc_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_gtc_init;
+	eap->deinit = eap_gtc_deinit;
+	eap->process = eap_gtc_process;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_i.h b/hostap/src/eap_peer/eap_i.h
new file mode 100644
index 0000000..99b44da
--- /dev/null
+++ b/hostap/src/eap_peer/eap_i.h
@@ -0,0 +1,389 @@
+/*
+ * EAP peer state machines internal structures (RFC 4137)
+ * 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.
+ */
+
+#ifndef EAP_I_H
+#define EAP_I_H
+
+#include "wpabuf.h"
+#include "utils/list.h"
+#include "eap_peer/eap.h"
+#include "eap_common/eap_common.h"
+
+/* RFC 4137 - EAP Peer state machine */
+
+typedef enum {
+	DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC
+} EapDecision;
+
+typedef enum {
+	METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE
+} EapMethodState;
+
+/**
+ * struct eap_method_ret - EAP return values from struct eap_method::process()
+ *
+ * These structure contains OUT variables for the interface between peer state
+ * machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as
+ * the return value of struct eap_method::process() so it is not included in
+ * this structure.
+ */
+struct eap_method_ret {
+	/**
+	 * ignore - Whether method decided to drop the current packed (OUT)
+	 */
+	Boolean ignore;
+
+	/**
+	 * methodState - Method-specific state (IN/OUT)
+	 */
+	EapMethodState methodState;
+
+	/**
+	 * decision - Authentication decision (OUT)
+	 */
+	EapDecision decision;
+
+	/**
+	 * allowNotifications - Whether method allows notifications (OUT)
+	 */
+	Boolean allowNotifications;
+};
+
+
+/**
+ * struct eap_method - EAP method interface
+ * This structure defines the EAP method interface. Each method will need to
+ * register its own EAP type, EAP name, and set of function pointers for method
+ * specific operations. This interface is based on section 4.4 of RFC 4137.
+ */
+struct eap_method {
+	/**
+	 * vendor - EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
+	 */
+	int vendor;
+
+	/**
+	 * method - EAP type number (EAP_TYPE_*)
+	 */
+	EapType method;
+
+	/**
+	 * name - Name of the method (e.g., "TLS")
+	 */
+	const char *name;
+
+	/**
+	 * init - Initialize an EAP method
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * Returns: Pointer to allocated private data, or %NULL on failure
+	 *
+	 * This function is used to initialize the EAP method explicitly
+	 * instead of using METHOD_INIT state as specific in RFC 4137. The
+	 * method is expected to initialize it method-specific state and return
+	 * a pointer that will be used as the priv argument to other calls.
+	 */
+	void * (*init)(struct eap_sm *sm);
+
+	/**
+	 * deinit - Deinitialize an EAP method
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 *
+	 * Deinitialize the EAP method and free any allocated private data.
+	 */
+	void (*deinit)(struct eap_sm *sm, void *priv);
+
+	/**
+	 * process - Process an EAP request
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * @ret: Return values from EAP request validation and processing
+	 * @reqData: EAP request to be processed (eapReqData)
+	 * Returns: Pointer to allocated EAP response packet (eapRespData)
+	 *
+	 * This function is a combination of m.check(), m.process(), and
+	 * m.buildResp() procedures defined in section 4.4 of RFC 4137 In other
+	 * words, this function validates the incoming request, processes it,
+	 * and build a response packet. m.check() and m.process() return values
+	 * are returned through struct eap_method_ret *ret variable. Caller is
+	 * responsible for freeing the returned EAP response packet.
+	 */
+	struct wpabuf * (*process)(struct eap_sm *sm, void *priv,
+				   struct eap_method_ret *ret,
+				   const struct wpabuf *reqData);
+
+	/**
+	 * isKeyAvailable - Find out whether EAP method has keying material
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * Returns: %TRUE if key material (eapKeyData) is available
+	 */
+	Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv);
+
+	/**
+	 * getKey - Get EAP method specific keying material (eapKeyData)
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * @len: Pointer to variable to store key length (eapKeyDataLen)
+	 * Returns: Keying material (eapKeyData) or %NULL if not available
+	 *
+	 * This function can be used to get the keying material from the EAP
+	 * method. The key may already be stored in the method-specific private
+	 * data or this function may derive the key.
+	 */
+	u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
+
+	/**
+	 * get_status - Get EAP method status
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * @buf: Buffer for status information
+	 * @buflen: Maximum buffer length
+	 * @verbose: Whether to include verbose status information
+	 * Returns: Number of bytes written to buf
+	 *
+	 * Query EAP method for status information. This function fills in a
+	 * text area with current status information from the EAP method. If
+	 * the buffer (buf) is not large enough, status information will be
+	 * truncated to fit the buffer.
+	 */
+	int (*get_status)(struct eap_sm *sm, void *priv, char *buf,
+			  size_t buflen, int verbose);
+
+	/**
+	 * has_reauth_data - Whether method is ready for fast reauthentication
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * Returns: %TRUE or %FALSE based on whether fast reauthentication is
+	 * possible
+	 *
+	 * This function is an optional handler that only EAP methods
+	 * supporting fast re-authentication need to implement.
+	 */
+	Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv);
+
+	/**
+	 * deinit_for_reauth - Release data that is not needed for fast re-auth
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 *
+	 * This function is an optional handler that only EAP methods
+	 * supporting fast re-authentication need to implement. This is called
+	 * when authentication has been completed and EAP state machine is
+	 * requesting that enough state information is maintained for fast
+	 * re-authentication
+	 */
+	void (*deinit_for_reauth)(struct eap_sm *sm, void *priv);
+
+	/**
+	 * init_for_reauth - Prepare for start of fast re-authentication
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 *
+	 * This function is an optional handler that only EAP methods
+	 * supporting fast re-authentication need to implement. This is called
+	 * when EAP authentication is started and EAP state machine is
+	 * requesting fast re-authentication to be used.
+	 */
+	void * (*init_for_reauth)(struct eap_sm *sm, void *priv);
+
+	/**
+	 * get_identity - Get method specific identity for re-authentication
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * @len: Length of the returned identity
+	 * Returns: Pointer to the method specific identity or %NULL if default
+	 * identity is to be used
+	 *
+	 * This function is an optional handler that only EAP methods
+	 * that use method specific identity need to implement.
+	 */
+	const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
+
+	/**
+	 * free - Free EAP method data
+	 * @method: Pointer to the method data registered with
+	 * eap_peer_method_register().
+	 *
+	 * This function will be called when the EAP method is being
+	 * unregistered. If the EAP method allocated resources during
+	 * registration (e.g., allocated struct eap_method), they should be
+	 * freed in this function. No other method functions will be called
+	 * after this call. If this function is not defined (i.e., function
+	 * pointer is %NULL), a default handler is used to release the method
+	 * data with free(method). This is suitable for most cases.
+	 */
+	void (*free)(struct eap_method *method);
+
+#define EAP_PEER_METHOD_INTERFACE_VERSION 1
+	/**
+	 * version - Version of the EAP peer method interface
+	 *
+	 * The EAP peer method implementation should set this variable to
+	 * EAP_PEER_METHOD_INTERFACE_VERSION. This is used to verify that the
+	 * EAP method is using supported API version when using dynamically
+	 * loadable EAP methods.
+	 */
+	int version;
+
+	/**
+	 * next - Pointer to the next EAP method
+	 *
+	 * This variable is used internally in the EAP method registration code
+	 * to create a linked list of registered EAP methods.
+	 */
+	struct eap_method *next;
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+	/**
+	 * dl_handle - Handle for the dynamic library
+	 *
+	 * This variable is used internally in the EAP method registration code
+	 * to store a handle for the dynamic library. If the method is linked
+	 * in statically, this is %NULL.
+	 */
+	void *dl_handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+	/**
+	 * get_emsk - Get EAP method specific keying extended material (EMSK)
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * @len: Pointer to a variable to store EMSK length
+	 * Returns: EMSK or %NULL if not available
+	 *
+	 * This function can be used to get the extended keying material from
+	 * the EAP method. The key may already be stored in the method-specific
+	 * private data or this function may derive the key.
+	 */
+	u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
+
+	/**
+	 * getSessionId - Get EAP method specific Session-Id
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * @len: Pointer to a variable to store Session-Id length
+	 * Returns: Session-Id or %NULL if not available
+	 *
+	 * This function can be used to get the Session-Id from the EAP method.
+	 * The Session-Id may already be stored in the method-specific private
+	 * data or this function may derive the Session-Id.
+	 */
+	u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len);
+};
+
+
+struct eap_erp_key {
+	struct dl_list list;
+	size_t rRK_len;
+	size_t rIK_len;
+	u8 rRK[ERP_MAX_KEY_LEN];
+	u8 rIK[ERP_MAX_KEY_LEN];
+	u32 next_seq;
+	char keyname_nai[];
+};
+
+/**
+ * struct eap_sm - EAP state machine data
+ */
+struct eap_sm {
+	enum {
+		EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED,
+		EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD,
+		EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS,
+		EAP_FAILURE
+	} EAP_state;
+	/* Long-term local variables */
+	EapType selectedMethod;
+	EapMethodState methodState;
+	int lastId;
+	struct wpabuf *lastRespData;
+	EapDecision decision;
+	/* Short-term local variables */
+	Boolean rxReq;
+	Boolean rxSuccess;
+	Boolean rxFailure;
+	int reqId;
+	EapType reqMethod;
+	int reqVendor;
+	u32 reqVendorMethod;
+	Boolean ignore;
+	/* Constants */
+	int ClientTimeout;
+
+	/* Miscellaneous variables */
+	Boolean allowNotifications; /* peer state machine <-> methods */
+	struct wpabuf *eapRespData; /* peer to lower layer */
+	Boolean eapKeyAvailable; /* peer to lower layer */
+	u8 *eapKeyData; /* peer to lower layer */
+	size_t eapKeyDataLen; /* peer to lower layer */
+	u8 *eapSessionId; /* peer to lower layer */
+	size_t eapSessionIdLen; /* peer to lower layer */
+	const struct eap_method *m; /* selected EAP method */
+	/* not defined in RFC 4137 */
+	Boolean changed;
+	void *eapol_ctx;
+	const struct eapol_callbacks *eapol_cb;
+	void *eap_method_priv;
+	int init_phase2;
+	int fast_reauth;
+	Boolean reauthInit; /* send EAP-Identity/Re-auth */
+	u32 erp_seq;
+
+	Boolean rxResp /* LEAP only */;
+	Boolean leap_done;
+	Boolean peap_done;
+	u8 req_sha1[20]; /* SHA1() of the current EAP packet */
+	u8 last_sha1[20]; /* SHA1() of the previously received EAP packet; used
+			   * in duplicate request detection. */
+
+	void *msg_ctx;
+	void *scard_ctx;
+	void *ssl_ctx;
+	void *ssl_ctx2;
+
+	unsigned int workaround;
+
+	/* Optional challenges generated in Phase 1 (EAP-FAST) */
+	u8 *peer_challenge, *auth_challenge;
+
+	int num_rounds;
+	int force_disabled;
+
+	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;
+
+	struct dl_list erp_keys; /* struct eap_erp_key */
+};
+
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash);
+const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len);
+void eap_clear_config_otp(struct eap_sm *sm);
+const char * eap_get_config_phase1(struct eap_sm *sm);
+const char * eap_get_config_phase2(struct eap_sm *sm);
+int eap_get_config_fragment_size(struct eap_sm *sm);
+struct eap_peer_config * eap_get_config(struct eap_sm *sm);
+void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob);
+const struct wpa_config_blob *
+eap_get_config_blob(struct eap_sm *sm, const char *name);
+void eap_notify_pending(struct eap_sm *sm);
+int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method);
+
+#endif /* EAP_I_H */
diff --git a/hostap/src/eap_peer/eap_ikev2.c b/hostap/src/eap_peer/eap_ikev2.c
new file mode 100644
index 0000000..b5ef71b
--- /dev/null
+++ b/hostap/src/eap_peer/eap_ikev2.c
@@ -0,0 +1,536 @@
+/*
+ * EAP-IKEv2 peer (RFC 5106)
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_common/eap_ikev2_common.h"
+#include "ikev2.h"
+
+
+struct eap_ikev2_data {
+	struct ikev2_responder_data ikev2;
+	enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state;
+	struct wpabuf *in_buf;
+	struct wpabuf *out_buf;
+	size_t out_used;
+	size_t fragment_size;
+	int keys_ready;
+	u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN];
+	int keymat_ok;
+};
+
+
+static const char * eap_ikev2_state_txt(int state)
+{
+	switch (state) {
+	case WAIT_START:
+		return "WAIT_START";
+	case PROC_MSG:
+		return "PROC_MSG";
+	case WAIT_FRAG_ACK:
+		return "WAIT_FRAG_ACK";
+	case DONE:
+		return "DONE";
+	case FAIL:
+		return "FAIL";
+	default:
+		return "?";
+	}
+}
+
+
+static void eap_ikev2_state(struct eap_ikev2_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s",
+		   eap_ikev2_state_txt(data->state),
+		   eap_ikev2_state_txt(state));
+	data->state = state;
+}
+
+
+static void * eap_ikev2_init(struct eap_sm *sm)
+{
+	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) {
+		wpa_printf(MSG_INFO, "EAP-IKEV2: No identity available");
+		return NULL;
+	}
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = WAIT_START;
+	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");
+	if (data->ikev2.key_pad == NULL)
+		goto failed;
+	data->ikev2.key_pad_len = 21;
+	data->ikev2.IDr = os_malloc(identity_len);
+	if (data->ikev2.IDr == NULL)
+		goto failed;
+	os_memcpy(data->ikev2.IDr, identity, identity_len);
+	data->ikev2.IDr_len = identity_len;
+
+	password = eap_get_config_password(sm, &password_len);
+	if (password) {
+		data->ikev2.shared_secret = os_malloc(password_len);
+		if (data->ikev2.shared_secret == NULL)
+			goto failed;
+		os_memcpy(data->ikev2.shared_secret, password, password_len);
+		data->ikev2.shared_secret_len = password_len;
+	}
+
+	return data;
+
+failed:
+	ikev2_responder_deinit(&data->ikev2);
+	os_free(data);
+	return NULL;
+}
+
+
+static void eap_ikev2_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_ikev2_data *data = priv;
+	wpabuf_free(data->in_buf);
+	wpabuf_free(data->out_buf);
+	ikev2_responder_deinit(&data->ikev2);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static int eap_ikev2_peer_keymat(struct eap_ikev2_data *data)
+{
+	if (eap_ikev2_derive_keymat(
+		    data->ikev2.proposal.prf, &data->ikev2.keys,
+		    data->ikev2.i_nonce, data->ikev2.i_nonce_len,
+		    data->ikev2.r_nonce, data->ikev2.r_nonce_len,
+		    data->keymat) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to "
+			   "derive key material");
+		return -1;
+	}
+	data->keymat_ok = 1;
+	return 0;
+}
+
+
+static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data,
+					   struct eap_method_ret *ret, u8 id)
+{
+	struct wpabuf *resp;
+	u8 flags;
+	size_t send_len, plen, icv_len = 0;
+
+	ret->ignore = FALSE;
+	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Response");
+	ret->allowNotifications = TRUE;
+
+	flags = 0;
+	send_len = wpabuf_len(data->out_buf) - data->out_used;
+	if (1 + send_len > data->fragment_size) {
+		send_len = data->fragment_size - 1;
+		flags |= IKEV2_FLAGS_MORE_FRAGMENTS;
+		if (data->out_used == 0) {
+			flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
+			send_len -= 4;
+		}
+	}
+
+	plen = 1 + send_len;
+	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
+		plen += 4;
+	if (data->keys_ready) {
+		const struct ikev2_integ_alg *integ;
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum "
+			   "Data");
+		flags |= IKEV2_FLAGS_ICV_INCLUDED;
+		integ = ikev2_get_integ(data->ikev2.proposal.integ);
+		if (integ == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
+				   "transform / cannot generate ICV");
+			return NULL;
+		}
+		icv_len = integ->hash_len;
+
+		plen += icv_len;
+	}
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen,
+			     EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+
+	wpabuf_put_u8(resp, flags); /* Flags */
+	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
+		wpabuf_put_be32(resp, wpabuf_len(data->out_buf));
+
+	wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
+			send_len);
+	data->out_used += send_len;
+
+	if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
+		const u8 *msg = wpabuf_head(resp);
+		size_t len = wpabuf_len(resp);
+		ikev2_integ_hash(data->ikev2.proposal.integ,
+				 data->ikev2.keys.SK_ar,
+				 data->ikev2.keys.SK_integ_len,
+				 msg, len, wpabuf_put(resp, icv_len));
+	}
+
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_FAIL;
+
+	if (data->out_used == wpabuf_len(data->out_buf)) {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
+			   "(message sent completely)",
+			   (unsigned long) send_len);
+		wpabuf_free(data->out_buf);
+		data->out_buf = NULL;
+		data->out_used = 0;
+		switch (data->ikev2.state) {
+		case SA_AUTH:
+			/* SA_INIT was sent out, so message have to be
+			 * integrity protected from now on. */
+			data->keys_ready = 1;
+			break;
+		case IKEV2_DONE:
+			ret->methodState = METHOD_DONE;
+			if (data->state == FAIL)
+				break;
+			ret->decision = DECISION_COND_SUCC;
+			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication "
+				   "completed successfully");
+			if (eap_ikev2_peer_keymat(data))
+				break;
+			eap_ikev2_state(data, DONE);
+			break;
+		case IKEV2_FAILED:
+			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication "
+				   "failed");
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			break;
+		default:
+			break;
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
+			   "(%lu more to send)", (unsigned long) send_len,
+			   (unsigned long) wpabuf_len(data->out_buf) -
+			   data->out_used);
+		eap_ikev2_state(data, WAIT_FRAG_ACK);
+	}
+
+	return resp;
+}
+
+
+static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
+				 const struct wpabuf *reqData,
+				 u8 flags, const u8 *pos, const u8 **end,
+				 int frag_ack)
+{
+	if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
+		int icv_len = eap_ikev2_validate_icv(
+			data->ikev2.proposal.integ, &data->ikev2.keys, 1,
+			reqData, pos, *end);
+		if (icv_len < 0)
+			return -1;
+		/* Hide Integrity Checksum Data from further processing */
+		*end -= icv_len;
+	} else if (data->keys_ready && !frag_ack) {
+		wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
+			   "included integrity checksum");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eap_ikev2_process_cont(struct eap_ikev2_data *data,
+				  const u8 *buf, size_t len)
+{
+	/* Process continuation of a pending message */
+	if (len > wpabuf_tailroom(data->in_buf)) {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow");
+		eap_ikev2_state(data, FAIL);
+		return -1;
+	}
+
+	wpabuf_put_data(data->in_buf, buf, len);
+	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting "
+		   "for %lu bytes more", (unsigned long) len,
+		   (unsigned long) wpabuf_tailroom(data->in_buf));
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data,
+						  struct eap_method_ret *ret,
+						  u8 id, u8 flags,
+						  u32 message_length,
+						  const u8 *buf, size_t len)
+{
+	/* Process a fragment that is not the last one of the message */
+	if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in "
+			   "a fragmented packet");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (data->in_buf == NULL) {
+		/* First fragment of the message */
+		if (message_length > 50000) {
+			/* Limit maximum memory allocation */
+			wpa_printf(MSG_DEBUG,
+				   "EAP-IKEV2: Ignore too long message");
+			ret->ignore = TRUE;
+			return NULL;
+		}
+		data->in_buf = wpabuf_alloc(message_length);
+		if (data->in_buf == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
+				   "message");
+			ret->ignore = TRUE;
+			return NULL;
+		}
+		wpabuf_put_data(data->in_buf, buf, len);
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first "
+			   "fragment, waiting for %lu bytes more",
+			   (unsigned long) len,
+			   (unsigned long) wpabuf_tailroom(data->in_buf));
+	}
+
+	ret->ignore = FALSE;
+	return eap_ikev2_build_frag_ack(id, EAP_CODE_RESPONSE);
+}
+
+
+static struct wpabuf * eap_ikev2_process(struct eap_sm *sm, void *priv,
+					 struct eap_method_ret *ret,
+					 const struct wpabuf *reqData)
+{
+	struct eap_ikev2_data *data = priv;
+	const u8 *start, *pos, *end;
+	size_t len;
+	u8 flags, id;
+	u32 message_length = 0;
+	struct wpabuf tmpbuf;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, reqData, &len);
+	if (pos == NULL) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	id = eap_get_id(reqData);
+
+	start = pos;
+	end = start + len;
+
+	if (len == 0)
+		flags = 0; /* fragment ack */
+	else
+		flags = *pos++;
+
+	if (eap_ikev2_process_icv(data, reqData, flags, pos, &end,
+				  data->state == WAIT_FRAG_ACK && len == 0) < 0)
+	{
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) {
+		if (end - pos < 4) {
+			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow");
+			ret->ignore = TRUE;
+			return NULL;
+		}
+		message_length = WPA_GET_BE32(pos);
+		pos += 4;
+
+		if (message_length < (u32) (end - pos)) {
+			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message "
+				   "Length (%d; %ld remaining in this msg)",
+				   message_length, (long) (end - pos));
+			ret->ignore = TRUE;
+			return NULL;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x "
+		   "Message Length %u", flags, message_length);
+
+	if (data->state == WAIT_FRAG_ACK) {
+		if (len != 0) {
+			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
+				   "in WAIT_FRAG_ACK state");
+			ret->ignore = TRUE;
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged");
+		eap_ikev2_state(data, PROC_MSG);
+		return eap_ikev2_build_msg(data, ret, id);
+	}
+
+	if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+		
+	if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) {
+		return eap_ikev2_process_fragment(data, ret, id, flags,
+						  message_length, pos,
+						  end - pos);
+	}
+
+	if (data->in_buf == NULL) {
+		/* Wrap unfragmented messages as wpabuf without extra copy */
+		wpabuf_set(&tmpbuf, pos, end - pos);
+		data->in_buf = &tmpbuf;
+	}
+
+	if (ikev2_responder_process(&data->ikev2, data->in_buf) < 0) {
+		if (data->in_buf == &tmpbuf)
+			data->in_buf = NULL;
+		eap_ikev2_state(data, FAIL);
+		return NULL;
+	}
+
+	if (data->in_buf != &tmpbuf)
+		wpabuf_free(data->in_buf);
+	data->in_buf = NULL;
+
+	if (data->out_buf == NULL) {
+		data->out_buf = ikev2_responder_build(&data->ikev2);
+		if (data->out_buf == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to generate "
+				   "IKEv2 message");
+			return NULL;
+		}
+		data->out_used = 0;
+	}
+
+	eap_ikev2_state(data, PROC_MSG);
+	return eap_ikev2_build_msg(data, ret, id);
+}
+
+
+static Boolean eap_ikev2_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_ikev2_data *data = priv;
+	return data->state == DONE && data->keymat_ok;
+}
+
+
+static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ikev2_data *data = priv;
+	u8 *key;
+
+	if (data->state != DONE || !data->keymat_ok)
+		return NULL;
+
+	key = os_malloc(EAP_MSK_LEN);
+	if (key) {
+		os_memcpy(key, data->keymat, EAP_MSK_LEN);
+		*len = EAP_MSK_LEN;
+	}
+
+	return key;
+}
+
+
+static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ikev2_data *data = priv;
+	u8 *key;
+
+	if (data->state != DONE || !data->keymat_ok)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key) {
+		os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN);
+		*len = EAP_EMSK_LEN;
+	}
+
+	return key;
+}
+
+
+static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ikev2_data *data = priv;
+	u8 *sid;
+	size_t sid_len;
+	size_t offset;
+
+	if (data->state != DONE || !data->keymat_ok)
+		return NULL;
+
+	sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len;
+	sid = os_malloc(sid_len);
+	if (sid) {
+		offset = 0;
+		sid[offset] = EAP_TYPE_IKEV2;
+		offset++;
+		os_memcpy(sid + offset, data->ikev2.i_nonce,
+			  data->ikev2.i_nonce_len);
+		offset += data->ikev2.i_nonce_len;
+		os_memcpy(sid + offset, data->ikev2.r_nonce,
+			  data->ikev2.r_nonce_len);
+		*len = sid_len;
+		wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id",
+			    sid, sid_len);
+	}
+
+	return sid;
+}
+
+
+int eap_peer_ikev2_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_IKEV2,
+				    "IKEV2");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_ikev2_init;
+	eap->deinit = eap_ikev2_deinit;
+	eap->process = eap_ikev2_process;
+	eap->isKeyAvailable = eap_ikev2_isKeyAvailable;
+	eap->getKey = eap_ikev2_getKey;
+	eap->get_emsk = eap_ikev2_get_emsk;
+	eap->getSessionId = eap_ikev2_get_session_id;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_leap.c b/hostap/src/eap_peer/eap_leap.c
new file mode 100644
index 0000000..e0f8bcf
--- /dev/null
+++ b/hostap/src/eap_peer/eap_leap.c
@@ -0,0 +1,413 @@
+/*
+ * EAP peer method: LEAP
+ * Copyright (c) 2004-2007, 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/ms_funcs.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "eap_i.h"
+
+#define LEAP_VERSION 1
+#define LEAP_CHALLENGE_LEN 8
+#define LEAP_RESPONSE_LEN 24
+#define LEAP_KEY_LEN 16
+
+
+struct eap_leap_data {
+	enum {
+		LEAP_WAIT_CHALLENGE,
+		LEAP_WAIT_SUCCESS,
+		LEAP_WAIT_RESPONSE,
+		LEAP_DONE
+	} state;
+
+	u8 peer_challenge[LEAP_CHALLENGE_LEN];
+	u8 peer_response[LEAP_RESPONSE_LEN];
+
+	u8 ap_challenge[LEAP_CHALLENGE_LEN];
+	u8 ap_response[LEAP_RESPONSE_LEN];
+};
+
+
+static void * eap_leap_init(struct eap_sm *sm)
+{
+	struct eap_leap_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = LEAP_WAIT_CHALLENGE;
+
+	sm->leap_done = FALSE;
+	return data;
+}
+
+
+static void eap_leap_deinit(struct eap_sm *sm, void *priv)
+{
+	os_free(priv);
+}
+
+
+static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv,
+						struct eap_method_ret *ret,
+						const struct wpabuf *reqData)
+{
+	struct eap_leap_data *data = priv;
+	struct wpabuf *resp;
+	const u8 *pos, *challenge, *identity, *password;
+	u8 challenge_len, *rpos;
+	size_t identity_len, password_len, len;
+	int pwhash;
+
+	wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request");
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	password = eap_get_config_password2(sm, &password_len, &pwhash);
+	if (identity == NULL || password == NULL)
+		return NULL;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
+	if (pos == NULL || len < 3) {
+		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (*pos != LEAP_VERSION) {
+		wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
+			   "%d", *pos);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	pos++;
+
+	pos++; /* skip unused byte */
+
+	challenge_len = *pos++;
+	if (challenge_len != LEAP_CHALLENGE_LEN || challenge_len > len - 3) {
+		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge "
+			   "(challenge_len=%d reqDataLen=%lu)",
+			   challenge_len, (unsigned long) wpabuf_len(reqData));
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	challenge = pos;
+	os_memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP",
+		    challenge, LEAP_CHALLENGE_LEN);
+
+	wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response");
+
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
+			     3 + LEAP_RESPONSE_LEN + identity_len,
+			     EAP_CODE_RESPONSE, eap_get_id(reqData));
+	if (resp == NULL)
+		return NULL;
+	wpabuf_put_u8(resp, LEAP_VERSION);
+	wpabuf_put_u8(resp, 0); /* unused */
+	wpabuf_put_u8(resp, LEAP_RESPONSE_LEN);
+	rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN);
+	if (pwhash)
+		challenge_response(challenge, password, rpos);
+	else
+		nt_challenge_response(challenge, password, password_len, rpos);
+	os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response",
+		    rpos, LEAP_RESPONSE_LEN);
+	wpabuf_put_data(resp, identity, identity_len);
+
+	data->state = LEAP_WAIT_SUCCESS;
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv,
+						struct eap_method_ret *ret,
+						const struct wpabuf *reqData)
+{
+	struct eap_leap_data *data = priv;
+	struct wpabuf *resp;
+	u8 *pos;
+	const u8 *identity;
+	size_t identity_len;
+
+	wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success");
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	if (identity == NULL)
+		return NULL;
+
+	if (data->state != LEAP_WAIT_SUCCESS) {
+		wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in "
+			   "unexpected state (%d) - ignored", data->state);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
+			     3 + LEAP_CHALLENGE_LEN + identity_len,
+			     EAP_CODE_REQUEST, eap_get_id(reqData));
+	if (resp == NULL)
+		return NULL;
+	wpabuf_put_u8(resp, LEAP_VERSION);
+	wpabuf_put_u8(resp, 0); /* unused */
+	wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN);
+	pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN);
+	if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) {
+		wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
+			   "for challenge");
+		wpabuf_free(resp);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos,
+		    LEAP_CHALLENGE_LEN);
+	wpabuf_put_data(resp, identity, identity_len);
+
+	data->state = LEAP_WAIT_RESPONSE;
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv,
+						 struct eap_method_ret *ret,
+						 const struct wpabuf *reqData)
+{
+	struct eap_leap_data *data = priv;
+	const u8 *pos, *password;
+	u8 response_len, pw_hash[16], pw_hash_hash[16],
+		expected[LEAP_RESPONSE_LEN];
+	size_t password_len, len;
+	int pwhash;
+
+	wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response");
+
+	password = eap_get_config_password2(sm, &password_len, &pwhash);
+	if (password == NULL)
+		return NULL;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
+	if (pos == NULL || len < 3) {
+		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (*pos != LEAP_VERSION) {
+		wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
+			   "%d", *pos);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	pos++;
+
+	pos++; /* skip unused byte */
+
+	response_len = *pos++;
+	if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) {
+		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response "
+			   "(response_len=%d reqDataLen=%lu)",
+			   response_len, (unsigned long) wpabuf_len(reqData));
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP",
+		    pos, LEAP_RESPONSE_LEN);
+	os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN);
+
+	if (pwhash) {
+		if (hash_nt_password_hash(password, pw_hash_hash)) {
+			ret->ignore = TRUE;
+			return NULL;
+		}
+	} else {
+		if (nt_password_hash(password, password_len, pw_hash) ||
+		    hash_nt_password_hash(pw_hash, pw_hash_hash)) {
+			ret->ignore = TRUE;
+			return NULL;
+		}
+	}
+	challenge_response(data->ap_challenge, pw_hash_hash, expected);
+
+	ret->methodState = METHOD_DONE;
+	ret->allowNotifications = FALSE;
+
+	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",
+			    expected, LEAP_RESPONSE_LEN);
+		ret->decision = DECISION_FAIL;
+		return NULL;
+	}
+
+	ret->decision = DECISION_UNCOND_SUCC;
+
+	/* LEAP is somewhat odd method since it sends EAP-Success in the middle
+	 * of the authentication. Use special variable to transit EAP state
+	 * machine to SUCCESS state. */
+	sm->leap_done = TRUE;
+	data->state = LEAP_DONE;
+
+	/* No more authentication messages expected; AP will send EAPOL-Key
+	 * frames if encryption is enabled. */
+	return NULL;
+}
+
+
+static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv,
+					struct eap_method_ret *ret,
+					const struct wpabuf *reqData)
+{
+	const struct eap_hdr *eap;
+	size_t password_len;
+	const u8 *password;
+
+	password = eap_get_config_password(sm, &password_len);
+	if (password == NULL) {
+		wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured");
+		eap_sm_request_password(sm);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	/*
+	 * LEAP needs to be able to handle EAP-Success frame which does not
+	 * include Type field. Consequently, eap_hdr_validate() cannot be used
+	 * here. This validation will be done separately for EAP-Request and
+	 * EAP-Response frames.
+	 */
+	eap = wpabuf_head(reqData);
+	if (wpabuf_len(reqData) < sizeof(*eap) ||
+	    be_to_host16(eap->length) > wpabuf_len(reqData)) {
+		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	ret->ignore = FALSE;
+	ret->allowNotifications = TRUE;
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_FAIL;
+
+	sm->leap_done = FALSE;
+
+	switch (eap->code) {
+	case EAP_CODE_REQUEST:
+		return eap_leap_process_request(sm, priv, ret, reqData);
+	case EAP_CODE_SUCCESS:
+		return eap_leap_process_success(sm, priv, ret, reqData);
+	case EAP_CODE_RESPONSE:
+		return eap_leap_process_response(sm, priv, ret, reqData);
+	default:
+		wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - "
+			   "ignored", eap->code);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+}
+
+
+static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_leap_data *data = priv;
+	return data->state == LEAP_DONE;
+}
+
+
+static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_leap_data *data = priv;
+	u8 *key, pw_hash_hash[16], pw_hash[16];
+	const u8 *addr[5], *password;
+	size_t elen[5], password_len;
+	int pwhash;
+
+	if (data->state != LEAP_DONE)
+		return NULL;
+
+	password = eap_get_config_password2(sm, &password_len, &pwhash);
+	if (password == NULL)
+		return NULL;
+
+	key = os_malloc(LEAP_KEY_LEN);
+	if (key == NULL)
+		return NULL;
+
+	if (pwhash) {
+		if (hash_nt_password_hash(password, pw_hash_hash)) {
+			os_free(key);
+			return NULL;
+		}
+	} else {
+		if (nt_password_hash(password, password_len, pw_hash) ||
+		    hash_nt_password_hash(pw_hash, pw_hash_hash)) {
+			os_free(key);
+			return NULL;
+		}
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash",
+			pw_hash_hash, 16);
+	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge",
+		    data->peer_challenge, LEAP_CHALLENGE_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response",
+		    data->peer_response, LEAP_RESPONSE_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge",
+		    data->ap_challenge, LEAP_CHALLENGE_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response",
+		    data->ap_response, LEAP_RESPONSE_LEN);
+
+	addr[0] = pw_hash_hash;
+	elen[0] = 16;
+	addr[1] = data->ap_challenge;
+	elen[1] = LEAP_CHALLENGE_LEN;
+	addr[2] = data->ap_response;
+	elen[2] = LEAP_RESPONSE_LEN;
+	addr[3] = data->peer_challenge;
+	elen[3] = LEAP_CHALLENGE_LEN;
+	addr[4] = data->peer_response;
+	elen[4] = LEAP_RESPONSE_LEN;
+	md5_vector(5, addr, elen, key);
+	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;
+}
+
+
+int eap_peer_leap_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_leap_init;
+	eap->deinit = eap_leap_deinit;
+	eap->process = eap_leap_process;
+	eap->isKeyAvailable = eap_leap_isKeyAvailable;
+	eap->getKey = eap_leap_getKey;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_md5.c b/hostap/src/eap_peer/eap_md5.c
new file mode 100644
index 0000000..d06befa
--- /dev/null
+++ b/hostap/src/eap_peer/eap_md5.c
@@ -0,0 +1,120 @@
+/*
+ * EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994)
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_common/chap.h"
+
+
+static void * eap_md5_init(struct eap_sm *sm)
+{
+	/* No need for private data. However, must return non-NULL to indicate
+	 * success. */
+	return (void *) 1;
+}
+
+
+static void eap_md5_deinit(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv,
+				       struct eap_method_ret *ret,
+				       const struct wpabuf *reqData)
+{
+	struct wpabuf *resp;
+	const u8 *pos, *challenge, *password;
+	u8 *rpos, id;
+	size_t len, challenge_len, password_len;
+
+	password = eap_get_config_password(sm, &password_len);
+	if (password == NULL) {
+		wpa_printf(MSG_INFO, "EAP-MD5: Password not configured");
+		eap_sm_request_password(sm);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, reqData, &len);
+	if (pos == NULL || len == 0) {
+		wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame (pos=%p len=%lu)",
+			   pos, (unsigned long) len);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	/*
+	 * CHAP Challenge:
+	 * Value-Size (1 octet) | Value(Challenge) | Name(optional)
+	 */
+	challenge_len = *pos++;
+	if (challenge_len == 0 || challenge_len > len - 1) {
+		wpa_printf(MSG_INFO, "EAP-MD5: Invalid challenge "
+			   "(challenge_len=%lu len=%lu)",
+			   (unsigned long) challenge_len, (unsigned long) len);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	ret->ignore = FALSE;
+	challenge = pos;
+	wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge",
+		    challenge, challenge_len);
+
+	wpa_printf(MSG_DEBUG, "EAP-MD5: Generating Challenge Response");
+	ret->methodState = METHOD_DONE;
+	ret->decision = DECISION_COND_SUCC;
+	ret->allowNotifications = TRUE;
+
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHAP_MD5_LEN,
+			     EAP_CODE_RESPONSE, eap_get_id(reqData));
+	if (resp == NULL)
+		return NULL;
+
+	/*
+	 * CHAP Response:
+	 * Value-Size (1 octet) | Value(Response) | Name(optional)
+	 */
+	wpabuf_put_u8(resp, CHAP_MD5_LEN);
+
+	id = eap_get_id(resp);
+	rpos = wpabuf_put(resp, CHAP_MD5_LEN);
+	if (chap_md5(id, password, password_len, challenge, challenge_len,
+		     rpos)) {
+		wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed");
+		ret->ignore = TRUE;
+		wpabuf_free(resp);
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN);
+
+	return resp;
+}
+
+
+int eap_peer_md5_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_md5_init;
+	eap->deinit = eap_md5_deinit;
+	eap->process = eap_md5_process;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_methods.c b/hostap/src/eap_peer/eap_methods.c
new file mode 100644
index 0000000..1bdd81e
--- /dev/null
+++ b/hostap/src/eap_peer/eap_methods.c
@@ -0,0 +1,369 @@
+/*
+ * EAP peer: Method registration
+ * Copyright (c) 2004-2007, 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"
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+#include <dlfcn.h>
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_methods.h"
+
+
+static struct eap_method *eap_methods = NULL;
+
+
+/**
+ * eap_peer_get_eap_method - Get EAP method based on type number
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @method: EAP type number
+ * Returns: Pointer to EAP method or %NULL if not found
+ */
+const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method)
+{
+	struct eap_method *m;
+	for (m = eap_methods; m; m = m->next) {
+		if (m->vendor == vendor && m->method == method)
+			return m;
+	}
+	return NULL;
+}
+
+
+/**
+ * eap_peer_get_type - Get EAP type for the given EAP method name
+ * @name: EAP method name, e.g., TLS
+ * @vendor: Buffer for returning EAP Vendor-Id
+ * Returns: EAP method type or %EAP_TYPE_NONE if not found
+ *
+ * This function maps EAP type names into EAP type numbers based on the list of
+ * EAP methods included in the build.
+ */
+EapType eap_peer_get_type(const char *name, int *vendor)
+{
+	struct eap_method *m;
+	for (m = eap_methods; m; m = m->next) {
+		if (os_strcmp(m->name, name) == 0) {
+			*vendor = m->vendor;
+			return m->method;
+		}
+	}
+	*vendor = EAP_VENDOR_IETF;
+	return EAP_TYPE_NONE;
+}
+
+
+/**
+ * eap_get_name - Get EAP method name for the given EAP type
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @type: EAP method type
+ * Returns: EAP method name, e.g., TLS, or %NULL if not found
+ *
+ * This function maps EAP type numbers into EAP type names based on the list of
+ * EAP methods included in the build.
+ */
+const char * eap_get_name(int vendor, EapType type)
+{
+	struct eap_method *m;
+	if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED)
+		return "expanded";
+	for (m = eap_methods; m; m = m->next) {
+		if (m->vendor == vendor && m->method == type)
+			return m->name;
+	}
+	return NULL;
+}
+
+
+/**
+ * eap_get_names - Get space separated list of names for supported EAP methods
+ * @buf: Buffer for names
+ * @buflen: Buffer length
+ * Returns: Number of characters written into buf (not including nul
+ * termination)
+ */
+size_t eap_get_names(char *buf, size_t buflen)
+{
+	char *pos, *end;
+	struct eap_method *m;
+	int ret;
+
+	if (buflen == 0)
+		return 0;
+
+	pos = buf;
+	end = pos + buflen;
+
+	for (m = eap_methods; m; m = m->next) {
+		ret = os_snprintf(pos, end - pos, "%s%s",
+				  m == eap_methods ? "" : " ", m->name);
+		if (os_snprintf_error(end - pos, ret))
+			break;
+		pos += ret;
+	}
+	buf[buflen - 1] = '\0';
+
+	return pos - buf;
+}
+
+
+/**
+ * eap_get_names_as_string_array - Get supported EAP methods as string array
+ * @num: Buffer for returning the number of items in array, not including %NULL
+ * terminator. This parameter can be %NULL if the length is not needed.
+ * Returns: A %NULL-terminated array of strings, or %NULL on error.
+ *
+ * This function returns the list of names for all supported EAP methods as an
+ * array of strings. The caller must free the returned array items and the
+ * array.
+ */
+char ** eap_get_names_as_string_array(size_t *num)
+{
+	struct eap_method *m;
+	size_t array_len = 0;
+	char **array;
+	int i = 0, j;
+
+	for (m = eap_methods; m; m = m->next)
+		array_len++;
+
+	array = os_calloc(array_len + 1, sizeof(char *));
+	if (array == NULL)
+		return NULL;
+
+	for (m = eap_methods; m; m = m->next) {
+		array[i++] = os_strdup(m->name);
+		if (array[i - 1] == NULL) {
+			for (j = 0; j < i; j++)
+				os_free(array[j]);
+			os_free(array);
+			return NULL;
+		}
+	}
+	array[i] = NULL;
+
+	if (num)
+		*num = array_len;
+
+	return array;
+}
+
+
+/**
+ * eap_peer_get_methods - Get a list of enabled EAP peer methods
+ * @count: Set to number of available methods
+ * Returns: List of enabled EAP peer methods
+ */
+const struct eap_method * eap_peer_get_methods(size_t *count)
+{
+	int c = 0;
+	struct eap_method *m;
+
+	for (m = eap_methods; m; m = m->next)
+		c++;
+	
+	*count = c;
+	return eap_methods;
+}
+
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+/**
+ * eap_peer_method_load - Load a dynamic EAP method library (shared object)
+ * @so: File path for the shared object file to load
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_method_load(const char *so)
+{
+	void *handle;
+	int (*dyn_init)(void);
+	int ret;
+
+	handle = dlopen(so, RTLD_LAZY);
+	if (handle == NULL) {
+		wpa_printf(MSG_ERROR, "EAP: Failed to open dynamic EAP method "
+			   "'%s': %s", so, dlerror());
+		return -1;
+	}
+
+	dyn_init = dlsym(handle, "eap_peer_method_dynamic_init");
+	if (dyn_init == NULL) {
+		dlclose(handle);
+		wpa_printf(MSG_ERROR, "EAP: Invalid EAP method '%s' - no "
+			   "eap_peer_method_dynamic_init()", so);
+		return -1;
+	}
+
+	ret = dyn_init();
+	if (ret) {
+		dlclose(handle);
+		wpa_printf(MSG_ERROR, "EAP: Failed to add EAP method '%s' - "
+			   "ret %d", so, ret);
+		return ret;
+	}
+
+	/* Store the handle for this shared object. It will be freed with
+	 * dlclose() when the EAP method is unregistered. */
+	eap_methods->dl_handle = handle;
+
+	wpa_printf(MSG_DEBUG, "EAP: Loaded dynamic EAP method: '%s'", so);
+
+	return 0;
+}
+
+
+/**
+ * eap_peer_method_unload - Unload a dynamic EAP method library (shared object)
+ * @method: Pointer to the dynamically loaded EAP method
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to unload EAP methods that have been previously
+ * loaded with eap_peer_method_load(). Before unloading the method, all
+ * references to the method must be removed to make sure that no dereferences
+ * of freed memory will occur after unloading.
+ */
+int eap_peer_method_unload(struct eap_method *method)
+{
+	struct eap_method *m, *prev;
+	void *handle;
+
+	m = eap_methods;
+	prev = NULL;
+	while (m) {
+		if (m == method)
+			break;
+		prev = m;
+		m = m->next;
+	}
+
+	if (m == NULL || m->dl_handle == NULL)
+		return -1;
+
+	if (prev)
+		prev->next = m->next;
+	else
+		eap_methods = m->next;
+
+	handle = m->dl_handle;
+
+	if (m->free)
+		m->free(m);
+	else
+		eap_peer_method_free(m);
+
+	dlclose(handle);
+
+	return 0;
+}
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+
+/**
+ * eap_peer_method_alloc - Allocate EAP peer method structure
+ * @version: Version of the EAP peer method interface (set to
+ * EAP_PEER_METHOD_INTERFACE_VERSION)
+ * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
+ * @method: EAP type number (EAP_TYPE_*)
+ * @name: Name of the method (e.g., "TLS")
+ * Returns: Allocated EAP method structure or %NULL on failure
+ *
+ * The returned structure should be freed with eap_peer_method_free() when it
+ * is not needed anymore.
+ */
+struct eap_method * eap_peer_method_alloc(int version, int vendor,
+					  EapType method, const char *name)
+{
+	struct eap_method *eap;
+	eap = os_zalloc(sizeof(*eap));
+	if (eap == NULL)
+		return NULL;
+	eap->version = version;
+	eap->vendor = vendor;
+	eap->method = method;
+	eap->name = name;
+	return eap;
+}
+
+
+/**
+ * eap_peer_method_free - Free EAP peer method structure
+ * @method: Method structure allocated with eap_peer_method_alloc()
+ */
+void eap_peer_method_free(struct eap_method *method)
+{
+	os_free(method);
+}
+
+
+/**
+ * eap_peer_method_register - Register an EAP peer method
+ * @method: EAP method to register
+ * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method
+ * has already been registered
+ *
+ * Each EAP peer method needs to call this function to register itself as a
+ * supported EAP method.
+ */
+int eap_peer_method_register(struct eap_method *method)
+{
+	struct eap_method *m, *last = NULL;
+
+	if (method == NULL || method->name == NULL ||
+	    method->version != EAP_PEER_METHOD_INTERFACE_VERSION)
+		return -1;
+
+	for (m = eap_methods; m; m = m->next) {
+		if ((m->vendor == method->vendor &&
+		     m->method == method->method) ||
+		    os_strcmp(m->name, method->name) == 0)
+			return -2;
+		last = m;
+	}
+
+	if (last)
+		last->next = method;
+	else
+		eap_methods = method;
+
+	return 0;
+}
+
+
+/**
+ * eap_peer_unregister_methods - Unregister EAP peer methods
+ *
+ * This function is called at program termination to unregister all EAP peer
+ * methods.
+ */
+void eap_peer_unregister_methods(void)
+{
+	struct eap_method *m;
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+	void *handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+	while (eap_methods) {
+		m = eap_methods;
+		eap_methods = eap_methods->next;
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+		handle = m->dl_handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+		if (m->free)
+			m->free(m);
+		else
+			eap_peer_method_free(m);
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+		if (handle)
+			dlclose(handle);
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+	}
+}
diff --git a/hostap/src/eap_peer/eap_methods.h b/hostap/src/eap_peer/eap_methods.h
new file mode 100644
index 0000000..e35c919
--- /dev/null
+++ b/hostap/src/eap_peer/eap_methods.h
@@ -0,0 +1,111 @@
+/*
+ * EAP peer: Method registration
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_METHODS_H
+#define EAP_METHODS_H
+
+#include "eap_common/eap_defs.h"
+
+const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method);
+const struct eap_method * eap_peer_get_methods(size_t *count);
+
+struct eap_method * eap_peer_method_alloc(int version, int vendor,
+					  EapType method, const char *name);
+void eap_peer_method_free(struct eap_method *method);
+int eap_peer_method_register(struct eap_method *method);
+
+
+#ifdef IEEE8021X_EAPOL
+
+EapType eap_peer_get_type(const char *name, int *vendor);
+const char * eap_get_name(int vendor, EapType type);
+size_t eap_get_names(char *buf, size_t buflen);
+char ** eap_get_names_as_string_array(size_t *num);
+void eap_peer_unregister_methods(void);
+
+#else /* IEEE8021X_EAPOL */
+
+static inline EapType eap_peer_get_type(const char *name, int *vendor)
+{
+	*vendor = EAP_VENDOR_IETF;
+	return EAP_TYPE_NONE;
+}
+
+static inline const char * eap_get_name(int vendor, EapType type)
+{
+	return NULL;
+}
+
+static inline size_t eap_get_names(char *buf, size_t buflen)
+{
+	return 0;
+}
+
+static inline int eap_peer_register_methods(void)
+{
+	return 0;
+}
+
+static inline void eap_peer_unregister_methods(void)
+{
+}
+
+static inline char ** eap_get_names_as_string_array(size_t *num)
+{
+	return NULL;
+}
+
+#endif /* IEEE8021X_EAPOL */
+
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+
+int eap_peer_method_load(const char *so);
+int eap_peer_method_unload(struct eap_method *method);
+
+#else /* CONFIG_DYNAMIC_EAP_METHODS */
+
+static inline int eap_peer_method_load(const char *so)
+{
+	return 0;
+}
+
+static inline int eap_peer_method_unload(struct eap_method *method)
+{
+	return 0;
+}
+
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+/* EAP peer method registration calls for statically linked in methods */
+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);
+int eap_peer_gtc_register(void);
+int eap_peer_otp_register(void);
+int eap_peer_sim_register(void);
+int eap_peer_leap_register(void);
+int eap_peer_psk_register(void);
+int eap_peer_aka_register(void);
+int eap_peer_aka_prime_register(void);
+int eap_peer_fast_register(void);
+int eap_peer_pax_register(void);
+int eap_peer_sake_register(void);
+int eap_peer_gpsk_register(void);
+int eap_peer_wsc_register(void);
+int eap_peer_ikev2_register(void);
+int eap_peer_vendor_test_register(void);
+int eap_peer_tnc_register(void);
+int eap_peer_pwd_register(void);
+int eap_peer_eke_register(void);
+
+#endif /* EAP_METHODS_H */
diff --git a/hostap/src/eap_peer/eap_mschapv2.c b/hostap/src/eap_peer/eap_mschapv2.c
new file mode 100644
index 0000000..6acf1e8
--- /dev/null
+++ b/hostap/src/eap_peer/eap_mschapv2.c
@@ -0,0 +1,901 @@
+/*
+ * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26).
+ * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP
+ * Extensions Protocol, Version 2, for mutual authentication and key
+ * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in
+ * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in
+ * RFC 3079.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/ms_funcs.h"
+#include "crypto/random.h"
+#include "common/wpa_ctrl.h"
+#include "mschapv2.h"
+#include "eap_i.h"
+#include "eap_config.h"
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_mschapv2_hdr {
+	u8 op_code; /* MSCHAPV2_OP_* */
+	u8 mschapv2_id; /* usually same as EAP identifier; must be changed
+			 * for challenges, but not for success/failure */
+	u8 ms_length[2]; /* Note: misaligned; length - 5 */
+	/* followed by data */
+} STRUCT_PACKED;
+
+/* Response Data field */
+struct ms_response {
+	u8 peer_challenge[MSCHAPV2_CHAL_LEN];
+	u8 reserved[8];
+	u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
+	u8 flags;
+} STRUCT_PACKED;
+
+/* Change-Password Data field */
+struct ms_change_password {
+	u8 encr_password[516];
+	u8 encr_hash[16];
+	u8 peer_challenge[MSCHAPV2_CHAL_LEN];
+	u8 reserved[8];
+	u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
+	u8 flags[2];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define MSCHAPV2_OP_CHALLENGE 1
+#define MSCHAPV2_OP_RESPONSE 2
+#define MSCHAPV2_OP_SUCCESS 3
+#define MSCHAPV2_OP_FAILURE 4
+#define MSCHAPV2_OP_CHANGE_PASSWORD 7
+
+#define ERROR_RESTRICTED_LOGON_HOURS 646
+#define ERROR_ACCT_DISABLED 647
+#define ERROR_PASSWD_EXPIRED 648
+#define ERROR_NO_DIALIN_PERMISSION 649
+#define ERROR_AUTHENTICATION_FAILURE 691
+#define ERROR_CHANGING_PASSWORD 709
+
+#define PASSWD_CHANGE_CHAL_LEN 16
+#define MSCHAPV2_KEY_LEN 16
+
+
+struct eap_mschapv2_data {
+	u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+	int auth_response_valid;
+
+	int prev_error;
+	u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
+	int passwd_change_challenge_valid;
+	int passwd_change_version;
+
+	/* Optional challenge values generated in EAP-FAST Phase 1 negotiation
+	 */
+	u8 *peer_challenge;
+	u8 *auth_challenge;
+
+	int phase2;
+	u8 master_key[MSCHAPV2_MASTER_KEY_LEN];
+	int master_key_valid;
+	int success;
+
+	struct wpabuf *prev_challenge;
+};
+
+
+static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_mschapv2_init(struct eap_sm *sm)
+{
+	struct eap_mschapv2_data *data;
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	if (sm->peer_challenge) {
+		data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
+		if (data->peer_challenge == NULL) {
+			eap_mschapv2_deinit(sm, data);
+			return NULL;
+		}
+		os_memcpy(data->peer_challenge, sm->peer_challenge,
+			  MSCHAPV2_CHAL_LEN);
+	}
+
+	if (sm->auth_challenge) {
+		data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
+		if (data->auth_challenge == NULL) {
+			eap_mschapv2_deinit(sm, data);
+			return NULL;
+		}
+		os_memcpy(data->auth_challenge, sm->auth_challenge,
+			  MSCHAPV2_CHAL_LEN);
+	}
+
+	data->phase2 = sm->init_phase2;
+
+	return data;
+}
+
+
+static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_mschapv2_data *data = priv;
+	os_free(data->peer_challenge);
+	os_free(data->auth_challenge);
+	wpabuf_free(data->prev_challenge);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_mschapv2_challenge_reply(
+	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id,
+	u8 mschapv2_id, const u8 *auth_challenge)
+{
+	struct wpabuf *resp;
+	struct eap_mschapv2_hdr *ms;
+	u8 *peer_challenge;
+	int ms_len;
+	struct ms_response *r;
+	size_t identity_len, password_len;
+	const u8 *identity, *password;
+	int pwhash;
+
+	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	password = eap_get_config_password2(sm, &password_len, &pwhash);
+	if (identity == NULL || password == NULL)
+		return NULL;
+
+	ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+			     EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+
+	ms = wpabuf_put(resp, sizeof(*ms));
+	ms->op_code = MSCHAPV2_OP_RESPONSE;
+	ms->mschapv2_id = mschapv2_id;
+	if (data->prev_error) {
+		/*
+		 * TODO: this does not seem to be enough when processing two
+		 * or more failure messages. IAS did not increment mschapv2_id
+		 * in its own packets, but it seemed to expect the peer to
+		 * increment this for all packets(?).
+		 */
+		ms->mschapv2_id++;
+	}
+	WPA_PUT_BE16(ms->ms_length, ms_len);
+
+	wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */
+
+	/* Response */
+	r = wpabuf_put(resp, sizeof(*r));
+	peer_challenge = r->peer_challenge;
+	if (data->peer_challenge) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
+			   "in Phase 1");
+		peer_challenge = data->peer_challenge;
+		os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
+	} else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
+		wpabuf_free(resp);
+		return NULL;
+	}
+	os_memset(r->reserved, 0, 8);
+	if (data->auth_challenge) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
+			   "in Phase 1");
+		auth_challenge = data->auth_challenge;
+	}
+	if (mschapv2_derive_response(identity, identity_len, password,
+				     password_len, pwhash, auth_challenge,
+				     peer_challenge, r->nt_response,
+				     data->auth_response, data->master_key)) {
+		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive "
+			   "response");
+		wpabuf_free(resp);
+		return NULL;
+	}
+	data->auth_response_valid = 1;
+	data->master_key_valid = 1;
+
+	r->flags = 0; /* reserved, must be zero */
+
+	wpabuf_put_data(resp, identity, identity_len);
+	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
+		   "(response)", id, ms->mschapv2_id);
+	return resp;
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in the request
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_challenge(
+	struct eap_sm *sm, struct eap_mschapv2_data *data,
+	struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req,
+	size_t req_len, u8 id)
+{
+	size_t len, challenge_len;
+	const u8 *pos, *challenge;
+
+	if (eap_get_config_identity(sm, &len) == NULL ||
+	    eap_get_config_password(sm, &len) == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
+	if (req_len < sizeof(*req) + 1) {
+		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data "
+			   "(len %lu)", (unsigned long) req_len);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	pos = (const u8 *) (req + 1);
+	challenge_len = *pos++;
+	len = req_len - sizeof(*req) - 1;
+	if (challenge_len != MSCHAPV2_CHAL_LEN) {
+		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
+			   "%lu", (unsigned long) challenge_len);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (len < challenge_len) {
+		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
+			   " packet: len=%lu challenge_len=%lu",
+			   (unsigned long) len, (unsigned long) challenge_len);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (data->passwd_change_challenge_valid) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the "
+			   "failure message");
+		challenge = data->passwd_change_challenge;
+	} else
+		challenge = pos;
+	pos += challenge_len;
+	len -= challenge_len;
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
+		    pos, len);
+
+	ret->ignore = FALSE;
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_FAIL;
+	ret->allowNotifications = TRUE;
+
+	return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id,
+					    challenge);
+}
+
+
+static void eap_mschapv2_password_changed(struct eap_sm *sm,
+					  struct eap_mschapv2_data *data)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	if (config && config->new_password) {
+		wpa_msg(sm->msg_ctx, MSG_INFO,
+			WPA_EVENT_PASSWORD_CHANGED
+			"EAP-MSCHAPV2: Password changed successfully");
+		data->prev_error = 0;
+		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)) {
+				bin_clear_free(config->password,
+					       config->password_len);
+				config->password = NULL;
+				config->password_len = 0;
+			}
+			bin_clear_free(config->new_password,
+				       config->new_password_len);
+		} else {
+			config->password = config->new_password;
+			config->password_len = config->new_password_len;
+		}
+		config->new_password = NULL;
+		config->new_password_len = 0;
+	}
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in th erequest
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm,
+					    struct eap_mschapv2_data *data,
+					    struct eap_method_ret *ret,
+					    const struct eap_mschapv2_hdr *req,
+					    size_t req_len, u8 id)
+{
+	struct wpabuf *resp;
+	const u8 *pos;
+	size_t len;
+
+	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
+	len = req_len - sizeof(*req);
+	pos = (const u8 *) (req + 1);
+	if (!data->auth_response_valid ||
+	    mschapv2_verify_auth_response(data->auth_response, pos, len)) {
+		wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
+			   "response in success request");
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		return NULL;
+	}
+	pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
+	len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
+	while (len > 0 && *pos == ' ') {
+		pos++;
+		len--;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
+			  pos, len);
+	wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
+
+	/* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
+	 * message. */
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
+			     EAP_CODE_RESPONSE, id);
+	if (resp == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
+			   "buffer for success response");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
+
+	ret->methodState = METHOD_DONE;
+	ret->decision = DECISION_UNCOND_SUCC;
+	ret->allowNotifications = FALSE;
+	data->success = 1;
+
+	if (data->prev_error == ERROR_PASSWD_EXPIRED)
+		eap_mschapv2_password_changed(sm, data);
+
+	return resp;
+}
+
+
+static int eap_mschapv2_failure_txt(struct eap_sm *sm,
+				    struct eap_mschapv2_data *data, char *txt)
+{
+	char *pos, *msg = "";
+	int retry = 1;
+	struct eap_peer_config *config = eap_get_config(sm);
+
+	/* For example:
+	 * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
+	 */
+
+	pos = txt;
+
+	if (pos && os_strncmp(pos, "E=", 2) == 0) {
+		pos += 2;
+		data->prev_error = atoi(pos);
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
+			   data->prev_error);
+		pos = os_strchr(pos, ' ');
+		if (pos)
+			pos++;
+	}
+
+	if (pos && os_strncmp(pos, "R=", 2) == 0) {
+		pos += 2;
+		retry = atoi(pos);
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
+			   retry == 1 ? "" : "not ");
+		pos = os_strchr(pos, ' ');
+		if (pos)
+			pos++;
+	}
+
+	if (pos && os_strncmp(pos, "C=", 2) == 0) {
+		int hex_len;
+		pos += 2;
+		hex_len = os_strchr(pos, ' ') - (char *) pos;
+		if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
+			if (hexstr2bin(pos, data->passwd_change_challenge,
+				       PASSWD_CHANGE_CHAL_LEN)) {
+				wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
+					   "failure challenge");
+			} else {
+				wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
+					    "challenge",
+					    data->passwd_change_challenge,
+					    PASSWD_CHANGE_CHAL_LEN);
+				data->passwd_change_challenge_valid = 1;
+			}
+		} else {
+			wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
+				   "challenge len %d", hex_len);
+		}
+		pos = os_strchr(pos, ' ');
+		if (pos)
+			pos++;
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
+			   "was not present in failure message");
+	}
+
+	if (pos && os_strncmp(pos, "V=", 2) == 0) {
+		pos += 2;
+		data->passwd_change_version = atoi(pos);
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
+			   "protocol version %d", data->passwd_change_version);
+		pos = os_strchr(pos, ' ');
+		if (pos)
+			pos++;
+	}
+
+	if (pos && os_strncmp(pos, "M=", 2) == 0) {
+		pos += 2;
+		msg = pos;
+	}
+	if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry &&
+	    config && config->phase2 &&
+	    os_strstr(config->phase2, "mschapv2_retry=0")) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-MSCHAPV2: mark password retry disabled based on local configuration");
+		retry = 0;
+	}
+	wpa_msg(sm->msg_ctx, MSG_WARNING,
+		"EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
+		"%d)",
+		msg, retry == 1 ? "" : "not ", data->prev_error);
+	if (data->prev_error == ERROR_PASSWD_EXPIRED &&
+	    data->passwd_change_version == 3 && config) {
+		if (config->new_password == NULL) {
+			wpa_msg(sm->msg_ctx, MSG_INFO,
+				"EAP-MSCHAPV2: Password expired - password "
+				"change required");
+			eap_sm_request_new_password(sm);
+		}
+	} else if (retry == 1 && config) {
+		/* TODO: could prevent the current password from being used
+		 * again at least for some period of time */
+		if (!config->mschapv2_retry)
+			eap_sm_request_identity(sm);
+		eap_sm_request_password(sm);
+		config->mschapv2_retry = 1;
+	} else if (config) {
+		/* TODO: prevent retries using same username/password */
+		config->mschapv2_retry = 0;
+	}
+
+	return retry == 1;
+}
+
+
+static struct wpabuf * eap_mschapv2_change_password(
+	struct eap_sm *sm, struct eap_mschapv2_data *data,
+	struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
+{
+#ifdef CONFIG_NO_RC4
+	wpa_printf(MSG_ERROR,
+		"EAP-MSCHAPV2: RC4 not support in the build - cannot change password");
+	return NULL;
+#else /* CONFIG_NO_RC4 */
+	struct wpabuf *resp;
+	int ms_len;
+	const u8 *username, *password, *new_password;
+	size_t username_len, password_len, new_password_len;
+	struct eap_mschapv2_hdr *ms;
+	struct ms_change_password *cp;
+	u8 password_hash[16], password_hash_hash[16];
+	int pwhash;
+
+	username = eap_get_config_identity(sm, &username_len);
+	password = eap_get_config_password2(sm, &password_len, &pwhash);
+	new_password = eap_get_config_new_password(sm, &new_password_len);
+	if (username == NULL || password == NULL || new_password == NULL)
+		return NULL;
+
+	username = mschapv2_remove_domain(username, &username_len);
+
+	ret->ignore = FALSE;
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_COND_SUCC;
+	ret->allowNotifications = TRUE;
+
+	ms_len = sizeof(*ms) + sizeof(*cp);
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+			     EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+
+	ms = wpabuf_put(resp, sizeof(*ms));
+	ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
+	ms->mschapv2_id = req->mschapv2_id + 1;
+	WPA_PUT_BE16(ms->ms_length, ms_len);
+	cp = wpabuf_put(resp, sizeof(*cp));
+
+	/* Encrypted-Password */
+	if (pwhash) {
+		if (encrypt_pw_block_with_password_hash(
+			    new_password, new_password_len,
+			    password, cp->encr_password))
+			goto fail;
+	} else {
+		if (new_password_encrypted_with_old_nt_password_hash(
+			    new_password, new_password_len,
+			    password, password_len, cp->encr_password))
+			goto fail;
+	}
+
+	/* Encrypted-Hash */
+	if (pwhash) {
+		u8 new_password_hash[16];
+		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 {
+		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 */
+	if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
+		goto fail;
+
+	/* Reserved, must be zero */
+	os_memset(cp->reserved, 0, 8);
+
+	/* NT-Response */
+	wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
+		    data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
+		    cp->peer_challenge, MSCHAPV2_CHAL_LEN);
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
+			  username, username_len);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
+			      new_password, new_password_len);
+	generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
+			     username, username_len,
+			     new_password, new_password_len,
+			     cp->nt_response);
+	wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response",
+		    cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN);
+
+	/* Authenticator response is not really needed yet, but calculate it
+	 * here so that challenges need not be saved. */
+	generate_authenticator_response(new_password, new_password_len,
+					cp->peer_challenge,
+					data->passwd_change_challenge,
+					username, username_len,
+					cp->nt_response, data->auth_response);
+	data->auth_response_valid = 1;
+
+	/* Likewise, generate master_key here since we have the needed data
+	 * available. */
+	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 */
+	os_memset(cp->flags, 0, 2);
+
+	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
+		   "(change pw)", id, ms->mschapv2_id);
+
+	return resp;
+
+fail:
+	wpabuf_free(resp);
+	return NULL;
+#endif /* CONFIG_NO_RC4 */
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in th erequest
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm,
+					    struct eap_mschapv2_data *data,
+					    struct eap_method_ret *ret,
+					    const struct eap_mschapv2_hdr *req,
+					    size_t req_len, u8 id)
+{
+	struct wpabuf *resp;
+	const u8 *msdata = (const u8 *) (req + 1);
+	char *buf;
+	size_t len = req_len - sizeof(*req);
+	int retry = 0;
+
+	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
+			  msdata, len);
+	/*
+	 * eap_mschapv2_failure_txt() expects a nul terminated string, so we
+	 * must allocate a large enough temporary buffer to create that since
+	 * the received message does not include nul termination.
+	 */
+	buf = dup_binstr(msdata, len);
+	if (buf) {
+		retry = eap_mschapv2_failure_txt(sm, data, buf);
+		os_free(buf);
+	}
+
+	ret->ignore = FALSE;
+	ret->methodState = METHOD_DONE;
+	ret->decision = DECISION_FAIL;
+	ret->allowNotifications = FALSE;
+
+	if (data->prev_error == ERROR_PASSWD_EXPIRED &&
+	    data->passwd_change_version == 3) {
+		struct eap_peer_config *config = eap_get_config(sm);
+		if (config && config->new_password)
+			return eap_mschapv2_change_password(sm, data, ret, req,
+							    id);
+		if (config && config->pending_req_new_password)
+			return NULL;
+	} else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
+		/* TODO: could try to retry authentication, e.g, after having
+		 * changed the username/password. In this case, EAP MS-CHAP-v2
+		 * Failure Response would not be sent here. */
+		return NULL;
+	}
+
+	/* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
+	 * message. */
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
+			     EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+
+	wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
+
+	return resp;
+}
+
+
+static int eap_mschapv2_check_config(struct eap_sm *sm)
+{
+	size_t len;
+
+	if (eap_get_config_identity(sm, &len) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
+		eap_sm_request_identity(sm);
+		return -1;
+	}
+
+	if (eap_get_config_password(sm, &len) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
+		eap_sm_request_password(sm);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
+				    const struct eap_mschapv2_hdr *ms)
+{
+	size_t ms_len = WPA_GET_BE16(ms->ms_length);
+
+	if (ms_len == len)
+		return 0;
+
+	wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
+		   "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
+	if (sm->workaround) {
+		/* Some authentication servers use invalid ms_len,
+		 * ignore it for interoperability. */
+		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
+			   " invalid ms_len %lu (len %lu)",
+			   (unsigned long) ms_len,
+			   (unsigned long) len);
+		return 0;
+	}
+
+	return -1;
+}
+
+
+static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
+					const struct wpabuf *reqData)
+{
+	/*
+	 * Store a copy of the challenge message, so that it can be processed
+	 * again in case retry is allowed after a possible failure.
+	 */
+	wpabuf_free(data->prev_challenge);
+	data->prev_challenge = wpabuf_dup(reqData);
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 request
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
+					    struct eap_method_ret *ret,
+					    const struct wpabuf *reqData)
+{
+	struct eap_mschapv2_data *data = priv;
+	struct eap_peer_config *config = eap_get_config(sm);
+	const struct eap_mschapv2_hdr *ms;
+	int using_prev_challenge = 0;
+	const u8 *pos;
+	size_t len;
+	u8 id;
+
+	if (eap_mschapv2_check_config(sm)) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (config->mschapv2_retry && data->prev_challenge &&
+	    data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
+			   "with the previous challenge");
+
+		reqData = data->prev_challenge;
+		using_prev_challenge = 1;
+		config->mschapv2_retry = 0;
+	}
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
+			       &len);
+	if (pos == NULL || len < sizeof(*ms) + 1) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	ms = (const struct eap_mschapv2_hdr *) pos;
+	if (eap_mschapv2_check_mslen(sm, len, ms)) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	id = eap_get_id(reqData);
+	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
+		   id, ms->mschapv2_id);
+
+	switch (ms->op_code) {
+	case MSCHAPV2_OP_CHALLENGE:
+		if (!using_prev_challenge)
+			eap_mschapv2_copy_challenge(data, reqData);
+		return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
+	case MSCHAPV2_OP_SUCCESS:
+		return eap_mschapv2_success(sm, data, ret, ms, len, id);
+	case MSCHAPV2_OP_FAILURE:
+		return eap_mschapv2_failure(sm, data, ret, ms, len, id);
+	default:
+		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
+			   ms->op_code);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+}
+
+
+static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_mschapv2_data *data = priv;
+	return data->success && data->master_key_valid;
+}
+
+
+static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_mschapv2_data *data = priv;
+	u8 *key;
+	int key_len;
+
+	if (!data->master_key_valid || !data->success)
+		return NULL;
+
+	key_len = 2 * MSCHAPV2_KEY_LEN;
+
+	key = os_malloc(key_len);
+	if (key == NULL)
+		return NULL;
+
+	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
+	 *	peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
+	get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0);
+	get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
+				MSCHAPV2_KEY_LEN, 0, 0);
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
+			key, key_len);
+
+	*len = key_len;
+	return key;
+}
+
+
+/**
+ * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to register EAP-MSCHAPv2 peer method into the EAP
+ * method list.
+ */
+int eap_peer_mschapv2_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
+				    "MSCHAPV2");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_mschapv2_init;
+	eap->deinit = eap_mschapv2_deinit;
+	eap->process = eap_mschapv2_process;
+	eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
+	eap->getKey = eap_mschapv2_getKey;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_otp.c b/hostap/src/eap_peer/eap_otp.c
new file mode 100644
index 0000000..9ac744a
--- /dev/null
+++ b/hostap/src/eap_peer/eap_otp.c
@@ -0,0 +1,101 @@
+/*
+ * EAP peer method: EAP-OTP (RFC 3748)
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+static void * eap_otp_init(struct eap_sm *sm)
+{
+	/* No need for private data. However, must return non-NULL to indicate
+	 * success. */
+	return (void *) 1;
+}
+
+
+static void eap_otp_deinit(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static struct wpabuf * eap_otp_process(struct eap_sm *sm, void *priv,
+				       struct eap_method_ret *ret,
+				       const struct wpabuf *reqData)
+{
+	struct wpabuf *resp;
+	const u8 *pos, *password;
+	size_t password_len, len;
+	int otp;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_OTP, reqData, &len);
+	if (pos == NULL) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-OTP: Request message",
+			  pos, len);
+
+	password = eap_get_config_otp(sm, &password_len);
+	if (password)
+		otp = 1;
+	else {
+		password = eap_get_config_password(sm, &password_len);
+		otp = 0;
+	}
+
+	if (password == NULL) {
+		wpa_printf(MSG_INFO, "EAP-OTP: Password not configured");
+		eap_sm_request_otp(sm, (const char *) pos, len);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	ret->ignore = FALSE;
+
+	ret->methodState = METHOD_DONE;
+	ret->decision = DECISION_COND_SUCC;
+	ret->allowNotifications = FALSE;
+
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_OTP, password_len,
+			     EAP_CODE_RESPONSE, eap_get_id(reqData));
+	if (resp == NULL)
+		return NULL;
+	wpabuf_put_data(resp, password, password_len);
+	wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-OTP: Response",
+			      password, password_len);
+
+	if (otp) {
+		wpa_printf(MSG_DEBUG, "EAP-OTP: Forgetting used password");
+		eap_clear_config_otp(sm);
+	}
+
+	return resp;
+}
+
+
+int eap_peer_otp_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_OTP, "OTP");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_otp_init;
+	eap->deinit = eap_otp_deinit;
+	eap->process = eap_otp_process;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_pax.c b/hostap/src/eap_peer/eap_pax.c
new file mode 100644
index 0000000..c920bcd
--- /dev/null
+++ b/hostap/src/eap_peer/eap_pax.c
@@ -0,0 +1,547 @@
+/*
+ * EAP peer method: EAP-PAX (RFC 4746)
+ * Copyright (c) 2005-2008, 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/random.h"
+#include "eap_common/eap_pax_common.h"
+#include "eap_i.h"
+
+/*
+ * Note: only PAX_STD subprotocol is currently supported
+ *
+ * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
+ * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
+ * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
+ * RSAES-OAEP).
+ */
+
+struct eap_pax_data {
+	enum { PAX_INIT, PAX_STD_2_SENT, PAX_DONE } state;
+	u8 mac_id, dh_group_id, public_key_id;
+	union {
+		u8 e[2 * EAP_PAX_RAND_LEN];
+		struct {
+			u8 x[EAP_PAX_RAND_LEN]; /* server rand */
+			u8 y[EAP_PAX_RAND_LEN]; /* client rand */
+		} r;
+	} rand;
+	char *cid;
+	size_t cid_len;
+	u8 ak[EAP_PAX_AK_LEN];
+	u8 mk[EAP_PAX_MK_LEN];
+	u8 ck[EAP_PAX_CK_LEN];
+	u8 ick[EAP_PAX_ICK_LEN];
+	u8 mid[EAP_PAX_MID_LEN];
+};
+
+
+static void eap_pax_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_pax_init(struct eap_sm *sm)
+{
+	struct eap_pax_data *data;
+	const u8 *identity, *password;
+	size_t identity_len, password_len;
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	password = eap_get_config_password(sm, &password_len);
+	if (!identity || !password) {
+		wpa_printf(MSG_INFO, "EAP-PAX: CID (nai) or key (password) "
+			   "not configured");
+		return NULL;
+	}
+
+	if (password_len != EAP_PAX_AK_LEN) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Invalid PSK length");
+		return NULL;
+	}
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = PAX_INIT;
+
+	data->cid = os_malloc(identity_len);
+	if (data->cid == NULL) {
+		eap_pax_deinit(sm, data);
+		return NULL;
+	}
+	os_memcpy(data->cid, identity, identity_len);
+	data->cid_len = identity_len;
+
+	os_memcpy(data->ak, password, EAP_PAX_AK_LEN);
+
+	return data;
+}
+
+
+static void eap_pax_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_pax_data *data = priv;
+	os_free(data->cid);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_pax_alloc_resp(const struct eap_pax_hdr *req,
+					  u8 id, u8 op_code, size_t plen)
+{
+	struct wpabuf *resp;
+	struct eap_pax_hdr *pax;
+
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
+			     sizeof(*pax) + plen, EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+
+	pax = wpabuf_put(resp, sizeof(*pax));
+	pax->op_code = op_code;
+	pax->flags = 0;
+	pax->mac_id = req->mac_id;
+	pax->dh_group_id = req->dh_group_id;
+	pax->public_key_id = req->public_key_id;
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data,
+					     struct eap_method_ret *ret, u8 id,
+					     const struct eap_pax_hdr *req,
+					     size_t req_plen)
+{
+	struct wpabuf *resp;
+	const u8 *pos;
+	u8 *rpos;
+	size_t left, plen;
+
+	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (received)");
+
+	if (data->state != PAX_INIT) {
+		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 received in "
+			   "unexpected state (%d) - ignored", data->state);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (req->flags & EAP_PAX_FLAGS_CE) {
+		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with CE flag set - "
+			   "ignored");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	left = req_plen - sizeof(*req);
+
+	if (left < 2 + EAP_PAX_RAND_LEN) {
+		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with too short "
+			   "payload");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	pos = (const u8 *) (req + 1);
+	if (WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
+		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with incorrect A "
+			   "length %d (expected %d)",
+			   WPA_GET_BE16(pos), EAP_PAX_RAND_LEN);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	pos += 2;
+	left -= 2;
+	os_memcpy(data->rand.r.x, pos, EAP_PAX_RAND_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: X (server rand)",
+		    data->rand.r.x, EAP_PAX_RAND_LEN);
+	pos += EAP_PAX_RAND_LEN;
+	left -= EAP_PAX_RAND_LEN;
+
+	if (left > 0) {
+		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
+			    pos, left);
+	}
+
+	if (random_get_bytes(data->rand.r.y, EAP_PAX_RAND_LEN)) {
+		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
+		    data->rand.r.y, EAP_PAX_RAND_LEN);
+
+	if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e,
+					   data->mk, data->ck, data->ick,
+					   data->mid) < 0) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-2 (sending)");
+
+	plen = 2 + EAP_PAX_RAND_LEN + 2 + data->cid_len + 2 + EAP_PAX_MAC_LEN +
+		EAP_PAX_ICV_LEN;
+	resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_STD_2, plen);
+	if (resp == NULL)
+		return NULL;
+
+	wpabuf_put_be16(resp, EAP_PAX_RAND_LEN);
+	wpabuf_put_data(resp, data->rand.r.y, EAP_PAX_RAND_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: B = Y (client rand)",
+		    data->rand.r.y, EAP_PAX_RAND_LEN);
+
+	wpabuf_put_be16(resp, data->cid_len);
+	wpabuf_put_data(resp, data->cid, data->cid_len);
+	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
+			  (u8 *) data->cid, data->cid_len);
+
+	wpabuf_put_be16(resp, EAP_PAX_MAC_LEN);
+	rpos = wpabuf_put(resp, EAP_PAX_MAC_LEN);
+	eap_pax_mac(req->mac_id, data->ck, EAP_PAX_CK_LEN,
+		    data->rand.r.x, EAP_PAX_RAND_LEN,
+		    data->rand.r.y, EAP_PAX_RAND_LEN,
+		    (u8 *) data->cid, data->cid_len, rpos);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
+		    rpos, EAP_PAX_MAC_LEN);
+
+	/* Optional ADE could be added here, if needed */
+
+	rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN);
+	eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN,
+		    wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN,
+		    NULL, 0, NULL, 0, rpos);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
+
+	data->state = PAX_STD_2_SENT;
+	data->mac_id = req->mac_id;
+	data->dh_group_id = req->dh_group_id;
+	data->public_key_id = req->public_key_id;
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data,
+					     struct eap_method_ret *ret, u8 id,
+					     const struct eap_pax_hdr *req,
+					     size_t req_plen)
+{
+	struct wpabuf *resp;
+	u8 *rpos, mac[EAP_PAX_MAC_LEN];
+	const u8 *pos;
+	size_t left;
+
+	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (received)");
+
+	if (data->state != PAX_STD_2_SENT) {
+		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 received in "
+			   "unexpected state (%d) - ignored", data->state);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (req->flags & EAP_PAX_FLAGS_CE) {
+		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with CE flag set - "
+			   "ignored");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	left = req_plen - sizeof(*req);
+
+	if (left < 2 + EAP_PAX_MAC_LEN) {
+		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with too short "
+			   "payload");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	pos = (const u8 *) (req + 1);
+	if (WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
+		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with incorrect "
+			   "MAC_CK length %d (expected %d)",
+			   WPA_GET_BE16(pos), EAP_PAX_MAC_LEN);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	pos += 2;
+	left -= 2;
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
+		    pos, EAP_PAX_MAC_LEN);
+	eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
+		    data->rand.r.y, EAP_PAX_RAND_LEN,
+		    (u8 *) data->cid, data->cid_len, NULL, 0, mac);
+	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)",
+			    mac, EAP_PAX_MAC_LEN);
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		return NULL;
+	}
+
+	pos += EAP_PAX_MAC_LEN;
+	left -= EAP_PAX_MAC_LEN;
+
+	if (left > 0) {
+		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
+			    pos, left);
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX-ACK (sending)");
+
+	resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_ACK, EAP_PAX_ICV_LEN);
+	if (resp == NULL)
+		return NULL;
+
+	/* Optional ADE could be added here, if needed */
+
+	rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN);
+	eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+		    wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN,
+		    NULL, 0, NULL, 0, rpos);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
+
+	data->state = PAX_DONE;
+	ret->methodState = METHOD_DONE;
+	ret->decision = DECISION_UNCOND_SUCC;
+	ret->allowNotifications = FALSE;
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv,
+				       struct eap_method_ret *ret,
+				       const struct wpabuf *reqData)
+{
+	struct eap_pax_data *data = priv;
+	const struct eap_pax_hdr *req;
+	struct wpabuf *resp;
+	u8 icvbuf[EAP_PAX_ICV_LEN], id;
+	const u8 *icv, *pos;
+	size_t len;
+	u16 flen, mlen;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len);
+	if (pos == NULL || len < sizeof(*req) + EAP_PAX_ICV_LEN) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	id = eap_get_id(reqData);
+	req = (const struct eap_pax_hdr *) pos;
+	flen = len - EAP_PAX_ICV_LEN;
+	mlen = wpabuf_len(reqData) - EAP_PAX_ICV_LEN;
+
+	wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
+		   "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
+		   "public_key_id 0x%x",
+		   req->op_code, req->flags, req->mac_id, req->dh_group_id,
+		   req->public_key_id);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
+		    pos, len - EAP_PAX_ICV_LEN);
+
+	if (data->state != PAX_INIT && data->mac_id != req->mac_id) {
+		wpa_printf(MSG_INFO, "EAP-PAX: MAC ID changed during "
+			   "authentication (was 0x%d, is 0x%d)",
+			   data->mac_id, req->mac_id);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (data->state != PAX_INIT && data->dh_group_id != req->dh_group_id) {
+		wpa_printf(MSG_INFO, "EAP-PAX: DH Group ID changed during "
+			   "authentication (was 0x%d, is 0x%d)",
+			   data->dh_group_id, req->dh_group_id);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (data->state != PAX_INIT &&
+	    data->public_key_id != req->public_key_id) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Public Key ID changed during "
+			   "authentication (was 0x%d, is 0x%d)",
+			   data->public_key_id, req->public_key_id);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	/* TODO: add support EAP_PAX_HMAC_SHA256_128 */
+	if (req->mac_id != EAP_PAX_MAC_HMAC_SHA1_128) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Unsupported MAC ID 0x%x",
+			   req->mac_id);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (req->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Unsupported DH Group ID 0x%x",
+			   req->dh_group_id);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (req->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Unsupported Public Key ID 0x%x",
+			   req->public_key_id);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (req->flags & EAP_PAX_FLAGS_MF) {
+		/* TODO: add support for reassembling fragments */
+		wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported - "
+			   "ignored packet");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	icv = pos + len - EAP_PAX_ICV_LEN;
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
+	if (req->op_code == EAP_PAX_OP_STD_1) {
+		eap_pax_mac(req->mac_id, (u8 *) "", 0,
+			    wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
+			    icvbuf);
+	} else {
+		eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN,
+			    wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
+			    icvbuf);
+	}
+	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",
+			    icvbuf, EAP_PAX_ICV_LEN);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	ret->ignore = FALSE;
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_FAIL;
+	ret->allowNotifications = TRUE;
+
+	switch (req->op_code) {
+	case EAP_PAX_OP_STD_1:
+		resp = eap_pax_process_std_1(data, ret, id, req, flen);
+		break;
+	case EAP_PAX_OP_STD_3:
+		resp = eap_pax_process_std_3(data, ret, id, req, flen);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-PAX: ignoring message with unknown "
+			   "op_code %d", req->op_code);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (ret->methodState == METHOD_DONE) {
+		ret->allowNotifications = FALSE;
+	}
+
+	return resp;
+}
+
+
+static Boolean eap_pax_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_pax_data *data = priv;
+	return data->state == PAX_DONE;
+}
+
+
+static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_pax_data *data = priv;
+	u8 *key;
+
+	if (data->state != PAX_DONE)
+		return NULL;
+
+	key = os_malloc(EAP_MSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_MSK_LEN;
+	eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
+		    "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
+		    EAP_MSK_LEN, key);
+
+	return key;
+}
+
+
+static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_pax_data *data = priv;
+	u8 *key;
+
+	if (data->state != PAX_DONE)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_EMSK_LEN;
+	eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
+		    "Extended Master Session Key",
+		    data->rand.e, 2 * EAP_PAX_RAND_LEN,
+		    EAP_EMSK_LEN, key);
+
+	return key;
+}
+
+
+static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_pax_data *data = priv;
+	u8 *sid;
+
+	if (data->state != PAX_DONE)
+		return NULL;
+
+	sid = os_malloc(1 + EAP_PAX_MID_LEN);
+	if (sid == NULL)
+		return NULL;
+
+	*len = 1 + EAP_PAX_MID_LEN;
+	sid[0] = EAP_TYPE_PAX;
+	os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN);
+
+	return sid;
+}
+
+
+int eap_peer_pax_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_pax_init;
+	eap->deinit = eap_pax_deinit;
+	eap->process = eap_pax_process;
+	eap->isKeyAvailable = eap_pax_isKeyAvailable;
+	eap->getKey = eap_pax_getKey;
+	eap->get_emsk = eap_pax_get_emsk;
+	eap->getSessionId = eap_pax_get_session_id;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_peap.c b/hostap/src/eap_peer/eap_peap.c
new file mode 100644
index 0000000..98a48a6
--- /dev/null
+++ b/hostap/src/eap_peer/eap_peap.c
@@ -0,0 +1,1262 @@
+/*
+ * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_common/eap_peap_common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+#include "tncc.h"
+
+
+/* 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
+ */
+#define EAP_PEAP_VERSION 1
+
+
+static void eap_peap_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_peap_data {
+	struct eap_ssl_data ssl;
+
+	int peap_version, force_peap_version, force_new_label;
+
+	const struct eap_method *phase2_method;
+	void *phase2_priv;
+	int phase2_success;
+	int phase2_eap_success;
+	int phase2_eap_started;
+
+	struct eap_method_type phase2_type;
+	struct eap_method_type *phase2_types;
+	size_t num_phase2_types;
+
+	int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner
+				 * EAP-Success
+				 * 1 = reply with tunneled EAP-Success to inner
+				 * EAP-Success and expect AS to send outer
+				 * (unencrypted) EAP-Success after this
+				 * 2 = reply with PEAP/TLS ACK to inner
+				 * EAP-Success and expect AS to send outer
+				 * (unencrypted) EAP-Success after this */
+	int resuming; /* starting a resumed session */
+	int reauth; /* reauthentication */
+	u8 *key_data;
+	u8 *session_id;
+	size_t id_len;
+
+	struct wpabuf *pending_phase2_req;
+	enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
+	int crypto_binding_used;
+	u8 binding_nonce[32];
+	u8 ipmk[40];
+	u8 cmk[20];
+	int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP)
+		  * is enabled. */
+};
+
+
+static int eap_peap_parse_phase1(struct eap_peap_data *data,
+				 const char *phase1)
+{
+	const char *pos;
+
+	pos = os_strstr(phase1, "peapver=");
+	if (pos) {
+		data->force_peap_version = atoi(pos + 8);
+		data->peap_version = data->force_peap_version;
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d",
+			   data->force_peap_version);
+	}
+
+	if (os_strstr(phase1, "peaplabel=1")) {
+		data->force_new_label = 1;
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key "
+			   "derivation");
+	}
+
+	if (os_strstr(phase1, "peap_outer_success=0")) {
+		data->peap_outer_success = 0;
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on "
+			   "tunneled EAP-Success");
+	} else if (os_strstr(phase1, "peap_outer_success=1")) {
+		data->peap_outer_success = 1;
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success "
+			   "after receiving tunneled EAP-Success");
+	} else if (os_strstr(phase1, "peap_outer_success=2")) {
+		data->peap_outer_success = 2;
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after "
+			   "receiving tunneled EAP-Success");
+	}
+
+	if (os_strstr(phase1, "crypto_binding=0")) {
+		data->crypto_binding = NO_BINDING;
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding");
+	} else if (os_strstr(phase1, "crypto_binding=1")) {
+		data->crypto_binding = OPTIONAL_BINDING;
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding");
+	} else if (os_strstr(phase1, "crypto_binding=2")) {
+		data->crypto_binding = REQUIRE_BINDING;
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding");
+	}
+
+#ifdef EAP_TNC
+	if (os_strstr(phase1, "tnc=soh2")) {
+		data->soh = 2;
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
+	} else if (os_strstr(phase1, "tnc=soh1")) {
+		data->soh = 1;
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled");
+	} else if (os_strstr(phase1, "tnc=soh")) {
+		data->soh = 2;
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
+	}
+#endif /* EAP_TNC */
+
+	return 0;
+}
+
+
+static void * eap_peap_init(struct eap_sm *sm)
+{
+	struct eap_peap_data *data;
+	struct eap_peer_config *config = eap_get_config(sm);
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	sm->peap_done = FALSE;
+	data->peap_version = EAP_PEAP_VERSION;
+	data->force_peap_version = -1;
+	data->peap_outer_success = 2;
+	data->crypto_binding = OPTIONAL_BINDING;
+
+	if (config && config->phase1 &&
+	    eap_peap_parse_phase1(data, config->phase1) < 0) {
+		eap_peap_deinit(sm, data);
+		return NULL;
+	}
+
+	if (eap_peer_select_phase2_methods(config, "auth=",
+					   &data->phase2_types,
+					   &data->num_phase2_types) < 0) {
+		eap_peap_deinit(sm, data);
+		return NULL;
+	}
+
+	data->phase2_type.vendor = EAP_VENDOR_IETF;
+	data->phase2_type.method = EAP_TYPE_NONE;
+
+	if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) {
+		wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
+		eap_peap_deinit(sm, data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+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;
+	if (data == NULL)
+		return;
+	if (data->phase2_priv && data->phase2_method)
+		data->phase2_method->deinit(sm, data->phase2_priv);
+	os_free(data->phase2_types);
+	eap_peer_tls_ssl_deinit(sm, &data->ssl);
+	eap_peap_free_key(data);
+	os_free(data->session_id);
+	wpabuf_free(data->pending_phase2_req);
+	os_free(data);
+}
+
+
+/**
+ * eap_tlv_build_nak - Build EAP-TLV NAK message
+ * @id: EAP identifier for the header
+ * @nak_type: TLV type (EAP_TLV_*)
+ * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure
+ *
+ * This function builds an EAP-TLV NAK message. The caller is responsible for
+ * freeing the returned buffer.
+ */
+static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type)
+{
+	struct wpabuf *msg;
+
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10,
+			    EAP_CODE_RESPONSE, id);
+	if (msg == NULL)
+		return NULL;
+
+	wpabuf_put_u8(msg, 0x80); /* Mandatory */
+	wpabuf_put_u8(msg, EAP_TLV_NAK_TLV);
+	wpabuf_put_be16(msg, 6); /* Length */
+	wpabuf_put_be32(msg, 0); /* Vendor-Id */
+	wpabuf_put_be16(msg, nak_type); /* NAK-Type */
+
+	return msg;
+}
+
+
+static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data,
+			    u8 *isk, size_t isk_len)
+{
+	u8 *key;
+	size_t key_len;
+
+	os_memset(isk, 0, isk_len);
+	if (data->phase2_method == NULL || data->phase2_priv == NULL ||
+	    data->phase2_method->isKeyAvailable == NULL ||
+	    data->phase2_method->getKey == NULL)
+		return 0;
+
+	if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||
+	    (key = data->phase2_method->getKey(sm, data->phase2_priv,
+					       &key_len)) == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material "
+			   "from Phase 2");
+		return -1;
+	}
+
+	if (key_len > isk_len)
+		key_len = isk_len;
+	os_memcpy(isk, key, key_len);
+	os_free(key);
+
+	return 0;
+}
+
+
+static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
+{
+	u8 *tk;
+	u8 isk[32], imck[60];
+
+	/*
+	 * Tunnel key (TK) is the first 60 octets of the key generated by
+	 * phase 1 of PEAP (based on TLS).
+	 */
+	tk = data->key_data;
+	if (tk == NULL)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
+
+	if (data->reauth &&
+	    tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
+		/* Fast-connect: IPMK|CMK = TK */
+		os_memcpy(data->ipmk, tk, 40);
+		wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK",
+				data->ipmk, 40);
+		os_memcpy(data->cmk, tk + 40, 20);
+		wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK",
+				data->cmk, 20);
+		return 0;
+	}
+
+	if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));
+
+	/*
+	 * IPMK Seed = "Inner Methods Compound Keys" | ISK
+	 * TempKey = First 40 octets of TK
+	 * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60)
+	 * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space
+	 * in the end of the label just before ISK; is that just a typo?)
+	 */
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
+	if (peap_prfplus(data->peap_version, tk, 40,
+			 "Inner Methods Compound Keys",
+			 isk, sizeof(isk), imck, sizeof(imck)) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
+			imck, sizeof(imck));
+
+	os_memcpy(data->ipmk, imck, 40);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
+	os_memcpy(data->cmk, imck + 40, 20);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
+
+	return 0;
+}
+
+
+static int eap_tlv_add_cryptobinding(struct eap_sm *sm,
+				     struct eap_peap_data *data,
+				     struct wpabuf *buf)
+{
+	u8 *mac;
+	u8 eap_type = EAP_TYPE_PEAP;
+	const u8 *addr[2];
+	size_t len[2];
+	u16 tlv_type;
+
+	/* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
+	addr[0] = wpabuf_put(buf, 0);
+	len[0] = 60;
+	addr[1] = &eap_type;
+	len[1] = 1;
+
+	tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
+	wpabuf_put_be16(buf, tlv_type);
+	wpabuf_put_be16(buf, 56);
+
+	wpabuf_put_u8(buf, 0); /* Reserved */
+	wpabuf_put_u8(buf, data->peap_version); /* Version */
+	wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */
+	wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */
+	wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */
+	mac = wpabuf_put(buf, 20); /* Compound_MAC */
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",
+		    addr[0], len[0]);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",
+		    addr[1], len[1]);
+	hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN);
+	data->crypto_binding_used = 1;
+
+	return 0;
+}
+
+
+/**
+ * eap_tlv_build_result - Build EAP-TLV Result message
+ * @id: EAP identifier for the header
+ * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE)
+ * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure
+ *
+ * This function builds an EAP-TLV Result message. The caller is responsible
+ * for freeing the returned buffer.
+ */
+static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm,
+					    struct eap_peap_data *data,
+					    int crypto_tlv_used,
+					    int id, u16 status)
+{
+	struct wpabuf *msg;
+	size_t len;
+
+	if (data->crypto_binding == NO_BINDING)
+		crypto_tlv_used = 0;
+
+	len = 6;
+	if (crypto_tlv_used)
+		len += 60; /* Cryptobinding TLV */
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len,
+			    EAP_CODE_RESPONSE, id);
+	if (msg == NULL)
+		return NULL;
+
+	wpabuf_put_u8(msg, 0x80); /* Mandatory */
+	wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV);
+	wpabuf_put_be16(msg, 2); /* Length */
+	wpabuf_put_be16(msg, status); /* Status */
+
+	if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
+					  struct eap_peap_data *data,
+					  const u8 *crypto_tlv,
+					  size_t crypto_tlv_len)
+{
+	u8 buf[61], mac[SHA1_MAC_LEN];
+	const u8 *pos;
+
+	if (eap_peap_derive_cmk(sm, data) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK");
+		return -1;
+	}
+
+	if (crypto_tlv_len != 4 + 56) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV "
+			   "length %d", (int) crypto_tlv_len);
+		return -1;
+	}
+
+	pos = crypto_tlv;
+	pos += 4; /* TLV header */
+	if (pos[1] != data->peap_version) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version "
+			   "mismatch (was %d; expected %d)",
+			   pos[1], data->peap_version);
+		return -1;
+	}
+
+	if (pos[3] != 0) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV "
+			   "SubType %d", pos[3]);
+		return -1;
+	}
+	pos += 4;
+	os_memcpy(data->binding_nonce, pos, 32);
+	pos += 32; /* Nonce */
+
+	/* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
+	os_memcpy(buf, crypto_tlv, 60);
+	os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */
+	buf[60] = EAP_TYPE_PEAP;
+	wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data",
+		    buf, sizeof(buf));
+	hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
+
+	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",
+			    pos, SHA1_MAC_LEN);
+		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC",
+			    mac, SHA1_MAC_LEN);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received");
+
+	return 0;
+}
+
+
+/**
+ * eap_tlv_process - Process a received EAP-TLV message and generate a response
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: EAP-TLV request to be processed. The caller must have validated that
+ * the buffer is large enough to contain full request (hdr->length bytes) and
+ * that the EAP type is EAP_TYPE_TLV.
+ * @resp: Buffer to return a pointer to the allocated response message. This
+ * field should be initialized to %NULL before the call. The value will be
+ * updated if a response message is generated. The caller is responsible for
+ * freeing the allocated message.
+ * @force_failure: Force negotiation to fail
+ * Returns: 0 on success, -1 on failure
+ */
+static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data,
+			   struct eap_method_ret *ret,
+			   const struct wpabuf *req, struct wpabuf **resp,
+			   int force_failure)
+{
+	size_t left, tlv_len;
+	const u8 *pos;
+	const u8 *result_tlv = NULL, *crypto_tlv = NULL;
+	size_t result_tlv_len = 0, crypto_tlv_len = 0;
+	int tlv_type, mandatory;
+
+	/* Parse TLVs */
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left);
+	if (pos == NULL)
+		return -1;
+	wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left);
+	while (left >= 4) {
+		mandatory = !!(pos[0] & 0x80);
+		tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+		pos += 2;
+		tlv_len = WPA_GET_BE16(pos);
+		pos += 2;
+		left -= 4;
+		if (tlv_len > left) {
+			wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun "
+				   "(tlv_len=%lu left=%lu)",
+				   (unsigned long) tlv_len,
+				   (unsigned long) left);
+			return -1;
+		}
+		switch (tlv_type) {
+		case EAP_TLV_RESULT_TLV:
+			result_tlv = pos;
+			result_tlv_len = tlv_len;
+			break;
+		case EAP_TLV_CRYPTO_BINDING_TLV:
+			crypto_tlv = pos;
+			crypto_tlv_len = tlv_len;
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "
+				   "%d%s", tlv_type,
+				   mandatory ? " (mandatory)" : "");
+			if (mandatory) {
+				/* NAK TLV and ignore all TLVs in this packet.
+				 */
+				*resp = eap_tlv_build_nak(eap_get_id(req),
+							  tlv_type);
+				return *resp == NULL ? -1 : 0;
+			}
+			/* Ignore this TLV, but process other TLVs */
+			break;
+		}
+
+		pos += tlv_len;
+		left -= tlv_len;
+	}
+	if (left) {
+		wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in "
+			   "Request (left=%lu)", (unsigned long) left);
+		return -1;
+	}
+
+	/* Process supported TLVs */
+	if (crypto_tlv && data->crypto_binding != NO_BINDING) {
+		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV",
+			    crypto_tlv, crypto_tlv_len);
+		if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4,
+						   crypto_tlv_len + 4) < 0) {
+			if (result_tlv == NULL)
+				return -1;
+			force_failure = 1;
+			crypto_tlv = NULL; /* do not include Cryptobinding TLV
+					    * in response, if the received
+					    * cryptobinding was invalid. */
+		}
+	} else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");
+		return -1;
+	}
+
+	if (result_tlv) {
+		int status, resp_status;
+		wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",
+			    result_tlv, result_tlv_len);
+		if (result_tlv_len < 2) {
+			wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV "
+				   "(len=%lu)",
+				   (unsigned long) result_tlv_len);
+			return -1;
+		}
+		status = WPA_GET_BE16(result_tlv);
+		if (status == EAP_TLV_RESULT_SUCCESS) {
+			wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "
+				   "- EAP-TLV/Phase2 Completed");
+			if (force_failure) {
+				wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure"
+					   " - force failed Phase 2");
+				resp_status = EAP_TLV_RESULT_FAILURE;
+				ret->decision = DECISION_FAIL;
+			} else {
+				resp_status = EAP_TLV_RESULT_SUCCESS;
+				ret->decision = DECISION_UNCOND_SUCC;
+			}
+		} else if (status == EAP_TLV_RESULT_FAILURE) {
+			wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure");
+			resp_status = EAP_TLV_RESULT_FAILURE;
+			ret->decision = DECISION_FAIL;
+		} else {
+			wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "
+				   "Status %d", status);
+			resp_status = EAP_TLV_RESULT_FAILURE;
+			ret->decision = DECISION_FAIL;
+		}
+		ret->methodState = METHOD_DONE;
+
+		*resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL,
+					     eap_get_id(req), resp_status);
+	}
+
+	return 0;
+}
+
+
+static int eap_peap_phase2_request(struct eap_sm *sm,
+				   struct eap_peap_data *data,
+				   struct eap_method_ret *ret,
+				   struct wpabuf *req,
+				   struct wpabuf **resp)
+{
+	struct eap_hdr *hdr = wpabuf_mhead(req);
+	size_t len = be_to_host16(hdr->length);
+	u8 *pos;
+	struct eap_method_ret iret;
+	struct eap_peer_config *config = eap_get_config(sm);
+
+	if (len <= sizeof(struct eap_hdr)) {
+		wpa_printf(MSG_INFO, "EAP-PEAP: too short "
+			   "Phase 2 request (len=%lu)", (unsigned long) len);
+		return -1;
+	}
+	pos = (u8 *) (hdr + 1);
+	wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos);
+	switch (*pos) {
+	case EAP_TYPE_IDENTITY:
+		*resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+		break;
+	case EAP_TYPE_TLV:
+		os_memset(&iret, 0, sizeof(iret));
+		if (eap_tlv_process(sm, data, &iret, req, resp,
+				    data->phase2_eap_started &&
+				    !data->phase2_eap_success)) {
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			return -1;
+		}
+		if (iret.methodState == METHOD_DONE ||
+		    iret.methodState == METHOD_MAY_CONT) {
+			ret->methodState = iret.methodState;
+			ret->decision = iret.decision;
+			data->phase2_success = 1;
+		}
+		break;
+	case EAP_TYPE_EXPANDED:
+#ifdef EAP_TNC
+		if (data->soh) {
+			const u8 *epos;
+			size_t eleft;
+
+			epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21,
+						req, &eleft);
+			if (epos) {
+				struct wpabuf *buf;
+				wpa_printf(MSG_DEBUG,
+					   "EAP-PEAP: SoH EAP Extensions");
+				buf = tncc_process_soh_request(data->soh,
+							       epos, eleft);
+				if (buf) {
+					*resp = eap_msg_alloc(
+						EAP_VENDOR_MICROSOFT, 0x21,
+						wpabuf_len(buf),
+						EAP_CODE_RESPONSE,
+						hdr->identifier);
+					if (*resp == NULL) {
+						ret->methodState = METHOD_DONE;
+						ret->decision = DECISION_FAIL;
+						return -1;
+					}
+					wpabuf_put_buf(*resp, buf);
+					wpabuf_free(buf);
+					break;
+				}
+			}
+		}
+#endif /* EAP_TNC */
+		/* fall through */
+	default:
+		if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
+		    data->phase2_type.method == EAP_TYPE_NONE) {
+			size_t i;
+			for (i = 0; i < data->num_phase2_types; i++) {
+				if (data->phase2_types[i].vendor !=
+				    EAP_VENDOR_IETF ||
+				    data->phase2_types[i].method != *pos)
+					continue;
+
+				data->phase2_type.vendor =
+					data->phase2_types[i].vendor;
+				data->phase2_type.method =
+					data->phase2_types[i].method;
+				wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected "
+					   "Phase 2 EAP vendor %d method %d",
+					   data->phase2_type.vendor,
+					   data->phase2_type.method);
+				break;
+			}
+		}
+		if (*pos != data->phase2_type.method ||
+		    *pos == EAP_TYPE_NONE) {
+			if (eap_peer_tls_phase2_nak(data->phase2_types,
+						    data->num_phase2_types,
+						    hdr, resp))
+				return -1;
+			return 0;
+		}
+
+		if (data->phase2_priv == NULL) {
+			data->phase2_method = eap_peer_get_eap_method(
+				data->phase2_type.vendor,
+				data->phase2_type.method);
+			if (data->phase2_method) {
+				sm->init_phase2 = 1;
+				data->phase2_priv =
+					data->phase2_method->init(sm);
+				sm->init_phase2 = 0;
+			}
+		}
+		if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+			wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize "
+				   "Phase 2 EAP method %d", *pos);
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			return -1;
+		}
+		data->phase2_eap_started = 1;
+		os_memset(&iret, 0, sizeof(iret));
+		*resp = data->phase2_method->process(sm, data->phase2_priv,
+						     &iret, req);
+		if ((iret.methodState == METHOD_DONE ||
+		     iret.methodState == METHOD_MAY_CONT) &&
+		    (iret.decision == DECISION_UNCOND_SUCC ||
+		     iret.decision == DECISION_COND_SUCC)) {
+			data->phase2_eap_success = 1;
+			data->phase2_success = 1;
+		}
+		break;
+	}
+
+	if (*resp == NULL &&
+	    (config->pending_req_identity || config->pending_req_password ||
+	     config->pending_req_otp || config->pending_req_new_password)) {
+		wpabuf_free(data->pending_phase2_req);
+		data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
+	}
+
+	return 0;
+}
+
+
+static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data,
+			    struct eap_method_ret *ret,
+			    const struct eap_hdr *req,
+			    const struct wpabuf *in_data,
+			    struct wpabuf **out_data)
+{
+	struct wpabuf *in_decrypted = NULL;
+	int res, skip_change = 0;
+	struct eap_hdr *hdr, *rhdr;
+	struct wpabuf *resp = NULL;
+	size_t len;
+
+	wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"
+		   " Phase 2", (unsigned long) wpabuf_len(in_data));
+
+	if (data->pending_phase2_req) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - "
+			   "skip decryption and use old data");
+		/* Clear TLS reassembly state. */
+		eap_peer_tls_reset_input(&data->ssl);
+		in_decrypted = data->pending_phase2_req;
+		data->pending_phase2_req = NULL;
+		skip_change = 1;
+		goto continue_req;
+	}
+
+	if (wpabuf_len(in_data) == 0 && sm->workaround &&
+	    data->phase2_success) {
+		/*
+		 * Cisco ACS seems to be using TLS ACK to terminate
+		 * EAP-PEAPv0/GTC. Try to reply with TLS ACK.
+		 */
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but "
+			   "expected data - acknowledge with TLS ACK since "
+			   "Phase 2 has been completed");
+		ret->decision = DECISION_COND_SUCC;
+		ret->methodState = METHOD_DONE;
+		return 1;
+	} else if (wpabuf_len(in_data) == 0) {
+		/* Received TLS ACK - requesting more fragments */
+		return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
+					    data->peap_version,
+					    req->identifier, NULL, out_data);
+	}
+
+	res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+	if (res)
+		return res;
+
+continue_req:
+	wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",
+			in_decrypted);
+
+	hdr = wpabuf_mhead(in_decrypted);
+	if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST &&
+	    be_to_host16(hdr->length) == 5 &&
+	    eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) {
+		/* At least FreeRADIUS seems to send full EAP header with
+		 * EAP Request Identity */
+		skip_change = 1;
+	}
+	if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST &&
+	    eap_get_type(in_decrypted) == EAP_TYPE_TLV) {
+		skip_change = 1;
+	}
+
+	if (data->peap_version == 0 && !skip_change) {
+		struct eap_hdr *nhdr;
+		struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) +
+						   wpabuf_len(in_decrypted));
+		if (nmsg == NULL) {
+			wpabuf_free(in_decrypted);
+			return 0;
+		}
+		nhdr = wpabuf_put(nmsg, sizeof(*nhdr));
+		wpabuf_put_buf(nmsg, in_decrypted);
+		nhdr->code = req->code;
+		nhdr->identifier = req->identifier;
+		nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
+					    wpabuf_len(in_decrypted));
+
+		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 "
+			   "EAP frame (len=%lu)",
+			   (unsigned long) wpabuf_len(in_decrypted));
+		wpabuf_free(in_decrypted);
+		return 0;
+	}
+	len = be_to_host16(hdr->length);
+	if (len > wpabuf_len(in_decrypted)) {
+		wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
+			   "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
+			   (unsigned long) wpabuf_len(in_decrypted),
+			   (unsigned long) len);
+		wpabuf_free(in_decrypted);
+		return 0;
+	}
+	if (len < wpabuf_len(in_decrypted)) {
+		wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has "
+			   "shorter length than full decrypted data "
+			   "(%lu < %lu)",
+			   (unsigned long) len,
+			   (unsigned long) wpabuf_len(in_decrypted));
+	}
+	wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
+		   "identifier=%d length=%lu", hdr->code, hdr->identifier,
+		   (unsigned long) len);
+	switch (hdr->code) {
+	case EAP_CODE_REQUEST:
+		if (eap_peap_phase2_request(sm, data, ret, in_decrypted,
+					    &resp)) {
+			wpabuf_free(in_decrypted);
+			wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request "
+				   "processing failed");
+			return 0;
+		}
+		break;
+	case EAP_CODE_SUCCESS:
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
+		if (data->peap_version == 1) {
+			/* EAP-Success within TLS tunnel is used to indicate
+			 * shutdown of the TLS channel. The authentication has
+			 * been completed. */
+			if (data->phase2_eap_started &&
+			    !data->phase2_eap_success) {
+				wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 "
+					   "Success used to indicate success, "
+					   "but Phase 2 EAP was not yet "
+					   "completed successfully");
+				ret->methodState = METHOD_DONE;
+				ret->decision = DECISION_FAIL;
+				wpabuf_free(in_decrypted);
+				return 0;
+			}
+			wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - "
+				   "EAP-Success within TLS tunnel - "
+				   "authentication completed");
+			ret->decision = DECISION_UNCOND_SUCC;
+			ret->methodState = METHOD_DONE;
+			data->phase2_success = 1;
+			if (data->peap_outer_success == 2) {
+				wpabuf_free(in_decrypted);
+				wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK "
+					   "to finish authentication");
+				return 1;
+			} else if (data->peap_outer_success == 1) {
+				/* Reply with EAP-Success within the TLS
+				 * channel to complete the authentication. */
+				resp = wpabuf_alloc(sizeof(struct eap_hdr));
+				if (resp) {
+					rhdr = wpabuf_put(resp, sizeof(*rhdr));
+					rhdr->code = EAP_CODE_SUCCESS;
+					rhdr->identifier = hdr->identifier;
+					rhdr->length =
+						host_to_be16(sizeof(*rhdr));
+				}
+			} else {
+				/* No EAP-Success expected for Phase 1 (outer,
+				 * unencrypted auth), so force EAP state
+				 * machine to SUCCESS state. */
+				sm->peap_done = TRUE;
+			}
+		} else {
+			/* FIX: ? */
+		}
+		break;
+	case EAP_CODE_FAILURE:
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
+		ret->decision = DECISION_FAIL;
+		ret->methodState = METHOD_MAY_CONT;
+		ret->allowNotifications = FALSE;
+		/* Reply with EAP-Failure within the TLS channel to complete
+		 * failure reporting. */
+		resp = wpabuf_alloc(sizeof(struct eap_hdr));
+		if (resp) {
+			rhdr = wpabuf_put(resp, sizeof(*rhdr));
+			rhdr->code = EAP_CODE_FAILURE;
+			rhdr->identifier = hdr->identifier;
+			rhdr->length = host_to_be16(sizeof(*rhdr));
+		}
+		break;
+	default:
+		wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
+			   "Phase 2 EAP header", hdr->code);
+		break;
+	}
+
+	wpabuf_free(in_decrypted);
+
+	if (resp) {
+		int skip_change2 = 0;
+		struct wpabuf *rmsg, buf;
+
+		wpa_hexdump_buf_key(MSG_DEBUG,
+				    "EAP-PEAP: Encrypting Phase 2 data", resp);
+		/* PEAP version changes */
+		if (wpabuf_len(resp) >= 5 &&
+		    wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE &&
+		    eap_get_type(resp) == EAP_TYPE_TLV)
+			skip_change2 = 1;
+		rmsg = resp;
+		if (data->peap_version == 0 && !skip_change2) {
+			wpabuf_set(&buf, wpabuf_head_u8(resp) +
+				   sizeof(struct eap_hdr),
+				   wpabuf_len(resp) - sizeof(struct eap_hdr));
+			rmsg = &buf;
+		}
+
+		if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
+					 data->peap_version, req->identifier,
+					 rmsg, out_data)) {
+			wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt "
+				   "a Phase 2 frame");
+		}
+		wpabuf_free(resp);
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
+					struct eap_method_ret *ret,
+					const struct wpabuf *reqData)
+{
+	const struct eap_hdr *req;
+	size_t left;
+	int res;
+	u8 flags, id;
+	struct wpabuf *resp;
+	const u8 *pos;
+	struct eap_peap_data *data = priv;
+	struct wpabuf msg;
+
+	pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret,
+					reqData, &left, &flags);
+	if (pos == NULL)
+		return NULL;
+	req = wpabuf_head(reqData);
+	id = req->identifier;
+
+	if (flags & EAP_TLS_FLAGS_START) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own "
+			   "ver=%d)", flags & EAP_TLS_VERSION_MASK,
+			data->peap_version);
+		if ((flags & EAP_TLS_VERSION_MASK) < data->peap_version)
+			data->peap_version = flags & EAP_TLS_VERSION_MASK;
+		if (data->force_peap_version >= 0 &&
+		    data->force_peap_version != data->peap_version) {
+			wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select "
+				   "forced PEAP version %d",
+				   data->force_peap_version);
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			ret->allowNotifications = FALSE;
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d",
+			   data->peap_version);
+		left = 0; /* make sure that this frame is empty, even though it
+			   * should always be, anyway */
+	}
+
+	wpabuf_set(&msg, pos, left);
+
+	resp = NULL;
+	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+	    !data->resuming) {
+		res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp);
+	} else {
+		res = eap_peer_tls_process_helper(sm, &data->ssl,
+						  EAP_TYPE_PEAP,
+						  data->peap_version, id, &msg,
+						  &resp);
+
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-PEAP: TLS processing failed");
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			return resp;
+		}
+		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+			char *label;
+			wpa_printf(MSG_DEBUG,
+				   "EAP-PEAP: TLS done, proceed to Phase 2");
+			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
+			 * PEAPv1 implementations seem to be using the old
+			 * label, "client EAP encryption", instead. Use the old
+			 * label by default, but allow it to be configured with
+			 * phase1 parameter peaplabel=1. */
+			if (data->force_new_label)
+				label = "client PEAP encryption";
+			else
+				label = "client EAP encryption";
+			wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in "
+				   "key derivation", label);
+			data->key_data =
+				eap_peer_tls_derive_key(sm, &data->ssl, label,
+							EAP_TLS_KEY_LEN);
+			if (data->key_data) {
+				wpa_hexdump_key(MSG_DEBUG, 
+						"EAP-PEAP: Derived key",
+						data->key_data,
+						EAP_TLS_KEY_LEN);
+			} else {
+				wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to "
+					   "derive key");
+			}
+
+			os_free(data->session_id);
+			data->session_id =
+				eap_peer_tls_derive_session_id(sm, &data->ssl,
+							       EAP_TYPE_PEAP,
+							       &data->id_len);
+			if (data->session_id) {
+				wpa_hexdump(MSG_DEBUG,
+					    "EAP-PEAP: Derived Session-Id",
+					    data->session_id, data->id_len);
+			} else {
+				wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to "
+					   "derive Session-Id");
+			}
+
+			if (sm->workaround && data->resuming) {
+				/*
+				 * At least few RADIUS servers (Aegis v1.1.6;
+				 * but not v1.1.4; and Cisco ACS) seem to be
+				 * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco
+				 * ACS) session resumption with outer
+				 * EAP-Success. This does not seem to follow
+				 * draft-josefsson-pppext-eap-tls-eap-05.txt
+				 * section 4.2, so only allow this if EAP
+				 * workarounds are enabled.
+				 */
+				wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - "
+					   "allow outer EAP-Success to "
+					   "terminate PEAP resumption");
+				ret->decision = DECISION_COND_SUCC;
+				data->phase2_success = 1;
+			}
+
+			data->resuming = 0;
+		}
+
+		if (res == 2) {
+			/*
+			 * Application data included in the handshake message.
+			 */
+			wpabuf_free(data->pending_phase2_req);
+			data->pending_phase2_req = resp;
+			resp = NULL;
+			res = eap_peap_decrypt(sm, data, ret, req, &msg,
+					       &resp);
+		}
+	}
+
+	if (ret->methodState == METHOD_DONE) {
+		ret->allowNotifications = FALSE;
+	}
+
+	if (res == 1) {
+		wpabuf_free(resp);
+		return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP,
+					      data->peap_version);
+	}
+
+	return resp;
+}
+
+
+static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+	struct eap_peap_data *data = priv;
+	return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+		data->phase2_success;
+}
+
+
+static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+	struct eap_peap_data *data = priv;
+	wpabuf_free(data->pending_phase2_req);
+	data->pending_phase2_req = NULL;
+	data->crypto_binding_used = 0;
+}
+
+
+static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+	struct eap_peap_data *data = priv;
+	eap_peap_free_key(data);
+	os_free(data->session_id);
+	data->session_id = NULL;
+	if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+		os_free(data);
+		return NULL;
+	}
+	if (data->phase2_priv && data->phase2_method &&
+	    data->phase2_method->init_for_reauth)
+		data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+	data->phase2_success = 0;
+	data->phase2_eap_success = 0;
+	data->phase2_eap_started = 0;
+	data->resuming = 1;
+	data->reauth = 1;
+	sm->peap_done = FALSE;
+	return priv;
+}
+
+
+static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf,
+			       size_t buflen, int verbose)
+{
+	struct eap_peap_data *data = priv;
+	int len, ret;
+
+	len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+	if (data->phase2_method) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "EAP-PEAPv%d Phase2 method=%s\n",
+				  data->peap_version,
+				  data->phase2_method->name);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+	return len;
+}
+
+
+static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_peap_data *data = priv;
+	return data->key_data != NULL && data->phase2_success;
+}
+
+
+static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_peap_data *data = priv;
+	u8 *key;
+
+	if (data->key_data == NULL || !data->phase2_success)
+		return NULL;
+
+	key = os_malloc(EAP_TLS_KEY_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_TLS_KEY_LEN;
+
+	if (data->crypto_binding_used) {
+		u8 csk[128];
+		/*
+		 * Note: It looks like Microsoft implementation requires null
+		 * termination for this label while the one used for deriving
+		 * IPMK|CMK did not use null termination.
+		 */
+		if (peap_prfplus(data->peap_version, data->ipmk, 40,
+				 "Session Key Generating Function",
+				 (u8 *) "\00", 1, csk, sizeof(csk)) < 0) {
+			os_free(key);
+			return NULL;
+		}
+		wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
+		os_memcpy(key, csk, EAP_TLS_KEY_LEN);
+		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
+			    key, EAP_TLS_KEY_LEN);
+	} else
+		os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+	return key;
+}
+
+
+static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_peap_data *data = priv;
+	u8 *id;
+
+	if (data->session_id == NULL || !data->phase2_success)
+		return NULL;
+
+	id = os_malloc(data->id_len);
+	if (id == NULL)
+		return NULL;
+
+	*len = data->id_len;
+	os_memcpy(id, data->session_id, data->id_len);
+
+	return id;
+}
+
+
+int eap_peer_peap_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_peap_init;
+	eap->deinit = eap_peap_deinit;
+	eap->process = eap_peap_process;
+	eap->isKeyAvailable = eap_peap_isKeyAvailable;
+	eap->getKey = eap_peap_getKey;
+	eap->get_status = eap_peap_get_status;
+	eap->has_reauth_data = eap_peap_has_reauth_data;
+	eap->deinit_for_reauth = eap_peap_deinit_for_reauth;
+	eap->init_for_reauth = eap_peap_init_for_reauth;
+	eap->getSessionId = eap_peap_get_session_id;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_proxy.h b/hostap/src/eap_peer/eap_proxy.h
new file mode 100644
index 0000000..23cdbe6
--- /dev/null
+++ b/hostap/src/eap_peer/eap_proxy.h
@@ -0,0 +1,49 @@
+/*
+ * EAP proxy definitions
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_PROXY_H
+#define EAP_PROXY_H
+
+struct eap_proxy_sm;
+struct eapol_callbacks;
+struct eap_sm;
+struct eap_peer_config;
+
+enum eap_proxy_status {
+	EAP_PROXY_FAILURE = 0x00,
+	EAP_PROXY_SUCCESS
+};
+
+struct eap_proxy_sm *
+eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
+	       void *msg_ctx);
+
+void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy);
+
+int eap_proxy_key_available(struct eap_proxy_sm *sm);
+
+const u8 * eap_proxy_get_eapKeyData(struct eap_proxy_sm *sm, size_t *len);
+
+struct wpabuf * eap_proxy_get_eapRespData(struct eap_proxy_sm *sm);
+
+int eap_proxy_sm_step(struct eap_proxy_sm *sm, struct eap_sm *eap_sm);
+
+enum eap_proxy_status
+eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData,
+			int eapReqDataLen);
+
+int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen,
+			    int verbose);
+
+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);
+
+#endif /* EAP_PROXY_H */
diff --git a/hostap/src/eap_peer/eap_proxy_dummy.c b/hostap/src/eap_peer/eap_proxy_dummy.c
new file mode 100644
index 0000000..d84f012
--- /dev/null
+++ b/hostap/src/eap_peer/eap_proxy_dummy.c
@@ -0,0 +1,77 @@
+/*
+ * EAP proxy - dummy implementation for build testing
+ * 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 "eap_proxy.h"
+
+struct eap_proxy_sm *
+eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
+	       void *msg_ctx)
+{
+	return NULL;
+}
+
+
+void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy)
+{
+}
+
+
+int eap_proxy_key_available(struct eap_proxy_sm *sm)
+{
+	return 0;
+}
+
+
+const u8 * eap_proxy_get_eapKeyData(struct eap_proxy_sm *sm, size_t *len)
+{
+	return NULL;
+}
+
+
+struct wpabuf * eap_proxy_get_eapRespData(struct eap_proxy_sm *sm)
+{
+	return NULL;
+}
+
+
+int eap_proxy_sm_step(struct eap_proxy_sm *sm, struct eap_sm *eap_sm)
+{
+	return 0;
+}
+
+
+enum eap_proxy_status
+eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData,
+			int eapReqDataLen)
+{
+	return EAP_PROXY_FAILURE;
+}
+
+
+int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen,
+			    int verbose)
+{
+	return 0;
+}
+
+
+int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf,
+		       size_t *imsi_len)
+{
+	return -1;
+}
+
+
+int eap_proxy_notify_config(struct eap_proxy_sm *sm,
+			    struct eap_peer_config *config)
+{
+	return -1;
+}
diff --git a/hostap/src/eap_peer/eap_psk.c b/hostap/src/eap_peer/eap_psk.c
new file mode 100644
index 0000000..f012663
--- /dev/null
+++ b/hostap/src/eap_peer/eap_psk.c
@@ -0,0 +1,502 @@
+/*
+ * EAP peer method: EAP-PSK (RFC 4764)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * Note: EAP-PSK is an EAP authentication method and as such, completely
+ * different from WPA-PSK. This file is not needed for WPA-PSK functionality.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
+#include "eap_common/eap_psk_common.h"
+#include "eap_i.h"
+
+
+struct eap_psk_data {
+	enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state;
+	u8 rand_p[EAP_PSK_RAND_LEN];
+	u8 rand_s[EAP_PSK_RAND_LEN];
+	u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN];
+	u8 *id_s, *id_p;
+	size_t id_s_len, id_p_len;
+	u8 msk[EAP_MSK_LEN];
+	u8 emsk[EAP_EMSK_LEN];
+};
+
+
+static void * eap_psk_init(struct eap_sm *sm)
+{
+	struct eap_psk_data *data;
+	const u8 *identity, *password;
+	size_t identity_len, password_len;
+
+	password = eap_get_config_password(sm, &password_len);
+	if (!password || password_len != 16) {
+		wpa_printf(MSG_INFO, "EAP-PSK: 16-octet pre-shared key not "
+			   "configured");
+		return NULL;
+	}
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	if (eap_psk_key_setup(password, data->ak, data->kdk)) {
+		os_free(data);
+		return NULL;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN);
+	data->state = PSK_INIT;
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	if (identity) {
+		data->id_p = os_malloc(identity_len);
+		if (data->id_p)
+			os_memcpy(data->id_p, identity, identity_len);
+		data->id_p_len = identity_len;
+	}
+	if (data->id_p == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity");
+		os_free(data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+static void eap_psk_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_psk_data *data = priv;
+	os_free(data->id_s);
+	os_free(data->id_p);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data,
+					 struct eap_method_ret *ret,
+					 const struct wpabuf *reqData)
+{
+	const struct eap_psk_hdr_1 *hdr1;
+	struct eap_psk_hdr_2 *hdr2;
+	struct wpabuf *resp;
+	u8 *buf, *pos;
+	size_t buflen, len;
+	const u8 *cpos;
+
+	wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state");
+
+	cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len);
+	hdr1 = (const struct eap_psk_hdr_1 *) cpos;
+	if (cpos == NULL || len < sizeof(*hdr1)) {
+		wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message "
+			   "length (%lu; expected %lu or more)",
+			   (unsigned long) len,
+			   (unsigned long) sizeof(*hdr1));
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags);
+	if (EAP_PSK_FLAGS_GET_T(hdr1->flags) != 0) {
+		wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)",
+			   EAP_PSK_FLAGS_GET_T(hdr1->flags));
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s,
+		    EAP_PSK_RAND_LEN);
+	os_memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN);
+	os_free(data->id_s);
+	data->id_s_len = len - sizeof(*hdr1);
+	data->id_s = os_malloc(data->id_s_len);
+	if (data->id_s == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for "
+			   "ID_S (len=%lu)", (unsigned long) data->id_s_len);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S",
+			  data->id_s, data->id_s_len);
+
+	if (random_get_bytes(data->rand_p, EAP_PSK_RAND_LEN)) {
+		wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK,
+			     sizeof(*hdr2) + data->id_p_len, EAP_CODE_RESPONSE,
+			     eap_get_id(reqData));
+	if (resp == NULL)
+		return NULL;
+	hdr2 = wpabuf_put(resp, sizeof(*hdr2));
+	hdr2->flags = EAP_PSK_FLAGS_SET_T(1); /* T=1 */
+	os_memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN);
+	os_memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN);
+	wpabuf_put_data(resp, data->id_p, data->id_p_len);
+	/* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */
+	buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN;
+	buf = os_malloc(buflen);
+	if (buf == NULL) {
+		wpabuf_free(resp);
+		return NULL;
+	}
+	os_memcpy(buf, data->id_p, data->id_p_len);
+	pos = buf + data->id_p_len;
+	os_memcpy(pos, data->id_s, data->id_s_len);
+	pos += data->id_s_len;
+	os_memcpy(pos, hdr1->rand_s, EAP_PSK_RAND_LEN);
+	pos += EAP_PSK_RAND_LEN;
+	os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN);
+	if (omac1_aes_128(data->ak, buf, buflen, hdr2->mac_p)) {
+		os_free(buf);
+		wpabuf_free(resp);
+		return NULL;
+	}
+	os_free(buf);
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_P", hdr2->rand_p,
+		    EAP_PSK_RAND_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", hdr2->mac_p, EAP_PSK_MAC_LEN);
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_P",
+			  data->id_p, data->id_p_len);
+
+	data->state = PSK_MAC_SENT;
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data,
+					 struct eap_method_ret *ret,
+					 const struct wpabuf *reqData)
+{
+	const struct eap_psk_hdr_3 *hdr3;
+	struct eap_psk_hdr_4 *hdr4;
+	struct wpabuf *resp;
+	u8 *buf, *rpchannel, nonce[16], *decrypted;
+	const u8 *pchannel, *tag, *msg;
+	u8 mac[EAP_PSK_MAC_LEN];
+	size_t buflen, left, data_len, len, plen;
+	int failed = 0;
+	const u8 *pos;
+
+	wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state");
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK,
+			       reqData, &len);
+	hdr3 = (const struct eap_psk_hdr_3 *) pos;
+	if (pos == NULL || len < sizeof(*hdr3)) {
+		wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message "
+			   "length (%lu; expected %lu or more)",
+			   (unsigned long) len,
+			   (unsigned long) sizeof(*hdr3));
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	left = len - sizeof(*hdr3);
+	pchannel = (const u8 *) (hdr3 + 1);
+	wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags);
+	if (EAP_PSK_FLAGS_GET_T(hdr3->flags) != 2) {
+		wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)",
+			   EAP_PSK_FLAGS_GET_T(hdr3->flags));
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s,
+		    EAP_PSK_RAND_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left);
+
+	if (left < 4 + 16 + 1) {
+		wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in "
+			   "third message (len=%lu, expected 21)",
+			   (unsigned long) left);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	/* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */
+	buflen = data->id_s_len + EAP_PSK_RAND_LEN;
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return NULL;
+	os_memcpy(buf, data->id_s, data->id_s_len);
+	os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN);
+	if (omac1_aes_128(data->ak, buf, buflen, mac)) {
+		os_free(buf);
+		return NULL;
+	}
+	os_free(buf);
+	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;
+		ret->decision = DECISION_FAIL;
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully");
+
+	if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek,
+				data->msk, data->emsk)) {
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		return NULL;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN);
+
+	os_memset(nonce, 0, 12);
+	os_memcpy(nonce + 12, pchannel, 4);
+	pchannel += 4;
+	left -= 4;
+
+	tag = pchannel;
+	pchannel += 16;
+	left -= 16;
+
+	msg = pchannel;
+
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce",
+		    nonce, sizeof(nonce));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr",
+		    wpabuf_head(reqData), 5);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left);
+
+	decrypted = os_malloc(left);
+	if (decrypted == NULL) {
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		return NULL;
+	}
+	os_memcpy(decrypted, msg, left);
+
+	if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
+				wpabuf_head(reqData),
+				sizeof(struct eap_hdr) + 1 +
+				sizeof(*hdr3) - EAP_PSK_MAC_LEN, decrypted,
+				left, tag)) {
+		wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed");
+		os_free(decrypted);
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message",
+		    decrypted, left);
+
+	/* Verify R flag */
+	switch (decrypted[0] >> 6) {
+	case EAP_PSK_R_FLAG_CONT:
+		wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported");
+		failed = 1;
+		break;
+	case EAP_PSK_R_FLAG_DONE_SUCCESS:
+		wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS");
+		break;
+	case EAP_PSK_R_FLAG_DONE_FAILURE:
+		wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE");
+		wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected "
+			   "authentication");
+		failed = 1;
+		break;
+	}
+
+	data_len = 1;
+	if ((decrypted[0] & EAP_PSK_E_FLAG) && left > 1)
+		data_len++;
+	plen = sizeof(*hdr4) + 4 + 16 + data_len;
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, plen,
+			     EAP_CODE_RESPONSE, eap_get_id(reqData));
+	if (resp == NULL) {
+		os_free(decrypted);
+		return NULL;
+	}
+	hdr4 = wpabuf_put(resp, sizeof(*hdr4));
+	hdr4->flags = EAP_PSK_FLAGS_SET_T(3); /* T=3 */
+	os_memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN);
+	rpchannel = wpabuf_put(resp, 4 + 16 + data_len);
+
+	/* nonce++ */
+	inc_byte_array(nonce, sizeof(nonce));
+	os_memcpy(rpchannel, nonce + 12, 4);
+
+	if (decrypted[0] & EAP_PSK_E_FLAG) {
+		wpa_printf(MSG_DEBUG, "EAP-PSK: Unsupported E (Ext) flag");
+		failed = 1;
+		rpchannel[4 + 16] = (EAP_PSK_R_FLAG_DONE_FAILURE << 6) |
+			EAP_PSK_E_FLAG;
+		if (left > 1) {
+			/* Add empty EXT_Payload with same EXT_Type */
+			rpchannel[4 + 16 + 1] = decrypted[1];
+		}
+	} else if (failed)
+		rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_FAILURE << 6;
+	else
+		rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6;
+
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)",
+		    rpchannel + 4 + 16, data_len);
+	if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce),
+				wpabuf_head(resp),
+				sizeof(struct eap_hdr) + 1 + sizeof(*hdr4),
+				rpchannel + 4 + 16, data_len, rpchannel + 4)) {
+		os_free(decrypted);
+		wpabuf_free(resp);
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)",
+		    rpchannel, 4 + 16 + data_len);
+
+	wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully",
+		   failed ? "un" : "");
+	data->state = PSK_DONE;
+	ret->methodState = METHOD_DONE;
+	ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC;
+
+	os_free(decrypted);
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_psk_process(struct eap_sm *sm, void *priv,
+				       struct eap_method_ret *ret,
+				       const struct wpabuf *reqData)
+{
+	struct eap_psk_data *data = priv;
+	const u8 *pos;
+	struct wpabuf *resp = NULL;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len);
+	if (pos == NULL) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	ret->ignore = FALSE;
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_FAIL;
+	ret->allowNotifications = TRUE;
+
+	switch (data->state) {
+	case PSK_INIT:
+		resp = eap_psk_process_1(data, ret, reqData);
+		break;
+	case PSK_MAC_SENT:
+		resp = eap_psk_process_3(data, ret, reqData);
+		break;
+	case PSK_DONE:
+		wpa_printf(MSG_DEBUG, "EAP-PSK: in DONE state - ignore "
+			   "unexpected message");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (ret->methodState == METHOD_DONE) {
+		ret->allowNotifications = FALSE;
+	}
+
+	return resp;
+}
+
+
+static Boolean eap_psk_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_psk_data *data = priv;
+	return data->state == PSK_DONE;
+}
+
+
+static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_psk_data *data = priv;
+	u8 *key;
+
+	if (data->state != PSK_DONE)
+		return NULL;
+
+	key = os_malloc(EAP_MSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_MSK_LEN;
+	os_memcpy(key, data->msk, EAP_MSK_LEN);
+
+	return key;
+}
+
+
+static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_psk_data *data = priv;
+	u8 *id;
+
+	if (data->state != PSK_DONE)
+		return NULL;
+
+	*len = 1 + 2 * EAP_PSK_RAND_LEN;
+	id = os_malloc(*len);
+	if (id == NULL)
+		return NULL;
+
+	id[0] = EAP_TYPE_PSK;
+	os_memcpy(id + 1, data->rand_p, EAP_PSK_RAND_LEN);
+	os_memcpy(id + 1 + EAP_PSK_RAND_LEN, data->rand_s, EAP_PSK_RAND_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: Derived Session-Id", id, *len);
+
+	return id;
+}
+
+
+static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_psk_data *data = priv;
+	u8 *key;
+
+	if (data->state != PSK_DONE)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_EMSK_LEN;
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+	return key;
+}
+
+
+int eap_peer_psk_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_psk_init;
+	eap->deinit = eap_psk_deinit;
+	eap->process = eap_psk_process;
+	eap->isKeyAvailable = eap_psk_isKeyAvailable;
+	eap->getKey = eap_psk_getKey;
+	eap->getSessionId = eap_psk_get_session_id;
+	eap->get_emsk = eap_psk_get_emsk;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_pwd.c b/hostap/src/eap_peer/eap_pwd.c
new file mode 100644
index 0000000..1f78544
--- /dev/null
+++ b/hostap/src/eap_peer/eap_pwd.c
@@ -0,0 +1,1076 @@
+/*
+ * EAP peer method: EAP-pwd (RFC 5931)
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * 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/sha256.h"
+#include "crypto/ms_funcs.h"
+#include "eap_peer/eap_i.h"
+#include "eap_common/eap_pwd_common.h"
+
+
+struct eap_pwd_data {
+	enum {
+		PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req,
+		SUCCESS_ON_FRAG_COMPLETION, SUCCESS, FAILURE
+	} state;
+	u8 *id_peer;
+	size_t id_peer_len;
+	u8 *id_server;
+	size_t id_server_len;
+	u8 *password;
+	size_t password_len;
+	int password_hash;
+	u16 group_num;
+	EAP_PWD_group *grp;
+
+	struct wpabuf *inbuf;
+	size_t in_frag_pos;
+	struct wpabuf *outbuf;
+	size_t out_frag_pos;
+	size_t mtu;
+
+	BIGNUM *k;
+	BIGNUM *private_value;
+	BIGNUM *server_scalar;
+	BIGNUM *my_scalar;
+	EC_POINT *my_element;
+	EC_POINT *server_element;
+
+	u8 msk[EAP_MSK_LEN];
+	u8 emsk[EAP_EMSK_LEN];
+	u8 session_id[1 + SHA256_MAC_LEN];
+
+	BN_CTX *bnctx;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_pwd_state_txt(int state)
+{
+	switch (state) {
+        case PWD_ID_Req:
+		return "PWD-ID-Req";
+        case PWD_Commit_Req:
+		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:
+		return "FAILURE";
+        default:
+		return "PWD-UNK";
+	}
+}
+#endif  /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_pwd_state(struct eap_pwd_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-PWD: %s -> %s",
+		   eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
+	data->state = state;
+}
+
+
+static void * eap_pwd_init(struct eap_sm *sm)
+{
+	struct eap_pwd_data *data;
+	const u8 *identity, *password;
+	size_t identity_len, password_len;
+	int fragment_size;
+	int pwhash;
+
+	password = eap_get_config_password2(sm, &password_len, &pwhash);
+	if (password == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD: No password configured!");
+		return NULL;
+	}
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	if (identity == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD: No identity configured!");
+		return NULL;
+	}
+
+	if ((data = os_zalloc(sizeof(*data))) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD: memory allocation data fail");
+		return NULL;
+	}
+
+	if ((data->bnctx = BN_CTX_new()) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
+		os_free(data);
+		return NULL;
+	}
+
+	if ((data->id_peer = os_malloc(identity_len)) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
+		BN_CTX_free(data->bnctx);
+		os_free(data);
+		return NULL;
+	}
+
+	os_memcpy(data->id_peer, identity, identity_len);
+	data->id_peer_len = identity_len;
+
+	if ((data->password = os_malloc(password_len)) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail");
+		BN_CTX_free(data->bnctx);
+		bin_clear_free(data->id_peer, data->id_peer_len);
+		os_free(data);
+		return NULL;
+	}
+	os_memcpy(data->password, password, password_len);
+	data->password_len = password_len;
+	data->password_hash = pwhash;
+
+	data->out_frag_pos = data->in_frag_pos = 0;
+	data->inbuf = data->outbuf = NULL;
+	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;
+
+	return data;
+}
+
+
+static void eap_pwd_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_pwd_data *data = priv;
+
+	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_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_clear_free(data->grp->pwe);
+		BN_clear_free(data->grp->order);
+		BN_clear_free(data->grp->prime);
+		os_free(data->grp);
+	}
+	wpabuf_free(data->inbuf);
+	wpabuf_free(data->outbuf);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_pwd_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_MSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	os_memcpy(key, data->msk, EAP_MSK_LEN);
+	*len = EAP_MSK_LEN;
+
+	return key;
+}
+
+
+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,
+			    const struct wpabuf *reqData,
+			    const u8 *payload, size_t payload_len)
+{
+	struct eap_pwd_id *id;
+	const u8 *password;
+	size_t password_len;
+	u8 pwhashhash[16];
+	int res;
+
+	if (data->state != PWD_ID_Req) {
+		ret->ignore = TRUE;
+		eap_pwd_state(data, FAILURE);
+		return;
+	}
+
+	if (payload_len < sizeof(struct eap_pwd_id)) {
+		ret->ignore = TRUE;
+		eap_pwd_state(data, FAILURE);
+		return;
+	}
+
+	id = (struct eap_pwd_id *) payload;
+	data->group_num = be_to_host16(id->group_num);
+	wpa_printf(MSG_DEBUG,
+		   "EAP-PWD: Server EAP-pwd-ID proposal: group=%u random=%u prf=%u prep=%u",
+		   data->group_num, id->random_function, id->prf, id->prep);
+	if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
+	    (id->prf != EAP_PWD_DEFAULT_PRF)) {
+		ret->ignore = TRUE;
+		eap_pwd_state(data, FAILURE);
+		return;
+	}
+
+	if (id->prep != EAP_PWD_PREP_NONE &&
+	    id->prep != EAP_PWD_PREP_MS) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-PWD: Unsupported password pre-processing technique (Prep=%u)",
+			   id->prep);
+		eap_pwd_state(data, FAILURE);
+		return;
+	}
+
+	if (id->prep == EAP_PWD_PREP_NONE && data->password_hash) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-PWD: Unhashed password not available");
+		eap_pwd_state(data, FAILURE);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d",
+		   data->group_num);
+
+	data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id));
+	if (data->id_server == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
+		eap_pwd_state(data, FAILURE);
+		return;
+	}
+	data->id_server_len = payload_len - sizeof(struct eap_pwd_id);
+	os_memcpy(data->id_server, id->identity, data->id_server_len);
+	wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of",
+			  data->id_server, data->id_server_len);
+
+	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);
+		return;
+	}
+
+	if (id->prep == EAP_PWD_PREP_MS) {
+#ifdef CONFIG_FIPS
+		wpa_printf(MSG_ERROR,
+			   "EAP-PWD (peer): MS password hash not supported in FIPS mode");
+		eap_pwd_state(data, FAILURE);
+		return;
+#else /* CONFIG_FIPS */
+		if (data->password_hash) {
+			res = hash_nt_password_hash(data->password, pwhashhash);
+		} else {
+			u8 pwhash[16];
+
+			res = nt_password_hash(data->password,
+					       data->password_len, pwhash);
+			if (res == 0)
+				res = hash_nt_password_hash(pwhash, pwhashhash);
+			os_memset(pwhash, 0, sizeof(pwhash));
+		}
+
+		if (res) {
+			eap_pwd_state(data, FAILURE);
+			return;
+		}
+
+		password = pwhashhash;
+		password_len = sizeof(pwhashhash);
+#endif /* CONFIG_FIPS */
+	} else {
+		password = data->password;
+		password_len = data->password_len;
+	}
+
+	/* compute PWE */
+	res = compute_password_element(data->grp, data->group_num,
+				       password, password_len,
+				       data->id_server, data->id_server_len,
+				       data->id_peer, data->id_peer_len,
+				       id->token);
+	os_memset(pwhashhash, 0, sizeof(pwhashhash));
+	if (res) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE");
+		eap_pwd_state(data, FAILURE);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...",
+		   BN_num_bits(data->grp->prime));
+
+	data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) +
+				    data->id_peer_len);
+	if (data->outbuf == NULL) {
+		eap_pwd_state(data, FAILURE);
+		return;
+	}
+	wpabuf_put_be16(data->outbuf, data->group_num);
+	wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
+	wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
+	wpabuf_put_data(data->outbuf, id->token, sizeof(id->token));
+	wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE);
+	wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len);
+
+	eap_pwd_state(data, PWD_Commit_Req);
+}
+
+
+static void
+eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
+				struct eap_method_ret *ret,
+				const struct wpabuf *reqData,
+				const u8 *payload, size_t payload_len)
+{
+	EC_POINT *K = NULL, *point = NULL;
+	BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL;
+	u16 offset;
+	u8 *ptr, *scalar = NULL, *element = NULL;
+	size_t prime_len, order_len;
+
+	if (data->state != PWD_Commit_Req) {
+		ret->ignore = TRUE;
+		goto fin;
+	}
+
+	prime_len = BN_num_bytes(data->grp->prime);
+	order_len = BN_num_bytes(data->grp->order);
+
+	if (payload_len != 2 * prime_len + order_len) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+			   (unsigned int) payload_len,
+			   (unsigned int) (2 * prime_len + order_len));
+		goto fin;
+	}
+
+	if (((data->private_value = BN_new()) == NULL) ||
+	    ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
+	    ((cofactor = BN_new()) == NULL) ||
+	    ((data->my_scalar = BN_new()) == NULL) ||
+	    ((mask = BN_new()) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail");
+		goto fin;
+	}
+
+	if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
+		wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor "
+			   "for curve");
+		goto fin;
+	}
+
+	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)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation "
+			   "fail");
+		eap_pwd_state(data, FAILURE);
+		goto fin;
+	}
+
+	if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
+	{
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail");
+		goto fin;
+	}
+	BN_clear_free(mask);
+
+	if (((x = BN_new()) == NULL) ||
+	    ((y = BN_new()) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail");
+		goto fin;
+	}
+
+	/* process the request */
+	if (((data->server_scalar = BN_new()) == NULL) ||
+	    ((data->k = BN_new()) == NULL) ||
+	    ((K = EC_POINT_new(data->grp->group)) == NULL) ||
+	    ((point = EC_POINT_new(data->grp->group)) == NULL) ||
+	    ((data->server_element = EC_POINT_new(data->grp->group)) == NULL))
+	{
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
+			   "fail");
+		goto fin;
+	}
+
+	/* element, x then y, followed by scalar */
+	ptr = (u8 *) payload;
+	BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
+	ptr += BN_num_bytes(data->grp->prime);
+	BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
+	ptr += BN_num_bytes(data->grp->prime);
+	BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar);
+	if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
+						 data->server_element, x, y,
+						 data->bnctx)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
+			   "fail");
+		goto fin;
+	}
+
+	/* check to ensure server's element is not in a small sub-group */
+	if (BN_cmp(cofactor, BN_value_one())) {
+		if (!EC_POINT_mul(data->grp->group, point, NULL,
+				  data->server_element, cofactor, NULL)) {
+			wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
+				   "server element by order!\n");
+			goto fin;
+		}
+		if (EC_POINT_is_at_infinity(data->grp->group, point)) {
+			wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
+				   "is at infinity!\n");
+			goto fin;
+		}
+	}
+
+	/* compute the shared key, k */
+	if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
+			   data->server_scalar, data->bnctx)) ||
+	    (!EC_POINT_add(data->grp->group, K, K, data->server_element,
+			   data->bnctx)) ||
+	    (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
+			   data->bnctx))) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key "
+			   "fail");
+		goto fin;
+	}
+
+	/* ensure that the shared key isn't in a small sub-group */
+	if (BN_cmp(cofactor, BN_value_one())) {
+		if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
+				  NULL)) {
+			wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
+				   "shared key point by order");
+			goto fin;
+		}
+	}
+
+	/*
+	 * This check is strictly speaking just for the case above where
+	 * co-factor > 1 but it was suggested that even though this is probably
+	 * never going to happen it is a simple and safe check "just to be
+	 * sure" so let's be safe.
+	 */
+	if (EC_POINT_is_at_infinity(data->grp->group, K)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at "
+			   "infinity!\n");
+		goto fin;
+	}
+
+	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
+						 NULL, data->bnctx)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract "
+			   "shared secret from point");
+		goto fin;
+	}
+
+	/* now do the response */
+	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+						 data->my_element, x, y,
+						 data->bnctx)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
+		goto fin;
+	}
+
+	if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
+	    ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
+	     NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail");
+		goto fin;
+	}
+
+	/*
+	 * bignums occupy as little memory as possible so one that is
+	 * sufficiently smaller than the prime or order might need pre-pending
+	 * with zeros.
+	 */
+	os_memset(scalar, 0, BN_num_bytes(data->grp->order));
+	os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
+	offset = BN_num_bytes(data->grp->order) -
+		BN_num_bytes(data->my_scalar);
+	BN_bn2bin(data->my_scalar, scalar + offset);
+
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+	BN_bn2bin(x, element + offset);
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+	BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
+
+	data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) +
+				    2 * BN_num_bytes(data->grp->prime));
+	if (data->outbuf == NULL)
+		goto fin;
+
+	/* we send the element as (x,y) follwed by the scalar */
+	wpabuf_put_data(data->outbuf, element,
+			2 * BN_num_bytes(data->grp->prime));
+	wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order));
+
+fin:
+	os_free(scalar);
+	os_free(element);
+	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
+		eap_pwd_state(data, PWD_Confirm_Req);
+}
+
+
+static void
+eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
+				 struct eap_method_ret *ret,
+				 const struct wpabuf *reqData,
+				 const u8 *payload, size_t payload_len)
+{
+	BIGNUM *x = NULL, *y = NULL;
+	struct crypto_hash *hash;
+	u32 cs;
+	u16 grp;
+	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
+	int offset;
+
+	if (data->state != PWD_Confirm_Req) {
+		ret->ignore = TRUE;
+		goto fin;
+	}
+
+	if (payload_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd: Unexpected Confirm payload length %u (expected %u)",
+			   (unsigned int) payload_len, SHA256_MAC_LEN);
+		goto fin;
+	}
+
+	/*
+	 * first build up the ciphersuite which is group | random_function |
+	 *	prf
+	 */
+	grp = htons(data->group_num);
+	ptr = (u8 *) &cs;
+	os_memcpy(ptr, &grp, sizeof(u16));
+	ptr += sizeof(u16);
+	*ptr = EAP_PWD_DEFAULT_RAND_FUNC;
+	ptr += sizeof(u8);
+	*ptr = EAP_PWD_DEFAULT_PRF;
+
+	/* each component of the cruft will be at most as big as the prime */
+	if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
+	    ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation "
+			   "fail");
+		goto fin;
+	}
+
+	/*
+	 * server's commit is H(k | server_element | server_scalar |
+	 *			peer_element | peer_scalar | ciphersuite)
+	 */
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
+
+	/*
+	 * zero the memory each time because this is mod prime math and some
+	 * value may start with a few zeros and the previous one did not.
+	 */
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
+	BN_bn2bin(data->k, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+	/* server element: x, y */
+	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+						 data->server_element, x, y,
+						 data->bnctx)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+	BN_bn2bin(x, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+	BN_bn2bin(y, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+	/* server scalar */
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->order) -
+		BN_num_bytes(data->server_scalar);
+	BN_bn2bin(data->server_scalar, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+	/* my element: x, y */
+	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+						 data->my_element, x, y,
+						 data->bnctx)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+	BN_bn2bin(x, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+	BN_bn2bin(y, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+	/* my scalar */
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->order) -
+		BN_num_bytes(data->my_scalar);
+	BN_bn2bin(data->my_scalar, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+	/* the ciphersuite */
+	eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
+
+	/* random function fin */
+	eap_pwd_h_final(hash, conf);
+
+	ptr = (u8 *) payload;
+	if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify");
+		goto fin;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified");
+
+	/*
+	 * compute confirm:
+	 *  H(k | peer_element | peer_scalar | server_element | server_scalar |
+	 *    ciphersuite)
+	 */
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
+
+	/* k */
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
+	BN_bn2bin(data->k, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+	/* my element */
+	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+						 data->my_element, x, y,
+						 data->bnctx)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+	BN_bn2bin(x, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+	BN_bn2bin(y, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+	/* my scalar */
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->order) -
+		BN_num_bytes(data->my_scalar);
+	BN_bn2bin(data->my_scalar, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+	/* server element: x, y */
+	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+						 data->server_element, x, y,
+						 data->bnctx)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+	BN_bn2bin(x, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+	BN_bn2bin(y, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+	/* server scalar */
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->order) -
+		BN_num_bytes(data->server_scalar);
+	BN_bn2bin(data->server_scalar, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+	/* the ciphersuite */
+	eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
+
+	/* all done */
+	eap_pwd_h_final(hash, conf);
+
+	if (compute_keys(data->grp, data->bnctx, data->k,
+			 data->my_scalar, data->server_scalar, conf, ptr,
+			 &cs, data->msk, data->emsk, data->session_id) < 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
+			   "EMSK");
+		goto fin;
+	}
+
+	data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
+	if (data->outbuf == NULL)
+		goto fin;
+
+	wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
+
+fin:
+	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 {
+		eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION);
+	}
+}
+
+
+static struct wpabuf *
+eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
+		const struct wpabuf *reqData)
+{
+	struct eap_pwd_data *data = priv;
+	struct wpabuf *resp = NULL;
+	const u8 *pos, *buf;
+	size_t len;
+	u16 tot_len = 0;
+	u8 lm_exch;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len);
+	if ((pos == NULL) || (len < 1)) {
+		wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and "
+			   "len is %d",
+			   pos == NULL ? "NULL" : "not NULL", (int) len);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	ret->ignore = FALSE;
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_FAIL;
+	ret->allowNotifications = FALSE;
+
+	lm_exch = *pos;
+	pos++;                  /* skip over the bits and the exch */
+	len--;
+
+	/*
+	 * we're fragmenting so send out the next fragment
+	 */
+	if (data->out_frag_pos) {
+		/*
+		 * this should be an ACK
+		 */
+		if (len)
+			wpa_printf(MSG_INFO, "Bad Response! Fragmenting but "
+				   "not an ACK");
+
+		wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment");
+		/*
+		 * check if there are going to be more fragments
+		 */
+		len = wpabuf_len(data->outbuf) - data->out_frag_pos;
+		if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
+			len = data->mtu - EAP_PWD_HDR_SIZE;
+			EAP_PWD_SET_MORE_BIT(lm_exch);
+		}
+		resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+				     EAP_PWD_HDR_SIZE + len,
+				     EAP_CODE_RESPONSE, eap_get_id(reqData));
+		if (resp == NULL) {
+			wpa_printf(MSG_INFO, "Unable to allocate memory for "
+				   "next fragment!");
+			return NULL;
+		}
+		wpabuf_put_u8(resp, lm_exch);
+		buf = wpabuf_head_u8(data->outbuf);
+		wpabuf_put_data(resp, buf + data->out_frag_pos, len);
+		data->out_frag_pos += len;
+		/*
+		 * this is the last fragment so get rid of the out buffer
+		 */
+		if (data->out_frag_pos >= wpabuf_len(data->outbuf)) {
+			wpabuf_free(data->outbuf);
+			data->outbuf = NULL;
+			data->out_frag_pos = 0;
+		}
+		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;
+	}
+
+	/*
+	 * see if this is a fragment that needs buffering
+	 *
+	 * if it's the first fragment there'll be a length field
+	 */
+	if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-pwd: Frame too short to contain Total-Length field");
+			ret->ignore = TRUE;
+			return NULL;
+		}
+		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;
+		if (data->inbuf) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-pwd: Unexpected new fragment start when previous fragment is still in use");
+			ret->ignore = TRUE;
+			return NULL;
+		}
+		data->inbuf = wpabuf_alloc(tot_len);
+		if (data->inbuf == NULL) {
+			wpa_printf(MSG_INFO, "Out of memory to buffer "
+				   "fragments!");
+			return NULL;
+		}
+		data->in_frag_pos = 0;
+		pos += sizeof(u16);
+		len -= sizeof(u16);
+	}
+	/*
+	 * buffer and ACK the fragment
+	 */
+	if (EAP_PWD_GET_MORE_BIT(lm_exch)) {
+		data->in_frag_pos += len;
+		if (data->in_frag_pos > wpabuf_size(data->inbuf)) {
+			wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack "
+				   "detected (%d vs. %d)!",
+				   (int) data->in_frag_pos,
+				   (int) wpabuf_len(data->inbuf));
+			wpabuf_free(data->inbuf);
+			data->inbuf = NULL;
+			data->in_frag_pos = 0;
+			return NULL;
+		}
+		wpabuf_put_data(data->inbuf, pos, len);
+
+		resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+				     EAP_PWD_HDR_SIZE,
+				     EAP_CODE_RESPONSE, eap_get_id(reqData));
+		if (resp != NULL)
+			wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch)));
+		wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment",
+			   (int) len);
+		return resp;
+	}
+	/*
+	 * we're buffering and this is the last fragment
+	 */
+	if (data->in_frag_pos) {
+		wpabuf_put_data(data->inbuf, pos, len);
+		wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes",
+			   (int) len);
+		data->in_frag_pos += len;
+		pos = wpabuf_head_u8(data->inbuf);
+		len = data->in_frag_pos;
+	}
+	wpa_printf(MSG_DEBUG, "EAP-pwd: processing frame: exch %d, len %d",
+		   EAP_PWD_GET_EXCHANGE(lm_exch), (int) len);
+
+	switch (EAP_PWD_GET_EXCHANGE(lm_exch)) {
+	case EAP_PWD_OPCODE_ID_EXCH:
+		eap_pwd_perform_id_exchange(sm, data, ret, reqData,
+					    pos, len);
+		break;
+	case EAP_PWD_OPCODE_COMMIT_EXCH:
+		eap_pwd_perform_commit_exchange(sm, data, ret, reqData,
+						pos, len);
+		break;
+	case EAP_PWD_OPCODE_CONFIRM_EXCH:
+		eap_pwd_perform_confirm_exchange(sm, data, ret, reqData,
+						 pos, len);
+		break;
+	default:
+		wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown "
+			   "opcode %d", lm_exch);
+		break;
+	}
+	/*
+	 * if we buffered the just processed input now's the time to free it
+	 */
+	if (data->in_frag_pos) {
+		wpabuf_free(data->inbuf);
+		data->inbuf = NULL;
+		data->in_frag_pos = 0;
+	}
+
+	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?
+	 */
+	lm_exch = EAP_PWD_GET_EXCHANGE(lm_exch);
+	len = wpabuf_len(data->outbuf);
+	if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
+		resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu,
+				     EAP_CODE_RESPONSE, eap_get_id(reqData));
+		/*
+		 * if so it's the first so include a length field
+		 */
+		EAP_PWD_SET_LENGTH_BIT(lm_exch);
+		EAP_PWD_SET_MORE_BIT(lm_exch);
+		tot_len = len;
+		/*
+		 * keep the packet at the MTU
+		 */
+		len = data->mtu - EAP_PWD_HDR_SIZE - sizeof(u16);
+		wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, total "
+			   "length = %d", tot_len);
+	} else {
+		resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+				     EAP_PWD_HDR_SIZE + len,
+				     EAP_CODE_RESPONSE, eap_get_id(reqData));
+	}
+	if (resp == NULL)
+		return NULL;
+
+	wpabuf_put_u8(resp, lm_exch);
+	if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
+		wpabuf_put_be16(resp, tot_len);
+		data->out_frag_pos += len;
+	}
+	buf = wpabuf_head_u8(data->outbuf);
+	wpabuf_put_data(resp, buf, len);
+	/*
+	 * if we're not fragmenting then there's no need to carry this around
+	 */
+	if (data->out_frag_pos == 0) {
+		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;
+}
+
+
+static Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv)
+{
+	struct eap_pwd_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_pwd_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	if ((key = os_malloc(EAP_EMSK_LEN)) == NULL)
+		return NULL;
+
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+	*len = EAP_EMSK_LEN;
+
+	return key;
+}
+
+
+int eap_peer_pwd_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_pwd_init;
+	eap->deinit = eap_pwd_deinit;
+	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);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_sake.c b/hostap/src/eap_peer/eap_sake.c
new file mode 100644
index 0000000..c4f9843
--- /dev/null
+++ b/hostap/src/eap_peer/eap_sake.c
@@ -0,0 +1,516 @@
+/*
+ * EAP peer method: EAP-SAKE (RFC 4763)
+ * Copyright (c) 2006-2008, 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/random.h"
+#include "eap_peer/eap_i.h"
+#include "eap_common/eap_sake_common.h"
+
+struct eap_sake_data {
+	enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
+	u8 root_secret_a[EAP_SAKE_ROOT_SECRET_LEN];
+	u8 root_secret_b[EAP_SAKE_ROOT_SECRET_LEN];
+	u8 rand_s[EAP_SAKE_RAND_LEN];
+	u8 rand_p[EAP_SAKE_RAND_LEN];
+	struct {
+		u8 auth[EAP_SAKE_TEK_AUTH_LEN];
+		u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
+	} tek;
+	u8 msk[EAP_MSK_LEN];
+	u8 emsk[EAP_EMSK_LEN];
+	u8 session_id;
+	int session_id_set;
+	u8 *peerid;
+	size_t peerid_len;
+	u8 *serverid;
+	size_t serverid_len;
+};
+
+
+static const char * eap_sake_state_txt(int state)
+{
+	switch (state) {
+	case IDENTITY:
+		return "IDENTITY";
+	case CHALLENGE:
+		return "CHALLENGE";
+	case CONFIRM:
+		return "CONFIRM";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "?";
+	}
+}
+
+
+static void eap_sake_state(struct eap_sake_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
+		   eap_sake_state_txt(data->state),
+		   eap_sake_state_txt(state));
+	data->state = state;
+}
+
+
+static void eap_sake_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_sake_init(struct eap_sm *sm)
+{
+	struct eap_sake_data *data;
+	const u8 *identity, *password;
+	size_t identity_len, password_len;
+
+	password = eap_get_config_password(sm, &password_len);
+	if (!password || password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: No key of correct length "
+			   "configured");
+		return NULL;
+	}
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = IDENTITY;
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	if (identity) {
+		data->peerid = os_malloc(identity_len);
+		if (data->peerid == NULL) {
+			eap_sake_deinit(sm, data);
+			return NULL;
+		}
+		os_memcpy(data->peerid, identity, identity_len);
+		data->peerid_len = identity_len;
+	}
+
+	os_memcpy(data->root_secret_a, password, EAP_SAKE_ROOT_SECRET_LEN);
+	os_memcpy(data->root_secret_b,
+		  password + EAP_SAKE_ROOT_SECRET_LEN,
+		  EAP_SAKE_ROOT_SECRET_LEN);
+
+	return data;
+}
+
+
+static void eap_sake_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_sake_data *data = priv;
+	os_free(data->serverid);
+	os_free(data->peerid);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
+					  int id, size_t length, u8 subtype)
+{
+	struct eap_sake_hdr *sake;
+	struct wpabuf *msg;
+	size_t plen;
+
+	plen = length + sizeof(struct eap_sake_hdr);
+
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
+			    EAP_CODE_RESPONSE, id);
+	if (msg == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
+			   "request");
+		return NULL;
+	}
+
+	sake = wpabuf_put(msg, sizeof(*sake));
+	sake->version = EAP_SAKE_VERSION;
+	sake->session_id = data->session_id;
+	sake->subtype = subtype;
+
+	return msg;
+}
+
+
+static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm,
+						 struct eap_sake_data *data,
+						 struct eap_method_ret *ret,
+						 u8 id,
+						 const u8 *payload,
+						 size_t payload_len)
+{
+	struct eap_sake_parse_attr attr;
+	struct wpabuf *resp;
+
+	if (data->state != IDENTITY) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Identity");
+
+	if (eap_sake_parse_attributes(payload, payload_len, &attr))
+		return NULL;
+
+	if (!attr.perm_id_req && !attr.any_id_req) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: No AT_PERM_ID_REQ or "
+			   "AT_ANY_ID_REQ in Request/Identity");
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity");
+
+	resp = eap_sake_build_msg(data, id, 2 + data->peerid_len,
+				  EAP_SAKE_SUBTYPE_IDENTITY);
+	if (resp == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
+	eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
+			  data->peerid, data->peerid_len);
+
+	eap_sake_state(data, CHALLENGE);
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm,
+						  struct eap_sake_data *data,
+						  struct eap_method_ret *ret,
+						  u8 id,
+						  const u8 *payload,
+						  size_t payload_len)
+{
+	struct eap_sake_parse_attr attr;
+	struct wpabuf *resp;
+	u8 *rpos;
+	size_t rlen;
+
+	if (data->state != IDENTITY && data->state != CHALLENGE) {
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge received "
+			   "in unexpected state (%d)", data->state);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	if (data->state == IDENTITY)
+		eap_sake_state(data, CHALLENGE);
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Challenge");
+
+	if (eap_sake_parse_attributes(payload, payload_len, &attr))
+		return NULL;
+
+	if (!attr.rand_s) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Request/Challenge did not "
+			   "include AT_RAND_S");
+		return NULL;
+	}
+
+	os_memcpy(data->rand_s, attr.rand_s, EAP_SAKE_RAND_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
+		    data->rand_s, EAP_SAKE_RAND_LEN);
+
+	if (random_get_bytes(data->rand_p, EAP_SAKE_RAND_LEN)) {
+		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_P (peer rand)",
+		    data->rand_p, EAP_SAKE_RAND_LEN);
+
+	os_free(data->serverid);
+	data->serverid = NULL;
+	data->serverid_len = 0;
+	if (attr.serverid) {
+		wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID",
+				  attr.serverid, attr.serverid_len);
+		data->serverid = os_malloc(attr.serverid_len);
+		if (data->serverid == NULL)
+			return NULL;
+		os_memcpy(data->serverid, attr.serverid, attr.serverid_len);
+		data->serverid_len = attr.serverid_len;
+	}
+
+	eap_sake_derive_keys(data->root_secret_a, data->root_secret_b,
+			     data->rand_s, data->rand_p,
+			     (u8 *) &data->tek, data->msk, data->emsk);
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge");
+
+	rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN;
+	if (data->peerid)
+		rlen += 2 + data->peerid_len;
+	resp = eap_sake_build_msg(data, id, rlen, EAP_SAKE_SUBTYPE_CHALLENGE);
+	if (resp == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_P");
+	eap_sake_add_attr(resp, EAP_SAKE_AT_RAND_P,
+			  data->rand_p, EAP_SAKE_RAND_LEN);
+
+	if (data->peerid) {
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
+		eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
+				  data->peerid, data->peerid_len);
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
+	wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
+	wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
+	rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
+	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+				 data->serverid, data->serverid_len,
+				 data->peerid, data->peerid_len, 1,
+				 wpabuf_head(resp), wpabuf_len(resp), rpos,
+				 rpos)) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+		wpabuf_free(resp);
+		return NULL;
+	}
+
+	eap_sake_state(data, CONFIRM);
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm,
+						struct eap_sake_data *data,
+						struct eap_method_ret *ret,
+						u8 id,
+						const struct wpabuf *reqData,
+						const u8 *payload,
+						size_t payload_len)
+{
+	struct eap_sake_parse_attr attr;
+	u8 mic_s[EAP_SAKE_MIC_LEN];
+	struct wpabuf *resp;
+	u8 *rpos;
+
+	if (data->state != CONFIRM) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Confirm");
+
+	if (eap_sake_parse_attributes(payload, payload_len, &attr))
+		return NULL;
+
+	if (!attr.mic_s) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Request/Confirm did not "
+			   "include AT_MIC_S");
+		return NULL;
+	}
+
+	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+			     data->serverid, data->serverid_len,
+			     data->peerid, data->peerid_len, 0,
+			     wpabuf_head(reqData), wpabuf_len(reqData),
+			     attr.mic_s, mic_s);
+	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;
+		ret->decision = DECISION_FAIL;
+		ret->allowNotifications = FALSE;
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending "
+			   "Response/Auth-Reject");
+		return eap_sake_build_msg(data, id, 0,
+					  EAP_SAKE_SUBTYPE_AUTH_REJECT);
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm");
+
+	resp = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN,
+				  EAP_SAKE_SUBTYPE_CONFIRM);
+	if (resp == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
+	wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
+	wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
+	rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
+	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+				 data->serverid, data->serverid_len,
+				 data->peerid, data->peerid_len, 1,
+				 wpabuf_head(resp), wpabuf_len(resp), rpos,
+				 rpos)) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+		wpabuf_free(resp);
+		return NULL;
+	}
+
+	eap_sake_state(data, SUCCESS);
+	ret->methodState = METHOD_DONE;
+	ret->decision = DECISION_UNCOND_SUCC;
+	ret->allowNotifications = FALSE;
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv,
+					struct eap_method_ret *ret,
+					const struct wpabuf *reqData)
+{
+	struct eap_sake_data *data = priv;
+	const struct eap_sake_hdr *req;
+	struct wpabuf *resp;
+	const u8 *pos, *end;
+	size_t len;
+	u8 subtype, session_id, id;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len);
+	if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	req = (const struct eap_sake_hdr *) pos;
+	end = pos + len;
+	id = eap_get_id(reqData);
+	subtype = req->subtype;
+	session_id = req->session_id;
+	pos = (const u8 *) (req + 1);
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype %d "
+		   "session_id %d", subtype, session_id);
+	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
+		    pos, end - pos);
+
+	if (data->session_id_set && data->session_id != session_id) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
+			   session_id, data->session_id);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	data->session_id = session_id;
+	data->session_id_set = 1;
+
+	ret->ignore = FALSE;
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_FAIL;
+	ret->allowNotifications = TRUE;
+
+	switch (subtype) {
+	case EAP_SAKE_SUBTYPE_IDENTITY:
+		resp = eap_sake_process_identity(sm, data, ret, id,
+						 pos, end - pos);
+		break;
+	case EAP_SAKE_SUBTYPE_CHALLENGE:
+		resp = eap_sake_process_challenge(sm, data, ret, id,
+						  pos, end - pos);
+		break;
+	case EAP_SAKE_SUBTYPE_CONFIRM:
+		resp = eap_sake_process_confirm(sm, data, ret, id, reqData,
+						pos, end - pos);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring message with "
+			   "unknown subtype %d", subtype);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (ret->methodState == METHOD_DONE)
+		ret->allowNotifications = FALSE;
+
+	return resp;
+}
+
+
+static Boolean eap_sake_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_sake_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_sake_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_MSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->msk, EAP_MSK_LEN);
+	*len = EAP_MSK_LEN;
+
+	return key;
+}
+
+
+static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_sake_data *data = priv;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	*len = 1 + 2 * EAP_SAKE_RAND_LEN;
+	id = os_malloc(*len);
+	if (id == NULL)
+		return NULL;
+
+	id[0] = EAP_TYPE_SAKE;
+	os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN);
+	os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len);
+
+	return id;
+}
+
+
+static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_sake_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+	*len = EAP_EMSK_LEN;
+
+	return key;
+}
+
+
+int eap_peer_sake_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_sake_init;
+	eap->deinit = eap_sake_deinit;
+	eap->process = eap_sake_process;
+	eap->isKeyAvailable = eap_sake_isKeyAvailable;
+	eap->getKey = eap_sake_getKey;
+	eap->getSessionId = eap_sake_get_session_id;
+	eap->get_emsk = eap_sake_get_emsk;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_sim.c b/hostap/src/eap_peer/eap_sim.c
new file mode 100644
index 0000000..99a2816
--- /dev/null
+++ b/hostap/src/eap_peer/eap_sim.c
@@ -0,0 +1,1261 @@
+/*
+ * EAP peer method: EAP-SIM (RFC 4186)
+ * Copyright (c) 2004-2012, 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 "pcsc_funcs.h"
+#include "crypto/milenage.h"
+#include "crypto/random.h"
+#include "eap_peer/eap_i.h"
+#include "eap_config.h"
+#include "eap_common/eap_sim_common.h"
+
+
+struct eap_sim_data {
+	u8 *ver_list;
+	size_t ver_list_len;
+	int selected_version;
+	size_t min_num_chal, num_chal;
+
+	u8 kc[3][EAP_SIM_KC_LEN];
+	u8 sres[3][EAP_SIM_SRES_LEN];
+	u8 nonce_mt[EAP_SIM_NONCE_MT_LEN], nonce_s[EAP_SIM_NONCE_S_LEN];
+	u8 mk[EAP_SIM_MK_LEN];
+	u8 k_aut[EAP_SIM_K_AUT_LEN];
+	u8 k_encr[EAP_SIM_K_ENCR_LEN];
+	u8 msk[EAP_SIM_KEYING_DATA_LEN];
+	u8 emsk[EAP_EMSK_LEN];
+	u8 rand[3][GSM_RAND_LEN];
+
+	int num_id_req, num_notification;
+	u8 *pseudonym;
+	size_t pseudonym_len;
+	u8 *reauth_id;
+	size_t reauth_id_len;
+	int reauth;
+	unsigned int counter, counter_too_small;
+	u8 *last_eap_identity;
+	size_t last_eap_identity_len;
+	enum {
+		CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
+	} state;
+	int result_ind, use_result_ind;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_sim_state_txt(int state)
+{
+	switch (state) {
+	case CONTINUE:
+		return "CONTINUE";
+	case RESULT_SUCCESS:
+		return "RESULT_SUCCESS";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "?";
+	}
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_sim_state(struct eap_sim_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s",
+		   eap_sim_state_txt(data->state),
+		   eap_sim_state_txt(state));
+	data->state = state;
+}
+
+
+static void * eap_sim_init(struct eap_sm *sm)
+{
+	struct eap_sim_data *data;
+	struct eap_peer_config *config = eap_get_config(sm);
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
+			   "for NONCE_MT");
+		os_free(data);
+		return NULL;
+	}
+
+	data->min_num_chal = 2;
+	if (config && config->phase1) {
+		char *pos = os_strstr(config->phase1, "sim_min_num_chal=");
+		if (pos) {
+			data->min_num_chal = atoi(pos + 17);
+			if (data->min_num_chal < 2 || data->min_num_chal > 3) {
+				wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
+					   "sim_min_num_chal configuration "
+					   "(%lu, expected 2 or 3)",
+					   (unsigned long) data->min_num_chal);
+				os_free(data);
+				return NULL;
+			}
+			wpa_printf(MSG_DEBUG, "EAP-SIM: Set minimum number of "
+				   "challenges to %lu",
+				   (unsigned long) data->min_num_chal);
+		}
+
+		data->result_ind = os_strstr(config->phase1, "result_ind=1") !=
+			NULL;
+	}
+
+	if (config && config->anonymous_identity) {
+		data->pseudonym = os_malloc(config->anonymous_identity_len);
+		if (data->pseudonym) {
+			os_memcpy(data->pseudonym, config->anonymous_identity,
+				  config->anonymous_identity_len);
+			data->pseudonym_len = config->anonymous_identity_len;
+		}
+	}
+
+	eap_sim_state(data, CONTINUE);
+
+	return data;
+}
+
+
+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;
+	if (data) {
+		os_free(data->ver_list);
+		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;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication algorithm");
+
+	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]) ||
+		    scard_gsm_auth(sm->scard_ctx, data->rand[1],
+				   data->sres[1], data->kc[1]) ||
+		    (data->num_chal > 2 &&
+		     scard_gsm_auth(sm->scard_ctx, data->rand[2],
+				    data->sres[2], data->kc[2]))) {
+			wpa_printf(MSG_DEBUG, "EAP-SIM: GSM SIM "
+				   "authentication could not be completed");
+			return -1;
+		}
+		return 0;
+	}
+
+#ifdef CONFIG_SIM_SIMULATOR
+	if (conf->password) {
+		u8 opc[16], k[16];
+		const char *pos;
+		size_t i;
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Use internal GSM-Milenage "
+			   "implementation for authentication");
+		if (conf->password_len < 65) {
+			wpa_printf(MSG_DEBUG, "EAP-SIM: invalid GSM-Milenage "
+				   "password");
+			return -1;
+		}
+		pos = (const char *) conf->password;
+		if (hexstr2bin(pos, k, 16))
+			return -1;
+		pos += 32;
+		if (*pos != ':')
+			return -1;
+		pos++;
+
+		if (hexstr2bin(pos, opc, 16))
+			return -1;
+
+		for (i = 0; i < data->num_chal; i++) {
+			if (gsm_milenage(opc, k, data->rand[i],
+					 data->sres[i], data->kc[i])) {
+				wpa_printf(MSG_DEBUG, "EAP-SIM: "
+					   "GSM-Milenage authentication "
+					   "could not be completed");
+				return -1;
+			}
+			wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND",
+				    data->rand[i], GSM_RAND_LEN);
+			wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES",
+					data->sres[i], EAP_SIM_SRES_LEN);
+			wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc",
+					data->kc[i], EAP_SIM_KC_LEN);
+		}
+		return 0;
+	}
+#endif /* CONFIG_SIM_SIMULATOR */
+
+#ifdef CONFIG_SIM_HARDCODED
+	/* These hardcoded Kc and SRES values are used for testing. RAND to
+	 * KC/SREC mapping is very bogus as far as real authentication is
+	 * concerned, but it is quite useful for cases where the AS is rotating
+	 * the order of pre-configured values. */
+	{
+		size_t i;
+
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Use hardcoded Kc and SRES "
+			   "values for testing");
+
+		for (i = 0; i < data->num_chal; i++) {
+			if (data->rand[i][0] == 0xaa) {
+				os_memcpy(data->kc[i],
+					  "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7",
+					  EAP_SIM_KC_LEN);
+				os_memcpy(data->sres[i], "\xd1\xd2\xd3\xd4",
+					  EAP_SIM_SRES_LEN);
+			} else if (data->rand[i][0] == 0xbb) {
+				os_memcpy(data->kc[i],
+					  "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7",
+					  EAP_SIM_KC_LEN);
+				os_memcpy(data->sres[i], "\xe1\xe2\xe3\xe4",
+					  EAP_SIM_SRES_LEN);
+			} else {
+				os_memcpy(data->kc[i],
+					  "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7",
+					  EAP_SIM_KC_LEN);
+				os_memcpy(data->sres[i], "\xf1\xf2\xf3\xf4",
+					  EAP_SIM_SRES_LEN);
+			}
+		}
+	}
+
+	return 0;
+
+#else /* CONFIG_SIM_HARDCODED */
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: No GSM authentication algorithm "
+		   "enabled");
+	return -1;
+
+#endif /* CONFIG_SIM_HARDCODED */
+}
+
+
+static int eap_sim_supported_ver(int version)
+{
+	return version == EAP_SIM_VERSION;
+}
+
+
+#define CLEAR_PSEUDONYM	0x01
+#define CLEAR_REAUTH_ID	0x02
+#define CLEAR_EAP_ID	0x04
+
+static void eap_sim_clear_identities(struct eap_sm *sm,
+				     struct eap_sim_data *data, int id)
+{
+	if ((id & CLEAR_PSEUDONYM) && data->pseudonym) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old pseudonym");
+		os_free(data->pseudonym);
+		data->pseudonym = NULL;
+		data->pseudonym_len = 0;
+		eap_set_anon_id(sm, NULL, 0);
+	}
+	if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id");
+		os_free(data->reauth_id);
+		data->reauth_id = NULL;
+		data->reauth_id_len = 0;
+	}
+	if ((id & CLEAR_EAP_ID) && data->last_eap_identity) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old eap_id");
+		os_free(data->last_eap_identity);
+		data->last_eap_identity = NULL;
+		data->last_eap_identity_len = 0;
+	}
+}
+
+
+static int eap_sim_learn_ids(struct eap_sm *sm, struct eap_sim_data *data,
+			     struct eap_sim_attrs *attr)
+{
+	if (attr->next_pseudonym) {
+		const u8 *identity = NULL;
+		size_t identity_len = 0;
+		const u8 *realm = NULL;
+		size_t realm_len = 0;
+
+		wpa_hexdump_ascii(MSG_DEBUG,
+				  "EAP-SIM: (encr) AT_NEXT_PSEUDONYM",
+				  attr->next_pseudonym,
+				  attr->next_pseudonym_len);
+		os_free(data->pseudonym);
+		/* Look for the realm of the permanent identity */
+		identity = eap_get_config_identity(sm, &identity_len);
+		if (identity) {
+			for (realm = identity, realm_len = identity_len;
+			     realm_len > 0; realm_len--, realm++) {
+				if (*realm == '@')
+					break;
+			}
+		}
+		data->pseudonym = os_malloc(attr->next_pseudonym_len +
+					    realm_len);
+		if (data->pseudonym == NULL) {
+			wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
+				   "next pseudonym");
+			data->pseudonym_len = 0;
+			return -1;
+		}
+		os_memcpy(data->pseudonym, attr->next_pseudonym,
+			  attr->next_pseudonym_len);
+		if (realm_len) {
+			os_memcpy(data->pseudonym + attr->next_pseudonym_len,
+				  realm, realm_len);
+		}
+		data->pseudonym_len = attr->next_pseudonym_len + realm_len;
+		eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len);
+	}
+
+	if (attr->next_reauth_id) {
+		os_free(data->reauth_id);
+		data->reauth_id = os_malloc(attr->next_reauth_id_len);
+		if (data->reauth_id == NULL) {
+			wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
+				   "next reauth_id");
+			data->reauth_id_len = 0;
+			return -1;
+		}
+		os_memcpy(data->reauth_id, attr->next_reauth_id,
+			  attr->next_reauth_id_len);
+		data->reauth_id_len = attr->next_reauth_id_len;
+		wpa_hexdump_ascii(MSG_DEBUG,
+				  "EAP-SIM: (encr) AT_NEXT_REAUTH_ID",
+				  data->reauth_id,
+				  data->reauth_id_len);
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id,
+					    int err)
+{
+	struct eap_sim_msg *msg;
+
+	eap_sim_state(data, FAILURE);
+	data->num_id_req = 0;
+	data->num_notification = 0;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Send Client-Error (error code %d)",
+		   err);
+	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, EAP_TYPE_SIM, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_sim_response_start(struct eap_sm *sm,
+					      struct eap_sim_data *data, u8 id,
+					      enum eap_sim_id_req id_req)
+{
+	const u8 *identity = NULL;
+	size_t identity_len = 0;
+	struct eap_sim_msg *msg;
+
+	data->reauth = 0;
+	if (id_req == ANY_ID && data->reauth_id) {
+		identity = data->reauth_id;
+		identity_len = data->reauth_id_len;
+		data->reauth = 1;
+	} else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) &&
+		   data->pseudonym) {
+		identity = data->pseudonym;
+		identity_len = data->pseudonym_len;
+		eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID);
+	} else if (id_req != NO_ID_REQ) {
+		identity = eap_get_config_identity(sm, &identity_len);
+		if (identity) {
+			eap_sim_clear_identities(sm, data, CLEAR_PSEUDONYM |
+						 CLEAR_REAUTH_ID);
+		}
+	}
+	if (id_req != NO_ID_REQ)
+		eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
+
+	wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id);
+	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
+			       EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START);
+	if (!data->reauth) {
+		wpa_hexdump(MSG_DEBUG, "   AT_NONCE_MT",
+			    data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+		eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_MT, 0,
+				data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+		wpa_printf(MSG_DEBUG, "   AT_SELECTED_VERSION %d",
+			   data->selected_version);
+		eap_sim_msg_add(msg, EAP_SIM_AT_SELECTED_VERSION,
+				data->selected_version, NULL, 0);
+	}
+
+	if (identity) {
+		wpa_hexdump_ascii(MSG_DEBUG, "   AT_IDENTITY",
+				  identity, identity_len);
+		eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
+				identity, identity_len);
+	}
+
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data,
+						  u8 id)
+{
+	struct eap_sim_msg *msg;
+
+	wpa_printf(MSG_DEBUG, "Generating EAP-SIM Challenge (id=%d)", id);
+	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
+			       EAP_SIM_SUBTYPE_CHALLENGE);
+	if (data->use_result_ind) {
+		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+	}
+	wpa_printf(MSG_DEBUG, "   AT_MAC");
+	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut,
+				  (u8 *) data->sres,
+				  data->num_chal * EAP_SIM_SRES_LEN);
+}
+
+
+static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data,
+					       u8 id, int counter_too_small,
+					       const u8 *nonce_s)
+{
+	struct eap_sim_msg *msg;
+	unsigned int counter;
+
+	wpa_printf(MSG_DEBUG, "Generating EAP-SIM Reauthentication (id=%d)",
+		   id);
+	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
+			       EAP_SIM_SUBTYPE_REAUTHENTICATION);
+	wpa_printf(MSG_DEBUG, "   AT_IV");
+	wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+	eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+	if (counter_too_small) {
+		wpa_printf(MSG_DEBUG, "   *AT_COUNTER_TOO_SMALL");
+		eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0);
+		counter = data->counter_too_small;
+	} else
+		counter = data->counter;
+
+	wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", counter);
+	eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+
+	if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
+			   "AT_ENCR_DATA");
+		eap_sim_msg_free(msg);
+		return NULL;
+	}
+	if (data->use_result_ind) {
+		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+	}
+	wpa_printf(MSG_DEBUG, "   AT_MAC");
+	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, nonce_s,
+				  EAP_SIM_NONCE_S_LEN);
+}
+
+
+static struct wpabuf * eap_sim_response_notification(struct eap_sim_data *data,
+						     u8 id, u16 notification)
+{
+	struct eap_sim_msg *msg;
+	u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL;
+
+	wpa_printf(MSG_DEBUG, "Generating EAP-SIM Notification (id=%d)", id);
+	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
+			       EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION);
+	if (k_aut && data->reauth) {
+		wpa_printf(MSG_DEBUG, "   AT_IV");
+		wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+		eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
+					   EAP_SIM_AT_ENCR_DATA);
+		wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", data->counter);
+		eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
+				NULL, 0);
+		if (eap_sim_msg_add_encr_end(msg, data->k_encr,
+					     EAP_SIM_AT_PADDING)) {
+			wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
+				   "AT_ENCR_DATA");
+			eap_sim_msg_free(msg);
+			return NULL;
+		}
+	}
+	if (k_aut) {
+		wpa_printf(MSG_DEBUG, "   AT_MAC");
+		eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+	}
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, k_aut, (u8 *) "", 0);
+}
+
+
+static struct wpabuf * eap_sim_process_start(struct eap_sm *sm,
+					     struct eap_sim_data *data, u8 id,
+					     struct eap_sim_attrs *attr)
+{
+	int selected_version = -1, id_error;
+	size_t i;
+	u8 *pos;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Start");
+	if (attr->version_list == NULL) {
+		wpa_printf(MSG_INFO, "EAP-SIM: No AT_VERSION_LIST in "
+			   "SIM/Start");
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNSUPPORTED_VERSION);
+	}
+
+	os_free(data->ver_list);
+	data->ver_list = os_malloc(attr->version_list_len);
+	if (data->ver_list == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate "
+			   "memory for version list");
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
+	os_memcpy(data->ver_list, attr->version_list, attr->version_list_len);
+	data->ver_list_len = attr->version_list_len;
+	pos = data->ver_list;
+	for (i = 0; i < data->ver_list_len / 2; i++) {
+		int ver = pos[0] * 256 + pos[1];
+		pos += 2;
+		if (eap_sim_supported_ver(ver)) {
+			selected_version = ver;
+			break;
+		}
+	}
+	if (selected_version < 0) {
+		wpa_printf(MSG_INFO, "EAP-SIM: Could not find a supported "
+			   "version");
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNSUPPORTED_VERSION);
+	}
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Selected Version %d",
+		   selected_version);
+	data->selected_version = selected_version;
+
+	id_error = 0;
+	switch (attr->id_req) {
+	case NO_ID_REQ:
+		break;
+	case ANY_ID:
+		if (data->num_id_req > 0)
+			id_error++;
+		data->num_id_req++;
+		break;
+	case FULLAUTH_ID:
+		if (data->num_id_req > 1)
+			id_error++;
+		data->num_id_req++;
+		break;
+	case PERMANENT_ID:
+		if (data->num_id_req > 2)
+			id_error++;
+		data->num_id_req++;
+		break;
+	}
+	if (id_error) {
+		wpa_printf(MSG_INFO, "EAP-SIM: Too many ID requests "
+			   "used within one authentication");
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	return eap_sim_response_start(sm, data, id, attr->id_req);
+}
+
+
+static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm,
+						 struct eap_sim_data *data,
+						 u8 id,
+						 const struct wpabuf *reqData,
+						 struct eap_sim_attrs *attr)
+{
+	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;
+	if (!attr->mac || !attr->rand) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
+			   "did not include%s%s",
+			   !attr->mac ? " AT_MAC" : "",
+			   !attr->rand ? " AT_RAND" : "");
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: %lu challenges",
+		   (unsigned long) attr->num_chal);
+	if (attr->num_chal < data->min_num_chal) {
+		wpa_printf(MSG_INFO, "EAP-SIM: Insufficient number of "
+			   "challenges (%lu)", (unsigned long) attr->num_chal);
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_INSUFFICIENT_NUM_OF_CHAL);
+	}
+	if (attr->num_chal > 3) {
+		wpa_printf(MSG_INFO, "EAP-SIM: Too many challenges "
+			   "(%lu)", (unsigned long) attr->num_chal);
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	/* Verify that RANDs are different */
+	if (os_memcmp(attr->rand, attr->rand + GSM_RAND_LEN,
+		   GSM_RAND_LEN) == 0 ||
+	    (attr->num_chal > 2 &&
+	     (os_memcmp(attr->rand, attr->rand + 2 * GSM_RAND_LEN,
+			GSM_RAND_LEN) == 0 ||
+	      os_memcmp(attr->rand + GSM_RAND_LEN,
+			attr->rand + 2 * GSM_RAND_LEN,
+			GSM_RAND_LEN) == 0))) {
+		wpa_printf(MSG_INFO, "EAP-SIM: Same RAND used multiple times");
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_RAND_NOT_FRESH);
+	}
+
+	os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN);
+	data->num_chal = attr->num_chal;
+
+	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);
+	}
+	if (data->last_eap_identity) {
+		identity = data->last_eap_identity;
+		identity_len = data->last_eap_identity_len;
+	} else if (data->pseudonym) {
+		identity = data->pseudonym;
+		identity_len = data->pseudonym_len;
+	} else
+		identity = eap_get_config_identity(sm, &identity_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK "
+			  "derivation", identity, identity_len);
+	eap_sim_derive_mk(identity, identity_len, data->nonce_mt,
+			  data->selected_version, data->ver_list,
+			  data->ver_list_len, data->num_chal,
+			  (const u8 *) data->kc, data->mk);
+	eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
+			    data->emsk);
+	if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, data->nonce_mt,
+			       EAP_SIM_NONCE_MT_LEN)) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
+			   "used invalid AT_MAC");
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	/* Old reauthentication identity must not be used anymore. In
+	 * other words, if no new reauth identity is received, full
+	 * authentication will be used on next reauthentication (using
+	 * pseudonym identity or permanent identity). */
+	eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+
+	if (attr->encr_data) {
+		u8 *decrypted;
+		decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+					       attr->encr_data_len, attr->iv,
+					       &eattr, 0);
+		if (decrypted == NULL) {
+			return eap_sim_client_error(
+				data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+		}
+		eap_sim_learn_ids(sm, data, &eattr);
+		os_free(decrypted);
+	}
+
+	if (data->result_ind && attr->result_ind)
+		data->use_result_ind = 1;
+
+	if (data->state != FAILURE) {
+		eap_sim_state(data, data->use_result_ind ?
+			      RESULT_SUCCESS : SUCCESS);
+	}
+
+	data->num_id_req = 0;
+	data->num_notification = 0;
+	/* RFC 4186 specifies that counter is initialized to one after
+	 * fullauth, but initializing it to zero makes it easier to implement
+	 * reauth verification. */
+	data->counter = 0;
+	return eap_sim_response_challenge(data, id);
+}
+
+
+static int eap_sim_process_notification_reauth(struct eap_sim_data *data,
+					       struct eap_sim_attrs *attr)
+{
+	struct eap_sim_attrs eattr;
+	u8 *decrypted;
+
+	if (attr->encr_data == NULL || attr->iv == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Notification message after "
+			   "reauth did not include encrypted data");
+		return -1;
+	}
+
+	decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+				       attr->encr_data_len, attr->iv, &eattr,
+				       0);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
+			   "data from notification message");
+		return -1;
+	}
+
+	if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Counter in notification "
+			   "message does not match with counter in reauth "
+			   "message");
+		os_free(decrypted);
+		return -1;
+	}
+
+	os_free(decrypted);
+	return 0;
+}
+
+
+static int eap_sim_process_notification_auth(struct eap_sim_data *data,
+					     const struct wpabuf *reqData,
+					     struct eap_sim_attrs *attr)
+{
+	if (attr->mac == NULL) {
+		wpa_printf(MSG_INFO, "EAP-SIM: no AT_MAC in after_auth "
+			   "Notification message");
+		return -1;
+	}
+
+	if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0))
+	{
+		wpa_printf(MSG_WARNING, "EAP-SIM: Notification message "
+			   "used invalid AT_MAC");
+		return -1;
+	}
+
+	if (data->reauth &&
+	    eap_sim_process_notification_reauth(data, attr)) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Invalid notification "
+			   "message after reauth");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_sim_process_notification(
+	struct eap_sm *sm, struct eap_sim_data *data, u8 id,
+	const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+	wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Notification");
+	if (data->num_notification > 0) {
+		wpa_printf(MSG_INFO, "EAP-SIM: too many notification "
+			   "rounds (only one allowed)");
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
+	data->num_notification++;
+	if (attr->notification == -1) {
+		wpa_printf(MSG_INFO, "EAP-SIM: no AT_NOTIFICATION in "
+			   "Notification message");
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	if ((attr->notification & 0x4000) == 0 &&
+	    eap_sim_process_notification_auth(data, reqData, attr)) {
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	eap_sim_report_notification(sm->msg_ctx, attr->notification, 0);
+	if (attr->notification >= 0 && attr->notification < 32768) {
+		eap_sim_state(data, FAILURE);
+	} else if (attr->notification == EAP_SIM_SUCCESS &&
+		   data->state == RESULT_SUCCESS)
+		eap_sim_state(data, SUCCESS);
+	return eap_sim_response_notification(data, id, attr->notification);
+}
+
+
+static struct wpabuf * eap_sim_process_reauthentication(
+	struct eap_sm *sm, struct eap_sim_data *data, u8 id,
+	const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+	struct eap_sim_attrs eattr;
+	u8 *decrypted;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Reauthentication");
+
+	if (data->reauth_id == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Server is trying "
+			   "reauthentication, but no reauth_id available");
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	data->reauth = 1;
+	if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0))
+	{
+		wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
+			   "did not have valid AT_MAC");
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	if (attr->encr_data == NULL || attr->iv == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
+			   "message did not include encrypted data");
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+				       attr->encr_data_len, attr->iv, &eattr,
+				       0);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
+			   "data from reauthentication message");
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	if (eattr.nonce_s == NULL || eattr.counter < 0) {
+		wpa_printf(MSG_INFO, "EAP-SIM: (encr) No%s%s in reauth packet",
+			   !eattr.nonce_s ? " AT_NONCE_S" : "",
+			   eattr.counter < 0 ? " AT_COUNTER" : "");
+		os_free(decrypted);
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
+
+	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
+		 * packet, it has to saved for the following fullauth to be
+		 * used in MK derivation. */
+		os_free(data->last_eap_identity);
+		data->last_eap_identity = data->reauth_id;
+		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 res;
+	}
+	data->counter = eattr.counter;
+
+	os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-SIM: (encr) AT_NONCE_S",
+		    data->nonce_s, EAP_SIM_NONCE_S_LEN);
+
+	eap_sim_derive_keys_reauth(data->counter,
+				   data->reauth_id, data->reauth_id_len,
+				   data->nonce_s, data->mk, data->msk,
+				   data->emsk);
+	eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+	eap_sim_learn_ids(sm, data, &eattr);
+
+	if (data->result_ind && attr->result_ind)
+		data->use_result_ind = 1;
+
+	if (data->state != FAILURE) {
+		eap_sim_state(data, data->use_result_ind ?
+			      RESULT_SUCCESS : SUCCESS);
+	}
+
+	data->num_id_req = 0;
+	data->num_notification = 0;
+	if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of "
+			   "fast reauths performed - force fullauth");
+		eap_sim_clear_identities(sm, data,
+					 CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+	}
+	os_free(decrypted);
+	return eap_sim_response_reauth(data, id, 0, data->nonce_s);
+}
+
+
+static struct wpabuf * eap_sim_process(struct eap_sm *sm, void *priv,
+				       struct eap_method_ret *ret,
+				       const struct wpabuf *reqData)
+{
+	struct eap_sim_data *data = priv;
+	const struct eap_hdr *req;
+	u8 subtype, id;
+	struct wpabuf *res;
+	const u8 *pos;
+	struct eap_sim_attrs attr;
+	size_t len;
+
+	wpa_hexdump_buf(MSG_DEBUG, "EAP-SIM: EAP data", reqData);
+	if (eap_get_config_identity(sm, &len) == NULL) {
+		wpa_printf(MSG_INFO, "EAP-SIM: Identity not configured");
+		eap_sm_request_identity(sm);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, reqData, &len);
+	if (pos == NULL || len < 3) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	req = wpabuf_head(reqData);
+	id = req->identifier;
+	len = be_to_host16(req->length);
+
+	ret->ignore = FALSE;
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_FAIL;
+	ret->allowNotifications = TRUE;
+
+	subtype = *pos++;
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Subtype=%d", subtype);
+	pos += 2; /* Reserved */
+
+	if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 0,
+			       0)) {
+		res = eap_sim_client_error(data, id,
+					   EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+		goto done;
+	}
+
+	switch (subtype) {
+	case EAP_SIM_SUBTYPE_START:
+		res = eap_sim_process_start(sm, data, id, &attr);
+		break;
+	case EAP_SIM_SUBTYPE_CHALLENGE:
+		res = eap_sim_process_challenge(sm, data, id, reqData, &attr);
+		break;
+	case EAP_SIM_SUBTYPE_NOTIFICATION:
+		res = eap_sim_process_notification(sm, data, id, reqData,
+						   &attr);
+		break;
+	case EAP_SIM_SUBTYPE_REAUTHENTICATION:
+		res = eap_sim_process_reauthentication(sm, data, id, reqData,
+						       &attr);
+		break;
+	case EAP_SIM_SUBTYPE_CLIENT_ERROR:
+		wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Client-Error");
+		res = eap_sim_client_error(data, id,
+					   EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown subtype=%d", subtype);
+		res = eap_sim_client_error(data, id,
+					   EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+		break;
+	}
+
+done:
+	if (data->state == FAILURE) {
+		ret->decision = DECISION_FAIL;
+		ret->methodState = METHOD_DONE;
+	} else if (data->state == SUCCESS) {
+		ret->decision = data->use_result_ind ?
+			DECISION_UNCOND_SUCC : DECISION_COND_SUCC;
+		ret->methodState = data->use_result_ind ?
+			METHOD_DONE : METHOD_MAY_CONT;
+	} else if (data->state == RESULT_SUCCESS)
+		ret->methodState = METHOD_CONT;
+
+	if (ret->methodState == METHOD_DONE) {
+		ret->allowNotifications = FALSE;
+	}
+
+	return res;
+}
+
+
+static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+	struct eap_sim_data *data = priv;
+	return data->pseudonym || data->reauth_id;
+}
+
+
+static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+	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);
+}
+
+
+static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+	struct eap_sim_data *data = priv;
+	if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
+			   "for NONCE_MT");
+		os_free(data);
+		return NULL;
+	}
+	data->num_id_req = 0;
+	data->num_notification = 0;
+	eap_sim_state(data, CONTINUE);
+	return priv;
+}
+
+
+static const u8 * eap_sim_get_identity(struct eap_sm *sm, void *priv,
+				       size_t *len)
+{
+	struct eap_sim_data *data = priv;
+
+	if (data->reauth_id) {
+		*len = data->reauth_id_len;
+		return data->reauth_id;
+	}
+
+	if (data->pseudonym) {
+		*len = data->pseudonym_len;
+		return data->pseudonym;
+	}
+
+	return NULL;
+}
+
+
+static Boolean eap_sim_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_sim_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_sim_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_SIM_KEYING_DATA_LEN;
+	os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+
+	return key;
+}
+
+
+static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_sim_data *data = priv;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	*len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN;
+	id = os_malloc(*len);
+	if (id == NULL)
+		return NULL;
+
+	id[0] = EAP_TYPE_SIM;
+	os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN);
+	os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt,
+		  EAP_SIM_NONCE_MT_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len);
+
+	return id;
+}
+
+
+static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_sim_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_EMSK_LEN;
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+	return key;
+}
+
+
+int eap_peer_sim_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_sim_init;
+	eap->deinit = eap_sim_deinit;
+	eap->process = eap_sim_process;
+	eap->isKeyAvailable = eap_sim_isKeyAvailable;
+	eap->getKey = eap_sim_getKey;
+	eap->getSessionId = eap_sim_get_session_id;
+	eap->has_reauth_data = eap_sim_has_reauth_data;
+	eap->deinit_for_reauth = eap_sim_deinit_for_reauth;
+	eap->init_for_reauth = eap_sim_init_for_reauth;
+	eap->get_identity = eap_sim_get_identity;
+	eap->get_emsk = eap_sim_get_emsk;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_tls.c b/hostap/src/eap_peer/eap_tls.c
new file mode 100644
index 0000000..66a027a
--- /dev/null
+++ b/hostap/src/eap_peer/eap_tls.c
@@ -0,0 +1,439 @@
+/*
+ * EAP peer method: EAP-TLS (RFC 2716)
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/tls.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+
+
+static void eap_tls_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_tls_data {
+	struct eap_ssl_data ssl;
+	u8 *key_data;
+	u8 *session_id;
+	size_t id_len;
+	void *ssl_ctx;
+	u8 eap_type;
+};
+
+
+static void * eap_tls_init(struct eap_sm *sm)
+{
+	struct eap_tls_data *data;
+	struct eap_peer_config *config = eap_get_config(sm);
+	if (config == NULL ||
+	    ((sm->init_phase2 ? config->private_key2 : config->private_key)
+	     == NULL &&
+	     (sm->init_phase2 ? config->engine2 : config->engine) == 0)) {
+		wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured");
+		return NULL;
+	}
+
+	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_TYPE_TLS)) {
+		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+		eap_tls_deinit(sm, data);
+		if (config->engine) {
+			wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting Smartcard "
+				   "PIN");
+			eap_sm_request_pin(sm);
+			sm->ignore = TRUE;
+		} else if (config->private_key && !config->private_key_passwd)
+		{
+			wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private "
+				   "key passphrase");
+			eap_sm_request_passphrase(sm);
+			sm->ignore = TRUE;
+		}
+		return NULL;
+	}
+
+	data->eap_type = EAP_TYPE_TLS;
+
+	return data;
+}
+
+
+#ifdef EAP_UNAUTH_TLS
+static void * eap_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_UNAUTH_TLS_TYPE)) {
+		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+		eap_tls_deinit(sm, data);
+		return NULL;
+	}
+
+	data->eap_type = EAP_UNAUTH_TLS_TYPE;
+
+	return data;
+}
+#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);
+	eap_tls_free_key(data);
+	os_free(data->session_id);
+	os_free(data);
+}
+
+
+static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
+				       struct eap_tls_data *data,
+				       struct eap_method_ret *ret, int res,
+				       struct wpabuf *resp, u8 id)
+{
+	wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed");
+
+	ret->methodState = METHOD_DONE;
+	ret->decision = DECISION_FAIL;
+
+	if (resp) {
+		/*
+		 * This is likely an alert message, so send it instead of just
+		 * ACKing the error.
+		 */
+		return resp;
+	}
+
+	return eap_peer_tls_build_ack(id, data->eap_type, 0);
+}
+
+
+static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data,
+			    struct eap_method_ret *ret)
+{
+	wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
+
+	ret->methodState = METHOD_DONE;
+	ret->decision = DECISION_UNCOND_SUCC;
+
+	eap_tls_free_key(data);
+	data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
+						 "client EAP encryption",
+						 EAP_TLS_KEY_LEN +
+						 EAP_EMSK_LEN);
+	if (data->key_data) {
+		wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key",
+				data->key_data, EAP_TLS_KEY_LEN);
+		wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived EMSK",
+				data->key_data + EAP_TLS_KEY_LEN,
+				EAP_EMSK_LEN);
+	} else {
+		wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key");
+	}
+
+	os_free(data->session_id);
+	data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,
+							  EAP_TYPE_TLS,
+			                                  &data->id_len);
+	if (data->session_id) {
+		wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived Session-Id",
+			    data->session_id, data->id_len);
+	} else {
+		wpa_printf(MSG_ERROR, "EAP-TLS: Failed to derive Session-Id");
+	}
+}
+
+
+static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv,
+				       struct eap_method_ret *ret,
+				       const struct wpabuf *reqData)
+{
+	size_t left;
+	int res;
+	struct wpabuf *resp;
+	u8 flags, id;
+	const u8 *pos;
+	struct eap_tls_data *data = priv;
+	struct wpabuf msg;
+
+	pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret,
+					reqData, &left, &flags);
+	if (pos == NULL)
+		return NULL;
+	id = eap_get_id(reqData);
+
+	if (flags & EAP_TLS_FLAGS_START) {
+		wpa_printf(MSG_DEBUG, "EAP-TLS: Start");
+		left = 0; /* make sure that this frame is empty, even though it
+			   * should always be, anyway */
+	}
+
+	resp = NULL;
+	wpabuf_set(&msg, pos, left);
+	res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0,
+					  id, &msg, &resp);
+
+	if (res < 0) {
+		return eap_tls_failure(sm, data, ret, res, resp, id);
+	}
+
+	if (tls_connection_established(data->ssl_ctx, data->ssl.conn))
+		eap_tls_success(sm, data, ret);
+
+	if (res == 1) {
+		wpabuf_free(resp);
+		return eap_peer_tls_build_ack(id, data->eap_type, 0);
+	}
+
+	return resp;
+}
+
+
+static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+	struct eap_tls_data *data = priv;
+	return tls_connection_established(data->ssl_ctx, data->ssl.conn);
+}
+
+
+static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+	struct eap_tls_data *data = priv;
+	eap_tls_free_key(data);
+	os_free(data->session_id);
+	data->session_id = NULL;
+	if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+		os_free(data);
+		return NULL;
+	}
+	return priv;
+}
+
+
+static int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf,
+			      size_t buflen, int verbose)
+{
+	struct eap_tls_data *data = priv;
+	return eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+}
+
+
+static Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_tls_data *data = priv;
+	return data->key_data != NULL;
+}
+
+
+static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_tls_data *data = priv;
+	u8 *key;
+
+	if (data->key_data == NULL)
+		return NULL;
+
+	key = os_malloc(EAP_TLS_KEY_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_TLS_KEY_LEN;
+	os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+	return key;
+}
+
+
+static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_tls_data *data = priv;
+	u8 *key;
+
+	if (data->key_data == NULL)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_EMSK_LEN;
+	os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
+
+	return key;
+}
+
+
+static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_tls_data *data = priv;
+	u8 *id;
+
+	if (data->session_id == NULL)
+		return NULL;
+
+	id = os_malloc(data->id_len);
+	if (id == NULL)
+		return NULL;
+
+	*len = data->id_len;
+	os_memcpy(id, data->session_id, data->id_len);
+
+	return id;
+}
+
+
+int eap_peer_tls_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_tls_init;
+	eap->deinit = eap_tls_deinit;
+	eap->process = eap_tls_process;
+	eap->isKeyAvailable = eap_tls_isKeyAvailable;
+	eap->getKey = eap_tls_getKey;
+	eap->getSessionId = eap_tls_get_session_id;
+	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;
+}
+
+
+#ifdef EAP_UNAUTH_TLS
+int eap_peer_unauth_tls_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_UNAUTH_TLS,
+				    EAP_VENDOR_TYPE_UNAUTH_TLS, "UNAUTH-TLS");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_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 /* 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/hostap/src/eap_peer/eap_tls_common.c b/hostap/src/eap_peer/eap_tls_common.c
new file mode 100644
index 0000000..af2b754
--- /dev/null
+++ b/hostap/src/eap_peer/eap_tls_common.c
@@ -0,0 +1,1108 @@
+/*
+ * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+
+
+static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
+					 u8 code, u8 identifier)
+{
+	if (type == EAP_UNAUTH_TLS_TYPE)
+		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);
+}
+
+
+static int eap_tls_check_blob(struct eap_sm *sm, const char **name,
+			      const u8 **data, size_t *data_len)
+{
+	const struct wpa_config_blob *blob;
+
+	if (*name == NULL || os_strncmp(*name, "blob://", 7) != 0)
+		return 0;
+
+	blob = eap_get_config_blob(sm, *name + 7);
+	if (blob == NULL) {
+		wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not "
+			   "found", __func__, *name + 7);
+		return -1;
+	}
+
+	*name = NULL;
+	*data = blob->data;
+	*data_len = blob->len;
+
+	return 0;
+}
+
+
+static void eap_tls_params_flags(struct tls_connection_params *params,
+				 const char *txt)
+{
+	if (txt == NULL)
+		return;
+	if (os_strstr(txt, "tls_allow_md5=1"))
+		params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
+	if (os_strstr(txt, "tls_disable_time_checks=1"))
+		params->flags |= TLS_CONN_DISABLE_TIME_CHECKS;
+	if (os_strstr(txt, "tls_disable_session_ticket=1"))
+		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_0=1"))
+		params->flags |= TLS_CONN_DISABLE_TLSv1_0;
+	if (os_strstr(txt, "tls_disable_tlsv1_0=0"))
+		params->flags &= ~TLS_CONN_DISABLE_TLSv1_0;
+	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;
+}
+
+
+static void eap_tls_params_from_conf1(struct tls_connection_params *params,
+				      struct eap_peer_config *config)
+{
+	params->ca_cert = (char *) config->ca_cert;
+	params->ca_path = (char *) config->ca_path;
+	params->client_cert = (char *) config->client_cert;
+	params->private_key = (char *) config->private_key;
+	params->private_key_passwd = (char *) config->private_key_passwd;
+	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->domain_match = config->domain_match;
+	params->engine = config->engine;
+	params->engine_id = config->engine_id;
+	params->pin = config->pin;
+	params->key_id = config->key_id;
+	params->cert_id = config->cert_id;
+	params->ca_cert_id = config->ca_cert_id;
+	eap_tls_params_flags(params, config->phase1);
+}
+
+
+static void eap_tls_params_from_conf2(struct tls_connection_params *params,
+				      struct eap_peer_config *config)
+{
+	params->ca_cert = (char *) config->ca_cert2;
+	params->ca_path = (char *) config->ca_path2;
+	params->client_cert = (char *) config->client_cert2;
+	params->private_key = (char *) config->private_key2;
+	params->private_key_passwd = (char *) config->private_key2_passwd;
+	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->domain_match = config->domain_match2;
+	params->engine = config->engine2;
+	params->engine_id = config->engine2_id;
+	params->pin = config->pin2;
+	params->key_id = config->key2_id;
+	params->cert_id = config->cert2_id;
+	params->ca_cert_id = config->ca_cert2_id;
+	eap_tls_params_flags(params, config->phase2);
+}
+
+
+static int eap_tls_params_from_conf(struct eap_sm *sm,
+				    struct eap_ssl_data *data,
+				    struct tls_connection_params *params,
+				    struct eap_peer_config *config, int phase2)
+{
+	os_memset(params, 0, sizeof(*params));
+	if (sm->workaround && data->eap_type != EAP_TYPE_FAST) {
+		/*
+		 * Some deployed authentication servers seem to be unable to
+		 * handle the TLS Session Ticket extension (they are supposed
+		 * to ignore unrecognized TLS extensions, but end up rejecting
+		 * the ClientHello instead). As a workaround, disable use of
+		 * TLS Sesson Ticket extension for EAP-TLS, EAP-PEAP, and
+		 * EAP-TTLS (EAP-FAST uses session ticket, so any server that
+		 * supports EAP-FAST does not need this workaround).
+		 */
+		params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
+	}
+	if (phase2) {
+		wpa_printf(MSG_DEBUG, "TLS: using phase2 config options");
+		eap_tls_params_from_conf2(params, config);
+	} else {
+		wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
+		eap_tls_params_from_conf1(params, config);
+		if (data->eap_type == EAP_TYPE_FAST)
+			params->flags |= TLS_CONN_EAP_FAST;
+	}
+
+	/*
+	 * Use blob data, if available. Otherwise, leave reference to external
+	 * file as-is.
+	 */
+	if (eap_tls_check_blob(sm, &params->ca_cert, &params->ca_cert_blob,
+			       &params->ca_cert_blob_len) ||
+	    eap_tls_check_blob(sm, &params->client_cert,
+			       &params->client_cert_blob,
+			       &params->client_cert_blob_len) ||
+	    eap_tls_check_blob(sm, &params->private_key,
+			       &params->private_key_blob,
+			       &params->private_key_blob_len) ||
+	    eap_tls_check_blob(sm, &params->dh_file, &params->dh_blob,
+			       &params->dh_blob_len)) {
+		wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs");
+		return -1;
+	}
+
+	params->openssl_ciphers = config->openssl_ciphers;
+
+	return 0;
+}
+
+
+static int eap_tls_init_connection(struct eap_sm *sm,
+				   struct eap_ssl_data *data,
+				   struct eap_peer_config *config,
+				   struct tls_connection_params *params)
+{
+	int res;
+
+	if (config->ocsp)
+		params->flags |= TLS_CONN_REQUEST_OCSP;
+	if (config->ocsp == 2)
+		params->flags |= TLS_CONN_REQUIRE_OCSP;
+	data->conn = tls_connection_init(data->ssl_ctx);
+	if (data->conn == NULL) {
+		wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
+			   "connection");
+		return -1;
+	}
+
+	res = tls_connection_set_params(data->ssl_ctx, data->conn, params);
+	if (res == TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN) {
+		/*
+		 * At this point with the pkcs11 engine the PIN is wrong. We
+		 * reset the PIN in the configuration to be sure to not use it
+		 * again and the calling function must request a new one.
+		 */
+		wpa_printf(MSG_INFO,
+			   "TLS: Bad PIN provided, requesting a new one");
+		os_free(config->pin);
+		config->pin = NULL;
+		eap_sm_request_pin(sm);
+		sm->ignore = TRUE;
+	} else if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
+		wpa_printf(MSG_INFO, "TLS: Failed to initialize engine");
+	} else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) {
+		wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+		sm->ignore = TRUE;
+	}
+	if (res) {
+		wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
+			   "parameters");
+		tls_connection_deinit(data->ssl_ctx, data->conn);
+		data->conn = NULL;
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * eap_peer_tls_ssl_init - Initialize shared TLS functionality
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @config: Pointer to the network configuration
+ * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to initialize shared TLS functionality for EAP-TLS,
+ * EAP-PEAP, EAP-TTLS, and EAP-FAST.
+ */
+int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+			  struct eap_peer_config *config, u8 eap_type)
+{
+	struct tls_connection_params params;
+
+	if (config == NULL)
+		return -1;
+
+	data->eap = sm;
+	data->eap_type = eap_type;
+	data->phase2 = sm->init_phase2;
+	data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+		sm->ssl_ctx;
+	if (eap_tls_params_from_conf(sm, data, &params, config, data->phase2) <
+	    0)
+		return -1;
+
+	if (eap_tls_init_connection(sm, data, config, &params) < 0)
+		return -1;
+
+	data->tls_out_limit = config->fragment_size;
+	if (data->phase2) {
+		/* Limit the fragment size in the inner TLS authentication
+		 * since the outer authentication with EAP-PEAP does not yet
+		 * support fragmentation */
+		if (data->tls_out_limit > 100)
+			data->tls_out_limit -= 100;
+	}
+
+	if (config->phase1 &&
+	    os_strstr(config->phase1, "include_tls_length=1")) {
+		wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in "
+			   "unfragmented packets");
+		data->include_tls_length = 1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ *
+ * This function deinitializes shared TLS functionality that was initialized
+ * with eap_peer_tls_ssl_init().
+ */
+void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+	tls_connection_deinit(data->ssl_ctx, data->conn);
+	eap_peer_tls_reset_input(data);
+	eap_peer_tls_reset_output(data);
+}
+
+
+/**
+ * eap_peer_tls_derive_key - Derive a key based on TLS session data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @label: Label string for deriving the keys, e.g., "client EAP encryption"
+ * @len: Length of the key material to generate (usually 64 for MSK)
+ * Returns: Pointer to allocated key on success or %NULL on failure
+ *
+ * This function uses TLS-PRF to generate pseudo-random data based on the TLS
+ * session data (client/server random and master key). Each key type may use a
+ * different label to bind the key usage into the generated material.
+ *
+ * The caller is responsible for freeing the returned buffer.
+ */
+u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+			     const char *label, size_t len)
+{
+	u8 *out;
+
+	out = os_malloc(len);
+	if (out == NULL)
+		return NULL;
+
+	if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, 0,
+			       out, len)) {
+		os_free(out);
+		return NULL;
+	}
+
+	return out;
+}
+
+
+/**
+ * eap_peer_tls_derive_session_id - Derive a Session-Id based on TLS data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+ * @len: Pointer to length of the session ID generated
+ * Returns: Pointer to allocated Session-Id on success or %NULL on failure
+ *
+ * This function derive the Session-Id based on the TLS session data
+ * (client/server random and method type).
+ *
+ * The caller is responsible for freeing the returned buffer.
+ */
+u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
+				    struct eap_ssl_data *data, u8 eap_type,
+				    size_t *len)
+{
+	struct tls_random keys;
+	u8 *out;
+
+	if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys))
+		return NULL;
+
+	if (keys.client_random == NULL || keys.server_random == NULL)
+		return NULL;
+
+	*len = 1 + keys.client_random_len + keys.server_random_len;
+	out = os_malloc(*len);
+	if (out == NULL)
+		return NULL;
+
+	/* Session-Id = EAP type || client.random || server.random */
+	out[0] = eap_type;
+	os_memcpy(out + 1, keys.client_random, keys.client_random_len);
+	os_memcpy(out + 1 + keys.client_random_len, keys.server_random,
+		  keys.server_random_len);
+
+	return out;
+}
+
+
+/**
+ * eap_peer_tls_reassemble_fragment - Reassemble a received fragment
+ * @data: Data for TLS processing
+ * @in_data: Next incoming TLS segment
+ * Returns: 0 on success, 1 if more data is needed for the full message, or
+ * -1 on error
+ */
+static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
+					    const struct wpabuf *in_data)
+{
+	size_t tls_in_len, in_len;
+
+	tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0;
+	in_len = in_data ? wpabuf_len(in_data) : 0;
+
+	if (tls_in_len + in_len == 0) {
+		/* No message data received?! */
+		wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: "
+			   "tls_in_left=%lu tls_in_len=%lu in_len=%lu",
+			   (unsigned long) data->tls_in_left,
+			   (unsigned long) tls_in_len,
+			   (unsigned long) in_len);
+		eap_peer_tls_reset_input(data);
+		return -1;
+	}
+
+	if (tls_in_len + in_len > 65536) {
+		/*
+		 * Limit length to avoid rogue servers from causing large
+		 * memory allocations.
+		 */
+		wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over "
+			   "64 kB)");
+		eap_peer_tls_reset_input(data);
+		return -1;
+	}
+
+	if (in_len > data->tls_in_left) {
+		/* Sender is doing something odd - reject message */
+		wpa_printf(MSG_INFO, "SSL: more data than TLS message length "
+			   "indicated");
+		eap_peer_tls_reset_input(data);
+		return -1;
+	}
+
+	if (wpabuf_resize(&data->tls_in, in_len) < 0) {
+		wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS "
+			   "data");
+		eap_peer_tls_reset_input(data);
+		return -1;
+	}
+	if (in_data)
+		wpabuf_put_buf(data->tls_in, in_data);
+	data->tls_in_left -= in_len;
+
+	if (data->tls_in_left > 0) {
+		wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input "
+			   "data", (unsigned long) data->tls_in_left);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * eap_peer_tls_data_reassemble - Reassemble TLS data
+ * @data: Data for TLS processing
+ * @in_data: Next incoming TLS segment
+ * @need_more_input: Variable for returning whether more input data is needed
+ * to reassemble this TLS packet
+ * Returns: Pointer to output data, %NULL on error or when more data is needed
+ * for the full message (in which case, *need_more_input is also set to 1).
+ *
+ * This function reassembles TLS fragments. Caller must not free the returned
+ * data buffer since an internal pointer to it is maintained.
+ */
+static const struct wpabuf * eap_peer_tls_data_reassemble(
+	struct eap_ssl_data *data, const struct wpabuf *in_data,
+	int *need_more_input)
+{
+	*need_more_input = 0;
+
+	if (data->tls_in_left > wpabuf_len(in_data) || data->tls_in) {
+		/* Message has fragments */
+		int res = eap_peer_tls_reassemble_fragment(data, in_data);
+		if (res) {
+			if (res == 1)
+				*need_more_input = 1;
+			return NULL;
+		}
+
+		/* Message is now fully reassembled. */
+	} else {
+		/* No fragments in this message, so just make a copy of it. */
+		data->tls_in_left = 0;
+		data->tls_in = wpabuf_dup(in_data);
+		if (data->tls_in == NULL)
+			return NULL;
+	}
+
+	return data->tls_in;
+}
+
+
+/**
+ * eap_tls_process_input - Process incoming TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @in_data: Message received from the server
+ * @out_data: Buffer for returning a pointer to application data (if available)
+ * Returns: 0 on success, 1 if more input data is needed, 2 if application data
+ * is available, -1 on failure
+ */
+static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data,
+				 const struct wpabuf *in_data,
+				 struct wpabuf **out_data)
+{
+	const struct wpabuf *msg;
+	int need_more_input;
+	struct wpabuf *appl_data;
+
+	msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input);
+	if (msg == NULL)
+		return need_more_input ? 1 : -1;
+
+	/* Full TLS message reassembled - continue handshake processing */
+	if (data->tls_out) {
+		/* This should not happen.. */
+		wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending "
+			   "tls_out data even though tls_out_len = 0");
+		wpabuf_free(data->tls_out);
+		WPA_ASSERT(data->tls_out == NULL);
+	}
+	appl_data = NULL;
+	data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn,
+						 msg, &appl_data);
+
+	eap_peer_tls_reset_input(data);
+
+	if (appl_data &&
+	    tls_connection_established(data->ssl_ctx, data->conn) &&
+	    !tls_connection_get_failed(data->ssl_ctx, data->conn)) {
+		wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data",
+				    appl_data);
+		*out_data = appl_data;
+		return 2;
+	}
+
+	wpabuf_free(appl_data);
+
+	return 0;
+}
+
+
+/**
+ * eap_tls_process_output - Process outgoing TLS message
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @ret: Return value to use on success
+ * @out_data: Buffer for returning the allocated output buffer
+ * Returns: ret (0 or 1) on success, -1 on failure
+ */
+static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type,
+				  int peap_version, u8 id, int ret,
+				  struct wpabuf **out_data)
+{
+	size_t len;
+	u8 *flags;
+	int more_fragments, length_included;
+
+	if (data->tls_out == NULL)
+		return -1;
+	len = wpabuf_len(data->tls_out) - data->tls_out_pos;
+	wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total "
+		   "%lu bytes)",
+		   (unsigned long) len,
+		   (unsigned long) wpabuf_len(data->tls_out));
+
+	/*
+	 * Limit outgoing message to the configured maximum size. Fragment
+	 * message if needed.
+	 */
+	if (len > data->tls_out_limit) {
+		more_fragments = 1;
+		len = data->tls_out_limit;
+		wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments "
+			   "will follow", (unsigned long) len);
+	} else
+		more_fragments = 0;
+
+	length_included = data->tls_out_pos == 0 &&
+		(wpabuf_len(data->tls_out) > data->tls_out_limit ||
+		 data->include_tls_length);
+	if (!length_included &&
+	    eap_type == EAP_TYPE_PEAP && peap_version == 0 &&
+	    !tls_connection_established(data->eap->ssl_ctx, data->conn)) {
+		/*
+		 * Windows Server 2008 NPS really wants to have the TLS Message
+		 * length included in phase 0 even for unfragmented frames or
+		 * it will get very confused with Compound MAC calculation and
+		 * Outer TLVs.
+		 */
+		length_included = 1;
+	}
+
+	*out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len,
+				      EAP_CODE_RESPONSE, id);
+	if (*out_data == NULL)
+		return -1;
+
+	flags = wpabuf_put(*out_data, 1);
+	*flags = peap_version;
+	if (more_fragments)
+		*flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
+	if (length_included) {
+		*flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
+		wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out));
+	}
+
+	wpabuf_put_data(*out_data,
+			wpabuf_head_u8(data->tls_out) + data->tls_out_pos,
+			len);
+	data->tls_out_pos += len;
+
+	if (!more_fragments)
+		eap_peer_tls_reset_output(data);
+
+	return ret;
+}
+
+
+/**
+ * eap_peer_tls_process_helper - Process TLS handshake message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @in_data: Message received from the server
+ * @out_data: Buffer for returning a pointer to the response message
+ * Returns: 0 on success, 1 if more input data is needed, 2 if application data
+ * is available, or -1 on failure
+ *
+ * This function can be used to process TLS handshake messages. It reassembles
+ * the received fragments and uses a TLS library to process the messages. The
+ * response data from the TLS library is fragmented to suitable output messages
+ * that the caller can send out.
+ *
+ * out_data is used to return the response message if the return value of this
+ * function is 0, 2, or -1. In case of failure, the message is likely a TLS
+ * alarm message. The caller is responsible for freeing the allocated buffer if
+ * *out_data is not %NULL.
+ *
+ * This function is called for each received TLS message during the TLS
+ * handshake after eap_peer_tls_process_init() call and possible processing of
+ * TLS Flags field. Once the handshake has been completed, i.e., when
+ * tls_connection_established() returns 1, EAP method specific decrypting of
+ * the tunneled data is used.
+ */
+int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+				EapType eap_type, int peap_version,
+				u8 id, const struct wpabuf *in_data,
+				struct wpabuf **out_data)
+{
+	int ret = 0;
+
+	*out_data = NULL;
+
+	if (data->tls_out && wpabuf_len(data->tls_out) > 0 &&
+	    wpabuf_len(in_data) > 0) {
+		wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output "
+			   "fragments are waiting to be sent out");
+		return -1;
+	}
+
+	if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
+		/*
+		 * No more data to send out - expect to receive more data from
+		 * the AS.
+		 */
+		int res = eap_tls_process_input(sm, data, in_data, out_data);
+		if (res) {
+			/*
+			 * Input processing failed (res = -1) or more data is
+			 * needed (res = 1).
+			 */
+			return res;
+		}
+
+		/*
+		 * The incoming message has been reassembled and processed. The
+		 * response was allocated into data->tls_out buffer.
+		 */
+	}
+
+	if (data->tls_out == NULL) {
+		/*
+		 * No outgoing fragments remaining from the previous message
+		 * and no new message generated. This indicates an error in TLS
+		 * processing.
+		 */
+		eap_peer_tls_reset_output(data);
+		return -1;
+	}
+
+	if (tls_connection_get_failed(data->ssl_ctx, data->conn)) {
+		/* TLS processing has failed - return error */
+		wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
+			   "report error (len=%u)",
+			   (unsigned int) wpabuf_len(data->tls_out));
+		ret = -1;
+		/* TODO: clean pin if engine used? */
+		if (wpabuf_len(data->tls_out) == 0) {
+			wpabuf_free(data->tls_out);
+			data->tls_out = NULL;
+			return -1;
+		}
+	}
+
+	if (wpabuf_len(data->tls_out) == 0) {
+		/*
+		 * TLS negotiation should now be complete since all other cases
+		 * needing more data should have been caught above based on
+		 * the TLS Message Length field.
+		 */
+		wpa_printf(MSG_DEBUG, "SSL: No data to be sent out");
+		wpabuf_free(data->tls_out);
+		data->tls_out = NULL;
+		return 1;
+	}
+
+	/* Send the pending message (in fragments, if needed). */
+	return eap_tls_process_output(data, eap_type, peap_version, id, ret,
+				      out_data);
+}
+
+
+/**
+ * eap_peer_tls_build_ack - Build a TLS ACK frame
+ * @id: EAP identifier for the response
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * Returns: Pointer to the allocated ACK frame or %NULL on failure
+ */
+struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+				       int peap_version)
+{
+	struct wpabuf *resp;
+
+	resp = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+	wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)",
+		   (int) eap_type, id, peap_version);
+	wpabuf_put_u8(resp, peap_version); /* Flags */
+	return resp;
+}
+
+
+/**
+ * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+	eap_peer_tls_reset_input(data);
+	eap_peer_tls_reset_output(data);
+	return tls_connection_shutdown(data->ssl_ctx, data->conn);
+}
+
+
+/**
+ * eap_peer_tls_status - Get TLS status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ */
+int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
+			char *buf, size_t buflen, int verbose)
+{
+	char version[20], name[128];
+	int len = 0, ret;
+
+	if (tls_get_version(data->ssl_ctx, data->conn, version,
+			    sizeof(version)) < 0)
+		version[0] = '\0';
+	if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) < 0)
+		name[0] = '\0';
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "eap_tls_version=%s\n"
+			  "EAP TLS cipher=%s\n"
+			  "tls_session_reused=%d\n",
+			  version, name,
+			  tls_connection_resumed(data->ssl_ctx, data->conn));
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	return len;
+}
+
+
+/**
+ * eap_peer_tls_process_init - Initial validation/processing of EAP requests
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * @len: Buffer for returning length of the remaining payload
+ * @flags: Buffer for returning TLS flags
+ * Returns: Pointer to payload after TLS flags and length or %NULL on failure
+ *
+ * This function validates the EAP header and processes the optional TLS
+ * Message Length field. If this is the first fragment of a TLS message, the
+ * TLS reassembly code is initialized to receive the indicated number of bytes.
+ *
+ * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this
+ * function as the first step in processing received messages. They will need
+ * to process the flags (apart from Message Length Included) that are returned
+ * through the flags pointer and the message payload that will be returned (and
+ * the length is returned through the len pointer). Return values (ret) are set
+ * for continuation of EAP method processing. The caller is responsible for
+ * setting these to indicate completion (either success or failure) based on
+ * the authentication result.
+ */
+const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
+				     struct eap_ssl_data *data,
+				     EapType eap_type,
+				     struct eap_method_ret *ret,
+				     const struct wpabuf *reqData,
+				     size_t *len, u8 *flags)
+{
+	const u8 *pos;
+	size_t left;
+	unsigned int tls_msg_len;
+
+	if (tls_get_errors(data->ssl_ctx)) {
+		wpa_printf(MSG_INFO, "SSL: TLS errors detected");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (eap_type == EAP_UNAUTH_TLS_TYPE)
+		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);
+	if (pos == NULL) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+	if (left == 0) {
+		wpa_printf(MSG_DEBUG, "SSL: Invalid TLS message: no Flags "
+			   "octet included");
+		if (!sm->workaround) {
+			ret->ignore = TRUE;
+			return NULL;
+		}
+
+		wpa_printf(MSG_DEBUG, "SSL: Workaround - assume no Flags "
+			   "indicates ACK frame");
+		*flags = 0;
+	} else {
+		*flags = *pos++;
+		left--;
+	}
+	wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - "
+		   "Flags 0x%02x", (unsigned long) wpabuf_len(reqData),
+		   *flags);
+	if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+		if (left < 4) {
+			wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
+				   "length");
+			ret->ignore = TRUE;
+			return NULL;
+		}
+		tls_msg_len = WPA_GET_BE32(pos);
+		wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
+			   tls_msg_len);
+		if (data->tls_in_left == 0) {
+			data->tls_in_total = tls_msg_len;
+			data->tls_in_left = tls_msg_len;
+			wpabuf_free(data->tls_in);
+			data->tls_in = NULL;
+		}
+		pos += 4;
+		left -= 4;
+
+		if (left > tls_msg_len) {
+			wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d "
+				   "bytes) smaller than this fragment (%d "
+				   "bytes)", (int) tls_msg_len, (int) left);
+			ret->ignore = TRUE;
+			return NULL;
+		}
+	}
+
+	ret->ignore = FALSE;
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_FAIL;
+	ret->allowNotifications = TRUE;
+
+	*len = left;
+	return pos;
+}
+
+
+/**
+ * eap_peer_tls_reset_input - Reset input buffers
+ * @data: Data for TLS processing
+ *
+ * This function frees any allocated memory for input buffers and resets input
+ * state.
+ */
+void eap_peer_tls_reset_input(struct eap_ssl_data *data)
+{
+	data->tls_in_left = data->tls_in_total = 0;
+	wpabuf_free(data->tls_in);
+	data->tls_in = NULL;
+}
+
+
+/**
+ * eap_peer_tls_reset_output - Reset output buffers
+ * @data: Data for TLS processing
+ *
+ * This function frees any allocated memory for output buffers and resets
+ * output state.
+ */
+void eap_peer_tls_reset_output(struct eap_ssl_data *data)
+{
+	data->tls_out_pos = 0;
+	wpabuf_free(data->tls_out);
+	data->tls_out = NULL;
+}
+
+
+/**
+ * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @in_data: Message received from the server
+ * @in_decrypted: Buffer for returning a pointer to the decrypted message
+ * Returns: 0 on success, 1 if more input data is needed, or -1 on failure
+ */
+int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+			 const struct wpabuf *in_data,
+			 struct wpabuf **in_decrypted)
+{
+	const struct wpabuf *msg;
+	int need_more_input;
+
+	msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input);
+	if (msg == NULL)
+		return need_more_input ? 1 : -1;
+
+	*in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg);
+	eap_peer_tls_reset_input(data);
+	if (*in_decrypted == NULL) {
+		wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data");
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * eap_peer_tls_encrypt - Encrypt phase 2 TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments
+ * @out_data: Buffer for returning a pointer to the encrypted response message
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+			 EapType eap_type, int peap_version, u8 id,
+			 const struct wpabuf *in_data,
+			 struct wpabuf **out_data)
+{
+	if (in_data) {
+		eap_peer_tls_reset_output(data);
+		data->tls_out = tls_connection_encrypt(data->ssl_ctx,
+						       data->conn, in_data);
+		if (data->tls_out == NULL) {
+			wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 "
+				   "data (in_len=%lu)",
+				   (unsigned long) wpabuf_len(in_data));
+			eap_peer_tls_reset_output(data);
+			return -1;
+		}
+	}
+
+	return eap_tls_process_output(data, eap_type, peap_version, id, 0,
+				      out_data);
+}
+
+
+/**
+ * eap_peer_select_phase2_methods - Select phase 2 EAP method
+ * @config: Pointer to the network configuration
+ * @prefix: 'phase2' configuration prefix, e.g., "auth="
+ * @types: Buffer for returning allocated list of allowed EAP methods
+ * @num_types: Buffer for returning number of allocated EAP methods
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to parse EAP method list and select allowed methods
+ * for Phase2 authentication.
+ */
+int eap_peer_select_phase2_methods(struct eap_peer_config *config,
+				   const char *prefix,
+				   struct eap_method_type **types,
+				   size_t *num_types)
+{
+	char *start, *pos, *buf;
+	struct eap_method_type *methods = NULL, *_methods;
+	u32 method;
+	size_t num_methods = 0, prefix_len;
+
+	if (config == NULL || config->phase2 == NULL)
+		goto get_defaults;
+
+	start = buf = os_strdup(config->phase2);
+	if (buf == NULL)
+		return -1;
+
+	prefix_len = os_strlen(prefix);
+
+	while (start && *start != '\0') {
+		int vendor;
+		pos = os_strstr(start, prefix);
+		if (pos == NULL)
+			break;
+		if (start != pos && *(pos - 1) != ' ') {
+			start = pos + prefix_len;
+			continue;
+		}
+
+		start = pos + prefix_len;
+		pos = os_strchr(start, ' ');
+		if (pos)
+			*pos++ = '\0';
+		method = eap_get_phase2_type(start, &vendor);
+		if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) {
+			wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP "
+				   "method '%s'", start);
+		} else {
+			num_methods++;
+			_methods = os_realloc_array(methods, num_methods,
+						    sizeof(*methods));
+			if (_methods == NULL) {
+				os_free(methods);
+				os_free(buf);
+				return -1;
+			}
+			methods = _methods;
+			methods[num_methods - 1].vendor = vendor;
+			methods[num_methods - 1].method = method;
+		}
+
+		start = pos;
+	}
+
+	os_free(buf);
+
+get_defaults:
+	if (methods == NULL)
+		methods = eap_get_phase2_types(config, &num_methods);
+
+	if (methods == NULL) {
+		wpa_printf(MSG_ERROR, "TLS: No Phase2 EAP methods available");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types",
+		    (u8 *) methods,
+		    num_methods * sizeof(struct eap_method_type));
+
+	*types = methods;
+	*num_types = num_methods;
+
+	return 0;
+}
+
+
+/**
+ * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2
+ * @types: Buffer for returning allocated list of allowed EAP methods
+ * @num_types: Buffer for returning number of allocated EAP methods
+ * @hdr: EAP-Request header (and the following EAP type octet)
+ * @resp: Buffer for returning the EAP-Nak message
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
+			    struct eap_hdr *hdr, struct wpabuf **resp)
+{
+	u8 *pos = (u8 *) (hdr + 1);
+	size_t i;
+
+	/* TODO: add support for expanded Nak */
+	wpa_printf(MSG_DEBUG, "TLS: Phase 2 Request: Nak type=%d", *pos);
+	wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types",
+		    (u8 *) types, num_types * sizeof(struct eap_method_type));
+	*resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types,
+			      EAP_CODE_RESPONSE, hdr->identifier);
+	if (*resp == NULL)
+		return -1;
+
+	for (i = 0; i < num_types; i++) {
+		if (types[i].vendor == EAP_VENDOR_IETF &&
+		    types[i].method < 256)
+			wpabuf_put_u8(*resp, types[i].method);
+	}
+
+	eap_update_len(*resp);
+
+	return 0;
+}
diff --git a/hostap/src/eap_peer/eap_tls_common.h b/hostap/src/eap_peer/eap_tls_common.h
new file mode 100644
index 0000000..acd2b78
--- /dev/null
+++ b/hostap/src/eap_peer/eap_tls_common.h
@@ -0,0 +1,132 @@
+/*
+ * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
+ * Copyright (c) 2004-2009, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_TLS_COMMON_H
+#define EAP_TLS_COMMON_H
+
+/**
+ * struct eap_ssl_data - TLS data for EAP methods
+ */
+struct eap_ssl_data {
+	/**
+	 * conn - TLS connection context data from tls_connection_init()
+	 */
+	struct tls_connection *conn;
+
+	/**
+	 * tls_out - TLS message to be sent out in fragments
+	 */
+	struct wpabuf *tls_out;
+
+	/**
+	 * tls_out_pos - The current position in the outgoing TLS message
+	 */
+	size_t tls_out_pos;
+
+	/**
+	 * tls_out_limit - Maximum fragment size for outgoing TLS messages
+	 */
+	size_t tls_out_limit;
+
+	/**
+	 * tls_in - Received TLS message buffer for re-assembly
+	 */
+	struct wpabuf *tls_in;
+
+	/**
+	 * tls_in_left - Number of remaining bytes in the incoming TLS message
+	 */
+	size_t tls_in_left;
+
+	/**
+	 * tls_in_total - Total number of bytes in the incoming TLS message
+	 */
+	size_t tls_in_total;
+
+	/**
+	 * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
+	 */
+	int phase2;
+
+	/**
+	 * include_tls_length - Whether the TLS length field is included even
+	 * if the TLS data is not fragmented
+	 */
+	int include_tls_length;
+
+	/**
+	 * eap - EAP state machine allocated with eap_peer_sm_init()
+	 */
+	struct eap_sm *eap;
+
+	/**
+	 * ssl_ctx - TLS library context to use for the connection
+	 */
+	void *ssl_ctx;
+
+	/**
+	 * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+	 */
+	u8 eap_type;
+};
+
+
+/* EAP TLS Flags */
+#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TLS_FLAGS_START 0x20
+#define EAP_TLS_VERSION_MASK 0x07
+
+ /* could be up to 128 bytes, but only the first 64 bytes are used */
+#define EAP_TLS_KEY_LEN 64
+
+/* 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,
+			  struct eap_peer_config *config, u8 eap_type);
+void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
+u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+			     const char *label, size_t len);
+u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
+				    struct eap_ssl_data *data, u8 eap_type,
+				    size_t *len);
+int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+				EapType eap_type, int peap_version,
+				u8 id, const struct wpabuf *in_data,
+				struct wpabuf **out_data);
+struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+				       int peap_version);
+int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data);
+int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
+			char *buf, size_t buflen, int verbose);
+const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
+				     struct eap_ssl_data *data,
+				     EapType eap_type,
+				     struct eap_method_ret *ret,
+				     const struct wpabuf *reqData,
+				     size_t *len, u8 *flags);
+void eap_peer_tls_reset_input(struct eap_ssl_data *data);
+void eap_peer_tls_reset_output(struct eap_ssl_data *data);
+int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+			 const struct wpabuf *in_data,
+			 struct wpabuf **in_decrypted);
+int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+			 EapType eap_type, int peap_version, u8 id,
+			 const struct wpabuf *in_data,
+			 struct wpabuf **out_data);
+int eap_peer_select_phase2_methods(struct eap_peer_config *config,
+				   const char *prefix,
+				   struct eap_method_type **types,
+				   size_t *num_types);
+int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
+			    struct eap_hdr *hdr, struct wpabuf **resp);
+
+#endif /* EAP_TLS_COMMON_H */
diff --git a/hostap/src/eap_peer/eap_tnc.c b/hostap/src/eap_peer/eap_tnc.c
new file mode 100644
index 0000000..25b9f12
--- /dev/null
+++ b/hostap/src/eap_peer/eap_tnc.c
@@ -0,0 +1,428 @@
+/*
+ * EAP peer method: EAP-TNC (Trusted Network Connect)
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "tncc.h"
+
+
+struct eap_tnc_data {
+	enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state;
+	struct tncc_data *tncc;
+	struct wpabuf *in_buf;
+	struct wpabuf *out_buf;
+	size_t out_used;
+	size_t fragment_size;
+};
+
+
+/* EAP-TNC Flags */
+#define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TNC_FLAGS_START 0x20
+#define EAP_TNC_VERSION_MASK 0x07
+
+#define EAP_TNC_VERSION 1
+
+
+static void * eap_tnc_init(struct eap_sm *sm)
+{
+	struct eap_tnc_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = WAIT_START;
+	data->fragment_size = 1300;
+	data->tncc = tncc_init();
+	if (data->tncc == NULL) {
+		os_free(data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+static void eap_tnc_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_tnc_data *data = priv;
+
+	wpabuf_free(data->in_buf);
+	wpabuf_free(data->out_buf);
+	tncc_deinit(data->tncc);
+	os_free(data);
+}
+
+
+static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code)
+{
+	struct wpabuf *msg;
+
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id);
+	if (msg == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory "
+			   "for fragment ack");
+		return NULL;
+	}
+	wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */
+
+	wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack");
+
+	return msg;
+}
+
+
+static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data,
+					 struct eap_method_ret *ret, u8 id)
+{
+	struct wpabuf *resp;
+	u8 flags;
+	size_t send_len, plen;
+
+	ret->ignore = FALSE;
+	wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Response");
+	ret->allowNotifications = TRUE;
+
+	flags = EAP_TNC_VERSION;
+	send_len = wpabuf_len(data->out_buf) - data->out_used;
+	if (1 + send_len > data->fragment_size) {
+		send_len = data->fragment_size - 1;
+		flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS;
+		if (data->out_used == 0) {
+			flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED;
+			send_len -= 4;
+		}
+	}
+
+	plen = 1 + send_len;
+	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
+		plen += 4;
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen,
+			     EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+
+	wpabuf_put_u8(resp, flags); /* Flags */
+	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
+		wpabuf_put_be32(resp, wpabuf_len(data->out_buf));
+
+	wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
+			send_len);
+	data->out_used += send_len;
+
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_FAIL;
+
+	if (data->out_used == wpabuf_len(data->out_buf)) {
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
+			   "(message sent completely)",
+			   (unsigned long) send_len);
+		wpabuf_free(data->out_buf);
+		data->out_buf = NULL;
+		data->out_used = 0;
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
+			   "(%lu more to send)", (unsigned long) send_len,
+			   (unsigned long) wpabuf_len(data->out_buf) -
+			   data->out_used);
+		data->state = WAIT_FRAG_ACK;
+	}
+
+	return resp;
+}
+
+
+static int eap_tnc_process_cont(struct eap_tnc_data *data,
+				const u8 *buf, size_t len)
+{
+	/* Process continuation of a pending message */
+	if (len > wpabuf_tailroom(data->in_buf)) {
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow");
+		data->state = FAIL;
+		return -1;
+	}
+
+	wpabuf_put_data(data->in_buf, buf, len);
+	wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for "
+		   "%lu bytes more", (unsigned long) len,
+		   (unsigned long) wpabuf_tailroom(data->in_buf));
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_tnc_process_fragment(struct eap_tnc_data *data,
+						struct eap_method_ret *ret,
+						u8 id, u8 flags,
+						u32 message_length,
+						const u8 *buf, size_t len)
+{
+	/* Process a fragment that is not the last one of the message */
+	if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) {
+		wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a "
+			   "fragmented packet");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (data->in_buf == NULL) {
+		/* First fragment of the message */
+		data->in_buf = wpabuf_alloc(message_length);
+		if (data->in_buf == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for "
+				   "message");
+			ret->ignore = TRUE;
+			return NULL;
+		}
+		wpabuf_put_data(data->in_buf, buf, len);
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first "
+			   "fragment, waiting for %lu bytes more",
+			   (unsigned long) len,
+			   (unsigned long) wpabuf_tailroom(data->in_buf));
+	}
+
+	return eap_tnc_build_frag_ack(id, EAP_CODE_RESPONSE);
+}
+
+
+static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv,
+				       struct eap_method_ret *ret,
+				       const struct wpabuf *reqData)
+{
+	struct eap_tnc_data *data = priv;
+	struct wpabuf *resp;
+	const u8 *pos, *end;
+	u8 *rpos, *rpos1;
+	size_t len, rlen;
+	size_t imc_len;
+	char *start_buf, *end_buf;
+	size_t start_len, end_len;
+	int tncs_done = 0;
+	u8 flags, id;
+	u32 message_length = 0;
+	struct wpabuf tmpbuf;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, reqData, &len);
+	if (pos == NULL) {
+		wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (pos=%p len=%lu)",
+			   pos, (unsigned long) len);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	id = eap_get_id(reqData);
+
+	end = pos + len;
+
+	if (len == 0)
+		flags = 0; /* fragment ack */
+	else
+		flags = *pos++;
+
+	if (len > 0 && (flags & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) {
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d",
+			   flags & EAP_TNC_VERSION_MASK);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) {
+		if (end - pos < 4) {
+			wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow");
+			ret->ignore = TRUE;
+			return NULL;
+		}
+		message_length = WPA_GET_BE32(pos);
+		pos += 4;
+
+		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));
+			ret->ignore = TRUE;
+			return NULL;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x "
+		   "Message Length %u", flags, message_length);
+
+	if (data->state == WAIT_FRAG_ACK) {
+		if (len > 1) {
+			wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload in "
+				   "WAIT_FRAG_ACK state");
+			ret->ignore = TRUE;
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged");
+		data->state = PROC_MSG;
+		return eap_tnc_build_msg(data, ret, id);
+	}
+
+	if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+		
+	if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) {
+		return eap_tnc_process_fragment(data, ret, id, flags,
+						message_length, pos,
+						end - pos);
+	}
+
+	if (data->in_buf == NULL) {
+		/* Wrap unfragmented messages as wpabuf without extra copy */
+		wpabuf_set(&tmpbuf, pos, end - pos);
+		data->in_buf = &tmpbuf;
+	}
+
+	if (data->state == WAIT_START) {
+		if (!(flags & EAP_TNC_FLAGS_START)) {
+			wpa_printf(MSG_DEBUG, "EAP-TNC: Server did not use "
+				   "start flag in the first message");
+			ret->ignore = TRUE;
+			goto fail;
+		}
+
+		tncc_init_connection(data->tncc);
+
+		data->state = PROC_MSG;
+	} else {
+		enum tncc_process_res res;
+
+		if (flags & EAP_TNC_FLAGS_START) {
+			wpa_printf(MSG_DEBUG, "EAP-TNC: Server used start "
+				   "flag again");
+			ret->ignore = TRUE;
+			goto fail;
+		}
+
+		res = tncc_process_if_tnccs(data->tncc,
+					    wpabuf_head(data->in_buf),
+					    wpabuf_len(data->in_buf));
+		switch (res) {
+		case TNCCS_PROCESS_ERROR:
+			ret->ignore = TRUE;
+			goto fail;
+		case TNCCS_PROCESS_OK_NO_RECOMMENDATION:
+		case TNCCS_RECOMMENDATION_ERROR:
+			wpa_printf(MSG_DEBUG, "EAP-TNC: No "
+				   "TNCCS-Recommendation received");
+			break;
+		case TNCCS_RECOMMENDATION_ALLOW:
+			wpa_msg(sm->msg_ctx, MSG_INFO,
+				"TNC: Recommendation = allow");
+			tncs_done = 1;
+			break;
+		case TNCCS_RECOMMENDATION_NONE:
+			wpa_msg(sm->msg_ctx, MSG_INFO,
+				"TNC: Recommendation = none");
+			tncs_done = 1;
+			break;
+		case TNCCS_RECOMMENDATION_ISOLATE:
+			wpa_msg(sm->msg_ctx, MSG_INFO,
+				"TNC: Recommendation = isolate");
+			tncs_done = 1;
+			break;
+		}
+	}
+
+	if (data->in_buf != &tmpbuf)
+		wpabuf_free(data->in_buf);
+	data->in_buf = NULL;
+
+	ret->ignore = FALSE;
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_UNCOND_SUCC;
+	ret->allowNotifications = TRUE;
+
+	if (data->out_buf) {
+		data->state = PROC_MSG;
+		return eap_tnc_build_msg(data, ret, id);
+	}
+
+	if (tncs_done) {
+		resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1,
+				     EAP_CODE_RESPONSE, eap_get_id(reqData));
+		if (resp == NULL)
+			return NULL;
+
+		wpabuf_put_u8(resp, EAP_TNC_VERSION);
+		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS done - reply with an "
+			   "empty ACK message");
+		return resp;
+	}
+
+	imc_len = tncc_total_send_len(data->tncc);
+
+	start_buf = tncc_if_tnccs_start(data->tncc);
+	if (start_buf == NULL)
+		return NULL;
+	start_len = os_strlen(start_buf);
+	end_buf = tncc_if_tnccs_end();
+	if (end_buf == NULL) {
+		os_free(start_buf);
+		return NULL;
+	}
+	end_len = os_strlen(end_buf);
+
+	rlen = start_len + imc_len + end_len;
+	resp = wpabuf_alloc(rlen);
+	if (resp == NULL) {
+		os_free(start_buf);
+		os_free(end_buf);
+		return NULL;
+	}
+
+	wpabuf_put_data(resp, start_buf, start_len);
+	os_free(start_buf);
+
+	rpos1 = wpabuf_put(resp, 0);
+	rpos = tncc_copy_send_buf(data->tncc, rpos1);
+	wpabuf_put(resp, rpos - rpos1);
+
+	wpabuf_put_data(resp, end_buf, end_len);
+	os_free(end_buf);
+
+	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Response",
+			  wpabuf_head(resp), wpabuf_len(resp));
+
+	data->out_buf = resp;
+	data->state = PROC_MSG;
+	return eap_tnc_build_msg(data, ret, id);
+
+fail:
+	if (data->in_buf == &tmpbuf)
+		data->in_buf = NULL;
+	return NULL;
+}
+
+
+int eap_peer_tnc_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_tnc_init;
+	eap->deinit = eap_tnc_deinit;
+	eap->process = eap_tnc_process;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_ttls.c b/hostap/src/eap_peer/eap_ttls.c
new file mode 100644
index 0000000..b186c91
--- /dev/null
+++ b/hostap/src/eap_peer/eap_ttls.c
@@ -0,0 +1,1721 @@
+/*
+ * EAP peer method: EAP-TTLS (RFC 5281)
+ * Copyright (c) 2004-2011, 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/ms_funcs.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_common/chap.h"
+#include "eap_common/eap_ttls.h"
+#include "mschapv2.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+
+
+#define EAP_TTLS_VERSION 0
+
+
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_ttls_data {
+	struct eap_ssl_data ssl;
+
+	int ttls_version;
+
+	const struct eap_method *phase2_method;
+	void *phase2_priv;
+	int phase2_success;
+	int phase2_start;
+
+	enum phase2_types {
+		EAP_TTLS_PHASE2_EAP,
+		EAP_TTLS_PHASE2_MSCHAPV2,
+		EAP_TTLS_PHASE2_MSCHAP,
+		EAP_TTLS_PHASE2_PAP,
+		EAP_TTLS_PHASE2_CHAP
+	} phase2_type;
+	struct eap_method_type phase2_eap_type;
+	struct eap_method_type *phase2_eap_types;
+	size_t num_phase2_eap_types;
+
+	u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+	int auth_response_valid;
+	u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */
+	u8 ident;
+	int resuming; /* starting a resumed session */
+	int reauth; /* reauthentication */
+	u8 *key_data;
+	u8 *session_id;
+	size_t id_len;
+
+	struct wpabuf *pending_phase2_req;
+
+#ifdef EAP_TNC
+	int ready_for_tnc;
+	int tnc_started;
+#endif /* EAP_TNC */
+};
+
+
+static void * eap_ttls_init(struct eap_sm *sm)
+{
+	struct eap_ttls_data *data;
+	struct eap_peer_config *config = eap_get_config(sm);
+	char *selected;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->ttls_version = EAP_TTLS_VERSION;
+	selected = "EAP";
+	data->phase2_type = EAP_TTLS_PHASE2_EAP;
+
+	if (config && config->phase2) {
+		if (os_strstr(config->phase2, "autheap=")) {
+			selected = "EAP";
+			data->phase2_type = EAP_TTLS_PHASE2_EAP;
+		} else if (os_strstr(config->phase2, "auth=MSCHAPV2")) {
+			selected = "MSCHAPV2";
+			data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;
+		} else if (os_strstr(config->phase2, "auth=MSCHAP")) {
+			selected = "MSCHAP";
+			data->phase2_type = EAP_TTLS_PHASE2_MSCHAP;
+		} else if (os_strstr(config->phase2, "auth=PAP")) {
+			selected = "PAP";
+			data->phase2_type = EAP_TTLS_PHASE2_PAP;
+		} else if (os_strstr(config->phase2, "auth=CHAP")) {
+			selected = "CHAP";
+			data->phase2_type = EAP_TTLS_PHASE2_CHAP;
+		}
+	}
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected);
+
+	if (data->phase2_type == EAP_TTLS_PHASE2_EAP) {
+		if (eap_peer_select_phase2_methods(config, "autheap=",
+						   &data->phase2_eap_types,
+						   &data->num_phase2_eap_types)
+		    < 0) {
+			eap_ttls_deinit(sm, data);
+			return NULL;
+		}
+
+		data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
+		data->phase2_eap_type.method = EAP_TYPE_NONE;
+	}
+
+	if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
+		eap_ttls_deinit(sm, data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm,
+				       struct eap_ttls_data *data)
+{
+	if (data->phase2_priv && data->phase2_method) {
+		data->phase2_method->deinit(sm, data->phase2_priv);
+		data->phase2_method = NULL;
+		data->phase2_priv = NULL;
+	}
+}
+
+
+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 + EAP_EMSK_LEN);
+		data->key_data = NULL;
+	}
+}
+
+
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_ttls_data *data = priv;
+	if (data == NULL)
+		return;
+	eap_ttls_phase2_eap_deinit(sm, data);
+	os_free(data->phase2_eap_types);
+	eap_peer_tls_ssl_deinit(sm, &data->ssl);
+	eap_ttls_free_key(data);
+	os_free(data->session_id);
+	wpabuf_free(data->pending_phase2_req);
+	os_free(data);
+}
+
+
+static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
+			     int mandatory, size_t len)
+{
+	struct ttls_avp_vendor *avp;
+	u8 flags;
+	size_t hdrlen;
+
+	avp = (struct ttls_avp_vendor *) avphdr;
+	flags = mandatory ? AVP_FLAGS_MANDATORY : 0;
+	if (vendor_id) {
+		flags |= AVP_FLAGS_VENDOR;
+		hdrlen = sizeof(*avp);
+		avp->vendor_id = host_to_be32(vendor_id);
+	} else {
+		hdrlen = sizeof(struct ttls_avp);
+	}
+
+	avp->avp_code = host_to_be32(avp_code);
+	avp->avp_length = host_to_be32(((u32) flags << 24) |
+				       (u32) (hdrlen + len));
+
+	return avphdr + hdrlen;
+}
+
+
+static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code,
+			     u32 vendor_id, int mandatory,
+			     const u8 *data, size_t len)
+{
+	u8 *pos;
+	pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len);
+	os_memcpy(pos, data, len);
+	pos += len;
+	AVP_PAD(start, pos);
+	return pos;
+}
+
+
+static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,
+				    int mandatory)
+{
+	struct wpabuf *msg;
+	u8 *avp, *pos;
+
+	msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4);
+	if (msg == NULL) {
+		wpabuf_free(*resp);
+		*resp = NULL;
+		return -1;
+	}
+
+	avp = wpabuf_mhead(msg);
+	pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp));
+	os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp));
+	pos += wpabuf_len(*resp);
+	AVP_PAD(avp, pos);
+	wpabuf_free(*resp);
+	wpabuf_put(msg, pos - avp);
+	*resp = msg;
+	return 0;
+}
+
+
+static int eap_ttls_v0_derive_key(struct eap_sm *sm,
+				  struct eap_ttls_data *data)
+{
+	eap_ttls_free_key(data);
+	data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
+						 "ttls keying material",
+						 EAP_TLS_KEY_LEN +
+						 EAP_EMSK_LEN);
+	if (!data->key_data) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key");
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
+			data->key_data, EAP_TLS_KEY_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived EMSK",
+			data->key_data + EAP_TLS_KEY_LEN,
+			EAP_EMSK_LEN);
+
+	os_free(data->session_id);
+	data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,
+							  EAP_TYPE_TTLS,
+	                                                  &data->id_len);
+	if (data->session_id) {
+		wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived Session-Id",
+			    data->session_id, data->id_len);
+	} else {
+		wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id");
+	}
+
+	return 0;
+}
+
+
+#ifndef CONFIG_FIPS
+static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
+					struct eap_ttls_data *data, size_t len)
+{
+	return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len);
+}
+#endif /* CONFIG_FIPS */
+
+
+static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data,
+					      u8 method)
+{
+	size_t i;
+	for (i = 0; i < data->num_phase2_eap_types; i++) {
+		if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF ||
+		    data->phase2_eap_types[i].method != method)
+			continue;
+
+		data->phase2_eap_type.vendor =
+			data->phase2_eap_types[i].vendor;
+		data->phase2_eap_type.method =
+			data->phase2_eap_types[i].method;
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+			   "Phase 2 EAP vendor %d method %d",
+			   data->phase2_eap_type.vendor,
+			   data->phase2_eap_type.method);
+		break;
+	}
+}
+
+
+static int eap_ttls_phase2_eap_process(struct eap_sm *sm,
+				       struct eap_ttls_data *data,
+				       struct eap_method_ret *ret,
+				       struct eap_hdr *hdr, size_t len,
+				       struct wpabuf **resp)
+{
+	struct wpabuf msg;
+	struct eap_method_ret iret;
+
+	os_memset(&iret, 0, sizeof(iret));
+	wpabuf_set(&msg, hdr, len);
+	*resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
+					     &msg);
+	if ((iret.methodState == METHOD_DONE ||
+	     iret.methodState == METHOD_MAY_CONT) &&
+	    (iret.decision == DECISION_UNCOND_SUCC ||
+	     iret.decision == DECISION_COND_SUCC ||
+	     iret.decision == DECISION_FAIL)) {
+		ret->methodState = iret.methodState;
+		ret->decision = iret.decision;
+	}
+
+	return 0;
+}
+
+
+static int eap_ttls_phase2_request_eap_method(struct eap_sm *sm,
+					      struct eap_ttls_data *data,
+					      struct eap_method_ret *ret,
+					      struct eap_hdr *hdr, size_t len,
+					      u8 method, struct wpabuf **resp)
+{
+#ifdef EAP_TNC
+	if (data->tnc_started && data->phase2_method &&
+	    data->phase2_priv && method == EAP_TYPE_TNC &&
+	    data->phase2_eap_type.method == EAP_TYPE_TNC)
+		return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len,
+						   resp);
+
+	if (data->ready_for_tnc && !data->tnc_started &&
+	    method == EAP_TYPE_TNC) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
+			   "EAP method");
+		data->tnc_started = 1;
+	}
+
+	if (data->tnc_started) {
+		if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF ||
+		    data->phase2_eap_type.method == EAP_TYPE_TNC) {
+			wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP "
+				   "type %d for TNC", method);
+			return -1;
+		}
+
+		data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
+		data->phase2_eap_type.method = method;
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+			   "Phase 2 EAP vendor %d method %d (TNC)",
+			   data->phase2_eap_type.vendor,
+			   data->phase2_eap_type.method);
+
+		if (data->phase2_type == EAP_TTLS_PHASE2_EAP)
+			eap_ttls_phase2_eap_deinit(sm, data);
+	}
+#endif /* EAP_TNC */
+
+	if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF &&
+	    data->phase2_eap_type.method == EAP_TYPE_NONE)
+		eap_ttls_phase2_select_eap_method(data, method);
+
+	if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE)
+	{
+		if (eap_peer_tls_phase2_nak(data->phase2_eap_types,
+					    data->num_phase2_eap_types,
+					    hdr, resp))
+			return -1;
+		return 0;
+	}
+
+	if (data->phase2_priv == NULL) {
+		data->phase2_method = eap_peer_get_eap_method(
+			EAP_VENDOR_IETF, method);
+		if (data->phase2_method) {
+			sm->init_phase2 = 1;
+			data->phase2_priv = data->phase2_method->init(sm);
+			sm->init_phase2 = 0;
+		}
+	}
+	if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize "
+			   "Phase 2 EAP method %d", method);
+		return -1;
+	}
+
+	return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp);
+}
+
+
+static int eap_ttls_phase2_request_eap(struct eap_sm *sm,
+				       struct eap_ttls_data *data,
+				       struct eap_method_ret *ret,
+				       struct eap_hdr *hdr,
+				       struct wpabuf **resp)
+{
+	size_t len = be_to_host16(hdr->length);
+	u8 *pos;
+	struct eap_peer_config *config = eap_get_config(sm);
+
+	if (len <= sizeof(struct eap_hdr)) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: too short "
+			   "Phase 2 request (len=%lu)", (unsigned long) len);
+		return -1;
+	}
+	pos = (u8 *) (hdr + 1);
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos);
+	switch (*pos) {
+	case EAP_TYPE_IDENTITY:
+		*resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+		break;
+	default:
+		if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len,
+						       *pos, resp) < 0)
+			return -1;
+		break;
+	}
+
+	if (*resp == NULL &&
+	    (config->pending_req_identity || config->pending_req_password ||
+	     config->pending_req_otp)) {
+		return 0;
+	}
+
+	if (*resp == NULL)
+		return -1;
+
+	wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response",
+			*resp);
+	return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1);
+}
+
+
+static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
+					    struct eap_ttls_data *data,
+					    struct eap_method_ret *ret,
+					    struct wpabuf **resp)
+{
+#ifdef CONFIG_FIPS
+	wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPV2 not supported in FIPS build");
+	return -1;
+#else /* CONFIG_FIPS */
+#ifdef EAP_MSCHAPv2
+	struct wpabuf *msg;
+	u8 *buf, *pos, *challenge, *peer_challenge;
+	const u8 *identity, *password;
+	size_t identity_len, password_len;
+	int pwhash;
+
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request");
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	password = eap_get_config_password2(sm, &password_len, &pwhash);
+	if (identity == NULL || password == NULL)
+		return -1;
+
+	msg = wpabuf_alloc(identity_len + 1000);
+	if (msg == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "EAP-TTLS/MSCHAPV2: Failed to allocate memory");
+		return -1;
+	}
+	pos = buf = wpabuf_mhead(msg);
+
+	/* User-Name */
+	pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+			       identity, identity_len);
+
+	/* MS-CHAP-Challenge */
+	challenge = eap_ttls_implicit_challenge(
+		sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1);
+	if (challenge == NULL) {
+		wpabuf_free(msg);
+		wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
+			   "implicit challenge");
+		return -1;
+	}
+
+	pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+			       RADIUS_VENDOR_ID_MICROSOFT, 1,
+			       challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+
+	/* MS-CHAP2-Response */
+	pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE,
+			       RADIUS_VENDOR_ID_MICROSOFT, 1,
+			       EAP_TTLS_MSCHAPV2_RESPONSE_LEN);
+	data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];
+	*pos++ = data->ident;
+	*pos++ = 0; /* Flags */
+	if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) {
+		os_free(challenge);
+		wpabuf_free(msg);
+		wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get "
+			   "random data for peer challenge");
+		return -1;
+	}
+	peer_challenge = pos;
+	pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
+	os_memset(pos, 0, 8); /* Reserved, must be zero */
+	pos += 8;
+	if (mschapv2_derive_response(identity, identity_len, password,
+				     password_len, pwhash, challenge,
+				     peer_challenge, pos, data->auth_response,
+				     data->master_key)) {
+		os_free(challenge);
+		wpabuf_free(msg);
+		wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
+			   "response");
+		return -1;
+	}
+	data->auth_response_valid = 1;
+
+	pos += 24;
+	os_free(challenge);
+	AVP_PAD(buf, pos);
+
+	wpabuf_put(msg, pos - buf);
+	*resp = msg;
+
+	return 0;
+#else /* EAP_MSCHAPv2 */
+	wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
+	return -1;
+#endif /* EAP_MSCHAPv2 */
+#endif /* CONFIG_FIPS */
+}
+
+
+static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
+					  struct eap_ttls_data *data,
+					  struct eap_method_ret *ret,
+					  struct wpabuf **resp)
+{
+#ifdef CONFIG_FIPS
+	wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAP not supported in FIPS build");
+	return -1;
+#else /* CONFIG_FIPS */
+	struct wpabuf *msg;
+	u8 *buf, *pos, *challenge;
+	const u8 *identity, *password;
+	size_t identity_len, password_len;
+	int pwhash;
+
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request");
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	password = eap_get_config_password2(sm, &password_len, &pwhash);
+	if (identity == NULL || password == NULL)
+		return -1;
+
+	msg = wpabuf_alloc(identity_len + 1000);
+	if (msg == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "EAP-TTLS/MSCHAP: Failed to allocate memory");
+		return -1;
+	}
+	pos = buf = wpabuf_mhead(msg);
+
+	/* User-Name */
+	pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+			       identity, identity_len);
+
+	/* MS-CHAP-Challenge */
+	challenge = eap_ttls_implicit_challenge(
+		sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);
+	if (challenge == NULL) {
+		wpabuf_free(msg);
+		wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive "
+			   "implicit challenge");
+		return -1;
+	}
+
+	pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+			       RADIUS_VENDOR_ID_MICROSOFT, 1,
+			       challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+
+	/* MS-CHAP-Response */
+	pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE,
+			       RADIUS_VENDOR_ID_MICROSOFT, 1,
+			       EAP_TTLS_MSCHAP_RESPONSE_LEN);
+	data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN];
+	*pos++ = data->ident;
+	*pos++ = 1; /* Flags: Use NT style passwords */
+	os_memset(pos, 0, 24); /* LM-Response */
+	pos += 24;
+	if (pwhash) {
+		challenge_response(challenge, password, pos); /* NT-Response */
+		wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash",
+				password, 16);
+	} else {
+		nt_challenge_response(challenge, password, password_len,
+				      pos); /* NT-Response */
+		wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password",
+				      password, password_len);
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge",
+		    challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24);
+	pos += 24;
+	os_free(challenge);
+	AVP_PAD(buf, pos);
+
+	wpabuf_put(msg, pos - buf);
+	*resp = msg;
+
+	/* EAP-TTLS/MSCHAP does not provide tunneled success
+	 * notification, so assume that Phase2 succeeds. */
+	ret->methodState = METHOD_DONE;
+	ret->decision = DECISION_COND_SUCC;
+
+	return 0;
+#endif /* CONFIG_FIPS */
+}
+
+
+static int eap_ttls_phase2_request_pap(struct eap_sm *sm,
+				       struct eap_ttls_data *data,
+				       struct eap_method_ret *ret,
+				       struct wpabuf **resp)
+{
+	struct wpabuf *msg;
+	u8 *buf, *pos;
+	size_t pad;
+	const u8 *identity, *password;
+	size_t identity_len, password_len;
+
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request");
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	password = eap_get_config_password(sm, &password_len);
+	if (identity == NULL || password == NULL)
+		return -1;
+
+	msg = wpabuf_alloc(identity_len + password_len + 100);
+	if (msg == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "EAP-TTLS/PAP: Failed to allocate memory");
+		return -1;
+	}
+	pos = buf = wpabuf_mhead(msg);
+
+	/* User-Name */
+	pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+			       identity, identity_len);
+
+	/* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts
+	 * the data, so no separate encryption is used in the AVP itself.
+	 * However, the password is padded to obfuscate its length. */
+	pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15;
+	pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1,
+			       password_len + pad);
+	os_memcpy(pos, password, password_len);
+	pos += password_len;
+	os_memset(pos, 0, pad);
+	pos += pad;
+	AVP_PAD(buf, pos);
+
+	wpabuf_put(msg, pos - buf);
+	*resp = msg;
+
+	/* EAP-TTLS/PAP does not provide tunneled success notification,
+	 * so assume that Phase2 succeeds. */
+	ret->methodState = METHOD_DONE;
+	ret->decision = DECISION_COND_SUCC;
+
+	return 0;
+}
+
+
+static int eap_ttls_phase2_request_chap(struct eap_sm *sm,
+					struct eap_ttls_data *data,
+					struct eap_method_ret *ret,
+					struct wpabuf **resp)
+{
+#ifdef CONFIG_FIPS
+	wpa_printf(MSG_ERROR, "EAP-TTLS: CHAP not supported in FIPS build");
+	return -1;
+#else /* CONFIG_FIPS */
+	struct wpabuf *msg;
+	u8 *buf, *pos, *challenge;
+	const u8 *identity, *password;
+	size_t identity_len, password_len;
+
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request");
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	password = eap_get_config_password(sm, &password_len);
+	if (identity == NULL || password == NULL)
+		return -1;
+
+	msg = wpabuf_alloc(identity_len + 1000);
+	if (msg == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "EAP-TTLS/CHAP: Failed to allocate memory");
+		return -1;
+	}
+	pos = buf = wpabuf_mhead(msg);
+
+	/* User-Name */
+	pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+			       identity, identity_len);
+
+	/* CHAP-Challenge */
+	challenge = eap_ttls_implicit_challenge(
+		sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1);
+	if (challenge == NULL) {
+		wpabuf_free(msg);
+		wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive "
+			   "implicit challenge");
+		return -1;
+	}
+
+	pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1,
+			       challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+
+	/* CHAP-Password */
+	pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1,
+			       1 + EAP_TTLS_CHAP_PASSWORD_LEN);
+	data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN];
+	*pos++ = data->ident;
+
+	/* MD5(Ident + Password + Challenge) */
+	chap_md5(data->ident, password, password_len, challenge,
+		 EAP_TTLS_CHAP_CHALLENGE_LEN, pos);
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username",
+			  identity, identity_len);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password",
+			      password, password_len);
+	wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge",
+		    challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password",
+		    pos, EAP_TTLS_CHAP_PASSWORD_LEN);
+	pos += EAP_TTLS_CHAP_PASSWORD_LEN;
+	os_free(challenge);
+	AVP_PAD(buf, pos);
+
+	wpabuf_put(msg, pos - buf);
+	*resp = msg;
+
+	/* EAP-TTLS/CHAP does not provide tunneled success
+	 * notification, so assume that Phase2 succeeds. */
+	ret->methodState = METHOD_DONE;
+	ret->decision = DECISION_COND_SUCC;
+
+	return 0;
+#endif /* CONFIG_FIPS */
+}
+
+
+static int eap_ttls_phase2_request(struct eap_sm *sm,
+				   struct eap_ttls_data *data,
+				   struct eap_method_ret *ret,
+				   struct eap_hdr *hdr,
+				   struct wpabuf **resp)
+{
+	int res = 0;
+	size_t len;
+	enum phase2_types phase2_type = data->phase2_type;
+
+#ifdef EAP_TNC
+	if (data->tnc_started) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC");
+		phase2_type = EAP_TTLS_PHASE2_EAP;
+	}
+#endif /* EAP_TNC */
+
+	if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 ||
+	    phase2_type == EAP_TTLS_PHASE2_MSCHAP ||
+	    phase2_type == EAP_TTLS_PHASE2_PAP ||
+	    phase2_type == EAP_TTLS_PHASE2_CHAP) {
+		if (eap_get_config_identity(sm, &len) == NULL) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TTLS: Identity not configured");
+			eap_sm_request_identity(sm);
+			if (eap_get_config_password(sm, &len) == NULL)
+				eap_sm_request_password(sm);
+			return 0;
+		}
+
+		if (eap_get_config_password(sm, &len) == NULL) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TTLS: Password not configured");
+			eap_sm_request_password(sm);
+			return 0;
+		}
+	}
+
+	switch (phase2_type) {
+	case EAP_TTLS_PHASE2_EAP:
+		res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp);
+		break;
+	case EAP_TTLS_PHASE2_MSCHAPV2:
+		res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp);
+		break;
+	case EAP_TTLS_PHASE2_MSCHAP:
+		res = eap_ttls_phase2_request_mschap(sm, data, ret, resp);
+		break;
+	case EAP_TTLS_PHASE2_PAP:
+		res = eap_ttls_phase2_request_pap(sm, data, ret, resp);
+		break;
+	case EAP_TTLS_PHASE2_CHAP:
+		res = eap_ttls_phase2_request_chap(sm, data, ret, resp);
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown");
+		res = -1;
+		break;
+	}
+
+	if (res < 0) {
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+	}
+
+	return res;
+}
+
+
+struct ttls_parse_avp {
+	u8 *mschapv2;
+	u8 *eapdata;
+	size_t eap_len;
+	int mschapv2_error;
+};
+
+
+static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen,
+				   struct ttls_parse_avp *parse)
+{
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
+	if (parse->eapdata == NULL) {
+		parse->eapdata = os_malloc(dlen);
+		if (parse->eapdata == NULL) {
+			wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+				   "memory for Phase 2 EAP data");
+			return -1;
+		}
+		os_memcpy(parse->eapdata, dpos, dlen);
+		parse->eap_len = dlen;
+	} else {
+		u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen);
+		if (neweap == NULL) {
+			wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+				   "memory for Phase 2 EAP data");
+			return -1;
+		}
+		os_memcpy(neweap + parse->eap_len, dpos, dlen);
+		parse->eapdata = neweap;
+		parse->eap_len += dlen;
+	}
+
+	return 0;
+}
+
+
+static int eap_ttls_parse_avp(u8 *pos, size_t left,
+			      struct ttls_parse_avp *parse)
+{
+	struct ttls_avp *avp;
+	u32 avp_code, avp_length, vendor_id = 0;
+	u8 avp_flags, *dpos;
+	size_t dlen;
+
+	avp = (struct ttls_avp *) pos;
+	avp_code = be_to_host32(avp->avp_code);
+	avp_length = be_to_host32(avp->avp_length);
+	avp_flags = (avp_length >> 24) & 0xff;
+	avp_length &= 0xffffff;
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x "
+		   "length=%d", (int) avp_code, avp_flags,
+		   (int) avp_length);
+
+	if (avp_length > left) {
+		wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow "
+			   "(len=%d, left=%lu) - dropped",
+			   (int) avp_length, (unsigned long) left);
+		return -1;
+	}
+
+	if (avp_length < sizeof(*avp)) {
+		wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length %d",
+			   avp_length);
+		return -1;
+	}
+
+	dpos = (u8 *) (avp + 1);
+	dlen = avp_length - sizeof(*avp);
+	if (avp_flags & AVP_FLAGS_VENDOR) {
+		if (dlen < 4) {
+			wpa_printf(MSG_WARNING, "EAP-TTLS: Vendor AVP "
+				   "underflow");
+			return -1;
+		}
+		vendor_id = WPA_GET_BE32(dpos);
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d",
+			   (int) vendor_id);
+		dpos += 4;
+		dlen -= 4;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen);
+
+	if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
+		if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0)
+			return -1;
+	} else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) {
+		/* This is an optional message that can be displayed to
+		 * the user. */
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message",
+				  dpos, dlen);
+	} else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+		   avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success",
+				  dpos, dlen);
+		if (dlen != 43) {
+			wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected "
+				   "MS-CHAP2-Success length "
+				   "(len=%lu, expected 43)",
+				   (unsigned long) dlen);
+			return -1;
+		}
+		parse->mschapv2 = dpos;
+	} else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+		   avp_code == RADIUS_ATTR_MS_CHAP_ERROR) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error",
+				  dpos, dlen);
+		parse->mschapv2_error = 1;
+	} else if (avp_flags & AVP_FLAGS_MANDATORY) {
+		wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported mandatory AVP "
+			   "code %d vendor_id %d - dropped",
+			   (int) avp_code, (int) vendor_id);
+		return -1;
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported AVP "
+			   "code %d vendor_id %d",
+			   (int) avp_code, (int) vendor_id);
+	}
+
+	return avp_length;
+}
+
+
+static int eap_ttls_parse_avps(struct wpabuf *in_decrypted,
+			       struct ttls_parse_avp *parse)
+{
+	u8 *pos;
+	size_t left, pad;
+	int avp_length;
+
+	pos = wpabuf_mhead(in_decrypted);
+	left = wpabuf_len(in_decrypted);
+	wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left);
+	if (left < sizeof(struct ttls_avp)) {
+		wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame"
+			   " len=%lu expected %lu or more - dropped",
+			   (unsigned long) left,
+			   (unsigned long) sizeof(struct ttls_avp));
+		return -1;
+	}
+
+	/* Parse AVPs */
+	os_memset(parse, 0, sizeof(*parse));
+
+	while (left > 0) {
+		avp_length = eap_ttls_parse_avp(pos, left, parse);
+		if (avp_length < 0)
+			return -1;
+
+		pad = (4 - (avp_length & 3)) & 3;
+		pos += avp_length + pad;
+		if (left < avp_length + pad)
+			left = 0;
+		else
+			left -= avp_length + pad;
+	}
+
+	return 0;
+}
+
+
+static u8 * eap_ttls_fake_identity_request(void)
+{
+	struct eap_hdr *hdr;
+	u8 *buf;
+
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of "
+		   "Phase 2 - use fake EAP-Request Identity");
+	buf = os_malloc(sizeof(*hdr) + 1);
+	if (buf == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate "
+			   "memory for fake EAP-Identity Request");
+		return NULL;
+	}
+
+	hdr = (struct eap_hdr *) buf;
+	hdr->code = EAP_CODE_REQUEST;
+	hdr->identifier = 0;
+	hdr->length = host_to_be16(sizeof(*hdr) + 1);
+	buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY;
+
+	return buf;
+}
+
+
+static int eap_ttls_encrypt_response(struct eap_sm *sm,
+				     struct eap_ttls_data *data,
+				     struct wpabuf *resp, u8 identifier,
+				     struct wpabuf **out_data)
+{
+	if (resp == NULL)
+		return 0;
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data",
+			    resp);
+	if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,
+				 data->ttls_version, identifier,
+				 resp, out_data)) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 "
+			   "frame");
+		wpabuf_free(resp);
+		return -1;
+	}
+	wpabuf_free(resp);
+
+	return 0;
+}
+
+
+static int eap_ttls_process_phase2_eap(struct eap_sm *sm,
+				       struct eap_ttls_data *data,
+				       struct eap_method_ret *ret,
+				       struct ttls_parse_avp *parse,
+				       struct wpabuf **resp)
+{
+	struct eap_hdr *hdr;
+	size_t len;
+
+	if (parse->eapdata == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the "
+			   "packet - dropped");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP",
+		    parse->eapdata, parse->eap_len);
+	hdr = (struct eap_hdr *) parse->eapdata;
+
+	if (parse->eap_len < sizeof(*hdr)) {
+		wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP "
+			   "frame (len=%lu, expected %lu or more) - dropped",
+			   (unsigned long) parse->eap_len,
+			   (unsigned long) sizeof(*hdr));
+		return -1;
+	}
+	len = be_to_host16(hdr->length);
+	if (len > parse->eap_len) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 "
+			   "EAP frame (EAP hdr len=%lu, EAP data len in "
+			   "AVP=%lu)",
+			   (unsigned long) len,
+			   (unsigned long) parse->eap_len);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d "
+		   "identifier=%d length=%lu",
+		   hdr->code, hdr->identifier, (unsigned long) len);
+	switch (hdr->code) {
+	case EAP_CODE_REQUEST:
+		if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) {
+			wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
+				   "processing failed");
+			return -1;
+		}
+		break;
+	default:
+		wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in "
+			   "Phase 2 EAP header", hdr->code);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
+					    struct eap_ttls_data *data,
+					    struct eap_method_ret *ret,
+					    struct ttls_parse_avp *parse)
+{
+#ifdef EAP_MSCHAPv2
+	if (parse->mschapv2_error) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received "
+			   "MS-CHAP-Error - failed");
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		/* Reply with empty data to ACK error */
+		return 1;
+	}
+
+	if (parse->mschapv2 == NULL) {
+#ifdef EAP_TNC
+		if (data->phase2_success && parse->eapdata) {
+			/*
+			 * Allow EAP-TNC to be started after successfully
+			 * completed MSCHAPV2.
+			 */
+			return 1;
+		}
+#endif /* EAP_TNC */
+		wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success AVP "
+			   "received for Phase2 MSCHAPV2");
+		return -1;
+	}
+	if (parse->mschapv2[0] != data->ident) {
+		wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch for Phase 2 "
+			   "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)",
+			   parse->mschapv2[0], data->ident);
+		return -1;
+	}
+	if (!data->auth_response_valid ||
+	    mschapv2_verify_auth_response(data->auth_response,
+					  parse->mschapv2 + 1, 42)) {
+		wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid authenticator "
+			   "response in Phase 2 MSCHAPV2 success request");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 "
+		   "authentication succeeded");
+	ret->methodState = METHOD_DONE;
+	ret->decision = DECISION_UNCOND_SUCC;
+	data->phase2_success = 1;
+
+	/*
+	 * Reply with empty data; authentication server will reply
+	 * with EAP-Success after this.
+	 */
+	return 1;
+#else /* EAP_MSCHAPv2 */
+	wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
+	return -1;
+#endif /* EAP_MSCHAPv2 */
+}
+
+
+#ifdef EAP_TNC
+static int eap_ttls_process_tnc_start(struct eap_sm *sm,
+				      struct eap_ttls_data *data,
+				      struct eap_method_ret *ret,
+				      struct ttls_parse_avp *parse,
+				      struct wpabuf **resp)
+{
+	/* TNC uses inner EAP method after non-EAP TTLS phase 2. */
+	if (parse->eapdata == NULL) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received "
+			   "unexpected tunneled data (no EAP)");
+		return -1;
+	}
+
+	if (!data->ready_for_tnc) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received "
+			   "EAP after non-EAP, but not ready for TNC");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
+		   "non-EAP method");
+	data->tnc_started = 1;
+
+	if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0)
+		return -1;
+
+	return 0;
+}
+#endif /* EAP_TNC */
+
+
+static int eap_ttls_process_decrypted(struct eap_sm *sm,
+				      struct eap_ttls_data *data,
+				      struct eap_method_ret *ret,
+				      u8 identifier,
+				      struct ttls_parse_avp *parse,
+				      struct wpabuf *in_decrypted,
+				      struct wpabuf **out_data)
+{
+	struct wpabuf *resp = NULL;
+	struct eap_peer_config *config = eap_get_config(sm);
+	int res;
+	enum phase2_types phase2_type = data->phase2_type;
+
+#ifdef EAP_TNC
+	if (data->tnc_started)
+		phase2_type = EAP_TTLS_PHASE2_EAP;
+#endif /* EAP_TNC */
+
+	switch (phase2_type) {
+	case EAP_TTLS_PHASE2_EAP:
+		if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) <
+		    0)
+			return -1;
+		break;
+	case EAP_TTLS_PHASE2_MSCHAPV2:
+		res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse);
+#ifdef EAP_TNC
+		if (res == 1 && parse->eapdata && data->phase2_success) {
+			/*
+			 * TNC may be required as the next
+			 * authentication method within the tunnel.
+			 */
+			ret->methodState = METHOD_MAY_CONT;
+			data->ready_for_tnc = 1;
+			if (eap_ttls_process_tnc_start(sm, data, ret, parse,
+						       &resp) == 0)
+				break;
+		}
+#endif /* EAP_TNC */
+		return res;
+	case EAP_TTLS_PHASE2_MSCHAP:
+	case EAP_TTLS_PHASE2_PAP:
+	case EAP_TTLS_PHASE2_CHAP:
+#ifdef EAP_TNC
+		if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) <
+		    0)
+			return -1;
+		break;
+#else /* EAP_TNC */
+		/* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled
+		 * requests to the supplicant */
+		wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected "
+			   "tunneled data");
+		return -1;
+#endif /* EAP_TNC */
+	}
+
+	if (resp) {
+		if (eap_ttls_encrypt_response(sm, data, resp, identifier,
+					      out_data) < 0)
+			return -1;
+	} else if (config->pending_req_identity ||
+		   config->pending_req_password ||
+		   config->pending_req_otp ||
+		   config->pending_req_new_password) {
+		wpabuf_free(data->pending_phase2_req);
+		data->pending_phase2_req = wpabuf_dup(in_decrypted);
+	}
+
+	return 0;
+}
+
+
+static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
+					      struct eap_ttls_data *data,
+					      struct eap_method_ret *ret,
+					      u8 identifier,
+					      struct wpabuf **out_data)
+{
+	int retval = 0;
+	struct eap_hdr *hdr;
+	struct wpabuf *resp;
+
+	hdr = (struct eap_hdr *) eap_ttls_fake_identity_request();
+	if (hdr == NULL) {
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		return -1;
+	}
+
+	resp = NULL;
+	if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
+			   "processing failed");
+		retval = -1;
+	} else {
+		struct eap_peer_config *config = eap_get_config(sm);
+		if (resp == NULL &&
+		    (config->pending_req_identity ||
+		     config->pending_req_password ||
+		     config->pending_req_otp ||
+		     config->pending_req_new_password)) {
+			/*
+			 * Use empty buffer to force implicit request
+			 * processing when EAP request is re-processed after
+			 * user input.
+			 */
+			wpabuf_free(data->pending_phase2_req);
+			data->pending_phase2_req = wpabuf_alloc(0);
+		}
+
+		retval = eap_ttls_encrypt_response(sm, data, resp, identifier,
+						   out_data);
+	}
+
+	os_free(hdr);
+
+	if (retval < 0) {
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+	}
+
+	return retval;
+}
+
+
+static int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data,
+				 struct eap_method_ret *ret, u8 identifier,
+				 struct wpabuf **out_data)
+{
+	data->phase2_start = 0;
+
+	/*
+	 * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only
+	 * if TLS part was indeed resuming a previous session. Most
+	 * Authentication Servers terminate EAP-TTLS before reaching this
+	 * point, but some do not. Make wpa_supplicant stop phase 2 here, if
+	 * needed.
+	 */
+	if (data->reauth &&
+	    tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - "
+			   "skip phase 2");
+		*out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS,
+						   data->ttls_version);
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_UNCOND_SUCC;
+		data->phase2_success = 1;
+		return 0;
+	}
+
+	return eap_ttls_implicit_identity_request(sm, data, ret, identifier,
+						  out_data);
+}
+
+
+static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data,
+			    struct eap_method_ret *ret, u8 identifier,
+			    const struct wpabuf *in_data,
+			    struct wpabuf **out_data)
+{
+	struct wpabuf *in_decrypted = NULL;
+	int retval = 0;
+	struct ttls_parse_avp parse;
+
+	os_memset(&parse, 0, sizeof(parse));
+
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"
+		   " Phase 2",
+		   in_data ? (unsigned long) wpabuf_len(in_data) : 0);
+
+	if (data->pending_phase2_req) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - "
+			   "skip decryption and use old data");
+		/* Clear TLS reassembly state. */
+		eap_peer_tls_reset_input(&data->ssl);
+
+		in_decrypted = data->pending_phase2_req;
+		data->pending_phase2_req = NULL;
+		if (wpabuf_len(in_decrypted) == 0) {
+			wpabuf_free(in_decrypted);
+			return eap_ttls_implicit_identity_request(
+				sm, data, ret, identifier, out_data);
+		}
+		goto continue_req;
+	}
+
+	if ((in_data == NULL || wpabuf_len(in_data) == 0) &&
+	    data->phase2_start) {
+		return eap_ttls_phase2_start(sm, data, ret, identifier,
+					     out_data);
+	}
+
+	if (in_data == NULL || wpabuf_len(in_data) == 0) {
+		/* Received TLS ACK - requesting more fragments */
+		return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,
+					    data->ttls_version,
+					    identifier, NULL, out_data);
+	}
+
+	retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+	if (retval)
+		goto done;
+
+continue_req:
+	data->phase2_start = 0;
+
+	if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) {
+		retval = -1;
+		goto done;
+	}
+
+	retval = eap_ttls_process_decrypted(sm, data, ret, identifier,
+					    &parse, in_decrypted, out_data);
+
+done:
+	wpabuf_free(in_decrypted);
+	os_free(parse.eapdata);
+
+	if (retval < 0) {
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+	}
+
+	return retval;
+}
+
+
+static int eap_ttls_process_handshake(struct eap_sm *sm,
+				      struct eap_ttls_data *data,
+				      struct eap_method_ret *ret,
+				      u8 identifier,
+				      const struct wpabuf *in_data,
+				      struct wpabuf **out_data)
+{
+	int res;
+
+	res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,
+					  data->ttls_version, identifier,
+					  in_data, out_data);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS processing failed");
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		return -1;
+	}
+
+	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "
+			   "Phase 2");
+		if (data->resuming) {
+			wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may "
+				   "skip Phase 2");
+			ret->decision = DECISION_COND_SUCC;
+			ret->methodState = METHOD_MAY_CONT;
+		}
+		data->phase2_start = 1;
+		eap_ttls_v0_derive_key(sm, data);
+
+		if (*out_data == NULL || wpabuf_len(*out_data) == 0) {
+			if (eap_ttls_decrypt(sm, data, ret, identifier,
+					     NULL, out_data)) {
+				wpa_printf(MSG_WARNING, "EAP-TTLS: "
+					   "failed to process early "
+					   "start for Phase 2");
+			}
+			res = 0;
+		}
+		data->resuming = 0;
+	}
+
+	if (res == 2) {
+		/*
+		 * Application data included in the handshake message.
+		 */
+		wpabuf_free(data->pending_phase2_req);
+		data->pending_phase2_req = *out_data;
+		*out_data = NULL;
+		res = eap_ttls_decrypt(sm, data, ret, identifier, in_data,
+				       out_data);
+	}
+
+	return res;
+}
+
+
+static void eap_ttls_check_auth_status(struct eap_sm *sm, 
+				       struct eap_ttls_data *data,
+				       struct eap_method_ret *ret)
+{
+	if (ret->methodState == METHOD_DONE) {
+		ret->allowNotifications = FALSE;
+		if (ret->decision == DECISION_UNCOND_SUCC ||
+		    ret->decision == DECISION_COND_SUCC) {
+			wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
+				   "completed successfully");
+			data->phase2_success = 1;
+#ifdef EAP_TNC
+			if (!data->ready_for_tnc && !data->tnc_started) {
+				/*
+				 * TNC may be required as the next
+				 * authentication method within the tunnel.
+				 */
+				ret->methodState = METHOD_MAY_CONT;
+				data->ready_for_tnc = 1;
+			}
+#endif /* EAP_TNC */
+		}
+	} else if (ret->methodState == METHOD_MAY_CONT &&
+		   (ret->decision == DECISION_UNCOND_SUCC ||
+		    ret->decision == DECISION_COND_SUCC)) {
+			wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
+				   "completed successfully (MAY_CONT)");
+			data->phase2_success = 1;
+	}
+}
+
+
+static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
+					struct eap_method_ret *ret,
+					const struct wpabuf *reqData)
+{
+	size_t left;
+	int res;
+	u8 flags, id;
+	struct wpabuf *resp;
+	const u8 *pos;
+	struct eap_ttls_data *data = priv;
+	struct wpabuf msg;
+
+	pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret,
+					reqData, &left, &flags);
+	if (pos == NULL)
+		return NULL;
+	id = eap_get_id(reqData);
+
+	if (flags & EAP_TLS_FLAGS_START) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own "
+			   "ver=%d)", flags & EAP_TLS_VERSION_MASK,
+			   data->ttls_version);
+
+		/* RFC 5281, Ch. 9.2:
+		 * "This packet MAY contain additional information in the form
+		 * of AVPs, which may provide useful hints to the client"
+		 * For now, ignore any potential extra data.
+		 */
+		left = 0;
+	}
+
+	wpabuf_set(&msg, pos, left);
+
+	resp = NULL;
+	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+	    !data->resuming) {
+		res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp);
+	} else {
+		res = eap_ttls_process_handshake(sm, data, ret, id,
+						 &msg, &resp);
+	}
+
+	eap_ttls_check_auth_status(sm, data, ret);
+
+	/* FIX: what about res == -1? Could just move all error processing into
+	 * the other functions and get rid of this res==1 case here. */
+	if (res == 1) {
+		wpabuf_free(resp);
+		return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS,
+					      data->ttls_version);
+	}
+	return resp;
+}
+
+
+static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+	struct eap_ttls_data *data = priv;
+	return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+		data->phase2_success;
+}
+
+
+static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+	struct eap_ttls_data *data = priv;
+	wpabuf_free(data->pending_phase2_req);
+	data->pending_phase2_req = NULL;
+#ifdef EAP_TNC
+	data->ready_for_tnc = 0;
+	data->tnc_started = 0;
+#endif /* EAP_TNC */
+}
+
+
+static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+	struct eap_ttls_data *data = priv;
+	eap_ttls_free_key(data);
+	os_free(data->session_id);
+	data->session_id = NULL;
+	if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+		os_free(data);
+		return NULL;
+	}
+	if (data->phase2_priv && data->phase2_method &&
+	    data->phase2_method->init_for_reauth)
+		data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+	data->phase2_start = 0;
+	data->phase2_success = 0;
+	data->resuming = 1;
+	data->reauth = 1;
+	return priv;
+}
+
+
+static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
+			       size_t buflen, int verbose)
+{
+	struct eap_ttls_data *data = priv;
+	int len, ret;
+
+	len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+	ret = os_snprintf(buf + len, buflen - len,
+			  "EAP-TTLSv%d Phase2 method=",
+			  data->ttls_version);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+	switch (data->phase2_type) {
+	case EAP_TTLS_PHASE2_EAP:
+		ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n",
+				  data->phase2_method ?
+				  data->phase2_method->name : "?");
+		break;
+	case EAP_TTLS_PHASE2_MSCHAPV2:
+		ret = os_snprintf(buf + len, buflen - len, "MSCHAPV2\n");
+		break;
+	case EAP_TTLS_PHASE2_MSCHAP:
+		ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n");
+		break;
+	case EAP_TTLS_PHASE2_PAP:
+		ret = os_snprintf(buf + len, buflen - len, "PAP\n");
+		break;
+	case EAP_TTLS_PHASE2_CHAP:
+		ret = os_snprintf(buf + len, buflen - len, "CHAP\n");
+		break;
+	default:
+		ret = 0;
+		break;
+	}
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	return len;
+}
+
+
+static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_ttls_data *data = priv;
+	return data->key_data != NULL && data->phase2_success;
+}
+
+
+static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ttls_data *data = priv;
+	u8 *key;
+
+	if (data->key_data == NULL || !data->phase2_success)
+		return NULL;
+
+	key = os_malloc(EAP_TLS_KEY_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_TLS_KEY_LEN;
+	os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+	return key;
+}
+
+
+static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ttls_data *data = priv;
+	u8 *id;
+
+	if (data->session_id == NULL || !data->phase2_success)
+		return NULL;
+
+	id = os_malloc(data->id_len);
+	if (id == NULL)
+		return NULL;
+
+	*len = data->id_len;
+	os_memcpy(id, data->session_id, data->id_len);
+
+	return id;
+}
+
+
+static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ttls_data *data = priv;
+	u8 *key;
+
+	if (data->key_data == NULL)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_EMSK_LEN;
+	os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
+
+	return key;
+}
+
+
+int eap_peer_ttls_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_ttls_init;
+	eap->deinit = eap_ttls_deinit;
+	eap->process = eap_ttls_process;
+	eap->isKeyAvailable = eap_ttls_isKeyAvailable;
+	eap->getKey = eap_ttls_getKey;
+	eap->getSessionId = eap_ttls_get_session_id;
+	eap->get_status = eap_ttls_get_status;
+	eap->has_reauth_data = eap_ttls_has_reauth_data;
+	eap->deinit_for_reauth = eap_ttls_deinit_for_reauth;
+	eap->init_for_reauth = eap_ttls_init_for_reauth;
+	eap->get_emsk = eap_ttls_get_emsk;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_vendor_test.c b/hostap/src/eap_peer/eap_vendor_test.c
new file mode 100644
index 0000000..b61057e
--- /dev/null
+++ b/hostap/src/eap_peer/eap_vendor_test.c
@@ -0,0 +1,190 @@
+/*
+ * EAP peer method: Test method for vendor specific (expanded) EAP type
+ * Copyright (c) 2005-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements a vendor specific test method using EAP expanded types.
+ * This is only for test use and must not be used for authentication since no
+ * security is provided.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eloop.h"
+
+
+#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP
+#define EAP_VENDOR_TYPE 0xfcfbfaf9
+
+
+struct eap_vendor_test_data {
+	enum { INIT, CONFIRM, SUCCESS } state;
+	int first_try;
+	int test_pending_req;
+};
+
+
+static void * eap_vendor_test_init(struct eap_sm *sm)
+{
+	struct eap_vendor_test_data *data;
+	const u8 *password;
+	size_t password_len;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = INIT;
+	data->first_try = 1;
+
+	password = eap_get_config_password(sm, &password_len);
+	data->test_pending_req = password && password_len == 7 &&
+		os_memcmp(password, "pending", 7) == 0;
+
+	return data;
+}
+
+
+static void eap_vendor_test_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_vendor_test_data *data = priv;
+	os_free(data);
+}
+
+
+static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx)
+{
+	struct eap_sm *sm = eloop_ctx;
+	wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Ready to re-process pending "
+		   "request");
+	eap_notify_pending(sm);
+}
+
+
+static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv,
+					       struct eap_method_ret *ret,
+					       const struct wpabuf *reqData)
+{
+	struct eap_vendor_test_data *data = priv;
+	struct wpabuf *resp;
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, reqData, &len);
+	if (pos == NULL || len < 1) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (data->state == INIT && *pos != 1) {
+		wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
+			   "%d in INIT state", *pos);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (data->state == CONFIRM && *pos != 3) {
+		wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
+			   "%d in CONFIRM state", *pos);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (data->state == SUCCESS) {
+		wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
+			   "in SUCCESS state");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (data->state == CONFIRM) {
+		if (data->test_pending_req && data->first_try) {
+			data->first_try = 0;
+			wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Testing "
+				   "pending request");
+			ret->ignore = TRUE;
+			eloop_register_timeout(1, 0, eap_vendor_ready, sm,
+					       NULL);
+			return NULL;
+		}
+	}
+
+	ret->ignore = FALSE;
+
+	wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Generating Response");
+	ret->allowNotifications = TRUE;
+
+	resp = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1,
+			     EAP_CODE_RESPONSE, eap_get_id(reqData));
+	if (resp == NULL)
+		return NULL;
+
+	if (data->state == INIT) {
+		wpabuf_put_u8(resp, 2);
+		data->state = CONFIRM;
+		ret->methodState = METHOD_CONT;
+		ret->decision = DECISION_FAIL;
+	} else {
+		wpabuf_put_u8(resp, 4);
+		data->state = SUCCESS;
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_UNCOND_SUCC;
+	}
+
+	return resp;
+}
+
+
+static Boolean eap_vendor_test_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_vendor_test_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_vendor_test_data *data = priv;
+	u8 *key;
+	const int key_len = 64;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(key_len);
+	if (key == NULL)
+		return NULL;
+
+	os_memset(key, 0x11, key_len / 2);
+	os_memset(key + key_len / 2, 0x22, key_len / 2);
+	*len = key_len;
+
+	return key;
+}
+
+
+int eap_peer_vendor_test_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_ID, EAP_VENDOR_TYPE,
+				    "VENDOR-TEST");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_vendor_test_init;
+	eap->deinit = eap_vendor_test_deinit;
+	eap->process = eap_vendor_test_process;
+	eap->isKeyAvailable = eap_vendor_test_isKeyAvailable;
+	eap->getKey = eap_vendor_test_getKey;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/eap_wsc.c b/hostap/src/eap_peer/eap_wsc.c
new file mode 100644
index 0000000..7ac99c7
--- /dev/null
+++ b/hostap/src/eap_peer/eap_wsc.c
@@ -0,0 +1,598 @@
+/*
+ * EAP-WSC peer for Wi-Fi Protected Setup
+ * Copyright (c) 2007-2009, 2012, 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 "uuid.h"
+#include "eap_i.h"
+#include "eap_common/eap_wsc_common.h"
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
+
+
+struct eap_wsc_data {
+	enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
+	int registrar;
+	struct wpabuf *in_buf;
+	struct wpabuf *out_buf;
+	enum wsc_op_code in_op_code, out_op_code;
+	size_t out_used;
+	size_t fragment_size;
+	struct wps_data *wps;
+	struct wps_context *wps_ctx;
+};
+
+
+static const char * eap_wsc_state_txt(int state)
+{
+	switch (state) {
+	case WAIT_START:
+		return "WAIT_START";
+	case MESG:
+		return "MESG";
+	case FRAG_ACK:
+		return "FRAG_ACK";
+	case WAIT_FRAG_ACK:
+		return "WAIT_FRAG_ACK";
+	case DONE:
+		return "DONE";
+	case FAIL:
+		return "FAIL";
+	default:
+		return "?";
+	}
+}
+
+
+static void eap_wsc_state(struct eap_wsc_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
+		   eap_wsc_state_txt(data->state),
+		   eap_wsc_state_txt(state));
+	data->state = state;
+}
+
+
+static int eap_wsc_new_ap_settings(struct wps_credential *cred,
+				   const char *params)
+{
+	const char *pos, *end;
+	size_t len;
+
+	os_memset(cred, 0, sizeof(*cred));
+
+	pos = os_strstr(params, "new_ssid=");
+	if (pos == NULL)
+		return 0;
+	pos += 9;
+	end = os_strchr(pos, ' ');
+	if (end == NULL)
+		len = os_strlen(pos);
+	else
+		len = end - pos;
+	if ((len & 1) || len > 2 * sizeof(cred->ssid) ||
+	    hexstr2bin(pos, cred->ssid, len / 2)) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_ssid");
+		return -1;
+	}
+	cred->ssid_len = len / 2;
+
+	pos = os_strstr(params, "new_auth=");
+	if (pos == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_auth");
+		return -1;
+	}
+	if (os_strncmp(pos + 9, "OPEN", 4) == 0)
+		cred->auth_type = WPS_AUTH_OPEN;
+	else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0)
+		cred->auth_type = WPS_AUTH_WPAPSK;
+	else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0)
+		cred->auth_type = WPS_AUTH_WPA2PSK;
+	else {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_auth");
+		return -1;
+	}
+
+	pos = os_strstr(params, "new_encr=");
+	if (pos == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_encr");
+		return -1;
+	}
+	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)
+		cred->encr_type = WPS_ENCR_AES;
+	else {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_encr");
+		return -1;
+	}
+
+	pos = os_strstr(params, "new_key=");
+	if (pos == NULL)
+		return 0;
+	pos += 8;
+	end = os_strchr(pos, ' ');
+	if (end == NULL)
+		len = os_strlen(pos);
+	else
+		len = end - pos;
+	if ((len & 1) || len > 2 * sizeof(cred->key) ||
+	    hexstr2bin(pos, cred->key, len / 2)) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_key");
+		return -1;
+	}
+	cred->key_len = len / 2;
+
+	return 1;
+}
+
+
+static void * eap_wsc_init(struct eap_sm *sm)
+{
+	struct eap_wsc_data *data;
+	const u8 *identity;
+	size_t identity_len;
+	int registrar;
+	struct wps_config cfg;
+	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) {
+		wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available");
+		return NULL;
+	}
+
+	identity = eap_get_config_identity(sm, &identity_len);
+
+	if (identity && identity_len == WSC_ID_REGISTRAR_LEN &&
+	    os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0)
+		registrar = 1; /* Supplicant is Registrar */
+	else if (identity && identity_len == WSC_ID_ENROLLEE_LEN &&
+	    os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0)
+		registrar = 0; /* Supplicant is Enrollee */
+	else {
+		wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
+				  identity, identity_len);
+		return NULL;
+	}
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = registrar ? MESG : WAIT_START;
+	data->registrar = registrar;
+	data->wps_ctx = wps;
+
+	os_memset(&cfg, 0, sizeof(cfg));
+	cfg.wps = wps;
+	cfg.registrar = registrar;
+
+	phase1 = eap_get_config_phase1(sm);
+	if (phase1 == NULL) {
+		wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not "
+			   "set");
+		os_free(data);
+		return NULL;
+	}
+
+	pos = os_strstr(phase1, "pin=");
+	if (pos) {
+		pos += 4;
+		cfg.pin = (const u8 *) pos;
+		while (*pos != '\0' && *pos != ' ')
+			pos++;
+		cfg.pin_len = pos - (const char *) cfg.pin;
+		if (cfg.pin_len == 6 &&
+		    os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) {
+			cfg.pin = NULL;
+			cfg.pin_len = 0;
+			nfc = 1;
+		}
+	} else {
+		pos = os_strstr(phase1, "pbc=1");
+		if (pos)
+			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");
+		os_free(data);
+		return NULL;
+	}
+
+	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) {
+		os_free(data);
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to parse new AP "
+			   "settings");
+		return NULL;
+	}
+	if (res == 1) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for "
+			   "WPS");
+		cfg.new_ap_settings = &new_ap_settings;
+	}
+
+	data->wps = wps_init(&cfg);
+	if (data->wps == NULL) {
+		os_free(data);
+		wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed");
+		return NULL;
+	}
+	res = eap_get_config_fragment_size(sm);
+	if (res > 0)
+		data->fragment_size = res;
+	else
+		data->fragment_size = WSC_FRAGMENT_SIZE;
+	wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u",
+		   (unsigned int) data->fragment_size);
+
+	if (registrar && cfg.pin) {
+		wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
+				      cfg.pin, cfg.pin_len, 0);
+	}
+
+	/* Use reduced client timeout for WPS to avoid long wait */
+	if (sm->ClientTimeout > 30)
+		sm->ClientTimeout = 30;
+
+	return data;
+}
+
+
+static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_wsc_data *data = priv;
+	wpabuf_free(data->in_buf);
+	wpabuf_free(data->out_buf);
+	wps_deinit(data->wps);
+	os_free(data->wps_ctx->network_key);
+	data->wps_ctx->network_key = NULL;
+	os_free(data);
+}
+
+
+static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
+					 struct eap_method_ret *ret, u8 id)
+{
+	struct wpabuf *resp;
+	u8 flags;
+	size_t send_len, plen;
+
+	ret->ignore = FALSE;
+	wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
+	ret->allowNotifications = TRUE;
+
+	flags = 0;
+	send_len = wpabuf_len(data->out_buf) - data->out_used;
+	if (2 + send_len > data->fragment_size) {
+		send_len = data->fragment_size - 2;
+		flags |= WSC_FLAGS_MF;
+		if (data->out_used == 0) {
+			flags |= WSC_FLAGS_LF;
+			send_len -= 2;
+		}
+	}
+	plen = 2 + send_len;
+	if (flags & WSC_FLAGS_LF)
+		plen += 2;
+	resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
+			     EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+
+	wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
+	wpabuf_put_u8(resp, flags); /* Flags */
+	if (flags & WSC_FLAGS_LF)
+		wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
+
+	wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
+			send_len);
+	data->out_used += send_len;
+
+	ret->methodState = METHOD_MAY_CONT;
+	ret->decision = DECISION_FAIL;
+
+	if (data->out_used == wpabuf_len(data->out_buf)) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
+			   "(message sent completely)",
+			   (unsigned long) send_len);
+		wpabuf_free(data->out_buf);
+		data->out_buf = NULL;
+		data->out_used = 0;
+		if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
+		    data->out_op_code == WSC_NACK ||
+		    data->out_op_code == WSC_Done) {
+			eap_wsc_state(data, FAIL);
+			ret->methodState = METHOD_DONE;
+		} else
+			eap_wsc_state(data, MESG);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
+			   "(%lu more to send)", (unsigned long) send_len,
+			   (unsigned long) wpabuf_len(data->out_buf) -
+			   data->out_used);
+		eap_wsc_state(data, WAIT_FRAG_ACK);
+	}
+
+	return resp;
+}
+
+
+static int eap_wsc_process_cont(struct eap_wsc_data *data,
+				const u8 *buf, size_t len, u8 op_code)
+{
+	/* Process continuation of a pending message */
+	if (op_code != data->in_op_code) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
+			   "fragment (expected %d)",
+			   op_code, data->in_op_code);
+		return -1;
+	}
+
+	if (len > wpabuf_tailroom(data->in_buf)) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
+		eap_wsc_state(data, FAIL);
+		return -1;
+	}
+
+	wpabuf_put_data(data->in_buf, buf, len);
+	wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting "
+		   "for %lu bytes more", (unsigned long) len,
+		   (unsigned long) wpabuf_tailroom(data->in_buf));
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
+						struct eap_method_ret *ret,
+						u8 id, u8 flags, u8 op_code,
+						u16 message_length,
+						const u8 *buf, size_t len)
+{
+	/* Process a fragment that is not the last one of the message */
+	if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a "
+			   "fragmented packet");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (data->in_buf == NULL) {
+		/* First fragment of the message */
+		data->in_buf = wpabuf_alloc(message_length);
+		if (data->in_buf == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
+				   "message");
+			ret->ignore = TRUE;
+			return NULL;
+		}
+		data->in_op_code = op_code;
+		wpabuf_put_data(data->in_buf, buf, len);
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first "
+			   "fragment, waiting for %lu bytes more",
+			   (unsigned long) len,
+			   (unsigned long) wpabuf_tailroom(data->in_buf));
+	}
+
+	return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
+}
+
+
+static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
+				       struct eap_method_ret *ret,
+				       const struct wpabuf *reqData)
+{
+	struct eap_wsc_data *data = priv;
+	const u8 *start, *pos, *end;
+	size_t len;
+	u8 op_code, flags, id;
+	u16 message_length = 0;
+	enum wps_process_res res;
+	struct wpabuf tmpbuf;
+	struct wpabuf *r;
+
+	pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
+			       &len);
+	if (pos == NULL || len < 2) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	id = eap_get_id(reqData);
+
+	start = pos;
+	end = start + len;
+
+	op_code = *pos++;
+	flags = *pos++;
+	if (flags & WSC_FLAGS_LF) {
+		if (end - pos < 2) {
+			wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
+			ret->ignore = TRUE;
+			return NULL;
+		}
+		message_length = WPA_GET_BE16(pos);
+		pos += 2;
+
+		if (message_length < end - pos || message_length > 50000) {
+			wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
+				   "Length");
+			ret->ignore = TRUE;
+			return NULL;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
+		   "Flags 0x%x Message Length %d",
+		   op_code, flags, message_length);
+
+	if (data->state == WAIT_FRAG_ACK) {
+		if (op_code != WSC_FRAG_ACK) {
+			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
+				   "in WAIT_FRAG_ACK state", op_code);
+			ret->ignore = TRUE;
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
+		eap_wsc_state(data, MESG);
+		return eap_wsc_build_msg(data, ret, id);
+	}
+
+	if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
+	    op_code != WSC_Done && op_code != WSC_Start) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
+			   op_code);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (data->state == WAIT_START) {
+		if (op_code != WSC_Start) {
+			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
+				   "in WAIT_START state", op_code);
+			ret->ignore = TRUE;
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
+		eap_wsc_state(data, MESG);
+		/* Start message has empty payload, skip processing */
+		goto send_msg;
+	} else if (op_code == WSC_Start) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
+			   op_code);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (data->in_buf &&
+	    eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (flags & WSC_FLAGS_MF) {
+		return eap_wsc_process_fragment(data, ret, id, flags, op_code,
+						message_length, pos,
+						end - pos);
+	}
+
+	if (data->in_buf == NULL) {
+		/* Wrap unfragmented messages as wpabuf without extra copy */
+		wpabuf_set(&tmpbuf, pos, end - pos);
+		data->in_buf = &tmpbuf;
+	}
+
+	res = wps_process_msg(data->wps, op_code, data->in_buf);
+	switch (res) {
+	case WPS_DONE:
+		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
+			   "successfully - wait for EAP failure");
+		eap_wsc_state(data, FAIL);
+		break;
+	case WPS_CONTINUE:
+		eap_wsc_state(data, MESG);
+		break;
+	case WPS_FAILURE:
+	case WPS_PENDING:
+		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
+		eap_wsc_state(data, FAIL);
+		break;
+	}
+
+	if (data->in_buf != &tmpbuf)
+		wpabuf_free(data->in_buf);
+	data->in_buf = NULL;
+
+send_msg:
+	if (data->out_buf == NULL) {
+		data->out_buf = wps_get_msg(data->wps, &data->out_op_code);
+		if (data->out_buf == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
+				   "message from WPS");
+			eap_wsc_state(data, FAIL);
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			return NULL;
+		}
+		data->out_used = 0;
+	}
+
+	eap_wsc_state(data, MESG);
+	r = eap_wsc_build_msg(data, ret, id);
+	if (data->state == FAIL && ret->methodState == METHOD_DONE) {
+		/* Use reduced client timeout for WPS to avoid long wait */
+		if (sm->ClientTimeout > 2)
+			sm->ClientTimeout = 2;
+	}
+	return r;
+}
+
+
+int eap_peer_wsc_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
+				    "WSC");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_wsc_init;
+	eap->deinit = eap_wsc_deinit;
+	eap->process = eap_wsc_process;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_peer/ikev2.c b/hostap/src/eap_peer/ikev2.c
new file mode 100644
index 0000000..55ab72a
--- /dev/null
+++ b/hostap/src/eap_peer/ikev2.c
@@ -0,0 +1,1241 @@
+/*
+ * IKEv2 responder (RFC 4306) for EAP-IKEV2
+ * Copyright (c) 2007, 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/dh_groups.h"
+#include "crypto/random.h"
+#include "ikev2.h"
+
+
+void ikev2_responder_deinit(struct ikev2_responder_data *data)
+{
+	ikev2_free_keys(&data->keys);
+	wpabuf_free(data->i_dh_public);
+	wpabuf_free(data->r_dh_private);
+	os_free(data->IDi);
+	os_free(data->IDr);
+	os_free(data->shared_secret);
+	wpabuf_free(data->i_sign_msg);
+	wpabuf_free(data->r_sign_msg);
+	os_free(data->key_pad);
+}
+
+
+static int ikev2_derive_keys(struct ikev2_responder_data *data)
+{
+	u8 *buf, *pos, *pad, skeyseed[IKEV2_MAX_HASH_LEN];
+	size_t buf_len, pad_len;
+	struct wpabuf *shared;
+	const struct ikev2_integ_alg *integ;
+	const struct ikev2_prf_alg *prf;
+	const struct ikev2_encr_alg *encr;
+	int ret;
+	const u8 *addr[2];
+	size_t len[2];
+
+	/* RFC 4306, Sect. 2.14 */
+
+	integ = ikev2_get_integ(data->proposal.integ);
+	prf = ikev2_get_prf(data->proposal.prf);
+	encr = ikev2_get_encr(data->proposal.encr);
+	if (integ == NULL || prf == NULL || encr == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported proposal");
+		return -1;
+	}
+
+	shared = dh_derive_shared(data->i_dh_public, data->r_dh_private,
+				  data->dh);
+	if (shared == NULL)
+		return -1;
+
+	/* Construct Ni | Nr | SPIi | SPIr */
+
+	buf_len = data->i_nonce_len + data->r_nonce_len + 2 * IKEV2_SPI_LEN;
+	buf = os_malloc(buf_len);
+	if (buf == NULL) {
+		wpabuf_free(shared);
+		return -1;
+	}
+
+	pos = buf;
+	os_memcpy(pos, data->i_nonce, data->i_nonce_len);
+	pos += data->i_nonce_len;
+	os_memcpy(pos, data->r_nonce, data->r_nonce_len);
+	pos += data->r_nonce_len;
+	os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN);
+	pos += IKEV2_SPI_LEN;
+	os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN);
+
+	/* SKEYSEED = prf(Ni | Nr, g^ir) */
+	/* Use zero-padding per RFC 4306, Sect. 2.14 */
+	pad_len = data->dh->prime_len - wpabuf_len(shared);
+	pad = os_zalloc(pad_len ? pad_len : 1);
+	if (pad == NULL) {
+		wpabuf_free(shared);
+		os_free(buf);
+		return -1;
+	}
+
+	addr[0] = pad;
+	len[0] = pad_len;
+	addr[1] = wpabuf_head(shared);
+	len[1] = wpabuf_len(shared);
+	if (ikev2_prf_hash(prf->id, buf, data->i_nonce_len + data->r_nonce_len,
+			   2, addr, len, skeyseed) < 0) {
+		wpabuf_free(shared);
+		os_free(buf);
+		os_free(pad);
+		return -1;
+	}
+	os_free(pad);
+	wpabuf_free(shared);
+
+	/* DH parameters are not needed anymore, so free them */
+	wpabuf_free(data->i_dh_public);
+	data->i_dh_public = NULL;
+	wpabuf_free(data->r_dh_private);
+	data->r_dh_private = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "IKEV2: SKEYSEED",
+			skeyseed, prf->hash_len);
+
+	ret = ikev2_derive_sk_keys(prf, integ, encr, skeyseed, buf, buf_len,
+				   &data->keys);
+	os_free(buf);
+	return ret;
+}
+
+
+static int ikev2_parse_transform(struct ikev2_proposal_data *prop,
+				 const u8 *pos, const u8 *end)
+{
+	int transform_len;
+	const struct ikev2_transform *t;
+	u16 transform_id;
+	const u8 *tend;
+
+	if (end - pos < (int) sizeof(*t)) {
+		wpa_printf(MSG_INFO, "IKEV2: Too short transform");
+		return -1;
+	}
+
+	t = (const struct ikev2_transform *) pos;
+	transform_len = WPA_GET_BE16(t->transform_length);
+	if (transform_len < (int) sizeof(*t) || pos + transform_len > end) {
+		wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d",
+			   transform_len);
+		return -1;
+	}
+	tend = pos + transform_len;
+
+	transform_id = WPA_GET_BE16(t->transform_id);
+
+	wpa_printf(MSG_DEBUG, "IKEV2:   Transform:");
+	wpa_printf(MSG_DEBUG, "IKEV2:     Type: %d  Transform Length: %d  "
+		   "Transform Type: %d  Transform ID: %d",
+		   t->type, transform_len, t->transform_type, transform_id);
+
+	if (t->type != 0 && t->type != 3) {
+		wpa_printf(MSG_INFO, "IKEV2: Unexpected Transform type");
+		return -1;
+	}
+
+	pos = (const u8 *) (t + 1);
+	if (pos < tend) {
+		wpa_hexdump(MSG_DEBUG, "IKEV2:     Transform Attributes",
+			    pos, tend - pos);
+	}
+
+	switch (t->transform_type) {
+	case IKEV2_TRANSFORM_ENCR:
+		if (ikev2_get_encr(transform_id)) {
+			if (transform_id == ENCR_AES_CBC) {
+				if (tend - pos != 4) {
+					wpa_printf(MSG_DEBUG, "IKEV2: No "
+						   "Transform Attr for AES");
+					break;
+				}
+				if (WPA_GET_BE16(pos) != 0x800e) {
+					wpa_printf(MSG_DEBUG, "IKEV2: Not a "
+						   "Key Size attribute for "
+						   "AES");
+					break;
+				}
+				if (WPA_GET_BE16(pos + 2) != 128) {
+					wpa_printf(MSG_DEBUG, "IKEV2: "
+						   "Unsupported AES key size "
+						   "%d bits",
+						   WPA_GET_BE16(pos + 2));
+					break;
+				}
+			}
+			prop->encr = transform_id;
+		}
+		break;
+	case IKEV2_TRANSFORM_PRF:
+		if (ikev2_get_prf(transform_id))
+			prop->prf = transform_id;
+		break;
+	case IKEV2_TRANSFORM_INTEG:
+		if (ikev2_get_integ(transform_id))
+			prop->integ = transform_id;
+		break;
+	case IKEV2_TRANSFORM_DH:
+		if (dh_groups_get(transform_id))
+			prop->dh = transform_id;
+		break;
+	}
+
+	return transform_len;
+}
+
+
+static int ikev2_parse_proposal(struct ikev2_proposal_data *prop,
+				const u8 *pos, const u8 *end)
+{
+	const u8 *pend, *ppos;
+	int proposal_len, i;
+	const struct ikev2_proposal *p;
+
+	if (end - pos < (int) sizeof(*p)) {
+		wpa_printf(MSG_INFO, "IKEV2: Too short proposal");
+		return -1;
+	}
+
+	/* FIX: AND processing if multiple proposals use the same # */
+
+	p = (const struct ikev2_proposal *) pos;
+	proposal_len = WPA_GET_BE16(p->proposal_length);
+	if (proposal_len < (int) sizeof(*p) || proposal_len > end - pos) {
+		wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d",
+			   proposal_len);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "IKEV2: SAi1 Proposal # %d",
+		   p->proposal_num);
+	wpa_printf(MSG_DEBUG, "IKEV2:   Type: %d  Proposal Length: %d "
+		   " Protocol ID: %d",
+		   p->type, proposal_len, p->protocol_id);
+	wpa_printf(MSG_DEBUG, "IKEV2:   SPI Size: %d  Transforms: %d",
+		   p->spi_size, p->num_transforms);
+
+	if (p->type != 0 && p->type != 2) {
+		wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal type");
+		return -1;
+	}
+
+	if (p->protocol_id != IKEV2_PROTOCOL_IKE) {
+		wpa_printf(MSG_DEBUG, "IKEV2: Unexpected Protocol ID "
+			   "(only IKE allowed for EAP-IKEv2)");
+		return -1;
+	}
+
+	if (p->proposal_num != prop->proposal_num) {
+		if (p->proposal_num == prop->proposal_num + 1)
+			prop->proposal_num = p->proposal_num;
+		else {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal #");
+			return -1;
+		}
+	}
+
+	ppos = (const u8 *) (p + 1);
+	pend = pos + proposal_len;
+	if (ppos + p->spi_size > pend) {
+		wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI "
+			   "in proposal");
+		return -1;
+	}
+	if (p->spi_size) {
+		wpa_hexdump(MSG_DEBUG, "IKEV2:    SPI",
+			    ppos, p->spi_size);
+		ppos += p->spi_size;
+	}
+
+	/*
+	 * For initial IKE_SA negotiation, SPI Size MUST be zero; for
+	 * subsequent negotiations, it must be 8 for IKE. We only support
+	 * initial case for now.
+	 */
+	if (p->spi_size != 0) {
+		wpa_printf(MSG_INFO, "IKEV2: Unexpected SPI Size");
+		return -1;
+	}
+
+	if (p->num_transforms == 0) {
+		wpa_printf(MSG_INFO, "IKEV2: At least one transform required");
+		return -1;
+	}
+
+	for (i = 0; i < (int) p->num_transforms; i++) {
+		int tlen = ikev2_parse_transform(prop, ppos, pend);
+		if (tlen < 0)
+			return -1;
+		ppos += tlen;
+	}
+
+	if (ppos != pend) {
+		wpa_printf(MSG_INFO, "IKEV2: Unexpected data after "
+			   "transforms");
+		return -1;
+	}
+
+	return proposal_len;
+}
+
+
+static int ikev2_process_sai1(struct ikev2_responder_data *data,
+			      const u8 *sai1, size_t sai1_len)
+{
+	struct ikev2_proposal_data prop;
+	const u8 *pos, *end;
+	int found = 0;
+
+	/* Security Association Payloads: <Proposals> */
+
+	if (sai1 == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: SAi1 not received");
+		return -1;
+	}
+
+	os_memset(&prop, 0, sizeof(prop));
+	prop.proposal_num = 1;
+
+	pos = sai1;
+	end = sai1 + sai1_len;
+
+	while (pos < end) {
+		int plen;
+
+		prop.integ = -1;
+		prop.prf = -1;
+		prop.encr = -1;
+		prop.dh = -1;
+		plen = ikev2_parse_proposal(&prop, pos, end);
+		if (plen < 0)
+			return -1;
+
+		if (!found && prop.integ != -1 && prop.prf != -1 &&
+		    prop.encr != -1 && prop.dh != -1) {
+			os_memcpy(&data->proposal, &prop, sizeof(prop));
+			data->dh = dh_groups_get(prop.dh);
+			found = 1;
+		}
+
+		pos += plen;
+	}
+
+	if (pos != end) {
+		wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposals");
+		return -1;
+	}
+
+	if (!found) {
+		wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d "
+		   "INTEG:%d D-H:%d", data->proposal.proposal_num,
+		   data->proposal.encr, data->proposal.prf,
+		   data->proposal.integ, data->proposal.dh);
+
+	return 0;
+}
+
+
+static int ikev2_process_kei(struct ikev2_responder_data *data,
+			     const u8 *kei, size_t kei_len)
+{
+	u16 group;
+
+	/*
+	 * Key Exchange Payload:
+	 * DH Group # (16 bits)
+	 * RESERVED (16 bits)
+	 * Key Exchange Data (Diffie-Hellman public value)
+	 */
+
+	if (kei == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: KEi not received");
+		return -1;
+	}
+
+	if (kei_len < 4 + 96) {
+		wpa_printf(MSG_INFO, "IKEV2: Too short Key Exchange Payload");
+		return -1;
+	}
+
+	group = WPA_GET_BE16(kei);
+	wpa_printf(MSG_DEBUG, "IKEV2: KEi DH Group #%u", group);
+
+	if (group != data->proposal.dh) {
+		wpa_printf(MSG_DEBUG, "IKEV2: KEi DH Group #%u does not match "
+			   "with the selected proposal (%u)",
+			   group, data->proposal.dh);
+		/* Reject message with Notify payload of type
+		 * INVALID_KE_PAYLOAD (RFC 4306, Sect. 3.4) */
+		data->error_type = INVALID_KE_PAYLOAD;
+		data->state = NOTIFY;
+		return -1;
+	}
+
+	if (data->dh == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group");
+		return -1;
+	}
+
+	/* RFC 4306, Section 3.4:
+	 * The length of DH public value MUST be equal to the length of the
+	 * prime modulus.
+	 */
+	if (kei_len - 4 != data->dh->prime_len) {
+		wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length "
+			   "%ld (expected %ld)",
+			   (long) (kei_len - 4), (long) data->dh->prime_len);
+		return -1;
+	}
+
+	wpabuf_free(data->i_dh_public);
+	data->i_dh_public = wpabuf_alloc(kei_len - 4);
+	if (data->i_dh_public == NULL)
+		return -1;
+	wpabuf_put_data(data->i_dh_public, kei + 4, kei_len - 4);
+
+	wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEi Diffie-Hellman Public Value",
+			data->i_dh_public);
+	
+	return 0;
+}
+
+
+static int ikev2_process_ni(struct ikev2_responder_data *data,
+			    const u8 *ni, size_t ni_len)
+{
+	if (ni == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: Ni not received");
+		return -1;
+	}
+
+	if (ni_len < IKEV2_NONCE_MIN_LEN || ni_len > IKEV2_NONCE_MAX_LEN) {
+		wpa_printf(MSG_INFO, "IKEV2: Invalid Ni length %ld",
+		           (long) ni_len);
+		return -1;
+	}
+
+	data->i_nonce_len = ni_len;
+	os_memcpy(data->i_nonce, ni, ni_len);
+	wpa_hexdump(MSG_MSGDUMP, "IKEV2: Ni",
+		    data->i_nonce, data->i_nonce_len);
+
+	return 0;
+}
+
+
+static int ikev2_process_sa_init(struct ikev2_responder_data *data,
+				 const struct ikev2_hdr *hdr,
+				 struct ikev2_payloads *pl)
+{
+	if (ikev2_process_sai1(data, pl->sa, pl->sa_len) < 0 ||
+	    ikev2_process_kei(data, pl->ke, pl->ke_len) < 0 ||
+	    ikev2_process_ni(data, pl->nonce, pl->nonce_len) < 0)
+		return -1;
+
+	os_memcpy(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN);
+
+	return 0;
+}
+
+
+static int ikev2_process_idi(struct ikev2_responder_data *data,
+			     const u8 *idi, size_t idi_len)
+{
+	u8 id_type;
+
+	if (idi == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: No IDi received");
+		return -1;
+	}
+
+	if (idi_len < 4) {
+		wpa_printf(MSG_INFO, "IKEV2: Too short IDi payload");
+		return -1;
+	}
+
+	id_type = idi[0];
+	idi += 4;
+	idi_len -= 4;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: IDi ID Type %d", id_type);
+	wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDi", idi, idi_len);
+	os_free(data->IDi);
+	data->IDi = os_malloc(idi_len);
+	if (data->IDi == NULL)
+		return -1;
+	os_memcpy(data->IDi, idi, idi_len);
+	data->IDi_len = idi_len;
+	data->IDi_type = id_type;
+
+	return 0;
+}
+
+
+static int ikev2_process_cert(struct ikev2_responder_data *data,
+			      const u8 *cert, size_t cert_len)
+{
+	u8 cert_encoding;
+
+	if (cert == NULL) {
+		if (data->peer_auth == PEER_AUTH_CERT) {
+			wpa_printf(MSG_INFO, "IKEV2: No Certificate received");
+			return -1;
+		}
+		return 0;
+	}
+
+	if (cert_len < 1) {
+		wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field");
+		return -1;
+	}
+
+	cert_encoding = cert[0];
+	cert++;
+	cert_len--;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding);
+	wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len);
+
+	/* TODO: validate certificate */
+
+	return 0;
+}
+
+
+static int ikev2_process_auth_cert(struct ikev2_responder_data *data,
+				   u8 method, const u8 *auth, size_t auth_len)
+{
+	if (method != AUTH_RSA_SIGN) {
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
+			   "method %d", method);
+		return -1;
+	}
+
+	/* TODO: validate AUTH */
+	return 0;
+}
+
+
+static int ikev2_process_auth_secret(struct ikev2_responder_data *data,
+				     u8 method, const u8 *auth,
+				     size_t auth_len)
+{
+	u8 auth_data[IKEV2_MAX_HASH_LEN];
+	const struct ikev2_prf_alg *prf;
+
+	if (method != AUTH_SHARED_KEY_MIC) {
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
+			   "method %d", method);
+		return -1;
+	}
+
+	/* msg | Nr | prf(SK_pi,IDi') */
+	if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg,
+				   data->IDi, data->IDi_len, data->IDi_type,
+				   &data->keys, 1, data->shared_secret,
+				   data->shared_secret_len,
+				   data->r_nonce, data->r_nonce_len,
+				   data->key_pad, data->key_pad_len,
+				   auth_data) < 0) {
+		wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
+		return -1;
+	}
+
+	wpabuf_free(data->i_sign_msg);
+	data->i_sign_msg = NULL;
+
+	prf = ikev2_get_prf(data->proposal.prf);
+	if (prf == NULL)
+		return -1;
+
+	if (auth_len != prf->hash_len ||
+	    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);
+		wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data",
+			    auth_data, prf->hash_len);
+		data->error_type = AUTHENTICATION_FAILED;
+		data->state = NOTIFY;
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Server authenticated successfully "
+		   "using shared keys");
+
+	return 0;
+}
+
+
+static int ikev2_process_auth(struct ikev2_responder_data *data,
+			      const u8 *auth, size_t auth_len)
+{
+	u8 auth_method;
+
+	if (auth == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload");
+		return -1;
+	}
+
+	if (auth_len < 4) {
+		wpa_printf(MSG_INFO, "IKEV2: Too short Authentication "
+			   "Payload");
+		return -1;
+	}
+
+	auth_method = auth[0];
+	auth += 4;
+	auth_len -= 4;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method);
+	wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len);
+
+	switch (data->peer_auth) {
+	case PEER_AUTH_CERT:
+		return ikev2_process_auth_cert(data, auth_method, auth,
+					       auth_len);
+	case PEER_AUTH_SECRET:
+		return ikev2_process_auth_secret(data, auth_method, auth,
+						 auth_len);
+	}
+
+	return -1;
+}
+
+
+static int ikev2_process_sa_auth_decrypted(struct ikev2_responder_data *data,
+					   u8 next_payload,
+					   u8 *payload, size_t payload_len)
+{
+	struct ikev2_payloads pl;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads");
+
+	if (ikev2_parse_payloads(&pl, next_payload, payload, payload +
+				 payload_len) < 0) {
+		wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted "
+			   "payloads");
+		return -1;
+	}
+
+	if (ikev2_process_idi(data, pl.idi, pl.idi_len) < 0 ||
+	    ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 ||
+	    ikev2_process_auth(data, pl.auth, pl.auth_len) < 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int ikev2_process_sa_auth(struct ikev2_responder_data *data,
+				 const struct ikev2_hdr *hdr,
+				 struct ikev2_payloads *pl)
+{
+	u8 *decrypted;
+	size_t decrypted_len;
+	int ret;
+
+	decrypted = ikev2_decrypt_payload(data->proposal.encr,
+					  data->proposal.integ,
+					  &data->keys, 1, hdr, pl->encrypted,
+					  pl->encrypted_len, &decrypted_len);
+	if (decrypted == NULL)
+		return -1;
+
+	ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload,
+					      decrypted, decrypted_len);
+	os_free(decrypted);
+
+	return ret;
+}
+
+
+static int ikev2_validate_rx_state(struct ikev2_responder_data *data,
+				   u8 exchange_type, u32 message_id)
+{
+	switch (data->state) {
+	case SA_INIT:
+		/* Expect to receive IKE_SA_INIT: HDR, SAi1, KEi, Ni */
+		if (exchange_type != IKE_SA_INIT) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+				   "%u in SA_INIT state", exchange_type);
+			return -1;
+		}
+		if (message_id != 0) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+				   "in SA_INIT state", message_id);
+			return -1;
+		}
+		break;
+	case SA_AUTH:
+		/* Expect to receive IKE_SA_AUTH:
+		 * HDR, SK {IDi, [CERT,] [CERTREQ,] [IDr,]
+		 *	AUTH, SAi2, TSi, TSr}
+		 */
+		if (exchange_type != IKE_SA_AUTH) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+				   "%u in SA_AUTH state", exchange_type);
+			return -1;
+		}
+		if (message_id != 1) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+				   "in SA_AUTH state", message_id);
+			return -1;
+		}
+		break;
+	case CHILD_SA:
+		if (exchange_type != CREATE_CHILD_SA) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+				   "%u in CHILD_SA state", exchange_type);
+			return -1;
+		}
+		if (message_id != 2) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+				   "in CHILD_SA state", message_id);
+			return -1;
+		}
+		break;
+	case NOTIFY:
+	case IKEV2_DONE:
+	case IKEV2_FAILED:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int ikev2_responder_process(struct ikev2_responder_data *data,
+			    const struct wpabuf *buf)
+{
+	const struct ikev2_hdr *hdr;
+	u32 length, message_id;
+	const u8 *pos, *end;
+	struct ikev2_payloads pl;
+
+	wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)",
+		   (unsigned long) wpabuf_len(buf));
+
+	if (wpabuf_len(buf) < sizeof(*hdr)) {
+		wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR");
+		return -1;
+	}
+
+	data->error_type = 0;
+	hdr = (const struct ikev2_hdr *) wpabuf_head(buf);
+	end = wpabuf_head_u8(buf) + wpabuf_len(buf);
+	message_id = WPA_GET_BE32(hdr->message_id);
+	length = WPA_GET_BE32(hdr->length);
+
+	wpa_hexdump(MSG_DEBUG, "IKEV2:   IKE_SA Initiator's SPI",
+		    hdr->i_spi, IKEV2_SPI_LEN);
+	wpa_hexdump(MSG_DEBUG, "IKEV2:   IKE_SA Responder's SPI",
+		    hdr->r_spi, IKEV2_SPI_LEN);
+	wpa_printf(MSG_DEBUG, "IKEV2:   Next Payload: %u  Version: 0x%x  "
+		   "Exchange Type: %u",
+		   hdr->next_payload, hdr->version, hdr->exchange_type);
+	wpa_printf(MSG_DEBUG, "IKEV2:   Message ID: %u  Length: %u",
+		   message_id, length);
+
+	if (hdr->version != IKEV2_VERSION) {
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x "
+			   "(expected 0x%x)", hdr->version, IKEV2_VERSION);
+		return -1;
+	}
+
+	if (length != wpabuf_len(buf)) {
+		wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != "
+			   "RX: %lu)", (unsigned long) length,
+			   (unsigned long) wpabuf_len(buf));
+		return -1;
+	}
+
+	if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0)
+		return -1;
+
+	if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) !=
+	    IKEV2_HDR_INITIATOR) {
+		wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x",
+			   hdr->flags);
+		return -1;
+	}
+
+	if (data->state != SA_INIT) {
+		if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
+				   "Initiator's SPI");
+			return -1;
+		}
+		if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
+				   "Responder's SPI");
+			return -1;
+		}
+	}
+
+	pos = (const u8 *) (hdr + 1);
+	if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0)
+		return -1;
+
+	if (data->state == SA_INIT) {
+		data->last_msg = LAST_MSG_SA_INIT;
+		if (ikev2_process_sa_init(data, hdr, &pl) < 0) {
+			if (data->state == NOTIFY)
+				return 0;
+			return -1;
+		}
+		wpabuf_free(data->i_sign_msg);
+		data->i_sign_msg = wpabuf_dup(buf);
+	}
+
+	if (data->state == SA_AUTH) {
+		data->last_msg = LAST_MSG_SA_AUTH;
+		if (ikev2_process_sa_auth(data, hdr, &pl) < 0) {
+			if (data->state == NOTIFY)
+				return 0;
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static void ikev2_build_hdr(struct ikev2_responder_data *data,
+			    struct wpabuf *msg, u8 exchange_type,
+			    u8 next_payload, u32 message_id)
+{
+	struct ikev2_hdr *hdr;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR");
+
+	/* HDR - RFC 4306, Sect. 3.1 */
+	hdr = wpabuf_put(msg, sizeof(*hdr));
+	os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN);
+	os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN);
+	hdr->next_payload = next_payload;
+	hdr->version = IKEV2_VERSION;
+	hdr->exchange_type = exchange_type;
+	hdr->flags = IKEV2_HDR_RESPONSE;
+	WPA_PUT_BE32(hdr->message_id, message_id);
+}
+
+
+static int ikev2_build_sar1(struct ikev2_responder_data *data,
+			    struct wpabuf *msg, u8 next_payload)
+{
+	struct ikev2_payload_hdr *phdr;
+	size_t plen;
+	struct ikev2_proposal *p;
+	struct ikev2_transform *t;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Adding SAr1 payload");
+
+	/* SAr1 - RFC 4306, Sect. 2.7 and 3.3 */
+	phdr = wpabuf_put(msg, sizeof(*phdr));
+	phdr->next_payload = next_payload;
+	phdr->flags = 0;
+
+	p = wpabuf_put(msg, sizeof(*p));
+	p->proposal_num = data->proposal.proposal_num;
+	p->protocol_id = IKEV2_PROTOCOL_IKE;
+	p->num_transforms = 4;
+
+	t = wpabuf_put(msg, sizeof(*t));
+	t->type = 3;
+	t->transform_type = IKEV2_TRANSFORM_ENCR;
+	WPA_PUT_BE16(t->transform_id, data->proposal.encr);
+	if (data->proposal.encr == ENCR_AES_CBC) {
+		/* Transform Attribute: Key Len = 128 bits */
+		wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */
+		wpabuf_put_be16(msg, 128); /* 128-bit key */
+	}
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t;
+	WPA_PUT_BE16(t->transform_length, plen);
+
+	t = wpabuf_put(msg, sizeof(*t));
+	t->type = 3;
+	WPA_PUT_BE16(t->transform_length, sizeof(*t));
+	t->transform_type = IKEV2_TRANSFORM_PRF;
+	WPA_PUT_BE16(t->transform_id, data->proposal.prf);
+
+	t = wpabuf_put(msg, sizeof(*t));
+	t->type = 3;
+	WPA_PUT_BE16(t->transform_length, sizeof(*t));
+	t->transform_type = IKEV2_TRANSFORM_INTEG;
+	WPA_PUT_BE16(t->transform_id, data->proposal.integ);
+
+	t = wpabuf_put(msg, sizeof(*t));
+	WPA_PUT_BE16(t->transform_length, sizeof(*t));
+	t->transform_type = IKEV2_TRANSFORM_DH;
+	WPA_PUT_BE16(t->transform_id, data->proposal.dh);
+
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p;
+	WPA_PUT_BE16(p->proposal_length, plen);
+
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+	WPA_PUT_BE16(phdr->payload_length, plen);
+
+	return 0;
+}
+
+
+static int ikev2_build_ker(struct ikev2_responder_data *data,
+			   struct wpabuf *msg, u8 next_payload)
+{
+	struct ikev2_payload_hdr *phdr;
+	size_t plen;
+	struct wpabuf *pv;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Adding KEr payload");
+
+	pv = dh_init(data->dh, &data->r_dh_private);
+	if (pv == NULL) {
+		wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH");
+		return -1;
+	}
+
+	/* KEr - RFC 4306, Sect. 3.4 */
+	phdr = wpabuf_put(msg, sizeof(*phdr));
+	phdr->next_payload = next_payload;
+	phdr->flags = 0;
+
+	wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */
+	wpabuf_put(msg, 2); /* RESERVED */
+	/*
+	 * RFC 4306, Sect. 3.4: possible zero padding for public value to
+	 * match the length of the prime.
+	 */
+	wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv));
+	wpabuf_put_buf(msg, pv);
+	wpabuf_free(pv);
+
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+	WPA_PUT_BE16(phdr->payload_length, plen);
+	return 0;
+}
+
+
+static int ikev2_build_nr(struct ikev2_responder_data *data,
+			  struct wpabuf *msg, u8 next_payload)
+{
+	struct ikev2_payload_hdr *phdr;
+	size_t plen;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Adding Nr payload");
+
+	/* Nr - RFC 4306, Sect. 3.9 */
+	phdr = wpabuf_put(msg, sizeof(*phdr));
+	phdr->next_payload = next_payload;
+	phdr->flags = 0;
+	wpabuf_put_data(msg, data->r_nonce, data->r_nonce_len);
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+	WPA_PUT_BE16(phdr->payload_length, plen);
+	return 0;
+}
+
+
+static int ikev2_build_idr(struct ikev2_responder_data *data,
+			   struct wpabuf *msg, u8 next_payload)
+{
+	struct ikev2_payload_hdr *phdr;
+	size_t plen;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Adding IDr payload");
+
+	if (data->IDr == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: No IDr available");
+		return -1;
+	}
+
+	/* IDr - RFC 4306, Sect. 3.5 */
+	phdr = wpabuf_put(msg, sizeof(*phdr));
+	phdr->next_payload = next_payload;
+	phdr->flags = 0;
+	wpabuf_put_u8(msg, ID_KEY_ID);
+	wpabuf_put(msg, 3); /* RESERVED */
+	wpabuf_put_data(msg, data->IDr, data->IDr_len);
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+	WPA_PUT_BE16(phdr->payload_length, plen);
+	return 0;
+}
+
+
+static int ikev2_build_auth(struct ikev2_responder_data *data,
+			    struct wpabuf *msg, u8 next_payload)
+{
+	struct ikev2_payload_hdr *phdr;
+	size_t plen;
+	const struct ikev2_prf_alg *prf;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload");
+
+	prf = ikev2_get_prf(data->proposal.prf);
+	if (prf == NULL)
+		return -1;
+
+	/* Authentication - RFC 4306, Sect. 3.8 */
+	phdr = wpabuf_put(msg, sizeof(*phdr));
+	phdr->next_payload = next_payload;
+	phdr->flags = 0;
+	wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC);
+	wpabuf_put(msg, 3); /* RESERVED */
+
+	/* msg | Ni | prf(SK_pr,IDr') */
+	if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg,
+				   data->IDr, data->IDr_len, ID_KEY_ID,
+				   &data->keys, 0, data->shared_secret,
+				   data->shared_secret_len,
+				   data->i_nonce, data->i_nonce_len,
+				   data->key_pad, data->key_pad_len,
+				   wpabuf_put(msg, prf->hash_len)) < 0) {
+		wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
+		return -1;
+	}
+	wpabuf_free(data->r_sign_msg);
+	data->r_sign_msg = NULL;
+
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+	WPA_PUT_BE16(phdr->payload_length, plen);
+	return 0;
+}
+
+
+static int ikev2_build_notification(struct ikev2_responder_data *data,
+				    struct wpabuf *msg, u8 next_payload)
+{
+	struct ikev2_payload_hdr *phdr;
+	size_t plen;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Adding Notification payload");
+
+	if (data->error_type == 0) {
+		wpa_printf(MSG_INFO, "IKEV2: No Notify Message Type "
+			   "available");
+		return -1;
+	}
+
+	/* Notify - RFC 4306, Sect. 3.10 */
+	phdr = wpabuf_put(msg, sizeof(*phdr));
+	phdr->next_payload = next_payload;
+	phdr->flags = 0;
+	wpabuf_put_u8(msg, 0); /* Protocol ID: no existing SA */
+	wpabuf_put_u8(msg, 0); /* SPI Size */
+	wpabuf_put_be16(msg, data->error_type);
+
+	switch (data->error_type) {
+	case INVALID_KE_PAYLOAD:
+		if (data->proposal.dh == -1) {
+			wpa_printf(MSG_INFO, "IKEV2: No DH Group selected for "
+				   "INVALID_KE_PAYLOAD notifications");
+			return -1;
+		}
+		wpabuf_put_be16(msg, data->proposal.dh);
+		wpa_printf(MSG_DEBUG, "IKEV2: INVALID_KE_PAYLOAD - request "
+			   "DH Group #%d", data->proposal.dh);
+		break;
+	case AUTHENTICATION_FAILED:
+		/* no associated data */
+		break;
+	default:
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported Notify Message Type "
+			   "%d", data->error_type);
+		return -1;
+	}
+
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+	WPA_PUT_BE16(phdr->payload_length, plen);
+	return 0;
+}
+
+
+static struct wpabuf * ikev2_build_sa_init(struct ikev2_responder_data *data)
+{
+	struct wpabuf *msg;
+
+	/* build IKE_SA_INIT: HDR, SAr1, KEr, Nr, [CERTREQ], [SK{IDr}] */
+
+	if (os_get_random(data->r_spi, IKEV2_SPI_LEN))
+		return NULL;
+	wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Responder's SPI",
+		    data->r_spi, IKEV2_SPI_LEN);
+
+	data->r_nonce_len = IKEV2_NONCE_MIN_LEN;
+	if (random_get_bytes(data->r_nonce, data->r_nonce_len))
+		return NULL;
+	wpa_hexdump(MSG_DEBUG, "IKEV2: Nr", data->r_nonce, data->r_nonce_len);
+
+	msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1500);
+	if (msg == NULL)
+		return NULL;
+
+	ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_SA, 0);
+	if (ikev2_build_sar1(data, msg, IKEV2_PAYLOAD_KEY_EXCHANGE) ||
+	    ikev2_build_ker(data, msg, IKEV2_PAYLOAD_NONCE) ||
+	    ikev2_build_nr(data, msg, data->peer_auth == PEER_AUTH_SECRET ?
+			   IKEV2_PAYLOAD_ENCRYPTED :
+			   IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	if (ikev2_derive_keys(data)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	if (data->peer_auth == PEER_AUTH_CERT) {
+		/* TODO: CERTREQ with SHA-1 hashes of Subject Public Key Info
+		 * for trust agents */
+	}
+
+	if (data->peer_auth == PEER_AUTH_SECRET) {
+		struct wpabuf *plain = wpabuf_alloc(data->IDr_len + 1000);
+		if (plain == NULL) {
+			wpabuf_free(msg);
+			return NULL;
+		}
+		if (ikev2_build_idr(data, plain,
+				    IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) ||
+		    ikev2_build_encrypted(data->proposal.encr,
+					  data->proposal.integ,
+					  &data->keys, 0, msg, plain,
+					  IKEV2_PAYLOAD_IDr)) {
+			wpabuf_free(plain);
+			wpabuf_free(msg);
+			return NULL;
+		}
+		wpabuf_free(plain);
+	}
+
+	ikev2_update_hdr(msg);
+
+	wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_INIT)", msg);
+
+	data->state = SA_AUTH;
+
+	wpabuf_free(data->r_sign_msg);
+	data->r_sign_msg = wpabuf_dup(msg);
+
+	return msg;
+}
+
+
+static struct wpabuf * ikev2_build_sa_auth(struct ikev2_responder_data *data)
+{
+	struct wpabuf *msg, *plain;
+
+	/* build IKE_SA_AUTH: HDR, SK {IDr, [CERT,] AUTH} */
+
+	msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1000);
+	if (msg == NULL)
+		return NULL;
+	ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1);
+
+	plain = wpabuf_alloc(data->IDr_len + 1000);
+	if (plain == NULL) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	if (ikev2_build_idr(data, plain, IKEV2_PAYLOAD_AUTHENTICATION) ||
+	    ikev2_build_auth(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) ||
+	    ikev2_build_encrypted(data->proposal.encr, data->proposal.integ,
+				  &data->keys, 0, msg, plain,
+				  IKEV2_PAYLOAD_IDr)) {
+		wpabuf_free(plain);
+		wpabuf_free(msg);
+		return NULL;
+	}
+	wpabuf_free(plain);
+
+	wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_AUTH)", msg);
+
+	data->state = IKEV2_DONE;
+
+	return msg;
+}
+
+
+static struct wpabuf * ikev2_build_notify(struct ikev2_responder_data *data)
+{
+	struct wpabuf *msg;
+
+	msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + 1000);
+	if (msg == NULL)
+		return NULL;
+	if (data->last_msg == LAST_MSG_SA_AUTH) {
+		/* HDR, SK{N} */
+		struct wpabuf *plain = wpabuf_alloc(100);
+		if (plain == NULL) {
+			wpabuf_free(msg);
+			return NULL;
+		}
+		ikev2_build_hdr(data, msg, IKE_SA_AUTH,
+				IKEV2_PAYLOAD_ENCRYPTED, 1);
+		if (ikev2_build_notification(data, plain,
+					     IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) ||
+		    ikev2_build_encrypted(data->proposal.encr,
+					  data->proposal.integ,
+					  &data->keys, 0, msg, plain,
+					  IKEV2_PAYLOAD_NOTIFICATION)) {
+			wpabuf_free(plain);
+			wpabuf_free(msg);
+			return NULL;
+		}
+		wpabuf_free(plain);
+		data->state = IKEV2_FAILED;
+	} else {
+		/* HDR, N */
+		ikev2_build_hdr(data, msg, IKE_SA_INIT,
+				IKEV2_PAYLOAD_NOTIFICATION, 0);
+		if (ikev2_build_notification(data, msg,
+					     IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) {
+			wpabuf_free(msg);
+			return NULL;
+		}
+		data->state = SA_INIT;
+	}
+
+	ikev2_update_hdr(msg);
+
+	wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (Notification)",
+			msg);
+
+	return msg;
+}
+
+
+struct wpabuf * ikev2_responder_build(struct ikev2_responder_data *data)
+{
+	switch (data->state) {
+	case SA_INIT:
+		return ikev2_build_sa_init(data);
+	case SA_AUTH:
+		return ikev2_build_sa_auth(data);
+	case CHILD_SA:
+		return NULL;
+	case NOTIFY:
+		return ikev2_build_notify(data);
+	case IKEV2_DONE:
+	case IKEV2_FAILED:
+		return NULL;
+	}
+	return NULL;
+}
diff --git a/hostap/src/eap_peer/ikev2.h b/hostap/src/eap_peer/ikev2.h
new file mode 100644
index 0000000..627a2cb
--- /dev/null
+++ b/hostap/src/eap_peer/ikev2.h
@@ -0,0 +1,59 @@
+/*
+ * IKEv2 responder (RFC 4306) for EAP-IKEV2
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IKEV2_H
+#define IKEV2_H
+
+#include "eap_common/ikev2_common.h"
+
+struct ikev2_proposal_data {
+	u8 proposal_num;
+	int integ;
+	int prf;
+	int encr;
+	int dh;
+};
+
+
+struct ikev2_responder_data {
+	enum { SA_INIT, SA_AUTH, CHILD_SA, NOTIFY, IKEV2_DONE, IKEV2_FAILED }
+		state;
+	u8 i_spi[IKEV2_SPI_LEN];
+	u8 r_spi[IKEV2_SPI_LEN];
+	u8 i_nonce[IKEV2_NONCE_MAX_LEN];
+	size_t i_nonce_len;
+	u8 r_nonce[IKEV2_NONCE_MAX_LEN];
+	size_t r_nonce_len;
+	struct wpabuf *i_dh_public;
+	struct wpabuf *r_dh_private;
+	struct ikev2_proposal_data proposal;
+	const struct dh_group *dh;
+	struct ikev2_keys keys;
+	u8 *IDi;
+	size_t IDi_len;
+	u8 IDi_type;
+	u8 *IDr;
+	size_t IDr_len;
+	struct wpabuf *r_sign_msg;
+	struct wpabuf *i_sign_msg;
+	u8 *shared_secret;
+	size_t shared_secret_len;
+	enum { PEER_AUTH_CERT, PEER_AUTH_SECRET } peer_auth;
+	u8 *key_pad;
+	size_t key_pad_len;
+	u16 error_type;
+	enum { LAST_MSG_SA_INIT, LAST_MSG_SA_AUTH } last_msg;
+};
+
+
+void ikev2_responder_deinit(struct ikev2_responder_data *data);
+int ikev2_responder_process(struct ikev2_responder_data *data,
+			    const struct wpabuf *buf);
+struct wpabuf * ikev2_responder_build(struct ikev2_responder_data *data);
+
+#endif /* IKEV2_H */
diff --git a/hostap/src/eap_peer/mschapv2.c b/hostap/src/eap_peer/mschapv2.c
new file mode 100644
index 0000000..9bc7370
--- /dev/null
+++ b/hostap/src/eap_peer/mschapv2.c
@@ -0,0 +1,124 @@
+/*
+ * MSCHAPV2 (RFC 2759)
+ * Copyright (c) 2004-2008, 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/ms_funcs.h"
+#include "mschapv2.h"
+
+const u8 * mschapv2_remove_domain(const u8 *username, size_t *len)
+{
+	size_t i;
+
+	/*
+	 * MSCHAPv2 does not include optional domain name in the
+	 * challenge-response calculation, so remove domain prefix
+	 * (if present).
+	 */
+
+	for (i = 0; i < *len; i++) {
+		if (username[i] == '\\') {
+			*len -= i + 1;
+			return username + i + 1;
+		}
+	}
+
+	return username;
+}
+
+
+int mschapv2_derive_response(const u8 *identity, size_t identity_len,
+			     const u8 *password, size_t password_len,
+			     int pwhash,
+			     const u8 *auth_challenge,
+			     const u8 *peer_challenge,
+			     u8 *nt_response, u8 *auth_response,
+			     u8 *master_key)
+{
+	const u8 *username;
+	size_t username_len;
+	u8 password_hash[16], password_hash_hash[16];
+
+	wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity",
+			  identity, identity_len);
+	username_len = identity_len;
+	username = mschapv2_remove_domain(identity, &username_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username",
+			  username, username_len);
+
+	wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge",
+		    auth_challenge, MSCHAPV2_CHAL_LEN);
+	wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge",
+		    peer_challenge, MSCHAPV2_CHAL_LEN);
+	wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username",
+			  username, username_len);
+	/* Authenticator response is not really needed yet, but calculate it
+	 * here so that challenges need not be saved. */
+	if (pwhash) {
+		wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash",
+				password, password_len);
+		if (generate_nt_response_pwhash(auth_challenge, peer_challenge,
+						username, username_len,
+						password, nt_response) ||
+		    generate_authenticator_response_pwhash(
+			    password, peer_challenge, auth_challenge,
+			    username, username_len, nt_response,
+			    auth_response))
+			return -1;
+	} else {
+		wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password",
+				      password, password_len);
+		if (generate_nt_response(auth_challenge, peer_challenge,
+					 username, username_len,
+					 password, password_len,
+					 nt_response) ||
+		    generate_authenticator_response(password, password_len,
+						    peer_challenge,
+						    auth_challenge,
+						    username, username_len,
+						    nt_response,
+						    auth_response))
+			return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response",
+		    nt_response, MSCHAPV2_NT_RESPONSE_LEN);
+	wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response",
+		    auth_response, MSCHAPV2_AUTH_RESPONSE_LEN);
+
+	/* Generate master_key here since we have the needed data available. */
+	if (pwhash) {
+		if (hash_nt_password_hash(password, password_hash_hash))
+			return -1;
+	} else {
+		if (nt_password_hash(password, password_len, password_hash) ||
+		    hash_nt_password_hash(password_hash, password_hash_hash))
+			return -1;
+	}
+	if (get_master_key(password_hash_hash, nt_response, master_key))
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key",
+			master_key, MSCHAPV2_MASTER_KEY_LEN);
+
+	return 0;
+}
+
+
+int mschapv2_verify_auth_response(const u8 *auth_response,
+				  const u8 *buf, size_t buf_len)
+{
+	u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+	if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN ||
+	    buf[0] != 'S' || buf[1] != '=' ||
+	    hexstr2bin((char *) (buf + 2), recv_response,
+		       MSCHAPV2_AUTH_RESPONSE_LEN) ||
+	    os_memcmp_const(auth_response, recv_response,
+			    MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
+		return -1;
+	return 0;
+}
diff --git a/hostap/src/eap_peer/mschapv2.h b/hostap/src/eap_peer/mschapv2.h
new file mode 100644
index 0000000..edd458b
--- /dev/null
+++ b/hostap/src/eap_peer/mschapv2.h
@@ -0,0 +1,28 @@
+/*
+ * MSCHAPV2 (RFC 2759)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MSCHAPV2_H
+#define MSCHAPV2_H
+
+#define MSCHAPV2_CHAL_LEN 16
+#define MSCHAPV2_NT_RESPONSE_LEN 24
+#define MSCHAPV2_AUTH_RESPONSE_LEN 20
+#define MSCHAPV2_MASTER_KEY_LEN 16
+
+const u8 * mschapv2_remove_domain(const u8 *username, size_t *len);
+int mschapv2_derive_response(const u8 *username, size_t username_len,
+			     const u8 *password, size_t password_len,
+			     int pwhash,
+			     const u8 *auth_challenge,
+			     const u8 *peer_challenge,
+			     u8 *nt_response, u8 *auth_response,
+			     u8 *master_key);
+int mschapv2_verify_auth_response(const u8 *auth_response,
+				  const u8 *buf, size_t buf_len);
+
+#endif /* MSCHAPV2_H */
diff --git a/hostap/src/eap_peer/tncc.c b/hostap/src/eap_peer/tncc.c
new file mode 100644
index 0000000..7ca956e
--- /dev/null
+++ b/hostap/src/eap_peer/tncc.c
@@ -0,0 +1,1316 @@
+/*
+ * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
+ * Copyright (c) 2007, 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"
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <dlfcn.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#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"
+
+
+#ifdef UNICODE
+#define TSTR "%S"
+#else /* UNICODE */
+#define TSTR "%s"
+#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" \
+"<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
+"xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
+"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
+"xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
+"IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
+#define IF_TNCCS_END "\n</TNCCS-Batch>"
+
+/* TNC IF-IMC */
+
+/* IF-TNCCS-SOH - SSoH and SSoHR Attributes */
+enum {
+	SSOH_MS_MACHINE_INVENTORY = 1,
+	SSOH_MS_QUARANTINE_STATE = 2,
+	SSOH_MS_PACKET_INFO = 3,
+	SSOH_MS_SYSTEMGENERATED_IDS = 4,
+	SSOH_MS_MACHINENAME = 5,
+	SSOH_MS_CORRELATIONID = 6,
+	SSOH_MS_INSTALLED_SHVS = 7,
+	SSOH_MS_MACHINE_INVENTORY_EX = 8
+};
+
+struct tnc_if_imc {
+	struct tnc_if_imc *next;
+	char *name;
+	char *path;
+	void *dlhandle; /* from dlopen() */
+	TNC_IMCID imcID;
+	TNC_ConnectionID connectionID;
+	TNC_MessageTypeList supported_types;
+	size_t num_supported_types;
+	u8 *imc_send;
+	size_t imc_send_len;
+
+	/* Functions implemented by IMCs (with TNC_IMC_ prefix) */
+	TNC_Result (*Initialize)(
+		TNC_IMCID imcID,
+		TNC_Version minVersion,
+		TNC_Version maxVersion,
+		TNC_Version *pOutActualVersion);
+	TNC_Result (*NotifyConnectionChange)(
+		TNC_IMCID imcID,
+		TNC_ConnectionID connectionID,
+		TNC_ConnectionState newState);
+	TNC_Result (*BeginHandshake)(
+		TNC_IMCID imcID,
+		TNC_ConnectionID connectionID);
+	TNC_Result (*ReceiveMessage)(
+		TNC_IMCID imcID,
+		TNC_ConnectionID connectionID,
+		TNC_BufferReference messageBuffer,
+		TNC_UInt32 messageLength,
+		TNC_MessageType messageType);
+	TNC_Result (*BatchEnding)(
+		TNC_IMCID imcID,
+		TNC_ConnectionID connectionID);
+	TNC_Result (*Terminate)(TNC_IMCID imcID);
+	TNC_Result (*ProvideBindFunction)(
+		TNC_IMCID imcID,
+		TNC_TNCC_BindFunctionPointer bindFunction);
+};
+
+struct tncc_data {
+	struct tnc_if_imc *imc;
+	unsigned int last_batchid;
+};
+
+#define TNC_MAX_IMC_ID 10
+static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL };
+
+
+/* TNCC functions that IMCs can call */
+
+TNC_Result TNC_TNCC_ReportMessageTypes(
+	TNC_IMCID imcID,
+	TNC_MessageTypeList supportedTypes,
+	TNC_UInt32 typeCount)
+{
+	TNC_UInt32 i;
+	struct tnc_if_imc *imc;
+
+	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu "
+		   "typeCount=%lu)",
+		   (unsigned long) imcID, (unsigned long) typeCount);
+
+	for (i = 0; i < typeCount; i++) {
+		wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
+			   i, supportedTypes[i]);
+	}
+
+	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	imc = tnc_imc[imcID];
+	os_free(imc->supported_types);
+	imc->supported_types =
+		os_malloc(typeCount * sizeof(TNC_MessageType));
+	if (imc->supported_types == NULL)
+		return TNC_RESULT_FATAL;
+	os_memcpy(imc->supported_types, supportedTypes,
+		  typeCount * sizeof(TNC_MessageType));
+	imc->num_supported_types = typeCount;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCC_SendMessage(
+	TNC_IMCID imcID,
+	TNC_ConnectionID connectionID,
+	TNC_BufferReference message,
+	TNC_UInt32 messageLength,
+	TNC_MessageType messageType)
+{
+	struct tnc_if_imc *imc;
+	unsigned char *b64;
+	size_t b64len;
+
+	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu "
+		   "connectionID=%lu messageType=%lu)",
+		   imcID, connectionID, messageType);
+	wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage",
+			  message, messageLength);
+
+	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	b64 = base64_encode(message, messageLength, &b64len);
+	if (b64 == NULL)
+		return TNC_RESULT_FATAL;
+
+	imc = tnc_imc[imcID];
+	os_free(imc->imc_send);
+	imc->imc_send_len = 0;
+	imc->imc_send = os_zalloc(b64len + 100);
+	if (imc->imc_send == NULL) {
+		os_free(b64);
+		return TNC_RESULT_OTHER;
+	}
+
+	imc->imc_send_len =
+		os_snprintf((char *) imc->imc_send, b64len + 100,
+			    "<IMC-IMV-Message><Type>%08X</Type>"
+			    "<Base64>%s</Base64></IMC-IMV-Message>",
+			    (unsigned int) messageType, b64);
+
+	os_free(b64);
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCC_RequestHandshakeRetry(
+	TNC_IMCID imcID,
+	TNC_ConnectionID connectionID,
+	TNC_RetryReason reason)
+{
+	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry");
+
+	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	/*
+	 * TODO: trigger a call to eapol_sm_request_reauth(). This would
+	 * require that the IMC continues to be loaded in memory afer
+	 * authentication..
+	 */
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity,
+			       const char *message)
+{
+	wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu "
+		   "severity==%lu message='%s')",
+		   imcID, severity, message);
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID,
+				const char *message)
+{
+	wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu "
+		   "connectionID==%lu message='%s')",
+		   imcID, connectionID, message);
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCC_BindFunction(
+	TNC_IMCID imcID,
+	char *functionName,
+	void **pOutfunctionPointer)
+{
+	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, "
+		   "functionName='%s')", (unsigned long) imcID, functionName);
+
+	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	if (pOutfunctionPointer == NULL)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	if (os_strcmp(functionName, "TNC_TNCC_ReportMessageTypes") == 0)
+		*pOutfunctionPointer = TNC_TNCC_ReportMessageTypes;
+	else if (os_strcmp(functionName, "TNC_TNCC_SendMessage") == 0)
+		*pOutfunctionPointer = TNC_TNCC_SendMessage;
+	else if (os_strcmp(functionName, "TNC_TNCC_RequestHandshakeRetry") ==
+		 0)
+		*pOutfunctionPointer = TNC_TNCC_RequestHandshakeRetry;
+	else if (os_strcmp(functionName, "TNC_9048_LogMessage") == 0)
+		*pOutfunctionPointer = TNC_9048_LogMessage;
+	else if (os_strcmp(functionName, "TNC_9048_UserMessage") == 0)
+		*pOutfunctionPointer = TNC_9048_UserMessage;
+	else
+		*pOutfunctionPointer = NULL;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+static void * tncc_get_sym(void *handle, char *func)
+{
+	void *fptr;
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#ifdef _WIN32_WCE
+	fptr = GetProcAddressA(handle, func);
+#else /* _WIN32_WCE */
+	fptr = GetProcAddress(handle, func);
+#endif /* _WIN32_WCE */
+#else /* CONFIG_NATIVE_WINDOWS */
+	fptr = dlsym(handle, func);
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	return fptr;
+}
+
+
+static int tncc_imc_resolve_funcs(struct tnc_if_imc *imc)
+{
+	void *handle = imc->dlhandle;
+
+	/* Mandatory IMC functions */
+	imc->Initialize = tncc_get_sym(handle, "TNC_IMC_Initialize");
+	if (imc->Initialize == NULL) {
+		wpa_printf(MSG_ERROR, "TNC: IMC does not export "
+			   "TNC_IMC_Initialize");
+		return -1;
+	}
+
+	imc->BeginHandshake = tncc_get_sym(handle, "TNC_IMC_BeginHandshake");
+	if (imc->BeginHandshake == NULL) {
+		wpa_printf(MSG_ERROR, "TNC: IMC does not export "
+			   "TNC_IMC_BeginHandshake");
+		return -1;
+	}
+
+	imc->ProvideBindFunction =
+		tncc_get_sym(handle, "TNC_IMC_ProvideBindFunction");
+	if (imc->ProvideBindFunction == NULL) {
+		wpa_printf(MSG_ERROR, "TNC: IMC does not export "
+			   "TNC_IMC_ProvideBindFunction");
+		return -1;
+	}
+
+	/* Optional IMC functions */
+	imc->NotifyConnectionChange =
+		tncc_get_sym(handle, "TNC_IMC_NotifyConnectionChange");
+	imc->ReceiveMessage = tncc_get_sym(handle, "TNC_IMC_ReceiveMessage");
+	imc->BatchEnding = tncc_get_sym(handle, "TNC_IMC_BatchEnding");
+	imc->Terminate = tncc_get_sym(handle, "TNC_IMC_Terminate");
+
+	return 0;
+}
+
+
+static int tncc_imc_initialize(struct tnc_if_imc *imc)
+{
+	TNC_Result res;
+	TNC_Version imc_ver;
+
+	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Initialize for IMC '%s'",
+		   imc->name);
+	res = imc->Initialize(imc->imcID, TNC_IFIMC_VERSION_1,
+			      TNC_IFIMC_VERSION_1, &imc_ver);
+	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Initialize: res=%lu imc_ver=%lu",
+		   (unsigned long) res, (unsigned long) imc_ver);
+
+	return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_imc_terminate(struct tnc_if_imc *imc)
+{
+	TNC_Result res;
+
+	if (imc->Terminate == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Terminate for IMC '%s'",
+		   imc->name);
+	res = imc->Terminate(imc->imcID);
+	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Terminate: %lu",
+		   (unsigned long) res);
+
+	return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_imc_provide_bind_function(struct tnc_if_imc *imc)
+{
+	TNC_Result res;
+
+	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_ProvideBindFunction for "
+		   "IMC '%s'", imc->name);
+	res = imc->ProvideBindFunction(imc->imcID, TNC_TNCC_BindFunction);
+	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_ProvideBindFunction: res=%lu",
+		   (unsigned long) res);
+
+	return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_imc_notify_connection_change(struct tnc_if_imc *imc,
+					     TNC_ConnectionState state)
+{
+	TNC_Result res;
+
+	if (imc->NotifyConnectionChange == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_NotifyConnectionChange(%d)"
+		   " for IMC '%s'", (int) state, imc->name);
+	res = imc->NotifyConnectionChange(imc->imcID, imc->connectionID,
+					  state);
+	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
+		   (unsigned long) res);
+
+	return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_imc_begin_handshake(struct tnc_if_imc *imc)
+{
+	TNC_Result res;
+
+	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_BeginHandshake for IMC "
+		   "'%s'", imc->name);
+	res = imc->BeginHandshake(imc->imcID, imc->connectionID);
+	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_BeginHandshake: %lu",
+		   (unsigned long) res);
+
+	return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_load_imc(struct tnc_if_imc *imc)
+{
+	if (imc->path == NULL) {
+		wpa_printf(MSG_DEBUG, "TNC: No IMC configured");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TNC: Opening IMC: %s (%s)",
+		   imc->name, imc->path);
+#ifdef CONFIG_NATIVE_WINDOWS
+#ifdef UNICODE
+	{
+		TCHAR *lib = wpa_strdup_tchar(imc->path);
+		if (lib == NULL)
+			return -1;
+		imc->dlhandle = LoadLibrary(lib);
+		os_free(lib);
+	}
+#else /* UNICODE */
+	imc->dlhandle = LoadLibrary(imc->path);
+#endif /* UNICODE */
+	if (imc->dlhandle == NULL) {
+		wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %d",
+			   imc->name, imc->path, (int) GetLastError());
+		return -1;
+	}
+#else /* CONFIG_NATIVE_WINDOWS */
+	imc->dlhandle = dlopen(imc->path, RTLD_LAZY);
+	if (imc->dlhandle == NULL) {
+		wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %s",
+			   imc->name, imc->path, dlerror());
+		return -1;
+	}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	if (tncc_imc_resolve_funcs(imc) < 0) {
+		wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMC functions");
+		return -1;
+	}
+
+	if (tncc_imc_initialize(imc) < 0 ||
+	    tncc_imc_provide_bind_function(imc) < 0) {
+		wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMC");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void tncc_unload_imc(struct tnc_if_imc *imc)
+{
+	tncc_imc_terminate(imc);
+	tnc_imc[imc->imcID] = NULL;
+
+	if (imc->dlhandle) {
+#ifdef CONFIG_NATIVE_WINDOWS
+		FreeLibrary(imc->dlhandle);
+#else /* CONFIG_NATIVE_WINDOWS */
+		dlclose(imc->dlhandle);
+#endif /* CONFIG_NATIVE_WINDOWS */
+	}
+	os_free(imc->name);
+	os_free(imc->path);
+	os_free(imc->supported_types);
+	os_free(imc->imc_send);
+}
+
+
+static int tncc_supported_type(struct tnc_if_imc *imc, unsigned int type)
+{
+	size_t i;
+	unsigned int vendor, subtype;
+
+	if (imc == NULL || imc->supported_types == NULL)
+		return 0;
+
+	vendor = type >> 8;
+	subtype = type & 0xff;
+
+	for (i = 0; i < imc->num_supported_types; i++) {
+		unsigned int svendor, ssubtype;
+		svendor = imc->supported_types[i] >> 8;
+		ssubtype = imc->supported_types[i] & 0xff;
+		if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
+		    (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void tncc_send_to_imcs(struct tncc_data *tncc, unsigned int type,
+			      const u8 *msg, size_t len)
+{
+	struct tnc_if_imc *imc;
+	TNC_Result res;
+
+	wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMC(s)", msg, len);
+
+	for (imc = tncc->imc; imc; imc = imc->next) {
+		if (imc->ReceiveMessage == NULL ||
+		    !tncc_supported_type(imc, type))
+			continue;
+
+		wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMC '%s'",
+			   imc->name);
+		res = imc->ReceiveMessage(imc->imcID, imc->connectionID,
+					  (TNC_BufferReference) msg, len,
+					  type);
+		wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
+			   (unsigned long) res);
+	}
+}
+
+
+void tncc_init_connection(struct tncc_data *tncc)
+{
+	struct tnc_if_imc *imc;
+
+	for (imc = tncc->imc; imc; imc = imc->next) {
+		tncc_imc_notify_connection_change(
+			imc, TNC_CONNECTION_STATE_CREATE);
+		tncc_imc_notify_connection_change(
+			imc, TNC_CONNECTION_STATE_HANDSHAKE);
+
+		os_free(imc->imc_send);
+		imc->imc_send = NULL;
+		imc->imc_send_len = 0;
+
+		tncc_imc_begin_handshake(imc);
+	}
+}
+
+
+size_t tncc_total_send_len(struct tncc_data *tncc)
+{
+	struct tnc_if_imc *imc;
+
+	size_t len = 0;
+	for (imc = tncc->imc; imc; imc = imc->next)
+		len += imc->imc_send_len;
+	return len;
+}
+
+
+u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos)
+{
+	struct tnc_if_imc *imc;
+
+	for (imc = tncc->imc; imc; imc = imc->next) {
+		if (imc->imc_send == NULL)
+			continue;
+
+		os_memcpy(pos, imc->imc_send, imc->imc_send_len);
+		pos += imc->imc_send_len;
+		os_free(imc->imc_send);
+		imc->imc_send = NULL;
+		imc->imc_send_len = 0;
+	}
+
+	return pos;
+}
+
+
+char * tncc_if_tnccs_start(struct tncc_data *tncc)
+{
+	char *buf = os_malloc(1000);
+	if (buf == NULL)
+		return NULL;
+	tncc->last_batchid++;
+	os_snprintf(buf, 1000, IF_TNCCS_START, tncc->last_batchid);
+	return buf;
+}
+
+
+char * tncc_if_tnccs_end(void)
+{
+	char *buf = os_malloc(100);
+	if (buf == NULL)
+		return NULL;
+	os_snprintf(buf, 100, IF_TNCCS_END);
+	return buf;
+}
+
+
+static void tncc_notify_recommendation(struct tncc_data *tncc,
+				       enum tncc_process_res res)
+{
+	TNC_ConnectionState state;
+	struct tnc_if_imc *imc;
+
+	switch (res) {
+	case TNCCS_RECOMMENDATION_ALLOW:
+		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
+		break;
+	case TNCCS_RECOMMENDATION_NONE:
+		state = TNC_CONNECTION_STATE_ACCESS_NONE;
+		break;
+	case TNCCS_RECOMMENDATION_ISOLATE:
+		state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
+		break;
+	default:
+		state = TNC_CONNECTION_STATE_ACCESS_NONE;
+		break;
+	}
+
+	for (imc = tncc->imc; imc; imc = imc->next)
+		tncc_imc_notify_connection_change(imc, state);
+}
+
+
+static int tncc_get_type(char *start, unsigned int *type)
+{
+	char *pos = os_strstr(start, "<Type>");
+	if (pos == NULL)
+		return -1;
+	pos += 6;
+	*type = strtoul(pos, NULL, 16);
+	return 0;
+}
+
+
+static unsigned char * tncc_get_base64(char *start, size_t *decoded_len)
+{
+	char *pos, *pos2;
+	unsigned char *decoded;
+
+	pos = os_strstr(start, "<Base64>");
+	if (pos == NULL)
+		return NULL;
+
+	pos += 8;
+	pos2 = os_strstr(pos, "</Base64>");
+	if (pos2 == NULL)
+		return NULL;
+	*pos2 = '\0';
+
+	decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
+				decoded_len);
+	*pos2 = '<';
+	if (decoded == NULL) {
+		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
+	}
+
+	return decoded;
+}
+
+
+static enum tncc_process_res tncc_get_recommendation(char *start)
+{
+	char *pos, *pos2, saved;
+	int recom;
+
+	pos = os_strstr(start, "<TNCCS-Recommendation ");
+	if (pos == NULL)
+		return TNCCS_RECOMMENDATION_ERROR;
+
+	pos += 21;
+	pos = os_strstr(pos, " type=");
+	if (pos == NULL)
+		return TNCCS_RECOMMENDATION_ERROR;
+	pos += 6;
+
+	if (*pos == '"')
+		pos++;
+
+	pos2 = pos;
+	while (*pos2 != '\0' && *pos2 != '"' && *pos2 != '>')
+		pos2++;
+
+	if (*pos2 == '\0')
+		return TNCCS_RECOMMENDATION_ERROR;
+
+	saved = *pos2;
+	*pos2 = '\0';
+	wpa_printf(MSG_DEBUG, "TNC: TNCCS-Recommendation: '%s'", pos);
+
+	recom = TNCCS_RECOMMENDATION_ERROR;
+	if (os_strcmp(pos, "allow") == 0)
+		recom = TNCCS_RECOMMENDATION_ALLOW;
+	else if (os_strcmp(pos, "none") == 0)
+		recom = TNCCS_RECOMMENDATION_NONE;
+	else if (os_strcmp(pos, "isolate") == 0)
+		recom = TNCCS_RECOMMENDATION_ISOLATE;
+
+	*pos2 = saved;
+
+	return recom;
+}
+
+
+enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc,
+					    const u8 *msg, size_t len)
+{
+	char *buf, *start, *end, *pos, *pos2, *payload;
+	unsigned int batch_id;
+	unsigned char *decoded;
+	size_t decoded_len;
+	enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION;
+	int recommendation_msg = 0;
+
+	buf = dup_binstr(msg, len);
+	if (buf == NULL)
+		return TNCCS_PROCESS_ERROR;
+
+	start = os_strstr(buf, "<TNCCS-Batch ");
+	end = os_strstr(buf, "</TNCCS-Batch>");
+	if (start == NULL || end == NULL || start > end) {
+		os_free(buf);
+		return TNCCS_PROCESS_ERROR;
+	}
+
+	start += 13;
+	while (*start == ' ')
+		start++;
+	*end = '\0';
+
+	pos = os_strstr(start, "BatchId=");
+	if (pos == NULL) {
+		os_free(buf);
+		return TNCCS_PROCESS_ERROR;
+	}
+
+	pos += 8;
+	if (*pos == '"')
+		pos++;
+	batch_id = atoi(pos);
+	wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
+		   batch_id);
+	if (batch_id != tncc->last_batchid + 1) {
+		wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
+			   "%u (expected %u)",
+			   batch_id, tncc->last_batchid + 1);
+		os_free(buf);
+		return TNCCS_PROCESS_ERROR;
+	}
+	tncc->last_batchid = batch_id;
+
+	while (*pos != '\0' && *pos != '>')
+		pos++;
+	if (*pos == '\0') {
+		os_free(buf);
+		return TNCCS_PROCESS_ERROR;
+	}
+	pos++;
+	payload = start;
+
+	/*
+	 * <IMC-IMV-Message>
+	 * <Type>01234567</Type>
+	 * <Base64>foo==</Base64>
+	 * </IMC-IMV-Message>
+	 */
+
+	while (*start) {
+		char *endpos;
+		unsigned int type;
+
+		pos = os_strstr(start, "<IMC-IMV-Message>");
+		if (pos == NULL)
+			break;
+		start = pos + 17;
+		end = os_strstr(start, "</IMC-IMV-Message>");
+		if (end == NULL)
+			break;
+		*end = '\0';
+		endpos = end;
+		end += 18;
+
+		if (tncc_get_type(start, &type) < 0) {
+			*endpos = '<';
+			start = end;
+			continue;
+		}
+		wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
+
+		decoded = tncc_get_base64(start, &decoded_len);
+		if (decoded == NULL) {
+			*endpos = '<';
+			start = end;
+			continue;
+		}
+
+		tncc_send_to_imcs(tncc, type, decoded, decoded_len);
+
+		os_free(decoded);
+
+		start = end;
+	}
+
+	/*
+	 * <TNCC-TNCS-Message>
+	 * <Type>01234567</Type>
+	 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
+	 * <Base64>foo==</Base64>
+	 * </TNCC-TNCS-Message>
+	 */
+
+	start = payload;
+	while (*start) {
+		unsigned int type;
+		char *xml, *xmlend, *endpos;
+
+		pos = os_strstr(start, "<TNCC-TNCS-Message>");
+		if (pos == NULL)
+			break;
+		start = pos + 19;
+		end = os_strstr(start, "</TNCC-TNCS-Message>");
+		if (end == NULL)
+			break;
+		*end = '\0';
+		endpos = end;
+		end += 20;
+
+		if (tncc_get_type(start, &type) < 0) {
+			*endpos = '<';
+			start = end;
+			continue;
+		}
+		wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
+			   type);
+
+		/* Base64 OR XML */
+		decoded = NULL;
+		xml = NULL;
+		xmlend = NULL;
+		pos = os_strstr(start, "<XML>");
+		if (pos) {
+			pos += 5;
+			pos2 = os_strstr(pos, "</XML>");
+			if (pos2 == NULL) {
+				*endpos = '<';
+				start = end;
+				continue;
+			}
+			xmlend = pos2;
+			xml = pos;
+		} else {
+			decoded = tncc_get_base64(start, &decoded_len);
+			if (decoded == NULL) {
+				*endpos = '<';
+				start = end;
+				continue;
+			}
+		}
+
+		if (decoded) {
+			wpa_hexdump_ascii(MSG_MSGDUMP,
+					  "TNC: TNCC-TNCS-Message Base64",
+					  decoded, decoded_len);
+			os_free(decoded);
+		}
+
+		if (xml) {
+			wpa_hexdump_ascii(MSG_MSGDUMP,
+					  "TNC: TNCC-TNCS-Message XML",
+					  (unsigned char *) xml,
+					  xmlend - xml);
+		}
+
+		if (type == TNC_TNCCS_RECOMMENDATION && xml) {
+			/*
+			 * <TNCCS-Recommendation type="allow">
+			 * </TNCCS-Recommendation>
+			 */
+			*xmlend = '\0';
+			res = tncc_get_recommendation(xml);
+			*xmlend = '<';
+			recommendation_msg = 1;
+		}
+
+		start = end;
+	}
+
+	os_free(buf);
+
+	if (recommendation_msg)
+		tncc_notify_recommendation(tncc, res);
+
+	return res;
+}
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+static int tncc_read_config_reg(struct tncc_data *tncc, HKEY hive)
+{
+	HKEY hk, hk2;
+	LONG ret;
+	DWORD i;
+	struct tnc_if_imc *imc, *last;
+	int j;
+
+	last = tncc->imc;
+	while (last && last->next)
+		last = last->next;
+
+	ret = RegOpenKeyEx(hive, TNC_WINREG_PATH, 0, KEY_ENUMERATE_SUB_KEYS,
+			   &hk);
+	if (ret != ERROR_SUCCESS)
+		return 0;
+
+	for (i = 0; ; i++) {
+		TCHAR name[255], *val;
+		DWORD namelen, buflen;
+
+		namelen = 255;
+		ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL,
+				   NULL);
+
+		if (ret == ERROR_NO_MORE_ITEMS)
+			break;
+
+		if (ret != ERROR_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "TNC: RegEnumKeyEx failed: 0x%x",
+				   (unsigned int) ret);
+			break;
+		}
+
+		if (namelen >= 255)
+			namelen = 255 - 1;
+		name[namelen] = '\0';
+
+		wpa_printf(MSG_DEBUG, "TNC: IMC '" TSTR "'", name);
+
+		ret = RegOpenKeyEx(hk, name, 0, KEY_QUERY_VALUE, &hk2);
+		if (ret != ERROR_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "Could not open IMC key '" TSTR
+				   "'", name);
+			continue;
+		}
+
+		ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, NULL,
+				      &buflen);
+		if (ret != ERROR_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "TNC: Could not read Path from "
+				   "IMC key '" TSTR "'", name);
+			RegCloseKey(hk2);
+			continue;
+		}
+
+		val = os_malloc(buflen);
+		if (val == NULL) {
+			RegCloseKey(hk2);
+			continue;
+		}
+
+		ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL,
+				      (LPBYTE) val, &buflen);
+		if (ret != ERROR_SUCCESS) {
+			os_free(val);
+			RegCloseKey(hk2);
+			continue;
+		}
+
+		RegCloseKey(hk2);
+
+		wpa_unicode2ascii_inplace(val);
+		wpa_printf(MSG_DEBUG, "TNC: IMC Path '%s'", (char *) val);
+
+		for (j = 0; j < TNC_MAX_IMC_ID; j++) {
+			if (tnc_imc[j] == NULL)
+				break;
+		}
+		if (j >= TNC_MAX_IMC_ID) {
+			wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
+			os_free(val);
+			continue;
+		}
+
+		imc = os_zalloc(sizeof(*imc));
+		if (imc == NULL) {
+			os_free(val);
+			break;
+		}
+
+		imc->imcID = j;
+
+		wpa_unicode2ascii_inplace(name);
+		imc->name = os_strdup((char *) name);
+		imc->path = os_strdup((char *) val);
+
+		os_free(val);
+
+		if (last == NULL)
+			tncc->imc = imc;
+		else
+			last->next = imc;
+		last = imc;
+
+		tnc_imc[imc->imcID] = imc;
+	}
+
+	RegCloseKey(hk);
+
+	return 0;
+}
+
+
+static int tncc_read_config(struct tncc_data *tncc)
+{
+	if (tncc_read_config_reg(tncc, HKEY_LOCAL_MACHINE) < 0 ||
+	    tncc_read_config_reg(tncc, HKEY_CURRENT_USER) < 0)
+		return -1;
+	return 0;
+}
+
+#else /* CONFIG_NATIVE_WINDOWS */
+
+static struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error)
+{
+	struct tnc_if_imc *imc;
+	char *pos, *pos2;
+	int i;
+
+	for (i = 0; i < TNC_MAX_IMC_ID; i++) {
+		if (tnc_imc[i] == NULL)
+			break;
+	}
+	if (i >= TNC_MAX_IMC_ID) {
+		wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
+		return NULL;
+	}
+
+	imc = os_zalloc(sizeof(*imc));
+	if (imc == NULL) {
+		*error = 1;
+		return NULL;
+	}
+
+	imc->imcID = i;
+
+	pos = start;
+	wpa_printf(MSG_DEBUG, "TNC: Configured IMC: %s", pos);
+	if (pos + 1 >= end || *pos != '"') {
+		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
+			   "(no starting quotation mark)", start);
+		os_free(imc);
+		return NULL;
+	}
+
+	pos++;
+	pos2 = pos;
+	while (pos2 < end && *pos2 != '"')
+		pos2++;
+	if (pos2 >= end) {
+		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
+			   "(no ending quotation mark)", start);
+		os_free(imc);
+		return NULL;
+	}
+	*pos2 = '\0';
+	wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
+	imc->name = os_strdup(pos);
+
+	pos = pos2 + 1;
+	if (pos >= end || *pos != ' ') {
+		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
+			   "(no space after name)", start);
+		os_free(imc->name);
+		os_free(imc);
+		return NULL;
+	}
+
+	pos++;
+	wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos);
+	imc->path = os_strdup(pos);
+	tnc_imc[imc->imcID] = imc;
+
+	return imc;
+}
+
+
+static int tncc_read_config(struct tncc_data *tncc)
+{
+	char *config, *end, *pos, *line_end;
+	size_t config_len;
+	struct tnc_if_imc *imc, *last;
+
+	last = NULL;
+
+	config = os_readfile(TNC_CONFIG_FILE, &config_len);
+	if (config == NULL) {
+		wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
+			   "file '%s'", TNC_CONFIG_FILE);
+		return -1;
+	}
+
+	end = config + config_len;
+	for (pos = config; pos < end; pos = line_end + 1) {
+		line_end = pos;
+		while (*line_end != '\n' && *line_end != '\r' &&
+		       line_end < end)
+			line_end++;
+		*line_end = '\0';
+
+		if (os_strncmp(pos, "IMC ", 4) == 0) {
+			int error = 0;
+
+			imc = tncc_parse_imc(pos + 4, line_end, &error);
+			if (error) {
+				os_free(config);
+				return -1;
+			}
+			if (imc) {
+				if (last == NULL)
+					tncc->imc = imc;
+				else
+					last->next = imc;
+				last = imc;
+			}
+		}
+	}
+
+	os_free(config);
+
+	return 0;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+struct tncc_data * tncc_init(void)
+{
+	struct tncc_data *tncc;
+	struct tnc_if_imc *imc;
+
+	tncc = os_zalloc(sizeof(*tncc));
+	if (tncc == NULL)
+		return NULL;
+
+	/* TODO:
+	 * move loading and Initialize() to a location that is not
+	 *    re-initialized for every EAP-TNC session (?)
+	 */
+
+	if (tncc_read_config(tncc) < 0) {
+		wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
+		goto failed;
+	}
+
+	for (imc = tncc->imc; imc; imc = imc->next) {
+		if (tncc_load_imc(imc)) {
+			wpa_printf(MSG_ERROR, "TNC: Failed to load IMC '%s'",
+				   imc->name);
+			goto failed;
+		}
+	}
+
+	return tncc;
+
+failed:
+	tncc_deinit(tncc);
+	return NULL;
+}
+
+
+void tncc_deinit(struct tncc_data *tncc)
+{
+	struct tnc_if_imc *imc, *prev;
+
+	imc = tncc->imc;
+	while (imc) {
+		tncc_unload_imc(imc);
+
+		prev = imc;
+		imc = imc->next;
+		os_free(prev);
+	}
+
+	os_free(tncc);
+}
+
+
+static struct wpabuf * tncc_build_soh(int ver)
+{
+	struct wpabuf *buf;
+	u8 *tlv_len, *tlv_len2, *outer_len, *inner_len, *ssoh_len, *end;
+	u8 correlation_id[24];
+	/* TODO: get correct name */
+	char *machinename = "wpa_supplicant@w1.fi";
+
+	if (os_get_random(correlation_id, sizeof(correlation_id)))
+		return NULL;
+	wpa_hexdump(MSG_DEBUG, "TNC: SoH Correlation ID",
+		    correlation_id, sizeof(correlation_id));
+
+	buf = wpabuf_alloc(200);
+	if (buf == NULL)
+		return NULL;
+
+	/* Vendor-Specific TLV (Microsoft) - SoH */
+	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
+	tlv_len = wpabuf_put(buf, 2); /* Length */
+	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
+	wpabuf_put_be16(buf, 0x01); /* TLV Type - SoH TLV */
+	tlv_len2 = wpabuf_put(buf, 2); /* Length */
+
+	/* SoH Header */
+	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* Outer Type */
+	outer_len = wpabuf_put(buf, 2);
+	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
+	wpabuf_put_be16(buf, ver); /* Inner Type */
+	inner_len = wpabuf_put(buf, 2);
+
+	if (ver == 2) {
+		/* SoH Mode Sub-Header */
+		/* Outer Type */
+		wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
+		wpabuf_put_be16(buf, 4 + 24 + 1 + 1); /* Length */
+		wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
+		/* Value: */
+		wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
+		wpabuf_put_u8(buf, 0x01); /* Intent Flag - Request */
+		wpabuf_put_u8(buf, 0x00); /* Content-Type Flag */
+	}
+
+	/* SSoH TLV */
+	/* System-Health-Id */
+	wpabuf_put_be16(buf, 0x0002); /* Type */
+	wpabuf_put_be16(buf, 4); /* Length */
+	wpabuf_put_be32(buf, 79616);
+	/* Vendor-Specific Attribute */
+	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
+	ssoh_len = wpabuf_put(buf, 2);
+	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
+
+	/* MS-Packet-Info */
+	wpabuf_put_u8(buf, SSOH_MS_PACKET_INFO);
+	/* Note: IF-TNCCS-SOH v1.0 r8 claims this field to be:
+	 * Reserved(4 bits) r(1 bit) Vers(3 bits), but Windows XP
+	 * SP3 seems to be sending 0x11 for SSoH, i.e., r(request/response) bit
+	 * would not be in the specified location.
+	 * [MS-SOH] 4.0.2: Reserved(3 bits) r(1 bit) Vers(4 bits)
+	 */
+	wpabuf_put_u8(buf, 0x11); /* r=request, vers=1 */
+
+	/* MS-Machine-Inventory */
+	/* TODO: get correct values; 0 = not applicable for OS */
+	wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY);
+	wpabuf_put_be32(buf, 0); /* osVersionMajor */
+	wpabuf_put_be32(buf, 0); /* osVersionMinor */
+	wpabuf_put_be32(buf, 0); /* osVersionBuild */
+	wpabuf_put_be16(buf, 0); /* spVersionMajor */
+	wpabuf_put_be16(buf, 0); /* spVersionMinor */
+	wpabuf_put_be16(buf, 0); /* procArch */
+
+	/* MS-MachineName */
+	wpabuf_put_u8(buf, SSOH_MS_MACHINENAME);
+	wpabuf_put_be16(buf, os_strlen(machinename) + 1);
+	wpabuf_put_data(buf, machinename, os_strlen(machinename) + 1);
+
+	/* MS-CorrelationId */
+	wpabuf_put_u8(buf, SSOH_MS_CORRELATIONID);
+	wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
+
+	/* MS-Quarantine-State */
+	wpabuf_put_u8(buf, SSOH_MS_QUARANTINE_STATE);
+	wpabuf_put_be16(buf, 1); /* Flags: ExtState=0, f=0, qState=1 */
+	wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (hi) */
+	wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (lo) */
+	wpabuf_put_be16(buf, 1); /* urlLenInBytes */
+	wpabuf_put_u8(buf, 0); /* null termination for the url */
+
+	/* MS-Machine-Inventory-Ex */
+	wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY_EX);
+	wpabuf_put_be32(buf, 0); /* Reserved
+				  * (note: Windows XP SP3 uses 0xdecafbad) */
+	wpabuf_put_u8(buf, 1); /* ProductType: Client */
+
+	/* Update SSoH Length */
+	end = wpabuf_put(buf, 0);
+	WPA_PUT_BE16(ssoh_len, end - ssoh_len - 2);
+
+	/* TODO: SoHReportEntry TLV (zero or more) */
+
+	/* Update length fields */
+	end = wpabuf_put(buf, 0);
+	WPA_PUT_BE16(tlv_len, end - tlv_len - 2);
+	WPA_PUT_BE16(tlv_len2, end - tlv_len2 - 2);
+	WPA_PUT_BE16(outer_len, end - outer_len - 2);
+	WPA_PUT_BE16(inner_len, end - inner_len - 2);
+
+	return buf;
+}
+
+
+struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len)
+{
+	const u8 *pos;
+
+	wpa_hexdump(MSG_DEBUG, "TNC: SoH Request", data, len);
+
+	if (len < 12)
+		return NULL;
+
+	/* SoH Request */
+	pos = data;
+
+	/* TLV Type */
+	if (WPA_GET_BE16(pos) != EAP_TLV_VENDOR_SPECIFIC_TLV)
+		return NULL;
+	pos += 2;
+
+	/* Length */
+	if (WPA_GET_BE16(pos) < 8)
+		return NULL;
+	pos += 2;
+
+	/* Vendor_Id */
+	if (WPA_GET_BE32(pos) != EAP_VENDOR_MICROSOFT)
+		return NULL;
+	pos += 4;
+
+	/* TLV Type */
+	if (WPA_GET_BE16(pos) != 0x02 /* SoH request TLV */)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "TNC: SoH Request TLV received");
+
+	return tncc_build_soh(2);
+}
diff --git a/hostap/src/eap_peer/tncc.h b/hostap/src/eap_peer/tncc.h
new file mode 100644
index 0000000..df2a287
--- /dev/null
+++ b/hostap/src/eap_peer/tncc.h
@@ -0,0 +1,36 @@
+/*
+ * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TNCC_H
+#define TNCC_H
+
+struct tncc_data;
+
+struct tncc_data * tncc_init(void);
+void tncc_deinit(struct tncc_data *tncc);
+void tncc_init_connection(struct tncc_data *tncc);
+size_t tncc_total_send_len(struct tncc_data *tncc);
+u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos);
+char * tncc_if_tnccs_start(struct tncc_data *tncc);
+char * tncc_if_tnccs_end(void);
+
+enum tncc_process_res {
+	TNCCS_PROCESS_ERROR = -1,
+	TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0,
+	TNCCS_RECOMMENDATION_ERROR,
+	TNCCS_RECOMMENDATION_ALLOW,
+	TNCCS_RECOMMENDATION_NONE,
+	TNCCS_RECOMMENDATION_ISOLATE
+};
+
+enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc,
+					    const u8 *msg, size_t len);
+
+struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len);
+
+#endif /* TNCC_H */
diff --git a/hostap/src/eap_server/Makefile b/hostap/src/eap_server/Makefile
new file mode 100644
index 0000000..1172b72
--- /dev/null
+++ b/hostap/src/eap_server/Makefile
@@ -0,0 +1,21 @@
+all: libeap_server.a
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeap_server.a
+
+install:
+	@echo Nothing to be made.
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_HS20
+
+LIB_OBJS= \
+	eap_server.o \
+	eap_server_identity.o \
+	eap_server_methods.o
+
+libeap_server.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/eap_server/eap.h b/hostap/src/eap_server/eap.h
new file mode 100644
index 0000000..69eaab8
--- /dev/null
+++ b/hostap/src/eap_server/eap.h
@@ -0,0 +1,157 @@
+/*
+ * hostapd / EAP Full Authenticator state machine (RFC 4137)
+ * 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.
+ */
+
+#ifndef EAP_H
+#define EAP_H
+
+#include "common/defs.h"
+#include "utils/list.h"
+#include "eap_common/eap_defs.h"
+#include "eap_server/eap_methods.h"
+#include "wpabuf.h"
+
+struct eap_sm;
+
+#define EAP_TTLS_AUTH_PAP 1
+#define EAP_TTLS_AUTH_CHAP 2
+#define EAP_TTLS_AUTH_MSCHAP 4
+#define EAP_TTLS_AUTH_MSCHAPV2 8
+
+struct eap_user {
+	struct {
+		int vendor;
+		u32 method;
+	} methods[EAP_MAX_METHODS];
+	u8 *password;
+	size_t password_len;
+	int password_hash; /* whether password is hashed with
+			    * 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 {
+	/* Lower layer to full authenticator variables */
+	Boolean eapResp; /* shared with EAPOL Backend Authentication */
+	struct wpabuf *eapRespData;
+	Boolean portEnabled;
+	int retransWhile;
+	Boolean eapRestart; /* shared with EAPOL Authenticator PAE */
+	int eapSRTT;
+	int eapRTTVAR;
+
+	/* Full authenticator to lower layer variables */
+	Boolean eapReq; /* shared with EAPOL Backend Authentication */
+	Boolean eapNoReq; /* shared with EAPOL Backend Authentication */
+	Boolean eapSuccess;
+	Boolean eapFail;
+	Boolean eapTimeout;
+	struct wpabuf *eapReqData;
+	u8 *eapKeyData;
+	size_t eapKeyDataLen;
+	u8 *eapSessionId;
+	size_t eapSessionIdLen;
+	Boolean eapKeyAvailable; /* called keyAvailable in IEEE 802.1X-2004 */
+
+	/* AAA interface to full authenticator variables */
+	Boolean aaaEapReq;
+	Boolean aaaEapNoReq;
+	Boolean aaaSuccess;
+	Boolean aaaFail;
+	struct wpabuf *aaaEapReqData;
+	u8 *aaaEapKeyData;
+	size_t aaaEapKeyDataLen;
+	Boolean aaaEapKeyAvailable;
+	int aaaMethodTimeout;
+
+	/* Full authenticator to AAA interface variables */
+	Boolean aaaEapResp;
+	struct wpabuf *aaaEapRespData;
+	/* aaaIdentity -> eap_get_identity() */
+	Boolean aaaTimeout;
+};
+
+struct eap_server_erp_key {
+	struct dl_list list;
+	size_t rRK_len;
+	size_t rIK_len;
+	u8 rRK[ERP_MAX_KEY_LEN];
+	u8 rIK[ERP_MAX_KEY_LEN];
+	u32 recv_seq;
+	u8 cryptosuite;
+	char keyname_nai[];
+};
+
+struct eapol_callbacks {
+	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);
+	int (*get_erp_send_reauth_start)(void *ctx);
+	const char * (*get_erp_domain)(void *ctx);
+	struct eap_server_erp_key * (*erp_get_key)(void *ctx,
+						   const char *keyname);
+	int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp);
+};
+
+struct eap_config {
+	void *ssl_ctx;
+	void *msg_ctx;
+	void *eap_sim_db_priv;
+	Boolean backend_auth;
+	int eap_server;
+	u16 pwd_group;
+	u8 *pac_opaque_encr_key;
+	u8 *eap_fast_a_id;
+	size_t eap_fast_a_id_len;
+	char *eap_fast_a_id_info;
+	int eap_fast_prov;
+	int pac_key_lifetime;
+	int pac_key_refresh_time;
+	int eap_sim_aka_result_ind;
+	int tnc;
+	struct wps_context *wps;
+	const struct wpabuf *assoc_wps_ie;
+	const struct wpabuf *assoc_p2p_ie;
+	const u8 *peer_addr;
+	int fragment_size;
+
+	int pbc_in_m1;
+
+	const u8 *server_id;
+	size_t server_id_len;
+	int erp;
+	unsigned int tls_session_lifetime;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	u32 tls_test_flags;
+#endif /* CONFIG_TESTING_OPTIONS */
+};
+
+
+struct eap_sm * eap_server_sm_init(void *eapol_ctx,
+				   const struct eapol_callbacks *eapol_cb,
+				   struct eap_config *eap_conf);
+void eap_server_sm_deinit(struct eap_sm *sm);
+int eap_server_sm_step(struct eap_sm *sm);
+void eap_sm_notify_cached(struct eap_sm *sm);
+void eap_sm_pending_cb(struct eap_sm *sm);
+int eap_sm_method_pending(struct eap_sm *sm);
+const u8 * eap_get_identity(struct eap_sm *sm, size_t *len);
+struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm);
+void eap_server_clear_identity(struct eap_sm *sm);
+void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source,
+				   const u8 *username, size_t username_len,
+				   const u8 *challenge, const u8 *response);
+
+#endif /* EAP_H */
diff --git a/hostap/src/eap_server/eap_i.h b/hostap/src/eap_server/eap_i.h
new file mode 100644
index 0000000..c90443d
--- /dev/null
+++ b/hostap/src/eap_server/eap_i.h
@@ -0,0 +1,226 @@
+/*
+ * hostapd / EAP Authenticator state machine internal structures (RFC 4137)
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_I_H
+#define EAP_I_H
+
+#include "wpabuf.h"
+#include "eap_server/eap.h"
+#include "eap_common/eap_common.h"
+
+/* RFC 4137 - EAP Standalone Authenticator */
+
+/**
+ * struct eap_method - EAP method interface
+ * This structure defines the EAP method interface. Each method will need to
+ * register its own EAP type, EAP name, and set of function pointers for method
+ * specific operations. This interface is based on section 5.4 of RFC 4137.
+ */
+struct eap_method {
+	int vendor;
+	EapType method;
+	const char *name;
+
+	void * (*init)(struct eap_sm *sm);
+	void * (*initPickUp)(struct eap_sm *sm);
+	void (*reset)(struct eap_sm *sm, void *priv);
+
+	struct wpabuf * (*buildReq)(struct eap_sm *sm, void *priv, u8 id);
+	int (*getTimeout)(struct eap_sm *sm, void *priv);
+	Boolean (*check)(struct eap_sm *sm, void *priv,
+			 struct wpabuf *respData);
+	void (*process)(struct eap_sm *sm, void *priv,
+			struct wpabuf *respData);
+	Boolean (*isDone)(struct eap_sm *sm, void *priv);
+	u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
+	/* isSuccess is not specified in draft-ietf-eap-statemachine-05.txt,
+	 * but it is useful in implementing Policy.getDecision() */
+	Boolean (*isSuccess)(struct eap_sm *sm, void *priv);
+
+	/**
+	 * free - Free EAP method data
+	 * @method: Pointer to the method data registered with
+	 * eap_server_method_register().
+	 *
+	 * This function will be called when the EAP method is being
+	 * unregistered. If the EAP method allocated resources during
+	 * registration (e.g., allocated struct eap_method), they should be
+	 * freed in this function. No other method functions will be called
+	 * after this call. If this function is not defined (i.e., function
+	 * pointer is %NULL), a default handler is used to release the method
+	 * data with free(method). This is suitable for most cases.
+	 */
+	void (*free)(struct eap_method *method);
+
+#define EAP_SERVER_METHOD_INTERFACE_VERSION 1
+	/**
+	 * version - Version of the EAP server method interface
+	 *
+	 * The EAP server method implementation should set this variable to
+	 * EAP_SERVER_METHOD_INTERFACE_VERSION. This is used to verify that the
+	 * EAP method is using supported API version when using dynamically
+	 * loadable EAP methods.
+	 */
+	int version;
+
+	/**
+	 * next - Pointer to the next EAP method
+	 *
+	 * This variable is used internally in the EAP method registration code
+	 * to create a linked list of registered EAP methods.
+	 */
+	struct eap_method *next;
+
+	/**
+	 * get_emsk - Get EAP method specific keying extended material (EMSK)
+	 * @sm: Pointer to EAP state machine allocated with eap_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * @len: Pointer to a variable to store EMSK length
+	 * Returns: EMSK or %NULL if not available
+	 *
+	 * This function can be used to get the extended keying material from
+	 * the EAP method. The key may already be stored in the method-specific
+	 * private data or this function may derive the key.
+	 */
+	u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
+
+	/**
+	 * getSessionId - Get EAP method specific Session-Id
+	 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * @len: Pointer to a variable to store Session-Id length
+	 * Returns: Session-Id or %NULL if not available
+	 *
+	 * This function can be used to get the Session-Id from the EAP method.
+	 * The Session-Id may already be stored in the method-specific private
+	 * data or this function may derive the Session-Id.
+	 */
+	u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len);
+};
+
+/**
+ * struct eap_sm - EAP server state machine data
+ */
+struct eap_sm {
+	enum {
+		EAP_DISABLED, EAP_INITIALIZE, EAP_IDLE, EAP_RECEIVED,
+		EAP_INTEGRITY_CHECK, EAP_METHOD_RESPONSE, EAP_METHOD_REQUEST,
+		EAP_PROPOSE_METHOD, EAP_SELECT_ACTION, EAP_SEND_REQUEST,
+		EAP_DISCARD, EAP_NAK, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE,
+		EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD,
+		EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2,
+		EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2,
+		EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE,
+		EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2,
+		EAP_INITIATE_REAUTH_START, EAP_INITIATE_RECEIVED
+	} EAP_state;
+
+	/* Constants */
+	int MaxRetrans;
+
+	struct eap_eapol_interface eap_if;
+
+	/* Full authenticator state machine local variables */
+
+	/* Long-term (maintained between packets) */
+	EapType currentMethod;
+	int currentId;
+	enum {
+		METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END
+	} methodState;
+	int retransCount;
+	struct wpabuf *lastReqData;
+	int methodTimeout;
+
+	/* Short-term (not maintained between packets) */
+	Boolean rxResp;
+	Boolean rxInitiate;
+	int respId;
+	EapType respMethod;
+	int respVendor;
+	u32 respVendorMethod;
+	Boolean ignore;
+	enum {
+		DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE,
+		DECISION_PASSTHROUGH, DECISION_INITIATE_REAUTH_START
+	} decision;
+
+	/* Miscellaneous variables */
+	const struct eap_method *m; /* selected EAP method */
+	/* not defined in RFC 4137 */
+	Boolean changed;
+	void *eapol_ctx, *msg_ctx;
+	const struct eapol_callbacks *eapol_cb;
+	void *eap_method_priv;
+	u8 *identity;
+	size_t identity_len;
+	/* Whether Phase 2 method should validate identity match */
+	int require_identity_match;
+	int lastId; /* Identifier used in the last EAP-Packet */
+	struct eap_user *user;
+	int user_eap_method_index;
+	int init_phase2;
+	void *ssl_ctx;
+	struct eap_sim_db_data *eap_sim_db_priv;
+	Boolean backend_auth;
+	Boolean update_user;
+	int eap_server;
+
+	int num_rounds;
+	enum {
+		METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT
+	} method_pending;
+
+	u8 *auth_challenge;
+	u8 *peer_challenge;
+
+	u8 *pac_opaque_encr_key;
+	u8 *eap_fast_a_id;
+	size_t eap_fast_a_id_len;
+	char *eap_fast_a_id_info;
+	enum {
+		NO_PROV, ANON_PROV, AUTH_PROV, BOTH_PROV
+	} eap_fast_prov;
+	int pac_key_lifetime;
+	int pac_key_refresh_time;
+	int eap_sim_aka_result_ind;
+	int tnc;
+	u16 pwd_group;
+	struct wps_context *wps;
+	struct wpabuf *assoc_wps_ie;
+	struct wpabuf *assoc_p2p_ie;
+
+	Boolean start_reauth;
+
+	u8 peer_addr[ETH_ALEN];
+
+	/* Fragmentation size for EAP method init() handler */
+	int fragment_size;
+
+	int pbc_in_m1;
+
+	const u8 *server_id;
+	size_t server_id_len;
+
+	Boolean initiate_reauth_start_sent;
+	Boolean try_initiate_reauth;
+	int erp;
+	unsigned int tls_session_lifetime;
+
+#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/hostap/src/eap_server/eap_methods.h b/hostap/src/eap_server/eap_methods.h
new file mode 100644
index 0000000..0baa327
--- /dev/null
+++ b/hostap/src/eap_server/eap_methods.h
@@ -0,0 +1,51 @@
+/*
+ * EAP server method registration
+ * 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.
+ */
+
+#ifndef EAP_SERVER_METHODS_H
+#define EAP_SERVER_METHODS_H
+
+#include "eap_common/eap_defs.h"
+
+const struct eap_method * eap_server_get_eap_method(int vendor,
+						    EapType method);
+struct eap_method * eap_server_method_alloc(int version, int vendor,
+					    EapType method, const char *name);
+void eap_server_method_free(struct eap_method *method);
+int eap_server_method_register(struct eap_method *method);
+
+EapType eap_server_get_type(const char *name, int *vendor);
+void eap_server_unregister_methods(void);
+const char * eap_server_get_name(int vendor, EapType type);
+
+/* EAP server method registration calls for statically linked in methods */
+int eap_server_identity_register(void);
+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);
+int eap_server_gtc_register(void);
+int eap_server_ttls_register(void);
+int eap_server_sim_register(void);
+int eap_server_aka_register(void);
+int eap_server_aka_prime_register(void);
+int eap_server_pax_register(void);
+int eap_server_psk_register(void);
+int eap_server_sake_register(void);
+int eap_server_gpsk_register(void);
+int eap_server_vendor_test_register(void);
+int eap_server_fast_register(void);
+int eap_server_wsc_register(void);
+int eap_server_ikev2_register(void);
+int eap_server_tnc_register(void);
+int eap_server_pwd_register(void);
+int eap_server_eke_register(void);
+
+#endif /* EAP_SERVER_METHODS_H */
diff --git a/hostap/src/eap_server/eap_server.c b/hostap/src/eap_server/eap_server.c
new file mode 100644
index 0000000..84ecafc
--- /dev/null
+++ b/hostap/src/eap_server/eap_server.c
@@ -0,0 +1,2016 @@
+/*
+ * hostapd / EAP Full Authenticator state machine (RFC 4137)
+ * 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.
+ *
+ * This state machine is based on the full authenticator state machine defined
+ * in RFC 4137. However, to support backend authentication in RADIUS
+ * authentication server functionality, parts of backend authenticator (also
+ * from RFC 4137) are mixed in. This functionality is enabled by setting
+ * backend_auth configuration variable to TRUE.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha256.h"
+#include "eap_i.h"
+#include "state_machine.h"
+#include "common/wpa_ctrl.h"
+
+#define STATE_MACHINE_DATA struct eap_sm
+#define STATE_MACHINE_DEBUG_PREFIX "EAP"
+
+#define EAP_MAX_AUTH_ROUNDS 50
+
+static void eap_user_free(struct eap_user *user);
+
+
+/* EAP state machines are described in RFC 4137 */
+
+static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
+				   int eapSRTT, int eapRTTVAR,
+				   int methodTimeout);
+static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp);
+static int eap_sm_getId(const struct wpabuf *data);
+static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id);
+static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id);
+static int eap_sm_nextId(struct eap_sm *sm, int id);
+static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
+				 size_t len);
+static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor);
+static int eap_sm_Policy_getDecision(struct eap_sm *sm);
+static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);
+
+
+static int eap_get_erp_send_reauth_start(struct eap_sm *sm)
+{
+	if (sm->eapol_cb->get_erp_send_reauth_start)
+		return sm->eapol_cb->get_erp_send_reauth_start(sm->eapol_ctx);
+	return 0;
+}
+
+
+static const char * eap_get_erp_domain(struct eap_sm *sm)
+{
+	if (sm->eapol_cb->get_erp_domain)
+		return sm->eapol_cb->get_erp_domain(sm->eapol_ctx);
+	return NULL;
+}
+
+
+#ifdef CONFIG_ERP
+
+static struct eap_server_erp_key * eap_erp_get_key(struct eap_sm *sm,
+						   const char *keyname)
+{
+	if (sm->eapol_cb->erp_get_key)
+		return sm->eapol_cb->erp_get_key(sm->eapol_ctx, keyname);
+	return NULL;
+}
+
+
+static int eap_erp_add_key(struct eap_sm *sm, struct eap_server_erp_key *erp)
+{
+	if (sm->eapol_cb->erp_add_key)
+		return sm->eapol_cb->erp_add_key(sm->eapol_ctx, erp);
+	return -1;
+}
+
+#endif /* CONFIG_ERP */
+
+
+static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm,
+						       u8 id)
+{
+	const char *domain;
+	size_t plen = 1;
+	struct wpabuf *msg;
+	size_t domain_len = 0;
+
+	domain = eap_get_erp_domain(sm);
+	if (domain) {
+		domain_len = os_strlen(domain);
+		plen += 2 + domain_len;
+	}
+
+	msg = eap_msg_alloc(EAP_VENDOR_IETF,
+			    (EapType) EAP_ERP_TYPE_REAUTH_START, plen,
+			    EAP_CODE_INITIATE, id);
+	if (msg == NULL)
+		return NULL;
+	wpabuf_put_u8(msg, 0); /* Reserved */
+	if (domain) {
+		/* Domain name TLV */
+		wpabuf_put_u8(msg, EAP_ERP_TLV_DOMAIN_NAME);
+		wpabuf_put_u8(msg, domain_len);
+		wpabuf_put_data(msg, domain, domain_len);
+	}
+
+	return msg;
+}
+
+
+static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src)
+{
+	if (src == NULL)
+		return -1;
+
+	wpabuf_free(*dst);
+	*dst = wpabuf_dup(src);
+	return *dst ? 0 : -1;
+}
+
+
+static int eap_copy_data(u8 **dst, size_t *dst_len,
+			 const u8 *src, size_t src_len)
+{
+	if (src == NULL)
+		return -1;
+
+	os_free(*dst);
+	*dst = os_malloc(src_len);
+	if (*dst) {
+		os_memcpy(*dst, src, src_len);
+		*dst_len = src_len;
+		return 0;
+	} else {
+		*dst_len = 0;
+		return -1;
+	}
+}
+
+#define EAP_COPY(dst, src) \
+	eap_copy_data((dst), (dst ## Len), (src), (src ## Len))
+
+
+/**
+ * eap_user_get - Fetch user information from the database
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * @identity: Identity (User-Name) of the user
+ * @identity_len: Length of identity in bytes
+ * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user
+ * Returns: 0 on success, or -1 on failure
+ *
+ * This function is used to fetch user information for EAP. The user will be
+ * selected based on the specified identity. sm->user and
+ * sm->user_eap_method_index are updated for the new user when a matching user
+ * is found. sm->user can be used to get user information (e.g., password).
+ */
+int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
+		 int phase2)
+{
+	struct eap_user *user;
+
+	if (sm == NULL || sm->eapol_cb == NULL ||
+	    sm->eapol_cb->get_eap_user == NULL)
+		return -1;
+
+	eap_user_free(sm->user);
+	sm->user = NULL;
+
+	user = os_zalloc(sizeof(*user));
+	if (user == NULL)
+	    return -1;
+
+	if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity,
+				       identity_len, phase2, user) != 0) {
+		eap_user_free(user);
+		return -1;
+	}
+
+	sm->user = user;
+	sm->user_eap_method_index = 0;
+
+	return 0;
+}
+
+
+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);
+	sm->num_rounds = 0;
+}
+
+
+SM_STATE(EAP, INITIALIZE)
+{
+	SM_ENTRY(EAP, INITIALIZE);
+
+	if (sm->eap_if.eapRestart && !sm->eap_server && sm->identity) {
+		/*
+		 * Need to allow internal Identity method to be used instead
+		 * of passthrough at the beginning of reauthentication.
+		 */
+		eap_server_clear_identity(sm);
+	}
+
+	sm->try_initiate_reauth = FALSE;
+	sm->currentId = -1;
+	sm->eap_if.eapSuccess = FALSE;
+	sm->eap_if.eapFail = FALSE;
+	sm->eap_if.eapTimeout = FALSE;
+	bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
+	sm->eap_if.eapKeyData = NULL;
+	sm->eap_if.eapKeyDataLen = 0;
+	os_free(sm->eap_if.eapSessionId);
+	sm->eap_if.eapSessionId = NULL;
+	sm->eap_if.eapSessionIdLen = 0;
+	sm->eap_if.eapKeyAvailable = FALSE;
+	sm->eap_if.eapRestart = FALSE;
+
+	/*
+	 * This is not defined in RFC 4137, but method state needs to be
+	 * reseted here so that it does not remain in success state when
+	 * re-authentication starts.
+	 */
+	if (sm->m && sm->eap_method_priv) {
+		sm->m->reset(sm, sm->eap_method_priv);
+		sm->eap_method_priv = NULL;
+	}
+	sm->m = NULL;
+	sm->user_eap_method_index = 0;
+
+	if (sm->backend_auth) {
+		sm->currentMethod = EAP_TYPE_NONE;
+		/* parse rxResp, respId, respMethod */
+		eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
+		if (sm->rxResp) {
+			sm->currentId = sm->respId;
+		}
+	}
+	sm->num_rounds = 0;
+	sm->method_pending = METHOD_PENDING_NONE;
+
+	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
+		MACSTR, MAC2STR(sm->peer_addr));
+}
+
+
+SM_STATE(EAP, PICK_UP_METHOD)
+{
+	SM_ENTRY(EAP, PICK_UP_METHOD);
+
+	if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) {
+		sm->currentMethod = sm->respMethod;
+		if (sm->m && sm->eap_method_priv) {
+			sm->m->reset(sm, sm->eap_method_priv);
+			sm->eap_method_priv = NULL;
+		}
+		sm->m = eap_server_get_eap_method(EAP_VENDOR_IETF,
+						  sm->currentMethod);
+		if (sm->m && sm->m->initPickUp) {
+			sm->eap_method_priv = sm->m->initPickUp(sm);
+			if (sm->eap_method_priv == NULL) {
+				wpa_printf(MSG_DEBUG, "EAP: Failed to "
+					   "initialize EAP method %d",
+					   sm->currentMethod);
+				sm->m = NULL;
+				sm->currentMethod = EAP_TYPE_NONE;
+			}
+		} else {
+			sm->m = NULL;
+			sm->currentMethod = EAP_TYPE_NONE;
+		}
+	}
+
+	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+		"method=%u", sm->currentMethod);
+}
+
+
+SM_STATE(EAP, IDLE)
+{
+	SM_ENTRY(EAP, IDLE);
+
+	sm->eap_if.retransWhile = eap_sm_calculateTimeout(
+		sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
+		sm->methodTimeout);
+}
+
+
+SM_STATE(EAP, RETRANSMIT)
+{
+	SM_ENTRY(EAP, RETRANSMIT);
+
+	sm->retransCount++;
+	if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
+		if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
+			sm->eap_if.eapReq = TRUE;
+	}
+}
+
+
+SM_STATE(EAP, RECEIVED)
+{
+	SM_ENTRY(EAP, RECEIVED);
+
+	/* parse rxResp, respId, respMethod */
+	eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
+	sm->num_rounds++;
+}
+
+
+SM_STATE(EAP, DISCARD)
+{
+	SM_ENTRY(EAP, DISCARD);
+	sm->eap_if.eapResp = FALSE;
+	sm->eap_if.eapNoReq = TRUE;
+}
+
+
+SM_STATE(EAP, SEND_REQUEST)
+{
+	SM_ENTRY(EAP, SEND_REQUEST);
+
+	sm->retransCount = 0;
+	if (sm->eap_if.eapReqData) {
+		if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
+		{
+			sm->eap_if.eapResp = FALSE;
+			sm->eap_if.eapReq = TRUE;
+		} else {
+			sm->eap_if.eapResp = FALSE;
+			sm->eap_if.eapReq = FALSE;
+		}
+	} else {
+		wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData");
+		sm->eap_if.eapResp = FALSE;
+		sm->eap_if.eapReq = FALSE;
+		sm->eap_if.eapNoReq = TRUE;
+	}
+}
+
+
+SM_STATE(EAP, INTEGRITY_CHECK)
+{
+	SM_ENTRY(EAP, INTEGRITY_CHECK);
+
+	if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) {
+		sm->ignore = TRUE;
+		return;
+	}
+
+	if (sm->m->check) {
+		sm->ignore = sm->m->check(sm, sm->eap_method_priv,
+					  sm->eap_if.eapRespData);
+	}
+}
+
+
+SM_STATE(EAP, METHOD_REQUEST)
+{
+	SM_ENTRY(EAP, METHOD_REQUEST);
+
+	if (sm->m == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP: method not initialized");
+		return;
+	}
+
+	sm->currentId = eap_sm_nextId(sm, sm->currentId);
+	wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d",
+		   sm->currentId);
+	sm->lastId = sm->currentId;
+	wpabuf_free(sm->eap_if.eapReqData);
+	sm->eap_if.eapReqData = sm->m->buildReq(sm, sm->eap_method_priv,
+						sm->currentId);
+	if (sm->m->getTimeout)
+		sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv);
+	else
+		sm->methodTimeout = 0;
+}
+
+
+static void eap_server_erp_init(struct eap_sm *sm)
+{
+#ifdef CONFIG_ERP
+	u8 *emsk = NULL;
+	size_t emsk_len = 0;
+	u8 EMSKname[EAP_EMSK_NAME_LEN];
+	u8 len[2];
+	const char *domain;
+	size_t domain_len, nai_buf_len;
+	struct eap_server_erp_key *erp = NULL;
+	int pos;
+
+	domain = eap_get_erp_domain(sm);
+	if (!domain)
+		return;
+
+	domain_len = os_strlen(domain);
+
+	nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + domain_len;
+	if (nai_buf_len > 253) {
+		/*
+		 * keyName-NAI has a maximum length of 253 octet to fit in
+		 * RADIUS attributes.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Too long realm for ERP keyName-NAI maximum length");
+		return;
+	}
+	nai_buf_len++; /* null termination */
+	erp = os_zalloc(sizeof(*erp) + nai_buf_len);
+	if (erp == NULL)
+		goto fail;
+	erp->recv_seq = (u32) -1;
+
+	emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
+	if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: No suitable EMSK available for ERP");
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
+
+	WPA_PUT_BE16(len, 8);
+	if (hmac_sha256_kdf(sm->eap_if.eapSessionId, sm->eap_if.eapSessionIdLen,
+			    "EMSK", len, sizeof(len),
+			    EMSKname, EAP_EMSK_NAME_LEN) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN);
+
+	pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len,
+			       EMSKname, EAP_EMSK_NAME_LEN);
+	erp->keyname_nai[pos] = '@';
+	os_memcpy(&erp->keyname_nai[pos + 1], domain, domain_len);
+
+	WPA_PUT_BE16(len, emsk_len);
+	if (hmac_sha256_kdf(emsk, emsk_len,
+			    "EAP Re-authentication Root Key@ietf.org",
+			    len, sizeof(len), erp->rRK, emsk_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP");
+		goto fail;
+	}
+	erp->rRK_len = emsk_len;
+	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len);
+
+	if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+			    "EAP Re-authentication Integrity Key@ietf.org",
+			    len, sizeof(len), erp->rIK, erp->rRK_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP");
+		goto fail;
+	}
+	erp->rIK_len = erp->rRK_len;
+	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len);
+
+	if (eap_erp_add_key(sm, erp) == 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s",
+			   erp->keyname_nai);
+		erp = NULL;
+	}
+
+fail:
+	bin_clear_free(emsk, emsk_len);
+	bin_clear_free(erp, sizeof(*erp));
+#endif /* CONFIG_ERP */
+}
+
+
+SM_STATE(EAP, METHOD_RESPONSE)
+{
+	SM_ENTRY(EAP, METHOD_RESPONSE);
+
+	if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
+		return;
+
+	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);
+		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,
+				&sm->eap_if.eapKeyDataLen);
+		} else {
+			sm->eap_if.eapKeyData = NULL;
+			sm->eap_if.eapKeyDataLen = 0;
+		}
+		os_free(sm->eap_if.eapSessionId);
+		sm->eap_if.eapSessionId = NULL;
+		if (sm->m->getSessionId) {
+			sm->eap_if.eapSessionId = sm->m->getSessionId(
+				sm, sm->eap_method_priv,
+				&sm->eap_if.eapSessionIdLen);
+			wpa_hexdump(MSG_DEBUG, "EAP: Session-Id",
+				    sm->eap_if.eapSessionId,
+				    sm->eap_if.eapSessionIdLen);
+		}
+		if (sm->erp && sm->m->get_emsk && sm->eap_if.eapSessionId)
+			eap_server_erp_init(sm);
+		sm->methodState = METHOD_END;
+	} else {
+		sm->methodState = METHOD_CONTINUE;
+	}
+}
+
+
+SM_STATE(EAP, PROPOSE_METHOD)
+{
+	int vendor;
+	EapType type;
+
+	SM_ENTRY(EAP, PROPOSE_METHOD);
+
+	sm->try_initiate_reauth = FALSE;
+try_another_method:
+	type = eap_sm_Policy_getNextMethod(sm, &vendor);
+	if (vendor == EAP_VENDOR_IETF)
+		sm->currentMethod = type;
+	else
+		sm->currentMethod = EAP_TYPE_EXPANDED;
+	if (sm->m && sm->eap_method_priv) {
+		sm->m->reset(sm, sm->eap_method_priv);
+		sm->eap_method_priv = NULL;
+	}
+	sm->m = eap_server_get_eap_method(vendor, type);
+	if (sm->m) {
+		sm->eap_method_priv = sm->m->init(sm);
+		if (sm->eap_method_priv == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP "
+				   "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;
+	else
+		sm->methodState = METHOD_PROPOSED;
+
+	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);
+}
+
+
+SM_STATE(EAP, NAK)
+{
+	const struct eap_hdr *nak;
+	size_t len = 0;
+	const u8 *pos;
+	const u8 *nak_list = NULL;
+
+	SM_ENTRY(EAP, NAK);
+
+	if (sm->eap_method_priv) {
+		sm->m->reset(sm, sm->eap_method_priv);
+		sm->eap_method_priv = NULL;
+	}
+	sm->m = NULL;
+
+	if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
+		return;
+
+	nak = wpabuf_head(sm->eap_if.eapRespData);
+	if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) {
+		len = be_to_host16(nak->length);
+		if (len > wpabuf_len(sm->eap_if.eapRespData))
+			len = wpabuf_len(sm->eap_if.eapRespData);
+		pos = (const u8 *) (nak + 1);
+		len -= sizeof(*nak);
+		if (*pos == EAP_TYPE_NAK) {
+			pos++;
+			len--;
+			nak_list = pos;
+		}
+	}
+	eap_sm_Policy_update(sm, nak_list, len);
+}
+
+
+SM_STATE(EAP, SELECT_ACTION)
+{
+	SM_ENTRY(EAP, SELECT_ACTION);
+
+	sm->decision = eap_sm_Policy_getDecision(sm);
+}
+
+
+SM_STATE(EAP, TIMEOUT_FAILURE)
+{
+	SM_ENTRY(EAP, TIMEOUT_FAILURE);
+
+	sm->eap_if.eapTimeout = TRUE;
+}
+
+
+SM_STATE(EAP, FAILURE)
+{
+	SM_ENTRY(EAP, FAILURE);
+
+	wpabuf_free(sm->eap_if.eapReqData);
+	sm->eap_if.eapReqData = eap_sm_buildFailure(sm, sm->currentId);
+	wpabuf_free(sm->lastReqData);
+	sm->lastReqData = NULL;
+	sm->eap_if.eapFail = TRUE;
+
+	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+		MACSTR, MAC2STR(sm->peer_addr));
+}
+
+
+SM_STATE(EAP, SUCCESS)
+{
+	SM_ENTRY(EAP, SUCCESS);
+
+	wpabuf_free(sm->eap_if.eapReqData);
+	sm->eap_if.eapReqData = eap_sm_buildSuccess(sm, sm->currentId);
+	wpabuf_free(sm->lastReqData);
+	sm->lastReqData = NULL;
+	if (sm->eap_if.eapKeyData)
+		sm->eap_if.eapKeyAvailable = TRUE;
+	sm->eap_if.eapSuccess = TRUE;
+
+	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+		MACSTR, MAC2STR(sm->peer_addr));
+}
+
+
+SM_STATE(EAP, INITIATE_REAUTH_START)
+{
+	SM_ENTRY(EAP, INITIATE_REAUTH_START);
+
+	sm->initiate_reauth_start_sent = TRUE;
+	sm->try_initiate_reauth = TRUE;
+	sm->currentId = eap_sm_nextId(sm, sm->currentId);
+	wpa_printf(MSG_DEBUG,
+		   "EAP: building EAP-Initiate-Re-auth-Start: Identifier %d",
+		   sm->currentId);
+	sm->lastId = sm->currentId;
+	wpabuf_free(sm->eap_if.eapReqData);
+	sm->eap_if.eapReqData = eap_sm_buildInitiateReauthStart(sm,
+								sm->currentId);
+	wpabuf_free(sm->lastReqData);
+	sm->lastReqData = NULL;
+}
+
+
+#ifdef CONFIG_ERP
+
+static void erp_send_finish_reauth(struct eap_sm *sm,
+				   struct eap_server_erp_key *erp, u8 id,
+				   u8 flags, u16 seq, const char *nai)
+{
+	size_t plen;
+	struct wpabuf *msg;
+	u8 hash[SHA256_MAC_LEN];
+	size_t hash_len;
+	u8 seed[4];
+
+	if (erp) {
+		switch (erp->cryptosuite) {
+		case EAP_ERP_CS_HMAC_SHA256_256:
+			hash_len = 32;
+			break;
+		case EAP_ERP_CS_HMAC_SHA256_128:
+			hash_len = 16;
+			break;
+		default:
+			return;
+		}
+	} else
+		hash_len = 0;
+
+	plen = 1 + 2 + 2 + os_strlen(nai);
+	if (hash_len)
+		plen += 1 + hash_len;
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
+			    plen, EAP_CODE_FINISH, id);
+	if (msg == NULL)
+		return;
+	wpabuf_put_u8(msg, flags);
+	wpabuf_put_be16(msg, seq);
+
+	wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI);
+	wpabuf_put_u8(msg, os_strlen(nai));
+	wpabuf_put_str(msg, nai);
+
+	if (erp) {
+		wpabuf_put_u8(msg, erp->cryptosuite);
+		if (hmac_sha256(erp->rIK, erp->rIK_len,
+				wpabuf_head(msg), wpabuf_len(msg), hash) < 0) {
+			wpabuf_free(msg);
+			return;
+		}
+		wpabuf_put_data(msg, hash, hash_len);
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP: Send EAP-Finish/Re-auth (%s)",
+		   flags & 0x80 ? "failure" : "success");
+
+	sm->lastId = sm->currentId;
+	sm->currentId = id;
+	wpabuf_free(sm->eap_if.eapReqData);
+	sm->eap_if.eapReqData = msg;
+	wpabuf_free(sm->lastReqData);
+	sm->lastReqData = NULL;
+
+	if ((flags & 0x80) || !erp) {
+		sm->eap_if.eapFail = TRUE;
+		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+			MACSTR, MAC2STR(sm->peer_addr));
+		return;
+	}
+
+	bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
+	sm->eap_if.eapKeyDataLen = 0;
+	sm->eap_if.eapKeyData = os_malloc(erp->rRK_len);
+	if (!sm->eap_if.eapKeyData)
+		return;
+
+	WPA_PUT_BE16(seed, seq);
+	WPA_PUT_BE16(&seed[2], erp->rRK_len);
+	if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+			    "Re-authentication Master Session Key@ietf.org",
+			    seed, sizeof(seed),
+			    sm->eap_if.eapKeyData, erp->rRK_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP");
+		bin_clear_free(sm->eap_if.eapKeyData, erp->rRK_len);
+		sm->eap_if.eapKeyData = NULL;
+		return;
+	}
+	sm->eap_if.eapKeyDataLen = erp->rRK_len;
+	sm->eap_if.eapKeyAvailable = TRUE;
+	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK",
+			sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
+	sm->eap_if.eapSuccess = TRUE;
+
+	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+		MACSTR, MAC2STR(sm->peer_addr));
+}
+
+
+SM_STATE(EAP, INITIATE_RECEIVED)
+{
+	const u8 *pos, *end, *start, *tlvs, *hdr;
+	const struct eap_hdr *ehdr;
+	size_t len;
+	u8 flags;
+	u16 seq;
+	char nai[254];
+	struct eap_server_erp_key *erp;
+	int max_len;
+	u8 hash[SHA256_MAC_LEN];
+	size_t hash_len;
+	struct erp_tlvs parse;
+	u8 resp_flags = 0x80; /* default to failure; cleared on success */
+
+	SM_ENTRY(EAP, INITIATE_RECEIVED);
+
+	sm->rxInitiate = FALSE;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
+			       sm->eap_if.eapRespData, &len);
+	if (pos == NULL) {
+		wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame");
+		goto fail;
+	}
+	hdr = wpabuf_head(sm->eap_if.eapRespData);
+	ehdr = wpabuf_head(sm->eap_if.eapRespData);
+
+	wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth", pos, len);
+	if (len < 4) {
+		wpa_printf(MSG_INFO, "EAP: Too short EAP-Initiate/Re-auth");
+		goto fail;
+	}
+	end = pos + len;
+
+	flags = *pos++;
+	seq = WPA_GET_BE16(pos);
+	pos += 2;
+	wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq);
+	tlvs = pos;
+
+	/*
+	 * Parse TVs/TLVs. Since we do not yet know the length of the
+	 * Authentication Tag, stop parsing if an unknown TV/TLV is seen and
+	 * just try to find the keyName-NAI first so that we can check the
+	 * Authentication Tag.
+	 */
+	if (erp_parse_tlvs(tlvs, end, &parse, 1) < 0)
+		goto fail;
+
+	if (!parse.keyname) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: No keyName-NAI in EAP-Initiate/Re-auth Packet");
+		goto fail;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth - keyName-NAI",
+			  parse.keyname, parse.keyname_len);
+	if (parse.keyname_len > 253) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Too long keyName-NAI in EAP-Initiate/Re-auth");
+		goto fail;
+	}
+	os_memcpy(nai, parse.keyname, parse.keyname_len);
+	nai[parse.keyname_len] = '\0';
+
+	if (!sm->eap_server) {
+		/*
+		 * In passthrough case, EAP-Initiate/Re-auth replaces
+		 * EAP Identity exchange. Use keyName-NAI as the user identity
+		 * and forward EAP-Initiate/Re-auth to the backend
+		 * authentication server.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Use keyName-NAI as user identity for backend authentication");
+		eap_server_clear_identity(sm);
+		sm->identity = (u8 *) dup_binstr(parse.keyname,
+						 parse.keyname_len);
+		if (!sm->identity)
+			goto fail;
+		sm->identity_len = parse.keyname_len;
+		return;
+	}
+
+	erp = eap_erp_get_key(sm, nai);
+	if (!erp) {
+		wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s",
+			   nai);
+		goto report_error;
+	}
+
+	if (erp->recv_seq != (u32) -1 && erp->recv_seq >= seq) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: SEQ=%u replayed (already received SEQ=%u)",
+			   seq, erp->recv_seq);
+		goto fail;
+	}
+
+	/* Is there enough room for Cryptosuite and Authentication Tag? */
+	start = parse.keyname + parse.keyname_len;
+	max_len = end - start;
+	if (max_len <
+	    1 + (erp->cryptosuite == EAP_ERP_CS_HMAC_SHA256_256 ? 32 : 16)) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Not enough room for Authentication Tag");
+		goto fail;
+	}
+
+	switch (erp->cryptosuite) {
+	case EAP_ERP_CS_HMAC_SHA256_256:
+		if (end[-33] != erp->cryptosuite) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP: Different Cryptosuite used");
+			goto fail;
+		}
+		hash_len = 32;
+		break;
+	case EAP_ERP_CS_HMAC_SHA256_128:
+		if (end[-17] != erp->cryptosuite) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP: Different Cryptosuite used");
+			goto fail;
+		}
+		hash_len = 16;
+		break;
+	default:
+		hash_len = 0;
+		break;
+	}
+
+	if (hash_len) {
+		if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
+				end - hdr - hash_len, hash) < 0)
+			goto fail;
+		if (os_memcmp(end - hash_len, hash, hash_len) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP: Authentication Tag mismatch");
+			goto fail;
+		}
+	}
+
+	/* Check if any supported CS results in matching tag */
+	if (!hash_len && max_len >= 1 + 32 &&
+	    end[-33] == EAP_ERP_CS_HMAC_SHA256_256) {
+		if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
+				end - hdr - 32, hash) < 0)
+			goto fail;
+		if (os_memcmp(end - 32, hash, 32) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP: Authentication Tag match using HMAC-SHA256-256");
+			hash_len = 32;
+			erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_256;
+		}
+	}
+
+	if (!hash_len && end[-17] == EAP_ERP_CS_HMAC_SHA256_128) {
+		if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
+				end - hdr - 16, hash) < 0)
+			goto fail;
+		if (os_memcmp(end - 16, hash, 16) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP: Authentication Tag match using HMAC-SHA256-128");
+			hash_len = 16;
+			erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_128;
+		}
+	}
+
+	if (!hash_len) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: No supported cryptosuite matched Authentication Tag");
+		goto fail;
+	}
+	end -= 1 + hash_len;
+
+	/*
+	 * Parse TVs/TLVs again now that we know the exact part of the buffer
+	 * that contains them.
+	 */
+	wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth TVs/TLVs",
+		    tlvs, end - tlvs);
+	if (erp_parse_tlvs(tlvs, end, &parse, 0) < 0)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "EAP: ERP key %s SEQ updated to %u",
+		   erp->keyname_nai, seq);
+	erp->recv_seq = seq;
+	resp_flags &= ~0x80; /* R=0 - success */
+
+report_error:
+	erp_send_finish_reauth(sm, erp, ehdr->identifier, resp_flags, seq, nai);
+	return;
+
+fail:
+	sm->ignore = TRUE;
+}
+
+#endif /* CONFIG_ERP */
+
+
+SM_STATE(EAP, INITIALIZE_PASSTHROUGH)
+{
+	SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH);
+
+	wpabuf_free(sm->eap_if.aaaEapRespData);
+	sm->eap_if.aaaEapRespData = NULL;
+	sm->try_initiate_reauth = FALSE;
+}
+
+
+SM_STATE(EAP, IDLE2)
+{
+	SM_ENTRY(EAP, IDLE2);
+
+	sm->eap_if.retransWhile = eap_sm_calculateTimeout(
+		sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
+		sm->methodTimeout);
+}
+
+
+SM_STATE(EAP, RETRANSMIT2)
+{
+	SM_ENTRY(EAP, RETRANSMIT2);
+
+	sm->retransCount++;
+	if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
+		if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
+			sm->eap_if.eapReq = TRUE;
+	}
+}
+
+
+SM_STATE(EAP, RECEIVED2)
+{
+	SM_ENTRY(EAP, RECEIVED2);
+
+	/* parse rxResp, respId, respMethod */
+	eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
+}
+
+
+SM_STATE(EAP, DISCARD2)
+{
+	SM_ENTRY(EAP, DISCARD2);
+	sm->eap_if.eapResp = FALSE;
+	sm->eap_if.eapNoReq = TRUE;
+}
+
+
+SM_STATE(EAP, SEND_REQUEST2)
+{
+	SM_ENTRY(EAP, SEND_REQUEST2);
+
+	sm->retransCount = 0;
+	if (sm->eap_if.eapReqData) {
+		if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
+		{
+			sm->eap_if.eapResp = FALSE;
+			sm->eap_if.eapReq = TRUE;
+		} else {
+			sm->eap_if.eapResp = FALSE;
+			sm->eap_if.eapReq = FALSE;
+		}
+	} else {
+		wpa_printf(MSG_INFO, "EAP: SEND_REQUEST2 - no eapReqData");
+		sm->eap_if.eapResp = FALSE;
+		sm->eap_if.eapReq = FALSE;
+		sm->eap_if.eapNoReq = TRUE;
+	}
+}
+
+
+SM_STATE(EAP, AAA_REQUEST)
+{
+	SM_ENTRY(EAP, AAA_REQUEST);
+
+	if (sm->eap_if.eapRespData == NULL) {
+		wpa_printf(MSG_INFO, "EAP: AAA_REQUEST - no eapRespData");
+		return;
+	}
+
+	/*
+	 * if (respMethod == IDENTITY)
+	 *	aaaIdentity = eapRespData
+	 * This is already taken care of by the EAP-Identity method which
+	 * stores the identity into sm->identity.
+	 */
+
+	eap_copy_buf(&sm->eap_if.aaaEapRespData, sm->eap_if.eapRespData);
+}
+
+
+SM_STATE(EAP, AAA_RESPONSE)
+{
+	SM_ENTRY(EAP, AAA_RESPONSE);
+
+	eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
+	sm->currentId = eap_sm_getId(sm->eap_if.eapReqData);
+	sm->methodTimeout = sm->eap_if.aaaMethodTimeout;
+}
+
+
+SM_STATE(EAP, AAA_IDLE)
+{
+	SM_ENTRY(EAP, AAA_IDLE);
+
+	sm->eap_if.aaaFail = FALSE;
+	sm->eap_if.aaaSuccess = FALSE;
+	sm->eap_if.aaaEapReq = FALSE;
+	sm->eap_if.aaaEapNoReq = FALSE;
+	sm->eap_if.aaaEapResp = TRUE;
+}
+
+
+SM_STATE(EAP, TIMEOUT_FAILURE2)
+{
+	SM_ENTRY(EAP, TIMEOUT_FAILURE2);
+
+	sm->eap_if.eapTimeout = TRUE;
+}
+
+
+SM_STATE(EAP, FAILURE2)
+{
+	SM_ENTRY(EAP, FAILURE2);
+
+	eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
+	sm->eap_if.eapFail = TRUE;
+}
+
+
+SM_STATE(EAP, SUCCESS2)
+{
+	SM_ENTRY(EAP, SUCCESS2);
+
+	eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
+
+	sm->eap_if.eapKeyAvailable = sm->eap_if.aaaEapKeyAvailable;
+	if (sm->eap_if.aaaEapKeyAvailable) {
+		EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData);
+	} else {
+		bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
+		sm->eap_if.eapKeyData = NULL;
+		sm->eap_if.eapKeyDataLen = 0;
+	}
+
+	sm->eap_if.eapSuccess = TRUE;
+
+	/*
+	 * Start reauthentication with identity request even though we know the
+	 * previously used identity. This is needed to get reauthentication
+	 * started properly.
+	 */
+	sm->start_reauth = TRUE;
+}
+
+
+SM_STEP(EAP)
+{
+	if (sm->eap_if.eapRestart && sm->eap_if.portEnabled)
+		SM_ENTER_GLOBAL(EAP, INITIALIZE);
+	else if (!sm->eap_if.portEnabled)
+		SM_ENTER_GLOBAL(EAP, DISABLED);
+	else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
+		if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
+			wpa_printf(MSG_DEBUG, "EAP: more than %d "
+				   "authentication rounds - abort",
+				   EAP_MAX_AUTH_ROUNDS);
+			sm->num_rounds++;
+			SM_ENTER_GLOBAL(EAP, FAILURE);
+		}
+	} else switch (sm->EAP_state) {
+	case EAP_INITIALIZE:
+		if (sm->backend_auth) {
+			if (!sm->rxResp)
+				SM_ENTER(EAP, SELECT_ACTION);
+			else if (sm->rxResp &&
+				 (sm->respMethod == EAP_TYPE_NAK ||
+				  (sm->respMethod == EAP_TYPE_EXPANDED &&
+				   sm->respVendor == EAP_VENDOR_IETF &&
+				   sm->respVendorMethod == EAP_TYPE_NAK)))
+				SM_ENTER(EAP, NAK);
+			else
+				SM_ENTER(EAP, PICK_UP_METHOD);
+		} else {
+			SM_ENTER(EAP, SELECT_ACTION);
+		}
+		break;
+	case EAP_PICK_UP_METHOD:
+		if (sm->currentMethod == EAP_TYPE_NONE) {
+			SM_ENTER(EAP, SELECT_ACTION);
+		} else {
+			SM_ENTER(EAP, METHOD_RESPONSE);
+		}
+		break;
+	case EAP_DISABLED:
+		if (sm->eap_if.portEnabled)
+			SM_ENTER(EAP, INITIALIZE);
+		break;
+	case EAP_IDLE:
+		if (sm->eap_if.retransWhile == 0) {
+			if (sm->try_initiate_reauth) {
+				sm->try_initiate_reauth = FALSE;
+				SM_ENTER(EAP, SELECT_ACTION);
+			} else {
+				SM_ENTER(EAP, RETRANSMIT);
+			}
+		} else if (sm->eap_if.eapResp)
+			SM_ENTER(EAP, RECEIVED);
+		break;
+	case EAP_RETRANSMIT:
+		if (sm->retransCount > sm->MaxRetrans)
+			SM_ENTER(EAP, TIMEOUT_FAILURE);
+		else
+			SM_ENTER(EAP, IDLE);
+		break;
+	case EAP_RECEIVED:
+		if (sm->rxResp && (sm->respId == sm->currentId) &&
+		    (sm->respMethod == EAP_TYPE_NAK ||
+		     (sm->respMethod == EAP_TYPE_EXPANDED &&
+		      sm->respVendor == EAP_VENDOR_IETF &&
+		      sm->respVendorMethod == EAP_TYPE_NAK))
+		    && (sm->methodState == METHOD_PROPOSED))
+			SM_ENTER(EAP, NAK);
+		else if (sm->rxResp && (sm->respId == sm->currentId) &&
+			 ((sm->respMethod == sm->currentMethod) ||
+			  (sm->respMethod == EAP_TYPE_EXPANDED &&
+			   sm->respVendor == EAP_VENDOR_IETF &&
+			   sm->respVendorMethod == sm->currentMethod)))
+			SM_ENTER(EAP, INTEGRITY_CHECK);
+#ifdef CONFIG_ERP
+		else if (sm->rxInitiate)
+			SM_ENTER(EAP, INITIATE_RECEIVED);
+#endif /* CONFIG_ERP */
+		else {
+			wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: "
+				   "rxResp=%d respId=%d currentId=%d "
+				   "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;
+	case EAP_DISCARD:
+		SM_ENTER(EAP, IDLE);
+		break;
+	case EAP_SEND_REQUEST:
+		SM_ENTER(EAP, IDLE);
+		break;
+	case EAP_INTEGRITY_CHECK:
+		if (sm->ignore)
+			SM_ENTER(EAP, DISCARD);
+		else
+			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);
+		if (sm->eap_if.eapNoReq && !sm->eap_if.eapReq) {
+			/*
+			 * This transition is not mentioned in RFC 4137, but it
+			 * is needed to handle cleanly a case where EAP method
+			 * buildReq fails.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "EAP: Method did not return a request");
+			SM_ENTER(EAP, FAILURE);
+			break;
+		}
+		break;
+	case EAP_METHOD_RESPONSE:
+		/*
+		 * Note: Mechanism to allow EAP methods to wait while going
+		 * through pending processing is an extension to RFC 4137
+		 * which only defines the transits to SELECT_ACTION and
+		 * METHOD_REQUEST from this METHOD_RESPONSE state.
+		 */
+		if (sm->methodState == METHOD_END)
+			SM_ENTER(EAP, SELECT_ACTION);
+		else if (sm->method_pending == METHOD_PENDING_WAIT) {
+			wpa_printf(MSG_DEBUG, "EAP: Method has pending "
+				   "processing - wait before proceeding to "
+				   "METHOD_REQUEST state");
+		} else if (sm->method_pending == METHOD_PENDING_CONT) {
+			wpa_printf(MSG_DEBUG, "EAP: Method has completed "
+				   "pending processing - reprocess pending "
+				   "EAP message");
+			sm->method_pending = METHOD_PENDING_NONE;
+			SM_ENTER(EAP, METHOD_RESPONSE);
+		} else
+			SM_ENTER(EAP, METHOD_REQUEST);
+		break;
+	case EAP_PROPOSE_METHOD:
+		/*
+		 * Note: Mechanism to allow EAP methods to wait while going
+		 * through pending processing is an extension to RFC 4137
+		 * which only defines the transit to METHOD_REQUEST from this
+		 * PROPOSE_METHOD state.
+		 */
+		if (sm->method_pending == METHOD_PENDING_WAIT) {
+			wpa_printf(MSG_DEBUG, "EAP: Method has pending "
+				   "processing - wait before proceeding to "
+				   "METHOD_REQUEST state");
+			if (sm->user_eap_method_index > 0)
+				sm->user_eap_method_index--;
+		} else if (sm->method_pending == METHOD_PENDING_CONT) {
+			wpa_printf(MSG_DEBUG, "EAP: Method has completed "
+				   "pending processing - reprocess pending "
+				   "EAP message");
+			sm->method_pending = METHOD_PENDING_NONE;
+			SM_ENTER(EAP, PROPOSE_METHOD);
+		} else
+			SM_ENTER(EAP, METHOD_REQUEST);
+		break;
+	case EAP_NAK:
+		SM_ENTER(EAP, SELECT_ACTION);
+		break;
+	case EAP_SELECT_ACTION:
+		if (sm->decision == DECISION_FAILURE)
+			SM_ENTER(EAP, FAILURE);
+		else if (sm->decision == DECISION_SUCCESS)
+			SM_ENTER(EAP, SUCCESS);
+		else if (sm->decision == DECISION_PASSTHROUGH)
+			SM_ENTER(EAP, INITIALIZE_PASSTHROUGH);
+		else if (sm->decision == DECISION_INITIATE_REAUTH_START)
+			SM_ENTER(EAP, INITIATE_REAUTH_START);
+#ifdef CONFIG_ERP
+		else if (sm->eap_server && sm->erp && sm->rxInitiate)
+			SM_ENTER(EAP, INITIATE_RECEIVED);
+#endif /* CONFIG_ERP */
+		else
+			SM_ENTER(EAP, PROPOSE_METHOD);
+		break;
+	case EAP_INITIATE_REAUTH_START:
+		SM_ENTER(EAP, SEND_REQUEST);
+		break;
+	case EAP_INITIATE_RECEIVED:
+		if (!sm->eap_server)
+			SM_ENTER(EAP, SELECT_ACTION);
+		break;
+	case EAP_TIMEOUT_FAILURE:
+		break;
+	case EAP_FAILURE:
+		break;
+	case EAP_SUCCESS:
+		break;
+
+	case EAP_INITIALIZE_PASSTHROUGH:
+		if (sm->currentId == -1)
+			SM_ENTER(EAP, AAA_IDLE);
+		else
+			SM_ENTER(EAP, AAA_REQUEST);
+		break;
+	case EAP_IDLE2:
+		if (sm->eap_if.eapResp)
+			SM_ENTER(EAP, RECEIVED2);
+		else if (sm->eap_if.retransWhile == 0)
+			SM_ENTER(EAP, RETRANSMIT2);
+		break;
+	case EAP_RETRANSMIT2:
+		if (sm->retransCount > sm->MaxRetrans)
+			SM_ENTER(EAP, TIMEOUT_FAILURE2);
+		else
+			SM_ENTER(EAP, IDLE2);
+		break;
+	case EAP_RECEIVED2:
+		if (sm->rxResp && (sm->respId == sm->currentId))
+			SM_ENTER(EAP, AAA_REQUEST);
+		else
+			SM_ENTER(EAP, DISCARD2);
+		break;
+	case EAP_DISCARD2:
+		SM_ENTER(EAP, IDLE2);
+		break;
+	case EAP_SEND_REQUEST2:
+		SM_ENTER(EAP, IDLE2);
+		break;
+	case EAP_AAA_REQUEST:
+		SM_ENTER(EAP, AAA_IDLE);
+		break;
+	case EAP_AAA_RESPONSE:
+		SM_ENTER(EAP, SEND_REQUEST2);
+		break;
+	case EAP_AAA_IDLE:
+		if (sm->eap_if.aaaFail)
+			SM_ENTER(EAP, FAILURE2);
+		else if (sm->eap_if.aaaSuccess)
+			SM_ENTER(EAP, SUCCESS2);
+		else if (sm->eap_if.aaaEapReq)
+			SM_ENTER(EAP, AAA_RESPONSE);
+		else if (sm->eap_if.aaaTimeout)
+			SM_ENTER(EAP, TIMEOUT_FAILURE2);
+		break;
+	case EAP_TIMEOUT_FAILURE2:
+		break;
+	case EAP_FAILURE2:
+		break;
+	case EAP_SUCCESS2:
+		break;
+	}
+}
+
+
+static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
+				   int eapSRTT, int eapRTTVAR,
+				   int methodTimeout)
+{
+	int rto, i;
+
+	if (sm->try_initiate_reauth) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: retransmit timeout 1 second for EAP-Initiate-Re-auth-Start");
+		return 1;
+	}
+
+	if (methodTimeout) {
+		/*
+		 * EAP method (either internal or through AAA server, provided
+		 * timeout hint. Use that as-is as a timeout for retransmitting
+		 * the EAP request if no response is received.
+		 */
+		wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
+			   "(from EAP method hint)", methodTimeout);
+		return methodTimeout;
+	}
+
+	/*
+	 * RFC 3748 recommends algorithms described in RFC 2988 for estimation
+	 * of the retransmission timeout. This should be implemented once
+	 * round-trip time measurements are available. For nowm a simple
+	 * backoff mechanism is used instead if there are no EAP method
+	 * specific hints.
+	 *
+	 * SRTT = smoothed round-trip time
+	 * RTTVAR = round-trip time variation
+	 * RTO = retransmission timeout
+	 */
+
+	/*
+	 * RFC 2988, 2.1: before RTT measurement, set RTO to 3 seconds for
+	 * initial retransmission and then double the RTO to provide back off
+	 * per 5.5. Limit the maximum RTO to 20 seconds per RFC 3748, 4.3
+	 * modified RTOmax.
+	 */
+	rto = 3;
+	for (i = 0; i < retransCount; i++) {
+		rto *= 2;
+		if (rto >= 20) {
+			rto = 20;
+			break;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
+		   "(from dynamic back off; retransCount=%d)",
+		   rto, retransCount);
+
+	return rto;
+}
+
+
+static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
+{
+	const struct eap_hdr *hdr;
+	size_t plen;
+
+	/* parse rxResp, respId, respMethod */
+	sm->rxResp = FALSE;
+	sm->rxInitiate = FALSE;
+	sm->respId = -1;
+	sm->respMethod = EAP_TYPE_NONE;
+	sm->respVendor = EAP_VENDOR_IETF;
+	sm->respVendorMethod = EAP_TYPE_NONE;
+
+	if (resp == NULL || wpabuf_len(resp) < sizeof(*hdr)) {
+		wpa_printf(MSG_DEBUG, "EAP: parseEapResp: invalid resp=%p "
+			   "len=%lu", resp,
+			   resp ? (unsigned long) wpabuf_len(resp) : 0);
+		return;
+	}
+
+	hdr = wpabuf_head(resp);
+	plen = be_to_host16(hdr->length);
+	if (plen > wpabuf_len(resp)) {
+		wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
+			   "(len=%lu plen=%lu)",
+			   (unsigned long) wpabuf_len(resp),
+			   (unsigned long) plen);
+		return;
+	}
+
+	sm->respId = hdr->identifier;
+
+	if (hdr->code == EAP_CODE_RESPONSE)
+		sm->rxResp = TRUE;
+	else if (hdr->code == EAP_CODE_INITIATE)
+		sm->rxInitiate = TRUE;
+
+	if (plen > sizeof(*hdr)) {
+		u8 *pos = (u8 *) (hdr + 1);
+		sm->respMethod = *pos++;
+		if (sm->respMethod == EAP_TYPE_EXPANDED) {
+			if (plen < sizeof(*hdr) + 8) {
+				wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
+					   "expanded EAP-Packet (plen=%lu)",
+					   (unsigned long) plen);
+				return;
+			}
+			sm->respVendor = WPA_GET_BE24(pos);
+			pos += 3;
+			sm->respVendorMethod = WPA_GET_BE32(pos);
+		}
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP: parseEapResp: rxResp=%d rxInitiate=%d respId=%d respMethod=%u respVendor=%u respVendorMethod=%u",
+		   sm->rxResp, sm->rxInitiate, sm->respId, sm->respMethod,
+		   sm->respVendor, sm->respVendorMethod);
+}
+
+
+static int eap_sm_getId(const struct wpabuf *data)
+{
+	const struct eap_hdr *hdr;
+
+	if (data == NULL || wpabuf_len(data) < sizeof(*hdr))
+		return -1;
+
+	hdr = wpabuf_head(data);
+	wpa_printf(MSG_DEBUG, "EAP: getId: id=%d", hdr->identifier);
+	return hdr->identifier;
+}
+
+
+static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id)
+{
+	struct wpabuf *msg;
+	struct eap_hdr *resp;
+	wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id);
+
+	msg = wpabuf_alloc(sizeof(*resp));
+	if (msg == NULL)
+		return NULL;
+	resp = wpabuf_put(msg, sizeof(*resp));
+	resp->code = EAP_CODE_SUCCESS;
+	resp->identifier = id;
+	resp->length = host_to_be16(sizeof(*resp));
+
+	return msg;
+}
+
+
+static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id)
+{
+	struct wpabuf *msg;
+	struct eap_hdr *resp;
+	wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id);
+
+	msg = wpabuf_alloc(sizeof(*resp));
+	if (msg == NULL)
+		return NULL;
+	resp = wpabuf_put(msg, sizeof(*resp));
+	resp->code = EAP_CODE_FAILURE;
+	resp->identifier = id;
+	resp->length = host_to_be16(sizeof(*resp));
+
+	return msg;
+}
+
+
+static int eap_sm_nextId(struct eap_sm *sm, int id)
+{
+	if (id < 0) {
+		/* RFC 3748 Ch 4.1: recommended to initialize Identifier with a
+		 * random number */
+		id = rand() & 0xff;
+		if (id != sm->lastId)
+			return id;
+	}
+	return (id + 1) & 0xff;
+}
+
+
+/**
+ * eap_sm_process_nak - Process EAP-Response/Nak
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * @nak_list: Nak list (allowed methods) from the supplicant
+ * @len: Length of nak_list in bytes
+ *
+ * This function is called when EAP-Response/Nak is received from the
+ * supplicant. This can happen for both phase 1 and phase 2 authentications.
+ */
+void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len)
+{
+	int i;
+	size_t j;
+
+	if (sm->user == NULL)
+		return;
+
+	wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method "
+		   "index %d)", sm->user_eap_method_index);
+
+	wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods",
+		    (u8 *) sm->user->methods,
+		    EAP_MAX_METHODS * sizeof(sm->user->methods[0]));
+	wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer",
+		    nak_list, len);
+
+	i = sm->user_eap_method_index;
+	while (i < EAP_MAX_METHODS &&
+	       (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+		sm->user->methods[i].method != EAP_TYPE_NONE)) {
+		if (sm->user->methods[i].vendor != EAP_VENDOR_IETF)
+			goto not_found;
+		for (j = 0; j < len; j++) {
+			if (nak_list[j] == sm->user->methods[i].method) {
+				break;
+			}
+		}
+
+		if (j < len) {
+			/* found */
+			i++;
+			continue;
+		}
+
+	not_found:
+		/* not found - remove from the list */
+		if (i + 1 < EAP_MAX_METHODS) {
+			os_memmove(&sm->user->methods[i],
+				   &sm->user->methods[i + 1],
+				   (EAP_MAX_METHODS - i - 1) *
+				   sizeof(sm->user->methods[0]));
+		}
+		sm->user->methods[EAP_MAX_METHODS - 1].vendor =
+			EAP_VENDOR_IETF;
+		sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods",
+		    (u8 *) sm->user->methods, EAP_MAX_METHODS *
+		    sizeof(sm->user->methods[0]));
+}
+
+
+static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
+				 size_t len)
+{
+	if (nak_list == NULL || sm == NULL || sm->user == NULL)
+		return;
+
+	if (sm->user->phase2) {
+		wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user"
+			   " info was selected - reject");
+		sm->decision = DECISION_FAILURE;
+		return;
+	}
+
+	eap_sm_process_nak(sm, nak_list, len);
+}
+
+
+static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor)
+{
+	EapType next;
+	int idx = sm->user_eap_method_index;
+
+	/* In theory, there should be no problems with starting
+	 * re-authentication with something else than EAP-Request/Identity and
+	 * this does indeed work with wpa_supplicant. However, at least Funk
+	 * Supplicant seemed to ignore re-auth if it skipped
+	 * EAP-Request/Identity.
+	 * Re-auth sets currentId == -1, so that can be used here to select
+	 * whether Identity needs to be requested again. */
+	if (sm->identity == NULL || sm->currentId == -1) {
+		*vendor = EAP_VENDOR_IETF;
+		next = EAP_TYPE_IDENTITY;
+		sm->update_user = TRUE;
+	} else if (sm->user && idx < EAP_MAX_METHODS &&
+		   (sm->user->methods[idx].vendor != EAP_VENDOR_IETF ||
+		    sm->user->methods[idx].method != EAP_TYPE_NONE)) {
+		*vendor = sm->user->methods[idx].vendor;
+		next = sm->user->methods[idx].method;
+		sm->user_eap_method_index++;
+	} else {
+		*vendor = EAP_VENDOR_IETF;
+		next = EAP_TYPE_NONE;
+	}
+	wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d",
+		   *vendor, next);
+	return next;
+}
+
+
+static int eap_sm_Policy_getDecision(struct eap_sm *sm)
+{
+	if (!sm->eap_server && sm->identity && !sm->start_reauth) {
+		wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH");
+		return DECISION_PASSTHROUGH;
+	}
+
+	if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY &&
+	    sm->m->isSuccess(sm, sm->eap_method_priv)) {
+		wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> "
+			   "SUCCESS");
+		sm->update_user = TRUE;
+		return DECISION_SUCCESS;
+	}
+
+	if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) &&
+	    !sm->m->isSuccess(sm, sm->eap_method_priv)) {
+		wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> "
+			   "FAILURE");
+		sm->update_user = TRUE;
+		return DECISION_FAILURE;
+	}
+
+	if ((sm->user == NULL || sm->update_user) && sm->identity &&
+	    !sm->start_reauth) {
+		/*
+		 * Allow Identity method to be started once to allow identity
+		 * selection hint to be sent from the authentication server,
+		 * but prevent a loop of Identity requests by only allowing
+		 * this to happen once.
+		 */
+		int id_req = 0;
+		if (sm->user && sm->currentMethod == EAP_TYPE_IDENTITY &&
+		    sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
+		    sm->user->methods[0].method == EAP_TYPE_IDENTITY)
+			id_req = 1;
+		if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) {
+			wpa_printf(MSG_DEBUG, "EAP: getDecision: user not "
+				   "found from database -> FAILURE");
+			return DECISION_FAILURE;
+		}
+		if (id_req && sm->user &&
+		    sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
+		    sm->user->methods[0].method == EAP_TYPE_IDENTITY) {
+			wpa_printf(MSG_DEBUG, "EAP: getDecision: stop "
+				   "identity request loop -> FAILURE");
+			sm->update_user = TRUE;
+			return DECISION_FAILURE;
+		}
+		sm->update_user = FALSE;
+	}
+	sm->start_reauth = FALSE;
+
+	if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+	    (sm->user->methods[sm->user_eap_method_index].vendor !=
+	     EAP_VENDOR_IETF ||
+	     sm->user->methods[sm->user_eap_method_index].method !=
+	     EAP_TYPE_NONE)) {
+		wpa_printf(MSG_DEBUG, "EAP: getDecision: another method "
+			   "available -> CONTINUE");
+		return DECISION_CONTINUE;
+	}
+
+	if (!sm->identity && eap_get_erp_send_reauth_start(sm) &&
+	    !sm->initiate_reauth_start_sent) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: getDecision: send EAP-Initiate/Re-auth-Start");
+		return DECISION_INITIATE_REAUTH_START;
+	}
+
+	if (sm->identity == NULL || sm->currentId == -1) {
+		wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known "
+			   "yet -> CONTINUE");
+		return DECISION_CONTINUE;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> "
+		   "FAILURE");
+	return DECISION_FAILURE;
+}
+
+
+static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method)
+{
+	return method == EAP_TYPE_IDENTITY ? TRUE : FALSE;
+}
+
+
+/**
+ * eap_server_sm_step - Step EAP server state machine
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * Returns: 1 if EAP state was changed or 0 if not
+ *
+ * This function advances EAP state machine to a new state to match with the
+ * current variables. This should be called whenever variables used by the EAP
+ * state machine have changed.
+ */
+int eap_server_sm_step(struct eap_sm *sm)
+{
+	int res = 0;
+	do {
+		sm->changed = FALSE;
+		SM_STEP_RUN(EAP);
+		if (sm->changed)
+			res = 1;
+	} while (sm->changed);
+	return res;
+}
+
+
+static void eap_user_free(struct eap_user *user)
+{
+	if (user == NULL)
+		return;
+	bin_clear_free(user->password, user->password_len);
+	user->password = NULL;
+	os_free(user);
+}
+
+
+/**
+ * eap_server_sm_init - Allocate and initialize EAP server state machine
+ * @eapol_ctx: Context data to be used with eapol_cb calls
+ * @eapol_cb: Pointer to EAPOL callback functions
+ * @conf: EAP configuration
+ * Returns: Pointer to the allocated EAP state machine or %NULL on failure
+ *
+ * This function allocates and initializes an EAP state machine.
+ */
+struct eap_sm * eap_server_sm_init(void *eapol_ctx,
+				   const struct eapol_callbacks *eapol_cb,
+				   struct eap_config *conf)
+{
+	struct eap_sm *sm;
+
+	sm = os_zalloc(sizeof(*sm));
+	if (sm == NULL)
+		return NULL;
+	sm->eapol_ctx = eapol_ctx;
+	sm->eapol_cb = eapol_cb;
+	sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */
+	sm->ssl_ctx = conf->ssl_ctx;
+	sm->msg_ctx = conf->msg_ctx;
+	sm->eap_sim_db_priv = conf->eap_sim_db_priv;
+	sm->backend_auth = conf->backend_auth;
+	sm->eap_server = conf->eap_server;
+	if (conf->pac_opaque_encr_key) {
+		sm->pac_opaque_encr_key = os_malloc(16);
+		if (sm->pac_opaque_encr_key) {
+			os_memcpy(sm->pac_opaque_encr_key,
+				  conf->pac_opaque_encr_key, 16);
+		}
+	}
+	if (conf->eap_fast_a_id) {
+		sm->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
+		if (sm->eap_fast_a_id) {
+			os_memcpy(sm->eap_fast_a_id, conf->eap_fast_a_id,
+				  conf->eap_fast_a_id_len);
+			sm->eap_fast_a_id_len = conf->eap_fast_a_id_len;
+		}
+	}
+	if (conf->eap_fast_a_id_info)
+		sm->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
+	sm->eap_fast_prov = conf->eap_fast_prov;
+	sm->pac_key_lifetime = conf->pac_key_lifetime;
+	sm->pac_key_refresh_time = conf->pac_key_refresh_time;
+	sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
+	sm->tnc = conf->tnc;
+	sm->wps = conf->wps;
+	if (conf->assoc_wps_ie)
+		sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie);
+	if (conf->assoc_p2p_ie)
+		sm->assoc_p2p_ie = wpabuf_dup(conf->assoc_p2p_ie);
+	if (conf->peer_addr)
+		os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN);
+	sm->fragment_size = conf->fragment_size;
+	sm->pwd_group = conf->pwd_group;
+	sm->pbc_in_m1 = conf->pbc_in_m1;
+	sm->server_id = conf->server_id;
+	sm->server_id_len = conf->server_id_len;
+	sm->erp = conf->erp;
+	sm->tls_session_lifetime = conf->tls_session_lifetime;
+
+#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;
+}
+
+
+/**
+ * eap_server_sm_deinit - Deinitialize and free an EAP server state machine
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ *
+ * This function deinitializes EAP state machine and frees all allocated
+ * resources.
+ */
+void eap_server_sm_deinit(struct eap_sm *sm)
+{
+	if (sm == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "EAP: Server state machine removed");
+	if (sm->m && sm->eap_method_priv)
+		sm->m->reset(sm, sm->eap_method_priv);
+	wpabuf_free(sm->eap_if.eapReqData);
+	bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
+	os_free(sm->eap_if.eapSessionId);
+	wpabuf_free(sm->lastReqData);
+	wpabuf_free(sm->eap_if.eapRespData);
+	os_free(sm->identity);
+	os_free(sm->pac_opaque_encr_key);
+	os_free(sm->eap_fast_a_id);
+	os_free(sm->eap_fast_a_id_info);
+	wpabuf_free(sm->eap_if.aaaEapReqData);
+	wpabuf_free(sm->eap_if.aaaEapRespData);
+	bin_clear_free(sm->eap_if.aaaEapKeyData, sm->eap_if.aaaEapKeyDataLen);
+	eap_user_free(sm->user);
+	wpabuf_free(sm->assoc_wps_ie);
+	wpabuf_free(sm->assoc_p2p_ie);
+	os_free(sm);
+}
+
+
+/**
+ * eap_sm_notify_cached - Notify EAP state machine of cached PMK
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ *
+ * This function is called when PMKSA caching is used to skip EAP
+ * authentication.
+ */
+void eap_sm_notify_cached(struct eap_sm *sm)
+{
+	if (sm == NULL)
+		return;
+
+	sm->EAP_state = EAP_SUCCESS;
+}
+
+
+/**
+ * eap_sm_pending_cb - EAP state machine callback for a pending EAP request
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ *
+ * This function is called when data for a pending EAP-Request is received.
+ */
+void eap_sm_pending_cb(struct eap_sm *sm)
+{
+	if (sm == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "EAP: Callback for pending request received");
+	if (sm->method_pending == METHOD_PENDING_WAIT)
+		sm->method_pending = METHOD_PENDING_CONT;
+}
+
+
+/**
+ * eap_sm_method_pending - Query whether EAP method is waiting for pending data
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * Returns: 1 if method is waiting for pending data or 0 if not
+ */
+int eap_sm_method_pending(struct eap_sm *sm)
+{
+	if (sm == NULL)
+		return 0;
+	return sm->method_pending == METHOD_PENDING_WAIT;
+}
+
+
+/**
+ * eap_get_identity - Get the user identity (from EAP-Response/Identity)
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * @len: Buffer for returning identity length
+ * Returns: Pointer to the user identity or %NULL if not available
+ */
+const u8 * eap_get_identity(struct eap_sm *sm, size_t *len)
+{
+	*len = sm->identity_len;
+	return sm->identity;
+}
+
+
+/**
+ * eap_get_interface - Get pointer to EAP-EAPOL interface data
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * Returns: Pointer to the EAP-EAPOL interface data
+ */
+struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm)
+{
+	return &sm->eap_if;
+}
+
+
+/**
+ * eap_server_clear_identity - Clear EAP identity information
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ *
+ * This function can be used to clear the EAP identity information in the EAP
+ * server context. This allows the EAP/Identity method to be used again after
+ * EAPOL-Start or EAPOL-Logoff.
+ */
+void eap_server_clear_identity(struct eap_sm *sm)
+{
+	os_free(sm->identity);
+	sm->identity = NULL;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source,
+				   const u8 *username, size_t username_len,
+				   const u8 *challenge, const u8 *response)
+{
+	char hex_challenge[30], hex_response[90], user[100];
+
+	/* Print out Challenge and Response in format supported by asleap. */
+	if (username)
+		printf_encode(user, sizeof(user), username, username_len);
+	else
+		user[0] = '\0';
+	wpa_snprintf_hex_sep(hex_challenge, sizeof(hex_challenge),
+			     challenge, sizeof(challenge), ':');
+	wpa_snprintf_hex_sep(hex_response, sizeof(hex_response), response, 24,
+			     ':');
+	wpa_printf(MSG_DEBUG, "[%s/user=%s] asleap -C %s -R %s",
+		   source, user, hex_challenge, hex_response);
+}
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/hostap/src/eap_server/eap_server_aka.c b/hostap/src/eap_server/eap_server_aka.c
new file mode 100644
index 0000000..db9b6aa
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_aka.c
@@ -0,0 +1,1376 @@
+/*
+ * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448)
+ * Copyright (c) 2005-2012, 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/sha256.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "eap_common/eap_sim_common.h"
+#include "eap_server/eap_i.h"
+#include "eap_server/eap_sim_db.h"
+
+
+struct eap_aka_data {
+	u8 mk[EAP_SIM_MK_LEN];
+	u8 nonce_s[EAP_SIM_NONCE_S_LEN];
+	u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
+	u8 k_encr[EAP_SIM_K_ENCR_LEN];
+	u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */
+	u8 msk[EAP_SIM_KEYING_DATA_LEN];
+	u8 emsk[EAP_EMSK_LEN];
+	u8 rand[EAP_AKA_RAND_LEN];
+	u8 autn[EAP_AKA_AUTN_LEN];
+	u8 ck[EAP_AKA_CK_LEN];
+	u8 ik[EAP_AKA_IK_LEN];
+	u8 res[EAP_AKA_RES_MAX_LEN];
+	size_t res_len;
+	enum {
+		IDENTITY, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE
+	} state;
+	char *next_pseudonym;
+	char *next_reauth_id;
+	u16 counter;
+	struct eap_sim_reauth *reauth;
+	int auts_reported; /* whether the current AUTS has been reported to the
+			    * eap_sim_db */
+	u16 notification;
+	int use_result_ind;
+
+	struct wpabuf *id_msgs;
+	int pending_id;
+	u8 eap_method;
+	u8 *network_name;
+	size_t network_name_len;
+	u16 kdf;
+	int identity_round;
+	char permanent[20]; /* Permanent username */
+};
+
+
+static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data);
+
+
+static const char * eap_aka_state_txt(int state)
+{
+	switch (state) {
+	case IDENTITY:
+		return "IDENTITY";
+	case CHALLENGE:
+		return "CHALLENGE";
+	case REAUTH:
+		return "REAUTH";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	case NOTIFICATION:
+		return "NOTIFICATION";
+	default:
+		return "Unknown?!";
+	}
+}
+
+
+static void eap_aka_state(struct eap_aka_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s",
+		   eap_aka_state_txt(data->state),
+		   eap_aka_state_txt(state));
+	data->state = state;
+}
+
+
+static int eap_aka_check_identity_reauth(struct eap_sm *sm,
+					 struct eap_aka_data *data,
+					 const char *username)
+{
+	if (data->eap_method == EAP_TYPE_AKA_PRIME &&
+	    username[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX)
+		return 0;
+	if (data->eap_method == EAP_TYPE_AKA &&
+	    username[0] != EAP_AKA_REAUTH_ID_PREFIX)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth username '%s'", username);
+	data->reauth = eap_sim_db_get_reauth_entry(sm->eap_sim_db_priv,
+						   username);
+	if (data->reauth == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown reauth identity - "
+			   "request full auth identity");
+		/* Remain in IDENTITY state for another round */
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast re-authentication");
+	os_strlcpy(data->permanent, data->reauth->permanent,
+		   sizeof(data->permanent));
+	data->counter = data->reauth->counter;
+	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+		os_memcpy(data->k_encr, data->reauth->k_encr,
+			  EAP_SIM_K_ENCR_LEN);
+		os_memcpy(data->k_aut, data->reauth->k_aut,
+			  EAP_AKA_PRIME_K_AUT_LEN);
+		os_memcpy(data->k_re, data->reauth->k_re,
+			  EAP_AKA_PRIME_K_RE_LEN);
+	} else {
+		os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN);
+	}
+
+	eap_aka_state(data, REAUTH);
+	return 1;
+}
+
+
+static void eap_aka_check_identity(struct eap_sm *sm,
+				   struct eap_aka_data *data)
+{
+	char *username;
+
+	/* Check if we already know the identity from EAP-Response/Identity */
+
+	username = sim_get_username(sm->identity, sm->identity_len);
+	if (username == NULL)
+		return;
+
+	if (eap_aka_check_identity_reauth(sm, data, username) > 0) {
+		os_free(username);
+		/*
+		 * Since re-auth username was recognized, skip AKA/Identity
+		 * exchange.
+		 */
+		return;
+	}
+
+	if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
+	     username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
+	    (data->eap_method == EAP_TYPE_AKA &&
+	     username[0] == EAP_AKA_PSEUDONYM_PREFIX)) {
+		const char *permanent;
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'",
+			   username);
+		permanent = eap_sim_db_get_permanent(
+			sm->eap_sim_db_priv, username);
+		if (permanent == NULL) {
+			os_free(username);
+			wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym "
+				   "identity - request permanent identity");
+			/* Remain in IDENTITY state for another round */
+			return;
+		}
+		os_strlcpy(data->permanent, permanent,
+			   sizeof(data->permanent));
+		/*
+		 * Since pseudonym username was recognized, skip AKA/Identity
+		 * exchange.
+		 */
+		eap_aka_fullauth(sm, data);
+	}
+
+	os_free(username);
+}
+
+
+static void * eap_aka_init(struct eap_sm *sm)
+{
+	struct eap_aka_data *data;
+
+	if (sm->eap_sim_db_priv == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured");
+		return NULL;
+	}
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	data->eap_method = EAP_TYPE_AKA;
+
+	data->state = IDENTITY;
+	data->pending_id = -1;
+	eap_aka_check_identity(sm, data);
+
+	return data;
+}
+
+
+#ifdef EAP_SERVER_AKA_PRIME
+static void * eap_aka_prime_init(struct eap_sm *sm)
+{
+	struct eap_aka_data *data;
+	/* TODO: make ANID configurable; see 3GPP TS 24.302 */
+	char *network_name = "WLAN";
+
+	if (sm->eap_sim_db_priv == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured");
+		return NULL;
+	}
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	data->eap_method = EAP_TYPE_AKA_PRIME;
+	data->network_name = (u8 *) os_strdup(network_name);
+	if (data->network_name == NULL) {
+		os_free(data);
+		return NULL;
+	}
+
+	data->network_name_len = os_strlen(network_name);
+
+	data->state = IDENTITY;
+	data->pending_id = -1;
+	eap_aka_check_identity(sm, data);
+
+	return data;
+}
+#endif /* EAP_SERVER_AKA_PRIME */
+
+
+static void eap_aka_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_aka_data *data = priv;
+	os_free(data->next_pseudonym);
+	os_free(data->next_reauth_id);
+	wpabuf_free(data->id_msgs);
+	os_free(data->network_name);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static int eap_aka_add_id_msg(struct eap_aka_data *data,
+			      const struct wpabuf *msg)
+{
+	if (msg == NULL)
+		return -1;
+
+	if (data->id_msgs == NULL) {
+		data->id_msgs = wpabuf_dup(msg);
+		return data->id_msgs == NULL ? -1 : 0;
+	}
+
+	if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0)
+		return -1;
+	wpabuf_put_buf(data->id_msgs, msg);
+
+	return 0;
+}
+
+
+static void eap_aka_add_checkcode(struct eap_aka_data *data,
+				  struct eap_sim_msg *msg)
+{
+	const u8 *addr;
+	size_t len;
+	u8 hash[SHA256_MAC_LEN];
+
+	wpa_printf(MSG_DEBUG, "   AT_CHECKCODE");
+
+	if (data->id_msgs == NULL) {
+		/*
+		 * No EAP-AKA/Identity packets were exchanged - send empty
+		 * checkcode.
+		 */
+		eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0);
+		return;
+	}
+
+	/* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */
+	addr = wpabuf_head(data->id_msgs);
+	len = wpabuf_len(data->id_msgs);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len);
+	if (data->eap_method == EAP_TYPE_AKA_PRIME)
+		sha256_vector(1, &addr, &len, hash);
+	else
+		sha1_vector(1, &addr, &len, hash);
+
+	eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash,
+			data->eap_method == EAP_TYPE_AKA_PRIME ?
+			EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN);
+}
+
+
+static int eap_aka_verify_checkcode(struct eap_aka_data *data,
+				    const u8 *checkcode, size_t checkcode_len)
+{
+	const u8 *addr;
+	size_t len;
+	u8 hash[SHA256_MAC_LEN];
+	size_t hash_len;
+
+	if (checkcode == NULL)
+		return -1;
+
+	if (data->id_msgs == NULL) {
+		if (checkcode_len != 0) {
+			wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer "
+				   "indicates that AKA/Identity messages were "
+				   "used, but they were not");
+			return -1;
+		}
+		return 0;
+	}
+
+	hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ?
+		EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN;
+
+	if (checkcode_len != hash_len) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer indicates "
+			   "that AKA/Identity message were not used, but they "
+			   "were");
+		return -1;
+	}
+
+	/* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */
+	addr = wpabuf_head(data->id_msgs);
+	len = wpabuf_len(data->id_msgs);
+	if (data->eap_method == EAP_TYPE_AKA_PRIME)
+		sha256_vector(1, &addr, &len, hash);
+	else
+		sha1_vector(1, &addr, &len, hash);
+
+	if (os_memcmp_const(hash, checkcode, hash_len) != 0) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm,
+					      struct eap_aka_data *data, u8 id)
+{
+	struct eap_sim_msg *msg;
+	struct wpabuf *buf;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity");
+	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
+			       EAP_AKA_SUBTYPE_IDENTITY);
+	data->identity_round++;
+	if (data->identity_round == 1) {
+		/*
+		 * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is
+		 * ignored and the AKA/Identity is used to request the
+		 * identity.
+		 */
+		wpa_printf(MSG_DEBUG, "   AT_ANY_ID_REQ");
+		eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
+	} else if (data->identity_round > 3) {
+		/* Cannot use more than three rounds of Identity messages */
+		eap_sim_msg_free(msg);
+		return NULL;
+	} else if (sm->identity && sm->identity_len > 0 &&
+		   (sm->identity[0] == EAP_AKA_REAUTH_ID_PREFIX ||
+		    sm->identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX)) {
+		/* Reauth id may have expired - try fullauth */
+		wpa_printf(MSG_DEBUG, "   AT_FULLAUTH_ID_REQ");
+		eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0);
+	} else {
+		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, data->eap_method, NULL, NULL, 0);
+	if (eap_aka_add_id_msg(data, buf) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+	data->pending_id = id;
+	return buf;
+}
+
+
+static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data,
+			      struct eap_sim_msg *msg, u16 counter,
+			      const u8 *nonce_s)
+{
+	os_free(data->next_pseudonym);
+	if (nonce_s == NULL) {
+		data->next_pseudonym =
+			eap_sim_db_get_next_pseudonym(
+				sm->eap_sim_db_priv,
+				data->eap_method == EAP_TYPE_AKA_PRIME ?
+				EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA);
+	} else {
+		/* Do not update pseudonym during re-authentication */
+		data->next_pseudonym = NULL;
+	}
+	os_free(data->next_reauth_id);
+	if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) {
+		data->next_reauth_id =
+			eap_sim_db_get_next_reauth_id(
+				sm->eap_sim_db_priv,
+				data->eap_method == EAP_TYPE_AKA_PRIME ?
+				EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication "
+			   "count exceeded - force full authentication");
+		data->next_reauth_id = NULL;
+	}
+
+	if (data->next_pseudonym == NULL && data->next_reauth_id == NULL &&
+	    counter == 0 && nonce_s == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "   AT_IV");
+	wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+	eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+	if (counter > 0) {
+		wpa_printf(MSG_DEBUG, "   *AT_COUNTER (%u)", counter);
+		eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+	}
+
+	if (nonce_s) {
+		wpa_printf(MSG_DEBUG, "   *AT_NONCE_S");
+		eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s,
+				EAP_SIM_NONCE_S_LEN);
+	}
+
+	if (data->next_pseudonym) {
+		wpa_printf(MSG_DEBUG, "   *AT_NEXT_PSEUDONYM (%s)",
+			   data->next_pseudonym);
+		eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM,
+				os_strlen(data->next_pseudonym),
+				(u8 *) data->next_pseudonym,
+				os_strlen(data->next_pseudonym));
+	}
+
+	if (data->next_reauth_id) {
+		wpa_printf(MSG_DEBUG, "   *AT_NEXT_REAUTH_ID (%s)",
+			   data->next_reauth_id);
+		eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID,
+				os_strlen(data->next_reauth_id),
+				(u8 *) data->next_reauth_id,
+				os_strlen(data->next_reauth_id));
+	}
+
+	if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
+			   "AT_ENCR_DATA");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm,
+					       struct eap_aka_data *data,
+					       u8 id)
+{
+	struct eap_sim_msg *msg;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Challenge");
+	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
+			       EAP_AKA_SUBTYPE_CHALLENGE);
+	wpa_printf(MSG_DEBUG, "   AT_RAND");
+	eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN);
+	wpa_printf(MSG_DEBUG, "   AT_AUTN");
+	eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN);
+	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+		if (data->kdf) {
+			/* Add the selected KDF into the beginning */
+			wpa_printf(MSG_DEBUG, "   AT_KDF");
+			eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf,
+					NULL, 0);
+		}
+		wpa_printf(MSG_DEBUG, "   AT_KDF");
+		eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF,
+				NULL, 0);
+		wpa_printf(MSG_DEBUG, "   AT_KDF_INPUT");
+		eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT,
+				data->network_name_len,
+				data->network_name, data->network_name_len);
+	}
+
+	if (eap_aka_build_encr(sm, data, msg, 0, NULL)) {
+		eap_sim_msg_free(msg);
+		return NULL;
+	}
+
+	eap_aka_add_checkcode(data, msg);
+
+	if (sm->eap_sim_aka_result_ind) {
+		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+	}
+
+#ifdef EAP_SERVER_AKA_PRIME
+	if (data->eap_method == EAP_TYPE_AKA) {
+		u16 flags = 0;
+		int i;
+		int aka_prime_preferred = 0;
+
+		i = 0;
+		while (sm->user && i < EAP_MAX_METHODS &&
+		       (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+			sm->user->methods[i].method != EAP_TYPE_NONE)) {
+			if (sm->user->methods[i].vendor == EAP_VENDOR_IETF) {
+				if (sm->user->methods[i].method ==
+				    EAP_TYPE_AKA)
+					break;
+				if (sm->user->methods[i].method ==
+				    EAP_TYPE_AKA_PRIME) {
+					aka_prime_preferred = 1;
+					break;
+				}
+			}
+			i++;
+		}
+
+		if (aka_prime_preferred)
+			flags |= EAP_AKA_BIDDING_FLAG_D;
+		eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0);
+	}
+#endif /* EAP_SERVER_AKA_PRIME */
+
+	wpa_printf(MSG_DEBUG, "   AT_MAC");
+	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm,
+					    struct eap_aka_data *data, u8 id)
+{
+	struct eap_sim_msg *msg;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication");
+
+	if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN))
+		return NULL;
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S",
+			data->nonce_s, EAP_SIM_NONCE_S_LEN);
+
+	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+		eap_aka_prime_derive_keys_reauth(data->k_re, data->counter,
+						 sm->identity,
+						 sm->identity_len,
+						 data->nonce_s,
+						 data->msk, data->emsk);
+	} else {
+		eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
+				    data->msk, data->emsk);
+		eap_sim_derive_keys_reauth(data->counter, sm->identity,
+					   sm->identity_len, data->nonce_s,
+					   data->mk, data->msk, data->emsk);
+	}
+
+	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
+			       EAP_AKA_SUBTYPE_REAUTHENTICATION);
+
+	if (eap_aka_build_encr(sm, data, msg, data->counter, data->nonce_s)) {
+		eap_sim_msg_free(msg);
+		return NULL;
+	}
+
+	eap_aka_add_checkcode(data, msg);
+
+	if (sm->eap_sim_aka_result_ind) {
+		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+	}
+
+	wpa_printf(MSG_DEBUG, "   AT_MAC");
+	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_build_notification(struct eap_sm *sm,
+						  struct eap_aka_data *data,
+						  u8 id)
+{
+	struct eap_sim_msg *msg;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Notification");
+	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
+			       EAP_AKA_SUBTYPE_NOTIFICATION);
+	wpa_printf(MSG_DEBUG, "   AT_NOTIFICATION (%d)", data->notification);
+	eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification,
+			NULL, 0);
+	if (data->use_result_ind) {
+		if (data->reauth) {
+			wpa_printf(MSG_DEBUG, "   AT_IV");
+			wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+			eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
+						   EAP_SIM_AT_ENCR_DATA);
+			wpa_printf(MSG_DEBUG, "   *AT_COUNTER (%u)",
+				   data->counter);
+			eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
+					NULL, 0);
+
+			if (eap_sim_msg_add_encr_end(msg, data->k_encr,
+						     EAP_SIM_AT_PADDING)) {
+				wpa_printf(MSG_WARNING, "EAP-AKA: Failed to "
+					   "encrypt AT_ENCR_DATA");
+				eap_sim_msg_free(msg);
+				return NULL;
+			}
+		}
+
+		wpa_printf(MSG_DEBUG, "   AT_MAC");
+		eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+	}
+	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_aka_data *data = priv;
+
+	data->auts_reported = 0;
+	switch (data->state) {
+	case IDENTITY:
+		return eap_aka_build_identity(sm, data, id);
+	case CHALLENGE:
+		return eap_aka_build_challenge(sm, data, id);
+	case REAUTH:
+		return eap_aka_build_reauth(sm, data, id);
+	case NOTIFICATION:
+		return eap_aka_build_notification(sm, data, id);
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in "
+			   "buildReq", data->state);
+		break;
+	}
+	return NULL;
+}
+
+
+static Boolean eap_aka_check(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_aka_data *data = priv;
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData,
+			       &len);
+	if (pos == NULL || len < 3) {
+		wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static Boolean eap_aka_subtype_ok(struct eap_aka_data *data, u8 subtype)
+{
+	if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR ||
+	    subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT)
+		return FALSE;
+
+	switch (data->state) {
+	case IDENTITY:
+		if (subtype != EAP_AKA_SUBTYPE_IDENTITY) {
+			wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
+				   "subtype %d", subtype);
+			return TRUE;
+		}
+		break;
+	case CHALLENGE:
+		if (subtype != EAP_AKA_SUBTYPE_CHALLENGE &&
+		    subtype != EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) {
+			wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
+				   "subtype %d", subtype);
+			return TRUE;
+		}
+		break;
+	case REAUTH:
+		if (subtype != EAP_AKA_SUBTYPE_REAUTHENTICATION) {
+			wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
+				   "subtype %d", subtype);
+			return TRUE;
+		}
+		break;
+	case NOTIFICATION:
+		if (subtype != EAP_AKA_SUBTYPE_NOTIFICATION) {
+			wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
+				   "subtype %d", subtype);
+			return TRUE;
+		}
+		break;
+	default:
+		wpa_printf(MSG_INFO, "EAP-AKA: Unexpected state (%d) for "
+			   "processing a response", data->state);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static void eap_aka_determine_identity(struct eap_sm *sm,
+				       struct eap_aka_data *data)
+{
+	char *username;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity",
+			  sm->identity, sm->identity_len);
+
+	username = sim_get_username(sm->identity, sm->identity_len);
+	if (username == NULL) {
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+
+	if (eap_aka_check_identity_reauth(sm, data, username) > 0) {
+		os_free(username);
+		return;
+	}
+
+	if (((data->eap_method == EAP_TYPE_AKA_PRIME &&
+	      username[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) ||
+	     (data->eap_method == EAP_TYPE_AKA &&
+	      username[0] == EAP_AKA_REAUTH_ID_PREFIX)) &&
+	    data->identity_round == 1) {
+		/* Remain in IDENTITY state for another round to request full
+		 * auth identity since we did not recognize reauth id */
+		os_free(username);
+		return;
+	}
+
+	if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
+	     username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
+	    (data->eap_method == EAP_TYPE_AKA &&
+	     username[0] == EAP_AKA_PSEUDONYM_PREFIX)) {
+		const char *permanent;
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'",
+			   username);
+		permanent = eap_sim_db_get_permanent(
+			sm->eap_sim_db_priv, username);
+		os_free(username);
+		if (permanent == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym "
+				   "identity - request permanent identity");
+			/* Remain in IDENTITY state for another round */
+			return;
+		}
+		os_strlcpy(data->permanent, permanent,
+			   sizeof(data->permanent));
+	} else if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
+		    username[0] == EAP_AKA_PRIME_PERMANENT_PREFIX) ||
+		   (data->eap_method == EAP_TYPE_AKA &&
+		    username[0] == EAP_AKA_PERMANENT_PREFIX)) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent username '%s'",
+			   username);
+		os_strlcpy(data->permanent, username, sizeof(data->permanent));
+		os_free(username);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized username '%s'",
+			   username);
+		os_free(username);
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+
+	eap_aka_fullauth(sm, data);
+}
+
+
+static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data)
+{
+	size_t identity_len;
+	int res;
+
+	res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, data->permanent,
+				      data->rand, data->autn, data->ik,
+				      data->ck, data->res, &data->res_len, sm);
+	if (res == EAP_SIM_DB_PENDING) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data "
+			   "not yet available - pending request");
+		sm->method_pending = METHOD_PENDING_WAIT;
+		return;
+	}
+
+#ifdef EAP_SERVER_AKA_PRIME
+	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+		/* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the
+		 * needed 6-octet SQN ^AK for CK',IK' derivation */
+		eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik,
+						 data->autn,
+						 data->network_name,
+						 data->network_name_len);
+	}
+#endif /* EAP_SERVER_AKA_PRIME */
+
+	data->reauth = NULL;
+	data->counter = 0; /* reset re-auth counter since this is full auth */
+
+	if (res != 0) {
+		wpa_printf(MSG_INFO, "EAP-AKA: Failed to get AKA "
+			   "authentication data for the peer");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+	if (sm->method_pending == METHOD_PENDING_WAIT) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data "
+			   "available - abort pending wait");
+		sm->method_pending = METHOD_PENDING_NONE;
+	}
+
+	identity_len = sm->identity_len;
+	while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Workaround - drop last null "
+			   "character from identity");
+		identity_len--;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation",
+			  sm->identity, identity_len);
+
+	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+		eap_aka_prime_derive_keys(sm->identity, identity_len, data->ik,
+					  data->ck, data->k_encr, data->k_aut,
+					  data->k_re, data->msk, data->emsk);
+	} else {
+		eap_aka_derive_mk(sm->identity, identity_len, data->ik,
+				  data->ck, data->mk);
+		eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
+				    data->msk, data->emsk);
+	}
+
+	eap_aka_state(data, CHALLENGE);
+}
+
+
+static void eap_aka_process_identity(struct eap_sm *sm,
+				     struct eap_aka_data *data,
+				     struct wpabuf *respData,
+				     struct eap_sim_attrs *attr)
+{
+	u8 *new_identity;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity");
+
+	if (attr->mac || attr->iv || attr->encr_data) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Unexpected attribute "
+			   "received in EAP-Response/AKA-Identity");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+
+	/*
+	 * We always request identity with AKA/Identity, so the peer is
+	 * required to have replied with one.
+	 */
+	if (!attr->identity || attr->identity_len == 0) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Peer did not provide any "
+			   "identity");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+
+	new_identity = os_malloc(attr->identity_len);
+	if (new_identity == NULL) {
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+	os_free(sm->identity);
+	sm->identity = new_identity;
+	os_memcpy(sm->identity, attr->identity, attr->identity_len);
+	sm->identity_len = attr->identity_len;
+
+	eap_aka_determine_identity(sm, data);
+	if (eap_get_id(respData) == data->pending_id) {
+		data->pending_id = -1;
+		eap_aka_add_id_msg(data, respData);
+	}
+}
+
+
+static int eap_aka_verify_mac(struct eap_aka_data *data,
+			      const struct wpabuf *req,
+			      const u8 *mac, const u8 *extra,
+			      size_t extra_len)
+{
+	if (data->eap_method == EAP_TYPE_AKA_PRIME)
+		return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra,
+						 extra_len);
+	return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len);
+}
+
+
+static void eap_aka_process_challenge(struct eap_sm *sm,
+				      struct eap_aka_data *data,
+				      struct wpabuf *respData,
+				      struct eap_sim_attrs *attr)
+{
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge");
+
+#ifdef EAP_SERVER_AKA_PRIME
+#if 0
+	/* KDF negotiation; to be enabled only after more than one KDF is
+	 * supported */
+	if (data->eap_method == EAP_TYPE_AKA_PRIME &&
+	    attr->kdf_count == 1 && attr->mac == NULL) {
+		if (attr->kdf[0] != EAP_AKA_PRIME_KDF) {
+			wpa_printf(MSG_WARNING, "EAP-AKA': Peer selected "
+				   "unknown KDF");
+			data->notification =
+				EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+			eap_aka_state(data, NOTIFICATION);
+			return;
+		}
+
+		data->kdf = attr->kdf[0];
+
+		/* Allow negotiation to continue with the selected KDF by
+		 * sending another Challenge message */
+		wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf);
+		return;
+	}
+#endif
+#endif /* EAP_SERVER_AKA_PRIME */
+
+	if (attr->checkcode &&
+	    eap_aka_verify_checkcode(data, attr->checkcode,
+				     attr->checkcode_len)) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
+			   "message");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+	if (attr->mac == NULL ||
+	    eap_aka_verify_mac(data, respData, attr->mac, NULL, 0)) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
+			   "did not include valid AT_MAC");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+
+	/*
+	 * AT_RES is padded, so verify that there is enough room for RES and
+	 * that the RES length in bits matches with the expected RES.
+	 */
+	if (attr->res == NULL || attr->res_len < data->res_len ||
+	    attr->res_len_bits != data->res_len * 8 ||
+	    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)",
+			   (unsigned long) attr->res_len,
+			   (unsigned long) attr->res_len_bits,
+			   (unsigned long) data->res_len * 8);
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Challenge response includes the "
+		   "correct AT_MAC");
+	if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+		data->use_result_ind = 1;
+		data->notification = EAP_SIM_SUCCESS;
+		eap_aka_state(data, NOTIFICATION);
+	} else
+		eap_aka_state(data, SUCCESS);
+
+	if (data->next_pseudonym) {
+		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent,
+					 data->next_pseudonym);
+		data->next_pseudonym = NULL;
+	}
+	if (data->next_reauth_id) {
+		if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+#ifdef EAP_SERVER_AKA_PRIME
+			eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
+						    data->permanent,
+						    data->next_reauth_id,
+						    data->counter + 1,
+						    data->k_encr, data->k_aut,
+						    data->k_re);
+#endif /* EAP_SERVER_AKA_PRIME */
+		} else {
+			eap_sim_db_add_reauth(sm->eap_sim_db_priv,
+					      data->permanent,
+					      data->next_reauth_id,
+					      data->counter + 1,
+					      data->mk);
+		}
+		data->next_reauth_id = NULL;
+	}
+}
+
+
+static void eap_aka_process_sync_failure(struct eap_sm *sm,
+					 struct eap_aka_data *data,
+					 struct wpabuf *respData,
+					 struct eap_sim_attrs *attr)
+{
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Synchronization-Failure");
+
+	if (attr->auts == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Synchronization-Failure "
+			   "message did not include valid AT_AUTS");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+
+	/* Avoid re-reporting AUTS when processing pending EAP packet by
+	 * maintaining a local flag stating whether this AUTS has already been
+	 * reported. */
+	if (!data->auts_reported &&
+	    eap_sim_db_resynchronize(sm->eap_sim_db_priv, data->permanent,
+				     attr->auts, data->rand)) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+	data->auts_reported = 1;
+
+	/* Remain in CHALLENGE state to re-try after resynchronization */
+	eap_aka_fullauth(sm, data);
+}
+
+
+static void eap_aka_process_reauth(struct eap_sm *sm,
+				   struct eap_aka_data *data,
+				   struct wpabuf *respData,
+				   struct eap_sim_attrs *attr)
+{
+	struct eap_sim_attrs eattr;
+	u8 *decrypted = NULL;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication");
+
+	if (attr->mac == NULL ||
+	    eap_aka_verify_mac(data, respData, attr->mac, data->nonce_s,
+			       EAP_SIM_NONCE_S_LEN)) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message "
+			   "did not include valid AT_MAC");
+		goto fail;
+	}
+
+	if (attr->encr_data == NULL || attr->iv == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
+			   "message did not include encrypted data");
+		goto fail;
+	}
+
+	decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+				       attr->encr_data_len, attr->iv, &eattr,
+				       0);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
+			   "data from reauthentication message");
+		goto fail;
+	}
+
+	if (eattr.counter != data->counter) {
+		wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message "
+			   "used incorrect counter %u, expected %u",
+			   eattr.counter, data->counter);
+		goto fail;
+	}
+	os_free(decrypted);
+	decrypted = NULL;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response includes "
+		   "the correct AT_MAC");
+
+	if (eattr.counter_too_small) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response "
+			   "included AT_COUNTER_TOO_SMALL - starting full "
+			   "authentication");
+		eap_aka_fullauth(sm, data);
+		return;
+	}
+
+	if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+		data->use_result_ind = 1;
+		data->notification = EAP_SIM_SUCCESS;
+		eap_aka_state(data, NOTIFICATION);
+	} else
+		eap_aka_state(data, SUCCESS);
+
+	if (data->next_reauth_id) {
+		if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+#ifdef EAP_SERVER_AKA_PRIME
+			eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
+						    data->permanent,
+						    data->next_reauth_id,
+						    data->counter + 1,
+						    data->k_encr, data->k_aut,
+						    data->k_re);
+#endif /* EAP_SERVER_AKA_PRIME */
+		} else {
+			eap_sim_db_add_reauth(sm->eap_sim_db_priv,
+					      data->permanent,
+					      data->next_reauth_id,
+					      data->counter + 1,
+					      data->mk);
+		}
+		data->next_reauth_id = NULL;
+	} else {
+		eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+		data->reauth = NULL;
+	}
+
+	return;
+
+fail:
+	data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+	eap_aka_state(data, NOTIFICATION);
+	eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+	data->reauth = NULL;
+	os_free(decrypted);
+}
+
+
+static void eap_aka_process_client_error(struct eap_sm *sm,
+					 struct eap_aka_data *data,
+					 struct wpabuf *respData,
+					 struct eap_sim_attrs *attr)
+{
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Client reported error %d",
+		   attr->client_error_code);
+	if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
+		eap_aka_state(data, SUCCESS);
+	else
+		eap_aka_state(data, FAILURE);
+}
+
+
+static void eap_aka_process_authentication_reject(
+	struct eap_sm *sm, struct eap_aka_data *data,
+	struct wpabuf *respData, struct eap_sim_attrs *attr)
+{
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Client rejected authentication");
+	eap_aka_state(data, FAILURE);
+}
+
+
+static void eap_aka_process_notification(struct eap_sm *sm,
+					 struct eap_aka_data *data,
+					 struct wpabuf *respData,
+					 struct eap_sim_attrs *attr)
+{
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Client replied to notification");
+	if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
+		eap_aka_state(data, SUCCESS);
+	else
+		eap_aka_state(data, FAILURE);
+}
+
+
+static void eap_aka_process(struct eap_sm *sm, void *priv,
+			    struct wpabuf *respData)
+{
+	struct eap_aka_data *data = priv;
+	const u8 *pos, *end;
+	u8 subtype;
+	size_t len;
+	struct eap_sim_attrs attr;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData,
+			       &len);
+	if (pos == NULL || len < 3)
+		return;
+
+	end = pos + len;
+	subtype = *pos;
+	pos += 3;
+
+	if (eap_aka_subtype_ok(data, subtype)) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized or unexpected "
+			   "EAP-AKA Subtype in EAP Response");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+
+	if (eap_sim_parse_attr(pos, end, &attr,
+			       data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1,
+			       0)) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+
+	if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR) {
+		eap_aka_process_client_error(sm, data, respData, &attr);
+		return;
+	}
+
+	if (subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) {
+		eap_aka_process_authentication_reject(sm, data, respData,
+						      &attr);
+		return;
+	}
+
+	switch (data->state) {
+	case IDENTITY:
+		eap_aka_process_identity(sm, data, respData, &attr);
+		break;
+	case CHALLENGE:
+		if (subtype == EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) {
+			eap_aka_process_sync_failure(sm, data, respData,
+						     &attr);
+		} else {
+			eap_aka_process_challenge(sm, data, respData, &attr);
+		}
+		break;
+	case REAUTH:
+		eap_aka_process_reauth(sm, data, respData, &attr);
+		break;
+	case NOTIFICATION:
+		eap_aka_process_notification(sm, data, respData, &attr);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in "
+			   "process", data->state);
+		break;
+	}
+}
+
+
+static Boolean eap_aka_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_aka_data *data = priv;
+	return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_aka_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+	*len = EAP_SIM_KEYING_DATA_LEN;
+	return key;
+}
+
+
+static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_aka_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+	*len = EAP_EMSK_LEN;
+	return key;
+}
+
+
+static Boolean eap_aka_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_aka_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_aka_data *data = priv;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	*len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN;
+	id = os_malloc(*len);
+	if (id == NULL)
+		return NULL;
+
+	id[0] = data->eap_method;
+	os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN);
+	os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len);
+
+	return id;
+}
+
+
+int eap_server_aka_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_aka_init;
+	eap->reset = eap_aka_reset;
+	eap->buildReq = eap_aka_buildReq;
+	eap->check = eap_aka_check;
+	eap->process = eap_aka_process;
+	eap->isDone = eap_aka_isDone;
+	eap->getKey = eap_aka_getKey;
+	eap->isSuccess = eap_aka_isSuccess;
+	eap->get_emsk = eap_aka_get_emsk;
+	eap->getSessionId = eap_aka_get_session_id;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
+
+
+#ifdef EAP_SERVER_AKA_PRIME
+int eap_server_aka_prime_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME,
+				      "AKA'");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_aka_prime_init;
+	eap->reset = eap_aka_reset;
+	eap->buildReq = eap_aka_buildReq;
+	eap->check = eap_aka_check;
+	eap->process = eap_aka_process;
+	eap->isDone = eap_aka_isDone;
+	eap->getKey = eap_aka_getKey;
+	eap->isSuccess = eap_aka_isSuccess;
+	eap->get_emsk = eap_aka_get_emsk;
+	eap->getSessionId = eap_aka_get_session_id;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+
+	return ret;
+}
+#endif /* EAP_SERVER_AKA_PRIME */
diff --git a/hostap/src/eap_server/eap_server_eke.c b/hostap/src/eap_server/eap_server_eke.c
new file mode 100644
index 0000000..ba82be9
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_eke.c
@@ -0,0 +1,817 @@
+/*
+ * hostapd / EAP-EKE (RFC 6124) server
+ * Copyright (c) 2013, 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/random.h"
+#include "eap_server/eap_i.h"
+#include "eap_common/eap_eke_common.h"
+
+
+struct eap_eke_data {
+	enum {
+		IDENTITY, COMMIT, CONFIRM, FAILURE_REPORT, SUCCESS, FAILURE
+	} state;
+	u8 msk[EAP_MSK_LEN];
+	u8 emsk[EAP_EMSK_LEN];
+	u8 *peerid;
+	size_t peerid_len;
+	u8 peerid_type;
+	u8 serverid_type;
+	u8 dh_priv[EAP_EKE_MAX_DH_LEN];
+	u8 key[EAP_EKE_MAX_KEY_LEN];
+	struct eap_eke_session sess;
+	u8 nonce_p[EAP_EKE_MAX_NONCE_LEN];
+	u8 nonce_s[EAP_EKE_MAX_NONCE_LEN];
+	struct wpabuf *msgs;
+	int phase2;
+	u32 failure_code;
+};
+
+
+static const char * eap_eke_state_txt(int state)
+{
+	switch (state) {
+	case IDENTITY:
+		return "IDENTITY";
+	case COMMIT:
+		return "COMMIT";
+	case CONFIRM:
+		return "CONFIRM";
+	case FAILURE_REPORT:
+		return "FAILURE_REPORT";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "?";
+	}
+}
+
+
+static void eap_eke_state(struct eap_eke_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s",
+		   eap_eke_state_txt(data->state),
+		   eap_eke_state_txt(state));
+	data->state = state;
+}
+
+
+static void eap_eke_fail(struct eap_eke_data *data, u32 code)
+{
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Failure - code 0x%x", code);
+	data->failure_code = code;
+	eap_eke_state(data, FAILURE_REPORT);
+}
+
+
+static void * eap_eke_init(struct eap_sm *sm)
+{
+	struct eap_eke_data *data;
+	size_t i;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	eap_eke_state(data, IDENTITY);
+
+	data->serverid_type = EAP_EKE_ID_OPAQUE;
+	for (i = 0; i < sm->server_id_len; i++) {
+		if (sm->server_id[i] == '.' &&
+		    data->serverid_type == EAP_EKE_ID_OPAQUE)
+			data->serverid_type = EAP_EKE_ID_FQDN;
+		if (sm->server_id[i] == '@')
+			data->serverid_type = EAP_EKE_ID_NAI;
+	}
+
+	data->phase2 = sm->init_phase2;
+
+	return data;
+}
+
+
+static void eap_eke_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_eke_data *data = priv;
+	eap_eke_session_clean(&data->sess);
+	os_free(data->peerid);
+	wpabuf_free(data->msgs);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data,
+					 u8 id, size_t length, u8 eke_exch)
+{
+	struct wpabuf *msg;
+	size_t plen;
+
+	plen = 1 + length;
+
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen,
+			    EAP_CODE_REQUEST, id);
+	if (msg == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory");
+		return NULL;
+	}
+
+	wpabuf_put_u8(msg, eke_exch);
+
+	return msg;
+}
+
+
+static int supported_proposal(const u8 *pos)
+{
+	if (pos[0] == EAP_EKE_DHGROUP_EKE_16 &&
+	    pos[1] == EAP_EKE_ENCR_AES128_CBC &&
+	    pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 &&
+	    pos[3] == EAP_EKE_MAC_HMAC_SHA2_256)
+		return 1;
+
+	if (pos[0] == EAP_EKE_DHGROUP_EKE_15 &&
+	    pos[1] == EAP_EKE_ENCR_AES128_CBC &&
+	    pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 &&
+	    pos[3] == EAP_EKE_MAC_HMAC_SHA2_256)
+		return 1;
+
+	if (pos[0] == EAP_EKE_DHGROUP_EKE_14 &&
+	    pos[1] == EAP_EKE_ENCR_AES128_CBC &&
+	    pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 &&
+	    pos[3] == EAP_EKE_MAC_HMAC_SHA2_256)
+		return 1;
+
+	if (pos[0] == EAP_EKE_DHGROUP_EKE_14 &&
+	    pos[1] == EAP_EKE_ENCR_AES128_CBC &&
+	    pos[2] == EAP_EKE_PRF_HMAC_SHA1 &&
+	    pos[3] == EAP_EKE_MAC_HMAC_SHA1)
+		return 1;
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_eke_build_failure(struct eap_eke_data *data, u8 id)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Failure: Failure-Code=0x%x",
+		   data->failure_code);
+
+	msg = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE);
+	if (msg == NULL) {
+		eap_eke_state(data, FAILURE);
+		return NULL;
+	}
+	wpabuf_put_be32(msg, data->failure_code);
+
+	return msg;
+}
+
+
+static struct wpabuf * eap_eke_build_identity(struct eap_sm *sm,
+					      struct eap_eke_data *data,
+					      u8 id)
+{
+	struct wpabuf *msg;
+	size_t plen;
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Identity");
+
+	plen = 2 + 4 * 4 + 1 + sm->server_id_len;
+	msg = eap_eke_build_msg(data, id, plen, EAP_EKE_ID);
+	if (msg == NULL)
+		return NULL;
+
+	wpabuf_put_u8(msg, 4); /* NumProposals */
+	wpabuf_put_u8(msg, 0); /* Reserved */
+
+	/* Proposal - DH Group 16 with AES128-CBC and SHA256 */
+	wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_16); /* Group Description */
+	wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
+	wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */
+	wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */
+
+	/* Proposal - DH Group 15 with AES128-CBC and SHA256 */
+	wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_15); /* Group Description */
+	wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
+	wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */
+	wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */
+
+	/* Proposal - DH Group 14 with AES128-CBC and SHA256 */
+	wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */
+	wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
+	wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */
+	wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */
+
+	/*
+	 * Proposal - DH Group 14 with AES128-CBC and SHA1
+	 * (mandatory to implement algorithms)
+	 */
+	wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */
+	wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
+	wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA1); /* PRF */
+	wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA1); /* MAC */
+
+	/* Server IDType + Identity */
+	wpabuf_put_u8(msg, data->serverid_type);
+	wpabuf_put_data(msg, sm->server_id, sm->server_id_len);
+
+	wpabuf_free(data->msgs);
+	data->msgs = wpabuf_dup(msg);
+	if (data->msgs == NULL) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+static struct wpabuf * eap_eke_build_commit(struct eap_sm *sm,
+					    struct eap_eke_data *data, u8 id)
+{
+	struct wpabuf *msg;
+	u8 pub[EAP_EKE_MAX_DH_LEN];
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Commit");
+
+	if (sm->user == NULL || sm->user->password == NULL) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Password with not configured");
+		eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
+		return eap_eke_build_failure(data, id);
+	}
+
+	if (eap_eke_derive_key(&data->sess, sm->user->password,
+			       sm->user->password_len,
+			       sm->server_id, sm->server_id_len,
+			       data->peerid, data->peerid_len, data->key) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key");
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return eap_eke_build_failure(data, id);
+	}
+
+	msg = eap_eke_build_msg(data, id, data->sess.dhcomp_len,
+				EAP_EKE_COMMIT);
+	if (msg == NULL) {
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return eap_eke_build_failure(data, id);
+	}
+
+	/*
+	 * y_s = g ^ x_s (mod p)
+	 * x_s = random number 2 .. p-1
+	 * temp = prf(0+, password)
+	 * key = prf+(temp, ID_S | ID_P)
+	 * DHComponent_S = Encr(key, y_s)
+	 */
+
+	if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH");
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return eap_eke_build_failure(data, id);
+	}
+
+	if (eap_eke_dhcomp(&data->sess, data->key, pub,
+			   wpabuf_put(msg, data->sess.dhcomp_len))
+	    < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S");
+		wpabuf_free(msg);
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return eap_eke_build_failure(data, id);
+	}
+
+	if (wpabuf_resize(&data->msgs, wpabuf_len(msg)) < 0) {
+		wpabuf_free(msg);
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return eap_eke_build_failure(data, id);
+	}
+	wpabuf_put_buf(data->msgs, msg);
+
+	return msg;
+}
+
+
+static struct wpabuf * eap_eke_build_confirm(struct eap_sm *sm,
+					     struct eap_eke_data *data, u8 id)
+{
+	struct wpabuf *msg;
+	size_t plen, prot_len;
+	u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN];
+	u8 *auth;
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Confirm");
+
+	plen = data->sess.pnonce_ps_len + data->sess.prf_len;
+	msg = eap_eke_build_msg(data, id, plen, EAP_EKE_CONFIRM);
+	if (msg == NULL) {
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return eap_eke_build_failure(data, id);
+	}
+
+	if (random_get_bytes(data->nonce_s, data->sess.nonce_len)) {
+		wpabuf_free(msg);
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return eap_eke_build_failure(data, id);
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S",
+			data->nonce_s, data->sess.nonce_len);
+
+	os_memcpy(nonces, data->nonce_p, data->sess.nonce_len);
+	os_memcpy(nonces + data->sess.nonce_len, data->nonce_s,
+		  data->sess.nonce_len);
+	prot_len = wpabuf_tailroom(msg);
+	if (eap_eke_prot(&data->sess, nonces, 2 * data->sess.nonce_len,
+			 wpabuf_put(msg, 0), &prot_len) < 0) {
+		wpabuf_free(msg);
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return eap_eke_build_failure(data, id);
+	}
+	wpabuf_put(msg, prot_len);
+
+	if (eap_eke_derive_ka(&data->sess,
+			      sm->server_id, sm->server_id_len,
+			      data->peerid, data->peerid_len,
+			      data->nonce_p, data->nonce_s) < 0) {
+		wpabuf_free(msg);
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return eap_eke_build_failure(data, id);
+	}
+
+	auth = wpabuf_put(msg, data->sess.prf_len);
+	if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth) < 0) {
+		wpabuf_free(msg);
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return eap_eke_build_failure(data, id);
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth, data->sess.prf_len);
+
+	return msg;
+}
+
+
+static struct wpabuf * eap_eke_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_eke_data *data = priv;
+
+	switch (data->state) {
+	case IDENTITY:
+		return eap_eke_build_identity(sm, data, id);
+	case COMMIT:
+		return eap_eke_build_commit(sm, data, id);
+	case CONFIRM:
+		return eap_eke_build_confirm(sm, data, id);
+	case FAILURE_REPORT:
+		return eap_eke_build_failure(data, id);
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-EKE: Unknown state %d in buildReq",
+			   data->state);
+		break;
+	}
+	return NULL;
+}
+
+
+static Boolean eap_eke_check(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_eke_data *data = priv;
+	size_t len;
+	const u8 *pos;
+	u8 eke_exch;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len);
+	if (pos == NULL || len < 1) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Invalid frame");
+		return TRUE;
+	}
+
+	eke_exch = *pos;
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: EKE-Exch=%d", eke_exch);
+
+	if (data->state == IDENTITY && eke_exch == EAP_EKE_ID)
+		return FALSE;
+
+	if (data->state == COMMIT && eke_exch == EAP_EKE_COMMIT)
+		return FALSE;
+
+	if (data->state == CONFIRM && eke_exch == EAP_EKE_CONFIRM)
+		return FALSE;
+
+	if (eke_exch == EAP_EKE_FAILURE)
+		return FALSE;
+
+	wpa_printf(MSG_INFO, "EAP-EKE: Unexpected EKE-Exch=%d in state=%d",
+		   eke_exch, data->state);
+
+	return TRUE;
+}
+
+
+static void eap_eke_process_identity(struct eap_sm *sm,
+				     struct eap_eke_data *data,
+				     const struct wpabuf *respData,
+				     const u8 *payload, size_t payloadlen)
+{
+	const u8 *pos, *end;
+	int i;
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Identity");
+
+	if (data->state != IDENTITY) {
+		eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+		return;
+	}
+
+	pos = payload;
+	end = payload + payloadlen;
+
+	if (pos + 2 + 4 + 1 > end) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Too short EAP-EKE-ID payload");
+		eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+		return;
+	}
+
+	if (*pos != 1) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Unexpected NumProposals %d (expected 1)",
+			   *pos);
+		eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+		return;
+	}
+
+	pos += 2;
+
+	if (!supported_proposal(pos)) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Unexpected Proposal (%u:%u:%u:%u)",
+			   pos[0], pos[1], pos[2], pos[3]);
+		eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Selected Proposal (%u:%u:%u:%u)",
+		   pos[0], pos[1], pos[2], pos[3]);
+	if (eap_eke_session_init(&data->sess, pos[0], pos[1], pos[2], pos[3]) <
+	    0) {
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return;
+	}
+	pos += 4;
+
+	data->peerid_type = *pos++;
+	os_free(data->peerid);
+	data->peerid = os_malloc(end - pos);
+	if (data->peerid == NULL) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid");
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return;
+	}
+	os_memcpy(data->peerid, pos, end - pos);
+	data->peerid_len = end - pos;
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type);
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity",
+			  data->peerid, data->peerid_len);
+
+	if (eap_user_get(sm, data->peerid, data->peerid_len, data->phase2)) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Peer Identity not found from user database");
+		eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
+		return;
+	}
+
+	for (i = 0; i < EAP_MAX_METHODS; i++) {
+		if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
+		    sm->user->methods[i].method == EAP_TYPE_EKE)
+			break;
+	}
+	if (i == EAP_MAX_METHODS) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Matching user entry does not allow EAP-EKE");
+		eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
+		return;
+	}
+
+	if (sm->user->password == NULL || sm->user->password_len == 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: No password configured for peer");
+		eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
+		return;
+	}
+
+	if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) {
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return;
+	}
+	wpabuf_put_buf(data->msgs, respData);
+
+	eap_eke_state(data, COMMIT);
+}
+
+
+static void eap_eke_process_commit(struct eap_sm *sm,
+				   struct eap_eke_data *data,
+				   const struct wpabuf *respData,
+				   const u8 *payload, size_t payloadlen)
+{
+	const u8 *pos, *end, *dhcomp, *pnonce;
+	size_t decrypt_len;
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Commit");
+
+	if (data->state != COMMIT) {
+		eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+		return;
+	}
+
+	pos = payload;
+	end = payload + payloadlen;
+
+	if (pos + data->sess.dhcomp_len + data->sess.pnonce_len > end) {
+		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit");
+		eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+		return;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P",
+		    pos, data->sess.dhcomp_len);
+	dhcomp = pos;
+	pos += data->sess.dhcomp_len;
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", pos, data->sess.pnonce_len);
+	pnonce = pos;
+	pos += data->sess.pnonce_len;
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos);
+
+	if (eap_eke_shared_secret(&data->sess, data->key, data->dh_priv, dhcomp)
+	    < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret");
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return;
+	}
+
+	if (eap_eke_derive_ke_ki(&data->sess,
+				 sm->server_id, sm->server_id_len,
+				 data->peerid, data->peerid_len) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki");
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return;
+	}
+
+	decrypt_len = sizeof(data->nonce_p);
+	if (eap_eke_decrypt_prot(&data->sess, pnonce, data->sess.pnonce_len,
+				 data->nonce_p, &decrypt_len) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_P");
+		eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+		return;
+	}
+	if (decrypt_len < (size_t) data->sess.nonce_len) {
+		wpa_printf(MSG_INFO, "EAP-EKE: PNonce_P protected data too short to include Nonce_P");
+		eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+		return;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P",
+			data->nonce_p, data->sess.nonce_len);
+
+	if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) {
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return;
+	}
+	wpabuf_put_buf(data->msgs, respData);
+
+	eap_eke_state(data, CONFIRM);
+}
+
+
+static void eap_eke_process_confirm(struct eap_sm *sm,
+				    struct eap_eke_data *data,
+				    const struct wpabuf *respData,
+				    const u8 *payload, size_t payloadlen)
+{
+	size_t decrypt_len;
+	u8 nonce[EAP_EKE_MAX_NONCE_LEN];
+	u8 auth_p[EAP_EKE_MAX_HASH_LEN];
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm");
+
+	if (data->state != CONFIRM) {
+		eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm");
+
+	if (payloadlen < (size_t) data->sess.pnonce_len + data->sess.prf_len) {
+		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm");
+		eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+		return;
+	}
+
+	decrypt_len = sizeof(nonce);
+	if (eap_eke_decrypt_prot(&data->sess, payload, data->sess.pnonce_len,
+				 nonce, &decrypt_len) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_S");
+		eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+		return;
+	}
+	if (decrypt_len < (size_t) data->sess.nonce_len) {
+		wpa_printf(MSG_INFO, "EAP-EKE: PNonce_S protected data too short to include Nonce_S");
+		eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+		return;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_S",
+			nonce, data->sess.nonce_len);
+	if (os_memcmp(nonce, data->nonce_s, data->sess.nonce_len) != 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_S does not match previously sent Nonce_S");
+		eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+		return;
+	}
+
+	if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth_p) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Could not derive Auth_P");
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth_p, data->sess.prf_len);
+	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;
+	}
+
+	if (eap_eke_derive_msk(&data->sess, sm->server_id, sm->server_id_len,
+			       data->peerid, data->peerid_len,
+			       data->nonce_s, data->nonce_p,
+			       data->msk, data->emsk) < 0) {
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK");
+		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+		return;
+	}
+
+	os_memset(data->dh_priv, 0, sizeof(data->dh_priv));
+	os_memset(data->key, 0, sizeof(data->key));
+	eap_eke_session_clean(&data->sess);
+
+	eap_eke_state(data, SUCCESS);
+}
+
+
+static void eap_eke_process_failure(struct eap_sm *sm,
+				    struct eap_eke_data *data,
+				    const struct wpabuf *respData,
+				    const u8 *payload, size_t payloadlen)
+{
+	u32 code;
+
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Failure");
+
+	if (payloadlen < 4) {
+		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure");
+		eap_eke_state(data, FAILURE);
+		return;
+	}
+
+	code = WPA_GET_BE32(payload);
+	wpa_printf(MSG_DEBUG, "EAP-EKE: Peer reported failure code 0x%x", code);
+
+	eap_eke_state(data, FAILURE);
+}
+
+
+static void eap_eke_process(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_eke_data *data = priv;
+	u8 eke_exch;
+	size_t len;
+	const u8 *pos, *end;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len);
+	if (pos == NULL || len < 1)
+		return;
+
+	eke_exch = *pos;
+	end = pos + len;
+	pos++;
+
+	wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received payload", pos, end - pos);
+
+	switch (eke_exch) {
+	case EAP_EKE_ID:
+		eap_eke_process_identity(sm, data, respData, pos, end - pos);
+		break;
+	case EAP_EKE_COMMIT:
+		eap_eke_process_commit(sm, data, respData, pos, end - pos);
+		break;
+	case EAP_EKE_CONFIRM:
+		eap_eke_process_confirm(sm, data, respData, pos, end - pos);
+		break;
+	case EAP_EKE_FAILURE:
+		eap_eke_process_failure(sm, data, respData, pos, end - pos);
+		break;
+	}
+}
+
+
+static Boolean eap_eke_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_eke_data *data = priv;
+	return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_eke_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_MSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->msk, EAP_MSK_LEN);
+	*len = EAP_MSK_LEN;
+
+	return key;
+}
+
+
+static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_eke_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+	*len = EAP_EMSK_LEN;
+
+	return key;
+}
+
+
+static Boolean eap_eke_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_eke_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_eke_data *data = priv;
+	u8 *sid;
+	size_t sid_len;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	sid_len = 1 + 2 * data->sess.nonce_len;
+	sid = os_malloc(sid_len);
+	if (sid == NULL)
+		return NULL;
+	sid[0] = EAP_TYPE_EKE;
+	os_memcpy(sid + 1, data->nonce_p, data->sess.nonce_len);
+	os_memcpy(sid + 1 + data->sess.nonce_len, data->nonce_s,
+		  data->sess.nonce_len);
+	*len = sid_len;
+
+	return sid;
+}
+
+
+int eap_server_eke_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_eke_init;
+	eap->reset = eap_eke_reset;
+	eap->buildReq = eap_eke_buildReq;
+	eap->check = eap_eke_check;
+	eap->process = eap_eke_process;
+	eap->isDone = eap_eke_isDone;
+	eap->getKey = eap_eke_getKey;
+	eap->isSuccess = eap_eke_isSuccess;
+	eap->get_emsk = eap_eke_get_emsk;
+	eap->getSessionId = eap_eke_get_session_id;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_fast.c b/hostap/src/eap_server/eap_server_fast.c
new file mode 100644
index 0000000..bd9018e
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_fast.c
@@ -0,0 +1,1632 @@
+/*
+ * EAP-FAST server (RFC 4851)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "crypto/random.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_common/eap_fast_common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+
+
+static void eap_fast_reset(struct eap_sm *sm, void *priv);
+
+
+/* Private PAC-Opaque TLV types */
+#define PAC_OPAQUE_TYPE_PAD 0
+#define PAC_OPAQUE_TYPE_KEY 1
+#define PAC_OPAQUE_TYPE_LIFETIME 2
+#define PAC_OPAQUE_TYPE_IDENTITY 3
+
+struct eap_fast_data {
+	struct eap_ssl_data ssl;
+	enum {
+		START, PHASE1, PHASE2_START, PHASE2_ID, PHASE2_METHOD,
+		CRYPTO_BINDING, REQUEST_PAC, SUCCESS, FAILURE
+	} state;
+
+	int fast_version;
+	const struct eap_method *phase2_method;
+	void *phase2_priv;
+	int force_version;
+	int peer_version;
+
+	u8 crypto_binding_nonce[32];
+	int final_result;
+
+	struct eap_fast_key_block_provisioning *key_block_p;
+
+	u8 simck[EAP_FAST_SIMCK_LEN];
+	u8 cmk[EAP_FAST_CMK_LEN];
+	int simck_idx;
+
+	u8 pac_opaque_encr[16];
+	u8 *srv_id;
+	size_t srv_id_len;
+	char *srv_id_info;
+
+	int anon_provisioning;
+	int send_new_pac; /* server triggered re-keying of Tunnel PAC */
+	struct wpabuf *pending_phase2_resp;
+	u8 *identity; /* from PAC-Opaque */
+	size_t identity_len;
+	int eap_seq;
+	int tnc_started;
+
+	int pac_key_lifetime;
+	int pac_key_refresh_time;
+};
+
+
+static int eap_fast_process_phase2_start(struct eap_sm *sm,
+					 struct eap_fast_data *data);
+
+
+static const char * eap_fast_state_txt(int state)
+{
+	switch (state) {
+	case START:
+		return "START";
+	case PHASE1:
+		return "PHASE1";
+	case PHASE2_START:
+		return "PHASE2_START";
+	case PHASE2_ID:
+		return "PHASE2_ID";
+	case PHASE2_METHOD:
+		return "PHASE2_METHOD";
+	case CRYPTO_BINDING:
+		return "CRYPTO_BINDING";
+	case REQUEST_PAC:
+		return "REQUEST_PAC";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "Unknown?!";
+	}
+}
+
+
+static void eap_fast_state(struct eap_fast_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-FAST: %s -> %s",
+		   eap_fast_state_txt(data->state),
+		   eap_fast_state_txt(state));
+	data->state = state;
+}
+
+
+static EapType eap_fast_req_failure(struct eap_sm *sm,
+				    struct eap_fast_data *data)
+{
+	/* TODO: send Result TLV(FAILURE) */
+	eap_fast_state(data, FAILURE);
+	return EAP_TYPE_NONE;
+}
+
+
+static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
+				      const u8 *client_random,
+				      const u8 *server_random,
+				      u8 *master_secret)
+{
+	struct eap_fast_data *data = ctx;
+	const u8 *pac_opaque;
+	size_t pac_opaque_len;
+	u8 *buf, *pos, *end, *pac_key = NULL;
+	os_time_t lifetime = 0;
+	struct os_time now;
+	u8 *identity = NULL;
+	size_t identity_len = 0;
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback");
+	wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket (PAC-Opaque)",
+		    ticket, len);
+
+	if (len < 4 || WPA_GET_BE16(ticket) != PAC_TYPE_PAC_OPAQUE) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Ignore invalid "
+			   "SessionTicket");
+		return 0;
+	}
+
+	pac_opaque_len = WPA_GET_BE16(ticket + 2);
+	pac_opaque = ticket + 4;
+	if (pac_opaque_len < 8 || pac_opaque_len % 8 ||
+	    pac_opaque_len > len - 4) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Ignore invalid PAC-Opaque "
+			   "(len=%lu left=%lu)",
+			   (unsigned long) pac_opaque_len,
+			   (unsigned long) len);
+		return 0;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-FAST: Received PAC-Opaque",
+		    pac_opaque, pac_opaque_len);
+
+	buf = os_malloc(pac_opaque_len - 8);
+	if (buf == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory "
+			   "for decrypting PAC-Opaque");
+		return 0;
+	}
+
+	if (aes_unwrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr),
+		       (pac_opaque_len - 8) / 8, pac_opaque, buf) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to decrypt "
+			   "PAC-Opaque");
+		os_free(buf);
+		/*
+		 * This may have been caused by server changing the PAC-Opaque
+		 * encryption key, so just ignore this PAC-Opaque instead of
+		 * failing the authentication completely. Provisioning can now
+		 * be used to provision a new PAC.
+		 */
+		return 0;
+	}
+
+	end = buf + pac_opaque_len - 8;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Decrypted PAC-Opaque",
+			buf, end - buf);
+
+	pos = buf;
+	while (pos + 1 < end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+
+		switch (*pos) {
+		case PAC_OPAQUE_TYPE_PAD:
+			goto done;
+		case PAC_OPAQUE_TYPE_KEY:
+			if (pos[1] != EAP_FAST_PAC_KEY_LEN) {
+				wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
+					   "PAC-Key length %d", pos[1]);
+				os_free(buf);
+				return -1;
+			}
+			pac_key = pos + 2;
+			wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key from "
+					"decrypted PAC-Opaque",
+					pac_key, EAP_FAST_PAC_KEY_LEN);
+			break;
+		case PAC_OPAQUE_TYPE_LIFETIME:
+			if (pos[1] != 4) {
+				wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
+					   "PAC-Key lifetime length %d",
+					   pos[1]);
+				os_free(buf);
+				return -1;
+			}
+			lifetime = WPA_GET_BE32(pos + 2);
+			break;
+		case PAC_OPAQUE_TYPE_IDENTITY:
+			identity = pos + 2;
+			identity_len = pos[1];
+			break;
+		}
+
+		pos += 2 + pos[1];
+	}
+done:
+
+	if (pac_key == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key included in "
+			   "PAC-Opaque");
+		os_free(buf);
+		return -1;
+	}
+
+	if (identity) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: Identity from "
+				  "PAC-Opaque", identity, identity_len);
+		os_free(data->identity);
+		data->identity = os_malloc(identity_len);
+		if (data->identity) {
+			os_memcpy(data->identity, identity, identity_len);
+			data->identity_len = identity_len;
+		}
+	}
+
+	if (os_get_time(&now) < 0 || lifetime <= 0 || now.sec > lifetime) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Key not valid anymore "
+			   "(lifetime=%ld now=%ld)", lifetime, now.sec);
+		data->send_new_pac = 2;
+		/*
+		 * Allow PAC to be used to allow a PAC update with some level
+		 * of server authentication (i.e., do not fall back to full TLS
+		 * handshake since we cannot be sure that the peer would be
+		 * able to validate server certificate now). However, reject
+		 * the authentication since the PAC was not valid anymore. Peer
+		 * can connect again with the newly provisioned PAC after this.
+		 */
+	} else if (lifetime - now.sec < data->pac_key_refresh_time) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Key soft timeout; send "
+			   "an update if authentication succeeds");
+		data->send_new_pac = 1;
+	}
+
+	eap_fast_derive_master_secret(pac_key, server_random, client_random,
+				      master_secret);
+
+	os_free(buf);
+
+	return 1;
+}
+
+
+static void eap_fast_derive_key_auth(struct eap_sm *sm,
+				     struct eap_fast_data *data)
+{
+	u8 *sks;
+
+	/* RFC 4851, Section 5.1:
+	 * Extra key material after TLS key_block: session_key_seed[40]
+	 */
+
+	sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion",
+				  EAP_FAST_SKS_LEN);
+	if (sks == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
+			   "session_key_seed");
+		return;
+	}
+
+	/*
+	 * RFC 4851, Section 5.2:
+	 * S-IMCK[0] = session_key_seed
+	 */
+	wpa_hexdump_key(MSG_DEBUG,
+			"EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+			sks, EAP_FAST_SKS_LEN);
+	data->simck_idx = 0;
+	os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN);
+	os_free(sks);
+}
+
+
+static void eap_fast_derive_key_provisioning(struct eap_sm *sm,
+					     struct eap_fast_data *data)
+{
+	os_free(data->key_block_p);
+	data->key_block_p = (struct eap_fast_key_block_provisioning *)
+		eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
+				    "key expansion",
+				    sizeof(*data->key_block_p));
+	if (data->key_block_p == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
+		return;
+	}
+	/*
+	 * RFC 4851, Section 5.2:
+	 * S-IMCK[0] = session_key_seed
+	 */
+	wpa_hexdump_key(MSG_DEBUG,
+			"EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+			data->key_block_p->session_key_seed,
+			sizeof(data->key_block_p->session_key_seed));
+	data->simck_idx = 0;
+	os_memcpy(data->simck, data->key_block_p->session_key_seed,
+		  EAP_FAST_SIMCK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge",
+			data->key_block_p->server_challenge,
+			sizeof(data->key_block_p->server_challenge));
+	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge",
+			data->key_block_p->client_challenge,
+			sizeof(data->key_block_p->client_challenge));
+}
+
+
+static int eap_fast_get_phase2_key(struct eap_sm *sm,
+				   struct eap_fast_data *data,
+				   u8 *isk, size_t isk_len)
+{
+	u8 *key;
+	size_t key_len;
+
+	os_memset(isk, 0, isk_len);
+
+	if (data->phase2_method == NULL || data->phase2_priv == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not "
+			   "available");
+		return -1;
+	}
+
+	if (data->phase2_method->getKey == NULL)
+		return 0;
+
+	if ((key = data->phase2_method->getKey(sm, data->phase2_priv,
+					       &key_len)) == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material "
+			   "from Phase 2");
+		return -1;
+	}
+
+	if (key_len > isk_len)
+		key_len = isk_len;
+	if (key_len == 32 &&
+	    data->phase2_method->vendor == EAP_VENDOR_IETF &&
+	    data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
+		/*
+		 * EAP-FAST uses reverse order for MS-MPPE keys when deriving
+		 * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
+		 * ISK for EAP-FAST cryptobinding.
+		 */
+		os_memcpy(isk, key + 16, 16);
+		os_memcpy(isk + 16, key, 16);
+	} else
+		os_memcpy(isk, key, key_len);
+	os_free(key);
+
+	return 0;
+}
+
+
+static int eap_fast_update_icmk(struct eap_sm *sm, struct eap_fast_data *data)
+{
+	u8 isk[32], imck[60];
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Deriving ICMK[%d] (S-IMCK and CMK)",
+		   data->simck_idx + 1);
+
+	/*
+	 * RFC 4851, Section 5.2:
+	 * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys",
+	 *                 MSK[j], 60)
+	 * S-IMCK[j] = first 40 octets of IMCK[j]
+	 * CMK[j] = last 20 octets of IMCK[j]
+	 */
+
+	if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk));
+	sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
+		   "Inner Methods Compound Keys",
+		   isk, sizeof(isk), imck, sizeof(imck));
+	data->simck_idx++;
+	os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]",
+			data->simck, EAP_FAST_SIMCK_LEN);
+	os_memcpy(data->cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]",
+			data->cmk, EAP_FAST_CMK_LEN);
+
+	return 0;
+}
+
+
+static void * eap_fast_init(struct eap_sm *sm)
+{
+	struct eap_fast_data *data;
+	u8 ciphers[5] = {
+		TLS_CIPHER_ANON_DH_AES128_SHA,
+		TLS_CIPHER_AES128_SHA,
+		TLS_CIPHER_RSA_DHE_AES128_SHA,
+		TLS_CIPHER_RC4_SHA,
+		TLS_CIPHER_NONE
+	};
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->fast_version = EAP_FAST_VERSION;
+	data->force_version = -1;
+	if (sm->user && sm->user->force_version >= 0) {
+		data->force_version = sm->user->force_version;
+		wpa_printf(MSG_DEBUG, "EAP-FAST: forcing version %d",
+			   data->force_version);
+		data->fast_version = data->force_version;
+	}
+	data->state = START;
+
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_FAST)) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
+		eap_fast_reset(sm, data);
+		return NULL;
+	}
+
+	if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn,
+					   ciphers) < 0) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to set TLS cipher "
+			   "suites");
+		eap_fast_reset(sm, data);
+		return NULL;
+	}
+
+	if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
+						 eap_fast_session_ticket_cb,
+						 data) < 0) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket "
+			   "callback");
+		eap_fast_reset(sm, data);
+		return NULL;
+	}
+
+	if (sm->pac_opaque_encr_key == NULL) {
+		wpa_printf(MSG_INFO, "EAP-FAST: No PAC-Opaque encryption key "
+			   "configured");
+		eap_fast_reset(sm, data);
+		return NULL;
+	}
+	os_memcpy(data->pac_opaque_encr, sm->pac_opaque_encr_key,
+		  sizeof(data->pac_opaque_encr));
+
+	if (sm->eap_fast_a_id == NULL) {
+		wpa_printf(MSG_INFO, "EAP-FAST: No A-ID configured");
+		eap_fast_reset(sm, data);
+		return NULL;
+	}
+	data->srv_id = os_malloc(sm->eap_fast_a_id_len);
+	if (data->srv_id == NULL) {
+		eap_fast_reset(sm, data);
+		return NULL;
+	}
+	os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len);
+	data->srv_id_len = sm->eap_fast_a_id_len;
+
+	if (sm->eap_fast_a_id_info == NULL) {
+		wpa_printf(MSG_INFO, "EAP-FAST: No A-ID-Info configured");
+		eap_fast_reset(sm, data);
+		return NULL;
+	}
+	data->srv_id_info = os_strdup(sm->eap_fast_a_id_info);
+	if (data->srv_id_info == NULL) {
+		eap_fast_reset(sm, data);
+		return NULL;
+	}
+
+	/* PAC-Key lifetime in seconds (hard limit) */
+	data->pac_key_lifetime = sm->pac_key_lifetime;
+
+	/*
+	 * PAC-Key refresh time in seconds (soft limit on remaining hard
+	 * limit). The server will generate a new PAC-Key when this number of
+	 * seconds (or fewer) of the lifetime remains.
+	 */
+	data->pac_key_refresh_time = sm->pac_key_refresh_time;
+
+	return data;
+}
+
+
+static void eap_fast_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_fast_data *data = priv;
+	if (data == NULL)
+		return;
+	if (data->phase2_priv && data->phase2_method)
+		data->phase2_method->reset(sm, data->phase2_priv);
+	eap_server_tls_ssl_deinit(sm, &data->ssl);
+	os_free(data->srv_id);
+	os_free(data->srv_id_info);
+	os_free(data->key_block_p);
+	wpabuf_free(data->pending_phase2_resp);
+	os_free(data->identity);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_fast_build_start(struct eap_sm *sm,
+					    struct eap_fast_data *data, u8 id)
+{
+	struct wpabuf *req;
+
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_FAST,
+			    1 + sizeof(struct pac_tlv_hdr) + data->srv_id_len,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-FAST: Failed to allocate memory for"
+			   " request");
+		eap_fast_state(data, FAILURE);
+		return NULL;
+	}
+
+	wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->fast_version);
+
+	/* RFC 4851, 4.1.1. Authority ID Data */
+	eap_fast_put_tlv(req, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len);
+
+	eap_fast_state(data, PHASE1);
+
+	return req;
+}
+
+
+static int eap_fast_phase1_done(struct eap_sm *sm, struct eap_fast_data *data)
+{
+	char cipher[64];
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Phase1 done, starting Phase2");
+
+	if (tls_get_cipher(sm->ssl_ctx, data->ssl.conn, cipher, sizeof(cipher))
+	    < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to get cipher "
+			   "information");
+		eap_fast_state(data, FAILURE);
+		return -1;
+	}
+	data->anon_provisioning = os_strstr(cipher, "ADH") != NULL;
+		    
+	if (data->anon_provisioning) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Anonymous provisioning");
+		eap_fast_derive_key_provisioning(sm, data);
+	} else
+		eap_fast_derive_key_auth(sm, data);
+
+	eap_fast_state(data, PHASE2_START);
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_fast_build_phase2_req(struct eap_sm *sm,
+						 struct eap_fast_data *data,
+						 u8 id)
+{
+	struct wpabuf *req;
+
+	if (data->phase2_priv == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not "
+			   "initialized");
+		return NULL;
+	}
+	req = data->phase2_method->buildReq(sm, data->phase2_priv, id);
+	if (req == NULL)
+		return NULL;
+
+	wpa_hexdump_buf_key(MSG_MSGDUMP, "EAP-FAST: Phase 2 EAP-Request", req);
+	return eap_fast_tlv_eap_payload(req);
+}
+
+
+static struct wpabuf * eap_fast_build_crypto_binding(
+	struct eap_sm *sm, struct eap_fast_data *data)
+{
+	struct wpabuf *buf;
+	struct eap_tlv_result_tlv *result;
+	struct eap_tlv_crypto_binding_tlv *binding;
+
+	buf = wpabuf_alloc(2 * sizeof(*result) + sizeof(*binding));
+	if (buf == NULL)
+		return NULL;
+
+	if (data->send_new_pac || data->anon_provisioning ||
+	    data->phase2_method)
+		data->final_result = 0;
+	else
+		data->final_result = 1;
+
+	if (!data->final_result || data->eap_seq > 1) {
+		/* Intermediate-Result */
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Add Intermediate-Result TLV "
+			   "(status=SUCCESS)");
+		result = wpabuf_put(buf, sizeof(*result));
+		result->tlv_type = host_to_be16(
+			EAP_TLV_TYPE_MANDATORY |
+			EAP_TLV_INTERMEDIATE_RESULT_TLV);
+		result->length = host_to_be16(2);
+		result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+	}
+
+	if (data->final_result) {
+		/* Result TLV */
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV "
+			   "(status=SUCCESS)");
+		result = wpabuf_put(buf, sizeof(*result));
+		result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+						EAP_TLV_RESULT_TLV);
+		result->length = host_to_be16(2);
+		result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+	}
+
+	/* Crypto-Binding TLV */
+	binding = wpabuf_put(buf, sizeof(*binding));
+	binding->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+					 EAP_TLV_CRYPTO_BINDING_TLV);
+	binding->length = host_to_be16(sizeof(*binding) -
+				       sizeof(struct eap_tlv_hdr));
+	binding->version = EAP_FAST_VERSION;
+	binding->received_version = data->peer_version;
+	binding->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST;
+	if (random_get_bytes(binding->nonce, sizeof(binding->nonce)) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	/*
+	 * RFC 4851, Section 4.2.8:
+	 * The nonce in a request MUST have its least significant bit set to 0.
+	 */
+	binding->nonce[sizeof(binding->nonce) - 1] &= ~0x01;
+
+	os_memcpy(data->crypto_binding_nonce, binding->nonce,
+		  sizeof(binding->nonce));
+
+	/*
+	 * RFC 4851, Section 5.3:
+	 * CMK = CMK[j]
+	 * Compound-MAC = HMAC-SHA1( CMK, Crypto-Binding TLV )
+	 */
+
+	hmac_sha1(data->cmk, EAP_FAST_CMK_LEN,
+		  (u8 *) binding, sizeof(*binding),
+		  binding->compound_mac);
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Add Crypto-Binding TLV: Version %d "
+		   "Received Version %d SubType %d",
+		   binding->version, binding->received_version,
+		   binding->subtype);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+		    binding->nonce, sizeof(binding->nonce));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+		    binding->compound_mac, sizeof(binding->compound_mac));
+
+	return buf;
+}
+
+
+static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm,
+					  struct eap_fast_data *data)
+{
+	u8 pac_key[EAP_FAST_PAC_KEY_LEN];
+	u8 *pac_buf, *pac_opaque;
+	struct wpabuf *buf;
+	u8 *pos;
+	size_t buf_len, srv_id_info_len, pac_len;
+	struct eap_tlv_hdr *pac_tlv;
+	struct pac_tlv_hdr *pac_info;
+	struct eap_tlv_result_tlv *result;
+	struct os_time now;
+
+	if (random_get_bytes(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 ||
+	    os_get_time(&now) < 0)
+		return NULL;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Generated PAC-Key",
+			pac_key, EAP_FAST_PAC_KEY_LEN);
+
+	pac_len = (2 + EAP_FAST_PAC_KEY_LEN) + (2 + 4) +
+		(2 + sm->identity_len) + 8;
+	pac_buf = os_malloc(pac_len);
+	if (pac_buf == NULL)
+		return NULL;
+
+	srv_id_info_len = os_strlen(data->srv_id_info);
+
+	pos = pac_buf;
+	*pos++ = PAC_OPAQUE_TYPE_KEY;
+	*pos++ = EAP_FAST_PAC_KEY_LEN;
+	os_memcpy(pos, pac_key, EAP_FAST_PAC_KEY_LEN);
+	pos += EAP_FAST_PAC_KEY_LEN;
+
+	*pos++ = PAC_OPAQUE_TYPE_LIFETIME;
+	*pos++ = 4;
+	WPA_PUT_BE32(pos, now.sec + data->pac_key_lifetime);
+	pos += 4;
+
+	if (sm->identity) {
+		*pos++ = PAC_OPAQUE_TYPE_IDENTITY;
+		*pos++ = sm->identity_len;
+		os_memcpy(pos, sm->identity, sm->identity_len);
+		pos += sm->identity_len;
+	}
+
+	pac_len = pos - pac_buf;
+	while (pac_len % 8) {
+		*pos++ = PAC_OPAQUE_TYPE_PAD;
+		pac_len++;
+	}
+
+	pac_opaque = os_malloc(pac_len + 8);
+	if (pac_opaque == NULL) {
+		os_free(pac_buf);
+		return NULL;
+	}
+	if (aes_wrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr),
+		     pac_len / 8, pac_buf, pac_opaque) < 0) {
+		os_free(pac_buf);
+		os_free(pac_opaque);
+		return NULL;
+	}
+	os_free(pac_buf);
+
+	pac_len += 8;
+	wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque",
+		    pac_opaque, pac_len);
+
+	buf_len = sizeof(*pac_tlv) +
+		sizeof(struct pac_tlv_hdr) + EAP_FAST_PAC_KEY_LEN +
+		sizeof(struct pac_tlv_hdr) + pac_len +
+		data->srv_id_len + srv_id_info_len + 100 + sizeof(*result);
+	buf = wpabuf_alloc(buf_len);
+	if (buf == NULL) {
+		os_free(pac_opaque);
+		return NULL;
+	}
+
+	/* Result TLV */
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV (status=SUCCESS)");
+	result = wpabuf_put(buf, sizeof(*result));
+	WPA_PUT_BE16((u8 *) &result->tlv_type,
+		     EAP_TLV_TYPE_MANDATORY | EAP_TLV_RESULT_TLV);
+	WPA_PUT_BE16((u8 *) &result->length, 2);
+	WPA_PUT_BE16((u8 *) &result->status, EAP_TLV_RESULT_SUCCESS);
+
+	/* PAC TLV */
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV");
+	pac_tlv = wpabuf_put(buf, sizeof(*pac_tlv));
+	pac_tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+					 EAP_TLV_PAC_TLV);
+
+	/* PAC-Key */
+	eap_fast_put_tlv(buf, PAC_TYPE_PAC_KEY, pac_key, EAP_FAST_PAC_KEY_LEN);
+
+	/* PAC-Opaque */
+	eap_fast_put_tlv(buf, PAC_TYPE_PAC_OPAQUE, pac_opaque, pac_len);
+	os_free(pac_opaque);
+
+	/* PAC-Info */
+	pac_info = wpabuf_put(buf, sizeof(*pac_info));
+	pac_info->type = host_to_be16(PAC_TYPE_PAC_INFO);
+
+	/* PAC-Lifetime (inside PAC-Info) */
+	eap_fast_put_tlv_hdr(buf, PAC_TYPE_CRED_LIFETIME, 4);
+	wpabuf_put_be32(buf, now.sec + data->pac_key_lifetime);
+
+	/* A-ID (inside PAC-Info) */
+	eap_fast_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len);
+	
+	/* Note: headers may be misaligned after A-ID */
+
+	if (sm->identity) {
+		eap_fast_put_tlv(buf, PAC_TYPE_I_ID, sm->identity,
+				 sm->identity_len);
+	}
+
+	/* A-ID-Info (inside PAC-Info) */
+	eap_fast_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info,
+			 srv_id_info_len);
+
+	/* PAC-Type (inside PAC-Info) */
+	eap_fast_put_tlv_hdr(buf, PAC_TYPE_PAC_TYPE, 2);
+	wpabuf_put_be16(buf, PAC_TYPE_TUNNEL_PAC);
+
+	/* Update PAC-Info and PAC TLV Length fields */
+	pos = wpabuf_put(buf, 0);
+	pac_info->len = host_to_be16(pos - (u8 *) (pac_info + 1));
+	pac_tlv->length = host_to_be16(pos - (u8 *) (pac_tlv + 1));
+
+	return buf;
+}
+
+
+static int eap_fast_encrypt_phase2(struct eap_sm *sm,
+				   struct eap_fast_data *data,
+				   struct wpabuf *plain, int piggyback)
+{
+	struct wpabuf *encr;
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 TLVs",
+			    plain);
+	encr = eap_server_tls_encrypt(sm, &data->ssl, plain);
+	wpabuf_free(plain);
+
+	if (!encr)
+		return -1;
+
+	if (data->ssl.tls_out && piggyback) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Piggyback Phase 2 data "
+			   "(len=%d) with last Phase 1 Message (len=%d "
+			   "used=%d)",
+			   (int) wpabuf_len(encr),
+			   (int) wpabuf_len(data->ssl.tls_out),
+			   (int) data->ssl.tls_out_pos);
+		if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) {
+			wpa_printf(MSG_WARNING, "EAP-FAST: Failed to resize "
+				   "output buffer");
+			wpabuf_free(encr);
+			return -1;
+		}
+		wpabuf_put_buf(data->ssl.tls_out, encr);
+		wpabuf_free(encr);
+	} else {
+		wpabuf_free(data->ssl.tls_out);
+		data->ssl.tls_out_pos = 0;
+		data->ssl.tls_out = encr;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_fast_data *data = priv;
+	struct wpabuf *req = NULL;
+	int piggyback = 0;
+
+	if (data->ssl.state == FRAG_ACK) {
+		return eap_server_tls_build_ack(id, EAP_TYPE_FAST,
+						data->fast_version);
+	}
+
+	if (data->ssl.state == WAIT_FRAG_ACK) {
+		return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST,
+						data->fast_version, id);
+	}
+
+	switch (data->state) {
+	case START:
+		return eap_fast_build_start(sm, data, id);
+	case PHASE1:
+		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+			if (eap_fast_phase1_done(sm, data) < 0)
+				return NULL;
+			if (data->state == PHASE2_START) {
+				/*
+				 * Try to generate Phase 2 data to piggyback
+				 * with the end of Phase 1 to avoid extra
+				 * roundtrip.
+				 */
+				wpa_printf(MSG_DEBUG, "EAP-FAST: Try to start "
+					   "Phase 2");
+				if (eap_fast_process_phase2_start(sm, data))
+					break;
+				req = eap_fast_build_phase2_req(sm, data, id);
+				piggyback = 1;
+			}
+		}
+		break;
+	case PHASE2_ID:
+	case PHASE2_METHOD:
+		req = eap_fast_build_phase2_req(sm, data, id);
+		break;
+	case CRYPTO_BINDING:
+		req = eap_fast_build_crypto_binding(sm, data);
+		if (data->phase2_method) {
+			/*
+			 * Include the start of the next EAP method in the
+			 * sequence in the same message with Crypto-Binding to
+			 * save a round-trip.
+			 */
+			struct wpabuf *eap;
+			eap = eap_fast_build_phase2_req(sm, data, id);
+			req = wpabuf_concat(req, eap);
+			eap_fast_state(data, PHASE2_METHOD);
+		}
+		break;
+	case REQUEST_PAC:
+		req = eap_fast_build_pac(sm, data);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-FAST: %s - unexpected state %d",
+			   __func__, data->state);
+		return NULL;
+	}
+
+	if (req &&
+	    eap_fast_encrypt_phase2(sm, data, req, piggyback) < 0)
+		return NULL;
+
+	return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST,
+					data->fast_version, id);
+}
+
+
+static Boolean eap_fast_check(struct eap_sm *sm, void *priv,
+			      struct wpabuf *respData)
+{
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_FAST, respData, &len);
+	if (pos == NULL || len < 1) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Invalid frame");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static int eap_fast_phase2_init(struct eap_sm *sm, struct eap_fast_data *data,
+				EapType eap_type)
+{
+	if (data->phase2_priv && data->phase2_method) {
+		data->phase2_method->reset(sm, data->phase2_priv);
+		data->phase2_method = NULL;
+		data->phase2_priv = NULL;
+	}
+	data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF,
+							eap_type);
+	if (!data->phase2_method)
+		return -1;
+
+	if (data->key_block_p) {
+		sm->auth_challenge = data->key_block_p->server_challenge;
+		sm->peer_challenge = data->key_block_p->client_challenge;
+	}
+	sm->init_phase2 = 1;
+	data->phase2_priv = data->phase2_method->init(sm);
+	sm->init_phase2 = 0;
+	sm->auth_challenge = NULL;
+	sm->peer_challenge = NULL;
+
+	return data->phase2_priv == NULL ? -1 : 0;
+}
+
+
+static void eap_fast_process_phase2_response(struct eap_sm *sm,
+					     struct eap_fast_data *data,
+					     u8 *in_data, size_t in_len)
+{
+	u8 next_type = EAP_TYPE_NONE;
+	struct eap_hdr *hdr;
+	u8 *pos;
+	size_t left;
+	struct wpabuf buf;
+	const struct eap_method *m = data->phase2_method;
+	void *priv = data->phase2_priv;
+
+	if (priv == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: %s - Phase2 not "
+			   "initialized?!", __func__);
+		return;
+	}
+
+	hdr = (struct eap_hdr *) in_data;
+	pos = (u8 *) (hdr + 1);
+
+	if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
+		left = in_len - sizeof(*hdr);
+		wpa_hexdump(MSG_DEBUG, "EAP-FAST: Phase2 type Nak'ed; "
+			    "allowed types", pos + 1, left - 1);
+#ifdef EAP_SERVER_TNC
+		if (m && m->vendor == EAP_VENDOR_IETF &&
+		    m->method == EAP_TYPE_TNC) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Peer Nak'ed required "
+				   "TNC negotiation");
+			next_type = eap_fast_req_failure(sm, data);
+			eap_fast_phase2_init(sm, data, next_type);
+			return;
+		}
+#endif /* EAP_SERVER_TNC */
+		eap_sm_process_nak(sm, pos + 1, left - 1);
+		if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+		    sm->user->methods[sm->user_eap_method_index].method !=
+		    EAP_TYPE_NONE) {
+			next_type = sm->user->methods[
+				sm->user_eap_method_index++].method;
+			wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d",
+				   next_type);
+		} else {
+			next_type = eap_fast_req_failure(sm, data);
+		}
+		eap_fast_phase2_init(sm, data, next_type);
+		return;
+	}
+
+	wpabuf_set(&buf, in_data, in_len);
+
+	if (m->check(sm, priv, &buf)) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 check() asked to "
+			   "ignore the packet");
+		eap_fast_req_failure(sm, data);
+		return;
+	}
+
+	m->process(sm, priv, &buf);
+
+	if (!m->isDone(sm, priv))
+		return;
+
+	if (!m->isSuccess(sm, priv)) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method failed");
+		next_type = eap_fast_req_failure(sm, data);
+		eap_fast_phase2_init(sm, data, next_type);
+		return;
+	}
+
+	switch (data->state) {
+	case PHASE2_ID:
+		if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+			wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: Phase2 "
+					  "Identity not found in the user "
+					  "database",
+					  sm->identity, sm->identity_len);
+			next_type = eap_fast_req_failure(sm, data);
+			break;
+		}
+
+		eap_fast_state(data, PHASE2_METHOD);
+		if (data->anon_provisioning) {
+			/*
+			 * Only EAP-MSCHAPv2 is allowed for anonymous
+			 * provisioning.
+			 */
+			next_type = EAP_TYPE_MSCHAPV2;
+			sm->user_eap_method_index = 0;
+		} else {
+			next_type = sm->user->methods[0].method;
+			sm->user_eap_method_index = 1;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", next_type);
+		break;
+	case PHASE2_METHOD:
+	case CRYPTO_BINDING:
+		eap_fast_update_icmk(sm, data);
+		eap_fast_state(data, CRYPTO_BINDING);
+		data->eap_seq++;
+		next_type = EAP_TYPE_NONE;
+#ifdef EAP_SERVER_TNC
+		if (sm->tnc && !data->tnc_started) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Initialize TNC");
+			next_type = EAP_TYPE_TNC;
+			data->tnc_started = 1;
+		}
+#endif /* EAP_SERVER_TNC */
+		break;
+	case FAILURE:
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-FAST: %s - unexpected state %d",
+			   __func__, data->state);
+		break;
+	}
+
+	eap_fast_phase2_init(sm, data, next_type);
+}
+
+
+static void eap_fast_process_phase2_eap(struct eap_sm *sm,
+					struct eap_fast_data *data,
+					u8 *in_data, size_t in_len)
+{
+	struct eap_hdr *hdr;
+	size_t len;
+
+	hdr = (struct eap_hdr *) in_data;
+	if (in_len < (int) sizeof(*hdr)) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 "
+			   "EAP frame (len=%lu)", (unsigned long) in_len);
+		eap_fast_req_failure(sm, data);
+		return;
+	}
+	len = be_to_host16(hdr->length);
+	if (len > in_len) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Length mismatch in "
+			   "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
+			   (unsigned long) in_len, (unsigned long) len);
+		eap_fast_req_failure(sm, data);
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: code=%d "
+		   "identifier=%d length=%lu", hdr->code, hdr->identifier,
+		   (unsigned long) len);
+	switch (hdr->code) {
+	case EAP_CODE_RESPONSE:
+		eap_fast_process_phase2_response(sm, data, (u8 *) hdr, len);
+		break;
+	default:
+		wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in "
+			   "Phase 2 EAP header", hdr->code);
+		break;
+	}
+}
+
+
+static int eap_fast_parse_tlvs(struct wpabuf *data,
+			       struct eap_fast_tlv_parse *tlv)
+{
+	int mandatory, tlv_type, res;
+	size_t len;
+	u8 *pos, *end;
+
+	os_memset(tlv, 0, sizeof(*tlv));
+
+	pos = wpabuf_mhead(data);
+	end = pos + wpabuf_len(data);
+	while (pos + 4 < end) {
+		mandatory = pos[0] & 0x80;
+		tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+		pos += 2;
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (len > (size_t) (end - pos)) {
+			wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: "
+			   "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)
+			break;
+		if (res < 0) {
+			if (mandatory) {
+				wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown "
+					   "mandatory TLV type %d", tlv_type);
+				/* TODO: generate Nak TLV */
+				break;
+			} else {
+				wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored "
+					   "unknown optional TLV type %d",
+					   tlv_type);
+			}
+		}
+
+		pos += len;
+	}
+
+	return 0;
+}
+
+
+static int eap_fast_validate_crypto_binding(
+	struct eap_fast_data *data, struct eap_tlv_crypto_binding_tlv *b,
+	size_t bind_len)
+{
+	u8 cmac[SHA1_MAC_LEN];
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: "
+		   "Version %d Received Version %d SubType %d",
+		   b->version, b->received_version, b->subtype);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+		    b->nonce, sizeof(b->nonce));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+		    b->compound_mac, sizeof(b->compound_mac));
+
+	if (b->version != EAP_FAST_VERSION ||
+	    b->received_version != EAP_FAST_VERSION) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected version "
+			   "in Crypto-Binding: version %d "
+			   "received_version %d", b->version,
+			   b->received_version);
+		return -1;
+	}
+
+	if (b->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected subtype in "
+			   "Crypto-Binding: %d", b->subtype);
+		return -1;
+	}
+
+	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");
+		return -1;
+	}
+
+	os_memcpy(cmac, b->compound_mac, sizeof(cmac));
+	os_memset(b->compound_mac, 0, sizeof(cmac));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for "
+		    "Compound MAC calculation",
+		    (u8 *) b, bind_len);
+	hmac_sha1(data->cmk, EAP_FAST_CMK_LEN, (u8 *) b, bind_len,
+		  b->compound_mac);
+	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));
+		wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not "
+			   "match");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eap_fast_pac_type(u8 *pac, size_t len, u16 type)
+{
+	struct eap_tlv_pac_type_tlv *tlv;
+
+	if (pac == NULL || len != sizeof(*tlv))
+		return 0;
+
+	tlv = (struct eap_tlv_pac_type_tlv *) pac;
+
+	return be_to_host16(tlv->tlv_type) == PAC_TYPE_PAC_TYPE &&
+		be_to_host16(tlv->length) == 2 &&
+		be_to_host16(tlv->pac_type) == type;
+}
+
+
+static void eap_fast_process_phase2_tlvs(struct eap_sm *sm,
+					 struct eap_fast_data *data,
+					 struct wpabuf *in_data)
+{
+	struct eap_fast_tlv_parse tlv;
+	int check_crypto_binding = data->state == CRYPTO_BINDING;
+
+	if (eap_fast_parse_tlvs(in_data, &tlv) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to parse received "
+			   "Phase 2 TLVs");
+		return;
+	}
+
+	if (tlv.result == EAP_TLV_RESULT_FAILURE) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Result TLV indicated "
+			   "failure");
+		eap_fast_state(data, FAILURE);
+		return;
+	}
+
+	if (data->state == REQUEST_PAC) {
+		u16 type, len, res;
+		if (tlv.pac == NULL || tlv.pac_len < 6) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC "
+				   "Acknowledgement received");
+			eap_fast_state(data, FAILURE);
+			return;
+		}
+
+		type = WPA_GET_BE16(tlv.pac);
+		len = WPA_GET_BE16(tlv.pac + 2);
+		res = WPA_GET_BE16(tlv.pac + 4);
+
+		if (type != PAC_TYPE_PAC_ACKNOWLEDGEMENT || len != 2 ||
+		    res != EAP_TLV_RESULT_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV did not "
+				   "contain acknowledgement");
+			eap_fast_state(data, FAILURE);
+			return;
+		}
+
+		wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Acknowledgement received "
+			   "- PAC provisioning succeeded");
+		eap_fast_state(data, (data->anon_provisioning ||
+				      data->send_new_pac == 2) ?
+			       FAILURE : SUCCESS);
+		return;
+	}
+
+	if (check_crypto_binding) {
+		if (tlv.crypto_binding == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: No Crypto-Binding "
+				   "TLV received");
+			eap_fast_state(data, FAILURE);
+			return;
+		}
+
+		if (data->final_result &&
+		    tlv.result != EAP_TLV_RESULT_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV "
+				   "without Success Result");
+			eap_fast_state(data, FAILURE);
+			return;
+		}
+
+		if (!data->final_result &&
+		    tlv.iresult != EAP_TLV_RESULT_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV "
+				   "without intermediate Success Result");
+			eap_fast_state(data, FAILURE);
+			return;
+		}
+
+		if (eap_fast_validate_crypto_binding(data, tlv.crypto_binding,
+						     tlv.crypto_binding_len)) {
+			eap_fast_state(data, FAILURE);
+			return;
+		}
+
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Valid Crypto-Binding TLV "
+			   "received");
+		if (data->final_result) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
+				   "completed successfully");
+		}
+
+		if (data->anon_provisioning &&
+		    sm->eap_fast_prov != ANON_PROV &&
+		    sm->eap_fast_prov != BOTH_PROV) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to "
+				   "use unauthenticated provisioning which is "
+				   "disabled");
+			eap_fast_state(data, FAILURE);
+			return;
+		}
+
+		if (sm->eap_fast_prov != AUTH_PROV &&
+		    sm->eap_fast_prov != BOTH_PROV &&
+		    tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV &&
+		    eap_fast_pac_type(tlv.pac, tlv.pac_len,
+				      PAC_TYPE_TUNNEL_PAC)) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to "
+				   "use authenticated provisioning which is "
+				   "disabled");
+			eap_fast_state(data, FAILURE);
+			return;
+		}
+
+		if (data->anon_provisioning ||
+		    (tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV &&
+		     eap_fast_pac_type(tlv.pac, tlv.pac_len,
+				       PAC_TYPE_TUNNEL_PAC))) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Requested a new "
+				   "Tunnel PAC");
+			eap_fast_state(data, REQUEST_PAC);
+		} else if (data->send_new_pac) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Server triggered "
+				   "re-keying of Tunnel PAC");
+			eap_fast_state(data, REQUEST_PAC);
+		} else if (data->final_result)
+			eap_fast_state(data, SUCCESS);
+	}
+
+	if (tlv.eap_payload_tlv) {
+		eap_fast_process_phase2_eap(sm, data, tlv.eap_payload_tlv,
+					    tlv.eap_payload_tlv_len);
+	}
+}
+
+
+static void eap_fast_process_phase2(struct eap_sm *sm,
+				    struct eap_fast_data *data,
+				    struct wpabuf *in_buf)
+{
+	struct wpabuf *in_decrypted;
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for"
+		   " Phase 2", (unsigned long) wpabuf_len(in_buf));
+
+	if (data->pending_phase2_resp) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - "
+			   "skip decryption and use old data");
+		eap_fast_process_phase2_tlvs(sm, data,
+					     data->pending_phase2_resp);
+		wpabuf_free(data->pending_phase2_resp);
+		data->pending_phase2_resp = NULL;
+		return;
+	}
+
+	in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+					      in_buf);
+	if (in_decrypted == NULL) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 "
+			   "data");
+		eap_fast_state(data, FAILURE);
+		return;
+	}
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Decrypted Phase 2 TLVs",
+			    in_decrypted);
+
+	eap_fast_process_phase2_tlvs(sm, data, in_decrypted);
+
+	if (sm->method_pending == METHOD_PENDING_WAIT) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method is in "
+			   "pending wait state - save decrypted response");
+		wpabuf_free(data->pending_phase2_resp);
+		data->pending_phase2_resp = in_decrypted;
+		return;
+	}
+
+	wpabuf_free(in_decrypted);
+}
+
+
+static int eap_fast_process_version(struct eap_sm *sm, void *priv,
+				    int peer_version)
+{
+	struct eap_fast_data *data = priv;
+
+	data->peer_version = peer_version;
+
+	if (data->force_version >= 0 && peer_version != data->force_version) {
+		wpa_printf(MSG_INFO, "EAP-FAST: peer did not select the forced"
+			   " version (forced=%d peer=%d) - reject",
+			   data->force_version, peer_version);
+		return -1;
+	}
+
+	if (peer_version < data->fast_version) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: peer ver=%d, own ver=%d; "
+			   "use version %d",
+			   peer_version, data->fast_version, peer_version);
+		data->fast_version = peer_version;
+	}
+
+	return 0;
+}
+
+
+static int eap_fast_process_phase1(struct eap_sm *sm,
+				   struct eap_fast_data *data)
+{
+	if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
+		wpa_printf(MSG_INFO, "EAP-FAST: TLS processing failed");
+		eap_fast_state(data, FAILURE);
+		return -1;
+	}
+
+	if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+	    wpabuf_len(data->ssl.tls_out) > 0)
+		return 1;
+
+	/*
+	 * Phase 1 was completed with the received message (e.g., when using
+	 * abbreviated handshake), so Phase 2 can be started immediately
+	 * without having to send through an empty message to the peer.
+	 */
+
+	return eap_fast_phase1_done(sm, data);
+}
+
+
+static int eap_fast_process_phase2_start(struct eap_sm *sm,
+					 struct eap_fast_data *data)
+{
+	u8 next_type;
+
+	if (data->identity) {
+		os_free(sm->identity);
+		sm->identity = data->identity;
+		data->identity = NULL;
+		sm->identity_len = data->identity_len;
+		data->identity_len = 0;
+		sm->require_identity_match = 1;
+		if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+			wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: "
+					  "Phase2 Identity not found "
+					  "in the user database",
+					  sm->identity, sm->identity_len);
+			next_type = eap_fast_req_failure(sm, data);
+		} else {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Identity already "
+				   "known - skip Phase 2 Identity Request");
+			next_type = sm->user->methods[0].method;
+			sm->user_eap_method_index = 1;
+		}
+
+		eap_fast_state(data, PHASE2_METHOD);
+	} else {
+		eap_fast_state(data, PHASE2_ID);
+		next_type = EAP_TYPE_IDENTITY;
+	}
+
+	return eap_fast_phase2_init(sm, data, next_type);
+}
+
+
+static void eap_fast_process_msg(struct eap_sm *sm, void *priv,
+				 const struct wpabuf *respData)
+{
+	struct eap_fast_data *data = priv;
+
+	switch (data->state) {
+	case PHASE1:
+		if (eap_fast_process_phase1(sm, data))
+			break;
+
+		/* fall through to PHASE2_START */
+	case PHASE2_START:
+		eap_fast_process_phase2_start(sm, data);
+		break;
+	case PHASE2_ID:
+	case PHASE2_METHOD:
+	case CRYPTO_BINDING:
+	case REQUEST_PAC:
+		eap_fast_process_phase2(sm, data, data->ssl.tls_in);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected state %d in %s",
+			   data->state, __func__);
+		break;
+	}
+}
+
+
+static void eap_fast_process(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_fast_data *data = priv;
+	if (eap_server_tls_process(sm, &data->ssl, respData, data,
+				   EAP_TYPE_FAST, eap_fast_process_version,
+				   eap_fast_process_msg) < 0)
+		eap_fast_state(data, FAILURE);
+}
+
+
+static Boolean eap_fast_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_fast_data *data = priv;
+	return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_fast_data *data = priv;
+	u8 *eapKeyData;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	eapKeyData = os_malloc(EAP_FAST_KEY_LEN);
+	if (eapKeyData == NULL)
+		return NULL;
+
+	eap_fast_derive_eap_msk(data->simck, eapKeyData);
+	*len = EAP_FAST_KEY_LEN;
+
+	return eapKeyData;
+}
+
+
+static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_fast_data *data = priv;
+	u8 *eapKeyData;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	eapKeyData = os_malloc(EAP_EMSK_LEN);
+	if (eapKeyData == NULL)
+		return NULL;
+
+	eap_fast_derive_eap_emsk(data->simck, eapKeyData);
+	*len = EAP_EMSK_LEN;
+
+	return eapKeyData;
+}
+
+
+static Boolean eap_fast_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_fast_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_fast_data *data = priv;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_FAST,
+						len);
+}
+
+
+int eap_server_fast_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_fast_init;
+	eap->reset = eap_fast_reset;
+	eap->buildReq = eap_fast_buildReq;
+	eap->check = eap_fast_check;
+	eap->process = eap_fast_process;
+	eap->isDone = eap_fast_isDone;
+	eap->getKey = eap_fast_getKey;
+	eap->get_emsk = eap_fast_get_emsk;
+	eap->isSuccess = eap_fast_isSuccess;
+	eap->getSessionId = eap_fast_get_session_id;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_gpsk.c b/hostap/src/eap_server/eap_server_gpsk.c
new file mode 100644
index 0000000..50f15c3
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_gpsk.c
@@ -0,0 +1,656 @@
+/*
+ * hostapd / EAP-GPSK (RFC 5433) server
+ * Copyright (c) 2006-2007, 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/random.h"
+#include "eap_server/eap_i.h"
+#include "eap_common/eap_gpsk_common.h"
+
+
+struct eap_gpsk_data {
+	enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state;
+	u8 rand_server[EAP_GPSK_RAND_LEN];
+	u8 rand_peer[EAP_GPSK_RAND_LEN];
+	u8 msk[EAP_MSK_LEN];
+	u8 emsk[EAP_EMSK_LEN];
+	u8 sk[EAP_GPSK_MAX_SK_LEN];
+	size_t sk_len;
+	u8 pk[EAP_GPSK_MAX_PK_LEN];
+	size_t pk_len;
+	u8 session_id[128];
+	size_t id_len;
+	u8 *id_peer;
+	size_t id_peer_len;
+#define MAX_NUM_CSUITES 2
+	struct eap_gpsk_csuite csuite_list[MAX_NUM_CSUITES];
+	size_t csuite_count;
+	int vendor; /* CSuite/Vendor */
+	int specifier; /* CSuite/Specifier */
+};
+
+
+static const char * eap_gpsk_state_txt(int state)
+{
+	switch (state) {
+	case GPSK_1:
+		return "GPSK-1";
+	case GPSK_3:
+		return "GPSK-3";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "?";
+	}
+}
+
+
+static void eap_gpsk_state(struct eap_gpsk_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s",
+		   eap_gpsk_state_txt(data->state),
+		   eap_gpsk_state_txt(state));
+	data->state = state;
+}
+
+
+static void * eap_gpsk_init(struct eap_sm *sm)
+{
+	struct eap_gpsk_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = GPSK_1;
+
+	data->csuite_count = 0;
+	if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF,
+					   EAP_GPSK_CIPHER_AES)) {
+		WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor,
+			     EAP_GPSK_VENDOR_IETF);
+		WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier,
+			     EAP_GPSK_CIPHER_AES);
+		data->csuite_count++;
+	}
+	if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF,
+					   EAP_GPSK_CIPHER_SHA256)) {
+		WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor,
+			     EAP_GPSK_VENDOR_IETF);
+		WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier,
+			     EAP_GPSK_CIPHER_SHA256);
+		data->csuite_count++;
+	}
+
+	return data;
+}
+
+
+static void eap_gpsk_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_gpsk_data *data = priv;
+	os_free(data->id_peer);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm,
+					     struct eap_gpsk_data *data, u8 id)
+{
+	size_t len;
+	struct wpabuf *req;
+
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1");
+
+	if (random_get_bytes(data->rand_server, EAP_GPSK_RAND_LEN)) {
+		wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to get random data");
+		eap_gpsk_state(data, FAILURE);
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "EAP-GPSK: RAND_Server",
+		    data->rand_server, EAP_GPSK_RAND_LEN);
+
+	len = 1 + 2 + sm->server_id_len + EAP_GPSK_RAND_LEN + 2 +
+		data->csuite_count * sizeof(struct eap_gpsk_csuite);
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory "
+			   "for request/GPSK-1");
+		eap_gpsk_state(data, FAILURE);
+		return NULL;
+	}
+
+	wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_1);
+	wpabuf_put_be16(req, sm->server_id_len);
+	wpabuf_put_data(req, sm->server_id, sm->server_id_len);
+	wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN);
+	wpabuf_put_be16(req,
+			data->csuite_count * sizeof(struct eap_gpsk_csuite));
+	wpabuf_put_data(req, data->csuite_list,
+			data->csuite_count * sizeof(struct eap_gpsk_csuite));
+
+	return req;
+}
+
+
+static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm,
+					     struct eap_gpsk_data *data, u8 id)
+{
+	u8 *pos, *start;
+	size_t len, miclen;
+	struct eap_gpsk_csuite *csuite;
+	struct wpabuf *req;
+
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-3");
+
+	miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+	len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + sm->server_id_len +
+		sizeof(struct eap_gpsk_csuite) + 2 + miclen;
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory "
+			   "for request/GPSK-3");
+		eap_gpsk_state(data, FAILURE);
+		return NULL;
+	}
+
+	wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_3);
+	start = wpabuf_put(req, 0);
+
+	wpabuf_put_data(req, data->rand_peer, EAP_GPSK_RAND_LEN);
+	wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN);
+	wpabuf_put_be16(req, sm->server_id_len);
+	wpabuf_put_data(req, sm->server_id, sm->server_id_len);
+	csuite = wpabuf_put(req, sizeof(*csuite));
+	WPA_PUT_BE32(csuite->vendor, data->vendor);
+	WPA_PUT_BE16(csuite->specifier, data->specifier);
+
+	/* no PD_Payload_2 */
+	wpabuf_put_be16(req, 0);
+
+	pos = wpabuf_put(req, miclen);
+	if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+				 data->specifier, start, pos - start, pos) < 0)
+	{
+		os_free(req);
+		eap_gpsk_state(data, FAILURE);
+		return NULL;
+	}
+
+	return req;
+}
+
+
+static struct wpabuf * eap_gpsk_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_gpsk_data *data = priv;
+
+	switch (data->state) {
+	case GPSK_1:
+		return eap_gpsk_build_gpsk_1(sm, data, id);
+	case GPSK_3:
+		return eap_gpsk_build_gpsk_3(sm, data, id);
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown state %d in buildReq",
+			   data->state);
+		break;
+	}
+	return NULL;
+}
+
+
+static Boolean eap_gpsk_check(struct eap_sm *sm, void *priv,
+			      struct wpabuf *respData)
+{
+	struct eap_gpsk_data *data = priv;
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len);
+	if (pos == NULL || len < 1) {
+		wpa_printf(MSG_INFO, "EAP-GPSK: Invalid frame");
+		return TRUE;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode=%d", *pos);
+
+	if (data->state == GPSK_1 && *pos == EAP_GPSK_OPCODE_GPSK_2)
+		return FALSE;
+
+	if (data->state == GPSK_3 && *pos == EAP_GPSK_OPCODE_GPSK_4)
+		return FALSE;
+
+	wpa_printf(MSG_INFO, "EAP-GPSK: Unexpected opcode=%d in state=%d",
+		   *pos, data->state);
+
+	return TRUE;
+}
+
+
+static void eap_gpsk_process_gpsk_2(struct eap_sm *sm,
+				    struct eap_gpsk_data *data,
+				    const u8 *payload, size_t payloadlen)
+{
+	const u8 *pos, *end;
+	u16 alen;
+	const struct eap_gpsk_csuite *csuite;
+	size_t i, miclen;
+	u8 mic[EAP_GPSK_MAX_MIC_LEN];
+
+	if (data->state != GPSK_1)
+		return;
+
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-2");
+
+	pos = payload;
+	end = payload + payloadlen;
+
+	if (end - pos < 2) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+			   "ID_Peer length");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	alen = WPA_GET_BE16(pos);
+	pos += 2;
+	if (end - pos < alen) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+			   "ID_Peer");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	os_free(data->id_peer);
+	data->id_peer = os_malloc(alen);
+	if (data->id_peer == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store "
+			   "%d-octet ID_Peer", alen);
+		return;
+	}
+	os_memcpy(data->id_peer, pos, alen);
+	data->id_peer_len = alen;
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
+			  data->id_peer, data->id_peer_len);
+	pos += alen;
+
+	if (end - pos < 2) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+			   "ID_Server length");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	alen = WPA_GET_BE16(pos);
+	pos += 2;
+	if (end - pos < alen) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+			   "ID_Server");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	if (alen != sm->server_id_len ||
+	    os_memcmp(pos, sm->server_id, alen) != 0) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1 and "
+			   "GPSK-2 did not match");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	pos += alen;
+
+	if (end - pos < EAP_GPSK_RAND_LEN) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+			   "RAND_Peer");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	os_memcpy(data->rand_peer, pos, EAP_GPSK_RAND_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer",
+		    data->rand_peer, EAP_GPSK_RAND_LEN);
+	pos += EAP_GPSK_RAND_LEN;
+
+	if (end - pos < EAP_GPSK_RAND_LEN) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+			   "RAND_Server");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	if (os_memcmp(data->rand_server, pos, EAP_GPSK_RAND_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and "
+			   "GPSK-2 did not match");
+		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1",
+			    data->rand_server, EAP_GPSK_RAND_LEN);
+		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-2",
+			    pos, EAP_GPSK_RAND_LEN);
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	pos += EAP_GPSK_RAND_LEN;
+
+	if (end - pos < 2) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+			   "CSuite_List length");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	alen = WPA_GET_BE16(pos);
+	pos += 2;
+	if (end - pos < alen) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+			   "CSuite_List");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	if (alen != data->csuite_count * sizeof(struct eap_gpsk_csuite) ||
+	    os_memcmp(pos, data->csuite_list, alen) != 0) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List in GPSK-1 and "
+			   "GPSK-2 did not match");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	pos += alen;
+
+	if (end - pos < (int) sizeof(*csuite)) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+			   "CSuite_Sel");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	csuite = (const struct eap_gpsk_csuite *) pos;
+	for (i = 0; i < data->csuite_count; i++) {
+		if (os_memcmp(csuite, &data->csuite_list[i], sizeof(*csuite))
+		    == 0)
+			break;
+	}
+	if (i == data->csuite_count) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Peer selected unsupported "
+			   "ciphersuite %d:%d",
+			   WPA_GET_BE32(csuite->vendor),
+			   WPA_GET_BE16(csuite->specifier));
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	data->vendor = WPA_GET_BE32(csuite->vendor);
+	data->specifier = WPA_GET_BE16(csuite->specifier);
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d",
+		   data->vendor, data->specifier);
+	pos += sizeof(*csuite);	
+
+	if (end - pos < 2) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+			   "PD_Payload_1 length");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	alen = WPA_GET_BE16(pos);
+	pos += 2;
+	if (end - pos < alen) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+			   "PD_Payload_1");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen);
+	pos += alen;
+
+	if (sm->user == NULL || sm->user->password == NULL) {
+		wpa_printf(MSG_INFO, "EAP-GPSK: No PSK/password configured "
+			   "for the user");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+
+	if (eap_gpsk_derive_keys(sm->user->password, sm->user->password_len,
+				 data->vendor, data->specifier,
+				 data->rand_peer, data->rand_server,
+				 data->id_peer, data->id_peer_len,
+				 sm->server_id, sm->server_id_len,
+				 data->msk, data->emsk,
+				 data->sk, &data->sk_len,
+				 data->pk, &data->pk_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+
+	if (eap_gpsk_derive_session_id(sm->user->password,
+				       sm->user->password_len,
+				       data->vendor, data->specifier,
+				       data->rand_peer, data->rand_server,
+				       data->id_peer, data->id_peer_len,
+				       sm->server_id, sm->server_id_len,
+				       EAP_TYPE_GPSK,
+				       data->session_id, &data->id_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id",
+		    data->session_id, data->id_len);
+
+	miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+	if (end - pos < (int) miclen) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
+			   "(left=%lu miclen=%lu)",
+			   (unsigned long) (end - pos),
+			   (unsigned long) miclen);
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+				 data->specifier, payload, pos - payload, mic)
+	    < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	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);
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	pos += miclen;
+
+	if (pos != end) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
+			   "data in the end of GPSK-2",
+			   (unsigned long) (end - pos));
+	}
+
+	eap_gpsk_state(data, GPSK_3);
+}
+
+
+static void eap_gpsk_process_gpsk_4(struct eap_sm *sm,
+				    struct eap_gpsk_data *data,
+				    const u8 *payload, size_t payloadlen)
+{
+	const u8 *pos, *end;
+	u16 alen;
+	size_t miclen;
+	u8 mic[EAP_GPSK_MAX_MIC_LEN];
+
+	if (data->state != GPSK_3)
+		return;
+
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-4");
+
+	pos = payload;
+	end = payload + payloadlen;
+
+	if (end - pos < 2) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+			   "PD_Payload_1 length");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	alen = WPA_GET_BE16(pos);
+	pos += 2;
+	if (end - pos < alen) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+			   "PD_Payload_1");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen);
+	pos += alen;
+
+	miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+	if (end - pos < (int) miclen) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
+			   "(left=%lu miclen=%lu)",
+			   (unsigned long) (end - pos),
+			   (unsigned long) miclen);
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+				 data->specifier, payload, pos - payload, mic)
+	    < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	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);
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	pos += miclen;
+
+	if (pos != end) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
+			   "data in the end of GPSK-4",
+			   (unsigned long) (end - pos));
+	}
+
+	eap_gpsk_state(data, SUCCESS);
+}
+
+
+static void eap_gpsk_process(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_gpsk_data *data = priv;
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len);
+	if (pos == NULL || len < 1)
+		return;
+
+	switch (*pos) {
+	case EAP_GPSK_OPCODE_GPSK_2:
+		eap_gpsk_process_gpsk_2(sm, data, pos + 1, len - 1);
+		break;
+	case EAP_GPSK_OPCODE_GPSK_4:
+		eap_gpsk_process_gpsk_4(sm, data, pos + 1, len - 1);
+		break;
+	}
+}
+
+
+static Boolean eap_gpsk_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_gpsk_data *data = priv;
+	return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_gpsk_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_MSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->msk, EAP_MSK_LEN);
+	*len = EAP_MSK_LEN;
+
+	return key;
+}
+
+
+static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_gpsk_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+	*len = EAP_EMSK_LEN;
+
+	return key;
+}
+
+
+static Boolean eap_gpsk_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_gpsk_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_gpsk_data *data = priv;
+	u8 *sid;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	sid = os_malloc(data->id_len);
+	if (sid == NULL)
+		return NULL;
+	os_memcpy(sid, data->session_id, data->id_len);
+	*len = data->id_len;
+
+	return sid;
+}
+
+
+int eap_server_gpsk_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_gpsk_init;
+	eap->reset = eap_gpsk_reset;
+	eap->buildReq = eap_gpsk_buildReq;
+	eap->check = eap_gpsk_check;
+	eap->process = eap_gpsk_process;
+	eap->isDone = eap_gpsk_isDone;
+	eap->getKey = eap_gpsk_getKey;
+	eap->isSuccess = eap_gpsk_isSuccess;
+	eap->get_emsk = eap_gpsk_get_emsk;
+	eap->getSessionId = eap_gpsk_get_session_id;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_gtc.c b/hostap/src/eap_server/eap_server_gtc.c
new file mode 100644
index 0000000..98ac3c6
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_gtc.c
@@ -0,0 +1,224 @@
+/*
+ * hostapd / EAP-GTC (RFC 3748)
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+struct eap_gtc_data {
+	enum { CONTINUE, SUCCESS, FAILURE } state;
+	int prefix;
+};
+
+
+static void * eap_gtc_init(struct eap_sm *sm)
+{
+	struct eap_gtc_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = CONTINUE;
+
+#ifdef EAP_SERVER_FAST
+	if (sm->m && sm->m->vendor == EAP_VENDOR_IETF &&
+	    sm->m->method == EAP_TYPE_FAST) {
+		wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix "
+			   "with challenge/response");
+		data->prefix = 1;
+	}
+#endif /* EAP_SERVER_FAST */
+
+	return data;
+}
+
+
+static void eap_gtc_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_gtc_data *data = priv;
+	os_free(data);
+}
+
+
+static struct wpabuf * eap_gtc_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_gtc_data *data = priv;
+	struct wpabuf *req;
+	char *msg;
+	size_t msg_len;
+
+	msg = data->prefix ? "CHALLENGE=Password" : "Password";
+
+	msg_len = os_strlen(msg);
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, msg_len,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-GTC: Failed to allocate memory for "
+			   "request");
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	wpabuf_put_data(req, msg, msg_len);
+
+	data->state = CONTINUE;
+
+	return req;
+}
+
+
+static Boolean eap_gtc_check(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &len);
+	if (pos == NULL || len < 1) {
+		wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static void eap_gtc_process(struct eap_sm *sm, void *priv,
+			    struct wpabuf *respData)
+{
+	struct eap_gtc_data *data = priv;
+	const u8 *pos;
+	size_t rlen;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &rlen);
+	if (pos == NULL || rlen < 1)
+		return; /* Should not happen - frame already validated */
+
+	wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", pos, rlen);
+
+#ifdef EAP_SERVER_FAST
+	if (data->prefix) {
+		const u8 *pos2, *end;
+		/* "RESPONSE=<user>\0<password>" */
+		if (rlen < 10) {
+			wpa_printf(MSG_DEBUG, "EAP-GTC: Too short response "
+				   "for EAP-FAST prefix");
+			data->state = FAILURE;
+			return;
+		}
+
+		end = pos + rlen;
+		pos += 9;
+		pos2 = pos;
+		while (pos2 < end && *pos2)
+			pos2++;
+		if (pos2 == end) {
+			wpa_printf(MSG_DEBUG, "EAP-GTC: No password in "
+				   "response to EAP-FAST prefix");
+			data->state = FAILURE;
+			return;
+		}
+
+		wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Response user",
+				  pos, pos2 - pos);
+		if (sm->identity && sm->require_identity_match &&
+		    (pos2 - pos != (int) sm->identity_len ||
+		     os_memcmp(pos, sm->identity, sm->identity_len))) {
+			wpa_printf(MSG_DEBUG, "EAP-GTC: Phase 2 Identity did "
+				   "not match with required Identity");
+			wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Expected "
+					  "identity",
+					  sm->identity, sm->identity_len);
+			data->state = FAILURE;
+			return;
+		} else {
+			os_free(sm->identity);
+			sm->identity_len = pos2 - pos;
+			sm->identity = os_malloc(sm->identity_len);
+			if (sm->identity == NULL) {
+				data->state = FAILURE;
+				return;
+			}
+			os_memcpy(sm->identity, pos, sm->identity_len);
+		}
+
+		if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+			wpa_hexdump_ascii(MSG_DEBUG, "EAP-GTC: Phase2 "
+					  "Identity not found in the user "
+					  "database",
+					  sm->identity, sm->identity_len);
+			data->state = FAILURE;
+			return;
+		}
+
+		pos = pos2 + 1;
+		rlen = end - pos;
+		wpa_hexdump_ascii_key(MSG_MSGDUMP,
+				      "EAP-GTC: Response password",
+				      pos, rlen);
+	}
+#endif /* EAP_SERVER_FAST */
+
+	if (sm->user == NULL || sm->user->password == NULL ||
+	    sm->user->password_hash) {
+		wpa_printf(MSG_INFO, "EAP-GTC: Plaintext password not "
+			   "configured");
+		data->state = FAILURE;
+		return;
+	}
+
+	if (rlen != sm->user->password_len ||
+	    os_memcmp_const(pos, sm->user->password, rlen) != 0) {
+		wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure");
+		data->state = FAILURE;
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Success");
+		data->state = SUCCESS;
+	}
+}
+
+
+static Boolean eap_gtc_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_gtc_data *data = priv;
+	return data->state != CONTINUE;
+}
+
+
+static Boolean eap_gtc_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_gtc_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+int eap_server_gtc_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_gtc_init;
+	eap->reset = eap_gtc_reset;
+	eap->buildReq = eap_gtc_buildReq;
+	eap->check = eap_gtc_check;
+	eap->process = eap_gtc_process;
+	eap->isDone = eap_gtc_isDone;
+	eap->isSuccess = eap_gtc_isSuccess;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_identity.c b/hostap/src/eap_server/eap_server_identity.c
new file mode 100644
index 0000000..4501533
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_identity.c
@@ -0,0 +1,181 @@
+/*
+ * hostapd / EAP-Identity
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+struct eap_identity_data {
+	enum { CONTINUE, SUCCESS, FAILURE } state;
+	int pick_up;
+};
+
+
+static void * eap_identity_init(struct eap_sm *sm)
+{
+	struct eap_identity_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = CONTINUE;
+
+	return data;
+}
+
+
+static void * eap_identity_initPickUp(struct eap_sm *sm)
+{
+	struct eap_identity_data *data;
+	data = eap_identity_init(sm);
+	if (data) {
+		data->pick_up = 1;
+	}
+	return data;
+}
+
+
+static void eap_identity_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_identity_data *data = priv;
+	os_free(data);
+}
+
+
+static struct wpabuf * eap_identity_buildReq(struct eap_sm *sm, void *priv,
+					     u8 id)
+{
+	struct eap_identity_data *data = priv;
+	struct wpabuf *req;
+	const char *req_data;
+	size_t req_data_len;
+
+	if (sm->eapol_cb->get_eap_req_id_text) {
+		req_data = sm->eapol_cb->get_eap_req_id_text(sm->eapol_ctx,
+							     &req_data_len);
+	} else {
+		req_data = NULL;
+		req_data_len = 0;
+	}
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req_data_len,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-Identity: Failed to allocate "
+			   "memory for request");
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	wpabuf_put_data(req, req_data, req_data_len);
+
+	return req;
+}
+
+
+static Boolean eap_identity_check(struct eap_sm *sm, void *priv,
+				  struct wpabuf *respData)
+{
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
+			       respData, &len);
+	if (pos == NULL) {
+		wpa_printf(MSG_INFO, "EAP-Identity: Invalid frame");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static void eap_identity_process(struct eap_sm *sm, void *priv,
+				 struct wpabuf *respData)
+{
+	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)) {
+			wpa_printf(MSG_DEBUG, "EAP-Identity: failed to pick "
+				   "up already started negotiation");
+			data->state = FAILURE;
+			return;
+		}
+		data->pick_up = 0;
+	}
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
+			       respData, &len);
+	if (pos == NULL)
+		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);
+	sm->identity = os_malloc(len ? len : 1);
+	if (sm->identity == NULL) {
+		data->state = FAILURE;
+	} else {
+		os_memcpy(sm->identity, pos, len);
+		sm->identity_len = len;
+		data->state = SUCCESS;
+	}
+}
+
+
+static Boolean eap_identity_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_identity_data *data = priv;
+	return data->state != CONTINUE;
+}
+
+
+static Boolean eap_identity_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_identity_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+int eap_server_identity_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
+				      "Identity");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_identity_init;
+	eap->initPickUp = eap_identity_initPickUp;
+	eap->reset = eap_identity_reset;
+	eap->buildReq = eap_identity_buildReq;
+	eap->check = eap_identity_check;
+	eap->process = eap_identity_process;
+	eap->isDone = eap_identity_isDone;
+	eap->isSuccess = eap_identity_isSuccess;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_ikev2.c b/hostap/src/eap_server/eap_server_ikev2.c
new file mode 100644
index 0000000..16e6276
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_ikev2.c
@@ -0,0 +1,576 @@
+/*
+ * EAP-IKEv2 server (RFC 5106)
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_common/eap_ikev2_common.h"
+#include "ikev2.h"
+
+
+struct eap_ikev2_data {
+	struct ikev2_initiator_data ikev2;
+	enum { MSG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
+	struct wpabuf *in_buf;
+	struct wpabuf *out_buf;
+	size_t out_used;
+	size_t fragment_size;
+	int keys_ready;
+	u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN];
+	int keymat_ok;
+};
+
+
+static const u8 * eap_ikev2_get_shared_secret(void *ctx, const u8 *IDr,
+					      size_t IDr_len,
+					      size_t *secret_len)
+{
+	struct eap_sm *sm = ctx;
+
+	if (IDr == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No IDr received - default "
+			   "to user identity from EAP-Identity");
+		IDr = sm->identity;
+		IDr_len = sm->identity_len;
+	}
+
+	if (eap_user_get(sm, IDr, IDr_len, 0) < 0 || sm->user == NULL ||
+	    sm->user->password == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No user entry found");
+		return NULL;
+	}
+
+	*secret_len = sm->user->password_len;
+	return sm->user->password;
+}
+
+
+static const char * eap_ikev2_state_txt(int state)
+{
+	switch (state) {
+	case MSG:
+		return "MSG";
+	case FRAG_ACK:
+		return "FRAG_ACK";
+	case WAIT_FRAG_ACK:
+		return "WAIT_FRAG_ACK";
+	case DONE:
+		return "DONE";
+	case FAIL:
+		return "FAIL";
+	default:
+		return "?";
+	}
+}
+
+
+static void eap_ikev2_state(struct eap_ikev2_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s",
+		   eap_ikev2_state_txt(data->state),
+		   eap_ikev2_state_txt(state));
+	data->state = state;
+}
+
+
+static void * eap_ikev2_init(struct eap_sm *sm)
+{
+	struct eap_ikev2_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = MSG;
+	data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size :
+		IKEV2_FRAGMENT_SIZE;
+	data->ikev2.state = SA_INIT;
+	data->ikev2.peer_auth = PEER_AUTH_SECRET;
+	data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2");
+	if (data->ikev2.key_pad == NULL)
+		goto failed;
+	data->ikev2.key_pad_len = 21;
+
+	/* TODO: make proposals configurable */
+	data->ikev2.proposal.proposal_num = 1;
+	data->ikev2.proposal.integ = AUTH_HMAC_SHA1_96;
+	data->ikev2.proposal.prf = PRF_HMAC_SHA1;
+	data->ikev2.proposal.encr = ENCR_AES_CBC;
+	data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP;
+
+	data->ikev2.IDi = os_malloc(sm->server_id_len);
+	if (data->ikev2.IDi == NULL)
+		goto failed;
+	os_memcpy(data->ikev2.IDi, sm->server_id, sm->server_id_len);
+	data->ikev2.IDi_len = sm->server_id_len;
+
+	data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret;
+	data->ikev2.cb_ctx = sm;
+
+	return data;
+
+failed:
+	ikev2_initiator_deinit(&data->ikev2);
+	os_free(data);
+	return NULL;
+}
+
+
+static void eap_ikev2_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_ikev2_data *data = priv;
+	wpabuf_free(data->in_buf);
+	wpabuf_free(data->out_buf);
+	ikev2_initiator_deinit(&data->ikev2);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, u8 id)
+{
+	struct wpabuf *req;
+	u8 flags;
+	size_t send_len, plen, icv_len = 0;
+
+	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Request");
+
+	flags = 0;
+	send_len = wpabuf_len(data->out_buf) - data->out_used;
+	if (1 + send_len > data->fragment_size) {
+		send_len = data->fragment_size - 1;
+		flags |= IKEV2_FLAGS_MORE_FRAGMENTS;
+		if (data->out_used == 0) {
+			flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
+			send_len -= 4;
+		}
+	}
+
+	plen = 1 + send_len;
+	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
+		plen += 4;
+	if (data->keys_ready) {
+		const struct ikev2_integ_alg *integ;
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum "
+			   "Data");
+		flags |= IKEV2_FLAGS_ICV_INCLUDED;
+		integ = ikev2_get_integ(data->ikev2.proposal.integ);
+		if (integ == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
+				   "transform / cannot generate ICV");
+			return NULL;
+		}
+		icv_len = integ->hash_len;
+
+		plen += icv_len;
+	}
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL)
+		return NULL;
+
+	wpabuf_put_u8(req, flags); /* Flags */
+	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
+		wpabuf_put_be32(req, wpabuf_len(data->out_buf));
+
+	wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
+			send_len);
+	data->out_used += send_len;
+
+	if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
+		const u8 *msg = wpabuf_head(req);
+		size_t len = wpabuf_len(req);
+		ikev2_integ_hash(data->ikev2.proposal.integ,
+				 data->ikev2.keys.SK_ai,
+				 data->ikev2.keys.SK_integ_len,
+				 msg, len, wpabuf_put(req, icv_len));
+	}
+
+	if (data->out_used == wpabuf_len(data->out_buf)) {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
+			   "(message sent completely)",
+			   (unsigned long) send_len);
+		wpabuf_free(data->out_buf);
+		data->out_buf = NULL;
+		data->out_used = 0;
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
+			   "(%lu more to send)", (unsigned long) send_len,
+			   (unsigned long) wpabuf_len(data->out_buf) -
+			   data->out_used);
+		eap_ikev2_state(data, WAIT_FRAG_ACK);
+	}
+
+	return req;
+}
+
+
+static struct wpabuf * eap_ikev2_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_ikev2_data *data = priv;
+
+	switch (data->state) {
+	case MSG:
+		if (data->out_buf == NULL) {
+			data->out_buf = ikev2_initiator_build(&data->ikev2);
+			if (data->out_buf == NULL) {
+				wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to "
+					   "generate IKEv2 message");
+				return NULL;
+			}
+			data->out_used = 0;
+		}
+		/* pass through */
+	case WAIT_FRAG_ACK:
+		return eap_ikev2_build_msg(data, id);
+	case FRAG_ACK:
+		return eap_ikev2_build_frag_ack(id, EAP_CODE_REQUEST);
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected state %d in "
+			   "buildReq", data->state);
+		return NULL;
+	}
+}
+
+
+static Boolean eap_ikev2_check(struct eap_sm *sm, void *priv,
+			       struct wpabuf *respData)
+{
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData,
+			       &len);
+	if (pos == NULL) {
+		wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid frame");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
+				 const struct wpabuf *respData,
+				 u8 flags, const u8 *pos, const u8 **end,
+				 int frag_ack)
+{
+	if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
+		int icv_len = eap_ikev2_validate_icv(
+			data->ikev2.proposal.integ, &data->ikev2.keys, 0,
+			respData, pos, *end);
+		if (icv_len < 0)
+			return -1;
+		/* Hide Integrity Checksum Data from further processing */
+		*end -= icv_len;
+	} else if (data->keys_ready && !frag_ack) {
+		wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
+			   "included integrity checksum");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eap_ikev2_process_cont(struct eap_ikev2_data *data,
+				  const u8 *buf, size_t len)
+{
+	/* Process continuation of a pending message */
+	if (len > wpabuf_tailroom(data->in_buf)) {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow");
+		eap_ikev2_state(data, FAIL);
+		return -1;
+	}
+
+	wpabuf_put_data(data->in_buf, buf, len);
+	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting for %lu "
+		   "bytes more", (unsigned long) len,
+		   (unsigned long) wpabuf_tailroom(data->in_buf));
+
+	return 0;
+}
+
+
+static int eap_ikev2_process_fragment(struct eap_ikev2_data *data,
+				      u8 flags, u32 message_length,
+				      const u8 *buf, size_t len)
+{
+	/* Process a fragment that is not the last one of the message */
+	if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in "
+			   "a fragmented packet");
+		return -1;
+	}
+
+	if (data->in_buf == NULL) {
+		/* First fragment of the message */
+		if (message_length > 50000) {
+			/* Limit maximum memory allocation */
+			wpa_printf(MSG_DEBUG,
+				   "EAP-IKEV2: Ignore too long message");
+			return -1;
+		}
+		data->in_buf = wpabuf_alloc(message_length);
+		if (data->in_buf == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
+				   "message");
+			return -1;
+		}
+		wpabuf_put_data(data->in_buf, buf, len);
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first "
+			   "fragment, waiting for %lu bytes more",
+			   (unsigned long) len,
+			   (unsigned long) wpabuf_tailroom(data->in_buf));
+	}
+
+	return 0;
+}
+
+
+static int eap_ikev2_server_keymat(struct eap_ikev2_data *data)
+{
+	if (eap_ikev2_derive_keymat(
+		    data->ikev2.proposal.prf, &data->ikev2.keys,
+		    data->ikev2.i_nonce, data->ikev2.i_nonce_len,
+		    data->ikev2.r_nonce, data->ikev2.r_nonce_len,
+		    data->keymat) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to derive "
+			   "key material");
+		return -1;
+	}
+	data->keymat_ok = 1;
+	return 0;
+}
+
+
+static void eap_ikev2_process(struct eap_sm *sm, void *priv,
+			      struct wpabuf *respData)
+{
+	struct eap_ikev2_data *data = priv;
+	const u8 *start, *pos, *end;
+	size_t len;
+	u8 flags;
+	u32 message_length = 0;
+	struct wpabuf tmpbuf;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData,
+			       &len);
+	if (pos == NULL)
+		return; /* Should not happen; message already verified */
+
+	start = pos;
+	end = start + len;
+
+	if (len == 0) {
+		/* fragment ack */
+		flags = 0;
+	} else
+		flags = *pos++;
+
+	if (eap_ikev2_process_icv(data, respData, flags, pos, &end,
+				  data->state == WAIT_FRAG_ACK && len == 0) < 0)
+	{
+		eap_ikev2_state(data, FAIL);
+		return;
+	}
+
+	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) {
+		if (end - pos < 4) {
+			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow");
+			eap_ikev2_state(data, FAIL);
+			return;
+		}
+		message_length = WPA_GET_BE32(pos);
+		pos += 4;
+
+		if (message_length < (u32) (end - pos)) {
+			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message "
+				   "Length (%d; %ld remaining in this msg)",
+				   message_length, (long) (end - pos));
+			eap_ikev2_state(data, FAIL);
+			return;
+		}
+	}
+	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x "
+		   "Message Length %u", flags, message_length);
+
+	if (data->state == WAIT_FRAG_ACK) {
+		if (len != 0) {
+			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
+				   "in WAIT_FRAG_ACK state");
+			eap_ikev2_state(data, FAIL);
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged");
+		eap_ikev2_state(data, MSG);
+		return;
+	}
+
+	if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) {
+		eap_ikev2_state(data, FAIL);
+		return;
+	}
+		
+	if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) {
+		if (eap_ikev2_process_fragment(data, flags, message_length,
+					       pos, end - pos) < 0)
+			eap_ikev2_state(data, FAIL);
+		else
+			eap_ikev2_state(data, FRAG_ACK);
+		return;
+	} else if (data->state == FRAG_ACK) {
+		wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
+		data->state = MSG;
+	}
+
+	if (data->in_buf == NULL) {
+		/* Wrap unfragmented messages as wpabuf without extra copy */
+		wpabuf_set(&tmpbuf, pos, end - pos);
+		data->in_buf = &tmpbuf;
+	}
+
+	if (ikev2_initiator_process(&data->ikev2, data->in_buf) < 0) {
+		if (data->in_buf == &tmpbuf)
+			data->in_buf = NULL;
+		eap_ikev2_state(data, FAIL);
+		return;
+	}
+
+	switch (data->ikev2.state) {
+	case SA_AUTH:
+		/* SA_INIT was sent out, so message have to be
+		 * integrity protected from now on. */
+		data->keys_ready = 1;
+		break;
+	case IKEV2_DONE:
+		if (data->state == FAIL)
+			break;
+		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication completed "
+			   "successfully");
+		if (eap_ikev2_server_keymat(data))
+			break;
+		eap_ikev2_state(data, DONE);
+		break;
+	default:
+		break;
+	}
+
+	if (data->in_buf != &tmpbuf)
+		wpabuf_free(data->in_buf);
+	data->in_buf = NULL;
+}
+
+
+static Boolean eap_ikev2_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_ikev2_data *data = priv;
+	return data->state == DONE || data->state == FAIL;
+}
+
+
+static Boolean eap_ikev2_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_ikev2_data *data = priv;
+	return data->state == DONE && data->ikev2.state == IKEV2_DONE &&
+		data->keymat_ok;
+}
+
+
+static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ikev2_data *data = priv;
+	u8 *key;
+
+	if (data->state != DONE || !data->keymat_ok)
+		return NULL;
+
+	key = os_malloc(EAP_MSK_LEN);
+	if (key) {
+		os_memcpy(key, data->keymat, EAP_MSK_LEN);
+		*len = EAP_MSK_LEN;
+	}
+
+	return key;
+}
+
+
+static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ikev2_data *data = priv;
+	u8 *key;
+
+	if (data->state != DONE || !data->keymat_ok)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key) {
+		os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN);
+		*len = EAP_EMSK_LEN;
+	}
+
+	return key;
+}
+
+
+static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ikev2_data *data = priv;
+	u8 *sid;
+	size_t sid_len;
+	size_t offset;
+
+	if (data->state != DONE || !data->keymat_ok)
+		return NULL;
+
+	sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len;
+	sid = os_malloc(sid_len);
+	if (sid) {
+		offset = 0;
+		sid[offset] = EAP_TYPE_IKEV2;
+		offset++;
+		os_memcpy(sid + offset, data->ikev2.i_nonce,
+			  data->ikev2.i_nonce_len);
+		offset += data->ikev2.i_nonce_len;
+		os_memcpy(sid + offset, data->ikev2.r_nonce,
+			  data->ikev2.r_nonce_len);
+		*len = sid_len;
+		wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id",
+			    sid, sid_len);
+	}
+
+	return sid;
+}
+
+
+int eap_server_ikev2_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_IKEV2,
+				      "IKEV2");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_ikev2_init;
+	eap->reset = eap_ikev2_reset;
+	eap->buildReq = eap_ikev2_buildReq;
+	eap->check = eap_ikev2_check;
+	eap->process = eap_ikev2_process;
+	eap->isDone = eap_ikev2_isDone;
+	eap->getKey = eap_ikev2_getKey;
+	eap->isSuccess = eap_ikev2_isSuccess;
+	eap->get_emsk = eap_ikev2_get_emsk;
+	eap->getSessionId = eap_ikev2_get_session_id;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_md5.c b/hostap/src/eap_server/eap_server_md5.c
new file mode 100644
index 0000000..71e8d59
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_md5.c
@@ -0,0 +1,175 @@
+/*
+ * hostapd / EAP-MD5 server
+ * Copyright (c) 2004-2012, 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/random.h"
+#include "eap_i.h"
+#include "eap_common/chap.h"
+
+
+#define CHALLENGE_LEN 16
+
+struct eap_md5_data {
+	u8 challenge[CHALLENGE_LEN];
+	enum { CONTINUE, SUCCESS, FAILURE } state;
+};
+
+
+static void * eap_md5_init(struct eap_sm *sm)
+{
+	struct eap_md5_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = CONTINUE;
+
+	return data;
+}
+
+
+static void eap_md5_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_md5_data *data = priv;
+	os_free(data);
+}
+
+
+static struct wpabuf * eap_md5_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_md5_data *data = priv;
+	struct wpabuf *req;
+
+	if (random_get_bytes(data->challenge, CHALLENGE_LEN)) {
+		wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data");
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHALLENGE_LEN,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-MD5: Failed to allocate memory for "
+			   "request");
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	wpabuf_put_u8(req, CHALLENGE_LEN);
+	wpabuf_put_data(req, data->challenge, CHALLENGE_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", data->challenge,
+		    CHALLENGE_LEN);
+
+	data->state = CONTINUE;
+
+	return req;
+}
+
+
+static Boolean eap_md5_check(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, respData, &len);
+	if (pos == NULL || len < 1) {
+		wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame");
+		return TRUE;
+	}
+	if (*pos != CHAP_MD5_LEN || 1 + CHAP_MD5_LEN > len) {
+		wpa_printf(MSG_INFO, "EAP-MD5: Invalid response "
+			   "(response_len=%d payload_len=%lu",
+			   *pos, (unsigned long) len);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static void eap_md5_process(struct eap_sm *sm, void *priv,
+			    struct wpabuf *respData)
+{
+	struct eap_md5_data *data = priv;
+	const u8 *pos;
+	size_t plen;
+	u8 hash[CHAP_MD5_LEN], id;
+
+	if (sm->user == NULL || sm->user->password == NULL ||
+	    sm->user->password_hash) {
+		wpa_printf(MSG_INFO, "EAP-MD5: Plaintext password not "
+			   "configured");
+		data->state = FAILURE;
+		return;
+	}
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, respData, &plen);
+	if (pos == NULL || *pos != CHAP_MD5_LEN || plen < 1 + CHAP_MD5_LEN)
+		return; /* Should not happen - frame already validated */
+
+	pos++; /* Skip response len */
+	wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, CHAP_MD5_LEN);
+
+	id = eap_get_id(respData);
+	if (chap_md5(id, sm->user->password, sm->user->password_len,
+		     data->challenge, CHALLENGE_LEN, hash)) {
+		wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed");
+		data->state = FAILURE;
+		return;
+	}
+
+	if (os_memcmp_const(hash, pos, CHAP_MD5_LEN) == 0) {
+		wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success");
+		data->state = SUCCESS;
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Failure");
+		data->state = FAILURE;
+	}
+}
+
+
+static Boolean eap_md5_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_md5_data *data = priv;
+	return data->state != CONTINUE;
+}
+
+
+static Boolean eap_md5_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_md5_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+int eap_server_md5_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_md5_init;
+	eap->reset = eap_md5_reset;
+	eap->buildReq = eap_md5_buildReq;
+	eap->check = eap_md5_check;
+	eap->process = eap_md5_process;
+	eap->isDone = eap_md5_isDone;
+	eap->isSuccess = eap_md5_isSuccess;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_methods.c b/hostap/src/eap_server/eap_server_methods.c
new file mode 100644
index 0000000..9e9dc93
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_methods.c
@@ -0,0 +1,171 @@
+/*
+ * EAP server method registration
+ * 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 "common.h"
+#include "eap_i.h"
+#include "eap_methods.h"
+
+
+static struct eap_method *eap_methods;
+
+
+/**
+ * eap_server_get_eap_method - Get EAP method based on type number
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @method: EAP type number
+ * Returns: Pointer to EAP method or %NULL if not found
+ */
+const struct eap_method * eap_server_get_eap_method(int vendor, EapType method)
+{
+	struct eap_method *m;
+	for (m = eap_methods; m; m = m->next) {
+		if (m->vendor == vendor && m->method == method)
+			return m;
+	}
+	return NULL;
+}
+
+
+/**
+ * eap_server_get_type - Get EAP type for the given EAP method name
+ * @name: EAP method name, e.g., TLS
+ * @vendor: Buffer for returning EAP Vendor-Id
+ * Returns: EAP method type or %EAP_TYPE_NONE if not found
+ *
+ * This function maps EAP type names into EAP type numbers based on the list of
+ * EAP methods included in the build.
+ */
+EapType eap_server_get_type(const char *name, int *vendor)
+{
+	struct eap_method *m;
+	for (m = eap_methods; m; m = m->next) {
+		if (os_strcmp(m->name, name) == 0) {
+			*vendor = m->vendor;
+			return m->method;
+		}
+	}
+	*vendor = EAP_VENDOR_IETF;
+	return EAP_TYPE_NONE;
+}
+
+
+/**
+ * eap_server_method_alloc - Allocate EAP server method structure
+ * @version: Version of the EAP server method interface (set to
+ * EAP_SERVER_METHOD_INTERFACE_VERSION)
+ * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
+ * @method: EAP type number (EAP_TYPE_*)
+ * @name: Name of the method (e.g., "TLS")
+ * Returns: Allocated EAP method structure or %NULL on failure
+ *
+ * The returned structure should be freed with eap_server_method_free() when it
+ * is not needed anymore.
+ */
+struct eap_method * eap_server_method_alloc(int version, int vendor,
+					    EapType method, const char *name)
+{
+	struct eap_method *eap;
+	eap = os_zalloc(sizeof(*eap));
+	if (eap == NULL)
+		return NULL;
+	eap->version = version;
+	eap->vendor = vendor;
+	eap->method = method;
+	eap->name = name;
+	return eap;
+}
+
+
+/**
+ * eap_server_method_free - Free EAP server method structure
+ * @method: Method structure allocated with eap_server_method_alloc()
+ */
+void eap_server_method_free(struct eap_method *method)
+{
+	os_free(method);
+}
+
+
+/**
+ * eap_server_method_register - Register an EAP server method
+ * @method: EAP method to register
+ * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method
+ * has already been registered
+ *
+ * Each EAP server method needs to call this function to register itself as a
+ * supported EAP method.
+ */
+int eap_server_method_register(struct eap_method *method)
+{
+	struct eap_method *m, *last = NULL;
+
+	if (method == NULL || method->name == NULL ||
+	    method->version != EAP_SERVER_METHOD_INTERFACE_VERSION)
+		return -1;
+
+	for (m = eap_methods; m; m = m->next) {
+		if ((m->vendor == method->vendor &&
+		     m->method == method->method) ||
+		    os_strcmp(m->name, method->name) == 0)
+			return -2;
+		last = m;
+	}
+
+	if (last)
+		last->next = method;
+	else
+		eap_methods = method;
+
+	return 0;
+}
+
+
+/**
+ * eap_server_unregister_methods - Unregister EAP server methods
+ *
+ * This function is called at program termination to unregister all EAP server
+ * methods.
+ */
+void eap_server_unregister_methods(void)
+{
+	struct eap_method *m;
+
+	while (eap_methods) {
+		m = eap_methods;
+		eap_methods = eap_methods->next;
+
+		if (m->free)
+			m->free(m);
+		else
+			eap_server_method_free(m);
+	}
+}
+
+
+/**
+ * eap_server_get_name - Get EAP method name for the given EAP type
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @type: EAP method type
+ * Returns: EAP method name, e.g., TLS, or "unknown" if not found
+ *
+ * This function maps EAP type numbers into EAP type names based on the list of
+ * EAP methods included in the build.
+ */
+const char * eap_server_get_name(int vendor, EapType type)
+{
+	struct eap_method *m;
+	if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED)
+		return "expanded";
+	for (m = eap_methods; m; m = m->next) {
+		if (m->vendor == vendor && m->method == type)
+			return m->name;
+	}
+	return "unknown";
+}
diff --git a/hostap/src/eap_server/eap_server_mschapv2.c b/hostap/src/eap_server/eap_server_mschapv2.c
new file mode 100644
index 0000000..98d74e0
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_mschapv2.c
@@ -0,0 +1,595 @@
+/*
+ * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
+ * Copyright (c) 2004-2007, 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/ms_funcs.h"
+#include "crypto/random.h"
+#include "eap_i.h"
+
+
+struct eap_mschapv2_hdr {
+	u8 op_code; /* MSCHAPV2_OP_* */
+	u8 mschapv2_id; /* must be changed for challenges, but not for
+			 * success/failure */
+	u8 ms_length[2]; /* Note: misaligned; length - 5 */
+	/* followed by data */
+} STRUCT_PACKED;
+
+#define MSCHAPV2_OP_CHALLENGE 1
+#define MSCHAPV2_OP_RESPONSE 2
+#define MSCHAPV2_OP_SUCCESS 3
+#define MSCHAPV2_OP_FAILURE 4
+#define MSCHAPV2_OP_CHANGE_PASSWORD 7
+
+#define MSCHAPV2_RESP_LEN 49
+
+#define ERROR_RESTRICTED_LOGON_HOURS 646
+#define ERROR_ACCT_DISABLED 647
+#define ERROR_PASSWD_EXPIRED 648
+#define ERROR_NO_DIALIN_PERMISSION 649
+#define ERROR_AUTHENTICATION_FAILURE 691
+#define ERROR_CHANGING_PASSWORD 709
+
+#define PASSWD_CHANGE_CHAL_LEN 16
+#define MSCHAPV2_KEY_LEN 16
+
+
+#define CHALLENGE_LEN 16
+
+struct eap_mschapv2_data {
+	u8 auth_challenge[CHALLENGE_LEN];
+	int auth_challenge_from_tls;
+	u8 *peer_challenge;
+	u8 auth_response[20];
+	enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state;
+	u8 resp_mschapv2_id;
+	u8 master_key[16];
+	int master_key_valid;
+};
+
+
+static void * eap_mschapv2_init(struct eap_sm *sm)
+{
+	struct eap_mschapv2_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = CHALLENGE;
+
+	if (sm->auth_challenge) {
+		os_memcpy(data->auth_challenge, sm->auth_challenge,
+			  CHALLENGE_LEN);
+		data->auth_challenge_from_tls = 1;
+	}
+
+	if (sm->peer_challenge) {
+		data->peer_challenge = os_malloc(CHALLENGE_LEN);
+		if (data->peer_challenge == NULL) {
+			os_free(data);
+			return NULL;
+		}
+		os_memcpy(data->peer_challenge, sm->peer_challenge,
+			  CHALLENGE_LEN);
+	}
+
+	return data;
+}
+
+
+static void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_mschapv2_data *data = priv;
+	if (data == NULL)
+		return;
+
+	os_free(data->peer_challenge);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_mschapv2_build_challenge(
+	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
+{
+	struct wpabuf *req;
+	struct eap_mschapv2_hdr *ms;
+	size_t ms_len;
+
+	if (!data->auth_challenge_from_tls &&
+	    random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) {
+		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
+			   "data");
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len;
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
+			   " for request");
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	ms = wpabuf_put(req, sizeof(*ms));
+	ms->op_code = MSCHAPV2_OP_CHALLENGE;
+	ms->mschapv2_id = id;
+	WPA_PUT_BE16(ms->ms_length, ms_len);
+
+	wpabuf_put_u8(req, CHALLENGE_LEN);
+	if (!data->auth_challenge_from_tls)
+		wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
+	else
+		wpabuf_put(req, CHALLENGE_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
+		    data->auth_challenge, CHALLENGE_LEN);
+	wpabuf_put_data(req, sm->server_id, sm->server_id_len);
+
+	return req;
+}
+
+
+static struct wpabuf * eap_mschapv2_build_success_req(
+	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
+{
+	struct wpabuf *req;
+	struct eap_mschapv2_hdr *ms;
+	u8 *msg;
+	char *message = "OK";
+	size_t ms_len;
+
+	ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
+		os_strlen(message);
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
+			   " for request");
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	ms = wpabuf_put(req, sizeof(*ms));
+	ms->op_code = MSCHAPV2_OP_SUCCESS;
+	ms->mschapv2_id = data->resp_mschapv2_id;
+	WPA_PUT_BE16(ms->ms_length, ms_len);
+	msg = (u8 *) (ms + 1);
+
+	wpabuf_put_u8(req, 'S');
+	wpabuf_put_u8(req, '=');
+	wpa_snprintf_hex_uppercase(
+		wpabuf_put(req, sizeof(data->auth_response) * 2),
+		sizeof(data->auth_response) * 2 + 1,
+		data->auth_response, sizeof(data->auth_response));
+	wpabuf_put_u8(req, ' ');
+	wpabuf_put_u8(req, 'M');
+	wpabuf_put_u8(req, '=');
+	wpabuf_put_data(req, message, os_strlen(message));
+
+	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
+			  msg, ms_len - sizeof(*ms));
+
+	return req;
+}
+
+
+static struct wpabuf * eap_mschapv2_build_failure_req(
+	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
+{
+	struct wpabuf *req;
+	struct eap_mschapv2_hdr *ms;
+	char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
+		"M=FAILED";
+	size_t ms_len;
+
+	ms_len = sizeof(*ms) + os_strlen(message);
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
+			   " for request");
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	ms = wpabuf_put(req, sizeof(*ms));
+	ms->op_code = MSCHAPV2_OP_FAILURE;
+	ms->mschapv2_id = data->resp_mschapv2_id;
+	WPA_PUT_BE16(ms->ms_length, ms_len);
+
+	wpabuf_put_data(req, message, os_strlen(message));
+
+	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
+			  (u8 *) message, os_strlen(message));
+
+	return req;
+}
+
+
+static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
+					     u8 id)
+{
+	struct eap_mschapv2_data *data = priv;
+
+	switch (data->state) {
+	case CHALLENGE:
+		return eap_mschapv2_build_challenge(sm, data, id);
+	case SUCCESS_REQ:
+		return eap_mschapv2_build_success_req(sm, data, id);
+	case FAILURE_REQ:
+		return eap_mschapv2_build_failure_req(sm, data, id);
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
+			   "buildReq", data->state);
+		break;
+	}
+	return NULL;
+}
+
+
+static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
+				  struct wpabuf *respData)
+{
+	struct eap_mschapv2_data *data = priv;
+	struct eap_mschapv2_hdr *resp;
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
+			       &len);
+	if (pos == NULL || len < 1) {
+		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
+		return TRUE;
+	}
+
+	resp = (struct eap_mschapv2_hdr *) pos;
+	if (data->state == CHALLENGE &&
+	    resp->op_code != MSCHAPV2_OP_RESPONSE) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
+			   "ignore op %d", resp->op_code);
+		return TRUE;
+	}
+
+	if (data->state == SUCCESS_REQ &&
+	    resp->op_code != MSCHAPV2_OP_SUCCESS &&
+	    resp->op_code != MSCHAPV2_OP_FAILURE) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
+			   "Failure - ignore op %d", resp->op_code);
+		return TRUE;
+	}
+
+	if (data->state == FAILURE_REQ &&
+	    resp->op_code != MSCHAPV2_OP_FAILURE) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
+			   "- ignore op %d", resp->op_code);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static void eap_mschapv2_process_response(struct eap_sm *sm,
+					  struct eap_mschapv2_data *data,
+					  struct wpabuf *respData)
+{
+	struct eap_mschapv2_hdr *resp;
+	const u8 *pos, *end, *peer_challenge, *nt_response, *name;
+	u8 flags;
+	size_t len, name_len, i;
+	u8 expected[24];
+	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);
+	if (pos == NULL || len < 1)
+		return; /* Should not happen - frame already validated */
+
+	end = pos + len;
+	resp = (struct eap_mschapv2_hdr *) pos;
+	pos = (u8 *) (resp + 1);
+
+	if (len < sizeof(*resp) + 1 + 49 ||
+	    resp->op_code != MSCHAPV2_OP_RESPONSE ||
+	    pos[0] != 49) {
+		wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
+				respData);
+		data->state = FAILURE;
+		return;
+	}
+	data->resp_mschapv2_id = resp->mschapv2_id;
+	pos++;
+	peer_challenge = pos;
+	pos += 16 + 8;
+	nt_response = pos;
+	pos += 24;
+	flags = *pos++;
+	name = pos;
+	name_len = end - name;
+
+	if (data->peer_challenge) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured "
+			   "Peer-Challenge");
+		peer_challenge = data->peer_challenge;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
+		    peer_challenge, 16);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24);
+	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). */
+	username = sm->identity;
+	username_len = sm->identity_len;
+	for (i = 0; i < username_len; i++) {
+		if (username[i] == '\\') {
+			username_len -= i + 1;
+			username += i + 1;
+			break;
+		}
+	}
+
+	user = name;
+	user_len = name_len;
+	for (i = 0; i < user_len; i++) {
+		if (user[i] == '\\') {
+			user_len -= i + 1;
+			user += i + 1;
+			break;
+		}
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	{
+		u8 challenge[8];
+
+		if (challenge_hash(peer_challenge, data->auth_challenge,
+				   username, username_len, challenge) == 0) {
+			eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2",
+						      username, username_len,
+						      challenge, nt_response);
+		}
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (username_len != user_len ||
+	    os_memcmp(username, user, username_len) != 0) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
+				  "name", username, username_len);
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
+				  "name", user, user_len);
+		data->state = FAILURE;
+		return;
+	}
+
+	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
+			  username, username_len);
+
+	if (sm->user->password_hash) {
+		res = generate_nt_response_pwhash(data->auth_challenge,
+						  peer_challenge,
+						  username, username_len,
+						  sm->user->password,
+						  expected);
+	} else {
+		res = generate_nt_response(data->auth_challenge,
+					   peer_challenge,
+					   username, username_len,
+					   sm->user->password,
+					   sm->user->password_len,
+					   expected);
+	}
+	if (res) {
+		data->state = FAILURE;
+		return;
+	}
+
+	if (os_memcmp_const(nt_response, expected, 24) == 0) {
+		const u8 *pw_hash;
+		u8 pw_hash_buf[16], pw_hash_hash[16];
+
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
+		data->state = SUCCESS_REQ;
+
+		/* Authenticator response is not really needed yet, but
+		 * calculate it here so that peer_challenge and username need
+		 * not be saved. */
+		if (sm->user->password_hash) {
+			pw_hash = sm->user->password;
+		} else {
+			if (nt_password_hash(sm->user->password,
+					     sm->user->password_len,
+					     pw_hash_buf) < 0) {
+				data->state = FAILURE;
+				return;
+			}
+			pw_hash = pw_hash_buf;
+		}
+		if (generate_authenticator_response_pwhash(
+			    pw_hash, peer_challenge, data->auth_challenge,
+			    username, username_len, nt_response,
+			    data->auth_response) < 0 ||
+		    hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 ||
+		    get_master_key(pw_hash_hash, nt_response,
+				   data->master_key)) {
+			data->state = FAILURE;
+			return;
+		}
+		data->master_key_valid = 1;
+		wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
+				data->master_key, MSCHAPV2_KEY_LEN);
+	} else {
+		wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
+			    expected, 24);
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
+		data->state = FAILURE_REQ;
+	}
+}
+
+
+static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
+					      struct eap_mschapv2_data *data,
+					      struct wpabuf *respData)
+{
+	struct eap_mschapv2_hdr *resp;
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
+			       &len);
+	if (pos == NULL || len < 1)
+		return; /* Should not happen - frame already validated */
+
+	resp = (struct eap_mschapv2_hdr *) pos;
+
+	if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
+			   " - authentication completed successfully");
+		data->state = SUCCESS;
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
+			   "Response - peer rejected authentication");
+		data->state = FAILURE;
+	}
+}
+
+
+static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
+					      struct eap_mschapv2_data *data,
+					      struct wpabuf *respData)
+{
+	struct eap_mschapv2_hdr *resp;
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
+			       &len);
+	if (pos == NULL || len < 1)
+		return; /* Should not happen - frame already validated */
+
+	resp = (struct eap_mschapv2_hdr *) pos;
+
+	if (resp->op_code == MSCHAPV2_OP_FAILURE) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
+			   " - authentication failed");
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
+			   "Response - authentication failed");
+	}
+
+	data->state = FAILURE;
+}
+
+
+static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
+				 struct wpabuf *respData)
+{
+	struct eap_mschapv2_data *data = priv;
+
+	if (sm->user == NULL || sm->user->password == NULL) {
+		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
+		data->state = FAILURE;
+		return;
+	}
+
+	switch (data->state) {
+	case CHALLENGE:
+		eap_mschapv2_process_response(sm, data, respData);
+		break;
+	case SUCCESS_REQ:
+		eap_mschapv2_process_success_resp(sm, data, respData);
+		break;
+	case FAILURE_REQ:
+		eap_mschapv2_process_failure_resp(sm, data, respData);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
+			   "process", data->state);
+		break;
+	}
+}
+
+
+static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_mschapv2_data *data = priv;
+	return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_mschapv2_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS || !data->master_key_valid)
+		return NULL;
+
+	*len = 2 * MSCHAPV2_KEY_LEN;
+	key = os_malloc(*len);
+	if (key == NULL)
+		return NULL;
+	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
+	get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1);
+	get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
+				MSCHAPV2_KEY_LEN, 1, 1);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
+
+	return key;
+}
+
+
+static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_mschapv2_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+int eap_server_mschapv2_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
+				      "MSCHAPV2");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_mschapv2_init;
+	eap->reset = eap_mschapv2_reset;
+	eap->buildReq = eap_mschapv2_buildReq;
+	eap->check = eap_mschapv2_check;
+	eap->process = eap_mschapv2_process;
+	eap->isDone = eap_mschapv2_isDone;
+	eap->getKey = eap_mschapv2_getKey;
+	eap->isSuccess = eap_mschapv2_isSuccess;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_pax.c b/hostap/src/eap_server/eap_server_pax.c
new file mode 100644
index 0000000..0e6b4a0
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_pax.c
@@ -0,0 +1,590 @@
+/*
+ * hostapd / EAP-PAX (RFC 4746) server
+ * Copyright (c) 2005-2007, 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/random.h"
+#include "eap_server/eap_i.h"
+#include "eap_common/eap_pax_common.h"
+
+/*
+ * Note: only PAX_STD subprotocol is currently supported
+ *
+ * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
+ * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
+ * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
+ * RSAES-OAEP).
+ */
+
+struct eap_pax_data {
+	enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state;
+	u8 mac_id;
+	union {
+		u8 e[2 * EAP_PAX_RAND_LEN];
+		struct {
+			u8 x[EAP_PAX_RAND_LEN]; /* server rand */
+			u8 y[EAP_PAX_RAND_LEN]; /* client rand */
+		} r;
+	} rand;
+	u8 ak[EAP_PAX_AK_LEN];
+	u8 mk[EAP_PAX_MK_LEN];
+	u8 ck[EAP_PAX_CK_LEN];
+	u8 ick[EAP_PAX_ICK_LEN];
+	u8 mid[EAP_PAX_MID_LEN];
+	int keys_set;
+	char *cid;
+	size_t cid_len;
+};
+
+
+static void * eap_pax_init(struct eap_sm *sm)
+{
+	struct eap_pax_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = PAX_STD_1;
+	/*
+	 * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is
+	 * supported
+	 */
+	data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128;
+
+	return data;
+}
+
+
+static void eap_pax_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_pax_data *data = priv;
+	os_free(data->cid);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm,
+					   struct eap_pax_data *data, u8 id)
+{
+	struct wpabuf *req;
+	struct eap_pax_hdr *pax;
+	u8 *pos;
+
+	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)");
+
+	if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) {
+		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
+			    sizeof(*pax) + 2 + EAP_PAX_RAND_LEN +
+			    EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
+			   "request");
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	pax = wpabuf_put(req, sizeof(*pax));
+	pax->op_code = EAP_PAX_OP_STD_1;
+	pax->flags = 0;
+	pax->mac_id = data->mac_id;
+	pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
+	pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
+
+	wpabuf_put_be16(req, EAP_PAX_RAND_LEN);
+	wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)",
+		    data->rand.r.x, EAP_PAX_RAND_LEN);
+
+	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
+	eap_pax_mac(data->mac_id, (u8 *) "", 0,
+		    wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
+		    NULL, 0, NULL, 0, pos);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
+
+	return req;
+}
+
+
+static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm,
+					   struct eap_pax_data *data, u8 id)
+{
+	struct wpabuf *req;
+	struct eap_pax_hdr *pax;
+	u8 *pos;
+
+	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)");
+
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
+			    sizeof(*pax) + 2 + EAP_PAX_MAC_LEN +
+			    EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
+			   "request");
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	pax = wpabuf_put(req, sizeof(*pax));
+	pax->op_code = EAP_PAX_OP_STD_3;
+	pax->flags = 0;
+	pax->mac_id = data->mac_id;
+	pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
+	pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
+
+	wpabuf_put_be16(req, EAP_PAX_MAC_LEN);
+	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
+	eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
+		    data->rand.r.y, EAP_PAX_RAND_LEN,
+		    (u8 *) data->cid, data->cid_len, NULL, 0, pos);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
+		    pos, EAP_PAX_MAC_LEN);
+
+	/* Optional ADE could be added here, if needed */
+
+	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
+	eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+		    wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
+		    NULL, 0, NULL, 0, pos);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
+
+	return req;
+}
+
+
+static struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_pax_data *data = priv;
+
+	switch (data->state) {
+	case PAX_STD_1:
+		return eap_pax_build_std_1(sm, data, id);
+	case PAX_STD_3:
+		return eap_pax_build_std_3(sm, data, id);
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq",
+			   data->state);
+		break;
+	}
+	return NULL;
+}
+
+
+static Boolean eap_pax_check(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_pax_data *data = priv;
+	struct eap_pax_hdr *resp;
+	const u8 *pos;
+	size_t len, mlen;
+	u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
+	if (pos == NULL || len < sizeof(*resp)) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
+		return TRUE;
+	}
+
+	mlen = sizeof(struct eap_hdr) + 1 + len;
+	resp = (struct eap_pax_hdr *) pos;
+
+	wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
+		   "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
+		   "public_key_id 0x%x",
+		   resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id,
+		   resp->public_key_id);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
+		    (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN);
+
+	if (data->state == PAX_STD_1 &&
+	    resp->op_code != EAP_PAX_OP_STD_2) {
+		wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - "
+			   "ignore op %d", resp->op_code);
+		return TRUE;
+	}
+
+	if (data->state == PAX_STD_3 &&
+	    resp->op_code != EAP_PAX_OP_ACK) {
+		wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - "
+			   "ignore op %d", resp->op_code);
+		return TRUE;
+	}
+
+	if (resp->op_code != EAP_PAX_OP_STD_2 &&
+	    resp->op_code != EAP_PAX_OP_ACK) {
+		wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x",
+			   resp->op_code);
+	}
+
+	if (data->mac_id != resp->mac_id) {
+		wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, "
+			   "received 0x%x", data->mac_id, resp->mac_id);
+		return TRUE;
+	}
+
+	if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, "
+			   "received 0x%x", EAP_PAX_DH_GROUP_NONE,
+			   resp->dh_group_id);
+		return TRUE;
+	}
+
+	if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, "
+			   "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE,
+			   resp->public_key_id);
+		return TRUE;
+	}
+
+	if (resp->flags & EAP_PAX_FLAGS_MF) {
+		/* TODO: add support for reassembling fragments */
+		wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported");
+		return TRUE;
+	}
+
+	if (resp->flags & EAP_PAX_FLAGS_CE) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag");
+		return TRUE;
+	}
+
+	if (data->keys_set) {
+		if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) {
+			wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet");
+			return TRUE;
+		}
+		icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN;
+		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
+		eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+			    wpabuf_mhead(respData),
+			    wpabuf_len(respData) - EAP_PAX_ICV_LEN,
+			    NULL, 0, NULL, 0, icvbuf);
+		if (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);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+
+static void eap_pax_process_std_2(struct eap_sm *sm,
+				  struct eap_pax_data *data,
+				  struct wpabuf *respData)
+{
+	struct eap_pax_hdr *resp;
+	u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN];
+	const u8 *pos;
+	size_t len, left, cid_len;
+	int i;
+
+	if (data->state != PAX_STD_1)
+		return;
+
+	wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2");
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
+	if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN)
+		return;
+
+	resp = (struct eap_pax_hdr *) pos;
+	pos = (u8 *) (resp + 1);
+	left = len - sizeof(*resp);
+
+	if (left < 2 + EAP_PAX_RAND_LEN ||
+	    WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)");
+		return;
+	}
+	pos += 2;
+	left -= 2;
+	os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
+		    data->rand.r.y, EAP_PAX_RAND_LEN);
+	pos += EAP_PAX_RAND_LEN;
+	left -= EAP_PAX_RAND_LEN;
+
+	if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)");
+		return;
+	}
+	cid_len = WPA_GET_BE16(pos);
+	if (cid_len > 1500) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Too long CID");
+		return;
+	}
+	data->cid_len = cid_len;
+	os_free(data->cid);
+	data->cid = os_malloc(data->cid_len);
+	if (data->cid == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
+			   "CID");
+		return;
+	}
+	os_memcpy(data->cid, pos + 2, data->cid_len);
+	pos += 2 + data->cid_len;
+	left -= 2 + data->cid_len;
+	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
+			  (u8 *) data->cid, data->cid_len);
+
+	if (left < 2 + EAP_PAX_MAC_LEN ||
+	    WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)");
+		return;
+	}
+	pos += 2;
+	left -= 2;
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
+		    pos, EAP_PAX_MAC_LEN);
+
+	if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID",
+				  (u8 *) data->cid, data->cid_len);
+		data->state = FAILURE;
+		return;
+	}
+
+	for (i = 0;
+	     i < EAP_MAX_METHODS &&
+		     (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+		      sm->user->methods[i].method != EAP_TYPE_NONE);
+	     i++) {
+		if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
+		    sm->user->methods[i].method == EAP_TYPE_PAX)
+			break;
+	}
+
+	if (i >= EAP_MAX_METHODS ||
+	    sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+	    sm->user->methods[i].method != EAP_TYPE_PAX) {
+		wpa_hexdump_ascii(MSG_DEBUG,
+				  "EAP-PAX: EAP-PAX not enabled for CID",
+				  (u8 *) data->cid, data->cid_len);
+		data->state = FAILURE;
+		return;
+	}
+
+	if (sm->user->password == NULL ||
+	    sm->user->password_len != EAP_PAX_AK_LEN) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in "
+				  "user database for CID",
+				  (u8 *) data->cid, data->cid_len);
+		data->state = FAILURE;
+		return;
+	}
+	os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN);
+
+	if (eap_pax_initial_key_derivation(data->mac_id, data->ak,
+					   data->rand.e, data->mk, data->ck,
+					   data->ick, data->mid) < 0) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
+			   "key derivation");
+		data->state = FAILURE;
+		return;
+	}
+	data->keys_set = 1;
+
+	eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
+		    data->rand.r.x, EAP_PAX_RAND_LEN,
+		    data->rand.r.y, EAP_PAX_RAND_LEN,
+		    (u8 *) data->cid, data->cid_len, mac);
+	if (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)",
+			    mac, EAP_PAX_MAC_LEN);
+		data->state = FAILURE;
+		return;
+	}
+
+	pos += EAP_PAX_MAC_LEN;
+	left -= EAP_PAX_MAC_LEN;
+
+	if (left < EAP_PAX_ICV_LEN) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in "
+			   "PAX_STD-2", (unsigned long) left);
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
+	eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+		    wpabuf_head(respData),
+		    wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0,
+		    icvbuf);
+	if (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);
+		return;
+	}
+	pos += EAP_PAX_ICV_LEN;
+	left -= EAP_PAX_ICV_LEN;
+
+	if (left > 0) {
+		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
+			    pos, left);
+	}
+
+	data->state = PAX_STD_3;
+}
+
+
+static void eap_pax_process_ack(struct eap_sm *sm,
+				struct eap_pax_data *data,
+				struct wpabuf *respData)
+{
+	if (data->state != PAX_STD_3)
+		return;
+
+	wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication "
+		   "completed successfully");
+	data->state = SUCCESS;
+}
+
+
+static void eap_pax_process(struct eap_sm *sm, void *priv,
+			    struct wpabuf *respData)
+{
+	struct eap_pax_data *data = priv;
+	struct eap_pax_hdr *resp;
+	const u8 *pos;
+	size_t len;
+
+	if (sm->user == NULL || sm->user->password == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not "
+			   "configured");
+		data->state = FAILURE;
+		return;
+	}
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
+	if (pos == NULL || len < sizeof(*resp))
+		return;
+
+	resp = (struct eap_pax_hdr *) pos;
+
+	switch (resp->op_code) {
+	case EAP_PAX_OP_STD_2:
+		eap_pax_process_std_2(sm, data, respData);
+		break;
+	case EAP_PAX_OP_ACK:
+		eap_pax_process_ack(sm, data, respData);
+		break;
+	}
+}
+
+
+static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_pax_data *data = priv;
+	return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_pax_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_MSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_MSK_LEN;
+	eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
+		    "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
+		    EAP_MSK_LEN, key);
+
+	return key;
+}
+
+
+static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_pax_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_EMSK_LEN;
+	eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
+		    "Extended Master Session Key",
+		    data->rand.e, 2 * EAP_PAX_RAND_LEN,
+		    EAP_EMSK_LEN, key);
+
+	return key;
+}
+
+
+static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_pax_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_pax_data *data = priv;
+	u8 *sid;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	sid = os_malloc(1 + EAP_PAX_MID_LEN);
+	if (sid == NULL)
+		return NULL;
+
+	*len = 1 + EAP_PAX_MID_LEN;
+	sid[0] = EAP_TYPE_PAX;
+	os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN);
+
+	return sid;
+}
+
+
+int eap_server_pax_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_pax_init;
+	eap->reset = eap_pax_reset;
+	eap->buildReq = eap_pax_buildReq;
+	eap->check = eap_pax_check;
+	eap->process = eap_pax_process;
+	eap->isDone = eap_pax_isDone;
+	eap->getKey = eap_pax_getKey;
+	eap->isSuccess = eap_pax_isSuccess;
+	eap->get_emsk = eap_pax_get_emsk;
+	eap->getSessionId = eap_pax_get_session_id;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_peap.c b/hostap/src/eap_server/eap_server_peap.c
new file mode 100644
index 0000000..51062b0
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_peap.c
@@ -0,0 +1,1375 @@
+/*
+ * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "crypto/random.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_common/eap_peap_common.h"
+#include "tncs.h"
+
+
+/* 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
+ */
+#define EAP_PEAP_VERSION 1
+
+
+static void eap_peap_reset(struct eap_sm *sm, void *priv);
+
+
+struct eap_peap_data {
+	struct eap_ssl_data ssl;
+	enum {
+		START, PHASE1, PHASE1_ID2, PHASE2_START, PHASE2_ID,
+		PHASE2_METHOD, PHASE2_SOH,
+		PHASE2_TLV, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE
+	} state;
+
+	int peap_version;
+	int recv_version;
+	const struct eap_method *phase2_method;
+	void *phase2_priv;
+	int force_version;
+	struct wpabuf *pending_phase2_resp;
+	enum { TLV_REQ_NONE, TLV_REQ_SUCCESS, TLV_REQ_FAILURE } tlv_request;
+	int crypto_binding_sent;
+	int crypto_binding_used;
+	enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
+	u8 binding_nonce[32];
+	u8 ipmk[40];
+	u8 cmk[20];
+	u8 *phase2_key;
+	size_t phase2_key_len;
+	struct wpabuf *soh_response;
+};
+
+
+static const char * eap_peap_state_txt(int state)
+{
+	switch (state) {
+	case START:
+		return "START";
+	case PHASE1:
+		return "PHASE1";
+	case PHASE1_ID2:
+		return "PHASE1_ID2";
+	case PHASE2_START:
+		return "PHASE2_START";
+	case PHASE2_ID:
+		return "PHASE2_ID";
+	case PHASE2_METHOD:
+		return "PHASE2_METHOD";
+	case PHASE2_SOH:
+		return "PHASE2_SOH";
+	case PHASE2_TLV:
+		return "PHASE2_TLV";
+	case SUCCESS_REQ:
+		return "SUCCESS_REQ";
+	case FAILURE_REQ:
+		return "FAILURE_REQ";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "Unknown?!";
+	}
+}
+
+
+static void eap_peap_state(struct eap_peap_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-PEAP: %s -> %s",
+		   eap_peap_state_txt(data->state),
+		   eap_peap_state_txt(state));
+	data->state = state;
+	if (state == FAILURE || state == FAILURE_REQ)
+		tls_connection_remove_session(data->ssl.conn);
+}
+
+
+static void eap_peap_valid_session(struct eap_sm *sm,
+				   struct eap_peap_data *data)
+{
+	struct wpabuf *buf;
+
+	if (!sm->tls_session_lifetime ||
+	    tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+		return;
+
+	buf = wpabuf_alloc(1 + 1 + sm->identity_len);
+	if (!buf)
+		return;
+	wpabuf_put_u8(buf, EAP_TYPE_PEAP);
+	if (sm->identity) {
+		u8 id_len;
+
+		if (sm->identity_len <= 255)
+			id_len = sm->identity_len;
+		else
+			id_len = 255;
+		wpabuf_put_u8(buf, id_len);
+		wpabuf_put_data(buf, sm->identity, id_len);
+	} else {
+		wpabuf_put_u8(buf, 0);
+	}
+	tls_connection_set_success_data(data->ssl.conn, buf);
+}
+
+
+static void eap_peap_req_success(struct eap_sm *sm,
+				 struct eap_peap_data *data)
+{
+	if (data->state == FAILURE || data->state == FAILURE_REQ) {
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	if (data->peap_version == 0) {
+		data->tlv_request = TLV_REQ_SUCCESS;
+		eap_peap_state(data, PHASE2_TLV);
+	} else {
+		eap_peap_state(data, SUCCESS_REQ);
+	}
+}
+
+
+static void eap_peap_req_failure(struct eap_sm *sm,
+				 struct eap_peap_data *data)
+{
+	if (data->state == FAILURE || data->state == FAILURE_REQ ||
+	    data->state == SUCCESS_REQ || data->tlv_request != TLV_REQ_NONE) {
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	if (data->peap_version == 0) {
+		data->tlv_request = TLV_REQ_FAILURE;
+		eap_peap_state(data, PHASE2_TLV);
+	} else {
+		eap_peap_state(data, FAILURE_REQ);
+	}
+}
+
+
+static void * eap_peap_init(struct eap_sm *sm)
+{
+	struct eap_peap_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->peap_version = EAP_PEAP_VERSION;
+	data->force_version = -1;
+	if (sm->user && sm->user->force_version >= 0) {
+		data->force_version = sm->user->force_version;
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: forcing version %d",
+			   data->force_version);
+		data->peap_version = data->force_version;
+	}
+	data->state = START;
+	data->crypto_binding = OPTIONAL_BINDING;
+
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_PEAP)) {
+		wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
+		eap_peap_reset(sm, data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+static void eap_peap_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_peap_data *data = priv;
+	if (data == NULL)
+		return;
+	if (data->phase2_priv && data->phase2_method)
+		data->phase2_method->reset(sm, data->phase2_priv);
+	eap_server_tls_ssl_deinit(sm, &data->ssl);
+	wpabuf_free(data->pending_phase2_resp);
+	os_free(data->phase2_key);
+	wpabuf_free(data->soh_response);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_peap_build_start(struct eap_sm *sm,
+					    struct eap_peap_data *data, u8 id)
+{
+	struct wpabuf *req;
+
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PEAP, 1,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to allocate memory for"
+			   " request");
+		eap_peap_state(data, FAILURE);
+		return NULL;
+	}
+
+	wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->peap_version);
+
+	eap_peap_state(data, PHASE1);
+
+	return req;
+}
+
+
+static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm,
+						 struct eap_peap_data *data,
+						 u8 id)
+{
+	struct wpabuf *buf, *encr_req, msgbuf;
+	const u8 *req;
+	size_t req_len;
+
+	if (data->phase2_method == NULL || data->phase2_priv == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 method not ready");
+		return NULL;
+	}
+	buf = data->phase2_method->buildReq(sm, data->phase2_priv, id);
+	if (buf == NULL)
+		return NULL;
+
+	req = wpabuf_head(buf);
+	req_len = wpabuf_len(buf);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data",
+			req, req_len);
+
+	if (data->peap_version == 0 &&
+	    data->phase2_method->method != EAP_TYPE_TLV) {
+		req += sizeof(struct eap_hdr);
+		req_len -= sizeof(struct eap_hdr);
+	}
+
+	wpabuf_set(&msgbuf, req, req_len);
+	encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf);
+	wpabuf_free(buf);
+
+	return encr_req;
+}
+
+
+#ifdef EAP_SERVER_TNC
+static struct wpabuf * eap_peap_build_phase2_soh(struct eap_sm *sm,
+						 struct eap_peap_data *data,
+						 u8 id)
+{
+	struct wpabuf *buf1, *buf, *encr_req, msgbuf;
+	const u8 *req;
+	size_t req_len;
+
+	buf1 = tncs_build_soh_request();
+	if (buf1 == NULL)
+		return NULL;
+
+	buf = eap_msg_alloc(EAP_VENDOR_MICROSOFT, 0x21, wpabuf_len(buf1),
+			    EAP_CODE_REQUEST, id);
+	if (buf == NULL) {
+		wpabuf_free(buf1);
+		return NULL;
+	}
+	wpabuf_put_buf(buf, buf1);
+	wpabuf_free(buf1);
+
+	req = wpabuf_head(buf);
+	req_len = wpabuf_len(buf);
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 SOH data",
+			req, req_len);
+
+	req += sizeof(struct eap_hdr);
+	req_len -= sizeof(struct eap_hdr);
+	wpabuf_set(&msgbuf, req, req_len);
+
+	encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf);
+	wpabuf_free(buf);
+
+	return encr_req;
+}
+#endif /* EAP_SERVER_TNC */
+
+
+static void eap_peap_get_isk(struct eap_peap_data *data,
+			     u8 *isk, size_t isk_len)
+{
+	size_t key_len;
+
+	os_memset(isk, 0, isk_len);
+	if (data->phase2_key == NULL)
+		return;
+
+	key_len = data->phase2_key_len;
+	if (key_len > isk_len)
+		key_len = isk_len;
+	os_memcpy(isk, data->phase2_key, key_len);
+}
+
+
+static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
+{
+	u8 *tk;
+	u8 isk[32], imck[60];
+
+	/*
+	 * Tunnel key (TK) is the first 60 octets of the key generated by
+	 * phase 1 of PEAP (based on TLS).
+	 */
+	tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption",
+				       EAP_TLS_KEY_LEN);
+	if (tk == NULL)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
+
+	eap_peap_get_isk(data, isk, sizeof(isk));
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));
+
+	/*
+	 * IPMK Seed = "Inner Methods Compound Keys" | ISK
+	 * TempKey = First 40 octets of TK
+	 * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60)
+	 * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space
+	 * in the end of the label just before ISK; is that just a typo?)
+	 */
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
+	if (peap_prfplus(data->peap_version, tk, 40,
+			 "Inner Methods Compound Keys",
+			 isk, sizeof(isk), imck, sizeof(imck)) < 0) {
+		os_free(tk);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
+			imck, sizeof(imck));
+
+	os_free(tk);
+
+	/* TODO: fast-connect: IPMK|CMK = TK */
+	os_memcpy(data->ipmk, imck, 40);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
+	os_memcpy(data->cmk, imck + 40, 20);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm,
+						 struct eap_peap_data *data,
+						 u8 id)
+{
+	struct wpabuf *buf, *encr_req;
+	size_t mlen;
+
+	mlen = 6; /* Result TLV */
+	if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS &&
+	    data->crypto_binding != NO_BINDING) {
+		mlen += 60; /* Cryptobinding TLV */
+#ifdef EAP_SERVER_TNC
+		if (data->soh_response)
+			mlen += wpabuf_len(data->soh_response);
+#endif /* EAP_SERVER_TNC */
+	}
+
+	buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen,
+			    EAP_CODE_REQUEST, id);
+	if (buf == NULL)
+		return NULL;
+
+	wpabuf_put_u8(buf, 0x80); /* Mandatory */
+	wpabuf_put_u8(buf, EAP_TLV_RESULT_TLV);
+	/* Length */
+	wpabuf_put_be16(buf, 2);
+	/* Status */
+	wpabuf_put_be16(buf, data->tlv_request == TLV_REQ_SUCCESS ?
+			EAP_TLV_RESULT_SUCCESS : EAP_TLV_RESULT_FAILURE);
+
+	if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS &&
+	    data->crypto_binding != NO_BINDING) {
+		u8 *mac;
+		u8 eap_type = EAP_TYPE_PEAP;
+		const u8 *addr[2];
+		size_t len[2];
+		u16 tlv_type;
+
+#ifdef EAP_SERVER_TNC
+		if (data->soh_response) {
+			wpa_printf(MSG_DEBUG, "EAP-PEAP: Adding MS-SOH "
+				   "Response TLV");
+			wpabuf_put_buf(buf, data->soh_response);
+			wpabuf_free(data->soh_response);
+			data->soh_response = NULL;
+		}
+#endif /* EAP_SERVER_TNC */
+
+		if (eap_peap_derive_cmk(sm, data) < 0 ||
+		    random_get_bytes(data->binding_nonce, 32)) {
+			wpabuf_free(buf);
+			return NULL;
+		}
+
+		/* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
+		addr[0] = wpabuf_put(buf, 0);
+		len[0] = 60;
+		addr[1] = &eap_type;
+		len[1] = 1;
+
+		tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
+		wpabuf_put_be16(buf, tlv_type);
+		wpabuf_put_be16(buf, 56);
+
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		wpabuf_put_u8(buf, data->peap_version); /* Version */
+		wpabuf_put_u8(buf, data->recv_version); /* RecvVersion */
+		wpabuf_put_u8(buf, 0); /* SubType: 0 = Request, 1 = Response */
+		wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */
+		mac = wpabuf_put(buf, 20); /* Compound_MAC */
+		wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK",
+			    data->cmk, 20);
+		wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",
+			    addr[0], len[0]);
+		wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",
+			    addr[1], len[1]);
+		hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);
+		wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC",
+			    mac, SHA1_MAC_LEN);
+		data->crypto_binding_sent = 1;
+	}
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 TLV data",
+			    buf);
+
+	encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf);
+	wpabuf_free(buf);
+
+	return encr_req;
+}
+
+
+static struct wpabuf * eap_peap_build_phase2_term(struct eap_sm *sm,
+						  struct eap_peap_data *data,
+						  u8 id, int success)
+{
+	struct wpabuf *encr_req, msgbuf;
+	size_t req_len;
+	struct eap_hdr *hdr;
+
+	req_len = sizeof(*hdr);
+	hdr = os_zalloc(req_len);
+	if (hdr == NULL)
+		return NULL;
+
+	hdr->code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE;
+	hdr->identifier = id;
+	hdr->length = host_to_be16(req_len);
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data",
+			(u8 *) hdr, req_len);
+
+	wpabuf_set(&msgbuf, hdr, req_len);
+	encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf);
+	os_free(hdr);
+
+	return encr_req;
+}
+
+
+static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_peap_data *data = priv;
+
+	if (data->ssl.state == FRAG_ACK) {
+		return eap_server_tls_build_ack(id, EAP_TYPE_PEAP,
+						data->peap_version);
+	}
+
+	if (data->ssl.state == WAIT_FRAG_ACK) {
+		return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP,
+						data->peap_version, id);
+	}
+
+	switch (data->state) {
+	case START:
+		return eap_peap_build_start(sm, data, id);
+	case PHASE1:
+	case PHASE1_ID2:
+		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);
+		}
+		break;
+	case PHASE2_ID:
+	case PHASE2_METHOD:
+		wpabuf_free(data->ssl.tls_out);
+		data->ssl.tls_out_pos = 0;
+		data->ssl.tls_out = eap_peap_build_phase2_req(sm, data, id);
+		break;
+#ifdef EAP_SERVER_TNC
+	case PHASE2_SOH:
+		wpabuf_free(data->ssl.tls_out);
+		data->ssl.tls_out_pos = 0;
+		data->ssl.tls_out = eap_peap_build_phase2_soh(sm, data, id);
+		break;
+#endif /* EAP_SERVER_TNC */
+	case PHASE2_TLV:
+		wpabuf_free(data->ssl.tls_out);
+		data->ssl.tls_out_pos = 0;
+		data->ssl.tls_out = eap_peap_build_phase2_tlv(sm, data, id);
+		break;
+	case SUCCESS_REQ:
+		wpabuf_free(data->ssl.tls_out);
+		data->ssl.tls_out_pos = 0;
+		data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id,
+							       1);
+		break;
+	case FAILURE_REQ:
+		wpabuf_free(data->ssl.tls_out);
+		data->ssl.tls_out_pos = 0;
+		data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id,
+							       0);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d",
+			   __func__, data->state);
+		return NULL;
+	}
+
+	return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP,
+					data->peap_version, id);
+}
+
+
+static Boolean eap_peap_check(struct eap_sm *sm, void *priv,
+			      struct wpabuf *respData)
+{
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PEAP, respData, &len);
+	if (pos == NULL || len < 1) {
+		wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data,
+				int vendor, EapType eap_type)
+{
+	if (data->phase2_priv && data->phase2_method) {
+		data->phase2_method->reset(sm, data->phase2_priv);
+		data->phase2_method = NULL;
+		data->phase2_priv = NULL;
+	}
+	data->phase2_method = eap_server_get_eap_method(vendor, eap_type);
+	if (!data->phase2_method)
+		return -1;
+
+	sm->init_phase2 = 1;
+	data->phase2_priv = data->phase2_method->init(sm);
+	sm->init_phase2 = 0;
+	return 0;
+}
+
+
+static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
+					  struct eap_peap_data *data,
+					  const u8 *crypto_tlv,
+					  size_t crypto_tlv_len)
+{
+	u8 buf[61], mac[SHA1_MAC_LEN];
+	const u8 *pos;
+
+	if (crypto_tlv_len != 4 + 56) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV "
+			   "length %d", (int) crypto_tlv_len);
+		return -1;
+	}
+
+	pos = crypto_tlv;
+	pos += 4; /* TLV header */
+	if (pos[1] != data->peap_version) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version "
+			   "mismatch (was %d; expected %d)",
+			   pos[1], data->peap_version);
+		return -1;
+	}
+
+	if (pos[3] != 1) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV "
+			   "SubType %d", pos[3]);
+		return -1;
+	}
+	pos += 4;
+	pos += 32; /* Nonce */
+
+	/* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
+	os_memcpy(buf, crypto_tlv, 60);
+	os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */
+	buf[60] = EAP_TYPE_PEAP;
+	hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
+
+	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);
+		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding seed data",
+			    buf, 61);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received");
+
+	return 0;
+}
+
+
+static void eap_peap_process_phase2_tlv(struct eap_sm *sm,
+					struct eap_peap_data *data,
+					struct wpabuf *in_data)
+{
+	const u8 *pos;
+	size_t left;
+	const u8 *result_tlv = NULL, *crypto_tlv = NULL;
+	size_t result_tlv_len = 0, crypto_tlv_len = 0;
+	int tlv_type, mandatory, tlv_len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, in_data, &left);
+	if (pos == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid EAP-TLV header");
+		return;
+	}
+
+	/* Parse TLVs */
+	wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs", pos, left);
+	while (left >= 4) {
+		mandatory = !!(pos[0] & 0x80);
+		tlv_type = pos[0] & 0x3f;
+		tlv_type = (tlv_type << 8) | pos[1];
+		tlv_len = ((int) pos[2] << 8) | pos[3];
+		pos += 4;
+		left -= 4;
+		if ((size_t) tlv_len > left) {
+			wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun "
+				   "(tlv_len=%d left=%lu)", tlv_len,
+				   (unsigned long) left);
+			eap_peap_state(data, FAILURE);
+			return;
+		}
+		switch (tlv_type) {
+		case EAP_TLV_RESULT_TLV:
+			result_tlv = pos;
+			result_tlv_len = tlv_len;
+			break;
+		case EAP_TLV_CRYPTO_BINDING_TLV:
+			crypto_tlv = pos;
+			crypto_tlv_len = tlv_len;
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type "
+				   "%d%s", tlv_type,
+				   mandatory ? " (mandatory)" : "");
+			if (mandatory) {
+				eap_peap_state(data, FAILURE);
+				return;
+			}
+			/* Ignore this TLV, but process other TLVs */
+			break;
+		}
+
+		pos += tlv_len;
+		left -= tlv_len;
+	}
+	if (left) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in "
+			   "Request (left=%lu)", (unsigned long) left);
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	/* Process supported TLVs */
+	if (crypto_tlv && data->crypto_binding_sent) {
+		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV",
+			    crypto_tlv, crypto_tlv_len);
+		if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4,
+						   crypto_tlv_len + 4) < 0) {
+			eap_peap_state(data, FAILURE);
+			return;
+		}
+		data->crypto_binding_used = 1;
+	} else if (!crypto_tlv && data->crypto_binding_sent &&
+		   data->crypto_binding == REQUIRE_BINDING) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	if (result_tlv) {
+		int status;
+		const char *requested;
+
+		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Result TLV",
+			    result_tlv, result_tlv_len);
+		if (result_tlv_len < 2) {
+			wpa_printf(MSG_INFO, "EAP-PEAP: Too short Result TLV "
+				   "(len=%lu)",
+				   (unsigned long) result_tlv_len);
+			eap_peap_state(data, FAILURE);
+			return;
+		}
+		requested = data->tlv_request == TLV_REQ_SUCCESS ? "Success" :
+			"Failure";
+		status = WPA_GET_BE16(result_tlv);
+		if (status == EAP_TLV_RESULT_SUCCESS) {
+			wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Success "
+				   "- requested %s", requested);
+			if (data->tlv_request == TLV_REQ_SUCCESS) {
+				eap_peap_state(data, SUCCESS);
+				eap_peap_valid_session(sm, data);
+			} else {
+				eap_peap_state(data, FAILURE);
+			}
+			
+		} else if (status == EAP_TLV_RESULT_FAILURE) {
+			wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure "
+				   "- requested %s", requested);
+			eap_peap_state(data, FAILURE);
+		} else {
+			wpa_printf(MSG_INFO, "EAP-PEAP: Unknown TLV Result "
+				   "Status %d", status);
+			eap_peap_state(data, FAILURE);
+		}
+	}
+}
+
+
+#ifdef EAP_SERVER_TNC
+static void eap_peap_process_phase2_soh(struct eap_sm *sm,
+					struct eap_peap_data *data,
+					struct wpabuf *in_data)
+{
+	const u8 *pos, *vpos;
+	size_t left;
+	const u8 *soh_tlv = NULL;
+	size_t soh_tlv_len = 0;
+	int tlv_type, mandatory, tlv_len, vtlv_len;
+	u32 next_type;
+	u32 vendor_id;
+
+	pos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, in_data, &left);
+	if (pos == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Not a valid SoH EAP "
+			   "Extensions Method header - skip TNC");
+		goto auth_method;
+	}
+
+	/* Parse TLVs */
+	wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs (SoH)", pos, left);
+	while (left >= 4) {
+		mandatory = !!(pos[0] & 0x80);
+		tlv_type = pos[0] & 0x3f;
+		tlv_type = (tlv_type << 8) | pos[1];
+		tlv_len = ((int) pos[2] << 8) | pos[3];
+		pos += 4;
+		left -= 4;
+		if ((size_t) tlv_len > left) {
+			wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun "
+				   "(tlv_len=%d left=%lu)", tlv_len,
+				   (unsigned long) left);
+			eap_peap_state(data, FAILURE);
+			return;
+		}
+		switch (tlv_type) {
+		case EAP_TLV_VENDOR_SPECIFIC_TLV:
+			if (tlv_len < 4) {
+				wpa_printf(MSG_DEBUG, "EAP-PEAP: Too short "
+					   "vendor specific TLV (len=%d)",
+					   (int) tlv_len);
+				eap_peap_state(data, FAILURE);
+				return;
+			}
+
+			vendor_id = WPA_GET_BE32(pos);
+			if (vendor_id != EAP_VENDOR_MICROSOFT) {
+				if (mandatory) {
+					eap_peap_state(data, FAILURE);
+					return;
+				}
+				break;
+			}
+
+			vpos = pos + 4;
+			mandatory = !!(vpos[0] & 0x80);
+			tlv_type = vpos[0] & 0x3f;
+			tlv_type = (tlv_type << 8) | vpos[1];
+			vtlv_len = ((int) vpos[2] << 8) | vpos[3];
+			vpos += 4;
+			if (vpos + vtlv_len > pos + left) {
+				wpa_printf(MSG_DEBUG, "EAP-PEAP: Vendor TLV "
+					   "underrun");
+				eap_peap_state(data, FAILURE);
+				return;
+			}
+
+			if (tlv_type == 1) {
+				soh_tlv = vpos;
+				soh_tlv_len = vtlv_len;
+				break;
+			}
+
+			wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported MS-TLV "
+				   "Type %d%s", tlv_type,
+				   mandatory ? " (mandatory)" : "");
+			if (mandatory) {
+				eap_peap_state(data, FAILURE);
+				return;
+			}
+			/* Ignore this TLV, but process other TLVs */
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type "
+				   "%d%s", tlv_type,
+				   mandatory ? " (mandatory)" : "");
+			if (mandatory) {
+				eap_peap_state(data, FAILURE);
+				return;
+			}
+			/* Ignore this TLV, but process other TLVs */
+			break;
+		}
+
+		pos += tlv_len;
+		left -= tlv_len;
+	}
+	if (left) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in "
+			   "Request (left=%lu)", (unsigned long) left);
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	/* Process supported TLVs */
+	if (soh_tlv) {
+		int failure = 0;
+		wpabuf_free(data->soh_response);
+		data->soh_response = tncs_process_soh(soh_tlv, soh_tlv_len,
+						      &failure);
+		if (failure) {
+			eap_peap_state(data, FAILURE);
+			return;
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: No SoH TLV received");
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+auth_method:
+	eap_peap_state(data, PHASE2_METHOD);
+	next_type = sm->user->methods[0].method;
+	sm->user_eap_method_index = 1;
+	wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP vendor %d type %d",
+		   sm->user->methods[0].vendor, next_type);
+	eap_peap_phase2_init(sm, data, sm->user->methods[0].vendor, next_type);
+}
+#endif /* EAP_SERVER_TNC */
+
+
+static void eap_peap_process_phase2_response(struct eap_sm *sm,
+					     struct eap_peap_data *data,
+					     struct wpabuf *in_data)
+{
+	int next_vendor = EAP_VENDOR_IETF;
+	u32 next_type = EAP_TYPE_NONE;
+	const struct eap_hdr *hdr;
+	const u8 *pos;
+	size_t left;
+
+	if (data->state == PHASE2_TLV) {
+		eap_peap_process_phase2_tlv(sm, data, in_data);
+		return;
+	}
+
+#ifdef EAP_SERVER_TNC
+	if (data->state == PHASE2_SOH) {
+		eap_peap_process_phase2_soh(sm, data, in_data);
+		return;
+	}
+#endif /* EAP_SERVER_TNC */
+
+	if (data->phase2_priv == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - Phase2 not "
+			   "initialized?!", __func__);
+		return;
+	}
+
+	hdr = wpabuf_head(in_data);
+	pos = (const u8 *) (hdr + 1);
+
+	if (wpabuf_len(in_data) > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
+		left = wpabuf_len(in_data) - sizeof(*hdr);
+		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 type Nak'ed; "
+			    "allowed types", pos + 1, left - 1);
+		eap_sm_process_nak(sm, pos + 1, left - 1);
+		if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+		    (sm->user->methods[sm->user_eap_method_index].vendor !=
+		     EAP_VENDOR_IETF ||
+		     sm->user->methods[sm->user_eap_method_index].method !=
+		     EAP_TYPE_NONE)) {
+			next_vendor = sm->user->methods[
+				sm->user_eap_method_index].vendor;
+			next_type = sm->user->methods[
+				sm->user_eap_method_index++].method;
+			wpa_printf(MSG_DEBUG,
+				   "EAP-PEAP: try EAP vendor %d type 0x%x",
+				   next_vendor, next_type);
+		} else {
+			eap_peap_req_failure(sm, data);
+			next_vendor = EAP_VENDOR_IETF;
+			next_type = EAP_TYPE_NONE;
+		}
+		eap_peap_phase2_init(sm, data, next_vendor, next_type);
+		return;
+	}
+
+	if (data->phase2_method->check(sm, data->phase2_priv, in_data)) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 check() asked to "
+			   "ignore the packet");
+		return;
+	}
+
+	data->phase2_method->process(sm, data->phase2_priv, in_data);
+
+	if (sm->method_pending == METHOD_PENDING_WAIT) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method is in "
+			   "pending wait state - save decrypted response");
+		wpabuf_free(data->pending_phase2_resp);
+		data->pending_phase2_resp = wpabuf_dup(in_data);
+	}
+
+	if (!data->phase2_method->isDone(sm, data->phase2_priv))
+		return;
+
+	if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed");
+		eap_peap_req_failure(sm, data);
+		next_vendor = EAP_VENDOR_IETF;
+		next_type = EAP_TYPE_NONE;
+		eap_peap_phase2_init(sm, data, next_vendor, next_type);
+		return;
+	}
+
+	os_free(data->phase2_key);
+	if (data->phase2_method->getKey) {
+		data->phase2_key = data->phase2_method->getKey(
+			sm, data->phase2_priv, &data->phase2_key_len);
+		if (data->phase2_key == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 getKey "
+				   "failed");
+			eap_peap_req_failure(sm, data);
+			eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF,
+					     EAP_TYPE_NONE);
+			return;
+		}
+	}
+
+	switch (data->state) {
+	case PHASE1_ID2:
+	case PHASE2_ID:
+	case PHASE2_SOH:
+		if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+			wpa_hexdump_ascii(MSG_DEBUG, "EAP_PEAP: Phase2 "
+					  "Identity not found in the user "
+					  "database",
+					  sm->identity, sm->identity_len);
+			eap_peap_req_failure(sm, data);
+			next_vendor = EAP_VENDOR_IETF;
+			next_type = EAP_TYPE_NONE;
+			break;
+		}
+
+#ifdef EAP_SERVER_TNC
+		if (data->state != PHASE2_SOH && sm->tnc &&
+		    data->peap_version == 0) {
+			eap_peap_state(data, PHASE2_SOH);
+			wpa_printf(MSG_DEBUG, "EAP-PEAP: Try to initialize "
+				   "TNC (NAP SOH)");
+			next_vendor = EAP_VENDOR_IETF;
+			next_type = EAP_TYPE_NONE;
+			break;
+		}
+#endif /* EAP_SERVER_TNC */
+
+		eap_peap_state(data, PHASE2_METHOD);
+		next_vendor = sm->user->methods[0].vendor;
+		next_type = sm->user->methods[0].method;
+		sm->user_eap_method_index = 1;
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP vendor %d type 0x%x",
+			   next_vendor, next_type);
+		break;
+	case PHASE2_METHOD:
+		eap_peap_req_success(sm, data);
+		next_vendor = EAP_VENDOR_IETF;
+		next_type = EAP_TYPE_NONE;
+		break;
+	case FAILURE:
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d",
+			   __func__, data->state);
+		break;
+	}
+
+	eap_peap_phase2_init(sm, data, next_vendor, next_type);
+}
+
+
+static void eap_peap_process_phase2(struct eap_sm *sm,
+				    struct eap_peap_data *data,
+				    const struct wpabuf *respData,
+				    struct wpabuf *in_buf)
+{
+	struct wpabuf *in_decrypted;
+	const struct eap_hdr *hdr;
+	size_t len;
+
+	wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"
+		   " Phase 2", (unsigned long) wpabuf_len(in_buf));
+
+	if (data->pending_phase2_resp) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - "
+			   "skip decryption and use old data");
+		eap_peap_process_phase2_response(sm, data,
+						 data->pending_phase2_resp);
+		wpabuf_free(data->pending_phase2_resp);
+		data->pending_phase2_resp = NULL;
+		return;
+	}
+
+	in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+					      in_buf);
+	if (in_decrypted == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 "
+			   "data");
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",
+			    in_decrypted);
+
+	if (data->peap_version == 0 && data->state != PHASE2_TLV) {
+		const struct eap_hdr *resp;
+		struct eap_hdr *nhdr;
+		struct wpabuf *nbuf =
+			wpabuf_alloc(sizeof(struct eap_hdr) +
+				     wpabuf_len(in_decrypted));
+		if (nbuf == NULL) {
+			wpabuf_free(in_decrypted);
+			return;
+		}
+
+		resp = wpabuf_head(respData);
+		nhdr = wpabuf_put(nbuf, sizeof(*nhdr));
+		nhdr->code = resp->code;
+		nhdr->identifier = resp->identifier;
+		nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
+					    wpabuf_len(in_decrypted));
+		wpabuf_put_buf(nbuf, in_decrypted);
+		wpabuf_free(in_decrypted);
+
+		in_decrypted = nbuf;
+	}
+
+	hdr = wpabuf_head(in_decrypted);
+	if (wpabuf_len(in_decrypted) < (int) sizeof(*hdr)) {
+		wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
+			   "EAP frame (len=%lu)",
+			   (unsigned long) wpabuf_len(in_decrypted));
+		wpabuf_free(in_decrypted);
+		eap_peap_req_failure(sm, data);
+		return;
+	}
+	len = be_to_host16(hdr->length);
+	if (len > wpabuf_len(in_decrypted)) {
+		wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
+			   "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
+			   (unsigned long) wpabuf_len(in_decrypted),
+			   (unsigned long) len);
+		wpabuf_free(in_decrypted);
+		eap_peap_req_failure(sm, data);
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
+		   "identifier=%d length=%lu", hdr->code, hdr->identifier,
+		   (unsigned long) len);
+	switch (hdr->code) {
+	case EAP_CODE_RESPONSE:
+		eap_peap_process_phase2_response(sm, data, in_decrypted);
+		break;
+	case EAP_CODE_SUCCESS:
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
+		if (data->state == SUCCESS_REQ) {
+			eap_peap_state(data, SUCCESS);
+			eap_peap_valid_session(sm, data);
+		}
+		break;
+	case EAP_CODE_FAILURE:
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
+		eap_peap_state(data, FAILURE);
+		break;
+	default:
+		wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
+			   "Phase 2 EAP header", hdr->code);
+		break;
+	}
+
+	wpabuf_free(in_decrypted);
+}
+
+
+static int eap_peap_process_version(struct eap_sm *sm, void *priv,
+				    int peer_version)
+{
+	struct eap_peap_data *data = priv;
+
+	data->recv_version = peer_version;
+	if (data->force_version >= 0 && peer_version != data->force_version) {
+		wpa_printf(MSG_INFO, "EAP-PEAP: peer did not select the forced"
+			   " version (forced=%d peer=%d) - reject",
+			   data->force_version, peer_version);
+		return -1;
+	}
+	if (peer_version < data->peap_version) {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: peer ver=%d, own ver=%d; "
+			   "use version %d",
+			   peer_version, data->peap_version, peer_version);
+		data->peap_version = peer_version;
+	}
+
+	return 0;
+}
+
+
+static void eap_peap_process_msg(struct eap_sm *sm, void *priv,
+				 const struct wpabuf *respData)
+{
+	struct eap_peap_data *data = priv;
+
+	switch (data->state) {
+	case PHASE1:
+		if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
+			eap_peap_state(data, FAILURE);
+			break;
+		}
+		break;
+	case PHASE2_START:
+		eap_peap_state(data, PHASE2_ID);
+		eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF,
+				     EAP_TYPE_IDENTITY);
+		break;
+	case PHASE1_ID2:
+	case PHASE2_ID:
+	case PHASE2_METHOD:
+	case PHASE2_SOH:
+	case PHASE2_TLV:
+		eap_peap_process_phase2(sm, data, respData, data->ssl.tls_in);
+		break;
+	case SUCCESS_REQ:
+		eap_peap_state(data, SUCCESS);
+		eap_peap_valid_session(sm, data);
+		break;
+	case FAILURE_REQ:
+		eap_peap_state(data, FAILURE);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected state %d in %s",
+			   data->state, __func__);
+		break;
+	}
+}
+
+
+static void eap_peap_process(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_peap_data *data = priv;
+	const struct wpabuf *buf;
+	const u8 *pos;
+	u8 id_len;
+
+	if (eap_server_tls_process(sm, &data->ssl, respData, data,
+				   EAP_TYPE_PEAP, eap_peap_process_version,
+				   eap_peap_process_msg) < 0) {
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	if (data->state == SUCCESS ||
+	    !tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+	    !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+		return;
+
+	buf = tls_connection_get_success_data(data->ssl.conn);
+	if (!buf || wpabuf_len(buf) < 2) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-PEAP: No success data in resumed session - reject attempt");
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	pos = wpabuf_head(buf);
+	if (*pos != EAP_TYPE_PEAP) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-PEAP: Resumed session for another EAP type (%u) - reject attempt",
+			   *pos);
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	pos++;
+	id_len = *pos++;
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Identity from cached session",
+			  pos, id_len);
+	os_free(sm->identity);
+	sm->identity = os_malloc(id_len ? id_len : 1);
+	if (!sm->identity) {
+		sm->identity_len = 0;
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	os_memcpy(sm->identity, pos, id_len);
+	sm->identity_len = id_len;
+
+	if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Phase2 Identity not found in the user database",
+				  sm->identity, sm->identity_len);
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-PEAP: Resuming previous session - skip Phase2");
+	eap_peap_state(data, SUCCESS_REQ);
+	tls_connection_set_success_data_resumed(data->ssl.conn);
+}
+
+
+static Boolean eap_peap_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_peap_data *data = priv;
+	return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_peap_data *data = priv;
+	u8 *eapKeyData;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	if (data->crypto_binding_used) {
+		u8 csk[128];
+		/*
+		 * Note: It looks like Microsoft implementation requires null
+		 * termination for this label while the one used for deriving
+		 * IPMK|CMK did not use null termination.
+		 */
+		if (peap_prfplus(data->peap_version, data->ipmk, 40,
+				 "Session Key Generating Function",
+				 (u8 *) "\00", 1, csk, sizeof(csk)) < 0)
+			return NULL;
+		wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
+		eapKeyData = os_malloc(EAP_TLS_KEY_LEN);
+		if (eapKeyData) {
+			os_memcpy(eapKeyData, csk, EAP_TLS_KEY_LEN);
+			*len = EAP_TLS_KEY_LEN;
+			wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
+				    eapKeyData, EAP_TLS_KEY_LEN);
+		} else {
+			wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive "
+				   "key");
+		}
+
+		return eapKeyData;
+	}
+
+	/* TODO: PEAPv1 - different label in some cases */
+	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+					       "client EAP encryption",
+					       EAP_TLS_KEY_LEN);
+	if (eapKeyData) {
+		*len = EAP_TLS_KEY_LEN;
+		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
+			    eapKeyData, EAP_TLS_KEY_LEN);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive key");
+	}
+
+	return eapKeyData;
+}
+
+
+static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_peap_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_peap_data *data = priv;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_PEAP,
+						len);
+}
+
+
+int eap_server_peap_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_peap_init;
+	eap->reset = eap_peap_reset;
+	eap->buildReq = eap_peap_buildReq;
+	eap->check = eap_peap_check;
+	eap->process = eap_peap_process;
+	eap->isDone = eap_peap_isDone;
+	eap->getKey = eap_peap_getKey;
+	eap->isSuccess = eap_peap_isSuccess;
+	eap->getSessionId = eap_peap_get_session_id;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_psk.c b/hostap/src/eap_server/eap_server_psk.c
new file mode 100644
index 0000000..12b5d25
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_psk.c
@@ -0,0 +1,535 @@
+/*
+ * hostapd / EAP-PSK (RFC 4764) server
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * Note: EAP-PSK is an EAP authentication method and as such, completely
+ * different from WPA-PSK. This file is not needed for WPA-PSK functionality.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
+#include "eap_common/eap_psk_common.h"
+#include "eap_server/eap_i.h"
+
+
+struct eap_psk_data {
+	enum { PSK_1, PSK_3, SUCCESS, FAILURE } state;
+	u8 rand_s[EAP_PSK_RAND_LEN];
+	u8 rand_p[EAP_PSK_RAND_LEN];
+	u8 *id_p;
+	size_t id_p_len;
+	u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN];
+	u8 msk[EAP_MSK_LEN];
+	u8 emsk[EAP_EMSK_LEN];
+};
+
+
+static void * eap_psk_init(struct eap_sm *sm)
+{
+	struct eap_psk_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = PSK_1;
+
+	return data;
+}
+
+
+static void eap_psk_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_psk_data *data = priv;
+	os_free(data->id_p);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_psk_build_1(struct eap_sm *sm,
+				       struct eap_psk_data *data, u8 id)
+{
+	struct wpabuf *req;
+	struct eap_psk_hdr_1 *psk;
+
+	wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)");
+
+	if (random_get_bytes(data->rand_s, EAP_PSK_RAND_LEN)) {
+		wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
+		data->state = FAILURE;
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_S (server rand)",
+		    data->rand_s, EAP_PSK_RAND_LEN);
+
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK,
+			    sizeof(*psk) + sm->server_id_len,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory "
+			   "request");
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	psk = wpabuf_put(req, sizeof(*psk));
+	psk->flags = EAP_PSK_FLAGS_SET_T(0); /* T=0 */
+	os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN);
+	wpabuf_put_data(req, sm->server_id, sm->server_id_len);
+
+	return req;
+}
+
+
+static struct wpabuf * eap_psk_build_3(struct eap_sm *sm,
+				       struct eap_psk_data *data, u8 id)
+{
+	struct wpabuf *req;
+	struct eap_psk_hdr_3 *psk;
+	u8 *buf, *pchannel, nonce[16];
+	size_t buflen;
+
+	wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-3 (sending)");
+
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK,
+			    sizeof(*psk) + 4 + 16 + 1, EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory "
+			   "request");
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	psk = wpabuf_put(req, sizeof(*psk));
+	psk->flags = EAP_PSK_FLAGS_SET_T(2); /* T=2 */
+	os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN);
+
+	/* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */
+	buflen = sm->server_id_len + EAP_PSK_RAND_LEN;
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		goto fail;
+
+	os_memcpy(buf, sm->server_id, sm->server_id_len);
+	os_memcpy(buf + sm->server_id_len, data->rand_p, EAP_PSK_RAND_LEN);
+	if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) {
+		os_free(buf);
+		goto fail;
+	}
+	os_free(buf);
+
+	if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk,
+				data->emsk))
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN);
+
+	os_memset(nonce, 0, sizeof(nonce));
+	pchannel = wpabuf_put(req, 4 + 16 + 1);
+	os_memcpy(pchannel, nonce + 12, 4);
+	os_memset(pchannel + 4, 0, 16); /* Tag */
+	pchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6;
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (plaintext)",
+		    pchannel, 4 + 16 + 1);
+	if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce),
+				wpabuf_head(req), 22,
+				pchannel + 4 + 16, 1, pchannel + 4))
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (encrypted)",
+		    pchannel, 4 + 16 + 1);
+
+	return req;
+
+fail:
+	wpabuf_free(req);
+	data->state = FAILURE;
+	return NULL;
+}
+
+
+static struct wpabuf * eap_psk_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_psk_data *data = priv;
+
+	switch (data->state) {
+	case PSK_1:
+		return eap_psk_build_1(sm, data, id);
+	case PSK_3:
+		return eap_psk_build_3(sm, data, id);
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-PSK: Unknown state %d in buildReq",
+			   data->state);
+		break;
+	}
+	return NULL;
+}
+
+
+static Boolean eap_psk_check(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_psk_data *data = priv;
+	size_t len;
+	u8 t;
+	const u8 *pos;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &len);
+	if (pos == NULL || len < 1) {
+		wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame");
+		return TRUE;
+	}
+	t = EAP_PSK_FLAGS_GET_T(*pos);
+
+	wpa_printf(MSG_DEBUG, "EAP-PSK: received frame: T=%d", t);
+
+	if (data->state == PSK_1 && t != 1) {
+		wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-2 - "
+			   "ignore T=%d", t);
+		return TRUE;
+	}
+
+	if (data->state == PSK_3 && t != 3) {
+		wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-4 - "
+			   "ignore T=%d", t);
+		return TRUE;
+	}
+
+	if ((t == 1 && len < sizeof(struct eap_psk_hdr_2)) ||
+	    (t == 3 && len < sizeof(struct eap_psk_hdr_4))) {
+		wpa_printf(MSG_DEBUG, "EAP-PSK: Too short frame");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static void eap_psk_process_2(struct eap_sm *sm,
+			      struct eap_psk_data *data,
+			      struct wpabuf *respData)
+{
+	const struct eap_psk_hdr_2 *resp;
+	u8 *pos, mac[EAP_PSK_MAC_LEN], *buf;
+	size_t left, buflen;
+	int i;
+	const u8 *cpos;
+
+	if (data->state != PSK_1)
+		return;
+
+	wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-2");
+
+	cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData,
+				&left);
+	if (cpos == NULL || left < sizeof(*resp)) {
+		wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame");
+		return;
+	}
+	resp = (const struct eap_psk_hdr_2 *) cpos;
+	cpos = (const u8 *) (resp + 1);
+	left -= sizeof(*resp);
+
+	os_free(data->id_p);
+	data->id_p = os_malloc(left);
+	if (data->id_p == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for "
+			   "ID_P");
+		return;
+	}
+	os_memcpy(data->id_p, cpos, left);
+	data->id_p_len = left;
+	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P",
+			  data->id_p, data->id_p_len);
+
+	if (eap_user_get(sm, data->id_p, data->id_p_len, 0) < 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: unknown ID_P",
+				  data->id_p, data->id_p_len);
+		data->state = FAILURE;
+		return;
+	}
+
+	for (i = 0;
+	     i < EAP_MAX_METHODS &&
+		     (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+		      sm->user->methods[i].method != EAP_TYPE_NONE);
+	     i++) {
+		if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
+		    sm->user->methods[i].method == EAP_TYPE_PSK)
+			break;
+	}
+
+	if (i >= EAP_MAX_METHODS ||
+	    sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+	    sm->user->methods[i].method != EAP_TYPE_PSK) {
+		wpa_hexdump_ascii(MSG_DEBUG,
+				  "EAP-PSK: EAP-PSK not enabled for ID_P",
+				  data->id_p, data->id_p_len);
+		data->state = FAILURE;
+		return;
+	}
+
+	if (sm->user->password == NULL ||
+	    sm->user->password_len != EAP_PSK_PSK_LEN) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: invalid password in "
+				  "user database for ID_P",
+				  data->id_p, data->id_p_len);
+		data->state = FAILURE;
+		return;
+	}
+	if (eap_psk_key_setup(sm->user->password, data->ak, data->kdk)) {
+		data->state = FAILURE;
+		return;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN);
+
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_P (client rand)",
+		    resp->rand_p, EAP_PSK_RAND_LEN);
+	os_memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN);
+
+	/* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */
+	buflen = data->id_p_len + sm->server_id_len + 2 * EAP_PSK_RAND_LEN;
+	buf = os_malloc(buflen);
+	if (buf == NULL) {
+		data->state = FAILURE;
+		return;
+	}
+	os_memcpy(buf, data->id_p, data->id_p_len);
+	pos = buf + data->id_p_len;
+	os_memcpy(pos, sm->server_id, sm->server_id_len);
+	pos += sm->server_id_len;
+	os_memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN);
+	pos += EAP_PSK_RAND_LEN;
+	os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN);
+	if (omac1_aes_128(data->ak, buf, buflen, mac)) {
+		os_free(buf);
+		data->state = FAILURE;
+		return;
+	}
+	os_free(buf);
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", resp->mac_p, EAP_PSK_MAC_LEN);
+	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);
+		data->state = FAILURE;
+		return;
+	}
+
+	data->state = PSK_3;
+}
+
+
+static void eap_psk_process_4(struct eap_sm *sm,
+			      struct eap_psk_data *data,
+			      struct wpabuf *respData)
+{
+	const struct eap_psk_hdr_4 *resp;
+	u8 *decrypted, nonce[16];
+	size_t left;
+	const u8 *pos, *tag;
+
+	if (data->state != PSK_3)
+		return;
+
+	wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-4");
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &left);
+	if (pos == NULL || left < sizeof(*resp)) {
+		wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame");
+		return;
+	}
+	resp = (const struct eap_psk_hdr_4 *) pos;
+	pos = (const u8 *) (resp + 1);
+	left -= sizeof(*resp);
+
+	wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Encrypted PCHANNEL", pos, left);
+
+	if (left < 4 + 16 + 1) {
+		wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in "
+			   "PSK-4 (len=%lu, expected 21)",
+			   (unsigned long) left);
+		return;
+	}
+
+	if (pos[0] == 0 && pos[1] == 0 && pos[2] == 0 && pos[3] == 0) {
+		wpa_printf(MSG_DEBUG, "EAP-PSK: Nonce did not increase");
+		return;
+	}
+
+	os_memset(nonce, 0, 12);
+	os_memcpy(nonce + 12, pos, 4);
+	pos += 4;
+	left -= 4;
+	tag = pos;
+	pos += 16;
+	left -= 16;
+
+	decrypted = os_malloc(left);
+	if (decrypted == NULL)
+		return;
+	os_memcpy(decrypted, pos, left);
+
+	if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
+				wpabuf_head(respData), 22, decrypted, left,
+				tag)) {
+		wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed");
+		os_free(decrypted);
+		data->state = FAILURE;
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message",
+		    decrypted, left);
+
+	/* Verify R flag */
+	switch (decrypted[0] >> 6) {
+	case EAP_PSK_R_FLAG_CONT:
+		wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported");
+		data->state = FAILURE;
+		break;
+	case EAP_PSK_R_FLAG_DONE_SUCCESS:
+		wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS");
+		data->state = SUCCESS;
+		break;
+	case EAP_PSK_R_FLAG_DONE_FAILURE:
+		wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE");
+		data->state = FAILURE;
+		break;
+	}
+	os_free(decrypted);
+}
+
+
+static void eap_psk_process(struct eap_sm *sm, void *priv,
+			    struct wpabuf *respData)
+{
+	struct eap_psk_data *data = priv;
+	const u8 *pos;
+	size_t len;
+
+	if (sm->user == NULL || sm->user->password == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PSK: Plaintext password not "
+			   "configured");
+		data->state = FAILURE;
+		return;
+	}
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &len);
+	if (pos == NULL || len < 1)
+		return;
+
+	switch (EAP_PSK_FLAGS_GET_T(*pos)) {
+	case 1:
+		eap_psk_process_2(sm, data, respData);
+		break;
+	case 3:
+		eap_psk_process_4(sm, data, respData);
+		break;
+	}
+}
+
+
+static Boolean eap_psk_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_psk_data *data = priv;
+	return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_psk_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_MSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->msk, EAP_MSK_LEN);
+	*len = EAP_MSK_LEN;
+
+	return key;
+}
+
+
+static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_psk_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+	*len = EAP_EMSK_LEN;
+
+	return key;
+}
+
+
+static Boolean eap_psk_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_psk_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_psk_data *data = priv;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	*len = 1 + 2 * EAP_PSK_RAND_LEN;
+	id = os_malloc(*len);
+	if (id == NULL)
+		return NULL;
+
+	id[0] = EAP_TYPE_PSK;
+	os_memcpy(id + 1, data->rand_p, EAP_PSK_RAND_LEN);
+	os_memcpy(id + 1 + EAP_PSK_RAND_LEN, data->rand_s, EAP_PSK_RAND_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: Derived Session-Id", id, *len);
+
+	return id;
+}
+
+
+int eap_server_psk_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_psk_init;
+	eap->reset = eap_psk_reset;
+	eap->buildReq = eap_psk_buildReq;
+	eap->check = eap_psk_check;
+	eap->process = eap_psk_process;
+	eap->isDone = eap_psk_isDone;
+	eap->getKey = eap_psk_getKey;
+	eap->isSuccess = eap_psk_isSuccess;
+	eap->get_emsk = eap_psk_get_emsk;
+	eap->getSessionId = eap_psk_get_session_id;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_pwd.c b/hostap/src/eap_server/eap_server_pwd.c
new file mode 100644
index 0000000..cb83ff7
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_pwd.c
@@ -0,0 +1,1129 @@
+/*
+ * hostapd / EAP-pwd (RFC 5931) server
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * 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/sha256.h"
+#include "crypto/ms_funcs.h"
+#include "eap_server/eap_i.h"
+#include "eap_common/eap_pwd_common.h"
+
+
+struct eap_pwd_data {
+	enum {
+		PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE
+	} state;
+	u8 *id_peer;
+	size_t id_peer_len;
+	u8 *id_server;
+	size_t id_server_len;
+	u8 *password;
+	size_t password_len;
+	int password_hash;
+	u32 token;
+	u16 group_num;
+	EAP_PWD_group *grp;
+
+	struct wpabuf *inbuf;
+	size_t in_frag_pos;
+	struct wpabuf *outbuf;
+	size_t out_frag_pos;
+	size_t mtu;
+
+	BIGNUM *k;
+	BIGNUM *private_value;
+	BIGNUM *peer_scalar;
+	BIGNUM *my_scalar;
+	EC_POINT *my_element;
+	EC_POINT *peer_element;
+
+	u8 my_confirm[SHA256_MAC_LEN];
+
+	u8 msk[EAP_MSK_LEN];
+	u8 emsk[EAP_EMSK_LEN];
+	u8 session_id[1 + SHA256_MAC_LEN];
+
+	BN_CTX *bnctx;
+};
+
+
+static const char * eap_pwd_state_txt(int state)
+{
+	switch (state) {
+        case PWD_ID_Req:
+		return "PWD-ID-Req";
+        case PWD_Commit_Req:
+		return "PWD-Commit-Req";
+        case PWD_Confirm_Req:
+		return "PWD-Confirm-Req";
+        case SUCCESS:
+		return "SUCCESS";
+        case FAILURE:
+		return "FAILURE";
+        default:
+		return "PWD-Unk";
+	}
+}
+
+
+static void eap_pwd_state(struct eap_pwd_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-pwd: %s -> %s",
+		   eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
+	data->state = state;
+}
+
+
+static void * eap_pwd_init(struct eap_sm *sm)
+{
+	struct eap_pwd_data *data;
+
+	if (sm->user == NULL || sm->user->password == NULL ||
+	    sm->user->password_len == 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): Password is not "
+			   "configured");
+		return NULL;
+	}
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	data->group_num = sm->pwd_group;
+	wpa_printf(MSG_DEBUG, "EAP-pwd: Selected group number %d",
+		   data->group_num);
+	data->state = PWD_ID_Req;
+
+	data->id_server = (u8 *) os_strdup("server");
+	if (data->id_server)
+		data->id_server_len = os_strlen((char *) data->id_server);
+
+	data->password = os_malloc(sm->user->password_len);
+	if (data->password == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD: Memory allocation password "
+			   "fail");
+		bin_clear_free(data->id_server, data->id_server_len);
+		os_free(data);
+		return NULL;
+	}
+	data->password_len = sm->user->password_len;
+	os_memcpy(data->password, sm->user->password, data->password_len);
+	data->password_hash = sm->user->password_hash;
+
+	data->bnctx = BN_CTX_new();
+	if (data->bnctx == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
+		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;
+	/* use default MTU from RFC 5931 if not configured otherwise */
+	data->mtu = sm->fragment_size > 0 ? sm->fragment_size : 1020;
+
+	return data;
+}
+
+
+static void eap_pwd_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_pwd_data *data = priv;
+
+	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_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_clear_free(data->grp->pwe);
+		BN_clear_free(data->grp->order);
+		BN_clear_free(data->grp->prime);
+		os_free(data->grp);
+	}
+	wpabuf_free(data->inbuf);
+	wpabuf_free(data->outbuf);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data,
+				 u8 id)
+{
+	wpa_printf(MSG_DEBUG, "EAP-pwd: ID/Request");
+	/*
+	 * if we're fragmenting then we already have an id request, just return
+	 */
+	if (data->out_frag_pos)
+		return;
+
+	data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) +
+				    data->id_server_len);
+	if (data->outbuf == NULL) {
+		eap_pwd_state(data, FAILURE);
+		return;
+	}
+
+	/* an lfsr is good enough to generate unpredictable tokens */
+	data->token = os_random();
+	wpabuf_put_be16(data->outbuf, data->group_num);
+	wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
+	wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
+	wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token));
+	wpabuf_put_u8(data->outbuf, data->password_hash ? EAP_PWD_PREP_MS :
+		      EAP_PWD_PREP_NONE);
+	wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len);
+}
+
+
+static void eap_pwd_build_commit_req(struct eap_sm *sm,
+				     struct eap_pwd_data *data, u8 id)
+{
+	BIGNUM *mask = NULL, *x = NULL, *y = NULL;
+	u8 *scalar = NULL, *element = NULL;
+	u16 offset;
+
+	wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request");
+	/*
+	 * if we're fragmenting then we already have an commit request, just
+	 * return
+	 */
+	if (data->out_frag_pos)
+		return;
+
+	if (((data->private_value = BN_new()) == NULL) ||
+	    ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
+	    ((data->my_scalar = BN_new()) == NULL) ||
+	    ((mask = BN_new()) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation "
+			   "fail");
+		goto fin;
+	}
+
+	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)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation "
+			   "fail");
+		eap_pwd_state(data, FAILURE);
+		goto fin;
+	}
+
+	if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
+	{
+		wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion "
+			   "fail");
+		goto fin;
+	}
+	BN_clear_free(mask);
+
+	if (((x = BN_new()) == NULL) ||
+	    ((y = BN_new()) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation "
+			   "fail");
+		goto fin;
+	}
+	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+						 data->my_element, x, y,
+						 data->bnctx)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
+			   "fail");
+		goto fin;
+	}
+
+	if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
+	    ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
+	     NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail");
+		goto fin;
+	}
+
+	/*
+	 * bignums occupy as little memory as possible so one that is
+	 * sufficiently smaller than the prime or order might need pre-pending
+	 * with zeros.
+	 */
+	os_memset(scalar, 0, BN_num_bytes(data->grp->order));
+	os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
+	offset = BN_num_bytes(data->grp->order) -
+		BN_num_bytes(data->my_scalar);
+	BN_bn2bin(data->my_scalar, scalar + offset);
+
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+	BN_bn2bin(x, element + offset);
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+	BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
+
+	data->outbuf = wpabuf_alloc(2 * BN_num_bytes(data->grp->prime) +
+				    BN_num_bytes(data->grp->order));
+	if (data->outbuf == NULL)
+		goto fin;
+
+	/* We send the element as (x,y) followed by the scalar */
+	wpabuf_put_data(data->outbuf, element,
+			2 * BN_num_bytes(data->grp->prime));
+	wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order));
+
+fin:
+	os_free(scalar);
+	os_free(element);
+	BN_clear_free(x);
+	BN_clear_free(y);
+	if (data->outbuf == NULL)
+		eap_pwd_state(data, FAILURE);
+}
+
+
+static void eap_pwd_build_confirm_req(struct eap_sm *sm,
+				      struct eap_pwd_data *data, u8 id)
+{
+	BIGNUM *x = NULL, *y = NULL;
+	struct crypto_hash *hash;
+	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
+	u16 grp;
+	int offset;
+
+	wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request");
+	/*
+	 * if we're fragmenting then we already have an confirm request, just
+	 * return
+	 */
+	if (data->out_frag_pos)
+		return;
+
+	/* Each component of the cruft will be at most as big as the prime */
+	if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
+	    ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation "
+			   "fail");
+		goto fin;
+	}
+
+	/*
+	 * commit is H(k | server_element | server_scalar | peer_element |
+	 *	       peer_scalar | ciphersuite)
+	 */
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
+
+	/*
+	 * Zero the memory each time because this is mod prime math and some
+	 * value may start with a few zeros and the previous one did not.
+	 *
+	 * First is k
+	 */
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
+	BN_bn2bin(data->k, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+	/* server element: x, y */
+	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+						 data->my_element, x, y,
+						 data->bnctx)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+	BN_bn2bin(x, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+	BN_bn2bin(y, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+	/* server scalar */
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->order) -
+		BN_num_bytes(data->my_scalar);
+	BN_bn2bin(data->my_scalar, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+	/* peer element: x, y */
+	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+						 data->peer_element, x, y,
+						 data->bnctx)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+	BN_bn2bin(x, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+	BN_bn2bin(y, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+	/* peer scalar */
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->order) -
+		BN_num_bytes(data->peer_scalar);
+	BN_bn2bin(data->peer_scalar, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+	/* ciphersuite */
+	grp = htons(data->group_num);
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	ptr = cruft;
+	os_memcpy(ptr, &grp, sizeof(u16));
+	ptr += sizeof(u16);
+	*ptr = EAP_PWD_DEFAULT_RAND_FUNC;
+	ptr += sizeof(u8);
+	*ptr = EAP_PWD_DEFAULT_PRF;
+	ptr += sizeof(u8);
+	eap_pwd_h_update(hash, cruft, ptr - cruft);
+
+	/* all done with the random function */
+	eap_pwd_h_final(hash, conf);
+	os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN);
+
+	data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
+	if (data->outbuf == NULL)
+		goto fin;
+
+	wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
+
+fin:
+	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);
+}
+
+
+static struct wpabuf *
+eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_pwd_data *data = priv;
+	struct wpabuf *req;
+	u8 lm_exch;
+	const u8 *buf;
+	u16 totlen = 0;
+	size_t len;
+
+	/*
+	 * if we're buffering response fragments then just ACK
+	 */
+	if (data->in_frag_pos) {
+		wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a fragment!!");
+		req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+				    EAP_PWD_HDR_SIZE, EAP_CODE_REQUEST, id);
+		if (req == NULL) {
+			eap_pwd_state(data, FAILURE);
+			return NULL;
+		}
+		switch (data->state) {
+		case PWD_ID_Req:
+			wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH);
+			break;
+		case PWD_Commit_Req:
+			wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH);
+			break;
+		case PWD_Confirm_Req:
+			wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH);
+			break;
+		default:
+			eap_pwd_state(data, FAILURE);   /* just to be sure */
+			wpabuf_free(req);
+			return NULL;
+		}
+		return req;
+	}
+
+	/*
+	 * build the data portion of a request
+	 */
+	switch (data->state) {
+	case PWD_ID_Req:
+		eap_pwd_build_id_req(sm, data, id);
+		lm_exch = EAP_PWD_OPCODE_ID_EXCH;
+		break;
+	case PWD_Commit_Req:
+		eap_pwd_build_commit_req(sm, data, id);
+		lm_exch = EAP_PWD_OPCODE_COMMIT_EXCH;
+		break;
+	case PWD_Confirm_Req:
+		eap_pwd_build_confirm_req(sm, data, id);
+		lm_exch = EAP_PWD_OPCODE_CONFIRM_EXCH;
+		break;
+	default:
+		wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req",
+			   data->state);
+		eap_pwd_state(data, FAILURE);
+		lm_exch = 0;    /* hush now, sweet compiler */
+		break;
+	}
+
+	if (data->state == FAILURE)
+		return NULL;
+
+	/*
+	 * determine whether that data needs to be fragmented
+	 */
+	len = wpabuf_len(data->outbuf) - data->out_frag_pos;
+	if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
+		len = data->mtu - EAP_PWD_HDR_SIZE;
+		EAP_PWD_SET_MORE_BIT(lm_exch);
+		/*
+		 * if this is the first fragment, need to set the M bit
+		 * and add the total length to the eap_pwd_hdr
+		 */
+		if (data->out_frag_pos == 0) {
+			EAP_PWD_SET_LENGTH_BIT(lm_exch);
+			totlen = wpabuf_len(data->outbuf) +
+				EAP_PWD_HDR_SIZE + sizeof(u16);
+			len -= sizeof(u16);
+			wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, "
+				   "total length = %d", totlen);
+		}
+		wpa_printf(MSG_DEBUG, "EAP-pwd: Send a %d byte fragment",
+			   (int) len);
+	}
+
+	/*
+	 * alloc an eap request and populate it with the data
+	 */
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+			    EAP_PWD_HDR_SIZE + len +
+			    (totlen ? sizeof(u16) : 0),
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		eap_pwd_state(data, FAILURE);
+		return NULL;
+	}
+
+	wpabuf_put_u8(req, lm_exch);
+	if (EAP_PWD_GET_LENGTH_BIT(lm_exch))
+		wpabuf_put_be16(req, totlen);
+
+	buf = wpabuf_head_u8(data->outbuf);
+	wpabuf_put_data(req, buf + data->out_frag_pos, len);
+	data->out_frag_pos += len;
+	/*
+	 * either not fragged or last fragment, either way free up the data
+	 */
+	if (data->out_frag_pos >= wpabuf_len(data->outbuf)) {
+		wpabuf_free(data->outbuf);
+		data->outbuf = NULL;
+		data->out_frag_pos = 0;
+	}
+
+	return req;
+}
+
+
+static Boolean eap_pwd_check(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_pwd_data *data = priv;
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len);
+	if (pos == NULL || len < 1) {
+		wpa_printf(MSG_INFO, "EAP-pwd: Invalid frame");
+		return TRUE;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-pwd: Received frame: exch = %d, len = %d",
+		   EAP_PWD_GET_EXCHANGE(*pos), (int) len);
+
+	if (data->state == PWD_ID_Req &&
+	    ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_ID_EXCH))
+		return FALSE;
+
+	if (data->state == PWD_Commit_Req &&
+	    ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_COMMIT_EXCH))
+		return FALSE;
+
+	if (data->state == PWD_Confirm_Req &&
+	    ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_CONFIRM_EXCH))
+		return FALSE;
+
+	wpa_printf(MSG_INFO, "EAP-pwd: Unexpected opcode=%d in state=%d",
+		   *pos, data->state);
+
+	return TRUE;
+}
+
+
+static void eap_pwd_process_id_resp(struct eap_sm *sm,
+				    struct eap_pwd_data *data,
+				    const u8 *payload, size_t payload_len)
+{
+	struct eap_pwd_id *id;
+	const u8 *password;
+	size_t password_len;
+	u8 pwhashhash[16];
+	int res;
+
+	if (payload_len < sizeof(struct eap_pwd_id)) {
+		wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response");
+		return;
+	}
+
+	id = (struct eap_pwd_id *) payload;
+	if ((data->group_num != be_to_host16(id->group_num)) ||
+	    (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
+	    (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) ||
+	    (id->prf != EAP_PWD_DEFAULT_PRF)) {
+		wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters");
+		eap_pwd_state(data, FAILURE);
+		return;
+	}
+	data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id));
+	if (data->id_peer == NULL) {
+		wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
+		return;
+	}
+	data->id_peer_len = payload_len - sizeof(struct eap_pwd_id);
+	os_memcpy(data->id_peer, id->identity, data->id_peer_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of",
+			  data->id_peer, data->id_peer_len);
+
+	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;
+	}
+
+	if (data->password_hash) {
+		res = hash_nt_password_hash(data->password, pwhashhash);
+		if (res)
+			return;
+		password = pwhashhash;
+		password_len = sizeof(pwhashhash);
+	} else {
+		password = data->password;
+		password_len = data->password_len;
+	}
+
+	res = compute_password_element(data->grp, data->group_num,
+				       password, password_len,
+				       data->id_server, data->id_server_len,
+				       data->id_peer, data->id_peer_len,
+				       (u8 *) &data->token);
+	os_memset(pwhashhash, 0, sizeof(pwhashhash));
+	if (res) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute "
+			   "PWE");
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...",
+		   BN_num_bits(data->grp->prime));
+
+	eap_pwd_state(data, PWD_Commit_Req);
+}
+
+
+static void
+eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
+			    const u8 *payload, size_t payload_len)
+{
+	u8 *ptr;
+	BIGNUM *x = NULL, *y = NULL, *cofactor = NULL;
+	EC_POINT *K = NULL, *point = NULL;
+	int res = 0;
+	size_t prime_len, order_len;
+
+	wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response");
+
+	prime_len = BN_num_bytes(data->grp->prime);
+	order_len = BN_num_bytes(data->grp->order);
+
+	if (payload_len != 2 * prime_len + order_len) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+			   (unsigned int) payload_len,
+			   (unsigned int) (2 * prime_len + order_len));
+		goto fin;
+	}
+
+	if (((data->peer_scalar = BN_new()) == NULL) ||
+	    ((data->k = BN_new()) == NULL) ||
+	    ((cofactor = BN_new()) == NULL) ||
+	    ((x = BN_new()) == NULL) ||
+	    ((y = BN_new()) == NULL) ||
+	    ((point = EC_POINT_new(data->grp->group)) == NULL) ||
+	    ((K = EC_POINT_new(data->grp->group)) == NULL) ||
+	    ((data->peer_element = EC_POINT_new(data->grp->group)) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
+			   "fail");
+		goto fin;
+	}
+
+	if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get "
+			   "cofactor for curve");
+		goto fin;
+	}
+
+	/* element, x then y, followed by scalar */
+	ptr = (u8 *) payload;
+	BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
+	ptr += BN_num_bytes(data->grp->prime);
+	BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
+	ptr += BN_num_bytes(data->grp->prime);
+	BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->peer_scalar);
+	if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
+						 data->peer_element, x, y,
+						 data->bnctx)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
+			   "fail");
+		goto fin;
+	}
+
+	/* check to ensure peer's element is not in a small sub-group */
+	if (BN_cmp(cofactor, BN_value_one())) {
+		if (!EC_POINT_mul(data->grp->group, point, NULL,
+				  data->peer_element, cofactor, NULL)) {
+			wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
+				   "multiply peer element by order");
+			goto fin;
+		}
+		if (EC_POINT_is_at_infinity(data->grp->group, point)) {
+			wpa_printf(MSG_INFO, "EAP-PWD (server): peer element "
+				   "is at infinity!\n");
+			goto fin;
+		}
+	}
+
+	/* compute the shared key, k */
+	if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
+			   data->peer_scalar, data->bnctx)) ||
+	    (!EC_POINT_add(data->grp->group, K, K, data->peer_element,
+			   data->bnctx)) ||
+	    (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
+			   data->bnctx))) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key "
+			   "fail");
+		goto fin;
+	}
+
+	/* ensure that the shared key isn't in a small sub-group */
+	if (BN_cmp(cofactor, BN_value_one())) {
+		if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
+				  NULL)) {
+			wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
+				   "multiply shared key point by order!\n");
+			goto fin;
+		}
+	}
+
+	/*
+	 * This check is strictly speaking just for the case above where
+	 * co-factor > 1 but it was suggested that even though this is probably
+	 * never going to happen it is a simple and safe check "just to be
+	 * sure" so let's be safe.
+	 */
+	if (EC_POINT_is_at_infinity(data->grp->group, K)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is "
+			   "at infinity");
+		goto fin;
+	}
+	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
+						 NULL, data->bnctx)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract "
+			   "shared secret from secret point");
+		goto fin;
+	}
+	res = 1;
+
+fin:
+	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);
+	else
+		eap_pwd_state(data, FAILURE);
+}
+
+
+static void
+eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
+			     const u8 *payload, size_t payload_len)
+{
+	BIGNUM *x = NULL, *y = NULL;
+	struct crypto_hash *hash;
+	u32 cs;
+	u16 grp;
+	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
+	int offset;
+
+	if (payload_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd: Unexpected Confirm payload length %u (expected %u)",
+			   (unsigned int) payload_len, SHA256_MAC_LEN);
+		goto fin;
+	}
+
+	/* build up the ciphersuite: group | random_function | prf */
+	grp = htons(data->group_num);
+	ptr = (u8 *) &cs;
+	os_memcpy(ptr, &grp, sizeof(u16));
+	ptr += sizeof(u16);
+	*ptr = EAP_PWD_DEFAULT_RAND_FUNC;
+	ptr += sizeof(u8);
+	*ptr = EAP_PWD_DEFAULT_PRF;
+
+	/* each component of the cruft will be at most as big as the prime */
+	if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
+	    ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail");
+		goto fin;
+	}
+
+	/*
+	 * commit is H(k | peer_element | peer_scalar | server_element |
+	 *	       server_scalar | ciphersuite)
+	 */
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
+
+	/* k */
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
+	BN_bn2bin(data->k, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+	/* peer element: x, y */
+	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+						 data->peer_element, x, y,
+						 data->bnctx)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+	BN_bn2bin(x, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+	BN_bn2bin(y, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+	/* peer scalar */
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->order) -
+		BN_num_bytes(data->peer_scalar);
+	BN_bn2bin(data->peer_scalar, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+	/* server element: x, y */
+	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+						 data->my_element, x, y,
+						 data->bnctx)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+			   "assignment fail");
+		goto fin;
+	}
+
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+	BN_bn2bin(x, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+	BN_bn2bin(y, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+	/* server scalar */
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	offset = BN_num_bytes(data->grp->order) -
+		BN_num_bytes(data->my_scalar);
+	BN_bn2bin(data->my_scalar, cruft + offset);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+	/* ciphersuite */
+	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
+
+	/* all done */
+	eap_pwd_h_final(hash, conf);
+
+	ptr = (u8 *) payload;
+	if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not "
+			   "verify");
+		goto fin;
+	}
+
+	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,
+			 data->session_id) < 0)
+		eap_pwd_state(data, FAILURE);
+	else
+		eap_pwd_state(data, SUCCESS);
+
+fin:
+	bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
+	BN_clear_free(x);
+	BN_clear_free(y);
+}
+
+
+static void eap_pwd_process(struct eap_sm *sm, void *priv,
+			    struct wpabuf *respData)
+{
+	struct eap_pwd_data *data = priv;
+	const u8 *pos;
+	size_t len;
+	u8 lm_exch;
+	u16 tot_len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len);
+	if ((pos == NULL) || (len < 1)) {
+		wpa_printf(MSG_INFO, "Bad EAP header! pos %s and len = %d",
+			   (pos == NULL) ? "is NULL" : "is not NULL",
+			   (int) len);
+		return;
+	}
+
+	lm_exch = *pos;
+	pos++;            /* skip over the bits and the exch */
+	len--;
+
+	/*
+	 * if we're fragmenting then this should be an ACK with no data,
+	 * just return and continue fragmenting in the "build" section above
+	 */
+	if (data->out_frag_pos) {
+		if (len > 1)
+			wpa_printf(MSG_INFO, "EAP-pwd: Bad response! "
+				   "Fragmenting but not an ACK");
+		else
+			wpa_printf(MSG_DEBUG, "EAP-pwd: received ACK from "
+				   "peer");
+		return;
+	}
+	/*
+	 * if we're receiving fragmented packets then we need to buffer...
+	 *
+	 * the first fragment has a total length
+	 */
+	if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-pwd: Frame too short to contain Total-Length field");
+			return;
+		}
+		tot_len = WPA_GET_BE16(pos);
+		wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total "
+			   "length = %d", tot_len);
+		if (tot_len > 15000)
+			return;
+		if (data->inbuf) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-pwd: Unexpected new fragment start when previous fragment is still in use");
+			return;
+		}
+		data->inbuf = wpabuf_alloc(tot_len);
+		if (data->inbuf == NULL) {
+			wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to "
+				   "buffer fragments!");
+			return;
+		}
+		data->in_frag_pos = 0;
+		pos += sizeof(u16);
+		len -= sizeof(u16);
+	}
+	/*
+	 * the first and all intermediate fragments have the M bit set
+	 */
+	if (EAP_PWD_GET_MORE_BIT(lm_exch)) {
+		if ((data->in_frag_pos + len) > wpabuf_size(data->inbuf)) {
+			wpa_printf(MSG_DEBUG, "EAP-pwd: Buffer overflow "
+				   "attack detected! (%d+%d > %d)",
+				   (int) data->in_frag_pos, (int) len,
+				   (int) wpabuf_size(data->inbuf));
+			eap_pwd_state(data, FAILURE);
+			return;
+		}
+		wpabuf_put_data(data->inbuf, pos, len);
+		data->in_frag_pos += len;
+		wpa_printf(MSG_DEBUG, "EAP-pwd: Got a %d byte fragment",
+			   (int) len);
+		return;
+	}
+	/*
+	 * last fragment won't have the M bit set (but we're obviously
+	 * buffering fragments so that's how we know it's the last)
+	 */
+	if (data->in_frag_pos) {
+		wpabuf_put_data(data->inbuf, pos, len);
+		data->in_frag_pos += len;
+		pos = wpabuf_head_u8(data->inbuf);
+		len = data->in_frag_pos;
+		wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes",
+			   (int) len);
+	}
+	switch (EAP_PWD_GET_EXCHANGE(lm_exch)) {
+	case EAP_PWD_OPCODE_ID_EXCH:
+		eap_pwd_process_id_resp(sm, data, pos, len);
+		break;
+	case EAP_PWD_OPCODE_COMMIT_EXCH:
+		eap_pwd_process_commit_resp(sm, data, pos, len);
+		break;
+	case EAP_PWD_OPCODE_CONFIRM_EXCH:
+		eap_pwd_process_confirm_resp(sm, data, pos, len);
+		break;
+	}
+	/*
+	 * if we had been buffering fragments, here's a great place
+	 * to clean up
+	 */
+	if (data->in_frag_pos) {
+		wpabuf_free(data->inbuf);
+		data->inbuf = NULL;
+		data->in_frag_pos = 0;
+	}
+}
+
+
+static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_pwd_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_MSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	os_memcpy(key, data->msk, EAP_MSK_LEN);
+	*len = EAP_MSK_LEN;
+
+	return key;
+}
+
+
+static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_pwd_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+	*len = EAP_EMSK_LEN;
+
+	return key;
+}
+
+
+static Boolean eap_pwd_is_success(struct eap_sm *sm, void *priv)
+{
+	struct eap_pwd_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static Boolean eap_pwd_is_done(struct eap_sm *sm, void *priv)
+{
+	struct eap_pwd_data *data = priv;
+	return (data->state == SUCCESS) || (data->state == FAILURE);
+}
+
+
+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;
+}
+
+
+int eap_server_pwd_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+	struct timeval tp;
+	struct timezone tz;
+	u32 sr;
+
+	sr = 0xdeaddada;
+	(void) gettimeofday(&tp, &tz);
+	sr ^= (tp.tv_sec ^ tp.tv_usec);
+	srandom(sr);
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_PWD,
+				      "PWD");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_pwd_init;
+	eap->reset = eap_pwd_reset;
+	eap->buildReq = eap_pwd_build_req;
+	eap->check = eap_pwd_check;
+	eap->process = eap_pwd_process;
+	eap->isDone = eap_pwd_is_done;
+	eap->getKey = eap_pwd_getkey;
+	eap->get_emsk = eap_pwd_get_emsk;
+	eap->isSuccess = eap_pwd_is_success;
+	eap->getSessionId = eap_pwd_get_session_id;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
+
diff --git a/hostap/src/eap_server/eap_server_sake.c b/hostap/src/eap_server/eap_server_sake.c
new file mode 100644
index 0000000..de70777
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_sake.c
@@ -0,0 +1,545 @@
+/*
+ * hostapd / EAP-SAKE (RFC 4763) server
+ * Copyright (c) 2006-2007, 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/random.h"
+#include "eap_server/eap_i.h"
+#include "eap_common/eap_sake_common.h"
+
+
+struct eap_sake_data {
+	enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
+	u8 rand_s[EAP_SAKE_RAND_LEN];
+	u8 rand_p[EAP_SAKE_RAND_LEN];
+	struct {
+		u8 auth[EAP_SAKE_TEK_AUTH_LEN];
+		u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
+	} tek;
+	u8 msk[EAP_MSK_LEN];
+	u8 emsk[EAP_EMSK_LEN];
+	u8 session_id;
+	u8 *peerid;
+	size_t peerid_len;
+};
+
+
+static const char * eap_sake_state_txt(int state)
+{
+	switch (state) {
+	case IDENTITY:
+		return "IDENTITY";
+	case CHALLENGE:
+		return "CHALLENGE";
+	case CONFIRM:
+		return "CONFIRM";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "?";
+	}
+}
+
+
+static void eap_sake_state(struct eap_sake_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
+		   eap_sake_state_txt(data->state),
+		   eap_sake_state_txt(state));
+	data->state = state;
+}
+
+
+static void * eap_sake_init(struct eap_sm *sm)
+{
+	struct eap_sake_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = CHALLENGE;
+
+	if (os_get_random(&data->session_id, 1)) {
+		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
+		os_free(data);
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d",
+		   data->session_id);
+
+	return data;
+}
+
+
+static void eap_sake_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_sake_data *data = priv;
+	os_free(data->peerid);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
+					  u8 id, size_t length, u8 subtype)
+{
+	struct eap_sake_hdr *sake;
+	struct wpabuf *msg;
+	size_t plen;
+
+	plen = sizeof(struct eap_sake_hdr) + length;
+
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
+			    EAP_CODE_REQUEST, id);
+	if (msg == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
+			   "request");
+		return NULL;
+	}
+
+	sake = wpabuf_put(msg, sizeof(*sake));
+	sake->version = EAP_SAKE_VERSION;
+	sake->session_id = data->session_id;
+	sake->subtype = subtype;
+
+	return msg;
+}
+
+
+static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm,
+					       struct eap_sake_data *data,
+					       u8 id)
+{
+	struct wpabuf *msg;
+	size_t plen;
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity");
+
+	plen = 4;
+	plen += 2 + sm->server_id_len;
+	msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY);
+	if (msg == NULL) {
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ");
+	eap_sake_add_attr(msg, EAP_SAKE_AT_PERM_ID_REQ, NULL, 2);
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
+	eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
+			  sm->server_id, sm->server_id_len);
+
+	return msg;
+}
+
+
+static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm,
+						struct eap_sake_data *data,
+						u8 id)
+{
+	struct wpabuf *msg;
+	size_t plen;
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge");
+
+	if (random_get_bytes(data->rand_s, EAP_SAKE_RAND_LEN)) {
+		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
+		data->state = FAILURE;
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
+		    data->rand_s, EAP_SAKE_RAND_LEN);
+
+	plen = 2 + EAP_SAKE_RAND_LEN + 2 + sm->server_id_len;
+	msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE);
+	if (msg == NULL) {
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_S");
+	eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S,
+			  data->rand_s, EAP_SAKE_RAND_LEN);
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
+	eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
+			  sm->server_id, sm->server_id_len);
+
+	return msg;
+}
+
+
+static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm,
+					      struct eap_sake_data *data,
+					      u8 id)
+{
+	struct wpabuf *msg;
+	u8 *mic;
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Confirm");
+
+	msg = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN,
+				 EAP_SAKE_SUBTYPE_CONFIRM);
+	if (msg == NULL) {
+		data->state = FAILURE;
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_S");
+	wpabuf_put_u8(msg, EAP_SAKE_AT_MIC_S);
+	wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN);
+	mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN);
+	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+				 sm->server_id, sm->server_id_len,
+				 data->peerid, data->peerid_len, 0,
+				 wpabuf_head(msg), wpabuf_len(msg), mic, mic))
+	{
+		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+		data->state = FAILURE;
+		os_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+static struct wpabuf * eap_sake_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_sake_data *data = priv;
+
+	switch (data->state) {
+	case IDENTITY:
+		return eap_sake_build_identity(sm, data, id);
+	case CHALLENGE:
+		return eap_sake_build_challenge(sm, data, id);
+	case CONFIRM:
+		return eap_sake_build_confirm(sm, data, id);
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown state %d in buildReq",
+			   data->state);
+		break;
+	}
+	return NULL;
+}
+
+
+static Boolean eap_sake_check(struct eap_sm *sm, void *priv,
+			      struct wpabuf *respData)
+{
+	struct eap_sake_data *data = priv;
+	struct eap_sake_hdr *resp;
+	size_t len;
+	u8 version, session_id, subtype;
+	const u8 *pos;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
+	if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Invalid frame");
+		return TRUE;
+	}
+
+	resp = (struct eap_sake_hdr *) pos;
+	version = resp->version;
+	session_id = resp->session_id;
+	subtype = resp->subtype;
+
+	if (version != EAP_SAKE_VERSION) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Unknown version %d", version);
+		return TRUE;
+	}
+
+	if (session_id != data->session_id) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
+			   session_id, data->session_id);
+		return TRUE;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype=%d", subtype);
+
+	if (data->state == IDENTITY && subtype == EAP_SAKE_SUBTYPE_IDENTITY)
+		return FALSE;
+
+	if (data->state == CHALLENGE && subtype == EAP_SAKE_SUBTYPE_CHALLENGE)
+		return FALSE;
+
+	if (data->state == CONFIRM && subtype == EAP_SAKE_SUBTYPE_CONFIRM)
+		return FALSE;
+
+	if (subtype == EAP_SAKE_SUBTYPE_AUTH_REJECT)
+		return FALSE;
+
+	wpa_printf(MSG_INFO, "EAP-SAKE: Unexpected subtype=%d in state=%d",
+		   subtype, data->state);
+
+	return TRUE;
+}
+
+
+static void eap_sake_process_identity(struct eap_sm *sm,
+				      struct eap_sake_data *data,
+				      const struct wpabuf *respData,
+				      const u8 *payload, size_t payloadlen)
+{
+	if (data->state != IDENTITY)
+		return;
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Identity");
+	/* TODO: update identity and select new user data */
+	eap_sake_state(data, CHALLENGE);
+}
+
+
+static void eap_sake_process_challenge(struct eap_sm *sm,
+				       struct eap_sake_data *data,
+				       const struct wpabuf *respData,
+				       const u8 *payload, size_t payloadlen)
+{
+	struct eap_sake_parse_attr attr;
+	u8 mic_p[EAP_SAKE_MIC_LEN];
+
+	if (data->state != CHALLENGE)
+		return;
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Challenge");
+
+	if (eap_sake_parse_attributes(payload, payloadlen, &attr))
+		return;
+
+	if (!attr.rand_p || !attr.mic_p) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Response/Challenge did not "
+			   "include AT_RAND_P or AT_MIC_P");
+		return;
+	}
+
+	os_memcpy(data->rand_p, attr.rand_p, EAP_SAKE_RAND_LEN);
+
+	os_free(data->peerid);
+	data->peerid = NULL;
+	data->peerid_len = 0;
+	if (attr.peerid) {
+		data->peerid = os_malloc(attr.peerid_len);
+		if (data->peerid == NULL)
+			return;
+		os_memcpy(data->peerid, attr.peerid, attr.peerid_len);
+		data->peerid_len = attr.peerid_len;
+	}
+
+	if (sm->user == NULL || sm->user->password == NULL ||
+	    sm->user->password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Plaintext password with "
+			   "%d-byte key not configured",
+			   2 * EAP_SAKE_ROOT_SECRET_LEN);
+		data->state = FAILURE;
+		return;
+	}
+	eap_sake_derive_keys(sm->user->password,
+			     sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
+			     data->rand_s, data->rand_p,
+			     (u8 *) &data->tek, data->msk, data->emsk);
+
+	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+			     sm->server_id, sm->server_id_len,
+			     data->peerid, data->peerid_len, 1,
+			     wpabuf_head(respData), wpabuf_len(respData),
+			     attr.mic_p, mic_p);
+	if (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;
+	}
+
+	eap_sake_state(data, CONFIRM);
+}
+
+
+static void eap_sake_process_confirm(struct eap_sm *sm,
+				     struct eap_sake_data *data,
+				     const struct wpabuf *respData,
+				     const u8 *payload, size_t payloadlen)
+{
+	struct eap_sake_parse_attr attr;
+	u8 mic_p[EAP_SAKE_MIC_LEN];
+
+	if (data->state != CONFIRM)
+		return;
+
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Confirm");
+
+	if (eap_sake_parse_attributes(payload, payloadlen, &attr))
+		return;
+
+	if (!attr.mic_p) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Response/Confirm did not "
+			   "include AT_MIC_P");
+		return;
+	}
+
+	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+			     sm->server_id, sm->server_id_len,
+			     data->peerid, data->peerid_len, 1,
+			     wpabuf_head(respData), wpabuf_len(respData),
+			     attr.mic_p, mic_p);
+	if (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
+		eap_sake_state(data, SUCCESS);
+}
+
+
+static void eap_sake_process_auth_reject(struct eap_sm *sm,
+					 struct eap_sake_data *data,
+					 const struct wpabuf *respData,
+					 const u8 *payload, size_t payloadlen)
+{
+	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject");
+	eap_sake_state(data, FAILURE);
+}
+
+
+static void eap_sake_process(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_sake_data *data = priv;
+	struct eap_sake_hdr *resp;
+	u8 subtype;
+	size_t len;
+	const u8 *pos, *end;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
+	if (pos == NULL || len < sizeof(struct eap_sake_hdr))
+		return;
+
+	resp = (struct eap_sake_hdr *) pos;
+	end = pos + len;
+	subtype = resp->subtype;
+	pos = (u8 *) (resp + 1);
+
+	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
+		    pos, end - pos);
+
+	switch (subtype) {
+	case EAP_SAKE_SUBTYPE_IDENTITY:
+		eap_sake_process_identity(sm, data, respData, pos, end - pos);
+		break;
+	case EAP_SAKE_SUBTYPE_CHALLENGE:
+		eap_sake_process_challenge(sm, data, respData, pos, end - pos);
+		break;
+	case EAP_SAKE_SUBTYPE_CONFIRM:
+		eap_sake_process_confirm(sm, data, respData, pos, end - pos);
+		break;
+	case EAP_SAKE_SUBTYPE_AUTH_REJECT:
+		eap_sake_process_auth_reject(sm, data, respData, pos,
+					     end - pos);
+		break;
+	}
+}
+
+
+static Boolean eap_sake_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_sake_data *data = priv;
+	return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_sake_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_MSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->msk, EAP_MSK_LEN);
+	*len = EAP_MSK_LEN;
+
+	return key;
+}
+
+
+static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_sake_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+	*len = EAP_EMSK_LEN;
+
+	return key;
+}
+
+
+static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_sake_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_sake_data *data = priv;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	*len = 1 + 2 * EAP_SAKE_RAND_LEN;
+	id = os_malloc(*len);
+	if (id == NULL)
+		return NULL;
+
+	id[0] = EAP_TYPE_SAKE;
+	os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN);
+	os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len);
+
+	return id;
+}
+
+
+int eap_server_sake_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_sake_init;
+	eap->reset = eap_sake_reset;
+	eap->buildReq = eap_sake_buildReq;
+	eap->check = eap_sake_check;
+	eap->process = eap_sake_process;
+	eap->isDone = eap_sake_isDone;
+	eap->getKey = eap_sake_getKey;
+	eap->isSuccess = eap_sake_isSuccess;
+	eap->get_emsk = eap_sake_get_emsk;
+	eap->getSessionId = eap_sake_get_session_id;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_sim.c b/hostap/src/eap_server/eap_server_sim.c
new file mode 100644
index 0000000..ddfb71c
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_sim.c
@@ -0,0 +1,871 @@
+/*
+ * hostapd / EAP-SIM (RFC 4186)
+ * Copyright (c) 2005-2012, 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/random.h"
+#include "eap_server/eap_i.h"
+#include "eap_common/eap_sim_common.h"
+#include "eap_server/eap_sim_db.h"
+
+
+struct eap_sim_data {
+	u8 mk[EAP_SIM_MK_LEN];
+	u8 nonce_mt[EAP_SIM_NONCE_MT_LEN];
+	u8 nonce_s[EAP_SIM_NONCE_S_LEN];
+	u8 k_aut[EAP_SIM_K_AUT_LEN];
+	u8 k_encr[EAP_SIM_K_ENCR_LEN];
+	u8 msk[EAP_SIM_KEYING_DATA_LEN];
+	u8 emsk[EAP_EMSK_LEN];
+	u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
+	u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
+	u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
+	int num_chal;
+	enum {
+		START, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE
+	} state;
+	char *next_pseudonym;
+	char *next_reauth_id;
+	u16 counter;
+	struct eap_sim_reauth *reauth;
+	u16 notification;
+	int use_result_ind;
+	int start_round;
+	char permanent[20]; /* Permanent username */
+};
+
+
+static const char * eap_sim_state_txt(int state)
+{
+	switch (state) {
+	case START:
+		return "START";
+	case CHALLENGE:
+		return "CHALLENGE";
+	case REAUTH:
+		return "REAUTH";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	case NOTIFICATION:
+		return "NOTIFICATION";
+	default:
+		return "Unknown?!";
+	}
+}
+
+
+static void eap_sim_state(struct eap_sim_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s",
+		   eap_sim_state_txt(data->state),
+		   eap_sim_state_txt(state));
+	data->state = state;
+}
+
+
+static void * eap_sim_init(struct eap_sm *sm)
+{
+	struct eap_sim_data *data;
+
+	if (sm->eap_sim_db_priv == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured");
+		return NULL;
+	}
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = START;
+
+	return data;
+}
+
+
+static void eap_sim_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_sim_data *data = priv;
+	os_free(data->next_pseudonym);
+	os_free(data->next_reauth_id);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_sim_build_start(struct eap_sm *sm,
+					   struct eap_sim_data *data, u8 id)
+{
+	struct eap_sim_msg *msg;
+	u8 ver[2];
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start");
+	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
+			       EAP_SIM_SUBTYPE_START);
+	data->start_round++;
+	if (data->start_round == 1) {
+		/*
+		 * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is
+		 * ignored and the SIM/Start is used to request the identity.
+		 */
+		wpa_printf(MSG_DEBUG, "   AT_ANY_ID_REQ");
+		eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
+	} else if (data->start_round > 3) {
+		/* Cannot use more than three rounds of Start messages */
+		eap_sim_msg_free(msg);
+		return NULL;
+	} else if (data->start_round == 0) {
+		/*
+		 * This is a special case that is used to recover from
+		 * AT_COUNTER_TOO_SMALL during re-authentication. Since we
+		 * already know the identity of the peer, there is no need to
+		 * request any identity in this case.
+		 */
+	} else if (sm->identity && sm->identity_len > 0 &&
+		   sm->identity[0] == EAP_SIM_REAUTH_ID_PREFIX) {
+		/* Reauth id may have expired - try fullauth */
+		wpa_printf(MSG_DEBUG, "   AT_FULLAUTH_ID_REQ");
+		eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0);
+	} else {
+		wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
+		eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
+	}
+	wpa_printf(MSG_DEBUG, "   AT_VERSION_LIST");
+	ver[0] = 0;
+	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, EAP_TYPE_SIM, NULL, NULL, 0);
+}
+
+
+static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data,
+			      struct eap_sim_msg *msg, u16 counter,
+			      const u8 *nonce_s)
+{
+	os_free(data->next_pseudonym);
+	if (nonce_s == NULL) {
+		data->next_pseudonym =
+			eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv,
+						      EAP_SIM_DB_SIM);
+	} else {
+		/* Do not update pseudonym during re-authentication */
+		data->next_pseudonym = NULL;
+	}
+	os_free(data->next_reauth_id);
+	if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
+		data->next_reauth_id =
+			eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv,
+						      EAP_SIM_DB_SIM);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication "
+			   "count exceeded - force full authentication");
+		data->next_reauth_id = NULL;
+	}
+
+	if (data->next_pseudonym == NULL && data->next_reauth_id == NULL &&
+	    counter == 0 && nonce_s == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "   AT_IV");
+	wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+	eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+	if (counter > 0) {
+		wpa_printf(MSG_DEBUG, "   *AT_COUNTER (%u)", counter);
+		eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+	}
+
+	if (nonce_s) {
+		wpa_printf(MSG_DEBUG, "   *AT_NONCE_S");
+		eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s,
+				EAP_SIM_NONCE_S_LEN);
+	}
+
+	if (data->next_pseudonym) {
+		wpa_printf(MSG_DEBUG, "   *AT_NEXT_PSEUDONYM (%s)",
+			   data->next_pseudonym);
+		eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM,
+				os_strlen(data->next_pseudonym),
+				(u8 *) data->next_pseudonym,
+				os_strlen(data->next_pseudonym));
+	}
+
+	if (data->next_reauth_id) {
+		wpa_printf(MSG_DEBUG, "   *AT_NEXT_REAUTH_ID (%s)",
+			   data->next_reauth_id);
+		eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID,
+				os_strlen(data->next_reauth_id),
+				(u8 *) data->next_reauth_id,
+				os_strlen(data->next_reauth_id));
+	}
+
+	if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
+			   "AT_ENCR_DATA");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_sim_build_challenge(struct eap_sm *sm,
+					       struct eap_sim_data *data,
+					       u8 id)
+{
+	struct eap_sim_msg *msg;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Challenge");
+	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
+			       EAP_SIM_SUBTYPE_CHALLENGE);
+	wpa_printf(MSG_DEBUG, "   AT_RAND");
+	eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand,
+			data->num_chal * GSM_RAND_LEN);
+
+	if (eap_sim_build_encr(sm, data, msg, 0, NULL)) {
+		eap_sim_msg_free(msg);
+		return NULL;
+	}
+
+	if (sm->eap_sim_aka_result_ind) {
+		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+	}
+
+	wpa_printf(MSG_DEBUG, "   AT_MAC");
+	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut,
+				  data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+}
+
+
+static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm,
+					    struct eap_sim_data *data, u8 id)
+{
+	struct eap_sim_msg *msg;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication");
+
+	if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN))
+		return NULL;
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S",
+			data->nonce_s, EAP_SIM_NONCE_S_LEN);
+
+	eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
+			    data->emsk);
+	eap_sim_derive_keys_reauth(data->counter, sm->identity,
+				   sm->identity_len, data->nonce_s, data->mk,
+				   data->msk, data->emsk);
+
+	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
+			       EAP_SIM_SUBTYPE_REAUTHENTICATION);
+
+	if (eap_sim_build_encr(sm, data, msg, data->counter, data->nonce_s)) {
+		eap_sim_msg_free(msg);
+		return NULL;
+	}
+
+	if (sm->eap_sim_aka_result_ind) {
+		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+	}
+
+	wpa_printf(MSG_DEBUG, "   AT_MAC");
+	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
+}
+
+
+static struct wpabuf * eap_sim_build_notification(struct eap_sm *sm,
+						  struct eap_sim_data *data,
+						  u8 id)
+{
+	struct eap_sim_msg *msg;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Notification");
+	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
+			       EAP_SIM_SUBTYPE_NOTIFICATION);
+	wpa_printf(MSG_DEBUG, "   AT_NOTIFICATION (%d)", data->notification);
+	eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification,
+			NULL, 0);
+	if (data->use_result_ind) {
+		if (data->reauth) {
+			wpa_printf(MSG_DEBUG, "   AT_IV");
+			wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+			eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
+						   EAP_SIM_AT_ENCR_DATA);
+			wpa_printf(MSG_DEBUG, "   *AT_COUNTER (%u)",
+				   data->counter);
+			eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
+					NULL, 0);
+
+			if (eap_sim_msg_add_encr_end(msg, data->k_encr,
+						     EAP_SIM_AT_PADDING)) {
+				wpa_printf(MSG_WARNING, "EAP-SIM: Failed to "
+					   "encrypt AT_ENCR_DATA");
+				eap_sim_msg_free(msg);
+				return NULL;
+			}
+		}
+
+		wpa_printf(MSG_DEBUG, "   AT_MAC");
+		eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+	}
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
+}
+
+
+static struct wpabuf * eap_sim_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_sim_data *data = priv;
+
+	switch (data->state) {
+	case START:
+		return eap_sim_build_start(sm, data, id);
+	case CHALLENGE:
+		return eap_sim_build_challenge(sm, data, id);
+	case REAUTH:
+		return eap_sim_build_reauth(sm, data, id);
+	case NOTIFICATION:
+		return eap_sim_build_notification(sm, data, id);
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
+			   "buildReq", data->state);
+		break;
+	}
+	return NULL;
+}
+
+
+static Boolean eap_sim_check(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
+	if (pos == NULL || len < 3) {
+		wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static Boolean eap_sim_unexpected_subtype(struct eap_sim_data *data,
+					  u8 subtype)
+{
+	if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR)
+		return FALSE;
+
+	switch (data->state) {
+	case START:
+		if (subtype != EAP_SIM_SUBTYPE_START) {
+			wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
+				   "subtype %d", subtype);
+			return TRUE;
+		}
+		break;
+	case CHALLENGE:
+		if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) {
+			wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
+				   "subtype %d", subtype);
+			return TRUE;
+		}
+		break;
+	case REAUTH:
+		if (subtype != EAP_SIM_SUBTYPE_REAUTHENTICATION) {
+			wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
+				   "subtype %d", subtype);
+			return TRUE;
+		}
+		break;
+	case NOTIFICATION:
+		if (subtype != EAP_SIM_SUBTYPE_NOTIFICATION) {
+			wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
+				   "subtype %d", subtype);
+			return TRUE;
+		}
+		break;
+	default:
+		wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for "
+			   "processing a response", data->state);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static int eap_sim_supported_ver(struct eap_sim_data *data, int version)
+{
+	return version == EAP_SIM_VERSION;
+}
+
+
+static void eap_sim_process_start(struct eap_sm *sm,
+				  struct eap_sim_data *data,
+				  struct wpabuf *respData,
+				  struct eap_sim_attrs *attr)
+{
+	size_t identity_len;
+	u8 ver_list[2];
+	u8 *new_identity;
+	char *username;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response");
+
+	if (data->start_round == 0) {
+		/*
+		 * Special case for AT_COUNTER_TOO_SMALL recovery - no identity
+		 * was requested since we already know it.
+		 */
+		goto skip_id_update;
+	}
+
+	/*
+	 * We always request identity in SIM/Start, so the peer is required to
+	 * have replied with one.
+	 */
+	if (!attr->identity || attr->identity_len == 0) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Peer did not provide any "
+			   "identity");
+		goto failed;
+	}
+
+	new_identity = os_malloc(attr->identity_len);
+	if (new_identity == NULL)
+		goto failed;
+	os_free(sm->identity);
+	sm->identity = new_identity;
+	os_memcpy(sm->identity, attr->identity, attr->identity_len);
+	sm->identity_len = attr->identity_len;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
+			  sm->identity, sm->identity_len);
+	username = sim_get_username(sm->identity, sm->identity_len);
+	if (username == NULL)
+		goto failed;
+
+	if (username[0] == EAP_SIM_REAUTH_ID_PREFIX) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Reauth username '%s'",
+			   username);
+		data->reauth = eap_sim_db_get_reauth_entry(
+			sm->eap_sim_db_priv, username);
+		os_free(username);
+		if (data->reauth == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown reauth "
+				   "identity - request full auth identity");
+			/* Remain in START state for another round */
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast re-authentication");
+		os_strlcpy(data->permanent, data->reauth->permanent,
+			   sizeof(data->permanent));
+		data->counter = data->reauth->counter;
+		os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN);
+		eap_sim_state(data, REAUTH);
+		return;
+	}
+
+	if (username[0] == EAP_SIM_PSEUDONYM_PREFIX) {
+		const char *permanent;
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Pseudonym username '%s'",
+			   username);
+		permanent = eap_sim_db_get_permanent(
+			sm->eap_sim_db_priv, username);
+		os_free(username);
+		if (permanent == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown pseudonym "
+				   "identity - request permanent identity");
+			/* Remain in START state for another round */
+			return;
+		}
+		os_strlcpy(data->permanent, permanent,
+			   sizeof(data->permanent));
+	} else if (username[0] == EAP_SIM_PERMANENT_PREFIX) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Permanent username '%s'",
+			   username);
+		os_strlcpy(data->permanent, username, sizeof(data->permanent));
+		os_free(username);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized username '%s'",
+			   username);
+		os_free(username);
+		goto failed;
+	}
+
+skip_id_update:
+	/* Full authentication */
+
+	if (attr->nonce_mt == NULL || attr->selected_version < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing "
+			   "required attributes");
+		goto failed;
+	}
+
+	if (!eap_sim_supported_ver(data, attr->selected_version)) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported "
+			   "version %d", attr->selected_version);
+		goto failed;
+	}
+
+	data->counter = 0; /* reset re-auth counter since this is full auth */
+	data->reauth = NULL;
+
+	data->num_chal = eap_sim_db_get_gsm_triplets(
+		sm->eap_sim_db_priv, data->permanent, EAP_SIM_MAX_CHAL,
+		(u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm);
+	if (data->num_chal == EAP_SIM_DB_PENDING) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets "
+			   "not yet available - pending request");
+		sm->method_pending = METHOD_PENDING_WAIT;
+		return;
+	}
+	if (data->num_chal < 2) {
+		wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM "
+			   "authentication triplets for the peer");
+		goto failed;
+	}
+
+	identity_len = sm->identity_len;
+	while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop last null "
+			   "character from identity");
+		identity_len--;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity for MK derivation",
+			  sm->identity, identity_len);
+
+	os_memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+	WPA_PUT_BE16(ver_list, EAP_SIM_VERSION);
+	eap_sim_derive_mk(sm->identity, identity_len, attr->nonce_mt,
+			  attr->selected_version, ver_list, sizeof(ver_list),
+			  data->num_chal, (const u8 *) data->kc, data->mk);
+	eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
+			    data->emsk);
+
+	eap_sim_state(data, CHALLENGE);
+	return;
+
+failed:
+	data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+	eap_sim_state(data, NOTIFICATION);
+}
+
+
+static void eap_sim_process_challenge(struct eap_sm *sm,
+				      struct eap_sim_data *data,
+				      struct wpabuf *respData,
+				      struct eap_sim_attrs *attr)
+{
+	if (attr->mac == NULL ||
+	    eap_sim_verify_mac(data->k_aut, respData, attr->mac,
+			       (u8 *) data->sres,
+			       data->num_chal * EAP_SIM_SRES_LEN)) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
+			   "did not include valid AT_MAC");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_sim_state(data, NOTIFICATION);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the "
+		   "correct AT_MAC");
+	if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+		data->use_result_ind = 1;
+		data->notification = EAP_SIM_SUCCESS;
+		eap_sim_state(data, NOTIFICATION);
+	} else
+		eap_sim_state(data, SUCCESS);
+
+	if (data->next_pseudonym) {
+		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent,
+					 data->next_pseudonym);
+		data->next_pseudonym = NULL;
+	}
+	if (data->next_reauth_id) {
+		eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent,
+				      data->next_reauth_id, data->counter + 1,
+				      data->mk);
+		data->next_reauth_id = NULL;
+	}
+}
+
+
+static void eap_sim_process_reauth(struct eap_sm *sm,
+				   struct eap_sim_data *data,
+				   struct wpabuf *respData,
+				   struct eap_sim_attrs *attr)
+{
+	struct eap_sim_attrs eattr;
+	u8 *decrypted = NULL;
+
+	if (attr->mac == NULL ||
+	    eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s,
+			       EAP_SIM_NONCE_S_LEN)) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
+			   "did not include valid AT_MAC");
+		goto fail;
+	}
+
+	if (attr->encr_data == NULL || attr->iv == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
+			   "message did not include encrypted data");
+		goto fail;
+	}
+
+	decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+				       attr->encr_data_len, attr->iv, &eattr,
+				       0);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
+			   "data from reauthentication message");
+		goto fail;
+	}
+
+	if (eattr.counter != data->counter) {
+		wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
+			   "used incorrect counter %u, expected %u",
+			   eattr.counter, data->counter);
+		goto fail;
+	}
+	os_free(decrypted);
+	decrypted = NULL;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes "
+		   "the correct AT_MAC");
+
+	if (eattr.counter_too_small) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response "
+			   "included AT_COUNTER_TOO_SMALL - starting full "
+			   "authentication");
+		data->start_round = -1;
+		eap_sim_state(data, START);
+		return;
+	}
+
+	if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+		data->use_result_ind = 1;
+		data->notification = EAP_SIM_SUCCESS;
+		eap_sim_state(data, NOTIFICATION);
+	} else
+		eap_sim_state(data, SUCCESS);
+
+	if (data->next_reauth_id) {
+		eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent,
+				      data->next_reauth_id,
+				      data->counter + 1, data->mk);
+		data->next_reauth_id = NULL;
+	} else {
+		eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+		data->reauth = NULL;
+	}
+
+	return;
+
+fail:
+	data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+	eap_sim_state(data, NOTIFICATION);
+	eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+	data->reauth = NULL;
+	os_free(decrypted);
+}
+
+
+static void eap_sim_process_client_error(struct eap_sm *sm,
+					 struct eap_sim_data *data,
+					 struct wpabuf *respData,
+					 struct eap_sim_attrs *attr)
+{
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d",
+		   attr->client_error_code);
+	if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
+		eap_sim_state(data, SUCCESS);
+	else
+		eap_sim_state(data, FAILURE);
+}
+
+
+static void eap_sim_process_notification(struct eap_sm *sm,
+					 struct eap_sim_data *data,
+					 struct wpabuf *respData,
+					 struct eap_sim_attrs *attr)
+{
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Client replied to notification");
+	if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
+		eap_sim_state(data, SUCCESS);
+	else
+		eap_sim_state(data, FAILURE);
+}
+
+
+static void eap_sim_process(struct eap_sm *sm, void *priv,
+			    struct wpabuf *respData)
+{
+	struct eap_sim_data *data = priv;
+	const u8 *pos, *end;
+	u8 subtype;
+	size_t len;
+	struct eap_sim_attrs attr;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
+	if (pos == NULL || len < 3)
+		return;
+
+	end = pos + len;
+	subtype = *pos;
+	pos += 3;
+
+	if (eap_sim_unexpected_subtype(data, subtype)) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized or unexpected "
+			   "EAP-SIM Subtype in EAP Response");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_sim_state(data, NOTIFICATION);
+		return;
+	}
+
+	if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes");
+		if (subtype != EAP_SIM_SUBTYPE_CLIENT_ERROR &&
+		    (data->state == START || data->state == CHALLENGE ||
+		     data->state == REAUTH)) {
+			data->notification =
+				EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+			eap_sim_state(data, NOTIFICATION);
+			return;
+		}
+		eap_sim_state(data, FAILURE);
+		return;
+	}
+
+	if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) {
+		eap_sim_process_client_error(sm, data, respData, &attr);
+		return;
+	}
+
+	switch (data->state) {
+	case START:
+		eap_sim_process_start(sm, data, respData, &attr);
+		break;
+	case CHALLENGE:
+		eap_sim_process_challenge(sm, data, respData, &attr);
+		break;
+	case REAUTH:
+		eap_sim_process_reauth(sm, data, respData, &attr);
+		break;
+	case NOTIFICATION:
+		eap_sim_process_notification(sm, data, respData, &attr);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
+			   "process", data->state);
+		break;
+	}
+}
+
+
+static Boolean eap_sim_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_sim_data *data = priv;
+	return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_sim_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+	*len = EAP_SIM_KEYING_DATA_LEN;
+	return key;
+}
+
+
+static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_sim_data *data = priv;
+	u8 *key;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+	*len = EAP_EMSK_LEN;
+	return key;
+}
+
+
+static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_sim_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_sim_data *data = priv;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	*len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN;
+	id = os_malloc(*len);
+	if (id == NULL)
+		return NULL;
+
+	id[0] = EAP_TYPE_SIM;
+	os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN);
+	os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt,
+		  EAP_SIM_NONCE_MT_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len);
+
+	return id;
+}
+
+
+int eap_server_sim_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_sim_init;
+	eap->reset = eap_sim_reset;
+	eap->buildReq = eap_sim_buildReq;
+	eap->check = eap_sim_check;
+	eap->process = eap_sim_process;
+	eap->isDone = eap_sim_isDone;
+	eap->getKey = eap_sim_getKey;
+	eap->isSuccess = eap_sim_isSuccess;
+	eap->get_emsk = eap_sim_get_emsk;
+	eap->getSessionId = eap_sim_get_session_id;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_tls.c b/hostap/src/eap_server/eap_server_tls.c
new file mode 100644
index 0000000..bd18a4b
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_tls.c
@@ -0,0 +1,462 @@
+/*
+ * hostapd / EAP-TLS (RFC 2716)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "crypto/tls.h"
+
+
+static void eap_tls_reset(struct eap_sm *sm, void *priv);
+
+
+struct eap_tls_data {
+	struct eap_ssl_data ssl;
+	enum { START, CONTINUE, SUCCESS, FAILURE } state;
+	int established;
+	u8 eap_type;
+};
+
+
+static const char * eap_tls_state_txt(int state)
+{
+	switch (state) {
+	case START:
+		return "START";
+	case CONTINUE:
+		return "CONTINUE";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "Unknown?!";
+	}
+}
+
+
+static void eap_tls_state(struct eap_tls_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s",
+		   eap_tls_state_txt(data->state),
+		   eap_tls_state_txt(state));
+	data->state = state;
+	if (state == FAILURE)
+		tls_connection_remove_session(data->ssl.conn);
+}
+
+
+static void eap_tls_valid_session(struct eap_sm *sm, struct eap_tls_data *data)
+{
+	struct wpabuf *buf;
+
+	if (!sm->tls_session_lifetime)
+		return;
+
+	buf = wpabuf_alloc(1);
+	if (!buf)
+		return;
+	wpabuf_put_u8(buf, data->eap_type);
+	tls_connection_set_success_data(data->ssl.conn, buf);
+}
+
+
+static void * eap_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, 1, EAP_TYPE_TLS)) {
+		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+		eap_tls_reset(sm, data);
+		return NULL;
+	}
+
+	data->eap_type = EAP_TYPE_TLS;
+
+	return data;
+}
+
+
+#ifdef EAP_SERVER_UNAUTH_TLS
+static void * eap_unauth_tls_init(struct eap_sm *sm)
+{
+	struct eap_tls_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = START;
+
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_UNAUTH_TLS_TYPE)) {
+		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+		eap_tls_reset(sm, data);
+		return NULL;
+	}
+
+	data->eap_type = EAP_UNAUTH_TLS_TYPE;
+	return data;
+}
+#endif /* EAP_SERVER_UNAUTH_TLS */
+
+
+#ifdef CONFIG_HS20
+static void * eap_wfa_unauth_tls_init(struct eap_sm *sm)
+{
+	struct eap_tls_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = START;
+
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 0,
+				    EAP_WFA_UNAUTH_TLS_TYPE)) {
+		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+		eap_tls_reset(sm, data);
+		return NULL;
+	}
+
+	data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE;
+	return data;
+}
+#endif /* CONFIG_HS20 */
+
+
+static void eap_tls_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_tls_data *data = priv;
+	if (data == NULL)
+		return;
+	eap_server_tls_ssl_deinit(sm, &data->ssl);
+	os_free(data);
+}
+
+
+static struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
+					   struct eap_tls_data *data, u8 id)
+{
+	struct wpabuf *req;
+
+	req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
+			   "request");
+		eap_tls_state(data, FAILURE);
+		return NULL;
+	}
+
+	wpabuf_put_u8(req, EAP_TLS_FLAGS_START);
+
+	eap_tls_state(data, CONTINUE);
+
+	return req;
+}
+
+
+static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_tls_data *data = priv;
+	struct wpabuf *res;
+
+	if (data->ssl.state == FRAG_ACK) {
+		return eap_server_tls_build_ack(id, data->eap_type, 0);
+	}
+
+	if (data->ssl.state == WAIT_FRAG_ACK) {
+		res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0,
+					       id);
+		goto check_established;
+	}
+
+	switch (data->state) {
+	case START:
+		return eap_tls_build_start(sm, data, id);
+	case CONTINUE:
+		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
+			data->established = 1;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
+			   __func__, data->state);
+		return NULL;
+	}
+
+	res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id);
+
+check_established:
+	if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
+		/* TLS handshake has been completed and there are no more
+		 * fragments waiting to be sent out. */
+		wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
+		eap_tls_state(data, SUCCESS);
+		eap_tls_valid_session(sm, data);
+	}
+
+	return res;
+}
+
+
+static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_tls_data *data = priv;
+	const u8 *pos;
+	size_t len;
+
+	if (data->eap_type == EAP_UNAUTH_TLS_TYPE)
+		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);
+	if (pos == NULL || len < 1) {
+		wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static void eap_tls_process_msg(struct eap_sm *sm, void *priv,
+				const struct wpabuf *respData)
+{
+	struct eap_tls_data *data = priv;
+	if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) {
+		wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS "
+			   "handshake message");
+		return;
+	}
+	if (eap_server_tls_phase1(sm, &data->ssl) < 0)
+		eap_tls_state(data, FAILURE);
+}
+
+
+static void eap_tls_process(struct eap_sm *sm, void *priv,
+			    struct wpabuf *respData)
+{
+	struct eap_tls_data *data = priv;
+	const struct wpabuf *buf;
+	const u8 *pos;
+
+	if (eap_server_tls_process(sm, &data->ssl, respData, data,
+				   data->eap_type, NULL, eap_tls_process_msg) <
+	    0) {
+		eap_tls_state(data, FAILURE);
+		return;
+	}
+
+	if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+	    !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+		return;
+
+	buf = tls_connection_get_success_data(data->ssl.conn);
+	if (!buf || wpabuf_len(buf) < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TLS: No success data in resumed session - reject attempt");
+		eap_tls_state(data, FAILURE);
+		return;
+	}
+
+	pos = wpabuf_head(buf);
+	if (*pos != data->eap_type) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TLS: Resumed session for another EAP type (%u) - reject attempt",
+			   *pos);
+		eap_tls_state(data, FAILURE);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TLS: Resuming previous session");
+	eap_tls_state(data, SUCCESS);
+	tls_connection_set_success_data_resumed(data->ssl.conn);
+}
+
+
+static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_tls_data *data = priv;
+	return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_tls_data *data = priv;
+	u8 *eapKeyData;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+					       "client EAP encryption",
+					       EAP_TLS_KEY_LEN);
+	if (eapKeyData) {
+		*len = EAP_TLS_KEY_LEN;
+		wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
+			    eapKeyData, EAP_TLS_KEY_LEN);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
+	}
+
+	return eapKeyData;
+}
+
+
+static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_tls_data *data = priv;
+	u8 *eapKeyData, *emsk;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+					       "client EAP encryption",
+					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+	if (eapKeyData) {
+		emsk = os_malloc(EAP_EMSK_LEN);
+		if (emsk)
+			os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
+				  EAP_EMSK_LEN);
+		bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+	} else
+		emsk = NULL;
+
+	if (emsk) {
+		*len = EAP_EMSK_LEN;
+		wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
+			    emsk, EAP_EMSK_LEN);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
+	}
+
+	return emsk;
+}
+
+
+static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_tls_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_tls_data *data = priv;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TLS,
+						len);
+}
+
+
+int eap_server_tls_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_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;
+	eap->getSessionId = eap_tls_get_session_id;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
+
+
+#ifdef EAP_SERVER_UNAUTH_TLS
+int eap_server_unauth_tls_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_UNAUTH_TLS,
+				      EAP_VENDOR_TYPE_UNAUTH_TLS,
+				      "UNAUTH-TLS");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_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 /* 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/hostap/src/eap_server/eap_server_tls_common.c b/hostap/src/eap_server/eap_server_tls_common.c
new file mode 100644
index 0000000..05677b7
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_tls_common.c
@@ -0,0 +1,480 @@
+/*
+ * EAP-TLS/PEAP/TTLS/FAST server common functions
+ * 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 "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+
+
+static void eap_server_tls_free_in_buf(struct eap_ssl_data *data);
+
+
+struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
+				  u8 code, u8 identifier)
+{
+	if (type == EAP_UNAUTH_TLS_TYPE)
+		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, int eap_type)
+{
+	u8 session_ctx[8];
+	unsigned int flags = 0;
+
+	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;
+
+	data->conn = tls_connection_init(sm->ssl_ctx);
+	if (data->conn == NULL) {
+		wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
+			   "connection");
+		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 (eap_type != EAP_TYPE_FAST)
+		flags |= TLS_CONN_DISABLE_SESSION_TICKET;
+	os_memcpy(session_ctx, "hostapd", 7);
+	session_ctx[7] = (u8) eap_type;
+	if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer,
+				      flags, session_ctx,
+				      sizeof(session_ctx))) {
+		wpa_printf(MSG_INFO, "SSL: Failed to configure verification "
+			   "of TLS peer certificate");
+		tls_connection_deinit(sm->ssl_ctx, data->conn);
+		data->conn = NULL;
+		return -1;
+	}
+
+	data->tls_out_limit = sm->fragment_size > 0 ? sm->fragment_size : 1398;
+	if (data->phase2) {
+		/* Limit the fragment size in the inner TLS authentication
+		 * since the outer authentication with EAP-PEAP does not yet
+		 * support fragmentation */
+		if (data->tls_out_limit > 100)
+			data->tls_out_limit -= 100;
+	}
+	return 0;
+}
+
+
+void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+	tls_connection_deinit(sm->ssl_ctx, data->conn);
+	eap_server_tls_free_in_buf(data);
+	wpabuf_free(data->tls_out);
+	data->tls_out = NULL;
+}
+
+
+u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+			       char *label, size_t len)
+{
+	u8 *out;
+
+	out = os_malloc(len);
+	if (out == NULL)
+		return NULL;
+
+	if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, 0,
+			       out, len)) {
+		os_free(out);
+		return NULL;
+	}
+
+	return out;
+}
+
+
+/**
+ * eap_server_tls_derive_session_id - Derive a Session-Id based on TLS data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+ * @len: Pointer to length of the session ID generated
+ * Returns: Pointer to allocated Session-Id on success or %NULL on failure
+ *
+ * This function derive the Session-Id based on the TLS session data
+ * (client/server random and method type).
+ *
+ * The caller is responsible for freeing the returned buffer.
+ */
+u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
+				      struct eap_ssl_data *data, u8 eap_type,
+				      size_t *len)
+{
+	struct tls_random keys;
+	u8 *out;
+
+	if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys))
+		return NULL;
+
+	if (keys.client_random == NULL || keys.server_random == NULL)
+		return NULL;
+
+	*len = 1 + keys.client_random_len + keys.server_random_len;
+	out = os_malloc(*len);
+	if (out == NULL)
+		return NULL;
+
+	/* Session-Id = EAP type || client.random || server.random */
+	out[0] = eap_type;
+	os_memcpy(out + 1, keys.client_random, keys.client_random_len);
+	os_memcpy(out + 1 + keys.client_random_len, keys.server_random,
+		  keys.server_random_len);
+
+	return out;
+}
+
+
+struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data,
+					 int eap_type, int version, u8 id)
+{
+	struct wpabuf *req;
+	u8 flags;
+	size_t send_len, plen;
+
+	wpa_printf(MSG_DEBUG, "SSL: Generating Request");
+	if (data->tls_out == NULL) {
+		wpa_printf(MSG_ERROR, "SSL: tls_out NULL in %s", __func__);
+		return NULL;
+	}
+
+	flags = version;
+	send_len = wpabuf_len(data->tls_out) - data->tls_out_pos;
+	if (1 + send_len > data->tls_out_limit) {
+		send_len = data->tls_out_limit - 1;
+		flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
+		if (data->tls_out_pos == 0) {
+			flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
+			send_len -= 4;
+		}
+	}
+
+	plen = 1 + send_len;
+	if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
+		plen += 4;
+
+	req = eap_tls_msg_alloc(eap_type, plen, EAP_CODE_REQUEST, id);
+	if (req == NULL)
+		return NULL;
+
+	wpabuf_put_u8(req, flags); /* Flags */
+	if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
+		wpabuf_put_be32(req, wpabuf_len(data->tls_out));
+
+	wpabuf_put_data(req, wpabuf_head_u8(data->tls_out) + data->tls_out_pos,
+			send_len);
+	data->tls_out_pos += send_len;
+
+	if (data->tls_out_pos == wpabuf_len(data->tls_out)) {
+		wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes "
+			   "(message sent completely)",
+			   (unsigned long) send_len);
+		wpabuf_free(data->tls_out);
+		data->tls_out = NULL;
+		data->tls_out_pos = 0;
+		data->state = MSG;
+	} else {
+		wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes "
+			   "(%lu more to send)", (unsigned long) send_len,
+			   (unsigned long) wpabuf_len(data->tls_out) -
+			   data->tls_out_pos);
+		data->state = WAIT_FRAG_ACK;
+	}
+
+	return req;
+}
+
+
+struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version)
+{
+	struct wpabuf *req;
+
+	req = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_REQUEST, id);
+	if (req == NULL)
+		return NULL;
+	wpa_printf(MSG_DEBUG, "SSL: Building ACK");
+	wpabuf_put_u8(req, version); /* Flags */
+	return req;
+}
+
+
+static int eap_server_tls_process_cont(struct eap_ssl_data *data,
+				       const u8 *buf, size_t len)
+{
+	/* Process continuation of a pending message */
+	if (len > wpabuf_tailroom(data->tls_in)) {
+		wpa_printf(MSG_DEBUG, "SSL: Fragment overflow");
+		return -1;
+	}
+
+	wpabuf_put_data(data->tls_in, buf, len);
+	wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes, waiting for %lu "
+		   "bytes more", (unsigned long) len,
+		   (unsigned long) wpabuf_tailroom(data->tls_in));
+
+	return 0;
+}
+
+
+static int eap_server_tls_process_fragment(struct eap_ssl_data *data,
+					   u8 flags, u32 message_length,
+					   const u8 *buf, size_t len)
+{
+	/* Process a fragment that is not the last one of the message */
+	if (data->tls_in == NULL && !(flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)) {
+		wpa_printf(MSG_DEBUG, "SSL: No Message Length field in a "
+			   "fragmented packet");
+		return -1;
+	}
+
+	if (data->tls_in == NULL) {
+		/* First fragment of the message */
+
+		/* Limit length to avoid rogue peers from causing large
+		 * memory allocations. */
+		if (message_length > 65536) {
+			wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size"
+				   " over 64 kB)");
+			return -1;
+		}
+
+		if (len > message_length) {
+			wpa_printf(MSG_INFO, "SSL: Too much data (%d bytes) in "
+				   "first fragment of frame (TLS Message "
+				   "Length %d bytes)",
+				   (int) len, (int) message_length);
+			return -1;
+		}
+
+		data->tls_in = wpabuf_alloc(message_length);
+		if (data->tls_in == NULL) {
+			wpa_printf(MSG_DEBUG, "SSL: No memory for message");
+			return -1;
+		}
+		wpabuf_put_data(data->tls_in, buf, len);
+		wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes in first "
+			   "fragment, waiting for %lu bytes more",
+			   (unsigned long) len,
+			   (unsigned long) wpabuf_tailroom(data->tls_in));
+	}
+
+	return 0;
+}
+
+
+int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+	if (data->tls_out) {
+		/* This should not happen.. */
+		wpa_printf(MSG_INFO, "SSL: pending tls_out data when "
+			   "processing new message");
+		wpabuf_free(data->tls_out);
+		WPA_ASSERT(data->tls_out == NULL);
+	}
+
+	data->tls_out = tls_connection_server_handshake(sm->ssl_ctx,
+							data->conn,
+							data->tls_in, NULL);
+	if (data->tls_out == NULL) {
+		wpa_printf(MSG_INFO, "SSL: TLS processing failed");
+		return -1;
+	}
+	if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+		/* TLS processing has failed - return error */
+		wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
+			   "report error");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags,
+				     const u8 **pos, size_t *left)
+{
+	unsigned int tls_msg_len = 0;
+	const u8 *end = *pos + *left;
+
+	if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+		if (*left < 4) {
+			wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
+				   "length");
+			return -1;
+		}
+		tls_msg_len = WPA_GET_BE32(*pos);
+		wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
+			   tls_msg_len);
+		*pos += 4;
+		*left -= 4;
+
+		if (*left > tls_msg_len) {
+			wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d "
+				   "bytes) smaller than this fragment (%d "
+				   "bytes)", (int) tls_msg_len, (int) *left);
+			return -1;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "SSL: Received packet: Flags 0x%x "
+		   "Message Length %u", flags, tls_msg_len);
+
+	if (data->state == WAIT_FRAG_ACK) {
+		if (*left != 0) {
+			wpa_printf(MSG_DEBUG, "SSL: Unexpected payload in "
+				   "WAIT_FRAG_ACK state");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "SSL: Fragment acknowledged");
+		return 1;
+	}
+
+	if (data->tls_in &&
+	    eap_server_tls_process_cont(data, *pos, end - *pos) < 0)
+		return -1;
+		
+	if (flags & EAP_TLS_FLAGS_MORE_FRAGMENTS) {
+		if (eap_server_tls_process_fragment(data, flags, tls_msg_len,
+						    *pos, end - *pos) < 0)
+			return -1;
+
+		data->state = FRAG_ACK;
+		return 1;
+	}
+
+	if (data->state == FRAG_ACK) {
+		wpa_printf(MSG_DEBUG, "SSL: All fragments received");
+		data->state = MSG;
+	}
+
+	if (data->tls_in == NULL) {
+		/* Wrap unfragmented messages as wpabuf without extra copy */
+		wpabuf_set(&data->tmpbuf, *pos, end - *pos);
+		data->tls_in = &data->tmpbuf;
+	}
+
+	return 0;
+}
+
+
+static void eap_server_tls_free_in_buf(struct eap_ssl_data *data)
+{
+	if (data->tls_in != &data->tmpbuf)
+		wpabuf_free(data->tls_in);
+	data->tls_in = NULL;
+}
+
+
+struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm,
+				       struct eap_ssl_data *data,
+				       const struct wpabuf *plain)
+{
+	struct wpabuf *buf;
+
+	buf = tls_connection_encrypt(sm->ssl_ctx, data->conn,
+				     plain);
+	if (buf == NULL) {
+		wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 data");
+		return NULL;
+	}
+
+	return buf;
+}
+
+
+int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data,
+			   struct wpabuf *respData, void *priv, int eap_type,
+			   int (*proc_version)(struct eap_sm *sm, void *priv,
+					       int peer_version),
+			   void (*proc_msg)(struct eap_sm *sm, void *priv,
+					    const struct wpabuf *respData))
+{
+	const u8 *pos;
+	u8 flags;
+	size_t left;
+	int ret, res = 0;
+
+	if (eap_type == EAP_UNAUTH_TLS_TYPE)
+		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);
+	if (pos == NULL || left < 1)
+		return 0; /* Should not happen - frame already validated */
+	flags = *pos++;
+	left--;
+	wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - Flags 0x%02x",
+		   (unsigned long) wpabuf_len(respData), flags);
+
+	if (proc_version &&
+	    proc_version(sm, priv, flags & EAP_TLS_VERSION_MASK) < 0)
+		return -1;
+
+	ret = eap_server_tls_reassemble(data, flags, &pos, &left);
+	if (ret < 0) {
+		res = -1;
+		goto done;
+	} else if (ret == 1)
+		return 0;
+
+	if (proc_msg)
+		proc_msg(sm, priv, respData);
+
+	if (tls_connection_get_write_alerts(sm->ssl_ctx, data->conn) > 1) {
+		wpa_printf(MSG_INFO, "SSL: Locally detected fatal error in "
+			   "TLS processing");
+		res = -1;
+	}
+
+done:
+	eap_server_tls_free_in_buf(data);
+
+	return res;
+}
diff --git a/hostap/src/eap_server/eap_server_tnc.c b/hostap/src/eap_server/eap_server_tnc.c
new file mode 100644
index 0000000..21bd26f
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_tnc.c
@@ -0,0 +1,576 @@
+/*
+ * EAP server method: EAP-TNC (Trusted Network Connect)
+ * Copyright (c) 2007-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "tncs.h"
+
+
+struct eap_tnc_data {
+	enum eap_tnc_state {
+		START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE,
+		FAIL
+	} state;
+	enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation;
+	struct tncs_data *tncs;
+	struct wpabuf *in_buf;
+	struct wpabuf *out_buf;
+	size_t out_used;
+	size_t fragment_size;
+	unsigned int was_done:1;
+	unsigned int was_fail:1;
+};
+
+
+/* EAP-TNC Flags */
+#define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TNC_FLAGS_START 0x20
+#define EAP_TNC_VERSION_MASK 0x07
+
+#define EAP_TNC_VERSION 1
+
+
+static const char * eap_tnc_state_txt(enum eap_tnc_state state)
+{
+	switch (state) {
+	case START:
+		return "START";
+	case CONTINUE:
+		return "CONTINUE";
+	case RECOMMENDATION:
+		return "RECOMMENDATION";
+	case FRAG_ACK:
+		return "FRAG_ACK";
+	case WAIT_FRAG_ACK:
+		return "WAIT_FRAG_ACK";
+	case DONE:
+		return "DONE";
+	case FAIL:
+		return "FAIL";
+	}
+	return "??";
+}
+
+
+static void eap_tnc_set_state(struct eap_tnc_data *data,
+			      enum eap_tnc_state new_state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s",
+		   eap_tnc_state_txt(data->state),
+		   eap_tnc_state_txt(new_state));
+	data->state = new_state;
+}
+
+
+static void * eap_tnc_init(struct eap_sm *sm)
+{
+	struct eap_tnc_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	eap_tnc_set_state(data, START);
+	data->tncs = tncs_init();
+	if (data->tncs == NULL) {
+		os_free(data);
+		return NULL;
+	}
+
+	data->fragment_size = sm->fragment_size > 100 ?
+		sm->fragment_size - 98 : 1300;
+
+	return data;
+}
+
+
+static void eap_tnc_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_tnc_data *data = priv;
+	wpabuf_free(data->in_buf);
+	wpabuf_free(data->out_buf);
+	tncs_deinit(data->tncs);
+	os_free(data);
+}
+
+
+static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm,
+					   struct eap_tnc_data *data, u8 id)
+{
+	struct wpabuf *req;
+
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST,
+			    id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for "
+			   "request");
+		eap_tnc_set_state(data, FAIL);
+		return NULL;
+	}
+
+	wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION);
+
+	eap_tnc_set_state(data, CONTINUE);
+
+	return req;
+}
+
+
+static struct wpabuf * eap_tnc_build(struct eap_sm *sm,
+				     struct eap_tnc_data *data)
+{
+	struct wpabuf *req;
+	u8 *rpos, *rpos1;
+	size_t rlen;
+	char *start_buf, *end_buf;
+	size_t start_len, end_len;
+	size_t imv_len;
+
+	imv_len = tncs_total_send_len(data->tncs);
+
+	start_buf = tncs_if_tnccs_start(data->tncs);
+	if (start_buf == NULL)
+		return NULL;
+	start_len = os_strlen(start_buf);
+	end_buf = tncs_if_tnccs_end();
+	if (end_buf == NULL) {
+		os_free(start_buf);
+		return NULL;
+	}
+	end_len = os_strlen(end_buf);
+
+	rlen = start_len + imv_len + end_len;
+	req = wpabuf_alloc(rlen);
+	if (req == NULL) {
+		os_free(start_buf);
+		os_free(end_buf);
+		return NULL;
+	}
+
+	wpabuf_put_data(req, start_buf, start_len);
+	os_free(start_buf);
+
+	rpos1 = wpabuf_put(req, 0);
+	rpos = tncs_copy_send_buf(data->tncs, rpos1);
+	wpabuf_put(req, rpos - rpos1);
+
+	wpabuf_put_data(req, end_buf, end_len);
+	os_free(end_buf);
+
+	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request",
+			  wpabuf_head(req), wpabuf_len(req));
+
+	return req;
+}
+
+
+static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm,
+						    struct eap_tnc_data *data)
+{
+	switch (data->recommendation) {
+	case ALLOW:
+		eap_tnc_set_state(data, DONE);
+		break;
+	case ISOLATE:
+		eap_tnc_set_state(data, FAIL);
+		/* TODO: support assignment to a different VLAN */
+		break;
+	case NO_ACCESS:
+		eap_tnc_set_state(data, FAIL);
+		break;
+	case NO_RECOMMENDATION:
+		eap_tnc_set_state(data, DONE);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation");
+		return NULL;
+	}
+
+	return eap_tnc_build(sm, data);
+}
+
+
+static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code)
+{
+	struct wpabuf *msg;
+
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id);
+	if (msg == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory "
+			   "for fragment ack");
+		return NULL;
+	}
+	wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */
+
+	wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack");
+
+	return msg;
+}
+
+
+static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id)
+{
+	struct wpabuf *req;
+	u8 flags;
+	size_t send_len, plen;
+
+	wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request");
+
+	flags = EAP_TNC_VERSION;
+	send_len = wpabuf_len(data->out_buf) - data->out_used;
+	if (1 + send_len > data->fragment_size) {
+		send_len = data->fragment_size - 1;
+		flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS;
+		if (data->out_used == 0) {
+			flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED;
+			send_len -= 4;
+		}
+	}
+
+	plen = 1 + send_len;
+	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
+		plen += 4;
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL)
+		return NULL;
+
+	wpabuf_put_u8(req, flags); /* Flags */
+	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
+		wpabuf_put_be32(req, wpabuf_len(data->out_buf));
+
+	wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
+			send_len);
+	data->out_used += send_len;
+
+	if (data->out_used == wpabuf_len(data->out_buf)) {
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
+			   "(message sent completely)",
+			   (unsigned long) send_len);
+		wpabuf_free(data->out_buf);
+		data->out_buf = NULL;
+		data->out_used = 0;
+		if (data->was_fail)
+			eap_tnc_set_state(data, FAIL);
+		else if (data->was_done)
+			eap_tnc_set_state(data, DONE);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
+			   "(%lu more to send)", (unsigned long) send_len,
+			   (unsigned long) wpabuf_len(data->out_buf) -
+			   data->out_used);
+		if (data->state == FAIL)
+			data->was_fail = 1;
+		else if (data->state == DONE)
+			data->was_done = 1;
+		eap_tnc_set_state(data, WAIT_FRAG_ACK);
+	}
+
+	return req;
+}
+
+
+static struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_tnc_data *data = priv;
+
+	switch (data->state) {
+	case START:
+		tncs_init_connection(data->tncs);
+		return eap_tnc_build_start(sm, data, id);
+	case CONTINUE:
+		if (data->out_buf == NULL) {
+			data->out_buf = eap_tnc_build(sm, data);
+			if (data->out_buf == NULL) {
+				wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
+					   "generate message");
+				return NULL;
+			}
+			data->out_used = 0;
+		}
+		return eap_tnc_build_msg(data, id);
+	case RECOMMENDATION:
+		if (data->out_buf == NULL) {
+			data->out_buf = eap_tnc_build_recommendation(sm, data);
+			if (data->out_buf == NULL) {
+				wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
+					   "generate recommendation message");
+				return NULL;
+			}
+			data->out_used = 0;
+		}
+		return eap_tnc_build_msg(data, id);
+	case WAIT_FRAG_ACK:
+		return eap_tnc_build_msg(data, id);
+	case FRAG_ACK:
+		return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST);
+	case DONE:
+	case FAIL:
+		return NULL;
+	}
+
+	return NULL;
+}
+
+
+static Boolean eap_tnc_check(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_tnc_data *data = priv;
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData,
+			       &len);
+	if (pos == NULL) {
+		wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame");
+		return TRUE;
+	}
+
+	if (len == 0 && data->state != WAIT_FRAG_ACK) {
+		wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)");
+		return TRUE;
+	}
+
+	if (len == 0)
+		return FALSE; /* Fragment ACK does not include flags */
+
+	if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) {
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d",
+			   *pos & EAP_TNC_VERSION_MASK);
+		return TRUE;
+	}
+
+	if (*pos & EAP_TNC_FLAGS_START) {
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf)
+{
+	enum tncs_process_res res;
+
+	res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf),
+				    wpabuf_len(inbuf));
+	switch (res) {
+	case TNCCS_RECOMMENDATION_ALLOW:
+		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access");
+		eap_tnc_set_state(data, RECOMMENDATION);
+		data->recommendation = ALLOW;
+		break;
+	case TNCCS_RECOMMENDATION_NO_RECOMMENDATION:
+		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation");
+		eap_tnc_set_state(data, RECOMMENDATION);
+		data->recommendation = NO_RECOMMENDATION;
+		break;
+	case TNCCS_RECOMMENDATION_ISOLATE:
+		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation");
+		eap_tnc_set_state(data, RECOMMENDATION);
+		data->recommendation = ISOLATE;
+		break;
+	case TNCCS_RECOMMENDATION_NO_ACCESS:
+		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access");
+		eap_tnc_set_state(data, RECOMMENDATION);
+		data->recommendation = NO_ACCESS;
+		break;
+	case TNCCS_PROCESS_ERROR:
+		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error");
+		eap_tnc_set_state(data, FAIL);
+		break;
+	default:
+		break;
+	}
+}
+
+
+static int eap_tnc_process_cont(struct eap_tnc_data *data,
+				const u8 *buf, size_t len)
+{
+	/* Process continuation of a pending message */
+	if (len > wpabuf_tailroom(data->in_buf)) {
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow");
+		eap_tnc_set_state(data, FAIL);
+		return -1;
+	}
+
+	wpabuf_put_data(data->in_buf, buf, len);
+	wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu "
+		   "bytes more", (unsigned long) len,
+		   (unsigned long) wpabuf_tailroom(data->in_buf));
+
+	return 0;
+}
+
+
+static int eap_tnc_process_fragment(struct eap_tnc_data *data,
+				    u8 flags, u32 message_length,
+				    const u8 *buf, size_t len)
+{
+	/* Process a fragment that is not the last one of the message */
+	if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) {
+		wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a "
+			   "fragmented packet");
+		return -1;
+	}
+
+	if (data->in_buf == NULL) {
+		/* First fragment of the message */
+		data->in_buf = wpabuf_alloc(message_length);
+		if (data->in_buf == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for "
+				   "message");
+			return -1;
+		}
+		wpabuf_put_data(data->in_buf, buf, len);
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first "
+			   "fragment, waiting for %lu bytes more",
+			   (unsigned long) len,
+			   (unsigned long) wpabuf_tailroom(data->in_buf));
+	}
+
+	return 0;
+}
+
+
+static void eap_tnc_process(struct eap_sm *sm, void *priv,
+			    struct wpabuf *respData)
+{
+	struct eap_tnc_data *data = priv;
+	const u8 *pos, *end;
+	size_t len;
+	u8 flags;
+	u32 message_length = 0;
+	struct wpabuf tmpbuf;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len);
+	if (pos == NULL)
+		return; /* Should not happen; message already verified */
+
+	end = pos + len;
+
+	if (len == 1 && (data->state == DONE || data->state == FAIL)) {
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last "
+			   "message");
+		return;
+	}
+
+	if (len == 0) {
+		/* fragment ack */
+		flags = 0;
+	} else
+		flags = *pos++;
+
+	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) {
+		if (end - pos < 4) {
+			wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow");
+			eap_tnc_set_state(data, FAIL);
+			return;
+		}
+		message_length = WPA_GET_BE32(pos);
+		pos += 4;
+
+		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));
+			eap_tnc_set_state(data, FAIL);
+			return;
+		}
+	}
+	wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x "
+		   "Message Length %u", flags, message_length);
+
+	if (data->state == WAIT_FRAG_ACK) {
+		if (len > 1) {
+			wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload "
+				   "in WAIT_FRAG_ACK state");
+			eap_tnc_set_state(data, FAIL);
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged");
+		eap_tnc_set_state(data, CONTINUE);
+		return;
+	}
+
+	if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) {
+		eap_tnc_set_state(data, FAIL);
+		return;
+	}
+		
+	if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) {
+		if (eap_tnc_process_fragment(data, flags, message_length,
+					     pos, end - pos) < 0)
+			eap_tnc_set_state(data, FAIL);
+		else
+			eap_tnc_set_state(data, FRAG_ACK);
+		return;
+	} else if (data->state == FRAG_ACK) {
+		wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
+		eap_tnc_set_state(data, CONTINUE);
+	}
+
+	if (data->in_buf == NULL) {
+		/* Wrap unfragmented messages as wpabuf without extra copy */
+		wpabuf_set(&tmpbuf, pos, end - pos);
+		data->in_buf = &tmpbuf;
+	}
+
+	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload",
+			  wpabuf_head(data->in_buf), wpabuf_len(data->in_buf));
+	tncs_process(data, data->in_buf);
+
+	if (data->in_buf != &tmpbuf)
+		wpabuf_free(data->in_buf);
+	data->in_buf = NULL;
+}
+
+
+static Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_tnc_data *data = priv;
+	return data->state == DONE || data->state == FAIL;
+}
+
+
+static Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_tnc_data *data = priv;
+	return data->state == DONE;
+}
+
+
+int eap_server_tnc_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_tnc_init;
+	eap->reset = eap_tnc_reset;
+	eap->buildReq = eap_tnc_buildReq;
+	eap->check = eap_tnc_check;
+	eap->process = eap_tnc_process;
+	eap->isDone = eap_tnc_isDone;
+	eap->isSuccess = eap_tnc_isSuccess;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_ttls.c b/hostap/src/eap_server/eap_server_ttls.c
new file mode 100644
index 0000000..53ffa1e
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_ttls.c
@@ -0,0 +1,1360 @@
+/*
+ * hostapd / EAP-TTLS (RFC 5281)
+ * Copyright (c) 2004-2011, 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/ms_funcs.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_server/eap_i.h"
+#include "eap_server/eap_tls_common.h"
+#include "eap_common/chap.h"
+#include "eap_common/eap_ttls.h"
+
+
+#define EAP_TTLS_VERSION 0
+
+
+static void eap_ttls_reset(struct eap_sm *sm, void *priv);
+
+
+struct eap_ttls_data {
+	struct eap_ssl_data ssl;
+	enum {
+		START, PHASE1, PHASE2_START, PHASE2_METHOD,
+		PHASE2_MSCHAPV2_RESP, SUCCESS, FAILURE
+	} state;
+
+	int ttls_version;
+	const struct eap_method *phase2_method;
+	void *phase2_priv;
+	int mschapv2_resp_ok;
+	u8 mschapv2_auth_response[20];
+	u8 mschapv2_ident;
+	struct wpabuf *pending_phase2_eap_resp;
+	int tnc_started;
+};
+
+
+static const char * eap_ttls_state_txt(int state)
+{
+	switch (state) {
+	case START:
+		return "START";
+	case PHASE1:
+		return "PHASE1";
+	case PHASE2_START:
+		return "PHASE2_START";
+	case PHASE2_METHOD:
+		return "PHASE2_METHOD";
+	case PHASE2_MSCHAPV2_RESP:
+		return "PHASE2_MSCHAPV2_RESP";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "Unknown?!";
+	}
+}
+
+
+static void eap_ttls_state(struct eap_ttls_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: %s -> %s",
+		   eap_ttls_state_txt(data->state),
+		   eap_ttls_state_txt(state));
+	data->state = state;
+	if (state == FAILURE)
+		tls_connection_remove_session(data->ssl.conn);
+}
+
+
+static void eap_ttls_valid_session(struct eap_sm *sm,
+				   struct eap_ttls_data *data)
+{
+	struct wpabuf *buf;
+
+	if (!sm->tls_session_lifetime)
+		return;
+
+	buf = wpabuf_alloc(1 + 1 + sm->identity_len);
+	if (!buf)
+		return;
+	wpabuf_put_u8(buf, EAP_TYPE_TTLS);
+	if (sm->identity) {
+		u8 id_len;
+
+		if (sm->identity_len <= 255)
+			id_len = sm->identity_len;
+		else
+			id_len = 255;
+		wpabuf_put_u8(buf, id_len);
+		wpabuf_put_data(buf, sm->identity, id_len);
+	} else {
+		wpabuf_put_u8(buf, 0);
+	}
+	tls_connection_set_success_data(data->ssl.conn, buf);
+}
+
+
+static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
+			     int mandatory, size_t len)
+{
+	struct ttls_avp_vendor *avp;
+	u8 flags;
+	size_t hdrlen;
+
+	avp = (struct ttls_avp_vendor *) avphdr;
+	flags = mandatory ? AVP_FLAGS_MANDATORY : 0;
+	if (vendor_id) {
+		flags |= AVP_FLAGS_VENDOR;
+		hdrlen = sizeof(*avp);
+		avp->vendor_id = host_to_be32(vendor_id);
+	} else {
+		hdrlen = sizeof(struct ttls_avp);
+	}
+
+	avp->avp_code = host_to_be32(avp_code);
+	avp->avp_length = host_to_be32(((u32) flags << 24) |
+				       ((u32) (hdrlen + len)));
+
+	return avphdr + hdrlen;
+}
+
+
+static struct wpabuf * eap_ttls_avp_encapsulate(struct wpabuf *resp,
+						u32 avp_code, int mandatory)
+{
+	struct wpabuf *avp;
+	u8 *pos;
+
+	avp = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(resp) + 4);
+	if (avp == NULL) {
+		wpabuf_free(resp);
+		return NULL;
+	}
+
+	pos = eap_ttls_avp_hdr(wpabuf_mhead(avp), avp_code, 0, mandatory,
+			       wpabuf_len(resp));
+	os_memcpy(pos, wpabuf_head(resp), wpabuf_len(resp));
+	pos += wpabuf_len(resp);
+	AVP_PAD((const u8 *) wpabuf_head(avp), pos);
+	wpabuf_free(resp);
+	wpabuf_put(avp, pos - (u8 *) wpabuf_head(avp));
+	return avp;
+}
+
+
+struct eap_ttls_avp {
+	 /* Note: eap is allocated memory; caller is responsible for freeing
+	  * it. All the other pointers are pointing to the packet data, i.e.,
+	  * they must not be freed separately. */
+	u8 *eap;
+	size_t eap_len;
+	u8 *user_name;
+	size_t user_name_len;
+	u8 *user_password;
+	size_t user_password_len;
+	u8 *chap_challenge;
+	size_t chap_challenge_len;
+	u8 *chap_password;
+	size_t chap_password_len;
+	u8 *mschap_challenge;
+	size_t mschap_challenge_len;
+	u8 *mschap_response;
+	size_t mschap_response_len;
+	u8 *mschap2_response;
+	size_t mschap2_response_len;
+};
+
+
+static int eap_ttls_avp_parse(struct wpabuf *buf, struct eap_ttls_avp *parse)
+{
+	struct ttls_avp *avp;
+	u8 *pos;
+	int left;
+
+	pos = wpabuf_mhead(buf);
+	left = wpabuf_len(buf);
+	os_memset(parse, 0, sizeof(*parse));
+
+	while (left > 0) {
+		u32 avp_code, avp_length, vendor_id = 0;
+		u8 avp_flags, *dpos;
+		size_t pad, dlen;
+		avp = (struct ttls_avp *) pos;
+		avp_code = be_to_host32(avp->avp_code);
+		avp_length = be_to_host32(avp->avp_length);
+		avp_flags = (avp_length >> 24) & 0xff;
+		avp_length &= 0xffffff;
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x "
+			   "length=%d", (int) avp_code, avp_flags,
+			   (int) avp_length);
+		if ((int) avp_length > left) {
+			wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow "
+				   "(len=%d, left=%d) - dropped",
+				   (int) avp_length, left);
+			goto fail;
+		}
+		if (avp_length < sizeof(*avp)) {
+			wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length "
+				   "%d", avp_length);
+			goto fail;
+		}
+		dpos = (u8 *) (avp + 1);
+		dlen = avp_length - sizeof(*avp);
+		if (avp_flags & AVP_FLAGS_VENDOR) {
+			if (dlen < 4) {
+				wpa_printf(MSG_WARNING, "EAP-TTLS: vendor AVP "
+					   "underflow");
+				goto fail;
+			}
+			vendor_id = be_to_host32(* (be32 *) dpos);
+			wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d",
+				   (int) vendor_id);
+			dpos += 4;
+			dlen -= 4;
+		}
+
+		wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen);
+
+		if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
+			wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
+			if (parse->eap == NULL) {
+				parse->eap = os_malloc(dlen);
+				if (parse->eap == NULL) {
+					wpa_printf(MSG_WARNING, "EAP-TTLS: "
+						   "failed to allocate memory "
+						   "for Phase 2 EAP data");
+					goto fail;
+				}
+				os_memcpy(parse->eap, dpos, dlen);
+				parse->eap_len = dlen;
+			} else {
+				u8 *neweap = os_realloc(parse->eap,
+							parse->eap_len + dlen);
+				if (neweap == NULL) {
+					wpa_printf(MSG_WARNING, "EAP-TTLS: "
+						   "failed to allocate memory "
+						   "for Phase 2 EAP data");
+					goto fail;
+				}
+				os_memcpy(neweap + parse->eap_len, dpos, dlen);
+				parse->eap = neweap;
+				parse->eap_len += dlen;
+			}
+		} else if (vendor_id == 0 &&
+			   avp_code == RADIUS_ATTR_USER_NAME) {
+			wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: User-Name",
+					  dpos, dlen);
+			parse->user_name = dpos;
+			parse->user_name_len = dlen;
+		} else if (vendor_id == 0 &&
+			   avp_code == RADIUS_ATTR_USER_PASSWORD) {
+			u8 *password = dpos;
+			size_t password_len = dlen;
+			while (password_len > 0 &&
+			       password[password_len - 1] == '\0') {
+				password_len--;
+			}
+			wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: "
+					      "User-Password (PAP)",
+					      password, password_len);
+			parse->user_password = password;
+			parse->user_password_len = password_len;
+		} else if (vendor_id == 0 &&
+			   avp_code == RADIUS_ATTR_CHAP_CHALLENGE) {
+			wpa_hexdump(MSG_DEBUG,
+				    "EAP-TTLS: CHAP-Challenge (CHAP)",
+				    dpos, dlen);
+			parse->chap_challenge = dpos;
+			parse->chap_challenge_len = dlen;
+		} else if (vendor_id == 0 &&
+			   avp_code == RADIUS_ATTR_CHAP_PASSWORD) {
+			wpa_hexdump(MSG_DEBUG,
+				    "EAP-TTLS: CHAP-Password (CHAP)",
+				    dpos, dlen);
+			parse->chap_password = dpos;
+			parse->chap_password_len = dlen;
+		} else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+			   avp_code == RADIUS_ATTR_MS_CHAP_CHALLENGE) {
+			wpa_hexdump(MSG_DEBUG,
+				    "EAP-TTLS: MS-CHAP-Challenge",
+				    dpos, dlen);
+			parse->mschap_challenge = dpos;
+			parse->mschap_challenge_len = dlen;
+		} else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+			   avp_code == RADIUS_ATTR_MS_CHAP_RESPONSE) {
+			wpa_hexdump(MSG_DEBUG,
+				    "EAP-TTLS: MS-CHAP-Response (MSCHAP)",
+				    dpos, dlen);
+			parse->mschap_response = dpos;
+			parse->mschap_response_len = dlen;
+		} else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+			   avp_code == RADIUS_ATTR_MS_CHAP2_RESPONSE) {
+			wpa_hexdump(MSG_DEBUG,
+				    "EAP-TTLS: MS-CHAP2-Response (MSCHAPV2)",
+				    dpos, dlen);
+			parse->mschap2_response = dpos;
+			parse->mschap2_response_len = dlen;
+		} else if (avp_flags & AVP_FLAGS_MANDATORY) {
+			wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported "
+				   "mandatory AVP code %d vendor_id %d - "
+				   "dropped", (int) avp_code, (int) vendor_id);
+			goto fail;
+		} else {
+			wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported "
+				   "AVP code %d vendor_id %d",
+				   (int) avp_code, (int) vendor_id);
+		}
+
+		pad = (4 - (avp_length & 3)) & 3;
+		pos += avp_length + pad;
+		left -= avp_length + pad;
+	}
+
+	return 0;
+
+fail:
+	os_free(parse->eap);
+	parse->eap = NULL;
+	return -1;
+}
+
+
+static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
+					struct eap_ttls_data *data, size_t len)
+{
+	return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge",
+					 len);
+}
+
+
+static void * eap_ttls_init(struct eap_sm *sm)
+{
+	struct eap_ttls_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->ttls_version = EAP_TTLS_VERSION;
+	data->state = START;
+
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TTLS)) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
+		eap_ttls_reset(sm, data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+static void eap_ttls_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_ttls_data *data = priv;
+	if (data == NULL)
+		return;
+	if (data->phase2_priv && data->phase2_method)
+		data->phase2_method->reset(sm, data->phase2_priv);
+	eap_server_tls_ssl_deinit(sm, &data->ssl);
+	wpabuf_free(data->pending_phase2_eap_resp);
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_ttls_build_start(struct eap_sm *sm,
+					    struct eap_ttls_data *data, u8 id)
+{	
+	struct wpabuf *req;
+
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate memory for"
+			   " request");
+		eap_ttls_state(data, FAILURE);
+		return NULL;
+	}
+
+	wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->ttls_version);
+
+	eap_ttls_state(data, PHASE1);
+
+	return req;
+}
+
+
+static struct wpabuf * eap_ttls_build_phase2_eap_req(
+	struct eap_sm *sm, struct eap_ttls_data *data, u8 id)
+{
+	struct wpabuf *buf, *encr_req;
+
+
+	buf = data->phase2_method->buildReq(sm, data->phase2_priv, id);
+	if (buf == NULL)
+		return NULL;
+
+	wpa_hexdump_buf_key(MSG_DEBUG,
+			    "EAP-TTLS/EAP: Encapsulate Phase 2 data", buf);
+
+	buf = eap_ttls_avp_encapsulate(buf, RADIUS_ATTR_EAP_MESSAGE, 1);
+	if (buf == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Failed to encapsulate "
+			   "packet");
+		return NULL;
+	}
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated "
+			    "Phase 2 data", buf);
+
+	encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf);
+	wpabuf_free(buf);
+
+	return encr_req;
+}
+
+
+static struct wpabuf * eap_ttls_build_phase2_mschapv2(
+	struct eap_sm *sm, struct eap_ttls_data *data)
+{
+	struct wpabuf *encr_req, msgbuf;
+	u8 *req, *pos, *end;
+	int ret;
+
+	pos = req = os_malloc(100);
+	if (req == NULL)
+		return NULL;
+	end = req + 100;
+
+	if (data->mschapv2_resp_ok) {
+		pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_SUCCESS,
+				       RADIUS_VENDOR_ID_MICROSOFT, 1, 43);
+		*pos++ = data->mschapv2_ident;
+		ret = os_snprintf((char *) pos, end - pos, "S=");
+		if (!os_snprintf_error(end - pos, ret))
+			pos += ret;
+		pos += wpa_snprintf_hex_uppercase(
+			(char *) pos, end - pos, data->mschapv2_auth_response,
+			sizeof(data->mschapv2_auth_response));
+	} else {
+		pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_ERROR,
+				       RADIUS_VENDOR_ID_MICROSOFT, 1, 6);
+		os_memcpy(pos, "Failed", 6);
+		pos += 6;
+		AVP_PAD(req, pos);
+	}
+
+	wpabuf_set(&msgbuf, req, pos - req);
+	wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 "
+			    "data", &msgbuf);
+
+	encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf);
+	os_free(req);
+
+	return encr_req;
+}
+
+
+static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_ttls_data *data = priv;
+
+	if (data->ssl.state == FRAG_ACK) {
+		return eap_server_tls_build_ack(id, EAP_TYPE_TTLS,
+						data->ttls_version);
+	}
+
+	if (data->ssl.state == WAIT_FRAG_ACK) {
+		return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS,
+						data->ttls_version, id);
+	}
+
+	switch (data->state) {
+	case START:
+		return eap_ttls_build_start(sm, data, id);
+	case PHASE1:
+		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+			wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, "
+				   "starting Phase2");
+			eap_ttls_state(data, PHASE2_START);
+		}
+		break;
+	case PHASE2_METHOD:
+		wpabuf_free(data->ssl.tls_out);
+		data->ssl.tls_out_pos = 0;
+		data->ssl.tls_out = eap_ttls_build_phase2_eap_req(sm, data,
+								  id);
+		break;
+	case PHASE2_MSCHAPV2_RESP:
+		wpabuf_free(data->ssl.tls_out);
+		data->ssl.tls_out_pos = 0;
+		data->ssl.tls_out = eap_ttls_build_phase2_mschapv2(sm, data);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d",
+			   __func__, data->state);
+		return NULL;
+	}
+
+	return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS,
+					data->ttls_version, id);
+}
+
+
+static Boolean eap_ttls_check(struct eap_sm *sm, void *priv,
+			      struct wpabuf *respData)
+{
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TTLS, respData, &len);
+	if (pos == NULL || len < 1) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static void eap_ttls_process_phase2_pap(struct eap_sm *sm,
+					struct eap_ttls_data *data,
+					const u8 *user_password,
+					size_t user_password_len)
+{
+	if (!sm->user || !sm->user->password || sm->user->password_hash ||
+	    !(sm->user->ttls_auth & EAP_TTLS_AUTH_PAP)) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No plaintext user "
+			   "password configured");
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	if (sm->user->password_len != user_password_len ||
+	    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;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password");
+	eap_ttls_state(data, SUCCESS);
+	eap_ttls_valid_session(sm, data);
+}
+
+
+static void eap_ttls_process_phase2_chap(struct eap_sm *sm,
+					 struct eap_ttls_data *data,
+					 const u8 *challenge,
+					 size_t challenge_len,
+					 const u8 *password,
+					 size_t password_len)
+{
+	u8 *chal, hash[CHAP_MD5_LEN];
+
+	if (challenge == NULL || password == NULL ||
+	    challenge_len != EAP_TTLS_CHAP_CHALLENGE_LEN ||
+	    password_len != 1 + EAP_TTLS_CHAP_PASSWORD_LEN) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid CHAP attributes "
+			   "(challenge len %lu password len %lu)",
+			   (unsigned long) challenge_len,
+			   (unsigned long) password_len);
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	if (!sm->user || !sm->user->password || sm->user->password_hash ||
+	    !(sm->user->ttls_auth & EAP_TTLS_AUTH_CHAP)) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No plaintext user "
+			   "password configured");
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	chal = eap_ttls_implicit_challenge(sm, data,
+					   EAP_TTLS_CHAP_CHALLENGE_LEN + 1);
+	if (chal == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Failed to generate "
+			   "challenge from TLS data");
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	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);
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+	os_free(chal);
+
+	/* MD5(Ident + Password + Challenge) */
+	chap_md5(password[0], sm->user->password, sm->user->password_len,
+		 challenge, challenge_len, hash);
+
+	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);
+		eap_ttls_valid_session(sm, data);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password");
+		eap_ttls_state(data, FAILURE);
+	}
+}
+
+
+static void eap_ttls_process_phase2_mschap(struct eap_sm *sm,
+					   struct eap_ttls_data *data,
+					   u8 *challenge, size_t challenge_len,
+					   u8 *response, size_t response_len)
+{
+	u8 *chal, nt_response[24];
+
+	if (challenge == NULL || response == NULL ||
+	    challenge_len != EAP_TTLS_MSCHAP_CHALLENGE_LEN ||
+	    response_len != EAP_TTLS_MSCHAP_RESPONSE_LEN) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid MS-CHAP "
+			   "attributes (challenge len %lu response len %lu)",
+			   (unsigned long) challenge_len,
+			   (unsigned long) response_len);
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	if (!sm->user || !sm->user->password ||
+	    !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAP)) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: No user password "
+			   "configured");
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	chal = eap_ttls_implicit_challenge(sm, data,
+					   EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);
+	if (chal == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Failed to generate "
+			   "challenge from TLS data");
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	eap_server_mschap_rx_callback(sm, "TTLS-MSCHAP",
+				      sm->identity, sm->identity_len,
+				      challenge, response + 2 + 24);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	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);
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+	os_free(chal);
+
+	if (sm->user->password_hash)
+		challenge_response(challenge, sm->user->password, nt_response);
+	else
+		nt_challenge_response(challenge, sm->user->password,
+				      sm->user->password_len, nt_response);
+
+	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);
+		eap_ttls_valid_session(sm, data);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response");
+		wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received",
+			    response + 2 + 24, 24);
+		wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Expected",
+			    nt_response, 24);
+		eap_ttls_state(data, FAILURE);
+	}
+}
+
+
+static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
+					     struct eap_ttls_data *data,
+					     u8 *challenge,
+					     size_t challenge_len,
+					     u8 *response, size_t response_len)
+{
+	u8 *chal, *username, nt_response[24], *rx_resp, *peer_challenge,
+		*auth_challenge;
+	size_t username_len, i;
+
+	if (challenge == NULL || response == NULL ||
+	    challenge_len != EAP_TTLS_MSCHAPV2_CHALLENGE_LEN ||
+	    response_len != EAP_TTLS_MSCHAPV2_RESPONSE_LEN) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid MS-CHAP2 "
+			   "attributes (challenge len %lu response len %lu)",
+			   (unsigned long) challenge_len,
+			   (unsigned long) response_len);
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	if (!sm->user || !sm->user->password ||
+	    !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAPV2)) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user password "
+			   "configured");
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	if (sm->identity == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user identity "
+			   "known");
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	/* MSCHAPv2 does not include optional domain name in the
+	 * challenge-response calculation, so remove domain prefix
+	 * (if present). */
+	username = sm->identity;
+	username_len = sm->identity_len;
+	for (i = 0; i < username_len; i++) {
+		if (username[i] == '\\') {
+			username_len -= i + 1;
+			username += i + 1;
+			break;
+		}
+	}
+
+	chal = eap_ttls_implicit_challenge(
+		sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1);
+	if (chal == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Failed to generate "
+			   "challenge from TLS data");
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	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);
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+	os_free(chal);
+
+	auth_challenge = challenge;
+	peer_challenge = response + 2;
+
+	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: User",
+			  username, username_len);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: auth_challenge",
+		    auth_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: peer_challenge",
+		    peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+
+	if (sm->user->password_hash) {
+		generate_nt_response_pwhash(auth_challenge, peer_challenge,
+					    username, username_len,
+					    sm->user->password,
+					    nt_response);
+	} else {
+		generate_nt_response(auth_challenge, peer_challenge,
+				     username, username_len,
+				     sm->user->password,
+				     sm->user->password_len,
+				     nt_response);
+	}
+
+	rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8;
+#ifdef CONFIG_TESTING_OPTIONS
+	{
+		u8 challenge2[8];
+
+		if (challenge_hash(peer_challenge, auth_challenge,
+				   username, username_len, challenge2) == 0) {
+			eap_server_mschap_rx_callback(sm, "TTLS-MSCHAPV2",
+						      username, username_len,
+						      challenge2, rx_resp);
+		}
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	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;
+
+		if (sm->user->password_hash) {
+			generate_authenticator_response_pwhash(
+				sm->user->password,
+				peer_challenge, auth_challenge,
+				username, username_len, nt_response,
+				data->mschapv2_auth_response);
+		} else {
+			generate_authenticator_response(
+				sm->user->password, sm->user->password_len,
+				peer_challenge, auth_challenge,
+				username, username_len, nt_response,
+				data->mschapv2_auth_response);
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid "
+			   "NT-Response");
+		wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Received",
+			    rx_resp, 24);
+		wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Expected",
+			    nt_response, 24);
+		data->mschapv2_resp_ok = 0;
+	}
+	eap_ttls_state(data, PHASE2_MSCHAPV2_RESP);
+	data->mschapv2_ident = response[0];
+}
+
+
+static int eap_ttls_phase2_eap_init(struct eap_sm *sm,
+				    struct eap_ttls_data *data,
+				    EapType eap_type)
+{
+	if (data->phase2_priv && data->phase2_method) {
+		data->phase2_method->reset(sm, data->phase2_priv);
+		data->phase2_method = NULL;
+		data->phase2_priv = NULL;
+	}
+	data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF,
+							eap_type);
+	if (!data->phase2_method)
+		return -1;
+
+	sm->init_phase2 = 1;
+	data->phase2_priv = data->phase2_method->init(sm);
+	sm->init_phase2 = 0;
+	return data->phase2_priv == NULL ? -1 : 0;
+}
+
+
+static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm,
+						 struct eap_ttls_data *data,
+						 u8 *in_data, size_t in_len)
+{
+	u8 next_type = EAP_TYPE_NONE;
+	struct eap_hdr *hdr;
+	u8 *pos;
+	size_t left;
+	struct wpabuf buf;
+	const struct eap_method *m = data->phase2_method;
+	void *priv = data->phase2_priv;
+
+	if (priv == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: %s - Phase2 not "
+			   "initialized?!", __func__);
+		return;
+	}
+
+	hdr = (struct eap_hdr *) in_data;
+	pos = (u8 *) (hdr + 1);
+
+	if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
+		left = in_len - sizeof(*hdr);
+		wpa_hexdump(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 type Nak'ed; "
+			    "allowed types", pos + 1, left - 1);
+		eap_sm_process_nak(sm, pos + 1, left - 1);
+		if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+		    sm->user->methods[sm->user_eap_method_index].method !=
+		    EAP_TYPE_NONE) {
+			next_type = sm->user->methods[
+				sm->user_eap_method_index++].method;
+			wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d",
+				   next_type);
+			if (eap_ttls_phase2_eap_init(sm, data, next_type)) {
+				wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to "
+					   "initialize EAP type %d",
+					   next_type);
+				eap_ttls_state(data, FAILURE);
+				return;
+			}
+		} else {
+			eap_ttls_state(data, FAILURE);
+		}
+		return;
+	}
+
+	wpabuf_set(&buf, in_data, in_len);
+
+	if (m->check(sm, priv, &buf)) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 check() asked to "
+			   "ignore the packet");
+		return;
+	}
+
+	m->process(sm, priv, &buf);
+
+	if (sm->method_pending == METHOD_PENDING_WAIT) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method is in "
+			   "pending wait state - save decrypted response");
+		wpabuf_free(data->pending_phase2_eap_resp);
+		data->pending_phase2_eap_resp = wpabuf_dup(&buf);
+	}
+
+	if (!m->isDone(sm, priv))
+		return;
+
+	if (!m->isSuccess(sm, priv)) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method failed");
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	switch (data->state) {
+	case PHASE2_START:
+		if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+			wpa_hexdump_ascii(MSG_DEBUG, "EAP_TTLS: Phase2 "
+					  "Identity not found in the user "
+					  "database",
+					  sm->identity, sm->identity_len);
+			eap_ttls_state(data, FAILURE);
+			break;
+		}
+
+		eap_ttls_state(data, PHASE2_METHOD);
+		next_type = sm->user->methods[0].method;
+		sm->user_eap_method_index = 1;
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type);
+		if (eap_ttls_phase2_eap_init(sm, data, next_type)) {
+			wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize "
+				   "EAP type %d", next_type);
+			eap_ttls_state(data, FAILURE);
+		}
+		break;
+	case PHASE2_METHOD:
+		eap_ttls_state(data, SUCCESS);
+		eap_ttls_valid_session(sm, data);
+		break;
+	case FAILURE:
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d",
+			   __func__, data->state);
+		break;
+	}
+}
+
+
+static void eap_ttls_process_phase2_eap(struct eap_sm *sm,
+					struct eap_ttls_data *data,
+					const u8 *eap, size_t eap_len)
+{
+	struct eap_hdr *hdr;
+	size_t len;
+
+	if (data->state == PHASE2_START) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: initializing Phase 2");
+		if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_IDENTITY) < 0)
+		{
+			wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: failed to "
+				   "initialize EAP-Identity");
+			return;
+		}
+	}
+
+	if (eap_len < sizeof(*hdr)) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: too short Phase 2 EAP "
+			   "packet (len=%lu)", (unsigned long) eap_len);
+		return;
+	}
+
+	hdr = (struct eap_hdr *) eap;
+	len = be_to_host16(hdr->length);
+	wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: received Phase 2 EAP: code=%d "
+		   "identifier=%d length=%lu", hdr->code, hdr->identifier,
+		   (unsigned long) len);
+	if (len > eap_len) {
+		wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Length mismatch in Phase 2"
+			   " EAP frame (hdr len=%lu, data len in AVP=%lu)",
+			   (unsigned long) len, (unsigned long) eap_len);
+		return;
+	}
+
+	switch (hdr->code) {
+	case EAP_CODE_RESPONSE:
+		eap_ttls_process_phase2_eap_response(sm, data, (u8 *) hdr,
+						     len);
+		break;
+	default:
+		wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Unexpected code=%d in "
+			   "Phase 2 EAP header", hdr->code);
+		break;
+	}
+}
+
+
+static void eap_ttls_process_phase2(struct eap_sm *sm,
+				    struct eap_ttls_data *data,
+				    struct wpabuf *in_buf)
+{
+	struct wpabuf *in_decrypted;
+	struct eap_ttls_avp parse;
+
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"
+		   " Phase 2", (unsigned long) wpabuf_len(in_buf));
+
+	if (data->pending_phase2_eap_resp) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 EAP response "
+			   "- skip decryption and use old data");
+		eap_ttls_process_phase2_eap(
+			sm, data, wpabuf_head(data->pending_phase2_eap_resp),
+			wpabuf_len(data->pending_phase2_eap_resp));
+		wpabuf_free(data->pending_phase2_eap_resp);
+		data->pending_phase2_eap_resp = NULL;
+		return;
+	}
+
+	in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+					      in_buf);
+	if (in_decrypted == NULL) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 "
+			   "data");
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP",
+			    in_decrypted);
+
+	if (eap_ttls_avp_parse(in_decrypted, &parse) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to parse AVPs");
+		wpabuf_free(in_decrypted);
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	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) {
+			eap_ttls_state(data, FAILURE);
+			goto done;
+		}
+		os_memcpy(sm->identity, parse.user_name, parse.user_name_len);
+		sm->identity_len = parse.user_name_len;
+		if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1)
+		    != 0) {
+			wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not "
+				   "found in the user database");
+			eap_ttls_state(data, FAILURE);
+			goto done;
+		}
+	}
+
+#ifdef EAP_SERVER_TNC
+	if (data->tnc_started && parse.eap == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: TNC started but no EAP "
+			   "response from peer");
+		eap_ttls_state(data, FAILURE);
+		goto done;
+	}
+#endif /* EAP_SERVER_TNC */
+
+	if (parse.eap) {
+		eap_ttls_process_phase2_eap(sm, data, parse.eap,
+					    parse.eap_len);
+	} else if (parse.user_password) {
+		eap_ttls_process_phase2_pap(sm, data, parse.user_password,
+					    parse.user_password_len);
+	} else if (parse.chap_password) {
+		eap_ttls_process_phase2_chap(sm, data,
+					     parse.chap_challenge,
+					     parse.chap_challenge_len,
+					     parse.chap_password,
+					     parse.chap_password_len);
+	} else if (parse.mschap_response) {
+		eap_ttls_process_phase2_mschap(sm, data,
+					       parse.mschap_challenge,
+					       parse.mschap_challenge_len,
+					       parse.mschap_response,
+					       parse.mschap_response_len);
+	} else if (parse.mschap2_response) {
+		eap_ttls_process_phase2_mschapv2(sm, data,
+						 parse.mschap_challenge,
+						 parse.mschap_challenge_len,
+						 parse.mschap2_response,
+						 parse.mschap2_response_len);
+	}
+
+done:
+	wpabuf_free(in_decrypted);
+	os_free(parse.eap);
+}
+
+
+static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data)
+{
+#ifdef EAP_SERVER_TNC
+	if (!sm->tnc || data->state != SUCCESS || data->tnc_started)
+		return;
+
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: Initialize TNC");
+	if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_TNC)) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize TNC");
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	data->tnc_started = 1;
+	eap_ttls_state(data, PHASE2_METHOD);
+#endif /* EAP_SERVER_TNC */
+}
+
+
+static int eap_ttls_process_version(struct eap_sm *sm, void *priv,
+				    int peer_version)
+{
+	struct eap_ttls_data *data = priv;
+	if (peer_version < data->ttls_version) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: peer ver=%d, own ver=%d; "
+			   "use version %d",
+			   peer_version, data->ttls_version, peer_version);
+		data->ttls_version = peer_version;
+	}
+
+	return 0;
+}
+
+
+static void eap_ttls_process_msg(struct eap_sm *sm, void *priv,
+				 const struct wpabuf *respData)
+{
+	struct eap_ttls_data *data = priv;
+
+	switch (data->state) {
+	case PHASE1:
+		if (eap_server_tls_phase1(sm, &data->ssl) < 0)
+			eap_ttls_state(data, FAILURE);
+		break;
+	case PHASE2_START:
+	case PHASE2_METHOD:
+		eap_ttls_process_phase2(sm, data, data->ssl.tls_in);
+		eap_ttls_start_tnc(sm, data);
+		break;
+	case PHASE2_MSCHAPV2_RESP:
+		if (data->mschapv2_resp_ok && wpabuf_len(data->ssl.tls_in) ==
+		    0) {
+			wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
+				   "acknowledged response");
+			eap_ttls_state(data, SUCCESS);
+			eap_ttls_valid_session(sm, data);
+		} else if (!data->mschapv2_resp_ok) {
+			wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
+				   "acknowledged error");
+			eap_ttls_state(data, FAILURE);
+		} else {
+			wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Unexpected "
+				   "frame from peer (payload len %lu, "
+				   "expected empty frame)",
+				   (unsigned long)
+				   wpabuf_len(data->ssl.tls_in));
+			eap_ttls_state(data, FAILURE);
+		}
+		eap_ttls_start_tnc(sm, data);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected state %d in %s",
+			   data->state, __func__);
+		break;
+	}
+}
+
+
+static void eap_ttls_process(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_ttls_data *data = priv;
+	const struct wpabuf *buf;
+	const u8 *pos;
+	u8 id_len;
+
+	if (eap_server_tls_process(sm, &data->ssl, respData, data,
+				   EAP_TYPE_TTLS, eap_ttls_process_version,
+				   eap_ttls_process_msg) < 0) {
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+	    !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+		return;
+
+	buf = tls_connection_get_success_data(data->ssl.conn);
+	if (!buf || wpabuf_len(buf) < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TTLS: No success data in resumed session - reject attempt");
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	pos = wpabuf_head(buf);
+	if (*pos != EAP_TYPE_TTLS) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TTLS: Resumed session for another EAP type (%u) - reject attempt",
+			   *pos);
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	pos++;
+	id_len = *pos++;
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Identity from cached session",
+			  pos, id_len);
+	os_free(sm->identity);
+	sm->identity = os_malloc(id_len ? id_len : 1);
+	if (!sm->identity) {
+		sm->identity_len = 0;
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	os_memcpy(sm->identity, pos, id_len);
+	sm->identity_len = id_len;
+
+	if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not found in the user database",
+				  sm->identity, sm->identity_len);
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TTLS: Resuming previous session - skip Phase2");
+	eap_ttls_state(data, SUCCESS);
+	tls_connection_set_success_data_resumed(data->ssl.conn);
+}
+
+
+static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_ttls_data *data = priv;
+	return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ttls_data *data = priv;
+	u8 *eapKeyData;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+					       "ttls keying material",
+					       EAP_TLS_KEY_LEN);
+	if (eapKeyData) {
+		*len = EAP_TLS_KEY_LEN;
+		wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
+				eapKeyData, EAP_TLS_KEY_LEN);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
+	}
+
+	return eapKeyData;
+}
+
+
+static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_ttls_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ttls_data *data = priv;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TTLS,
+						len);
+}
+
+
+static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ttls_data *data = priv;
+	u8 *eapKeyData, *emsk;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+					       "ttls keying material",
+					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+	if (eapKeyData) {
+		emsk = os_malloc(EAP_EMSK_LEN);
+		if (emsk)
+			os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
+				  EAP_EMSK_LEN);
+		bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+	} else
+		emsk = NULL;
+
+	if (emsk) {
+		*len = EAP_EMSK_LEN;
+		wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived EMSK",
+			    emsk, EAP_EMSK_LEN);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive EMSK");
+	}
+
+	return emsk;
+}
+
+
+int eap_server_ttls_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_ttls_init;
+	eap->reset = eap_ttls_reset;
+	eap->buildReq = eap_ttls_buildReq;
+	eap->check = eap_ttls_check;
+	eap->process = eap_ttls_process;
+	eap->isDone = eap_ttls_isDone;
+	eap->getKey = eap_ttls_getKey;
+	eap->isSuccess = eap_ttls_isSuccess;
+	eap->getSessionId = eap_ttls_get_session_id;
+	eap->get_emsk = eap_ttls_get_emsk;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_vendor_test.c b/hostap/src/eap_server/eap_server_vendor_test.c
new file mode 100644
index 0000000..30f600d
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_vendor_test.c
@@ -0,0 +1,192 @@
+/*
+ * hostapd / Test method for vendor specific (expanded) EAP type
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP
+#define EAP_VENDOR_TYPE 0xfcfbfaf9
+
+
+struct eap_vendor_test_data {
+	enum { INIT, CONFIRM, SUCCESS, FAILURE } state;
+};
+
+
+static const char * eap_vendor_test_state_txt(int state)
+{
+	switch (state) {
+	case INIT:
+		return "INIT";
+	case CONFIRM:
+		return "CONFIRM";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "?";
+	}
+}
+
+
+static void eap_vendor_test_state(struct eap_vendor_test_data *data,
+				  int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: %s -> %s",
+		   eap_vendor_test_state_txt(data->state),
+		   eap_vendor_test_state_txt(state));
+	data->state = state;
+}
+
+
+static void * eap_vendor_test_init(struct eap_sm *sm)
+{
+	struct eap_vendor_test_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = INIT;
+
+	return data;
+}
+
+
+static void eap_vendor_test_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_vendor_test_data *data = priv;
+	os_free(data);
+}
+
+
+static struct wpabuf * eap_vendor_test_buildReq(struct eap_sm *sm, void *priv,
+						u8 id)
+{
+	struct eap_vendor_test_data *data = priv;
+	struct wpabuf *req;
+
+	req = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-VENDOR-TEST: Failed to allocate "
+			   "memory for request");
+		return NULL;
+	}
+
+	wpabuf_put_u8(req, data->state == INIT ? 1 : 3);
+
+	return req;
+}
+
+
+static Boolean eap_vendor_test_check(struct eap_sm *sm, void *priv,
+				     struct wpabuf *respData)
+{
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, respData, &len);
+	if (pos == NULL || len < 1) {
+		wpa_printf(MSG_INFO, "EAP-VENDOR-TEST: Invalid frame");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static void eap_vendor_test_process(struct eap_sm *sm, void *priv,
+				    struct wpabuf *respData)
+{
+	struct eap_vendor_test_data *data = priv;
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, respData, &len);
+	if (pos == NULL || len < 1)
+		return;
+
+	if (data->state == INIT) {
+		if (*pos == 2)
+			eap_vendor_test_state(data, CONFIRM);
+		else
+			eap_vendor_test_state(data, FAILURE);
+	} else if (data->state == CONFIRM) {
+		if (*pos == 4)
+			eap_vendor_test_state(data, SUCCESS);
+		else
+			eap_vendor_test_state(data, FAILURE);
+	} else
+		eap_vendor_test_state(data, FAILURE);
+}
+
+
+static Boolean eap_vendor_test_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_vendor_test_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_vendor_test_data *data = priv;
+	u8 *key;
+	const int key_len = 64;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	key = os_malloc(key_len);
+	if (key == NULL)
+		return NULL;
+
+	os_memset(key, 0x11, key_len / 2);
+	os_memset(key + key_len / 2, 0x22, key_len / 2);
+	*len = key_len;
+
+	return key;
+}
+
+
+static Boolean eap_vendor_test_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_vendor_test_data *data = priv;
+	return data->state == SUCCESS;
+}
+
+
+int eap_server_vendor_test_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_ID, EAP_VENDOR_TYPE,
+				      "VENDOR-TEST");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_vendor_test_init;
+	eap->reset = eap_vendor_test_reset;
+	eap->buildReq = eap_vendor_test_buildReq;
+	eap->check = eap_vendor_test_check;
+	eap->process = eap_vendor_test_process;
+	eap->isDone = eap_vendor_test_isDone;
+	eap->getKey = eap_vendor_test_getKey;
+	eap->isSuccess = eap_vendor_test_isSuccess;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_server_wsc.c b/hostap/src/eap_server/eap_server_wsc.c
new file mode 100644
index 0000000..9d9c28d
--- /dev/null
+++ b/hostap/src/eap_server/eap_server_wsc.c
@@ -0,0 +1,512 @@
+/*
+ * EAP-WSC server for Wi-Fi Protected Setup
+ * Copyright (c) 2007-2008, 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 "eloop.h"
+#include "eap_i.h"
+#include "eap_common/eap_wsc_common.h"
+#include "p2p/p2p.h"
+#include "wps/wps.h"
+
+
+struct eap_wsc_data {
+	enum { START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
+	int registrar;
+	struct wpabuf *in_buf;
+	struct wpabuf *out_buf;
+	enum wsc_op_code in_op_code, out_op_code;
+	size_t out_used;
+	size_t fragment_size;
+	struct wps_data *wps;
+	int ext_reg_timeout;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_wsc_state_txt(int state)
+{
+	switch (state) {
+	case START:
+		return "START";
+	case MESG:
+		return "MESG";
+	case FRAG_ACK:
+		return "FRAG_ACK";
+	case WAIT_FRAG_ACK:
+		return "WAIT_FRAG_ACK";
+	case DONE:
+		return "DONE";
+	case FAIL:
+		return "FAIL";
+	default:
+		return "?";
+	}
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_wsc_state(struct eap_wsc_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
+		   eap_wsc_state_txt(data->state),
+		   eap_wsc_state_txt(state));
+	data->state = state;
+}
+
+
+static void eap_wsc_ext_reg_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct eap_sm *sm = eloop_ctx;
+	struct eap_wsc_data *data = timeout_ctx;
+
+	if (sm->method_pending != METHOD_PENDING_WAIT)
+		return;
+
+	wpa_printf(MSG_DEBUG, "EAP-WSC: Timeout while waiting for an External "
+		   "Registrar");
+	data->ext_reg_timeout = 1;
+	eap_sm_pending_cb(sm);
+}
+
+
+static void * eap_wsc_init(struct eap_sm *sm)
+{
+	struct eap_wsc_data *data;
+	int registrar;
+	struct wps_config cfg;
+
+	if (sm->identity && sm->identity_len == WSC_ID_REGISTRAR_LEN &&
+	    os_memcmp(sm->identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) ==
+	    0)
+		registrar = 0; /* Supplicant is Registrar */
+	else if (sm->identity && sm->identity_len == WSC_ID_ENROLLEE_LEN &&
+		 os_memcmp(sm->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)
+		 == 0)
+		registrar = 1; /* Supplicant is Enrollee */
+	else {
+		wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
+				  sm->identity, sm->identity_len);
+		return NULL;
+	}
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = registrar ? START : MESG;
+	data->registrar = registrar;
+
+	os_memset(&cfg, 0, sizeof(cfg));
+	cfg.wps = sm->wps;
+	cfg.registrar = registrar;
+	if (registrar) {
+		if (sm->wps == NULL || sm->wps->registrar == NULL) {
+			wpa_printf(MSG_INFO, "EAP-WSC: WPS Registrar not "
+				   "initialized");
+			os_free(data);
+			return NULL;
+		}
+	} else {
+		if (sm->user == NULL || sm->user->password == NULL) {
+			/*
+			 * In theory, this should not really be needed, but
+			 * Windows 7 uses Registrar mode to probe AP's WPS
+			 * capabilities before trying to use Enrollee and fails
+			 * if the AP does not allow that probing to happen..
+			 */
+			wpa_printf(MSG_DEBUG, "EAP-WSC: No AP PIN (password) "
+				   "configured for Enrollee functionality - "
+				   "allow for probing capabilities (M1)");
+		} else {
+			cfg.pin = sm->user->password;
+			cfg.pin_len = sm->user->password_len;
+		}
+	}
+	cfg.assoc_wps_ie = sm->assoc_wps_ie;
+	cfg.peer_addr = sm->peer_addr;
+#ifdef CONFIG_P2P
+	if (sm->assoc_p2p_ie) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Prefer PSK format for P2P "
+			   "client");
+		cfg.use_psk_key = 1;
+		cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie);
+	}
+#endif /* CONFIG_P2P */
+	cfg.pbc_in_m1 = sm->pbc_in_m1;
+	data->wps = wps_init(&cfg);
+	if (data->wps == NULL) {
+		os_free(data);
+		return NULL;
+	}
+	data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size :
+		WSC_FRAGMENT_SIZE;
+
+	return data;
+}
+
+
+static void eap_wsc_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_wsc_data *data = priv;
+	eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
+	wpabuf_free(data->in_buf);
+	wpabuf_free(data->out_buf);
+	wps_deinit(data->wps);
+	os_free(data);
+}
+
+
+static struct wpabuf * eap_wsc_build_start(struct eap_sm *sm,
+					   struct eap_wsc_data *data, u8 id)
+{
+	struct wpabuf *req;
+
+	req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
+			   "request");
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/Start");
+	wpabuf_put_u8(req, WSC_Start); /* Op-Code */
+	wpabuf_put_u8(req, 0); /* Flags */
+
+	return req;
+}
+
+
+static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, u8 id)
+{
+	struct wpabuf *req;
+	u8 flags;
+	size_t send_len, plen;
+
+	flags = 0;
+	send_len = wpabuf_len(data->out_buf) - data->out_used;
+	if (2 + send_len > data->fragment_size) {
+		send_len = data->fragment_size - 2;
+		flags |= WSC_FLAGS_MF;
+		if (data->out_used == 0) {
+			flags |= WSC_FLAGS_LF;
+			send_len -= 2;
+		}
+	}
+	plen = 2 + send_len;
+	if (flags & WSC_FLAGS_LF)
+		plen += 2;
+	req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
+			    EAP_CODE_REQUEST, id);
+	if (req == NULL) {
+		wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
+			   "request");
+		return NULL;
+	}
+
+	wpabuf_put_u8(req, data->out_op_code); /* Op-Code */
+	wpabuf_put_u8(req, flags); /* Flags */
+	if (flags & WSC_FLAGS_LF)
+		wpabuf_put_be16(req, wpabuf_len(data->out_buf));
+
+	wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
+			send_len);
+	data->out_used += send_len;
+
+	if (data->out_used == wpabuf_len(data->out_buf)) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
+			   "(message sent completely)",
+			   (unsigned long) send_len);
+		wpabuf_free(data->out_buf);
+		data->out_buf = NULL;
+		data->out_used = 0;
+		eap_wsc_state(data, MESG);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
+			   "(%lu more to send)", (unsigned long) send_len,
+			   (unsigned long) wpabuf_len(data->out_buf) -
+			   data->out_used);
+		eap_wsc_state(data, WAIT_FRAG_ACK);
+	}
+
+	return req;
+}
+
+
+static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_wsc_data *data = priv;
+
+	switch (data->state) {
+	case START:
+		return eap_wsc_build_start(sm, data, id);
+	case MESG:
+		if (data->out_buf == NULL) {
+			data->out_buf = wps_get_msg(data->wps,
+						    &data->out_op_code);
+			if (data->out_buf == NULL) {
+				wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to "
+					   "receive message from WPS");
+				return NULL;
+			}
+			data->out_used = 0;
+		}
+		/* pass through */
+	case WAIT_FRAG_ACK:
+		return eap_wsc_build_msg(data, id);
+	case FRAG_ACK:
+		return eap_wsc_build_frag_ack(id, EAP_CODE_REQUEST);
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected state %d in "
+			   "buildReq", data->state);
+		return NULL;
+	}
+}
+
+
+static Boolean eap_wsc_check(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
+			       respData, &len);
+	if (pos == NULL || len < 2) {
+		wpa_printf(MSG_INFO, "EAP-WSC: Invalid frame");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static int eap_wsc_process_cont(struct eap_wsc_data *data,
+				const u8 *buf, size_t len, u8 op_code)
+{
+	/* Process continuation of a pending message */
+	if (op_code != data->in_op_code) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
+			   "fragment (expected %d)",
+			   op_code, data->in_op_code);
+		eap_wsc_state(data, FAIL);
+		return -1;
+	}
+
+	if (len > wpabuf_tailroom(data->in_buf)) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
+		eap_wsc_state(data, FAIL);
+		return -1;
+	}
+
+	wpabuf_put_data(data->in_buf, buf, len);
+	wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting for %lu "
+		   "bytes more", (unsigned long) len,
+		   (unsigned long) wpabuf_tailroom(data->in_buf));
+
+	return 0;
+}
+
+
+static int eap_wsc_process_fragment(struct eap_wsc_data *data,
+				    u8 flags, u8 op_code, u16 message_length,
+				    const u8 *buf, size_t len)
+{
+	/* Process a fragment that is not the last one of the message */
+	if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length "
+			   "field in a fragmented packet");
+		return -1;
+	}
+
+	if (data->in_buf == NULL) {
+		/* First fragment of the message */
+		data->in_buf = wpabuf_alloc(message_length);
+		if (data->in_buf == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
+				   "message");
+			return -1;
+		}
+		data->in_op_code = op_code;
+		wpabuf_put_data(data->in_buf, buf, len);
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in "
+			   "first fragment, waiting for %lu bytes more",
+			   (unsigned long) len,
+			   (unsigned long) wpabuf_tailroom(data->in_buf));
+	}
+
+	return 0;
+}
+
+
+static void eap_wsc_process(struct eap_sm *sm, void *priv,
+			    struct wpabuf *respData)
+{
+	struct eap_wsc_data *data = priv;
+	const u8 *start, *pos, *end;
+	size_t len;
+	u8 op_code, flags;
+	u16 message_length = 0;
+	enum wps_process_res res;
+	struct wpabuf tmpbuf;
+
+	eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
+	if (data->ext_reg_timeout) {
+		eap_wsc_state(data, FAIL);
+		return;
+	}
+
+	pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
+			       respData, &len);
+	if (pos == NULL || len < 2)
+		return; /* Should not happen; message already verified */
+
+	start = pos;
+	end = start + len;
+
+	op_code = *pos++;
+	flags = *pos++;
+	if (flags & WSC_FLAGS_LF) {
+		if (end - pos < 2) {
+			wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
+			return;
+		}
+		message_length = WPA_GET_BE16(pos);
+		pos += 2;
+
+		if (message_length < end - pos || message_length > 50000) {
+			wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
+				   "Length");
+			return;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
+		   "Flags 0x%x Message Length %d",
+		   op_code, flags, message_length);
+
+	if (data->state == WAIT_FRAG_ACK) {
+		if (op_code != WSC_FRAG_ACK) {
+			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
+				   "in WAIT_FRAG_ACK state", op_code);
+			eap_wsc_state(data, FAIL);
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
+		eap_wsc_state(data, MESG);
+		return;
+	}
+
+	if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
+	    op_code != WSC_Done) {
+		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
+			   op_code);
+		eap_wsc_state(data, FAIL);
+		return;
+	}
+
+	if (data->in_buf &&
+	    eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
+		eap_wsc_state(data, FAIL);
+		return;
+	}
+
+	if (flags & WSC_FLAGS_MF) {
+		if (eap_wsc_process_fragment(data, flags, op_code,
+					     message_length, pos, end - pos) <
+		    0)
+			eap_wsc_state(data, FAIL);
+		else
+			eap_wsc_state(data, FRAG_ACK);
+		return;
+	}
+
+	if (data->in_buf == NULL) {
+		/* Wrap unfragmented messages as wpabuf without extra copy */
+		wpabuf_set(&tmpbuf, pos, end - pos);
+		data->in_buf = &tmpbuf;
+	}
+
+	res = wps_process_msg(data->wps, op_code, data->in_buf);
+	switch (res) {
+	case WPS_DONE:
+		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
+			   "successfully - report EAP failure");
+		eap_wsc_state(data, FAIL);
+		break;
+	case WPS_CONTINUE:
+		eap_wsc_state(data, MESG);
+		break;
+	case WPS_FAILURE:
+		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
+		eap_wsc_state(data, FAIL);
+		break;
+	case WPS_PENDING:
+		eap_wsc_state(data, MESG);
+		sm->method_pending = METHOD_PENDING_WAIT;
+		eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
+		eloop_register_timeout(5, 0, eap_wsc_ext_reg_timeout,
+				       sm, data);
+		break;
+	}
+
+	if (data->in_buf != &tmpbuf)
+		wpabuf_free(data->in_buf);
+	data->in_buf = NULL;
+}
+
+
+static Boolean eap_wsc_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_wsc_data *data = priv;
+	return data->state == FAIL;
+}
+
+
+static Boolean eap_wsc_isSuccess(struct eap_sm *sm, void *priv)
+{
+	/* EAP-WSC will always result in EAP-Failure */
+	return FALSE;
+}
+
+
+static int eap_wsc_getTimeout(struct eap_sm *sm, void *priv)
+{
+	/* Recommended retransmit times: retransmit timeout 5 seconds,
+	 * per-message timeout 15 seconds, i.e., 3 tries. */
+	sm->MaxRetrans = 2; /* total 3 attempts */
+	return 5;
+}
+
+
+int eap_server_wsc_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
+				      "WSC");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_wsc_init;
+	eap->reset = eap_wsc_reset;
+	eap->buildReq = eap_wsc_buildReq;
+	eap->check = eap_wsc_check;
+	eap->process = eap_wsc_process;
+	eap->isDone = eap_wsc_isDone;
+	eap->isSuccess = eap_wsc_isSuccess;
+	eap->getTimeout = eap_wsc_getTimeout;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
diff --git a/hostap/src/eap_server/eap_sim_db.c b/hostap/src/eap_server/eap_sim_db.c
new file mode 100644
index 0000000..acf5435
--- /dev/null
+++ b/hostap/src/eap_server/eap_sim_db.c
@@ -0,0 +1,1503 @@
+/*
+ * hostapd / EAP-SIM database/authenticator gateway
+ * Copyright (c) 2005-2010, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This is an example implementation of the EAP-SIM/AKA database/authentication
+ * gateway interface that is using an external program as an SS7 gateway to
+ * GSM/UMTS authentication center (HLR/AuC). hlr_auc_gw is an example
+ * implementation of such a gateway program. This eap_sim_db.c takes care of
+ * EAP-SIM/AKA pseudonyms and re-auth identities. It can be used with different
+ * gateway implementations for HLR/AuC access. Alternatively, it can also be
+ * completely replaced if the in-memory database of pseudonyms/re-auth
+ * identities is not suitable for some cases.
+ */
+
+#include "includes.h"
+#include <sys/un.h>
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
+
+#include "common.h"
+#include "crypto/random.h"
+#include "eap_common/eap_sim_common.h"
+#include "eap_server/eap_sim_db.h"
+#include "eloop.h"
+
+struct eap_sim_pseudonym {
+	struct eap_sim_pseudonym *next;
+	char *permanent; /* permanent username */
+	char *pseudonym; /* pseudonym username */
+};
+
+struct eap_sim_db_pending {
+	struct eap_sim_db_pending *next;
+	char imsi[20];
+	enum { PENDING, SUCCESS, FAILURE } state;
+	void *cb_session_ctx;
+	int aka;
+	union {
+		struct {
+			u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
+			u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
+			u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
+			int num_chal;
+		} sim;
+		struct {
+			u8 rand[EAP_AKA_RAND_LEN];
+			u8 autn[EAP_AKA_AUTN_LEN];
+			u8 ik[EAP_AKA_IK_LEN];
+			u8 ck[EAP_AKA_CK_LEN];
+			u8 res[EAP_AKA_RES_MAX_LEN];
+			size_t res_len;
+		} aka;
+	} u;
+};
+
+struct eap_sim_db_data {
+	int sock;
+	char *fname;
+	char *local_sock;
+	void (*get_complete_cb)(void *ctx, void *session_ctx);
+	void *ctx;
+	struct eap_sim_pseudonym *pseudonyms;
+	struct eap_sim_reauth *reauths;
+	struct eap_sim_db_pending *pending;
+#ifdef CONFIG_SQLITE
+	sqlite3 *sqlite_db;
+	char db_tmp_identity[100];
+	char db_tmp_pseudonym_str[100];
+	struct eap_sim_pseudonym db_tmp_pseudonym;
+	struct eap_sim_reauth db_tmp_reauth;
+#endif /* CONFIG_SQLITE */
+};
+
+
+#ifdef CONFIG_SQLITE
+
+static int db_table_exists(sqlite3 *db, const char *name)
+{
+	char cmd[128];
+	os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
+	return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
+}
+
+
+static int db_table_create_pseudonym(sqlite3 *db)
+{
+	char *err = NULL;
+	const char *sql =
+		"CREATE TABLE pseudonyms("
+		"  permanent CHAR(21) PRIMARY KEY,"
+		"  pseudonym CHAR(21) NOT NULL"
+		");";
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for "
+		   "pseudonym information");
+	if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int db_table_create_reauth(sqlite3 *db)
+{
+	char *err = NULL;
+	const char *sql =
+		"CREATE TABLE reauth("
+		"  permanent CHAR(21) PRIMARY KEY,"
+		"  reauth_id CHAR(21) NOT NULL,"
+		"  counter INTEGER,"
+		"  mk CHAR(40),"
+		"  k_encr CHAR(32),"
+		"  k_aut CHAR(64),"
+		"  k_re CHAR(64)"
+		");";
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for "
+		   "reauth information");
+	if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static sqlite3 * db_open(const char *db_file)
+{
+	sqlite3 *db;
+
+	if (sqlite3_open(db_file, &db)) {
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: Failed to open database "
+			   "%s: %s", db_file, sqlite3_errmsg(db));
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	if (!db_table_exists(db, "pseudonyms") &&
+	    db_table_create_pseudonym(db) < 0) {
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	if (!db_table_exists(db, "reauth") &&
+	    db_table_create_reauth(db) < 0) {
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	return db;
+}
+
+
+static int valid_db_string(const char *str)
+{
+	const char *pos = str;
+	while (*pos) {
+		if ((*pos < '0' || *pos > '9') &&
+		    (*pos < 'a' || *pos > 'f'))
+			return 0;
+		pos++;
+	}
+	return 1;
+}
+
+
+static int db_add_pseudonym(struct eap_sim_db_data *data,
+			    const char *permanent, char *pseudonym)
+{
+	char cmd[128];
+	char *err = NULL;
+
+	if (!valid_db_string(permanent) || !valid_db_string(pseudonym)) {
+		os_free(pseudonym);
+		return -1;
+	}
+
+	os_snprintf(cmd, sizeof(cmd), "INSERT OR REPLACE INTO pseudonyms "
+		    "(permanent, pseudonym) VALUES ('%s', '%s');",
+		    permanent, pseudonym);
+	os_free(pseudonym);
+	if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK)
+	{
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int get_pseudonym_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+	struct eap_sim_db_data *data = ctx;
+	int i;
+
+	for (i = 0; i < argc; i++) {
+		if (os_strcmp(col[i], "permanent") == 0 && argv[i]) {
+			os_strlcpy(data->db_tmp_identity, argv[i],
+				   sizeof(data->db_tmp_identity));
+		}
+	}
+
+	return 0;
+}
+
+
+static char *
+db_get_pseudonym(struct eap_sim_db_data *data, const char *pseudonym)
+{
+	char cmd[128];
+
+	if (!valid_db_string(pseudonym))
+		return NULL;
+	os_memset(&data->db_tmp_identity, 0, sizeof(data->db_tmp_identity));
+	os_snprintf(cmd, sizeof(cmd),
+		    "SELECT permanent FROM pseudonyms WHERE pseudonym='%s';",
+		    pseudonym);
+	if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) !=
+	    SQLITE_OK)
+		return NULL;
+	if (data->db_tmp_identity[0] == '\0')
+		return NULL;
+	return data->db_tmp_identity;
+}
+
+
+static int db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
+			 char *reauth_id, u16 counter, const u8 *mk,
+			 const u8 *k_encr, const u8 *k_aut, const u8 *k_re)
+{
+	char cmd[2000], *pos, *end;
+	char *err = NULL;
+
+	if (!valid_db_string(permanent) || !valid_db_string(reauth_id)) {
+		os_free(reauth_id);
+		return -1;
+	}
+
+	pos = cmd;
+	end = pos + sizeof(cmd);
+	pos += os_snprintf(pos, end - pos, "INSERT OR REPLACE INTO reauth "
+			   "(permanent, reauth_id, counter%s%s%s%s) "
+			   "VALUES ('%s', '%s', %u",
+			   mk ? ", mk" : "",
+			   k_encr ? ", k_encr" : "",
+			   k_aut ? ", k_aut" : "",
+			   k_re ? ", k_re" : "",
+			   permanent, reauth_id, counter);
+	os_free(reauth_id);
+
+	if (mk) {
+		pos += os_snprintf(pos, end - pos, ", '");
+		pos += wpa_snprintf_hex(pos, end - pos, mk, EAP_SIM_MK_LEN);
+		pos += os_snprintf(pos, end - pos, "'");
+	}
+
+	if (k_encr) {
+		pos += os_snprintf(pos, end - pos, ", '");
+		pos += wpa_snprintf_hex(pos, end - pos, k_encr,
+					EAP_SIM_K_ENCR_LEN);
+		pos += os_snprintf(pos, end - pos, "'");
+	}
+
+	if (k_aut) {
+		pos += os_snprintf(pos, end - pos, ", '");
+		pos += wpa_snprintf_hex(pos, end - pos, k_aut,
+					EAP_AKA_PRIME_K_AUT_LEN);
+		pos += os_snprintf(pos, end - pos, "'");
+	}
+
+	if (k_re) {
+		pos += os_snprintf(pos, end - pos, ", '");
+		pos += wpa_snprintf_hex(pos, end - pos, k_re,
+					EAP_AKA_PRIME_K_RE_LEN);
+		pos += os_snprintf(pos, end - pos, "'");
+	}
+
+	os_snprintf(pos, end - pos, ");");
+
+	if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK)
+	{
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int get_reauth_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+	struct eap_sim_db_data *data = ctx;
+	int i;
+	struct eap_sim_reauth *reauth = &data->db_tmp_reauth;
+
+	for (i = 0; i < argc; i++) {
+		if (os_strcmp(col[i], "permanent") == 0 && argv[i]) {
+			os_strlcpy(data->db_tmp_identity, argv[i],
+				   sizeof(data->db_tmp_identity));
+			reauth->permanent = data->db_tmp_identity;
+		} else if (os_strcmp(col[i], "counter") == 0 && argv[i]) {
+			reauth->counter = atoi(argv[i]);
+		} else if (os_strcmp(col[i], "mk") == 0 && argv[i]) {
+			hexstr2bin(argv[i], reauth->mk, sizeof(reauth->mk));
+		} else if (os_strcmp(col[i], "k_encr") == 0 && argv[i]) {
+			hexstr2bin(argv[i], reauth->k_encr,
+				   sizeof(reauth->k_encr));
+		} else if (os_strcmp(col[i], "k_aut") == 0 && argv[i]) {
+			hexstr2bin(argv[i], reauth->k_aut,
+				   sizeof(reauth->k_aut));
+		} else if (os_strcmp(col[i], "k_re") == 0 && argv[i]) {
+			hexstr2bin(argv[i], reauth->k_re,
+				   sizeof(reauth->k_re));
+		}
+	}
+
+	return 0;
+}
+
+
+static struct eap_sim_reauth *
+db_get_reauth(struct eap_sim_db_data *data, const char *reauth_id)
+{
+	char cmd[256];
+
+	if (!valid_db_string(reauth_id))
+		return NULL;
+	os_memset(&data->db_tmp_reauth, 0, sizeof(data->db_tmp_reauth));
+	os_strlcpy(data->db_tmp_pseudonym_str, reauth_id,
+		   sizeof(data->db_tmp_pseudonym_str));
+	data->db_tmp_reauth.reauth_id = data->db_tmp_pseudonym_str;
+	os_snprintf(cmd, sizeof(cmd),
+		    "SELECT * FROM reauth WHERE reauth_id='%s';", reauth_id);
+	if (sqlite3_exec(data->sqlite_db, cmd, get_reauth_cb, data, NULL) !=
+	    SQLITE_OK)
+		return NULL;
+	if (data->db_tmp_reauth.permanent == NULL)
+		return NULL;
+	return &data->db_tmp_reauth;
+}
+
+
+static void db_remove_reauth(struct eap_sim_db_data *data,
+			     struct eap_sim_reauth *reauth)
+{
+	char cmd[256];
+
+	if (!valid_db_string(reauth->permanent))
+		return;
+	os_snprintf(cmd, sizeof(cmd),
+		    "DELETE FROM reauth WHERE permanent='%s';",
+		    reauth->permanent);
+	sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, NULL);
+}
+
+#endif /* CONFIG_SQLITE */
+
+
+static struct eap_sim_db_pending *
+eap_sim_db_get_pending(struct eap_sim_db_data *data, const char *imsi, int aka)
+{
+	struct eap_sim_db_pending *entry, *prev = NULL;
+
+	entry = data->pending;
+	while (entry) {
+		if (entry->aka == aka && os_strcmp(entry->imsi, imsi) == 0) {
+			if (prev)
+				prev->next = entry->next;
+			else
+				data->pending = entry->next;
+			break;
+		}
+		prev = entry;
+		entry = entry->next;
+	}
+	return entry;
+}
+
+
+static void eap_sim_db_add_pending(struct eap_sim_db_data *data,
+				   struct eap_sim_db_pending *entry)
+{
+	entry->next = data->pending;
+	data->pending = entry;
+}
+
+
+static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
+				     const char *imsi, char *buf)
+{
+	char *start, *end, *pos;
+	struct eap_sim_db_pending *entry;
+	int num_chal;
+
+	/*
+	 * SIM-RESP-AUTH <IMSI> Kc(i):SRES(i):RAND(i) ...
+	 * SIM-RESP-AUTH <IMSI> FAILURE
+	 * (IMSI = ASCII string, Kc/SRES/RAND = hex string)
+	 */
+
+	entry = eap_sim_db_get_pending(data, imsi, 0);
+	if (entry == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
+			   "received message found");
+		return;
+	}
+
+	start = buf;
+	if (os_strncmp(start, "FAILURE", 7) == 0) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
+			   "failure");
+		entry->state = FAILURE;
+		eap_sim_db_add_pending(data, entry);
+		data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+		return;
+	}
+
+	num_chal = 0;
+	while (num_chal < EAP_SIM_MAX_CHAL) {
+		end = os_strchr(start, ' ');
+		if (end)
+			*end = '\0';
+
+		pos = os_strchr(start, ':');
+		if (pos == NULL)
+			goto parse_fail;
+		*pos = '\0';
+		if (hexstr2bin(start, entry->u.sim.kc[num_chal],
+			       EAP_SIM_KC_LEN))
+			goto parse_fail;
+
+		start = pos + 1;
+		pos = os_strchr(start, ':');
+		if (pos == NULL)
+			goto parse_fail;
+		*pos = '\0';
+		if (hexstr2bin(start, entry->u.sim.sres[num_chal],
+			       EAP_SIM_SRES_LEN))
+			goto parse_fail;
+
+		start = pos + 1;
+		if (hexstr2bin(start, entry->u.sim.rand[num_chal],
+			       GSM_RAND_LEN))
+			goto parse_fail;
+
+		num_chal++;
+		if (end == NULL)
+			break;
+		else
+			start = end + 1;
+	}
+	entry->u.sim.num_chal = num_chal;
+
+	entry->state = SUCCESS;
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
+		   "successfully - callback");
+	eap_sim_db_add_pending(data, entry);
+	data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+	return;
+
+parse_fail:
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
+	os_free(entry);
+}
+
+
+static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data,
+				     const char *imsi, char *buf)
+{
+	char *start, *end;
+	struct eap_sim_db_pending *entry;
+
+	/*
+	 * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES>
+	 * AKA-RESP-AUTH <IMSI> FAILURE
+	 * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string)
+	 */
+
+	entry = eap_sim_db_get_pending(data, imsi, 1);
+	if (entry == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
+			   "received message found");
+		return;
+	}
+
+	start = buf;
+	if (os_strncmp(start, "FAILURE", 7) == 0) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
+			   "failure");
+		entry->state = FAILURE;
+		eap_sim_db_add_pending(data, entry);
+		data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+		return;
+	}
+
+	end = os_strchr(start, ' ');
+	if (end == NULL)
+		goto parse_fail;
+	*end = '\0';
+	if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN))
+		goto parse_fail;
+
+	start = end + 1;
+	end = os_strchr(start, ' ');
+	if (end == NULL)
+		goto parse_fail;
+	*end = '\0';
+	if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN))
+		goto parse_fail;
+
+	start = end + 1;
+	end = os_strchr(start, ' ');
+	if (end == NULL)
+		goto parse_fail;
+	*end = '\0';
+	if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN))
+		goto parse_fail;
+
+	start = end + 1;
+	end = os_strchr(start, ' ');
+	if (end == NULL)
+		goto parse_fail;
+	*end = '\0';
+	if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN))
+		goto parse_fail;
+
+	start = end + 1;
+	end = os_strchr(start, ' ');
+	if (end)
+		*end = '\0';
+	else {
+		end = start;
+		while (*end)
+			end++;
+	}
+	entry->u.aka.res_len = (end - start) / 2;
+	if (entry->u.aka.res_len > EAP_AKA_RES_MAX_LEN) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Too long RES");
+		entry->u.aka.res_len = 0;
+		goto parse_fail;
+	}
+	if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len))
+		goto parse_fail;
+
+	entry->state = SUCCESS;
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
+		   "successfully - callback");
+	eap_sim_db_add_pending(data, entry);
+	data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+	return;
+
+parse_fail:
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
+	os_free(entry);
+}
+
+
+static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct eap_sim_db_data *data = eloop_ctx;
+	char buf[1000], *pos, *cmd, *imsi;
+	int res;
+
+	res = recv(sock, buf, sizeof(buf) - 1, 0);
+	if (res < 0)
+		return;
+	buf[res] = '\0';
+	wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an "
+			      "external source", (u8 *) buf, res);
+	if (res == 0)
+		return;
+
+	if (data->get_complete_cb == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb "
+			   "registered");
+		return;
+	}
+
+	/* <cmd> <IMSI> ... */
+
+	cmd = buf;
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		goto parse_fail;
+	*pos = '\0';
+	imsi = pos + 1;
+	pos = os_strchr(imsi, ' ');
+	if (pos == NULL)
+		goto parse_fail;
+	*pos = '\0';
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s",
+		   cmd, imsi);
+
+	if (os_strcmp(cmd, "SIM-RESP-AUTH") == 0)
+		eap_sim_db_sim_resp_auth(data, imsi, pos + 1);
+	else if (os_strcmp(cmd, "AKA-RESP-AUTH") == 0)
+		eap_sim_db_aka_resp_auth(data, imsi, pos + 1);
+	else
+		wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response "
+			   "'%s'", cmd);
+	return;
+
+parse_fail:
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
+}
+
+
+static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
+{
+	struct sockaddr_un addr;
+	static int counter = 0;
+
+	if (os_strncmp(data->fname, "unix:", 5) != 0)
+		return -1;
+
+	data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (data->sock < 0) {
+		wpa_printf(MSG_INFO, "socket(eap_sim_db): %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	os_snprintf(addr.sun_path, sizeof(addr.sun_path),
+		    "/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) {
+		wpa_printf(MSG_INFO, "bind(eap_sim_db): %s", strerror(errno));
+		close(data->sock);
+		data->sock = -1;
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	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) {
+		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;
+	}
+
+	eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL);
+
+	return 0;
+}
+
+
+static void eap_sim_db_close_socket(struct eap_sim_db_data *data)
+{
+	if (data->sock >= 0) {
+		eloop_unregister_read_sock(data->sock);
+		close(data->sock);
+		data->sock = -1;
+	}
+	if (data->local_sock) {
+		unlink(data->local_sock);
+		os_free(data->local_sock);
+		data->local_sock = NULL;
+	}
+}
+
+
+/**
+ * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
+ * @config: Configuration data (e.g., file name)
+ * @get_complete_cb: Callback function for reporting availability of triplets
+ * @ctx: Context pointer for get_complete_cb
+ * Returns: Pointer to a private data structure or %NULL on failure
+ */
+struct eap_sim_db_data *
+eap_sim_db_init(const char *config,
+		void (*get_complete_cb)(void *ctx, void *session_ctx),
+		void *ctx)
+{
+	struct eap_sim_db_data *data;
+	char *pos;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	data->sock = -1;
+	data->get_complete_cb = get_complete_cb;
+	data->ctx = ctx;
+	data->fname = os_strdup(config);
+	if (data->fname == NULL)
+		goto fail;
+	pos = os_strstr(data->fname, " db=");
+	if (pos) {
+		*pos = '\0';
+#ifdef CONFIG_SQLITE
+		pos += 4;
+		data->sqlite_db = db_open(pos);
+		if (data->sqlite_db == NULL)
+			goto fail;
+#endif /* CONFIG_SQLITE */
+	}
+
+	if (os_strncmp(data->fname, "unix:", 5) == 0) {
+		if (eap_sim_db_open_socket(data)) {
+			wpa_printf(MSG_DEBUG, "EAP-SIM DB: External database "
+				   "connection not available - will retry "
+				   "later");
+		}
+	}
+
+	return data;
+
+fail:
+	eap_sim_db_close_socket(data);
+	os_free(data->fname);
+	os_free(data);
+	return NULL;
+}
+
+
+static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
+{
+	os_free(p->permanent);
+	os_free(p->pseudonym);
+	os_free(p);
+}
+
+
+static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
+{
+	os_free(r->permanent);
+	os_free(r->reauth_id);
+	os_free(r);
+}
+
+
+/**
+ * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface
+ * @priv: Private data pointer from eap_sim_db_init()
+ */
+void eap_sim_db_deinit(void *priv)
+{
+	struct eap_sim_db_data *data = priv;
+	struct eap_sim_pseudonym *p, *prev;
+	struct eap_sim_reauth *r, *prevr;
+	struct eap_sim_db_pending *pending, *prev_pending;
+
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db) {
+		sqlite3_close(data->sqlite_db);
+		data->sqlite_db = NULL;
+	}
+#endif /* CONFIG_SQLITE */
+
+	eap_sim_db_close_socket(data);
+	os_free(data->fname);
+
+	p = data->pseudonyms;
+	while (p) {
+		prev = p;
+		p = p->next;
+		eap_sim_db_free_pseudonym(prev);
+	}
+
+	r = data->reauths;
+	while (r) {
+		prevr = r;
+		r = r->next;
+		eap_sim_db_free_reauth(prevr);
+	}
+
+	pending = data->pending;
+	while (pending) {
+		prev_pending = pending;
+		pending = pending->next;
+		os_free(prev_pending);
+	}
+
+	os_free(data);
+}
+
+
+static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
+			   size_t len)
+{
+	int _errno = 0;
+
+	if (send(data->sock, msg, len, 0) < 0) {
+		_errno = errno;
+		wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s",
+			   strerror(errno));
+	}
+
+	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
+	    _errno == ECONNREFUSED) {
+		/* Try to reconnect */
+		eap_sim_db_close_socket(data);
+		if (eap_sim_db_open_socket(data) < 0)
+			return -1;
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
+			   "external server");
+		if (send(data->sock, msg, len, 0) < 0) {
+			wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s",
+				   strerror(errno));
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
+{
+	/* TODO: add limit for maximum length for pending list; remove latest
+	 * (i.e., last) entry from the list if the limit is reached; could also
+	 * use timeout to expire pending entries */
+}
+
+
+/**
+ * eap_sim_db_get_gsm_triplets - Get GSM triplets
+ * @data: Private data pointer from eap_sim_db_init()
+ * @username: Permanent username (prefix | IMSI)
+ * @max_chal: Maximum number of triplets
+ * @_rand: Buffer for RAND values
+ * @kc: Buffer for Kc values
+ * @sres: Buffer for SRES values
+ * @cb_session_ctx: Session callback context for get_complete_cb()
+ * Returns: Number of triplets received (has to be less than or equal to
+ * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or
+ * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the
+ * callback function registered with eap_sim_db_init() will be called once the
+ * results become available.
+ *
+ * When using an external server for GSM triplets, this function can always
+ * start a request and return EAP_SIM_DB_PENDING immediately if authentication
+ * triplets are not available. Once the triplets are received, callback
+ * function registered with eap_sim_db_init() is called to notify EAP state
+ * machine to reprocess the message. This eap_sim_db_get_gsm_triplets()
+ * function will then be called again and the newly received triplets will then
+ * be given to the caller.
+ */
+int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data,
+				const char *username, int max_chal,
+				u8 *_rand, u8 *kc, u8 *sres,
+				void *cb_session_ctx)
+{
+	struct eap_sim_db_pending *entry;
+	int len, ret;
+	char msg[40];
+	const char *imsi;
+	size_t imsi_len;
+
+	if (username == NULL || username[0] != EAP_SIM_PERMANENT_PREFIX ||
+	    username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
+			   username);
+		return EAP_SIM_DB_FAILURE;
+	}
+	imsi = username + 1;
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI '%s'",
+		   imsi);
+
+	entry = eap_sim_db_get_pending(data, imsi, 0);
+	if (entry) {
+		int num_chal;
+		if (entry->state == FAILURE) {
+			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
+				   "failure");
+			os_free(entry);
+			return EAP_SIM_DB_FAILURE;
+		}
+
+		if (entry->state == PENDING) {
+			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
+				   "still pending");
+			eap_sim_db_add_pending(data, entry);
+			return EAP_SIM_DB_PENDING;
+		}
+
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
+			   "%d challenges", entry->u.sim.num_chal);
+		num_chal = entry->u.sim.num_chal;
+		if (num_chal > max_chal)
+			num_chal = max_chal;
+		os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN);
+		os_memcpy(sres, entry->u.sim.sres,
+			  num_chal * EAP_SIM_SRES_LEN);
+		os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN);
+		os_free(entry);
+		return num_chal;
+	}
+
+	if (data->sock < 0) {
+		if (eap_sim_db_open_socket(data) < 0)
+			return EAP_SIM_DB_FAILURE;
+	}
+
+	imsi_len = os_strlen(imsi);
+	len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
+	if (os_snprintf_error(sizeof(msg), len) ||
+	    len + imsi_len >= sizeof(msg))
+		return EAP_SIM_DB_FAILURE;
+	os_memcpy(msg + len, imsi, imsi_len);
+	len += imsi_len;
+	ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
+	if (os_snprintf_error(sizeof(msg) - len, ret))
+		return EAP_SIM_DB_FAILURE;
+	len += ret;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
+		   "data for IMSI '%s'", imsi);
+	if (eap_sim_db_send(data, msg, len) < 0)
+		return EAP_SIM_DB_FAILURE;
+
+	entry = os_zalloc(sizeof(*entry));
+	if (entry == NULL)
+		return EAP_SIM_DB_FAILURE;
+
+	os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
+	entry->cb_session_ctx = cb_session_ctx;
+	entry->state = PENDING;
+	eap_sim_db_add_pending(data, entry);
+	eap_sim_db_expire_pending(data);
+
+	return EAP_SIM_DB_PENDING;
+}
+
+
+static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
+{
+	char *id, *pos, *end;
+	u8 buf[10];
+
+	if (random_get_bytes(buf, sizeof(buf)))
+		return NULL;
+	id = os_malloc(sizeof(buf) * 2 + 2);
+	if (id == NULL)
+		return NULL;
+
+	pos = id;
+	end = id + sizeof(buf) * 2 + 2;
+	*pos++ = prefix;
+	wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
+	
+	return id;
+}
+
+
+/**
+ * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
+ * @data: Private data pointer from eap_sim_db_init()
+ * @method: EAP method (SIM/AKA/AKA')
+ * Returns: Next pseudonym (allocated string) or %NULL on failure
+ *
+ * This function is used to generate a pseudonym for EAP-SIM. The returned
+ * pseudonym is not added to database at this point; it will need to be added
+ * with eap_sim_db_add_pseudonym() once the authentication has been completed
+ * successfully. Caller is responsible for freeing the returned buffer.
+ */
+char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data,
+				     enum eap_sim_db_method method)
+{
+	char prefix = EAP_SIM_REAUTH_ID_PREFIX;
+
+	switch (method) {
+	case EAP_SIM_DB_SIM:
+		prefix = EAP_SIM_PSEUDONYM_PREFIX;
+		break;
+	case EAP_SIM_DB_AKA:
+		prefix = EAP_AKA_PSEUDONYM_PREFIX;
+		break;
+	case EAP_SIM_DB_AKA_PRIME:
+		prefix = EAP_AKA_PRIME_PSEUDONYM_PREFIX;
+		break;
+	}
+
+	return eap_sim_db_get_next(data, prefix);
+}
+
+
+/**
+ * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
+ * @data: Private data pointer from eap_sim_db_init()
+ * @method: EAP method (SIM/AKA/AKA')
+ * Returns: Next reauth_id (allocated string) or %NULL on failure
+ *
+ * This function is used to generate a fast re-authentication identity for
+ * EAP-SIM. The returned reauth_id is not added to database at this point; it
+ * will need to be added with eap_sim_db_add_reauth() once the authentication
+ * has been completed successfully. Caller is responsible for freeing the
+ * returned buffer.
+ */
+char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data,
+				     enum eap_sim_db_method method)
+{
+	char prefix = EAP_SIM_REAUTH_ID_PREFIX;
+
+	switch (method) {
+	case EAP_SIM_DB_SIM:
+		prefix = EAP_SIM_REAUTH_ID_PREFIX;
+		break;
+	case EAP_SIM_DB_AKA:
+		prefix = EAP_AKA_REAUTH_ID_PREFIX;
+		break;
+	case EAP_SIM_DB_AKA_PRIME:
+		prefix = EAP_AKA_PRIME_REAUTH_ID_PREFIX;
+		break;
+	}
+
+	return eap_sim_db_get_next(data, prefix);
+}
+
+
+/**
+ * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
+ * @data: Private data pointer from eap_sim_db_init()
+ * @permanent: Permanent username
+ * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
+ * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
+ * free it.
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
+ * responsible of freeing pseudonym buffer once it is not needed anymore.
+ */
+int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data,
+			     const char *permanent, char *pseudonym)
+{
+	struct eap_sim_pseudonym *p;
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add pseudonym '%s' for permanent "
+		   "username '%s'", pseudonym, permanent);
+
+	/* TODO: could store last two pseudonyms */
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_add_pseudonym(data, permanent, pseudonym);
+#endif /* CONFIG_SQLITE */
+	for (p = data->pseudonyms; p; p = p->next) {
+		if (os_strcmp(permanent, p->permanent) == 0)
+			break;
+	}
+	if (p) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
+			   "pseudonym: %s", p->pseudonym);
+		os_free(p->pseudonym);
+		p->pseudonym = pseudonym;
+		return 0;
+	}
+
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL) {
+		os_free(pseudonym);
+		return -1;
+	}
+
+	p->next = data->pseudonyms;
+	p->permanent = os_strdup(permanent);
+	if (p->permanent == NULL) {
+		os_free(p);
+		os_free(pseudonym);
+		return -1;
+	}
+	p->pseudonym = pseudonym;
+	data->pseudonyms = p;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry");
+	return 0;
+}
+
+
+static struct eap_sim_reauth *
+eap_sim_db_add_reauth_data(struct eap_sim_db_data *data,
+			   const char *permanent,
+			   char *reauth_id, u16 counter)
+{
+	struct eap_sim_reauth *r;
+
+	for (r = data->reauths; r; r = r->next) {
+		if (os_strcmp(r->permanent, permanent) == 0)
+			break;
+	}
+
+	if (r) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
+			   "reauth_id: %s", r->reauth_id);
+		os_free(r->reauth_id);
+		r->reauth_id = reauth_id;
+	} else {
+		r = os_zalloc(sizeof(*r));
+		if (r == NULL) {
+			os_free(reauth_id);
+			return NULL;
+		}
+
+		r->next = data->reauths;
+		r->permanent = os_strdup(permanent);
+		if (r->permanent == NULL) {
+			os_free(r);
+			os_free(reauth_id);
+			return NULL;
+		}
+		r->reauth_id = reauth_id;
+		data->reauths = r;
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
+	}
+
+	r->counter = counter;
+
+	return r;
+}
+
+
+/**
+ * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @permanent: Permanent username
+ * @identity_len: Length of identity
+ * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
+ * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
+ * free it.
+ * @counter: AT_COUNTER value for fast re-authentication
+ * @mk: 16-byte MK from the previous full authentication or %NULL
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds a new re-authentication entry for an EAP-SIM user.
+ * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
+ * anymore.
+ */
+int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
+			  char *reauth_id, u16 counter, const u8 *mk)
+{
+	struct eap_sim_reauth *r;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent "
+		   "identity '%s'", reauth_id, permanent);
+
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_add_reauth(data, permanent, reauth_id, counter, mk,
+				     NULL, NULL, NULL);
+#endif /* CONFIG_SQLITE */
+	r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter);
+	if (r == NULL)
+		return -1;
+
+	os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
+
+	return 0;
+}
+
+
+#ifdef EAP_SERVER_AKA_PRIME
+/**
+ * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
+ * @data: Private data pointer from eap_sim_db_init()
+ * @permanent: Permanent username
+ * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
+ * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
+ * free it.
+ * @counter: AT_COUNTER value for fast re-authentication
+ * @k_encr: K_encr from the previous full authentication
+ * @k_aut: K_aut from the previous full authentication
+ * @k_re: 32-byte K_re from the previous full authentication
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds a new re-authentication entry for an EAP-AKA' user.
+ * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
+ * anymore.
+ */
+int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data,
+				const char *permanent, char *reauth_id,
+				u16 counter, const u8 *k_encr,
+				const u8 *k_aut, const u8 *k_re)
+{
+	struct eap_sim_reauth *r;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent "
+		   "identity '%s'", reauth_id, permanent);
+
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_add_reauth(data, permanent, reauth_id, counter, NULL,
+				     k_encr, k_aut, k_re);
+#endif /* CONFIG_SQLITE */
+	r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter);
+	if (r == NULL)
+		return -1;
+
+	os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
+	os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
+	os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
+
+	return 0;
+}
+#endif /* EAP_SERVER_AKA_PRIME */
+
+
+/**
+ * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
+ * @data: Private data pointer from eap_sim_db_init()
+ * @pseudonym: Pseudonym username
+ * Returns: Pointer to permanent username or %NULL if not found
+ */
+const char *
+eap_sim_db_get_permanent(struct eap_sim_db_data *data, const char *pseudonym)
+{
+	struct eap_sim_pseudonym *p;
+
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_get_pseudonym(data, pseudonym);
+#endif /* CONFIG_SQLITE */
+
+	p = data->pseudonyms;
+	while (p) {
+		if (os_strcmp(p->pseudonym, pseudonym) == 0)
+			return p->permanent;
+		p = p->next;
+	}
+
+	return NULL;
+}
+
+
+/**
+ * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
+ * @data: Private data pointer from eap_sim_db_init()
+ * @reauth_id: Fast re-authentication username
+ * Returns: Pointer to the re-auth entry, or %NULL if not found
+ */
+struct eap_sim_reauth *
+eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data,
+			    const char *reauth_id)
+{
+	struct eap_sim_reauth *r;
+
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_get_reauth(data, reauth_id);
+#endif /* CONFIG_SQLITE */
+
+	r = data->reauths;
+	while (r) {
+		if (os_strcmp(r->reauth_id, reauth_id) == 0)
+			break;
+		r = r->next;
+	}
+
+	return r;
+}
+
+
+/**
+ * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
+ * @data: Private data pointer from eap_sim_db_init()
+ * @reauth: Pointer to re-authentication entry from
+ * eap_sim_db_get_reauth_entry()
+ */
+void eap_sim_db_remove_reauth(struct eap_sim_db_data *data,
+			      struct eap_sim_reauth *reauth)
+{
+	struct eap_sim_reauth *r, *prev = NULL;
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db) {
+		db_remove_reauth(data, reauth);
+		return;
+	}
+#endif /* CONFIG_SQLITE */
+	r = data->reauths;
+	while (r) {
+		if (r == reauth) {
+			if (prev)
+				prev->next = r->next;
+			else
+				data->reauths = r->next;
+			eap_sim_db_free_reauth(r);
+			return;
+		}
+		prev = r;
+		r = r->next;
+	}
+}
+
+
+/**
+ * eap_sim_db_get_aka_auth - Get AKA authentication values
+ * @data: Private data pointer from eap_sim_db_init()
+ * @username: Permanent username (prefix | IMSI)
+ * @_rand: Buffer for RAND value
+ * @autn: Buffer for AUTN value
+ * @ik: Buffer for IK value
+ * @ck: Buffer for CK value
+ * @res: Buffer for RES value
+ * @res_len: Buffer for RES length
+ * @cb_session_ctx: Session callback context for get_complete_cb()
+ * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
+ * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
+ * case, the callback function registered with eap_sim_db_init() will be
+ * called once the results become available.
+ *
+ * When using an external server for AKA authentication, this function can
+ * always start a request and return EAP_SIM_DB_PENDING immediately if
+ * authentication triplets are not available. Once the authentication data are
+ * received, callback function registered with eap_sim_db_init() is called to
+ * notify EAP state machine to reprocess the message. This
+ * eap_sim_db_get_aka_auth() function will then be called again and the newly
+ * received triplets will then be given to the caller.
+ */
+int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username,
+			    u8 *_rand, u8 *autn, u8 *ik, u8 *ck,
+			    u8 *res, size_t *res_len, void *cb_session_ctx)
+{
+	struct eap_sim_db_pending *entry;
+	int len;
+	char msg[40];
+	const char *imsi;
+	size_t imsi_len;
+
+	if (username == NULL ||
+	    (username[0] != EAP_AKA_PERMANENT_PREFIX &&
+	     username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) ||
+	    username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
+			   username);
+		return EAP_SIM_DB_FAILURE;
+	}
+	imsi = username + 1;
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'",
+		   imsi);
+
+	entry = eap_sim_db_get_pending(data, imsi, 1);
+	if (entry) {
+		if (entry->state == FAILURE) {
+			os_free(entry);
+			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
+			return EAP_SIM_DB_FAILURE;
+		}
+
+		if (entry->state == PENDING) {
+			eap_sim_db_add_pending(data, entry);
+			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending");
+			return EAP_SIM_DB_PENDING;
+		}
+
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully "
+			   "received authentication data");
+		os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN);
+		os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN);
+		os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN);
+		os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
+		os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
+		*res_len = entry->u.aka.res_len;
+		os_free(entry);
+		return 0;
+	}
+
+	if (data->sock < 0) {
+		if (eap_sim_db_open_socket(data) < 0)
+			return EAP_SIM_DB_FAILURE;
+	}
+
+	imsi_len = os_strlen(imsi);
+	len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
+	if (os_snprintf_error(sizeof(msg), len) ||
+	    len + imsi_len >= sizeof(msg))
+		return EAP_SIM_DB_FAILURE;
+	os_memcpy(msg + len, imsi, imsi_len);
+	len += imsi_len;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
+		    "data for IMSI '%s'", imsi);
+	if (eap_sim_db_send(data, msg, len) < 0)
+		return EAP_SIM_DB_FAILURE;
+
+	entry = os_zalloc(sizeof(*entry));
+	if (entry == NULL)
+		return EAP_SIM_DB_FAILURE;
+
+	entry->aka = 1;
+	os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
+	entry->cb_session_ctx = cb_session_ctx;
+	entry->state = PENDING;
+	eap_sim_db_add_pending(data, entry);
+	eap_sim_db_expire_pending(data);
+
+	return EAP_SIM_DB_PENDING;
+}
+
+
+/**
+ * eap_sim_db_resynchronize - Resynchronize AKA AUTN
+ * @data: Private data pointer from eap_sim_db_init()
+ * @username: Permanent username
+ * @auts: AUTS value from the peer
+ * @_rand: RAND value used in the rejected message
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when the peer reports synchronization failure in the
+ * AUTN value by sending AUTS. The AUTS and RAND values should be sent to
+ * HLR/AuC to allow it to resynchronize with the peer. After this,
+ * eap_sim_db_get_aka_auth() will be called again to to fetch updated
+ * RAND/AUTN values for the next challenge.
+ */
+int eap_sim_db_resynchronize(struct eap_sim_db_data *data,
+			     const char *username,
+			     const u8 *auts, const u8 *_rand)
+{
+	const char *imsi;
+	size_t imsi_len;
+
+	if (username == NULL ||
+	    (username[0] != EAP_AKA_PERMANENT_PREFIX &&
+	     username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) ||
+	    username[1] == '\0' || os_strlen(username) > 20) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
+			   username);
+		return -1;
+	}
+	imsi = username + 1;
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'",
+		   imsi);
+
+	if (data->sock >= 0) {
+		char msg[100];
+		int len, ret;
+
+		imsi_len = os_strlen(imsi);
+		len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
+		if (os_snprintf_error(sizeof(msg), len) ||
+		    len + imsi_len >= sizeof(msg))
+			return -1;
+		os_memcpy(msg + len, imsi, imsi_len);
+		len += imsi_len;
+
+		ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
+		if (os_snprintf_error(sizeof(msg) - len, ret))
+			return -1;
+		len += ret;
+		len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
+					auts, EAP_AKA_AUTS_LEN);
+		ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
+		if (os_snprintf_error(sizeof(msg) - len, ret))
+			return -1;
+		len += ret;
+		len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
+					_rand, EAP_AKA_RAND_LEN);
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
+			   "IMSI '%s'", imsi);
+		if (eap_sim_db_send(data, msg, len) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * sim_get_username - Extract username from SIM identity
+ * @identity: Identity
+ * @identity_len: Identity length
+ * Returns: Allocated buffer with the username part of the identity
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+char * sim_get_username(const u8 *identity, size_t identity_len)
+{
+	size_t pos;
+
+	if (identity == NULL)
+		return NULL;
+
+	for (pos = 0; pos < identity_len; pos++) {
+		if (identity[pos] == '@' || identity[pos] == '\0')
+			break;
+	}
+
+	return dup_binstr(identity, pos);
+}
diff --git a/hostap/src/eap_server/eap_sim_db.h b/hostap/src/eap_server/eap_sim_db.h
new file mode 100644
index 0000000..53a1a7c
--- /dev/null
+++ b/hostap/src/eap_server/eap_sim_db.h
@@ -0,0 +1,95 @@
+/*
+ * hostapd / EAP-SIM database/authenticator gateway
+ * Copyright (c) 2005-2008, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_SIM_DB_H
+#define EAP_SIM_DB_H
+
+#include "eap_common/eap_sim_common.h"
+
+/* Identity prefixes */
+#define EAP_SIM_PERMANENT_PREFIX '1'
+#define EAP_SIM_PSEUDONYM_PREFIX '3'
+#define EAP_SIM_REAUTH_ID_PREFIX '5'
+#define EAP_AKA_PERMANENT_PREFIX '0'
+#define EAP_AKA_PSEUDONYM_PREFIX '2'
+#define EAP_AKA_REAUTH_ID_PREFIX '4'
+#define EAP_AKA_PRIME_PERMANENT_PREFIX '6'
+#define EAP_AKA_PRIME_PSEUDONYM_PREFIX '7'
+#define EAP_AKA_PRIME_REAUTH_ID_PREFIX '8'
+
+enum eap_sim_db_method {
+	EAP_SIM_DB_SIM,
+	EAP_SIM_DB_AKA,
+	EAP_SIM_DB_AKA_PRIME
+};
+
+struct eap_sim_db_data;
+
+struct eap_sim_db_data *
+eap_sim_db_init(const char *config,
+		void (*get_complete_cb)(void *ctx, void *session_ctx),
+		void *ctx);
+
+void eap_sim_db_deinit(void *priv);
+
+int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data,
+				const char *username, int max_chal,
+				u8 *_rand, u8 *kc, u8 *sres,
+				void *cb_session_ctx);
+
+#define EAP_SIM_DB_FAILURE -1
+#define EAP_SIM_DB_PENDING -2
+
+char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data,
+				     enum eap_sim_db_method method);
+
+char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data,
+				     enum eap_sim_db_method method);
+
+int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data,
+			     const char *permanent, char *pseudonym);
+
+int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
+			  char *reauth_id, u16 counter, const u8 *mk);
+int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data,
+				const char *permanent,
+				char *reauth_id, u16 counter, const u8 *k_encr,
+				const u8 *k_aut, const u8 *k_re);
+
+const char * eap_sim_db_get_permanent(struct eap_sim_db_data *data,
+				      const char *pseudonym);
+
+struct eap_sim_reauth {
+	struct eap_sim_reauth *next;
+	char *permanent; /* Permanent username */
+	char *reauth_id; /* Fast re-authentication username */
+	u16 counter;
+	u8 mk[EAP_SIM_MK_LEN];
+	u8 k_encr[EAP_SIM_K_ENCR_LEN];
+	u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
+	u8 k_re[EAP_AKA_PRIME_K_RE_LEN];
+};
+
+struct eap_sim_reauth *
+eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data,
+			    const char *reauth_id);
+
+void eap_sim_db_remove_reauth(struct eap_sim_db_data *data,
+			      struct eap_sim_reauth *reauth);
+
+int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username,
+			    u8 *_rand, u8 *autn, u8 *ik, u8 *ck,
+			    u8 *res, size_t *res_len, void *cb_session_ctx);
+
+int eap_sim_db_resynchronize(struct eap_sim_db_data *data,
+			     const char *username, const u8 *auts,
+			     const u8 *_rand);
+
+char * sim_get_username(const u8 *identity, size_t identity_len);
+
+#endif /* EAP_SIM_DB_H */
diff --git a/hostap/src/eap_server/eap_tls_common.h b/hostap/src/eap_server/eap_tls_common.h
new file mode 100644
index 0000000..dc943eb
--- /dev/null
+++ b/hostap/src/eap_server/eap_tls_common.h
@@ -0,0 +1,94 @@
+/*
+ * EAP-TLS/PEAP/TTLS/FAST server common functions
+ * 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.
+ */
+
+#ifndef EAP_TLS_COMMON_H
+#define EAP_TLS_COMMON_H
+
+/**
+ * struct eap_ssl_data - TLS data for EAP methods
+ */
+struct eap_ssl_data {
+	/**
+	 * conn - TLS connection context data from tls_connection_init()
+	 */
+	struct tls_connection *conn;
+
+	/**
+	 * tls_out - TLS message to be sent out in fragments
+	 */
+	struct wpabuf *tls_out;
+
+	/**
+	 * tls_out_pos - The current position in the outgoing TLS message
+	 */
+	size_t tls_out_pos;
+
+	/**
+	 * tls_out_limit - Maximum fragment size for outgoing TLS messages
+	 */
+	size_t tls_out_limit;
+
+	/**
+	 * tls_in - Received TLS message buffer for re-assembly
+	 */
+	struct wpabuf *tls_in;
+
+	/**
+	 * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
+	 */
+	int phase2;
+
+	/**
+	 * eap - EAP state machine allocated with eap_server_sm_init()
+	 */
+	struct eap_sm *eap;
+
+	enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state;
+	struct wpabuf tmpbuf;
+};
+
+
+/* EAP TLS Flags */
+#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TLS_FLAGS_START 0x20
+#define EAP_TLS_VERSION_MASK 0x07
+
+ /* could be up to 128 bytes, but only the first 64 bytes are used */
+#define EAP_TLS_KEY_LEN 64
+
+/* 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,
+				  u8 code, u8 identifier);
+int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+			    int verify_peer, int eap_type);
+void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
+u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+			       char *label, size_t len);
+u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
+				      struct eap_ssl_data *data, u8 eap_type,
+				      size_t *len);
+struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data,
+					 int eap_type, int version, u8 id);
+struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version);
+int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data);
+struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm,
+				       struct eap_ssl_data *data,
+				       const struct wpabuf *plain);
+int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data,
+			   struct wpabuf *respData, void *priv, int eap_type,
+			   int (*proc_version)(struct eap_sm *sm, void *priv,
+					       int peer_version),
+			   void (*proc_msg)(struct eap_sm *sm, void *priv,
+					    const struct wpabuf *respData));
+
+#endif /* EAP_TLS_COMMON_H */
diff --git a/hostap/src/eap_server/ikev2.c b/hostap/src/eap_server/ikev2.c
new file mode 100644
index 0000000..632598f
--- /dev/null
+++ b/hostap/src/eap_server/ikev2.c
@@ -0,0 +1,1200 @@
+/*
+ * IKEv2 initiator (RFC 4306) for EAP-IKEV2
+ * Copyright (c) 2007, 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/dh_groups.h"
+#include "crypto/random.h"
+#include "ikev2.h"
+
+
+static int ikev2_process_idr(struct ikev2_initiator_data *data,
+			     const u8 *idr, size_t idr_len);
+
+
+void ikev2_initiator_deinit(struct ikev2_initiator_data *data)
+{
+	ikev2_free_keys(&data->keys);
+	wpabuf_free(data->r_dh_public);
+	wpabuf_free(data->i_dh_private);
+	os_free(data->IDi);
+	os_free(data->IDr);
+	os_free(data->shared_secret);
+	wpabuf_free(data->i_sign_msg);
+	wpabuf_free(data->r_sign_msg);
+	os_free(data->key_pad);
+}
+
+
+static int ikev2_derive_keys(struct ikev2_initiator_data *data)
+{
+	u8 *buf, *pos, *pad, skeyseed[IKEV2_MAX_HASH_LEN];
+	size_t buf_len, pad_len;
+	struct wpabuf *shared;
+	const struct ikev2_integ_alg *integ;
+	const struct ikev2_prf_alg *prf;
+	const struct ikev2_encr_alg *encr;
+	int ret;
+	const u8 *addr[2];
+	size_t len[2];
+
+	/* RFC 4306, Sect. 2.14 */
+
+	integ = ikev2_get_integ(data->proposal.integ);
+	prf = ikev2_get_prf(data->proposal.prf);
+	encr = ikev2_get_encr(data->proposal.encr);
+	if (integ == NULL || prf == NULL || encr == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported proposal");
+		return -1;
+	}
+
+	shared = dh_derive_shared(data->r_dh_public, data->i_dh_private,
+				  data->dh);
+	if (shared == NULL)
+		return -1;
+
+	/* Construct Ni | Nr | SPIi | SPIr */
+
+	buf_len = data->i_nonce_len + data->r_nonce_len + 2 * IKEV2_SPI_LEN;
+	buf = os_malloc(buf_len);
+	if (buf == NULL) {
+		wpabuf_free(shared);
+		return -1;
+	}
+
+	pos = buf;
+	os_memcpy(pos, data->i_nonce, data->i_nonce_len);
+	pos += data->i_nonce_len;
+	os_memcpy(pos, data->r_nonce, data->r_nonce_len);
+	pos += data->r_nonce_len;
+	os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN);
+	pos += IKEV2_SPI_LEN;
+	os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN);
+
+	/* SKEYSEED = prf(Ni | Nr, g^ir) */
+
+	/* Use zero-padding per RFC 4306, Sect. 2.14 */
+	pad_len = data->dh->prime_len - wpabuf_len(shared);
+	pad = os_zalloc(pad_len ? pad_len : 1);
+	if (pad == NULL) {
+		wpabuf_free(shared);
+		os_free(buf);
+		return -1;
+	}
+	addr[0] = pad;
+	len[0] = pad_len;
+	addr[1] = wpabuf_head(shared);
+	len[1] = wpabuf_len(shared);
+	if (ikev2_prf_hash(prf->id, buf, data->i_nonce_len + data->r_nonce_len,
+			   2, addr, len, skeyseed) < 0) {
+		wpabuf_free(shared);
+		os_free(buf);
+		os_free(pad);
+		return -1;
+	}
+	os_free(pad);
+	wpabuf_free(shared);
+
+	/* DH parameters are not needed anymore, so free them */
+	wpabuf_free(data->r_dh_public);
+	data->r_dh_public = NULL;
+	wpabuf_free(data->i_dh_private);
+	data->i_dh_private = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "IKEV2: SKEYSEED",
+			skeyseed, prf->hash_len);
+
+	ret = ikev2_derive_sk_keys(prf, integ, encr, skeyseed, buf, buf_len,
+				   &data->keys);
+	os_free(buf);
+	return ret;
+}
+
+
+static int ikev2_parse_transform(struct ikev2_initiator_data *data,
+				 struct ikev2_proposal_data *prop,
+				 const u8 *pos, const u8 *end)
+{
+	int transform_len;
+	const struct ikev2_transform *t;
+	u16 transform_id;
+	const u8 *tend;
+
+	if (end - pos < (int) sizeof(*t)) {
+		wpa_printf(MSG_INFO, "IKEV2: Too short transform");
+		return -1;
+	}
+
+	t = (const struct ikev2_transform *) pos;
+	transform_len = WPA_GET_BE16(t->transform_length);
+	if (transform_len < (int) sizeof(*t) || pos + transform_len > end) {
+		wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d",
+			   transform_len);
+		return -1;
+	}
+	tend = pos + transform_len;
+
+	transform_id = WPA_GET_BE16(t->transform_id);
+
+	wpa_printf(MSG_DEBUG, "IKEV2:   Transform:");
+	wpa_printf(MSG_DEBUG, "IKEV2:     Type: %d  Transform Length: %d  "
+		   "Transform Type: %d  Transform ID: %d",
+		   t->type, transform_len, t->transform_type, transform_id);
+
+	if (t->type != 0 && t->type != 3) {
+		wpa_printf(MSG_INFO, "IKEV2: Unexpected Transform type");
+		return -1;
+	}
+
+	pos = (const u8 *) (t + 1);
+	if (pos < tend) {
+		wpa_hexdump(MSG_DEBUG, "IKEV2:     Transform Attributes",
+			    pos, tend - pos);
+	}
+
+	switch (t->transform_type) {
+	case IKEV2_TRANSFORM_ENCR:
+		if (ikev2_get_encr(transform_id) &&
+		    transform_id == data->proposal.encr) {
+			if (transform_id == ENCR_AES_CBC) {
+				if (tend - pos != 4) {
+					wpa_printf(MSG_DEBUG, "IKEV2: No "
+						   "Transform Attr for AES");
+					break;
+				}
+				if (WPA_GET_BE16(pos) != 0x800e) {
+					wpa_printf(MSG_DEBUG, "IKEV2: Not a "
+						   "Key Size attribute for "
+						   "AES");
+					break;
+				}
+				if (WPA_GET_BE16(pos + 2) != 128) {
+					wpa_printf(MSG_DEBUG, "IKEV2: "
+						   "Unsupported AES key size "
+						   "%d bits",
+						   WPA_GET_BE16(pos + 2));
+					break;
+				}
+			}
+			prop->encr = transform_id;
+		}
+		break;
+	case IKEV2_TRANSFORM_PRF:
+		if (ikev2_get_prf(transform_id) &&
+		    transform_id == data->proposal.prf)
+			prop->prf = transform_id;
+		break;
+	case IKEV2_TRANSFORM_INTEG:
+		if (ikev2_get_integ(transform_id) &&
+		    transform_id == data->proposal.integ)
+			prop->integ = transform_id;
+		break;
+	case IKEV2_TRANSFORM_DH:
+		if (dh_groups_get(transform_id) &&
+		    transform_id == data->proposal.dh)
+			prop->dh = transform_id;
+		break;
+	}
+
+	return transform_len;
+}
+
+
+static int ikev2_parse_proposal(struct ikev2_initiator_data *data,
+				struct ikev2_proposal_data *prop,
+				const u8 *pos, const u8 *end)
+{
+	const u8 *pend, *ppos;
+	int proposal_len, i;
+	const struct ikev2_proposal *p;
+
+	if (end - pos < (int) sizeof(*p)) {
+		wpa_printf(MSG_INFO, "IKEV2: Too short proposal");
+		return -1;
+	}
+
+	p = (const struct ikev2_proposal *) pos;
+	proposal_len = WPA_GET_BE16(p->proposal_length);
+	if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) {
+		wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d",
+			   proposal_len);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "IKEV2: SAi1 Proposal # %d",
+		   p->proposal_num);
+	wpa_printf(MSG_DEBUG, "IKEV2:   Type: %d  Proposal Length: %d "
+		   " Protocol ID: %d",
+		   p->type, proposal_len, p->protocol_id);
+	wpa_printf(MSG_DEBUG, "IKEV2:   SPI Size: %d  Transforms: %d",
+		   p->spi_size, p->num_transforms);
+
+	if (p->type != 0 && p->type != 2) {
+		wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal type");
+		return -1;
+	}
+
+	if (p->protocol_id != IKEV2_PROTOCOL_IKE) {
+		wpa_printf(MSG_DEBUG, "IKEV2: Unexpected Protocol ID "
+			   "(only IKE allowed for EAP-IKEv2)");
+		return -1;
+	}
+
+	if (p->proposal_num != prop->proposal_num) {
+		if (p->proposal_num == prop->proposal_num + 1)
+			prop->proposal_num = p->proposal_num;
+		else {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal #");
+			return -1;
+		}
+	}
+
+	ppos = (const u8 *) (p + 1);
+	pend = pos + proposal_len;
+	if (ppos + p->spi_size > pend) {
+		wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI "
+			   "in proposal");
+		return -1;
+	}
+	if (p->spi_size) {
+		wpa_hexdump(MSG_DEBUG, "IKEV2:    SPI",
+			    ppos, p->spi_size);
+		ppos += p->spi_size;
+	}
+
+	/*
+	 * For initial IKE_SA negotiation, SPI Size MUST be zero; for
+	 * subsequent negotiations, it must be 8 for IKE. We only support
+	 * initial case for now.
+	 */
+	if (p->spi_size != 0) {
+		wpa_printf(MSG_INFO, "IKEV2: Unexpected SPI Size");
+		return -1;
+	}
+
+	if (p->num_transforms == 0) {
+		wpa_printf(MSG_INFO, "IKEV2: At least one transform required");
+		return -1;
+	}
+
+	for (i = 0; i < (int) p->num_transforms; i++) {
+		int tlen = ikev2_parse_transform(data, prop, ppos, pend);
+		if (tlen < 0)
+			return -1;
+		ppos += tlen;
+	}
+
+	if (ppos != pend) {
+		wpa_printf(MSG_INFO, "IKEV2: Unexpected data after "
+			   "transforms");
+		return -1;
+	}
+
+	return proposal_len;
+}
+
+
+static int ikev2_process_sar1(struct ikev2_initiator_data *data,
+			      const u8 *sar1, size_t sar1_len)
+{
+	struct ikev2_proposal_data prop;
+	const u8 *pos, *end;
+	int found = 0;
+
+	/* Security Association Payloads: <Proposals> */
+
+	if (sar1 == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: SAr1 not received");
+		return -1;
+	}
+
+	os_memset(&prop, 0, sizeof(prop));
+	prop.proposal_num = 1;
+
+	pos = sar1;
+	end = sar1 + sar1_len;
+
+	while (pos < end) {
+		int plen;
+
+		prop.integ = -1;
+		prop.prf = -1;
+		prop.encr = -1;
+		prop.dh = -1;
+		plen = ikev2_parse_proposal(data, &prop, pos, end);
+		if (plen < 0)
+			return -1;
+
+		if (!found && prop.integ != -1 && prop.prf != -1 &&
+		    prop.encr != -1 && prop.dh != -1) {
+			found = 1;
+		}
+
+		pos += plen;
+
+		/* Only one proposal expected in SAr */
+		break;
+	}
+
+	if (pos != end) {
+		wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposal");
+		return -1;
+	}
+
+	if (!found) {
+		wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d "
+		   "INTEG:%d D-H:%d", data->proposal.proposal_num,
+		   data->proposal.encr, data->proposal.prf,
+		   data->proposal.integ, data->proposal.dh);
+
+	return 0;
+}
+
+
+static int ikev2_process_ker(struct ikev2_initiator_data *data,
+			     const u8 *ker, size_t ker_len)
+{
+	u16 group;
+
+	/*
+	 * Key Exchange Payload:
+	 * DH Group # (16 bits)
+	 * RESERVED (16 bits)
+	 * Key Exchange Data (Diffie-Hellman public value)
+	 */
+
+	if (ker == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: KEr not received");
+		return -1;
+	}
+
+	if (ker_len < 4 + 96) {
+		wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload");
+		return -1;
+	}
+
+	group = WPA_GET_BE16(ker);
+	wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u", group);
+
+	if (group != data->proposal.dh) {
+		wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u does not match "
+			   "with the selected proposal (%u)",
+			   group, data->proposal.dh);
+		return -1;
+	}
+
+	if (data->dh == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group");
+		return -1;
+	}
+
+	/* RFC 4306, Section 3.4:
+	 * The length of DH public value MUST be equal to the length of the
+	 * prime modulus.
+	 */
+	if (ker_len - 4 != data->dh->prime_len) {
+		wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length "
+			   "%ld (expected %ld)",
+			   (long) (ker_len - 4), (long) data->dh->prime_len);
+		return -1;
+	}
+
+	wpabuf_free(data->r_dh_public);
+	data->r_dh_public = wpabuf_alloc_copy(ker + 4, ker_len - 4);
+	if (data->r_dh_public == NULL)
+		return -1;
+
+	wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEr Diffie-Hellman Public Value",
+			data->r_dh_public);
+	
+	return 0;
+}
+
+
+static int ikev2_process_nr(struct ikev2_initiator_data *data,
+			    const u8 *nr, size_t nr_len)
+{
+	if (nr == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: Nr not received");
+		return -1;
+	}
+
+	if (nr_len < IKEV2_NONCE_MIN_LEN || nr_len > IKEV2_NONCE_MAX_LEN) {
+		wpa_printf(MSG_INFO, "IKEV2: Invalid Nr length %ld",
+			   (long) nr_len);
+		return -1;
+	}
+
+	data->r_nonce_len = nr_len;
+	os_memcpy(data->r_nonce, nr, nr_len);
+	wpa_hexdump(MSG_MSGDUMP, "IKEV2: Nr",
+		    data->r_nonce, data->r_nonce_len);
+
+	return 0;
+}
+
+
+static int ikev2_process_sa_init_encr(struct ikev2_initiator_data *data,
+				      const struct ikev2_hdr *hdr,
+				      const u8 *encrypted,
+				      size_t encrypted_len, u8 next_payload)
+{
+	u8 *decrypted;
+	size_t decrypted_len;
+	struct ikev2_payloads pl;
+	int ret = 0;
+
+	decrypted = ikev2_decrypt_payload(data->proposal.encr,
+					  data->proposal.integ, &data->keys, 0,
+					  hdr, encrypted, encrypted_len,
+					  &decrypted_len);
+	if (decrypted == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads");
+
+	if (ikev2_parse_payloads(&pl, next_payload, decrypted,
+				 decrypted + decrypted_len) < 0) {
+		wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted "
+			   "payloads");
+		return -1;
+	}
+
+	if (pl.idr)
+		ret = ikev2_process_idr(data, pl.idr, pl.idr_len);
+
+	os_free(decrypted);
+
+	return ret;
+}
+
+
+static int ikev2_process_sa_init(struct ikev2_initiator_data *data,
+				 const struct ikev2_hdr *hdr,
+				 struct ikev2_payloads *pl)
+{
+	if (ikev2_process_sar1(data, pl->sa, pl->sa_len) < 0 ||
+	    ikev2_process_ker(data, pl->ke, pl->ke_len) < 0 ||
+	    ikev2_process_nr(data, pl->nonce, pl->nonce_len) < 0)
+		return -1;
+
+	os_memcpy(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN);
+
+	if (ikev2_derive_keys(data) < 0)
+		return -1;
+
+	if (pl->encrypted) {
+		wpa_printf(MSG_DEBUG, "IKEV2: Encrypted payload in SA_INIT - "
+			   "try to get IDr from it");
+		if (ikev2_process_sa_init_encr(data, hdr, pl->encrypted,
+					       pl->encrypted_len,
+					       pl->encr_next_payload) < 0) {
+			wpa_printf(MSG_INFO, "IKEV2: Failed to process "
+				   "encrypted payload");
+			return -1;
+		}
+	}
+
+	data->state = SA_AUTH;
+
+	return 0;
+}
+
+
+static int ikev2_process_idr(struct ikev2_initiator_data *data,
+			     const u8 *idr, size_t idr_len)
+{
+	u8 id_type;
+
+	if (idr == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: No IDr received");
+		return -1;
+	}
+
+	if (idr_len < 4) {
+		wpa_printf(MSG_INFO, "IKEV2: Too short IDr payload");
+		return -1;
+	}
+
+	id_type = idr[0];
+	idr += 4;
+	idr_len -= 4;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: IDr ID Type %d", id_type);
+	wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDr", idr, idr_len);
+	if (data->IDr) {
+		if (id_type != data->IDr_type || idr_len != data->IDr_len ||
+		    os_memcmp(idr, data->IDr, idr_len) != 0) {
+			wpa_printf(MSG_INFO, "IKEV2: IDr differs from the one "
+				   "received earlier");
+			wpa_printf(MSG_DEBUG, "IKEV2: Previous IDr ID Type %d",
+				   id_type);
+			wpa_hexdump_ascii(MSG_DEBUG, "Previous IKEV2: IDr",
+					  data->IDr, data->IDr_len);
+			return -1;
+		}
+		os_free(data->IDr);
+	}
+	data->IDr = os_malloc(idr_len);
+	if (data->IDr == NULL)
+		return -1;
+	os_memcpy(data->IDr, idr, idr_len);
+	data->IDr_len = idr_len;
+	data->IDr_type = id_type;
+
+	return 0;
+}
+
+
+static int ikev2_process_cert(struct ikev2_initiator_data *data,
+			      const u8 *cert, size_t cert_len)
+{
+	u8 cert_encoding;
+
+	if (cert == NULL) {
+		if (data->peer_auth == PEER_AUTH_CERT) {
+			wpa_printf(MSG_INFO, "IKEV2: No Certificate received");
+			return -1;
+		}
+		return 0;
+	}
+
+	if (cert_len < 1) {
+		wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field");
+		return -1;
+	}
+
+	cert_encoding = cert[0];
+	cert++;
+	cert_len--;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding);
+	wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len);
+
+	/* TODO: validate certificate */
+
+	return 0;
+}
+
+
+static int ikev2_process_auth_cert(struct ikev2_initiator_data *data,
+				   u8 method, const u8 *auth, size_t auth_len)
+{
+	if (method != AUTH_RSA_SIGN) {
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
+			   "method %d", method);
+		return -1;
+	}
+
+	/* TODO: validate AUTH */
+	return 0;
+}
+
+
+static int ikev2_process_auth_secret(struct ikev2_initiator_data *data,
+				     u8 method, const u8 *auth,
+				     size_t auth_len)
+{
+	u8 auth_data[IKEV2_MAX_HASH_LEN];
+	const struct ikev2_prf_alg *prf;
+
+	if (method != AUTH_SHARED_KEY_MIC) {
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
+			   "method %d", method);
+		return -1;
+	}
+
+	/* msg | Ni | prf(SK_pr,IDr') */
+	if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg,
+				   data->IDr, data->IDr_len, data->IDr_type,
+				   &data->keys, 0, data->shared_secret,
+				   data->shared_secret_len,
+				   data->i_nonce, data->i_nonce_len,
+				   data->key_pad, data->key_pad_len,
+				   auth_data) < 0) {
+		wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
+		return -1;
+	}
+
+	wpabuf_free(data->r_sign_msg);
+	data->r_sign_msg = NULL;
+
+	prf = ikev2_get_prf(data->proposal.prf);
+	if (prf == NULL)
+		return -1;
+
+	if (auth_len != prf->hash_len ||
+	    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);
+		wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data",
+			    auth_data, prf->hash_len);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Peer authenticated successfully "
+		   "using shared keys");
+
+	return 0;
+}
+
+
+static int ikev2_process_auth(struct ikev2_initiator_data *data,
+			      const u8 *auth, size_t auth_len)
+{
+	u8 auth_method;
+
+	if (auth == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload");
+		return -1;
+	}
+
+	if (auth_len < 4) {
+		wpa_printf(MSG_INFO, "IKEV2: Too short Authentication "
+			   "Payload");
+		return -1;
+	}
+
+	auth_method = auth[0];
+	auth += 4;
+	auth_len -= 4;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method);
+	wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len);
+
+	switch (data->peer_auth) {
+	case PEER_AUTH_CERT:
+		return ikev2_process_auth_cert(data, auth_method, auth,
+					       auth_len);
+	case PEER_AUTH_SECRET:
+		return ikev2_process_auth_secret(data, auth_method, auth,
+						 auth_len);
+	}
+
+	return -1;
+}
+
+
+static int ikev2_process_sa_auth_decrypted(struct ikev2_initiator_data *data,
+					   u8 next_payload,
+					   u8 *payload, size_t payload_len)
+{
+	struct ikev2_payloads pl;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads");
+
+	if (ikev2_parse_payloads(&pl, next_payload, payload, payload +
+				 payload_len) < 0) {
+		wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted "
+			   "payloads");
+		return -1;
+	}
+
+	if (ikev2_process_idr(data, pl.idr, pl.idr_len) < 0 ||
+	    ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 ||
+	    ikev2_process_auth(data, pl.auth, pl.auth_len) < 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int ikev2_process_sa_auth(struct ikev2_initiator_data *data,
+				 const struct ikev2_hdr *hdr,
+				 struct ikev2_payloads *pl)
+{
+	u8 *decrypted;
+	size_t decrypted_len;
+	int ret;
+
+	decrypted = ikev2_decrypt_payload(data->proposal.encr,
+					  data->proposal.integ,
+					  &data->keys, 0, hdr, pl->encrypted,
+					  pl->encrypted_len, &decrypted_len);
+	if (decrypted == NULL)
+		return -1;
+
+	ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload,
+					      decrypted, decrypted_len);
+	os_free(decrypted);
+
+	if (ret == 0 && !data->unknown_user) {
+		wpa_printf(MSG_DEBUG, "IKEV2: Authentication completed");
+		data->state = IKEV2_DONE;
+	}
+
+	return ret;
+}
+
+
+static int ikev2_validate_rx_state(struct ikev2_initiator_data *data,
+				   u8 exchange_type, u32 message_id)
+{
+	switch (data->state) {
+	case SA_INIT:
+		/* Expect to receive IKE_SA_INIT: HDR, SAr, KEr, Nr, [CERTREQ],
+		 * [SK{IDr}] */
+		if (exchange_type != IKE_SA_INIT) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+				   "%u in SA_INIT state", exchange_type);
+			return -1;
+		}
+		if (message_id != 0) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+				   "in SA_INIT state", message_id);
+			return -1;
+		}
+		break;
+	case SA_AUTH:
+		/* Expect to receive IKE_SA_AUTH:
+		 * HDR, SK {IDr, [CERT,] [CERTREQ,] [NFID,] AUTH}
+		 */
+		if (exchange_type != IKE_SA_AUTH) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+				   "%u in SA_AUTH state", exchange_type);
+			return -1;
+		}
+		if (message_id != 1) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+				   "in SA_AUTH state", message_id);
+			return -1;
+		}
+		break;
+	case CHILD_SA:
+		if (exchange_type != CREATE_CHILD_SA) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+				   "%u in CHILD_SA state", exchange_type);
+			return -1;
+		}
+		if (message_id != 2) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+				   "in CHILD_SA state", message_id);
+			return -1;
+		}
+		break;
+	case IKEV2_DONE:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int ikev2_initiator_process(struct ikev2_initiator_data *data,
+			    const struct wpabuf *buf)
+{
+	const struct ikev2_hdr *hdr;
+	u32 length, message_id;
+	const u8 *pos, *end;
+	struct ikev2_payloads pl;
+
+	wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)",
+		   (unsigned long) wpabuf_len(buf));
+
+	if (wpabuf_len(buf) < sizeof(*hdr)) {
+		wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR");
+		return -1;
+	}
+
+	hdr = (const struct ikev2_hdr *) wpabuf_head(buf);
+	end = wpabuf_head_u8(buf) + wpabuf_len(buf);
+	message_id = WPA_GET_BE32(hdr->message_id);
+	length = WPA_GET_BE32(hdr->length);
+
+	wpa_hexdump(MSG_DEBUG, "IKEV2:   IKE_SA Initiator's SPI",
+		    hdr->i_spi, IKEV2_SPI_LEN);
+	wpa_hexdump(MSG_DEBUG, "IKEV2:   IKE_SA Initiator's SPI",
+		    hdr->r_spi, IKEV2_SPI_LEN);
+	wpa_printf(MSG_DEBUG, "IKEV2:   Next Payload: %u  Version: 0x%x  "
+		   "Exchange Type: %u",
+		   hdr->next_payload, hdr->version, hdr->exchange_type);
+	wpa_printf(MSG_DEBUG, "IKEV2:   Message ID: %u  Length: %u",
+		   message_id, length);
+
+	if (hdr->version != IKEV2_VERSION) {
+		wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x "
+			   "(expected 0x%x)", hdr->version, IKEV2_VERSION);
+		return -1;
+	}
+
+	if (length != wpabuf_len(buf)) {
+		wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != "
+			   "RX: %lu)", (unsigned long) length,
+			   (unsigned long) wpabuf_len(buf));
+		return -1;
+	}
+
+	if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0)
+		return -1;
+
+	if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) !=
+	    IKEV2_HDR_RESPONSE) {
+		wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x",
+			   hdr->flags);
+		return -1;
+	}
+
+	if (data->state != SA_INIT) {
+		if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
+				   "Initiator's SPI");
+			return -1;
+		}
+		if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) {
+			wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
+				   "Responder's SPI");
+			return -1;
+		}
+	}
+
+	pos = (const u8 *) (hdr + 1);
+	if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0)
+		return -1;
+
+	switch (data->state) {
+	case SA_INIT:
+		if (ikev2_process_sa_init(data, hdr, &pl) < 0)
+			return -1;
+		wpabuf_free(data->r_sign_msg);
+		data->r_sign_msg = wpabuf_dup(buf);
+		break;
+	case SA_AUTH:
+		if (ikev2_process_sa_auth(data, hdr, &pl) < 0)
+			return -1;
+		break;
+	case CHILD_SA:
+	case IKEV2_DONE:
+		break;
+	}
+
+	return 0;
+}
+
+
+static void ikev2_build_hdr(struct ikev2_initiator_data *data,
+			    struct wpabuf *msg, u8 exchange_type,
+			    u8 next_payload, u32 message_id)
+{
+	struct ikev2_hdr *hdr;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR");
+
+	/* HDR - RFC 4306, Sect. 3.1 */
+	hdr = wpabuf_put(msg, sizeof(*hdr));
+	os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN);
+	os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN);
+	hdr->next_payload = next_payload;
+	hdr->version = IKEV2_VERSION;
+	hdr->exchange_type = exchange_type;
+	hdr->flags = IKEV2_HDR_INITIATOR;
+	WPA_PUT_BE32(hdr->message_id, message_id);
+}
+
+
+static int ikev2_build_sai(struct ikev2_initiator_data *data,
+			    struct wpabuf *msg, u8 next_payload)
+{
+	struct ikev2_payload_hdr *phdr;
+	size_t plen;
+	struct ikev2_proposal *p;
+	struct ikev2_transform *t;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Adding SAi payload");
+
+	/* SAi1 - RFC 4306, Sect. 2.7 and 3.3 */
+	phdr = wpabuf_put(msg, sizeof(*phdr));
+	phdr->next_payload = next_payload;
+	phdr->flags = 0;
+
+	/* TODO: support for multiple proposals */
+	p = wpabuf_put(msg, sizeof(*p));
+	p->proposal_num = data->proposal.proposal_num;
+	p->protocol_id = IKEV2_PROTOCOL_IKE;
+	p->num_transforms = 4;
+
+	t = wpabuf_put(msg, sizeof(*t));
+	t->type = 3;
+	t->transform_type = IKEV2_TRANSFORM_ENCR;
+	WPA_PUT_BE16(t->transform_id, data->proposal.encr);
+	if (data->proposal.encr == ENCR_AES_CBC) {
+		/* Transform Attribute: Key Len = 128 bits */
+		wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */
+		wpabuf_put_be16(msg, 128); /* 128-bit key */
+	}
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t;
+	WPA_PUT_BE16(t->transform_length, plen);
+
+	t = wpabuf_put(msg, sizeof(*t));
+	t->type = 3;
+	WPA_PUT_BE16(t->transform_length, sizeof(*t));
+	t->transform_type = IKEV2_TRANSFORM_PRF;
+	WPA_PUT_BE16(t->transform_id, data->proposal.prf);
+
+	t = wpabuf_put(msg, sizeof(*t));
+	t->type = 3;
+	WPA_PUT_BE16(t->transform_length, sizeof(*t));
+	t->transform_type = IKEV2_TRANSFORM_INTEG;
+	WPA_PUT_BE16(t->transform_id, data->proposal.integ);
+
+	t = wpabuf_put(msg, sizeof(*t));
+	WPA_PUT_BE16(t->transform_length, sizeof(*t));
+	t->transform_type = IKEV2_TRANSFORM_DH;
+	WPA_PUT_BE16(t->transform_id, data->proposal.dh);
+
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p;
+	WPA_PUT_BE16(p->proposal_length, plen);
+
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+	WPA_PUT_BE16(phdr->payload_length, plen);
+
+	return 0;
+}
+
+
+static int ikev2_build_kei(struct ikev2_initiator_data *data,
+			   struct wpabuf *msg, u8 next_payload)
+{
+	struct ikev2_payload_hdr *phdr;
+	size_t plen;
+	struct wpabuf *pv;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Adding KEi payload");
+
+	data->dh = dh_groups_get(data->proposal.dh);
+	pv = dh_init(data->dh, &data->i_dh_private);
+	if (pv == NULL) {
+		wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH");
+		return -1;
+	}
+
+	/* KEi - RFC 4306, Sect. 3.4 */
+	phdr = wpabuf_put(msg, sizeof(*phdr));
+	phdr->next_payload = next_payload;
+	phdr->flags = 0;
+
+	wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */
+	wpabuf_put(msg, 2); /* RESERVED */
+	/*
+	 * RFC 4306, Sect. 3.4: possible zero padding for public value to
+	 * match the length of the prime.
+	 */
+	wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv));
+	wpabuf_put_buf(msg, pv);
+	wpabuf_free(pv);
+
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+	WPA_PUT_BE16(phdr->payload_length, plen);
+	return 0;
+}
+
+
+static int ikev2_build_ni(struct ikev2_initiator_data *data,
+			  struct wpabuf *msg, u8 next_payload)
+{
+	struct ikev2_payload_hdr *phdr;
+	size_t plen;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Adding Ni payload");
+
+	/* Ni - RFC 4306, Sect. 3.9 */
+	phdr = wpabuf_put(msg, sizeof(*phdr));
+	phdr->next_payload = next_payload;
+	phdr->flags = 0;
+	wpabuf_put_data(msg, data->i_nonce, data->i_nonce_len);
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+	WPA_PUT_BE16(phdr->payload_length, plen);
+	return 0;
+}
+
+
+static int ikev2_build_idi(struct ikev2_initiator_data *data,
+			   struct wpabuf *msg, u8 next_payload)
+{
+	struct ikev2_payload_hdr *phdr;
+	size_t plen;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Adding IDi payload");
+
+	if (data->IDi == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: No IDi available");
+		return -1;
+	}
+
+	/* IDi - RFC 4306, Sect. 3.5 */
+	phdr = wpabuf_put(msg, sizeof(*phdr));
+	phdr->next_payload = next_payload;
+	phdr->flags = 0;
+	wpabuf_put_u8(msg, ID_KEY_ID);
+	wpabuf_put(msg, 3); /* RESERVED */
+	wpabuf_put_data(msg, data->IDi, data->IDi_len);
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+	WPA_PUT_BE16(phdr->payload_length, plen);
+	return 0;
+}
+
+
+static int ikev2_build_auth(struct ikev2_initiator_data *data,
+			    struct wpabuf *msg, u8 next_payload)
+{
+	struct ikev2_payload_hdr *phdr;
+	size_t plen;
+	const struct ikev2_prf_alg *prf;
+
+	wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload");
+
+	prf = ikev2_get_prf(data->proposal.prf);
+	if (prf == NULL)
+		return -1;
+
+	/* Authentication - RFC 4306, Sect. 3.8 */
+	phdr = wpabuf_put(msg, sizeof(*phdr));
+	phdr->next_payload = next_payload;
+	phdr->flags = 0;
+	wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC);
+	wpabuf_put(msg, 3); /* RESERVED */
+
+	/* msg | Nr | prf(SK_pi,IDi') */
+	if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg,
+				   data->IDi, data->IDi_len, ID_KEY_ID,
+				   &data->keys, 1, data->shared_secret,
+				   data->shared_secret_len,
+				   data->r_nonce, data->r_nonce_len,
+				   data->key_pad, data->key_pad_len,
+				   wpabuf_put(msg, prf->hash_len)) < 0) {
+		wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
+		return -1;
+	}
+	wpabuf_free(data->i_sign_msg);
+	data->i_sign_msg = NULL;
+
+	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+	WPA_PUT_BE16(phdr->payload_length, plen);
+	return 0;
+}
+
+
+static struct wpabuf * ikev2_build_sa_init(struct ikev2_initiator_data *data)
+{
+	struct wpabuf *msg;
+
+	/* build IKE_SA_INIT: HDR, SAi, KEi, Ni */
+
+	if (os_get_random(data->i_spi, IKEV2_SPI_LEN))
+		return NULL;
+	wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI",
+		    data->i_spi, IKEV2_SPI_LEN);
+
+	data->i_nonce_len = IKEV2_NONCE_MIN_LEN;
+	if (random_get_bytes(data->i_nonce, data->i_nonce_len))
+		return NULL;
+	wpa_hexdump(MSG_DEBUG, "IKEV2: Ni", data->i_nonce, data->i_nonce_len);
+
+	msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + 1000);
+	if (msg == NULL)
+		return NULL;
+
+	ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_SA, 0);
+	if (ikev2_build_sai(data, msg, IKEV2_PAYLOAD_KEY_EXCHANGE) ||
+	    ikev2_build_kei(data, msg, IKEV2_PAYLOAD_NONCE) ||
+	    ikev2_build_ni(data, msg, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	ikev2_update_hdr(msg);
+
+	wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_INIT)", msg);
+
+	wpabuf_free(data->i_sign_msg);
+	data->i_sign_msg = wpabuf_dup(msg);
+
+	return msg;
+}
+
+
+static struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data)
+{
+	struct wpabuf *msg, *plain;
+	const u8 *secret;
+	size_t secret_len;
+
+	secret = data->get_shared_secret(data->cb_ctx, data->IDr,
+					 data->IDr_len, &secret_len);
+	if (secret == NULL) {
+		wpa_printf(MSG_INFO, "IKEV2: Could not get shared secret - "
+			   "use fake value");
+		/* RFC 5106, Sect. 7:
+		 * Use a random key to fake AUTH generation in order to prevent
+		 * probing of user identities.
+		 */
+		data->unknown_user = 1;
+		os_free(data->shared_secret);
+		data->shared_secret = os_malloc(16);
+		if (data->shared_secret == NULL)
+			return NULL;
+		data->shared_secret_len = 16;
+		if (random_get_bytes(data->shared_secret, 16))
+			return NULL;
+	} else {
+		os_free(data->shared_secret);
+		data->shared_secret = os_malloc(secret_len);
+		if (data->shared_secret == NULL)
+			return NULL;
+		os_memcpy(data->shared_secret, secret, secret_len);
+		data->shared_secret_len = secret_len;
+	}
+
+	/* build IKE_SA_AUTH: HDR, SK {IDi, [CERT,] [CERTREQ,] AUTH} */
+
+	msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1000);
+	if (msg == NULL)
+		return NULL;
+	ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1);
+
+	plain = wpabuf_alloc(data->IDr_len + 1000);
+	if (plain == NULL) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	if (ikev2_build_idi(data, plain, IKEV2_PAYLOAD_AUTHENTICATION) ||
+	    ikev2_build_auth(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) ||
+	    ikev2_build_encrypted(data->proposal.encr, data->proposal.integ,
+				  &data->keys, 1, msg, plain,
+				  IKEV2_PAYLOAD_IDi)) {
+		wpabuf_free(plain);
+		wpabuf_free(msg);
+		return NULL;
+	}
+	wpabuf_free(plain);
+
+	wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_AUTH)", msg);
+
+	return msg;
+}
+
+
+struct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data)
+{
+	switch (data->state) {
+	case SA_INIT:
+		return ikev2_build_sa_init(data);
+	case SA_AUTH:
+		return ikev2_build_sa_auth(data);
+	case CHILD_SA:
+		return NULL;
+	case IKEV2_DONE:
+		return NULL;
+	}
+	return NULL;
+}
diff --git a/hostap/src/eap_server/ikev2.h b/hostap/src/eap_server/ikev2.h
new file mode 100644
index 0000000..051a938
--- /dev/null
+++ b/hostap/src/eap_server/ikev2.h
@@ -0,0 +1,61 @@
+/*
+ * IKEv2 initiator (RFC 4306) for EAP-IKEV2
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IKEV2_H
+#define IKEV2_H
+
+#include "eap_common/ikev2_common.h"
+
+struct ikev2_proposal_data {
+	u8 proposal_num;
+	int integ;
+	int prf;
+	int encr;
+	int dh;
+};
+
+
+struct ikev2_initiator_data {
+	enum { SA_INIT, SA_AUTH, CHILD_SA, IKEV2_DONE } state;
+	u8 i_spi[IKEV2_SPI_LEN];
+	u8 r_spi[IKEV2_SPI_LEN];
+	u8 i_nonce[IKEV2_NONCE_MAX_LEN];
+	size_t i_nonce_len;
+	u8 r_nonce[IKEV2_NONCE_MAX_LEN];
+	size_t r_nonce_len;
+	struct wpabuf *r_dh_public;
+	struct wpabuf *i_dh_private;
+	struct ikev2_proposal_data proposal;
+	const struct dh_group *dh;
+	struct ikev2_keys keys;
+	u8 *IDi;
+	size_t IDi_len;
+	u8 *IDr;
+	size_t IDr_len;
+	u8 IDr_type;
+	struct wpabuf *r_sign_msg;
+	struct wpabuf *i_sign_msg;
+	u8 *shared_secret;
+	size_t shared_secret_len;
+	enum { PEER_AUTH_CERT, PEER_AUTH_SECRET } peer_auth;
+	u8 *key_pad;
+	size_t key_pad_len;
+
+	const u8 * (*get_shared_secret)(void *ctx, const u8 *IDr,
+					size_t IDr_len, size_t *secret_len);
+	void *cb_ctx;
+	int unknown_user;
+};
+
+
+void ikev2_initiator_deinit(struct ikev2_initiator_data *data);
+int ikev2_initiator_process(struct ikev2_initiator_data *data,
+			    const struct wpabuf *buf);
+struct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data);
+
+#endif /* IKEV2_H */
diff --git a/hostap/src/eap_server/tncs.c b/hostap/src/eap_server/tncs.c
new file mode 100644
index 0000000..dc6f689
--- /dev/null
+++ b/hostap/src/eap_server/tncs.c
@@ -0,0 +1,1202 @@
+/*
+ * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
+ * Copyright (c) 2007-2008, 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 <dlfcn.h>
+
+#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"
+
+
+/* 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\" " \
+"xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
+"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
+"xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
+"IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
+#define IF_TNCCS_END "\n</TNCCS-Batch>"
+
+/* TNC IF-IMV */
+
+struct tnc_if_imv {
+	struct tnc_if_imv *next;
+	char *name;
+	char *path;
+	void *dlhandle; /* from dlopen() */
+	TNC_IMVID imvID;
+	TNC_MessageTypeList supported_types;
+	size_t num_supported_types;
+
+	/* Functions implemented by IMVs (with TNC_IMV_ prefix) */
+	TNC_Result (*Initialize)(
+		TNC_IMVID imvID,
+		TNC_Version minVersion,
+		TNC_Version maxVersion,
+		TNC_Version *pOutActualVersion);
+	TNC_Result (*NotifyConnectionChange)(
+		TNC_IMVID imvID,
+		TNC_ConnectionID connectionID,
+		TNC_ConnectionState newState);
+	TNC_Result (*ReceiveMessage)(
+		TNC_IMVID imvID,
+		TNC_ConnectionID connectionID,
+		TNC_BufferReference message,
+		TNC_UInt32 messageLength,
+		TNC_MessageType messageType);
+	TNC_Result (*SolicitRecommendation)(
+		TNC_IMVID imvID,
+		TNC_ConnectionID connectionID);
+	TNC_Result (*BatchEnding)(
+		TNC_IMVID imvID,
+		TNC_ConnectionID connectionID);
+	TNC_Result (*Terminate)(TNC_IMVID imvID);
+	TNC_Result (*ProvideBindFunction)(
+		TNC_IMVID imvID,
+		TNC_TNCS_BindFunctionPointer bindFunction);
+};
+
+
+#define TNC_MAX_IMV_ID 10
+
+struct tncs_data {
+	struct tncs_data *next;
+	struct tnc_if_imv *imv; /* local copy of tncs_global_data->imv */
+	TNC_ConnectionID connectionID;
+	unsigned int last_batchid;
+	enum IMV_Action_Recommendation recommendation;
+	int done;
+
+	struct conn_imv {
+		u8 *imv_send;
+		size_t imv_send_len;
+		enum IMV_Action_Recommendation recommendation;
+		int recommendation_set;
+	} imv_data[TNC_MAX_IMV_ID];
+
+	char *tncs_message;
+};
+
+
+struct tncs_global {
+	struct tnc_if_imv *imv;
+	TNC_ConnectionID next_conn_id;
+	struct tncs_data *connections;
+};
+
+static struct tncs_global *tncs_global_data = NULL;
+
+
+static struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID)
+{
+	struct tnc_if_imv *imv;
+
+	if (imvID >= TNC_MAX_IMV_ID || tncs_global_data == NULL)
+		return NULL;
+	imv = tncs_global_data->imv;
+	while (imv) {
+		if (imv->imvID == imvID)
+			return imv;
+		imv = imv->next;
+	}
+	return NULL;
+}
+
+
+static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID)
+{
+	struct tncs_data *tncs;
+
+	if (tncs_global_data == NULL)
+		return NULL;
+
+	tncs = tncs_global_data->connections;
+	while (tncs) {
+		if (tncs->connectionID == connectionID)
+			return tncs;
+		tncs = tncs->next;
+	}
+
+	wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found",
+		   (unsigned long) connectionID);
+
+	return NULL;
+}
+
+
+/* TNCS functions that IMVs can call */
+TNC_Result TNC_TNCS_ReportMessageTypes(
+	TNC_IMVID imvID,
+	TNC_MessageTypeList supportedTypes,
+	TNC_UInt32 typeCount)
+{
+	TNC_UInt32 i;
+	struct tnc_if_imv *imv;
+
+	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu "
+		   "typeCount=%lu)",
+		   (unsigned long) imvID, (unsigned long) typeCount);
+
+	for (i = 0; i < typeCount; i++) {
+		wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
+			   i, supportedTypes[i]);
+	}
+
+	imv = tncs_get_imv(imvID);
+	if (imv == NULL)
+		return TNC_RESULT_INVALID_PARAMETER;
+	os_free(imv->supported_types);
+	imv->supported_types =
+		os_malloc(typeCount * sizeof(TNC_MessageType));
+	if (imv->supported_types == NULL)
+		return TNC_RESULT_FATAL;
+	os_memcpy(imv->supported_types, supportedTypes,
+		  typeCount * sizeof(TNC_MessageType));
+	imv->num_supported_types = typeCount;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_SendMessage(
+	TNC_IMVID imvID,
+	TNC_ConnectionID connectionID,
+	TNC_BufferReference message,
+	TNC_UInt32 messageLength,
+	TNC_MessageType messageType)
+{
+	struct tncs_data *tncs;
+	unsigned char *b64;
+	size_t b64len;
+
+	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu "
+		   "connectionID=%lu messageType=%lu)",
+		   imvID, connectionID, messageType);
+	wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage",
+			  message, messageLength);
+
+	if (tncs_get_imv(imvID) == NULL)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	tncs = tncs_get_conn(connectionID);
+	if (tncs == NULL)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	b64 = base64_encode(message, messageLength, &b64len);
+	if (b64 == NULL)
+		return TNC_RESULT_FATAL;
+
+	os_free(tncs->imv_data[imvID].imv_send);
+	tncs->imv_data[imvID].imv_send_len = 0;
+	tncs->imv_data[imvID].imv_send = os_zalloc(b64len + 100);
+	if (tncs->imv_data[imvID].imv_send == NULL) {
+		os_free(b64);
+		return TNC_RESULT_OTHER;
+	}
+
+	tncs->imv_data[imvID].imv_send_len =
+		os_snprintf((char *) tncs->imv_data[imvID].imv_send,
+			    b64len + 100,
+			    "<IMC-IMV-Message><Type>%08X</Type>"
+			    "<Base64>%s</Base64></IMC-IMV-Message>",
+			    (unsigned int) messageType, b64);
+
+	os_free(b64);
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_RequestHandshakeRetry(
+	TNC_IMVID imvID,
+	TNC_ConnectionID connectionID,
+	TNC_RetryReason reason)
+{
+	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry");
+	/* TODO */
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_ProvideRecommendation(
+	TNC_IMVID imvID,
+	TNC_ConnectionID connectionID,
+	TNC_IMV_Action_Recommendation recommendation,
+	TNC_IMV_Evaluation_Result evaluation)
+{
+	struct tncs_data *tncs;
+
+	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ProvideRecommendation(imvID=%lu "
+		   "connectionID=%lu recommendation=%lu evaluation=%lu)",
+		   (unsigned long) imvID, (unsigned long) connectionID,
+		   (unsigned long) recommendation, (unsigned long) evaluation);
+
+	if (tncs_get_imv(imvID) == NULL)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	tncs = tncs_get_conn(connectionID);
+	if (tncs == NULL)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	tncs->imv_data[imvID].recommendation = recommendation;
+	tncs->imv_data[imvID].recommendation_set = 1;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_GetAttribute(
+	TNC_IMVID imvID,
+	TNC_ConnectionID connectionID,
+	TNC_AttributeID attribureID,
+	TNC_UInt32 bufferLength,
+	TNC_BufferReference buffer,
+	TNC_UInt32 *pOutValueLength)
+{
+	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute");
+	/* TODO */
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_SetAttribute(
+	TNC_IMVID imvID,
+	TNC_ConnectionID connectionID,
+	TNC_AttributeID attribureID,
+	TNC_UInt32 bufferLength,
+	TNC_BufferReference buffer)
+{
+	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute");
+	/* TODO */
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_BindFunction(
+	TNC_IMVID imvID,
+	char *functionName,
+	void **pOutFunctionPointer)
+{
+	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, "
+		   "functionName='%s')", (unsigned long) imvID, functionName);
+
+	if (tncs_get_imv(imvID) == NULL)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	if (pOutFunctionPointer == NULL)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	if (os_strcmp(functionName, "TNC_TNCS_ReportMessageTypes") == 0)
+		*pOutFunctionPointer = TNC_TNCS_ReportMessageTypes;
+	else if (os_strcmp(functionName, "TNC_TNCS_SendMessage") == 0)
+		*pOutFunctionPointer = TNC_TNCS_SendMessage;
+	else if (os_strcmp(functionName, "TNC_TNCS_RequestHandshakeRetry") ==
+		 0)
+		*pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry;
+	else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") ==
+		 0)
+		*pOutFunctionPointer = TNC_TNCS_ProvideRecommendation;
+	else if (os_strcmp(functionName, "TNC_TNCS_GetAttribute") == 0)
+		*pOutFunctionPointer = TNC_TNCS_GetAttribute;
+	else if (os_strcmp(functionName, "TNC_TNCS_SetAttribute") == 0)
+		*pOutFunctionPointer = TNC_TNCS_SetAttribute;
+	else
+		*pOutFunctionPointer = NULL;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+static void * tncs_get_sym(void *handle, char *func)
+{
+	void *fptr;
+
+	fptr = dlsym(handle, func);
+
+	return fptr;
+}
+
+
+static int tncs_imv_resolve_funcs(struct tnc_if_imv *imv)
+{
+	void *handle = imv->dlhandle;
+
+	/* Mandatory IMV functions */
+	imv->Initialize = tncs_get_sym(handle, "TNC_IMV_Initialize");
+	if (imv->Initialize == NULL) {
+		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
+			   "TNC_IMV_Initialize");
+		return -1;
+	}
+
+	imv->SolicitRecommendation = tncs_get_sym(
+		handle, "TNC_IMV_SolicitRecommendation");
+	if (imv->SolicitRecommendation == NULL) {
+		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
+			   "TNC_IMV_SolicitRecommendation");
+		return -1;
+	}
+
+	imv->ProvideBindFunction =
+		tncs_get_sym(handle, "TNC_IMV_ProvideBindFunction");
+	if (imv->ProvideBindFunction == NULL) {
+		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
+			   "TNC_IMV_ProvideBindFunction");
+		return -1;
+	}
+
+	/* Optional IMV functions */
+	imv->NotifyConnectionChange =
+		tncs_get_sym(handle, "TNC_IMV_NotifyConnectionChange");
+	imv->ReceiveMessage = tncs_get_sym(handle, "TNC_IMV_ReceiveMessage");
+	imv->BatchEnding = tncs_get_sym(handle, "TNC_IMV_BatchEnding");
+	imv->Terminate = tncs_get_sym(handle, "TNC_IMV_Terminate");
+
+	return 0;
+}
+
+
+static int tncs_imv_initialize(struct tnc_if_imv *imv)
+{
+	TNC_Result res;
+	TNC_Version imv_ver;
+
+	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Initialize for IMV '%s'",
+		   imv->name);
+	res = imv->Initialize(imv->imvID, TNC_IFIMV_VERSION_1,
+			      TNC_IFIMV_VERSION_1, &imv_ver);
+	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Initialize: res=%lu imv_ver=%lu",
+		   (unsigned long) res, (unsigned long) imv_ver);
+
+	return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncs_imv_terminate(struct tnc_if_imv *imv)
+{
+	TNC_Result res;
+
+	if (imv->Terminate == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Terminate for IMV '%s'",
+		   imv->name);
+	res = imv->Terminate(imv->imvID);
+	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Terminate: %lu",
+		   (unsigned long) res);
+
+	return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncs_imv_provide_bind_function(struct tnc_if_imv *imv)
+{
+	TNC_Result res;
+
+	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_ProvideBindFunction for "
+		   "IMV '%s'", imv->name);
+	res = imv->ProvideBindFunction(imv->imvID, TNC_TNCS_BindFunction);
+	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_ProvideBindFunction: res=%lu",
+		   (unsigned long) res);
+
+	return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncs_imv_notify_connection_change(struct tnc_if_imv *imv,
+					     TNC_ConnectionID conn,
+					     TNC_ConnectionState state)
+{
+	TNC_Result res;
+
+	if (imv->NotifyConnectionChange == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_NotifyConnectionChange(%d)"
+		   " for IMV '%s'", (int) state, imv->name);
+	res = imv->NotifyConnectionChange(imv->imvID, conn, state);
+	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
+		   (unsigned long) res);
+
+	return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncs_load_imv(struct tnc_if_imv *imv)
+{
+	if (imv->path == NULL) {
+		wpa_printf(MSG_DEBUG, "TNC: No IMV configured");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TNC: Opening IMV: %s (%s)",
+		   imv->name, imv->path);
+	imv->dlhandle = dlopen(imv->path, RTLD_LAZY);
+	if (imv->dlhandle == NULL) {
+		wpa_printf(MSG_ERROR, "TNC: Failed to open IMV '%s' (%s): %s",
+			   imv->name, imv->path, dlerror());
+		return -1;
+	}
+
+	if (tncs_imv_resolve_funcs(imv) < 0) {
+		wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions");
+		return -1;
+	}
+
+	if (tncs_imv_initialize(imv) < 0 ||
+	    tncs_imv_provide_bind_function(imv) < 0) {
+		wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMV");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void tncs_free_imv(struct tnc_if_imv *imv)
+{
+	os_free(imv->name);
+	os_free(imv->path);
+	os_free(imv->supported_types);
+}
+
+static void tncs_unload_imv(struct tnc_if_imv *imv)
+{
+	tncs_imv_terminate(imv);
+
+	if (imv->dlhandle)
+		dlclose(imv->dlhandle);
+
+	tncs_free_imv(imv);
+}
+
+
+static int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type)
+{
+	size_t i;
+	unsigned int vendor, subtype;
+
+	if (imv == NULL || imv->supported_types == NULL)
+		return 0;
+
+	vendor = type >> 8;
+	subtype = type & 0xff;
+
+	for (i = 0; i < imv->num_supported_types; i++) {
+		unsigned int svendor, ssubtype;
+		svendor = imv->supported_types[i] >> 8;
+		ssubtype = imv->supported_types[i] & 0xff;
+		if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
+		    (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type,
+			      const u8 *msg, size_t len)
+{
+	struct tnc_if_imv *imv;
+	TNC_Result res;
+
+	wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len);
+
+	for (imv = tncs->imv; imv; imv = imv->next) {
+		if (imv->ReceiveMessage == NULL ||
+		    !tncs_supported_type(imv, type))
+			continue;
+
+		wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMV '%s'",
+			   imv->name);
+		res = imv->ReceiveMessage(imv->imvID, tncs->connectionID,
+					  (TNC_BufferReference) msg, len,
+					  type);
+		wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
+			   (unsigned long) res);
+	}
+}
+
+
+static void tncs_batch_ending(struct tncs_data *tncs)
+{
+	struct tnc_if_imv *imv;
+	TNC_Result res;
+
+	for (imv = tncs->imv; imv; imv = imv->next) {
+		if (imv->BatchEnding == NULL)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "TNC: Call BatchEnding for IMV '%s'",
+			   imv->name);
+		res = imv->BatchEnding(imv->imvID, tncs->connectionID);
+		wpa_printf(MSG_DEBUG, "TNC: BatchEnding: %lu",
+			   (unsigned long) res);
+	}
+}
+
+
+static void tncs_solicit_recommendation(struct tncs_data *tncs)
+{
+	struct tnc_if_imv *imv;
+	TNC_Result res;
+
+	for (imv = tncs->imv; imv; imv = imv->next) {
+		if (tncs->imv_data[imv->imvID].recommendation_set)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "TNC: Call SolicitRecommendation for "
+			   "IMV '%s'", imv->name);
+		res = imv->SolicitRecommendation(imv->imvID,
+						 tncs->connectionID);
+		wpa_printf(MSG_DEBUG, "TNC: SolicitRecommendation: %lu",
+			   (unsigned long) res);
+	}
+}
+
+
+void tncs_init_connection(struct tncs_data *tncs)
+{
+	struct tnc_if_imv *imv;
+	int i;
+
+	for (imv = tncs->imv; imv; imv = imv->next) {
+		tncs_imv_notify_connection_change(
+			imv, tncs->connectionID, TNC_CONNECTION_STATE_CREATE);
+		tncs_imv_notify_connection_change(
+			imv, tncs->connectionID,
+			TNC_CONNECTION_STATE_HANDSHAKE);
+	}
+
+	for (i = 0; i < TNC_MAX_IMV_ID; i++) {
+		os_free(tncs->imv_data[i].imv_send);
+		tncs->imv_data[i].imv_send = NULL;
+		tncs->imv_data[i].imv_send_len = 0;
+	}
+}
+
+
+size_t tncs_total_send_len(struct tncs_data *tncs)
+{
+	int i;
+	size_t len = 0;
+
+	for (i = 0; i < TNC_MAX_IMV_ID; i++)
+		len += tncs->imv_data[i].imv_send_len;
+	if (tncs->tncs_message)
+		len += os_strlen(tncs->tncs_message);
+	return len;
+}
+
+
+u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos)
+{
+	int i;
+
+	for (i = 0; i < TNC_MAX_IMV_ID; i++) {
+		if (tncs->imv_data[i].imv_send == NULL)
+			continue;
+
+		os_memcpy(pos, tncs->imv_data[i].imv_send,
+			  tncs->imv_data[i].imv_send_len);
+		pos += tncs->imv_data[i].imv_send_len;
+		os_free(tncs->imv_data[i].imv_send);
+		tncs->imv_data[i].imv_send = NULL;
+		tncs->imv_data[i].imv_send_len = 0;
+	}
+
+	if (tncs->tncs_message) {
+		size_t len = os_strlen(tncs->tncs_message);
+		os_memcpy(pos, tncs->tncs_message, len);
+		pos += len;
+		os_free(tncs->tncs_message);
+		tncs->tncs_message = NULL;
+	}
+
+	return pos;
+}
+
+
+char * tncs_if_tnccs_start(struct tncs_data *tncs)
+{
+	char *buf = os_malloc(1000);
+	if (buf == NULL)
+		return NULL;
+	tncs->last_batchid++;
+	os_snprintf(buf, 1000, IF_TNCCS_START, tncs->last_batchid);
+	return buf;
+}
+
+
+char * tncs_if_tnccs_end(void)
+{
+	char *buf = os_malloc(100);
+	if (buf == NULL)
+		return NULL;
+	os_snprintf(buf, 100, IF_TNCCS_END);
+	return buf;
+}
+
+
+static int tncs_get_type(char *start, unsigned int *type)
+{
+	char *pos = os_strstr(start, "<Type>");
+	if (pos == NULL)
+		return -1;
+	pos += 6;
+	*type = strtoul(pos, NULL, 16);
+	return 0;
+}
+
+
+static unsigned char * tncs_get_base64(char *start, size_t *decoded_len)
+{
+	char *pos, *pos2;
+	unsigned char *decoded;
+
+	pos = os_strstr(start, "<Base64>");
+	if (pos == NULL)
+		return NULL;
+
+	pos += 8;
+	pos2 = os_strstr(pos, "</Base64>");
+	if (pos2 == NULL)
+		return NULL;
+	*pos2 = '\0';
+
+	decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
+				decoded_len);
+	*pos2 = '<';
+	if (decoded == NULL) {
+		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
+	}
+
+	return decoded;
+}
+
+
+static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs)
+{
+	enum IMV_Action_Recommendation rec;
+	struct tnc_if_imv *imv;
+	TNC_ConnectionState state;
+	char *txt;
+
+	wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs");
+
+	if (tncs->done)
+		return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
+
+	tncs_solicit_recommendation(tncs);
+
+	/* Select the most restrictive recommendation */
+	rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
+	for (imv = tncs->imv; imv; imv = imv->next) {
+		TNC_IMV_Action_Recommendation irec;
+		irec = tncs->imv_data[imv->imvID].recommendation;
+		if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
+			rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
+		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE &&
+		    rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
+			rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
+		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW &&
+		    rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION)
+			rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
+	}
+
+	wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec);
+	tncs->recommendation = rec;
+	tncs->done = 1;
+
+	txt = NULL;
+	switch (rec) {
+	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
+	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
+		txt = "allow";
+		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
+		break;
+	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
+		txt = "isolate";
+		state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
+		break;
+	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
+		txt = "none";
+		state = TNC_CONNECTION_STATE_ACCESS_NONE;
+		break;
+	default:
+		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
+		break;
+	}
+
+	if (txt) {
+		os_free(tncs->tncs_message);
+		tncs->tncs_message = os_zalloc(200);
+		if (tncs->tncs_message) {
+			os_snprintf(tncs->tncs_message, 199,
+				    "<TNCC-TNCS-Message><Type>%08X</Type>"
+				    "<XML><TNCCS-Recommendation type=\"%s\">"
+				    "</TNCCS-Recommendation></XML>"
+				    "</TNCC-TNCS-Message>",
+				    TNC_TNCCS_RECOMMENDATION, txt);
+		}
+	}
+
+	for (imv = tncs->imv; imv; imv = imv->next) {
+		tncs_imv_notify_connection_change(imv, tncs->connectionID,
+						  state);
+	}
+
+	switch (rec) {
+	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
+		return TNCCS_RECOMMENDATION_ALLOW;
+	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
+		return TNCCS_RECOMMENDATION_NO_ACCESS;
+	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
+		return TNCCS_RECOMMENDATION_ISOLATE;
+	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
+		return TNCCS_RECOMMENDATION_NO_RECOMMENDATION;
+	default:
+		return TNCCS_PROCESS_ERROR;
+	}
+}
+
+
+enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
+					    const u8 *msg, size_t len)
+{
+	char *buf, *start, *end, *pos, *pos2, *payload;
+	unsigned int batch_id;
+	unsigned char *decoded;
+	size_t decoded_len;
+
+	buf = dup_binstr(msg, len);
+	if (buf == NULL)
+		return TNCCS_PROCESS_ERROR;
+
+	start = os_strstr(buf, "<TNCCS-Batch ");
+	end = os_strstr(buf, "</TNCCS-Batch>");
+	if (start == NULL || end == NULL || start > end) {
+		os_free(buf);
+		return TNCCS_PROCESS_ERROR;
+	}
+
+	start += 13;
+	while (*start == ' ')
+		start++;
+	*end = '\0';
+
+	pos = os_strstr(start, "BatchId=");
+	if (pos == NULL) {
+		os_free(buf);
+		return TNCCS_PROCESS_ERROR;
+	}
+
+	pos += 8;
+	if (*pos == '"')
+		pos++;
+	batch_id = atoi(pos);
+	wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
+		   batch_id);
+	if (batch_id != tncs->last_batchid + 1) {
+		wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
+			   "%u (expected %u)",
+			   batch_id, tncs->last_batchid + 1);
+		os_free(buf);
+		return TNCCS_PROCESS_ERROR;
+	}
+	tncs->last_batchid = batch_id;
+
+	while (*pos != '\0' && *pos != '>')
+		pos++;
+	if (*pos == '\0') {
+		os_free(buf);
+		return TNCCS_PROCESS_ERROR;
+	}
+	pos++;
+	payload = start;
+
+	/*
+	 * <IMC-IMV-Message>
+	 * <Type>01234567</Type>
+	 * <Base64>foo==</Base64>
+	 * </IMC-IMV-Message>
+	 */
+
+	while (*start) {
+		char *endpos;
+		unsigned int type;
+
+		pos = os_strstr(start, "<IMC-IMV-Message>");
+		if (pos == NULL)
+			break;
+		start = pos + 17;
+		end = os_strstr(start, "</IMC-IMV-Message>");
+		if (end == NULL)
+			break;
+		*end = '\0';
+		endpos = end;
+		end += 18;
+
+		if (tncs_get_type(start, &type) < 0) {
+			*endpos = '<';
+			start = end;
+			continue;
+		}
+		wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
+
+		decoded = tncs_get_base64(start, &decoded_len);
+		if (decoded == NULL) {
+			*endpos = '<';
+			start = end;
+			continue;
+		}
+
+		tncs_send_to_imvs(tncs, type, decoded, decoded_len);
+
+		os_free(decoded);
+
+		start = end;
+	}
+
+	/*
+	 * <TNCC-TNCS-Message>
+	 * <Type>01234567</Type>
+	 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
+	 * <Base64>foo==</Base64>
+	 * </TNCC-TNCS-Message>
+	 */
+
+	start = payload;
+	while (*start) {
+		unsigned int type;
+		char *xml, *xmlend, *endpos;
+
+		pos = os_strstr(start, "<TNCC-TNCS-Message>");
+		if (pos == NULL)
+			break;
+		start = pos + 19;
+		end = os_strstr(start, "</TNCC-TNCS-Message>");
+		if (end == NULL)
+			break;
+		*end = '\0';
+		endpos = end;
+		end += 20;
+
+		if (tncs_get_type(start, &type) < 0) {
+			*endpos = '<';
+			start = end;
+			continue;
+		}
+		wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
+			   type);
+
+		/* Base64 OR XML */
+		decoded = NULL;
+		xml = NULL;
+		xmlend = NULL;
+		pos = os_strstr(start, "<XML>");
+		if (pos) {
+			pos += 5;
+			pos2 = os_strstr(pos, "</XML>");
+			if (pos2 == NULL) {
+				*endpos = '<';
+				start = end;
+				continue;
+			}
+			xmlend = pos2;
+			xml = pos;
+		} else {
+			decoded = tncs_get_base64(start, &decoded_len);
+			if (decoded == NULL) {
+				*endpos = '<';
+				start = end;
+				continue;
+			}
+		}
+
+		if (decoded) {
+			wpa_hexdump_ascii(MSG_MSGDUMP,
+					  "TNC: TNCC-TNCS-Message Base64",
+					  decoded, decoded_len);
+			os_free(decoded);
+		}
+
+		if (xml) {
+			wpa_hexdump_ascii(MSG_MSGDUMP,
+					  "TNC: TNCC-TNCS-Message XML",
+					  (unsigned char *) xml,
+					  xmlend - xml);
+		}
+
+		start = end;
+	}
+
+	os_free(buf);
+
+	tncs_batch_ending(tncs);
+
+	if (tncs_total_send_len(tncs) == 0)
+		return tncs_derive_recommendation(tncs);
+
+	return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
+}
+
+
+static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
+					  int *error)
+{
+	struct tnc_if_imv *imv;
+	char *pos, *pos2;
+
+	if (id >= TNC_MAX_IMV_ID) {
+		wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
+		return NULL;
+	}
+
+	imv = os_zalloc(sizeof(*imv));
+	if (imv == NULL) {
+		*error = 1;
+		return NULL;
+	}
+
+	imv->imvID = id;
+
+	pos = start;
+	wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos);
+	if (pos + 1 >= end || *pos != '"') {
+		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
+			   "(no starting quotation mark)", start);
+		os_free(imv);
+		return NULL;
+	}
+
+	pos++;
+	pos2 = pos;
+	while (pos2 < end && *pos2 != '"')
+		pos2++;
+	if (pos2 >= end) {
+		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
+			   "(no ending quotation mark)", start);
+		os_free(imv);
+		return NULL;
+	}
+	*pos2 = '\0';
+	wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
+	imv->name = os_strdup(pos);
+
+	pos = pos2 + 1;
+	if (pos >= end || *pos != ' ') {
+		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
+			   "(no space after name)", start);
+		os_free(imv);
+		return NULL;
+	}
+
+	pos++;
+	wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
+	imv->path = os_strdup(pos);
+
+	return imv;
+}
+
+
+static int tncs_read_config(struct tncs_global *global)
+{
+	char *config, *end, *pos, *line_end;
+	size_t config_len;
+	struct tnc_if_imv *imv, *last;
+	int id = 0;
+
+	last = NULL;
+
+	config = os_readfile(TNC_CONFIG_FILE, &config_len);
+	if (config == NULL) {
+		wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
+			   "file '%s'", TNC_CONFIG_FILE);
+		return -1;
+	}
+
+	end = config + config_len;
+	for (pos = config; pos < end; pos = line_end + 1) {
+		line_end = pos;
+		while (*line_end != '\n' && *line_end != '\r' &&
+		       line_end < end)
+			line_end++;
+		*line_end = '\0';
+
+		if (os_strncmp(pos, "IMV ", 4) == 0) {
+			int error = 0;
+
+			imv = tncs_parse_imv(id++, pos + 4, line_end, &error);
+			if (error)
+				return -1;
+			if (imv) {
+				if (last == NULL)
+					global->imv = imv;
+				else
+					last->next = imv;
+				last = imv;
+			}
+		}
+	}
+
+	os_free(config);
+
+	return 0;
+}
+
+
+struct tncs_data * tncs_init(void)
+{
+	struct tncs_data *tncs;
+
+	if (tncs_global_data == NULL)
+		return NULL;
+
+	tncs = os_zalloc(sizeof(*tncs));
+	if (tncs == NULL)
+		return NULL;
+	tncs->imv = tncs_global_data->imv;
+	tncs->connectionID = tncs_global_data->next_conn_id++;
+	tncs->next = tncs_global_data->connections;
+	tncs_global_data->connections = tncs;
+
+	return tncs;
+}
+
+
+void tncs_deinit(struct tncs_data *tncs)
+{
+	int i;
+	struct tncs_data *prev, *conn;
+
+	if (tncs == NULL)
+		return;
+
+	for (i = 0; i < TNC_MAX_IMV_ID; i++)
+		os_free(tncs->imv_data[i].imv_send);
+
+	prev = NULL;
+	conn = tncs_global_data->connections;
+	while (conn) {
+		if (conn == tncs) {
+			if (prev)
+				prev->next = tncs->next;
+			else
+				tncs_global_data->connections = tncs->next;
+			break;
+		}
+		prev = conn;
+		conn = conn->next;
+	}
+
+	os_free(tncs->tncs_message);
+	os_free(tncs);
+}
+
+
+int tncs_global_init(void)
+{
+	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;
+
+	if (tncs_read_config(tncs_global_data) < 0) {
+		wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
+		goto failed;
+	}
+
+	for (imv = tncs_global_data->imv; imv; imv = imv->next) {
+		if (tncs_load_imv(imv)) {
+			wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'",
+				   imv->name);
+			goto failed;
+		}
+	}
+
+	return 0;
+
+failed:
+	tncs_global_deinit();
+	return -1;
+}
+
+
+void tncs_global_deinit(void)
+{
+	struct tnc_if_imv *imv, *prev;
+
+	if (tncs_global_data == NULL)
+		return;
+
+	imv = tncs_global_data->imv;
+	while (imv) {
+		tncs_unload_imv(imv);
+
+		prev = imv;
+		imv = imv->next;
+		os_free(prev);
+	}
+
+	os_free(tncs_global_data);
+	tncs_global_data = NULL;
+}
+
+
+struct wpabuf * tncs_build_soh_request(void)
+{
+	struct wpabuf *buf;
+
+	/*
+	 * Build a SoH Request TLV (to be used inside SoH EAP Extensions
+	 * Method)
+	 */
+
+	buf = wpabuf_alloc(8 + 4);
+	if (buf == NULL)
+		return NULL;
+
+	/* Vendor-Specific TLV (Microsoft) - SoH Request */
+	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
+	wpabuf_put_be16(buf, 8); /* Length */
+
+	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
+
+	wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
+	wpabuf_put_be16(buf, 0); /* Length */
+
+	return buf;
+}
+
+
+struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
+				 int *failure)
+{
+	wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
+	*failure = 0;
+
+	/* TODO: return MS-SoH Response TLV */
+
+	return NULL;
+}
diff --git a/hostap/src/eap_server/tncs.h b/hostap/src/eap_server/tncs.h
new file mode 100644
index 0000000..ac7251b
--- /dev/null
+++ b/hostap/src/eap_server/tncs.h
@@ -0,0 +1,43 @@
+/*
+ * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
+ * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TNCS_H
+#define TNCS_H
+
+struct tncs_data;
+
+struct tncs_data * tncs_init(void);
+void tncs_deinit(struct tncs_data *tncs);
+void tncs_init_connection(struct tncs_data *tncs);
+size_t tncs_total_send_len(struct tncs_data *tncs);
+u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos);
+char * tncs_if_tnccs_start(struct tncs_data *tncs);
+char * tncs_if_tnccs_end(void);
+
+enum tncs_process_res {
+	TNCCS_PROCESS_ERROR = -1,
+	TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0,
+	TNCCS_RECOMMENDATION_ERROR,
+	TNCCS_RECOMMENDATION_ALLOW,
+	TNCCS_RECOMMENDATION_NONE,
+	TNCCS_RECOMMENDATION_ISOLATE,
+	TNCCS_RECOMMENDATION_NO_ACCESS,
+	TNCCS_RECOMMENDATION_NO_RECOMMENDATION
+};
+
+enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
+					    const u8 *msg, size_t len);
+
+int tncs_global_init(void);
+void tncs_global_deinit(void);
+
+struct wpabuf * tncs_build_soh_request(void);
+struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
+				 int *failure);
+
+#endif /* TNCS_H */
diff --git a/hostap/src/eapol_auth/Makefile b/hostap/src/eapol_auth/Makefile
new file mode 100644
index 0000000..7b927a1
--- /dev/null
+++ b/hostap/src/eapol_auth/Makefile
@@ -0,0 +1,16 @@
+all: libeapol_auth.a
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeapol_auth.a
+
+install:
+	@echo Nothing to be made.
+
+include ../lib.rules
+
+LIB_OBJS = eapol_auth_sm.o eapol_auth_dump.o
+
+libeapol_auth.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/eapol_auth/eapol_auth_dump.c b/hostap/src/eapol_auth/eapol_auth_dump.c
new file mode 100644
index 0000000..5579582
--- /dev/null
+++ b/hostap/src/eapol_auth/eapol_auth_dump.c
@@ -0,0 +1,289 @@
+/*
+ * IEEE 802.1X-2004 Authenticator - State dump
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_server/eap.h"
+#include "eapol_auth_sm.h"
+#include "eapol_auth_sm_i.h"
+
+static inline const char * port_type_txt(PortTypes pt)
+{
+	switch (pt) {
+	case ForceUnauthorized: return "ForceUnauthorized";
+	case ForceAuthorized: return "ForceAuthorized";
+	case Auto: return "Auto";
+	default: return "Unknown";
+	}
+}
+
+
+static inline const char * port_state_txt(PortState ps)
+{
+	switch (ps) {
+	case Unauthorized: return "Unauthorized";
+	case Authorized: return "Authorized";
+	default: return "Unknown";
+	}
+}
+
+
+static inline const char * ctrl_dir_txt(ControlledDirection dir)
+{
+	switch (dir) {
+	case Both: return "Both";
+	case In: return "In";
+	default: return "Unknown";
+	}
+}
+
+
+static inline const char * auth_pae_state_txt(int s)
+{
+	switch (s) {
+	case AUTH_PAE_INITIALIZE: return "INITIALIZE";
+	case AUTH_PAE_DISCONNECTED: return "DISCONNECTED";
+	case AUTH_PAE_CONNECTING: return "CONNECTING";
+	case AUTH_PAE_AUTHENTICATING: return "AUTHENTICATING";
+	case AUTH_PAE_AUTHENTICATED: return "AUTHENTICATED";
+	case AUTH_PAE_ABORTING: return "ABORTING";
+	case AUTH_PAE_HELD: return "HELD";
+	case AUTH_PAE_FORCE_AUTH: return "FORCE_AUTH";
+	case AUTH_PAE_FORCE_UNAUTH: return "FORCE_UNAUTH";
+	case AUTH_PAE_RESTART: return "RESTART";
+	default: return "Unknown";
+	}
+}
+
+
+static inline const char * be_auth_state_txt(int s)
+{
+	switch (s) {
+	case BE_AUTH_REQUEST: return "REQUEST";
+	case BE_AUTH_RESPONSE: return "RESPONSE";
+	case BE_AUTH_SUCCESS: return "SUCCESS";
+	case BE_AUTH_FAIL: return "FAIL";
+	case BE_AUTH_TIMEOUT: return "TIMEOUT";
+	case BE_AUTH_IDLE: return "IDLE";
+	case BE_AUTH_INITIALIZE: return "INITIALIZE";
+	case BE_AUTH_IGNORE: return "IGNORE";
+	default: return "Unknown";
+	}
+}
+
+
+static inline const char * reauth_timer_state_txt(int s)
+{
+	switch (s) {
+	case REAUTH_TIMER_INITIALIZE: return "INITIALIZE";
+	case REAUTH_TIMER_REAUTHENTICATE: return "REAUTHENTICATE";
+	default: return "Unknown";
+	}
+}
+
+
+static inline const char * auth_key_tx_state_txt(int s)
+{
+	switch (s) {
+	case AUTH_KEY_TX_NO_KEY_TRANSMIT: return "NO_KEY_TRANSMIT";
+	case AUTH_KEY_TX_KEY_TRANSMIT: return "KEY_TRANSMIT";
+	default: return "Unknown";
+	}
+}
+
+
+static inline const char * key_rx_state_txt(int s)
+{
+	switch (s) {
+	case KEY_RX_NO_KEY_RECEIVE: return "NO_KEY_RECEIVE";
+	case KEY_RX_KEY_RECEIVE: return "KEY_RECEIVE";
+	default: return "Unknown";
+	}
+}
+
+
+static inline const char * ctrl_dir_state_txt(int s)
+{
+	switch (s) {
+	case CTRL_DIR_FORCE_BOTH: return "FORCE_BOTH";
+	case CTRL_DIR_IN_OR_BOTH: return "IN_OR_BOTH";
+	default: return "Unknown";
+	}
+}
+
+
+int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf,
+			  size_t buflen)
+{
+	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 (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+#define _SB(b) ((b) ? "TRUE" : "FALSE")
+	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 (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	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 (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	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 (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	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 (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	ret = os_snprintf(pos, end - pos,
+			  "auth_key_tx_state=%s\n",
+			  auth_key_tx_state_txt(sm->auth_key_tx_state));
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	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 (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	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 (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+#undef _SB
+
+	return pos - buf;
+}
diff --git a/hostap/src/eapol_auth/eapol_auth_sm.c b/hostap/src/eapol_auth/eapol_auth_sm.c
new file mode 100644
index 0000000..ff33d28
--- /dev/null
+++ b/hostap/src/eapol_auth/eapol_auth_sm.c
@@ -0,0 +1,1321 @@
+/*
+ * IEEE 802.1X-2004 Authenticator - EAPOL state machine
+ * Copyright (c) 2002-2015, 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 "eloop.h"
+#include "state_machine.h"
+#include "common/eapol_common.h"
+#include "eap_common/eap_defs.h"
+#include "eap_common/eap_common.h"
+#include "eap_server/eap.h"
+#include "eapol_auth_sm.h"
+#include "eapol_auth_sm_i.h"
+
+#define STATE_MACHINE_DATA struct eapol_state_machine
+#define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X"
+#define STATE_MACHINE_ADDR sm->addr
+
+static const struct eapol_callbacks eapol_cb;
+
+/* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */
+
+#define setPortAuthorized() \
+sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 1)
+#define setPortUnauthorized() \
+sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 0)
+
+/* procedures */
+#define txCannedFail() eapol_auth_tx_canned_eap(sm, 0)
+#define txCannedSuccess() eapol_auth_tx_canned_eap(sm, 1)
+#define txReq() eapol_auth_tx_req(sm)
+#define abortAuth() sm->eapol->cb.abort_auth(sm->eapol->conf.ctx, sm->sta)
+#define txKey() sm->eapol->cb.tx_key(sm->eapol->conf.ctx, sm->sta)
+#define processKey() do { } while (0)
+
+
+static void eapol_sm_step_run(struct eapol_state_machine *sm);
+static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx);
+static void eapol_auth_initialize(struct eapol_state_machine *sm);
+static void eapol_auth_conf_free(struct eapol_auth_config *conf);
+
+
+static void eapol_auth_logger(struct eapol_authenticator *eapol,
+			      const u8 *addr, eapol_logger_level level,
+			      const char *txt)
+{
+	if (eapol->cb.logger == NULL)
+		return;
+	eapol->cb.logger(eapol->conf.ctx, addr, level, txt);
+}
+
+
+static void eapol_auth_vlogger(struct eapol_authenticator *eapol,
+			       const u8 *addr, eapol_logger_level level,
+			       const char *fmt, ...)
+{
+	char *format;
+	int maxlen;
+	va_list ap;
+
+	if (eapol->cb.logger == NULL)
+		return;
+
+	maxlen = os_strlen(fmt) + 100;
+	format = os_malloc(maxlen);
+	if (!format)
+		return;
+
+	va_start(ap, fmt);
+	vsnprintf(format, maxlen, fmt, ap);
+	va_end(ap);
+
+	eapol_auth_logger(eapol, addr, level, format);
+
+	os_free(format);
+}
+
+
+static void eapol_auth_tx_canned_eap(struct eapol_state_machine *sm,
+				     int success)
+{
+	struct eap_hdr eap;
+
+	os_memset(&eap, 0, sizeof(eap));
+
+	eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE;
+	eap.identifier = ++sm->last_eap_id;
+	eap.length = host_to_be16(sizeof(eap));
+
+	eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG,
+			   "Sending canned EAP packet %s (identifier %d)",
+			   success ? "SUCCESS" : "FAILURE", eap.identifier);
+	sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta,
+				 IEEE802_1X_TYPE_EAP_PACKET,
+				 (u8 *) &eap, sizeof(eap));
+	sm->dot1xAuthEapolFramesTx++;
+}
+
+
+static void eapol_auth_tx_req(struct eapol_state_machine *sm)
+{
+	if (sm->eap_if->eapReqData == NULL ||
+	    wpabuf_len(sm->eap_if->eapReqData) < sizeof(struct eap_hdr)) {
+		eapol_auth_logger(sm->eapol, sm->addr,
+				  EAPOL_LOGGER_DEBUG,
+				  "TxReq called, but there is no EAP request "
+				  "from authentication server");
+		return;
+	}
+
+	if (sm->flags & EAPOL_SM_WAIT_START) {
+		wpa_printf(MSG_DEBUG, "EAPOL: Drop EAPOL TX to " MACSTR
+			   " while waiting for EAPOL-Start",
+			   MAC2STR(sm->addr));
+		return;
+	}
+
+	sm->last_eap_id = eap_get_id(sm->eap_if->eapReqData);
+	eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG,
+			   "Sending EAP Packet (identifier %d)",
+			   sm->last_eap_id);
+	sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta,
+				 IEEE802_1X_TYPE_EAP_PACKET,
+				 wpabuf_head(sm->eap_if->eapReqData),
+				 wpabuf_len(sm->eap_if->eapReqData));
+	sm->dot1xAuthEapolFramesTx++;
+	if (eap_get_type(sm->eap_if->eapReqData) == EAP_TYPE_IDENTITY)
+		sm->dot1xAuthEapolReqIdFramesTx++;
+	else
+		sm->dot1xAuthEapolReqFramesTx++;
+}
+
+
+/**
+ * eapol_port_timers_tick - Port Timers state machine
+ * @eloop_ctx: struct eapol_state_machine *
+ * @timeout_ctx: Not used
+ *
+ * This statemachine is implemented as a function that will be called
+ * once a second as a registered event loop timeout.
+ */
+static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
+{
+	struct eapol_state_machine *state = timeout_ctx;
+
+	if (state->aWhile > 0) {
+		state->aWhile--;
+		if (state->aWhile == 0) {
+			wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
+				   " - aWhile --> 0",
+				   MAC2STR(state->addr));
+		}
+	}
+
+	if (state->quietWhile > 0) {
+		state->quietWhile--;
+		if (state->quietWhile == 0) {
+			wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
+				   " - quietWhile --> 0",
+				   MAC2STR(state->addr));
+		}
+	}
+
+	if (state->reAuthWhen > 0) {
+		state->reAuthWhen--;
+		if (state->reAuthWhen == 0) {
+			wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
+				   " - reAuthWhen --> 0",
+				   MAC2STR(state->addr));
+		}
+	}
+
+	if (state->eap_if->retransWhile > 0) {
+		state->eap_if->retransWhile--;
+		if (state->eap_if->retransWhile == 0) {
+			wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
+				   " - (EAP) retransWhile --> 0",
+				   MAC2STR(state->addr));
+		}
+	}
+
+	eapol_sm_step_run(state);
+
+	eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state);
+}
+
+
+
+/* Authenticator PAE state machine */
+
+SM_STATE(AUTH_PAE, INITIALIZE)
+{
+	SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae);
+	sm->portMode = Auto;
+
+	/*
+	 * Clearing keyRun here is not specified in IEEE Std 802.1X-2004, but
+	 * it looks like this would be logical thing to do here since the
+	 * EAPOL-Key exchange is not possible in this state. It is possible to
+	 * get here on disconnection event without advancing to the
+	 * AUTHENTICATING state to clear keyRun before the IEEE 802.11 RSN
+	 * authenticator state machine runs and that may advance from
+	 * AUTHENTICATION2 to INITPMK if keyRun = TRUE has been left from the
+	 * last association. This can be avoided by clearing keyRun here.
+	 */
+	sm->keyRun = FALSE;
+}
+
+
+SM_STATE(AUTH_PAE, DISCONNECTED)
+{
+	int from_initialize = sm->auth_pae_state == AUTH_PAE_INITIALIZE;
+
+	if (sm->eapolLogoff) {
+		if (sm->auth_pae_state == AUTH_PAE_CONNECTING)
+			sm->authEapLogoffsWhileConnecting++;
+		else if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED)
+			sm->authAuthEapLogoffWhileAuthenticated++;
+	}
+
+	SM_ENTRY_MA(AUTH_PAE, DISCONNECTED, auth_pae);
+
+	sm->authPortStatus = Unauthorized;
+	setPortUnauthorized();
+	sm->reAuthCount = 0;
+	sm->eapolLogoff = FALSE;
+	if (!from_initialize) {
+		sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
+				       sm->flags & EAPOL_SM_PREAUTH,
+				       sm->remediation);
+	}
+}
+
+
+SM_STATE(AUTH_PAE, RESTART)
+{
+	if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) {
+		if (sm->reAuthenticate)
+			sm->authAuthReauthsWhileAuthenticated++;
+		if (sm->eapolStart)
+			sm->authAuthEapStartsWhileAuthenticated++;
+		if (sm->eapolLogoff)
+			sm->authAuthEapLogoffWhileAuthenticated++;
+	}
+
+	SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae);
+
+	sm->eap_if->eapRestart = TRUE;
+}
+
+
+SM_STATE(AUTH_PAE, CONNECTING)
+{
+	if (sm->auth_pae_state != AUTH_PAE_CONNECTING)
+		sm->authEntersConnecting++;
+
+	SM_ENTRY_MA(AUTH_PAE, CONNECTING, auth_pae);
+
+	sm->reAuthenticate = FALSE;
+	sm->reAuthCount++;
+}
+
+
+SM_STATE(AUTH_PAE, HELD)
+{
+	if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authFail)
+		sm->authAuthFailWhileAuthenticating++;
+
+	SM_ENTRY_MA(AUTH_PAE, HELD, auth_pae);
+
+	sm->authPortStatus = Unauthorized;
+	setPortUnauthorized();
+	sm->quietWhile = sm->quietPeriod;
+	sm->eapolLogoff = FALSE;
+
+	eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_WARNING,
+			   "authentication failed - EAP type: %d (%s)",
+			   sm->eap_type_authsrv,
+			   eap_server_get_name(0, sm->eap_type_authsrv));
+	if (sm->eap_type_authsrv != sm->eap_type_supp) {
+		eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO,
+				   "Supplicant used different EAP type: "
+				   "%d (%s)", sm->eap_type_supp,
+				   eap_server_get_name(0, sm->eap_type_supp));
+	}
+	sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
+			       sm->flags & EAPOL_SM_PREAUTH, sm->remediation);
+}
+
+
+SM_STATE(AUTH_PAE, AUTHENTICATED)
+{
+	char *extra = "";
+
+	if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess)
+		sm->authAuthSuccessesWhileAuthenticating++;
+							
+	SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae);
+
+	sm->authPortStatus = Authorized;
+	setPortAuthorized();
+	sm->reAuthCount = 0;
+	if (sm->flags & EAPOL_SM_PREAUTH)
+		extra = " (pre-authentication)";
+	else if (sm->flags & EAPOL_SM_FROM_PMKSA_CACHE)
+		extra = " (PMKSA cache)";
+	eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO,
+			   "authenticated - EAP type: %d (%s)%s",
+			   sm->eap_type_authsrv,
+			   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->remediation);
+}
+
+
+SM_STATE(AUTH_PAE, AUTHENTICATING)
+{
+	SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae);
+
+	sm->eapolStart = FALSE;
+	sm->authSuccess = FALSE;
+	sm->authFail = FALSE;
+	sm->authTimeout = FALSE;
+	sm->authStart = TRUE;
+	sm->keyRun = FALSE;
+	sm->keyDone = FALSE;
+}
+
+
+SM_STATE(AUTH_PAE, ABORTING)
+{
+	if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING) {
+		if (sm->authTimeout)
+			sm->authAuthTimeoutsWhileAuthenticating++;
+		if (sm->eapolStart)
+			sm->authAuthEapStartsWhileAuthenticating++;
+		if (sm->eapolLogoff)
+			sm->authAuthEapLogoffWhileAuthenticating++;
+	}
+
+	SM_ENTRY_MA(AUTH_PAE, ABORTING, auth_pae);
+
+	sm->authAbort = TRUE;
+	sm->keyRun = FALSE;
+	sm->keyDone = FALSE;
+}
+
+
+SM_STATE(AUTH_PAE, FORCE_AUTH)
+{
+	SM_ENTRY_MA(AUTH_PAE, FORCE_AUTH, auth_pae);
+
+	sm->authPortStatus = Authorized;
+	setPortAuthorized();
+	sm->portMode = ForceAuthorized;
+	sm->eapolStart = FALSE;
+	txCannedSuccess();
+}
+
+
+SM_STATE(AUTH_PAE, FORCE_UNAUTH)
+{
+	SM_ENTRY_MA(AUTH_PAE, FORCE_UNAUTH, auth_pae);
+
+	sm->authPortStatus = Unauthorized;
+	setPortUnauthorized();
+	sm->portMode = ForceUnauthorized;
+	sm->eapolStart = FALSE;
+	txCannedFail();
+}
+
+
+SM_STEP(AUTH_PAE)
+{
+	if ((sm->portControl == Auto && sm->portMode != sm->portControl) ||
+	    sm->initialize || !sm->eap_if->portEnabled)
+		SM_ENTER_GLOBAL(AUTH_PAE, INITIALIZE);
+	else if (sm->portControl == ForceAuthorized &&
+		 sm->portMode != sm->portControl &&
+		 !(sm->initialize || !sm->eap_if->portEnabled))
+		SM_ENTER_GLOBAL(AUTH_PAE, FORCE_AUTH);
+	else if (sm->portControl == ForceUnauthorized &&
+		 sm->portMode != sm->portControl &&
+		 !(sm->initialize || !sm->eap_if->portEnabled))
+		SM_ENTER_GLOBAL(AUTH_PAE, FORCE_UNAUTH);
+	else {
+		switch (sm->auth_pae_state) {
+		case AUTH_PAE_INITIALIZE:
+			SM_ENTER(AUTH_PAE, DISCONNECTED);
+			break;
+		case AUTH_PAE_DISCONNECTED:
+			SM_ENTER(AUTH_PAE, RESTART);
+			break;
+		case AUTH_PAE_RESTART:
+			if (!sm->eap_if->eapRestart)
+				SM_ENTER(AUTH_PAE, CONNECTING);
+			break;
+		case AUTH_PAE_HELD:
+			if (sm->quietWhile == 0)
+				SM_ENTER(AUTH_PAE, RESTART);
+			break;
+		case AUTH_PAE_CONNECTING:
+			if (sm->eapolLogoff || sm->reAuthCount > sm->reAuthMax)
+				SM_ENTER(AUTH_PAE, DISCONNECTED);
+			else if ((sm->eap_if->eapReq &&
+				  sm->reAuthCount <= sm->reAuthMax) ||
+				 sm->eap_if->eapSuccess || sm->eap_if->eapFail)
+				SM_ENTER(AUTH_PAE, AUTHENTICATING);
+			break;
+		case AUTH_PAE_AUTHENTICATED:
+			if (sm->eapolStart || sm->reAuthenticate)
+				SM_ENTER(AUTH_PAE, RESTART);
+			else if (sm->eapolLogoff || !sm->portValid)
+				SM_ENTER(AUTH_PAE, DISCONNECTED);
+			break;
+		case AUTH_PAE_AUTHENTICATING:
+			if (sm->authSuccess && sm->portValid)
+				SM_ENTER(AUTH_PAE, AUTHENTICATED);
+			else if (sm->authFail ||
+				 (sm->keyDone && !sm->portValid))
+				SM_ENTER(AUTH_PAE, HELD);
+			else if (sm->eapolStart || sm->eapolLogoff ||
+				 sm->authTimeout)
+				SM_ENTER(AUTH_PAE, ABORTING);
+			break;
+		case AUTH_PAE_ABORTING:
+			if (sm->eapolLogoff && !sm->authAbort)
+				SM_ENTER(AUTH_PAE, DISCONNECTED);
+			else if (!sm->eapolLogoff && !sm->authAbort)
+				SM_ENTER(AUTH_PAE, RESTART);
+			break;
+		case AUTH_PAE_FORCE_AUTH:
+			if (sm->eapolStart)
+				SM_ENTER(AUTH_PAE, FORCE_AUTH);
+			break;
+		case AUTH_PAE_FORCE_UNAUTH:
+			if (sm->eapolStart)
+				SM_ENTER(AUTH_PAE, FORCE_UNAUTH);
+			break;
+		}
+	}
+}
+
+
+
+/* Backend Authentication state machine */
+
+SM_STATE(BE_AUTH, INITIALIZE)
+{
+	SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth);
+
+	abortAuth();
+	sm->eap_if->eapNoReq = FALSE;
+	sm->authAbort = FALSE;
+}
+
+
+SM_STATE(BE_AUTH, REQUEST)
+{
+	SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth);
+
+	txReq();
+	sm->eap_if->eapReq = FALSE;
+	sm->backendOtherRequestsToSupplicant++;
+
+	/*
+	 * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but
+	 * it looks like this would be logical thing to do there since the old
+	 * EAP response would not be valid anymore after the new EAP request
+	 * was sent out.
+	 *
+	 * A race condition has been reported, in which hostapd ended up
+	 * sending out EAP-Response/Identity as a response to the first
+	 * EAP-Request from the main EAP method. This can be avoided by
+	 * clearing eapolEap here.
+	 */
+	sm->eapolEap = FALSE;
+}
+
+
+SM_STATE(BE_AUTH, RESPONSE)
+{
+	SM_ENTRY_MA(BE_AUTH, RESPONSE, be_auth);
+
+	sm->authTimeout = FALSE;
+	sm->eapolEap = FALSE;
+	sm->eap_if->eapNoReq = FALSE;
+	sm->aWhile = sm->serverTimeout;
+	sm->eap_if->eapResp = TRUE;
+	/* sendRespToServer(); */
+	sm->backendResponses++;
+}
+
+
+SM_STATE(BE_AUTH, SUCCESS)
+{
+	SM_ENTRY_MA(BE_AUTH, SUCCESS, be_auth);
+
+	txReq();
+	sm->authSuccess = TRUE;
+	sm->keyRun = TRUE;
+}
+
+
+SM_STATE(BE_AUTH, FAIL)
+{
+	SM_ENTRY_MA(BE_AUTH, FAIL, be_auth);
+
+	txReq();
+	sm->authFail = TRUE;
+}
+
+
+SM_STATE(BE_AUTH, TIMEOUT)
+{
+	SM_ENTRY_MA(BE_AUTH, TIMEOUT, be_auth);
+
+	sm->authTimeout = TRUE;
+}
+
+
+SM_STATE(BE_AUTH, IDLE)
+{
+	SM_ENTRY_MA(BE_AUTH, IDLE, be_auth);
+
+	sm->authStart = FALSE;
+}
+
+
+SM_STATE(BE_AUTH, IGNORE)
+{
+	SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth);
+
+	sm->eap_if->eapNoReq = FALSE;
+}
+
+
+SM_STEP(BE_AUTH)
+{
+	if (sm->portControl != Auto || sm->initialize || sm->authAbort) {
+		SM_ENTER_GLOBAL(BE_AUTH, INITIALIZE);
+		return;
+	}
+
+	switch (sm->be_auth_state) {
+	case BE_AUTH_INITIALIZE:
+		SM_ENTER(BE_AUTH, IDLE);
+		break;
+	case BE_AUTH_REQUEST:
+		if (sm->eapolEap)
+			SM_ENTER(BE_AUTH, RESPONSE);
+		else if (sm->eap_if->eapReq)
+			SM_ENTER(BE_AUTH, REQUEST);
+		else if (sm->eap_if->eapTimeout)
+			SM_ENTER(BE_AUTH, TIMEOUT);
+		break;
+	case BE_AUTH_RESPONSE:
+		if (sm->eap_if->eapNoReq)
+			SM_ENTER(BE_AUTH, IGNORE);
+		if (sm->eap_if->eapReq) {
+			sm->backendAccessChallenges++;
+			SM_ENTER(BE_AUTH, REQUEST);
+		} else if (sm->aWhile == 0)
+			SM_ENTER(BE_AUTH, TIMEOUT);
+		else if (sm->eap_if->eapFail) {
+			sm->backendAuthFails++;
+			SM_ENTER(BE_AUTH, FAIL);
+		} else if (sm->eap_if->eapSuccess) {
+			sm->backendAuthSuccesses++;
+			SM_ENTER(BE_AUTH, SUCCESS);
+		}
+		break;
+	case BE_AUTH_SUCCESS:
+		SM_ENTER(BE_AUTH, IDLE);
+		break;
+	case BE_AUTH_FAIL:
+		SM_ENTER(BE_AUTH, IDLE);
+		break;
+	case BE_AUTH_TIMEOUT:
+		SM_ENTER(BE_AUTH, IDLE);
+		break;
+	case BE_AUTH_IDLE:
+		if (sm->eap_if->eapFail && sm->authStart)
+			SM_ENTER(BE_AUTH, FAIL);
+		else if (sm->eap_if->eapReq && sm->authStart)
+			SM_ENTER(BE_AUTH, REQUEST);
+		else if (sm->eap_if->eapSuccess && sm->authStart)
+			SM_ENTER(BE_AUTH, SUCCESS);
+		break;
+	case BE_AUTH_IGNORE:
+		if (sm->eapolEap)
+			SM_ENTER(BE_AUTH, RESPONSE);
+		else if (sm->eap_if->eapReq)
+			SM_ENTER(BE_AUTH, REQUEST);
+		else if (sm->eap_if->eapTimeout)
+			SM_ENTER(BE_AUTH, TIMEOUT);
+		break;
+	}
+}
+
+
+
+/* Reauthentication Timer state machine */
+
+SM_STATE(REAUTH_TIMER, INITIALIZE)
+{
+	SM_ENTRY_MA(REAUTH_TIMER, INITIALIZE, reauth_timer);
+
+	sm->reAuthWhen = sm->reAuthPeriod;
+}
+
+
+SM_STATE(REAUTH_TIMER, REAUTHENTICATE)
+{
+	SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer);
+
+	sm->reAuthenticate = TRUE;
+	sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta,
+				  EAPOL_AUTH_REAUTHENTICATE);
+}
+
+
+SM_STEP(REAUTH_TIMER)
+{
+	if (sm->portControl != Auto || sm->initialize ||
+	    sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) {
+		SM_ENTER_GLOBAL(REAUTH_TIMER, INITIALIZE);
+		return;
+	}
+
+	switch (sm->reauth_timer_state) {
+	case REAUTH_TIMER_INITIALIZE:
+		if (sm->reAuthWhen == 0)
+			SM_ENTER(REAUTH_TIMER, REAUTHENTICATE);
+		break;
+	case REAUTH_TIMER_REAUTHENTICATE:
+		SM_ENTER(REAUTH_TIMER, INITIALIZE);
+		break;
+	}
+}
+
+
+
+/* Authenticator Key Transmit state machine */
+
+SM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT)
+{
+	SM_ENTRY_MA(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx);
+}
+
+
+SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT)
+{
+	SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx);
+
+	txKey();
+	sm->eap_if->eapKeyAvailable = FALSE;
+	sm->keyDone = TRUE;
+}
+
+
+SM_STEP(AUTH_KEY_TX)
+{
+	if (sm->initialize || sm->portControl != Auto) {
+		SM_ENTER_GLOBAL(AUTH_KEY_TX, NO_KEY_TRANSMIT);
+		return;
+	}
+
+	switch (sm->auth_key_tx_state) {
+	case AUTH_KEY_TX_NO_KEY_TRANSMIT:
+		if (sm->keyTxEnabled && sm->eap_if->eapKeyAvailable &&
+		    sm->keyRun && !(sm->flags & EAPOL_SM_USES_WPA))
+			SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT);
+		break;
+	case AUTH_KEY_TX_KEY_TRANSMIT:
+		if (!sm->keyTxEnabled || !sm->keyRun)
+			SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT);
+		else if (sm->eap_if->eapKeyAvailable)
+			SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT);
+		break;
+	}
+}
+
+
+
+/* Key Receive state machine */
+
+SM_STATE(KEY_RX, NO_KEY_RECEIVE)
+{
+	SM_ENTRY_MA(KEY_RX, NO_KEY_RECEIVE, key_rx);
+}
+
+
+SM_STATE(KEY_RX, KEY_RECEIVE)
+{
+	SM_ENTRY_MA(KEY_RX, KEY_RECEIVE, key_rx);
+
+	processKey();
+	sm->rxKey = FALSE;
+}
+
+
+SM_STEP(KEY_RX)
+{
+	if (sm->initialize || !sm->eap_if->portEnabled) {
+		SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
+		return;
+	}
+
+	switch (sm->key_rx_state) {
+	case KEY_RX_NO_KEY_RECEIVE:
+		if (sm->rxKey)
+			SM_ENTER(KEY_RX, KEY_RECEIVE);
+		break;
+	case KEY_RX_KEY_RECEIVE:
+		if (sm->rxKey)
+			SM_ENTER(KEY_RX, KEY_RECEIVE);
+		break;
+	}
+}
+
+
+
+/* Controlled Directions state machine */
+
+SM_STATE(CTRL_DIR, FORCE_BOTH)
+{
+	SM_ENTRY_MA(CTRL_DIR, FORCE_BOTH, ctrl_dir);
+	sm->operControlledDirections = Both;
+}
+
+
+SM_STATE(CTRL_DIR, IN_OR_BOTH)
+{
+	SM_ENTRY_MA(CTRL_DIR, IN_OR_BOTH, ctrl_dir);
+	sm->operControlledDirections = sm->adminControlledDirections;
+}
+
+
+SM_STEP(CTRL_DIR)
+{
+	if (sm->initialize) {
+		SM_ENTER_GLOBAL(CTRL_DIR, IN_OR_BOTH);
+		return;
+	}
+
+	switch (sm->ctrl_dir_state) {
+	case CTRL_DIR_FORCE_BOTH:
+		if (sm->eap_if->portEnabled && sm->operEdge)
+			SM_ENTER(CTRL_DIR, IN_OR_BOTH);
+		break;
+	case CTRL_DIR_IN_OR_BOTH:
+		if (sm->operControlledDirections !=
+		    sm->adminControlledDirections)
+			SM_ENTER(CTRL_DIR, IN_OR_BOTH);
+		if (!sm->eap_if->portEnabled || !sm->operEdge)
+			SM_ENTER(CTRL_DIR, FORCE_BOTH);
+		break;
+	}
+}
+
+
+
+struct eapol_state_machine *
+eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
+		 int flags, const struct wpabuf *assoc_wps_ie,
+		 const struct wpabuf *assoc_p2p_ie, void *sta_ctx,
+		 const char *identity, const char *radius_cui)
+{
+	struct eapol_state_machine *sm;
+	struct eap_config eap_conf;
+
+	if (eapol == NULL)
+		return NULL;
+
+	sm = os_zalloc(sizeof(*sm));
+	if (sm == NULL) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.1X state machine allocation "
+			   "failed");
+		return NULL;
+	}
+	sm->radius_identifier = -1;
+	os_memcpy(sm->addr, addr, ETH_ALEN);
+	sm->flags = flags;
+
+	sm->eapol = eapol;
+	sm->sta = sta_ctx;
+
+	/* Set default values for state machine constants */
+	sm->auth_pae_state = AUTH_PAE_INITIALIZE;
+	sm->quietPeriod = AUTH_PAE_DEFAULT_quietPeriod;
+	sm->reAuthMax = AUTH_PAE_DEFAULT_reAuthMax;
+
+	sm->be_auth_state = BE_AUTH_INITIALIZE;
+	sm->serverTimeout = BE_AUTH_DEFAULT_serverTimeout;
+
+	sm->reauth_timer_state = REAUTH_TIMER_INITIALIZE;
+	sm->reAuthPeriod = eapol->conf.eap_reauth_period;
+	sm->reAuthEnabled = eapol->conf.eap_reauth_period > 0 ? TRUE : FALSE;
+
+	sm->auth_key_tx_state = AUTH_KEY_TX_NO_KEY_TRANSMIT;
+
+	sm->key_rx_state = KEY_RX_NO_KEY_RECEIVE;
+
+	sm->ctrl_dir_state = CTRL_DIR_IN_OR_BOTH;
+
+	sm->portControl = Auto;
+
+	if (!eapol->conf.wpa &&
+	    (eapol->default_wep_key || eapol->conf.individual_wep_key_len > 0))
+		sm->keyTxEnabled = TRUE;
+	else
+		sm->keyTxEnabled = FALSE;
+	if (eapol->conf.wpa)
+		sm->portValid = FALSE;
+	else
+		sm->portValid = TRUE;
+
+	os_memset(&eap_conf, 0, sizeof(eap_conf));
+	eap_conf.eap_server = eapol->conf.eap_server;
+	eap_conf.ssl_ctx = eapol->conf.ssl_ctx;
+	eap_conf.msg_ctx = eapol->conf.msg_ctx;
+	eap_conf.eap_sim_db_priv = eapol->conf.eap_sim_db_priv;
+	eap_conf.pac_opaque_encr_key = eapol->conf.pac_opaque_encr_key;
+	eap_conf.eap_fast_a_id = eapol->conf.eap_fast_a_id;
+	eap_conf.eap_fast_a_id_len = eapol->conf.eap_fast_a_id_len;
+	eap_conf.eap_fast_a_id_info = eapol->conf.eap_fast_a_id_info;
+	eap_conf.eap_fast_prov = eapol->conf.eap_fast_prov;
+	eap_conf.pac_key_lifetime = eapol->conf.pac_key_lifetime;
+	eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time;
+	eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind;
+	eap_conf.tnc = eapol->conf.tnc;
+	eap_conf.wps = eapol->conf.wps;
+	eap_conf.assoc_wps_ie = assoc_wps_ie;
+	eap_conf.assoc_p2p_ie = assoc_p2p_ie;
+	eap_conf.peer_addr = addr;
+	eap_conf.fragment_size = eapol->conf.fragment_size;
+	eap_conf.pwd_group = eapol->conf.pwd_group;
+	eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1;
+	eap_conf.server_id = eapol->conf.server_id;
+	eap_conf.server_id_len = eapol->conf.server_id_len;
+	eap_conf.erp = eapol->conf.erp;
+	eap_conf.tls_session_lifetime = eapol->conf.tls_session_lifetime;
+	sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
+	if (sm->eap == NULL) {
+		eapol_auth_free(sm);
+		return NULL;
+	}
+	sm->eap_if = eap_get_interface(sm->eap);
+
+	eapol_auth_initialize(sm);
+
+	if (identity) {
+		sm->identity = (u8 *) os_strdup(identity);
+		if (sm->identity)
+			sm->identity_len = os_strlen(identity);
+	}
+	if (radius_cui)
+		sm->radius_cui = wpabuf_alloc_copy(radius_cui,
+						   os_strlen(radius_cui));
+
+	sm->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo++;
+	if (eapol->acct_multi_session_id_lo == 0)
+		eapol->acct_multi_session_id_hi++;
+	sm->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi;
+
+	return sm;
+}
+
+
+void eapol_auth_free(struct eapol_state_machine *sm)
+{
+	if (sm == NULL)
+		return;
+
+	eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
+	eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL);
+	if (sm->eap)
+		eap_server_sm_deinit(sm->eap);
+	os_free(sm);
+}
+
+
+static int eapol_sm_sta_entry_alive(struct eapol_authenticator *eapol,
+				    const u8 *addr)
+{
+	return eapol->cb.sta_entry_alive(eapol->conf.ctx, addr);
+}
+
+
+static void eapol_sm_step_run(struct eapol_state_machine *sm)
+{
+	struct eapol_authenticator *eapol = sm->eapol;
+	u8 addr[ETH_ALEN];
+	unsigned int prev_auth_pae, prev_be_auth, prev_reauth_timer,
+		prev_auth_key_tx, prev_key_rx, prev_ctrl_dir;
+	int max_steps = 100;
+
+	os_memcpy(addr, sm->addr, ETH_ALEN);
+
+	/*
+	 * Allow EAPOL state machines to run as long as there are state
+	 * changes, but exit and return here through event loop if more than
+	 * 100 steps is needed as a precaution against infinite loops inside
+	 * eloop callback.
+	 */
+restart:
+	prev_auth_pae = sm->auth_pae_state;
+	prev_be_auth = sm->be_auth_state;
+	prev_reauth_timer = sm->reauth_timer_state;
+	prev_auth_key_tx = sm->auth_key_tx_state;
+	prev_key_rx = sm->key_rx_state;
+	prev_ctrl_dir = sm->ctrl_dir_state;
+
+	SM_STEP_RUN(AUTH_PAE);
+	if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
+		SM_STEP_RUN(BE_AUTH);
+	if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
+		SM_STEP_RUN(REAUTH_TIMER);
+	if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
+		SM_STEP_RUN(AUTH_KEY_TX);
+	if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
+		SM_STEP_RUN(KEY_RX);
+	if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
+		SM_STEP_RUN(CTRL_DIR);
+
+	if (prev_auth_pae != sm->auth_pae_state ||
+	    prev_be_auth != sm->be_auth_state ||
+	    prev_reauth_timer != sm->reauth_timer_state ||
+	    prev_auth_key_tx != sm->auth_key_tx_state ||
+	    prev_key_rx != sm->key_rx_state ||
+	    prev_ctrl_dir != sm->ctrl_dir_state) {
+		if (--max_steps > 0)
+			goto restart;
+		/* Re-run from eloop timeout */
+		eapol_auth_step(sm);
+		return;
+	}
+
+	if (eapol_sm_sta_entry_alive(eapol, addr) && sm->eap) {
+		if (eap_server_sm_step(sm->eap)) {
+			if (--max_steps > 0)
+				goto restart;
+			/* Re-run from eloop timeout */
+			eapol_auth_step(sm);
+			return;
+		}
+
+		/* TODO: find a better location for this */
+		if (sm->eap_if->aaaEapResp) {
+			sm->eap_if->aaaEapResp = FALSE;
+			if (sm->eap_if->aaaEapRespData == NULL) {
+				wpa_printf(MSG_DEBUG, "EAPOL: aaaEapResp set, "
+					   "but no aaaEapRespData available");
+				return;
+			}
+			sm->eapol->cb.aaa_send(
+				sm->eapol->conf.ctx, sm->sta,
+				wpabuf_head(sm->eap_if->aaaEapRespData),
+				wpabuf_len(sm->eap_if->aaaEapRespData));
+		}
+	}
+
+	if (eapol_sm_sta_entry_alive(eapol, addr))
+		sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta,
+					  EAPOL_AUTH_SM_CHANGE);
+}
+
+
+static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx)
+{
+	struct eapol_state_machine *sm = eloop_ctx;
+	eapol_sm_step_run(sm);
+}
+
+
+/**
+ * eapol_auth_step - Advance EAPOL state machines
+ * @sm: EAPOL state machine
+ *
+ * This function is called to advance EAPOL state machines after any change
+ * that could affect their state.
+ */
+void eapol_auth_step(struct eapol_state_machine *sm)
+{
+	/*
+	 * Run eapol_sm_step_run from a registered timeout to make sure that
+	 * other possible timeouts/events are processed and to avoid long
+	 * function call chains.
+	 */
+
+	eloop_register_timeout(0, 0, eapol_sm_step_cb, sm, NULL);
+}
+
+
+static void eapol_auth_initialize(struct eapol_state_machine *sm)
+{
+	sm->initializing = TRUE;
+	/* Initialize the state machines by asserting initialize and then
+	 * deasserting it after one step */
+	sm->initialize = TRUE;
+	eapol_sm_step_run(sm);
+	sm->initialize = FALSE;
+	eapol_sm_step_run(sm);
+	sm->initializing = FALSE;
+
+	/* Start one second tick for port timers state machine */
+	eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
+	eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+}
+
+
+static int eapol_sm_get_eap_user(void *ctx, const u8 *identity,
+				 size_t identity_len, int phase2,
+				 struct eap_user *user)
+{
+	struct eapol_state_machine *sm = ctx;
+	int ret;
+
+	ret = sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
+					 identity_len, phase2, user);
+	if (user->remediation)
+		sm->remediation = 1;
+	return ret;
+}
+
+
+static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len)
+{
+	struct eapol_state_machine *sm = ctx;
+	*len = sm->eapol->conf.eap_req_id_text_len;
+	return sm->eapol->conf.eap_req_id_text;
+}
+
+
+static int eapol_sm_get_erp_send_reauth_start(void *ctx)
+{
+	struct eapol_state_machine *sm = ctx;
+	return sm->eapol->conf.erp_send_reauth_start;
+}
+
+
+static const char * eapol_sm_get_erp_domain(void *ctx)
+{
+	struct eapol_state_machine *sm = ctx;
+	return sm->eapol->conf.erp_domain;
+}
+
+
+static struct eap_server_erp_key * eapol_sm_erp_get_key(void *ctx,
+							const char *keyname)
+{
+	struct eapol_state_machine *sm = ctx;
+	return sm->eapol->cb.erp_get_key(sm->eapol->conf.ctx, keyname);
+}
+
+
+static int eapol_sm_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
+{
+	struct eapol_state_machine *sm = ctx;
+	return sm->eapol->cb.erp_add_key(sm->eapol->conf.ctx, erp);
+}
+
+
+static const struct eapol_callbacks eapol_cb =
+{
+	eapol_sm_get_eap_user,
+	eapol_sm_get_eap_req_id_text,
+	NULL,
+	eapol_sm_get_erp_send_reauth_start,
+	eapol_sm_get_erp_domain,
+	eapol_sm_erp_get_key,
+	eapol_sm_erp_add_key,
+};
+
+
+int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx)
+{
+	if (sm == NULL || ctx == NULL || ctx != sm->eap)
+		return -1;
+
+	eap_sm_pending_cb(sm->eap);
+	eapol_auth_step(sm);
+
+	return 0;
+}
+
+
+void eapol_auth_reauthenticate(struct eapol_state_machine *sm)
+{
+	wpa_printf(MSG_DEBUG, "EAPOL: External reauthentication trigger for "
+		   MACSTR, MAC2STR(sm->addr));
+	sm->reAuthenticate = TRUE;
+	eapol_auth_step(sm);
+}
+
+
+int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param,
+			const char *value)
+{
+	wpa_printf(MSG_DEBUG, "EAPOL: External configuration operation for "
+		   MACSTR " - param=%s value=%s",
+		   MAC2STR(sm->addr), param, value);
+
+	if (os_strcasecmp(param, "AdminControlledDirections") == 0) {
+		if (os_strcmp(value, "Both") == 0)
+			sm->adminControlledDirections = Both;
+		else if (os_strcmp(value, "In") == 0)
+			sm->adminControlledDirections = In;
+		else
+			return -1;
+		eapol_auth_step(sm);
+		return 0;
+	}
+
+	if (os_strcasecmp(param, "AdminControlledPortControl") == 0) {
+		if (os_strcmp(value, "ForceAuthorized") == 0)
+			sm->portControl = ForceAuthorized;
+		else if (os_strcmp(value, "ForceUnauthorized") == 0)
+			sm->portControl = ForceUnauthorized;
+		else if (os_strcmp(value, "Auto") == 0)
+			sm->portControl = Auto;
+		else
+			return -1;
+		eapol_auth_step(sm);
+		return 0;
+	}
+
+	if (os_strcasecmp(param, "quietPeriod") == 0) {
+		sm->quietPeriod = atoi(value);
+		return 0;
+	}
+
+	if (os_strcasecmp(param, "serverTimeout") == 0) {
+		sm->serverTimeout = atoi(value);
+		return 0;
+	}
+
+	if (os_strcasecmp(param, "reAuthPeriod") == 0) {
+		sm->reAuthPeriod = atoi(value);
+		return 0;
+	}
+
+	if (os_strcasecmp(param, "reAuthEnabled") == 0) {
+		if (os_strcmp(value, "TRUE") == 0)
+			sm->reAuthEnabled = TRUE;
+		else if (os_strcmp(value, "FALSE") == 0)
+			sm->reAuthEnabled = FALSE;
+		else
+			return -1;
+		eapol_auth_step(sm);
+		return 0;
+	}
+
+	if (os_strcasecmp(param, "KeyTransmissionEnabled") == 0) {
+		if (os_strcmp(value, "TRUE") == 0)
+			sm->keyTxEnabled = TRUE;
+		else if (os_strcmp(value, "FALSE") == 0)
+			sm->keyTxEnabled = FALSE;
+		else
+			return -1;
+		eapol_auth_step(sm);
+		return 0;
+	}
+
+	return -1;
+}
+
+
+static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
+				 struct eapol_auth_config *src)
+{
+	dst->ctx = src->ctx;
+	dst->eap_reauth_period = src->eap_reauth_period;
+	dst->wpa = src->wpa;
+	dst->individual_wep_key_len = src->individual_wep_key_len;
+	dst->eap_server = src->eap_server;
+	dst->ssl_ctx = src->ssl_ctx;
+	dst->msg_ctx = src->msg_ctx;
+	dst->eap_sim_db_priv = src->eap_sim_db_priv;
+	os_free(dst->eap_req_id_text);
+	dst->pwd_group = src->pwd_group;
+	dst->pbc_in_m1 = src->pbc_in_m1;
+	dst->server_id = src->server_id;
+	dst->server_id_len = src->server_id_len;
+	if (src->eap_req_id_text) {
+		dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len);
+		if (dst->eap_req_id_text == NULL)
+			return -1;
+		os_memcpy(dst->eap_req_id_text, src->eap_req_id_text,
+			  src->eap_req_id_text_len);
+		dst->eap_req_id_text_len = src->eap_req_id_text_len;
+	} else {
+		dst->eap_req_id_text = NULL;
+		dst->eap_req_id_text_len = 0;
+	}
+	if (src->pac_opaque_encr_key) {
+		dst->pac_opaque_encr_key = os_malloc(16);
+		if (dst->pac_opaque_encr_key == NULL)
+			goto fail;
+		os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key,
+			  16);
+	} else
+		dst->pac_opaque_encr_key = NULL;
+	if (src->eap_fast_a_id) {
+		dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len);
+		if (dst->eap_fast_a_id == NULL)
+			goto fail;
+		os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id,
+			  src->eap_fast_a_id_len);
+		dst->eap_fast_a_id_len = src->eap_fast_a_id_len;
+	} else
+		dst->eap_fast_a_id = NULL;
+	if (src->eap_fast_a_id_info) {
+		dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info);
+		if (dst->eap_fast_a_id_info == NULL)
+			goto fail;
+	} else
+		dst->eap_fast_a_id_info = NULL;
+	dst->eap_fast_prov = src->eap_fast_prov;
+	dst->pac_key_lifetime = src->pac_key_lifetime;
+	dst->pac_key_refresh_time = src->pac_key_refresh_time;
+	dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind;
+	dst->tnc = src->tnc;
+	dst->wps = src->wps;
+	dst->fragment_size = src->fragment_size;
+
+	os_free(dst->erp_domain);
+	if (src->erp_domain) {
+		dst->erp_domain = os_strdup(src->erp_domain);
+		if (dst->erp_domain == NULL)
+			goto fail;
+	} else {
+		dst->erp_domain = NULL;
+	}
+	dst->erp_send_reauth_start = src->erp_send_reauth_start;
+	dst->erp = src->erp;
+	dst->tls_session_lifetime = src->tls_session_lifetime;
+
+	return 0;
+
+fail:
+	eapol_auth_conf_free(dst);
+	return -1;
+}
+
+
+static void eapol_auth_conf_free(struct eapol_auth_config *conf)
+{
+	os_free(conf->eap_req_id_text);
+	conf->eap_req_id_text = NULL;
+	os_free(conf->pac_opaque_encr_key);
+	conf->pac_opaque_encr_key = NULL;
+	os_free(conf->eap_fast_a_id);
+	conf->eap_fast_a_id = NULL;
+	os_free(conf->eap_fast_a_id_info);
+	conf->eap_fast_a_id_info = NULL;
+	os_free(conf->erp_domain);
+	conf->erp_domain = NULL;
+}
+
+
+struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf,
+					     struct eapol_auth_cb *cb)
+{
+	struct eapol_authenticator *eapol;
+	struct os_time now;
+
+	eapol = os_zalloc(sizeof(*eapol));
+	if (eapol == NULL)
+		return NULL;
+
+	if (eapol_auth_conf_clone(&eapol->conf, conf) < 0) {
+		os_free(eapol);
+		return NULL;
+	}
+
+	if (conf->individual_wep_key_len > 0) {
+		/* use key0 in individual key and key1 in broadcast key */
+		eapol->default_wep_key_idx = 1;
+	}
+
+	eapol->cb.eapol_send = cb->eapol_send;
+	eapol->cb.aaa_send = cb->aaa_send;
+	eapol->cb.finished = cb->finished;
+	eapol->cb.get_eap_user = cb->get_eap_user;
+	eapol->cb.sta_entry_alive = cb->sta_entry_alive;
+	eapol->cb.logger = cb->logger;
+	eapol->cb.set_port_authorized = cb->set_port_authorized;
+	eapol->cb.abort_auth = cb->abort_auth;
+	eapol->cb.tx_key = cb->tx_key;
+	eapol->cb.eapol_event = cb->eapol_event;
+	eapol->cb.erp_get_key = cb->erp_get_key;
+	eapol->cb.erp_add_key = cb->erp_add_key;
+
+	/* Acct-Multi-Session-Id should be unique over reboots. If reliable
+	 * clock is not available, this could be replaced with reboot counter,
+	 * etc. */
+	os_get_time(&now);
+	eapol->acct_multi_session_id_hi = now.sec;
+
+	return eapol;
+}
+
+
+void eapol_auth_deinit(struct eapol_authenticator *eapol)
+{
+	if (eapol == NULL)
+		return;
+
+	eapol_auth_conf_free(&eapol->conf);
+	os_free(eapol->default_wep_key);
+	os_free(eapol);
+}
diff --git a/hostap/src/eapol_auth/eapol_auth_sm.h b/hostap/src/eapol_auth/eapol_auth_sm.h
new file mode 100644
index 0000000..e1974e4
--- /dev/null
+++ b/hostap/src/eapol_auth/eapol_auth_sm.h
@@ -0,0 +1,102 @@
+/*
+ * IEEE 802.1X-2004 Authenticator - EAPOL state machine
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAPOL_AUTH_SM_H
+#define EAPOL_AUTH_SM_H
+
+#define EAPOL_SM_PREAUTH BIT(0)
+#define EAPOL_SM_WAIT_START BIT(1)
+#define EAPOL_SM_USES_WPA BIT(2)
+#define EAPOL_SM_FROM_PMKSA_CACHE BIT(3)
+
+struct eapol_auth_config {
+	int eap_reauth_period;
+	int wpa;
+	int individual_wep_key_len;
+	int eap_server;
+	void *ssl_ctx;
+	void *msg_ctx;
+	void *eap_sim_db_priv;
+	char *eap_req_id_text; /* a copy of this will be allocated */
+	size_t eap_req_id_text_len;
+	int erp_send_reauth_start;
+	char *erp_domain; /* a copy of this will be allocated */
+	int erp; /* Whether ERP is enabled on authentication server */
+	unsigned int tls_session_lifetime;
+	u8 *pac_opaque_encr_key;
+	u8 *eap_fast_a_id;
+	size_t eap_fast_a_id_len;
+	char *eap_fast_a_id_info;
+	int eap_fast_prov;
+	int pac_key_lifetime;
+	int pac_key_refresh_time;
+	int eap_sim_aka_result_ind;
+	int tnc;
+	struct wps_context *wps;
+	int fragment_size;
+	u16 pwd_group;
+	int pbc_in_m1;
+	const u8 *server_id;
+	size_t server_id_len;
+
+	/* Opaque context pointer to owner data for callback functions */
+	void *ctx;
+};
+
+struct eap_user;
+struct eap_server_erp_key;
+
+typedef enum {
+	EAPOL_LOGGER_DEBUG, EAPOL_LOGGER_INFO, EAPOL_LOGGER_WARNING
+} eapol_logger_level;
+
+enum eapol_event {
+	EAPOL_AUTH_SM_CHANGE,
+	EAPOL_AUTH_REAUTHENTICATE
+};
+
+struct eapol_auth_cb {
+	void (*eapol_send)(void *ctx, void *sta_ctx, u8 type, const u8 *data,
+			   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,
+			 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);
+	void (*logger)(void *ctx, const u8 *addr, eapol_logger_level level,
+		       const char *txt);
+	void (*set_port_authorized)(void *ctx, void *sta_ctx, int authorized);
+	void (*abort_auth)(void *ctx, void *sta_ctx);
+	void (*tx_key)(void *ctx, void *sta_ctx);
+	void (*eapol_event)(void *ctx, void *sta_ctx, enum eapol_event type);
+	struct eap_server_erp_key * (*erp_get_key)(void *ctx,
+						   const char *keyname);
+	int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp);
+};
+
+
+struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf,
+					     struct eapol_auth_cb *cb);
+void eapol_auth_deinit(struct eapol_authenticator *eapol);
+struct eapol_state_machine *
+eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
+		 int flags, const struct wpabuf *assoc_wps_ie,
+		 const struct wpabuf *assoc_p2p_ie, void *sta_ctx,
+		 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);
+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);
+void eapol_auth_reauthenticate(struct eapol_state_machine *sm);
+int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param,
+			const char *value);
+
+#endif /* EAPOL_AUTH_SM_H */
diff --git a/hostap/src/eapol_auth/eapol_auth_sm_i.h b/hostap/src/eapol_auth/eapol_auth_sm_i.h
new file mode 100644
index 0000000..a29b49c
--- /dev/null
+++ b/hostap/src/eapol_auth/eapol_auth_sm_i.h
@@ -0,0 +1,186 @@
+/*
+ * IEEE 802.1X-2004 Authenticator - EAPOL state machine (internal definitions)
+ * 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 EAPOL_AUTH_SM_I_H
+#define EAPOL_AUTH_SM_I_H
+
+#include "common/defs.h"
+#include "radius/radius.h"
+
+/* IEEE Std 802.1X-2004, Ch. 8.2 */
+
+typedef enum { ForceUnauthorized = 1, ForceAuthorized = 3, Auto = 2 }
+	PortTypes;
+typedef enum { Unauthorized = 2, Authorized = 1 } PortState;
+typedef enum { Both = 0, In = 1 } ControlledDirection;
+typedef unsigned int Counter;
+
+
+/**
+ * struct eapol_authenticator - Global EAPOL authenticator data
+ */
+struct eapol_authenticator {
+	struct eapol_auth_config conf;
+	struct eapol_auth_cb cb;
+
+	u8 *default_wep_key;
+	u8 default_wep_key_idx;
+
+	u32 acct_multi_session_id_hi;
+	u32 acct_multi_session_id_lo;
+};
+
+
+/**
+ * struct eapol_state_machine - Per-Supplicant Authenticator state machines
+ */
+struct eapol_state_machine {
+	/* timers */
+	int aWhile;
+	int quietWhile;
+	int reAuthWhen;
+
+	/* global variables */
+	Boolean authAbort;
+	Boolean authFail;
+	PortState authPortStatus;
+	Boolean authStart;
+	Boolean authTimeout;
+	Boolean authSuccess;
+	Boolean eapolEap;
+	Boolean initialize;
+	Boolean keyDone;
+	Boolean keyRun;
+	Boolean keyTxEnabled;
+	PortTypes portControl;
+	Boolean portValid;
+	Boolean reAuthenticate;
+
+	/* Port Timers state machine */
+	/* 'Boolean tick' implicitly handled as registered timeout */
+
+	/* Authenticator PAE state machine */
+	enum { AUTH_PAE_INITIALIZE, AUTH_PAE_DISCONNECTED, AUTH_PAE_CONNECTING,
+	       AUTH_PAE_AUTHENTICATING, AUTH_PAE_AUTHENTICATED,
+	       AUTH_PAE_ABORTING, AUTH_PAE_HELD, AUTH_PAE_FORCE_AUTH,
+	       AUTH_PAE_FORCE_UNAUTH, AUTH_PAE_RESTART } auth_pae_state;
+	/* variables */
+	Boolean eapolLogoff;
+	Boolean eapolStart;
+	PortTypes portMode;
+	unsigned int reAuthCount;
+	/* constants */
+	unsigned int quietPeriod; /* default 60; 0..65535 */
+#define AUTH_PAE_DEFAULT_quietPeriod 60
+	unsigned int reAuthMax; /* default 2 */
+#define AUTH_PAE_DEFAULT_reAuthMax 2
+	/* counters */
+	Counter authEntersConnecting;
+	Counter authEapLogoffsWhileConnecting;
+	Counter authEntersAuthenticating;
+	Counter authAuthSuccessesWhileAuthenticating;
+	Counter authAuthTimeoutsWhileAuthenticating;
+	Counter authAuthFailWhileAuthenticating;
+	Counter authAuthEapStartsWhileAuthenticating;
+	Counter authAuthEapLogoffWhileAuthenticating;
+	Counter authAuthReauthsWhileAuthenticated;
+	Counter authAuthEapStartsWhileAuthenticated;
+	Counter authAuthEapLogoffWhileAuthenticated;
+
+	/* Backend Authentication state machine */
+	enum { BE_AUTH_REQUEST, BE_AUTH_RESPONSE, BE_AUTH_SUCCESS,
+	       BE_AUTH_FAIL, BE_AUTH_TIMEOUT, BE_AUTH_IDLE, BE_AUTH_INITIALIZE,
+	       BE_AUTH_IGNORE
+	} be_auth_state;
+	/* constants */
+	unsigned int serverTimeout; /* default 30; 1..X */
+#define BE_AUTH_DEFAULT_serverTimeout 30
+	/* counters */
+	Counter backendResponses;
+	Counter backendAccessChallenges;
+	Counter backendOtherRequestsToSupplicant;
+	Counter backendAuthSuccesses;
+	Counter backendAuthFails;
+
+	/* Reauthentication Timer state machine */
+	enum { REAUTH_TIMER_INITIALIZE, REAUTH_TIMER_REAUTHENTICATE
+	} reauth_timer_state;
+	/* constants */
+	unsigned int reAuthPeriod; /* default 3600 s */
+	Boolean reAuthEnabled;
+
+	/* Authenticator Key Transmit state machine */
+	enum { AUTH_KEY_TX_NO_KEY_TRANSMIT, AUTH_KEY_TX_KEY_TRANSMIT
+	} auth_key_tx_state;
+
+	/* Key Receive state machine */
+	enum { KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE } key_rx_state;
+	/* variables */
+	Boolean rxKey;
+
+	/* Controlled Directions state machine */
+	enum { CTRL_DIR_FORCE_BOTH, CTRL_DIR_IN_OR_BOTH } ctrl_dir_state;
+	/* variables */
+	ControlledDirection adminControlledDirections;
+	ControlledDirection operControlledDirections;
+	Boolean operEdge;
+
+	/* Authenticator Statistics Table */
+	Counter dot1xAuthEapolFramesRx;
+	Counter dot1xAuthEapolFramesTx;
+	Counter dot1xAuthEapolStartFramesRx;
+	Counter dot1xAuthEapolLogoffFramesRx;
+	Counter dot1xAuthEapolRespIdFramesRx;
+	Counter dot1xAuthEapolRespFramesRx;
+	Counter dot1xAuthEapolReqIdFramesTx;
+	Counter dot1xAuthEapolReqFramesTx;
+	Counter dot1xAuthInvalidEapolFramesRx;
+	Counter dot1xAuthEapLengthErrorFramesRx;
+	Counter dot1xAuthLastEapolFrameVersion;
+
+	/* Other variables - not defined in IEEE 802.1X */
+	u8 addr[ETH_ALEN]; /* Supplicant address */
+	int flags; /* EAPOL_SM_* */
+
+	/* EAPOL/AAA <-> EAP full authenticator interface */
+	struct eap_eapol_interface *eap_if;
+
+	int radius_identifier;
+	/* TODO: check when the last messages can be released */
+	struct radius_msg *last_recv_radius;
+	u8 last_eap_id; /* last used EAP Identifier */
+	u8 *identity;
+	size_t identity_len;
+	u8 eap_type_authsrv; /* EAP type of the last EAP packet from
+			      * Authentication server */
+	u8 eap_type_supp; /* EAP type of the last EAP packet from Supplicant */
+	struct radius_class_data radius_class;
+	struct wpabuf *radius_cui; /* Chargeable-User-Identity */
+
+	/* Keys for encrypting and signing EAPOL-Key frames */
+	u8 *eapol_key_sign;
+	size_t eapol_key_sign_len;
+	u8 *eapol_key_crypt;
+	size_t eapol_key_crypt_len;
+
+	struct eap_sm *eap;
+
+	Boolean initializing; /* in process of initializing state machines */
+	Boolean changed;
+
+	struct eapol_authenticator *eapol;
+
+	void *sta; /* station context pointer to use in callbacks */
+
+	int remediation;
+
+	u32 acct_multi_session_id_hi;
+	u32 acct_multi_session_id_lo;
+};
+
+#endif /* EAPOL_AUTH_SM_I_H */
diff --git a/hostap/src/eapol_supp/Makefile b/hostap/src/eapol_supp/Makefile
new file mode 100644
index 0000000..80db9d4
--- /dev/null
+++ b/hostap/src/eapol_supp/Makefile
@@ -0,0 +1,18 @@
+all: libeapol_supp.a
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeapol_supp.a
+
+install:
+	@echo Nothing to be made.
+
+include ../lib.rules
+
+CFLAGS += -DIEEE8021X_EAPOL
+
+LIB_OBJS = eapol_supp_sm.o
+
+libeapol_supp.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/eapol_supp/eapol_supp_sm.c b/hostap/src/eapol_supp/eapol_supp_sm.c
new file mode 100644
index 0000000..09cf4f6
--- /dev/null
+++ b/hostap/src/eapol_supp/eapol_supp_sm.c
@@ -0,0 +1,2150 @@
+/*
+ * EAPOL supplicant state machines
+ * Copyright (c) 2004-2012, 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 "state_machine.h"
+#include "wpabuf.h"
+#include "eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/md5.h"
+#include "common/eapol_common.h"
+#include "eap_peer/eap.h"
+#include "eap_peer/eap_proxy.h"
+#include "eapol_supp_sm.h"
+
+#define STATE_MACHINE_DATA struct eapol_sm
+#define STATE_MACHINE_DEBUG_PREFIX "EAPOL"
+
+
+/* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */
+
+/**
+ * struct eapol_sm - Internal data for EAPOL state machines
+ */
+struct eapol_sm {
+	/* Timers */
+	unsigned int authWhile;
+	unsigned int heldWhile;
+	unsigned int startWhen;
+	unsigned int idleWhile; /* for EAP state machine */
+	int timer_tick_enabled;
+
+	/* Global variables */
+	Boolean eapFail;
+	Boolean eapolEap;
+	Boolean eapSuccess;
+	Boolean initialize;
+	Boolean keyDone;
+	Boolean keyRun;
+	PortControl portControl;
+	Boolean portEnabled;
+	PortStatus suppPortStatus;  /* dot1xSuppControlledPortStatus */
+	Boolean portValid;
+	Boolean suppAbort;
+	Boolean suppFail;
+	Boolean suppStart;
+	Boolean suppSuccess;
+	Boolean suppTimeout;
+
+	/* Supplicant PAE state machine */
+	enum {
+		SUPP_PAE_UNKNOWN = 0,
+		SUPP_PAE_DISCONNECTED = 1,
+		SUPP_PAE_LOGOFF = 2,
+		SUPP_PAE_CONNECTING = 3,
+		SUPP_PAE_AUTHENTICATING = 4,
+		SUPP_PAE_AUTHENTICATED = 5,
+		/* unused(6) */
+		SUPP_PAE_HELD = 7,
+		SUPP_PAE_RESTART = 8,
+		SUPP_PAE_S_FORCE_AUTH = 9,
+		SUPP_PAE_S_FORCE_UNAUTH = 10
+	} SUPP_PAE_state; /* dot1xSuppPaeState */
+	/* Variables */
+	Boolean userLogoff;
+	Boolean logoffSent;
+	unsigned int startCount;
+	Boolean eapRestart;
+	PortControl sPortMode;
+	/* Constants */
+	unsigned int heldPeriod; /* dot1xSuppHeldPeriod */
+	unsigned int startPeriod; /* dot1xSuppStartPeriod */
+	unsigned int maxStart; /* dot1xSuppMaxStart */
+
+	/* Key Receive state machine */
+	enum {
+		KEY_RX_UNKNOWN = 0,
+		KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE
+	} KEY_RX_state;
+	/* Variables */
+	Boolean rxKey;
+
+	/* Supplicant Backend state machine */
+	enum {
+		SUPP_BE_UNKNOWN = 0,
+		SUPP_BE_INITIALIZE = 1,
+		SUPP_BE_IDLE = 2,
+		SUPP_BE_REQUEST = 3,
+		SUPP_BE_RECEIVE = 4,
+		SUPP_BE_RESPONSE = 5,
+		SUPP_BE_FAIL = 6,
+		SUPP_BE_TIMEOUT = 7, 
+		SUPP_BE_SUCCESS = 8
+	} SUPP_BE_state; /* dot1xSuppBackendPaeState */
+	/* Variables */
+	Boolean eapNoResp;
+	Boolean eapReq;
+	Boolean eapResp;
+	/* Constants */
+	unsigned int authPeriod; /* dot1xSuppAuthPeriod */
+
+	/* Statistics */
+	unsigned int dot1xSuppEapolFramesRx;
+	unsigned int dot1xSuppEapolFramesTx;
+	unsigned int dot1xSuppEapolStartFramesTx;
+	unsigned int dot1xSuppEapolLogoffFramesTx;
+	unsigned int dot1xSuppEapolRespFramesTx;
+	unsigned int dot1xSuppEapolReqIdFramesRx;
+	unsigned int dot1xSuppEapolReqFramesRx;
+	unsigned int dot1xSuppInvalidEapolFramesRx;
+	unsigned int dot1xSuppEapLengthErrorFramesRx;
+	unsigned int dot1xSuppLastEapolFrameVersion;
+	unsigned char dot1xSuppLastEapolFrameSource[6];
+
+	/* Miscellaneous variables (not defined in IEEE 802.1X-2004) */
+	Boolean changed;
+	struct eap_sm *eap;
+	struct eap_peer_config *config;
+	Boolean initial_req;
+	u8 *last_rx_key;
+	size_t last_rx_key_len;
+	struct wpabuf *eapReqData; /* for EAP */
+	Boolean altAccept; /* for EAP */
+	Boolean altReject; /* for EAP */
+	Boolean eapTriggerStart;
+	Boolean replay_counter_valid;
+	u8 last_replay_counter[16];
+	struct eapol_config conf;
+	struct eapol_ctx *ctx;
+	enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE }
+		cb_status;
+	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;
+#endif /* CONFIG_EAP_PROXY */
+};
+
+
+static void eapol_sm_txLogoff(struct eapol_sm *sm);
+static void eapol_sm_txStart(struct eapol_sm *sm);
+static void eapol_sm_processKey(struct eapol_sm *sm);
+static void eapol_sm_getSuppRsp(struct eapol_sm *sm);
+static void eapol_sm_txSuppRsp(struct eapol_sm *sm);
+static void eapol_sm_abortSupp(struct eapol_sm *sm);
+static void eapol_sm_abort_cached(struct eapol_sm *sm);
+static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx);
+static void eapol_sm_set_port_authorized(struct eapol_sm *sm);
+static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm);
+
+
+/* Port Timers state machine - implemented as a function that will be called
+ * once a second as a registered event loop timeout */
+static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
+{
+	struct eapol_sm *sm = timeout_ctx;
+
+	if (sm->authWhile > 0) {
+		sm->authWhile--;
+		if (sm->authWhile == 0)
+			wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0");
+	}
+	if (sm->heldWhile > 0) {
+		sm->heldWhile--;
+		if (sm->heldWhile == 0)
+			wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0");
+	}
+	if (sm->startWhen > 0) {
+		sm->startWhen--;
+		if (sm->startWhen == 0)
+			wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0");
+	}
+	if (sm->idleWhile > 0) {
+		sm->idleWhile--;
+		if (sm->idleWhile == 0)
+			wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0");
+	}
+
+	if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) {
+		eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx,
+				       sm);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick");
+		sm->timer_tick_enabled = 0;
+	}
+	eapol_sm_step(sm);
+}
+
+
+static void eapol_enable_timer_tick(struct eapol_sm *sm)
+{
+	if (sm->timer_tick_enabled)
+		return;
+	wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick");
+	sm->timer_tick_enabled = 1;
+	eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
+	eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+}
+
+
+SM_STATE(SUPP_PAE, LOGOFF)
+{
+	SM_ENTRY(SUPP_PAE, LOGOFF);
+	eapol_sm_txLogoff(sm);
+	sm->logoffSent = TRUE;
+	eapol_sm_set_port_unauthorized(sm);
+}
+
+
+SM_STATE(SUPP_PAE, DISCONNECTED)
+{
+	SM_ENTRY(SUPP_PAE, DISCONNECTED);
+	sm->sPortMode = Auto;
+	sm->startCount = 0;
+	sm->eapTriggerStart = FALSE;
+	sm->logoffSent = FALSE;
+	eapol_sm_set_port_unauthorized(sm);
+	sm->suppAbort = TRUE;
+
+	sm->unicast_key_received = FALSE;
+	sm->broadcast_key_received = FALSE;
+
+	/*
+	 * IEEE Std 802.1X-2004 does not clear heldWhile here, but doing so
+	 * allows the timer tick to be stopped more quickly when the port is
+	 * not enabled. Since this variable is used only within HELD state,
+	 * clearing it on initialization does not change actual state machine
+	 * behavior.
+	 */
+	sm->heldWhile = 0;
+}
+
+
+SM_STATE(SUPP_PAE, CONNECTING)
+{
+	int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING ||
+		sm->SUPP_PAE_state == SUPP_PAE_HELD;
+	SM_ENTRY(SUPP_PAE, CONNECTING);
+
+	if (sm->eapTriggerStart)
+		send_start = 1;
+	sm->eapTriggerStart = FALSE;
+
+	if (send_start) {
+		sm->startWhen = sm->startPeriod;
+		sm->startCount++;
+	} else {
+		/*
+		 * Do not send EAPOL-Start immediately since in most cases,
+		 * Authenticator is going to start authentication immediately
+		 * after association and an extra EAPOL-Start is just going to
+		 * delay authentication. Use a short timeout to send the first
+		 * EAPOL-Start if Authenticator does not start authentication.
+		 */
+		if (sm->conf.wps && !(sm->conf.wps & EAPOL_PEER_IS_WPS20_AP)) {
+			/* Reduce latency on starting WPS negotiation. */
+			wpa_printf(MSG_DEBUG,
+				   "EAPOL: Using shorter startWhen for WPS");
+			sm->startWhen = 1;
+		} else {
+			sm->startWhen = 2;
+		}
+	}
+	eapol_enable_timer_tick(sm);
+	sm->eapolEap = FALSE;
+	if (send_start)
+		eapol_sm_txStart(sm);
+}
+
+
+SM_STATE(SUPP_PAE, AUTHENTICATING)
+{
+	SM_ENTRY(SUPP_PAE, AUTHENTICATING);
+	sm->startCount = 0;
+	sm->suppSuccess = FALSE;
+	sm->suppFail = FALSE;
+	sm->suppTimeout = FALSE;
+	sm->keyRun = FALSE;
+	sm->keyDone = FALSE;
+	sm->suppStart = TRUE;
+}
+
+
+SM_STATE(SUPP_PAE, HELD)
+{
+	SM_ENTRY(SUPP_PAE, HELD);
+	sm->heldWhile = sm->heldPeriod;
+	eapol_enable_timer_tick(sm);
+	eapol_sm_set_port_unauthorized(sm);
+	sm->cb_status = EAPOL_CB_FAILURE;
+}
+
+
+SM_STATE(SUPP_PAE, AUTHENTICATED)
+{
+	SM_ENTRY(SUPP_PAE, AUTHENTICATED);
+	eapol_sm_set_port_authorized(sm);
+	sm->cb_status = EAPOL_CB_SUCCESS;
+}
+
+
+SM_STATE(SUPP_PAE, RESTART)
+{
+	SM_ENTRY(SUPP_PAE, RESTART);
+	sm->eapRestart = TRUE;
+}
+
+
+SM_STATE(SUPP_PAE, S_FORCE_AUTH)
+{
+	SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
+	eapol_sm_set_port_authorized(sm);
+	sm->sPortMode = ForceAuthorized;
+}
+
+
+SM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
+{
+	SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
+	eapol_sm_set_port_unauthorized(sm);
+	sm->sPortMode = ForceUnauthorized;
+	eapol_sm_txLogoff(sm);
+}
+
+
+SM_STEP(SUPP_PAE)
+{
+	if ((sm->userLogoff && !sm->logoffSent) &&
+	    !(sm->initialize || !sm->portEnabled))
+		SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF);
+	else if (((sm->portControl == Auto) &&
+		  (sm->sPortMode != sm->portControl)) ||
+		 sm->initialize || !sm->portEnabled)
+		SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED);
+	else if ((sm->portControl == ForceAuthorized) &&
+		 (sm->sPortMode != sm->portControl) &&
+		 !(sm->initialize || !sm->portEnabled))
+		SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH);
+	else if ((sm->portControl == ForceUnauthorized) &&
+		 (sm->sPortMode != sm->portControl) &&
+		 !(sm->initialize || !sm->portEnabled))
+		SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH);
+	else switch (sm->SUPP_PAE_state) {
+	case SUPP_PAE_UNKNOWN:
+		break;
+	case SUPP_PAE_LOGOFF:
+		if (!sm->userLogoff)
+			SM_ENTER(SUPP_PAE, DISCONNECTED);
+		break;
+	case SUPP_PAE_DISCONNECTED:
+		SM_ENTER(SUPP_PAE, CONNECTING);
+		break;
+	case SUPP_PAE_CONNECTING:
+		if (sm->startWhen == 0 && sm->startCount < sm->maxStart)
+			SM_ENTER(SUPP_PAE, CONNECTING);
+		else if (sm->startWhen == 0 &&
+			 sm->startCount >= sm->maxStart &&
+			 sm->portValid)
+			SM_ENTER(SUPP_PAE, AUTHENTICATED);
+		else if (sm->eapSuccess || sm->eapFail)
+			SM_ENTER(SUPP_PAE, AUTHENTICATING);
+		else if (sm->eapolEap)
+			SM_ENTER(SUPP_PAE, RESTART);
+		else if (sm->startWhen == 0 &&
+			 sm->startCount >= sm->maxStart &&
+			 !sm->portValid)
+			SM_ENTER(SUPP_PAE, HELD);
+		break;
+	case SUPP_PAE_AUTHENTICATING:
+		if (sm->eapSuccess && !sm->portValid &&
+		    sm->conf.accept_802_1x_keys &&
+		    sm->conf.required_keys == 0) {
+			wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for "
+				   "plaintext connection; no EAPOL-Key frames "
+				   "required");
+			sm->portValid = TRUE;
+			if (sm->ctx->eapol_done_cb)
+				sm->ctx->eapol_done_cb(sm->ctx->ctx);
+		}
+		if (sm->eapSuccess && sm->portValid)
+			SM_ENTER(SUPP_PAE, AUTHENTICATED);
+		else if (sm->eapFail || (sm->keyDone && !sm->portValid))
+			SM_ENTER(SUPP_PAE, HELD);
+		else if (sm->suppTimeout)
+			SM_ENTER(SUPP_PAE, CONNECTING);
+		else if (sm->eapTriggerStart)
+			SM_ENTER(SUPP_PAE, CONNECTING);
+		break;
+	case SUPP_PAE_HELD:
+		if (sm->heldWhile == 0)
+			SM_ENTER(SUPP_PAE, CONNECTING);
+		else if (sm->eapolEap)
+			SM_ENTER(SUPP_PAE, RESTART);
+		break;
+	case SUPP_PAE_AUTHENTICATED:
+		if (sm->eapolEap && sm->portValid)
+			SM_ENTER(SUPP_PAE, RESTART);
+		else if (!sm->portValid)
+			SM_ENTER(SUPP_PAE, DISCONNECTED);
+		break;
+	case SUPP_PAE_RESTART:
+		if (!sm->eapRestart)
+			SM_ENTER(SUPP_PAE, AUTHENTICATING);
+		break;
+	case SUPP_PAE_S_FORCE_AUTH:
+		break;
+	case SUPP_PAE_S_FORCE_UNAUTH:
+		break;
+	}
+}
+
+
+SM_STATE(KEY_RX, NO_KEY_RECEIVE)
+{
+	SM_ENTRY(KEY_RX, NO_KEY_RECEIVE);
+}
+
+
+SM_STATE(KEY_RX, KEY_RECEIVE)
+{
+	SM_ENTRY(KEY_RX, KEY_RECEIVE);
+	eapol_sm_processKey(sm);
+	sm->rxKey = FALSE;
+}
+
+
+SM_STEP(KEY_RX)
+{
+	if (sm->initialize || !sm->portEnabled)
+		SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
+	switch (sm->KEY_RX_state) {
+	case KEY_RX_UNKNOWN:
+		break;
+	case KEY_RX_NO_KEY_RECEIVE:
+		if (sm->rxKey)
+			SM_ENTER(KEY_RX, KEY_RECEIVE);
+		break;
+	case KEY_RX_KEY_RECEIVE:
+		if (sm->rxKey)
+			SM_ENTER(KEY_RX, KEY_RECEIVE);
+		break;
+	}
+}
+
+
+SM_STATE(SUPP_BE, REQUEST)
+{
+	SM_ENTRY(SUPP_BE, REQUEST);
+	sm->authWhile = 0;
+	sm->eapReq = TRUE;
+	eapol_sm_getSuppRsp(sm);
+}
+
+
+SM_STATE(SUPP_BE, RESPONSE)
+{
+	SM_ENTRY(SUPP_BE, RESPONSE);
+	eapol_sm_txSuppRsp(sm);
+	sm->eapResp = FALSE;
+}
+
+
+SM_STATE(SUPP_BE, SUCCESS)
+{
+	SM_ENTRY(SUPP_BE, SUCCESS);
+	sm->keyRun = TRUE;
+	sm->suppSuccess = TRUE;
+
+#ifdef CONFIG_EAP_PROXY
+	if (sm->use_eap_proxy) {
+		if (eap_proxy_key_available(sm->eap_proxy)) {
+			/* New key received - clear IEEE 802.1X EAPOL-Key replay
+			 * counter */
+			sm->replay_counter_valid = FALSE;
+		}
+		return;
+	}
+#endif /* CONFIG_EAP_PROXY */
+
+	if (eap_key_available(sm->eap)) {
+		/* New key received - clear IEEE 802.1X EAPOL-Key replay
+		 * counter */
+		sm->replay_counter_valid = FALSE;
+	}
+}
+
+
+SM_STATE(SUPP_BE, FAIL)
+{
+	SM_ENTRY(SUPP_BE, FAIL);
+	sm->suppFail = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, TIMEOUT)
+{
+	SM_ENTRY(SUPP_BE, TIMEOUT);
+	sm->suppTimeout = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, IDLE)
+{
+	SM_ENTRY(SUPP_BE, IDLE);
+	sm->suppStart = FALSE;
+	sm->initial_req = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, INITIALIZE)
+{
+	SM_ENTRY(SUPP_BE, INITIALIZE);
+	eapol_sm_abortSupp(sm);
+	sm->suppAbort = FALSE;
+
+	/*
+	 * IEEE Std 802.1X-2004 does not clear authWhile here, but doing so
+	 * allows the timer tick to be stopped more quickly when the port is
+	 * not enabled. Since this variable is used only within RECEIVE state,
+	 * clearing it on initialization does not change actual state machine
+	 * behavior.
+	 */
+	sm->authWhile = 0;
+}
+
+
+SM_STATE(SUPP_BE, RECEIVE)
+{
+	SM_ENTRY(SUPP_BE, RECEIVE);
+	sm->authWhile = sm->authPeriod;
+	eapol_enable_timer_tick(sm);
+	sm->eapolEap = FALSE;
+	sm->eapNoResp = FALSE;
+	sm->initial_req = FALSE;
+}
+
+
+SM_STEP(SUPP_BE)
+{
+	if (sm->initialize || sm->suppAbort)
+		SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE);
+	else switch (sm->SUPP_BE_state) {
+	case SUPP_BE_UNKNOWN:
+		break;
+	case SUPP_BE_REQUEST:
+		/*
+		 * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL
+		 * and SUCCESS based on eapFail and eapSuccess, respectively.
+		 * However, IEEE Std 802.1X-2004 is also specifying that
+		 * eapNoResp should be set in conjunction with eapSuccess and
+		 * eapFail which would mean that more than one of the
+		 * transitions here would be activated at the same time.
+		 * Skipping RESPONSE and/or RECEIVE states in these cases can
+		 * cause problems and the direct transitions to do not seem
+		 * correct. Because of this, the conditions for these
+		 * transitions are verified only after eapNoResp. They are
+		 * unlikely to be used since eapNoResp should always be set if
+		 * either of eapSuccess or eapFail is set.
+		 */
+		if (sm->eapResp && sm->eapNoResp) {
+			wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both "
+				   "eapResp and eapNoResp set?!");
+		}
+		if (sm->eapResp)
+			SM_ENTER(SUPP_BE, RESPONSE);
+		else if (sm->eapNoResp)
+			SM_ENTER(SUPP_BE, RECEIVE);
+		else if (sm->eapFail)
+			SM_ENTER(SUPP_BE, FAIL);
+		else if (sm->eapSuccess)
+			SM_ENTER(SUPP_BE, SUCCESS);
+		break;
+	case SUPP_BE_RESPONSE:
+		SM_ENTER(SUPP_BE, RECEIVE);
+		break;
+	case SUPP_BE_SUCCESS:
+		SM_ENTER(SUPP_BE, IDLE);
+		break;
+	case SUPP_BE_FAIL:
+		SM_ENTER(SUPP_BE, IDLE);
+		break;
+	case SUPP_BE_TIMEOUT:
+		SM_ENTER(SUPP_BE, IDLE);
+		break;
+	case SUPP_BE_IDLE:
+		if (sm->eapFail && sm->suppStart)
+			SM_ENTER(SUPP_BE, FAIL);
+		else if (sm->eapolEap && sm->suppStart)
+			SM_ENTER(SUPP_BE, REQUEST);
+		else if (sm->eapSuccess && sm->suppStart)
+			SM_ENTER(SUPP_BE, SUCCESS);
+		break;
+	case SUPP_BE_INITIALIZE:
+		SM_ENTER(SUPP_BE, IDLE);
+		break;
+	case SUPP_BE_RECEIVE:
+		if (sm->eapolEap)
+			SM_ENTER(SUPP_BE, REQUEST);
+		else if (sm->eapFail)
+			SM_ENTER(SUPP_BE, FAIL);
+		else if (sm->authWhile == 0)
+			SM_ENTER(SUPP_BE, TIMEOUT);
+		else if (sm->eapSuccess)
+			SM_ENTER(SUPP_BE, SUCCESS);
+		break;
+	}
+}
+
+
+static void eapol_sm_txLogoff(struct eapol_sm *sm)
+{
+	wpa_printf(MSG_DEBUG, "EAPOL: txLogoff");
+	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
+			    IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0);
+	sm->dot1xSuppEapolLogoffFramesTx++;
+	sm->dot1xSuppEapolFramesTx++;
+}
+
+
+static void eapol_sm_txStart(struct eapol_sm *sm)
+{
+	wpa_printf(MSG_DEBUG, "EAPOL: txStart");
+	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
+			    IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0);
+	sm->dot1xSuppEapolStartFramesTx++;
+	sm->dot1xSuppEapolFramesTx++;
+}
+
+
+#define IEEE8021X_ENCR_KEY_LEN 32
+#define IEEE8021X_SIGN_KEY_LEN 32
+
+struct eap_key_data {
+	u8 encr_key[IEEE8021X_ENCR_KEY_LEN];
+	u8 sign_key[IEEE8021X_SIGN_KEY_LEN];
+};
+
+
+static void eapol_sm_processKey(struct eapol_sm *sm)
+{
+#ifndef CONFIG_FIPS
+	struct ieee802_1x_hdr *hdr;
+	struct ieee802_1x_eapol_key *key;
+	struct eap_key_data keydata;
+	u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
+#ifndef CONFIG_NO_RC4
+	u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
+#endif /* CONFIG_NO_RC4 */
+	int key_len, res, sign_key_len, encr_key_len;
+	u16 rx_key_length;
+	size_t plen;
+
+	wpa_printf(MSG_DEBUG, "EAPOL: processKey");
+	if (sm->last_rx_key == NULL)
+		return;
+
+	if (!sm->conf.accept_802_1x_keys) {
+		wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key"
+			   " even though this was not accepted - "
+			   "ignoring this packet");
+		return;
+	}
+
+	if (sm->last_rx_key_len < sizeof(*hdr) + sizeof(*key))
+		return;
+	hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;
+	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+	plen = be_to_host16(hdr->length);
+	if (sizeof(*hdr) + plen > sm->last_rx_key_len || plen < sizeof(*key)) {
+		wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
+		return;
+	}
+	rx_key_length = WPA_GET_BE16(key->key_length);
+	wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d "
+		   "EAPOL-Key: type=%d key_length=%d key_index=0x%x",
+		   hdr->version, hdr->type, be_to_host16(hdr->length),
+		   key->type, rx_key_length, key->key_index);
+
+	eapol_sm_notify_lower_layer_success(sm, 1);
+	sign_key_len = IEEE8021X_SIGN_KEY_LEN;
+	encr_key_len = IEEE8021X_ENCR_KEY_LEN;
+	res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata));
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for "
+			   "decrypting EAPOL-Key keys");
+		return;
+	}
+	if (res == 16) {
+		/* LEAP derives only 16 bytes of keying material. */
+		res = eapol_sm_get_key(sm, (u8 *) &keydata, 16);
+		if (res) {
+			wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP "
+				   "master key for decrypting EAPOL-Key keys");
+			return;
+		}
+		sign_key_len = 16;
+		encr_key_len = 16;
+		os_memcpy(keydata.sign_key, keydata.encr_key, 16);
+	} else if (res) {
+		wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key "
+			   "data for decrypting EAPOL-Key keys (res=%d)", res);
+		return;
+	}
+
+	/* The key replay_counter must increase when same master key */
+	if (sm->replay_counter_valid &&
+	    os_memcmp(sm->last_replay_counter, key->replay_counter,
+		      IEEE8021X_REPLAY_COUNTER_LEN) >= 0) {
+		wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did "
+			   "not increase - ignoring key");
+		wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter",
+			    sm->last_replay_counter,
+			    IEEE8021X_REPLAY_COUNTER_LEN);
+		wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter",
+			    key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN);
+		return;
+	}
+
+	/* Verify key signature (HMAC-MD5) */
+	os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN);
+	os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN);
+	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_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,
+			  IEEE8021X_KEY_SIGN_LEN);
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
+
+	key_len = plen - sizeof(*key);
+	if (key_len > 32 || rx_key_length > 32) {
+		wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
+			   key_len ? key_len : rx_key_length);
+		return;
+	}
+	if (key_len == rx_key_length) {
+#ifdef CONFIG_NO_RC4
+		if (encr_key_len) {
+			/* otherwise unused */
+		}
+		wpa_printf(MSG_ERROR, "EAPOL: RC4 not supported in the build");
+		return;
+#else /* CONFIG_NO_RC4 */
+		os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
+		os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
+			  encr_key_len);
+		os_memcpy(datakey, key + 1, key_len);
+		rc4_skip(ekey, IEEE8021X_KEY_IV_LEN + encr_key_len, 0,
+			 datakey, key_len);
+		wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
+				datakey, key_len);
+#endif /* CONFIG_NO_RC4 */
+	} else if (key_len == 0) {
+		/*
+		 * IEEE 802.1X-2004 specifies that least significant Key Length
+		 * octets from MS-MPPE-Send-Key are used as the key if the key
+		 * data is not present. This seems to be meaning the beginning
+		 * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in
+		 * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator.
+		 * Anyway, taking the beginning of the keying material from EAP
+		 * seems to interoperate with Authenticators.
+		 */
+		key_len = rx_key_length;
+		os_memcpy(datakey, keydata.encr_key, key_len);
+		wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying "
+				"material data encryption key",
+				datakey, key_len);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d "
+			   "(key_length=%d)", key_len, rx_key_length);
+		return;
+	}
+
+	sm->replay_counter_valid = TRUE;
+	os_memcpy(sm->last_replay_counter, key->replay_counter,
+		  IEEE8021X_REPLAY_COUNTER_LEN);
+
+	wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d "
+		   "len %d",
+		   key->key_index & IEEE8021X_KEY_INDEX_FLAG ?
+		   "unicast" : "broadcast",
+		   key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len);
+
+	if (sm->ctx->set_wep_key &&
+	    sm->ctx->set_wep_key(sm->ctx->ctx,
+				 key->key_index & IEEE8021X_KEY_INDEX_FLAG,
+				 key->key_index & IEEE8021X_KEY_INDEX_MASK,
+				 datakey, key_len) < 0) {
+		wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the "
+			   " driver.");
+	} else {
+		if (key->key_index & IEEE8021X_KEY_INDEX_FLAG)
+			sm->unicast_key_received = TRUE;
+		else
+			sm->broadcast_key_received = TRUE;
+
+		if ((sm->unicast_key_received ||
+		     !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) &&
+		    (sm->broadcast_key_received ||
+		     !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST)))
+		{
+			wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key "
+				   "frames received");
+			sm->portValid = TRUE;
+			if (sm->ctx->eapol_done_cb)
+				sm->ctx->eapol_done_cb(sm->ctx->ctx);
+		}
+	}
+#endif /* CONFIG_FIPS */
+}
+
+
+static void eapol_sm_getSuppRsp(struct eapol_sm *sm)
+{
+	wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp");
+	/* EAP layer processing; no special code is needed, since Supplicant
+	 * Backend state machine is waiting for eapNoResp or eapResp to be set
+	 * and these are only set in the EAP state machine when the processing
+	 * has finished. */
+}
+
+
+static void eapol_sm_txSuppRsp(struct eapol_sm *sm)
+{
+	struct wpabuf *resp;
+
+	wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");
+
+#ifdef CONFIG_EAP_PROXY
+	if (sm->use_eap_proxy) {
+		/* Get EAP Response from EAP Proxy */
+		resp = eap_proxy_get_eapRespData(sm->eap_proxy);
+		if (resp == NULL) {
+			wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP Proxy "
+				   "response data not available");
+			return;
+		}
+	} else
+#endif /* CONFIG_EAP_PROXY */
+
+	resp = eap_get_eapRespData(sm->eap);
+	if (resp == NULL) {
+		wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "
+			   "not available");
+		return;
+	}
+
+	/* Send EAP-Packet from the EAP layer to the Authenticator */
+	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
+			    IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp),
+			    wpabuf_len(resp));
+
+	/* eapRespData is not used anymore, so free it here */
+	wpabuf_free(resp);
+
+	if (sm->initial_req)
+		sm->dot1xSuppEapolReqIdFramesRx++;
+	else
+		sm->dot1xSuppEapolReqFramesRx++;
+	sm->dot1xSuppEapolRespFramesTx++;
+	sm->dot1xSuppEapolFramesTx++;
+}
+
+
+static void eapol_sm_abortSupp(struct eapol_sm *sm)
+{
+	/* release system resources that may have been allocated for the
+	 * authentication session */
+	os_free(sm->last_rx_key);
+	sm->last_rx_key = NULL;
+	wpabuf_free(sm->eapReqData);
+	sm->eapReqData = NULL;
+	eap_sm_abort(sm->eap);
+}
+
+
+static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	eapol_sm_step(timeout_ctx);
+}
+
+
+static void eapol_sm_set_port_authorized(struct eapol_sm *sm)
+{
+	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)
+{
+	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);
+}
+
+
+/**
+ * eapol_sm_step - EAPOL state machine step function
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * This function is called to notify the state machine about changed external
+ * variables. It will step through the EAPOL state machines in loop to process
+ * all triggered state changes.
+ */
+void eapol_sm_step(struct eapol_sm *sm)
+{
+	int i;
+
+	/* In theory, it should be ok to run this in loop until !changed.
+	 * However, it is better to use a limit on number of iterations to
+	 * allow events (e.g., SIGTERM) to stop the program cleanly if the
+	 * state machine were to generate a busy loop. */
+	for (i = 0; i < 100; i++) {
+		sm->changed = FALSE;
+		SM_STEP_RUN(SUPP_PAE);
+		SM_STEP_RUN(KEY_RX);
+		SM_STEP_RUN(SUPP_BE);
+#ifdef CONFIG_EAP_PROXY
+		if (sm->use_eap_proxy) {
+			/* Drive the EAP proxy state machine */
+			if (eap_proxy_sm_step(sm->eap_proxy, sm->eap))
+				sm->changed = TRUE;
+		} else
+#endif /* CONFIG_EAP_PROXY */
+		if (eap_peer_sm_step(sm->eap))
+			sm->changed = TRUE;
+		if (!sm->changed)
+			break;
+	}
+
+	if (sm->changed) {
+		/* restart EAPOL state machine step from timeout call in order
+		 * to allow other events to be processed. */
+		eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
+		eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm);
+	}
+
+	if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
+		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, result, sm->ctx->cb_ctx);
+	}
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+static const char *eapol_supp_pae_state(int state)
+{
+	switch (state) {
+	case SUPP_PAE_LOGOFF:
+		return "LOGOFF";
+	case SUPP_PAE_DISCONNECTED:
+		return "DISCONNECTED";
+	case SUPP_PAE_CONNECTING:
+		return "CONNECTING";
+	case SUPP_PAE_AUTHENTICATING:
+		return "AUTHENTICATING";
+	case SUPP_PAE_HELD:
+		return "HELD";
+	case SUPP_PAE_AUTHENTICATED:
+		return "AUTHENTICATED";
+	case SUPP_PAE_RESTART:
+		return "RESTART";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+
+static const char *eapol_supp_be_state(int state)
+{
+	switch (state) {
+	case SUPP_BE_REQUEST:
+		return "REQUEST";
+	case SUPP_BE_RESPONSE:
+		return "RESPONSE";
+	case SUPP_BE_SUCCESS:
+		return "SUCCESS";
+	case SUPP_BE_FAIL:
+		return "FAIL";
+	case SUPP_BE_TIMEOUT:
+		return "TIMEOUT";
+	case SUPP_BE_IDLE:
+		return "IDLE";
+	case SUPP_BE_INITIALIZE:
+		return "INITIALIZE";
+	case SUPP_BE_RECEIVE:
+		return "RECEIVE";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+
+static const char * eapol_port_status(PortStatus status)
+{
+	if (status == Authorized)
+		return "Authorized";
+	else
+		return "Unauthorized";
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static const char * eapol_port_control(PortControl ctrl)
+{
+	switch (ctrl) {
+	case Auto:
+		return "Auto";
+	case ForceUnauthorized:
+		return "ForceUnauthorized";
+	case ForceAuthorized:
+		return "ForceAuthorized";
+	default:
+		return "Unknown";
+	}
+}
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+/**
+ * eapol_sm_configure - Set EAPOL variables
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @heldPeriod: dot1xSuppHeldPeriod
+ * @authPeriod: dot1xSuppAuthPeriod
+ * @startPeriod: dot1xSuppStartPeriod
+ * @maxStart: dot1xSuppMaxStart
+ *
+ * Set configurable EAPOL state machine variables. Each variable can be set to
+ * the given value or ignored if set to -1 (to set only some of the variables).
+ */
+void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
+			int startPeriod, int maxStart)
+{
+	if (sm == NULL)
+		return;
+	if (heldPeriod >= 0)
+		sm->heldPeriod = heldPeriod;
+	if (authPeriod >= 0)
+		sm->authPeriod = authPeriod;
+	if (startPeriod >= 0)
+		sm->startPeriod = startPeriod;
+	if (maxStart >= 0)
+		sm->maxStart = maxStart;
+}
+
+
+/**
+ * eapol_sm_get_method_name - Get EAPOL method name
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * Returns: Static string containing name of current eap method or NULL
+ */
+const char * eapol_sm_get_method_name(struct eapol_sm *sm)
+{
+	if (sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED ||
+	    sm->suppPortStatus != Authorized)
+		return NULL;
+
+	return eap_sm_get_method_name(sm->eap);
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+/**
+ * eapol_sm_get_status - Get EAPOL state machine status
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query EAPOL state machine for status information. This function fills in a
+ * text area with current status information from the EAPOL state machine. If
+ * the buffer (buf) is not large enough, status information will be truncated
+ * to fit the buffer.
+ */
+int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
+			int verbose)
+{
+	int len, ret;
+	if (sm == NULL)
+		return 0;
+
+	len = os_snprintf(buf, buflen,
+			  "Supplicant PAE state=%s\n"
+			  "suppPortStatus=%s\n",
+			  eapol_supp_pae_state(sm->SUPP_PAE_state),
+			  eapol_port_status(sm->suppPortStatus));
+	if (os_snprintf_error(buflen, len))
+		return 0;
+
+	if (verbose) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "heldPeriod=%u\n"
+				  "authPeriod=%u\n"
+				  "startPeriod=%u\n"
+				  "maxStart=%u\n"
+				  "portControl=%s\n"
+				  "Supplicant Backend state=%s\n",
+				  sm->heldPeriod,
+				  sm->authPeriod,
+				  sm->startPeriod,
+				  sm->maxStart,
+				  eapol_port_control(sm->portControl),
+				  eapol_supp_be_state(sm->SUPP_BE_state));
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
+#ifdef CONFIG_EAP_PROXY
+	if (sm->use_eap_proxy)
+		len += eap_proxy_sm_get_status(sm->eap_proxy,
+					       buf + len, buflen - len,
+					       verbose);
+	else
+#endif /* CONFIG_EAP_PROXY */
+	len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);
+
+	return len;
+}
+
+
+/**
+ * eapol_sm_get_mib - Get EAPOL state machine MIBs
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @buf: Buffer for MIB information
+ * @buflen: Maximum buffer length
+ * Returns: Number of bytes written to buf.
+ *
+ * Query EAPOL state machine for MIB information. This function fills in a
+ * text area with current MIB information from the EAPOL state machine. If
+ * the buffer (buf) is not large enough, MIB information will be truncated to
+ * fit the buffer.
+ */
+int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
+{
+	size_t len;
+	int ret;
+
+	if (sm == NULL)
+		return 0;
+	ret = os_snprintf(buf, buflen,
+			  "dot1xSuppPaeState=%d\n"
+			  "dot1xSuppHeldPeriod=%u\n"
+			  "dot1xSuppAuthPeriod=%u\n"
+			  "dot1xSuppStartPeriod=%u\n"
+			  "dot1xSuppMaxStart=%u\n"
+			  "dot1xSuppSuppControlledPortStatus=%s\n"
+			  "dot1xSuppBackendPaeState=%d\n",
+			  sm->SUPP_PAE_state,
+			  sm->heldPeriod,
+			  sm->authPeriod,
+			  sm->startPeriod,
+			  sm->maxStart,
+			  sm->suppPortStatus == Authorized ?
+			  "Authorized" : "Unauthorized",
+			  sm->SUPP_BE_state);
+
+	if (os_snprintf_error(buflen, ret))
+		return 0;
+	len = ret;
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot1xSuppEapolFramesRx=%u\n"
+			  "dot1xSuppEapolFramesTx=%u\n"
+			  "dot1xSuppEapolStartFramesTx=%u\n"
+			  "dot1xSuppEapolLogoffFramesTx=%u\n"
+			  "dot1xSuppEapolRespFramesTx=%u\n"
+			  "dot1xSuppEapolReqIdFramesRx=%u\n"
+			  "dot1xSuppEapolReqFramesRx=%u\n"
+			  "dot1xSuppInvalidEapolFramesRx=%u\n"
+			  "dot1xSuppEapLengthErrorFramesRx=%u\n"
+			  "dot1xSuppLastEapolFrameVersion=%u\n"
+			  "dot1xSuppLastEapolFrameSource=" MACSTR "\n",
+			  sm->dot1xSuppEapolFramesRx,
+			  sm->dot1xSuppEapolFramesTx,
+			  sm->dot1xSuppEapolStartFramesTx,
+			  sm->dot1xSuppEapolLogoffFramesTx,
+			  sm->dot1xSuppEapolRespFramesTx,
+			  sm->dot1xSuppEapolReqIdFramesRx,
+			  sm->dot1xSuppEapolReqFramesRx,
+			  sm->dot1xSuppInvalidEapolFramesRx,
+			  sm->dot1xSuppEapLengthErrorFramesRx,
+			  sm->dot1xSuppLastEapolFrameVersion,
+			  MAC2STR(sm->dot1xSuppLastEapolFrameSource));
+
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	return len;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+/**
+ * eapol_sm_rx_eapol - Process received EAPOL frames
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @src: Source MAC address of the EAPOL packet
+ * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
+ * @len: Length of the EAPOL frame
+ * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine,
+ * -1 failure
+ */
+int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
+		      size_t len)
+{
+	const struct ieee802_1x_hdr *hdr;
+	const struct ieee802_1x_eapol_key *key;
+	int data_len;
+	int res = 1;
+	size_t plen;
+
+	if (sm == NULL)
+		return 0;
+	sm->dot1xSuppEapolFramesRx++;
+	if (len < sizeof(*hdr)) {
+		sm->dot1xSuppInvalidEapolFramesRx++;
+		return 0;
+	}
+	hdr = (const struct ieee802_1x_hdr *) buf;
+	sm->dot1xSuppLastEapolFrameVersion = hdr->version;
+	os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN);
+	if (hdr->version < EAPOL_VERSION) {
+		/* TODO: backwards compatibility */
+	}
+	plen = be_to_host16(hdr->length);
+	if (plen > len - sizeof(*hdr)) {
+		sm->dot1xSuppEapLengthErrorFramesRx++;
+		return 0;
+	}
+#ifdef CONFIG_WPS
+	if (sm->conf.wps && sm->conf.workaround &&
+	    plen < len - sizeof(*hdr) &&
+	    hdr->type == IEEE802_1X_TYPE_EAP_PACKET &&
+	    len - sizeof(*hdr) > sizeof(struct eap_hdr)) {
+		const struct eap_hdr *ehdr =
+			(const struct eap_hdr *) (hdr + 1);
+		u16 elen;
+
+		elen = be_to_host16(ehdr->length);
+		if (elen > plen && elen <= len - sizeof(*hdr)) {
+			/*
+			 * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS
+			 * packets with too short EAPOL header length field
+			 * (14 octets). This is fixed in firmware Ver.1.49.
+			 * As a workaround, fix the EAPOL header based on the
+			 * correct length in the EAP packet.
+			 */
+			wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL "
+				   "payload length based on EAP header: "
+				   "%d -> %d", (int) plen, elen);
+			plen = elen;
+		}
+	}
+#endif /* CONFIG_WPS */
+	data_len = plen + sizeof(*hdr);
+
+	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
+			 * EAPOL state machines.
+			 */
+			eapol_sm_abort_cached(sm);
+		}
+		wpabuf_free(sm->eapReqData);
+		sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen);
+		if (sm->eapReqData) {
+			wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "
+				   "frame");
+			sm->eapolEap = TRUE;
+#ifdef CONFIG_EAP_PROXY
+			if (sm->use_eap_proxy) {
+				eap_proxy_packet_update(
+					sm->eap_proxy,
+					wpabuf_mhead_u8(sm->eapReqData),
+					wpabuf_len(sm->eapReqData));
+				wpa_printf(MSG_DEBUG, "EAPOL: eap_proxy "
+					   "EAP Req updated");
+			}
+#endif /* CONFIG_EAP_PROXY */
+			eapol_sm_step(sm);
+		}
+		break;
+	case IEEE802_1X_TYPE_EAPOL_KEY:
+		if (plen < sizeof(*key)) {
+			wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key "
+				   "frame received");
+			break;
+		}
+		key = (const struct ieee802_1x_eapol_key *) (hdr + 1);
+		if (key->type == EAPOL_KEY_TYPE_WPA ||
+		    key->type == EAPOL_KEY_TYPE_RSN) {
+			/* WPA Supplicant takes care of this frame. */
+			wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key "
+				   "frame in EAPOL state machines");
+			res = 0;
+			break;
+		}
+		if (key->type != EAPOL_KEY_TYPE_RC4) {
+			wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown "
+				   "EAPOL-Key type %d", key->type);
+			break;
+		}
+		os_free(sm->last_rx_key);
+		sm->last_rx_key = os_malloc(data_len);
+		if (sm->last_rx_key) {
+			wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key "
+				   "frame");
+			os_memcpy(sm->last_rx_key, buf, data_len);
+			sm->last_rx_key_len = data_len;
+			sm->rxKey = TRUE;
+			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);
+		sm->dot1xSuppInvalidEapolFramesRx++;
+		break;
+	}
+
+	return res;
+}
+
+
+/**
+ * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machine about transmitted EAPOL packet from an external
+ * component, e.g., WPA. This will update the statistics.
+ */
+void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
+{
+	if (sm)
+		sm->dot1xSuppEapolFramesTx++;
+}
+
+
+/**
+ * eapol_sm_notify_portEnabled - Notification about portEnabled change
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @enabled: New portEnabled value
+ *
+ * Notify EAPOL state machine about new portEnabled value.
+ */
+void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled)
+{
+	if (sm == NULL)
+		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);
+}
+
+
+/**
+ * eapol_sm_notify_portValid - Notification about portValid change
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @valid: New portValid value
+ *
+ * Notify EAPOL state machine about new portValid value.
+ */
+void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid)
+{
+	if (sm == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+		   "portValid=%d", valid);
+	sm->portValid = valid;
+	eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_eap_success - Notification of external EAP success trigger
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @success: %TRUE = set success, %FALSE = clear success
+ *
+ * Notify the EAPOL state machine that external event has forced EAP state to
+ * success (success = %TRUE). This can be cleared by setting success = %FALSE.
+ *
+ * This function is called to update EAP state when WPA-PSK key handshake has
+ * been completed successfully since WPA-PSK does not use EAP state machine.
+ */
+void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success)
+{
+	if (sm == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+		   "EAP success=%d", success);
+	sm->eapSuccess = success;
+	sm->altAccept = success;
+	if (success)
+		eap_notify_success(sm->eap);
+	eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @fail: %TRUE = set failure, %FALSE = clear failure
+ *
+ * Notify EAPOL state machine that external event has forced EAP state to
+ * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE.
+ */
+void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
+{
+	if (sm == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+		   "EAP fail=%d", fail);
+	sm->eapFail = fail;
+	sm->altReject = fail;
+	eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_config - Notification of EAPOL configuration change
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @config: Pointer to current network EAP configuration
+ * @conf: Pointer to EAPOL configuration data
+ *
+ * Notify EAPOL state machine that configuration has changed. config will be
+ * stored as a backpointer to network configuration. This can be %NULL to clear
+ * the stored pointed. conf will be copied to local EAPOL/EAP configuration
+ * data. If conf is %NULL, this part of the configuration change will be
+ * skipped.
+ */
+void eapol_sm_notify_config(struct eapol_sm *sm,
+			    struct eap_peer_config *config,
+			    const struct eapol_config *conf)
+{
+	if (sm == NULL)
+		return;
+
+	sm->config = config;
+#ifdef CONFIG_EAP_PROXY
+	sm->use_eap_proxy = eap_proxy_notify_config(sm->eap_proxy, config) > 0;
+#endif /* CONFIG_EAP_PROXY */
+
+	if (conf == NULL)
+		return;
+
+	sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys;
+	sm->conf.required_keys = conf->required_keys;
+	sm->conf.fast_reauth = conf->fast_reauth;
+	sm->conf.workaround = conf->workaround;
+	sm->conf.wps = conf->wps;
+#ifdef CONFIG_EAP_PROXY
+	if (sm->use_eap_proxy) {
+		/* Using EAP Proxy, so skip EAP state machine update */
+		return;
+	}
+#endif /* CONFIG_EAP_PROXY */
+	if (sm->eap) {
+		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);
+	}
+}
+
+
+/**
+ * eapol_sm_get_key - Get master session key (MSK) from EAP
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @key: Pointer for key buffer
+ * @len: Number of bytes to copy to key
+ * Returns: 0 on success (len of key available), maximum available key len
+ * (>0) if key is available but it is shorter than len, or -1 on failure.
+ *
+ * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key
+ * is available only after a successful authentication.
+ */
+int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
+{
+	const u8 *eap_key;
+	size_t eap_len;
+
+#ifdef CONFIG_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");
+			return -1;
+		}
+		eap_key = eap_proxy_get_eapKeyData(sm->eap_proxy, &eap_len);
+		if (eap_key == NULL) {
+			wpa_printf(MSG_DEBUG, "EAPOL: Failed to get "
+				   "eapKeyData");
+			return -1;
+		}
+		goto key_fetched;
+	}
+#endif /* CONFIG_EAP_PROXY */
+	if (sm == NULL || !eap_key_available(sm->eap)) {
+		wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
+		return -1;
+	}
+	eap_key = eap_get_eapKeyData(sm->eap, &eap_len);
+	if (eap_key == NULL) {
+		wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData");
+		return -1;
+	}
+#ifdef CONFIG_EAP_PROXY
+key_fetched:
+#endif /* CONFIG_EAP_PROXY */
+	if (len > eap_len) {
+		wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not "
+			   "available (len=%lu)",
+			   (unsigned long) len, (unsigned long) eap_len);
+		return eap_len;
+	}
+	os_memcpy(key, eap_key, len);
+	wpa_printf(MSG_DEBUG, "EAPOL: Successfully fetched key (len=%lu)",
+		   (unsigned long) len);
+	return 0;
+}
+
+
+/**
+ * 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
+ *
+ * Notify EAPOL state machines that user requested logon/logoff.
+ */
+void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
+{
+	if (sm) {
+		sm->userLogoff = logoff;
+		if (!logoff) {
+			/* If there is a delayed txStart queued, start now. */
+			sm->startWhen = 0;
+		}
+		eapol_sm_step(sm);
+	}
+}
+
+
+/**
+ * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machines that PMKSA caching was successful. This is used
+ * to move EAPOL and EAP state machines into authenticated/successful state.
+ */
+void eapol_sm_notify_cached(struct eapol_sm *sm)
+{
+	if (sm == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL");
+	sm->eapSuccess = TRUE;
+	eap_notify_success(sm->eap);
+	eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machines if PMKSA caching is used.
+ */
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm)
+{
+	if (sm == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
+	sm->cached_pmk = TRUE;
+}
+
+
+static void eapol_sm_abort_cached(struct eapol_sm *sm)
+{
+	wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, "
+		   "doing full EAP authentication");
+	if (sm == NULL)
+		return;
+	sm->cached_pmk = FALSE;
+	sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
+	eapol_sm_set_port_unauthorized(sm);
+
+	/* Make sure we do not start sending EAPOL-Start frames first, but
+	 * instead move to RESTART state to start EAPOL authentication. */
+	sm->startWhen = 3;
+	eapol_enable_timer_tick(sm);
+
+	if (sm->ctx->aborted_cached)
+		sm->ctx->aborted_cached(sm->ctx->ctx);
+}
+
+
+/**
+ * eapol_sm_register_scard_ctx - Notification of smart card context
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @ctx: Context data for smart card operations
+ *
+ * Notify EAPOL state machines of context data for smart card operations. This
+ * context data will be used as a parameter for scard_*() functions.
+ */
+void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx)
+{
+	if (sm) {
+		sm->ctx->scard_ctx = ctx;
+		eap_register_scard_ctx(sm->eap, ctx);
+	}
+}
+
+
+/**
+ * eapol_sm_notify_portControl - Notification of portControl changes
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @portControl: New value for portControl variable
+ *
+ * Notify EAPOL state machines that portControl variable has changed.
+ */
+void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl)
+{
+	if (sm == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+		   "portControl=%s", eapol_port_control(portControl));
+	sm->portControl = portControl;
+	eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_ctrl_attached - Notification of attached monitor
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machines that a monitor was attached to the control
+ * interface to trigger re-sending of pending requests for user input.
+ */
+void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
+{
+	if (sm == NULL)
+		return;
+	eap_sm_notify_ctrl_attached(sm->eap);
+}
+
+
+/**
+ * eapol_sm_notify_ctrl_response - Notification of received user input
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machines that a control response, i.e., user
+ * input, was received in order to trigger retrying of a pending EAP request.
+ */
+void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
+{
+	if (sm == NULL)
+		return;
+	if (sm->eapReqData && !sm->eapReq) {
+		wpa_printf(MSG_DEBUG, "EAPOL: received control response (user "
+			   "input) notification - retrying pending EAP "
+			   "Request");
+		sm->eapolEap = TRUE;
+		sm->eapReq = TRUE;
+		eapol_sm_step(sm);
+	}
+}
+
+
+/**
+ * eapol_sm_request_reauth - Request reauthentication
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * This function can be used to request EAPOL reauthentication, e.g., when the
+ * current PMKSA entry is nearing expiration.
+ */
+void eapol_sm_request_reauth(struct eapol_sm *sm)
+{
+	if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED)
+		return;
+	eapol_sm_txStart(sm);
+}
+
+
+/**
+ * eapol_sm_notify_lower_layer_success - Notification of lower layer success
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @in_eapol_sm: Whether the caller is already running inside EAPOL state
+ * machine loop (eapol_sm_step())
+ *
+ * Notify EAPOL (and EAP) state machines that a lower layer has detected a
+ * successful authentication. This is used to recover from dropped EAP-Success
+ * messages.
+ */
+void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm)
+{
+	if (sm == NULL)
+		return;
+	eap_notify_lower_layer_success(sm->eap);
+	if (!in_eapol_sm)
+		eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_invalidate_cached_session - Mark cached EAP session data invalid
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ */
+void eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
+{
+	if (sm)
+		eap_invalidate_cached_session(sm->eap);
+}
+
+
+static struct eap_peer_config * eapol_sm_get_config(void *ctx)
+{
+	struct eapol_sm *sm = ctx;
+	return sm ? sm->config : NULL;
+}
+
+
+static struct wpabuf * eapol_sm_get_eapReqData(void *ctx)
+{
+	struct eapol_sm *sm = ctx;
+	if (sm == NULL || sm->eapReqData == NULL)
+		return NULL;
+
+	return sm->eapReqData;
+}
+
+
+static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
+{
+	struct eapol_sm *sm = ctx;
+	if (sm == NULL)
+		return FALSE;
+	switch (variable) {
+	case EAPOL_eapSuccess:
+		return sm->eapSuccess;
+	case EAPOL_eapRestart:
+		return sm->eapRestart;
+	case EAPOL_eapFail:
+		return sm->eapFail;
+	case EAPOL_eapResp:
+		return sm->eapResp;
+	case EAPOL_eapNoResp:
+		return sm->eapNoResp;
+	case EAPOL_eapReq:
+		return sm->eapReq;
+	case EAPOL_portEnabled:
+		return sm->portEnabled;
+	case EAPOL_altAccept:
+		return sm->altAccept;
+	case EAPOL_altReject:
+		return sm->altReject;
+	case EAPOL_eapTriggerStart:
+		return sm->eapTriggerStart;
+	}
+	return FALSE;
+}
+
+
+static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
+			      Boolean value)
+{
+	struct eapol_sm *sm = ctx;
+	if (sm == NULL)
+		return;
+	switch (variable) {
+	case EAPOL_eapSuccess:
+		sm->eapSuccess = value;
+		break;
+	case EAPOL_eapRestart:
+		sm->eapRestart = value;
+		break;
+	case EAPOL_eapFail:
+		sm->eapFail = value;
+		break;
+	case EAPOL_eapResp:
+		sm->eapResp = value;
+		break;
+	case EAPOL_eapNoResp:
+		sm->eapNoResp = value;
+		break;
+	case EAPOL_eapReq:
+		sm->eapReq = value;
+		break;
+	case EAPOL_portEnabled:
+		sm->portEnabled = value;
+		break;
+	case EAPOL_altAccept:
+		sm->altAccept = value;
+		break;
+	case EAPOL_altReject:
+		sm->altReject = value;
+		break;
+	case EAPOL_eapTriggerStart:
+		sm->eapTriggerStart = value;
+		break;
+	}
+}
+
+
+static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable)
+{
+	struct eapol_sm *sm = ctx;
+	if (sm == NULL)
+		return 0;
+	switch (variable) {
+	case EAPOL_idleWhile:
+		return sm->idleWhile;
+	}
+	return 0;
+}
+
+
+static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable,
+			     unsigned int value)
+{
+	struct eapol_sm *sm = ctx;
+	if (sm == NULL)
+		return;
+	switch (variable) {
+	case EAPOL_idleWhile:
+		sm->idleWhile = value;
+		if (sm->idleWhile > 0)
+			eapol_enable_timer_tick(sm);
+		break;
+	}
+}
+
+
+static void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+	struct eapol_sm *sm = ctx;
+	if (sm && sm->ctx && sm->ctx->set_config_blob)
+		sm->ctx->set_config_blob(sm->ctx->ctx, blob);
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+static const struct wpa_config_blob *
+eapol_sm_get_config_blob(void *ctx, const char *name)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+	struct eapol_sm *sm = ctx;
+	if (sm && sm->ctx && sm->ctx->get_config_blob)
+		return sm->ctx->get_config_blob(sm->ctx->ctx, name);
+	else
+		return NULL;
+#else /* CONFIG_NO_CONFIG_BLOBS */
+	return NULL;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+static void eapol_sm_notify_pending(void *ctx)
+{
+	struct eapol_sm *sm = ctx;
+	if (sm == NULL)
+		return;
+	if (sm->eapReqData && !sm->eapReq) {
+		wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP "
+			   "state machine - retrying pending EAP Request");
+		sm->eapolEap = TRUE;
+		sm->eapReq = TRUE;
+		eapol_sm_step(sm);
+	}
+}
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field,
+				      const char *txt)
+{
+	struct eapol_sm *sm = ctx;
+	wpa_printf(MSG_DEBUG, "EAPOL: EAP parameter needed");
+	if (sm->ctx->eap_param_needed)
+		sm->ctx->eap_param_needed(sm->ctx->ctx, field, txt);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define eapol_sm_eap_param_needed NULL
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject,
+				 const char *altsubject[],
+				 int num_altsubject, const char *cert_hash,
+				 const struct wpabuf *cert)
+{
+	struct eapol_sm *sm = ctx;
+	if (sm->ctx->cert_cb)
+		sm->ctx->cert_cb(sm->ctx->ctx, depth, subject, altsubject,
+				 num_altsubject, cert_hash, cert);
+}
+
+
+static void eapol_sm_notify_status(void *ctx, const char *status,
+				   const char *parameter)
+{
+	struct eapol_sm *sm = ctx;
+
+	if (sm->ctx->status_cb)
+		sm->ctx->status_cb(sm->ctx->ctx, status, parameter);
+}
+
+
+#ifdef CONFIG_EAP_PROXY
+static void eapol_sm_eap_proxy_cb(void *ctx)
+{
+	struct eapol_sm *sm = ctx;
+
+	if (sm->ctx->eap_proxy_cb)
+		sm->ctx->eap_proxy_cb(sm->ctx->ctx);
+}
+#endif /* CONFIG_EAP_PROXY */
+
+
+static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+	struct eapol_sm *sm = ctx;
+
+	if (sm->ctx->set_anon_id)
+		sm->ctx->set_anon_id(sm->ctx->ctx, id, len);
+}
+
+
+static const struct eapol_callbacks eapol_cb =
+{
+	eapol_sm_get_config,
+	eapol_sm_get_bool,
+	eapol_sm_set_bool,
+	eapol_sm_get_int,
+	eapol_sm_set_int,
+	eapol_sm_get_eapReqData,
+	eapol_sm_set_config_blob,
+	eapol_sm_get_config_blob,
+	eapol_sm_notify_pending,
+	eapol_sm_eap_param_needed,
+	eapol_sm_notify_cert,
+	eapol_sm_notify_status,
+#ifdef CONFIG_EAP_PROXY
+	eapol_sm_eap_proxy_cb,
+#endif /* CONFIG_EAP_PROXY */
+	eapol_sm_set_anon_id
+};
+
+
+/**
+ * eapol_sm_init - Initialize EAPOL state machine
+ * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer
+ * and EAPOL state machine will free it in eapol_sm_deinit()
+ * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure
+ *
+ * Allocate and initialize an EAPOL state machine.
+ */
+struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
+{
+	struct eapol_sm *sm;
+	struct eap_config conf;
+	sm = os_zalloc(sizeof(*sm));
+	if (sm == NULL)
+		return NULL;
+	sm->ctx = ctx;
+
+	sm->portControl = Auto;
+
+	/* Supplicant PAE state machine */
+	sm->heldPeriod = 60;
+	sm->startPeriod = 30;
+	sm->maxStart = 3;
+
+	/* Supplicant Backend state machine */
+	sm->authPeriod = 30;
+
+	os_memset(&conf, 0, sizeof(conf));
+	conf.opensc_engine_path = ctx->opensc_engine_path;
+	conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
+	conf.pkcs11_module_path = ctx->pkcs11_module_path;
+	conf.openssl_ciphers = ctx->openssl_ciphers;
+	conf.wps = ctx->wps;
+	conf.cert_in_cb = ctx->cert_in_cb;
+
+	sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
+	if (sm->eap == NULL) {
+		os_free(sm);
+		return NULL;
+	}
+
+#ifdef CONFIG_EAP_PROXY
+	sm->use_eap_proxy = FALSE;
+	sm->eap_proxy = eap_proxy_init(sm, &eapol_cb, sm->ctx->msg_ctx);
+	if (sm->eap_proxy == NULL) {
+		wpa_printf(MSG_ERROR, "Unable to initialize EAP Proxy");
+	}
+#endif /* CONFIG_EAP_PROXY */
+
+	/* Initialize EAPOL state machines */
+	sm->force_authorized_update = TRUE;
+	sm->initialize = TRUE;
+	eapol_sm_step(sm);
+	sm->initialize = FALSE;
+	eapol_sm_step(sm);
+
+	sm->timer_tick_enabled = 1;
+	eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+
+	return sm;
+}
+
+
+/**
+ * eapol_sm_deinit - Deinitialize EAPOL state machine
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Deinitialize and free EAPOL state machine.
+ */
+void eapol_sm_deinit(struct eapol_sm *sm)
+{
+	if (sm == NULL)
+		return;
+	eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
+	eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
+	eap_peer_sm_deinit(sm->eap);
+#ifdef CONFIG_EAP_PROXY
+	eap_proxy_deinit(sm->eap_proxy);
+#endif /* CONFIG_EAP_PROXY */
+	os_free(sm->last_rx_key);
+	wpabuf_free(sm->eapReqData);
+	os_free(sm->ctx);
+	os_free(sm);
+}
+
+
+void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
+			     struct ext_password_data *ext)
+{
+	if (sm && sm->eap)
+		eap_sm_set_ext_pw_ctx(sm->eap, ext);
+}
+
+
+int eapol_sm_failed(struct eapol_sm *sm)
+{
+	if (sm == NULL)
+		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 */
+}
+
+
+void eapol_sm_erp_flush(struct eapol_sm *sm)
+{
+	if (sm)
+		eap_peer_erp_free_keys(sm->eap);
+}
diff --git a/hostap/src/eapol_supp/eapol_supp_sm.h b/hostap/src/eapol_supp/eapol_supp_sm.h
new file mode 100644
index 0000000..1309ff7
--- /dev/null
+++ b/hostap/src/eapol_supp/eapol_supp_sm.h
@@ -0,0 +1,443 @@
+/*
+ * EAPOL supplicant state machines
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAPOL_SUPP_SM_H
+#define EAPOL_SUPP_SM_H
+
+#include "common/defs.h"
+
+typedef enum { Unauthorized, Authorized } PortStatus;
+typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl;
+
+/**
+ * struct eapol_config - Per network configuration for EAPOL state machines
+ */
+struct eapol_config {
+	/**
+	 * accept_802_1x_keys - Accept IEEE 802.1X (non-WPA) EAPOL-Key frames
+	 *
+	 * This variable should be set to 1 when using EAPOL state machines
+	 * with non-WPA security policy to generate dynamic WEP keys. When
+	 * using WPA, this should be set to 0 so that WPA state machine can
+	 * process the EAPOL-Key frames.
+	 */
+	int accept_802_1x_keys;
+
+#define EAPOL_REQUIRE_KEY_UNICAST BIT(0)
+#define EAPOL_REQUIRE_KEY_BROADCAST BIT(1)
+	/**
+	 * required_keys - Which EAPOL-Key packets are required
+	 *
+	 * This variable determines which EAPOL-Key packets are required before
+	 * marking connection authenticated. This is a bit field of
+	 * EAPOL_REQUIRE_KEY_UNICAST and EAPOL_REQUIRE_KEY_BROADCAST flags.
+	 */
+	int required_keys;
+
+	/**
+	 * fast_reauth - Whether fast EAP reauthentication is enabled
+	 */
+	int fast_reauth;
+
+	/**
+	 * workaround - Whether EAP workarounds are enabled
+	 */
+	unsigned int workaround;
+
+	/**
+	 * eap_disabled - Whether EAP is disabled
+	 */
+	int eap_disabled;
+
+	/**
+	 * external_sim - Use external processing for SIM/USIM operations
+	 */
+	int external_sim;
+
+#define EAPOL_LOCAL_WPS_IN_USE BIT(0)
+#define EAPOL_PEER_IS_WPS20_AP BIT(1)
+	/**
+	 * wps - Whether this connection is used for WPS
+	 */
+	int wps;
+};
+
+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
+ */
+struct eapol_ctx {
+	/**
+	 * ctx - Pointer to arbitrary upper level context
+	 */
+	void *ctx;
+
+	/**
+	 * preauth - IEEE 802.11i/RSN pre-authentication
+	 *
+	 * This EAPOL state machine is used for IEEE 802.11i/RSN
+	 * pre-authentication
+	 */
+	int preauth;
+
+	/**
+	 * cb - Function to be called when EAPOL negotiation has been completed
+	 * @eapol: Pointer to EAPOL state machine data
+	 * @result: Whether the authentication was completed successfully
+	 * @ctx: Pointer to context data (cb_ctx)
+	 *
+	 * This optional callback function will be called when the EAPOL
+	 * authentication has been completed. This allows the owner of the
+	 * 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, enum eapol_supp_result result,
+		   void *ctx);
+
+	/**
+	 * cb_ctx - Callback context for cb()
+	 */
+	void *cb_ctx;
+
+	/**
+	 * msg_ctx - Callback context for wpa_msg() calls
+	 */
+	void *msg_ctx;
+
+	/**
+	 * scard_ctx - Callback context for PC/SC scard_*() function calls
+	 *
+	 * This context can be updated with eapol_sm_register_scard_ctx().
+	 */
+	void *scard_ctx;
+
+	/**
+	 * eapol_send_ctx - Callback context for eapol_send() calls
+	 */
+	void *eapol_send_ctx;
+
+	/**
+	 * eapol_done_cb - Function to be called at successful completion
+	 * @ctx: Callback context (ctx)
+	 *
+	 * This function is called at the successful completion of EAPOL
+	 * authentication. If dynamic WEP keys are used, this is called only
+	 * after all the expected keys have been received.
+	 */
+	void (*eapol_done_cb)(void *ctx);
+
+	/**
+	 * eapol_send - Send EAPOL packets
+	 * @ctx: Callback context (eapol_send_ctx)
+	 * @type: EAPOL type (IEEE802_1X_TYPE_*)
+	 * @buf: Pointer to EAPOL payload
+	 * @len: Length of the EAPOL payload
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*eapol_send)(void *ctx, int type, const u8 *buf, size_t len);
+
+	/**
+	 * set_wep_key - Configure WEP keys
+	 * @ctx: Callback context (ctx)
+	 * @unicast: Non-zero = unicast, 0 = multicast/broadcast key
+	 * @keyidx: Key index (0..3)
+	 * @key: WEP key
+	 * @keylen: Length of the WEP key
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_wep_key)(void *ctx, int unicast, int keyidx,
+			   const u8 *key, size_t keylen);
+
+	/**
+	 * set_config_blob - Set or add a named configuration blob
+	 * @ctx: Callback context (ctx)
+	 * @blob: New value for the blob
+	 *
+	 * Adds a new configuration blob or replaces the current value of an
+	 * existing blob.
+	 */
+	void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+
+	/**
+	 * get_config_blob - Get a named configuration blob
+	 * @ctx: Callback context (ctx)
+	 * @name: Name of the blob
+	 * Returns: Pointer to blob data or %NULL if not found
+	 */
+	const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+							  const char *name);
+
+	/**
+	 * aborted_cached - Notify that cached PMK attempt was aborted
+	 * @ctx: Callback context (ctx)
+	 */
+	void (*aborted_cached)(void *ctx);
+
+	/**
+	 * opensc_engine_path - Path to the OpenSSL engine for opensc
+	 *
+	 * This is an OpenSSL specific configuration option for loading OpenSC
+	 * engine (engine_opensc.so); if %NULL, this engine is not loaded.
+	 */
+	const char *opensc_engine_path;
+
+	/**
+	 * pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11
+	 *
+	 * This is an OpenSSL specific configuration option for loading PKCS#11
+	 * engine (engine_pkcs11.so); if %NULL, this engine is not loaded.
+	 */
+	const char *pkcs11_engine_path;
+
+	/**
+	 * pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module
+	 *
+	 * This is an OpenSSL specific configuration option for configuring
+	 * path to OpenSC/PKCS#11 engine (opensc-pkcs11.so); if %NULL, this
+	 * module is not loaded.
+	 */
+	const char *pkcs11_module_path;
+
+	/**
+	 * openssl_ciphers - OpenSSL cipher string
+	 *
+	 * This is an OpenSSL specific configuration option for configuring the
+	 * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the
+	 * default.
+	 */
+	const char *openssl_ciphers;
+
+	/**
+	 * wps - WPS context data
+	 *
+	 * This is only used by EAP-WSC and can be left %NULL if not available.
+	 */
+	struct wps_context *wps;
+
+	/**
+	 * eap_param_needed - Notify that EAP parameter is needed
+	 * @ctx: Callback context (ctx)
+	 * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY)
+	 * @txt: User readable text describing the required parameter
+	 */
+	void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field,
+				 const char *txt);
+
+	/**
+	 * port_cb - Set port authorized/unauthorized callback (optional)
+	 * @ctx: Callback context (ctx)
+	 * @authorized: Whether the supplicant port is now in authorized state
+	 */
+	void (*port_cb)(void *ctx, int authorized);
+
+	/**
+	 * cert_cb - Notification of a peer certificate
+	 * @ctx: Callback context (ctx)
+	 * @depth: Depth in certificate chain (0 = server)
+	 * @subject: Subject of the peer certificate
+	 * @altsubject: Select fields from AltSubject of the peer certificate
+	 * @num_altsubject: Number of altsubject values
+	 * @cert_hash: SHA-256 hash of the certificate
+	 * @cert: Peer certificate
+	 */
+	void (*cert_cb)(void *ctx, int depth, const char *subject,
+			const char *altsubject[], int num_altsubject,
+			const char *cert_hash, const struct wpabuf *cert);
+
+	/**
+	 * cert_in_cb - Include server certificates in callback
+	 */
+	int cert_in_cb;
+
+	/**
+	 * status_cb - Notification of a change in EAP status
+	 * @ctx: Callback context (ctx)
+	 * @status: Step in the process of EAP authentication
+	 * @parameter: Step-specific parameter, e.g., EAP method name
+	 */
+	void (*status_cb)(void *ctx, const char *status,
+			  const char *parameter);
+
+#ifdef CONFIG_EAP_PROXY
+	/**
+	 * eap_proxy_cb - Callback signifying any updates from eap_proxy
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 */
+	void (*eap_proxy_cb)(void *ctx);
+#endif /* CONFIG_EAP_PROXY */
+
+	/**
+	 * set_anon_id - Set or add anonymous identity
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @id: Anonymous identity (e.g., EAP-SIM pseudonym)
+	 * @len: Length of anonymous identity in octets
+	 */
+	void (*set_anon_id)(void *ctx, const u8 *id, size_t len);
+};
+
+
+struct eap_peer_config;
+struct ext_password_data;
+
+#ifdef IEEE8021X_EAPOL
+struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx);
+void eapol_sm_deinit(struct eapol_sm *sm);
+void eapol_sm_step(struct eapol_sm *sm);
+int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
+			int verbose);
+int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen);
+void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
+			int startPeriod, int maxStart);
+int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
+		      size_t len);
+void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm);
+void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled);
+void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid);
+void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success);
+void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail);
+void eapol_sm_notify_config(struct eapol_sm *sm,
+			    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);
+void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx);
+void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl);
+void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm);
+void eapol_sm_notify_ctrl_response(struct eapol_sm *sm);
+void eapol_sm_request_reauth(struct eapol_sm *sm);
+void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm);
+void eapol_sm_invalidate_cached_session(struct eapol_sm *sm);
+const char * eapol_sm_get_method_name(struct eapol_sm *sm);
+void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
+			     struct ext_password_data *ext);
+int eapol_sm_failed(struct eapol_sm *sm);
+void eapol_sm_erp_flush(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)
+{
+	free(ctx);
+	return (struct eapol_sm *) 1;
+}
+static inline void eapol_sm_deinit(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_step(struct eapol_sm *sm)
+{
+}
+static inline int eapol_sm_get_status(struct eapol_sm *sm, char *buf,
+				      size_t buflen, int verbose)
+{
+	return 0;
+}
+static inline int eapol_sm_get_mib(struct eapol_sm *sm, char *buf,
+				   size_t buflen)
+{
+	return 0;
+}
+static inline void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod,
+				      int authPeriod, int startPeriod,
+				      int maxStart)
+{
+}
+static inline int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src,
+				    const u8 *buf, size_t len)
+{
+	return 0;
+}
+static inline void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_portEnabled(struct eapol_sm *sm,
+					       Boolean enabled)
+{
+}
+static inline void eapol_sm_notify_portValid(struct eapol_sm *sm,
+					     Boolean valid)
+{
+}
+static inline void eapol_sm_notify_eap_success(struct eapol_sm *sm,
+					       Boolean success)
+{
+}
+static inline void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
+{
+}
+static inline void eapol_sm_notify_config(struct eapol_sm *sm,
+					  struct eap_peer_config *config,
+					  struct eapol_config *conf)
+{
+}
+static inline int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
+{
+	return -1;
+}
+static inline const u8 *
+eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len)
+{
+	return NULL;
+}
+static inline void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
+{
+}
+static inline void eapol_sm_notify_cached(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm)
+{
+}
+#define eapol_sm_register_scard_ctx(sm, ctx) do { } while (0)
+static inline void eapol_sm_notify_portControl(struct eapol_sm *sm,
+					       PortControl portControl)
+{
+}
+static inline void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_request_reauth(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm,
+						       int in_eapol_sm)
+{
+}
+static inline void eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
+{
+}
+static inline const char * eapol_sm_get_method_name(struct eapol_sm *sm)
+{
+	return NULL;
+}
+static inline void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
+					   struct ext_password_data *ext)
+{
+}
+static inline int eapol_sm_failed(struct eapol_sm *sm)
+{
+	return 0;
+}
+static inline void eapol_sm_erp_flush(struct eapol_sm *sm)
+{
+}
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* EAPOL_SUPP_SM_H */
diff --git a/hostap/src/fst/Makefile b/hostap/src/fst/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/hostap/src/fst/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/hostap/src/fst/fst.c b/hostap/src/fst/fst.c
new file mode 100644
index 0000000..2880870
--- /dev/null
+++ b/hostap/src/fst/fst.c
@@ -0,0 +1,225 @@
+/*
+ * FST module implementation
+ * Copyright (c) 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 "fst/fst.h"
+#include "fst/fst_internal.h"
+#include "fst/fst_defs.h"
+#include "fst/fst_ctrl_iface.h"
+
+struct dl_list fst_global_ctrls_list;
+
+
+static void fst_ctrl_iface_notify_peer_state_change(struct fst_iface *iface,
+						    Boolean connected,
+						    const u8 *peer_addr)
+{
+	union fst_event_extra extra;
+
+	extra.peer_state.connected = connected;
+	os_strlcpy(extra.peer_state.ifname, fst_iface_get_name(iface),
+		   sizeof(extra.peer_state.ifname));
+	os_memcpy(extra.peer_state.addr, peer_addr, ETH_ALEN);
+
+	foreach_fst_ctrl_call(on_event, EVENT_PEER_STATE_CHANGED,
+			      iface, NULL, &extra);
+}
+
+
+struct fst_iface * fst_attach(const char *ifname, const u8 *own_addr,
+			      const struct fst_wpa_obj *iface_obj,
+			      const struct fst_iface_cfg *cfg)
+{
+	struct fst_group *g;
+	struct fst_group *group = NULL;
+	struct fst_iface *iface = NULL;
+	Boolean new_group = FALSE;
+
+	WPA_ASSERT(ifname != NULL);
+	WPA_ASSERT(iface_obj != NULL);
+	WPA_ASSERT(cfg != NULL);
+
+	foreach_fst_group(g) {
+		if (os_strcmp(cfg->group_id, fst_group_get_id(g)) == 0) {
+			group = g;
+			break;
+		}
+	}
+
+	if (!group) {
+		group = fst_group_create(cfg->group_id);
+		if (!group) {
+			fst_printf(MSG_ERROR, "%s: FST group cannot be created",
+				   cfg->group_id);
+			return NULL;
+		}
+		new_group = TRUE;
+	}
+
+	iface = fst_iface_create(group, ifname, own_addr, iface_obj, cfg);
+	if (!iface) {
+		fst_printf_group(group, MSG_ERROR, "cannot create iface for %s",
+				 ifname);
+		if (new_group)
+			fst_group_delete(group);
+		return NULL;
+	}
+
+	fst_group_attach_iface(group, iface);
+	fst_group_update_ie(group);
+
+	foreach_fst_ctrl_call(on_iface_added, iface);
+
+	fst_printf_iface(iface, MSG_DEBUG,
+			 "iface attached to group %s (prio=%d, llt=%d)",
+			 cfg->group_id, cfg->priority, cfg->llt);
+
+	return iface;
+}
+
+
+void fst_detach(struct fst_iface *iface)
+{
+	struct fst_group *group = fst_iface_get_group(iface);
+
+	fst_printf_iface(iface, MSG_DEBUG, "iface detached from group %s",
+			 fst_group_get_id(group));
+	fst_session_global_on_iface_detached(iface);
+	foreach_fst_ctrl_call(on_iface_removed, iface);
+	fst_group_detach_iface(group, iface);
+	fst_iface_delete(iface);
+	fst_group_update_ie(group);
+	fst_group_delete_if_empty(group);
+}
+
+
+int fst_global_init(void)
+{
+	dl_list_init(&fst_global_groups_list);
+	dl_list_init(&fst_global_ctrls_list);
+	fst_session_global_init();
+	return 0;
+}
+
+
+void fst_global_deinit(void)
+{
+	struct fst_group *group;
+	struct fst_ctrl_handle *h;
+
+	fst_session_global_deinit();
+	while ((group = fst_first_group()) != NULL)
+		fst_group_delete(group);
+	while ((h = dl_list_first(&fst_global_ctrls_list,
+				  struct fst_ctrl_handle,
+				  global_ctrls_lentry)))
+		fst_global_del_ctrl(h);
+}
+
+
+struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl)
+{
+	struct fst_ctrl_handle *h;
+
+	if (!ctrl)
+		return NULL;
+
+	h = os_zalloc(sizeof(*h));
+	if (!h)
+		return NULL;
+
+	if (ctrl->init && ctrl->init()) {
+		os_free(h);
+		return NULL;
+	}
+
+	h->ctrl = *ctrl;
+	dl_list_add_tail(&fst_global_ctrls_list, &h->global_ctrls_lentry);
+
+	return h;
+}
+
+
+void fst_global_del_ctrl(struct fst_ctrl_handle *h)
+{
+	dl_list_del(&h->global_ctrls_lentry);
+	if (h->ctrl.deinit)
+		h->ctrl.deinit();
+	os_free(h);
+}
+
+
+void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt,
+		   size_t len)
+{
+	if (fst_iface_is_connected(iface, mgmt->sa))
+		fst_session_on_action_rx(iface, mgmt, len);
+	else
+		wpa_printf(MSG_DEBUG,
+			   "FST: Ignore FST Action frame - no FST connection with "
+			   MACSTR, MAC2STR(mgmt->sa));
+}
+
+
+void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr)
+{
+	if (is_zero_ether_addr(addr))
+		return;
+
+#ifndef HOSTAPD
+	fst_group_update_ie(fst_iface_get_group(iface));
+#endif /* HOSTAPD */
+
+	fst_printf_iface(iface, MSG_DEBUG, MACSTR " became connected",
+			 MAC2STR(addr));
+
+	fst_ctrl_iface_notify_peer_state_change(iface, TRUE, addr);
+}
+
+
+void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr)
+{
+	if (is_zero_ether_addr(addr))
+		return;
+
+#ifndef HOSTAPD
+	fst_group_update_ie(fst_iface_get_group(iface));
+#endif /* HOSTAPD */
+
+	fst_printf_iface(iface, MSG_DEBUG, MACSTR " became disconnected",
+			 MAC2STR(addr));
+
+	fst_ctrl_iface_notify_peer_state_change(iface, FALSE, addr);
+}
+
+
+Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1,
+				  struct fst_iface *iface2)
+{
+	return fst_iface_get_group(iface1) == fst_iface_get_group(iface2);
+}
+
+
+enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode)
+{
+	switch (mode) {
+	case HOSTAPD_MODE_IEEE80211B:
+	case HOSTAPD_MODE_IEEE80211G:
+		return MB_BAND_ID_WIFI_2_4GHZ;
+	case HOSTAPD_MODE_IEEE80211A:
+		return MB_BAND_ID_WIFI_5GHZ;
+	case HOSTAPD_MODE_IEEE80211AD:
+		return MB_BAND_ID_WIFI_60GHZ;
+	default:
+		WPA_ASSERT(0);
+		return MB_BAND_ID_WIFI_2_4GHZ;
+	}
+}
diff --git a/hostap/src/fst/fst.h b/hostap/src/fst/fst.h
new file mode 100644
index 0000000..0c0e435
--- /dev/null
+++ b/hostap/src/fst/fst.h
@@ -0,0 +1,296 @@
+/*
+ * FST module - interface 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 FST_H
+#define FST_H
+
+#ifdef CONFIG_FST
+
+#include "common/defs.h"
+#include "fst/fst_ctrl_iface.h"
+
+/* FST module hostap integration API */
+
+#define US_IN_MS           1000
+#define LLT_UNIT_US        32 /* See 10.32.2.2  Transitioning between states */
+
+#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US)
+#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS)
+
+#define FST_MAX_LLT_MS       FST_LLT_VAL_TO_MS(-1)
+#define FST_MAX_PRIO_VALUE   ((u8) -1)
+#define FST_MAX_GROUP_ID_LEN IFNAMSIZ
+
+#define FST_DEFAULT_LLT_CFG_VALUE 50
+
+struct hostapd_hw_modes;
+struct ieee80211_mgmt;
+struct fst_iface;
+struct fst_group;
+struct fst_session;
+struct fst_get_peer_ctx;
+struct fst_ctrl_handle;
+
+struct fst_wpa_obj {
+	void *ctx;
+
+	/**
+	 * get_bssid - Get BSSID of the interface
+	 * @ctx: User context %ctx
+	 * Returns: BSSID for success, %NULL for failure.
+	 *
+	 * NOTE: For AP it returns the own BSSID, while for STA - the BSSID of
+	 * the associated AP.
+	 */
+	const u8 * (*get_bssid)(void *ctx);
+
+	/**
+	 * get_channel_info - Get current channel info
+	 * @ctx: User context %ctx
+	 * @hw_mode: OUT, current HW mode
+	 * @channel: OUT, current channel
+	 */
+	void (*get_channel_info)(void *ctx, enum hostapd_hw_mode *hw_mode,
+				 u8 *channel);
+
+	/**
+	 * get_hw_modes - Get hardware modes
+	 * @ctx: User context %ctx
+	 * @modes: OUT, pointer on array of hw modes
+	 *
+	 * Returns: Number of hw modes available.
+	 */
+	int (*get_hw_modes)(void *ctx, struct hostapd_hw_modes **modes);
+
+	/**
+	 * set_ies - Set interface's MB IE
+	 * @ctx: User context %ctx
+	 * @fst_ies: MB IE buffer (owned by FST module)
+	 */
+	void (*set_ies)(void *ctx, const struct wpabuf *fst_ies);
+
+	/**
+	 * send_action - Send FST Action frame via the interface
+	 * @ctx: User context %ctx
+	 * @addr: Address of the destination STA
+	 * @data: Action frame buffer
+	 * Returns: 0 for success, negative error code for failure.
+	 */
+	int (*send_action)(void *ctx, const u8 *addr, struct wpabuf *data);
+
+	/**
+	 * get_mb_ie - Get last MB IE received from STA
+	 * @ctx: User context %ctx
+	 * @addr: Address of the STA
+	 * Returns: MB IE buffer, %NULL if no MB IE received from the STA
+	 */
+	const struct wpabuf * (*get_mb_ie)(void *ctx, const u8 *addr);
+
+	/**
+	 * update_mb_ie - Update last MB IE received from STA
+	 * @ctx: User context %ctx
+	 * @addr: Address of the STA
+	 * @buf: Buffer that contains the MB IEs data
+	 * @size: Size of data in %buf
+	 */
+	void (*update_mb_ie)(void *ctx, const u8 *addr,
+			     const u8 *buf, size_t size);
+
+	/**
+	 * get_peer_first - Get MAC address of the 1st connected STA
+	 * @ctx: User context %ctx
+	 * @get_ctx: Context to be used for %get_peer_next call
+	 * @mb_only: %TRUE if only multi-band capable peer should be reported
+	 * Returns: Address of the 1st connected STA, %NULL if no STAs connected
+	 */
+	const u8 * (*get_peer_first)(void *ctx,
+				     struct fst_get_peer_ctx **get_ctx,
+				     Boolean mb_only);
+	/**
+	 * get_peer_next - Get MAC address of the next connected STA
+	 * @ctx: User context %ctx
+	 * @get_ctx: Context received from %get_peer_first or previous
+	 *           %get_peer_next call
+	 * @mb_only: %TRUE if only multi-band capable peer should be reported
+	 * Returns: Address of the next connected STA, %NULL if no more STAs
+	 *          connected
+	 */
+	const u8 * (*get_peer_next)(void *ctx,
+				    struct fst_get_peer_ctx **get_ctx,
+				    Boolean mb_only);
+};
+
+/**
+ * fst_global_init - Global FST module initiator
+ * Returns: 0 for success, negative error code for failure.
+ * Note: The purpose of this function is to allocate and initiate global
+ *       FST module data structures (linked lists, static data etc.)
+ *       This function should be called prior to the 1st %fst_attach call.
+ */
+int fst_global_init(void);
+
+/**
+ * fst_global_deinit - Global FST module de-initiator
+ * Note: The purpose of this function is to deallocate and de-initiate global
+ *       FST module data structures (linked lists, static data etc.)
+ */
+void fst_global_deinit(void);
+
+/**
+ * struct fst_ctrl - Notification interface for FST module
+ */
+struct fst_ctrl {
+	/**
+	 * init - Initialize the notification interface
+	 * Returns: 0 for success, negative error code for failure.
+	 */
+	int (*init)(void);
+
+	/**
+	 * deinit - Deinitialize the notification interface
+	 */
+	void (*deinit)(void);
+
+	/**
+	 * on_group_created - Notify about FST group creation
+	 * Returns: 0 for success, negative error code for failure.
+	 */
+	int (*on_group_created)(struct fst_group *g);
+
+	/**
+	 * on_group_deleted - Notify about FST group deletion
+	 */
+	void (*on_group_deleted)(struct fst_group *g);
+
+	/**
+	 * on_iface_added - Notify about interface addition
+	 * Returns: 0 for success, negative error code for failure.
+	 */
+	int (*on_iface_added)(struct fst_iface *i);
+
+	/**
+	 * on_iface_removed - Notify about interface removal
+	 */
+	void (*on_iface_removed)(struct fst_iface *i);
+
+	/**
+	 * on_session_added - Notify about FST session addition
+	 * Returns: 0 for success, negative error code for failure.
+	 */
+	int (*on_session_added)(struct fst_session *s);
+
+	/**
+	 * on_session_removed - Notify about FST session removal
+	 */
+	void (*on_session_removed)(struct fst_session *s);
+
+	/**
+	 * on_event - Notify about FST event
+	 * @event_type: Event type
+	 * @i: Interface object that relates to the event or NULL
+	 * @g: Group object that relates to the event or NULL
+	 * @extra - Event specific data (see fst_ctrl_iface.h for more info)
+	 */
+	void (*on_event)(enum fst_event_type event_type, struct fst_iface *i,
+			 struct fst_session *s,
+			 const union fst_event_extra *extra);
+};
+
+struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl);
+void fst_global_del_ctrl(struct fst_ctrl_handle *h);
+
+/**
+ * NOTE: These values have to be read from configuration file
+ */
+struct fst_iface_cfg {
+	char group_id[FST_MAX_GROUP_ID_LEN + 1];
+	u8 priority;
+	u32 llt;
+};
+
+/**
+ * fst_attach - Attach interface to an FST group according to configuration read
+ * @ifname: Interface name
+ * @own_addr: Own interface MAC address
+ * @iface_obj: Callbacks to be used by FST module to communicate with
+ *             hostapd/wpa_supplicant
+ * @cfg: FST-related interface configuration read from the configuration file
+ * Returns: FST interface object for success, %NULL for failure.
+ */
+struct fst_iface * fst_attach(const char *ifname,
+			      const u8 *own_addr,
+			      const struct fst_wpa_obj *iface_obj,
+			      const struct fst_iface_cfg *cfg);
+
+/**
+ * fst_detach - Detach an interface
+ * @iface: FST interface object
+ */
+void fst_detach(struct fst_iface *iface);
+
+/* FST module inputs */
+/**
+ * fst_rx_action - FST Action frames handler
+ * @iface: FST interface object
+ * @mgmt: Action frame arrived
+ * @len: Action frame length
+ */
+void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt,
+		   size_t len);
+
+/**
+ * fst_notify_peer_connected - FST STA connect handler
+ * @iface: FST interface object
+ * @addr: Address of the connected STA
+ */
+void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr);
+
+/**
+ * fst_notify_peer_disconnected - FST STA disconnect handler
+ * @iface: FST interface object
+ * @addr: Address of the disconnected STA
+ */
+void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr);
+
+/* FST module auxiliary routines */
+
+/**
+ * fst_are_ifaces_aggregated - Determines whether 2 interfaces belong to the
+ *                             same FST group
+ * @iface1: 1st FST interface object
+ * @iface1: 2nd FST interface object
+ *
+ * Returns: %TRUE if the interfaces belong to the same FST group,
+ *          %FALSE otherwise
+ */
+Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1,
+				  struct fst_iface *iface2);
+
+#else /* CONFIG_FST */
+
+static inline int fst_global_init(void)
+{
+	return 0;
+}
+
+static inline int fst_global_start(void)
+{
+	return 0;
+}
+
+static inline void fst_global_stop(void)
+{
+}
+
+static inline void fst_global_deinit(void)
+{
+}
+
+#endif /* CONFIG_FST */
+
+#endif /* FST_H */
diff --git a/hostap/src/fst/fst_ctrl_aux.c b/hostap/src/fst/fst_ctrl_aux.c
new file mode 100644
index 0000000..dc7b2a7
--- /dev/null
+++ b/hostap/src/fst/fst_ctrl_aux.c
@@ -0,0 +1,69 @@
+/*
+ * FST module implementation
+ * Copyright (c) 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 "common/defs.h"
+#include "fst_ctrl_defs.h"
+#include "fst_ctrl_aux.h"
+
+
+static const char *session_event_names[] = {
+	[EVENT_FST_ESTABLISHED] FST_PVAL_EVT_TYPE_ESTABLISHED,
+	[EVENT_FST_SETUP] FST_PVAL_EVT_TYPE_SETUP,
+	[EVENT_FST_SESSION_STATE_CHANGED] FST_PVAL_EVT_TYPE_SESSION_STATE,
+};
+
+static const char *reason_names[] = {
+	[REASON_TEARDOWN] FST_CS_PVAL_REASON_TEARDOWN,
+	[REASON_SETUP] FST_CS_PVAL_REASON_SETUP,
+	[REASON_SWITCH] FST_CS_PVAL_REASON_SWITCH,
+	[REASON_STT] FST_CS_PVAL_REASON_STT,
+	[REASON_REJECT] FST_CS_PVAL_REASON_REJECT,
+	[REASON_ERROR_PARAMS] FST_CS_PVAL_REASON_ERROR_PARAMS,
+	[REASON_RESET] FST_CS_PVAL_REASON_RESET,
+	[REASON_DETACH_IFACE] FST_CS_PVAL_REASON_DETACH_IFACE,
+};
+
+static const char *session_state_names[] = {
+	[FST_SESSION_STATE_INITIAL] FST_CS_PVAL_STATE_INITIAL,
+	[FST_SESSION_STATE_SETUP_COMPLETION] FST_CS_PVAL_STATE_SETUP_COMPLETION,
+	[FST_SESSION_STATE_TRANSITION_DONE] FST_CS_PVAL_STATE_TRANSITION_DONE,
+	[FST_SESSION_STATE_TRANSITION_CONFIRMED]
+	FST_CS_PVAL_STATE_TRANSITION_CONFIRMED,
+};
+
+
+/* helpers */
+const char * fst_get_str_name(unsigned index, const char *names[],
+			      size_t names_size)
+{
+	if (index >= names_size || !names[index])
+		return FST_NAME_UNKNOWN;
+	return names[index];
+}
+
+
+const char * fst_session_event_type_name(enum fst_event_type event)
+{
+	return fst_get_str_name(event, session_event_names,
+				ARRAY_SIZE(session_event_names));
+}
+
+
+const char * fst_reason_name(enum fst_reason reason)
+{
+	return fst_get_str_name(reason, reason_names, ARRAY_SIZE(reason_names));
+}
+
+
+const char * fst_session_state_name(enum fst_session_state state)
+{
+	return fst_get_str_name(state, session_state_names,
+				ARRAY_SIZE(session_state_names));
+}
diff --git a/hostap/src/fst/fst_ctrl_aux.h b/hostap/src/fst/fst_ctrl_aux.h
new file mode 100644
index 0000000..e2133f5
--- /dev/null
+++ b/hostap/src/fst/fst_ctrl_aux.h
@@ -0,0 +1,91 @@
+/*
+ * FST module - miscellaneous 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 FST_CTRL_AUX_H
+#define FST_CTRL_AUX_H
+
+#include "common/defs.h"
+
+/* FST module control interface API */
+#define FST_INVALID_SESSION_ID ((u32) -1)
+#define FST_MAX_GROUP_ID_SIZE   32
+#define FST_MAX_INTERFACE_SIZE  32
+
+enum fst_session_state {
+	FST_SESSION_STATE_INITIAL,
+	FST_SESSION_STATE_SETUP_COMPLETION,
+	FST_SESSION_STATE_TRANSITION_DONE,
+	FST_SESSION_STATE_TRANSITION_CONFIRMED,
+	FST_SESSION_STATE_LAST
+};
+
+enum fst_event_type {
+	EVENT_FST_IFACE_STATE_CHANGED,  /* An interface has been either attached
+					 * to or detached from an FST group */
+	EVENT_FST_ESTABLISHED,          /* FST Session has been established */
+	EVENT_FST_SETUP,                /* FST Session request received */
+	EVENT_FST_SESSION_STATE_CHANGED,/* FST Session state has been changed */
+	EVENT_PEER_STATE_CHANGED        /* FST related generic event occurred,
+					 * see struct fst_hostap_event_data for
+					 *  more info */
+};
+
+enum fst_initiator {
+	FST_INITIATOR_UNDEFINED,
+	FST_INITIATOR_LOCAL,
+	FST_INITIATOR_REMOTE,
+};
+
+union fst_event_extra {
+	struct fst_event_extra_iface_state {
+		Boolean attached;
+		char ifname[FST_MAX_INTERFACE_SIZE];
+		char group_id[FST_MAX_GROUP_ID_SIZE];
+	} iface_state; /* for EVENT_FST_IFACE_STATE_CHANGED */
+	struct fst_event_extra_peer_state {
+		Boolean connected;
+		char ifname[FST_MAX_INTERFACE_SIZE];
+		u8 addr[ETH_ALEN];
+	} peer_state; /* for EVENT_PEER_STATE_CHANGED */
+	struct fst_event_extra_session_state {
+		enum fst_session_state old_state;
+		enum fst_session_state new_state;
+		union fst_session_state_switch_extra {
+			struct {
+				enum fst_reason {
+					REASON_TEARDOWN,
+					REASON_SETUP,
+					REASON_SWITCH,
+					REASON_STT,
+					REASON_REJECT,
+					REASON_ERROR_PARAMS,
+					REASON_RESET,
+					REASON_DETACH_IFACE,
+				} reason;
+				u8 reject_code; /* REASON_REJECT */
+				/* REASON_SWITCH,
+				 * REASON_TEARDOWN,
+				 * REASON_REJECT
+				 */
+				enum fst_initiator initiator;
+			} to_initial;
+		} extra;
+	} session_state; /* for EVENT_FST_SESSION_STATE_CHANGED */
+};
+
+/* helpers - prints enum in string form */
+#define FST_NAME_UNKNOWN "UNKNOWN"
+
+const char * fst_get_str_name(unsigned index, const char *names[],
+			      size_t names_size);
+
+const char * fst_session_event_type_name(enum fst_event_type);
+const char * fst_reason_name(enum fst_reason reason);
+const char * fst_session_state_name(enum fst_session_state state);
+
+#endif /* FST_CTRL_AUX_H */
diff --git a/hostap/src/fst/fst_ctrl_defs.h b/hostap/src/fst/fst_ctrl_defs.h
new file mode 100644
index 0000000..6735389
--- /dev/null
+++ b/hostap/src/fst/fst_ctrl_defs.h
@@ -0,0 +1,109 @@
+/*
+ * FST module - shared Control interface 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 FST_CTRL_DEFS_H
+#define FST_CTRL_DEFS_H
+
+/* Undefined value */
+#define FST_CTRL_PVAL_NONE                     "NONE"
+
+/* FST-ATTACH parameters */
+#define FST_ATTACH_CMD_PNAME_LLT      "llt"      /* pval = desired LLT */
+#define FST_ATTACH_CMD_PNAME_PRIORITY "priority" /* pval = desired priority */
+
+/* FST-MANAGER parameters */
+/* FST Session states */
+#define FST_CS_PVAL_STATE_INITIAL              "INITIAL"
+#define FST_CS_PVAL_STATE_SETUP_COMPLETION     "SETUP_COMPLETION"
+#define FST_CS_PVAL_STATE_TRANSITION_DONE      "TRANSITION_DONE"
+#define FST_CS_PVAL_STATE_TRANSITION_CONFIRMED "TRANSITION_CONFIRMED"
+
+/* FST Session reset reasons */
+#define FST_CS_PVAL_REASON_TEARDOWN     "REASON_TEARDOWN"
+#define FST_CS_PVAL_REASON_SETUP        "REASON_SETUP"
+#define FST_CS_PVAL_REASON_SWITCH       "REASON_SWITCH"
+#define FST_CS_PVAL_REASON_STT          "REASON_STT"
+#define FST_CS_PVAL_REASON_REJECT       "REASON_REJECT"
+#define FST_CS_PVAL_REASON_ERROR_PARAMS "REASON_ERROR_PARAMS"
+#define FST_CS_PVAL_REASON_RESET        "REASON_RESET"
+#define FST_CS_PVAL_REASON_DETACH_IFACE "REASON_DETACH_IFACE"
+
+/* FST Session responses */
+#define FST_CS_PVAL_RESPONSE_ACCEPT "ACCEPT"
+#define FST_CS_PVAL_RESPONSE_REJECT "REJECT"
+
+/* FST Session action initiator */
+#define FST_CS_PVAL_INITIATOR_LOCAL  "LOCAL"
+#define FST_CS_PVAL_INITIATOR_REMOTE "REMOTE"
+
+/* FST-CLI subcommands and parameter names */
+#define FST_CMD_LIST_GROUPS      "list_groups"
+#define FST_CMD_LIST_IFACES      "list_ifaces"
+#define FST_CMD_IFACE_PEERS      "iface_peers"
+#define FST_CMD_GET_PEER_MBIES   "get_peer_mbies"
+#define FST_CMD_LIST_SESSIONS    "list_sessions"
+#define FST_CMD_SESSION_ADD      "session_add"
+#define FST_CMD_SESSION_REMOVE   "session_remove"
+#define FST_CMD_SESSION_GET      "session_get"
+#define FST_CSG_PNAME_OLD_PEER_ADDR  "old_peer_addr" /* pval = address string */
+#define FST_CSG_PNAME_NEW_PEER_ADDR  "new_peer_addr" /* pval = address string */
+#define FST_CSG_PNAME_OLD_IFNAME "old_ifname" /* pval = ifname */
+#define FST_CSG_PNAME_NEW_IFNAME "new_ifname" /* pval = ifname */
+#define FST_CSG_PNAME_LLT        "llt"        /* pval = numeric llt value */
+#define FST_CSG_PNAME_STATE      "state"      /* pval = FST_CS_PVAL_STATE_... */
+#define FST_CMD_SESSION_SET      "session_set"
+#define FST_CSS_PNAME_OLD_PEER_ADDR  FST_CSG_PNAME_OLD_PEER_ADDR
+#define FST_CSS_PNAME_NEW_PEER_ADDR  FST_CSG_PNAME_NEW_PEER_ADDR
+#define FST_CSS_PNAME_OLD_IFNAME     FST_CSG_PNAME_OLD_IFNAME
+#define FST_CSS_PNAME_NEW_IFNAME     FST_CSG_PNAME_NEW_IFNAME
+#define FST_CSS_PNAME_LLT            FST_CSG_PNAME_LLT
+#define FST_CMD_SESSION_INITIATE "session_initiate"
+#define FST_CMD_SESSION_RESPOND  "session_respond"
+#define FST_CMD_SESSION_TRANSFER "session_transfer"
+#define FST_CMD_SESSION_TEARDOWN "session_teardown"
+
+#ifdef CONFIG_FST_TEST
+#define FST_CTR_PVAL_BAD_NEW_BAND        "bad_new_band"
+
+#define FST_CMD_TEST_REQUEST    "test_request"
+#define FST_CTR_IS_SUPPORTED        "is_supported"
+#define FST_CTR_SEND_SETUP_REQUEST  "send_setup_request"
+#define FST_CTR_SEND_SETUP_RESPONSE "send_setup_response"
+#define FST_CTR_SEND_ACK_REQUEST    "send_ack_request"
+#define FST_CTR_SEND_ACK_RESPONSE   "send_ack_response"
+#define FST_CTR_SEND_TEAR_DOWN      "send_tear_down"
+#define FST_CTR_GET_FSTS_ID         "get_fsts_id"
+#define FST_CTR_GET_LOCAL_MBIES     "get_local_mbies"
+#endif /* CONFIG_FST_TEST */
+
+/* Events */
+#define FST_CTRL_EVENT_IFACE "FST-EVENT-IFACE"
+#define FST_CEI_PNAME_IFNAME       "ifname"
+#define FST_CEI_PNAME_GROUP        "group"
+#define FST_CEI_PNAME_ATTACHED     "attached"
+#define FST_CEI_PNAME_DETACHED     "detached"
+#define FST_CTRL_EVENT_PEER "FST-EVENT-PEER"
+#define FST_CEP_PNAME_IFNAME       "ifname"
+#define FST_CEP_PNAME_ADDR         "peer_addr"
+#define FST_CEP_PNAME_CONNECTED    "connected"
+#define FST_CEP_PNAME_DISCONNECTED "disconnected"
+#define FST_CTRL_EVENT_SESSION "FST-EVENT-SESSION"
+#define FST_CES_PNAME_SESSION_ID "session_id"
+#define FST_CES_PNAME_EVT_TYPE   "event_type"
+#define FST_PVAL_EVT_TYPE_SESSION_STATE "EVENT_FST_SESSION_STATE"
+/* old_state/new_state: pval = FST_CS_PVAL_STATE_... */
+#define FST_CES_PNAME_OLD_STATE   "old_state"
+#define FST_CES_PNAME_NEW_STATE   "new_state"
+#define FST_CES_PNAME_REASON      "reason" /* pval = FST_CS_PVAL_REASON_... */
+#define FST_CES_PNAME_REJECT_CODE "reject_code" /* pval = u8 code */
+/* pval = FST_CS_PVAL_INITIATOR_... */
+#define FST_CES_PNAME_INITIATOR   "initiator"
+#define FST_PVAL_EVT_TYPE_ESTABLISHED "EVENT_FST_ESTABLISHED"
+#define FST_PVAL_EVT_TYPE_SETUP "EVENT_FST_SETUP"
+
+#endif /* FST_CTRL_DEFS_H */
diff --git a/hostap/src/fst/fst_ctrl_iface.c b/hostap/src/fst/fst_ctrl_iface.c
new file mode 100644
index 0000000..d090718
--- /dev/null
+++ b/hostap/src/fst/fst_ctrl_iface.c
@@ -0,0 +1,948 @@
+/*
+ * FST module - Control Interface implementation
+ * Copyright (c) 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 "common/defs.h"
+#include "list.h"
+#include "fst/fst.h"
+#include "fst/fst_internal.h"
+#include "fst_ctrl_defs.h"
+#include "fst_ctrl_iface.h"
+
+
+static struct fst_group * get_fst_group_by_id(const char *id)
+{
+	struct fst_group *g;
+
+	foreach_fst_group(g) {
+		const char *group_id = fst_group_get_id(g);
+
+		if (os_strncmp(group_id, id, os_strlen(group_id)) == 0)
+			return g;
+	}
+
+	return NULL;
+}
+
+
+/* notifications */
+static Boolean format_session_state_extra(const union fst_event_extra *extra,
+					  char *buffer, size_t size)
+{
+	int len;
+	char reject_str[32] = FST_CTRL_PVAL_NONE;
+	const char *initiator = FST_CTRL_PVAL_NONE;
+	const struct fst_event_extra_session_state *ss;
+
+	ss = &extra->session_state;
+	if (ss->new_state != FST_SESSION_STATE_INITIAL)
+		return TRUE;
+
+	switch (ss->extra.to_initial.reason) {
+	case REASON_REJECT:
+		if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS)
+			os_snprintf(reject_str, sizeof(reject_str), "%u",
+				    ss->extra.to_initial.reject_code);
+		/* no break */
+	case REASON_TEARDOWN:
+	case REASON_SWITCH:
+		switch (ss->extra.to_initial.initiator) {
+		case FST_INITIATOR_LOCAL:
+			initiator = FST_CS_PVAL_INITIATOR_LOCAL;
+			break;
+		case FST_INITIATOR_REMOTE:
+			initiator = FST_CS_PVAL_INITIATOR_REMOTE;
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+
+	len = os_snprintf(buffer, size,
+			  FST_CES_PNAME_REASON "=%s "
+			  FST_CES_PNAME_REJECT_CODE "=%s "
+			  FST_CES_PNAME_INITIATOR "=%s",
+			  fst_reason_name(ss->extra.to_initial.reason),
+			  reject_str, initiator);
+
+	return !os_snprintf_error(size, len);
+}
+
+
+static void fst_ctrl_iface_notify(struct fst_iface *f, u32 session_id,
+				  enum fst_event_type event_type,
+				  const union fst_event_extra *extra)
+{
+	struct fst_group *g;
+	char extra_str[128] = "";
+	const struct fst_event_extra_session_state *ss;
+	const struct fst_event_extra_iface_state *is;
+	const struct fst_event_extra_peer_state *ps;
+
+	/*
+	 * FST can use any of interface objects as it only sends messages
+	 * on global Control Interface, so we just pick the 1st one.
+	 */
+
+	if (!f) {
+		foreach_fst_group(g) {
+			f = fst_group_first_iface(g);
+			if (f)
+				break;
+		}
+		if (!f)
+			return;
+	}
+
+	WPA_ASSERT(f->iface_obj.ctx);
+
+	switch (event_type) {
+	case EVENT_FST_IFACE_STATE_CHANGED:
+		if (!extra)
+			return;
+		is = &extra->iface_state;
+		wpa_msg_global_only(f->iface_obj.ctx, MSG_INFO,
+				    FST_CTRL_EVENT_IFACE " %s "
+				    FST_CEI_PNAME_IFNAME "=%s "
+				    FST_CEI_PNAME_GROUP "=%s",
+				    is->attached ? FST_CEI_PNAME_ATTACHED :
+				    FST_CEI_PNAME_DETACHED,
+				    is->ifname, is->group_id);
+		break;
+	case EVENT_PEER_STATE_CHANGED:
+		if (!extra)
+			return;
+		ps = &extra->peer_state;
+		wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
+				    FST_CTRL_EVENT_PEER " %s "
+				    FST_CEP_PNAME_IFNAME "=%s "
+				    FST_CEP_PNAME_ADDR "=" MACSTR,
+				    ps->connected ? FST_CEP_PNAME_CONNECTED :
+				    FST_CEP_PNAME_DISCONNECTED,
+				    ps->ifname, MAC2STR(ps->addr));
+		break;
+	case EVENT_FST_SESSION_STATE_CHANGED:
+		if (!extra)
+			return;
+		if (!format_session_state_extra(extra, extra_str,
+						sizeof(extra_str))) {
+			fst_printf(MSG_ERROR,
+				   "CTRL: Cannot format STATE_CHANGE extra");
+			extra_str[0] = 0;
+		}
+		ss = &extra->session_state;
+		wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
+				    FST_CTRL_EVENT_SESSION " "
+				    FST_CES_PNAME_SESSION_ID "=%u "
+				    FST_CES_PNAME_EVT_TYPE "=%s "
+				    FST_CES_PNAME_OLD_STATE "=%s "
+				    FST_CES_PNAME_NEW_STATE "=%s %s",
+				    session_id,
+				    fst_session_event_type_name(event_type),
+				    fst_session_state_name(ss->old_state),
+				    fst_session_state_name(ss->new_state),
+				    extra_str);
+		break;
+	case EVENT_FST_ESTABLISHED:
+	case EVENT_FST_SETUP:
+		wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
+				    FST_CTRL_EVENT_SESSION " "
+				    FST_CES_PNAME_SESSION_ID "=%u "
+				    FST_CES_PNAME_EVT_TYPE "=%s",
+				    session_id,
+				    fst_session_event_type_name(event_type));
+		break;
+	}
+}
+
+
+/* command processors */
+
+/* fst session_get */
+static int session_get(const char *session_id, char *buf, size_t buflen)
+{
+	struct fst_session *s;
+	struct fst_iface *new_iface, *old_iface;
+	const u8 *old_peer_addr, *new_peer_addr;
+	u32 id;
+
+	id = strtoul(session_id, NULL, 0);
+
+	s = fst_session_get_by_id(id);
+	if (!s) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	old_peer_addr = fst_session_get_peer_addr(s, TRUE);
+	new_peer_addr = fst_session_get_peer_addr(s, FALSE);
+	new_iface = fst_session_get_iface(s, FALSE);
+	old_iface = fst_session_get_iface(s, TRUE);
+
+	return os_snprintf(buf, buflen,
+			   FST_CSG_PNAME_OLD_PEER_ADDR "=" MACSTR "\n"
+			   FST_CSG_PNAME_NEW_PEER_ADDR "=" MACSTR "\n"
+			   FST_CSG_PNAME_NEW_IFNAME "=%s\n"
+			   FST_CSG_PNAME_OLD_IFNAME "=%s\n"
+			   FST_CSG_PNAME_LLT "=%u\n"
+			   FST_CSG_PNAME_STATE "=%s\n",
+			   MAC2STR(old_peer_addr),
+			   MAC2STR(new_peer_addr),
+			   new_iface ? fst_iface_get_name(new_iface) :
+			   FST_CTRL_PVAL_NONE,
+			   old_iface ? fst_iface_get_name(old_iface) :
+			   FST_CTRL_PVAL_NONE,
+			   fst_session_get_llt(s),
+			   fst_session_state_name(fst_session_get_state(s)));
+}
+
+
+/* fst session_set */
+static int session_set(const char *session_id, char *buf, size_t buflen)
+{
+	struct fst_session *s;
+	char *p, *q;
+	u32 id;
+	int ret;
+
+	id = strtoul(session_id, &p, 0);
+
+	s = fst_session_get_by_id(id);
+	if (!s) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	if (*p != ' ' || !(q = os_strchr(p + 1, '=')))
+		return os_snprintf(buf, buflen, "FAIL\n");
+	p++;
+
+	if (os_strncasecmp(p, FST_CSS_PNAME_OLD_IFNAME, q - p) == 0) {
+		ret = fst_session_set_str_ifname(s, q + 1, TRUE);
+	} else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_IFNAME, q - p) == 0) {
+		ret = fst_session_set_str_ifname(s, q + 1, FALSE);
+	} else if (os_strncasecmp(p, FST_CSS_PNAME_OLD_PEER_ADDR, q - p) == 0) {
+		ret = fst_session_set_str_peer_addr(s, q + 1, TRUE);
+	} else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_PEER_ADDR, q - p) == 0) {
+		ret = fst_session_set_str_peer_addr(s, q + 1, FALSE);
+	} else if (os_strncasecmp(p, FST_CSS_PNAME_LLT, q - p) == 0) {
+		ret = fst_session_set_str_llt(s, q + 1);
+	} else {
+		fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
+}
+
+
+/* fst session_add/remove */
+static int session_add(const char *group_id, char *buf, size_t buflen)
+{
+	struct fst_group *g;
+	struct fst_session *s;
+
+	g = get_fst_group_by_id(group_id);
+	if (!g) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
+			   group_id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	s = fst_session_create(g);
+	if (!s) {
+		fst_printf(MSG_ERROR,
+			   "CTRL: Cannot create session for group '%s'",
+			   group_id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	return os_snprintf(buf, buflen, "%u\n", fst_session_get_id(s));
+}
+
+
+static int session_remove(const char *session_id, char *buf, size_t buflen)
+{
+	struct fst_session *s;
+	struct fst_group *g;
+	u32 id;
+
+	id = strtoul(session_id, NULL, 0);
+
+	s = fst_session_get_by_id(id);
+	if (!s) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	g = fst_session_get_group(s);
+	fst_session_reset(s);
+	fst_session_delete(s);
+	fst_group_delete_if_empty(g);
+
+	return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+/* fst session_initiate */
+static int session_initiate(const char *session_id, char *buf, size_t buflen)
+{
+	struct fst_session *s;
+	u32 id;
+
+	id = strtoul(session_id, NULL, 0);
+
+	s = fst_session_get_by_id(id);
+	if (!s) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	if (fst_session_initiate_setup(s)) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot initiate session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+/* fst session_respond */
+static int session_respond(const char *session_id, char *buf, size_t buflen)
+{
+	struct fst_session *s;
+	char *p;
+	u32 id;
+	u8 status_code;
+
+	id = strtoul(session_id, &p, 0);
+
+	s = fst_session_get_by_id(id);
+	if (!s) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	if (*p != ' ')
+		return os_snprintf(buf, buflen, "FAIL\n");
+	p++;
+
+	if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_ACCEPT)) {
+		status_code = WLAN_STATUS_SUCCESS;
+	} else if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_REJECT)) {
+		status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
+	} else {
+		fst_printf(MSG_WARNING,
+			   "CTRL: session %u: unknown response status: %s",
+			   id, p);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	if (fst_session_respond(s, status_code)) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot respond to session %u",
+			   id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	fst_printf(MSG_INFO, "CTRL: session %u responded", id);
+
+	return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+/* fst session_transfer */
+static int session_transfer(const char *session_id, char *buf, size_t buflen)
+{
+	struct fst_session *s;
+	u32 id;
+
+	id = strtoul(session_id, NULL, 0);
+
+	s = fst_session_get_by_id(id);
+	if (!s) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	if (fst_session_initiate_switch(s)) {
+		fst_printf(MSG_WARNING,
+			   "CTRL: Cannot initiate ST for session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+/* fst session_teardown */
+static int session_teardown(const char *session_id, char *buf, size_t buflen)
+{
+	struct fst_session *s;
+	u32 id;
+
+	id = strtoul(session_id, NULL, 0);
+
+	s = fst_session_get_by_id(id);
+	if (!s) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	if (fst_session_tear_down_setup(s)) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot tear down session %u",
+			   id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+#ifdef CONFIG_FST_TEST
+/* fst test_request */
+static int test_request(const char *request, char *buf, size_t buflen)
+{
+	const char *p = request;
+	int ret;
+
+	if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_REQUEST,
+			    os_strlen(FST_CTR_SEND_SETUP_REQUEST))) {
+		ret = fst_test_req_send_fst_request(
+			p + os_strlen(FST_CTR_SEND_SETUP_REQUEST));
+	} else if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_RESPONSE,
+				   os_strlen(FST_CTR_SEND_SETUP_RESPONSE))) {
+		ret = fst_test_req_send_fst_response(
+			p + os_strlen(FST_CTR_SEND_SETUP_RESPONSE));
+	} else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_REQUEST,
+				   os_strlen(FST_CTR_SEND_ACK_REQUEST))) {
+		ret = fst_test_req_send_ack_request(
+			p + os_strlen(FST_CTR_SEND_ACK_REQUEST));
+	} else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_RESPONSE,
+				   os_strlen(FST_CTR_SEND_ACK_RESPONSE))) {
+		ret = fst_test_req_send_ack_response(
+			p + os_strlen(FST_CTR_SEND_ACK_RESPONSE));
+	} else if (!os_strncasecmp(p, FST_CTR_SEND_TEAR_DOWN,
+				   os_strlen(FST_CTR_SEND_TEAR_DOWN))) {
+		ret = fst_test_req_send_tear_down(
+			p + os_strlen(FST_CTR_SEND_TEAR_DOWN));
+	} else if (!os_strncasecmp(p, FST_CTR_GET_FSTS_ID,
+				   os_strlen(FST_CTR_GET_FSTS_ID))) {
+		u32 fsts_id = fst_test_req_get_fsts_id(
+			p + os_strlen(FST_CTR_GET_FSTS_ID));
+		if (fsts_id != FST_FSTS_ID_NOT_FOUND)
+			return os_snprintf(buf, buflen, "%u\n", fsts_id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	} else if (!os_strncasecmp(p, FST_CTR_GET_LOCAL_MBIES,
+				   os_strlen(FST_CTR_GET_LOCAL_MBIES))) {
+		return fst_test_req_get_local_mbies(
+			p + os_strlen(FST_CTR_GET_LOCAL_MBIES), buf, buflen);
+	} else if (!os_strncasecmp(p, FST_CTR_IS_SUPPORTED,
+				   os_strlen(FST_CTR_IS_SUPPORTED))) {
+		ret = 0;
+	} else {
+		fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
+}
+#endif /* CONFIG_FST_TEST */
+
+
+/* fst list_sessions */
+struct list_sessions_cb_ctx {
+	char *buf;
+	size_t buflen;
+	size_t reply_len;
+};
+
+
+static void list_session_enum_cb(struct fst_group *g, struct fst_session *s,
+				 void *ctx)
+{
+	struct list_sessions_cb_ctx *c = ctx;
+	int ret;
+
+	ret = os_snprintf(c->buf, c->buflen, " %u", fst_session_get_id(s));
+
+	c->buf += ret;
+	c->buflen -= ret;
+	c->reply_len += ret;
+}
+
+
+static int list_sessions(const char *group_id, char *buf, size_t buflen)
+{
+	struct list_sessions_cb_ctx ctx;
+	struct fst_group *g;
+
+	g = get_fst_group_by_id(group_id);
+	if (!g) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
+			   group_id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	ctx.buf = buf;
+	ctx.buflen = buflen;
+	ctx.reply_len = 0;
+
+	fst_session_enum(g, list_session_enum_cb, &ctx);
+
+	ctx.reply_len += os_snprintf(buf + ctx.reply_len, ctx.buflen, "\n");
+
+	return ctx.reply_len;
+}
+
+
+/* fst iface_peers */
+static int iface_peers(const char *group_id, char *buf, size_t buflen)
+{
+	const char *ifname;
+	struct fst_group *g;
+	struct fst_iface *f;
+	struct fst_get_peer_ctx *ctx;
+	const u8 *addr;
+	unsigned found = 0;
+	int ret = 0;
+
+	g = get_fst_group_by_id(group_id);
+	if (!g) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
+			   group_id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	ifname = os_strchr(group_id, ' ');
+	if (!ifname)
+		return os_snprintf(buf, buflen, "FAIL\n");
+	ifname++;
+
+	foreach_fst_group_iface(g, f) {
+		const char *in = fst_iface_get_name(f);
+
+		if (os_strncmp(ifname, in, os_strlen(in)) == 0) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found)
+		return os_snprintf(buf, buflen, "FAIL\n");
+
+	addr = fst_iface_get_peer_first(f, &ctx, FALSE);
+	for (; addr != NULL; addr = fst_iface_get_peer_next(f, &ctx, FALSE)) {
+		int res;
+
+		res = os_snprintf(buf + ret, buflen - ret, MACSTR "\n",
+				  MAC2STR(addr));
+		if (os_snprintf_error(buflen - ret, res))
+			break;
+		ret += res;
+	}
+
+	return ret;
+}
+
+
+static int get_peer_mbies(const char *params, char *buf, size_t buflen)
+{
+	char *endp;
+	char ifname[FST_MAX_INTERFACE_SIZE];
+	u8 peer_addr[ETH_ALEN];
+	struct fst_group *g;
+	struct fst_iface *iface = NULL;
+	const struct wpabuf *mbies;
+
+	if (fst_read_next_text_param(params, ifname, sizeof(ifname), &endp) ||
+	    !*ifname)
+		goto problem;
+
+	while (isspace(*endp))
+		endp++;
+	if (fst_read_peer_addr(endp, peer_addr))
+		goto problem;
+
+	foreach_fst_group(g) {
+		iface = fst_group_get_iface_by_name(g, ifname);
+		if (iface)
+			break;
+	}
+	if (!iface)
+		goto problem;
+
+	mbies = fst_iface_get_peer_mb_ie(iface, peer_addr);
+	if (!mbies)
+		goto problem;
+
+	return wpa_snprintf_hex(buf, buflen, wpabuf_head(mbies),
+				wpabuf_len(mbies));
+
+problem:
+	return os_snprintf(buf, buflen, "FAIL\n");
+}
+
+
+/* fst list_ifaces */
+static int list_ifaces(const char *group_id, char *buf, size_t buflen)
+{
+	struct fst_group *g;
+	struct fst_iface *f;
+	int ret = 0;
+
+	g = get_fst_group_by_id(group_id);
+	if (!g) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
+			   group_id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	foreach_fst_group_iface(g, f) {
+		int res;
+		const u8 *iface_addr = fst_iface_get_addr(f);
+
+		res = os_snprintf(buf + ret, buflen - ret,
+				  "%s|" MACSTR "|%u|%u\n",
+				  fst_iface_get_name(f),
+				  MAC2STR(iface_addr),
+				  fst_iface_get_priority(f),
+				  fst_iface_get_llt(f));
+		if (os_snprintf_error(buflen - ret, res))
+			break;
+		ret += res;
+	}
+
+	return ret;
+}
+
+
+/* fst list_groups */
+static int list_groups(const char *cmd, char *buf, size_t buflen)
+{
+	struct fst_group *g;
+	int ret = 0;
+
+	foreach_fst_group(g) {
+		int res;
+
+		res = os_snprintf(buf + ret, buflen - ret, "%s\n",
+				  fst_group_get_id(g));
+		if (os_snprintf_error(buflen - ret, res))
+			break;
+		ret += res;
+	}
+
+	return ret;
+}
+
+
+static const char * band_freq(enum mb_band_id band)
+{
+	static const char *band_names[] = {
+		[MB_BAND_ID_WIFI_2_4GHZ] "2.4GHZ",
+		[MB_BAND_ID_WIFI_5GHZ] "5GHZ",
+		[MB_BAND_ID_WIFI_60GHZ] "60GHZ",
+	};
+
+	return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names));
+}
+
+
+static int print_band(unsigned num, struct fst_iface *iface, const u8 *addr,
+		      char *buf, size_t buflen)
+{
+	const struct wpabuf *wpabuf;
+	enum hostapd_hw_mode hw_mode;
+	u8 channel;
+	int ret = 0;
+
+	fst_iface_get_channel_info(iface, &hw_mode, &channel);
+
+	ret += os_snprintf(buf + ret, buflen - ret, "band%u_frequency=%s\n",
+			   num, band_freq(fst_hw_mode_to_band(hw_mode)));
+	ret += os_snprintf(buf + ret, buflen - ret, "band%u_iface=%s\n",
+			   num, fst_iface_get_name(iface));
+	wpabuf = fst_iface_get_peer_mb_ie(iface, addr);
+	if (wpabuf) {
+		ret += os_snprintf(buf + ret, buflen - ret, "band%u_mb_ies=",
+				   num);
+		ret += wpa_snprintf_hex(buf + ret, buflen - ret,
+					wpabuf_head(wpabuf),
+					wpabuf_len(wpabuf));
+		ret += os_snprintf(buf + ret, buflen - ret, "\n");
+	}
+	ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_group_id=%s\n",
+			   num, fst_iface_get_group_id(iface));
+	ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_priority=%u\n",
+			   num, fst_iface_get_priority(iface));
+	ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_llt=%u\n",
+			   num, fst_iface_get_llt(iface));
+
+	return ret;
+}
+
+
+static void fst_ctrl_iface_on_iface_state_changed(struct fst_iface *i,
+						  Boolean attached)
+{
+	union fst_event_extra extra;
+
+	os_memset(&extra, 0, sizeof(extra));
+	extra.iface_state.attached = attached;
+	os_strlcpy(extra.iface_state.ifname, fst_iface_get_name(i),
+		   sizeof(extra.iface_state.ifname));
+	os_strlcpy(extra.iface_state.group_id, fst_iface_get_group_id(i),
+		   sizeof(extra.iface_state.group_id));
+
+	fst_ctrl_iface_notify(i, FST_INVALID_SESSION_ID,
+			      EVENT_FST_IFACE_STATE_CHANGED, &extra);
+}
+
+
+static int fst_ctrl_iface_on_iface_added(struct fst_iface *i)
+{
+	fst_ctrl_iface_on_iface_state_changed(i, TRUE);
+	return 0;
+}
+
+
+static void fst_ctrl_iface_on_iface_removed(struct fst_iface *i)
+{
+	fst_ctrl_iface_on_iface_state_changed(i, FALSE);
+}
+
+
+static void fst_ctrl_iface_on_event(enum fst_event_type event_type,
+				    struct fst_iface *i, struct fst_session *s,
+				    const union fst_event_extra *extra)
+{
+	u32 session_id = s ? fst_session_get_id(s) : FST_INVALID_SESSION_ID;
+
+	fst_ctrl_iface_notify(i, session_id, event_type, extra);
+}
+
+
+static const struct fst_ctrl ctrl_cli = {
+	.on_iface_added = fst_ctrl_iface_on_iface_added,
+	.on_iface_removed =  fst_ctrl_iface_on_iface_removed,
+	.on_event = fst_ctrl_iface_on_event,
+};
+
+const struct fst_ctrl *fst_ctrl_cli = &ctrl_cli;
+
+
+int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
+{
+	struct fst_group *g;
+	struct fst_iface *f;
+	unsigned num = 0;
+	int ret = 0;
+
+	foreach_fst_group(g) {
+		foreach_fst_group_iface(g, f) {
+			if (fst_iface_is_connected(f, addr)) {
+				ret += print_band(num++, f, addr,
+						  buf + ret, buflen - ret);
+			}
+		}
+	}
+
+	return ret;
+}
+
+
+/* fst ctrl processor */
+int fst_ctrl_iface_receive(const char *cmd, char *reply, size_t reply_size)
+{
+	static const struct fst_command {
+		const char *name;
+		unsigned has_param;
+		int (*process)(const char *group_id, char *buf, size_t buflen);
+	} commands[] = {
+		{ FST_CMD_LIST_GROUPS, 0, list_groups},
+		{ FST_CMD_LIST_IFACES, 1, list_ifaces},
+		{ FST_CMD_IFACE_PEERS, 1, iface_peers},
+		{ FST_CMD_GET_PEER_MBIES, 1, get_peer_mbies},
+		{ FST_CMD_LIST_SESSIONS, 1, list_sessions},
+		{ FST_CMD_SESSION_ADD, 1, session_add},
+		{ FST_CMD_SESSION_REMOVE, 1, session_remove},
+		{ FST_CMD_SESSION_GET, 1, session_get},
+		{ FST_CMD_SESSION_SET, 1, session_set},
+		{ FST_CMD_SESSION_INITIATE, 1, session_initiate},
+		{ FST_CMD_SESSION_RESPOND, 1, session_respond},
+		{ FST_CMD_SESSION_TRANSFER, 1, session_transfer},
+		{ FST_CMD_SESSION_TEARDOWN, 1, session_teardown},
+#ifdef CONFIG_FST_TEST
+		{ FST_CMD_TEST_REQUEST, 1, test_request },
+#endif /* CONFIG_FST_TEST */
+		{ NULL, 0, NULL }
+	};
+	const struct fst_command *c;
+	const char *p;
+	const char *temp;
+	Boolean non_spaces_found;
+
+	for (c = commands; c->name; c++) {
+		if (os_strncasecmp(cmd, c->name, os_strlen(c->name)) != 0)
+			continue;
+		p = cmd + os_strlen(c->name);
+		if (c->has_param) {
+			if (!isspace(p[0]))
+				return os_snprintf(reply, reply_size, "FAIL\n");
+			p++;
+			temp = p;
+			non_spaces_found = FALSE;
+			while (*temp) {
+				if (!isspace(*temp)) {
+					non_spaces_found = TRUE;
+					break;
+				}
+				temp++;
+			}
+			if (!non_spaces_found)
+				return os_snprintf(reply, reply_size, "FAIL\n");
+		}
+		return c->process(p, reply, reply_size);
+	}
+
+	return os_snprintf(reply, reply_size, "UNKNOWN FST COMMAND\n");
+}
+
+
+int fst_read_next_int_param(const char *params, Boolean *valid, char **endp)
+{
+	int ret = -1;
+	const char *curp;
+
+	*valid = FALSE;
+	*endp = (char *) params;
+	curp = params;
+	if (*curp) {
+		ret = (int) strtol(curp, endp, 0);
+		if (!**endp || isspace(**endp))
+			*valid = TRUE;
+	}
+
+	return ret;
+}
+
+
+int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
+			     char **endp)
+{
+	size_t max_chars_to_copy;
+	char *cur_dest;
+
+	*endp = (char *) params;
+	while (isspace(**endp))
+		(*endp)++;
+	if (!**endp || buflen <= 1)
+		return -EINVAL;
+
+	max_chars_to_copy = buflen - 1;
+	/* We need 1 byte for the terminating zero */
+	cur_dest = buf;
+	while (**endp && !isspace(**endp) && max_chars_to_copy > 0) {
+		*cur_dest = **endp;
+		(*endp)++;
+		cur_dest++;
+		max_chars_to_copy--;
+	}
+	*cur_dest = 0;
+
+	return 0;
+}
+
+
+int fst_read_peer_addr(const char *mac, u8 *peer_addr)
+{
+	if (hwaddr_aton(mac, peer_addr)) {
+		fst_printf(MSG_WARNING, "Bad peer_mac %s: invalid addr string",
+			   mac);
+		return -1;
+	}
+
+	if (is_zero_ether_addr(peer_addr) ||
+	    is_multicast_ether_addr(peer_addr)) {
+		fst_printf(MSG_WARNING, "Bad peer_mac %s: not a unicast addr",
+			   mac);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
+			     struct fst_iface_cfg *cfg)
+{
+	char *pos;
+	char *endp;
+	Boolean is_valid;
+	int val;
+
+	if (fst_read_next_text_param(cmd, ifname, ifname_size, &endp) ||
+	    fst_read_next_text_param(endp, cfg->group_id, sizeof(cfg->group_id),
+				     &endp))
+		return -EINVAL;
+
+	cfg->llt = FST_DEFAULT_LLT_CFG_VALUE;
+	cfg->priority = 0;
+	pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_LLT);
+	if (pos) {
+		pos += os_strlen(FST_ATTACH_CMD_PNAME_LLT);
+		if (*pos == '=') {
+			val = fst_read_next_int_param(pos + 1, &is_valid,
+						      &endp);
+			if (is_valid)
+				cfg->llt = val;
+		}
+	}
+	pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_PRIORITY);
+	if (pos) {
+		pos += os_strlen(FST_ATTACH_CMD_PNAME_PRIORITY);
+		if (*pos == '=') {
+			val = fst_read_next_int_param(pos + 1, &is_valid,
+						      &endp);
+			if (is_valid)
+				cfg->priority = (u8) val;
+		}
+	}
+
+	return 0;
+}
+
+
+int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size)
+{
+	char *endp;
+
+	return fst_read_next_text_param(cmd, ifname, ifname_size, &endp);
+}
+
+
+int fst_iface_detach(const char *ifname)
+{
+	struct fst_group *g;
+
+	foreach_fst_group(g) {
+		struct fst_iface *f;
+
+		f = fst_group_get_iface_by_name(g, ifname);
+		if (f) {
+			fst_detach(f);
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
diff --git a/hostap/src/fst/fst_ctrl_iface.h b/hostap/src/fst/fst_ctrl_iface.h
new file mode 100644
index 0000000..4d0cd9f
--- /dev/null
+++ b/hostap/src/fst/fst_ctrl_iface.h
@@ -0,0 +1,45 @@
+/*
+ * FST module - internal Control interface 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 FST_CTRL_IFACE_H
+#define FST_CTRL_IFACE_H
+
+#include "fst/fst_ctrl_aux.h"
+
+#ifdef CONFIG_FST
+
+/* receiver */
+int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen);
+
+int fst_ctrl_iface_receive(const char *txtaddr, char *buf, size_t buflen);
+
+extern const struct fst_ctrl *fst_ctrl_cli;
+
+#else /* CONFIG_FST */
+
+static inline int
+fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
+{
+	return 0;
+}
+
+#endif /* CONFIG_FST */
+
+int fst_read_next_int_param(const char *params, Boolean *valid, char **endp);
+int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
+			     char **endp);
+int fst_read_peer_addr(const char *mac, u8 *peer_addr);
+
+struct fst_iface_cfg;
+
+int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
+			     struct fst_iface_cfg *cfg);
+int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size);
+int fst_iface_detach(const char *ifname);
+
+#endif /* CTRL_IFACE_FST_H */
diff --git a/hostap/src/fst/fst_defs.h b/hostap/src/fst/fst_defs.h
new file mode 100644
index 0000000..8ddcc61
--- /dev/null
+++ b/hostap/src/fst/fst_defs.h
@@ -0,0 +1,87 @@
+/*
+ * FST module - FST related 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 IEEE_80211_FST_DEFS_H
+#define IEEE_80211_FST_DEFS_H
+
+/* IEEE Std 802.11ad */
+
+#define MB_STA_CHANNEL_ALL 0
+
+enum session_type {
+	SESSION_TYPE_BSS = 0, /*  Infrastructure BSS */
+	SESSION_TYPE_IBSS = 1,
+	SESSION_TYPE_DLS = 2,
+	SESSION_TYPE_TDLS = 3,
+	SESSION_TYPE_PBSS = 4
+};
+
+#define SESSION_CONTROL(session_type, switch_intent) \
+	(((u8) ((session_type) & 0x7)) | ((switch_intent) ? 0x10 : 0x00))
+
+#define GET_SESSION_CONTROL_TYPE(session_control) \
+	((u8) ((session_control) & 0x7))
+
+#define GET_SESSION_CONTROL_SWITCH_INTENT(session_control) \
+	(((session_control) & 0x10) >> 4)
+
+/* 8.4.2.147  Session Transition element */
+struct session_transition_ie {
+	u8 element_id;
+	u8 length;
+	u32 fsts_id;
+	u8 session_control;
+	u8 new_band_id;
+	u8 new_band_setup;
+	u8 new_band_op;
+	u8 old_band_id;
+	u8 old_band_setup;
+	u8 old_band_op;
+} STRUCT_PACKED;
+
+struct fst_setup_req {
+	u8 action;
+	u8 dialog_token;
+	u32 llt;
+	struct session_transition_ie stie;
+	/* Multi-band (optional) */
+	/* Wakeup Schedule (optional) */
+	/* Awake Window (optional) */
+	/* Switching Stream (optional) */
+} STRUCT_PACKED;
+
+struct fst_setup_res {
+	u8 action;
+	u8 dialog_token;
+	u8 status_code;
+	struct session_transition_ie stie;
+	/* Multi-band (optional) */
+	/* Wakeup Schedule (optional) */
+	/* Awake Window (optional) */
+	/* Switching Stream (optional) */
+	/* Timeout Interval (optional) */
+} STRUCT_PACKED;
+
+struct fst_ack_req {
+	u8 action;
+	u8 dialog_token;
+	u32 fsts_id;
+} STRUCT_PACKED;
+
+struct fst_ack_res {
+	u8 action;
+	u8 dialog_token;
+	u32 fsts_id;
+} STRUCT_PACKED;
+
+struct fst_tear_down {
+	u8 action;
+	u32 fsts_id;
+} STRUCT_PACKED;
+
+#endif /* IEEE_80211_FST_DEFS_H */
diff --git a/hostap/src/fst/fst_group.c b/hostap/src/fst/fst_group.c
new file mode 100644
index 0000000..f6c7be9
--- /dev/null
+++ b/hostap/src/fst/fst_group.c
@@ -0,0 +1,462 @@
+/*
+ * FST module - FST group object implementation
+ * Copyright (c) 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 "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "drivers/driver.h"
+#include "fst/fst_internal.h"
+#include "fst/fst_defs.h"
+
+
+struct dl_list fst_global_groups_list;
+
+#ifndef HOSTAPD
+static Boolean fst_has_fst_peer(struct fst_iface *iface, Boolean *has_peer)
+{
+	const u8 *bssid;
+
+	bssid = fst_iface_get_bssid(iface);
+	if (!bssid) {
+		*has_peer = FALSE;
+		return FALSE;
+	}
+
+	*has_peer = TRUE;
+	return fst_iface_get_peer_mb_ie(iface, bssid) != NULL;
+}
+#endif /* HOSTAPD */
+
+
+static void fst_dump_mb_ies(const char *group_id, const char *ifname,
+			    struct wpabuf *mbies)
+{
+	const u8 *p = wpabuf_head(mbies);
+	size_t s = wpabuf_len(mbies);
+
+	while (s >= 2) {
+		const struct multi_band_ie *mbie =
+			(const struct multi_band_ie *) p;
+		WPA_ASSERT(mbie->eid == WLAN_EID_MULTI_BAND);
+		WPA_ASSERT(2 + mbie->len >= sizeof(*mbie));
+
+		fst_printf(MSG_WARNING,
+			   "%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid="
+			   MACSTR
+			   " beacon_int=%u tsf_offs=[%u %u %u %u %u %u %u %u] mb_cc=0x%02x tmout=%u",
+			   group_id, ifname,
+			   mbie->mb_ctrl, mbie->band_id, mbie->op_class,
+			   mbie->chan, MAC2STR(mbie->bssid), mbie->beacon_int,
+			   mbie->tsf_offs[0], mbie->tsf_offs[1],
+			   mbie->tsf_offs[2], mbie->tsf_offs[3],
+			   mbie->tsf_offs[4], mbie->tsf_offs[5],
+			   mbie->tsf_offs[6], mbie->tsf_offs[7],
+			   mbie->mb_connection_capability,
+			   mbie->fst_session_tmout);
+
+		p += 2 + mbie->len;
+		s -= 2 + mbie->len;
+	}
+}
+
+
+static void fst_fill_mb_ie(struct wpabuf *buf, const u8 *bssid,
+			   const u8 *own_addr, enum mb_band_id band, u8 channel)
+{
+	struct multi_band_ie *mbie;
+	size_t len = sizeof(*mbie);
+
+	if (own_addr)
+		len += ETH_ALEN;
+
+	mbie = wpabuf_put(buf, len);
+
+	os_memset(mbie, 0, len);
+
+	mbie->eid = WLAN_EID_MULTI_BAND;
+	mbie->len = len - 2;
+#ifdef HOSTAPD
+	mbie->mb_ctrl = MB_STA_ROLE_AP;
+	mbie->mb_connection_capability = MB_CONNECTION_CAPABILITY_AP;
+#else /* HOSTAPD */
+	mbie->mb_ctrl = MB_STA_ROLE_NON_PCP_NON_AP;
+	mbie->mb_connection_capability = 0;
+#endif /* HOSTAPD */
+	if (bssid)
+		os_memcpy(mbie->bssid, bssid, ETH_ALEN);
+	mbie->band_id = band;
+	mbie->op_class = 0;  /* means all */
+	mbie->chan = channel;
+	mbie->fst_session_tmout = FST_DEFAULT_SESSION_TIMEOUT_TU;
+
+	if (own_addr) {
+		mbie->mb_ctrl |= MB_CTRL_STA_MAC_PRESENT;
+		os_memcpy(&mbie[1], own_addr, ETH_ALEN);
+	}
+}
+
+
+static unsigned fst_fill_iface_mb_ies(struct fst_iface *f, struct wpabuf *buf)
+{
+	const  u8 *bssid;
+
+	bssid = fst_iface_get_bssid(f);
+	if (bssid) {
+		enum hostapd_hw_mode hw_mode;
+		u8 channel;
+
+		if (buf) {
+			fst_iface_get_channel_info(f, &hw_mode, &channel);
+			fst_fill_mb_ie(buf, bssid, fst_iface_get_addr(f),
+				       fst_hw_mode_to_band(hw_mode), channel);
+		}
+		return 1;
+	} else {
+		unsigned bands[MB_BAND_ID_WIFI_60GHZ + 1] = {};
+		struct hostapd_hw_modes *modes;
+		enum mb_band_id b;
+		int num_modes = fst_iface_get_hw_modes(f, &modes);
+		int ret = 0;
+
+		while (num_modes--) {
+			b = fst_hw_mode_to_band(modes->mode);
+			modes++;
+			if (b >= ARRAY_SIZE(bands) || bands[b]++)
+				continue;
+			ret++;
+			if (buf)
+				fst_fill_mb_ie(buf, NULL, fst_iface_get_addr(f),
+					       b, MB_STA_CHANNEL_ALL);
+		}
+		return ret;
+	}
+}
+
+
+static struct wpabuf * fst_group_create_mb_ie(struct fst_group *g,
+					      struct fst_iface *i)
+{
+	struct wpabuf *buf;
+	struct fst_iface *f;
+	unsigned int nof_mbies = 0;
+	unsigned int nof_ifaces_added = 0;
+#ifndef HOSTAPD
+	Boolean has_peer;
+	Boolean has_fst_peer;
+
+	foreach_fst_group_iface(g, f) {
+		has_fst_peer = fst_has_fst_peer(f, &has_peer);
+		if (has_peer && !has_fst_peer)
+			return NULL;
+	}
+#endif /* HOSTAPD */
+
+	foreach_fst_group_iface(g, f) {
+		if (f == i)
+			continue;
+		nof_mbies += fst_fill_iface_mb_ies(f, NULL);
+	}
+
+	buf = wpabuf_alloc(nof_mbies *
+			   (sizeof(struct multi_band_ie) + ETH_ALEN));
+	if (!buf) {
+		fst_printf_iface(i, MSG_ERROR,
+				 "cannot allocate mem for %u MB IEs",
+				 nof_mbies);
+		return NULL;
+	}
+
+	/* The list is sorted in descending order by priorities, so MB IEs will
+	 * be arranged in the same order, as required by spec (see corresponding
+	 * comment in.fst_attach().
+	 */
+	foreach_fst_group_iface(g, f) {
+		if (f == i)
+			continue;
+
+		fst_fill_iface_mb_ies(f, buf);
+		++nof_ifaces_added;
+
+		fst_printf_iface(i, MSG_DEBUG, "added to MB IE");
+	}
+
+	if (!nof_ifaces_added) {
+		wpabuf_free(buf);
+		buf = NULL;
+		fst_printf_iface(i, MSG_INFO,
+				 "cannot add MB IE: no backup ifaces");
+	} else {
+		fst_dump_mb_ies(fst_group_get_id(g), fst_iface_get_name(i),
+				buf);
+	}
+
+	return buf;
+}
+
+
+static const u8 * fst_mbie_get_peer_addr(const struct multi_band_ie *mbie)
+{
+	const u8 *peer_addr = NULL;
+
+	switch (MB_CTRL_ROLE(mbie->mb_ctrl)) {
+	case MB_STA_ROLE_AP:
+		peer_addr = mbie->bssid;
+		break;
+	case MB_STA_ROLE_NON_PCP_NON_AP:
+		if (mbie->mb_ctrl & MB_CTRL_STA_MAC_PRESENT &&
+		    (size_t) 2 + mbie->len >= sizeof(*mbie) + ETH_ALEN)
+			peer_addr = (const u8 *) &mbie[1];
+		break;
+	default:
+		break;
+	}
+
+	return peer_addr;
+}
+
+
+static struct fst_iface *
+fst_group_get_new_iface_by_mbie_and_band_id(struct fst_group *g,
+					    const u8 *mb_ies_buff,
+					    size_t mb_ies_size,
+					    u8 band_id,
+					    u8 *iface_peer_addr)
+{
+	while (mb_ies_size >= 2) {
+		const struct multi_band_ie *mbie =
+			(const struct multi_band_ie *) mb_ies_buff;
+
+		if (mbie->eid != WLAN_EID_MULTI_BAND ||
+		    (size_t) 2 + mbie->len < sizeof(*mbie))
+			break;
+
+		if (mbie->band_id == band_id) {
+			struct fst_iface *iface;
+
+			foreach_fst_group_iface(g, iface) {
+				const u8 *peer_addr =
+					fst_mbie_get_peer_addr(mbie);
+
+				if (peer_addr &&
+				    fst_iface_is_connected(iface, peer_addr) &&
+				    band_id == fst_iface_get_band_id(iface)) {
+					os_memcpy(iface_peer_addr, peer_addr,
+						  ETH_ALEN);
+					return iface;
+				}
+			}
+			break;
+		}
+
+		mb_ies_buff += 2 + mbie->len;
+		mb_ies_size -= 2 + mbie->len;
+	}
+
+	return NULL;
+}
+
+
+struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g,
+					       const char *ifname)
+{
+	struct fst_iface *f;
+
+	foreach_fst_group_iface(g, f) {
+		const char *in = fst_iface_get_name(f);
+
+		if (os_strncmp(in, ifname, os_strlen(in)) == 0)
+			return f;
+	}
+
+	return NULL;
+}
+
+
+u8 fst_group_assign_dialog_token(struct fst_group *g)
+{
+	g->dialog_token++;
+	if (g->dialog_token == 0)
+		g->dialog_token++;
+	return g->dialog_token;
+}
+
+
+u32 fst_group_assign_fsts_id(struct fst_group *g)
+{
+	g->fsts_id++;
+	return g->fsts_id;
+}
+
+
+static Boolean
+fst_group_does_iface_appear_in_other_mbies(struct fst_group *g,
+					   struct fst_iface *iface,
+					   struct fst_iface *other,
+					   u8 *peer_addr)
+{
+	struct fst_get_peer_ctx *ctx;
+	const u8 *addr;
+	const u8 *iface_addr;
+	enum mb_band_id  iface_band_id;
+
+	WPA_ASSERT(g == fst_iface_get_group(iface));
+	WPA_ASSERT(g == fst_iface_get_group(other));
+
+	iface_addr = fst_iface_get_addr(iface);
+	iface_band_id = fst_iface_get_band_id(iface);
+
+	addr = fst_iface_get_peer_first(other, &ctx, TRUE);
+	for (; addr; addr = fst_iface_get_peer_next(other, &ctx, TRUE)) {
+		const struct wpabuf *mbies;
+		u8 other_iface_peer_addr[ETH_ALEN];
+		struct fst_iface *other_new_iface;
+
+		mbies = fst_iface_get_peer_mb_ie(other, addr);
+		if (!mbies)
+			continue;
+
+		other_new_iface = fst_group_get_new_iface_by_mbie_and_band_id(
+			g, wpabuf_head(mbies), wpabuf_len(mbies),
+			iface_band_id, other_iface_peer_addr);
+		if (other_new_iface == iface &&
+		    os_memcmp(iface_addr, other_iface_peer_addr,
+			      ETH_ALEN) != 0) {
+			os_memcpy(peer_addr, addr, ETH_ALEN);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+
+struct fst_iface *
+fst_group_find_new_iface_by_stie(struct fst_group *g,
+				 struct fst_iface *iface,
+				 const u8 *peer_addr,
+				 const struct session_transition_ie *stie,
+				 u8 *iface_peer_addr)
+{
+	struct fst_iface *i;
+
+	foreach_fst_group_iface(g, i) {
+		if (i == iface ||
+		    stie->new_band_id != fst_iface_get_band_id(i))
+			continue;
+		if (fst_group_does_iface_appear_in_other_mbies(g, iface, i,
+			iface_peer_addr))
+			return i;
+		break;
+	}
+	return NULL;
+}
+
+
+struct fst_iface *
+fst_group_get_new_iface_by_stie_and_mbie(
+	struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size,
+	const struct session_transition_ie *stie, u8 *iface_peer_addr)
+{
+	return fst_group_get_new_iface_by_mbie_and_band_id(
+		g, mb_ies_buff, mb_ies_size, stie->new_band_id,
+		iface_peer_addr);
+}
+
+
+struct fst_group * fst_group_create(const char *group_id)
+{
+	struct fst_group *g;
+
+	g = os_zalloc(sizeof(*g));
+	if (g == NULL) {
+		fst_printf(MSG_ERROR, "%s: Cannot alloc group", group_id);
+		return NULL;
+	}
+
+	dl_list_init(&g->ifaces);
+	os_strlcpy(g->group_id, group_id, sizeof(g->group_id));
+
+	dl_list_add_tail(&fst_global_groups_list, &g->global_groups_lentry);
+	fst_printf_group(g, MSG_DEBUG, "instance created");
+
+	foreach_fst_ctrl_call(on_group_created, g);
+
+	return g;
+}
+
+
+void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i)
+{
+	struct dl_list *list = &g->ifaces;
+	struct fst_iface *f;
+
+	/*
+	 * Add new interface to the list.
+	 * The list is sorted in descending order by priority to allow
+	 * multiple MB IEs creation according to the spec (see 10.32 Multi-band
+	 * operation, 10.32.1 General), as they should be ordered according to
+	 * priorities.
+	 */
+	foreach_fst_group_iface(g, f) {
+		if (fst_iface_get_priority(f) < fst_iface_get_priority(i))
+			break;
+		list = &f->group_lentry;
+	}
+	dl_list_add(list, &i->group_lentry);
+}
+
+
+void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i)
+{
+	dl_list_del(&i->group_lentry);
+}
+
+
+void fst_group_delete(struct fst_group *group)
+{
+	struct fst_session *s;
+
+	dl_list_del(&group->global_groups_lentry);
+	WPA_ASSERT(dl_list_empty(&group->ifaces));
+	foreach_fst_ctrl_call(on_group_deleted, group);
+	fst_printf_group(group, MSG_DEBUG, "instance deleted");
+	while ((s = fst_session_global_get_first_by_group(group)) != NULL)
+		fst_session_delete(s);
+	os_free(group);
+}
+
+
+Boolean fst_group_delete_if_empty(struct fst_group *group)
+{
+	Boolean is_empty = !fst_group_has_ifaces(group) &&
+		!fst_session_global_get_first_by_group(group);
+
+	if (is_empty)
+		fst_group_delete(group);
+
+	return is_empty;
+}
+
+
+void fst_group_update_ie(struct fst_group *g)
+{
+	struct fst_iface *i;
+
+	foreach_fst_group_iface(g, i) {
+		struct wpabuf *mbie = fst_group_create_mb_ie(g, i);
+
+		if (!mbie)
+			fst_printf_iface(i, MSG_WARNING, "cannot create MB IE");
+
+		fst_iface_attach_mbie(i, mbie);
+		fst_iface_set_ies(i, mbie);
+		fst_printf_iface(i, MSG_DEBUG, "multi-band IE set to %p", mbie);
+	}
+}
diff --git a/hostap/src/fst/fst_group.h b/hostap/src/fst/fst_group.h
new file mode 100644
index 0000000..3a87c0b
--- /dev/null
+++ b/hostap/src/fst/fst_group.h
@@ -0,0 +1,75 @@
+/*
+ * FST module - FST group object 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 FST_GROUP_H
+#define FST_GROUP_H
+
+struct fst_group {
+	char group_id[IFNAMSIZ + 1];
+	struct dl_list ifaces;
+	u8 dialog_token;
+	u32 fsts_id;
+	struct dl_list global_groups_lentry;
+};
+
+struct session_transition_ie;
+
+#define foreach_fst_group_iface(g, i) \
+	dl_list_for_each((i), &(g)->ifaces, struct fst_iface, group_lentry)
+
+struct fst_group * fst_group_create(const char *group_id);
+void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i);
+void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i);
+void fst_group_delete(struct fst_group *g);
+
+void fst_group_update_ie(struct fst_group *g);
+
+static inline Boolean fst_group_has_ifaces(struct fst_group *g)
+{
+	return !dl_list_empty(&g->ifaces);
+}
+
+static inline struct fst_iface * fst_group_first_iface(struct fst_group *g)
+{
+	return dl_list_first(&g->ifaces, struct fst_iface, group_lentry);
+}
+
+static inline const char * fst_group_get_id(struct fst_group *g)
+{
+	return g->group_id;
+}
+
+Boolean fst_group_delete_if_empty(struct fst_group *group);
+struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g,
+					       const char *ifname);
+struct fst_iface *
+fst_group_find_new_iface_by_stie(struct fst_group *g,
+				 struct fst_iface *iface,
+				 const u8 *peer_addr,
+				 const struct session_transition_ie *stie,
+				 u8 *iface_peer_addr);
+struct fst_iface *
+fst_group_get_new_iface_by_stie_and_mbie(
+	struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size,
+	const struct session_transition_ie *stie, u8 *iface_peer_addr);
+u8  fst_group_assign_dialog_token(struct fst_group *g);
+u32 fst_group_assign_fsts_id(struct fst_group *g);
+
+extern struct dl_list fst_global_groups_list;
+
+#define foreach_fst_group(g) \
+	dl_list_for_each((g), &fst_global_groups_list, \
+			 struct fst_group, global_groups_lentry)
+
+static inline struct fst_group * fst_first_group(void)
+{
+	return dl_list_first(&fst_global_groups_list, struct fst_group,
+			     global_groups_lentry);
+}
+
+#endif /* FST_GROUP_H */
diff --git a/hostap/src/fst/fst_iface.c b/hostap/src/fst/fst_iface.c
new file mode 100644
index 0000000..5a92d2c
--- /dev/null
+++ b/hostap/src/fst/fst_iface.c
@@ -0,0 +1,79 @@
+/*
+ * FST module - FST interface object implementation
+ * Copyright (c) 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 "fst/fst_internal.h"
+#include "fst/fst_defs.h"
+
+
+struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname,
+				    const u8 *own_addr,
+				    const struct fst_wpa_obj *iface_obj,
+				    const struct fst_iface_cfg *cfg)
+{
+	struct fst_iface *i;
+
+	i = os_zalloc(sizeof(*i));
+	if (!i) {
+		fst_printf_group(g, MSG_ERROR, "cannot allocate iface for %s",
+				ifname);
+		return NULL;
+	}
+
+	i->cfg = *cfg;
+	i->iface_obj = *iface_obj;
+	i->group = g;
+	os_strlcpy(i->ifname, ifname, sizeof(i->ifname));
+	os_memcpy(i->own_addr, own_addr, sizeof(i->own_addr));
+
+	if (!i->cfg.llt) {
+		fst_printf_iface(i, MSG_WARNING, "Zero llt adjusted");
+		i->cfg.llt = FST_DEFAULT_LLT_CFG_VALUE;
+	}
+
+	return i;
+}
+
+
+void fst_iface_delete(struct fst_iface *i)
+{
+	fst_iface_set_ies(i, NULL);
+	wpabuf_free(i->mb_ie);
+	os_free(i);
+}
+
+
+Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr)
+{
+	struct fst_get_peer_ctx *ctx;
+	const u8 *a = fst_iface_get_peer_first(iface, &ctx, TRUE);
+
+	for (; a != NULL; a = fst_iface_get_peer_next(iface, &ctx, TRUE))
+		if (os_memcmp(addr, a, ETH_ALEN) == 0)
+			return TRUE;
+
+	return FALSE;
+}
+
+
+void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie)
+{
+	wpabuf_free(i->mb_ie);
+	i->mb_ie = mbie;
+}
+
+
+enum mb_band_id fst_iface_get_band_id(struct fst_iface *i)
+{
+	enum hostapd_hw_mode hw_mode;
+	u8 channel;
+
+	fst_iface_get_channel_info(i, &hw_mode, &channel);
+	return fst_hw_mode_to_band(hw_mode);
+}
diff --git a/hostap/src/fst/fst_iface.h b/hostap/src/fst/fst_iface.h
new file mode 100644
index 0000000..4670d89
--- /dev/null
+++ b/hostap/src/fst/fst_iface.h
@@ -0,0 +1,135 @@
+/*
+ * FST module - FST interface object 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 FST_IFACE_H
+#define FST_IFACE_H
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "list.h"
+#include "fst.h"
+
+struct fst_iface {
+	struct fst_group *group;
+	struct fst_wpa_obj iface_obj;
+	u8 own_addr[ETH_ALEN];
+	struct wpabuf *mb_ie;
+	char ifname[IFNAMSIZ + 1];
+	struct fst_iface_cfg cfg;
+	struct dl_list group_lentry;
+};
+
+struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname,
+				    const u8 *own_addr,
+				    const struct fst_wpa_obj *iface_obj,
+				    const struct fst_iface_cfg *cfg);
+void fst_iface_delete(struct fst_iface *i);
+
+static inline struct fst_group * fst_iface_get_group(struct fst_iface *i)
+{
+	return i->group;
+}
+
+static inline const char * fst_iface_get_name(struct fst_iface *i)
+{
+	return i->ifname;
+}
+
+static inline const u8 * fst_iface_get_addr(struct fst_iface *i)
+{
+	return i->own_addr;
+}
+
+static inline const char * fst_iface_get_group_id(struct fst_iface *i)
+{
+	return i->cfg.group_id;
+}
+
+static inline u8 fst_iface_get_priority(struct fst_iface *i)
+{
+	return i->cfg.priority;
+}
+
+static inline u32 fst_iface_get_llt(struct fst_iface *i)
+{
+	return i->cfg.llt;
+}
+
+static inline const struct wpabuf * fst_iface_get_mbie(struct fst_iface *i)
+{
+	return i->mb_ie;
+}
+
+static inline const u8 * fst_iface_get_bssid(struct fst_iface *i)
+{
+	return i->iface_obj.get_bssid(i->iface_obj.ctx);
+}
+
+static inline void fst_iface_get_channel_info(struct fst_iface *i,
+					      enum hostapd_hw_mode *hw_mode,
+					      u8 *channel)
+{
+	i->iface_obj.get_channel_info(i->iface_obj.ctx, hw_mode, channel);
+}
+
+static inline int fst_iface_get_hw_modes(struct fst_iface *i,
+					 struct hostapd_hw_modes **modes)
+{
+	return i->iface_obj.get_hw_modes(i->iface_obj.ctx, modes);
+}
+
+static inline void fst_iface_set_ies(struct fst_iface *i,
+				     const struct wpabuf *fst_ies)
+{
+	i->iface_obj.set_ies(i->iface_obj.ctx, fst_ies);
+}
+
+static inline int fst_iface_send_action(struct fst_iface *i,
+					const u8 *addr, struct wpabuf *data)
+{
+	return i->iface_obj.send_action(i->iface_obj.ctx, addr, data);
+}
+
+static inline const struct wpabuf *
+fst_iface_get_peer_mb_ie(struct fst_iface *i, const u8 *addr)
+{
+	return i->iface_obj.get_mb_ie(i->iface_obj.ctx, addr);
+}
+
+static inline void fst_iface_update_mb_ie(struct fst_iface *i,
+					  const u8 *addr,
+					  const u8 *buf, size_t size)
+{
+	return i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size);
+}
+
+static inline const u8 * fst_iface_get_peer_first(struct fst_iface *i,
+						  struct fst_get_peer_ctx **ctx,
+						  Boolean mb_only)
+{
+	return i->iface_obj.get_peer_first(i->iface_obj.ctx, ctx, mb_only);
+}
+
+static inline const u8 * fst_iface_get_peer_next(struct fst_iface *i,
+						 struct fst_get_peer_ctx **ctx,
+						 Boolean mb_only)
+{
+	return i->iface_obj.get_peer_next(i->iface_obj.ctx, ctx, mb_only);
+}
+
+Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr);
+void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie);
+enum mb_band_id fst_iface_get_band_id(struct fst_iface *i);
+
+static inline void * fst_iface_get_wpa_obj_ctx(struct fst_iface *i)
+{
+	return i->iface_obj.ctx;
+}
+
+#endif /* FST_IFACE_H */
diff --git a/hostap/src/fst/fst_internal.h b/hostap/src/fst/fst_internal.h
new file mode 100644
index 0000000..9fe32b8
--- /dev/null
+++ b/hostap/src/fst/fst_internal.h
@@ -0,0 +1,49 @@
+/*
+ * FST module - auxiliary 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 FST_INTERNAL_H
+#define FST_INTERNAL_H
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "fst/fst_iface.h"
+#include "fst/fst_group.h"
+#include "fst/fst_session.h"
+
+#define fst_printf(level, format, ...) \
+	wpa_printf((level), "FST: " format, ##__VA_ARGS__)
+
+#define fst_printf_group(group, level, format, ...) \
+	wpa_printf((level), "FST: %s: " format, \
+		   fst_group_get_id(group), ##__VA_ARGS__)
+
+#define fst_printf_iface(iface, level, format, ...) \
+	fst_printf_group(fst_iface_get_group(iface), (level), "%s: " format, \
+			 fst_iface_get_name(iface), ##__VA_ARGS__)
+
+enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode);
+
+struct fst_ctrl_handle {
+	struct fst_ctrl ctrl;
+	struct dl_list global_ctrls_lentry;
+};
+
+extern struct dl_list fst_global_ctrls_list;
+
+#define foreach_fst_ctrl_call(clb, ...) \
+	do { \
+		struct fst_ctrl_handle *__fst_ctrl_h; \
+		dl_list_for_each(__fst_ctrl_h, &fst_global_ctrls_list, \
+			struct fst_ctrl_handle, global_ctrls_lentry) \
+			if (__fst_ctrl_h->ctrl.clb) \
+				__fst_ctrl_h->ctrl.clb(__VA_ARGS__);\
+	} while (0)
+
+#endif /* FST_INTERNAL_H */
diff --git a/hostap/src/fst/fst_session.c b/hostap/src/fst/fst_session.c
new file mode 100644
index 0000000..55fa694
--- /dev/null
+++ b/hostap/src/fst/fst_session.c
@@ -0,0 +1,1620 @@
+/*
+ * FST module - FST Session implementation
+ * Copyright (c) 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 "fst/fst_internal.h"
+#include "fst/fst_defs.h"
+#include "fst/fst_ctrl_iface.h"
+#ifdef CONFIG_FST_TEST
+#include "fst/fst_ctrl_defs.h"
+#endif /* CONFIG_FST_TEST */
+
+#define US_80211_TU 1024
+
+#define US_TO_TU(m) ((m) * / US_80211_TU)
+#define TU_TO_US(m) ((m) * US_80211_TU)
+
+#define FST_LLT_SWITCH_IMMEDIATELY 0
+
+#define fst_printf_session(s, level, format, ...) \
+	fst_printf((level), "%u (0x%08x): [" MACSTR "," MACSTR "] :" format, \
+		   (s)->id, (s)->data.fsts_id, \
+		   MAC2STR((s)->data.old_peer_addr), \
+		   MAC2STR((s)->data.new_peer_addr), \
+		   ##__VA_ARGS__)
+
+#define fst_printf_siface(s, iface, level, format, ...) \
+	fst_printf_session((s), (level), "%s: " format, \
+			   fst_iface_get_name(iface), ##__VA_ARGS__)
+
+#define fst_printf_sframe(s, is_old, level, format, ...) \
+	fst_printf_siface((s), \
+		(is_old) ? (s)->data.old_iface : (s)->data.new_iface, \
+		(level), format, ##__VA_ARGS__)
+
+#define FST_LLT_MS_DEFAULT 50
+#define FST_ACTION_MAX_SUPPORTED   FST_ACTION_ON_CHANNEL_TUNNEL
+
+const char * const fst_action_names[] = {
+	[FST_ACTION_SETUP_REQUEST]     = "Setup Request",
+	[FST_ACTION_SETUP_RESPONSE]    = "Setup Response",
+	[FST_ACTION_TEAR_DOWN]         = "Tear Down",
+	[FST_ACTION_ACK_REQUEST]       = "Ack Request",
+	[FST_ACTION_ACK_RESPONSE]      = "Ack Response",
+	[FST_ACTION_ON_CHANNEL_TUNNEL] = "On Channel Tunnel",
+};
+
+struct fst_session {
+	struct {
+		/* Session configuration that can be zeroed on reset */
+		u8 old_peer_addr[ETH_ALEN];
+		u8 new_peer_addr[ETH_ALEN];
+		struct fst_iface *new_iface;
+		struct fst_iface *old_iface;
+		u32 llt_ms;
+		u8 pending_setup_req_dlgt;
+		u32 fsts_id; /* FSTS ID, see spec, 8.4.2.147
+			      * Session Transition element */
+	} data;
+	/* Session object internal fields which won't be zeroed on reset */
+	struct dl_list global_sessions_lentry;
+	u32 id; /* Session object ID used to identify
+		 * specific session object */
+	struct fst_group *group;
+	enum fst_session_state state;
+	Boolean stt_armed;
+};
+
+static struct dl_list global_sessions_list;
+static u32 global_session_id = 0;
+
+#define foreach_fst_session(s) \
+	dl_list_for_each((s), &global_sessions_list, \
+			 struct fst_session, global_sessions_lentry)
+
+#define foreach_fst_session_safe(s, temp) \
+	dl_list_for_each_safe((s), (temp), &global_sessions_list, \
+			      struct fst_session, global_sessions_lentry)
+
+
+static void fst_session_global_inc_id(void)
+{
+	global_session_id++;
+	if (global_session_id == FST_INVALID_SESSION_ID)
+		global_session_id++;
+}
+
+
+int fst_session_global_init(void)
+{
+	dl_list_init(&global_sessions_list);
+	return 0;
+}
+
+
+void fst_session_global_deinit(void)
+{
+	WPA_ASSERT(dl_list_empty(&global_sessions_list));
+}
+
+
+static inline void fst_session_notify_ctrl(struct fst_session *s,
+					   enum fst_event_type event_type,
+					   union fst_event_extra *extra)
+{
+	foreach_fst_ctrl_call(on_event, event_type, NULL, s, extra);
+}
+
+
+static void fst_session_set_state(struct fst_session *s,
+				  enum fst_session_state state,
+				  union fst_session_state_switch_extra *extra)
+{
+	if (s->state != state) {
+		union fst_event_extra evext = {
+			.session_state = {
+				.old_state = s->state,
+				.new_state = state,
+			},
+		};
+
+		if (extra)
+			evext.session_state.extra = *extra;
+		fst_session_notify_ctrl(s, EVENT_FST_SESSION_STATE_CHANGED,
+					&evext);
+		fst_printf_session(s, MSG_INFO, "State: %s => %s",
+				   fst_session_state_name(s->state),
+				   fst_session_state_name(state));
+		s->state = state;
+	}
+}
+
+
+static u32 fst_find_free_session_id(void)
+{
+	u32 i, id = FST_INVALID_SESSION_ID;
+	struct fst_session *s;
+
+	for (i = 0; i < (u32) -1; i++) {
+		Boolean in_use = FALSE;
+
+		foreach_fst_session(s) {
+			if (s->id == global_session_id) {
+				fst_session_global_inc_id();
+				in_use = TRUE;
+				break;
+			}
+		}
+		if (!in_use) {
+			id = global_session_id;
+			fst_session_global_inc_id();
+			break;
+		}
+	}
+
+	return id;
+}
+
+
+static void fst_session_timeout_handler(void *eloop_data, void *user_ctx)
+{
+	struct fst_session *s = user_ctx;
+	union fst_session_state_switch_extra extra = {
+		.to_initial = {
+			.reason = REASON_STT,
+		},
+	};
+
+	fst_printf_session(s, MSG_WARNING, "Session State Timeout");
+	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &extra);
+}
+
+
+static void fst_session_stt_arm(struct fst_session *s)
+{
+	eloop_register_timeout(0, TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU),
+			       fst_session_timeout_handler, NULL, s);
+	s->stt_armed = TRUE;
+}
+
+
+static void fst_session_stt_disarm(struct fst_session *s)
+{
+	if (s->stt_armed) {
+		eloop_cancel_timeout(fst_session_timeout_handler, NULL, s);
+		s->stt_armed = FALSE;
+	}
+}
+
+
+static Boolean fst_session_is_in_transition(struct fst_session *s)
+{
+	/* See spec, 10.32.2.2  Transitioning between states */
+	return s->stt_armed;
+}
+
+
+static int fst_session_is_in_progress(struct fst_session *s)
+{
+	return s->state != FST_SESSION_STATE_INITIAL;
+}
+
+
+static int fst_session_is_ready_pending(struct fst_session *s)
+{
+	return s->state == FST_SESSION_STATE_SETUP_COMPLETION &&
+		fst_session_is_in_transition(s);
+}
+
+
+static int fst_session_is_ready(struct fst_session *s)
+{
+	return s->state == FST_SESSION_STATE_SETUP_COMPLETION &&
+		!fst_session_is_in_transition(s);
+}
+
+
+static int fst_session_is_switch_requested(struct fst_session *s)
+{
+	return s->state == FST_SESSION_STATE_TRANSITION_DONE &&
+		fst_session_is_in_transition(s);
+}
+
+
+static struct fst_session *
+fst_find_session_in_progress(const u8 *peer_addr, struct fst_group *g)
+{
+	struct fst_session *s;
+
+	foreach_fst_session(s) {
+		if (s->group == g &&
+		    (os_memcmp(s->data.old_peer_addr, peer_addr,
+			       ETH_ALEN) == 0 ||
+		     os_memcmp(s->data.new_peer_addr, peer_addr,
+			       ETH_ALEN) == 0) &&
+		    fst_session_is_in_progress(s))
+			return s;
+	}
+
+	return NULL;
+}
+
+
+static void fst_session_reset_ex(struct fst_session *s, enum fst_reason reason)
+{
+	union fst_session_state_switch_extra evext = {
+		.to_initial = {
+			.reason = reason,
+		},
+	};
+
+	if (s->state == FST_SESSION_STATE_SETUP_COMPLETION ||
+	    s->state == FST_SESSION_STATE_TRANSITION_DONE)
+		fst_session_tear_down_setup(s);
+	fst_session_stt_disarm(s);
+	os_memset(&s->data, 0, sizeof(s->data));
+	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+}
+
+
+static int fst_session_send_action(struct fst_session *s, Boolean old_iface,
+				   const void *payload, size_t size,
+				   const struct wpabuf *extra_buf)
+{
+	size_t len;
+	int res;
+	struct wpabuf *buf;
+	u8 action;
+	struct fst_iface *iface =
+		old_iface ? s->data.old_iface : s->data.new_iface;
+
+	WPA_ASSERT(payload != NULL);
+	WPA_ASSERT(size != 0);
+
+	action = *(const u8 *) payload;
+
+	WPA_ASSERT(action <= FST_ACTION_MAX_SUPPORTED);
+
+	if (!iface) {
+		fst_printf_session(s, MSG_ERROR,
+				   "no %s interface for FST Action '%s' sending",
+				   old_iface ? "old" : "new",
+				   fst_action_names[action]);
+		return -1;
+	}
+
+	len = sizeof(u8) /* category */ + size;
+	if (extra_buf)
+		len += wpabuf_size(extra_buf);
+
+	buf = wpabuf_alloc(len);
+	if (!buf) {
+		fst_printf_session(s, MSG_ERROR,
+				   "cannot allocate buffer of %zu bytes for FST Action '%s' sending",
+				   len, fst_action_names[action]);
+		return -1;
+	}
+
+	wpabuf_put_u8(buf, WLAN_ACTION_FST);
+	wpabuf_put_data(buf, payload, size);
+	if (extra_buf)
+		wpabuf_put_buf(buf, extra_buf);
+
+	res = fst_iface_send_action(iface,
+				    old_iface ? s->data.old_peer_addr :
+				    s->data.new_peer_addr, buf);
+	if (res < 0)
+		fst_printf_siface(s, iface, MSG_ERROR,
+				  "failed to send FST Action '%s'",
+				  fst_action_names[action]);
+	else
+		fst_printf_siface(s, iface, MSG_DEBUG, "FST Action '%s' sent",
+				  fst_action_names[action]);
+	wpabuf_free(buf);
+
+	return res;
+}
+
+
+static int fst_session_send_tear_down(struct fst_session *s)
+{
+	struct fst_tear_down td;
+	int res;
+
+	if (!fst_session_is_in_progress(s)) {
+		fst_printf_session(s, MSG_ERROR, "No FST setup to tear down");
+		return -1;
+	}
+
+	WPA_ASSERT(s->data.old_iface != NULL);
+	WPA_ASSERT(s->data.new_iface != NULL);
+
+	os_memset(&td, 0, sizeof(td));
+
+	td.action = FST_ACTION_TEAR_DOWN;
+	td.fsts_id = host_to_le32(s->data.fsts_id);
+
+	res = fst_session_send_action(s, TRUE, &td, sizeof(td), NULL);
+	if (!res)
+		fst_printf_sframe(s, TRUE, MSG_INFO, "FST TearDown sent");
+	else
+		fst_printf_sframe(s, TRUE, MSG_ERROR,
+				  "failed to send FST TearDown");
+
+	return res;
+}
+
+
+static void fst_session_handle_setup_request(struct fst_iface *iface,
+					     const struct ieee80211_mgmt *mgmt,
+					     size_t frame_len)
+{
+	struct fst_session *s;
+	const struct fst_setup_req *req;
+	struct fst_iface *new_iface = NULL;
+	struct fst_group *g;
+	u8 new_iface_peer_addr[ETH_ALEN];
+	const struct wpabuf *peer_mbies;
+	size_t plen;
+
+	if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req))  {
+		fst_printf_iface(iface, MSG_WARNING,
+				 "FST Request dropped: too short (%zu < %zu)",
+				 frame_len,
+				 IEEE80211_HDRLEN + 1 + sizeof(*req));
+		return;
+	}
+	plen = frame_len - IEEE80211_HDRLEN - 1;
+	req = (const struct fst_setup_req *)
+		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+	if (req->stie.element_id != WLAN_EID_SESSION_TRANSITION ||
+	    req->stie.length < 11) {
+		fst_printf_iface(iface, MSG_WARNING,
+				 "FST Request dropped: invalid STIE");
+		return;
+	}
+
+	if (req->stie.new_band_id == req->stie.old_band_id) {
+		fst_printf_iface(iface, MSG_WARNING,
+				 "FST Request dropped: new and old band IDs are the same");
+		return;
+	}
+
+	g = fst_iface_get_group(iface);
+
+	if (plen > sizeof(*req)) {
+		fst_iface_update_mb_ie(iface, mgmt->sa, (const u8 *) (req + 1),
+				       plen - sizeof(*req));
+		fst_printf_iface(iface, MSG_INFO,
+				 "FST Request: MB IEs updated for " MACSTR,
+				 MAC2STR(mgmt->sa));
+	}
+
+	peer_mbies = fst_iface_get_peer_mb_ie(iface, mgmt->sa);
+	if (peer_mbies) {
+		new_iface = fst_group_get_new_iface_by_stie_and_mbie(
+			g, wpabuf_head(peer_mbies), wpabuf_len(peer_mbies),
+			&req->stie, new_iface_peer_addr);
+		if (new_iface)
+			fst_printf_iface(iface, MSG_INFO,
+					 "FST Request: new iface (%s:" MACSTR
+					 ") found by MB IEs",
+					 fst_iface_get_name(new_iface),
+					 MAC2STR(new_iface_peer_addr));
+	}
+
+	if (!new_iface) {
+		new_iface = fst_group_find_new_iface_by_stie(
+			g, iface, mgmt->sa, &req->stie,
+			new_iface_peer_addr);
+		if (new_iface)
+			fst_printf_iface(iface, MSG_INFO,
+					 "FST Request: new iface (%s:" MACSTR
+					 ") found by others",
+					 fst_iface_get_name(new_iface),
+					 MAC2STR(new_iface_peer_addr));
+	}
+
+	if (!new_iface) {
+		fst_printf_iface(iface, MSG_WARNING,
+				 "FST Request dropped: new iface not found");
+		return;
+	}
+
+	s = fst_find_session_in_progress(mgmt->sa, g);
+	if (s) {
+		union fst_session_state_switch_extra evext = {
+			.to_initial = {
+				.reason = REASON_SETUP,
+			},
+		};
+
+		/*
+		 * 10.32.2.2  Transitioning between states:
+		 * Upon receipt of an FST Setup Request frame, the responder
+		 * shall respond with an FST Setup Response frame unless it has
+		 * a pending FST Setup Request frame addressed to the initiator
+		 * and the responder has a numerically larger MAC address than
+		 * the initiator’s MAC address, in which case, the responder
+		 * shall delete the received FST Setup Request.
+		 */
+		if (os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) {
+			fst_printf_session(s, MSG_WARNING,
+					   "FST Request dropped due to MAC comparison (our MAC is "
+					   MACSTR ")",
+					   MAC2STR(mgmt->da));
+			return;
+		}
+
+		if (!fst_session_is_ready_pending(s)) {
+			fst_printf_session(s, MSG_WARNING,
+					   "FST Request from " MACSTR
+					   " dropped due to inappropriate state %s",
+					   MAC2STR(mgmt->da),
+					   fst_session_state_name(s->state));
+			return;
+		}
+
+
+		/*
+		 * If FST Setup Request arrived with the same FSTS ID as one we
+		 * initialized before, it means the other side either didn't
+		 * receive our FST Request or skipped it for some reason (for
+		 * example, due to numerical MAC comparison).
+		 *
+		 * In this case, there's no need to tear down the session.
+		 * Moreover, as FSTS ID is the same, the other side will
+		 * associate this tear down with the session it initiated that
+		 * will break the sync.
+		 */
+		if (le_to_host32(req->stie.fsts_id) != s->data.fsts_id)
+			fst_session_send_tear_down(s);
+		else
+			fst_printf_session(s, MSG_WARNING,
+					   "Skipping TearDown as the FST request has the same FSTS ID as initiated");
+		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+		fst_session_stt_disarm(s);
+		fst_printf_session(s, MSG_WARNING, "reset due to FST request");
+	}
+
+	s = fst_session_create(g);
+	if (!s) {
+		fst_printf(MSG_WARNING,
+			   "FST Request dropped: cannot create session for %s and %s",
+			   fst_iface_get_name(iface),
+			   fst_iface_get_name(new_iface));
+		return;
+	}
+
+	fst_session_set_iface(s, iface, TRUE);
+	fst_session_set_peer_addr(s, mgmt->sa, TRUE);
+	fst_session_set_iface(s, new_iface, FALSE);
+	fst_session_set_peer_addr(s, new_iface_peer_addr, FALSE);
+	fst_session_set_llt(s, FST_LLT_VAL_TO_MS(le_to_host32(req->llt)));
+	s->data.pending_setup_req_dlgt = req->dialog_token;
+	s->data.fsts_id = le_to_host32(req->stie.fsts_id);
+
+	fst_session_stt_arm(s);
+
+	fst_session_notify_ctrl(s, EVENT_FST_SETUP, NULL);
+
+	fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, NULL);
+}
+
+
+static void fst_session_handle_setup_response(struct fst_session *s,
+					      struct fst_iface *iface,
+					      const struct ieee80211_mgmt *mgmt,
+					      size_t frame_len)
+{
+	const struct fst_setup_res *res;
+	size_t plen = frame_len - IEEE80211_HDRLEN - 1;
+	enum hostapd_hw_mode hw_mode;
+	u8 channel;
+	union fst_session_state_switch_extra evext = {
+		.to_initial = {0},
+	};
+
+	if (iface != s->data.old_iface) {
+		fst_printf_session(s, MSG_WARNING,
+				   "FST Response dropped: %s is not the old iface",
+				   fst_iface_get_name(iface));
+		return;
+	}
+
+	if (!fst_session_is_ready_pending(s)) {
+		fst_printf_session(s, MSG_WARNING,
+				   "FST Response dropped due to wrong state: %s",
+				   fst_session_state_name(s->state));
+		return;
+	}
+
+	if (plen < sizeof(*res)) {
+		fst_printf_session(s, MSG_WARNING,
+				   "Too short FST Response dropped");
+		return;
+	}
+	res = (const struct fst_setup_res *)
+		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+	if (res->stie.element_id != WLAN_EID_SESSION_TRANSITION ||
+	    res->stie.length < 11) {
+		fst_printf_iface(iface, MSG_WARNING,
+				 "FST Response dropped: invalid STIE");
+		return;
+	}
+
+	if (res->dialog_token != s->data.pending_setup_req_dlgt)  {
+		fst_printf_session(s, MSG_WARNING,
+				   "FST Response dropped due to wrong dialog token (%u != %u)",
+				   s->data.pending_setup_req_dlgt,
+				   res->dialog_token);
+		return;
+	}
+
+	if (res->status_code == WLAN_STATUS_SUCCESS &&
+	    le_to_host32(res->stie.fsts_id) != s->data.fsts_id) {
+		fst_printf_session(s, MSG_WARNING,
+				   "FST Response dropped due to wrong FST Session ID (%u)",
+				   le_to_host32(res->stie.fsts_id));
+		return;
+	}
+
+	fst_session_stt_disarm(s);
+
+	if (res->status_code != WLAN_STATUS_SUCCESS) {
+		/*
+		 * 10.32.2.2  Transitioning between states
+		 * The initiator shall set the STT to the value of the
+		 * FSTSessionTimeOut field at ... and at each ACK frame sent in
+		 * response to a received FST Setup Response with the Status
+		 * Code field equal to PENDING_ADMITTING_FST_SESSION or
+		 * PENDING_GAP_IN_BA_WINDOW.
+		 */
+		evext.to_initial.reason = REASON_REJECT;
+		evext.to_initial.reject_code = res->status_code;
+		evext.to_initial.initiator = FST_INITIATOR_REMOTE;
+		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+		fst_printf_session(s, MSG_WARNING,
+				   "FST Setup rejected by remote side with status %u",
+				   res->status_code);
+		return;
+	}
+
+	fst_iface_get_channel_info(s->data.new_iface, &hw_mode, &channel);
+
+	if (fst_hw_mode_to_band(hw_mode) != res->stie.new_band_id) {
+		evext.to_initial.reason = REASON_ERROR_PARAMS;
+		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+		fst_printf_session(s, MSG_WARNING,
+				   "invalid FST Setup parameters");
+		fst_session_tear_down_setup(s);
+		return;
+	}
+
+	fst_printf_session(s, MSG_INFO,
+			   "%s: FST Setup established for %s (llt=%u)",
+			   fst_iface_get_name(s->data.old_iface),
+			   fst_iface_get_name(s->data.new_iface),
+			   s->data.llt_ms);
+
+	fst_session_notify_ctrl(s, EVENT_FST_ESTABLISHED, NULL);
+
+	if (s->data.llt_ms == FST_LLT_SWITCH_IMMEDIATELY)
+		fst_session_initiate_switch(s);
+}
+
+
+static void fst_session_handle_tear_down(struct fst_session *s,
+					 struct fst_iface *iface,
+					 const struct ieee80211_mgmt *mgmt,
+					 size_t frame_len)
+{
+	const struct fst_tear_down *td;
+	size_t plen = frame_len - IEEE80211_HDRLEN - 1;
+	union fst_session_state_switch_extra evext = {
+		.to_initial = {
+			.reason = REASON_TEARDOWN,
+			.initiator = FST_INITIATOR_REMOTE,
+		},
+	};
+
+	if (plen < sizeof(*td)) {
+		fst_printf_session(s, MSG_WARNING,
+				   "Too short FST Tear Down dropped");
+		return;
+	}
+	td = (const struct fst_tear_down *)
+		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+
+	if (le_to_host32(td->fsts_id) != s->data.fsts_id) {
+		fst_printf_siface(s, iface, MSG_WARNING,
+				  "tear down for wrong FST Setup ID (%u)",
+				  le_to_host32(td->fsts_id));
+		return;
+	}
+
+	fst_session_stt_disarm(s);
+
+	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+}
+
+
+static void fst_session_handle_ack_request(struct fst_session *s,
+					   struct fst_iface *iface,
+					   const struct ieee80211_mgmt *mgmt,
+					   size_t frame_len)
+{
+	const struct fst_ack_req *req;
+	size_t plen = frame_len - IEEE80211_HDRLEN - 1;
+	struct fst_ack_res res;
+	union fst_session_state_switch_extra evext = {
+		.to_initial = {
+			.reason = REASON_SWITCH,
+			.initiator = FST_INITIATOR_REMOTE,
+		},
+	};
+
+	if (!fst_session_is_ready(s) && !fst_session_is_switch_requested(s)) {
+		fst_printf_siface(s, iface, MSG_ERROR,
+				  "cannot initiate switch due to wrong session state (%s)",
+				  fst_session_state_name(s->state));
+		return;
+	}
+
+	WPA_ASSERT(s->data.new_iface != NULL);
+
+	if (iface != s->data.new_iface) {
+		fst_printf_siface(s, iface, MSG_ERROR,
+				  "Ack received on wrong interface");
+		return;
+	}
+
+	if (plen < sizeof(*req)) {
+		fst_printf_session(s, MSG_WARNING,
+				   "Too short FST Ack Request dropped");
+		return;
+	}
+	req = (const struct fst_ack_req *)
+		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+
+	if (le_to_host32(req->fsts_id) != s->data.fsts_id) {
+		fst_printf_siface(s, iface, MSG_WARNING,
+				  "Ack for wrong FST Setup ID (%u)",
+				  le_to_host32(req->fsts_id));
+		return;
+	}
+
+	os_memset(&res, 0, sizeof(res));
+
+	res.action = FST_ACTION_ACK_RESPONSE;
+	res.dialog_token = req->dialog_token;
+	res.fsts_id = req->fsts_id;
+
+	if (!fst_session_send_action(s, FALSE, &res, sizeof(res), NULL)) {
+		fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Response sent");
+		fst_session_stt_disarm(s);
+		fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE,
+				      NULL);
+		fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED,
+				      NULL);
+		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+	}
+}
+
+
+static void
+fst_session_handle_ack_response(struct fst_session *s,
+				struct fst_iface *iface,
+				const struct ieee80211_mgmt *mgmt,
+				size_t frame_len)
+{
+	const struct fst_ack_res *res;
+	size_t plen = frame_len - IEEE80211_HDRLEN - 1;
+	union fst_session_state_switch_extra evext = {
+		.to_initial = {
+			.reason = REASON_SWITCH,
+			.initiator = FST_INITIATOR_LOCAL,
+		},
+	};
+
+	if (!fst_session_is_switch_requested(s)) {
+		fst_printf_siface(s, iface, MSG_ERROR,
+				  "Ack Response in inappropriate session state (%s)",
+				  fst_session_state_name(s->state));
+		return;
+	}
+
+	WPA_ASSERT(s->data.new_iface != NULL);
+
+	if (iface != s->data.new_iface) {
+		fst_printf_siface(s, iface, MSG_ERROR,
+				  "Ack response received on wrong interface");
+		return;
+	}
+
+	if (plen < sizeof(*res)) {
+		fst_printf_session(s, MSG_WARNING,
+				   "Too short FST Ack Response dropped");
+		return;
+	}
+	res = (const struct fst_ack_res *)
+		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+
+	if (le_to_host32(res->fsts_id) != s->data.fsts_id) {
+		fst_printf_siface(s, iface, MSG_ERROR,
+				  "Ack response for wrong FST Setup ID (%u)",
+				  le_to_host32(res->fsts_id));
+		return;
+	}
+
+	fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, NULL);
+	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+
+	fst_session_stt_disarm(s);
+}
+
+
+struct fst_session * fst_session_create(struct fst_group *g)
+{
+	struct fst_session *s;
+	u32 id;
+
+	WPA_ASSERT(!is_zero_ether_addr(own_addr));
+
+	id = fst_find_free_session_id();
+	if (id == FST_INVALID_SESSION_ID) {
+		fst_printf(MSG_ERROR, "Cannot assign new session ID");
+		return NULL;
+	}
+
+	s = os_zalloc(sizeof(*s));
+	if (!s) {
+		fst_printf(MSG_ERROR, "Cannot allocate new session object");
+		return NULL;
+	}
+
+	s->id = id;
+	s->group = g;
+	s->state = FST_SESSION_STATE_INITIAL;
+
+	s->data.llt_ms = FST_LLT_MS_DEFAULT;
+
+	fst_printf(MSG_INFO, "Session %u created", s->id);
+
+	dl_list_add_tail(&global_sessions_list, &s->global_sessions_lentry);
+
+	foreach_fst_ctrl_call(on_session_added, s);
+
+	return s;
+}
+
+
+void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface,
+			   Boolean is_old)
+{
+	if (is_old)
+		s->data.old_iface = iface;
+	else
+		s->data.new_iface = iface;
+
+}
+
+
+void fst_session_set_llt(struct fst_session *s, u32 llt)
+{
+	s->data.llt_ms = llt;
+}
+
+
+void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr,
+			       Boolean is_old)
+{
+	u8 *a = is_old ? s->data.old_peer_addr : s->data.new_peer_addr;
+
+	os_memcpy(a, addr, ETH_ALEN);
+}
+
+
+int fst_session_initiate_setup(struct fst_session *s)
+{
+	struct fst_setup_req req;
+	int res;
+	u32 fsts_id;
+	u8 dialog_token;
+	struct fst_session *_s;
+
+	if (fst_session_is_in_progress(s)) {
+		fst_printf_session(s, MSG_ERROR, "Session in progress");
+		return -EINVAL;
+	}
+
+	if (is_zero_ether_addr(s->data.old_peer_addr)) {
+		fst_printf_session(s, MSG_ERROR, "No old peer MAC address");
+		return -EINVAL;
+	}
+
+	if (is_zero_ether_addr(s->data.new_peer_addr)) {
+		fst_printf_session(s, MSG_ERROR, "No new peer MAC address");
+		return -EINVAL;
+	}
+
+	if (!s->data.old_iface) {
+		fst_printf_session(s, MSG_ERROR, "No old interface defined");
+		return -EINVAL;
+	}
+
+	if (!s->data.new_iface) {
+		fst_printf_session(s, MSG_ERROR, "No new interface defined");
+		return -EINVAL;
+	}
+
+	if (s->data.new_iface == s->data.old_iface) {
+		fst_printf_session(s, MSG_ERROR,
+				   "Same interface set as old and new");
+		return -EINVAL;
+	}
+
+	if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) {
+		fst_printf_session(s, MSG_ERROR,
+				   "The preset old peer address is not connected");
+		return -EINVAL;
+	}
+
+	if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr)) {
+		fst_printf_session(s, MSG_ERROR,
+				   "The preset new peer address is not connected");
+		return -EINVAL;
+	}
+
+	_s = fst_find_session_in_progress(s->data.old_peer_addr, s->group);
+	if (_s) {
+		fst_printf_session(s, MSG_ERROR,
+				   "There is another session in progress (old): %u",
+				   _s->id);
+		return -EINVAL;
+	}
+
+	_s = fst_find_session_in_progress(s->data.new_peer_addr, s->group);
+	if (_s) {
+		fst_printf_session(s, MSG_ERROR,
+				   "There is another session in progress (new): %u",
+				   _s->id);
+		return -EINVAL;
+	}
+
+	dialog_token = fst_group_assign_dialog_token(s->group);
+	fsts_id = fst_group_assign_fsts_id(s->group);
+
+	os_memset(&req, 0, sizeof(req));
+
+	fst_printf_siface(s, s->data.old_iface, MSG_INFO,
+		"initiating FST setup for %s (llt=%u ms)",
+		fst_iface_get_name(s->data.new_iface), s->data.llt_ms);
+
+	req.action = FST_ACTION_SETUP_REQUEST;
+	req.dialog_token = dialog_token;
+	req.llt = host_to_le32(FST_LLT_MS_TO_VAL(s->data.llt_ms));
+	/* 8.4.2.147 Session Transition element */
+	req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
+	req.stie.length = sizeof(req.stie) - 2;
+	req.stie.fsts_id = host_to_le32(fsts_id);
+	req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
+
+	req.stie.new_band_id = fst_iface_get_band_id(s->data.new_iface);
+	req.stie.new_band_op = 1;
+	req.stie.new_band_setup = 0;
+
+	req.stie.old_band_id = fst_iface_get_band_id(s->data.old_iface);
+	req.stie.old_band_op = 1;
+	req.stie.old_band_setup = 0;
+
+	res = fst_session_send_action(s, TRUE, &req, sizeof(req),
+				      fst_iface_get_mbie(s->data.old_iface));
+	if (!res) {
+		s->data.fsts_id = fsts_id;
+		s->data.pending_setup_req_dlgt = dialog_token;
+		fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Request sent");
+		fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION,
+				      NULL);
+
+		fst_session_stt_arm(s);
+	}
+
+	return res;
+}
+
+
+int fst_session_respond(struct fst_session *s, u8 status_code)
+{
+	struct fst_setup_res res;
+	enum hostapd_hw_mode hw_mode;
+	u8 channel;
+
+	if (!fst_session_is_ready_pending(s)) {
+		fst_printf_session(s, MSG_ERROR, "incorrect state: %s",
+				   fst_session_state_name(s->state));
+		return -EINVAL;
+	}
+
+	if (is_zero_ether_addr(s->data.old_peer_addr)) {
+		fst_printf_session(s, MSG_ERROR, "No peer MAC address");
+		return -EINVAL;
+	}
+
+	if (!s->data.old_iface) {
+		fst_printf_session(s, MSG_ERROR, "No old interface defined");
+		return -EINVAL;
+	}
+
+	if (!s->data.new_iface) {
+		fst_printf_session(s, MSG_ERROR, "No new interface defined");
+		return -EINVAL;
+	}
+
+	if (s->data.new_iface == s->data.old_iface) {
+		fst_printf_session(s, MSG_ERROR,
+				   "Same interface set as old and new");
+		return -EINVAL;
+	}
+
+	if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) {
+		fst_printf_session(s, MSG_ERROR,
+				   "The preset peer address is not in the peer list");
+		return -EINVAL;
+	}
+
+	fst_session_stt_disarm(s);
+
+	os_memset(&res, 0, sizeof(res));
+
+	res.action = FST_ACTION_SETUP_RESPONSE;
+	res.dialog_token = s->data.pending_setup_req_dlgt;
+	res.status_code = status_code;
+
+	res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
+	res.stie.length = sizeof(res.stie) - 2;
+
+	if (status_code == WLAN_STATUS_SUCCESS) {
+		res.stie.fsts_id = s->data.fsts_id;
+		res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
+
+		fst_iface_get_channel_info(s->data.new_iface, &hw_mode,
+					   &channel);
+		res.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
+		res.stie.new_band_op = 1;
+		res.stie.new_band_setup = 0;
+
+		fst_iface_get_channel_info(s->data.old_iface, &hw_mode,
+					   &channel);
+		res.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
+		res.stie.old_band_op = 1;
+		res.stie.old_band_setup = 0;
+
+		fst_printf_session(s, MSG_INFO,
+				   "%s: FST Setup Request accepted for %s (llt=%u)",
+				   fst_iface_get_name(s->data.old_iface),
+				   fst_iface_get_name(s->data.new_iface),
+				   s->data.llt_ms);
+	} else {
+		fst_printf_session(s, MSG_WARNING,
+				   "%s: FST Setup Request rejected with code %d",
+				   fst_iface_get_name(s->data.old_iface),
+				   status_code);
+	}
+
+	if (fst_session_send_action(s, TRUE, &res, sizeof(res),
+				    fst_iface_get_mbie(s->data.old_iface))) {
+		fst_printf_sframe(s, TRUE, MSG_ERROR,
+				  "cannot send FST Setup Response with code %d",
+				  status_code);
+		return -EINVAL;
+	}
+
+	fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Response sent");
+
+	if (status_code != WLAN_STATUS_SUCCESS) {
+		union fst_session_state_switch_extra evext = {
+			.to_initial = {
+				.reason = REASON_REJECT,
+				.reject_code = status_code,
+				.initiator = FST_INITIATOR_LOCAL,
+			},
+		};
+		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+	}
+
+	return 0;
+}
+
+
+int fst_session_initiate_switch(struct fst_session *s)
+{
+	struct fst_ack_req req;
+	int res;
+	u8 dialog_token;
+
+	if (!fst_session_is_ready(s)) {
+		fst_printf_session(s, MSG_ERROR,
+				   "cannot initiate switch due to wrong setup state (%d)",
+				   s->state);
+		return -1;
+	}
+
+	dialog_token = fst_group_assign_dialog_token(s->group);
+
+	WPA_ASSERT(s->data.new_iface != NULL);
+	WPA_ASSERT(s->data.old_iface != NULL);
+
+	fst_printf_session(s, MSG_INFO, "initiating FST switch: %s => %s",
+			   fst_iface_get_name(s->data.old_iface),
+			   fst_iface_get_name(s->data.new_iface));
+
+	os_memset(&req, 0, sizeof(req));
+
+	req.action = FST_ACTION_ACK_REQUEST;
+	req.dialog_token = dialog_token;
+	req.fsts_id = host_to_le32(s->data.fsts_id);
+
+	res = fst_session_send_action(s, FALSE, &req, sizeof(req), NULL);
+	if (!res) {
+		fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Request sent");
+		fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE,
+				      NULL);
+		fst_session_stt_arm(s);
+	} else {
+		fst_printf_sframe(s, FALSE, MSG_ERROR,
+				  "Cannot send FST Ack Request");
+	}
+
+	return res;
+}
+
+
+void fst_session_handle_action(struct fst_session *s,
+			       struct fst_iface *iface,
+			       const struct ieee80211_mgmt *mgmt,
+			       size_t frame_len)
+{
+	switch (mgmt->u.action.u.fst_action.action) {
+	case FST_ACTION_SETUP_REQUEST:
+		WPA_ASSERT(0);
+		break;
+	case FST_ACTION_SETUP_RESPONSE:
+		fst_session_handle_setup_response(s, iface, mgmt, frame_len);
+		break;
+	case FST_ACTION_TEAR_DOWN:
+		fst_session_handle_tear_down(s, iface, mgmt, frame_len);
+		break;
+	case FST_ACTION_ACK_REQUEST:
+		fst_session_handle_ack_request(s, iface, mgmt, frame_len);
+		break;
+	case FST_ACTION_ACK_RESPONSE:
+		fst_session_handle_ack_response(s, iface, mgmt, frame_len);
+		break;
+	case FST_ACTION_ON_CHANNEL_TUNNEL:
+	default:
+		fst_printf_sframe(s, FALSE, MSG_ERROR,
+				  "Unsupported FST Action frame");
+		break;
+	}
+}
+
+
+int fst_session_tear_down_setup(struct fst_session *s)
+{
+	int res;
+	union fst_session_state_switch_extra evext = {
+		.to_initial = {
+			.reason = REASON_TEARDOWN,
+			.initiator = FST_INITIATOR_LOCAL,
+		},
+	};
+
+	res = fst_session_send_tear_down(s);
+
+	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+
+	return res;
+}
+
+
+void fst_session_reset(struct fst_session *s)
+{
+	fst_session_reset_ex(s, REASON_RESET);
+}
+
+
+void fst_session_delete(struct fst_session *s)
+{
+	fst_printf(MSG_INFO, "Session %u deleted", s->id);
+	dl_list_del(&s->global_sessions_lentry);
+	foreach_fst_ctrl_call(on_session_removed, s);
+	os_free(s);
+}
+
+
+struct fst_group * fst_session_get_group(struct fst_session *s)
+{
+	return s->group;
+}
+
+
+struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old)
+{
+	return is_old ? s->data.old_iface : s->data.new_iface;
+}
+
+
+u32 fst_session_get_id(struct fst_session *s)
+{
+	return s->id;
+}
+
+
+const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old)
+{
+	return is_old ? s->data.old_peer_addr : s->data.new_peer_addr;
+}
+
+
+u32 fst_session_get_llt(struct fst_session *s)
+{
+	return s->data.llt_ms;
+}
+
+
+enum fst_session_state fst_session_get_state(struct fst_session *s)
+{
+	return s->state;
+}
+
+
+struct fst_session * fst_session_get_by_id(u32 id)
+{
+	struct fst_session *s;
+
+	foreach_fst_session(s) {
+		if (id == s->id)
+			return s;
+	}
+
+	return NULL;
+}
+
+
+void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx)
+{
+	struct fst_session *s;
+
+	foreach_fst_session(s) {
+		if (!g || s->group == g)
+			clb(s->group, s, ctx);
+	}
+}
+
+
+void fst_session_on_action_rx(struct fst_iface *iface,
+			      const struct ieee80211_mgmt *mgmt,
+			      size_t len)
+{
+	struct fst_session *s;
+
+	if (len < IEEE80211_HDRLEN + 2 ||
+	    mgmt->u.action.category != WLAN_ACTION_FST) {
+		fst_printf_iface(iface, MSG_ERROR,
+				 "invalid Action frame received");
+		return;
+	}
+
+	if (mgmt->u.action.u.fst_action.action <= FST_ACTION_MAX_SUPPORTED) {
+		fst_printf_iface(iface, MSG_DEBUG,
+				 "FST Action '%s' received!",
+				 fst_action_names[mgmt->u.action.u.fst_action.action]);
+	} else {
+		fst_printf_iface(iface, MSG_WARNING,
+				 "unknown FST Action (%u) received!",
+				 mgmt->u.action.u.fst_action.action);
+		return;
+	}
+
+	if (mgmt->u.action.u.fst_action.action == FST_ACTION_SETUP_REQUEST) {
+		fst_session_handle_setup_request(iface, mgmt, len);
+		return;
+	}
+
+	s = fst_find_session_in_progress(mgmt->sa, fst_iface_get_group(iface));
+	if (s) {
+		fst_session_handle_action(s, iface, mgmt, len);
+	} else {
+		fst_printf_iface(iface, MSG_WARNING,
+				 "FST Action '%s' dropped: no session in progress found",
+				 fst_action_names[mgmt->u.action.u.fst_action.action]);
+	}
+}
+
+
+int fst_session_set_str_ifname(struct fst_session *s, const char *ifname,
+			       Boolean is_old)
+{
+	struct fst_group *g = fst_session_get_group(s);
+	struct fst_iface *i;
+
+	i = fst_group_get_iface_by_name(g, ifname);
+	if (!i) {
+		fst_printf_session(s, MSG_WARNING,
+				   "Cannot set iface %s: no such iface within group '%s'",
+				   ifname, fst_group_get_id(g));
+		return -1;
+	}
+
+	fst_session_set_iface(s, i, is_old);
+
+	return 0;
+}
+
+
+int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac,
+				  Boolean is_old)
+{
+	u8 peer_addr[ETH_ALEN];
+	int res = fst_read_peer_addr(mac, peer_addr);
+
+	if (res)
+		return res;
+
+	fst_session_set_peer_addr(s, peer_addr, is_old);
+
+	return 0;
+}
+
+
+int fst_session_set_str_llt(struct fst_session *s, const char *llt_str)
+{
+	char *endp;
+	long int llt = strtol(llt_str, &endp, 0);
+
+	if (*endp || llt < 0 || (unsigned long int) llt > FST_MAX_LLT_MS) {
+		fst_printf_session(s, MSG_WARNING,
+				   "Cannot set llt %s: Invalid llt value (1..%u expected)",
+				   llt_str, FST_MAX_LLT_MS);
+		return -1;
+	}
+	fst_session_set_llt(s, (u32) llt);
+
+	return 0;
+}
+
+
+void fst_session_global_on_iface_detached(struct fst_iface *iface)
+{
+	struct fst_session *s;
+
+	foreach_fst_session(s) {
+		if (fst_session_is_in_progress(s) &&
+		    (s->data.new_iface == iface ||
+		     s->data.old_iface == iface))
+			fst_session_reset_ex(s, REASON_DETACH_IFACE);
+	}
+}
+
+
+struct fst_session * fst_session_global_get_first_by_group(struct fst_group *g)
+{
+	struct fst_session *s;
+
+	foreach_fst_session(s) {
+		if (s->group == g)
+			return s;
+	}
+
+	return NULL;
+}
+
+
+#ifdef CONFIG_FST_TEST
+
+static int get_group_fill_session(struct fst_group **g, struct fst_session *s)
+{
+	const u8 *old_addr, *new_addr;
+	struct fst_get_peer_ctx *ctx;
+
+	os_memset(s, 0, sizeof(*s));
+	foreach_fst_group(*g) {
+		s->data.new_iface = fst_group_first_iface(*g);
+		if (s->data.new_iface)
+			break;
+	}
+	if (!s->data.new_iface)
+		return -EINVAL;
+
+	s->data.old_iface = dl_list_entry(s->data.new_iface->group_lentry.next,
+					  struct fst_iface, group_lentry);
+	if (!s->data.old_iface)
+		return -EINVAL;
+
+	old_addr = fst_iface_get_peer_first(s->data.old_iface, &ctx, TRUE);
+	if (!old_addr)
+		return -EINVAL;
+
+	new_addr = fst_iface_get_peer_first(s->data.new_iface, &ctx, TRUE);
+	if (!new_addr)
+		return -EINVAL;
+
+	os_memcpy(s->data.old_peer_addr, old_addr, ETH_ALEN);
+	os_memcpy(s->data.new_peer_addr, new_addr, ETH_ALEN);
+
+	return 0;
+}
+
+
+#define FST_MAX_COMMAND_WORD_NAME_LENGTH 16
+
+int fst_test_req_send_fst_request(const char *params)
+{
+	int fsts_id;
+	Boolean is_valid;
+	char *endp;
+	struct fst_setup_req req;
+	struct fst_session s;
+	struct fst_group *g;
+	enum hostapd_hw_mode hw_mode;
+	u8 channel;
+	char additional_param[FST_MAX_COMMAND_WORD_NAME_LENGTH];
+
+	if (params[0] != ' ')
+		return -EINVAL;
+	params++;
+	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+	if (!is_valid)
+		return -EINVAL;
+
+	if (get_group_fill_session(&g, &s))
+		return -EINVAL;
+
+	req.action = FST_ACTION_SETUP_REQUEST;
+	req.dialog_token = g->dialog_token;
+	req.llt = host_to_le32(FST_LLT_MS_DEFAULT);
+	/* 8.4.2.147 Session Transition element */
+	req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
+	req.stie.length = sizeof(req.stie) - 2;
+	req.stie.fsts_id = host_to_le32(fsts_id);
+	req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
+
+	fst_iface_get_channel_info(s.data.new_iface, &hw_mode, &channel);
+	req.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
+	req.stie.new_band_op = 1;
+	req.stie.new_band_setup = 0;
+
+	fst_iface_get_channel_info(s.data.old_iface, &hw_mode, &channel);
+	req.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
+	req.stie.old_band_op = 1;
+	req.stie.old_band_setup = 0;
+
+	if (!fst_read_next_text_param(endp, additional_param,
+				       sizeof(additional_param), &endp)) {
+		if (!os_strcasecmp(additional_param, FST_CTR_PVAL_BAD_NEW_BAND))
+			req.stie.new_band_id = req.stie.old_band_id;
+	}
+
+	return fst_session_send_action(&s, TRUE, &req, sizeof(req),
+				       s.data.old_iface->mb_ie);
+}
+
+
+int fst_test_req_send_fst_response(const char *params)
+{
+	int fsts_id;
+	Boolean is_valid;
+	char *endp;
+	struct fst_setup_res res;
+	struct fst_session s;
+	struct fst_group *g;
+	enum hostapd_hw_mode hw_mode;
+	u8 status_code;
+	u8 channel;
+	char response[FST_MAX_COMMAND_WORD_NAME_LENGTH];
+	struct fst_session *_s;
+
+	if (params[0] != ' ')
+		return -EINVAL;
+	params++;
+	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+	if (!is_valid)
+		return -EINVAL;
+
+	if (get_group_fill_session(&g, &s))
+		return -EINVAL;
+
+	status_code = WLAN_STATUS_SUCCESS;
+	if (!fst_read_next_text_param(endp, response, sizeof(response),
+				      &endp)) {
+		if (!os_strcasecmp(response, FST_CS_PVAL_RESPONSE_REJECT))
+			status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
+	}
+
+	os_memset(&res, 0, sizeof(res));
+
+	res.action = FST_ACTION_SETUP_RESPONSE;
+	/*
+	 * If some session has just received an FST Setup Request, then
+	 * use the correct dialog token copied from this request.
+	 */
+	_s = fst_find_session_in_progress(fst_session_get_peer_addr(&s, TRUE),
+					  g);
+	res.dialog_token = (_s && fst_session_is_ready_pending(_s)) ?
+		_s->data.pending_setup_req_dlgt : g->dialog_token;
+	res.status_code  = status_code;
+
+	res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
+	res.stie.length = sizeof(res.stie) - 2;
+
+	if (res.status_code == WLAN_STATUS_SUCCESS) {
+		res.stie.fsts_id = fsts_id;
+		res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
+
+		fst_iface_get_channel_info(s.data.new_iface, &hw_mode,
+					    &channel);
+		res.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
+		res.stie.new_band_op = 1;
+		res.stie.new_band_setup = 0;
+
+		fst_iface_get_channel_info(s.data.old_iface, &hw_mode,
+					   &channel);
+		res.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
+		res.stie.old_band_op = 1;
+		res.stie.old_band_setup = 0;
+	}
+
+	if (!fst_read_next_text_param(endp, response, sizeof(response),
+				      &endp)) {
+		if (!os_strcasecmp(response, FST_CTR_PVAL_BAD_NEW_BAND))
+			res.stie.new_band_id = res.stie.old_band_id;
+	}
+
+	return fst_session_send_action(&s, TRUE, &res, sizeof(res),
+				       s.data.old_iface->mb_ie);
+}
+
+
+int fst_test_req_send_ack_request(const char *params)
+{
+	int fsts_id;
+	Boolean is_valid;
+	char *endp;
+	struct fst_ack_req req;
+	struct fst_session s;
+	struct fst_group *g;
+
+	if (params[0] != ' ')
+		return -EINVAL;
+	params++;
+	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+	if (!is_valid)
+		return -EINVAL;
+
+	if (get_group_fill_session(&g, &s))
+		return -EINVAL;
+
+	os_memset(&req, 0, sizeof(req));
+	req.action = FST_ACTION_ACK_REQUEST;
+	req.dialog_token = g->dialog_token;
+	req.fsts_id = fsts_id;
+
+	return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL);
+}
+
+
+int fst_test_req_send_ack_response(const char *params)
+{
+	int fsts_id;
+	Boolean is_valid;
+	char *endp;
+	struct fst_ack_res res;
+	struct fst_session s;
+	struct fst_group *g;
+
+	if (params[0] != ' ')
+		return -EINVAL;
+	params++;
+	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+	if (!is_valid)
+		return -EINVAL;
+
+	if (get_group_fill_session(&g, &s))
+		return -EINVAL;
+
+	os_memset(&res, 0, sizeof(res));
+	res.action = FST_ACTION_ACK_RESPONSE;
+	res.dialog_token = g->dialog_token;
+	res.fsts_id = fsts_id;
+
+	return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL);
+}
+
+
+int fst_test_req_send_tear_down(const char *params)
+{
+	int fsts_id;
+	Boolean is_valid;
+	char *endp;
+	struct fst_tear_down td;
+	struct fst_session s;
+	struct fst_group *g;
+
+	if (params[0] != ' ')
+		return -EINVAL;
+	params++;
+	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+	if (!is_valid)
+		return -EINVAL;
+
+	if (get_group_fill_session(&g, &s))
+		return -EINVAL;
+
+	os_memset(&td, 0, sizeof(td));
+	td.action = FST_ACTION_TEAR_DOWN;
+	td.fsts_id = fsts_id;
+
+	return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL);
+}
+
+
+u32 fst_test_req_get_fsts_id(const char *params)
+{
+	int sid;
+	Boolean is_valid;
+	char *endp;
+	struct fst_session *s;
+
+	if (params[0] != ' ')
+		return FST_FSTS_ID_NOT_FOUND;
+	params++;
+	sid = fst_read_next_int_param(params, &is_valid, &endp);
+	if (!is_valid)
+		return FST_FSTS_ID_NOT_FOUND;
+
+	s = fst_session_get_by_id(sid);
+	if (!s)
+		return FST_FSTS_ID_NOT_FOUND;
+
+	return s->data.fsts_id;
+}
+
+
+int fst_test_req_get_local_mbies(const char *request, char *buf, size_t buflen)
+{
+	char *endp;
+	char ifname[FST_MAX_COMMAND_WORD_NAME_LENGTH];
+	struct fst_group *g;
+	struct fst_iface *iface;
+
+	if (request[0] != ' ')
+		return -EINVAL;
+	request++;
+	if (fst_read_next_text_param(request, ifname, sizeof(ifname), &endp) ||
+	    !*ifname)
+		goto problem;
+	g = dl_list_first(&fst_global_groups_list, struct fst_group,
+			  global_groups_lentry);
+	if (!g)
+		goto problem;
+	iface = fst_group_get_iface_by_name(g, ifname);
+	if (!iface || !iface->mb_ie)
+		goto problem;
+	return wpa_snprintf_hex(buf, buflen, wpabuf_head(iface->mb_ie),
+				wpabuf_len(iface->mb_ie));
+
+problem:
+	return os_snprintf(buf, buflen, "FAIL\n");
+}
+
+#endif /* CONFIG_FST_TEST */
diff --git a/hostap/src/fst/fst_session.h b/hostap/src/fst/fst_session.h
new file mode 100644
index 0000000..1162de4
--- /dev/null
+++ b/hostap/src/fst/fst_session.h
@@ -0,0 +1,80 @@
+/*
+ * FST module - FST Session related 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 FST_SESSION_H
+#define FST_SESSION_H
+
+#define FST_DEFAULT_SESSION_TIMEOUT_TU 255 /* u8 */
+
+struct fst_iface;
+struct fst_group;
+struct fst_session;
+enum fst_session_state;
+
+int  fst_session_global_init(void);
+void fst_session_global_deinit(void);
+void fst_session_global_on_iface_detached(struct fst_iface *iface);
+struct fst_session *
+fst_session_global_get_first_by_group(struct fst_group *g);
+
+struct fst_session * fst_session_create(struct fst_group *g);
+void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface,
+			   Boolean is_old);
+void fst_session_set_llt(struct fst_session *s, u32 llt);
+void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr,
+			       Boolean is_old);
+int fst_session_initiate_setup(struct fst_session *s);
+int fst_session_respond(struct fst_session *s, u8 status_code);
+int fst_session_initiate_switch(struct fst_session *s);
+void fst_session_handle_action(struct fst_session *s, struct fst_iface *iface,
+			       const struct ieee80211_mgmt *mgmt,
+			       size_t frame_len);
+int fst_session_tear_down_setup(struct fst_session *s);
+void fst_session_reset(struct fst_session *s);
+void fst_session_delete(struct fst_session *s);
+
+struct fst_group * fst_session_get_group(struct fst_session *s);
+struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old);
+const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old);
+u32 fst_session_get_id(struct fst_session *s);
+u32 fst_session_get_llt(struct fst_session *s);
+enum fst_session_state fst_session_get_state(struct fst_session *s);
+
+struct fst_session *fst_session_get_by_id(u32 id);
+
+typedef void (*fst_session_enum_clb)(struct fst_group *g, struct fst_session *s,
+				     void *ctx);
+
+void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx);
+
+void fst_session_on_action_rx(struct fst_iface *iface,
+			      const struct ieee80211_mgmt *mgmt, size_t len);
+
+
+int fst_session_set_str_ifname(struct fst_session *s, const char *ifname,
+			       Boolean is_old);
+int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac,
+				  Boolean is_old);
+int fst_session_set_str_llt(struct fst_session *s, const char *llt_str);
+
+#ifdef CONFIG_FST_TEST
+
+#define FST_FSTS_ID_NOT_FOUND ((u32) -1)
+
+int fst_test_req_send_fst_request(const char *params);
+int fst_test_req_send_fst_response(const char *params);
+int fst_test_req_send_ack_request(const char *params);
+int fst_test_req_send_ack_response(const char *params);
+int fst_test_req_send_tear_down(const char *params);
+u32 fst_test_req_get_fsts_id(const char *params);
+int fst_test_req_get_local_mbies(const char *request, char *buf,
+				 size_t buflen);
+
+#endif /* CONFIG_FST_TEST */
+
+#endif /* FST_SESSION_H */
diff --git a/hostap/src/l2_packet/Makefile b/hostap/src/l2_packet/Makefile
new file mode 100644
index 0000000..47925b7
--- /dev/null
+++ b/hostap/src/l2_packet/Makefile
@@ -0,0 +1,16 @@
+all: libl2_packet.a
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libl2_packet.a
+
+install:
+	@echo Nothing to be made.
+
+include ../lib.rules
+
+LIB_OBJS = l2_packet_linux.o
+
+libl2_packet.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/l2_packet/l2_packet.h b/hostap/src/l2_packet/l2_packet.h
new file mode 100644
index 0000000..2a45245
--- /dev/null
+++ b/hostap/src/l2_packet/l2_packet.h
@@ -0,0 +1,154 @@
+/*
+ * WPA Supplicant - Layer2 packet interface definition
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file defines an interface for layer 2 (link layer) packet sending and
+ * receiving. l2_packet_linux.c is one implementation for such a layer 2
+ * implementation using Linux packet sockets and l2_packet_pcap.c another one
+ * using libpcap and libdnet. When porting %wpa_supplicant to other operating
+ * systems, a new l2_packet implementation may need to be added.
+ */
+
+#ifndef L2_PACKET_H
+#define L2_PACKET_H
+
+/**
+ * struct l2_packet_data - Internal l2_packet data structure
+ *
+ * This structure is used by the l2_packet implementation to store its private
+ * data. Other files use a pointer to this data when calling the l2_packet
+ * functions, but the contents of this structure should not be used directly
+ * outside l2_packet implementation.
+ */
+struct l2_packet_data;
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct l2_ethhdr {
+	u8 h_dest[ETH_ALEN];
+	u8 h_source[ETH_ALEN];
+	be16 h_proto;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+enum l2_packet_filter_type {
+	L2_PACKET_FILTER_DHCP,
+	L2_PACKET_FILTER_NDISC,
+};
+
+/**
+ * l2_packet_init - Initialize l2_packet interface
+ * @ifname: Interface name
+ * @own_addr: Optional own MAC address if available from driver interface or
+ *	%NULL if not available
+ * @protocol: Ethernet protocol number in host byte order
+ * @rx_callback: Callback function that will be called for each received packet
+ * @rx_callback_ctx: Callback data (ctx) for calls to rx_callback()
+ * @l2_hdr: 1 = include layer 2 header, 0 = do not include header
+ * Returns: Pointer to internal data or %NULL on failure
+ *
+ * rx_callback function will be called with src_addr pointing to the source
+ * address (MAC address) of the the packet. If l2_hdr is set to 0, buf
+ * points to len bytes of the payload after the layer 2 header and similarly,
+ * TX buffers start with payload. This behavior can be changed by setting
+ * l2_hdr=1 to include the layer 2 header in the data buffer.
+ */
+struct l2_packet_data * l2_packet_init(
+	const char *ifname, const u8 *own_addr, unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr);
+
+/**
+ * l2_packet_init_bridge - Like l2_packet_init() but with bridge workaround
+ *
+ * This version of l2_packet_init() can be used to enable a workaround for Linux
+ * packet socket in case of a station interface in a bridge.
+ */
+struct l2_packet_data * l2_packet_init_bridge(
+	const char *br_ifname, const char *ifname, const u8 *own_addr,
+	unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr);
+
+/**
+ * l2_packet_deinit - Deinitialize l2_packet interface
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ */
+void l2_packet_deinit(struct l2_packet_data *l2);
+
+/**
+ * l2_packet_get_own_addr - Get own layer 2 address
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @addr: Buffer for the own address (6 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr);
+
+/**
+ * l2_packet_send - Send a packet
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @dst_addr: Destination address for the packet (only used if l2_hdr == 0)
+ * @proto: Protocol/ethertype for the packet in host byte order (only used if
+ * l2_hdr == 0)
+ * @buf: Packet contents to be sent; including layer 2 header if l2_hdr was
+ * set to 1 in l2_packet_init() call. Otherwise, only the payload of the packet
+ * is included.
+ * @len: Length of the buffer (including l2 header only if l2_hdr == 1)
+ * Returns: >=0 on success, <0 on failure
+ */
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+		   const u8 *buf, size_t len);
+
+/**
+ * l2_packet_get_ip_addr - Get the current IP address from the interface
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @buf: Buffer for the IP address in text format
+ * @len: Maximum buffer length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to get the current IP address from the interface
+ * bound to the l2_packet. This is mainly for status information and the IP
+ * address will be stored as an ASCII string. This function is not essential
+ * for %wpa_supplicant operation, so full implementation is not required.
+ * l2_packet implementation will need to define the function, but it can return
+ * -1 if the IP address information is not available.
+ */
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len);
+
+
+/**
+ * l2_packet_notify_auth_start - Notify l2_packet about start of authentication
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ *
+ * This function is called when authentication is expected to start, e.g., when
+ * association has been completed, in order to prepare l2_packet implementation
+ * for EAPOL frames. This function is used mainly if the l2_packet code needs
+ * to do polling in which case it can increasing polling frequency. This can
+ * also be an empty function if the l2_packet implementation does not benefit
+ * from knowing about the starting authentication.
+ */
+void l2_packet_notify_auth_start(struct l2_packet_data *l2);
+
+/**
+ * l2_packet_set_packet_filter - Set socket filter for l2_packet
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @type: enum l2_packet_filter_type, type of filter
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to set the socket filter for l2_packet socket.
+ *
+ */
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+				enum l2_packet_filter_type type);
+
+#endif /* L2_PACKET_H */
diff --git a/hostap/src/l2_packet/l2_packet_freebsd.c b/hostap/src/l2_packet/l2_packet_freebsd.c
new file mode 100644
index 0000000..aa83648
--- /dev/null
+++ b/hostap/src/l2_packet/l2_packet_freebsd.c
@@ -0,0 +1,329 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with FreeBSD
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005, Sam Leffler <sam@errno.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#if defined(__APPLE__) || defined(__GLIBC__)
+#include <net/bpf.h>
+#endif /* __APPLE__ */
+#include <pcap.h>
+
+#include <sys/ioctl.h>
+#ifdef __sun__
+#include <libdlpi.h>
+#else /* __sun__ */
+#include <sys/sysctl.h>
+#endif /* __sun__ */
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+struct l2_packet_data {
+	pcap_t *pcap;
+	char ifname[100];
+	u8 own_addr[ETH_ALEN];
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len);
+	void *rx_callback_ctx;
+	int l2_hdr; /* whether to include layer 2 (Ethernet) header data
+		     * buffers */
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+	os_memcpy(addr, l2->own_addr, ETH_ALEN);
+	return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+		   const u8 *buf, size_t len)
+{
+	if (!l2->l2_hdr) {
+		int ret;
+		struct l2_ethhdr *eth = os_malloc(sizeof(*eth) + len);
+		if (eth == NULL)
+			return -1;
+		os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+		os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+		eth->h_proto = htons(proto);
+		os_memcpy(eth + 1, buf, len);
+		ret = pcap_inject(l2->pcap, (u8 *) eth, len + sizeof(*eth));
+		os_free(eth);
+		return ret;
+	} else
+		return pcap_inject(l2->pcap, buf, len);
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct l2_packet_data *l2 = eloop_ctx;
+	pcap_t *pcap = sock_ctx;
+	struct pcap_pkthdr hdr;
+	const u_char *packet;
+	struct l2_ethhdr *ethhdr;
+	unsigned char *buf;
+	size_t len;
+
+	packet = pcap_next(pcap, &hdr);
+
+	if (packet == NULL || hdr.caplen < sizeof(*ethhdr))
+		return;
+
+	ethhdr = (struct l2_ethhdr *) packet;
+	if (l2->l2_hdr) {
+		buf = (unsigned char *) ethhdr;
+		len = hdr.caplen;
+	} else {
+		buf = (unsigned char *) (ethhdr + 1);
+		len = hdr.caplen - sizeof(*ethhdr);
+	}
+	l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+}
+
+
+static int l2_packet_init_libpcap(struct l2_packet_data *l2,
+				  unsigned short protocol)
+{
+	bpf_u_int32 pcap_maskp, pcap_netp;
+	char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
+	struct bpf_program pcap_fp;
+
+	pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
+	l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err);
+	if (l2->pcap == NULL) {
+		fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+		fprintf(stderr, "ifname='%s'\n", l2->ifname);
+		return -1;
+	}
+	if (pcap_datalink(l2->pcap) != DLT_EN10MB &&
+	    pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) {
+		fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n",
+			pcap_geterr(l2->pcap));
+		return -1;
+	}
+	os_snprintf(pcap_filter, sizeof(pcap_filter),
+		    "not ether src " MACSTR " and "
+		    "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
+		    "ether proto 0x%x",
+		    MAC2STR(l2->own_addr), /* do not receive own packets */
+		    MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
+		    protocol);
+	if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
+		fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
+		return -1;
+	}
+
+	if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) {
+		fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap));
+		return -1;
+	}
+
+	pcap_freecode(&pcap_fp);
+#ifndef __sun__
+	/*
+	 * When libpcap uses BPF we must enable "immediate mode" to
+	 * receive frames right away; otherwise the system may
+	 * buffer them for us.
+	 */
+	{
+		unsigned int on = 1;
+		if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) {
+			fprintf(stderr, "%s: cannot enable immediate mode on "
+				"interface %s: %s\n",
+				__func__, l2->ifname, strerror(errno));
+			/* XXX should we fail? */
+		}
+	}
+#endif /* __sun__ */
+
+	eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap),
+				 l2_packet_receive, l2, l2->pcap);
+
+	return 0;
+}
+
+
+static int eth_get(const char *device, u8 ea[ETH_ALEN])
+{
+#ifdef __sun__
+	dlpi_handle_t dh;
+	u32 physaddrlen = DLPI_PHYSADDR_MAX;
+	u8 physaddr[DLPI_PHYSADDR_MAX];
+	int retval;
+
+	retval = dlpi_open(device, &dh, 0);
+	if (retval != DLPI_SUCCESS) {
+		wpa_printf(MSG_ERROR, "dlpi_open error: %s",
+			   dlpi_strerror(retval));
+		return -1;
+	}
+
+	retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, physaddr,
+				   &physaddrlen);
+	if (retval != DLPI_SUCCESS) {
+		wpa_printf(MSG_ERROR, "dlpi_get_physaddr error: %s",
+			   dlpi_strerror(retval));
+		dlpi_close(dh);
+		return -1;
+	}
+	os_memcpy(ea, physaddr, ETH_ALEN);
+	dlpi_close(dh);
+#else /* __sun__ */
+	struct if_msghdr *ifm;
+	struct sockaddr_dl *sdl;
+	u_char *p, *buf;
+	size_t len;
+	int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
+
+	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+		return -1;
+	if ((buf = os_malloc(len)) == NULL)
+		return -1;
+	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+		os_free(buf);
+		return -1;
+	}
+	for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
+		ifm = (struct if_msghdr *)p;
+		sdl = (struct sockaddr_dl *)(ifm + 1);
+		if (ifm->ifm_type != RTM_IFINFO ||
+		    (ifm->ifm_addrs & RTA_IFP) == 0)
+			continue;
+		if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
+		    os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
+			continue;
+		os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
+		break;
+	}
+	os_free(buf);
+
+	if (p >= buf + len) {
+		errno = ESRCH;
+		return -1;
+	}
+#endif /* __sun__ */
+	return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+	const char *ifname, const u8 *own_addr, unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr)
+{
+	struct l2_packet_data *l2;
+
+	l2 = os_zalloc(sizeof(struct l2_packet_data));
+	if (l2 == NULL)
+		return NULL;
+	os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+	l2->rx_callback = rx_callback;
+	l2->rx_callback_ctx = rx_callback_ctx;
+	l2->l2_hdr = l2_hdr;
+
+	if (eth_get(l2->ifname, l2->own_addr) < 0) {
+		fprintf(stderr, "Failed to get link-level address for "
+			"interface '%s'.\n", l2->ifname);
+		os_free(l2);
+		return NULL;
+	}
+
+	if (l2_packet_init_libpcap(l2, protocol)) {
+		os_free(l2);
+		return NULL;
+	}
+
+	return l2;
+}
+
+
+struct l2_packet_data * l2_packet_init_bridge(
+	const char *br_ifname, const char *ifname, const u8 *own_addr,
+	unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr)
+{
+	return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+			      rx_callback_ctx, l2_hdr);
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+	if (l2 != NULL) {
+		if (l2->pcap) {
+			eloop_unregister_read_sock(
+				pcap_get_selectable_fd(l2->pcap));
+			pcap_close(l2->pcap);
+		}
+		os_free(l2);
+	}
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+	pcap_if_t *devs, *dev;
+	struct pcap_addr *addr;
+	struct sockaddr_in *saddr;
+	int found = 0;
+	char err[PCAP_ERRBUF_SIZE + 1];
+
+	if (pcap_findalldevs(&devs, err) < 0) {
+		wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
+		return -1;
+	}
+
+	for (dev = devs; dev && !found; dev = dev->next) {
+		if (os_strcmp(dev->name, l2->ifname) != 0)
+			continue;
+
+		addr = dev->addresses;
+		while (addr) {
+			saddr = (struct sockaddr_in *) addr->addr;
+			if (saddr && saddr->sin_family == AF_INET) {
+				os_strlcpy(buf, inet_ntoa(saddr->sin_addr),
+					   len);
+				found = 1;
+				break;
+			}
+			addr = addr->next;
+		}
+	}
+
+	pcap_freealldevs(devs);
+
+	return found ? 0 : -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+}
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+				enum l2_packet_filter_type type)
+{
+	return -1;
+}
diff --git a/hostap/src/l2_packet/l2_packet_linux.c b/hostap/src/l2_packet/l2_packet_linux.c
new file mode 100644
index 0000000..41de2f8
--- /dev/null
+++ b/hostap/src/l2_packet/l2_packet_linux.c
@@ -0,0 +1,447 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with Linux packet sockets
+ * Copyright (c) 2003-2015, 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 <sys/ioctl.h>
+#include <netpacket/packet.h>
+#include <net/if.h>
+#include <linux/filter.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "crypto/sha1.h"
+#include "crypto/crypto.h"
+#include "l2_packet.h"
+
+
+struct l2_packet_data {
+	int fd; /* packet socket for EAPOL frames */
+	char ifname[IFNAMSIZ + 1];
+	int ifindex;
+	u8 own_addr[ETH_ALEN];
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len);
+	void *rx_callback_ctx;
+	int l2_hdr; /* whether to include layer 2 (Ethernet) header data
+		     * buffers */
+
+	/* For working around Linux packet socket behavior and regression. */
+	int fd_br_rx;
+	int last_from_br;
+	u8 last_hash[SHA1_MAC_LEN];
+	unsigned int num_rx, num_rx_br;
+};
+
+/* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and
+ * src port bootps and dst port bootpc'
+ */
+static struct sock_filter dhcp_sock_filter_insns[] = {
+	{ 0x80, 0, 0, 0x00000000 },
+	{ 0x35, 0, 12, 0x00000116 },
+	{ 0x28, 0, 0, 0x0000000c },
+	{ 0x15, 0, 10, 0x00000800 },
+	{ 0x30, 0, 0, 0x00000017 },
+	{ 0x15, 0, 8, 0x00000011 },
+	{ 0x28, 0, 0, 0x00000014 },
+	{ 0x45, 6, 0, 0x00001fff },
+	{ 0xb1, 0, 0, 0x0000000e },
+	{ 0x48, 0, 0, 0x0000000e },
+	{ 0x15, 0, 3, 0x00000043 },
+	{ 0x48, 0, 0, 0x00000010 },
+	{ 0x15, 0, 1, 0x00000044 },
+	{ 0x6, 0, 0, 0x00000bb8 },
+	{ 0x6, 0, 0, 0x00000000 },
+};
+
+static const struct sock_fprog dhcp_sock_filter = {
+	.len = ARRAY_SIZE(dhcp_sock_filter_insns),
+	.filter = dhcp_sock_filter_insns,
+};
+
+
+/* Generated by 'sudo tcpdump -dd -s 1500 multicast and ip6[6]=58' */
+static struct sock_filter ndisc_sock_filter_insns[] = {
+	{ 0x30, 0, 0, 0x00000000 },
+	{ 0x45, 0, 5, 0x00000001 },
+	{ 0x28, 0, 0, 0x0000000c },
+	{ 0x15, 0, 3, 0x000086dd },
+	{ 0x30, 0, 0, 0x00000014 },
+	{ 0x15, 0, 1, 0x0000003a },
+	{ 0x6, 0, 0, 0x000005dc },
+	{ 0x6, 0, 0, 0x00000000 },
+};
+
+static const struct sock_fprog ndisc_sock_filter = {
+	.len = ARRAY_SIZE(ndisc_sock_filter_insns),
+	.filter = ndisc_sock_filter_insns,
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+	os_memcpy(addr, l2->own_addr, ETH_ALEN);
+	return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+		   const u8 *buf, size_t len)
+{
+	int ret;
+	if (l2 == NULL)
+		return -1;
+	if (l2->l2_hdr) {
+		ret = send(l2->fd, buf, len, 0);
+		if (ret < 0)
+			wpa_printf(MSG_ERROR, "l2_packet_send - send: %s",
+				   strerror(errno));
+	} else {
+		struct sockaddr_ll ll;
+		os_memset(&ll, 0, sizeof(ll));
+		ll.sll_family = AF_PACKET;
+		ll.sll_ifindex = l2->ifindex;
+		ll.sll_protocol = htons(proto);
+		ll.sll_halen = ETH_ALEN;
+		os_memcpy(ll.sll_addr, dst_addr, ETH_ALEN);
+		ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll,
+			     sizeof(ll));
+		if (ret < 0) {
+			wpa_printf(MSG_ERROR, "l2_packet_send - sendto: %s",
+				   strerror(errno));
+		}
+	}
+	return ret;
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct l2_packet_data *l2 = eloop_ctx;
+	u8 buf[2300];
+	int res;
+	struct sockaddr_ll ll;
+	socklen_t fromlen;
+
+	l2->num_rx++;
+	os_memset(&ll, 0, sizeof(ll));
+	fromlen = sizeof(ll);
+	res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
+		       &fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "l2_packet_receive - recvfrom: %s",
+			   strerror(errno));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
+		   __func__, MAC2STR(ll.sll_addr), (int) res);
+
+	if (l2->fd_br_rx >= 0) {
+		u8 hash[SHA1_MAC_LEN];
+		const u8 *addr[1];
+		size_t len[1];
+
+		/*
+		 * Close the workaround socket if the kernel version seems to be
+		 * able to deliver packets through the packet socket before
+		 * authorization has been completed (in dormant state).
+		 */
+		if (l2->num_rx_br <= 1) {
+			wpa_printf(MSG_DEBUG,
+				   "l2_packet_receive: Main packet socket for %s seems to have working RX - close workaround bridge socket",
+				   l2->ifname);
+			eloop_unregister_read_sock(l2->fd_br_rx);
+			close(l2->fd_br_rx);
+			l2->fd_br_rx = -1;
+		}
+
+		addr[0] = buf;
+		len[0] = res;
+		sha1_vector(1, addr, len, hash);
+		if (l2->last_from_br &&
+		    os_memcmp(hash, l2->last_hash, SHA1_MAC_LEN) == 0) {
+			wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX",
+				   __func__);
+			return;
+		}
+		os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
+	}
+
+	l2->last_from_br = 0;
+	l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
+}
+
+
+static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct l2_packet_data *l2 = eloop_ctx;
+	u8 buf[2300];
+	int res;
+	struct sockaddr_ll ll;
+	socklen_t fromlen;
+	u8 hash[SHA1_MAC_LEN];
+	const u8 *addr[1];
+	size_t len[1];
+
+	l2->num_rx_br++;
+	os_memset(&ll, 0, sizeof(ll));
+	fromlen = sizeof(ll);
+	res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
+		       &fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "l2_packet_receive_br - recvfrom: %s",
+			   strerror(errno));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
+		   __func__, MAC2STR(ll.sll_addr), (int) res);
+
+	addr[0] = buf;
+	len[0] = res;
+	sha1_vector(1, addr, len, hash);
+	if (!l2->last_from_br &&
+	    os_memcmp(hash, l2->last_hash, SHA1_MAC_LEN) == 0) {
+		wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX", __func__);
+		return;
+	}
+	l2->last_from_br = 1;
+	os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
+	l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
+}
+
+
+struct l2_packet_data * l2_packet_init(
+	const char *ifname, const u8 *own_addr, unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr)
+{
+	struct l2_packet_data *l2;
+	struct ifreq ifr;
+	struct sockaddr_ll ll;
+
+	l2 = os_zalloc(sizeof(struct l2_packet_data));
+	if (l2 == NULL)
+		return NULL;
+	os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+	l2->rx_callback = rx_callback;
+	l2->rx_callback_ctx = rx_callback_ctx;
+	l2->l2_hdr = l2_hdr;
+	l2->fd_br_rx = -1;
+
+	l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
+			htons(protocol));
+	if (l2->fd < 0) {
+		wpa_printf(MSG_ERROR, "%s: socket(PF_PACKET): %s",
+			   __func__, strerror(errno));
+		os_free(l2);
+		return NULL;
+	}
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name));
+	if (ioctl(l2->fd, SIOCGIFINDEX, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFINDEX]: %s",
+			   __func__, strerror(errno));
+		close(l2->fd);
+		os_free(l2);
+		return NULL;
+	}
+	l2->ifindex = ifr.ifr_ifindex;
+
+	os_memset(&ll, 0, sizeof(ll));
+	ll.sll_family = PF_PACKET;
+	ll.sll_ifindex = ifr.ifr_ifindex;
+	ll.sll_protocol = htons(protocol);
+	if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+		wpa_printf(MSG_ERROR, "%s: bind[PF_PACKET]: %s",
+			   __func__, strerror(errno));
+		close(l2->fd);
+		os_free(l2);
+		return NULL;
+	}
+
+	if (ioctl(l2->fd, SIOCGIFHWADDR, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFHWADDR]: %s",
+			   __func__, strerror(errno));
+		close(l2->fd);
+		os_free(l2);
+		return NULL;
+	}
+	os_memcpy(l2->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+	eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+
+	return l2;
+}
+
+
+struct l2_packet_data * l2_packet_init_bridge(
+	const char *br_ifname, const char *ifname, const u8 *own_addr,
+	unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr)
+{
+	struct l2_packet_data *l2;
+	struct sock_filter ethertype_sock_filter_insns[] = {
+		/* Load ethertype */
+		BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 2 * ETH_ALEN),
+		/* Jump over next statement if ethertype does not match */
+		BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, protocol, 0, 1),
+		/* Ethertype match - return all */
+		BPF_STMT(BPF_RET | BPF_K, ~0),
+		/* No match - drop */
+		BPF_STMT(BPF_RET | BPF_K, 0)
+	};
+	const struct sock_fprog ethertype_sock_filter = {
+		.len = ARRAY_SIZE(ethertype_sock_filter_insns),
+		.filter = ethertype_sock_filter_insns,
+	};
+	struct sockaddr_ll ll;
+
+	l2 = l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+			    rx_callback_ctx, l2_hdr);
+	if (!l2)
+		return NULL;
+
+	/*
+	 * The Linux packet socket behavior has changed over the years and there
+	 * is an inconvenient regression in it that breaks RX for a specific
+	 * protocol from interfaces in a bridge when that interface is not in
+	 * fully operation state (i.e., when in station mode and not completed
+	 * authorization). To work around this, register ETH_P_ALL version of
+	 * the packet socket bound to the real netdev and use socket filter to
+	 * match the ethertype value. This version is less efficient, but
+	 * required for functionality with many kernel version. If the main
+	 * packet socket is found to be working, this less efficient version
+	 * gets closed automatically.
+	 */
+
+	l2->fd_br_rx = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
+			      htons(ETH_P_ALL));
+	if (l2->fd_br_rx < 0) {
+		wpa_printf(MSG_DEBUG, "%s: socket(PF_PACKET-fd_br_rx): %s",
+			   __func__, strerror(errno));
+		/* try to continue without the workaround RX socket */
+		return l2;
+	}
+
+	os_memset(&ll, 0, sizeof(ll));
+	ll.sll_family = PF_PACKET;
+	ll.sll_ifindex = if_nametoindex(ifname);
+	ll.sll_protocol = htons(ETH_P_ALL);
+	if (bind(l2->fd_br_rx, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+		wpa_printf(MSG_DEBUG, "%s: bind[PF_PACKET-fd_br_rx]: %s",
+			   __func__, strerror(errno));
+		/* try to continue without the workaround RX socket */
+		close(l2->fd_br_rx);
+		l2->fd_br_rx = -1;
+		return l2;
+	}
+
+	if (setsockopt(l2->fd_br_rx, SOL_SOCKET, SO_ATTACH_FILTER,
+		       &ethertype_sock_filter, sizeof(struct sock_fprog))) {
+		wpa_printf(MSG_DEBUG,
+			   "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s",
+			   strerror(errno));
+		/* try to continue without the workaround RX socket */
+		close(l2->fd_br_rx);
+		l2->fd_br_rx = -1;
+		return l2;
+	}
+
+	eloop_register_read_sock(l2->fd_br_rx, l2_packet_receive_br, l2, NULL);
+
+	return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+	if (l2 == NULL)
+		return;
+
+	if (l2->fd >= 0) {
+		eloop_unregister_read_sock(l2->fd);
+		close(l2->fd);
+	}
+
+	if (l2->fd_br_rx >= 0) {
+		eloop_unregister_read_sock(l2->fd_br_rx);
+		close(l2->fd_br_rx);
+	}
+
+	os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+	int s;
+	struct ifreq ifr;
+	struct sockaddr_in *saddr;
+	size_t res;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "%s: socket: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name));
+	if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
+		if (errno != EADDRNOTAVAIL)
+			wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFADDR]: %s",
+				   __func__, strerror(errno));
+		close(s);
+		return -1;
+	}
+	close(s);
+	saddr = aliasing_hide_typecast(&ifr.ifr_addr, struct sockaddr_in);
+	if (saddr->sin_family != AF_INET)
+		return -1;
+	res = os_strlcpy(buf, inet_ntoa(saddr->sin_addr), len);
+	if (res >= len)
+		return -1;
+	return 0;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+}
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+				enum l2_packet_filter_type type)
+{
+	const struct sock_fprog *sock_filter;
+
+	switch (type) {
+	case L2_PACKET_FILTER_DHCP:
+		sock_filter = &dhcp_sock_filter;
+		break;
+	case L2_PACKET_FILTER_NDISC:
+		sock_filter = &ndisc_sock_filter;
+		break;
+	default:
+		return -1;
+	}
+
+	if (setsockopt(l2->fd, SOL_SOCKET, SO_ATTACH_FILTER,
+		       sock_filter, sizeof(struct sock_fprog))) {
+		wpa_printf(MSG_ERROR,
+			   "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/hostap/src/l2_packet/l2_packet_ndis.c b/hostap/src/l2_packet/l2_packet_ndis.c
new file mode 100644
index 0000000..7167781
--- /dev/null
+++ b/hostap/src/l2_packet/l2_packet_ndis.c
@@ -0,0 +1,535 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This implementation requires Windows specific event loop implementation,
+ * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with
+ * driver_ndis.c, so only that driver interface can be used and
+ * CONFIG_USE_NDISUIO must be defined.
+ *
+ * WinXP version of the code uses overlapped I/O and a single threaded design
+ * with callback functions from I/O code. WinCE version uses a separate RX
+ * thread that blocks on ReadFile() whenever the media status is connected.
+ */
+
+#include "includes.h"
+#include <winsock2.h>
+#include <ntddndis.h>
+
+#ifdef _WIN32_WCE
+#include <winioctl.h>
+#include <nuiouser.h>
+#endif /* _WIN32_WCE */
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+#ifndef _WIN32_WCE
+/* from nuiouser.h */
+#define FSCTL_NDISUIO_BASE      FILE_DEVICE_NETWORK
+#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
+	CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
+#define IOCTL_NDISUIO_SET_ETHER_TYPE \
+	_NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#endif /* _WIN32_WCE */
+
+/* From driver_ndis.c to shared the handle to NDISUIO */
+HANDLE driver_ndis_get_ndisuio_handle(void);
+
+/*
+ * NDISUIO supports filtering of only one ethertype at the time, so we must
+ * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth
+ * whenever wpa_supplicant is trying to pre-authenticate and then switching
+ * back to EAPOL when pre-authentication has been completed.
+ */
+
+struct l2_packet_data;
+
+struct l2_packet_ndisuio_global {
+	int refcount;
+	unsigned short first_proto;
+	struct l2_packet_data *l2[2];
+#ifdef _WIN32_WCE
+	HANDLE rx_thread;
+	HANDLE stop_request;
+	HANDLE ready_for_read;
+	HANDLE rx_processed;
+#endif /* _WIN32_WCE */
+};
+
+static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL;
+
+struct l2_packet_data {
+	char ifname[100];
+	u8 own_addr[ETH_ALEN];
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len);
+	void *rx_callback_ctx;
+	int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to
+		     * rx_callback and l2_packet_send() */
+	HANDLE rx_avail;
+#ifndef _WIN32_WCE
+	OVERLAPPED rx_overlapped;
+#endif /* _WIN32_WCE */
+	u8 rx_buf[1514];
+	DWORD rx_written;
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+	os_memcpy(addr, l2->own_addr, ETH_ALEN);
+	return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+		   const u8 *buf, size_t len)
+{
+	BOOL res;
+	DWORD written;
+	struct l2_ethhdr *eth;
+#ifndef _WIN32_WCE
+	OVERLAPPED overlapped;
+#endif /* _WIN32_WCE */
+	OVERLAPPED *o;
+
+	if (l2 == NULL)
+		return -1;
+
+#ifdef _WIN32_WCE
+	o = NULL;
+#else /* _WIN32_WCE */
+	os_memset(&overlapped, 0, sizeof(overlapped));
+	o = &overlapped;
+#endif /* _WIN32_WCE */
+
+	if (l2->l2_hdr) {
+		res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len,
+				&written, o);
+	} else {
+		size_t mlen = sizeof(*eth) + len;
+		eth = os_malloc(mlen);
+		if (eth == NULL)
+			return -1;
+
+		os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+		os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+		eth->h_proto = htons(proto);
+		os_memcpy(eth + 1, buf, len);
+		res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen,
+				&written, o);
+		os_free(eth);
+	}
+
+	if (!res) {
+		DWORD err = GetLastError();
+#ifndef _WIN32_WCE
+		if (err == ERROR_IO_PENDING) {
+			wpa_printf(MSG_DEBUG, "L2(NDISUIO): Wait for pending "
+				   "write to complete");
+			res = GetOverlappedResult(
+				driver_ndis_get_ndisuio_handle(), &overlapped,
+				&written, TRUE);
+			if (!res) {
+				wpa_printf(MSG_DEBUG, "L2(NDISUIO): "
+					   "GetOverlappedResult failed: %d",
+					   (int) GetLastError());
+				return -1;
+			}
+			return 0;
+		}
+#endif /* _WIN32_WCE */
+		wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d",
+			   (int) GetLastError());
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void l2_packet_callback(struct l2_packet_data *l2);
+
+#ifdef _WIN32_WCE
+static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2)
+{
+	HANDLE handles[2];
+
+	wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile");
+	if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
+		      sizeof(l2->rx_buf), &l2->rx_written, NULL)) {
+		DWORD err = GetLastError();
+		wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: "
+			   "%d", (int) err);
+		/*
+		 * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED
+		 * error whenever the connection is not up. Yield the thread to
+		 * avoid triggering a busy loop. Connection event should stop
+		 * us from looping for long, but we need to allow enough CPU
+		 * for the main thread to process the media disconnection.
+		 */
+		Sleep(100);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet",
+		   (int) l2->rx_written);
+
+	/*
+	 * Notify the main thread about the availability of a frame and wait
+	 * for the frame to be processed.
+	 */
+	SetEvent(l2->rx_avail);
+	handles[0] = l2_ndisuio_global->stop_request;
+	handles[1] = l2_ndisuio_global->rx_processed;
+	WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+	ResetEvent(l2_ndisuio_global->rx_processed);
+}
+
+
+static DWORD WINAPI l2_packet_rx_thread(LPVOID arg)
+{
+	struct l2_packet_data *l2 = arg;
+	DWORD res;
+	HANDLE handles[2];
+	int run = 1;
+
+	wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started");
+	handles[0] = l2_ndisuio_global->stop_request;
+	handles[1] = l2_ndisuio_global->ready_for_read;
+
+	/*
+	 * Unfortunately, NDISUIO on WinCE does not seem to support waiting
+	 * on the handle. There do not seem to be anything else that we could
+	 * wait for either. If one were to modify NDISUIO to set a named event
+	 * whenever packets are available, this event could be used here to
+	 * avoid having to poll for new packets or we could even move to use a
+	 * single threaded design.
+	 *
+	 * In addition, NDISUIO on WinCE is returning
+	 * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while
+	 * the adapter is not in connected state. For now, we are just using a
+	 * local event to allow ReadFile calls only after having received NDIS
+	 * media connect event. This event could be easily converted to handle
+	 * another event if the protocol driver is replaced with somewhat more
+	 * useful design.
+	 */
+
+	while (l2_ndisuio_global && run) {
+		res = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+		switch (res) {
+		case WAIT_OBJECT_0:
+			wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received "
+				   "request to stop RX thread");
+			run = 0;
+			break;
+		case WAIT_OBJECT_0 + 1:
+			l2_packet_rx_thread_try_read(l2);
+			break;
+		case WAIT_FAILED:
+		default:
+			wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: "
+				   "WaitForMultipleObjects failed: %d",
+				   (int) GetLastError());
+			run = 0;
+			break;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped");
+
+	return 0;
+}
+#else /* _WIN32_WCE */
+static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive)
+{
+	os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped));
+	l2->rx_overlapped.hEvent = l2->rx_avail;
+	if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
+		      sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped))
+	{
+		DWORD err = GetLastError();
+		if (err != ERROR_IO_PENDING) {
+			wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: "
+				   "%d", (int) err);
+			return -1;
+		}
+		/*
+		 * Once read is completed, l2_packet_rx_event() will be
+		 * called.
+		 */
+	} else {
+		wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data "
+			   "without wait for completion");
+		if (!recursive)
+			l2_packet_callback(l2);
+	}
+
+	return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static void l2_packet_callback(struct l2_packet_data *l2)
+{
+	const u8 *rx_buf, *rx_src;
+	size_t rx_len;
+	struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf;
+
+	wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes",
+		   (int) l2->rx_written);
+
+	if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) {
+		rx_buf = (u8 *) ethhdr;
+		rx_len = l2->rx_written;
+	} else {
+		rx_buf = (u8 *) (ethhdr + 1);
+		rx_len = l2->rx_written - sizeof(*ethhdr);
+	}
+	rx_src = ethhdr->h_source;
+
+	l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len);
+#ifndef _WIN32_WCE
+	l2_ndisuio_start_read(l2, 1);
+#endif /* _WIN32_WCE */
+}
+
+
+static void l2_packet_rx_event(void *eloop_data, void *user_data)
+{
+	struct l2_packet_data *l2 = eloop_data;
+
+	if (l2_ndisuio_global)
+		l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1];
+
+	ResetEvent(l2->rx_avail);
+
+#ifndef _WIN32_WCE
+	if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(),
+				 &l2->rx_overlapped, &l2->rx_written, FALSE)) {
+		wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult "
+			   "failed: %d", (int) GetLastError());
+		return;
+	}
+#endif /* _WIN32_WCE */
+
+	l2_packet_callback(l2);
+
+#ifdef _WIN32_WCE
+	SetEvent(l2_ndisuio_global->rx_processed);
+#endif /* _WIN32_WCE */
+}
+
+
+static int l2_ndisuio_set_ether_type(unsigned short protocol)
+{
+	USHORT proto = htons(protocol);
+	DWORD written;
+
+	if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
+			     IOCTL_NDISUIO_SET_ETHER_TYPE, &proto,
+			     sizeof(proto), NULL, 0, &written, NULL)) {
+		wpa_printf(MSG_ERROR, "L2(NDISUIO): "
+			   "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d",
+			   (int) GetLastError());
+		return -1;
+	}
+
+	return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+	const char *ifname, const u8 *own_addr, unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr)
+{
+	struct l2_packet_data *l2;
+
+	if (l2_ndisuio_global == NULL) {
+		l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global));
+		if (l2_ndisuio_global == NULL)
+			return NULL;
+		l2_ndisuio_global->first_proto = protocol;
+	}
+	if (l2_ndisuio_global->refcount >= 2) {
+		wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two "
+			   "simultaneous connections allowed");
+		return NULL;
+	}
+	l2_ndisuio_global->refcount++;
+
+	l2 = os_zalloc(sizeof(struct l2_packet_data));
+	if (l2 == NULL)
+		return NULL;
+	l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2;
+
+	os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+	l2->rx_callback = rx_callback;
+	l2->rx_callback_ctx = rx_callback_ctx;
+	l2->l2_hdr = l2_hdr;
+
+	if (own_addr)
+		os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
+
+	if (l2_ndisuio_set_ether_type(protocol) < 0) {
+		os_free(l2);
+		return NULL;
+	}
+
+	if (l2_ndisuio_global->refcount > 1) {
+		wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting "
+			   "filtering ethertype to %04x", protocol);
+		if (l2_ndisuio_global->l2[0])
+			l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail;
+		return l2;
+	}
+
+	l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if (l2->rx_avail == NULL) {
+		os_free(l2);
+		return NULL;
+	}
+
+	eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
+			     l2_packet_rx_event, l2, NULL);
+
+#ifdef _WIN32_WCE
+	l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL);
+	/*
+	 * This event is being set based on media connect/disconnect
+	 * notifications in driver_ndis.c.
+	 */
+	l2_ndisuio_global->ready_for_read =
+		CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
+	l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if (l2_ndisuio_global->stop_request == NULL ||
+	    l2_ndisuio_global->ready_for_read == NULL ||
+	    l2_ndisuio_global->rx_processed == NULL) {
+		if (l2_ndisuio_global->stop_request) {
+			CloseHandle(l2_ndisuio_global->stop_request);
+			l2_ndisuio_global->stop_request = NULL;
+		}
+		if (l2_ndisuio_global->ready_for_read) {
+			CloseHandle(l2_ndisuio_global->ready_for_read);
+			l2_ndisuio_global->ready_for_read = NULL;
+		}
+		if (l2_ndisuio_global->rx_processed) {
+			CloseHandle(l2_ndisuio_global->rx_processed);
+			l2_ndisuio_global->rx_processed = NULL;
+		}
+		eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+		os_free(l2);
+		return NULL;
+	}
+
+	l2_ndisuio_global->rx_thread = CreateThread(NULL, 0,
+						    l2_packet_rx_thread, l2, 0,
+						    NULL);
+	if (l2_ndisuio_global->rx_thread == NULL) {
+		wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX "
+			   "thread: %d", (int) GetLastError());
+		eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+		CloseHandle(l2_ndisuio_global->stop_request);
+		l2_ndisuio_global->stop_request = NULL;
+		os_free(l2);
+		return NULL;
+	}
+#else /* _WIN32_WCE */
+	l2_ndisuio_start_read(l2, 0);
+#endif /* _WIN32_WCE */
+
+	return l2;
+}
+
+
+struct l2_packet_data * l2_packet_init_bridge(
+	const char *br_ifname, const char *ifname, const u8 *own_addr,
+	unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr)
+{
+	return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+			      rx_callback_ctx, l2_hdr);
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+	if (l2 == NULL)
+		return;
+
+	if (l2_ndisuio_global) {
+		l2_ndisuio_global->refcount--;
+		l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL;
+		if (l2_ndisuio_global->refcount) {
+			wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering "
+				   "ethertype to %04x",
+				   l2_ndisuio_global->first_proto);
+			l2_ndisuio_set_ether_type(
+				l2_ndisuio_global->first_proto);
+			return;
+		}
+
+#ifdef _WIN32_WCE
+		wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to "
+			   "stop");
+		SetEvent(l2_ndisuio_global->stop_request);
+		/*
+		 * Cancel pending ReadFile() in the RX thread (if we were still
+		 * connected at this point).
+		 */
+		if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
+				     IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL,
+				     NULL)) {
+			wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ "
+				   "failed: %d", (int) GetLastError());
+			/* RX thread will exit blocking ReadFile once NDISUIO
+			 * notices that the adapter is disconnected. */
+		}
+		WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE);
+		wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited");
+		CloseHandle(l2_ndisuio_global->rx_thread);
+		CloseHandle(l2_ndisuio_global->stop_request);
+		CloseHandle(l2_ndisuio_global->ready_for_read);
+		CloseHandle(l2_ndisuio_global->rx_processed);
+#endif /* _WIN32_WCE */
+
+		os_free(l2_ndisuio_global);
+		l2_ndisuio_global = NULL;
+	}
+
+#ifndef _WIN32_WCE
+	CancelIo(driver_ndis_get_ndisuio_handle());
+#endif /* _WIN32_WCE */
+
+	eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+	CloseHandle(l2->rx_avail);
+	os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+	return -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+}
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+				enum l2_packet_filter_type type)
+{
+	return -1;
+}
diff --git a/hostap/src/l2_packet/l2_packet_none.c b/hostap/src/l2_packet/l2_packet_none.c
new file mode 100644
index 0000000..307fc6d
--- /dev/null
+++ b/hostap/src/l2_packet/l2_packet_none.c
@@ -0,0 +1,137 @@
+/*
+ * WPA Supplicant - Layer2 packet handling example with dummy functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file can be used as a starting point for layer2 packet implementation.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+struct l2_packet_data {
+	char ifname[17];
+	u8 own_addr[ETH_ALEN];
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len);
+	void *rx_callback_ctx;
+	int l2_hdr; /* whether to include layer 2 (Ethernet) header data
+		     * buffers */
+	int fd;
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+	os_memcpy(addr, l2->own_addr, ETH_ALEN);
+	return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+		   const u8 *buf, size_t len)
+{
+	if (l2 == NULL)
+		return -1;
+
+	/*
+	 * TODO: Send frame (may need different implementation depending on
+	 * whether l2->l2_hdr is set).
+	 */
+
+	return 0;
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct l2_packet_data *l2 = eloop_ctx;
+	u8 buf[2300];
+	int res;
+
+	/* TODO: receive frame (e.g., recv() using sock */
+	buf[0] = 0;
+	res = 0;
+
+	l2->rx_callback(l2->rx_callback_ctx, NULL /* TODO: src addr */,
+			buf, res);
+}
+
+
+struct l2_packet_data * l2_packet_init(
+	const char *ifname, const u8 *own_addr, unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr)
+{
+	struct l2_packet_data *l2;
+
+	l2 = os_zalloc(sizeof(struct l2_packet_data));
+	if (l2 == NULL)
+		return NULL;
+	os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+	l2->rx_callback = rx_callback;
+	l2->rx_callback_ctx = rx_callback_ctx;
+	l2->l2_hdr = l2_hdr;
+
+	/*
+	 * TODO: open connection for receiving frames
+	 */
+	l2->fd = -1;
+	if (l2->fd >= 0)
+		eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+
+	return l2;
+}
+
+
+struct l2_packet_data * l2_packet_init_bridge(
+	const char *br_ifname, const char *ifname, const u8 *own_addr,
+	unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr)
+{
+	return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+			      rx_callback_ctx, l2_hdr);
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+	if (l2 == NULL)
+		return;
+
+	if (l2->fd >= 0) {
+		eloop_unregister_read_sock(l2->fd);
+		/* TODO: close connection */
+	}
+		
+	os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+	/* TODO: get interface IP address */
+	return -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+	/* This function can be left empty */
+}
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+				enum l2_packet_filter_type type)
+{
+	return -1;
+}
diff --git a/hostap/src/l2_packet/l2_packet_pcap.c b/hostap/src/l2_packet/l2_packet_pcap.c
new file mode 100644
index 0000000..bb4f4a3
--- /dev/null
+++ b/hostap/src/l2_packet/l2_packet_pcap.c
@@ -0,0 +1,388 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with libpcap/libdnet and WinPcap
+ * Copyright (c) 2003-2006, 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"
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/ioctl.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+#include <pcap.h>
+#ifndef CONFIG_WINPCAP
+#include <dnet.h>
+#endif /* CONFIG_WINPCAP */
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+struct l2_packet_data {
+	pcap_t *pcap;
+#ifdef CONFIG_WINPCAP
+	unsigned int num_fast_poll;
+#else /* CONFIG_WINPCAP */
+	eth_t *eth;
+#endif /* CONFIG_WINPCAP */
+	char ifname[100];
+	u8 own_addr[ETH_ALEN];
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len);
+	void *rx_callback_ctx;
+	int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls
+			* to rx_callback */
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+	os_memcpy(addr, l2->own_addr, ETH_ALEN);
+	return 0;
+}
+
+
+#ifndef CONFIG_WINPCAP
+static int l2_packet_init_libdnet(struct l2_packet_data *l2)
+{
+	eth_addr_t own_addr;
+
+	l2->eth = eth_open(l2->ifname);
+	if (!l2->eth) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to open interface '%s' - eth_open: %s",
+			   l2->ifname, strerror(errno));
+		return -1;
+	}
+
+	if (eth_get(l2->eth, &own_addr) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to get own hw address from interface '%s' - eth_get: %s",
+			   l2->ifname, strerror(errno));
+		eth_close(l2->eth);
+		l2->eth = NULL;
+		return -1;
+	}
+	os_memcpy(l2->own_addr, own_addr.data, ETH_ALEN);
+
+	return 0;
+}
+#endif /* CONFIG_WINPCAP */
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+		   const u8 *buf, size_t len)
+{
+	int ret;
+	struct l2_ethhdr *eth;
+
+	if (l2 == NULL)
+		return -1;
+
+	if (l2->l2_hdr) {
+#ifdef CONFIG_WINPCAP
+		ret = pcap_sendpacket(l2->pcap, buf, len);
+#else /* CONFIG_WINPCAP */
+		ret = eth_send(l2->eth, buf, len);
+#endif /* CONFIG_WINPCAP */
+	} else {
+		size_t mlen = sizeof(*eth) + len;
+		eth = os_malloc(mlen);
+		if (eth == NULL)
+			return -1;
+
+		os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+		os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+		eth->h_proto = htons(proto);
+		os_memcpy(eth + 1, buf, len);
+
+#ifdef CONFIG_WINPCAP
+		ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen);
+#else /* CONFIG_WINPCAP */
+		ret = eth_send(l2->eth, (u8 *) eth, mlen);
+#endif /* CONFIG_WINPCAP */
+
+		os_free(eth);
+	}
+
+	return ret;
+}
+
+
+#ifndef CONFIG_WINPCAP
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct l2_packet_data *l2 = eloop_ctx;
+	pcap_t *pcap = sock_ctx;
+	struct pcap_pkthdr hdr;
+	const u_char *packet;
+	struct l2_ethhdr *ethhdr;
+	unsigned char *buf;
+	size_t len;
+
+	packet = pcap_next(pcap, &hdr);
+
+	if (packet == NULL || hdr.caplen < sizeof(*ethhdr))
+		return;
+
+	ethhdr = (struct l2_ethhdr *) packet;
+	if (l2->l2_hdr) {
+		buf = (unsigned char *) ethhdr;
+		len = hdr.caplen;
+	} else {
+		buf = (unsigned char *) (ethhdr + 1);
+		len = hdr.caplen - sizeof(*ethhdr);
+	}
+	l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+}
+#endif /* CONFIG_WINPCAP */
+
+
+#ifdef CONFIG_WINPCAP
+static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr,
+				 const u_char *pkt_data)
+{
+	struct l2_packet_data *l2 = (struct l2_packet_data *) user;
+	struct l2_ethhdr *ethhdr;
+	unsigned char *buf;
+	size_t len;
+
+	if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr))
+		return;
+
+	ethhdr = (struct l2_ethhdr *) pkt_data;
+	if (l2->l2_hdr) {
+		buf = (unsigned char *) ethhdr;
+		len = hdr->caplen;
+	} else {
+		buf = (unsigned char *) (ethhdr + 1);
+		len = hdr->caplen - sizeof(*ethhdr);
+	}
+	l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+	/*
+	 * Use shorter poll interval for 3 seconds to reduce latency during key
+	 * handshake.
+	 */
+	l2->num_fast_poll = 3 * 50;
+}
+
+
+static void l2_packet_receive_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct l2_packet_data *l2 = eloop_ctx;
+	pcap_t *pcap = timeout_ctx;
+	int timeout;
+
+	if (l2->num_fast_poll > 0) {
+		timeout = 20000;
+		l2->num_fast_poll--;
+	} else
+		timeout = 100000;
+
+	/* Register new timeout before calling l2_packet_receive() since
+	 * receive handler may free this l2_packet instance (which will
+	 * cancel this timeout). */
+	eloop_register_timeout(0, timeout, l2_packet_receive_timeout,
+			       l2, pcap);
+	pcap_dispatch(pcap, 10, l2_packet_receive_cb, (u_char *) l2);
+}
+#endif /* CONFIG_WINPCAP */
+
+
+static int l2_packet_init_libpcap(struct l2_packet_data *l2,
+				  unsigned short protocol)
+{
+	bpf_u_int32 pcap_maskp, pcap_netp;
+	char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
+	struct bpf_program pcap_fp;
+
+#ifdef CONFIG_WINPCAP
+	char ifname[128];
+	os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", l2->ifname);
+	pcap_lookupnet(ifname, &pcap_netp, &pcap_maskp, pcap_err);
+	l2->pcap = pcap_open_live(ifname, 2500, 0, 10, pcap_err);
+	if (l2->pcap == NULL) {
+		fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+		fprintf(stderr, "ifname='%s'\n", ifname);
+		return -1;
+	}
+	if (pcap_setnonblock(l2->pcap, 1, pcap_err) < 0)
+		fprintf(stderr, "pcap_setnonblock: %s\n",
+			pcap_geterr(l2->pcap));
+#else /* CONFIG_WINPCAP */
+	pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
+	l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err);
+	if (l2->pcap == NULL) {
+		fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+		fprintf(stderr, "ifname='%s'\n", l2->ifname);
+		return -1;
+	}
+	if (pcap_datalink(l2->pcap) != DLT_EN10MB &&
+	    pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) {
+		fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n",
+			pcap_geterr(l2->pcap));
+		return -1;
+	}
+#endif /* CONFIG_WINPCAP */
+	os_snprintf(pcap_filter, sizeof(pcap_filter),
+		    "not ether src " MACSTR " and "
+		    "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
+		    "ether proto 0x%x",
+		    MAC2STR(l2->own_addr), /* do not receive own packets */
+		    MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
+		    protocol);
+	if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
+		fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
+		return -1;
+	}
+
+	if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) {
+		fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap));
+		return -1;
+	}
+
+	pcap_freecode(&pcap_fp);
+#ifdef BIOCIMMEDIATE
+	/*
+	 * When libpcap uses BPF we must enable "immediate mode" to
+	 * receive frames right away; otherwise the system may
+	 * buffer them for us.
+	 */
+	{
+		unsigned int on = 1;
+		if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) {
+			fprintf(stderr, "%s: cannot enable immediate mode on "
+				"interface %s: %s\n",
+				__func__, l2->ifname, strerror(errno));
+			/* XXX should we fail? */
+		}
+	}
+#endif /* BIOCIMMEDIATE */
+
+#ifdef CONFIG_WINPCAP
+	eloop_register_timeout(0, 100000, l2_packet_receive_timeout,
+			       l2, l2->pcap);
+#else /* CONFIG_WINPCAP */
+	eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap),
+				 l2_packet_receive, l2, l2->pcap);
+#endif /* CONFIG_WINPCAP */
+
+	return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+	const char *ifname, const u8 *own_addr, unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr)
+{
+	struct l2_packet_data *l2;
+
+	l2 = os_zalloc(sizeof(struct l2_packet_data));
+	if (l2 == NULL)
+		return NULL;
+	os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+	l2->rx_callback = rx_callback;
+	l2->rx_callback_ctx = rx_callback_ctx;
+	l2->l2_hdr = l2_hdr;
+
+#ifdef CONFIG_WINPCAP
+	if (own_addr)
+		os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
+#else /* CONFIG_WINPCAP */
+	if (l2_packet_init_libdnet(l2))
+		return NULL;
+#endif /* CONFIG_WINPCAP */
+
+	if (l2_packet_init_libpcap(l2, protocol)) {
+#ifndef CONFIG_WINPCAP
+		eth_close(l2->eth);
+#endif /* CONFIG_WINPCAP */
+		os_free(l2);
+		return NULL;
+	}
+
+	return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+	if (l2 == NULL)
+		return;
+
+#ifdef CONFIG_WINPCAP
+	eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap);
+#else /* CONFIG_WINPCAP */
+	if (l2->eth)
+		eth_close(l2->eth);
+	eloop_unregister_read_sock(pcap_get_selectable_fd(l2->pcap));
+#endif /* CONFIG_WINPCAP */
+	if (l2->pcap)
+		pcap_close(l2->pcap);
+	os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+	pcap_if_t *devs, *dev;
+	struct pcap_addr *addr;
+	struct sockaddr_in *saddr;
+	int found = 0;
+	char err[PCAP_ERRBUF_SIZE + 1];
+
+	if (pcap_findalldevs(&devs, err) < 0) {
+		wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
+		return -1;
+	}
+
+	for (dev = devs; dev && !found; dev = dev->next) {
+		if (os_strcmp(dev->name, l2->ifname) != 0)
+			continue;
+
+		addr = dev->addresses;
+		while (addr) {
+			saddr = (struct sockaddr_in *) addr->addr;
+			if (saddr && saddr->sin_family == AF_INET) {
+				os_strlcpy(buf, inet_ntoa(saddr->sin_addr),
+					   len);
+				found = 1;
+				break;
+			}
+			addr = addr->next;
+		}
+	}
+
+	pcap_freealldevs(devs);
+
+	return found ? 0 : -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+#ifdef CONFIG_WINPCAP
+	/*
+	 * Use shorter poll interval for 3 seconds to reduce latency during key
+	 * handshake.
+	 */
+	l2->num_fast_poll = 3 * 50;
+	eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap);
+	eloop_register_timeout(0, 10000, l2_packet_receive_timeout,
+			       l2, l2->pcap);
+#endif /* CONFIG_WINPCAP */
+}
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+				enum l2_packet_filter_type type)
+{
+	return -1;
+}
diff --git a/hostap/src/l2_packet/l2_packet_privsep.c b/hostap/src/l2_packet/l2_packet_privsep.c
new file mode 100644
index 0000000..e26ca20
--- /dev/null
+++ b/hostap/src/l2_packet/l2_packet_privsep.c
@@ -0,0 +1,283 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with privilege separation
+ * Copyright (c) 2007, 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 <sys/un.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+#include "common/privsep_commands.h"
+
+
+struct l2_packet_data {
+	int fd; /* UNIX domain socket for privsep access */
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len);
+	void *rx_callback_ctx;
+	u8 own_addr[ETH_ALEN];
+	char *own_socket_path;
+	struct sockaddr_un priv_addr;
+};
+
+
+static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd,
+			const void *data, size_t data_len)
+{
+	struct msghdr msg;
+	struct iovec io[2];
+
+	io[0].iov_base = &cmd;
+	io[0].iov_len = sizeof(cmd);
+	io[1].iov_base = (u8 *) data;
+	io[1].iov_len = data_len;
+
+	os_memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = io;
+	msg.msg_iovlen = data ? 2 : 1;
+	msg.msg_name = &l2->priv_addr;
+	msg.msg_namelen = sizeof(l2->priv_addr);
+
+	if (sendmsg(l2->fd, &msg, 0) < 0) {
+		wpa_printf(MSG_ERROR, "L2: sendmsg(cmd): %s", strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+			     
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+	os_memcpy(addr, l2->own_addr, ETH_ALEN);
+	return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+		   const u8 *buf, size_t len)
+{
+	struct msghdr msg;
+	struct iovec io[4];
+	int cmd = PRIVSEP_CMD_L2_SEND;
+
+	io[0].iov_base = &cmd;
+	io[0].iov_len = sizeof(cmd);
+	io[1].iov_base = &dst_addr;
+	io[1].iov_len = ETH_ALEN;
+	io[2].iov_base = &proto;
+	io[2].iov_len = 2;
+	io[3].iov_base = (u8 *) buf;
+	io[3].iov_len = len;
+
+	os_memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 4;
+	msg.msg_name = &l2->priv_addr;
+	msg.msg_namelen = sizeof(l2->priv_addr);
+
+	if (sendmsg(l2->fd, &msg, 0) < 0) {
+		wpa_printf(MSG_ERROR, "L2: sendmsg(packet_send): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct l2_packet_data *l2 = eloop_ctx;
+	u8 buf[2300];
+	int res;
+	struct sockaddr_un from;
+	socklen_t fromlen = sizeof(from);
+
+	os_memset(&from, 0, sizeof(from));
+	res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
+		       &fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "l2_packet_receive - recvfrom: %s",
+			   strerror(errno));
+		return;
+	}
+	if (res < ETH_ALEN) {
+		wpa_printf(MSG_DEBUG, "L2: Too show packet received");
+		return;
+	}
+
+	if (from.sun_family != AF_UNIX ||
+	    os_strncmp(from.sun_path, l2->priv_addr.sun_path,
+		       sizeof(from.sun_path)) != 0) {
+		wpa_printf(MSG_DEBUG, "L2: Received message from unexpected "
+			   "source");
+		return;
+	}
+
+	l2->rx_callback(l2->rx_callback_ctx, buf, buf + ETH_ALEN,
+			res - ETH_ALEN);
+}
+
+
+struct l2_packet_data * l2_packet_init(
+	const char *ifname, const u8 *own_addr, unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr)
+{
+	struct l2_packet_data *l2;
+	char *own_dir = "/tmp";
+	char *priv_dir = "/var/run/wpa_priv";
+	size_t len;
+	static unsigned int counter = 0;
+	struct sockaddr_un addr;
+	fd_set rfds;
+	struct timeval tv;
+	int res;
+	u8 reply[ETH_ALEN + 1];
+	int reg_cmd[2];
+
+	l2 = os_zalloc(sizeof(struct l2_packet_data));
+	if (l2 == NULL)
+		return NULL;
+	l2->rx_callback = rx_callback;
+	l2->rx_callback_ctx = rx_callback_ctx;
+
+	len = os_strlen(own_dir) + 50;
+	l2->own_socket_path = os_malloc(len);
+	if (l2->own_socket_path == NULL) {
+		os_free(l2);
+		return NULL;
+	}
+	os_snprintf(l2->own_socket_path, len, "%s/wpa_privsep-l2-%d-%d",
+		    own_dir, getpid(), counter++);
+
+	l2->priv_addr.sun_family = AF_UNIX;
+	os_snprintf(l2->priv_addr.sun_path, sizeof(l2->priv_addr.sun_path),
+		    "%s/%s", priv_dir, ifname);
+
+	l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (l2->fd < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
+		os_free(l2->own_socket_path);
+		l2->own_socket_path = NULL;
+		os_free(l2);
+		return NULL;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path));
+	if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_ERROR, "l2-pkt-privsep: bind(PF_UNIX): %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	reg_cmd[0] = protocol;
+	reg_cmd[1] = l2_hdr;
+	if (wpa_priv_cmd(l2, PRIVSEP_CMD_L2_REGISTER, reg_cmd, sizeof(reg_cmd))
+	    < 0) {
+		wpa_printf(MSG_ERROR, "L2: Failed to register with wpa_priv");
+		goto fail;
+	}
+
+	FD_ZERO(&rfds);
+	FD_SET(l2->fd, &rfds);
+	tv.tv_sec = 5;
+	tv.tv_usec = 0;
+	res = select(l2->fd + 1, &rfds, NULL, NULL, &tv);
+	if (res < 0 && errno != EINTR) {
+		wpa_printf(MSG_ERROR, "select: %s", strerror(errno));
+		goto fail;
+	}
+
+	if (FD_ISSET(l2->fd, &rfds)) {
+		res = recv(l2->fd, reply, sizeof(reply), 0);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+			goto fail;
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "L2: Timeout while waiting for "
+			   "registration reply");
+		goto fail;
+	}
+
+	if (res != ETH_ALEN) {
+		wpa_printf(MSG_DEBUG, "L2: Unexpected registration reply "
+			   "(len=%d)", res);
+	}
+	os_memcpy(l2->own_addr, reply, ETH_ALEN);
+
+	eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+
+	return l2;
+
+fail:
+	close(l2->fd);
+	l2->fd = -1;
+	unlink(l2->own_socket_path);
+	os_free(l2->own_socket_path);
+	l2->own_socket_path = NULL;
+	os_free(l2);
+	return NULL;
+}
+
+
+struct l2_packet_data * l2_packet_init_bridge(
+	const char *br_ifname, const char *ifname, const u8 *own_addr,
+	unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr)
+{
+	return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+			      rx_callback_ctx, l2_hdr);
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+	if (l2 == NULL)
+		return;
+
+	if (l2->fd >= 0) {
+		wpa_priv_cmd(l2, PRIVSEP_CMD_L2_UNREGISTER, NULL, 0);
+		eloop_unregister_read_sock(l2->fd);
+		close(l2->fd);
+	}
+
+	if (l2->own_socket_path) {
+		unlink(l2->own_socket_path);
+		os_free(l2->own_socket_path);
+	}
+		
+	os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+	/* TODO */
+	return -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+	wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0);
+}
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+				enum l2_packet_filter_type type)
+{
+	return -1;
+}
diff --git a/hostap/src/l2_packet/l2_packet_winpcap.c b/hostap/src/l2_packet/l2_packet_winpcap.c
new file mode 100644
index 0000000..74085a3
--- /dev/null
+++ b/hostap/src/l2_packet/l2_packet_winpcap.c
@@ -0,0 +1,347 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with WinPcap RX thread
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This l2_packet implementation is explicitly for WinPcap and Windows events.
+ * l2_packet_pcap.c has support for WinPcap, but it requires polling to receive
+ * frames which means relatively long latency for EAPOL RX processing. The
+ * implementation here uses a separate thread to allow WinPcap to be receiving
+ * all the time to reduce latency for EAPOL receiving from about 100 ms to 3 ms
+ * when comparing l2_packet_pcap.c to l2_packet_winpcap.c. Extra sleep of 50 ms
+ * is added in to receive thread whenever no EAPOL frames has been received for
+ * a while. Whenever an EAPOL handshake is expected, this sleep is removed.
+ *
+ * The RX thread receives a frame and signals main thread through Windows event
+ * about the availability of a new frame. Processing the received frame is
+ * synchronized with pair of Windows events so that no extra buffer or queuing
+ * mechanism is needed. This implementation requires Windows specific event
+ * loop implementation, i.e., eloop_win.c.
+ *
+ * WinPcap has pcap_getevent() that could, in theory at least, be used to
+ * implement this kind of waiting with a simpler single-thread design. However,
+ * that event handle is not really signaled immediately when receiving each
+ * frame, so it does not really work for this kind of use.
+ */
+
+#include "includes.h"
+#include <pcap.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+/*
+ * Number of pcap_dispatch() iterations to do without extra wait after each
+ * received EAPOL packet or authentication notification. This is used to reduce
+ * latency for EAPOL receive.
+ */
+static const size_t no_wait_count = 750;
+
+struct l2_packet_data {
+	pcap_t *pcap;
+	unsigned int num_fast_poll;
+	char ifname[100];
+	u8 own_addr[ETH_ALEN];
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len);
+	void *rx_callback_ctx;
+	int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to
+		     * rx_callback and l2_packet_send() */
+	int running;
+	HANDLE rx_avail, rx_done, rx_thread, rx_thread_done, rx_notify;
+	u8 *rx_buf, *rx_src;
+	size_t rx_len;
+	size_t rx_no_wait;
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+	os_memcpy(addr, l2->own_addr, ETH_ALEN);
+	return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+		   const u8 *buf, size_t len)
+{
+	int ret;
+	struct l2_ethhdr *eth;
+
+	if (l2 == NULL)
+		return -1;
+
+	if (l2->l2_hdr) {
+		ret = pcap_sendpacket(l2->pcap, buf, len);
+	} else {
+		size_t mlen = sizeof(*eth) + len;
+		eth = os_malloc(mlen);
+		if (eth == NULL)
+			return -1;
+
+		os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+		os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+		eth->h_proto = htons(proto);
+		os_memcpy(eth + 1, buf, len);
+		ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen);
+		os_free(eth);
+	}
+
+	return ret;
+}
+
+
+/* pcap_dispatch() callback for the RX thread */
+static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr,
+				 const u_char *pkt_data)
+{
+	struct l2_packet_data *l2 = (struct l2_packet_data *) user;
+	struct l2_ethhdr *ethhdr;
+
+	if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr))
+		return;
+
+	ethhdr = (struct l2_ethhdr *) pkt_data;
+	if (l2->l2_hdr) {
+		l2->rx_buf = (u8 *) ethhdr;
+		l2->rx_len = hdr->caplen;
+	} else {
+		l2->rx_buf = (u8 *) (ethhdr + 1);
+		l2->rx_len = hdr->caplen - sizeof(*ethhdr);
+	}
+	l2->rx_src = ethhdr->h_source;
+	SetEvent(l2->rx_avail);
+	WaitForSingleObject(l2->rx_done, INFINITE);
+	ResetEvent(l2->rx_done);
+	l2->rx_no_wait = no_wait_count;
+}
+
+
+/* main RX loop that is running in a separate thread */
+static DWORD WINAPI l2_packet_receive_thread(LPVOID arg)
+{
+	struct l2_packet_data *l2 = arg;
+
+	while (l2->running) {
+		pcap_dispatch(l2->pcap, 1, l2_packet_receive_cb,
+			      (u_char *) l2);
+		if (l2->rx_no_wait > 0)
+			l2->rx_no_wait--;
+		if (WaitForSingleObject(l2->rx_notify,
+					l2->rx_no_wait ? 0 : 50) ==
+		    WAIT_OBJECT_0) {
+			l2->rx_no_wait = no_wait_count;
+			ResetEvent(l2->rx_notify);
+		}
+	}
+	SetEvent(l2->rx_thread_done);
+	ExitThread(0);
+	return 0;
+}
+
+
+/* main thread RX event handler */
+static void l2_packet_rx_event(void *eloop_data, void *user_data)
+{
+	struct l2_packet_data *l2 = eloop_data;
+	l2->rx_callback(l2->rx_callback_ctx, l2->rx_src, l2->rx_buf,
+			l2->rx_len);
+	ResetEvent(l2->rx_avail);
+	SetEvent(l2->rx_done);
+}
+
+
+static int l2_packet_init_libpcap(struct l2_packet_data *l2,
+				  unsigned short protocol)
+{
+	bpf_u_int32 pcap_maskp, pcap_netp;
+	char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
+	struct bpf_program pcap_fp;
+
+	pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
+	l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 1, pcap_err);
+	if (l2->pcap == NULL) {
+		fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+		fprintf(stderr, "ifname='%s'\n", l2->ifname);
+		return -1;
+	}
+	os_snprintf(pcap_filter, sizeof(pcap_filter),
+		    "not ether src " MACSTR " and "
+		    "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
+		    "ether proto 0x%x",
+		    MAC2STR(l2->own_addr), /* do not receive own packets */
+		    MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
+		    protocol);
+	if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
+		fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
+		return -1;
+	}
+
+	if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) {
+		fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap));
+		return -1;
+	}
+
+	pcap_freecode(&pcap_fp);
+
+	return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+	const char *ifname, const u8 *own_addr, unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr)
+{
+	struct l2_packet_data *l2;
+	DWORD thread_id;
+
+	l2 = os_zalloc(sizeof(struct l2_packet_data));
+	if (l2 == NULL)
+		return NULL;
+	if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0)
+		os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+	else
+		os_snprintf(l2->ifname, sizeof(l2->ifname), "\\Device\\NPF_%s",
+			    ifname);
+	l2->rx_callback = rx_callback;
+	l2->rx_callback_ctx = rx_callback_ctx;
+	l2->l2_hdr = l2_hdr;
+
+	if (own_addr)
+		os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
+
+	if (l2_packet_init_libpcap(l2, protocol)) {
+		os_free(l2);
+		return NULL;
+	}
+
+	l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
+	l2->rx_done = CreateEvent(NULL, TRUE, FALSE, NULL);
+	l2->rx_notify = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if (l2->rx_avail == NULL || l2->rx_done == NULL ||
+	    l2->rx_notify == NULL) {
+		CloseHandle(l2->rx_avail);
+		CloseHandle(l2->rx_done);
+		CloseHandle(l2->rx_notify);
+		pcap_close(l2->pcap);
+		os_free(l2);
+		return NULL;
+	}
+
+	eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
+			     l2_packet_rx_event, l2, NULL);
+
+	l2->running = 1;
+	l2->rx_thread = CreateThread(NULL, 0, l2_packet_receive_thread, l2, 0,
+				     &thread_id);
+
+	return l2;
+}
+
+
+struct l2_packet_data * l2_packet_init_bridge(
+	const char *br_ifname, const char *ifname, const u8 *own_addr,
+	unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr)
+{
+	return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+			      rx_callback_ctx, l2_hdr);
+}
+
+
+static void l2_packet_deinit_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct l2_packet_data *l2 = eloop_ctx;
+
+	if (l2->rx_thread_done &&
+	    WaitForSingleObject(l2->rx_thread_done, 2000) != WAIT_OBJECT_0) {
+		wpa_printf(MSG_DEBUG, "l2_packet_winpcap: RX thread did not "
+			   "exit - kill it\n");
+		TerminateThread(l2->rx_thread, 0);
+	}
+	CloseHandle(l2->rx_thread_done);
+	CloseHandle(l2->rx_thread);
+	if (l2->pcap)
+		pcap_close(l2->pcap);
+	eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+	CloseHandle(l2->rx_avail);
+	CloseHandle(l2->rx_done);
+	CloseHandle(l2->rx_notify);
+	os_free(l2);
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+	if (l2 == NULL)
+		return;
+
+	l2->rx_thread_done = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+	l2->running = 0;
+	pcap_breakloop(l2->pcap);
+
+	/*
+	 * RX thread may be waiting in l2_packet_receive_cb() for l2->rx_done
+	 * event and this event is set in l2_packet_rx_event(). However,
+	 * l2_packet_deinit() may end up being called from l2->rx_callback(),
+	 * so we need to return from here and complete deinitialization in
+	 * a registered timeout to avoid having to forcefully kill the RX
+	 * thread.
+	 */
+	eloop_register_timeout(0, 0, l2_packet_deinit_timeout, l2, NULL);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+	pcap_if_t *devs, *dev;
+	struct pcap_addr *addr;
+	struct sockaddr_in *saddr;
+	int found = 0;
+	char err[PCAP_ERRBUF_SIZE + 1];
+
+	if (pcap_findalldevs(&devs, err) < 0) {
+		wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
+		return -1;
+	}
+
+	for (dev = devs; dev && !found; dev = dev->next) {
+		if (os_strcmp(dev->name, l2->ifname) != 0)
+			continue;
+
+		addr = dev->addresses;
+		while (addr) {
+			saddr = (struct sockaddr_in *) addr->addr;
+			if (saddr && saddr->sin_family == AF_INET) {
+				os_strlcpy(buf, inet_ntoa(saddr->sin_addr),
+					   len);
+				found = 1;
+				break;
+			}
+			addr = addr->next;
+		}
+	}
+
+	pcap_freealldevs(devs);
+
+	return found ? 0 : -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+	if (l2)
+		SetEvent(l2->rx_notify);
+}
diff --git a/hostap/src/lib.rules b/hostap/src/lib.rules
new file mode 100644
index 0000000..0c79d99
--- /dev/null
+++ b/hostap/src/lib.rules
@@ -0,0 +1,25 @@
+ifndef CC
+CC=gcc
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+CFLAGS += -I.. -I../utils
+
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
+
+%.o: %.c
+	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
+	@$(E) "  CC " $<
diff --git a/hostap/src/p2p/Makefile b/hostap/src/p2p/Makefile
new file mode 100644
index 0000000..5587fcf
--- /dev/null
+++ b/hostap/src/p2p/Makefile
@@ -0,0 +1,29 @@
+all: libp2p.a
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libp2p.a
+
+install:
+	@echo Nothing to be made.
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_WIFI_DISPLAY
+CFLAGS += -DCONFIG_WPS_NFC
+
+LIB_OBJS= \
+	p2p_build.o \
+	p2p.o \
+	p2p_dev_disc.o \
+	p2p_go_neg.o \
+	p2p_group.o \
+	p2p_invitation.o \
+	p2p_parse.o \
+	p2p_pd.o \
+	p2p_sd.o \
+	p2p_utils.o
+
+libp2p.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/p2p/p2p.c b/hostap/src/p2p/p2p.c
new file mode 100644
index 0000000..767706c
--- /dev/null
+++ b/hostap/src/p2p/p2p.c
@@ -0,0 +1,5402 @@
+/*
+ * Wi-Fi Direct - P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/sha256.h"
+#include "crypto/crypto.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx);
+static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev);
+static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da,
+				     const u8 *sa, const u8 *data, size_t len,
+				     int rx_freq);
+static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
+				      const u8 *sa, const u8 *data,
+				      size_t len);
+static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx);
+static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+
+
+/*
+ * p2p_scan recovery timeout
+ *
+ * Many drivers are using 30 second timeout on scan results. Allow a bit larger
+ * timeout for this to avoid hitting P2P timeout unnecessarily.
+ */
+#define P2P_SCAN_TIMEOUT 35
+
+/**
+ * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer
+ * entries will be removed
+ */
+#ifndef P2P_PEER_EXPIRATION_AGE
+#define P2P_PEER_EXPIRATION_AGE 60
+#endif /* P2P_PEER_EXPIRATION_AGE */
+
+
+void p2p_expire_peers(struct p2p_data *p2p)
+{
+	struct p2p_device *dev, *n;
+	struct os_reltime now;
+	size_t i;
+
+	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)) {
+			/*
+			 * 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_reltime(&dev->last_seen);
+			continue;
+		}
+
+		for (i = 0; i < p2p->num_groups; i++) {
+			if (p2p_group_is_client_connected(
+				    p2p->groups[i], dev->info.p2p_device_addr))
+				break;
+		}
+		if (i < p2p->num_groups) {
+			/*
+			 * The peer is connected as a client in a group where
+			 * we are the GO, so do not expire the peer entry.
+			 */
+			os_get_reltime(&dev->last_seen);
+			continue;
+		}
+
+		p2p_dbg(p2p, "Expiring old peer entry " MACSTR,
+			MAC2STR(dev->info.p2p_device_addr));
+		dl_list_del(&dev->list);
+		p2p_device_free(p2p, dev);
+	}
+}
+
+
+static const char * p2p_state_txt(int state)
+{
+	switch (state) {
+	case P2P_IDLE:
+		return "IDLE";
+	case P2P_SEARCH:
+		return "SEARCH";
+	case P2P_CONNECT:
+		return "CONNECT";
+	case P2P_CONNECT_LISTEN:
+		return "CONNECT_LISTEN";
+	case P2P_GO_NEG:
+		return "GO_NEG";
+	case P2P_LISTEN_ONLY:
+		return "LISTEN_ONLY";
+	case P2P_WAIT_PEER_CONNECT:
+		return "WAIT_PEER_CONNECT";
+	case P2P_WAIT_PEER_IDLE:
+		return "WAIT_PEER_IDLE";
+	case P2P_SD_DURING_FIND:
+		return "SD_DURING_FIND";
+	case P2P_PROVISIONING:
+		return "PROVISIONING";
+	case P2P_PD_DURING_FIND:
+		return "PD_DURING_FIND";
+	case P2P_INVITE:
+		return "INVITE";
+	case P2P_INVITE_LISTEN:
+		return "INVITE_LISTEN";
+	default:
+		return "?";
+	}
+}
+
+
+const char * p2p_get_state_txt(struct p2p_data *p2p)
+{
+	return p2p_state_txt(p2p->state);
+}
+
+
+struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p)
+{
+	return p2p ? p2p->p2ps_adv_list : NULL;
+}
+
+
+void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr)
+{
+	if (p2p && intended_addr)
+		os_memcpy(p2p->intended_addr, intended_addr, ETH_ALEN);
+}
+
+
+u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr)
+{
+	struct p2p_device *dev = NULL;
+
+	if (!addr || !p2p)
+		return 0;
+
+	dev = p2p_get_device(p2p, addr);
+	if (dev)
+		return dev->wps_prov_info;
+	else
+		return 0;
+}
+
+
+void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr)
+{
+	struct p2p_device *dev = NULL;
+
+	if (!addr || !p2p)
+		return;
+
+	dev = p2p_get_device(p2p, addr);
+	if (dev)
+		dev->wps_prov_info = 0;
+}
+
+
+void p2p_set_state(struct p2p_data *p2p, int new_state)
+{
+	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;
+	}
+}
+
+
+void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec)
+{
+	p2p_dbg(p2p, "Set timeout (state=%s): %u.%06u sec",
+		p2p_state_txt(p2p->state), sec, usec);
+	eloop_cancel_timeout(p2p_state_timeout, p2p, NULL);
+	eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL);
+}
+
+
+void p2p_clear_timeout(struct p2p_data *p2p)
+{
+	p2p_dbg(p2p, "Clear timeout (state=%s)", p2p_state_txt(p2p->state));
+	eloop_cancel_timeout(p2p_state_timeout, p2p, NULL);
+}
+
+
+void p2p_go_neg_failed(struct p2p_data *p2p, int status)
+{
+	struct p2p_go_neg_results res;
+	struct p2p_device *peer = p2p->go_neg_peer;
+
+	if (!peer)
+		return;
+
+	eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
+	if (p2p->state != P2P_SEARCH) {
+		/*
+		 * Clear timeouts related to GO Negotiation if no new p2p_find
+		 * has been started.
+		 */
+		p2p_clear_timeout(p2p);
+		p2p_set_state(p2p, P2P_IDLE);
+	}
+
+	peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
+	peer->wps_method = WPS_NOT_READY;
+	peer->oob_pw_id = 0;
+	wpabuf_free(peer->go_neg_conf);
+	peer->go_neg_conf = NULL;
+	p2p->go_neg_peer = NULL;
+
+	os_memset(&res, 0, sizeof(res));
+	res.status = status;
+	os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
+	os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
+	p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
+}
+
+
+static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc)
+{
+	unsigned int r, tu;
+	int freq;
+	struct wpabuf *ies;
+
+	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");
+		return;
+	}
+
+	if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+		r = 0;
+	tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) +
+	      p2p->min_disc_int) * 100;
+	if (p2p->max_disc_tu >= 0 && tu > (unsigned int) p2p->max_disc_tu)
+		tu = p2p->max_disc_tu;
+	if (!dev_disc && tu < 100)
+		tu = 100; /* Need to wait in non-device discovery use cases */
+	if (p2p->cfg->max_listen && 1024 * tu / 1000 > p2p->cfg->max_listen)
+		tu = p2p->cfg->max_listen * 1000 / 1024;
+
+	if (tu == 0) {
+		p2p_dbg(p2p, "Skip listen state since duration was 0 TU");
+		p2p_set_timeout(p2p, 0, 0);
+		return;
+	}
+
+	ies = p2p_build_probe_resp_ies(p2p, NULL, 0);
+	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");
+		p2p->pending_listen_freq = 0;
+	}
+	wpabuf_free(ies);
+}
+
+
+int p2p_listen(struct p2p_data *p2p, unsigned int timeout)
+{
+	int freq;
+	struct wpabuf *ies;
+
+	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_sec = timeout / 1000;
+	p2p->pending_listen_usec = (timeout % 1000) * 1000;
+
+	if (p2p->p2p_scan_running) {
+		if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) {
+			p2p_dbg(p2p, "p2p_scan running - connect is already pending - skip listen");
+			return 0;
+		}
+		p2p_dbg(p2p, "p2p_scan running - delay start of listen state");
+		p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN;
+		return 0;
+	}
+
+	ies = p2p_build_probe_resp_ies(p2p, NULL, 0);
+	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;
+		wpabuf_free(ies);
+		return -1;
+	}
+	wpabuf_free(ies);
+
+	p2p_set_state(p2p, P2P_LISTEN_ONLY);
+
+	return 0;
+}
+
+
+static void p2p_device_clear_reported(struct p2p_data *p2p)
+{
+	struct p2p_device *dev;
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		dev->flags &= ~P2P_DEV_REPORTED;
+		dev->sd_reqs = 0;
+	}
+}
+
+
+/**
+ * p2p_get_device - Fetch a peer entry
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer
+ * Returns: Pointer to the device entry or %NULL if not found
+ */
+struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr)
+{
+	struct p2p_device *dev;
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		if (os_memcmp(dev->info.p2p_device_addr, addr, ETH_ALEN) == 0)
+			return dev;
+	}
+	return NULL;
+}
+
+
+/**
+ * p2p_get_device_interface - Fetch a peer entry based on P2P Interface Address
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Interface Address of the peer
+ * Returns: Pointer to the device entry or %NULL if not found
+ */
+struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
+					     const u8 *addr)
+{
+	struct p2p_device *dev;
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		if (os_memcmp(dev->interface_addr, addr, ETH_ALEN) == 0)
+			return dev;
+	}
+	return NULL;
+}
+
+
+/**
+ * p2p_create_device - Create a peer entry
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer
+ * Returns: Pointer to the device entry or %NULL on failure
+ *
+ * If there is already an entry for the peer, it will be returned instead of
+ * creating a new one.
+ */
+static struct p2p_device * p2p_create_device(struct p2p_data *p2p,
+					     const u8 *addr)
+{
+	struct p2p_device *dev, *oldest = NULL;
+	size_t count = 0;
+
+	dev = p2p_get_device(p2p, addr);
+	if (dev)
+		return dev;
+
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		count++;
+		if (oldest == NULL ||
+		    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");
+		dl_list_del(&oldest->list);
+		p2p_device_free(p2p, oldest);
+	}
+
+	dev = os_zalloc(sizeof(*dev));
+	if (dev == NULL)
+		return NULL;
+	dl_list_add(&p2p->devices, &dev->list);
+	os_memcpy(dev->info.p2p_device_addr, addr, ETH_ALEN);
+
+	return dev;
+}
+
+
+static void p2p_copy_client_info(struct p2p_device *dev,
+				 struct p2p_client_info *cli)
+{
+	os_memcpy(dev->info.device_name, cli->dev_name, cli->dev_name_len);
+	dev->info.device_name[cli->dev_name_len] = '\0';
+	dev->info.dev_capab = cli->dev_capab;
+	dev->info.config_methods = cli->config_methods;
+	os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8);
+	dev->info.wps_sec_dev_type_list_len = 8 * cli->num_sec_dev_types;
+	os_memcpy(dev->info.wps_sec_dev_type_list, cli->sec_dev_types,
+		  dev->info.wps_sec_dev_type_list_len);
+}
+
+
+static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr,
+				 const u8 *go_interface_addr, int freq,
+				 const u8 *gi, size_t gi_len,
+				 struct os_reltime *rx_time)
+{
+	struct p2p_group_info info;
+	size_t c;
+	struct p2p_device *dev;
+
+	if (gi == NULL)
+		return 0;
+
+	if (p2p_group_info_parse(gi, gi_len, &info) < 0)
+		return -1;
+
+	/*
+	 * Clear old data for this group; if the devices are still in the
+	 * group, the information will be restored in the loop following this.
+	 */
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		if (os_memcmp(dev->member_in_go_iface, go_interface_addr,
+			      ETH_ALEN) == 0) {
+			os_memset(dev->member_in_go_iface, 0, ETH_ALEN);
+			os_memset(dev->member_in_go_dev, 0, ETH_ALEN);
+		}
+	}
+
+	for (c = 0; c < info.num_clients; c++) {
+		struct p2p_client_info *cli = &info.client[c];
+		if (os_memcmp(cli->p2p_device_addr, p2p->cfg->dev_addr,
+			      ETH_ALEN) == 0)
+			continue; /* ignore our own entry */
+		dev = p2p_get_device(p2p, cli->p2p_device_addr);
+		if (dev) {
+			if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY |
+					  P2P_DEV_PROBE_REQ_ONLY)) {
+				/*
+				 * Update information since we have not
+				 * received this directly from the client.
+				 */
+				p2p_copy_client_info(dev, cli);
+			} else {
+				/*
+				 * Need to update P2P Client Discoverability
+				 * flag since it is valid only in P2P Group
+				 * Info attribute.
+				 */
+				dev->info.dev_capab &=
+					~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+				dev->info.dev_capab |=
+					cli->dev_capab &
+					P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+			}
+			if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+				dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
+			}
+		} else {
+			dev = p2p_create_device(p2p, cli->p2p_device_addr);
+			if (dev == NULL)
+				continue;
+			dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
+			p2p_copy_client_info(dev, cli);
+			dev->oper_freq = freq;
+			p2p->cfg->dev_found(p2p->cfg->cb_ctx,
+					    dev->info.p2p_device_addr,
+					    &dev->info, 1);
+			dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+		}
+
+		os_memcpy(dev->interface_addr, cli->p2p_interface_addr,
+			  ETH_ALEN);
+		os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime));
+		os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN);
+		os_memcpy(dev->member_in_go_iface, go_interface_addr,
+			  ETH_ALEN);
+		dev->flags |= P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT;
+	}
+
+	return 0;
+}
+
+
+static void p2p_copy_wps_info(struct p2p_data *p2p, struct p2p_device *dev,
+			      int probe_req, const struct p2p_message *msg)
+{
+	os_memcpy(dev->info.device_name, msg->device_name,
+		  sizeof(dev->info.device_name));
+
+	if (msg->manufacturer &&
+	    msg->manufacturer_len < sizeof(dev->info.manufacturer)) {
+		os_memset(dev->info.manufacturer, 0,
+			  sizeof(dev->info.manufacturer));
+		os_memcpy(dev->info.manufacturer, msg->manufacturer,
+			  msg->manufacturer_len);
+	}
+
+	if (msg->model_name &&
+	    msg->model_name_len < sizeof(dev->info.model_name)) {
+		os_memset(dev->info.model_name, 0,
+			  sizeof(dev->info.model_name));
+		os_memcpy(dev->info.model_name, msg->model_name,
+			  msg->model_name_len);
+	}
+
+	if (msg->model_number &&
+	    msg->model_number_len < sizeof(dev->info.model_number)) {
+		os_memset(dev->info.model_number, 0,
+			  sizeof(dev->info.model_number));
+		os_memcpy(dev->info.model_number, msg->model_number,
+			  msg->model_number_len);
+	}
+
+	if (msg->serial_number &&
+	    msg->serial_number_len < sizeof(dev->info.serial_number)) {
+		os_memset(dev->info.serial_number, 0,
+			  sizeof(dev->info.serial_number));
+		os_memcpy(dev->info.serial_number, msg->serial_number,
+			  msg->serial_number_len);
+	}
+
+	if (msg->pri_dev_type)
+		os_memcpy(dev->info.pri_dev_type, msg->pri_dev_type,
+			  sizeof(dev->info.pri_dev_type));
+	else if (msg->wps_pri_dev_type)
+		os_memcpy(dev->info.pri_dev_type, msg->wps_pri_dev_type,
+			  sizeof(dev->info.pri_dev_type));
+
+	if (msg->wps_sec_dev_type_list) {
+		os_memcpy(dev->info.wps_sec_dev_type_list,
+			  msg->wps_sec_dev_type_list,
+			  msg->wps_sec_dev_type_list_len);
+		dev->info.wps_sec_dev_type_list_len =
+			msg->wps_sec_dev_type_list_len;
+	}
+
+	if (msg->capability) {
+		/*
+		 * P2P Client Discoverability bit is reserved in all frames
+		 * that use this function, so do not change its value here.
+		 */
+		dev->info.dev_capab &= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+		dev->info.dev_capab |= msg->capability[0] &
+			~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+		dev->info.group_capab = msg->capability[1];
+	}
+
+	if (msg->ext_listen_timing) {
+		dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing);
+		dev->ext_listen_interval =
+			WPA_GET_LE16(msg->ext_listen_timing + 2);
+	}
+
+	if (!probe_req) {
+		u16 new_config_methods;
+		new_config_methods = msg->config_methods ?
+			msg->config_methods : msg->wps_config_methods;
+		if (new_config_methods &&
+		    dev->info.config_methods != new_config_methods) {
+			p2p_dbg(p2p, "Update peer " MACSTR
+				" config_methods 0x%x -> 0x%x",
+				MAC2STR(dev->info.p2p_device_addr),
+				dev->info.config_methods,
+				new_config_methods);
+			dev->info.config_methods = new_config_methods;
+		}
+	}
+}
+
+
+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);
+	}
+}
+
+
+static int p2p_compare_wfd_info(struct p2p_device *dev,
+			      const struct p2p_message *msg)
+{
+	if (dev->info.wfd_subelems && msg->wfd_subelems) {
+		if (dev->info.wfd_subelems->used != msg->wfd_subelems->used)
+			return 1;
+
+		return os_memcmp(dev->info.wfd_subelems->buf,
+				 msg->wfd_subelems->buf,
+				 dev->info.wfd_subelems->used);
+	}
+	if (dev->info.wfd_subelems || msg->wfd_subelems)
+		return 1;
+
+	return 0;
+}
+
+
+/**
+ * p2p_add_device - Add peer entries based on scan results or P2P frames
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Source address of Beacon or Probe Response frame (may be either
+ *	P2P Device Address or P2P Interface Address)
+ * @level: Signal level (signal strength of the received frame from the peer)
+ * @freq: Frequency on which the Beacon or Probe Response frame was received
+ * @rx_time: Time when the result was received
+ * @ies: IEs from the Beacon or Probe Response frame
+ * @ies_len: Length of ies buffer in octets
+ * @scan_res: Whether this was based on scan results
+ * Returns: 0 on success, -1 on failure
+ *
+ * If the scan result is for a GO, the clients in the group will also be added
+ * to the peer table. This function can also be used with some other frames
+ * like Provision Discovery Request that contains P2P Capability and P2P Device
+ * Info attributes.
+ */
+int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
+		   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 wfd_changed;
+	int i;
+	struct os_reltime time_now;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_ies(ies, ies_len, &msg)) {
+		p2p_dbg(p2p, "Failed to parse P2P IE for a device entry");
+		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 (!is_zero_ether_addr(p2p->peer_filter) &&
+	    os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) {
+		p2p_dbg(p2p, "Do not add peer filter for " MACSTR
+			" due to peer filter", MAC2STR(p2p_dev_addr));
+		p2p_parse_free(&msg);
+		return 0;
+	}
+
+	dev = p2p_create_device(p2p, p2p_dev_addr);
+	if (dev == NULL) {
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	if (rx_time == NULL) {
+		os_get_reltime(&time_now);
+		rx_time = &time_now;
+	}
+
+	/*
+	 * Update the device entry only if the new peer
+	 * entry is newer than the one previously stored, or if
+	 * the device was previously seen as a P2P Client in a group
+	 * and the new entry isn't older than a threshold.
+	 */
+	if (dev->last_seen.sec > 0 &&
+	    os_reltime_before(rx_time, &dev->last_seen) &&
+	    (!(dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT) ||
+	     os_reltime_expired(&dev->last_seen, rx_time,
+				P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD))) {
+		p2p_dbg(p2p,
+			"Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u flags=0x%x)",
+			(unsigned int) rx_time->sec,
+			(unsigned int) rx_time->usec,
+			(unsigned int) dev->last_seen.sec,
+			(unsigned int) dev->last_seen.usec,
+			dev->flags);
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime));
+
+	dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY |
+			P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT);
+
+	if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0)
+		os_memcpy(dev->interface_addr, addr, ETH_ALEN);
+	if (msg.ssid &&
+	    msg.ssid[1] <= sizeof(dev->oper_ssid) &&
+	    (msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
+	     os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
+	     != 0)) {
+		os_memcpy(dev->oper_ssid, msg.ssid + 2, msg.ssid[1]);
+		dev->oper_ssid_len = msg.ssid[1];
+	}
+
+	if (msg.adv_service_instance && msg.adv_service_instance_len) {
+		wpabuf_free(dev->info.p2ps_instance);
+		dev->info.p2ps_instance = wpabuf_alloc_copy(
+			msg.adv_service_instance, msg.adv_service_instance_len);
+	}
+
+	if (freq >= 2412 && freq <= 2484 && msg.ds_params &&
+	    *msg.ds_params >= 1 && *msg.ds_params <= 14) {
+		int ds_freq;
+		if (*msg.ds_params == 14)
+			ds_freq = 2484;
+		else
+			ds_freq = 2407 + *msg.ds_params * 5;
+		if (freq != ds_freq) {
+			p2p_dbg(p2p, "Update Listen frequency based on DS Parameter Set IE: %d -> %d MHz",
+				freq, ds_freq);
+			freq = ds_freq;
+		}
+	}
+
+	if (dev->listen_freq && dev->listen_freq != freq && scan_res) {
+		p2p_dbg(p2p, "Update Listen frequency based on scan results ("
+			MACSTR " %d -> %d MHz (DS param %d)",
+			MAC2STR(dev->info.p2p_device_addr), dev->listen_freq,
+			freq, msg.ds_params ? *msg.ds_params : -1);
+	}
+	if (scan_res) {
+		dev->listen_freq = freq;
+		if (msg.group_info)
+			dev->oper_freq = freq;
+	}
+	dev->info.level = level;
+
+	p2p_copy_wps_info(p2p, dev, 0, &msg);
+
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		wpabuf_free(dev->info.wps_vendor_ext[i]);
+		dev->info.wps_vendor_ext[i] = NULL;
+	}
+
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		if (msg.wps_vendor_ext[i] == NULL)
+			break;
+		dev->info.wps_vendor_ext[i] = wpabuf_alloc_copy(
+			msg.wps_vendor_ext[i], msg.wps_vendor_ext_len[i]);
+		if (dev->info.wps_vendor_ext[i] == NULL)
+			break;
+	}
+
+	wfd_changed = p2p_compare_wfd_info(dev, &msg);
+
+	if (msg.wfd_subelems) {
+		wpabuf_free(dev->info.wfd_subelems);
+		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+	}
+
+	if (scan_res) {
+		p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq,
+				      msg.group_info, msg.group_info_len,
+				      rx_time);
+	}
+
+	p2p_parse_free(&msg);
+
+	p2p_update_peer_vendor_elems(dev, ies, ies_len);
+
+	if (dev->flags & P2P_DEV_REPORTED && !wfd_changed &&
+	    (!msg.adv_service_instance ||
+	     (dev->flags & P2P_DEV_P2PS_REPORTED)))
+		return 0;
+
+	p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)",
+		freq, (unsigned int) rx_time->sec,
+		(unsigned int) rx_time->usec);
+	if (dev->flags & P2P_DEV_USER_REJECTED) {
+		p2p_dbg(p2p, "Do not report rejected device");
+		return 0;
+	}
+
+	if (dev->info.config_methods == 0 &&
+	    (freq == 2412 || freq == 2437 || freq == 2462)) {
+		/*
+		 * If we have only seen a Beacon frame from a GO, we do not yet
+		 * know what WPS config methods it supports. Since some
+		 * applications use config_methods value from P2P-DEVICE-FOUND
+		 * events, postpone reporting this peer until we've fully
+		 * discovered its capabilities.
+		 *
+		 * At least for now, do this only if the peer was detected on
+		 * one of the social channels since that peer can be easily be
+		 * found again and there are no limitations of having to use
+		 * passive scan on this channels, so this can be done through
+		 * Probe Response frame that includes the config_methods
+		 * information.
+		 */
+		p2p_dbg(p2p, "Do not report peer " MACSTR
+			" with unknown config methods", MAC2STR(addr));
+		return 0;
+	}
+
+	p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info,
+			    !(dev->flags & P2P_DEV_REPORTED_ONCE));
+	dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+
+	if (msg.adv_service_instance)
+		dev->flags |= P2P_DEV_P2PS_REPORTED;
+
+	return 0;
+}
+
+
+static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
+{
+	int i;
+
+	if (p2p->go_neg_peer == dev) {
+		/*
+		 * If GO Negotiation is in progress, report that it has failed.
+		 */
+		p2p_go_neg_failed(p2p, -1);
+	}
+	if (p2p->invite_peer == dev)
+		p2p->invite_peer = NULL;
+	if (p2p->sd_peer == dev)
+		p2p->sd_peer = NULL;
+	if (p2p->pending_client_disc_go == dev)
+		p2p->pending_client_disc_go = NULL;
+
+	/* dev_lost() device, but only if it was previously dev_found() */
+	if (dev->flags & P2P_DEV_REPORTED_ONCE)
+		p2p->cfg->dev_lost(p2p->cfg->cb_ctx,
+				   dev->info.p2p_device_addr);
+
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		wpabuf_free(dev->info.wps_vendor_ext[i]);
+		dev->info.wps_vendor_ext[i] = NULL;
+	}
+
+	wpabuf_free(dev->info.wfd_subelems);
+	wpabuf_free(dev->info.vendor_elems);
+	wpabuf_free(dev->go_neg_conf);
+	wpabuf_free(dev->info.p2ps_instance);
+
+	os_free(dev);
+}
+
+
+static int p2p_get_next_prog_freq(struct p2p_data *p2p)
+{
+	struct p2p_channels *c;
+	struct p2p_reg_class *cla;
+	size_t cl, ch;
+	int found = 0;
+	u8 reg_class;
+	u8 channel;
+	int freq;
+
+	c = &p2p->cfg->channels;
+	for (cl = 0; cl < c->reg_classes; cl++) {
+		cla = &c->reg_class[cl];
+		if (cla->reg_class != p2p->last_prog_scan_class)
+			continue;
+		for (ch = 0; ch < cla->channels; ch++) {
+			if (cla->channel[ch] == p2p->last_prog_scan_chan) {
+				found = 1;
+				break;
+			}
+		}
+		if (found)
+			break;
+	}
+
+	if (!found) {
+		/* Start from beginning */
+		reg_class = c->reg_class[0].reg_class;
+		channel = c->reg_class[0].channel[0];
+	} else {
+		/* Pick the next channel */
+		ch++;
+		if (ch == cla->channels) {
+			cl++;
+			if (cl == c->reg_classes)
+				cl = 0;
+			ch = 0;
+		}
+		reg_class = c->reg_class[cl].reg_class;
+		channel = c->reg_class[cl].channel[ch];
+	}
+
+	freq = p2p_channel_to_freq(reg_class, channel);
+	p2p_dbg(p2p, "Next progressive search channel: reg_class %u channel %u -> %d MHz",
+		reg_class, channel, freq);
+	p2p->last_prog_scan_class = reg_class;
+	p2p->last_prog_scan_chan = channel;
+
+	if (freq == 2412 || freq == 2437 || freq == 2462)
+		return 0; /* No need to add social channels */
+	return freq;
+}
+
+
+static void p2p_search(struct p2p_data *p2p)
+{
+	int freq = 0;
+	enum p2p_scan_type type;
+	u16 pw_id = DEV_PW_DEFAULT;
+	int res;
+
+	if (p2p->drv_in_listen) {
+		p2p_dbg(p2p, "Driver is still in Listen state - wait for it to end before continuing");
+		return;
+	}
+	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+
+	if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
+	    (freq = p2p_get_next_prog_freq(p2p)) > 0) {
+		type = P2P_SCAN_SOCIAL_PLUS_ONE;
+		p2p_dbg(p2p, "Starting search (+ freq %u)", freq);
+	} else {
+		type = P2P_SCAN_SOCIAL;
+		p2p_dbg(p2p, "Starting search");
+	}
+
+	res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq,
+				 p2p->num_req_dev_types, p2p->req_dev_types,
+				 p2p->find_dev_id, pw_id);
+	if (res < 0) {
+		p2p_dbg(p2p, "Scan request schedule failed");
+		p2p_continue_find(p2p);
+	}
+}
+
+
+static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+	p2p_dbg(p2p, "Find timeout -> stop");
+	p2p_stop_find(p2p);
+}
+
+
+void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status)
+{
+	if (status != 0) {
+		p2p_dbg(p2p, "Scan request failed");
+		/* Do continue find even for the first p2p_find_scan */
+		p2p_continue_find(p2p);
+	} else {
+		p2p_dbg(p2p, "Running p2p_scan");
+		p2p->p2p_scan_running = 1;
+		eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+		eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
+				       p2p, NULL);
+	}
+}
+
+
+static int p2p_run_after_scan(struct p2p_data *p2p)
+{
+	struct p2p_device *dev;
+	enum p2p_after_scan op;
+
+	if (p2p->after_scan_tx) {
+		p2p->after_scan_tx_in_progress = 1;
+		p2p_dbg(p2p, "Send pending Action frame at p2p_scan completion");
+		p2p->cfg->send_action(p2p->cfg->cb_ctx,
+				      p2p->after_scan_tx->freq,
+				      p2p->after_scan_tx->dst,
+				      p2p->after_scan_tx->src,
+				      p2p->after_scan_tx->bssid,
+				      (u8 *) (p2p->after_scan_tx + 1),
+				      p2p->after_scan_tx->len,
+				      p2p->after_scan_tx->wait_time);
+		os_free(p2p->after_scan_tx);
+		p2p->after_scan_tx = NULL;
+		return 1;
+	}
+
+	op = p2p->start_after_scan;
+	p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+	switch (op) {
+	case P2P_AFTER_SCAN_NOTHING:
+		break;
+	case P2P_AFTER_SCAN_LISTEN:
+		p2p_dbg(p2p, "Start previously requested Listen state");
+		p2p_listen(p2p, p2p->pending_listen_sec * 1000 +
+			   p2p->pending_listen_usec / 1000);
+		return 1;
+	case P2P_AFTER_SCAN_CONNECT:
+		p2p_dbg(p2p, "Start previously requested connect with " MACSTR,
+			MAC2STR(p2p->after_scan_peer));
+		dev = p2p_get_device(p2p, p2p->after_scan_peer);
+		if (dev == NULL) {
+			p2p_dbg(p2p, "Peer not known anymore");
+			break;
+		}
+		p2p_connect_send(p2p, dev);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+	int running;
+	p2p_dbg(p2p, "p2p_scan timeout (running=%d)", p2p->p2p_scan_running);
+	running = p2p->p2p_scan_running;
+	/* Make sure we recover from missed scan results callback */
+	p2p->p2p_scan_running = 0;
+
+	if (running)
+		p2p_run_after_scan(p2p);
+}
+
+
+static void p2p_free_req_dev_types(struct p2p_data *p2p)
+{
+	p2p->num_req_dev_types = 0;
+	os_free(p2p->req_dev_types);
+	p2p->req_dev_types = NULL;
+}
+
+
+static int p2ps_gen_hash(struct p2p_data *p2p, const char *str, u8 *hash)
+{
+	u8 buf[SHA256_MAC_LEN];
+	char str_buf[256];
+	const u8 *adv_array;
+	size_t i, adv_len;
+
+	if (!str || !hash)
+		return 0;
+
+	if (!str[0]) {
+		os_memcpy(hash, p2p->wild_card_hash, P2PS_HASH_LEN);
+		return 1;
+	}
+
+	adv_array = (u8 *) str_buf;
+	adv_len = os_strlen(str);
+	if (adv_len >= sizeof(str_buf))
+		return 0;
+
+	for (i = 0; i < adv_len; i++) {
+		if (str[i] >= 'A' && str[i] <= 'Z')
+			str_buf[i] = str[i] - 'A' + 'a';
+		else
+			str_buf[i] = str[i];
+	}
+
+	if (sha256_vector(1, &adv_array, &adv_len, buf))
+		return 0;
+
+	os_memcpy(hash, buf, P2PS_HASH_LEN);
+	return 1;
+}
+
+
+int p2p_find(struct p2p_data *p2p, unsigned int timeout,
+	     enum p2p_discovery_type type,
+	     unsigned int num_req_dev_types, const u8 *req_dev_types,
+	     const u8 *dev_id, unsigned int search_delay,
+	     u8 seek_count, const char **seek, int freq)
+{
+	int res;
+
+	p2p_dbg(p2p, "Starting find (type=%d)", type);
+	os_get_reltime(&p2p->find_start);
+	if (p2p->p2p_scan_running) {
+		p2p_dbg(p2p, "p2p_scan is already running");
+	}
+
+	p2p_free_req_dev_types(p2p);
+	if (req_dev_types && num_req_dev_types) {
+		p2p->req_dev_types = os_malloc(num_req_dev_types *
+					       WPS_DEV_TYPE_LEN);
+		if (p2p->req_dev_types == NULL)
+			return -1;
+		os_memcpy(p2p->req_dev_types, req_dev_types,
+			  num_req_dev_types * WPS_DEV_TYPE_LEN);
+		p2p->num_req_dev_types = num_req_dev_types;
+	}
+
+	if (dev_id) {
+		os_memcpy(p2p->find_dev_id_buf, dev_id, ETH_ALEN);
+		p2p->find_dev_id = p2p->find_dev_id_buf;
+	} else
+		p2p->find_dev_id = NULL;
+
+	if (seek_count == 0 || !seek) {
+		/* Not an ASP search */
+		p2p->p2ps_seek = 0;
+	} else if (seek_count == 1 && seek && (!seek[0] || !seek[0][0])) {
+		/*
+		 * An empty seek string means no hash values, but still an ASP
+		 * search.
+		 */
+		p2p_dbg(p2p, "ASP search");
+		p2p->p2ps_seek_count = 0;
+		p2p->p2ps_seek = 1;
+	} else if (seek && seek_count <= P2P_MAX_QUERY_HASH) {
+		u8 buf[P2PS_HASH_LEN];
+		int i, count = 0;
+
+		for (i = 0; i < seek_count; i++) {
+			if (!p2ps_gen_hash(p2p, seek[i], buf))
+				continue;
+
+			p2p_dbg(p2p, "Seek service %s hash " MACSTR,
+				seek[i], MAC2STR(buf));
+			os_memcpy(&p2p->p2ps_seek_hash[count * P2PS_HASH_LEN],
+				  buf, P2PS_HASH_LEN);
+			count++;
+		}
+
+		p2p->p2ps_seek_count = count;
+		p2p->p2ps_seek = 1;
+	} else {
+		p2p->p2ps_seek_count = 0;
+		p2p->p2ps_seek = 1;
+	}
+
+	/* Special case to perform wildcard search */
+	if (p2p->p2ps_seek_count == 0 && p2p->p2ps_seek) {
+		p2p->p2ps_seek_count = 1;
+		os_memcpy(&p2p->p2ps_seek_hash, p2p->wild_card_hash,
+			  P2PS_HASH_LEN);
+	}
+
+	p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+	p2p_clear_timeout(p2p);
+	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+	p2p->find_type = type;
+	p2p_device_clear_reported(p2p);
+	p2p_set_state(p2p, P2P_SEARCH);
+	p2p->search_delay = search_delay;
+	p2p->in_search_delay = 0;
+	eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+	p2p->last_p2p_find_timeout = timeout;
+	if (timeout)
+		eloop_register_timeout(timeout, 0, p2p_find_timeout,
+				       p2p, NULL);
+	switch (type) {
+	case P2P_FIND_START_WITH_FULL:
+		if (freq > 0) {
+			/*
+			 * Start with the specified channel and then move to
+			 * social channels only scans.
+			 */
+			res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx,
+						 P2P_SCAN_SPECIFIC, freq,
+						 p2p->num_req_dev_types,
+						 p2p->req_dev_types, dev_id,
+						 DEV_PW_DEFAULT);
+			break;
+		}
+		/* fall through */
+	case P2P_FIND_PROGRESSIVE:
+		res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0,
+					 p2p->num_req_dev_types,
+					 p2p->req_dev_types, dev_id,
+					 DEV_PW_DEFAULT);
+		break;
+	case P2P_FIND_ONLY_SOCIAL:
+		res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0,
+					 p2p->num_req_dev_types,
+					 p2p->req_dev_types, dev_id,
+					 DEV_PW_DEFAULT);
+		break;
+	default:
+		return -1;
+	}
+
+	if (res != 0 && 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 if (res != 0) {
+		p2p_dbg(p2p, "Failed to start p2p_scan");
+		p2p_set_state(p2p, P2P_IDLE);
+		eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+	}
+
+	return res;
+}
+
+
+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_SD_DURING_FIND)
+		p2p->cfg->find_stopped(p2p->cfg->cb_ctx);
+
+	p2p->p2ps_seek_count = 0;
+
+	p2p_set_state(p2p, P2P_IDLE);
+	p2p_free_req_dev_types(p2p);
+	p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+	if (p2p->go_neg_peer)
+		p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
+	p2p->go_neg_peer = NULL;
+	p2p->sd_peer = NULL;
+	p2p->invite_peer = NULL;
+	p2p_stop_listen_for_freq(p2p, freq);
+	p2p->send_action_in_progress = 0;
+}
+
+
+void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq)
+{
+	if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) {
+		p2p_dbg(p2p, "Skip stop_listen since we are on correct channel for response");
+		return;
+	}
+	if (p2p->in_listen) {
+		p2p->in_listen = 0;
+		p2p_clear_timeout(p2p);
+	}
+	if (p2p->drv_in_listen) {
+		/*
+		 * The driver may not deliver callback to p2p_listen_end()
+		 * when the operation gets canceled, so clear the internal
+		 * variable that is tracking driver state.
+		 */
+		p2p_dbg(p2p, "Clear drv_in_listen (%d)", p2p->drv_in_listen);
+		p2p->drv_in_listen = 0;
+	}
+	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+}
+
+
+void p2p_stop_listen(struct p2p_data *p2p)
+{
+	if (p2p->state != P2P_LISTEN_ONLY) {
+		p2p_dbg(p2p, "Skip stop_listen since not in listen_only state.");
+		return;
+	}
+
+	p2p_stop_listen_for_freq(p2p, 0);
+	p2p_set_state(p2p, P2P_IDLE);
+}
+
+
+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, 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 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) &&
+	    (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;
+	}
+
+	p2p->op_reg_class = op_class;
+	p2p->op_channel = op_channel;
+
+	if (force_freq) {
+		p2p->channels.reg_classes = 1;
+		p2p->channels.reg_class[0].channels = 1;
+		p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
+		p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
+	} else {
+		os_memcpy(&p2p->channels, &p2p->cfg->channels,
+			  sizeof(struct p2p_channels));
+	}
+
+	return 0;
+}
+
+
+static void p2p_prepare_channel_best(struct p2p_data *p2p)
+{
+	u8 op_class, op_channel;
+	const int op_classes_5ghz[] = { 124, 125, 115, 0 };
+	const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
+	const int op_classes_vht[] = { 128, 0 };
+
+	p2p_dbg(p2p, "Prepare channel best");
+
+	if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 &&
+	    p2p_supported_freq(p2p, p2p->best_freq_overall) &&
+	    p2p_freq_to_channel(p2p->best_freq_overall, &op_class, &op_channel)
+	    == 0) {
+		p2p_dbg(p2p, "Select best overall channel as operating channel preference");
+		p2p->op_reg_class = op_class;
+		p2p->op_channel = op_channel;
+	} else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 &&
+		   p2p_supported_freq(p2p, p2p->best_freq_5) &&
+		   p2p_freq_to_channel(p2p->best_freq_5, &op_class, &op_channel)
+		   == 0) {
+		p2p_dbg(p2p, "Select best 5 GHz channel as operating channel preference");
+		p2p->op_reg_class = op_class;
+		p2p->op_channel = op_channel;
+	} else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_24 > 0 &&
+		   p2p_supported_freq(p2p, p2p->best_freq_24) &&
+		   p2p_freq_to_channel(p2p->best_freq_24, &op_class,
+				       &op_channel) == 0) {
+		p2p_dbg(p2p, "Select best 2.4 GHz channel as operating channel preference");
+		p2p->op_reg_class = op_class;
+		p2p->op_channel = op_channel;
+	} else if (p2p->cfg->num_pref_chan > 0 &&
+		   p2p_channels_includes(&p2p->cfg->channels,
+					 p2p->cfg->pref_chan[0].op_class,
+					 p2p->cfg->pref_chan[0].chan)) {
+		p2p_dbg(p2p, "Select first pref_chan entry as operating channel preference");
+		p2p->op_reg_class = p2p->cfg->pref_chan[0].op_class;
+		p2p->op_channel = p2p->cfg->pref_chan[0].chan;
+	} else if (p2p_channel_select(&p2p->cfg->channels, op_classes_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 (op_class %u channel %u) as operating channel preference",
+			p2p->op_reg_class, 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,
+		  sizeof(struct p2p_channels));
+}
+
+
+/**
+ * p2p_prepare_channel - Select operating channel for GO Negotiation
+ * @p2p: P2P module context from p2p_init()
+ * @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
+ * Negotiation prior to having received peer information. The selected channel
+ * may be further optimized in p2p_reselect_channel() once the peer information
+ * is available.
+ */
+int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
+			unsigned int force_freq, unsigned int pref_freq, int go)
+{
+	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, 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_inplace(&p2p->channels,
+					   &p2p->cfg->cli_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)" : "");
+
+	if (force_freq)
+		dev->flags |= P2P_DEV_FORCE_FREQ;
+	else
+		dev->flags &= ~P2P_DEV_FORCE_FREQ;
+
+	return 0;
+}
+
+
+static void p2p_set_dev_persistent(struct p2p_device *dev,
+				   int persistent_group)
+{
+	switch (persistent_group) {
+	case 0:
+		dev->flags &= ~(P2P_DEV_PREFER_PERSISTENT_GROUP |
+				P2P_DEV_PREFER_PERSISTENT_RECONN);
+		break;
+	case 1:
+		dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
+		dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_RECONN;
+		break;
+	case 2:
+		dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP |
+			P2P_DEV_PREFER_PERSISTENT_RECONN;
+		break;
+	}
+}
+
+
+int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
+		enum p2p_wps_method wps_method,
+		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, 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 "
+		"oob_pw_id=%u",
+		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
+		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)) {
+		p2p_dbg(p2p, "Cannot connect to unknown P2P Device " MACSTR,
+			MAC2STR(peer_addr));
+		return -1;
+	}
+
+	if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq,
+				go_intent == 15) < 0)
+		return -1;
+
+	if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+		if (!(dev->info.dev_capab &
+		      P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+			p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR
+				" that is in a group and is not discoverable",
+				MAC2STR(peer_addr));
+			return -1;
+		}
+		if (dev->oper_freq <= 0) {
+			p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR
+				" with incomplete information",
+				MAC2STR(peer_addr));
+			return -1;
+		}
+
+		/*
+		 * First, try to connect directly. If the peer does not
+		 * acknowledge frames, assume it is sleeping and use device
+		 * discoverability via the GO at that point.
+		 */
+	}
+
+	p2p->ssid_set = 0;
+	if (force_ssid) {
+		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
+				  force_ssid, force_ssid_len);
+		os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
+		p2p->ssid_len = force_ssid_len;
+		p2p->ssid_set = 1;
+	}
+
+	dev->flags &= ~P2P_DEV_NOT_YET_READY;
+	dev->flags &= ~P2P_DEV_USER_REJECTED;
+	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+	if (pd_before_go_neg)
+		dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG;
+	else {
+		dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
+		/*
+		 * Assign dialog token and tie breaker here to use the same
+		 * values in each retry within the same GO Negotiation exchange.
+		 */
+		dev->dialog_token++;
+		if (dev->dialog_token == 0)
+			dev->dialog_token = 1;
+		dev->tie_breaker = p2p->next_tie_breaker;
+		p2p->next_tie_breaker = !p2p->next_tie_breaker;
+	}
+	dev->connect_reqs = 0;
+	dev->go_neg_req_sent = 0;
+	dev->go_state = UNKNOWN_GO;
+	p2p_set_dev_persistent(dev, persistent_group);
+	p2p->go_intent = go_intent;
+	os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+
+	if (p2p->state != P2P_IDLE)
+		p2p_stop_find(p2p);
+
+	if (p2p->after_scan_tx) {
+		/*
+		 * We need to drop the pending frame to avoid issues with the
+		 * new GO Negotiation, e.g., when the pending frame was from a
+		 * previous attempt at starting a GO Negotiation.
+		 */
+		p2p_dbg(p2p, "Dropped previous pending Action frame TX that was waiting for p2p_scan completion");
+		os_free(p2p->after_scan_tx);
+		p2p->after_scan_tx = NULL;
+	}
+
+	dev->wps_method = wps_method;
+	dev->oob_pw_id = oob_pw_id;
+	dev->status = P2P_SC_SUCCESS;
+
+	if (p2p->p2p_scan_running) {
+		p2p_dbg(p2p, "p2p_scan running - delay connect send");
+		p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT;
+		os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN);
+		return 0;
+	}
+	p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+
+	return p2p_connect_send(p2p, dev);
+}
+
+
+int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
+		  enum p2p_wps_method wps_method,
+		  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, 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 oob_pw_id=%u",
+		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
+		wps_method, persistent_group, oob_pw_id);
+
+	dev = p2p_get_device(p2p, peer_addr);
+	if (dev == NULL) {
+		p2p_dbg(p2p, "Cannot authorize unknown P2P Device " MACSTR,
+			MAC2STR(peer_addr));
+		return -1;
+	}
+
+	if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent ==
+				15) < 0)
+		return -1;
+
+	p2p->ssid_set = 0;
+	if (force_ssid) {
+		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
+				  force_ssid, force_ssid_len);
+		os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
+		p2p->ssid_len = force_ssid_len;
+		p2p->ssid_set = 1;
+	}
+
+	dev->flags &= ~P2P_DEV_NOT_YET_READY;
+	dev->flags &= ~P2P_DEV_USER_REJECTED;
+	dev->go_neg_req_sent = 0;
+	dev->go_state = UNKNOWN_GO;
+	p2p_set_dev_persistent(dev, persistent_group);
+	p2p->go_intent = go_intent;
+	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;
+}
+
+
+void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
+		      struct p2p_device *dev, struct p2p_message *msg)
+{
+	os_get_reltime(&dev->last_seen);
+
+	p2p_copy_wps_info(p2p, dev, 0, msg);
+
+	if (msg->listen_channel) {
+		int freq;
+		freq = p2p_channel_to_freq(msg->listen_channel[3],
+					   msg->listen_channel[4]);
+		if (freq < 0) {
+			p2p_dbg(p2p, "Unknown peer Listen channel: "
+				"country=%c%c(0x%02x) reg_class=%u channel=%u",
+				msg->listen_channel[0],
+				msg->listen_channel[1],
+				msg->listen_channel[2],
+				msg->listen_channel[3],
+				msg->listen_channel[4]);
+		} else {
+			p2p_dbg(p2p, "Update peer " MACSTR
+				" Listen channel: %u -> %u MHz",
+				MAC2STR(dev->info.p2p_device_addr),
+				dev->listen_freq, freq);
+			dev->listen_freq = freq;
+		}
+	}
+
+	if (msg->wfd_subelems) {
+		wpabuf_free(dev->info.wfd_subelems);
+		dev->info.wfd_subelems = wpabuf_dup(msg->wfd_subelems);
+	}
+
+	if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+		dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
+		p2p_dbg(p2p, "Completed device entry based on data from GO Negotiation Request");
+	} else {
+		p2p_dbg(p2p, "Created device entry based on GO Neg Req: "
+			MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' "
+			"listen_freq=%d",
+			MAC2STR(dev->info.p2p_device_addr),
+			dev->info.dev_capab, dev->info.group_capab,
+			dev->info.device_name, dev->listen_freq);
+	}
+
+	dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY;
+
+	if (dev->flags & P2P_DEV_USER_REJECTED) {
+		p2p_dbg(p2p, "Do not report rejected device");
+		return;
+	}
+
+	p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info,
+			    !(dev->flags & P2P_DEV_REPORTED_ONCE));
+	dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+}
+
+
+void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len)
+{
+	os_memcpy(ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
+	p2p_random((char *) &ssid[P2P_WILDCARD_SSID_LEN], 2);
+	os_memcpy(&ssid[P2P_WILDCARD_SSID_LEN + 2],
+		  p2p->cfg->ssid_postfix, p2p->cfg->ssid_postfix_len);
+	*ssid_len = P2P_WILDCARD_SSID_LEN + 2 + p2p->cfg->ssid_postfix_len;
+}
+
+
+int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params)
+{
+	if (p2p->ssid_set) {
+		os_memcpy(params->ssid, p2p->ssid, p2p->ssid_len);
+		params->ssid_len = p2p->ssid_len;
+	} else {
+		p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
+	}
+	p2p->ssid_set = 0;
+
+	p2p_random(params->passphrase, p2p->cfg->passphrase_len);
+	return 0;
+}
+
+
+void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
+{
+	struct p2p_go_neg_results res;
+	int go = peer->go_state == LOCAL_GO;
+	struct p2p_channels intersection;
+
+	p2p_dbg(p2p, "GO Negotiation with " MACSTR " completed (%s will be GO)",
+		MAC2STR(peer->info.p2p_device_addr), go ? "local end" : "peer");
+
+	os_memset(&res, 0, sizeof(res));
+	res.role_go = go;
+	os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
+	os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
+	res.wps_method = peer->wps_method;
+	if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
+		if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
+			res.persistent_group = 2;
+		else
+			res.persistent_group = 1;
+	}
+
+	if (go) {
+		/* Setup AP mode for WPS provisioning */
+		res.freq = p2p_channel_to_freq(p2p->op_reg_class,
+					       p2p->op_channel);
+		os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
+		res.ssid_len = p2p->ssid_len;
+		p2p_random(res.passphrase, p2p->cfg->passphrase_len);
+	} else {
+		res.freq = peer->oper_freq;
+		if (p2p->ssid_len) {
+			os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
+			res.ssid_len = p2p->ssid_len;
+		}
+	}
+
+	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);
+	}
+
+	p2p_channels_to_freqs(&intersection, res.freq_list,
+			      P2P_MAX_CHANNELS);
+
+	res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout;
+
+	p2p_clear_timeout(p2p);
+	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);
+}
+
+
+static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa,
+			      const u8 *data, size_t len, int rx_freq)
+{
+	p2p_dbg(p2p, "RX P2P Public Action from " MACSTR, MAC2STR(sa));
+	wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len);
+
+	if (len < 1)
+		return;
+
+	switch (data[0]) {
+	case P2P_GO_NEG_REQ:
+		p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq);
+		break;
+	case P2P_GO_NEG_RESP:
+		p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq);
+		break;
+	case P2P_GO_NEG_CONF:
+		p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1);
+		break;
+	case P2P_INVITATION_REQ:
+		p2p_process_invitation_req(p2p, sa, data + 1, len - 1,
+					   rx_freq);
+		break;
+	case P2P_INVITATION_RESP:
+		p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
+		break;
+	case P2P_PROV_DISC_REQ:
+		p2p_process_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
+		break;
+	case P2P_PROV_DISC_RESP:
+		p2p_process_prov_disc_resp(p2p, sa, data + 1, len - 1);
+		break;
+	case P2P_DEV_DISC_REQ:
+		p2p_process_dev_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
+		break;
+	case P2P_DEV_DISC_RESP:
+		p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1);
+		break;
+	default:
+		p2p_dbg(p2p, "Unsupported P2P Public Action frame type %d",
+			data[0]);
+		break;
+	}
+}
+
+
+static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da,
+				 const u8 *sa, const u8 *bssid, const u8 *data,
+				 size_t len, int freq)
+{
+	if (len < 1)
+		return;
+
+	switch (data[0]) {
+	case WLAN_PA_VENDOR_SPECIFIC:
+		data++;
+		len--;
+		if (len < 4)
+			return;
+		if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE)
+			return;
+
+		data += 4;
+		len -= 4;
+
+		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);
+		break;
+	case WLAN_PA_GAS_INITIAL_RESP:
+		p2p_rx_gas_initial_resp(p2p, sa, data + 1, len - 1, freq);
+		break;
+	case WLAN_PA_GAS_COMEBACK_REQ:
+		p2p_rx_gas_comeback_req(p2p, sa, data + 1, len - 1, freq);
+		break;
+	case WLAN_PA_GAS_COMEBACK_RESP:
+		p2p_rx_gas_comeback_resp(p2p, sa, data + 1, len - 1, freq);
+		break;
+	}
+}
+
+
+void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+		   const u8 *bssid, u8 category,
+		   const u8 *data, size_t len, int freq)
+{
+	if (category == WLAN_ACTION_PUBLIC) {
+		p2p_rx_action_public(p2p, da, sa, bssid, data, len, freq);
+		return;
+	}
+
+	if (category != WLAN_ACTION_VENDOR_SPECIFIC)
+		return;
+
+	if (len < 4)
+		return;
+
+	if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE)
+		return;
+	data += 4;
+	len -= 4;
+
+	/* P2P action frame */
+	p2p_dbg(p2p, "RX P2P Action from " MACSTR, MAC2STR(sa));
+	wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len);
+
+	if (len < 1)
+		return;
+	switch (data[0]) {
+	case P2P_NOA:
+		p2p_dbg(p2p, "Received P2P Action - Notice of Absence");
+		/* TODO */
+		break;
+	case P2P_PRESENCE_REQ:
+		p2p_process_presence_req(p2p, da, sa, data + 1, len - 1, freq);
+		break;
+	case P2P_PRESENCE_RESP:
+		p2p_process_presence_resp(p2p, da, sa, data + 1, len - 1);
+		break;
+	case P2P_GO_DISC_REQ:
+		p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq);
+		break;
+	default:
+		p2p_dbg(p2p, "Received P2P Action - unknown type %u", data[0]);
+		break;
+	}
+}
+
+
+static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+	if (p2p->go_neg_peer == NULL)
+		return;
+	if (p2p->pending_listen_freq) {
+		p2p_dbg(p2p, "Clear pending_listen_freq for p2p_go_neg_start");
+		p2p->pending_listen_freq = 0;
+	}
+	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+	p2p->go_neg_peer->status = P2P_SC_SUCCESS;
+	/*
+	 * Set new timeout to make sure a previously set one does not expire
+	 * too quickly while waiting for the GO Negotiation to complete.
+	 */
+	p2p_set_timeout(p2p, 0, 500000);
+	p2p_connect_send(p2p, p2p->go_neg_peer);
+}
+
+
+static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+	if (p2p->invite_peer == NULL)
+		return;
+	if (p2p->pending_listen_freq) {
+		p2p_dbg(p2p, "Clear pending_listen_freq for p2p_invite_start");
+		p2p->pending_listen_freq = 0;
+	}
+	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+	p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
+			p2p->invite_dev_pw_id);
+}
+
+
+static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr,
+				       const u8 *ie, size_t ie_len)
+{
+	struct p2p_message msg;
+	struct p2p_device *dev;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_ies(ie, ie_len, &msg) < 0 || msg.p2p_attributes == NULL)
+	{
+		p2p_parse_free(&msg);
+		return; /* not a P2P probe */
+	}
+
+	if (msg.ssid == NULL || msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
+	    os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
+	    != 0) {
+		/* The Probe Request is not part of P2P Device Discovery. It is
+		 * not known whether the source address of the frame is the P2P
+		 * Device Address or P2P Interface Address. Do not add a new
+		 * peer entry based on this frames.
+		 */
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	dev = p2p_get_device(p2p, addr);
+	if (dev) {
+		if (dev->country[0] == 0 && msg.listen_channel)
+			os_memcpy(dev->country, msg.listen_channel, 3);
+		os_get_reltime(&dev->last_seen);
+		p2p_parse_free(&msg);
+		return; /* already known */
+	}
+
+	dev = p2p_create_device(p2p, addr);
+	if (dev == NULL) {
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	os_get_reltime(&dev->last_seen);
+	dev->flags |= P2P_DEV_PROBE_REQ_ONLY;
+
+	if (msg.listen_channel) {
+		os_memcpy(dev->country, msg.listen_channel, 3);
+		dev->listen_freq = p2p_channel_to_freq(msg.listen_channel[3],
+						       msg.listen_channel[4]);
+	}
+
+	p2p_copy_wps_info(p2p, dev, 1, &msg);
+
+	if (msg.wfd_subelems) {
+		wpabuf_free(dev->info.wfd_subelems);
+		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+	}
+
+	p2p_parse_free(&msg);
+
+	p2p_dbg(p2p, "Created device entry based on Probe Req: " MACSTR
+		" dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d",
+		MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab,
+		dev->info.group_capab, dev->info.device_name,
+		dev->listen_freq);
+}
+
+
+struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
+						const u8 *addr,
+						struct p2p_message *msg)
+{
+	struct p2p_device *dev;
+
+	dev = p2p_get_device(p2p, addr);
+	if (dev) {
+		os_get_reltime(&dev->last_seen);
+		return dev; /* already known */
+	}
+
+	dev = p2p_create_device(p2p, addr);
+	if (dev == NULL)
+		return NULL;
+
+	p2p_add_dev_info(p2p, addr, dev, msg);
+
+	return dev;
+}
+
+
+static int dev_type_match(const u8 *dev_type, const u8 *req_dev_type)
+{
+	if (os_memcmp(dev_type, req_dev_type, WPS_DEV_TYPE_LEN) == 0)
+		return 1;
+	if (os_memcmp(dev_type, req_dev_type, 2) == 0 &&
+	    WPA_GET_BE32(&req_dev_type[2]) == 0 &&
+	    WPA_GET_BE16(&req_dev_type[6]) == 0)
+		return 1; /* Category match with wildcard OUI/sub-category */
+	return 0;
+}
+
+
+int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
+			size_t num_req_dev_type)
+{
+	size_t i;
+	for (i = 0; i < num_req_dev_type; i++) {
+		if (dev_type_match(dev_type, req_dev_type[i]))
+			return 1;
+	}
+	return 0;
+}
+
+
+/**
+ * p2p_match_dev_type - Match local device type with requested type
+ * @p2p: P2P module context from p2p_init()
+ * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
+ * Returns: 1 on match, 0 on mismatch
+ *
+ * This function can be used to match the Requested Device Type attribute in
+ * WPS IE with the local device types for deciding whether to reply to a Probe
+ * Request frame.
+ */
+int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps)
+{
+	struct wps_parse_attr attr;
+	size_t i;
+
+	if (wps_parse_msg(wps, &attr))
+		return 1; /* assume no Requested Device Type attributes */
+
+	if (attr.num_req_dev_type == 0)
+		return 1; /* no Requested Device Type attributes -> match */
+
+	if (dev_type_list_match(p2p->cfg->pri_dev_type, attr.req_dev_type,
+				attr.num_req_dev_type))
+		return 1; /* Own Primary Device Type matches */
+
+	for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) {
+		if (dev_type_list_match(p2p->cfg->sec_dev_type[i],
+					attr.req_dev_type,
+					attr.num_req_dev_type))
+			return 1; /* Own Secondary Device Type matches */
+	}
+
+	/* No matching device type found */
+	return 0;
+}
+
+
+struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p,
+					 const u8 *query_hash,
+					 u8 query_count)
+{
+	struct wpabuf *buf;
+	u8 *len;
+	int pw_id = -1;
+	size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_probe_resp)
+		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]);
+
+	if (query_count)
+		extra += MAX_SVC_ADV_IE_LEN;
+
+	buf = wpabuf_alloc(1000 + extra);
+	if (buf == NULL)
+		return NULL;
+
+	if (p2p->go_neg_peer) {
+		/* Advertise immediate availability of WPS credential */
+		pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method);
+	}
+
+	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 &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+	if (p2p->ext_listen_interval)
+		p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
+					      p2p->ext_listen_interval);
+	p2p_buf_add_device_info(buf, p2p, NULL);
+	p2p_buf_update_ie_hdr(buf, len);
+
+	if (query_count) {
+		p2p_buf_add_service_instance(buf, p2p, query_count, query_hash,
+					     p2p->p2ps_adv_list);
+	}
+
+	return buf;
+}
+
+
+static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash)
+{
+	struct p2ps_advertisement *adv_data;
+	int any_wfa;
+
+	p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list);
+
+	/* Wildcard org.wi-fi.wfds matches any WFA spec defined service */
+	any_wfa = os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0;
+
+	adv_data = p2p->p2ps_adv_list;
+	while (adv_data) {
+		if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0)
+			return 1; /* exact hash match */
+		if (any_wfa &&
+		    os_strncmp(adv_data->svc_name, P2PS_WILD_HASH_STR,
+			       os_strlen(P2PS_WILD_HASH_STR)) == 0)
+			return 1; /* WFA service match */
+		adv_data = adv_data->next;
+	}
+
+	return 0;
+}
+
+
+static enum p2p_probe_req_status
+p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+		const u8 *bssid, const u8 *ie, size_t ie_len,
+		unsigned int rx_freq)
+{
+	struct ieee802_11_elems elems;
+	struct wpabuf *buf;
+	struct ieee80211_mgmt *resp;
+	struct p2p_message msg;
+	struct wpabuf *ies;
+	u8 channel, op_class;
+
+	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;
+	}
+
+	if (dst && !is_broadcast_ether_addr(dst) &&
+	    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;
+	}
+
+	if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN ||
+	    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.service_hash && msg.service_hash_count) {
+		const u8 *hash = msg.service_hash;
+		u8 i;
+		int p2ps_svc_found = 0;
+
+		p2p_dbg(p2p, "in_listen=%d drv_in_listen=%d when received P2PS Probe Request at %u MHz; own Listen channel %u, pending listen freq %u MHz",
+			p2p->in_listen, p2p->drv_in_listen, rx_freq,
+			p2p->cfg->channel, p2p->pending_listen_freq);
+
+		if (!p2p->in_listen && !p2p->drv_in_listen &&
+		    p2p->pending_listen_freq && rx_freq &&
+		    rx_freq != p2p->pending_listen_freq) {
+			p2p_dbg(p2p, "Do not reply to Probe Request frame that was received on %u MHz while waiting to start Listen state on %u MHz",
+				rx_freq, p2p->pending_listen_freq);
+			p2p_parse_free(&msg);
+			return P2P_PREQ_NOT_LISTEN;
+		}
+
+		for (i = 0; i < msg.service_hash_count; i++) {
+			if (p2p_service_find_asp(p2p, hash)) {
+				p2p_dbg(p2p, "Service Hash match found: "
+					MACSTR, MAC2STR(hash));
+				p2ps_svc_found = 1;
+				break;
+			}
+			hash += P2PS_HASH_LEN;
+		}
+
+		/* Probed hash unknown */
+		if (!p2ps_svc_found) {
+			p2p_dbg(p2p, "No Service Hash match found");
+			p2p_parse_free(&msg);
+			return P2P_PREQ_NOT_PROCESSED;
+		}
+	} else {
+		/* This is not a P2PS Probe Request */
+		p2p_dbg(p2p, "No P2PS Hash in Probe Request");
+
+		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);
+			p2p_parse_free(&msg);
+			return P2P_PREQ_NOT_LISTEN;
+		}
+	}
+
+	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;
+	}
+
+	/* Check Requested Device Type match */
+	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;
+	}
+
+	if (!p2p->cfg->send_probe_resp) {
+		/* Response generated elsewhere */
+		p2p_dbg(p2p, "Probe Resp generated elsewhere - do not generate additional response");
+		p2p_parse_free(&msg);
+		return P2P_PREQ_NOT_PROCESSED;
+	}
+
+	p2p_dbg(p2p, "Reply to P2P Probe Request in Listen state");
+
+	/*
+	 * We do not really have a specific BSS that this frame is advertising,
+	 * so build a frame that has some information in valid format. This is
+	 * really only used for discovery purposes, not to learn exact BSS
+	 * parameters.
+	 */
+	ies = p2p_build_probe_resp_ies(p2p, msg.service_hash,
+				       msg.service_hash_count);
+	p2p_parse_free(&msg);
+	if (ies == NULL)
+		return P2P_PREQ_NOT_PROCESSED;
+
+	buf = wpabuf_alloc(200 + wpabuf_len(ies));
+	if (buf == NULL) {
+		wpabuf_free(ies);
+		return P2P_PREQ_NOT_PROCESSED;
+	}
+
+	resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
+					u.probe_resp.variable));
+
+	resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+					   (WLAN_FC_STYPE_PROBE_RESP << 4));
+	os_memcpy(resp->da, addr, ETH_ALEN);
+	os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN);
+	os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN);
+	resp->u.probe_resp.beacon_int = host_to_le16(100);
+	/* hardware or low-level driver will setup seq_ctrl and timestamp */
+	resp->u.probe_resp.capab_info =
+		host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE |
+			     WLAN_CAPABILITY_PRIVACY |
+			     WLAN_CAPABILITY_SHORT_SLOT_TIME);
+
+	wpabuf_put_u8(buf, WLAN_EID_SSID);
+	wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN);
+	wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
+
+	wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES);
+	wpabuf_put_u8(buf, 8);
+	wpabuf_put_u8(buf, (60 / 5) | 0x80);
+	wpabuf_put_u8(buf, 90 / 5);
+	wpabuf_put_u8(buf, (120 / 5) | 0x80);
+	wpabuf_put_u8(buf, 180 / 5);
+	wpabuf_put_u8(buf, (240 / 5) | 0x80);
+	wpabuf_put_u8(buf, 360 / 5);
+	wpabuf_put_u8(buf, 480 / 5);
+	wpabuf_put_u8(buf, 540 / 5);
+
+	if (!rx_freq) {
+		channel = p2p->cfg->channel;
+	} else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) {
+		wpabuf_free(ies);
+		wpabuf_free(buf);
+		return P2P_PREQ_NOT_PROCESSED;
+	}
+
+	wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS);
+	wpabuf_put_u8(buf, 1);
+	wpabuf_put_u8(buf, channel);
+
+	wpabuf_put_buf(buf, ies);
+	wpabuf_free(ies);
+
+	p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq);
+
+	wpabuf_free(buf);
+
+	return P2P_PREQ_PROCESSED;
+}
+
+
+enum p2p_probe_req_status
+p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+		 const u8 *bssid, const u8 *ie, size_t ie_len,
+		 unsigned int rx_freq)
+{
+	enum p2p_probe_req_status res;
+
+	p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
+
+	res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len, rx_freq);
+	if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED)
+		return res;
+
+	/*
+	 * Activate a pending GO Negotiation/Invite flow if a received Probe
+	 * Request frame is from an expected peer. Some devices may share the
+	 * same address for P2P and non-P2P STA running simultaneously. The
+	 * P2P_PREQ_PROCESSED and P2P_PREQ_NOT_PROCESSED p2p_reply_probe()
+	 * return values verified above ensure we are handling a Probe Request
+	 * frame from a P2P peer.
+	 */
+	if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
+	    p2p->go_neg_peer &&
+	    os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN)
+	    == 0 &&
+	    !(p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
+		/* Received a Probe Request from GO Negotiation peer */
+		p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout");
+		eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL);
+		eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);
+		return res;
+	}
+
+	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 res;
+	}
+
+	return res;
+}
+
+
+static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid,
+				    u8 *buf, size_t len, struct wpabuf *p2p_ie)
+{
+	struct wpabuf *tmp;
+	u8 *lpos;
+	size_t tmplen;
+	int res;
+	u8 group_capab;
+	struct p2p_message msg;
+
+	if (p2p_ie == NULL)
+		return 0; /* WLAN AP is not a P2P manager */
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(p2p_ie, &msg) < 0)
+		return 0;
+
+	p2p_dbg(p2p, "BSS P2P manageability %s",
+		msg.manageability ? "enabled" : "disabled");
+
+	if (!msg.manageability)
+		return 0;
+
+	/*
+	 * (Re)Association Request - P2P IE
+	 * P2P Capability attribute (shall be present)
+	 * P2P Interface attribute (present if concurrent device and
+	 *	P2P Management is enabled)
+	 */
+	tmp = wpabuf_alloc(200);
+	if (tmp == NULL)
+		return -1;
+
+	lpos = p2p_buf_add_ie_hdr(tmp);
+	group_capab = 0;
+	if (p2p->num_groups > 0) {
+		group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
+		if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) &&
+		    (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) &&
+		    p2p->cross_connect)
+			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+	}
+	p2p_buf_add_capability(tmp, p2p->dev_capab, group_capab);
+	if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) &&
+	    (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED))
+		p2p_buf_add_p2p_interface(tmp, p2p);
+	p2p_buf_update_ie_hdr(tmp, lpos);
+
+	tmplen = wpabuf_len(tmp);
+	if (tmplen > len)
+		res = -1;
+	else {
+		os_memcpy(buf, wpabuf_head(tmp), tmplen);
+		res = tmplen;
+	}
+	wpabuf_free(tmp);
+
+	return res;
+}
+
+
+int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
+		     size_t len, int p2p_group, struct wpabuf *p2p_ie)
+{
+	struct wpabuf *tmp;
+	u8 *lpos;
+	struct p2p_device *peer;
+	size_t tmplen;
+	int res;
+	size_t extra = 0;
+
+	if (!p2p_group)
+		return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie);
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_assoc_req)
+		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)
+	 * Extended Listen Timing (may be present)
+	 * P2P Device Info attribute (shall be present)
+	 */
+	tmp = wpabuf_alloc(200 + extra);
+	if (tmp == NULL)
+		return -1;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_assoc_req)
+		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);
+	p2p_buf_add_capability(tmp, p2p->dev_capab, 0);
+	if (p2p->ext_listen_interval)
+		p2p_buf_add_ext_listen_timing(tmp, p2p->ext_listen_period,
+					      p2p->ext_listen_interval);
+	p2p_buf_add_device_info(tmp, p2p, peer);
+	p2p_buf_update_ie_hdr(tmp, lpos);
+
+	tmplen = wpabuf_len(tmp);
+	if (tmplen > len)
+		res = -1;
+	else {
+		os_memcpy(buf, wpabuf_head(tmp), tmplen);
+		res = tmplen;
+	}
+	wpabuf_free(tmp);
+
+	return res;
+}
+
+
+int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end)
+{
+	struct wpabuf *p2p_ie;
+	int ret;
+
+	p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE);
+	if (p2p_ie == NULL)
+		return 0;
+
+	ret = p2p_attr_text(p2p_ie, buf, end);
+	wpabuf_free(p2p_ie);
+	return ret;
+}
+
+
+struct p2ps_advertisement *
+p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id)
+{
+	struct p2ps_advertisement *adv_data;
+
+	if (!p2p)
+		return NULL;
+
+	adv_data = p2p->p2ps_adv_list;
+	while (adv_data) {
+		if (adv_data->id == adv_id)
+			return adv_data;
+		adv_data = adv_data->next;
+	}
+
+	return NULL;
+}
+
+
+int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id)
+{
+	struct p2ps_advertisement *adv_data;
+	struct p2ps_advertisement **prior;
+
+	if (!p2p)
+		return -1;
+
+	adv_data = p2p->p2ps_adv_list;
+	prior = &p2p->p2ps_adv_list;
+	while (adv_data) {
+		if (adv_data->id == adv_id) {
+			p2p_dbg(p2p, "Delete ASP adv_id=0x%x", adv_id);
+			*prior = adv_data->next;
+			os_free(adv_data);
+			return 0;
+		}
+		prior = &adv_data->next;
+		adv_data = adv_data->next;
+	}
+
+	return -1;
+}
+
+
+int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
+			const char *adv_str, u8 svc_state, u16 config_methods,
+			const char *svc_info, const u8 *cpt_priority)
+{
+	struct p2ps_advertisement *adv_data, *tmp, **prev;
+	u8 buf[P2PS_HASH_LEN];
+	size_t adv_data_len, adv_len, info_len = 0;
+	int i;
+
+	if (!p2p || !adv_str || !adv_str[0] || !cpt_priority)
+		return -1;
+
+	if (!(config_methods & p2p->cfg->config_methods)) {
+		p2p_dbg(p2p, "Config methods not supported svc: 0x%x dev: 0x%x",
+			config_methods, p2p->cfg->config_methods);
+		return -1;
+	}
+
+	if (!p2ps_gen_hash(p2p, adv_str, buf))
+		return -1;
+
+	if (svc_info)
+		info_len = os_strlen(svc_info);
+	adv_len = os_strlen(adv_str);
+	adv_data_len = sizeof(struct p2ps_advertisement) + adv_len + 1 +
+		info_len + 1;
+
+	adv_data = os_zalloc(adv_data_len);
+	if (!adv_data)
+		return -1;
+
+	os_memcpy(adv_data->hash, buf, P2PS_HASH_LEN);
+	adv_data->id = adv_id;
+	adv_data->state = svc_state;
+	adv_data->config_methods = config_methods & p2p->cfg->config_methods;
+	adv_data->auto_accept = (u8) auto_accept;
+	os_memcpy(adv_data->svc_name, adv_str, adv_len);
+
+	for (i = 0; cpt_priority[i] && i < P2PS_FEATURE_CAPAB_CPT_MAX; i++) {
+		adv_data->cpt_priority[i] = cpt_priority[i];
+		adv_data->cpt_mask |= cpt_priority[i];
+	}
+
+	if (svc_info && info_len) {
+		adv_data->svc_info = &adv_data->svc_name[adv_len + 1];
+		os_memcpy(adv_data->svc_info, svc_info, info_len);
+	}
+
+	/*
+	 * Group Advertisements by service string. They do not need to be
+	 * sorted, but groups allow easier Probe Response instance grouping
+	 */
+	tmp = p2p->p2ps_adv_list;
+	prev = &p2p->p2ps_adv_list;
+	while (tmp) {
+		if (tmp->id == adv_data->id) {
+			if (os_strcmp(tmp->svc_name, adv_data->svc_name) != 0) {
+				os_free(adv_data);
+				return -1;
+			}
+			adv_data->next = tmp->next;
+			*prev = adv_data;
+			os_free(tmp);
+			goto inserted;
+		} else {
+			if (os_strcmp(tmp->svc_name, adv_data->svc_name) == 0) {
+				adv_data->next = tmp->next;
+				tmp->next = adv_data;
+				goto inserted;
+			}
+		}
+		prev = &tmp->next;
+		tmp = tmp->next;
+	}
+
+	/* No svc_name match found */
+	adv_data->next = p2p->p2ps_adv_list;
+	p2p->p2ps_adv_list = adv_data;
+
+inserted:
+	p2p_dbg(p2p,
+		"Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s' cpt_mask=0x%x",
+		adv_id, adv_data->config_methods, svc_state, adv_str,
+		adv_data->cpt_mask);
+
+	return 0;
+}
+
+
+void p2p_service_flush_asp(struct p2p_data *p2p)
+{
+	struct p2ps_advertisement *adv, *prev;
+
+	if (!p2p)
+		return;
+
+	adv = p2p->p2ps_adv_list;
+	while (adv) {
+		prev = adv;
+		adv = adv->next;
+		os_free(prev);
+	}
+
+	p2p->p2ps_adv_list = NULL;
+	p2p_dbg(p2p, "All ASP advertisements flushed");
+}
+
+
+int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr)
+{
+	struct p2p_message msg;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(p2p_ie, &msg))
+		return -1;
+
+	if (msg.p2p_device_addr) {
+		os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+		return 0;
+	} else if (msg.device_id) {
+		os_memcpy(dev_addr, msg.device_id, ETH_ALEN);
+		return 0;
+	}
+	return -1;
+}
+
+
+int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr)
+{
+	struct wpabuf *p2p_ie;
+	int ret;
+
+	p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+					     P2P_IE_VENDOR_TYPE);
+	if (p2p_ie == NULL)
+		return -1;
+	ret = p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr);
+	wpabuf_free(p2p_ie);
+	return ret;
+}
+
+
+static void p2p_clear_go_neg(struct p2p_data *p2p)
+{
+	p2p->go_neg_peer = NULL;
+	p2p_clear_timeout(p2p);
+	p2p_set_state(p2p, P2P_IDLE);
+}
+
+
+void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr)
+{
+	if (p2p->go_neg_peer == NULL) {
+		p2p_dbg(p2p, "No pending Group Formation - ignore WPS registration success notification");
+		return; /* No pending Group Formation */
+	}
+
+	if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) !=
+	    0) {
+		p2p_dbg(p2p, "Ignore WPS registration success notification for "
+			MACSTR " (GO Negotiation peer " MACSTR ")",
+			MAC2STR(mac_addr),
+			MAC2STR(p2p->go_neg_peer->intended_addr));
+		return; /* Ignore unexpected peer address */
+	}
+
+	p2p_dbg(p2p, "Group Formation completed successfully with " MACSTR,
+		MAC2STR(mac_addr));
+
+	p2p_clear_go_neg(p2p);
+}
+
+
+void p2p_group_formation_failed(struct p2p_data *p2p)
+{
+	if (p2p->go_neg_peer == NULL) {
+		p2p_dbg(p2p, "No pending Group Formation - ignore group formation failure notification");
+		return; /* No pending Group Formation */
+	}
+
+	p2p_dbg(p2p, "Group Formation failed with " MACSTR,
+		MAC2STR(p2p->go_neg_peer->intended_addr));
+
+	p2p_clear_go_neg(p2p);
+}
+
+
+struct p2p_data * p2p_init(const struct p2p_config *cfg)
+{
+	struct p2p_data *p2p;
+
+	if (cfg->max_peers < 1 ||
+	    cfg->passphrase_len < 8 || cfg->passphrase_len > 63)
+		return NULL;
+
+	p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg));
+	if (p2p == NULL)
+		return NULL;
+	p2p->cfg = (struct p2p_config *) (p2p + 1);
+	os_memcpy(p2p->cfg, cfg, sizeof(*cfg));
+	if (cfg->dev_name)
+		p2p->cfg->dev_name = os_strdup(cfg->dev_name);
+	if (cfg->manufacturer)
+		p2p->cfg->manufacturer = os_strdup(cfg->manufacturer);
+	if (cfg->model_name)
+		p2p->cfg->model_name = os_strdup(cfg->model_name);
+	if (cfg->model_number)
+		p2p->cfg->model_number = os_strdup(cfg->model_number);
+	if (cfg->serial_number)
+		p2p->cfg->serial_number = os_strdup(cfg->serial_number);
+	if (cfg->pref_chan) {
+		p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan *
+						sizeof(struct p2p_channel));
+		if (p2p->cfg->pref_chan) {
+			os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan,
+				  cfg->num_pref_chan *
+				  sizeof(struct p2p_channel));
+		} else
+			p2p->cfg->num_pref_chan = 0;
+	}
+
+	p2ps_gen_hash(p2p, P2PS_WILD_HASH_STR, p2p->wild_card_hash);
+
+	p2p->min_disc_int = 1;
+	p2p->max_disc_int = 3;
+	p2p->max_disc_tu = -1;
+
+	if (os_get_random(&p2p->next_tie_breaker, 1) < 0)
+		p2p->next_tie_breaker = 0;
+	p2p->next_tie_breaker &= 0x01;
+	if (cfg->sd_request)
+		p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+	p2p->dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
+	if (cfg->concurrent_operations)
+		p2p->dev_capab |= P2P_DEV_CAPAB_CONCURRENT_OPER;
+	p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+
+	dl_list_init(&p2p->devices);
+
+	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;
+}
+
+
+void p2p_deinit(struct p2p_data *p2p)
+{
+#ifdef CONFIG_WIFI_DISPLAY
+	wpabuf_free(p2p->wfd_ie_beacon);
+	wpabuf_free(p2p->wfd_ie_probe_req);
+	wpabuf_free(p2p->wfd_ie_probe_resp);
+	wpabuf_free(p2p->wfd_ie_assoc_req);
+	wpabuf_free(p2p->wfd_ie_invitation);
+	wpabuf_free(p2p->wfd_ie_prov_disc_req);
+	wpabuf_free(p2p->wfd_ie_prov_disc_resp);
+	wpabuf_free(p2p->wfd_ie_go_neg);
+	wpabuf_free(p2p->wfd_dev_info);
+	wpabuf_free(p2p->wfd_assoc_bssid);
+	wpabuf_free(p2p->wfd_coupled_sink_info);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
+	eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+	eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL);
+	eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
+	p2p_flush(p2p);
+	p2p_free_req_dev_types(p2p);
+	os_free(p2p->cfg->dev_name);
+	os_free(p2p->cfg->manufacturer);
+	os_free(p2p->cfg->model_name);
+	os_free(p2p->cfg->model_number);
+	os_free(p2p->cfg->serial_number);
+	os_free(p2p->cfg->pref_chan);
+	os_free(p2p->groups);
+	p2ps_prov_free(p2p);
+	wpabuf_free(p2p->sd_resp);
+	os_free(p2p->after_scan_tx);
+	p2p_remove_wps_vendor_extensions(p2p);
+	os_free(p2p->no_go_freq.range);
+	p2p_service_flush_asp(p2p);
+
+	os_free(p2p);
+}
+
+
+void p2p_flush(struct p2p_data *p2p)
+{
+	struct p2p_device *dev, *prev;
+	p2p_stop_find(p2p);
+	dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device,
+			      list) {
+		dl_list_del(&dev->list);
+		p2p_device_free(p2p, dev);
+	}
+	p2p_free_sd_queries(p2p);
+	os_free(p2p->after_scan_tx);
+	p2p->after_scan_tx = NULL;
+	p2p->ssid_set = 0;
+}
+
+
+int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr)
+{
+	struct p2p_device *dev;
+
+	dev = p2p_get_device(p2p, addr);
+	if (dev == NULL)
+		return -1;
+
+	p2p_dbg(p2p, "Unauthorizing " MACSTR, MAC2STR(addr));
+
+	if (p2p->go_neg_peer == dev) {
+		eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
+		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;
+
+	/* Check if after_scan_tx is for this peer. If so free it */
+	if (p2p->after_scan_tx &&
+	    os_memcmp(addr, p2p->after_scan_tx->dst, ETH_ALEN) == 0) {
+		os_free(p2p->after_scan_tx);
+		p2p->after_scan_tx = NULL;
+	}
+
+	return 0;
+}
+
+
+int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name)
+{
+	os_free(p2p->cfg->dev_name);
+	if (dev_name) {
+		p2p->cfg->dev_name = os_strdup(dev_name);
+		if (p2p->cfg->dev_name == NULL)
+			return -1;
+	} else
+		p2p->cfg->dev_name = NULL;
+	return 0;
+}
+
+
+int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer)
+{
+	os_free(p2p->cfg->manufacturer);
+	p2p->cfg->manufacturer = NULL;
+	if (manufacturer) {
+		p2p->cfg->manufacturer = os_strdup(manufacturer);
+		if (p2p->cfg->manufacturer == NULL)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+int p2p_set_model_name(struct p2p_data *p2p, const char *model_name)
+{
+	os_free(p2p->cfg->model_name);
+	p2p->cfg->model_name = NULL;
+	if (model_name) {
+		p2p->cfg->model_name = os_strdup(model_name);
+		if (p2p->cfg->model_name == NULL)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+int p2p_set_model_number(struct p2p_data *p2p, const char *model_number)
+{
+	os_free(p2p->cfg->model_number);
+	p2p->cfg->model_number = NULL;
+	if (model_number) {
+		p2p->cfg->model_number = os_strdup(model_number);
+		if (p2p->cfg->model_number == NULL)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number)
+{
+	os_free(p2p->cfg->serial_number);
+	p2p->cfg->serial_number = NULL;
+	if (serial_number) {
+		p2p->cfg->serial_number = os_strdup(serial_number);
+		if (p2p->cfg->serial_number == NULL)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods)
+{
+	p2p->cfg->config_methods = config_methods;
+}
+
+
+void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid)
+{
+	os_memcpy(p2p->cfg->uuid, uuid, 16);
+}
+
+
+int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type)
+{
+	os_memcpy(p2p->cfg->pri_dev_type, pri_dev_type, 8);
+	return 0;
+}
+
+
+int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8],
+			  size_t num_dev_types)
+{
+	if (num_dev_types > P2P_SEC_DEVICE_TYPES)
+		num_dev_types = P2P_SEC_DEVICE_TYPES;
+	p2p->cfg->num_sec_dev_types = num_dev_types;
+	os_memcpy(p2p->cfg->sec_dev_type, dev_types, num_dev_types * 8);
+	return 0;
+}
+
+
+void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p)
+{
+	int i;
+
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		wpabuf_free(p2p->wps_vendor_ext[i]);
+		p2p->wps_vendor_ext[i] = NULL;
+	}
+}
+
+
+int p2p_add_wps_vendor_extension(struct p2p_data *p2p,
+				 const struct wpabuf *vendor_ext)
+{
+	int i;
+
+	if (vendor_ext == NULL)
+		return -1;
+
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		if (p2p->wps_vendor_ext[i] == NULL)
+			break;
+	}
+	if (i >= P2P_MAX_WPS_VENDOR_EXT)
+		return -1;
+
+	p2p->wps_vendor_ext[i] = wpabuf_dup(vendor_ext);
+	if (p2p->wps_vendor_ext[i] == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+int p2p_set_country(struct p2p_data *p2p, const char *country)
+{
+	os_memcpy(p2p->cfg->country, country, 3);
+	return 0;
+}
+
+
+static int p2p_pre_find_operation(struct p2p_data *p2p, struct p2p_device *dev)
+{
+	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;
+	}
+
+	if (p2p_start_sd(p2p, dev) == 0)
+		return 1;
+
+	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),
+			dev->req_config_methods);
+		if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+void p2p_continue_find(struct p2p_data *p2p)
+{
+	struct p2p_device *dev;
+	int found;
+
+	p2p_set_state(p2p, P2P_SEARCH);
+
+	/* Continue from the device following the last iteration */
+	found = 0;
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		if (dev == p2p->last_p2p_find_oper) {
+			found = 1;
+			continue;
+		}
+		if (!found)
+			continue;
+		if (p2p_pre_find_operation(p2p, dev) > 0) {
+			p2p->last_p2p_find_oper = dev;
+			return;
+		}
+	}
+
+	/*
+	 * Wrap around to the beginning of the list and continue until the last
+	 * iteration device.
+	 */
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		if (p2p_pre_find_operation(p2p, dev) > 0) {
+			p2p->last_p2p_find_oper = dev;
+			return;
+		}
+		if (dev == p2p->last_p2p_find_oper)
+			break;
+	}
+
+	p2p_listen_in_find(p2p, 1);
+}
+
+
+static void p2p_sd_cb(struct p2p_data *p2p, int success)
+{
+	p2p_dbg(p2p, "Service Discovery Query TX callback: success=%d",
+		success);
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+	if (!success) {
+		if (p2p->sd_peer)
+			p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		p2p->sd_peer = NULL;
+		if (p2p->state != P2P_IDLE)
+			p2p_continue_find(p2p);
+		return;
+	}
+
+	if (p2p->sd_peer == NULL) {
+		p2p_dbg(p2p, "No SD peer entry known");
+		if (p2p->state != P2P_IDLE)
+			p2p_continue_find(p2p);
+		return;
+	}
+
+	if (p2p->sd_query && p2p->sd_query->for_all_peers) {
+		/* Update the pending broadcast SD query count for this device
+		 */
+		p2p->sd_peer->sd_pending_bcast_queries--;
+
+		/*
+		 * If there are no pending broadcast queries for this device,
+		 * mark it as done (-1).
+		 */
+		if (p2p->sd_peer->sd_pending_bcast_queries == 0)
+			p2p->sd_peer->sd_pending_bcast_queries = -1;
+	}
+
+	/* Wait for response from the peer */
+	p2p_set_state(p2p, P2P_SD_DURING_FIND);
+	p2p_set_timeout(p2p, 0, 200000);
+}
+
+
+/**
+ * p2p_retry_pd - Retry any pending provision disc requests in IDLE state
+ * @p2p: P2P module context from p2p_init()
+ */
+static void p2p_retry_pd(struct p2p_data *p2p)
+{
+	struct p2p_device *dev;
+
+	/*
+	 * Retry the prov disc req attempt only for the peer that the user had
+	 * requested.
+	 */
+
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		if (os_memcmp(p2p->pending_pd_devaddr,
+			      dev->info.p2p_device_addr, ETH_ALEN) != 0)
+			continue;
+		if (!dev->req_config_methods)
+			continue;
+
+		p2p_dbg(p2p, "Send pending Provision Discovery Request to "
+			MACSTR " (config methods 0x%x)",
+			MAC2STR(dev->info.p2p_device_addr),
+			dev->req_config_methods);
+		p2p_send_prov_disc_req(p2p, dev,
+				       dev->flags & P2P_DEV_PD_FOR_JOIN,
+				       p2p->pd_force_freq);
+		return;
+	}
+}
+
+
+static void p2p_prov_disc_cb(struct p2p_data *p2p, int success)
+{
+	p2p_dbg(p2p, "Provision Discovery Request TX callback: success=%d",
+		success);
+
+	/*
+	 * Postpone resetting the pending action state till after we actually
+	 * time out. This allows us to take some action like notifying any
+	 * interested parties about no response to the request.
+	 *
+	 * When the timer (below) goes off we check in IDLE, SEARCH, or
+	 * LISTEN_ONLY state, which are the only allowed states to issue a PD
+	 * requests in, if this was still pending and then raise notification.
+	 */
+
+	if (!success) {
+		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+		if (p2p->user_initiated_pd &&
+		    (p2p->state == P2P_SEARCH || p2p->state == P2P_LISTEN_ONLY))
+		{
+			/* Retry request from timeout to avoid busy loops */
+			p2p->pending_action_state = P2P_PENDING_PD;
+			p2p_set_timeout(p2p, 0, 50000);
+		} else if (p2p->state != P2P_IDLE)
+			p2p_continue_find(p2p);
+		else if (p2p->user_initiated_pd) {
+			p2p->pending_action_state = P2P_PENDING_PD;
+			p2p_set_timeout(p2p, 0, 300000);
+		}
+		return;
+	}
+
+	/*
+	 * This postponing, of resetting pending_action_state, needs to be
+	 * done only for user initiated PD requests and not internal ones.
+	 */
+	if (p2p->user_initiated_pd)
+		p2p->pending_action_state = P2P_PENDING_PD;
+	else
+		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+	/* Wait for response from the peer */
+	if (p2p->state == P2P_SEARCH)
+		p2p_set_state(p2p, P2P_PD_DURING_FIND);
+	p2p_set_timeout(p2p, 0, 200000);
+}
+
+
+static int p2p_check_after_scan_tx_continuation(struct p2p_data *p2p)
+{
+	if (p2p->after_scan_tx_in_progress) {
+		p2p->after_scan_tx_in_progress = 0;
+		if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING &&
+		    p2p_run_after_scan(p2p))
+			return 1;
+		if (p2p->state == P2P_SEARCH) {
+			p2p_dbg(p2p, "Continue find after after_scan_tx completion");
+			p2p_continue_find(p2p);
+		}
+	}
+
+	return 0;
+}
+
+
+static void p2p_prov_disc_resp_cb(struct p2p_data *p2p, int success)
+{
+	p2p_dbg(p2p, "Provision Discovery Response TX callback: success=%d",
+		success);
+
+	if (p2p->send_action_in_progress) {
+		p2p->send_action_in_progress = 0;
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	}
+
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+	if (!success)
+		goto continue_search;
+
+	if (!p2p->cfg->prov_disc_resp_cb ||
+	    p2p->cfg->prov_disc_resp_cb(p2p->cfg->cb_ctx) < 1)
+		goto continue_search;
+
+	p2p_dbg(p2p,
+		"Post-Provision Discovery operations started - do not try to continue other P2P operations");
+	return;
+
+continue_search:
+	p2p_check_after_scan_tx_continuation(p2p);
+}
+
+
+int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
+			 struct os_reltime *rx_time, int level, const u8 *ies,
+			 size_t ies_len)
+{
+	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
+		 * stale information, update P2P peers only based on results
+		 * that have based on frames received after the last p2p_find
+		 * operation was started.
+		 */
+		p2p_dbg(p2p, "Ignore old scan result for " MACSTR
+			" (rx_time=%u.%06u)",
+			MAC2STR(bssid), (unsigned int) rx_time->sec,
+			(unsigned int) rx_time->usec);
+		return 0;
+	}
+
+	p2p_add_device(p2p, bssid, freq, rx_time, level, ies, ies_len, 1);
+
+	return 0;
+}
+
+
+void p2p_scan_res_handled(struct p2p_data *p2p)
+{
+	if (!p2p->p2p_scan_running) {
+		p2p_dbg(p2p, "p2p_scan was not running, but scan results received");
+	}
+	p2p->p2p_scan_running = 0;
+	eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+
+	if (p2p_run_after_scan(p2p))
+		return;
+	if (p2p->state == P2P_SEARCH)
+		p2p_continue_find(p2p);
+}
+
+
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
+{
+	u8 dev_capab;
+	u8 *len;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_probe_req)
+		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);
+
+	dev_capab = p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+
+	/* P2PS requires Probe Request frames to include SD bit */
+	if (p2p->p2ps_seek && p2p->p2ps_seek_count)
+		dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+
+	p2p_buf_add_capability(ies, dev_capab, 0);
+
+	if (dev_id)
+		p2p_buf_add_device_id(ies, dev_id);
+	if (p2p->cfg->reg_class && p2p->cfg->channel)
+		p2p_buf_add_listen_channel(ies, p2p->cfg->country,
+					   p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+	if (p2p->ext_listen_interval)
+		p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period,
+					      p2p->ext_listen_interval);
+
+	if (p2p->p2ps_seek && p2p->p2ps_seek_count)
+		p2p_buf_add_service_hash(ies, p2p);
+
+	/* TODO: p2p_buf_add_operating_channel() if GO */
+	p2p_buf_update_ie_hdr(ies, len);
+}
+
+
+size_t p2p_scan_ie_buf_len(struct p2p_data *p2p)
+{
+	size_t len = 100;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p && p2p->wfd_ie_probe_req)
+		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;
+}
+
+
+int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end)
+{
+	return p2p_attr_text(p2p_ie, buf, end);
+}
+
+
+static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success)
+{
+	struct p2p_device *dev = p2p->go_neg_peer;
+	int timeout;
+
+	p2p_dbg(p2p, "GO Negotiation Request TX callback: success=%d", success);
+
+	if (dev == NULL) {
+		p2p_dbg(p2p, "No pending GO Negotiation");
+		return;
+	}
+
+	if (success) {
+		if (dev->flags & P2P_DEV_USER_REJECTED) {
+			p2p_set_state(p2p, P2P_IDLE);
+			return;
+		}
+	} else if (dev->go_neg_req_sent) {
+		/* Cancel the increment from p2p_connect_send() on failure */
+		dev->go_neg_req_sent--;
+	}
+
+	if (!success &&
+	    (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) &&
+	    !is_zero_ether_addr(dev->member_in_go_dev)) {
+		p2p_dbg(p2p, "Peer " MACSTR " did not acknowledge request - try to use device discoverability through its GO",
+			MAC2STR(dev->info.p2p_device_addr));
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		p2p_send_dev_disc_req(p2p, dev);
+		return;
+	}
+
+	/*
+	 * Use P2P find, if needed, to find the other device from its listen
+	 * channel.
+	 */
+	p2p_set_state(p2p, P2P_CONNECT);
+	timeout = success ? 500000 : 100000;
+	if (!success && p2p->go_neg_peer &&
+	    (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE)) {
+		unsigned int r;
+		/*
+		 * Peer is expected to wait our response and we will skip the
+		 * listen phase. Add some randomness to the wait time here to
+		 * make it less likely to hit cases where we could end up in
+		 * sync with peer not listening.
+		 */
+		if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+			r = 0;
+		timeout += r % 100000;
+	}
+	p2p_set_timeout(p2p, 0, timeout);
+}
+
+
+static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success)
+{
+	p2p_dbg(p2p, "GO Negotiation Response TX callback: success=%d",
+		success);
+	if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) {
+		p2p_dbg(p2p, "Ignore TX callback event - GO Negotiation is not running anymore");
+		return;
+	}
+	p2p_set_state(p2p, P2P_CONNECT);
+	p2p_set_timeout(p2p, 0, 500000);
+}
+
+
+static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success,
+				       const u8 *addr)
+{
+	p2p_dbg(p2p, "GO Negotiation Response (failure) TX callback: success=%d", success);
+	if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) {
+		p2p_go_neg_failed(p2p, p2p->go_neg_peer->status);
+		return;
+	}
+
+	if (success) {
+		struct p2p_device *dev;
+		dev = p2p_get_device(p2p, addr);
+		if (dev &&
+		    dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
+			dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE;
+	}
+
+	if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND)
+		p2p_continue_find(p2p);
+}
+
+
+static void p2p_go_neg_conf_cb(struct p2p_data *p2p,
+			       enum p2p_send_action_result result)
+{
+	struct p2p_device *dev;
+
+	p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result);
+	if (result == P2P_SEND_ACTION_FAILED) {
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		p2p_go_neg_failed(p2p, -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
+		 * immediately after having received the frame, we may not see
+		 * an Ack for retries, so just dropping a single frame may
+		 * trigger this. To allow the group formation to succeed if the
+		 * peer did indeed receive the frame, continue regardless of
+		 * the TX status.
+		 */
+		p2p_dbg(p2p, "Assume GO Negotiation Confirm TX was actually received by the peer even though Ack was not reported");
+	}
+
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
+	if (dev == NULL)
+		return;
+
+	p2p_go_complete(p2p, dev);
+}
+
+
+void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+			const u8 *src, const u8 *bssid,
+			enum p2p_send_action_result result)
+{
+	enum p2p_pending_action_state state;
+	int success;
+
+	p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR
+		" src=" MACSTR " bssid=" MACSTR " result=%d p2p_state=%s)",
+		p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src),
+		MAC2STR(bssid), result, p2p_state_txt(p2p->state));
+	success = result == P2P_SEND_ACTION_SUCCESS;
+	state = p2p->pending_action_state;
+	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);
+		}
+		p2p_check_after_scan_tx_continuation(p2p);
+		break;
+	case P2P_PENDING_GO_NEG_REQUEST:
+		p2p_go_neg_req_cb(p2p, success);
+		break;
+	case P2P_PENDING_GO_NEG_RESPONSE:
+		p2p_go_neg_resp_cb(p2p, success);
+		break;
+	case P2P_PENDING_GO_NEG_RESPONSE_FAILURE:
+		p2p_go_neg_resp_failure_cb(p2p, success, dst);
+		break;
+	case P2P_PENDING_GO_NEG_CONFIRM:
+		p2p_go_neg_conf_cb(p2p, result);
+		break;
+	case P2P_PENDING_SD:
+		p2p_sd_cb(p2p, success);
+		break;
+	case P2P_PENDING_PD:
+		p2p_prov_disc_cb(p2p, success);
+		break;
+	case P2P_PENDING_PD_RESPONSE:
+		p2p_prov_disc_resp_cb(p2p, success);
+		break;
+	case P2P_PENDING_INVITATION_REQUEST:
+		p2p_invitation_req_cb(p2p, success);
+		break;
+	case P2P_PENDING_INVITATION_RESPONSE:
+		p2p_invitation_resp_cb(p2p, success);
+		break;
+	case P2P_PENDING_DEV_DISC_REQUEST:
+		p2p_dev_disc_req_cb(p2p, success);
+		break;
+	case P2P_PENDING_DEV_DISC_RESPONSE:
+		p2p_dev_disc_resp_cb(p2p, success);
+		break;
+	case P2P_PENDING_GO_DISC_REQ:
+		p2p_go_disc_req_cb(p2p, success);
+		break;
+	}
+
+	p2p->after_scan_tx_in_progress = 0;
+}
+
+
+void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
+		   unsigned int duration)
+{
+	if (freq == p2p->pending_client_disc_freq) {
+		p2p_dbg(p2p, "Client discoverability remain-awake completed");
+		p2p->pending_client_disc_freq = 0;
+		return;
+	}
+
+	if (freq != p2p->pending_listen_freq) {
+		p2p_dbg(p2p, "Unexpected listen callback for freq=%u duration=%u (pending_listen_freq=%u)",
+			freq, duration, p2p->pending_listen_freq);
+		return;
+	}
+
+	p2p_dbg(p2p, "Starting Listen timeout(%u,%u) on freq=%u based on callback",
+		p2p->pending_listen_sec, p2p->pending_listen_usec,
+		p2p->pending_listen_freq);
+	p2p->in_listen = 1;
+	p2p->drv_in_listen = freq;
+	if (p2p->pending_listen_sec || p2p->pending_listen_usec) {
+		/*
+		 * Add 20 msec extra wait to avoid race condition with driver
+		 * remain-on-channel end event, i.e., give driver more time to
+		 * complete the operation before our timeout expires.
+		 */
+		p2p_set_timeout(p2p, p2p->pending_listen_sec,
+				p2p->pending_listen_usec + 20000);
+	}
+
+	p2p->pending_listen_freq = 0;
+}
+
+
+int p2p_listen_end(struct p2p_data *p2p, unsigned int freq)
+{
+	p2p_dbg(p2p, "Driver ended Listen state (freq=%u)", freq);
+	p2p->drv_in_listen = 0;
+	if (p2p->in_listen)
+		return 0; /* Internal timeout will trigger the next step */
+
+	if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) {
+		if (p2p->go_neg_peer->connect_reqs >= 120) {
+			p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response");
+			p2p_go_neg_failed(p2p, -1);
+			return 0;
+		}
+
+		p2p_set_state(p2p, P2P_CONNECT);
+		p2p_connect_send(p2p, p2p->go_neg_peer);
+		return 1;
+	} else if (p2p->state == P2P_SEARCH) {
+		if (p2p->p2p_scan_running) {
+			 /*
+			  * Search is already in progress. This can happen if
+			  * an Action frame RX is reported immediately after
+			  * the end of a remain-on-channel operation and the
+			  * response frame to that is sent using an offchannel
+			  * operation while in p2p_find. Avoid an attempt to
+			  * restart a scan here.
+			  */
+			p2p_dbg(p2p, "p2p_scan already in progress - do not try to start a new one");
+			return 1;
+		}
+		if (p2p->pending_listen_freq) {
+			/*
+			 * Better wait a bit if the driver is unable to start
+			 * offchannel operation for some reason. p2p_search()
+			 * will be started from internal timeout.
+			 */
+			p2p_dbg(p2p, "Listen operation did not seem to start - delay search phase to avoid busy loop");
+			p2p_set_timeout(p2p, 0, 100000);
+			return 1;
+		}
+		if (p2p->search_delay) {
+			p2p_dbg(p2p, "Delay search operation by %u ms",
+				p2p->search_delay);
+			p2p_set_timeout(p2p, p2p->search_delay / 1000,
+					(p2p->search_delay % 1000) * 1000);
+			return 1;
+		}
+		p2p_search(p2p);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static void p2p_timeout_connect(struct p2p_data *p2p)
+{
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	if (p2p->go_neg_peer &&
+	    (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
+		p2p_dbg(p2p, "Wait for GO Negotiation Confirm timed out - assume GO Negotiation failed");
+		p2p_go_neg_failed(p2p, -1);
+		return;
+	}
+	if (p2p->go_neg_peer &&
+	    (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE) &&
+	    p2p->go_neg_peer->connect_reqs < 120) {
+		p2p_dbg(p2p, "Peer expected to wait our response - skip listen");
+		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);
+}
+
+
+static void p2p_timeout_connect_listen(struct p2p_data *p2p)
+{
+	if (p2p->go_neg_peer) {
+		if (p2p->drv_in_listen) {
+			p2p_dbg(p2p, "Driver is still in Listen state; wait for it to complete");
+			return;
+		}
+
+		if (p2p->go_neg_peer->connect_reqs >= 120) {
+			p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response");
+			p2p_go_neg_failed(p2p, -1);
+			return;
+		}
+
+		p2p_set_state(p2p, P2P_CONNECT);
+		p2p_connect_send(p2p, p2p->go_neg_peer);
+	} else
+		p2p_set_state(p2p, P2P_IDLE);
+}
+
+
+static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p)
+{
+	p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
+
+	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;
+
+	if (dev == NULL) {
+		p2p_dbg(p2p, "Unknown GO Neg peer - stop GO Neg wait");
+		return;
+	}
+
+	p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation");
+	p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
+	p2p_listen_in_find(p2p, 0);
+}
+
+
+static void p2p_timeout_sd_during_find(struct p2p_data *p2p)
+{
+	p2p_dbg(p2p, "Service Discovery Query timeout");
+	if (p2p->sd_peer) {
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		p2p->sd_peer = NULL;
+	}
+	p2p_continue_find(p2p);
+}
+
+
+static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p)
+{
+	p2p_dbg(p2p, "Provision Discovery Request timeout");
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	p2p_continue_find(p2p);
+}
+
+
+static void p2p_timeout_prov_disc_req(struct p2p_data *p2p)
+{
+	u32 adv_id = 0;
+	u8 *adv_mac = NULL;
+
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+	/*
+	 * For user initiated PD requests that we have not gotten any responses
+	 * for while in IDLE state, we retry them a couple of times before
+	 * giving up.
+	 */
+	if (!p2p->user_initiated_pd)
+		return;
+
+	p2p_dbg(p2p, "User initiated Provision Discovery Request timeout");
+
+	if (p2p->pd_retries) {
+		p2p->pd_retries--;
+		p2p_retry_pd(p2p);
+	} else {
+		struct p2p_device *dev;
+		int for_join = 0;
+
+		dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+			if (os_memcmp(p2p->pending_pd_devaddr,
+				      dev->info.p2p_device_addr, ETH_ALEN) != 0)
+				continue;
+			if (dev->req_config_methods &&
+			    (dev->flags & P2P_DEV_PD_FOR_JOIN))
+				for_join = 1;
+		}
+
+		if (p2p->p2ps_prov) {
+			adv_id = p2p->p2ps_prov->adv_id;
+			adv_mac = p2p->p2ps_prov->adv_mac;
+		}
+
+		if (p2p->cfg->prov_disc_fail)
+			p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx,
+						 p2p->pending_pd_devaddr,
+						 for_join ?
+						 P2P_PROV_DISC_TIMEOUT_JOIN :
+						 P2P_PROV_DISC_TIMEOUT,
+						 adv_id, adv_mac, NULL);
+		p2p_reset_pending_pd(p2p);
+	}
+}
+
+
+static void p2p_timeout_invite(struct p2p_data *p2p)
+{
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	p2p_set_state(p2p, P2P_INVITE_LISTEN);
+	if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) {
+		/*
+		 * Better remain on operating channel instead of listen channel
+		 * when running a group.
+		 */
+		p2p_dbg(p2p, "Inviting in active GO role - wait on operating channel");
+		p2p_set_timeout(p2p, 0, 100000);
+		return;
+	}
+	p2p_listen_in_find(p2p, 0);
+}
+
+
+static void p2p_timeout_invite_listen(struct p2p_data *p2p)
+{
+	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_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,
+					0, 0);
+		}
+		p2p_set_state(p2p, P2P_IDLE);
+	}
+}
+
+
+static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+
+	p2p_dbg(p2p, "Timeout (state=%s)", p2p_state_txt(p2p->state));
+
+	p2p->in_listen = 0;
+	if (p2p->drv_in_listen) {
+		p2p_dbg(p2p, "Driver is still in listen state - stop it");
+		p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+	}
+
+	switch (p2p->state) {
+	case P2P_IDLE:
+		/* Check if we timed out waiting for PD req */
+		if (p2p->pending_action_state == P2P_PENDING_PD)
+			p2p_timeout_prov_disc_req(p2p);
+		break;
+	case P2P_SEARCH:
+		/* Check if we timed out waiting for PD req */
+		if (p2p->pending_action_state == P2P_PENDING_PD)
+			p2p_timeout_prov_disc_req(p2p);
+		if (p2p->search_delay && !p2p->in_search_delay) {
+			p2p_dbg(p2p, "Delay search operation by %u ms",
+				p2p->search_delay);
+			p2p->in_search_delay = 1;
+			p2p_set_timeout(p2p, p2p->search_delay / 1000,
+					(p2p->search_delay % 1000) * 1000);
+			break;
+		}
+		p2p->in_search_delay = 0;
+		p2p_search(p2p);
+		break;
+	case P2P_CONNECT:
+		p2p_timeout_connect(p2p);
+		break;
+	case P2P_CONNECT_LISTEN:
+		p2p_timeout_connect_listen(p2p);
+		break;
+	case P2P_GO_NEG:
+		break;
+	case P2P_LISTEN_ONLY:
+		/* Check if we timed out waiting for PD req */
+		if (p2p->pending_action_state == P2P_PENDING_PD)
+			p2p_timeout_prov_disc_req(p2p);
+
+		if (p2p->ext_listen_only) {
+			p2p_dbg(p2p, "Extended Listen Timing - Listen State completed");
+			p2p->ext_listen_only = 0;
+			p2p_set_state(p2p, P2P_IDLE);
+		}
+		break;
+	case P2P_WAIT_PEER_CONNECT:
+		p2p_timeout_wait_peer_connect(p2p);
+		break;
+	case P2P_WAIT_PEER_IDLE:
+		p2p_timeout_wait_peer_idle(p2p);
+		break;
+	case P2P_SD_DURING_FIND:
+		p2p_timeout_sd_during_find(p2p);
+		break;
+	case P2P_PROVISIONING:
+		break;
+	case P2P_PD_DURING_FIND:
+		p2p_timeout_prov_disc_during_find(p2p);
+		break;
+	case P2P_INVITE:
+		p2p_timeout_invite(p2p);
+		break;
+	case P2P_INVITE_LISTEN:
+		p2p_timeout_invite_listen(p2p);
+		break;
+	}
+}
+
+
+int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr)
+{
+	struct p2p_device *dev;
+
+	dev = p2p_get_device(p2p, peer_addr);
+	p2p_dbg(p2p, "Local request to reject connection attempts by peer "
+		MACSTR, MAC2STR(peer_addr));
+	if (dev == NULL) {
+		p2p_dbg(p2p, "Peer " MACSTR " unknown", MAC2STR(peer_addr));
+		return -1;
+	}
+	dev->status = P2P_SC_FAIL_REJECTED_BY_USER;
+	dev->flags |= P2P_DEV_USER_REJECTED;
+	return 0;
+}
+
+
+const char * p2p_wps_method_text(enum p2p_wps_method method)
+{
+	switch (method) {
+	case WPS_NOT_READY:
+		return "not-ready";
+	case WPS_PIN_DISPLAY:
+		return "Display";
+	case WPS_PIN_KEYPAD:
+		return "Keypad";
+	case WPS_PBC:
+		return "PBC";
+	case WPS_NFC:
+		return "NFC";
+	case WPS_P2PS:
+		return "P2PS";
+	}
+
+	return "??";
+}
+
+
+static const char * p2p_go_state_text(enum p2p_go_state go_state)
+{
+	switch (go_state) {
+	case UNKNOWN_GO:
+		return "unknown";
+	case LOCAL_GO:
+		return "local";
+	case  REMOTE_GO:
+		return "remote";
+	}
+
+	return "??";
+}
+
+
+const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p,
+					       const u8 *addr, int next)
+{
+	struct p2p_device *dev;
+
+	if (addr)
+		dev = p2p_get_device(p2p, addr);
+	else
+		dev = dl_list_first(&p2p->devices, struct p2p_device, list);
+
+	if (dev && next) {
+		dev = dl_list_first(&dev->list, struct p2p_device, list);
+		if (&dev->list == &p2p->devices)
+			dev = NULL;
+	}
+
+	if (dev == NULL)
+		return NULL;
+
+	return &dev->info;
+}
+
+
+int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
+			  char *buf, size_t buflen)
+{
+	struct p2p_device *dev;
+	int res;
+	char *pos, *end;
+	struct os_reltime now;
+
+	if (info == NULL)
+		return -1;
+
+	dev = (struct p2p_device *) (((u8 *) info) -
+				     offsetof(struct p2p_device, info));
+
+	pos = buf;
+	end = buf + buflen;
+
+	os_get_reltime(&now);
+	res = os_snprintf(pos, end - pos,
+			  "age=%d\n"
+			  "listen_freq=%d\n"
+			  "wps_method=%s\n"
+			  "interface_addr=" MACSTR "\n"
+			  "member_in_go_dev=" MACSTR "\n"
+			  "member_in_go_iface=" MACSTR "\n"
+			  "go_neg_req_sent=%d\n"
+			  "go_state=%s\n"
+			  "dialog_token=%u\n"
+			  "intended_addr=" MACSTR "\n"
+			  "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"
+			  "status=%d\n"
+			  "invitation_reqs=%u\n",
+			  (int) (now.sec - dev->last_seen.sec),
+			  dev->listen_freq,
+			  p2p_wps_method_text(dev->wps_method),
+			  MAC2STR(dev->interface_addr),
+			  MAC2STR(dev->member_in_go_dev),
+			  MAC2STR(dev->member_in_go_iface),
+			  dev->go_neg_req_sent,
+			  p2p_go_state_text(dev->go_state),
+			  dev->dialog_token,
+			  MAC2STR(dev->intended_addr),
+			  dev->country[0] ? dev->country[0] : '_',
+			  dev->country[1] ? dev->country[1] : '_',
+			  dev->oper_freq,
+			  dev->req_config_methods,
+			  dev->flags & P2P_DEV_PROBE_REQ_ONLY ?
+			  "[PROBE_REQ_ONLY]" : "",
+			  dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "",
+			  dev->flags & P2P_DEV_NOT_YET_READY ?
+			  "[NOT_YET_READY]" : "",
+			  dev->flags & P2P_DEV_PD_PEER_DISPLAY ?
+			  "[PD_PEER_DISPLAY]" : "",
+			  dev->flags & P2P_DEV_PD_PEER_KEYPAD ?
+			  "[PD_PEER_KEYPAD]" : "",
+			  dev->flags & P2P_DEV_PD_PEER_P2PS ?
+			  "[PD_PEER_P2PS]" : "",
+			  dev->flags & P2P_DEV_USER_REJECTED ?
+			  "[USER_REJECTED]" : "",
+			  dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ?
+			  "[PEER_WAITING_RESPONSE]" : "",
+			  dev->flags & P2P_DEV_PREFER_PERSISTENT_GROUP ?
+			  "[PREFER_PERSISTENT_GROUP]" : "",
+			  dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE ?
+			  "[WAIT_GO_NEG_RESPONSE]" : "",
+			  dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM ?
+			  "[WAIT_GO_NEG_CONFIRM]" : "",
+			  dev->flags & P2P_DEV_GROUP_CLIENT_ONLY ?
+			  "[GROUP_CLIENT_ONLY]" : "",
+			  dev->flags & P2P_DEV_FORCE_FREQ ?
+			  "[FORCE_FREQ]" : "",
+			  dev->flags & P2P_DEV_PD_FOR_JOIN ?
+			  "[PD_FOR_JOIN]" : "",
+			  dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT ?
+			  "[LAST_SEEN_AS_GROUP_CLIENT]" : "",
+			  dev->status,
+			  dev->invitation_reqs);
+	if (os_snprintf_error(end - pos, res))
+		return pos - buf;
+	pos += res;
+
+	if (dev->ext_listen_period) {
+		res = os_snprintf(pos, end - pos,
+				  "ext_listen_period=%u\n"
+				  "ext_listen_interval=%u\n",
+				  dev->ext_listen_period,
+				  dev->ext_listen_interval);
+		if (os_snprintf_error(end - pos, res))
+			return pos - buf;
+		pos += res;
+	}
+
+	if (dev->oper_ssid_len) {
+		res = os_snprintf(pos, end - pos,
+				  "oper_ssid=%s\n",
+				  wpa_ssid_txt(dev->oper_ssid,
+					       dev->oper_ssid_len));
+		if (os_snprintf_error(end - pos, res))
+			return pos - buf;
+		pos += res;
+	}
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (dev->info.wfd_subelems) {
+		res = os_snprintf(pos, end - pos, "wfd_subelems=");
+		if (os_snprintf_error(end - pos, res))
+			return pos - buf;
+		pos += res;
+
+		pos += wpa_snprintf_hex(pos, end - pos,
+					wpabuf_head(dev->info.wfd_subelems),
+					wpabuf_len(dev->info.wfd_subelems));
+
+		res = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, res))
+			return pos - buf;
+		pos += res;
+	}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	return pos - buf;
+}
+
+
+int p2p_peer_known(struct p2p_data *p2p, const u8 *addr)
+{
+	return p2p_get_device(p2p, addr) != NULL;
+}
+
+
+void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled)
+{
+	if (enabled) {
+		p2p_dbg(p2p, "Client discoverability enabled");
+		p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+	} else {
+		p2p_dbg(p2p, "Client discoverability disabled");
+		p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+	}
+}
+
+
+static struct wpabuf * p2p_build_presence_req(u32 duration1, u32 interval1,
+					      u32 duration2, u32 interval2)
+{
+	struct wpabuf *req;
+	struct p2p_noa_desc desc1, desc2, *ptr1 = NULL, *ptr2 = NULL;
+	u8 *len;
+
+	req = wpabuf_alloc(100);
+	if (req == NULL)
+		return NULL;
+
+	if (duration1 || interval1) {
+		os_memset(&desc1, 0, sizeof(desc1));
+		desc1.count_type = 1;
+		desc1.duration = duration1;
+		desc1.interval = interval1;
+		ptr1 = &desc1;
+
+		if (duration2 || interval2) {
+			os_memset(&desc2, 0, sizeof(desc2));
+			desc2.count_type = 2;
+			desc2.duration = duration2;
+			desc2.interval = interval2;
+			ptr2 = &desc2;
+		}
+	}
+
+	p2p_buf_add_action_hdr(req, P2P_PRESENCE_REQ, 1);
+	len = p2p_buf_add_ie_hdr(req);
+	p2p_buf_add_noa(req, 0, 0, 0, ptr1, ptr2);
+	p2p_buf_update_ie_hdr(req, len);
+
+	return req;
+}
+
+
+int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr,
+		     const u8 *own_interface_addr, unsigned int freq,
+		     u32 duration1, u32 interval1, u32 duration2,
+		     u32 interval2)
+{
+	struct wpabuf *req;
+
+	p2p_dbg(p2p, "Send Presence Request to GO " MACSTR
+		" (own interface " MACSTR ") freq=%u dur1=%u int1=%u "
+		"dur2=%u int2=%u",
+		MAC2STR(go_interface_addr), MAC2STR(own_interface_addr),
+		freq, duration1, interval1, duration2, interval2);
+
+	req = p2p_build_presence_req(duration1, interval1, duration2,
+				     interval2);
+	if (req == NULL)
+		return -1;
+
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	if (p2p_send_action(p2p, freq, go_interface_addr, own_interface_addr,
+			    go_interface_addr,
+			    wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+		p2p_dbg(p2p, "Failed to send Action frame");
+	}
+	wpabuf_free(req);
+
+	return 0;
+}
+
+
+static struct wpabuf * p2p_build_presence_resp(u8 status, const u8 *noa,
+					       size_t noa_len, u8 dialog_token)
+{
+	struct wpabuf *resp;
+	u8 *len;
+
+	resp = wpabuf_alloc(100 + noa_len);
+	if (resp == NULL)
+		return NULL;
+
+	p2p_buf_add_action_hdr(resp, P2P_PRESENCE_RESP, dialog_token);
+	len = p2p_buf_add_ie_hdr(resp);
+	p2p_buf_add_status(resp, status);
+	if (noa) {
+		wpabuf_put_u8(resp, P2P_ATTR_NOTICE_OF_ABSENCE);
+		wpabuf_put_le16(resp, noa_len);
+		wpabuf_put_data(resp, noa, noa_len);
+	} else
+		p2p_buf_add_noa(resp, 0, 0, 0, NULL, NULL);
+	p2p_buf_update_ie_hdr(resp, len);
+
+	return resp;
+}
+
+
+static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da,
+				     const u8 *sa, const u8 *data, size_t len,
+				     int rx_freq)
+{
+	struct p2p_message msg;
+	u8 status;
+	struct wpabuf *resp;
+	size_t g;
+	struct p2p_group *group = NULL;
+	int parsed = 0;
+	u8 noa[50];
+	int noa_len;
+
+	p2p_dbg(p2p, "Received P2P Action - P2P Presence Request");
+
+	for (g = 0; g < p2p->num_groups; g++) {
+		if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]),
+			      ETH_ALEN) == 0) {
+			group = p2p->groups[g];
+			break;
+		}
+	}
+	if (group == NULL) {
+		p2p_dbg(p2p, "Ignore P2P Presence Request for unknown group "
+			MACSTR, MAC2STR(da));
+		return;
+	}
+
+	if (p2p_parse(data, len, &msg) < 0) {
+		p2p_dbg(p2p, "Failed to parse P2P Presence Request");
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+	parsed = 1;
+
+	if (msg.noa == NULL) {
+		p2p_dbg(p2p, "No NoA attribute in P2P Presence Request");
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+
+	status = p2p_group_presence_req(group, sa, msg.noa, msg.noa_len);
+
+fail:
+	if (p2p->cfg->get_noa)
+		noa_len = p2p->cfg->get_noa(p2p->cfg->cb_ctx, da, noa,
+					    sizeof(noa));
+	else
+		noa_len = -1;
+	resp = p2p_build_presence_resp(status, noa_len > 0 ? noa : NULL,
+				       noa_len > 0 ? noa_len : 0,
+				       msg.dialog_token);
+	if (parsed)
+		p2p_parse_free(&msg);
+	if (resp == NULL)
+		return;
+
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	if (p2p_send_action(p2p, rx_freq, sa, da, da,
+			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+		p2p_dbg(p2p, "Failed to send Action frame");
+	}
+	wpabuf_free(resp);
+}
+
+
+static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
+				      const u8 *sa, const u8 *data, size_t len)
+{
+	struct p2p_message msg;
+
+	p2p_dbg(p2p, "Received P2P Action - P2P Presence Response");
+
+	if (p2p_parse(data, len, &msg) < 0) {
+		p2p_dbg(p2p, "Failed to parse P2P Presence Response");
+		return;
+	}
+
+	if (msg.status == NULL || msg.noa == NULL) {
+		p2p_dbg(p2p, "No Status or NoA attribute in P2P Presence Response");
+		p2p_parse_free(&msg);
+		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);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	p2p_dbg(p2p, "P2P Presence Request was accepted");
+	wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA",
+		    msg.noa, msg.noa_len);
+	/* TODO: process NoA */
+	p2p_parse_free(&msg);
+}
+
+
+static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+
+	if (p2p->ext_listen_interval) {
+		/* Schedule next extended listen timeout */
+		eloop_register_timeout(p2p->ext_listen_interval_sec,
+				       p2p->ext_listen_interval_usec,
+				       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
+		 * command may fail is something else (e.g., a scan) was
+		 * running at an inconvenient time. As a workaround, allow new
+		 * Extended Listen operation to be started.
+		 */
+		p2p_dbg(p2p, "Previous Extended Listen operation had not been completed - try again");
+		p2p->ext_listen_only = 0;
+		p2p_set_state(p2p, P2P_IDLE);
+	}
+
+	if (p2p->state != P2P_IDLE) {
+		p2p_dbg(p2p, "Skip Extended Listen timeout in active state (%s)", p2p_state_txt(p2p->state));
+		return;
+	}
+
+	p2p_dbg(p2p, "Extended Listen timeout");
+	p2p->ext_listen_only = 1;
+	if (p2p_listen(p2p, p2p->ext_listen_period) < 0) {
+		p2p_dbg(p2p, "Failed to start Listen state for Extended Listen Timing");
+		p2p->ext_listen_only = 0;
+	}
+}
+
+
+int p2p_ext_listen(struct p2p_data *p2p, unsigned int period,
+		   unsigned int interval)
+{
+	if (period > 65535 || interval > 65535 || period > interval ||
+	    (period == 0 && interval > 0) || (period > 0 && interval == 0)) {
+		p2p_dbg(p2p, "Invalid Extended Listen Timing request: period=%u interval=%u",
+			period, interval);
+		return -1;
+	}
+
+	eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
+
+	if (interval == 0) {
+		p2p_dbg(p2p, "Disabling Extended Listen Timing");
+		p2p->ext_listen_period = 0;
+		p2p->ext_listen_interval = 0;
+		return 0;
+	}
+
+	p2p_dbg(p2p, "Enabling Extended Listen Timing: period %u msec, interval %u msec",
+		period, interval);
+	p2p->ext_listen_period = period;
+	p2p->ext_listen_interval = interval;
+	p2p->ext_listen_interval_sec = interval / 1000;
+	p2p->ext_listen_interval_usec = (interval % 1000) * 1000;
+
+	eloop_register_timeout(p2p->ext_listen_interval_sec,
+			       p2p->ext_listen_interval_usec,
+			       p2p_ext_listen_timeout, p2p, NULL);
+
+	return 0;
+}
+
+
+void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+		      const u8 *ie, size_t ie_len)
+{
+	struct p2p_message msg;
+
+	if (bssid == NULL || ie == NULL)
+		return;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_ies(ie, ie_len, &msg))
+		return;
+	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",
+		MAC2STR(bssid), reason_code, *msg.minor_reason_code);
+
+	p2p_parse_free(&msg);
+}
+
+
+void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+			const u8 *ie, size_t ie_len)
+{
+	struct p2p_message msg;
+
+	if (bssid == NULL || ie == NULL)
+		return;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_ies(ie, ie_len, &msg))
+		return;
+	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",
+		MAC2STR(bssid), reason_code, *msg.minor_reason_code);
+
+	p2p_parse_free(&msg);
+}
+
+
+void p2p_set_managed_oper(struct p2p_data *p2p, int enabled)
+{
+	if (enabled) {
+		p2p_dbg(p2p, "Managed P2P Device operations enabled");
+		p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED;
+	} else {
+		p2p_dbg(p2p, "Managed P2P Device operations disabled");
+		p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED;
+	}
+}
+
+
+int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class,
+				 u8 *op_channel)
+{
+	return p2p_channel_random_social(&p2p->channels, op_class, op_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;
+
+	/*
+	 * Listen channel was set in configuration or set by control interface;
+	 * cannot override it.
+	 */
+	if (p2p->cfg->channel_forced && forced == 0) {
+		p2p_dbg(p2p,
+			"Listen channel was previously configured - do not override based on optimization");
+		return -1;
+	}
+
+	p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u",
+		reg_class, channel);
+
+	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));
+	if (postfix == NULL) {
+		p2p->cfg->ssid_postfix_len = 0;
+		return 0;
+	}
+	if (len > sizeof(p2p->cfg->ssid_postfix))
+		return -1;
+	os_memcpy(p2p->cfg->ssid_postfix, postfix, len);
+	p2p->cfg->ssid_postfix_len = len;
+	return 0;
+}
+
+
+int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
+			 int cfg_op_channel)
+{
+	if (p2p_channel_to_freq(op_reg_class, op_channel) < 0)
+		return -1;
+
+	p2p_dbg(p2p, "Set Operating channel: reg_class %u channel %u",
+		op_reg_class, op_channel);
+	p2p->cfg->op_reg_class = op_reg_class;
+	p2p->cfg->op_channel = op_channel;
+	p2p->cfg->cfg_op_channel = cfg_op_channel;
+	return 0;
+}
+
+
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+		      const struct p2p_channel *pref_chan)
+{
+	struct p2p_channel *n;
+
+	if (pref_chan) {
+		n = os_malloc(num_pref_chan * sizeof(struct p2p_channel));
+		if (n == NULL)
+			return -1;
+		os_memcpy(n, pref_chan,
+			  num_pref_chan * sizeof(struct p2p_channel));
+	} else
+		n = NULL;
+
+	os_free(p2p->cfg->pref_chan);
+	p2p->cfg->pref_chan = n;
+	p2p->cfg->num_pref_chan = num_pref_chan;
+
+	return 0;
+}
+
+
+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)
+{
+	struct p2p_device *dev = p2p_get_device(p2p, dev_addr);
+	if (dev == NULL || is_zero_ether_addr(dev->interface_addr))
+		return -1;
+	os_memcpy(iface_addr, dev->interface_addr, ETH_ALEN);
+	return 0;
+}
+
+
+int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
+			   u8 *dev_addr)
+{
+	struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr);
+	if (dev == NULL)
+		return -1;
+	os_memcpy(dev_addr, dev->info.p2p_device_addr, ETH_ALEN);
+	return 0;
+}
+
+
+void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr)
+{
+	os_memcpy(p2p->peer_filter, addr, ETH_ALEN);
+	if (is_zero_ether_addr(p2p->peer_filter))
+		p2p_dbg(p2p, "Disable peer filter");
+	else
+		p2p_dbg(p2p, "Enable peer filter for " MACSTR,
+			MAC2STR(p2p->peer_filter));
+}
+
+
+void p2p_set_cross_connect(struct p2p_data *p2p, int enabled)
+{
+	p2p_dbg(p2p, "Cross connection %s", enabled ? "enabled" : "disabled");
+	if (p2p->cross_connect == enabled)
+		return;
+	p2p->cross_connect = enabled;
+	/* TODO: may need to tear down any action group where we are GO(?) */
+}
+
+
+int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr)
+{
+	struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr);
+	if (dev == NULL)
+		return -1;
+	if (dev->oper_freq <= 0)
+		return -1;
+	return dev->oper_freq;
+}
+
+
+void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled)
+{
+	p2p_dbg(p2p, "Intra BSS distribution %s",
+		enabled ? "enabled" : "disabled");
+	p2p->cfg->p2p_intra_bss = enabled;
+}
+
+
+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);
+}
+
+
+int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+		    const u8 *src, const u8 *bssid, const u8 *buf,
+		    size_t len, unsigned int wait_time)
+{
+	if (p2p->p2p_scan_running) {
+		p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes");
+		if (p2p->after_scan_tx) {
+			p2p_dbg(p2p, "Dropped previous pending Action frame TX");
+			os_free(p2p->after_scan_tx);
+		}
+		p2p->after_scan_tx = os_malloc(sizeof(*p2p->after_scan_tx) +
+					       len);
+		if (p2p->after_scan_tx == NULL)
+			return -1;
+		p2p->after_scan_tx->freq = freq;
+		os_memcpy(p2p->after_scan_tx->dst, dst, ETH_ALEN);
+		os_memcpy(p2p->after_scan_tx->src, src, ETH_ALEN);
+		os_memcpy(p2p->after_scan_tx->bssid, bssid, ETH_ALEN);
+		p2p->after_scan_tx->len = len;
+		p2p->after_scan_tx->wait_time = wait_time;
+		os_memcpy(p2p->after_scan_tx + 1, buf, len);
+		return 0;
+	}
+
+	return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid,
+				     buf, len, wait_time);
+}
+
+
+void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5,
+			   int freq_overall)
+{
+	p2p_dbg(p2p, "Best channel: 2.4 GHz: %d,  5 GHz: %d,  overall: %d",
+		freq_24, freq_5, freq_overall);
+	p2p->best_freq_24 = freq_24;
+	p2p->best_freq_5 = freq_5;
+	p2p->best_freq_overall = freq_overall;
+}
+
+
+void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq)
+{
+	p2p_dbg(p2p, "Own frequency preference: %d MHz", freq);
+	p2p->own_freq_preference = freq;
+}
+
+
+const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p)
+{
+	if (p2p == NULL || p2p->go_neg_peer == NULL)
+		return NULL;
+	return p2p->go_neg_peer->info.p2p_device_addr;
+}
+
+
+const struct p2p_peer_info *
+p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next)
+{
+	struct p2p_device *dev;
+
+	if (addr) {
+		dev = p2p_get_device(p2p, addr);
+		if (!dev)
+			return NULL;
+
+		if (!next) {
+			if (dev->flags & P2P_DEV_PROBE_REQ_ONLY)
+				return NULL;
+
+			return &dev->info;
+		} else {
+			do {
+				dev = dl_list_first(&dev->list,
+						    struct p2p_device,
+						    list);
+				if (!dev || &dev->list == &p2p->devices)
+					return NULL;
+			} while (dev->flags & P2P_DEV_PROBE_REQ_ONLY);
+		}
+	} else {
+		dev = dl_list_first(&p2p->devices, struct p2p_device, list);
+		if (!dev)
+			return NULL;
+		while (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+			dev = dl_list_first(&dev->list,
+					    struct p2p_device,
+					    list);
+			if (!dev || &dev->list == &p2p->devices)
+				return NULL;
+		}
+	}
+
+	return &dev->info;
+}
+
+
+int p2p_in_progress(struct p2p_data *p2p)
+{
+	if (p2p == NULL)
+		return 0;
+	if (p2p->state == P2P_SEARCH)
+		return 2;
+	return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING;
+}
+
+
+void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
+			    u8 client_timeout)
+{
+	if (p2p) {
+		p2p->go_timeout = go_timeout;
+		p2p->client_timeout = client_timeout;
+	}
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+static void p2p_update_wfd_ie_groups(struct p2p_data *p2p)
+{
+	size_t g;
+	struct p2p_group *group;
+
+	for (g = 0; g < p2p->num_groups; g++) {
+		group = p2p->groups[g];
+		p2p_group_force_beacon_update_ies(group);
+	}
+}
+
+
+int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_beacon);
+	p2p->wfd_ie_beacon = ie;
+	p2p_update_wfd_ie_groups(p2p);
+	return 0;
+}
+
+
+int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_probe_req);
+	p2p->wfd_ie_probe_req = ie;
+	return 0;
+}
+
+
+int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_probe_resp);
+	p2p->wfd_ie_probe_resp = ie;
+	p2p_update_wfd_ie_groups(p2p);
+	return 0;
+}
+
+
+int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_assoc_req);
+	p2p->wfd_ie_assoc_req = ie;
+	return 0;
+}
+
+
+int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_invitation);
+	p2p->wfd_ie_invitation = ie;
+	return 0;
+}
+
+
+int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_prov_disc_req);
+	p2p->wfd_ie_prov_disc_req = ie;
+	return 0;
+}
+
+
+int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_prov_disc_resp);
+	p2p->wfd_ie_prov_disc_resp = ie;
+	return 0;
+}
+
+
+int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_go_neg);
+	p2p->wfd_ie_go_neg = ie;
+	return 0;
+}
+
+
+int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem)
+{
+	wpabuf_free(p2p->wfd_dev_info);
+	if (elem) {
+		p2p->wfd_dev_info = wpabuf_dup(elem);
+		if (p2p->wfd_dev_info == NULL)
+			return -1;
+	} else
+		p2p->wfd_dev_info = NULL;
+
+	return 0;
+}
+
+
+int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem)
+{
+	wpabuf_free(p2p->wfd_assoc_bssid);
+	if (elem) {
+		p2p->wfd_assoc_bssid = wpabuf_dup(elem);
+		if (p2p->wfd_assoc_bssid == NULL)
+			return -1;
+	} else
+		p2p->wfd_assoc_bssid = NULL;
+
+	return 0;
+}
+
+
+int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
+				  const struct wpabuf *elem)
+{
+	wpabuf_free(p2p->wfd_coupled_sink_info);
+	if (elem) {
+		p2p->wfd_coupled_sink_info = wpabuf_dup(elem);
+		if (p2p->wfd_coupled_sink_info == NULL)
+			return -1;
+	} else
+		p2p->wfd_coupled_sink_info = NULL;
+
+	return 0;
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int,
+		     int max_disc_tu)
+{
+	if (min_disc_int > max_disc_int || min_disc_int < 0 || max_disc_int < 0)
+		return -1;
+
+	p2p->min_disc_int = min_disc_int;
+	p2p->max_disc_int = max_disc_int;
+	p2p->max_disc_tu = max_disc_tu;
+	p2p_dbg(p2p, "Set discoverable interval: min=%d max=%d max_tu=%d",
+		min_disc_int, max_disc_int, max_disc_tu);
+
+	return 0;
+}
+
+
+void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
+{
+	va_list ap;
+	char buf[500];
+
+	if (!p2p->cfg->debug_print)
+		return;
+
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	buf[sizeof(buf) - 1] = '\0';
+	va_end(ap);
+	p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_DEBUG, buf);
+}
+
+
+void p2p_info(struct p2p_data *p2p, const char *fmt, ...)
+{
+	va_list ap;
+	char buf[500];
+
+	if (!p2p->cfg->debug_print)
+		return;
+
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	buf[sizeof(buf) - 1] = '\0';
+	va_end(ap);
+	p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_INFO, buf);
+}
+
+
+void p2p_err(struct p2p_data *p2p, const char *fmt, ...)
+{
+	va_list ap;
+	char buf[500];
+
+	if (!p2p->cfg->debug_print)
+		return;
+
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	buf[sizeof(buf) - 1] = '\0';
+	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");
+		p2p_parse_free(&msg);
+		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");
+		p2p_parse_free(&msg);
+		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");
+			p2p_parse_free(&msg);
+			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;
+}
+
+
+void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+
+	p2p_dbg(p2p,
+		"Timeout on waiting peer to become ready for GO Negotiation");
+	p2p_go_neg_failed(p2p, -1);
+}
+
+
+void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
+				const unsigned int *pref_freq_list,
+				unsigned int size)
+{
+	unsigned int i;
+
+	if (size > P2P_MAX_PREF_CHANNELS)
+		size = P2P_MAX_PREF_CHANNELS;
+	p2p->num_pref_freq = size;
+	for (i = 0; i < size; i++) {
+		p2p->pref_freq_list[i] = pref_freq_list[i];
+		p2p_dbg(p2p, "Own preferred frequency list[%u]=%u MHz",
+			i, p2p->pref_freq_list[i]);
+	}
+}
diff --git a/hostap/src/p2p/p2p.h b/hostap/src/p2p/p2p.h
new file mode 100644
index 0000000..b4060be
--- /dev/null
+++ b/hostap/src/p2p/p2p.h
@@ -0,0 +1,2343 @@
+/*
+ * Wi-Fi Direct - P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef P2P_H
+#define P2P_H
+
+#include "common/ieee802_11_defs.h"
+#include "wps/wps.h"
+
+/* P2P ASP Setup Capability */
+#define P2PS_SETUP_NONE 0
+#define P2PS_SETUP_NEW BIT(0)
+#define P2PS_SETUP_CLIENT BIT(1)
+#define P2PS_SETUP_GROUP_OWNER BIT(2)
+
+#define P2PS_WILD_HASH_STR "org.wi-fi.wfds"
+#define P2PS_HASH_LEN 6
+#define P2P_MAX_QUERY_HASH 6
+#define P2PS_FEATURE_CAPAB_CPT_MAX 2
+
+/**
+ * P2P_MAX_PREF_CHANNELS - Maximum number of preferred channels
+ */
+#define P2P_MAX_PREF_CHANNELS 100
+
+/**
+ * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
+ */
+#define P2P_MAX_REG_CLASSES 10
+
+/**
+ * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class
+ */
+#define P2P_MAX_REG_CLASS_CHANNELS 20
+
+/**
+ * struct p2p_channels - List of supported channels
+ */
+struct p2p_channels {
+	/**
+	 * struct p2p_reg_class - Supported regulatory class
+	 */
+	struct p2p_reg_class {
+		/**
+		 * reg_class - Regulatory class (IEEE 802.11-2007, Annex J)
+		 */
+		u8 reg_class;
+
+		/**
+		 * channel - Supported channels
+		 */
+		u8 channel[P2P_MAX_REG_CLASS_CHANNELS];
+
+		/**
+		 * channels - Number of channel entries in use
+		 */
+		size_t channels;
+	} reg_class[P2P_MAX_REG_CLASSES];
+
+	/**
+	 * reg_classes - Number of reg_class entries in use
+	 */
+	size_t reg_classes;
+};
+
+enum p2p_wps_method {
+	WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC,
+	WPS_P2PS
+};
+
+/**
+ * struct p2p_go_neg_results - P2P Group Owner Negotiation results
+ */
+struct p2p_go_neg_results {
+	/**
+	 * status - Negotiation result (Status Code)
+	 *
+	 * 0 (P2P_SC_SUCCESS) indicates success. Non-zero values indicate
+	 * failed negotiation.
+	 */
+	int status;
+
+	/**
+	 * role_go - Whether local end is Group Owner
+	 */
+	int role_go;
+
+	/**
+	 * freq - Frequency of the group operational channel in MHz
+	 */
+	int freq;
+
+	int ht40;
+
+	int vht;
+
+	/**
+	 * ssid - SSID of the group
+	 */
+	u8 ssid[SSID_MAX_LEN];
+
+	/**
+	 * ssid_len - Length of SSID in octets
+	 */
+	size_t ssid_len;
+
+	/**
+	 * psk - WPA pre-shared key (256 bits) (GO only)
+	 */
+	u8 psk[32];
+
+	/**
+	 * psk_set - Whether PSK field is configured (GO only)
+	 */
+	int psk_set;
+
+	/**
+	 * passphrase - WPA2-Personal passphrase for the group (GO only)
+	 */
+	char passphrase[64];
+
+	/**
+	 * peer_device_addr - P2P Device Address of the peer
+	 */
+	u8 peer_device_addr[ETH_ALEN];
+
+	/**
+	 * peer_interface_addr - P2P Interface Address of the peer
+	 */
+	u8 peer_interface_addr[ETH_ALEN];
+
+	/**
+	 * wps_method - WPS method to be used during provisioning
+	 */
+	enum p2p_wps_method wps_method;
+
+#define P2P_MAX_CHANNELS 50
+
+	/**
+	 * freq_list - Zero-terminated list of possible operational channels
+	 */
+	int freq_list[P2P_MAX_CHANNELS];
+
+	/**
+	 * persistent_group - Whether the group should be made persistent
+	 * 0 = not persistent
+	 * 1 = persistent group without persistent reconnect
+	 * 2 = persistent group with persistent reconnect
+	 */
+	int persistent_group;
+
+	/**
+	 * peer_config_timeout - Peer configuration timeout (in 10 msec units)
+	 */
+	unsigned int peer_config_timeout;
+};
+
+struct p2ps_provision {
+	/**
+	 * pd_seeker - P2PS provision discovery seeker role
+	 */
+	unsigned int pd_seeker:1;
+
+	/**
+	 * status - Remote returned provisioning status code
+	 */
+	int status;
+
+	/**
+	 * adv_id - P2PS Advertisement ID
+	 */
+	u32 adv_id;
+
+	/**
+	 * session_id - P2PS Session ID
+	 */
+	u32 session_id;
+
+	/**
+	 * method - WPS Method (to be) used to establish session
+	 */
+	u16 method;
+
+	/**
+	 * conncap - Connection Capabilities negotiated between P2P peers
+	 */
+	u8 conncap;
+
+	/**
+	 * role - Info about the roles to be used for this connection
+	 */
+	u8 role;
+
+	/**
+	 * session_mac - MAC address of the peer that started the session
+	 */
+	u8 session_mac[ETH_ALEN];
+
+	/**
+	 * adv_mac - MAC address of the peer advertised the service
+	 */
+	u8 adv_mac[ETH_ALEN];
+
+	/**
+	 * cpt_mask - Supported Coordination Protocol Transport mask
+	 *
+	 * A bitwise mask of supported ASP Coordination Protocol Transports.
+	 * This property is set together and corresponds with cpt_priority.
+	 */
+	u8 cpt_mask;
+
+	/**
+	 * cpt_priority - Coordination Protocol Transport priority list
+	 *
+	 * Priorities of supported ASP Coordination Protocol Transports.
+	 * This property is set together and corresponds with cpt_mask.
+	 * The CPT priority list is 0 terminated.
+	 */
+	u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
+
+	/**
+	 * info - Vendor defined extra Provisioning information
+	 */
+	char info[0];
+};
+
+struct p2ps_advertisement {
+	struct p2ps_advertisement *next;
+
+	/**
+	 * svc_info - Pointer to (internal) Service defined information
+	 */
+	char *svc_info;
+
+	/**
+	 * id - P2PS Advertisement ID
+	 */
+	u32 id;
+
+	/**
+	 * config_methods - WPS Methods which are allowed for this service
+	 */
+	u16 config_methods;
+
+	/**
+	 * state - Current state of the service: 0 - Out Of Service, 1-255 Vendor defined
+	 */
+	u8 state;
+
+	/**
+	 * auto_accept - Automatically Accept provisioning request if possible.
+	 */
+	u8 auto_accept;
+
+	/**
+	 * hash - 6 octet Service Name has to match against incoming Probe Requests
+	 */
+	u8 hash[P2PS_HASH_LEN];
+
+	/**
+	 * cpt_mask - supported Coordination Protocol Transport mask
+	 *
+	 * A bitwise mask of supported ASP Coordination Protocol Transports.
+	 * This property is set together and corresponds with cpt_priority.
+	 */
+	u8 cpt_mask;
+
+	/**
+	 * cpt_priority - Coordination Protocol Transport priority list
+	 *
+	 * Priorities of supported ASP Coordinatin Protocol Transports.
+	 * This property is set together and corresponds with cpt_mask.
+	 * The CPT priority list is 0 terminated.
+	 */
+	u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
+
+	/**
+	 * svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage
+	 */
+	char svc_name[0];
+};
+
+
+struct p2p_data;
+
+enum p2p_scan_type {
+	P2P_SCAN_SOCIAL,
+	P2P_SCAN_FULL,
+	P2P_SCAN_SPECIFIC,
+	P2P_SCAN_SOCIAL_PLUS_ONE
+};
+
+#define P2P_MAX_WPS_VENDOR_EXT 10
+
+/**
+ * struct p2p_peer_info - P2P peer information
+ */
+struct p2p_peer_info {
+	/**
+	 * p2p_device_addr - P2P Device Address of the peer
+	 */
+	u8 p2p_device_addr[ETH_ALEN];
+
+	/**
+	 * pri_dev_type - Primary Device Type
+	 */
+	u8 pri_dev_type[8];
+
+	/**
+	 * device_name - Device Name (0..32 octets encoded in UTF-8)
+	 */
+	char device_name[WPS_DEV_NAME_MAX_LEN + 1];
+
+	/**
+	 * manufacturer - Manufacturer (0..64 octets encoded in UTF-8)
+	 */
+	char manufacturer[WPS_MANUFACTURER_MAX_LEN + 1];
+
+	/**
+	 * model_name - Model Name (0..32 octets encoded in UTF-8)
+	 */
+	char model_name[WPS_MODEL_NAME_MAX_LEN + 1];
+
+	/**
+	 * model_number - Model Number (0..32 octets encoded in UTF-8)
+	 */
+	char model_number[WPS_MODEL_NUMBER_MAX_LEN + 1];
+
+	/**
+	 * serial_number - Serial Number (0..32 octets encoded in UTF-8)
+	 */
+	char serial_number[WPS_SERIAL_NUMBER_MAX_LEN + 1];
+
+	/**
+	 * level - Signal level
+	 */
+	int level;
+
+	/**
+	 * config_methods - WPS Configuration Methods
+	 */
+	u16 config_methods;
+
+	/**
+	 * dev_capab - Device Capabilities
+	 */
+	u8 dev_capab;
+
+	/**
+	 * group_capab - Group Capabilities
+	 */
+	u8 group_capab;
+
+	/**
+	 * wps_sec_dev_type_list - WPS secondary device type list
+	 *
+	 * This list includes from 0 to 16 Secondary Device Types as indicated
+	 * by wps_sec_dev_type_list_len (8 * number of types).
+	 */
+	u8 wps_sec_dev_type_list[WPS_SEC_DEV_TYPE_MAX_LEN];
+
+	/**
+	 * wps_sec_dev_type_list_len - Length of secondary device type list
+	 */
+	size_t wps_sec_dev_type_list_len;
+
+	struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+
+	/**
+	 * 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;
+
+	/**
+	 * p2ps_instance - P2PS Application Service Info
+	 */
+	struct wpabuf *p2ps_instance;
+};
+
+enum p2p_prov_disc_status {
+	P2P_PROV_DISC_SUCCESS,
+	P2P_PROV_DISC_TIMEOUT,
+	P2P_PROV_DISC_REJECTED,
+	P2P_PROV_DISC_TIMEOUT_JOIN,
+	P2P_PROV_DISC_INFO_UNAVAILABLE,
+};
+
+struct p2p_channel {
+	u8 op_class;
+	u8 chan;
+};
+
+/**
+ * struct p2p_config - P2P configuration
+ *
+ * This configuration is provided to the P2P module during initialization with
+ * p2p_init().
+ */
+struct p2p_config {
+	/**
+	 * country - Country code to use in P2P operations
+	 */
+	char country[3];
+
+	/**
+	 * reg_class - Regulatory class for own listen channel
+	 */
+	u8 reg_class;
+
+	/**
+	 * channel - Own listen channel
+	 */
+	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;
+
+	/**
+	 * op_channel - Own operational channel
+	 */
+	u8 op_channel;
+
+	/**
+	 * cfg_op_channel - Whether op_channel is hardcoded in configuration
+	 */
+	u8 cfg_op_channel;
+
+	/**
+	 * channels - Own supported regulatory classes and channels
+	 *
+	 * List of supposerted channels per regulatory class. The regulatory
+	 * classes are defined in IEEE Std 802.11-2007 Annex J and the
+	 * numbering of the clases depends on the configured country code.
+	 */
+	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;
+
+	/**
+	 * pref_chan - Preferred channels for GO Negotiation
+	 */
+	struct p2p_channel *pref_chan;
+
+	/**
+	 * pri_dev_type - Primary Device Type (see WPS)
+	 */
+	u8 pri_dev_type[8];
+
+	/**
+	 * P2P_SEC_DEVICE_TYPES - Maximum number of secondary device types
+	 */
+#define P2P_SEC_DEVICE_TYPES 5
+
+	/**
+	 * sec_dev_type - Optional secondary device types
+	 */
+	u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8];
+
+	/**
+	 * num_sec_dev_types - Number of sec_dev_type entries
+	 */
+	size_t num_sec_dev_types;
+
+	/**
+	 * dev_addr - P2P Device Address
+	 */
+	u8 dev_addr[ETH_ALEN];
+
+	/**
+	 * dev_name - Device Name
+	 */
+	char *dev_name;
+
+	char *manufacturer;
+	char *model_name;
+	char *model_number;
+	char *serial_number;
+
+	u8 uuid[16];
+	u16 config_methods;
+
+	/**
+	 * concurrent_operations - Whether concurrent operations are supported
+	 */
+	int concurrent_operations;
+
+	/**
+	 * max_peers - Maximum number of discovered peers to remember
+	 *
+	 * If more peers are discovered, older entries will be removed to make
+	 * room for the new ones.
+	 */
+	size_t max_peers;
+
+	/**
+	 * p2p_intra_bss - Intra BSS communication is supported
+	 */
+	int p2p_intra_bss;
+
+	/**
+	 * ssid_postfix - Postfix data to add to the SSID
+	 *
+	 * This data will be added to the end of the SSID after the
+	 * DIRECT-<random two octets> prefix.
+	 */
+	u8 ssid_postfix[SSID_MAX_LEN - 9];
+
+	/**
+	 * ssid_postfix_len - Length of the ssid_postfix data
+	 */
+	size_t ssid_postfix_len;
+
+	/**
+	 * max_listen - Maximum listen duration in ms
+	 */
+	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;
+
+	/**
+	 * debug_print - Debug print
+	 * @ctx: Callback context from cb_ctx
+	 * @level: Debug verbosity level (MSG_*)
+	 * @msg: Debug message
+	 */
+	void (*debug_print)(void *ctx, int level, const char *msg);
+
+
+	/* Callbacks to request lower layer driver operations */
+
+	/**
+	 * p2p_scan - Request a P2P scan/search
+	 * @ctx: Callback context from cb_ctx
+	 * @type: Scan type
+	 * @freq: Specific frequency (MHz) to scan or 0 for no restriction
+	 * @num_req_dev_types: Number of requested device types
+	 * @req_dev_types: Array containing requested device types
+	 * @dev_id: Device ID to search for or %NULL to find all devices
+	 * @pw_id: Device Password ID
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This callback function is used to request a P2P scan or search
+	 * operation to be completed. Type type argument specifies which type
+	 * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the
+	 * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL
+	 * indicates that all channels are to be scanned. @P2P_SCAN_SPECIFIC
+	 * request a scan of a single channel specified by freq.
+	 * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels
+	 * plus one extra channel specified by freq.
+	 *
+	 * The full scan is used for the initial scan to find group owners from
+	 * all. The other types are used during search phase scan of the social
+	 * channels (with potential variation if the Listen channel of the
+	 * target peer is known or if other channels are scanned in steps).
+	 *
+	 * The scan results are returned after this call by calling
+	 * p2p_scan_res_handler() for each scan result that has a P2P IE and
+	 * then calling p2p_scan_res_handled() to indicate that all scan
+	 * results have been indicated.
+	 */
+	int (*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);
+
+	/**
+	 * send_probe_resp - Transmit a Probe Response frame
+	 * @ctx: Callback context from cb_ctx
+	 * @buf: Probe Response frame (including the header and body)
+	 * @freq: Forced frequency (in MHz) to use or 0.
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is used to reply to Probe Request frames that were
+	 * indicated with a call to p2p_probe_req_rx(). The response is to be
+	 * sent on the same channel, unless otherwise specified, or to be
+	 * dropped if the driver is not listening to Probe Request frames
+	 * anymore.
+	 *
+	 * Alternatively, the responsibility for building the Probe Response
+	 * frames in Listen state may be in another system component in which
+	 * case this function need to be implemented (i.e., the function
+	 * pointer can be %NULL). The WPS and P2P IEs to be added for Probe
+	 * Response frames in such a case are available from the
+	 * start_listen() callback. It should be noted that the received Probe
+	 * Request frames must be indicated by calling p2p_probe_req_rx() even
+	 * if this send_probe_resp() is not used.
+	 */
+	int (*send_probe_resp)(void *ctx, const struct wpabuf *buf,
+			       unsigned int freq);
+
+	/**
+	 * send_action - Transmit an Action frame
+	 * @ctx: Callback context from cb_ctx
+	 * @freq: Frequency in MHz for the channel on which to transmit
+	 * @dst: Destination MAC address (Address 1)
+	 * @src: Source MAC address (Address 2)
+	 * @bssid: BSSID (Address 3)
+	 * @buf: Frame body (starting from Category field)
+	 * @len: Length of buf in octets
+	 * @wait_time: How many msec to wait for a response frame
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * The Action frame may not be transmitted immediately and the status
+	 * of the transmission must be reported by calling
+	 * p2p_send_action_cb() once the frame has either been transmitted or
+	 * it has been dropped due to excessive retries or other failure to
+	 * transmit.
+	 */
+	int (*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);
+
+	/**
+	 * send_action_done - Notify that Action frame sequence was completed
+	 * @ctx: Callback context from cb_ctx
+	 *
+	 * This function is called when the Action frame sequence that was
+	 * started with send_action() has been completed, i.e., when there is
+	 * no need to wait for a response from the destination peer anymore.
+	 */
+	void (*send_action_done)(void *ctx);
+
+	/**
+	 * start_listen - Start Listen state
+	 * @ctx: Callback context from cb_ctx
+	 * @freq: Frequency of the listen channel in MHz
+	 * @duration: Duration for the Listen state in milliseconds
+	 * @probe_resp_ie: IE(s) to be added to Probe Response frames
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This Listen state may not start immediately since the driver may
+	 * have other pending operations to complete first. Once the Listen
+	 * state has started, p2p_listen_cb() must be called to notify the P2P
+	 * module. Once the Listen state is stopped, p2p_listen_end() must be
+	 * called to notify the P2P module that the driver is not in the Listen
+	 * state anymore.
+	 *
+	 * If the send_probe_resp() is not used for generating the response,
+	 * the IEs from probe_resp_ie need to be added to the end of the Probe
+	 * Response frame body. If send_probe_resp() is used, the probe_resp_ie
+	 * information can be ignored.
+	 */
+	int (*start_listen)(void *ctx, unsigned int freq,
+			    unsigned int duration,
+			    const struct wpabuf *probe_resp_ie);
+	/**
+	 * stop_listen - Stop Listen state
+	 * @ctx: Callback context from cb_ctx
+	 *
+	 * This callback can be used to stop a Listen state operation that was
+	 * previously requested with start_listen().
+	 */
+	void (*stop_listen)(void *ctx);
+
+	/**
+	 * get_noa - Get current Notice of Absence attribute payload
+	 * @ctx: Callback context from cb_ctx
+	 * @interface_addr: P2P Interface Address of the GO
+	 * @buf: Buffer for returning NoA
+	 * @buf_len: Buffer length in octets
+	 * Returns: Number of octets used in buf, 0 to indicate no NoA is being
+	 * advertized, or -1 on failure
+	 *
+	 * This function is used to fetch the current Notice of Absence
+	 * attribute value from GO.
+	 */
+	int (*get_noa)(void *ctx, const u8 *interface_addr, u8 *buf,
+		       size_t buf_len);
+
+	/* Callbacks to notify events to upper layer management entity */
+
+	/**
+	 * dev_found - Notification of a found P2P Device
+	 * @ctx: Callback context from cb_ctx
+	 * @addr: Source address of the message triggering this notification
+	 * @info: P2P peer information
+	 * @new_device: Inform if the peer is newly found
+	 *
+	 * This callback is used to notify that a new P2P Device has been
+	 * found. This may happen, e.g., during Search state based on scan
+	 * results or during Listen state based on receive Probe Request and
+	 * Group Owner Negotiation Request.
+	 */
+	void (*dev_found)(void *ctx, const u8 *addr,
+			  const struct p2p_peer_info *info,
+			  int new_device);
+
+	/**
+	 * dev_lost - Notification of a lost P2P Device
+	 * @ctx: Callback context from cb_ctx
+	 * @dev_addr: P2P Device Address of the lost P2P Device
+	 *
+	 * This callback is used to notify that a P2P Device has been deleted.
+	 */
+	void (*dev_lost)(void *ctx, const u8 *dev_addr);
+
+	/**
+	 * find_stopped - Notification of a p2p_find operation stopping
+	 * @ctx: Callback context from cb_ctx
+	 */
+	void (*find_stopped)(void *ctx);
+
+	/**
+	 * go_neg_req_rx - Notification of a receive GO Negotiation Request
+	 * @ctx: Callback context from cb_ctx
+	 * @src: Source address of the message triggering this notification
+	 * @dev_passwd_id: WPS Device Password ID
+	 * @go_intent: Peer's GO Intent
+	 *
+	 * This callback is used to notify that a P2P Device is requesting
+	 * group owner negotiation with us, but we do not have all the
+	 * necessary information to start GO Negotiation. This indicates that
+	 * the local user has not authorized the connection yet by providing a
+	 * PIN or PBC button press. This information can be provided with a
+	 * call to p2p_connect().
+	 */
+	void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id,
+			      u8 go_intent);
+
+	/**
+	 * go_neg_completed - Notification of GO Negotiation results
+	 * @ctx: Callback context from cb_ctx
+	 * @res: GO Negotiation results
+	 *
+	 * This callback is used to notify that Group Owner Negotiation has
+	 * been completed. Non-zero struct p2p_go_neg_results::status indicates
+	 * failed negotiation. In case of success, this function is responsible
+	 * for creating a new group interface (or using the existing interface
+	 * depending on driver features), setting up the group interface in
+	 * proper mode based on struct p2p_go_neg_results::role_go and
+	 * initializing WPS provisioning either as a Registrar (if GO) or as an
+	 * Enrollee. Successful WPS provisioning must be indicated by calling
+	 * p2p_wps_success_cb(). The callee is responsible for timing out group
+	 * formation if WPS provisioning cannot be completed successfully
+	 * within 15 seconds.
+	 */
+	void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res);
+
+	/**
+	 * sd_request - Callback on Service Discovery Request
+	 * @ctx: Callback context from cb_ctx
+	 * @freq: Frequency (in MHz) of the channel
+	 * @sa: Source address of the request
+	 * @dialog_token: Dialog token
+	 * @update_indic: Service Update Indicator from the source of request
+	 * @tlvs: P2P Service Request TLV(s)
+	 * @tlvs_len: Length of tlvs buffer in octets
+	 *
+	 * This callback is used to indicate reception of a service discovery
+	 * request. Response to the query must be indicated by calling
+	 * p2p_sd_response() with the context information from the arguments to
+	 * this callback function.
+	 *
+	 * This callback handler can be set to %NULL to indicate that service
+	 * discovery is not supported.
+	 */
+	void (*sd_request)(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+			   u16 update_indic, const u8 *tlvs, size_t tlvs_len);
+
+	/**
+	 * sd_response - Callback on Service Discovery Response
+	 * @ctx: Callback context from cb_ctx
+	 * @sa: Source address of the request
+	 * @update_indic: Service Update Indicator from the source of response
+	 * @tlvs: P2P Service Response TLV(s)
+	 * @tlvs_len: Length of tlvs buffer in octets
+	 *
+	 * This callback is used to indicate reception of a service discovery
+	 * response. This callback handler can be set to %NULL if no service
+	 * discovery requests are used. The information provided with this call
+	 * is replies to the queries scheduled with p2p_sd_request().
+	 */
+	void (*sd_response)(void *ctx, const u8 *sa, u16 update_indic,
+			    const u8 *tlvs, size_t tlvs_len);
+
+	/**
+	 * prov_disc_req - Callback on Provisiong Discovery Request
+	 * @ctx: Callback context from cb_ctx
+	 * @peer: Source address of the request
+	 * @config_methods: Requested WPS Config Method
+	 * @dev_addr: P2P Device Address of the found P2P Device
+	 * @pri_dev_type: Primary Device Type
+	 * @dev_name: Device Name
+	 * @supp_config_methods: Supported configuration Methods
+	 * @dev_capab: Device Capabilities
+	 * @group_capab: Group Capabilities
+	 * @group_id: P2P Group ID (or %NULL if not included)
+	 * @group_id_len: Length of P2P Group ID
+	 *
+	 * This callback is used to indicate reception of a Provision Discovery
+	 * Request frame that the P2P module accepted.
+	 */
+	void (*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);
+
+	/**
+	 * prov_disc_resp - Callback on Provisiong Discovery Response
+	 * @ctx: Callback context from cb_ctx
+	 * @peer: Source address of the response
+	 * @config_methods: Value from p2p_prov_disc_req() or 0 on failure
+	 *
+	 * This callback is used to indicate reception of a Provision Discovery
+	 * Response frame for a pending request scheduled with
+	 * p2p_prov_disc_req(). This callback handler can be set to %NULL if
+	 * provision discovery is not used.
+	 */
+	void (*prov_disc_resp)(void *ctx, const u8 *peer, u16 config_methods);
+
+	/**
+	 * prov_disc_fail - Callback on Provision Discovery failure
+	 * @ctx: Callback context from cb_ctx
+	 * @peer: Source address of the response
+	 * @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS
+	 * @adv_id: If non-zero, then the adv_id of the PD Request
+	 * @adv_mac: P2P Device Address of the advertizer
+	 * @deferred_session_resp: Deferred session response sent by advertizer
+	 *
+	 * This callback is used to indicate either a failure or no response
+	 * to an earlier provision discovery request.
+	 *
+	 * This callback handler can be set to %NULL if provision discovery
+	 * is not used or failures do not need to be indicated.
+	 */
+	void (*prov_disc_fail)(void *ctx, const u8 *peer,
+			       enum p2p_prov_disc_status status,
+			       u32 adv_id, const u8 *adv_mac,
+			       const char *deferred_session_resp);
+
+	/**
+	 * invitation_process - Optional callback for processing Invitations
+	 * @ctx: Callback context from cb_ctx
+	 * @sa: Source address of the Invitation Request
+	 * @bssid: P2P Group BSSID from the request or %NULL if not included
+	 * @go_dev_addr: GO Device Address from P2P Group ID
+	 * @ssid: SSID from P2P Group ID
+	 * @ssid_len: Length of ssid buffer in octets
+	 * @go: Variable for returning whether the local end is GO in the group
+	 * @group_bssid: Buffer for returning P2P Group BSSID (if local end GO)
+	 * @force_freq: Variable for returning forced frequency for the group
+	 * @persistent_group: Whether this is an invitation to reinvoke a
+	 *	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
+	 * by allowing automatic restarting of persistent groups without user
+	 * interaction. If this callback is not implemented (i.e., is %NULL),
+	 * the received Invitation Request frames are replied with
+	 * %P2P_SC_REQ_RECEIVED status and indicated to upper layer with the
+	 * invitation_result() callback.
+	 *
+	 * If the requested parameters are acceptable and the group is known,
+	 * %P2P_SC_SUCCESS may be returned. If the requested group is unknown,
+	 * %P2P_SC_FAIL_UNKNOWN_GROUP should be returned. %P2P_SC_REQ_RECEIVED
+	 * can be returned if there is not enough data to provide immediate
+	 * response, i.e., if some sort of user interaction is needed. The
+	 * invitation_received() callback will be called in that case
+	 * immediately after this call.
+	 */
+	u8 (*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,
+				 int dev_pw_id);
+
+	/**
+	 * invitation_received - Callback on Invitation Request RX
+	 * @ctx: Callback context from cb_ctx
+	 * @sa: Source address of the Invitation Request
+	 * @bssid: P2P Group BSSID or %NULL if not received
+	 * @ssid: SSID of the group
+	 * @ssid_len: Length of ssid in octets
+	 * @go_dev_addr: GO Device Address
+	 * @status: Response Status
+	 * @op_freq: Operational frequency for the group
+	 *
+	 * This callback is used to indicate sending of an Invitation Response
+	 * for a received Invitation Request. If status == 0 (success), the
+	 * upper layer code is responsible for starting the group. status == 1
+	 * indicates need to get user authorization for the group. Other status
+	 * values indicate that the invitation request was rejected.
+	 */
+	void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid,
+				    const u8 *ssid, size_t ssid_len,
+				    const u8 *go_dev_addr, u8 status,
+				    int op_freq);
+
+	/**
+	 * invitation_result - Callback on Invitation result
+	 * @ctx: Callback context from cb_ctx
+	 * @status: Negotiation result (Status Code)
+	 * @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
+	 * the value received from the peer in Invitation Response with 0
+	 * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a
+	 * local failure in transmitting the Invitation Request.
+	 */
+	void (*invitation_result)(void *ctx, int status, const u8 *bssid,
+				  const struct p2p_channels *channels,
+				  const u8 *addr, int freq, int peer_oper_freq);
+
+	/**
+	 * go_connected - Check whether we are connected to a GO
+	 * @ctx: Callback context from cb_ctx
+	 * @dev_addr: P2P Device Address of a GO
+	 * Returns: 1 if we are connected as a P2P client to the specified GO
+	 * 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);
+
+	/**
+	 * Determine if we have a persistent group we share with remote peer
+	 * and allocate interface for this group if needed
+	 * @ctx: Callback context from cb_ctx
+	 * @addr: Peer device address to search for
+	 * @ssid: Persistent group SSID or %NULL if any
+	 * @ssid_len: Length of @ssid
+	 * @go_dev_addr: Buffer for returning GO P2P Device Address
+	 * @ret_ssid: Buffer for returning group SSID
+	 * @ret_ssid_len: Buffer for returning length of @ssid
+	 * @intended_iface_addr: Buffer for returning intended iface address
+	 * Returns: 1 if a matching persistent group was found, 0 otherwise
+	 */
+	int (*get_persistent_group)(void *ctx, const u8 *addr, const u8 *ssid,
+				    size_t ssid_len, u8 *go_dev_addr,
+				    u8 *ret_ssid, size_t *ret_ssid_len,
+				    u8 *intended_iface_addr);
+
+	/**
+	 * Get information about a possible local GO role
+	 * @ctx: Callback context from cb_ctx
+	 * @intended_addr: Buffer for returning intended GO interface address
+	 * @ssid: Buffer for returning group SSID
+	 * @ssid_len: Buffer for returning length of @ssid
+	 * @group_iface: Buffer for returning whether a separate group interface
+	 *	would be used
+	 * Returns: 1 if GO info found, 0 otherwise
+	 *
+	 * This is used to compose New Group settings (SSID, and intended
+	 * address) during P2PS provisioning if results of provisioning *might*
+	 * result in our being an autonomous GO.
+	 */
+	int (*get_go_info)(void *ctx, u8 *intended_addr,
+			   u8 *ssid, size_t *ssid_len, int *group_iface);
+
+	/**
+	 * remove_stale_groups - Remove stale P2PS groups
+	 *
+	 * Because P2PS stages *potential* GOs, and remote devices can remove
+	 * credentials unilaterally, we need to make sure we don't let stale
+	 * unusable groups build up.
+	 */
+	int (*remove_stale_groups)(void *ctx, const u8 *peer, const u8 *go,
+				   const u8 *ssid, size_t ssid_len);
+
+	/**
+	 * p2ps_prov_complete - P2PS provisioning complete
+	 *
+	 * When P2PS provisioning completes (successfully or not) we must
+	 * transmit all of the results to the upper layers.
+	 */
+	void (*p2ps_prov_complete)(void *ctx, u8 status, const u8 *dev,
+				   const u8 *adv_mac, const u8 *ses_mac,
+				   const u8 *grp_mac, u32 adv_id, u32 ses_id,
+				   u8 conncap, int passwd_id,
+				   const u8 *persist_ssid,
+				   size_t persist_ssid_size, int response_done,
+				   int prov_start, const char *session_info,
+				   const u8 *feat_cap, size_t feat_cap_len);
+
+	/**
+	 * prov_disc_resp_cb - Callback for indicating completion of PD Response
+	 * @ctx: Callback context from cb_ctx
+	 * Returns: 1 if operation was started, 0 otherwise
+	 *
+	 * This callback can be used to perform any pending actions after
+	 * provisioning. It is mainly used for P2PS pending group creation.
+	 */
+	int (*prov_disc_resp_cb)(void *ctx);
+
+	/**
+	 * p2ps_group_capability - Determine group capability
+	 *
+	 * This function can be used to determine group capability based on
+	 * information from P2PS PD exchange and the current state of ongoing
+	 * groups and driver capabilities.
+	 *
+	 * P2PS_SETUP_* bitmap is used as the parameters and return value.
+	 */
+	u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role);
+
+	/**
+	 * get_pref_freq_list - Get preferred frequency list for an interface
+	 * @ctx: Callback context from cb_ctx
+	 * @go: Whether the use if for GO role
+	 * @len: Length of freq_list in entries (both IN and OUT)
+	 * @freq_list: Buffer for returning the preferred frequencies (MHz)
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function can be used to query the preferred frequency list from
+	 * the driver specific to a particular interface type.
+	 */
+	int (*get_pref_freq_list)(void *ctx, int go,
+				  unsigned int *len, unsigned int *freq_list);
+};
+
+
+/* P2P module initialization/deinitialization */
+
+/**
+ * p2p_init - Initialize P2P module
+ * @cfg: P2P module configuration
+ * Returns: Pointer to private data or %NULL on failure
+ *
+ * This function is used to initialize global P2P module context (one per
+ * device). The P2P module will keep a copy of the configuration data, so the
+ * caller does not need to maintain this structure. However, the callback
+ * functions and the context parameters to them must be kept available until
+ * the P2P module is deinitialized with p2p_deinit().
+ */
+struct p2p_data * p2p_init(const struct p2p_config *cfg);
+
+/**
+ * p2p_deinit - Deinitialize P2P module
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_deinit(struct p2p_data *p2p);
+
+/**
+ * p2p_flush - Flush P2P module state
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This command removes the P2P module state like peer device entries.
+ */
+void p2p_flush(struct p2p_data *p2p);
+
+/**
+ * p2p_unauthorize - Unauthorize the specified peer device
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P peer entry to be unauthorized
+ * Returns: 0 on success, -1 on failure
+ *
+ * This command removes any connection authorization from the specified P2P
+ * peer device address. This can be used, e.g., to cancel effect of a previous
+ * p2p_authorize() or p2p_connect() call that has not yet resulted in completed
+ * GO Negotiation.
+ */
+int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_set_dev_name - Set device name
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name);
+
+int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer);
+int p2p_set_model_name(struct p2p_data *p2p, const char *model_name);
+int p2p_set_model_number(struct p2p_data *p2p, const char *model_number);
+int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number);
+
+void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods);
+void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid);
+
+/**
+ * p2p_set_pri_dev_type - Set primary device type
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type);
+
+/**
+ * p2p_set_sec_dev_types - Set secondary device types
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8],
+			  size_t num_dev_types);
+
+int p2p_set_country(struct p2p_data *p2p, const char *country);
+
+
+/* Commands from upper layer management entity */
+
+enum p2p_discovery_type {
+	P2P_FIND_START_WITH_FULL,
+	P2P_FIND_ONLY_SOCIAL,
+	P2P_FIND_PROGRESSIVE
+};
+
+/**
+ * p2p_find - Start P2P Find (Device Discovery)
+ * @p2p: P2P module context from p2p_init()
+ * @timeout: Timeout for find operation in seconds or 0 for no timeout
+ * @type: Device Discovery type
+ * @num_req_dev_types: Number of requested device types
+ * @req_dev_types: Requested device types array, must be an array
+ *	containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no
+ *	requested device types.
+ * @dev_id: Device ID to search for or %NULL to find all devices
+ * @search_delay: Extra delay in milliseconds between search iterations
+ * @seek_count: Number of ASP Service Strings in the seek_string array
+ * @seek_string: ASP Service Strings to query for in Probe Requests
+ * @freq: Requested first scan frequency (in MHz) to modify type ==
+ *	P2P_FIND_START_WITH_FULL behavior. 0 = Use normal full scan.
+ *	If p2p_find is already in progress, this parameter is ignored and full
+ *	scan will be executed.
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_find(struct p2p_data *p2p, unsigned int timeout,
+	     enum p2p_discovery_type type,
+	     unsigned int num_req_dev_types, const u8 *req_dev_types,
+	     const u8 *dev_id, unsigned int search_delay,
+	     u8 seek_count, const char **seek_string, int freq);
+
+/**
+ * p2p_notify_scan_trigger_status - Indicate scan trigger status
+ * @p2p: P2P module context from p2p_init()
+ * @status: 0 on success, -1 on failure
+ */
+void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status);
+
+/**
+ * p2p_stop_find - Stop P2P Find (Device Discovery)
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_stop_find(struct p2p_data *p2p);
+
+/**
+ * p2p_stop_find_for_freq - Stop P2P Find for next oper on specific freq
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Frequency in MHz for next operation
+ *
+ * This is like p2p_stop_find(), but Listen state is not stopped if we are
+ * already on the same frequency.
+ */
+void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq);
+
+/**
+ * p2p_listen - Start P2P Listen state for specified duration
+ * @p2p: P2P module context from p2p_init()
+ * @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.
+ */
+int p2p_listen(struct p2p_data *p2p, unsigned int timeout);
+
+/**
+ * p2p_stop_listen - Stop P2P Listen
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_stop_listen(struct p2p_data *p2p);
+
+/**
+ * p2p_connect - Start P2P group formation (GO negotiation)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: WPS method to be used in provisioning
+ * @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 a persistent group (0 = no, 1 =
+ * persistent group without persistent reconnect, 2 = persistent group with
+ * persistent reconnect)
+ * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate
+ *	a new SSID
+ * @force_ssid_len: Length of $force_ssid buffer
+ * @pd_before_go_neg: Whether to send Provision Discovery prior to GO
+ *	Negotiation as an interoperability workaround when initiating group
+ *	formation
+ * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
+ *	force_freq == 0)
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
+		enum p2p_wps_method wps_method,
+		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, u16 oob_pw_id);
+
+/**
+ * p2p_authorize - Authorize P2P group formation (GO negotiation)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: WPS method to be used in provisioning
+ * @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 a persistent group (0 = no, 1 =
+ * persistent group without persistent reconnect, 2 = persistent group with
+ * persistent reconnect)
+ * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate
+ *	a new SSID
+ * @force_ssid_len: Length of $force_ssid buffer
+ * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
+ *	force_freq == 0)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is like p2p_connect(), but the actual group negotiation is not
+ * initiated automatically, i.e., the other end is expected to do that.
+ */
+int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
+		  enum p2p_wps_method wps_method,
+		  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, u16 oob_pw_id);
+
+/**
+ * p2p_reject - Reject peer device (explicitly block connection attempts)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr);
+
+/**
+ * p2p_prov_disc_req - Send Provision Discovery Request
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @p2ps_prov: Provisioning info for P2PS
+ * @config_methods: WPS Config Methods value (only one bit set)
+ * @join: Whether this is used by a client joining an active group
+ * @force_freq: Forced TX frequency for the frame (mainly for the join case)
+ * @user_initiated_pd: Flag to indicate if initiated by user or not
+ * 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 p2p_config::prov_disc_resp() callback.
+ */
+int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+		      struct p2ps_provision *p2ps_prov, u16 config_methods,
+		      int join, int force_freq,
+		      int user_initiated_pd);
+
+/**
+ * p2p_sd_request - Schedule a service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @dst: Destination peer or %NULL to apply for all peers
+ * @tlvs: P2P Service Query TLV(s)
+ * Returns: Reference to the query or %NULL on failure
+ *
+ * Response to the query is indicated with the p2p_config::sd_response()
+ * callback.
+ */
+void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
+		      const struct wpabuf *tlvs);
+
+#ifdef CONFIG_WIFI_DISPLAY
+void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst,
+			  const struct wpabuf *tlvs);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+/**
+ * p2p_sd_cancel_request - Cancel a pending service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @req: Query reference from p2p_sd_request()
+ * Returns: 0 if request for cancelled; -1 if not found
+ */
+int p2p_sd_cancel_request(struct p2p_data *p2p, void *req);
+
+/**
+ * p2p_sd_response - Send response to a service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Frequency from p2p_config::sd_request() callback
+ * @dst: Destination address from p2p_config::sd_request() callback
+ * @dialog_token: Dialog token from p2p_config::sd_request() callback
+ * @resp_tlvs: P2P Service Response TLV(s)
+ *
+ * This function is called as a response to the request indicated with
+ * p2p_config::sd_request() callback.
+ */
+void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
+		     u8 dialog_token, const struct wpabuf *resp_tlvs);
+
+/**
+ * p2p_sd_service_update - Indicate a change in local services
+ * @p2p: P2P module context from p2p_init()
+ *
+ * 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.
+ */
+void p2p_sd_service_update(struct p2p_data *p2p);
+
+
+enum p2p_invite_role {
+	P2P_INVITE_ROLE_GO,
+	P2P_INVITE_ROLE_ACTIVE_GO,
+	P2P_INVITE_ROLE_CLIENT
+};
+
+/**
+ * p2p_invite - Invite a P2P Device into a group
+ * @p2p: P2P module context from p2p_init()
+ * @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
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @go_dev_addr: Forced GO Device Address or %NULL if none
+ * @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 dev_pw_id);
+
+/**
+ * p2p_presence_req - Request GO presence
+ * @p2p: P2P module context from p2p_init()
+ * @go_interface_addr: GO P2P Interface Address
+ * @own_interface_addr: Own P2P Interface Address for this group
+ * @freq: Group operating frequence (in MHz)
+ * @duration1: Preferred presence duration in microseconds
+ * @interval1: Preferred presence interval in microseconds
+ * @duration2: Acceptable presence duration in microseconds
+ * @interval2: Acceptable presence interval in microseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * If both duration and interval values are zero, the parameter pair is not
+ * specified (i.e., to remove Presence Request, use duration1 = interval1 = 0).
+ */
+int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr,
+		     const u8 *own_interface_addr, unsigned int freq,
+		     u32 duration1, u32 interval1, u32 duration2,
+		     u32 interval2);
+
+/**
+ * p2p_ext_listen - Set Extended Listen Timing
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Group operating frequence (in MHz)
+ * @period: Availability period in milliseconds (1-65535; 0 to disable)
+ * @interval: Availability interval in milliseconds (1-65535; 0 to disable)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to enable or disable (period = interval = 0)
+ * Extended Listen Timing. When enabled, the P2P Device will become
+ * discoverable (go into Listen State) every @interval milliseconds for at
+ * least @period milliseconds.
+ */
+int p2p_ext_listen(struct p2p_data *p2p, unsigned int period,
+		   unsigned int interval);
+
+/* Event notifications from upper layer management operations */
+
+/**
+ * p2p_wps_success_cb - Report successfully completed WPS provisioning
+ * @p2p: P2P module context from p2p_init()
+ * @mac_addr: Peer address
+ *
+ * This function is used to report successfully completed WPS provisioning
+ * during group formation in both GO/Registrar and client/Enrollee roles.
+ */
+void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr);
+
+/**
+ * p2p_group_formation_failed - Report failed WPS provisioning
+ * @p2p: P2P module context from p2p_init()
+ *
+ * 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.
+ */
+void p2p_group_formation_failed(struct p2p_data *p2p);
+
+/**
+ * p2p_get_provisioning_info - Get any stored provisioning info
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Peer P2P Device Address
+ * Returns: WPS provisioning information (WPS config method) or 0 if no
+ * information is available
+ *
+ * This function is used to retrieve stored WPS provisioning info for the given
+ * peer.
+ */
+u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_clear_provisioning_info - Clear any stored provisioning info
+ * @p2p: P2P module context from p2p_init()
+ * @iface_addr: Peer P2P Device Address
+ *
+ * This function is used to clear stored WPS provisioning info for the given
+ * peer.
+ */
+void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr);
+
+
+/* Event notifications from lower layer driver operations */
+
+/**
+ * enum p2p_probe_req_status
+ *
+ * @P2P_PREQ_MALFORMED: frame was not well-formed
+ * @P2P_PREQ_NOT_LISTEN: device isn't in listen state, frame ignored
+ * @P2P_PREQ_NOT_P2P: frame was not a P2P probe request
+ * @P2P_PREQ_P2P_NOT_PROCESSED: frame was P2P but wasn't processed
+ * @P2P_PREQ_P2P_PROCESSED: frame has been processed by P2P
+ */
+enum p2p_probe_req_status {
+	P2P_PREQ_MALFORMED,
+	P2P_PREQ_NOT_LISTEN,
+	P2P_PREQ_NOT_P2P,
+	P2P_PREQ_NOT_PROCESSED,
+	P2P_PREQ_PROCESSED
+};
+
+/**
+ * p2p_probe_req_rx - Report reception of a Probe Request frame
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Source MAC address
+ * @dst: Destination MAC address if available or %NULL
+ * @bssid: BSSID if available or %NULL
+ * @ie: Information elements from the Probe Request frame body
+ * @ie_len: Length of ie buffer in octets
+ * @rx_freq: Probe Request frame RX frequency
+ * Returns: value indicating the type and status of the probe request
+ */
+enum p2p_probe_req_status
+p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+		 const u8 *bssid, const u8 *ie, size_t ie_len,
+		 unsigned int rx_freq);
+
+/**
+ * p2p_rx_action - Report received Action frame
+ * @p2p: P2P module context from p2p_init()
+ * @da: Destination address of the received Action frame
+ * @sa: Source address of the received Action frame
+ * @bssid: Address 3 of the received Action frame
+ * @category: Category of the received Action frame
+ * @data: Action frame body after the Category field
+ * @len: Length of the data buffer in octets
+ * @freq: Frequency (in MHz) on which the frame was received
+ */
+void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+		   const u8 *bssid, u8 category,
+		   const u8 *data, size_t len, int freq);
+
+/**
+ * p2p_scan_res_handler - Indicate a P2P scan results
+ * @p2p: P2P module context from p2p_init()
+ * @bssid: BSSID of the scan result
+ * @freq: Frequency of the channel on which the device was found in MHz
+ * @rx_time: Time when the result was received
+ * @level: Signal level (signal strength of the received Beacon/Probe Response
+ *	frame)
+ * @ies: Pointer to IEs from the scan result
+ * @ies_len: Length of the ies buffer
+ * Returns: 0 to continue or 1 to stop scan result indication
+ *
+ * This function is called to indicate a scan result entry with P2P IE from a
+ * scan requested with struct p2p_config::p2p_scan(). This can be called during
+ * the actual scan process (i.e., whenever a new device is found) or as a
+ * sequence of calls after the full scan has been completed. The former option
+ * can result in optimized operations, but may not be supported by all
+ * driver/firmware designs. The ies buffer need to include at least the P2P IE,
+ * but it is recommended to include all IEs received from the device. The
+ * caller does not need to check that the IEs contain a P2P IE before calling
+ * this function since frames will be filtered internally if needed.
+ *
+ * This function will return 1 if it wants to stop scan result iteration (and
+ * scan in general if it is still in progress). This is used to allow faster
+ * 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_reltime *rx_time, int level, const u8 *ies,
+			 size_t ies_len);
+
+/**
+ * p2p_scan_res_handled - Indicate end of scan results
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function is called to indicate that all P2P scan results from a scan
+ * have been reported with zero or more calls to p2p_scan_res_handler(). This
+ * function must be called as a response to successful
+ * struct p2p_config::p2p_scan() call if none of the p2p_scan_res_handler()
+ * calls stopped iteration.
+ */
+void p2p_scan_res_handled(struct p2p_data *p2p);
+
+enum p2p_send_action_result {
+	P2P_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */,
+	P2P_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged */,
+	P2P_SEND_ACTION_FAILED /* Frame was not sent due to a failure */
+};
+
+/**
+ * p2p_send_action_cb - Notify TX status of an Action frame
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * @dst: Destination MAC address (Address 1)
+ * @src: Source MAC address (Address 2)
+ * @bssid: BSSID (Address 3)
+ * @result: Result of the transmission attempt
+ *
+ * This function is used to indicate the result of an Action frame transmission
+ * that was requested with struct p2p_config::send_action() callback.
+ */
+void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+			const u8 *src, const u8 *bssid,
+			enum p2p_send_action_result result);
+
+/**
+ * p2p_listen_cb - Indicate the start of a requested Listen state
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Listen channel frequency in MHz
+ * @duration: Duration for the Listen state in milliseconds
+ *
+ * This function is used to indicate that a Listen state requested with
+ * struct p2p_config::start_listen() callback has started.
+ */
+void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
+		   unsigned int duration);
+
+/**
+ * p2p_listen_end - Indicate the end of a requested Listen state
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Listen channel frequency in MHz
+ * Returns: 0 if no operations were started, 1 if an operation was started
+ *
+ * This function is used to indicate that a Listen state requested with
+ * struct p2p_config::start_listen() callback has ended.
+ */
+int p2p_listen_end(struct p2p_data *p2p, unsigned int freq);
+
+void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+		      const u8 *ie, size_t ie_len);
+
+void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+			const u8 *ie, size_t ie_len);
+
+
+/* Per-group P2P state for GO */
+
+struct p2p_group;
+
+/**
+ * struct p2p_group_config - P2P group configuration
+ *
+ * This configuration is provided to the P2P module during initialization of
+ * the per-group information with p2p_group_init().
+ */
+struct p2p_group_config {
+	/**
+	 * persistent_group - Whether the group is persistent
+	 * 0 = not a persistent group
+	 * 1 = persistent group without persistent reconnect
+	 * 2 = persistent group with persistent reconnect
+	 */
+	int persistent_group;
+
+	/**
+	 * interface_addr - P2P Interface Address of the group
+	 */
+	u8 interface_addr[ETH_ALEN];
+
+	/**
+	 * max_clients - Maximum number of clients in the group
+	 */
+	unsigned int max_clients;
+
+	/**
+	 * ssid - Group SSID
+	 */
+	u8 ssid[SSID_MAX_LEN];
+
+	/**
+	 * ssid_len - Length of SSID
+	 */
+	size_t ssid_len;
+
+	/**
+	 * freq - Operating channel of the group
+	 */
+	int freq;
+
+	/**
+	 * cb_ctx - Context to use with callback functions
+	 */
+	void *cb_ctx;
+
+	/**
+	 * ie_update - Notification of IE update
+	 * @ctx: Callback context from cb_ctx
+	 * @beacon_ies: P2P IE for Beacon frames or %NULL if no change
+	 * @proberesp_ies: P2P Ie for Probe Response frames
+	 *
+	 * P2P module uses this callback function to notify whenever the P2P IE
+	 * in Beacon or Probe Response frames should be updated based on group
+	 * events.
+	 *
+	 * The callee is responsible for freeing the returned buffer(s) with
+	 * wpabuf_free().
+	 */
+	void (*ie_update)(void *ctx, struct wpabuf *beacon_ies,
+			  struct wpabuf *proberesp_ies);
+
+	/**
+	 * idle_update - Notification of changes in group idle state
+	 * @ctx: Callback context from cb_ctx
+	 * @idle: Whether the group is idle (no associated stations)
+	 */
+	void (*idle_update)(void *ctx, int idle);
+};
+
+/**
+ * p2p_group_init - Initialize P2P group
+ * @p2p: P2P module context from p2p_init()
+ * @config: P2P group configuration (will be freed by p2p_group_deinit())
+ * Returns: Pointer to private data or %NULL on failure
+ *
+ * This function is used to initialize per-group P2P module context. Currently,
+ * this is only used to manage GO functionality and P2P clients do not need to
+ * create an instance of this per-group information.
+ */
+struct p2p_group * p2p_group_init(struct p2p_data *p2p,
+				  struct p2p_group_config *config);
+
+/**
+ * p2p_group_deinit - Deinitialize P2P group
+ * @group: P2P group context from p2p_group_init()
+ */
+void p2p_group_deinit(struct p2p_group *group);
+
+/**
+ * p2p_group_notif_assoc - Notification of P2P client association with GO
+ * @group: P2P group context from p2p_group_init()
+ * @addr: Interface address of the P2P client
+ * @ie: IEs from the (Re)association Request frame
+ * @len: Length of the ie buffer in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
+			  const u8 *ie, size_t len);
+
+/**
+ * p2p_group_assoc_resp_ie - Build P2P IE for (re)association response
+ * @group: P2P group context from p2p_group_init()
+ * @status: Status value (P2P_SC_SUCCESS if association succeeded)
+ * Returns: P2P IE for (Re)association Response or %NULL on failure
+ *
+ * The caller is responsible for freeing the returned buffer with
+ * wpabuf_free().
+ */
+struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status);
+
+/**
+ * p2p_group_notif_disassoc - Notification of P2P client disassociation from GO
+ * @group: P2P group context from p2p_group_init()
+ * @addr: Interface address of the P2P client
+ */
+void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr);
+
+/**
+ * p2p_group_notif_formation_done - Notification of completed group formation
+ * @group: P2P group context from p2p_group_init()
+ */
+void p2p_group_notif_formation_done(struct p2p_group *group);
+
+/**
+ * p2p_group_notif_noa - Notification of NoA change
+ * @group: P2P group context from p2p_group_init()
+ * @noa: Notice of Absence attribute payload, %NULL if none
+ * @noa_len: Length of noa buffer in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * Notify the P2P group management about a new NoA contents. This will be
+ * inserted into the P2P IEs in Beacon and Probe Response frames with rest of
+ * the group information.
+ */
+int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
+			size_t noa_len);
+
+/**
+ * p2p_group_match_dev_type - Match device types in group with requested type
+ * @group: P2P group context from p2p_group_init()
+ * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
+ * Returns: 1 on match, 0 on mismatch
+ *
+ * This function can be used to match the Requested Device Type attribute in
+ * WPS IE with the device types of a group member for deciding whether a GO
+ * should reply to a Probe Request frame. Match will be reported if the WPS IE
+ * is not requested any specific device type.
+ */
+int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps);
+
+/**
+ * p2p_group_match_dev_id - Match P2P Device Address in group with requested device id
+ */
+int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p);
+
+/**
+ * p2p_group_go_discover - Send GO Discoverability Request to a group client
+ * @group: P2P group context from p2p_group_init()
+ * Returns: 0 on success (frame scheduled); -1 if client was not found
+ */
+int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
+			  const u8 *searching_dev, int rx_freq);
+
+
+/* Generic helper functions */
+
+/**
+ * p2p_ie_text - Build text format description of P2P IE
+ * @p2p_ie: P2P IE
+ * @buf: Buffer for returning text
+ * @end: Pointer to the end of the buf area
+ * Returns: Number of octets written to the buffer or -1 on failure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end);
+
+/**
+ * p2p_scan_result_text - Build text format description of P2P IE
+ * @ies: Information elements from scan results
+ * @ies_len: ies buffer length in octets
+ * @buf: Buffer for returning text
+ * @end: Pointer to the end of the buf area
+ * Returns: Number of octets written to the buffer or -1 on failure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end);
+
+/**
+ * p2p_parse_dev_addr_in_p2p_ie - Parse P2P Device Address from a concatenated
+ * P2P IE
+ * @p2p_ie: P2P IE
+ * @dev_addr: Buffer for returning P2P Device Address
+ * Returns: 0 on success or -1 if P2P Device Address could not be parsed
+ */
+int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr);
+
+/**
+ * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s)
+ * @ies: Information elements from scan results
+ * @ies_len: ies buffer length in octets
+ * @dev_addr: Buffer for returning P2P Device Address
+ * Returns: 0 on success or -1 if P2P Device Address could not be parsed
+ */
+int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr);
+
+/**
+ * p2p_assoc_req_ie - Build P2P IE for (Re)Association Request frame
+ * @p2p: P2P module context from p2p_init()
+ * @bssid: BSSID
+ * @buf: Buffer for writing the P2P IE
+ * @len: Maximum buf length in octets
+ * @p2p_group: Whether this is for association with a P2P GO
+ * @p2p_ie: Reassembled P2P IE data from scan results or %NULL if none
+ * Returns: Number of octets written into buf or -1 on failure
+ */
+int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
+		     size_t len, int p2p_group, struct wpabuf *p2p_ie);
+
+/**
+ * p2p_scan_ie - Build P2P IE for Probe Request
+ * @p2p: P2P module context from p2p_init()
+ * @ies: Buffer for writing P2P IE
+ * @dev_id: Device ID to search for or %NULL for any
+ */
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id);
+
+/**
+ * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie
+ * @p2p: P2P module context from p2p_init()
+ * Returns: Number of octets that p2p_scan_ie() may add to the buffer
+ */
+size_t p2p_scan_ie_buf_len(struct p2p_data *p2p);
+
+/**
+ * p2p_go_params - Generate random P2P group parameters
+ * @p2p: P2P module context from p2p_init()
+ * @params: Buffer for parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params);
+
+/**
+ * p2p_get_group_capab - Get Group Capability from P2P IE data
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: Group Capability
+ */
+u8 p2p_get_group_capab(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_cross_connect_disallowed - Does WLAN AP disallows cross connection
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: 0 if cross connection is allow, 1 if not
+ */
+int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_go_dev_addr - Get P2P Device Address from P2P IE data
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: Pointer to P2P Device Address or %NULL if not included
+ */
+const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_peer_info - Get P2P peer information
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer or %NULL to indicate the first peer
+ * @next: Whether to select the peer entry following the one indicated by addr
+ * Returns: Pointer to peer info or %NULL if not found
+ */
+const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p,
+					       const u8 *addr, int next);
+
+/**
+ * p2p_get_peer_info_txt - Get internal P2P peer information in text format
+ * @info: Pointer to P2P peer info from p2p_get_peer_info()
+ * @buf: Buffer for returning text
+ * @buflen: Maximum buffer length
+ * Returns: Number of octets written to the buffer or -1 on failure
+ *
+ * Note: This information is internal to the P2P module and subject to change.
+ * As such, this should not really be used by external programs for purposes
+ * other than debugging.
+ */
+int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
+			  char *buf, size_t buflen);
+
+/**
+ * p2p_peer_known - Check whether P2P peer is known
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer
+ * Returns: 1 if the specified device is in the P2P peer table or 0 if not
+ */
+int p2p_peer_known(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_set_client_discoverability - Set client discoverability capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether client discoverability will be enabled
+ *
+ * This function can be used to disable (and re-enable) client discoverability.
+ * This capability is enabled by default and should not be disabled in normal
+ * use cases, i.e., this is mainly for testing purposes.
+ */
+void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled);
+
+/**
+ * p2p_set_managed_oper - Set managed P2P Device operations capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether managed P2P Device operations will be enabled
+ */
+void p2p_set_managed_oper(struct p2p_data *p2p, int enabled);
+
+/**
+ * p2p_config_get_random_social - Return a random social channel
+ * @p2p: P2P config
+ * @op_class: Selected operating class
+ * @op_channel: Selected social channel
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used before p2p_init is called. A random social channel
+ * from supports bands 2.4 GHz (channels 1,6,11) and 60 GHz (channel 2) is
+ * returned on success.
+ */
+int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class,
+				 u8 *op_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);
+
+int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
+			   u8 *iface_addr);
+int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
+			   u8 *dev_addr);
+
+void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_set_cross_connect - Set cross connection capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether cross connection will be enabled
+ */
+void p2p_set_cross_connect(struct p2p_data *p2p, int enabled);
+
+int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr);
+
+/**
+ * p2p_set_intra_bss_dist - Set intra BSS distribution
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether intra BSS distribution will be enabled
+ */
+void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled);
+
+int p2p_channels_includes_freq(const struct p2p_channels *channels,
+			       unsigned int freq);
+
+int p2p_channels_to_freqs(const struct p2p_channels *channels,
+			  int *freq_list, unsigned int max_len);
+
+/**
+ * p2p_supported_freq - Check whether channel is supported for P2P
+ * @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(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
+ * Returns: Preferred channel
+ */
+unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
+			       const struct p2p_channels *channels);
+
+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
+ * @p2p: P2P module context from p2p_init()
+ * @freq_24: Frequency (MHz) of best channel in 2.4 GHz band
+ * @freq_5: Frequency (MHz) of best channel in 5 GHz band
+ * @freq_overall: Frequency (MHz) of best channel overall
+ */
+void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5,
+			   int freq_overall);
+
+/**
+ * p2p_set_own_freq_preference - Set own preference for channel
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Frequency (MHz) of the preferred channel or 0 if no preference
+ *
+ * This function can be used to set a preference on the operating channel based
+ * on frequencies used on the other virtual interfaces that share the same
+ * radio. If non-zero, this is used to try to avoid multi-channel concurrency.
+ */
+void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq);
+
+const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p);
+
+/**
+ * p2p_get_group_num_members - Get number of members in group
+ * @group: P2P group context from p2p_group_init()
+ * Returns: Number of members in the group
+ */
+unsigned int p2p_get_group_num_members(struct p2p_group *group);
+
+/**
+ * p2p_client_limit_reached - Check if client limit is reached
+ * @group: P2P group context from p2p_group_init()
+ * Returns: 1 if no of clients limit reached
+ */
+int p2p_client_limit_reached(struct p2p_group *group);
+
+/**
+ * p2p_iterate_group_members - Iterate group members
+ * @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 Device Address for each call and %NULL for no more members
+ */
+const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next);
+
+/**
+ * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group
+ * @group: P2P group context from p2p_group_init()
+ * @addr: P2P Interface Address of the client
+ * Returns: P2P Device Address of the client if found or %NULL if no match
+ * found
+ */
+const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr);
+
+/**
+ * p2p_group_is_client_connected - Check whether a specific client is connected
+ * @group: P2P group context from p2p_group_init()
+ * @addr: P2P Device Address of the client
+ * Returns: 1 if client is connected or 0 if not
+ */
+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
+ * @next: Whether to select the peer entry following the one indicated by addr
+ * Returns: The first P2P peer info available or %NULL if no such peer exists
+ */
+const struct p2p_peer_info *
+p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next);
+
+/**
+ * p2p_remove_wps_vendor_extensions - Remove WPS vendor extensions
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p);
+
+/**
+ * p2p_add_wps_vendor_extension - Add a WPS vendor extension
+ * @p2p: P2P module context from p2p_init()
+ * @vendor_ext: The vendor extensions to add
+ * Returns: 0 on success, -1 on failure
+ *
+ * The wpabuf structures in the array are owned by the P2P
+ * module after this call.
+ */
+int p2p_add_wps_vendor_extension(struct p2p_data *p2p,
+				 const struct wpabuf *vendor_ext);
+
+/**
+ * p2p_set_oper_channel - Set the P2P operating channel
+ * @p2p: P2P module context from p2p_init()
+ * @op_reg_class: Operating regulatory class to set
+ * @op_channel: operating channel to set
+ * @cfg_op_channel : Whether op_channel is hardcoded in configuration
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
+			 int cfg_op_channel);
+
+/**
+ * p2p_set_pref_chan - Set P2P preferred channel list
+ * @p2p: P2P module context from p2p_init()
+ * @num_pref_chan: Number of entries in pref_chan list
+ * @pref_chan: Preferred channels or %NULL to remove preferences
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+		      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, 1 if an operation is in progress but not
+ * in search state, or 2 if search state operation is in progress
+ */
+int p2p_in_progress(struct p2p_data *p2p);
+
+const char * p2p_wps_method_text(enum p2p_wps_method method);
+
+/**
+ * p2p_set_config_timeout - Set local config timeouts
+ * @p2p: P2P module context from p2p_init()
+ * @go_timeout: Time in 10 ms units it takes to start the GO mode
+ * @client_timeout: Time in 10 ms units it takes to start the client mode
+ */
+void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
+			    u8 client_timeout);
+
+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);
+int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem);
+int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem);
+int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
+				  const struct wpabuf *elem);
+struct wpabuf * wifi_display_encaps(struct wpabuf *subelems);
+
+/**
+ * p2p_set_disc_int - Set min/max discoverable interval for p2p_find
+ * @p2p: P2P module context from p2p_init()
+ * @min_disc_int: minDiscoverableInterval (in units of 100 TU); default 1
+ * @max_disc_int: maxDiscoverableInterval (in units of 100 TU); default 3
+ * @max_disc_tu: Maximum number of TUs (1.024 ms) for discoverable interval; or
+ *	-1 not to limit
+ * Returns: 0 on success, or -1 on failure
+ *
+ * This function can be used to configure minDiscoverableInterval and
+ * maxDiscoverableInterval parameters for the Listen state during device
+ * discovery (p2p_find). A random number of 100 TU units is picked for each
+ * Listen state iteration from [min_disc_int,max_disc_int] range.
+ *
+ * max_disc_tu can be used to futher limit the discoverable duration. However,
+ * it should be noted that use of this parameter is not recommended since it
+ * would not be compliant with the P2P specification.
+ */
+int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int,
+		     int max_disc_tu);
+
+/**
+ * p2p_get_state_txt - Get current P2P state for debug purposes
+ * @p2p: P2P module context from p2p_init()
+ * Returns: Name of the current P2P module state
+ *
+ * It should be noted that the P2P module state names are internal information
+ * and subject to change at any point, i.e., this information should be used
+ * mainly for debugging purposes.
+ */
+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[SSID_MAX_LEN];
+	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);
+
+void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr);
+
+struct p2ps_advertisement *
+p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id);
+int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
+			const char *adv_str, u8 svc_state,
+			u16 config_methods, const char *svc_info,
+			const u8 *cpt_priority);
+int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id);
+void p2p_service_flush_asp(struct p2p_data *p2p);
+struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p);
+
+/**
+ * p2p_expire_peers - Periodic cleanup function to expire peers
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This is a cleanup function that the entity calling p2p_init() is
+ * expected to call periodically to clean up expired peer entries.
+ */
+void p2p_expire_peers(struct p2p_data *p2p);
+
+void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
+				const unsigned int *pref_freq_list,
+				unsigned int size);
+
+/**
+ * p2p_group_get_common_freqs - Get the group common frequencies
+ * @group: P2P group context from p2p_group_init()
+ * @common_freqs: On return will hold the group common frequencies
+ * @num: On return will hold the number of group common frequencies
+ * Returns: 0 on success, -1 otherwise
+ */
+int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
+			       unsigned int *num);
+
+#endif /* P2P_H */
diff --git a/hostap/src/p2p/p2p_build.c b/hostap/src/p2p/p2p_build.c
new file mode 100644
index 0000000..793d28b
--- /dev/null
+++ b/hostap/src/p2p/p2p_build.c
@@ -0,0 +1,835 @@
+/*
+ * P2P - IE builder
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/qca-vendor.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+
+
+void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token)
+{
+	wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC);
+	wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
+
+	wpabuf_put_u8(buf, subtype); /* OUI Subtype */
+	wpabuf_put_u8(buf, dialog_token);
+	wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
+}
+
+
+void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
+				   u8 dialog_token)
+{
+	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
+	wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
+
+	wpabuf_put_u8(buf, subtype); /* OUI Subtype */
+	wpabuf_put_u8(buf, dialog_token);
+	wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
+}
+
+
+u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf)
+{
+	u8 *len;
+
+	/* P2P IE header */
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	len = wpabuf_put(buf, 1); /* IE length to be filled */
+	wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
+	wpa_printf(MSG_DEBUG, "P2P: * P2P IE header");
+	return len;
+}
+
+
+void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len)
+{
+	/* Update P2P IE Length */
+	*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
+}
+
+
+void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab)
+{
+	/* P2P Capability */
+	wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY);
+	wpabuf_put_le16(buf, 2);
+	wpabuf_put_u8(buf, dev_capab); /* Device Capabilities */
+	wpabuf_put_u8(buf, group_capab); /* Group Capabilities */
+	wpa_printf(MSG_DEBUG, "P2P: * Capability dev=%02x group=%02x",
+		   dev_capab, group_capab);
+}
+
+
+void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent)
+{
+	/* Group Owner Intent */
+	wpabuf_put_u8(buf, P2P_ATTR_GROUP_OWNER_INTENT);
+	wpabuf_put_le16(buf, 1);
+	wpabuf_put_u8(buf, go_intent);
+	wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u Tie breaker %u",
+		   go_intent >> 1, go_intent & 0x01);
+}
+
+
+void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
+				u8 reg_class, u8 channel)
+{
+	/* Listen Channel */
+	wpabuf_put_u8(buf, P2P_ATTR_LISTEN_CHANNEL);
+	wpabuf_put_le16(buf, 5);
+	wpabuf_put_data(buf, country, 3);
+	wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
+	wpabuf_put_u8(buf, channel); /* Channel Number */
+	wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Regulatory Class %u "
+		   "Channel %u", reg_class, channel);
+}
+
+
+void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
+				   u8 reg_class, u8 channel)
+{
+	/* Operating Channel */
+	wpabuf_put_u8(buf, P2P_ATTR_OPERATING_CHANNEL);
+	wpabuf_put_le16(buf, 5);
+	wpabuf_put_data(buf, country, 3);
+	wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
+	wpabuf_put_u8(buf, channel); /* Channel Number */
+	wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: Regulatory Class %u "
+		   "Channel %u", reg_class, channel);
+}
+
+
+void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
+				   const u32 *preferred_freq_list,
+				   unsigned int size)
+{
+	unsigned int i, count = 0;
+	u8 op_class, op_channel;
+
+	if (!size)
+		return;
+
+	/*
+	 * First, determine the number of P2P supported channels in the
+	 * pref_freq_list returned from driver. This is needed for calculations
+	 * of the vendor IE size.
+	 */
+	for (i = 0; i < size; i++) {
+		if (p2p_freq_to_channel(preferred_freq_list[i], &op_class,
+					&op_channel) == 0)
+			count++;
+	}
+
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 4 + count * sizeof(u16));
+	wpabuf_put_be24(buf, OUI_QCA);
+	wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST);
+	for (i = 0; i < size; i++) {
+		if (p2p_freq_to_channel(preferred_freq_list[i], &op_class,
+					&op_channel) < 0) {
+			wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz",
+				   preferred_freq_list[i]);
+			continue;
+		}
+		wpabuf_put_u8(buf, op_class);
+		wpabuf_put_u8(buf, op_channel);
+	}
+}
+
+
+void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
+			      struct p2p_channels *chan)
+{
+	u8 *len;
+	size_t i;
+
+	/* Channel List */
+	wpabuf_put_u8(buf, P2P_ATTR_CHANNEL_LIST);
+	len = wpabuf_put(buf, 2); /* IE length to be filled */
+	wpabuf_put_data(buf, country, 3); /* Country String */
+
+	for (i = 0; i < chan->reg_classes; i++) {
+		struct p2p_reg_class *c = &chan->reg_class[i];
+		wpabuf_put_u8(buf, c->reg_class);
+		wpabuf_put_u8(buf, c->channels);
+		wpabuf_put_data(buf, c->channel, c->channels);
+	}
+
+	/* Update attribute length */
+	WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+	wpa_hexdump(MSG_DEBUG, "P2P: * Channel List",
+		    len + 2, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+void p2p_buf_add_status(struct wpabuf *buf, u8 status)
+{
+	/* Status */
+	wpabuf_put_u8(buf, P2P_ATTR_STATUS);
+	wpabuf_put_le16(buf, 1);
+	wpabuf_put_u8(buf, status);
+	wpa_printf(MSG_DEBUG, "P2P: * Status: %d", status);
+}
+
+
+void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
+			     struct p2p_device *peer)
+{
+	u8 *len;
+	u16 methods;
+	size_t nlen, i;
+
+	/* P2P Device Info */
+	wpabuf_put_u8(buf, P2P_ATTR_DEVICE_INFO);
+	len = wpabuf_put(buf, 2); /* IE length to be filled */
+
+	/* P2P Device address */
+	wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
+
+	/* Config Methods */
+	methods = 0;
+	if (peer && peer->wps_method != WPS_NOT_READY) {
+		if (peer->wps_method == WPS_PBC)
+			methods |= WPS_CONFIG_PUSHBUTTON;
+		else if (peer->wps_method == WPS_PIN_DISPLAY ||
+			 peer->wps_method == WPS_PIN_KEYPAD) {
+			methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+			methods |= WPS_CONFIG_P2PS;
+		}
+	} else if (p2p->cfg->config_methods) {
+		methods |= p2p->cfg->config_methods &
+			(WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY |
+			 WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS);
+	} else {
+		methods |= WPS_CONFIG_PUSHBUTTON;
+		methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+		methods |= WPS_CONFIG_P2PS;
+	}
+	wpabuf_put_be16(buf, methods);
+
+	/* Primary Device Type */
+	wpabuf_put_data(buf, p2p->cfg->pri_dev_type,
+			sizeof(p2p->cfg->pri_dev_type));
+
+	/* Number of Secondary Device Types */
+	wpabuf_put_u8(buf, p2p->cfg->num_sec_dev_types);
+
+	/* Secondary Device Type List */
+	for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
+		wpabuf_put_data(buf, p2p->cfg->sec_dev_type[i],
+				WPS_DEV_TYPE_LEN);
+
+	/* Device Name */
+	nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0;
+	wpabuf_put_be16(buf, ATTR_DEV_NAME);
+	wpabuf_put_be16(buf, nlen);
+	wpabuf_put_data(buf, p2p->cfg->dev_name, nlen);
+
+	/* Update attribute length */
+	WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+	wpa_printf(MSG_DEBUG, "P2P: * Device Info");
+}
+
+
+void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr)
+{
+	/* P2P Device ID */
+	wpabuf_put_u8(buf, P2P_ATTR_DEVICE_ID);
+	wpabuf_put_le16(buf, ETH_ALEN);
+	wpabuf_put_data(buf, dev_addr, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "P2P: * Device ID: " MACSTR, MAC2STR(dev_addr));
+}
+
+
+void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
+				u8 client_timeout)
+{
+	/* Configuration Timeout */
+	wpabuf_put_u8(buf, P2P_ATTR_CONFIGURATION_TIMEOUT);
+	wpabuf_put_le16(buf, 2);
+	wpabuf_put_u8(buf, go_timeout);
+	wpabuf_put_u8(buf, client_timeout);
+	wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout: GO %d (*10ms)  "
+		   "client %d (*10ms)", go_timeout, client_timeout);
+}
+
+
+void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr)
+{
+	/* Intended P2P Interface Address */
+	wpabuf_put_u8(buf, P2P_ATTR_INTENDED_INTERFACE_ADDR);
+	wpabuf_put_le16(buf, ETH_ALEN);
+	wpabuf_put_data(buf, interface_addr, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address " MACSTR,
+		   MAC2STR(interface_addr));
+}
+
+
+void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid)
+{
+	/* P2P Group BSSID */
+	wpabuf_put_u8(buf, P2P_ATTR_GROUP_BSSID);
+	wpabuf_put_le16(buf, ETH_ALEN);
+	wpabuf_put_data(buf, bssid, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID " MACSTR,
+		   MAC2STR(bssid));
+}
+
+
+void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
+			  const u8 *ssid, size_t ssid_len)
+{
+	/* P2P Group ID */
+	wpabuf_put_u8(buf, P2P_ATTR_GROUP_ID);
+	wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
+	wpabuf_put_data(buf, dev_addr, ETH_ALEN);
+	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);
+}
+
+
+void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags)
+{
+	/* Invitation Flags */
+	wpabuf_put_u8(buf, P2P_ATTR_INVITATION_FLAGS);
+	wpabuf_put_le16(buf, 1);
+	wpabuf_put_u8(buf, flags);
+	wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", flags);
+}
+
+
+static void p2p_buf_add_noa_desc(struct wpabuf *buf, struct p2p_noa_desc *desc)
+{
+	if (desc == NULL)
+		return;
+
+	wpabuf_put_u8(buf, desc->count_type);
+	wpabuf_put_le32(buf, desc->duration);
+	wpabuf_put_le32(buf, desc->interval);
+	wpabuf_put_le32(buf, desc->start_time);
+}
+
+
+void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
+		     struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2)
+{
+	/* Notice of Absence */
+	wpabuf_put_u8(buf, P2P_ATTR_NOTICE_OF_ABSENCE);
+	wpabuf_put_le16(buf, 2 + (desc1 ? 13 : 0) + (desc2 ? 13 : 0));
+	wpabuf_put_u8(buf, noa_index);
+	wpabuf_put_u8(buf, (opp_ps ? 0x80 : 0) | (ctwindow & 0x7f));
+	p2p_buf_add_noa_desc(buf, desc1);
+	p2p_buf_add_noa_desc(buf, desc2);
+	wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
+}
+
+
+void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
+				   u16 interval)
+{
+	/* Extended Listen Timing */
+	wpabuf_put_u8(buf, P2P_ATTR_EXT_LISTEN_TIMING);
+	wpabuf_put_le16(buf, 4);
+	wpabuf_put_le16(buf, period);
+	wpabuf_put_le16(buf, interval);
+	wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing (period %u msec  "
+		   "interval %u msec)", period, interval);
+}
+
+
+void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p)
+{
+	/* P2P Interface */
+	wpabuf_put_u8(buf, P2P_ATTR_INTERFACE);
+	wpabuf_put_le16(buf, ETH_ALEN + 1 + ETH_ALEN);
+	/* P2P Device address */
+	wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
+	/*
+	 * FIX: Fetch interface address list from driver. Do not include
+	 * the P2P Device address if it is never used as interface address.
+	 */
+	/* P2P Interface Address Count */
+	wpabuf_put_u8(buf, 1);
+	wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
+}
+
+
+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);
+}
+
+
+void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p)
+{
+	if (!p2p)
+		return;
+
+	/* Service Hash */
+	wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH);
+	wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN);
+	wpabuf_put_data(buf, p2p->p2ps_seek_hash,
+			p2p->p2ps_seek_count * P2PS_HASH_LEN);
+	wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash",
+		    p2p->p2ps_seek_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN);
+}
+
+
+void p2p_buf_add_session_info(struct wpabuf *buf, const char *info)
+{
+	size_t info_len = 0;
+
+	if (info && info[0])
+		info_len = os_strlen(info);
+
+	/* Session Information Data Info */
+	wpabuf_put_u8(buf, P2P_ATTR_SESSION_INFORMATION_DATA);
+	wpabuf_put_le16(buf, (u16) info_len);
+
+	if (info) {
+		wpabuf_put_data(buf, info, info_len);
+		wpa_printf(MSG_DEBUG, "P2P: * Session Info Data (%s)", info);
+	}
+}
+
+
+void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap)
+{
+	/* Connection Capability Info */
+	wpabuf_put_u8(buf, P2P_ATTR_CONNECTION_CAPABILITY);
+	wpabuf_put_le16(buf, 1);
+	wpabuf_put_u8(buf, connection_cap);
+	wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x",
+		   connection_cap);
+}
+
+
+void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac)
+{
+	if (!buf || !mac)
+		return;
+
+	/* Advertisement ID Info */
+	wpabuf_put_u8(buf, P2P_ATTR_ADVERTISEMENT_ID);
+	wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
+	wpabuf_put_le32(buf, id);
+	wpabuf_put_data(buf, mac, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID (%x) " MACSTR,
+		   id, MAC2STR(mac));
+}
+
+
+static int p2ps_wildcard_hash(struct p2p_data *p2p,
+			      const u8 *hash, u8 hash_count)
+{
+	u8 i;
+	const u8 *test = hash;
+
+	for (i = 0; i < hash_count; i++) {
+		if (os_memcmp(test, p2p->wild_card_hash, P2PS_HASH_LEN) == 0)
+			return 1;
+		test += P2PS_HASH_LEN;
+	}
+
+	return 0;
+}
+
+
+static int p2p_wfa_service_adv(struct p2p_data *p2p)
+{
+	struct p2ps_advertisement *adv;
+
+	for (adv = p2p->p2ps_adv_list; adv; adv = adv->next) {
+		if (os_strncmp(adv->svc_name, P2PS_WILD_HASH_STR,
+			       os_strlen(P2PS_WILD_HASH_STR)) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int p2p_buf_add_service_info(struct wpabuf *buf, struct p2p_data *p2p,
+				    u32 adv_id, u16 config_methods,
+				    const char *svc_name, u8 **ie_len, u8 **pos,
+				    size_t *total_len, u8 *attr_len)
+{
+	size_t svc_len;
+	size_t remaining;
+	size_t info_len;
+
+	p2p_dbg(p2p, "Add service info for %s (adv_id=%u)", svc_name, adv_id);
+	svc_len = os_strlen(svc_name);
+	info_len = sizeof(adv_id) + sizeof(config_methods) + sizeof(u8) +
+		svc_len;
+
+	if (info_len + *total_len > MAX_SVC_ADV_LEN) {
+		p2p_dbg(p2p,
+			"Unsufficient buffer, failed to add advertised service info");
+		return -1;
+	}
+
+	if (svc_len > 255) {
+		p2p_dbg(p2p,
+			"Invalid service name length (%u bytes), failed to add advertised service info",
+			(unsigned int) svc_len);
+		return -1;
+	}
+
+	if (*ie_len) {
+		int ie_data_len = (*pos - *ie_len) - 1;
+
+		if (ie_data_len < 0 || ie_data_len > 255) {
+			p2p_dbg(p2p,
+				"Invalid IE length, failed to add advertised service info");
+			return -1;
+		}
+		remaining = 255 - ie_data_len;
+	} else {
+		/*
+		 * Adding new P2P IE header takes 6 extra bytes:
+		 * - 2 byte IE header (1 byte IE id and 1 byte length)
+		 * - 4 bytes of IE_VENDOR_TYPE are reduced from 255 below
+		 */
+		*ie_len = p2p_buf_add_ie_hdr(buf);
+		remaining = 255 - 4;
+	}
+
+	if (remaining < sizeof(u32) + sizeof(u16) + sizeof(u8)) {
+		/*
+		 * Split adv_id, config_methods, and svc_name_len between two
+		 * IEs.
+		 */
+		size_t front = remaining;
+		size_t back = sizeof(u32) + sizeof(u16) + sizeof(u8) - front;
+		u8 holder[sizeof(u32) + sizeof(u16) + sizeof(u8)];
+
+		WPA_PUT_LE32(holder, adv_id);
+		WPA_PUT_BE16(&holder[sizeof(u32)], config_methods);
+		holder[sizeof(u32) + sizeof(u16)] = svc_len;
+
+		if (front)
+			wpabuf_put_data(buf, holder, front);
+
+		p2p_buf_update_ie_hdr(buf, *ie_len);
+		*ie_len = p2p_buf_add_ie_hdr(buf);
+
+		wpabuf_put_data(buf, &holder[front], back);
+		remaining = 255 - 4 - (sizeof(u32) + sizeof(u16) + sizeof(u8)) -
+			back;
+	} else {
+		wpabuf_put_le32(buf, adv_id);
+		wpabuf_put_be16(buf, config_methods);
+		wpabuf_put_u8(buf, svc_len);
+		remaining -= sizeof(adv_id) + sizeof(config_methods) +
+			sizeof(u8);
+	}
+
+	if (remaining < svc_len) {
+		/* split svc_name between two or three IEs */
+		size_t front = remaining;
+		size_t back = svc_len - front;
+
+		if (front)
+			wpabuf_put_data(buf, svc_name, front);
+
+		p2p_buf_update_ie_hdr(buf, *ie_len);
+		*ie_len = p2p_buf_add_ie_hdr(buf);
+
+		/* In rare cases, we must split across 3 attributes */
+		if (back > 255 - 4) {
+			wpabuf_put_data(buf, &svc_name[front], 255 - 4);
+			back -= 255 - 4;
+			front += 255 - 4;
+			p2p_buf_update_ie_hdr(buf, *ie_len);
+			*ie_len = p2p_buf_add_ie_hdr(buf);
+		}
+
+		wpabuf_put_data(buf, &svc_name[front], back);
+		remaining = 255 - 4 - back;
+	} else {
+		wpabuf_put_data(buf, svc_name, svc_len);
+		remaining -= svc_len;
+	}
+
+	p2p_buf_update_ie_hdr(buf, *ie_len);
+
+	/* set *ie_len to NULL if a new IE has to be added on the next call */
+	if (!remaining)
+		*ie_len = NULL;
+
+	/* set *pos to point to the next byte to update */
+	*pos = wpabuf_put(buf, 0);
+
+	*total_len += info_len;
+	WPA_PUT_LE16(attr_len, (u16) *total_len);
+	return 0;
+}
+
+
+void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
+				  u8 hash_count, const u8 *hash,
+				  struct p2ps_advertisement *adv_list)
+{
+	struct p2ps_advertisement *adv;
+	int p2ps_wildcard;
+	size_t total_len;
+	struct wpabuf *tmp_buf = NULL;
+	u8 *pos, *attr_len, *ie_len = NULL;
+
+	if (!adv_list || !hash || !hash_count)
+		return;
+
+	wpa_hexdump(MSG_DEBUG, "P2PS: Probe Request service hash values",
+		    hash, hash_count * P2PS_HASH_LEN);
+	p2ps_wildcard = p2ps_wildcard_hash(p2p, hash, hash_count) &&
+		p2p_wfa_service_adv(p2p);
+
+	/* Allocate temp buffer, allowing for overflow of 1 instance */
+	tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN);
+	if (!tmp_buf)
+		return;
+
+	/*
+	 * Attribute data can be split into a number of IEs. Start with the
+	 * first IE and the attribute headers here.
+	 */
+	ie_len = p2p_buf_add_ie_hdr(tmp_buf);
+
+	total_len = 0;
+
+	wpabuf_put_u8(tmp_buf, P2P_ATTR_ADVERTISED_SERVICE);
+	attr_len = wpabuf_put(tmp_buf, sizeof(u16));
+	WPA_PUT_LE16(attr_len, (u16) total_len);
+	p2p_buf_update_ie_hdr(tmp_buf, ie_len);
+	pos = wpabuf_put(tmp_buf, 0);
+
+	if (p2ps_wildcard) {
+		/* org.wi-fi.wfds match found */
+		p2p_buf_add_service_info(tmp_buf, p2p, 0, 0, P2PS_WILD_HASH_STR,
+					 &ie_len, &pos, &total_len, attr_len);
+	}
+
+	/* add advertised service info of matching services */
+	for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN;
+	     adv = adv->next) {
+		const u8 *test = hash;
+		u8 i;
+
+		for (i = 0; i < hash_count; i++) {
+			/* exact name hash match */
+			if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0 &&
+			    p2p_buf_add_service_info(tmp_buf, p2p,
+						     adv->id,
+						     adv->config_methods,
+						     adv->svc_name,
+						     &ie_len, &pos,
+						     &total_len,
+						     attr_len))
+				break;
+
+			test += P2PS_HASH_LEN;
+		}
+	}
+
+	if (total_len)
+		wpabuf_put_buf(buf, tmp_buf);
+	wpabuf_free(tmp_buf);
+}
+
+
+void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac)
+{
+	if (!buf || !mac)
+		return;
+
+	/* Session ID Info */
+	wpabuf_put_u8(buf, P2P_ATTR_SESSION_ID);
+	wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
+	wpabuf_put_le32(buf, id);
+	wpabuf_put_data(buf, mac, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "P2P: * Session ID Info (%x) " MACSTR,
+		   id, MAC2STR(mac));
+}
+
+
+void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, const u8 *mask)
+{
+	if (!buf || !len || !mask)
+		return;
+
+	/* Feature Capability */
+	wpabuf_put_u8(buf, P2P_ATTR_FEATURE_CAPABILITY);
+	wpabuf_put_le16(buf, len);
+	wpabuf_put_data(buf, mask, len);
+	wpa_printf(MSG_DEBUG, "P2P: * Feature Capability (%d)", len);
+}
+
+
+void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
+				       const u8 *ssid, size_t ssid_len)
+{
+	/* P2P Group ID */
+	wpabuf_put_u8(buf, P2P_ATTR_PERSISTENT_GROUP);
+	wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
+	wpabuf_put_data(buf, dev_addr, ETH_ALEN);
+	wpabuf_put_data(buf, ssid, ssid_len);
+	wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
+		   MAC2STR(dev_addr));
+}
+
+
+static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
+			      const char *val)
+{
+	size_t len;
+
+	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) {
+		/*
+		 * Some deployed WPS implementations fail to parse zeor-length
+		 * 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 0;
+	}
+#endif /* CONFIG_WPS_STRICT */
+	wpabuf_put_be16(buf, len);
+	if (val)
+		wpabuf_put_data(buf, val, len);
+	return 0;
+}
+
+
+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);
+
+	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);
+		wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d",
+			   pw_id);
+		wpabuf_put_be16(buf, pw_id);
+	}
+
+	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);
+
+		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);
+
+		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);
+	}
+
+	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);
+		wpabuf_put_data(buf, p2p->cfg->sec_dev_type,
+				WPS_DEV_TYPE_LEN *
+				p2p->cfg->num_sec_dev_types);
+	}
+
+	/* Add the WPS vendor extensions */
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		if (p2p->wps_vendor_ext[i] == NULL)
+			break;
+		if (wpabuf_tailroom(buf) <
+		    4 + wpabuf_len(p2p->wps_vendor_ext[i]))
+			continue;
+		wpabuf_put_be16(buf, ATTR_VENDOR_EXT);
+		wpabuf_put_be16(buf, wpabuf_len(p2p->wps_vendor_ext[i]));
+		wpabuf_put_buf(buf, p2p->wps_vendor_ext[i]);
+	}
+
+	p2p_buf_update_ie_hdr(buf, len);
+
+	return 0;
+}
diff --git a/hostap/src/p2p/p2p_dev_disc.c b/hostap/src/p2p/p2p_dev_disc.c
new file mode 100644
index 0000000..98805fe
--- /dev/null
+++ b/hostap/src/p2p/p2p_dev_disc.c
@@ -0,0 +1,329 @@
+/*
+ * Wi-Fi Direct - P2P Device Discoverability procedure
+ * Copyright (c) 2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static struct wpabuf * p2p_build_dev_disc_req(struct p2p_data *p2p,
+					      struct p2p_device *go,
+					      const u8 *dev_id)
+{
+	struct wpabuf *buf;
+	u8 *len;
+
+	buf = wpabuf_alloc(100);
+	if (buf == NULL)
+		return NULL;
+
+	go->dialog_token++;
+	if (go->dialog_token == 0)
+		go->dialog_token = 1;
+	p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_REQ, go->dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	p2p_buf_add_device_id(buf, dev_id);
+	p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid,
+			     go->oper_ssid_len);
+	p2p_buf_update_ie_hdr(buf, len);
+
+	return buf;
+}
+
+
+void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success)
+{
+	p2p_dbg(p2p, "Device Discoverability Request TX callback: success=%d",
+		success);
+
+	if (!success) {
+		/*
+		 * Use P2P find, if needed, to find the other device or to
+		 * retry device discoverability.
+		 */
+		p2p_set_state(p2p, P2P_CONNECT);
+		p2p_set_timeout(p2p, 0, 100000);
+		return;
+	}
+
+	p2p_dbg(p2p, "GO acknowledged Device Discoverability Request - wait for response");
+	/*
+	 * TODO: is the remain-on-channel from Action frame TX long enough for
+	 * most cases or should we try to increase its duration and/or start
+	 * another remain-on-channel if needed once the previous one expires?
+	 */
+}
+
+
+int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev)
+{
+	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) {
+		p2p_dbg(p2p, "Could not find peer entry for GO and frequency to send Device Discoverability Request");
+		return -1;
+	}
+
+	req = p2p_build_dev_disc_req(p2p, go, dev->info.p2p_device_addr);
+	if (req == NULL)
+		return -1;
+
+	p2p_dbg(p2p, "Sending Device Discoverability Request to GO " MACSTR
+		" for client " MACSTR,
+		MAC2STR(go->info.p2p_device_addr),
+		MAC2STR(dev->info.p2p_device_addr));
+
+	p2p->pending_client_disc_go = go;
+	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), wait_time) < 0) {
+		p2p_dbg(p2p, "Failed to send Action frame");
+		wpabuf_free(req);
+		/* TODO: how to recover from failure? */
+		return -1;
+	}
+
+	wpabuf_free(req);
+
+	return 0;
+}
+
+
+static struct wpabuf * p2p_build_dev_disc_resp(u8 dialog_token, u8 status)
+{
+	struct wpabuf *buf;
+	u8 *len;
+
+	buf = wpabuf_alloc(100);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_RESP, dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	p2p_buf_add_status(buf, status);
+	p2p_buf_update_ie_hdr(buf, len);
+
+	return buf;
+}
+
+
+void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success)
+{
+	p2p_dbg(p2p, "Device Discoverability Response TX callback: success=%d",
+		success);
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+}
+
+
+static void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token,
+				   const u8 *addr, int freq, u8 status)
+{
+	struct wpabuf *resp;
+
+	resp = p2p_build_dev_disc_resp(dialog_token, status);
+	if (resp == NULL)
+		return;
+
+	p2p_dbg(p2p, "Sending Device Discoverability Response to " MACSTR
+		" (status %u freq %d)",
+		MAC2STR(addr), status, freq);
+
+	p2p->pending_action_state = P2P_PENDING_DEV_DISC_RESPONSE;
+	if (p2p_send_action(p2p, freq, addr, p2p->cfg->dev_addr,
+			    p2p->cfg->dev_addr,
+			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+		p2p_dbg(p2p, "Failed to send Action frame");
+	}
+
+	wpabuf_free(resp);
+}
+
+
+void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
+			      const u8 *data, size_t len, int rx_freq)
+{
+	struct p2p_message msg;
+	size_t g;
+
+	p2p_dbg(p2p, "Received Device Discoverability Request from " MACSTR
+		" (freq=%d)", MAC2STR(sa), rx_freq);
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	if (msg.dialog_token == 0) {
+		p2p_dbg(p2p, "Invalid Dialog Token 0 (must be nonzero) in Device Discoverability Request");
+		p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
+				       P2P_SC_FAIL_INVALID_PARAMS);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (msg.device_id == NULL) {
+		p2p_dbg(p2p, "P2P Device ID attribute missing from Device Discoverability Request");
+		p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
+				       P2P_SC_FAIL_INVALID_PARAMS);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	for (g = 0; g < p2p->num_groups; g++) {
+		if (p2p_group_go_discover(p2p->groups[g], msg.device_id, sa,
+					  rx_freq) == 0) {
+			p2p_dbg(p2p, "Scheduled GO Discoverability Request for the target device");
+			/*
+			 * P2P group code will use a callback to indicate TX
+			 * status, so that we can reply to the request once the
+			 * target client has acknowledged the request or it has
+			 * timed out.
+			 */
+			p2p->pending_dev_disc_dialog_token = msg.dialog_token;
+			os_memcpy(p2p->pending_dev_disc_addr, sa, ETH_ALEN);
+			p2p->pending_dev_disc_freq = rx_freq;
+			p2p_parse_free(&msg);
+			return;
+		}
+	}
+
+	p2p_dbg(p2p, "Requested client was not found in any group or did not support client discoverability");
+	p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
+			       P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE);
+	p2p_parse_free(&msg);
+}
+
+
+void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa,
+			       const u8 *data, size_t len)
+{
+	struct p2p_message msg;
+	struct p2p_device *go;
+	u8 status;
+
+	p2p_dbg(p2p, "Received Device Discoverability Response from " MACSTR,
+		MAC2STR(sa));
+
+	go = p2p->pending_client_disc_go;
+	if (go == NULL ||
+	    os_memcmp(sa, go->info.p2p_device_addr, ETH_ALEN) != 0) {
+		p2p_dbg(p2p, "Ignore unexpected Device Discoverability Response");
+		return;
+	}
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	if (msg.status == NULL) {
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (msg.dialog_token != go->dialog_token) {
+		p2p_dbg(p2p, "Ignore Device Discoverability Response with unexpected dialog token %u (expected %u)",
+			msg.dialog_token, go->dialog_token);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	status = *msg.status;
+	p2p_parse_free(&msg);
+
+	p2p_dbg(p2p, "Device Discoverability Response status %u", status);
+
+	if (p2p->go_neg_peer == NULL ||
+	    os_memcmp(p2p->pending_client_disc_addr,
+		      p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) != 0 ||
+	    os_memcmp(p2p->go_neg_peer->member_in_go_dev,
+		      go->info.p2p_device_addr, ETH_ALEN) != 0) {
+		p2p_dbg(p2p, "No pending operation with the client discoverability peer anymore");
+		return;
+	}
+
+	if (status == 0) {
+		/*
+		 * Peer is expected to be awake for at least 100 TU; try to
+		 * connect immediately.
+		 */
+		p2p_dbg(p2p, "Client discoverability request succeeded");
+		if (p2p->state == P2P_CONNECT) {
+			/*
+			 * Change state to force the timeout to start in
+			 * P2P_CONNECT again without going through the short
+			 * Listen state.
+			 */
+			p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+			p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		}
+		p2p_set_timeout(p2p, 0, 0);
+	} else {
+		/*
+		 * Client discoverability request failed; try to connect from
+		 * timeout.
+		 */
+		p2p_dbg(p2p, "Client discoverability request failed");
+		p2p_set_timeout(p2p, 0, 500000);
+	}
+
+}
+
+
+void p2p_go_disc_req_cb(struct p2p_data *p2p, int success)
+{
+	p2p_dbg(p2p, "GO Discoverability Request TX callback: success=%d",
+		success);
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
+	if (p2p->pending_dev_disc_dialog_token == 0) {
+		p2p_dbg(p2p, "No pending Device Discoverability Request");
+		return;
+	}
+
+	p2p_send_dev_disc_resp(p2p, p2p->pending_dev_disc_dialog_token,
+			       p2p->pending_dev_disc_addr,
+			       p2p->pending_dev_disc_freq,
+			       success ? P2P_SC_SUCCESS :
+			       P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE);
+
+	p2p->pending_dev_disc_dialog_token = 0;
+}
+
+
+void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq)
+{
+	unsigned int tu;
+	struct wpabuf *ies;
+
+	p2p_dbg(p2p, "Received GO Discoverability Request - remain awake for 100 TU");
+
+	ies = p2p_build_probe_resp_ies(p2p, NULL, 0);
+	if (ies == NULL)
+		return;
+
+	/* Remain awake 100 TU on operating channel */
+	p2p->pending_client_disc_freq = rx_freq;
+	tu = 100;
+	if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, rx_freq, 1024 * tu / 1000,
+		    ies) < 0) {
+		p2p_dbg(p2p, "Failed to start listen mode for client discoverability");
+	}
+	wpabuf_free(ies);
+}
diff --git a/hostap/src/p2p/p2p_go_neg.c b/hostap/src/p2p/p2p_go_neg.c
new file mode 100644
index 0000000..83b4356
--- /dev/null
+++ b/hostap/src/p2p/p2p_go_neg.c
@@ -0,0 +1,1512 @@
+/*
+ * Wi-Fi Direct - P2P Group Owner Negotiation
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * 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 "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "wps/wps_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static int p2p_go_det(u8 own_intent, u8 peer_value)
+{
+	u8 peer_intent = peer_value >> 1;
+	if (own_intent == peer_intent) {
+		if (own_intent == P2P_MAX_GO_INTENT)
+			return -1; /* both devices want to become GO */
+
+		/* Use tie breaker bit to determine GO */
+		return (peer_value & 0x01) ? 0 : 1;
+	}
+
+	return own_intent > peer_intent;
+}
+
+
+int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own,
+			    struct p2p_device *dev,
+			    const u8 *channel_list, size_t channel_list_len)
+{
+	const u8 *pos, *end;
+	struct p2p_channels *ch;
+	size_t channels;
+	struct p2p_channels intersection;
+
+	ch = &dev->channels;
+	os_memset(ch, 0, sizeof(*ch));
+	pos = channel_list;
+	end = channel_list + channel_list_len;
+
+	if (end - pos < 3)
+		return -1;
+	os_memcpy(dev->country, pos, 3);
+	wpa_hexdump_ascii(MSG_DEBUG, "P2P: Peer country", pos, 3);
+	if (pos[2] != 0x04 && os_memcmp(pos, p2p->cfg->country, 2) != 0) {
+		p2p_info(p2p, "Mismatching country (ours=%c%c peer's=%c%c)",
+			p2p->cfg->country[0], p2p->cfg->country[1],
+			pos[0], pos[1]);
+		return -1;
+	}
+	pos += 3;
+
+	while (pos + 2 < end) {
+		struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes];
+		cl->reg_class = *pos++;
+		if (pos + 1 + pos[0] > end) {
+			p2p_info(p2p, "Invalid peer Channel List");
+			return -1;
+		}
+		channels = *pos++;
+		cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ?
+			P2P_MAX_REG_CLASS_CHANNELS : channels;
+		os_memcpy(cl->channel, pos, cl->channels);
+		pos += channels;
+		ch->reg_classes++;
+		if (ch->reg_classes == P2P_MAX_REG_CLASSES)
+			break;
+	}
+
+	p2p_channels_intersect(own, &dev->channels, &intersection);
+	p2p_dbg(p2p, "Own reg_classes %d peer reg_classes %d intersection reg_classes %d",
+		(int) own->reg_classes,
+		(int) dev->channels.reg_classes,
+		(int) intersection.reg_classes);
+	if (intersection.reg_classes == 0) {
+		p2p_info(p2p, "No common channels found");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int p2p_peer_channels(struct p2p_data *p2p, struct p2p_device *dev,
+			     const u8 *channel_list, size_t channel_list_len)
+{
+	return p2p_peer_channels_check(p2p, &p2p->channels, dev,
+				       channel_list, channel_list_len);
+}
+
+
+u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
+{
+	switch (wps_method) {
+	case WPS_PIN_DISPLAY:
+		return DEV_PW_REGISTRAR_SPECIFIED;
+	case WPS_PIN_KEYPAD:
+		return DEV_PW_USER_SPECIFIED;
+	case WPS_PBC:
+		return DEV_PW_PUSHBUTTON;
+	case WPS_NFC:
+		return DEV_PW_NFC_CONNECTION_HANDOVER;
+	case WPS_P2PS:
+		return DEV_PW_P2PS_DEFAULT;
+	default:
+		return DEV_PW_DEFAULT;
+	}
+}
+
+
+static const char * p2p_wps_method_str(enum p2p_wps_method wps_method)
+{
+	switch (wps_method) {
+	case WPS_PIN_DISPLAY:
+		return "Display";
+	case WPS_PIN_KEYPAD:
+		return "Keypad";
+	case WPS_PBC:
+		return "PBC";
+	case WPS_NFC:
+		return "NFC";
+	case WPS_P2PS:
+		return "P2PS";
+	default:
+		return "??";
+	}
+}
+
+
+static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
+					    struct p2p_device *peer)
+{
+	struct wpabuf *buf;
+	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;
+
+	p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	group_capab = 0;
+	if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
+		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+		if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
+			group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+	}
+	if (p2p->cross_connect)
+		group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+	if (p2p->cfg->p2p_intra_bss)
+		group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+			       group_capab);
+	p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | peer->tie_breaker);
+	p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
+	p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
+				   p2p->cfg->channel);
+	if (p2p->ext_listen_interval)
+		p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
+					      p2p->ext_listen_interval);
+	p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+	p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
+	p2p_buf_add_device_info(buf, p2p, peer);
+	p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+				      p2p->op_reg_class, p2p->op_channel);
+	p2p_buf_update_ie_hdr(buf, len);
+
+	p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list,
+				      p2p->num_pref_freq);
+
+	/* WPS IE with Device Password ID attribute */
+	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;
+}
+
+
+int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
+{
+	struct wpabuf *req;
+	int freq;
+
+	if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
+		u16 config_method;
+		p2p_dbg(p2p, "Use PD-before-GO-Neg workaround for " MACSTR,
+			MAC2STR(dev->info.p2p_device_addr));
+		if (dev->wps_method == WPS_PIN_DISPLAY)
+			config_method = WPS_CONFIG_KEYPAD;
+		else if (dev->wps_method == WPS_PIN_KEYPAD)
+			config_method = WPS_CONFIG_DISPLAY;
+		else if (dev->wps_method == WPS_PBC)
+			config_method = WPS_CONFIG_PUSHBUTTON;
+		else if (dev->wps_method == WPS_P2PS)
+			config_method = WPS_CONFIG_P2PS;
+		else
+			return -1;
+		return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr,
+					 NULL, config_method, 0, 0, 1);
+	}
+
+	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",
+			MAC2STR(dev->info.p2p_device_addr));
+		return -1;
+	}
+
+	req = p2p_build_go_neg_req(p2p, dev);
+	if (req == NULL)
+		return -1;
+	p2p_dbg(p2p, "Sending GO Negotiation Request");
+	p2p_set_state(p2p, P2P_CONNECT);
+	p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST;
+	p2p->go_neg_peer = dev;
+	eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
+	dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
+	dev->connect_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), 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->go_neg_req_sent++;
+
+	wpabuf_free(req);
+
+	return 0;
+}
+
+
+static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
+					     struct p2p_device *peer,
+					     u8 dialog_token, u8 status,
+					     u8 tie_breaker)
+{
+	struct wpabuf *buf;
+	u8 *len;
+	u8 group_capab;
+	size_t extra = 0;
+	u16 pw_id;
+
+	p2p_dbg(p2p, "Building GO Negotiation Response");
+
+#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_RESP])
+		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]);
+
+	buf = wpabuf_alloc(1000 + extra);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_RESP, dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	p2p_buf_add_status(buf, status);
+	group_capab = 0;
+	if (peer && peer->go_state == LOCAL_GO) {
+		if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
+			group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+			if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
+				group_capab |=
+					P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+		}
+		if (p2p->cross_connect)
+			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+		if (p2p->cfg->p2p_intra_bss)
+			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+	}
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+			       group_capab);
+	p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
+	p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
+	if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) {
+		p2p_dbg(p2p, "Omit Operating Channel attribute");
+	} else {
+		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+					      p2p->op_reg_class,
+					      p2p->op_channel);
+	}
+	p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+	if (status || peer == NULL) {
+		p2p_buf_add_channel_list(buf, p2p->cfg->country,
+					 &p2p->channels);
+	} else if (peer->go_state == REMOTE_GO) {
+		p2p_buf_add_channel_list(buf, p2p->cfg->country,
+					 &p2p->channels);
+	} else {
+		struct p2p_channels res;
+		p2p_channels_intersect(&p2p->channels, &peer->channels,
+				       &res);
+		p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
+	}
+	p2p_buf_add_device_info(buf, p2p, peer);
+	if (peer && peer->go_state == LOCAL_GO) {
+		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
+				     p2p->ssid_len);
+	}
+	p2p_buf_update_ie_hdr(buf, len);
+
+	/* WPS IE with Device Password ID attribute */
+	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;
+}
+
+
+/**
+ * p2p_reselect_channel - Re-select operating channel based on peer information
+ * @p2p: P2P module context from p2p_init()
+ * @intersection: Support channel list intersection from local and peer
+ *
+ * This function is used to re-select the best channel after having received
+ * information from the peer to allow supported channel lists to be intersected.
+ * This can be used to improve initial channel selection done in
+ * p2p_prepare_channel() prior to the start of GO Negotiation. In addition, this
+ * can be used for Invitation case.
+ */
+void p2p_reselect_channel(struct p2p_data *p2p,
+			  struct p2p_channels *intersection)
+{
+	struct p2p_reg_class *cl;
+	int freq;
+	u8 op_reg_class, op_channel;
+	unsigned int i;
+	const int op_classes_5ghz[] = { 124, 125, 115, 0 };
+	const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
+	const int op_classes_vht[] = { 128, 0 };
+
+	if (p2p->own_freq_preference > 0 &&
+	    p2p_freq_to_channel(p2p->own_freq_preference,
+				&op_reg_class, &op_channel) == 0 &&
+	    p2p_channels_includes(intersection, op_reg_class, op_channel)) {
+		p2p_dbg(p2p, "Pick own channel preference (reg_class %u channel %u) from intersection",
+			op_reg_class, op_channel);
+		p2p->op_reg_class = op_reg_class;
+		p2p->op_channel = op_channel;
+		return;
+	}
+
+	if (p2p->best_freq_overall > 0 &&
+	    p2p_freq_to_channel(p2p->best_freq_overall,
+				&op_reg_class, &op_channel) == 0 &&
+	    p2p_channels_includes(intersection, op_reg_class, op_channel)) {
+		p2p_dbg(p2p, "Pick best overall channel (reg_class %u channel %u) from intersection",
+			op_reg_class, op_channel);
+		p2p->op_reg_class = op_reg_class;
+		p2p->op_channel = op_channel;
+		return;
+	}
+
+	/* First, try to pick the best channel from another band */
+	freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel);
+	if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 &&
+	    !p2p_channels_includes(intersection, p2p->op_reg_class,
+				   p2p->op_channel) &&
+	    p2p_freq_to_channel(p2p->best_freq_5,
+				&op_reg_class, &op_channel) == 0 &&
+	    p2p_channels_includes(intersection, op_reg_class, op_channel)) {
+		p2p_dbg(p2p, "Pick best 5 GHz channel (reg_class %u channel %u) from intersection",
+			op_reg_class, op_channel);
+		p2p->op_reg_class = op_reg_class;
+		p2p->op_channel = op_channel;
+		return;
+	}
+
+	if (freq >= 4900 && freq < 6000 && p2p->best_freq_24 > 0 &&
+	    !p2p_channels_includes(intersection, p2p->op_reg_class,
+				   p2p->op_channel) &&
+	    p2p_freq_to_channel(p2p->best_freq_24,
+				&op_reg_class, &op_channel) == 0 &&
+	    p2p_channels_includes(intersection, op_reg_class, op_channel)) {
+		p2p_dbg(p2p, "Pick best 2.4 GHz channel (reg_class %u channel %u) from intersection",
+			op_reg_class, op_channel);
+		p2p->op_reg_class = op_reg_class;
+		p2p->op_channel = op_channel;
+		return;
+	}
+
+	/* Select channel with highest preference if the peer supports it */
+	for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) {
+		if (p2p_channels_includes(intersection,
+					  p2p->cfg->pref_chan[i].op_class,
+					  p2p->cfg->pref_chan[i].chan)) {
+			p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class;
+			p2p->op_channel = p2p->cfg->pref_chan[i].chan;
+			p2p_dbg(p2p, "Pick highest preferred channel (op_class %u channel %u) from intersection",
+				p2p->op_reg_class, p2p->op_channel);
+			return;
+		}
+	}
+
+	/* Try a channel where we might be able to use VHT */
+	if (p2p_channel_select(intersection, op_classes_vht,
+			       &p2p->op_reg_class, &p2p->op_channel) == 0) {
+		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 */
+	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 */
+	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;
+	}
+
+	/*
+	 * Try to see if the original channel is in the intersection. If
+	 * so, no need to change anything, as it already contains some
+	 * randomness.
+	 */
+	if (p2p_channels_includes(intersection, p2p->op_reg_class,
+				  p2p->op_channel)) {
+		p2p_dbg(p2p, "Using original operating class and channel (op_class %u channel %u) from intersection",
+			p2p->op_reg_class, p2p->op_channel);
+		return;
+	}
+
+	/*
+	 * Fall back to whatever is included in the channel intersection since
+	 * no better options seems to be available.
+	 */
+	cl = &intersection->reg_class[0];
+	p2p_dbg(p2p, "Pick another channel (reg_class %u channel %u) from intersection",
+		cl->reg_class, cl->channel[0]);
+	p2p->op_reg_class = cl->reg_class;
+	p2p->op_channel = cl->channel[0];
+}
+
+
+int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
+			  u8 *status)
+{
+	struct p2p_channels tmp, 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;
+		p2p_dbg(p2p, "No common channels found");
+		return -1;
+	}
+
+	if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
+				   p2p->op_channel)) {
+		if (dev->flags & P2P_DEV_FORCE_FREQ) {
+			*status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+			p2p_dbg(p2p, "Peer does not support the forced channel");
+			return -1;
+		}
+
+		p2p_dbg(p2p, "Selected operating channel (op_class %u channel %u) not acceptable to the peer",
+			p2p->op_reg_class, p2p->op_channel);
+		p2p_reselect_channel(p2p, &intersection);
+	} else if (!(dev->flags & P2P_DEV_FORCE_FREQ) &&
+		   !p2p->cfg->cfg_op_channel) {
+		p2p_dbg(p2p, "Try to optimize channel selection with peer information received; previously selected op_class %u channel %u",
+			p2p->op_reg_class, p2p->op_channel);
+		p2p_reselect_channel(p2p, &intersection);
+	}
+
+	if (!p2p->ssid_set) {
+		p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+		p2p->ssid_set = 1;
+	}
+
+	return 0;
+}
+
+
+static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go,
+					struct p2p_device *dev,
+					struct p2p_message *msg,
+					unsigned freq_list[], unsigned int size)
+{
+	u8 op_class, op_channel;
+	unsigned int oper_freq = 0, i, j;
+	int found = 0;
+
+	p2p_dbg(p2p,
+		"Peer didn't provide a preferred frequency list, see if any of our preferred channels are supported by peer device");
+
+	/*
+	 * Search for a common channel in our preferred frequency list which is
+	 * also supported by the peer device.
+	 */
+	for (i = 0; i < size && !found; i++) {
+		/*
+		 * Make sure that the common frequency is:
+		 * 1. Supported by peer
+		 * 2. Allowed for P2P use.
+		 */
+		oper_freq = freq_list[i];
+		if (p2p_freq_to_channel(oper_freq, &op_class,
+					&op_channel) < 0) {
+			p2p_dbg(p2p, "Unsupported frequency %u MHz", oper_freq);
+			continue;
+		}
+		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,
+				"Freq %u MHz (oper_class %u channel %u) not allowed for P2P",
+				oper_freq, op_class, op_channel);
+			break;
+		}
+		for (j = 0; j < msg->channel_list_len; j++) {
+
+			if (op_channel != msg->channel_list[j])
+				continue;
+
+			p2p->op_reg_class = op_class;
+			p2p->op_channel = op_channel;
+			os_memcpy(&p2p->channels, &p2p->cfg->channels,
+				  sizeof(struct p2p_channels));
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		p2p_dbg(p2p,
+			"Freq %d MHz is a preferred channel and is also supported by peer, use it as the operating channel",
+			oper_freq);
+	} else {
+		p2p_dbg(p2p,
+			"None of our preferred channels are supported by peer!. Use: %d MHz for oper_channel",
+			dev->oper_freq);
+	}
+}
+
+
+static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go,
+				     struct p2p_device *dev,
+				     struct p2p_message *msg,
+				     unsigned freq_list[], unsigned int size)
+{
+	u8 op_class, op_channel;
+	unsigned int oper_freq = 0, i, j;
+	int found = 0;
+
+	/*
+	 * Peer device supports a Preferred Frequency List.
+	 * Search for a common channel in the preferred frequency lists
+	 * of both peer and local devices.
+	 */
+	for (i = 0; i < size && !found; i++) {
+		for (j = 2; j < (msg->pref_freq_list_len / 2); j++) {
+			oper_freq = p2p_channel_to_freq(
+				msg->pref_freq_list[2 * j],
+				msg->pref_freq_list[2 * j + 1]);
+			if (freq_list[i] != oper_freq)
+				continue;
+
+			/*
+			 * Make sure that the found frequency is:
+			 * 1. Supported
+			 * 2. Allowed for P2P use.
+			 */
+			if (p2p_freq_to_channel(oper_freq, &op_class,
+						&op_channel) < 0) {
+				p2p_dbg(p2p, "Unsupported frequency %u MHz",
+					oper_freq);
+				continue;
+			}
+
+			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,
+					"Freq %u MHz (oper_class %u channel %u) not allowed for P2P",
+					oper_freq, op_class, op_channel);
+				break;
+			}
+			p2p->op_reg_class = op_class;
+			p2p->op_channel = op_channel;
+			os_memcpy(&p2p->channels, &p2p->cfg->channels,
+				  sizeof(struct p2p_channels));
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		p2p_dbg(p2p,
+			"Freq %d MHz is a common preferred channel for both peer and local, use it as operating channel",
+			oper_freq);
+	} else {
+		p2p_dbg(p2p,
+			"No common preferred channels found! Use: %d MHz for oper_channel",
+			dev->oper_freq);
+	}
+}
+
+
+void p2p_check_pref_chan(struct p2p_data *p2p, int go,
+			 struct p2p_device *dev, struct p2p_message *msg)
+{
+	unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size;
+	unsigned int i;
+	u8 op_class, op_channel;
+
+	/*
+	 * Use the preferred channel list from the driver only if there is no
+	 * forced_freq, e.g., P2P_CONNECT freq=..., and no preferred operating
+	 * channel hardcoded in the configuration file.
+	 */
+	if (!p2p->cfg->get_pref_freq_list || p2p->cfg->num_pref_chan ||
+	    (dev->flags & P2P_DEV_FORCE_FREQ) || p2p->cfg->cfg_op_channel)
+		return;
+
+	/* Obtain our preferred frequency list from driver based on P2P role. */
+	size = P2P_MAX_PREF_CHANNELS;
+	if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size,
+					 freq_list))
+		return;
+
+	/*
+	 * Check if peer's preference of operating channel is in
+	 * our preferred channel list.
+	 */
+	for (i = 0; i < size; i++) {
+		if (freq_list[i] == (unsigned int) dev->oper_freq)
+			break;
+	}
+	if (i != size) {
+		/* Peer operating channel preference matches our preference */
+		if (p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) <
+		    0) {
+			p2p_dbg(p2p,
+				"Peer operating channel preference is unsupported frequency %u MHz",
+				freq_list[i]);
+		} else {
+			p2p->op_reg_class = op_class;
+			p2p->op_channel = op_channel;
+			os_memcpy(&p2p->channels, &p2p->cfg->channels,
+				  sizeof(struct p2p_channels));
+			return;
+		}
+	}
+
+	p2p_dbg(p2p,
+		"Peer operating channel preference: %d MHz is not in our preferred channel list",
+		dev->oper_freq);
+
+	/*
+	  Check if peer's preferred channel list is
+	  * _not_ included in the GO Negotiation Request or Invitation Request.
+	  */
+	if (msg->pref_freq_list_len == 0)
+		p2p_check_pref_chan_no_recv(p2p, go, dev, msg, freq_list, size);
+	else
+		p2p_check_pref_chan_recv(p2p, go, dev, msg, freq_list, size);
+}
+
+
+void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
+			    const u8 *data, size_t len, int rx_freq)
+{
+	struct p2p_device *dev = NULL;
+	struct wpabuf *resp;
+	struct p2p_message msg;
+	u8 status = P2P_SC_FAIL_INVALID_PARAMS;
+	int tie_breaker = 0;
+	int freq;
+
+	p2p_dbg(p2p, "Received GO Negotiation Request from " MACSTR "(freq=%d)",
+		MAC2STR(sa), rx_freq);
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	if (!msg.capability) {
+		p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+		goto fail;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	if (msg.go_intent)
+		tie_breaker = *msg.go_intent & 0x01;
+	else {
+		p2p_dbg(p2p, "Mandatory GO Intent attribute missing from GO Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+		goto fail;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	if (!msg.config_timeout) {
+		p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+		goto fail;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	if (!msg.listen_channel) {
+		p2p_dbg(p2p, "No Listen Channel attribute received");
+		goto fail;
+	}
+	if (!msg.operating_channel) {
+		p2p_dbg(p2p, "No Operating Channel attribute received");
+		goto fail;
+	}
+	if (!msg.channel_list) {
+		p2p_dbg(p2p, "No Channel List attribute received");
+		goto fail;
+	}
+	if (!msg.intended_addr) {
+		p2p_dbg(p2p, "No Intended P2P Interface Address attribute received");
+		goto fail;
+	}
+	if (!msg.p2p_device_info) {
+		p2p_dbg(p2p, "No P2P Device Info attribute received");
+		goto fail;
+	}
+
+	if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) {
+		p2p_dbg(p2p, "Unexpected GO Negotiation Request SA=" MACSTR
+			" != dev_addr=" MACSTR,
+			MAC2STR(sa), MAC2STR(msg.p2p_device_addr));
+		goto fail;
+	}
+
+	dev = p2p_get_device(p2p, sa);
+
+	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, *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) ||
+		  !(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 (p2p->go_neg_peer && p2p->go_neg_peer == dev)
+		eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
+
+	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 &&
+		    (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;
+		p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa,
+					msg.dev_password_id,
+					msg.go_intent ? (*msg.go_intent >> 1) :
+					0);
+	} else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) {
+		p2p_dbg(p2p, "Already in Group Formation with another peer");
+		status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+	} else {
+		int go;
+
+		if (!p2p->go_neg_peer) {
+			p2p_dbg(p2p, "Starting GO Negotiation with previously authorized peer");
+			if (!(dev->flags & P2P_DEV_FORCE_FREQ)) {
+				p2p_dbg(p2p, "Use default channel settings");
+				p2p->op_reg_class = p2p->cfg->op_reg_class;
+				p2p->op_channel = p2p->cfg->op_channel;
+				os_memcpy(&p2p->channels, &p2p->cfg->channels,
+					  sizeof(struct p2p_channels));
+			} else {
+				p2p_dbg(p2p, "Use previously configured forced channel settings");
+			}
+		}
+
+		dev->flags &= ~P2P_DEV_NOT_YET_READY;
+
+		if (!msg.go_intent) {
+			p2p_dbg(p2p, "No GO Intent attribute received");
+			goto fail;
+		}
+		if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
+			p2p_dbg(p2p, "Invalid GO Intent value (%u) received",
+				*msg.go_intent >> 1);
+			goto fail;
+		}
+
+		if (dev->go_neg_req_sent &&
+		    os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) {
+			p2p_dbg(p2p, "Do not reply since peer has higher address and GO Neg Request already sent");
+			p2p_parse_free(&msg);
+			return;
+		}
+
+		go = p2p_go_det(p2p->go_intent, *msg.go_intent);
+		if (go < 0) {
+			p2p_dbg(p2p, "Incompatible GO Intent");
+			status = P2P_SC_FAIL_BOTH_GO_INTENT_15;
+			goto fail;
+		}
+
+		if (p2p_peer_channels(p2p, dev, msg.channel_list,
+				      msg.channel_list_len) < 0) {
+			p2p_dbg(p2p, "No common channels found");
+			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+			goto fail;
+		}
+
+		switch (msg.dev_password_id) {
+		case DEV_PW_REGISTRAR_SPECIFIED:
+			p2p_dbg(p2p, "PIN from peer Display");
+			if (dev->wps_method != WPS_PIN_KEYPAD) {
+				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;
+		case DEV_PW_USER_SPECIFIED:
+			p2p_dbg(p2p, "Peer entered PIN on Keypad");
+			if (dev->wps_method != WPS_PIN_DISPLAY) {
+				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;
+		case DEV_PW_PUSHBUTTON:
+			p2p_dbg(p2p, "Peer using pushbutton");
+			if (dev->wps_method != WPS_PBC) {
+				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;
+		case DEV_PW_P2PS_DEFAULT:
+			p2p_dbg(p2p, "Peer using P2PS pin");
+			if (dev->wps_method != WPS_P2PS) {
+				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;
+		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;
+			goto fail;
+		}
+
+		if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
+			goto fail;
+
+		dev->go_state = go ? LOCAL_GO : REMOTE_GO;
+		dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3],
+						     msg.operating_channel[4]);
+		p2p_dbg(p2p, "Peer operating channel preference: %d MHz",
+			dev->oper_freq);
+
+		/*
+		 * Use the driver preferred frequency list extension if
+		 * supported.
+		 */
+		p2p_check_pref_chan(p2p, go, dev, &msg);
+
+		if (msg.config_timeout) {
+			dev->go_timeout = msg.config_timeout[0];
+			dev->client_timeout = msg.config_timeout[1];
+		}
+
+		p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa));
+		if (p2p->state != P2P_IDLE)
+			p2p_stop_find_for_freq(p2p, rx_freq);
+		p2p_set_state(p2p, P2P_GO_NEG);
+		p2p_clear_timeout(p2p);
+		dev->dialog_token = msg.dialog_token;
+		os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+		p2p->go_neg_peer = dev;
+		eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
+		status = P2P_SC_SUCCESS;
+	}
+
+fail:
+	if (dev)
+		dev->status = status;
+	resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token, status,
+				     !tie_breaker);
+	p2p_parse_free(&msg);
+	if (resp == NULL)
+		return;
+	p2p_dbg(p2p, "Sending GO Negotiation Response");
+	if (rx_freq > 0)
+		freq = rx_freq;
+	else
+		freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+	if (freq < 0) {
+		p2p_dbg(p2p, "Unknown regulatory class/channel");
+		wpabuf_free(resp);
+		return;
+	}
+	if (status == P2P_SC_SUCCESS) {
+		p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE;
+		dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM;
+		if (os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) < 0) {
+			/*
+			 * Peer has smaller address, so the GO Negotiation
+			 * Response from us is expected to complete
+			 * negotiation. Ignore a GO Negotiation Response from
+			 * the peer if it happens to be received after this
+			 * point due to a race condition in GO Negotiation
+			 * Request transmission and processing.
+			 */
+			dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+		}
+	} else
+		p2p->pending_action_state =
+			P2P_PENDING_GO_NEG_RESPONSE_FAILURE;
+	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+			    p2p->cfg->dev_addr,
+			    wpabuf_head(resp), wpabuf_len(resp), 500) < 0) {
+		p2p_dbg(p2p, "Failed to send Action frame");
+	}
+
+	wpabuf_free(resp);
+}
+
+
+static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
+					     struct p2p_device *peer,
+					     u8 dialog_token, u8 status,
+					     const u8 *resp_chan, int go)
+{
+	struct wpabuf *buf;
+	u8 *len;
+	struct p2p_channels res;
+	u8 group_capab;
+	size_t extra = 0;
+
+	p2p_dbg(p2p, "Building GO Negotiation Confirm");
+
+#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_CONF])
+		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
+
+	buf = wpabuf_alloc(1000 + extra);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_CONF, dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	p2p_buf_add_status(buf, status);
+	group_capab = 0;
+	if (peer->go_state == LOCAL_GO) {
+		if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
+			group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+			if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
+				group_capab |=
+					P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+		}
+		if (p2p->cross_connect)
+			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+		if (p2p->cfg->p2p_intra_bss)
+			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+	}
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+			       group_capab);
+	if (go || resp_chan == NULL)
+		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+					      p2p->op_reg_class,
+					      p2p->op_channel);
+	else
+		p2p_buf_add_operating_channel(buf, (const char *) resp_chan,
+					      resp_chan[3], resp_chan[4]);
+	p2p_channels_intersect(&p2p->channels, &peer->channels, &res);
+	p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
+	if (go) {
+		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
+				     p2p->ssid_len);
+	}
+	p2p_buf_update_ie_hdr(buf, len);
+
+#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_CONF])
+		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
+
+	return buf;
+}
+
+
+void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq)
+{
+	struct p2p_device *dev;
+	int go = -1;
+	struct p2p_message msg;
+	u8 status = P2P_SC_SUCCESS;
+	int freq;
+
+	p2p_dbg(p2p, "Received GO Negotiation Response from " MACSTR
+		" (freq=%d)", MAC2STR(sa), rx_freq);
+	dev = p2p_get_device(p2p, sa);
+	if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
+	    dev != p2p->go_neg_peer) {
+		p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
+			MAC2STR(sa));
+		return;
+	}
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) {
+		p2p_dbg(p2p, "Was not expecting GO Negotiation Response - ignore");
+		p2p_parse_free(&msg);
+		return;
+	}
+	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+
+	if (msg.dialog_token != dev->dialog_token) {
+		p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)",
+			msg.dialog_token, dev->dialog_token);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (!msg.status) {
+		p2p_dbg(p2p, "No Status attribute received");
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+	if (*msg.status) {
+		p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status);
+		dev->go_neg_req_sent = 0;
+		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;
+			eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p,
+					     NULL);
+			eloop_register_timeout(120, 0, p2p_go_neg_wait_timeout,
+					       p2p, NULL);
+			if (p2p->state == P2P_CONNECT_LISTEN)
+				p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
+			else
+				p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
+			p2p_set_timeout(p2p, 0, 0);
+		} else {
+			p2p_dbg(p2p, "Stop GO Negotiation attempt");
+			p2p_go_neg_failed(p2p, *msg.status);
+		}
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (!msg.capability) {
+		p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	if (!msg.p2p_device_info) {
+		p2p_dbg(p2p, "Mandatory P2P Device Info attribute missing from GO Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	if (!msg.intended_addr) {
+		p2p_dbg(p2p, "No Intended P2P Interface Address attribute received");
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+
+	if (!msg.go_intent) {
+		p2p_dbg(p2p, "No GO Intent attribute received");
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+	if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
+		p2p_dbg(p2p, "Invalid GO Intent value (%u) received",
+			*msg.go_intent >> 1);
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+
+	go = p2p_go_det(p2p->go_intent, *msg.go_intent);
+	if (go < 0) {
+		p2p_dbg(p2p, "Incompatible GO Intent");
+		status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+		goto fail;
+	}
+
+	if (!go && msg.group_id) {
+		/* Store SSID for Provisioning step */
+		p2p->ssid_len = msg.group_id_len - ETH_ALEN;
+		os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len);
+	} else if (!go) {
+		p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Response");
+		p2p->ssid_len = 0;
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+
+	if (!msg.config_timeout) {
+		p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+#endif /* CONFIG_P2P_STRICT */
+	} else {
+		dev->go_timeout = msg.config_timeout[0];
+		dev->client_timeout = msg.config_timeout[1];
+	}
+
+	if (!msg.operating_channel && !go) {
+		/*
+		 * Note: P2P Client may omit Operating Channel attribute to
+		 * indicate it does not have a preference.
+		 */
+		p2p_dbg(p2p, "No Operating Channel attribute received");
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+	if (!msg.channel_list) {
+		p2p_dbg(p2p, "No Channel List attribute received");
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+
+	if (p2p_peer_channels(p2p, dev, msg.channel_list,
+			      msg.channel_list_len) < 0) {
+		p2p_dbg(p2p, "No common channels found");
+		status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+		goto fail;
+	}
+
+	if (msg.operating_channel) {
+		dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3],
+						     msg.operating_channel[4]);
+		p2p_dbg(p2p, "Peer operating channel preference: %d MHz",
+			dev->oper_freq);
+	} else
+		dev->oper_freq = 0;
+
+	switch (msg.dev_password_id) {
+	case DEV_PW_REGISTRAR_SPECIFIED:
+		p2p_dbg(p2p, "PIN from peer Display");
+		if (dev->wps_method != WPS_PIN_KEYPAD) {
+			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;
+	case DEV_PW_USER_SPECIFIED:
+		p2p_dbg(p2p, "Peer entered PIN on Keypad");
+		if (dev->wps_method != WPS_PIN_DISPLAY) {
+			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;
+	case DEV_PW_PUSHBUTTON:
+		p2p_dbg(p2p, "Peer using pushbutton");
+		if (dev->wps_method != WPS_PBC) {
+			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;
+	case DEV_PW_P2PS_DEFAULT:
+		p2p_dbg(p2p, "P2P: Peer using P2PS default pin");
+		if (dev->wps_method != WPS_P2PS) {
+			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;
+	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;
+		goto fail;
+	}
+
+	if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
+		goto fail;
+
+	/*
+	 * Use the driver preferred frequency list extension if local device is
+	 * GO.
+	 */
+	if (go)
+		p2p_check_pref_chan(p2p, go, dev, &msg);
+
+	p2p_set_state(p2p, P2P_GO_NEG);
+	p2p_clear_timeout(p2p);
+
+	p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa));
+	os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+
+fail:
+	/* 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 (dev->go_neg_conf == NULL)
+		return;
+	p2p_dbg(p2p, "Sending GO Negotiation Confirm");
+	if (status == P2P_SC_SUCCESS) {
+		p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM;
+		dev->go_state = go ? LOCAL_GO : REMOTE_GO;
+	} else
+		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	if (rx_freq > 0)
+		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(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, -1);
+		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, status);
+	}
+}
+
+
+void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len)
+{
+	struct p2p_device *dev;
+	struct p2p_message msg;
+
+	p2p_dbg(p2p, "Received GO Negotiation Confirm from " MACSTR,
+		MAC2STR(sa));
+	dev = p2p_get_device(p2p, sa);
+	if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
+	    dev != p2p->go_neg_peer) {
+		p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
+			MAC2STR(sa));
+		return;
+	}
+
+	if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) {
+		p2p_dbg(p2p, "Stopped waiting for TX status on GO Negotiation Response since we already received Confirmation");
+		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	}
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	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)",
+			msg.dialog_token, dev->dialog_token);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (!msg.status) {
+		p2p_dbg(p2p, "No Status attribute received");
+		p2p_parse_free(&msg);
+		return;
+	}
+	if (*msg.status) {
+		p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status);
+		p2p_go_neg_failed(p2p, *msg.status);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (dev->go_state == REMOTE_GO && msg.group_id) {
+		/* Store SSID for Provisioning step */
+		p2p->ssid_len = msg.group_id_len - ETH_ALEN;
+		os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len);
+	} else if (dev->go_state == REMOTE_GO) {
+		p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Confirmation");
+		p2p->ssid_len = 0;
+		p2p_go_neg_failed(p2p, P2P_SC_FAIL_INVALID_PARAMS);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (!msg.operating_channel) {
+		p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation");
+#ifdef CONFIG_P2P_STRICT
+		p2p_parse_free(&msg);
+		return;
+#endif /* CONFIG_P2P_STRICT */
+	} else if (dev->go_state == REMOTE_GO) {
+		int oper_freq = p2p_channel_to_freq(msg.operating_channel[3],
+						    msg.operating_channel[4]);
+		if (oper_freq != dev->oper_freq) {
+			p2p_dbg(p2p, "Updated peer (GO) operating channel preference from %d MHz to %d MHz",
+				dev->oper_freq, oper_freq);
+			dev->oper_freq = oper_freq;
+		}
+	}
+
+	if (!msg.channel_list) {
+		p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation");
+#ifdef CONFIG_P2P_STRICT
+		p2p_parse_free(&msg);
+		return;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	p2p_parse_free(&msg);
+
+	if (dev->go_state == UNKNOWN_GO) {
+		/*
+		 * This should not happen since GO negotiation has already
+		 * been completed.
+		 */
+		p2p_dbg(p2p, "Unexpected GO Neg state - do not know which end becomes GO");
+		return;
+	}
+
+	/*
+	 * The peer could have missed our ctrl::ack frame for GO Negotiation
+	 * Confirm and continue retransmitting the frame. To reduce the
+	 * likelihood of the peer not getting successful TX status for the
+	 * GO Negotiation Confirm frame, wait a short time here before starting
+	 * the group so that we will remain on the current channel to
+	 * acknowledge any possible retransmission from the peer.
+	 */
+	p2p_dbg(p2p, "20 ms wait on current channel before starting group");
+	os_sleep(0, 20000);
+
+	p2p_go_complete(p2p, dev);
+}
diff --git a/hostap/src/p2p/p2p_group.c b/hostap/src/p2p/p2p_group.c
new file mode 100644
index 0000000..0d66993
--- /dev/null
+++ b/hostap/src/p2p/p2p_group.c
@@ -0,0 +1,1113 @@
+/*
+ * Wi-Fi Direct - P2P group operations
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#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"
+#include "p2p.h"
+
+
+struct p2p_group_member {
+	struct p2p_group_member *next;
+	u8 addr[ETH_ALEN]; /* P2P Interface Address */
+	u8 dev_addr[ETH_ALEN]; /* P2P Device Address */
+	struct wpabuf *p2p_ie;
+	struct wpabuf *wfd_ie;
+	struct wpabuf *client_info;
+	u8 dev_capab;
+};
+
+/**
+ * struct p2p_group - Internal P2P module per-group data
+ */
+struct p2p_group {
+	struct p2p_data *p2p;
+	struct p2p_group_config *cfg;
+	struct p2p_group_member *members;
+	unsigned int num_members;
+	int group_formation;
+	int beacon_update;
+	struct wpabuf *noa;
+	struct wpabuf *wfd_ie;
+};
+
+
+struct p2p_group * p2p_group_init(struct p2p_data *p2p,
+				  struct p2p_group_config *config)
+{
+	struct p2p_group *group, **groups;
+
+	group = os_zalloc(sizeof(*group));
+	if (group == NULL)
+		return NULL;
+
+	groups = os_realloc_array(p2p->groups, p2p->num_groups + 1,
+				  sizeof(struct p2p_group *));
+	if (groups == NULL) {
+		os_free(group);
+		return NULL;
+	}
+	groups[p2p->num_groups++] = group;
+	p2p->groups = groups;
+
+	group->p2p = p2p;
+	group->cfg = config;
+	group->group_formation = 1;
+	group->beacon_update = 1;
+	p2p_group_update_ies(group);
+	group->cfg->idle_update(group->cfg->cb_ctx, 1);
+
+	return group;
+}
+
+
+static void p2p_group_free_member(struct p2p_group_member *m)
+{
+	wpabuf_free(m->wfd_ie);
+	wpabuf_free(m->p2p_ie);
+	wpabuf_free(m->client_info);
+	os_free(m);
+}
+
+
+static void p2p_group_free_members(struct p2p_group *group)
+{
+	struct p2p_group_member *m, *prev;
+	m = group->members;
+	group->members = NULL;
+	group->num_members = 0;
+	while (m) {
+		prev = m;
+		m = m->next;
+		p2p_group_free_member(prev);
+	}
+}
+
+
+void p2p_group_deinit(struct p2p_group *group)
+{
+	size_t g;
+	struct p2p_data *p2p;
+
+	if (group == NULL)
+		return;
+
+	p2p = group->p2p;
+
+	for (g = 0; g < p2p->num_groups; g++) {
+		if (p2p->groups[g] == group) {
+			while (g + 1 < p2p->num_groups) {
+				p2p->groups[g] = p2p->groups[g + 1];
+				g++;
+			}
+			p2p->num_groups--;
+			break;
+		}
+	}
+
+	p2p_group_free_members(group);
+	os_free(group->cfg);
+	wpabuf_free(group->noa);
+	wpabuf_free(group->wfd_ie);
+	os_free(group);
+}
+
+
+static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m)
+{
+	if (m->client_info == NULL)
+		return;
+	if (wpabuf_tailroom(ie) < wpabuf_len(m->client_info) + 1)
+		return;
+	wpabuf_put_buf(ie, m->client_info);
+}
+
+
+static void p2p_group_add_common_ies(struct p2p_group *group,
+				     struct wpabuf *ie)
+{
+	u8 dev_capab = group->p2p->dev_capab, group_capab = 0;
+
+	/* P2P Capability */
+	dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+	group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
+	if (group->cfg->persistent_group) {
+		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+		if (group->cfg->persistent_group == 2)
+			group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+	}
+	if (group->p2p->cfg->p2p_intra_bss)
+		group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+	if (group->group_formation)
+		group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION;
+	if (group->p2p->cross_connect)
+		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);
+}
+
+
+static void p2p_group_add_noa(struct wpabuf *ie, struct wpabuf *noa)
+{
+	if (noa == NULL)
+		return;
+	/* Notice of Absence */
+	wpabuf_put_u8(ie, P2P_ATTR_NOTICE_OF_ABSENCE);
+	wpabuf_put_le16(ie, wpabuf_len(noa));
+	wpabuf_put_buf(ie, noa);
+}
+
+
+static struct wpabuf * p2p_group_encaps_probe_resp(struct wpabuf *subelems)
+{
+	struct wpabuf *ie;
+	const u8 *pos, *end;
+	size_t len;
+
+	if (subelems == NULL)
+		return NULL;
+
+	len = wpabuf_len(subelems) + 100;
+
+	ie = wpabuf_alloc(len);
+	if (ie == NULL)
+		return NULL;
+
+	pos = wpabuf_head(subelems);
+	end = pos + wpabuf_len(subelems);
+
+	while (end > pos) {
+		size_t frag_len = end - pos;
+		if (frag_len > 251)
+			frag_len = 251;
+		wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+		wpabuf_put_u8(ie, 4 + frag_len);
+		wpabuf_put_be32(ie, P2P_IE_VENDOR_TYPE);
+		wpabuf_put_data(ie, pos, frag_len);
+		pos += frag_len;
+	}
+
+	return ie;
+}
+
+
+static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
+{
+	struct wpabuf *ie;
+	u8 *len;
+	size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (group->p2p->wfd_ie_beacon)
+		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;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (group->p2p->wfd_ie_beacon)
+		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);
+	p2p_group_add_noa(ie, group->noa);
+	p2p_buf_update_ie_hdr(ie, len);
+
+	return ie;
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g)
+{
+	return g->wfd_ie;
+}
+
+
+struct wpabuf * wifi_display_encaps(struct wpabuf *subelems)
+{
+	struct wpabuf *ie;
+	const u8 *pos, *end;
+
+	if (subelems == NULL)
+		return NULL;
+
+	ie = wpabuf_alloc(wpabuf_len(subelems) + 100);
+	if (ie == NULL)
+		return NULL;
+
+	pos = wpabuf_head(subelems);
+	end = pos + wpabuf_len(subelems);
+
+	while (end > pos) {
+		size_t frag_len = end - pos;
+		if (frag_len > 251)
+			frag_len = 251;
+		wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+		wpabuf_put_u8(ie, 4 + frag_len);
+		wpabuf_put_be32(ie, WFD_IE_VENDOR_TYPE);
+		wpabuf_put_data(ie, pos, frag_len);
+		pos += frag_len;
+	}
+
+	return ie;
+}
+
+
+static int wifi_display_add_dev_info_descr(struct wpabuf *buf,
+					   struct p2p_group_member *m)
+{
+	const u8 *pos, *end;
+	const u8 *dev_info = NULL;
+	const u8 *assoc_bssid = NULL;
+	const u8 *coupled_sink = NULL;
+	u8 zero_addr[ETH_ALEN];
+
+	if (m->wfd_ie == NULL)
+		return 0;
+
+	os_memset(zero_addr, 0, ETH_ALEN);
+	pos = wpabuf_head_u8(m->wfd_ie);
+	end = pos + wpabuf_len(m->wfd_ie);
+	while (pos + 1 < end) {
+		u8 id;
+		u16 len;
+
+		id = *pos++;
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (pos + len > end)
+			break;
+
+		switch (id) {
+		case WFD_SUBELEM_DEVICE_INFO:
+			if (len < 6)
+				break;
+			dev_info = pos;
+			break;
+		case WFD_SUBELEM_ASSOCIATED_BSSID:
+			if (len < ETH_ALEN)
+				break;
+			assoc_bssid = pos;
+			break;
+		case WFD_SUBELEM_COUPLED_SINK:
+			if (len < 1 + ETH_ALEN)
+				break;
+			coupled_sink = pos;
+			break;
+		}
+
+		pos += len;
+	}
+
+	if (dev_info == NULL)
+		return 0;
+
+	wpabuf_put_u8(buf, 23);
+	wpabuf_put_data(buf, m->dev_addr, ETH_ALEN);
+	if (assoc_bssid)
+		wpabuf_put_data(buf, assoc_bssid, ETH_ALEN);
+	else
+		wpabuf_put_data(buf, zero_addr, ETH_ALEN);
+	wpabuf_put_data(buf, dev_info, 2); /* WFD Device Info */
+	wpabuf_put_data(buf, dev_info + 4, 2); /* WFD Device Max Throughput */
+	if (coupled_sink) {
+		wpabuf_put_data(buf, coupled_sink, 1 + ETH_ALEN);
+	} else {
+		wpabuf_put_u8(buf, 0);
+		wpabuf_put_data(buf, zero_addr, ETH_ALEN);
+	}
+
+	return 1;
+}
+
+
+static struct wpabuf *
+wifi_display_build_go_ie(struct p2p_group *group)
+{
+	struct wpabuf *wfd_subelems, *wfd_ie;
+	struct p2p_group_member *m;
+	u8 *len;
+	unsigned int count = 0;
+
+	if (!group->p2p->wfd_ie_probe_resp)
+		return NULL;
+
+	wfd_subelems = wpabuf_alloc(wpabuf_len(group->p2p->wfd_ie_probe_resp) +
+				    group->num_members * 24 + 100);
+	if (wfd_subelems == NULL)
+		return NULL;
+	if (group->p2p->wfd_dev_info)
+		wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info);
+	if (group->p2p->wfd_assoc_bssid)
+		wpabuf_put_buf(wfd_subelems,
+			       group->p2p->wfd_assoc_bssid);
+	if (group->p2p->wfd_coupled_sink_info)
+		wpabuf_put_buf(wfd_subelems,
+			       group->p2p->wfd_coupled_sink_info);
+
+	/* Build WFD Session Info */
+	wpabuf_put_u8(wfd_subelems, WFD_SUBELEM_SESSION_INFO);
+	len = wpabuf_put(wfd_subelems, 2);
+	m = group->members;
+	while (m) {
+		if (wifi_display_add_dev_info_descr(wfd_subelems, m))
+			count++;
+		m = m->next;
+	}
+
+	if (count == 0) {
+		/* No Wi-Fi Display clients - do not include subelement */
+		wfd_subelems->used -= 3;
+	} else {
+		WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len -
+			     2);
+		p2p_dbg(group->p2p, "WFD: WFD Session Info: %u descriptors",
+			count);
+	}
+
+	wfd_ie = wifi_display_encaps(wfd_subelems);
+	wpabuf_free(wfd_subelems);
+
+	return wfd_ie;
+}
+
+static void wifi_display_group_update(struct p2p_group *group)
+{
+	wpabuf_free(group->wfd_ie);
+	group->wfd_ie = wifi_display_build_go_ie(group);
+}
+
+#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;
+
+	p2p_subelems = wpabuf_alloc(500);
+	if (p2p_subelems == NULL)
+		return NULL;
+
+	p2p_group_add_common_ies(group, p2p_subelems);
+	p2p_group_add_noa(p2p_subelems, group->noa);
+
+	/* P2P Device Info */
+	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)
+		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);
+		ie = wpabuf_concat(wfd, ie);
+	}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	return ie;
+}
+
+
+void p2p_group_update_ies(struct p2p_group *group)
+{
+	struct wpabuf *beacon_ie;
+	struct wpabuf *probe_resp_ie;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	wifi_display_group_update(group);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	probe_resp_ie = p2p_group_build_probe_resp_ie(group);
+	if (probe_resp_ie == NULL)
+		return;
+	wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Probe Response P2P IE",
+			probe_resp_ie);
+
+	if (group->beacon_update) {
+		beacon_ie = p2p_group_build_beacon_ie(group);
+		if (beacon_ie)
+			group->beacon_update = 0;
+		wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Beacon P2P IE",
+				beacon_ie);
+	} else
+		beacon_ie = NULL;
+
+	group->cfg->ie_update(group->cfg->cb_ctx, beacon_ie, probe_resp_ie);
+}
+
+
+/**
+ * p2p_build_client_info - Build P2P Client Info Descriptor
+ * @addr: MAC address of the peer device
+ * @p2p_ie: P2P IE from (Re)Association Request
+ * @dev_capab: Buffer for returning Device Capability
+ * @dev_addr: Buffer for returning P2P Device Address
+ * Returns: P2P Client Info Descriptor or %NULL on failure
+ *
+ * This function builds P2P Client Info Descriptor based on the information
+ * available from (Re)Association Request frame. Group owner can use this to
+ * build the P2P Group Info attribute for Probe Response frames.
+ */
+static struct wpabuf * p2p_build_client_info(const u8 *addr,
+					     struct wpabuf *p2p_ie,
+					     u8 *dev_capab, u8 *dev_addr)
+{
+	const u8 *spos;
+	struct p2p_message msg;
+	u8 *len_pos;
+	struct wpabuf *buf;
+
+	if (p2p_ie == NULL)
+		return NULL;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(p2p_ie, &msg) ||
+	    msg.capability == NULL || msg.p2p_device_info == NULL)
+		return NULL;
+
+	buf = wpabuf_alloc(ETH_ALEN + 1 + 1 + msg.p2p_device_info_len);
+	if (buf == NULL)
+		return NULL;
+
+	*dev_capab = msg.capability[0];
+	os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+
+	spos = msg.p2p_device_info; /* P2P Device address */
+
+	/* P2P Client Info Descriptor */
+	/* Length to be set */
+	len_pos = wpabuf_put(buf, 1);
+	/* P2P Device address */
+	wpabuf_put_data(buf, spos, ETH_ALEN);
+	/* P2P Interface address */
+	wpabuf_put_data(buf, addr, ETH_ALEN);
+	/* Device Capability Bitmap */
+	wpabuf_put_u8(buf, msg.capability[0]);
+	/*
+	 * Config Methods, Primary Device Type, Number of Secondary Device
+	 * Types, Secondary Device Type List, Device Name copied from
+	 * Device Info
+	 */
+	wpabuf_put_data(buf, spos + ETH_ALEN,
+			msg.p2p_device_info_len - ETH_ALEN);
+
+	*len_pos = wpabuf_len(buf) - 1;
+
+
+	return buf;
+}
+
+
+static int p2p_group_remove_member(struct p2p_group *group, const u8 *addr)
+{
+	struct p2p_group_member *m, *prev;
+
+	if (group == NULL)
+		return 0;
+
+	m = group->members;
+	prev = NULL;
+	while (m) {
+		if (os_memcmp(m->addr, addr, ETH_ALEN) == 0)
+			break;
+		prev = m;
+		m = m->next;
+	}
+
+	if (m == NULL)
+		return 0;
+
+	if (prev)
+		prev->next = m->next;
+	else
+		group->members = m->next;
+	p2p_group_free_member(m);
+	group->num_members--;
+
+	return 1;
+}
+
+
+int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
+			  const u8 *ie, size_t len)
+{
+	struct p2p_group_member *m;
+
+	if (group == NULL)
+		return -1;
+
+	p2p_add_device(group->p2p, addr, 0, NULL, 0, ie, len, 0);
+
+	m = os_zalloc(sizeof(*m));
+	if (m == NULL)
+		return -1;
+	os_memcpy(m->addr, addr, ETH_ALEN);
+	m->p2p_ie = ieee802_11_vendor_ie_concat(ie, len, P2P_IE_VENDOR_TYPE);
+	if (m->p2p_ie) {
+		m->client_info = p2p_build_client_info(addr, m->p2p_ie,
+						       &m->dev_capab,
+						       m->dev_addr);
+	}
+#ifdef CONFIG_WIFI_DISPLAY
+	m->wfd_ie = ieee802_11_vendor_ie_concat(ie, len, WFD_IE_VENDOR_TYPE);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	p2p_group_remove_member(group, addr);
+
+	m->next = group->members;
+	group->members = m;
+	group->num_members++;
+	p2p_dbg(group->p2p,  "Add client " MACSTR
+		" to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u",
+		MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0,
+		m->client_info ? 1 : 0,
+		group->num_members, group->cfg->max_clients);
+	if (group->num_members == group->cfg->max_clients)
+		group->beacon_update = 1;
+	p2p_group_update_ies(group);
+	if (group->num_members == 1)
+		group->cfg->idle_update(group->cfg->cb_ctx, 0);
+
+	return 0;
+}
+
+
+struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
+{
+	struct wpabuf *resp;
+	u8 *rlen;
+	size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (group->wfd_ie)
+		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
+	 *	denied)
+	 * Extended Listen Timing (may be present)
+	 */
+	resp = wpabuf_alloc(20 + extra);
+	if (resp == NULL)
+		return NULL;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (group->wfd_ie)
+		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);
+	p2p_buf_update_ie_hdr(resp, rlen);
+
+	return resp;
+}
+
+
+void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr)
+{
+	if (p2p_group_remove_member(group, addr)) {
+		p2p_dbg(group->p2p, "Remove client " MACSTR
+			" from group; num_members=%u/%u",
+			MAC2STR(addr), group->num_members,
+			group->cfg->max_clients);
+		if (group->num_members == group->cfg->max_clients - 1)
+			group->beacon_update = 1;
+		p2p_group_update_ies(group);
+		if (group->num_members == 0)
+			group->cfg->idle_update(group->cfg->cb_ctx, 1);
+	}
+}
+
+
+/**
+ * p2p_match_dev_type_member - Match client device type with requested type
+ * @m: Group member
+ * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
+ * Returns: 1 on match, 0 on mismatch
+ *
+ * This function can be used to match the Requested Device Type attribute in
+ * WPS IE with the device types of a group member for deciding whether a GO
+ * should reply to a Probe Request frame.
+ */
+static int p2p_match_dev_type_member(struct p2p_group_member *m,
+				     struct wpabuf *wps)
+{
+	const u8 *pos, *end;
+	struct wps_parse_attr attr;
+	u8 num_sec;
+
+	if (m->client_info == NULL || wps == NULL)
+		return 0;
+
+	pos = wpabuf_head(m->client_info);
+	end = pos + wpabuf_len(m->client_info);
+
+	pos += 1 + 2 * ETH_ALEN + 1 + 2;
+	if (end - pos < WPS_DEV_TYPE_LEN + 1)
+		return 0;
+
+	if (wps_parse_msg(wps, &attr))
+		return 1; /* assume no Requested Device Type attributes */
+
+	if (attr.num_req_dev_type == 0)
+		return 1; /* no Requested Device Type attributes -> match */
+
+	if (dev_type_list_match(pos, attr.req_dev_type, attr.num_req_dev_type))
+		return 1; /* Match with client Primary Device Type */
+
+	pos += WPS_DEV_TYPE_LEN;
+	num_sec = *pos++;
+	if (end - pos < num_sec * WPS_DEV_TYPE_LEN)
+		return 0;
+	while (num_sec > 0) {
+		num_sec--;
+		if (dev_type_list_match(pos, attr.req_dev_type,
+					attr.num_req_dev_type))
+			return 1; /* Match with client Secondary Device Type */
+		pos += WPS_DEV_TYPE_LEN;
+	}
+
+	/* No matching device type found */
+	return 0;
+}
+
+
+int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps)
+{
+	struct p2p_group_member *m;
+
+	if (p2p_match_dev_type(group->p2p, wps))
+		return 1; /* Match with own device type */
+
+	for (m = group->members; m; m = m->next) {
+		if (p2p_match_dev_type_member(m, wps))
+			return 1; /* Match with group client device type */
+	}
+
+	/* No match with Requested Device Type */
+	return 0;
+}
+
+
+int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p)
+{
+	struct p2p_group_member *m;
+	struct p2p_message msg;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(p2p, &msg))
+		return 1; /* Failed to parse - assume no filter on Device ID */
+
+	if (!msg.device_id)
+		return 1; /* No filter on Device ID */
+
+	if (os_memcmp(msg.device_id, group->p2p->cfg->dev_addr, ETH_ALEN) == 0)
+		return 1; /* Match with our P2P Device Address */
+
+	for (m = group->members; m; m = m->next) {
+		if (os_memcmp(msg.device_id, m->dev_addr, ETH_ALEN) == 0)
+			return 1; /* Match with group client P2P Device Address */
+	}
+
+	/* No match with Device ID */
+	return 0;
+}
+
+
+void p2p_group_notif_formation_done(struct p2p_group *group)
+{
+	if (group == NULL)
+		return;
+	group->group_formation = 0;
+	group->beacon_update = 1;
+	p2p_group_update_ies(group);
+}
+
+
+int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
+			size_t noa_len)
+{
+	if (noa == NULL) {
+		wpabuf_free(group->noa);
+		group->noa = NULL;
+	} else {
+		if (group->noa) {
+			if (wpabuf_size(group->noa) >= noa_len) {
+				group->noa->used = 0;
+				wpabuf_put_data(group->noa, noa, noa_len);
+			} else {
+				wpabuf_free(group->noa);
+				group->noa = NULL;
+			}
+		}
+
+		if (!group->noa) {
+			group->noa = wpabuf_alloc_copy(noa, noa_len);
+			if (group->noa == NULL)
+				return -1;
+		}
+	}
+
+	group->beacon_update = 1;
+	p2p_group_update_ies(group);
+	return 0;
+}
+
+
+static struct p2p_group_member * p2p_group_get_client(struct p2p_group *group,
+						      const u8 *dev_id)
+{
+	struct p2p_group_member *m;
+
+	for (m = group->members; m; m = m->next) {
+		if (os_memcmp(dev_id, m->dev_addr, ETH_ALEN) == 0)
+			return m;
+	}
+
+	return NULL;
+}
+
+
+static struct p2p_group_member * p2p_group_get_client_iface(
+	struct p2p_group *group, const u8 *interface_addr)
+{
+	struct p2p_group_member *m;
+
+	for (m = group->members; m; m = m->next) {
+		if (os_memcmp(interface_addr, m->addr, ETH_ALEN) == 0)
+			return m;
+	}
+
+	return NULL;
+}
+
+
+const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr)
+{
+	struct p2p_group_member *m;
+
+	if (group == NULL)
+		return NULL;
+	m = p2p_group_get_client_iface(group, addr);
+	if (m && !is_zero_ether_addr(m->dev_addr))
+		return m->dev_addr;
+	return NULL;
+}
+
+
+static struct wpabuf * p2p_build_go_disc_req(void)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(100);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_buf_add_action_hdr(buf, P2P_GO_DISC_REQ, 0);
+
+	return buf;
+}
+
+
+int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
+			  const u8 *searching_dev, int rx_freq)
+{
+	struct p2p_group_member *m;
+	struct wpabuf *req;
+	struct p2p_data *p2p = group->p2p;
+	int freq;
+
+	m = p2p_group_get_client(group, dev_id);
+	if (m == NULL || m->client_info == NULL) {
+		p2p_dbg(group->p2p, "Requested client was not in this group "
+			MACSTR, MAC2STR(group->cfg->interface_addr));
+		return -1;
+	}
+
+	if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+		p2p_dbg(group->p2p, "Requested client does not support client discoverability");
+		return -1;
+	}
+
+	p2p_dbg(group->p2p, "Schedule GO Discoverability Request to be sent to "
+		MACSTR, MAC2STR(dev_id));
+
+	req = p2p_build_go_disc_req();
+	if (req == NULL)
+		return -1;
+
+	/* TODO: Should really use group operating frequency here */
+	freq = rx_freq;
+
+	p2p->pending_action_state = P2P_PENDING_GO_DISC_REQ;
+	if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr,
+				  group->cfg->interface_addr,
+				  group->cfg->interface_addr,
+				  wpabuf_head(req), wpabuf_len(req), 200) < 0)
+	{
+		p2p_dbg(p2p, "Failed to send Action frame");
+	}
+
+	wpabuf_free(req);
+
+	return 0;
+}
+
+
+const u8 * p2p_group_get_interface_addr(struct p2p_group *group)
+{
+	return group->cfg->interface_addr;
+}
+
+
+u8 p2p_group_presence_req(struct p2p_group *group,
+			  const u8 *client_interface_addr,
+			  const u8 *noa, size_t noa_len)
+{
+	struct p2p_group_member *m;
+	u8 curr_noa[50];
+	int curr_noa_len;
+
+	m = p2p_group_get_client_iface(group, client_interface_addr);
+	if (m == NULL || m->client_info == NULL) {
+		p2p_dbg(group->p2p, "Client was not in this group");
+		return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "P2P: Presence Request NoA", noa, noa_len);
+
+	if (group->p2p->cfg->get_noa)
+		curr_noa_len = group->p2p->cfg->get_noa(
+			group->p2p->cfg->cb_ctx, group->cfg->interface_addr,
+			curr_noa, sizeof(curr_noa));
+	else
+		curr_noa_len = -1;
+	if (curr_noa_len < 0)
+		p2p_dbg(group->p2p, "Failed to fetch current NoA");
+	else if (curr_noa_len == 0)
+		p2p_dbg(group->p2p, "No NoA being advertized");
+	else
+		wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa,
+			    curr_noa_len);
+
+	/* TODO: properly process request and store copy */
+	if (curr_noa_len > 0 || curr_noa_len == -1)
+		return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+
+	return P2P_SC_SUCCESS;
+}
+
+
+unsigned int p2p_get_group_num_members(struct p2p_group *group)
+{
+	if (!group)
+		return 0;
+
+	return group->num_members;
+}
+
+
+int p2p_client_limit_reached(struct p2p_group *group)
+{
+	if (!group || !group->cfg)
+		return 1;
+
+	return group->num_members >= group->cfg->max_clients;
+}
+
+
+const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next)
+{
+	struct p2p_group_member *iter = *next;
+
+	if (!iter)
+		iter = group->members;
+	else
+		iter = iter->next;
+
+	*next = iter;
+
+	if (!iter)
+		return NULL;
+
+	return iter->dev_addr;
+}
+
+
+int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr)
+{
+	struct p2p_group_member *m;
+
+	for (m = group->members; m; m = m->next) {
+		if (os_memcmp(m->dev_addr, dev_addr, ETH_ALEN) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
+				size_t group_id_len)
+{
+	if (group_id_len != ETH_ALEN + group->cfg->ssid_len)
+		return 0;
+	if (os_memcmp(group_id, group->p2p->cfg->dev_addr, ETH_ALEN) != 0)
+		return 0;
+	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;
+	}
+}
+
+
+int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
+			       unsigned int *num)
+
+{
+	struct p2p_channels intersect, res;
+	struct p2p_group_member *m;
+
+	if (!group || !common_freqs || !num)
+		return -1;
+
+	os_memset(&intersect, 0, sizeof(intersect));
+	os_memset(&res, 0, sizeof(res));
+
+	p2p_channels_union(&intersect, &group->p2p->cfg->channels,
+			   &intersect);
+
+	p2p_channels_dump(group->p2p,
+			  "Group common freqs before iterating members",
+			  &intersect);
+
+	for (m = group->members; m; m = m->next) {
+		struct p2p_device *dev;
+
+		dev = p2p_get_device(group->p2p, m->dev_addr);
+		if (!dev)
+			continue;
+
+		p2p_channels_intersect(&intersect, &dev->channels, &res);
+		intersect = res;
+	}
+
+	p2p_channels_dump(group->p2p, "Group common channels", &intersect);
+
+	os_memset(common_freqs, 0, *num * sizeof(int));
+	*num = p2p_channels_to_freqs(&intersect, common_freqs, *num);
+
+	return 0;
+}
diff --git a/hostap/src/p2p/p2p_i.h b/hostap/src/p2p/p2p_i.h
new file mode 100644
index 0000000..0ce4058
--- /dev/null
+++ b/hostap/src/p2p/p2p_i.h
@@ -0,0 +1,884 @@
+/*
+ * P2P - Internal definitions for P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef P2P_I_H
+#define P2P_I_H
+
+#include "utils/list.h"
+#include "p2p.h"
+
+#define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1
+
+/*
+ * A threshold (in seconds) to prefer a direct Probe Response frame from a P2P
+ * Device over the P2P Client Info received from a GO.
+ */
+#define P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD 1
+
+enum p2p_role_indication;
+
+/*
+ * To force Service Instances to fit within a single P2P Tag, MAX_SVC_ADV_LEN
+ * must equal 248 or less. Must have a minimum size of 19.
+ */
+#define MAX_SVC_ADV_LEN	600
+#define MAX_SVC_ADV_IE_LEN (9 + MAX_SVC_ADV_LEN + (5 * (MAX_SVC_ADV_LEN / 240)))
+
+enum p2p_go_state {
+	UNKNOWN_GO,
+	LOCAL_GO,
+	REMOTE_GO
+};
+
+/**
+ * struct p2p_device - P2P Device data (internal to P2P module)
+ */
+struct p2p_device {
+	struct dl_list list;
+	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;
+
+	/*
+	 * If the peer was discovered based on an interface address (e.g., GO
+	 * from Beacon/Probe Response), the interface address is stored here.
+	 * p2p_device_addr must still be set in such a case to the unique
+	 * identifier for the P2P Device.
+	 *
+	 * This field is also used during P2PS PD to store the intended GO
+	 * address of the peer.
+	 */
+	u8 interface_addr[ETH_ALEN];
+
+	/*
+	 * P2P Device Address of the GO in whose group this P2P Device is a
+	 * client.
+	 */
+	u8 member_in_go_dev[ETH_ALEN];
+
+	/*
+	 * P2P Interface Address of the GO in whose group this P2P Device is a
+	 * client.
+	 */
+	u8 member_in_go_iface[ETH_ALEN];
+
+	int go_neg_req_sent;
+	enum p2p_go_state go_state;
+	u8 dialog_token;
+	u8 tie_breaker;
+	u8 intended_addr[ETH_ALEN];
+
+	char country[3];
+	struct p2p_channels channels;
+	int oper_freq;
+	u8 oper_ssid[SSID_MAX_LEN];
+	size_t oper_ssid_len;
+
+	/**
+	 * req_config_methods - Pending provision discovery methods
+	 */
+	u16 req_config_methods;
+
+	/**
+	 * wps_prov_info - Stored provisioning WPS config method
+	 *
+	 * This is used to store pending WPS config method between Provisioning
+	 * Discovery and connection to a running group.
+	 */
+	u16 wps_prov_info;
+
+#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_PD_PEER_DISPLAY BIT(5)
+#define P2P_DEV_PD_PEER_KEYPAD BIT(6)
+#define P2P_DEV_USER_REJECTED BIT(7)
+#define P2P_DEV_PEER_WAITING_RESPONSE BIT(8)
+#define P2P_DEV_PREFER_PERSISTENT_GROUP BIT(9)
+#define P2P_DEV_WAIT_GO_NEG_RESPONSE BIT(10)
+#define P2P_DEV_WAIT_GO_NEG_CONFIRM BIT(11)
+#define P2P_DEV_GROUP_CLIENT_ONLY BIT(12)
+#define P2P_DEV_FORCE_FREQ BIT(13)
+#define P2P_DEV_PD_FOR_JOIN BIT(14)
+#define P2P_DEV_REPORTED_ONCE BIT(15)
+#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)
+#define P2P_DEV_P2PS_REPORTED BIT(20)
+#define P2P_DEV_PD_PEER_P2PS BIT(21)
+#define P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT BIT(22)
+
+	unsigned int flags;
+
+	int status; /* enum p2p_status_code */
+	unsigned int wait_count;
+	unsigned int connect_reqs;
+	unsigned int invitation_reqs;
+	unsigned int sd_reqs;
+
+	u16 ext_listen_period;
+	u16 ext_listen_interval;
+
+	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 {
+	struct p2p_sd_query *next;
+	u8 peer[ETH_ALEN];
+	int for_all_peers;
+	int wsd; /* Wi-Fi Display Service Discovery Request */
+	struct wpabuf *tlvs;
+};
+
+struct p2p_pending_action_tx {
+	unsigned int freq;
+	u8 dst[ETH_ALEN];
+	u8 src[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
+	size_t len;
+	unsigned int wait_time;
+	/* Followed by len octets of the frame */
+};
+
+/**
+ * struct p2p_data - P2P module data (internal to P2P module)
+ */
+struct p2p_data {
+	/**
+	 * cfg - P2P module configuration
+	 *
+	 * This is included in the same memory allocation with the
+	 * struct p2p_data and as such, must not be freed separately.
+	 */
+	struct p2p_config *cfg;
+
+	/**
+	 * state - The current P2P state
+	 */
+	enum p2p_state {
+		/**
+		 * P2P_IDLE - Idle
+		 */
+		P2P_IDLE,
+
+		/**
+		 * P2P_SEARCH - Search (Device Discovery)
+		 */
+		P2P_SEARCH,
+
+		/**
+		 * P2P_CONNECT - Trying to start GO Negotiation
+		 */
+		P2P_CONNECT,
+
+		/**
+		 * P2P_CONNECT_LISTEN - Listen during GO Negotiation start
+		 */
+		P2P_CONNECT_LISTEN,
+
+		/**
+		 * P2P_GO_NEG - In GO Negotiation
+		 */
+		P2P_GO_NEG,
+
+		/**
+		 * P2P_LISTEN_ONLY - Listen only
+		 */
+		P2P_LISTEN_ONLY,
+
+		/**
+		 * P2P_WAIT_PEER_CONNECT - Waiting peer in List for GO Neg
+		 */
+		P2P_WAIT_PEER_CONNECT,
+
+		/**
+		 * P2P_WAIT_PEER_IDLE - Waiting peer idle for GO Neg
+		 */
+		P2P_WAIT_PEER_IDLE,
+
+		/**
+		 * P2P_SD_DURING_FIND - Service Discovery during find
+		 */
+		P2P_SD_DURING_FIND,
+
+		/**
+		 * P2P_PROVISIONING - Provisioning (during group formation)
+		 */
+		P2P_PROVISIONING,
+
+		/**
+		 * P2P_PD_DURING_FIND - Provision Discovery during find
+		 */
+		P2P_PD_DURING_FIND,
+
+		/**
+		 * P2P_INVITE - Trying to start Invite
+		 */
+		P2P_INVITE,
+
+		/**
+		 * P2P_INVITE_LISTEN - Listen during Invite
+		 */
+		P2P_INVITE_LISTEN,
+	} state;
+
+	/**
+	 * min_disc_int - minDiscoverableInterval
+	 */
+	int min_disc_int;
+
+	/**
+	 * max_disc_int - maxDiscoverableInterval
+	 */
+	int max_disc_int;
+
+	/**
+	 * max_disc_tu - Maximum number of TUs for discoverable interval
+	 */
+	int max_disc_tu;
+
+	/**
+	 * devices - List of known P2P Device peers
+	 */
+	struct dl_list devices;
+
+	/**
+	 * go_neg_peer - Pointer to GO Negotiation peer
+	 */
+	struct p2p_device *go_neg_peer;
+
+	/**
+	 * invite_peer - Pointer to Invite peer
+	 */
+	struct p2p_device *invite_peer;
+
+	/**
+	 * last_p2p_find_oper - Pointer to last pre-find operation peer
+	 */
+	struct p2p_device *last_p2p_find_oper;
+
+	const u8 *invite_go_dev_addr;
+	u8 invite_go_dev_addr_buf[ETH_ALEN];
+	int invite_dev_pw_id;
+
+	unsigned int retry_invite_req:1;
+	unsigned int retry_invite_req_sent:1;
+
+	/**
+	 * sd_peer - Pointer to Service Discovery peer
+	 */
+	struct p2p_device *sd_peer;
+
+	/**
+	 * sd_query - Pointer to Service Discovery query
+	 */
+	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 */
+
+	/**
+	 * intended_addr - Local Intended P2P Interface Address
+	 *
+	 * This address is used during group owner negotiation as the Intended
+	 * P2P Interface Address and the group interface will be created with
+	 * address as the local address in case of successfully completed
+	 * negotiation.
+	 */
+	u8 intended_addr[ETH_ALEN];
+
+	/**
+	 * go_intent - Local GO Intent to be used during GO Negotiation
+	 */
+	u8 go_intent;
+
+	/**
+	 * next_tie_breaker - Next tie-breaker value to use in GO Negotiation
+	 */
+	u8 next_tie_breaker;
+
+	/**
+	 * ssid - Selected SSID for GO Negotiation (if local end will be GO)
+	 */
+	u8 ssid[SSID_MAX_LEN];
+
+	/**
+	 * ssid_len - ssid length in octets
+	 */
+	size_t ssid_len;
+
+	/**
+	 * ssid_set - Whether SSID is already set for GO Negotiation
+	 */
+	int ssid_set;
+
+	/**
+	 * Regulatory class for own operational channel
+	 */
+	u8 op_reg_class;
+
+	/**
+	 * op_channel - Own operational channel
+	 */
+	u8 op_channel;
+
+	/**
+	 * channels - Own supported regulatory classes and channels
+	 *
+	 * List of supposerted channels per regulatory class. The regulatory
+	 * classes are defined in IEEE Std 802.11-2007 Annex J and the
+	 * numbering of the clases depends on the configured country code.
+	 */
+	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,
+		P2P_PENDING_GO_NEG_RESPONSE,
+		P2P_PENDING_GO_NEG_RESPONSE_FAILURE,
+		P2P_PENDING_GO_NEG_CONFIRM,
+		P2P_PENDING_SD,
+		P2P_PENDING_PD,
+		P2P_PENDING_PD_RESPONSE,
+		P2P_PENDING_INVITATION_REQUEST,
+		P2P_PENDING_INVITATION_RESPONSE,
+		P2P_PENDING_DEV_DISC_REQUEST,
+		P2P_PENDING_DEV_DISC_RESPONSE,
+		P2P_PENDING_GO_DISC_REQ
+	} pending_action_state;
+
+	unsigned int pending_listen_freq;
+	unsigned int pending_listen_sec;
+	unsigned int pending_listen_usec;
+
+	u8 dev_capab;
+
+	int in_listen;
+	int drv_in_listen;
+
+	/**
+	 * sd_queries - Pending service discovery queries
+	 */
+	struct p2p_sd_query *sd_queries;
+
+	/**
+	 * srv_update_indic - Service Update Indicator for local services
+	 */
+	u16 srv_update_indic;
+
+	struct wpabuf *sd_resp; /* Fragmented SD response */
+	u8 sd_resp_addr[ETH_ALEN];
+	u8 sd_resp_dialog_token;
+	size_t sd_resp_pos; /* Offset in sd_resp */
+	u8 sd_frag_id;
+
+	struct wpabuf *sd_rx_resp; /* Reassembled SD response */
+	u16 sd_rx_update_indic;
+
+	/* P2P Invitation data */
+	enum p2p_invite_role inv_role;
+	u8 inv_bssid[ETH_ALEN];
+	int inv_bssid_set;
+	u8 inv_ssid[SSID_MAX_LEN];
+	size_t inv_ssid_len;
+	u8 inv_sa[ETH_ALEN];
+	u8 inv_group_bssid[ETH_ALEN];
+	u8 *inv_group_bssid_ptr;
+	u8 inv_go_dev_addr[ETH_ALEN];
+	u8 inv_status;
+	int inv_op_freq;
+	int inv_persistent;
+
+	enum p2p_discovery_type find_type;
+	unsigned int last_p2p_find_timeout;
+	u8 last_prog_scan_class;
+	u8 last_prog_scan_chan;
+	int p2p_scan_running;
+	enum p2p_after_scan {
+		P2P_AFTER_SCAN_NOTHING,
+		P2P_AFTER_SCAN_LISTEN,
+		P2P_AFTER_SCAN_CONNECT
+	} start_after_scan;
+	u8 after_scan_peer[ETH_ALEN];
+	struct p2p_pending_action_tx *after_scan_tx;
+	unsigned int after_scan_tx_in_progress:1;
+	unsigned int send_action_in_progress:1;
+
+	/* Requested device types for find/search */
+	unsigned int num_req_dev_types;
+	u8 *req_dev_types;
+	u8 *find_dev_id;
+	u8 find_dev_id_buf[ETH_ALEN];
+
+	struct os_reltime find_start; /* time of last p2p_find start */
+
+	struct p2p_group **groups;
+	size_t num_groups;
+
+	struct p2p_device *pending_client_disc_go;
+	u8 pending_client_disc_addr[ETH_ALEN];
+	u8 pending_dev_disc_dialog_token;
+	u8 pending_dev_disc_addr[ETH_ALEN];
+	int pending_dev_disc_freq;
+	unsigned int pending_client_disc_freq;
+
+	int ext_listen_only;
+	unsigned int ext_listen_period;
+	unsigned int ext_listen_interval;
+	unsigned int ext_listen_interval_sec;
+	unsigned int ext_listen_interval_usec;
+
+	u8 peer_filter[ETH_ALEN];
+
+	int cross_connect;
+
+	int best_freq_24;
+	int best_freq_5;
+	int best_freq_overall;
+	int own_freq_preference;
+
+	/**
+	 * wps_vendor_ext - WPS Vendor Extensions to add
+	 */
+	struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+
+	/*
+	 * user_initiated_pd - Whether a PD request is user initiated or not.
+	 */
+	u8 user_initiated_pd;
+
+	/*
+	 * Keep track of which peer a given PD request was sent to.
+	 * Used to raise a timeout alert in case there is no response.
+	 */
+	u8 pending_pd_devaddr[ETH_ALEN];
+
+	/*
+	 * Retry counter for provision discovery requests when issued
+	 * in IDLE state.
+	 */
+	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;
+
+	/* Extra delay in milliseconds between search iterations */
+	unsigned int search_delay;
+	int in_search_delay;
+
+	u8 pending_reg_class;
+	u8 pending_channel;
+	u8 pending_channel_forced;
+
+	/* ASP Support */
+	struct p2ps_advertisement *p2ps_adv_list;
+	struct p2ps_provision *p2ps_prov;
+	u8 wild_card_hash[P2PS_HASH_LEN];
+	u8 p2ps_seek;
+	u8 p2ps_seek_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN];
+	u8 p2ps_seek_count;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	struct wpabuf *wfd_ie_beacon;
+	struct wpabuf *wfd_ie_probe_req;
+	struct wpabuf *wfd_ie_probe_resp;
+	struct wpabuf *wfd_ie_assoc_req;
+	struct wpabuf *wfd_ie_invitation;
+	struct wpabuf *wfd_ie_prov_disc_req;
+	struct wpabuf *wfd_ie_prov_disc_resp;
+	struct wpabuf *wfd_ie_go_neg;
+	struct wpabuf *wfd_dev_info;
+	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;
+
+	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
+	unsigned int num_pref_freq;
+};
+
+/**
+ * struct p2p_message - Parsed P2P message (or P2P IE)
+ */
+struct p2p_message {
+	struct wpabuf *p2p_attributes;
+	struct wpabuf *wps_attributes;
+	struct wpabuf *wfd_subelems;
+
+	u8 dialog_token;
+
+	const u8 *capability;
+	const u8 *go_intent;
+	const u8 *status;
+	const u8 *listen_channel;
+	const u8 *operating_channel;
+	const u8 *channel_list;
+	u8 channel_list_len;
+	const u8 *config_timeout;
+	const u8 *intended_addr;
+	const u8 *group_bssid;
+	const u8 *invitation_flags;
+
+	const u8 *group_info;
+	size_t group_info_len;
+
+	const u8 *group_id;
+	size_t group_id_len;
+
+	const u8 *device_id;
+
+	const u8 *manageability;
+
+	const u8 *noa;
+	size_t noa_len;
+
+	const u8 *ext_listen_timing;
+
+	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;
+	const u8 *p2p_device_addr;
+	const u8 *pri_dev_type;
+	u8 num_sec_dev_types;
+	char device_name[WPS_DEV_NAME_MAX_LEN + 1];
+	u16 config_methods;
+
+	/* 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;
+	size_t wps_sec_dev_type_list_len;
+	const u8 *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+	size_t wps_vendor_ext_len[P2P_MAX_WPS_VENDOR_EXT];
+	const u8 *manufacturer;
+	size_t manufacturer_len;
+	const u8 *model_name;
+	size_t model_name_len;
+	const u8 *model_number;
+	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;
+
+	/* SSID IE */
+	const u8 *ssid;
+
+	/* P2PS */
+	u8 service_hash_count;
+	const u8 *service_hash;
+
+	const u8 *session_info;
+	size_t session_info_len;
+
+	const u8 *conn_cap;
+
+	const u8 *adv_id;
+	const u8 *adv_mac;
+
+	const u8 *adv_service_instance;
+	size_t adv_service_instance_len;
+
+	const u8 *session_id;
+	const u8 *session_mac;
+
+	const u8 *feature_cap;
+	size_t feature_cap_len;
+
+	const u8 *persistent_dev;
+	const u8 *persistent_ssid;
+	size_t persistent_ssid_len;
+
+	const u8 *pref_freq_list;
+	size_t pref_freq_list_len;
+};
+
+
+#define P2P_MAX_GROUP_ENTRIES 50
+
+struct p2p_group_info {
+	unsigned int num_clients;
+	struct p2p_client_info {
+		const u8 *p2p_device_addr;
+		const u8 *p2p_interface_addr;
+		u8 dev_capab;
+		u16 config_methods;
+		const u8 *pri_dev_type;
+		u8 num_sec_dev_types;
+		const u8 *sec_dev_types;
+		const char *dev_name;
+		size_t dev_name_len;
+	} client[P2P_MAX_GROUP_ENTRIES];
+};
+
+
+/* p2p_utils.c */
+int p2p_random(char *buf, size_t len);
+int p2p_channel_to_freq(int op_class, int channel);
+int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel);
+void p2p_channels_intersect(const struct p2p_channels *a,
+			    const struct p2p_channels *b,
+			    struct p2p_channels *res);
+void p2p_channels_union_inplace(struct p2p_channels *res,
+				const struct p2p_channels *b);
+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);
+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,
+			 struct p2p_group_info *info);
+
+/* p2p_build.c */
+
+struct p2p_noa_desc {
+	u8 count_type;
+	u32 duration;
+	u32 interval;
+	u32 start_time;
+};
+
+/* p2p_group.c */
+const u8 * p2p_group_get_interface_addr(struct p2p_group *group);
+u8 p2p_group_presence_req(struct p2p_group *group,
+			  const u8 *client_interface_addr,
+			  const u8 *noa, size_t noa_len);
+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);
+void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
+				   u8 dialog_token);
+u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf);
+void p2p_buf_add_status(struct wpabuf *buf, u8 status);
+void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
+			     struct p2p_device *peer);
+void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr);
+void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len);
+void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab);
+void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent);
+void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
+				u8 reg_class, u8 channel);
+void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
+				   u8 reg_class, u8 channel);
+void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
+			      struct p2p_channels *chan);
+void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
+				u8 client_timeout);
+void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr);
+void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid);
+void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
+			  const u8 *ssid, size_t ssid_len);
+void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags);
+void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
+		     struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2);
+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_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
+				    u8 oper_class, u8 channel,
+				    enum p2p_role_indication role);
+void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p);
+void p2p_buf_add_session_info(struct wpabuf *buf, const char *info);
+void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap);
+void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac);
+void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
+				  u8 count, const u8 *hash,
+				  struct p2ps_advertisement *adv_list);
+void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac);
+void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len,
+				    const u8 *mask);
+void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
+				       const u8 *ssid, size_t ssid_len);
+int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
+		     int all_attr);
+void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
+				   const u32 *preferred_freq_list, u32 size);
+
+/* p2p_sd.c */
+struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
+					 struct p2p_device *dev);
+void p2p_free_sd_queries(struct p2p_data *p2p);
+void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa,
+			    const u8 *data, size_t len, int rx_freq);
+void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq);
+void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq);
+void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa,
+			      const u8 *data, size_t len, int rx_freq);
+int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev);
+
+/* p2p_go_neg.c */
+int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own,
+			    struct p2p_device *dev,
+			    const u8 *channel_list, size_t channel_list_len);
+void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
+			    const u8 *data, size_t len, int rx_freq);
+void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq);
+void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len);
+int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev);
+u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method);
+void p2p_reselect_channel(struct p2p_data *p2p,
+			  struct p2p_channels *intersection);
+void p2p_check_pref_chan(struct p2p_data *p2p, int go,
+			 struct p2p_device *dev, struct p2p_message *msg);
+
+/* p2p_pd.c */
+void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
+			       const u8 *data, size_t len, int rx_freq);
+void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
+				const u8 *data, size_t len);
+int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
+			   int join, int force_freq);
+void p2p_reset_pending_pd(struct p2p_data *p2p);
+void p2ps_prov_free(struct p2p_data *p2p);
+
+/* p2p_invitation.c */
+void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
+				const u8 *data, size_t len, int rx_freq);
+void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
+				 const u8 *data, size_t len);
+int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
+		    const u8 *go_dev_addr, int dev_pw_id);
+void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
+void p2p_invitation_resp_cb(struct p2p_data *p2p, int success);
+
+/* p2p_dev_disc.c */
+void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
+			      const u8 *data, size_t len, int rx_freq);
+void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success);
+int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev);
+void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success);
+void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa,
+			       const u8 *data, size_t len);
+void p2p_go_disc_req_cb(struct p2p_data *p2p, int success);
+void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq);
+
+/* p2p.c */
+void p2p_set_state(struct p2p_data *p2p, int new_state);
+void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec,
+		     unsigned int usec);
+void p2p_clear_timeout(struct p2p_data *p2p);
+void p2p_continue_find(struct p2p_data *p2p);
+struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
+						const u8 *addr,
+						struct p2p_message *msg);
+void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
+		      struct p2p_device *dev, struct p2p_message *msg);
+int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
+		   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,
+					     const u8 *addr);
+void p2p_go_neg_failed(struct p2p_data *p2p, int status);
+void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer);
+int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps);
+int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
+			size_t num_req_dev_type);
+struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p,
+					 const u8 *query_hash,
+					 u8 query_count);
+void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len);
+int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+		    const u8 *src, const u8 *bssid, const u8 *buf,
+		    size_t len, unsigned int wait_time);
+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,
+			int go);
+void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx);
+int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
+			  u8 *status);
+void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+void p2p_info(struct p2p_data *p2p, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+void p2p_err(struct p2p_data *p2p, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+
+#endif /* P2P_I_H */
diff --git a/hostap/src/p2p/p2p_invitation.c b/hostap/src/p2p/p2p_invitation.c
new file mode 100644
index 0000000..108e5b7
--- /dev/null
+++ b/hostap/src/p2p/p2p_invitation.c
@@ -0,0 +1,719 @@
+/*
+ * Wi-Fi Direct - P2P Invitation procedure
+ * Copyright (c) 2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#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,
+						int dev_pw_id)
+{
+	struct wpabuf *buf;
+	u8 *len;
+	const u8 *dev_addr;
+	size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	struct wpabuf *wfd_ie = p2p->wfd_ie_invitation;
+	if (wfd_ie && p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) {
+		size_t i;
+		for (i = 0; i < p2p->num_groups; i++) {
+			struct p2p_group *g = p2p->groups[i];
+			struct wpabuf *ie;
+			if (os_memcmp(p2p_group_get_interface_addr(g),
+				      p2p->inv_bssid, ETH_ALEN) != 0)
+				continue;
+			ie = p2p_group_get_wfd_ie(g);
+			if (ie) {
+				wfd_ie = ie;
+				break;
+			}
+		}
+	}
+	if (wfd_ie)
+		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;
+
+	peer->dialog_token++;
+	if (peer->dialog_token == 0)
+		peer->dialog_token = 1;
+	p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ,
+				      peer->dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent)
+		p2p_buf_add_config_timeout(buf, 0, 0);
+	else
+		p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+					   p2p->client_timeout);
+	p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ?
+				     P2P_INVITATION_FLAGS_TYPE : 0);
+	if (p2p->inv_role != P2P_INVITE_ROLE_CLIENT ||
+	    !(peer->flags & P2P_DEV_NO_PREF_CHAN))
+		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+					      p2p->op_reg_class,
+					      p2p->op_channel);
+	if (p2p->inv_bssid_set)
+		p2p_buf_add_group_bssid(buf, p2p->inv_bssid);
+	p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
+	if (go_dev_addr)
+		dev_addr = go_dev_addr;
+	else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT)
+		dev_addr = peer->info.p2p_device_addr;
+	else
+		dev_addr = p2p->cfg->dev_addr;
+	p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len);
+	p2p_buf_add_device_info(buf, p2p, peer);
+	p2p_buf_update_ie_hdr(buf, len);
+
+	p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list,
+				      p2p->num_pref_freq);
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (wfd_ie)
+		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;
+}
+
+
+static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p,
+						 struct p2p_device *peer,
+						 u8 dialog_token, u8 status,
+						 const u8 *group_bssid,
+						 u8 reg_class, u8 channel,
+						 struct p2p_channels *channels)
+{
+	struct wpabuf *buf;
+	u8 *len;
+	size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	struct wpabuf *wfd_ie = p2p->wfd_ie_invitation;
+	if (wfd_ie && group_bssid) {
+		size_t i;
+		for (i = 0; i < p2p->num_groups; i++) {
+			struct p2p_group *g = p2p->groups[i];
+			struct wpabuf *ie;
+			if (os_memcmp(p2p_group_get_interface_addr(g),
+				      group_bssid, ETH_ALEN) != 0)
+				continue;
+			ie = p2p_group_get_wfd_ie(g);
+			if (ie) {
+				wfd_ie = ie;
+				break;
+			}
+		}
+	}
+	if (wfd_ie)
+		extra = wpabuf_len(wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP])
+		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]);
+
+	buf = wpabuf_alloc(1000 + extra);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP,
+				      dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	p2p_buf_add_status(buf, status);
+	p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */
+	if (reg_class && channel)
+		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+					      reg_class, channel);
+	if (group_bssid)
+		p2p_buf_add_group_bssid(buf, group_bssid);
+	if (channels)
+		p2p_buf_add_channel_list(buf, p2p->cfg->country, channels);
+	p2p_buf_update_ie_hdr(buf, len);
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (wfd_ie)
+		wpabuf_put_buf(buf, wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP])
+		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]);
+
+	return buf;
+}
+
+
+void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
+				const u8 *data, size_t len, int rx_freq)
+{
+	struct p2p_device *dev;
+	struct p2p_message msg;
+	struct wpabuf *resp = NULL;
+	u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+	int freq;
+	int go = 0;
+	u8 group_bssid[ETH_ALEN], *bssid;
+	int op_freq = 0;
+	u8 reg_class = 0, channel = 0;
+	struct p2p_channels all_channels, intersection, *channels = NULL;
+	int persistent;
+
+	os_memset(group_bssid, 0, sizeof(group_bssid));
+
+	p2p_dbg(p2p, "Received Invitation Request from " MACSTR " (freq=%d)",
+		MAC2STR(sa), rx_freq);
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	dev = p2p_get_device(p2p, sa);
+	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+		p2p_dbg(p2p, "Invitation Request from unknown peer " MACSTR,
+			MAC2STR(sa));
+
+		if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1,
+				   0)) {
+			p2p_dbg(p2p, "Invitation Request add device failed "
+				MACSTR, MAC2STR(sa));
+			status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+			goto fail;
+		}
+
+		dev = p2p_get_device(p2p, sa);
+		if (dev == NULL) {
+			p2p_dbg(p2p, "Reject Invitation Request from unknown peer "
+				MACSTR, MAC2STR(sa));
+			status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+			goto fail;
+		}
+	}
+
+	if (!msg.group_id || !msg.channel_list) {
+		p2p_dbg(p2p, "Mandatory attribute missing in Invitation Request from "
+			MACSTR, MAC2STR(sa));
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+
+	if (msg.invitation_flags)
+		persistent = *msg.invitation_flags & P2P_INVITATION_FLAGS_TYPE;
+	else {
+		/* Invitation Flags is a mandatory attribute starting from P2P
+		 * spec 1.06. As a backwards compatibility mechanism, assume
+		 * the request was for a persistent group if the attribute is
+		 * missing.
+		 */
+		p2p_dbg(p2p, "Mandatory Invitation Flags attribute missing from Invitation Request");
+		persistent = 1;
+	}
+
+	p2p_channels_union(&p2p->cfg->channels, &p2p->cfg->cli_channels,
+			   &all_channels);
+
+	if (p2p_peer_channels_check(p2p, &all_channels, dev,
+				    msg.channel_list, msg.channel_list_len) <
+	    0) {
+		p2p_dbg(p2p, "No common channels found");
+		status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+		goto fail;
+	}
+
+	p2p_channels_dump(p2p, "own channels", &p2p->cfg->channels);
+	p2p_channels_dump(p2p, "own client channels", &all_channels);
+	p2p_channels_dump(p2p, "peer channels", &dev->channels);
+	p2p_channels_intersect(&all_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,
+			msg.dev_password_id_present ? msg.dev_password_id : -1);
+	}
+
+	if (go) {
+		p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+				       &intersection);
+		p2p_channels_dump(p2p, "intersection(GO)", &intersection);
+		if (intersection.reg_classes == 0) {
+			p2p_dbg(p2p, "No common channels found (GO)");
+			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+			goto fail;
+		}
+	}
+
+	if (op_freq) {
+		p2p_dbg(p2p, "Invitation processing forced frequency %d MHz",
+			op_freq);
+		if (p2p_freq_to_channel(op_freq, &reg_class, &channel) < 0) {
+			p2p_dbg(p2p, "Unknown forced freq %d MHz from invitation_process()",
+				op_freq);
+			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+			goto fail;
+		}
+
+		if (!p2p_channels_includes(&intersection, reg_class, channel))
+		{
+			p2p_dbg(p2p, "forced freq %d MHz not in the supported channels interaction",
+				op_freq);
+			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+			goto fail;
+		}
+
+		if (status == P2P_SC_SUCCESS)
+			channels = &intersection;
+	} else {
+		p2p_dbg(p2p, "No forced channel from invitation processing - figure out best one to use");
+
+		/* Default to own configuration as a starting point */
+		p2p->op_reg_class = p2p->cfg->op_reg_class;
+		p2p->op_channel = p2p->cfg->op_channel;
+		p2p_dbg(p2p, "Own default op_class %d channel %d",
+			p2p->op_reg_class, p2p->op_channel);
+
+		/* Use peer preference if specified and compatible */
+		if (msg.operating_channel) {
+			int req_freq;
+			req_freq = p2p_channel_to_freq(
+				msg.operating_channel[3],
+				msg.operating_channel[4]);
+			p2p_dbg(p2p, "Peer operating channel preference: %d MHz",
+				req_freq);
+			if (req_freq > 0 &&
+			    p2p_channels_includes(&intersection,
+						  msg.operating_channel[3],
+						  msg.operating_channel[4])) {
+				p2p->op_reg_class = msg.operating_channel[3];
+				p2p->op_channel = msg.operating_channel[4];
+				p2p_dbg(p2p, "Use peer preference op_class %d channel %d",
+					p2p->op_reg_class, p2p->op_channel);
+			} else {
+				p2p_dbg(p2p, "Cannot use peer channel preference");
+			}
+		}
+
+		/* 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);
+			p2p_reselect_channel(p2p, &intersection);
+			p2p_dbg(p2p, "Re-selection result: op_class %d channel %d",
+				p2p->op_reg_class, p2p->op_channel);
+			if (!p2p_channels_includes(&intersection,
+						   p2p->op_reg_class,
+						   p2p->op_channel)) {
+				p2p_dbg(p2p, "Peer does not support selected operating channel (reg_class=%u channel=%u)",
+					p2p->op_reg_class, p2p->op_channel);
+				status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+				goto fail;
+			}
+		} 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);
+			p2p_reselect_channel(p2p, &intersection);
+		}
+
+		/*
+		 * Use the driver preferred frequency list extension if
+		 * supported.
+		 */
+		p2p_check_pref_chan(p2p, go, dev, &msg);
+
+		op_freq = p2p_channel_to_freq(p2p->op_reg_class,
+					      p2p->op_channel);
+		if (op_freq < 0) {
+			p2p_dbg(p2p, "Unknown operational channel (country=%c%c reg_class=%u channel=%u)",
+				p2p->cfg->country[0], p2p->cfg->country[1],
+				p2p->op_reg_class, p2p->op_channel);
+			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+			goto fail;
+		}
+		p2p_dbg(p2p, "Selected operating channel - %d MHz", op_freq);
+
+		if (status == P2P_SC_SUCCESS) {
+			reg_class = p2p->op_reg_class;
+			channel = p2p->op_channel;
+			channels = &intersection;
+		}
+	}
+
+fail:
+	if (go && status == P2P_SC_SUCCESS && !is_zero_ether_addr(group_bssid))
+		bssid = group_bssid;
+	else
+		bssid = NULL;
+	resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status,
+					 bssid, reg_class, channel, channels);
+
+	if (resp == NULL)
+		goto out;
+
+	if (rx_freq > 0)
+		freq = rx_freq;
+	else
+		freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+	if (freq < 0) {
+		p2p_dbg(p2p, "Unknown regulatory class/channel");
+		goto out;
+	}
+
+	/*
+	 * Store copy of invitation data to be used when processing TX status
+	 * callback for the Acton frame.
+	 */
+	os_memcpy(p2p->inv_sa, sa, ETH_ALEN);
+	if (msg.group_bssid) {
+		os_memcpy(p2p->inv_group_bssid, msg.group_bssid, ETH_ALEN);
+		p2p->inv_group_bssid_ptr = p2p->inv_group_bssid;
+	} else
+		p2p->inv_group_bssid_ptr = NULL;
+	if (msg.group_id) {
+		if (msg.group_id_len - ETH_ALEN <= SSID_MAX_LEN) {
+			os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN,
+				  msg.group_id_len - ETH_ALEN);
+			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);
+	}
+	p2p->inv_status = status;
+	p2p->inv_op_freq = op_freq;
+
+	p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE;
+	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+			    p2p->cfg->dev_addr,
+			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+		p2p_dbg(p2p, "Failed to send Action frame");
+	}
+
+out:
+	wpabuf_free(resp);
+	p2p_parse_free(&msg);
+}
+
+
+void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
+				 const u8 *data, size_t len)
+{
+	struct p2p_device *dev;
+	struct p2p_message msg;
+	struct p2p_channels intersection, *channels = NULL;
+
+	p2p_dbg(p2p, "Received Invitation Response from " MACSTR,
+		MAC2STR(sa));
+
+	dev = p2p_get_device(p2p, sa);
+	if (dev == NULL) {
+		p2p_dbg(p2p, "Ignore Invitation Response from unknown peer "
+			MACSTR, MAC2STR(sa));
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		return;
+	}
+
+	if (dev != p2p->invite_peer) {
+		p2p_dbg(p2p, "Ignore unexpected Invitation Response from peer "
+			MACSTR, MAC2STR(sa));
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		return;
+	}
+
+	if (p2p_parse(data, len, &msg)) {
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		return;
+	}
+
+	if (!msg.status) {
+		p2p_dbg(p2p, "Mandatory Status attribute missing in Invitation Response from "
+			MACSTR, MAC2STR(sa));
+		p2p_parse_free(&msg);
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		return;
+	}
+
+	/*
+	 * We should not really receive a replayed response twice since
+	 * duplicate frames are supposed to be dropped. However, not all drivers
+	 * do that for pre-association frames. We did not use to verify dialog
+	 * token matches for invitation response frames, but that check can be
+	 * safely used to drop a replayed response to the previous Invitation
+	 * Request in case the suggested operating channel was changed. This
+	 * allows a duplicated reject frame to be dropped with the assumption
+	 * that the real response follows after it.
+	 */
+	if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS &&
+	    p2p->retry_invite_req_sent &&
+	    msg.dialog_token != dev->dialog_token) {
+		p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)",
+			msg.dialog_token, dev->dialog_token);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS &&
+	    p2p->retry_invite_req &&
+	    p2p_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class,
+				      &p2p->op_channel) == 0) {
+		p2p->retry_invite_req = 0;
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+		p2p_set_state(p2p, P2P_INVITE);
+		p2p_dbg(p2p, "Resend Invitation Request setting op_class %u channel %u as operating channel",
+			p2p->op_reg_class, p2p->op_channel);
+		p2p->retry_invite_req_sent = 1;
+		p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
+				p2p->invite_dev_pw_id);
+		p2p_parse_free(&msg);
+		return;
+	}
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	p2p->retry_invite_req = 0;
+
+	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
+		p2p_parse_free(&msg);
+		return;
+#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) {
+		p2p_dbg(p2p, "No common channels found");
+		p2p_parse_free(&msg);
+		return;
+	} else {
+		p2p_channels_intersect(&p2p->channels, &dev->channels,
+				       &intersection);
+		channels = &intersection;
+	}
+
+	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;
+		}
+
+		/*
+		 * Use the driver preferred frequency list extension if
+		 * supported.
+		 */
+		p2p_check_pref_chan(p2p, 0, dev, &msg);
+
+		p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
+					    msg.group_bssid, channels, sa,
+					    freq, peer_oper_freq);
+	}
+
+	p2p_parse_free(&msg);
+
+	p2p_clear_timeout(p2p);
+	p2p_set_state(p2p, P2P_IDLE);
+	p2p->invite_peer = NULL;
+}
+
+
+int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
+		    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",
+			MAC2STR(dev->info.p2p_device_addr));
+		return -1;
+	}
+
+	req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id);
+	if (req == NULL)
+		return -1;
+	if (p2p->state != P2P_IDLE)
+		p2p_stop_listen_for_freq(p2p, freq);
+	p2p_dbg(p2p, "Sending Invitation Request");
+	p2p_set_state(p2p, P2P_INVITE);
+	p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST;
+	p2p->invite_peer = dev;
+	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), 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);
+
+	return 0;
+}
+
+
+void p2p_invitation_req_cb(struct p2p_data *p2p, int success)
+{
+	p2p_dbg(p2p, "Invitation Request TX callback: success=%d", success);
+
+	if (p2p->invite_peer == NULL) {
+		p2p_dbg(p2p, "No pending Invite");
+		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 ? 500000 : 100000);
+}
+
+
+void p2p_invitation_resp_cb(struct p2p_data *p2p, int success)
+{
+	p2p_dbg(p2p, "Invitation Response TX callback: success=%d", success);
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
+	if (!success)
+		p2p_dbg(p2p, "Assume Invitation Response was actually received by the peer even though Ack was not reported");
+
+	if (p2p->cfg->invitation_received) {
+		p2p->cfg->invitation_received(p2p->cfg->cb_ctx,
+					      p2p->inv_sa,
+					      p2p->inv_group_bssid_ptr,
+					      p2p->inv_ssid, p2p->inv_ssid_len,
+					      p2p->inv_go_dev_addr,
+					      p2p->inv_status,
+					      p2p->inv_op_freq);
+	}
+}
+
+
+int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
+	       const u8 *bssid, const u8 *ssid, size_t ssid_len,
+	       unsigned int force_freq, const u8 *go_dev_addr,
+	       int persistent_group, unsigned int pref_freq, int dev_pw_id)
+{
+	struct p2p_device *dev;
+
+	p2p_dbg(p2p, "Request to invite peer " MACSTR " role=%d persistent=%d "
+		"force_freq=%u",
+		MAC2STR(peer), role, persistent_group, force_freq);
+	if (bssid)
+		p2p_dbg(p2p, "Invitation for BSSID " MACSTR, MAC2STR(bssid));
+	if (go_dev_addr) {
+		p2p_dbg(p2p, "Invitation for GO Device Address " MACSTR,
+			MAC2STR(go_dev_addr));
+		os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN);
+		p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf;
+	} else
+		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;
+	p2p->retry_invite_req = role == P2P_INVITE_ROLE_GO &&
+		persistent_group && !force_freq;
+	p2p->retry_invite_req_sent = 0;
+
+	dev = p2p_get_device(p2p, peer);
+	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,
+				role != P2P_INVITE_ROLE_CLIENT) < 0)
+		return -1;
+
+	if (persistent_group && role == P2P_INVITE_ROLE_CLIENT && !force_freq &&
+	    !pref_freq)
+		dev->flags |= P2P_DEV_NO_PREF_CHAN;
+	else
+		dev->flags &= ~P2P_DEV_NO_PREF_CHAN;
+
+	if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+		if (!(dev->info.dev_capab &
+		      P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+			p2p_dbg(p2p, "Cannot invite a P2P Device " MACSTR
+				" that is in a group and is not discoverable",
+				MAC2STR(peer));
+		}
+		/* TODO: use device discoverability request through GO */
+	}
+
+	dev->invitation_reqs = 0;
+
+	if (p2p->state != P2P_IDLE)
+		p2p_stop_find(p2p);
+
+	p2p->inv_role = role;
+	p2p->inv_bssid_set = bssid != NULL;
+	if (bssid)
+		os_memcpy(p2p->inv_bssid, bssid, ETH_ALEN);
+	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, dev_pw_id);
+}
diff --git a/hostap/src/p2p/p2p_parse.c b/hostap/src/p2p/p2p_parse.c
new file mode 100644
index 0000000..bd1e68b
--- /dev/null
+++ b/hostap/src/p2p/p2p_parse.c
@@ -0,0 +1,880 @@
+/*
+ * P2P - IE parser
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+
+
+static int p2p_parse_attribute(u8 id, const u8 *data, u16 len,
+			       struct p2p_message *msg)
+{
+	const u8 *pos;
+	size_t i, nlen;
+	char devtype[WPS_DEV_TYPE_BUFSIZE];
+
+	switch (id) {
+	case P2P_ATTR_CAPABILITY:
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Capability "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->capability = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Device Capability %02x "
+			   "Group Capability %02x",
+			   data[0], data[1]);
+		break;
+	case P2P_ATTR_DEVICE_ID:
+		if (len < ETH_ALEN) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Device ID "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->device_id = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Device ID " MACSTR,
+			   MAC2STR(msg->device_id));
+		break;
+	case P2P_ATTR_GROUP_OWNER_INTENT:
+		if (len < 1) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short GO Intent "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->go_intent = data;
+		wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u "
+			   "Tie breaker %u", data[0] >> 1, data[0] & 0x01);
+		break;
+	case P2P_ATTR_STATUS:
+		if (len < 1) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Status "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->status = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Status: %d", data[0]);
+		break;
+	case P2P_ATTR_LISTEN_CHANNEL:
+		if (len == 0) {
+			wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Ignore "
+				   "null channel");
+			break;
+		}
+		if (len < 5) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Listen Channel "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->listen_channel = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: "
+			   "Country %c%c(0x%02x) Regulatory "
+			   "Class %d Channel Number %d", data[0], data[1],
+			   data[2], data[3], data[4]);
+		break;
+	case P2P_ATTR_OPERATING_CHANNEL:
+		if (len == 0) {
+			wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: "
+				   "Ignore null channel");
+			break;
+		}
+		if (len < 5) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Operating "
+				   "Channel attribute (length %d)", len);
+			return -1;
+		}
+		msg->operating_channel = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: "
+			   "Country %c%c(0x%02x) Regulatory "
+			   "Class %d Channel Number %d", data[0], data[1],
+			   data[2], data[3], data[4]);
+		break;
+	case P2P_ATTR_CHANNEL_LIST:
+		if (len < 3) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Channel List "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->channel_list = data;
+		msg->channel_list_len = len;
+		wpa_printf(MSG_DEBUG, "P2P: * Channel List: Country String "
+			   "'%c%c(0x%02x)'", data[0], data[1], data[2]);
+		wpa_hexdump(MSG_MSGDUMP, "P2P: Channel List",
+			    msg->channel_list, msg->channel_list_len);
+		break;
+	case P2P_ATTR_GROUP_INFO:
+		msg->group_info = data;
+		msg->group_info_len = len;
+		wpa_printf(MSG_DEBUG, "P2P: * Group Info");
+		break;
+	case P2P_ATTR_DEVICE_INFO:
+		if (len < ETH_ALEN + 2 + 8 + 1) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Device Info "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->p2p_device_info = data;
+		msg->p2p_device_info_len = len;
+		pos = data;
+		msg->p2p_device_addr = pos;
+		pos += ETH_ALEN;
+		msg->config_methods = WPA_GET_BE16(pos);
+		pos += 2;
+		msg->pri_dev_type = pos;
+		pos += 8;
+		msg->num_sec_dev_types = *pos++;
+		if (msg->num_sec_dev_types * 8 > data + len - pos) {
+			wpa_printf(MSG_DEBUG, "P2P: Device Info underflow");
+			return -1;
+		}
+		pos += msg->num_sec_dev_types * 8;
+		if (data + len - pos < 4) {
+			wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
+				   "length %d", (int) (data + len - pos));
+			return -1;
+		}
+		if (WPA_GET_BE16(pos) != ATTR_DEV_NAME) {
+			wpa_hexdump(MSG_DEBUG, "P2P: Unexpected Device Name "
+				    "header", pos, 4);
+			return -1;
+		}
+		pos += 2;
+		nlen = WPA_GET_BE16(pos);
+		pos += 2;
+		if (data + len - pos < (int) nlen ||
+		    nlen > WPS_DEV_NAME_MAX_LEN) {
+			wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
+				   "length %d (buf len %d)", (int) nlen,
+				   (int) (data + len - pos));
+			return -1;
+		}
+		os_memcpy(msg->device_name, pos, nlen);
+		msg->device_name[nlen] = '\0';
+		for (i = 0; i < nlen; i++) {
+			if (msg->device_name[i] == '\0')
+				break;
+			if (is_ctrl_char(msg->device_name[i]))
+				msg->device_name[i] = '_';
+		}
+		wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR
+			   " primary device type %s device name '%s' "
+			   "config methods 0x%x",
+			   MAC2STR(msg->p2p_device_addr),
+			   wps_dev_type_bin2str(msg->pri_dev_type, devtype,
+						sizeof(devtype)),
+			   msg->device_name, msg->config_methods);
+		break;
+	case P2P_ATTR_CONFIGURATION_TIMEOUT:
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Configuration "
+				   "Timeout attribute (length %d)", len);
+			return -1;
+		}
+		msg->config_timeout = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout");
+		break;
+	case P2P_ATTR_INTENDED_INTERFACE_ADDR:
+		if (len < ETH_ALEN) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Intended P2P "
+				   "Interface Address attribute (length %d)",
+				   len);
+			return -1;
+		}
+		msg->intended_addr = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address: "
+			   MACSTR, MAC2STR(msg->intended_addr));
+		break;
+	case P2P_ATTR_GROUP_BSSID:
+		if (len < ETH_ALEN) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short P2P Group BSSID "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->group_bssid = data;
+		wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID: " MACSTR,
+			   MAC2STR(msg->group_bssid));
+		break;
+	case P2P_ATTR_GROUP_ID:
+		if (len < ETH_ALEN || len > ETH_ALEN + SSID_MAX_LEN) {
+			wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID "
+				   "attribute length %d", len);
+			return -1;
+		}
+		msg->group_id = data;
+		msg->group_id_len = len;
+		wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID: Device Address "
+			   MACSTR, MAC2STR(msg->group_id));
+		wpa_hexdump_ascii(MSG_DEBUG, "P2P: * P2P Group ID: SSID",
+				  msg->group_id + ETH_ALEN,
+				  msg->group_id_len - ETH_ALEN);
+		break;
+	case P2P_ATTR_INVITATION_FLAGS:
+		if (len < 1) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Invitation "
+				   "Flag attribute (length %d)", len);
+			return -1;
+		}
+		msg->invitation_flags = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x",
+			   data[0]);
+		break;
+	case P2P_ATTR_MANAGEABILITY:
+		if (len < 1) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Manageability "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->manageability = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Manageability: bitmap 0x%x",
+			   data[0]);
+		break;
+	case P2P_ATTR_NOTICE_OF_ABSENCE:
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Notice of "
+				   "Absence attribute (length %d)", len);
+			return -1;
+		}
+		msg->noa = data;
+		msg->noa_len = len;
+		wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
+		break;
+	case P2P_ATTR_EXT_LISTEN_TIMING:
+		if (len < 4) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Extended Listen "
+				   "Timing attribute (length %d)", len);
+			return -1;
+		}
+		msg->ext_listen_timing = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing "
+			   "(period %u msec  interval %u msec)",
+			   WPA_GET_LE16(msg->ext_listen_timing),
+			   WPA_GET_LE16(msg->ext_listen_timing + 2));
+		break;
+	case P2P_ATTR_MINOR_REASON_CODE:
+		if (len < 1) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Minor Reason "
+				   "Code attribute (length %d)", len);
+			return -1;
+		}
+		msg->minor_reason_code = data;
+		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;
+	case P2P_ATTR_SERVICE_HASH:
+		if (len < P2PS_HASH_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Too short Service Hash (length %u)",
+				   len);
+			return -1;
+		}
+		msg->service_hash_count = len / P2PS_HASH_LEN;
+		msg->service_hash = data;
+		wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash(s)", data, len);
+		break;
+	case P2P_ATTR_SESSION_INFORMATION_DATA:
+		msg->session_info = data;
+		msg->session_info_len = len;
+		wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %u bytes - %p",
+			   len, data);
+		break;
+	case P2P_ATTR_CONNECTION_CAPABILITY:
+		if (len < 1) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Too short Connection Capability (length %u)",
+				   len);
+			return -1;
+		}
+		msg->conn_cap = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x",
+			   *msg->conn_cap);
+		break;
+	case P2P_ATTR_ADVERTISEMENT_ID:
+		if (len < 10) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Too short Advertisement ID (length %u)",
+				   len);
+			return -1;
+		}
+		msg->adv_id = data;
+		msg->adv_mac = &data[sizeof(u32)];
+		wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID %x",
+			   WPA_GET_LE32(data));
+		break;
+	case P2P_ATTR_ADVERTISED_SERVICE:
+		if (len < 8) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Too short Service Instance (length %u)",
+				   len);
+			return -1;
+		}
+		msg->adv_service_instance = data;
+		msg->adv_service_instance_len = len;
+		if (len <= 255 + 8) {
+			char str[256];
+			u8 namelen;
+
+			namelen = data[6];
+			if (namelen > len - 7)
+				break;
+			os_memcpy(str, &data[7], namelen);
+			str[namelen] = '\0';
+			wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %x-%s",
+				   WPA_GET_LE32(data), str);
+		} else {
+			wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %p",
+				   data);
+		}
+		break;
+	case P2P_ATTR_SESSION_ID:
+		if (len < sizeof(u32) + ETH_ALEN) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Too short Session ID Info (length %u)",
+				   len);
+			return -1;
+		}
+		msg->session_id = data;
+		msg->session_mac = &data[sizeof(u32)];
+		wpa_printf(MSG_DEBUG, "P2P: * Session ID: %x " MACSTR,
+			   WPA_GET_LE32(data), MAC2STR(msg->session_mac));
+		break;
+	case P2P_ATTR_FEATURE_CAPABILITY:
+		if (!len) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Too short Feature Capability (length %u)",
+				   len);
+			return -1;
+		}
+		msg->feature_cap = data;
+		msg->feature_cap_len = len;
+		wpa_printf(MSG_DEBUG, "P2P: * Feature Cap (length=%u)", len);
+		break;
+	case P2P_ATTR_PERSISTENT_GROUP:
+	{
+		if (len < ETH_ALEN || len > ETH_ALEN + SSID_MAX_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Invalid Persistent Group Info (length %u)",
+				   len);
+			return -1;
+		}
+
+		msg->persistent_dev = data;
+		msg->persistent_ssid_len = len - ETH_ALEN;
+		msg->persistent_ssid = &data[ETH_ALEN];
+		wpa_printf(MSG_DEBUG, "P2P: * Persistent Group: " MACSTR " %s",
+			   MAC2STR(msg->persistent_dev),
+			   wpa_ssid_txt(msg->persistent_ssid,
+					msg->persistent_ssid_len));
+		break;
+	}
+	default:
+		wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
+			   "(length %d)", id, len);
+		break;
+	}
+
+	return 0;
+}
+
+
+/**
+ * p2p_parse_p2p_ie - Parse P2P IE
+ * @buf: Concatenated P2P IE(s) payload
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: Caller is responsible for clearing the msg data structure before
+ * calling this function.
+ */
+int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg)
+{
+	const u8 *pos = wpabuf_head_u8(buf);
+	const u8 *end = pos + wpabuf_len(buf);
+
+	wpa_printf(MSG_DEBUG, "P2P: Parsing P2P IE");
+
+	while (pos < end) {
+		u16 attr_len;
+		u8 id;
+
+		if (end - pos < 3) {
+			wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute");
+			return -1;
+		}
+		id = *pos++;
+		attr_len = WPA_GET_LE16(pos);
+		pos += 2;
+		wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u",
+			   id, attr_len);
+		if (attr_len > end - pos) {
+			wpa_printf(MSG_DEBUG, "P2P: Attribute underflow "
+				   "(len=%u left=%d)",
+				   attr_len, (int) (end - pos));
+			wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos);
+			return -1;
+		}
+		if (p2p_parse_attribute(id, pos, attr_len, msg))
+			return -1;
+		pos += attr_len;
+	}
+
+	return 0;
+}
+
+
+static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg)
+{
+	struct wps_parse_attr attr;
+	int i;
+
+	wpa_printf(MSG_DEBUG, "P2P: Parsing WPS IE");
+	if (wps_parse_msg(buf, &attr))
+		return -1;
+	if (attr.dev_name && attr.dev_name_len < sizeof(msg->device_name) &&
+	    !msg->device_name[0])
+		os_memcpy(msg->device_name, attr.dev_name, attr.dev_name_len);
+	if (attr.config_methods) {
+		msg->wps_config_methods =
+			WPA_GET_BE16(attr.config_methods);
+		wpa_printf(MSG_DEBUG, "P2P: Config Methods (WPS): 0x%x",
+			   msg->wps_config_methods);
+	}
+	if (attr.dev_password_id) {
+		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];
+		msg->wps_pri_dev_type = attr.primary_dev_type;
+		wpa_printf(MSG_DEBUG, "P2P: Primary Device Type (WPS): %s",
+			   wps_dev_type_bin2str(msg->wps_pri_dev_type, devtype,
+						sizeof(devtype)));
+	}
+	if (attr.sec_dev_type_list) {
+		msg->wps_sec_dev_type_list = attr.sec_dev_type_list;
+		msg->wps_sec_dev_type_list_len = attr.sec_dev_type_list_len;
+	}
+
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		msg->wps_vendor_ext[i] = attr.vendor_ext[i];
+		msg->wps_vendor_ext_len[i] = attr.vendor_ext_len[i];
+	}
+
+	msg->manufacturer = attr.manufacturer;
+	msg->manufacturer_len = attr.manufacturer_len;
+	msg->model_name = attr.model_name;
+	msg->model_name_len = attr.model_name_len;
+	msg->model_number = attr.model_number;
+	msg->model_number_len = attr.model_number_len;
+	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;
+}
+
+
+/**
+ * p2p_parse_ies - Parse P2P message IEs (both WPS and P2P IE)
+ * @data: IEs from the message
+ * @len: Length of data buffer in octets
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: Caller is responsible for clearing the msg data structure before
+ * calling this function.
+ *
+ * Note: Caller must free temporary memory allocations by calling
+ * p2p_parse_free() when the parsed data is not needed anymore.
+ */
+int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg)
+{
+	struct ieee802_11_elems elems;
+
+	ieee802_11_parse_elems(data, len, &elems, 0);
+	if (elems.ds_params)
+		msg->ds_params = elems.ds_params;
+	if (elems.ssid)
+		msg->ssid = elems.ssid - 2;
+
+	msg->wps_attributes = ieee802_11_vendor_ie_concat(data, len,
+							  WPS_DEV_OUI_WFA);
+	if (msg->wps_attributes &&
+	    p2p_parse_wps_ie(msg->wps_attributes, msg)) {
+		p2p_parse_free(msg);
+		return -1;
+	}
+
+	msg->p2p_attributes = ieee802_11_vendor_ie_concat(data, len,
+							  P2P_IE_VENDOR_TYPE);
+	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;
+	}
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (elems.wfd) {
+		msg->wfd_subelems = ieee802_11_vendor_ie_concat(
+			data, len, WFD_IE_VENDOR_TYPE);
+	}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	msg->pref_freq_list = elems.pref_freq_list;
+	msg->pref_freq_list_len = elems.pref_freq_list_len;
+
+	return 0;
+}
+
+
+/**
+ * p2p_parse - Parse a P2P Action frame contents
+ * @data: Action frame payload after Category and Code fields
+ * @len: Length of data buffer in octets
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: Caller must free temporary memory allocations by calling
+ * p2p_parse_free() when the parsed data is not needed anymore.
+ */
+int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg)
+{
+	os_memset(msg, 0, sizeof(*msg));
+	wpa_printf(MSG_DEBUG, "P2P: Parsing the received message");
+	if (len < 1) {
+		wpa_printf(MSG_DEBUG, "P2P: No Dialog Token in the message");
+		return -1;
+	}
+	msg->dialog_token = data[0];
+	wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", msg->dialog_token);
+
+	return p2p_parse_ies(data + 1, len - 1, msg);
+}
+
+
+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
+ */
+void p2p_parse_free(struct p2p_message *msg)
+{
+	wpabuf_free(msg->p2p_attributes);
+	msg->p2p_attributes = NULL;
+	wpabuf_free(msg->wps_attributes);
+	msg->wps_attributes = NULL;
+#ifdef CONFIG_WIFI_DISPLAY
+	wpabuf_free(msg->wfd_subelems);
+	msg->wfd_subelems = NULL;
+#endif /* CONFIG_WIFI_DISPLAY */
+}
+
+
+int p2p_group_info_parse(const u8 *gi, size_t gi_len,
+			 struct p2p_group_info *info)
+{
+	const u8 *g, *gend;
+
+	os_memset(info, 0, sizeof(*info));
+	if (gi == NULL)
+		return 0;
+
+	g = gi;
+	gend = gi + gi_len;
+	while (g < gend) {
+		struct p2p_client_info *cli;
+		const u8 *t, *cend;
+		int count;
+
+		cli = &info->client[info->num_clients];
+		cend = g + 1 + g[0];
+		if (cend > gend)
+			return -1; /* invalid data */
+		/* g at start of P2P Client Info Descriptor */
+		/* t at Device Capability Bitmap */
+		t = g + 1 + 2 * ETH_ALEN;
+		if (t > cend)
+			return -1; /* invalid data */
+		cli->p2p_device_addr = g + 1;
+		cli->p2p_interface_addr = g + 1 + ETH_ALEN;
+		cli->dev_capab = t[0];
+
+		if (t + 1 + 2 + 8 + 1 > cend)
+			return -1; /* invalid data */
+
+		cli->config_methods = WPA_GET_BE16(&t[1]);
+		cli->pri_dev_type = &t[3];
+
+		t += 1 + 2 + 8;
+		/* t at Number of Secondary Device Types */
+		cli->num_sec_dev_types = *t++;
+		if (t + 8 * cli->num_sec_dev_types > cend)
+			return -1; /* invalid data */
+		cli->sec_dev_types = t;
+		t += 8 * cli->num_sec_dev_types;
+
+		/* t at Device Name in WPS TLV format */
+		if (t + 2 + 2 > cend)
+			return -1; /* invalid data */
+		if (WPA_GET_BE16(t) != ATTR_DEV_NAME)
+			return -1; /* invalid Device Name TLV */
+		t += 2;
+		count = WPA_GET_BE16(t);
+		t += 2;
+		if (count > cend - t)
+			return -1; /* invalid Device Name TLV */
+		if (count >= WPS_DEV_NAME_MAX_LEN)
+			count = WPS_DEV_NAME_MAX_LEN;
+		cli->dev_name = (const char *) t;
+		cli->dev_name_len = count;
+
+		g = cend;
+
+		info->num_clients++;
+		if (info->num_clients == P2P_MAX_GROUP_ENTRIES)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf,
+			       char *end)
+{
+	char *pos = buf;
+	int ret;
+	struct p2p_group_info info;
+	unsigned int i;
+
+	if (p2p_group_info_parse(gi, gi_len, &info) < 0)
+		return 0;
+
+	for (i = 0; i < info.num_clients; i++) {
+		struct p2p_client_info *cli;
+		char name[WPS_DEV_NAME_MAX_LEN + 1];
+		char devtype[WPS_DEV_TYPE_BUFSIZE];
+		u8 s;
+		int count;
+
+		cli = &info.client[i];
+		ret = os_snprintf(pos, end - pos, "p2p_group_client: "
+				  "dev=" MACSTR " iface=" MACSTR,
+				  MAC2STR(cli->p2p_device_addr),
+				  MAC2STR(cli->p2p_interface_addr));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+
+		ret = os_snprintf(pos, end - pos,
+				  " dev_capab=0x%x config_methods=0x%x "
+				  "dev_type=%s",
+				  cli->dev_capab, cli->config_methods,
+				  wps_dev_type_bin2str(cli->pri_dev_type,
+						       devtype,
+						       sizeof(devtype)));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+
+		for (s = 0; s < cli->num_sec_dev_types; s++) {
+			ret = os_snprintf(pos, end - pos, " dev_type=%s",
+					  wps_dev_type_bin2str(
+						  &cli->sec_dev_types[s * 8],
+						  devtype, sizeof(devtype)));
+			if (os_snprintf_error(end - pos, ret))
+				return pos - buf;
+			pos += ret;
+		}
+
+		os_memcpy(name, cli->dev_name, cli->dev_name_len);
+		name[cli->dev_name_len] = '\0';
+		count = (int) cli->dev_name_len - 1;
+		while (count >= 0) {
+			if (is_ctrl_char(name[count]))
+				name[count] = '_';
+			count--;
+		}
+
+		ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+/**
+ * p2p_attr_text - Build text format description of P2P IE attributes
+ * @data: P2P IE contents
+ * @buf: Buffer for returning text
+ * @end: Pointer to the end of the buf area
+ * Returns: Number of octets written to the buffer or -1 on faikure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_attr_text(struct wpabuf *data, char *buf, char *end)
+{
+	struct p2p_message msg;
+	char *pos = buf;
+	int ret;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(data, &msg))
+		return -1;
+
+	if (msg.capability) {
+		ret = os_snprintf(pos, end - pos,
+				  "p2p_dev_capab=0x%x\n"
+				  "p2p_group_capab=0x%x\n",
+				  msg.capability[0], msg.capability[1]);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (msg.pri_dev_type) {
+		char devtype[WPS_DEV_TYPE_BUFSIZE];
+		ret = os_snprintf(pos, end - pos,
+				  "p2p_primary_device_type=%s\n",
+				  wps_dev_type_bin2str(msg.pri_dev_type,
+						       devtype,
+						       sizeof(devtype)));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n",
+			  msg.device_name);
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	if (msg.p2p_device_addr) {
+		ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR
+				  "\n",
+				  MAC2STR(msg.p2p_device_addr));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n",
+			  msg.config_methods);
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	ret = p2p_group_info_text(msg.group_info, msg.group_info_len,
+				  pos, end);
+	if (ret < 0)
+		return pos - buf;
+	pos += ret;
+
+	return pos - buf;
+}
+
+
+int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie)
+{
+	struct p2p_message msg;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(p2p_ie, &msg))
+		return 0;
+
+	if (!msg.manageability)
+		return 0;
+
+	return !(msg.manageability[0] & P2P_MAN_CROSS_CONNECTION_PERMITTED);
+}
+
+
+u8 p2p_get_group_capab(const struct wpabuf *p2p_ie)
+{
+	struct p2p_message msg;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(p2p_ie, &msg))
+		return 0;
+
+	if (!msg.capability)
+		return 0;
+
+	return msg.capability[1];
+}
+
+
+const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie)
+{
+	struct p2p_message msg;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(p2p_ie, &msg))
+		return NULL;
+
+	if (msg.p2p_device_addr)
+		return msg.p2p_device_addr;
+	if (msg.device_id)
+		return msg.device_id;
+
+	return NULL;
+}
diff --git a/hostap/src/p2p/p2p_pd.c b/hostap/src/p2p/p2p_pd.c
new file mode 100644
index 0000000..8900945
--- /dev/null
+++ b/hostap/src/p2p/p2p_pd.c
@@ -0,0 +1,1440 @@
+/*
+ * Wi-Fi Direct - P2P provision discovery
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#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"
+
+
+/*
+ * Number of retries to attempt for provision discovery requests
+ * in case the peer is not listening.
+ */
+#define MAX_PROV_DISC_REQ_RETRIES 120
+
+
+static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
+					    u16 config_methods)
+{
+	u8 *len;
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	len = wpabuf_put(buf, 1);
+	wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
+
+	/* Config Methods */
+	wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
+	wpabuf_put_be16(buf, 2);
+	wpabuf_put_be16(buf, config_methods);
+
+	p2p_buf_update_ie_hdr(buf, len);
+}
+
+
+static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf)
+{
+	int found;
+	u8 intended_addr[ETH_ALEN];
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	int group_iface;
+
+	if (!p2p->cfg->get_go_info)
+		return;
+
+	found = p2p->cfg->get_go_info(
+		p2p->cfg->cb_ctx, intended_addr, ssid,
+		&ssid_len, &group_iface);
+	if (found) {
+		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
+				     ssid, ssid_len);
+
+		if (group_iface)
+			p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+		else
+			p2p_buf_add_intended_addr(buf, intended_addr);
+	} else {
+		if (!p2p->ssid_set) {
+			p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+			p2p->ssid_set = 1;
+		}
+
+		/* Add pre-composed P2P Group ID */
+		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
+				     p2p->ssid, p2p->ssid_len);
+
+		if (group_iface)
+			p2p_buf_add_intended_addr(
+				buf, p2p->intended_addr);
+		else
+			p2p_buf_add_intended_addr(
+				buf, p2p->cfg->dev_addr);
+	}
+}
+
+
+static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev,
+				  struct wpabuf *buf, u16 config_methods)
+{
+	struct p2ps_provision *prov = p2p->p2ps_prov;
+	struct p2ps_feature_capab fcap = { prov->cpt_mask, 0 };
+	int shared_group = 0;
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	u8 go_dev_addr[ETH_ALEN];
+	u8 intended_addr[ETH_ALEN];
+
+	/* If we might be explicite group owner, add GO details */
+	if (prov->conncap & (P2PS_SETUP_GROUP_OWNER |
+			     P2PS_SETUP_NEW))
+		p2ps_add_new_group_info(p2p, buf);
+
+	if (prov->status >= 0)
+		p2p_buf_add_status(buf, (u8) prov->status);
+	else
+		prov->method = config_methods;
+
+	if (p2p->cfg->get_persistent_group) {
+		shared_group = p2p->cfg->get_persistent_group(
+			p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0,
+			go_dev_addr, ssid, &ssid_len, intended_addr);
+	}
+
+	/* Add Operating Channel if conncap includes GO */
+	if (shared_group ||
+	    (prov->conncap & (P2PS_SETUP_GROUP_OWNER |
+			      P2PS_SETUP_NEW))) {
+		u8 tmp;
+
+		p2p_go_select_channel(p2p, dev, &tmp);
+
+		if (p2p->op_reg_class && p2p->op_channel)
+			p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+						      p2p->op_reg_class,
+						      p2p->op_channel);
+		else
+			p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+						      p2p->cfg->op_reg_class,
+						      p2p->cfg->op_channel);
+	}
+
+	p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->cfg->channels);
+
+	if (prov->info[0])
+		p2p_buf_add_session_info(buf, prov->info);
+
+	p2p_buf_add_connection_capability(buf, prov->conncap);
+
+	p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac);
+
+	if (shared_group || prov->conncap == P2PS_SETUP_NEW ||
+	    prov->conncap ==
+	    (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) ||
+	    prov->conncap ==
+	    (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) {
+		/* Add Config Timeout */
+		p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+					   p2p->client_timeout);
+	}
+
+	p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
+				   p2p->cfg->channel);
+
+	p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac);
+
+	p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap);
+
+	if (shared_group) {
+		p2p_buf_add_persistent_group_info(buf, go_dev_addr,
+						  ssid, ssid_len);
+		/* Add intended interface address if it is not added yet */
+		if ((prov->conncap == P2PS_SETUP_NONE ||
+		     prov->conncap == P2PS_SETUP_CLIENT) &&
+		    !is_zero_ether_addr(intended_addr))
+			p2p_buf_add_intended_addr(buf, intended_addr);
+	}
+}
+
+
+static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
+					       struct p2p_device *dev,
+					       int join)
+{
+	struct wpabuf *buf;
+	u8 *len;
+	size_t extra = 0;
+	u8 dialog_token = dev->dialog_token;
+	u16 config_methods = dev->req_config_methods;
+	struct p2p_device *go = join ? dev : NULL;
+	u8 group_capab;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_prov_disc_req)
+		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]);
+
+	if (p2p->p2ps_prov)
+		extra += os_strlen(p2p->p2ps_prov->info) + 1 +
+			sizeof(struct p2ps_provision);
+
+	buf = wpabuf_alloc(1000 + extra);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+
+	group_capab = 0;
+	if (p2p->p2ps_prov) {
+		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+		if (p2p->cross_connect)
+			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+		if (p2p->cfg->p2p_intra_bss)
+			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+	}
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+			       group_capab);
+	p2p_buf_add_device_info(buf, p2p, NULL);
+	if (p2p->p2ps_prov) {
+		p2ps_add_pd_req_attrs(p2p, dev, buf, config_methods);
+	} else if (go) {
+		p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
+				     go->oper_ssid, go->oper_ssid_len);
+	}
+	p2p_buf_update_ie_hdr(buf, len);
+
+	/* WPS IE with Config Methods attribute */
+	p2p_build_wps_ie_config_methods(buf, config_methods);
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_prov_disc_req)
+		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;
+}
+
+
+static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
+						struct p2p_device *dev,
+						u8 dialog_token,
+						enum p2p_status_code status,
+						u16 config_methods,
+						u32 adv_id,
+						const u8 *group_id,
+						size_t group_id_len,
+						const u8 *persist_ssid,
+						size_t persist_ssid_len,
+						const u8 *fcap,
+						u16 fcap_len)
+{
+	struct wpabuf *buf;
+	size_t extra = 0;
+	int persist = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
+	if (wfd_ie && group_id) {
+		size_t i;
+		for (i = 0; i < p2p->num_groups; i++) {
+			struct p2p_group *g = p2p->groups[i];
+			struct wpabuf *ie;
+			if (!p2p_group_is_group_id_match(g, group_id,
+							 group_id_len))
+				continue;
+			ie = p2p_group_get_wfd_ie(g);
+			if (ie) {
+				wfd_ie = ie;
+				break;
+			}
+		}
+	}
+	if (wfd_ie)
+		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(1000 + extra);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
+
+	/* Add P2P IE for P2PS */
+	if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) {
+		u8 *len = p2p_buf_add_ie_hdr(buf);
+		struct p2ps_provision *prov = p2p->p2ps_prov;
+		u8 group_capab;
+
+		if (!status && prov->status != -1)
+			status = prov->status;
+
+		p2p_buf_add_status(buf, status);
+		group_capab = P2P_GROUP_CAPAB_PERSISTENT_GROUP |
+			P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+		if (p2p->cross_connect)
+			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+		if (p2p->cfg->p2p_intra_bss)
+			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+		p2p_buf_add_capability(buf, p2p->dev_capab &
+				       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+				       group_capab);
+		p2p_buf_add_device_info(buf, p2p, NULL);
+
+		if (persist_ssid && p2p->cfg->get_persistent_group &&
+		    (status == P2P_SC_SUCCESS ||
+		     status == P2P_SC_SUCCESS_DEFERRED)) {
+			u8 ssid[SSID_MAX_LEN];
+			size_t ssid_len;
+			u8 go_dev_addr[ETH_ALEN];
+			u8 intended_addr[ETH_ALEN];
+
+			persist = p2p->cfg->get_persistent_group(
+				p2p->cfg->cb_ctx,
+				dev->info.p2p_device_addr,
+				persist_ssid, persist_ssid_len, go_dev_addr,
+				ssid, &ssid_len, intended_addr);
+			if (persist) {
+				p2p_buf_add_persistent_group_info(
+					buf, go_dev_addr, ssid, ssid_len);
+				if (!is_zero_ether_addr(intended_addr))
+					p2p_buf_add_intended_addr(
+						buf, intended_addr);
+			}
+		}
+
+		if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER))
+			p2ps_add_new_group_info(p2p, buf);
+
+		/* Add Operating Channel if conncap indicates GO */
+		if (persist || (prov->conncap & P2PS_SETUP_GROUP_OWNER)) {
+			u8 tmp;
+
+			if (dev)
+				p2p_go_select_channel(p2p, dev, &tmp);
+
+			if (p2p->op_reg_class && p2p->op_channel)
+				p2p_buf_add_operating_channel(
+					buf, p2p->cfg->country,
+					p2p->op_reg_class,
+					p2p->op_channel);
+			else
+				p2p_buf_add_operating_channel(
+					buf, p2p->cfg->country,
+					p2p->cfg->op_reg_class,
+					p2p->cfg->op_channel);
+		}
+
+		p2p_buf_add_channel_list(buf, p2p->cfg->country,
+					 &p2p->cfg->channels);
+
+		if (!persist && (status == P2P_SC_SUCCESS ||
+				 status == P2P_SC_SUCCESS_DEFERRED))
+			p2p_buf_add_connection_capability(buf, prov->conncap);
+
+		p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac);
+
+		p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+					   p2p->client_timeout);
+
+		p2p_buf_add_session_id(buf, prov->session_id,
+				       prov->session_mac);
+
+		p2p_buf_add_feature_capability(buf, fcap_len, fcap);
+		p2p_buf_update_ie_hdr(buf, len);
+	} else if (status != P2P_SC_SUCCESS || adv_id) {
+		u8 *len = p2p_buf_add_ie_hdr(buf);
+
+		p2p_buf_add_status(buf, status);
+
+		if (p2p->p2ps_prov)
+			p2p_buf_add_advertisement_id(buf, adv_id,
+						     p2p->p2ps_prov->adv_mac);
+
+		p2p_buf_update_ie_hdr(buf, len);
+	}
+
+	/* WPS IE with Config Methods attribute */
+	p2p_build_wps_ie_config_methods(buf, config_methods);
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (wfd_ie)
+		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;
+}
+
+
+static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id,
+				u32 session_id, u16 method,
+				const u8 *session_mac, const u8 *adv_mac)
+{
+	struct p2ps_provision *tmp;
+
+	if (!p2p->p2ps_prov) {
+		p2p->p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + 1);
+		if (!p2p->p2ps_prov)
+			return -1;
+	} else {
+		os_memset(p2p->p2ps_prov, 0, sizeof(struct p2ps_provision) + 1);
+	}
+
+	tmp = p2p->p2ps_prov;
+	tmp->adv_id = adv_id;
+	tmp->session_id = session_id;
+	tmp->method = method;
+	os_memcpy(tmp->session_mac, session_mac, ETH_ALEN);
+	os_memcpy(tmp->adv_mac, adv_mac, ETH_ALEN);
+	tmp->info[0] = '\0';
+
+	return 0;
+}
+
+
+static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask)
+{
+	int i;
+
+	for (i = 0; cpt_priority[i]; i++)
+		if (req_cpt_mask & cpt_priority[i])
+			return cpt_priority[i];
+
+	return 0;
+}
+
+
+void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
+			       const u8 *data, size_t len, int rx_freq)
+{
+	struct p2p_message msg;
+	struct p2p_device *dev;
+	int freq;
+	enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+	struct wpabuf *resp;
+	u32 adv_id = 0;
+	struct p2ps_advertisement *p2ps_adv = NULL;
+	u8 conncap = P2PS_SETUP_NEW;
+	u8 auto_accept = 0;
+	u32 session_id = 0;
+	u8 session_mac[ETH_ALEN];
+	u8 adv_mac[ETH_ALEN];
+	const u8 *group_mac;
+	int passwd_id = DEV_PW_DEFAULT;
+	u16 config_methods;
+	u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+	struct p2ps_feature_capab resp_fcap = { 0, 0 };
+	struct p2ps_feature_capab *req_fcap;
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR
+		" with config methods 0x%x (freq=%d)",
+		MAC2STR(sa), msg.wps_config_methods, rx_freq);
+	group_mac = msg.intended_addr;
+
+	dev = p2p_get_device(p2p, sa);
+	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+		p2p_dbg(p2p, "Provision Discovery Request from unknown peer "
+			MACSTR, MAC2STR(sa));
+
+		if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1,
+				   0)) {
+			p2p_dbg(p2p, "Provision Discovery Request add device failed "
+				MACSTR, MAC2STR(sa));
+		}
+	} else if (msg.wfd_subelems) {
+		wpabuf_free(dev->info.wfd_subelems);
+		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+	}
+
+	if (msg.adv_id)
+		allowed_config_methods |= WPS_CONFIG_P2PS;
+	else
+		allowed_config_methods |= WPS_CONFIG_PUSHBUTTON;
+
+	if (!(msg.wps_config_methods & allowed_config_methods)) {
+		p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request");
+		goto out;
+	}
+
+	/* Legacy (non-P2PS) - Unknown groups allowed for P2PS */
+	if (!msg.adv_id && msg.group_id) {
+		size_t i;
+		for (i = 0; i < p2p->num_groups; i++) {
+			if (p2p_group_is_group_id_match(p2p->groups[i],
+							msg.group_id,
+							msg.group_id_len))
+				break;
+		}
+		if (i == p2p->num_groups) {
+			p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject");
+			goto out;
+		}
+	}
+
+	if (dev) {
+		dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+				P2P_DEV_PD_PEER_KEYPAD |
+				P2P_DEV_PD_PEER_P2PS);
+
+		/* Remove stale persistent groups */
+		if (p2p->cfg->remove_stale_groups) {
+			p2p->cfg->remove_stale_groups(
+				p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
+				msg.persistent_dev,
+				msg.persistent_ssid, msg.persistent_ssid_len);
+		}
+	}
+	if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
+		p2p_dbg(p2p, "Peer " MACSTR
+			" requested us to show a PIN on display", MAC2STR(sa));
+		if (dev)
+			dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+		passwd_id = DEV_PW_USER_SPECIFIED;
+	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+		p2p_dbg(p2p, "Peer " MACSTR
+			" requested us to write its PIN using keypad",
+			MAC2STR(sa));
+		if (dev)
+			dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+		passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
+	} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
+		p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN",
+			MAC2STR(sa));
+		if (dev)
+			dev->flags |= P2P_DEV_PD_PEER_P2PS;
+		passwd_id = DEV_PW_P2PS_DEFAULT;
+	}
+
+	reject = P2P_SC_SUCCESS;
+
+	os_memset(session_mac, 0, ETH_ALEN);
+	os_memset(adv_mac, 0, ETH_ALEN);
+
+	/* Note 1: A feature capability attribute structure can be changed
+	 * in the future. The assumption is that such modifications are
+	 * backwards compatible, therefore we allow processing of
+	 * msg.feature_cap exceeding the size of the p2ps_feature_capab
+	 * structure.
+	 * Note 2: Vverification of msg.feature_cap_len below has to be changed
+	 * to allow 2 byte feature capability processing if struct
+	 * p2ps_feature_capab is extended to include additional fields and it
+	 * affects the structure size.
+	 */
+	if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac &&
+	    msg.feature_cap && msg.feature_cap_len >= sizeof(*req_fcap) &&
+	    (msg.status || msg.conn_cap)) {
+		u8 remote_conncap;
+
+		req_fcap = (struct p2ps_feature_capab *) msg.feature_cap;
+
+		os_memcpy(session_mac, msg.session_mac, ETH_ALEN);
+		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
+
+		session_id = WPA_GET_LE32(msg.session_id);
+		adv_id = WPA_GET_LE32(msg.adv_id);
+
+		if (!msg.status)
+			p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
+
+		p2p_dbg(p2p, "adv_id: %x - p2ps_adv - %p", adv_id, p2ps_adv);
+
+		if (msg.conn_cap)
+			conncap = *msg.conn_cap;
+		remote_conncap = conncap;
+
+		if (p2ps_adv) {
+			auto_accept = p2ps_adv->auto_accept;
+			conncap = p2p->cfg->p2ps_group_capability(
+				p2p->cfg->cb_ctx, conncap, auto_accept);
+
+			p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
+				auto_accept, remote_conncap, conncap);
+
+			resp_fcap.cpt =
+				p2ps_own_preferred_cpt(p2ps_adv->cpt_priority,
+						       req_fcap->cpt);
+
+			p2p_dbg(p2p,
+				"cpt: service:0x%x remote:0x%x result:0x%x",
+				p2ps_adv->cpt_mask, req_fcap->cpt,
+				resp_fcap.cpt);
+
+			if (!resp_fcap.cpt) {
+				p2p_dbg(p2p,
+					"Incompatible P2PS feature capability CPT bitmask");
+				reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+			} else if (p2ps_adv->config_methods &&
+				   !(msg.wps_config_methods &
+				   p2ps_adv->config_methods)) {
+				p2p_dbg(p2p,
+					"Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)",
+					p2ps_adv->config_methods,
+					msg.wps_config_methods);
+				reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+			} else if (!p2ps_adv->state) {
+				p2p_dbg(p2p, "P2PS state unavailable");
+				reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+			} else if (!conncap) {
+				p2p_dbg(p2p, "Conncap resolution failed");
+				reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+			}
+
+			if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+				p2p_dbg(p2p, "Keypad - always defer");
+				auto_accept = 0;
+			}
+
+			if (auto_accept || reject != P2P_SC_SUCCESS) {
+				struct p2ps_provision *tmp;
+
+				if (reject == P2P_SC_SUCCESS && !conncap) {
+					reject =
+						P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+				}
+
+				if (p2ps_setup_p2ps_prov(
+					    p2p, adv_id, session_id,
+					    msg.wps_config_methods,
+					    session_mac, adv_mac) < 0) {
+					reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+					goto out;
+				}
+
+				tmp = p2p->p2ps_prov;
+				if (conncap) {
+					tmp->conncap = conncap;
+					tmp->status = P2P_SC_SUCCESS;
+				} else {
+					tmp->conncap = auto_accept;
+					tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+				}
+
+				if (reject != P2P_SC_SUCCESS)
+					goto out;
+			}
+		} else if (!msg.status) {
+			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+			goto out;
+		}
+
+		if (!msg.status && !auto_accept &&
+		    (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) {
+			struct p2ps_provision *tmp;
+
+			if (!conncap) {
+				reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+				goto out;
+			}
+
+			if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
+						 msg.wps_config_methods,
+						 session_mac, adv_mac) < 0) {
+				reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+				goto out;
+			}
+			tmp = p2p->p2ps_prov;
+			reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+			tmp->status = reject;
+		}
+
+		if (msg.status) {
+			if (*msg.status &&
+			    *msg.status != P2P_SC_SUCCESS_DEFERRED) {
+				reject = *msg.status;
+			} else if (*msg.status == P2P_SC_SUCCESS_DEFERRED &&
+				   p2p->p2ps_prov) {
+				u16 method = p2p->p2ps_prov->method;
+
+				conncap = p2p->cfg->p2ps_group_capability(
+					p2p->cfg->cb_ctx, remote_conncap,
+					p2p->p2ps_prov->conncap);
+
+				p2p_dbg(p2p,
+					"Conncap: local:%d remote:%d result:%d",
+					p2p->p2ps_prov->conncap,
+					remote_conncap, conncap);
+
+				resp_fcap.cpt = p2ps_own_preferred_cpt(
+					p2p->p2ps_prov->cpt_priority,
+					req_fcap->cpt);
+
+				p2p_dbg(p2p,
+					"cpt: local:0x%x remote:0x%x result:0x%x",
+					p2p->p2ps_prov->cpt_mask,
+					req_fcap->cpt, resp_fcap.cpt);
+
+				/*
+				 * Ensure that if we asked for PIN originally,
+				 * our method is consistent with original
+				 * request.
+				 */
+				if (method & WPS_CONFIG_DISPLAY)
+					method = WPS_CONFIG_KEYPAD;
+				else if (method & WPS_CONFIG_KEYPAD)
+					method = WPS_CONFIG_DISPLAY;
+
+				if (!conncap ||
+				    !(msg.wps_config_methods & method)) {
+					/*
+					 * Reject this "Deferred Accept*
+					 * if incompatible conncap or method
+					 */
+					reject =
+						P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+				} else if (!resp_fcap.cpt) {
+					p2p_dbg(p2p,
+						"Incompatible P2PS feature capability CPT bitmask");
+					reject =
+						P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+				} else {
+					reject = P2P_SC_SUCCESS;
+				}
+
+				p2p->p2ps_prov->status = reject;
+				p2p->p2ps_prov->conncap = conncap;
+			}
+		}
+	}
+
+out:
+	if (reject == P2P_SC_SUCCESS ||
+	    reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
+		config_methods = msg.wps_config_methods;
+	else
+		config_methods = 0;
+	resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, reject,
+					config_methods, adv_id,
+					msg.group_id, msg.group_id_len,
+					msg.persistent_ssid,
+					msg.persistent_ssid_len,
+					(const u8 *) &resp_fcap,
+					sizeof(resp_fcap));
+	if (resp == NULL) {
+		p2p_parse_free(&msg);
+		return;
+	}
+	p2p_dbg(p2p, "Sending Provision Discovery Response");
+	if (rx_freq > 0)
+		freq = rx_freq;
+	else
+		freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+	if (freq < 0) {
+		p2p_dbg(p2p, "Unknown regulatory class/channel");
+		wpabuf_free(resp);
+		p2p_parse_free(&msg);
+		return;
+	}
+	p2p->pending_action_state = P2P_PENDING_PD_RESPONSE;
+	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+			    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);
+
+	if (!p2p->cfg->p2ps_prov_complete) {
+		/* Don't emit anything */
+	} else if (msg.status && *msg.status != P2P_SC_SUCCESS &&
+		   *msg.status != P2P_SC_SUCCESS_DEFERRED) {
+		reject = *msg.status;
+		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
+					     sa, adv_mac, session_mac,
+					     NULL, adv_id, session_id,
+					     0, 0, msg.persistent_ssid,
+					     msg.persistent_ssid_len,
+					     0, 0, NULL, NULL, 0);
+	} else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
+		   p2p->p2ps_prov) {
+		p2p->p2ps_prov->status = reject;
+		p2p->p2ps_prov->conncap = conncap;
+
+		if (reject != P2P_SC_SUCCESS)
+			p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
+						     sa, adv_mac, session_mac,
+						     NULL, adv_id,
+						     session_id, conncap, 0,
+						     msg.persistent_ssid,
+						     msg.persistent_ssid_len, 0,
+						     0, NULL, NULL, 0);
+		else
+			p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx,
+						     *msg.status,
+						     sa, adv_mac, session_mac,
+						     group_mac, adv_id,
+						     session_id, conncap,
+						     passwd_id,
+						     msg.persistent_ssid,
+						     msg.persistent_ssid_len, 0,
+						     0, NULL,
+						     (const u8 *) &resp_fcap,
+						     sizeof(resp_fcap));
+	} else if (msg.status && p2p->p2ps_prov) {
+		p2p->p2ps_prov->status = P2P_SC_SUCCESS;
+		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa,
+					     adv_mac, session_mac, group_mac,
+					     adv_id, session_id, conncap,
+					     passwd_id,
+					     msg.persistent_ssid,
+					     msg.persistent_ssid_len,
+					     0, 0, NULL,
+					     (const u8 *) &resp_fcap,
+					     sizeof(resp_fcap));
+	} else if (msg.status) {
+	} else if (auto_accept && reject == P2P_SC_SUCCESS) {
+		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
+					     sa, adv_mac, session_mac,
+					     group_mac, adv_id, session_id,
+					     conncap, passwd_id,
+					     msg.persistent_ssid,
+					     msg.persistent_ssid_len,
+					     0, 0, NULL,
+					     (const u8 *) &resp_fcap,
+					     sizeof(resp_fcap));
+	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+		   (!msg.session_info || !msg.session_info_len)) {
+		p2p->p2ps_prov->method = msg.wps_config_methods;
+
+		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
+					     sa, adv_mac, session_mac,
+					     group_mac, adv_id, session_id,
+					     conncap, passwd_id,
+					     msg.persistent_ssid,
+					     msg.persistent_ssid_len,
+					     0, 1, NULL,
+					     (const u8 *) &resp_fcap,
+					     sizeof(resp_fcap));
+	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+		size_t buf_len = msg.session_info_len;
+		char *buf = os_malloc(2 * buf_len + 1);
+
+		if (buf) {
+			p2p->p2ps_prov->method = msg.wps_config_methods;
+
+			utf8_escape((char *) msg.session_info, buf_len,
+				    buf, 2 * buf_len + 1);
+
+			p2p->cfg->p2ps_prov_complete(
+				p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa,
+				adv_mac, session_mac, group_mac, adv_id,
+				session_id, conncap, passwd_id,
+				msg.persistent_ssid, msg.persistent_ssid_len,
+				0, 1, buf,
+				(const u8 *) &resp_fcap, sizeof(resp_fcap));
+
+			os_free(buf);
+		}
+	}
+
+	/*
+	 * prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN,
+	 * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
+	 * Call it either on legacy P2P PD or on P2PS PD only if we need to
+	 * enter/show PIN.
+	 *
+	 * The callback is called in the following cases:
+	 * 1. Legacy P2P PD request, response status SUCCESS
+	 * 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE,
+	 *    response status: SUCCESS
+	 * 3. P2PS advertiser, method  DISPLAY, autoaccept: FALSE,
+	 *    response status: INFO_CURRENTLY_UNAVAILABLE
+	 * 4. P2PS advertiser, method: KEYPAD, autoaccept==any,
+	 *    response status: INFO_CURRENTLY_UNAVAILABLE
+	 * 5. P2PS follow-on with SUCCESS_DEFERRED,
+	 *    advertiser role: DISPLAY, autoaccept: FALSE,
+	 *    seeker: KEYPAD, response status: SUCCESS
+	 */
+	if (p2p->cfg->prov_disc_req &&
+	    ((reject == P2P_SC_SUCCESS && !msg.adv_id) ||
+	     (!msg.status &&
+	     (reject == P2P_SC_SUCCESS ||
+	      reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) &&
+	      passwd_id == DEV_PW_USER_SPECIFIED) ||
+	     (!msg.status &&
+	      reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+	      passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
+	     (reject == P2P_SC_SUCCESS &&
+	      msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
+	       passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) {
+		const u8 *dev_addr = sa;
+
+		if (msg.p2p_device_addr)
+			dev_addr = msg.p2p_device_addr;
+		p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa,
+					msg.wps_config_methods,
+					dev_addr, msg.pri_dev_type,
+					msg.device_name, msg.config_methods,
+					msg.capability ? msg.capability[0] : 0,
+					msg.capability ? msg.capability[1] :
+					0,
+					msg.group_id, msg.group_id_len);
+	}
+
+	if (dev && reject == P2P_SC_SUCCESS) {
+		switch (config_methods) {
+		case WPS_CONFIG_DISPLAY:
+			dev->wps_prov_info = WPS_CONFIG_KEYPAD;
+			break;
+		case WPS_CONFIG_KEYPAD:
+			dev->wps_prov_info = WPS_CONFIG_DISPLAY;
+			break;
+		case WPS_CONFIG_PUSHBUTTON:
+			dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON;
+			break;
+		case WPS_CONFIG_P2PS:
+			dev->wps_prov_info = WPS_CONFIG_P2PS;
+			break;
+		default:
+			dev->wps_prov_info = 0;
+			break;
+		}
+
+		if (msg.intended_addr)
+			os_memcpy(dev->interface_addr, msg.intended_addr,
+				  ETH_ALEN);
+	}
+	p2p_parse_free(&msg);
+}
+
+
+static int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p,
+				     struct p2p_message *msg)
+{
+	u8 conn_cap_go = 0;
+	u8 conn_cap_cli = 0;
+	u32 session_id;
+	u32 adv_id;
+
+#define P2PS_PD_RESP_CHECK(_val, _attr) \
+	do { \
+		if ((_val) && !msg->_attr) { \
+			p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \
+			return -1; \
+		} \
+	} while (0)
+
+	P2PS_PD_RESP_CHECK(1, status);
+	P2PS_PD_RESP_CHECK(1, adv_id);
+	P2PS_PD_RESP_CHECK(1, adv_mac);
+	P2PS_PD_RESP_CHECK(1, capability);
+	P2PS_PD_RESP_CHECK(1, p2p_device_info);
+	P2PS_PD_RESP_CHECK(1, session_id);
+	P2PS_PD_RESP_CHECK(1, session_mac);
+	P2PS_PD_RESP_CHECK(1, feature_cap);
+
+	session_id = WPA_GET_LE32(msg->session_id);
+	adv_id = WPA_GET_LE32(msg->adv_id);
+
+	if (p2p->p2ps_prov->session_id != session_id) {
+		p2p_dbg(p2p,
+			"Ignore PD Response with unexpected Session ID");
+		return -1;
+	}
+
+	if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac,
+		      ETH_ALEN)) {
+		p2p_dbg(p2p,
+			"Ignore PD Response with unexpected Session MAC");
+		return -1;
+	}
+
+	if (p2p->p2ps_prov->adv_id != adv_id) {
+		p2p_dbg(p2p,
+			"Ignore PD Response with unexpected Advertisement ID");
+		return -1;
+	}
+
+	if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) {
+		p2p_dbg(p2p,
+			"Ignore PD Response with unexpected Advertisement MAC");
+		return -1;
+	}
+
+	if (msg->listen_channel) {
+		p2p_dbg(p2p,
+			"Ignore malformed PD Response - unexpected Listen Channel");
+		return -1;
+	}
+
+	if (*msg->status == P2P_SC_SUCCESS &&
+	    !(!!msg->conn_cap ^ !!msg->persistent_dev)) {
+		p2p_dbg(p2p,
+			"Ignore malformed PD Response - either conn_cap or persistent group should be present");
+		return -1;
+	}
+
+	if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) {
+		p2p_dbg(p2p,
+			"Ignore malformed PD Response - persistent group is present, but the status isn't success");
+		return -1;
+	}
+
+	if (msg->conn_cap) {
+		conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER;
+		conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT;
+	}
+
+	P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
+			   channel_list);
+	P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
+			   config_timeout);
+
+	P2PS_PD_RESP_CHECK(conn_cap_go, group_id);
+	P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr);
+	P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel);
+	/*
+	 * TODO: Also validate that operating channel is present if the device
+	 * is a GO in a persistent group. We can't do it here since we don't
+	 * know what is the role of the peer. It should be probably done in
+	 * p2ps_prov_complete callback, but currently operating channel isn't
+	 * passed to it.
+	 */
+
+#undef P2PS_PD_RESP_CHECK
+
+	return 0;
+}
+
+
+void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
+				const u8 *data, size_t len)
+{
+	struct p2p_message msg;
+	struct p2p_device *dev;
+	u16 report_config_methods = 0, req_config_methods;
+	u8 status = P2P_SC_SUCCESS;
+	u32 adv_id = 0;
+	u8 conncap = P2PS_SETUP_NEW;
+	u8 adv_mac[ETH_ALEN];
+	const u8 *group_mac;
+	int passwd_id = DEV_PW_DEFAULT;
+	int p2ps_seeker;
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) {
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	/* Parse the P2PS members present */
+	if (msg.status)
+		status = *msg.status;
+
+	group_mac = msg.intended_addr;
+
+	if (msg.adv_mac)
+		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
+	else
+		os_memset(adv_mac, 0, ETH_ALEN);
+
+	if (msg.adv_id)
+		adv_id = WPA_GET_LE32(msg.adv_id);
+
+	if (msg.conn_cap) {
+		conncap = *msg.conn_cap;
+
+		/* Switch bits to local relative */
+		switch (conncap) {
+		case P2PS_SETUP_GROUP_OWNER:
+			conncap = P2PS_SETUP_CLIENT;
+			break;
+		case P2PS_SETUP_CLIENT:
+			conncap = P2PS_SETUP_GROUP_OWNER;
+			break;
+		}
+	}
+
+	p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR
+		" with config methods 0x%x",
+		MAC2STR(sa), msg.wps_config_methods);
+
+	dev = p2p_get_device(p2p, sa);
+	if (dev == NULL || !dev->req_config_methods) {
+		p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR
+			" with no pending request", MAC2STR(sa));
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (dev->dialog_token != msg.dialog_token) {
+		p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)",
+			msg.dialog_token, dev->dialog_token);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (p2p->pending_action_state == P2P_PENDING_PD) {
+		os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
+		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	}
+
+	p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker;
+
+	/*
+	 * Use a local copy of the requested config methods since
+	 * p2p_reset_pending_pd() can clear this in the peer entry.
+	 */
+	req_config_methods = dev->req_config_methods;
+
+	/*
+	 * If the response is from the peer to whom a user initiated request
+	 * was sent earlier, we reset that state info here.
+	 */
+	if (p2p->user_initiated_pd &&
+	    os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0)
+		p2p_reset_pending_pd(p2p);
+
+	if (msg.wps_config_methods != req_config_methods) {
+		p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x",
+			msg.wps_config_methods, req_config_methods);
+		if (p2p->cfg->prov_disc_fail)
+			p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
+						 P2P_PROV_DISC_REJECTED,
+						 adv_id, adv_mac, NULL);
+		p2p_parse_free(&msg);
+		p2ps_prov_free(p2p);
+		goto out;
+	}
+
+	report_config_methods = req_config_methods;
+	dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+			P2P_DEV_PD_PEER_KEYPAD |
+			P2P_DEV_PD_PEER_P2PS);
+	if (req_config_methods & WPS_CONFIG_DISPLAY) {
+		p2p_dbg(p2p, "Peer " MACSTR
+			" accepted to show a PIN on display", MAC2STR(sa));
+		dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+		passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
+	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+		p2p_dbg(p2p, "Peer " MACSTR
+			" accepted to write our PIN using keypad",
+			MAC2STR(sa));
+		dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+		passwd_id = DEV_PW_USER_SPECIFIED;
+	} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
+		p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN",
+			MAC2STR(sa));
+		dev->flags |= P2P_DEV_PD_PEER_P2PS;
+		passwd_id = DEV_PW_P2PS_DEFAULT;
+	}
+
+	if ((msg.conn_cap || msg.persistent_dev) &&
+	    (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
+	    p2p->p2ps_prov) {
+		if (p2p->cfg->p2ps_prov_complete) {
+			p2p->cfg->p2ps_prov_complete(
+				p2p->cfg->cb_ctx, status, sa, adv_mac,
+				p2p->p2ps_prov->session_mac,
+				group_mac, adv_id, p2p->p2ps_prov->session_id,
+				conncap, passwd_id, msg.persistent_ssid,
+				msg.persistent_ssid_len, 1, 0, NULL,
+				msg.feature_cap, msg.feature_cap_len);
+		}
+		p2ps_prov_free(p2p);
+	} else if (status != P2P_SC_SUCCESS &&
+		   status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+		   status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
+		if (p2p->cfg->p2ps_prov_complete)
+			p2p->cfg->p2ps_prov_complete(
+				p2p->cfg->cb_ctx, status, sa, adv_mac,
+				p2p->p2ps_prov->session_mac,
+				group_mac, adv_id, p2p->p2ps_prov->session_id,
+				0, 0, NULL, 0, 1, 0, NULL, NULL, 0);
+		p2ps_prov_free(p2p);
+	}
+
+	if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+		if (p2p->cfg->remove_stale_groups) {
+			p2p->cfg->remove_stale_groups(p2p->cfg->cb_ctx,
+						      dev->info.p2p_device_addr,
+						      NULL, NULL, 0);
+		}
+
+		if (msg.session_info && msg.session_info_len) {
+			size_t info_len = msg.session_info_len;
+			char *deferred_sess_resp = os_malloc(2 * info_len + 1);
+
+			if (!deferred_sess_resp) {
+				p2p_parse_free(&msg);
+				p2ps_prov_free(p2p);
+				goto out;
+			}
+			utf8_escape((char *) msg.session_info, info_len,
+				    deferred_sess_resp, 2 * info_len + 1);
+
+			if (p2p->cfg->prov_disc_fail)
+				p2p->cfg->prov_disc_fail(
+					p2p->cfg->cb_ctx, sa,
+					P2P_PROV_DISC_INFO_UNAVAILABLE,
+					adv_id, adv_mac,
+					deferred_sess_resp);
+			os_free(deferred_sess_resp);
+		} else
+			if (p2p->cfg->prov_disc_fail)
+				p2p->cfg->prov_disc_fail(
+					p2p->cfg->cb_ctx, sa,
+					P2P_PROV_DISC_INFO_UNAVAILABLE,
+					adv_id, adv_mac, NULL);
+	} else if (status != P2P_SC_SUCCESS) {
+		p2p_dbg(p2p, "Peer rejected our Provision Discovery Request");
+		if (p2p->cfg->prov_disc_fail)
+			p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
+						 P2P_PROV_DISC_REJECTED,
+						 adv_id, adv_mac, NULL);
+		p2p_parse_free(&msg);
+		p2ps_prov_free(p2p);
+		goto out;
+	}
+
+	/* Store the provisioning info */
+	dev->wps_prov_info = msg.wps_config_methods;
+	if (msg.intended_addr)
+		os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN);
+
+	p2p_parse_free(&msg);
+
+out:
+	dev->req_config_methods = 0;
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
+		p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with "
+			MACSTR, MAC2STR(dev->info.p2p_device_addr));
+		dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
+		p2p_connect_send(p2p, dev);
+		return;
+	}
+
+	/*
+	 * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN,
+	 * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
+	 * Call it only for a legacy P2P PD or for P2PS PD scenarios where
+	 * show/enter PIN events are needed.
+	 *
+	 * The callback is called in the following cases:
+	 * 1. Legacy P2P PD response with a status SUCCESS
+	 * 2. P2PS, advertiser method: DISPLAY, autoaccept: true,
+	 *    response status: SUCCESS, local method KEYPAD
+	 * 3. P2PS, advertiser method: KEYPAD,Seeker side,
+	 *    response status: INFO_CURRENTLY_UNAVAILABLE,
+	 *    local method: DISPLAY
+	 */
+	if (p2p->cfg->prov_disc_resp &&
+	    ((status == P2P_SC_SUCCESS && !adv_id) ||
+	     (p2ps_seeker && status == P2P_SC_SUCCESS &&
+	      passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
+	     (p2ps_seeker &&
+	      status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+	      passwd_id == DEV_PW_USER_SPECIFIED)))
+		p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
+					 report_config_methods);
+
+	if (p2p->state == P2P_PD_DURING_FIND) {
+		p2p_clear_timeout(p2p);
+		p2p_continue_find(p2p);
+	}
+}
+
+
+int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
+			   int join, int force_freq)
+{
+	struct wpabuf *req;
+	int freq;
+
+	if (force_freq > 0)
+		freq = force_freq;
+	else
+		freq = dev->listen_freq > 0 ? dev->listen_freq :
+			dev->oper_freq;
+	if (freq <= 0) {
+		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
+			MACSTR " to send Provision Discovery Request",
+			MAC2STR(dev->info.p2p_device_addr));
+		return -1;
+	}
+
+	if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+		if (!(dev->info.dev_capab &
+		      P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+			p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR
+				" that is in a group and is not discoverable",
+				MAC2STR(dev->info.p2p_device_addr));
+			return -1;
+		}
+		/* TODO: use device discoverability request through GO */
+	}
+
+	if (p2p->p2ps_prov) {
+		if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) {
+			if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY)
+				dev->req_config_methods = WPS_CONFIG_KEYPAD;
+			else if (p2p->p2ps_prov->method == WPS_CONFIG_KEYPAD)
+				dev->req_config_methods = WPS_CONFIG_DISPLAY;
+			else
+				dev->req_config_methods = WPS_CONFIG_P2PS;
+		} else {
+			/* Order of preference, based on peer's capabilities */
+			if (p2p->p2ps_prov->method)
+				dev->req_config_methods =
+					p2p->p2ps_prov->method;
+			else if (dev->info.config_methods & WPS_CONFIG_P2PS)
+				dev->req_config_methods = WPS_CONFIG_P2PS;
+			else if (dev->info.config_methods & WPS_CONFIG_DISPLAY)
+				dev->req_config_methods = WPS_CONFIG_DISPLAY;
+			else
+				dev->req_config_methods = WPS_CONFIG_KEYPAD;
+		}
+		p2p_dbg(p2p,
+			"Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x",
+			p2p->p2ps_prov->method, p2p->p2ps_prov->status,
+			dev->req_config_methods);
+	}
+
+	req = p2p_build_prov_disc_req(p2p, dev, join);
+	if (req == NULL)
+		return -1;
+
+	if (p2p->state != P2P_IDLE)
+		p2p_stop_listen_for_freq(p2p, freq);
+	p2p->pending_action_state = P2P_PENDING_PD;
+	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) {
+		p2p_dbg(p2p, "Failed to send Action frame");
+		wpabuf_free(req);
+		return -1;
+	}
+
+	os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);
+
+	wpabuf_free(req);
+	return 0;
+}
+
+
+int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+		      struct p2ps_provision *p2ps_prov,
+		      u16 config_methods, int join, int force_freq,
+		      int user_initiated_pd)
+{
+	struct p2p_device *dev;
+
+	dev = p2p_get_device(p2p, peer_addr);
+	if (dev == NULL)
+		dev = p2p_get_device_interface(p2p, peer_addr);
+	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+		p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR
+			" not yet known", MAC2STR(peer_addr));
+		os_free(p2ps_prov);
+		return -1;
+	}
+
+	p2p_dbg(p2p, "Provision Discovery Request with " MACSTR
+		" (config methods 0x%x)",
+		MAC2STR(peer_addr), config_methods);
+	if (config_methods == 0 && !p2ps_prov) {
+		os_free(p2ps_prov);
+		return -1;
+	}
+
+	if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED &&
+	    p2p->p2ps_prov) {
+		/* Use cached method from deferred provisioning */
+		p2ps_prov->method = p2p->p2ps_prov->method;
+	}
+
+	/* Reset provisioning info */
+	dev->wps_prov_info = 0;
+	p2ps_prov_free(p2p);
+	p2p->p2ps_prov = p2ps_prov;
+
+	dev->req_config_methods = config_methods;
+	if (join)
+		dev->flags |= P2P_DEV_PD_FOR_JOIN;
+	else
+		dev->flags &= ~P2P_DEV_PD_FOR_JOIN;
+
+	if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
+	    p2p->state != P2P_LISTEN_ONLY) {
+		p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with "
+			MACSTR " (config methods 0x%x)",
+			MAC2STR(peer_addr), config_methods);
+		return 0;
+	}
+
+	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;
+
+	/*
+	 * Assign dialog token here to use the same value in each retry within
+	 * the same PD exchange.
+	 */
+	dev->dialog_token++;
+	if (dev->dialog_token == 0)
+		dev->dialog_token = 1;
+
+	return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
+}
+
+
+void p2p_reset_pending_pd(struct p2p_data *p2p)
+{
+	struct p2p_device *dev;
+
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		if (os_memcmp(p2p->pending_pd_devaddr,
+			      dev->info.p2p_device_addr, ETH_ALEN))
+			continue;
+		if (!dev->req_config_methods)
+			continue;
+		if (dev->flags & P2P_DEV_PD_FOR_JOIN)
+			continue;
+		/* Reset the config methods of the device */
+		dev->req_config_methods = 0;
+	}
+
+	p2p->user_initiated_pd = 0;
+	os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
+	p2p->pd_retries = 0;
+	p2p->pd_force_freq = 0;
+}
+
+
+void p2ps_prov_free(struct p2p_data *p2p)
+{
+	os_free(p2p->p2ps_prov);
+	p2p->p2ps_prov = NULL;
+}
diff --git a/hostap/src/p2p/p2p_sd.c b/hostap/src/p2p/p2p_sd.c
new file mode 100644
index 0000000..1a2af04
--- /dev/null
+++ b/hostap/src/p2p/p2p_sd.c
@@ -0,0 +1,917 @@
+/*
+ * Wi-Fi Direct - P2P service discovery
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+static int wfd_wsd_supported(struct wpabuf *wfd)
+{
+	const u8 *pos, *end;
+	u8 subelem;
+	u16 len;
+
+	if (wfd == NULL)
+		return 0;
+
+	pos = wpabuf_head(wfd);
+	end = pos + wpabuf_len(wfd);
+
+	while (pos + 3 <= end) {
+		subelem = *pos++;
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (pos + len > end)
+			break;
+
+		if (subelem == WFD_SUBELEM_DEVICE_INFO && len >= 6) {
+			u16 info = WPA_GET_BE16(pos);
+			return !!(info & 0x0040);
+		}
+
+		pos += len;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
+					 struct p2p_device *dev)
+{
+	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 */
+#ifdef CONFIG_WIFI_DISPLAY
+	if (wfd_wsd_supported(dev->info.wfd_subelems))
+		wsd = 1;
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	for (q = p2p->sd_queries; q; q = q->next) {
+		/* Use WSD only if the peer indicates support or it */
+		if (q->wsd && !wsd)
+			continue;
+		/* 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)
+				goto found;
+			count++;
+		}
+		if (!q->for_all_peers &&
+		    os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) ==
+		    0)
+			goto found;
+	}
+
+	return NULL;
+
+found:
+	if (dev->sd_reqs > 100) {
+		p2p_dbg(p2p, "Too many SD request attempts to " MACSTR
+			" - skip remaining queries",
+			MAC2STR(dev->info.p2p_device_addr));
+		return NULL;
+	}
+	return q;
+}
+
+
+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
+				p2p->sd_queries = q->next;
+			if (p2p->sd_query == query)
+				p2p->sd_query = NULL;
+			return 1;
+		}
+		if (q->for_all_peers)
+			query_number++;
+		prev = q;
+		q = q->next;
+	}
+	return 0;
+}
+
+
+static void p2p_free_sd_query(struct p2p_sd_query *q)
+{
+	if (q == NULL)
+		return;
+	wpabuf_free(q->tlvs);
+	os_free(q);
+}
+
+
+void p2p_free_sd_queries(struct p2p_data *p2p)
+{
+	struct p2p_sd_query *q, *prev;
+	q = p2p->sd_queries;
+	p2p->sd_queries = NULL;
+	while (q) {
+		prev = q;
+		q = q->next;
+		p2p_free_sd_query(prev);
+	}
+	p2p->num_p2p_sd_queries = 0;
+}
+
+
+static struct wpabuf * p2p_build_sd_query(u16 update_indic,
+					  struct wpabuf *tlvs)
+{
+	struct wpabuf *buf;
+	u8 *len_pos;
+
+	buf = gas_anqp_build_initial_req(0, 100 + wpabuf_len(tlvs));
+	if (buf == NULL)
+		return NULL;
+
+	/* ANQP Query Request Frame */
+	len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+	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);
+
+	gas_anqp_set_len(buf);
+
+	return buf;
+}
+
+
+static void p2p_send_gas_comeback_req(struct p2p_data *p2p, const u8 *dst,
+				      u8 dialog_token, int freq)
+{
+	struct wpabuf *req;
+
+	req = gas_build_comeback_req(dialog_token);
+	if (req == NULL)
+		return;
+
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, dst,
+			    wpabuf_head(req), wpabuf_len(req), 200) < 0)
+		p2p_dbg(p2p, "Failed to send Action frame");
+
+	wpabuf_free(req);
+}
+
+
+static struct wpabuf * p2p_build_sd_response(u8 dialog_token, u16 status_code,
+					     u16 comeback_delay,
+					     u16 update_indic,
+					     const struct wpabuf *tlvs)
+{
+	struct wpabuf *buf;
+	u8 *len_pos;
+
+	buf = gas_anqp_build_initial_resp(dialog_token, status_code,
+					  comeback_delay,
+					  100 + (tlvs ? wpabuf_len(tlvs) : 0));
+	if (buf == NULL)
+		return NULL;
+
+	if (tlvs) {
+		/* ANQP Query Response Frame */
+		len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
+		 /* Service Update Indicator */
+		wpabuf_put_le16(buf, update_indic);
+		wpabuf_put_buf(buf, tlvs);
+		gas_anqp_set_element_len(buf, len_pos);
+	}
+
+	gas_anqp_set_len(buf);
+
+	return buf;
+}
+
+
+static struct wpabuf * p2p_build_gas_comeback_resp(u8 dialog_token,
+						   u16 status_code,
+						   u16 update_indic,
+						   const u8 *data, size_t len,
+						   u8 frag_id, u8 more,
+						   u16 total_len)
+{
+	struct wpabuf *buf;
+
+	buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id,
+					   more, 0, 100 + len);
+	if (buf == NULL)
+		return NULL;
+
+	if (frag_id == 0) {
+		/* ANQP Query Response Frame */
+		wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */
+		wpabuf_put_le16(buf, 3 + 1 + 2 + total_len);
+		wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
+		/* Service Update Indicator */
+		wpabuf_put_le16(buf, update_indic);
+	}
+
+	wpabuf_put_data(buf, data, len);
+	gas_anqp_set_len(buf);
+
+	return buf;
+}
+
+
+int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev)
+{
+	struct wpabuf *req;
+	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) {
+		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
+			MACSTR " to send SD Request",
+			MAC2STR(dev->info.p2p_device_addr));
+		return -1;
+	}
+
+	query = p2p_pending_sd_req(p2p, dev);
+	if (query == NULL)
+		return -1;
+
+	p2p_dbg(p2p, "Start Service Discovery with " MACSTR,
+		MAC2STR(dev->info.p2p_device_addr));
+
+	req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs);
+	if (req == NULL)
+		return -1;
+
+	dev->sd_reqs++;
+	p2p->sd_peer = dev;
+	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), wait_time) < 0) {
+		p2p_dbg(p2p, "Failed to send Action frame");
+		ret = -1;
+	}
+
+	wpabuf_free(req);
+
+	return ret;
+}
+
+
+void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa,
+			    const u8 *data, size_t len, int rx_freq)
+{
+	const u8 *pos = data;
+	const u8 *end = data + len;
+	const u8 *next;
+	u8 dialog_token;
+	u16 slen;
+	int freq;
+	u16 update_indic;
+
+
+	if (p2p->cfg->sd_request == NULL)
+		return;
+
+	if (rx_freq > 0)
+		freq = rx_freq;
+	else
+		freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+	if (freq < 0)
+		return;
+
+	if (len < 1 + 2)
+		return;
+
+	dialog_token = *pos++;
+	p2p_dbg(p2p, "GAS Initial Request from " MACSTR
+		" (dialog token %u, freq %d)",
+		MAC2STR(sa), dialog_token, rx_freq);
+
+	if (*pos != WLAN_EID_ADV_PROTO) {
+		p2p_dbg(p2p, "Unexpected IE in GAS Initial Request: %u", *pos);
+		return;
+	}
+	pos++;
+
+	slen = *pos++;
+	next = pos + slen;
+	if (next > end || slen < 2) {
+		p2p_dbg(p2p, "Invalid IE in GAS Initial Request");
+		return;
+	}
+	pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+		p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u",
+			*pos);
+		return;
+	}
+
+	pos = next;
+	/* Query Request */
+	if (pos + 2 > end)
+		return;
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	if (pos + slen > end)
+		return;
+	end = pos + slen;
+
+	/* ANQP Query Request */
+	if (pos + 4 > end)
+		return;
+	if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
+		p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
+		return;
+	}
+	pos += 2;
+
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	if (pos + slen > end || slen < 3 + 1) {
+		p2p_dbg(p2p, "Invalid ANQP Query Request length");
+		return;
+	}
+
+	if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+		p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+			WPA_GET_BE32(pos));
+		return;
+	}
+	pos += 4;
+
+	if (pos + 2 > end)
+		return;
+	update_indic = WPA_GET_LE16(pos);
+	p2p_dbg(p2p, "Service Update Indicator: %u", update_indic);
+	pos += 2;
+
+	p2p->cfg->sd_request(p2p->cfg->cb_ctx, freq, sa, dialog_token,
+			     update_indic, pos, end - pos);
+	/* the response will be indicated with a call to p2p_sd_response() */
+}
+
+
+void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
+		     u8 dialog_token, const struct wpabuf *resp_tlvs)
+{
+	struct wpabuf *resp;
+
+	/* TODO: fix the length limit to match with the maximum frame length */
+	if (wpabuf_len(resp_tlvs) > 1400) {
+		p2p_dbg(p2p, "SD response long enough to require fragmentation");
+		if (p2p->sd_resp) {
+			/*
+			 * TODO: Could consider storing the fragmented response
+			 * separately for each peer to avoid having to drop old
+			 * one if there is more than one pending SD query.
+			 * Though, that would eat more memory, so there are
+			 * also benefits to just using a single buffer.
+			 */
+			p2p_dbg(p2p, "Drop previous SD response");
+			wpabuf_free(p2p->sd_resp);
+		}
+		p2p->sd_resp = wpabuf_dup(resp_tlvs);
+		if (p2p->sd_resp == NULL) {
+			p2p_err(p2p, "Failed to allocate SD response fragmentation area");
+			return;
+		}
+		os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN);
+		p2p->sd_resp_dialog_token = dialog_token;
+		p2p->sd_resp_pos = 0;
+		p2p->sd_frag_id = 0;
+		resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS,
+					     1, p2p->srv_update_indic, NULL);
+	} else {
+		p2p_dbg(p2p, "SD response fits in initial response");
+		resp = p2p_build_sd_response(dialog_token,
+					     WLAN_STATUS_SUCCESS, 0,
+					     p2p->srv_update_indic, resp_tlvs);
+	}
+	if (resp == NULL)
+		return;
+
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr,
+			    p2p->cfg->dev_addr,
+			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+		p2p_dbg(p2p, "Failed to send Action frame");
+
+	wpabuf_free(resp);
+}
+
+
+void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq)
+{
+	const u8 *pos = data;
+	const u8 *end = data + len;
+	const u8 *next;
+	u8 dialog_token;
+	u16 status_code;
+	u16 comeback_delay;
+	u16 slen;
+	u16 update_indic;
+
+	if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
+	    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));
+		return;
+	}
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	p2p_clear_timeout(p2p);
+
+	p2p_dbg(p2p, "Received GAS Initial Response from " MACSTR " (len=%d)",
+		MAC2STR(sa), (int) len);
+
+	if (len < 5 + 2) {
+		p2p_dbg(p2p, "Too short GAS Initial Response frame");
+		return;
+	}
+
+	dialog_token = *pos++;
+	/* TODO: check dialog_token match */
+	status_code = WPA_GET_LE16(pos);
+	pos += 2;
+	comeback_delay = WPA_GET_LE16(pos);
+	pos += 2;
+	p2p_dbg(p2p, "dialog_token=%u status_code=%u comeback_delay=%u",
+		dialog_token, status_code, comeback_delay);
+	if (status_code) {
+		p2p_dbg(p2p, "Service Discovery failed: status code %u",
+			status_code);
+		return;
+	}
+
+	if (*pos != WLAN_EID_ADV_PROTO) {
+		p2p_dbg(p2p, "Unexpected IE in GAS Initial Response: %u", *pos);
+		return;
+	}
+	pos++;
+
+	slen = *pos++;
+	next = pos + slen;
+	if (next > end || slen < 2) {
+		p2p_dbg(p2p, "Invalid IE in GAS Initial Response");
+		return;
+	}
+	pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+		p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u",
+			*pos);
+		return;
+	}
+
+	pos = next;
+	/* Query Response */
+	if (pos + 2 > end) {
+		p2p_dbg(p2p, "Too short Query Response");
+		return;
+	}
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	p2p_dbg(p2p, "Query Response Length: %d", slen);
+	if (pos + slen > end) {
+		p2p_dbg(p2p, "Not enough Query Response data");
+		return;
+	}
+	end = pos + slen;
+
+	if (comeback_delay) {
+		p2p_dbg(p2p, "Fragmented response - request fragments");
+		if (p2p->sd_rx_resp) {
+			p2p_dbg(p2p, "Drop old SD reassembly buffer");
+			wpabuf_free(p2p->sd_rx_resp);
+			p2p->sd_rx_resp = NULL;
+		}
+		p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq);
+		return;
+	}
+
+	/* ANQP Query Response */
+	if (pos + 4 > end)
+		return;
+	if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
+		p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
+		return;
+	}
+	pos += 2;
+
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	if (pos + slen > end || slen < 3 + 1) {
+		p2p_dbg(p2p, "Invalid ANQP Query Response length");
+		return;
+	}
+
+	if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+		p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+			WPA_GET_BE32(pos));
+		return;
+	}
+	pos += 4;
+
+	if (pos + 2 > end)
+		return;
+	update_indic = WPA_GET_LE16(pos);
+	p2p_dbg(p2p, "Service Update Indicator: %u", update_indic);
+	pos += 2;
+
+	p2p->sd_peer = NULL;
+
+	if (p2p->sd_query) {
+		if (!p2p->sd_query->for_all_peers) {
+			struct p2p_sd_query *q;
+			p2p_dbg(p2p, "Remove completed SD query %p",
+				p2p->sd_query);
+			q = p2p->sd_query;
+			p2p_unlink_sd_query(p2p, p2p->sd_query);
+			p2p_free_sd_query(q);
+		}
+		p2p->sd_query = NULL;
+	}
+
+	if (p2p->cfg->sd_response)
+		p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa, update_indic,
+				      pos, end - pos);
+	p2p_continue_find(p2p);
+}
+
+
+void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq)
+{
+	struct wpabuf *resp;
+	u8 dialog_token;
+	size_t frag_len;
+	int more = 0;
+
+	wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len);
+	if (len < 1)
+		return;
+	dialog_token = *data;
+	p2p_dbg(p2p, "Dialog Token: %u", dialog_token);
+	if (dialog_token != p2p->sd_resp_dialog_token) {
+		p2p_dbg(p2p, "No pending SD response fragment for dialog token %u",
+			dialog_token);
+		return;
+	}
+
+	if (p2p->sd_resp == NULL) {
+		p2p_dbg(p2p, "No pending SD response fragment available");
+		return;
+	}
+	if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) {
+		p2p_dbg(p2p, "No pending SD response fragment for " MACSTR,
+			MAC2STR(sa));
+		return;
+	}
+
+	frag_len = wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos;
+	if (frag_len > 1400) {
+		frag_len = 1400;
+		more = 1;
+	}
+	resp = p2p_build_gas_comeback_resp(dialog_token, WLAN_STATUS_SUCCESS,
+					   p2p->srv_update_indic,
+					   wpabuf_head_u8(p2p->sd_resp) +
+					   p2p->sd_resp_pos, frag_len,
+					   p2p->sd_frag_id, more,
+					   wpabuf_len(p2p->sd_resp));
+	if (resp == NULL)
+		return;
+	p2p_dbg(p2p, "Send GAS Comeback Response (frag_id %d more=%d frag_len=%d)",
+		p2p->sd_frag_id, more, (int) frag_len);
+	p2p->sd_frag_id++;
+	p2p->sd_resp_pos += frag_len;
+
+	if (more) {
+		p2p_dbg(p2p, "%d more bytes remain to be sent",
+			(int) (wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos));
+	} else {
+		p2p_dbg(p2p, "All fragments of SD response sent");
+		wpabuf_free(p2p->sd_resp);
+		p2p->sd_resp = NULL;
+	}
+
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr,
+			    p2p->cfg->dev_addr,
+			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+		p2p_dbg(p2p, "Failed to send Action frame");
+
+	wpabuf_free(resp);
+}
+
+
+void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa,
+			      const u8 *data, size_t len, int rx_freq)
+{
+	const u8 *pos = data;
+	const u8 *end = data + len;
+	const u8 *next;
+	u8 dialog_token;
+	u16 status_code;
+	u8 frag_id;
+	u8 more_frags;
+	u16 comeback_delay;
+	u16 slen;
+
+	wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len);
+
+	if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
+	    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));
+		return;
+	}
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	p2p_clear_timeout(p2p);
+
+	p2p_dbg(p2p, "Received GAS Comeback Response from " MACSTR " (len=%d)",
+		MAC2STR(sa), (int) len);
+
+	if (len < 6 + 2) {
+		p2p_dbg(p2p, "Too short GAS Comeback Response frame");
+		return;
+	}
+
+	dialog_token = *pos++;
+	/* TODO: check dialog_token match */
+	status_code = WPA_GET_LE16(pos);
+	pos += 2;
+	frag_id = *pos & 0x7f;
+	more_frags = (*pos & 0x80) >> 7;
+	pos++;
+	comeback_delay = WPA_GET_LE16(pos);
+	pos += 2;
+	p2p_dbg(p2p, "dialog_token=%u status_code=%u frag_id=%d more_frags=%d "
+		"comeback_delay=%u",
+		dialog_token, status_code, frag_id, more_frags,
+		comeback_delay);
+	/* TODO: check frag_id match */
+	if (status_code) {
+		p2p_dbg(p2p, "Service Discovery failed: status code %u",
+			status_code);
+		return;
+	}
+
+	if (*pos != WLAN_EID_ADV_PROTO) {
+		p2p_dbg(p2p, "Unexpected IE in GAS Comeback Response: %u",
+			*pos);
+		return;
+	}
+	pos++;
+
+	slen = *pos++;
+	next = pos + slen;
+	if (next > end || slen < 2) {
+		p2p_dbg(p2p, "Invalid IE in GAS Comeback Response");
+		return;
+	}
+	pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+		p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u",
+			*pos);
+		return;
+	}
+
+	pos = next;
+	/* Query Response */
+	if (pos + 2 > end) {
+		p2p_dbg(p2p, "Too short Query Response");
+		return;
+	}
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	p2p_dbg(p2p, "Query Response Length: %d", slen);
+	if (pos + slen > end) {
+		p2p_dbg(p2p, "Not enough Query Response data");
+		return;
+	}
+	if (slen == 0) {
+		p2p_dbg(p2p, "No Query Response data");
+		return;
+	}
+	end = pos + slen;
+
+	if (p2p->sd_rx_resp) {
+		 /*
+		  * ANQP header is only included in the first fragment; rest of
+		  * the fragments start with continue TLVs.
+		  */
+		goto skip_nqp_header;
+	}
+
+	/* ANQP Query Response */
+	if (pos + 4 > end)
+		return;
+	if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
+		p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
+		return;
+	}
+	pos += 2;
+
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	p2p_dbg(p2p, "ANQP Query Response length: %u", slen);
+	if (slen < 3 + 1) {
+		p2p_dbg(p2p, "Invalid ANQP Query Response length");
+		return;
+	}
+	if (pos + 4 > end)
+		return;
+
+	if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+		p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+			WPA_GET_BE32(pos));
+		return;
+	}
+	pos += 4;
+
+	if (pos + 2 > end)
+		return;
+	p2p->sd_rx_update_indic = WPA_GET_LE16(pos);
+	p2p_dbg(p2p, "Service Update Indicator: %u", p2p->sd_rx_update_indic);
+	pos += 2;
+
+skip_nqp_header:
+	if (wpabuf_resize(&p2p->sd_rx_resp, end - pos) < 0)
+		return;
+	wpabuf_put_data(p2p->sd_rx_resp, pos, end - pos);
+	p2p_dbg(p2p, "Current SD reassembly buffer length: %u",
+		(unsigned int) wpabuf_len(p2p->sd_rx_resp));
+
+	if (more_frags) {
+		p2p_dbg(p2p, "More fragments remains");
+		/* TODO: what would be a good size limit? */
+		if (wpabuf_len(p2p->sd_rx_resp) > 64000) {
+			wpabuf_free(p2p->sd_rx_resp);
+			p2p->sd_rx_resp = NULL;
+			p2p_dbg(p2p, "Too long SD response - drop it");
+			return;
+		}
+		p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq);
+		return;
+	}
+
+	p2p->sd_peer = NULL;
+
+	if (p2p->sd_query) {
+		if (!p2p->sd_query->for_all_peers) {
+			struct p2p_sd_query *q;
+			p2p_dbg(p2p, "Remove completed SD query %p",
+				p2p->sd_query);
+			q = p2p->sd_query;
+			p2p_unlink_sd_query(p2p, p2p->sd_query);
+			p2p_free_sd_query(q);
+		}
+		p2p->sd_query = NULL;
+	}
+
+	if (p2p->cfg->sd_response)
+		p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa,
+				      p2p->sd_rx_update_indic,
+				      wpabuf_head(p2p->sd_rx_resp),
+				      wpabuf_len(p2p->sd_rx_resp));
+	wpabuf_free(p2p->sd_rx_resp);
+	p2p->sd_rx_resp = NULL;
+
+	p2p_continue_find(p2p);
+}
+
+
+void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
+		      const struct wpabuf *tlvs)
+{
+	struct p2p_sd_query *q;
+
+	q = os_zalloc(sizeof(*q));
+	if (q == NULL)
+		return NULL;
+
+	if (dst)
+		os_memcpy(q->peer, dst, ETH_ALEN);
+	else
+		q->for_all_peers = 1;
+
+	q->tlvs = wpabuf_dup(tlvs);
+	if (q->tlvs == NULL) {
+		p2p_free_sd_query(q);
+		return NULL;
+	}
+
+	q->next = p2p->sd_queries;
+	p2p->sd_queries = q;
+	p2p_dbg(p2p, "Added SD Query %p", q);
+
+	if (dst == NULL) {
+		struct p2p_device *dev;
+
+		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;
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst,
+			  const struct wpabuf *tlvs)
+{
+	struct p2p_sd_query *q;
+	q = p2p_sd_request(p2p, dst, tlvs);
+	if (q)
+		q->wsd = 1;
+	return q;
+}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+void p2p_sd_service_update(struct p2p_data *p2p)
+{
+	p2p->srv_update_indic++;
+}
+
+
+int p2p_sd_cancel_request(struct p2p_data *p2p, void *req)
+{
+	if (p2p_unlink_sd_query(p2p, req)) {
+		p2p_dbg(p2p, "Cancel pending SD query %p", req);
+		p2p_free_sd_query(req);
+		return 0;
+	}
+	return -1;
+}
diff --git a/hostap/src/p2p/p2p_utils.c b/hostap/src/p2p/p2p_utils.c
new file mode 100644
index 0000000..2e2aa8a
--- /dev/null
+++ b/hostap/src/p2p/p2p_utils.c
@@ -0,0 +1,485 @@
+/*
+ * P2P - generic helper functions
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_common.h"
+#include "p2p_i.h"
+
+
+/**
+ * p2p_random - Generate random string for SSID and passphrase
+ * @buf: Buffer for returning the result
+ * @len: Number of octets to write to the buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function generates a random string using the following character set:
+ * 'A'-'Z', 'a'-'z', '0'-'9'.
+ */
+int p2p_random(char *buf, size_t len)
+{
+	u8 val;
+	size_t i;
+	u8 letters = 'Z' - 'A' + 1;
+	u8 numbers = 10;
+
+	if (os_get_random((unsigned char *) buf, len))
+		return -1;
+	/* Character set: 'A'-'Z', 'a'-'z', '0'-'9' */
+	for (i = 0; i < len; i++) {
+		val = buf[i];
+		val %= 2 * letters + numbers;
+		if (val < letters)
+			buf[i] = 'A' + val;
+		else if (val < 2 * letters)
+			buf[i] = 'a' + (val - letters);
+		else
+			buf[i] = '0' + (val - 2 * letters);
+	}
+
+	return 0;
+}
+
+
+/**
+ * p2p_channel_to_freq - Convert channel info to frequency
+ * @op_class: Operating class
+ * @channel: Channel number
+ * Returns: Frequency in MHz or -1 if the specified channel is unknown
+ */
+int p2p_channel_to_freq(int op_class, int channel)
+{
+	return ieee80211_chan_to_freq(NULL, op_class, channel);
+}
+
+
+/**
+ * p2p_freq_to_channel - Convert frequency into channel info
+ * @op_class: Buffer for returning operating class
+ * @channel: Buffer for returning channel number
+ * Returns: 0 on success, -1 if the specified frequency is unknown
+ */
+int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel)
+{
+	if (ieee80211_freq_to_channel_ext(freq, 0, 0, op_class, channel) ==
+	    NUM_HOSTAPD_MODES)
+		return -1;
+
+	return 0;
+}
+
+
+static void p2p_reg_class_intersect(const struct p2p_reg_class *a,
+				    const struct p2p_reg_class *b,
+				    struct p2p_reg_class *res)
+{
+	size_t i, j;
+
+	res->reg_class = a->reg_class;
+
+	for (i = 0; i < a->channels; i++) {
+		for (j = 0; j < b->channels; j++) {
+			if (a->channel[i] != b->channel[j])
+				continue;
+			res->channel[res->channels] = a->channel[i];
+			res->channels++;
+			if (res->channels == P2P_MAX_REG_CLASS_CHANNELS)
+				return;
+		}
+	}
+}
+
+
+/**
+ * p2p_channels_intersect - Intersection of supported channel lists
+ * @a: First set of supported channels
+ * @b: Second set of supported channels
+ * @res: Data structure for returning the intersection of support channels
+ *
+ * This function can be used to find a common set of supported channels. Both
+ * input channels sets are assumed to use the same country code. If different
+ * country codes are used, the regulatory class numbers may not be matched
+ * correctly and results are undefined.
+ */
+void p2p_channels_intersect(const struct p2p_channels *a,
+			    const struct p2p_channels *b,
+			    struct p2p_channels *res)
+{
+	size_t i, j;
+
+	os_memset(res, 0, sizeof(*res));
+
+	for (i = 0; i < a->reg_classes; i++) {
+		const struct p2p_reg_class *a_reg = &a->reg_class[i];
+		for (j = 0; j < b->reg_classes; j++) {
+			const struct p2p_reg_class *b_reg = &b->reg_class[j];
+			if (a_reg->reg_class != b_reg->reg_class)
+				continue;
+			p2p_reg_class_intersect(
+				a_reg, b_reg,
+				&res->reg_class[res->reg_classes]);
+			if (res->reg_class[res->reg_classes].channels) {
+				res->reg_classes++;
+				if (res->reg_classes == P2P_MAX_REG_CLASSES)
+					return;
+			}
+		}
+	}
+}
+
+
+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_inplace - Inplace union of channel lists
+ * @res: Input data and place for returning union of the channel sets
+ * @b: Second set of channels
+ */
+void p2p_channels_union_inplace(struct p2p_channels *res,
+				const struct p2p_channels *b)
+{
+	size_t i, j;
+
+	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));
+		}
+	}
+}
+
+
+/**
+ * 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)
+{
+	os_memcpy(res, a, sizeof(*res));
+	p2p_channels_union_inplace(res, b);
+}
+
+
+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
+ * @reg_class: Regulatory class of the channel to search
+ * @channel: Channel number of the channel to search
+ * Returns: 1 if channel was found or 0 if not
+ */
+int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
+			  u8 channel)
+{
+	size_t i, j;
+	for (i = 0; i < channels->reg_classes; i++) {
+		const struct p2p_reg_class *reg = &channels->reg_class[i];
+		if (reg->reg_class != reg_class)
+			continue;
+		for (j = 0; j < reg->channels; j++) {
+			if (reg->channel[j] == channel)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+
+int p2p_channels_includes_freq(const struct p2p_channels *channels,
+			       unsigned int freq)
+{
+	size_t i, j;
+	for (i = 0; i < channels->reg_classes; i++) {
+		const struct p2p_reg_class *reg = &channels->reg_class[i];
+		for (j = 0; j < reg->channels; j++) {
+			if (p2p_channel_to_freq(reg->reg_class,
+						reg->channel[j]) == (int) freq)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+
+int p2p_supported_freq(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);
+}
+
+
+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 (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(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 (os_snprintf_error(end - pos, ret))
+			break;
+		pos += ret;
+
+		for (j = 0; j < c->channels; j++) {
+			ret = os_snprintf(pos, end - pos, "%s%u",
+					  j == 0 ? "" : ",",
+					  c->channel[j]);
+			if (os_snprintf_error(end - pos, ret))
+				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;
+	if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+		r = 0;
+	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[4];
+	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;
+
+	/* Try to find available social channels from 60 GHz */
+	if (p2p_channels_includes(chans, 180, 2))
+		chan[num_channels++] = 2;
+
+	if (num_channels == 0)
+		return -1;
+
+	*op_channel = p2p_channel_pick_random(chan, num_channels);
+	if (*op_channel == 2)
+		*op_class = 180;
+	else
+		*op_class = 81;
+
+	return 0;
+}
+
+
+int p2p_channels_to_freqs(const struct p2p_channels *channels, int *freq_list,
+			  unsigned int max_len)
+{
+	unsigned int i, idx;
+
+	if (!channels || max_len == 0)
+		return 0;
+
+	for (i = 0, idx = 0; i < channels->reg_classes; i++) {
+		const struct p2p_reg_class *c = &channels->reg_class[i];
+		unsigned int j;
+
+		if (idx + 1 == max_len)
+			break;
+		for (j = 0; j < c->channels; j++) {
+			int freq;
+			unsigned int k;
+
+			if (idx + 1 == max_len)
+				break;
+			freq = p2p_channel_to_freq(c->reg_class,
+						   c->channel[j]);
+			if (freq < 0)
+				continue;
+
+			for (k = 0; k < idx; k++) {
+				if (freq_list[k] == freq)
+					break;
+			}
+
+			if (k < idx)
+				continue;
+			freq_list[idx++] = freq;
+		}
+	}
+
+	freq_list[idx] = 0;
+
+	return idx;
+}
diff --git a/hostap/src/pae/Makefile b/hostap/src/pae/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/hostap/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/hostap/src/pae/ieee802_1x_cp.c b/hostap/src/pae/ieee802_1x_cp.c
new file mode 100644
index 0000000..cf43c59
--- /dev/null
+++ b/hostap/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/hostap/src/pae/ieee802_1x_cp.h b/hostap/src/pae/ieee802_1x_cp.h
new file mode 100644
index 0000000..773c930
--- /dev/null
+++ b/hostap/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/hostap/src/pae/ieee802_1x_kay.c b/hostap/src/pae/ieee802_1x_kay.c
new file mode 100644
index 0000000..ef74430
--- /dev/null
+++ b/hostap/src/pae/ieee802_1x_kay.c
@@ -0,0 +1,3541 @@
+/*
+ * 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) {
+		if (os_get_random(participant->mi, sizeof(participant->mi)) < 0)
+			return NULL;
+		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) {
+				if (os_get_random(participant->mi,
+						  sizeof(participant->mi)) < 0)
+					wpa_printf(MSG_DEBUG,
+						   "KaY: Could not update 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) {
+				if (os_get_random(participant->mi,
+						  sizeof(participant->mi)) < 0)
+					wpa_printf(MSG_DEBUG,
+						   "KaY: Could not update 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, 16,
+		     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, 16, 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;
+	if (os_get_random(context + ctx_offset, conf->key_len) < 0) {
+		os_free(context);
+		os_free(conf->key);
+		os_free(conf);
+		return -1;
+	}
+	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 = Disabled;
+		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 = Strict;
+		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;
+
+	if (os_get_random(participant->mi, sizeof(participant->mi)) < 0)
+		goto fail;
+	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_cp_control_protect_frames(kay, kay->macsec_protect);
+	secy_cp_control_replay(kay, kay->macsec_replay_protect,
+			       kay->macsec_replay_window);
+	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/hostap/src/pae/ieee802_1x_kay.h b/hostap/src/pae/ieee802_1x_kay.h
new file mode 100644
index 0000000..064417e
--- /dev/null
+++ b/hostap/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/hostap/src/pae/ieee802_1x_kay_i.h b/hostap/src/pae/ieee802_1x_kay_i.h
new file mode 100644
index 0000000..bdad3a5
--- /dev/null
+++ b/hostap/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/hostap/src/pae/ieee802_1x_key.c b/hostap/src/pae/ieee802_1x_key.c
new file mode 100644
index 0000000..9a8d923
--- /dev/null
+++ b/hostap/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/hostap/src/pae/ieee802_1x_key.h b/hostap/src/pae/ieee802_1x_key.h
new file mode 100644
index 0000000..ea318ea
--- /dev/null
+++ b/hostap/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/hostap/src/pae/ieee802_1x_secy_ops.c b/hostap/src/pae/ieee802_1x_secy_ops.c
new file mode 100644
index 0000000..fbe05dc
--- /dev/null
+++ b/hostap/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/hostap/src/pae/ieee802_1x_secy_ops.h b/hostap/src/pae/ieee802_1x_secy_ops.h
new file mode 100644
index 0000000..295b823
--- /dev/null
+++ b/hostap/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/hostap/src/radius/Makefile b/hostap/src/radius/Makefile
new file mode 100644
index 0000000..3ad4751
--- /dev/null
+++ b/hostap/src/radius/Makefile
@@ -0,0 +1,23 @@
+all: libradius.a
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libradius.a
+
+install:
+	@echo Nothing to be made.
+
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_IPV6
+
+LIB_OBJS= \
+	radius.o \
+	radius_client.o \
+	radius_das.o \
+	radius_server.o
+
+libradius.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/radius/radius.c b/hostap/src/radius/radius.c
new file mode 100644
index 0000000..1ebfd11
--- /dev/null
+++ b/hostap/src/radius/radius.c
@@ -0,0 +1,1671 @@
+/*
+ * RADIUS message processing
+ * 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.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/wpabuf.h"
+#include "crypto/md5.h"
+#include "crypto/crypto.h"
+#include "radius.h"
+
+
+/**
+ * struct radius_msg - RADIUS message structure for new and parsed messages
+ */
+struct radius_msg {
+	/**
+	 * buf - Allocated buffer for RADIUS message
+	 */
+	struct wpabuf *buf;
+
+	/**
+	 * hdr - Pointer to the RADIUS header in buf
+	 */
+	struct radius_hdr *hdr;
+
+	/**
+	 * attr_pos - Array of indexes to attributes
+	 *
+	 * The values are number of bytes from buf to the beginning of
+	 * struct radius_attr_hdr.
+	 */
+	size_t *attr_pos;
+
+	/**
+	 * attr_size - Total size of the attribute pointer array
+	 */
+	size_t attr_size;
+
+	/**
+	 * attr_used - Total number of attributes in the array
+	 */
+	size_t attr_used;
+};
+
+
+struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg)
+{
+	return msg->hdr;
+}
+
+
+struct wpabuf * radius_msg_get_buf(struct radius_msg *msg)
+{
+	return msg->buf;
+}
+
+
+static struct radius_attr_hdr *
+radius_get_attr_hdr(struct radius_msg *msg, int idx)
+{
+	return (struct radius_attr_hdr *)
+		(wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]);
+}
+
+
+static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
+{
+	msg->hdr->code = code;
+	msg->hdr->identifier = identifier;
+}
+
+
+static int radius_msg_initialize(struct radius_msg *msg)
+{
+	msg->attr_pos = os_calloc(RADIUS_DEFAULT_ATTR_COUNT,
+				  sizeof(*msg->attr_pos));
+	if (msg->attr_pos == NULL)
+		return -1;
+
+	msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT;
+	msg->attr_used = 0;
+
+	return 0;
+}
+
+
+/**
+ * radius_msg_new - Create a new RADIUS message
+ * @code: Code for RADIUS header
+ * @identifier: Identifier for RADIUS header
+ * Returns: Context for RADIUS message or %NULL on failure
+ *
+ * The caller is responsible for freeing the returned data with
+ * radius_msg_free().
+ */
+struct radius_msg * radius_msg_new(u8 code, u8 identifier)
+{
+	struct radius_msg *msg;
+
+	msg = os_zalloc(sizeof(*msg));
+	if (msg == NULL)
+		return NULL;
+
+	msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE);
+	if (msg->buf == NULL || radius_msg_initialize(msg)) {
+		radius_msg_free(msg);
+		return NULL;
+	}
+	msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr));
+
+	radius_msg_set_hdr(msg, code, identifier);
+
+	return msg;
+}
+
+
+/**
+ * radius_msg_free - Free a RADIUS message
+ * @msg: RADIUS message from radius_msg_new() or radius_msg_parse()
+ */
+void radius_msg_free(struct radius_msg *msg)
+{
+	if (msg == NULL)
+		return;
+
+	wpabuf_free(msg->buf);
+	os_free(msg->attr_pos);
+	os_free(msg);
+}
+
+
+static const char *radius_code_string(u8 code)
+{
+	switch (code) {
+	case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request";
+	case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept";
+	case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject";
+	case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request";
+	case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response";
+	case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge";
+	case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
+	case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
+	case RADIUS_CODE_RESERVED: return "Reserved";
+	case RADIUS_CODE_DISCONNECT_REQUEST: return "Disconnect-Request";
+	case RADIUS_CODE_DISCONNECT_ACK: return "Disconnect-ACK";
+	case RADIUS_CODE_DISCONNECT_NAK: return "Disconnect-NAK";
+	case RADIUS_CODE_COA_REQUEST: return "CoA-Request";
+	case RADIUS_CODE_COA_ACK: return "CoA-ACK";
+	case RADIUS_CODE_COA_NAK: return "CoA-NAK";
+	default: return "?Unknown?";
+	}
+}
+
+
+struct radius_attr_type {
+	u8 type;
+	char *name;
+	enum {
+		RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
+		RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6
+	} data_type;
+};
+
+static const struct radius_attr_type radius_attrs[] =
+{
+	{ RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
+	{ RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id",
+	  RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id",
+	  RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
+	  RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", 
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type",
+	  RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_TUNNEL_PASSWORD, "Tunnel-Password",
+	  RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
+	  RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id",
+	  RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
+	  RADIUS_ATTR_INT32 },
+	{ 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_EAP_KEY_NAME, "EAP-Key-Name", RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_OPERATOR_NAME, "Operator-Name", RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_LOCATION_INFO, "Location-Information",
+	  RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_LOCATION_DATA, "Location-Data", RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES,
+	  "Basic-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES,
+	  "Extended-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_LOCATION_CAPABLE, "Location-Capable", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_REQUESTED_LOCATION_INFO, "Requested-Location-Info",
+	  RADIUS_ATTR_INT32 },
+	{ 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 ARRAY_SIZE(radius_attrs)
+
+
+static const struct radius_attr_type *radius_get_attr_type(u8 type)
+{
+	size_t i;
+
+	for (i = 0; i < RADIUS_ATTRS; i++) {
+		if (type == radius_attrs[i].type)
+			return &radius_attrs[i];
+	}
+
+	return NULL;
+}
+
+
+static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
+{
+	const struct radius_attr_type *attr;
+	int len;
+	unsigned char *pos;
+	char buf[1000];
+
+	attr = radius_get_attr_type(hdr->type);
+
+	wpa_printf(MSG_INFO, "   Attribute %d (%s) length=%d",
+		   hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
+
+	if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr))
+		return;
+
+	len = hdr->length - sizeof(struct radius_attr_hdr);
+	pos = (unsigned char *) (hdr + 1);
+
+	switch (attr->data_type) {
+	case RADIUS_ATTR_TEXT:
+		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);
+			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) {
+			const char *atxt;
+			struct in6_addr *addr = (struct in6_addr *) pos;
+			atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf));
+			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:
+		wpa_snprintf_hex(buf, sizeof(buf), pos, len);
+		wpa_printf(MSG_INFO, "      Value: %s", buf);
+		break;
+
+	case RADIUS_ATTR_INT32:
+		if (len == 4)
+			wpa_printf(MSG_INFO, "      Value: %u",
+				   WPA_GET_BE32(pos));
+		else
+			wpa_printf(MSG_INFO, "      Invalid INT32 length %d",
+				   len);
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+void radius_msg_dump(struct radius_msg *msg)
+{
+	size_t i;
+
+	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);
+		radius_msg_dump_attr(attr);
+	}
+}
+
+
+int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
+		      size_t secret_len)
+{
+	if (secret) {
+		u8 auth[MD5_MAC_LEN];
+		struct radius_attr_hdr *attr;
+
+		os_memset(auth, 0, MD5_MAC_LEN);
+		attr = radius_msg_add_attr(msg,
+					   RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+					   auth, MD5_MAC_LEN);
+		if (attr == NULL) {
+			wpa_printf(MSG_WARNING, "RADIUS: Could not add "
+				   "Message-Authenticator");
+			return -1;
+		}
+		msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
+		hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+			 wpabuf_len(msg->buf), (u8 *) (attr + 1));
+	} else
+		msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
+
+	if (wpabuf_len(msg->buf) > 0xffff) {
+		wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
+			   (unsigned long) wpabuf_len(msg->buf));
+		return -1;
+	}
+	return 0;
+}
+
+
+int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
+			  size_t secret_len, const u8 *req_authenticator)
+{
+	u8 auth[MD5_MAC_LEN];
+	struct radius_attr_hdr *attr;
+	const u8 *addr[4];
+	size_t len[4];
+
+	os_memset(auth, 0, MD5_MAC_LEN);
+	attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+				   auth, MD5_MAC_LEN);
+	if (attr == NULL) {
+		wpa_printf(MSG_ERROR, "WARNING: Could not add Message-Authenticator");
+		return -1;
+	}
+	msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
+	os_memcpy(msg->hdr->authenticator, req_authenticator,
+		  sizeof(msg->hdr->authenticator));
+	hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+		 wpabuf_len(msg->buf), (u8 *) (attr + 1));
+
+	/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
+	addr[0] = (u8 *) msg->hdr;
+	len[0] = 1 + 1 + 2;
+	addr[1] = req_authenticator;
+	len[1] = MD5_MAC_LEN;
+	addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
+	len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
+	addr[3] = secret;
+	len[3] = secret_len;
+	md5_vector(4, addr, len, msg->hdr->authenticator);
+
+	if (wpabuf_len(msg->buf) > 0xffff) {
+		wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
+			   (unsigned long) wpabuf_len(msg->buf));
+		return -1;
+	}
+	return 0;
+}
+
+
+int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len,
+			       const struct radius_hdr *req_hdr)
+{
+	const u8 *addr[2];
+	size_t len[2];
+	u8 auth[MD5_MAC_LEN];
+	struct radius_attr_hdr *attr;
+
+	os_memset(auth, 0, MD5_MAC_LEN);
+	attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+				   auth, MD5_MAC_LEN);
+	if (attr == NULL) {
+		wpa_printf(MSG_WARNING, "Could not add Message-Authenticator");
+		return -1;
+	}
+
+	msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
+	os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16);
+	hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+		 wpabuf_len(msg->buf), (u8 *) (attr + 1));
+
+	/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
+	addr[0] = wpabuf_head_u8(msg->buf);
+	len[0] = wpabuf_len(msg->buf);
+	addr[1] = secret;
+	len[1] = secret_len;
+	if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0)
+		return -1;
+
+	if (wpabuf_len(msg->buf) > 0xffff) {
+		wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
+			   (unsigned long) wpabuf_len(msg->buf));
+		return -1;
+	}
+	return 0;
+}
+
+
+void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
+			    size_t secret_len)
+{
+	const u8 *addr[2];
+	size_t len[2];
+
+	msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
+	os_memset(msg->hdr->authenticator, 0, 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));
+	}
+}
+
+
+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)
+{
+	const u8 *addr[4];
+	size_t len[4];
+	u8 zero[MD5_MAC_LEN];
+	u8 hash[MD5_MAC_LEN];
+
+	os_memset(zero, 0, sizeof(zero));
+	addr[0] = (u8 *) msg->hdr;
+	len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
+	addr[1] = zero;
+	len[1] = MD5_MAC_LEN;
+	addr[2] = (u8 *) (msg->hdr + 1);
+	len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
+	addr[3] = secret;
+	len[3] = secret_len;
+	md5_vector(4, addr, len, hash);
+	return os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
+}
+
+
+int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
+			      size_t secret_len)
+{
+	const u8 *addr[4];
+	size_t len[4];
+	u8 zero[MD5_MAC_LEN];
+	u8 hash[MD5_MAC_LEN];
+	u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
+	u8 orig_authenticator[16];
+
+	struct radius_attr_hdr *attr = NULL, *tmp;
+	size_t i;
+
+	os_memset(zero, 0, sizeof(zero));
+	addr[0] = (u8 *) msg->hdr;
+	len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
+	addr[1] = zero;
+	len[1] = MD5_MAC_LEN;
+	addr[2] = (u8 *) (msg->hdr + 1);
+	len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
+	addr[3] = secret;
+	len[3] = secret_len;
+	md5_vector(4, addr, len, hash);
+	if (os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
+		return 1;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		tmp = radius_get_attr_hdr(msg, i);
+		if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
+			if (attr != NULL) {
+				wpa_printf(MSG_WARNING, "Multiple "
+					   "Message-Authenticator attributes "
+					   "in RADIUS message");
+				return 1;
+			}
+			attr = tmp;
+		}
+	}
+
+	if (attr == NULL) {
+		/* Message-Authenticator is MAY; not required */
+		return 0;
+	}
+
+	os_memcpy(orig, attr + 1, MD5_MAC_LEN);
+	os_memset(attr + 1, 0, MD5_MAC_LEN);
+	os_memcpy(orig_authenticator, msg->hdr->authenticator,
+		  sizeof(orig_authenticator));
+	os_memset(msg->hdr->authenticator, 0,
+		  sizeof(msg->hdr->authenticator));
+	hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+		 wpabuf_len(msg->buf), auth);
+	os_memcpy(attr + 1, orig, MD5_MAC_LEN);
+	os_memcpy(msg->hdr->authenticator, orig_authenticator,
+		  sizeof(orig_authenticator));
+
+	return os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0;
+}
+
+
+static int radius_msg_add_attr_to_array(struct radius_msg *msg,
+					struct radius_attr_hdr *attr)
+{
+	if (msg->attr_used >= msg->attr_size) {
+		size_t *nattr_pos;
+		int nlen = msg->attr_size * 2;
+
+		nattr_pos = os_realloc_array(msg->attr_pos, nlen,
+					     sizeof(*msg->attr_pos));
+		if (nattr_pos == NULL)
+			return -1;
+
+		msg->attr_pos = nattr_pos;
+		msg->attr_size = nlen;
+	}
+
+	msg->attr_pos[msg->attr_used++] =
+		(unsigned char *) attr - wpabuf_head_u8(msg->buf);
+
+	return 0;
+}
+
+
+struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
+					    const u8 *data, size_t data_len)
+{
+	size_t buf_needed;
+	struct radius_attr_hdr *attr;
+
+	if (data_len > RADIUS_MAX_ATTR_LEN) {
+		wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)",
+		       (unsigned long) data_len);
+		return NULL;
+	}
+
+	buf_needed = sizeof(*attr) + data_len;
+
+	if (wpabuf_tailroom(msg->buf) < buf_needed) {
+		/* allocate more space for message buffer */
+		if (wpabuf_resize(&msg->buf, buf_needed) < 0)
+			return NULL;
+		msg->hdr = wpabuf_mhead(msg->buf);
+	}
+
+	attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr));
+	attr->type = type;
+	attr->length = sizeof(*attr) + data_len;
+	wpabuf_put_data(msg->buf, data, data_len);
+
+	if (radius_msg_add_attr_to_array(msg, attr))
+		return NULL;
+
+	return attr;
+}
+
+
+/**
+ * radius_msg_parse - Parse a RADIUS message
+ * @data: RADIUS message to be parsed
+ * @len: Length of data buffer in octets
+ * Returns: Parsed RADIUS message or %NULL on failure
+ *
+ * This parses a RADIUS message and makes a copy of its data. The caller is
+ * responsible for freeing the returned data with radius_msg_free().
+ */
+struct radius_msg * radius_msg_parse(const u8 *data, size_t len)
+{
+	struct radius_msg *msg;
+	struct radius_hdr *hdr;
+	struct radius_attr_hdr *attr;
+	size_t msg_len;
+	unsigned char *pos, *end;
+
+	if (data == NULL || len < sizeof(*hdr))
+		return NULL;
+
+	hdr = (struct radius_hdr *) data;
+
+	msg_len = be_to_host16(hdr->length);
+	if (msg_len < sizeof(*hdr) || msg_len > len) {
+		wpa_printf(MSG_INFO, "RADIUS: Invalid message length");
+		return NULL;
+	}
+
+	if (msg_len < len) {
+		wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after "
+			   "RADIUS message", (unsigned long) len - msg_len);
+	}
+
+	msg = os_zalloc(sizeof(*msg));
+	if (msg == NULL)
+		return NULL;
+
+	msg->buf = wpabuf_alloc_copy(data, msg_len);
+	if (msg->buf == NULL || radius_msg_initialize(msg)) {
+		radius_msg_free(msg);
+		return NULL;
+	}
+	msg->hdr = wpabuf_mhead(msg->buf);
+
+	/* parse attributes */
+	pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr);
+	end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf);
+	while (pos < end) {
+		if ((size_t) (end - pos) < sizeof(*attr))
+			goto fail;
+
+		attr = (struct radius_attr_hdr *) pos;
+
+		if (pos + attr->length > end || attr->length < sizeof(*attr))
+			goto fail;
+
+		/* TODO: check that attr->length is suitable for attr->type */
+
+		if (radius_msg_add_attr_to_array(msg, attr))
+			goto fail;
+
+		pos += attr->length;
+	}
+
+	return msg;
+
+ fail:
+	radius_msg_free(msg);
+	return NULL;
+}
+
+
+int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len)
+{
+	const u8 *pos = data;
+	size_t left = data_len;
+
+	while (left > 0) {
+		int len;
+		if (left > RADIUS_MAX_ATTR_LEN)
+			len = RADIUS_MAX_ATTR_LEN;
+		else
+			len = left;
+
+		if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE,
+					 pos, len))
+			return 0;
+
+		pos += len;
+		left -= len;
+	}
+
+	return 1;
+}
+
+
+struct wpabuf * radius_msg_get_eap(struct radius_msg *msg)
+{
+	struct wpabuf *eap;
+	size_t len, i;
+	struct radius_attr_hdr *attr;
+
+	if (msg == NULL)
+		return NULL;
+
+	len = 0;
+	for (i = 0; i < msg->attr_used; i++) {
+		attr = radius_get_attr_hdr(msg, i);
+		if (attr->type == RADIUS_ATTR_EAP_MESSAGE &&
+		    attr->length > sizeof(struct radius_attr_hdr))
+			len += attr->length - sizeof(struct radius_attr_hdr);
+	}
+
+	if (len == 0)
+		return NULL;
+
+	eap = wpabuf_alloc(len);
+	if (eap == NULL)
+		return NULL;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		attr = radius_get_attr_hdr(msg, i);
+		if (attr->type == RADIUS_ATTR_EAP_MESSAGE &&
+		    attr->length > sizeof(struct radius_attr_hdr)) {
+			int flen = attr->length - sizeof(*attr);
+			wpabuf_put_data(eap, attr + 1, flen);
+		}
+	}
+
+	return eap;
+}
+
+
+int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len, const u8 *req_auth)
+{
+	u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
+	u8 orig_authenticator[16];
+	struct radius_attr_hdr *attr = NULL, *tmp;
+	size_t i;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		tmp = radius_get_attr_hdr(msg, i);
+		if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
+			if (attr != NULL) {
+				wpa_printf(MSG_INFO, "Multiple Message-Authenticator attributes in RADIUS message");
+				return 1;
+			}
+			attr = tmp;
+		}
+	}
+
+	if (attr == NULL) {
+		wpa_printf(MSG_INFO, "No Message-Authenticator attribute found");
+		return 1;
+	}
+
+	os_memcpy(orig, attr + 1, MD5_MAC_LEN);
+	os_memset(attr + 1, 0, MD5_MAC_LEN);
+	if (req_auth) {
+		os_memcpy(orig_authenticator, msg->hdr->authenticator,
+			  sizeof(orig_authenticator));
+		os_memcpy(msg->hdr->authenticator, req_auth,
+			  sizeof(msg->hdr->authenticator));
+	}
+	hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+		 wpabuf_len(msg->buf), auth);
+	os_memcpy(attr + 1, orig, MD5_MAC_LEN);
+	if (req_auth) {
+		os_memcpy(msg->hdr->authenticator, orig_authenticator,
+			  sizeof(orig_authenticator));
+	}
+
+	if (os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0) {
+		wpa_printf(MSG_INFO, "Invalid Message-Authenticator!");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
+		      size_t secret_len, struct radius_msg *sent_msg, int auth)
+{
+	const u8 *addr[4];
+	size_t len[4];
+	u8 hash[MD5_MAC_LEN];
+
+	if (sent_msg == NULL) {
+		wpa_printf(MSG_INFO, "No matching Access-Request message found");
+		return 1;
+	}
+
+	if (auth &&
+	    radius_msg_verify_msg_auth(msg, secret, secret_len,
+				       sent_msg->hdr->authenticator)) {
+		return 1;
+	}
+
+	/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
+	addr[0] = (u8 *) msg->hdr;
+	len[0] = 1 + 1 + 2;
+	addr[1] = sent_msg->hdr->authenticator;
+	len[1] = MD5_MAC_LEN;
+	addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
+	len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
+	addr[3] = secret;
+	len[3] = secret_len;
+	md5_vector(4, addr, len, hash);
+	if (os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
+		wpa_printf(MSG_INFO, "Response Authenticator invalid!");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
+			 u8 type)
+{
+	struct radius_attr_hdr *attr;
+	size_t i;
+	int count = 0;
+
+	for (i = 0; i < src->attr_used; i++) {
+		attr = radius_get_attr_hdr(src, i);
+		if (attr->type == type && attr->length >= sizeof(*attr)) {
+			if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
+						 attr->length - sizeof(*attr)))
+				return -1;
+			count++;
+		}
+	}
+
+	return count;
+}
+
+
+/* Create Request Authenticator. The value should be unique over the lifetime
+ * of the shared secret between authenticator and authentication server.
+ * Use one-way MD5 hash calculated from current timestamp and some data given
+ * by the caller. */
+void radius_msg_make_authenticator(struct radius_msg *msg,
+				   const u8 *data, size_t len)
+{
+	struct os_time tv;
+	long int l;
+	const u8 *addr[3];
+	size_t elen[3];
+
+	os_get_time(&tv);
+	l = os_random();
+	addr[0] = (u8 *) &tv;
+	elen[0] = sizeof(tv);
+	addr[1] = data;
+	elen[1] = len;
+	addr[2] = (u8 *) &l;
+	elen[2] = sizeof(l);
+	md5_vector(3, addr, elen, msg->hdr->authenticator);
+}
+
+
+/* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message.
+ * Returns the Attribute payload and sets alen to indicate the length of the
+ * payload if a vendor attribute with subtype is found, otherwise returns NULL.
+ * The returned payload is allocated with os_malloc() and caller must free it
+ * by calling os_free().
+ */
+static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
+				      u8 subtype, size_t *alen)
+{
+	u8 *data, *pos;
+	size_t i, len;
+
+	if (msg == NULL)
+		return NULL;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
+		size_t left;
+		u32 vendor_id;
+		struct radius_attr_vendor *vhdr;
+
+		if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC ||
+		    attr->length < sizeof(*attr))
+			continue;
+
+		left = attr->length - sizeof(*attr);
+		if (left < 4)
+			continue;
+
+		pos = (u8 *) (attr + 1);
+
+		os_memcpy(&vendor_id, pos, 4);
+		pos += 4;
+		left -= 4;
+
+		if (ntohl(vendor_id) != vendor)
+			continue;
+
+		while (left >= sizeof(*vhdr)) {
+			vhdr = (struct radius_attr_vendor *) pos;
+			if (vhdr->vendor_length > left ||
+			    vhdr->vendor_length < sizeof(*vhdr)) {
+				break;
+			}
+			if (vhdr->vendor_type != subtype) {
+				pos += vhdr->vendor_length;
+				left -= vhdr->vendor_length;
+				continue;
+			}
+
+			len = vhdr->vendor_length - sizeof(*vhdr);
+			data = os_malloc(len);
+			if (data == NULL)
+				return NULL;
+			os_memcpy(data, pos + sizeof(*vhdr), len);
+			if (alen)
+				*alen = len;
+			return data;
+		}
+	}
+
+	return NULL;
+}
+
+
+static u8 * decrypt_ms_key(const u8 *key, size_t len,
+			   const u8 *req_authenticator,
+			   const u8 *secret, size_t secret_len, size_t *reslen)
+{
+	u8 *plain, *ppos, *res;
+	const u8 *pos;
+	size_t left, plen;
+	u8 hash[MD5_MAC_LEN];
+	int i, first = 1;
+	const u8 *addr[3];
+	size_t elen[3];
+
+	/* key: 16-bit salt followed by encrypted key info */
+
+	if (len < 2 + 16) {
+		wpa_printf(MSG_DEBUG, "RADIUS: %s: Len is too small: %d",
+			   __func__, (int) len);
+		return NULL;
+	}
+
+	pos = key + 2;
+	left = len - 2;
+	if (left % 16) {
+		wpa_printf(MSG_INFO, "RADIUS: Invalid ms key len %lu",
+			   (unsigned long) left);
+		return NULL;
+	}
+
+	plen = left;
+	ppos = plain = os_malloc(plen);
+	if (plain == NULL)
+		return NULL;
+	plain[0] = 0;
+
+	while (left > 0) {
+		/* b(1) = MD5(Secret + Request-Authenticator + Salt)
+		 * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
+
+		addr[0] = secret;
+		elen[0] = secret_len;
+		if (first) {
+			addr[1] = req_authenticator;
+			elen[1] = MD5_MAC_LEN;
+			addr[2] = key;
+			elen[2] = 2; /* Salt */
+		} else {
+			addr[1] = pos - MD5_MAC_LEN;
+			elen[1] = MD5_MAC_LEN;
+		}
+		md5_vector(first ? 3 : 2, addr, elen, hash);
+		first = 0;
+
+		for (i = 0; i < MD5_MAC_LEN; i++)
+			*ppos++ = *pos++ ^ hash[i];
+		left -= MD5_MAC_LEN;
+	}
+
+	if (plain[0] == 0 || plain[0] > plen - 1) {
+		wpa_printf(MSG_INFO, "RADIUS: Failed to decrypt MPPE key");
+		os_free(plain);
+		return NULL;
+	}
+
+	res = os_malloc(plain[0]);
+	if (res == NULL) {
+		os_free(plain);
+		return NULL;
+	}
+	os_memcpy(res, plain + 1, plain[0]);
+	if (reslen)
+		*reslen = plain[0];
+	os_free(plain);
+	return res;
+}
+
+
+static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
+			   const u8 *req_authenticator,
+			   const u8 *secret, size_t secret_len,
+			   u8 *ebuf, size_t *elen)
+{
+	int i, len, first = 1;
+	u8 hash[MD5_MAC_LEN], saltbuf[2], *pos;
+	const u8 *addr[3];
+	size_t _len[3];
+
+	WPA_PUT_BE16(saltbuf, salt);
+
+	len = 1 + key_len;
+	if (len & 0x0f) {
+		len = (len & 0xf0) + 16;
+	}
+	os_memset(ebuf, 0, len);
+	ebuf[0] = key_len;
+	os_memcpy(ebuf + 1, key, key_len);
+
+	*elen = len;
+
+	pos = ebuf;
+	while (len > 0) {
+		/* b(1) = MD5(Secret + Request-Authenticator + Salt)
+		 * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
+		addr[0] = secret;
+		_len[0] = secret_len;
+		if (first) {
+			addr[1] = req_authenticator;
+			_len[1] = MD5_MAC_LEN;
+			addr[2] = saltbuf;
+			_len[2] = sizeof(saltbuf);
+		} else {
+			addr[1] = pos - MD5_MAC_LEN;
+			_len[1] = MD5_MAC_LEN;
+		}
+		md5_vector(first ? 3 : 2, addr, _len, hash);
+		first = 0;
+
+		for (i = 0; i < MD5_MAC_LEN; i++)
+			*pos++ ^= hash[i];
+
+		len -= MD5_MAC_LEN;
+	}
+}
+
+
+struct radius_ms_mppe_keys *
+radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+		       const u8 *secret, size_t secret_len)
+{
+	u8 *key;
+	size_t keylen;
+	struct radius_ms_mppe_keys *keys;
+
+	if (msg == NULL || sent_msg == NULL)
+		return NULL;
+
+	keys = os_zalloc(sizeof(*keys));
+	if (keys == NULL)
+		return NULL;
+
+	key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
+					 RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
+					 &keylen);
+	if (key) {
+		keys->send = decrypt_ms_key(key, keylen,
+					    sent_msg->hdr->authenticator,
+					    secret, secret_len,
+					    &keys->send_len);
+		if (!keys->send) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS: Failed to decrypt send key");
+		}
+		os_free(key);
+	}
+
+	key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
+					 RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
+					 &keylen);
+	if (key) {
+		keys->recv = decrypt_ms_key(key, keylen,
+					    sent_msg->hdr->authenticator,
+					    secret, secret_len,
+					    &keys->recv_len);
+		if (!keys->recv) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS: Failed to decrypt recv key");
+		}
+		os_free(key);
+	}
+
+	return keys;
+}
+
+
+struct radius_ms_mppe_keys *
+radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+			  const u8 *secret, size_t secret_len)
+{
+	u8 *key;
+	size_t keylen;
+	struct radius_ms_mppe_keys *keys;
+
+	if (msg == NULL || sent_msg == NULL)
+		return NULL;
+
+	keys = os_zalloc(sizeof(*keys));
+	if (keys == NULL)
+		return NULL;
+
+	key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO,
+					 RADIUS_CISCO_AV_PAIR, &keylen);
+	if (key && keylen == 51 &&
+	    os_memcmp(key, "leap:session-key=", 17) == 0) {
+		keys->recv = decrypt_ms_key(key + 17, keylen - 17,
+					    sent_msg->hdr->authenticator,
+					    secret, secret_len,
+					    &keys->recv_len);
+	}
+	os_free(key);
+
+	return keys;
+}
+
+
+int radius_msg_add_mppe_keys(struct radius_msg *msg,
+			     const u8 *req_authenticator,
+			     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)
+{
+	struct radius_attr_hdr *attr;
+	u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT);
+	u8 *buf;
+	struct radius_attr_vendor *vhdr;
+	u8 *pos;
+	size_t elen;
+	int hlen;
+	u16 salt;
+
+	hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2;
+
+	/* MS-MPPE-Send-Key */
+	buf = os_malloc(hlen + send_key_len + 16);
+	if (buf == NULL) {
+		return 0;
+	}
+	pos = buf;
+	os_memcpy(pos, &vendor_id, sizeof(vendor_id));
+	pos += sizeof(vendor_id);
+	vhdr = (struct radius_attr_vendor *) pos;
+	vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
+	pos = (u8 *) (vhdr + 1);
+	salt = os_random() | 0x8000;
+	WPA_PUT_BE16(pos, salt);
+	pos += 2;
+	encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
+		       secret_len, pos, &elen);
+	vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
+
+	attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+				   buf, hlen + elen);
+	os_free(buf);
+	if (attr == NULL) {
+		return 0;
+	}
+
+	/* MS-MPPE-Recv-Key */
+	buf = os_malloc(hlen + recv_key_len + 16);
+	if (buf == NULL) {
+		return 0;
+	}
+	pos = buf;
+	os_memcpy(pos, &vendor_id, sizeof(vendor_id));
+	pos += sizeof(vendor_id);
+	vhdr = (struct radius_attr_vendor *) pos;
+	vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY;
+	pos = (u8 *) (vhdr + 1);
+	salt ^= 1;
+	WPA_PUT_BE16(pos, salt);
+	pos += 2;
+	encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret,
+		       secret_len, pos, &elen);
+	vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
+
+	attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+				   buf, hlen + elen);
+	os_free(buf);
+	if (attr == NULL) {
+		return 0;
+	}
+
+	return 1;
+}
+
+
+int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
+		       size_t len)
+{
+	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 + 16 > buf_len)
+		return -1;
+
+	os_memcpy(buf, data, data_len);
+
+	padlen = data_len % 16;
+	if (padlen && data_len < buf_len) {
+		padlen = 16 - padlen;
+		os_memset(buf + data_len, 0, padlen);
+		buf_len = data_len + padlen;
+	} else {
+		buf_len = data_len;
+	}
+
+	addr[0] = secret;
+	len[0] = secret_len;
+	addr[1] = msg->hdr->authenticator;
+	len[1] = 16;
+	md5_vector(2, addr, len, hash);
+
+	for (i = 0; i < 16; i++)
+		buf[i] ^= hash[i];
+	pos = 16;
+
+	while (pos < buf_len) {
+		addr[0] = secret;
+		len[0] = secret_len;
+		addr[1] = &buf[pos - 16];
+		len[1] = 16;
+		md5_vector(2, addr, len, hash);
+
+		for (i = 0; i < 16; i++)
+			buf[pos + i] ^= hash[i];
+
+		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, res);
+}
+
+
+int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
+{
+	struct radius_attr_hdr *attr = NULL, *tmp;
+	size_t i, dlen;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		tmp = radius_get_attr_hdr(msg, i);
+		if (tmp->type == type) {
+			attr = tmp;
+			break;
+		}
+	}
+
+	if (!attr || attr->length < sizeof(*attr))
+		return -1;
+
+	dlen = attr->length - sizeof(*attr);
+	if (buf)
+		os_memcpy(buf, (attr + 1), dlen > len ? len : dlen);
+	return dlen;
+}
+
+
+int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
+			    size_t *len, const u8 *start)
+{
+	size_t i;
+	struct radius_attr_hdr *attr = NULL, *tmp;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		tmp = radius_get_attr_hdr(msg, i);
+		if (tmp->type == type &&
+		    (start == NULL || (u8 *) tmp > start)) {
+			attr = tmp;
+			break;
+		}
+	}
+
+	if (!attr || attr->length < sizeof(*attr))
+		return -1;
+
+	*buf = (u8 *) (attr + 1);
+	*len = attr->length - sizeof(*attr);
+	return 0;
+}
+
+
+int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len)
+{
+	size_t i;
+	int count;
+
+	for (count = 0, i = 0; i < msg->attr_used; i++) {
+		struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
+		if (attr->type == type &&
+		    attr->length >= sizeof(struct radius_attr_hdr) + min_len)
+			count++;
+	}
+
+	return count;
+}
+
+
+struct radius_tunnel_attrs {
+	int tag_used;
+	int type; /* Tunnel-Type */
+	int medium_type; /* Tunnel-Medium-Type */
+	int vlanid;
+};
+
+
+/**
+ * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information
+ * @msg: RADIUS message
+ * Returns: VLAN ID for the first tunnel configuration or 0 if none is found
+ */
+int radius_msg_get_vlanid(struct radius_msg *msg)
+{
+	struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun;
+	size_t i;
+	struct radius_attr_hdr *attr = NULL;
+	const u8 *data;
+	char buf[10];
+	size_t dlen;
+
+	os_memset(&tunnel, 0, sizeof(tunnel));
+
+	for (i = 0; i < msg->attr_used; i++) {
+		attr = radius_get_attr_hdr(msg, i);
+		if (attr->length < sizeof(*attr))
+			return -1;
+		data = (const u8 *) (attr + 1);
+		dlen = attr->length - sizeof(*attr);
+		if (attr->length < 3)
+			continue;
+		if (data[0] >= RADIUS_TUNNEL_TAGS)
+			tun = &tunnel[0];
+		else
+			tun = &tunnel[data[0]];
+
+		switch (attr->type) {
+		case RADIUS_ATTR_TUNNEL_TYPE:
+			if (attr->length != 6)
+				break;
+			tun->tag_used++;
+			tun->type = WPA_GET_BE24(data + 1);
+			break;
+		case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
+			if (attr->length != 6)
+				break;
+			tun->tag_used++;
+			tun->medium_type = WPA_GET_BE24(data + 1);
+			break;
+		case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
+			if (data[0] < RADIUS_TUNNEL_TAGS) {
+				data++;
+				dlen--;
+			}
+			if (dlen >= sizeof(buf))
+				break;
+			os_memcpy(buf, data, dlen);
+			buf[dlen] = '\0';
+			tun->tag_used++;
+			tun->vlanid = atoi(buf);
+			break;
+		}
+	}
+
+	for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) {
+		tun = &tunnel[i];
+		if (tun->tag_used &&
+		    tun->type == RADIUS_TUNNEL_TYPE_VLAN &&
+		    tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 &&
+		    tun->vlanid > 0)
+			return tun->vlanid;
+	}
+
+	return 0;
+}
+
+
+/**
+ * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password
+ * @msg: Received RADIUS message
+ * @keylen: Length of returned password
+ * @secret: RADIUS shared secret
+ * @secret_len: Length of secret
+ * @sent_msg: Sent RADIUS message
+ * @n: Number of password attribute to return (starting with 0)
+ * Returns: Pointer to n-th password (free with os_free) or %NULL
+ */
+char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
+				      const u8 *secret, size_t secret_len,
+				      struct radius_msg *sent_msg, size_t n)
+{
+	u8 *buf = NULL;
+	size_t buflen;
+	const u8 *salt;
+	u8 *str;
+	const u8 *addr[3];
+	size_t len[3];
+	u8 hash[16];
+	u8 *pos;
+	size_t i, j = 0;
+	struct radius_attr_hdr *attr;
+	const u8 *data;
+	size_t dlen;
+	const u8 *fdata = NULL; /* points to found item */
+	size_t fdlen = -1;
+	char *ret = NULL;
+
+	/* find n-th valid Tunnel-Password attribute */
+	for (i = 0; i < msg->attr_used; i++) {
+		attr = radius_get_attr_hdr(msg, i);
+		if (attr == NULL ||
+		    attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) {
+			continue;
+		}
+		if (attr->length <= 5)
+			continue;
+		data = (const u8 *) (attr + 1);
+		dlen = attr->length - sizeof(*attr);
+		if (dlen <= 3 || dlen % 16 != 3)
+			continue;
+		j++;
+		if (j <= n)
+			continue;
+
+		fdata = data;
+		fdlen = dlen;
+		break;
+	}
+	if (fdata == NULL)
+		goto out;
+
+	/* alloc writable memory for decryption */
+	buf = os_malloc(fdlen);
+	if (buf == NULL)
+		goto out;
+	os_memcpy(buf, fdata, fdlen);
+	buflen = fdlen;
+
+	/* init pointers */
+	salt = buf + 1;
+	str = buf + 3;
+
+	/* decrypt blocks */
+	pos = buf + buflen - 16; /* last block */
+	while (pos >= str + 16) { /* all but the first block */
+		addr[0] = secret;
+		len[0] = secret_len;
+		addr[1] = pos - 16;
+		len[1] = 16;
+		md5_vector(2, addr, len, hash);
+
+		for (i = 0; i < 16; i++)
+			pos[i] ^= hash[i];
+
+		pos -= 16;
+	}
+
+	/* decrypt first block */
+	if (str != pos)
+		goto out;
+	addr[0] = secret;
+	len[0] = secret_len;
+	addr[1] = sent_msg->hdr->authenticator;
+	len[1] = 16;
+	addr[2] = salt;
+	len[2] = 2;
+	md5_vector(3, addr, len, hash);
+
+	for (i = 0; i < 16; i++)
+		pos[i] ^= hash[i];
+
+	/* derive plaintext length from first subfield */
+	*keylen = (unsigned char) str[0];
+	if ((u8 *) (str + *keylen) >= (u8 *) (buf + buflen)) {
+		/* decryption error - invalid key length */
+		goto out;
+	}
+	if (*keylen == 0) {
+		/* empty password */
+		goto out;
+	}
+
+	/* copy passphrase into new buffer */
+	ret = os_malloc(*keylen);
+	if (ret)
+		os_memcpy(ret, str + 1, *keylen);
+
+out:
+	/* return new buffer */
+	os_free(buf);
+	return ret;
+}
+
+
+void radius_free_class(struct radius_class_data *c)
+{
+	size_t i;
+	if (c == NULL)
+		return;
+	for (i = 0; i < c->count; i++)
+		os_free(c->attr[i].data);
+	os_free(c->attr);
+	c->attr = NULL;
+	c->count = 0;
+}
+
+
+int radius_copy_class(struct radius_class_data *dst,
+		      const struct radius_class_data *src)
+{
+	size_t i;
+
+	if (src->attr == NULL)
+		return 0;
+
+	dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data));
+	if (dst->attr == NULL)
+		return -1;
+
+	dst->count = 0;
+
+	for (i = 0; i < src->count; i++) {
+		dst->attr[i].data = os_malloc(src->attr[i].len);
+		if (dst->attr[i].data == NULL)
+			break;
+		dst->count++;
+		os_memcpy(dst->attr[i].data, src->attr[i].data,
+			  src->attr[i].len);
+		dst->attr[i].len = src->attr[i].len;
+	}
+
+	return 0;
+}
+
+
+u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs)
+{
+	size_t i, j;
+	struct radius_attr_hdr *attr;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		attr = radius_get_attr_hdr(msg, i);
+
+		for (j = 0; attrs[j]; j++) {
+			if (attr->type == attrs[j])
+				break;
+		}
+
+		if (attrs[j] == 0)
+			return attr->type; /* unlisted attr */
+	}
+
+	return 0;
+}
diff --git a/hostap/src/radius/radius.h b/hostap/src/radius/radius.h
new file mode 100644
index 0000000..5977339
--- /dev/null
+++ b/hostap/src/radius/radius.h
@@ -0,0 +1,322 @@
+/*
+ * RADIUS message processing
+ * 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.
+ */
+
+#ifndef RADIUS_H
+#define RADIUS_H
+
+/* RFC 2865 - RADIUS */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct radius_hdr {
+	u8 code;
+	u8 identifier;
+	be16 length; /* including this header */
+	u8 authenticator[16];
+	/* followed by length-20 octets of attributes */
+} STRUCT_PACKED;
+
+enum { RADIUS_CODE_ACCESS_REQUEST = 1,
+       RADIUS_CODE_ACCESS_ACCEPT = 2,
+       RADIUS_CODE_ACCESS_REJECT = 3,
+       RADIUS_CODE_ACCOUNTING_REQUEST = 4,
+       RADIUS_CODE_ACCOUNTING_RESPONSE = 5,
+       RADIUS_CODE_ACCESS_CHALLENGE = 11,
+       RADIUS_CODE_STATUS_SERVER = 12,
+       RADIUS_CODE_STATUS_CLIENT = 13,
+       RADIUS_CODE_DISCONNECT_REQUEST = 40,
+       RADIUS_CODE_DISCONNECT_ACK = 41,
+       RADIUS_CODE_DISCONNECT_NAK = 42,
+       RADIUS_CODE_COA_REQUEST = 43,
+       RADIUS_CODE_COA_ACK = 44,
+       RADIUS_CODE_COA_NAK = 45,
+       RADIUS_CODE_RESERVED = 255
+};
+
+struct radius_attr_hdr {
+	u8 type;
+	u8 length; /* including this header */
+	/* followed by length-2 octets of attribute value */
+} STRUCT_PACKED;
+
+#define RADIUS_MAX_ATTR_LEN (255 - sizeof(struct radius_attr_hdr))
+
+enum { RADIUS_ATTR_USER_NAME = 1,
+       RADIUS_ATTR_USER_PASSWORD = 2,
+       RADIUS_ATTR_NAS_IP_ADDRESS = 4,
+       RADIUS_ATTR_NAS_PORT = 5,
+       RADIUS_ATTR_FRAMED_MTU = 12,
+       RADIUS_ATTR_REPLY_MESSAGE = 18,
+       RADIUS_ATTR_STATE = 24,
+       RADIUS_ATTR_CLASS = 25,
+       RADIUS_ATTR_VENDOR_SPECIFIC = 26,
+       RADIUS_ATTR_SESSION_TIMEOUT = 27,
+       RADIUS_ATTR_IDLE_TIMEOUT = 28,
+       RADIUS_ATTR_TERMINATION_ACTION = 29,
+       RADIUS_ATTR_CALLED_STATION_ID = 30,
+       RADIUS_ATTR_CALLING_STATION_ID = 31,
+       RADIUS_ATTR_NAS_IDENTIFIER = 32,
+       RADIUS_ATTR_PROXY_STATE = 33,
+       RADIUS_ATTR_ACCT_STATUS_TYPE = 40,
+       RADIUS_ATTR_ACCT_DELAY_TIME = 41,
+       RADIUS_ATTR_ACCT_INPUT_OCTETS = 42,
+       RADIUS_ATTR_ACCT_OUTPUT_OCTETS = 43,
+       RADIUS_ATTR_ACCT_SESSION_ID = 44,
+       RADIUS_ATTR_ACCT_AUTHENTIC = 45,
+       RADIUS_ATTR_ACCT_SESSION_TIME = 46,
+       RADIUS_ATTR_ACCT_INPUT_PACKETS = 47,
+       RADIUS_ATTR_ACCT_OUTPUT_PACKETS = 48,
+       RADIUS_ATTR_ACCT_TERMINATE_CAUSE = 49,
+       RADIUS_ATTR_ACCT_MULTI_SESSION_ID = 50,
+       RADIUS_ATTR_ACCT_LINK_COUNT = 51,
+       RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52,
+       RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53,
+       RADIUS_ATTR_EVENT_TIMESTAMP = 55,
+       RADIUS_ATTR_NAS_PORT_TYPE = 61,
+       RADIUS_ATTR_TUNNEL_TYPE = 64,
+       RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65,
+       RADIUS_ATTR_TUNNEL_PASSWORD = 69,
+       RADIUS_ATTR_CONNECT_INFO = 77,
+       RADIUS_ATTR_EAP_MESSAGE = 79,
+       RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80,
+       RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81,
+       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_EAP_KEY_NAME = 102,
+       RADIUS_ATTR_OPERATOR_NAME = 126,
+       RADIUS_ATTR_LOCATION_INFO = 127,
+       RADIUS_ATTR_LOCATION_DATA = 128,
+       RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES = 129,
+       RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES = 130,
+       RADIUS_ATTR_LOCATION_CAPABLE = 131,
+       RADIUS_ATTR_REQUESTED_LOCATION_INFO = 132,
+       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,
+};
+
+
+/* Termination-Action */
+#define RADIUS_TERMINATION_ACTION_DEFAULT 0
+#define RADIUS_TERMINATION_ACTION_RADIUS_REQUEST 1
+
+/* NAS-Port-Type */
+#define RADIUS_NAS_PORT_TYPE_IEEE_802_11 19
+
+/* Acct-Status-Type */
+#define RADIUS_ACCT_STATUS_TYPE_START 1
+#define RADIUS_ACCT_STATUS_TYPE_STOP 2
+#define RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE 3
+#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON 7
+#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF 8
+
+/* Acct-Authentic */
+#define RADIUS_ACCT_AUTHENTIC_RADIUS 1
+#define RADIUS_ACCT_AUTHENTIC_LOCAL 2
+#define RADIUS_ACCT_AUTHENTIC_REMOTE 3
+
+/* Acct-Terminate-Cause */
+#define RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST 1
+#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_CARRIER 2
+#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_SERVICE 3
+#define RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT 4
+#define RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT 5
+#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET 6
+#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT 7
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_ERROR 8
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_ERROR 9
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REQUEST 10
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT 11
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_UNNEEDED 12
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_PREEMPTED 13
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_SUSPENDED 14
+#define RADIUS_ACCT_TERMINATE_CAUSE_SERVICE_UNAVAILABLE 15
+#define RADIUS_ACCT_TERMINATE_CAUSE_CALLBACK 16
+#define RADIUS_ACCT_TERMINATE_CAUSE_USER_ERROR 17
+#define RADIUS_ACCT_TERMINATE_CAUSE_HOST_REQUEST 18
+
+#define RADIUS_TUNNEL_TAGS 32
+
+/* Tunnel-Type */
+#define RADIUS_TUNNEL_TYPE_PPTP 1
+#define RADIUS_TUNNEL_TYPE_L2TP 3
+#define RADIUS_TUNNEL_TYPE_IPIP 7
+#define RADIUS_TUNNEL_TYPE_GRE 10
+#define RADIUS_TUNNEL_TYPE_VLAN 13
+
+/* Tunnel-Medium-Type */
+#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV4 1
+#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV6 2
+#define RADIUS_TUNNEL_MEDIUM_TYPE_802 6
+
+
+struct radius_attr_vendor {
+	u8 vendor_type;
+	u8 vendor_length;
+} STRUCT_PACKED;
+
+#define RADIUS_VENDOR_ID_CISCO 9
+#define RADIUS_CISCO_AV_PAIR 1
+
+/* RFC 2548 - Microsoft Vendor-specific RADIUS Attributes */
+#define RADIUS_VENDOR_ID_MICROSOFT 311
+
+enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16,
+       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 */
+
+struct radius_ms_mppe_keys {
+	u8 *send;
+	size_t send_len;
+	u8 *recv;
+	size_t recv_len;
+};
+
+
+struct radius_msg;
+
+/* Default size to be allocated for new RADIUS messages */
+#define RADIUS_DEFAULT_MSG_SIZE 1024
+
+/* Default size to be allocated for attribute array */
+#define RADIUS_DEFAULT_ATTR_COUNT 16
+
+
+/* MAC address ASCII format for IEEE 802.1X use
+ * (draft-congdon-radius-8021x-20.txt) */
+#define RADIUS_802_1X_ADDR_FORMAT "%02X-%02X-%02X-%02X-%02X-%02X"
+/* MAC address ASCII format for non-802.1X use */
+#define RADIUS_ADDR_FORMAT "%02x%02x%02x%02x%02x%02x"
+
+struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg);
+struct wpabuf * radius_msg_get_buf(struct radius_msg *msg);
+struct radius_msg * radius_msg_new(u8 code, u8 identifier);
+void radius_msg_free(struct radius_msg *msg);
+void radius_msg_dump(struct radius_msg *msg);
+int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
+		      size_t secret_len);
+int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
+			  size_t secret_len, const u8 *req_authenticator);
+int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len,
+			       const struct radius_hdr *req_hdr);
+void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
+			    size_t secret_len);
+void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
+				 size_t secret_len,
+				 const u8 *req_authenticator);
+int radius_msg_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,
+			       size_t secret_len);
+struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type,
+					     const u8 *data, size_t data_len);
+struct radius_msg * radius_msg_parse(const u8 *data, size_t len);
+int radius_msg_add_eap(struct radius_msg *msg, const u8 *data,
+		       size_t data_len);
+struct wpabuf * radius_msg_get_eap(struct radius_msg *msg);
+int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
+		      size_t secret_len, struct radius_msg *sent_msg,
+		      int auth);
+int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len, const u8 *req_auth);
+int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
+			 u8 type);
+void radius_msg_make_authenticator(struct radius_msg *msg,
+				   const u8 *data, size_t len);
+struct radius_ms_mppe_keys *
+radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+		       const u8 *secret, size_t secret_len);
+struct radius_ms_mppe_keys *
+radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+			  const u8 *secret, size_t secret_len);
+int radius_msg_add_mppe_keys(struct radius_msg *msg,
+			     const u8 *req_authenticator,
+			     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,
+				  const u8 *secret, size_t secret_len);
+int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len);
+int radius_msg_get_vlanid(struct radius_msg *msg);
+char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
+				      const u8 *secret, size_t secret_len,
+				      struct radius_msg *sent_msg, size_t n);
+
+static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type,
+					    u32 value)
+{
+	u32 val = htonl(value);
+	return radius_msg_add_attr(msg, type, (u8 *) &val, 4) != NULL;
+}
+
+static inline int radius_msg_get_attr_int32(struct radius_msg *msg, u8 type,
+					    u32 *value)
+{
+	u32 val;
+	int res;
+	res = radius_msg_get_attr(msg, type, (u8 *) &val, 4);
+	if (res != 4)
+		return -1;
+
+	*value = ntohl(val);
+	return 0;
+}
+int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
+			    size_t *len, const u8 *start);
+int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len);
+
+
+struct radius_attr_data {
+	u8 *data;
+	size_t len;
+};
+
+struct radius_class_data {
+	struct radius_attr_data *attr;
+	size_t count;
+};
+
+void radius_free_class(struct radius_class_data *c);
+int radius_copy_class(struct radius_class_data *dst,
+		      const struct radius_class_data *src);
+
+u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs);
+
+#endif /* RADIUS_H */
diff --git a/hostap/src/radius/radius_client.c b/hostap/src/radius/radius_client.c
new file mode 100644
index 0000000..693f61e
--- /dev/null
+++ b/hostap/src/radius/radius_client.c
@@ -0,0 +1,1624 @@
+/*
+ * RADIUS client
+ * Copyright (c) 2002-2015, 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 "radius.h"
+#include "radius_client.h"
+#include "eloop.h"
+
+/* Defaults for RADIUS retransmit values (exponential backoff) */
+
+/**
+ * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds
+ */
+#define RADIUS_CLIENT_FIRST_WAIT 3
+
+/**
+ * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds
+ */
+#define RADIUS_CLIENT_MAX_WAIT 120
+
+/**
+ * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries
+ *
+ * Maximum number of retransmit attempts before the entry is removed from
+ * retransmit list.
+ */
+#define RADIUS_CLIENT_MAX_RETRIES 10
+
+/**
+ * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages
+ *
+ * Maximum number of entries in retransmit list (oldest entries will be
+ * removed, if this limit is exceeded).
+ */
+#define RADIUS_CLIENT_MAX_ENTRIES 30
+
+/**
+ * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point
+ *
+ * The number of failed retry attempts after which the RADIUS server will be
+ * changed (if one of more backup servers are configured).
+ */
+#define RADIUS_CLIENT_NUM_FAILOVER 4
+
+
+/**
+ * struct radius_rx_handler - RADIUS client RX handler
+ *
+ * This data structure is used internally inside the RADIUS client module to
+ * store registered RX handlers. These handlers are registered by calls to
+ * radius_client_register() and unregistered when the RADIUS client is
+ * deinitialized with a call to radius_client_deinit().
+ */
+struct radius_rx_handler {
+	/**
+	 * handler - Received RADIUS message handler
+	 */
+	RadiusRxResult (*handler)(struct radius_msg *msg,
+				  struct radius_msg *req,
+				  const u8 *shared_secret,
+				  size_t shared_secret_len,
+				  void *data);
+
+	/**
+	 * data - Context data for the handler
+	 */
+	void *data;
+};
+
+
+/**
+ * struct radius_msg_list - RADIUS client message retransmit list
+ *
+ * This data structure is used internally inside the RADIUS client module to
+ * store pending RADIUS requests that may still need to be retransmitted.
+ */
+struct radius_msg_list {
+	/**
+	 * addr - STA/client address
+	 *
+	 * This is used to find RADIUS messages for the same STA.
+	 */
+	u8 addr[ETH_ALEN];
+
+	/**
+	 * msg - RADIUS message
+	 */
+	struct radius_msg *msg;
+
+	/**
+	 * msg_type - Message type
+	 */
+	RadiusType msg_type;
+
+	/**
+	 * first_try - Time of the first transmission attempt
+	 */
+	os_time_t first_try;
+
+	/**
+	 * next_try - Time for the next transmission attempt
+	 */
+	os_time_t next_try;
+
+	/**
+	 * attempts - Number of transmission attempts
+	 */
+	int attempts;
+
+	/**
+	 * next_wait - Next retransmission wait time in seconds
+	 */
+	int next_wait;
+
+	/**
+	 * last_attempt - Time of the last transmission attempt
+	 */
+	struct os_reltime last_attempt;
+
+	/**
+	 * shared_secret - Shared secret with the target RADIUS server
+	 */
+	const u8 *shared_secret;
+
+	/**
+	 * shared_secret_len - shared_secret length in octets
+	 */
+	size_t shared_secret_len;
+
+	/* TODO: server config with failover to backup server(s) */
+
+	/**
+	 * next - Next message in the list
+	 */
+	struct radius_msg_list *next;
+};
+
+
+/**
+ * struct radius_client_data - Internal RADIUS client data
+ *
+ * This data structure is used internally inside the RADIUS client module.
+ * External users allocate this by calling radius_client_init() and free it by
+ * calling radius_client_deinit(). The pointer to this opaque data is used in
+ * calls to other functions as an identifier for the RADIUS client instance.
+ */
+struct radius_client_data {
+	/**
+	 * ctx - Context pointer for hostapd_logger() callbacks
+	 */
+	void *ctx;
+
+	/**
+	 * conf - RADIUS client configuration (list of RADIUS servers to use)
+	 */
+	struct hostapd_radius_servers *conf;
+
+	/**
+	 * auth_serv_sock - IPv4 socket for RADIUS authentication messages
+	 */
+	int auth_serv_sock;
+
+	/**
+	 * acct_serv_sock - IPv4 socket for RADIUS accounting messages
+	 */
+	int acct_serv_sock;
+
+	/**
+	 * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages
+	 */
+	int auth_serv_sock6;
+
+	/**
+	 * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages
+	 */
+	int acct_serv_sock6;
+
+	/**
+	 * auth_sock - Currently used socket for RADIUS authentication server
+	 */
+	int auth_sock;
+
+	/**
+	 * acct_sock - Currently used socket for RADIUS accounting server
+	 */
+	int acct_sock;
+
+	/**
+	 * auth_handlers - Authentication message handlers
+	 */
+	struct radius_rx_handler *auth_handlers;
+
+	/**
+	 * num_auth_handlers - Number of handlers in auth_handlers
+	 */
+	size_t num_auth_handlers;
+
+	/**
+	 * acct_handlers - Accounting message handlers
+	 */
+	struct radius_rx_handler *acct_handlers;
+
+	/**
+	 * num_acct_handlers - Number of handlers in acct_handlers
+	 */
+	size_t num_acct_handlers;
+
+	/**
+	 * msgs - Pending outgoing RADIUS messages
+	 */
+	struct radius_msg_list *msgs;
+
+	/**
+	 * num_msgs - Number of pending messages in the msgs list
+	 */
+	size_t num_msgs;
+
+	/**
+	 * next_radius_identifier - Next RADIUS message identifier to use
+	 */
+	u8 next_radius_identifier;
+};
+
+
+static int
+radius_change_server(struct radius_client_data *radius,
+		     struct hostapd_radius_server *nserv,
+		     struct hostapd_radius_server *oserv,
+		     int sock, int sock6, int auth);
+static int radius_client_init_acct(struct radius_client_data *radius);
+static int radius_client_init_auth(struct radius_client_data *radius);
+static void radius_client_auth_failover(struct radius_client_data *radius);
+static void radius_client_acct_failover(struct radius_client_data *radius);
+
+
+static void radius_client_msg_free(struct radius_msg_list *req)
+{
+	radius_msg_free(req->msg);
+	os_free(req);
+}
+
+
+/**
+ * radius_client_register - Register a RADIUS client RX handler
+ * @radius: RADIUS client context from radius_client_init()
+ * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT)
+ * @handler: Handler for received RADIUS messages
+ * @data: Context pointer for handler callbacks
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to register a handler for processing received RADIUS
+ * authentication and accounting messages. The handler() callback function will
+ * be called whenever a RADIUS message is received from the active server.
+ *
+ * There can be multiple registered RADIUS message handlers. The handlers will
+ * be called in order until one of them indicates that it has processed or
+ * queued the message.
+ */
+int radius_client_register(struct radius_client_data *radius,
+			   RadiusType msg_type,
+			   RadiusRxResult (*handler)(struct radius_msg *msg,
+						     struct radius_msg *req,
+						     const u8 *shared_secret,
+						     size_t shared_secret_len,
+						     void *data),
+			   void *data)
+{
+	struct radius_rx_handler **handlers, *newh;
+	size_t *num;
+
+	if (msg_type == RADIUS_ACCT) {
+		handlers = &radius->acct_handlers;
+		num = &radius->num_acct_handlers;
+	} else {
+		handlers = &radius->auth_handlers;
+		num = &radius->num_auth_handlers;
+	}
+
+	newh = os_realloc_array(*handlers, *num + 1,
+				sizeof(struct radius_rx_handler));
+	if (newh == NULL)
+		return -1;
+
+	newh[*num].handler = handler;
+	newh[*num].data = data;
+	(*num)++;
+	*handlers = newh;
+
+	return 0;
+}
+
+
+/*
+ * 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;
+	wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno));
+	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
+	    _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");
+		if (msg_type == RADIUS_ACCT ||
+		    msg_type == RADIUS_ACCT_INTERIM) {
+			radius_client_init_acct(radius);
+			return 0;
+		} else {
+			radius_client_init_auth(radius);
+			return 1;
+		}
+	}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	return 0;
+}
+
+
+static int radius_client_retransmit(struct radius_client_data *radius,
+				    struct radius_msg_list *entry,
+				    os_time_t now)
+{
+	struct hostapd_radius_servers *conf = radius->conf;
+	int s;
+	struct wpabuf *buf;
+	size_t prev_num_msgs;
+
+	if (entry->msg_type == RADIUS_ACCT ||
+	    entry->msg_type == RADIUS_ACCT_INTERIM) {
+		if (radius->acct_sock < 0)
+			radius_client_init_acct(radius);
+		if (radius->acct_sock < 0 && conf->num_acct_servers > 1) {
+			prev_num_msgs = radius->num_msgs;
+			radius_client_acct_failover(radius);
+			if (prev_num_msgs != radius->num_msgs)
+				return 0;
+		}
+		s = radius->acct_sock;
+		if (entry->attempts == 0)
+			conf->acct_server->requests++;
+		else {
+			conf->acct_server->timeouts++;
+			conf->acct_server->retransmissions++;
+		}
+	} else {
+		if (radius->auth_sock < 0)
+			radius_client_init_auth(radius);
+		if (radius->auth_sock < 0 && conf->num_auth_servers > 1) {
+			prev_num_msgs = radius->num_msgs;
+			radius_client_auth_failover(radius);
+			if (prev_num_msgs != radius->num_msgs)
+				return 0;
+		}
+		s = radius->auth_sock;
+		if (entry->attempts == 0)
+			conf->auth_server->requests++;
+		else {
+			conf->auth_server->timeouts++;
+			conf->auth_server->retransmissions++;
+		}
+	}
+	if (s < 0) {
+		wpa_printf(MSG_INFO,
+			   "RADIUS: No valid socket for retransmission");
+		return 1;
+	}
+
+	/* retransmit; remove entry if too many attempts */
+	entry->attempts++;
+	hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
+		       radius_msg_get_hdr(entry->msg)->identifier);
+
+	os_get_reltime(&entry->last_attempt);
+	buf = radius_msg_get_buf(entry->msg);
+	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) {
+		wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct radius_client_data *radius = eloop_ctx;
+	struct hostapd_radius_servers *conf = radius->conf;
+	struct os_reltime now;
+	os_time_t first;
+	struct radius_msg_list *entry, *prev, *tmp;
+	int auth_failover = 0, acct_failover = 0;
+	size_t prev_num_msgs;
+	int s;
+
+	entry = radius->msgs;
+	if (!entry)
+		return;
+
+	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)
+				prev->next = entry->next;
+			else
+				radius->msgs = entry->next;
+
+			tmp = entry;
+			entry = entry->next;
+			radius_client_msg_free(tmp);
+			radius->num_msgs--;
+			continue;
+		}
+
+		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++;
+			else
+				auth_failover++;
+		}
+
+		if (first == 0 || entry->next_try < first)
+			first = entry->next_try;
+
+		prev = entry;
+		entry = entry->next;
+	}
+
+	if (radius->msgs) {
+		if (first < now.sec)
+			first = now.sec;
+		eloop_register_timeout(first - now.sec, 0,
+				       radius_client_timer, radius, NULL);
+		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+			       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client "
+			       "retransmit in %ld seconds",
+			       (long int) (first - now.sec));
+	}
+
+	if (auth_failover && conf->num_auth_servers > 1)
+		radius_client_auth_failover(radius);
+
+	if (acct_failover && conf->num_acct_servers > 1)
+		radius_client_acct_failover(radius);
+}
+
+
+static void radius_client_auth_failover(struct radius_client_data *radius)
+{
+	struct hostapd_radius_servers *conf = radius->conf;
+	struct hostapd_radius_server *next, *old;
+	struct radius_msg_list *entry;
+	char abuf[50];
+
+	old = conf->auth_server;
+	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_NOTICE,
+		       "No response from Authentication server %s:%d - failover",
+		       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
+		       old->port);
+
+	for (entry = radius->msgs; entry; entry = entry->next) {
+		if (entry->msg_type == RADIUS_AUTH)
+			old->timeouts++;
+	}
+
+	next = old + 1;
+	if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
+		next = conf->auth_servers;
+	conf->auth_server = next;
+	radius_change_server(radius, next, old,
+			     radius->auth_serv_sock,
+			     radius->auth_serv_sock6, 1);
+}
+
+
+static void radius_client_acct_failover(struct radius_client_data *radius)
+{
+	struct hostapd_radius_servers *conf = radius->conf;
+	struct hostapd_radius_server *next, *old;
+	struct radius_msg_list *entry;
+	char abuf[50];
+
+	old = conf->acct_server;
+	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_NOTICE,
+		       "No response from Accounting server %s:%d - failover",
+		       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
+		       old->port);
+
+	for (entry = radius->msgs; entry; entry = entry->next) {
+		if (entry->msg_type == RADIUS_ACCT ||
+		    entry->msg_type == RADIUS_ACCT_INTERIM)
+			old->timeouts++;
+	}
+
+	next = old + 1;
+	if (next > &conf->acct_servers[conf->num_acct_servers - 1])
+		next = conf->acct_servers;
+	conf->acct_server = next;
+	radius_change_server(radius, next, old,
+			     radius->acct_serv_sock,
+			     radius->acct_serv_sock6, 0);
+}
+
+
+static void radius_client_update_timeout(struct radius_client_data *radius)
+{
+	struct os_reltime now;
+	os_time_t first;
+	struct radius_msg_list *entry;
+
+	eloop_cancel_timeout(radius_client_timer, radius, NULL);
+
+	if (radius->msgs == NULL) {
+		return;
+	}
+
+	first = 0;
+	for (entry = radius->msgs; entry; entry = entry->next) {
+		if (first == 0 || entry->next_try < first)
+			first = entry->next_try;
+	}
+
+	os_get_reltime(&now);
+	if (first < now.sec)
+		first = now.sec;
+	eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
+			       NULL);
+	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
+		       " %ld seconds", (long int) (first - now.sec));
+}
+
+
+static void radius_client_list_add(struct radius_client_data *radius,
+				   struct radius_msg *msg,
+				   RadiusType msg_type,
+				   const u8 *shared_secret,
+				   size_t shared_secret_len, const u8 *addr)
+{
+	struct radius_msg_list *entry, *prev;
+
+	if (eloop_terminated()) {
+		/* No point in adding entries to retransmit queue since event
+		 * loop has already been terminated. */
+		radius_msg_free(msg);
+		return;
+	}
+
+	entry = os_zalloc(sizeof(*entry));
+	if (entry == NULL) {
+		wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list");
+		radius_msg_free(msg);
+		return;
+	}
+
+	if (addr)
+		os_memcpy(entry->addr, addr, ETH_ALEN);
+	entry->msg = msg;
+	entry->msg_type = msg_type;
+	entry->shared_secret = shared_secret;
+	entry->shared_secret_len = shared_secret_len;
+	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;
+	entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
+	entry->next = radius->msgs;
+	radius->msgs = entry;
+	radius_client_update_timeout(radius);
+
+	if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
+		wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits");
+		prev = NULL;
+		while (entry->next) {
+			prev = entry;
+			entry = entry->next;
+		}
+		if (prev) {
+			prev->next = NULL;
+			radius_client_msg_free(entry);
+		}
+	} else
+		radius->num_msgs++;
+}
+
+
+static void radius_client_list_del(struct radius_client_data *radius,
+				   RadiusType msg_type, const u8 *addr)
+{
+	struct radius_msg_list *entry, *prev, *tmp;
+
+	if (addr == NULL)
+		return;
+
+	entry = radius->msgs;
+	prev = NULL;
+	while (entry) {
+		if (entry->msg_type == msg_type &&
+		    os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
+			if (prev)
+				prev->next = entry->next;
+			else
+				radius->msgs = entry->next;
+			tmp = entry;
+			entry = entry->next;
+			hostapd_logger(radius->ctx, addr,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "Removing matching RADIUS message");
+			radius_client_msg_free(tmp);
+			radius->num_msgs--;
+			continue;
+		}
+		prev = entry;
+		entry = entry->next;
+	}
+}
+
+
+/**
+ * radius_client_send - Send a RADIUS request
+ * @radius: RADIUS client context from radius_client_init()
+ * @msg: RADIUS message to be sent
+ * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM)
+ * @addr: MAC address of the device related to this message or %NULL
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or
+ * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference
+ * between accounting and interim accounting messages is that the interim
+ * message will override any pending interim accounting updates while a new
+ * accounting message does not remove any pending messages.
+ *
+ * The message is added on the retransmission queue and will be retransmitted
+ * automatically until a response is received or maximum number of retries
+ * (RADIUS_CLIENT_MAX_RETRIES) is reached.
+ *
+ * The related device MAC address can be used to identify pending messages that
+ * can be removed with radius_client_flush_auth() or with interim accounting
+ * updates.
+ */
+int radius_client_send(struct radius_client_data *radius,
+		       struct radius_msg *msg, RadiusType msg_type,
+		       const u8 *addr)
+{
+	struct hostapd_radius_servers *conf = radius->conf;
+	const u8 *shared_secret;
+	size_t shared_secret_len;
+	char *name;
+	int s, res;
+	struct wpabuf *buf;
+
+	if (msg_type == RADIUS_ACCT_INTERIM) {
+		/* Remove any pending interim acct update for the same STA. */
+		radius_client_list_del(radius, msg_type, addr);
+	}
+
+	if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
+		if (conf->acct_server && radius->acct_sock < 0)
+			radius_client_init_acct(radius);
+
+		if (conf->acct_server == NULL || radius->acct_sock < 0 ||
+		    conf->acct_server->shared_secret == NULL) {
+			hostapd_logger(radius->ctx, NULL,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO,
+				       "No accounting server configured");
+			return -1;
+		}
+		shared_secret = conf->acct_server->shared_secret;
+		shared_secret_len = conf->acct_server->shared_secret_len;
+		radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
+		name = "accounting";
+		s = radius->acct_sock;
+		conf->acct_server->requests++;
+	} else {
+		if (conf->auth_server && radius->auth_sock < 0)
+			radius_client_init_auth(radius);
+
+		if (conf->auth_server == NULL || radius->auth_sock < 0 ||
+		    conf->auth_server->shared_secret == NULL) {
+			hostapd_logger(radius->ctx, NULL,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO,
+				       "No authentication server configured");
+			return -1;
+		}
+		shared_secret = conf->auth_server->shared_secret;
+		shared_secret_len = conf->auth_server->shared_secret_len;
+		radius_msg_finish(msg, shared_secret, shared_secret_len);
+		name = "authentication";
+		s = radius->auth_sock;
+		conf->auth_server->requests++;
+	}
+
+	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s "
+		       "server", name);
+	if (conf->msg_dumps)
+		radius_msg_dump(msg);
+
+	buf = radius_msg_get_buf(msg);
+	res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
+	if (res < 0)
+		radius_client_handle_send_error(radius, s, msg_type);
+
+	radius_client_list_add(radius, msg, msg_type, shared_secret,
+			       shared_secret_len, addr);
+
+	return 0;
+}
+
+
+static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct radius_client_data *radius = eloop_ctx;
+	struct hostapd_radius_servers *conf = radius->conf;
+	RadiusType msg_type = (RadiusType) sock_ctx;
+	int len, roundtrip;
+	unsigned char buf[3000];
+	struct radius_msg *msg;
+	struct radius_hdr *hdr;
+	struct radius_rx_handler *handlers;
+	size_t num_handlers, i;
+	struct radius_msg_list *req, *prev_req;
+	struct os_reltime now;
+	struct hostapd_radius_server *rconf;
+	int invalid_authenticator = 0;
+
+	if (msg_type == RADIUS_ACCT) {
+		handlers = radius->acct_handlers;
+		num_handlers = radius->num_acct_handlers;
+		rconf = conf->acct_server;
+	} else {
+		handlers = radius->auth_handlers;
+		num_handlers = radius->num_auth_handlers;
+		rconf = conf->auth_server;
+	}
+
+	len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
+	if (len < 0) {
+		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)) {
+		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) {
+		wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed");
+		rconf->malformed_responses++;
+		return;
+	}
+	hdr = radius_msg_get_hdr(msg);
+
+	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");
+	if (conf->msg_dumps)
+		radius_msg_dump(msg);
+
+	switch (hdr->code) {
+	case RADIUS_CODE_ACCESS_ACCEPT:
+		rconf->access_accepts++;
+		break;
+	case RADIUS_CODE_ACCESS_REJECT:
+		rconf->access_rejects++;
+		break;
+	case RADIUS_CODE_ACCESS_CHALLENGE:
+		rconf->access_challenges++;
+		break;
+	case RADIUS_CODE_ACCOUNTING_RESPONSE:
+		rconf->responses++;
+		break;
+	}
+
+	prev_req = NULL;
+	req = radius->msgs;
+	while (req) {
+		/* TODO: also match by src addr:port of the packet when using
+		 * alternative RADIUS servers (?) */
+		if ((req->msg_type == msg_type ||
+		     (req->msg_type == RADIUS_ACCT_INTERIM &&
+		      msg_type == RADIUS_ACCT)) &&
+		    radius_msg_get_hdr(req->msg)->identifier ==
+		    hdr->identifier)
+			break;
+
+		prev_req = req;
+		req = req->next;
+	}
+
+	if (req == NULL) {
+		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "No matching RADIUS request found (type=%d "
+			       "id=%d) - dropping packet",
+			       msg_type, hdr->identifier);
+		goto fail;
+	}
+
+	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,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "Received RADIUS packet matched with a pending "
+		       "request, round trip time %d.%02d sec",
+		       roundtrip / 100, roundtrip % 100);
+	rconf->round_trip_time = roundtrip;
+
+	/* Remove ACKed RADIUS packet from retransmit list */
+	if (prev_req)
+		prev_req->next = req->next;
+	else
+		radius->msgs = req->next;
+	radius->num_msgs--;
+
+	for (i = 0; i < num_handlers; i++) {
+		RadiusRxResult res;
+		res = handlers[i].handler(msg, req->msg, req->shared_secret,
+					  req->shared_secret_len,
+					  handlers[i].data);
+		switch (res) {
+		case RADIUS_RX_PROCESSED:
+			radius_msg_free(msg);
+			/* continue */
+		case RADIUS_RX_QUEUED:
+			radius_client_msg_free(req);
+			return;
+		case RADIUS_RX_INVALID_AUTHENTICATOR:
+			invalid_authenticator++;
+			/* continue */
+		case RADIUS_RX_UNKNOWN:
+			/* continue with next handler */
+			break;
+		}
+	}
+
+	if (invalid_authenticator)
+		rconf->bad_authenticators++;
+	else
+		rconf->unknown_types++;
+	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
+		       "(type=%d code=%d id=%d)%s - dropping packet",
+		       msg_type, hdr->code, hdr->identifier,
+		       invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
+		       "");
+	radius_client_msg_free(req);
+
+ fail:
+	radius_msg_free(msg);
+}
+
+
+/**
+ * radius_client_get_id - Get an identifier for a new RADIUS message
+ * @radius: RADIUS client context from radius_client_init()
+ * Returns: Allocated identifier
+ *
+ * This function is used to fetch a unique (among pending requests) identifier
+ * for a new RADIUS message.
+ */
+u8 radius_client_get_id(struct radius_client_data *radius)
+{
+	struct radius_msg_list *entry, *prev, *_remove;
+	u8 id = radius->next_radius_identifier++;
+
+	/* remove entries with matching id from retransmit list to avoid
+	 * using new reply from the RADIUS server with an old request */
+	entry = radius->msgs;
+	prev = NULL;
+	while (entry) {
+		if (radius_msg_get_hdr(entry->msg)->identifier == id) {
+			hostapd_logger(radius->ctx, entry->addr,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "Removing pending RADIUS message, "
+				       "since its id (%d) is reused", id);
+			if (prev)
+				prev->next = entry->next;
+			else
+				radius->msgs = entry->next;
+			_remove = entry;
+		} else {
+			_remove = NULL;
+			prev = entry;
+		}
+		entry = entry->next;
+
+		if (_remove)
+			radius_client_msg_free(_remove);
+	}
+
+	return id;
+}
+
+
+/**
+ * radius_client_flush - Flush all pending RADIUS client messages
+ * @radius: RADIUS client context from radius_client_init()
+ * @only_auth: Whether only authentication messages are removed
+ */
+void radius_client_flush(struct radius_client_data *radius, int only_auth)
+{
+	struct radius_msg_list *entry, *prev, *tmp;
+
+	if (!radius)
+		return;
+
+	prev = NULL;
+	entry = radius->msgs;
+
+	while (entry) {
+		if (!only_auth || entry->msg_type == RADIUS_AUTH) {
+			if (prev)
+				prev->next = entry->next;
+			else
+				radius->msgs = entry->next;
+
+			tmp = entry;
+			entry = entry->next;
+			radius_client_msg_free(tmp);
+			radius->num_msgs--;
+		} else {
+			prev = entry;
+			entry = entry->next;
+		}
+	}
+
+	if (radius->msgs == NULL)
+		eloop_cancel_timeout(radius_client_timer, radius, NULL);
+}
+
+
+static void radius_client_update_acct_msgs(struct radius_client_data *radius,
+					   const u8 *shared_secret,
+					   size_t shared_secret_len)
+{
+	struct radius_msg_list *entry;
+
+	if (!radius)
+		return;
+
+	for (entry = radius->msgs; entry; entry = entry->next) {
+		if (entry->msg_type == RADIUS_ACCT) {
+			entry->shared_secret = shared_secret;
+			entry->shared_secret_len = shared_secret_len;
+			radius_msg_finish_acct(entry->msg, shared_secret,
+					       shared_secret_len);
+		}
+	}
+}
+
+
+static int
+radius_change_server(struct radius_client_data *radius,
+		     struct hostapd_radius_server *nserv,
+		     struct hostapd_radius_server *oserv,
+		     int sock, int sock6, int auth)
+{
+	struct sockaddr_in serv, claddr;
+#ifdef CONFIG_IPV6
+	struct sockaddr_in6 serv6, claddr6;
+#endif /* CONFIG_IPV6 */
+	struct sockaddr *addr, *cl_addr;
+	socklen_t addrlen, claddrlen;
+	char abuf[50];
+	int sel_sock;
+	struct radius_msg_list *entry;
+	struct hostapd_radius_servers *conf = radius->conf;
+
+	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_INFO,
+		       "%s server %s:%d",
+		       auth ? "Authentication" : "Accounting",
+		       hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
+		       nserv->port);
+
+	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
+		 * since they would require more changes and the new RADIUS
+		 * server may not be prepared to receive them anyway due to
+		 * missing state information. Client will likely retry
+		 * authentication, so this should not be an issue. */
+		if (auth)
+			radius_client_flush(radius, 1);
+		else {
+			radius_client_update_acct_msgs(
+				radius, nserv->shared_secret,
+				nserv->shared_secret_len);
+		}
+	}
+
+	/* Reset retry counters for the new server */
+	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;
+		entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
+		entry->attempts = 0;
+		entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
+	}
+
+	if (radius->msgs) {
+		eloop_cancel_timeout(radius_client_timer, radius, NULL);
+		eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
+				       radius_client_timer, radius, NULL);
+	}
+
+	switch (nserv->addr.af) {
+	case AF_INET:
+		os_memset(&serv, 0, sizeof(serv));
+		serv.sin_family = AF_INET;
+		serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr;
+		serv.sin_port = htons(nserv->port);
+		addr = (struct sockaddr *) &serv;
+		addrlen = sizeof(serv);
+		sel_sock = sock;
+		break;
+#ifdef CONFIG_IPV6
+	case AF_INET6:
+		os_memset(&serv6, 0, sizeof(serv6));
+		serv6.sin6_family = AF_INET6;
+		os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6,
+			  sizeof(struct in6_addr));
+		serv6.sin6_port = htons(nserv->port);
+		addr = (struct sockaddr *) &serv6;
+		addrlen = sizeof(serv6);
+		sel_sock = sock6;
+		break;
+#endif /* CONFIG_IPV6 */
+	default:
+		return -1;
+	}
+
+	if (sel_sock < 0) {
+		wpa_printf(MSG_INFO,
+			   "RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d",
+			   nserv->addr.af, sock, sock6, auth);
+		return -1;
+	}
+
+	if (conf->force_client_addr) {
+		switch (conf->client_addr.af) {
+		case AF_INET:
+			os_memset(&claddr, 0, sizeof(claddr));
+			claddr.sin_family = AF_INET;
+			claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr;
+			claddr.sin_port = htons(0);
+			cl_addr = (struct sockaddr *) &claddr;
+			claddrlen = sizeof(claddr);
+			break;
+#ifdef CONFIG_IPV6
+		case AF_INET6:
+			os_memset(&claddr6, 0, sizeof(claddr6));
+			claddr6.sin6_family = AF_INET6;
+			os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6,
+				  sizeof(struct in6_addr));
+			claddr6.sin6_port = htons(0);
+			cl_addr = (struct sockaddr *) &claddr6;
+			claddrlen = sizeof(claddr6);
+			break;
+#endif /* CONFIG_IPV6 */
+		default:
+			return -1;
+		}
+
+		if (bind(sel_sock, cl_addr, claddrlen) < 0) {
+			wpa_printf(MSG_INFO, "bind[radius]: %s",
+				   strerror(errno));
+			return -1;
+		}
+	}
+
+	if (connect(sel_sock, addr, addrlen) < 0) {
+		wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
+		return -1;
+	}
+
+#ifndef CONFIG_NATIVE_WINDOWS
+	switch (nserv->addr.af) {
+	case AF_INET:
+		claddrlen = sizeof(claddr);
+		if (getsockname(sel_sock, (struct sockaddr *) &claddr,
+				&claddrlen) == 0) {
+			wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
+				   inet_ntoa(claddr.sin_addr),
+				   ntohs(claddr.sin_port));
+		}
+		break;
+#ifdef CONFIG_IPV6
+	case AF_INET6: {
+		claddrlen = sizeof(claddr6);
+		if (getsockname(sel_sock, (struct sockaddr *) &claddr6,
+				&claddrlen) == 0) {
+			wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
+				   inet_ntop(AF_INET6, &claddr6.sin6_addr,
+					     abuf, sizeof(abuf)),
+				   ntohs(claddr6.sin6_port));
+		}
+		break;
+	}
+#endif /* CONFIG_IPV6 */
+	}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	if (auth)
+		radius->auth_sock = sel_sock;
+	else
+		radius->acct_sock = sel_sock;
+
+	return 0;
+}
+
+
+static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct radius_client_data *radius = eloop_ctx;
+	struct hostapd_radius_servers *conf = radius->conf;
+	struct hostapd_radius_server *oserv;
+
+	if (radius->auth_sock >= 0 && conf->auth_servers &&
+	    conf->auth_server != conf->auth_servers) {
+		oserv = conf->auth_server;
+		conf->auth_server = conf->auth_servers;
+		if (radius_change_server(radius, conf->auth_server, oserv,
+					 radius->auth_serv_sock,
+					 radius->auth_serv_sock6, 1) < 0) {
+			conf->auth_server = oserv;
+			radius_change_server(radius, oserv, conf->auth_server,
+					     radius->auth_serv_sock,
+					     radius->auth_serv_sock6, 1);
+		}
+	}
+
+	if (radius->acct_sock >= 0 && conf->acct_servers &&
+	    conf->acct_server != conf->acct_servers) {
+		oserv = conf->acct_server;
+		conf->acct_server = conf->acct_servers;
+		if (radius_change_server(radius, conf->acct_server, oserv,
+					 radius->acct_serv_sock,
+					 radius->acct_serv_sock6, 0) < 0) {
+			conf->acct_server = oserv;
+			radius_change_server(radius, oserv, conf->acct_server,
+					     radius->acct_serv_sock,
+					     radius->acct_serv_sock6, 0);
+		}
+	}
+
+	if (conf->retry_primary_interval)
+		eloop_register_timeout(conf->retry_primary_interval, 0,
+				       radius_retry_primary_timer, radius,
+				       NULL);
+}
+
+
+static int radius_client_disable_pmtu_discovery(int s)
+{
+	int r = -1;
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+	/* Turn off Path MTU discovery on IPv4/UDP sockets. */
+	int action = IP_PMTUDISC_DONT;
+	r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
+		       sizeof(action));
+	if (r == -1)
+		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)
+		wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
+	else {
+		radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
+		ok++;
+	}
+
+#ifdef CONFIG_IPV6
+	radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
+	if (radius->auth_serv_sock6 < 0)
+		wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
+			   strerror(errno));
+	else
+		ok++;
+#endif /* CONFIG_IPV6 */
+
+	if (ok == 0)
+		return -1;
+
+	radius_change_server(radius, conf->auth_server, NULL,
+			     radius->auth_serv_sock, radius->auth_serv_sock6,
+			     1);
+
+	if (radius->auth_serv_sock >= 0 &&
+	    eloop_register_read_sock(radius->auth_serv_sock,
+				     radius_client_receive, radius,
+				     (void *) RADIUS_AUTH)) {
+		wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
+		radius_close_auth_sockets(radius);
+		return -1;
+	}
+
+#ifdef CONFIG_IPV6
+	if (radius->auth_serv_sock6 >= 0 &&
+	    eloop_register_read_sock(radius->auth_serv_sock6,
+				     radius_client_receive, radius,
+				     (void *) RADIUS_AUTH)) {
+		wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
+		radius_close_auth_sockets(radius);
+		return -1;
+	}
+#endif /* CONFIG_IPV6 */
+
+	return 0;
+}
+
+
+static int radius_client_init_acct(struct radius_client_data *radius)
+{
+	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)
+		wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
+	else {
+		radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
+		ok++;
+	}
+
+#ifdef CONFIG_IPV6
+	radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
+	if (radius->acct_serv_sock6 < 0)
+		wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
+			   strerror(errno));
+	else
+		ok++;
+#endif /* CONFIG_IPV6 */
+
+	if (ok == 0)
+		return -1;
+
+	radius_change_server(radius, conf->acct_server, NULL,
+			     radius->acct_serv_sock, radius->acct_serv_sock6,
+			     0);
+
+	if (radius->acct_serv_sock >= 0 &&
+	    eloop_register_read_sock(radius->acct_serv_sock,
+				     radius_client_receive, radius,
+				     (void *) RADIUS_ACCT)) {
+		wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
+		radius_close_acct_sockets(radius);
+		return -1;
+	}
+
+#ifdef CONFIG_IPV6
+	if (radius->acct_serv_sock6 >= 0 &&
+	    eloop_register_read_sock(radius->acct_serv_sock6,
+				     radius_client_receive, radius,
+				     (void *) RADIUS_ACCT)) {
+		wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
+		radius_close_acct_sockets(radius);
+		return -1;
+	}
+#endif /* CONFIG_IPV6 */
+
+	return 0;
+}
+
+
+/**
+ * radius_client_init - Initialize RADIUS client
+ * @ctx: Callback context to be used in hostapd_logger() calls
+ * @conf: RADIUS client configuration (RADIUS servers)
+ * Returns: Pointer to private RADIUS client context or %NULL on failure
+ *
+ * The caller is responsible for keeping the configuration data available for
+ * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is
+ * called for the returned context pointer.
+ */
+struct radius_client_data *
+radius_client_init(void *ctx, struct hostapd_radius_servers *conf)
+{
+	struct radius_client_data *radius;
+
+	radius = os_zalloc(sizeof(struct radius_client_data));
+	if (radius == NULL)
+		return NULL;
+
+	radius->ctx = ctx;
+	radius->conf = conf;
+	radius->auth_serv_sock = radius->acct_serv_sock =
+		radius->auth_serv_sock6 = radius->acct_serv_sock6 =
+		radius->auth_sock = radius->acct_sock = -1;
+
+	if (conf->auth_server && radius_client_init_auth(radius)) {
+		radius_client_deinit(radius);
+		return NULL;
+	}
+
+	if (conf->acct_server && radius_client_init_acct(radius)) {
+		radius_client_deinit(radius);
+		return NULL;
+	}
+
+	if (conf->retry_primary_interval)
+		eloop_register_timeout(conf->retry_primary_interval, 0,
+				       radius_retry_primary_timer, radius,
+				       NULL);
+
+	return radius;
+}
+
+
+/**
+ * radius_client_deinit - Deinitialize RADIUS client
+ * @radius: RADIUS client context from radius_client_init()
+ */
+void radius_client_deinit(struct radius_client_data *radius)
+{
+	if (!radius)
+		return;
+
+	radius_close_auth_sockets(radius);
+	radius_close_acct_sockets(radius);
+
+	eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
+
+	radius_client_flush(radius, 0);
+	os_free(radius->auth_handlers);
+	os_free(radius->acct_handlers);
+	os_free(radius);
+}
+
+
+/**
+ * radius_client_flush_auth - Flush pending RADIUS messages for an address
+ * @radius: RADIUS client context from radius_client_init()
+ * @addr: MAC address of the related device
+ *
+ * This function can be used to remove pending RADIUS authentication messages
+ * that are related to a specific device. The addr parameter is matched with
+ * the one used in radius_client_send() call that was used to transmit the
+ * authentication request.
+ */
+void radius_client_flush_auth(struct radius_client_data *radius,
+			      const u8 *addr)
+{
+	struct radius_msg_list *entry, *prev, *tmp;
+
+	prev = NULL;
+	entry = radius->msgs;
+	while (entry) {
+		if (entry->msg_type == RADIUS_AUTH &&
+		    os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
+			hostapd_logger(radius->ctx, addr,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "Removing pending RADIUS authentication"
+				       " message for removed client");
+
+			if (prev)
+				prev->next = entry->next;
+			else
+				radius->msgs = entry->next;
+
+			tmp = entry;
+			entry = entry->next;
+			radius_client_msg_free(tmp);
+			radius->num_msgs--;
+			continue;
+		}
+
+		prev = entry;
+		entry = entry->next;
+	}
+}
+
+
+static int radius_client_dump_auth_server(char *buf, size_t buflen,
+					  struct hostapd_radius_server *serv,
+					  struct radius_client_data *cli)
+{
+	int pending = 0;
+	struct radius_msg_list *msg;
+	char abuf[50];
+
+	if (cli) {
+		for (msg = cli->msgs; msg; msg = msg->next) {
+			if (msg->msg_type == RADIUS_AUTH)
+				pending++;
+		}
+	}
+
+	return os_snprintf(buf, buflen,
+			   "radiusAuthServerIndex=%d\n"
+			   "radiusAuthServerAddress=%s\n"
+			   "radiusAuthClientServerPortNumber=%d\n"
+			   "radiusAuthClientRoundTripTime=%d\n"
+			   "radiusAuthClientAccessRequests=%u\n"
+			   "radiusAuthClientAccessRetransmissions=%u\n"
+			   "radiusAuthClientAccessAccepts=%u\n"
+			   "radiusAuthClientAccessRejects=%u\n"
+			   "radiusAuthClientAccessChallenges=%u\n"
+			   "radiusAuthClientMalformedAccessResponses=%u\n"
+			   "radiusAuthClientBadAuthenticators=%u\n"
+			   "radiusAuthClientPendingRequests=%u\n"
+			   "radiusAuthClientTimeouts=%u\n"
+			   "radiusAuthClientUnknownTypes=%u\n"
+			   "radiusAuthClientPacketsDropped=%u\n",
+			   serv->index,
+			   hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
+			   serv->port,
+			   serv->round_trip_time,
+			   serv->requests,
+			   serv->retransmissions,
+			   serv->access_accepts,
+			   serv->access_rejects,
+			   serv->access_challenges,
+			   serv->malformed_responses,
+			   serv->bad_authenticators,
+			   pending,
+			   serv->timeouts,
+			   serv->unknown_types,
+			   serv->packets_dropped);
+}
+
+
+static int radius_client_dump_acct_server(char *buf, size_t buflen,
+					  struct hostapd_radius_server *serv,
+					  struct radius_client_data *cli)
+{
+	int pending = 0;
+	struct radius_msg_list *msg;
+	char abuf[50];
+
+	if (cli) {
+		for (msg = cli->msgs; msg; msg = msg->next) {
+			if (msg->msg_type == RADIUS_ACCT ||
+			    msg->msg_type == RADIUS_ACCT_INTERIM)
+				pending++;
+		}
+	}
+
+	return os_snprintf(buf, buflen,
+			   "radiusAccServerIndex=%d\n"
+			   "radiusAccServerAddress=%s\n"
+			   "radiusAccClientServerPortNumber=%d\n"
+			   "radiusAccClientRoundTripTime=%d\n"
+			   "radiusAccClientRequests=%u\n"
+			   "radiusAccClientRetransmissions=%u\n"
+			   "radiusAccClientResponses=%u\n"
+			   "radiusAccClientMalformedResponses=%u\n"
+			   "radiusAccClientBadAuthenticators=%u\n"
+			   "radiusAccClientPendingRequests=%u\n"
+			   "radiusAccClientTimeouts=%u\n"
+			   "radiusAccClientUnknownTypes=%u\n"
+			   "radiusAccClientPacketsDropped=%u\n",
+			   serv->index,
+			   hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
+			   serv->port,
+			   serv->round_trip_time,
+			   serv->requests,
+			   serv->retransmissions,
+			   serv->responses,
+			   serv->malformed_responses,
+			   serv->bad_authenticators,
+			   pending,
+			   serv->timeouts,
+			   serv->unknown_types,
+			   serv->packets_dropped);
+}
+
+
+/**
+ * radius_client_get_mib - Get RADIUS client MIB information
+ * @radius: RADIUS client context from radius_client_init()
+ * @buf: Buffer for returning MIB data in text format
+ * @buflen: Maximum buf length in octets
+ * Returns: Number of octets written into the buffer
+ */
+int radius_client_get_mib(struct radius_client_data *radius, char *buf,
+			  size_t buflen)
+{
+	struct hostapd_radius_servers *conf = radius->conf;
+	int i;
+	struct hostapd_radius_server *serv;
+	int count = 0;
+
+	if (conf->auth_servers) {
+		for (i = 0; i < conf->num_auth_servers; i++) {
+			serv = &conf->auth_servers[i];
+			count += radius_client_dump_auth_server(
+				buf + count, buflen - count, serv,
+				serv == conf->auth_server ?
+				radius : NULL);
+		}
+	}
+
+	if (conf->acct_servers) {
+		for (i = 0; i < conf->num_acct_servers; i++) {
+			serv = &conf->acct_servers[i];
+			count += radius_client_dump_acct_server(
+				buf + count, buflen - count, serv,
+				serv == conf->acct_server ?
+				radius : NULL);
+		}
+	}
+
+	return count;
+}
+
+
+void radius_client_reconfig(struct radius_client_data *radius,
+			    struct hostapd_radius_servers *conf)
+{
+	if (radius)
+		radius->conf = conf;
+}
diff --git a/hostap/src/radius/radius_client.h b/hostap/src/radius/radius_client.h
new file mode 100644
index 0000000..3db16aa
--- /dev/null
+++ b/hostap/src/radius/radius_client.h
@@ -0,0 +1,259 @@
+/*
+ * RADIUS client
+ * 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 RADIUS_CLIENT_H
+#define RADIUS_CLIENT_H
+
+#include "ip_addr.h"
+
+struct radius_msg;
+
+/**
+ * struct hostapd_radius_server - RADIUS server information for RADIUS client
+ *
+ * This structure contains information about a RADIUS server. The values are
+ * mainly for MIB information. The MIB variable prefix (radiusAuth or
+ * radiusAcc) depends on whether this is an authentication or accounting
+ * server.
+ *
+ * radiusAuthClientPendingRequests (or radiusAccClientPendingRequests) is the
+ * number struct radius_client_data::msgs for matching msg_type.
+ */
+struct hostapd_radius_server {
+	/**
+	 * addr - radiusAuthServerAddress or radiusAccServerAddress
+	 */
+	struct hostapd_ip_addr addr;
+
+	/**
+	 * port - radiusAuthClientServerPortNumber or radiusAccClientServerPortNumber
+	 */
+	int port;
+
+	/**
+	 * shared_secret - Shared secret for authenticating RADIUS messages
+	 */
+	u8 *shared_secret;
+
+	/**
+	 * shared_secret_len - Length of shared_secret in octets
+	 */
+	size_t shared_secret_len;
+
+	/* Dynamic (not from configuration file) MIB data */
+
+	/**
+	 * index - radiusAuthServerIndex or radiusAccServerIndex
+	 */
+	int index;
+
+	/**
+	 * round_trip_time - radiusAuthClientRoundTripTime or radiusAccClientRoundTripTime
+	 * Round-trip time in hundredths of a second.
+	 */
+	int round_trip_time;
+
+	/**
+	 * requests - radiusAuthClientAccessRequests or radiusAccClientRequests
+	 */
+	u32 requests;
+
+	/**
+	 * retransmissions - radiusAuthClientAccessRetransmissions or radiusAccClientRetransmissions
+	 */
+	u32 retransmissions;
+
+	/**
+	 * access_accepts - radiusAuthClientAccessAccepts
+	 */
+	u32 access_accepts;
+
+	/**
+	 * access_rejects - radiusAuthClientAccessRejects
+	 */
+	u32 access_rejects;
+
+	/**
+	 * access_challenges - radiusAuthClientAccessChallenges
+	 */
+	u32 access_challenges;
+
+	/**
+	 * responses - radiusAccClientResponses
+	 */
+	u32 responses;
+
+	/**
+	 * malformed_responses - radiusAuthClientMalformedAccessResponses or radiusAccClientMalformedResponses
+	 */
+	u32 malformed_responses;
+
+	/**
+	 * bad_authenticators - radiusAuthClientBadAuthenticators or radiusAccClientBadAuthenticators
+	 */
+	u32 bad_authenticators;
+
+	/**
+	 * timeouts - radiusAuthClientTimeouts or radiusAccClientTimeouts
+	 */
+	u32 timeouts;
+
+	/**
+	 * unknown_types - radiusAuthClientUnknownTypes or radiusAccClientUnknownTypes
+	 */
+	u32 unknown_types;
+
+	/**
+	 * packets_dropped - radiusAuthClientPacketsDropped or radiusAccClientPacketsDropped
+	 */
+	u32 packets_dropped;
+};
+
+/**
+ * struct hostapd_radius_servers - RADIUS servers for RADIUS client
+ */
+struct hostapd_radius_servers {
+	/**
+	 * auth_servers - RADIUS Authentication servers in priority order
+	 */
+	struct hostapd_radius_server *auth_servers;
+
+	/**
+	 * num_auth_servers - Number of auth_servers entries
+	 */
+	int num_auth_servers;
+
+	/**
+	 * auth_server - The current Authentication server
+	 */
+	struct hostapd_radius_server *auth_server;
+
+	/**
+	 * acct_servers - RADIUS Accounting servers in priority order
+	 */
+	struct hostapd_radius_server *acct_servers;
+
+	/**
+	 * num_acct_servers - Number of acct_servers entries
+	 */
+	int num_acct_servers;
+
+	/**
+	 * acct_server - The current Accounting server
+	 */
+	struct hostapd_radius_server *acct_server;
+
+	/**
+	 * retry_primary_interval - Retry interval for trying primary server
+	 *
+	 * This specifies a retry interval in sexconds for trying to return to
+	 * the primary RADIUS server. RADIUS client code will automatically try
+	 * to use the next server when the current server is not replying to
+	 * requests. If this interval is set (non-zero), the primary server
+	 * will be retried after the specified number of seconds has passed
+	 * even if the current used secondary server is still working.
+	 */
+	int retry_primary_interval;
+
+	/**
+	 * msg_dumps - Whether RADIUS message details are shown in stdout
+	 */
+	int msg_dumps;
+
+	/**
+	 * client_addr - Client (local) address to use if force_client_addr
+	 */
+	struct hostapd_ip_addr client_addr;
+
+	/**
+	 * force_client_addr - Whether to force client (local) address
+	 */
+	int force_client_addr;
+};
+
+
+/**
+ * RadiusType - RADIUS server type for RADIUS client
+ */
+typedef enum {
+	/**
+	 * RADIUS authentication
+	 */
+	RADIUS_AUTH,
+
+	/**
+	 * RADIUS_ACCT - RADIUS accounting
+	 */
+	RADIUS_ACCT,
+
+	/**
+	 * RADIUS_ACCT_INTERIM - RADIUS interim accounting message
+	 *
+	 * Used only with radius_client_send(). This behaves just like
+	 * RADIUS_ACCT, but removes any pending interim RADIUS Accounting
+	 * messages for the same STA before sending the new interim update.
+	 */
+	RADIUS_ACCT_INTERIM
+} RadiusType;
+
+/**
+ * RadiusRxResult - RADIUS client RX handler result
+ */
+typedef enum {
+	/**
+	 * RADIUS_RX_PROCESSED - Message processed
+	 *
+	 * This stops handler calls and frees the message.
+	 */
+	RADIUS_RX_PROCESSED,
+
+	/**
+	 * RADIUS_RX_QUEUED - Message has been queued
+	 *
+	 * This stops handler calls, but does not free the message; the handler
+	 * that returned this is responsible for eventually freeing the
+	 * message.
+	 */
+	RADIUS_RX_QUEUED,
+
+	/**
+	 * RADIUS_RX_UNKNOWN - Message is not for this handler
+	 */
+	RADIUS_RX_UNKNOWN,
+
+	/**
+	 * RADIUS_RX_INVALID_AUTHENTICATOR - Message has invalid Authenticator
+	 */
+	RADIUS_RX_INVALID_AUTHENTICATOR
+} RadiusRxResult;
+
+struct radius_client_data;
+
+int radius_client_register(struct radius_client_data *radius,
+			   RadiusType msg_type,
+			   RadiusRxResult (*handler)
+			   (struct radius_msg *msg, struct radius_msg *req,
+			    const u8 *shared_secret, size_t shared_secret_len,
+			    void *data),
+			   void *data);
+int radius_client_send(struct radius_client_data *radius,
+		       struct radius_msg *msg,
+		       RadiusType msg_type, const u8 *addr);
+u8 radius_client_get_id(struct radius_client_data *radius);
+void radius_client_flush(struct radius_client_data *radius, int only_auth);
+struct radius_client_data *
+radius_client_init(void *ctx, struct hostapd_radius_servers *conf);
+void radius_client_deinit(struct radius_client_data *radius);
+void radius_client_flush_auth(struct radius_client_data *radius,
+			      const u8 *addr);
+int radius_client_get_mib(struct radius_client_data *radius, char *buf,
+			  size_t buflen);
+void radius_client_reconfig(struct radius_client_data *radius,
+			    struct hostapd_radius_servers *conf);
+
+#endif /* RADIUS_CLIENT_H */
diff --git a/hostap/src/radius/radius_das.c b/hostap/src/radius/radius_das.c
new file mode 100644
index 0000000..b7d991b
--- /dev/null
+++ b/hostap/src/radius/radius_das.c
@@ -0,0 +1,410 @@
+/*
+ * RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
+ * 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.
+ */
+
+#include "includes.h"
+#include <net/if.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/ip_addr.h"
+#include "radius.h"
+#include "radius_das.h"
+
+
+struct radius_das_data {
+	int sock;
+	u8 *shared_secret;
+	size_t shared_secret_len;
+	struct hostapd_ip_addr client_addr;
+	unsigned int time_window;
+	int require_event_timestamp;
+	void *ctx;
+	enum radius_das_res (*disconnect)(void *ctx,
+					  struct radius_das_attrs *attr);
+};
+
+
+static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
+						 struct radius_msg *msg,
+						 const char *abuf,
+						 int from_port)
+{
+	struct radius_hdr *hdr;
+	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_ACCT_MULTI_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;
+	u8 attr;
+	enum radius_das_res res;
+	struct radius_das_attrs attrs;
+	u8 *buf;
+	size_t len;
+	char tmp[100];
+	u8 sta_addr[ETH_ALEN];
+
+	hdr = radius_msg_get_hdr(msg);
+
+	attr = radius_msg_find_unlisted_attr(msg, allowed);
+	if (attr) {
+		wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in "
+			   "Disconnect-Request from %s:%d", attr,
+			   abuf, from_port);
+		error = 401;
+		goto fail;
+	}
+
+	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))
+			len = sizeof(tmp) - 1;
+		os_memcpy(tmp, buf, len);
+		tmp[len] = '\0';
+		if (hwaddr_aton2(tmp, sta_addr) < 0) {
+			wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
+				   "'%s' from %s:%d", tmp, abuf, from_port);
+			error = 407;
+			goto fail;
+		}
+		attrs.sta_addr = sta_addr;
+	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
+				    &buf, &len, NULL) == 0) {
+		attrs.user_name = buf;
+		attrs.user_name_len = len;
+	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+				    &buf, &len, NULL) == 0) {
+		attrs.acct_session_id = buf;
+		attrs.acct_session_id_len = len;
+	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+				    &buf, &len, NULL) == 0) {
+		attrs.acct_multi_session_id = buf;
+		attrs.acct_multi_session_id_len = len;
+	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+				    &buf, &len, NULL) == 0) {
+		attrs.cui = buf;
+		attrs.cui_len = len;
+	}
+
+	res = das->disconnect(das->ctx, &attrs);
+	switch (res) {
+	case RADIUS_DAS_NAS_MISMATCH:
+		wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
+			   abuf, from_port);
+		error = 403;
+		break;
+	case RADIUS_DAS_SESSION_NOT_FOUND:
+		wpa_printf(MSG_INFO, "DAS: Session not found for request from "
+			   "%s:%d", abuf, from_port);
+		error = 503;
+		break;
+	case RADIUS_DAS_MULTI_SESSION_MATCH:
+		wpa_printf(MSG_INFO,
+			   "DAS: Multiple sessions match for request from %s:%d",
+			   abuf, from_port);
+		error = 508;
+		break;
+	case RADIUS_DAS_SUCCESS:
+		error = 0;
+		break;
+	}
+
+fail:
+	reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK :
+			       RADIUS_CODE_DISCONNECT_ACK, hdr->identifier);
+	if (reply == NULL)
+		return NULL;
+
+	if (error) {
+		if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
+					       error)) {
+			radius_msg_free(reply);
+			return NULL;
+		}
+	}
+
+	return reply;
+}
+
+
+static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct radius_das_data *das = eloop_ctx;
+	u8 buf[1500];
+	union {
+		struct sockaddr_storage ss;
+		struct sockaddr_in sin;
+#ifdef CONFIG_IPV6
+		struct sockaddr_in6 sin6;
+#endif /* CONFIG_IPV6 */
+	} from;
+	char abuf[50];
+	int from_port = 0;
+	socklen_t fromlen;
+	int len;
+	struct radius_msg *msg, *reply = NULL;
+	struct radius_hdr *hdr;
+	struct wpabuf *rbuf;
+	u32 val;
+	int res;
+	struct os_time now;
+
+	fromlen = sizeof(from);
+	len = recvfrom(sock, buf, sizeof(buf), 0,
+		       (struct sockaddr *) &from.ss, &fromlen);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
+		return;
+	}
+
+	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
+	from_port = ntohs(from.sin.sin_port);
+
+	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
+		   len, abuf, from_port);
+	if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
+		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
+		return;
+	}
+
+	msg = radius_msg_parse(buf, len);
+	if (msg == NULL) {
+		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
+			   "from %s:%d failed", abuf, from_port);
+		return;
+	}
+
+	if (wpa_debug_level <= MSG_MSGDUMP)
+		radius_msg_dump(msg);
+
+	if (radius_msg_verify_das_req(msg, das->shared_secret,
+				       das->shared_secret_len)) {
+		wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet "
+			   "from %s:%d - drop", abuf, from_port);
+		goto fail;
+	}
+
+	os_get_time(&now);
+	res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
+				  (u8 *) &val, 4);
+	if (res == 4) {
+		u32 timestamp = ntohl(val);
+		if ((unsigned int) abs((int) (now.sec - timestamp)) >
+		    das->time_window) {
+			wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
+				   "Event-Timestamp (%u; local time %u) in "
+				   "packet from %s:%d - drop",
+				   timestamp, (unsigned int) now.sec,
+				   abuf, from_port);
+			goto fail;
+		}
+	} else if (das->require_event_timestamp) {
+		wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet "
+			   "from %s:%d - drop", abuf, from_port);
+		goto fail;
+	}
+
+	hdr = radius_msg_get_hdr(msg);
+
+	switch (hdr->code) {
+	case RADIUS_CODE_DISCONNECT_REQUEST:
+		reply = radius_das_disconnect(das, msg, abuf, from_port);
+		break;
+	case RADIUS_CODE_COA_REQUEST:
+		/* TODO */
+		reply = radius_msg_new(RADIUS_CODE_COA_NAK,
+				       hdr->identifier);
+		if (reply == NULL)
+			break;
+
+		/* Unsupported Service */
+		if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
+					       405)) {
+			radius_msg_free(reply);
+			reply = NULL;
+			break;
+		}
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
+			   "packet from %s:%d",
+			   hdr->code, abuf, from_port);
+	}
+
+	if (reply) {
+		wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port);
+
+		if (!radius_msg_add_attr_int32(reply,
+					       RADIUS_ATTR_EVENT_TIMESTAMP,
+					       now.sec)) {
+			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
+				   "Event-Timestamp attribute");
+		}
+
+		if (radius_msg_finish_das_resp(reply, das->shared_secret,
+					       das->shared_secret_len, hdr) <
+		    0) {
+			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
+				   "Message-Authenticator attribute");
+		}
+
+		if (wpa_debug_level <= MSG_MSGDUMP)
+			radius_msg_dump(reply);
+
+		rbuf = radius_msg_get_buf(reply);
+		res = sendto(das->sock, wpabuf_head(rbuf),
+			     wpabuf_len(rbuf), 0,
+			     (struct sockaddr *) &from.ss, fromlen);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
+				   abuf, from_port, strerror(errno));
+		}
+	}
+
+fail:
+	radius_msg_free(msg);
+	radius_msg_free(reply);
+}
+
+
+static int radius_das_open_socket(int port)
+{
+	int s;
+	struct sockaddr_in addr;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno));
+		close(s);
+		return -1;
+	}
+
+	return s;
+}
+
+
+struct radius_das_data *
+radius_das_init(struct radius_das_conf *conf)
+{
+	struct radius_das_data *das;
+
+	if (conf->port == 0 || conf->shared_secret == NULL ||
+	    conf->client_addr == NULL)
+		return NULL;
+
+	das = os_zalloc(sizeof(*das));
+	if (das == NULL)
+		return NULL;
+
+	das->time_window = conf->time_window;
+	das->require_event_timestamp = conf->require_event_timestamp;
+	das->ctx = conf->ctx;
+	das->disconnect = conf->disconnect;
+
+	os_memcpy(&das->client_addr, conf->client_addr,
+		  sizeof(das->client_addr));
+
+	das->shared_secret = os_malloc(conf->shared_secret_len);
+	if (das->shared_secret == NULL) {
+		radius_das_deinit(das);
+		return NULL;
+	}
+	os_memcpy(das->shared_secret, conf->shared_secret,
+		  conf->shared_secret_len);
+	das->shared_secret_len = conf->shared_secret_len;
+
+	das->sock = radius_das_open_socket(conf->port);
+	if (das->sock < 0) {
+		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
+			   "DAS");
+		radius_das_deinit(das);
+		return NULL;
+	}
+
+	if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
+	{
+		radius_das_deinit(das);
+		return NULL;
+	}
+
+	return das;
+}
+
+
+void radius_das_deinit(struct radius_das_data *das)
+{
+	if (das == NULL)
+		return;
+
+	if (das->sock >= 0) {
+		eloop_unregister_read_sock(das->sock);
+		close(das->sock);
+	}
+
+	os_free(das->shared_secret);
+	os_free(das);
+}
diff --git a/hostap/src/radius/radius_das.h b/hostap/src/radius/radius_das.h
new file mode 100644
index 0000000..ce731d4
--- /dev/null
+++ b/hostap/src/radius/radius_das.h
@@ -0,0 +1,57 @@
+/*
+ * RADIUS Dynamic Authorization Server (DAS)
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RADIUS_DAS_H
+#define RADIUS_DAS_H
+
+struct radius_das_data;
+
+enum radius_das_res {
+	RADIUS_DAS_SUCCESS,
+	RADIUS_DAS_NAS_MISMATCH,
+	RADIUS_DAS_SESSION_NOT_FOUND,
+	RADIUS_DAS_MULTI_SESSION_MATCH,
+};
+
+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;
+	const u8 *acct_session_id;
+	size_t acct_session_id_len;
+	const u8 *acct_multi_session_id;
+	size_t acct_multi_session_id_len;
+	const u8 *cui;
+	size_t cui_len;
+};
+
+struct radius_das_conf {
+	int port;
+	const u8 *shared_secret;
+	size_t shared_secret_len;
+	const struct hostapd_ip_addr *client_addr;
+	unsigned int time_window;
+	int require_event_timestamp;
+	void *ctx;
+	enum radius_das_res (*disconnect)(void *ctx,
+					  struct radius_das_attrs *attr);
+};
+
+struct radius_das_data *
+radius_das_init(struct radius_das_conf *conf);
+
+void radius_das_deinit(struct radius_das_data *data);
+
+#endif /* RADIUS_DAS_H */
diff --git a/hostap/src/radius/radius_server.c b/hostap/src/radius/radius_server.c
new file mode 100644
index 0000000..744283c
--- /dev/null
+++ b/hostap/src/radius/radius_server.c
@@ -0,0 +1,2168 @@
+/*
+ * RADIUS authentication server
+ * 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.
+ */
+
+#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"
+
+/**
+ * RADIUS_SESSION_TIMEOUT - Session timeout in seconds
+ */
+#define RADIUS_SESSION_TIMEOUT 60
+
+/**
+ * RADIUS_MAX_SESSION - Maximum number of active sessions
+ */
+#define RADIUS_MAX_SESSION 100
+
+/**
+ * RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages
+ */
+#define RADIUS_MAX_MSG_LEN 3000
+
+static const struct eapol_callbacks radius_server_eapol_cb;
+
+struct radius_client;
+struct radius_server_data;
+
+/**
+ * struct radius_server_counters - RADIUS server statistics counters
+ */
+struct radius_server_counters {
+	u32 access_requests;
+	u32 invalid_requests;
+	u32 dup_access_requests;
+	u32 access_accepts;
+	u32 access_rejects;
+	u32 access_challenges;
+	u32 malformed_access_requests;
+	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;
+};
+
+/**
+ * struct radius_session - Internal RADIUS server data for a session
+ */
+struct radius_session {
+	struct radius_session *next;
+	struct radius_client *client;
+	struct radius_server_data *server;
+	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;
+	int last_from_port;
+	struct sockaddr_storage last_from;
+	socklen_t last_fromlen;
+	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;
+};
+
+/**
+ * struct radius_client - Internal RADIUS server data for a client
+ */
+struct radius_client {
+	struct radius_client *next;
+	struct in_addr addr;
+	struct in_addr mask;
+#ifdef CONFIG_IPV6
+	struct in6_addr addr6;
+	struct in6_addr mask6;
+#endif /* CONFIG_IPV6 */
+	char *shared_secret;
+	int shared_secret_len;
+	struct radius_session *sessions;
+	struct radius_server_counters counters;
+};
+
+/**
+ * struct radius_server_data - Internal RADIUS server data
+ */
+struct radius_server_data {
+	/**
+	 * auth_sock - Socket for RADIUS authentication messages
+	 */
+	int auth_sock;
+
+	/**
+	 * acct_sock - Socket for RADIUS accounting messages
+	 */
+	int acct_sock;
+
+	/**
+	 * clients - List of authorized RADIUS clients
+	 */
+	struct radius_client *clients;
+
+	/**
+	 * next_sess_id - Next session identifier
+	 */
+	unsigned int next_sess_id;
+
+	/**
+	 * conf_ctx - Context pointer for callbacks
+	 *
+	 * This is used as the ctx argument in get_eap_user() calls.
+	 */
+	void *conf_ctx;
+
+	/**
+	 * num_sess - Number of active sessions
+	 */
+	int num_sess;
+
+	/**
+	 * eap_sim_db_priv - EAP-SIM/AKA database context
+	 *
+	 * This is passed to the EAP-SIM/AKA server implementation as a
+	 * callback context.
+	 */
+	void *eap_sim_db_priv;
+
+	/**
+	 * ssl_ctx - TLS context
+	 *
+	 * This is passed to the EAP server implementation as a callback
+	 * context for TLS operations.
+	 */
+	void *ssl_ctx;
+
+	/**
+	 * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST
+	 *
+	 * This parameter is used to set a key for EAP-FAST to encrypt the
+	 * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If
+	 * set, must point to a 16-octet key.
+	 */
+	u8 *pac_opaque_encr_key;
+
+	/**
+	 * eap_fast_a_id - EAP-FAST authority identity (A-ID)
+	 *
+	 * If EAP-FAST is not used, this can be set to %NULL. In theory, this
+	 * is a variable length field, but due to some existing implementations
+	 * requiring A-ID to be 16 octets in length, it is recommended to use
+	 * that length for the field to provide interoperability with deployed
+	 * peer implementations.
+	 */
+	u8 *eap_fast_a_id;
+
+	/**
+	 * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets
+	 */
+	size_t eap_fast_a_id_len;
+
+	/**
+	 * eap_fast_a_id_info - EAP-FAST authority identifier information
+	 *
+	 * This A-ID-Info contains a user-friendly name for the A-ID. For
+	 * example, this could be the enterprise and server names in
+	 * human-readable format. This field is encoded as UTF-8. If EAP-FAST
+	 * is not used, this can be set to %NULL.
+	 */
+	char *eap_fast_a_id_info;
+
+	/**
+	 * eap_fast_prov - EAP-FAST provisioning modes
+	 *
+	 * 0 = provisioning disabled, 1 = only anonymous provisioning allowed,
+	 * 2 = only authenticated provisioning allowed, 3 = both provisioning
+	 * modes allowed.
+	 */
+	int eap_fast_prov;
+
+	/**
+	 * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds
+	 *
+	 * This is the hard limit on how long a provisioned PAC-Key can be
+	 * used.
+	 */
+	int pac_key_lifetime;
+
+	/**
+	 * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds
+	 *
+	 * This is a soft limit on the PAC-Key. The server will automatically
+	 * generate a new PAC-Key when this number of seconds (or fewer) of the
+	 * lifetime remains.
+	 */
+	int pac_key_refresh_time;
+
+	/**
+	 * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
+	 *
+	 * This controls whether the protected success/failure indication
+	 * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA.
+	 */
+	int eap_sim_aka_result_ind;
+
+	/**
+	 * tnc - Trusted Network Connect (TNC)
+	 *
+	 * This controls whether TNC is enabled and will be required before the
+	 * peer is allowed to connect. Note: This is only used with EAP-TTLS
+	 * and EAP-FAST. If any other EAP method is enabled, the peer will be
+	 * allowed to connect without TNC.
+	 */
+	int tnc;
+
+	/**
+	 * pwd_group - The D-H group assigned for EAP-pwd
+	 *
+	 * If EAP-pwd is not used it can be set to zero.
+	 */
+	u16 pwd_group;
+
+	/**
+	 * server_id - Server identity
+	 */
+	const char *server_id;
+
+	/**
+	 * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
+	 *
+	 * This controls whether the authentication server derives ERP key
+	 * hierarchy (rRK and rIK) from full EAP authentication and allows
+	 * these keys to be used to perform ERP to derive rMSK instead of full
+	 * EAP authentication to derive MSK.
+	 */
+	int erp;
+
+	const char *erp_domain;
+
+	struct dl_list erp_keys; /* struct eap_server_erp_key */
+
+	unsigned int tls_session_lifetime;
+
+	/**
+	 * wps - Wi-Fi Protected Setup context
+	 *
+	 * If WPS is used with an external RADIUS server (which is quite
+	 * unlikely configuration), this is used to provide a pointer to WPS
+	 * context data. Normally, this can be set to %NULL.
+	 */
+	struct wps_context *wps;
+
+	/**
+	 * ipv6 - Whether to enable IPv6 support in the RADIUS server
+	 */
+	int ipv6;
+
+	/**
+	 * start_time - Timestamp of server start
+	 */
+	struct os_reltime start_time;
+
+	/**
+	 * counters - Statistics counters for server operations
+	 *
+	 * These counters are the sum over all clients.
+	 */
+	struct radius_server_counters counters;
+
+	/**
+	 * get_eap_user - Callback for fetching EAP user information
+	 * @ctx: Context data from conf_ctx
+	 * @identity: User identity
+	 * @identity_len: identity buffer length in octets
+	 * @phase2: Whether this is for Phase 2 identity
+	 * @user: Data structure for filling in the user information
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is used to fetch information from user database. The callback
+	 * will fill in information about allowed EAP methods and the user
+	 * password. The password field will be an allocated copy of the
+	 * password data and RADIUS server will free it after use.
+	 */
+	int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
+			    int phase2, struct eap_user *user);
+
+	/**
+	 * eap_req_id_text - Optional data for EAP-Request/Identity
+	 *
+	 * This can be used to configure an optional, displayable message that
+	 * will be sent in EAP-Request/Identity. This string can contain an
+	 * ASCII-0 character (nul) to separate network infromation per RFC
+	 * 4284. The actual string length is explicit provided in
+	 * eap_req_id_text_len since nul character will not be used as a string
+	 * terminator.
+	 */
+	char *eap_req_id_text;
+
+	/**
+	 * eap_req_id_text_len - Length of eap_req_id_text buffer in octets
+	 */
+	size_t eap_req_id_text_len;
+
+	/*
+	 * msg_ctx - Context data for wpa_msg() calls
+	 */
+	void *msg_ctx;
+
+#ifdef CONFIG_RADIUS_TEST
+	char *dump_msk_file;
+#endif /* CONFIG_RADIUS_TEST */
+
+	char *subscr_remediation_url;
+	u8 subscr_remediation_method;
+
+#ifdef CONFIG_SQLITE
+	sqlite3 *db;
+#endif /* CONFIG_SQLITE */
+};
+
+
+#define RADIUS_DEBUG(args...) \
+wpa_printf(MSG_DEBUG, "RADIUS SRV: " args)
+#define RADIUS_ERROR(args...) \
+wpa_printf(MSG_ERROR, "RADIUS SRV: " args)
+#define RADIUS_DUMP(args...) \
+wpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args)
+#define RADIUS_DUMP_ASCII(args...) \
+wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args)
+
+
+static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx);
+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,
+			 int ipv6)
+{
+	struct radius_client *client = data->clients;
+
+	while (client) {
+#ifdef CONFIG_IPV6
+		if (ipv6) {
+			struct in6_addr *addr6;
+			int i;
+
+			addr6 = (struct in6_addr *) addr;
+			for (i = 0; i < 16; i++) {
+				if ((addr6->s6_addr[i] &
+				     client->mask6.s6_addr[i]) !=
+				    (client->addr6.s6_addr[i] &
+				     client->mask6.s6_addr[i])) {
+					i = 17;
+					break;
+				}
+			}
+			if (i == 16) {
+				break;
+			}
+		}
+#endif /* CONFIG_IPV6 */
+		if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) ==
+		    (addr->s_addr & client->mask.s_addr)) {
+			break;
+		}
+
+		client = client->next;
+	}
+
+	return client;
+}
+
+
+static struct radius_session *
+radius_server_get_session(struct radius_client *client, unsigned int sess_id)
+{
+	struct radius_session *sess = client->sessions;
+
+	while (sess) {
+		if (sess->sess_id == sess_id) {
+			break;
+		}
+		sess = sess->next;
+	}
+
+	return sess;
+}
+
+
+static void radius_server_session_free(struct radius_server_data *data,
+				       struct radius_session *sess)
+{
+	eloop_cancel_timeout(radius_server_session_timeout, data, sess);
+	eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess);
+	eap_server_sm_deinit(sess->eap);
+	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--;
+}
+
+
+static void radius_server_session_remove(struct radius_server_data *data,
+					 struct radius_session *sess)
+{
+	struct radius_client *client = sess->client;
+	struct radius_session *session, *prev;
+
+	eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess);
+
+	prev = NULL;
+	session = client->sessions;
+	while (session) {
+		if (session == sess) {
+			if (prev == NULL) {
+				client->sessions = sess->next;
+			} else {
+				prev->next = sess->next;
+			}
+			radius_server_session_free(data, sess);
+			break;
+		}
+		prev = session;
+		session = session->next;
+	}
+}
+
+
+static void radius_server_session_remove_timeout(void *eloop_ctx,
+						 void *timeout_ctx)
+{
+	struct radius_server_data *data = eloop_ctx;
+	struct radius_session *sess = timeout_ctx;
+	RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id);
+	radius_server_session_remove(data, sess);
+}
+
+
+static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct radius_server_data *data = eloop_ctx;
+	struct radius_session *sess = timeout_ctx;
+
+	RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id);
+	radius_server_session_remove(data, sess);
+}
+
+
+static struct radius_session *
+radius_server_new_session(struct radius_server_data *data,
+			  struct radius_client *client)
+{
+	struct radius_session *sess;
+
+	if (data->num_sess >= RADIUS_MAX_SESSION) {
+		RADIUS_DEBUG("Maximum number of existing session - no room "
+			     "for a new session");
+		return NULL;
+	}
+
+	sess = os_zalloc(sizeof(*sess));
+	if (sess == NULL)
+		return NULL;
+
+	sess->server = data;
+	sess->client = client;
+	sess->sess_id = data->next_sess_id++;
+	sess->next = client->sessions;
+	client->sessions = sess;
+	eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0,
+			       radius_server_session_timeout, data, sess);
+	data->num_sess++;
+	return sess;
+}
+
+
+#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, 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");
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &user,
+				    &user_len, NULL) < 0) {
+		RADIUS_DEBUG("Could not get User-Name");
+		return NULL;
+	}
+	RADIUS_DUMP_ASCII("User-Name", user, user_len);
+
+	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("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;
+	eap_conf.eap_sim_db_priv = data->eap_sim_db_priv;
+	eap_conf.backend_auth = TRUE;
+	eap_conf.eap_server = 1;
+	eap_conf.pac_opaque_encr_key = data->pac_opaque_encr_key;
+	eap_conf.eap_fast_a_id = data->eap_fast_a_id;
+	eap_conf.eap_fast_a_id_len = data->eap_fast_a_id_len;
+	eap_conf.eap_fast_a_id_info = data->eap_fast_a_id_info;
+	eap_conf.eap_fast_prov = data->eap_fast_prov;
+	eap_conf.pac_key_lifetime = data->pac_key_lifetime;
+	eap_conf.pac_key_refresh_time = data->pac_key_refresh_time;
+	eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind;
+	eap_conf.tnc = data->tnc;
+	eap_conf.wps = data->wps;
+	eap_conf.pwd_group = data->pwd_group;
+	eap_conf.server_id = (const u8 *) data->server_id;
+	eap_conf.server_id_len = os_strlen(data->server_id);
+	eap_conf.erp = data->erp;
+	eap_conf.tls_session_lifetime = data->tls_session_lifetime;
+	radius_server_testing_options(sess, &eap_conf);
+	sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
+				       &eap_conf);
+	if (sess->eap == NULL) {
+		RADIUS_DEBUG("Failed to initialize EAP state machine for the "
+			     "new session");
+		radius_server_session_free(data, sess);
+		return NULL;
+	}
+	sess->eap_if = eap_get_interface(sess->eap);
+	sess->eap_if->eapRestart = TRUE;
+	sess->eap_if->portEnabled = TRUE;
+
+	RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id);
+
+	return sess;
+}
+
+
+static struct radius_msg *
+radius_server_encapsulate_eap(struct radius_server_data *data,
+			      struct radius_client *client,
+			      struct radius_session *sess,
+			      struct radius_msg *request)
+{
+	struct radius_msg *msg;
+	int code;
+	unsigned int sess_id;
+	struct radius_hdr *hdr = radius_msg_get_hdr(request);
+
+	if (sess->eap_if->eapFail) {
+		sess->eap_if->eapFail = FALSE;
+		code = RADIUS_CODE_ACCESS_REJECT;
+	} else if (sess->eap_if->eapSuccess) {
+		sess->eap_if->eapSuccess = FALSE;
+		code = RADIUS_CODE_ACCESS_ACCEPT;
+	} else {
+		sess->eap_if->eapReq = FALSE;
+		code = RADIUS_CODE_ACCESS_CHALLENGE;
+	}
+
+	msg = radius_msg_new(code, hdr->identifier);
+	if (msg == NULL) {
+		RADIUS_DEBUG("Failed to allocate reply message");
+		return NULL;
+	}
+
+	sess_id = htonl(sess->sess_id);
+	if (code == RADIUS_CODE_ACCESS_CHALLENGE &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_STATE,
+				 (u8 *) &sess_id, sizeof(sess_id))) {
+		RADIUS_DEBUG("Failed to add State attribute");
+	}
+
+	if (sess->eap_if->eapReqData &&
+	    !radius_msg_add_eap(msg, wpabuf_head(sess->eap_if->eapReqData),
+				wpabuf_len(sess->eap_if->eapReqData))) {
+		RADIUS_DEBUG("Failed to add EAP-Message attribute");
+	}
+
+	if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) {
+		int len;
+#ifdef CONFIG_RADIUS_TEST
+		if (data->dump_msk_file) {
+			FILE *f;
+			char buf[2 * 64 + 1];
+			f = fopen(data->dump_msk_file, "a");
+			if (f) {
+				len = sess->eap_if->eapKeyDataLen;
+				if (len > 64)
+					len = 64;
+				len = wpa_snprintf_hex(
+					buf, sizeof(buf),
+					sess->eap_if->eapKeyData, len);
+				buf[len] = '\0';
+				fprintf(f, "%s\n", buf);
+				fclose(f);
+			}
+		}
+#endif /* CONFIG_RADIUS_TEST */
+		if (sess->eap_if->eapKeyDataLen > 64) {
+			len = 32;
+		} else {
+			len = sess->eap_if->eapKeyDataLen / 2;
+		}
+		if (!radius_msg_add_mppe_keys(msg, hdr->authenticator,
+					      (u8 *) client->shared_secret,
+					      client->shared_secret_len,
+					      sess->eap_if->eapKeyData + len,
+					      len, sess->eap_if->eapKeyData,
+					      len)) {
+			RADIUS_DEBUG("Failed to add MPPE key attributes");
+		}
+	}
+
+#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) {
+		RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
+	}
+
+	return msg;
+}
+
+
+static int radius_server_reject(struct radius_server_data *data,
+				struct radius_client *client,
+				struct radius_msg *request,
+				struct sockaddr *from, socklen_t fromlen,
+				const char *from_addr, int from_port)
+{
+	struct radius_msg *msg;
+	int ret = 0;
+	struct eap_hdr eapfail;
+	struct wpabuf *buf;
+	struct radius_hdr *hdr = radius_msg_get_hdr(request);
+
+	RADIUS_DEBUG("Reject invalid request from %s:%d",
+		     from_addr, from_port);
+
+	msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, hdr->identifier);
+	if (msg == NULL) {
+		return -1;
+	}
+
+	os_memset(&eapfail, 0, sizeof(eapfail));
+	eapfail.code = EAP_CODE_FAILURE;
+	eapfail.identifier = 0;
+	eapfail.length = host_to_be16(sizeof(eapfail));
+
+	if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) {
+		RADIUS_DEBUG("Failed to add EAP-Message attribute");
+	}
+
+	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 -1;
+	}
+
+	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");
+	}
+
+	if (wpa_debug_level <= MSG_MSGDUMP) {
+		radius_msg_dump(msg);
+	}
+
+	data->counters.access_rejects++;
+	client->counters.access_rejects++;
+	buf = radius_msg_get_buf(msg);
+	if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
+		   (struct sockaddr *) from, sizeof(*from)) < 0) {
+		wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno));
+		ret = -1;
+	}
+
+	radius_msg_free(msg);
+
+	return ret;
+}
+
+
+static int radius_server_request(struct radius_server_data *data,
+				 struct radius_msg *msg,
+				 struct sockaddr *from, socklen_t fromlen,
+				 struct radius_client *client,
+				 const char *from_addr, int from_port,
+				 struct radius_session *force_sess)
+{
+	struct wpabuf *eap = NULL;
+	int res, state_included = 0;
+	u8 statebuf[4];
+	unsigned int state;
+	struct radius_session *sess;
+	struct radius_msg *reply;
+	int is_complete = 0;
+
+	if (force_sess)
+		sess = force_sess;
+	else {
+		res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf,
+					  sizeof(statebuf));
+		state_included = res >= 0;
+		if (res == sizeof(statebuf)) {
+			state = WPA_GET_BE32(statebuf);
+			sess = radius_server_get_session(client, state);
+		} else {
+			sess = NULL;
+		}
+	}
+
+	if (sess) {
+		RADIUS_DEBUG("Request for session 0x%x", sess->sess_id);
+	} else if (state_included) {
+		RADIUS_DEBUG("State attribute included but no session found");
+		radius_server_reject(data, client, msg, from, fromlen,
+				     from_addr, from_port);
+		return -1;
+	} else {
+		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,
+					     from_addr, from_port);
+			return -1;
+		}
+	}
+
+	if (sess->last_from_port == from_port &&
+	    sess->last_identifier == radius_msg_get_hdr(msg)->identifier &&
+	    os_memcmp(sess->last_authenticator,
+		      radius_msg_get_hdr(msg)->authenticator, 16) == 0) {
+		RADIUS_DEBUG("Duplicate message from %s", from_addr);
+		data->counters.dup_access_requests++;
+		client->counters.dup_access_requests++;
+
+		if (sess->last_reply) {
+			struct wpabuf *buf;
+			buf = radius_msg_get_buf(sess->last_reply);
+			res = sendto(data->auth_sock, wpabuf_head(buf),
+				     wpabuf_len(buf), 0,
+				     (struct sockaddr *) from, fromlen);
+			if (res < 0) {
+				wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
+					   strerror(errno));
+			}
+			return 0;
+		}
+
+		RADIUS_DEBUG("No previous reply available for duplicate "
+			     "message");
+		return -1;
+	}
+		      
+	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);
+		data->counters.packets_dropped++;
+		client->counters.packets_dropped++;
+		return -1;
+	}
+
+	RADIUS_DUMP("Received EAP data", wpabuf_head(eap), wpabuf_len(eap));
+
+	/* FIX: if Code is Request, Success, or Failure, send Access-Reject;
+	 * RFC3579 Sect. 2.6.2.
+	 * Include EAP-Response/Nak with no preferred method if
+	 * code == request.
+	 * If code is not 1-4, discard the packet silently.
+	 * Or is this already done by the EAP state machine? */
+
+	wpabuf_free(sess->eap_if->eapRespData);
+	sess->eap_if->eapRespData = eap;
+	sess->eap_if->eapResp = TRUE;
+	eap_server_sm_step(sess->eap);
+
+	if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess ||
+	     sess->eap_if->eapFail) && sess->eap_if->eapReqData) {
+		RADIUS_DUMP("EAP data from the state machine",
+			    wpabuf_head(sess->eap_if->eapReqData),
+			    wpabuf_len(sess->eap_if->eapReqData));
+	} else if (sess->eap_if->eapFail) {
+		RADIUS_DEBUG("No EAP data from the state machine, but eapFail "
+			     "set");
+	} else if (eap_sm_method_pending(sess->eap)) {
+		radius_msg_free(sess->last_msg);
+		sess->last_msg = msg;
+		sess->last_from_port = from_port;
+		os_free(sess->last_from_addr);
+		sess->last_from_addr = os_strdup(from_addr);
+		sess->last_fromlen = fromlen;
+		os_memcpy(&sess->last_from, from, fromlen);
+		return -2;
+	} else {
+		RADIUS_DEBUG("No EAP data from the state machine - ignore this"
+			     " Access-Request silently (assuming it was a "
+			     "duplicate)");
+		data->counters.packets_dropped++;
+		client->counters.packets_dropped++;
+		return -1;
+	}
+
+	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;
+
+		RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port);
+		if (wpa_debug_level <= MSG_MSGDUMP) {
+			radius_msg_dump(reply);
+		}
+
+		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;
+		case RADIUS_CODE_ACCESS_CHALLENGE:
+			data->counters.access_challenges++;
+			client->counters.access_challenges++;
+			break;
+		}
+		buf = radius_msg_get_buf(reply);
+		res = sendto(data->auth_sock, wpabuf_head(buf),
+			     wpabuf_len(buf), 0,
+			     (struct sockaddr *) from, fromlen);
+		if (res < 0) {
+			wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
+				   strerror(errno));
+		}
+		radius_msg_free(sess->last_reply);
+		sess->last_reply = reply;
+		sess->last_from_port = from_port;
+		hdr = radius_msg_get_hdr(msg);
+		sess->last_identifier = hdr->identifier;
+		os_memcpy(sess->last_authenticator, hdr->authenticator, 16);
+	} else {
+		data->counters.packets_dropped++;
+		client->counters.packets_dropped++;
+	}
+
+	if (is_complete) {
+		RADIUS_DEBUG("Removing completed session 0x%x after timeout",
+			     sess->sess_id);
+		eloop_cancel_timeout(radius_server_session_remove_timeout,
+				     data, sess);
+		eloop_register_timeout(10, 0,
+				       radius_server_session_remove_timeout,
+				       data, sess);
+	}
+
+	return 0;
+}
+
+
+static void radius_server_receive_auth(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;
+	struct radius_client *client = NULL;
+	struct radius_msg *msg = NULL;
+	char abuf[50];
+	int from_port = 0;
+
+	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_requests++;
+		goto fail;
+	}
+
+	msg = radius_msg_parse(buf, len);
+	if (msg == NULL) {
+		RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
+		data->counters.malformed_access_requests++;
+		client->counters.malformed_access_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_ACCESS_REQUEST) {
+		RADIUS_DEBUG("Unexpected RADIUS code %d",
+			     radius_msg_get_hdr(msg)->code);
+		data->counters.unknown_types++;
+		client->counters.unknown_types++;
+		goto fail;
+	}
+
+	data->counters.access_requests++;
+	client->counters.access_requests++;
+
+	if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret,
+				       client->shared_secret_len, NULL)) {
+		RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf);
+		data->counters.bad_authenticators++;
+		client->counters.bad_authenticators++;
+		goto fail;
+	}
+
+	if (radius_server_request(data, msg, (struct sockaddr *) &from,
+				  fromlen, client, abuf, from_port, NULL) ==
+	    -2)
+		return; /* msg was stored with the session */
+
+fail:
+	radius_msg_free(msg);
+	os_free(buf);
+}
+
+
+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;
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+	/* Turn off Path MTU discovery on IPv4/UDP sockets. */
+	int action = IP_PMTUDISC_DONT;
+	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));
+#endif
+	return r;
+}
+
+
+static int radius_server_open_socket(int port)
+{
+	int s;
+	struct sockaddr_in addr;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: socket: %s", strerror(errno));
+		return -1;
+	}
+
+	radius_server_disable_pmtu_discovery(s);
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
+		close(s);
+		return -1;
+	}
+
+	return s;
+}
+
+
+#ifdef CONFIG_IPV6
+static int radius_server_open_socket6(int port)
+{
+	int s;
+	struct sockaddr_in6 addr;
+
+	s = socket(PF_INET6, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: socket[IPv6]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sin6_family = AF_INET6;
+	os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
+	addr.sin6_port = htons(port);
+	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
+		close(s);
+		return -1;
+	}
+
+	return s;
+}
+#endif /* CONFIG_IPV6 */
+
+
+static void radius_server_free_sessions(struct radius_server_data *data,
+					struct radius_session *sessions)
+{
+	struct radius_session *session, *prev;
+
+	session = sessions;
+	while (session) {
+		prev = session;
+		session = session->next;
+		radius_server_session_free(data, prev);
+	}
+}
+
+
+static void radius_server_free_clients(struct radius_server_data *data,
+				       struct radius_client *clients)
+{
+	struct radius_client *client, *prev;
+
+	client = clients;
+	while (client) {
+		prev = client;
+		client = client->next;
+
+		radius_server_free_sessions(data, prev->sessions);
+		os_free(prev->shared_secret);
+		os_free(prev);
+	}
+}
+
+
+static struct radius_client *
+radius_server_read_clients(const char *client_file, int ipv6)
+{
+	FILE *f;
+	const int buf_size = 1024;
+	char *buf, *pos;
+	struct radius_client *clients, *tail, *entry;
+	int line = 0, mask, failed = 0, i;
+	struct in_addr addr;
+#ifdef CONFIG_IPV6
+	struct in6_addr addr6;
+#endif /* CONFIG_IPV6 */
+	unsigned int val;
+
+	f = fopen(client_file, "r");
+	if (f == NULL) {
+		RADIUS_ERROR("Could not open client file '%s'", client_file);
+		return NULL;
+	}
+
+	buf = os_malloc(buf_size);
+	if (buf == NULL) {
+		fclose(f);
+		return NULL;
+	}
+
+	clients = tail = NULL;
+	while (fgets(buf, buf_size, f)) {
+		/* Configuration file format:
+		 * 192.168.1.0/24 secret
+		 * 192.168.1.2 secret
+		 * fe80::211:22ff:fe33:4455/64 secretipv6
+		 */
+		line++;
+		buf[buf_size - 1] = '\0';
+		pos = buf;
+		while (*pos != '\0' && *pos != '\n')
+			pos++;
+		if (*pos == '\n')
+			*pos = '\0';
+		if (*buf == '\0' || *buf == '#')
+			continue;
+
+		pos = buf;
+		while ((*pos >= '0' && *pos <= '9') || *pos == '.' ||
+		       (*pos >= 'a' && *pos <= 'f') || *pos == ':' ||
+		       (*pos >= 'A' && *pos <= 'F')) {
+			pos++;
+		}
+
+		if (*pos == '\0') {
+			failed = 1;
+			break;
+		}
+
+		if (*pos == '/') {
+			char *end;
+			*pos++ = '\0';
+			mask = strtol(pos, &end, 10);
+			if ((pos == end) ||
+			    (mask < 0 || mask > (ipv6 ? 128 : 32))) {
+				failed = 1;
+				break;
+			}
+			pos = end;
+		} else {
+			mask = ipv6 ? 128 : 32;
+			*pos++ = '\0';
+		}
+
+		if (!ipv6 && inet_aton(buf, &addr) == 0) {
+			failed = 1;
+			break;
+		}
+#ifdef CONFIG_IPV6
+		if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) {
+			if (inet_pton(AF_INET, buf, &addr) <= 0) {
+				failed = 1;
+				break;
+			}
+			/* Convert IPv4 address to IPv6 */
+			if (mask <= 32)
+				mask += (128 - 32);
+			os_memset(addr6.s6_addr, 0, 10);
+			addr6.s6_addr[10] = 0xff;
+			addr6.s6_addr[11] = 0xff;
+			os_memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr,
+				  4);
+		}
+#endif /* CONFIG_IPV6 */
+
+		while (*pos == ' ' || *pos == '\t') {
+			pos++;
+		}
+
+		if (*pos == '\0') {
+			failed = 1;
+			break;
+		}
+
+		entry = os_zalloc(sizeof(*entry));
+		if (entry == NULL) {
+			failed = 1;
+			break;
+		}
+		entry->shared_secret = os_strdup(pos);
+		if (entry->shared_secret == NULL) {
+			failed = 1;
+			os_free(entry);
+			break;
+		}
+		entry->shared_secret_len = os_strlen(entry->shared_secret);
+		if (!ipv6) {
+			entry->addr.s_addr = addr.s_addr;
+			val = 0;
+			for (i = 0; i < mask; i++)
+				val |= 1 << (31 - i);
+			entry->mask.s_addr = htonl(val);
+		}
+#ifdef CONFIG_IPV6
+		if (ipv6) {
+			int offset = mask / 8;
+
+			os_memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16);
+			os_memset(entry->mask6.s6_addr, 0xff, offset);
+			val = 0;
+			for (i = 0; i < (mask % 8); i++)
+				val |= 1 << (7 - i);
+			if (offset < 16)
+				entry->mask6.s6_addr[offset] = val;
+		}
+#endif /* CONFIG_IPV6 */
+
+		if (tail == NULL) {
+			clients = tail = entry;
+		} else {
+			tail->next = entry;
+			tail = entry;
+		}
+	}
+
+	if (failed) {
+		RADIUS_ERROR("Invalid line %d in '%s'", line, client_file);
+		radius_server_free_clients(NULL, clients);
+		clients = NULL;
+	}
+
+	os_free(buf);
+	fclose(f);
+
+	return clients;
+}
+
+
+/**
+ * radius_server_init - Initialize RADIUS server
+ * @conf: Configuration for the RADIUS server
+ * Returns: Pointer to private RADIUS server context or %NULL on failure
+ *
+ * This initializes a RADIUS server instance and returns a context pointer that
+ * will be used in other calls to the RADIUS server module. The server can be
+ * deinitialize by calling radius_server_deinit().
+ */
+struct radius_server_data *
+radius_server_init(struct radius_server_conf *conf)
+{
+	struct radius_server_data *data;
+
+#ifndef CONFIG_IPV6
+	if (conf->ipv6) {
+		wpa_printf(MSG_ERROR, "RADIUS server compiled without IPv6 support");
+		return NULL;
+	}
+#endif /* CONFIG_IPV6 */
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	dl_list_init(&data->erp_keys);
+	os_get_reltime(&data->start_time);
+	data->conf_ctx = conf->conf_ctx;
+	data->eap_sim_db_priv = conf->eap_sim_db_priv;
+	data->ssl_ctx = conf->ssl_ctx;
+	data->msg_ctx = conf->msg_ctx;
+	data->ipv6 = conf->ipv6;
+	if (conf->pac_opaque_encr_key) {
+		data->pac_opaque_encr_key = os_malloc(16);
+		if (data->pac_opaque_encr_key) {
+			os_memcpy(data->pac_opaque_encr_key,
+				  conf->pac_opaque_encr_key, 16);
+		}
+	}
+	if (conf->eap_fast_a_id) {
+		data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
+		if (data->eap_fast_a_id) {
+			os_memcpy(data->eap_fast_a_id, conf->eap_fast_a_id,
+				  conf->eap_fast_a_id_len);
+			data->eap_fast_a_id_len = conf->eap_fast_a_id_len;
+		}
+	}
+	if (conf->eap_fast_a_id_info)
+		data->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
+	data->eap_fast_prov = conf->eap_fast_prov;
+	data->pac_key_lifetime = conf->pac_key_lifetime;
+	data->pac_key_refresh_time = conf->pac_key_refresh_time;
+	data->get_eap_user = conf->get_eap_user;
+	data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
+	data->tnc = conf->tnc;
+	data->wps = conf->wps;
+	data->pwd_group = conf->pwd_group;
+	data->server_id = conf->server_id;
+	if (conf->eap_req_id_text) {
+		data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len);
+		if (data->eap_req_id_text) {
+			os_memcpy(data->eap_req_id_text, conf->eap_req_id_text,
+				  conf->eap_req_id_text_len);
+			data->eap_req_id_text_len = conf->eap_req_id_text_len;
+		}
+	}
+	data->erp = conf->erp;
+	data->erp_domain = conf->erp_domain;
+	data->tls_session_lifetime = conf->tls_session_lifetime;
+
+	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);
+#endif /* CONFIG_RADIUS_TEST */
+
+	data->clients = radius_server_read_clients(conf->client_file,
+						   conf->ipv6);
+	if (data->clients == NULL) {
+		wpa_printf(MSG_ERROR, "No RADIUS clients configured");
+		radius_server_deinit(data);
+		return NULL;
+	}
+
+#ifdef CONFIG_IPV6
+	if (conf->ipv6)
+		data->auth_sock = radius_server_open_socket6(conf->auth_port);
+	else
+#endif /* CONFIG_IPV6 */
+	data->auth_sock = radius_server_open_socket(conf->auth_port);
+	if (data->auth_sock < 0) {
+		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server");
+		radius_server_deinit(data);
+		return NULL;
+	}
+	if (eloop_register_read_sock(data->auth_sock,
+				     radius_server_receive_auth,
+				     data, NULL)) {
+		radius_server_deinit(data);
+		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;
+}
+
+
+/**
+ * radius_server_erp_flush - Flush all ERP keys
+ * @data: RADIUS server context from radius_server_init()
+ */
+void radius_server_erp_flush(struct radius_server_data *data)
+{
+	struct eap_server_erp_key *erp;
+
+	if (data == NULL)
+		return;
+	while ((erp = dl_list_first(&data->erp_keys, struct eap_server_erp_key,
+				    list)) != NULL) {
+		dl_list_del(&erp->list);
+		bin_clear_free(erp, sizeof(*erp));
+	}
+}
+
+
+/**
+ * radius_server_deinit - Deinitialize RADIUS server
+ * @data: RADIUS server context from radius_server_init()
+ */
+void radius_server_deinit(struct radius_server_data *data)
+{
+	if (data == NULL)
+		return;
+
+	if (data->auth_sock >= 0) {
+		eloop_unregister_read_sock(data->auth_sock);
+		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);
+	os_free(data->eap_fast_a_id);
+	os_free(data->eap_fast_a_id_info);
+	os_free(data->eap_req_id_text);
+#ifdef CONFIG_RADIUS_TEST
+	os_free(data->dump_msk_file);
+#endif /* CONFIG_RADIUS_TEST */
+	os_free(data->subscr_remediation_url);
+
+#ifdef CONFIG_SQLITE
+	if (data->db)
+		sqlite3_close(data->db);
+#endif /* CONFIG_SQLITE */
+
+	radius_server_erp_flush(data);
+
+	os_free(data);
+}
+
+
+/**
+ * radius_server_get_mib - Get RADIUS server MIB information
+ * @data: RADIUS server context from radius_server_init()
+ * @buf: Buffer for returning the MIB data in text format
+ * @buflen: buf length in octets
+ * Returns: Number of octets written into buf
+ */
+int radius_server_get_mib(struct radius_server_data *data, char *buf,
+			  size_t buflen)
+{
+	int ret, uptime;
+	unsigned int idx;
+	char *end, *pos;
+	struct os_reltime now;
+	struct radius_client *cli;
+
+	/* RFC 2619 - RADIUS Authentication Server MIB */
+
+	if (data == NULL || buflen == 0)
+		return 0;
+
+	pos = buf;
+	end = buf + buflen;
+
+	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,
+			  "RADIUS-AUTH-SERVER-MIB\n"
+			  "radiusAuthServIdent=hostapd\n"
+			  "radiusAuthServUpTime=%d\n"
+			  "radiusAuthServResetTime=0\n"
+			  "radiusAuthServConfigReset=4\n",
+			  uptime);
+	if (os_snprintf_error(end - pos, ret)) {
+		*pos = '\0';
+		return pos - buf;
+	}
+	pos += ret;
+
+	ret = os_snprintf(pos, end - pos,
+			  "radiusAuthServTotalAccessRequests=%u\n"
+			  "radiusAuthServTotalInvalidRequests=%u\n"
+			  "radiusAuthServTotalDupAccessRequests=%u\n"
+			  "radiusAuthServTotalAccessAccepts=%u\n"
+			  "radiusAuthServTotalAccessRejects=%u\n"
+			  "radiusAuthServTotalAccessChallenges=%u\n"
+			  "radiusAuthServTotalMalformedAccessRequests=%u\n"
+			  "radiusAuthServTotalBadAuthenticators=%u\n"
+			  "radiusAuthServTotalPacketsDropped=%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,
+			  data->counters.access_accepts,
+			  data->counters.access_rejects,
+			  data->counters.access_challenges,
+			  data->counters.malformed_access_requests,
+			  data->counters.bad_authenticators,
+			  data->counters.packets_dropped,
+			  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 (os_snprintf_error(end - pos, ret)) {
+		*pos = '\0';
+		return pos - buf;
+	}
+	pos += ret;
+
+	for (cli = data->clients, idx = 0; cli; cli = cli->next, idx++) {
+		char abuf[50], mbuf[50];
+#ifdef CONFIG_IPV6
+		if (data->ipv6) {
+			if (inet_ntop(AF_INET6, &cli->addr6, abuf,
+				      sizeof(abuf)) == NULL)
+				abuf[0] = '\0';
+			if (inet_ntop(AF_INET6, &cli->mask6, mbuf,
+				      sizeof(mbuf)) == NULL)
+				mbuf[0] = '\0';
+		}
+#endif /* CONFIG_IPV6 */
+		if (!data->ipv6) {
+			os_strlcpy(abuf, inet_ntoa(cli->addr), sizeof(abuf));
+			os_strlcpy(mbuf, inet_ntoa(cli->mask), sizeof(mbuf));
+		}
+
+		ret = os_snprintf(pos, end - pos,
+				  "radiusAuthClientIndex=%u\n"
+				  "radiusAuthClientAddress=%s/%s\n"
+				  "radiusAuthServAccessRequests=%u\n"
+				  "radiusAuthServDupAccessRequests=%u\n"
+				  "radiusAuthServAccessAccepts=%u\n"
+				  "radiusAuthServAccessRejects=%u\n"
+				  "radiusAuthServAccessChallenges=%u\n"
+				  "radiusAuthServMalformedAccessRequests=%u\n"
+				  "radiusAuthServBadAuthenticators=%u\n"
+				  "radiusAuthServPacketsDropped=%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,
+				  cli->counters.dup_access_requests,
+				  cli->counters.access_accepts,
+				  cli->counters.access_rejects,
+				  cli->counters.access_challenges,
+				  cli->counters.malformed_access_requests,
+				  cli->counters.bad_authenticators,
+				  cli->counters.packets_dropped,
+				  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 (os_snprintf_error(end - pos, ret)) {
+			*pos = '\0';
+			return pos - buf;
+		}
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+static int radius_server_get_eap_user(void *ctx, const u8 *identity,
+				      size_t identity_len, int phase2,
+				      struct eap_user *user)
+{
+	struct radius_session *sess = ctx;
+	struct radius_server_data *data = sess->server;
+	int ret;
+
+	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;
+	}
+
+	if (ret) {
+		RADIUS_DEBUG("%s: User-Name not found from user database",
+			     __func__);
+	}
+
+	return ret;
+}
+
+
+static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len)
+{
+	struct radius_session *sess = ctx;
+	struct radius_server_data *data = sess->server;
+	*len = data->eap_req_id_text_len;
+	return data->eap_req_id_text;
+}
+
+
+static void radius_server_log_msg(void *ctx, const char *msg)
+{
+	struct radius_session *sess = ctx;
+	srv_log(sess, "EAP: %s", msg);
+}
+
+
+#ifdef CONFIG_ERP
+
+static const char * radius_server_get_erp_domain(void *ctx)
+{
+	struct radius_session *sess = ctx;
+	struct radius_server_data *data = sess->server;
+
+	return data->erp_domain;
+}
+
+
+static struct eap_server_erp_key *
+radius_server_erp_get_key(void *ctx, const char *keyname)
+{
+	struct radius_session *sess = ctx;
+	struct radius_server_data *data = sess->server;
+	struct eap_server_erp_key *erp;
+
+	dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key,
+			 list) {
+		if (os_strcmp(erp->keyname_nai, keyname) == 0)
+			return erp;
+	}
+
+	return NULL;
+}
+
+
+static int radius_server_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
+{
+	struct radius_session *sess = ctx;
+	struct radius_server_data *data = sess->server;
+
+	dl_list_add(&data->erp_keys, &erp->list);
+	return 0;
+}
+
+#endif /* CONFIG_ERP */
+
+
+static const 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,
+#ifdef CONFIG_ERP
+	.get_erp_send_reauth_start = NULL,
+	.get_erp_domain = radius_server_get_erp_domain,
+	.erp_get_key = radius_server_erp_get_key,
+	.erp_add_key = radius_server_erp_add_key,
+#endif /* CONFIG_ERP */
+};
+
+
+/**
+ * radius_server_eap_pending_cb - Pending EAP data notification
+ * @data: RADIUS server context from radius_server_init()
+ * @ctx: Pending EAP context pointer
+ *
+ * This function is used to notify EAP server module that a pending operation
+ * has been completed and processing of the EAP session can proceed.
+ */
+void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx)
+{
+	struct radius_client *cli;
+	struct radius_session *s, *sess = NULL;
+	struct radius_msg *msg;
+
+	if (data == NULL)
+		return;
+
+	for (cli = data->clients; cli; cli = cli->next) {
+		for (s = cli->sessions; s; s = s->next) {
+			if (s->eap == ctx && s->last_msg) {
+				sess = s;
+				break;
+			}
+		}
+		if (sess)
+			break;
+	}
+
+	if (sess == NULL) {
+		RADIUS_DEBUG("No session matched callback ctx");
+		return;
+	}
+
+	msg = sess->last_msg;
+	sess->last_msg = NULL;
+	eap_sm_pending_cb(sess->eap);
+	if (radius_server_request(data, msg,
+				  (struct sockaddr *) &sess->last_from,
+				  sess->last_fromlen, cli,
+				  sess->last_from_addr,
+				  sess->last_from_port, sess) == -2)
+		return; /* msg was stored with the session */
+
+	radius_msg_free(msg);
+}
diff --git a/hostap/src/radius/radius_server.h b/hostap/src/radius/radius_server.h
new file mode 100644
index 0000000..7a25802
--- /dev/null
+++ b/hostap/src/radius/radius_server.h
@@ -0,0 +1,248 @@
+/*
+ * RADIUS authentication server
+ * Copyright (c) 2005-2009, 2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RADIUS_SERVER_H
+#define RADIUS_SERVER_H
+
+struct radius_server_data;
+struct eap_user;
+
+/**
+ * struct radius_server_conf - RADIUS server configuration
+ */
+struct radius_server_conf {
+	/**
+	 * auth_port - UDP port to listen to as an authentication server
+	 */
+	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
+	 * used with them in a format where each client is on its own line. The
+	 * first item on the line is the IPv4 or IPv6 address of the client
+	 * with an optional address mask to allow full network to be specified
+	 * (e.g., 192.168.1.2 or 192.168.1.0/24). This is followed by white
+	 * space (space or tabulator) and the shared secret. Lines starting
+	 * with '#' are skipped and can be used as comments.
+	 */
+	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.
+	 */
+	void *conf_ctx;
+
+	/**
+	 * eap_sim_db_priv - EAP-SIM/AKA database context
+	 *
+	 * This is passed to the EAP-SIM/AKA server implementation as a
+	 * callback context.
+	 */
+	void *eap_sim_db_priv;
+
+	/**
+	 * ssl_ctx - TLS context
+	 *
+	 * This is passed to the EAP server implementation as a callback
+	 * context for TLS operations.
+	 */
+	void *ssl_ctx;
+
+	/**
+	 * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST
+	 *
+	 * This parameter is used to set a key for EAP-FAST to encrypt the
+	 * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If
+	 * set, must point to a 16-octet key.
+	 */
+	u8 *pac_opaque_encr_key;
+
+	/**
+	 * eap_fast_a_id - EAP-FAST authority identity (A-ID)
+	 *
+	 * If EAP-FAST is not used, this can be set to %NULL. In theory, this
+	 * is a variable length field, but due to some existing implementations
+	 * requiring A-ID to be 16 octets in length, it is recommended to use
+	 * that length for the field to provide interoperability with deployed
+	 * peer implementations.
+	 */
+	u8 *eap_fast_a_id;
+
+	/**
+	 * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets
+	 */
+	size_t eap_fast_a_id_len;
+
+	/**
+	 * eap_fast_a_id_info - EAP-FAST authority identifier information
+	 *
+	 * This A-ID-Info contains a user-friendly name for the A-ID. For
+	 * example, this could be the enterprise and server names in
+	 * human-readable format. This field is encoded as UTF-8. If EAP-FAST
+	 * is not used, this can be set to %NULL.
+	 */
+	char *eap_fast_a_id_info;
+
+	/**
+	 * eap_fast_prov - EAP-FAST provisioning modes
+	 *
+	 * 0 = provisioning disabled, 1 = only anonymous provisioning allowed,
+	 * 2 = only authenticated provisioning allowed, 3 = both provisioning
+	 * modes allowed.
+	 */
+	int eap_fast_prov;
+
+	/**
+	 * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds
+	 *
+	 * This is the hard limit on how long a provisioned PAC-Key can be
+	 * used.
+	 */
+	int pac_key_lifetime;
+
+	/**
+	 * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds
+	 *
+	 * This is a soft limit on the PAC-Key. The server will automatically
+	 * generate a new PAC-Key when this number of seconds (or fewer) of the
+	 * lifetime remains.
+	 */
+	int pac_key_refresh_time;
+
+	/**
+	 * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
+	 *
+	 * This controls whether the protected success/failure indication
+	 * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA.
+	 */
+	int eap_sim_aka_result_ind;
+
+	/**
+	 * tnc - Trusted Network Connect (TNC)
+	 *
+	 * This controls whether TNC is enabled and will be required before the
+	 * peer is allowed to connect. Note: This is only used with EAP-TTLS
+	 * and EAP-FAST. If any other EAP method is enabled, the peer will be
+	 * allowed to connect without TNC.
+	 */
+	int tnc;
+
+	/**
+	 * pwd_group - EAP-pwd D-H group
+	 *
+	 * This is used to select which D-H group to use with EAP-pwd.
+	 */
+	u16 pwd_group;
+
+	/**
+	 * server_id - Server identity
+	 */
+	const char *server_id;
+
+	/**
+	 * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
+	 *
+	 * This controls whether the authentication server derives ERP key
+	 * hierarchy (rRK and rIK) from full EAP authentication and allows
+	 * these keys to be used to perform ERP to derive rMSK instead of full
+	 * EAP authentication to derive MSK.
+	 */
+	int erp;
+
+	const char *erp_domain;
+
+	unsigned int tls_session_lifetime;
+
+	/**
+	 * wps - Wi-Fi Protected Setup context
+	 *
+	 * If WPS is used with an external RADIUS server (which is quite
+	 * unlikely configuration), this is used to provide a pointer to WPS
+	 * context data. Normally, this can be set to %NULL.
+	 */
+	struct wps_context *wps;
+
+	/**
+	 * ipv6 - Whether to enable IPv6 support in the RADIUS server
+	 */
+	int ipv6;
+
+	/**
+	 * get_eap_user - Callback for fetching EAP user information
+	 * @ctx: Context data from conf_ctx
+	 * @identity: User identity
+	 * @identity_len: identity buffer length in octets
+	 * @phase2: Whether this is for Phase 2 identity
+	 * @user: Data structure for filling in the user information
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is used to fetch information from user database. The callback
+	 * will fill in information about allowed EAP methods and the user
+	 * password. The password field will be an allocated copy of the
+	 * password data and RADIUS server will free it after use.
+	 */
+	int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
+			    int phase2, struct eap_user *user);
+
+	/**
+	 * eap_req_id_text - Optional data for EAP-Request/Identity
+	 *
+	 * This can be used to configure an optional, displayable message that
+	 * will be sent in EAP-Request/Identity. This string can contain an
+	 * ASCII-0 character (nul) to separate network infromation per RFC
+	 * 4284. The actual string length is explicit provided in
+	 * eap_req_id_text_len since nul character will not be used as a string
+	 * terminator.
+	 */
+	const char *eap_req_id_text;
+
+	/**
+	 * eap_req_id_text_len - Length of eap_req_id_text buffer in octets
+	 */
+	size_t eap_req_id_text_len;
+
+	/*
+	 * msg_ctx - Context data for wpa_msg() calls
+	 */
+	void *msg_ctx;
+
+#ifdef CONFIG_RADIUS_TEST
+	const char *dump_msk_file;
+#endif /* CONFIG_RADIUS_TEST */
+
+	char *subscr_remediation_url;
+	u8 subscr_remediation_method;
+};
+
+
+struct radius_server_data *
+radius_server_init(struct radius_server_conf *conf);
+
+void radius_server_erp_flush(struct radius_server_data *data);
+void radius_server_deinit(struct radius_server_data *data);
+
+int radius_server_get_mib(struct radius_server_data *data, char *buf,
+			  size_t buflen);
+
+void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx);
+
+#endif /* RADIUS_SERVER_H */
diff --git a/hostap/src/rsn_supp/Makefile b/hostap/src/rsn_supp/Makefile
new file mode 100644
index 0000000..d5e61fe
--- /dev/null
+++ b/hostap/src/rsn_supp/Makefile
@@ -0,0 +1,30 @@
+all: librsn_supp.a
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov librsn_supp.a
+
+install:
+	@echo Nothing to be made.
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_IEEE80211W
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_PEERKEY
+CFLAGS += -DCONFIG_TDLS
+CFLAGS += -DCONFIG_WNM
+CFLAGS += -DIEEE8021X_EAPOL
+
+LIB_OBJS= \
+	pmksa_cache.o \
+	wpa_ft.o \
+	peerkey.o \
+	tdls.o \
+	preauth.o \
+	wpa.o \
+	wpa_ie.o
+
+librsn_supp.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/rsn_supp/peerkey.c b/hostap/src/rsn_supp/peerkey.c
new file mode 100644
index 0000000..79764d9
--- /dev/null
+++ b/hostap/src/rsn_supp/peerkey.c
@@ -0,0 +1,1155 @@
+/*
+ * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
+ * Copyright (c) 2006-2015, 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"
+
+#ifdef CONFIG_PEERKEY
+
+#include "common.h"
+#include "eloop.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "wpa.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+#include "peerkey.h"
+
+
+static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
+{
+	os_memcpy(pos, ie, ie_len);
+	return pos + ie_len;
+}
+
+
+static u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len)
+{
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = RSN_SELECTOR_LEN + data_len;
+	RSN_SELECTOR_PUT(pos, kde);
+	pos += RSN_SELECTOR_LEN;
+	os_memcpy(pos, data, data_len);
+	pos += data_len;
+	return pos;
+}
+
+
+static void wpa_supplicant_smk_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+#if 0
+	struct wpa_sm *sm = eloop_ctx;
+	struct wpa_peerkey *peerkey = timeout_ctx;
+#endif
+	/* TODO: time out SMK and any STK that was generated using this SMK */
+}
+
+
+static void wpa_supplicant_peerkey_free(struct wpa_sm *sm,
+					struct wpa_peerkey *peerkey)
+{
+	eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
+	os_free(peerkey);
+}
+
+
+static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst,
+					 const u8 *peer,
+					 u16 mui, u16 error_type, int ver)
+{
+	size_t rlen;
+	struct wpa_eapol_key *err;
+	struct wpa_eapol_key_192 *err192;
+	struct rsn_error_kde error;
+	u8 *rbuf, *pos;
+	size_t kde_len;
+	u16 key_info;
+
+	kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error);
+	if (peer)
+		kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+
+	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
+				  NULL, sizeof(*err) + kde_len, &rlen,
+				  (void *) &err);
+	if (rbuf == NULL)
+		return -1;
+	err192 = (struct wpa_eapol_key_192 *) err;
+
+	err->type = EAPOL_KEY_TYPE_RSN;
+	key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
+		WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ERROR |
+		WPA_KEY_INFO_REQUEST;
+	WPA_PUT_BE16(err->key_info, key_info);
+	WPA_PUT_BE16(err->key_length, 0);
+	os_memcpy(err->replay_counter, sm->request_counter,
+		  WPA_REPLAY_COUNTER_LEN);
+	inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+	WPA_PUT_BE16(err->key_data_length, (u16) kde_len);
+	pos = (u8 *) (err + 1);
+
+	if (peer) {
+		/* Peer MAC Address KDE */
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN);
+	}
+
+	/* Error KDE */
+	error.mui = host_to_be16(mui);
+	error.error_type = host_to_be16(error_type);
+	wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error));
+
+	if (peer) {
+		wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error (peer "
+			   MACSTR " mui %d error_type %d)",
+			   MAC2STR(peer), mui, error_type);
+	} else {
+		wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error "
+			   "(mui %d error_type %d)", mui, error_type);
+	}
+
+	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, dst,
+			   ETH_P_EAPOL, rbuf, rlen, err192->key_mic);
+
+	return 0;
+}
+
+
+static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm,
+				      const unsigned char *src_addr,
+				      const struct wpa_eapol_key *key,
+				      int ver, struct wpa_peerkey *peerkey)
+{
+	size_t rlen;
+	struct wpa_eapol_key *reply;
+	struct wpa_eapol_key_192 *reply192;
+	u8 *rbuf, *pos;
+	size_t kde_len;
+	u16 key_info;
+
+	/* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */
+	kde_len = peerkey->rsnie_p_len +
+		2 + RSN_SELECTOR_LEN + ETH_ALEN +
+		2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN;
+
+	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
+				  NULL, sizeof(*reply) + kde_len, &rlen,
+				  (void *) &reply);
+	if (rbuf == NULL)
+		return -1;
+	reply192 = (struct wpa_eapol_key_192 *) reply;
+
+	reply->type = EAPOL_KEY_TYPE_RSN;
+	key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
+		WPA_KEY_INFO_SECURE;
+	WPA_PUT_BE16(reply->key_info, key_info);
+	WPA_PUT_BE16(reply->key_length, 0);
+	os_memcpy(reply->replay_counter, key->replay_counter,
+		  WPA_REPLAY_COUNTER_LEN);
+
+	os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN);
+
+	WPA_PUT_BE16(reply->key_data_length, (u16) kde_len);
+	pos = (u8 *) (reply + 1);
+
+	/* Peer RSN IE */
+	pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
+
+	/* Initiator MAC Address KDE */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peerkey->addr, ETH_ALEN);
+
+	/* Initiator Nonce */
+	wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN);
+
+	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3");
+	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, src_addr,
+			   ETH_P_EAPOL, rbuf, rlen, reply192->key_mic);
+
+	return 0;
+}
+
+
+static int wpa_supplicant_process_smk_m2(
+	struct wpa_sm *sm, const unsigned char *src_addr,
+	const struct wpa_eapol_key *key, size_t extra_len, int ver)
+{
+	struct wpa_peerkey *peerkey;
+	struct wpa_eapol_ie_parse kde;
+	struct wpa_ie_data ie;
+	int cipher;
+	struct rsn_ie_hdr *hdr;
+	u8 *pos;
+
+	wpa_printf(MSG_DEBUG, "RSN: Received SMK M2");
+
+	if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
+		wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for "
+			   "the current network");
+		return -1;
+	}
+
+	if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
+	    0) {
+		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2");
+		return -1;
+	}
+
+	if (kde.rsn_ie == NULL || kde.mac_addr == NULL ||
+	    kde.mac_addr_len < ETH_ALEN) {
+		wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in "
+			   "SMK M2");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR,
+		   MAC2STR(kde.mac_addr));
+
+	if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) {
+		wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK "
+			   "M2");
+		return -1;
+	}
+
+	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
+		wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2");
+		return -1;
+	}
+
+	cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher &
+					  sm->allowed_pairwise_cipher, 0);
+	if (cipher < 0) {
+		wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2");
+		wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr,
+					      STK_MUI_SMK, STK_ERR_CPHR_NS,
+					      ver);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey",
+		   wpa_cipher_txt(cipher));
+
+	/* TODO: find existing entry and if found, use that instead of adding
+	 * a new one; how to handle the case where both ends initiate at the
+	 * same time? */
+	peerkey = os_zalloc(sizeof(*peerkey));
+	if (peerkey == NULL)
+		return -1;
+	os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN);
+	os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
+	os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
+	peerkey->rsnie_i_len = kde.rsn_ie_len;
+	peerkey->cipher = cipher;
+	peerkey->akmp = ie.key_mgmt;
+
+	if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Failed to get random data for PNonce");
+		wpa_supplicant_peerkey_free(sm, peerkey);
+		return -1;
+	}
+
+	hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p;
+	hdr->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(hdr->version, RSN_VERSION);
+	pos = (u8 *) (hdr + 1);
+	/* Group Suite can be anything for SMK RSN IE; receiver will just
+	 * ignore it. */
+	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+	pos += RSN_SELECTOR_LEN;
+	/* Include only the selected cipher in pairwise cipher suite */
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher));
+	pos += RSN_SELECTOR_LEN;
+
+	hdr->len = (pos - peerkey->rsnie_p) - 2;
+	peerkey->rsnie_p_len = pos - peerkey->rsnie_p;
+	wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
+		    peerkey->rsnie_p, peerkey->rsnie_p_len);
+
+	wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey);
+
+	peerkey->next = sm->peerkey;
+	sm->peerkey = peerkey;
+
+	return 0;
+}
+
+
+/**
+ * rsn_smkid - Derive SMK identifier
+ * @smk: Station master key (32 bytes)
+ * @pnonce: Peer Nonce
+ * @mac_p: Peer MAC address
+ * @inonce: Initiator Nonce
+ * @mac_i: Initiator MAC address
+ * @akmp: Negotiated AKM
+ *
+ * 8.5.1.4 Station to station (STK) key hierarchy
+ * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I)
+ */
+static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p,
+		      const u8 *inonce, const u8 *mac_i, u8 *smkid,
+		      int akmp)
+{
+	char *title = "SMK Name";
+	const u8 *addr[5];
+	const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN,
+				ETH_ALEN };
+	unsigned char hash[SHA256_MAC_LEN];
+
+	addr[0] = (u8 *) title;
+	addr[1] = pnonce;
+	addr[2] = mac_p;
+	addr[3] = inonce;
+	addr[4] = mac_i;
+
+#ifdef CONFIG_IEEE80211W
+	if (wpa_key_mgmt_sha256(akmp))
+		hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash);
+	else
+#endif /* CONFIG_IEEE80211W */
+		hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash);
+	os_memcpy(smkid, hash, PMKID_LEN);
+}
+
+
+static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm,
+					   struct wpa_peerkey *peerkey)
+{
+	size_t mlen;
+	struct wpa_eapol_key *msg;
+	u8 *mbuf;
+	size_t kde_len;
+	u16 key_info, ver;
+
+	kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
+
+	mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+				  sizeof(*msg) + kde_len, &mlen,
+				  (void *) &msg);
+	if (mbuf == NULL)
+		return;
+
+	msg->type = EAPOL_KEY_TYPE_RSN;
+
+	if (peerkey->cipher != WPA_CIPHER_TKIP)
+		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+	else
+		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+	key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK;
+	WPA_PUT_BE16(msg->key_info, key_info);
+
+	if (peerkey->cipher != WPA_CIPHER_TKIP)
+		WPA_PUT_BE16(msg->key_length, 16);
+	else
+		WPA_PUT_BE16(msg->key_length, 32);
+
+	os_memcpy(msg->replay_counter, peerkey->replay_counter,
+		  WPA_REPLAY_COUNTER_LEN);
+	inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
+	WPA_PUT_BE16(msg->key_data_length, kde_len);
+	wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID,
+		    peerkey->smkid, PMKID_LEN);
+
+	if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"RSN: Failed to get random data for INonce (STK)");
+		os_free(mbuf);
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake",
+		    peerkey->inonce, WPA_NONCE_LEN);
+	os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
+
+	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR,
+		   MAC2STR(peerkey->addr));
+	wpa_eapol_key_send(sm, NULL, 0, ver, peerkey->addr, ETH_P_EAPOL,
+			   mbuf, mlen, NULL);
+}
+
+
+static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm,
+					   struct wpa_peerkey *peerkey)
+{
+	size_t mlen;
+	struct wpa_eapol_key *msg;
+	u8 *mbuf, *pos;
+	size_t kde_len;
+	u16 key_info, ver;
+	be32 lifetime;
+
+	kde_len = peerkey->rsnie_i_len +
+		2 + RSN_SELECTOR_LEN + sizeof(lifetime);
+
+	mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+				  sizeof(*msg) + kde_len, &mlen,
+				  (void *) &msg);
+	if (mbuf == NULL)
+		return;
+
+	msg->type = EAPOL_KEY_TYPE_RSN;
+
+	if (peerkey->cipher != WPA_CIPHER_TKIP)
+		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+	else
+		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+	key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK |
+		WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+	WPA_PUT_BE16(msg->key_info, key_info);
+
+	if (peerkey->cipher != WPA_CIPHER_TKIP)
+		WPA_PUT_BE16(msg->key_length, 16);
+	else
+		WPA_PUT_BE16(msg->key_length, 32);
+
+	os_memcpy(msg->replay_counter, peerkey->replay_counter,
+		  WPA_REPLAY_COUNTER_LEN);
+	inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
+	WPA_PUT_BE16(msg->key_data_length, kde_len);
+	pos = (u8 *) (msg + 1);
+	pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
+	lifetime = host_to_be32(peerkey->lifetime);
+	wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+		    (u8 *) &lifetime, sizeof(lifetime));
+
+	os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
+
+	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR,
+		   MAC2STR(peerkey->addr));
+	wpa_eapol_key_send(sm, peerkey->stk.kck, peerkey->stk.kck_len, ver,
+			   peerkey->addr, ETH_P_EAPOL, mbuf, mlen,
+			   msg->key_mic);
+}
+
+
+static int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey,
+					 struct wpa_eapol_ie_parse *kde)
+{
+	wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")",
+		   MAC2STR(kde->mac_addr));
+
+	if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0)
+	{
+		wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not "
+			   "match with the one used in SMK M3");
+		return -1;
+	}
+
+	if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
+		wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not "
+			   "match with the one received in SMK M2");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm,
+					 const unsigned char *src_addr,
+					 const struct wpa_eapol_key *key,
+					 int ver,
+					 struct wpa_peerkey *peerkey,
+					 struct wpa_eapol_ie_parse *kde)
+{
+	int cipher;
+	struct wpa_ie_data ie;
+
+	wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")",
+		   MAC2STR(kde->mac_addr));
+	if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN ||
+	    wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) {
+		wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5");
+		/* TODO: abort negotiation */
+		return -1;
+	}
+
+	if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
+		wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does "
+			   "not match with INonce used in SMK M1");
+		return -1;
+	}
+
+	if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0)
+	{
+		wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not "
+			   "match with the one used in SMK M1");
+		return -1;
+	}
+
+	os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len);
+	peerkey->rsnie_p_len = kde->rsn_ie_len;
+	os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN);
+
+	cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher &
+					  sm->allowed_pairwise_cipher, 0);
+	if (cipher < 0) {
+		wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected "
+			   "unacceptable cipher", MAC2STR(kde->mac_addr));
+		wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr,
+					      STK_MUI_SMK, STK_ERR_CPHR_NS,
+					      ver);
+		/* TODO: abort negotiation */
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey",
+		   wpa_cipher_txt(cipher));
+	peerkey->cipher = cipher;
+
+	return 0;
+}
+
+
+static int wpa_supplicant_process_smk_m45(
+	struct wpa_sm *sm, const unsigned char *src_addr,
+	const struct wpa_eapol_key *key, size_t extra_len, int ver)
+{
+	struct wpa_peerkey *peerkey;
+	struct wpa_eapol_ie_parse kde;
+	u32 lifetime;
+
+	if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
+		wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
+			   "the current network");
+		return -1;
+	}
+
+	if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
+	    0) {
+		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5");
+		return -1;
+	}
+
+	if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
+	    kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN ||
+	    kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN ||
+	    kde.lifetime == NULL || kde.lifetime_len < 4) {
+		wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or "
+			   "Lifetime KDE in SMK M4/M5");
+		return -1;
+	}
+
+	for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+		if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 &&
+		    os_memcmp(peerkey->initiator ? peerkey->inonce :
+			   peerkey->pnonce,
+			   key->key_nonce, WPA_NONCE_LEN) == 0)
+			break;
+	}
+	if (peerkey == NULL) {
+		wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found "
+			   "for SMK M4/M5: peer " MACSTR,
+			   MAC2STR(kde.mac_addr));
+		return -1;
+	}
+
+	if (peerkey->initiator) {
+		if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver,
+						  peerkey, &kde) < 0)
+			return -1;
+	} else {
+		if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0)
+			return -1;
+	}
+
+	os_memcpy(peerkey->smk, kde.smk, PMK_LEN);
+	peerkey->smk_complete = 1;
+	wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN);
+	lifetime = WPA_GET_BE32(kde.lifetime);
+	wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime);
+	if (lifetime > 1000000000)
+		lifetime = 1000000000; /* avoid overflowing eloop time */
+	peerkey->lifetime = lifetime;
+	eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
+			       sm, peerkey);
+
+	if (peerkey->initiator) {
+		rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr,
+			  peerkey->inonce, sm->own_addr, peerkey->smkid,
+			  peerkey->akmp);
+		wpa_supplicant_send_stk_1_of_4(sm, peerkey);
+	} else {
+		rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr,
+			  peerkey->inonce, peerkey->addr, peerkey->smkid,
+			  peerkey->akmp);
+	}
+	wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN);
+
+	return 0;
+}
+
+
+static int wpa_supplicant_process_smk_error(
+	struct wpa_sm *sm, const unsigned char *src_addr,
+	const struct wpa_eapol_key *key, size_t extra_len)
+{
+	struct wpa_eapol_ie_parse kde;
+	struct rsn_error_kde error;
+	u8 peer[ETH_ALEN];
+	u16 error_type;
+
+	wpa_printf(MSG_DEBUG, "RSN: Received SMK Error");
+
+	if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
+		wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
+			   "the current network");
+		return -1;
+	}
+
+	if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
+	    0) {
+		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
+		return -1;
+	}
+
+	if (kde.error == NULL || kde.error_len < sizeof(error)) {
+		wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error");
+		return -1;
+	}
+
+	if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN)
+		os_memcpy(peer, kde.mac_addr, ETH_ALEN);
+	else
+		os_memset(peer, 0, ETH_ALEN);
+	os_memcpy(&error, kde.error, sizeof(error));
+	error_type = be_to_host16(error.error_type);
+	wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+		"RSN: SMK Error KDE received: MUI %d error_type %d peer "
+		MACSTR,
+		be_to_host16(error.mui), error_type,
+		MAC2STR(peer));
+
+	if (kde.mac_addr &&
+	    (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN ||
+	     error_type == STK_ERR_CPHR_NS)) {
+		struct wpa_peerkey *peerkey;
+
+		for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+			if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) ==
+			    0)
+				break;
+		}
+		if (peerkey == NULL) {
+			wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake "
+				   "found for SMK Error");
+			return -1;
+		}
+		/* TODO: abort SMK/STK handshake and remove all related keys */
+	}
+
+	return 0;
+}
+
+
+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, const u8 *key_data,
+					      size_t key_data_len)
+{
+	struct wpa_eapol_ie_parse ie;
+	size_t kde_buf_len;
+	struct wpa_ptk *stk;
+	u8 buf[8], *kde_buf, *pos;
+	be32 lifetime;
+
+	wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from "
+		   MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+	os_memset(&ie, 0, sizeof(ie));
+
+	/* RSN: msg 1/4 should contain SMKID for the selected SMK */
+	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_const(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+		wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4",
+			    ie.pmkid, PMKID_LEN);
+		return;
+	}
+
+	if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"RSN: Failed to get random data for PNonce");
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPA: Renewed PNonce",
+		    peerkey->pnonce, WPA_NONCE_LEN);
+
+	/* Calculate STK which will be stored as a temporary STK until it has
+	 * been verified when processing message 3/4. */
+	stk = &peerkey->tstk;
+	wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
+		       sm->own_addr, peerkey->addr,
+		       peerkey->pnonce, key->key_nonce,
+		       stk, peerkey->akmp, peerkey->cipher);
+	/* Supplicant: swap tx/rx Mic keys */
+	os_memcpy(buf, &stk->tk[16], 8);
+	os_memcpy(&stk->tk[16], &stk->tk[24], 8);
+	os_memcpy(&stk->tk[24], buf, 8);
+	peerkey->tstk_set = 1;
+
+	kde_buf_len = peerkey->rsnie_p_len +
+		2 + RSN_SELECTOR_LEN + sizeof(lifetime) +
+		2 + RSN_SELECTOR_LEN + PMKID_LEN;
+	kde_buf = os_malloc(kde_buf_len);
+	if (kde_buf == NULL)
+		return;
+	pos = kde_buf;
+	pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
+	lifetime = host_to_be32(peerkey->lifetime);
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+			  (u8 *) &lifetime, sizeof(lifetime));
+	wpa_add_kde(pos, RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN);
+
+	if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver,
+				       peerkey->pnonce, kde_buf, kde_buf_len,
+				       stk)) {
+		os_free(kde_buf);
+		return;
+	}
+	os_free(kde_buf);
+
+	os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
+}
+
+
+static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm,
+					       struct wpa_peerkey *peerkey,
+					       struct wpa_eapol_ie_parse *kde)
+{
+	u32 lifetime;
+
+	if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime))
+		return;
+
+	lifetime = WPA_GET_BE32(kde->lifetime);
+
+	if (lifetime >= peerkey->lifetime) {
+		wpa_printf(MSG_DEBUG, "RSN: Peer used SMK lifetime %u seconds "
+			   "which is larger than or equal to own value %u "
+			   "seconds - ignored", lifetime, peerkey->lifetime);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "RSN: Peer used shorter SMK lifetime %u seconds "
+		   "(own was %u seconds) - updated",
+		   lifetime, peerkey->lifetime);
+	peerkey->lifetime = lifetime;
+
+	eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
+	eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
+			       sm, peerkey);
+}
+
+
+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, const u8 *key_data,
+					      size_t key_data_len)
+{
+	struct wpa_eapol_ie_parse kde;
+
+	wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from "
+		   MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+	os_memset(&kde, 0, sizeof(kde));
+
+	/* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE
+	 * from the peer. It may also include Lifetime KDE. */
+	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_const(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+		wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4",
+			    kde.pmkid, PMKID_LEN);
+		return;
+	}
+
+	if (kde.rsn_ie_len != peerkey->rsnie_p_len ||
+	    os_memcmp(kde.rsn_ie, peerkey->rsnie_p, kde.rsn_ie_len) != 0) {
+		wpa_printf(MSG_INFO, "RSN: Peer RSN IE in SMK and STK "
+			   "handshakes did not match");
+		wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in SMK handshake",
+			    peerkey->rsnie_p, peerkey->rsnie_p_len);
+		wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in STK handshake",
+			    kde.rsn_ie, kde.rsn_ie_len);
+		return;
+	}
+
+	wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde);
+
+	wpa_supplicant_send_stk_3_of_4(sm, peerkey);
+	os_memcpy(peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN);
+}
+
+
+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, const u8 *key_data,
+					      size_t key_data_len)
+{
+	struct wpa_eapol_ie_parse kde;
+	size_t key_len;
+	const u8 *_key;
+	u8 key_buf[32], rsc[6];
+
+	wpa_printf(MSG_DEBUG, "RSN: RX message 3 of STK 4-Way Handshake from "
+		   MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+	os_memset(&kde, 0, sizeof(kde));
+
+	/* RSN: msg 3/4 should contain Initiator RSN IE. It may also include
+	 * Lifetime KDE. */
+	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;
+	}
+
+	if (kde.rsn_ie_len != peerkey->rsnie_i_len ||
+	    os_memcmp(kde.rsn_ie, peerkey->rsnie_i, kde.rsn_ie_len) != 0) {
+		wpa_printf(MSG_INFO, "RSN: Initiator RSN IE in SMK and STK "
+			   "handshakes did not match");
+		wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in SMK "
+			    "handshake",
+			    peerkey->rsnie_i, peerkey->rsnie_i_len);
+		wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in STK "
+			    "handshake",
+			    kde.rsn_ie, kde.rsn_ie_len);
+		return;
+	}
+
+	if (os_memcmp(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+		wpa_printf(MSG_WARNING, "RSN: INonce from message 1 of STK "
+			   "4-Way Handshake differs from 3 of STK 4-Way "
+			   "Handshake - drop packet (src=" MACSTR ")",
+			   MAC2STR(peerkey->addr));
+		return;
+	}
+
+	wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde);
+
+	if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver,
+				       WPA_GET_BE16(key->key_info),
+				       &peerkey->stk))
+		return;
+
+	_key = peerkey->stk.tk;
+	if (peerkey->cipher == WPA_CIPHER_TKIP) {
+		/* Swap Tx/Rx keys for Michael MIC */
+		os_memcpy(key_buf, _key, 16);
+		os_memcpy(key_buf + 16, _key + 24, 8);
+		os_memcpy(key_buf + 24, _key + 16, 8);
+		_key = key_buf;
+		key_len = 32;
+	} else
+		key_len = 16;
+
+	os_memset(rsc, 0, 6);
+	if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
+			   rsc, sizeof(rsc), _key, key_len) < 0) {
+		os_memset(key_buf, 0, sizeof(key_buf));
+		wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
+			   "driver.");
+		return;
+	}
+	os_memset(key_buf, 0, sizeof(key_buf));
+}
+
+
+static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm,
+					      struct wpa_peerkey *peerkey,
+					      const struct wpa_eapol_key *key,
+					      u16 ver)
+{
+	u8 rsc[6];
+
+	wpa_printf(MSG_DEBUG, "RSN: RX message 4 of STK 4-Way Handshake from "
+		   MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+	os_memset(rsc, 0, 6);
+	if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
+			   rsc, sizeof(rsc), peerkey->stk.tk,
+			   peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) {
+		wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
+			   "driver.");
+		return;
+	}
+}
+
+
+/**
+ * peerkey_verify_eapol_key_mic - Verify PeerKey MIC
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @peerkey: Pointer to the PeerKey data for the peer
+ * @key: Pointer to the EAPOL-Key frame header
+ * @ver: Version bits from EAPOL-Key Key Info
+ * @buf: Pointer to the beginning of EAPOL-Key frame
+ * @len: Length of the EAPOL-Key frame
+ * Returns: 0 on success, -1 on failure
+ */
+int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
+				 struct wpa_peerkey *peerkey,
+				 struct wpa_eapol_key_192 *key, u16 ver,
+				 const u8 *buf, size_t len)
+{
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	size_t mic_len = 16;
+	int ok = 0;
+
+	if (peerkey->initiator && !peerkey->stk_set) {
+		wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
+			       sm->own_addr, peerkey->addr,
+			       peerkey->inonce, key->key_nonce,
+			       &peerkey->stk, peerkey->akmp, peerkey->cipher);
+		peerkey->stk_set = 1;
+	}
+
+	os_memcpy(mic, key->key_mic, mic_len);
+	if (peerkey->tstk_set) {
+		os_memset(key->key_mic, 0, mic_len);
+		wpa_eapol_key_mic(peerkey->tstk.kck, peerkey->tstk.kck_len,
+				  sm->key_mgmt, ver, buf, len, key->key_mic);
+		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+			wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
+				   "when using TSTK - ignoring TSTK");
+		} else {
+			ok = 1;
+			peerkey->tstk_set = 0;
+			peerkey->stk_set = 1;
+			os_memcpy(&peerkey->stk, &peerkey->tstk,
+				  sizeof(peerkey->stk));
+			os_memset(&peerkey->tstk, 0, sizeof(peerkey->tstk));
+		}
+	}
+
+	if (!ok && peerkey->stk_set) {
+		os_memset(key->key_mic, 0, mic_len);
+		wpa_eapol_key_mic(peerkey->stk.kck, peerkey->stk.kck_len,
+				  sm->key_mgmt, ver, buf, len, key->key_mic);
+		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+			wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
+				   "- dropping packet");
+			return -1;
+		}
+		ok = 1;
+	}
+
+	if (!ok) {
+		wpa_printf(MSG_WARNING, "RSN: Could not verify EAPOL-Key MIC "
+			   "- dropping packet");
+		return -1;
+	}
+
+	os_memcpy(peerkey->replay_counter, key->replay_counter,
+		  WPA_REPLAY_COUNTER_LEN);
+	peerkey->replay_counter_set = 1;
+	return 0;
+}
+
+
+/**
+ * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1)
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @peer: MAC address of the peer STA
+ * Returns: 0 on success, or -1 on failure
+ *
+ * Send an EAPOL-Key Request to the current authenticator to start STK
+ * handshake with the peer.
+ */
+int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
+{
+	size_t rlen, kde_len;
+	struct wpa_eapol_key *req;
+	int key_info, ver;
+	u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos;
+	u16 count;
+	struct rsn_ie_hdr *hdr;
+	struct wpa_peerkey *peerkey;
+	struct wpa_ie_data ie;
+
+	if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled)
+		return -1;
+
+	if (sm->ap_rsn_ie &&
+	    wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 &&
+	    !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) {
+		wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK");
+		return -1;
+	}
+
+	if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
+		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+	else
+		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+	if (wpa_sm_get_bssid(sm, bssid) < 0) {
+		wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
+			   "SMK M1");
+		return -1;
+	}
+
+	/* TODO: find existing entry and if found, use that instead of adding
+	 * a new one */
+	peerkey = os_zalloc(sizeof(*peerkey));
+	if (peerkey == NULL)
+		return -1;
+	peerkey->initiator = 1;
+	os_memcpy(peerkey->addr, peer, ETH_ALEN);
+	peerkey->akmp = sm->key_mgmt;
+
+	/* SMK M1:
+	 * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
+	 *           MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE))
+	 */
+
+	hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i;
+	hdr->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(hdr->version, RSN_VERSION);
+	pos = (u8 *) (hdr + 1);
+	/* Group Suite can be anything for SMK RSN IE; receiver will just
+	 * ignore it. */
+	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+	pos += RSN_SELECTOR_LEN;
+	count_pos = pos;
+	pos += 2;
+
+	count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher);
+	pos += count * RSN_SELECTOR_LEN;
+	WPA_PUT_LE16(count_pos, count);
+
+	hdr->len = (pos - peerkey->rsnie_i) - 2;
+	peerkey->rsnie_i_len = pos - peerkey->rsnie_i;
+	wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
+		    peerkey->rsnie_i, peerkey->rsnie_i_len);
+
+	kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+
+	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+				  sizeof(*req) + kde_len, &rlen,
+				  (void *) &req);
+	if (rbuf == NULL) {
+		wpa_supplicant_peerkey_free(sm, peerkey);
+		return -1;
+	}
+
+	req->type = EAPOL_KEY_TYPE_RSN;
+	key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
+		WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver;
+	WPA_PUT_BE16(req->key_info, key_info);
+	WPA_PUT_BE16(req->key_length, 0);
+	os_memcpy(req->replay_counter, sm->request_counter,
+		  WPA_REPLAY_COUNTER_LEN);
+	inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+	if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Failed to get random data for INonce");
+		os_free(rbuf);
+		wpa_supplicant_peerkey_free(sm, peerkey);
+		return -1;
+	}
+	os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake",
+		    req->key_nonce, WPA_NONCE_LEN);
+
+	WPA_PUT_BE16(req->key_data_length, (u16) kde_len);
+	pos = (u8 *) (req + 1);
+
+	/* Initiator RSN IE */
+	pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
+	/* Peer MAC address KDE */
+	wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN);
+
+	wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer "
+		   MACSTR ")", MAC2STR(peer));
+	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
+			   ETH_P_EAPOL, rbuf, rlen, req->key_mic);
+
+	peerkey->next = sm->peerkey;
+	sm->peerkey = peerkey;
+
+	return 0;
+}
+
+
+/**
+ * peerkey_deinit - Free PeerKey values
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void peerkey_deinit(struct wpa_sm *sm)
+{
+	struct wpa_peerkey *prev, *peerkey = sm->peerkey;
+	while (peerkey) {
+		prev = peerkey;
+		peerkey = peerkey->next;
+		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,
+			   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,
+						  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,
+						  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,
+						  key_data, 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)
+{
+	if (key_info & WPA_KEY_INFO_ERROR) {
+		/* SMK Error */
+		wpa_supplicant_process_smk_error(sm, src_addr, key, extra_len);
+	} else if (key_info & WPA_KEY_INFO_ACK) {
+		/* SMK M2 */
+		wpa_supplicant_process_smk_m2(sm, src_addr, key, extra_len,
+					      ver);
+	} else {
+		/* SMK M4 or M5 */
+		wpa_supplicant_process_smk_m45(sm, src_addr, key, extra_len,
+					       ver);
+	}
+}
+
+#endif /* CONFIG_PEERKEY */
diff --git a/hostap/src/rsn_supp/peerkey.h b/hostap/src/rsn_supp/peerkey.h
new file mode 100644
index 0000000..6ccd948
--- /dev/null
+++ b/hostap/src/rsn_supp/peerkey.h
@@ -0,0 +1,82 @@
+/*
+ * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
+ * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PEERKEY_H
+#define PEERKEY_H
+
+#define PEERKEY_MAX_IE_LEN 80
+struct wpa_peerkey {
+	struct wpa_peerkey *next;
+	int initiator; /* whether this end was initator for SMK handshake */
+	u8 addr[ETH_ALEN]; /* other end MAC address */
+	u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
+	u8 pnonce[WPA_NONCE_LEN]; /* Peer Nonce */
+	u8 rsnie_i[PEERKEY_MAX_IE_LEN]; /* Initiator RSN IE */
+	size_t rsnie_i_len;
+	u8 rsnie_p[PEERKEY_MAX_IE_LEN]; /* Peer RSN IE */
+	size_t rsnie_p_len;
+	u8 smk[PMK_LEN];
+	int smk_complete;
+	u8 smkid[PMKID_LEN];
+	u32 lifetime;
+	int cipher; /* Selected cipher (WPA_CIPHER_*) */
+	u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+	int replay_counter_set;
+	int akmp;
+
+	struct wpa_ptk stk, tstk;
+	int stk_set, tstk_set;
+};
+
+
+#ifdef CONFIG_PEERKEY
+
+int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
+				 struct wpa_peerkey *peerkey,
+				 struct wpa_eapol_key_192 *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,
+			   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);
+void peerkey_deinit(struct wpa_sm *sm);
+
+#else /* CONFIG_PEERKEY */
+
+static inline int
+peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
+			     struct wpa_peerkey *peerkey,
+			     struct wpa_eapol_key *key, u16 ver,
+			     const u8 *buf, size_t len)
+{
+	return -1;
+}
+
+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,
+		      const u8 *key_data, size_t key_data_len)
+{
+}
+
+static inline 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)
+{
+}
+
+static inline void peerkey_deinit(struct wpa_sm *sm)
+{
+}
+
+#endif /* CONFIG_PEERKEY */
+
+#endif /* PEERKEY_H */
diff --git a/hostap/src/rsn_supp/pmksa_cache.c b/hostap/src/rsn_supp/pmksa_cache.c
new file mode 100644
index 0000000..ef7b683
--- /dev/null
+++ b/hostap/src/rsn_supp/pmksa_cache.c
@@ -0,0 +1,537 @@
+/*
+ * WPA Supplicant - RSN PMKSA cache
+ * Copyright (c) 2004-2009, 2011-2015, 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 "eloop.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "wpa.h"
+#include "wpa_i.h"
+#include "pmksa_cache.h"
+
+#ifdef IEEE8021X_EAPOL
+
+static const int pmksa_cache_max_entries = 32;
+
+struct rsn_pmksa_cache {
+	struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */
+	int pmksa_count; /* number of entries in PMKSA cache */
+	struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
+
+	void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
+			enum pmksa_free_reason reason);
+	void *ctx;
+};
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
+
+
+static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
+{
+	bin_clear_free(entry, sizeof(*entry));
+}
+
+
+static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+				   struct rsn_pmksa_cache_entry *entry,
+				   enum pmksa_free_reason reason)
+{
+	wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid);
+	pmksa->pmksa_count--;
+	pmksa->free_cb(entry, pmksa->ctx, reason);
+	_pmksa_cache_free_entry(entry);
+}
+
+
+static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+{
+	struct rsn_pmksa_cache *pmksa = eloop_ctx;
+	struct os_reltime 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;
+		wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
+			   MACSTR, MAC2STR(entry->aa));
+		pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE);
+	}
+
+	pmksa_cache_set_expiration(pmksa);
+}
+
+
+static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
+{
+	struct rsn_pmksa_cache *pmksa = eloop_ctx;
+	pmksa->sm->cur_pmksa = NULL;
+	eapol_sm_request_reauth(pmksa->sm->eapol);
+}
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
+{
+	int sec;
+	struct rsn_pmksa_cache_entry *entry;
+	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_reltime(&now);
+	sec = pmksa->pmksa->expiration - now.sec;
+	if (sec < 0)
+		sec = 0;
+	eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
+
+	entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
+		pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL);
+	if (entry) {
+		sec = pmksa->pmksa->reauth_time - now.sec;
+		if (sec < 0)
+			sec = 0;
+		eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
+				       NULL);
+	}
+}
+
+
+/**
+ * pmksa_cache_add - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @network_ctx: Network configuration context for this PMK
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
+ *
+ * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
+ * cache. If an old entry is already in the cache for the same Authenticator,
+ * this entry will be replaced with the new entry. PMKID will be calculated
+ * based on the PMK and the driver interface is notified of the new PMKID.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+		const u8 *kck, size_t kck_len,
+		const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
+{
+	struct rsn_pmksa_cache_entry *entry, *pos, *prev;
+	struct os_reltime now;
+
+	if (pmk_len > PMK_LEN)
+		return NULL;
+
+	if (wpa_key_mgmt_suite_b(akmp) && !kck)
+		return NULL;
+
+	entry = os_zalloc(sizeof(*entry));
+	if (entry == NULL)
+		return NULL;
+	os_memcpy(entry->pmk, pmk, pmk_len);
+	entry->pmk_len = pmk_len;
+	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
+	else if (wpa_key_mgmt_suite_b(akmp))
+		rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
+	else
+		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
+			  wpa_key_mgmt_sha256(akmp));
+	os_get_reltime(&now);
+	entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
+	entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
+		pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
+	entry->akmp = akmp;
+	os_memcpy(entry->aa, aa, ETH_ALEN);
+	entry->network_ctx = network_ctx;
+
+	/* Replace an old entry for the same Authenticator (if found) with the
+	 * new entry */
+	pos = pmksa->pmksa;
+	prev = NULL;
+	while (pos) {
+		if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
+			if (pos->pmk_len == pmk_len &&
+			    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);
+				return pos;
+			}
+			if (prev == NULL)
+				pmksa->pmksa = pos->next;
+			else
+				prev->next = pos->next;
+
+			/*
+			 * If OKC is used, there may be other PMKSA cache
+			 * entries based on the same PMK. These needs to be
+			 * flushed so that a new entry can be created based on
+			 * the new PMK. Only clear other entries if they have a
+			 * matching PMK and this PMK has been used successfully
+			 * with the current AP, i.e., if opportunistic flag has
+			 * been cleared in wpa_supplicant_key_neg_complete().
+			 */
+			wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
+				   "the current AP and any PMKSA cache entry "
+				   "that was based on the old PMK");
+			if (!pos->opportunistic)
+				pmksa_cache_flush(pmksa, network_ctx, pos->pmk,
+						  pos->pmk_len);
+			pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE);
+			break;
+		}
+		prev = pos;
+		pos = pos->next;
+	}
+
+	if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
+		/* Remove the oldest entry to make room for the new entry */
+		pos = pmksa->pmksa;
+
+		if (pos == pmksa->sm->cur_pmksa) {
+			/*
+			 * Never remove the current PMKSA cache entry, since
+			 * it's in use, and removing it triggers a needless
+			 * deauthentication.
+			 */
+			pos = pos->next;
+			pmksa->pmksa->next = pos ? pos->next : NULL;
+		} else
+			pmksa->pmksa = pos->next;
+
+		if (pos) {
+			wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle "
+				   "PMKSA cache entry (for " MACSTR ") to "
+				   "make room for new one",
+				   MAC2STR(pos->aa));
+			pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE);
+		}
+	}
+
+	/* Add the new entry; order by expiration time */
+	pos = pmksa->pmksa;
+	prev = NULL;
+	while (pos) {
+		if (pos->expiration > entry->expiration)
+			break;
+		prev = pos;
+		pos = pos->next;
+	}
+	if (prev == NULL) {
+		entry->next = pmksa->pmksa;
+		pmksa->pmksa = entry;
+		pmksa_cache_set_expiration(pmksa);
+	} else {
+		entry->next = prev->next;
+		prev->next = entry;
+	}
+	pmksa->pmksa_count++;
+	wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
+		   " network_ctx=%p", MAC2STR(entry->aa), network_ctx);
+	wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
+
+	return entry;
+}
+
+
+/**
+ * pmksa_cache_flush - Flush PMKSA cache entries for a specific network
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @network_ctx: Network configuration context or %NULL to flush all entries
+ * @pmk: PMK to match for or %NYLL to match all PMKs
+ * @pmk_len: PMK length
+ */
+void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
+		       const u8 *pmk, size_t pmk_len)
+{
+	struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp;
+	int removed = 0;
+
+	entry = pmksa->pmksa;
+	while (entry) {
+		if ((entry->network_ctx == network_ctx ||
+		     network_ctx == NULL) &&
+		    (pmk == NULL ||
+		     (pmk_len == entry->pmk_len &&
+		      os_memcmp(pmk, entry->pmk, pmk_len) == 0))) {
+			wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry "
+				   "for " MACSTR, MAC2STR(entry->aa));
+			if (prev)
+				prev->next = entry->next;
+			else
+				pmksa->pmksa = entry->next;
+			tmp = entry;
+			entry = entry->next;
+			pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE);
+			removed++;
+		} else {
+			prev = entry;
+			entry = entry->next;
+		}
+	}
+	if (removed)
+		pmksa_cache_set_expiration(pmksa);
+}
+
+
+/**
+ * pmksa_cache_deinit - Free all entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ */
+void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
+{
+	struct rsn_pmksa_cache_entry *entry, *prev;
+
+	if (pmksa == NULL)
+		return;
+
+	entry = pmksa->pmksa;
+	pmksa->pmksa = NULL;
+	while (entry) {
+		prev = entry;
+		entry = entry->next;
+		os_free(prev);
+	}
+	pmksa_cache_set_expiration(pmksa);
+	os_free(pmksa);
+}
+
+
+/**
+ * pmksa_cache_get - Fetch a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @aa: Authenticator address or %NULL to match any
+ * @pmkid: PMKID or %NULL to match any
+ * @network_ctx: Network context or %NULL to match any
+ * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
+ */
+struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
+					       const u8 *aa, const u8 *pmkid,
+					       const void *network_ctx)
+{
+	struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+	while (entry) {
+		if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
+		    (pmkid == NULL ||
+		     os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) &&
+		    (network_ctx == NULL || network_ctx == entry->network_ctx))
+			return entry;
+		entry = entry->next;
+	}
+	return NULL;
+}
+
+
+static struct rsn_pmksa_cache_entry *
+pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
+			const struct rsn_pmksa_cache_entry *old_entry,
+			const u8 *aa)
+{
+	struct rsn_pmksa_cache_entry *new_entry;
+
+	new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
+				    NULL, 0,
+				    aa, pmksa->sm->own_addr,
+				    old_entry->network_ctx, old_entry->akmp);
+	if (new_entry == NULL)
+		return NULL;
+
+	/* TODO: reorder entries based on expiration time? */
+	new_entry->expiration = old_entry->expiration;
+	new_entry->opportunistic = 1;
+
+	return new_entry;
+}
+
+
+/**
+ * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @network_ctx: Network configuration context
+ * @aa: Authenticator address for the new AP
+ * Returns: Pointer to a new PMKSA cache entry or %NULL if not available
+ *
+ * Try to create a new PMKSA cache entry opportunistically by guessing that the
+ * new AP is sharing the same PMK as another AP that has the same SSID and has
+ * already an entry in PMKSA cache.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
+			      const u8 *aa)
+{
+	struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+
+	wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa));
+	if (network_ctx == NULL)
+		return NULL;
+	while (entry) {
+		if (entry->network_ctx == network_ctx) {
+			entry = pmksa_cache_clone_entry(pmksa, entry, aa);
+			if (entry) {
+				wpa_printf(MSG_DEBUG, "RSN: added "
+					   "opportunistic PMKSA cache entry "
+					   "for " MACSTR, MAC2STR(aa));
+			}
+			return entry;
+		}
+		entry = entry->next;
+	}
+	return NULL;
+}
+
+
+/**
+ * pmksa_cache_get_current - Get the current used PMKSA entry
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: Pointer to the current PMKSA cache entry or %NULL if not available
+ */
+struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm)
+{
+	if (sm == NULL)
+		return NULL;
+	return sm->cur_pmksa;
+}
+
+
+/**
+ * pmksa_cache_clear_current - Clear the current PMKSA entry selection
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void pmksa_cache_clear_current(struct wpa_sm *sm)
+{
+	if (sm == NULL)
+		return;
+	sm->cur_pmksa = NULL;
+}
+
+
+/**
+ * pmksa_cache_set_current - Set the current PMKSA entry selection
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @pmkid: PMKID for selecting PMKSA or %NULL if not used
+ * @bssid: BSSID for PMKSA or %NULL if not used
+ * @network_ctx: Network configuration context
+ * @try_opportunistic: Whether to allow opportunistic PMKSA caching
+ * Returns: 0 if PMKSA was found or -1 if no matching entry was found
+ */
+int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+			    const u8 *bssid, void *network_ctx,
+			    int try_opportunistic)
+{
+	struct rsn_pmksa_cache *pmksa = sm->pmksa;
+	wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p "
+		   "try_opportunistic=%d", network_ctx, try_opportunistic);
+	if (pmkid)
+		wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID",
+			    pmkid, PMKID_LEN);
+	if (bssid)
+		wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR,
+			   MAC2STR(bssid));
+
+	sm->cur_pmksa = NULL;
+	if (pmkid)
+		sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid,
+						network_ctx);
+	if (sm->cur_pmksa == NULL && bssid)
+		sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL,
+						network_ctx);
+	if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
+		sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
+							      network_ctx,
+							      bssid);
+	if (sm->cur_pmksa) {
+		wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID",
+			    sm->cur_pmksa->pmkid, PMKID_LEN);
+		return 0;
+	}
+	wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found");
+	return -1;
+}
+
+
+/**
+ * pmksa_cache_list - Dump text list of entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @buf: Buffer for the list
+ * @len: Length of the buffer
+ * Returns: number of bytes written to buffer
+ *
+ * This function is used to generate a text format representation of the
+ * current PMKSA cache contents for the ctrl_iface PMKSA command.
+ */
+int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+{
+	int i, ret;
+	char *pos = buf;
+	struct rsn_pmksa_cache_entry *entry;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+	ret = os_snprintf(pos, buf + len - pos,
+			  "Index / AA / PMKID / expiration (in seconds) / "
+			  "opportunistic\n");
+	if (os_snprintf_error(buf + len - pos, ret))
+		return pos - buf;
+	pos += ret;
+	i = 0;
+	entry = pmksa->pmksa;
+	while (entry) {
+		i++;
+		ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
+				  i, MAC2STR(entry->aa));
+		if (os_snprintf_error(buf + len - pos, ret))
+			return pos - buf;
+		pos += ret;
+		pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
+					PMKID_LEN);
+		ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
+				  (int) (entry->expiration - now.sec),
+				  entry->opportunistic);
+		if (os_snprintf_error(buf + len - pos, ret))
+			return pos - buf;
+		pos += ret;
+		entry = entry->next;
+	}
+	return pos - buf;
+}
+
+
+/**
+ * pmksa_cache_init - Initialize PMKSA cache
+ * @free_cb: Callback function to be called when a PMKSA cache entry is freed
+ * @ctx: Context pointer for free_cb function
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: Pointer to PMKSA cache data or %NULL on failure
+ */
+struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+				 void *ctx, enum pmksa_free_reason reason),
+		 void *ctx, struct wpa_sm *sm)
+{
+	struct rsn_pmksa_cache *pmksa;
+
+	pmksa = os_zalloc(sizeof(*pmksa));
+	if (pmksa) {
+		pmksa->free_cb = free_cb;
+		pmksa->ctx = ctx;
+		pmksa->sm = sm;
+	}
+
+	return pmksa;
+}
+
+#endif /* IEEE8021X_EAPOL */
diff --git a/hostap/src/rsn_supp/pmksa_cache.h b/hostap/src/rsn_supp/pmksa_cache.h
new file mode 100644
index 0000000..f8e040e
--- /dev/null
+++ b/hostap/src/rsn_supp/pmksa_cache.h
@@ -0,0 +1,134 @@
+/*
+ * wpa_supplicant - WPA2/RSN PMKSA cache functions
+ * Copyright (c) 2003-2009, 2011-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PMKSA_CACHE_H
+#define PMKSA_CACHE_H
+
+/**
+ * struct rsn_pmksa_cache_entry - PMKSA cache entry
+ */
+struct rsn_pmksa_cache_entry {
+	struct rsn_pmksa_cache_entry *next;
+	u8 pmkid[PMKID_LEN];
+	u8 pmk[PMK_LEN];
+	size_t pmk_len;
+	os_time_t expiration;
+	int akmp; /* WPA_KEY_MGMT_* */
+	u8 aa[ETH_ALEN];
+
+	os_time_t reauth_time;
+
+	/**
+	 * network_ctx - Network configuration context
+	 *
+	 * This field is only used to match PMKSA cache entries to a specific
+	 * network configuration (e.g., a specific SSID and security policy).
+	 * This can be a pointer to the configuration entry, but PMKSA caching
+	 * code does not dereference the value and this could be any kind of
+	 * identifier.
+	 */
+	void *network_ctx;
+	int opportunistic;
+};
+
+struct rsn_pmksa_cache;
+
+enum pmksa_free_reason {
+	PMKSA_FREE,
+	PMKSA_REPLACE,
+	PMKSA_EXPIRE,
+};
+
+#ifdef IEEE8021X_EAPOL
+
+struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+				 void *ctx, enum pmksa_free_reason reason),
+		 void *ctx, struct wpa_sm *sm);
+void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
+					       const u8 *aa, const u8 *pmkid,
+					       const void *network_ctx);
+int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+		const u8 *kck, size_t kck_len,
+		const u8 *aa, const u8 *spa, void *network_ctx, int akmp);
+struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm);
+void pmksa_cache_clear_current(struct wpa_sm *sm);
+int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+			    const u8 *bssid, void *network_ctx,
+			    int try_opportunistic);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
+			      void *network_ctx, const u8 *aa);
+void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
+		       const u8 *pmk, size_t pmk_len);
+
+#else /* IEEE8021X_EAPOL */
+
+static inline struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+				 void *ctx, enum pmksa_free_reason reason),
+		 void *ctx, struct wpa_sm *sm)
+{
+	return (void *) -1;
+}
+
+static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
+{
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid,
+		const void *network_ctx)
+{
+	return NULL;
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_get_current(struct wpa_sm *sm)
+{
+	return NULL;
+}
+
+static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf,
+				   size_t len)
+{
+	return -1;
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+		const u8 *kck, size_t kck_len,
+		const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
+{
+	return NULL;
+}
+
+static inline void pmksa_cache_clear_current(struct wpa_sm *sm)
+{
+}
+
+static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+					  const u8 *bssid,
+					  void *network_ctx,
+					  int try_opportunistic)
+{
+	return -1;
+}
+
+static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa,
+				     void *network_ctx,
+				     const u8 *pmk, size_t pmk_len)
+{
+}
+
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* PMKSA_CACHE_H */
diff --git a/hostap/src/rsn_supp/preauth.c b/hostap/src/rsn_supp/preauth.c
new file mode 100644
index 0000000..c6534af
--- /dev/null
+++ b/hostap/src/rsn_supp/preauth.c
@@ -0,0 +1,541 @@
+/*
+ * RSN pre-authentication (supplicant)
+ * Copyright (c) 2003-2015, 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 "wpa.h"
+#include "eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "preauth.h"
+#include "pmksa_cache.h"
+#include "wpa_i.h"
+
+
+#ifdef IEEE8021X_EAPOL
+
+#define PMKID_CANDIDATE_PRIO_SCAN 1000
+
+
+struct rsn_pmksa_candidate {
+	struct dl_list list;
+	u8 bssid[ETH_ALEN];
+	int priority;
+};
+
+
+/**
+ * pmksa_candidate_free - Free all entries in PMKSA candidate list
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void pmksa_candidate_free(struct wpa_sm *sm)
+{
+	struct rsn_pmksa_candidate *entry, *n;
+
+	if (sm == NULL)
+		return;
+
+	dl_list_for_each_safe(entry, n, &sm->pmksa_candidates,
+			      struct rsn_pmksa_candidate, list) {
+		dl_list_del(&entry->list);
+		os_free(entry);
+	}
+}
+
+
+static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
+				const u8 *buf, size_t len)
+{
+	struct wpa_sm *sm = ctx;
+
+	wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr));
+	wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len);
+
+	if (sm->preauth_eapol == NULL ||
+	    is_zero_ether_addr(sm->preauth_bssid) ||
+	    os_memcmp(sm->preauth_bssid, src_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_WARNING, "RSN pre-auth frame received from "
+			   "unexpected source " MACSTR " - dropped",
+			   MAC2STR(src_addr));
+		return;
+	}
+
+	eapol_sm_rx_eapol(sm->preauth_eapol, src_addr, buf, len);
+}
+
+
+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 (result == EAPOL_SUPP_RESULT_SUCCESS) {
+		int res, pmk_len;
+		pmk_len = PMK_LEN;
+		res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
+		if (res) {
+			/*
+			 * EAP-LEAP is an exception from other EAP methods: it
+			 * uses only 16-byte PMK.
+			 */
+			res = eapol_sm_get_key(eapol, pmk, 16);
+			pmk_len = 16;
+		}
+		if (res == 0) {
+			wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth",
+					pmk, pmk_len);
+			sm->pmk_len = pmk_len;
+			pmksa_cache_add(sm->pmksa, pmk, pmk_len,
+					NULL, 0,
+					sm->preauth_bssid, sm->own_addr,
+					sm->network_ctx,
+					WPA_KEY_MGMT_IEEE8021X);
+		} else {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"RSN: failed to get master session key from "
+				"pre-auth EAPOL state machines");
+			result = EAPOL_SUPP_RESULT_FAILURE;
+		}
+	}
+
+	wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with "
+		MACSTR " %s", MAC2STR(sm->preauth_bssid),
+		result == EAPOL_SUPP_RESULT_SUCCESS ? "completed successfully" :
+		"failed");
+
+	rsn_preauth_deinit(sm);
+	rsn_preauth_candidate_process(sm);
+}
+
+
+static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_sm *sm = eloop_ctx;
+
+	wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with "
+		MACSTR " timed out", MAC2STR(sm->preauth_bssid));
+	rsn_preauth_deinit(sm);
+	rsn_preauth_candidate_process(sm);
+}
+
+
+static int rsn_preauth_eapol_send(void *ctx, int type, const u8 *buf,
+				  size_t len)
+{
+	struct wpa_sm *sm = ctx;
+	u8 *msg;
+	size_t msglen;
+	int res;
+
+	/* TODO: could add l2_packet_sendmsg that allows fragments to avoid
+	 * extra copy here */
+
+	if (sm->l2_preauth == NULL)
+		return -1;
+
+	msg = wpa_sm_alloc_eapol(sm, type, buf, len, &msglen, NULL);
+	if (msg == NULL)
+		return -1;
+
+	wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen);
+	res = l2_packet_send(sm->l2_preauth, sm->preauth_bssid,
+			     ETH_P_RSN_PREAUTH, msg, msglen);
+	os_free(msg);
+	return res;
+}
+
+
+/**
+ * rsn_preauth_init - Start new RSN pre-authentication
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @dst: Authenticator address (BSSID) with which to preauthenticate
+ * @eap_conf: Current EAP configuration
+ * Returns: 0 on success, -1 on another pre-authentication is in progress,
+ * -2 on layer 2 packet initialization failure, -3 on EAPOL state machine
+ * initialization failure, -4 on memory allocation failure
+ *
+ * This function request an RSN pre-authentication with a given destination
+ * address. This is usually called for PMKSA candidates found from scan results
+ * or from driver reports. In addition, ctrl_iface PREAUTH command can trigger
+ * pre-authentication.
+ */
+int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+		     struct eap_peer_config *eap_conf)
+{
+	struct eapol_config eapol_conf;
+	struct eapol_ctx *ctx;
+	int ret;
+
+	if (sm->preauth_eapol)
+		return -1;
+
+	wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"RSN: starting pre-authentication with " MACSTR, MAC2STR(dst));
+
+	sm->l2_preauth = l2_packet_init(sm->ifname, sm->own_addr,
+					ETH_P_RSN_PREAUTH,
+					rsn_preauth_receive, sm, 0);
+	if (sm->l2_preauth == NULL) {
+		wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet "
+			   "processing for pre-authentication");
+		return -2;
+	}
+
+	if (sm->bridge_ifname) {
+		sm->l2_preauth_br = l2_packet_init(sm->bridge_ifname,
+						   sm->own_addr,
+						   ETH_P_RSN_PREAUTH,
+						   rsn_preauth_receive, sm, 0);
+		if (sm->l2_preauth_br == NULL) {
+			wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 "
+				   "packet processing (bridge) for "
+				   "pre-authentication");
+			ret = -2;
+			goto fail;
+		}
+	}
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL) {
+		wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context.");
+		ret = -4;
+		goto fail;
+	}
+	ctx->ctx = sm->ctx->ctx;
+	ctx->msg_ctx = sm->ctx->ctx;
+	ctx->preauth = 1;
+	ctx->cb = rsn_preauth_eapol_cb;
+	ctx->cb_ctx = sm;
+	ctx->scard_ctx = sm->scard_ctx;
+	ctx->eapol_send = rsn_preauth_eapol_send;
+	ctx->eapol_send_ctx = sm;
+	ctx->set_config_blob = sm->ctx->set_config_blob;
+	ctx->get_config_blob = sm->ctx->get_config_blob;
+
+	sm->preauth_eapol = eapol_sm_init(ctx);
+	if (sm->preauth_eapol == NULL) {
+		os_free(ctx);
+		wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL "
+			   "state machines for pre-authentication");
+		ret = -3;
+		goto fail;
+	}
+	os_memset(&eapol_conf, 0, sizeof(eapol_conf));
+	eapol_conf.accept_802_1x_keys = 0;
+	eapol_conf.required_keys = 0;
+	eapol_conf.fast_reauth = sm->fast_reauth;
+	eapol_conf.workaround = sm->eap_workaround;
+	eapol_sm_notify_config(sm->preauth_eapol, eap_conf, &eapol_conf);
+	/*
+	 * Use a shorter startPeriod with preauthentication since the first
+	 * preauth EAPOL-Start frame may end up being dropped due to race
+	 * condition in the AP between the data receive and key configuration
+	 * after the 4-Way Handshake.
+	 */
+	eapol_sm_configure(sm->preauth_eapol, -1, -1, 5, 6);
+	os_memcpy(sm->preauth_bssid, dst, ETH_ALEN);
+
+	eapol_sm_notify_portValid(sm->preauth_eapol, TRUE);
+	/* 802.1X::portControl = Auto */
+	eapol_sm_notify_portEnabled(sm->preauth_eapol, TRUE);
+
+	eloop_register_timeout(sm->dot11RSNAConfigSATimeout, 0,
+			       rsn_preauth_timeout, sm, NULL);
+
+	return 0;
+
+fail:
+	if (sm->l2_preauth_br) {
+		l2_packet_deinit(sm->l2_preauth_br);
+		sm->l2_preauth_br = NULL;
+	}
+	l2_packet_deinit(sm->l2_preauth);
+	sm->l2_preauth = NULL;
+	return ret;
+}
+
+
+/**
+ * rsn_preauth_deinit - Abort RSN pre-authentication
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * This function aborts the current RSN pre-authentication (if one is started)
+ * and frees resources allocated for it.
+ */
+void rsn_preauth_deinit(struct wpa_sm *sm)
+{
+	if (sm == NULL || !sm->preauth_eapol)
+		return;
+
+	eloop_cancel_timeout(rsn_preauth_timeout, sm, NULL);
+	eapol_sm_deinit(sm->preauth_eapol);
+	sm->preauth_eapol = NULL;
+	os_memset(sm->preauth_bssid, 0, ETH_ALEN);
+
+	l2_packet_deinit(sm->l2_preauth);
+	sm->l2_preauth = NULL;
+	if (sm->l2_preauth_br) {
+		l2_packet_deinit(sm->l2_preauth_br);
+		sm->l2_preauth_br = NULL;
+	}
+}
+
+
+/**
+ * rsn_preauth_candidate_process - Process PMKSA candidates
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * Go through the PMKSA candidates and start pre-authentication if a candidate
+ * without an existing PMKSA cache entry is found. Processed candidates will be
+ * removed from the list.
+ */
+void rsn_preauth_candidate_process(struct wpa_sm *sm)
+{
+	struct rsn_pmksa_candidate *candidate, *n;
+
+	if (dl_list_empty(&sm->pmksa_candidates))
+		return;
+
+	/* TODO: drop priority for old candidate entries */
+
+	wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: processing PMKSA candidate "
+		"list");
+	if (sm->preauth_eapol ||
+	    sm->proto != WPA_PROTO_RSN ||
+	    wpa_sm_get_state(sm) != WPA_COMPLETED ||
+	    (sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X &&
+	     sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256 &&
+	     sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SUITE_B &&
+	     sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable "
+			"state for new pre-authentication");
+		return; /* invalid state for new pre-auth */
+	}
+
+	dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates,
+			      struct rsn_pmksa_candidate, list) {
+		struct rsn_pmksa_cache_entry *p = NULL;
+		p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL);
+		if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 &&
+		    (p == NULL || p->opportunistic)) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA "
+				"candidate " MACSTR
+				" selected for pre-authentication",
+				MAC2STR(candidate->bssid));
+			dl_list_del(&candidate->list);
+			rsn_preauth_init(sm, candidate->bssid,
+					 sm->eap_conf_ctx);
+			os_free(candidate);
+			return;
+		}
+		wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA candidate "
+			MACSTR " does not need pre-authentication anymore",
+			MAC2STR(candidate->bssid));
+		/* Some drivers (e.g., NDIS) expect to get notified about the
+		 * PMKIDs again, so report the existing data now. */
+		if (p) {
+			wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid);
+		}
+
+		dl_list_del(&candidate->list);
+		os_free(candidate);
+	}
+	wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no more pending PMKSA "
+		"candidates");
+}
+
+
+/**
+ * pmksa_candidate_add - Add a new PMKSA candidate
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @bssid: BSSID (authenticator address) of the candidate
+ * @prio: Priority (the smaller number, the higher priority)
+ * @preauth: Whether the candidate AP advertises support for pre-authentication
+ *
+ * This function is used to add PMKSA candidates for RSN pre-authentication. It
+ * is called from scan result processing and from driver events for PMKSA
+ * candidates, i.e., EVENT_PMKID_CANDIDATE events to wpa_supplicant_event().
+ */
+void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid,
+			 int prio, int preauth)
+{
+	struct rsn_pmksa_candidate *cand, *pos;
+
+	if (sm->network_ctx && sm->proactive_key_caching)
+		pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx,
+					      bssid);
+
+	if (!preauth) {
+		wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without "
+			   "preauth flag");
+		return;
+	}
+
+	/* If BSSID already on candidate list, update the priority of the old
+	 * entry. Do not override priority based on normal scan results. */
+	cand = NULL;
+	dl_list_for_each(pos, &sm->pmksa_candidates,
+			 struct rsn_pmksa_candidate, list) {
+		if (os_memcmp(pos->bssid, bssid, ETH_ALEN) == 0) {
+			cand = pos;
+			break;
+		}
+	}
+
+	if (cand) {
+		dl_list_del(&cand->list);
+		if (prio < PMKID_CANDIDATE_PRIO_SCAN)
+			cand->priority = prio;
+	} else {
+		cand = os_zalloc(sizeof(*cand));
+		if (cand == NULL)
+			return;
+		os_memcpy(cand->bssid, bssid, ETH_ALEN);
+		cand->priority = prio;
+	}
+
+	/* Add candidate to the list; order by increasing priority value. i.e.,
+	 * highest priority (smallest value) first. */
+	dl_list_for_each(pos, &sm->pmksa_candidates,
+			 struct rsn_pmksa_candidate, list) {
+		if (cand->priority <= pos->priority) {
+			if (!pos->list.prev) {
+				/*
+				 * This cannot really happen in pracrice since
+				 * pos was fetched from the list and the prev
+				 * pointer must be set. It looks like clang
+				 * static analyzer gets confused with the
+				 * dl_list_del(&cand->list) call above and ends
+				 * up assuming pos->list.prev could be NULL.
+				 */
+				os_free(cand);
+				return;
+			}
+			dl_list_add(pos->list.prev, &cand->list);
+			cand = NULL;
+			break;
+		}
+	}
+	if (cand)
+		dl_list_add_tail(&sm->pmksa_candidates, &cand->list);
+
+	wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: added PMKSA cache "
+		"candidate " MACSTR " prio %d", MAC2STR(bssid), prio);
+	rsn_preauth_candidate_process(sm);
+}
+
+
+/* TODO: schedule periodic scans if current AP supports preauth */
+
+/**
+ * rsn_preauth_scan_results - Start processing scan results for canditates
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: 0 if ready to process results or -1 to skip processing
+ *
+ * This functions is used to notify RSN code about start of new scan results
+ * processing. The actual scan results will be provided by calling
+ * rsn_preauth_scan_result() for each BSS if this function returned 0.
+ */
+int rsn_preauth_scan_results(struct wpa_sm *sm)
+{
+	if (sm->ssid_len == 0)
+		return -1;
+
+	/*
+	 * TODO: is it ok to free all candidates? What about the entries
+	 * received from EVENT_PMKID_CANDIDATE?
+	 */
+	pmksa_candidate_free(sm);
+
+	return 0;
+}
+
+
+/**
+ * rsn_preauth_scan_result - Processing scan result for PMKSA canditates
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * Add all suitable APs (Authenticators) from scan results into PMKSA
+ * candidate list.
+ */
+void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
+			     const u8 *ssid, const u8 *rsn)
+{
+	struct wpa_ie_data ie;
+	struct rsn_pmksa_cache_entry *pmksa;
+
+	if (ssid[1] != sm->ssid_len ||
+	    os_memcmp(ssid + 2, sm->ssid, sm->ssid_len) != 0)
+		return; /* Not for the current SSID */
+
+	if (os_memcmp(bssid, sm->bssid, ETH_ALEN) == 0)
+		return; /* Ignore current AP */
+
+	if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie))
+		return;
+
+	pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL);
+	if (pmksa && (!pmksa->opportunistic ||
+		      !(ie.capabilities & WPA_CAPABILITY_PREAUTH)))
+		return;
+
+	/* Give less priority to candidates found from normal scan results. */
+	pmksa_candidate_add(sm, bssid, PMKID_CANDIDATE_PRIO_SCAN,
+			    ie.capabilities & WPA_CAPABILITY_PREAUTH);
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+/**
+ * rsn_preauth_get_status - Get pre-authentication status
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query WPA2 pre-authentication for status information. This function fills in
+ * a text area with current status information. If the buffer (buf) is not
+ * large enough, status information will be truncated to fit the buffer.
+ */
+int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+			   int verbose)
+{
+	char *pos = buf, *end = buf + buflen;
+	int res, ret;
+
+	if (sm->preauth_eapol) {
+		ret = os_snprintf(pos, end - pos, "Pre-authentication "
+				  "EAPOL state machines:\n");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+		res = eapol_sm_get_status(sm->preauth_eapol,
+					  pos, end - pos, verbose);
+		if (res >= 0)
+			pos += res;
+	}
+
+	return pos - buf;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+/**
+ * rsn_preauth_in_progress - Verify whether pre-authentication is in progress
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+int rsn_preauth_in_progress(struct wpa_sm *sm)
+{
+	return sm->preauth_eapol != NULL;
+}
+
+#endif /* IEEE8021X_EAPOL */
diff --git a/hostap/src/rsn_supp/preauth.h b/hostap/src/rsn_supp/preauth.h
new file mode 100644
index 0000000..277f066
--- /dev/null
+++ b/hostap/src/rsn_supp/preauth.h
@@ -0,0 +1,79 @@
+/*
+ * wpa_supplicant - WPA2/RSN pre-authentication functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PREAUTH_H
+#define PREAUTH_H
+
+struct wpa_scan_results;
+
+#ifdef IEEE8021X_EAPOL
+
+void pmksa_candidate_free(struct wpa_sm *sm);
+int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+		     struct eap_peer_config *eap_conf);
+void rsn_preauth_deinit(struct wpa_sm *sm);
+int rsn_preauth_scan_results(struct wpa_sm *sm);
+void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
+			     const u8 *ssid, const u8 *rsn);
+void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid,
+			 int prio, int preauth);
+void rsn_preauth_candidate_process(struct wpa_sm *sm);
+int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+			   int verbose);
+int rsn_preauth_in_progress(struct wpa_sm *sm);
+
+#else /* IEEE8021X_EAPOL */
+
+static inline void pmksa_candidate_free(struct wpa_sm *sm)
+{
+}
+
+static inline void rsn_preauth_candidate_process(struct wpa_sm *sm)
+{
+}
+
+static inline int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+				   struct eap_peer_config *eap_conf)
+{
+	return -1;
+}
+
+static inline void rsn_preauth_deinit(struct wpa_sm *sm)
+{
+}
+
+static inline int rsn_preauth_scan_results(struct wpa_sm *sm)
+{
+	return -1;
+}
+
+static inline void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
+					   const u8 *ssid, const u8 *rsn)
+{
+}
+
+static inline void pmksa_candidate_add(struct wpa_sm *sm,
+				       const u8 *bssid,
+				       int prio, int preauth)
+{
+}
+
+static inline int rsn_preauth_get_status(struct wpa_sm *sm, char *buf,
+					 size_t buflen, int verbose)
+{
+	return 0;
+}
+
+static inline int rsn_preauth_in_progress(struct wpa_sm *sm)
+{
+	return 0;
+}
+
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* PREAUTH_H */
diff --git a/hostap/src/rsn_supp/tdls.c b/hostap/src/rsn_supp/tdls.c
new file mode 100644
index 0000000..722c20a
--- /dev/null
+++ b/hostap/src/rsn_supp/tdls.c
@@ -0,0 +1,3008 @@
+/*
+ * wpa_supplicant - TDLS
+ * Copyright (c) 2010-2011, Atheros Communications
+ *
+ * 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 "utils/os.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/sha256.h"
+#include "crypto/crypto.h"
+#include "crypto/aes_wrap.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/wpa_ie.h"
+#include "rsn_supp/wpa_i.h"
+#include "drivers/driver.h"
+#include "l2_packet/l2_packet.h"
+
+#ifdef CONFIG_TDLS_TESTING
+#define TDLS_TESTING_LONG_FRAME BIT(0)
+#define TDLS_TESTING_ALT_RSN_IE BIT(1)
+#define TDLS_TESTING_DIFF_BSSID BIT(2)
+#define TDLS_TESTING_SHORT_LIFETIME BIT(3)
+#define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4)
+#define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5)
+#define TDLS_TESTING_LONG_LIFETIME BIT(6)
+#define TDLS_TESTING_CONCURRENT_INIT BIT(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 */
+
+#define TPK_LIFETIME 43200 /* 12 hours */
+#define TPK_M1_RETRY_COUNT 3
+#define TPK_M1_TIMEOUT 5000 /* in milliseconds */
+#define TPK_M2_RETRY_COUNT 10
+#define TPK_M2_TIMEOUT 500 /* in milliseconds */
+
+#define TDLS_MIC_LEN		16
+
+#define TDLS_TIMEOUT_LEN	4
+
+struct wpa_tdls_ftie {
+	u8 ie_type; /* FTIE */
+	u8 ie_len;
+	u8 mic_ctrl[2];
+	u8 mic[TDLS_MIC_LEN];
+	u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */
+	u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */
+	/* followed by optional elements */
+} STRUCT_PACKED;
+
+struct wpa_tdls_timeoutie {
+	u8 ie_type; /* Timeout IE */
+	u8 ie_len;
+	u8 interval_type;
+	u8 value[TDLS_TIMEOUT_LEN];
+} STRUCT_PACKED;
+
+struct wpa_tdls_lnkid {
+	u8 ie_type; /* Link Identifier IE */
+	u8 ie_len;
+	u8 bssid[ETH_ALEN];
+	u8 init_sta[ETH_ALEN];
+	u8 resp_sta[ETH_ALEN];
+} STRUCT_PACKED;
+
+/* TDLS frame headers as per IEEE Std 802.11z-2010 */
+struct wpa_tdls_frame {
+	u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */
+	u8 category; /* Category */
+	u8 action; /* Action (enum tdls_frame_type) */
+} STRUCT_PACKED;
+
+static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs);
+static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx);
+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
+#define IEEE80211_MAX_SUPP_RATES 32
+
+struct wpa_tdls_peer {
+	struct wpa_tdls_peer *next;
+	unsigned int reconfig_key:1;
+	int initiator; /* whether this end was initiator for TDLS setup */
+	u8 addr[ETH_ALEN]; /* other end MAC address */
+	u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
+	u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */
+	u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */
+	size_t rsnie_i_len;
+	u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */
+	size_t rsnie_p_len;
+	u32 lifetime;
+	int cipher; /* Selected cipher (WPA_CIPHER_*) */
+	u8 dtoken;
+
+	struct tpk {
+		u8 kck[16]; /* TPK-KCK */
+		u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */
+	} tpk;
+	int tpk_set;
+	int tpk_success;
+	int tpk_in_progress;
+
+	struct tpk_timer {
+		u8 dest[ETH_ALEN];
+		int count;      /* Retry Count */
+		int timer;      /* Timeout in milliseconds */
+		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;
+
+	u16 capability;
+
+	u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
+	size_t supp_rates_len;
+
+	struct ieee80211_ht_capabilities *ht_capabilities;
+	struct ieee80211_vht_capabilities *vht_capabilities;
+
+	u8 qos_info;
+
+	u16 aid;
+
+	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;
+
+	/* channel switch currently enabled */
+	int chan_switch_enabled;
+};
+
+
+static int wpa_tdls_get_privacy(struct wpa_sm *sm)
+{
+	/*
+	 * Get info needed from supplicant to check if the current BSS supports
+	 * security. Other than OPEN mode, rest are considered secured
+	 * WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake.
+	 */
+	return sm->pairwise_cipher != WPA_CIPHER_NONE;
+}
+
+
+static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
+{
+	os_memcpy(pos, ie, ie_len);
+	return pos + ie_len;
+}
+
+
+static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+	if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr,
+			   0, 0, NULL, 0, NULL, 0) < 0) {
+		wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
+			   "the driver");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+	u8 key_len;
+	u8 rsc[6];
+	enum wpa_alg alg;
+
+	os_memset(rsc, 0, 6);
+
+	switch (peer->cipher) {
+	case WPA_CIPHER_CCMP:
+		alg = WPA_ALG_CCMP;
+		key_len = 16;
+		break;
+	case WPA_CIPHER_NONE:
+		wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: "
+			   "NONE - do not use pairwise keys");
+		return -1;
+	default:
+		wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d",
+			   sm->pairwise_cipher);
+		return -1;
+	}
+
+	if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1,
+			   rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) {
+		wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
+			   "driver");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst,
+				 u8 action_code, u8 dialog_token,
+				 u16 status_code, u32 peer_capab,
+				 int initiator, const u8 *buf, size_t len)
+{
+	return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token,
+				     status_code, peer_capab, initiator, buf,
+				     len);
+}
+
+
+static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
+			     u8 dialog_token, u16 status_code, u32 peer_capab,
+			     int initiator, 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 peer_capab=%u initiator=%d "
+		   "msg_len=%u",
+		   MAC2STR(dest), action_code, dialog_token, status_code,
+		   peer_capab, initiator, (unsigned int) msg_len);
+
+	if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
+				  status_code, peer_capab, initiator, msg,
+				  msg_len)) {
+		wpa_printf(MSG_INFO, "TDLS: Failed to send message "
+			   "(action_code=%u)", action_code);
+		return -1;
+	}
+
+	if (action_code == WLAN_TDLS_SETUP_CONFIRM ||
+	    action_code == WLAN_TDLS_TEARDOWN ||
+	    action_code == WLAN_TDLS_DISCOVERY_REQUEST ||
+	    action_code == WLAN_TDLS_DISCOVERY_RESPONSE)
+		return 0; /* No retries */
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
+			   "retry " MACSTR, MAC2STR(dest));
+		return 0;
+	}
+
+	eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+
+	if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
+		peer->sm_tmr.count = TPK_M2_RETRY_COUNT;
+		peer->sm_tmr.timer = TPK_M2_TIMEOUT;
+	} else {
+		peer->sm_tmr.count = TPK_M1_RETRY_COUNT;
+		peer->sm_tmr.timer = TPK_M1_TIMEOUT;
+	}
+
+	/* Copy message to resend on timeout */
+	os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN);
+	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);
+	if (peer->sm_tmr.buf == NULL)
+		return -1;
+	os_memcpy(peer->sm_tmr.buf, msg, msg_len);
+
+	wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered "
+		   "(action_code=%u)", action_code);
+	eloop_register_timeout(peer->sm_tmr.timer / 1000,
+			       (peer->sm_tmr.timer % 1000) * 1000,
+			       wpa_tdls_tpk_retry_timeout, sm, peer);
+	return 0;
+}
+
+
+static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
+				u16 reason_code)
+{
+	int ret;
+
+	ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code);
+	/* disable the link after teardown was sent */
+	wpa_tdls_disable_peer_link(sm, peer);
+
+	return ret;
+}
+
+
+static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+
+	struct wpa_sm *sm = eloop_ctx;
+	struct wpa_tdls_peer *peer = timeout_ctx;
+
+	if (peer->sm_tmr.count) {
+		peer->sm_tmr.count--;
+
+		wpa_printf(MSG_INFO, "TDLS: Retrying sending of message "
+			   "(action_code=%u)",
+			   peer->sm_tmr.action_code);
+
+		if (peer->sm_tmr.buf == NULL) {
+			wpa_printf(MSG_INFO, "TDLS: No retry buffer available "
+				   "for action_code=%u",
+				   peer->sm_tmr.action_code);
+			eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm,
+					     peer);
+			return;
+		}
+
+		/* resend TPK Handshake Message to Peer */
+		if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest,
+					  peer->sm_tmr.action_code,
+					  peer->sm_tmr.dialog_token,
+					  peer->sm_tmr.status_code,
+					  peer->sm_tmr.peer_capab,
+					  peer->initiator,
+					  peer->sm_tmr.buf,
+					  peer->sm_tmr.buf_len)) {
+			wpa_printf(MSG_INFO, "TDLS: Failed to retry "
+				   "transmission");
+		}
+
+		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+		eloop_register_timeout(peer->sm_tmr.timer / 1000,
+				       (peer->sm_tmr.timer % 1000) * 1000,
+				       wpa_tdls_tpk_retry_timeout, sm, peer);
+	} else {
+		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+
+		wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request");
+		wpa_tdls_do_teardown(sm, peer,
+				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+	}
+}
+
+
+static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm,
+					      struct wpa_tdls_peer *peer,
+					      u8 action_code)
+{
+	if (action_code == peer->sm_tmr.action_code) {
+		wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for "
+			   "action_code=%u", action_code);
+
+		/* Cancel Timeout registered */
+		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+
+		/* free all resources meant for retry */
+		os_free(peer->sm_tmr.buf);
+		peer->sm_tmr.buf = NULL;
+
+		peer->sm_tmr.count = 0;
+		peer->sm_tmr.timer = 0;
+		peer->sm_tmr.buf_len = 0;
+		peer->sm_tmr.action_code = 0xff;
+	} else {
+		wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout "
+			   "(Unknown action_code=%u)", action_code);
+	}
+}
+
+
+static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer,
+				  const u8 *own_addr, const u8 *bssid)
+{
+	u8 key_input[SHA256_MAC_LEN];
+	const u8 *nonce[2];
+	size_t len[2];
+	u8 data[3 * ETH_ALEN];
+
+	/* IEEE Std 802.11z-2010 8.5.9.1:
+	 * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce))
+	 */
+	len[0] = WPA_NONCE_LEN;
+	len[1] = WPA_NONCE_LEN;
+	if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) {
+		nonce[0] = peer->inonce;
+		nonce[1] = peer->rnonce;
+	} else {
+		nonce[0] = peer->rnonce;
+		nonce[1] = peer->inonce;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN);
+	sha256_vector(2, nonce, len, key_input);
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input",
+			key_input, SHA256_MAC_LEN);
+
+	/*
+	 * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK",
+	 *	min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY)
+	 * TODO: is N_KEY really included in KDF Context and if so, in which
+	 * presentation format (little endian 16-bit?) is it used? It gets
+	 * added by the KDF anyway..
+	 */
+
+	if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) {
+		os_memcpy(data, own_addr, ETH_ALEN);
+		os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN);
+	} else {
+		os_memcpy(data, peer->addr, ETH_ALEN);
+		os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN);
+	}
+	os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN);
+	wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data));
+
+	sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data),
+		   (u8 *) &peer->tpk, sizeof(peer->tpk));
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK",
+			peer->tpk.kck, sizeof(peer->tpk.kck));
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK",
+			peer->tpk.tk, sizeof(peer->tpk.tk));
+	peer->tpk_set = 1;
+}
+
+
+/**
+ * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC
+ * @kck: TPK-KCK
+ * @lnkid: Pointer to the beginning of Link Identifier IE
+ * @rsnie: Pointer to the beginning of RSN IE used for handshake
+ * @timeoutie: Pointer to the beginning of Timeout IE used for handshake
+ * @ftie: Pointer to the beginning of FT IE
+ * @mic: Pointer for writing MIC
+ *
+ * Calculate MIC for TDLS frame.
+ */
+static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid,
+			     const u8 *rsnie, const u8 *timeoutie,
+			     const u8 *ftie, u8 *mic)
+{
+	u8 *buf, *pos;
+	struct wpa_tdls_ftie *_ftie;
+	const struct wpa_tdls_lnkid *_lnkid;
+	int ret;
+	int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] +
+		2 + timeoutie[1] + 2 + ftie[1];
+	buf = os_zalloc(len);
+	if (!buf) {
+		wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
+		return -1;
+	}
+
+	pos = buf;
+	_lnkid = (const struct wpa_tdls_lnkid *) lnkid;
+	/* 1) TDLS initiator STA MAC address */
+	os_memcpy(pos, _lnkid->init_sta, ETH_ALEN);
+	pos += ETH_ALEN;
+	/* 2) TDLS responder STA MAC address */
+	os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN);
+	pos += ETH_ALEN;
+	/* 3) Transaction Sequence number */
+	*pos++ = trans_seq;
+	/* 4) Link Identifier IE */
+	os_memcpy(pos, lnkid, 2 + lnkid[1]);
+	pos += 2 + lnkid[1];
+	/* 5) RSN IE */
+	os_memcpy(pos, rsnie, 2 + rsnie[1]);
+	pos += 2 + rsnie[1];
+	/* 6) Timeout Interval IE */
+	os_memcpy(pos, timeoutie, 2 + timeoutie[1]);
+	pos += 2 + timeoutie[1];
+	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
+	os_memcpy(pos, ftie, 2 + ftie[1]);
+	_ftie = (struct wpa_tdls_ftie *) pos;
+	os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
+	pos += 2 + ftie[1];
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
+	ret = omac1_aes_128(kck, buf, pos - buf, mic);
+	os_free(buf);
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
+	return ret;
+}
+
+
+/**
+ * wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame
+ * @kck: TPK-KCK
+ * @trans_seq: Transaction Sequence Number (4 - Teardown)
+ * @rcode: Reason code for Teardown
+ * @dtoken: Dialog Token used for that particular link
+ * @lnkid: Pointer to the beginning of Link Identifier IE
+ * @ftie: Pointer to the beginning of FT IE
+ * @mic: Pointer for writing MIC
+ *
+ * Calculate MIC for TDLS frame.
+ */
+static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode,
+				     u8 dtoken, const u8 *lnkid,
+				     const u8 *ftie, u8 *mic)
+{
+	u8 *buf, *pos;
+	struct wpa_tdls_ftie *_ftie;
+	int ret;
+	int len;
+
+	if (lnkid == NULL)
+		return -1;
+
+	len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) +
+		sizeof(trans_seq) + 2 + ftie[1];
+
+	buf = os_zalloc(len);
+	if (!buf) {
+		wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
+		return -1;
+	}
+
+	pos = buf;
+	/* 1) Link Identifier IE */
+	os_memcpy(pos, lnkid, 2 + lnkid[1]);
+	pos += 2 + lnkid[1];
+	/* 2) Reason Code */
+	WPA_PUT_LE16(pos, rcode);
+	pos += sizeof(rcode);
+	/* 3) Dialog token */
+	*pos++ = dtoken;
+	/* 4) Transaction Sequence number */
+	*pos++ = trans_seq;
+	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
+	os_memcpy(pos, ftie, 2 + ftie[1]);
+	_ftie = (struct wpa_tdls_ftie *) pos;
+	os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
+	pos += 2 + ftie[1];
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
+	ret = omac1_aes_128(kck, buf, pos - buf, mic);
+	os_free(buf);
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
+	return ret;
+}
+
+
+static int wpa_supplicant_verify_tdls_mic(u8 trans_seq,
+					  struct wpa_tdls_peer *peer,
+					  const u8 *lnkid, const u8 *timeoutie,
+					  const struct wpa_tdls_ftie *ftie)
+{
+	u8 mic[16];
+
+	if (peer->tpk_set) {
+		wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
+				  peer->rsnie_p, timeoutie, (u8 *) ftie,
+				  mic);
+		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",
+				    ftie->mic, 16);
+			wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC",
+				    mic, 16);
+			return -1;
+		}
+	} else {
+		wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, "
+			   "TPK not set - dropping packet");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wpa_supplicant_verify_tdls_mic_teardown(
+	u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer,
+	const u8 *lnkid, const struct wpa_tdls_ftie *ftie)
+{
+	u8 mic[16];
+
+	if (peer->tpk_set) {
+		wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
+					  dtoken, lnkid, (u8 *) ftie, mic);
+		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
+			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
+				   "dropping packet");
+			return -1;
+		}
+	} else {
+		wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown "
+			   "MIC, TPK not set - dropping packet");
+		return -1;
+	}
+	return 0;
+}
+
+
+static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_sm *sm = eloop_ctx;
+	struct wpa_tdls_peer *peer = timeout_ctx;
+
+	/*
+	 * On TPK lifetime expiration, we have an option of either tearing down
+	 * the direct link or trying to re-initiate it. The selection of what
+	 * to do is not strictly speaking controlled by our role in the expired
+	 * link, but for now, use that to select whether to renew or tear down
+	 * the link.
+	 */
+
+	if (peer->initiator) {
+		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
+			   " - try to renew", MAC2STR(peer->addr));
+		wpa_tdls_start(sm, peer->addr);
+	} else {
+		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
+			   " - tear down", MAC2STR(peer->addr));
+		wpa_tdls_do_teardown(sm, peer,
+				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+	}
+}
+
+
+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));
+	eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
+	eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+	peer->reconfig_key = 0;
+	peer->initiator = 0;
+	peer->tpk_in_progress = 0;
+	os_free(peer->sm_tmr.buf);
+	peer->sm_tmr.buf = NULL;
+	os_free(peer->ht_capabilities);
+	peer->ht_capabilities = NULL;
+	os_free(peer->vht_capabilities);
+	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;
+	peer->chan_switch_enabled = 0;
+	os_memset(&peer->tpk, 0, sizeof(peer->tpk));
+	os_memset(peer->inonce, 0, WPA_NONCE_LEN);
+	os_memset(peer->rnonce, 0, WPA_NONCE_LEN);
+}
+
+
+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)
+{
+	lnkid->ie_type = WLAN_EID_LINK_ID;
+	lnkid->ie_len = 3 * ETH_ALEN;
+	os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN);
+	if (peer->initiator) {
+		os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN);
+		os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN);
+	} else {
+		os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN);
+		os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN);
+	}
+}
+
+
+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;
+	struct wpa_tdls_lnkid lnkid;
+	u8 dialog_token;
+	u8 *rbuf, *pos;
+	int ielen;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+	/* Find the node and free from the list */
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
+			   "Teardown " MACSTR, MAC2STR(addr));
+		return 0;
+	}
+
+	/* Cancel active channel switch before teardown */
+	if (peer->chan_switch_enabled) {
+		wpa_printf(MSG_DEBUG, "TDLS: First returning link with " MACSTR
+			   " to base channel", MAC2STR(addr));
+		wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
+	}
+
+	dialog_token = peer->dtoken;
+
+	wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR,
+		   MAC2STR(addr));
+
+	ielen = 0;
+	if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) {
+		/* To add FTIE for Teardown request and compute MIC */
+		ielen += sizeof(*ftie);
+#ifdef CONFIG_TDLS_TESTING
+		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
+			ielen += 170;
+#endif /* CONFIG_TDLS_TESTING */
+	}
+
+	rbuf = os_zalloc(ielen + 1);
+	if (rbuf == NULL)
+		return -1;
+	pos = rbuf;
+
+	if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
+		goto skip_ies;
+
+	ftie = (struct wpa_tdls_ftie *) pos;
+	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+	/* Using the recent nonce which should be for CONFIRM frame */
+	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
+	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+	pos = (u8 *) (ftie + 1);
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+			   "FTIE");
+		ftie->ie_len += 170;
+		*pos++ = 255; /* FTIE subelem */
+		*pos++ = 168; /* FTIE subelem length */
+		pos += 168;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake",
+		    (u8 *) ftie, pos - (u8 *) ftie);
+
+	/* compute MIC before sending */
+	wpa_tdls_linkid(sm, peer, &lnkid);
+	wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code,
+				  dialog_token, (u8 *) &lnkid, (u8 *) ftie,
+				  ftie->mic);
+
+skip_ies:
+	/* TODO: register for a Timeout handler, if Teardown is not received at
+	 * the other end, then try again another time */
+
+	/* request driver to send Teardown using this FTIE */
+	wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
+			  reason_code, 0, peer->initiator, rbuf, pos - rbuf);
+	os_free(rbuf);
+
+	return 0;
+}
+
+
+int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
+{
+	struct wpa_tdls_peer *peer;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL) {
+		wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR
+		   " for link Teardown", MAC2STR(addr));
+		return -1;
+	}
+
+	if (!peer->tpk_success) {
+		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
+		   " not connected - cannot Teardown link", MAC2STR(addr));
+		return -1;
+	}
+
+	return wpa_tdls_do_teardown(sm, peer, reason_code);
+}
+
+
+static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
+				       struct wpa_tdls_peer *peer)
+{
+	wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+	wpa_tdls_peer_free(sm, peer);
+}
+
+
+void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr)
+{
+	struct wpa_tdls_peer *peer;
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	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)) {
+		/*
+		 * Get us on the base channel, disable the link, send a
+		 * teardown packet through the AP, and then reset link data.
+		 */
+		if (peer->chan_switch_enabled)
+			wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
+		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";
+}
+
+
+static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr,
+				  const u8 *buf, size_t len)
+{
+	struct wpa_tdls_peer *peer = NULL;
+	struct wpa_tdls_ftie *ftie;
+	struct wpa_tdls_lnkid *lnkid;
+	struct wpa_eapol_ie_parse kde;
+	u16 reason_code;
+	const u8 *pos;
+	int ielen;
+
+	/* Find the node and free from the list */
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
+			   "Teardown " MACSTR, MAC2STR(src_addr));
+		return 0;
+	}
+
+	pos = buf;
+	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+
+	reason_code = WPA_GET_LE16(pos);
+	pos += 2;
+
+	wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR
+		   " (reason code %u)", MAC2STR(src_addr), reason_code);
+
+	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 may add arbitrary padding to the
+	 * end of short TDLS frames and that would look like invalid IEs.
+	 */
+	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0)
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse IEs in Teardown - ignore as an interop workaround");
+
+	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+		wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS "
+			   "Teardown");
+		return -1;
+	}
+	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+
+	if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
+		goto skip_ftie;
+
+	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
+		wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown");
+		return -1;
+	}
+
+	ftie = (struct wpa_tdls_ftie *) kde.ftie;
+
+	/* Process MIC check to see if TDLS Teardown is right */
+	if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code,
+						    peer->dtoken, peer,
+						    (u8 *) lnkid, ftie) < 0) {
+		wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS "
+			   "Teardown Request from " MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+
+skip_ftie:
+	/*
+	 * Request the driver to disable the direct link and clear associated
+	 * keys.
+	 */
+	wpa_tdls_disable_peer_link(sm, peer);
+	return 0;
+}
+
+
+/**
+ * wpa_tdls_send_error - To send suitable TDLS status response with
+ *	appropriate status code mentioning reason for error/failure.
+ * @dst 	- MAC addr of Peer station
+ * @tdls_action - TDLS frame type for which error code is sent
+ * @initiator   - was this end the initiator of the connection
+ * @status 	- status code mentioning reason
+ */
+
+static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst,
+			       u8 tdls_action, u8 dialog_token, int initiator,
+			       u16 status)
+{
+	wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR
+		   " (action=%u status=%u)",
+		   MAC2STR(dst), tdls_action, status);
+	return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
+				 0, initiator, NULL, 0);
+}
+
+
+static struct wpa_tdls_peer *
+wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing)
+{
+	struct wpa_tdls_peer *peer;
+
+	if (existing)
+		*existing = 0;
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) {
+			if (existing)
+				*existing = 1;
+			return peer; /* re-use existing entry */
+		}
+	}
+
+	wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR,
+		   MAC2STR(addr));
+
+	peer = os_zalloc(sizeof(*peer));
+	if (peer == NULL)
+		return NULL;
+
+	os_memcpy(peer->addr, addr, ETH_ALEN);
+	peer->next = sm->tdls;
+	sm->tdls = peer;
+
+	return peer;
+}
+
+
+static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm,
+				struct wpa_tdls_peer *peer)
+{
+	size_t buf_len;
+	struct wpa_tdls_timeoutie timeoutie;
+	u16 rsn_capab;
+	struct wpa_tdls_ftie *ftie;
+	u8 *rbuf, *pos, *count_pos;
+	u16 count;
+	struct rsn_ie_hdr *hdr;
+	int status;
+
+	if (!wpa_tdls_get_privacy(sm)) {
+		wpa_printf(MSG_DEBUG, "TDLS: No security used on the link");
+		peer->rsnie_i_len = 0;
+		goto skip_rsnie;
+	}
+
+	/*
+	 * TPK Handshake Message 1:
+	 * FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I,
+	 * Timeout Interval IE))
+	 */
+
+	/* Filling RSN IE */
+	hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
+	hdr->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(hdr->version, RSN_VERSION);
+
+	pos = (u8 *) (hdr + 1);
+	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	pos += RSN_SELECTOR_LEN;
+	count_pos = pos;
+	pos += 2;
+
+	count = 0;
+
+	/*
+	 * AES-CCMP is the default Encryption preferred for TDLS, so
+	 * RSN IE is filled only with CCMP CIPHER
+	 * Note: TKIP is not used to encrypt TDLS link.
+	 *
+	 * Regardless of the cipher used on the AP connection, select CCMP
+	 * here.
+	 */
+	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+	pos += RSN_SELECTOR_LEN;
+	count++;
+
+	WPA_PUT_LE16(count_pos, count);
+
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
+	pos += RSN_SELECTOR_LEN;
+
+	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
+		wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for "
+			   "testing");
+		rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+	WPA_PUT_LE16(pos, rsn_capab);
+	pos += 2;
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
+		/* Number of PMKIDs */
+		*pos++ = 0x00;
+		*pos++ = 0x00;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	hdr->len = (pos - peer->rsnie_i) - 2;
+	peer->rsnie_i_len = pos - peer->rsnie_i;
+	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
+		    peer->rsnie_i, peer->rsnie_i_len);
+
+skip_rsnie:
+	buf_len = 0;
+	if (wpa_tdls_get_privacy(sm))
+		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
+			sizeof(struct wpa_tdls_timeoutie);
+#ifdef CONFIG_TDLS_TESTING
+	if (wpa_tdls_get_privacy(sm) &&
+	    (tdls_testing & TDLS_TESTING_LONG_FRAME))
+		buf_len += 170;
+	if (tdls_testing & TDLS_TESTING_DIFF_BSSID)
+		buf_len += sizeof(struct wpa_tdls_lnkid);
+#endif /* CONFIG_TDLS_TESTING */
+	rbuf = os_zalloc(buf_len + 1);
+	if (rbuf == NULL) {
+		wpa_tdls_peer_free(sm, peer);
+		return -1;
+	}
+	pos = rbuf;
+
+	if (!wpa_tdls_get_privacy(sm))
+		goto skip_ies;
+
+	/* Initiator RSN IE */
+	pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
+
+	ftie = (struct wpa_tdls_ftie *) pos;
+	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+
+	if (os_get_random(peer->inonce, WPA_NONCE_LEN)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"TDLS: Failed to get random data for initiator Nonce");
+		os_free(rbuf);
+		wpa_tdls_peer_free(sm, peer);
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake",
+		    peer->inonce, WPA_NONCE_LEN);
+	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1",
+		    (u8 *) ftie, sizeof(struct wpa_tdls_ftie));
+
+	pos = (u8 *) (ftie + 1);
+
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+			   "FTIE");
+		ftie->ie_len += 170;
+		*pos++ = 255; /* FTIE subelem */
+		*pos++ = 168; /* FTIE subelem length */
+		pos += 168;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	/* Lifetime */
+	peer->lifetime = TPK_LIFETIME;
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK "
+			   "lifetime");
+		peer->lifetime = 301;
+	}
+	if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK "
+			   "lifetime");
+		peer->lifetime = 0xffffffff;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+				     sizeof(timeoutie), peer->lifetime);
+	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
+
+skip_ies:
+
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_DIFF_BSSID) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in "
+			   "Link Identifier");
+		struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos;
+		wpa_tdls_linkid(sm, peer, l);
+		l->bssid[5] ^= 0x01;
+		pos += sizeof(*l);
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK "
+		   "Handshake Message 1 (peer " MACSTR ")",
+		   MAC2STR(peer->addr));
+
+	status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST,
+				   1, 0, 0, peer->initiator, rbuf, pos - rbuf);
+	os_free(rbuf);
+
+	return status;
+}
+
+
+static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm,
+				const unsigned char *src_addr, u8 dtoken,
+				struct wpa_tdls_lnkid *lnkid,
+				const struct wpa_tdls_peer *peer)
+{
+	u8 *rbuf, *pos;
+	size_t buf_len;
+	u32 lifetime;
+	struct wpa_tdls_timeoutie timeoutie;
+	struct wpa_tdls_ftie *ftie;
+	int status;
+
+	buf_len = 0;
+	if (wpa_tdls_get_privacy(sm)) {
+		/* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
+		 * Lifetime */
+		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
+			sizeof(struct wpa_tdls_timeoutie);
+#ifdef CONFIG_TDLS_TESTING
+		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
+			buf_len += 170;
+#endif /* CONFIG_TDLS_TESTING */
+	}
+
+	rbuf = os_zalloc(buf_len + 1);
+	if (rbuf == NULL)
+		return -1;
+	pos = rbuf;
+
+	if (!wpa_tdls_get_privacy(sm))
+		goto skip_ies;
+
+	/* Peer RSN IE */
+	pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
+
+	ftie = (struct wpa_tdls_ftie *) pos;
+	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+	/* TODO: ftie->mic_control to set 2-RESPONSE */
+	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
+	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2",
+		    (u8 *) ftie, sizeof(*ftie));
+
+	pos = (u8 *) (ftie + 1);
+
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+			   "FTIE");
+		ftie->ie_len += 170;
+		*pos++ = 255; /* FTIE subelem */
+		*pos++ = 168; /* FTIE subelem length */
+		pos += 168;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	/* Lifetime */
+	lifetime = peer->lifetime;
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
+			   "lifetime in response");
+		lifetime++;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+				     sizeof(timeoutie), lifetime);
+	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator",
+		   lifetime);
+
+	/* 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, 0, peer->initiator, rbuf,
+				   pos - rbuf);
+	os_free(rbuf);
+
+	return status;
+}
+
+
+static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm,
+				const unsigned char *src_addr, u8 dtoken,
+				struct wpa_tdls_lnkid *lnkid,
+				const struct wpa_tdls_peer *peer)
+{
+	u8 *rbuf, *pos;
+	size_t buf_len;
+	struct wpa_tdls_ftie *ftie;
+	struct wpa_tdls_timeoutie timeoutie;
+	u32 lifetime;
+	int status;
+	u32 peer_capab = 0;
+
+	buf_len = 0;
+	if (wpa_tdls_get_privacy(sm)) {
+		/* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
+		 * Lifetime */
+		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
+			sizeof(struct wpa_tdls_timeoutie);
+#ifdef CONFIG_TDLS_TESTING
+		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
+			buf_len += 170;
+#endif /* CONFIG_TDLS_TESTING */
+	}
+
+	rbuf = os_zalloc(buf_len + 1);
+	if (rbuf == NULL)
+		return -1;
+	pos = rbuf;
+
+	if (!wpa_tdls_get_privacy(sm))
+		goto skip_ies;
+
+	/* Peer RSN IE */
+	pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
+
+	ftie = (struct wpa_tdls_ftie *) pos;
+	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+	/*TODO: ftie->mic_control to set 3-CONFIRM */
+	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
+	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+
+	pos = (u8 *) (ftie + 1);
+
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+			   "FTIE");
+		ftie->ie_len += 170;
+		*pos++ = 255; /* FTIE subelem */
+		*pos++ = 168; /* FTIE subelem length */
+		pos += 168;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	/* Lifetime */
+	lifetime = peer->lifetime;
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
+			   "lifetime in confirm");
+		lifetime++;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+				     sizeof(timeoutie), lifetime);
+	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds",
+		   lifetime);
+
+	/* 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, peer_capab, peer->initiator,
+				   rbuf, pos - rbuf);
+	os_free(rbuf);
+
+	return status;
+}
+
+
+static int wpa_tdls_send_discovery_response(struct wpa_sm *sm,
+					    struct wpa_tdls_peer *peer,
+					    u8 dialog_token)
+{
+	size_t buf_len = 0;
+	struct wpa_tdls_timeoutie timeoutie;
+	u16 rsn_capab;
+	u8 *rbuf, *pos, *count_pos;
+	u16 count;
+	struct rsn_ie_hdr *hdr;
+	int status;
+
+	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response "
+		   "(peer " MACSTR ")", MAC2STR(peer->addr));
+	if (!wpa_tdls_get_privacy(sm))
+		goto skip_rsn_ies;
+
+	/* Filling RSN IE */
+	hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
+	hdr->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(hdr->version, RSN_VERSION);
+	pos = (u8 *) (hdr + 1);
+	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	pos += RSN_SELECTOR_LEN;
+	count_pos = pos;
+	pos += 2;
+	count = 0;
+
+	/*
+	* AES-CCMP is the default encryption preferred for TDLS, so
+	* RSN IE is filled only with CCMP cipher suite.
+	* Note: TKIP is not used to encrypt TDLS link.
+	*
+	* Regardless of the cipher used on the AP connection, select CCMP
+	* here.
+	*/
+	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+	pos += RSN_SELECTOR_LEN;
+	count++;
+	WPA_PUT_LE16(count_pos, count);
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
+	pos += RSN_SELECTOR_LEN;
+
+	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
+	WPA_PUT_LE16(pos, rsn_capab);
+	pos += 2;
+	hdr->len = (pos - (u8 *) hdr) - 2;
+	peer->rsnie_i_len = pos - peer->rsnie_i;
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for Discovery Response",
+		    (u8 *) hdr, hdr->len + 2);
+skip_rsn_ies:
+	buf_len = 0;
+	if (wpa_tdls_get_privacy(sm)) {
+		/* Peer RSN IE, Lifetime */
+		buf_len += peer->rsnie_i_len +
+			sizeof(struct wpa_tdls_timeoutie);
+	}
+	rbuf = os_zalloc(buf_len + 1);
+	if (rbuf == NULL) {
+		wpa_tdls_peer_free(sm, peer);
+		return -1;
+	}
+	pos = rbuf;
+
+	if (!wpa_tdls_get_privacy(sm))
+		goto skip_ies;
+	/* Initiator RSN IE */
+	pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
+	/* Lifetime */
+	peer->lifetime = TPK_LIFETIME;
+	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+				     sizeof(timeoutie), peer->lifetime);
+	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
+skip_ies:
+	status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
+				   dialog_token, 0, 0, 0, rbuf, pos - rbuf);
+	os_free(rbuf);
+
+	return status;
+}
+
+
+static int
+wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr,
+				   const u8 *buf, size_t len)
+{
+	struct wpa_eapol_ie_parse kde;
+	const struct wpa_tdls_lnkid *lnkid;
+	struct wpa_tdls_peer *peer;
+	size_t min_req_len = sizeof(struct wpa_tdls_frame) +
+		1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid);
+	u8 dialog_token;
+
+	wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR,
+		   MAC2STR(addr));
+
+	if (len < min_req_len) {
+		wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: "
+			   "%d", (int) len);
+		return -1;
+	}
+
+	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) {
+		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 "
+			   "Request");
+		return -1;
+	}
+
+	lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid;
+
+	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different "
+			   " BSS " MACSTR, MAC2STR(lnkid->bssid));
+		return -1;
+	}
+
+	peer = wpa_tdls_add_peer(sm, addr, NULL);
+	if (peer == NULL)
+		return -1;
+
+	return wpa_tdls_send_discovery_response(sm, peer, dialog_token);
+}
+
+
+int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr)
+{
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+	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, 0, 1, NULL, 0);
+}
+
+
+static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde,
+			   struct wpa_tdls_peer *peer)
+{
+	if (!kde->supp_rates) {
+		wpa_printf(MSG_DEBUG, "TDLS: No supported rates received");
+		return -1;
+	}
+	peer->supp_rates_len = merge_byte_arrays(
+		peer->supp_rates, sizeof(peer->supp_rates),
+		kde->supp_rates + 2, kde->supp_rates_len - 2,
+		kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL,
+		kde->ext_supp_rates_len - 2);
+	return 0;
+}
+
+
+static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde,
+			      struct wpa_tdls_peer *peer)
+{
+	if (!kde->ht_capabilities) {
+		wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities "
+			   "received");
+		return 0;
+	}
+
+	if (!peer->ht_capabilities) {
+		peer->ht_capabilities =
+                        os_zalloc(sizeof(struct ieee80211_ht_capabilities));
+		if (peer->ht_capabilities == NULL)
+                        return -1;
+	}
+
+	os_memcpy(peer->ht_capabilities, kde->ht_capabilities,
+                  sizeof(struct ieee80211_ht_capabilities));
+	wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities",
+		    (u8 *) peer->ht_capabilities,
+		    sizeof(struct ieee80211_ht_capabilities));
+
+	return 0;
+}
+
+
+static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde,
+			      struct wpa_tdls_peer *peer)
+{
+	if (!kde->vht_capabilities) {
+		wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities "
+			   "received");
+		return 0;
+	}
+
+	if (!peer->vht_capabilities) {
+		peer->vht_capabilities =
+                        os_zalloc(sizeof(struct ieee80211_vht_capabilities));
+		if (peer->vht_capabilities == NULL)
+                        return -1;
+	}
+
+	os_memcpy(peer->vht_capabilities, kde->vht_capabilities,
+                  sizeof(struct ieee80211_vht_capabilities));
+	wpa_hexdump(MSG_DEBUG, "TDLS: Peer VHT capabilities",
+		    (u8 *) peer->vht_capabilities,
+		    sizeof(struct ieee80211_vht_capabilities));
+
+	return 0;
+}
+
+
+static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde,
+			       struct wpa_tdls_peer *peer)
+{
+	if (!kde->ext_capab) {
+		wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities "
+			   "received");
+		return 0;
+	}
+
+	if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) {
+		/* Need to allocate buffer to fit the new information */
+		os_free(peer->ext_capab);
+		peer->ext_capab = os_zalloc(kde->ext_capab_len - 2);
+		if (peer->ext_capab == NULL)
+			return -1;
+	}
+
+	peer->ext_capab_len = kde->ext_capab_len - 2;
+	os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len);
+
+	return 0;
+}
+
+
+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->wmm_capable,
+				       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)
+{
+	struct wpa_tdls_peer *peer;
+	struct wpa_eapol_ie_parse kde;
+	struct wpa_ie_data ie;
+	int cipher;
+	const u8 *cpos;
+	struct wpa_tdls_ftie *ftie = NULL;
+	struct wpa_tdls_timeoutie *timeoutie;
+	struct wpa_tdls_lnkid *lnkid;
+	u32 lifetime = 0;
+#if 0
+	struct rsn_ie_hdr *hdr;
+	u8 *pos;
+	u16 rsn_capab;
+	u16 rsn_ver;
+#endif
+	u8 dtoken;
+	u16 ielen;
+	u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	int tdls_prohibited = sm->tdls_prohibited;
+	int existing_peer = 0;
+
+	if (len < 3 + 3)
+		return -1;
+
+	cpos = buf;
+	cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+
+	/* driver had already verified the frame format */
+	dtoken = *cpos++; /* dialog token */
+
+	wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken);
+
+	peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer);
+	if (peer == NULL)
+		goto error;
+
+	/* If found, use existing entry instead of adding a new one;
+	 * how to handle the case where both ends initiate at the
+	 * same time? */
+	if (existing_peer) {
+		if (peer->tpk_success) {
+			wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
+				   "direct link is enabled - tear down the "
+				   "old link first");
+			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 "
+					   MACSTR, MAC2STR(src_addr));
+				return -1;
+			} else {
+				wpa_printf(MSG_DEBUG, "TDLS: Accept request "
+					   "from peer with lower address "
+					   MACSTR " (terminate previously "
+					   "initiated negotiation",
+					   MAC2STR(src_addr));
+				wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
+						 peer->addr);
+				wpa_tdls_peer_clear(sm, peer);
+			}
+		}
+	}
+
+	/* capability information */
+	peer->capability = WPA_GET_LE16(cpos);
+	cpos += 2;
+
+	ielen = len - (cpos - 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 may add arbitrary padding to the
+	 * end of short TDLS frames and that would look like invalid IEs.
+	 */
+	if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0)
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse IEs in TPK M1 - ignore as an interop workaround");
+
+	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+		wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
+			   "TPK M1");
+		goto error;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1",
+		    kde.lnkid, kde.lnkid_len);
+	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS");
+		status = WLAN_STATUS_REQUEST_DECLINED;
+		goto error;
+	}
+
+	wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR,
+		   MAC2STR(src_addr));
+
+	if (copy_supp_rates(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_ht_capab(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_vht_capab(&kde, peer) < 0)
+		goto error;
+
+	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
+	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
+		peer = wpa_tdls_add_peer(sm, src_addr, NULL);
+		if (peer == NULL)
+			goto error;
+		wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of "
+			   "TDLS setup - send own request");
+		peer->initiator = 1;
+		wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
+					NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0);
+		wpa_tdls_send_tpk_m1(sm, peer);
+	}
+
+	if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
+	    tdls_prohibited) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
+			   "on TDLS");
+		tdls_prohibited = 0;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	if (tdls_prohibited) {
+		wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS");
+		status = WLAN_STATUS_REQUEST_DECLINED;
+		goto error;
+	}
+
+	if (!wpa_tdls_get_privacy(sm)) {
+		if (kde.rsn_ie) {
+			wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while "
+				   "security is disabled");
+			status = WLAN_STATUS_SECURITY_DISABLED;
+			goto error;
+		}
+		goto skip_rsn;
+	}
+
+	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
+	    kde.rsn_ie == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1");
+		status = WLAN_STATUS_INVALID_PARAMETERS;
+		goto error;
+	}
+
+	if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
+		wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in "
+			   "TPK M1");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto error;
+	}
+
+	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
+		wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto error;
+	}
+
+	cipher = ie.pairwise_cipher;
+	if (cipher & WPA_CIPHER_CCMP) {
+		wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
+		cipher = WPA_CIPHER_CCMP;
+	} else {
+		wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1");
+		status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+		goto error;
+	}
+
+	if ((ie.capabilities &
+	     (WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) !=
+	    WPA_CAPABILITY_PEERKEY_ENABLED) {
+		wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in "
+			   "TPK M1");
+		status = WLAN_STATUS_INVALID_RSN_IE_CAPAB;
+		goto error;
+	}
+
+	/* Lifetime */
+	if (kde.key_lifetime == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1");
+		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+		goto error;
+	}
+	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
+	lifetime = WPA_GET_LE32(timeoutie->value);
+	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime);
+	if (lifetime < 300) {
+		wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime");
+		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+		goto error;
+	}
+
+skip_rsn:
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
+		if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) {
+			/*
+			 * The request frame from us is going to win, so do not
+			 * replace information based on this request frame from
+			 * the peer.
+			 */
+			goto skip_rsn_check;
+		}
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	peer->initiator = 0; /* Need to check */
+	peer->dtoken = dtoken;
+
+	if (!wpa_tdls_get_privacy(sm)) {
+		peer->rsnie_i_len = 0;
+		peer->rsnie_p_len = 0;
+		peer->cipher = WPA_CIPHER_NONE;
+		goto skip_rsn_check;
+	}
+
+	ftie = (struct wpa_tdls_ftie *) kde.ftie;
+	os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
+	peer->rsnie_i_len = kde.rsn_ie_len;
+	peer->cipher = cipher;
+
+	if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) {
+		/*
+		 * There is no point in updating the RNonce for every obtained
+		 * TPK M1 frame (e.g., retransmission due to timeout) with the
+		 * same INonce (SNonce in FTIE). However, if the TPK M1 is
+		 * retransmitted with a different INonce, update the RNonce
+		 * since this is for a new TDLS session.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: New TPK M1 INonce - generate new RNonce");
+		os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN);
+		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");
+			goto error;
+		}
+	}
+
+#if 0
+	/* get version info from RSNIE received from Peer */
+	hdr = (struct rsn_ie_hdr *) kde.rsn_ie;
+	rsn_ver = WPA_GET_LE16(hdr->version);
+
+	/* use min(peer's version, out version) */
+	if (rsn_ver > RSN_VERSION)
+		rsn_ver = RSN_VERSION;
+
+	hdr = (struct rsn_ie_hdr *) peer->rsnie_p;
+
+	hdr->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(hdr->version, rsn_ver);
+	pos = (u8 *) (hdr + 1);
+
+	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	pos += RSN_SELECTOR_LEN;
+	/* Include only the selected cipher in pairwise cipher suite */
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	if (cipher == WPA_CIPHER_CCMP)
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+	pos += RSN_SELECTOR_LEN;
+
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
+	pos += RSN_SELECTOR_LEN;
+
+	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
+	WPA_PUT_LE16(pos, rsn_capab);
+	pos += 2;
+
+	hdr->len = (pos - peer->rsnie_p) - 2;
+	peer->rsnie_p_len = pos - peer->rsnie_p;
+#endif
+
+	/* temp fix: validation of RSNIE later */
+	os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len);
+	peer->rsnie_p_len = peer->rsnie_i_len;
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
+		    peer->rsnie_p, peer->rsnie_p_len);
+
+	peer->lifetime = lifetime;
+
+	wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
+
+skip_rsn_check:
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT)
+		goto skip_add_peer;
+#endif /* CONFIG_TDLS_TESTING */
+
+	/* add supported rates, capabilities, and qos_info to the TDLS peer */
+	if (wpa_tdls_addset_peer(sm, peer, 1) < 0)
+		goto error;
+
+#ifdef CONFIG_TDLS_TESTING
+skip_add_peer:
+#endif /* CONFIG_TDLS_TESTING */
+	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_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+		goto error;
+	}
+
+	return 0;
+
+error:
+	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0,
+			    status);
+	if (peer)
+		wpa_tdls_peer_free(sm, peer);
+	return -1;
+}
+
+
+static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+	peer->tpk_success = 1;
+	peer->tpk_in_progress = 0;
+	eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
+	if (wpa_tdls_get_privacy(sm)) {
+		u32 lifetime = peer->lifetime;
+		/*
+		 * Start the initiator process a bit earlier to avoid race
+		 * condition with the responder sending teardown request.
+		 */
+		if (lifetime > 3 && peer->initiator)
+			lifetime -= 3;
+		eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout,
+				       sm, peer);
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK "
+			   "expiration");
+		eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
+	}
+#endif /* CONFIG_TDLS_TESTING */
+	}
+
+	if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) {
+		wpa_printf(MSG_INFO, "TDLS: Could not configure key to the "
+			   "driver");
+		return -1;
+	}
+	peer->reconfig_key = 0;
+
+	return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr);
+}
+
+
+static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
+				   const u8 *buf, size_t len)
+{
+	struct wpa_tdls_peer *peer;
+	struct wpa_eapol_ie_parse kde;
+	struct wpa_ie_data ie;
+	int cipher;
+	struct wpa_tdls_ftie *ftie;
+	struct wpa_tdls_timeoutie *timeoutie;
+	struct wpa_tdls_lnkid *lnkid;
+	u32 lifetime;
+	u8 dtoken;
+	int ielen;
+	u16 status;
+	const u8 *pos;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 "
+		   "(Peer " MACSTR ")", MAC2STR(src_addr));
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+			break;
+	}
+	if (peer == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
+			   "TPK M2: " MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+	if (!peer->initiator) {
+		/*
+		 * This may happen if both devices try to initiate TDLS at the
+		 * same time and we accept the TPK M1 from the peer in
+		 * wpa_tdls_process_tpk_m1() and clear our previous state.
+		 */
+		wpa_printf(MSG_INFO, "TDLS: We were not the initiator, so "
+			   "ignore TPK M2 from " MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+	wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST);
+
+	if (len < 3 + 2 + 1) {
+		wpa_tdls_disable_peer_link(sm, peer);
+		return -1;
+	}
+
+	pos = buf;
+	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+	status = WPA_GET_LE16(pos);
+	pos += 2 /* status code */;
+
+	if (status != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u",
+			   status);
+		wpa_tdls_disable_peer_link(sm, peer);
+		return -1;
+	}
+
+	status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	/* TODO: need to verify dialog token matches here or in kernel */
+	dtoken = *pos++; /* dialog token */
+
+	wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken);
+
+	if (len < 3 + 2 + 1 + 2) {
+		wpa_tdls_disable_peer_link(sm, peer);
+		return -1;
+	}
+
+	/* capability information */
+	peer->capability = WPA_GET_LE16(pos);
+	pos += 2;
+
+	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 may add arbitrary padding to the
+	 * end of short TDLS frames and that would look like invalid IEs.
+	 */
+	if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0)
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse IEs in TPK M2 - ignore as an interop workaround");
+
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_DECLINE_RESP) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response");
+		status = WLAN_STATUS_REQUEST_DECLINED;
+		goto error;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+		wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
+			   "TPK M2");
+		goto error;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2",
+		    kde.lnkid, kde.lnkid_len);
+	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+
+	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS");
+		status = WLAN_STATUS_NOT_IN_SAME_BSS;
+		goto error;
+	}
+
+	if (copy_supp_rates(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_ht_capab(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_vht_capab(&kde, peer) < 0)
+		goto error;
+
+	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)) {
+		peer->rsnie_p_len = 0;
+		peer->cipher = WPA_CIPHER_NONE;
+		goto skip_rsn;
+	}
+
+	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
+	    kde.rsn_ie == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2");
+		status = WLAN_STATUS_INVALID_PARAMETERS;
+		goto error;
+	}
+	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
+	 * match. Since we list only a single pairwise cipher in TPK M1, the
+	 * memcmp is likely to work in most cases, though.
+	 */
+	if (kde.rsn_ie_len != peer->rsnie_i_len ||
+	    os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) {
+		wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does "
+			   "not match with RSN IE used in TPK M1");
+		wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1",
+			    peer->rsnie_i, peer->rsnie_i_len);
+		wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
+			    kde.rsn_ie, kde.rsn_ie_len);
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto error;
+	}
+
+	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
+		wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto error;
+	}
+
+	cipher = ie.pairwise_cipher;
+	if (cipher == WPA_CIPHER_CCMP) {
+		wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
+		cipher = WPA_CIPHER_CCMP;
+	} else {
+		wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2");
+		status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+		goto error;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2",
+		    kde.ftie, sizeof(*ftie));
+	ftie = (struct wpa_tdls_ftie *) kde.ftie;
+
+	if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
+		wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does "
+			   "not match with FTIE SNonce used in TPK M1");
+		/* Silently discard the frame */
+		return -1;
+	}
+
+	/* Responder Nonce and RSN IE */
+	os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN);
+	os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len);
+	peer->rsnie_p_len = kde.rsn_ie_len;
+	peer->cipher = cipher;
+
+	/* Lifetime */
+	if (kde.key_lifetime == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2");
+		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+		goto error;
+	}
+	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
+	lifetime = WPA_GET_LE32(timeoutie->value);
+	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2",
+		   lifetime);
+	if (lifetime != peer->lifetime) {
+		wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
+			   "TPK M2 (expected %u)", lifetime, peer->lifetime);
+		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+		goto error;
+	}
+
+	wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
+
+	/* Process MIC check to see if TPK M2 is right */
+	if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid,
+					   (u8 *) timeoutie, ftie) < 0) {
+		/* Discard the frame */
+		wpa_tdls_del_key(sm, peer);
+		wpa_tdls_disable_peer_link(sm, peer);
+		return -1;
+	}
+
+	if (wpa_tdls_set_key(sm, peer) < 0) {
+		/*
+		 * Some drivers may not be able to config the key prior to full
+		 * STA entry having been configured.
+		 */
+		wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
+			   "STA entry is complete");
+		peer->reconfig_key = 1;
+	}
+
+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)
+		goto error;
+
+	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;
+
+error:
+	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1,
+			    status);
+	wpa_tdls_disable_peer_link(sm, peer);
+	return -1;
+}
+
+
+static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
+				   const u8 *buf, size_t len)
+{
+	struct wpa_tdls_peer *peer;
+	struct wpa_eapol_ie_parse kde;
+	struct wpa_tdls_ftie *ftie;
+	struct wpa_tdls_timeoutie *timeoutie;
+	struct wpa_tdls_lnkid *lnkid;
+	int ielen;
+	u16 status;
+	const u8 *pos;
+	u32 lifetime;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 "
+		   "(Peer " MACSTR ")", MAC2STR(src_addr));
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+			break;
+	}
+	if (peer == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
+			   "TPK M3: " MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+	wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE);
+
+	if (len < 3 + 3)
+		goto error;
+	pos = buf;
+	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+
+	status = WPA_GET_LE16(pos);
+
+	if (status != 0) {
+		wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u",
+			   status);
+		goto error;
+	}
+	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_DEBUG,
+			   "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround");
+	}
+
+	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+		wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3");
+		goto error;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3",
+		    (u8 *) kde.lnkid, kde.lnkid_len);
+	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+
+	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS");
+		goto error;
+	}
+
+	if (!wpa_tdls_get_privacy(sm))
+		goto skip_rsn;
+
+	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
+		wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3");
+		goto error;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3",
+		    kde.ftie, sizeof(*ftie));
+	ftie = (struct wpa_tdls_ftie *) kde.ftie;
+
+	if (kde.rsn_ie == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3");
+		goto error;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3",
+		    kde.rsn_ie, kde.rsn_ie_len);
+	if (kde.rsn_ie_len != peer->rsnie_p_len ||
+	    os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) {
+		wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match "
+			   "with the one sent in TPK M2");
+		goto error;
+	}
+
+	if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) {
+		wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does "
+			   "not match with FTIE ANonce used in TPK M2");
+		goto error;
+	}
+
+	if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
+		wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not "
+			   "match with FTIE SNonce used in TPK M1");
+		goto error;
+	}
+
+	if (kde.key_lifetime == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3");
+		goto error;
+	}
+	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
+	wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3",
+		    (u8 *) timeoutie, sizeof(*timeoutie));
+	lifetime = WPA_GET_LE32(timeoutie->value);
+	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3",
+		   lifetime);
+	if (lifetime != peer->lifetime) {
+		wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
+			   "TPK M3 (expected %u)", lifetime, peer->lifetime);
+		goto error;
+	}
+
+	if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
+					   (u8 *) timeoutie, ftie) < 0) {
+		wpa_tdls_del_key(sm, peer);
+		goto error;
+	}
+
+	if (wpa_tdls_set_key(sm, peer) < 0) {
+		/*
+		 * Some drivers may not be able to config the key prior to full
+		 * STA entry having been configured.
+		 */
+		wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
+			   "STA entry is complete");
+		peer->reconfig_key = 1;
+	}
+
+skip_rsn:
+	/* 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_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+	return -1;
+}
+
+
+static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs)
+{
+	struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie;
+
+	os_memset(lifetime, 0, ie_len);
+	lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL;
+	lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2;
+	lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME;
+	WPA_PUT_LE32(lifetime->value, tsecs);
+	os_memcpy(pos, ie, ie_len);
+	return pos + ie_len;
+}
+
+
+/**
+ * wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1)
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @peer: MAC address of the peer STA
+ * Returns: 0 on success, or -1 on failure
+ *
+ * Send TPK Handshake Message 1 info to driver to start TDLS
+ * handshake with the peer.
+ */
+int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr)
+{
+	struct wpa_tdls_peer *peer;
+	int tdls_prohibited = sm->tdls_prohibited;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+#ifdef CONFIG_TDLS_TESTING
+	if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
+	    tdls_prohibited) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
+			   "on TDLS");
+		tdls_prohibited = 0;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	if (tdls_prohibited) {
+		wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - "
+			   "reject request to start setup");
+		return -1;
+	}
+
+	peer = wpa_tdls_add_peer(sm, addr, NULL);
+	if (peer == NULL)
+		return -1;
+
+	if (peer->tpk_in_progress) {
+		wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer");
+		return 0;
+	}
+
+	peer->initiator = 1;
+
+	/* add the peer to the driver as a "setup in progress" peer */
+	if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
+				    NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0)) {
+		wpa_tdls_disable_peer_link(sm, peer);
+		return -1;
+	}
+
+	peer->tpk_in_progress = 1;
+
+	if (wpa_tdls_send_tpk_m1(sm, peer) < 0) {
+		wpa_tdls_disable_peer_link(sm, peer);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr)
+{
+	struct wpa_tdls_peer *peer;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return;
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL || !peer->tpk_success)
+		return;
+
+	if (sm->tdls_external_setup) {
+		/*
+		 * Disable previous link to allow renegotiation to be completed
+		 * on AP path.
+		 */
+		wpa_tdls_do_teardown(sm, peer,
+				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+	}
+}
+
+
+/**
+ * wpa_supplicant_rx_tdls - Receive TDLS data frame
+ *
+ * This function is called to receive TDLS (ethertype = 0x890d) data frames.
+ */
+static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr,
+				   const u8 *buf, size_t len)
+{
+	struct wpa_sm *sm = ctx;
+	struct wpa_tdls_frame *tf;
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation",
+		    buf, len);
+
+	if (sm->tdls_disabled || !sm->tdls_supported) {
+		wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled "
+			   "or unsupported by driver");
+		return;
+	}
+
+	if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) {
+		wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message");
+		return;
+	}
+
+	if (len < sizeof(*tf)) {
+		wpa_printf(MSG_INFO, "TDLS: Drop too short frame");
+		return;
+	}
+
+	/* Check to make sure its a valid encapsulated TDLS frame */
+	tf = (struct wpa_tdls_frame *) buf;
+	if (tf->payloadtype != 2 /* TDLS_RFTYPE */ ||
+	    tf->category != WLAN_ACTION_TDLS) {
+		wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u "
+			   "category=%u action=%u",
+			   tf->payloadtype, tf->category, tf->action);
+		return;
+	}
+
+	switch (tf->action) {
+	case WLAN_TDLS_SETUP_REQUEST:
+		wpa_tdls_process_tpk_m1(sm, src_addr, buf, len);
+		break;
+	case WLAN_TDLS_SETUP_RESPONSE:
+		wpa_tdls_process_tpk_m2(sm, src_addr, buf, len);
+		break;
+	case WLAN_TDLS_SETUP_CONFIRM:
+		wpa_tdls_process_tpk_m3(sm, src_addr, buf, len);
+		break;
+	case WLAN_TDLS_TEARDOWN:
+		wpa_tdls_recv_teardown(sm, src_addr, buf, len);
+		break;
+	case WLAN_TDLS_DISCOVERY_REQUEST:
+		wpa_tdls_process_discovery_request(sm, src_addr, buf, len);
+		break;
+	default:
+		/* Kernel code will process remaining frames */
+		wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u",
+			   tf->action);
+		break;
+	}
+}
+
+
+/**
+ * wpa_tdls_init - Initialize driver interface parameters for TDLS
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called to initialize driver interface parameters for TDLS.
+ * wpa_drv_init() must have been called before this function to initialize the
+ * driver interface.
+ */
+int wpa_tdls_init(struct wpa_sm *sm)
+{
+	if (sm == NULL)
+		return -1;
+
+	sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname :
+				     sm->ifname,
+				     sm->own_addr,
+				     ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls,
+				     sm, 0);
+	if (sm->l2_tdls == NULL) {
+		wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet "
+			   "connection");
+		return -1;
+	}
+
+	/*
+	 * Drivers that support TDLS but don't implement the get_capa callback
+	 * are assumed to perform everything internally
+	 */
+	if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported,
+				 &sm->tdls_external_setup,
+				 &sm->tdls_chan_switch) < 0) {
+		sm->tdls_supported = 1;
+		sm->tdls_external_setup = 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by "
+		   "driver", sm->tdls_supported ? "" : " not");
+	wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup",
+		   sm->tdls_external_setup ? "external" : "internal");
+	wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS channel switching",
+		   sm->tdls_chan_switch ? "supports" : "does not support");
+
+	return 0;
+}
+
+
+void wpa_tdls_teardown_peers(struct wpa_sm *sm)
+{
+	struct wpa_tdls_peer *peer, *tmp;
+
+	if (!sm)
+		return;
+	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_do_teardown(sm, peer,
+					     WLAN_REASON_DEAUTH_LEAVING);
+		else
+			wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
+
+		peer = tmp;
+	}
+}
+
+
+static void wpa_tdls_remove_peers(struct wpa_sm *sm)
+{
+	struct wpa_tdls_peer *peer, *tmp;
+
+	peer = sm->tdls;
+
+	while (peer) {
+		int res;
+		tmp = peer->next;
+		res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+		wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)",
+			   MAC2STR(peer->addr), res);
+		wpa_tdls_peer_free(sm, peer);
+		peer = tmp;
+	}
+}
+
+
+/**
+ * wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS
+ *
+ * This function is called to recover driver interface parameters for TDLS
+ * and frees resources allocated for it.
+ */
+void wpa_tdls_deinit(struct wpa_sm *sm)
+{
+	if (sm == NULL)
+		return;
+
+	if (sm->l2_tdls)
+		l2_packet_deinit(sm->l2_tdls);
+	sm->l2_tdls = NULL;
+
+	wpa_tdls_remove_peers(sm);
+}
+
+
+void wpa_tdls_assoc(struct wpa_sm *sm)
+{
+	wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association");
+	wpa_tdls_remove_peers(sm);
+}
+
+
+void wpa_tdls_disassoc(struct wpa_sm *sm)
+{
+	wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation");
+	wpa_tdls_remove_peers(sm);
+}
+
+
+static int wpa_tdls_prohibited(struct ieee802_11_elems *elems)
+{
+	/* bit 38 - TDLS Prohibited */
+	return !!(elems->ext_capab[2 + 4] & 0x40);
+}
+
+
+static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems)
+{
+	/* bit 39 - TDLS Channel Switch Prohibited */
+	return !!(elems->ext_capab[2 + 4] & 0x80);
+}
+
+
+void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
+{
+	struct ieee802_11_elems elems;
+
+	sm->tdls_prohibited = 0;
+	sm->tdls_chan_switch_prohibited = 0;
+
+	if (ies == NULL ||
+	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
+	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+		return;
+
+	sm->tdls_prohibited = wpa_tdls_prohibited(&elems);
+	wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS",
+		   sm->tdls_prohibited ? "prohibited" : "allowed");
+	sm->tdls_chan_switch_prohibited =
+		wpa_tdls_chan_switch_prohibited(&elems);
+	wpa_printf(MSG_DEBUG, "TDLS: TDLS channel switch %s in the target BSS",
+		   sm->tdls_chan_switch_prohibited ? "prohibited" : "allowed");
+}
+
+
+void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
+{
+	struct ieee802_11_elems elems;
+
+	if (ies == NULL ||
+	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
+	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+		return;
+
+	if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) {
+		wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on "
+			   "(Re)Association Response IEs");
+		sm->tdls_prohibited = 1;
+	}
+
+	if (!sm->tdls_chan_switch_prohibited &&
+	    wpa_tdls_chan_switch_prohibited(&elems)) {
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: TDLS channel switch prohibited based on (Re)Association Response IEs");
+		sm->tdls_chan_switch_prohibited = 1;
+	}
+}
+
+
+void wpa_tdls_enable(struct wpa_sm *sm, int enabled)
+{
+	wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled");
+	sm->tdls_disabled = !enabled;
+}
+
+
+int wpa_tdls_is_external_setup(struct wpa_sm *sm)
+{
+	return sm->tdls_external_setup;
+}
+
+
+int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
+				u8 oper_class,
+				struct hostapd_freq_params *freq_params)
+{
+	struct wpa_tdls_peer *peer;
+	int ret;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+	if (!sm->tdls_chan_switch) {
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Channel switching not supported by the driver");
+		return -1;
+	}
+
+	if (sm->tdls_chan_switch_prohibited) {
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Channel switching is prohibited in this BSS - reject request to switch channel");
+		return -1;
+	}
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL || !peer->tpk_success) {
+		wpa_printf(MSG_ERROR, "TDLS: Peer " MACSTR
+			   " not found for channel switching", MAC2STR(addr));
+		return -1;
+	}
+
+	if (peer->chan_switch_enabled) {
+		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
+			   " already has channel switching enabled",
+			   MAC2STR(addr));
+		return 0;
+	}
+
+	ret = wpa_sm_tdls_enable_channel_switch(sm, peer->addr,
+						oper_class, freq_params);
+	if (!ret)
+		peer->chan_switch_enabled = 1;
+
+	return ret;
+}
+
+
+int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr)
+{
+	struct wpa_tdls_peer *peer;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (!peer || !peer->chan_switch_enabled) {
+		wpa_printf(MSG_ERROR, "TDLS: Channel switching not enabled for "
+			   MACSTR, MAC2STR(addr));
+		return -1;
+	}
+
+	/* ignore the return value */
+	wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
+
+	peer->chan_switch_enabled = 0;
+	return 0;
+}
diff --git a/hostap/src/rsn_supp/wpa.c b/hostap/src/rsn_supp/wpa.c
new file mode 100644
index 0000000..335f5a0
--- /dev/null
+++ b/hostap/src/rsn_supp/wpa.c
@@ -0,0 +1,3013 @@
+/*
+ * WPA Supplicant - WPA state machine and EAPOL-Key processing
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "wpa.h"
+#include "eloop.h"
+#include "preauth.h"
+#include "pmksa_cache.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+#include "peerkey.h"
+
+
+/**
+ * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @kck: Key Confirmation Key (KCK, part of PTK)
+ * @kck_len: KCK length in octets
+ * @ver: Version field from Key Info
+ * @dest: Destination address for the frame
+ * @proto: Ethertype (usually ETH_P_EAPOL)
+ * @msg: EAPOL-Key message
+ * @msg_len: Length of message
+ * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written
+ */
+void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+			int ver, const u8 *dest, u16 proto,
+			u8 *msg, size_t msg_len, u8 *key_mic)
+{
+	size_t mic_len = wpa_mic_len(sm->key_mgmt);
+
+	if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) {
+		/*
+		 * Association event was not yet received; try to fetch
+		 * BSSID from the driver.
+		 */
+		if (wpa_sm_get_bssid(sm, sm->bssid) < 0) {
+			wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+				"WPA: Failed to read BSSID for "
+				"EAPOL-Key destination address");
+		} else {
+			dest = sm->bssid;
+			wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+				"WPA: Use BSSID (" MACSTR
+				") as the destination for EAPOL-Key",
+				MAC2STR(dest));
+		}
+	}
+	if (key_mic &&
+	    wpa_eapol_key_mic(kck, kck_len, sm->key_mgmt, ver, msg, msg_len,
+			      key_mic)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+			"WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC",
+			ver, sm->key_mgmt);
+		goto out;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len);
+	wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len);
+	wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
+	wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
+	eapol_sm_notify_tx_eapol_key(sm->eapol);
+out:
+	os_free(msg);
+}
+
+
+/**
+ * wpa_sm_key_request - Send EAPOL-Key Request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @error: Indicate whether this is an Michael MIC error report
+ * @pairwise: 1 = error report for pairwise packet, 0 = for group packet
+ *
+ * Send an EAPOL-Key Request to the current authenticator. This function is
+ * used to request rekeying and it is usually called when a local Michael MIC
+ * failure is detected.
+ */
+void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
+{
+	size_t mic_len, hdrlen, rlen;
+	struct wpa_eapol_key *reply;
+	struct wpa_eapol_key_192 *reply192;
+	int key_info, ver;
+	u8 bssid[ETH_ALEN], *rbuf, *key_mic;
+
+	if (sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
+	    wpa_key_mgmt_suite_b(sm->key_mgmt))
+		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;
+	else
+		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+	if (wpa_sm_get_bssid(sm, bssid) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"Failed to read BSSID for EAPOL-Key request");
+		return;
+	}
+
+	mic_len = wpa_mic_len(sm->key_mgmt);
+	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+				  hdrlen, &rlen, (void *) &reply);
+	if (rbuf == NULL)
+		return;
+	reply192 = (struct wpa_eapol_key_192 *) reply;
+
+	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)
+		key_info |= WPA_KEY_INFO_MIC;
+	if (error)
+		key_info |= WPA_KEY_INFO_ERROR;
+	if (pairwise)
+		key_info |= WPA_KEY_INFO_KEY_TYPE;
+	WPA_PUT_BE16(reply->key_info, key_info);
+	WPA_PUT_BE16(reply->key_length, 0);
+	os_memcpy(reply->replay_counter, sm->request_counter,
+		  WPA_REPLAY_COUNTER_LEN);
+	inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+	if (mic_len == 24)
+		WPA_PUT_BE16(reply192->key_data_length, 0);
+	else
+		WPA_PUT_BE16(reply->key_data_length, 0);
+	if (!(key_info & WPA_KEY_INFO_MIC))
+		key_mic = NULL;
+	else
+		key_mic = reply192->key_mic; /* same offset in reply */
+
+	wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+		"WPA: Sending EAPOL-Key Request (error=%d "
+		"pairwise=%d ptk_set=%d len=%lu)",
+		error, pairwise, sm->ptk_set, (unsigned long) rlen);
+	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
+			   ETH_P_EAPOL, rbuf, rlen, key_mic);
+}
+
+
+static void wpa_supplicant_key_mgmt_set_pmk(struct wpa_sm *sm)
+{
+#ifdef CONFIG_IEEE80211R
+	if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
+		if (wpa_sm_key_mgmt_set_pmk(sm, sm->xxkey, sm->xxkey_len))
+			wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+				"RSN: Cannot set low order 256 bits of MSK for key management offload");
+	} else {
+#endif /* CONFIG_IEEE80211R */
+		if (wpa_sm_key_mgmt_set_pmk(sm, sm->pmk, sm->pmk_len))
+			wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+				"RSN: Cannot set PMK for key management offload");
+#ifdef CONFIG_IEEE80211R
+	}
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
+				  const unsigned char *src_addr,
+				  const u8 *pmkid)
+{
+	int abort_cached = 0;
+
+	if (pmkid && !sm->cur_pmksa) {
+		/* When using drivers that generate RSN IE, wpa_supplicant may
+		 * not have enough time to get the association information
+		 * event before receiving this 1/4 message, so try to find a
+		 * matching PMKSA cache entry here. */
+		sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid,
+						NULL);
+		if (sm->cur_pmksa) {
+			wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+				"RSN: found matching PMKID from PMKSA cache");
+		} else {
+			wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+				"RSN: no matching PMKID found");
+			abort_cached = 1;
+		}
+	}
+
+	if (pmkid && sm->cur_pmksa &&
+	    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",
+				sm->pmk, sm->pmk_len);
+		eapol_sm_notify_cached(sm->eapol);
+#ifdef CONFIG_IEEE80211R
+		sm->xxkey_len = 0;
+#endif /* CONFIG_IEEE80211R */
+	} else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) {
+		int res, pmk_len;
+		pmk_len = PMK_LEN;
+		res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN);
+		if (res) {
+			/*
+			 * EAP-LEAP is an exception from other EAP methods: it
+			 * uses only 16-byte PMK.
+			 */
+			res = eapol_sm_get_key(sm->eapol, sm->pmk, 16);
+			pmk_len = 16;
+		} else {
+#ifdef CONFIG_IEEE80211R
+			u8 buf[2 * PMK_LEN];
+			if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0)
+			{
+				os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN);
+				sm->xxkey_len = PMK_LEN;
+				os_memset(buf, 0, sizeof(buf));
+			}
+#endif /* CONFIG_IEEE80211R */
+		}
+		if (res == 0) {
+			struct rsn_pmksa_cache_entry *sa = NULL;
+			wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
+					"machines", sm->pmk, pmk_len);
+			sm->pmk_len = pmk_len;
+			wpa_supplicant_key_mgmt_set_pmk(sm);
+			if (sm->proto == WPA_PROTO_RSN &&
+			    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+			    !wpa_key_mgmt_ft(sm->key_mgmt)) {
+				sa = pmksa_cache_add(sm->pmksa,
+						     sm->pmk, pmk_len,
+						     NULL, 0,
+						     src_addr, sm->own_addr,
+						     sm->network_ctx,
+						     sm->key_mgmt);
+			}
+			if (!sm->cur_pmksa && pmkid &&
+			    pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL))
+			{
+				wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+					"RSN: the new PMK matches with the "
+					"PMKID");
+				abort_cached = 0;
+			} else if (sa && !sm->cur_pmksa && pmkid) {
+				/*
+				 * It looks like the authentication server
+				 * derived mismatching MSK. This should not
+				 * really happen, but bugs happen.. There is not
+				 * much we can do here without knowing what
+				 * exactly caused the server to misbehave.
+				 */
+				wpa_dbg(sm->ctx->msg_ctx, MSG_INFO,
+					"RSN: PMKID mismatch - authentication server may have derived different MSK?!");
+				return -1;
+			}
+
+			if (!sm->cur_pmksa)
+				sm->cur_pmksa = sa;
+		} else {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: Failed to get master session key from "
+				"EAPOL state machines - key handshake "
+				"aborted");
+			if (sm->cur_pmksa) {
+				wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+					"RSN: Cancelled PMKSA caching "
+					"attempt");
+				sm->cur_pmksa = NULL;
+				abort_cached = 1;
+			} else if (!abort_cached) {
+				return -1;
+			}
+		}
+	}
+
+	if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) &&
+	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+	    !wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN)
+	{
+		/* Send EAPOL-Start to trigger full EAP authentication. */
+		u8 *buf;
+		size_t buflen;
+
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"RSN: no PMKSA entry found - trigger "
+			"full EAP authentication");
+		buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START,
+					 NULL, 0, &buflen, NULL);
+		if (buf) {
+			wpa_sm_ether_send(sm, sm->bssid, ETH_P_EAPOL,
+					  buf, buflen);
+			os_free(buf);
+			return -2;
+		}
+
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_supplicant_send_2_of_4 - Send message 2 of WPA/RSN 4-Way Handshake
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @dst: Destination address for the frame
+ * @key: Pointer to the EAPOL-Key frame header
+ * @ver: Version bits from EAPOL-Key Key Info
+ * @nonce: Nonce value for the EAPOL-Key frame
+ * @wpa_ie: WPA/RSN IE
+ * @wpa_ie_len: Length of the WPA/RSN IE
+ * @ptk: PTK to use for keyed hash and encryption
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
+			       const struct wpa_eapol_key *key,
+			       int ver, const u8 *nonce,
+			       const u8 *wpa_ie, size_t wpa_ie_len,
+			       struct wpa_ptk *ptk)
+{
+	size_t mic_len, hdrlen, rlen;
+	struct wpa_eapol_key *reply;
+	struct wpa_eapol_key_192 *reply192;
+	u8 *rbuf, *key_mic;
+	u8 *rsn_ie_buf = NULL;
+
+	if (wpa_ie == NULL) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - "
+			"cannot generate msg 2/4");
+		return -1;
+	}
+
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->key_mgmt)) {
+		int res;
+
+		/*
+		 * Add PMKR1Name into RSN IE (PMKID-List) and add MDIE and
+		 * FTIE from (Re)Association Response.
+		 */
+		rsn_ie_buf = os_malloc(wpa_ie_len + 2 + 2 + PMKID_LEN +
+				       sm->assoc_resp_ies_len);
+		if (rsn_ie_buf == NULL)
+			return -1;
+		os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len);
+		res = wpa_insert_pmkid(rsn_ie_buf, wpa_ie_len,
+				       sm->pmk_r1_name);
+		if (res < 0) {
+			os_free(rsn_ie_buf);
+			return -1;
+		}
+		wpa_ie_len += res;
+
+		if (sm->assoc_resp_ies) {
+			os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies,
+				  sm->assoc_resp_ies_len);
+			wpa_ie_len += sm->assoc_resp_ies_len;
+		}
+
+		wpa_ie = rsn_ie_buf;
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
+
+	mic_len = wpa_mic_len(sm->key_mgmt);
+	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
+				  NULL, hdrlen + wpa_ie_len,
+				  &rlen, (void *) &reply);
+	if (rbuf == NULL) {
+		os_free(rsn_ie_buf);
+		return -1;
+	}
+	reply192 = (struct wpa_eapol_key_192 *) reply;
+
+	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 || 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_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter,
+		    WPA_REPLAY_COUNTER_LEN);
+
+	key_mic = reply192->key_mic; /* same offset for reply and reply192 */
+	if (mic_len == 24) {
+		WPA_PUT_BE16(reply192->key_data_length, wpa_ie_len);
+		os_memcpy(reply192 + 1, wpa_ie, wpa_ie_len);
+	} else {
+		WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
+		os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
+	}
+	os_free(rsn_ie_buf);
+
+	os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
+
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
+	wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
+			   rbuf, rlen, key_mic);
+
+	return 0;
+}
+
+
+static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
+			  const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
+{
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->key_mgmt))
+		return wpa_derive_ptk_ft(sm, src_addr, key, ptk);
+#endif /* CONFIG_IEEE80211R */
+
+	return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
+			      sm->own_addr, sm->bssid, sm->snonce,
+			      key->key_nonce, ptk, sm->key_mgmt,
+			      sm->pairwise_cipher);
+}
+
+
+static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
+					  const unsigned char *src_addr,
+					  const struct wpa_eapol_key *key,
+					  u16 ver, const u8 *key_data,
+					  size_t key_data_len)
+{
+	struct wpa_eapol_ie_parse ie;
+	struct wpa_ptk *ptk;
+	int res;
+	u8 *kde, *kde_buf = NULL;
+	size_t kde_len;
+
+	if (wpa_sm_get_network_ctx(sm) == NULL) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info "
+			"found (msg 1 of 4)");
+		return;
+	}
+
+	wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of 4-Way "
+		"Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+	os_memset(&ie, 0, sizeof(ie));
+
+	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
+		/* RSN: msg 1/4 should contain PMKID for the selected PMK */
+		wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data",
+			    key_data, key_data_len);
+		if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+			goto failed;
+		if (ie.pmkid) {
+			wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
+				    "Authenticator", ie.pmkid, PMKID_LEN);
+		}
+	}
+
+	res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
+	if (res == -2) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to "
+			"msg 1/4 - requesting full EAP authentication");
+		return;
+	}
+	if (res)
+		goto failed;
+
+	if (sm->renew_snonce) {
+		if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: Failed to get random data for SNonce");
+			goto failed;
+		}
+		sm->renew_snonce = 0;
+		wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce",
+			    sm->snonce, WPA_NONCE_LEN);
+	}
+
+	/* Calculate PTK which will be stored as a temporary PTK until it has
+	 * been verified when processing message 3/4. */
+	ptk = &sm->tptk;
+	wpa_derive_ptk(sm, src_addr, key, ptk);
+	if (sm->pairwise_cipher == WPA_CIPHER_TKIP) {
+		u8 buf[8];
+		/* Supplicant: swap tx/rx Mic keys */
+		os_memcpy(buf, &ptk->tk[16], 8);
+		os_memcpy(&ptk->tk[16], &ptk->tk[24], 8);
+		os_memcpy(&ptk->tk[24], buf, 8);
+		os_memset(buf, 0, sizeof(buf));
+	}
+	sm->tptk_set = 1;
+	sm->tk_to_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,
+				       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);
+}
+
+
+static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_sm *sm = eloop_ctx;
+	rsn_preauth_candidate_process(sm);
+}
+
+
+static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
+					    const u8 *addr, int secure)
+{
+	wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+		"WPA: Key negotiation completed with "
+		MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr),
+		wpa_cipher_txt(sm->pairwise_cipher),
+		wpa_cipher_txt(sm->group_cipher));
+	wpa_sm_cancel_auth_timeout(sm);
+	wpa_sm_set_state(sm, WPA_COMPLETED);
+
+	if (secure) {
+		wpa_sm_mlme_setprotection(
+			sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX,
+			MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+		eapol_sm_notify_portValid(sm->eapol, TRUE);
+		if (wpa_key_mgmt_wpa_psk(sm->key_mgmt))
+			eapol_sm_notify_eap_success(sm->eapol, TRUE);
+		/*
+		 * Start preauthentication after a short wait to avoid a
+		 * possible race condition between the data receive and key
+		 * configuration after the 4-Way Handshake. This increases the
+		 * likelihood of the first preauth EAPOL-Start frame getting to
+		 * the target AP.
+		 */
+		eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL);
+	}
+
+	if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"RSN: Authenticator accepted "
+			"opportunistic PMKSA entry - marking it valid");
+		sm->cur_pmksa->opportunistic = 0;
+	}
+
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->key_mgmt)) {
+		/* Prepare for the next transition */
+		wpa_ft_prepare_auth_request(sm, NULL);
+	}
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_sm *sm = eloop_ctx;
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying");
+	wpa_sm_key_request(sm, 0, 1);
+}
+
+
+static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
+				      const struct wpa_eapol_key *key)
+{
+	int keylen, rsclen;
+	enum wpa_alg alg;
+	const u8 *key_rsc;
+	u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	if (!sm->tk_to_set) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: Do not re-install same PTK to the driver");
+		return 0;
+	}
+
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"WPA: Installing PTK to the driver");
+
+	if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher "
+			"Suite: NONE - do not use pairwise keys");
+		return 0;
+	}
+
+	if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Unsupported pairwise cipher %d",
+			sm->pairwise_cipher);
+		return -1;
+	}
+
+	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
+	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+	rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
+
+	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
+		key_rsc = null_rsc;
+	} else {
+		key_rsc = key->key_rsc;
+		wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
+	}
+
+	if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen,
+			   sm->ptk.tk, keylen) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Failed to set PTK to the "
+			"driver (alg=%d keylen=%d bssid=" MACSTR ")",
+			alg, keylen, MAC2STR(sm->bssid));
+		return -1;
+	}
+
+	/* TK is not needed anymore in supplicant */
+	os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+	sm->tk_to_set = 0;
+
+	if (sm->wpa_ptk_rekey) {
+		eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
+		eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk,
+				       sm, NULL);
+	}
+
+	return 0;
+}
+
+
+static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm,
+					     int group_cipher,
+					     int keylen, int maxkeylen,
+					     int *key_rsc_len,
+					     enum wpa_alg *alg)
+{
+	int klen;
+
+	*alg = wpa_cipher_to_alg(group_cipher);
+	if (*alg == WPA_ALG_NONE) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Unsupported Group Cipher %d",
+			group_cipher);
+		return -1;
+	}
+	*key_rsc_len = wpa_cipher_rsc_len(group_cipher);
+
+	klen = wpa_cipher_key_len(group_cipher);
+	if (keylen != klen || maxkeylen < klen) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Unsupported %s Group Cipher key length %d (%d)",
+			wpa_cipher_txt(group_cipher), keylen, maxkeylen);
+		return -1;
+	}
+	return 0;
+}
+
+
+struct wpa_gtk_data {
+	enum wpa_alg alg;
+	int tx, key_rsc_len, keyidx;
+	u8 gtk[32];
+	int gtk_len;
+};
+
+
+static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
+				      const struct wpa_gtk_data *gd,
+				      const u8 *key_rsc)
+{
+	const u8 *_gtk = gd->gtk;
+	u8 gtk_buf[32];
+
+	/* Detect possible key reinstallation */
+	if (sm->gtk.gtk_len == (size_t) gd->gtk_len &&
+	    os_memcmp(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len) == 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: Not reinstalling already in-use GTK to the driver (keyidx=%d tx=%d len=%d)",
+			gd->keyidx, gd->tx, gd->gtk_len);
+		return 0;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len);
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)",
+		gd->keyidx, gd->tx, gd->gtk_len);
+	wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len);
+	if (sm->group_cipher == WPA_CIPHER_TKIP) {
+		/* Swap Tx/Rx keys for Michael MIC */
+		os_memcpy(gtk_buf, gd->gtk, 16);
+		os_memcpy(gtk_buf + 16, gd->gtk + 24, 8);
+		os_memcpy(gtk_buf + 24, gd->gtk + 16, 8);
+		_gtk = gtk_buf;
+	}
+	if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
+		if (wpa_sm_set_key(sm, gd->alg, NULL,
+				   gd->keyidx, 1, key_rsc, gd->key_rsc_len,
+				   _gtk, gd->gtk_len) < 0) {
+			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,
+				  gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len,
+				  _gtk, gd->gtk_len) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"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));
+
+	sm->gtk.gtk_len = gd->gtk_len;
+	os_memcpy(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len);
+
+	return 0;
+}
+
+
+static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm,
+						int tx)
+{
+	if (tx && sm->pairwise_cipher != WPA_CIPHER_NONE) {
+		/* Ignore Tx bit for GTK if a pairwise key is used. One AP
+		 * seemed to set this bit (incorrectly, since Tx is only when
+		 * doing Group Key only APs) and without this workaround, the
+		 * data connection does not work because wpa_supplicant
+		 * configured non-zero keyidx to be used for unicast. */
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: Tx bit set for GTK, but pairwise "
+			"keys are used - ignore Tx bit");
+		return 0;
+	}
+	return tx;
+}
+
+
+static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
+				       const struct wpa_eapol_key *key,
+				       const u8 *gtk, size_t gtk_len,
+				       int key_info)
+{
+	struct wpa_gtk_data gd;
+
+	/*
+	 * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x
+	 * GTK KDE format:
+	 * KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7]
+	 * Reserved [bits 0-7]
+	 * GTK
+	 */
+
+	os_memset(&gd, 0, sizeof(gd));
+	wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake",
+			gtk, gtk_len);
+
+	if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk))
+		return -1;
+
+	gd.keyidx = gtk[0] & 0x3;
+	gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+						     !!(gtk[0] & BIT(2)));
+	gtk += 2;
+	gtk_len -= 2;
+
+	os_memcpy(gd.gtk, gtk, gtk_len);
+	gd.gtk_len = gtk_len;
+
+	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);
+	return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+static int wpa_supplicant_install_igtk(struct wpa_sm *sm,
+				       const struct wpa_igtk_kde *igtk)
+{
+	size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+	u16 keyidx = WPA_GET_LE16(igtk->keyid);
+
+	/* Detect possible key reinstallation */
+	if (sm->igtk.igtk_len == len &&
+	    os_memcmp(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len) == 0) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: Not reinstalling already in-use IGTK to the driver (keyidx=%d)",
+			keyidx);
+		return  0;
+	}
+
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"WPA: IGTK keyid %d pn %02x%02x%02x%02x%02x%02x",
+		keyidx, MAC2STR(igtk->pn));
+	wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", 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_cipher_to_alg(sm->mgmt_group_cipher),
+			   broadcast_ether_addr,
+			   keyidx, 0, igtk->pn, sizeof(igtk->pn),
+			   igtk->igtk, len) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Failed to configure IGTK to the driver");
+		return -1;
+	}
+
+	sm->igtk.igtk_len = len;
+	os_memcpy(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len);
+
+	return 0;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+static int ieee80211w_set_keys(struct wpa_sm *sm,
+			       struct wpa_eapol_ie_parse *ie)
+{
+#ifdef CONFIG_IEEE80211W
+	if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher))
+		return 0;
+
+	if (ie->igtk) {
+		size_t len;
+		const struct wpa_igtk_kde *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;
+		if (wpa_supplicant_install_igtk(sm, igtk) < 0)
+			return -1;
+	}
+
+	return 0;
+#else /* CONFIG_IEEE80211W */
+	return 0;
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+static void wpa_report_ie_mismatch(struct wpa_sm *sm,
+				   const char *reason, const u8 *src_addr,
+				   const u8 *wpa_ie, size_t wpa_ie_len,
+				   const u8 *rsn_ie, size_t rsn_ie_len)
+{
+	wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")",
+		reason, MAC2STR(src_addr));
+
+	if (sm->ap_wpa_ie) {
+		wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp",
+			    sm->ap_wpa_ie, sm->ap_wpa_ie_len);
+	}
+	if (wpa_ie) {
+		if (!sm->ap_wpa_ie) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"WPA: No WPA IE in Beacon/ProbeResp");
+		}
+		wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg",
+			    wpa_ie, wpa_ie_len);
+	}
+
+	if (sm->ap_rsn_ie) {
+		wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp",
+			    sm->ap_rsn_ie, sm->ap_rsn_ie_len);
+	}
+	if (rsn_ie) {
+		if (!sm->ap_rsn_ie) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"WPA: No RSN IE in Beacon/ProbeResp");
+		}
+		wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg",
+			    rsn_ie, rsn_ie_len);
+	}
+
+	wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+static int ft_validate_mdie(struct wpa_sm *sm,
+			    const unsigned char *src_addr,
+			    struct wpa_eapol_ie_parse *ie,
+			    const u8 *assoc_resp_mdie)
+{
+	struct rsn_mdie *mdie;
+
+	mdie = (struct rsn_mdie *) (ie->mdie + 2);
+	if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) ||
+	    os_memcmp(mdie->mobility_domain, sm->mobility_domain,
+		      MOBILITY_DOMAIN_ID_LEN) != 0) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE in msg 3/4 did "
+			"not match with the current mobility domain");
+		return -1;
+	}
+
+	if (assoc_resp_mdie &&
+	    (assoc_resp_mdie[1] != ie->mdie[1] ||
+	     os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE mismatch");
+		wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4",
+			    ie->mdie, 2 + ie->mdie[1]);
+		wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response",
+			    assoc_resp_mdie, 2 + assoc_resp_mdie[1]);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int ft_validate_ftie(struct wpa_sm *sm,
+			    const unsigned char *src_addr,
+			    struct wpa_eapol_ie_parse *ie,
+			    const u8 *assoc_resp_ftie)
+{
+	if (ie->ftie == NULL) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"FT: No FTIE in EAPOL-Key msg 3/4");
+		return -1;
+	}
+
+	if (assoc_resp_ftie == NULL)
+		return 0;
+
+	if (assoc_resp_ftie[1] != ie->ftie[1] ||
+	    os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: FTIE mismatch");
+		wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4",
+			    ie->ftie, 2 + ie->ftie[1]);
+		wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response",
+			    assoc_resp_ftie, 2 + assoc_resp_ftie[1]);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int ft_validate_rsnie(struct wpa_sm *sm,
+			     const unsigned char *src_addr,
+			     struct wpa_eapol_ie_parse *ie)
+{
+	struct wpa_ie_data rsn;
+
+	if (!ie->rsn_ie)
+		return 0;
+
+	/*
+	 * Verify that PMKR1Name from EAPOL-Key message 3/4
+	 * matches with the value we derived.
+	 */
+	if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 ||
+	    rsn.num_pmkid != 1 || rsn.pmkid == NULL) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No PMKR1Name in "
+			"FT 4-way handshake message 3/4");
+		return -1;
+	}
+
+	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");
+		wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Authenticator",
+			    rsn.pmkid, WPA_PMK_NAME_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
+			    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_supplicant_validate_ie_ft(struct wpa_sm *sm,
+					 const unsigned char *src_addr,
+					 struct wpa_eapol_ie_parse *ie)
+{
+	const u8 *pos, *end, *mdie = NULL, *ftie = NULL;
+
+	if (sm->assoc_resp_ies) {
+		pos = sm->assoc_resp_ies;
+		end = pos + sm->assoc_resp_ies_len;
+		while (pos + 2 < end) {
+			if (pos + 2 + pos[1] > end)
+				break;
+			switch (*pos) {
+			case WLAN_EID_MOBILITY_DOMAIN:
+				mdie = pos;
+				break;
+			case WLAN_EID_FAST_BSS_TRANSITION:
+				ftie = pos;
+				break;
+			}
+			pos += 2 + pos[1];
+		}
+	}
+
+	if (ft_validate_mdie(sm, src_addr, ie, mdie) < 0 ||
+	    ft_validate_ftie(sm, src_addr, ie, ftie) < 0 ||
+	    ft_validate_rsnie(sm, src_addr, ie) < 0)
+		return -1;
+
+	return 0;
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+static int wpa_supplicant_validate_ie(struct wpa_sm *sm,
+				      const unsigned char *src_addr,
+				      struct wpa_eapol_ie_parse *ie)
+{
+	if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: No WPA/RSN IE for this AP known. "
+			"Trying to get from scan results");
+		if (wpa_sm_get_beacon_ie(sm) < 0) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: Could not find AP from "
+				"the scan results");
+		} else {
+			wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
+				"WPA: Found the current AP from "
+				"updated scan results");
+		}
+	}
+
+	if (ie->wpa_ie == NULL && ie->rsn_ie == NULL &&
+	    (sm->ap_wpa_ie || sm->ap_rsn_ie)) {
+		wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match "
+				       "with IE in Beacon/ProbeResp (no IE?)",
+				       src_addr, ie->wpa_ie, ie->wpa_ie_len,
+				       ie->rsn_ie, ie->rsn_ie_len);
+		return -1;
+	}
+
+	if ((ie->wpa_ie && sm->ap_wpa_ie &&
+	     (ie->wpa_ie_len != sm->ap_wpa_ie_len ||
+	      os_memcmp(ie->wpa_ie, sm->ap_wpa_ie, ie->wpa_ie_len) != 0)) ||
+	    (ie->rsn_ie && sm->ap_rsn_ie &&
+	     wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt),
+				sm->ap_rsn_ie, sm->ap_rsn_ie_len,
+				ie->rsn_ie, ie->rsn_ie_len))) {
+		wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match "
+				       "with IE in Beacon/ProbeResp",
+				       src_addr, ie->wpa_ie, ie->wpa_ie_len,
+				       ie->rsn_ie, ie->rsn_ie_len);
+		return -1;
+	}
+
+	if (sm->proto == WPA_PROTO_WPA &&
+	    ie->rsn_ie && sm->ap_rsn_ie == NULL && sm->rsn_enabled) {
+		wpa_report_ie_mismatch(sm, "Possible downgrade attack "
+				       "detected - RSN was enabled and RSN IE "
+				       "was in msg 3/4, but not in "
+				       "Beacon/ProbeResp",
+				       src_addr, ie->wpa_ie, ie->wpa_ie_len,
+				       ie->rsn_ie, ie->rsn_ie_len);
+		return -1;
+	}
+
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->key_mgmt) &&
+	    wpa_supplicant_validate_ie_ft(sm, src_addr, ie) < 0)
+		return -1;
+#endif /* CONFIG_IEEE80211R */
+
+	return 0;
+}
+
+
+/**
+ * wpa_supplicant_send_4_of_4 - Send message 4 of WPA/RSN 4-Way Handshake
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @dst: Destination address for the frame
+ * @key: Pointer to the EAPOL-Key frame header
+ * @ver: Version bits from EAPOL-Key Key Info
+ * @key_info: Key Info
+ * @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,
+			       struct wpa_ptk *ptk)
+{
+	size_t mic_len, hdrlen, rlen;
+	struct wpa_eapol_key *reply;
+	struct wpa_eapol_key_192 *reply192;
+	u8 *rbuf, *key_mic;
+
+	mic_len = wpa_mic_len(sm->key_mgmt);
+	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+				  hdrlen, &rlen, (void *) &reply);
+	if (rbuf == NULL)
+		return -1;
+	reply192 = (struct wpa_eapol_key_192 *) reply;
+
+	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 || 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);
+
+	key_mic = reply192->key_mic; /* same offset for reply and reply192 */
+	if (mic_len == 24)
+		WPA_PUT_BE16(reply192->key_data_length, 0);
+	else
+		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, ptk->kck_len, ver, dst, ETH_P_EAPOL,
+			   rbuf, rlen, key_mic);
+
+	return 0;
+}
+
+
+static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
+					  const struct wpa_eapol_key *key,
+					  u16 ver, const u8 *key_data,
+					  size_t key_data_len)
+{
+	u16 key_info, keylen;
+	struct wpa_eapol_ie_parse ie;
+
+	wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 3 of 4-Way "
+		"Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver);
+
+	key_info = WPA_GET_BE16(key->key_info);
+
+	wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len);
+	if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+		goto failed;
+	if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: GTK IE in unencrypted key data");
+		goto failed;
+	}
+#ifdef CONFIG_IEEE80211W
+	if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: IGTK KDE in unencrypted key data");
+		goto failed;
+	}
+
+	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);
+		goto failed;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
+		goto failed;
+
+	if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: ANonce from message 1 of 4-Way Handshake "
+			"differs from 3 of 4-Way Handshake - drop packet (src="
+			MACSTR ")", MAC2STR(sm->bssid));
+		goto failed;
+	}
+
+	keylen = WPA_GET_BE16(key->key_length);
+	if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Invalid %s key length %d (src=" MACSTR
+			")", wpa_cipher_txt(sm->pairwise_cipher), keylen,
+			MAC2STR(sm->bssid));
+		goto failed;
+	}
+
+#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,
+				       &sm->ptk)) {
+		goto failed;
+	}
+
+	/* SNonce was successfully used in msg 3/4, so mark it to be renewed
+	 * for the next 4-Way Handshake. If msg 3 is received again, the old
+	 * SNonce will still be used to avoid changing PTK. */
+	sm->renew_snonce = 1;
+
+	if (key_info & WPA_KEY_INFO_INSTALL) {
+		if (wpa_supplicant_install_ptk(sm, key))
+			goto failed;
+	}
+
+	if (key_info & WPA_KEY_INFO_SECURE) {
+		wpa_sm_mlme_setprotection(
+			sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX,
+			MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+		eapol_sm_notify_portValid(sm->eapol, TRUE);
+	}
+	wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+	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,
+			"RSN: Failed to configure GTK");
+		goto failed;
+	}
+
+	if (ieee80211w_set_keys(sm, &ie) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Failed to configure IGTK");
+		goto failed;
+	}
+
+	if (ie.gtk)
+		wpa_sm_set_rekey_offload(sm);
+
+	if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+		struct rsn_pmksa_cache_entry *sa;
+
+		sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len,
+				     sm->ptk.kck, sm->ptk.kck_len,
+				     sm->bssid, sm->own_addr,
+				     sm->network_ctx, sm->key_mgmt);
+		if (!sm->cur_pmksa)
+			sm->cur_pmksa = sa;
+	}
+
+	sm->msg_3_of_4_ok = 1;
+	return;
+
+failed:
+	wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
+static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
+					     const u8 *keydata,
+					     size_t keydatalen,
+					     u16 key_info,
+					     struct wpa_gtk_data *gd)
+{
+	int maxkeylen;
+	struct wpa_eapol_ie_parse ie;
+
+	wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen);
+	if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0)
+		return -1;
+	if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: GTK IE in unencrypted key data");
+		return -1;
+	}
+	if (ie.gtk == NULL) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: No GTK IE in Group Key msg 1/2");
+		return -1;
+	}
+	maxkeylen = gd->gtk_len = ie.gtk_len - 2;
+
+	if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+					      gd->gtk_len, maxkeylen,
+					      &gd->key_rsc_len, &gd->alg))
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake",
+			ie.gtk, ie.gtk_len);
+	gd->keyidx = ie.gtk[0] & 0x3;
+	gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+						      !!(ie.gtk[0] & BIT(2)));
+	if (ie.gtk_len - 2 > sizeof(gd->gtk)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Too long GTK in GTK IE (len=%lu)",
+			(unsigned long) ie.gtk_len - 2);
+		return -1;
+	}
+	os_memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2);
+
+	if (ieee80211w_set_keys(sm, &ie) < 0)
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Failed to configure IGTK");
+
+	return 0;
+}
+
+
+static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
+					     const struct wpa_eapol_key *key,
+					     const u8 *key_data,
+					     size_t key_data_len, u16 key_info,
+					     u16 ver, struct wpa_gtk_data *gd)
+{
+	size_t maxkeylen;
+	u16 gtk_len;
+
+	gtk_len = WPA_GET_BE16(key->key_length);
+	maxkeylen = key_data_len;
+	if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+		if (maxkeylen < 8) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"WPA: Too short maxkeylen (%lu)",
+				(unsigned long) maxkeylen);
+			return -1;
+		}
+		maxkeylen -= 8;
+	}
+
+	if (gtk_len > maxkeylen ||
+	    wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+					      gtk_len, maxkeylen,
+					      &gd->key_rsc_len, &gd->alg))
+		return -1;
+
+	gd->gtk_len = gtk_len;
+	gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+		WPA_KEY_INFO_KEY_INDEX_SHIFT;
+	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
+#ifdef CONFIG_NO_RC4
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: RC4 not supported in the build");
+		return -1;
+#else /* CONFIG_NO_RC4 */
+		u8 ek[32];
+		if (key_data_len > sizeof(gd->gtk)) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: RC4 key data too long (%lu)",
+				(unsigned long) key_data_len);
+			return -1;
+		}
+		os_memcpy(ek, key->key_iv, 16);
+		os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
+		os_memcpy(gd->gtk, key_data, key_data_len);
+		if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) {
+			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));
+#endif /* CONFIG_NO_RC4 */
+	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+		if (maxkeylen % 8) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: Unsupported AES-WRAP len %lu",
+				(unsigned long) maxkeylen);
+			return -1;
+		}
+		if (maxkeylen > sizeof(gd->gtk)) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: AES-WRAP key data "
+				"too long (keydatalen=%lu maxkeylen=%lu)",
+				(unsigned long) key_data_len,
+				(unsigned long) maxkeylen);
+			return -1;
+		}
+		if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8,
+			       key_data, gd->gtk)) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: AES unwrap failed - could not decrypt "
+				"GTK");
+			return -1;
+		}
+	} else {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Unsupported key_info type %d", ver);
+		return -1;
+	}
+	gd->tx = wpa_supplicant_gtk_tx_bit_workaround(
+		sm, !!(key_info & WPA_KEY_INFO_TXRX));
+	return 0;
+}
+
+
+static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
+				      const struct wpa_eapol_key *key,
+				      int ver, u16 key_info)
+{
+	size_t mic_len, hdrlen, rlen;
+	struct wpa_eapol_key *reply;
+	struct wpa_eapol_key_192 *reply192;
+	u8 *rbuf, *key_mic;
+
+	mic_len = wpa_mic_len(sm->key_mgmt);
+	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+				  hdrlen, &rlen, (void *) &reply);
+	if (rbuf == NULL)
+		return -1;
+	reply192 = (struct wpa_eapol_key_192 *) reply;
+
+	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 || 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);
+
+	key_mic = reply192->key_mic; /* same offset for reply and reply192 */
+	if (mic_len == 24)
+		WPA_PUT_BE16(reply192->key_data_length, 0);
+	else
+		WPA_PUT_BE16(reply->key_data_length, 0);
+
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
+	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid,
+			   ETH_P_EAPOL, rbuf, rlen, key_mic);
+
+	return 0;
+}
+
+
+static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
+					  const unsigned char *src_addr,
+					  const struct wpa_eapol_key *key,
+					  const u8 *key_data,
+					  size_t key_data_len, u16 ver)
+{
+	u16 key_info;
+	int rekey, ret;
+	struct wpa_gtk_data gd;
+
+	if (!sm->msg_3_of_4_ok) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: Group Key Handshake started prior to completion of 4-way handshake");
+		goto failed;
+	}
+
+	os_memset(&gd, 0, sizeof(gd));
+
+	rekey = wpa_sm_get_state(sm) == WPA_COMPLETED;
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of Group Key "
+		"Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+	key_info = WPA_GET_BE16(key->key_info);
+
+	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
+		ret = wpa_supplicant_process_1_of_2_rsn(sm, key_data,
+							key_data_len, key_info,
+							&gd);
+	} else {
+		ret = wpa_supplicant_process_1_of_2_wpa(sm, key, key_data,
+							key_data_len,
+							key_info, ver, &gd);
+	}
+
+	wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+	if (ret)
+		goto failed;
+
+	if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) ||
+	    wpa_supplicant_send_2_of_2(sm, key, ver, key_info))
+		goto failed;
+	os_memset(&gd, 0, sizeof(gd));
+
+	if (rekey) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying "
+			"completed with " MACSTR " [GTK=%s]",
+			MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
+		wpa_sm_cancel_auth_timeout(sm);
+		wpa_sm_set_state(sm, WPA_COMPLETED);
+	} else {
+		wpa_supplicant_key_neg_complete(sm, sm->bssid,
+						key_info &
+						WPA_KEY_INFO_SECURE);
+	}
+
+	wpa_sm_set_rekey_offload(sm);
+
+	return;
+
+failed:
+	os_memset(&gd, 0, sizeof(gd));
+	wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
+static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
+					       struct wpa_eapol_key_192 *key,
+					       u16 ver,
+					       const u8 *buf, size_t len)
+{
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	int ok = 0;
+	size_t mic_len = wpa_mic_len(sm->key_mgmt);
+
+	os_memcpy(mic, key->key_mic, mic_len);
+	if (sm->tptk_set) {
+		os_memset(key->key_mic, 0, mic_len);
+		wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt,
+				  ver, buf, len, key->key_mic);
+		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: Invalid EAPOL-Key MIC "
+				"when using TPTK - ignoring TPTK");
+		} else {
+			ok = 1;
+			sm->tptk_set = 0;
+			sm->ptk_set = 1;
+			os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
+			os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+		}
+	}
+
+	if (!ok && sm->ptk_set) {
+		os_memset(key->key_mic, 0, mic_len);
+		wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt,
+				  ver, buf, len, key->key_mic);
+		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: Invalid EAPOL-Key MIC - "
+				"dropping packet");
+			return -1;
+		}
+		ok = 1;
+	}
+
+	if (!ok) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Could not verify EAPOL-Key MIC - "
+			"dropping packet");
+		return -1;
+	}
+
+	os_memcpy(sm->rx_replay_counter, key->replay_counter,
+		  WPA_REPLAY_COUNTER_LEN);
+	sm->rx_replay_counter_set = 1;
+	return 0;
+}
+
+
+/* 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,
+					   u8 *key_data, size_t *key_data_len)
+{
+	wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
+		    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 "
+			"Data");
+		return -1;
+	}
+
+	/* Decrypt key data here so that this operation does not need
+	 * to be implemented separately for each message type. */
+	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
+#ifdef CONFIG_NO_RC4
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: RC4 not supported in the build");
+		return -1;
+#else /* CONFIG_NO_RC4 */
+		u8 ek[32];
+		os_memcpy(ek, key->key_iv, 16);
+		os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
+		if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) {
+			os_memset(ek, 0, sizeof(ek));
+			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+				"WPA: RC4 failed");
+			return -1;
+		}
+		os_memset(ek, 0, sizeof(ek));
+#endif /* CONFIG_NO_RC4 */
+	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+		   ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
+		   sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
+		   wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+		u8 *buf;
+		if (*key_data_len < 8 || *key_data_len % 8) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: Unsupported AES-WRAP len %u",
+				(unsigned int) *key_data_len);
+			return -1;
+		}
+		*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, sm->ptk.kek_len, *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_data, buf, *key_data_len);
+		os_free(buf);
+		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",
+			key_data, *key_data_len);
+	return 0;
+}
+
+
+/**
+ * wpa_sm_aborted_cached - Notify WPA that PMKSA caching was aborted
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void wpa_sm_aborted_cached(struct wpa_sm *sm)
+{
+	if (sm && sm->cur_pmksa) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"RSN: Cancelling PMKSA caching attempt");
+		sm->cur_pmksa = NULL;
+	}
+}
+
+
+static void wpa_eapol_key_dump(struct wpa_sm *sm,
+			       const struct wpa_eapol_key *key,
+			       unsigned int key_data_len,
+			       const u8 *mic, unsigned int mic_len)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+	u16 key_info = WPA_GET_BE16(key->key_info);
+
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "  EAPOL-Key type=%d", key->type);
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"  key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s%s%s%s%s%s%s%s)",
+		key_info, key_info & WPA_KEY_INFO_TYPE_MASK,
+		(key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+		WPA_KEY_INFO_KEY_INDEX_SHIFT,
+		(key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13,
+		key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group",
+		key_info & WPA_KEY_INFO_INSTALL ? " Install" : "",
+		key_info & WPA_KEY_INFO_ACK ? " Ack" : "",
+		key_info & WPA_KEY_INFO_MIC ? " MIC" : "",
+		key_info & WPA_KEY_INFO_SECURE ? " Secure" : "",
+		key_info & WPA_KEY_INFO_ERROR ? " Error" : "",
+		key_info & WPA_KEY_INFO_REQUEST ? " Request" : "",
+		key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : "");
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"  key_length=%u key_data_length=%u",
+		WPA_GET_BE16(key->key_length), key_data_len);
+	wpa_hexdump(MSG_DEBUG, "  replay_counter",
+		    key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+	wpa_hexdump(MSG_DEBUG, "  key_nonce", key->key_nonce, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "  key_iv", key->key_iv, 16);
+	wpa_hexdump(MSG_DEBUG, "  key_rsc", key->key_rsc, 8);
+	wpa_hexdump(MSG_DEBUG, "  key_id (reserved)", key->key_id, 8);
+	wpa_hexdump(MSG_DEBUG, "  key_mic", mic, mic_len);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+/**
+ * wpa_sm_rx_eapol - Process received WPA EAPOL frames
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @src_addr: Source MAC address of the EAPOL packet
+ * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
+ * @len: Length of the EAPOL frame
+ * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure
+ *
+ * This function is called for each received EAPOL frame. Other than EAPOL-Key
+ * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is
+ * only processing WPA and WPA2 EAPOL-Key frames.
+ *
+ * The received EAPOL-Key packets are validated and valid packets are replied
+ * to. In addition, key material (PTK, GTK) is configured at the end of a
+ * successful key handshake.
+ */
+int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
+		    const u8 *buf, size_t len)
+{
+	size_t plen, data_len, key_data_len;
+	const struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	struct wpa_eapol_key_192 *key192;
+	u16 key_info, ver;
+	u8 *tmp = NULL;
+	int ret = -1;
+	struct wpa_peerkey *peerkey = NULL;
+	u8 *key_data;
+	size_t mic_len, keyhdrlen;
+
+#ifdef CONFIG_IEEE80211R
+	sm->ft_completed = 0;
+#endif /* CONFIG_IEEE80211R */
+
+	mic_len = wpa_mic_len(sm->key_mgmt);
+	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+	if (len < sizeof(*hdr) + keyhdrlen) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: EAPOL frame too short to be a WPA "
+			"EAPOL-Key (len %lu, expecting at least %lu)",
+			(unsigned long) len,
+			(unsigned long) sizeof(*hdr) + keyhdrlen);
+		return 0;
+	}
+
+	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,
+		"IEEE 802.1X RX: version=%d type=%d length=%lu",
+		hdr->version, hdr->type, (unsigned long) plen);
+
+	if (hdr->version < EAPOL_VERSION) {
+		/* TODO: backwards compatibility */
+	}
+	if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: EAPOL frame (type %u) discarded, "
+			"not a Key frame", hdr->type);
+		ret = 0;
+		goto out;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len);
+	if (plen > len - sizeof(*hdr) || plen < keyhdrlen) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: EAPOL frame payload size %lu "
+			"invalid (frame size %lu)",
+			(unsigned long) plen, (unsigned long) len);
+		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));
+	key192 = (struct wpa_eapol_key_192 *)
+		(tmp + sizeof(struct ieee802_1x_hdr));
+	if (mic_len == 24)
+		key_data = (u8 *) (key192 + 1);
+	else
+		key_data = (u8 *) (key + 1);
+
+	if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
+	{
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: EAPOL-Key type (%d) unknown, discarded",
+			key->type);
+		ret = 0;
+		goto out;
+	}
+
+	if (mic_len == 24)
+		key_data_len = WPA_GET_BE16(key192->key_data_length);
+	else
+		key_data_len = WPA_GET_BE16(key->key_data_length);
+	wpa_eapol_key_dump(sm, key, key_data_len, key192->key_mic, mic_len);
+
+	if (key_data_len > plen - keyhdrlen) {
+		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 - keyhdrlen));
+		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 &&
+	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+	    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;
+	}
+
+	if (wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+	    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)",
+			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). */
+		if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"FT: AP did not use AES-128-CMAC");
+			goto out;
+		}
+	} else
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+	if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
+		if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+		    sm->key_mgmt != WPA_KEY_MGMT_OSEN &&
+		    !wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"WPA: AP did not use the "
+				"negotiated AES-128-CMAC");
+			goto out;
+		}
+	} else
+#endif /* CONFIG_IEEE80211W */
+	if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
+	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: CCMP is used, but EAPOL-Key "
+			"descriptor version (%d) is not 2", ver);
+		if (sm->group_cipher != WPA_CIPHER_CCMP &&
+		    !(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+			/* Earlier versions of IEEE 802.11i did not explicitly
+			 * require version 2 descriptor for all EAPOL-Key
+			 * packets, so allow group keys to use version 1 if
+			 * CCMP is not used for them. */
+			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;
+	} else if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
+		   !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+		   ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: GCMP is used, but EAPOL-Key "
+			"descriptor version (%d) is not 2", ver);
+		goto out;
+	}
+
+#ifdef CONFIG_PEERKEY
+	for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+		if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) {
+		if (!peerkey->initiator && peerkey->replay_counter_set &&
+		    os_memcmp(key->replay_counter, peerkey->replay_counter,
+			      WPA_REPLAY_COUNTER_LEN) <= 0) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"RSN: EAPOL-Key Replay Counter did not "
+				"increase (STK) - dropping packet");
+			goto out;
+		} else if (peerkey->initiator) {
+			u8 _tmp[WPA_REPLAY_COUNTER_LEN];
+			os_memcpy(_tmp, key->replay_counter,
+				  WPA_REPLAY_COUNTER_LEN);
+			inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN);
+			if (os_memcmp(_tmp, peerkey->replay_counter,
+				      WPA_REPLAY_COUNTER_LEN) != 0) {
+				wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+					"RSN: EAPOL-Key Replay "
+					"Counter did not match (STK) - "
+					"dropping packet");
+				goto out;
+			}
+		}
+	}
+
+	if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Ack bit in key_info from STK peer");
+		goto out;
+	}
+#endif /* CONFIG_PEERKEY */
+
+	if (!peerkey && sm->rx_replay_counter_set &&
+	    os_memcmp(key->replay_counter, sm->rx_replay_counter,
+		      WPA_REPLAY_COUNTER_LEN) <= 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: EAPOL-Key Replay Counter did not increase - "
+			"dropping packet");
+		goto out;
+	}
+
+	if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE))
+#ifdef CONFIG_PEERKEY
+	    && (peerkey == NULL || !peerkey->initiator)
+#endif /* CONFIG_PEERKEY */
+		) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: No Ack bit in key_info");
+		goto out;
+	}
+
+	if (key_info & WPA_KEY_INFO_REQUEST) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: EAPOL-Key with Request bit - dropped");
+		goto out;
+	}
+
+	if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
+	    wpa_supplicant_verify_eapol_key_mic(sm, key192, ver, tmp, data_len))
+		goto out;
+
+#ifdef CONFIG_PEERKEY
+	if ((key_info & WPA_KEY_INFO_MIC) && peerkey &&
+	    peerkey_verify_eapol_key_mic(sm, peerkey, key192, ver, tmp,
+					 data_len))
+		goto out;
+#endif /* CONFIG_PEERKEY */
+
+	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, key_data,
+						    &key_data_len))
+			goto out;
+	}
+
+	if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+		if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: Ignored EAPOL-Key (Pairwise) with "
+				"non-zero key index");
+			goto out;
+		}
+		if (peerkey) {
+			/* PeerKey 4-Way Handshake */
+			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, key_data,
+						      key_data_len);
+		} else {
+			/* 1/4 4-Way Handshake */
+			wpa_supplicant_process_1_of_4(sm, src_addr, key,
+						      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, 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,
+						      key_data, key_data_len,
+						      ver);
+		} else {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: EAPOL-Key (Group) without Mic bit - "
+				"dropped");
+		}
+	}
+
+	ret = 1;
+
+out:
+	bin_clear_free(tmp, data_len);
+	return ret;
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
+{
+	switch (sm->key_mgmt) {
+	case WPA_KEY_MGMT_IEEE8021X:
+		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:
+		return (sm->proto == WPA_PROTO_RSN ?
+			RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X :
+			WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+#ifdef CONFIG_IEEE80211R
+	case WPA_KEY_MGMT_FT_IEEE8021X:
+		return RSN_AUTH_KEY_MGMT_FT_802_1X;
+	case WPA_KEY_MGMT_FT_PSK:
+		return RSN_AUTH_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+	case WPA_KEY_MGMT_IEEE8021X_SHA256:
+		return RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+	case WPA_KEY_MGMT_PSK_SHA256:
+		return RSN_AUTH_KEY_MGMT_PSK_SHA256;
+#endif /* CONFIG_IEEE80211W */
+	case WPA_KEY_MGMT_CCKM:
+		return (sm->proto == WPA_PROTO_RSN ?
+			RSN_AUTH_KEY_MGMT_CCKM:
+			WPA_AUTH_KEY_MGMT_CCKM);
+	case WPA_KEY_MGMT_WPA_NONE:
+		return WPA_AUTH_KEY_MGMT_NONE;
+	case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+		return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
+	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+		return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+	default:
+		return 0;
+	}
+}
+
+
+#define RSN_SUITE "%02x-%02x-%02x-%d"
+#define RSN_SUITE_ARG(s) \
+((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
+
+/**
+ * wpa_sm_get_mib - Dump text list of MIB entries
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for the list
+ * @buflen: Length of the buffer
+ * Returns: Number of bytes written to buffer
+ *
+ * This function is used fetch dot11 MIB variables.
+ */
+int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
+{
+	char pmkid_txt[PMKID_LEN * 2 + 1];
+	int rsna, ret;
+	size_t len;
+
+	if (sm->cur_pmksa) {
+		wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt),
+				 sm->cur_pmksa->pmkid, PMKID_LEN);
+	} else
+		pmkid_txt[0] = '\0';
+
+	if ((wpa_key_mgmt_wpa_psk(sm->key_mgmt) ||
+	     wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) &&
+	    sm->proto == WPA_PROTO_RSN)
+		rsna = 1;
+	else
+		rsna = 0;
+
+	ret = os_snprintf(buf, buflen,
+			  "dot11RSNAOptionImplemented=TRUE\n"
+			  "dot11RSNAPreauthenticationImplemented=TRUE\n"
+			  "dot11RSNAEnabled=%s\n"
+			  "dot11RSNAPreauthenticationEnabled=%s\n"
+			  "dot11RSNAConfigVersion=%d\n"
+			  "dot11RSNAConfigPairwiseKeysSupported=5\n"
+			  "dot11RSNAConfigGroupCipherSize=%d\n"
+			  "dot11RSNAConfigPMKLifetime=%d\n"
+			  "dot11RSNAConfigPMKReauthThreshold=%d\n"
+			  "dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n"
+			  "dot11RSNAConfigSATimeout=%d\n",
+			  rsna ? "TRUE" : "FALSE",
+			  rsna ? "TRUE" : "FALSE",
+			  RSN_VERSION,
+			  wpa_cipher_key_len(sm->group_cipher) * 8,
+			  sm->dot11RSNAConfigPMKLifetime,
+			  sm->dot11RSNAConfigPMKReauthThreshold,
+			  sm->dot11RSNAConfigSATimeout);
+	if (os_snprintf_error(buflen, ret))
+		return 0;
+	len = ret;
+
+	ret = os_snprintf(
+		buf + len, buflen - len,
+		"dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
+		"dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
+		"dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
+		"dot11RSNAPMKIDUsed=%s\n"
+		"dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
+		"dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
+		"dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
+		"dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n"
+		"dot11RSNA4WayHandshakeFailures=%u\n",
+		RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
+		RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+						  sm->pairwise_cipher)),
+		RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+						  sm->group_cipher)),
+		pmkid_txt,
+		RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
+		RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+						  sm->pairwise_cipher)),
+		RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+						  sm->group_cipher)),
+		sm->dot11RSNA4WayHandshakeFailures);
+	if (!os_snprintf_error(buflen - len, ret))
+		len += ret;
+
+	return (int) len;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
+				 void *ctx, enum pmksa_free_reason reason)
+{
+	struct wpa_sm *sm = ctx;
+	int deauth = 0;
+
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA cache entry free_cb: "
+		MACSTR " reason=%d", MAC2STR(entry->aa), reason);
+
+	if (sm->cur_pmksa == entry) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"RSN: %s current PMKSA entry",
+			reason == PMKSA_REPLACE ? "replaced" : "removed");
+		pmksa_cache_clear_current(sm);
+
+		/*
+		 * If an entry is simply being replaced, there's no need to
+		 * deauthenticate because it will be immediately re-added.
+		 * This happens when EAP authentication is completed again
+		 * (reauth or failed PMKSA caching attempt).
+		 */
+		if (reason != PMKSA_REPLACE)
+			deauth = 1;
+	}
+
+	if (reason == PMKSA_EXPIRE &&
+	    (sm->pmk_len == entry->pmk_len &&
+	     os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"RSN: deauthenticating due to expired PMK");
+		pmksa_cache_clear_current(sm);
+		deauth = 1;
+	}
+
+	if (deauth) {
+		os_memset(sm->pmk, 0, sizeof(sm->pmk));
+		wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+	}
+}
+
+
+/**
+ * wpa_sm_init - Initialize WPA state machine
+ * @ctx: Context pointer for callbacks; this needs to be an allocated buffer
+ * Returns: Pointer to the allocated WPA state machine data
+ *
+ * This function is used to allocate a new WPA state machine and the returned
+ * value is passed to all WPA state machine calls.
+ */
+struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
+{
+	struct wpa_sm *sm;
+
+	sm = os_zalloc(sizeof(*sm));
+	if (sm == NULL)
+		return NULL;
+	dl_list_init(&sm->pmksa_candidates);
+	sm->renew_snonce = 1;
+	sm->ctx = ctx;
+
+	sm->dot11RSNAConfigPMKLifetime = 43200;
+	sm->dot11RSNAConfigPMKReauthThreshold = 70;
+	sm->dot11RSNAConfigSATimeout = 60;
+
+	sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm);
+	if (sm->pmksa == NULL) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+			"RSN: PMKSA cache initialization failed");
+		os_free(sm);
+		return NULL;
+	}
+
+	return sm;
+}
+
+
+/**
+ * wpa_sm_deinit - Deinitialize WPA state machine
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void wpa_sm_deinit(struct wpa_sm *sm)
+{
+	if (sm == NULL)
+		return;
+	pmksa_cache_deinit(sm->pmksa);
+	eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
+	eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
+	os_free(sm->assoc_wpa_ie);
+	os_free(sm->ap_wpa_ie);
+	os_free(sm->ap_rsn_ie);
+	wpa_sm_drop_sa(sm);
+	os_free(sm->ctx);
+	peerkey_deinit(sm);
+#ifdef CONFIG_IEEE80211R
+	os_free(sm->assoc_resp_ies);
+#endif /* CONFIG_IEEE80211R */
+	os_free(sm);
+}
+
+
+/**
+ * wpa_sm_notify_assoc - Notify WPA state machine about association
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @bssid: The BSSID of the new association
+ *
+ * This function is called to let WPA state machine know that the connection
+ * was established.
+ */
+void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
+{
+	int clear_keys = 1;
+
+	if (sm == NULL)
+		return;
+
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"WPA: Association event - clear replay counter");
+	os_memcpy(sm->bssid, bssid, ETH_ALEN);
+	os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
+	sm->rx_replay_counter_set = 0;
+	sm->renew_snonce = 1;
+	if (os_memcmp(sm->preauth_bssid, bssid, ETH_ALEN) == 0)
+		rsn_preauth_deinit(sm);
+
+#ifdef CONFIG_IEEE80211R
+	if (wpa_ft_is_completed(sm)) {
+		/*
+		 * Clear portValid to kick EAPOL state machine to re-enter
+		 * AUTHENTICATED state to get the EAPOL port Authorized.
+		 */
+		eapol_sm_notify_portValid(sm->eapol, FALSE);
+		wpa_supplicant_key_neg_complete(sm, sm->bssid, 1);
+
+		/* Prepare for the next transition */
+		wpa_ft_prepare_auth_request(sm, NULL);
+
+		clear_keys = 0;
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	if (clear_keys) {
+		/*
+		 * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if
+		 * this is not part of a Fast BSS Transition.
+		 */
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK");
+		sm->ptk_set = 0;
+		os_memset(&sm->ptk, 0, sizeof(sm->ptk));
+		sm->tptk_set = 0;
+		os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+		os_memset(&sm->gtk, 0, sizeof(sm->gtk));
+#ifdef CONFIG_IEEE80211W
+		os_memset(&sm->igtk, 0, sizeof(sm->igtk));
+#endif /* CONFIG_IEEE80211W */
+	}
+
+#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 */
+}
+
+
+/**
+ * wpa_sm_notify_disassoc - Notify WPA state machine about disassociation
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * This function is called to let WPA state machine know that the connection
+ * was lost. This will abort any existing pre-authentication session.
+ */
+void wpa_sm_notify_disassoc(struct wpa_sm *sm)
+{
+	eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
+	eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
+	peerkey_deinit(sm);
+	rsn_preauth_deinit(sm);
+	pmksa_cache_clear_current(sm);
+	if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
+		sm->dot11RSNA4WayHandshakeFailures++;
+#ifdef CONFIG_TDLS
+	wpa_tdls_disassoc(sm);
+#endif /* CONFIG_TDLS */
+
+	/* Keys are not needed in the WPA state machine anymore */
+	wpa_sm_drop_sa(sm);
+
+	sm->msg_3_of_4_ok = 0;
+}
+
+
+/**
+ * wpa_sm_set_pmk - Set PMK
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @pmk: The new PMK
+ * @pmk_len: The length of the new PMK in bytes
+ * @bssid: AA to add into PMKSA cache or %NULL to not cache the PMK
+ *
+ * Configure the PMK for WPA state machine.
+ */
+void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+		    const u8 *bssid)
+{
+	if (sm == NULL)
+		return;
+
+	sm->pmk_len = pmk_len;
+	os_memcpy(sm->pmk, pmk, pmk_len);
+
+#ifdef CONFIG_IEEE80211R
+	/* Set XXKey to be PSK for FT key derivation */
+	sm->xxkey_len = pmk_len;
+	os_memcpy(sm->xxkey, pmk, pmk_len);
+#endif /* CONFIG_IEEE80211R */
+
+	if (bssid) {
+		pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, 0,
+				bssid, sm->own_addr,
+				sm->network_ctx, sm->key_mgmt);
+	}
+}
+
+
+/**
+ * wpa_sm_set_pmk_from_pmksa - Set PMK based on the current PMKSA
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * Take the PMK from the current PMKSA into use. If no PMKSA is active, the PMK
+ * will be cleared.
+ */
+void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm)
+{
+	if (sm == NULL)
+		return;
+
+	if (sm->cur_pmksa) {
+		sm->pmk_len = sm->cur_pmksa->pmk_len;
+		os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len);
+	} else {
+		sm->pmk_len = PMK_LEN;
+		os_memset(sm->pmk, 0, PMK_LEN);
+	}
+}
+
+
+/**
+ * wpa_sm_set_fast_reauth - Set fast reauthentication (EAP) enabled/disabled
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @fast_reauth: Whether fast reauthentication (EAP) is allowed
+ */
+void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth)
+{
+	if (sm)
+		sm->fast_reauth = fast_reauth;
+}
+
+
+/**
+ * wpa_sm_set_scard_ctx - Set context pointer for smartcard callbacks
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @scard_ctx: Context pointer for smartcard related callback functions
+ */
+void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx)
+{
+	if (sm == NULL)
+		return;
+	sm->scard_ctx = scard_ctx;
+	if (sm->preauth_eapol)
+		eapol_sm_register_scard_ctx(sm->preauth_eapol, scard_ctx);
+}
+
+
+/**
+ * wpa_sm_set_config - Notification of current configration change
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @config: Pointer to current network configuration
+ *
+ * Notify WPA state machine that configuration has changed. config will be
+ * stored as a backpointer to network configuration. This can be %NULL to clear
+ * the stored pointed.
+ */
+void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)
+{
+	if (!sm)
+		return;
+
+	if (config) {
+		sm->network_ctx = config->network_ctx;
+		sm->peerkey_enabled = config->peerkey_enabled;
+		sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher;
+		sm->proactive_key_caching = config->proactive_key_caching;
+		sm->eap_workaround = config->eap_workaround;
+		sm->eap_conf_ctx = config->eap_conf_ctx;
+		if (config->ssid) {
+			os_memcpy(sm->ssid, config->ssid, config->ssid_len);
+			sm->ssid_len = config->ssid_len;
+		} 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;
+		sm->allowed_pairwise_cipher = 0;
+		sm->proactive_key_caching = 0;
+		sm->eap_workaround = 0;
+		sm->eap_conf_ctx = NULL;
+		sm->ssid_len = 0;
+		sm->wpa_ptk_rekey = 0;
+		sm->p2p = 0;
+	}
+}
+
+
+/**
+ * wpa_sm_set_own_addr - Set own MAC address
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @addr: Own MAC address
+ */
+void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
+{
+	if (sm)
+		os_memcpy(sm->own_addr, addr, ETH_ALEN);
+}
+
+
+/**
+ * wpa_sm_set_ifname - Set network interface name
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ifname: Interface name
+ * @bridge_ifname: Optional bridge interface name (for pre-auth)
+ */
+void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+		       const char *bridge_ifname)
+{
+	if (sm) {
+		sm->ifname = ifname;
+		sm->bridge_ifname = bridge_ifname;
+	}
+}
+
+
+/**
+ * wpa_sm_set_eapol - Set EAPOL state machine pointer
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @eapol: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ */
+void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol)
+{
+	if (sm)
+		sm->eapol = eapol;
+}
+
+
+/**
+ * wpa_sm_set_param - Set WPA state machine parameters
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @param: Parameter field
+ * @value: Parameter value
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
+		     unsigned int value)
+{
+	int ret = 0;
+
+	if (sm == NULL)
+		return -1;
+
+	switch (param) {
+	case RSNA_PMK_LIFETIME:
+		if (value > 0)
+			sm->dot11RSNAConfigPMKLifetime = value;
+		else
+			ret = -1;
+		break;
+	case RSNA_PMK_REAUTH_THRESHOLD:
+		if (value > 0 && value <= 100)
+			sm->dot11RSNAConfigPMKReauthThreshold = value;
+		else
+			ret = -1;
+		break;
+	case RSNA_SA_TIMEOUT:
+		if (value > 0)
+			sm->dot11RSNAConfigSATimeout = value;
+		else
+			ret = -1;
+		break;
+	case WPA_PARAM_PROTO:
+		sm->proto = value;
+		break;
+	case WPA_PARAM_PAIRWISE:
+		sm->pairwise_cipher = value;
+		break;
+	case WPA_PARAM_GROUP:
+		sm->group_cipher = value;
+		break;
+	case WPA_PARAM_KEY_MGMT:
+		sm->key_mgmt = value;
+		break;
+#ifdef CONFIG_IEEE80211W
+	case WPA_PARAM_MGMT_GROUP:
+		sm->mgmt_group_cipher = value;
+		break;
+#endif /* CONFIG_IEEE80211W */
+	case WPA_PARAM_RSN_ENABLED:
+		sm->rsn_enabled = value;
+		break;
+	case WPA_PARAM_MFP:
+		sm->mfp = value;
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+
+/**
+ * wpa_sm_get_status - Get WPA state machine
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query WPA state machine for status information. This function fills in
+ * a text area with current status information. If the buffer (buf) is not
+ * large enough, status information will be truncated to fit the buffer.
+ */
+int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+		      int verbose)
+{
+	char *pos = buf, *end = buf + buflen;
+	int ret;
+
+	ret = os_snprintf(pos, end - pos,
+			  "pairwise_cipher=%s\n"
+			  "group_cipher=%s\n"
+			  "key_mgmt=%s\n",
+			  wpa_cipher_txt(sm->pairwise_cipher),
+			  wpa_cipher_txt(sm->group_cipher),
+			  wpa_key_mgmt_txt(sm->key_mgmt, sm->proto));
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) {
+		struct wpa_ie_data rsn;
+		if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn)
+		    >= 0 &&
+		    rsn.capabilities & (WPA_CAPABILITY_MFPR |
+					WPA_CAPABILITY_MFPC)) {
+			ret = os_snprintf(pos, end - pos, "pmf=%d\n",
+					  (rsn.capabilities &
+					   WPA_CAPABILITY_MFPR) ? 2 : 1);
+			if (os_snprintf_error(end - pos, ret))
+				return pos - buf;
+			pos += ret;
+		}
+	}
+
+	return pos - buf;
+}
+
+
+int wpa_sm_pmf_enabled(struct wpa_sm *sm)
+{
+	struct wpa_ie_data rsn;
+
+	if (sm->mfp == NO_MGMT_FRAME_PROTECTION || !sm->ap_rsn_ie)
+		return 0;
+
+	if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 &&
+	    rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC))
+		return 1;
+
+	return 0;
+}
+
+
+/**
+ * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @wpa_ie: Pointer to buffer for WPA/RSN IE
+ * @wpa_ie_len: Pointer to the length of the wpa_ie buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
+				    size_t *wpa_ie_len)
+{
+	int res;
+
+	if (sm == NULL)
+		return -1;
+
+	res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len);
+	if (res < 0)
+		return -1;
+	*wpa_ie_len = res;
+
+	wpa_hexdump(MSG_DEBUG, "WPA: Set own WPA IE default",
+		    wpa_ie, *wpa_ie_len);
+
+	if (sm->assoc_wpa_ie == NULL) {
+		/*
+		 * Make a copy of the WPA/RSN IE so that 4-Way Handshake gets
+		 * the correct version of the IE even if PMKSA caching is
+		 * aborted (which would remove PMKID from IE generation).
+		 */
+		sm->assoc_wpa_ie = os_malloc(*wpa_ie_len);
+		if (sm->assoc_wpa_ie == NULL)
+			return -1;
+
+		os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len);
+		sm->assoc_wpa_ie_len = *wpa_ie_len;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_sm_set_assoc_wpa_ie - Set own WPA/RSN IE from (Re)AssocReq
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the WPA/RSN IE used in (Re)Association
+ * Request frame. The IE will be used to override the default value generated
+ * with wpa_sm_set_assoc_wpa_ie_default().
+ */
+int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
+{
+	if (sm == NULL)
+		return -1;
+
+	os_free(sm->assoc_wpa_ie);
+	if (ie == NULL || len == 0) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: clearing own WPA/RSN IE");
+		sm->assoc_wpa_ie = NULL;
+		sm->assoc_wpa_ie_len = 0;
+	} else {
+		wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len);
+		sm->assoc_wpa_ie = os_malloc(len);
+		if (sm->assoc_wpa_ie == NULL)
+			return -1;
+
+		os_memcpy(sm->assoc_wpa_ie, ie, len);
+		sm->assoc_wpa_ie_len = len;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the WPA IE used in Beacon / Probe Response
+ * frame.
+ */
+int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
+{
+	if (sm == NULL)
+		return -1;
+
+	os_free(sm->ap_wpa_ie);
+	if (ie == NULL || len == 0) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: clearing AP WPA IE");
+		sm->ap_wpa_ie = NULL;
+		sm->ap_wpa_ie_len = 0;
+	} else {
+		wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len);
+		sm->ap_wpa_ie = os_malloc(len);
+		if (sm->ap_wpa_ie == NULL)
+			return -1;
+
+		os_memcpy(sm->ap_wpa_ie, ie, len);
+		sm->ap_wpa_ie_len = len;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_sm_set_ap_rsn_ie - Set AP RSN IE from Beacon/ProbeResp
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the RSN IE used in Beacon / Probe Response
+ * frame.
+ */
+int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
+{
+	if (sm == NULL)
+		return -1;
+
+	os_free(sm->ap_rsn_ie);
+	if (ie == NULL || len == 0) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: clearing AP RSN IE");
+		sm->ap_rsn_ie = NULL;
+		sm->ap_rsn_ie_len = 0;
+	} else {
+		wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len);
+		sm->ap_rsn_ie = os_malloc(len);
+		if (sm->ap_rsn_ie == NULL)
+			return -1;
+
+		os_memcpy(sm->ap_rsn_ie, ie, len);
+		sm->ap_rsn_ie_len = len;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @data: Pointer to data area for parsing results
+ * Returns: 0 on success, -1 if IE is not known, or -2 on parsing failure
+ *
+ * Parse the contents of the own WPA or RSN IE from (Re)AssocReq and write the
+ * parsed data into data.
+ */
+int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data)
+{
+	if (sm == NULL)
+		return -1;
+
+	if (sm->assoc_wpa_ie == NULL) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: No WPA/RSN IE available from association info");
+		return -1;
+	}
+	if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data))
+		return -2;
+	return 0;
+}
+
+
+int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len)
+{
+	return pmksa_cache_list(sm->pmksa, buf, len);
+}
+
+
+void wpa_sm_drop_sa(struct wpa_sm *sm)
+{
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
+	sm->ptk_set = 0;
+	sm->tptk_set = 0;
+	os_memset(sm->pmk, 0, sizeof(sm->pmk));
+	os_memset(&sm->ptk, 0, sizeof(sm->ptk));
+	os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+	os_memset(&sm->gtk, 0, sizeof(sm->gtk));
+#ifdef CONFIG_IEEE80211W
+	os_memset(&sm->igtk, 0, sizeof(sm->igtk));
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
+	os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
+	os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0));
+	os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+int wpa_sm_has_ptk(struct wpa_sm *sm)
+{
+	if (sm == NULL)
+		return 0;
+	return sm->ptk_set;
+}
+
+
+void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr)
+{
+	os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN);
+}
+
+
+void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx)
+{
+	pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0);
+}
+
+
+#ifdef CONFIG_WNM
+int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
+{
+	u16 keyinfo;
+	u8 keylen;  /* plaintext key len */
+	u8 *key_rsc;
+
+	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;
+		if (gd.gtk_len != buf[4]) {
+			wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d",
+				   gd.gtk_len, buf[4]);
+			return -1;
+		}
+		gd.keyidx = keyinfo & 0x03; /* B0 - B1 */
+		gd.tx = wpa_supplicant_gtk_tx_bit_workaround(
+		         sm, !!(keyinfo & WPA_KEY_INFO_TXRX));
+
+		os_memcpy(gd.gtk, buf + 13, gd.gtk_len);
+
+		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) {
+		const struct wpa_igtk_kde *igtk;
+
+		igtk = (const struct wpa_igtk_kde *) (buf + 2);
+		if (wpa_supplicant_install_igtk(sm, igtk) < 0)
+			return -1;
+#endif /* CONFIG_IEEE80211W */
+	} else {
+		wpa_printf(MSG_DEBUG, "Unknown element id");
+		return -1;
+	}
+
+	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 */
+
+
+void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter)
+{
+	if (rx_replay_counter == NULL)
+		return;
+
+	os_memcpy(sm->rx_replay_counter, rx_replay_counter,
+		  WPA_REPLAY_COUNTER_LEN);
+	sm->rx_replay_counter_set = 1;
+	wpa_printf(MSG_DEBUG, "Updated key replay counter");
+}
+
+
+void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
+			    const u8 *ptk_kck, size_t ptk_kck_len,
+			    const u8 *ptk_kek, size_t ptk_kek_len)
+{
+	if (ptk_kck && ptk_kck_len <= WPA_KCK_MAX_LEN) {
+		os_memcpy(sm->ptk.kck, ptk_kck, ptk_kck_len);
+		sm->ptk.kck_len = ptk_kck_len;
+		wpa_printf(MSG_DEBUG, "Updated PTK KCK");
+	}
+	if (ptk_kek && ptk_kek_len <= WPA_KEK_MAX_LEN) {
+		os_memcpy(sm->ptk.kek, ptk_kek, ptk_kek_len);
+		sm->ptk.kek_len = ptk_kek_len;
+		wpa_printf(MSG_DEBUG, "Updated PTK KEK");
+	}
+	sm->ptk_set = 1;
+}
diff --git a/hostap/src/rsn_supp/wpa.h b/hostap/src/rsn_supp/wpa.h
new file mode 100644
index 0000000..e163b70
--- /dev/null
+++ b/hostap/src/rsn_supp/wpa.h
@@ -0,0 +1,421 @@
+/*
+ * wpa_supplicant - WPA definitions
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_H
+#define WPA_H
+
+#include "common/defs.h"
+#include "common/eapol_common.h"
+#include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
+
+struct wpa_sm;
+struct eapol_sm;
+struct wpa_config_blob;
+struct hostapd_freq_params;
+
+struct wpa_sm_ctx {
+	void *ctx; /* pointer to arbitrary upper level context */
+	void *msg_ctx; /* upper level context for wpa_msg() calls */
+
+	void (*set_state)(void *ctx, enum wpa_states state);
+	enum wpa_states (*get_state)(void *ctx);
+	void (*deauthenticate)(void * ctx, int reason_code); 
+	int (*set_key)(void *ctx, enum wpa_alg alg,
+		       const u8 *addr, int key_idx, int set_tx,
+		       const u8 *seq, size_t seq_len,
+		       const u8 *key, size_t key_len);
+	void * (*get_network_ctx)(void *ctx);
+	int (*get_bssid)(void *ctx, u8 *bssid);
+	int (*ether_send)(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
+			  size_t len);
+	int (*get_beacon_ie)(void *ctx);
+	void (*cancel_auth_timeout)(void *ctx);
+	u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len,
+			    size_t *msg_len, void **data_pos);
+	int (*add_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid);
+	int (*remove_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid);
+	void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+	const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+							  const char *name);
+	int (*mlme_setprotection)(void *ctx, const u8 *addr,
+				  int protection_type, int key_type);
+	int (*update_ft_ies)(void *ctx, const u8 *md, const u8 *ies,
+			     size_t ies_len);
+	int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap,
+			      const u8 *ies, size_t ies_len);
+	int (*mark_authenticated)(void *ctx, const u8 *target_ap);
+#ifdef CONFIG_TDLS
+	int (*tdls_get_capa)(void *ctx, int *tdls_supported,
+			     int *tdls_ext_setup, int *tdls_chan_switch);
+	int (*send_tdls_mgmt)(void *ctx, const u8 *dst,
+			      u8 action_code, u8 dialog_token,
+			      u16 status_code, u32 peer_capab,
+			      int initiator, 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,
+				size_t supp_rates_len,
+				const struct ieee80211_ht_capabilities *ht_capab,
+				const struct ieee80211_vht_capabilities *vht_capab,
+				u8 qosinfo, int wmm, 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);
+	int (*tdls_enable_channel_switch)(
+		void *ctx, const u8 *addr, u8 oper_class,
+		const struct hostapd_freq_params *params);
+	int (*tdls_disable_channel_switch)(void *ctx, const u8 *addr);
+#endif /* CONFIG_TDLS */
+	void (*set_rekey_offload)(void *ctx, const u8 *kek, size_t kek_len,
+				  const u8 *kck, size_t kck_len,
+				  const u8 *replay_ctr);
+	int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len);
+};
+
+
+enum wpa_sm_conf_params {
+	RSNA_PMK_LIFETIME /* dot11RSNAConfigPMKLifetime */,
+	RSNA_PMK_REAUTH_THRESHOLD /* dot11RSNAConfigPMKReauthThreshold */,
+	RSNA_SA_TIMEOUT /* dot11RSNAConfigSATimeout */,
+	WPA_PARAM_PROTO,
+	WPA_PARAM_PAIRWISE,
+	WPA_PARAM_GROUP,
+	WPA_PARAM_KEY_MGMT,
+	WPA_PARAM_MGMT_GROUP,
+	WPA_PARAM_RSN_ENABLED,
+	WPA_PARAM_MFP
+};
+
+struct rsn_supp_config {
+	void *network_ctx;
+	int peerkey_enabled;
+	int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
+	int proactive_key_caching;
+	int eap_workaround;
+	void *eap_conf_ctx;
+	const u8 *ssid;
+	size_t ssid_len;
+	int wpa_ptk_rekey;
+	int p2p;
+};
+
+#ifndef CONFIG_NO_WPA
+
+struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx);
+void wpa_sm_deinit(struct wpa_sm *sm);
+void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid);
+void wpa_sm_notify_disassoc(struct wpa_sm *sm);
+void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+		    const u8 *bssid);
+void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm);
+void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth);
+void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx);
+void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config);
+void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr);
+void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+		       const char *bridge_ifname);
+void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol);
+int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
+				    size_t *wpa_ie_len);
+int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen);
+
+int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
+		     unsigned int value);
+
+int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+		      int verbose);
+int wpa_sm_pmf_enabled(struct wpa_sm *sm);
+
+void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise);
+
+int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+		     struct wpa_ie_data *data);
+
+void wpa_sm_aborted_cached(struct wpa_sm *sm);
+int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
+		    const u8 *buf, size_t len);
+int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data);
+int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len);
+void wpa_sm_drop_sa(struct wpa_sm *sm);
+int wpa_sm_has_ptk(struct wpa_sm *sm);
+
+void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr);
+
+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);
+
+void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter);
+void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
+			    const u8 *ptk_kck, size_t ptk_kck_len,
+			    const u8 *ptk_kek, size_t ptk_kek_len);
+
+#else /* CONFIG_NO_WPA */
+
+static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
+{
+	return (struct wpa_sm *) 1;
+}
+
+static inline void wpa_sm_deinit(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
+{
+}
+
+static inline void wpa_sm_notify_disassoc(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk,
+				  size_t pmk_len)
+{
+}
+
+static inline void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth)
+{
+}
+
+static inline void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx)
+{
+}
+
+static inline void wpa_sm_set_config(struct wpa_sm *sm,
+				     struct rsn_supp_config *config)
+{
+}
+
+static inline void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
+{
+}
+
+static inline void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+				     const char *bridge_ifname)
+{
+}
+
+static inline void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol)
+{
+}
+
+static inline int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie,
+					  size_t len)
+{
+	return -1;
+}
+
+static inline int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm,
+						  u8 *wpa_ie,
+						  size_t *wpa_ie_len)
+{
+	return -1;
+}
+
+static inline int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie,
+				       size_t len)
+{
+	return -1;
+}
+
+static inline int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie,
+				       size_t len)
+{
+	return -1;
+}
+
+static inline int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
+{
+	return 0;
+}
+
+static inline int wpa_sm_set_param(struct wpa_sm *sm,
+				   enum wpa_sm_conf_params param,
+				   unsigned int value)
+{
+	return -1;
+}
+
+static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf,
+				    size_t buflen, int verbose)
+{
+	return 0;
+}
+
+static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm)
+{
+	return 0;
+}
+
+static inline void wpa_sm_key_request(struct wpa_sm *sm, int error,
+				      int pairwise)
+{
+}
+
+static inline int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+				   struct wpa_ie_data *data)
+{
+	return -1;
+}
+
+static inline void wpa_sm_aborted_cached(struct wpa_sm *sm)
+{
+}
+
+static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
+				  const u8 *buf, size_t len)
+{
+	return -1;
+}
+
+static inline int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm,
+					  struct wpa_ie_data *data)
+{
+	return -1;
+}
+
+static inline int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf,
+					  size_t len)
+{
+	return -1;
+}
+
+static inline void wpa_sm_drop_sa(struct wpa_sm *sm)
+{
+}
+
+static inline int wpa_sm_has_ptk(struct wpa_sm *sm)
+{
+	return 0;
+}
+
+static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm,
+					    const u8 *replay_ctr)
+{
+}
+
+static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm,
+					    void *network_ctx)
+{
+}
+
+static inline void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm,
+					    const u8 *rx_replay_counter)
+{
+}
+
+static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
+					  const u8 *ptk_kek)
+{
+}
+
+#endif /* CONFIG_NO_WPA */
+
+#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
+
+int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len);
+int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie);
+int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+			    int ft_action, const u8 *target_ap,
+			    const u8 *ric_ies, size_t ric_ies_len);
+int wpa_ft_is_completed(struct wpa_sm *sm);
+void wpa_reset_ft_completed(struct wpa_sm *sm);
+int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
+				 size_t ies_len, const u8 *src_addr);
+int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
+			 const u8 *mdie);
+
+#else /* CONFIG_IEEE80211R */
+
+static inline int
+wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
+{
+	return 0;
+}
+
+static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm,
+					      const u8 *mdie)
+{
+	return 0;
+}
+
+static inline int
+wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+			int ft_action, const u8 *target_ap)
+{
+	return 0;
+}
+
+static inline int wpa_ft_is_completed(struct wpa_sm *sm)
+{
+	return 0;
+}
+
+static inline void wpa_reset_ft_completed(struct wpa_sm *sm)
+{
+}
+
+static inline int
+wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+			     const u8 *src_addr)
+{
+	return -1;
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+/* tdls.c */
+void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len);
+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_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_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_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
+				u8 oper_class,
+				struct hostapd_freq_params *freq_params);
+int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr);
+
+int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
+
+#endif /* WPA_H */
diff --git a/hostap/src/rsn_supp/wpa_ft.c b/hostap/src/rsn_supp/wpa_ft.c
new file mode 100644
index 0000000..205793e
--- /dev/null
+++ b/hostap/src/rsn_supp/wpa_ft.c
@@ -0,0 +1,847 @@
+/*
+ * WPA Supplicant - IEEE 802.11r - Fast BSS Transition
+ * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wpa.h"
+#include "wpa_i.h"
+
+#ifdef CONFIG_IEEE80211R
+
+int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
+		      const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
+{
+	u8 ptk_name[WPA_PMK_NAME_LEN];
+	const u8 *anonce = key->key_nonce;
+
+	if (sm->xxkey_len == 0) {
+		wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
+			   "derivation");
+		return -1;
+	}
+
+	wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid,
+			  sm->ssid_len, sm->mobility_domain,
+			  sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
+			  sm->pmk_r0, sm->pmk_r0_name);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name",
+		    sm->pmk_r0_name, WPA_PMK_NAME_LEN);
+	wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
+			  sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
+		    WPA_PMK_NAME_LEN);
+	return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr,
+				 sm->bssid, sm->pmk_r1_name, ptk, ptk_name,
+				 sm->key_mgmt, sm->pairwise_cipher);
+}
+
+
+/**
+ * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ies: Association Response IEs or %NULL to clear FT parameters
+ * @ies_len: Length of ies buffer in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
+{
+	struct wpa_ft_ies ft;
+
+	if (sm == NULL)
+		return 0;
+
+	if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0)
+		return -1;
+
+	if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1)
+		return -1;
+
+	if (ft.mdie) {
+		wpa_hexdump(MSG_DEBUG, "FT: Mobility domain",
+			    ft.mdie, MOBILITY_DOMAIN_ID_LEN);
+		os_memcpy(sm->mobility_domain, ft.mdie,
+			  MOBILITY_DOMAIN_ID_LEN);
+		sm->mdie_ft_capab = ft.mdie[MOBILITY_DOMAIN_ID_LEN];
+		wpa_printf(MSG_DEBUG, "FT: Capability and Policy: 0x%02x",
+			   sm->mdie_ft_capab);
+	} else
+		os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN);
+
+	if (ft.r0kh_id) {
+		wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID",
+			    ft.r0kh_id, ft.r0kh_id_len);
+		os_memcpy(sm->r0kh_id, ft.r0kh_id, ft.r0kh_id_len);
+		sm->r0kh_id_len = ft.r0kh_id_len;
+	} else {
+		/* FIX: When should R0KH-ID be cleared? We need to keep the
+		 * old R0KH-ID in order to be able to use this during FT. */
+		/*
+		 * os_memset(sm->r0kh_id, 0, FT_R0KH_ID_LEN);
+		 * sm->r0kh_id_len = 0;
+		 */
+	}
+
+	if (ft.r1kh_id) {
+		wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID",
+			    ft.r1kh_id, FT_R1KH_ID_LEN);
+		os_memcpy(sm->r1kh_id, ft.r1kh_id, FT_R1KH_ID_LEN);
+	} else
+		os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN);
+
+	os_free(sm->assoc_resp_ies);
+	sm->assoc_resp_ies = os_malloc(ft.mdie_len + 2 + ft.ftie_len + 2);
+	if (sm->assoc_resp_ies) {
+		u8 *pos = sm->assoc_resp_ies;
+		if (ft.mdie) {
+			os_memcpy(pos, ft.mdie - 2, ft.mdie_len + 2);
+			pos += ft.mdie_len + 2;
+		}
+		if (ft.ftie) {
+			os_memcpy(pos, ft.ftie - 2, ft.ftie_len + 2);
+			pos += ft.ftie_len + 2;
+		}
+		sm->assoc_resp_ies_len = pos - sm->assoc_resp_ies;
+		wpa_hexdump(MSG_DEBUG, "FT: Stored MDIE and FTIE from "
+			    "(Re)Association Response",
+			    sm->assoc_resp_ies, sm->assoc_resp_ies_len);
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc Request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @len: Buffer for returning the length of the IEs
+ * @anonce: ANonce or %NULL if not yet available
+ * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List
+ * @kck: 128-bit KCK for MIC or %NULL if no MIC is used
+ * @kck_len: KCK length in octets
+ * @target_ap: Target AP address
+ * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL
+ * @ric_ies_len: Length of ric_ies buffer in octets
+ * @ap_mdie: Mobility Domain IE from the target AP
+ * Returns: Pointer to buffer with IEs or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free();
+ */
+static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
+			       const u8 *anonce, const u8 *pmk_name,
+			       const u8 *kck, size_t kck_len,
+			       const u8 *target_ap,
+			       const u8 *ric_ies, size_t ric_ies_len,
+			       const u8 *ap_mdie)
+{
+	size_t buf_len;
+	u8 *buf, *pos, *ftie_len, *ftie_pos;
+	struct rsn_mdie *mdie;
+	struct rsn_ftie *ftie;
+	struct rsn_ie_hdr *rsnie;
+	u16 capab;
+
+	sm->ft_completed = 0;
+
+	buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
+		2 + sm->r0kh_id_len + ric_ies_len + 100;
+	buf = os_zalloc(buf_len);
+	if (buf == NULL)
+		return NULL;
+	pos = buf;
+
+	/* RSNIE[PMKR0Name/PMKR1Name] */
+	rsnie = (struct rsn_ie_hdr *) pos;
+	rsnie->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(rsnie->version, RSN_VERSION);
+	pos = (u8 *) (rsnie + 1);
+
+	/* Group Suite Selector */
+	if (!wpa_cipher_valid_group(sm->group_cipher)) {
+		wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
+			   sm->group_cipher);
+		os_free(buf);
+		return NULL;
+	}
+	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+						  sm->group_cipher));
+	pos += RSN_SELECTOR_LEN;
+
+	/* Pairwise Suite Count */
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+
+	/* Pairwise Suite List */
+	if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
+		wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
+			   sm->pairwise_cipher);
+		os_free(buf);
+		return NULL;
+	}
+	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+						  sm->pairwise_cipher));
+	pos += RSN_SELECTOR_LEN;
+
+	/* Authenticated Key Management Suite Count */
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+
+	/* Authenticated Key Management Suite List */
+	if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X)
+		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);
+		os_free(buf);
+		return NULL;
+	}
+	pos += RSN_SELECTOR_LEN;
+
+	/* RSN Capabilities */
+	capab = 0;
+#ifdef CONFIG_IEEE80211W
+	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
+		capab |= WPA_CAPABILITY_MFPC;
+#endif /* CONFIG_IEEE80211W */
+	WPA_PUT_LE16(pos, capab);
+	pos += 2;
+
+	/* PMKID Count */
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+
+	/* PMKID List [PMKR0Name/PMKR1Name] */
+	os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN);
+	pos += WPA_PMK_NAME_LEN;
+
+#ifdef CONFIG_IEEE80211W
+	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+		/* Management Group Cipher Suite */
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+		pos += RSN_SELECTOR_LEN;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	rsnie->len = (pos - (u8 *) rsnie) - 2;
+
+	/* MDIE */
+	*pos++ = WLAN_EID_MOBILITY_DOMAIN;
+	*pos++ = sizeof(*mdie);
+	mdie = (struct rsn_mdie *) pos;
+	pos += sizeof(*mdie);
+	os_memcpy(mdie->mobility_domain, sm->mobility_domain,
+		  MOBILITY_DOMAIN_ID_LEN);
+	mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] :
+		sm->mdie_ft_capab;
+
+	/* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */
+	ftie_pos = pos;
+	*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
+	ftie_len = pos++;
+	ftie = (struct rsn_ftie *) pos;
+	pos += sizeof(*ftie);
+	os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
+	if (anonce)
+		os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
+	if (kck) {
+		/* R1KH-ID sub-element in third FT message */
+		*pos++ = FTIE_SUBELEM_R1KH_ID;
+		*pos++ = FT_R1KH_ID_LEN;
+		os_memcpy(pos, sm->r1kh_id, FT_R1KH_ID_LEN);
+		pos += FT_R1KH_ID_LEN;
+	}
+	/* R0KH-ID sub-element */
+	*pos++ = FTIE_SUBELEM_R0KH_ID;
+	*pos++ = sm->r0kh_id_len;
+	os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len);
+	pos += sm->r0kh_id_len;
+	*ftie_len = pos - ftie_len - 1;
+
+	if (ric_ies) {
+		/* RIC Request */
+		os_memcpy(pos, ric_ies, ric_ies_len);
+		pos += ric_ies_len;
+	}
+
+	if (kck) {
+		/*
+		 * IEEE Std 802.11r-2008, 11A.8.4
+		 * MIC shall be calculated over:
+		 * non-AP STA MAC address
+		 * Target AP MAC address
+		 * Transaction seq number (5 for ReassocReq, 3 otherwise)
+		 * RSN IE
+		 * MDIE
+		 * FTIE (with MIC field set to 0)
+		 * RIC-Request (if present)
+		 */
+		/* Information element count */
+		ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies,
+							       ric_ies_len);
+		if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5,
+			       ((u8 *) mdie) - 2, 2 + sizeof(*mdie),
+			       ftie_pos, 2 + *ftie_len,
+			       (u8 *) rsnie, 2 + rsnie->len, ric_ies,
+			       ric_ies_len, ftie->mic) < 0) {
+			wpa_printf(MSG_INFO, "FT: Failed to calculate MIC");
+			os_free(buf);
+			return NULL;
+		}
+	}
+
+	*len = pos - buf;
+
+	return buf;
+}
+
+
+static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
+{
+	int keylen;
+	enum wpa_alg alg;
+	u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 };
+
+	wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver.");
+
+	if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
+		wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d",
+			   sm->pairwise_cipher);
+		return -1;
+	}
+
+	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
+	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+
+	if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc,
+			   sizeof(null_rsc), (u8 *) sm->ptk.tk, keylen) < 0) {
+		wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_ft_prepare_auth_request - Generate over-the-air auth request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @mdie: Target AP MDIE
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie)
+{
+	u8 *ft_ies;
+	size_t ft_ies_len;
+
+	/* Generate a new SNonce */
+	if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
+		wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
+		return -1;
+	}
+
+	ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
+				    NULL, 0, sm->bssid, NULL, 0, mdie);
+	if (ft_ies) {
+		wpa_sm_update_ft_ies(sm, sm->mobility_domain,
+				     ft_ies, ft_ies_len);
+		os_free(ft_ies);
+	}
+
+	return 0;
+}
+
+
+int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+			    int ft_action, const u8 *target_ap,
+			    const u8 *ric_ies, size_t ric_ies_len)
+{
+	u8 *ft_ies;
+	size_t ft_ies_len;
+	struct wpa_ft_ies parse;
+	struct rsn_mdie *mdie;
+	struct rsn_ftie *ftie;
+	u8 ptk_name[WPA_PMK_NAME_LEN];
+	int ret;
+	const u8 *bssid;
+
+	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
+	wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len);
+
+	if (ft_action) {
+		if (!sm->over_the_ds_in_progress) {
+			wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
+				   "- drop FT Action Response");
+			return -1;
+		}
+
+		if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) {
+			wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
+				   "with this Target AP - drop FT Action "
+				   "Response");
+			return -1;
+		}
+	}
+
+	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;
+	}
+
+	if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
+		return -1;
+	}
+
+	mdie = (struct rsn_mdie *) parse.mdie;
+	if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+	    os_memcmp(mdie->mobility_domain, sm->mobility_domain,
+		      MOBILITY_DOMAIN_ID_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+		return -1;
+	}
+
+	ftie = (struct rsn_ftie *) parse.ftie;
+	if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+		return -1;
+	}
+
+	if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
+		wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+			    ftie->snonce, WPA_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+			    sm->snonce, WPA_NONCE_LEN);
+		return -1;
+	}
+
+	if (parse.r0kh_id == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+		return -1;
+	}
+
+	if (parse.r0kh_id_len != sm->r0kh_id_len ||
+	    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",
+			    parse.r0kh_id, parse.r0kh_id_len);
+		wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+			    sm->r0kh_id, sm->r0kh_id_len);
+		return -1;
+	}
+
+	if (parse.r1kh_id == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+		return -1;
+	}
+
+	if (parse.rsn_pmkid == NULL ||
+	    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;
+	}
+
+	os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN);
+	os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN);
+	wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
+			  sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
+		    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+
+	bssid = target_ap;
+	if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce,
+			      sm->own_addr, bssid, sm->pmk_r1_name, &sm->ptk,
+			      ptk_name, sm->key_mgmt, sm->pairwise_cipher) < 0)
+		return -1;
+
+	ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce,
+				    sm->pmk_r1_name,
+				    sm->ptk.kck, sm->ptk.kck_len, bssid,
+				    ric_ies, ric_ies_len,
+				    parse.mdie ? parse.mdie - 2 : NULL);
+	if (ft_ies) {
+		wpa_sm_update_ft_ies(sm, sm->mobility_domain,
+				     ft_ies, ft_ies_len);
+		os_free(ft_ies);
+	}
+
+	wpa_sm_mark_authenticated(sm, bssid);
+	ret = wpa_ft_install_ptk(sm, bssid);
+	if (ret) {
+		/*
+		 * Some drivers do not support key configuration when we are
+		 * not associated with the target AP. Work around this by
+		 * trying again after the following reassociation gets
+		 * completed.
+		 */
+		wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to "
+			   "association - try again after reassociation");
+		sm->set_ptk_after_assoc = 1;
+	} else
+		sm->set_ptk_after_assoc = 0;
+
+	sm->ft_completed = 1;
+	if (ft_action) {
+		/*
+		 * The caller is expected trigger re-association with the
+		 * Target AP.
+		 */
+		os_memcpy(sm->bssid, target_ap, ETH_ALEN);
+	}
+
+	return 0;
+}
+
+
+int wpa_ft_is_completed(struct wpa_sm *sm)
+{
+	if (sm == NULL)
+		return 0;
+
+	if (!wpa_key_mgmt_ft(sm->key_mgmt))
+		return 0;
+
+	return sm->ft_completed;
+}
+
+
+void wpa_reset_ft_completed(struct wpa_sm *sm)
+{
+	if (sm != NULL)
+		sm->ft_completed = 0;
+}
+
+
+static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
+				      size_t gtk_elem_len)
+{
+	u8 gtk[32];
+	int keyidx;
+	enum wpa_alg alg;
+	size_t gtk_len, keylen, rsc_len;
+
+	if (gtk_elem == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE");
+		return 0;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp",
+			gtk_elem, gtk_elem_len);
+
+	if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 ||
+	    gtk_elem_len - 19 > sizeof(gtk)) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem "
+			   "length %lu", (unsigned long) gtk_elem_len);
+		return -1;
+	}
+	gtk_len = gtk_elem_len - 19;
+	if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, gtk_len / 8, gtk_elem + 11,
+		       gtk)) {
+		wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
+			   "decrypt GTK");
+		return -1;
+	}
+
+	keylen = wpa_cipher_key_len(sm->group_cipher);
+	rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
+	alg = wpa_cipher_to_alg(sm->group_cipher);
+	if (alg == WPA_ALG_NONE) {
+		wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
+			   sm->group_cipher);
+		return -1;
+	}
+
+	if (gtk_len < keylen) {
+		wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE");
+		return -1;
+	}
+
+	/* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */
+
+	keyidx = WPA_GET_LE16(gtk_elem) & 0x03;
+
+	if (gtk_elem[2] != keylen) {
+		wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d "
+			   "negotiated %lu",
+			   gtk_elem[2], (unsigned long) keylen);
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen);
+	if (sm->group_cipher == WPA_CIPHER_TKIP) {
+		/* Swap Tx/Rx keys for Michael MIC */
+		u8 tmp[8];
+		os_memcpy(tmp, gtk + 16, 8);
+		os_memcpy(gtk + 16, gtk + 24, 8);
+		os_memcpy(gtk + 24, tmp, 8);
+	}
+	if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0,
+			   gtk_elem + 3, rsc_len, gtk, keylen) < 0) {
+		wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
+			   "driver.");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
+				       size_t igtk_elem_len)
+{
+	u8 igtk[WPA_IGTK_LEN];
+	u16 keyidx;
+
+	if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC)
+		return 0;
+
+	if (igtk_elem == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No IGTK included in FTIE");
+		return 0;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp",
+			igtk_elem, igtk_elem_len);
+
+	if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem "
+			   "length %lu", (unsigned long) igtk_elem_len);
+		return -1;
+	}
+	if (igtk_elem[8] != WPA_IGTK_LEN) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length "
+			   "%d", igtk_elem[8]);
+		return -1;
+	}
+
+	if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, WPA_IGTK_LEN / 8,
+		       igtk_elem + 9, igtk)) {
+		wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
+			   "decrypt IGTK");
+		return -1;
+	}
+
+	/* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */
+
+	keyidx = WPA_GET_LE16(igtk_elem);
+
+	wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk,
+			WPA_IGTK_LEN);
+	if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0,
+			   igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) {
+		wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the "
+			   "driver.");
+		return -1;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
+				 size_t ies_len, const u8 *src_addr)
+{
+	struct wpa_ft_ies parse;
+	struct rsn_mdie *mdie;
+	struct rsn_ftie *ftie;
+	unsigned int count;
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+
+	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
+
+	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;
+	}
+
+	if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
+		return -1;
+	}
+
+	mdie = (struct rsn_mdie *) parse.mdie;
+	if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+	    os_memcmp(mdie->mobility_domain, sm->mobility_domain,
+		      MOBILITY_DOMAIN_ID_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+		return -1;
+	}
+
+	ftie = (struct rsn_ftie *) parse.ftie;
+	if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+		return -1;
+	}
+
+	if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
+		wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+			    ftie->snonce, WPA_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+			    sm->snonce, WPA_NONCE_LEN);
+		return -1;
+	}
+
+	if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
+		wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
+			    ftie->anonce, WPA_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
+			    sm->anonce, WPA_NONCE_LEN);
+		return -1;
+	}
+
+	if (parse.r0kh_id == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+		return -1;
+	}
+
+	if (parse.r0kh_id_len != sm->r0kh_id_len ||
+	    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",
+			    parse.r0kh_id, parse.r0kh_id_len);
+		wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+			    sm->r0kh_id, sm->r0kh_id_len);
+		return -1;
+	}
+
+	if (parse.r1kh_id == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+		return -1;
+	}
+
+	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_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;
+	}
+
+	count = 3;
+	if (parse.ric)
+		count += ieee802_11_ie_count(parse.ric, parse.ric_len);
+	if (ftie->mic_control[1] != count) {
+		wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
+			   "Control: received %u expected %u",
+			   ftie->mic_control[1], count);
+		return -1;
+	}
+
+	if (wpa_ft_mic(sm->ptk.kck, sm->ptk.kck_len, sm->own_addr, src_addr, 6,
+		       parse.mdie - 2, parse.mdie_len + 2,
+		       parse.ftie - 2, parse.ftie_len + 2,
+		       parse.rsn - 2, parse.rsn_len + 2,
+		       parse.ric, parse.ric_len,
+		       mic) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+		return -1;
+	}
+
+	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);
+		return -1;
+	}
+
+	if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0)
+		return -1;
+
+#ifdef CONFIG_IEEE80211W
+	if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0)
+		return -1;
+#endif /* CONFIG_IEEE80211W */
+
+	if (sm->set_ptk_after_assoc) {
+		wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we "
+			   "are associated");
+		if (wpa_ft_install_ptk(sm, src_addr) < 0)
+			return -1;
+		sm->set_ptk_after_assoc = 0;
+	}
+
+	if (parse.ric) {
+		wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response",
+			    parse.ric, parse.ric_len);
+		/* TODO: parse response and inform driver about results when
+		 * using wpa_supplicant SME */
+	}
+
+	wpa_printf(MSG_DEBUG, "FT: Completed successfully");
+
+	return 0;
+}
+
+
+/**
+ * wpa_ft_start_over_ds - Generate over-the-DS auth request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @target_ap: Target AP Address
+ * @mdie: Mobility Domain IE from the target AP
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
+			 const u8 *mdie)
+{
+	u8 *ft_ies;
+	size_t ft_ies_len;
+
+	wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR,
+		   MAC2STR(target_ap));
+
+	/* Generate a new SNonce */
+	if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
+		wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
+		return -1;
+	}
+
+	ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
+				    NULL, 0, target_ap, NULL, 0, mdie);
+	if (ft_ies) {
+		sm->over_the_ds_in_progress = 1;
+		os_memcpy(sm->target_ap, target_ap, ETH_ALEN);
+		wpa_sm_send_ft_action(sm, 1, target_ap, ft_ies, ft_ies_len);
+		os_free(ft_ies);
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_IEEE80211R */
diff --git a/hostap/src/rsn_supp/wpa_i.h b/hostap/src/rsn_supp/wpa_i.h
new file mode 100644
index 0000000..ee21a8a
--- /dev/null
+++ b/hostap/src/rsn_supp/wpa_i.h
@@ -0,0 +1,376 @@
+/*
+ * Internal WPA/RSN supplicant state machine definitions
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_I_H
+#define WPA_I_H
+
+#include "utils/list.h"
+
+struct wpa_peerkey;
+struct wpa_tdls_peer;
+struct wpa_eapol_key;
+
+/**
+ * struct wpa_sm - Internal WPA state machine data
+ */
+struct wpa_sm {
+	u8 pmk[PMK_LEN];
+	size_t pmk_len;
+	struct wpa_ptk ptk, tptk;
+	int ptk_set, tptk_set;
+	unsigned int msg_3_of_4_ok:1;
+	unsigned int tk_to_set:1;
+	u8 snonce[WPA_NONCE_LEN];
+	u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
+	int renew_snonce;
+	u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN];
+	int rx_replay_counter_set;
+	u8 request_counter[WPA_REPLAY_COUNTER_LEN];
+	struct wpa_gtk gtk;
+#ifdef CONFIG_IEEE80211W
+	struct wpa_igtk igtk;
+#endif /* CONFIG_IEEE80211W */
+
+	struct eapol_sm *eapol; /* EAPOL state machine from upper level code */
+
+	struct rsn_pmksa_cache *pmksa; /* PMKSA cache */
+	struct rsn_pmksa_cache_entry *cur_pmksa; /* current PMKSA entry */
+	struct dl_list pmksa_candidates;
+
+	struct l2_packet_data *l2_preauth;
+	struct l2_packet_data *l2_preauth_br;
+	struct l2_packet_data *l2_tdls;
+	u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or
+				     * 00:00:00:00:00:00 if no pre-auth is
+				     * in progress */
+	struct eapol_sm *preauth_eapol;
+
+	struct wpa_sm_ctx *ctx;
+
+	void *scard_ctx; /* context for smartcard callbacks */
+	int fast_reauth; /* whether EAP fast re-authentication is enabled */
+
+	void *network_ctx;
+	int peerkey_enabled;
+	int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
+	int proactive_key_caching;
+	int eap_workaround;
+	void *eap_conf_ctx;
+	u8 ssid[32];
+	size_t ssid_len;
+	int wpa_ptk_rekey;
+	int p2p;
+
+	u8 own_addr[ETH_ALEN];
+	const char *ifname;
+	const char *bridge_ifname;
+	u8 bssid[ETH_ALEN];
+
+	unsigned int dot11RSNAConfigPMKLifetime;
+	unsigned int dot11RSNAConfigPMKReauthThreshold;
+	unsigned int dot11RSNAConfigSATimeout;
+
+	unsigned int dot11RSNA4WayHandshakeFailures;
+
+	/* Selected configuration (based on Beacon/ProbeResp WPA IE) */
+	unsigned int proto;
+	unsigned int pairwise_cipher;
+	unsigned int group_cipher;
+	unsigned int key_mgmt;
+	unsigned int mgmt_group_cipher;
+
+	int rsn_enabled; /* Whether RSN is enabled in configuration */
+	int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */
+
+	u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
+	size_t assoc_wpa_ie_len;
+	u8 *ap_wpa_ie, *ap_rsn_ie;
+	size_t ap_wpa_ie_len, ap_rsn_ie_len;
+
+#ifdef CONFIG_PEERKEY
+	struct wpa_peerkey *peerkey;
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_TDLS
+	struct wpa_tdls_peer *tdls;
+	int tdls_prohibited;
+	int tdls_chan_switch_prohibited;
+	int tdls_disabled;
+
+	/* The driver supports TDLS */
+	int tdls_supported;
+
+	/*
+	 * The driver requires explicit discovery/setup/teardown frames sent
+	 * to it via tdls_mgmt.
+	 */
+	int tdls_external_setup;
+
+	/* The driver supports TDLS channel switching */
+	int tdls_chan_switch;
+#endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_IEEE80211R
+	u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
+	size_t xxkey_len;
+	u8 pmk_r0[PMK_LEN];
+	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN];
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+	u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
+	size_t r0kh_id_len;
+	u8 r1kh_id[FT_R1KH_ID_LEN];
+	int ft_completed;
+	int over_the_ds_in_progress;
+	u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */
+	int set_ptk_after_assoc;
+	u8 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */
+	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 */
+};
+
+
+static inline void wpa_sm_set_state(struct wpa_sm *sm, enum wpa_states state)
+{
+	WPA_ASSERT(sm->ctx->set_state);
+	sm->ctx->set_state(sm->ctx->ctx, state);
+}
+
+static inline enum wpa_states wpa_sm_get_state(struct wpa_sm *sm)
+{
+	WPA_ASSERT(sm->ctx->get_state);
+	return sm->ctx->get_state(sm->ctx->ctx);
+}
+
+static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code)
+{
+	WPA_ASSERT(sm->ctx->deauthenticate);
+	sm->ctx->deauthenticate(sm->ctx->ctx, reason_code);
+}
+
+static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg,
+				 const u8 *addr, int key_idx, int set_tx,
+				 const u8 *seq, size_t seq_len,
+				 const u8 *key, size_t key_len)
+{
+	WPA_ASSERT(sm->ctx->set_key);
+	return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx,
+				seq, seq_len, key, key_len);
+}
+
+static inline void * wpa_sm_get_network_ctx(struct wpa_sm *sm)
+{
+	WPA_ASSERT(sm->ctx->get_network_ctx);
+	return sm->ctx->get_network_ctx(sm->ctx->ctx);
+}
+
+static inline int wpa_sm_get_bssid(struct wpa_sm *sm, u8 *bssid)
+{
+	WPA_ASSERT(sm->ctx->get_bssid);
+	return sm->ctx->get_bssid(sm->ctx->ctx, bssid);
+}
+
+static inline int wpa_sm_ether_send(struct wpa_sm *sm, const u8 *dest,
+				    u16 proto, const u8 *buf, size_t len)
+{
+	WPA_ASSERT(sm->ctx->ether_send);
+	return sm->ctx->ether_send(sm->ctx->ctx, dest, proto, buf, len);
+}
+
+static inline int wpa_sm_get_beacon_ie(struct wpa_sm *sm)
+{
+	WPA_ASSERT(sm->ctx->get_beacon_ie);
+	return sm->ctx->get_beacon_ie(sm->ctx->ctx);
+}
+
+static inline void wpa_sm_cancel_auth_timeout(struct wpa_sm *sm)
+{
+	WPA_ASSERT(sm->ctx->cancel_auth_timeout);
+	sm->ctx->cancel_auth_timeout(sm->ctx->ctx);
+}
+
+static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type,
+				      const void *data, u16 data_len,
+				      size_t *msg_len, void **data_pos)
+{
+	WPA_ASSERT(sm->ctx->alloc_eapol);
+	return sm->ctx->alloc_eapol(sm->ctx->ctx, type, data, data_len,
+				    msg_len, data_pos);
+}
+
+static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid,
+				   const u8 *pmkid)
+{
+	WPA_ASSERT(sm->ctx->add_pmkid);
+	return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid);
+}
+
+static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid,
+				      const u8 *pmkid)
+{
+	WPA_ASSERT(sm->ctx->remove_pmkid);
+	return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid);
+}
+
+static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr,
+					    int protect_type, int key_type)
+{
+	WPA_ASSERT(sm->ctx->mlme_setprotection);
+	return sm->ctx->mlme_setprotection(sm->ctx->ctx, addr, protect_type,
+					   key_type);
+}
+
+static inline int wpa_sm_update_ft_ies(struct wpa_sm *sm, const u8 *md,
+				       const u8 *ies, size_t ies_len)
+{
+	if (sm->ctx->update_ft_ies)
+		return sm->ctx->update_ft_ies(sm->ctx->ctx, md, ies, ies_len);
+	return -1;
+}
+
+static inline int wpa_sm_send_ft_action(struct wpa_sm *sm, u8 action,
+					const u8 *target_ap,
+					const u8 *ies, size_t ies_len)
+{
+	if (sm->ctx->send_ft_action)
+		return sm->ctx->send_ft_action(sm->ctx->ctx, action, target_ap,
+					       ies, ies_len);
+	return -1;
+}
+
+static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm,
+					    const u8 *target_ap)
+{
+	if (sm->ctx->mark_authenticated)
+		return sm->ctx->mark_authenticated(sm->ctx->ctx, target_ap);
+	return -1;
+}
+
+static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm)
+{
+	if (!sm->ctx->set_rekey_offload)
+		return;
+	sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek, sm->ptk.kek_len,
+				   sm->ptk.kck, sm->ptk.kck_len,
+				   sm->rx_replay_counter);
+}
+
+#ifdef CONFIG_TDLS
+static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm,
+				       int *tdls_supported,
+				       int *tdls_ext_setup,
+				       int *tdls_chan_switch)
+{
+	if (sm->ctx->tdls_get_capa)
+		return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported,
+					      tdls_ext_setup, tdls_chan_switch);
+	return -1;
+}
+
+static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst,
+					u8 action_code, u8 dialog_token,
+					u16 status_code, u32 peer_capab,
+					int initiator, 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,
+					       peer_capab, initiator, buf,
+					       len);
+	return -1;
+}
+
+static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper,
+				   const u8 *peer)
+{
+	if (sm->ctx->tdls_oper)
+		return sm->ctx->tdls_oper(sm->ctx->ctx, oper, peer);
+	return -1;
+}
+
+static inline int
+wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add,
+			u16 aid, u16 capability, 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, int wmm, 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, wmm,
+						 ext_capab, ext_capab_len,
+						 supp_channels,
+						 supp_channels_len,
+						 supp_oper_classes,
+						 supp_oper_classes_len);
+	return -1;
+}
+
+static inline int
+wpa_sm_tdls_enable_channel_switch(struct wpa_sm *sm, const u8 *addr,
+				  u8 oper_class,
+				  const struct hostapd_freq_params *freq_params)
+{
+	if (sm->ctx->tdls_enable_channel_switch)
+		return sm->ctx->tdls_enable_channel_switch(sm->ctx->ctx, addr,
+							   oper_class,
+							   freq_params);
+	return -1;
+}
+
+static inline int
+wpa_sm_tdls_disable_channel_switch(struct wpa_sm *sm, const u8 *addr)
+{
+	if (sm->ctx->tdls_disable_channel_switch)
+		return sm->ctx->tdls_disable_channel_switch(sm->ctx->ctx, addr);
+	return -1;
+}
+#endif /* CONFIG_TDLS */
+
+static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm,
+					  const u8 *pmk, size_t pmk_len)
+{
+	if (!sm->proactive_key_caching)
+		return 0;
+	if (!sm->ctx->key_mgmt_set_pmk)
+		return -1;
+	return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len);
+}
+
+void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+			int ver, const u8 *dest, u16 proto,
+			u8 *msg, size_t msg_len, u8 *key_mic);
+int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
+			       const struct wpa_eapol_key *key,
+			       int ver, const u8 *nonce,
+			       const u8 *wpa_ie, size_t wpa_ie_len,
+			       struct wpa_ptk *ptk);
+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,
+			       struct wpa_ptk *ptk);
+
+int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
+		      const struct wpa_eapol_key *key, struct wpa_ptk *ptk);
+
+void wpa_tdls_assoc(struct wpa_sm *sm);
+void wpa_tdls_disassoc(struct wpa_sm *sm);
+
+#endif /* WPA_I_H */
diff --git a/hostap/src/rsn_supp/wpa_ie.c b/hostap/src/rsn_supp/wpa_ie.c
new file mode 100644
index 0000000..0c37b35
--- /dev/null
+++ b/hostap/src/rsn_supp/wpa_ie.c
@@ -0,0 +1,605 @@
+/*
+ * wpa_supplicant - WPA/RSN IE and KDE processing
+ * Copyright (c) 2003-2015, 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 "wpa.h"
+#include "pmksa_cache.h"
+#include "common/ieee802_11_defs.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+
+
+/**
+ * wpa_parse_wpa_ie - Parse WPA/RSN IE
+ * @wpa_ie: Pointer to WPA or RSN IE
+ * @wpa_ie_len: Length of the WPA/RSN IE
+ * @data: Pointer to data area for parsing results
+ * Returns: 0 on success, -1 on failure
+ *
+ * Parse the contents of WPA or RSN IE and write the parsed data into data.
+ */
+int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+		     struct wpa_ie_data *data)
+{
+	if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN)
+		return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
+	if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC &&
+	    wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE)
+		return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
+	else
+		return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
+}
+
+
+static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len,
+			      int pairwise_cipher, int group_cipher,
+			      int key_mgmt)
+{
+	u8 *pos;
+	struct wpa_ie_hdr *hdr;
+	u32 suite;
+
+	if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN +
+	    2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN)
+		return -1;
+
+	hdr = (struct wpa_ie_hdr *) wpa_ie;
+	hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
+	RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
+	WPA_PUT_LE16(hdr->version, WPA_VERSION);
+	pos = (u8 *) (hdr + 1);
+
+	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher);
+	if (suite == 0) {
+		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+			   group_cipher);
+		return -1;
+	}
+	RSN_SELECTOR_PUT(pos, suite);
+	pos += WPA_SELECTOR_LEN;
+
+	*pos++ = 1;
+	*pos++ = 0;
+	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, 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 += WPA_SELECTOR_LEN;
+
+	*pos++ = 1;
+	*pos++ = 0;
+	if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
+	} else if (key_mgmt == WPA_KEY_MGMT_PSK) {
+		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+	} else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE);
+	} else if (key_mgmt == WPA_KEY_MGMT_CCKM) {
+		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM);
+	} else {
+		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+			   key_mgmt);
+		return -1;
+	}
+	pos += WPA_SELECTOR_LEN;
+
+	/* WPA Capabilities; use defaults, so no need to include it */
+
+	hdr->len = (pos - wpa_ie) - 2;
+
+	WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
+
+	return pos - wpa_ie;
+}
+
+
+static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
+			      int pairwise_cipher, int group_cipher,
+			      int key_mgmt, int mgmt_group_cipher,
+			      struct wpa_sm *sm)
+{
+	u8 *pos;
+	struct rsn_ie_hdr *hdr;
+	u16 capab;
+	u32 suite;
+
+	if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN +
+	    2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 +
+	    (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) {
+		wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)",
+			   (unsigned long) rsn_ie_len);
+		return -1;
+	}
+
+	hdr = (struct rsn_ie_hdr *) rsn_ie;
+	hdr->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(hdr->version, RSN_VERSION);
+	pos = (u8 *) (hdr + 1);
+
+	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;
+
+	*pos++ = 1;
+	*pos++ = 0;
+	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;
+
+	*pos++ = 1;
+	*pos++ = 0;
+	if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
+	} else if (key_mgmt == WPA_KEY_MGMT_PSK) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+	} else if (key_mgmt == WPA_KEY_MGMT_CCKM) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM);
+#ifdef CONFIG_IEEE80211R
+	} else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+	} else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
+	} else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+	} else if (key_mgmt == WPA_KEY_MGMT_SAE) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
+	} else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+#endif /* CONFIG_SAE */
+	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
+	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
+	} else {
+		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+			   key_mgmt);
+		return -1;
+	}
+	pos += RSN_SELECTOR_LEN;
+
+	/* RSN Capabilities */
+	capab = 0;
+#ifdef CONFIG_IEEE80211W
+	if (sm->mfp)
+		capab |= WPA_CAPABILITY_MFPC;
+	if (sm->mfp == 2)
+		capab |= WPA_CAPABILITY_MFPR;
+#endif /* CONFIG_IEEE80211W */
+	WPA_PUT_LE16(pos, capab);
+	pos += 2;
+
+	if (sm->cur_pmksa) {
+		/* PMKID Count (2 octets, little endian) */
+		*pos++ = 1;
+		*pos++ = 0;
+		/* PMKID */
+		os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN);
+		pos += PMKID_LEN;
+	}
+
+#ifdef CONFIG_IEEE80211W
+	if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) {
+		if (!sm->cur_pmksa) {
+			/* PMKID Count */
+			WPA_PUT_LE16(pos, 0);
+			pos += 2;
+		}
+
+		/* Management Group Cipher Suite */
+		RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+							  mgmt_group_cipher));
+		pos += RSN_SELECTOR_LEN;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	hdr->len = (pos - rsn_ie) - 2;
+
+	WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len);
+
+	return pos - rsn_ie;
+}
+
+
+#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()
+ * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE
+ * @wpa_ie_len: Maximum length of the generated WPA/RSN IE
+ * Returns: Length of the generated WPA/RSN IE or -1 on failure
+ */
+int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
+{
+	if (sm->proto == WPA_PROTO_RSN)
+		return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len,
+					  sm->pairwise_cipher,
+					  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,
+					  sm->group_cipher,
+					  sm->key_mgmt);
+}
+
+
+/**
+ * wpa_parse_vendor_specific - Parse Vendor Specific IEs
+ * @pos: Pointer to the IE header
+ * @end: Pointer to the end of the Key Data buffer
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, 1 if end mark is found, -1 on failure
+ */
+static int wpa_parse_vendor_specific(const u8 *pos, const u8 *end,
+				     struct wpa_eapol_ie_parse *ie)
+{
+	unsigned int oui;
+
+	if (pos[1] < 4) {
+		wpa_printf(MSG_MSGDUMP, "Too short vendor specific IE ignored (len=%u)",
+			   pos[1]);
+		return 1;
+	}
+
+	oui = WPA_GET_BE24(&pos[2]);
+	if (oui == OUI_MICROSOFT && pos[5] == WMM_OUI_TYPE && pos[1] > 4) {
+		if (pos[6] == WMM_OUI_SUBTYPE_INFORMATION_ELEMENT) {
+			ie->wmm = &pos[2];
+			ie->wmm_len = pos[1];
+			wpa_hexdump(MSG_DEBUG, "WPA: WMM IE",
+				    ie->wmm, ie->wmm_len);
+		} else if (pos[6] == WMM_OUI_SUBTYPE_PARAMETER_ELEMENT) {
+			ie->wmm = &pos[2];
+			ie->wmm_len = pos[1];
+			wpa_hexdump(MSG_DEBUG, "WPA: WMM Parameter Element",
+				    ie->wmm, ie->wmm_len);
+		}
+	}
+	return 0;
+}
+
+
+/**
+ * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
+ * @pos: Pointer to the IE header
+ * @end: Pointer to the end of the Key Data buffer
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, 1 if end mark is found, -1 on failure
+ */
+static int wpa_parse_generic(const u8 *pos, const u8 *end,
+			     struct wpa_eapol_ie_parse *ie)
+{
+	if (pos[1] == 0)
+		return 1;
+
+	if (pos[1] >= 6 &&
+	    RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
+	    pos[2 + WPA_SELECTOR_LEN] == 1 &&
+	    pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
+		ie->wpa_ie = pos;
+		ie->wpa_ie_len = pos[1] + 2;
+		wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key",
+			    ie->wpa_ie, ie->wpa_ie_len);
+		return 0;
+	}
+
+	if (pos + 1 + RSN_SELECTOR_LEN < end &&
+	    pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
+		ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key",
+			    pos, pos[1] + 2);
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
+		ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
+		ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
+		wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key",
+				pos, pos[1] + 2);
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
+		ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
+		ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key",
+			    pos, pos[1] + 2);
+		return 0;
+	}
+
+#ifdef CONFIG_PEERKEY
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
+		ie->smk = pos + 2 + RSN_SELECTOR_LEN;
+		ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
+		wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key",
+				pos, pos[1] + 2);
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
+		ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
+		ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key",
+			    pos, pos[1] + 2);
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
+		ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
+		ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key",
+			    pos, pos[1] + 2);
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
+		ie->error = pos + 2 + RSN_SELECTOR_LEN;
+		ie->error_len = pos[1] - RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key",
+			    pos, pos[1] + 2);
+		return 0;
+	}
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211W
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
+		ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
+		ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
+		wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key",
+				pos, pos[1] + 2);
+		return 0;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_P2P
+	if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
+		ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
+			    ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
+		return 0;
+	}
+
+	if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
+		ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG,
+			    "WPA: IP Address Allocation in EAPOL-Key",
+			    ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
+		return 0;
+	}
+#endif /* CONFIG_P2P */
+
+	return 0;
+}
+
+
+/**
+ * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs
+ * @buf: Pointer to the Key Data buffer
+ * @len: Key Data Length
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
+			     struct wpa_eapol_ie_parse *ie)
+{
+	const u8 *pos, *end;
+	int ret = 0;
+
+	os_memset(ie, 0, sizeof(*ie));
+	for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
+		if (pos[0] == 0xdd &&
+		    ((pos == buf + len - 1) || pos[1] == 0)) {
+			/* Ignore padding */
+			break;
+		}
+		if (pos + 2 + pos[1] > end) {
+			wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
+				   "underflow (ie=%d len=%d pos=%d)",
+				   pos[0], pos[1], (int) (pos - buf));
+			wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data",
+					buf, len);
+			ret = -1;
+			break;
+		}
+		if (*pos == WLAN_EID_RSN) {
+			ie->rsn_ie = pos;
+			ie->rsn_ie_len = pos[1] + 2;
+			wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
+				    ie->rsn_ie, ie->rsn_ie_len);
+		} else if (*pos == WLAN_EID_MOBILITY_DOMAIN &&
+			   pos[1] >= sizeof(struct rsn_mdie)) {
+			ie->mdie = pos;
+			ie->mdie_len = pos[1] + 2;
+			wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key",
+				    ie->mdie, ie->mdie_len);
+		} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION &&
+			   pos[1] >= sizeof(struct rsn_ftie)) {
+			ie->ftie = pos;
+			ie->ftie_len = pos[1] + 2;
+			wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key",
+				    ie->ftie, ie->ftie_len);
+		} else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) {
+			if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) {
+				ie->reassoc_deadline = pos;
+				wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline "
+					    "in EAPOL-Key",
+					    ie->reassoc_deadline, pos[1] + 2);
+			} else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) {
+				ie->key_lifetime = pos;
+				wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime "
+					    "in EAPOL-Key",
+					    ie->key_lifetime, pos[1] + 2);
+			} else {
+				wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized "
+					    "EAPOL-Key Key Data IE",
+					    pos, 2 + pos[1]);
+			}
+		} else if (*pos == WLAN_EID_LINK_ID) {
+			if (pos[1] >= 18) {
+				ie->lnkid = pos;
+				ie->lnkid_len = pos[1] + 2;
+			}
+		} else if (*pos == WLAN_EID_EXT_CAPAB) {
+			ie->ext_capab = pos;
+			ie->ext_capab_len = pos[1] + 2;
+		} else if (*pos == WLAN_EID_SUPP_RATES) {
+			ie->supp_rates = pos;
+			ie->supp_rates_len = pos[1] + 2;
+		} else if (*pos == WLAN_EID_EXT_SUPP_RATES) {
+			ie->ext_supp_rates = pos;
+			ie->ext_supp_rates_len = pos[1] + 2;
+		} else if (*pos == WLAN_EID_HT_CAP &&
+			   pos[1] >= sizeof(struct ieee80211_ht_capabilities)) {
+			ie->ht_capabilities = pos + 2;
+		} else if (*pos == WLAN_EID_VHT_AID) {
+			if (pos[1] >= 2)
+				ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff;
+		} else if (*pos == WLAN_EID_VHT_CAP &&
+			   pos[1] >= sizeof(struct ieee80211_vht_capabilities))
+		{
+			ie->vht_capabilities = pos + 2;
+		} else if (*pos == WLAN_EID_QOS && pos[1] >= 1) {
+			ie->qosinfo = pos[2];
+		} else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) {
+			ie->supp_channels = pos + 2;
+			ie->supp_channels_len = pos[1];
+		} else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) {
+			/*
+			 * The value of the Length field of the Supported
+			 * Operating Classes element is between 2 and 253.
+			 * Silently skip invalid elements to avoid interop
+			 * issues when trying to use the value.
+			 */
+			if (pos[1] >= 2 && pos[1] <= 253) {
+				ie->supp_oper_classes = pos + 2;
+				ie->supp_oper_classes_len = pos[1];
+			}
+		} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
+			ret = wpa_parse_generic(pos, end, ie);
+			if (ret < 0)
+				break;
+			if (ret > 0) {
+				ret = 0;
+				break;
+			}
+
+			ret = wpa_parse_vendor_specific(pos, end, ie);
+			if (ret < 0)
+				break;
+			if (ret > 0) {
+				ret = 0;
+				break;
+			}
+		} else {
+			wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key "
+				    "Key Data IE", pos, 2 + pos[1]);
+		}
+	}
+
+	return ret;
+}
diff --git a/hostap/src/rsn_supp/wpa_ie.h b/hostap/src/rsn_supp/wpa_ie.h
new file mode 100644
index 0000000..fe95af0
--- /dev/null
+++ b/hostap/src/rsn_supp/wpa_ie.h
@@ -0,0 +1,72 @@
+/*
+ * wpa_supplicant - WPA/RSN IE and KDE definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_IE_H
+#define WPA_IE_H
+
+struct wpa_sm;
+
+struct wpa_eapol_ie_parse {
+	const u8 *wpa_ie;
+	size_t wpa_ie_len;
+	const u8 *rsn_ie;
+	size_t rsn_ie_len;
+	const u8 *pmkid;
+	const u8 *gtk;
+	size_t gtk_len;
+	const u8 *mac_addr;
+	size_t mac_addr_len;
+#ifdef CONFIG_PEERKEY
+	const u8 *smk;
+	size_t smk_len;
+	const u8 *nonce;
+	size_t nonce_len;
+	const u8 *lifetime;
+	size_t lifetime_len;
+	const u8 *error;
+	size_t error_len;
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_IEEE80211W
+	const u8 *igtk;
+	size_t igtk_len;
+#endif /* CONFIG_IEEE80211W */
+	const u8 *mdie;
+	size_t mdie_len;
+	const u8 *ftie;
+	size_t ftie_len;
+	const u8 *reassoc_deadline;
+	const u8 *key_lifetime;
+	const u8 *lnkid;
+	size_t lnkid_len;
+	const u8 *ext_capab;
+	size_t ext_capab_len;
+	const u8 *supp_rates;
+	size_t supp_rates_len;
+	const u8 *ext_supp_rates;
+	size_t ext_supp_rates_len;
+	const u8 *ht_capabilities;
+	const u8 *vht_capabilities;
+	const u8 *supp_channels;
+	size_t supp_channels_len;
+	const u8 *supp_oper_classes;
+	size_t supp_oper_classes_len;
+	u8 qosinfo;
+	u16 aid;
+	const u8 *wmm;
+	size_t wmm_len;
+#ifdef CONFIG_P2P
+	const u8 *ip_addr_req;
+	const u8 *ip_addr_alloc;
+#endif /* CONFIG_P2P */
+};
+
+int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
+			     struct wpa_eapol_ie_parse *ie);
+int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len);
+
+#endif /* WPA_IE_H */
diff --git a/hostap/src/tls/Makefile b/hostap/src/tls/Makefile
new file mode 100644
index 0000000..27cdfca
--- /dev/null
+++ b/hostap/src/tls/Makefile
@@ -0,0 +1,39 @@
+all: libtls.a
+
+clean:
+	rm -f *~ *.o *.d libtls.a
+
+install:
+	@echo Nothing to be made.
+
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+CFLAGS += -DCONFIG_TLSV11
+CFLAGS += -DCONFIG_TLSV12
+
+LIB_OBJS= \
+	asn1.o \
+	bignum.o \
+	pkcs1.o \
+	pkcs5.o \
+	pkcs8.o \
+	rsa.o \
+	tlsv1_client.o \
+	tlsv1_client_read.o \
+	tlsv1_client_write.o \
+	tlsv1_common.o \
+	tlsv1_cred.o \
+	tlsv1_record.o \
+	tlsv1_server.o \
+	tlsv1_server_read.o \
+	tlsv1_server_write.o \
+	x509v3.o
+
+
+libtls.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/tls/asn1.c b/hostap/src/tls/asn1.c
new file mode 100644
index 0000000..cec1092
--- /dev/null
+++ b/hostap/src/tls/asn1.c
@@ -0,0 +1,233 @@
+/*
+ * ASN.1 DER parsing
+ * 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.
+ */
+
+#include "includes.h"
+
+#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;
+	u8 tmp;
+
+	os_memset(hdr, 0, sizeof(*hdr));
+	pos = buf;
+	end = buf + len;
+
+	hdr->identifier = *pos++;
+	hdr->class = hdr->identifier >> 6;
+	hdr->constructed = !!(hdr->identifier & (1 << 5));
+
+	if ((hdr->identifier & 0x1f) == 0x1f) {
+		hdr->tag = 0;
+		do {
+			if (pos >= end) {
+				wpa_printf(MSG_DEBUG, "ASN.1: Identifier "
+					   "underflow");
+				return -1;
+			}
+			tmp = *pos++;
+			wpa_printf(MSG_MSGDUMP, "ASN.1: Extended tag data: "
+				   "0x%02x", tmp);
+			hdr->tag = (hdr->tag << 7) | (tmp & 0x7f);
+		} while (tmp & 0x80);
+	} else
+		hdr->tag = hdr->identifier & 0x1f;
+
+	tmp = *pos++;
+	if (tmp & 0x80) {
+		if (tmp == 0xff) {
+			wpa_printf(MSG_DEBUG, "ASN.1: Reserved length "
+				   "value 0xff used");
+			return -1;
+		}
+		tmp &= 0x7f; /* number of subsequent octets */
+		hdr->length = 0;
+		if (tmp > 4) {
+			wpa_printf(MSG_DEBUG, "ASN.1: Too long length field");
+			return -1;
+		}
+		while (tmp--) {
+			if (pos >= end) {
+				wpa_printf(MSG_DEBUG, "ASN.1: Length "
+					   "underflow");
+				return -1;
+			}
+			hdr->length = (hdr->length << 8) | *pos++;
+		}
+	} else {
+		/* Short form - length 0..127 in one octet */
+		hdr->length = tmp;
+	}
+
+	if (end < pos || hdr->length > (unsigned int) (end - pos)) {
+		wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow");
+		return -1;
+	}
+
+	hdr->payload = pos;
+	return 0;
+}
+
+
+int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid)
+{
+	const u8 *pos, *end;
+	unsigned long val;
+	u8 tmp;
+
+	os_memset(oid, 0, sizeof(*oid));
+
+	pos = buf;
+	end = buf + len;
+
+	while (pos < end) {
+		val = 0;
+
+		do {
+			if (pos >= end)
+				return -1;
+			tmp = *pos++;
+			val = (val << 7) | (tmp & 0x7f);
+		} while (tmp & 0x80);
+
+		if (oid->len >= ASN1_MAX_OID_LEN) {
+			wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value");
+			return -1;
+		}
+		if (oid->len == 0) {
+			/*
+			 * The first octet encodes the first two object
+			 * identifier components in (X*40) + Y formula.
+			 * X = 0..2.
+			 */
+			oid->oid[0] = val / 40;
+			if (oid->oid[0] > 2)
+				oid->oid[0] = 2;
+			oid->oid[1] = val - oid->oid[0] * 40;
+			oid->len = 2;
+		} else
+			oid->oid[oid->len++] = val;
+	}
+
+	return 0;
+}
+
+
+int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
+		 const u8 **next)
+{
+	struct asn1_hdr hdr;
+
+	if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0)
+		return -1;
+
+	if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) {
+		wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d "
+			   "tag 0x%x", hdr.class, hdr.tag);
+		return -1;
+	}
+
+	*next = hdr.payload + hdr.length;
+
+	return asn1_parse_oid(hdr.payload, hdr.length, oid);
+}
+
+
+void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len)
+{
+	char *pos = buf;
+	size_t i;
+	int ret;
+
+	if (len == 0)
+		return;
+
+	buf[0] = '\0';
+
+	for (i = 0; i < oid->len; i++) {
+		ret = os_snprintf(pos, buf + len - pos,
+				  "%s%lu",
+				  i == 0 ? "" : ".", oid->oid[i]);
+		if (os_snprintf_error(buf + len - pos, ret))
+			break;
+		pos += ret;
+	}
+	buf[len - 1] = '\0';
+}
+
+
+static u8 rotate_bits(u8 octet)
+{
+	int i;
+	u8 res;
+
+	res = 0;
+	for (i = 0; i < 8; i++) {
+		res <<= 1;
+		if (octet & 1)
+			res |= 1;
+		octet >>= 1;
+	}
+
+	return res;
+}
+
+
+unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len)
+{
+	unsigned long val = 0;
+	const u8 *pos = buf;
+
+	/* BER requires that unused bits are zero, so we can ignore the number
+	 * of unused bits */
+	pos++;
+
+	if (len >= 2)
+		val |= rotate_bits(*pos++);
+	if (len >= 3)
+		val |= ((unsigned long) rotate_bits(*pos++)) << 8;
+	if (len >= 4)
+		val |= ((unsigned long) rotate_bits(*pos++)) << 16;
+	if (len >= 5)
+		val |= ((unsigned long) rotate_bits(*pos++)) << 24;
+	if (len >= 6)
+		wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored "
+			   "(BIT STRING length %lu)",
+			   __func__, (unsigned long) len);
+
+	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/hostap/src/tls/asn1.h b/hostap/src/tls/asn1.h
new file mode 100644
index 0000000..7475007
--- /dev/null
+++ b/hostap/src/tls/asn1.h
@@ -0,0 +1,70 @@
+/*
+ * ASN.1 DER parsing
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ASN1_H
+#define ASN1_H
+
+#define ASN1_TAG_EOC		0x00 /* not used with DER */
+#define ASN1_TAG_BOOLEAN	0x01
+#define ASN1_TAG_INTEGER	0x02
+#define ASN1_TAG_BITSTRING	0x03
+#define ASN1_TAG_OCTETSTRING	0x04
+#define ASN1_TAG_NULL		0x05
+#define ASN1_TAG_OID		0x06
+#define ASN1_TAG_OBJECT_DESCRIPTOR	0x07 /* not yet parsed */
+#define ASN1_TAG_EXTERNAL	0x08 /* not yet parsed */
+#define ASN1_TAG_REAL		0x09 /* not yet parsed */
+#define ASN1_TAG_ENUMERATED	0x0A /* not yet parsed */
+#define ASN1_TAG_UTF8STRING	0x0C /* not yet parsed */
+#define ANS1_TAG_RELATIVE_OID	0x0D
+#define ASN1_TAG_SEQUENCE	0x10 /* shall be constructed */
+#define ASN1_TAG_SET		0x11
+#define ASN1_TAG_NUMERICSTRING	0x12 /* not yet parsed */
+#define ASN1_TAG_PRINTABLESTRING	0x13
+#define ASN1_TAG_TG1STRING	0x14 /* not yet parsed */
+#define ASN1_TAG_VIDEOTEXSTRING	0x15 /* not yet parsed */
+#define ASN1_TAG_IA5STRING	0x16
+#define ASN1_TAG_UTCTIME	0x17
+#define ASN1_TAG_GENERALIZEDTIME	0x18 /* not yet parsed */
+#define ASN1_TAG_GRAPHICSTRING	0x19 /* not yet parsed */
+#define ASN1_TAG_VISIBLESTRING	0x1A
+#define ASN1_TAG_GENERALSTRING	0x1B /* not yet parsed */
+#define ASN1_TAG_UNIVERSALSTRING	0x1C /* not yet parsed */
+#define ASN1_TAG_BMPSTRING	0x1D /* not yet parsed */
+
+#define ASN1_CLASS_UNIVERSAL		0
+#define ASN1_CLASS_APPLICATION		1
+#define ASN1_CLASS_CONTEXT_SPECIFIC	2
+#define ASN1_CLASS_PRIVATE		3
+
+
+struct asn1_hdr {
+	const u8 *payload;
+	u8 identifier, class, constructed;
+	unsigned int tag, length;
+};
+
+#define ASN1_MAX_OID_LEN 20
+struct asn1_oid {
+	unsigned long oid[ASN1_MAX_OID_LEN];
+	size_t len;
+};
+
+
+int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr);
+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(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/hostap/src/tls/bignum.c b/hostap/src/tls/bignum.c
new file mode 100644
index 0000000..f3baafe
--- /dev/null
+++ b/hostap/src/tls/bignum.c
@@ -0,0 +1,224 @@
+/*
+ * Big number math
+ * Copyright (c) 2006, 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 "bignum.h"
+
+#ifdef CONFIG_INTERNAL_LIBTOMMATH
+#include "libtommath.c"
+#else /* CONFIG_INTERNAL_LIBTOMMATH */
+#include <tommath.h>
+#endif /* CONFIG_INTERNAL_LIBTOMMATH */
+
+
+/*
+ * The current version is just a wrapper for LibTomMath library, so
+ * struct bignum is just typecast to mp_int.
+ */
+
+/**
+ * bignum_init - Allocate memory for bignum
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct bignum * bignum_init(void)
+{
+	struct bignum *n = os_zalloc(sizeof(mp_int));
+	if (n == NULL)
+		return NULL;
+	if (mp_init((mp_int *) n) != MP_OKAY) {
+		os_free(n);
+		n = NULL;
+	}
+	return n;
+}
+
+
+/**
+ * bignum_deinit - Free bignum
+ * @n: Bignum from bignum_init()
+ */
+void bignum_deinit(struct bignum *n)
+{
+	if (n) {
+		mp_clear((mp_int *) n);
+		os_free(n);
+	}
+}
+
+
+/**
+ * bignum_get_unsigned_bin - Get length of bignum as an unsigned binary buffer
+ * @n: Bignum from bignum_init()
+ * Returns: Length of n if written to a binary buffer
+ */
+size_t bignum_get_unsigned_bin_len(struct bignum *n)
+{
+	return mp_unsigned_bin_size((mp_int *) n);
+}
+
+
+/**
+ * bignum_get_unsigned_bin - Set binary buffer to unsigned bignum
+ * @n: Bignum from bignum_init()
+ * @buf: Buffer for the binary number
+ * @len: Length of the buffer, can be %NULL if buffer is known to be long
+ * enough. Set to used buffer length on success if not %NULL.
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len)
+{
+	size_t need = mp_unsigned_bin_size((mp_int *) n);
+	if (len && need > *len) {
+		*len = need;
+		return -1;
+	}
+	if (mp_to_unsigned_bin((mp_int *) n, buf) != MP_OKAY) {
+		wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+		return -1;
+	}
+	if (len)
+		*len = need;
+	return 0;
+}
+
+
+/**
+ * bignum_set_unsigned_bin - Set bignum based on unsigned binary buffer
+ * @n: Bignum from bignum_init(); to be set to the given value
+ * @buf: Buffer with unsigned binary value
+ * @len: Length of buf in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len)
+{
+	if (mp_read_unsigned_bin((mp_int *) n, (u8 *) buf, len) != MP_OKAY) {
+		wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * bignum_cmp - Signed comparison
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_cmp(const struct bignum *a, const struct bignum *b)
+{
+	return mp_cmp((mp_int *) a, (mp_int *) b);
+}
+
+
+/**
+ * bignum_cmd_d - Compare bignum to standard integer
+ * @a: Bignum from bignum_init()
+ * @b: Small integer
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_cmp_d(const struct bignum *a, unsigned long b)
+{
+	return mp_cmp_d((mp_int *) a, b);
+}
+
+
+/**
+ * bignum_add - c = a + b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_add(const struct bignum *a, const struct bignum *b,
+	       struct bignum *c)
+{
+	if (mp_add((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+		wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * bignum_sub - c = a - b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a - b
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_sub(const struct bignum *a, const struct bignum *b,
+	       struct bignum *c)
+{
+	if (mp_sub((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+		wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * bignum_mul - c = a * b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a * b
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_mul(const struct bignum *a, const struct bignum *b,
+	       struct bignum *c)
+{
+	if (mp_mul((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+		wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * bignum_mulmod - d = a * b (mod c)
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); modulus
+ * @d: Bignum from bignum_init(); used to store the result of a * b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_mulmod(const struct bignum *a, const struct bignum *b,
+		  const struct bignum *c, struct bignum *d)
+{
+	if (mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
+	    != MP_OKAY) {
+		wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * bignum_exptmod - Modular exponentiation: d = a^b (mod c)
+ * @a: Bignum from bignum_init(); base
+ * @b: Bignum from bignum_init(); exponent
+ * @c: Bignum from bignum_init(); modulus
+ * @d: Bignum from bignum_init(); used to store the result of a^b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_exptmod(const struct bignum *a, const struct bignum *b,
+		   const struct bignum *c, struct bignum *d)
+{
+	if (mp_exptmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
+	    != MP_OKAY) {
+		wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+		return -1;
+	}
+	return 0;
+}
diff --git a/hostap/src/tls/bignum.h b/hostap/src/tls/bignum.h
new file mode 100644
index 0000000..24acdce
--- /dev/null
+++ b/hostap/src/tls/bignum.h
@@ -0,0 +1,32 @@
+/*
+ * Big number math
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BIGNUM_H
+#define BIGNUM_H
+
+struct bignum;
+
+struct bignum * bignum_init(void);
+void bignum_deinit(struct bignum *n);
+size_t bignum_get_unsigned_bin_len(struct bignum *n);
+int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len);
+int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len);
+int bignum_cmp(const struct bignum *a, const struct bignum *b);
+int bignum_cmp_d(const struct bignum *a, unsigned long b);
+int bignum_add(const struct bignum *a, const struct bignum *b,
+	       struct bignum *c);
+int bignum_sub(const struct bignum *a, const struct bignum *b,
+	       struct bignum *c);
+int bignum_mul(const struct bignum *a, const struct bignum *b,
+	       struct bignum *c);
+int bignum_mulmod(const struct bignum *a, const struct bignum *b,
+		  const struct bignum *c, struct bignum *d);
+int bignum_exptmod(const struct bignum *a, const struct bignum *b,
+		   const struct bignum *c, struct bignum *d);
+
+#endif /* BIGNUM_H */
diff --git a/hostap/src/tls/libtommath.c b/hostap/src/tls/libtommath.c
new file mode 100644
index 0000000..8bc824f
--- /dev/null
+++ b/hostap/src/tls/libtommath.c
@@ -0,0 +1,3400 @@
+/*
+ * Minimal code for RSA support from LibTomMath 0.41
+ * http://libtom.org/
+ * http://libtom.org/files/ltm-0.41.tar.bz2
+ * This library was released in public domain by Tom St Denis.
+ *
+ * The combination in this file may not use all of the optimized algorithms
+ * from LibTomMath and may be considerable slower than the LibTomMath with its
+ * default settings. The main purpose of having this version here is to make it
+ * easier to build bignum.c wrapper without having to install and build an
+ * external library.
+ *
+ * If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this
+ * libtommath.c file instead of using the external LibTomMath library.
+ */
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#define BN_MP_INVMOD_C
+#define BN_S_MP_EXPTMOD_C /* Note: #undef in tommath_superclass.h; this would
+			   * require BN_MP_EXPTMOD_FAST_C instead */
+#define BN_S_MP_MUL_DIGS_C
+#define BN_MP_INVMOD_SLOW_C
+#define BN_S_MP_SQR_C
+#define BN_S_MP_MUL_HIGH_DIGS_C /* Note: #undef in tommath_superclass.h; this
+				 * would require other than mp_reduce */
+
+#ifdef LTM_FAST
+
+/* Use faster div at the cost of about 1 kB */
+#define BN_MP_MUL_D_C
+
+/* Include faster exptmod (Montgomery) at the cost of about 2.5 kB in code */
+#define BN_MP_EXPTMOD_FAST_C
+#define BN_MP_MONTGOMERY_SETUP_C
+#define BN_FAST_MP_MONTGOMERY_REDUCE_C
+#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+#define BN_MP_MUL_2_C
+
+/* Include faster sqr at the cost of about 0.5 kB in code */
+#define BN_FAST_S_MP_SQR_C
+
+/* About 0.25 kB of code, but ~1.7kB of stack space! */
+#define BN_FAST_S_MP_MUL_DIGS_C
+
+#else /* LTM_FAST */
+
+#define BN_MP_DIV_SMALL
+#define BN_MP_INIT_MULTI_C
+#define BN_MP_CLEAR_MULTI_C
+#define BN_MP_ABS_C
+#endif /* LTM_FAST */
+
+/* Current uses do not require support for negative exponent in exptmod, so we
+ * can save about 1.5 kB in leaving out invmod. */
+#define LTM_NO_NEG_EXP
+
+/* from tommath.h */
+
+#ifndef MIN
+   #define MIN(x,y) ((x)<(y)?(x):(y))
+#endif
+
+#ifndef MAX
+   #define MAX(x,y) ((x)>(y)?(x):(y))
+#endif
+
+#define  OPT_CAST(x)
+
+#ifdef __x86_64__
+typedef unsigned long mp_digit;
+typedef unsigned long mp_word __attribute__((mode(TI)));
+
+#define DIGIT_BIT 60
+#define MP_64BIT
+#else
+typedef unsigned long mp_digit;
+typedef u64 mp_word;
+
+#define DIGIT_BIT          28
+#define MP_28BIT
+#endif
+
+
+#define XMALLOC  os_malloc
+#define XFREE    os_free
+#define XREALLOC os_realloc
+
+
+#define MP_MASK          ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1))
+
+#define MP_LT        -1   /* less than */
+#define MP_EQ         0   /* equal to */
+#define MP_GT         1   /* greater than */
+
+#define MP_ZPOS       0   /* positive integer */
+#define MP_NEG        1   /* negative */
+
+#define MP_OKAY       0   /* ok result */
+#define MP_MEM        -2  /* out of mem */
+#define MP_VAL        -3  /* invalid input */
+
+#define MP_YES        1   /* yes response */
+#define MP_NO         0   /* no response */
+
+typedef int           mp_err;
+
+/* define this to use lower memory usage routines (exptmods mostly) */
+#define MP_LOW_MEM
+
+/* default precision */
+#ifndef MP_PREC
+   #ifndef MP_LOW_MEM
+      #define MP_PREC                 32     /* default digits of precision */
+   #else
+      #define MP_PREC                 8      /* default digits of precision */
+   #endif   
+#endif
+
+/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */
+#define MP_WARRAY               (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1))
+
+/* the infamous mp_int structure */
+typedef struct  {
+    int used, alloc, sign;
+    mp_digit *dp;
+} mp_int;
+
+
+/* ---> Basic Manipulations <--- */
+#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO)
+#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO)
+#define mp_isodd(a)  (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO)
+
+
+/* prototypes for copied functions */
+#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1)
+static int s_mp_exptmod(mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode);
+static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs);
+static int s_mp_sqr(mp_int * a, mp_int * b);
+static int s_mp_mul_high_digs(mp_int * a, mp_int * b, mp_int * c, int digs);
+
+#ifdef BN_FAST_S_MP_MUL_DIGS_C
+static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs);
+#endif
+
+#ifdef BN_MP_INIT_MULTI_C
+static int mp_init_multi(mp_int *mp, ...);
+#endif
+#ifdef BN_MP_CLEAR_MULTI_C
+static void mp_clear_multi(mp_int *mp, ...);
+#endif
+static int mp_lshd(mp_int * a, int b);
+static void mp_set(mp_int * a, mp_digit b);
+static void mp_clamp(mp_int * a);
+static void mp_exch(mp_int * a, mp_int * b);
+static void mp_rshd(mp_int * a, int b);
+static void mp_zero(mp_int * a);
+static int mp_mod_2d(mp_int * a, int b, mp_int * c);
+static int mp_div_2d(mp_int * a, int b, mp_int * c, mp_int * d);
+static int mp_init_copy(mp_int * a, mp_int * b);
+static int mp_mul_2d(mp_int * a, int b, mp_int * c);
+#ifndef LTM_NO_NEG_EXP
+static int mp_div_2(mp_int * a, mp_int * b);
+static int mp_invmod(mp_int * a, mp_int * b, mp_int * c);
+static int mp_invmod_slow(mp_int * a, mp_int * b, mp_int * c);
+#endif /* LTM_NO_NEG_EXP */
+static int mp_copy(mp_int * a, mp_int * b);
+static int mp_count_bits(mp_int * a);
+static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d);
+static int mp_mod(mp_int * a, mp_int * b, mp_int * c);
+static int mp_grow(mp_int * a, int size);
+static int mp_cmp_mag(mp_int * a, mp_int * b);
+#ifdef BN_MP_ABS_C
+static int mp_abs(mp_int * a, mp_int * b);
+#endif
+static int mp_sqr(mp_int * a, mp_int * b);
+static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d);
+static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d);
+static int mp_2expt(mp_int * a, int b);
+static int mp_reduce_setup(mp_int * a, mp_int * b);
+static int mp_reduce(mp_int * x, mp_int * m, mp_int * mu);
+static int mp_init_size(mp_int * a, int size);
+#ifdef BN_MP_EXPTMOD_FAST_C
+static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode);
+#endif /* BN_MP_EXPTMOD_FAST_C */
+#ifdef BN_FAST_S_MP_SQR_C
+static int fast_s_mp_sqr (mp_int * a, mp_int * b);
+#endif /* BN_FAST_S_MP_SQR_C */
+#ifdef BN_MP_MUL_D_C
+static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c);
+#endif /* BN_MP_MUL_D_C */
+
+
+
+/* functions from bn_<func name>.c */
+
+
+/* reverse an array, used for radix code */
+static void bn_reverse (unsigned char *s, int len)
+{
+  int     ix, iy;
+  unsigned char t;
+
+  ix = 0;
+  iy = len - 1;
+  while (ix < iy) {
+    t     = s[ix];
+    s[ix] = s[iy];
+    s[iy] = t;
+    ++ix;
+    --iy;
+  }
+}
+
+
+/* low level addition, based on HAC pp.594, Algorithm 14.7 */
+static int s_mp_add (mp_int * a, mp_int * b, mp_int * c)
+{
+  mp_int *x;
+  int     olduse, res, min, max;
+
+  /* find sizes, we let |a| <= |b| which means we have to sort
+   * them.  "x" will point to the input with the most digits
+   */
+  if (a->used > b->used) {
+    min = b->used;
+    max = a->used;
+    x = a;
+  } else {
+    min = a->used;
+    max = b->used;
+    x = b;
+  }
+
+  /* init result */
+  if (c->alloc < max + 1) {
+    if ((res = mp_grow (c, max + 1)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* get old used digit count and set new one */
+  olduse = c->used;
+  c->used = max + 1;
+
+  {
+    register mp_digit u, *tmpa, *tmpb, *tmpc;
+    register int i;
+
+    /* alias for digit pointers */
+
+    /* first input */
+    tmpa = a->dp;
+
+    /* second input */
+    tmpb = b->dp;
+
+    /* destination */
+    tmpc = c->dp;
+
+    /* zero the carry */
+    u = 0;
+    for (i = 0; i < min; i++) {
+      /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */
+      *tmpc = *tmpa++ + *tmpb++ + u;
+
+      /* U = carry bit of T[i] */
+      u = *tmpc >> ((mp_digit)DIGIT_BIT);
+
+      /* take away carry bit from T[i] */
+      *tmpc++ &= MP_MASK;
+    }
+
+    /* now copy higher words if any, that is in A+B 
+     * if A or B has more digits add those in 
+     */
+    if (min != max) {
+      for (; i < max; i++) {
+        /* T[i] = X[i] + U */
+        *tmpc = x->dp[i] + u;
+
+        /* U = carry bit of T[i] */
+        u = *tmpc >> ((mp_digit)DIGIT_BIT);
+
+        /* take away carry bit from T[i] */
+        *tmpc++ &= MP_MASK;
+      }
+    }
+
+    /* add carry */
+    *tmpc++ = u;
+
+    /* clear digits above oldused */
+    for (i = c->used; i < olduse; i++) {
+      *tmpc++ = 0;
+    }
+  }
+
+  mp_clamp (c);
+  return MP_OKAY;
+}
+
+
+/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */
+static int s_mp_sub (mp_int * a, mp_int * b, mp_int * c)
+{
+  int     olduse, res, min, max;
+
+  /* find sizes */
+  min = b->used;
+  max = a->used;
+
+  /* init result */
+  if (c->alloc < max) {
+    if ((res = mp_grow (c, max)) != MP_OKAY) {
+      return res;
+    }
+  }
+  olduse = c->used;
+  c->used = max;
+
+  {
+    register mp_digit u, *tmpa, *tmpb, *tmpc;
+    register int i;
+
+    /* alias for digit pointers */
+    tmpa = a->dp;
+    tmpb = b->dp;
+    tmpc = c->dp;
+
+    /* set carry to zero */
+    u = 0;
+    for (i = 0; i < min; i++) {
+      /* T[i] = A[i] - B[i] - U */
+      *tmpc = *tmpa++ - *tmpb++ - u;
+
+      /* U = carry bit of T[i]
+       * Note this saves performing an AND operation since
+       * if a carry does occur it will propagate all the way to the
+       * MSB.  As a result a single shift is enough to get the carry
+       */
+      u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1));
+
+      /* Clear carry from T[i] */
+      *tmpc++ &= MP_MASK;
+    }
+
+    /* now copy higher words if any, e.g. if A has more digits than B  */
+    for (; i < max; i++) {
+      /* T[i] = A[i] - U */
+      *tmpc = *tmpa++ - u;
+
+      /* U = carry bit of T[i] */
+      u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1));
+
+      /* Clear carry from T[i] */
+      *tmpc++ &= MP_MASK;
+    }
+
+    /* clear digits above used (since we may not have grown result above) */
+    for (i = c->used; i < olduse; i++) {
+      *tmpc++ = 0;
+    }
+  }
+
+  mp_clamp (c);
+  return MP_OKAY;
+}
+
+
+/* init a new mp_int */
+static int mp_init (mp_int * a)
+{
+  int i;
+
+  /* allocate memory required and clear it */
+  a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC);
+  if (a->dp == NULL) {
+    return MP_MEM;
+  }
+
+  /* set the digits to zero */
+  for (i = 0; i < MP_PREC; i++) {
+      a->dp[i] = 0;
+  }
+
+  /* set the used to zero, allocated digits to the default precision
+   * and sign to positive */
+  a->used  = 0;
+  a->alloc = MP_PREC;
+  a->sign  = MP_ZPOS;
+
+  return MP_OKAY;
+}
+
+
+/* clear one (frees)  */
+static void mp_clear (mp_int * a)
+{
+  int i;
+
+  /* only do anything if a hasn't been freed previously */
+  if (a->dp != NULL) {
+    /* first zero the digits */
+    for (i = 0; i < a->used; i++) {
+        a->dp[i] = 0;
+    }
+
+    /* free ram */
+    XFREE(a->dp);
+
+    /* reset members to make debugging easier */
+    a->dp    = NULL;
+    a->alloc = a->used = 0;
+    a->sign  = MP_ZPOS;
+  }
+}
+
+
+/* high level addition (handles signs) */
+static int mp_add (mp_int * a, mp_int * b, mp_int * c)
+{
+  int     sa, sb, res;
+
+  /* get sign of both inputs */
+  sa = a->sign;
+  sb = b->sign;
+
+  /* handle two cases, not four */
+  if (sa == sb) {
+    /* both positive or both negative */
+    /* add their magnitudes, copy the sign */
+    c->sign = sa;
+    res = s_mp_add (a, b, c);
+  } else {
+    /* one positive, the other negative */
+    /* subtract the one with the greater magnitude from */
+    /* the one of the lesser magnitude.  The result gets */
+    /* the sign of the one with the greater magnitude. */
+    if (mp_cmp_mag (a, b) == MP_LT) {
+      c->sign = sb;
+      res = s_mp_sub (b, a, c);
+    } else {
+      c->sign = sa;
+      res = s_mp_sub (a, b, c);
+    }
+  }
+  return res;
+}
+
+
+/* high level subtraction (handles signs) */
+static int mp_sub (mp_int * a, mp_int * b, mp_int * c)
+{
+  int     sa, sb, res;
+
+  sa = a->sign;
+  sb = b->sign;
+
+  if (sa != sb) {
+    /* subtract a negative from a positive, OR */
+    /* subtract a positive from a negative. */
+    /* In either case, ADD their magnitudes, */
+    /* and use the sign of the first number. */
+    c->sign = sa;
+    res = s_mp_add (a, b, c);
+  } else {
+    /* subtract a positive from a positive, OR */
+    /* subtract a negative from a negative. */
+    /* First, take the difference between their */
+    /* magnitudes, then... */
+    if (mp_cmp_mag (a, b) != MP_LT) {
+      /* Copy the sign from the first */
+      c->sign = sa;
+      /* The first has a larger or equal magnitude */
+      res = s_mp_sub (a, b, c);
+    } else {
+      /* The result has the *opposite* sign from */
+      /* the first number. */
+      c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS;
+      /* The second has a larger magnitude */
+      res = s_mp_sub (b, a, c);
+    }
+  }
+  return res;
+}
+
+
+/* high level multiplication (handles sign) */
+static int mp_mul (mp_int * a, mp_int * b, mp_int * c)
+{
+  int     res, neg;
+  neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
+
+  /* use Toom-Cook? */
+#ifdef BN_MP_TOOM_MUL_C
+  if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) {
+    res = mp_toom_mul(a, b, c);
+  } else 
+#endif
+#ifdef BN_MP_KARATSUBA_MUL_C
+  /* use Karatsuba? */
+  if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) {
+    res = mp_karatsuba_mul (a, b, c);
+  } else 
+#endif
+  {
+    /* can we use the fast multiplier?
+     *
+     * The fast multiplier can be used if the output will 
+     * have less than MP_WARRAY digits and the number of 
+     * digits won't affect carry propagation
+     */
+#ifdef BN_FAST_S_MP_MUL_DIGS_C
+    int     digs = a->used + b->used + 1;
+
+    if ((digs < MP_WARRAY) &&
+        MIN(a->used, b->used) <= 
+        (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+      res = fast_s_mp_mul_digs (a, b, c, digs);
+    } else 
+#endif
+#ifdef BN_S_MP_MUL_DIGS_C
+      res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */
+#else
+#error mp_mul could fail
+      res = MP_VAL;
+#endif
+
+  }
+  c->sign = (c->used > 0) ? neg : MP_ZPOS;
+  return res;
+}
+
+
+/* d = a * b (mod c) */
+static int mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
+{
+  int     res;
+  mp_int  t;
+
+  if ((res = mp_init (&t)) != MP_OKAY) {
+    return res;
+  }
+
+  if ((res = mp_mul (a, b, &t)) != MP_OKAY) {
+    mp_clear (&t);
+    return res;
+  }
+  res = mp_mod (&t, c, d);
+  mp_clear (&t);
+  return res;
+}
+
+
+/* c = a mod b, 0 <= c < b */
+static int mp_mod (mp_int * a, mp_int * b, mp_int * c)
+{
+  mp_int  t;
+  int     res;
+
+  if ((res = mp_init (&t)) != MP_OKAY) {
+    return res;
+  }
+
+  if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) {
+    mp_clear (&t);
+    return res;
+  }
+
+  if (t.sign != b->sign) {
+    res = mp_add (b, &t, c);
+  } else {
+    res = MP_OKAY;
+    mp_exch (&t, c);
+  }
+
+  mp_clear (&t);
+  return res;
+}
+
+
+/* this is a shell function that calls either the normal or Montgomery
+ * exptmod functions.  Originally the call to the montgomery code was
+ * embedded in the normal function but that wasted a lot of stack space
+ * for nothing (since 99% of the time the Montgomery code would be called)
+ */
+static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
+{
+  int dr;
+
+  /* modulus P must be positive */
+  if (P->sign == MP_NEG) {
+     return MP_VAL;
+  }
+
+  /* if exponent X is negative we have to recurse */
+  if (X->sign == MP_NEG) {
+#ifdef LTM_NO_NEG_EXP
+        return MP_VAL;
+#else /* LTM_NO_NEG_EXP */
+#ifdef BN_MP_INVMOD_C
+     mp_int tmpG, tmpX;
+     int err;
+
+     /* first compute 1/G mod P */
+     if ((err = mp_init(&tmpG)) != MP_OKAY) {
+        return err;
+     }
+     if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) {
+        mp_clear(&tmpG);
+        return err;
+     }
+
+     /* now get |X| */
+     if ((err = mp_init(&tmpX)) != MP_OKAY) {
+        mp_clear(&tmpG);
+        return err;
+     }
+     if ((err = mp_abs(X, &tmpX)) != MP_OKAY) {
+        mp_clear_multi(&tmpG, &tmpX, NULL);
+        return err;
+     }
+
+     /* and now compute (1/G)**|X| instead of G**X [X < 0] */
+     err = mp_exptmod(&tmpG, &tmpX, P, Y);
+     mp_clear_multi(&tmpG, &tmpX, NULL);
+     return err;
+#else 
+#error mp_exptmod would always fail
+     /* no invmod */
+     return MP_VAL;
+#endif
+#endif /* LTM_NO_NEG_EXP */
+  }
+
+/* modified diminished radix reduction */
+#if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C)
+  if (mp_reduce_is_2k_l(P) == MP_YES) {
+     return s_mp_exptmod(G, X, P, Y, 1);
+  }
+#endif
+
+#ifdef BN_MP_DR_IS_MODULUS_C
+  /* is it a DR modulus? */
+  dr = mp_dr_is_modulus(P);
+#else
+  /* default to no */
+  dr = 0;
+#endif
+
+#ifdef BN_MP_REDUCE_IS_2K_C
+  /* if not, is it a unrestricted DR modulus? */
+  if (dr == 0) {
+     dr = mp_reduce_is_2k(P) << 1;
+  }
+#endif
+    
+  /* if the modulus is odd or dr != 0 use the montgomery method */
+#ifdef BN_MP_EXPTMOD_FAST_C
+  if (mp_isodd (P) == 1 || dr !=  0) {
+    return mp_exptmod_fast (G, X, P, Y, dr);
+  } else {
+#endif
+#ifdef BN_S_MP_EXPTMOD_C
+    /* otherwise use the generic Barrett reduction technique */
+    return s_mp_exptmod (G, X, P, Y, 0);
+#else
+#error mp_exptmod could fail
+    /* no exptmod for evens */
+    return MP_VAL;
+#endif
+#ifdef BN_MP_EXPTMOD_FAST_C
+  }
+#endif
+  if (dr == 0) {
+    /* avoid compiler warnings about possibly unused variable */
+  }
+}
+
+
+/* compare two ints (signed)*/
+static int mp_cmp (mp_int * a, mp_int * b)
+{
+  /* compare based on sign */
+  if (a->sign != b->sign) {
+     if (a->sign == MP_NEG) {
+        return MP_LT;
+     } else {
+        return MP_GT;
+     }
+  }
+  
+  /* compare digits */
+  if (a->sign == MP_NEG) {
+     /* if negative compare opposite direction */
+     return mp_cmp_mag(b, a);
+  } else {
+     return mp_cmp_mag(a, b);
+  }
+}
+
+
+/* compare a digit */
+static int mp_cmp_d(mp_int * a, mp_digit b)
+{
+  /* compare based on sign */
+  if (a->sign == MP_NEG) {
+    return MP_LT;
+  }
+
+  /* compare based on magnitude */
+  if (a->used > 1) {
+    return MP_GT;
+  }
+
+  /* compare the only digit of a to b */
+  if (a->dp[0] > b) {
+    return MP_GT;
+  } else if (a->dp[0] < b) {
+    return MP_LT;
+  } else {
+    return MP_EQ;
+  }
+}
+
+
+#ifndef LTM_NO_NEG_EXP
+/* hac 14.61, pp608 */
+static int mp_invmod (mp_int * a, mp_int * b, mp_int * c)
+{
+  /* b cannot be negative */
+  if (b->sign == MP_NEG || mp_iszero(b) == 1) {
+    return MP_VAL;
+  }
+
+#ifdef BN_FAST_MP_INVMOD_C
+  /* if the modulus is odd we can use a faster routine instead */
+  if (mp_isodd (b) == 1) {
+    return fast_mp_invmod (a, b, c);
+  }
+#endif
+
+#ifdef BN_MP_INVMOD_SLOW_C
+  return mp_invmod_slow(a, b, c);
+#endif
+
+#ifndef BN_FAST_MP_INVMOD_C
+#ifndef BN_MP_INVMOD_SLOW_C
+#error mp_invmod would always fail
+#endif
+#endif
+  return MP_VAL;
+}
+#endif /* LTM_NO_NEG_EXP */
+
+
+/* get the size for an unsigned equivalent */
+static int mp_unsigned_bin_size (mp_int * a)
+{
+  int     size = mp_count_bits (a);
+  return (size / 8 + ((size & 7) != 0 ? 1 : 0));
+}
+
+
+#ifndef LTM_NO_NEG_EXP
+/* hac 14.61, pp608 */
+static int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c)
+{
+  mp_int  x, y, u, v, A, B, C, D;
+  int     res;
+
+  /* b cannot be negative */
+  if (b->sign == MP_NEG || mp_iszero(b) == 1) {
+    return MP_VAL;
+  }
+
+  /* init temps */
+  if ((res = mp_init_multi(&x, &y, &u, &v, 
+                           &A, &B, &C, &D, NULL)) != MP_OKAY) {
+     return res;
+  }
+
+  /* x = a, y = b */
+  if ((res = mp_mod(a, b, &x)) != MP_OKAY) {
+      goto LBL_ERR;
+  }
+  if ((res = mp_copy (b, &y)) != MP_OKAY) {
+    goto LBL_ERR;
+  }
+
+  /* 2. [modified] if x,y are both even then return an error! */
+  if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) {
+    res = MP_VAL;
+    goto LBL_ERR;
+  }
+
+  /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */
+  if ((res = mp_copy (&x, &u)) != MP_OKAY) {
+    goto LBL_ERR;
+  }
+  if ((res = mp_copy (&y, &v)) != MP_OKAY) {
+    goto LBL_ERR;
+  }
+  mp_set (&A, 1);
+  mp_set (&D, 1);
+
+top:
+  /* 4.  while u is even do */
+  while (mp_iseven (&u) == 1) {
+    /* 4.1 u = u/2 */
+    if ((res = mp_div_2 (&u, &u)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+    /* 4.2 if A or B is odd then */
+    if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) {
+      /* A = (A+y)/2, B = (B-x)/2 */
+      if ((res = mp_add (&A, &y, &A)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+      if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+    }
+    /* A = A/2, B = B/2 */
+    if ((res = mp_div_2 (&A, &A)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+    if ((res = mp_div_2 (&B, &B)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+  }
+
+  /* 5.  while v is even do */
+  while (mp_iseven (&v) == 1) {
+    /* 5.1 v = v/2 */
+    if ((res = mp_div_2 (&v, &v)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+    /* 5.2 if C or D is odd then */
+    if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) {
+      /* C = (C+y)/2, D = (D-x)/2 */
+      if ((res = mp_add (&C, &y, &C)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+      if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+    }
+    /* C = C/2, D = D/2 */
+    if ((res = mp_div_2 (&C, &C)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+    if ((res = mp_div_2 (&D, &D)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+  }
+
+  /* 6.  if u >= v then */
+  if (mp_cmp (&u, &v) != MP_LT) {
+    /* u = u - v, A = A - C, B = B - D */
+    if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+
+    if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+
+    if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+  } else {
+    /* v - v - u, C = C - A, D = D - B */
+    if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+
+    if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+
+    if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+  }
+
+  /* if not zero goto step 4 */
+  if (mp_iszero (&u) == 0)
+    goto top;
+
+  /* now a = C, b = D, gcd == g*v */
+
+  /* if v != 1 then there is no inverse */
+  if (mp_cmp_d (&v, 1) != MP_EQ) {
+    res = MP_VAL;
+    goto LBL_ERR;
+  }
+
+  /* if its too low */
+  while (mp_cmp_d(&C, 0) == MP_LT) {
+      if ((res = mp_add(&C, b, &C)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+  }
+  
+  /* too big */
+  while (mp_cmp_mag(&C, b) != MP_LT) {
+      if ((res = mp_sub(&C, b, &C)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+  }
+  
+  /* C is now the inverse */
+  mp_exch (&C, c);
+  res = MP_OKAY;
+LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL);
+  return res;
+}
+#endif /* LTM_NO_NEG_EXP */
+
+
+/* compare maginitude of two ints (unsigned) */
+static int mp_cmp_mag (mp_int * a, mp_int * b)
+{
+  int     n;
+  mp_digit *tmpa, *tmpb;
+
+  /* compare based on # of non-zero digits */
+  if (a->used > b->used) {
+    return MP_GT;
+  }
+  
+  if (a->used < b->used) {
+    return MP_LT;
+  }
+
+  /* alias for a */
+  tmpa = a->dp + (a->used - 1);
+
+  /* alias for b */
+  tmpb = b->dp + (a->used - 1);
+
+  /* compare based on digits  */
+  for (n = 0; n < a->used; ++n, --tmpa, --tmpb) {
+    if (*tmpa > *tmpb) {
+      return MP_GT;
+    }
+
+    if (*tmpa < *tmpb) {
+      return MP_LT;
+    }
+  }
+  return MP_EQ;
+}
+
+
+/* reads a unsigned char array, assumes the msb is stored first [big endian] */
+static int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c)
+{
+  int     res;
+
+  /* make sure there are at least two digits */
+  if (a->alloc < 2) {
+     if ((res = mp_grow(a, 2)) != MP_OKAY) {
+        return res;
+     }
+  }
+
+  /* zero the int */
+  mp_zero (a);
+
+  /* read the bytes in */
+  while (c-- > 0) {
+    if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) {
+      return res;
+    }
+
+#ifndef MP_8BIT
+      a->dp[0] |= *b++;
+      a->used += 1;
+#else
+      a->dp[0] = (*b & MP_MASK);
+      a->dp[1] |= ((*b++ >> 7U) & 1);
+      a->used += 2;
+#endif
+  }
+  mp_clamp (a);
+  return MP_OKAY;
+}
+
+
+/* store in unsigned [big endian] format */
+static int mp_to_unsigned_bin (mp_int * a, unsigned char *b)
+{
+  int     x, res;
+  mp_int  t;
+
+  if ((res = mp_init_copy (&t, a)) != MP_OKAY) {
+    return res;
+  }
+
+  x = 0;
+  while (mp_iszero (&t) == 0) {
+#ifndef MP_8BIT
+      b[x++] = (unsigned char) (t.dp[0] & 255);
+#else
+      b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7));
+#endif
+    if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) {
+      mp_clear (&t);
+      return res;
+    }
+  }
+  bn_reverse (b, x);
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+/* shift right by a certain bit count (store quotient in c, optional remainder in d) */
+static int mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d)
+{
+  mp_digit D, r, rr;
+  int     x, res;
+  mp_int  t;
+
+
+  /* if the shift count is <= 0 then we do no work */
+  if (b <= 0) {
+    res = mp_copy (a, c);
+    if (d != NULL) {
+      mp_zero (d);
+    }
+    return res;
+  }
+
+  if ((res = mp_init (&t)) != MP_OKAY) {
+    return res;
+  }
+
+  /* get the remainder */
+  if (d != NULL) {
+    if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) {
+      mp_clear (&t);
+      return res;
+    }
+  }
+
+  /* copy */
+  if ((res = mp_copy (a, c)) != MP_OKAY) {
+    mp_clear (&t);
+    return res;
+  }
+
+  /* shift by as many digits in the bit count */
+  if (b >= (int)DIGIT_BIT) {
+    mp_rshd (c, b / DIGIT_BIT);
+  }
+
+  /* shift any bit count < DIGIT_BIT */
+  D = (mp_digit) (b % DIGIT_BIT);
+  if (D != 0) {
+    register mp_digit *tmpc, mask, shift;
+
+    /* mask */
+    mask = (((mp_digit)1) << D) - 1;
+
+    /* shift for lsb */
+    shift = DIGIT_BIT - D;
+
+    /* alias */
+    tmpc = c->dp + (c->used - 1);
+
+    /* carry */
+    r = 0;
+    for (x = c->used - 1; x >= 0; x--) {
+      /* get the lower  bits of this word in a temp */
+      rr = *tmpc & mask;
+
+      /* shift the current word and mix in the carry bits from the previous word */
+      *tmpc = (*tmpc >> D) | (r << shift);
+      --tmpc;
+
+      /* set the carry to the carry bits of the current word found above */
+      r = rr;
+    }
+  }
+  mp_clamp (c);
+  if (d != NULL) {
+    mp_exch (&t, d);
+  }
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+static int mp_init_copy (mp_int * a, mp_int * b)
+{
+  int     res;
+
+  if ((res = mp_init (a)) != MP_OKAY) {
+    return res;
+  }
+  return mp_copy (b, a);
+}
+
+
+/* set to zero */
+static void mp_zero (mp_int * a)
+{
+  int       n;
+  mp_digit *tmp;
+
+  a->sign = MP_ZPOS;
+  a->used = 0;
+
+  tmp = a->dp;
+  for (n = 0; n < a->alloc; n++) {
+     *tmp++ = 0;
+  }
+}
+
+
+/* copy, b = a */
+static int mp_copy (mp_int * a, mp_int * b)
+{
+  int     res, n;
+
+  /* if dst == src do nothing */
+  if (a == b) {
+    return MP_OKAY;
+  }
+
+  /* grow dest */
+  if (b->alloc < a->used) {
+     if ((res = mp_grow (b, a->used)) != MP_OKAY) {
+        return res;
+     }
+  }
+
+  /* zero b and copy the parameters over */
+  {
+    register mp_digit *tmpa, *tmpb;
+
+    /* pointer aliases */
+
+    /* source */
+    tmpa = a->dp;
+
+    /* destination */
+    tmpb = b->dp;
+
+    /* copy all the digits */
+    for (n = 0; n < a->used; n++) {
+      *tmpb++ = *tmpa++;
+    }
+
+    /* clear high digits */
+    for (; n < b->used; n++) {
+      *tmpb++ = 0;
+    }
+  }
+
+  /* copy used count and sign */
+  b->used = a->used;
+  b->sign = a->sign;
+  return MP_OKAY;
+}
+
+
+/* shift right a certain amount of digits */
+static void mp_rshd (mp_int * a, int b)
+{
+  int     x;
+
+  /* if b <= 0 then ignore it */
+  if (b <= 0) {
+    return;
+  }
+
+  /* if b > used then simply zero it and return */
+  if (a->used <= b) {
+    mp_zero (a);
+    return;
+  }
+
+  {
+    register mp_digit *bottom, *top;
+
+    /* shift the digits down */
+
+    /* bottom */
+    bottom = a->dp;
+
+    /* top [offset into digits] */
+    top = a->dp + b;
+
+    /* this is implemented as a sliding window where 
+     * the window is b-digits long and digits from 
+     * the top of the window are copied to the bottom
+     *
+     * e.g.
+
+     b-2 | b-1 | b0 | b1 | b2 | ... | bb |   ---->
+                 /\                   |      ---->
+                  \-------------------/      ---->
+     */
+    for (x = 0; x < (a->used - b); x++) {
+      *bottom++ = *top++;
+    }
+
+    /* zero the top digits */
+    for (; x < a->used; x++) {
+      *bottom++ = 0;
+    }
+  }
+  
+  /* remove excess digits */
+  a->used -= b;
+}
+
+
+/* swap the elements of two integers, for cases where you can't simply swap the 
+ * mp_int pointers around
+ */
+static void mp_exch (mp_int * a, mp_int * b)
+{
+  mp_int  t;
+
+  t  = *a;
+  *a = *b;
+  *b = t;
+}
+
+
+/* trim unused digits 
+ *
+ * This is used to ensure that leading zero digits are
+ * trimed and the leading "used" digit will be non-zero
+ * Typically very fast.  Also fixes the sign if there
+ * are no more leading digits
+ */
+static void mp_clamp (mp_int * a)
+{
+  /* decrease used while the most significant digit is
+   * zero.
+   */
+  while (a->used > 0 && a->dp[a->used - 1] == 0) {
+    --(a->used);
+  }
+
+  /* reset the sign flag if used == 0 */
+  if (a->used == 0) {
+    a->sign = MP_ZPOS;
+  }
+}
+
+
+/* grow as required */
+static int mp_grow (mp_int * a, int size)
+{
+  int     i;
+  mp_digit *tmp;
+
+  /* if the alloc size is smaller alloc more ram */
+  if (a->alloc < size) {
+    /* ensure there are always at least MP_PREC digits extra on top */
+    size += (MP_PREC * 2) - (size % MP_PREC);
+
+    /* reallocate the array a->dp
+     *
+     * We store the return in a temporary variable
+     * in case the operation failed we don't want
+     * to overwrite the dp member of a.
+     */
+    tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size);
+    if (tmp == NULL) {
+      /* reallocation failed but "a" is still valid [can be freed] */
+      return MP_MEM;
+    }
+
+    /* reallocation succeeded so set a->dp */
+    a->dp = tmp;
+
+    /* zero excess digits */
+    i        = a->alloc;
+    a->alloc = size;
+    for (; i < a->alloc; i++) {
+      a->dp[i] = 0;
+    }
+  }
+  return MP_OKAY;
+}
+
+
+#ifdef BN_MP_ABS_C
+/* b = |a| 
+ *
+ * Simple function copies the input and fixes the sign to positive
+ */
+static int mp_abs (mp_int * a, mp_int * b)
+{
+  int     res;
+
+  /* copy a to b */
+  if (a != b) {
+     if ((res = mp_copy (a, b)) != MP_OKAY) {
+       return res;
+     }
+  }
+
+  /* force the sign of b to positive */
+  b->sign = MP_ZPOS;
+
+  return MP_OKAY;
+}
+#endif
+
+
+/* set to a digit */
+static void mp_set (mp_int * a, mp_digit b)
+{
+  mp_zero (a);
+  a->dp[0] = b & MP_MASK;
+  a->used  = (a->dp[0] != 0) ? 1 : 0;
+}
+
+
+#ifndef LTM_NO_NEG_EXP
+/* b = a/2 */
+static int mp_div_2(mp_int * a, mp_int * b)
+{
+  int     x, res, oldused;
+
+  /* copy */
+  if (b->alloc < a->used) {
+    if ((res = mp_grow (b, a->used)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  oldused = b->used;
+  b->used = a->used;
+  {
+    register mp_digit r, rr, *tmpa, *tmpb;
+
+    /* source alias */
+    tmpa = a->dp + b->used - 1;
+
+    /* dest alias */
+    tmpb = b->dp + b->used - 1;
+
+    /* carry */
+    r = 0;
+    for (x = b->used - 1; x >= 0; x--) {
+      /* get the carry for the next iteration */
+      rr = *tmpa & 1;
+
+      /* shift the current digit, add in carry and store */
+      *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1));
+
+      /* forward carry to next iteration */
+      r = rr;
+    }
+
+    /* zero excess digits */
+    tmpb = b->dp + b->used;
+    for (x = b->used; x < oldused; x++) {
+      *tmpb++ = 0;
+    }
+  }
+  b->sign = a->sign;
+  mp_clamp (b);
+  return MP_OKAY;
+}
+#endif /* LTM_NO_NEG_EXP */
+
+
+/* shift left by a certain bit count */
+static int mp_mul_2d (mp_int * a, int b, mp_int * c)
+{
+  mp_digit d;
+  int      res;
+
+  /* copy */
+  if (a != c) {
+     if ((res = mp_copy (a, c)) != MP_OKAY) {
+       return res;
+     }
+  }
+
+  if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) {
+     if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) {
+       return res;
+     }
+  }
+
+  /* shift by as many digits in the bit count */
+  if (b >= (int)DIGIT_BIT) {
+    if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* shift any bit count < DIGIT_BIT */
+  d = (mp_digit) (b % DIGIT_BIT);
+  if (d != 0) {
+    register mp_digit *tmpc, shift, mask, r, rr;
+    register int x;
+
+    /* bitmask for carries */
+    mask = (((mp_digit)1) << d) - 1;
+
+    /* shift for msbs */
+    shift = DIGIT_BIT - d;
+
+    /* alias */
+    tmpc = c->dp;
+
+    /* carry */
+    r    = 0;
+    for (x = 0; x < c->used; x++) {
+      /* get the higher bits of the current word */
+      rr = (*tmpc >> shift) & mask;
+
+      /* shift the current word and OR in the carry */
+      *tmpc = ((*tmpc << d) | r) & MP_MASK;
+      ++tmpc;
+
+      /* set the carry to the carry bits of the current word */
+      r = rr;
+    }
+    
+    /* set final carry */
+    if (r != 0) {
+       c->dp[(c->used)++] = r;
+    }
+  }
+  mp_clamp (c);
+  return MP_OKAY;
+}
+
+
+#ifdef BN_MP_INIT_MULTI_C
+static int mp_init_multi(mp_int *mp, ...) 
+{
+    mp_err res = MP_OKAY;      /* Assume ok until proven otherwise */
+    int n = 0;                 /* Number of ok inits */
+    mp_int* cur_arg = mp;
+    va_list args;
+
+    va_start(args, mp);        /* init args to next argument from caller */
+    while (cur_arg != NULL) {
+        if (mp_init(cur_arg) != MP_OKAY) {
+            /* Oops - error! Back-track and mp_clear what we already
+               succeeded in init-ing, then return error.
+            */
+            va_list clean_args;
+            
+            /* end the current list */
+            va_end(args);
+            
+            /* now start cleaning up */            
+            cur_arg = mp;
+            va_start(clean_args, mp);
+            while (n--) {
+                mp_clear(cur_arg);
+                cur_arg = va_arg(clean_args, mp_int*);
+            }
+            va_end(clean_args);
+            return MP_MEM;
+        }
+        n++;
+        cur_arg = va_arg(args, mp_int*);
+    }
+    va_end(args);
+    return res;                /* Assumed ok, if error flagged above. */
+}
+#endif
+
+
+#ifdef BN_MP_CLEAR_MULTI_C
+static void mp_clear_multi(mp_int *mp, ...) 
+{
+    mp_int* next_mp = mp;
+    va_list args;
+    va_start(args, mp);
+    while (next_mp != NULL) {
+        mp_clear(next_mp);
+        next_mp = va_arg(args, mp_int*);
+    }
+    va_end(args);
+}
+#endif
+
+
+/* shift left a certain amount of digits */
+static int mp_lshd (mp_int * a, int b)
+{
+  int     x, res;
+
+  /* if its less than zero return */
+  if (b <= 0) {
+    return MP_OKAY;
+  }
+
+  /* grow to fit the new digits */
+  if (a->alloc < a->used + b) {
+     if ((res = mp_grow (a, a->used + b)) != MP_OKAY) {
+       return res;
+     }
+  }
+
+  {
+    register mp_digit *top, *bottom;
+
+    /* increment the used by the shift amount then copy upwards */
+    a->used += b;
+
+    /* top */
+    top = a->dp + a->used - 1;
+
+    /* base */
+    bottom = a->dp + a->used - 1 - b;
+
+    /* much like mp_rshd this is implemented using a sliding window
+     * except the window goes the otherway around.  Copying from
+     * the bottom to the top.  see bn_mp_rshd.c for more info.
+     */
+    for (x = a->used - 1; x >= b; x--) {
+      *top-- = *bottom--;
+    }
+
+    /* zero the lower digits */
+    top = a->dp;
+    for (x = 0; x < b; x++) {
+      *top++ = 0;
+    }
+  }
+  return MP_OKAY;
+}
+
+
+/* returns the number of bits in an int */
+static int mp_count_bits (mp_int * a)
+{
+  int     r;
+  mp_digit q;
+
+  /* shortcut */
+  if (a->used == 0) {
+    return 0;
+  }
+
+  /* get number of digits and add that */
+  r = (a->used - 1) * DIGIT_BIT;
+  
+  /* take the last digit and count the bits in it */
+  q = a->dp[a->used - 1];
+  while (q > ((mp_digit) 0)) {
+    ++r;
+    q >>= ((mp_digit) 1);
+  }
+  return r;
+}
+
+
+/* calc a value mod 2**b */
+static int mp_mod_2d (mp_int * a, int b, mp_int * c)
+{
+  int     x, res;
+
+  /* if b is <= 0 then zero the int */
+  if (b <= 0) {
+    mp_zero (c);
+    return MP_OKAY;
+  }
+
+  /* if the modulus is larger than the value than return */
+  if (b >= (int) (a->used * DIGIT_BIT)) {
+    res = mp_copy (a, c);
+    return res;
+  }
+
+  /* copy */
+  if ((res = mp_copy (a, c)) != MP_OKAY) {
+    return res;
+  }
+
+  /* zero digits above the last digit of the modulus */
+  for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) {
+    c->dp[x] = 0;
+  }
+  /* clear the digit that is not completely outside/inside the modulus */
+  c->dp[b / DIGIT_BIT] &=
+    (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1));
+  mp_clamp (c);
+  return MP_OKAY;
+}
+
+
+#ifdef BN_MP_DIV_SMALL
+
+/* slower bit-bang division... also smaller */
+static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d)
+{
+   mp_int ta, tb, tq, q;
+   int    res, n, n2;
+
+  /* is divisor zero ? */
+  if (mp_iszero (b) == 1) {
+    return MP_VAL;
+  }
+
+  /* if a < b then q=0, r = a */
+  if (mp_cmp_mag (a, b) == MP_LT) {
+    if (d != NULL) {
+      res = mp_copy (a, d);
+    } else {
+      res = MP_OKAY;
+    }
+    if (c != NULL) {
+      mp_zero (c);
+    }
+    return res;
+  }
+	
+  /* init our temps */
+  if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) {
+     return res;
+  }
+
+
+  mp_set(&tq, 1);
+  n = mp_count_bits(a) - mp_count_bits(b);
+  if (((res = mp_abs(a, &ta)) != MP_OKAY) ||
+      ((res = mp_abs(b, &tb)) != MP_OKAY) || 
+      ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) ||
+      ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) {
+      goto LBL_ERR;
+  }
+
+  while (n-- >= 0) {
+     if (mp_cmp(&tb, &ta) != MP_GT) {
+        if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) ||
+            ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) {
+           goto LBL_ERR;
+        }
+     }
+     if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) ||
+         ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) {
+           goto LBL_ERR;
+     }
+  }
+
+  /* now q == quotient and ta == remainder */
+  n  = a->sign;
+  n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG);
+  if (c != NULL) {
+     mp_exch(c, &q);
+     c->sign  = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2;
+  }
+  if (d != NULL) {
+     mp_exch(d, &ta);
+     d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n;
+  }
+LBL_ERR:
+   mp_clear_multi(&ta, &tb, &tq, &q, NULL);
+   return res;
+}
+
+#else
+
+/* integer signed division. 
+ * c*b + d == a [e.g. a/b, c=quotient, d=remainder]
+ * HAC pp.598 Algorithm 14.20
+ *
+ * Note that the description in HAC is horribly 
+ * incomplete.  For example, it doesn't consider 
+ * the case where digits are removed from 'x' in 
+ * the inner loop.  It also doesn't consider the 
+ * case that y has fewer than three digits, etc..
+ *
+ * The overall algorithm is as described as 
+ * 14.20 from HAC but fixed to treat these cases.
+*/
+static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
+{
+  mp_int  q, x, y, t1, t2;
+  int     res, n, t, i, norm, neg;
+
+  /* is divisor zero ? */
+  if (mp_iszero (b) == 1) {
+    return MP_VAL;
+  }
+
+  /* if a < b then q=0, r = a */
+  if (mp_cmp_mag (a, b) == MP_LT) {
+    if (d != NULL) {
+      res = mp_copy (a, d);
+    } else {
+      res = MP_OKAY;
+    }
+    if (c != NULL) {
+      mp_zero (c);
+    }
+    return res;
+  }
+
+  if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) {
+    return res;
+  }
+  q.used = a->used + 2;
+
+  if ((res = mp_init (&t1)) != MP_OKAY) {
+    goto LBL_Q;
+  }
+
+  if ((res = mp_init (&t2)) != MP_OKAY) {
+    goto LBL_T1;
+  }
+
+  if ((res = mp_init_copy (&x, a)) != MP_OKAY) {
+    goto LBL_T2;
+  }
+
+  if ((res = mp_init_copy (&y, b)) != MP_OKAY) {
+    goto LBL_X;
+  }
+
+  /* fix the sign */
+  neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
+  x.sign = y.sign = MP_ZPOS;
+
+  /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */
+  norm = mp_count_bits(&y) % DIGIT_BIT;
+  if (norm < (int)(DIGIT_BIT-1)) {
+     norm = (DIGIT_BIT-1) - norm;
+     if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) {
+       goto LBL_Y;
+     }
+     if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) {
+       goto LBL_Y;
+     }
+  } else {
+     norm = 0;
+  }
+
+  /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */
+  n = x.used - 1;
+  t = y.used - 1;
+
+  /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */
+  if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */
+    goto LBL_Y;
+  }
+
+  while (mp_cmp (&x, &y) != MP_LT) {
+    ++(q.dp[n - t]);
+    if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) {
+      goto LBL_Y;
+    }
+  }
+
+  /* reset y by shifting it back down */
+  mp_rshd (&y, n - t);
+
+  /* step 3. for i from n down to (t + 1) */
+  for (i = n; i >= (t + 1); i--) {
+    if (i > x.used) {
+      continue;
+    }
+
+    /* step 3.1 if xi == yt then set q{i-t-1} to b-1, 
+     * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */
+    if (x.dp[i] == y.dp[t]) {
+      q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1);
+    } else {
+      mp_word tmp;
+      tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT);
+      tmp |= ((mp_word) x.dp[i - 1]);
+      tmp /= ((mp_word) y.dp[t]);
+      if (tmp > (mp_word) MP_MASK)
+        tmp = MP_MASK;
+      q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK));
+    }
+
+    /* while (q{i-t-1} * (yt * b + y{t-1})) > 
+             xi * b**2 + xi-1 * b + xi-2 
+     
+       do q{i-t-1} -= 1; 
+    */
+    q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK;
+    do {
+      q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK;
+
+      /* find left hand */
+      mp_zero (&t1);
+      t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1];
+      t1.dp[1] = y.dp[t];
+      t1.used = 2;
+      if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) {
+        goto LBL_Y;
+      }
+
+      /* find right hand */
+      t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2];
+      t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1];
+      t2.dp[2] = x.dp[i];
+      t2.used = 3;
+    } while (mp_cmp_mag(&t1, &t2) == MP_GT);
+
+    /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */
+    if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) {
+      goto LBL_Y;
+    }
+
+    if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) {
+      goto LBL_Y;
+    }
+
+    if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) {
+      goto LBL_Y;
+    }
+
+    /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */
+    if (x.sign == MP_NEG) {
+      if ((res = mp_copy (&y, &t1)) != MP_OKAY) {
+        goto LBL_Y;
+      }
+      if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) {
+        goto LBL_Y;
+      }
+      if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) {
+        goto LBL_Y;
+      }
+
+      q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK;
+    }
+  }
+
+  /* now q is the quotient and x is the remainder 
+   * [which we have to normalize] 
+   */
+  
+  /* get sign before writing to c */
+  x.sign = x.used == 0 ? MP_ZPOS : a->sign;
+
+  if (c != NULL) {
+    mp_clamp (&q);
+    mp_exch (&q, c);
+    c->sign = neg;
+  }
+
+  if (d != NULL) {
+    mp_div_2d (&x, norm, &x, NULL);
+    mp_exch (&x, d);
+  }
+
+  res = MP_OKAY;
+
+LBL_Y:mp_clear (&y);
+LBL_X:mp_clear (&x);
+LBL_T2:mp_clear (&t2);
+LBL_T1:mp_clear (&t1);
+LBL_Q:mp_clear (&q);
+  return res;
+}
+
+#endif
+
+
+#ifdef MP_LOW_MEM
+   #define TAB_SIZE 32
+#else
+   #define TAB_SIZE 256
+#endif
+
+static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
+{
+  mp_int  M[TAB_SIZE], res, mu;
+  mp_digit buf;
+  int     err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
+  int (*redux)(mp_int*,mp_int*,mp_int*);
+
+  /* find window size */
+  x = mp_count_bits (X);
+  if (x <= 7) {
+    winsize = 2;
+  } else if (x <= 36) {
+    winsize = 3;
+  } else if (x <= 140) {
+    winsize = 4;
+  } else if (x <= 450) {
+    winsize = 5;
+  } else if (x <= 1303) {
+    winsize = 6;
+  } else if (x <= 3529) {
+    winsize = 7;
+  } else {
+    winsize = 8;
+  }
+
+#ifdef MP_LOW_MEM
+    if (winsize > 5) {
+       winsize = 5;
+    }
+#endif
+
+  /* init M array */
+  /* init first cell */
+  if ((err = mp_init(&M[1])) != MP_OKAY) {
+     return err; 
+  }
+
+  /* now init the second half of the array */
+  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+    if ((err = mp_init(&M[x])) != MP_OKAY) {
+      for (y = 1<<(winsize-1); y < x; y++) {
+        mp_clear (&M[y]);
+      }
+      mp_clear(&M[1]);
+      return err;
+    }
+  }
+
+  /* create mu, used for Barrett reduction */
+  if ((err = mp_init (&mu)) != MP_OKAY) {
+    goto LBL_M;
+  }
+  
+  if (redmode == 0) {
+     if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) {
+        goto LBL_MU;
+     }
+     redux = mp_reduce;
+  } else {
+     if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) {
+        goto LBL_MU;
+     }
+     redux = mp_reduce_2k_l;
+  }    
+
+  /* create M table
+   *
+   * The M table contains powers of the base, 
+   * e.g. M[x] = G**x mod P
+   *
+   * The first half of the table is not 
+   * computed though accept for M[0] and M[1]
+   */
+  if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) {
+    goto LBL_MU;
+  }
+
+  /* compute the value at M[1<<(winsize-1)] by squaring 
+   * M[1] (winsize-1) times 
+   */
+  if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
+    goto LBL_MU;
+  }
+
+  for (x = 0; x < (winsize - 1); x++) {
+    /* square it */
+    if ((err = mp_sqr (&M[1 << (winsize - 1)], 
+                       &M[1 << (winsize - 1)])) != MP_OKAY) {
+      goto LBL_MU;
+    }
+
+    /* reduce modulo P */
+    if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) {
+      goto LBL_MU;
+    }
+  }
+
+  /* create upper table, that is M[x] = M[x-1] * M[1] (mod P)
+   * for x = (2**(winsize - 1) + 1) to (2**winsize - 1)
+   */
+  for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
+    if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
+      goto LBL_MU;
+    }
+    if ((err = redux (&M[x], P, &mu)) != MP_OKAY) {
+      goto LBL_MU;
+    }
+  }
+
+  /* setup result */
+  if ((err = mp_init (&res)) != MP_OKAY) {
+    goto LBL_MU;
+  }
+  mp_set (&res, 1);
+
+  /* set initial mode and bit cnt */
+  mode   = 0;
+  bitcnt = 1;
+  buf    = 0;
+  digidx = X->used - 1;
+  bitcpy = 0;
+  bitbuf = 0;
+
+  for (;;) {
+    /* grab next digit as required */
+    if (--bitcnt == 0) {
+      /* if digidx == -1 we are out of digits */
+      if (digidx == -1) {
+        break;
+      }
+      /* read next digit and reset the bitcnt */
+      buf    = X->dp[digidx--];
+      bitcnt = (int) DIGIT_BIT;
+    }
+
+    /* grab the next msb from the exponent */
+    y     = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1;
+    buf <<= (mp_digit)1;
+
+    /* if the bit is zero and mode == 0 then we ignore it
+     * These represent the leading zero bits before the first 1 bit
+     * in the exponent.  Technically this opt is not required but it
+     * does lower the # of trivial squaring/reductions used
+     */
+    if (mode == 0 && y == 0) {
+      continue;
+    }
+
+    /* if the bit is zero and mode == 1 then we square */
+    if (mode == 1 && y == 0) {
+      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      continue;
+    }
+
+    /* else we add it to the window */
+    bitbuf |= (y << (winsize - ++bitcpy));
+    mode    = 2;
+
+    if (bitcpy == winsize) {
+      /* ok window is filled so square as required and multiply  */
+      /* square first */
+      for (x = 0; x < winsize; x++) {
+        if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+        if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+      }
+
+      /* then multiply */
+      if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+
+      /* empty window and reset */
+      bitcpy = 0;
+      bitbuf = 0;
+      mode   = 1;
+    }
+  }
+
+  /* if bits remain then square/multiply */
+  if (mode == 2 && bitcpy > 0) {
+    /* square then multiply if the bit is set */
+    for (x = 0; x < bitcpy; x++) {
+      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+
+      bitbuf <<= 1;
+      if ((bitbuf & (1 << winsize)) != 0) {
+        /* then multiply */
+        if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+        if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+      }
+    }
+  }
+
+  mp_exch (&res, Y);
+  err = MP_OKAY;
+LBL_RES:mp_clear (&res);
+LBL_MU:mp_clear (&mu);
+LBL_M:
+  mp_clear(&M[1]);
+  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+    mp_clear (&M[x]);
+  }
+  return err;
+}
+
+
+/* computes b = a*a */
+static int mp_sqr (mp_int * a, mp_int * b)
+{
+  int     res;
+
+#ifdef BN_MP_TOOM_SQR_C
+  /* use Toom-Cook? */
+  if (a->used >= TOOM_SQR_CUTOFF) {
+    res = mp_toom_sqr(a, b);
+  /* Karatsuba? */
+  } else 
+#endif
+#ifdef BN_MP_KARATSUBA_SQR_C
+if (a->used >= KARATSUBA_SQR_CUTOFF) {
+    res = mp_karatsuba_sqr (a, b);
+  } else 
+#endif
+  {
+#ifdef BN_FAST_S_MP_SQR_C
+    /* can we use the fast comba multiplier? */
+    if ((a->used * 2 + 1) < MP_WARRAY && 
+         a->used < 
+         (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) {
+      res = fast_s_mp_sqr (a, b);
+    } else
+#endif
+#ifdef BN_S_MP_SQR_C
+      res = s_mp_sqr (a, b);
+#else
+#error mp_sqr could fail
+      res = MP_VAL;
+#endif
+  }
+  b->sign = MP_ZPOS;
+  return res;
+}
+
+
+/* reduces a modulo n where n is of the form 2**p - d 
+   This differs from reduce_2k since "d" can be larger
+   than a single digit.
+*/
+static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d)
+{
+   mp_int q;
+   int    p, res;
+   
+   if ((res = mp_init(&q)) != MP_OKAY) {
+      return res;
+   }
+   
+   p = mp_count_bits(n);    
+top:
+   /* q = a/2**p, a = a mod 2**p */
+   if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) {
+      goto ERR;
+   }
+   
+   /* q = q * d */
+   if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { 
+      goto ERR;
+   }
+   
+   /* a = a + q */
+   if ((res = s_mp_add(a, &q, a)) != MP_OKAY) {
+      goto ERR;
+   }
+   
+   if (mp_cmp_mag(a, n) != MP_LT) {
+      s_mp_sub(a, n, a);
+      goto top;
+   }
+   
+ERR:
+   mp_clear(&q);
+   return res;
+}
+
+
+/* determines the setup value */
+static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d)
+{
+   int    res;
+   mp_int tmp;
+   
+   if ((res = mp_init(&tmp)) != MP_OKAY) {
+      return res;
+   }
+   
+   if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) {
+      goto ERR;
+   }
+   
+   if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) {
+      goto ERR;
+   }
+   
+ERR:
+   mp_clear(&tmp);
+   return res;
+}
+
+
+/* computes a = 2**b 
+ *
+ * Simple algorithm which zeroes the int, grows it then just sets one bit
+ * as required.
+ */
+static int mp_2expt (mp_int * a, int b)
+{
+  int     res;
+
+  /* zero a as per default */
+  mp_zero (a);
+
+  /* grow a to accommodate the single bit */
+  if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) {
+    return res;
+  }
+
+  /* set the used count of where the bit will go */
+  a->used = b / DIGIT_BIT + 1;
+
+  /* put the single bit in its place */
+  a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT);
+
+  return MP_OKAY;
+}
+
+
+/* pre-calculate the value required for Barrett reduction
+ * For a given modulus "b" it calulates the value required in "a"
+ */
+static int mp_reduce_setup (mp_int * a, mp_int * b)
+{
+  int     res;
+  
+  if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) {
+    return res;
+  }
+  return mp_div (a, b, a, NULL);
+}
+
+
+/* reduces x mod m, assumes 0 < x < m**2, mu is 
+ * precomputed via mp_reduce_setup.
+ * From HAC pp.604 Algorithm 14.42
+ */
+static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu)
+{
+  mp_int  q;
+  int     res, um = m->used;
+
+  /* q = x */
+  if ((res = mp_init_copy (&q, x)) != MP_OKAY) {
+    return res;
+  }
+
+  /* q1 = x / b**(k-1)  */
+  mp_rshd (&q, um - 1);         
+
+  /* according to HAC this optimization is ok */
+  if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) {
+    if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+  } else {
+#ifdef BN_S_MP_MUL_HIGH_DIGS_C
+    if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+#elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C)
+    if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+#else 
+    { 
+#error mp_reduce would always fail
+      res = MP_VAL;
+      goto CLEANUP;
+    }
+#endif
+  }
+
+  /* q3 = q2 / b**(k+1) */
+  mp_rshd (&q, um + 1);         
+
+  /* x = x mod b**(k+1), quick (no division) */
+  if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) {
+    goto CLEANUP;
+  }
+
+  /* q = q * m mod b**(k+1), quick (no division) */
+  if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) {
+    goto CLEANUP;
+  }
+
+  /* x = x - q */
+  if ((res = mp_sub (x, &q, x)) != MP_OKAY) {
+    goto CLEANUP;
+  }
+
+  /* If x < 0, add b**(k+1) to it */
+  if (mp_cmp_d (x, 0) == MP_LT) {
+    mp_set (&q, 1);
+    if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+    if ((res = mp_add (x, &q, x)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+  }
+
+  /* Back off if it's too big */
+  while (mp_cmp (x, m) != MP_LT) {
+    if ((res = s_mp_sub (x, m, x)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+  }
+  
+CLEANUP:
+  mp_clear (&q);
+
+  return res;
+}
+
+
+/* multiplies |a| * |b| and only computes up to digs digits of result
+ * HAC pp. 595, Algorithm 14.12  Modified so you can control how 
+ * many digits of output are created.
+ */
+static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
+{
+  mp_int  t;
+  int     res, pa, pb, ix, iy;
+  mp_digit u;
+  mp_word r;
+  mp_digit tmpx, *tmpt, *tmpy;
+
+#ifdef BN_FAST_S_MP_MUL_DIGS_C
+  /* can we use the fast multiplier? */
+  if (((digs) < MP_WARRAY) &&
+      MIN (a->used, b->used) < 
+          (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+    return fast_s_mp_mul_digs (a, b, c, digs);
+  }
+#endif
+
+  if ((res = mp_init_size (&t, digs)) != MP_OKAY) {
+    return res;
+  }
+  t.used = digs;
+
+  /* compute the digits of the product directly */
+  pa = a->used;
+  for (ix = 0; ix < pa; ix++) {
+    /* set the carry to zero */
+    u = 0;
+
+    /* limit ourselves to making digs digits of output */
+    pb = MIN (b->used, digs - ix);
+
+    /* setup some aliases */
+    /* copy of the digit from a used within the nested loop */
+    tmpx = a->dp[ix];
+    
+    /* an alias for the destination shifted ix places */
+    tmpt = t.dp + ix;
+    
+    /* an alias for the digits of b */
+    tmpy = b->dp;
+
+    /* compute the columns of the output and propagate the carry */
+    for (iy = 0; iy < pb; iy++) {
+      /* compute the column as a mp_word */
+      r       = ((mp_word)*tmpt) +
+                ((mp_word)tmpx) * ((mp_word)*tmpy++) +
+                ((mp_word) u);
+
+      /* the new column is the lower part of the result */
+      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+      /* get the carry word from the result */
+      u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
+    }
+    /* set carry if it is placed below digs */
+    if (ix + iy < digs) {
+      *tmpt = u;
+    }
+  }
+
+  mp_clamp (&t);
+  mp_exch (&t, c);
+
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+#ifdef BN_FAST_S_MP_MUL_DIGS_C
+/* Fast (comba) multiplier
+ *
+ * This is the fast column-array [comba] multiplier.  It is 
+ * designed to compute the columns of the product first 
+ * then handle the carries afterwards.  This has the effect 
+ * of making the nested loops that compute the columns very
+ * simple and schedulable on super-scalar processors.
+ *
+ * This has been modified to produce a variable number of 
+ * digits of output so if say only a half-product is required 
+ * you don't have to compute the upper half (a feature 
+ * required for fast Barrett reduction).
+ *
+ * Based on Algorithm 14.12 on pp.595 of HAC.
+ *
+ */
+static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
+{
+  int     olduse, res, pa, ix, iz;
+  mp_digit W[MP_WARRAY];
+  register mp_word  _W;
+
+  /* grow the destination as required */
+  if (c->alloc < digs) {
+    if ((res = mp_grow (c, digs)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* number of output digits to produce */
+  pa = MIN(digs, a->used + b->used);
+
+  /* clear the carry */
+  _W = 0;
+  for (ix = 0; ix < pa; ix++) { 
+      int      tx, ty;
+      int      iy;
+      mp_digit *tmpx, *tmpy;
+
+      /* get offsets into the two bignums */
+      ty = MIN(b->used-1, ix);
+      tx = ix - ty;
+
+      /* setup temp aliases */
+      tmpx = a->dp + tx;
+      tmpy = b->dp + ty;
+
+      /* this is the number of times the loop will iterrate, essentially 
+         while (tx++ < a->used && ty-- >= 0) { ... }
+       */
+      iy = MIN(a->used-tx, ty+1);
+
+      /* execute loop */
+      for (iz = 0; iz < iy; ++iz) {
+         _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--);
+
+      }
+
+      /* store term */
+      W[ix] = ((mp_digit)_W) & MP_MASK;
+
+      /* make next carry */
+      _W = _W >> ((mp_word)DIGIT_BIT);
+ }
+
+  /* setup dest */
+  olduse  = c->used;
+  c->used = pa;
+
+  {
+    register mp_digit *tmpc;
+    tmpc = c->dp;
+    for (ix = 0; ix < pa+1; ix++) {
+      /* now extract the previous digit [below the carry] */
+      *tmpc++ = W[ix];
+    }
+
+    /* clear unused digits [that existed in the old copy of c] */
+    for (; ix < olduse; ix++) {
+      *tmpc++ = 0;
+    }
+  }
+  mp_clamp (c);
+  return MP_OKAY;
+}
+#endif /* BN_FAST_S_MP_MUL_DIGS_C */
+
+
+/* init an mp_init for a given size */
+static int mp_init_size (mp_int * a, int size)
+{
+  int x;
+
+  /* pad size so there are always extra digits */
+  size += (MP_PREC * 2) - (size % MP_PREC);	
+  
+  /* alloc mem */
+  a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size);
+  if (a->dp == NULL) {
+    return MP_MEM;
+  }
+
+  /* set the members */
+  a->used  = 0;
+  a->alloc = size;
+  a->sign  = MP_ZPOS;
+
+  /* zero the digits */
+  for (x = 0; x < size; x++) {
+      a->dp[x] = 0;
+  }
+
+  return MP_OKAY;
+}
+
+
+/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */
+static int s_mp_sqr (mp_int * a, mp_int * b)
+{
+  mp_int  t;
+  int     res, ix, iy, pa;
+  mp_word r;
+  mp_digit u, tmpx, *tmpt;
+
+  pa = a->used;
+  if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) {
+    return res;
+  }
+
+  /* default used is maximum possible size */
+  t.used = 2*pa + 1;
+
+  for (ix = 0; ix < pa; ix++) {
+    /* first calculate the digit at 2*ix */
+    /* calculate double precision result */
+    r = ((mp_word) t.dp[2*ix]) +
+        ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]);
+
+    /* store lower part in result */
+    t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK));
+
+    /* get the carry */
+    u           = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
+
+    /* left hand side of A[ix] * A[iy] */
+    tmpx        = a->dp[ix];
+
+    /* alias for where to store the results */
+    tmpt        = t.dp + (2*ix + 1);
+    
+    for (iy = ix + 1; iy < pa; iy++) {
+      /* first calculate the product */
+      r       = ((mp_word)tmpx) * ((mp_word)a->dp[iy]);
+
+      /* now calculate the double precision result, note we use
+       * addition instead of *2 since it's easier to optimize
+       */
+      r       = ((mp_word) *tmpt) + r + r + ((mp_word) u);
+
+      /* store lower part */
+      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+      /* get carry */
+      u       = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
+    }
+    /* propagate upwards */
+    while (u != ((mp_digit) 0)) {
+      r       = ((mp_word) *tmpt) + ((mp_word) u);
+      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+      u       = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
+    }
+  }
+
+  mp_clamp (&t);
+  mp_exch (&t, b);
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+/* multiplies |a| * |b| and does not compute the lower digs digits
+ * [meant to get the higher part of the product]
+ */
+static int s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
+{
+  mp_int  t;
+  int     res, pa, pb, ix, iy;
+  mp_digit u;
+  mp_word r;
+  mp_digit tmpx, *tmpt, *tmpy;
+
+  /* can we use the fast multiplier? */
+#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C
+  if (((a->used + b->used + 1) < MP_WARRAY)
+      && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+    return fast_s_mp_mul_high_digs (a, b, c, digs);
+  }
+#endif
+
+  if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) {
+    return res;
+  }
+  t.used = a->used + b->used + 1;
+
+  pa = a->used;
+  pb = b->used;
+  for (ix = 0; ix < pa; ix++) {
+    /* clear the carry */
+    u = 0;
+
+    /* left hand side of A[ix] * B[iy] */
+    tmpx = a->dp[ix];
+
+    /* alias to the address of where the digits will be stored */
+    tmpt = &(t.dp[digs]);
+
+    /* alias for where to read the right hand side from */
+    tmpy = b->dp + (digs - ix);
+
+    for (iy = digs - ix; iy < pb; iy++) {
+      /* calculate the double precision result */
+      r       = ((mp_word)*tmpt) +
+                ((mp_word)tmpx) * ((mp_word)*tmpy++) +
+                ((mp_word) u);
+
+      /* get the lower part */
+      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+      /* carry the carry */
+      u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
+    }
+    *tmpt = u;
+  }
+  mp_clamp (&t);
+  mp_exch (&t, c);
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+#ifdef BN_MP_MONTGOMERY_SETUP_C
+/* setups the montgomery reduction stuff */
+static int
+mp_montgomery_setup (mp_int * n, mp_digit * rho)
+{
+  mp_digit x, b;
+
+/* fast inversion mod 2**k
+ *
+ * Based on the fact that
+ *
+ * XA = 1 (mod 2**n)  =>  (X(2-XA)) A = 1 (mod 2**2n)
+ *                    =>  2*X*A - X*X*A*A = 1
+ *                    =>  2*(1) - (1)     = 1
+ */
+  b = n->dp[0];
+
+  if ((b & 1) == 0) {
+    return MP_VAL;
+  }
+
+  x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */
+  x *= 2 - b * x;               /* here x*a==1 mod 2**8 */
+#if !defined(MP_8BIT)
+  x *= 2 - b * x;               /* here x*a==1 mod 2**16 */
+#endif
+#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT))
+  x *= 2 - b * x;               /* here x*a==1 mod 2**32 */
+#endif
+#ifdef MP_64BIT
+  x *= 2 - b * x;               /* here x*a==1 mod 2**64 */
+#endif
+
+  /* rho = -1/m mod b */
+  *rho = (unsigned long)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK;
+
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C
+/* computes xR**-1 == x (mod N) via Montgomery Reduction
+ *
+ * This is an optimized implementation of montgomery_reduce
+ * which uses the comba method to quickly calculate the columns of the
+ * reduction.
+ *
+ * Based on Algorithm 14.32 on pp.601 of HAC.
+*/
+static int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho)
+{
+  int     ix, res, olduse;
+  mp_word W[MP_WARRAY];
+
+  /* get old used count */
+  olduse = x->used;
+
+  /* grow a as required */
+  if (x->alloc < n->used + 1) {
+    if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* first we have to get the digits of the input into
+   * an array of double precision words W[...]
+   */
+  {
+    register mp_word *_W;
+    register mp_digit *tmpx;
+
+    /* alias for the W[] array */
+    _W   = W;
+
+    /* alias for the digits of  x*/
+    tmpx = x->dp;
+
+    /* copy the digits of a into W[0..a->used-1] */
+    for (ix = 0; ix < x->used; ix++) {
+      *_W++ = *tmpx++;
+    }
+
+    /* zero the high words of W[a->used..m->used*2] */
+    for (; ix < n->used * 2 + 1; ix++) {
+      *_W++ = 0;
+    }
+  }
+
+  /* now we proceed to zero successive digits
+   * from the least significant upwards
+   */
+  for (ix = 0; ix < n->used; ix++) {
+    /* mu = ai * m' mod b
+     *
+     * We avoid a double precision multiplication (which isn't required)
+     * by casting the value down to a mp_digit.  Note this requires
+     * that W[ix-1] have  the carry cleared (see after the inner loop)
+     */
+    register mp_digit mu;
+    mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK);
+
+    /* a = a + mu * m * b**i
+     *
+     * This is computed in place and on the fly.  The multiplication
+     * by b**i is handled by offseting which columns the results
+     * are added to.
+     *
+     * Note the comba method normally doesn't handle carries in the
+     * inner loop In this case we fix the carry from the previous
+     * column since the Montgomery reduction requires digits of the
+     * result (so far) [see above] to work.  This is
+     * handled by fixing up one carry after the inner loop.  The
+     * carry fixups are done in order so after these loops the
+     * first m->used words of W[] have the carries fixed
+     */
+    {
+      register int iy;
+      register mp_digit *tmpn;
+      register mp_word *_W;
+
+      /* alias for the digits of the modulus */
+      tmpn = n->dp;
+
+      /* Alias for the columns set by an offset of ix */
+      _W = W + ix;
+
+      /* inner loop */
+      for (iy = 0; iy < n->used; iy++) {
+          *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++);
+      }
+    }
+
+    /* now fix carry for next digit, W[ix+1] */
+    W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT);
+  }
+
+  /* now we have to propagate the carries and
+   * shift the words downward [all those least
+   * significant digits we zeroed].
+   */
+  {
+    register mp_digit *tmpx;
+    register mp_word *_W, *_W1;
+
+    /* nox fix rest of carries */
+
+    /* alias for current word */
+    _W1 = W + ix;
+
+    /* alias for next word, where the carry goes */
+    _W = W + ++ix;
+
+    for (; ix <= n->used * 2 + 1; ix++) {
+      *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT);
+    }
+
+    /* copy out, A = A/b**n
+     *
+     * The result is A/b**n but instead of converting from an
+     * array of mp_word to mp_digit than calling mp_rshd
+     * we just copy them in the right order
+     */
+
+    /* alias for destination word */
+    tmpx = x->dp;
+
+    /* alias for shifted double precision result */
+    _W = W + n->used;
+
+    for (ix = 0; ix < n->used + 1; ix++) {
+      *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK));
+    }
+
+    /* zero oldused digits, if the input a was larger than
+     * m->used+1 we'll have to clear the digits
+     */
+    for (; ix < olduse; ix++) {
+      *tmpx++ = 0;
+    }
+  }
+
+  /* set the max used and clamp */
+  x->used = n->used + 1;
+  mp_clamp (x);
+
+  /* if A >= m then A = A - m */
+  if (mp_cmp_mag (x, n) != MP_LT) {
+    return s_mp_sub (x, n, x);
+  }
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_MUL_2_C
+/* b = a*2 */
+static int mp_mul_2(mp_int * a, mp_int * b)
+{
+  int     x, res, oldused;
+
+  /* grow to accommodate result */
+  if (b->alloc < a->used + 1) {
+    if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  oldused = b->used;
+  b->used = a->used;
+
+  {
+    register mp_digit r, rr, *tmpa, *tmpb;
+
+    /* alias for source */
+    tmpa = a->dp;
+    
+    /* alias for dest */
+    tmpb = b->dp;
+
+    /* carry */
+    r = 0;
+    for (x = 0; x < a->used; x++) {
+    
+      /* get what will be the *next* carry bit from the 
+       * MSB of the current digit 
+       */
+      rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1));
+      
+      /* now shift up this digit, add in the carry [from the previous] */
+      *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK;
+      
+      /* copy the carry that would be from the source 
+       * digit into the next iteration 
+       */
+      r = rr;
+    }
+
+    /* new leading digit? */
+    if (r != 0) {
+      /* add a MSB which is always 1 at this point */
+      *tmpb = 1;
+      ++(b->used);
+    }
+
+    /* now zero any excess digits on the destination 
+     * that we didn't write to 
+     */
+    tmpb = b->dp + b->used;
+    for (x = b->used; x < oldused; x++) {
+      *tmpb++ = 0;
+    }
+  }
+  b->sign = a->sign;
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+/*
+ * shifts with subtractions when the result is greater than b.
+ *
+ * The method is slightly modified to shift B unconditionally up to just under
+ * the leading bit of b.  This saves a lot of multiple precision shifting.
+ */
+static int mp_montgomery_calc_normalization (mp_int * a, mp_int * b)
+{
+  int     x, bits, res;
+
+  /* how many bits of last digit does b use */
+  bits = mp_count_bits (b) % DIGIT_BIT;
+
+  if (b->used > 1) {
+     if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) {
+        return res;
+     }
+  } else {
+     mp_set(a, 1);
+     bits = 1;
+  }
+
+
+  /* now compute C = A * B mod b */
+  for (x = bits - 1; x < (int)DIGIT_BIT; x++) {
+    if ((res = mp_mul_2 (a, a)) != MP_OKAY) {
+      return res;
+    }
+    if (mp_cmp_mag (a, b) != MP_LT) {
+      if ((res = s_mp_sub (a, b, a)) != MP_OKAY) {
+        return res;
+      }
+    }
+  }
+
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_EXPTMOD_FAST_C
+/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85
+ *
+ * Uses a left-to-right k-ary sliding window to compute the modular exponentiation.
+ * The value of k changes based on the size of the exponent.
+ *
+ * Uses Montgomery or Diminished Radix reduction [whichever appropriate]
+ */
+
+static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
+{
+  mp_int  M[TAB_SIZE], res;
+  mp_digit buf, mp;
+  int     err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
+
+  /* use a pointer to the reduction algorithm.  This allows us to use
+   * one of many reduction algorithms without modding the guts of
+   * the code with if statements everywhere.
+   */
+  int     (*redux)(mp_int*,mp_int*,mp_digit);
+
+  /* find window size */
+  x = mp_count_bits (X);
+  if (x <= 7) {
+    winsize = 2;
+  } else if (x <= 36) {
+    winsize = 3;
+  } else if (x <= 140) {
+    winsize = 4;
+  } else if (x <= 450) {
+    winsize = 5;
+  } else if (x <= 1303) {
+    winsize = 6;
+  } else if (x <= 3529) {
+    winsize = 7;
+  } else {
+    winsize = 8;
+  }
+
+#ifdef MP_LOW_MEM
+  if (winsize > 5) {
+     winsize = 5;
+  }
+#endif
+
+  /* init M array */
+  /* init first cell */
+  if ((err = mp_init(&M[1])) != MP_OKAY) {
+     return err;
+  }
+
+  /* now init the second half of the array */
+  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+    if ((err = mp_init(&M[x])) != MP_OKAY) {
+      for (y = 1<<(winsize-1); y < x; y++) {
+        mp_clear (&M[y]);
+      }
+      mp_clear(&M[1]);
+      return err;
+    }
+  }
+
+  /* determine and setup reduction code */
+  if (redmode == 0) {
+#ifdef BN_MP_MONTGOMERY_SETUP_C     
+     /* now setup montgomery  */
+     if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) {
+        goto LBL_M;
+     }
+#else
+     err = MP_VAL;
+     goto LBL_M;
+#endif
+
+     /* automatically pick the comba one if available (saves quite a few calls/ifs) */
+#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C
+     if (((P->used * 2 + 1) < MP_WARRAY) &&
+          P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+        redux = fast_mp_montgomery_reduce;
+     } else 
+#endif
+     {
+#ifdef BN_MP_MONTGOMERY_REDUCE_C
+        /* use slower baseline Montgomery method */
+        redux = mp_montgomery_reduce;
+#else
+        err = MP_VAL;
+        goto LBL_M;
+#endif
+     }
+  } else if (redmode == 1) {
+#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C)
+     /* setup DR reduction for moduli of the form B**k - b */
+     mp_dr_setup(P, &mp);
+     redux = mp_dr_reduce;
+#else
+     err = MP_VAL;
+     goto LBL_M;
+#endif
+  } else {
+#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C)
+     /* setup DR reduction for moduli of the form 2**k - b */
+     if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) {
+        goto LBL_M;
+     }
+     redux = mp_reduce_2k;
+#else
+     err = MP_VAL;
+     goto LBL_M;
+#endif
+  }
+
+  /* setup result */
+  if ((err = mp_init (&res)) != MP_OKAY) {
+    goto LBL_M;
+  }
+
+  /* create M table
+   *
+
+   *
+   * The first half of the table is not computed though accept for M[0] and M[1]
+   */
+
+  if (redmode == 0) {
+#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+     /* now we need R mod m */
+     if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) {
+       goto LBL_RES;
+     }
+#else 
+     err = MP_VAL;
+     goto LBL_RES;
+#endif
+
+     /* now set M[1] to G * R mod m */
+     if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) {
+       goto LBL_RES;
+     }
+  } else {
+     mp_set(&res, 1);
+     if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) {
+        goto LBL_RES;
+     }
+  }
+
+  /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */
+  if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
+    goto LBL_RES;
+  }
+
+  for (x = 0; x < (winsize - 1); x++) {
+    if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) {
+      goto LBL_RES;
+    }
+    if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) {
+      goto LBL_RES;
+    }
+  }
+
+  /* create upper table */
+  for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
+    if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
+      goto LBL_RES;
+    }
+    if ((err = redux (&M[x], P, mp)) != MP_OKAY) {
+      goto LBL_RES;
+    }
+  }
+
+  /* set initial mode and bit cnt */
+  mode   = 0;
+  bitcnt = 1;
+  buf    = 0;
+  digidx = X->used - 1;
+  bitcpy = 0;
+  bitbuf = 0;
+
+  for (;;) {
+    /* grab next digit as required */
+    if (--bitcnt == 0) {
+      /* if digidx == -1 we are out of digits so break */
+      if (digidx == -1) {
+        break;
+      }
+      /* read next digit and reset bitcnt */
+      buf    = X->dp[digidx--];
+      bitcnt = (int)DIGIT_BIT;
+    }
+
+    /* grab the next msb from the exponent */
+    y     = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1;
+    buf <<= (mp_digit)1;
+
+    /* if the bit is zero and mode == 0 then we ignore it
+     * These represent the leading zero bits before the first 1 bit
+     * in the exponent.  Technically this opt is not required but it
+     * does lower the # of trivial squaring/reductions used
+     */
+    if (mode == 0 && y == 0) {
+      continue;
+    }
+
+    /* if the bit is zero and mode == 1 then we square */
+    if (mode == 1 && y == 0) {
+      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, mp)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      continue;
+    }
+
+    /* else we add it to the window */
+    bitbuf |= (y << (winsize - ++bitcpy));
+    mode    = 2;
+
+    if (bitcpy == winsize) {
+      /* ok window is filled so square as required and multiply  */
+      /* square first */
+      for (x = 0; x < winsize; x++) {
+        if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+        if ((err = redux (&res, P, mp)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+      }
+
+      /* then multiply */
+      if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, mp)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+
+      /* empty window and reset */
+      bitcpy = 0;
+      bitbuf = 0;
+      mode   = 1;
+    }
+  }
+
+  /* if bits remain then square/multiply */
+  if (mode == 2 && bitcpy > 0) {
+    /* square then multiply if the bit is set */
+    for (x = 0; x < bitcpy; x++) {
+      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, mp)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+
+      /* get next bit of the window */
+      bitbuf <<= 1;
+      if ((bitbuf & (1 << winsize)) != 0) {
+        /* then multiply */
+        if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+        if ((err = redux (&res, P, mp)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+      }
+    }
+  }
+
+  if (redmode == 0) {
+     /* fixup result if Montgomery reduction is used
+      * recall that any value in a Montgomery system is
+      * actually multiplied by R mod n.  So we have
+      * to reduce one more time to cancel out the factor
+      * of R.
+      */
+     if ((err = redux(&res, P, mp)) != MP_OKAY) {
+       goto LBL_RES;
+     }
+  }
+
+  /* swap res with Y */
+  mp_exch (&res, Y);
+  err = MP_OKAY;
+LBL_RES:mp_clear (&res);
+LBL_M:
+  mp_clear(&M[1]);
+  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+    mp_clear (&M[x]);
+  }
+  return err;
+}
+#endif
+
+
+#ifdef BN_FAST_S_MP_SQR_C
+/* the jist of squaring...
+ * you do like mult except the offset of the tmpx [one that 
+ * starts closer to zero] can't equal the offset of tmpy.  
+ * So basically you set up iy like before then you min it with
+ * (ty-tx) so that it never happens.  You double all those 
+ * you add in the inner loop
+
+After that loop you do the squares and add them in.
+*/
+
+static int fast_s_mp_sqr (mp_int * a, mp_int * b)
+{
+  int       olduse, res, pa, ix, iz;
+  mp_digit   W[MP_WARRAY], *tmpx;
+  mp_word   W1;
+
+  /* grow the destination as required */
+  pa = a->used + a->used;
+  if (b->alloc < pa) {
+    if ((res = mp_grow (b, pa)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* number of output digits to produce */
+  W1 = 0;
+  for (ix = 0; ix < pa; ix++) { 
+      int      tx, ty, iy;
+      mp_word  _W;
+      mp_digit *tmpy;
+
+      /* clear counter */
+      _W = 0;
+
+      /* get offsets into the two bignums */
+      ty = MIN(a->used-1, ix);
+      tx = ix - ty;
+
+      /* setup temp aliases */
+      tmpx = a->dp + tx;
+      tmpy = a->dp + ty;
+
+      /* this is the number of times the loop will iterrate, essentially
+         while (tx++ < a->used && ty-- >= 0) { ... }
+       */
+      iy = MIN(a->used-tx, ty+1);
+
+      /* now for squaring tx can never equal ty 
+       * we halve the distance since they approach at a rate of 2x
+       * and we have to round because odd cases need to be executed
+       */
+      iy = MIN(iy, (ty-tx+1)>>1);
+
+      /* execute loop */
+      for (iz = 0; iz < iy; iz++) {
+         _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--);
+      }
+
+      /* double the inner product and add carry */
+      _W = _W + _W + W1;
+
+      /* even columns have the square term in them */
+      if ((ix&1) == 0) {
+         _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]);
+      }
+
+      /* store it */
+      W[ix] = (mp_digit)(_W & MP_MASK);
+
+      /* make next carry */
+      W1 = _W >> ((mp_word)DIGIT_BIT);
+  }
+
+  /* setup dest */
+  olduse  = b->used;
+  b->used = a->used+a->used;
+
+  {
+    mp_digit *tmpb;
+    tmpb = b->dp;
+    for (ix = 0; ix < pa; ix++) {
+      *tmpb++ = W[ix] & MP_MASK;
+    }
+
+    /* clear unused digits [that existed in the old copy of c] */
+    for (; ix < olduse; ix++) {
+      *tmpb++ = 0;
+    }
+  }
+  mp_clamp (b);
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_MUL_D_C
+/* multiply by a digit */
+static int
+mp_mul_d (mp_int * a, mp_digit b, mp_int * c)
+{
+  mp_digit u, *tmpa, *tmpc;
+  mp_word  r;
+  int      ix, res, olduse;
+
+  /* make sure c is big enough to hold a*b */
+  if (c->alloc < a->used + 1) {
+    if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* get the original destinations used count */
+  olduse = c->used;
+
+  /* set the sign */
+  c->sign = a->sign;
+
+  /* alias for a->dp [source] */
+  tmpa = a->dp;
+
+  /* alias for c->dp [dest] */
+  tmpc = c->dp;
+
+  /* zero carry */
+  u = 0;
+
+  /* compute columns */
+  for (ix = 0; ix < a->used; ix++) {
+    /* compute product and carry sum for this term */
+    r       = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b);
+
+    /* mask off higher bits to get a single digit */
+    *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+    /* send carry into next iteration */
+    u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
+  }
+
+  /* store final carry [if any] and increment ix offset  */
+  *tmpc++ = u;
+  ++ix;
+
+  /* now zero digits above the top */
+  while (ix++ < olduse) {
+     *tmpc++ = 0;
+  }
+
+  /* set used count */
+  c->used = a->used + 1;
+  mp_clamp(c);
+
+  return MP_OKAY;
+}
+#endif
diff --git a/hostap/src/tls/pkcs1.c b/hostap/src/tls/pkcs1.c
new file mode 100644
index 0000000..141ac50
--- /dev/null
+++ b/hostap/src/tls/pkcs1.c
@@ -0,0 +1,320 @@
+/*
+ * PKCS #1 (RSA Encryption)
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/crypto.h"
+#include "rsa.h"
+#include "asn1.h"
+#include "pkcs1.h"
+
+
+static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen,
+					   const u8 *in, size_t inlen,
+					   u8 *out, size_t *outlen)
+{
+	size_t ps_len;
+	u8 *pos;
+
+	/*
+	 * PKCS #1 v1.5, 8.1:
+	 *
+	 * EB = 00 || BT || PS || 00 || D
+	 * BT = 00 or 01 for private-key operation; 02 for public-key operation
+	 * PS = k-3-||D||; at least eight octets
+	 * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero)
+	 * k = length of modulus in octets (modlen)
+	 */
+
+	if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) {
+		wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer "
+			   "lengths (modlen=%lu outlen=%lu inlen=%lu)",
+			   __func__, (unsigned long) modlen,
+			   (unsigned long) *outlen,
+			   (unsigned long) inlen);
+		return -1;
+	}
+
+	pos = out;
+	*pos++ = 0x00;
+	*pos++ = block_type; /* BT */
+	ps_len = modlen - inlen - 3;
+	switch (block_type) {
+	case 0:
+		os_memset(pos, 0x00, ps_len);
+		pos += ps_len;
+		break;
+	case 1:
+		os_memset(pos, 0xff, ps_len);
+		pos += ps_len;
+		break;
+	case 2:
+		if (os_get_random(pos, ps_len) < 0) {
+			wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get "
+				   "random data for PS", __func__);
+			return -1;
+		}
+		while (ps_len--) {
+			if (*pos == 0x00)
+				*pos = 0x01;
+			pos++;
+		}
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type "
+			   "%d", __func__, block_type);
+		return -1;
+	}
+	*pos++ = 0x00;
+	os_memcpy(pos, in, inlen); /* D */
+
+	return 0;
+}
+
+
+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)
+{
+	size_t modlen;
+
+	modlen = crypto_rsa_get_modulus_len(key);
+
+	if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen,
+					    out, outlen) < 0)
+		return -1;
+
+	return crypto_rsa_exptmod(out, modlen, out, outlen, key, use_private);
+}
+
+
+int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key,
+				  const u8 *in, size_t inlen,
+				  u8 *out, size_t *outlen)
+{
+	int res;
+	u8 *pos, *end;
+
+	res = crypto_rsa_exptmod(in, inlen, out, outlen, key, 1);
+	if (res)
+		return res;
+
+	if (*outlen < 2 || out[0] != 0 || out[1] != 2)
+		return -1;
+
+	/* Skip PS (pseudorandom non-zero octets) */
+	pos = out + 2;
+	end = out + *outlen;
+	while (*pos && pos < end)
+		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;
+
+	/* Strip PKCS #1 header */
+	os_memmove(out, pos, *outlen);
+
+	return 0;
+}
+
+
+int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
+			     const u8 *crypt, size_t crypt_len,
+			     u8 *plain, size_t *plain_len)
+{
+	size_t len;
+	u8 *pos;
+
+	len = *plain_len;
+	if (crypto_rsa_exptmod(crypt, crypt_len, plain, &len, key, 0) < 0)
+		return -1;
+
+	/*
+	 * PKCS #1 v1.5, 8.1:
+	 *
+	 * EB = 00 || BT || PS || 00 || D
+	 * 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] != 0x01) {
+		wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+			   "structure");
+		return -1;
+	}
+
+	pos = plain + 3;
+	/* 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 */
+		wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature "
+			   "padding");
+		return -1;
+	}
+
+	if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) {
+		wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+			   "structure (2)");
+		return -1;
+	}
+	pos++;
+	len -= pos - plain;
+
+	/* Strip PKCS #1 header */
+	os_memmove(plain, pos, len);
+	*plain_len = len;
+
+	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/hostap/src/tls/pkcs1.h b/hostap/src/tls/pkcs1.h
new file mode 100644
index 0000000..f37ebf3
--- /dev/null
+++ b/hostap/src/tls/pkcs1.h
@@ -0,0 +1,29 @@
+/*
+ * PKCS #1 (RSA Encryption)
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#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);
+int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key,
+				  const u8 *in, size_t inlen,
+				  u8 *out, size_t *outlen);
+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/hostap/src/tls/pkcs5.c b/hostap/src/tls/pkcs5.c
new file mode 100644
index 0000000..8a93483
--- /dev/null
+++ b/hostap/src/tls/pkcs5.c
@@ -0,0 +1,232 @@
+/*
+ * PKCS #5 (Password-based Encryption)
+ * 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/crypto.h"
+#include "crypto/md5.h"
+#include "asn1.h"
+#include "pkcs5.h"
+
+
+struct pkcs5_params {
+	enum pkcs5_alg {
+		PKCS5_ALG_UNKNOWN,
+		PKCS5_ALG_MD5_DES_CBC
+	} alg;
+	u8 salt[8];
+	size_t salt_len;
+	unsigned int iter_count;
+};
+
+
+static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid)
+{
+	if (oid->len == 7 &&
+	    oid->oid[0] == 1 /* iso */ &&
+	    oid->oid[1] == 2 /* member-body */ &&
+	    oid->oid[2] == 840 /* us */ &&
+	    oid->oid[3] == 113549 /* rsadsi */ &&
+	    oid->oid[4] == 1 /* pkcs */ &&
+	    oid->oid[5] == 5 /* pkcs-5 */ &&
+	    oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */)
+		return PKCS5_ALG_MD5_DES_CBC;
+
+	return PKCS5_ALG_UNKNOWN;
+}
+
+
+static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len,
+			    struct pkcs5_params *params)
+{
+	struct asn1_hdr hdr;
+	const u8 *enc_alg_end, *pos, *end;
+	struct asn1_oid oid;
+	char obuf[80];
+
+	/* AlgorithmIdentifier */
+
+	enc_alg_end = enc_alg + enc_alg_len;
+
+	os_memset(params, 0, sizeof(*params));
+
+	if (asn1_get_oid(enc_alg, enc_alg_end - enc_alg, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG, "PKCS #5: Failed to parse OID "
+			   "(algorithm)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #5: encryption algorithm %s", obuf);
+	params->alg = pkcs5_get_alg(&oid);
+	if (params->alg == PKCS5_ALG_UNKNOWN) {
+		wpa_printf(MSG_INFO, "PKCS #5: unsupported encryption "
+			   "algorithm %s", obuf);
+		return -1;
+	}
+
+	/*
+	 * PKCS#5, Section 8
+	 * PBEParameter ::= SEQUENCE {
+	 *   salt OCTET STRING SIZE(8),
+	 *   iterationCount INTEGER }
+	 */
+
+	if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "PKCS #5: Expected SEQUENCE "
+			   "(PBEParameter) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/* salt OCTET STRING SIZE(8) */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING ||
+	    hdr.length != 8) {
+		wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) "
+			   "(salt) - found class %d tag 0x%x size %d",
+			   hdr.class, hdr.tag, hdr.length);
+		return -1;
+	}
+	pos = hdr.payload + hdr.length;
+	os_memcpy(params->salt, hdr.payload, hdr.length);
+	params->salt_len = hdr.length;
+	wpa_hexdump(MSG_DEBUG, "PKCS #5: salt",
+		    params->salt, params->salt_len);
+
+	/* iterationCount INTEGER */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+		wpa_printf(MSG_DEBUG, "PKCS #5: Expected INTEGER - found "
+			   "class %d tag 0x%x", hdr.class, hdr.tag);
+		return -1;
+	}
+	if (hdr.length == 1)
+		params->iter_count = *hdr.payload;
+	else if (hdr.length == 2)
+		params->iter_count = WPA_GET_BE16(hdr.payload);
+	else if (hdr.length == 4)
+		params->iter_count = WPA_GET_BE32(hdr.payload);
+	else {
+		wpa_hexdump(MSG_DEBUG, "PKCS #5: Unsupported INTEGER value "
+			    " (iterationCount)",
+			    hdr.payload, hdr.length);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x",
+		   params->iter_count);
+	if (params->iter_count == 0 || params->iter_count > 0xffff) {
+		wpa_printf(MSG_INFO, "PKCS #5: Unsupported "
+			   "iterationCount=0x%x", params->iter_count);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params,
+						const char *passwd)
+{
+	unsigned int i;
+	u8 hash[MD5_MAC_LEN];
+	const u8 *addr[2];
+	size_t len[2];
+
+	if (params->alg != PKCS5_ALG_MD5_DES_CBC)
+		return NULL;
+
+	addr[0] = (const u8 *) passwd;
+	len[0] = os_strlen(passwd);
+	addr[1] = params->salt;
+	len[1] = params->salt_len;
+	if (md5_vector(2, addr, len, hash) < 0)
+		return NULL;
+	addr[0] = hash;
+	len[0] = MD5_MAC_LEN;
+	for (i = 1; i < params->iter_count; i++) {
+		if (md5_vector(1, addr, len, hash) < 0)
+			return NULL;
+	}
+	/* TODO: DES key parity bits(?) */
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES key", hash, 8);
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES IV", hash + 8, 8);
+
+	return crypto_cipher_init(CRYPTO_CIPHER_ALG_DES, hash + 8, hash, 8);
+}
+
+
+u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len,
+		   const u8 *enc_data, size_t enc_data_len,
+		   const char *passwd, size_t *data_len)
+{
+	struct crypto_cipher *ctx;
+	u8 *eb, pad;
+	struct pkcs5_params params;
+	unsigned int i;
+
+	if (pkcs5_get_params(enc_alg, enc_alg_len, &params) < 0) {
+		wpa_printf(MSG_DEBUG, "PKCS #5: Unsupported parameters");
+		return NULL;
+	}
+
+	ctx = pkcs5_crypto_init(&params, passwd);
+	if (ctx == NULL) {
+		wpa_printf(MSG_DEBUG, "PKCS #5: Failed to initialize crypto");
+		return NULL;
+	}
+
+	/* PKCS #5, Section 7 - Decryption process */
+	if (enc_data_len < 16 || enc_data_len % 8) {
+		wpa_printf(MSG_INFO, "PKCS #5: invalid length of ciphertext "
+			   "%d", (int) enc_data_len);
+		crypto_cipher_deinit(ctx);
+		return NULL;
+	}
+
+	eb = os_malloc(enc_data_len);
+	if (eb == NULL) {
+		crypto_cipher_deinit(ctx);
+		return NULL;
+	}
+
+	if (crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) {
+		wpa_printf(MSG_DEBUG, "PKCS #5: Failed to decrypt EB");
+		crypto_cipher_deinit(ctx);
+		os_free(eb);
+		return NULL;
+	}
+	crypto_cipher_deinit(ctx);
+
+	pad = eb[enc_data_len - 1];
+	if (pad > 8) {
+		wpa_printf(MSG_INFO, "PKCS #5: Invalid PS octet 0x%x", pad);
+		os_free(eb);
+		return NULL;
+	}
+	for (i = enc_data_len - pad; i < enc_data_len; i++) {
+		if (eb[i] != pad) {
+			wpa_hexdump(MSG_INFO, "PKCS #5: Invalid PS",
+				    eb + enc_data_len - pad, pad);
+			os_free(eb);
+			return NULL;
+		}
+	}
+
+	wpa_hexdump_key(MSG_MSGDUMP, "PKCS #5: message M (encrypted key)",
+			eb, enc_data_len - pad);
+
+	*data_len = enc_data_len - pad;
+	return eb;
+}
diff --git a/hostap/src/tls/pkcs5.h b/hostap/src/tls/pkcs5.h
new file mode 100644
index 0000000..20ddadc
--- /dev/null
+++ b/hostap/src/tls/pkcs5.h
@@ -0,0 +1,16 @@
+/*
+ * PKCS #5 (Password-based Encryption)
+ * 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.
+ */
+
+#ifndef PKCS5_H
+#define PKCS5_H
+
+u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len,
+		   const u8 *enc_data, size_t enc_data_len,
+		   const char *passwd, size_t *data_len);
+
+#endif /* PKCS5_H */
diff --git a/hostap/src/tls/pkcs8.c b/hostap/src/tls/pkcs8.c
new file mode 100644
index 0000000..52e43a4
--- /dev/null
+++ b/hostap/src/tls/pkcs8.c
@@ -0,0 +1,187 @@
+/*
+ * PKCS #8 (Private-key information syntax)
+ * Copyright (c) 2006-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 "asn1.h"
+#include "bignum.h"
+#include "rsa.h"
+#include "pkcs5.h"
+#include "pkcs8.h"
+
+
+struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	struct bignum *zero;
+	struct asn1_oid oid;
+	char obuf[80];
+
+	/* PKCS #8, Chapter 6 */
+
+	/* PrivateKeyInfo ::= SEQUENCE */
+	if (asn1_get_next(buf, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
+			   "header (SEQUENCE); assume PKCS #8 not used");
+		return NULL;
+	}
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/* version Version (Version ::= INTEGER) */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Expected INTEGER - found "
+			   "class %d tag 0x%x; assume PKCS #8 not used",
+			   hdr.class, hdr.tag);
+		return NULL;
+	}
+
+	zero = bignum_init();
+	if (zero == NULL)
+		return NULL;
+
+	if (bignum_set_unsigned_bin(zero, hdr.payload, hdr.length) < 0) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse INTEGER");
+		bignum_deinit(zero);
+		return NULL;
+	}
+	pos = hdr.payload + hdr.length;
+
+	if (bignum_cmp_d(zero, 0) != 0) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Expected zero INTEGER in the "
+			   "beginning of private key; not found; assume "
+			   "PKCS #8 not used");
+		bignum_deinit(zero);
+		return NULL;
+	}
+	bignum_deinit(zero);
+
+	/* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier
+	 * (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */
+	if (asn1_get_next(pos, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
+			   "(AlgorithmIdentifier) - found class %d tag 0x%x; "
+			   "assume PKCS #8 not used",
+			   hdr.class, hdr.tag);
+		return NULL;
+	}
+
+	if (asn1_get_oid(hdr.payload, hdr.length, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse OID "
+			   "(algorithm); assume PKCS #8 not used");
+		return NULL;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #8: algorithm=%s", obuf);
+
+	if (oid.len != 7 ||
+	    oid.oid[0] != 1 /* iso */ ||
+	    oid.oid[1] != 2 /* member-body */ ||
+	    oid.oid[2] != 840 /* us */ ||
+	    oid.oid[3] != 113549 /* rsadsi */ ||
+	    oid.oid[4] != 1 /* pkcs */ ||
+	    oid.oid[5] != 1 /* pkcs-1 */ ||
+	    oid.oid[6] != 1 /* rsaEncryption */) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Unsupported private key "
+			   "algorithm %s", obuf);
+		return NULL;
+	}
+
+	pos = hdr.payload + hdr.length;
+
+	/* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
+			   "(privateKey) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey");
+
+	return (struct crypto_private_key *)
+		crypto_rsa_import_private_key(hdr.payload, hdr.length);
+}
+
+
+struct crypto_private_key *
+pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end, *enc_alg;
+	size_t enc_alg_len;
+	u8 *data;
+	size_t data_len;
+
+	if (passwd == NULL)
+		return NULL;
+
+	/*
+	 * PKCS #8, Chapter 7
+	 * EncryptedPrivateKeyInfo ::= SEQUENCE {
+	 *   encryptionAlgorithm EncryptionAlgorithmIdentifier,
+	 *   encryptedData EncryptedData }
+	 * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+	 * EncryptedData ::= OCTET STRING
+	 */
+
+	if (asn1_get_next(buf, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
+			   "header (SEQUENCE); assume encrypted PKCS #8 not "
+			   "used");
+		return NULL;
+	}
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/* encryptionAlgorithm EncryptionAlgorithmIdentifier */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
+			   "(AlgorithmIdentifier) - found class %d tag 0x%x; "
+			   "assume encrypted PKCS #8 not used",
+			   hdr.class, hdr.tag);
+		return NULL;
+	}
+	enc_alg = hdr.payload;
+	enc_alg_len = hdr.length;
+	pos = hdr.payload + hdr.length;
+
+	/* encryptedData EncryptedData */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
+			   "(encryptedData) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return NULL;
+	}
+
+	data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length,
+			     passwd, &data_len);
+	if (data) {
+		struct crypto_private_key *key;
+		key = pkcs8_key_import(data, data_len);
+		os_free(data);
+		return key;
+	}
+
+	return NULL;
+}
diff --git a/hostap/src/tls/pkcs8.h b/hostap/src/tls/pkcs8.h
new file mode 100644
index 0000000..bebf840
--- /dev/null
+++ b/hostap/src/tls/pkcs8.h
@@ -0,0 +1,16 @@
+/*
+ * PKCS #8 (Private-key information syntax)
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PKCS8_H
+#define PKCS8_H
+
+struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len);
+struct crypto_private_key *
+pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd);
+
+#endif /* PKCS8_H */
diff --git a/hostap/src/tls/rsa.c b/hostap/src/tls/rsa.c
new file mode 100644
index 0000000..0b7b530
--- /dev/null
+++ b/hostap/src/tls/rsa.c
@@ -0,0 +1,375 @@
+/*
+ * RSA
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "asn1.h"
+#include "bignum.h"
+#include "rsa.h"
+
+
+struct crypto_rsa_key {
+	int private_key; /* whether private key is set */
+	struct bignum *n; /* modulus (p * q) */
+	struct bignum *e; /* public exponent */
+	/* The following parameters are available only if private_key is set */
+	struct bignum *d; /* private exponent */
+	struct bignum *p; /* prime p (factor of n) */
+	struct bignum *q; /* prime q (factor of n) */
+	struct bignum *dmp1; /* d mod (p - 1); CRT exponent */
+	struct bignum *dmq1; /* d mod (q - 1); CRT exponent */
+	struct bignum *iqmp; /* 1 / q mod p; CRT coefficient */
+};
+
+
+static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end,
+					   struct bignum *num)
+{
+	struct asn1_hdr hdr;
+
+	if (pos == NULL)
+		return NULL;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+		wpa_printf(MSG_DEBUG, "RSA: Expected INTEGER - found class %d "
+			   "tag 0x%x", hdr.class, hdr.tag);
+		return NULL;
+	}
+
+	if (bignum_set_unsigned_bin(num, hdr.payload, hdr.length) < 0) {
+		wpa_printf(MSG_DEBUG, "RSA: Failed to parse INTEGER");
+		return NULL;
+	}
+
+	return hdr.payload + hdr.length;
+}
+
+
+/**
+ * crypto_rsa_import_public_key - Import an RSA public key
+ * @buf: Key buffer (DER encoded RSA public key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the public key or %NULL on failure
+ */
+struct crypto_rsa_key *
+crypto_rsa_import_public_key(const u8 *buf, size_t len)
+{
+	struct crypto_rsa_key *key;
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+
+	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) {
+		crypto_rsa_free(key);
+		return NULL;
+	}
+
+	/*
+	 * PKCS #1, 7.1:
+	 * RSAPublicKey ::= SEQUENCE {
+	 *     modulus INTEGER, -- n
+	 *     publicExponent INTEGER -- e 
+	 * }
+	 */
+
+	if (asn1_get_next(buf, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE "
+			   "(public key) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto error;
+	}
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	pos = crypto_rsa_parse_integer(pos, end, key->n);
+	pos = crypto_rsa_parse_integer(pos, end, key->e);
+
+	if (pos == NULL)
+		goto error;
+
+	if (pos != end) {
+		wpa_hexdump(MSG_DEBUG,
+			    "RSA: Extra data in public key SEQUENCE",
+			    pos, end - pos);
+		goto error;
+	}
+
+	return key;
+
+error:
+	crypto_rsa_free(key);
+	return NULL;
+}
+
+
+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)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the private key or %NULL on failure
+ */
+struct crypto_rsa_key *
+crypto_rsa_import_private_key(const u8 *buf, size_t len)
+{
+	struct crypto_rsa_key *key;
+	struct bignum *zero;
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+
+	key = os_zalloc(sizeof(*key));
+	if (key == NULL)
+		return NULL;
+
+	key->private_key = 1;
+
+	key->n = bignum_init();
+	key->e = bignum_init();
+	key->d = bignum_init();
+	key->p = bignum_init();
+	key->q = bignum_init();
+	key->dmp1 = bignum_init();
+	key->dmq1 = bignum_init();
+	key->iqmp = bignum_init();
+
+	if (key->n == NULL || key->e == NULL || key->d == NULL ||
+	    key->p == NULL || key->q == NULL || key->dmp1 == NULL ||
+	    key->dmq1 == NULL || key->iqmp == NULL) {
+		crypto_rsa_free(key);
+		return NULL;
+	}
+
+	/*
+	 * PKCS #1, 7.2:
+	 * RSAPrivateKey ::= SEQUENCE {
+	 *    version Version,
+	 *    modulus INTEGER, -- n
+	 *    publicExponent INTEGER, -- e
+	 *    privateExponent INTEGER, -- d
+	 *    prime1 INTEGER, -- p
+	 *    prime2 INTEGER, -- q
+	 *    exponent1 INTEGER, -- d mod (p-1)
+	 *    exponent2 INTEGER, -- d mod (q-1)
+	 *    coefficient INTEGER -- (inverse of q) mod p
+	 * }
+	 *
+	 * Version ::= INTEGER -- shall be 0 for this version of the standard
+	 */
+	if (asn1_get_next(buf, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE "
+			   "(public key) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto error;
+	}
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	zero = bignum_init();
+	if (zero == NULL)
+		goto error;
+	pos = crypto_rsa_parse_integer(pos, end, zero);
+	if (pos == NULL || bignum_cmp_d(zero, 0) != 0) {
+		wpa_printf(MSG_DEBUG, "RSA: Expected zero INTEGER in the "
+			   "beginning of private key; not found");
+		bignum_deinit(zero);
+		goto error;
+	}
+	bignum_deinit(zero);
+
+	pos = crypto_rsa_parse_integer(pos, end, key->n);
+	pos = crypto_rsa_parse_integer(pos, end, key->e);
+	pos = crypto_rsa_parse_integer(pos, end, key->d);
+	pos = crypto_rsa_parse_integer(pos, end, key->p);
+	pos = crypto_rsa_parse_integer(pos, end, key->q);
+	pos = crypto_rsa_parse_integer(pos, end, key->dmp1);
+	pos = crypto_rsa_parse_integer(pos, end, key->dmq1);
+	pos = crypto_rsa_parse_integer(pos, end, key->iqmp);
+
+	if (pos == NULL)
+		goto error;
+
+	if (pos != end) {
+		wpa_hexdump(MSG_DEBUG,
+			    "RSA: Extra data in public key SEQUENCE",
+			    pos, end - pos);
+		goto error;
+	}
+
+	return key;
+
+error:
+	crypto_rsa_free(key);
+	return NULL;
+}
+
+
+/**
+ * crypto_rsa_get_modulus_len - Get the modulus length of the RSA key
+ * @key: RSA key
+ * Returns: Modulus length of the key
+ */
+size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key)
+{
+	return bignum_get_unsigned_bin_len(key->n);
+}
+
+
+/**
+ * crypto_rsa_exptmod - RSA modular exponentiation
+ * @in: Input data
+ * @inlen: Input data length
+ * @out: Buffer for output data
+ * @outlen: Maximum size of the output buffer and used size on success
+ * @key: RSA key
+ * @use_private: 1 = Use RSA private key, 0 = Use RSA public key
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen,
+		       struct crypto_rsa_key *key, int use_private)
+{
+	struct bignum *tmp, *a = NULL, *b = NULL;
+	int ret = -1;
+	size_t modlen;
+
+	if (use_private && !key->private_key)
+		return -1;
+
+	tmp = bignum_init();
+	if (tmp == NULL)
+		return -1;
+
+	if (bignum_set_unsigned_bin(tmp, in, inlen) < 0)
+		goto error;
+	if (bignum_cmp(key->n, tmp) < 0) {
+		/* Too large input value for the RSA key modulus */
+		goto error;
+	}
+
+	if (use_private) {
+		/*
+		 * Decrypt (or sign) using Chinese remainer theorem to speed
+		 * up calculation. This is equivalent to tmp = tmp^d mod n
+		 * (which would require more CPU to calculate directly).
+		 *
+		 * dmp1 = (1/e) mod (p-1)
+		 * dmq1 = (1/e) mod (q-1)
+		 * iqmp = (1/q) mod p, where p > q
+		 * m1 = c^dmp1 mod p
+		 * m2 = c^dmq1 mod q
+		 * h = q^-1 (m1 - m2) mod p
+		 * m = m2 + hq
+		 */
+		a = bignum_init();
+		b = bignum_init();
+		if (a == NULL || b == NULL)
+			goto error;
+
+		/* a = tmp^dmp1 mod p */
+		if (bignum_exptmod(tmp, key->dmp1, key->p, a) < 0)
+			goto error;
+
+		/* b = tmp^dmq1 mod q */
+		if (bignum_exptmod(tmp, key->dmq1, key->q, b) < 0)
+			goto error;
+
+		/* tmp = (a - b) * (1/q mod p) (mod p) */
+		if (bignum_sub(a, b, tmp) < 0 ||
+		    bignum_mulmod(tmp, key->iqmp, key->p, tmp) < 0)
+			goto error;
+
+		/* tmp = b + q * tmp */
+		if (bignum_mul(tmp, key->q, tmp) < 0 ||
+		    bignum_add(tmp, b, tmp) < 0)
+			goto error;
+	} else {
+		/* Encrypt (or verify signature) */
+		/* tmp = tmp^e mod N */
+		if (bignum_exptmod(tmp, key->e, key->n, tmp) < 0)
+			goto error;
+	}
+
+	modlen = crypto_rsa_get_modulus_len(key);
+	if (modlen > *outlen) {
+		*outlen = modlen;
+		goto error;
+	}
+
+	if (bignum_get_unsigned_bin_len(tmp) > modlen)
+		goto error; /* should never happen */
+
+	*outlen = modlen;
+	os_memset(out, 0, modlen);
+	if (bignum_get_unsigned_bin(
+		    tmp, out +
+		    (modlen - bignum_get_unsigned_bin_len(tmp)), NULL) < 0)
+		goto error;
+
+	ret = 0;
+
+error:
+	bignum_deinit(tmp);
+	bignum_deinit(a);
+	bignum_deinit(b);
+	return ret;
+}
+
+
+/**
+ * crypto_rsa_free - Free RSA key
+ * @key: RSA key to be freed
+ *
+ * This function frees an RSA key imported with either
+ * crypto_rsa_import_public_key() or crypto_rsa_import_private_key().
+ */
+void crypto_rsa_free(struct crypto_rsa_key *key)
+{
+	if (key) {
+		bignum_deinit(key->n);
+		bignum_deinit(key->e);
+		bignum_deinit(key->d);
+		bignum_deinit(key->p);
+		bignum_deinit(key->q);
+		bignum_deinit(key->dmp1);
+		bignum_deinit(key->dmq1);
+		bignum_deinit(key->iqmp);
+		os_free(key);
+	}
+}
diff --git a/hostap/src/tls/rsa.h b/hostap/src/tls/rsa.h
new file mode 100644
index 0000000..b65818e
--- /dev/null
+++ b/hostap/src/tls/rsa.h
@@ -0,0 +1,26 @@
+/*
+ * RSA
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RSA_H
+#define RSA_H
+
+struct crypto_rsa_key;
+
+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,
+		       struct crypto_rsa_key *key, int use_private);
+void crypto_rsa_free(struct crypto_rsa_key *key);
+
+#endif /* RSA_H */
diff --git a/hostap/src/tls/tlsv1_client.c b/hostap/src/tls/tlsv1_client.c
new file mode 100644
index 0000000..a6f0587
--- /dev/null
+++ b/hostap/src/tls/tlsv1_client.c
@@ -0,0 +1,830 @@
+/*
+ * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_client.h"
+#include "tlsv1_client_i.h"
+
+/* TODO:
+ * Support for a message fragmented across several records (RFC 2246, 6.2.1)
+ */
+
+
+void tls_alert(struct tlsv1_client *conn, u8 level, u8 description)
+{
+	conn->alert_level = level;
+	conn->alert_description = description;
+}
+
+
+void tlsv1_client_free_dh(struct tlsv1_client *conn)
+{
+	os_free(conn->dh_p);
+	os_free(conn->dh_g);
+	os_free(conn->dh_ys);
+	conn->dh_p = conn->dh_g = conn->dh_ys = NULL;
+}
+
+
+int tls_derive_pre_master_secret(u8 *pre_master_secret)
+{
+	WPA_PUT_BE16(pre_master_secret, TLS_VERSION);
+	if (os_get_random(pre_master_secret + 2,
+			  TLS_PRE_MASTER_SECRET_LEN - 2))
+		return -1;
+	return 0;
+}
+
+
+int tls_derive_keys(struct tlsv1_client *conn,
+		    const u8 *pre_master_secret, size_t pre_master_secret_len)
+{
+	u8 seed[2 * TLS_RANDOM_LEN];
+	u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
+	u8 *pos;
+	size_t key_block_len;
+
+	if (pre_master_secret) {
+		wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
+				pre_master_secret, pre_master_secret_len);
+		os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+		os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+			  TLS_RANDOM_LEN);
+		if (tls_prf(conn->rl.tls_version,
+			    pre_master_secret, pre_master_secret_len,
+			    "master secret", seed, 2 * TLS_RANDOM_LEN,
+			    conn->master_secret, TLS_MASTER_SECRET_LEN)) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
+				   "master_secret");
+			return -1;
+		}
+		wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
+				conn->master_secret, TLS_MASTER_SECRET_LEN);
+	}
+
+	os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+	os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
+	key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len);
+	if (conn->rl.tls_version == TLS_VERSION_1)
+		key_block_len += 2 * conn->rl.iv_size;
+	if (tls_prf(conn->rl.tls_version,
+		    conn->master_secret, TLS_MASTER_SECRET_LEN,
+		    "key expansion", seed, 2 * TLS_RANDOM_LEN,
+		    key_block, key_block_len)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
+			key_block, key_block_len);
+
+	pos = key_block;
+
+	/* client_write_MAC_secret */
+	os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
+	pos += conn->rl.hash_size;
+	/* server_write_MAC_secret */
+	os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
+	pos += conn->rl.hash_size;
+
+	/* client_write_key */
+	os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
+	pos += conn->rl.key_material_len;
+	/* server_write_key */
+	os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
+	pos += conn->rl.key_material_len;
+
+	if (conn->rl.tls_version == TLS_VERSION_1) {
+		/* client_write_IV */
+		os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
+		pos += conn->rl.iv_size;
+		/* server_write_IV */
+		os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
+		pos += conn->rl.iv_size;
+	} else {
+		/*
+		 * Use IV field to set the mask value for TLS v1.1. A fixed
+		 * mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block
+		 * Cipher option 2a.
+		 */
+		os_memset(conn->rl.write_iv, 0, conn->rl.iv_size);
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_client_handshake - Process TLS handshake
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Input data from TLS peer
+ * @in_len: Input data length
+ * @out_len: Length of the output buffer.
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * @appl_data_len: Pointer to variable that is set to appl_data length
+ * @need_more_data: Set to 1 if more data would be needed to complete
+ *	processing
+ * Returns: Pointer to output data, %NULL on failure
+ */
+u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
+			    const u8 *in_data, size_t in_len,
+			    size_t *out_len, u8 **appl_data,
+			    size_t *appl_data_len, int *need_more_data)
+{
+	const u8 *pos, *end;
+	u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct;
+	size_t in_msg_len;
+	int no_appl_data;
+	int used;
+
+	if (need_more_data)
+		*need_more_data = 0;
+
+	if (conn->state == CLIENT_HELLO) {
+		if (in_len)
+			return NULL;
+		return tls_send_client_hello(conn, out_len);
+	}
+
+	if (conn->partial_input) {
+		if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+				   "memory for pending record");
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_INTERNAL_ERROR);
+			goto failed;
+		}
+		wpabuf_put_data(conn->partial_input, in_data, in_len);
+		in_data = wpabuf_head(conn->partial_input);
+		in_len = wpabuf_len(conn->partial_input);
+	}
+
+	if (in_data == NULL || in_len == 0)
+		return NULL;
+
+	pos = in_data;
+	end = in_data + in_len;
+	in_msg = os_malloc(in_len);
+	if (in_msg == NULL)
+		return NULL;
+
+	/* Each received packet may include multiple records */
+	while (pos < end) {
+		in_msg_len = in_len;
+		used = tlsv1_record_receive(&conn->rl, pos, end - pos,
+					    in_msg, &in_msg_len, &alert);
+		if (used < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
+				   "record failed");
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+			goto failed;
+		}
+		if (used == 0) {
+			struct wpabuf *partial;
+			wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
+			partial = wpabuf_alloc_copy(pos, end - pos);
+			wpabuf_free(conn->partial_input);
+			conn->partial_input = partial;
+			if (conn->partial_input == NULL) {
+				wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+					   "allocate memory for pending "
+					   "record");
+				tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					  TLS_ALERT_INTERNAL_ERROR);
+				goto failed;
+			}
+			os_free(in_msg);
+			if (need_more_data)
+				*need_more_data = 1;
+			return NULL;
+		}
+		ct = pos[0];
+
+		in_pos = in_msg;
+		in_end = in_msg + in_msg_len;
+
+		/* Each received record may include multiple messages of the
+		 * same ContentType. */
+		while (in_pos < in_end) {
+			in_msg_len = in_end - in_pos;
+			if (tlsv1_client_process_handshake(conn, ct, in_pos,
+							   &in_msg_len,
+							   appl_data,
+							   appl_data_len) < 0)
+				goto failed;
+			in_pos += in_msg_len;
+		}
+
+		pos += used;
+	}
+
+	os_free(in_msg);
+	in_msg = NULL;
+
+	no_appl_data = appl_data == NULL || *appl_data == NULL;
+	msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data);
+
+failed:
+	os_free(in_msg);
+	if (conn->alert_level) {
+		wpabuf_free(conn->partial_input);
+		conn->partial_input = NULL;
+		conn->state = FAILED;
+		os_free(msg);
+		msg = tlsv1_client_send_alert(conn, conn->alert_level,
+					      conn->alert_description,
+					      out_len);
+	} else if (msg == NULL) {
+		msg = os_zalloc(1);
+		*out_len = 0;
+	}
+
+	if (need_more_data == NULL || !(*need_more_data)) {
+		wpabuf_free(conn->partial_input);
+		conn->partial_input = NULL;
+	}
+
+	return msg;
+}
+
+
+/**
+ * tlsv1_client_encrypt - Encrypt data into TLS tunnel
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Pointer to plaintext data to be encrypted
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (encrypted TLS data)
+ * @out_len: Maximum out_data length 
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel.
+ */
+int tlsv1_client_encrypt(struct tlsv1_client *conn,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t out_len)
+{
+	size_t rlen;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
+			in_data, in_len);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
+			      out_data, out_len, in_data, in_len, &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	return rlen;
+}
+
+
+/**
+ * tlsv1_client_decrypt - Decrypt data from TLS tunnel
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Pointer to input buffer (encrypted TLS data)
+ * @in_len: Input buffer length
+ * @need_more_data: Set to 1 if more data would be needed to complete
+ *	processing
+ * Returns: Decrypted data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel.
+ */
+struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
+				     const u8 *in_data, size_t in_len,
+				     int *need_more_data)
+{
+	const u8 *in_end, *pos;
+	int used;
+	u8 alert, *out_pos, ct;
+	size_t olen;
+	struct wpabuf *buf = NULL;
+
+	if (need_more_data)
+		*need_more_data = 0;
+
+	if (conn->partial_input) {
+		if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+				   "memory for pending record");
+			alert = TLS_ALERT_INTERNAL_ERROR;
+			goto fail;
+		}
+		wpabuf_put_data(conn->partial_input, in_data, in_len);
+		in_data = wpabuf_head(conn->partial_input);
+		in_len = wpabuf_len(conn->partial_input);
+	}
+
+	pos = in_data;
+	in_end = in_data + in_len;
+
+	while (pos < in_end) {
+		ct = pos[0];
+		if (wpabuf_resize(&buf, in_end - pos) < 0) {
+			alert = TLS_ALERT_INTERNAL_ERROR;
+			goto fail;
+		}
+		out_pos = wpabuf_put(buf, 0);
+		olen = wpabuf_tailroom(buf);
+		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");
+			goto fail;
+		}
+		if (used == 0) {
+			struct wpabuf *partial;
+			wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
+			partial = wpabuf_alloc_copy(pos, in_end - pos);
+			wpabuf_free(conn->partial_input);
+			conn->partial_input = partial;
+			if (conn->partial_input == NULL) {
+				wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+					   "allocate memory for pending "
+					   "record");
+				alert = TLS_ALERT_INTERNAL_ERROR;
+				goto fail;
+			}
+			if (need_more_data)
+				*need_more_data = 1;
+			return buf;
+		}
+
+		if (ct == TLS_CONTENT_TYPE_ALERT) {
+			if (olen < 2) {
+				wpa_printf(MSG_DEBUG, "TLSv1: Alert "
+					   "underflow");
+				alert = TLS_ALERT_DECODE_ERROR;
+				goto fail;
+			}
+			wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+				   out_pos[0], out_pos[1]);
+			if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
+				/* Continue processing */
+				pos += used;
+				continue;
+			}
+
+			alert = out_pos[1];
+			goto fail;
+		}
+
+		if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
+				   "0x%x when decrypting application data",
+				   pos[0]);
+			alert = TLS_ALERT_UNEXPECTED_MESSAGE;
+			goto fail;
+		}
+
+		wpabuf_put(buf, olen);
+
+		pos += used;
+	}
+
+	wpabuf_free(conn->partial_input);
+	conn->partial_input = NULL;
+	return buf;
+
+fail:
+	wpabuf_free(buf);
+	wpabuf_free(conn->partial_input);
+	conn->partial_input = NULL;
+	tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+	return NULL;
+}
+
+
+/**
+ * tlsv1_client_global_init - Initialize TLSv1 client
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before using any other TLSv1 client functions.
+ */
+int tlsv1_client_global_init(void)
+{
+	return crypto_global_init();
+}
+
+
+/**
+ * tlsv1_client_global_deinit - Deinitialize TLSv1 client
+ *
+ * This function can be used to deinitialize the TLSv1 client that was
+ * initialized by calling tlsv1_client_global_init(). No TLSv1 client functions
+ * can be called after this before calling tlsv1_client_global_init() again.
+ */
+void tlsv1_client_global_deinit(void)
+{
+	crypto_global_deinit();
+}
+
+
+/**
+ * tlsv1_client_init - Initialize TLSv1 client connection
+ * Returns: Pointer to TLSv1 client connection data or %NULL on failure
+ */
+struct tlsv1_client * tlsv1_client_init(void)
+{
+	struct tlsv1_client *conn;
+	size_t count;
+	u16 *suites;
+
+	conn = os_zalloc(sizeof(*conn));
+	if (conn == NULL)
+		return NULL;
+
+	conn->state = CLIENT_HELLO;
+
+	if (tls_verify_hash_init(&conn->verify) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
+			   "hash");
+		os_free(conn);
+		return NULL;
+	}
+
+	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;
+	conn->num_cipher_suites = count;
+
+	conn->rl.tls_version = TLS_VERSION;
+
+	return conn;
+}
+
+
+/**
+ * tlsv1_client_deinit - Deinitialize TLSv1 client connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ */
+void tlsv1_client_deinit(struct tlsv1_client *conn)
+{
+	crypto_public_key_free(conn->server_rsa_key);
+	tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+	tlsv1_record_change_write_cipher(&conn->rl);
+	tlsv1_record_change_read_cipher(&conn->rl);
+	tls_verify_hash_free(&conn->verify);
+	os_free(conn->client_hello_ext);
+	tlsv1_client_free_dh(conn);
+	tlsv1_cred_free(conn->cred);
+	wpabuf_free(conn->partial_input);
+	os_free(conn);
+}
+
+
+/**
+ * tlsv1_client_established - Check whether connection has been established
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 1 if connection is established, 0 if not
+ */
+int tlsv1_client_established(struct tlsv1_client *conn)
+{
+	return conn->state == ESTABLISHED;
+}
+
+
+/**
+ * tlsv1_client_prf - Use TLS-PRF to derive keying material
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @label: Label (e.g., description of the key) for PRF
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+		     int server_random_first, u8 *out, size_t out_len)
+{
+	u8 seed[2 * TLS_RANDOM_LEN];
+
+	if (conn->state != ESTABLISHED)
+		return -1;
+
+	if (server_random_first) {
+		os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+		os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
+			  TLS_RANDOM_LEN);
+	} else {
+		os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+		os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+			  TLS_RANDOM_LEN);
+	}
+
+	return tls_prf(conn->rl.tls_version,
+		       conn->master_secret, TLS_MASTER_SECRET_LEN,
+		       label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
+}
+
+
+/**
+ * tlsv1_client_get_cipher - Get current cipher name
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
+			    size_t buflen)
+{
+	char *cipher;
+
+	switch (conn->rl.cipher_suite) {
+	case TLS_RSA_WITH_RC4_128_MD5:
+		cipher = "RC4-MD5";
+		break;
+	case TLS_RSA_WITH_RC4_128_SHA:
+		cipher = "RC4-SHA";
+		break;
+	case TLS_RSA_WITH_DES_CBC_SHA:
+		cipher = "DES-CBC-SHA";
+		break;
+	case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+		cipher = "DES-CBC3-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_DES_CBC_SHA:
+		cipher = "DHE-RSA-DES-CBC-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+		cipher = "DHE-RSA-DES-CBC3-SHA";
+		break;
+	case TLS_DH_anon_WITH_RC4_128_MD5:
+		cipher = "ADH-RC4-MD5";
+		break;
+	case TLS_DH_anon_WITH_DES_CBC_SHA:
+		cipher = "ADH-DES-SHA";
+		break;
+	case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+		cipher = "ADH-DES-CBC3-SHA";
+		break;
+	case TLS_RSA_WITH_AES_128_CBC_SHA:
+		cipher = "AES-128-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+		cipher = "DHE-RSA-AES-128-SHA";
+		break;
+	case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+		cipher = "ADH-AES-128-SHA";
+		break;
+	case TLS_RSA_WITH_AES_256_CBC_SHA:
+		cipher = "AES-256-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+		cipher = "DHE-RSA-AES-256-SHA";
+		break;
+	case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+		cipher = "ADH-AES-256-SHA";
+		break;
+	case TLS_RSA_WITH_AES_128_CBC_SHA256:
+		cipher = "AES-128-SHA256";
+		break;
+	case TLS_RSA_WITH_AES_256_CBC_SHA256:
+		cipher = "AES-256-SHA256";
+		break;
+	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+		cipher = "DHE-RSA-AES-128-SHA256";
+		break;
+	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+		cipher = "DHE-RSA-AES-256-SHA256";
+		break;
+	case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+		cipher = "ADH-AES-128-SHA256";
+		break;
+	case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+		cipher = "ADH-AES-256-SHA256";
+		break;
+	default:
+		return -1;
+	}
+
+	if (os_strlcpy(buf, cipher, buflen) >= buflen)
+		return -1;
+	return 0;
+}
+
+
+/**
+ * tlsv1_client_shutdown - Shutdown TLS connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_shutdown(struct tlsv1_client *conn)
+{
+	conn->state = CLIENT_HELLO;
+
+	if (tls_verify_hash_init(&conn->verify) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
+			   "hash");
+		return -1;
+	}
+
+	tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+	tlsv1_record_change_write_cipher(&conn->rl);
+	tlsv1_record_change_read_cipher(&conn->rl);
+
+	conn->certificate_requested = 0;
+	crypto_public_key_free(conn->server_rsa_key);
+	conn->server_rsa_key = NULL;
+	conn->session_resumed = 0;
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_client_resumed - Was session resumption used
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tlsv1_client_resumed(struct tlsv1_client *conn)
+{
+	return !!conn->session_resumed;
+}
+
+
+/**
+ * tlsv1_client_hello_ext - Set TLS extension for ClientHello
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @ext_type: Extension type
+ * @data: Extension payload (%NULL to remove extension)
+ * @data_len: Extension payload length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
+			   const u8 *data, size_t data_len)
+{
+	u8 *pos;
+
+	conn->session_ticket_included = 0;
+	os_free(conn->client_hello_ext);
+	conn->client_hello_ext = NULL;
+	conn->client_hello_ext_len = 0;
+
+	if (data == NULL || data_len == 0)
+		return 0;
+
+	pos = conn->client_hello_ext = os_malloc(6 + data_len);
+	if (pos == NULL)
+		return -1;
+
+	WPA_PUT_BE16(pos, 4 + data_len);
+	pos += 2;
+	WPA_PUT_BE16(pos, ext_type);
+	pos += 2;
+	WPA_PUT_BE16(pos, data_len);
+	pos += 2;
+	os_memcpy(pos, data, data_len);
+	conn->client_hello_ext_len = 6 + data_len;
+
+	if (ext_type == TLS_EXT_PAC_OPAQUE) {
+		conn->session_ticket_included = 1;
+		wpa_printf(MSG_DEBUG, "TLSv1: Using session ticket");
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_client_get_random - Get random data from TLS connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @keys: Structure of random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *keys)
+{
+	os_memset(keys, 0, sizeof(*keys));
+	if (conn->state == CLIENT_HELLO)
+		return -1;
+
+	keys->client_random = conn->client_random;
+	keys->client_random_len = TLS_RANDOM_LEN;
+
+	if (conn->state != SERVER_HELLO) {
+		keys->server_random = conn->server_random;
+		keys->server_random_len = TLS_RANDOM_LEN;
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_client_get_keyblock_size - Get TLS key_block size
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: Size of the key_block for the negotiated cipher suite or -1 on
+ * failure
+ */
+int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn)
+{
+	if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
+		return -1;
+
+	return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+		    conn->rl.iv_size);
+}
+
+
+/**
+ * tlsv1_client_set_cipher_list - Configure acceptable cipher suites
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers)
+{
+	size_t count;
+	u16 *suites;
+
+	/* TODO: implement proper configuration of cipher suites */
+	if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
+		count = 0;
+		suites = conn->cipher_suites;
+		suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256;
+		suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
+		suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256;
+		suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+		suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
+		suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
+		suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
+
+		/*
+		 * Cisco AP (at least 350 and 1200 series) local authentication
+		 * server does not know how to search cipher suites from the
+		 * list and seem to require that the last entry in the list is
+		 * the one that it wants to use. However, TLS specification
+		 * requires the list to be in the client preference order. As a
+		 * workaround, add anon-DH AES-128-SHA1 again at the end of the
+		 * list to allow the Cisco code to find it.
+		 */
+		suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+		conn->num_cipher_suites = count;
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_client_set_cred - Set client credentials
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @cred: Credentials from tlsv1_cred_alloc()
+ * Returns: 0 on success, -1 on failure
+ *
+ * On success, the client takes ownership of the credentials block and caller
+ * must not free it. On failure, caller is responsible for freeing the
+ * credential block.
+ */
+int tlsv1_client_set_cred(struct tlsv1_client *conn,
+			  struct tlsv1_credentials *cred)
+{
+	tlsv1_cred_free(conn->cred);
+	conn->cred = cred;
+	return 0;
+}
+
+
+void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled)
+{
+	conn->disable_time_checks = !enabled;
+}
+
+
+void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
+					tlsv1_client_session_ticket_cb cb,
+					void *ctx)
+{
+	wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
+		   cb, ctx);
+	conn->session_ticket_cb = cb;
+	conn->session_ticket_cb_ctx = ctx;
+}
diff --git a/hostap/src/tls/tlsv1_client.h b/hostap/src/tls/tlsv1_client.h
new file mode 100644
index 0000000..a4e25e9
--- /dev/null
+++ b/hostap/src/tls/tlsv1_client.h
@@ -0,0 +1,54 @@
+/*
+ * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLSV1_CLIENT_H
+#define TLSV1_CLIENT_H
+
+#include "tlsv1_cred.h"
+
+struct tlsv1_client;
+
+int tlsv1_client_global_init(void);
+void tlsv1_client_global_deinit(void);
+struct tlsv1_client * tlsv1_client_init(void);
+void tlsv1_client_deinit(struct tlsv1_client *conn);
+int tlsv1_client_established(struct tlsv1_client *conn);
+int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+		     int server_random_first, u8 *out, size_t out_len);
+u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
+			    const u8 *in_data, size_t in_len,
+			    size_t *out_len, u8 **appl_data,
+			    size_t *appl_data_len, int *need_more_data);
+int tlsv1_client_encrypt(struct tlsv1_client *conn,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t out_len);
+struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
+				     const u8 *in_data, size_t in_len,
+				     int *need_more_data);
+int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
+			    size_t buflen);
+int tlsv1_client_shutdown(struct tlsv1_client *conn);
+int tlsv1_client_resumed(struct tlsv1_client *conn);
+int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
+			   const u8 *data, size_t data_len);
+int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *data);
+int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn);
+int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers);
+int tlsv1_client_set_cred(struct tlsv1_client *conn,
+			  struct tlsv1_credentials *cred);
+void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled);
+
+typedef int (*tlsv1_client_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
+					tlsv1_client_session_ticket_cb cb,
+					void *ctx);
+
+#endif /* TLSV1_CLIENT_H */
diff --git a/hostap/src/tls/tlsv1_client_i.h b/hostap/src/tls/tlsv1_client_i.h
new file mode 100644
index 0000000..55fdcf8
--- /dev/null
+++ b/hostap/src/tls/tlsv1_client_i.h
@@ -0,0 +1,84 @@
+/*
+ * TLSv1 client - internal structures
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLSV1_CLIENT_I_H
+#define TLSV1_CLIENT_I_H
+
+struct tlsv1_client {
+	enum {
+		CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE,
+		SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST,
+		SERVER_HELLO_DONE, CLIENT_KEY_EXCHANGE, CHANGE_CIPHER_SPEC,
+		SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, ACK_FINISHED,
+		ESTABLISHED, FAILED
+	} state;
+
+	struct tlsv1_record_layer rl;
+
+	u8 session_id[TLS_SESSION_ID_MAX_LEN];
+	size_t session_id_len;
+	u8 client_random[TLS_RANDOM_LEN];
+	u8 server_random[TLS_RANDOM_LEN];
+	u8 master_secret[TLS_MASTER_SECRET_LEN];
+
+	u8 alert_level;
+	u8 alert_description;
+
+	unsigned int certificate_requested:1;
+	unsigned int session_resumed:1;
+	unsigned int session_ticket_included:1;
+	unsigned int use_session_ticket:1;
+	unsigned int disable_time_checks:1;
+
+	struct crypto_public_key *server_rsa_key;
+
+	struct tls_verify_hash verify;
+
+#define MAX_CIPHER_COUNT 30
+	u16 cipher_suites[MAX_CIPHER_COUNT];
+	size_t num_cipher_suites;
+
+	u16 prev_cipher_suite;
+
+	u8 *client_hello_ext;
+	size_t client_hello_ext_len;
+
+	/* The prime modulus used for Diffie-Hellman */
+	u8 *dh_p;
+	size_t dh_p_len;
+	/* The generator used for Diffie-Hellman */
+	u8 *dh_g;
+	size_t dh_g_len;
+	/* The server's Diffie-Hellman public value */
+	u8 *dh_ys;
+	size_t dh_ys_len;
+
+	struct tlsv1_credentials *cred;
+
+	tlsv1_client_session_ticket_cb session_ticket_cb;
+	void *session_ticket_cb_ctx;
+
+	struct wpabuf *partial_input;
+};
+
+
+void tls_alert(struct tlsv1_client *conn, u8 level, u8 description);
+void tlsv1_client_free_dh(struct tlsv1_client *conn);
+int tls_derive_pre_master_secret(u8 *pre_master_secret);
+int tls_derive_keys(struct tlsv1_client *conn,
+		    const u8 *pre_master_secret, size_t pre_master_secret_len);
+u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len);
+u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level,
+			     u8 description, size_t *out_len);
+u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len,
+				  int no_appl_data);
+int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct,
+				   const u8 *buf, size_t *len,
+				   u8 **out_data, size_t *out_len);
+
+#endif /* TLSV1_CLIENT_I_H */
diff --git a/hostap/src/tls/tlsv1_client_read.c b/hostap/src/tls/tlsv1_client_read.c
new file mode 100644
index 0000000..9ce9680
--- /dev/null
+++ b/hostap/src/tls/tlsv1_client_read.c
@@ -0,0 +1,1095 @@
+/*
+ * TLSv1 client - read handshake message
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/tls.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_client.h"
+#include "tlsv1_client_i.h"
+
+static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len);
+static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len);
+static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
+					 const u8 *in_data, size_t *in_len);
+
+
+static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
+				    const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len, i;
+	u16 cipher_suite;
+	u16 tls_version;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+			   "received content type 0x%x", ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4)
+		goto decode_error;
+
+	/* HandshakeType msg_type */
+	if (*pos != TLS_HANDSHAKE_TYPE_SERVER_HELLO) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+			   "message %d (expected ServerHello)", *pos);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHello");
+	pos++;
+	/* uint24 length */
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left)
+		goto decode_error;
+
+	/* body - ServerHello */
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello", pos, len);
+	end = pos + len;
+
+	/* ProtocolVersion server_version */
+	if (end - pos < 2)
+		goto decode_error;
+	tls_version = WPA_GET_BE16(pos);
+	if (!tls_version_ok(tls_version)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
+			   "ServerHello %u.%u", pos[0], pos[1]);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_PROTOCOL_VERSION);
+		return -1;
+	}
+	pos += 2;
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
+		   tls_version_str(tls_version));
+	conn->rl.tls_version = tls_version;
+
+	/* Random random */
+	if (end - pos < TLS_RANDOM_LEN)
+		goto decode_error;
+
+	os_memcpy(conn->server_random, pos, TLS_RANDOM_LEN);
+	pos += TLS_RANDOM_LEN;
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
+		    conn->server_random, TLS_RANDOM_LEN);
+
+	/* SessionID session_id */
+	if (end - pos < 1)
+		goto decode_error;
+	if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN)
+		goto decode_error;
+	if (conn->session_id_len && conn->session_id_len == *pos &&
+	    os_memcmp(conn->session_id, pos + 1, conn->session_id_len) == 0) {
+		pos += 1 + conn->session_id_len;
+		wpa_printf(MSG_DEBUG, "TLSv1: Resuming old session");
+		conn->session_resumed = 1;
+	} else {
+		conn->session_id_len = *pos;
+		pos++;
+		os_memcpy(conn->session_id, pos, conn->session_id_len);
+		pos += conn->session_id_len;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
+		    conn->session_id, conn->session_id_len);
+
+	/* CipherSuite cipher_suite */
+	if (end - pos < 2)
+		goto decode_error;
+	cipher_suite = WPA_GET_BE16(pos);
+	pos += 2;
+	for (i = 0; i < conn->num_cipher_suites; i++) {
+		if (cipher_suite == conn->cipher_suites[i])
+			break;
+	}
+	if (i == conn->num_cipher_suites) {
+		wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
+			   "cipher suite 0x%04x", cipher_suite);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_ILLEGAL_PARAMETER);
+		return -1;
+	}
+
+	if (conn->session_resumed && cipher_suite != conn->prev_cipher_suite) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Server selected a different "
+			   "cipher suite for a resumed connection (0x%04x != "
+			   "0x%04x)", cipher_suite, conn->prev_cipher_suite);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_ILLEGAL_PARAMETER);
+		return -1;
+	}
+
+	if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for "
+			   "record layer");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	conn->prev_cipher_suite = cipher_suite;
+
+	/* CompressionMethod compression_method */
+	if (end - pos < 1)
+		goto decode_error;
+	if (*pos != TLS_COMPRESSION_NULL) {
+		wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
+			   "compression 0x%02x", *pos);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_ILLEGAL_PARAMETER);
+		return -1;
+	}
+	pos++;
+
+	if (end != pos) {
+		/* TODO: ServerHello extensions */
+		wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the "
+			    "end of ServerHello", pos, end - pos);
+		goto decode_error;
+	}
+
+	if (conn->session_ticket_included && conn->session_ticket_cb) {
+		/* TODO: include SessionTicket extension if one was included in
+		 * ServerHello */
+		int res = conn->session_ticket_cb(
+			conn->session_ticket_cb_ctx, NULL, 0,
+			conn->client_random, conn->server_random,
+			conn->master_secret);
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
+				   "indicated failure");
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_HANDSHAKE_FAILURE);
+			return -1;
+		}
+		conn->use_session_ticket = !!res;
+	}
+
+	if ((conn->session_resumed || conn->use_session_ticket) &&
+	    tls_derive_keys(conn, NULL, 0)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	*in_len = end - in_data;
+
+	conn->state = (conn->session_resumed || conn->use_session_ticket) ?
+		SERVER_CHANGE_CIPHER_SPEC : SERVER_CERTIFICATE;
+
+	return 0;
+
+decode_error:
+	wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ServerHello");
+	tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+	return -1;
+}
+
+
+static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
+				   const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len, list_len, cert_len, idx;
+	u8 type;
+	struct x509_certificate *chain = NULL, *last = NULL, *cert;
+	int reason;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+			   "received content type 0x%x", ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
+			   "(len=%lu)", (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message "
+			   "length (len=%lu != left=%lu)",
+			   (unsigned long) len, (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	if (type == TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE)
+		return tls_process_server_key_exchange(conn, ct, in_data,
+						       in_len);
+	if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
+		return tls_process_certificate_request(conn, ct, in_data,
+						       in_len);
+	if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+		return tls_process_server_hello_done(conn, ct, in_data,
+						     in_len);
+	if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+			   "message %d (expected Certificate/"
+			   "ServerKeyExchange/CertificateRequest/"
+			   "ServerHelloDone)", type);
+		tls_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);
+
+	/*
+	 * opaque ASN.1Cert<2^24-1>;
+	 *
+	 * struct {
+	 *     ASN.1Cert certificate_list<1..2^24-1>;
+	 * } Certificate;
+	 */
+
+	end = pos + len;
+
+	if (end - pos < 3) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
+			   "(left=%lu)", (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	list_len = WPA_GET_BE24(pos);
+	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));
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	idx = 0;
+	while (pos < end) {
+		if (end - pos < 3) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+				   "certificate_list");
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_DECODE_ERROR);
+			x509_certificate_chain_free(chain);
+			return -1;
+		}
+
+		cert_len = WPA_GET_BE24(pos);
+		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));
+			tls_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);
+
+		if (idx == 0) {
+			crypto_public_key_free(conn->server_rsa_key);
+			if (tls_parse_cert(pos, cert_len,
+					   &conn->server_rsa_key)) {
+				wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+					   "the certificate");
+				tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					  TLS_ALERT_BAD_CERTIFICATE);
+				x509_certificate_chain_free(chain);
+				return -1;
+			}
+		}
+
+		cert = x509_certificate_parse(pos, cert_len);
+		if (cert == NULL) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+				   "the certificate");
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_BAD_CERTIFICATE);
+			x509_certificate_chain_free(chain);
+			return -1;
+		}
+
+		if (last == NULL)
+			chain = cert;
+		else
+			last->next = cert;
+		last = cert;
+
+		idx++;
+		pos += cert_len;
+	}
+
+	if (conn->cred &&
+	    x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
+					    &reason, conn->disable_time_checks)
+	    < 0) {
+		int tls_reason;
+		wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
+			   "validation failed (reason=%d)", reason);
+		switch (reason) {
+		case X509_VALIDATE_BAD_CERTIFICATE:
+			tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+			break;
+		case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
+			tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
+			break;
+		case X509_VALIDATE_CERTIFICATE_REVOKED:
+			tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
+			break;
+		case X509_VALIDATE_CERTIFICATE_EXPIRED:
+			tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+			break;
+		case X509_VALIDATE_CERTIFICATE_UNKNOWN:
+			tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
+			break;
+		case X509_VALIDATE_UNKNOWN_CA:
+			tls_reason = TLS_ALERT_UNKNOWN_CA;
+			break;
+		default:
+			tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+			break;
+		}
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason);
+		x509_certificate_chain_free(chain);
+		return -1;
+	}
+
+	x509_certificate_chain_free(chain);
+
+	*in_len = end - in_data;
+
+	conn->state = SERVER_KEY_EXCHANGE;
+
+	return 0;
+}
+
+
+static unsigned int count_bits(const u8 *val, size_t len)
+{
+	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;
+	u16 val;
+
+	tlsv1_client_free_dh(conn);
+
+	pos = buf;
+	end = buf + len;
+
+	if (end - pos < 3)
+		goto fail;
+	server_params = pos;
+	val = WPA_GET_BE16(pos);
+	pos += 2;
+	if (val == 0 || val > (size_t) (end - pos)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %u", val);
+		goto fail;
+	}
+	conn->dh_p_len = val;
+	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;
+	os_memcpy(conn->dh_p, pos, conn->dh_p_len);
+	pos += conn->dh_p_len;
+	wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)",
+		    conn->dh_p, conn->dh_p_len);
+
+	if (end - pos < 3)
+		goto fail;
+	val = WPA_GET_BE16(pos);
+	pos += 2;
+	if (val == 0 || val > (size_t) (end - pos))
+		goto fail;
+	conn->dh_g_len = val;
+	conn->dh_g = os_malloc(conn->dh_g_len);
+	if (conn->dh_g == NULL)
+		goto fail;
+	os_memcpy(conn->dh_g, pos, conn->dh_g_len);
+	pos += conn->dh_g_len;
+	wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)",
+		    conn->dh_g, conn->dh_g_len);
+	if (conn->dh_g_len == 1 && conn->dh_g[0] < 2)
+		goto fail;
+
+	if (end - pos < 3)
+		goto fail;
+	val = WPA_GET_BE16(pos);
+	pos += 2;
+	if (val == 0 || val > (size_t) (end - pos))
+		goto fail;
+	conn->dh_ys_len = val;
+	conn->dh_ys = os_malloc(conn->dh_ys_len);
+	if (conn->dh_ys == NULL)
+		goto fail;
+	os_memcpy(conn->dh_ys, pos, conn->dh_ys_len);
+	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;
+
+fail:
+	wpa_printf(MSG_DEBUG, "TLSv1: Processing DH params failed");
+	tlsv1_client_free_dh(conn);
+	return -1;
+}
+
+
+static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len;
+	u8 type;
+	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);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerKeyExchange "
+			   "(Left=%lu)", (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerKeyExchange "
+			   "length (len=%lu != left=%lu)",
+			   (unsigned long) len, (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	end = pos + len;
+
+	if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
+		return tls_process_certificate_request(conn, ct, in_data,
+						       in_len);
+	if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+		return tls_process_server_hello_done(conn, ct, in_data,
+						     in_len);
+	if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+			   "message %d (expected ServerKeyExchange/"
+			   "CertificateRequest/ServerHelloDone)", type);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Received ServerKeyExchange");
+
+	if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not allowed "
+			   "with the selected cipher suite");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	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 ||
+		      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;
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "TLSv1: UnexpectedServerKeyExchange");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	*in_len = end - in_data;
+
+	conn->state = SERVER_CERTIFICATE_REQUEST;
+
+	return 0;
+}
+
+
+static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len;
+	u8 type;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+			   "received content type 0x%x", ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateRequest "
+			   "(left=%lu)", (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in CertificateRequest "
+			   "length (len=%lu != left=%lu)",
+			   (unsigned long) len, (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	end = pos + len;
+
+	if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+		return tls_process_server_hello_done(conn, ct, in_data,
+						     in_len);
+	if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+			   "message %d (expected CertificateRequest/"
+			   "ServerHelloDone)", type);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateRequest");
+
+	conn->certificate_requested = 1;
+
+	*in_len = end - in_data;
+
+	conn->state = SERVER_HELLO_DONE;
+
+	return 0;
+}
+
+
+static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
+					 const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len;
+	u8 type;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+			   "received content type 0x%x", ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerHelloDone "
+			   "(left=%lu)", (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerHelloDone "
+			   "length (len=%lu != left=%lu)",
+			   (unsigned long) len, (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+	end = pos + len;
+
+	if (type != TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+			   "message %d (expected ServerHelloDone)", type);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone");
+
+	*in_len = end - in_data;
+
+	conn->state = CLIENT_KEY_EXCHANGE;
+
+	return 0;
+}
+
+
+static int tls_process_server_change_cipher_spec(struct tlsv1_client *conn,
+						 u8 ct, const u8 *in_data,
+						 size_t *in_len)
+{
+	const u8 *pos;
+	size_t left;
+
+	if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+			   "received content type 0x%x", ct);
+		if (conn->use_session_ticket) {
+			int res;
+			wpa_printf(MSG_DEBUG, "TLSv1: Server may have "
+				   "rejected SessionTicket");
+			conn->use_session_ticket = 0;
+
+			/* Notify upper layers that SessionTicket failed */
+			res = conn->session_ticket_cb(
+				conn->session_ticket_cb_ctx, NULL, 0, NULL,
+				NULL, NULL);
+			if (res < 0) {
+				wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket "
+					   "callback indicated failure");
+				tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					  TLS_ALERT_HANDSHAKE_FAILURE);
+				return -1;
+			}
+
+			conn->state = SERVER_CERTIFICATE;
+			return tls_process_certificate(conn, ct, in_data,
+						       in_len);
+		}
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 1) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
+		tls_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);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec");
+	if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
+			   "for record layer");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	*in_len = pos + 1 - in_data;
+
+	conn->state = SERVER_FINISHED;
+
+	return 0;
+}
+
+
+static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
+				       const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len, hlen;
+	u8 verify_data[TLS_VERIFY_DATA_LEN];
+	u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
+			   "received content type 0x%x", ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
+			   "Finished",
+			   (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received "
+			   "type 0x%x", pos[0]);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	len = WPA_GET_BE24(pos + 1);
+
+	pos += 4;
+	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);
+		tls_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);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
+		    pos, TLS_VERIFY_DATA_LEN);
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+		hlen = SHA256_MAC_LEN;
+		if (conn->verify.sha256_server == NULL ||
+		    crypto_hash_finish(conn->verify.sha256_server, hash, &hlen)
+		    < 0) {
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_INTERNAL_ERROR);
+			conn->verify.sha256_server = NULL;
+			return -1;
+		}
+		conn->verify.sha256_server = NULL;
+	} else {
+#endif /* CONFIG_TLSV12 */
+
+	hlen = MD5_MAC_LEN;
+	if (conn->verify.md5_server == NULL ||
+	    crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		conn->verify.md5_server = NULL;
+		crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
+		conn->verify.sha1_server = NULL;
+		return -1;
+	}
+	conn->verify.md5_server = NULL;
+	hlen = SHA1_MAC_LEN;
+	if (conn->verify.sha1_server == NULL ||
+	    crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
+			       &hlen) < 0) {
+		conn->verify.sha1_server = NULL;
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	conn->verify.sha1_server = NULL;
+	hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+	}
+#endif /* CONFIG_TLSV12 */
+
+	if (tls_prf(conn->rl.tls_version,
+		    conn->master_secret, TLS_MASTER_SECRET_LEN,
+		    "server finished", hash, hlen,
+		    verify_data, TLS_VERIFY_DATA_LEN)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_DECRYPT_ERROR);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
+			verify_data, TLS_VERIFY_DATA_LEN);
+
+	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;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
+
+	*in_len = end - in_data;
+
+	conn->state = (conn->session_resumed || conn->use_session_ticket) ?
+		CHANGE_CIPHER_SPEC : ACK_FINISHED;
+
+	return 0;
+}
+
+
+static int tls_process_application_data(struct tlsv1_client *conn, u8 ct,
+					const u8 *in_data, size_t *in_len,
+					u8 **out_data, size_t *out_len)
+{
+	const u8 *pos;
+	size_t left;
+
+	if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Application Data; "
+			   "received content type 0x%x", ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	wpa_hexdump(MSG_DEBUG, "TLSv1: Application Data included in Handshake",
+		    pos, left);
+
+	*out_data = os_malloc(left);
+	if (*out_data) {
+		os_memcpy(*out_data, pos, left);
+		*out_len = left;
+	}
+
+	return 0;
+}
+
+
+int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct,
+				   const u8 *buf, size_t *len,
+				   u8 **out_data, size_t *out_len)
+{
+	if (ct == TLS_CONTENT_TYPE_ALERT) {
+		if (*len < 2) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
+			tls_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]);
+		*len = 2;
+		conn->state = FAILED;
+		return -1;
+	}
+
+	if (ct == TLS_CONTENT_TYPE_HANDSHAKE && *len >= 4 &&
+	    buf[0] == TLS_HANDSHAKE_TYPE_HELLO_REQUEST) {
+		size_t hr_len = WPA_GET_BE24(buf + 1);
+		if (hr_len > *len - 4) {
+			wpa_printf(MSG_DEBUG, "TLSv1: HelloRequest underflow");
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_DECODE_ERROR);
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "TLSv1: Ignored HelloRequest");
+		*len = 4 + hr_len;
+		return 0;
+	}
+
+	switch (conn->state) {
+	case SERVER_HELLO:
+		if (tls_process_server_hello(conn, ct, buf, len))
+			return -1;
+		break;
+	case SERVER_CERTIFICATE:
+		if (tls_process_certificate(conn, ct, buf, len))
+			return -1;
+		break;
+	case SERVER_KEY_EXCHANGE:
+		if (tls_process_server_key_exchange(conn, ct, buf, len))
+			return -1;
+		break;
+	case SERVER_CERTIFICATE_REQUEST:
+		if (tls_process_certificate_request(conn, ct, buf, len))
+			return -1;
+		break;
+	case SERVER_HELLO_DONE:
+		if (tls_process_server_hello_done(conn, ct, buf, len))
+			return -1;
+		break;
+	case SERVER_CHANGE_CIPHER_SPEC:
+		if (tls_process_server_change_cipher_spec(conn, ct, buf, len))
+			return -1;
+		break;
+	case SERVER_FINISHED:
+		if (tls_process_server_finished(conn, ct, buf, len))
+			return -1;
+		break;
+	case ACK_FINISHED:
+		if (out_data &&
+		    tls_process_application_data(conn, ct, buf, len, out_data,
+						 out_len))
+			return -1;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
+			   "while processing received message",
+			   conn->state);
+		return -1;
+	}
+
+	if (ct == TLS_CONTENT_TYPE_HANDSHAKE)
+		tls_verify_hash_add(&conn->verify, buf, *len);
+
+	return 0;
+}
diff --git a/hostap/src/tls/tlsv1_client_write.c b/hostap/src/tls/tlsv1_client_write.c
new file mode 100644
index 0000000..d192f44
--- /dev/null
+++ b/hostap/src/tls/tlsv1_client_write.c
@@ -0,0 +1,860 @@
+/*
+ * TLSv1 client - write handshake message
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/tls.h"
+#include "crypto/random.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_client.h"
+#include "tlsv1_client_i.h"
+
+
+static size_t tls_client_cert_chain_der_len(struct tlsv1_client *conn)
+{
+	size_t len = 0;
+	struct x509_certificate *cert;
+
+	if (conn->cred == NULL)
+		return 0;
+
+	cert = conn->cred->cert;
+	while (cert) {
+		len += 3 + cert->cert_len;
+		if (x509_certificate_self_signed(cert))
+			break;
+		cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+						    &cert->issuer);
+	}
+
+	return len;
+}
+
+
+u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len)
+{
+	u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr;
+	struct os_time now;
+	size_t len, i;
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello");
+	*out_len = 0;
+
+	os_get_time(&now);
+	WPA_PUT_BE32(conn->client_random, now.sec);
+	if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
+		wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+			   "client_random");
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
+		    conn->client_random, TLS_RANDOM_LEN);
+
+	len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len;
+	hello = os_malloc(len);
+	if (hello == NULL)
+		return NULL;
+	end = hello + len;
+
+	rhdr = hello;
+	pos = rhdr + TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CLIENT_HELLO;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+	/* body - ClientHello */
+	/* ProtocolVersion client_version */
+	WPA_PUT_BE16(pos, TLS_VERSION);
+	pos += 2;
+	/* Random random: uint32 gmt_unix_time, opaque random_bytes */
+	os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN);
+	pos += TLS_RANDOM_LEN;
+	/* SessionID session_id */
+	*pos++ = conn->session_id_len;
+	os_memcpy(pos, conn->session_id, conn->session_id_len);
+	pos += conn->session_id_len;
+	/* CipherSuite cipher_suites<2..2^16-1> */
+	WPA_PUT_BE16(pos, 2 * conn->num_cipher_suites);
+	pos += 2;
+	for (i = 0; i < conn->num_cipher_suites; i++) {
+		WPA_PUT_BE16(pos, conn->cipher_suites[i]);
+		pos += 2;
+	}
+	/* CompressionMethod compression_methods<1..2^8-1> */
+	*pos++ = 1;
+	*pos++ = TLS_COMPRESSION_NULL;
+
+	if (conn->client_hello_ext) {
+		os_memcpy(pos, conn->client_hello_ext,
+			  conn->client_hello_ext_len);
+		pos += conn->client_hello_ext_len;
+	}
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      out_len) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(hello);
+		return NULL;
+	}
+
+	conn->state = SERVER_HELLO;
+
+	return hello;
+}
+
+
+static int tls_write_client_certificate(struct tlsv1_client *conn,
+					u8 **msgpos, u8 *end)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
+	size_t rlen;
+	struct x509_certificate *cert;
+
+	pos = *msgpos;
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+	/* body - Certificate */
+	/* uint24 length (to be filled) */
+	cert_start = pos;
+	pos += 3;
+	cert = conn->cred ? conn->cred->cert : NULL;
+	while (cert) {
+		if (pos + 3 + cert->cert_len > end) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
+				   "for Certificate (cert_len=%lu left=%lu)",
+				   (unsigned long) cert->cert_len,
+				   (unsigned long) (end - pos));
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		WPA_PUT_BE24(pos, cert->cert_len);
+		pos += 3;
+		os_memcpy(pos, cert->cert_start, cert->cert_len);
+		pos += cert->cert_len;
+
+		if (x509_certificate_self_signed(cert))
+			break;
+		cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+						    &cert->issuer);
+	}
+	if (conn->cred == NULL || cert == conn->cred->cert || cert == NULL) {
+		/*
+		 * Client was not configured with all the needed certificates
+		 * to form a full certificate chain. The server may fail to
+		 * validate the chain unless it is configured with all the
+		 * missing CA certificates.
+		 */
+		wpa_printf(MSG_DEBUG, "TLSv1: Full client certificate chain "
+			   "not configured - validation may fail");
+	}
+	WPA_PUT_BE24(cert_start, pos - cert_start - 3);
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tlsv1_key_x_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
+{
+	/* ClientDiffieHellmanPublic */
+	u8 *csecret, *csecret_start, *dh_yc, *shared;
+	size_t csecret_len, dh_yc_len, shared_len;
+
+	csecret_len = conn->dh_p_len;
+	csecret = os_malloc(csecret_len);
+	if (csecret == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+			   "memory for Yc (Diffie-Hellman)");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	if (random_get_bytes(csecret, csecret_len)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+			   "data for Diffie-Hellman");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		return -1;
+	}
+
+	if (os_memcmp(csecret, conn->dh_p, csecret_len) > 0)
+		csecret[0] = 0; /* make sure Yc < p */
+
+	csecret_start = csecret;
+	while (csecret_len > 1 && *csecret_start == 0) {
+		csecret_start++;
+		csecret_len--;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH client's secret value",
+			csecret_start, csecret_len);
+
+	/* Yc = g^csecret mod p */
+	dh_yc_len = conn->dh_p_len;
+	dh_yc = os_malloc(dh_yc_len);
+	if (dh_yc == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+			   "memory for Diffie-Hellman");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		return -1;
+	}
+	if (crypto_mod_exp(conn->dh_g, conn->dh_g_len,
+			   csecret_start, csecret_len,
+			   conn->dh_p, conn->dh_p_len,
+			   dh_yc, &dh_yc_len)) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		os_free(dh_yc);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
+		    dh_yc, dh_yc_len);
+
+	WPA_PUT_BE16(*pos, dh_yc_len);
+	*pos += 2;
+	if (*pos + dh_yc_len > end) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the "
+			   "message buffer for Yc");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		os_free(dh_yc);
+		return -1;
+	}
+	os_memcpy(*pos, dh_yc, dh_yc_len);
+	*pos += dh_yc_len;
+	os_free(dh_yc);
+
+	shared_len = conn->dh_p_len;
+	shared = os_malloc(shared_len);
+	if (shared == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
+			   "DH");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		return -1;
+	}
+
+	/* shared = Ys^csecret mod p */
+	if (crypto_mod_exp(conn->dh_ys, conn->dh_ys_len,
+			   csecret_start, csecret_len,
+			   conn->dh_p, conn->dh_p_len,
+			   shared, &shared_len)) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		os_free(shared);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange",
+			shared, shared_len);
+
+	os_memset(csecret_start, 0, csecret_len);
+	os_free(csecret);
+	if (tls_derive_keys(conn, shared, shared_len)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(shared);
+		return -1;
+	}
+	os_memset(shared, 0, shared_len);
+	os_free(shared);
+	tlsv1_client_free_dh(conn);
+	return 0;
+}
+
+
+static int tlsv1_key_x_rsa(struct tlsv1_client *conn, u8 **pos, u8 *end)
+{
+	u8 pre_master_secret[TLS_PRE_MASTER_SECRET_LEN];
+	size_t clen;
+	int res;
+
+	if (tls_derive_pre_master_secret(pre_master_secret) < 0 ||
+	    tls_derive_keys(conn, pre_master_secret,
+			    TLS_PRE_MASTER_SECRET_LEN)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	/* EncryptedPreMasterSecret */
+	if (conn->server_rsa_key == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No server RSA key to "
+			   "use for encrypting pre-master secret");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	/* RSA encrypted value is encoded with PKCS #1 v1.5 block type 2. */
+	*pos += 2;
+	clen = end - *pos;
+	res = crypto_public_key_encrypt_pkcs1_v15(
+		conn->server_rsa_key,
+		pre_master_secret, TLS_PRE_MASTER_SECRET_LEN,
+		*pos, &clen);
+	os_memset(pre_master_secret, 0, TLS_PRE_MASTER_SECRET_LEN);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: RSA encryption failed");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	WPA_PUT_BE16(*pos - 2, clen);
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: Encrypted pre_master_secret",
+		    *pos, clen);
+	*pos += clen;
+
+	return 0;
+}
+
+
+static int tls_write_client_key_exchange(struct tlsv1_client *conn,
+					 u8 **msgpos, u8 *end)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length;
+	size_t rlen;
+	tls_key_exchange keyx;
+	const struct tls_cipher_suite *suite;
+
+	suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+	if (suite == NULL)
+		keyx = TLS_KEY_X_NULL;
+	else
+		keyx = suite->key_exchange;
+
+	pos = *msgpos;
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send ClientKeyExchange");
+
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+	/* body - ClientKeyExchange */
+	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)
+			return -1;
+	}
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
+					       u8 **msgpos, u8 *end)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start;
+	size_t rlen, hlen, clen;
+	u8 hash[100], *hpos;
+
+	pos = *msgpos;
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateVerify");
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+
+	/*
+	 * RFC 2246: 7.4.3 and 7.4.8:
+	 * Signature signature
+	 *
+	 * RSA:
+	 * digitally-signed struct {
+	 *     opaque md5_hash[16];
+	 *     opaque sha_hash[20];
+	 * };
+	 *
+	 * DSA:
+	 * digitally-signed struct {
+	 *     opaque sha_hash[20];
+	 * };
+	 *
+	 * The hash values are calculated over all handshake messages sent or
+	 * received starting at ClientHello up to, but not including, this
+	 * CertificateVerify message, including the type and length fields of
+	 * the handshake messages.
+	 */
+
+	hpos = hash;
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version == TLS_VERSION_1_2) {
+		hlen = SHA256_MAC_LEN;
+		if (conn->verify.sha256_cert == NULL ||
+		    crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
+		    0) {
+			conn->verify.sha256_cert = NULL;
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		conn->verify.sha256_cert = NULL;
+
+		/*
+		 * 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
+		 */
+		os_memmove(hash + 19, hash, hlen);
+		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 {
+#endif /* CONFIG_TLSV12 */
+
+	hlen = MD5_MAC_LEN;
+	if (conn->verify.md5_cert == NULL ||
+	    crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		conn->verify.md5_cert = NULL;
+		crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+		conn->verify.sha1_cert = NULL;
+		return -1;
+	}
+	hpos += MD5_MAC_LEN;
+
+	conn->verify.md5_cert = NULL;
+	hlen = SHA1_MAC_LEN;
+	if (conn->verify.sha1_cert == NULL ||
+	    crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) {
+		conn->verify.sha1_cert = NULL;
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	conn->verify.sha1_cert = NULL;
+
+	hlen += MD5_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+	}
+#endif /* CONFIG_TLSV12 */
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+		/*
+		 * RFC 5246, 4.7:
+		 * TLS v1.2 adds explicit indication of the used signature and
+		 * hash algorithms.
+		 *
+		 * struct {
+		 *   HashAlgorithm hash;
+		 *   SignatureAlgorithm signature;
+		 * } SignatureAndHashAlgorithm;
+		 */
+		*pos++ = TLS_HASH_ALG_SHA256;
+		*pos++ = TLS_SIGN_ALG_RSA;
+	}
+#endif /* CONFIG_TLSV12 */
+
+	/*
+	 * 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)");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	WPA_PUT_BE16(signed_start, clen);
+
+	pos += clen;
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
+					       u8 **msgpos, u8 *end)
+{
+	size_t rlen;
+	u8 payload[1];
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
+
+	payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
+			      *msgpos, end - *msgpos, payload, sizeof(payload),
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
+			   "record layer");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	*msgpos += rlen;
+
+	return 0;
+}
+
+
+static int tls_write_client_finished(struct tlsv1_client *conn,
+				     u8 **msgpos, u8 *end)
+{
+	u8 *pos, *hs_start;
+	size_t rlen, hlen;
+	u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
+	u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
+
+	/* Encrypted Handshake Message: Finished */
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+		hlen = SHA256_MAC_LEN;
+		if (conn->verify.sha256_client == NULL ||
+		    crypto_hash_finish(conn->verify.sha256_client, hash, &hlen)
+		    < 0) {
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_INTERNAL_ERROR);
+			conn->verify.sha256_client = NULL;
+			return -1;
+		}
+		conn->verify.sha256_client = NULL;
+	} else {
+#endif /* CONFIG_TLSV12 */
+
+	hlen = MD5_MAC_LEN;
+	if (conn->verify.md5_client == NULL ||
+	    crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		conn->verify.md5_client = NULL;
+		crypto_hash_finish(conn->verify.sha1_client, NULL, NULL);
+		conn->verify.sha1_client = NULL;
+		return -1;
+	}
+	conn->verify.md5_client = NULL;
+	hlen = SHA1_MAC_LEN;
+	if (conn->verify.sha1_client == NULL ||
+	    crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN,
+			       &hlen) < 0) {
+		conn->verify.sha1_client = NULL;
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	conn->verify.sha1_client = NULL;
+	hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+	}
+#endif /* CONFIG_TLSV12 */
+
+	if (tls_prf(conn->rl.tls_version,
+		    conn->master_secret, TLS_MASTER_SECRET_LEN,
+		    "client finished", hash, hlen,
+		    verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
+			verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
+
+	/* Handshake */
+	pos = hs_start = verify_data;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
+	/* uint24 length */
+	WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
+	pos += 3;
+	pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      *msgpos, end - *msgpos, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	*msgpos += rlen;
+
+	return 0;
+}
+
+
+static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn,
+					 size_t *out_len)
+{
+	u8 *msg, *end, *pos;
+	size_t msglen;
+
+	*out_len = 0;
+
+	msglen = 2000;
+	if (conn->certificate_requested)
+		msglen += tls_client_cert_chain_der_len(conn);
+
+	msg = os_malloc(msglen);
+	if (msg == NULL)
+		return NULL;
+
+	pos = msg;
+	end = msg + msglen;
+
+	if (conn->certificate_requested) {
+		if (tls_write_client_certificate(conn, &pos, end) < 0) {
+			os_free(msg);
+			return NULL;
+		}
+	}
+
+	if (tls_write_client_key_exchange(conn, &pos, end) < 0 ||
+	    (conn->certificate_requested && conn->cred && conn->cred->key &&
+	     tls_write_client_certificate_verify(conn, &pos, end) < 0) ||
+	    tls_write_client_change_cipher_spec(conn, &pos, end) < 0 ||
+	    tls_write_client_finished(conn, &pos, end) < 0) {
+		os_free(msg);
+		return NULL;
+	}
+
+	*out_len = pos - msg;
+
+	conn->state = SERVER_CHANGE_CIPHER_SPEC;
+
+	return msg;
+}
+
+
+static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn,
+					size_t *out_len)
+{
+	u8 *msg, *end, *pos;
+
+	*out_len = 0;
+
+	msg = os_malloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	pos = msg;
+	end = msg + 1000;
+
+	if (tls_write_client_change_cipher_spec(conn, &pos, end) < 0 ||
+	    tls_write_client_finished(conn, &pos, end) < 0) {
+		os_free(msg);
+		return NULL;
+	}
+
+	*out_len = pos - msg;
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed "
+		   "successfully");
+	conn->state = ESTABLISHED;
+
+	return msg;
+}
+
+
+u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len,
+				  int no_appl_data)
+{
+	switch (conn->state) {
+	case CLIENT_KEY_EXCHANGE:
+		return tls_send_client_key_exchange(conn, out_len);
+	case CHANGE_CIPHER_SPEC:
+		return tls_send_change_cipher_spec(conn, out_len);
+	case ACK_FINISHED:
+		wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed "
+			   "successfully");
+		conn->state = ESTABLISHED;
+		*out_len = 0;
+		if (no_appl_data) {
+			/* Need to return something to get final TLS ACK. */
+			return os_malloc(1);
+		}
+		return NULL;
+	default:
+		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
+			   "generating reply", conn->state);
+		return NULL;
+	}
+}
+
+
+u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level,
+			     u8 description, size_t *out_len)
+{
+	u8 *alert, *pos, *length;
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
+	*out_len = 0;
+
+	alert = os_malloc(10);
+	if (alert == NULL)
+		return NULL;
+
+	pos = alert;
+
+	/* TLSPlaintext */
+	/* ContentType type */
+	*pos++ = TLS_CONTENT_TYPE_ALERT;
+	/* ProtocolVersion version */
+	WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
+		     TLS_VERSION);
+	pos += 2;
+	/* uint16 length (to be filled) */
+	length = pos;
+	pos += 2;
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Alert */
+	/* AlertLevel level */
+	*pos++ = level;
+	/* AlertDescription description */
+	*pos++ = description;
+
+	WPA_PUT_BE16(length, pos - length - 2);
+	*out_len = pos - alert;
+
+	return alert;
+}
diff --git a/hostap/src/tls/tlsv1_common.c b/hostap/src/tls/tlsv1_common.c
new file mode 100644
index 0000000..dabc12a
--- /dev/null
+++ b/hostap/src/tls/tlsv1_common.c
@@ -0,0 +1,492 @@
+/*
+ * TLSv1 common routines
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+
+
+/*
+ * TODO:
+ * RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
+ * Add support for commonly used cipher suites; don't bother with exportable
+ * suites.
+ */ 
+
+static const struct tls_cipher_suite tls_cipher_suites[] = {
+	{ TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL,
+	  TLS_HASH_NULL },
+	{ TLS_RSA_WITH_RC4_128_MD5, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
+	  TLS_HASH_MD5 },
+	{ TLS_RSA_WITH_RC4_128_SHA, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
+	  TLS_HASH_SHA },
+	{ TLS_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_DES_CBC,
+	  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,
+	  TLS_CIPHER_DES_CBC, TLS_HASH_SHA },
+ 	{ TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DH_anon,
+	  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_TLS_CIPHER_SUITES ARRAY_SIZE(tls_cipher_suites)
+
+
+static const struct tls_cipher_data tls_ciphers[] = {
+	{ TLS_CIPHER_NULL,         TLS_CIPHER_STREAM,  0,  0,  0,
+	  CRYPTO_CIPHER_NULL },
+	{ TLS_CIPHER_IDEA_CBC,     TLS_CIPHER_BLOCK,  16, 16,  8,
+	  CRYPTO_CIPHER_NULL },
+	{ TLS_CIPHER_RC2_CBC_40,   TLS_CIPHER_BLOCK,   5, 16,  0,
+	  CRYPTO_CIPHER_ALG_RC2 },
+	{ TLS_CIPHER_RC4_40,       TLS_CIPHER_STREAM,  5, 16,  0,
+	  CRYPTO_CIPHER_ALG_RC4 },
+	{ TLS_CIPHER_RC4_128,      TLS_CIPHER_STREAM, 16, 16,  0,
+	  CRYPTO_CIPHER_ALG_RC4 },
+	{ TLS_CIPHER_DES40_CBC,    TLS_CIPHER_BLOCK,   5,  8,  8,
+	  CRYPTO_CIPHER_ALG_DES },
+	{ TLS_CIPHER_DES_CBC,      TLS_CIPHER_BLOCK,   8,  8,  8,
+	  CRYPTO_CIPHER_ALG_DES },
+	{ TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_BLOCK,  24, 24,  8,
+	  CRYPTO_CIPHER_ALG_3DES },
+	{ TLS_CIPHER_AES_128_CBC,  TLS_CIPHER_BLOCK,  16, 16, 16,
+	  CRYPTO_CIPHER_ALG_AES },
+	{ TLS_CIPHER_AES_256_CBC,  TLS_CIPHER_BLOCK,  32, 32, 16,
+	  CRYPTO_CIPHER_ALG_AES }
+};
+
+#define NUM_TLS_CIPHER_DATA ARRAY_SIZE(tls_ciphers)
+
+
+/**
+ * tls_get_cipher_suite - Get TLS cipher suite
+ * @suite: Cipher suite identifier
+ * Returns: Pointer to the cipher data or %NULL if not found
+ */
+const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite)
+{
+	size_t i;
+	for (i = 0; i < NUM_TLS_CIPHER_SUITES; i++)
+		if (tls_cipher_suites[i].suite == suite)
+			return &tls_cipher_suites[i];
+	return NULL;
+}
+
+
+const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher)
+{
+	size_t i;
+	for (i = 0; i < NUM_TLS_CIPHER_DATA; i++)
+		if (tls_ciphers[i].cipher == cipher)
+			return &tls_ciphers[i];
+	return NULL;
+}
+
+
+int tls_server_key_exchange_allowed(tls_cipher cipher)
+{
+	const struct tls_cipher_suite *suite;
+
+	/* RFC 2246, Section 7.4.3 */
+	suite = tls_get_cipher_suite(cipher);
+	if (suite == NULL)
+		return 0;
+
+	switch (suite->key_exchange) {
+	case TLS_KEY_X_DHE_DSS:
+	case TLS_KEY_X_DHE_DSS_EXPORT:
+	case TLS_KEY_X_DHE_RSA:
+	case TLS_KEY_X_DHE_RSA_EXPORT:
+	case TLS_KEY_X_DH_anon_EXPORT:
+	case TLS_KEY_X_DH_anon:
+		return 1;
+	case TLS_KEY_X_RSA_EXPORT:
+		return 1 /* FIX: public key len > 512 bits */;
+	default:
+		return 0;
+	}
+}
+
+
+/**
+ * tls_parse_cert - Parse DER encoded X.509 certificate and get public key
+ * @buf: ASN.1 DER encoded certificate
+ * @len: Length of the buffer
+ * @pk: Buffer for returning the allocated public key
+ * Returns: 0 on success, -1 on failure
+ *
+ * This functions parses an ASN.1 DER encoded X.509 certificate and retrieves
+ * the public key from it. The caller is responsible for freeing the public key
+ * by calling crypto_public_key_free().
+ */
+int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk)
+{
+	struct x509_certificate *cert;
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: Parse ASN.1 DER certificate",
+		    buf, len);
+
+	*pk = crypto_public_key_from_cert(buf, len);
+	if (*pk)
+		return 0;
+
+	cert = x509_certificate_parse(buf, len);
+	if (cert == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse X.509 "
+			   "certificate");
+		return -1;
+	}
+
+	/* TODO
+	 * verify key usage (must allow encryption)
+	 *
+	 * All certificate profiles, key and cryptographic formats are
+	 * defined by the IETF PKIX working group [PKIX]. When a key
+	 * usage extension is present, the digitalSignature bit must be
+	 * set for the key to be eligible for signing, as described
+	 * above, and the keyEncipherment bit must be present to allow
+	 * encryption, as described above. The keyAgreement bit must be
+	 * set on Diffie-Hellman certificates. (PKIX: RFC 3280)
+	 */
+
+	*pk = crypto_public_key_import(cert->public_key, cert->public_key_len);
+	x509_certificate_free(cert);
+
+	if (*pk == NULL) {
+		wpa_printf(MSG_ERROR, "TLSv1: Failed to import "
+			   "server public key");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int tls_verify_hash_init(struct tls_verify_hash *verify)
+{
+	tls_verify_hash_free(verify);
+	verify->md5_client = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+	verify->md5_server = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+	verify->md5_cert = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+	verify->sha1_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+	verify->sha1_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+	verify->sha1_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+	if (verify->md5_client == NULL || verify->md5_server == NULL ||
+	    verify->md5_cert == NULL || verify->sha1_client == NULL ||
+	    verify->sha1_server == NULL || verify->sha1_cert == NULL) {
+		tls_verify_hash_free(verify);
+		return -1;
+	}
+#ifdef CONFIG_TLSV12
+	verify->sha256_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL,
+						 0);
+	verify->sha256_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL,
+						 0);
+	verify->sha256_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL,
+					       0);
+	if (verify->sha256_client == NULL || verify->sha256_server == NULL ||
+	    verify->sha256_cert == NULL) {
+		tls_verify_hash_free(verify);
+		return -1;
+	}
+#endif /* CONFIG_TLSV12 */
+	return 0;
+}
+
+
+void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
+			 size_t len)
+{
+	if (verify->md5_client && verify->sha1_client) {
+		crypto_hash_update(verify->md5_client, buf, len);
+		crypto_hash_update(verify->sha1_client, buf, len);
+	}
+	if (verify->md5_server && verify->sha1_server) {
+		crypto_hash_update(verify->md5_server, buf, len);
+		crypto_hash_update(verify->sha1_server, buf, len);
+	}
+	if (verify->md5_cert && verify->sha1_cert) {
+		crypto_hash_update(verify->md5_cert, buf, len);
+		crypto_hash_update(verify->sha1_cert, buf, len);
+	}
+#ifdef CONFIG_TLSV12
+	if (verify->sha256_client)
+		crypto_hash_update(verify->sha256_client, buf, len);
+	if (verify->sha256_server)
+		crypto_hash_update(verify->sha256_server, buf, len);
+	if (verify->sha256_cert)
+		crypto_hash_update(verify->sha256_cert, buf, len);
+#endif /* CONFIG_TLSV12 */
+}
+
+
+void tls_verify_hash_free(struct tls_verify_hash *verify)
+{
+	crypto_hash_finish(verify->md5_client, NULL, NULL);
+	crypto_hash_finish(verify->md5_server, NULL, NULL);
+	crypto_hash_finish(verify->md5_cert, NULL, NULL);
+	crypto_hash_finish(verify->sha1_client, NULL, NULL);
+	crypto_hash_finish(verify->sha1_server, NULL, NULL);
+	crypto_hash_finish(verify->sha1_cert, NULL, NULL);
+	verify->md5_client = NULL;
+	verify->md5_server = NULL;
+	verify->md5_cert = NULL;
+	verify->sha1_client = NULL;
+	verify->sha1_server = NULL;
+	verify->sha1_cert = NULL;
+#ifdef CONFIG_TLSV12
+	crypto_hash_finish(verify->sha256_client, NULL, NULL);
+	crypto_hash_finish(verify->sha256_server, NULL, NULL);
+	crypto_hash_finish(verify->sha256_cert, NULL, NULL);
+	verify->sha256_client = NULL;
+	verify->sha256_server = NULL;
+	verify->sha256_cert = NULL;
+#endif /* CONFIG_TLSV12 */
+}
+
+
+int tls_version_ok(u16 ver)
+{
+	if (ver == TLS_VERSION_1)
+		return 1;
+#ifdef CONFIG_TLSV11
+	if (ver == TLS_VERSION_1_1)
+		return 1;
+#endif /* CONFIG_TLSV11 */
+#ifdef CONFIG_TLSV12
+	if (ver == TLS_VERSION_1_2)
+		return 1;
+#endif /* CONFIG_TLSV12 */
+
+	return 0;
+}
+
+
+const char * tls_version_str(u16 ver)
+{
+	switch (ver) {
+	case TLS_VERSION_1:
+		return "1.0";
+	case TLS_VERSION_1_1:
+		return "1.1";
+	case TLS_VERSION_1_2:
+		return "1.2";
+	}
+
+	return "?";
+}
+
+
+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)
+{
+#ifdef CONFIG_TLSV12
+	if (ver >= TLS_VERSION_1_2) {
+		tls_prf_sha256(secret, secret_len, label, seed, seed_len,
+			       out, outlen);
+		return 0;
+	}
+#endif /* CONFIG_TLSV12 */
+
+	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;
+	struct crypto_hash *ctx;
+
+	hpos = hash;
+
+	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/hostap/src/tls/tlsv1_common.h b/hostap/src/tls/tlsv1_common.h
new file mode 100644
index 0000000..26e68af
--- /dev/null
+++ b/hostap/src/tls/tlsv1_common.h
@@ -0,0 +1,272 @@
+/*
+ * TLSv1 common definitions
+ * 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.
+ */
+
+#ifndef TLSV1_COMMON_H
+#define TLSV1_COMMON_H
+
+#include "crypto/crypto.h"
+
+#define TLS_VERSION_1 0x0301 /* TLSv1 */
+#define TLS_VERSION_1_1 0x0302 /* TLSv1.1 */
+#define TLS_VERSION_1_2 0x0303 /* TLSv1.2 */
+#ifdef CONFIG_TLSV12
+#define TLS_VERSION TLS_VERSION_1_2
+#else /* CONFIG_TLSV12 */
+#ifdef CONFIG_TLSV11
+#define TLS_VERSION TLS_VERSION_1_1
+#else /* CONFIG_TLSV11 */
+#define TLS_VERSION TLS_VERSION_1
+#endif /* CONFIG_TLSV11 */
+#endif /* CONFIG_TLSV12 */
+#define TLS_RANDOM_LEN 32
+#define TLS_PRE_MASTER_SECRET_LEN 48
+#define TLS_MASTER_SECRET_LEN 48
+#define TLS_SESSION_ID_MAX_LEN 32
+#define TLS_VERIFY_DATA_LEN 12
+
+/* HandshakeType */
+enum {
+	TLS_HANDSHAKE_TYPE_HELLO_REQUEST = 0,
+	TLS_HANDSHAKE_TYPE_CLIENT_HELLO = 1,
+	TLS_HANDSHAKE_TYPE_SERVER_HELLO = 2,
+	TLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET = 4 /* RFC 4507 */,
+	TLS_HANDSHAKE_TYPE_CERTIFICATE = 11,
+	TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE = 12,
+	TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST = 13,
+	TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE = 14,
+	TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY = 15,
+	TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE = 16,
+	TLS_HANDSHAKE_TYPE_FINISHED = 20,
+	TLS_HANDSHAKE_TYPE_CERTIFICATE_URL = 21 /* RFC 4366 */,
+	TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS = 22 /* RFC 4366 */
+};
+
+/* CipherSuite */
+#define TLS_NULL_WITH_NULL_NULL			0x0000 /* RFC 2246 */
+#define TLS_RSA_WITH_NULL_MD5			0x0001 /* RFC 2246 */
+#define TLS_RSA_WITH_NULL_SHA			0x0002 /* RFC 2246 */
+#define TLS_RSA_EXPORT_WITH_RC4_40_MD5		0x0003 /* RFC 2246 */
+#define TLS_RSA_WITH_RC4_128_MD5		0x0004 /* RFC 2246 */
+#define TLS_RSA_WITH_RC4_128_SHA		0x0005 /* RFC 2246 */
+#define TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5	0x0006 /* RFC 2246 */
+#define TLS_RSA_WITH_IDEA_CBC_SHA		0x0007 /* RFC 2246 */
+#define TLS_RSA_EXPORT_WITH_DES40_CBC_SHA	0x0008 /* RFC 2246 */
+#define TLS_RSA_WITH_DES_CBC_SHA		0x0009 /* RFC 2246 */
+#define TLS_RSA_WITH_3DES_EDE_CBC_SHA		0x000A /* RFC 2246 */
+#define TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA	0x000B /* RFC 2246 */
+#define TLS_DH_DSS_WITH_DES_CBC_SHA		0x000C /* RFC 2246 */
+#define TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA	0x000D /* RFC 2246 */
+#define TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA	0x000E /* RFC 2246 */
+#define TLS_DH_RSA_WITH_DES_CBC_SHA		0x000F /* RFC 2246 */
+#define TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA	0x0010 /* RFC 2246 */
+#define TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA	0x0011 /* RFC 2246 */
+#define TLS_DHE_DSS_WITH_DES_CBC_SHA		0x0012 /* RFC 2246 */
+#define TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA	0x0013 /* RFC 2246 */
+#define TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA	0x0014 /* RFC 2246 */
+#define TLS_DHE_RSA_WITH_DES_CBC_SHA		0x0015 /* RFC 2246 */
+#define TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA	0x0016 /* RFC 2246 */
+#define TLS_DH_anon_EXPORT_WITH_RC4_40_MD5	0x0017 /* RFC 2246 */
+#define TLS_DH_anon_WITH_RC4_128_MD5		0x0018 /* RFC 2246 */
+#define TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA	0x0019 /* RFC 2246 */
+#define TLS_DH_anon_WITH_DES_CBC_SHA		0x001A /* RFC 2246 */
+#define TLS_DH_anon_WITH_3DES_EDE_CBC_SHA	0x001B /* RFC 2246 */
+#define TLS_RSA_WITH_AES_128_CBC_SHA		0x002F /* RFC 3268 */
+#define TLS_DH_DSS_WITH_AES_128_CBC_SHA		0x0030 /* RFC 3268 */
+#define TLS_DH_RSA_WITH_AES_128_CBC_SHA		0x0031 /* RFC 3268 */
+#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA	0x0032 /* RFC 3268 */
+#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA	0x0033 /* RFC 3268 */
+#define TLS_DH_anon_WITH_AES_128_CBC_SHA	0x0034 /* RFC 3268 */
+#define TLS_RSA_WITH_AES_256_CBC_SHA		0x0035 /* RFC 3268 */
+#define TLS_DH_DSS_WITH_AES_256_CBC_SHA		0x0036 /* RFC 3268 */
+#define TLS_DH_RSA_WITH_AES_256_CBC_SHA		0x0037 /* RFC 3268 */
+#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA	0x0038 /* RFC 3268 */
+#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA	0x0039 /* RFC 3268 */
+#define TLS_DH_anon_WITH_AES_256_CBC_SHA	0x003A /* RFC 3268 */
+#define TLS_RSA_WITH_NULL_SHA256		0x003B /* RFC 5246 */
+#define TLS_RSA_WITH_AES_128_CBC_SHA256		0x003C /* RFC 5246 */
+#define TLS_RSA_WITH_AES_256_CBC_SHA256		0x003D /* RFC 5246 */
+#define TLS_DH_DSS_WITH_AES_128_CBC_SHA256	0x003E /* RFC 5246 */
+#define TLS_DH_RSA_WITH_AES_128_CBC_SHA256	0x003F /* RFC 5246 */
+#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA256	0x0040 /* RFC 5246 */
+#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256	0x0067 /* RFC 5246 */
+#define TLS_DH_DSS_WITH_AES_256_CBC_SHA256	0x0068 /* RFC 5246 */
+#define TLS_DH_RSA_WITH_AES_256_CBC_SHA256	0x0069 /* RFC 5246 */
+#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA256	0x006A /* RFC 5246 */
+#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256	0x006B /* RFC 5246 */
+#define TLS_DH_anon_WITH_AES_128_CBC_SHA256	0x006C /* RFC 5246 */
+#define TLS_DH_anon_WITH_AES_256_CBC_SHA256	0x006D /* RFC 5246 */
+
+/* CompressionMethod */
+#define TLS_COMPRESSION_NULL 0
+
+/* HashAlgorithm */
+enum {
+	TLS_HASH_ALG_NONE = 0,
+	TLS_HASH_ALG_MD5 = 1,
+	TLS_HASH_ALG_SHA1 = 2,
+	TLS_HASH_ALG_SHA224 = 3,
+	TLS_HASH_ALG_SHA256 = 4,
+	TLS_HASH_ALG_SHA384 = 5,
+	TLS_HASH_ALG_SHA512 = 6
+};
+
+/* SignatureAlgorithm */
+enum {
+	TLS_SIGN_ALG_ANONYMOUS = 0,
+	TLS_SIGN_ALG_RSA = 1,
+	TLS_SIGN_ALG_DSA = 2,
+	TLS_SIGN_ALG_ECDSA = 3,
+};
+
+/* AlertLevel */
+#define TLS_ALERT_LEVEL_WARNING 1
+#define TLS_ALERT_LEVEL_FATAL 2
+
+/* AlertDescription */
+#define TLS_ALERT_CLOSE_NOTIFY			0
+#define TLS_ALERT_UNEXPECTED_MESSAGE		10
+#define TLS_ALERT_BAD_RECORD_MAC		20
+#define TLS_ALERT_DECRYPTION_FAILED		21
+#define TLS_ALERT_RECORD_OVERFLOW		22
+#define TLS_ALERT_DECOMPRESSION_FAILURE		30
+#define TLS_ALERT_HANDSHAKE_FAILURE		40
+#define TLS_ALERT_BAD_CERTIFICATE		42
+#define TLS_ALERT_UNSUPPORTED_CERTIFICATE	43
+#define TLS_ALERT_CERTIFICATE_REVOKED		44
+#define TLS_ALERT_CERTIFICATE_EXPIRED		45
+#define TLS_ALERT_CERTIFICATE_UNKNOWN		46
+#define TLS_ALERT_ILLEGAL_PARAMETER		47
+#define TLS_ALERT_UNKNOWN_CA			48
+#define TLS_ALERT_ACCESS_DENIED			49
+#define TLS_ALERT_DECODE_ERROR			50
+#define TLS_ALERT_DECRYPT_ERROR			51
+#define TLS_ALERT_EXPORT_RESTRICTION		60
+#define TLS_ALERT_PROTOCOL_VERSION		70
+#define TLS_ALERT_INSUFFICIENT_SECURITY		71
+#define TLS_ALERT_INTERNAL_ERROR		80
+#define TLS_ALERT_USER_CANCELED			90
+#define TLS_ALERT_NO_RENEGOTIATION		100
+#define TLS_ALERT_UNSUPPORTED_EXTENSION		110 /* RFC 4366 */
+#define TLS_ALERT_CERTIFICATE_UNOBTAINABLE	111 /* RFC 4366 */
+#define TLS_ALERT_UNRECOGNIZED_NAME		112 /* RFC 4366 */
+#define TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE	113 /* RFC 4366 */
+#define TLS_ALERT_BAD_CERTIFICATE_HASH_VALUE	114 /* RFC 4366 */
+
+/* ChangeCipherSpec */
+enum {
+	TLS_CHANGE_CIPHER_SPEC = 1
+};
+
+/* TLS Extensions */
+#define TLS_EXT_SERVER_NAME			0 /* RFC 4366 */
+#define TLS_EXT_MAX_FRAGMENT_LENGTH		1 /* RFC 4366 */
+#define TLS_EXT_CLIENT_CERTIFICATE_URL		2 /* RFC 4366 */
+#define TLS_EXT_TRUSTED_CA_KEYS			3 /* RFC 4366 */
+#define TLS_EXT_TRUNCATED_HMAC			4 /* RFC 4366 */
+#define TLS_EXT_STATUS_REQUEST			5 /* RFC 4366 */
+#define TLS_EXT_SESSION_TICKET			35 /* RFC 4507 */
+
+#define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */
+
+
+typedef enum {
+	TLS_KEY_X_NULL,
+	TLS_KEY_X_RSA,
+	TLS_KEY_X_RSA_EXPORT,
+	TLS_KEY_X_DH_DSS_EXPORT,
+	TLS_KEY_X_DH_DSS,
+	TLS_KEY_X_DH_RSA_EXPORT,
+	TLS_KEY_X_DH_RSA,
+	TLS_KEY_X_DHE_DSS_EXPORT,
+	TLS_KEY_X_DHE_DSS,
+	TLS_KEY_X_DHE_RSA_EXPORT,
+	TLS_KEY_X_DHE_RSA,
+	TLS_KEY_X_DH_anon_EXPORT,
+	TLS_KEY_X_DH_anon
+} tls_key_exchange;
+
+typedef enum {
+	TLS_CIPHER_NULL,
+	TLS_CIPHER_RC4_40,
+	TLS_CIPHER_RC4_128,
+	TLS_CIPHER_RC2_CBC_40,
+	TLS_CIPHER_IDEA_CBC,
+	TLS_CIPHER_DES40_CBC,
+	TLS_CIPHER_DES_CBC,
+	TLS_CIPHER_3DES_EDE_CBC,
+	TLS_CIPHER_AES_128_CBC,
+	TLS_CIPHER_AES_256_CBC
+} tls_cipher;
+
+typedef enum {
+	TLS_HASH_NULL,
+	TLS_HASH_MD5,
+	TLS_HASH_SHA,
+	TLS_HASH_SHA256
+} tls_hash;
+
+struct tls_cipher_suite {
+	u16 suite;
+	tls_key_exchange key_exchange;
+	tls_cipher cipher;
+	tls_hash hash;
+};
+
+typedef enum {
+	TLS_CIPHER_STREAM,
+	TLS_CIPHER_BLOCK
+} tls_cipher_type;
+
+struct tls_cipher_data {
+	tls_cipher cipher;
+	tls_cipher_type type;
+	size_t key_material;
+	size_t expanded_key_material;
+	size_t block_size; /* also iv_size */
+	enum crypto_cipher_alg alg;
+};
+
+
+struct tls_verify_hash {
+	struct crypto_hash *md5_client;
+	struct crypto_hash *sha1_client;
+	struct crypto_hash *sha256_client;
+	struct crypto_hash *md5_server;
+	struct crypto_hash *sha1_server;
+	struct crypto_hash *sha256_server;
+	struct crypto_hash *md5_cert;
+	struct crypto_hash *sha1_cert;
+	struct crypto_hash *sha256_cert;
+};
+
+
+const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite);
+const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher);
+int tls_server_key_exchange_allowed(tls_cipher cipher);
+int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk);
+int tls_verify_hash_init(struct tls_verify_hash *verify);
+void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
+			 size_t len);
+void tls_verify_hash_free(struct tls_verify_hash *verify);
+int tls_version_ok(u16 ver);
+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/hostap/src/tls/tlsv1_cred.c b/hostap/src/tls/tlsv1_cred.c
new file mode 100644
index 0000000..1ea6827
--- /dev/null
+++ b/hostap/src/tls/tlsv1_cred.c
@@ -0,0 +1,506 @@
+/*
+ * TLSv1 credentials
+ * Copyright (c) 2006-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 "base64.h"
+#include "crypto/crypto.h"
+#include "x509v3.h"
+#include "tlsv1_cred.h"
+
+
+struct tlsv1_credentials * tlsv1_cred_alloc(void)
+{
+	struct tlsv1_credentials *cred;
+	cred = os_zalloc(sizeof(*cred));
+	return cred;
+}
+
+
+void tlsv1_cred_free(struct tlsv1_credentials *cred)
+{
+	if (cred == NULL)
+		return;
+
+	x509_certificate_chain_free(cred->trusted_certs);
+	x509_certificate_chain_free(cred->cert);
+	crypto_private_key_free(cred->key);
+	os_free(cred->dh_p);
+	os_free(cred->dh_g);
+	os_free(cred);
+}
+
+
+static int tlsv1_add_cert_der(struct x509_certificate **chain,
+			      const u8 *buf, size_t len)
+{
+	struct x509_certificate *cert, *p;
+	char name[128];
+
+	cert = x509_certificate_parse(buf, len);
+	if (cert == NULL) {
+		wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate",
+			   __func__);
+		return -1;
+	}
+
+	p = *chain;
+	while (p && p->next)
+		p = p->next;
+	if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) {
+		/*
+		 * The new certificate is the issuer of the last certificate in
+		 * the chain - add the new certificate to the end.
+		 */
+		p->next = cert;
+	} else {
+		/* Add to the beginning of the chain */
+		cert->next = *chain;
+		*chain = cert;
+	}
+
+	x509_name_string(&cert->subject, name, sizeof(name));
+	wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
+
+	return 0;
+}
+
+
+static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
+static const char *pem_cert_end = "-----END CERTIFICATE-----";
+static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
+static const char *pem_key_end = "-----END RSA PRIVATE KEY-----";
+static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----";
+static const char *pem_key2_end = "-----END PRIVATE KEY-----";
+static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
+static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----";
+
+
+static const u8 * search_tag(const char *tag, const u8 *buf, size_t len)
+{
+	size_t i, plen;
+
+	plen = os_strlen(tag);
+	if (len < plen)
+		return NULL;
+
+	for (i = 0; i < len - plen; i++) {
+		if (os_memcmp(buf + i, tag, plen) == 0)
+			return buf + i;
+	}
+
+	return NULL;
+}
+
+
+static int tlsv1_add_cert(struct x509_certificate **chain,
+			  const u8 *buf, size_t len)
+{
+	const u8 *pos, *end;
+	unsigned char *der;
+	size_t der_len;
+
+	pos = search_tag(pem_cert_begin, buf, len);
+	if (!pos) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - "
+			   "assume DER format");
+		return tlsv1_add_cert_der(chain, buf, len);
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into "
+		   "DER format");
+
+	while (pos) {
+		pos += os_strlen(pem_cert_begin);
+		end = search_tag(pem_cert_end, pos, buf + len - pos);
+		if (end == NULL) {
+			wpa_printf(MSG_INFO, "TLSv1: Could not find PEM "
+				   "certificate end tag (%s)", pem_cert_end);
+			return -1;
+		}
+
+		der = base64_decode(pos, end - pos, &der_len);
+		if (der == NULL) {
+			wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
+				   "certificate");
+			return -1;
+		}
+
+		if (tlsv1_add_cert_der(chain, der, der_len) < 0) {
+			wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM "
+				   "certificate after DER conversion");
+			os_free(der);
+			return -1;
+		}
+
+		os_free(der);
+
+		end += os_strlen(pem_cert_end);
+		pos = search_tag(pem_cert_begin, end, buf + len - end);
+	}
+
+	return 0;
+}
+
+
+static int tlsv1_set_cert_chain(struct x509_certificate **chain,
+				const char *cert, const u8 *cert_blob,
+				size_t cert_blob_len)
+{
+	if (cert_blob)
+		return tlsv1_add_cert(chain, cert_blob, cert_blob_len);
+
+	if (cert) {
+		u8 *buf;
+		size_t len;
+		int ret;
+
+		buf = (u8 *) os_readfile(cert, &len);
+		if (buf == NULL) {
+			wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+				   cert);
+			return -1;
+		}
+
+		ret = tlsv1_add_cert(chain, buf, len);
+		os_free(buf);
+		return ret;
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_set_ca_cert - Set trusted CA certificate(s)
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @cert: File or reference name for X.509 certificate in PEM or DER format
+ * @cert_blob: cert as inlined data or %NULL if not used
+ * @cert_blob_len: ca_cert_blob length
+ * @path: Path to CA certificates (not yet supported)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
+		      const u8 *cert_blob, size_t cert_blob_len,
+		      const char *path)
+{
+	if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
+				 cert_blob, cert_blob_len) < 0)
+		return -1;
+
+	if (path) {
+		/* TODO: add support for reading number of certificate files */
+		wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory "
+			   "not yet supported");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_set_cert - Set certificate
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @cert: File or reference name for X.509 certificate in PEM or DER format
+ * @cert_blob: cert as inlined data or %NULL if not used
+ * @cert_blob_len: cert_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
+		   const u8 *cert_blob, size_t cert_blob_len)
+{
+	return tlsv1_set_cert_chain(&cred->cert, cert,
+				    cert_blob, cert_blob_len);
+}
+
+
+static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len)
+{
+	const u8 *pos, *end;
+	unsigned char *der;
+	size_t der_len;
+	struct crypto_private_key *pkey;
+
+	pos = search_tag(pem_key_begin, key, len);
+	if (!pos) {
+		pos = search_tag(pem_key2_begin, key, len);
+		if (!pos)
+			return NULL;
+		pos += os_strlen(pem_key2_begin);
+		end = search_tag(pem_key2_end, pos, key + len - pos);
+		if (!end)
+			return NULL;
+	} else {
+		const u8 *pos2;
+		pos += os_strlen(pem_key_begin);
+		end = search_tag(pem_key_end, pos, key + len - pos);
+		if (!end)
+			return NULL;
+		pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos);
+		if (pos2) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key "
+				   "format (Proc-Type/DEK-Info)");
+			return NULL;
+		}
+	}
+
+	der = base64_decode(pos, end - pos, &der_len);
+	if (!der)
+		return NULL;
+	pkey = crypto_private_key_import(der, der_len, NULL);
+	os_free(der);
+	return pkey;
+}
+
+
+static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key,
+							 size_t len,
+							 const char *passwd)
+{
+	const u8 *pos, *end;
+	unsigned char *der;
+	size_t der_len;
+	struct crypto_private_key *pkey;
+
+	if (passwd == NULL)
+		return NULL;
+	pos = search_tag(pem_key_enc_begin, key, len);
+	if (!pos)
+		return NULL;
+	pos += os_strlen(pem_key_enc_begin);
+	end = search_tag(pem_key_enc_end, pos, key + len - pos);
+	if (!end)
+		return NULL;
+
+	der = base64_decode(pos, end - pos, &der_len);
+	if (!der)
+		return NULL;
+	pkey = crypto_private_key_import(der, der_len, passwd);
+	os_free(der);
+	return pkey;
+}
+
+
+static int tlsv1_set_key(struct tlsv1_credentials *cred,
+			 const u8 *key, size_t len, const char *passwd)
+{
+	cred->key = crypto_private_key_import(key, len, passwd);
+	if (cred->key == NULL)
+		cred->key = tlsv1_set_key_pem(key, len);
+	if (cred->key == NULL)
+		cred->key = tlsv1_set_key_enc_pem(key, len, passwd);
+	if (cred->key == NULL) {
+		wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * tlsv1_set_private_key - Set private key
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @private_key: File or reference name for the key in PEM or DER format
+ * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
+ * passphrase is used.
+ * @private_key_blob: private_key as inlined data or %NULL if not used
+ * @private_key_blob_len: private_key_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_private_key(struct tlsv1_credentials *cred,
+			  const char *private_key,
+			  const char *private_key_passwd,
+			  const u8 *private_key_blob,
+			  size_t private_key_blob_len)
+{
+	crypto_private_key_free(cred->key);
+	cred->key = NULL;
+
+	if (private_key_blob)
+		return tlsv1_set_key(cred, private_key_blob,
+				     private_key_blob_len,
+				     private_key_passwd);
+
+	if (private_key) {
+		u8 *buf;
+		size_t len;
+		int ret;
+
+		buf = (u8 *) os_readfile(private_key, &len);
+		if (buf == NULL) {
+			wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+				   private_key);
+			return -1;
+		}
+
+		ret = tlsv1_set_key(cred, buf, len, private_key_passwd);
+		os_free(buf);
+		return ret;
+	}
+
+	return 0;
+}
+
+
+static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
+				  const u8 *dh, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+
+	pos = dh;
+	end = dh + len;
+
+	/*
+	 * DHParameter ::= SEQUENCE {
+	 *   prime INTEGER, -- p
+	 *   base INTEGER, -- g
+	 *   privateValueLength INTEGER OPTIONAL }
+	 */
+
+	/* DHParamer ::= SEQUENCE */
+	if (asn1_get_next(pos, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a "
+			   "valid SEQUENCE - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+
+	/* prime INTEGER */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0)
+		return -1;
+
+	if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_INTEGER) {
+		wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; "
+			   "class=%d tag=0x%x", hdr.class, hdr.tag);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length);
+	if (hdr.length == 0)
+		return -1;
+	os_free(cred->dh_p);
+	cred->dh_p = os_malloc(hdr.length);
+	if (cred->dh_p == NULL)
+		return -1;
+	os_memcpy(cred->dh_p, hdr.payload, hdr.length);
+	cred->dh_p_len = hdr.length;
+	pos = hdr.payload + hdr.length;
+
+	/* base INTEGER */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0)
+		return -1;
+
+	if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_INTEGER) {
+		wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; "
+			   "class=%d tag=0x%x", hdr.class, hdr.tag);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length);
+	if (hdr.length == 0)
+		return -1;
+	os_free(cred->dh_g);
+	cred->dh_g = os_malloc(hdr.length);
+	if (cred->dh_g == NULL)
+		return -1;
+	os_memcpy(cred->dh_g, hdr.payload, hdr.length);
+	cred->dh_g_len = hdr.length;
+
+	return 0;
+}
+
+
+static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
+static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
+
+
+static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred,
+				   const u8 *buf, size_t len)
+{
+	const u8 *pos, *end;
+	unsigned char *der;
+	size_t der_len;
+
+	pos = search_tag(pem_dhparams_begin, buf, len);
+	if (!pos) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - "
+			   "assume DER format");
+		return tlsv1_set_dhparams_der(cred, buf, len);
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER "
+		   "format");
+
+	pos += os_strlen(pem_dhparams_begin);
+	end = search_tag(pem_dhparams_end, pos, buf + len - pos);
+	if (end == NULL) {
+		wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end "
+			   "tag (%s)", pem_dhparams_end);
+		return -1;
+	}
+
+	der = base64_decode(pos, end - pos, &der_len);
+	if (der == NULL) {
+		wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
+		return -1;
+	}
+
+	if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) {
+		wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams "
+			   "DER conversion");
+		os_free(der);
+		return -1;
+	}
+
+	os_free(der);
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_set_dhparams - Set Diffie-Hellman parameters
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @dh_file: File or reference name for the DH params in PEM or DER format
+ * @dh_blob: DH params as inlined data or %NULL if not used
+ * @dh_blob_len: dh_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
+		       const u8 *dh_blob, size_t dh_blob_len)
+{
+	if (dh_blob)
+		return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len);
+
+	if (dh_file) {
+		u8 *buf;
+		size_t len;
+		int ret;
+
+		buf = (u8 *) os_readfile(dh_file, &len);
+		if (buf == NULL) {
+			wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+				   dh_file);
+			return -1;
+		}
+
+		ret = tlsv1_set_dhparams_blob(cred, buf, len);
+		os_free(buf);
+		return ret;
+	}
+
+	return 0;
+}
diff --git a/hostap/src/tls/tlsv1_cred.h b/hostap/src/tls/tlsv1_cred.h
new file mode 100644
index 0000000..68fbdc9
--- /dev/null
+++ b/hostap/src/tls/tlsv1_cred.h
@@ -0,0 +1,40 @@
+/*
+ * TLSv1 credentials
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLSV1_CRED_H
+#define TLSV1_CRED_H
+
+struct tlsv1_credentials {
+	struct x509_certificate *trusted_certs;
+	struct x509_certificate *cert;
+	struct crypto_private_key *key;
+
+	/* Diffie-Hellman parameters */
+	u8 *dh_p; /* prime */
+	size_t dh_p_len;
+	u8 *dh_g; /* generator */
+	size_t dh_g_len;
+};
+
+
+struct tlsv1_credentials * tlsv1_cred_alloc(void);
+void tlsv1_cred_free(struct tlsv1_credentials *cred);
+int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
+		      const u8 *cert_blob, size_t cert_blob_len,
+		      const char *path);
+int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
+		   const u8 *cert_blob, size_t cert_blob_len);
+int tlsv1_set_private_key(struct tlsv1_credentials *cred,
+			  const char *private_key,
+			  const char *private_key_passwd,
+			  const u8 *private_key_blob,
+			  size_t private_key_blob_len);
+int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
+		       const u8 *dh_blob, size_t dh_blob_len);
+
+#endif /* TLSV1_CRED_H */
diff --git a/hostap/src/tls/tlsv1_record.c b/hostap/src/tls/tlsv1_record.c
new file mode 100644
index 0000000..0c6897a
--- /dev/null
+++ b/hostap/src/tls/tlsv1_record.c
@@ -0,0 +1,485 @@
+/*
+ * TLSv1 Record Protocol
+ * Copyright (c) 2006-2011, 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/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+
+
+/**
+ * tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite
+ * @rl: Pointer to TLS record layer data
+ * @cipher_suite: New cipher suite
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to prepare TLS record layer for cipher suite change.
+ * tlsv1_record_change_write_cipher() and
+ * tlsv1_record_change_read_cipher() functions can then be used to change the
+ * currently used ciphers.
+ */
+int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
+				  u16 cipher_suite)
+{
+	const struct tls_cipher_suite *suite;
+	const struct tls_cipher_data *data;
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x",
+		   cipher_suite);
+	rl->cipher_suite = cipher_suite;
+
+	suite = tls_get_cipher_suite(cipher_suite);
+	if (suite == NULL)
+		return -1;
+
+	if (suite->hash == TLS_HASH_MD5) {
+		rl->hash_alg = CRYPTO_HASH_ALG_HMAC_MD5;
+		rl->hash_size = MD5_MAC_LEN;
+	} else if (suite->hash == TLS_HASH_SHA) {
+		rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1;
+		rl->hash_size = SHA1_MAC_LEN;
+	} else if (suite->hash == TLS_HASH_SHA256) {
+		rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA256;
+		rl->hash_size = SHA256_MAC_LEN;
+	}
+
+	data = tls_get_cipher_data(suite->cipher);
+	if (data == NULL)
+		return -1;
+
+	rl->key_material_len = data->key_material;
+	rl->iv_size = data->block_size;
+	rl->cipher_alg = data->alg;
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_record_change_write_cipher - TLS record layer: Change write cipher
+ * @rl: Pointer to TLS record layer data
+ * Returns: 0 on success (cipher changed), -1 on failure
+ *
+ * This function changes TLS record layer to use the new cipher suite
+ * configured with tlsv1_record_set_cipher_suite() for writing.
+ */
+int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl)
+{
+	wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New write cipher suite "
+		   "0x%04x", rl->cipher_suite);
+	rl->write_cipher_suite = rl->cipher_suite;
+	os_memset(rl->write_seq_num, 0, TLS_SEQ_NUM_LEN);
+
+	if (rl->write_cbc) {
+		crypto_cipher_deinit(rl->write_cbc);
+		rl->write_cbc = NULL;
+	}
+	if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
+		rl->write_cbc = crypto_cipher_init(rl->cipher_alg,
+						   rl->write_iv, rl->write_key,
+						   rl->key_material_len);
+		if (rl->write_cbc == NULL) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
+				   "cipher");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_record_change_read_cipher - TLS record layer: Change read cipher
+ * @rl: Pointer to TLS record layer data
+ * Returns: 0 on success (cipher changed), -1 on failure
+ *
+ * This function changes TLS record layer to use the new cipher suite
+ * configured with tlsv1_record_set_cipher_suite() for reading.
+ */
+int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
+{
+	wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite "
+		   "0x%04x", rl->cipher_suite);
+	rl->read_cipher_suite = rl->cipher_suite;
+	os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN);
+
+	if (rl->read_cbc) {
+		crypto_cipher_deinit(rl->read_cbc);
+		rl->read_cbc = NULL;
+	}
+	if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
+		rl->read_cbc = crypto_cipher_init(rl->cipher_alg,
+						  rl->read_iv, rl->read_key,
+						  rl->key_material_len);
+		if (rl->read_cbc == NULL) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
+				   "cipher");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_record_send - TLS record layer: Send a message
+ * @rl: Pointer to TLS record layer data
+ * @content_type: Content type (TLS_CONTENT_TYPE_*)
+ * @buf: Buffer for the generated TLS message (needs to have extra space for
+ * header, IV (TLS v1.1), and HMAC)
+ * @buf_size: Maximum buf size
+ * @payload: Payload to be sent
+ * @payload_len: Length of the payload
+ * @out_len: Buffer for returning the used buf length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function fills in the TLS record layer header, adds HMAC, and encrypts
+ * the data using the current write cipher.
+ */
+int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
+		      size_t buf_size, const u8 *payload, size_t payload_len,
+		      size_t *out_len)
+{
+	u8 *pos, *ct_start, *length, *cpayload;
+	struct crypto_hash *hmac;
+	size_t clen;
+	int explicit_iv;
+
+	pos = buf;
+	if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size)
+		return -1;
+
+	/* ContentType type */
+	ct_start = pos;
+	*pos++ = content_type;
+	/* ProtocolVersion version */
+	WPA_PUT_BE16(pos, rl->tls_version);
+	pos += 2;
+	/* uint16 length */
+	length = pos;
+	WPA_PUT_BE16(length, payload_len);
+	pos += 2;
+
+	cpayload = pos;
+	explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL &&
+		rl->iv_size && rl->tls_version >= TLS_VERSION_1_1;
+	if (explicit_iv) {
+		/* opaque IV[Cipherspec.block_length] */
+		if (pos + rl->iv_size > buf + buf_size)
+			return -1;
+
+		/*
+		 * Use random number R per the RFC 4346, 6.2.3.2 CBC Block
+		 * Cipher option 2a.
+		 */
+
+		if (os_get_random(pos, rl->iv_size))
+			return -1;
+		pos += rl->iv_size;
+	}
+
+	/*
+	 * opaque fragment[TLSPlaintext.length]
+	 * (opaque content[TLSCompressed.length] in GenericBlockCipher)
+	 */
+	if (pos + payload_len > buf + buf_size)
+		return -1;
+	os_memmove(pos, payload, payload_len);
+	pos += payload_len;
+
+	if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+		/*
+		 * MAC calculated over seq_num + TLSCompressed.type +
+		 * TLSCompressed.version + TLSCompressed.length +
+		 * TLSCompressed.fragment
+		 */
+		hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret,
+					rl->hash_size);
+		if (hmac == NULL) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+				   "to initialize HMAC");
+			return -1;
+		}
+		crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN);
+		/* type + version + length + fragment */
+		crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN);
+		crypto_hash_update(hmac, payload, payload_len);
+		clen = buf + buf_size - pos;
+		if (clen < rl->hash_size) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not "
+				   "enough room for MAC");
+			crypto_hash_finish(hmac, NULL, NULL);
+			return -1;
+		}
+
+		if (crypto_hash_finish(hmac, pos, &clen) < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+				   "to calculate HMAC");
+			return -1;
+		}
+		wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC",
+			    pos, clen);
+		pos += clen;
+		if (rl->iv_size) {
+			size_t len = pos - cpayload;
+			size_t pad;
+			pad = (len + 1) % rl->iv_size;
+			if (pad)
+				pad = rl->iv_size - pad;
+			if (pos + pad + 1 > buf + buf_size) {
+				wpa_printf(MSG_DEBUG, "TLSv1: No room for "
+					   "block cipher padding");
+				return -1;
+			}
+			os_memset(pos, pad, pad + 1);
+			pos += pad + 1;
+		}
+
+		if (crypto_cipher_encrypt(rl->write_cbc, cpayload,
+					  cpayload, pos - cpayload) < 0)
+			return -1;
+	}
+
+	WPA_PUT_BE16(length, pos - length - 2);
+	inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN);
+
+	*out_len = pos - buf;
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_record_receive - TLS record layer: Process a received message
+ * @rl: Pointer to TLS record layer data
+ * @in_data: Received data
+ * @in_len: Length of the received data
+ * @out_data: Buffer for output data (must be at least as long as in_data)
+ * @out_len: Set to maximum out_data length by caller; used to return the
+ * length of the used data
+ * @alert: Buffer for returning an alert value on failure
+ * Returns: Number of bytes used from in_data on success, 0 if record was not
+ *	complete (more data needed), or -1 on failure
+ *
+ * This function decrypts the received message, verifies HMAC and TLS record
+ * layer header.
+ */
+int tlsv1_record_receive(struct tlsv1_record_layer *rl,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t *out_len, u8 *alert)
+{
+	size_t i, rlen, hlen;
+	u8 padlen;
+	struct crypto_hash *hmac;
+	u8 len[2], hash[100];
+	int force_mac_error = 0;
+	u8 ct;
+
+	if (in_len < TLS_RECORD_HEADER_LEN) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - "
+			   "need more data",
+			   (unsigned long) in_len);
+		wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
+			    in_data, in_len);
+		return 0;
+	}
+
+	ct = in_data[0];
+	rlen = WPA_GET_BE16(in_data + 3);
+	wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d "
+		   "length %d", ct, in_data[1], in_data[2], (int) rlen);
+
+	/*
+	 * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the
+	 * protocol version in record layer. As such, accept any {03,xx} value
+	 * to remain compatible with existing implementations.
+	 */
+	if (in_data[1] != 0x03) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version "
+			   "%u.%u", in_data[1], in_data[2]);
+		*alert = TLS_ALERT_PROTOCOL_VERSION;
+		return -1;
+	}
+
+	/* TLSCiphertext must not be more than 2^14+2048 bytes */
+	if (TLS_RECORD_HEADER_LEN + rlen > 18432) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
+			   (unsigned long) (TLS_RECORD_HEADER_LEN + rlen));
+		*alert = TLS_ALERT_RECORD_OVERFLOW;
+		return -1;
+	}
+
+	in_data += TLS_RECORD_HEADER_LEN;
+	in_len -= TLS_RECORD_HEADER_LEN;
+
+	if (rlen > in_len) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included "
+			   "(rlen=%lu > in_len=%lu)",
+			   (unsigned long) rlen, (unsigned long) in_len);
+		return 0;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
+		    in_data, rlen);
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE &&
+	    ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
+	    ct != TLS_CONTENT_TYPE_ALERT &&
+	    ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown "
+			   "content type 0x%x", ct);
+		*alert = TLS_ALERT_UNEXPECTED_MESSAGE;
+		return -1;
+	}
+
+	in_len = rlen;
+
+	if (*out_len < in_len) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for "
+			   "processing received record");
+		*alert = TLS_ALERT_INTERNAL_ERROR;
+		return -1;
+	}
+
+	if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+		size_t plen;
+		if (crypto_cipher_decrypt(rl->read_cbc, in_data,
+					  out_data, in_len) < 0) {
+			*alert = TLS_ALERT_DECRYPTION_FAILED;
+			return -1;
+		}
+		plen = in_len;
+		wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted "
+				"data", out_data, plen);
+
+		if (rl->iv_size) {
+			/*
+			 * TLS v1.0 defines different alert values for various
+			 * failures. That may information to aid in attacks, so
+			 * use the same bad_record_mac alert regardless of the
+			 * issues.
+			 *
+			 * In addition, instead of returning immediately on
+			 * error, run through the MAC check to make timing
+			 * attacks more difficult.
+			 */
+
+			if (rl->tls_version >= TLS_VERSION_1_1) {
+				/* Remove opaque IV[Cipherspec.block_length] */
+				if (plen < rl->iv_size) {
+					wpa_printf(MSG_DEBUG, "TLSv1.1: Not "
+						   "enough room for IV");
+					force_mac_error = 1;
+					goto check_mac;
+				}
+				os_memmove(out_data, out_data + rl->iv_size,
+					   plen - rl->iv_size);
+				plen -= rl->iv_size;
+			}
+
+			/* Verify and remove padding */
+			if (plen == 0) {
+				wpa_printf(MSG_DEBUG, "TLSv1: Too short record"
+					   " (no pad)");
+				force_mac_error = 1;
+				goto check_mac;
+			}
+			padlen = out_data[plen - 1];
+			if (padlen >= plen) {
+				wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad "
+					   "length (%u, plen=%lu) in "
+					   "received record",
+					   padlen, (unsigned long) plen);
+				force_mac_error = 1;
+				goto check_mac;
+			}
+			for (i = plen - padlen - 1; i < plen - 1; i++) {
+				if (out_data[i] != padlen) {
+					wpa_hexdump(MSG_DEBUG,
+						    "TLSv1: Invalid pad in "
+						    "received record",
+						    out_data + plen - padlen -
+						    1, padlen + 1);
+					force_mac_error = 1;
+					goto check_mac;
+				}
+			}
+
+			plen -= padlen + 1;
+
+			wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - "
+					"Decrypted data with IV and padding "
+					"removed", out_data, plen);
+		}
+
+	check_mac:
+		if (plen < rl->hash_size) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no "
+				   "hash value");
+			*alert = TLS_ALERT_BAD_RECORD_MAC;
+			return -1;
+		}
+
+		plen -= rl->hash_size;
+
+		hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret,
+					rl->hash_size);
+		if (hmac == NULL) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+				   "to initialize HMAC");
+			*alert = TLS_ALERT_INTERNAL_ERROR;
+			return -1;
+		}
+
+		crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN);
+		/* type + version + length + fragment */
+		crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3);
+		WPA_PUT_BE16(len, plen);
+		crypto_hash_update(hmac, len, 2);
+		crypto_hash_update(hmac, out_data, plen);
+		hlen = sizeof(hash);
+		if (crypto_hash_finish(hmac, hash, &hlen) < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+				   "to calculate HMAC");
+			*alert = TLS_ALERT_INTERNAL_ERROR;
+			return -1;
+		}
+		if (hlen != rl->hash_size ||
+		    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)",
+				   force_mac_error);
+			*alert = TLS_ALERT_BAD_RECORD_MAC;
+			return -1;
+		}
+
+		*out_len = plen;
+	} else {
+		os_memcpy(out_data, in_data, in_len);
+		*out_len = in_len;
+	}
+
+	/* TLSCompressed must not be more than 2^14+1024 bytes */
+	if (TLS_RECORD_HEADER_LEN + *out_len > 17408) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
+			   (unsigned long) (TLS_RECORD_HEADER_LEN + *out_len));
+		*alert = TLS_ALERT_RECORD_OVERFLOW;
+		return -1;
+	}
+
+	inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN);
+
+	return TLS_RECORD_HEADER_LEN + rlen;
+}
diff --git a/hostap/src/tls/tlsv1_record.h b/hostap/src/tls/tlsv1_record.h
new file mode 100644
index 0000000..48abcb0
--- /dev/null
+++ b/hostap/src/tls/tlsv1_record.h
@@ -0,0 +1,71 @@
+/*
+ * TLSv1 Record Protocol
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLSV1_RECORD_H
+#define TLSV1_RECORD_H
+
+#include "crypto/crypto.h"
+
+#define TLS_MAX_WRITE_MAC_SECRET_LEN 32
+#define TLS_MAX_WRITE_KEY_LEN 32
+#define TLS_MAX_IV_LEN 16
+#define TLS_MAX_KEY_BLOCK_LEN (2 * (TLS_MAX_WRITE_MAC_SECRET_LEN + \
+				    TLS_MAX_WRITE_KEY_LEN + TLS_MAX_IV_LEN))
+
+#define TLS_SEQ_NUM_LEN 8
+#define TLS_RECORD_HEADER_LEN 5
+
+/* ContentType */
+enum {
+	TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC = 20,
+	TLS_CONTENT_TYPE_ALERT = 21,
+	TLS_CONTENT_TYPE_HANDSHAKE = 22,
+	TLS_CONTENT_TYPE_APPLICATION_DATA = 23
+};
+
+struct tlsv1_record_layer {
+	u16 tls_version;
+
+	u8 write_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN];
+	u8 read_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN];
+	u8 write_key[TLS_MAX_WRITE_KEY_LEN];
+	u8 read_key[TLS_MAX_WRITE_KEY_LEN];
+	u8 write_iv[TLS_MAX_IV_LEN];
+	u8 read_iv[TLS_MAX_IV_LEN];
+
+	size_t hash_size;
+	size_t key_material_len;
+	size_t iv_size; /* also block_size */
+
+	enum crypto_hash_alg hash_alg;
+	enum crypto_cipher_alg cipher_alg;
+
+	u8 write_seq_num[TLS_SEQ_NUM_LEN];
+	u8 read_seq_num[TLS_SEQ_NUM_LEN];
+
+	u16 cipher_suite;
+	u16 write_cipher_suite;
+	u16 read_cipher_suite;
+
+	struct crypto_cipher *write_cbc;
+	struct crypto_cipher *read_cbc;
+};
+
+
+int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
+				  u16 cipher_suite);
+int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl);
+int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl);
+int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
+		      size_t buf_size, const u8 *payload, size_t payload_len,
+		      size_t *out_len);
+int tlsv1_record_receive(struct tlsv1_record_layer *rl,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t *out_len, u8 *alert);
+
+#endif /* TLSV1_RECORD_H */
diff --git a/hostap/src/tls/tlsv1_server.c b/hostap/src/tls/tlsv1_server.c
new file mode 100644
index 0000000..ba47337
--- /dev/null
+++ b/hostap/src/tls/tlsv1_server.c
@@ -0,0 +1,822 @@
+/*
+ * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_server.h"
+#include "tlsv1_server_i.h"
+
+/* TODO:
+ * Support for a message fragmented across several records (RFC 2246, 6.2.1)
+ */
+
+
+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;
+	conn->alert_description = description;
+}
+
+
+int tlsv1_server_derive_keys(struct tlsv1_server *conn,
+			     const u8 *pre_master_secret,
+			     size_t pre_master_secret_len)
+{
+	u8 seed[2 * TLS_RANDOM_LEN];
+	u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
+	u8 *pos;
+	size_t key_block_len;
+
+	if (pre_master_secret) {
+		wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
+				pre_master_secret, pre_master_secret_len);
+		os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+		os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+			  TLS_RANDOM_LEN);
+		if (tls_prf(conn->rl.tls_version,
+			    pre_master_secret, pre_master_secret_len,
+			    "master secret", seed, 2 * TLS_RANDOM_LEN,
+			    conn->master_secret, TLS_MASTER_SECRET_LEN)) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
+				   "master_secret");
+			return -1;
+		}
+		wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
+				conn->master_secret, TLS_MASTER_SECRET_LEN);
+	}
+
+	os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+	os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
+	key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+			     conn->rl.iv_size);
+	if (tls_prf(conn->rl.tls_version,
+		    conn->master_secret, TLS_MASTER_SECRET_LEN,
+		    "key expansion", seed, 2 * TLS_RANDOM_LEN,
+		    key_block, key_block_len)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
+			key_block, key_block_len);
+
+	pos = key_block;
+
+	/* client_write_MAC_secret */
+	os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
+	pos += conn->rl.hash_size;
+	/* server_write_MAC_secret */
+	os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
+	pos += conn->rl.hash_size;
+
+	/* client_write_key */
+	os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
+	pos += conn->rl.key_material_len;
+	/* server_write_key */
+	os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
+	pos += conn->rl.key_material_len;
+
+	/* client_write_IV */
+	os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
+	pos += conn->rl.iv_size;
+	/* server_write_IV */
+	os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
+	pos += conn->rl.iv_size;
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_server_handshake - Process TLS handshake
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Input data from TLS peer
+ * @in_len: Input data length
+ * @out_len: Length of the output buffer.
+ * Returns: Pointer to output data, %NULL on failure
+ */
+u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
+			    const u8 *in_data, size_t in_len,
+			    size_t *out_len)
+{
+	const u8 *pos, *end;
+	u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
+	size_t in_msg_len;
+	int used;
+
+	if (in_data == NULL || in_len == 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No input data to server");
+		return NULL;
+	}
+
+	pos = in_data;
+	end = in_data + in_len;
+	in_msg = os_malloc(in_len);
+	if (in_msg == NULL)
+		return NULL;
+
+	/* Each received packet may include multiple records */
+	while (pos < end) {
+		in_msg_len = in_len;
+		used = tlsv1_record_receive(&conn->rl, pos, end - pos,
+					    in_msg, &in_msg_len, &alert);
+		if (used < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
+				   "record failed");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+			goto failed;
+		}
+		if (used == 0) {
+			/* need more data */
+			wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
+				   "yet supported");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+			goto failed;
+		}
+		ct = pos[0];
+
+		in_pos = in_msg;
+		in_end = in_msg + in_msg_len;
+
+		/* Each received record may include multiple messages of the
+		 * same ContentType. */
+		while (in_pos < in_end) {
+			in_msg_len = in_end - in_pos;
+			if (tlsv1_server_process_handshake(conn, ct, in_pos,
+							   &in_msg_len) < 0)
+				goto failed;
+			in_pos += in_msg_len;
+		}
+
+		pos += used;
+	}
+
+	os_free(in_msg);
+	in_msg = NULL;
+
+	msg = tlsv1_server_handshake_write(conn, out_len);
+
+failed:
+	os_free(in_msg);
+	if (conn->alert_level) {
+		if (conn->state == FAILED) {
+			/* Avoid alert loops */
+			wpa_printf(MSG_DEBUG, "TLSv1: Drop alert loop");
+			os_free(msg);
+			return NULL;
+		}
+		conn->state = FAILED;
+		os_free(msg);
+		msg = tlsv1_server_send_alert(conn, conn->alert_level,
+					      conn->alert_description,
+					      out_len);
+	}
+
+	return msg;
+}
+
+
+/**
+ * tlsv1_server_encrypt - Encrypt data into TLS tunnel
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Pointer to plaintext data to be encrypted
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (encrypted TLS data)
+ * @out_len: Maximum out_data length 
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel.
+ */
+int tlsv1_server_encrypt(struct tlsv1_server *conn,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t out_len)
+{
+	size_t rlen;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
+			in_data, in_len);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
+			      out_data, out_len, in_data, in_len, &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	return rlen;
+}
+
+
+/**
+ * tlsv1_server_decrypt - Decrypt data from TLS tunnel
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Pointer to input buffer (encrypted TLS data)
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
+ * @out_len: Maximum out_data length
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel.
+ */
+int tlsv1_server_decrypt(struct tlsv1_server *conn,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t out_len)
+{
+	const u8 *in_end, *pos;
+	int used;
+	u8 alert, *out_end, *out_pos, ct;
+	size_t olen;
+
+	pos = in_data;
+	in_end = in_data + in_len;
+	out_pos = out_data;
+	out_end = out_data + out_len;
+
+	while (pos < in_end) {
+		ct = pos[0];
+		olen = out_end - out_pos;
+		used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+					    out_pos, &olen, &alert);
+		if (used < 0) {
+			tlsv1_server_log(conn, "Record layer processing failed");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+			return -1;
+		}
+		if (used == 0) {
+			/* need more data */
+			wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
+				   "yet supported");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+			return -1;
+		}
+
+		if (ct == TLS_CONTENT_TYPE_ALERT) {
+			if (olen < 2) {
+				tlsv1_server_log(conn, "Alert underflow");
+				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+						   TLS_ALERT_DECODE_ERROR);
+				return -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;
+				continue;
+			}
+
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   out_pos[1]);
+			return -1;
+		}
+
+		if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+			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 "
+				   "for processing the received record");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+
+		pos += used;
+	}
+
+	return out_pos - out_data;
+}
+
+
+/**
+ * tlsv1_server_global_init - Initialize TLSv1 server
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before using any other TLSv1 server functions.
+ */
+int tlsv1_server_global_init(void)
+{
+	return crypto_global_init();
+}
+
+
+/**
+ * tlsv1_server_global_deinit - Deinitialize TLSv1 server
+ *
+ * This function can be used to deinitialize the TLSv1 server that was
+ * initialized by calling tlsv1_server_global_init(). No TLSv1 server functions
+ * can be called after this before calling tlsv1_server_global_init() again.
+ */
+void tlsv1_server_global_deinit(void)
+{
+	crypto_global_deinit();
+}
+
+
+/**
+ * tlsv1_server_init - Initialize TLSv1 server connection
+ * @cred: Pointer to server credentials from tlsv1_server_cred_alloc()
+ * Returns: Pointer to TLSv1 server connection data or %NULL on failure
+ */
+struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred)
+{
+	struct tlsv1_server *conn;
+	size_t count;
+	u16 *suites;
+
+	conn = os_zalloc(sizeof(*conn));
+	if (conn == NULL)
+		return NULL;
+
+	conn->cred = cred;
+
+	conn->state = CLIENT_HELLO;
+
+	if (tls_verify_hash_init(&conn->verify) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
+			   "hash");
+		os_free(conn);
+		return NULL;
+	}
+
+	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;
+	conn->num_cipher_suites = count;
+
+	return conn;
+}
+
+
+static void tlsv1_server_clear_data(struct tlsv1_server *conn)
+{
+	tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+	tlsv1_record_change_write_cipher(&conn->rl);
+	tlsv1_record_change_read_cipher(&conn->rl);
+	tls_verify_hash_free(&conn->verify);
+
+	crypto_public_key_free(conn->client_rsa_key);
+	conn->client_rsa_key = NULL;
+
+	os_free(conn->session_ticket);
+	conn->session_ticket = NULL;
+	conn->session_ticket_len = 0;
+	conn->use_session_ticket = 0;
+
+	os_free(conn->dh_secret);
+	conn->dh_secret = NULL;
+	conn->dh_secret_len = 0;
+}
+
+
+/**
+ * tlsv1_server_deinit - Deinitialize TLSv1 server connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ */
+void tlsv1_server_deinit(struct tlsv1_server *conn)
+{
+	tlsv1_server_clear_data(conn);
+	os_free(conn);
+}
+
+
+/**
+ * tlsv1_server_established - Check whether connection has been established
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 1 if connection is established, 0 if not
+ */
+int tlsv1_server_established(struct tlsv1_server *conn)
+{
+	return conn->state == ESTABLISHED;
+}
+
+
+/**
+ * tlsv1_server_prf - Use TLS-PRF to derive keying material
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @label: Label (e.g., description of the key) for PRF
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+		     int server_random_first, u8 *out, size_t out_len)
+{
+	u8 seed[2 * TLS_RANDOM_LEN];
+
+	if (conn->state != ESTABLISHED)
+		return -1;
+
+	if (server_random_first) {
+		os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+		os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
+			  TLS_RANDOM_LEN);
+	} else {
+		os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+		os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+			  TLS_RANDOM_LEN);
+	}
+
+	return tls_prf(conn->rl.tls_version,
+		       conn->master_secret, TLS_MASTER_SECRET_LEN,
+		       label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
+}
+
+
+/**
+ * tlsv1_server_get_cipher - Get current cipher name
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
+			    size_t buflen)
+{
+	char *cipher;
+
+	switch (conn->rl.cipher_suite) {
+	case TLS_RSA_WITH_RC4_128_MD5:
+		cipher = "RC4-MD5";
+		break;
+	case TLS_RSA_WITH_RC4_128_SHA:
+		cipher = "RC4-SHA";
+		break;
+	case TLS_RSA_WITH_DES_CBC_SHA:
+		cipher = "DES-CBC-SHA";
+		break;
+	case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+		cipher = "DES-CBC3-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_DES_CBC_SHA:
+		cipher = "DHE-RSA-DES-CBC-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+		cipher = "DHE-RSA-DES-CBC3-SHA";
+		break;
+	case TLS_DH_anon_WITH_RC4_128_MD5:
+		cipher = "ADH-RC4-MD5";
+		break;
+	case TLS_DH_anon_WITH_DES_CBC_SHA:
+		cipher = "ADH-DES-SHA";
+		break;
+	case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+		cipher = "ADH-DES-CBC3-SHA";
+		break;
+	case TLS_RSA_WITH_AES_128_CBC_SHA:
+		cipher = "AES-128-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+		cipher = "DHE-RSA-AES-128-SHA";
+		break;
+	case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+		cipher = "ADH-AES-128-SHA";
+		break;
+	case TLS_RSA_WITH_AES_256_CBC_SHA:
+		cipher = "AES-256-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+		cipher = "DHE-RSA-AES-256-SHA";
+		break;
+	case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+		cipher = "ADH-AES-256-SHA";
+		break;
+	case TLS_RSA_WITH_AES_128_CBC_SHA256:
+		cipher = "AES-128-SHA256";
+		break;
+	case TLS_RSA_WITH_AES_256_CBC_SHA256:
+		cipher = "AES-256-SHA256";
+		break;
+	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+		cipher = "DHE-RSA-AES-128-SHA256";
+		break;
+	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+		cipher = "DHE-RSA-AES-256-SHA256";
+		break;
+	case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+		cipher = "ADH-AES-128-SHA256";
+		break;
+	case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+		cipher = "ADH-AES-256-SHA256";
+		break;
+	default:
+		return -1;
+	}
+
+	if (os_strlcpy(buf, cipher, buflen) >= buflen)
+		return -1;
+	return 0;
+}
+
+
+/**
+ * tlsv1_server_shutdown - Shutdown TLS connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_shutdown(struct tlsv1_server *conn)
+{
+	conn->state = CLIENT_HELLO;
+
+	if (tls_verify_hash_init(&conn->verify) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
+			   "hash");
+		return -1;
+	}
+
+	tlsv1_server_clear_data(conn);
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_server_resumed - Was session resumption used
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tlsv1_server_resumed(struct tlsv1_server *conn)
+{
+	return 0;
+}
+
+
+/**
+ * tlsv1_server_get_random - Get random data from TLS connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @keys: Structure of random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *keys)
+{
+	os_memset(keys, 0, sizeof(*keys));
+	if (conn->state == CLIENT_HELLO)
+		return -1;
+
+	keys->client_random = conn->client_random;
+	keys->client_random_len = TLS_RANDOM_LEN;
+
+	if (conn->state != SERVER_HELLO) {
+		keys->server_random = conn->server_random;
+		keys->server_random_len = TLS_RANDOM_LEN;
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_server_get_keyblock_size - Get TLS key_block size
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: Size of the key_block for the negotiated cipher suite or -1 on
+ * failure
+ */
+int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn)
+{
+	if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
+		return -1;
+
+	return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+		    conn->rl.iv_size);
+}
+
+
+/**
+ * tlsv1_server_set_cipher_list - Configure acceptable cipher suites
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers)
+{
+	size_t count;
+	u16 *suites;
+
+	/* TODO: implement proper configuration of cipher suites */
+	if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
+		count = 0;
+		suites = conn->cipher_suites;
+		suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+		suites[count++] = TLS_RSA_WITH_AES_128_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;
+		suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
+		suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+		suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
+		suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
+		suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
+		conn->num_cipher_suites = count;
+	}
+
+	return 0;
+}
+
+
+int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer)
+{
+	conn->verify_peer = verify_peer;
+	return 0;
+}
+
+
+void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
+					tlsv1_server_session_ticket_cb cb,
+					void *ctx)
+{
+	wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
+		   cb, ctx);
+	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/hostap/src/tls/tlsv1_server.h b/hostap/src/tls/tlsv1_server.h
new file mode 100644
index 0000000..10e7699
--- /dev/null
+++ b/hostap/src/tls/tlsv1_server.h
@@ -0,0 +1,53 @@
+/*
+ * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLSV1_SERVER_H
+#define TLSV1_SERVER_H
+
+#include "tlsv1_cred.h"
+
+struct tlsv1_server;
+
+int tlsv1_server_global_init(void);
+void tlsv1_server_global_deinit(void);
+struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred);
+void tlsv1_server_deinit(struct tlsv1_server *conn);
+int tlsv1_server_established(struct tlsv1_server *conn);
+int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+		     int server_random_first, u8 *out, size_t out_len);
+u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
+			    const u8 *in_data, size_t in_len, size_t *out_len);
+int tlsv1_server_encrypt(struct tlsv1_server *conn,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t out_len);
+int tlsv1_server_decrypt(struct tlsv1_server *conn,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t out_len);
+int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
+			    size_t buflen);
+int tlsv1_server_shutdown(struct tlsv1_server *conn);
+int tlsv1_server_resumed(struct tlsv1_server *conn);
+int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *data);
+int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn);
+int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers);
+int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer);
+
+typedef int (*tlsv1_server_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
+					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/hostap/src/tls/tlsv1_server_i.h b/hostap/src/tls/tlsv1_server_i.h
new file mode 100644
index 0000000..96d79b3
--- /dev/null
+++ b/hostap/src/tls/tlsv1_server_i.h
@@ -0,0 +1,84 @@
+/*
+ * TLSv1 server - internal structures
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLSV1_SERVER_I_H
+#define TLSV1_SERVER_I_H
+
+struct tlsv1_server {
+	enum {
+		CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE,
+		SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST,
+		SERVER_HELLO_DONE, CLIENT_CERTIFICATE, CLIENT_KEY_EXCHANGE,
+		CERTIFICATE_VERIFY, CHANGE_CIPHER_SPEC, CLIENT_FINISHED,
+		SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED,
+		ESTABLISHED, FAILED
+	} state;
+
+	struct tlsv1_record_layer rl;
+
+	u8 session_id[TLS_SESSION_ID_MAX_LEN];
+	size_t session_id_len;
+	u8 client_random[TLS_RANDOM_LEN];
+	u8 server_random[TLS_RANDOM_LEN];
+	u8 master_secret[TLS_MASTER_SECRET_LEN];
+
+	u8 alert_level;
+	u8 alert_description;
+
+	struct crypto_public_key *client_rsa_key;
+
+	struct tls_verify_hash verify;
+
+#define MAX_CIPHER_COUNT 30
+	u16 cipher_suites[MAX_CIPHER_COUNT];
+	size_t num_cipher_suites;
+
+	u16 cipher_suite;
+
+	struct tlsv1_credentials *cred;
+
+	int verify_peer;
+	u16 client_version;
+
+	u8 *session_ticket;
+	size_t session_ticket_len;
+
+	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,
+			     size_t pre_master_secret_len);
+u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len);
+u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
+			     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/hostap/src/tls/tlsv1_server_read.c b/hostap/src/tls/tlsv1_server_read.c
new file mode 100644
index 0000000..0f237ba
--- /dev/null
+++ b/hostap/src/tls/tlsv1_server_read.c
@@ -0,0 +1,1205 @@
+/*
+ * TLSv1 server - read handshake message
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/tls.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_server.h"
+#include "tlsv1_server_i.h"
+
+
+static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len);
+static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
+					  u8 ct, const u8 *in_data,
+					  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)
+{
+	const u8 *pos, *end, *c;
+	size_t left, len, i, j;
+	u16 cipher_suite;
+	u16 num_suites;
+	int compr_null_found;
+	u16 ext_type, ext_len;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		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;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4)
+		goto decode_error;
+
+	/* HandshakeType msg_type */
+	if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
+		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;
+	}
+	tlsv1_server_log(conn, "Received ClientHello");
+	pos++;
+	/* uint24 length */
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left)
+		goto decode_error;
+
+	/* body - ClientHello */
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello", pos, len);
+	end = pos + len;
+
+	/* ProtocolVersion client_version */
+	if (end - pos < 2)
+		goto decode_error;
+	conn->client_version = WPA_GET_BE16(pos);
+	tlsv1_server_log(conn, "Client version %d.%d",
+			 conn->client_version >> 8,
+			 conn->client_version & 0xff);
+	if (conn->client_version < TLS_VERSION_1) {
+		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;
+	}
+	pos += 2;
+
+	if (TLS_VERSION == TLS_VERSION_1)
+		conn->rl.tls_version = TLS_VERSION_1;
+#ifdef CONFIG_TLSV12
+	else if (conn->client_version >= TLS_VERSION_1_2)
+		conn->rl.tls_version = TLS_VERSION_1_2;
+#endif /* CONFIG_TLSV12 */
+	else if (conn->client_version > TLS_VERSION_1_1)
+		conn->rl.tls_version = TLS_VERSION_1_1;
+	else
+		conn->rl.tls_version = conn->client_version;
+	tlsv1_server_log(conn, "Using TLS v%s",
+			 tls_version_str(conn->rl.tls_version));
+
+	/* Random random */
+	if (end - pos < TLS_RANDOM_LEN)
+		goto decode_error;
+
+	os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN);
+	pos += TLS_RANDOM_LEN;
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
+		    conn->client_random, TLS_RANDOM_LEN);
+
+	/* SessionID session_id */
+	if (end - pos < 1)
+		goto decode_error;
+	if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN)
+		goto decode_error;
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos);
+	pos += 1 + *pos;
+	/* TODO: add support for session resumption */
+
+	/* CipherSuite cipher_suites<2..2^16-1> */
+	if (end - pos < 2)
+		goto decode_error;
+	num_suites = WPA_GET_BE16(pos);
+	pos += 2;
+	if (end - pos < num_suites)
+		goto decode_error;
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites",
+		    pos, num_suites);
+	if (num_suites & 1)
+		goto decode_error;
+	num_suites /= 2;
+
+	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);
+			c += 2;
+			if (!cipher_suite && tmp == conn->cipher_suites[i]) {
+				cipher_suite = tmp;
+				break;
+			}
+		}
+	}
+	pos += num_suites * 2;
+	if (!cipher_suite) {
+		tlsv1_server_log(conn, "No supported cipher suite available");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_ILLEGAL_PARAMETER);
+		return -1;
+	}
+
+	if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for "
+			   "record layer");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	conn->cipher_suite = cipher_suite;
+
+	/* CompressionMethod compression_methods<1..2^8-1> */
+	if (end - pos < 1)
+		goto decode_error;
+	num_suites = *pos++;
+	if (end - pos < num_suites)
+		goto decode_error;
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods",
+		    pos, num_suites);
+	compr_null_found = 0;
+	for (i = 0; i < num_suites; i++) {
+		if (*pos++ == TLS_COMPRESSION_NULL)
+			compr_null_found = 1;
+	}
+	if (!compr_null_found) {
+		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) {
+		tlsv1_server_log(conn, "Unexpected extra octet in the end of ClientHello: 0x%02x",
+				 *pos);
+		goto decode_error;
+	}
+
+	if (end - pos >= 2) {
+		/* Extension client_hello_extension_list<0..2^16-1> */
+		ext_len = WPA_GET_BE16(pos);
+		pos += 2;
+
+		tlsv1_server_log(conn, "%u bytes of ClientHello extensions",
+				 ext_len);
+		if (end - pos != ext_len) {
+			tlsv1_server_log(conn, "Invalid ClientHello extension list length %u (expected %u)",
+					 ext_len, (unsigned int) (end - pos));
+			goto decode_error;
+		}
+
+		/*
+		 * struct {
+		 *   ExtensionType extension_type (0..65535)
+		 *   opaque extension_data<0..2^16-1>
+		 * } Extension;
+		 */
+
+		while (pos < end) {
+			if (end - pos < 2) {
+				tlsv1_server_log(conn, "Invalid extension_type field");
+				goto decode_error;
+			}
+
+			ext_type = WPA_GET_BE16(pos);
+			pos += 2;
+
+			if (end - pos < 2) {
+				tlsv1_server_log(conn, "Invalid extension_data length field");
+				goto decode_error;
+			}
+
+			ext_len = WPA_GET_BE16(pos);
+			pos += 2;
+
+			if (end - pos < ext_len) {
+				tlsv1_server_log(conn, "Invalid extension_data field");
+				goto decode_error;
+			}
+
+			tlsv1_server_log(conn, "ClientHello Extension type %u",
+					 ext_type);
+			wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello "
+				    "Extension data", pos, ext_len);
+
+			if (ext_type == TLS_EXT_SESSION_TICKET) {
+				os_free(conn->session_ticket);
+				conn->session_ticket = os_malloc(ext_len);
+				if (conn->session_ticket) {
+					os_memcpy(conn->session_ticket, pos,
+						  ext_len);
+					conn->session_ticket_len = ext_len;
+				}
+			}
+
+			pos += ext_len;
+		}
+	}
+
+	*in_len = end - in_data;
+
+	tlsv1_server_log(conn, "ClientHello OK - proceed to ServerHello");
+	conn->state = SERVER_HELLO;
+
+	return 0;
+
+decode_error:
+	tlsv1_server_log(conn, "Failed to decode ClientHello");
+	tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			   TLS_ALERT_DECODE_ERROR);
+	return -1;
+}
+
+
+static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
+				   const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len, list_len, cert_len, idx;
+	u8 type;
+	struct x509_certificate *chain = NULL, *last = NULL, *cert;
+	int reason;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		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;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		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;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > 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;
+	}
+
+	if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
+		if (conn->verify_peer) {
+			tlsv1_server_log(conn, "Client did not include Certificate");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_UNEXPECTED_MESSAGE);
+			return -1;
+		}
+
+		return tls_process_client_key_exchange(conn, ct, in_data,
+						       in_len);
+	}
+	if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
+		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;
+	}
+
+	tlsv1_server_log(conn, "Received Certificate (certificate_list len %lu)",
+			 (unsigned long) len);
+
+	/*
+	 * opaque ASN.1Cert<2^24-1>;
+	 *
+	 * struct {
+	 *     ASN.1Cert certificate_list<1..2^24-1>;
+	 * } Certificate;
+	 */
+
+	end = pos + len;
+
+	if (end - pos < 3) {
+		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;
+	}
+
+	list_len = WPA_GET_BE24(pos);
+	pos += 3;
+
+	if ((size_t) (end - pos) != list_len) {
+		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;
+	}
+
+	idx = 0;
+	while (pos < end) {
+		if (end - pos < 3) {
+			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);
+			return -1;
+		}
+
+		cert_len = WPA_GET_BE24(pos);
+		pos += 3;
+
+		if ((size_t) (end - pos) < cert_len) {
+			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;
+		}
+
+		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)) {
+				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);
+				return -1;
+			}
+		}
+
+		cert = x509_certificate_parse(pos, cert_len);
+		if (cert == NULL) {
+			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);
+			return -1;
+		}
+
+		if (last == NULL)
+			chain = cert;
+		else
+			last->next = cert;
+		last = cert;
+
+		idx++;
+		pos += cert_len;
+	}
+
+	if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
+					    &reason, 0) < 0) {
+		int tls_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;
+			break;
+		case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
+			tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
+			break;
+		case X509_VALIDATE_CERTIFICATE_REVOKED:
+			tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
+			break;
+		case X509_VALIDATE_CERTIFICATE_EXPIRED:
+			tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+			break;
+		case X509_VALIDATE_CERTIFICATE_UNKNOWN:
+			tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
+			break;
+		case X509_VALIDATE_UNKNOWN_CA:
+			tls_reason = TLS_ALERT_UNKNOWN_CA;
+			break;
+		default:
+			tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+			break;
+		}
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason);
+		x509_certificate_chain_free(chain);
+		return -1;
+	}
+
+	x509_certificate_chain_free(chain);
+
+	*in_len = end - in_data;
+
+	conn->state = CLIENT_KEY_EXCHANGE;
+
+	return 0;
+}
+
+
+static int tls_process_client_key_exchange_rsa(
+	struct tlsv1_server *conn, const u8 *pos, const u8 *end)
+{
+	u8 *out;
+	size_t outlen, outbuflen;
+	u16 encr_len;
+	int res;
+	int use_random = 0;
+
+	if (end - pos < 2) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	encr_len = WPA_GET_BE16(pos);
+	pos += 2;
+	if (pos + encr_len > end) {
+		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;
+	}
+
+	outbuflen = outlen = end - pos;
+	out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ?
+			outlen : TLS_PRE_MASTER_SECRET_LEN);
+	if (out == NULL) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	/*
+	 * struct {
+	 *   ProtocolVersion client_version;
+	 *   opaque random[46];
+	 * } PreMasterSecret;
+	 *
+	 * struct {
+	 *   public-key-encrypted PreMasterSecret pre_master_secret;
+	 * } EncryptedPreMasterSecret;
+	 */
+
+	/*
+	 * Note: To avoid Bleichenbacher attack, we do not report decryption or
+	 * parsing errors from EncryptedPreMasterSecret processing to the
+	 * client. Instead, a random pre-master secret is used to force the
+	 * handshake to fail.
+	 */
+
+	if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key,
+						 pos, encr_len,
+						 out, &outlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt "
+			   "PreMasterSecret (encr_len=%u outlen=%lu)",
+			   encr_len, (unsigned long) outlen);
+		use_random = 1;
+	}
+
+	if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) {
+		tlsv1_server_log(conn, "Unexpected PreMasterSecret length %lu",
+				 (unsigned long) outlen);
+		use_random = 1;
+	}
+
+	if (!use_random && WPA_GET_BE16(out) != conn->client_version) {
+		tlsv1_server_log(conn, "Client version in ClientKeyExchange does not match with version in ClientHello");
+		use_random = 1;
+	}
+
+	if (use_random) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Using random premaster secret "
+			   "to avoid revealing information about private key");
+		outlen = TLS_PRE_MASTER_SECRET_LEN;
+		if (os_get_random(out, outlen)) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+				   "data");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			os_free(out);
+			return -1;
+		}
+	}
+
+	res = tlsv1_server_derive_keys(conn, out, outlen);
+
+	/* Clear the pre-master secret since it is not needed anymore */
+	os_memset(out, 0, outbuflen);
+	os_free(out);
+
+	if (res) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int tls_process_client_key_exchange_dh(
+	struct tlsv1_server *conn, const u8 *pos, const u8 *end)
+{
+	const u8 *dh_yc;
+	u16 dh_yc_len;
+	u8 *shared;
+	size_t shared_len;
+	int res;
+	const u8 *dh_p;
+	size_t dh_p_len;
+
+	/*
+	 * struct {
+	 *   select (PublicValueEncoding) {
+	 *     case implicit: struct { };
+	 *     case explicit: opaque dh_Yc<1..2^16-1>;
+	 *   } dh_public;
+	 * } ClientDiffieHellmanPublic;
+	 */
+
+	tlsv1_server_log(conn, "ClientDiffieHellmanPublic received");
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic",
+		    pos, end - pos);
+
+	if (end == pos) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Implicit public value encoding "
+			   "not supported");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	if (end - pos < 3) {
+		tlsv1_server_log(conn, "Invalid client public value length");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	dh_yc_len = WPA_GET_BE16(pos);
+	dh_yc = pos + 2;
+
+	if (dh_yc_len > end - dh_yc) {
+		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;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
+		    dh_yc, dh_yc_len);
+
+	if (conn->cred == NULL || conn->cred->dh_p == NULL ||
+	    conn->dh_secret == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	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 "
+			   "DH");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	/* shared = Yc^secret mod p */
+	if (crypto_mod_exp(dh_yc, dh_yc_len, conn->dh_secret,
+			   conn->dh_secret_len, dh_p, dh_p_len,
+			   shared, &shared_len)) {
+		os_free(shared);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange",
+			shared, shared_len);
+
+	os_memset(conn->dh_secret, 0, conn->dh_secret_len);
+	os_free(conn->dh_secret);
+	conn->dh_secret = NULL;
+
+	res = tlsv1_server_derive_keys(conn, shared, shared_len);
+
+	/* Clear the pre-master secret since it is not needed anymore */
+	os_memset(shared, 0, shared_len);
+	os_free(shared);
+
+	if (res) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len;
+	u8 type;
+	tls_key_exchange keyx;
+	const struct tls_cipher_suite *suite;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		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;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		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;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > 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;
+	}
+
+	end = pos + len;
+
+	if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
+		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;
+	}
+
+	tlsv1_server_log(conn, "Received ClientKeyExchange");
+
+	wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len);
+
+	suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+	if (suite == NULL)
+		keyx = TLS_KEY_X_NULL;
+	else
+		keyx = suite->key_exchange;
+
+	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 && keyx != TLS_KEY_X_DHE_RSA &&
+	    tls_process_client_key_exchange_rsa(conn, pos, end) < 0)
+		return -1;
+
+	*in_len = end - in_data;
+
+	conn->state = CERTIFICATE_VERIFY;
+
+	return 0;
+}
+
+
+static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
+					  const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len;
+	u8 type;
+	size_t hlen;
+	u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos;
+	u8 alert;
+
+	if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+		if (conn->verify_peer) {
+			tlsv1_server_log(conn, "Client did not include CertificateVerify");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_UNEXPECTED_MESSAGE);
+			return -1;
+		}
+
+		return tls_process_change_cipher_spec(conn, ct, in_data,
+						      in_len);
+	}
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		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;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		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;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > 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;
+	}
+
+	end = pos + len;
+
+	if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) {
+		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;
+	}
+
+	tlsv1_server_log(conn, "Received CertificateVerify");
+
+	/*
+	 * struct {
+	 *   Signature signature;
+	 * } CertificateVerify;
+	 */
+
+	hpos = hash;
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version == TLS_VERSION_1_2) {
+		/*
+		 * 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) {
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_DECODE_ERROR);
+			return -1;
+		}
+		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]);
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		pos += 2;
+
+		hlen = SHA256_MAC_LEN;
+		if (conn->verify.sha256_cert == NULL ||
+		    crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
+		    0) {
+			conn->verify.sha256_cert = NULL;
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		conn->verify.sha256_cert = NULL;
+	} else {
+#endif /* CONFIG_TLSV12 */
+
+	hlen = MD5_MAC_LEN;
+	if (conn->verify.md5_cert == NULL ||
+	    crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		conn->verify.md5_cert = NULL;
+		crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+		conn->verify.sha1_cert = NULL;
+		return -1;
+	}
+	hpos += MD5_MAC_LEN;
+
+	conn->verify.md5_cert = NULL;
+	hlen = SHA1_MAC_LEN;
+	if (conn->verify.sha1_cert == NULL ||
+	    crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) {
+		conn->verify.sha1_cert = NULL;
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	conn->verify.sha1_cert = NULL;
+
+	hlen += MD5_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+	}
+#endif /* CONFIG_TLSV12 */
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
+
+	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;
+	}
+
+	*in_len = end - in_data;
+
+	conn->state = CHANGE_CIPHER_SPEC;
+
+	return 0;
+}
+
+
+static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
+					  u8 ct, const u8 *in_data,
+					  size_t *in_len)
+{
+	const u8 *pos;
+	size_t left;
+
+	if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+		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;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 1) {
+		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) {
+		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;
+	}
+
+	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");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	*in_len = pos + 1 - in_data;
+
+	conn->state = CLIENT_FINISHED;
+
+	return 0;
+}
+
+
+static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
+				       const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len, hlen;
+	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) {
+		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;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		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;
+	}
+
+	if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received "
+			   "type 0x%x", pos[0]);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	len = WPA_GET_BE24(pos + 1);
+
+	pos += 4;
+	left -= 4;
+
+	if (len > 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) {
+		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;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
+		    pos, TLS_VERIFY_DATA_LEN);
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+		hlen = SHA256_MAC_LEN;
+		if (conn->verify.sha256_client == NULL ||
+		    crypto_hash_finish(conn->verify.sha256_client, hash, &hlen)
+		    < 0) {
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			conn->verify.sha256_client = NULL;
+			return -1;
+		}
+		conn->verify.sha256_client = NULL;
+	} else {
+#endif /* CONFIG_TLSV12 */
+
+	hlen = MD5_MAC_LEN;
+	if (conn->verify.md5_client == NULL ||
+	    crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		conn->verify.md5_client = NULL;
+		crypto_hash_finish(conn->verify.sha1_client, NULL, NULL);
+		conn->verify.sha1_client = NULL;
+		return -1;
+	}
+	conn->verify.md5_client = NULL;
+	hlen = SHA1_MAC_LEN;
+	if (conn->verify.sha1_client == NULL ||
+	    crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN,
+			       &hlen) < 0) {
+		conn->verify.sha1_client = NULL;
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	conn->verify.sha1_client = NULL;
+	hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+	}
+#endif /* CONFIG_TLSV12 */
+
+	if (tls_prf(conn->rl.tls_version,
+		    conn->master_secret, TLS_MASTER_SECRET_LEN,
+		    "client finished", hash, hlen,
+		    verify_data, TLS_VERIFY_DATA_LEN)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECRYPT_ERROR);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
+			verify_data, TLS_VERIFY_DATA_LEN);
+
+	if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+		tlsv1_server_log(conn, "Mismatch in verify_data");
+		return -1;
+	}
+
+	tlsv1_server_log(conn, "Received Finished");
+
+	*in_len = end - in_data;
+
+	if (conn->use_session_ticket) {
+		/* Abbreviated handshake using session ticket; RFC 4507 */
+		tlsv1_server_log(conn, "Abbreviated handshake completed successfully");
+		conn->state = ESTABLISHED;
+	} else {
+		/* Full handshake */
+		conn->state = SERVER_CHANGE_CIPHER_SPEC;
+	}
+
+	return 0;
+}
+
+
+int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct,
+				   const u8 *buf, size_t *len)
+{
+	if (ct == TLS_CONTENT_TYPE_ALERT) {
+		if (*len < 2) {
+			tlsv1_server_log(conn, "Alert underflow");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_DECODE_ERROR);
+			return -1;
+		}
+		tlsv1_server_log(conn, "Received alert %d:%d", buf[0], buf[1]);
+		*len = 2;
+		conn->state = FAILED;
+		return -1;
+	}
+
+	switch (conn->state) {
+	case CLIENT_HELLO:
+		if (tls_process_client_hello(conn, ct, buf, len))
+			return -1;
+		break;
+	case CLIENT_CERTIFICATE:
+		if (tls_process_certificate(conn, ct, buf, len))
+			return -1;
+		break;
+	case CLIENT_KEY_EXCHANGE:
+		if (tls_process_client_key_exchange(conn, ct, buf, len))
+			return -1;
+		break;
+	case CERTIFICATE_VERIFY:
+		if (tls_process_certificate_verify(conn, ct, buf, len))
+			return -1;
+		break;
+	case CHANGE_CIPHER_SPEC:
+		if (tls_process_change_cipher_spec(conn, ct, buf, len))
+			return -1;
+		break;
+	case CLIENT_FINISHED:
+		if (tls_process_client_finished(conn, ct, buf, len))
+			return -1;
+		break;
+	default:
+		tlsv1_server_log(conn, "Unexpected state %d while processing received message",
+				 conn->state);
+		return -1;
+	}
+
+	if (ct == TLS_CONTENT_TYPE_HANDSHAKE)
+		tls_verify_hash_add(&conn->verify, buf, *len);
+
+	return 0;
+}
diff --git a/hostap/src/tls/tlsv1_server_write.c b/hostap/src/tls/tlsv1_server_write.c
new file mode 100644
index 0000000..15e6692
--- /dev/null
+++ b/hostap/src/tls/tlsv1_server_write.c
@@ -0,0 +1,940 @@
+/*
+ * TLSv1 server - write handshake message
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/tls.h"
+#include "crypto/random.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_server.h"
+#include "tlsv1_server_i.h"
+
+
+static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn)
+{
+	size_t len = 0;
+	struct x509_certificate *cert;
+
+	cert = conn->cred->cert;
+	while (cert) {
+		len += 3 + cert->cert_len;
+		if (x509_certificate_self_signed(cert))
+			break;
+		cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+						    &cert->issuer);
+	}
+
+	return len;
+}
+
+
+static int tls_write_server_hello(struct tlsv1_server *conn,
+				  u8 **msgpos, u8 *end)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length;
+	struct os_time now;
+	size_t rlen;
+
+	pos = *msgpos;
+
+	tlsv1_server_log(conn, "Send ServerHello");
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	os_get_time(&now);
+	WPA_PUT_BE32(conn->server_random, now.sec);
+	if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) {
+		wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+			   "server_random");
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
+		    conn->server_random, TLS_RANDOM_LEN);
+
+	conn->session_id_len = TLS_SESSION_ID_MAX_LEN;
+	if (random_get_bytes(conn->session_id, conn->session_id_len)) {
+		wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+			   "session_id");
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
+		    conn->session_id, conn->session_id_len);
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+	/* body - ServerHello */
+	/* ProtocolVersion server_version */
+	WPA_PUT_BE16(pos, conn->rl.tls_version);
+	pos += 2;
+	/* Random random: uint32 gmt_unix_time, opaque random_bytes */
+	os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN);
+	pos += TLS_RANDOM_LEN;
+	/* SessionID session_id */
+	*pos++ = conn->session_id_len;
+	os_memcpy(pos, conn->session_id, conn->session_id_len);
+	pos += conn->session_id_len;
+	/* CipherSuite cipher_suite */
+	WPA_PUT_BE16(pos, conn->cipher_suite);
+	pos += 2;
+	/* CompressionMethod compression_method */
+	*pos++ = TLS_COMPRESSION_NULL;
+
+	if (conn->session_ticket && conn->session_ticket_cb) {
+		int res = conn->session_ticket_cb(
+			conn->session_ticket_cb_ctx,
+			conn->session_ticket, conn->session_ticket_len,
+			conn->client_random, conn->server_random,
+			conn->master_secret);
+		if (res < 0) {
+			tlsv1_server_log(conn, "SessionTicket callback indicated failure");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_HANDSHAKE_FAILURE);
+			return -1;
+		}
+		conn->use_session_ticket = res;
+
+		if (conn->use_session_ticket) {
+			if (tlsv1_server_derive_keys(conn, NULL, 0) < 0) {
+				wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+					   "derive keys");
+				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+						   TLS_ALERT_INTERNAL_ERROR);
+				return -1;
+			}
+		}
+
+		/*
+		 * RFC 4507 specifies that server would include an empty
+		 * SessionTicket extension in ServerHello and a
+		 * NewSessionTicket message after the ServerHello. However,
+		 * EAP-FAST (RFC 4851), i.e., the only user of SessionTicket
+		 * extension at the moment, does not use such extensions.
+		 *
+		 * TODO: Add support for configuring RFC 4507 behavior and make
+		 * EAP-FAST disable it.
+		 */
+	}
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tls_write_server_certificate(struct tlsv1_server *conn,
+					u8 **msgpos, u8 *end)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
+	size_t rlen;
+	struct x509_certificate *cert;
+	const struct tls_cipher_suite *suite;
+
+	suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+	if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Do not send Certificate when "
+			   "using anonymous DH");
+		return 0;
+	}
+
+	pos = *msgpos;
+
+	tlsv1_server_log(conn, "Send Certificate");
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+	/* body - Certificate */
+	/* uint24 length (to be filled) */
+	cert_start = pos;
+	pos += 3;
+	cert = conn->cred->cert;
+	while (cert) {
+		if (pos + 3 + cert->cert_len > end) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
+				   "for Certificate (cert_len=%lu left=%lu)",
+				   (unsigned long) cert->cert_len,
+				   (unsigned long) (end - pos));
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		WPA_PUT_BE24(pos, cert->cert_len);
+		pos += 3;
+		os_memcpy(pos, cert->cert_start, cert->cert_len);
+		pos += cert->cert_len;
+
+		if (x509_certificate_self_signed(cert))
+			break;
+		cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+						    &cert->issuer);
+	}
+	if (cert == conn->cred->cert || cert == NULL) {
+		/*
+		 * Server was not configured with all the needed certificates
+		 * to form a full certificate chain. The client may fail to
+		 * validate the chain unless it is configured with all the
+		 * missing CA certificates.
+		 */
+		wpa_printf(MSG_DEBUG, "TLSv1: Full server certificate chain "
+			   "not configured - validation may fail");
+	}
+	WPA_PUT_BE24(cert_start, pos - cert_start - 3);
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tls_write_server_key_exchange(struct tlsv1_server *conn,
+					 u8 **msgpos, u8 *end)
+{
+	tls_key_exchange keyx;
+	const struct tls_cipher_suite *suite;
+	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)
+		keyx = TLS_KEY_X_NULL;
+	else
+		keyx = suite->key_exchange;
+
+	if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No ServerKeyExchange needed");
+		return 0;
+	}
+
+	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;
+	}
+
+	if (conn->cred == NULL || conn->cred->dh_p == NULL ||
+	    conn->cred->dh_g == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for "
+			   "ServerKeyExhcange");
+		return -1;
+	}
+
+	tlsv1_server_get_dh_p(conn, &dh_p, &dh_p_len);
+
+	os_free(conn->dh_secret);
+	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 "
+			   "memory for secret (Diffie-Hellman)");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	if (random_get_bytes(conn->dh_secret, conn->dh_secret_len)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+			   "data for Diffie-Hellman");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		os_free(conn->dh_secret);
+		conn->dh_secret = NULL;
+		return -1;
+	}
+
+	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;
+	while (pos + 1 < conn->dh_secret + conn->dh_secret_len && *pos == 0)
+		pos++;
+	if (pos != conn->dh_secret) {
+		os_memmove(conn->dh_secret, pos,
+			   conn->dh_secret_len - (pos - conn->dh_secret));
+		conn->dh_secret_len -= pos - conn->dh_secret;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH server's secret value",
+			conn->dh_secret, conn->dh_secret_len);
+
+	/* Ys = g^secret mod p */
+	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 "
+			   "Diffie-Hellman");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	if (crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len,
+			   conn->dh_secret, conn->dh_secret_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);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
+		    dh_ys, dh_ys_len);
+
+	/*
+	 * struct {
+	 *    select (KeyExchangeAlgorithm) {
+	 *       case diffie_hellman:
+	 *          ServerDHParams params;
+	 *          Signature signed_params;
+	 *       case rsa:
+	 *          ServerRSAParams params;
+	 *          Signature signed_params;
+	 *    };
+	 * } ServerKeyExchange;
+	 *
+	 * struct {
+	 *    opaque dh_p<1..2^16-1>;
+	 *    opaque dh_g<1..2^16-1>;
+	 *    opaque dh_Ys<1..2^16-1>;
+	 * } ServerDHParams;
+	 */
+
+	pos = *msgpos;
+
+	tlsv1_server_log(conn, "Send ServerKeyExchange");
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+
+	/* body - ServerDHParams */
+	server_params = pos;
+	/* dh_p */
+	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,
+				   TLS_ALERT_INTERNAL_ERROR);
+		os_free(dh_ys);
+		return -1;
+	}
+	WPA_PUT_BE16(pos, dh_p_len);
+	pos += 2;
+	os_memcpy(pos, dh_p, dh_p_len);
+	pos += dh_p_len;
+
+	/* dh_g */
+	if (pos + 2 + conn->cred->dh_g_len > end) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
+			   "dh_g");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		os_free(dh_ys);
+		return -1;
+	}
+	WPA_PUT_BE16(pos, conn->cred->dh_g_len);
+	pos += 2;
+	os_memcpy(pos, conn->cred->dh_g, conn->cred->dh_g_len);
+	pos += conn->cred->dh_g_len;
+
+	/* dh_Ys */
+	if (pos + 2 + dh_ys_len > end) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
+			   "dh_Ys");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		os_free(dh_ys);
+		return -1;
+	}
+	WPA_PUT_BE16(pos, dh_ys_len);
+	pos += 2;
+	os_memcpy(pos, dh_ys, dh_ys_len);
+	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,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tls_write_server_certificate_request(struct tlsv1_server *conn,
+						u8 **msgpos, u8 *end)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length;
+	size_t rlen;
+
+	if (!conn->verify_peer) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No CertificateRequest needed");
+		return 0;
+	}
+
+	pos = *msgpos;
+
+	tlsv1_server_log(conn, "Send CertificateRequest");
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+	/* body - CertificateRequest */
+
+	/*
+	 * enum {
+	 *   rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
+	 *   (255)
+	 * } ClientCertificateType;
+	 * ClientCertificateType certificate_types<1..2^8-1>
+	 */
+	*pos++ = 1;
+	*pos++ = 1; /* rsa_sign */
+
+	/*
+	 * opaque DistinguishedName<1..2^16-1>
+	 * DistinguishedName certificate_authorities<3..2^16-1>
+	 */
+	/* TODO: add support for listing DNs for trusted CAs */
+	WPA_PUT_BE16(pos, 0);
+	pos += 2;
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tls_write_server_hello_done(struct tlsv1_server *conn,
+				       u8 **msgpos, u8 *end)
+{
+	u8 *pos;
+	size_t rlen;
+	u8 payload[4];
+
+	tlsv1_server_log(conn, "Send ServerHelloDone");
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	pos = payload;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE;
+	/* uint24 length */
+	WPA_PUT_BE24(pos, 0);
+	pos += 3;
+	/* body - ServerHelloDone (empty) */
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      *msgpos, end - *msgpos, payload, pos - payload,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	tls_verify_hash_add(&conn->verify, payload, pos - payload);
+
+	*msgpos += rlen;
+
+	return 0;
+}
+
+
+static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
+					       u8 **msgpos, u8 *end)
+{
+	size_t rlen;
+	u8 payload[1];
+
+	tlsv1_server_log(conn, "Send ChangeCipherSpec");
+
+	payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
+			      *msgpos, end - *msgpos, payload, sizeof(payload),
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
+			   "record layer");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	*msgpos += rlen;
+
+	return 0;
+}
+
+
+static int tls_write_server_finished(struct tlsv1_server *conn,
+				     u8 **msgpos, u8 *end)
+{
+	u8 *pos, *hs_start;
+	size_t rlen, hlen;
+	u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
+	u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+	pos = *msgpos;
+
+	tlsv1_server_log(conn, "Send Finished");
+
+	/* Encrypted Handshake Message: Finished */
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+		hlen = SHA256_MAC_LEN;
+		if (conn->verify.sha256_server == NULL ||
+		    crypto_hash_finish(conn->verify.sha256_server, hash, &hlen)
+		    < 0) {
+			conn->verify.sha256_server = NULL;
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		conn->verify.sha256_server = NULL;
+	} else {
+#endif /* CONFIG_TLSV12 */
+
+	hlen = MD5_MAC_LEN;
+	if (conn->verify.md5_server == NULL ||
+	    crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		conn->verify.md5_server = NULL;
+		crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
+		conn->verify.sha1_server = NULL;
+		return -1;
+	}
+	conn->verify.md5_server = NULL;
+	hlen = SHA1_MAC_LEN;
+	if (conn->verify.sha1_server == NULL ||
+	    crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
+			       &hlen) < 0) {
+		conn->verify.sha1_server = NULL;
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	conn->verify.sha1_server = NULL;
+	hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+	}
+#endif /* CONFIG_TLSV12 */
+
+	if (tls_prf(conn->rl.tls_version,
+		    conn->master_secret, TLS_MASTER_SECRET_LEN,
+		    "server finished", hash, hlen,
+		    verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	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;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
+	/* uint24 length */
+	WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
+	pos += 3;
+	pos += TLS_VERIFY_DATA_LEN;
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      *msgpos, end - *msgpos, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	*msgpos += rlen;
+
+	return 0;
+}
+
+
+static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len)
+{
+	u8 *msg, *end, *pos;
+	size_t msglen;
+
+	*out_len = 0;
+
+	msglen = 1000 + tls_server_cert_chain_der_len(conn);
+
+	msg = os_malloc(msglen);
+	if (msg == NULL)
+		return NULL;
+
+	pos = msg;
+	end = msg + msglen;
+
+	if (tls_write_server_hello(conn, &pos, end) < 0) {
+		os_free(msg);
+		return NULL;
+	}
+
+	if (conn->use_session_ticket) {
+		/* Abbreviated handshake using session ticket; RFC 4507 */
+		if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
+		    tls_write_server_finished(conn, &pos, end) < 0) {
+			os_free(msg);
+			return NULL;
+		}
+
+		*out_len = pos - msg;
+
+		conn->state = CHANGE_CIPHER_SPEC;
+
+		return msg;
+	}
+
+	/* Full handshake */
+	if (tls_write_server_certificate(conn, &pos, end) < 0 ||
+	    tls_write_server_key_exchange(conn, &pos, end) < 0 ||
+	    tls_write_server_certificate_request(conn, &pos, end) < 0 ||
+	    tls_write_server_hello_done(conn, &pos, end) < 0) {
+		os_free(msg);
+		return NULL;
+	}
+
+	*out_len = pos - msg;
+
+	conn->state = CLIENT_CERTIFICATE;
+
+	return msg;
+}
+
+
+static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn,
+					size_t *out_len)
+{
+	u8 *msg, *end, *pos;
+
+	*out_len = 0;
+
+	msg = os_malloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	pos = msg;
+	end = msg + 1000;
+
+	if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
+	    tls_write_server_finished(conn, &pos, end) < 0) {
+		os_free(msg);
+		return NULL;
+	}
+
+	*out_len = pos - msg;
+
+	tlsv1_server_log(conn, "Handshake completed successfully");
+	conn->state = ESTABLISHED;
+
+	return msg;
+}
+
+
+u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len)
+{
+	switch (conn->state) {
+	case SERVER_HELLO:
+		return tls_send_server_hello(conn, out_len);
+	case SERVER_CHANGE_CIPHER_SPEC:
+		return tls_send_change_cipher_spec(conn, out_len);
+	default:
+		if (conn->state == ESTABLISHED && conn->use_session_ticket) {
+			/* Abbreviated handshake was already completed. */
+			return NULL;
+		}
+		tlsv1_server_log(conn, "Unexpected state %d while generating reply",
+				 conn->state);
+		return NULL;
+	}
+}
+
+
+u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
+			     u8 description, size_t *out_len)
+{
+	u8 *alert, *pos, *length;
+
+	tlsv1_server_log(conn, "Send Alert(%d:%d)", level, description);
+	*out_len = 0;
+
+	alert = os_malloc(10);
+	if (alert == NULL)
+		return NULL;
+
+	pos = alert;
+
+	/* TLSPlaintext */
+	/* ContentType type */
+	*pos++ = TLS_CONTENT_TYPE_ALERT;
+	/* ProtocolVersion version */
+	WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
+		     TLS_VERSION);
+	pos += 2;
+	/* uint16 length (to be filled) */
+	length = pos;
+	pos += 2;
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Alert */
+	/* AlertLevel level */
+	*pos++ = level;
+	/* AlertDescription description */
+	*pos++ = description;
+
+	WPA_PUT_BE16(length, pos - length - 2);
+	*out_len = pos - alert;
+
+	return alert;
+}
diff --git a/hostap/src/tls/x509v3.c b/hostap/src/tls/x509v3.c
new file mode 100644
index 0000000..b51dfcd
--- /dev/null
+++ b/hostap/src/tls/x509v3.c
@@ -0,0 +1,1990 @@
+/*
+ * X.509v3 certificate parsing and processing (RFC 3280 profile)
+ * Copyright (c) 2006-2011, 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/crypto.h"
+#include "asn1.h"
+#include "x509v3.h"
+
+
+static void x509_free_name(struct x509_name *name)
+{
+	size_t i;
+
+	for (i = 0; i < name->num_attr; i++) {
+		os_free(name->attr[i].value);
+		name->attr[i].value = NULL;
+		name->attr[i].type = X509_NAME_ATTR_NOT_USED;
+	}
+	name->num_attr = 0;
+	os_free(name->email);
+	name->email = NULL;
+
+	os_free(name->alt_email);
+	os_free(name->dns);
+	os_free(name->uri);
+	os_free(name->ip);
+	name->alt_email = name->dns = name->uri = NULL;
+	name->ip = NULL;
+	name->ip_len = 0;
+	os_memset(&name->rid, 0, sizeof(name->rid));
+}
+
+
+/**
+ * x509_certificate_free - Free an X.509 certificate
+ * @cert: Certificate to be freed
+ */
+void x509_certificate_free(struct x509_certificate *cert)
+{
+	if (cert == NULL)
+		return;
+	if (cert->next) {
+		wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p "
+			   "was still on a list (next=%p)\n",
+			   cert, cert->next);
+	}
+	x509_free_name(&cert->issuer);
+	x509_free_name(&cert->subject);
+	os_free(cert->public_key);
+	os_free(cert->sign_value);
+	os_free(cert);
+}
+
+
+/**
+ * x509_certificate_free - Free an X.509 certificate chain
+ * @cert: Pointer to the first certificate in the chain
+ */
+void x509_certificate_chain_free(struct x509_certificate *cert)
+{
+	struct x509_certificate *next;
+
+	while (cert) {
+		next = cert->next;
+		cert->next = NULL;
+		x509_certificate_free(cert);
+		cert = next;
+	}
+}
+
+
+static int x509_whitespace(char c)
+{
+	return c == ' ' || c == '\t';
+}
+
+
+static void x509_str_strip_whitespace(char *a)
+{
+	char *ipos, *opos;
+	int remove_whitespace = 1;
+
+	ipos = opos = a;
+
+	while (*ipos) {
+		if (remove_whitespace && x509_whitespace(*ipos))
+			ipos++;
+		else {
+			remove_whitespace = x509_whitespace(*ipos);
+			*opos++ = *ipos++;
+		}
+	}
+
+	*opos-- = '\0';
+	if (opos > a && x509_whitespace(*opos))
+		*opos = '\0';
+}
+
+
+static int x509_str_compare(const char *a, const char *b)
+{
+	char *aa, *bb;
+	int ret;
+
+	if (!a && b)
+		return -1;
+	if (a && !b)
+		return 1;
+	if (!a && !b)
+		return 0;
+
+	aa = os_strdup(a);
+	bb = os_strdup(b);
+
+	if (aa == NULL || bb == NULL) {
+		os_free(aa);
+		os_free(bb);
+		return os_strcasecmp(a, b);
+	}
+
+	x509_str_strip_whitespace(aa);
+	x509_str_strip_whitespace(bb);
+
+	ret = os_strcasecmp(aa, bb);
+
+	os_free(aa);
+	os_free(bb);
+
+	return ret;
+}
+
+
+/**
+ * x509_name_compare - Compare X.509 certificate names
+ * @a: Certificate name
+ * @b: Certificate name
+ * Returns: <0, 0, or >0 based on whether a is less than, equal to, or
+ * greater than b
+ */
+int x509_name_compare(struct x509_name *a, struct x509_name *b)
+{
+	int res;
+	size_t i;
+
+	if (!a && b)
+		return -1;
+	if (a && !b)
+		return 1;
+	if (!a && !b)
+		return 0;
+	if (a->num_attr < b->num_attr)
+		return -1;
+	if (a->num_attr > b->num_attr)
+		return 1;
+
+	for (i = 0; i < a->num_attr; i++) {
+		if (a->attr[i].type < b->attr[i].type)
+			return -1;
+		if (a->attr[i].type > b->attr[i].type)
+			return -1;
+		res = x509_str_compare(a->attr[i].value, b->attr[i].value);
+		if (res)
+			return res;
+	}
+	res = x509_str_compare(a->email, b->email);
+	if (res)
+		return res;
+
+	return 0;
+}
+
+
+static int x509_parse_algorithm_identifier(
+	const u8 *buf, size_t len,
+	struct x509_algorithm_identifier *id, const u8 **next)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+
+	/*
+	 * AlgorithmIdentifier ::= SEQUENCE {
+	 *     algorithm            OBJECT IDENTIFIER,
+	 *     parameters           ANY DEFINED BY algorithm OPTIONAL
+	 * }
+	 */
+
+	if (asn1_get_next(buf, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+			   "(AlgorithmIdentifier) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	if (end > buf + len)
+		return -1;
+
+	*next = end;
+
+	if (asn1_get_oid(pos, end - pos, &id->oid, &pos))
+		return -1;
+
+	/* TODO: optional parameters */
+
+	return 0;
+}
+
+
+static int x509_parse_public_key(const u8 *buf, size_t len,
+				 struct x509_certificate *cert,
+				 const u8 **next)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+
+	/*
+	 * SubjectPublicKeyInfo ::= SEQUENCE {
+	 *     algorithm            AlgorithmIdentifier,
+	 *     subjectPublicKey     BIT STRING
+	 * }
+	 */
+
+	pos = buf;
+	end = buf + len;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+			   "(SubjectPublicKeyInfo) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+
+	if (pos + hdr.length > end)
+		return -1;
+	end = pos + hdr.length;
+	*next = end;
+
+	if (x509_parse_algorithm_identifier(pos, end - pos,
+					    &cert->public_key_alg, &pos))
+		return -1;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_BITSTRING) {
+		wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING "
+			   "(subjectPublicKey) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	if (hdr.length < 1)
+		return -1;
+	pos = hdr.payload;
+	if (*pos) {
+		wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits",
+			   *pos);
+		/*
+		 * TODO: should this be rejected? X.509 certificates are
+		 * unlikely to use such a construction. Now we would end up
+		 * including the extra bits in the buffer which may also be
+		 * ok.
+		 */
+	}
+	os_free(cert->public_key);
+	cert->public_key = os_malloc(hdr.length - 1);
+	if (cert->public_key == NULL) {
+		wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
+			   "public key");
+		return -1;
+	}
+	os_memcpy(cert->public_key, pos + 1, hdr.length - 1);
+	cert->public_key_len = hdr.length - 1;
+	wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey",
+		    cert->public_key, cert->public_key_len);
+
+	return 0;
+}
+
+
+static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
+			   const u8 **next)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end;
+	struct asn1_oid oid;
+	char *val;
+
+	/*
+	 * Name ::= CHOICE { RDNSequence }
+	 * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+	 * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
+	 * AttributeTypeAndValue ::= SEQUENCE {
+	 *     type     AttributeType,
+	 *     value    AttributeValue
+	 * }
+	 * AttributeType ::= OBJECT IDENTIFIER
+	 * AttributeValue ::= ANY DEFINED BY AttributeType
+	 */
+
+	if (asn1_get_next(buf, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+			   "(Name / RDNSequencer) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+
+	if (pos + hdr.length > buf + len)
+		return -1;
+
+	end = *next = pos + hdr.length;
+
+	while (pos < end) {
+		enum x509_name_attr_type type;
+
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL ||
+		    hdr.tag != ASN1_TAG_SET) {
+			wpa_printf(MSG_DEBUG, "X509: Expected SET "
+				   "(RelativeDistinguishedName) - found class "
+				   "%d tag 0x%x", hdr.class, hdr.tag);
+			x509_free_name(name);
+			return -1;
+		}
+
+		set_pos = hdr.payload;
+		pos = set_end = hdr.payload + hdr.length;
+
+		if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL ||
+		    hdr.tag != ASN1_TAG_SEQUENCE) {
+			wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+				   "(AttributeTypeAndValue) - found class %d "
+				   "tag 0x%x", hdr.class, hdr.tag);
+			x509_free_name(name);
+			return -1;
+		}
+
+		seq_pos = hdr.payload;
+		seq_end = hdr.payload + hdr.length;
+
+		if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) {
+			x509_free_name(name);
+			return -1;
+		}
+
+		if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL) {
+			wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+				   "AttributeValue");
+			x509_free_name(name);
+			return -1;
+		}
+
+		/* RFC 3280:
+		 * MUST: country, organization, organizational-unit,
+		 * distinguished name qualifier, state or province name,
+		 * common name, serial number.
+		 * SHOULD: locality, title, surname, given name, initials,
+		 * pseudonym, generation qualifier.
+		 * MUST: domainComponent (RFC 2247).
+		 */
+		type = X509_NAME_ATTR_NOT_USED;
+		if (oid.len == 4 &&
+		    oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) {
+			/* id-at ::= 2.5.4 */
+			switch (oid.oid[3]) {
+			case 3:
+				/* commonName */
+				type = X509_NAME_ATTR_CN;
+				break;
+			case 6:
+				/*  countryName */
+				type = X509_NAME_ATTR_C;
+				break;
+			case 7:
+				/* localityName */
+				type = X509_NAME_ATTR_L;
+				break;
+			case 8:
+				/* stateOrProvinceName */
+				type = X509_NAME_ATTR_ST;
+				break;
+			case 10:
+				/* organizationName */
+				type = X509_NAME_ATTR_O;
+				break;
+			case 11:
+				/* organizationalUnitName */
+				type = X509_NAME_ATTR_OU;
+				break;
+			}
+		} else if (oid.len == 7 &&
+			   oid.oid[0] == 1 && oid.oid[1] == 2 &&
+			   oid.oid[2] == 840 && oid.oid[3] == 113549 &&
+			   oid.oid[4] == 1 && oid.oid[5] == 9 &&
+			   oid.oid[6] == 1) {
+			/* 1.2.840.113549.1.9.1 - e-mailAddress */
+			os_free(name->email);
+			name->email = os_malloc(hdr.length + 1);
+			if (name->email == NULL) {
+				x509_free_name(name);
+				return -1;
+			}
+			os_memcpy(name->email, hdr.payload, hdr.length);
+			name->email[hdr.length] = '\0';
+			continue;
+		} else if (oid.len == 7 &&
+			   oid.oid[0] == 0 && oid.oid[1] == 9 &&
+			   oid.oid[2] == 2342 && oid.oid[3] == 19200300 &&
+			   oid.oid[4] == 100 && oid.oid[5] == 1 &&
+			   oid.oid[6] == 25) {
+			/* 0.9.2342.19200300.100.1.25 - domainComponent */
+			type = X509_NAME_ATTR_DC;
+		}
+
+		if (type == X509_NAME_ATTR_NOT_USED) {
+			wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID",
+				    (u8 *) oid.oid,
+				    oid.len * sizeof(oid.oid[0]));
+			wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data",
+					  hdr.payload, hdr.length);
+			continue;
+		}
+
+		if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) {
+			wpa_printf(MSG_INFO, "X509: Too many Name attributes");
+			x509_free_name(name);
+			return -1;
+		}
+
+		val = dup_binstr(hdr.payload, hdr.length);
+		if (val == NULL) {
+			x509_free_name(name);
+			return -1;
+		}
+		if (os_strlen(val) != hdr.length) {
+			wpa_printf(MSG_INFO, "X509: Reject certificate with "
+				   "embedded NUL byte in a string (%s[NUL])",
+				   val);
+			os_free(val);
+			x509_free_name(name);
+			return -1;
+		}
+
+		name->attr[name->num_attr].type = type;
+		name->attr[name->num_attr].value = val;
+		name->num_attr++;
+	}
+
+	return 0;
+}
+
+
+static char * x509_name_attr_str(enum x509_name_attr_type type)
+{
+	switch (type) {
+	case X509_NAME_ATTR_NOT_USED:
+		return "[N/A]";
+	case X509_NAME_ATTR_DC:
+		return "DC";
+	case X509_NAME_ATTR_CN:
+		return "CN";
+	case X509_NAME_ATTR_C:
+		return "C";
+	case X509_NAME_ATTR_L:
+		return "L";
+	case X509_NAME_ATTR_ST:
+		return "ST";
+	case X509_NAME_ATTR_O:
+		return "O";
+	case X509_NAME_ATTR_OU:
+		return "OU";
+	}
+	return "?";
+}
+
+
+/**
+ * x509_name_string - Convert an X.509 certificate name into a string
+ * @name: Name to convert
+ * @buf: Buffer for the string
+ * @len: Maximum buffer length
+ */
+void x509_name_string(struct x509_name *name, char *buf, size_t len)
+{
+	char *pos, *end;
+	int ret;
+	size_t i;
+
+	if (len == 0)
+		return;
+
+	pos = buf;
+	end = buf + len;
+
+	for (i = 0; i < name->num_attr; i++) {
+		ret = os_snprintf(pos, end - pos, "%s=%s, ",
+				  x509_name_attr_str(name->attr[i].type),
+				  name->attr[i].value);
+		if (os_snprintf_error(end - pos, ret))
+			goto done;
+		pos += ret;
+	}
+
+	if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') {
+		pos--;
+		*pos = '\0';
+		pos--;
+		*pos = '\0';
+	}
+
+	if (name->email) {
+		ret = os_snprintf(pos, end - pos, "/emailAddress=%s",
+				  name->email);
+		if (os_snprintf_error(end - pos, ret))
+			goto done;
+		pos += ret;
+	}
+
+done:
+	end[-1] = '\0';
+}
+
+
+static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag,
+			   os_time_t *val)
+{
+	const char *pos;
+	int year, month, day, hour, min, sec;
+
+	/*
+	 * Time ::= CHOICE {
+	 *     utcTime        UTCTime,
+	 *     generalTime    GeneralizedTime
+	 * }
+	 *
+	 * UTCTime: YYMMDDHHMMSSZ
+	 * GeneralizedTime: YYYYMMDDHHMMSSZ
+	 */
+
+	pos = (const char *) buf;
+
+	switch (asn1_tag) {
+	case ASN1_TAG_UTCTIME:
+		if (len != 13 || buf[12] != 'Z') {
+			wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
+					  "UTCTime format", buf, len);
+			return -1;
+		}
+		if (sscanf(pos, "%02d", &year) != 1) {
+			wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
+					  "UTCTime year", buf, len);
+			return -1;
+		}
+		if (year < 50)
+			year += 2000;
+		else
+			year += 1900;
+		pos += 2;
+		break;
+	case ASN1_TAG_GENERALIZEDTIME:
+		if (len != 15 || buf[14] != 'Z') {
+			wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
+					  "GeneralizedTime format", buf, len);
+			return -1;
+		}
+		if (sscanf(pos, "%04d", &year) != 1) {
+			wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
+					  "GeneralizedTime year", buf, len);
+			return -1;
+		}
+		pos += 4;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or "
+			   "GeneralizedTime - found tag 0x%x", asn1_tag);
+		return -1;
+	}
+
+	if (sscanf(pos, "%02d", &month) != 1) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+				  "(month)", buf, len);
+		return -1;
+	}
+	pos += 2;
+
+	if (sscanf(pos, "%02d", &day) != 1) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+				  "(day)", buf, len);
+		return -1;
+	}
+	pos += 2;
+
+	if (sscanf(pos, "%02d", &hour) != 1) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+				  "(hour)", buf, len);
+		return -1;
+	}
+	pos += 2;
+
+	if (sscanf(pos, "%02d", &min) != 1) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+				  "(min)", buf, len);
+		return -1;
+	}
+	pos += 2;
+
+	if (sscanf(pos, "%02d", &sec) != 1) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+				  "(sec)", buf, len);
+		return -1;
+	}
+
+	if (os_mktime(year, month, day, hour, min, sec, val) < 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time",
+				  buf, len);
+		if (year < 1970) {
+			/*
+			 * At least some test certificates have been configured
+			 * to use dates prior to 1970. Set the date to
+			 * beginning of 1970 to handle these case.
+			 */
+			wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - "
+				   "assume epoch as the time", year);
+			*val = 0;
+			return 0;
+		}
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int x509_parse_validity(const u8 *buf, size_t len,
+			       struct x509_certificate *cert, const u8 **next)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos;
+	size_t plen;
+
+	/*
+	 * Validity ::= SEQUENCE {
+	 *     notBefore      Time,
+	 *     notAfter       Time
+	 * }
+	 *
+	 * RFC 3280, 4.1.2.5:
+	 * CAs conforming to this profile MUST always encode certificate
+	 * validity dates through the year 2049 as UTCTime; certificate
+	 * validity dates in 2050 or later MUST be encoded as GeneralizedTime.
+	 */
+
+	if (asn1_get_next(buf, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+			   "(Validity) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+	plen = hdr.length;
+
+	if (pos + plen > buf + len)
+		return -1;
+
+	*next = pos + plen;
+
+	if (asn1_get_next(pos, plen, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+			    &cert->not_before) < 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore "
+				  "Time", hdr.payload, hdr.length);
+		return -1;
+	}
+
+	pos = hdr.payload + hdr.length;
+	plen = *next - pos;
+
+	if (asn1_get_next(pos, plen, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+			    &cert->not_after) < 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter "
+				  "Time", hdr.payload, hdr.length);
+		return -1;
+	}
+
+	wpa_printf(MSG_MSGDUMP, "X509: Validity: notBefore: %lu notAfter: %lu",
+		   (unsigned long) cert->not_before,
+		   (unsigned long) cert->not_after);
+
+	return 0;
+}
+
+
+static int x509_id_ce_oid(struct asn1_oid *oid)
+{
+	/* id-ce arc from X.509 for standard X.509v3 extensions */
+	return oid->len >= 4 &&
+		oid->oid[0] == 2 /* joint-iso-ccitt */ &&
+		oid->oid[1] == 5 /* ds */ &&
+		oid->oid[2] == 29 /* id-ce */;
+}
+
+
+static int x509_parse_ext_key_usage(struct x509_certificate *cert,
+				    const u8 *pos, size_t len)
+{
+	struct asn1_hdr hdr;
+
+	/*
+	 * KeyUsage ::= BIT STRING {
+	 *     digitalSignature        (0),
+	 *     nonRepudiation          (1),
+	 *     keyEncipherment         (2),
+	 *     dataEncipherment        (3),
+	 *     keyAgreement            (4),
+	 *     keyCertSign             (5),
+	 *     cRLSign                 (6),
+	 *     encipherOnly            (7),
+	 *     decipherOnly            (8) }
+	 */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_BITSTRING ||
+	    hdr.length < 1) {
+		wpa_printf(MSG_DEBUG, "X509: Expected BIT STRING in "
+			   "KeyUsage; found %d tag 0x%x len %d",
+			   hdr.class, hdr.tag, hdr.length);
+		return -1;
+	}
+
+	cert->extensions_present |= X509_EXT_KEY_USAGE;
+	cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length);
+
+	wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage);
+
+	return 0;
+}
+
+
+static int x509_parse_ext_basic_constraints(struct x509_certificate *cert,
+					    const u8 *pos, size_t len)
+{
+	struct asn1_hdr hdr;
+	unsigned long value;
+	size_t left;
+
+	/*
+	 * BasicConstraints ::= SEQUENCE {
+	 * cA                      BOOLEAN DEFAULT FALSE,
+	 * pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
+	 */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+			   "BasicConstraints; found %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS;
+
+	if (hdr.length == 0)
+		return 0;
+
+	if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL) {
+		wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+			   "BasicConstraints");
+		return -1;
+	}
+
+	if (hdr.tag == ASN1_TAG_BOOLEAN) {
+		if (hdr.length != 1) {
+			wpa_printf(MSG_DEBUG, "X509: Unexpected "
+				   "Boolean length (%u) in BasicConstraints",
+				   hdr.length);
+			return -1;
+		}
+		cert->ca = hdr.payload[0];
+
+		if (hdr.payload + hdr.length == pos + len) {
+			wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d",
+				   cert->ca);
+			return 0;
+		}
+
+		if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length,
+				  &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL) {
+			wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+				   "BasicConstraints");
+			return -1;
+		}
+	}
+
+	if (hdr.tag != ASN1_TAG_INTEGER) {
+		wpa_printf(MSG_DEBUG, "X509: Expected INTEGER in "
+			   "BasicConstraints; found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	left = hdr.length;
+	value = 0;
+	while (left) {
+		value <<= 8;
+		value |= *pos++;
+		left--;
+	}
+
+	cert->path_len_constraint = value;
+	cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT;
+
+	wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d "
+		   "pathLenConstraint=%lu",
+		   cert->ca, cert->path_len_constraint);
+
+	return 0;
+}
+
+
+static int x509_parse_alt_name_rfc8222(struct x509_name *name,
+				       const u8 *pos, size_t len)
+{
+	/* rfc822Name IA5String */
+	wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len);
+	os_free(name->alt_email);
+	name->alt_email = os_zalloc(len + 1);
+	if (name->alt_email == NULL)
+		return -1;
+	os_memcpy(name->alt_email, pos, len);
+	if (os_strlen(name->alt_email) != len) {
+		wpa_printf(MSG_INFO, "X509: Reject certificate with "
+			   "embedded NUL byte in rfc822Name (%s[NUL])",
+			   name->alt_email);
+		os_free(name->alt_email);
+		name->alt_email = NULL;
+		return -1;
+	}
+	return 0;
+}
+
+
+static int x509_parse_alt_name_dns(struct x509_name *name,
+				   const u8 *pos, size_t len)
+{
+	/* dNSName IA5String */
+	wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len);
+	os_free(name->dns);
+	name->dns = os_zalloc(len + 1);
+	if (name->dns == NULL)
+		return -1;
+	os_memcpy(name->dns, pos, len);
+	if (os_strlen(name->dns) != len) {
+		wpa_printf(MSG_INFO, "X509: Reject certificate with "
+			   "embedded NUL byte in dNSName (%s[NUL])",
+			   name->dns);
+		os_free(name->dns);
+		name->dns = NULL;
+		return -1;
+	}
+	return 0;
+}
+
+
+static int x509_parse_alt_name_uri(struct x509_name *name,
+				   const u8 *pos, size_t len)
+{
+	/* uniformResourceIdentifier IA5String */
+	wpa_hexdump_ascii(MSG_MSGDUMP,
+			  "X509: altName - uniformResourceIdentifier",
+			  pos, len);
+	os_free(name->uri);
+	name->uri = os_zalloc(len + 1);
+	if (name->uri == NULL)
+		return -1;
+	os_memcpy(name->uri, pos, len);
+	if (os_strlen(name->uri) != len) {
+		wpa_printf(MSG_INFO, "X509: Reject certificate with "
+			   "embedded NUL byte in uniformResourceIdentifier "
+			   "(%s[NUL])", name->uri);
+		os_free(name->uri);
+		name->uri = NULL;
+		return -1;
+	}
+	return 0;
+}
+
+
+static int x509_parse_alt_name_ip(struct x509_name *name,
+				       const u8 *pos, size_t len)
+{
+	/* iPAddress OCTET STRING */
+	wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len);
+	os_free(name->ip);
+	name->ip = os_malloc(len);
+	if (name->ip == NULL)
+		return -1;
+	os_memcpy(name->ip, pos, len);
+	name->ip_len = len;
+	return 0;
+}
+
+
+static int x509_parse_alt_name_rid(struct x509_name *name,
+				   const u8 *pos, size_t len)
+{
+	char buf[80];
+
+	/* registeredID OBJECT IDENTIFIER */
+	if (asn1_parse_oid(pos, len, &name->rid) < 0)
+		return -1;
+
+	asn1_oid_to_str(&name->rid, buf, sizeof(buf));
+	wpa_printf(MSG_MSGDUMP, "X509: altName - registeredID: %s", buf);
+
+	return 0;
+}
+
+
+static int x509_parse_ext_alt_name(struct x509_name *name,
+				   const u8 *pos, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *p, *end;
+
+	/*
+	 * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+	 *
+	 * GeneralName ::= CHOICE {
+	 *     otherName                       [0]     OtherName,
+	 *     rfc822Name                      [1]     IA5String,
+	 *     dNSName                         [2]     IA5String,
+	 *     x400Address                     [3]     ORAddress,
+	 *     directoryName                   [4]     Name,
+	 *     ediPartyName                    [5]     EDIPartyName,
+	 *     uniformResourceIdentifier       [6]     IA5String,
+	 *     iPAddress                       [7]     OCTET STRING,
+	 *     registeredID                    [8]     OBJECT IDENTIFIER }
+	 *
+	 * OtherName ::= SEQUENCE {
+	 *     type-id    OBJECT IDENTIFIER,
+	 *     value      [0] EXPLICIT ANY DEFINED BY type-id }
+	 *
+	 * EDIPartyName ::= SEQUENCE {
+	 *     nameAssigner            [0]     DirectoryString OPTIONAL,
+	 *     partyName               [1]     DirectoryString }
+	 */
+
+	for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) {
+		int res;
+
+		if (asn1_get_next(p, end - p, &hdr) < 0) {
+			wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+				   "SubjectAltName item");
+			return -1;
+		}
+
+		if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC)
+			continue;
+
+		switch (hdr.tag) {
+		case 1:
+			res = x509_parse_alt_name_rfc8222(name, hdr.payload,
+							  hdr.length);
+			break;
+		case 2:
+			res = x509_parse_alt_name_dns(name, hdr.payload,
+						      hdr.length);
+			break;
+		case 6:
+			res = x509_parse_alt_name_uri(name, hdr.payload,
+						      hdr.length);
+			break;
+		case 7:
+			res = x509_parse_alt_name_ip(name, hdr.payload,
+						     hdr.length);
+			break;
+		case 8:
+			res = x509_parse_alt_name_rid(name, hdr.payload,
+						      hdr.length);
+			break;
+		case 0: /* TODO: otherName */
+		case 3: /* TODO: x500Address */
+		case 4: /* TODO: directoryName */
+		case 5: /* TODO: ediPartyName */
+		default:
+			res = 0;
+			break;
+		}
+		if (res < 0)
+			return res;
+	}
+
+	return 0;
+}
+
+
+static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert,
+					   const u8 *pos, size_t len)
+{
+	struct asn1_hdr hdr;
+
+	/* SubjectAltName ::= GeneralNames */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+			   "SubjectAltName; found %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "X509: SubjectAltName");
+	cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME;
+
+	if (hdr.length == 0)
+		return 0;
+
+	return x509_parse_ext_alt_name(&cert->subject, hdr.payload,
+				       hdr.length);
+}
+
+
+static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert,
+					  const u8 *pos, size_t len)
+{
+	struct asn1_hdr hdr;
+
+	/* IssuerAltName ::= GeneralNames */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+			   "IssuerAltName; found %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "X509: IssuerAltName");
+	cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME;
+
+	if (hdr.length == 0)
+		return 0;
+
+	return x509_parse_ext_alt_name(&cert->issuer, hdr.payload,
+				       hdr.length);
+}
+
+
+static int x509_parse_extension_data(struct x509_certificate *cert,
+				     struct asn1_oid *oid,
+				     const u8 *pos, size_t len)
+{
+	if (!x509_id_ce_oid(oid))
+		return 1;
+
+	/* TODO: add other extensions required by RFC 3280, Ch 4.2:
+	 * certificate policies (section 4.2.1.5)
+	 * name constraints (section 4.2.1.11)
+	 * policy constraints (section 4.2.1.12)
+	 * extended key usage (section 4.2.1.13)
+	 * inhibit any-policy (section 4.2.1.15)
+	 */
+	switch (oid->oid[3]) {
+	case 15: /* id-ce-keyUsage */
+		return x509_parse_ext_key_usage(cert, pos, len);
+	case 17: /* id-ce-subjectAltName */
+		return x509_parse_ext_subject_alt_name(cert, pos, len);
+	case 18: /* id-ce-issuerAltName */
+		return x509_parse_ext_issuer_alt_name(cert, pos, len);
+	case 19: /* id-ce-basicConstraints */
+		return x509_parse_ext_basic_constraints(cert, pos, len);
+	default:
+		return 1;
+	}
+}
+
+
+static int x509_parse_extension(struct x509_certificate *cert,
+				const u8 *pos, size_t len, const u8 **next)
+{
+	const u8 *end;
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+	int critical_ext = 0, res;
+	char buf[80];
+
+	/*
+	 * Extension  ::=  SEQUENCE  {
+	 *     extnID      OBJECT IDENTIFIER,
+	 *     critical    BOOLEAN DEFAULT FALSE,
+	 *     extnValue   OCTET STRING
+	 * }
+	 */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in "
+			   "Extensions: class %d tag 0x%x; expected SEQUENCE",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+	*next = end = pos + hdr.length;
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
+		wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for "
+			   "Extension (expected OID)");
+		return -1;
+	}
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    (hdr.tag != ASN1_TAG_BOOLEAN &&
+	     hdr.tag != ASN1_TAG_OCTETSTRING)) {
+		wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in "
+			   "Extensions: class %d tag 0x%x; expected BOOLEAN "
+			   "or OCTET STRING", hdr.class, hdr.tag);
+		return -1;
+	}
+
+	if (hdr.tag == ASN1_TAG_BOOLEAN) {
+		if (hdr.length != 1) {
+			wpa_printf(MSG_DEBUG, "X509: Unexpected "
+				   "Boolean length (%u)", hdr.length);
+			return -1;
+		}
+		critical_ext = hdr.payload[0];
+		pos = hdr.payload;
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    (hdr.class != ASN1_CLASS_UNIVERSAL &&
+		     hdr.class != ASN1_CLASS_PRIVATE) ||
+		    hdr.tag != ASN1_TAG_OCTETSTRING) {
+			wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header "
+				   "in Extensions: class %d tag 0x%x; "
+				   "expected OCTET STRING",
+				   hdr.class, hdr.tag);
+			return -1;
+		}
+	}
+
+	asn1_oid_to_str(&oid, buf, sizeof(buf));
+	wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d",
+		   buf, critical_ext);
+	wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length);
+
+	res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length);
+	if (res < 0)
+		return res;
+	if (res == 1 && critical_ext) {
+		wpa_printf(MSG_INFO, "X509: Unknown critical extension %s",
+			   buf);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int x509_parse_extensions(struct x509_certificate *cert,
+				 const u8 *pos, size_t len)
+{
+	const u8 *end;
+	struct asn1_hdr hdr;
+
+	/* Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data "
+			   "for Extensions: class %d tag 0x%x; "
+			   "expected SEQUENCE", hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	while (pos < end) {
+		if (x509_parse_extension(cert, pos, end - pos, &pos)
+		    < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static int x509_parse_tbs_certificate(const u8 *buf, size_t len,
+				      struct x509_certificate *cert,
+				      const u8 **next)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	size_t left;
+	char sbuf[128];
+	unsigned long value;
+
+	/* tbsCertificate TBSCertificate ::= SEQUENCE */
+	if (asn1_get_next(buf, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start "
+			   "with a valid SEQUENCE - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+	end = *next = pos + hdr.length;
+
+	/*
+	 * version [0]  EXPLICIT Version DEFAULT v1
+	 * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0)
+		return -1;
+	pos = hdr.payload;
+
+	if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0)
+			return -1;
+
+		if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+		    hdr.tag != ASN1_TAG_INTEGER) {
+			wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
+				   "version field - found class %d tag 0x%x",
+				   hdr.class, hdr.tag);
+			return -1;
+		}
+		if (hdr.length != 1) {
+			wpa_printf(MSG_DEBUG, "X509: Unexpected version field "
+				   "length %u (expected 1)", hdr.length);
+			return -1;
+		}
+		pos = hdr.payload;
+		left = hdr.length;
+		value = 0;
+		while (left) {
+			value <<= 8;
+			value |= *pos++;
+			left--;
+		}
+
+		cert->version = value;
+		if (cert->version != X509_CERT_V1 &&
+		    cert->version != X509_CERT_V2 &&
+		    cert->version != X509_CERT_V3) {
+			wpa_printf(MSG_DEBUG, "X509: Unsupported version %d",
+				   cert->version + 1);
+			return -1;
+		}
+
+		if (asn1_get_next(pos, end - pos, &hdr) < 0)
+			return -1;
+	} else
+		cert->version = X509_CERT_V1;
+	wpa_printf(MSG_MSGDUMP, "X509: Version X.509v%d", cert->version + 1);
+
+	/* serialNumber CertificateSerialNumber ::= INTEGER */
+	if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_INTEGER) {
+		wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
+			   "serialNumber; class=%d tag=0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	left = hdr.length;
+	while (left) {
+		cert->serial_number <<= 8;
+		cert->serial_number |= *pos++;
+		left--;
+	}
+	wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu", cert->serial_number);
+
+	/* signature AlgorithmIdentifier */
+	if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature,
+					    &pos))
+		return -1;
+
+	/* issuer Name */
+	if (x509_parse_name(pos, end - pos, &cert->issuer, &pos))
+		return -1;
+	x509_name_string(&cert->issuer, sbuf, sizeof(sbuf));
+	wpa_printf(MSG_MSGDUMP, "X509: issuer %s", sbuf);
+
+	/* validity Validity */
+	if (x509_parse_validity(pos, end - pos, cert, &pos))
+		return -1;
+
+	/* subject Name */
+	if (x509_parse_name(pos, end - pos, &cert->subject, &pos))
+		return -1;
+	x509_name_string(&cert->subject, sbuf, sizeof(sbuf));
+	wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf);
+
+	/* subjectPublicKeyInfo SubjectPublicKeyInfo */
+	if (x509_parse_public_key(pos, end - pos, cert, &pos))
+		return -1;
+
+	if (pos == end)
+		return 0;
+
+	if (cert->version == X509_CERT_V1)
+		return 0;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+		wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+			   " tag to parse optional tbsCertificate "
+			   "field(s); parsed class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	if (hdr.tag == 1) {
+		/* issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL */
+		wpa_printf(MSG_DEBUG, "X509: issuerUniqueID");
+		/* TODO: parse UniqueIdentifier ::= BIT STRING */
+
+		pos = hdr.payload + hdr.length;
+		if (pos == end)
+			return 0;
+
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+			wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+				   " tag to parse optional tbsCertificate "
+				   "field(s); parsed class %d tag 0x%x",
+				   hdr.class, hdr.tag);
+			return -1;
+		}
+	}
+
+	if (hdr.tag == 2) {
+		/* subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL */
+		wpa_printf(MSG_DEBUG, "X509: subjectUniqueID");
+		/* TODO: parse UniqueIdentifier ::= BIT STRING */
+
+		pos = hdr.payload + hdr.length;
+		if (pos == end)
+			return 0;
+
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+			wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+				   " tag to parse optional tbsCertificate "
+				   "field(s); parsed class %d tag 0x%x",
+				   hdr.class, hdr.tag);
+			return -1;
+		}
+	}
+
+	if (hdr.tag != 3) {
+		wpa_printf(MSG_DEBUG, "X509: Ignored unexpected "
+			   "Context-Specific tag %d in optional "
+			   "tbsCertificate fields", hdr.tag);
+		return 0;
+	}
+
+	/* extensions      [3]  EXPLICIT Extensions OPTIONAL */
+
+	if (cert->version != X509_CERT_V3) {
+		wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and "
+			   "Extensions data which are only allowed for "
+			   "version 3", cert->version + 1);
+		return -1;
+	}
+
+	if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0)
+		return -1;
+
+	pos = hdr.payload + hdr.length;
+	if (pos < end) {
+		wpa_hexdump(MSG_DEBUG,
+			    "X509: Ignored extra tbsCertificate data",
+			    pos, end - pos);
+	}
+
+	return 0;
+}
+
+
+static int x509_rsadsi_oid(struct asn1_oid *oid)
+{
+	return oid->len >= 4 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 2 /* member-body */ &&
+		oid->oid[2] == 840 /* us */ &&
+		oid->oid[3] == 113549 /* rsadsi */;
+}
+
+
+static int x509_pkcs_oid(struct asn1_oid *oid)
+{
+	return oid->len >= 5 &&
+		x509_rsadsi_oid(oid) &&
+		oid->oid[4] == 1 /* pkcs */;
+}
+
+
+static int x509_digest_oid(struct asn1_oid *oid)
+{
+	return oid->len >= 5 &&
+		x509_rsadsi_oid(oid) &&
+		oid->oid[4] == 2 /* digestAlgorithm */;
+}
+
+
+static int x509_sha1_oid(struct asn1_oid *oid)
+{
+	return oid->len == 6 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 3 /* identified-organization */ &&
+		oid->oid[2] == 14 /* oiw */ &&
+		oid->oid[3] == 3 /* secsig */ &&
+		oid->oid[4] == 2 /* algorithms */ &&
+		oid->oid[5] == 26 /* id-sha1 */;
+}
+
+
+static int x509_sha256_oid(struct asn1_oid *oid)
+{
+	return oid->len == 9 &&
+		oid->oid[0] == 2 /* joint-iso-itu-t */ &&
+		oid->oid[1] == 16 /* country */ &&
+		oid->oid[2] == 840 /* us */ &&
+		oid->oid[3] == 1 /* organization */ &&
+		oid->oid[4] == 101 /* gov */ &&
+		oid->oid[5] == 3 /* csor */ &&
+		oid->oid[6] == 4 /* nistAlgorithm */ &&
+		oid->oid[7] == 2 /* hashAlgs */ &&
+		oid->oid[8] == 1 /* sha256 */;
+}
+
+
+/**
+ * x509_certificate_parse - Parse a X.509 certificate in DER format
+ * @buf: Pointer to the X.509 certificate in DER format
+ * @len: Buffer length
+ * Returns: Pointer to the parsed certificate or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned certificate by calling
+ * x509_certificate_free().
+ */
+struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end, *hash_start;
+	struct x509_certificate *cert;
+
+	cert = os_zalloc(sizeof(*cert) + len);
+	if (cert == NULL)
+		return NULL;
+	os_memcpy(cert + 1, buf, len);
+	cert->cert_start = (u8 *) (cert + 1);
+	cert->cert_len = len;
+
+	pos = buf;
+	end = buf + len;
+
+	/* RFC 3280 - X.509 v3 certificate / ASN.1 DER */
+
+	/* Certificate ::= SEQUENCE */
+	if (asn1_get_next(pos, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "X509: Certificate did not start with "
+			   "a valid SEQUENCE - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		x509_certificate_free(cert);
+		return NULL;
+	}
+	pos = hdr.payload;
+
+	if (pos + hdr.length > end) {
+		x509_certificate_free(cert);
+		return NULL;
+	}
+
+	if (pos + hdr.length < end) {
+		wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER "
+			    "encoded certificate",
+			    pos + hdr.length, end - (pos + hdr.length));
+		end = pos + hdr.length;
+	}
+
+	hash_start = pos;
+	cert->tbs_cert_start = cert->cert_start + (hash_start - buf);
+	if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) {
+		x509_certificate_free(cert);
+		return NULL;
+	}
+	cert->tbs_cert_len = pos - hash_start;
+
+	/* signatureAlgorithm AlgorithmIdentifier */
+	if (x509_parse_algorithm_identifier(pos, end - pos,
+					    &cert->signature_alg, &pos)) {
+		x509_certificate_free(cert);
+		return NULL;
+	}
+
+	/* signatureValue BIT STRING */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_BITSTRING) {
+		wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING "
+			   "(signatureValue) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		x509_certificate_free(cert);
+		return NULL;
+	}
+	if (hdr.length < 1) {
+		x509_certificate_free(cert);
+		return NULL;
+	}
+	pos = hdr.payload;
+	if (*pos) {
+		wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits",
+			   *pos);
+		/* PKCS #1 v1.5 10.2.1:
+		 * It is an error if the length in bits of the signature S is
+		 * not a multiple of eight.
+		 */
+		x509_certificate_free(cert);
+		return NULL;
+	}
+	os_free(cert->sign_value);
+	cert->sign_value = os_malloc(hdr.length - 1);
+	if (cert->sign_value == NULL) {
+		wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
+			   "signatureValue");
+		x509_certificate_free(cert);
+		return NULL;
+	}
+	os_memcpy(cert->sign_value, pos + 1, hdr.length - 1);
+	cert->sign_value_len = hdr.length - 1;
+	wpa_hexdump(MSG_MSGDUMP, "X509: signature",
+		    cert->sign_value, cert->sign_value_len);
+
+	return cert;
+}
+
+
+/**
+ * x509_certificate_check_signature - Verify certificate signature
+ * @issuer: Issuer certificate
+ * @cert: Certificate to be verified
+ * Returns: 0 if cert has a valid signature that was signed by the issuer,
+ * -1 if not
+ */
+int x509_certificate_check_signature(struct x509_certificate *issuer,
+				     struct x509_certificate *cert)
+{
+	struct crypto_public_key *pk;
+	u8 *data;
+	const u8 *pos, *end, *next, *da_end;
+	size_t data_len;
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+	u8 hash[32];
+	size_t hash_len;
+
+	if (!x509_pkcs_oid(&cert->signature.oid) ||
+	    cert->signature.oid.len != 7 ||
+	    cert->signature.oid.oid[5] != 1 /* pkcs-1 */) {
+		wpa_printf(MSG_DEBUG, "X509: Unrecognized signature "
+			   "algorithm");
+		return -1;
+	}
+
+	pk = crypto_public_key_import(issuer->public_key,
+				      issuer->public_key_len);
+	if (pk == NULL)
+		return -1;
+
+	data_len = cert->sign_value_len;
+	data = os_malloc(data_len);
+	if (data == NULL) {
+		crypto_public_key_free(pk);
+		return -1;
+	}
+
+	if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value,
+					    cert->sign_value_len, data,
+					    &data_len) < 0) {
+		wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature");
+		crypto_public_key_free(pk);
+		os_free(data);
+		return -1;
+	}
+	crypto_public_key_free(pk);
+
+	wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len);
+
+	/*
+	 * PKCS #1 v1.5, 10.1.2:
+	 *
+	 * DigestInfo ::= SEQUENCE {
+	 *     digestAlgorithm DigestAlgorithmIdentifier,
+	 *     digest Digest
+	 * }
+	 *
+	 * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+	 *
+	 * Digest ::= OCTET STRING
+	 *
+	 */
+	if (asn1_get_next(data, data_len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+			   "(DigestInfo) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		os_free(data);
+		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, "X509: Expected SEQUENCE "
+			   "(AlgorithmIdentifier) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		os_free(data);
+		return -1;
+	}
+	da_end = hdr.payload + hdr.length;
+
+	if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) {
+		wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm");
+		os_free(data);
+		return -1;
+	}
+
+	if (x509_sha1_oid(&oid)) {
+		if (cert->signature.oid.oid[6] !=
+		    5 /* sha-1WithRSAEncryption */) {
+			wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 "
+				   "does not match with certificate "
+				   "signatureAlgorithm (%lu)",
+				   cert->signature.oid.oid[6]);
+			os_free(data);
+			return -1;
+		}
+		goto skip_digest_oid;
+	}
+
+	if (x509_sha256_oid(&oid)) {
+		if (cert->signature.oid.oid[6] !=
+		    11 /* sha2561WithRSAEncryption */) {
+			wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 "
+				   "does not match with certificate "
+				   "signatureAlgorithm (%lu)",
+				   cert->signature.oid.oid[6]);
+			os_free(data);
+			return -1;
+		}
+		goto skip_digest_oid;
+	}
+
+	if (!x509_digest_oid(&oid)) {
+		wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm");
+		os_free(data);
+		return -1;
+	}
+	switch (oid.oid[5]) {
+	case 5: /* md5 */
+		if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */)
+		{
+			wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does "
+				   "not match with certificate "
+				   "signatureAlgorithm (%lu)",
+				   cert->signature.oid.oid[6]);
+			os_free(data);
+			return -1;
+		}
+		break;
+	case 2: /* md2 */
+	case 4: /* md4 */
+	default:
+		wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm "
+			   "(%lu)", oid.oid[5]);
+		os_free(data);
+		return -1;
+	}
+
+skip_digest_oid:
+	/* Digest ::= OCTET STRING */
+	pos = da_end;
+	end = data + data_len;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING "
+			   "(Digest) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		os_free(data);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest",
+		    hdr.payload, hdr.length);
+
+	switch (cert->signature.oid.oid[6]) {
+	case 4: /* md5WithRSAEncryption */
+		md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+			   hash);
+		hash_len = 16;
+		wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)",
+			    hash, hash_len);
+		break;
+	case 5: /* sha-1WithRSAEncryption */
+		sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+			    hash);
+		hash_len = 20;
+		wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)",
+			    hash, hash_len);
+		break;
+	case 11: /* sha256WithRSAEncryption */
+		sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+			      hash);
+		hash_len = 32;
+		wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)",
+			    hash, hash_len);
+		break;
+	case 2: /* md2WithRSAEncryption */
+	case 12: /* sha384WithRSAEncryption */
+	case 13: /* sha512WithRSAEncryption */
+	default:
+		wpa_printf(MSG_INFO, "X509: Unsupported certificate signature "
+			   "algorithm (%lu)", cert->signature.oid.oid[6]);
+		os_free(data);
+		return -1;
+	}
+
+	if (hdr.length != hash_len ||
+	    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 "
+		   "calculated tbsCertificate hash");
+
+	return 0;
+}
+
+
+static int x509_valid_issuer(const struct x509_certificate *cert)
+{
+	if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) &&
+	    !cert->ca) {
+		wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an "
+			   "issuer");
+		return -1;
+	}
+
+	if (cert->version == X509_CERT_V3 &&
+	    !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) {
+		wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not "
+			   "include BasicConstraints extension");
+		return -1;
+	}
+
+	if ((cert->extensions_present & X509_EXT_KEY_USAGE) &&
+	    !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) {
+		wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have "
+			   "keyCertSign bit in Key Usage");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * x509_certificate_chain_validate - Validate X.509 certificate chain
+ * @trusted: List of trusted certificates
+ * @chain: Certificate chain to be validated (first chain must be issued by
+ * signed by the second certificate in the chain and so on)
+ * @reason: Buffer for returning failure reason (X509_VALIDATE_*)
+ * Returns: 0 if chain is valid, -1 if not
+ */
+int x509_certificate_chain_validate(struct x509_certificate *trusted,
+				    struct x509_certificate *chain,
+				    int *reason, int disable_time_checks)
+{
+	long unsigned idx;
+	int chain_trusted = 0;
+	struct x509_certificate *cert, *trust;
+	char buf[128];
+	struct os_time now;
+
+	*reason = X509_VALIDATE_OK;
+
+	wpa_printf(MSG_DEBUG, "X509: Validate certificate chain");
+	os_get_time(&now);
+
+	for (cert = chain, idx = 0; cert; cert = cert->next, idx++) {
+		x509_name_string(&cert->subject, buf, sizeof(buf)); 
+		wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf);
+
+		if (chain_trusted)
+			continue;
+
+		if (!disable_time_checks &&
+		    ((unsigned long) now.sec <
+		     (unsigned long) cert->not_before ||
+		     (unsigned long) now.sec >
+		     (unsigned long) cert->not_after)) {
+			wpa_printf(MSG_INFO, "X509: Certificate not valid "
+				   "(now=%lu not_before=%lu not_after=%lu)",
+				   now.sec, cert->not_before, cert->not_after);
+			*reason = X509_VALIDATE_CERTIFICATE_EXPIRED;
+			return -1;
+		}
+
+		if (cert->next) {
+			if (x509_name_compare(&cert->issuer,
+					      &cert->next->subject) != 0) {
+				wpa_printf(MSG_DEBUG, "X509: Certificate "
+					   "chain issuer name mismatch");
+				x509_name_string(&cert->issuer, buf,
+						 sizeof(buf)); 
+				wpa_printf(MSG_DEBUG, "X509: cert issuer: %s",
+					   buf);
+				x509_name_string(&cert->next->subject, buf,
+						 sizeof(buf)); 
+				wpa_printf(MSG_DEBUG, "X509: next cert "
+					   "subject: %s", buf);
+				*reason = X509_VALIDATE_CERTIFICATE_UNKNOWN;
+				return -1;
+			}
+
+			if (x509_valid_issuer(cert->next) < 0) {
+				*reason = X509_VALIDATE_BAD_CERTIFICATE;
+				return -1;
+			}
+
+			if ((cert->next->extensions_present &
+			     X509_EXT_PATH_LEN_CONSTRAINT) &&
+			    idx > cert->next->path_len_constraint) {
+				wpa_printf(MSG_DEBUG, "X509: pathLenConstraint"
+					   " not met (idx=%lu issuer "
+					   "pathLenConstraint=%lu)", idx,
+					   cert->next->path_len_constraint);
+				*reason = X509_VALIDATE_BAD_CERTIFICATE;
+				return -1;
+			}
+
+			if (x509_certificate_check_signature(cert->next, cert)
+			    < 0) {
+				wpa_printf(MSG_DEBUG, "X509: Invalid "
+					   "certificate signature within "
+					   "chain");
+				*reason = X509_VALIDATE_BAD_CERTIFICATE;
+				return -1;
+			}
+		}
+
+		for (trust = trusted; trust; trust = trust->next) {
+			if (x509_name_compare(&cert->issuer, &trust->subject)
+			    == 0)
+				break;
+		}
+
+		if (trust) {
+			wpa_printf(MSG_DEBUG, "X509: Found issuer from the "
+				   "list of trusted certificates");
+			if (x509_valid_issuer(trust) < 0) {
+				*reason = X509_VALIDATE_BAD_CERTIFICATE;
+				return -1;
+			}
+
+			if (x509_certificate_check_signature(trust, cert) < 0)
+			{
+				wpa_printf(MSG_DEBUG, "X509: Invalid "
+					   "certificate signature");
+				*reason = X509_VALIDATE_BAD_CERTIFICATE;
+				return -1;
+			}
+
+			wpa_printf(MSG_DEBUG, "X509: Trusted certificate "
+				   "found to complete the chain");
+			chain_trusted = 1;
+		}
+	}
+
+	if (!chain_trusted) {
+		wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers "
+			   "from the list of trusted certificates");
+		if (trusted) {
+			*reason = X509_VALIDATE_UNKNOWN_CA;
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "X509: Certificate chain validation "
+			   "disabled - ignore unknown CA issue");
+	}
+
+	wpa_printf(MSG_DEBUG, "X509: Certificate chain valid");
+
+	return 0;
+}
+
+
+/**
+ * x509_certificate_get_subject - Get a certificate based on Subject name
+ * @chain: Certificate chain to search through
+ * @name: Subject name to search for
+ * Returns: Pointer to the certificate with the given Subject name or
+ * %NULL on failure
+ */
+struct x509_certificate *
+x509_certificate_get_subject(struct x509_certificate *chain,
+			     struct x509_name *name)
+{
+	struct x509_certificate *cert;
+
+	for (cert = chain; cert; cert = cert->next) {
+		if (x509_name_compare(&cert->subject, name) == 0)
+			return cert;
+	}
+	return NULL;
+}
+
+
+/**
+ * x509_certificate_self_signed - Is the certificate self-signed?
+ * @cert: Certificate
+ * Returns: 1 if certificate is self-signed, 0 if not
+ */
+int x509_certificate_self_signed(struct x509_certificate *cert)
+{
+	return x509_name_compare(&cert->issuer, &cert->subject) == 0;
+}
diff --git a/hostap/src/tls/x509v3.h b/hostap/src/tls/x509v3.h
new file mode 100644
index 0000000..91a35ba
--- /dev/null
+++ b/hostap/src/tls/x509v3.h
@@ -0,0 +1,123 @@
+/*
+ * X.509v3 certificate parsing and processing
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef X509V3_H
+#define X509V3_H
+
+#include "asn1.h"
+
+struct x509_algorithm_identifier {
+	struct asn1_oid oid;
+};
+
+struct x509_name_attr {
+	enum x509_name_attr_type {
+		X509_NAME_ATTR_NOT_USED,
+		X509_NAME_ATTR_DC,
+		X509_NAME_ATTR_CN,
+		X509_NAME_ATTR_C,
+		X509_NAME_ATTR_L,
+		X509_NAME_ATTR_ST,
+		X509_NAME_ATTR_O,
+		X509_NAME_ATTR_OU
+	} type;
+	char *value;
+};
+
+#define X509_MAX_NAME_ATTRIBUTES 20
+
+struct x509_name {
+	struct x509_name_attr attr[X509_MAX_NAME_ATTRIBUTES];
+	size_t num_attr;
+	char *email; /* emailAddress */
+
+	/* from alternative name extension */
+	char *alt_email; /* rfc822Name */
+	char *dns; /* dNSName */
+	char *uri; /* uniformResourceIdentifier */
+	u8 *ip; /* iPAddress */
+	size_t ip_len; /* IPv4: 4, IPv6: 16 */
+	struct asn1_oid rid; /* registeredID */
+};
+
+struct x509_certificate {
+	struct x509_certificate *next;
+	enum { X509_CERT_V1 = 0, X509_CERT_V2 = 1, X509_CERT_V3 = 2 } version;
+	unsigned long serial_number;
+	struct x509_algorithm_identifier signature;
+	struct x509_name issuer;
+	struct x509_name subject;
+	os_time_t not_before;
+	os_time_t not_after;
+	struct x509_algorithm_identifier public_key_alg;
+	u8 *public_key;
+	size_t public_key_len;
+	struct x509_algorithm_identifier signature_alg;
+	u8 *sign_value;
+	size_t sign_value_len;
+
+	/* Extensions */
+	unsigned int extensions_present;
+#define X509_EXT_BASIC_CONSTRAINTS		(1 << 0)
+#define X509_EXT_PATH_LEN_CONSTRAINT		(1 << 1)
+#define X509_EXT_KEY_USAGE			(1 << 2)
+#define X509_EXT_SUBJECT_ALT_NAME		(1 << 3)
+#define X509_EXT_ISSUER_ALT_NAME		(1 << 4)
+
+	/* BasicConstraints */
+	int ca; /* cA */
+	unsigned long path_len_constraint; /* pathLenConstraint */
+
+	/* KeyUsage */
+	unsigned long key_usage;
+#define X509_KEY_USAGE_DIGITAL_SIGNATURE	(1 << 0)
+#define X509_KEY_USAGE_NON_REPUDIATION		(1 << 1)
+#define X509_KEY_USAGE_KEY_ENCIPHERMENT		(1 << 2)
+#define X509_KEY_USAGE_DATA_ENCIPHERMENT	(1 << 3)
+#define X509_KEY_USAGE_KEY_AGREEMENT		(1 << 4)
+#define X509_KEY_USAGE_KEY_CERT_SIGN		(1 << 5)
+#define X509_KEY_USAGE_CRL_SIGN			(1 << 6)
+#define X509_KEY_USAGE_ENCIPHER_ONLY		(1 << 7)
+#define X509_KEY_USAGE_DECIPHER_ONLY		(1 << 8)
+
+	/*
+	 * The DER format certificate follows struct x509_certificate. These
+	 * pointers point to that buffer.
+	 */
+	const u8 *cert_start;
+	size_t cert_len;
+	const u8 *tbs_cert_start;
+	size_t tbs_cert_len;
+};
+
+enum {
+	X509_VALIDATE_OK,
+	X509_VALIDATE_BAD_CERTIFICATE,
+	X509_VALIDATE_UNSUPPORTED_CERTIFICATE,
+	X509_VALIDATE_CERTIFICATE_REVOKED,
+	X509_VALIDATE_CERTIFICATE_EXPIRED,
+	X509_VALIDATE_CERTIFICATE_UNKNOWN,
+	X509_VALIDATE_UNKNOWN_CA
+};
+
+void x509_certificate_free(struct x509_certificate *cert);
+struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len);
+void x509_name_string(struct x509_name *name, char *buf, size_t len);
+int x509_name_compare(struct x509_name *a, struct x509_name *b);
+void x509_certificate_chain_free(struct x509_certificate *cert);
+int x509_certificate_check_signature(struct x509_certificate *issuer,
+				     struct x509_certificate *cert);
+int x509_certificate_chain_validate(struct x509_certificate *trusted,
+				    struct x509_certificate *chain,
+				    int *reason, int disable_time_checks);
+struct x509_certificate *
+x509_certificate_get_subject(struct x509_certificate *chain,
+			     struct x509_name *name);
+int x509_certificate_self_signed(struct x509_certificate *cert);
+
+#endif /* X509V3_H */
diff --git a/hostap/src/utils/Makefile b/hostap/src/utils/Makefile
new file mode 100644
index 0000000..8aad813
--- /dev/null
+++ b/hostap/src/utils/Makefile
@@ -0,0 +1,41 @@
+all: libutils.a
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libutils.a
+
+install:
+	@echo Nothing to be made.
+
+
+include ../lib.rules
+
+#CFLAGS += -DWPA_TRACE
+CFLAGS += -DCONFIG_IPV6
+CFLAGS += -DCONFIG_DEBUG_FILE
+
+LIB_OBJS= \
+	base64.o \
+	bitfield.o \
+	common.o \
+	ip_addr.o \
+	radiotap.o \
+	trace.o \
+	uuid.o \
+	wpa_debug.o \
+	wpabuf.o
+
+# Pick correct OS wrapper implementation
+LIB_OBJS += os_unix.o
+
+# Pick correct event loop implementation
+LIB_OBJS += eloop.o
+
+# Pick correct edit implementation
+LIB_OBJS += edit.o
+
+#LIB_OBJS += pcsc_funcs.o
+
+libutils.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/utils/base64.c b/hostap/src/utils/base64.c
new file mode 100644
index 0000000..d44f290
--- /dev/null
+++ b/hostap/src/utils/base64.c
@@ -0,0 +1,157 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005-2011, 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 "os.h"
+#include "base64.h"
+
+static const unsigned char base64_table[65] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+			      size_t *out_len)
+{
+	unsigned char *out, *pos;
+	const unsigned char *end, *in;
+	size_t olen;
+	int line_len;
+
+	olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
+	olen += olen / 72; /* line feeds */
+	olen++; /* nul termination */
+	if (olen < len)
+		return NULL; /* integer overflow */
+	out = os_malloc(olen);
+	if (out == NULL)
+		return NULL;
+
+	end = src + len;
+	in = src;
+	pos = out;
+	line_len = 0;
+	while (end - in >= 3) {
+		*pos++ = base64_table[(in[0] >> 2) & 0x3f];
+		*pos++ = base64_table[(((in[0] & 0x03) << 4) |
+				       (in[1] >> 4)) & 0x3f];
+		*pos++ = base64_table[(((in[1] & 0x0f) << 2) |
+				       (in[2] >> 6)) & 0x3f];
+		*pos++ = base64_table[in[2] & 0x3f];
+		in += 3;
+		line_len += 4;
+		if (line_len >= 72) {
+			*pos++ = '\n';
+			line_len = 0;
+		}
+	}
+
+	if (end - in) {
+		*pos++ = base64_table[(in[0] >> 2) & 0x3f];
+		if (end - in == 1) {
+			*pos++ = base64_table[((in[0] & 0x03) << 4) & 0x3f];
+			*pos++ = '=';
+		} else {
+			*pos++ = base64_table[(((in[0] & 0x03) << 4) |
+					       (in[1] >> 4)) & 0x3f];
+			*pos++ = base64_table[((in[1] & 0x0f) << 2) & 0x3f];
+		}
+		*pos++ = '=';
+		line_len += 4;
+	}
+
+	if (line_len)
+		*pos++ = '\n';
+
+	*pos = '\0';
+	if (out_len)
+		*out_len = pos - out;
+	return out;
+}
+
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+			      size_t *out_len)
+{
+	unsigned char dtable[256], *out, *pos, block[4], tmp;
+	size_t i, count, olen;
+	int pad = 0;
+
+	os_memset(dtable, 0x80, 256);
+	for (i = 0; i < sizeof(base64_table) - 1; i++)
+		dtable[base64_table[i]] = (unsigned char) i;
+	dtable['='] = 0;
+
+	count = 0;
+	for (i = 0; i < len; i++) {
+		if (dtable[src[i]] != 0x80)
+			count++;
+	}
+
+	if (count == 0 || count % 4)
+		return NULL;
+
+	olen = count / 4 * 3;
+	pos = out = os_malloc(olen);
+	if (out == NULL)
+		return NULL;
+
+	count = 0;
+	for (i = 0; i < len; i++) {
+		tmp = dtable[src[i]];
+		if (tmp == 0x80)
+			continue;
+
+		if (src[i] == '=')
+			pad++;
+		block[count] = tmp;
+		count++;
+		if (count == 4) {
+			*pos++ = (block[0] << 2) | (block[1] >> 4);
+			*pos++ = (block[1] << 4) | (block[2] >> 2);
+			*pos++ = (block[2] << 6) | block[3];
+			count = 0;
+			if (pad) {
+				if (pad == 1)
+					pos--;
+				else if (pad == 2)
+					pos -= 2;
+				else {
+					/* Invalid padding */
+					os_free(out);
+					return NULL;
+				}
+				break;
+			}
+		}
+	}
+
+	*out_len = pos - out;
+	return out;
+}
diff --git a/hostap/src/utils/base64.h b/hostap/src/utils/base64.h
new file mode 100644
index 0000000..aa21fd0
--- /dev/null
+++ b/hostap/src/utils/base64.h
@@ -0,0 +1,17 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BASE64_H
+#define BASE64_H
+
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+			      size_t *out_len);
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+			      size_t *out_len);
+
+#endif /* BASE64_H */
diff --git a/hostap/src/utils/bitfield.c b/hostap/src/utils/bitfield.c
new file mode 100644
index 0000000..8dcec39
--- /dev/null
+++ b/hostap/src/utils/bitfield.c
@@ -0,0 +1,89 @@
+/*
+ * Bitfield
+ * Copyright (c) 2013, 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 "bitfield.h"
+
+
+struct bitfield {
+	u8 *bits;
+	size_t max_bits;
+};
+
+
+struct bitfield * bitfield_alloc(size_t max_bits)
+{
+	struct bitfield *bf;
+
+	bf = os_zalloc(sizeof(*bf) + (max_bits + 7) / 8);
+	if (bf == NULL)
+		return NULL;
+	bf->bits = (u8 *) (bf + 1);
+	bf->max_bits = max_bits;
+	return bf;
+}
+
+
+void bitfield_free(struct bitfield *bf)
+{
+	os_free(bf);
+}
+
+
+void bitfield_set(struct bitfield *bf, size_t bit)
+{
+	if (bit >= bf->max_bits)
+		return;
+	bf->bits[bit / 8] |= BIT(bit % 8);
+}
+
+
+void bitfield_clear(struct bitfield *bf, size_t bit)
+{
+	if (bit >= bf->max_bits)
+		return;
+	bf->bits[bit / 8] &= ~BIT(bit % 8);
+}
+
+
+int bitfield_is_set(struct bitfield *bf, size_t bit)
+{
+	if (bit >= bf->max_bits)
+		return 0;
+	return !!(bf->bits[bit / 8] & BIT(bit % 8));
+}
+
+
+static int first_zero(u8 val)
+{
+	int i;
+	for (i = 0; i < 8; i++) {
+		if (!(val & 0x01))
+			return i;
+		val >>= 1;
+	}
+	return -1;
+}
+
+
+int bitfield_get_first_zero(struct bitfield *bf)
+{
+	size_t i;
+	for (i = 0; i < (bf->max_bits + 7) / 8; i++) {
+		if (bf->bits[i] != 0xff)
+			break;
+	}
+	if (i == (bf->max_bits + 7) / 8)
+		return -1;
+	i = i * 8 + first_zero(bf->bits[i]);
+	if (i >= bf->max_bits)
+		return -1;
+	return i;
+}
diff --git a/hostap/src/utils/bitfield.h b/hostap/src/utils/bitfield.h
new file mode 100644
index 0000000..7050a20
--- /dev/null
+++ b/hostap/src/utils/bitfield.h
@@ -0,0 +1,21 @@
+/*
+ * Bitfield
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BITFIELD_H
+#define BITFIELD_H
+
+struct bitfield;
+
+struct bitfield * bitfield_alloc(size_t max_bits);
+void bitfield_free(struct bitfield *bf);
+void bitfield_set(struct bitfield *bf, size_t bit);
+void bitfield_clear(struct bitfield *bf, size_t bit);
+int bitfield_is_set(struct bitfield *bf, size_t bit);
+int bitfield_get_first_zero(struct bitfield *bf);
+
+#endif /* BITFIELD_H */
diff --git a/hostap/src/utils/browser-android.c b/hostap/src/utils/browser-android.c
new file mode 100644
index 0000000..9ce1a5c
--- /dev/null
+++ b/hostap/src/utils/browser-android.c
@@ -0,0 +1,128 @@
+/*
+ * 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)
+{
+	struct http_server *http;
+	struct in_addr addr;
+	struct browser_data data;
+	pid_t pid;
+
+	wpa_printf(MSG_INFO, "Launching Android browser to %s", url);
+
+	os_memset(&data, 0, sizeof(data));
+
+	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;
+	}
+
+	pid = fork();
+	if (pid < 0) {
+		wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
+		http_server_deinit(http);
+		eloop_destroy();
+		return -1;
+	}
+
+	if (pid == 0) {
+		/* run the external command in the child process */
+		char *argv[9];
+
+		argv[0] = "browser-android";
+		argv[1] = "start";
+		argv[2] = "-a";
+		argv[3] = "android.intent.action.VIEW";
+		argv[4] = "-d";
+		argv[5] = (void *) url;
+		argv[6] = "-n";
+		argv[7] = "com.android.browser/.BrowserActivity";
+		argv[8] = NULL;
+
+		execv("/system/bin/am", argv);
+		wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+		exit(0);
+		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("/system/bin/input keyevent KEYCODE_HOME") != 0) {
+		wpa_printf(MSG_INFO, "Failed to inject keyevent");
+	}
+
+	return data.success;
+}
diff --git a/hostap/src/utils/browser-system.c b/hostap/src/utils/browser-system.c
new file mode 100644
index 0000000..aed3970
--- /dev/null
+++ b/hostap/src/utils/browser-system.c
@@ -0,0 +1,119 @@
+/*
+ * 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)
+{
+	struct http_server *http;
+	struct in_addr addr;
+	struct browser_data data;
+	pid_t pid;
+
+	wpa_printf(MSG_INFO, "Launching system browser to %s", url);
+
+	os_memset(&data, 0, sizeof(data));
+
+	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;
+	}
+
+	pid = fork();
+	if (pid < 0) {
+		wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
+		http_server_deinit(http);
+		eloop_destroy();
+		return -1;
+	}
+
+	if (pid == 0) {
+		/* run the external command in the child process */
+		char *argv[3];
+
+		argv[0] = "browser-system";
+		argv[1] = (void *) url;
+		argv[2] = NULL;
+
+		execv("/usr/bin/x-www-browser", argv);
+		wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+		exit(0);
+		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/hostap/src/utils/browser-wpadebug.c b/hostap/src/utils/browser-wpadebug.c
new file mode 100644
index 0000000..59ba4d1
--- /dev/null
+++ b/hostap/src/utils/browser-wpadebug.c
@@ -0,0 +1,138 @@
+/*
+ * 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)
+{
+	struct http_server *http;
+	struct in_addr addr;
+	struct browser_data data;
+	pid_t pid;
+
+	wpa_printf(MSG_INFO, "Launching wpadebug browser to %s", url);
+
+	os_memset(&data, 0, sizeof(data));
+
+	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;
+	}
+
+	pid = fork();
+	if (pid < 0) {
+		wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
+		http_server_deinit(http);
+		eloop_destroy();
+		return -1;
+	}
+
+	if (pid == 0) {
+		/* run the external command in the child process */
+		char *argv[14];
+
+		argv[0] = "browser-wpadebug";
+		argv[1] = "start";
+		argv[2] = "-a";
+		argv[3] = "android.action.MAIN";
+		argv[4] = "-c";
+		argv[5] = "android.intent.category.LAUNCHER";
+		argv[6] = "-n";
+		argv[7] = "w1.fi.wpadebug/.WpaWebViewActivity";
+		argv[8] = "-e";
+		argv[9] = "w1.fi.wpadebug.URL";
+		argv[10] = (void *) url;
+		argv[11] = "--user";
+		argv[12] = "-3"; /* USER_CURRENT_OR_SELF */
+		argv[13] = NULL;
+
+		execv("/system/bin/am", argv);
+		wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+		exit(0);
+		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 (os_exec("/system/bin/am",
+		    "start -a android.action.MAIN "
+		    "-c android.intent.category.LAUNCHER "
+		    "-n w1.fi.wpadebug/.WpaWebViewActivity "
+		    "-e w1.fi.wpadebug.URL FINISH", 1) != 0) {
+		wpa_printf(MSG_INFO, "Failed to close wpadebug browser");
+	}
+
+	return data.success;
+}
diff --git a/hostap/src/utils/browser.c b/hostap/src/utils/browser.c
new file mode 100644
index 0000000..9cf6152
--- /dev/null
+++ b/hostap/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/hostap/src/utils/browser.h b/hostap/src/utils/browser.h
new file mode 100644
index 0000000..aaa0eed
--- /dev/null
+++ b/hostap/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/hostap/src/utils/build_config.h b/hostap/src/utils/build_config.h
new file mode 100644
index 0000000..c6f4e43
--- /dev/null
+++ b/hostap/src/utils/build_config.h
@@ -0,0 +1,50 @@
+/*
+ * wpa_supplicant/hostapd - Build time configuration defines
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This header file can be used to define configuration defines that were
+ * originally defined in Makefile. This is mainly meant for IDE use or for
+ * systems that do not have suitable 'make' tool. In these cases, it may be
+ * easier to have a single place for defining all the needed C pre-processor
+ * defines.
+ */
+
+#ifndef BUILD_CONFIG_H
+#define BUILD_CONFIG_H
+
+/* Insert configuration defines, e.g., #define EAP_MD5, here, if needed. */
+
+#ifdef CONFIG_WIN32_DEFAULTS
+#define CONFIG_NATIVE_WINDOWS
+#define CONFIG_ANSI_C_EXTRA
+#define CONFIG_WINPCAP
+#define IEEE8021X_EAPOL
+#define PKCS12_FUNCS
+#define PCSC_FUNCS
+#define CONFIG_CTRL_IFACE
+#define CONFIG_CTRL_IFACE_NAMED_PIPE
+#define CONFIG_DRIVER_NDIS
+#define CONFIG_NDIS_EVENTS_INTEGRATED
+#define CONFIG_DEBUG_FILE
+#define EAP_MD5
+#define EAP_TLS
+#define EAP_MSCHAPv2
+#define EAP_PEAP
+#define EAP_TTLS
+#define EAP_GTC
+#define EAP_OTP
+#define EAP_LEAP
+#define EAP_TNC
+#define _CRT_SECURE_NO_DEPRECATE
+
+#ifdef USE_INTERNAL_CRYPTO
+#define CONFIG_TLS_INTERNAL_CLIENT
+#define CONFIG_INTERNAL_LIBTOMMATH
+#define CONFIG_CRYPTO_INTERNAL
+#endif /* USE_INTERNAL_CRYPTO */
+#endif /* CONFIG_WIN32_DEFAULTS */
+
+#endif /* BUILD_CONFIG_H */
diff --git a/hostap/src/utils/common.c b/hostap/src/utils/common.c
new file mode 100644
index 0000000..660e9fc
--- /dev/null
+++ b/hostap/src/utils/common.c
@@ -0,0 +1,1125 @@
+/*
+ * wpa_supplicant/hostapd / common helper functions, etc.
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common/ieee802_11_defs.h"
+#include "common.h"
+
+
+static int hex2num(char c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	if (c >= 'a' && c <= 'f')
+		return c - 'a' + 10;
+	if (c >= 'A' && c <= 'F')
+		return c - 'A' + 10;
+	return -1;
+}
+
+
+int hex2byte(const char *hex)
+{
+	int a, b;
+	a = hex2num(*hex++);
+	if (a < 0)
+		return -1;
+	b = hex2num(*hex++);
+	if (b < 0)
+		return -1;
+	return (a << 4) | b;
+}
+
+
+static const char * hwaddr_parse(const char *txt, u8 *addr)
+{
+	size_t i;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		int a;
+
+		a = hex2byte(txt);
+		if (a < 0)
+			return NULL;
+		txt += 2;
+		addr[i] = a;
+		if (i < ETH_ALEN - 1 && *txt++ != ':')
+			return NULL;
+	}
+	return txt;
+}
+
+
+/**
+ * hwaddr_aton - Convert ASCII string to MAC address (colon-delimited format)
+ * @txt: MAC address as a string (e.g., "00:11:22:33:44:55")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_aton(const char *txt, u8 *addr)
+{
+	return hwaddr_parse(txt, addr) ? 0 : -1;
+}
+
+
+/**
+ * hwaddr_masked_aton - Convert ASCII string with optional mask to MAC address (colon-delimited format)
+ * @txt: MAC address with optional mask as a string (e.g., "00:11:22:33:44:55/ff:ff:ff:ff:00:00")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * @mask: Buffer for the MAC address mask (ETH_ALEN = 6 bytes)
+ * @maskable: Flag to indicate whether a mask is allowed
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable)
+{
+	const char *r;
+
+	/* parse address part */
+	r = hwaddr_parse(txt, addr);
+	if (!r)
+		return -1;
+
+	/* check for optional mask */
+	if (*r == '\0' || isspace(*r)) {
+		/* no mask specified, assume default */
+		os_memset(mask, 0xff, ETH_ALEN);
+	} else if (maskable && *r == '/') {
+		/* mask specified and allowed */
+		r = hwaddr_parse(r + 1, mask);
+		/* parser error? */
+		if (!r)
+			return -1;
+	} else {
+		/* mask specified but not allowed or trailing garbage */
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format)
+ * @txt: MAC address as a string (e.g., "001122334455")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_compact_aton(const char *txt, u8 *addr)
+{
+	int i;
+
+	for (i = 0; i < 6; i++) {
+		int a, b;
+
+		a = hex2num(*txt++);
+		if (a < 0)
+			return -1;
+		b = hex2num(*txt++);
+		if (b < 0)
+			return -1;
+		*addr++ = (a << 4) | b;
+	}
+
+	return 0;
+}
+
+/**
+ * hwaddr_aton2 - Convert ASCII string to MAC address (in any known format)
+ * @txt: MAC address as a string (e.g., 00:11:22:33:44:55 or 0011.2233.4455)
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: Characters used (> 0) on success, -1 on failure
+ */
+int hwaddr_aton2(const char *txt, u8 *addr)
+{
+	int i;
+	const char *pos = txt;
+
+	for (i = 0; i < 6; i++) {
+		int a, b;
+
+		while (*pos == ':' || *pos == '.' || *pos == '-')
+			pos++;
+
+		a = hex2num(*pos++);
+		if (a < 0)
+			return -1;
+		b = hex2num(*pos++);
+		if (b < 0)
+			return -1;
+		*addr++ = (a << 4) | b;
+	}
+
+	return pos - txt;
+}
+
+
+/**
+ * hexstr2bin - Convert ASCII hex string into binary data
+ * @hex: ASCII hex string (e.g., "01ab")
+ * @buf: Buffer for the binary data
+ * @len: Length of the text to convert in bytes (of buf); hex will be double
+ * this size
+ * Returns: 0 on success, -1 on failure (invalid hex string)
+ */
+int hexstr2bin(const char *hex, u8 *buf, size_t len)
+{
+	size_t i;
+	int a;
+	const char *ipos = hex;
+	u8 *opos = buf;
+
+	for (i = 0; i < len; i++) {
+		a = hex2byte(ipos);
+		if (a < 0)
+			return -1;
+		*opos++ = a;
+		ipos += 2;
+	}
+	return 0;
+}
+
+
+int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask)
+{
+	size_t i;
+	int print_mask = 0;
+	int res;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		if (mask[i] != 0xff) {
+			print_mask = 1;
+			break;
+		}
+	}
+
+	if (print_mask)
+		res = os_snprintf(buf, len, MACSTR "/" MACSTR,
+				  MAC2STR(addr), MAC2STR(mask));
+	else
+		res = os_snprintf(buf, len, MACSTR, MAC2STR(addr));
+	if (os_snprintf_error(len, res))
+		return -1;
+	return res;
+}
+
+
+/**
+ * inc_byte_array - Increment arbitrary length byte array by one
+ * @counter: Pointer to byte array
+ * @len: Length of the counter in bytes
+ *
+ * This function increments the last byte of the counter by one and continues
+ * rolling over to more significant bytes if the byte was incremented from
+ * 0xff to 0x00.
+ */
+void inc_byte_array(u8 *counter, size_t len)
+{
+	int pos = len - 1;
+	while (pos >= 0) {
+		counter[pos]++;
+		if (counter[pos] != 0)
+			break;
+		pos--;
+	}
+}
+
+
+void wpa_get_ntp_timestamp(u8 *buf)
+{
+	struct os_time now;
+	u32 sec, usec;
+	be32 tmp;
+
+	/* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */
+	os_get_time(&now);
+	sec = now.sec + 2208988800U; /* Epoch to 1900 */
+	/* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */
+	usec = now.usec;
+	usec = 4295 * usec - (usec >> 5) - (usec >> 9);
+	tmp = host_to_be32(sec);
+	os_memcpy(buf, (u8 *) &tmp, 4);
+	tmp = host_to_be32(usec);
+	os_memcpy(buf + 4, (u8 *) &tmp, 4);
+}
+
+/**
+ * wpa_scnprintf - Simpler-to-use snprintf function
+ * @buf: Output buffer
+ * @size: Buffer size
+ * @fmt: format
+ *
+ * Simpler snprintf version that doesn't require further error checks - the
+ * return value only indicates how many bytes were actually written, excluding
+ * the NULL byte (i.e., 0 on error, size-1 if buffer is not big enough).
+ */
+int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...)
+{
+	va_list ap;
+	int ret;
+
+	if (!size)
+		return 0;
+
+	va_start(ap, fmt);
+	ret = vsnprintf(buf, size, fmt, ap);
+	va_end(ap);
+
+	if (ret < 0)
+		return 0;
+	if ((size_t) ret >= size)
+		return size - 1;
+
+	return ret;
+}
+
+
+int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
+			 char sep)
+{
+	size_t i;
+	char *pos = buf, *end = buf + buf_size;
+	int ret;
+
+	if (buf_size == 0)
+		return 0;
+
+	for (i = 0; i < len; i++) {
+		ret = os_snprintf(pos, end - pos, "%02x%c",
+				  data[i], sep);
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return pos - buf;
+		}
+		pos += ret;
+	}
+	pos[-1] = '\0';
+	return pos - buf;
+}
+
+
+static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data,
+				    size_t len, int uppercase)
+{
+	size_t i;
+	char *pos = buf, *end = buf + buf_size;
+	int ret;
+	if (buf_size == 0)
+		return 0;
+	for (i = 0; i < len; i++) {
+		ret = os_snprintf(pos, end - pos, uppercase ? "%02X" : "%02x",
+				  data[i]);
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return pos - buf;
+		}
+		pos += ret;
+	}
+	end[-1] = '\0';
+	return pos - buf;
+}
+
+/**
+ * wpa_snprintf_hex - Print data as a hex string into a buffer
+ * @buf: Memory area to use as the output buffer
+ * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1)
+ * @data: Data to be printed
+ * @len: Length of data in bytes
+ * Returns: Number of bytes written
+ */
+int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len)
+{
+	return _wpa_snprintf_hex(buf, buf_size, data, len, 0);
+}
+
+
+/**
+ * wpa_snprintf_hex_uppercase - Print data as a upper case hex string into buf
+ * @buf: Memory area to use as the output buffer
+ * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1)
+ * @data: Data to be printed
+ * @len: Length of data in bytes
+ * Returns: Number of bytes written
+ */
+int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
+			       size_t len)
+{
+	return _wpa_snprintf_hex(buf, buf_size, data, len, 1);
+}
+
+
+#ifdef CONFIG_ANSI_C_EXTRA
+
+#ifdef _WIN32_WCE
+void perror(const char *s)
+{
+	wpa_printf(MSG_ERROR, "%s: GetLastError: %d",
+		   s, (int) GetLastError());
+}
+#endif /* _WIN32_WCE */
+
+
+int optind = 1;
+int optopt;
+char *optarg;
+
+int getopt(int argc, char *const argv[], const char *optstring)
+{
+	static int optchr = 1;
+	char *cp;
+
+	if (optchr == 1) {
+		if (optind >= argc) {
+			/* all arguments processed */
+			return EOF;
+		}
+
+		if (argv[optind][0] != '-' || argv[optind][1] == '\0') {
+			/* no option characters */
+			return EOF;
+		}
+	}
+
+	if (os_strcmp(argv[optind], "--") == 0) {
+		/* no more options */
+		optind++;
+		return EOF;
+	}
+
+	optopt = argv[optind][optchr];
+	cp = os_strchr(optstring, optopt);
+	if (cp == NULL || optopt == ':') {
+		if (argv[optind][++optchr] == '\0') {
+			optchr = 1;
+			optind++;
+		}
+		return '?';
+	}
+
+	if (cp[1] == ':') {
+		/* Argument required */
+		optchr = 1;
+		if (argv[optind][optchr + 1]) {
+			/* No space between option and argument */
+			optarg = &argv[optind++][optchr + 1];
+		} else if (++optind >= argc) {
+			/* option requires an argument */
+			return '?';
+		} else {
+			/* Argument in the next argv */
+			optarg = argv[optind++];
+		}
+	} else {
+		/* No argument */
+		if (argv[optind][++optchr] == '\0') {
+			optchr = 1;
+			optind++;
+		}
+		optarg = NULL;
+	}
+	return *cp;
+}
+#endif /* CONFIG_ANSI_C_EXTRA */
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+/**
+ * wpa_unicode2ascii_inplace - Convert unicode string into ASCII
+ * @str: Pointer to string to convert
+ *
+ * This function converts a unicode string to ASCII using the same
+ * buffer for output. If UNICODE is not set, the buffer is not
+ * modified.
+ */
+void wpa_unicode2ascii_inplace(TCHAR *str)
+{
+#ifdef UNICODE
+	char *dst = (char *) str;
+	while (*str)
+		*dst++ = (char) *str++;
+	*dst = '\0';
+#endif /* UNICODE */
+}
+
+
+TCHAR * wpa_strdup_tchar(const char *str)
+{
+#ifdef UNICODE
+	TCHAR *buf;
+	buf = os_malloc((strlen(str) + 1) * sizeof(TCHAR));
+	if (buf == NULL)
+		return NULL;
+	wsprintf(buf, L"%S", str);
+	return buf;
+#else /* UNICODE */
+	return os_strdup(str);
+#endif /* UNICODE */
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len)
+{
+	char *end = txt + maxlen;
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (txt + 4 >= end)
+			break;
+
+		switch (data[i]) {
+		case '\"':
+			*txt++ = '\\';
+			*txt++ = '\"';
+			break;
+		case '\\':
+			*txt++ = '\\';
+			*txt++ = '\\';
+			break;
+		case '\033':
+			*txt++ = '\\';
+			*txt++ = 'e';
+			break;
+		case '\n':
+			*txt++ = '\\';
+			*txt++ = 'n';
+			break;
+		case '\r':
+			*txt++ = '\\';
+			*txt++ = 'r';
+			break;
+		case '\t':
+			*txt++ = '\\';
+			*txt++ = 't';
+			break;
+		default:
+			if (data[i] >= 32 && data[i] <= 127) {
+				*txt++ = data[i];
+			} else {
+				txt += os_snprintf(txt, end - txt, "\\x%02x",
+						   data[i]);
+			}
+			break;
+		}
+	}
+
+	*txt = '\0';
+}
+
+
+size_t printf_decode(u8 *buf, size_t maxlen, const char *str)
+{
+	const char *pos = str;
+	size_t len = 0;
+	int val;
+
+	while (*pos) {
+		if (len + 1 >= maxlen)
+			break;
+		switch (*pos) {
+		case '\\':
+			pos++;
+			switch (*pos) {
+			case '\\':
+				buf[len++] = '\\';
+				pos++;
+				break;
+			case '"':
+				buf[len++] = '"';
+				pos++;
+				break;
+			case 'n':
+				buf[len++] = '\n';
+				pos++;
+				break;
+			case 'r':
+				buf[len++] = '\r';
+				pos++;
+				break;
+			case 't':
+				buf[len++] = '\t';
+				pos++;
+				break;
+			case 'e':
+				buf[len++] = '\033';
+				pos++;
+				break;
+			case 'x':
+				pos++;
+				val = hex2byte(pos);
+				if (val < 0) {
+					val = hex2num(*pos);
+					if (val < 0)
+						break;
+					buf[len++] = val;
+					pos++;
+				} else {
+					buf[len++] = val;
+					pos += 2;
+				}
+				break;
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+				val = *pos++ - '0';
+				if (*pos >= '0' && *pos <= '7')
+					val = val * 8 + (*pos++ - '0');
+				if (*pos >= '0' && *pos <= '7')
+					val = val * 8 + (*pos++ - '0');
+				buf[len++] = val;
+				break;
+			default:
+				break;
+			}
+			break;
+		default:
+			buf[len++] = *pos++;
+			break;
+		}
+	}
+	if (maxlen > len)
+		buf[len] = '\0';
+
+	return len;
+}
+
+
+/**
+ * wpa_ssid_txt - Convert SSID to a printable string
+ * @ssid: SSID (32-octet string)
+ * @ssid_len: Length of ssid in octets
+ * Returns: Pointer to a printable string
+ *
+ * This function can be used to convert SSIDs into printable form. In most
+ * cases, SSIDs do not use unprintable characters, but IEEE 802.11 standard
+ * does not limit the used character set, so anything could be used in an SSID.
+ *
+ * This function uses a static buffer, so only one call can be used at the
+ * time, i.e., this is not re-entrant and the returned buffer must be used
+ * before calling this again.
+ */
+const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len)
+{
+	static char ssid_txt[SSID_MAX_LEN * 4 + 1];
+
+	if (ssid == NULL) {
+		ssid_txt[0] = '\0';
+		return ssid_txt;
+	}
+
+	printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len);
+	return ssid_txt;
+}
+
+
+void * __hide_aliasing_typecast(void *foo)
+{
+	return foo;
+}
+
+
+char * wpa_config_parse_string(const char *value, size_t *len)
+{
+	if (*value == '"') {
+		const char *pos;
+		char *str;
+		value++;
+		pos = os_strrchr(value, '"');
+		if (pos == NULL || pos[1] != '\0')
+			return NULL;
+		*len = pos - value;
+		str = dup_binstr(value, *len);
+		if (str == NULL)
+			return NULL;
+		return str;
+	} else if (*value == 'P' && value[1] == '"') {
+		const char *pos;
+		char *tstr, *str;
+		size_t tlen;
+		value += 2;
+		pos = os_strrchr(value, '"');
+		if (pos == NULL || pos[1] != '\0')
+			return NULL;
+		tlen = pos - value;
+		tstr = dup_binstr(value, tlen);
+		if (tstr == NULL)
+			return NULL;
+
+		str = os_malloc(tlen + 1);
+		if (str == NULL) {
+			os_free(tstr);
+			return NULL;
+		}
+
+		*len = printf_decode((u8 *) str, tlen + 1, tstr);
+		os_free(tstr);
+
+		return str;
+	} else {
+		u8 *str;
+		size_t tlen, hlen = os_strlen(value);
+		if (hlen & 1)
+			return NULL;
+		tlen = hlen / 2;
+		str = os_malloc(tlen + 1);
+		if (str == NULL)
+			return NULL;
+		if (hexstr2bin(value, str, tlen)) {
+			os_free(str);
+			return NULL;
+		}
+		str[tlen] = '\0';
+		*len = tlen;
+		return (char *) str;
+	}
+}
+
+
+int is_hex(const u8 *data, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (data[i] < 32 || data[i] >= 127)
+			return 1;
+	}
+	return 0;
+}
+
+
+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)
+{
+	size_t len = 0;
+
+	os_memset(res, 0, res_len);
+
+	if (src1) {
+		if (src1_len >= res_len) {
+			os_memcpy(res, src1, res_len);
+			return res_len;
+		}
+
+		os_memcpy(res, src1, src1_len);
+		len += src1_len;
+	}
+
+	if (src2) {
+		if (len + src2_len >= res_len) {
+			os_memcpy(res + len, src2, res_len - len);
+			return res_len;
+		}
+
+		os_memcpy(res + len, src2, src2_len);
+		len += src2_len;
+	}
+
+	return len;
+}
+
+
+char * dup_binstr(const void *src, size_t len)
+{
+	char *res;
+
+	if (src == NULL)
+		return NULL;
+	res = os_malloc(len + 1);
+	if (res == NULL)
+		return NULL;
+	os_memcpy(res, src, len);
+	res[len] = '\0';
+
+	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 (os_snprintf_error(end - pos, res)) {
+			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);
+	}
+}
+
+
+int random_mac_addr(u8 *addr)
+{
+	if (os_get_random(addr, ETH_ALEN) < 0)
+		return -1;
+	addr[0] &= 0xfe; /* unicast */
+	addr[0] |= 0x02; /* locally administered */
+	return 0;
+}
+
+
+int random_mac_addr_keep_oui(u8 *addr)
+{
+	if (os_get_random(addr + 3, 3) < 0)
+		return -1;
+	addr[0] &= 0xfe; /* unicast */
+	addr[0] |= 0x02; /* locally administered */
+	return 0;
+}
+
+
+/**
+ * cstr_token - Get next token from const char string
+ * @str: a constant string to tokenize
+ * @delim: a string of delimiters
+ * @last: a pointer to a character following the returned token
+ *      It has to be set to NULL for the first call and passed for any
+ *      futher call.
+ * Returns: a pointer to token position in str or NULL
+ *
+ * This function is similar to str_token, but it can be used with both
+ * char and const char strings. Differences:
+ * - The str buffer remains unmodified
+ * - The returned token is not a NULL terminated string, but a token
+ *   position in str buffer. If a return value is not NULL a size
+ *   of the returned token could be calculated as (last - token).
+ */
+const char * cstr_token(const char *str, const char *delim, const char **last)
+{
+	const char *end, *token = str;
+
+	if (!str || !delim || !last)
+		return NULL;
+
+	if (*last)
+		token = *last;
+
+	while (*token && os_strchr(delim, *token))
+		token++;
+
+	if (!*token)
+		return NULL;
+
+	end = token + 1;
+
+	while (*end && !os_strchr(delim, *end))
+		end++;
+
+	*last = end;
+	return token;
+}
+
+
+/**
+ * str_token - Get next token from a string
+ * @buf: String to tokenize. Note that the string might be modified.
+ * @delim: String of delimiters
+ * @context: Pointer to save our context. Should be initialized with
+ *	NULL on the first call, and passed for any further call.
+ * Returns: The next token, NULL if there are no more valid tokens.
+ */
+char * str_token(char *str, const char *delim, char **context)
+{
+	char *token = (char *) cstr_token(str, delim, (const char **) context);
+
+	if (token && **context)
+		*(*context)++ = '\0';
+
+	return token;
+}
+
+
+size_t utf8_unescape(const char *inp, size_t in_size,
+		     char *outp, size_t out_size)
+{
+	size_t res_size = 0;
+
+	if (!inp || !outp)
+		return 0;
+
+	if (!in_size)
+		in_size = os_strlen(inp);
+
+	/* Advance past leading single quote */
+	if (*inp == '\'' && in_size) {
+		inp++;
+		in_size--;
+	}
+
+	while (in_size--) {
+		if (res_size >= out_size)
+			return 0;
+
+		switch (*inp) {
+		case '\'':
+			/* Terminate on bare single quote */
+			*outp = '\0';
+			return res_size;
+
+		case '\\':
+			if (!in_size--)
+				return 0;
+			inp++;
+			/* fall through */
+
+		default:
+			*outp++ = *inp++;
+			res_size++;
+		}
+	}
+
+	/* NUL terminate if space allows */
+	if (res_size < out_size)
+		*outp = '\0';
+
+	return res_size;
+}
+
+
+size_t utf8_escape(const char *inp, size_t in_size,
+		   char *outp, size_t out_size)
+{
+	size_t res_size = 0;
+
+	if (!inp || !outp)
+		return 0;
+
+	/* inp may or may not be NUL terminated, but must be if 0 size
+	 * is specified */
+	if (!in_size)
+		in_size = os_strlen(inp);
+
+	while (in_size--) {
+		if (res_size++ >= out_size)
+			return 0;
+
+		switch (*inp) {
+		case '\\':
+		case '\'':
+			if (res_size++ >= out_size)
+				return 0;
+			*outp++ = '\\';
+			/* fall through */
+
+		default:
+			*outp++ = *inp++;
+			break;
+		}
+	}
+
+	/* NUL terminate if space allows */
+	if (res_size < out_size)
+		*outp = '\0';
+
+	return res_size;
+}
+
+
+int is_ctrl_char(char c)
+{
+	return c > 0 && c < 32;
+}
diff --git a/hostap/src/utils/common.h b/hostap/src/utils/common.h
new file mode 100644
index 0000000..0b9cc3d
--- /dev/null
+++ b/hostap/src/utils/common.h
@@ -0,0 +1,559 @@
+/*
+ * wpa_supplicant/hostapd / common helper functions, etc.
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "os.h"
+
+#if defined(__linux__) || defined(__GLIBC__)
+#include <endian.h>
+#include <byteswap.h>
+#endif /* __linux__ */
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \
+    defined(__OpenBSD__)
+#include <sys/types.h>
+#include <sys/endian.h>
+#define __BYTE_ORDER	_BYTE_ORDER
+#define	__LITTLE_ENDIAN	_LITTLE_ENDIAN
+#define	__BIG_ENDIAN	_BIG_ENDIAN
+#ifdef __OpenBSD__
+#define bswap_16 swap16
+#define bswap_32 swap32
+#define bswap_64 swap64
+#else /* __OpenBSD__ */
+#define bswap_16 bswap16
+#define bswap_32 bswap32
+#define bswap_64 bswap64
+#endif /* __OpenBSD__ */
+#endif /* defined(__FreeBSD__) || defined(__NetBSD__) ||
+	* defined(__DragonFly__) || defined(__OpenBSD__) */
+
+#ifdef __APPLE__
+#include <sys/types.h>
+#include <machine/endian.h>
+#define __BYTE_ORDER	_BYTE_ORDER
+#define __LITTLE_ENDIAN	_LITTLE_ENDIAN
+#define __BIG_ENDIAN	_BIG_ENDIAN
+static inline unsigned short bswap_16(unsigned short v)
+{
+	return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int bswap_32(unsigned int v)
+{
+	return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+		((v & 0xff0000) >> 8) | (v >> 24);
+}
+#endif /* __APPLE__ */
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <winsock.h>
+
+typedef int socklen_t;
+
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0 /* not supported */
+#endif
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#ifdef _MSC_VER
+#define inline __inline
+
+#undef vsnprintf
+#define vsnprintf _vsnprintf
+#undef close
+#define close closesocket
+#endif /* _MSC_VER */
+
+
+/* Define platform specific integer types */
+
+#ifdef _MSC_VER
+typedef UINT64 u64;
+typedef UINT32 u32;
+typedef UINT16 u16;
+typedef UINT8 u8;
+typedef INT64 s64;
+typedef INT32 s32;
+typedef INT16 s16;
+typedef INT8 s8;
+#define WPA_TYPES_DEFINED
+#endif /* _MSC_VER */
+
+#ifdef __vxworks
+typedef unsigned long long u64;
+typedef UINT32 u32;
+typedef UINT16 u16;
+typedef UINT8 u8;
+typedef long long s64;
+typedef INT32 s32;
+typedef INT16 s16;
+typedef INT8 s8;
+#define WPA_TYPES_DEFINED
+#endif /* __vxworks */
+
+#ifndef WPA_TYPES_DEFINED
+#ifdef CONFIG_USE_INTTYPES_H
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef int64_t s64;
+typedef int32_t s32;
+typedef int16_t s16;
+typedef int8_t s8;
+#define WPA_TYPES_DEFINED
+#endif /* !WPA_TYPES_DEFINED */
+
+
+/* Define platform specific byte swapping macros */
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+
+static inline unsigned short wpa_swap_16(unsigned short v)
+{
+	return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int wpa_swap_32(unsigned int v)
+{
+	return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+		((v & 0xff0000) >> 8) | (v >> 24);
+}
+
+#define le_to_host16(n) (n)
+#define host_to_le16(n) (n)
+#define be_to_host16(n) wpa_swap_16(n)
+#define host_to_be16(n) wpa_swap_16(n)
+#define le_to_host32(n) (n)
+#define host_to_le32(n) (n)
+#define be_to_host32(n) wpa_swap_32(n)
+#define host_to_be32(n) wpa_swap_32(n)
+
+#define WPA_BYTE_SWAP_DEFINED
+
+#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */
+
+
+#ifndef WPA_BYTE_SWAP_DEFINED
+
+#ifndef __BYTE_ORDER
+#ifndef __LITTLE_ENDIAN
+#ifndef __BIG_ENDIAN
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#if defined(sparc)
+#define __BYTE_ORDER __BIG_ENDIAN
+#endif
+#endif /* __BIG_ENDIAN */
+#endif /* __LITTLE_ENDIAN */
+#endif /* __BYTE_ORDER */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define le_to_host16(n) ((__force u16) (le16) (n))
+#define host_to_le16(n) ((__force le16) (u16) (n))
+#define be_to_host16(n) bswap_16((__force u16) (be16) (n))
+#define host_to_be16(n) ((__force be16) bswap_16((n)))
+#define le_to_host32(n) ((__force u32) (le32) (n))
+#define host_to_le32(n) ((__force le32) (u32) (n))
+#define be_to_host32(n) bswap_32((__force u32) (be32) (n))
+#define host_to_be32(n) ((__force be32) bswap_32((n)))
+#define le_to_host64(n) ((__force u64) (le64) (n))
+#define host_to_le64(n) ((__force le64) (u64) (n))
+#define be_to_host64(n) bswap_64((__force u64) (be64) (n))
+#define host_to_be64(n) ((__force be64) bswap_64((n)))
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define le_to_host16(n) bswap_16(n)
+#define host_to_le16(n) bswap_16(n)
+#define be_to_host16(n) (n)
+#define host_to_be16(n) (n)
+#define le_to_host32(n) bswap_32(n)
+#define host_to_le32(n) bswap_32(n)
+#define be_to_host32(n) (n)
+#define host_to_be32(n) (n)
+#define le_to_host64(n) bswap_64(n)
+#define host_to_le64(n) bswap_64(n)
+#define be_to_host64(n) (n)
+#define host_to_be64(n) (n)
+#ifndef WORDS_BIGENDIAN
+#define WORDS_BIGENDIAN
+#endif
+#else
+#error Could not determine CPU byte order
+#endif
+
+#define WPA_BYTE_SWAP_DEFINED
+#endif /* !WPA_BYTE_SWAP_DEFINED */
+
+
+/* Macros for handling unaligned memory accesses */
+
+static inline u16 WPA_GET_BE16(const u8 *a)
+{
+	return (a[0] << 8) | a[1];
+}
+
+static inline void WPA_PUT_BE16(u8 *a, u16 val)
+{
+	a[0] = val >> 8;
+	a[1] = val & 0xff;
+}
+
+static inline u16 WPA_GET_LE16(const u8 *a)
+{
+	return (a[1] << 8) | a[0];
+}
+
+static inline void WPA_PUT_LE16(u8 *a, u16 val)
+{
+	a[1] = val >> 8;
+	a[0] = val & 0xff;
+}
+
+static inline u32 WPA_GET_BE24(const u8 *a)
+{
+	return (a[0] << 16) | (a[1] << 8) | a[2];
+}
+
+static inline void WPA_PUT_BE24(u8 *a, u32 val)
+{
+	a[0] = (val >> 16) & 0xff;
+	a[1] = (val >> 8) & 0xff;
+	a[2] = val & 0xff;
+}
+
+static inline u32 WPA_GET_BE32(const u8 *a)
+{
+	return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
+}
+
+static inline void WPA_PUT_BE32(u8 *a, u32 val)
+{
+	a[0] = (val >> 24) & 0xff;
+	a[1] = (val >> 16) & 0xff;
+	a[2] = (val >> 8) & 0xff;
+	a[3] = val & 0xff;
+}
+
+static inline u32 WPA_GET_LE32(const u8 *a)
+{
+	return ((u32) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
+}
+
+static inline void WPA_PUT_LE32(u8 *a, u32 val)
+{
+	a[3] = (val >> 24) & 0xff;
+	a[2] = (val >> 16) & 0xff;
+	a[1] = (val >> 8) & 0xff;
+	a[0] = val & 0xff;
+}
+
+static inline u64 WPA_GET_BE64(const u8 *a)
+{
+	return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) |
+		(((u64) a[2]) << 40) | (((u64) a[3]) << 32) |
+		(((u64) a[4]) << 24) | (((u64) a[5]) << 16) |
+		(((u64) a[6]) << 8) | ((u64) a[7]);
+}
+
+static inline void WPA_PUT_BE64(u8 *a, u64 val)
+{
+	a[0] = val >> 56;
+	a[1] = val >> 48;
+	a[2] = val >> 40;
+	a[3] = val >> 32;
+	a[4] = val >> 24;
+	a[5] = val >> 16;
+	a[6] = val >> 8;
+	a[7] = val & 0xff;
+}
+
+static inline u64 WPA_GET_LE64(const u8 *a)
+{
+	return (((u64) a[7]) << 56) | (((u64) a[6]) << 48) |
+		(((u64) a[5]) << 40) | (((u64) a[4]) << 32) |
+		(((u64) a[3]) << 24) | (((u64) a[2]) << 16) |
+		(((u64) a[1]) << 8) | ((u64) a[0]);
+}
+
+static inline void WPA_PUT_LE64(u8 *a, u64 val)
+{
+	a[7] = val >> 56;
+	a[6] = val >> 48;
+	a[5] = val >> 40;
+	a[4] = val >> 32;
+	a[3] = val >> 24;
+	a[2] = val >> 16;
+	a[1] = val >> 8;
+	a[0] = val & 0xff;
+}
+
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#ifndef ETH_HLEN
+#define ETH_HLEN 14
+#endif
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+#ifndef ETH_P_ALL
+#define ETH_P_ALL 0x0003
+#endif
+#ifndef ETH_P_80211_ENCAP
+#define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */
+#endif
+#ifndef ETH_P_PAE
+#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
+#endif /* ETH_P_PAE */
+#ifndef ETH_P_EAPOL
+#define ETH_P_EAPOL ETH_P_PAE
+#endif /* ETH_P_EAPOL */
+#ifndef ETH_P_RSN_PREAUTH
+#define ETH_P_RSN_PREAUTH 0x88c7
+#endif /* ETH_P_RSN_PREAUTH */
+#ifndef ETH_P_RRB
+#define ETH_P_RRB 0x890D
+#endif /* ETH_P_RRB */
+
+
+#ifdef __GNUC__
+#define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b))))
+#define STRUCT_PACKED __attribute__ ((packed))
+#else
+#define PRINTF_FORMAT(a,b)
+#define STRUCT_PACKED
+#endif
+
+
+#ifdef CONFIG_ANSI_C_EXTRA
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+/* snprintf - used in number of places; sprintf() is _not_ a good replacement
+ * due to possible buffer overflow; see, e.g.,
+ * http://www.ijs.si/software/snprintf/ for portable implementation of
+ * snprintf. */
+int snprintf(char *str, size_t size, const char *format, ...);
+
+/* vsnprintf - only used for wpa_msg() in wpa_supplicant.c */
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+#endif /* !defined(_MSC_VER) || _MSC_VER < 1400 */
+
+/* getopt - only used in main.c */
+int getopt(int argc, char *const argv[], const char *optstring);
+extern char *optarg;
+extern int optind;
+
+#ifndef CONFIG_NO_SOCKLEN_T_TYPEDEF
+#ifndef __socklen_t_defined
+typedef int socklen_t;
+#endif
+#endif
+
+/* inline - define as __inline or just define it to be empty, if needed */
+#ifdef CONFIG_NO_INLINE
+#define inline
+#else
+#define inline __inline
+#endif
+
+#ifndef __func__
+#define __func__ "__func__ not defined"
+#endif
+
+#ifndef bswap_16
+#define bswap_16(a) ((((u16) (a) << 8) & 0xff00) | (((u16) (a) >> 8) & 0xff))
+#endif
+
+#ifndef bswap_32
+#define bswap_32(a) ((((u32) (a) << 24) & 0xff000000) | \
+		     (((u32) (a) << 8) & 0xff0000) | \
+     		     (((u32) (a) >> 8) & 0xff00) | \
+     		     (((u32) (a) >> 24) & 0xff))
+#endif
+
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0
+#endif
+
+#ifdef _WIN32_WCE
+void perror(const char *s);
+#endif /* _WIN32_WCE */
+
+#endif /* CONFIG_ANSI_C_EXTRA */
+
+#ifndef MAC2STR
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+/*
+ * Compact form for string representation of MAC address
+ * To be used, e.g., for constructing dbus paths for P2P Devices
+ */
+#define COMPACT_MACSTR "%02x%02x%02x%02x%02x%02x"
+#endif
+
+#ifndef BIT
+#define BIT(x) (1U << (x))
+#endif
+
+/*
+ * Definitions for sparse validation
+ * (http://kernel.org/pub/linux/kernel/people/josh/sparse/)
+ */
+#ifdef __CHECKER__
+#define __force __attribute__((force))
+#define __bitwise __attribute__((bitwise))
+#else
+#define __force
+#define __bitwise
+#endif
+
+typedef u16 __bitwise be16;
+typedef u16 __bitwise le16;
+typedef u32 __bitwise be32;
+typedef u32 __bitwise le32;
+typedef u64 __bitwise be64;
+typedef u64 __bitwise le64;
+
+#ifndef __must_check
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define __must_check __attribute__((__warn_unused_result__))
+#else
+#define __must_check
+#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_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable);
+int hwaddr_compact_aton(const char *txt, u8 *addr);
+int hwaddr_aton2(const char *txt, u8 *addr);
+int hex2byte(const char *hex);
+int hexstr2bin(const char *hex, u8 *buf, size_t len);
+void inc_byte_array(u8 *counter, size_t len);
+void wpa_get_ntp_timestamp(u8 *buf);
+int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...);
+int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
+			 char sep);
+int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len);
+int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
+			       size_t len);
+
+int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask);
+
+#ifdef CONFIG_NATIVE_WINDOWS
+void wpa_unicode2ascii_inplace(TCHAR *str);
+TCHAR * wpa_strdup_tchar(const char *str);
+#else /* CONFIG_NATIVE_WINDOWS */
+#define wpa_unicode2ascii_inplace(s) do { } while (0)
+#define wpa_strdup_tchar(s) strdup((s))
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len);
+size_t printf_decode(u8 *buf, size_t maxlen, const char *str);
+
+const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len);
+
+char * wpa_config_parse_string(const char *value, size_t *len);
+int is_hex(const u8 *data, size_t len);
+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);
+char * dup_binstr(const void *src, size_t len);
+
+static inline int is_zero_ether_addr(const u8 *a)
+{
+	return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
+}
+
+static inline int is_broadcast_ether_addr(const u8 *a)
+{
+	return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff;
+}
+
+static inline int is_multicast_ether_addr(const u8 *a)
+{
+	return a[0] & 0x01;
+}
+
+#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff"
+
+#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);
+
+int random_mac_addr(u8 *addr);
+int random_mac_addr_keep_oui(u8 *addr);
+
+const char * cstr_token(const char *str, const char *delim, const char **last);
+char * str_token(char *str, const char *delim, char **context);
+size_t utf8_escape(const char *inp, size_t in_size,
+		   char *outp, size_t out_size);
+size_t utf8_unescape(const char *inp, size_t in_size,
+		     char *outp, size_t out_size);
+int is_ctrl_char(char c);
+
+
+/*
+ * 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
+ * cannot be easily avoided with union-based type-punning due to struct
+ * definitions including another struct in system header files. To avoid having
+ * to fully disable strict-aliasing warnings, provide a mechanism to hide the
+ * typecast from aliasing for now. A cleaner solution will hopefully be found
+ * in the future to handle these cases.
+ */
+void * __hide_aliasing_typecast(void *foo);
+#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a))
+
+#ifdef CONFIG_VALGRIND
+#include <valgrind/memcheck.h>
+#define WPA_MEM_DEFINED(ptr, len) VALGRIND_MAKE_MEM_DEFINED((ptr), (len))
+#else /* CONFIG_VALGRIND */
+#define WPA_MEM_DEFINED(ptr, len) do { } while (0)
+#endif /* CONFIG_VALGRIND */
+
+#endif /* COMMON_H */
diff --git a/hostap/src/utils/edit.c b/hostap/src/utils/edit.c
new file mode 100644
index 0000000..d340bfa
--- /dev/null
+++ b/hostap/src/utils/edit.c
@@ -0,0 +1,1174 @@
+/*
+ * Command line editing and history
+ * Copyright (c) 2010-2011, 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 <termios.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "list.h"
+#include "edit.h"
+
+#define CMD_BUF_LEN 4096
+static char cmdbuf[CMD_BUF_LEN];
+static int cmdbuf_pos = 0;
+static int cmdbuf_len = 0;
+static char currbuf[CMD_BUF_LEN];
+static int currbuf_valid = 0;
+static const char *ps2 = NULL;
+
+#define HISTORY_MAX 100
+
+struct edit_history {
+	struct dl_list list;
+	char str[1];
+};
+
+static struct dl_list history_list;
+static struct edit_history *history_curr;
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
+	NULL;
+
+static struct termios prevt, newt;
+
+
+#define CLEAR_END_LINE "\e[K"
+
+
+void edit_clear_line(void)
+{
+	int i;
+	putchar('\r');
+	for (i = 0; i < cmdbuf_len + 2 + (ps2 ? (int) os_strlen(ps2) : 0); i++)
+		putchar(' ');
+}
+
+
+static void move_start(void)
+{
+	cmdbuf_pos = 0;
+	edit_redraw();
+}
+
+
+static void move_end(void)
+{
+	cmdbuf_pos = cmdbuf_len;
+	edit_redraw();
+}
+
+
+static void move_left(void)
+{
+	if (cmdbuf_pos > 0) {
+		cmdbuf_pos--;
+		edit_redraw();
+	}
+}
+
+
+static void move_right(void)
+{
+	if (cmdbuf_pos < cmdbuf_len) {
+		cmdbuf_pos++;
+		edit_redraw();
+	}
+}
+
+
+static void move_word_left(void)
+{
+	while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ')
+		cmdbuf_pos--;
+	while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ')
+		cmdbuf_pos--;
+	edit_redraw();
+}
+
+
+static void move_word_right(void)
+{
+	while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ')
+		cmdbuf_pos++;
+	while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ')
+		cmdbuf_pos++;
+	edit_redraw();
+}
+
+
+static void delete_left(void)
+{
+	if (cmdbuf_pos == 0)
+		return;
+
+	edit_clear_line();
+	os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos,
+		   cmdbuf_len - cmdbuf_pos);
+	cmdbuf_pos--;
+	cmdbuf_len--;
+	edit_redraw();
+}
+
+
+static void delete_current(void)
+{
+	if (cmdbuf_pos == cmdbuf_len)
+		return;
+
+	edit_clear_line();
+	os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1,
+		   cmdbuf_len - cmdbuf_pos);
+	cmdbuf_len--;
+	edit_redraw();
+}
+
+
+static void delete_word(void)
+{
+	int pos;
+
+	edit_clear_line();
+	pos = cmdbuf_pos;
+	while (pos > 0 && cmdbuf[pos - 1] == ' ')
+		pos--;
+	while (pos > 0 && cmdbuf[pos - 1] != ' ')
+		pos--;
+	os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
+	cmdbuf_len -= cmdbuf_pos - pos;
+	cmdbuf_pos = pos;
+	edit_redraw();
+}
+
+
+static void clear_left(void)
+{
+	if (cmdbuf_pos == 0)
+		return;
+
+	edit_clear_line();
+	os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
+	cmdbuf_len -= cmdbuf_pos;
+	cmdbuf_pos = 0;
+	edit_redraw();
+}
+
+
+static void clear_right(void)
+{
+	if (cmdbuf_pos == cmdbuf_len)
+		return;
+
+	edit_clear_line();
+	cmdbuf_len = cmdbuf_pos;
+	edit_redraw();
+}
+
+
+static void history_add(const char *str)
+{
+	struct edit_history *h, *match = NULL, *last = NULL;
+	size_t len, count = 0;
+
+	if (str[0] == '\0')
+		return;
+
+	dl_list_for_each(h, &history_list, struct edit_history, list) {
+		if (os_strcmp(str, h->str) == 0) {
+			match = h;
+			break;
+		}
+		last = h;
+		count++;
+	}
+
+	if (match) {
+		dl_list_del(&h->list);
+		dl_list_add(&history_list, &h->list);
+		history_curr = h;
+		return;
+	}
+
+	if (count >= HISTORY_MAX && last) {
+		dl_list_del(&last->list);
+		os_free(last);
+	}
+
+	len = os_strlen(str);
+	h = os_zalloc(sizeof(*h) + len);
+	if (h == NULL)
+		return;
+	dl_list_add(&history_list, &h->list);
+	os_strlcpy(h->str, str, len + 1);
+	history_curr = h;
+}
+
+
+static void history_use(void)
+{
+	edit_clear_line();
+	cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str);
+	os_memcpy(cmdbuf, history_curr->str, cmdbuf_len);
+	edit_redraw();
+}
+
+
+static void history_prev(void)
+{
+	if (history_curr == NULL)
+		return;
+
+	if (history_curr ==
+	    dl_list_first(&history_list, struct edit_history, list)) {
+		if (!currbuf_valid) {
+			cmdbuf[cmdbuf_len] = '\0';
+			os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1);
+			currbuf_valid = 1;
+			history_use();
+			return;
+		}
+	}
+
+	if (history_curr ==
+	    dl_list_last(&history_list, struct edit_history, list))
+		return;
+
+	history_curr = dl_list_entry(history_curr->list.next,
+				     struct edit_history, list);
+	history_use();
+}
+
+
+static void history_next(void)
+{
+	if (history_curr == NULL ||
+	    history_curr ==
+	    dl_list_first(&history_list, struct edit_history, list)) {
+		if (currbuf_valid) {
+			currbuf_valid = 0;
+			edit_clear_line();
+			cmdbuf_len = cmdbuf_pos = os_strlen(currbuf);
+			os_memcpy(cmdbuf, currbuf, cmdbuf_len);
+			edit_redraw();
+		}
+		return;
+	}
+
+	history_curr = dl_list_entry(history_curr->list.prev,
+				     struct edit_history, list);
+	history_use();
+}
+
+
+static void history_read(const char *fname)
+{
+	FILE *f;
+	char buf[CMD_BUF_LEN], *pos;
+
+	f = fopen(fname, "r");
+	if (f == NULL)
+		return;
+
+	while (fgets(buf, CMD_BUF_LEN, f)) {
+		for (pos = buf; *pos; pos++) {
+			if (*pos == '\r' || *pos == '\n') {
+				*pos = '\0';
+				break;
+			}
+		}
+		history_add(buf);
+	}
+
+	fclose(f);
+}
+
+
+static void history_write(const char *fname,
+			  int (*filter_cb)(void *ctx, const char *cmd))
+{
+	FILE *f;
+	struct edit_history *h;
+
+	f = fopen(fname, "w");
+	if (f == NULL)
+		return;
+
+	dl_list_for_each_reverse(h, &history_list, struct edit_history, list) {
+		if (filter_cb && filter_cb(edit_cb_ctx, h->str))
+			continue;
+		fprintf(f, "%s\n", h->str);
+	}
+
+	fclose(f);
+}
+
+
+static void history_debug_dump(void)
+{
+	struct edit_history *h;
+	edit_clear_line();
+	printf("\r");
+	dl_list_for_each_reverse(h, &history_list, struct edit_history, list)
+		printf("%s%s\n", h == history_curr ? "[C]" : "", h->str);
+	if (currbuf_valid)
+		printf("{%s}\n", currbuf);
+	edit_redraw();
+}
+
+
+static void insert_char(int c)
+{
+	if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1)
+		return;
+	if (cmdbuf_len == cmdbuf_pos) {
+		cmdbuf[cmdbuf_pos++] = c;
+		cmdbuf_len++;
+		putchar(c);
+		fflush(stdout);
+	} else {
+		os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos,
+			   cmdbuf_len - cmdbuf_pos);
+		cmdbuf[cmdbuf_pos++] = c;
+		cmdbuf_len++;
+		edit_redraw();
+	}
+}
+
+
+static void process_cmd(void)
+{
+	currbuf_valid = 0;
+	if (cmdbuf_len == 0) {
+		printf("\n%s> ", ps2 ? ps2 : "");
+		fflush(stdout);
+		return;
+	}
+	printf("\n");
+	cmdbuf[cmdbuf_len] = '\0';
+	history_add(cmdbuf);
+	cmdbuf_pos = 0;
+	cmdbuf_len = 0;
+	edit_cmd_cb(edit_cb_ctx, cmdbuf);
+	printf("%s> ", ps2 ? ps2 : "");
+	fflush(stdout);
+}
+
+
+static void free_completions(char **c)
+{
+	int i;
+	if (c == NULL)
+		return;
+	for (i = 0; c[i]; i++)
+		os_free(c[i]);
+	os_free(c);
+}
+
+
+static int filter_strings(char **c, char *str, size_t len)
+{
+	int i, j;
+
+	for (i = 0, j = 0; c[j]; j++) {
+		if (os_strncasecmp(c[j], str, len) == 0) {
+			if (i != j) {
+				c[i] = c[j];
+				c[j] = NULL;
+			}
+			i++;
+		} else {
+			os_free(c[j]);
+			c[j] = NULL;
+		}
+	}
+	c[i] = NULL;
+	return i;
+}
+
+
+static int common_len(const char *a, const char *b)
+{
+	int len = 0;
+	while (a[len] && a[len] == b[len])
+		len++;
+	return len;
+}
+
+
+static int max_common_length(char **c)
+{
+	int len, i;
+
+	len = os_strlen(c[0]);
+	for (i = 1; c[i]; i++) {
+		int same = common_len(c[0], c[i]);
+		if (same < len)
+			len = same;
+	}
+
+	return len;
+}
+
+
+static int cmp_str(const void *a, const void *b)
+{
+	return os_strcmp(* (const char **) a, * (const char **) b);
+}
+
+static void complete(int list)
+{
+	char **c;
+	int i, len, count;
+	int start, end;
+	int room, plen, add_space;
+
+	if (edit_completion_cb == NULL)
+		return;
+
+	cmdbuf[cmdbuf_len] = '\0';
+	c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos);
+	if (c == NULL)
+		return;
+
+	end = cmdbuf_pos;
+	start = end;
+	while (start > 0 && cmdbuf[start - 1] != ' ')
+		start--;
+	plen = end - start;
+
+	count = filter_strings(c, &cmdbuf[start], plen);
+	if (count == 0) {
+		free_completions(c);
+		return;
+	}
+
+	len = max_common_length(c);
+	if (len <= plen && count > 1) {
+		if (list) {
+			qsort(c, count, sizeof(char *), cmp_str);
+			edit_clear_line();
+			printf("\r");
+			for (i = 0; c[i]; i++)
+				printf("%s%s", i > 0 ? " " : "", c[i]);
+			printf("\n");
+			edit_redraw();
+		}
+		free_completions(c);
+		return;
+	}
+	len -= plen;
+
+	room = sizeof(cmdbuf) - 1 - cmdbuf_len;
+	if (room < len)
+		len = room;
+	add_space = count == 1 && len < room;
+
+	os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos,
+		   cmdbuf_len - cmdbuf_pos);
+	os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len);
+	if (add_space)
+		cmdbuf[cmdbuf_pos + len] = ' ';
+
+	cmdbuf_pos += len + add_space;
+	cmdbuf_len += len + add_space;
+
+	edit_redraw();
+
+	free_completions(c);
+}
+
+
+enum edit_key_code {
+	EDIT_KEY_NONE = 256,
+	EDIT_KEY_TAB,
+	EDIT_KEY_UP,
+	EDIT_KEY_DOWN,
+	EDIT_KEY_RIGHT,
+	EDIT_KEY_LEFT,
+	EDIT_KEY_ENTER,
+	EDIT_KEY_BACKSPACE,
+	EDIT_KEY_INSERT,
+	EDIT_KEY_DELETE,
+	EDIT_KEY_HOME,
+	EDIT_KEY_END,
+	EDIT_KEY_PAGE_UP,
+	EDIT_KEY_PAGE_DOWN,
+	EDIT_KEY_F1,
+	EDIT_KEY_F2,
+	EDIT_KEY_F3,
+	EDIT_KEY_F4,
+	EDIT_KEY_F5,
+	EDIT_KEY_F6,
+	EDIT_KEY_F7,
+	EDIT_KEY_F8,
+	EDIT_KEY_F9,
+	EDIT_KEY_F10,
+	EDIT_KEY_F11,
+	EDIT_KEY_F12,
+	EDIT_KEY_CTRL_UP,
+	EDIT_KEY_CTRL_DOWN,
+	EDIT_KEY_CTRL_RIGHT,
+	EDIT_KEY_CTRL_LEFT,
+	EDIT_KEY_CTRL_A,
+	EDIT_KEY_CTRL_B,
+	EDIT_KEY_CTRL_D,
+	EDIT_KEY_CTRL_E,
+	EDIT_KEY_CTRL_F,
+	EDIT_KEY_CTRL_G,
+	EDIT_KEY_CTRL_H,
+	EDIT_KEY_CTRL_J,
+	EDIT_KEY_CTRL_K,
+	EDIT_KEY_CTRL_L,
+	EDIT_KEY_CTRL_N,
+	EDIT_KEY_CTRL_O,
+	EDIT_KEY_CTRL_P,
+	EDIT_KEY_CTRL_R,
+	EDIT_KEY_CTRL_T,
+	EDIT_KEY_CTRL_U,
+	EDIT_KEY_CTRL_V,
+	EDIT_KEY_CTRL_W,
+	EDIT_KEY_ALT_UP,
+	EDIT_KEY_ALT_DOWN,
+	EDIT_KEY_ALT_RIGHT,
+	EDIT_KEY_ALT_LEFT,
+	EDIT_KEY_SHIFT_UP,
+	EDIT_KEY_SHIFT_DOWN,
+	EDIT_KEY_SHIFT_RIGHT,
+	EDIT_KEY_SHIFT_LEFT,
+	EDIT_KEY_ALT_SHIFT_UP,
+	EDIT_KEY_ALT_SHIFT_DOWN,
+	EDIT_KEY_ALT_SHIFT_RIGHT,
+	EDIT_KEY_ALT_SHIFT_LEFT,
+	EDIT_KEY_EOF
+};
+
+static void show_esc_buf(const char *esc_buf, char c, int i)
+{
+	edit_clear_line();
+	printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i);
+	edit_redraw();
+}
+
+
+static enum edit_key_code esc_seq_to_key1_no(char last)
+{
+	switch (last) {
+	case 'A':
+		return EDIT_KEY_UP;
+	case 'B':
+		return EDIT_KEY_DOWN;
+	case 'C':
+		return EDIT_KEY_RIGHT;
+	case 'D':
+		return EDIT_KEY_LEFT;
+	default:
+		return EDIT_KEY_NONE;
+	}
+}
+
+
+static enum edit_key_code esc_seq_to_key1_shift(char last)
+{
+	switch (last) {
+	case 'A':
+		return EDIT_KEY_SHIFT_UP;
+	case 'B':
+		return EDIT_KEY_SHIFT_DOWN;
+	case 'C':
+		return EDIT_KEY_SHIFT_RIGHT;
+	case 'D':
+		return EDIT_KEY_SHIFT_LEFT;
+	default:
+		return EDIT_KEY_NONE;
+	}
+}
+
+
+static enum edit_key_code esc_seq_to_key1_alt(char last)
+{
+	switch (last) {
+	case 'A':
+		return EDIT_KEY_ALT_UP;
+	case 'B':
+		return EDIT_KEY_ALT_DOWN;
+	case 'C':
+		return EDIT_KEY_ALT_RIGHT;
+	case 'D':
+		return EDIT_KEY_ALT_LEFT;
+	default:
+		return EDIT_KEY_NONE;
+	}
+}
+
+
+static enum edit_key_code esc_seq_to_key1_alt_shift(char last)
+{
+	switch (last) {
+	case 'A':
+		return EDIT_KEY_ALT_SHIFT_UP;
+	case 'B':
+		return EDIT_KEY_ALT_SHIFT_DOWN;
+	case 'C':
+		return EDIT_KEY_ALT_SHIFT_RIGHT;
+	case 'D':
+		return EDIT_KEY_ALT_SHIFT_LEFT;
+	default:
+		return EDIT_KEY_NONE;
+	}
+}
+
+
+static enum edit_key_code esc_seq_to_key1_ctrl(char last)
+{
+	switch (last) {
+	case 'A':
+		return EDIT_KEY_CTRL_UP;
+	case 'B':
+		return EDIT_KEY_CTRL_DOWN;
+	case 'C':
+		return EDIT_KEY_CTRL_RIGHT;
+	case 'D':
+		return EDIT_KEY_CTRL_LEFT;
+	default:
+		return EDIT_KEY_NONE;
+	}
+}
+
+
+static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last)
+{
+	/* ESC-[<param1>;<param2><last> */
+
+	if (param1 < 0 && param2 < 0)
+		return esc_seq_to_key1_no(last);
+
+	if (param1 == 1 && param2 == 2)
+		return esc_seq_to_key1_shift(last);
+
+	if (param1 == 1 && param2 == 3)
+		return esc_seq_to_key1_alt(last);
+
+	if (param1 == 1 && param2 == 4)
+		return esc_seq_to_key1_alt_shift(last);
+
+	if (param1 == 1 && param2 == 5)
+		return esc_seq_to_key1_ctrl(last);
+
+	if (param2 < 0) {
+		if (last != '~')
+			return EDIT_KEY_NONE;
+		switch (param1) {
+		case 2:
+			return EDIT_KEY_INSERT;
+		case 3:
+			return EDIT_KEY_DELETE;
+		case 5:
+			return EDIT_KEY_PAGE_UP;
+		case 6:
+			return EDIT_KEY_PAGE_DOWN;
+		case 15:
+			return EDIT_KEY_F5;
+		case 17:
+			return EDIT_KEY_F6;
+		case 18:
+			return EDIT_KEY_F7;
+		case 19:
+			return EDIT_KEY_F8;
+		case 20:
+			return EDIT_KEY_F9;
+		case 21:
+			return EDIT_KEY_F10;
+		case 23:
+			return EDIT_KEY_F11;
+		case 24:
+			return EDIT_KEY_F12;
+		}
+	}
+
+	return EDIT_KEY_NONE;
+}
+
+
+static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last)
+{
+	/* ESC-O<param1>;<param2><last> */
+
+	if (param1 >= 0 || param2 >= 0)
+		return EDIT_KEY_NONE;
+
+	switch (last) {
+	case 'F':
+		return EDIT_KEY_END;
+	case 'H':
+		return EDIT_KEY_HOME;
+	case 'P':
+		return EDIT_KEY_F1;
+	case 'Q':
+		return EDIT_KEY_F2;
+	case 'R':
+		return EDIT_KEY_F3;
+	case 'S':
+		return EDIT_KEY_F4;
+	default:
+		return EDIT_KEY_NONE;
+	}
+}
+
+
+static enum edit_key_code esc_seq_to_key(char *seq)
+{
+	char last, *pos;
+	int param1 = -1, param2 = -1;
+	enum edit_key_code ret = EDIT_KEY_NONE;
+
+	last = '\0';
+	for (pos = seq; *pos; pos++)
+		last = *pos;
+
+	if (seq[1] >= '0' && seq[1] <= '9') {
+		param1 = atoi(&seq[1]);
+		pos = os_strchr(seq, ';');
+		if (pos)
+			param2 = atoi(pos + 1);
+	}
+
+	if (seq[0] == '[')
+		ret = esc_seq_to_key1(param1, param2, last);
+	else if (seq[0] == 'O')
+		ret = esc_seq_to_key2(param1, param2, last);
+
+	if (ret != EDIT_KEY_NONE)
+		return ret;
+
+	edit_clear_line();
+	printf("\rUnknown escape sequence '%s'\n", seq);
+	edit_redraw();
+	return EDIT_KEY_NONE;
+}
+
+
+static enum edit_key_code edit_read_key(int sock)
+{
+	int c;
+	unsigned char buf[1];
+	int res;
+	static int esc = -1;
+	static char esc_buf[7];
+
+	res = read(sock, buf, 1);
+	if (res < 0)
+		perror("read");
+	if (res <= 0)
+		return EDIT_KEY_EOF;
+
+	c = buf[0];
+
+	if (esc >= 0) {
+		if (c == 27 /* ESC */) {
+			esc = 0;
+			return EDIT_KEY_NONE;
+		}
+
+		if (esc == 6) {
+			show_esc_buf(esc_buf, c, 0);
+			esc = -1;
+		} else {
+			esc_buf[esc++] = c;
+			esc_buf[esc] = '\0';
+		}
+	}
+
+	if (esc == 1) {
+		if (esc_buf[0] != '[' && esc_buf[0] != 'O') {
+			show_esc_buf(esc_buf, c, 1);
+			esc = -1;
+			return EDIT_KEY_NONE;
+		} else
+			return EDIT_KEY_NONE; /* Escape sequence continues */
+	}
+
+	if (esc > 1) {
+		if ((c >= '0' && c <= '9') || c == ';')
+			return EDIT_KEY_NONE; /* Escape sequence continues */
+
+		if (c == '~' || (c >= 'A' && c <= 'Z')) {
+			esc = -1;
+			return esc_seq_to_key(esc_buf);
+		}
+
+		show_esc_buf(esc_buf, c, 2);
+		esc = -1;
+		return EDIT_KEY_NONE;
+	}
+
+	switch (c) {
+	case 1:
+		return EDIT_KEY_CTRL_A;
+	case 2:
+		return EDIT_KEY_CTRL_B;
+	case 4:
+		return EDIT_KEY_CTRL_D;
+	case 5:
+		return EDIT_KEY_CTRL_E;
+	case 6:
+		return EDIT_KEY_CTRL_F;
+	case 7:
+		return EDIT_KEY_CTRL_G;
+	case 8:
+		return EDIT_KEY_CTRL_H;
+	case 9:
+		return EDIT_KEY_TAB;
+	case 10:
+		return EDIT_KEY_CTRL_J;
+	case 13: /* CR */
+		return EDIT_KEY_ENTER;
+	case 11:
+		return EDIT_KEY_CTRL_K;
+	case 12:
+		return EDIT_KEY_CTRL_L;
+	case 14:
+		return EDIT_KEY_CTRL_N;
+	case 15:
+		return EDIT_KEY_CTRL_O;
+	case 16:
+		return EDIT_KEY_CTRL_P;
+	case 18:
+		return EDIT_KEY_CTRL_R;
+	case 20:
+		return EDIT_KEY_CTRL_T;
+	case 21:
+		return EDIT_KEY_CTRL_U;
+	case 22:
+		return EDIT_KEY_CTRL_V;
+	case 23:
+		return EDIT_KEY_CTRL_W;
+	case 27: /* ESC */
+		esc = 0;
+		return EDIT_KEY_NONE;
+	case 127:
+		return EDIT_KEY_BACKSPACE;
+	default:
+		return c;
+	}
+}
+
+
+static char search_buf[21];
+static int search_skip;
+
+static char * search_find(void)
+{
+	struct edit_history *h;
+	size_t len = os_strlen(search_buf);
+	int skip = search_skip;
+
+	if (len == 0)
+		return NULL;
+
+	dl_list_for_each(h, &history_list, struct edit_history, list) {
+		if (os_strstr(h->str, search_buf)) {
+			if (skip == 0)
+				return h->str;
+			skip--;
+		}
+	}
+
+	search_skip = 0;
+	return NULL;
+}
+
+
+static void search_redraw(void)
+{
+	char *match = search_find();
+	printf("\rsearch '%s': %s" CLEAR_END_LINE,
+	       search_buf, match ? match : "");
+	printf("\rsearch '%s", search_buf);
+	fflush(stdout);
+}
+
+
+static void search_start(void)
+{
+	edit_clear_line();
+	search_buf[0] = '\0';
+	search_skip = 0;
+	search_redraw();
+}
+
+
+static void search_clear(void)
+{
+	search_redraw();
+	printf("\r" CLEAR_END_LINE);
+}
+
+
+static void search_stop(void)
+{
+	char *match = search_find();
+	search_buf[0] = '\0';
+	search_clear();
+	if (match) {
+		os_strlcpy(cmdbuf, match, CMD_BUF_LEN);
+		cmdbuf_len = os_strlen(cmdbuf);
+		cmdbuf_pos = cmdbuf_len;
+	}
+	edit_redraw();
+}
+
+
+static void search_cancel(void)
+{
+	search_buf[0] = '\0';
+	search_clear();
+	edit_redraw();
+}
+
+
+static void search_backspace(void)
+{
+	size_t len;
+	len = os_strlen(search_buf);
+	if (len == 0)
+		return;
+	search_buf[len - 1] = '\0';
+	search_skip = 0;
+	search_redraw();
+}
+
+
+static void search_next(void)
+{
+	search_skip++;
+	search_find();
+	search_redraw();
+}
+
+
+static void search_char(char c)
+{
+	size_t len;
+	len = os_strlen(search_buf);
+	if (len == sizeof(search_buf) - 1)
+		return;
+	search_buf[len] = c;
+	search_buf[len + 1] = '\0';
+	search_skip = 0;
+	search_redraw();
+}
+
+
+static enum edit_key_code search_key(enum edit_key_code c)
+{
+	switch (c) {
+	case EDIT_KEY_ENTER:
+	case EDIT_KEY_CTRL_J:
+	case EDIT_KEY_LEFT:
+	case EDIT_KEY_RIGHT:
+	case EDIT_KEY_HOME:
+	case EDIT_KEY_END:
+	case EDIT_KEY_CTRL_A:
+	case EDIT_KEY_CTRL_E:
+		search_stop();
+		return c;
+	case EDIT_KEY_DOWN:
+	case EDIT_KEY_UP:
+		search_cancel();
+		return EDIT_KEY_EOF;
+	case EDIT_KEY_CTRL_H:
+	case EDIT_KEY_BACKSPACE:
+		search_backspace();
+		break;
+	case EDIT_KEY_CTRL_R:
+		search_next();
+		break;
+	default:
+		if (c >= 32 && c <= 255)
+			search_char(c);
+		break;
+	}
+
+	return EDIT_KEY_NONE;
+}
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	static int last_tab = 0;
+	static int search = 0;
+	enum edit_key_code c;
+
+	c = edit_read_key(sock);
+
+	if (search) {
+		c = search_key(c);
+		if (c == EDIT_KEY_NONE)
+			return;
+		search = 0;
+		if (c == EDIT_KEY_EOF)
+			return;
+	}
+
+	if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE)
+		last_tab = 0;
+
+	switch (c) {
+	case EDIT_KEY_NONE:
+		break;
+	case EDIT_KEY_EOF:
+		edit_eof_cb(edit_cb_ctx);
+		break;
+	case EDIT_KEY_TAB:
+		complete(last_tab);
+		last_tab = 1;
+		break;
+	case EDIT_KEY_UP:
+	case EDIT_KEY_CTRL_P:
+		history_prev();
+		break;
+	case EDIT_KEY_DOWN:
+	case EDIT_KEY_CTRL_N:
+		history_next();
+		break;
+	case EDIT_KEY_RIGHT:
+	case EDIT_KEY_CTRL_F:
+		move_right();
+		break;
+	case EDIT_KEY_LEFT:
+	case EDIT_KEY_CTRL_B:
+		move_left();
+		break;
+	case EDIT_KEY_CTRL_RIGHT:
+		move_word_right();
+		break;
+	case EDIT_KEY_CTRL_LEFT:
+		move_word_left();
+		break;
+	case EDIT_KEY_DELETE:
+		delete_current();
+		break;
+	case EDIT_KEY_END:
+		move_end();
+		break;
+	case EDIT_KEY_HOME:
+	case EDIT_KEY_CTRL_A:
+		move_start();
+		break;
+	case EDIT_KEY_F2:
+		history_debug_dump();
+		break;
+	case EDIT_KEY_CTRL_D:
+		if (cmdbuf_len > 0) {
+			delete_current();
+			return;
+		}
+		printf("\n");
+		edit_eof_cb(edit_cb_ctx);
+		break;
+	case EDIT_KEY_CTRL_E:
+		move_end();
+		break;
+	case EDIT_KEY_CTRL_H:
+	case EDIT_KEY_BACKSPACE:
+		delete_left();
+		break;
+	case EDIT_KEY_ENTER:
+	case EDIT_KEY_CTRL_J:
+		process_cmd();
+		break;
+	case EDIT_KEY_CTRL_K:
+		clear_right();
+		break;
+	case EDIT_KEY_CTRL_L:
+		edit_clear_line();
+		edit_redraw();
+		break;
+	case EDIT_KEY_CTRL_R:
+		search = 1;
+		search_start();
+		break;
+	case EDIT_KEY_CTRL_U:
+		clear_left();
+		break;
+	case EDIT_KEY_CTRL_W:
+		delete_word();
+		break;
+	default:
+		if (c >= 32 && c <= 255)
+			insert_char(c);
+		break;
+	}
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+	      void (*eof_cb)(void *ctx),
+	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+	      void *ctx, const char *history_file, const char *ps)
+{
+	currbuf[0] = '\0';
+	dl_list_init(&history_list);
+	history_curr = NULL;
+	if (history_file)
+		history_read(history_file);
+
+	edit_cb_ctx = ctx;
+	edit_cmd_cb = cmd_cb;
+	edit_eof_cb = eof_cb;
+	edit_completion_cb = completion_cb;
+
+	tcgetattr(STDIN_FILENO, &prevt);
+	newt = prevt;
+	newt.c_lflag &= ~(ICANON | ECHO);
+	tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+
+	eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+
+	ps2 = ps;
+	printf("%s> ", ps2 ? ps2 : "");
+	fflush(stdout);
+
+	return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+		 int (*filter_cb)(void *ctx, const char *cmd))
+{
+	struct edit_history *h;
+	if (history_file)
+		history_write(history_file, filter_cb);
+	while ((h = dl_list_first(&history_list, struct edit_history, list))) {
+		dl_list_del(&h->list);
+		os_free(h);
+	}
+	edit_clear_line();
+	putchar('\r');
+	fflush(stdout);
+	eloop_unregister_read_sock(STDIN_FILENO);
+	tcsetattr(STDIN_FILENO, TCSANOW, &prevt);
+}
+
+
+void edit_redraw(void)
+{
+	char tmp;
+	cmdbuf[cmdbuf_len] = '\0';
+	printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf);
+	if (cmdbuf_pos != cmdbuf_len) {
+		tmp = cmdbuf[cmdbuf_pos];
+		cmdbuf[cmdbuf_pos] = '\0';
+		printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf);
+		cmdbuf[cmdbuf_pos] = tmp;
+	}
+	fflush(stdout);
+}
diff --git a/hostap/src/utils/edit.h b/hostap/src/utils/edit.h
new file mode 100644
index 0000000..ad27f1c
--- /dev/null
+++ b/hostap/src/utils/edit.h
@@ -0,0 +1,21 @@
+/*
+ * Command line editing and history
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EDIT_H
+#define EDIT_H
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+	      void (*eof_cb)(void *ctx),
+	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+	      void *ctx, const char *history_file, const char *ps);
+void edit_deinit(const char *history_file,
+		 int (*filter_cb)(void *ctx, const char *cmd));
+void edit_clear_line(void);
+void edit_redraw(void);
+
+#endif /* EDIT_H */
diff --git a/hostap/src/utils/edit_readline.c b/hostap/src/utils/edit_readline.c
new file mode 100644
index 0000000..c2a5bca
--- /dev/null
+++ b/hostap/src/utils/edit_readline.c
@@ -0,0 +1,192 @@
+/*
+ * Command line editing and history wrapper for readline
+ * Copyright (c) 2010, 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 <readline/readline.h>
+#include <readline/history.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "edit.h"
+
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
+	NULL;
+
+static char **pending_completions = NULL;
+
+
+static void readline_free_completions(void)
+{
+	int i;
+	if (pending_completions == NULL)
+		return;
+	for (i = 0; pending_completions[i]; i++)
+		os_free(pending_completions[i]);
+	os_free(pending_completions);
+	pending_completions = NULL;
+}
+
+
+static char * readline_completion_func(const char *text, int state)
+{
+	static int pos = 0;
+	static size_t len = 0;
+
+	if (pending_completions == NULL) {
+		rl_attempted_completion_over = 1;
+		return NULL;
+	}
+
+	if (state == 0) {
+		pos = 0;
+		len = os_strlen(text);
+	}
+	for (; pending_completions[pos]; pos++) {
+		if (strncmp(pending_completions[pos], text, len) == 0)
+			return strdup(pending_completions[pos++]);
+	}
+
+	rl_attempted_completion_over = 1;
+	return NULL;
+}
+
+
+static char ** readline_completion(const char *text, int start, int end)
+{
+	readline_free_completions();
+	if (edit_completion_cb)
+		pending_completions = edit_completion_cb(edit_cb_ctx,
+							 rl_line_buffer, end);
+	return rl_completion_matches(text, readline_completion_func);
+}
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	rl_callback_read_char();
+}
+
+
+static void trunc_nl(char *str)
+{
+	char *pos = str;
+	while (*pos != '\0') {
+		if (*pos == '\n') {
+			*pos = '\0';
+			break;
+		}
+		pos++;
+	}
+}
+
+
+static void readline_cmd_handler(char *cmd)
+{
+	if (cmd && *cmd) {
+		HIST_ENTRY *h;
+		while (next_history())
+			;
+		h = previous_history();
+		if (h == NULL || os_strcmp(cmd, h->line) != 0)
+			add_history(cmd);
+		next_history();
+	}
+	if (cmd == NULL) {
+		edit_eof_cb(edit_cb_ctx);
+		return;
+	}
+	trunc_nl(cmd);
+	edit_cmd_cb(edit_cb_ctx, cmd);
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+	      void (*eof_cb)(void *ctx),
+	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+	      void *ctx, const char *history_file, const char *ps)
+{
+	edit_cb_ctx = ctx;
+	edit_cmd_cb = cmd_cb;
+	edit_eof_cb = eof_cb;
+	edit_completion_cb = completion_cb;
+
+	rl_attempted_completion_function = readline_completion;
+	if (history_file) {
+		read_history(history_file);
+		stifle_history(100);
+	}
+
+	eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+
+	if (ps) {
+		size_t blen = os_strlen(ps) + 3;
+		char *ps2 = os_malloc(blen);
+		if (ps2) {
+			os_snprintf(ps2, blen, "%s> ", ps);
+			rl_callback_handler_install(ps2, readline_cmd_handler);
+			os_free(ps2);
+			return 0;
+		}
+	}
+
+	rl_callback_handler_install("> ", readline_cmd_handler);
+
+	return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+		 int (*filter_cb)(void *ctx, const char *cmd))
+{
+	rl_set_prompt("");
+	rl_replace_line("", 0);
+	rl_redisplay();
+	rl_callback_handler_remove();
+	readline_free_completions();
+
+	eloop_unregister_read_sock(STDIN_FILENO);
+
+	if (history_file) {
+		/* Save command history, excluding lines that may contain
+		 * passwords. */
+		HIST_ENTRY *h;
+		history_set_pos(0);
+		while ((h = current_history())) {
+			char *p = h->line;
+			while (*p == ' ' || *p == '\t')
+				p++;
+			if (filter_cb && filter_cb(edit_cb_ctx, p)) {
+				h = remove_history(where_history());
+				if (h) {
+					free(h->line);
+					free(h->data);
+					free(h);
+				} else
+					next_history();
+			} else
+				next_history();
+		}
+		write_history(history_file);
+	}
+}
+
+
+void edit_clear_line(void)
+{
+}
+
+
+void edit_redraw(void)
+{
+	rl_on_new_line();
+	rl_redisplay();
+}
diff --git a/hostap/src/utils/edit_simple.c b/hostap/src/utils/edit_simple.c
new file mode 100644
index 0000000..13173cb
--- /dev/null
+++ b/hostap/src/utils/edit_simple.c
@@ -0,0 +1,92 @@
+/*
+ * Minimal command line editing
+ * Copyright (c) 2010, 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 "eloop.h"
+#include "edit.h"
+
+
+#define CMD_BUF_LEN 4096
+static char cmdbuf[CMD_BUF_LEN];
+static int cmdbuf_pos = 0;
+static const char *ps2 = NULL;
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	int c;
+	unsigned char buf[1];
+	int res;
+
+	res = read(sock, buf, 1);
+	if (res < 0)
+		perror("read");
+	if (res <= 0) {
+		edit_eof_cb(edit_cb_ctx);
+		return;
+	}
+	c = buf[0];
+
+	if (c == '\r' || c == '\n') {
+		cmdbuf[cmdbuf_pos] = '\0';
+		cmdbuf_pos = 0;
+		edit_cmd_cb(edit_cb_ctx, cmdbuf);
+		printf("%s> ", ps2 ? ps2 : "");
+		fflush(stdout);
+		return;
+	}
+
+	if (c >= 32 && c <= 255) {
+		if (cmdbuf_pos < (int) sizeof(cmdbuf) - 1) {
+			cmdbuf[cmdbuf_pos++] = c;
+		}
+	}
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+	      void (*eof_cb)(void *ctx),
+	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+	      void *ctx, const char *history_file, const char *ps)
+{
+	edit_cb_ctx = ctx;
+	edit_cmd_cb = cmd_cb;
+	edit_eof_cb = eof_cb;
+	eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+	ps2 = ps;
+
+	printf("%s> ", ps2 ? ps2 : "");
+	fflush(stdout);
+
+	return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+		 int (*filter_cb)(void *ctx, const char *cmd))
+{
+	eloop_unregister_read_sock(STDIN_FILENO);
+}
+
+
+void edit_clear_line(void)
+{
+}
+
+
+void edit_redraw(void)
+{
+	cmdbuf[cmdbuf_pos] = '\0';
+	printf("\r> %s", cmdbuf);
+}
diff --git a/hostap/src/utils/eloop.c b/hostap/src/utils/eloop.c
new file mode 100644
index 0000000..8ae76a7
--- /dev/null
+++ b/hostap/src/utils/eloop.c
@@ -0,0 +1,1175 @@
+/*
+ * Event loop based on select() loop
+ * 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 <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 <poll.h>
+#endif /* CONFIG_ELOOP_POLL */
+
+#ifdef CONFIG_ELOOP_EPOLL
+#include <sys/epoll.h>
+#endif /* CONFIG_ELOOP_EPOLL */
+
+struct eloop_sock {
+	int sock;
+	void *eloop_data;
+	void *user_data;
+	eloop_sock_handler handler;
+	WPA_TRACE_REF(eloop);
+	WPA_TRACE_REF(user);
+	WPA_TRACE_INFO
+};
+
+struct eloop_timeout {
+	struct dl_list list;
+	struct os_reltime time;
+	void *eloop_data;
+	void *user_data;
+	eloop_timeout_handler handler;
+	WPA_TRACE_REF(eloop);
+	WPA_TRACE_REF(user);
+	WPA_TRACE_INFO
+};
+
+struct eloop_signal {
+	int sig;
+	void *user_data;
+	eloop_signal_handler handler;
+	int signaled;
+};
+
+struct eloop_sock_table {
+	int count;
+	struct eloop_sock *table;
+	eloop_event_type type;
+	int changed;
+};
+
+struct eloop_data {
+	int max_sock;
+
+	int count; /* sum of all table counts */
+#ifdef CONFIG_ELOOP_POLL
+	int max_pollfd_map; /* number of pollfds_map currently allocated */
+	int max_poll_fds; /* number of pollfds currently allocated */
+	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;
+
+	struct dl_list timeout;
+
+	int signal_count;
+	struct eloop_signal *signals;
+	int signaled;
+	int pending_terminate;
+
+	int terminate;
+};
+
+static struct eloop_data eloop;
+
+
+
+#if defined(WPA_TRACE) || defined(WPA_TRACE_SIGNALS)
+static void eloop_error_signal_handler(int sig)
+{
+	const char * what;
+
+	/* use the whole string as the string functions are not called out a signal() safe */
+
+	switch (sig) {
+	case SIGSEGV:
+		what = "eloop SIGSEGV";
+		break;
+
+	case SIGABRT:
+		what = "eloop SIGABRT";
+		break;
+
+	case SIGBUS:
+		what = "eloop SIGBUS";
+		break;
+
+	case SIGILL:
+		what = "eloop SIGILL";
+		break;
+
+	default:
+		what = "eloop ???";
+	}
+
+	wpa_trace_show(what);
+	exit(sig);
+}
+#endif /* defined(WPA_TRACE) || defined(WPA_TRACE_SIGNALS) */
+
+
+#ifdef WPA_TRACE
+
+static void eloop_trace_sock_add_ref(struct eloop_sock_table *table)
+{
+	int i;
+	if (table == NULL || table->table == NULL)
+		return;
+	for (i = 0; i < table->count; i++) {
+		wpa_trace_add_ref(&table->table[i], eloop,
+				  table->table[i].eloop_data);
+		wpa_trace_add_ref(&table->table[i], user,
+				  table->table[i].user_data);
+	}
+}
+
+
+static void eloop_trace_sock_remove_ref(struct eloop_sock_table *table)
+{
+	int i;
+	if (table == NULL || table->table == NULL)
+		return;
+	for (i = 0; i < table->count; i++) {
+		wpa_trace_remove_ref(&table->table[i], eloop,
+				     table->table[i].eloop_data);
+		wpa_trace_remove_ref(&table->table[i], user,
+				     table->table[i].user_data);
+	}
+}
+
+#else /* WPA_TRACE */
+
+#define eloop_trace_sock_add_ref(table) do { } while (0)
+#define eloop_trace_sock_remove_ref(table) do { } while (0)
+
+#endif /* WPA_TRACE */
+
+
+int eloop_init(void)
+{
+	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 */
+
+#if defined(WPA_TRACE) || defined(WPA_TRACE_SIGNALS)
+	signal(SIGSEGV, eloop_error_signal_handler);
+	signal(SIGABRT, eloop_error_signal_handler);
+	signal(SIGBUS, eloop_error_signal_handler);
+	signal(SIGILL, eloop_error_signal_handler);
+#endif /* defined(WPA_TRACE) || defined(WPA_TRACE_SIGNALS) */
+	return 0;
+}
+
+
+static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
+                                     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;
+
+	if (sock > eloop.max_sock)
+		new_max_sock = sock;
+	else
+		new_max_sock = eloop.max_sock;
+
+	if (table == NULL)
+		return -1;
+
+#ifdef CONFIG_ELOOP_POLL
+	if (new_max_sock >= eloop.max_pollfd_map) {
+		struct pollfd **nmap;
+		nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50,
+					sizeof(struct pollfd *));
+		if (nmap == NULL)
+			return -1;
+
+		eloop.max_pollfd_map = new_max_sock + 50;
+		eloop.pollfds_map = nmap;
+	}
+
+	if (eloop.count + 1 > eloop.max_poll_fds) {
+		struct pollfd *n;
+		int nmax = eloop.count + 1 + 50;
+		n = os_realloc_array(eloop.pollfds, nmax,
+				     sizeof(struct pollfd));
+		if (n == NULL)
+			return -1;
+
+		eloop.max_poll_fds = nmax;
+		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,
+			       sizeof(struct eloop_sock));
+	if (tmp == NULL) {
+		eloop_trace_sock_add_ref(table);
+		return -1;
+	}
+
+	tmp[table->count].sock = sock;
+	tmp[table->count].eloop_data = eloop_data;
+	tmp[table->count].user_data = user_data;
+	tmp[table->count].handler = handler;
+	wpa_trace_record(&tmp[table->count]);
+	table->count++;
+	table->table = tmp;
+	eloop.max_sock = new_max_sock;
+	eloop.count++;
+	table->changed = 1;
+	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;
+}
+
+
+static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
+                                         int sock)
+{
+	int i;
+
+	if (table == NULL || table->table == NULL || table->count == 0)
+		return;
+
+	for (i = 0; i < table->count; i++) {
+		if (table->table[i].sock == sock)
+			break;
+	}
+	if (i == table->count)
+		return;
+	eloop_trace_sock_remove_ref(table);
+	if (i != table->count - 1) {
+		os_memmove(&table->table[i], &table->table[i + 1],
+			   (table->count - i - 1) *
+			   sizeof(struct eloop_sock));
+	}
+	table->count--;
+	eloop.count--;
+	table->changed = 1;
+	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 */
+}
+
+
+#ifdef CONFIG_ELOOP_POLL
+
+static struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx)
+{
+	if (fd < mx && fd >= 0)
+		return pollfds_map[fd];
+	return NULL;
+}
+
+
+static int eloop_sock_table_set_fds(struct eloop_sock_table *readers,
+				    struct eloop_sock_table *writers,
+				    struct eloop_sock_table *exceptions,
+				    struct pollfd *pollfds,
+				    struct pollfd **pollfds_map,
+				    int max_pollfd_map)
+{
+	int i;
+	int nxt = 0;
+	int fd;
+	struct pollfd *pfd;
+
+	/* Clear pollfd lookup map. It will be re-populated below. */
+	os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map);
+
+	if (readers && readers->table) {
+		for (i = 0; i < readers->count; i++) {
+			fd = readers->table[i].sock;
+			assert(fd >= 0 && fd < max_pollfd_map);
+			pollfds[nxt].fd = fd;
+			pollfds[nxt].events = POLLIN;
+			pollfds[nxt].revents = 0;
+			pollfds_map[fd] = &(pollfds[nxt]);
+			nxt++;
+		}
+	}
+
+	if (writers && writers->table) {
+		for (i = 0; i < writers->count; i++) {
+			/*
+			 * See if we already added this descriptor, update it
+			 * if so.
+			 */
+			fd = writers->table[i].sock;
+			assert(fd >= 0 && fd < max_pollfd_map);
+			pfd = pollfds_map[fd];
+			if (!pfd) {
+				pfd = &(pollfds[nxt]);
+				pfd->events = 0;
+				pfd->fd = fd;
+				pollfds[i].revents = 0;
+				pollfds_map[fd] = pfd;
+				nxt++;
+			}
+			pfd->events |= POLLOUT;
+		}
+	}
+
+	/*
+	 * Exceptions are always checked when using poll, but I suppose it's
+	 * possible that someone registered a socket *only* for exception
+	 * handling. Set the POLLIN bit in this case.
+	 */
+	if (exceptions && exceptions->table) {
+		for (i = 0; i < exceptions->count; i++) {
+			/*
+			 * See if we already added this descriptor, just use it
+			 * if so.
+			 */
+			fd = exceptions->table[i].sock;
+			assert(fd >= 0 && fd < max_pollfd_map);
+			pfd = pollfds_map[fd];
+			if (!pfd) {
+				pfd = &(pollfds[nxt]);
+				pfd->events = POLLIN;
+				pfd->fd = fd;
+				pollfds[i].revents = 0;
+				pollfds_map[fd] = pfd;
+				nxt++;
+			}
+		}
+	}
+
+	return nxt;
+}
+
+
+static int eloop_sock_table_dispatch_table(struct eloop_sock_table *table,
+					   struct pollfd **pollfds_map,
+					   int max_pollfd_map,
+					   short int revents)
+{
+	int i;
+	struct pollfd *pfd;
+
+	if (!table || !table->table)
+		return 0;
+
+	table->changed = 0;
+	for (i = 0; i < table->count; i++) {
+		pfd = find_pollfd(pollfds_map, table->table[i].sock,
+				  max_pollfd_map);
+		if (!pfd)
+			continue;
+
+		if (!(pfd->revents & revents))
+			continue;
+
+		table->table[i].handler(table->table[i].sock,
+					table->table[i].eloop_data,
+					table->table[i].user_data);
+		if (table->changed)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void eloop_sock_table_dispatch(struct eloop_sock_table *readers,
+				      struct eloop_sock_table *writers,
+				      struct eloop_sock_table *exceptions,
+				      struct pollfd **pollfds_map,
+				      int max_pollfd_map)
+{
+	if (eloop_sock_table_dispatch_table(readers, pollfds_map,
+					    max_pollfd_map, POLLIN | POLLERR |
+					    POLLHUP))
+		return; /* pollfds may be invalid at this point */
+
+	if (eloop_sock_table_dispatch_table(writers, pollfds_map,
+					    max_pollfd_map, POLLOUT))
+		return; /* pollfds may be invalid at this point */
+
+	eloop_sock_table_dispatch_table(exceptions, pollfds_map,
+					max_pollfd_map, POLLERR | POLLHUP);
+}
+
+#endif /* CONFIG_ELOOP_POLL */
+
+#ifdef CONFIG_ELOOP_SELECT
+
+static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
+				     fd_set *fds)
+{
+	int i;
+
+	FD_ZERO(fds);
+
+	if (table->table == NULL)
+		return;
+
+	for (i = 0; i < table->count; i++) {
+		assert(table->table[i].sock >= 0);
+		FD_SET(table->table[i].sock, fds);
+	}
+}
+
+
+static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
+				      fd_set *fds)
+{
+	int i;
+
+	if (table == NULL || table->table == NULL)
+		return;
+
+	table->changed = 0;
+	for (i = 0; i < table->count; i++) {
+		if (FD_ISSET(table->table[i].sock, fds)) {
+			table->table[i].handler(table->table[i].sock,
+						table->table[i].eloop_data,
+						table->table[i].user_data);
+			if (table->changed)
+				break;
+		}
+	}
+}
+
+#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);
+		if (eloop.readers.changed ||
+		    eloop.writers.changed ||
+		    eloop.exceptions.changed)
+			break;
+	}
+}
+#endif /* CONFIG_ELOOP_EPOLL */
+
+
+static void eloop_sock_table_destroy(struct eloop_sock_table *table)
+{
+	if (table) {
+		int i;
+		for (i = 0; i < table->count && table->table; i++) {
+			wpa_printf(MSG_INFO, "ELOOP: remaining socket: "
+				   "sock=%d eloop_data=%p user_data=%p "
+				   "handler=%p",
+				   table->table[i].sock,
+				   table->table[i].eloop_data,
+				   table->table[i].user_data,
+				   table->table[i].handler);
+			wpa_trace_dump_funcname("eloop unregistered socket "
+						"handler",
+						table->table[i].handler);
+			wpa_trace_dump("eloop sock", &table->table[i]);
+		}
+		os_free(table->table);
+	}
+}
+
+
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+			     void *eloop_data, void *user_data)
+{
+	return eloop_register_sock(sock, EVENT_TYPE_READ, handler,
+				   eloop_data, user_data);
+}
+
+
+void eloop_unregister_read_sock(int sock)
+{
+	eloop_unregister_sock(sock, EVENT_TYPE_READ);
+}
+
+
+static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type)
+{
+	switch (type) {
+	case EVENT_TYPE_READ:
+		return &eloop.readers;
+	case EVENT_TYPE_WRITE:
+		return &eloop.writers;
+	case EVENT_TYPE_EXCEPTION:
+		return &eloop.exceptions;
+	}
+
+	return NULL;
+}
+
+
+int eloop_register_sock(int sock, eloop_event_type type,
+			eloop_sock_handler handler,
+			void *eloop_data, void *user_data)
+{
+	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);
+}
+
+
+void eloop_unregister_sock(int sock, eloop_event_type type)
+{
+	struct eloop_sock_table *table;
+
+	table = eloop_get_sock_table(type);
+	eloop_sock_table_remove_sock(table, sock);
+}
+
+
+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_reltime(&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;
+	wpa_trace_add_ref(timeout, eloop, eloop_data);
+	wpa_trace_add_ref(timeout, user, user_data);
+	wpa_trace_record(timeout);
+
+	/* Maintain timeouts in order of increasing time */
+	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+		if (os_reltime_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);
+	wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data);
+	wpa_trace_remove_ref(timeout, user, timeout->user_data);
+	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_reltime *remaining)
+{
+	struct eloop_timeout *timeout, *prev;
+	int removed = 0;
+	struct os_reltime now;
+
+	os_get_reltime(&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_reltime_before(&now, &timeout->time))
+				os_reltime_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;
+}
+
+
+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)
+{
+	wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in "
+		   "two seconds. Looks like there\n"
+		   "is a bug that ends up in a busy loop that "
+		   "prevents clean shutdown.\n"
+		   "Killing program forcefully.\n");
+	exit(1);
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static void eloop_handle_signal(int sig)
+{
+	int i;
+
+#ifndef CONFIG_NATIVE_WINDOWS
+	if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) {
+		/* Use SIGALRM to break out from potential busy loops that
+		 * would not allow the program to be killed. */
+		eloop.pending_terminate = 1;
+		signal(SIGALRM, eloop_handle_alarm);
+		alarm(2);
+	}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	eloop.signaled++;
+	for (i = 0; i < eloop.signal_count; i++) {
+		if (eloop.signals[i].sig == sig) {
+			eloop.signals[i].signaled++;
+			break;
+		}
+	}
+}
+
+
+static void eloop_process_pending_signals(void)
+{
+	int i;
+
+	if (eloop.signaled == 0)
+		return;
+	eloop.signaled = 0;
+
+	if (eloop.pending_terminate) {
+#ifndef CONFIG_NATIVE_WINDOWS
+		alarm(0);
+#endif /* CONFIG_NATIVE_WINDOWS */
+		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;
+	signal(sig, eloop_handle_signal);
+
+	return 0;
+}
+
+
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+				    void *user_data)
+{
+	int ret = eloop_register_signal(SIGINT, handler, user_data);
+	if (ret == 0)
+		ret = eloop_register_signal(SIGTERM, handler, user_data);
+	return ret;
+}
+
+
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+				   void *user_data)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+	return 0;
+#else /* CONFIG_NATIVE_WINDOWS */
+	return eloop_register_signal(SIGHUP, handler, user_data);
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
+void eloop_run(void)
+{
+#ifdef CONFIG_ELOOP_POLL
+	int num_poll_fds;
+	int timeout_ms = 0;
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
+	fd_set *rfds, *wfds, *efds;
+	struct timeval _tv;
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+	int timeout_ms = -1;
+#endif /* CONFIG_ELOOP_EPOLL */
+	int res;
+	struct os_reltime tv, now;
+
+#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_SELECT */
+
+	while (!eloop.terminate &&
+	       (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
+		eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
+		struct eloop_timeout *timeout;
+
+		if (eloop.pending_terminate) {
+			/*
+			 * This may happen in some corner cases where a signal
+			 * is received during a blocking operation. We need to
+			 * process the pending signals and exit if requested to
+			 * avoid hitting the SIGALRM limit if the blocking
+			 * operation took more than two seconds.
+			 */
+			eloop_process_pending_signals();
+			if (eloop.terminate)
+				break;
+		}
+
+		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+					list);
+		if (timeout) {
+			os_get_reltime(&now);
+			if (os_reltime_before(&now, &timeout->time))
+				os_reltime_sub(&timeout->time, &now, &tv);
+			else
+				tv.sec = tv.usec = 0;
+#if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
+			timeout_ms = tv.sec * 1000 + tv.usec / 1000;
+#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_SELECT */
+		}
+
+#ifdef CONFIG_ELOOP_POLL
+		num_poll_fds = eloop_sock_table_set_fds(
+			&eloop.readers, &eloop.writers, &eloop.exceptions,
+			eloop.pollfds, eloop.pollfds_map,
+			eloop.max_pollfd_map);
+		res = poll(eloop.pollfds, num_poll_fds,
+			   timeout ? timeout_ms : -1);
+#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) {
+			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;
+		}
+
+		eloop.readers.changed = 0;
+		eloop.writers.changed = 0;
+		eloop.exceptions.changed = 0;
+
+		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_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 =
+					timeout->handler;
+				eloop_remove_timeout(timeout);
+				handler(eloop_data, user_data);
+			}
+
+		}
+
+		if (res <= 0)
+			continue;
+
+		if (eloop.readers.changed ||
+		    eloop.writers.changed ||
+		    eloop.exceptions.changed) {
+			 /*
+			  * Sockets may have been closed and reopened with the
+			  * same FD in the signal or timeout handlers, so we
+			  * must skip the previous results and check again
+			  * whether any of the currently registered sockets have
+			  * events.
+			  */
+			continue;
+		}
+
+#ifdef CONFIG_ELOOP_POLL
+		eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
+					  &eloop.exceptions, eloop.pollfds_map,
+					  eloop.max_pollfd_map);
+#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_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+		eloop_sock_table_dispatch(eloop.epoll_events, res);
+#endif /* CONFIG_ELOOP_EPOLL */
+	}
+
+	eloop.terminate = 0;
+out:
+#ifdef CONFIG_ELOOP_SELECT
+	os_free(rfds);
+	os_free(wfds);
+	os_free(efds);
+#endif /* CONFIG_ELOOP_SELECT */
+	return;
+}
+
+
+void eloop_terminate(void)
+{
+	eloop.terminate = 1;
+}
+
+
+void eloop_destroy(void)
+{
+	struct eloop_timeout *timeout, *prev;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
+			      struct eloop_timeout, list) {
+		int sec, usec;
+		sec = timeout->time.sec - now.sec;
+		usec = timeout->time.usec - now.usec;
+		if (timeout->time.usec < now.usec) {
+			sec--;
+			usec += 1000000;
+		}
+		wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d "
+			   "eloop_data=%p user_data=%p handler=%p",
+			   sec, usec, timeout->eloop_data, timeout->user_data,
+			   timeout->handler);
+		wpa_trace_dump_funcname("eloop unregistered timeout handler",
+					timeout->handler);
+		wpa_trace_dump("eloop timeout", timeout);
+		eloop_remove_timeout(timeout);
+	}
+	eloop_sock_table_destroy(&eloop.readers);
+	eloop_sock_table_destroy(&eloop.writers);
+	eloop_sock_table_destroy(&eloop.exceptions);
+	os_free(eloop.signals);
+
+#ifdef CONFIG_ELOOP_POLL
+	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 */
+}
+
+
+int eloop_terminated(void)
+{
+	return eloop.terminate || eloop.pending_terminate;
+}
+
+
+void eloop_wait_for_read_sock(int sock)
+{
+#ifdef CONFIG_ELOOP_POLL
+	struct pollfd pfd;
+
+	if (sock < 0)
+		return;
+
+	os_memset(&pfd, 0, sizeof(pfd));
+	pfd.fd = sock;
+	pfd.events = POLLIN;
+
+	poll(&pfd, 1, -1);
+#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)
+		return;
+
+	FD_ZERO(&rfds);
+	FD_SET(sock, &rfds);
+	select(sock + 1, &rfds, NULL, NULL, NULL);
+#endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */
+}
+
+#ifdef CONFIG_ELOOP_SELECT
+#undef CONFIG_ELOOP_SELECT
+#endif /* CONFIG_ELOOP_SELECT */
diff --git a/hostap/src/utils/eloop.h b/hostap/src/utils/eloop.h
new file mode 100644
index 0000000..07b8c0d
--- /dev/null
+++ b/hostap/src/utils/eloop.h
@@ -0,0 +1,359 @@
+/*
+ * Event loop
+ * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file defines an event loop interface that supports processing events
+ * from registered timeouts (i.e., do something after N seconds), sockets
+ * (e.g., a new packet available for reading), and signals. eloop.c is an
+ * implementation of this interface using select() and sockets. This is
+ * suitable for most UNIX/POSIX systems. When porting to other operating
+ * systems, it may be necessary to replace that implementation with OS specific
+ * mechanisms.
+ */
+
+#ifndef ELOOP_H
+#define ELOOP_H
+
+/**
+ * ELOOP_ALL_CTX - eloop_cancel_timeout() magic number to match all timeouts
+ */
+#define ELOOP_ALL_CTX (void *) -1
+
+/**
+ * eloop_event_type - eloop socket event type for eloop_register_sock()
+ * @EVENT_TYPE_READ: Socket has data available for reading
+ * @EVENT_TYPE_WRITE: Socket has room for new data to be written
+ * @EVENT_TYPE_EXCEPTION: An exception has been reported
+ */
+typedef enum {
+	EVENT_TYPE_READ = 0,
+	EVENT_TYPE_WRITE,
+	EVENT_TYPE_EXCEPTION
+} eloop_event_type;
+
+/**
+ * eloop_sock_handler - eloop socket event callback type
+ * @sock: File descriptor number for the socket
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx);
+
+/**
+ * eloop_event_handler - eloop generic event callback type
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_event_handler)(void *eloop_data, void *user_ctx);
+
+/**
+ * eloop_timeout_handler - eloop timeout event callback type
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx);
+
+/**
+ * eloop_signal_handler - eloop signal event callback type
+ * @sig: Signal number
+ * @signal_ctx: Registered callback context data (user_data from
+ * eloop_register_signal(), eloop_register_signal_terminate(), or
+ * eloop_register_signal_reconfig() call)
+ */
+typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
+
+/**
+ * eloop_init() - Initialize global event loop data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before any other eloop_* function.
+ */
+int eloop_init(void);
+
+/**
+ * eloop_register_read_sock - Register handler for read events
+ * @sock: File descriptor number for the socket
+ * @handler: Callback function to be called when data is available for reading
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a read socket notifier for the given file descriptor. The handler
+ * function will be called whenever data is available for reading from the
+ * socket. The handler function is responsible for clearing the event after
+ * having processed it in order to avoid eloop from calling the handler again
+ * for the same event.
+ */
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+			     void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_read_sock - Unregister handler for read events
+ * @sock: File descriptor number for the socket
+ *
+ * Unregister a read socket notifier that was previously registered with
+ * eloop_register_read_sock().
+ */
+void eloop_unregister_read_sock(int sock);
+
+/**
+ * eloop_register_sock - Register handler for socket events
+ * @sock: File descriptor number for the socket
+ * @type: Type of event to wait for
+ * @handler: Callback function to be called when the event is triggered
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register an event notifier for the given socket's file descriptor. The
+ * handler function will be called whenever the that event is triggered for the
+ * socket. The handler function is responsible for clearing the event after
+ * having processed it in order to avoid eloop from calling the handler again
+ * for the same event.
+ */
+int eloop_register_sock(int sock, eloop_event_type type,
+			eloop_sock_handler handler,
+			void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_sock - Unregister handler for socket events
+ * @sock: File descriptor number for the socket
+ * @type: Type of event for which sock was registered
+ *
+ * Unregister a socket event notifier that was previously registered with
+ * eloop_register_sock().
+ */
+void eloop_unregister_sock(int sock, eloop_event_type type);
+
+/**
+ * eloop_register_event - Register handler for generic events
+ * @event: Event to wait (eloop implementation specific)
+ * @event_size: Size of event data
+ * @handler: Callback function to be called when event is triggered
+ * @eloop_data: Callback context data (eloop_data)
+ * @user_data: Callback context data (user_data)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register an event handler for the given event. This function is used to
+ * register eloop implementation specific events which are mainly targeted for
+ * operating system specific code (driver interface and l2_packet) since the
+ * portable code will not be able to use such an OS-specific call. The handler
+ * function will be called whenever the event is triggered. The handler
+ * function is responsible for clearing the event after having processed it in
+ * order to avoid eloop from calling the handler again for the same event.
+ *
+ * In case of Windows implementation (eloop_win.c), event pointer is of HANDLE
+ * type, i.e., void*. The callers are likely to have 'HANDLE h' type variable,
+ * and they would call this function with eloop_register_event(h, sizeof(h),
+ * ...).
+ */
+int eloop_register_event(void *event, size_t event_size,
+			 eloop_event_handler handler,
+			 void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_event - Unregister handler for a generic event
+ * @event: Event to cancel (eloop implementation specific)
+ * @event_size: Size of event data
+ *
+ * Unregister a generic event notifier that was previously registered with
+ * eloop_register_event().
+ */
+void eloop_unregister_event(void *event, size_t event_size);
+
+/**
+ * eloop_register_timeout - Register timeout
+ * @secs: Number of seconds to the timeout
+ * @usecs: Number of microseconds to the timeout
+ * @handler: Callback function to be called when timeout occurs
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a timeout that will cause the handler function to be called after
+ * given time.
+ */
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+			   eloop_timeout_handler handler,
+			   void *eloop_data, void *user_data);
+
+/**
+ * eloop_cancel_timeout - Cancel timeouts
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all
+ * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all
+ * Returns: Number of cancelled timeouts
+ *
+ * Cancel matching <handler,eloop_data,user_data> timeouts registered with
+ * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for
+ * cancelling all timeouts regardless of eloop_data/user_data.
+ */
+int eloop_cancel_timeout(eloop_timeout_handler handler,
+			 void *eloop_data, void *user_data);
+
+/**
+ * eloop_cancel_timeout_one - Cancel a single timeout
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * @remaining: Time left on the cancelled timer
+ * Returns: Number of cancelled timeouts
+ *
+ * Cancel matching <handler,eloop_data,user_data> timeout registered with
+ * eloop_register_timeout() and return the remaining time left.
+ */
+int eloop_cancel_timeout_one(eloop_timeout_handler handler,
+			     void *eloop_data, void *user_data,
+			     struct os_reltime *remaining);
+
+/**
+ * eloop_is_timeout_registered - Check if a timeout is already registered
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is registered, 0 if the timeout is not registered
+ *
+ * Determine if a matching <handler,eloop_data,user_data> timeout is registered
+ * with eloop_register_timeout().
+ */
+int eloop_is_timeout_registered(eloop_timeout_handler handler,
+				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
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a signal is received.
+ * The callback function is actually called only after the system signal
+ * handler has returned. This means that the normal limits for sighandlers
+ * (i.e., only "safe functions" allowed) do not apply for the registered
+ * callback.
+ */
+int eloop_register_signal(int sig, eloop_signal_handler handler,
+			  void *user_data);
+
+/**
+ * eloop_register_signal_terminate - Register handler for terminate signals
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a process termination
+ * signal is received. The callback function is actually called only after the
+ * system signal handler has returned. This means that the normal limits for
+ * sighandlers (i.e., only "safe functions" allowed) do not apply for the
+ * registered callback.
+ *
+ * This function is a more portable version of eloop_register_signal() since
+ * the knowledge of exact details of the signals is hidden in eloop
+ * implementation. In case of operating systems using signal(), this function
+ * registers handlers for SIGINT and SIGTERM.
+ */
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+				    void *user_data);
+
+/**
+ * eloop_register_signal_reconfig - Register handler for reconfig signals
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a reconfiguration /
+ * hangup signal is received. The callback function is actually called only
+ * after the system signal handler has returned. This means that the normal
+ * limits for sighandlers (i.e., only "safe functions" allowed) do not apply
+ * for the registered callback.
+ *
+ * This function is a more portable version of eloop_register_signal() since
+ * the knowledge of exact details of the signals is hidden in eloop
+ * implementation. In case of operating systems using signal(), this function
+ * registers a handler for SIGHUP.
+ */
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+				   void *user_data);
+
+/**
+ * eloop_run - Start the event loop
+ *
+ * Start the event loop and continue running as long as there are any
+ * registered event handlers. This function is run after event loop has been
+ * initialized with event_init() and one or more events have been registered.
+ */
+void eloop_run(void);
+
+/**
+ * eloop_terminate - Terminate event loop
+ *
+ * Terminate event loop even if there are registered events. This can be used
+ * to request the program to be terminated cleanly.
+ */
+void eloop_terminate(void);
+
+/**
+ * eloop_destroy - Free any resources allocated for the event loop
+ *
+ * After calling eloop_destroy(), other eloop_* functions must not be called
+ * before re-running eloop_init().
+ */
+void eloop_destroy(void);
+
+/**
+ * eloop_terminated - Check whether event loop has been terminated
+ * Returns: 1 = event loop terminate, 0 = event loop still running
+ *
+ * This function can be used to check whether eloop_terminate() has been called
+ * to request termination of the event loop. This is normally used to abort
+ * operations that may still be queued to be run when eloop_terminate() was
+ * called.
+ */
+int eloop_terminated(void);
+
+/**
+ * eloop_wait_for_read_sock - Wait for a single reader
+ * @sock: File descriptor number for the socket
+ *
+ * Do a blocking wait for a single read socket.
+ */
+void eloop_wait_for_read_sock(int sock);
+
+#endif /* ELOOP_H */
diff --git a/hostap/src/utils/eloop_win.c b/hostap/src/utils/eloop_win.c
new file mode 100644
index 0000000..de47fb2
--- /dev/null
+++ b/hostap/src/utils/eloop_win.c
@@ -0,0 +1,694 @@
+/*
+ * Event loop based on Windows events and WaitForMultipleObjects
+ * 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 <winsock2.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;
+	WSAEVENT event;
+};
+
+struct eloop_event {
+	void *eloop_data;
+	void *user_data;
+	eloop_event_handler handler;
+	HANDLE event;
+};
+
+struct eloop_timeout {
+	struct dl_list list;
+	struct os_reltime 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;
+	size_t reader_count;
+	struct eloop_sock *readers;
+
+	size_t event_count;
+	struct eloop_event *events;
+
+	struct dl_list timeout;
+
+	int signal_count;
+	struct eloop_signal *signals;
+	int signaled;
+	int pending_terminate;
+
+	int terminate;
+	int reader_table_changed;
+
+	struct eloop_signal term_signal;
+	HANDLE term_event;
+
+	HANDLE *handles;
+	size_t num_handles;
+};
+
+static struct eloop_data eloop;
+
+
+int eloop_init(void)
+{
+	os_memset(&eloop, 0, sizeof(eloop));
+	dl_list_init(&eloop.timeout);
+	eloop.num_handles = 1;
+	eloop.handles = os_malloc(eloop.num_handles *
+				  sizeof(eloop.handles[0]));
+	if (eloop.handles == NULL)
+		return -1;
+
+	eloop.term_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+	if (eloop.term_event == NULL) {
+		printf("CreateEvent() failed: %d\n",
+		       (int) GetLastError());
+		os_free(eloop.handles);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eloop_prepare_handles(void)
+{
+	HANDLE *n;
+
+	if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8)
+		return 0;
+	n = os_realloc_array(eloop.handles, eloop.num_handles * 2,
+			     sizeof(eloop.handles[0]));
+	if (n == NULL)
+		return -1;
+	eloop.handles = n;
+	eloop.num_handles *= 2;
+	return 0;
+}
+
+
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+			     void *eloop_data, void *user_data)
+{
+	WSAEVENT event;
+	struct eloop_sock *tmp;
+
+	if (eloop_prepare_handles())
+		return -1;
+
+	event = WSACreateEvent();
+	if (event == WSA_INVALID_EVENT) {
+		printf("WSACreateEvent() failed: %d\n", WSAGetLastError());
+		return -1;
+	}
+
+	if (WSAEventSelect(sock, event, FD_READ)) {
+		printf("WSAEventSelect() failed: %d\n", WSAGetLastError());
+		WSACloseEvent(event);
+		return -1;
+	}
+	tmp = os_realloc_array(eloop.readers, eloop.reader_count + 1,
+			       sizeof(struct eloop_sock));
+	if (tmp == NULL) {
+		WSAEventSelect(sock, event, 0);
+		WSACloseEvent(event);
+		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;
+	tmp[eloop.reader_count].event = event;
+	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)
+{
+	size_t 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;
+
+	WSAEventSelect(eloop.readers[i].sock, eloop.readers[i].event, 0);
+	WSACloseEvent(eloop.readers[i].event);
+
+	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_event(void *event, size_t event_size,
+			 eloop_event_handler handler,
+			 void *eloop_data, void *user_data)
+{
+	struct eloop_event *tmp;
+	HANDLE h = event;
+
+	if (event_size != sizeof(HANDLE) || h == INVALID_HANDLE_VALUE)
+		return -1;
+
+	if (eloop_prepare_handles())
+		return -1;
+
+	tmp = os_realloc_array(eloop.events, eloop.event_count + 1,
+			       sizeof(struct eloop_event));
+	if (tmp == NULL)
+		return -1;
+
+	tmp[eloop.event_count].eloop_data = eloop_data;
+	tmp[eloop.event_count].user_data = user_data;
+	tmp[eloop.event_count].handler = handler;
+	tmp[eloop.event_count].event = h;
+	eloop.event_count++;
+	eloop.events = tmp;
+
+	return 0;
+}
+
+
+void eloop_unregister_event(void *event, size_t event_size)
+{
+	size_t i;
+	HANDLE h = event;
+
+	if (eloop.events == NULL || eloop.event_count == 0 ||
+	    event_size != sizeof(HANDLE))
+		return;
+
+	for (i = 0; i < eloop.event_count; i++) {
+		if (eloop.events[i].event == h)
+			break;
+	}
+	if (i == eloop.event_count)
+		return;
+
+	if (i != eloop.event_count - 1) {
+		os_memmove(&eloop.events[i], &eloop.events[i + 1],
+			   (eloop.event_count - i - 1) *
+			   sizeof(struct eloop_event));
+	}
+	eloop.event_count--;
+}
+
+
+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_reltime(&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_reltime_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_reltime *remaining)
+{
+	struct eloop_timeout *timeout, *prev;
+	int removed = 0;
+	struct os_reltime now;
+
+	os_get_reltime(&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_reltime_before(&now, &timeout->time))
+				os_reltime_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;
+}
+
+
+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)
+{
+	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);
+		}
+	}
+
+	if (eloop.term_signal.signaled) {
+		eloop.term_signal.signaled = 0;
+		eloop.term_signal.handler(eloop.term_signal.sig,
+					  eloop.term_signal.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;
+}
+
+
+#ifndef _WIN32_WCE
+static BOOL eloop_handle_console_ctrl(DWORD type)
+{
+	switch (type) {
+	case CTRL_C_EVENT:
+	case CTRL_BREAK_EVENT:
+		eloop.signaled++;
+		eloop.term_signal.signaled++;
+		SetEvent(eloop.term_event);
+		return TRUE;
+	default:
+		return FALSE;
+	}
+}
+#endif /* _WIN32_WCE */
+
+
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+				    void *user_data)
+{
+#ifndef _WIN32_WCE
+	if (SetConsoleCtrlHandler((PHANDLER_ROUTINE) eloop_handle_console_ctrl,
+				  TRUE) == 0) {
+		printf("SetConsoleCtrlHandler() failed: %d\n",
+		       (int) GetLastError());
+		return -1;
+	}
+#endif /* _WIN32_WCE */
+
+	eloop.term_signal.handler = handler;
+	eloop.term_signal.user_data = user_data;
+		
+	return 0;
+}
+
+
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+				   void *user_data)
+{
+	/* TODO */
+	return 0;
+}
+
+
+void eloop_run(void)
+{
+	struct os_reltime tv, now;
+	DWORD count, ret, timeout_val, err;
+	size_t i;
+
+	while (!eloop.terminate &&
+	       (!dl_list_empty(&eloop.timeout) || eloop.reader_count > 0 ||
+		eloop.event_count > 0)) {
+		struct eloop_timeout *timeout;
+		tv.sec = tv.usec = 0;
+		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+					list);
+		if (timeout) {
+			os_get_reltime(&now);
+			if (os_reltime_before(&now, &timeout->time))
+				os_reltime_sub(&timeout->time, &now, &tv);
+		}
+
+		count = 0;
+		for (i = 0; i < eloop.event_count; i++)
+			eloop.handles[count++] = eloop.events[i].event;
+
+		for (i = 0; i < eloop.reader_count; i++)
+			eloop.handles[count++] = eloop.readers[i].event;
+
+		if (eloop.term_event)
+			eloop.handles[count++] = eloop.term_event;
+
+		if (timeout)
+			timeout_val = tv.sec * 1000 + tv.usec / 1000;
+		else
+			timeout_val = INFINITE;
+
+		if (count > MAXIMUM_WAIT_OBJECTS) {
+			printf("WaitForMultipleObjects: Too many events: "
+			       "%d > %d (ignoring extra events)\n",
+			       (int) count, MAXIMUM_WAIT_OBJECTS);
+			count = MAXIMUM_WAIT_OBJECTS;
+		}
+#ifdef _WIN32_WCE
+		ret = WaitForMultipleObjects(count, eloop.handles, FALSE,
+					     timeout_val);
+#else /* _WIN32_WCE */
+		ret = WaitForMultipleObjectsEx(count, eloop.handles, FALSE,
+					       timeout_val, TRUE);
+#endif /* _WIN32_WCE */
+		err = GetLastError();
+
+		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_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 =
+					timeout->handler;
+				eloop_remove_timeout(timeout);
+				handler(eloop_data, user_data);
+			}
+
+		}
+
+		if (ret == WAIT_FAILED) {
+			printf("WaitForMultipleObjects(count=%d) failed: %d\n",
+			       (int) count, (int) err);
+			os_sleep(1, 0);
+			continue;
+		}
+
+#ifndef _WIN32_WCE
+		if (ret == WAIT_IO_COMPLETION)
+			continue;
+#endif /* _WIN32_WCE */
+
+		if (ret == WAIT_TIMEOUT)
+			continue;
+
+		while (ret >= WAIT_OBJECT_0 &&
+		       ret < WAIT_OBJECT_0 + eloop.event_count) {
+			eloop.events[ret].handler(
+				eloop.events[ret].eloop_data,
+				eloop.events[ret].user_data);
+			ret = WaitForMultipleObjects(eloop.event_count,
+						     eloop.handles, FALSE, 0);
+		}
+
+		eloop.reader_table_changed = 0;
+		for (i = 0; i < eloop.reader_count; i++) {
+			WSANETWORKEVENTS events;
+			if (WSAEnumNetworkEvents(eloop.readers[i].sock,
+						 eloop.readers[i].event,
+						 &events) == 0 &&
+			    (events.lNetworkEvents & FD_READ)) {
+				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;
+	SetEvent(eloop.term_event);
+}
+
+
+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);
+	if (eloop.term_event)
+		CloseHandle(eloop.term_event);
+	os_free(eloop.handles);
+	eloop.handles = NULL;
+	os_free(eloop.events);
+	eloop.events = NULL;
+}
+
+
+int eloop_terminated(void)
+{
+	return eloop.terminate;
+}
+
+
+void eloop_wait_for_read_sock(int sock)
+{
+	WSAEVENT event;
+
+	event = WSACreateEvent();
+	if (event == WSA_INVALID_EVENT) {
+		printf("WSACreateEvent() failed: %d\n", WSAGetLastError());
+		return;
+	}
+
+	if (WSAEventSelect(sock, event, FD_READ)) {
+		printf("WSAEventSelect() failed: %d\n", WSAGetLastError());
+		WSACloseEvent(event);
+		return ;
+	}
+
+	WaitForSingleObject(event, INFINITE);
+	WSAEventSelect(sock, event, 0);
+	WSACloseEvent(event);
+}
diff --git a/hostap/src/utils/ext_password.c b/hostap/src/utils/ext_password.c
new file mode 100644
index 0000000..0613119
--- /dev/null
+++ b/hostap/src/utils/ext_password.c
@@ -0,0 +1,116 @@
+/*
+ * External password backend
+ * Copyright (c) 2012, 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"
+
+#ifdef __linux__
+#include <sys/mman.h>
+#endif /* __linux__ */
+
+#include "common.h"
+#include "ext_password_i.h"
+
+
+#ifdef CONFIG_EXT_PASSWORD_TEST
+extern struct ext_password_backend ext_password_test;
+#endif /* CONFIG_EXT_PASSWORD_TEST */
+
+static const struct ext_password_backend *backends[] = {
+#ifdef CONFIG_EXT_PASSWORD_TEST
+	&ext_password_test,
+#endif /* CONFIG_EXT_PASSWORD_TEST */
+	NULL
+};
+
+struct ext_password_data {
+	const struct ext_password_backend *backend;
+	void *priv;
+};
+
+
+struct ext_password_data * ext_password_init(const char *backend,
+					     const char *params)
+{
+	struct ext_password_data *data;
+	int i;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	for (i = 0; backends[i]; i++) {
+		if (os_strcmp(backends[i]->name, backend) == 0) {
+			data->backend = backends[i];
+			break;
+		}
+	}
+
+	if (!data->backend) {
+		os_free(data);
+		return NULL;
+	}
+
+	data->priv = data->backend->init(params);
+	if (data->priv == NULL) {
+		os_free(data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+void ext_password_deinit(struct ext_password_data *data)
+{
+	if (data && data->backend && data->priv)
+		data->backend->deinit(data->priv);
+	os_free(data);
+}
+
+
+struct wpabuf * ext_password_get(struct ext_password_data *data,
+				 const char *name)
+{
+	if (data == NULL)
+		return NULL;
+	return data->backend->get(data->priv, name);
+}
+
+
+struct wpabuf * ext_password_alloc(size_t len)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(len);
+	if (buf == NULL)
+		return NULL;
+
+#ifdef __linux__
+	if (mlock(wpabuf_head(buf), wpabuf_len(buf)) < 0) {
+		wpa_printf(MSG_ERROR, "EXT PW: mlock failed: %s",
+			   strerror(errno));
+	}
+#endif /* __linux__ */
+
+	return buf;
+}
+
+
+void ext_password_free(struct wpabuf *pw)
+{
+	if (pw == NULL)
+		return;
+	os_memset(wpabuf_mhead(pw), 0, wpabuf_len(pw));
+#ifdef __linux__
+	if (munlock(wpabuf_head(pw), wpabuf_len(pw)) < 0) {
+		wpa_printf(MSG_ERROR, "EXT PW: munlock failed: %s",
+			   strerror(errno));
+	}
+#endif /* __linux__ */
+	wpabuf_free(pw);
+}
diff --git a/hostap/src/utils/ext_password.h b/hostap/src/utils/ext_password.h
new file mode 100644
index 0000000..e3e46ea
--- /dev/null
+++ b/hostap/src/utils/ext_password.h
@@ -0,0 +1,33 @@
+/*
+ * External password backend
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EXT_PASSWORD_H
+#define EXT_PASSWORD_H
+
+struct ext_password_data;
+
+#ifdef CONFIG_EXT_PASSWORD
+
+struct ext_password_data * ext_password_init(const char *backend,
+					     const char *params);
+void ext_password_deinit(struct ext_password_data *data);
+
+struct wpabuf * ext_password_get(struct ext_password_data *data,
+				 const char *name);
+void ext_password_free(struct wpabuf *pw);
+
+#else /* CONFIG_EXT_PASSWORD */
+
+#define ext_password_init(b, p) ((void *) 1)
+#define ext_password_deinit(d) do { } while (0)
+#define ext_password_get(d, n) (NULL)
+#define ext_password_free(p) do { } while (0)
+
+#endif /* CONFIG_EXT_PASSWORD */
+
+#endif /* EXT_PASSWORD_H */
diff --git a/hostap/src/utils/ext_password_i.h b/hostap/src/utils/ext_password_i.h
new file mode 100644
index 0000000..043e731
--- /dev/null
+++ b/hostap/src/utils/ext_password_i.h
@@ -0,0 +1,23 @@
+/*
+ * External password backend - internal definitions
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EXT_PASSWORD_I_H
+#define EXT_PASSWORD_I_H
+
+#include "ext_password.h"
+
+struct ext_password_backend {
+	const char *name;
+	void * (*init)(const char *params);
+	void (*deinit)(void *ctx);
+	struct wpabuf * (*get)(void *ctx, const char *name);
+};
+
+struct wpabuf * ext_password_alloc(size_t len);
+
+#endif /* EXT_PASSWORD_I_H */
diff --git a/hostap/src/utils/ext_password_test.c b/hostap/src/utils/ext_password_test.c
new file mode 100644
index 0000000..b3a4552
--- /dev/null
+++ b/hostap/src/utils/ext_password_test.c
@@ -0,0 +1,90 @@
+/*
+ * External password backend
+ * Copyright (c) 2012, 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 "ext_password_i.h"
+
+
+struct ext_password_test_data {
+	char *params;
+};
+
+
+static void * ext_password_test_init(const char *params)
+{
+	struct ext_password_test_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	if (params)
+		data->params = os_strdup(params);
+
+	return data;
+}
+
+
+static void ext_password_test_deinit(void *ctx)
+{
+	struct ext_password_test_data *data = ctx;
+
+	str_clear_free(data->params);
+	os_free(data);
+}
+
+
+static struct wpabuf * ext_password_test_get(void *ctx, const char *name)
+{
+	struct ext_password_test_data *data = ctx;
+	char *pos, *pos2;
+	size_t nlen;
+
+	wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s)", name);
+
+	pos = data->params;
+	if (pos == NULL)
+		return NULL;
+	nlen = os_strlen(name);
+
+	while (pos && *pos) {
+		if (os_strncmp(pos, name, nlen) == 0 && pos[nlen] == '=') {
+			struct wpabuf *buf;
+			pos += nlen + 1;
+			pos2 = pos;
+			while (*pos2 != '|' && *pos2 != '\0')
+				pos2++;
+			buf = ext_password_alloc(pos2 - pos);
+			if (buf == NULL)
+				return NULL;
+			wpabuf_put_data(buf, pos, pos2 - pos);
+			wpa_hexdump_ascii_key(MSG_DEBUG, "EXT PW TEST: value",
+					      wpabuf_head(buf),
+					      wpabuf_len(buf));
+			return buf;
+		}
+
+		pos = os_strchr(pos + 1, '|');
+		if (pos)
+			pos++;
+	}
+
+	wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s) - not found", name);
+
+	return NULL;
+}
+
+
+const struct ext_password_backend ext_password_test = {
+	.name = "test",
+	.init = ext_password_test_init,
+	.deinit = ext_password_test_deinit,
+	.get = ext_password_test_get,
+};
diff --git a/hostap/src/utils/http-utils.h b/hostap/src/utils/http-utils.h
new file mode 100644
index 0000000..8d4399a
--- /dev/null
+++ b/hostap/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/hostap/src/utils/http_curl.c b/hostap/src/utils/http_curl.c
new file mode 100644
index 0000000..653eb54
--- /dev/null
+++ b/hostap/src/utils/http_curl.c
@@ -0,0 +1,1649 @@
+/*
+ * 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) {
+		wpa_printf(MSG_DEBUG, "%s: no cert_cb configured", __func__);
+		return 0;
+	}
+
+	if (0) {
+		BIO *out;
+		out = BIO_new_fp(stdout, BIO_NOCLOSE);
+		X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+		BIO_free(out);
+	}
+
+	parse_cert(ctx, &hcert, cert, &names);
+	ret = ctx->cert_cb(ctx->cert_cb_ctx, &hcert);
+	parse_cert_free(&hcert, names);
+
+	return ret;
+}
+
+
+void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname)
+{
+	BIO *in, *out;
+	X509 *cert;
+	GENERAL_NAMES *names;
+	struct http_cert hcert;
+	unsigned int i;
+
+	in = BIO_new_file(fname, "r");
+	if (in == NULL) {
+		wpa_printf(MSG_ERROR, "Could not read '%s'", fname);
+		return;
+	}
+
+	cert = d2i_X509_bio(in, NULL);
+	BIO_free(in);
+
+	if (cert == NULL) {
+		wpa_printf(MSG_ERROR, "Could not parse certificate");
+		return;
+	}
+
+	out = BIO_new_fp(stdout, BIO_NOCLOSE);
+	if (out) {
+		X509_print_ex(out, cert, XN_FLAG_COMPAT,
+			      X509_FLAG_COMPAT);
+		BIO_free(out);
+	}
+
+	wpa_printf(MSG_INFO, "Additional parsing information:");
+	parse_cert(ctx, &hcert, cert, &names);
+	for (i = 0; i < hcert.num_othername; i++) {
+		if (os_strcmp(hcert.othername[i].oid,
+			      "1.3.6.1.4.1.40808.1.1.1") == 0) {
+			char *name = os_zalloc(hcert.othername[i].len + 1);
+			if (name) {
+				os_memcpy(name, hcert.othername[i].data,
+					  hcert.othername[i].len);
+				wpa_printf(MSG_INFO,
+					   "id-wfa-hotspot-friendlyName: %s",
+					   name);
+				os_free(name);
+			}
+			wpa_hexdump_ascii(MSG_INFO,
+					  "id-wfa-hotspot-friendlyName",
+					  hcert.othername[i].data,
+					  hcert.othername[i].len);
+		} else {
+			wpa_printf(MSG_INFO, "subjAltName[othername]: oid=%s",
+				   hcert.othername[i].oid);
+			wpa_hexdump_ascii(MSG_INFO, "unknown othername",
+					  hcert.othername[i].data,
+					  hcert.othername[i].len);
+		}
+	}
+	parse_cert_free(&hcert, names);
+
+	X509_free(cert);
+}
+
+
+static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+	struct http_ctx *ctx;
+	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, preverify_ok: %d",
+		   preverify_ok);
+
+	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");
+		}
+		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");
+				X509_free(cert);
+				sk_X509_free(certs);
+				certs = NULL;
+			}
+			if (certs && ctx->peer_issuer_issuer) {
+				cert = X509_dup(ctx->peer_issuer_issuer);
+				if (cert && !sk_X509_push(certs, cert)) {
+					tls_show_errors(
+						__func__,
+						"OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
+					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;
+#ifdef EAP_TLS_OPENSSL
+	const char *extra = " tls=openssl";
+#else /* EAP_TLS_OPENSSL */
+	const char *extra = "";
+#endif /* EAP_TLS_OPENSSL */
+
+	wpa_printf(MSG_DEBUG, "Start HTTP client: address=%s ca_fname=%s "
+		   "username=%s%s", address, ca_fname, username, extra);
+
+	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/hostap/src/utils/includes.h b/hostap/src/utils/includes.h
new file mode 100644
index 0000000..75513fc
--- /dev/null
+++ b/hostap/src/utils/includes.h
@@ -0,0 +1,45 @@
+/*
+ * wpa_supplicant/hostapd - Default include files
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This header file is included into all C files so that commonly used header
+ * files can be selected with OS specific ifdef blocks in one place instead of
+ * having to have OS/C library specific selection in many files.
+ */
+
+#ifndef INCLUDES_H
+#define INCLUDES_H
+
+/* Include possible build time configuration before including anything else */
+#include "build_config.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#ifndef _WIN32_WCE
+#include <signal.h>
+#include <sys/types.h>
+#include <errno.h>
+#endif /* _WIN32_WCE */
+#include <ctype.h>
+
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif /* _MSC_VER */
+
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifndef __vxworks
+#include <sys/uio.h>
+#include <sys/time.h>
+#endif /* __vxworks */
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#endif /* INCLUDES_H */
diff --git a/hostap/src/utils/ip_addr.c b/hostap/src/utils/ip_addr.c
new file mode 100644
index 0000000..92a3590
--- /dev/null
+++ b/hostap/src/utils/ip_addr.c
@@ -0,0 +1,53 @@
+/*
+ * IP address processing
+ * Copyright (c) 2003-2006, 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 "ip_addr.h"
+
+const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
+			    size_t buflen)
+{
+	if (buflen == 0 || addr == NULL)
+		return NULL;
+
+	if (addr->af == AF_INET) {
+		os_strlcpy(buf, inet_ntoa(addr->u.v4), buflen);
+	} else {
+		buf[0] = '\0';
+	}
+#ifdef CONFIG_IPV6
+	if (addr->af == AF_INET6) {
+		if (inet_ntop(AF_INET6, &addr->u.v6, buf, buflen) == NULL)
+			buf[0] = '\0';
+	}
+#endif /* CONFIG_IPV6 */
+
+	return buf;
+}
+
+
+int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr)
+{
+#ifndef CONFIG_NATIVE_WINDOWS
+	if (inet_aton(txt, &addr->u.v4)) {
+		addr->af = AF_INET;
+		return 0;
+	}
+
+#ifdef CONFIG_IPV6
+	if (inet_pton(AF_INET6, txt, &addr->u.v6) > 0) {
+		addr->af = AF_INET6;
+		return 0;
+	}
+#endif /* CONFIG_IPV6 */
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	return -1;
+}
diff --git a/hostap/src/utils/ip_addr.h b/hostap/src/utils/ip_addr.h
new file mode 100644
index 0000000..0670411
--- /dev/null
+++ b/hostap/src/utils/ip_addr.h
@@ -0,0 +1,27 @@
+/*
+ * IP address processing
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IP_ADDR_H
+#define IP_ADDR_H
+
+struct hostapd_ip_addr {
+	int af; /* AF_INET / AF_INET6 */
+	union {
+		struct in_addr v4;
+#ifdef CONFIG_IPV6
+		struct in6_addr v6;
+#endif /* CONFIG_IPV6 */
+		u8 max_len[16];
+	} u;
+};
+
+const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
+			    size_t buflen);
+int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr);
+
+#endif /* IP_ADDR_H */
diff --git a/hostap/src/utils/list.h b/hostap/src/utils/list.h
new file mode 100644
index 0000000..ee2f485
--- /dev/null
+++ b/hostap/src/utils/list.h
@@ -0,0 +1,97 @@
+/*
+ * Doubly-linked list
+ * 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.
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+/**
+ * struct dl_list - Doubly-linked list
+ */
+struct dl_list {
+	struct dl_list *next;
+	struct dl_list *prev;
+};
+
+#define DL_LIST_HEAD_INIT(l) { &(l), &(l) }
+
+static inline void dl_list_init(struct dl_list *list)
+{
+	list->next = list;
+	list->prev = list;
+}
+
+static inline void dl_list_add(struct dl_list *list, struct dl_list *item)
+{
+	item->next = list->next;
+	item->prev = list;
+	list->next->prev = item;
+	list->next = item;
+}
+
+static inline void dl_list_add_tail(struct dl_list *list, struct dl_list *item)
+{
+	dl_list_add(list->prev, item);
+}
+
+static inline void dl_list_del(struct dl_list *item)
+{
+	item->next->prev = item->prev;
+	item->prev->next = item->next;
+	item->next = NULL;
+	item->prev = NULL;
+}
+
+static inline int dl_list_empty(struct dl_list *list)
+{
+	return list->next == list;
+}
+
+static inline unsigned int dl_list_len(struct dl_list *list)
+{
+	struct dl_list *item;
+	int count = 0;
+	for (item = list->next; item != list; item = item->next)
+		count++;
+	return count;
+}
+
+#ifndef offsetof
+#define offsetof(type, member) ((long) &((type *) 0)->member)
+#endif
+
+#define dl_list_entry(item, type, member) \
+	((type *) ((char *) item - offsetof(type, member)))
+
+#define dl_list_first(list, type, member) \
+	(dl_list_empty((list)) ? NULL : \
+	 dl_list_entry((list)->next, type, member))
+
+#define dl_list_last(list, type, member) \
+	(dl_list_empty((list)) ? NULL : \
+	 dl_list_entry((list)->prev, type, member))
+
+#define dl_list_for_each(item, list, type, member) \
+	for (item = dl_list_entry((list)->next, type, member); \
+	     &item->member != (list); \
+	     item = dl_list_entry(item->member.next, type, member))
+
+#define dl_list_for_each_safe(item, n, list, type, member) \
+	for (item = dl_list_entry((list)->next, type, member), \
+		     n = dl_list_entry(item->member.next, type, member); \
+	     &item->member != (list); \
+	     item = n, n = dl_list_entry(n->member.next, type, member))
+
+#define dl_list_for_each_reverse(item, list, type, member) \
+	for (item = dl_list_entry((list)->prev, type, member); \
+	     &item->member != (list); \
+	     item = dl_list_entry(item->member.prev, type, member))
+
+#define DEFINE_DL_LIST(name) \
+	struct dl_list name = { &(name), &(name) }
+
+#endif /* LIST_H */
diff --git a/hostap/src/utils/os.h b/hostap/src/utils/os.h
new file mode 100644
index 0000000..9e496fb
--- /dev/null
+++ b/hostap/src/utils/os.h
@@ -0,0 +1,664 @@
+/*
+ * OS specific functions
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef OS_H
+#define OS_H
+
+typedef long os_time_t;
+
+/**
+ * os_sleep - Sleep (sec, usec)
+ * @sec: Number of seconds to sleep
+ * @usec: Number of microseconds to sleep
+ */
+void os_sleep(os_time_t sec, os_time_t usec);
+
+struct os_time {
+	os_time_t sec;
+	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
+ * Returns: 0 on success, -1 on failure
+ */
+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);
+
+
+/* Helpers for handling struct os_time */
+
+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
+ * @year: Four digit year
+ * @month: Month (1 .. 12)
+ * @day: Day of month (1 .. 31)
+ * @hour: Hour (0 .. 23)
+ * @min: Minute (0 .. 59)
+ * @sec: Second (0 .. 60)
+ * @t: Buffer for returning calendar time representation (seconds since
+ * 1970-01-01 00:00:00)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: The result is in seconds from Epoch, i.e., in UTC, not in local time
+ * which is used by POSIX mktime().
+ */
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+	      os_time_t *t);
+
+struct os_tm {
+	int sec; /* 0..59 or 60 for leap seconds */
+	int min; /* 0..59 */
+	int hour; /* 0..23 */
+	int day; /* 1..31 */
+	int month; /* 1..12 */
+	int year; /* Four digit year */
+};
+
+int os_gmtime(os_time_t t, struct os_tm *tm);
+
+/**
+ * os_daemonize - Run in the background (detach from the controlling terminal)
+ * @pid_file: File name to write the process ID to or %NULL to skip this
+ * Returns: 0 on success, -1 on failure
+ */
+int os_daemonize(const char *pid_file);
+
+/**
+ * os_daemonize_terminate - Stop running in the background (remove pid file)
+ * @pid_file: File name to write the process ID to or %NULL to skip this
+ */
+void os_daemonize_terminate(const char *pid_file);
+
+/**
+ * os_get_random - Get cryptographically strong pseudo random data
+ * @buf: Buffer for pseudo random data
+ * @len: Length of the buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_random(unsigned char *buf, size_t len);
+
+/**
+ * os_random - Get pseudo random value (not necessarily very strong)
+ * Returns: Pseudo random value
+ */
+unsigned long os_random(void);
+
+/**
+ * os_rel2abs_path - Get an absolute path for a file
+ * @rel_path: Relative path to a file
+ * Returns: Absolute path for the file or %NULL on failure
+ *
+ * This function tries to convert a relative path of a file to an absolute path
+ * in order for the file to be found even if current working directory has
+ * changed. The returned value is allocated and caller is responsible for
+ * freeing it. It is acceptable to just return the same path in an allocated
+ * buffer, e.g., return strdup(rel_path). This function is only used to find
+ * configuration files when os_daemonize() may have changed the current working
+ * directory and relative path would be pointing to a different location.
+ */
+char * os_rel2abs_path(const char *rel_path);
+
+/**
+ * os_program_init - Program initialization (called at start)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when a programs starts. If there are any OS specific
+ * processing that is needed, it can be placed here. It is also acceptable to
+ * just return 0 if not special processing is needed.
+ */
+int os_program_init(void);
+
+/**
+ * os_program_deinit - Program deinitialization (called just before exit)
+ *
+ * This function is called just before a program exists. If there are any OS
+ * specific processing, e.g., freeing resourced allocated in os_program_init(),
+ * it should be done here. It is also acceptable for this function to do
+ * nothing.
+ */
+void os_program_deinit(void);
+
+/**
+ * os_setenv - Set environment variable
+ * @name: Name of the variable
+ * @value: Value to set to the variable
+ * @overwrite: Whether existing variable should be overwritten
+ * Returns: 0 on success, -1 on error
+ *
+ * This function is only used for wpa_cli action scripts. OS wrapper does not
+ * need to implement this if such functionality is not needed.
+ */
+int os_setenv(const char *name, const char *value, int overwrite);
+
+/**
+ * os_unsetenv - Delete environent variable
+ * @name: Name of the variable
+ * Returns: 0 on success, -1 on error
+ *
+ * This function is only used for wpa_cli action scripts. OS wrapper does not
+ * need to implement this if such functionality is not needed.
+ */
+int os_unsetenv(const char *name);
+
+/**
+ * os_readfile - Read a file to an allocated memory buffer
+ * @name: Name of the file to read
+ * @len: For returning the length of the allocated buffer
+ * Returns: Pointer to the allocated buffer or %NULL on failure
+ *
+ * This function allocates memory and reads the given file to this buffer. Both
+ * binary and text files can be read with this function. The caller is
+ * responsible for freeing the returned buffer with os_free().
+ */
+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_fdatasync - Sync a file's (for a given stream) state with storage device
+ * @stream: the stream to be flushed
+ * Returns: 0 if the operation succeeded or -1 on failure
+ */
+int os_fdatasync(FILE *stream);
+
+/**
+ * os_zalloc - Allocate and zero memory
+ * @size: Number of bytes to allocate
+ * Returns: Pointer to allocated and zeroed memory or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+void * os_zalloc(size_t size);
+
+/**
+ * os_calloc - Allocate and zero memory for an array
+ * @nmemb: Number of members in the array
+ * @size: Number of bytes in each member
+ * Returns: Pointer to allocated and zeroed memory or %NULL on failure
+ *
+ * This function can be used as a wrapper for os_zalloc(nmemb * size) when an
+ * allocation is used for an array. The main benefit over os_zalloc() is in
+ * having an extra check to catch integer overflows in multiplication.
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+static inline void * os_calloc(size_t nmemb, size_t size)
+{
+	if (size && nmemb > (~(size_t) 0) / size)
+		return NULL;
+	return os_zalloc(nmemb * size);
+}
+
+
+/*
+ * The following functions are wrapper for standard ANSI C or POSIX functions.
+ * By default, they are just defined to use the standard function name and no
+ * os_*.c implementation is needed for them. This avoids extra function calls
+ * by allowing the C pre-processor take care of the function name mapping.
+ *
+ * If the target system uses a C library that does not provide these functions,
+ * build_config.h can be used to define the wrappers to use a different
+ * function name. This can be done on function-by-function basis since the
+ * defines here are only used if build_config.h does not define the os_* name.
+ * If needed, os_*.c file can be used to implement the functions that are not
+ * included in the C library on the target system. Alternatively,
+ * OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case
+ * these functions need to be implemented in os_*.c file for the target system.
+ */
+
+#ifdef OS_NO_C_LIB_DEFINES
+
+/**
+ * os_malloc - Allocate dynamic memory
+ * @size: Size of the buffer to allocate
+ * Returns: Allocated buffer or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+void * os_malloc(size_t size);
+
+/**
+ * os_realloc - Re-allocate dynamic memory
+ * @ptr: Old buffer from os_malloc() or os_realloc()
+ * @size: Size of the new buffer
+ * Returns: Allocated buffer or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ * If re-allocation fails, %NULL is returned and the original buffer (ptr) is
+ * not freed and caller is still responsible for freeing it.
+ */
+void * os_realloc(void *ptr, size_t size);
+
+/**
+ * os_free - Free dynamic memory
+ * @ptr: Old buffer from os_malloc() or os_realloc(); can be %NULL
+ */
+void os_free(void *ptr);
+
+/**
+ * os_memcpy - Copy memory area
+ * @dest: Destination
+ * @src: Source
+ * @n: Number of bytes to copy
+ * Returns: dest
+ *
+ * The memory areas src and dst must not overlap. os_memmove() can be used with
+ * overlapping memory.
+ */
+void * os_memcpy(void *dest, const void *src, size_t n);
+
+/**
+ * os_memmove - Copy memory area
+ * @dest: Destination
+ * @src: Source
+ * @n: Number of bytes to copy
+ * Returns: dest
+ *
+ * The memory areas src and dst may overlap.
+ */
+void * os_memmove(void *dest, const void *src, size_t n);
+
+/**
+ * os_memset - Fill memory with a constant byte
+ * @s: Memory area to be filled
+ * @c: Constant byte
+ * @n: Number of bytes started from s to fill with c
+ * Returns: s
+ */
+void * os_memset(void *s, int c, size_t n);
+
+/**
+ * os_memcmp - Compare memory areas
+ * @s1: First buffer
+ * @s2: Second buffer
+ * @n: Maximum numbers of octets to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_memcmp(const void *s1, const void *s2, size_t n);
+
+/**
+ * os_strdup - Duplicate a string
+ * @s: Source string
+ * Returns: Allocated buffer with the string copied into it or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+char * os_strdup(const char *s);
+
+/**
+ * os_strlen - Calculate the length of a string
+ * @s: '\0' terminated string
+ * Returns: Number of characters in s (not counting the '\0' terminator)
+ */
+size_t os_strlen(const char *s);
+
+/**
+ * os_strcasecmp - Compare two strings ignoring case
+ * @s1: First string
+ * @s2: Second string
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greatred than s2
+ */
+int os_strcasecmp(const char *s1, const char *s2);
+
+/**
+ * os_strncasecmp - Compare two strings ignoring case
+ * @s1: First string
+ * @s2: Second string
+ * @n: Maximum numbers of characters to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_strncasecmp(const char *s1, const char *s2, size_t n);
+
+/**
+ * os_strchr - Locate the first occurrence of a character in string
+ * @s: String
+ * @c: Character to search for
+ * Returns: Pointer to the matched character or %NULL if not found
+ */
+char * os_strchr(const char *s, int c);
+
+/**
+ * os_strrchr - Locate the last occurrence of a character in string
+ * @s: String
+ * @c: Character to search for
+ * Returns: Pointer to the matched character or %NULL if not found
+ */
+char * os_strrchr(const char *s, int c);
+
+/**
+ * os_strcmp - Compare two strings
+ * @s1: First string
+ * @s2: Second string
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greatred than s2
+ */
+int os_strcmp(const char *s1, const char *s2);
+
+/**
+ * os_strncmp - Compare two strings
+ * @s1: First string
+ * @s2: Second string
+ * @n: Maximum numbers of characters to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_strncmp(const char *s1, const char *s2, size_t n);
+
+/**
+ * os_strstr - Locate a substring
+ * @haystack: String (haystack) to search from
+ * @needle: Needle to search from haystack
+ * Returns: Pointer to the beginning of the substring or %NULL if not found
+ */
+char * os_strstr(const char *haystack, const char *needle);
+
+/**
+ * os_snprintf - Print to a memory buffer
+ * @str: Memory buffer to print into
+ * @size: Maximum length of the str buffer
+ * @format: printf format
+ * Returns: Number of characters printed (not including trailing '\0').
+ *
+ * If the output buffer is truncated, number of characters which would have
+ * been written is returned. Since some C libraries return -1 in such a case,
+ * the caller must be prepared on that value, too, to indicate truncation.
+ *
+ * Note: Some C library implementations of snprintf() may not guarantee null
+ * termination in case the output is truncated. The OS wrapper function of
+ * os_snprintf() should provide this guarantee, i.e., to null terminate the
+ * output buffer if a C library version of the function is used and if that
+ * function does not guarantee null termination.
+ *
+ * If the target system does not include snprintf(), see, e.g.,
+ * http://www.ijs.si/software/snprintf/ for an example of a portable
+ * implementation of snprintf.
+ */
+int os_snprintf(char *str, size_t size, const char *format, ...);
+
+#else /* OS_NO_C_LIB_DEFINES */
+
+#ifdef WPA_TRACE
+void * os_malloc(size_t size);
+void * os_realloc(void *ptr, size_t size);
+void os_free(void *ptr);
+char * os_strdup(const char *s);
+#else /* WPA_TRACE */
+#ifndef os_malloc
+#define os_malloc(s) malloc((s))
+#endif
+#ifndef os_realloc
+#define os_realloc(p, s) realloc((p), (s))
+#endif
+#ifndef os_free
+#define os_free(p) free((p))
+#endif
+#ifndef os_strdup
+#ifdef _MSC_VER
+#define os_strdup(s) _strdup(s)
+#else
+#define os_strdup(s) strdup(s)
+#endif
+#endif
+#endif /* WPA_TRACE */
+
+#ifndef os_memcpy
+#define os_memcpy(d, s, n) memcpy((d), (s), (n))
+#endif
+#ifndef os_memmove
+#define os_memmove(d, s, n) memmove((d), (s), (n))
+#endif
+#ifndef os_memset
+#define os_memset(s, c, n) memset(s, c, n)
+#endif
+#ifndef os_memcmp
+#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n))
+#endif
+
+#ifndef os_strlen
+#define os_strlen(s) strlen(s)
+#endif
+#ifndef os_strcasecmp
+#ifdef _MSC_VER
+#define os_strcasecmp(s1, s2) _stricmp((s1), (s2))
+#else
+#define os_strcasecmp(s1, s2) strcasecmp((s1), (s2))
+#endif
+#endif
+#ifndef os_strncasecmp
+#ifdef _MSC_VER
+#define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n))
+#else
+#define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n))
+#endif
+#endif
+#ifndef os_strchr
+#define os_strchr(s, c) strchr((s), (c))
+#endif
+#ifndef os_strcmp
+#define os_strcmp(s1, s2) strcmp((s1), (s2))
+#endif
+#ifndef os_strncmp
+#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n))
+#endif
+#ifndef os_strrchr
+#define os_strrchr(s, c) strrchr((s), (c))
+#endif
+#ifndef os_strstr
+#define os_strstr(h, n) strstr((h), (n))
+#endif
+
+#ifndef os_snprintf
+#ifdef _MSC_VER
+#define os_snprintf _snprintf
+#else
+#define os_snprintf snprintf
+#endif
+#endif
+
+#endif /* OS_NO_C_LIB_DEFINES */
+
+
+static inline int os_snprintf_error(size_t size, int res)
+{
+	return res < 0 || (unsigned int) res >= size;
+}
+
+
+static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size)
+{
+	if (size && nmemb > (~(size_t) 0) / size)
+		return NULL;
+	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
+ * @dest: Destination
+ * @src: Source
+ * @siz: Size of the target buffer
+ * Returns: Total length of the target string (length of src) (not including
+ * NUL-termination)
+ *
+ * This function matches in behavior with the strlcpy(3) function in OpenBSD.
+ */
+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
+#define realloc OS_DO_NOT_USE_realloc
+#define free OS_DO_NOT_USE_free
+#define memcpy OS_DO_NOT_USE_memcpy
+#define memmove OS_DO_NOT_USE_memmove
+#define memset OS_DO_NOT_USE_memset
+#define memcmp OS_DO_NOT_USE_memcmp
+#undef strdup
+#define strdup OS_DO_NOT_USE_strdup
+#define strlen OS_DO_NOT_USE_strlen
+#define strcasecmp OS_DO_NOT_USE_strcasecmp
+#define strncasecmp OS_DO_NOT_USE_strncasecmp
+#undef strchr
+#define strchr OS_DO_NOT_USE_strchr
+#undef strcmp
+#define strcmp OS_DO_NOT_USE_strcmp
+#undef strncmp
+#define strncmp OS_DO_NOT_USE_strncmp
+#undef strncpy
+#define strncpy OS_DO_NOT_USE_strncpy
+#define strrchr OS_DO_NOT_USE_strrchr
+#define strstr OS_DO_NOT_USE_strstr
+#undef snprintf
+#define snprintf OS_DO_NOT_USE_snprintf
+
+#define strcpy OS_DO_NOT_USE_strcpy
+#endif /* OS_REJECT_C_LIB_FUNCTIONS */
+
+
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+#define TEST_FAIL() testing_test_fail()
+int testing_test_fail(void);
+#else
+#define TEST_FAIL() 0
+#endif
+
+#endif /* OS_H */
diff --git a/hostap/src/utils/os_internal.c b/hostap/src/utils/os_internal.c
new file mode 100644
index 0000000..ed6eb3c
--- /dev/null
+++ b/hostap/src/utils/os_internal.c
@@ -0,0 +1,570 @@
+/*
+ * wpa_supplicant/hostapd / Internal implementation of OS specific functions
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file is an example of operating system specific  wrapper functions.
+ * This version implements many of the functions internally, so it can be used
+ * to fill in missing functions from the target system C libraries.
+ *
+ * Some of the functions are using standard C library calls in order to keep
+ * this file in working condition to allow the functions to be tested on a
+ * Linux target. Please note that OS_NO_C_LIB_DEFINES needs to be defined for
+ * this file to work correctly. Note that these implementations are only
+ * examples and are not optimized for speed.
+ */
+
+#include "includes.h"
+#include <time.h>
+#include <sys/wait.h>
+
+#undef OS_REJECT_C_LIB_FUNCTIONS
+#include "common.h"
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+	if (sec)
+		sleep(sec);
+	if (usec)
+		usleep(usec);
+}
+
+
+int os_get_time(struct os_time *t)
+{
+	int res;
+	struct timeval tv;
+	res = gettimeofday(&tv, NULL);
+	t->sec = tv.tv_sec;
+	t->usec = tv.tv_usec;
+	return res;
+}
+
+
+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)
+{
+	struct tm tm;
+
+	if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
+	    hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
+	    sec > 60)
+		return -1;
+
+	os_memset(&tm, 0, sizeof(tm));
+	tm.tm_year = year - 1900;
+	tm.tm_mon = month - 1;
+	tm.tm_mday = day;
+	tm.tm_hour = hour;
+	tm.tm_min = min;
+	tm.tm_sec = sec;
+
+	*t = (os_time_t) mktime(&tm);
+	return 0;
+}
+
+
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+	struct tm *tm2;
+	time_t t2 = t;
+
+	tm2 = gmtime(&t2);
+	if (tm2 == NULL)
+		return -1;
+	tm->sec = tm2->tm_sec;
+	tm->min = tm2->tm_min;
+	tm->hour = tm2->tm_hour;
+	tm->day = tm2->tm_mday;
+	tm->month = tm2->tm_mon + 1;
+	tm->year = tm2->tm_year + 1900;
+	return 0;
+}
+
+
+int os_daemonize(const char *pid_file)
+{
+	if (daemon(0, 0)) {
+		wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
+		return -1;
+	}
+
+	if (pid_file) {
+		FILE *f = fopen(pid_file, "w");
+		if (f) {
+			fprintf(f, "%u\n", getpid());
+			fclose(f);
+		}
+	}
+
+	return -0;
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+	if (pid_file)
+		unlink(pid_file);
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+	FILE *f;
+	size_t rc;
+
+	f = fopen("/dev/urandom", "rb");
+	if (f == NULL) {
+		printf("Could not open /dev/urandom.\n");
+		return -1;
+	}
+
+	rc = fread(buf, 1, len, f);
+	fclose(f);
+
+	return rc != len ? -1 : 0;
+}
+
+
+unsigned long os_random(void)
+{
+	return random();
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+	char *buf = NULL, *cwd, *ret;
+	size_t len = 128, cwd_len, rel_len, ret_len;
+
+	if (rel_path[0] == '/')
+		return os_strdup(rel_path);
+
+	for (;;) {
+		buf = os_malloc(len);
+		if (buf == NULL)
+			return NULL;
+		cwd = getcwd(buf, len);
+		if (cwd == NULL) {
+			os_free(buf);
+			if (errno != ERANGE) {
+				return NULL;
+			}
+			len *= 2;
+		} else {
+			break;
+		}
+	}
+
+	cwd_len = os_strlen(cwd);
+	rel_len = os_strlen(rel_path);
+	ret_len = cwd_len + 1 + rel_len + 1;
+	ret = os_malloc(ret_len);
+	if (ret) {
+		os_memcpy(ret, cwd, cwd_len);
+		ret[cwd_len] = '/';
+		os_memcpy(ret + cwd_len + 1, rel_path, rel_len);
+		ret[ret_len - 1] = '\0';
+	}
+	os_free(buf);
+	return ret;
+}
+
+
+int os_program_init(void)
+{
+	return 0;
+}
+
+
+void os_program_deinit(void)
+{
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+	return setenv(name, value, overwrite);
+}
+
+
+int os_unsetenv(const char *name)
+{
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+	unsetenv(name);
+	return 0;
+#else
+	return unsetenv(name);
+#endif
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+	FILE *f;
+	char *buf;
+
+	f = fopen(name, "rb");
+	if (f == NULL)
+		return NULL;
+
+	fseek(f, 0, SEEK_END);
+	*len = ftell(f);
+	fseek(f, 0, SEEK_SET);
+
+	buf = os_malloc(*len);
+	if (buf == NULL) {
+		fclose(f);
+		return NULL;
+	}
+
+	if (fread(buf, 1, *len, f) != *len) {
+		fclose(f);
+		os_free(buf);
+		return NULL;
+	}
+
+	fclose(f);
+
+	return buf;
+}
+
+
+int os_fdatasync(FILE *stream)
+{
+	return 0;
+}
+
+
+void * os_zalloc(size_t size)
+{
+	void *n = os_malloc(size);
+	if (n)
+		os_memset(n, 0, size);
+	return n;
+}
+
+
+void * os_malloc(size_t size)
+{
+	return malloc(size);
+}
+
+
+void * os_realloc(void *ptr, size_t size)
+{
+	return realloc(ptr, size);
+}
+
+
+void os_free(void *ptr)
+{
+	free(ptr);
+}
+
+
+void * os_memcpy(void *dest, const void *src, size_t n)
+{
+	char *d = dest;
+	const char *s = src;
+	while (n--)
+		*d++ = *s++;
+	return dest;
+}
+
+
+void * os_memmove(void *dest, const void *src, size_t n)
+{
+	if (dest < src)
+		os_memcpy(dest, src, n);
+	else {
+		/* overlapping areas */
+		char *d = (char *) dest + n;
+		const char *s = (const char *) src + n;
+		while (n--)
+			*--d = *--s;
+	}
+	return dest;
+}
+
+
+void * os_memset(void *s, int c, size_t n)
+{
+	char *p = s;
+	while (n--)
+		*p++ = c;
+	return s;
+}
+
+
+int os_memcmp(const void *s1, const void *s2, size_t n)
+{
+	const unsigned char *p1 = s1, *p2 = s2;
+
+	if (n == 0)
+		return 0;
+
+	while (*p1 == *p2) {
+		p1++;
+		p2++;
+		n--;
+		if (n == 0)
+			return 0;
+	}
+
+	return *p1 - *p2;
+}
+
+
+char * os_strdup(const char *s)
+{
+	char *res;
+	size_t len;
+	if (s == NULL)
+		return NULL;
+	len = os_strlen(s);
+	res = os_malloc(len + 1);
+	if (res)
+		os_memcpy(res, s, len + 1);
+	return res;
+}
+
+
+size_t os_strlen(const char *s)
+{
+	const char *p = s;
+	while (*p)
+		p++;
+	return p - s;
+}
+
+
+int os_strcasecmp(const char *s1, const char *s2)
+{
+	/*
+	 * Ignoring case is not required for main functionality, so just use
+	 * the case sensitive version of the function.
+	 */
+	return os_strcmp(s1, s2);
+}
+
+
+int os_strncasecmp(const char *s1, const char *s2, size_t n)
+{
+	/*
+	 * Ignoring case is not required for main functionality, so just use
+	 * the case sensitive version of the function.
+	 */
+	return os_strncmp(s1, s2, n);
+}
+
+
+char * os_strchr(const char *s, int c)
+{
+	while (*s) {
+		if (*s == c)
+			return (char *) s;
+		s++;
+	}
+	return NULL;
+}
+
+
+char * os_strrchr(const char *s, int c)
+{
+	const char *p = s;
+	while (*p)
+		p++;
+	p--;
+	while (p >= s) {
+		if (*p == c)
+			return (char *) p;
+		p--;
+	}
+	return NULL;
+}
+
+
+int os_strcmp(const char *s1, const char *s2)
+{
+	while (*s1 == *s2) {
+		if (*s1 == '\0')
+			break;
+		s1++;
+		s2++;
+	}
+
+	return *s1 - *s2;
+}
+
+
+int os_strncmp(const char *s1, const char *s2, size_t n)
+{
+	if (n == 0)
+		return 0;
+
+	while (*s1 == *s2) {
+		if (*s1 == '\0')
+			break;
+		s1++;
+		s2++;
+		n--;
+		if (n == 0)
+			return 0;
+	}
+
+	return *s1 - *s2;
+}
+
+
+char * os_strncpy(char *dest, const char *src, size_t n)
+{
+	char *d = dest;
+
+	while (n--) {
+		*d = *src;
+		if (*src == '\0')
+			break;
+		d++;
+		src++;
+	}
+
+	return dest;
+}
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t siz)
+{
+	const char *s = src;
+	size_t left = siz;
+
+	if (left) {
+		/* Copy string up to the maximum size of the dest buffer */
+		while (--left != 0) {
+			if ((*dest++ = *s++) == '\0')
+				break;
+		}
+	}
+
+	if (left == 0) {
+		/* Not enough room for the string; force NUL-termination */
+		if (siz != 0)
+			*dest = '\0';
+		while (*s++)
+			; /* determine total src string length */
+	}
+
+	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;
+}
+
+
+char * os_strstr(const char *haystack, const char *needle)
+{
+	size_t len = os_strlen(needle);
+	while (*haystack) {
+		if (os_strncmp(haystack, needle, len) == 0)
+			return (char *) haystack;
+		haystack++;
+	}
+
+	return NULL;
+}
+
+
+int os_snprintf(char *str, size_t size, const char *format, ...)
+{
+	va_list ap;
+	int ret;
+
+	/* See http://www.ijs.si/software/snprintf/ for portable
+	 * implementation of snprintf.
+	 */
+
+	va_start(ap, format);
+	ret = vsnprintf(str, size, format, ap);
+	va_end(ap);
+	if (size > 0)
+		str[size - 1] = '\0';
+	return ret;
+}
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+	pid_t pid;
+	int pid_status;
+
+	pid = fork();
+	if (pid < 0) {
+		wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
+		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);
+		wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+		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/hostap/src/utils/os_none.c b/hostap/src/utils/os_none.c
new file mode 100644
index 0000000..0c3214d
--- /dev/null
+++ b/hostap/src/utils/os_none.c
@@ -0,0 +1,248 @@
+/*
+ * wpa_supplicant/hostapd / Empty OS specific functions
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file can be used as a starting point when adding a new OS target. The
+ * functions here do not really work as-is since they are just empty or only
+ * return an error value. os_internal.c can be used as another starting point
+ * or reference since it has example implementation of many of these functions.
+ */
+
+#include "includes.h"
+
+#include "os.h"
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+}
+
+
+int os_get_time(struct os_time *t)
+{
+	return -1;
+}
+
+
+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)
+{
+	return -1;
+}
+
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+	return -1;
+}
+
+
+int os_daemonize(const char *pid_file)
+{
+	return -1;
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+	return -1;
+}
+
+
+unsigned long os_random(void)
+{
+	return 0;
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+	return NULL; /* strdup(rel_path) can be used here */
+}
+
+
+int os_program_init(void)
+{
+	return 0;
+}
+
+
+void os_program_deinit(void)
+{
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+	return -1;
+}
+
+
+int os_unsetenv(const char *name)
+{
+	return -1;
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+	return NULL;
+}
+
+
+int os_fdatasync(FILE *stream)
+{
+	return 0;
+}
+
+
+void * os_zalloc(size_t size)
+{
+	return NULL;
+}
+
+
+#ifdef OS_NO_C_LIB_DEFINES
+void * os_malloc(size_t size)
+{
+	return NULL;
+}
+
+
+void * os_realloc(void *ptr, size_t size)
+{
+	return NULL;
+}
+
+
+void os_free(void *ptr)
+{
+}
+
+
+void * os_memcpy(void *dest, const void *src, size_t n)
+{
+	return dest;
+}
+
+
+void * os_memmove(void *dest, const void *src, size_t n)
+{
+	return dest;
+}
+
+
+void * os_memset(void *s, int c, size_t n)
+{
+	return s;
+}
+
+
+int os_memcmp(const void *s1, const void *s2, size_t n)
+{
+	return 0;
+}
+
+
+char * os_strdup(const char *s)
+{
+	return NULL;
+}
+
+
+size_t os_strlen(const char *s)
+{
+	return 0;
+}
+
+
+int os_strcasecmp(const char *s1, const char *s2)
+{
+	/*
+	 * Ignoring case is not required for main functionality, so just use
+	 * the case sensitive version of the function.
+	 */
+	return os_strcmp(s1, s2);
+}
+
+
+int os_strncasecmp(const char *s1, const char *s2, size_t n)
+{
+	/*
+	 * Ignoring case is not required for main functionality, so just use
+	 * the case sensitive version of the function.
+	 */
+	return os_strncmp(s1, s2, n);
+}
+
+
+char * os_strchr(const char *s, int c)
+{
+	return NULL;
+}
+
+
+char * os_strrchr(const char *s, int c)
+{
+	return NULL;
+}
+
+
+int os_strcmp(const char *s1, const char *s2)
+{
+	return 0;
+}
+
+
+int os_strncmp(const char *s1, const char *s2, size_t n)
+{
+	return 0;
+}
+
+
+char * os_strncpy(char *dest, const char *src, size_t n)
+{
+	return dest;
+}
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t size)
+{
+	return 0;
+}
+
+
+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;
+}
+
+
+int os_snprintf(char *str, size_t size, const char *format, ...)
+{
+	return 0;
+}
+#endif /* OS_NO_C_LIB_DEFINES */
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+	return -1;
+}
diff --git a/hostap/src/utils/os_unix.c b/hostap/src/utils/os_unix.c
new file mode 100644
index 0000000..ffa2e78
--- /dev/null
+++ b/hostap/src/utils/os_unix.c
@@ -0,0 +1,822 @@
+/*
+ * OS specific functions for UNIX/POSIX systems
+ * Copyright (c) 2005-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 <time.h>
+#include <sys/wait.h>
+
+#ifdef ANDROID
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <private/android_filesystem_config.h>
+#endif /* ANDROID */
+
+#ifdef __MACH__
+#include <CoreServices/CoreServices.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#endif /* __MACH__ */
+
+#include "os.h"
+#include "common.h"
+
+#ifdef WPA_TRACE
+
+#include "wpa_debug.h"
+#include "trace.h"
+#include "list.h"
+
+static struct dl_list alloc_list = DL_LIST_HEAD_INIT(alloc_list);
+
+#define ALLOC_MAGIC 0xa84ef1b2
+#define FREED_MAGIC 0x67fd487a
+
+struct os_alloc_trace {
+	unsigned int magic;
+	struct dl_list list;
+	size_t len;
+	WPA_TRACE_INFO
+} __attribute__((aligned(16)));
+
+#endif /* WPA_TRACE */
+
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+	if (sec)
+		sleep(sec);
+	if (usec)
+		usleep(usec);
+}
+
+
+int os_get_time(struct os_time *t)
+{
+	int res;
+	struct timeval tv;
+	res = gettimeofday(&tv, NULL);
+	t->sec = tv.tv_sec;
+	t->usec = tv.tv_usec;
+	return res;
+}
+
+
+int os_get_reltime(struct os_reltime *t)
+{
+#ifndef __MACH__
+#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;
+		}
+	}
+#else /* __MACH__ */
+	uint64_t abstime, nano;
+	static mach_timebase_info_data_t info = { 0, 0 };
+
+	if (!info.denom) {
+		if (mach_timebase_info(&info) != KERN_SUCCESS)
+			return -1;
+	}
+
+	abstime = mach_absolute_time();
+	nano = (abstime * info.numer) / info.denom;
+
+	t->sec = nano / NSEC_PER_SEC;
+	t->usec = (nano - (((uint64_t) t->sec) * NSEC_PER_SEC)) / NSEC_PER_USEC;
+
+	return 0;
+#endif /* __MACH__ */
+}
+
+
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+	      os_time_t *t)
+{
+	struct tm tm, *tm1;
+	time_t t_local, t1, t2;
+	os_time_t tz_offset;
+
+	if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
+	    hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
+	    sec > 60)
+		return -1;
+
+	memset(&tm, 0, sizeof(tm));
+	tm.tm_year = year - 1900;
+	tm.tm_mon = month - 1;
+	tm.tm_mday = day;
+	tm.tm_hour = hour;
+	tm.tm_min = min;
+	tm.tm_sec = sec;
+
+	t_local = mktime(&tm);
+
+	/* figure out offset to UTC */
+	tm1 = localtime(&t_local);
+	if (tm1) {
+		t1 = mktime(tm1);
+		tm1 = gmtime(&t_local);
+		if (tm1) {
+			t2 = mktime(tm1);
+			tz_offset = t2 - t1;
+		} else
+			tz_offset = 0;
+	} else
+		tz_offset = 0;
+
+	*t = (os_time_t) t_local - tz_offset;
+	return 0;
+}
+
+
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+	struct tm *tm2;
+	time_t t2 = t;
+
+	tm2 = gmtime(&t2);
+	if (tm2 == NULL)
+		return -1;
+	tm->sec = tm2->tm_sec;
+	tm->min = tm2->tm_min;
+	tm->hour = tm2->tm_hour;
+	tm->day = tm2->tm_mday;
+	tm->month = tm2->tm_mon + 1;
+	tm->year = tm2->tm_year + 1900;
+	return 0;
+}
+
+
+#ifdef __APPLE__
+#include <fcntl.h>
+static int os_daemon(int nochdir, int noclose)
+{
+	int devnull;
+
+	if (chdir("/") < 0)
+		return -1;
+
+	devnull = open("/dev/null", O_RDWR);
+	if (devnull < 0)
+		return -1;
+
+	if (dup2(devnull, STDIN_FILENO) < 0) {
+		close(devnull);
+		return -1;
+	}
+
+	if (dup2(devnull, STDOUT_FILENO) < 0) {
+		close(devnull);
+		return -1;
+	}
+
+	if (dup2(devnull, STDERR_FILENO) < 0) {
+		close(devnull);
+		return -1;
+	}
+
+	return 0;
+}
+#else /* __APPLE__ */
+#define os_daemon daemon
+#endif /* __APPLE__ */
+
+
+int os_daemonize(const char *pid_file)
+{
+#if defined(__uClinux__) || defined(__sun__)
+	return -1;
+#else /* defined(__uClinux__) || defined(__sun__) */
+	if (os_daemon(0, 0)) {
+		perror("daemon");
+		return -1;
+	}
+
+	if (pid_file) {
+		FILE *f = fopen(pid_file, "w");
+		if (f) {
+			fprintf(f, "%u\n", getpid());
+			fclose(f);
+		}
+	}
+
+	return -0;
+#endif /* defined(__uClinux__) || defined(__sun__) */
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+	if (pid_file)
+		unlink(pid_file);
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+	FILE *f;
+	size_t rc;
+
+	if (TEST_FAIL())
+		return -1;
+
+	f = fopen("/dev/urandom", "rb");
+	if (f == NULL) {
+		printf("Could not open /dev/urandom.\n");
+		return -1;
+	}
+
+	rc = fread(buf, 1, len, f);
+	fclose(f);
+
+	return rc != len ? -1 : 0;
+}
+
+
+unsigned long os_random(void)
+{
+	return random();
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+	char *buf = NULL, *cwd, *ret;
+	size_t len = 128, cwd_len, rel_len, ret_len;
+	int last_errno;
+
+	if (!rel_path)
+		return NULL;
+
+	if (rel_path[0] == '/')
+		return os_strdup(rel_path);
+
+	for (;;) {
+		buf = os_malloc(len);
+		if (buf == NULL)
+			return NULL;
+		cwd = getcwd(buf, len);
+		if (cwd == NULL) {
+			last_errno = errno;
+			os_free(buf);
+			if (last_errno != ERANGE)
+				return NULL;
+			len *= 2;
+			if (len > 2000)
+				return NULL;
+		} else {
+			buf[len - 1] = '\0';
+			break;
+		}
+	}
+
+	cwd_len = os_strlen(cwd);
+	rel_len = os_strlen(rel_path);
+	ret_len = cwd_len + 1 + rel_len + 1;
+	ret = os_malloc(ret_len);
+	if (ret) {
+		os_memcpy(ret, cwd, cwd_len);
+		ret[cwd_len] = '/';
+		os_memcpy(ret + cwd_len + 1, rel_path, rel_len);
+		ret[ret_len - 1] = '\0';
+	}
+	os_free(buf);
+	return ret;
+}
+
+
+int os_program_init(void)
+{
+#ifdef ANDROID
+	/*
+	 * We ignore errors here since errors are normal if we
+	 * are already running as non-root.
+	 */
+#ifdef ANDROID_SETGROUPS_OVERRIDE
+	gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE };
+#else /* ANDROID_SETGROUPS_OVERRIDE */
+	gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE };
+#endif /* ANDROID_SETGROUPS_OVERRIDE */
+	struct __user_cap_header_struct header;
+	struct __user_cap_data_struct cap;
+
+	setgroups(ARRAY_SIZE(groups), groups);
+
+	prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+	setgid(AID_WIFI);
+	setuid(AID_WIFI);
+
+	header.version = _LINUX_CAPABILITY_VERSION;
+	header.pid = 0;
+	cap.effective = cap.permitted =
+		(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
+	cap.inheritable = 0;
+	capset(&header, &cap);
+#endif /* ANDROID */
+
+	return 0;
+}
+
+
+void os_program_deinit(void)
+{
+#ifdef WPA_TRACE
+	struct os_alloc_trace *a;
+	unsigned long total = 0;
+	dl_list_for_each(a, &alloc_list, struct os_alloc_trace, list) {
+		total += a->len;
+		if (a->magic != ALLOC_MAGIC) {
+			wpa_printf(MSG_INFO, "MEMLEAK[%p]: invalid magic 0x%x "
+				   "len %lu",
+				   a, a->magic, (unsigned long) a->len);
+			continue;
+		}
+		wpa_printf(MSG_INFO, "MEMLEAK[%p]: len %lu",
+			   a, (unsigned long) a->len);
+		wpa_trace_dump("memleak", a);
+	}
+	if (total)
+		wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes",
+			   (unsigned long) total);
+#endif /* WPA_TRACE */
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+	return setenv(name, value, overwrite);
+}
+
+
+int os_unsetenv(const char *name)
+{
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || \
+    defined(__OpenBSD__)
+	unsetenv(name);
+	return 0;
+#else
+	return unsetenv(name);
+#endif
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+	FILE *f;
+	char *buf;
+	long pos;
+
+	f = fopen(name, "rb");
+	if (f == NULL)
+		return NULL;
+
+	if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) {
+		fclose(f);
+		return NULL;
+	}
+	*len = pos;
+	if (fseek(f, 0, SEEK_SET) < 0) {
+		fclose(f);
+		return NULL;
+	}
+
+	buf = os_malloc(*len);
+	if (buf == NULL) {
+		fclose(f);
+		return NULL;
+	}
+
+	if (fread(buf, 1, *len, f) != *len) {
+		fclose(f);
+		os_free(buf);
+		return NULL;
+	}
+
+	fclose(f);
+
+	return buf;
+}
+
+
+int os_file_exists(const char *fname)
+{
+	FILE *f = fopen(fname, "rb");
+	if (f == NULL)
+		return 0;
+	fclose(f);
+	return 1;
+}
+
+
+int os_fdatasync(FILE *stream)
+{
+	if (!fflush(stream)) {
+#ifndef __MACH__
+		return fdatasync(fileno(stream));
+#else /* __MACH__ */
+#ifdef F_FULLFSYNC
+		/* OS X does not implement fdatasync(). */
+		return fcntl(fileno(stream), F_FULLFSYNC);
+#else /* F_FULLFSYNC */
+#error Neither fdatasync nor F_FULLSYNC are defined
+#endif /* F_FULLFSYNC */
+#endif /* __MACH__ */
+	}
+
+	return -1;
+}
+
+
+#ifndef WPA_TRACE
+void * os_zalloc(size_t size)
+{
+	return calloc(1, size);
+}
+#endif /* WPA_TRACE */
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t siz)
+{
+	const char *s = src;
+	size_t left = siz;
+
+	if (left) {
+		/* Copy string up to the maximum size of the dest buffer */
+		while (--left != 0) {
+			if ((*dest++ = *s++) == '\0')
+				break;
+		}
+	}
+
+	if (left == 0) {
+		/* Not enough room for the string; force NUL-termination */
+		if (siz != 0)
+			*dest = '\0';
+		while (*s++)
+			; /* determine total src string length */
+	}
+
+	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;
+}
+
+
+#ifdef WPA_TRACE
+
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+char wpa_trace_fail_func[256] = { 0 };
+unsigned int wpa_trace_fail_after;
+
+static int testing_fail_alloc(void)
+{
+	const char *func[WPA_TRACE_LEN];
+	size_t i, res, len;
+	char *pos, *next;
+	int match;
+
+	if (!wpa_trace_fail_after)
+		return 0;
+
+	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
+	i = 0;
+	if (i < res && os_strcmp(func[i], __func__) == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_malloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_zalloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_calloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_realloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_realloc_array") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_strdup") == 0)
+		i++;
+
+	pos = wpa_trace_fail_func;
+
+	match = 0;
+	while (i < res) {
+		int allow_skip = 1;
+		int maybe = 0;
+
+		if (*pos == '=') {
+			allow_skip = 0;
+			pos++;
+		} else if (*pos == '?') {
+			maybe = 1;
+			pos++;
+		}
+		next = os_strchr(pos, ';');
+		if (next)
+			len = next - pos;
+		else
+			len = os_strlen(pos);
+		if (os_memcmp(pos, func[i], len) != 0) {
+			if (maybe && next) {
+				pos = next + 1;
+				continue;
+			}
+			if (allow_skip) {
+				i++;
+				continue;
+			}
+			return 0;
+		}
+		if (!next) {
+			match = 1;
+			break;
+		}
+		pos = next + 1;
+		i++;
+	}
+	if (!match)
+		return 0;
+
+	wpa_trace_fail_after--;
+	if (wpa_trace_fail_after == 0) {
+		wpa_printf(MSG_INFO, "TESTING: fail allocation at %s",
+			   wpa_trace_fail_func);
+		for (i = 0; i < res; i++)
+			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
+				   (int) i, func[i]);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+char wpa_trace_test_fail_func[256] = { 0 };
+unsigned int wpa_trace_test_fail_after;
+
+int testing_test_fail(void)
+{
+	const char *func[WPA_TRACE_LEN];
+	size_t i, res, len;
+	char *pos, *next;
+	int match;
+
+	if (!wpa_trace_test_fail_after)
+		return 0;
+
+	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
+	i = 0;
+	if (i < res && os_strcmp(func[i], __func__) == 0)
+		i++;
+
+	pos = wpa_trace_test_fail_func;
+
+	match = 0;
+	while (i < res) {
+		int allow_skip = 1;
+		int maybe = 0;
+
+		if (*pos == '=') {
+			allow_skip = 0;
+			pos++;
+		} else if (*pos == '?') {
+			maybe = 1;
+			pos++;
+		}
+		next = os_strchr(pos, ';');
+		if (next)
+			len = next - pos;
+		else
+			len = os_strlen(pos);
+		if (os_memcmp(pos, func[i], len) != 0) {
+			if (maybe && next) {
+				pos = next + 1;
+				continue;
+			}
+			if (allow_skip) {
+				i++;
+				continue;
+			}
+			return 0;
+		}
+		if (!next) {
+			match = 1;
+			break;
+		}
+		pos = next + 1;
+		i++;
+	}
+	if (!match)
+		return 0;
+
+	wpa_trace_test_fail_after--;
+	if (wpa_trace_test_fail_after == 0) {
+		wpa_printf(MSG_INFO, "TESTING: fail at %s",
+			   wpa_trace_test_fail_func);
+		for (i = 0; i < res; i++)
+			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
+				   (int) i, func[i]);
+		return 1;
+	}
+
+	return 0;
+}
+
+#else
+
+static inline int testing_fail_alloc(void)
+{
+	return 0;
+}
+#endif
+
+void * os_malloc(size_t size)
+{
+	struct os_alloc_trace *a;
+
+	if (testing_fail_alloc())
+		return NULL;
+
+	a = malloc(sizeof(*a) + size);
+	if (a == NULL)
+		return NULL;
+	a->magic = ALLOC_MAGIC;
+	dl_list_add(&alloc_list, &a->list);
+	a->len = size;
+	wpa_trace_record(a);
+	return a + 1;
+}
+
+
+void * os_realloc(void *ptr, size_t size)
+{
+	struct os_alloc_trace *a;
+	size_t copy_len;
+	void *n;
+
+	if (ptr == NULL)
+		return os_malloc(size);
+
+	a = (struct os_alloc_trace *) ptr - 1;
+	if (a->magic != ALLOC_MAGIC) {
+		wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s",
+			   a, a->magic,
+			   a->magic == FREED_MAGIC ? " (already freed)" : "");
+		wpa_trace_show("Invalid os_realloc() call");
+		abort();
+	}
+	n = os_malloc(size);
+	if (n == NULL)
+		return NULL;
+	copy_len = a->len;
+	if (copy_len > size)
+		copy_len = size;
+	os_memcpy(n, a + 1, copy_len);
+	os_free(ptr);
+	return n;
+}
+
+
+void os_free(void *ptr)
+{
+	struct os_alloc_trace *a;
+
+	if (ptr == NULL)
+		return;
+	a = (struct os_alloc_trace *) ptr - 1;
+	if (a->magic != ALLOC_MAGIC) {
+		wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s",
+			   a, a->magic,
+			   a->magic == FREED_MAGIC ? " (already freed)" : "");
+		wpa_trace_show("Invalid os_free() call");
+		abort();
+	}
+	dl_list_del(&a->list);
+	a->magic = FREED_MAGIC;
+
+	wpa_trace_check_ref(ptr);
+	free(a);
+}
+
+
+void * os_zalloc(size_t size)
+{
+	void *ptr = os_malloc(size);
+	if (ptr)
+		os_memset(ptr, 0, size);
+	return ptr;
+}
+
+
+char * os_strdup(const char *s)
+{
+	size_t len;
+	char *d;
+	len = os_strlen(s);
+	d = os_malloc(len + 1);
+	if (d == NULL)
+		return NULL;
+	os_memcpy(d, s, len);
+	d[len] = '\0';
+	return d;
+}
+
+#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/hostap/src/utils/os_win32.c b/hostap/src/utils/os_win32.c
new file mode 100644
index 0000000..dea27b9
--- /dev/null
+++ b/hostap/src/utils/os_win32.c
@@ -0,0 +1,285 @@
+/*
+ * wpa_supplicant/hostapd / OS specific functions for Win32 systems
+ * Copyright (c) 2005-2006, 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 <time.h>
+#include <winsock2.h>
+#include <wincrypt.h>
+
+#include "os.h"
+#include "common.h"
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+	if (sec)
+		Sleep(sec * 1000);
+	if (usec)
+		Sleep(usec / 1000);
+}
+
+
+int os_get_time(struct os_time *t)
+{
+#define EPOCHFILETIME (116444736000000000ULL)
+	FILETIME ft;
+	LARGE_INTEGER li;
+	ULONGLONG tt;
+
+#ifdef _WIN32_WCE
+	SYSTEMTIME st;
+
+	GetSystemTime(&st);
+	SystemTimeToFileTime(&st, &ft);
+#else /* _WIN32_WCE */
+	GetSystemTimeAsFileTime(&ft);
+#endif /* _WIN32_WCE */
+	li.LowPart = ft.dwLowDateTime;
+	li.HighPart = ft.dwHighDateTime;
+	tt = (li.QuadPart - EPOCHFILETIME) / 10;
+	t->sec = (os_time_t) (tt / 1000000);
+	t->usec = (os_time_t) (tt % 1000000);
+
+	return 0;
+}
+
+
+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)
+{
+	struct tm tm, *tm1;
+	time_t t_local, t1, t2;
+	os_time_t tz_offset;
+
+	if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
+	    hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
+	    sec > 60)
+		return -1;
+
+	memset(&tm, 0, sizeof(tm));
+	tm.tm_year = year - 1900;
+	tm.tm_mon = month - 1;
+	tm.tm_mday = day;
+	tm.tm_hour = hour;
+	tm.tm_min = min;
+	tm.tm_sec = sec;
+
+	t_local = mktime(&tm);
+
+	/* figure out offset to UTC */
+	tm1 = localtime(&t_local);
+	if (tm1) {
+		t1 = mktime(tm1);
+		tm1 = gmtime(&t_local);
+		if (tm1) {
+			t2 = mktime(tm1);
+			tz_offset = t2 - t1;
+		} else
+			tz_offset = 0;
+	} else
+		tz_offset = 0;
+
+	*t = (os_time_t) t_local - tz_offset;
+	return 0;
+}
+
+
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+	struct tm *tm2;
+	time_t t2 = t;
+
+	tm2 = gmtime(&t2);
+	if (tm2 == NULL)
+		return -1;
+	tm->sec = tm2->tm_sec;
+	tm->min = tm2->tm_min;
+	tm->hour = tm2->tm_hour;
+	tm->day = tm2->tm_mday;
+	tm->month = tm2->tm_mon + 1;
+	tm->year = tm2->tm_year + 1900;
+	return 0;
+}
+
+
+int os_daemonize(const char *pid_file)
+{
+	/* TODO */
+	return -1;
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+	HCRYPTPROV prov;
+	BOOL ret;
+
+	if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL,
+				 CRYPT_VERIFYCONTEXT))
+		return -1;
+
+	ret = CryptGenRandom(prov, len, buf);
+	CryptReleaseContext(prov, 0);
+
+	return ret ? 0 : -1;
+}
+
+
+unsigned long os_random(void)
+{
+	return rand();
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+	return _strdup(rel_path);
+}
+
+
+int os_program_init(void)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+	WSADATA wsaData;
+	if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
+		printf("Could not find a usable WinSock.dll\n");
+		return -1;
+	}
+#endif /* CONFIG_NATIVE_WINDOWS */
+	return 0;
+}
+
+
+void os_program_deinit(void)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+	WSACleanup();
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+	return -1;
+}
+
+
+int os_unsetenv(const char *name)
+{
+	return -1;
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+	FILE *f;
+	char *buf;
+
+	f = fopen(name, "rb");
+	if (f == NULL)
+		return NULL;
+
+	fseek(f, 0, SEEK_END);
+	*len = ftell(f);
+	fseek(f, 0, SEEK_SET);
+
+	buf = malloc(*len);
+	if (buf == NULL) {
+		fclose(f);
+		return NULL;
+	}
+
+	fread(buf, 1, *len, f);
+	fclose(f);
+
+	return buf;
+}
+
+
+int os_fdatasync(FILE *stream)
+{
+	HANDLE h;
+
+	if (stream == NULL)
+		return -1;
+
+	h = (HANDLE) _get_osfhandle(_fileno(stream));
+	if (h == INVALID_HANDLE_VALUE)
+		return -1;
+
+	if (!FlushFileBuffers(h))
+		return -1;
+
+	return 0;
+}
+
+
+void * os_zalloc(size_t size)
+{
+	return calloc(1, size);
+}
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t siz)
+{
+	const char *s = src;
+	size_t left = siz;
+
+	if (left) {
+		/* Copy string up to the maximum size of the dest buffer */
+		while (--left != 0) {
+			if ((*dest++ = *s++) == '\0')
+				break;
+		}
+	}
+
+	if (left == 0) {
+		/* Not enough room for the string; force NUL-termination */
+		if (siz != 0)
+			*dest = '\0';
+		while (*s++)
+			; /* determine total src string length */
+	}
+
+	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/hostap/src/utils/pcsc_funcs.c b/hostap/src/utils/pcsc_funcs.c
new file mode 100644
index 0000000..6f5ea93
--- /dev/null
+++ b/hostap/src/utils/pcsc_funcs.c
@@ -0,0 +1,1434 @@
+/*
+ * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
+ * Copyright (c) 2004-2007, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements wrapper functions for accessing GSM SIM and 3GPP USIM
+ * cards through PC/SC smartcard library. These functions are used to implement
+ * authentication routines for EAP-SIM and EAP-AKA.
+ */
+
+#include "includes.h"
+#include <winscard.h>
+
+#include "common.h"
+#include "pcsc_funcs.h"
+
+
+/* See ETSI GSM 11.11 and ETSI TS 102 221 for details.
+ * SIM commands:
+ * Command APDU: CLA INS P1 P2 P3 Data
+ *   CLA (class of instruction): A0 for GSM, 00 for USIM
+ *   INS (instruction)
+ *   P1 P2 P3 (parameters, P3 = length of Data)
+ * Response APDU: Data SW1 SW2
+ *   SW1 SW2 (Status words)
+ * Commands (INS P1 P2 P3):
+ *   SELECT: A4 00 00 02 <file_id, 2 bytes>
+ *   GET RESPONSE: C0 00 00 <len>
+ *   RUN GSM ALG: 88 00 00 00 <RAND len = 10>
+ *   RUN UMTS ALG: 88 00 81 <len=0x22> data: 0x10 | RAND | 0x10 | AUTN
+ *	P1 = ID of alg in card
+ *	P2 = ID of secret key
+ *   READ BINARY: B0 <offset high> <offset low> <len>
+ *   READ RECORD: B2 <record number> <mode> <len>
+ *	P2 (mode) = '02' (next record), '03' (previous record),
+ *		    '04' (absolute mode)
+ *   VERIFY CHV: 20 00 <CHV number> 08
+ *   CHANGE CHV: 24 00 <CHV number> 10
+ *   DISABLE CHV: 26 00 01 08
+ *   ENABLE CHV: 28 00 01 08
+ *   UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10
+ *   SLEEP: FA 00 00 00
+ */
+
+/* GSM SIM commands */
+#define SIM_CMD_SELECT			0xa0, 0xa4, 0x00, 0x00, 0x02
+#define SIM_CMD_RUN_GSM_ALG		0xa0, 0x88, 0x00, 0x00, 0x10
+#define SIM_CMD_GET_RESPONSE		0xa0, 0xc0, 0x00, 0x00
+#define SIM_CMD_READ_BIN		0xa0, 0xb0, 0x00, 0x00
+#define SIM_CMD_READ_RECORD		0xa0, 0xb2, 0x00, 0x00
+#define SIM_CMD_VERIFY_CHV1		0xa0, 0x20, 0x00, 0x01, 0x08
+
+/* USIM commands */
+#define USIM_CLA			0x00
+#define USIM_CMD_RUN_UMTS_ALG		0x00, 0x88, 0x00, 0x81, 0x22
+#define USIM_CMD_GET_RESPONSE		0x00, 0xc0, 0x00, 0x00
+
+#define SIM_RECORD_MODE_ABSOLUTE 0x04
+
+#define USIM_FSP_TEMPL_TAG		0x62
+
+#define USIM_TLV_FILE_DESC		0x82
+#define USIM_TLV_FILE_ID		0x83
+#define USIM_TLV_DF_NAME		0x84
+#define USIM_TLV_PROPR_INFO		0xA5
+#define USIM_TLV_LIFE_CYCLE_STATUS	0x8A
+#define USIM_TLV_FILE_SIZE		0x80
+#define USIM_TLV_TOTAL_FILE_SIZE	0x81
+#define USIM_TLV_PIN_STATUS_TEMPLATE	0xC6
+#define USIM_TLV_SHORT_FILE_ID		0x88
+#define USIM_TLV_SECURITY_ATTR_8B	0x8B
+#define USIM_TLV_SECURITY_ATTR_8C	0x8C
+#define USIM_TLV_SECURITY_ATTR_AB	0xAB
+
+#define USIM_PS_DO_TAG			0x90
+
+#define AKA_RAND_LEN 16
+#define AKA_AUTN_LEN 16
+#define AKA_AUTS_LEN 14
+#define RES_MAX_LEN 16
+#define IK_LEN 16
+#define CK_LEN 16
+
+
+/* GSM files
+ * File type in first octet:
+ * 3F = Master File
+ * 7F = Dedicated File
+ * 2F = Elementary File under the Master File
+ * 6F = Elementary File under a Dedicated File
+ */
+#define SCARD_FILE_MF		0x3F00
+#define SCARD_FILE_GSM_DF	0x7F20
+#define SCARD_FILE_UMTS_DF	0x7F50
+#define SCARD_FILE_GSM_EF_IMSI	0x6F07
+#define SCARD_FILE_GSM_EF_AD	0x6FAD
+#define SCARD_FILE_EF_DIR	0x2F00
+#define SCARD_FILE_EF_ICCID	0x2FE2
+#define SCARD_FILE_EF_CK	0x6FE1
+#define SCARD_FILE_EF_IK	0x6FE2
+
+#define SCARD_CHV1_OFFSET	13
+#define SCARD_CHV1_FLAG		0x80
+
+
+typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types;
+
+struct scard_data {
+	SCARDCONTEXT ctx;
+	SCARDHANDLE card;
+	DWORD protocol;
+	sim_types sim_type;
+	int pin1_required;
+};
+
+#ifdef __MINGW32_VERSION
+/* MinGW does not yet support WinScard, so load the needed functions
+ * dynamically from winscard.dll for now. */
+
+static HINSTANCE dll = NULL; /* winscard.dll */
+
+static const SCARD_IO_REQUEST *dll_g_rgSCardT0Pci, *dll_g_rgSCardT1Pci;
+#undef SCARD_PCI_T0
+#define SCARD_PCI_T0 (dll_g_rgSCardT0Pci)
+#undef SCARD_PCI_T1
+#define SCARD_PCI_T1 (dll_g_rgSCardT1Pci)
+
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardEstablishContext)(IN DWORD dwScope,
+			     IN LPCVOID pvReserved1,
+			     IN LPCVOID pvReserved2,
+			     OUT LPSCARDCONTEXT phContext);
+#define SCardEstablishContext dll_SCardEstablishContext
+
+static long (*dll_SCardReleaseContext)(long hContext);
+#define SCardReleaseContext dll_SCardReleaseContext
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardListReadersA)(IN SCARDCONTEXT hContext,
+			 IN LPCSTR mszGroups,
+			 OUT LPSTR mszReaders,
+			 IN OUT LPDWORD pcchReaders);
+#undef SCardListReaders
+#define SCardListReaders dll_SCardListReadersA
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardConnectA)(IN SCARDCONTEXT hContext,
+		     IN LPCSTR szReader,
+		     IN DWORD dwShareMode,
+		     IN DWORD dwPreferredProtocols,
+		     OUT LPSCARDHANDLE phCard,
+		     OUT LPDWORD pdwActiveProtocol);
+#undef SCardConnect
+#define SCardConnect dll_SCardConnectA
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardDisconnect)(IN SCARDHANDLE hCard,
+		       IN DWORD dwDisposition);
+#define SCardDisconnect dll_SCardDisconnect
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardTransmit)(IN SCARDHANDLE hCard,
+		     IN LPCSCARD_IO_REQUEST pioSendPci,
+		     IN LPCBYTE pbSendBuffer,
+		     IN DWORD cbSendLength,
+		     IN OUT LPSCARD_IO_REQUEST pioRecvPci,
+		     OUT LPBYTE pbRecvBuffer,
+		     IN OUT LPDWORD pcbRecvLength);
+#define SCardTransmit dll_SCardTransmit
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardBeginTransaction)(IN SCARDHANDLE hCard);
+#define SCardBeginTransaction dll_SCardBeginTransaction
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardEndTransaction)(IN SCARDHANDLE hCard, IN DWORD dwDisposition);
+#define SCardEndTransaction dll_SCardEndTransaction
+
+
+static int mingw_load_symbols(void)
+{
+	char *sym;
+
+	if (dll)
+		return 0;
+
+	dll = LoadLibrary("winscard");
+	if (dll == NULL) {
+		wpa_printf(MSG_DEBUG, "WinSCard: Could not load winscard.dll "
+			   "library");
+		return -1;
+	}
+
+#define LOADSYM(s) \
+	sym = #s; \
+	dll_ ## s = (void *) GetProcAddress(dll, sym); \
+	if (dll_ ## s == NULL) \
+		goto fail;
+
+	LOADSYM(SCardEstablishContext);
+	LOADSYM(SCardReleaseContext);
+	LOADSYM(SCardListReadersA);
+	LOADSYM(SCardConnectA);
+	LOADSYM(SCardDisconnect);
+	LOADSYM(SCardTransmit);
+	LOADSYM(SCardBeginTransaction);
+	LOADSYM(SCardEndTransaction);
+	LOADSYM(g_rgSCardT0Pci);
+	LOADSYM(g_rgSCardT1Pci);
+
+#undef LOADSYM
+
+	return 0;
+
+fail:
+	wpa_printf(MSG_DEBUG, "WinSCard: Could not get address for %s from "
+		   "winscard.dll", sym);
+	FreeLibrary(dll);
+	dll = NULL;
+	return -1;
+}
+
+
+static void mingw_unload_symbols(void)
+{
+	if (dll == NULL)
+		return;
+
+	FreeLibrary(dll);
+	dll = NULL;
+}
+
+#else /* __MINGW32_VERSION */
+
+#define mingw_load_symbols() 0
+#define mingw_unload_symbols() do { } while (0)
+
+#endif /* __MINGW32_VERSION */
+
+
+static int _scard_select_file(struct scard_data *scard, unsigned short file_id,
+			      unsigned char *buf, size_t *buf_len,
+			      sim_types sim_type, unsigned char *aid,
+			      size_t aidlen);
+static int scard_select_file(struct scard_data *scard, unsigned short file_id,
+			     unsigned char *buf, size_t *buf_len);
+static int scard_verify_pin(struct scard_data *scard, const char *pin);
+static int scard_get_record_len(struct scard_data *scard,
+				unsigned char recnum, unsigned char mode);
+static int scard_read_record(struct scard_data *scard,
+			     unsigned char *data, size_t len,
+			     unsigned char recnum, unsigned char mode);
+
+
+static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len,
+				 int *ps_do, int *file_len)
+{
+	unsigned char *pos, *end;
+
+	if (ps_do)
+		*ps_do = -1;
+	if (file_len)
+		*file_len = -1;
+
+	pos = buf;
+	end = pos + buf_len;
+	if (*pos != USIM_FSP_TEMPL_TAG) {
+		wpa_printf(MSG_DEBUG, "SCARD: file header did not "
+			   "start with FSP template tag");
+		return -1;
+	}
+	pos++;
+	if (pos >= end)
+		return -1;
+	if ((pos + pos[0]) < end)
+		end = pos + 1 + pos[0];
+	pos++;
+	wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
+		    pos, end - pos);
+
+	while (end - pos >= 2) {
+		unsigned char type, len;
+
+		type = pos[0];
+		len = pos[1];
+		wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d",
+			   type, len);
+		pos += 2;
+
+		if (len > (unsigned int) (end - pos))
+			break;
+
+		switch (type) {
+		case USIM_TLV_FILE_DESC:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV",
+				    pos, len);
+			break;
+		case USIM_TLV_FILE_ID:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV",
+				    pos, len);
+			break;
+		case USIM_TLV_DF_NAME:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV",
+				    pos, len);
+			break;
+		case USIM_TLV_PROPR_INFO:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary "
+				    "information TLV", pos, len);
+			break;
+		case USIM_TLV_LIFE_CYCLE_STATUS:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status "
+				    "Integer TLV", pos, len);
+			break;
+		case USIM_TLV_FILE_SIZE:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV",
+				    pos, len);
+			if ((len == 1 || len == 2) && file_len) {
+				if (len == 1)
+					*file_len = (int) pos[0];
+				else
+					*file_len = WPA_GET_BE16(pos);
+				wpa_printf(MSG_DEBUG, "SCARD: file_size=%d",
+					   *file_len);
+			}
+			break;
+		case USIM_TLV_TOTAL_FILE_SIZE:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV",
+				    pos, len);
+			break;
+		case USIM_TLV_PIN_STATUS_TEMPLATE:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template "
+				    "DO TLV", pos, len);
+			if (len >= 2 && pos[0] == USIM_PS_DO_TAG &&
+			    pos[1] >= 1 && ps_do) {
+				wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x",
+					   pos[2]);
+				*ps_do = (int) pos[2];
+			}
+			break;
+		case USIM_TLV_SHORT_FILE_ID:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File "
+				    "Identifier (SFI) TLV", pos, len);
+			break;
+		case USIM_TLV_SECURITY_ATTR_8B:
+		case USIM_TLV_SECURITY_ATTR_8C:
+		case USIM_TLV_SECURITY_ATTR_AB:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute "
+				    "TLV", pos, len);
+			break;
+		default:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV",
+				    pos, len);
+			break;
+		}
+
+		pos += len;
+
+		if (pos == end)
+			return 0;
+	}
+	return -1;
+}
+
+
+static int scard_pin_needed(struct scard_data *scard,
+			    unsigned char *hdr, size_t hlen)
+{
+	if (scard->sim_type == SCARD_GSM_SIM) {
+		if (hlen > SCARD_CHV1_OFFSET &&
+		    !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG))
+			return 1;
+		return 0;
+	}
+
+	if (scard->sim_type == SCARD_USIM) {
+		int ps_do;
+		if (scard_parse_fsp_templ(hdr, hlen, &ps_do, NULL))
+			return -1;
+		/* TODO: there could be more than one PS_DO entry because of
+		 * multiple PINs in key reference.. */
+		if (ps_do > 0 && (ps_do & 0x80))
+			return 1;
+		return 0;
+	}
+
+	return -1;
+}
+
+
+static int scard_get_aid(struct scard_data *scard, unsigned char *aid,
+			 size_t maxlen)
+{
+	int rlen, rec;
+	struct efdir {
+		unsigned char appl_template_tag; /* 0x61 */
+		unsigned char appl_template_len;
+		unsigned char appl_id_tag; /* 0x4f */
+		unsigned char aid_len;
+		unsigned char rid[5];
+		unsigned char appl_code[2]; /* 0x1002 for 3G USIM */
+	} *efdir;
+	unsigned char buf[127], *aid_pos;
+	size_t blen;
+	unsigned int aid_len = 0;
+
+	efdir = (struct efdir *) buf;
+	aid_pos = &buf[4];
+	blen = sizeof(buf);
+	if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) {
+		wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR select", buf, blen);
+
+	for (rec = 1; rec < 10; rec++) {
+		rlen = scard_get_record_len(scard, rec,
+					    SIM_RECORD_MODE_ABSOLUTE);
+		if (rlen < 0) {
+			wpa_printf(MSG_DEBUG, "SCARD: Failed to get EF_DIR "
+				   "record length");
+			return -1;
+		}
+		blen = sizeof(buf);
+		if (rlen > (int) blen) {
+			wpa_printf(MSG_DEBUG, "SCARD: Too long EF_DIR record");
+			return -1;
+		}
+		if (scard_read_record(scard, buf, rlen, rec,
+				      SIM_RECORD_MODE_ABSOLUTE) < 0) {
+			wpa_printf(MSG_DEBUG, "SCARD: Failed to read "
+				   "EF_DIR record %d", rec);
+			return -1;
+		}
+		wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR record", buf, rlen);
+
+		if (efdir->appl_template_tag != 0x61) {
+			wpa_printf(MSG_DEBUG, "SCARD: Unexpected application "
+				   "template tag 0x%x",
+				   efdir->appl_template_tag);
+			continue;
+		}
+
+		if (efdir->appl_template_len > rlen - 2) {
+			wpa_printf(MSG_DEBUG, "SCARD: Too long application "
+				   "template (len=%d rlen=%d)",
+				   efdir->appl_template_len, rlen);
+			continue;
+		}
+
+		if (efdir->appl_id_tag != 0x4f) {
+			wpa_printf(MSG_DEBUG, "SCARD: Unexpected application "
+				   "identifier tag 0x%x", efdir->appl_id_tag);
+			continue;
+		}
+
+		aid_len = efdir->aid_len;
+		if (aid_len < 1 || aid_len > 16) {
+			wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %u",
+				   aid_len);
+			continue;
+		}
+
+		wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record",
+			    aid_pos, aid_len);
+
+		if (efdir->appl_code[0] == 0x10 &&
+		    efdir->appl_code[1] == 0x02) {
+			wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app found from "
+				   "EF_DIR record %d", rec);
+			break;
+		}
+	}
+
+	if (rec >= 10) {
+		wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app not found "
+			   "from EF_DIR records");
+		return -1;
+	}
+
+	if (aid_len > maxlen) {
+		wpa_printf(MSG_DEBUG, "SCARD: Too long AID");
+		return -1;
+	}
+
+	os_memcpy(aid, aid_pos, aid_len);
+
+	return aid_len;
+}
+
+
+/**
+ * scard_init - Initialize SIM/USIM connection using PC/SC
+ * @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. 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(const char *reader)
+{
+	long ret;
+	unsigned long len, pos;
+	struct scard_data *scard;
+#ifdef CONFIG_NATIVE_WINDOWS
+	TCHAR *readers = NULL;
+#else /* CONFIG_NATIVE_WINDOWS */
+	char *readers = NULL;
+#endif /* CONFIG_NATIVE_WINDOWS */
+	unsigned char buf[100];
+	size_t blen;
+	int transaction = 0;
+	int pin_needed;
+
+	wpa_printf(MSG_DEBUG, "SCARD: initializing smart card interface");
+	if (mingw_load_symbols())
+		return NULL;
+	scard = os_zalloc(sizeof(*scard));
+	if (scard == NULL)
+		return NULL;
+
+	ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
+				    &scard->ctx);
+	if (ret != SCARD_S_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "SCARD: Could not establish smart card "
+			   "context (err=%ld)", ret);
+		goto failed;
+	}
+
+	ret = SCardListReaders(scard->ctx, NULL, NULL, &len);
+	if (ret != SCARD_S_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed "
+			   "(err=%ld)", ret);
+		goto failed;
+	}
+
+#ifdef UNICODE
+	len *= 2;
+#endif /* UNICODE */
+	readers = os_malloc(len);
+	if (readers == NULL) {
+		wpa_printf(MSG_INFO, "SCARD: malloc failed\n");
+		goto failed;
+	}
+
+	ret = SCardListReaders(scard->ctx, NULL, readers, &len);
+	if (ret != SCARD_S_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed(2) "
+			   "(err=%ld)", ret);
+		goto failed;
+	}
+	if (len < 3) {
+		wpa_printf(MSG_WARNING, "SCARD: No smart card readers "
+			   "available.");
+		goto failed;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "SCARD: Readers", (u8 *) readers, len);
+	/*
+	 * readers is a list of available readers. The last entry is terminated
+	 * with double null.
+	 */
+	pos = 0;
+#ifdef UNICODE
+	/* TODO */
+#else /* UNICODE */
+	while (pos < len) {
+		if (reader == NULL ||
+		    os_strncmp(&readers[pos], reader, os_strlen(reader)) == 0)
+			break;
+		while (pos < len && readers[pos])
+			pos++;
+		pos++; /* skip separating null */
+		if (pos < len && readers[pos] == '\0')
+			pos = len; /* double null terminates list */
+	}
+#endif /* UNICODE */
+	if (pos >= len) {
+		wpa_printf(MSG_WARNING, "SCARD: No reader with prefix '%s' "
+			   "found", reader);
+		goto failed;
+	}
+
+#ifdef UNICODE
+	wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", &readers[pos]);
+#else /* UNICODE */
+	wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", &readers[pos]);
+#endif /* UNICODE */
+
+	ret = SCardConnect(scard->ctx, &readers[pos], SCARD_SHARE_SHARED,
+			   SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
+			   &scard->card, &scard->protocol);
+	if (ret != SCARD_S_SUCCESS) {
+		if (ret == (long) SCARD_E_NO_SMARTCARD)
+			wpa_printf(MSG_INFO, "No smart card inserted.");
+		else
+			wpa_printf(MSG_WARNING, "SCardConnect err=%lx", ret);
+		goto failed;
+	}
+
+	os_free(readers);
+	readers = NULL;
+
+	wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)",
+		   (unsigned int) scard->card, scard->protocol,
+		   scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1");
+
+	ret = SCardBeginTransaction(scard->card);
+	if (ret != SCARD_S_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "SCARD: Could not begin transaction: "
+			   "0x%x", (unsigned int) ret);
+		goto failed;
+	}
+	transaction = 1;
+
+	blen = sizeof(buf);
+
+	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) {
+		blen = sizeof(buf);
+		if (scard_select_file(scard, SCARD_FILE_MF, buf, &blen)) {
+			wpa_printf(MSG_DEBUG, "SCARD: Failed to read MF");
+			goto failed;
+		}
+
+		blen = sizeof(buf);
+		if (scard_select_file(scard, SCARD_FILE_GSM_DF, buf, &blen)) {
+			wpa_printf(MSG_DEBUG, "SCARD: Failed to read GSM DF");
+			goto failed;
+		}
+	} else {
+		unsigned char aid[32];
+		int aid_len;
+
+		aid_len = scard_get_aid(scard, aid, sizeof(aid));
+		if (aid_len < 0) {
+			wpa_printf(MSG_DEBUG, "SCARD: Failed to find AID for "
+				   "3G USIM app - try to use standard 3G RID");
+			os_memcpy(aid, "\xa0\x00\x00\x00\x87", 5);
+			aid_len = 5;
+		}
+		wpa_hexdump(MSG_DEBUG, "SCARD: 3G USIM AID", aid, aid_len);
+
+		/* Select based on AID = 3G RID from EF_DIR. This is usually
+		 * starting with A0 00 00 00 87. */
+		blen = sizeof(buf);
+		if (_scard_select_file(scard, 0, buf, &blen, scard->sim_type,
+				       aid, aid_len)) {
+			wpa_printf(MSG_INFO, "SCARD: Failed to read 3G USIM "
+				   "app");
+			wpa_hexdump(MSG_INFO, "SCARD: 3G USIM AID",
+				    aid, aid_len);
+			goto failed;
+		}
+	}
+
+	/* Verify whether CHV1 (PIN1) is needed to access the card. */
+	pin_needed = scard_pin_needed(scard, buf, blen);
+	if (pin_needed < 0) {
+		wpa_printf(MSG_DEBUG, "SCARD: Failed to determine whether PIN "
+			   "is needed");
+		goto failed;
+	}
+	if (pin_needed) {
+		scard->pin1_required = 1;
+		wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access (retry "
+			   "counter=%d)", scard_get_pin_retry_counter(scard));
+	}
+
+	ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
+	if (ret != SCARD_S_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "SCARD: Could not end transaction: "
+			   "0x%x", (unsigned int) ret);
+	}
+
+	return scard;
+
+failed:
+	if (transaction)
+		SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
+	os_free(readers);
+	scard_deinit(scard);
+	return NULL;
+}
+
+
+/**
+ * scard_set_pin - Set PIN (CHV1/PIN1) code for accessing SIM/USIM commands
+ * @scard: Pointer to private data from scard_init()
+ * @pin: PIN code as an ASCII string (e.g., "1234")
+ * Returns: 0 on success, -1 on failure
+ */
+int scard_set_pin(struct scard_data *scard, const char *pin)
+{
+	if (scard == NULL)
+		return -1;
+
+	/* Verify whether CHV1 (PIN1) is needed to access the card. */
+	if (scard->pin1_required) {
+		if (pin == NULL) {
+			wpa_printf(MSG_DEBUG, "No PIN configured for SIM "
+				   "access");
+			return -1;
+		}
+		if (scard_verify_pin(scard, pin)) {
+			wpa_printf(MSG_INFO, "PIN verification failed for "
+				"SIM access");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * scard_deinit - Deinitialize SIM/USIM connection
+ * @scard: Pointer to private data from scard_init()
+ *
+ * This function closes the SIM/USIM connect opened with scard_init().
+ */
+void scard_deinit(struct scard_data *scard)
+{
+	long ret;
+
+	if (scard == NULL)
+		return;
+
+	wpa_printf(MSG_DEBUG, "SCARD: deinitializing smart card interface");
+	if (scard->card) {
+		ret = SCardDisconnect(scard->card, SCARD_UNPOWER_CARD);
+		if (ret != SCARD_S_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "SCARD: Failed to disconnect "
+				   "smart card (err=%ld)", ret);
+		}
+	}
+
+	if (scard->ctx) {
+		ret = SCardReleaseContext(scard->ctx);
+		if (ret != SCARD_S_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "Failed to release smart card "
+				   "context (err=%ld)", ret);
+		}
+	}
+	os_free(scard);
+	mingw_unload_symbols();
+}
+
+
+static long scard_transmit(struct scard_data *scard,
+			   unsigned char *_send, size_t send_len,
+			   unsigned char *_recv, size_t *recv_len)
+{
+	long ret;
+	unsigned long rlen;
+
+	wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send",
+			_send, send_len);
+	rlen = *recv_len;
+	ret = SCardTransmit(scard->card,
+			    scard->protocol == SCARD_PROTOCOL_T1 ?
+			    SCARD_PCI_T1 : SCARD_PCI_T0,
+			    _send, (unsigned long) send_len,
+			    NULL, _recv, &rlen);
+	*recv_len = rlen;
+	if (ret == SCARD_S_SUCCESS) {
+		wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv",
+			    _recv, rlen);
+	} else {
+		wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed "
+			   "(err=0x%lx)", ret);
+	}
+	return ret;
+}
+
+
+static int _scard_select_file(struct scard_data *scard, unsigned short file_id,
+			      unsigned char *buf, size_t *buf_len,
+			      sim_types sim_type, unsigned char *aid,
+			      size_t aidlen)
+{
+	long ret;
+	unsigned char resp[3];
+	unsigned char cmd[50] = { SIM_CMD_SELECT };
+	int cmdlen;
+	unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE };
+	size_t len, rlen;
+
+	if (sim_type == SCARD_USIM) {
+		cmd[0] = USIM_CLA;
+		cmd[3] = 0x04;
+		get_resp[0] = USIM_CLA;
+	}
+
+	wpa_printf(MSG_DEBUG, "SCARD: select file %04x", file_id);
+	if (aid) {
+		wpa_hexdump(MSG_DEBUG, "SCARD: select file by AID",
+			    aid, aidlen);
+		if (5 + aidlen > sizeof(cmd))
+			return -1;
+		cmd[2] = 0x04; /* Select by AID */
+		cmd[4] = aidlen; /* len */
+		os_memcpy(cmd + 5, aid, aidlen);
+		cmdlen = 5 + aidlen;
+	} else {
+		cmd[5] = file_id >> 8;
+		cmd[6] = file_id & 0xff;
+		cmdlen = 7;
+	}
+	len = sizeof(resp);
+	ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
+	if (ret != SCARD_S_SUCCESS) {
+		wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed "
+			   "(err=0x%lx)", ret);
+		return -1;
+	}
+
+	if (len != 2) {
+		wpa_printf(MSG_WARNING, "SCARD: unexpected resp len "
+			   "%d (expected 2)", (int) len);
+		return -1;
+	}
+
+	if (resp[0] == 0x98 && resp[1] == 0x04) {
+		/* Security status not satisfied (PIN_WLAN) */
+		wpa_printf(MSG_WARNING, "SCARD: Security status not satisfied "
+			   "(PIN_WLAN)");
+		return -1;
+	}
+
+	if (resp[0] == 0x6e) {
+		wpa_printf(MSG_DEBUG, "SCARD: used CLA not supported");
+		return -1;
+	}
+
+	if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) {
+		wpa_printf(MSG_WARNING, "SCARD: unexpected response 0x%02x "
+			   "(expected 0x61, 0x6c, or 0x9f)", resp[0]);
+		return -1;
+	}
+	/* Normal ending of command; resp[1] bytes available */
+	get_resp[4] = resp[1];
+	wpa_printf(MSG_DEBUG, "SCARD: trying to get response (%d bytes)",
+		   resp[1]);
+
+	rlen = *buf_len;
+	ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &rlen);
+	if (ret == SCARD_S_SUCCESS) {
+		*buf_len = resp[1] < rlen ? resp[1] : rlen;
+		return 0;
+	}
+
+	wpa_printf(MSG_WARNING, "SCARD: SCardTransmit err=0x%lx\n", ret);
+	return -1;
+}
+
+
+static int scard_select_file(struct scard_data *scard, unsigned short file_id,
+			     unsigned char *buf, size_t *buf_len)
+{
+	return _scard_select_file(scard, file_id, buf, buf_len,
+				  scard->sim_type, NULL, 0);
+}
+
+
+static int scard_get_record_len(struct scard_data *scard, unsigned char recnum,
+				unsigned char mode)
+{
+	unsigned char buf[255];
+	unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ };
+	size_t blen;
+	long ret;
+
+	if (scard->sim_type == SCARD_USIM)
+		cmd[0] = USIM_CLA;
+	cmd[2] = recnum;
+	cmd[3] = mode;
+	cmd[4] = sizeof(buf);
+
+	blen = sizeof(buf);
+	ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
+	if (ret != SCARD_S_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "SCARD: failed to determine file "
+			   "length for record %d", recnum);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response",
+		    buf, blen);
+
+	if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) {
+		wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file "
+			   "length determination");
+		return -1;
+	}
+
+	return buf[1];
+}
+
+
+static int scard_read_record(struct scard_data *scard,
+			     unsigned char *data, size_t len,
+			     unsigned char recnum, unsigned char mode)
+{
+	unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ };
+	size_t blen = len + 3;
+	unsigned char *buf;
+	long ret;
+
+	if (scard->sim_type == SCARD_USIM)
+		cmd[0] = USIM_CLA;
+	cmd[2] = recnum;
+	cmd[3] = mode;
+	cmd[4] = len;
+
+	buf = os_malloc(blen);
+	if (buf == NULL)
+		return -1;
+
+	ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
+	if (ret != SCARD_S_SUCCESS) {
+		os_free(buf);
+		return -2;
+	}
+	if (blen != len + 2) {
+		wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected "
+			   "length %ld (expected %ld)",
+			   (long) blen, (long) len + 2);
+		os_free(buf);
+		return -3;
+	}
+
+	if (buf[len] != 0x90 || buf[len + 1] != 0x00) {
+		wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected "
+			   "status %02x %02x (expected 90 00)",
+			   buf[len], buf[len + 1]);
+		os_free(buf);
+		return -4;
+	}
+
+	os_memcpy(data, buf, len);
+	os_free(buf);
+
+	return 0;
+}
+
+
+static int scard_read_file(struct scard_data *scard,
+			   unsigned char *data, size_t len)
+{
+	unsigned char cmd[5] = { SIM_CMD_READ_BIN /* , len */ };
+	size_t blen = len + 3;
+	unsigned char *buf;
+	long ret;
+
+	cmd[4] = len;
+
+	buf = os_malloc(blen);
+	if (buf == NULL)
+		return -1;
+
+	if (scard->sim_type == SCARD_USIM)
+		cmd[0] = USIM_CLA;
+	ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
+	if (ret != SCARD_S_SUCCESS) {
+		os_free(buf);
+		return -2;
+	}
+	if (blen != len + 2) {
+		wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected "
+			   "length %ld (expected %ld)",
+			   (long) blen, (long) len + 2);
+		os_free(buf);
+		return -3;
+	}
+
+	if (buf[len] != 0x90 || buf[len + 1] != 0x00) {
+		wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected "
+			   "status %02x %02x (expected 90 00)",
+			   buf[len], buf[len + 1]);
+		os_free(buf);
+		return -4;
+	}
+
+	os_memcpy(data, buf, len);
+	os_free(buf);
+
+	return 0;
+}
+
+
+static int scard_verify_pin(struct scard_data *scard, const char *pin)
+{
+	long ret;
+	unsigned char resp[3];
+	unsigned char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 };
+	size_t len;
+
+	wpa_printf(MSG_DEBUG, "SCARD: verifying PIN");
+
+	if (pin == NULL || os_strlen(pin) > 8)
+		return -1;
+
+	if (scard->sim_type == SCARD_USIM)
+		cmd[0] = USIM_CLA;
+	os_memcpy(cmd + 5, pin, os_strlen(pin));
+	os_memset(cmd + 5 + os_strlen(pin), 0xff, 8 - os_strlen(pin));
+
+	len = sizeof(resp);
+	ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+	if (ret != SCARD_S_SUCCESS)
+		return -2;
+
+	if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) {
+		wpa_printf(MSG_WARNING, "SCARD: PIN verification failed");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "SCARD: PIN verified successfully");
+	return 0;
+}
+
+
+int scard_get_pin_retry_counter(struct scard_data *scard)
+{
+	long ret;
+	unsigned char resp[3];
+	unsigned char cmd[5] = { SIM_CMD_VERIFY_CHV1 };
+	size_t len;
+	u16 val;
+
+	wpa_printf(MSG_DEBUG, "SCARD: fetching PIN retry counter");
+
+	if (scard->sim_type == SCARD_USIM)
+		cmd[0] = USIM_CLA;
+	cmd[4] = 0; /* Empty data */
+
+	len = sizeof(resp);
+	ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+	if (ret != SCARD_S_SUCCESS)
+		return -2;
+
+	if (len != 2) {
+		wpa_printf(MSG_WARNING, "SCARD: failed to fetch PIN retry "
+			   "counter");
+		return -1;
+	}
+
+	val = WPA_GET_BE16(resp);
+	if (val == 0x63c0 || val == 0x6983) {
+		wpa_printf(MSG_DEBUG, "SCARD: PIN has been blocked");
+		return 0;
+	}
+
+	if (val >= 0x63c0 && val <= 0x63cf)
+		return val & 0x000f;
+
+	wpa_printf(MSG_DEBUG, "SCARD: Unexpected PIN retry counter response "
+		   "value 0x%x", val);
+	return 0;
+}
+
+
+/**
+ * scard_get_imsi - Read IMSI from SIM/USIM card
+ * @scard: Pointer to private data from scard_init()
+ * @imsi: Buffer for IMSI
+ * @len: Length of imsi buffer; set to IMSI length on success
+ * Returns: 0 on success, -1 if IMSI file cannot be selected, -2 if IMSI file
+ * selection returns invalid result code, -3 if parsing FSP template file fails
+ * (USIM only), -4 if IMSI does not fit in the provided imsi buffer (len is set
+ * to needed length), -5 if reading IMSI file fails.
+ *
+ * This function can be used to read IMSI from the SIM/USIM card. If the IMSI
+ * file is PIN protected, scard_set_pin() must have been used to set the
+ * correct PIN code before calling scard_get_imsi().
+ */
+int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len)
+{
+	unsigned char buf[100];
+	size_t blen, imsilen, i;
+	char *pos;
+
+	wpa_printf(MSG_DEBUG, "SCARD: reading IMSI from (GSM) EF-IMSI");
+	blen = sizeof(buf);
+	if (scard_select_file(scard, SCARD_FILE_GSM_EF_IMSI, buf, &blen))
+		return -1;
+	if (blen < 4) {
+		wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-IMSI "
+			   "header (len=%ld)", (long) blen);
+		return -2;
+	}
+
+	if (scard->sim_type == SCARD_GSM_SIM) {
+		blen = WPA_GET_BE16(&buf[2]);
+	} else {
+		int file_size;
+		if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
+			return -3;
+		blen = file_size;
+	}
+	if (blen < 2 || blen > sizeof(buf)) {
+		wpa_printf(MSG_DEBUG, "SCARD: invalid IMSI file length=%ld",
+			   (long) blen);
+		return -3;
+	}
+
+	imsilen = (blen - 2) * 2 + 1;
+	wpa_printf(MSG_DEBUG, "SCARD: IMSI file length=%ld imsilen=%ld",
+		   (long) blen, (long) imsilen);
+	if (blen < 2 || imsilen > *len) {
+		*len = imsilen;
+		return -4;
+	}
+
+	if (scard_read_file(scard, buf, blen))
+		return -5;
+
+	pos = imsi;
+	*pos++ = '0' + (buf[1] >> 4 & 0x0f);
+	for (i = 2; i < blen; i++) {
+		unsigned char digit;
+
+		digit = buf[i] & 0x0f;
+		if (digit < 10)
+			*pos++ = '0' + digit;
+		else
+			imsilen--;
+
+		digit = buf[i] >> 4 & 0x0f;
+		if (digit < 10)
+			*pos++ = '0' + digit;
+		else
+			imsilen--;
+	}
+	*len = imsilen;
+
+	return 0;
+}
+
+
+/**
+ * scard_get_mnc_len - Read length of MNC in the IMSI from SIM/USIM card
+ * @scard: Pointer to private data from scard_init()
+ * Returns: length (>0) on success, -1 if administrative data file cannot be
+ * selected, -2 if administrative data file selection returns invalid result
+ * code, -3 if parsing FSP template file fails (USIM only), -4 if length of
+ * the file is unexpected, -5 if reading file fails, -6 if MNC length is not
+ * in range (i.e. 2 or 3), -7 if MNC length is not available.
+ *
+ */
+int scard_get_mnc_len(struct scard_data *scard)
+{
+	unsigned char buf[100];
+	size_t blen;
+	int file_size;
+
+	wpa_printf(MSG_DEBUG, "SCARD: reading MNC len from (GSM) EF-AD");
+	blen = sizeof(buf);
+	if (scard_select_file(scard, SCARD_FILE_GSM_EF_AD, buf, &blen))
+		return -1;
+	if (blen < 4) {
+		wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-AD "
+			   "header (len=%ld)", (long) blen);
+		return -2;
+	}
+
+	if (scard->sim_type == SCARD_GSM_SIM) {
+		file_size = WPA_GET_BE16(&buf[2]);
+	} else {
+		if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
+			return -3;
+	}
+	if (file_size == 3) {
+		wpa_printf(MSG_DEBUG, "SCARD: MNC length not available");
+		return -7;
+	}
+	if (file_size < 4 || file_size > (int) sizeof(buf)) {
+		wpa_printf(MSG_DEBUG, "SCARD: invalid file length=%ld",
+			   (long) file_size);
+		return -4;
+	}
+
+	if (scard_read_file(scard, buf, file_size))
+		return -5;
+	buf[3] = buf[3] & 0x0f; /* upper nibble reserved for future use  */
+	if (buf[3] < 2 || buf[3] > 3) {
+		wpa_printf(MSG_DEBUG, "SCARD: invalid MNC length=%ld",
+			   (long) buf[3]);
+		return -6;
+	}
+	wpa_printf(MSG_DEBUG, "SCARD: MNC length=%ld", (long) buf[3]);
+	return buf[3];
+}
+
+
+/**
+ * scard_gsm_auth - Run GSM authentication command on SIM card
+ * @scard: Pointer to private data from scard_init()
+ * @_rand: 16-byte RAND value from HLR/AuC
+ * @sres: 4-byte buffer for SRES
+ * @kc: 8-byte buffer for Kc
+ * Returns: 0 on success, -1 if SIM/USIM connection has not been initialized,
+ * -2 if authentication command execution fails, -3 if unknown response code
+ * for authentication command is received, -4 if reading of response fails,
+ * -5 if if response data is of unexpected length
+ *
+ * This function performs GSM authentication using SIM/USIM card and the
+ * provided RAND value from HLR/AuC. If authentication command can be completed
+ * successfully, SRES and Kc values will be written into sres and kc buffers.
+ */
+int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand,
+		   unsigned char *sres, unsigned char *kc)
+{
+	unsigned char cmd[5 + 1 + 16] = { SIM_CMD_RUN_GSM_ALG };
+	int cmdlen;
+	unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE };
+	unsigned char resp[3], buf[12 + 3 + 2];
+	size_t len;
+	long ret;
+
+	if (scard == NULL)
+		return -1;
+
+	wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - RAND", _rand, 16);
+	if (scard->sim_type == SCARD_GSM_SIM) {
+		cmdlen = 5 + 16;
+		os_memcpy(cmd + 5, _rand, 16);
+	} else {
+		cmdlen = 5 + 1 + 16;
+		cmd[0] = USIM_CLA;
+		cmd[3] = 0x80;
+		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);
+	if (ret != SCARD_S_SUCCESS)
+		return -2;
+
+	if ((scard->sim_type == SCARD_GSM_SIM &&
+	     (len != 2 || resp[0] != 0x9f || resp[1] != 0x0c)) ||
+	    (scard->sim_type == SCARD_USIM &&
+	     (len != 2 || resp[0] != 0x61 || resp[1] != 0x0e))) {
+		wpa_printf(MSG_WARNING, "SCARD: unexpected response for GSM "
+			   "auth request (len=%ld resp=%02x %02x)",
+			   (long) len, resp[0], resp[1]);
+		return -3;
+	}
+	get_resp[4] = resp[1];
+
+	len = sizeof(buf);
+	ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len);
+	if (ret != SCARD_S_SUCCESS)
+		return -4;
+
+	if (scard->sim_type == SCARD_GSM_SIM) {
+		if (len != 4 + 8 + 2) {
+			wpa_printf(MSG_WARNING, "SCARD: unexpected data "
+				   "length for GSM auth (len=%ld, expected 14)",
+				   (long) len);
+			return -5;
+		}
+		os_memcpy(sres, buf, 4);
+		os_memcpy(kc, buf + 4, 8);
+	} else {
+		if (len != 1 + 4 + 1 + 8 + 2) {
+			wpa_printf(MSG_WARNING, "SCARD: unexpected data "
+				   "length for USIM auth (len=%ld, "
+				   "expected 16)", (long) len);
+			return -5;
+		}
+		if (buf[0] != 4 || buf[5] != 8) {
+			wpa_printf(MSG_WARNING, "SCARD: unexpected SREC/Kc "
+				   "length (%d %d, expected 4 8)",
+				   buf[0], buf[5]);
+		}
+		os_memcpy(sres, buf + 1, 4);
+		os_memcpy(kc, buf + 6, 8);
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - SRES", sres, 4);
+	wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - Kc", kc, 8);
+
+	return 0;
+}
+
+
+/**
+ * scard_umts_auth - Run UMTS authentication command on USIM card
+ * @scard: Pointer to private data from scard_init()
+ * @_rand: 16-byte RAND value from HLR/AuC
+ * @autn: 16-byte AUTN value from HLR/AuC
+ * @res: 16-byte buffer for RES
+ * @res_len: Variable that will be set to RES length
+ * @ik: 16-byte buffer for IK
+ * @ck: 16-byte buffer for CK
+ * @auts: 14-byte buffer for AUTS
+ * Returns: 0 on success, -1 on failure, or -2 if USIM reports synchronization
+ * failure
+ *
+ * This function performs AKA authentication using USIM card and the provided
+ * RAND and AUTN values from HLR/AuC. If authentication command can be
+ * completed successfully, RES, IK, and CK values will be written into provided
+ * buffers and res_len is set to length of received RES value. If USIM reports
+ * synchronization failure, the received AUTS value will be written into auts
+ * buffer. In this case, RES, IK, and CK are not valid.
+ */
+int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand,
+		    const unsigned char *autn,
+		    unsigned char *res, size_t *res_len,
+		    unsigned char *ik, unsigned char *ck, unsigned char *auts)
+{
+	unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] =
+		{ USIM_CMD_RUN_UMTS_ALG };
+	unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE };
+	unsigned char resp[3], buf[64], *pos, *end;
+	size_t len;
+	long ret;
+
+	if (scard == NULL)
+		return -1;
+
+	if (scard->sim_type == SCARD_GSM_SIM) {
+		wpa_printf(MSG_ERROR, "SCARD: Non-USIM card - cannot do UMTS "
+			   "auth");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - RAND", _rand, AKA_RAND_LEN);
+	wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - AUTN", autn, AKA_AUTN_LEN);
+	cmd[5] = AKA_RAND_LEN;
+	os_memcpy(cmd + 6, _rand, AKA_RAND_LEN);
+	cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN;
+	os_memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN);
+
+	len = sizeof(resp);
+	ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+	if (ret != SCARD_S_SUCCESS)
+		return -1;
+
+	if (len <= sizeof(resp))
+		wpa_hexdump(MSG_DEBUG, "SCARD: UMTS alg response", resp, len);
+
+	if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) {
+		wpa_printf(MSG_WARNING, "SCARD: UMTS auth failed - "
+			   "MAC != XMAC");
+		return -1;
+	} else if (len != 2 || resp[0] != 0x61) {
+		wpa_printf(MSG_WARNING, "SCARD: unexpected response for UMTS "
+			   "auth request (len=%ld resp=%02x %02x)",
+			   (long) len, resp[0], resp[1]);
+		return -1;
+	}
+	get_resp[4] = resp[1];
+
+	len = sizeof(buf);
+	ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len);
+	if (ret != SCARD_S_SUCCESS || len > sizeof(buf))
+		return -1;
+
+	wpa_hexdump(MSG_DEBUG, "SCARD: UMTS get response result", buf, len);
+	if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc &&
+	    buf[1] == AKA_AUTS_LEN) {
+		wpa_printf(MSG_DEBUG, "SCARD: UMTS Synchronization-Failure");
+		os_memcpy(auts, buf + 2, AKA_AUTS_LEN);
+		wpa_hexdump(MSG_DEBUG, "SCARD: AUTS", auts, AKA_AUTS_LEN);
+		return -2;
+	} else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) {
+		pos = buf + 1;
+		end = buf + len;
+
+		/* RES */
+		if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) {
+			wpa_printf(MSG_DEBUG, "SCARD: Invalid RES");
+			return -1;
+		}
+		*res_len = *pos++;
+		os_memcpy(res, pos, *res_len);
+		pos += *res_len;
+		wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len);
+
+		/* CK */
+		if (pos[0] != CK_LEN || pos + CK_LEN > end) {
+			wpa_printf(MSG_DEBUG, "SCARD: Invalid CK");
+			return -1;
+		}
+		pos++;
+		os_memcpy(ck, pos, CK_LEN);
+		pos += CK_LEN;
+		wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN);
+
+		/* IK */
+		if (pos[0] != IK_LEN || pos + IK_LEN > end) {
+			wpa_printf(MSG_DEBUG, "SCARD: Invalid IK");
+			return -1;
+		}
+		pos++;
+		os_memcpy(ik, pos, IK_LEN);
+		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;
+	}
+
+	wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response");
+	return -1;
+}
+
+
+int scard_supports_umts(struct scard_data *scard)
+{
+	return scard->sim_type == SCARD_USIM;
+}
diff --git a/hostap/src/utils/pcsc_funcs.h b/hostap/src/utils/pcsc_funcs.h
new file mode 100644
index 0000000..eacd2a2
--- /dev/null
+++ b/hostap/src/utils/pcsc_funcs.h
@@ -0,0 +1,42 @@
+/*
+ * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
+ * Copyright (c) 2004-2006, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PCSC_FUNCS_H
+#define PCSC_FUNCS_H
+
+#ifdef PCSC_FUNCS
+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);
+int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len);
+int scard_get_mnc_len(struct scard_data *scard);
+int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand,
+		   unsigned char *sres, unsigned char *kc);
+int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand,
+		    const unsigned char *autn,
+		    unsigned char *res, size_t *res_len,
+		    unsigned char *ik, unsigned char *ck, unsigned char *auts);
+int scard_get_pin_retry_counter(struct scard_data *scard);
+int scard_supports_umts(struct scard_data *scard);
+
+#else /* PCSC_FUNCS */
+
+#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
+#define scard_get_mnc_len(s) -1
+#define scard_gsm_auth(s, r, s2, k) -1
+#define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1
+#define scard_get_pin_retry_counter(s) -1
+#define scard_supports_umts(s) 0
+
+#endif /* PCSC_FUNCS */
+
+#endif /* PCSC_FUNCS_H */
diff --git a/hostap/src/utils/platform.h b/hostap/src/utils/platform.h
new file mode 100644
index 0000000..46cfe78
--- /dev/null
+++ b/hostap/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/hostap/src/utils/radiotap.c b/hostap/src/utils/radiotap.c
new file mode 100644
index 0000000..c9a5023
--- /dev/null
+++ b/hostap/src/utils/radiotap.c
@@ -0,0 +1,396 @@
+/*
+ * 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
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See COPYING for more details.
+ */
+#include "radiotap_iter.h"
+#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
+ * @radiotap_header: radiotap header to parse
+ * @max_length: total length we can parse into (eg, whole packet length)
+ *
+ * Returns: 0 or a negative error code if there is a problem.
+ *
+ * This function initializes an opaque iterator struct which can then
+ * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap
+ * argument which is present in the header.  It knows about extended
+ * present headers and handles them.
+ *
+ * How to use:
+ * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator
+ * struct ieee80211_radiotap_iterator (no need to init the struct beforehand)
+ * checking for a good 0 return code.  Then loop calling
+ * __ieee80211_radiotap_iterator_next()... it returns either 0,
+ * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem.
+ * The iterator's @this_arg member points to the start of the argument
+ * associated with the current argument index that is present, which can be
+ * found in the iterator's @this_arg_index member.  This arg index corresponds
+ * to the IEEE80211_RADIOTAP_... defines.
+ *
+ * Radiotap header length:
+ * You can find the CPU-endian total radiotap header length in
+ * iterator->max_length after executing ieee80211_radiotap_iterator_init()
+ * successfully.
+ *
+ * Alignment Gotcha:
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned.  Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ *
+ * Example code: parse.c
+ */
+
+int ieee80211_radiotap_iterator_init(
+	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 < get_unaligned_le16(&radiotap_header->it_len))
+		return -EINVAL;
+
+	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->_next_ns_data = NULL;
+	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 (iterator->_bitmap_shifter & BIT(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) &
+		       BIT(IEEE80211_RADIOTAP_EXT)) {
+			iterator->_arg += sizeof(uint32_t);
+
+			/*
+			 * check for insanity where the present bitmaps
+			 * keep claiming to extend up to or even beyond the
+			 * stated radiotap header length
+			 */
+
+			if ((unsigned long)iterator->_arg -
+			    (unsigned long)iterator->_rtheader +
+			    sizeof(uint32_t) >
+			    (unsigned long)iterator->_max_length)
+				return -EINVAL;
+		}
+
+		iterator->_arg += sizeof(uint32_t);
+
+		/*
+		 * no need to check again for blowing past stated radiotap
+		 * header length, because ieee80211_radiotap_iterator_next
+		 * checks it before it is dereferenced
+		 */
+	}
+
+	iterator->this_arg = iterator->_arg;
+	iterator->this_arg_index = 0;
+	iterator->this_arg_size = 0;
+
+	/* 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
+ * @iterator: radiotap_iterator to move to next arg (if any)
+ *
+ * Returns: 0 if there is an argument to handle,
+ * -ENOENT if there are no more args or -EINVAL
+ * if there is something else wrong.
+ *
+ * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*)
+ * in @this_arg_index and sets @this_arg to point to the
+ * payload for the field.  It takes care of alignment handling and extended
+ * present fields.  @this_arg can be changed by the caller (eg,
+ * incremented to move inside a compound argument like
+ * IEEE80211_RADIOTAP_CHANNEL).  The args pointed to are in
+ * little-endian format whatever the endianess of your CPU.
+ *
+ * Alignment Gotcha:
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned.  Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ */
+
+int ieee80211_radiotap_iterator_next(
+	struct ieee80211_radiotap_iterator *iterator)
+{
+	while (1) {
+		int hit = 0;
+		int pad, align, size, subns;
+		uint32_t oui;
+
+		/* 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
+		 *
+		 * 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.
+		 */
+
+		pad = ((unsigned long)iterator->_arg -
+		       (unsigned long)iterator->_rtheader) & (align - 1);
+
+		if (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;
+		iterator->this_arg_size = size;
+
+		/* internally move on the size of this arg */
+		iterator->_arg += size;
+
+		/*
+		 * check for insanity where we are given a bitmap that
+		 * claims to have more arg content than the length of the
+		 * radiotap section.  We will normally end up equalling this
+		 * max_length on the last arg, never exceeding it.
+		 */
+
+		if ((unsigned long)iterator->_arg -
+		    (unsigned long)iterator->_rtheader >
+		    (unsigned long)iterator->_max_length)
+			return -EINVAL;
+
+		/* 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;
+	}
+}
diff --git a/hostap/src/utils/radiotap.h b/hostap/src/utils/radiotap.h
new file mode 100644
index 0000000..0572e7c
--- /dev/null
+++ b/hostap/src/utils/radiotap.h
@@ -0,0 +1,291 @@
+/*-
+ * Copyright (c) 2003, 2004 David Young.  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. The name of David Young may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``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 DAVID
+ * YOUNG 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.
+ */
+
+/*
+ * Modifications to fit into the linux IEEE 802.11 stack,
+ * Mike Kershaw (dragorn@kismetwireless.net)
+ */
+
+#ifndef IEEE80211RADIOTAP_H
+#define IEEE80211RADIOTAP_H
+
+#include <stdint.h>
+
+/* Base version of the radiotap packet header data */
+#define PKTHDR_RADIOTAP_VERSION		0
+
+/* A generic radio capture format is desirable. There is one for
+ * Linux, but it is neither rigidly defined (there were not even
+ * units given for some fields) nor easily extensible.
+ *
+ * I suggest the following extensible radio capture format. It is
+ * based on a bitmap indicating which fields are present.
+ *
+ * I am trying to describe precisely what the application programmer
+ * should expect in the following, and for that reason I tell the
+ * units and origin of each measurement (where it applies), or else I
+ * use sufficiently weaselly language ("is a monotonically nondecreasing
+ * function of...") that I cannot set false expectations for lawyerly
+ * readers.
+ */
+
+/* The radio capture header precedes the 802.11 header.
+ * All data in the header is little endian on all platforms.
+ */
+struct ieee80211_radiotap_header {
+	uint8_t it_version;	/* Version 0. Only increases
+				 * for drastic changes,
+				 * introduction of compatible
+				 * new fields does not count.
+				 */
+	uint8_t it_pad;
+	uint16_t it_len;	/* length of the whole
+				 * header in bytes, including
+				 * it_version, it_pad,
+				 * it_len, and data fields.
+				 */
+	uint32_t it_present;	/* A bitmap telling which
+				 * fields are present. Set bit 31
+				 * (0x80000000) to extend the
+				 * bitmap by another 32 bits.
+				 * Additional extensions are made
+				 * by setting bit 31.
+				 */
+};
+
+/* Name                                 Data type    Units
+ * ----                                 ---------    -----
+ *
+ * IEEE80211_RADIOTAP_TSFT              __le64       microseconds
+ *
+ *      Value in microseconds of the MAC's 64-bit 802.11 Time
+ *      Synchronization Function timer when the first bit of the
+ *      MPDU arrived at the MAC. For received frames, only.
+ *
+ * IEEE80211_RADIOTAP_CHANNEL           2 x uint16_t   MHz, bitmap
+ *
+ *      Tx/Rx frequency in MHz, followed by flags (see below).
+ *
+ * IEEE80211_RADIOTAP_FHSS              uint16_t       see below
+ *
+ *      For frequency-hopping radios, the hop set (first byte)
+ *      and pattern (second byte).
+ *
+ * IEEE80211_RADIOTAP_RATE              u8           500kb/s
+ *
+ *      Tx/Rx data rate
+ *
+ * IEEE80211_RADIOTAP_DBM_ANTSIGNAL     s8           decibels from
+ *                                                   one milliwatt (dBm)
+ *
+ *      RF signal power at the antenna, decibel difference from
+ *      one milliwatt.
+ *
+ * IEEE80211_RADIOTAP_DBM_ANTNOISE      s8           decibels from
+ *                                                   one milliwatt (dBm)
+ *
+ *      RF noise power at the antenna, decibel difference from one
+ *      milliwatt.
+ *
+ * IEEE80211_RADIOTAP_DB_ANTSIGNAL      u8           decibel (dB)
+ *
+ *      RF signal power at the antenna, decibel difference from an
+ *      arbitrary, fixed reference.
+ *
+ * IEEE80211_RADIOTAP_DB_ANTNOISE       u8           decibel (dB)
+ *
+ *      RF noise power at the antenna, decibel difference from an
+ *      arbitrary, fixed reference point.
+ *
+ * IEEE80211_RADIOTAP_LOCK_QUALITY      uint16_t       unitless
+ *
+ *      Quality of Barker code lock. Unitless. Monotonically
+ *      nondecreasing with "better" lock strength. Called "Signal
+ *      Quality" in datasheets.  (Is there a standard way to measure
+ *      this?)
+ *
+ * IEEE80211_RADIOTAP_TX_ATTENUATION    uint16_t       unitless
+ *
+ *      Transmit power expressed as unitless distance from max
+ *      power set at factory calibration.  0 is max power.
+ *      Monotonically nondecreasing with lower power levels.
+ *
+ * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t       decibels (dB)
+ *
+ *      Transmit power expressed as decibel distance from max power
+ *      set at factory calibration.  0 is max power.  Monotonically
+ *      nondecreasing with lower power levels.
+ *
+ * IEEE80211_RADIOTAP_DBM_TX_POWER      s8           decibels from
+ *                                                   one milliwatt (dBm)
+ *
+ *      Transmit power expressed as dBm (decibels from a 1 milliwatt
+ *      reference). This is the absolute power level measured at
+ *      the antenna port.
+ *
+ * IEEE80211_RADIOTAP_FLAGS             u8           bitmap
+ *
+ *      Properties of transmitted and received frames. See flags
+ *      defined below.
+ *
+ * IEEE80211_RADIOTAP_ANTENNA           u8           antenna index
+ *
+ *      Unitless indication of the Rx/Tx antenna for this packet.
+ *      The first antenna is antenna 0.
+ *
+ * IEEE80211_RADIOTAP_RX_FLAGS          uint16_t       bitmap
+ *
+ *     Properties of received frames. See flags defined below.
+ *
+ * IEEE80211_RADIOTAP_TX_FLAGS          uint16_t       bitmap
+ *
+ *     Properties of transmitted frames. See flags defined below.
+ *
+ * IEEE80211_RADIOTAP_RTS_RETRIES       u8           data
+ *
+ *     Number of rts retries a transmitted frame used.
+ *
+ * IEEE80211_RADIOTAP_DATA_RETRIES      u8           data
+ *
+ *     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,
+	IEEE80211_RADIOTAP_FLAGS = 1,
+	IEEE80211_RADIOTAP_RATE = 2,
+	IEEE80211_RADIOTAP_CHANNEL = 3,
+	IEEE80211_RADIOTAP_FHSS = 4,
+	IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5,
+	IEEE80211_RADIOTAP_DBM_ANTNOISE = 6,
+	IEEE80211_RADIOTAP_LOCK_QUALITY = 7,
+	IEEE80211_RADIOTAP_TX_ATTENUATION = 8,
+	IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9,
+	IEEE80211_RADIOTAP_DBM_TX_POWER = 10,
+	IEEE80211_RADIOTAP_ANTENNA = 11,
+	IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
+	IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
+	IEEE80211_RADIOTAP_RX_FLAGS = 14,
+	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
+};
+
+/* Channel flags. */
+#define	IEEE80211_CHAN_TURBO	0x0010	/* Turbo channel */
+#define	IEEE80211_CHAN_CCK	0x0020	/* CCK channel */
+#define	IEEE80211_CHAN_OFDM	0x0040	/* OFDM channel */
+#define	IEEE80211_CHAN_2GHZ	0x0080	/* 2 GHz spectrum channel. */
+#define	IEEE80211_CHAN_5GHZ	0x0100	/* 5 GHz spectrum channel */
+#define	IEEE80211_CHAN_PASSIVE	0x0200	/* Only passive scan allowed */
+#define	IEEE80211_CHAN_DYN	0x0400	/* Dynamic CCK-OFDM channel */
+#define	IEEE80211_CHAN_GFSK	0x0800	/* GFSK channel (FHSS PHY) */
+
+/* For IEEE80211_RADIOTAP_FLAGS */
+#define	IEEE80211_RADIOTAP_F_CFP	0x01	/* sent/received
+						 * during CFP
+						 */
+#define	IEEE80211_RADIOTAP_F_SHORTPRE	0x02	/* sent/received
+						 * with short
+						 * preamble
+						 */
+#define	IEEE80211_RADIOTAP_F_WEP	0x04	/* sent/received
+						 * with WEP encryption
+						 */
+#define	IEEE80211_RADIOTAP_F_FRAG	0x08	/* sent/received
+						 * with fragmentation
+						 */
+#define	IEEE80211_RADIOTAP_F_FCS	0x10	/* frame includes FCS */
+#define	IEEE80211_RADIOTAP_F_DATAPAD	0x20	/* frame has padding between
+						 * 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_BADPLCP	0x0002 /* bad PLCP */
+
+/* For IEEE80211_RADIOTAP_TX_FLAGS */
+#define IEEE80211_RADIOTAP_F_TX_FAIL	0x0001	/* failed due to excessive
+						 * retries */
+#define IEEE80211_RADIOTAP_F_TX_CTS	0x0002	/* used cts 'protection' */
+#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/hostap/src/utils/radiotap_iter.h b/hostap/src/utils/radiotap_iter.h
new file mode 100644
index 0000000..b768c85
--- /dev/null
+++ b/hostap/src/utils/radiotap_iter.h
@@ -0,0 +1,96 @@
+#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
+ * @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;
+	const struct ieee80211_radiotap_vendor_namespaces *_vns;
+	const struct ieee80211_radiotap_namespace *current_namespace;
+
+	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, const struct ieee80211_radiotap_vendor_namespaces *vns);
+
+extern int ieee80211_radiotap_iterator_next(
+	struct ieee80211_radiotap_iterator *iterator);
+
+#endif /* __RADIOTAP_ITER_H */
diff --git a/hostap/src/utils/state_machine.h b/hostap/src/utils/state_machine.h
new file mode 100644
index 0000000..a514315
--- /dev/null
+++ b/hostap/src/utils/state_machine.h
@@ -0,0 +1,138 @@
+/*
+ * wpa_supplicant/hostapd - State machine definitions
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file includes a set of pre-processor macros that can be used to
+ * implement a state machine. In addition to including this header file, each
+ * file implementing a state machine must define STATE_MACHINE_DATA to be the
+ * data structure including state variables (enum machine_state,
+ * Boolean changed), and STATE_MACHINE_DEBUG_PREFIX to be a string that is used
+ * as a prefix for all debug messages. If SM_ENTRY_MA macro is used to define
+ * a group of state machines with shared data structure, STATE_MACHINE_ADDR
+ * needs to be defined to point to the MAC address used in debug output.
+ * SM_ENTRY_M macro can be used to define similar group of state machines
+ * without this additional debug info.
+ */
+
+#ifndef STATE_MACHINE_H
+#define STATE_MACHINE_H
+
+/**
+ * SM_STATE - Declaration of a state machine function
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is used to declare a state machine function. It is used in place
+ * of a C function definition to declare functions to be run when the state is
+ * entered by calling SM_ENTER or SM_ENTER_GLOBAL.
+ */
+#define SM_STATE(machine, state) \
+static void sm_ ## machine ## _ ## state ## _Enter(STATE_MACHINE_DATA *sm, \
+	int global)
+
+/**
+ * SM_ENTRY - State machine function entry point
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is used inside each state machine function declared with
+ * SM_STATE. SM_ENTRY should be in the beginning of the function body, but
+ * after declaration of possible local variables. This macro prints debug
+ * information about state transition and update the state machine state.
+ */
+#define SM_ENTRY(machine, state) \
+if (!global || sm->machine ## _state != machine ## _ ## state) { \
+	sm->changed = TRUE; \
+	wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " #machine \
+		   " entering state " #state); \
+} \
+sm->machine ## _state = machine ## _ ## state;
+
+/**
+ * SM_ENTRY_M - State machine function entry point for state machine group
+ * @machine: State machine name
+ * @_state: State machine state
+ * @data: State variable prefix (full variable: prefix_state)
+ *
+ * This macro is like SM_ENTRY, but for state machine groups that use a shared
+ * data structure for more than one state machine. Both machine and prefix
+ * parameters are set to "sub-state machine" name. prefix is used to allow more
+ * than one state variable to be stored in the same data structure.
+ */
+#define SM_ENTRY_M(machine, _state, data) \
+if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \
+	sm->changed = TRUE; \
+	wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " \
+		   #machine " entering state " #_state); \
+} \
+sm->data ## _ ## state = machine ## _ ## _state;
+
+/**
+ * SM_ENTRY_MA - State machine function entry point for state machine group
+ * @machine: State machine name
+ * @_state: State machine state
+ * @data: State variable prefix (full variable: prefix_state)
+ *
+ * This macro is like SM_ENTRY_M, but a MAC address is included in debug
+ * output. STATE_MACHINE_ADDR has to be defined to point to the MAC address to
+ * be included in debug.
+ */
+#define SM_ENTRY_MA(machine, _state, data) \
+if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \
+	sm->changed = TRUE; \
+	wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " MACSTR " " \
+		   #machine " entering state " #_state, \
+		   MAC2STR(STATE_MACHINE_ADDR)); \
+} \
+sm->data ## _ ## state = machine ## _ ## _state;
+
+/**
+ * SM_ENTER - Enter a new state machine state
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro expands to a function call to a state machine function defined
+ * with SM_STATE macro. SM_ENTER is used in a state machine step function to
+ * move the state machine to a new state.
+ */
+#define SM_ENTER(machine, state) \
+sm_ ## machine ## _ ## state ## _Enter(sm, 0)
+
+/**
+ * SM_ENTER_GLOBAL - Enter a new state machine state based on global rule
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is like SM_ENTER, but this is used when entering a new state
+ * based on a global (not specific to any particular state) rule. A separate
+ * macro is used to avoid unwanted debug message floods when the same global
+ * rule is forcing a state machine to remain in on state.
+ */
+#define SM_ENTER_GLOBAL(machine, state) \
+sm_ ## machine ## _ ## state ## _Enter(sm, 1)
+
+/**
+ * SM_STEP - Declaration of a state machine step function
+ * @machine: State machine name
+ *
+ * This macro is used to declare a state machine step function. It is used in
+ * place of a C function definition to declare a function that is used to move
+ * state machine to a new state based on state variables. This function uses
+ * SM_ENTER and SM_ENTER_GLOBAL macros to enter new state.
+ */
+#define SM_STEP(machine) \
+static void sm_ ## machine ## _Step(STATE_MACHINE_DATA *sm)
+
+/**
+ * SM_STEP_RUN - Call the state machine step function
+ * @machine: State machine name
+ *
+ * This macro expands to a function call to a state machine step function
+ * defined with SM_STEP macro.
+ */
+#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)
+
+#endif /* STATE_MACHINE_H */
diff --git a/hostap/src/utils/trace.c b/hostap/src/utils/trace.c
new file mode 100644
index 0000000..06f3c28
--- /dev/null
+++ b/hostap/src/utils/trace.c
@@ -0,0 +1,369 @@
+/*
+ * Backtrace debugging
+ * 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 "trace.h"
+
+#if defined(WPA_TRACE) || defined(WPA_TRACE_SIGNALS)
+
+static struct dl_list active_references =
+{ &active_references, &active_references };
+
+#ifdef WPA_TRACE_BFD
+#include <bfd.h>
+
+#define DMGL_PARAMS      (1 << 0)
+#define DMGL_ANSI        (1 << 1)
+
+static char *prg_fname = NULL;
+static bfd *cached_abfd = NULL;
+static asymbol **syms = NULL;
+
+static void get_prg_fname(void)
+{
+	char exe[50], fname[512];
+	int len;
+	os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid());
+	len = readlink(exe, fname, sizeof(fname) - 1);
+	if (len < 0 || len >= (int) sizeof(fname)) {
+		wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno));
+		return;
+	}
+	fname[len] = '\0';
+	prg_fname = strdup(fname);
+}
+
+
+static bfd * open_bfd(const char *fname)
+{
+	bfd *abfd;
+	char **matching;
+
+	abfd = bfd_openr(prg_fname, NULL);
+	if (abfd == NULL) {
+		wpa_printf(MSG_INFO, "bfd_openr failed");
+		return NULL;
+	}
+
+	if (bfd_check_format(abfd, bfd_archive)) {
+		wpa_printf(MSG_INFO, "bfd_check_format failed");
+		bfd_close(abfd);
+		return NULL;
+	}
+
+	if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
+		wpa_printf(MSG_INFO, "bfd_check_format_matches failed");
+		free(matching);
+		bfd_close(abfd);
+		return NULL;
+	}
+
+	return abfd;
+}
+
+
+static void read_syms(bfd *abfd)
+{
+	long storage, symcount;
+	bfd_boolean dynamic = FALSE;
+
+	if (syms)
+		return;
+
+	if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) {
+		wpa_printf(MSG_INFO, "No symbols");
+		return;
+	}
+
+	storage = bfd_get_symtab_upper_bound(abfd);
+	if (storage == 0) {
+		storage = bfd_get_dynamic_symtab_upper_bound(abfd);
+		dynamic = TRUE;
+	}
+	if (storage < 0) {
+		wpa_printf(MSG_INFO, "Unknown symtab upper bound");
+		return;
+	}
+
+	syms = malloc(storage);
+	if (syms == NULL) {
+		wpa_printf(MSG_INFO, "Failed to allocate memory for symtab "
+			   "(%ld bytes)", storage);
+		return;
+	}
+	if (dynamic)
+		symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
+	else
+		symcount = bfd_canonicalize_symtab(abfd, syms);
+	if (symcount < 0) {
+		wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab",
+			   dynamic ? "dynamic " : "");
+		free(syms);
+		syms = NULL;
+		return;
+	}
+}
+
+
+struct bfd_data {
+	bfd_vma pc;
+	bfd_boolean found;
+	const char *filename;
+	const char *function;
+	unsigned int line;
+};
+
+
+static void find_addr_sect(bfd *abfd, asection *section, void *obj)
+{
+	struct bfd_data *data = obj;
+	bfd_vma vma;
+	bfd_size_type size;
+
+	if (data->found)
+		return;
+
+	if (!(bfd_get_section_vma(abfd, section)))
+		return;
+
+	vma = bfd_get_section_vma(abfd, section);
+	if (data->pc < vma)
+		return;
+
+	size = bfd_get_section_size(section);
+	if (data->pc >= vma + size)
+		return;
+
+	data->found = bfd_find_nearest_line(abfd, section, syms,
+					    data->pc - vma,
+					    &data->filename,
+					    &data->function,
+					    &data->line);
+}
+
+
+static void wpa_trace_bfd_addr(void *pc)
+{
+	bfd *abfd = cached_abfd;
+	struct bfd_data data;
+	const char *name;
+	char *aname = NULL;
+	const char *filename;
+
+	if (abfd == NULL)
+		return;
+
+	data.pc = (bfd_hostptr_t) pc;
+	data.found = FALSE;
+	bfd_map_over_sections(abfd, find_addr_sect, &data);
+
+	if (!data.found)
+		return;
+
+	do {
+		if (data.function)
+			aname = bfd_demangle(abfd, data.function,
+					     DMGL_ANSI | DMGL_PARAMS);
+		name = aname ? aname : data.function;
+		filename = data.filename;
+		if (filename) {
+			char *end = os_strrchr(filename, '/');
+			int i = 0;
+			while (*filename && *filename == prg_fname[i] &&
+			       filename <= end) {
+				filename++;
+				i++;
+			}
+		}
+		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);
+	} while (data.found);
+}
+
+
+static const char * wpa_trace_bfd_addr2func(void *pc)
+{
+	bfd *abfd = cached_abfd;
+	struct bfd_data data;
+
+	if (abfd == NULL)
+		return NULL;
+
+	data.pc = (bfd_hostptr_t) pc;
+	data.found = FALSE;
+	bfd_map_over_sections(abfd, find_addr_sect, &data);
+
+	if (!data.found)
+		return NULL;
+
+	return data.function;
+}
+
+
+static void wpa_trace_bfd_init(void)
+{
+	if (!prg_fname) {
+		get_prg_fname();
+		if (!prg_fname)
+			return;
+	}
+
+	if (!cached_abfd) {
+		cached_abfd = open_bfd(prg_fname);
+		if (!cached_abfd) {
+			wpa_printf(MSG_INFO, "Failed to open bfd");
+			return;
+		}
+	}
+
+	read_syms(cached_abfd);
+	if (!syms) {
+		wpa_printf(MSG_INFO, "Failed to read symbols");
+		return;
+	}
+}
+
+
+void wpa_trace_dump_funcname(const char *title, void *pc)
+{
+	wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
+	wpa_trace_bfd_init();
+	wpa_trace_bfd_addr(pc);
+}
+
+
+size_t wpa_trace_calling_func(const char *buf[], size_t len)
+{
+	bfd *abfd;
+	void *btrace_res[WPA_TRACE_LEN];
+	int i, btrace_num;
+	size_t pos = 0;
+
+	if (len == 0)
+		return 0;
+	if (len > WPA_TRACE_LEN)
+		len = WPA_TRACE_LEN;
+
+	wpa_trace_bfd_init();
+	abfd = cached_abfd;
+	if (!abfd)
+		return 0;
+
+	btrace_num = backtrace(btrace_res, len);
+	if (btrace_num < 1)
+		return 0;
+
+	for (i = 0; i < btrace_num; i++) {
+		struct bfd_data data;
+
+		data.pc = (bfd_hostptr_t) btrace_res[i];
+		data.found = FALSE;
+		bfd_map_over_sections(abfd, find_addr_sect, &data);
+
+		while (data.found) {
+			if (data.function &&
+			    (pos > 0 ||
+			     os_strcmp(data.function, __func__) != 0)) {
+				buf[pos++] = data.function;
+				if (pos == len)
+					return pos;
+			}
+
+			data.found = bfd_find_inliner_info(abfd, &data.filename,
+							   &data.function,
+							   &data.line);
+		}
+	}
+
+	return pos;
+}
+
+#else /* WPA_TRACE_BFD */
+
+#define wpa_trace_bfd_init() do { } while (0)
+#define wpa_trace_bfd_addr(pc) do { } while (0)
+#define wpa_trace_bfd_addr2func(pc) NULL
+
+#endif /* WPA_TRACE_BFD */
+
+void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
+{
+	char **sym;
+	int i;
+	enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
+
+	wpa_trace_bfd_init();
+	wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
+	sym = backtrace_symbols(btrace, btrace_num);
+	state = TRACE_HEAD;
+	for (i = 0; i < btrace_num; i++) {
+		const char *func = wpa_trace_bfd_addr2func(btrace[i]);
+		if (state == TRACE_HEAD && func &&
+		    (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
+		     os_strcmp(func, "wpa_trace_check_ref") == 0 ||
+		     os_strcmp(func, "wpa_trace_show") == 0))
+			continue;
+		if (state == TRACE_TAIL && sym && sym[i] &&
+		    os_strstr(sym[i], "__libc_start_main"))
+			break;
+		if (state == TRACE_HEAD)
+			state = TRACE_RELEVANT;
+		if (sym)
+			wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
+		else
+			wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
+		wpa_trace_bfd_addr(btrace[i]);
+		if (state == TRACE_RELEVANT && func &&
+		    os_strcmp(func, "main") == 0)
+			state = TRACE_TAIL;
+	}
+	free(sym);
+	wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
+}
+
+
+void wpa_trace_show(const char *title)
+{
+	struct info {
+		WPA_TRACE_INFO
+	} info;
+	wpa_trace_record(&info);
+	wpa_trace_dump(title, &info);
+}
+
+
+void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
+{
+	if (addr == NULL)
+		return;
+	ref->addr = addr;
+	wpa_trace_record(ref);
+	dl_list_add(&active_references, &ref->list);
+}
+
+
+void wpa_trace_check_ref(const void *addr)
+{
+	struct wpa_trace_ref *ref;
+	dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
+		if (addr != ref->addr)
+			continue;
+		wpa_trace_show("Freeing referenced memory");
+		wpa_trace_dump("Reference registration", ref);
+		abort();
+	}
+}
+
+#endif /* defined(WPA_TRACE) || defined(WPA_TRACE_SIGNALS) */
diff --git a/hostap/src/utils/trace.h b/hostap/src/utils/trace.h
new file mode 100644
index 0000000..a623720
--- /dev/null
+++ b/hostap/src/utils/trace.h
@@ -0,0 +1,69 @@
+/*
+ * Backtrace debugging
+ * 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.
+ */
+
+#ifndef TRACE_H
+#define TRACE_H
+
+#define WPA_TRACE_LEN 16
+
+#if defined(WPA_TRACE) || defined(WPA_TRACE_SIGNALS)
+#include <execinfo.h>
+
+#include "list.h"
+
+#define WPA_TRACE_INFO void *btrace[WPA_TRACE_LEN]; int btrace_num;
+
+struct wpa_trace_ref {
+	struct dl_list list;
+	const void *addr;
+	WPA_TRACE_INFO
+};
+#define WPA_TRACE_REF(name) struct wpa_trace_ref wpa_trace_ref_##name
+
+#define wpa_trace_dump(title, ptr) \
+	wpa_trace_dump_func((title), (ptr)->btrace, (ptr)->btrace_num)
+void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num);
+#define wpa_trace_record(ptr) \
+	(ptr)->btrace_num = backtrace((ptr)->btrace, WPA_TRACE_LEN)
+void wpa_trace_show(const char *title);
+#define wpa_trace_add_ref(ptr, name, addr) \
+	wpa_trace_add_ref_func(&(ptr)->wpa_trace_ref_##name, (addr))
+void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr);
+#define wpa_trace_remove_ref(ptr, name, addr)	\
+	do { \
+		if ((addr)) \
+			dl_list_del(&(ptr)->wpa_trace_ref_##name.list); \
+	} while (0)
+void wpa_trace_check_ref(const void *addr);
+size_t wpa_trace_calling_func(const char *buf[], size_t len);
+
+#else /* defined(WPA_TRACE) || defined(WPA_TRACE_SIGNALS) */
+
+#define WPA_TRACE_INFO
+#define WPA_TRACE_REF(n)
+#define wpa_trace_dump(title, ptr) do { } while (0)
+#define wpa_trace_record(ptr) do { } while (0)
+#define wpa_trace_show(title) do { } while (0)
+#define wpa_trace_add_ref(ptr, name, addr) do { } while (0)
+#define wpa_trace_remove_ref(ptr, name, addr) do { } while (0)
+#define wpa_trace_check_ref(addr) do { } while (0)
+
+#endif /* defined(WPA_TRACE) || defined(WPA_TRACE_SIGNALS) */
+
+
+#ifdef WPA_TRACE_BFD
+
+void wpa_trace_dump_funcname(const char *title, void *pc);
+
+#else /* WPA_TRACE_BFD */
+
+#define wpa_trace_dump_funcname(title, pc) do { } while (0)
+
+#endif /* WPA_TRACE_BFD */
+
+#endif /* TRACE_H */
diff --git a/hostap/src/utils/utils_module_tests.c b/hostap/src/utils/utils_module_tests.c
new file mode 100644
index 0000000..41511b9
--- /dev/null
+++ b/hostap/src/utils/utils_module_tests.c
@@ -0,0 +1,861 @@
+/*
+ * utils module tests
+ * Copyright (c) 2014-2015, 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 "common/ieee802_11_defs.h"
+#include "utils/bitfield.h"
+#include "utils/ext_password.h"
+#include "utils/trace.h"
+#include "utils/base64.h"
+#include "utils/ip_addr.h"
+#include "utils/eloop.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;
+	int array[10];
+
+	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 (printf_decode(bin, 3, "abcde") != 2)
+		errors++;
+
+	if (printf_decode(bin, 3, "\\xa") != 1 || bin[0] != 10)
+		errors++;
+
+	if (printf_decode(bin, 3, "\\xq") != 1 || bin[0] != 'q')
+		errors++;
+
+	if (printf_decode(bin, 3, "\\a") != 1 || bin[0] != 'a')
+		errors++;
+
+	array[0] = 10;
+	array[1] = 10;
+	array[2] = 5;
+	array[3] = 10;
+	array[4] = 5;
+	array[5] = 0;
+	if (int_array_len(array) != 5)
+		errors++;
+	int_array_sort_unique(array);
+	if (int_array_len(array) != 2)
+		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);
+
+	bf = bitfield_alloc(8);
+	if (bf == NULL)
+		return -1;
+	if (bitfield_get_first_zero(bf) != 0)
+		errors++;
+	for (i = 0; i < 8; i++)
+		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;
+}
+
+
+static int base64_tests(void)
+{
+	int errors = 0;
+	unsigned char *res;
+	size_t res_len;
+
+	wpa_printf(MSG_INFO, "base64 tests");
+
+	res = base64_encode((const unsigned char *) "", ~0, &res_len);
+	if (res) {
+		errors++;
+		os_free(res);
+	}
+
+	res = base64_encode((const unsigned char *) "=", 1, &res_len);
+	if (!res || res_len != 5 || res[0] != 'P' || res[1] != 'Q' ||
+	    res[2] != '=' || res[3] != '=' || res[4] != '\n')
+		errors++;
+	os_free(res);
+
+	res = base64_encode((const unsigned char *) "=", 1, NULL);
+	if (!res || res[0] != 'P' || res[1] != 'Q' ||
+	    res[2] != '=' || res[3] != '=' || res[4] != '\n')
+		errors++;
+	os_free(res);
+
+	res = base64_decode((const unsigned char *) "", 0, &res_len);
+	if (res) {
+		errors++;
+		os_free(res);
+	}
+
+	res = base64_decode((const unsigned char *) "a", 1, &res_len);
+	if (res) {
+		errors++;
+		os_free(res);
+	}
+
+	res = base64_decode((const unsigned char *) "====", 4, &res_len);
+	if (res) {
+		errors++;
+		os_free(res);
+	}
+
+	res = base64_decode((const unsigned char *) "PQ==", 4, &res_len);
+	if (!res || res_len != 1 || res[0] != '=')
+		errors++;
+	os_free(res);
+
+	res = base64_decode((const unsigned char *) "P.Q-=!=*", 8, &res_len);
+	if (!res || res_len != 1 || res[0] != '=')
+		errors++;
+	os_free(res);
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d base64 test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int common_tests(void)
+{
+	char buf[3], longbuf[100];
+	u8 addr[ETH_ALEN] = { 1, 2, 3, 4, 5, 6 };
+	u8 bin[3];
+	int errors = 0;
+	struct wpa_freq_range_list ranges;
+	size_t len;
+	const char *txt;
+	u8 ssid[255];
+
+	wpa_printf(MSG_INFO, "common tests");
+
+	if (hwaddr_mask_txt(buf, 3, addr, addr) != -1)
+		errors++;
+
+	if (wpa_scnprintf(buf, 0, "hello") != 0 ||
+	    wpa_scnprintf(buf, 3, "hello") != 2)
+		errors++;
+
+	if (wpa_snprintf_hex(buf, 0, addr, ETH_ALEN) != 0 ||
+	    wpa_snprintf_hex(buf, 3, addr, ETH_ALEN) != 2)
+		errors++;
+
+	if (merge_byte_arrays(bin, 3, addr, ETH_ALEN, NULL, 0) != 3 ||
+	    merge_byte_arrays(bin, 3, NULL, 0, addr, ETH_ALEN) != 3)
+		errors++;
+
+	if (dup_binstr(NULL, 0) != NULL)
+		errors++;
+
+	if (freq_range_list_includes(NULL, 0) != 0)
+		errors++;
+
+	os_memset(&ranges, 0, sizeof(ranges));
+	if (freq_range_list_parse(&ranges, "") != 0 ||
+	    freq_range_list_includes(&ranges, 0) != 0 ||
+	    freq_range_list_str(&ranges) != NULL)
+		errors++;
+
+	if (utf8_unescape(NULL, 0, buf, sizeof(buf)) != 0 ||
+	    utf8_unescape("a", 1, NULL, 0) != 0 ||
+	    utf8_unescape("a\\", 2, buf, sizeof(buf)) != 0 ||
+	    utf8_unescape("abcde", 5, buf, sizeof(buf)) != 0 ||
+	    utf8_unescape("abc", 3, buf, 3) != 3)
+		errors++;
+
+	if (utf8_unescape("a", 0, buf, sizeof(buf)) != 1 || buf[0] != 'a')
+		errors++;
+
+	if (utf8_unescape("\\b", 2, buf, sizeof(buf)) != 1 || buf[0] != 'b')
+		errors++;
+
+	if (utf8_escape(NULL, 0, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("a", 1, NULL, 0) != 0 ||
+	    utf8_escape("abcde", 5, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("a\\bcde", 6, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("ab\\cde", 6, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("abc\\de", 6, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("abc", 3, buf, 3) != 3)
+		errors++;
+
+	if (utf8_escape("a", 0, buf, sizeof(buf)) != 1 || buf[0] != 'a')
+		errors++;
+
+	os_memset(ssid, 0, sizeof(ssid));
+	txt = wpa_ssid_txt(ssid, sizeof(ssid));
+	len = os_strlen(txt);
+	/* Verify that SSID_MAX_LEN * 4 buffer limit is enforced. */
+	if (len != SSID_MAX_LEN * 4) {
+		wpa_printf(MSG_ERROR,
+			   "Unexpected wpa_ssid_txt() result with too long SSID");
+		errors++;
+	}
+
+	if (wpa_snprintf_hex_sep(longbuf, 0, addr, ETH_ALEN, '-') != 0 ||
+	    wpa_snprintf_hex_sep(longbuf, 5, addr, ETH_ALEN, '-') != 3 ||
+	    os_strcmp(longbuf, "01-0") != 0)
+		errors++;
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d common test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int os_tests(void)
+{
+	int errors = 0;
+	void *ptr;
+	os_time_t t;
+
+	wpa_printf(MSG_INFO, "os tests");
+
+	ptr = os_calloc((size_t) -1, (size_t) -1);
+	if (ptr) {
+		errors++;
+		os_free(ptr);
+	}
+	ptr = os_calloc((size_t) 2, (size_t) -1);
+	if (ptr) {
+		errors++;
+		os_free(ptr);
+	}
+	ptr = os_calloc((size_t) -1, (size_t) 2);
+	if (ptr) {
+		errors++;
+		os_free(ptr);
+	}
+
+	ptr = os_realloc_array(NULL, (size_t) -1, (size_t) -1);
+	if (ptr) {
+		errors++;
+		os_free(ptr);
+	}
+
+	os_sleep(1, 1);
+
+	if (os_mktime(1969, 1, 1, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 0, 1, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 13, 1, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 0, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 32, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, -1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 24, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, -1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, 60, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, 1, -1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, 1, 61, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, 1, 1, &t) != 0 ||
+	    os_mktime(2020, 1, 2, 3, 4, 5, &t) != 0 ||
+	    os_mktime(2015, 12, 31, 23, 59, 59, &t) != 0)
+		errors++;
+
+	if (os_setenv("hwsim_test_env", "test value", 0) != 0 ||
+	    os_setenv("hwsim_test_env", "test value 2", 1) != 0 ||
+	    os_unsetenv("hwsim_test_env") != 0)
+		errors++;
+
+	if (os_file_exists("/this-file-does-not-exists-hwsim") != 0)
+		errors++;
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d os test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpabuf_tests(void)
+{
+	int errors = 0;
+	void *ptr;
+	struct wpabuf *buf;
+
+	wpa_printf(MSG_INFO, "wpabuf tests");
+
+	ptr = os_malloc(100);
+	if (ptr) {
+		buf = wpabuf_alloc_ext_data(ptr, 100);
+		if (buf) {
+			if (wpabuf_resize(&buf, 100) < 0)
+				errors++;
+			else
+				wpabuf_put(buf, 100);
+			wpabuf_free(buf);
+		} else {
+			errors++;
+			os_free(ptr);
+		}
+	} else {
+		errors++;
+	}
+
+	buf = wpabuf_alloc(100);
+	if (buf) {
+		struct wpabuf *buf2;
+
+		wpabuf_put(buf, 100);
+		if (wpabuf_resize(&buf, 100) < 0)
+			errors++;
+		else
+			wpabuf_put(buf, 100);
+		buf2 = wpabuf_concat(buf, NULL);
+		if (buf2 != buf)
+			errors++;
+		wpabuf_free(buf2);
+	} else {
+		errors++;
+	}
+
+	buf = NULL;
+	buf = wpabuf_zeropad(buf, 10);
+	if (buf != NULL)
+		errors++;
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d wpabuf test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int ip_addr_tests(void)
+{
+	int errors = 0;
+	struct hostapd_ip_addr addr;
+	char buf[100];
+
+	wpa_printf(MSG_INFO, "ip_addr tests");
+
+	if (hostapd_parse_ip_addr("1.2.3.4", &addr) != 0 ||
+	    addr.af != AF_INET ||
+	    hostapd_ip_txt(NULL, buf, sizeof(buf)) != NULL ||
+	    hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' ||
+	    hostapd_ip_txt(&addr, buf, 0) != NULL ||
+	    hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf)
+		errors++;
+
+	if (hostapd_parse_ip_addr("::", &addr) != 0 ||
+	    addr.af != AF_INET6 ||
+	    hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' ||
+	    hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf)
+		errors++;
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d ip_addr test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+struct test_eloop {
+	unsigned int magic;
+	int close_in_timeout;
+	int pipefd1[2];
+	int pipefd2[2];
+};
+
+
+static void eloop_tests_start(int close_in_timeout);
+
+
+static void eloop_test_read_2(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct test_eloop *t = eloop_ctx;
+	ssize_t res;
+	char buf[10];
+
+	wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->pipefd2[0] != sock) {
+		wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
+			   __func__, sock, t->pipefd2[0]);
+	}
+
+	res = read(sock, buf, sizeof(buf));
+	wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d",
+		   __func__, sock, (int) res);
+}
+
+
+static void eloop_test_read_2_wrong(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct test_eloop *t = eloop_ctx;
+
+	wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->pipefd2[0] != sock) {
+		wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
+			   __func__, sock, t->pipefd2[0]);
+	}
+
+	/*
+	 * This is expected to block due to the original socket with data having
+	 * been closed and no new data having been written to the new socket
+	 * with the same fd. To avoid blocking the process during test, skip the
+	 * read here.
+	 */
+	wpa_printf(MSG_ERROR, "%s: FAIL - should not have called this function",
+		   __func__);
+}
+
+
+static void reopen_pipefd2(struct test_eloop *t)
+{
+	if (t->pipefd2[0] < 0) {
+		wpa_printf(MSG_INFO, "pipefd2 had been closed");
+	} else {
+		int res;
+
+		wpa_printf(MSG_INFO, "close pipefd2");
+		eloop_unregister_read_sock(t->pipefd2[0]);
+		close(t->pipefd2[0]);
+		t->pipefd2[0] = -1;
+		close(t->pipefd2[1]);
+		t->pipefd2[1] = -1;
+
+		res = pipe(t->pipefd2);
+		if (res < 0) {
+			wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
+			t->pipefd2[0] = -1;
+			t->pipefd2[1] = -1;
+			return;
+		}
+
+		wpa_printf(MSG_INFO,
+			   "re-register pipefd2 with new sockets %d,%d",
+			   t->pipefd2[0], t->pipefd2[1]);
+		eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2_wrong,
+					 t, NULL);
+	}
+}
+
+
+static void eloop_test_read_1(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct test_eloop *t = eloop_ctx;
+	ssize_t res;
+	char buf[10];
+
+	wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->pipefd1[0] != sock) {
+		wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
+			   __func__, sock, t->pipefd1[0]);
+	}
+
+	res = read(sock, buf, sizeof(buf));
+	wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d",
+		   __func__, sock, (int) res);
+
+	if (!t->close_in_timeout)
+		reopen_pipefd2(t);
+}
+
+
+static void eloop_test_cb(void *eloop_data, void *user_ctx)
+{
+	struct test_eloop *t = eloop_data;
+
+	wpa_printf(MSG_INFO, "%s", __func__);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->close_in_timeout)
+		reopen_pipefd2(t);
+}
+
+
+static void eloop_test_timeout(void *eloop_data, void *user_ctx)
+{
+	struct test_eloop *t = eloop_data;
+	int next_run = 0;
+
+	wpa_printf(MSG_INFO, "%s", __func__);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->pipefd1[0] >= 0) {
+		wpa_printf(MSG_INFO, "pipefd1 had not been closed");
+		eloop_unregister_read_sock(t->pipefd1[0]);
+		close(t->pipefd1[0]);
+		t->pipefd1[0] = -1;
+		close(t->pipefd1[1]);
+		t->pipefd1[1] = -1;
+	}
+
+	if (t->pipefd2[0] >= 0) {
+		wpa_printf(MSG_INFO, "pipefd2 had not been closed");
+		eloop_unregister_read_sock(t->pipefd2[0]);
+		close(t->pipefd2[0]);
+		t->pipefd2[0] = -1;
+		close(t->pipefd2[1]);
+		t->pipefd2[1] = -1;
+	}
+
+	next_run = t->close_in_timeout;
+	t->magic = 0;
+	wpa_printf(MSG_INFO, "%s - free(%p)", __func__, t);
+	os_free(t);
+
+	if (next_run)
+		eloop_tests_start(0);
+}
+
+
+static void eloop_tests_start(int close_in_timeout)
+{
+	struct test_eloop *t;
+	int res;
+
+	t = os_zalloc(sizeof(*t));
+	if (!t)
+		return;
+	t->magic = 0x12345678;
+	t->close_in_timeout = close_in_timeout;
+
+	wpa_printf(MSG_INFO, "starting eloop tests (%p) (close_in_timeout=%d)",
+		   t, close_in_timeout);
+
+	res = pipe(t->pipefd1);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
+		os_free(t);
+		return;
+	}
+
+	res = pipe(t->pipefd2);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
+		close(t->pipefd1[0]);
+		close(t->pipefd1[1]);
+		os_free(t);
+		return;
+	}
+
+	wpa_printf(MSG_INFO, "pipe fds: %d,%d %d,%d",
+		   t->pipefd1[0], t->pipefd1[1],
+		   t->pipefd2[0], t->pipefd2[1]);
+
+	eloop_register_read_sock(t->pipefd1[0], eloop_test_read_1, t, NULL);
+	eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2, t, NULL);
+	eloop_register_timeout(0, 0, eloop_test_cb, t, NULL);
+	eloop_register_timeout(0, 200000, eloop_test_timeout, t, NULL);
+
+	if (write(t->pipefd1[1], "HELLO", 5) < 0)
+		wpa_printf(MSG_INFO, "write: %s", strerror(errno));
+	if (write(t->pipefd2[1], "TEST", 4) < 0)
+		wpa_printf(MSG_INFO, "write: %s", strerror(errno));
+	os_sleep(0, 50000);
+	wpa_printf(MSG_INFO, "waiting for eloop callbacks");
+}
+
+
+static void eloop_tests_run(void *eloop_data, void *user_ctx)
+{
+	eloop_tests_start(1);
+}
+
+
+static int eloop_tests(void)
+{
+	wpa_printf(MSG_INFO, "schedule eloop tests to be run");
+
+	/*
+	 * Cannot return error from these without a significant design change,
+	 * so for now, run the tests from a scheduled timeout and require
+	 * separate verification of the results from the debug log.
+	 */
+	eloop_register_timeout(0, 0, eloop_tests_run, NULL, NULL);
+
+	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 ||
+	    base64_tests() < 0 ||
+	    common_tests() < 0 ||
+	    os_tests() < 0 ||
+	    wpabuf_tests() < 0 ||
+	    ip_addr_tests() < 0 ||
+	    eloop_tests() < 0 ||
+	    int_array_tests() < 0)
+		ret = -1;
+
+	return ret;
+}
diff --git a/hostap/src/utils/uuid.c b/hostap/src/utils/uuid.c
new file mode 100644
index 0000000..0f224f9
--- /dev/null
+++ b/hostap/src/utils/uuid.c
@@ -0,0 +1,71 @@
+/*
+ * Universally Unique IDentifier (UUID)
+ * Copyright (c) 2008, 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 "uuid.h"
+
+int uuid_str2bin(const char *str, u8 *bin)
+{
+	const char *pos;
+	u8 *opos;
+
+	pos = str;
+	opos = bin;
+
+	if (hexstr2bin(pos, opos, 4))
+		return -1;
+	pos += 8;
+	opos += 4;
+
+	if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+		return -1;
+	pos += 4;
+	opos += 2;
+
+	if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+		return -1;
+	pos += 4;
+	opos += 2;
+
+	if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+		return -1;
+	pos += 4;
+	opos += 2;
+
+	if (*pos++ != '-' || hexstr2bin(pos, opos, 6))
+		return -1;
+
+	return 0;
+}
+
+
+int uuid_bin2str(const u8 *bin, char *str, size_t max_len)
+{
+	int len;
+	len = os_snprintf(str, max_len, "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+			  "%02x%02x-%02x%02x%02x%02x%02x%02x",
+			  bin[0], bin[1], bin[2], bin[3],
+			  bin[4], bin[5], bin[6], bin[7],
+			  bin[8], bin[9], bin[10], bin[11],
+			  bin[12], bin[13], bin[14], bin[15]);
+	if (os_snprintf_error(max_len, len))
+		return -1;
+	return 0;
+}
+
+
+int is_nil_uuid(const u8 *uuid)
+{
+	int i;
+	for (i = 0; i < UUID_LEN; i++)
+		if (uuid[i])
+			return 0;
+	return 1;
+}
diff --git a/hostap/src/utils/uuid.h b/hostap/src/utils/uuid.h
new file mode 100644
index 0000000..5e860cb
--- /dev/null
+++ b/hostap/src/utils/uuid.h
@@ -0,0 +1,18 @@
+/*
+ * Universally Unique IDentifier (UUID)
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef UUID_H
+#define UUID_H
+
+#define UUID_LEN 16
+
+int uuid_str2bin(const char *str, u8 *bin);
+int uuid_bin2str(const u8 *bin, char *str, size_t max_len);
+int is_nil_uuid(const u8 *uuid);
+
+#endif /* UUID_H */
diff --git a/hostap/src/utils/wpa_debug.c b/hostap/src/utils/wpa_debug.c
new file mode 100644
index 0000000..61c0d5c
--- /dev/null
+++ b/hostap/src/utils/wpa_debug.c
@@ -0,0 +1,860 @@
+/*
+ * wpa_supplicant/hostapd / Debug prints
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+
+#ifdef CONFIG_DEBUG_SYSLOG
+#include <syslog.h>
+
+static int wpa_debug_syslog = 0;
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+
+static FILE *wpa_debug_tracing_file = NULL;
+
+#define WPAS_TRACE_PFX "wpas <%d>: "
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+
+int wpa_debug_level = MSG_INFO;
+int wpa_debug_show_keys = 0;
+int wpa_debug_timestamp = 0;
+
+
+#ifdef CONFIG_ANDROID_LOG
+
+#include <android/log.h>
+
+#ifndef ANDROID_LOG_NAME
+#define ANDROID_LOG_NAME	"wpa_supplicant"
+#endif /* ANDROID_LOG_NAME */
+
+static int wpa_to_android_level(int level)
+{
+	if (level == MSG_ERROR)
+		return ANDROID_LOG_ERROR;
+	if (level == MSG_WARNING)
+		return ANDROID_LOG_WARN;
+	if (level == MSG_INFO)
+		return ANDROID_LOG_INFO;
+	return ANDROID_LOG_DEBUG;
+}
+
+#endif /* CONFIG_ANDROID_LOG */
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+
+#ifdef CONFIG_DEBUG_FILE
+static FILE *out_file = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+
+
+void wpa_debug_print_timestamp(void)
+{
+#ifndef CONFIG_ANDROID_LOG
+	struct os_time tv;
+
+	if (!wpa_debug_timestamp)
+		return;
+
+	os_get_time(&tv);
+#ifdef CONFIG_DEBUG_FILE
+	if (out_file) {
+		fprintf(out_file, "%ld.%06u: ", (long) tv.sec,
+			(unsigned int) tv.usec);
+	} else
+#endif /* CONFIG_DEBUG_FILE */
+	printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec);
+#endif /* CONFIG_ANDROID_LOG */
+}
+
+
+#ifdef CONFIG_DEBUG_SYSLOG
+#ifndef LOG_HOSTAPD
+#define LOG_HOSTAPD LOG_DAEMON
+#endif /* LOG_HOSTAPD */
+
+void wpa_debug_open_syslog(void)
+{
+	openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_HOSTAPD);
+	wpa_debug_syslog++;
+}
+
+
+void wpa_debug_close_syslog(void)
+{
+	if (wpa_debug_syslog)
+		closelog();
+}
+
+
+static int syslog_priority(int level)
+{
+	switch (level) {
+	case MSG_MSGDUMP:
+	case MSG_DEBUG:
+		return LOG_DEBUG;
+	case MSG_INFO:
+		return LOG_NOTICE;
+	case MSG_WARNING:
+		return LOG_WARNING;
+	case MSG_ERROR:
+		return LOG_ERR;
+	}
+	return LOG_INFO;
+}
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+
+int wpa_debug_open_linux_tracing(void)
+{
+	int mounts, trace_fd;
+	char buf[4096] = {};
+	ssize_t buflen;
+	char *line, *tmp1, *path = NULL;
+
+	mounts = open("/proc/mounts", O_RDONLY);
+	if (mounts < 0) {
+		printf("no /proc/mounts\n");
+		return -1;
+	}
+
+	buflen = read(mounts, buf, sizeof(buf) - 1);
+	close(mounts);
+	if (buflen < 0) {
+		printf("failed to read /proc/mounts\n");
+		return -1;
+	}
+
+	line = strtok_r(buf, "\n", &tmp1);
+	while (line) {
+		char *tmp2, *tmp_path, *fstype;
+		/* "<dev> <mountpoint> <fs type> ..." */
+		strtok_r(line, " ", &tmp2);
+		tmp_path = strtok_r(NULL, " ", &tmp2);
+		fstype = strtok_r(NULL, " ", &tmp2);
+		if (strcmp(fstype, "debugfs") == 0) {
+			path = tmp_path;
+			break;
+		}
+
+		line = strtok_r(NULL, "\n", &tmp1);
+	}
+
+	if (path == NULL) {
+		printf("debugfs mountpoint not found\n");
+		return -1;
+	}
+
+	snprintf(buf, sizeof(buf) - 1, "%s/tracing/trace_marker", path);
+
+	trace_fd = open(buf, O_WRONLY);
+	if (trace_fd < 0) {
+		printf("failed to open trace_marker file\n");
+		return -1;
+	}
+	wpa_debug_tracing_file = fdopen(trace_fd, "w");
+	if (wpa_debug_tracing_file == NULL) {
+		close(trace_fd);
+		printf("failed to fdopen()\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void wpa_debug_close_linux_tracing(void)
+{
+	if (wpa_debug_tracing_file == NULL)
+		return;
+	fclose(wpa_debug_tracing_file);
+	wpa_debug_tracing_file = NULL;
+}
+
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+
+/**
+ * wpa_printf - conditional printf
+ * @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. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void wpa_printf(int level, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	if (level >= wpa_debug_level) {
+#ifdef CONFIG_ANDROID_LOG
+		__android_log_vprint(wpa_to_android_level(level),
+				     ANDROID_LOG_NAME, fmt, ap);
+#else /* CONFIG_ANDROID_LOG */
+#ifdef CONFIG_DEBUG_SYSLOG
+		if (wpa_debug_syslog) {
+			vsyslog(syslog_priority(level), fmt, ap);
+		} else {
+#endif /* CONFIG_DEBUG_SYSLOG */
+		wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+		if (out_file) {
+			vfprintf(out_file, fmt, ap);
+			fprintf(out_file, "\n");
+		} else {
+#endif /* CONFIG_DEBUG_FILE */
+		vprintf(fmt, ap);
+		printf("\n");
+#ifdef CONFIG_DEBUG_FILE
+		}
+#endif /* CONFIG_DEBUG_FILE */
+#ifdef CONFIG_DEBUG_SYSLOG
+		}
+#endif /* CONFIG_DEBUG_SYSLOG */
+#endif /* CONFIG_ANDROID_LOG */
+	}
+	va_end(ap);
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	if (wpa_debug_tracing_file != NULL) {
+		va_start(ap, fmt);
+		fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX, level);
+		vfprintf(wpa_debug_tracing_file, fmt, ap);
+		fprintf(wpa_debug_tracing_file, "\n");
+		fflush(wpa_debug_tracing_file);
+		va_end(ap);
+	}
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+}
+
+
+static void _wpa_hexdump(int level, const char *title, const u8 *buf,
+			 size_t len, int show)
+{
+	size_t i;
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	if (wpa_debug_tracing_file != NULL) {
+		fprintf(wpa_debug_tracing_file,
+			WPAS_TRACE_PFX "%s - hexdump(len=%lu):",
+			level, title, (unsigned long) len);
+		if (buf == NULL) {
+			fprintf(wpa_debug_tracing_file, " [NULL]\n");
+		} else if (!show) {
+			fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
+		} else {
+			for (i = 0; i < len; i++)
+				fprintf(wpa_debug_tracing_file,
+					" %02x", buf[i]);
+		}
+		fflush(wpa_debug_tracing_file);
+	}
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+	if (level < wpa_debug_level)
+		return;
+#ifdef CONFIG_ANDROID_LOG
+	{
+		const char *display;
+		char *strbuf = NULL;
+		size_t slen = len;
+		if (buf == NULL) {
+			display = " [NULL]";
+		} else if (len == 0) {
+			display = "";
+		} else if (show && len) {
+			/* Limit debug message length for Android log */
+			if (slen > 32)
+				slen = 32;
+			strbuf = os_malloc(1 + 3 * slen);
+			if (strbuf == NULL) {
+				wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
+					   "allocate message buffer");
+				return;
+			}
+
+			for (i = 0; i < slen; i++)
+				os_snprintf(&strbuf[i * 3], 4, " %02x",
+					    buf[i]);
+
+			display = strbuf;
+		} else {
+			display = " [REMOVED]";
+		}
+
+		__android_log_print(wpa_to_android_level(level),
+				    ANDROID_LOG_NAME,
+				    "%s - hexdump(len=%lu):%s%s",
+				    title, (long unsigned int) len, display,
+				    len > slen ? " ..." : "");
+		bin_clear_free(strbuf, 1 + 3 * slen);
+		return;
+	}
+#else /* CONFIG_ANDROID_LOG */
+#ifdef CONFIG_DEBUG_SYSLOG
+	if (wpa_debug_syslog) {
+		const char *display;
+		char *strbuf = NULL;
+
+		if (buf == NULL) {
+			display = " [NULL]";
+		} else if (len == 0) {
+			display = "";
+		} else if (show && len) {
+			strbuf = os_malloc(1 + 3 * len);
+			if (strbuf == NULL) {
+				wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
+					   "allocate message buffer");
+				return;
+			}
+
+			for (i = 0; i < len; i++)
+				os_snprintf(&strbuf[i * 3], 4, " %02x",
+					    buf[i]);
+
+			display = strbuf;
+		} else {
+			display = " [REMOVED]";
+		}
+
+		syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s",
+		       title, (unsigned long) len, display);
+		bin_clear_free(strbuf, 1 + 3 * len);
+		return;
+	}
+#endif /* CONFIG_DEBUG_SYSLOG */
+	wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+	if (out_file) {
+		fprintf(out_file, "%s - hexdump(len=%lu):",
+			title, (unsigned long) len);
+		if (buf == NULL) {
+			fprintf(out_file, " [NULL]");
+		} else if (show) {
+			for (i = 0; i < len; i++)
+				fprintf(out_file, " %02x", buf[i]);
+		} else {
+			fprintf(out_file, " [REMOVED]");
+		}
+		fprintf(out_file, "\n");
+	} else {
+#endif /* CONFIG_DEBUG_FILE */
+	printf("%s - hexdump(len=%lu):", title, (unsigned long) len);
+	if (buf == NULL) {
+		printf(" [NULL]");
+	} else if (show) {
+		for (i = 0; i < len; i++)
+			printf(" %02x", buf[i]);
+	} else {
+		printf(" [REMOVED]");
+	}
+	printf("\n");
+#ifdef CONFIG_DEBUG_FILE
+	}
+#endif /* CONFIG_DEBUG_FILE */
+#endif /* CONFIG_ANDROID_LOG */
+}
+
+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 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 void *buf,
+			       size_t len, int show)
+{
+	size_t i, llen;
+	const u8 *pos = buf;
+	const size_t line_len = 16;
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	if (wpa_debug_tracing_file != NULL) {
+		fprintf(wpa_debug_tracing_file,
+			WPAS_TRACE_PFX "%s - hexdump_ascii(len=%lu):",
+			level, title, (unsigned long) len);
+		if (buf == NULL) {
+			fprintf(wpa_debug_tracing_file, " [NULL]\n");
+		} else if (!show) {
+			fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
+		} else {
+			/* can do ascii processing in userspace */
+			for (i = 0; i < len; i++)
+				fprintf(wpa_debug_tracing_file,
+					" %02x", pos[i]);
+		}
+		fflush(wpa_debug_tracing_file);
+	}
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+	if (level < wpa_debug_level)
+		return;
+#ifdef CONFIG_ANDROID_LOG
+	_wpa_hexdump(level, title, buf, len, show);
+#else /* CONFIG_ANDROID_LOG */
+	wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+	if (out_file) {
+		if (!show) {
+			fprintf(out_file,
+				"%s - hexdump_ascii(len=%lu): [REMOVED]\n",
+				title, (unsigned long) len);
+			return;
+		}
+		if (buf == NULL) {
+			fprintf(out_file,
+				"%s - hexdump_ascii(len=%lu): [NULL]\n",
+				title, (unsigned long) len);
+			return;
+		}
+		fprintf(out_file, "%s - hexdump_ascii(len=%lu):\n",
+			title, (unsigned long) len);
+		while (len) {
+			llen = len > line_len ? line_len : len;
+			fprintf(out_file, "    ");
+			for (i = 0; i < llen; i++)
+				fprintf(out_file, " %02x", pos[i]);
+			for (i = llen; i < line_len; i++)
+				fprintf(out_file, "   ");
+			fprintf(out_file, "   ");
+			for (i = 0; i < llen; i++) {
+				if (isprint(pos[i]))
+					fprintf(out_file, "%c", pos[i]);
+				else
+					fprintf(out_file, "_");
+			}
+			for (i = llen; i < line_len; i++)
+				fprintf(out_file, " ");
+			fprintf(out_file, "\n");
+			pos += llen;
+			len -= llen;
+		}
+	} else {
+#endif /* CONFIG_DEBUG_FILE */
+	if (!show) {
+		printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n",
+		       title, (unsigned long) len);
+		return;
+	}
+	if (buf == NULL) {
+		printf("%s - hexdump_ascii(len=%lu): [NULL]\n",
+		       title, (unsigned long) len);
+		return;
+	}
+	printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len);
+	while (len) {
+		llen = len > line_len ? line_len : len;
+		printf("    ");
+		for (i = 0; i < llen; i++)
+			printf(" %02x", pos[i]);
+		for (i = llen; i < line_len; i++)
+			printf("   ");
+		printf("   ");
+		for (i = 0; i < llen; i++) {
+			if (isprint(pos[i]))
+				printf("%c", pos[i]);
+			else
+				printf("_");
+		}
+		for (i = llen; i < line_len; i++)
+			printf(" ");
+		printf("\n");
+		pos += llen;
+		len -= llen;
+	}
+#ifdef CONFIG_DEBUG_FILE
+	}
+#endif /* CONFIG_DEBUG_FILE */
+#endif /* CONFIG_ANDROID_LOG */
+}
+
+
+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 void *buf,
+			   size_t len)
+{
+	_wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
+}
+
+
+#ifdef CONFIG_DEBUG_FILE
+static char *last_path = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+
+int wpa_debug_reopen_file(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+	int rv;
+	if (last_path) {
+		char *tmp = os_strdup(last_path);
+		wpa_debug_close_file();
+		rv = wpa_debug_open_file(tmp);
+		os_free(tmp);
+	} else {
+		wpa_printf(MSG_ERROR, "Last-path was not set, cannot "
+			   "re-open log file.");
+		rv = -1;
+	}
+	return rv;
+#else /* CONFIG_DEBUG_FILE */
+	return 0;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
+int wpa_debug_open_file(const char *path)
+{
+#ifdef CONFIG_DEBUG_FILE
+	if (!path)
+		return 0;
+
+	if (last_path == NULL || os_strcmp(last_path, path) != 0) {
+		/* Save our path to enable re-open */
+		os_free(last_path);
+		last_path = os_strdup(path);
+	}
+
+	out_file = fopen(path, "a");
+	if (out_file == NULL) {
+		wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open "
+			   "output file, using standard output");
+		return -1;
+	}
+#ifndef _WIN32
+	setvbuf(out_file, NULL, _IOLBF, 0);
+#endif /* _WIN32 */
+#else /* CONFIG_DEBUG_FILE */
+	(void)path;
+#endif /* CONFIG_DEBUG_FILE */
+	return 0;
+}
+
+
+void wpa_debug_close_file(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+	if (!out_file)
+		return;
+	fclose(out_file);
+	out_file = NULL;
+	os_free(last_path);
+	last_path = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
+void wpa_debug_setup_stdout(void)
+{
+#ifndef _WIN32
+	setvbuf(stdout, NULL, _IOLBF, 0);
+#endif /* _WIN32 */
+}
+
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifndef CONFIG_NO_WPA_MSG
+static wpa_msg_cb_func wpa_msg_cb = NULL;
+
+void wpa_msg_register_cb(wpa_msg_cb_func func)
+{
+	wpa_msg_cb = func;
+}
+
+
+static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL;
+
+void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
+{
+	wpa_msg_ifname_cb = func;
+}
+
+
+void wpa_msg(void *ctx, int level, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	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 "
+			   "buffer");
+		return;
+	}
+	va_start(ap, fmt);
+	prefix[0] = '\0';
+	if (wpa_msg_ifname_cb) {
+		const char *ifname = wpa_msg_ifname_cb(ctx);
+		if (ifname) {
+			int res = os_snprintf(prefix, sizeof(prefix), "%s: ",
+					      ifname);
+			if (os_snprintf_error(sizeof(prefix), res))
+				prefix[0] = '\0';
+		}
+	}
+	len = vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+	wpa_printf(level, "%s%s", prefix, buf);
+	if (wpa_msg_cb)
+		wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
+	bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_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_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, WPA_MSG_PER_INTERFACE, buf, len);
+	bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_global(void *ctx, int level, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	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 "
+			   "message buffer");
+		return;
+	}
+	va_start(ap, fmt);
+	len = vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+	wpa_printf(level, "%s", buf);
+	if (wpa_msg_cb)
+		wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
+	bin_clear_free(buf, buflen);
+}
+
+
+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, WPA_MSG_GLOBAL, buf, len);
+	bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	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 "
+			   "message buffer");
+		return;
+	}
+	va_start(ap, fmt);
+	len = vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+	wpa_printf(level, "%s", buf);
+	if (wpa_msg_cb)
+		wpa_msg_cb(ctx, level, WPA_MSG_NO_GLOBAL, buf, len);
+	bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	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, "%s: Failed to allocate message buffer",
+			   __func__);
+		return;
+	}
+	va_start(ap, fmt);
+	len = vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+	wpa_printf(level, "%s", buf);
+	if (wpa_msg_cb)
+		wpa_msg_cb(ctx, level, WPA_MSG_ONLY_GLOBAL, buf, len);
+	os_free(buf);
+}
+
+#endif /* CONFIG_NO_WPA_MSG */
+
+
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+static hostapd_logger_cb_func hostapd_logger_cb = NULL;
+
+void hostapd_logger_register_cb(hostapd_logger_cb_func func)
+{
+	hostapd_logger_cb = func;
+}
+
+
+void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
+		    const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	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 "
+			   "message buffer");
+		return;
+	}
+	va_start(ap, fmt);
+	len = vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+	if (hostapd_logger_cb)
+		hostapd_logger_cb(ctx, addr, module, level, buf, len);
+	else if (addr)
+		wpa_printf(MSG_DEBUG, "hostapd_logger: STA " MACSTR " - %s",
+			   MAC2STR(addr), buf);
+	else
+		wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf);
+	bin_clear_free(buf, buflen);
+}
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+
+const char * debug_level_str(int level)
+{
+	switch (level) {
+	case MSG_EXCESSIVE:
+		return "EXCESSIVE";
+	case MSG_MSGDUMP:
+		return "MSGDUMP";
+	case MSG_DEBUG:
+		return "DEBUG";
+	case MSG_INFO:
+		return "INFO";
+	case MSG_WARNING:
+		return "WARNING";
+	case MSG_ERROR:
+		return "ERROR";
+	default:
+		return "?";
+	}
+}
+
+
+int str_to_debug_level(const char *s)
+{
+	if (os_strcasecmp(s, "EXCESSIVE") == 0)
+		return MSG_EXCESSIVE;
+	if (os_strcasecmp(s, "MSGDUMP") == 0)
+		return MSG_MSGDUMP;
+	if (os_strcasecmp(s, "DEBUG") == 0)
+		return MSG_DEBUG;
+	if (os_strcasecmp(s, "INFO") == 0)
+		return MSG_INFO;
+	if (os_strcasecmp(s, "WARNING") == 0)
+		return MSG_WARNING;
+	if (os_strcasecmp(s, "ERROR") == 0)
+		return MSG_ERROR;
+	return -1;
+}
diff --git a/hostap/src/utils/wpa_debug.h b/hostap/src/utils/wpa_debug.h
new file mode 100644
index 0000000..17d8f96
--- /dev/null
+++ b/hostap/src/utils/wpa_debug.h
@@ -0,0 +1,370 @@
+/*
+ * wpa_supplicant/hostapd / Debug prints
+ * 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.
+ */
+
+#ifndef WPA_DEBUG_H
+#define WPA_DEBUG_H
+
+#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. */
+
+enum {
+	MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR
+};
+
+#ifdef CONFIG_NO_STDOUT_DEBUG
+
+#define wpa_debug_print_timestamp() do { } while (0)
+#define wpa_printf(args...) do { } while (0)
+#define wpa_hexdump(l,t,b,le) do { } while (0)
+#define wpa_hexdump_buf(l,t,b) do { } while (0)
+#define wpa_hexdump_key(l,t,b,le) do { } while (0)
+#define wpa_hexdump_buf_key(l,t,b) do { } while (0)
+#define wpa_hexdump_ascii(l,t,b,le) do { } while (0)
+#define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0)
+#define wpa_debug_open_file(p) do { } while (0)
+#define wpa_debug_close_file() do { } while (0)
+#define wpa_debug_setup_stdout() do { } while (0)
+#define wpa_dbg(args...) do { } while (0)
+
+static inline int wpa_debug_reopen_file(void)
+{
+	return 0;
+}
+
+#else /* CONFIG_NO_STDOUT_DEBUG */
+
+int wpa_debug_open_file(const char *path);
+int wpa_debug_reopen_file(void);
+void wpa_debug_close_file(void);
+void wpa_debug_setup_stdout(void);
+
+/**
+ * wpa_debug_printf_timestamp - Print timestamp for debug output
+ *
+ * This function prints a timestamp in seconds_from_1970.microsoconds
+ * format if debug output has been configured to include timestamps in debug
+ * messages.
+ */
+void wpa_debug_print_timestamp(void);
+
+/**
+ * wpa_printf - conditional printf
+ * @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. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void wpa_printf(int level, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+
+/**
+ * wpa_hexdump - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * 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 void *buf, size_t len);
+
+static inline void wpa_hexdump_buf(int level, const char *title,
+				   const struct wpabuf *buf)
+{
+	wpa_hexdump(level, title, buf ? wpabuf_head(buf) : NULL,
+		    buf ? wpabuf_len(buf) : 0);
+}
+
+/**
+ * wpa_hexdump_key - conditional hex dump, hide keys
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump. This works
+ * 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 void *buf, size_t len);
+
+static inline void wpa_hexdump_buf_key(int level, const char *title,
+				       const struct wpabuf *buf)
+{
+	wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : NULL,
+			buf ? wpabuf_len(buf) : 0);
+}
+
+/**
+ * wpa_hexdump_ascii - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump with both
+ * 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 void *buf,
+		       size_t len);
+
+/**
+ * wpa_hexdump_ascii_key - conditional hex dump, hide keys
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump with both
+ * the hex numbers and ASCII characters (for printable range) are shown. 16
+ * 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 void *buf,
+			   size_t len);
+
+/*
+ * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
+ * binary size. As such, it should be used with debugging messages that are not
+ * needed in the control interface while wpa_msg() has to be used for anything
+ * that needs to shown to control interface monitors.
+ */
+#define wpa_dbg(args...) wpa_msg(args)
+
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_NO_WPA_MSG
+#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_global_only(args...) do { } while (0)
+#define wpa_msg_register_cb(f) do { } while (0)
+#define wpa_msg_register_ifname_cb(f) do { } while (0)
+#else /* CONFIG_NO_WPA_MSG */
+/**
+ * wpa_msg - Conditional printf for default target and 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. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. This function is like wpa_printf(), but it also sends the
+ * same message to all attached ctrl_iface monitors.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
+
+/**
+ * wpa_msg_ctrl - Conditional 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(), but it sends the output only to the
+ * attached 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_ctrl(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+/**
+ * wpa_msg_global - 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(), but it sends the output as a global event,
+ * i.e., without being specific to an interface. For backwards compatibility,
+ * an old style event is also delivered on one of the interfaces (the one
+ * specified by the context data).
+ */
+void wpa_msg_global(void *ctx, int level, const char *fmt, ...)
+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()
+ * @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(), but it does not send the output as a global
+ * event.
+ */
+void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+/**
+ * wpa_msg_global_only - Conditional 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 as a
+ * global event.
+ */
+void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+enum wpa_msg_type {
+	WPA_MSG_PER_INTERFACE,
+	WPA_MSG_GLOBAL,
+	WPA_MSG_NO_GLOBAL,
+	WPA_MSG_ONLY_GLOBAL,
+};
+
+typedef void (*wpa_msg_cb_func)(void *ctx, int level, enum wpa_msg_type type,
+				const char *txt, size_t len);
+
+/**
+ * wpa_msg_register_cb - Register callback function for wpa_msg() messages
+ * @func: Callback function (%NULL to unregister)
+ */
+void wpa_msg_register_cb(wpa_msg_cb_func func);
+
+typedef const char * (*wpa_msg_get_ifname_func)(void *ctx);
+void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func);
+
+#endif /* CONFIG_NO_WPA_MSG */
+
+#ifdef CONFIG_NO_HOSTAPD_LOGGER
+#define hostapd_logger(args...) do { } while (0)
+#define hostapd_logger_register_cb(f) do { } while (0)
+#else /* CONFIG_NO_HOSTAPD_LOGGER */
+void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
+		    const char *fmt, ...) PRINTF_FORMAT(5, 6);
+
+typedef void (*hostapd_logger_cb_func)(void *ctx, const u8 *addr,
+				       unsigned int module, int level,
+				       const char *txt, size_t len);
+
+/**
+ * hostapd_logger_register_cb - Register callback function for hostapd_logger()
+ * @func: Callback function (%NULL to unregister)
+ */
+void hostapd_logger_register_cb(hostapd_logger_cb_func func);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+#define HOSTAPD_MODULE_IEEE80211	0x00000001
+#define HOSTAPD_MODULE_IEEE8021X	0x00000002
+#define HOSTAPD_MODULE_RADIUS		0x00000004
+#define HOSTAPD_MODULE_WPA		0x00000008
+#define HOSTAPD_MODULE_DRIVER		0x00000010
+#define HOSTAPD_MODULE_IAPP		0x00000020
+#define HOSTAPD_MODULE_MLME		0x00000040
+
+enum hostapd_logger_level {
+	HOSTAPD_LEVEL_DEBUG_VERBOSE = 0,
+	HOSTAPD_LEVEL_DEBUG = 1,
+	HOSTAPD_LEVEL_INFO = 2,
+	HOSTAPD_LEVEL_NOTICE = 3,
+	HOSTAPD_LEVEL_WARNING = 4
+};
+
+
+#ifdef CONFIG_DEBUG_SYSLOG
+
+void wpa_debug_open_syslog(void);
+void wpa_debug_close_syslog(void);
+
+#else /* CONFIG_DEBUG_SYSLOG */
+
+static inline void wpa_debug_open_syslog(void)
+{
+}
+
+static inline void wpa_debug_close_syslog(void)
+{
+}
+
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+
+int wpa_debug_open_linux_tracing(void);
+void wpa_debug_close_linux_tracing(void);
+
+#else /* CONFIG_DEBUG_LINUX_TRACING */
+
+static inline int wpa_debug_open_linux_tracing(void)
+{
+	return 0;
+}
+
+static inline void wpa_debug_close_linux_tracing(void)
+{
+}
+
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+
+#ifdef EAPOL_TEST
+#define WPA_ASSERT(a)						       \
+	do {							       \
+		if (!(a)) {					       \
+			printf("WPA_ASSERT FAILED '" #a "' "	       \
+			       "%s %s:%d\n",			       \
+			       __FUNCTION__, __FILE__, __LINE__);      \
+			exit(1);				       \
+		}						       \
+	} while (0)
+#else
+#define WPA_ASSERT(a) do { } while (0)
+#endif
+
+const char * debug_level_str(int level);
+int str_to_debug_level(const char *s);
+
+#endif /* WPA_DEBUG_H */
diff --git a/hostap/src/utils/wpabuf.c b/hostap/src/utils/wpabuf.c
new file mode 100644
index 0000000..11e7323
--- /dev/null
+++ b/hostap/src/utils/wpabuf.c
@@ -0,0 +1,312 @@
+/*
+ * Dynamic data buffer
+ * Copyright (c) 2007-2012, 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 "trace.h"
+#include "wpabuf.h"
+
+#ifdef WPA_TRACE
+#define WPABUF_MAGIC 0x51a974e3
+
+struct wpabuf_trace {
+	unsigned int magic;
+} __attribute__((aligned(8)));
+
+static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf)
+{
+	return (struct wpabuf_trace *)
+		((const u8 *) buf - sizeof(struct wpabuf_trace));
+}
+#endif /* WPA_TRACE */
+
+
+static void wpabuf_overflow(const struct wpabuf *buf, size_t len)
+{
+#ifdef WPA_TRACE
+	struct wpabuf_trace *trace = wpabuf_get_trace(buf);
+	if (trace->magic != WPABUF_MAGIC) {
+		wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x",
+			   trace->magic);
+	}
+#endif /* WPA_TRACE */
+	wpa_printf(MSG_ERROR, "wpabuf %p (size=%lu used=%lu) overflow len=%lu",
+		   buf, (unsigned long) buf->size, (unsigned long) buf->used,
+		   (unsigned long) len);
+	wpa_trace_show("wpabuf overflow");
+	abort();
+}
+
+
+int wpabuf_resize(struct wpabuf **_buf, size_t add_len)
+{
+	struct wpabuf *buf = *_buf;
+#ifdef WPA_TRACE
+	struct wpabuf_trace *trace;
+#endif /* WPA_TRACE */
+
+	if (buf == NULL) {
+		*_buf = wpabuf_alloc(add_len);
+		return *_buf == NULL ? -1 : 0;
+	}
+
+#ifdef WPA_TRACE
+	trace = wpabuf_get_trace(buf);
+	if (trace->magic != WPABUF_MAGIC) {
+		wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x",
+			   trace->magic);
+		wpa_trace_show("wpabuf_resize invalid magic");
+		abort();
+	}
+#endif /* WPA_TRACE */
+
+	if (buf->used + add_len > buf->size) {
+		unsigned char *nbuf;
+		if (buf->flags & WPABUF_FLAG_EXT_DATA) {
+			nbuf = os_realloc(buf->buf, buf->used + add_len);
+			if (nbuf == NULL)
+				return -1;
+			os_memset(nbuf + buf->used, 0, add_len);
+			buf->buf = nbuf;
+		} else {
+#ifdef WPA_TRACE
+			nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) +
+					  sizeof(struct wpabuf) +
+					  buf->used + add_len);
+			if (nbuf == NULL)
+				return -1;
+			trace = (struct wpabuf_trace *) nbuf;
+			buf = (struct wpabuf *) (trace + 1);
+			os_memset(nbuf + sizeof(struct wpabuf_trace) +
+				  sizeof(struct wpabuf) + buf->used, 0,
+				  add_len);
+#else /* WPA_TRACE */
+			nbuf = os_realloc(buf, sizeof(struct wpabuf) +
+					  buf->used + add_len);
+			if (nbuf == NULL)
+				return -1;
+			buf = (struct wpabuf *) nbuf;
+			os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0,
+				  add_len);
+#endif /* WPA_TRACE */
+			buf->buf = (u8 *) (buf + 1);
+			*_buf = buf;
+		}
+		buf->size = buf->used + add_len;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpabuf_alloc - Allocate a wpabuf of the given size
+ * @len: Length for the allocated buffer
+ * Returns: Buffer to the allocated wpabuf or %NULL on failure
+ */
+struct wpabuf * wpabuf_alloc(size_t len)
+{
+#ifdef WPA_TRACE
+	struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) +
+					       sizeof(struct wpabuf) + len);
+	struct wpabuf *buf;
+	if (trace == NULL)
+		return NULL;
+	trace->magic = WPABUF_MAGIC;
+	buf = (struct wpabuf *) (trace + 1);
+#else /* WPA_TRACE */
+	struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf) + len);
+	if (buf == NULL)
+		return NULL;
+#endif /* WPA_TRACE */
+
+	buf->size = len;
+	buf->buf = (u8 *) (buf + 1);
+	return buf;
+}
+
+
+struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len)
+{
+#ifdef WPA_TRACE
+	struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) +
+					       sizeof(struct wpabuf));
+	struct wpabuf *buf;
+	if (trace == NULL)
+		return NULL;
+	trace->magic = WPABUF_MAGIC;
+	buf = (struct wpabuf *) (trace + 1);
+#else /* WPA_TRACE */
+	struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf));
+	if (buf == NULL)
+		return NULL;
+#endif /* WPA_TRACE */
+
+	buf->size = len;
+	buf->used = len;
+	buf->buf = data;
+	buf->flags |= WPABUF_FLAG_EXT_DATA;
+
+	return buf;
+}
+
+
+struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len)
+{
+	struct wpabuf *buf = wpabuf_alloc(len);
+	if (buf)
+		wpabuf_put_data(buf, data, len);
+	return buf;
+}
+
+
+struct wpabuf * wpabuf_dup(const struct wpabuf *src)
+{
+	struct wpabuf *buf = wpabuf_alloc(wpabuf_len(src));
+	if (buf)
+		wpabuf_put_data(buf, wpabuf_head(src), wpabuf_len(src));
+	return buf;
+}
+
+
+/**
+ * wpabuf_free - Free a wpabuf
+ * @buf: wpabuf buffer
+ */
+void wpabuf_free(struct wpabuf *buf)
+{
+#ifdef WPA_TRACE
+	struct wpabuf_trace *trace;
+	if (buf == NULL)
+		return;
+	trace = wpabuf_get_trace(buf);
+	if (trace->magic != WPABUF_MAGIC) {
+		wpa_printf(MSG_ERROR, "wpabuf_free: invalid magic %x",
+			   trace->magic);
+		wpa_trace_show("wpabuf_free magic mismatch");
+		abort();
+	}
+	if (buf->flags & WPABUF_FLAG_EXT_DATA)
+		os_free(buf->buf);
+	os_free(trace);
+#else /* WPA_TRACE */
+	if (buf == NULL)
+		return;
+	if (buf->flags & WPABUF_FLAG_EXT_DATA)
+		os_free(buf->buf);
+	os_free(buf);
+#endif /* WPA_TRACE */
+}
+
+
+void wpabuf_clear_free(struct wpabuf *buf)
+{
+	if (buf) {
+		os_memset(wpabuf_mhead(buf), 0, wpabuf_len(buf));
+		wpabuf_free(buf);
+	}
+}
+
+
+void * wpabuf_put(struct wpabuf *buf, size_t len)
+{
+	void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
+	buf->used += len;
+	if (buf->used > buf->size) {
+		wpabuf_overflow(buf, len);
+	}
+	return tmp;
+}
+
+
+/**
+ * wpabuf_concat - Concatenate two buffers into a newly allocated one
+ * @a: First buffer
+ * @b: Second buffer
+ * Returns: wpabuf with concatenated a + b data or %NULL on failure
+ *
+ * Both buffers a and b will be freed regardless of the return value. Input
+ * buffers can be %NULL which is interpreted as an empty buffer.
+ */
+struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b)
+{
+	struct wpabuf *n = NULL;
+	size_t len = 0;
+
+	if (b == NULL)
+		return a;
+
+	if (a)
+		len += wpabuf_len(a);
+	if (b)
+		len += wpabuf_len(b);
+
+	n = wpabuf_alloc(len);
+	if (n) {
+		if (a)
+			wpabuf_put_buf(n, a);
+		if (b)
+			wpabuf_put_buf(n, b);
+	}
+
+	wpabuf_free(a);
+	wpabuf_free(b);
+
+	return n;
+}
+
+
+/**
+ * wpabuf_zeropad - Pad buffer with 0x00 octets (prefix) to specified length
+ * @buf: Buffer to be padded
+ * @len: Length for the padded buffer
+ * Returns: wpabuf padded to len octets or %NULL on failure
+ *
+ * If buf is longer than len octets or of same size, it will be returned as-is.
+ * Otherwise a new buffer is allocated and prefixed with 0x00 octets followed
+ * by the source data. The source buffer will be freed on error, i.e., caller
+ * will only be responsible on freeing the returned buffer. If buf is %NULL,
+ * %NULL will be returned.
+ */
+struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len)
+{
+	struct wpabuf *ret;
+	size_t blen;
+
+	if (buf == NULL)
+		return NULL;
+
+	blen = wpabuf_len(buf);
+	if (blen >= len)
+		return buf;
+
+	ret = wpabuf_alloc(len);
+	if (ret) {
+		os_memset(wpabuf_put(ret, len - blen), 0, len - blen);
+		wpabuf_put_buf(ret, buf);
+	}
+	wpabuf_free(buf);
+
+	return ret;
+}
+
+
+void wpabuf_printf(struct wpabuf *buf, char *fmt, ...)
+{
+	va_list ap;
+	void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
+	int res;
+
+	va_start(ap, fmt);
+	res = vsnprintf(tmp, buf->size - buf->used, fmt, ap);
+	va_end(ap);
+	if (res < 0 || (size_t) res >= buf->size - buf->used)
+		wpabuf_overflow(buf, res);
+	buf->used += res;
+}
diff --git a/hostap/src/utils/wpabuf.h b/hostap/src/utils/wpabuf.h
new file mode 100644
index 0000000..c3ef1ba
--- /dev/null
+++ b/hostap/src/utils/wpabuf.h
@@ -0,0 +1,163 @@
+/*
+ * Dynamic data buffer
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPABUF_H
+#define WPABUF_H
+
+/* wpabuf::buf is a pointer to external data */
+#define WPABUF_FLAG_EXT_DATA BIT(0)
+
+/*
+ * Internal data structure for wpabuf. Please do not touch this directly from
+ * elsewhere. This is only defined in header file to allow inline functions
+ * from this file to access data.
+ */
+struct wpabuf {
+	size_t size; /* total size of the allocated buffer */
+	size_t used; /* length of data in the buffer */
+	u8 *buf; /* pointer to the head of the buffer */
+	unsigned int flags;
+	/* optionally followed by the allocated buffer */
+};
+
+
+int wpabuf_resize(struct wpabuf **buf, size_t add_len);
+struct wpabuf * wpabuf_alloc(size_t len);
+struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len);
+struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len);
+struct wpabuf * wpabuf_dup(const struct wpabuf *src);
+void wpabuf_free(struct wpabuf *buf);
+void wpabuf_clear_free(struct wpabuf *buf);
+void * wpabuf_put(struct wpabuf *buf, size_t len);
+struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
+struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len);
+void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3);
+
+
+/**
+ * wpabuf_size - Get the currently allocated size of a wpabuf buffer
+ * @buf: wpabuf buffer
+ * Returns: Currently allocated size of the buffer
+ */
+static inline size_t wpabuf_size(const struct wpabuf *buf)
+{
+	return buf->size;
+}
+
+/**
+ * wpabuf_len - Get the current length of a wpabuf buffer data
+ * @buf: wpabuf buffer
+ * Returns: Currently used length of the buffer
+ */
+static inline size_t wpabuf_len(const struct wpabuf *buf)
+{
+	return buf->used;
+}
+
+/**
+ * wpabuf_tailroom - Get size of available tail room in the end of the buffer
+ * @buf: wpabuf buffer
+ * Returns: Tail room (in bytes) of available space in the end of the buffer
+ */
+static inline size_t wpabuf_tailroom(const struct wpabuf *buf)
+{
+	return buf->size - buf->used;
+}
+
+/**
+ * wpabuf_head - Get pointer to the head of the buffer data
+ * @buf: wpabuf buffer
+ * Returns: Pointer to the head of the buffer data
+ */
+static inline const void * wpabuf_head(const struct wpabuf *buf)
+{
+	return buf->buf;
+}
+
+static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf)
+{
+	return wpabuf_head(buf);
+}
+
+/**
+ * wpabuf_mhead - Get modifiable pointer to the head of the buffer data
+ * @buf: wpabuf buffer
+ * Returns: Pointer to the head of the buffer data
+ */
+static inline void * wpabuf_mhead(struct wpabuf *buf)
+{
+	return buf->buf;
+}
+
+static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf)
+{
+	return wpabuf_mhead(buf);
+}
+
+static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data)
+{
+	u8 *pos = wpabuf_put(buf, 1);
+	*pos = data;
+}
+
+static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data)
+{
+	u8 *pos = wpabuf_put(buf, 2);
+	WPA_PUT_LE16(pos, data);
+}
+
+static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data)
+{
+	u8 *pos = wpabuf_put(buf, 4);
+	WPA_PUT_LE32(pos, data);
+}
+
+static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data)
+{
+	u8 *pos = wpabuf_put(buf, 2);
+	WPA_PUT_BE16(pos, data);
+}
+
+static inline void wpabuf_put_be24(struct wpabuf *buf, u32 data)
+{
+	u8 *pos = wpabuf_put(buf, 3);
+	WPA_PUT_BE24(pos, data);
+}
+
+static inline void wpabuf_put_be32(struct wpabuf *buf, u32 data)
+{
+	u8 *pos = wpabuf_put(buf, 4);
+	WPA_PUT_BE32(pos, data);
+}
+
+static inline void wpabuf_put_data(struct wpabuf *buf, const void *data,
+				   size_t len)
+{
+	if (data)
+		os_memcpy(wpabuf_put(buf, len), data, len);
+}
+
+static inline void wpabuf_put_buf(struct wpabuf *dst,
+				  const struct wpabuf *src)
+{
+	wpabuf_put_data(dst, wpabuf_head(src), wpabuf_len(src));
+}
+
+static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len)
+{
+	buf->buf = (u8 *) data;
+	buf->flags = WPABUF_FLAG_EXT_DATA;
+	buf->size = buf->used = len;
+}
+
+static inline void wpabuf_put_str(struct wpabuf *dst, const char *str)
+{
+	wpabuf_put_data(dst, str, os_strlen(str));
+}
+
+#endif /* WPABUF_H */
diff --git a/hostap/src/utils/xml-utils.c b/hostap/src/utils/xml-utils.c
new file mode 100644
index 0000000..4916d29
--- /dev/null
+++ b/hostap/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/hostap/src/utils/xml-utils.h b/hostap/src/utils/xml-utils.h
new file mode 100644
index 0000000..fb6208c
--- /dev/null
+++ b/hostap/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/hostap/src/utils/xml_libxml2.c b/hostap/src/utils/xml_libxml2.c
new file mode 100644
index 0000000..c928394
--- /dev/null
+++ b/hostap/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/hostap/src/wps/Makefile b/hostap/src/wps/Makefile
new file mode 100644
index 0000000..4806fe8
--- /dev/null
+++ b/hostap/src/wps/Makefile
@@ -0,0 +1,41 @@
+all: libwps.a
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libwps.a
+
+install:
+	@echo Nothing to be made.
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_P2P
+CFLAGS += -DCONFIG_WPS_OOB
+CFLAGS += -DCONFIG_WPS_NFC
+
+LIB_OBJS= \
+	http_client.o \
+	httpread.o \
+	http_server.o \
+	ndef.o \
+	upnp_xml.o \
+	wps_attr_build.o \
+	wps_attr_parse.o \
+	wps_attr_process.o \
+	wps.o \
+	wps_common.o \
+	wps_dev_attr.o \
+	wps_enrollee.o \
+	wps_er.o \
+	wps_er_ssdp.o \
+	wps_module_tests.o \
+	wps_registrar.o \
+	wps_upnp_ap.o \
+	wps_upnp.o \
+	wps_upnp_event.o \
+	wps_upnp_ssdp.o \
+	wps_upnp_web.o
+
+libwps.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/src/wps/http.h b/hostap/src/wps/http.h
new file mode 100644
index 0000000..2fee3a8
--- /dev/null
+++ b/hostap/src/wps/http.h
@@ -0,0 +1,29 @@
+/*
+ * HTTP for WPS
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef HTTP_H
+#define HTTP_H
+
+enum http_reply_code {
+	HTTP_OK = 200,
+	HTTP_BAD_REQUEST = 400,
+	UPNP_INVALID_ACTION = 401,
+	UPNP_INVALID_ARGS = 402,
+	HTTP_NOT_FOUND = 404,
+	HTTP_PRECONDITION_FAILED = 412,
+	HTTP_INTERNAL_SERVER_ERROR = 500,
+	HTTP_UNIMPLEMENTED = 501,
+	UPNP_ACTION_FAILED = 501,
+	UPNP_ARG_VALUE_INVALID = 600,
+	UPNP_ARG_VALUE_OUT_OF_RANGE = 601,
+	UPNP_OUT_OF_MEMORY = 603
+};
+
+#endif /* HTTP_H */
diff --git a/hostap/src/wps/http_client.c b/hostap/src/wps/http_client.c
new file mode 100644
index 0000000..cdf3a51
--- /dev/null
+++ b/hostap/src/wps/http_client.c
@@ -0,0 +1,362 @@
+/*
+ * http_client - HTTP client
+ * 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 <fcntl.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "httpread.h"
+#include "http_client.h"
+
+
+#define HTTP_CLIENT_TIMEOUT_SEC 30
+
+
+struct http_client {
+	struct sockaddr_in dst;
+	int sd;
+	struct wpabuf *req;
+	size_t req_pos;
+	size_t max_response;
+
+	void (*cb)(void *ctx, struct http_client *c,
+		   enum http_client_event event);
+	void *cb_ctx;
+	struct httpread *hread;
+	struct wpabuf body;
+};
+
+
+static void http_client_timeout(void *eloop_data, void *user_ctx)
+{
+	struct http_client *c = eloop_data;
+	wpa_printf(MSG_DEBUG, "HTTP: Timeout (c=%p)", c);
+	c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
+}
+
+
+static void http_client_got_response(struct httpread *handle, void *cookie,
+				     enum httpread_event e)
+{
+	struct http_client *c = cookie;
+
+	wpa_printf(MSG_DEBUG, "HTTP: httpread callback: handle=%p cookie=%p "
+		   "e=%d", handle, cookie, e);
+
+	eloop_cancel_timeout(http_client_timeout, c, NULL);
+	switch (e) {
+	case HTTPREAD_EVENT_FILE_READY:
+		if (httpread_hdr_type_get(c->hread) == HTTPREAD_HDR_TYPE_REPLY)
+		{
+			int reply_code = httpread_reply_code_get(c->hread);
+			if (reply_code == 200 /* OK */) {
+				wpa_printf(MSG_DEBUG, "HTTP: Response OK from "
+					   "%s:%d",
+					   inet_ntoa(c->dst.sin_addr),
+					   ntohs(c->dst.sin_port));
+				c->cb(c->cb_ctx, c, HTTP_CLIENT_OK);
+			} else {
+				wpa_printf(MSG_DEBUG, "HTTP: Error %d from "
+					   "%s:%d", reply_code,
+					   inet_ntoa(c->dst.sin_addr),
+					   ntohs(c->dst.sin_port));
+				c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
+			}
+		} else
+			c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
+		break;
+	case HTTPREAD_EVENT_TIMEOUT:
+		c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
+		break;
+	case HTTPREAD_EVENT_ERROR:
+		c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
+		break;
+	}
+}
+
+
+static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct http_client *c = eloop_ctx;
+	int res;
+	size_t send_len;
+
+	send_len = wpabuf_len(c->req) - c->req_pos;
+	wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu "
+		   "bytes remaining)",
+		   inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port),
+		   (unsigned long) wpabuf_len(c->req),
+		   (unsigned long) send_len);
+
+	res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, send_len, 0);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s",
+			   strerror(errno));
+		eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
+		c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
+		return;
+	}
+
+	if ((size_t) res < send_len) {
+		wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes "
+			   "remaining",
+			   res, (unsigned long) wpabuf_len(c->req),
+			   (unsigned long) send_len - res);
+		c->req_pos += res;
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "HTTP: Full client request sent to %s:%d",
+		   inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port));
+	eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
+	wpabuf_free(c->req);
+	c->req = NULL;
+
+	c->hread = httpread_create(c->sd, http_client_got_response, c,
+				   c->max_response, HTTP_CLIENT_TIMEOUT_SEC);
+	if (c->hread == NULL) {
+		c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
+		return;
+	}
+}
+
+
+struct http_client * http_client_addr(struct sockaddr_in *dst,
+				      struct wpabuf *req, size_t max_response,
+				      void (*cb)(void *ctx,
+						 struct http_client *c,
+						 enum http_client_event event),
+				      void *cb_ctx)
+{
+	struct http_client *c;
+
+	c = os_zalloc(sizeof(*c));
+	if (c == NULL)
+		return NULL;
+	c->sd = -1;
+	c->dst = *dst;
+	c->max_response = max_response;
+	c->cb = cb;
+	c->cb_ctx = cb_ctx;
+
+	c->sd = socket(AF_INET, SOCK_STREAM, 0);
+	if (c->sd < 0)
+		goto fail;
+
+	if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) {
+		wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) {
+		if (errno != EINPROGRESS) {
+			wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s",
+				   strerror(errno));
+			goto fail;
+		}
+
+		/*
+		 * Continue connecting in the background; eloop will call us
+		 * once the connection is ready (or failed).
+		 */
+	}
+
+	if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready,
+				c, NULL) ||
+	    eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0,
+				   http_client_timeout, c, NULL))
+		goto fail;
+
+	c->req = req;
+
+	return c;
+
+fail:
+	http_client_free(c);
+	return NULL;
+}
+
+
+char * http_client_url_parse(const char *url, struct sockaddr_in *dst,
+			     char **ret_path)
+{
+	char *u, *addr, *port, *path;
+
+	u = os_strdup(url);
+	if (u == NULL)
+		return NULL;
+
+	os_memset(dst, 0, sizeof(*dst));
+	dst->sin_family = AF_INET;
+	addr = u + 7;
+	path = os_strchr(addr, '/');
+	port = os_strchr(addr, ':');
+	if (path == NULL) {
+		path = "/";
+	} else {
+		*path = '\0'; /* temporary nul termination for address */
+		if (port > path)
+			port = NULL;
+	}
+	if (port)
+		*port++ = '\0';
+
+	if (inet_aton(addr, &dst->sin_addr) == 0) {
+		/* TODO: name lookup */
+		wpa_printf(MSG_DEBUG, "HTTP: Unsupported address in URL '%s' "
+			   "(addr='%s' port='%s')",
+			   url, addr, port);
+		os_free(u);
+		return NULL;
+	}
+
+	if (port)
+		dst->sin_port = htons(atoi(port));
+	else
+		dst->sin_port = htons(80);
+
+	if (*path == '\0') {
+		/* remove temporary nul termination for address */
+		*path = '/';
+	}
+
+	*ret_path = path;
+
+	return u;
+}
+
+
+struct http_client * http_client_url(const char *url,
+				     struct wpabuf *req, size_t max_response,
+				     void (*cb)(void *ctx,
+						struct http_client *c,
+						enum http_client_event event),
+				     void *cb_ctx)
+{
+	struct sockaddr_in dst;
+	struct http_client *c;
+	char *u, *path;
+	struct wpabuf *req_buf = NULL;
+
+	if (os_strncmp(url, "http://", 7) != 0)
+		return NULL;
+	u = http_client_url_parse(url, &dst, &path);
+	if (u == NULL)
+		return NULL;
+
+	if (req == NULL) {
+		req_buf = wpabuf_alloc(os_strlen(url) + 1000);
+		if (req_buf == NULL) {
+			os_free(u);
+			return NULL;
+		}
+		req = req_buf;
+		wpabuf_printf(req,
+			      "GET %s HTTP/1.1\r\n"
+			      "Cache-Control: no-cache\r\n"
+			      "Pragma: no-cache\r\n"
+			      "Accept: text/xml, application/xml\r\n"
+			      "User-Agent: wpa_supplicant\r\n"
+			      "Host: %s:%d\r\n"
+			      "\r\n",
+			      path, inet_ntoa(dst.sin_addr),
+			      ntohs(dst.sin_port));
+	}
+	os_free(u);
+
+	c = http_client_addr(&dst, req, max_response, cb, cb_ctx);
+	if (c == NULL) {
+		wpabuf_free(req_buf);
+		return NULL;
+	}
+
+	return c;
+}
+
+
+void http_client_free(struct http_client *c)
+{
+	if (c == NULL)
+		return;
+	httpread_destroy(c->hread);
+	wpabuf_free(c->req);
+	if (c->sd >= 0) {
+		eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
+		close(c->sd);
+	}
+	eloop_cancel_timeout(http_client_timeout, c, NULL);
+	os_free(c);
+}
+
+
+struct wpabuf * http_client_get_body(struct http_client *c)
+{
+	if (c->hread == NULL)
+		return NULL;
+	wpabuf_set(&c->body, httpread_data_get(c->hread),
+		   httpread_length_get(c->hread));
+	return &c->body;
+}
+
+
+char * http_client_get_hdr_line(struct http_client *c, const char *tag)
+{
+	if (c->hread == NULL)
+		return NULL;
+	return httpread_hdr_line_get(c->hread, tag);
+}
+
+
+char * http_link_update(char *url, const char *base)
+{
+	char *n;
+	size_t len;
+	const char *pos;
+
+	/* RFC 2396, Chapter 5.2 */
+	/* TODO: consider adding all cases described in RFC 2396 */
+
+	if (url == NULL)
+		return NULL;
+
+	if (os_strncmp(url, "http://", 7) == 0)
+		return url; /* absolute link */
+
+	if (os_strncmp(base, "http://", 7) != 0)
+		return url; /* unable to handle base URL */
+
+	len = os_strlen(url) + 1 + os_strlen(base) + 1;
+	n = os_malloc(len);
+	if (n == NULL)
+		return url; /* failed */
+
+	if (url[0] == '/') {
+		pos = os_strchr(base + 7, '/');
+		if (pos == NULL) {
+			os_snprintf(n, len, "%s%s", base, url);
+		} else {
+			os_memcpy(n, base, pos - base);
+			os_memcpy(n + (pos - base), url, os_strlen(url) + 1);
+		}
+	} else {
+		pos = os_strrchr(base + 7, '/');
+		if (pos == NULL) {
+			os_snprintf(n, len, "%s/%s", base, url);
+		} else {
+			os_memcpy(n, base, pos - base + 1);
+			os_memcpy(n + (pos - base) + 1, url, os_strlen(url) +
+				  1);
+		}
+	}
+
+	os_free(url);
+
+	return n;
+}
diff --git a/hostap/src/wps/http_client.h b/hostap/src/wps/http_client.h
new file mode 100644
index 0000000..ddee2ad
--- /dev/null
+++ b/hostap/src/wps/http_client.h
@@ -0,0 +1,40 @@
+/*
+ * http_client - HTTP client
+ * 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.
+ */
+
+#ifndef HTTP_CLIENT_H
+#define HTTP_CLIENT_H
+
+struct http_client;
+
+enum http_client_event {
+	HTTP_CLIENT_FAILED,
+	HTTP_CLIENT_TIMEOUT,
+	HTTP_CLIENT_OK,
+	HTTP_CLIENT_INVALID_REPLY,
+};
+
+char * http_client_url_parse(const char *url, struct sockaddr_in *dst,
+			     char **path);
+struct http_client * http_client_addr(struct sockaddr_in *dst,
+				      struct wpabuf *req, size_t max_response,
+				      void (*cb)(void *ctx,
+						 struct http_client *c,
+						 enum http_client_event event),
+				      void *cb_ctx);
+struct http_client * http_client_url(const char *url,
+				     struct wpabuf *req, size_t max_response,
+				     void (*cb)(void *ctx,
+						struct http_client *c,
+						enum http_client_event event),
+				     void *cb_ctx);
+void http_client_free(struct http_client *c);
+struct wpabuf * http_client_get_body(struct http_client *c);
+char * http_client_get_hdr_line(struct http_client *c, const char *tag);
+char * http_link_update(char *url, const char *base);
+
+#endif /* HTTP_CLIENT_H */
diff --git a/hostap/src/wps/http_server.c b/hostap/src/wps/http_server.c
new file mode 100644
index 0000000..507abe8
--- /dev/null
+++ b/hostap/src/wps/http_server.c
@@ -0,0 +1,314 @@
+/*
+ * http_server - HTTP server
+ * 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 <fcntl.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "httpread.h"
+#include "http_server.h"
+
+#define HTTP_SERVER_TIMEOUT 30
+#define HTTP_SERVER_MAX_REQ_LEN 8000
+#define HTTP_SERVER_MAX_CONNECTIONS 10
+
+struct http_request {
+	struct http_request *next;
+	struct http_server *srv;
+	int fd;
+	struct sockaddr_in cli;
+	struct httpread *hread;
+};
+
+struct http_server {
+	void (*cb)(void *ctx, struct http_request *req);
+	void *cb_ctx;
+
+	int fd;
+	int port;
+
+	struct http_request *requests;
+	unsigned int request_count;
+};
+
+
+static void http_request_cb(struct httpread *handle, void *cookie,
+			    enum httpread_event en)
+{
+	struct http_request *req = cookie;
+	struct http_server *srv = req->srv;
+
+	if (en == HTTPREAD_EVENT_FILE_READY) {
+		wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d received",
+			   inet_ntoa(req->cli.sin_addr),
+			   ntohs(req->cli.sin_port));
+		srv->cb(srv->cb_ctx, req);
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d could not be received "
+		   "completely", inet_ntoa(req->cli.sin_addr),
+		   ntohs(req->cli.sin_port));
+	http_request_deinit(req);
+}
+
+
+static struct http_request * http_request_init(struct http_server *srv, int fd,
+					       struct sockaddr_in *cli)
+{
+	struct http_request *req;
+
+	if (srv->request_count >= HTTP_SERVER_MAX_CONNECTIONS) {
+		wpa_printf(MSG_DEBUG, "HTTP: Too many concurrent requests");
+		return NULL;
+	}
+
+	req = os_zalloc(sizeof(*req));
+	if (req == NULL)
+		return NULL;
+
+	req->srv = srv;
+	req->fd = fd;
+	req->cli = *cli;
+
+	req->hread = httpread_create(req->fd, http_request_cb, req,
+				     HTTP_SERVER_MAX_REQ_LEN,
+				     HTTP_SERVER_TIMEOUT);
+	if (req->hread == NULL) {
+		http_request_deinit(req);
+		return NULL;
+	}
+
+	return req;
+}
+
+
+void http_request_deinit(struct http_request *req)
+{
+	struct http_request *r, *p;
+	struct http_server *srv;
+
+	if (req == NULL)
+		return;
+
+	srv = req->srv;
+	p = NULL;
+	r = srv->requests;
+	while (r) {
+		if (r == req) {
+			if (p)
+				p->next = r->next;
+			else
+				srv->requests = r->next;
+			srv->request_count--;
+			break;
+		}
+		p = r;
+		r = r->next;
+	}
+
+	httpread_destroy(req->hread);
+	close(req->fd);
+	os_free(req);
+}
+
+
+static void http_request_free_all(struct http_request *req)
+{
+	struct http_request *prev;
+	while (req) {
+		prev = req;
+		req = req->next;
+		http_request_deinit(prev);
+	}
+}
+
+
+void http_request_send(struct http_request *req, struct wpabuf *resp)
+{
+	int res;
+
+	wpa_printf(MSG_DEBUG, "HTTP: Send %lu byte response to %s:%d",
+		   (unsigned long) wpabuf_len(resp),
+		   inet_ntoa(req->cli.sin_addr),
+		   ntohs(req->cli.sin_port));
+
+	res = send(req->fd, wpabuf_head(resp), wpabuf_len(resp), 0);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "HTTP: Send failed: %s",
+			   strerror(errno));
+	} else if ((size_t) res < wpabuf_len(resp)) {
+		wpa_printf(MSG_DEBUG, "HTTP: Sent only %d of %lu bytes",
+			   res, (unsigned long) wpabuf_len(resp));
+		/* TODO: add eloop handler for sending rest of the data */
+	}
+
+	wpabuf_free(resp);
+}
+
+
+void http_request_send_and_deinit(struct http_request *req,
+				  struct wpabuf *resp)
+{
+	http_request_send(req, resp);
+	http_request_deinit(req);
+}
+
+
+enum httpread_hdr_type http_request_get_type(struct http_request *req)
+{
+	return httpread_hdr_type_get(req->hread);
+}
+
+
+char * http_request_get_uri(struct http_request *req)
+{
+	return httpread_uri_get(req->hread);
+}
+
+
+char * http_request_get_hdr(struct http_request *req)
+{
+	return httpread_hdr_get(req->hread);
+}
+
+
+char * http_request_get_data(struct http_request *req)
+{
+	return httpread_data_get(req->hread);
+}
+
+
+char * http_request_get_hdr_line(struct http_request *req, const char *tag)
+{
+	return httpread_hdr_line_get(req->hread, tag);
+}
+
+
+struct sockaddr_in * http_request_get_cli_addr(struct http_request *req)
+{
+	return &req->cli;
+}
+
+
+static void http_server_cb(int sd, void *eloop_ctx, void *sock_ctx)
+{
+	struct sockaddr_in addr;
+	socklen_t addr_len = sizeof(addr);
+	struct http_server *srv = eloop_ctx;
+	int conn;
+	struct http_request *req;
+
+	conn = accept(srv->fd, (struct sockaddr *) &addr, &addr_len);
+	if (conn < 0) {
+		wpa_printf(MSG_DEBUG, "HTTP: Failed to accept new connection: "
+			   "%s", strerror(errno));
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "HTTP: Connection from %s:%d",
+		   inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+	req = http_request_init(srv, conn, &addr);
+	if (req == NULL) {
+		close(conn);
+		return;
+	}
+
+	req->next = srv->requests;
+	srv->requests = req;
+	srv->request_count++;
+}
+
+
+struct http_server * http_server_init(struct in_addr *addr, int port,
+				      void (*cb)(void *ctx,
+						 struct http_request *req),
+				      void *cb_ctx)
+{
+	struct sockaddr_in sin;
+	struct http_server *srv;
+	int on = 1;
+
+	srv = os_zalloc(sizeof(*srv));
+	if (srv == NULL)
+		return NULL;
+	srv->cb = cb;
+	srv->cb_ctx = cb_ctx;
+
+	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)
+		srv->port = 49152;
+	else
+		srv->port = port;
+
+	os_memset(&sin, 0, sizeof(sin));
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = addr->s_addr;
+
+	for (;;) {
+		sin.sin_port = htons(srv->port);
+		if (bind(srv->fd, (struct sockaddr *) &sin, sizeof(sin)) == 0)
+			break;
+		if (errno == EADDRINUSE) {
+			/* search for unused port */
+			if (++srv->port == 65535 || port >= 0)
+				goto fail;
+			continue;
+		}
+		wpa_printf(MSG_DEBUG, "HTTP: Failed to bind server port %d: "
+			   "%s", srv->port, strerror(errno));
+		goto fail;
+	}
+	if (listen(srv->fd, 10 /* max backlog */) < 0 ||
+	    fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0 ||
+	    eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb,
+				srv, NULL))
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "HTTP: Started server on %s:%d",
+		   inet_ntoa(*addr), srv->port);
+
+	return srv;
+
+fail:
+	http_server_deinit(srv);
+	return NULL;
+}
+
+
+void http_server_deinit(struct http_server *srv)
+{
+	if (srv == NULL)
+		return;
+	if (srv->fd >= 0) {
+		eloop_unregister_sock(srv->fd, EVENT_TYPE_READ);
+		close(srv->fd);
+	}
+	http_request_free_all(srv->requests);
+
+	os_free(srv);
+}
+
+
+int http_server_get_port(struct http_server *srv)
+{
+	return srv->port;
+}
diff --git a/hostap/src/wps/http_server.h b/hostap/src/wps/http_server.h
new file mode 100644
index 0000000..4b2b749
--- /dev/null
+++ b/hostap/src/wps/http_server.h
@@ -0,0 +1,33 @@
+/*
+ * http_server - HTTP server
+ * 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.
+ */
+
+#ifndef HTTP_SERVER_H
+#define HTTP_SERVER_H
+
+struct http_server;
+struct http_request;
+
+void http_request_deinit(struct http_request *req);
+void http_request_send(struct http_request *req, struct wpabuf *resp);
+void http_request_send_and_deinit(struct http_request *req,
+				  struct wpabuf *resp);
+enum httpread_hdr_type http_request_get_type(struct http_request *req);
+char * http_request_get_uri(struct http_request *req);
+char * http_request_get_hdr(struct http_request *req);
+char * http_request_get_data(struct http_request *req);
+char * http_request_get_hdr_line(struct http_request *req, const char *tag);
+struct sockaddr_in * http_request_get_cli_addr(struct http_request *req);
+
+struct http_server * http_server_init(struct in_addr *addr, int port,
+				      void (*cb)(void *ctx,
+						 struct http_request *req),
+				      void *cb_ctx);
+void http_server_deinit(struct http_server *srv);
+int http_server_get_port(struct http_server *srv);
+
+#endif /* HTTP_SERVER_H */
diff --git a/hostap/src/wps/httpread.c b/hostap/src/wps/httpread.c
new file mode 100644
index 0000000..7a2ba50
--- /dev/null
+++ b/hostap/src/wps/httpread.c
@@ -0,0 +1,848 @@
+/*
+ * httpread - Manage reading file(s) from HTTP/TCP socket
+ * Author: Ted Merrill
+ * Copyright 2008 Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * The files are buffered via internal callbacks from eloop, then presented to
+ * an application callback routine when completely read into memory. May also
+ * be used if no file is expected but just to get the header, including HTTP
+ * replies (e.g. HTTP/1.1 200 OK etc.).
+ *
+ * This does not attempt to be an optimally efficient implementation, but does
+ * attempt to be of reasonably small size and memory consumption; assuming that
+ * only small files are to be read. A maximum file size is provided by
+ * application and enforced.
+ *
+ * It is assumed that the application does not expect any of the following:
+ * -- transfer encoding other than chunked
+ * -- trailer fields
+ * It is assumed that, even if the other side requested that the connection be
+ * kept open, that we will close it (thus HTTP messages sent by application
+ * should have the connection closed field); this is allowed by HTTP/1.1 and
+ * simplifies things for us.
+ *
+ * Other limitations:
+ * -- HTTP header may not exceed a hard-coded size.
+ *
+ * Notes:
+ * This code would be massively simpler without some of the new features of
+ * HTTP/1.1, especially chunked data.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "httpread.h"
+
+
+/* Tunable parameters */
+#define HTTPREAD_READBUF_SIZE 1024      /* read in chunks of this size */
+#define HTTPREAD_HEADER_MAX_SIZE 4096   /* max allowed for headers */
+#define HTTPREAD_BODYBUF_DELTA 4096     /* increase allocation by this */
+
+
+/* control instance -- actual definition (opaque to application)
+ */
+struct httpread {
+	/* information from creation */
+	int sd;         /* descriptor of TCP socket to read from */
+	void (*cb)(struct httpread *handle, void *cookie,
+		    enum httpread_event e);  /* call on event */
+	void *cookie;   /* pass to callback */
+	int max_bytes;          /* maximum file size else abort it */
+	int timeout_seconds;            /* 0 or total duration timeout period */
+
+	/* dynamically used information follows */
+
+	int got_hdr;            /* nonzero when header is finalized */
+	char hdr[HTTPREAD_HEADER_MAX_SIZE+1];   /* headers stored here */
+	int hdr_nbytes;
+
+	enum httpread_hdr_type hdr_type;
+	int version;            /* 1 if we've seen 1.1 */
+	int reply_code;         /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */
+	int got_content_length; /* true if we know content length for sure */
+	int content_length;     /* body length,  iff got_content_length */
+	int chunked;            /* nonzero for chunked data */
+	char *uri;
+
+	int got_body;           /* nonzero when body is finalized */
+	char *body;
+	int body_nbytes;
+	int body_alloc_nbytes;  /* amount allocated */
+
+	int got_file;           /* here when we are done */
+
+	/* The following apply if data is chunked: */
+	int in_chunk_data;      /* 0=in/at header, 1=in the data or tail*/
+	int chunk_start;        /* offset in body of chunk hdr or data */
+	int chunk_size;         /* data of chunk (not hdr or ending CRLF)*/
+	int in_trailer;         /* in header fields after data (chunked only)*/
+	enum trailer_state {
+		trailer_line_begin = 0,
+		trailer_empty_cr,       /* empty line + CR */
+		trailer_nonempty,
+		trailer_nonempty_cr,
+	} trailer_state;
+};
+
+
+/* Check words for equality, where words consist of graphical characters
+ * delimited by whitespace
+ * Returns nonzero if "equal" doing case insensitive comparison.
+ */
+static int word_eq(char *s1, char *s2)
+{
+	int c1;
+	int c2;
+	int end1 = 0;
+	int end2 = 0;
+	for (;;) {
+		c1 = *s1++;
+		c2 = *s2++;
+		if (isalpha(c1) && isupper(c1))
+			c1 = tolower(c1);
+		if (isalpha(c2) && isupper(c2))
+			c2 = tolower(c2);
+		end1 = !isgraph(c1);
+		end2 = !isgraph(c2);
+		if (end1 || end2 || c1 != c2)
+			break;
+	}
+	return end1 && end2;  /* reached end of both words? */
+}
+
+
+static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
+
+/* httpread_destroy -- if h is non-NULL, clean up
+ * This must eventually be called by the application following
+ * call of the application's callback and may be called
+ * earlier if desired.
+ */
+void httpread_destroy(struct httpread *h)
+{
+	wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h);
+	if (!h)
+		return;
+
+	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 */
+	h->sd = -1;     /* aid debugging */
+	os_free(h);
+}
+
+
+/* httpread_timeout_handler -- called on excessive total duration
+ */
+static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
+{
+	struct httpread *h = user_ctx;
+	wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
+	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
+}
+
+
+/* Analyze options only so far as is needed to correctly obtain the file.
+ * The application can look at the raw header to find other options.
+ */
+static int httpread_hdr_option_analyze(
+	struct httpread *h,
+	char *hbp       /* pointer to current line in header buffer */
+	)
+{
+	if (word_eq(hbp, "CONTENT-LENGTH:")) {
+		while (isgraph(*hbp))
+			hbp++;
+		while (*hbp == ' ' || *hbp == '\t')
+			hbp++;
+		if (!isdigit(*hbp))
+			return -1;
+		h->content_length = atol(hbp);
+		if (h->content_length < 0 || h->content_length > h->max_bytes) {
+			wpa_printf(MSG_DEBUG,
+				   "httpread: Unacceptable Content-Length %d",
+				   h->content_length);
+			return -1;
+		}
+		h->got_content_length = 1;
+		return 0;
+	}
+	if (word_eq(hbp, "TRANSFER_ENCODING:") ||
+	    word_eq(hbp, "TRANSFER-ENCODING:")) {
+		while (isgraph(*hbp))
+			hbp++;
+		while (*hbp == ' ' || *hbp == '\t')
+			hbp++;
+		/* There should (?) be no encodings of interest
+		 * other than chunked...
+		 */
+		if (word_eq(hbp, "CHUNKED")) {
+			h->chunked = 1;
+			h->in_chunk_data = 0;
+			/* ignore possible ;<parameters> */
+		}
+		return 0;
+	}
+	/* skip anything we don't know, which is a lot */
+	return 0;
+}
+
+
+static int httpread_hdr_analyze(struct httpread *h)
+{
+	char *hbp = h->hdr;      /* pointer into h->hdr */
+	int standard_first_line = 1;
+
+	/* First line is special */
+	h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
+	if (!isgraph(*hbp))
+		goto bad;
+	if (os_strncmp(hbp, "HTTP/", 5) == 0) {
+		h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
+		standard_first_line = 0;
+		hbp += 5;
+		if (hbp[0] == '1' && hbp[1] == '.' &&
+		    isdigit(hbp[2]) && hbp[2] != '0')
+			h->version = 1;
+		while (isgraph(*hbp))
+			hbp++;
+		while (*hbp == ' ' || *hbp == '\t')
+			hbp++;
+		if (!isdigit(*hbp))
+			goto bad;
+		h->reply_code = atol(hbp);
+	} else if (word_eq(hbp, "GET"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_GET;
+	else if (word_eq(hbp, "HEAD"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
+	else if (word_eq(hbp, "POST"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_POST;
+	else if (word_eq(hbp, "PUT"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
+	else if (word_eq(hbp, "DELETE"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
+	else if (word_eq(hbp, "TRACE"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
+	else if (word_eq(hbp, "CONNECT"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
+	else if (word_eq(hbp, "NOTIFY"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
+	else if (word_eq(hbp, "M-SEARCH"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
+	else if (word_eq(hbp, "M-POST"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
+	else if (word_eq(hbp, "SUBSCRIBE"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
+	else if (word_eq(hbp, "UNSUBSCRIBE"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
+	else {
+	}
+
+	if (standard_first_line) {
+		char *rawuri;
+		char *uri;
+		/* skip type */
+		while (isgraph(*hbp))
+			hbp++;
+		while (*hbp == ' ' || *hbp == '\t')
+			hbp++;
+		/* parse uri.
+		 * Find length, allocate memory for translated
+		 * copy, then translate by changing %<hex><hex>
+		 * into represented value.
+		 */
+		rawuri = hbp;
+		while (isgraph(*hbp))
+			hbp++;
+		h->uri = os_malloc((hbp - rawuri) + 1);
+		if (h->uri == NULL)
+			goto bad;
+		uri = h->uri;
+		while (rawuri < hbp) {
+			int c = *rawuri;
+			if (c == '%' &&
+			    isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
+				*uri++ = hex2byte(rawuri + 1);
+				rawuri += 3;
+			} else {
+				*uri++ = c;
+				rawuri++;
+			}
+		}
+		*uri = 0;       /* null terminate */
+		while (*hbp == ' ' || *hbp == '\t')
+			hbp++;
+		/* get version */
+		if (0 == strncmp(hbp, "HTTP/", 5)) {
+			hbp += 5;
+			if (hbp[0] == '1' && hbp[1] == '.' &&
+			    isdigit(hbp[2]) && hbp[2] != '0')
+				h->version = 1;
+		}
+	}
+	/* skip rest of line */
+	while (*hbp)
+		if (*hbp++ == '\n')
+			break;
+
+	/* Remainder of lines are options, in any order;
+	 * or empty line to terminate
+	 */
+	for (;;) {
+		/* Empty line to terminate */
+		if (hbp[0] == '\n' ||
+		    (hbp[0] == '\r' && hbp[1] == '\n'))
+			break;
+		if (!isgraph(*hbp))
+			goto bad;
+		if (httpread_hdr_option_analyze(h, hbp))
+			goto bad;
+		/* skip line */
+		while (*hbp)
+			if (*hbp++ == '\n')
+				break;
+	}
+
+	/* chunked overrides content-length always */
+	if (h->chunked)
+		h->got_content_length = 0;
+
+	/* For some types, we should not try to read a body
+	 * This is in addition to the application determining
+	 * that we should not read a body.
+	 */
+	switch (h->hdr_type) {
+	case HTTPREAD_HDR_TYPE_REPLY:
+		/* Some codes can have a body and some not.
+		 * For now, just assume that any other than 200
+		 * do not...
+		 */
+		if (h->reply_code != 200)
+			h->max_bytes = 0;
+		break;
+	case HTTPREAD_HDR_TYPE_GET:
+	case HTTPREAD_HDR_TYPE_HEAD:
+		/* in practice it appears that it is assumed
+		 * that GETs have a body length of 0... ?
+		 */
+		if (h->chunked == 0 && h->got_content_length == 0)
+			h->max_bytes = 0;
+		break;
+	case HTTPREAD_HDR_TYPE_POST:
+	case HTTPREAD_HDR_TYPE_PUT:
+	case HTTPREAD_HDR_TYPE_DELETE:
+	case HTTPREAD_HDR_TYPE_TRACE:
+	case HTTPREAD_HDR_TYPE_CONNECT:
+	case HTTPREAD_HDR_TYPE_NOTIFY:
+	case HTTPREAD_HDR_TYPE_M_SEARCH:
+	case HTTPREAD_HDR_TYPE_M_POST:
+	case HTTPREAD_HDR_TYPE_SUBSCRIBE:
+	case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
+	default:
+		break;
+	}
+
+	return 0;
+
+bad:
+	/* Error */
+	return -1;
+}
+
+
+/* httpread_read_handler -- called when socket ready to read
+ *
+ * Note: any extra data we read past end of transmitted file is ignored;
+ * if we were to support keeping connections open for multiple files then
+ * this would have to be addressed.
+ */
+static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+	struct httpread *h = sock_ctx;
+	int nread;
+	char *rbp;      /* pointer into read buffer */
+	char *hbp;      /* pointer into header buffer */
+	char *bbp;      /* pointer into body buffer */
+	char readbuf[HTTPREAD_READBUF_SIZE];  /* temp use to read into */
+
+	/* read some at a time, then search for the interal
+	 * boundaries between header and data and etc.
+	 */
+	wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h);
+	nread = read(h->sd, readbuf, sizeof(readbuf));
+	if (nread < 0) {
+		wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno));
+		goto bad;
+	}
+	wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread);
+	if (nread == 0) {
+		/* end of transmission... this may be normal
+		 * or may be an error... in some cases we can't
+		 * tell which so we must assume it is normal then.
+		 */
+		if (!h->got_hdr) {
+			/* Must at least have completed header */
+			wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
+			goto bad;
+		}
+		if (h->chunked || h->got_content_length) {
+			/* Premature EOF; e.g. dropped connection */
+			wpa_printf(MSG_DEBUG,
+				   "httpread premature eof(%p) %d/%d",
+				   h, h->body_nbytes,
+				   h->content_length);
+			goto bad;
+		}
+		/* No explicit length, hopefully we have all the data
+		 * although dropped connections can cause false
+		 * end
+		 */
+		wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
+		h->got_body = 1;
+		goto got_file;
+	}
+	rbp = readbuf;
+
+	/* Header consists of text lines (terminated by both CR and LF)
+	 * and an empty line (CR LF only).
+	 */
+	if (!h->got_hdr) {
+		hbp = h->hdr + h->hdr_nbytes;
+		/* add to headers until:
+		 *      -- we run out of data in read buffer
+		 *      -- or, we run out of header buffer room
+		 *      -- or, we get double CRLF in headers
+		 */
+		for (;;) {
+			if (nread == 0)
+				goto get_more;
+			if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
+				wpa_printf(MSG_DEBUG,
+					   "httpread: Too long header");
+				goto bad;
+			}
+			*hbp++ = *rbp++;
+			nread--;
+			h->hdr_nbytes++;
+			if (h->hdr_nbytes >= 4 &&
+			    hbp[-1] == '\n' &&
+			    hbp[-2] == '\r' &&
+			    hbp[-3] == '\n' &&
+			    hbp[-4] == '\r' ) {
+				h->got_hdr = 1;
+				*hbp = 0;       /* null terminate */
+				break;
+			}
+		}
+		/* here we've just finished reading the header */
+		if (httpread_hdr_analyze(h)) {
+			wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
+			goto bad;
+		}
+		if (h->max_bytes == 0) {
+			wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)",
+				   h);
+			goto got_file;
+		}
+		if (h->got_content_length && h->content_length == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "httpread zero content length(%p)", h);
+			goto got_file;
+		}
+	}
+
+	/* Certain types of requests never have data and so
+	 * must be specially recognized.
+	 */
+	if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
+	    !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
+	    !os_strncasecmp(h->hdr, "HEAD", 4) ||
+	    !os_strncasecmp(h->hdr, "GET", 3)) {
+		if (!h->got_body) {
+			wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type");
+		}
+		h->got_body = 1;
+		goto got_file;
+	}
+
+	/* Data can be just plain binary data, or if "chunked"
+	 * consists of chunks each with a header, ending with
+	 * an ending header.
+	 */
+	if (nread == 0)
+		goto get_more;
+	if (!h->got_body) {
+		/* Here to get (more of) body */
+		/* ensure we have enough room for worst case for body
+		 * plus a null termination character
+		 */
+		if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
+			char *new_body;
+			int new_alloc_nbytes;
+
+			if (h->body_nbytes >= h->max_bytes) {
+				wpa_printf(MSG_DEBUG,
+					   "httpread: body_nbytes=%d >= max_bytes=%d",
+					   h->body_nbytes, h->max_bytes);
+				goto bad;
+			}
+			new_alloc_nbytes = h->body_alloc_nbytes +
+				HTTPREAD_BODYBUF_DELTA;
+			/* For content-length case, the first time
+			 * through we allocate the whole amount
+			 * we need.
+			 */
+			if (h->got_content_length &&
+			    new_alloc_nbytes < (h->content_length + 1))
+				new_alloc_nbytes = h->content_length + 1;
+			if (new_alloc_nbytes < h->body_alloc_nbytes ||
+			    new_alloc_nbytes > h->max_bytes +
+			    HTTPREAD_BODYBUF_DELTA) {
+				wpa_printf(MSG_DEBUG,
+					   "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)",
+					   new_alloc_nbytes,
+					   h->body_alloc_nbytes,
+					   h->max_bytes);
+				goto bad;
+			}
+			if ((new_body = os_realloc(h->body, new_alloc_nbytes))
+			    == NULL) {
+				wpa_printf(MSG_DEBUG,
+					   "httpread: Failed to reallocate buffer (len=%d)",
+					   new_alloc_nbytes);
+				goto bad;
+			}
+
+			h->body = new_body;
+			h->body_alloc_nbytes = new_alloc_nbytes;
+		}
+		/* add bytes */
+		bbp = h->body + h->body_nbytes;
+		for (;;) {
+			int ncopy;
+			/* See if we need to stop */
+			if (h->chunked && h->in_chunk_data == 0) {
+				/* in chunk header */
+				char *cbp = h->body + h->chunk_start;
+				if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
+				    bbp[-1] == '\n') {
+					/* end of chunk hdr line */
+					/* hdr line consists solely
+					 * of a hex numeral and CFLF
+					 */
+					if (!isxdigit(*cbp)) {
+						wpa_printf(MSG_DEBUG,
+							   "httpread: Unexpected chunk header value (not a hex digit)");
+						goto bad;
+					}
+					h->chunk_size = strtoul(cbp, NULL, 16);
+					if (h->chunk_size < 0 ||
+					    h->chunk_size > h->max_bytes) {
+						wpa_printf(MSG_DEBUG,
+							   "httpread: Invalid chunk size %d",
+							   h->chunk_size);
+						goto bad;
+					}
+					/* throw away chunk header
+					 * so we have only real data
+					 */
+					h->body_nbytes = h->chunk_start;
+					bbp = cbp;
+					if (h->chunk_size == 0) {
+						/* end of chunking */
+						/* trailer follows */
+						h->in_trailer = 1;
+						wpa_printf(MSG_DEBUG,
+							   "httpread end chunks(%p)",
+							   h);
+						break;
+					}
+					h->in_chunk_data = 1;
+					/* leave chunk_start alone */
+				}
+			} else if (h->chunked) {
+				/* in chunk data */
+				if ((h->body_nbytes - h->chunk_start) ==
+				    (h->chunk_size + 2)) {
+					/* end of chunk reached,
+					 * new chunk starts
+					 */
+					/* check chunk ended w/ CRLF
+					 * which we'll throw away
+					 */
+					if (bbp[-1] == '\n' &&
+					    bbp[-2] == '\r') {
+					} else {
+						wpa_printf(MSG_DEBUG,
+							   "httpread: Invalid chunk end");
+						goto bad;
+					}
+					h->body_nbytes -= 2;
+					bbp -= 2;
+					h->chunk_start = h->body_nbytes;
+					h->in_chunk_data = 0;
+					h->chunk_size = 0; /* just in case */
+				}
+			} else if (h->got_content_length &&
+				   h->body_nbytes >= h->content_length) {
+				h->got_body = 1;
+				wpa_printf(MSG_DEBUG,
+					   "httpread got content(%p)", h);
+				goto got_file;
+			}
+			if (nread <= 0)
+				break;
+			/* Now transfer. Optimize using memcpy where we can. */
+			if (h->chunked && h->in_chunk_data) {
+				/* copy up to remainder of chunk data
+				 * plus the required CR+LF at end
+				 */
+				ncopy = (h->chunk_start + h->chunk_size + 2) -
+					h->body_nbytes;
+			} else if (h->chunked) {
+				/*in chunk header -- don't optimize */
+				*bbp++ = *rbp++;
+				nread--;
+				h->body_nbytes++;
+				continue;
+			} else if (h->got_content_length) {
+				ncopy = h->content_length - h->body_nbytes;
+			} else {
+				ncopy = nread;
+			}
+			/* Note: should never be 0 */
+			if (ncopy < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "httpread: Invalid ncopy=%d", ncopy);
+				goto bad;
+			}
+			if (ncopy > nread)
+				ncopy = nread;
+			os_memcpy(bbp, rbp, ncopy);
+			bbp += ncopy;
+			h->body_nbytes += ncopy;
+			rbp += ncopy;
+			nread -= ncopy;
+		}       /* body copy loop */
+	}       /* !got_body */
+	if (h->chunked && h->in_trailer) {
+		/* If "chunked" then there is always a trailer,
+		 * consisting of zero or more non-empty lines
+		 * ending with CR LF and then an empty line w/ CR LF.
+		 * We do NOT support trailers except to skip them --
+		 * this is supported (generally) by the http spec.
+		 */
+		for (;;) {
+			int c;
+			if (nread <= 0)
+				break;
+			c = *rbp++;
+			nread--;
+			switch (h->trailer_state) {
+			case trailer_line_begin:
+				if (c == '\r')
+					h->trailer_state = trailer_empty_cr;
+				else
+					h->trailer_state = trailer_nonempty;
+				break;
+			case trailer_empty_cr:
+				/* end empty line */
+				if (c == '\n') {
+					h->trailer_state = trailer_line_begin;
+					h->in_trailer = 0;
+					wpa_printf(MSG_DEBUG,
+						   "httpread got content(%p)",
+						   h);
+					h->got_body = 1;
+					goto got_file;
+				}
+				h->trailer_state = trailer_nonempty;
+				break;
+			case trailer_nonempty:
+				if (c == '\r')
+					h->trailer_state = trailer_nonempty_cr;
+				break;
+			case trailer_nonempty_cr:
+				if (c == '\n')
+					h->trailer_state = trailer_line_begin;
+				else
+					h->trailer_state = trailer_nonempty;
+				break;
+			}
+		}
+	}
+	goto get_more;
+
+bad:
+	/* Error */
+	wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
+	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
+	return;
+
+get_more:
+	wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h);
+	return;
+
+got_file:
+	wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d",
+		   h->body_nbytes, h->hdr_type);
+	wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body",
+			  h->body, h->body_nbytes);
+	/* Null terminate for convenience of some applications */
+	if (h->body)
+		h->body[h->body_nbytes] = 0; /* null terminate */
+	h->got_file = 1;
+	/* Assume that we do NOT support keeping connection alive,
+	 * and just in case somehow we don't get destroyed right away,
+	 * unregister now.
+	 */
+	eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
+	/* The application can destroy us whenever they feel like...
+	 * cancel timeout.
+	 */
+	eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
+	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
+}
+
+
+/* httpread_create -- start a new reading session making use of eloop.
+ * The new instance will use the socket descriptor for reading (until
+ * it gets a file and not after) but will not close the socket, even
+ * when the instance is destroyed (the application must do that).
+ * Return NULL on error.
+ *
+ * Provided that httpread_create successfully returns a handle,
+ * the callback fnc is called to handle httpread_event events.
+ * The caller should do destroy on any errors or unknown events.
+ *
+ * Pass max_bytes == 0 to not read body at all (required for e.g.
+ * reply to HEAD request).
+ */
+struct httpread * httpread_create(
+	int sd,	 /* descriptor of TCP socket to read from */
+	void (*cb)(struct httpread *handle, void *cookie,
+		   enum httpread_event e),  /* call on event */
+	void *cookie,    /* pass to callback */
+	int max_bytes,	  /* maximum body size else abort it */
+	int timeout_seconds     /* 0; or total duration timeout period */
+	)
+{
+	struct httpread *h = NULL;
+
+	h = os_zalloc(sizeof(*h));
+	if (h == NULL)
+		goto fail;
+	h->sd = sd;
+	h->cb = cb;
+	h->cookie = cookie;
+	h->max_bytes = max_bytes;
+	h->timeout_seconds = timeout_seconds;
+
+	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;
+	}
+	return h;
+
+fail:
+
+	/* Error */
+	httpread_destroy(h);
+	return NULL;
+}
+
+
+/* httpread_hdr_type_get -- When file is ready, returns header type. */
+enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
+{
+	return h->hdr_type;
+}
+
+
+/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
+ * or possibly NULL (which would be an error).
+ */
+char * httpread_uri_get(struct httpread *h)
+{
+	return h->uri;
+}
+
+
+/* httpread_reply_code_get -- When reply is ready, returns reply code */
+int httpread_reply_code_get(struct httpread *h)
+{
+	return h->reply_code;
+}
+
+
+/* httpread_length_get -- When file is ready, returns file length. */
+int httpread_length_get(struct httpread *h)
+{
+	return h->body_nbytes;
+}
+
+
+/* httpread_data_get -- When file is ready, returns file content
+ * with null byte appened.
+ * Might return NULL in some error condition.
+ */
+void * httpread_data_get(struct httpread *h)
+{
+	return h->body ? h->body : "";
+}
+
+
+/* httpread_hdr_get -- When file is ready, returns header content
+ * with null byte appended.
+ * Might return NULL in some error condition.
+ */
+char * httpread_hdr_get(struct httpread *h)
+{
+	return h->hdr;
+}
+
+
+/* httpread_hdr_line_get -- When file is ready, returns pointer
+ * to line within header content matching the given tag
+ * (after the tag itself and any spaces/tabs).
+ *
+ * The tag should end with a colon for reliable matching.
+ *
+ * If not found, returns NULL;
+ */
+char * httpread_hdr_line_get(struct httpread *h, const char *tag)
+{
+	int tag_len = os_strlen(tag);
+	char *hdr = h->hdr;
+	hdr = os_strchr(hdr, '\n');
+	if (hdr == NULL)
+		return NULL;
+	hdr++;
+	for (;;) {
+		if (!os_strncasecmp(hdr, tag, tag_len)) {
+			hdr += tag_len;
+			while (*hdr == ' ' || *hdr == '\t')
+				hdr++;
+			return hdr;
+		}
+		hdr = os_strchr(hdr, '\n');
+		if (hdr == NULL)
+			return NULL;
+		hdr++;
+	}
+}
diff --git a/hostap/src/wps/httpread.h b/hostap/src/wps/httpread.h
new file mode 100644
index 0000000..454b618
--- /dev/null
+++ b/hostap/src/wps/httpread.h
@@ -0,0 +1,117 @@
+/*
+ * httpread - Manage reading file(s) from HTTP/TCP socket
+ * Author: Ted Merrill
+ * Copyright 2008 Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HTTPREAD_H
+#define HTTPREAD_H
+
+/* event types (passed to callback) */
+enum httpread_event {
+	HTTPREAD_EVENT_FILE_READY = 1,        /* including reply ready */
+	HTTPREAD_EVENT_TIMEOUT = 2,
+	HTTPREAD_EVENT_ERROR = 3      /* misc. error, esp malloc error */
+};
+
+
+/* header type detected
+ * available to callback via call to httpread_reply_code_get()
+ */
+enum httpread_hdr_type {
+	HTTPREAD_HDR_TYPE_UNKNOWN = 0,      /* none of the following */
+	HTTPREAD_HDR_TYPE_REPLY = 1,        /* hdr begins w/ HTTP/ */
+	HTTPREAD_HDR_TYPE_GET = 2,          /* hdr begins with GET<sp> */
+	HTTPREAD_HDR_TYPE_HEAD = 3,         /* hdr begins with HEAD<sp> */
+	HTTPREAD_HDR_TYPE_POST = 4,         /* hdr begins with POST<sp> */
+	HTTPREAD_HDR_TYPE_PUT = 5,          /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_DELETE = 6,       /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_TRACE = 7,        /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_CONNECT = 8,      /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_NOTIFY = 9,       /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_M_SEARCH = 10,    /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_M_POST = 11,      /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_SUBSCRIBE = 12,   /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_UNSUBSCRIBE = 13, /* hdr begins with ... */
+
+	HTTPREAD_N_HDR_TYPES    /* keep last */
+};
+
+
+/* control instance -- opaque struct declaration
+ */
+struct httpread;
+
+
+/* httpread_destroy -- if h is non-NULL, clean up
+ * This must eventually be called by the application following
+ * call of the application's callback and may be called
+ * earlier if desired.
+ */
+void httpread_destroy(struct httpread *h);
+
+/* httpread_create -- start a new reading session making use of eloop.
+ * The new instance will use the socket descriptor for reading (until
+ * it gets a file and not after) but will not close the socket, even
+ * when the instance is destroyed (the application must do that).
+ * Return NULL on error.
+ *
+ * Provided that httpread_create successfully returns a handle,
+ * the callback fnc is called to handle httpread_event events.
+ * The caller should do destroy on any errors or unknown events.
+ *
+ * Pass max_bytes == 0 to not read body at all (required for e.g.
+ * reply to HEAD request).
+ */
+struct httpread * httpread_create(
+	int sd,         /* descriptor of TCP socket to read from */
+	void (*cb)(struct httpread *handle, void *cookie,
+		    enum httpread_event e),  /* call on event */
+	void *cookie,    /* pass to callback */
+	int max_bytes,          /* maximum file size else abort it */
+	int timeout_seconds     /* 0; or total duration timeout period */
+	);
+
+/* httpread_hdr_type_get -- When file is ready, returns header type.
+ */
+enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h);
+
+
+/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
+ * or possibly NULL (which would be an error).
+ */
+char *httpread_uri_get(struct httpread *h);
+
+/* httpread_reply_code_get -- When reply is ready, returns reply code */
+int httpread_reply_code_get(struct httpread *h);
+
+
+/* httpread_length_get -- When file is ready, returns file length. */
+int httpread_length_get(struct httpread *h);
+
+/* httpread_data_get -- When file is ready, returns file content
+ * with null byte appened.
+ * Might return NULL in some error condition.
+ */
+void * httpread_data_get(struct httpread *h);
+
+/* httpread_hdr_get -- When file is ready, returns header content
+ * with null byte appended.
+ * Might return NULL in some error condition.
+ */
+char * httpread_hdr_get(struct httpread *h);
+
+/* httpread_hdr_line_get -- When file is ready, returns pointer
+ * to line within header content matching the given tag
+ * (after the tag itself and any spaces/tabs).
+ *
+ * The tag should end with a colon for reliable matching.
+ *
+ * If not found, returns NULL;
+ */
+char * httpread_hdr_line_get(struct httpread *h, const char *tag);
+
+#endif /* HTTPREAD_H */
diff --git a/hostap/src/wps/ndef.c b/hostap/src/wps/ndef.c
new file mode 100644
index 0000000..bb3c055
--- /dev/null
+++ b/hostap/src/wps/ndef.c
@@ -0,0 +1,204 @@
+/*
+ * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup
+ *   Reference is "NFCForum-TS-NDEF_1.0 2006-07-24".
+ * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include "common.h"
+#include "wps/wps.h"
+
+#define FLAG_MESSAGE_BEGIN (1 << 7)
+#define FLAG_MESSAGE_END (1 << 6)
+#define FLAG_CHUNK (1 << 5)
+#define FLAG_SHORT_RECORD (1 << 4)
+#define FLAG_ID_LENGTH_PRESENT (1 << 3)
+#define FLAG_TNF_NFC_FORUM (0x01)
+#define FLAG_TNF_RFC2046 (0x02)
+
+struct ndef_record {
+	const u8 *type;
+	const u8 *id;
+	const u8 *payload;
+	u8 type_length;
+	u8 id_length;
+	u32 payload_length;
+	u32 total_length;
+};
+
+static const char wifi_handover_type[] = "application/vnd.wfa.wsc";
+static const char p2p_handover_type[] = "application/vnd.wfa.p2p";
+
+static int ndef_parse_record(const u8 *data, u32 size,
+			     struct ndef_record *record)
+{
+	const u8 *pos = data + 1;
+
+	if (size < 2)
+		return -1;
+	record->type_length = *pos++;
+	if (data[0] & FLAG_SHORT_RECORD) {
+		if (size < 3)
+			return -1;
+		record->payload_length = *pos++;
+	} else {
+		u32 len;
+
+		if (size < 6)
+			return -1;
+		len = WPA_GET_BE32(pos);
+		if (len > size - 6 || len > 20000)
+			return -1;
+		record->payload_length = len;
+		pos += sizeof(u32);
+	}
+
+	if (data[0] & FLAG_ID_LENGTH_PRESENT) {
+		if ((int) size < pos - data + 1)
+			return -1;
+		record->id_length = *pos++;
+	} else
+		record->id_length = 0;
+
+	record->type = record->type_length == 0 ? NULL : pos;
+	pos += record->type_length;
+
+	record->id = record->id_length == 0 ? NULL : pos;
+	pos += record->id_length;
+
+	record->payload = record->payload_length == 0 ? NULL : pos;
+	pos += record->payload_length;
+
+	record->total_length = pos - data;
+	if (record->total_length > size ||
+	    record->total_length < record->payload_length)
+		return -1;
+	return 0;
+}
+
+
+static struct wpabuf * ndef_parse_records(const struct wpabuf *buf,
+					  int (*filter)(struct ndef_record *))
+{
+	struct ndef_record record;
+	int len = wpabuf_len(buf);
+	const u8 *data = wpabuf_head(buf);
+
+	while (len > 0) {
+		if (ndef_parse_record(data, len, &record) < 0) {
+			wpa_printf(MSG_ERROR, "NDEF : Failed to parse");
+			return NULL;
+		}
+		if (filter == NULL || filter(&record))
+			return wpabuf_alloc_copy(record.payload,
+						 record.payload_length);
+		data += record.total_length;
+		len -= record.total_length;
+	}
+	wpa_printf(MSG_ERROR, "NDEF : Record not found");
+	return NULL;
+}
+
+
+static struct wpabuf * ndef_build_record(u8 flags, const void *type,
+					 u8 type_length, void *id,
+					 u8 id_length,
+					 const struct wpabuf *payload)
+{
+	struct wpabuf *record;
+	size_t total_len;
+	int short_record;
+	u8 local_flag;
+	size_t payload_length = wpabuf_len(payload);
+
+	short_record = payload_length < 256 ? 1 : 0;
+
+	total_len = 2; /* flag + type length */
+	/* payload length */
+	total_len += short_record ? sizeof(u8) : sizeof(u32);
+	if (id_length > 0)
+		total_len += 1;
+	total_len += type_length + id_length + payload_length;
+	record = wpabuf_alloc(total_len);
+	if (record == NULL) {
+		wpa_printf(MSG_ERROR, "NDEF : Failed to allocate "
+			   "record for build");
+		return NULL;
+	}
+
+	local_flag = flags;
+	if (id_length > 0)
+		local_flag |= FLAG_ID_LENGTH_PRESENT;
+	if (short_record)
+		local_flag |= FLAG_SHORT_RECORD;
+	wpabuf_put_u8(record, local_flag);
+
+	wpabuf_put_u8(record, type_length);
+
+	if (short_record)
+		wpabuf_put_u8(record, payload_length);
+	else
+		wpabuf_put_be32(record, payload_length);
+
+	if (id_length > 0)
+		wpabuf_put_u8(record, id_length);
+	wpabuf_put_data(record, type, type_length);
+	wpabuf_put_data(record, id, id_length);
+	wpabuf_put_buf(record, payload);
+	return record;
+}
+
+
+static int wifi_filter(struct ndef_record *record)
+{
+	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)
+		return 0;
+	return 1;
+}
+
+
+struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf)
+{
+	return ndef_parse_records(buf, wifi_filter);
+}
+
+
+struct wpabuf * ndef_build_wifi(const struct wpabuf *buf)
+{
+	return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
+				 FLAG_TNF_RFC2046, wifi_handover_type,
+				 os_strlen(wifi_handover_type), NULL, 0, buf);
+}
+
+
+static int p2p_filter(struct ndef_record *record)
+{
+	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_parse_p2p(const struct wpabuf *buf)
+{
+	return ndef_parse_records(buf, p2p_filter);
+}
+
+
+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/hostap/src/wps/upnp_xml.c b/hostap/src/wps/upnp_xml.c
new file mode 100644
index 0000000..a9958ee
--- /dev/null
+++ b/hostap/src/wps/upnp_xml.c
@@ -0,0 +1,252 @@
+/*
+ * UPnP XML helper routines
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "http.h"
+#include "upnp_xml.h"
+
+
+/*
+ * XML parsing and formatting
+ *
+ * XML is a markup language based on unicode; usually (and in our case,
+ * always!) based on utf-8. utf-8 uses a variable number of bytes per
+ * character. utf-8 has the advantage that all non-ASCII unicode characters are
+ * represented by sequences of non-ascii (high bit set) bytes, whereas ASCII
+ * characters are single ascii bytes, thus we can use typical text processing.
+ *
+ * (One other interesting thing about utf-8 is that it is possible to look at
+ * any random byte and determine if it is the first byte of a character as
+ * versus a continuation byte).
+ *
+ * The base syntax of XML uses a few ASCII punctionation characters; any
+ * characters that would appear in the payload data are rewritten using
+ * sequences, e.g., &amp; for ampersand(&) and &lt for left angle bracket (<).
+ * Five such escapes total (more can be defined but that does not apply to our
+ * case). Thus we can safely parse for angle brackets etc.
+ *
+ * XML describes tree structures of tagged data, with each element beginning
+ * with an opening tag <label> and ending with a closing tag </label> with
+ * matching label. (There is also a self-closing tag <label/> which is supposed
+ * to be equivalent to <label></label>, i.e., no payload, but we are unlikely
+ * to see it for our purpose).
+ *
+ * Actually the opening tags are a little more complicated because they can
+ * contain "attributes" after the label (delimited by ascii space or tab chars)
+ * of the form attribute_label="value" or attribute_label='value'; as it turns
+ * out we do not have to read any of these attributes, just ignore them.
+ *
+ * Labels are any sequence of chars other than space, tab, right angle bracket
+ * (and ?), but may have an inner structure of <namespace><colon><plain_label>.
+ * As it turns out, we can ignore the namespaces, in fact we can ignore the
+ * entire tree hierarchy, because the plain labels we are looking for will be
+ * unique (not in general, but for this application). We do however have to be
+ * careful to skip over the namespaces.
+ *
+ * In generating XML we have to be more careful, but that is easy because
+ * everything we do is pretty canned. The only real care to take is to escape
+ * any special chars in our payload.
+ */
+
+/**
+ * xml_next_tag - Advance to next tag
+ * @in: Input
+ * @out: OUT: start of tag just after '<'
+ * @out_tagname: OUT: start of name of tag, skipping namespace
+ * @end: OUT: one after tag
+ * Returns: 0 on success, 1 on failure
+ *
+ * A tag has form:
+ *     <left angle bracket><...><right angle bracket>
+ * Within the angle brackets, there is an optional leading forward slash (which
+ * makes the tag an ending tag), then an optional leading label (followed by
+ * colon) and then the tag name itself.
+ *
+ * Note that angle brackets present in the original data must have been encoded
+ * as &lt; and &gt; so they will not trouble us.
+ */
+int xml_next_tag(const char *in, const char **out,
+		 const char **out_tagname, const char **end)
+{
+	while (*in && *in != '<')
+		in++;
+	if (*in != '<')
+		return 1;
+	*out = ++in;
+	if (*in == '/')
+		in++;
+	*out_tagname = in; /* maybe */
+	while (isalnum(*in) || *in == '-')
+		in++;
+	if (*in == ':')
+		*out_tagname = ++in;
+	while (*in && *in != '>')
+		in++;
+	if (*in != '>')
+		return 1;
+	*end = ++in;
+	return 0;
+}
+
+
+/* xml_data_encode -- format data for xml file, escaping special characters.
+ *
+ * Note that we assume we are using utf8 both as input and as output!
+ * In utf8, characters may be classed as follows:
+ *     0xxxxxxx(2) -- 1 byte ascii char
+ *     11xxxxxx(2) -- 1st byte of multi-byte char w/ unicode value >= 0x80
+ *         110xxxxx(2) -- 1st byte of 2 byte sequence (5 payload bits here)
+ *         1110xxxx(2) -- 1st byte of 3 byte sequence (4 payload bits here)
+ *         11110xxx(2) -- 1st byte of 4 byte sequence (3 payload bits here)
+ *      10xxxxxx(2) -- extension byte (6 payload bits per byte)
+ *      Some values implied by the above are however illegal because they
+ *      do not represent unicode chars or are not the shortest encoding.
+ * Actually, we can almost entirely ignore the above and just do
+ * text processing same as for ascii text.
+ *
+ * XML is written with arbitrary unicode characters, except that five
+ * characters have special meaning and so must be escaped where they
+ * appear in payload data... which we do here.
+ */
+void xml_data_encode(struct wpabuf *buf, const char *data, int len)
+{
+	int i;
+	for (i = 0; i < len; i++) {
+		u8 c = ((u8 *) data)[i];
+		if (c == '<') {
+			wpabuf_put_str(buf, "&lt;");
+			continue;
+		}
+		if (c == '>') {
+			wpabuf_put_str(buf, "&gt;");
+			continue;
+		}
+		if (c == '&') {
+			wpabuf_put_str(buf, "&amp;");
+			continue;
+		}
+		if (c == '\'') {
+			wpabuf_put_str(buf, "&apos;");
+			continue;
+		}
+		if (c == '"') {
+			wpabuf_put_str(buf, "&quot;");
+			continue;
+		}
+		/*
+		 * We could try to represent control characters using the
+		 * sequence: &#x; where x is replaced by a hex numeral, but not
+		 * clear why we would do this.
+		 */
+		wpabuf_put_u8(buf, c);
+	}
+}
+
+
+/* xml_add_tagged_data -- format tagged data as a new xml line.
+ *
+ * tag must not have any special chars.
+ * data may have special chars, which are escaped.
+ */
+void xml_add_tagged_data(struct wpabuf *buf, const char *tag, const char *data)
+{
+	wpabuf_printf(buf, "<%s>", tag);
+	xml_data_encode(buf, data, os_strlen(data));
+	wpabuf_printf(buf, "</%s>\n", tag);
+}
+
+
+/* A POST body looks something like (per upnp spec):
+ * <?xml version="1.0"?>
+ * <s:Envelope
+ *     xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
+ *     s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ *   <s:Body>
+ *     <u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v">
+ *       <argumentName>in arg value</argumentName>
+ *       other in args and their values go here, if any
+ *     </u:actionName>
+ *   </s:Body>
+ * </s:Envelope>
+ *
+ * where :
+ *      s: might be some other namespace name followed by colon
+ *      u: might be some other namespace name followed by colon
+ *      actionName will be replaced according to action requested
+ *      schema following actionName will be WFA scheme instead
+ *      argumentName will be actual argument name
+ *      (in arg value) will be actual argument value
+ */
+char * xml_get_first_item(const char *doc, const char *item)
+{
+	const char *match = item;
+	int match_len = os_strlen(item);
+	const char *tag, *tagname, *end;
+	char *value;
+
+	/*
+	 * This is crude: ignore any possible tag name conflicts and go right
+	 * to the first tag of this name. This should be ok for the limited
+	 * domain of UPnP messages.
+	 */
+	for (;;) {
+		if (xml_next_tag(doc, &tag, &tagname, &end))
+			return NULL;
+		doc = end;
+		if (!os_strncasecmp(tagname, match, match_len) &&
+		    *tag != '/' &&
+		    (tagname[match_len] == '>' ||
+		     !isgraph(tagname[match_len]))) {
+			break;
+		}
+	}
+	end = doc;
+	while (*end && *end != '<')
+		end++;
+	value = os_zalloc(1 + (end - doc));
+	if (value == NULL)
+		return NULL;
+	os_memcpy(value, doc, end - doc);
+	return value;
+}
+
+
+struct wpabuf * xml_get_base64_item(const char *data, const char *name,
+				    enum http_reply_code *ret)
+{
+	char *msg;
+	struct wpabuf *buf;
+	unsigned char *decoded;
+	size_t len;
+
+	msg = xml_get_first_item(data, name);
+	if (msg == NULL) {
+		*ret = UPNP_ARG_VALUE_INVALID;
+		return NULL;
+	}
+
+	decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len);
+	os_free(msg);
+	if (decoded == NULL) {
+		*ret = UPNP_OUT_OF_MEMORY;
+		return NULL;
+	}
+
+	buf = wpabuf_alloc_ext_data(decoded, len);
+	if (buf == NULL) {
+		os_free(decoded);
+		*ret = UPNP_OUT_OF_MEMORY;
+		return NULL;
+	}
+	return buf;
+}
diff --git a/hostap/src/wps/upnp_xml.h b/hostap/src/wps/upnp_xml.h
new file mode 100644
index 0000000..616af3d
--- /dev/null
+++ b/hostap/src/wps/upnp_xml.h
@@ -0,0 +1,25 @@
+/*
+ * UPnP XML helper routines
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef UPNP_XML_H
+#define UPNP_XML_H
+
+#include "http.h"
+
+void xml_data_encode(struct wpabuf *buf, const char *data, int len);
+void xml_add_tagged_data(struct wpabuf *buf, const char *tag,
+			 const char *data);
+int xml_next_tag(const char *in, const char **out,
+		 const char **out_tagname, const char **end);
+char * xml_get_first_item(const char *doc, const char *item);
+struct wpabuf * xml_get_base64_item(const char *data, const char *name,
+				    enum http_reply_code *ret);
+
+#endif /* UPNP_XML_H */
diff --git a/hostap/src/wps/wps.c b/hostap/src/wps/wps.c
new file mode 100644
index 0000000..fbaf85a
--- /dev/null
+++ b/hostap/src/wps/wps.c
@@ -0,0 +1,662 @@
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007-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/dh_group5.h"
+#include "common/ieee802_11_defs.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+#ifdef CONFIG_WPS_TESTING
+int wps_version_number = 0x20;
+int wps_testing_dummy_cred = 0;
+int wps_corrupt_pkhash = 0;
+#endif /* CONFIG_WPS_TESTING */
+
+
+/**
+ * wps_init - Initialize WPS Registration protocol data
+ * @cfg: WPS configuration
+ * Returns: Pointer to allocated data or %NULL on failure
+ *
+ * This function is used to initialize WPS data for a registration protocol
+ * instance (i.e., each run of registration protocol as a Registrar of
+ * Enrollee. The caller is responsible for freeing this data after the
+ * registration run has been completed by calling wps_deinit().
+ */
+struct wps_data * wps_init(const struct wps_config *cfg)
+{
+	struct wps_data *data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->wps = cfg->wps;
+	data->registrar = cfg->registrar;
+	if (cfg->registrar) {
+		os_memcpy(data->uuid_r, cfg->wps->uuid, WPS_UUID_LEN);
+	} else {
+		os_memcpy(data->mac_addr_e, cfg->wps->dev.mac_addr, ETH_ALEN);
+		os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN);
+	}
+	if (cfg->pin) {
+		data->dev_pw_id = cfg->dev_pw_id;
+		data->dev_password = os_malloc(cfg->pin_len);
+		if (data->dev_password == NULL) {
+			os_free(data);
+			return NULL;
+		}
+		os_memcpy(data->dev_password, cfg->pin, cfg->pin_len);
+		data->dev_password_len = cfg->pin_len;
+		wpa_hexdump_key(MSG_DEBUG, "WPS: AP PIN dev_password",
+				data->dev_password, data->dev_password_len);
+	}
+
+#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;
+		data->alt_dev_password = data->dev_password;
+		data->alt_dev_password_len = data->dev_password_len;
+
+		data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id;
+		data->dev_password =
+			os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw));
+		if (data->dev_password == NULL) {
+			os_free(data);
+			return NULL;
+		}
+		os_memcpy(data->dev_password,
+			  wpabuf_head(cfg->wps->ap_nfc_dev_pw),
+			  wpabuf_len(cfg->wps->ap_nfc_dev_pw));
+		data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw);
+		wpa_hexdump_key(MSG_DEBUG, "WPS: NFC dev_password",
+			    data->dev_password, data->dev_password_len);
+	}
+#endif /* CONFIG_WPS_NFC */
+
+	data->pbc = cfg->pbc;
+	if (cfg->pbc) {
+		/* Use special PIN '00000000' for PBC */
+		data->dev_pw_id = DEV_PW_PUSHBUTTON;
+		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);
+			return NULL;
+		}
+		data->dev_password_len = 8;
+	}
+
+	data->state = data->registrar ? RECV_M1 : SEND_M1;
+
+	if (cfg->assoc_wps_ie) {
+		struct wps_parse_attr attr;
+		wpa_hexdump_buf(MSG_DEBUG, "WPS: WPS IE from (Re)AssocReq",
+				cfg->assoc_wps_ie);
+		if (wps_parse_msg(cfg->assoc_wps_ie, &attr) < 0) {
+			wpa_printf(MSG_DEBUG, "WPS: Failed to parse WPS IE "
+				   "from (Re)AssocReq");
+		} else if (attr.request_type == NULL) {
+			wpa_printf(MSG_DEBUG, "WPS: No Request Type attribute "
+				   "in (Re)AssocReq WPS IE");
+		} else {
+			wpa_printf(MSG_DEBUG, "WPS: Request Type (from WPS IE "
+				   "in (Re)AssocReq WPS IE): %d",
+				   *attr.request_type);
+			data->request_type = *attr.request_type;
+		}
+	}
+
+	if (cfg->new_ap_settings) {
+		data->new_ap_settings =
+			os_malloc(sizeof(*data->new_ap_settings));
+		if (data->new_ap_settings == NULL) {
+			bin_clear_free(data->dev_password,
+				       data->dev_password_len);
+			os_free(data);
+			return NULL;
+		}
+		os_memcpy(data->new_ap_settings, cfg->new_ap_settings,
+			  sizeof(*data->new_ap_settings));
+	}
+
+	if (cfg->peer_addr)
+		os_memcpy(data->peer_dev.mac_addr, cfg->peer_addr, ETH_ALEN);
+	if (cfg->p2p_dev_addr)
+		os_memcpy(data->p2p_dev_addr, cfg->p2p_dev_addr, ETH_ALEN);
+
+	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;
+}
+
+
+/**
+ * wps_deinit - Deinitialize WPS Registration protocol data
+ * @data: WPS Registration protocol data from wps_init()
+ */
+void wps_deinit(struct wps_data *data)
+{
+#ifdef CONFIG_WPS_NFC
+	if (data->registrar && data->nfc_pw_token)
+		wps_registrar_remove_nfc_pw_token(data->wps->registrar,
+						  data->nfc_pw_token);
+#endif /* CONFIG_WPS_NFC */
+
+	if (data->wps_pin_revealed) {
+		wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and "
+			   "negotiation failed");
+		if (data->registrar)
+			wps_registrar_invalidate_pin(data->wps->registrar,
+						     data->uuid_e);
+	} else if (data->registrar)
+		wps_registrar_unlock_pin(data->wps->registrar, data->uuid_e);
+
+	wpabuf_free(data->dh_privkey);
+	wpabuf_free(data->dh_pubkey_e);
+	wpabuf_free(data->dh_pubkey_r);
+	wpabuf_free(data->last_msg);
+	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);
+	bin_clear_free(data->new_ap_settings, sizeof(*data->new_ap_settings));
+	dh5_free(data->dh_ctx);
+	os_free(data);
+}
+
+
+/**
+ * wps_process_msg - Process a WPS message
+ * @wps: WPS Registration protocol data from wps_init()
+ * @op_code: Message OP Code
+ * @msg: Message data
+ * Returns: Processing result
+ *
+ * This function is used to process WPS messages with OP Codes WSC_ACK,
+ * WSC_NACK, WSC_MSG, and WSC_Done. The caller (e.g., EAP server/peer) is
+ * responsible for reassembling the messages before calling this function.
+ * Response to this message is built by calling wps_get_msg().
+ */
+enum wps_process_res wps_process_msg(struct wps_data *wps,
+				     enum wsc_op_code op_code,
+				     const struct wpabuf *msg)
+{
+	if (wps->registrar)
+		return wps_registrar_process_msg(wps, op_code, msg);
+	else
+		return wps_enrollee_process_msg(wps, op_code, msg);
+}
+
+
+/**
+ * wps_get_msg - Build a WPS message
+ * @wps: WPS Registration protocol data from wps_init()
+ * @op_code: Buffer for returning message OP Code
+ * Returns: The generated WPS message or %NULL on failure
+ *
+ * This function is used to build a response to a message processed by calling
+ * wps_process_msg(). The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code)
+{
+	if (wps->registrar)
+		return wps_registrar_get_msg(wps, op_code);
+	else
+		return wps_enrollee_get_msg(wps, op_code);
+}
+
+
+/**
+ * wps_is_selected_pbc_registrar - Check whether WPS IE indicates active PBC
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if PBC Registrar is active, 0 if not
+ */
+int wps_is_selected_pbc_registrar(const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	/*
+	 * In theory, this could also verify that attr.sel_reg_config_methods
+	 * includes WPS_CONFIG_PUSHBUTTON, but some deployed AP implementations
+	 * do not set Selected Registrar Config Methods attribute properly, so
+	 * it is safer to just use Device Password ID here.
+	 */
+
+	if (wps_parse_msg(msg, &attr) < 0 ||
+	    !attr.selected_registrar || *attr.selected_registrar == 0 ||
+	    !attr.dev_password_id ||
+	    WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
+		return 0;
+
+#ifdef CONFIG_WPS_STRICT
+	if (!attr.sel_reg_config_methods ||
+	    !(WPA_GET_BE16(attr.sel_reg_config_methods) &
+	      WPS_CONFIG_PUSHBUTTON))
+		return 0;
+#endif /* CONFIG_WPS_STRICT */
+
+	return 1;
+}
+
+
+static int is_selected_pin_registrar(struct wps_parse_attr *attr)
+{
+	/*
+	 * In theory, this could also verify that attr.sel_reg_config_methods
+	 * includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD,
+	 * but some deployed AP implementations do not set Selected Registrar
+	 * Config Methods attribute properly, so it is safer to just use
+	 * Device Password ID here.
+	 */
+
+	if (!attr->selected_registrar || *attr->selected_registrar == 0)
+		return 0;
+
+	if (attr->dev_password_id != NULL &&
+	    WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON)
+		return 0;
+
+#ifdef CONFIG_WPS_STRICT
+	if (!attr->sel_reg_config_methods ||
+	    !(WPA_GET_BE16(attr->sel_reg_config_methods) &
+	      (WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD)))
+		return 0;
+#endif /* CONFIG_WPS_STRICT */
+
+	return 1;
+}
+
+
+/**
+ * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if PIN Registrar is active, 0 if not
+ */
+int wps_is_selected_pin_registrar(const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return 0;
+
+	return is_selected_pin_registrar(&attr);
+}
+
+
+/**
+ * wps_is_addr_authorized - Check whether WPS IE authorizes MAC address
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * @addr: MAC address to search for
+ * @ver1_compat: Whether to use version 1 compatibility mode
+ * Returns: 2 if the specified address is explicit authorized, 1 if address is
+ * authorized (broadcast), 0 if not
+ */
+int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
+			   int ver1_compat)
+{
+	struct wps_parse_attr attr;
+	unsigned int i;
+	const u8 *pos;
+	const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return 0;
+
+	if (!attr.version2 && ver1_compat) {
+		/*
+		 * Version 1.0 AP - AuthorizedMACs not used, so revert back to
+		 * old mechanism of using SelectedRegistrar.
+		 */
+		return is_selected_pin_registrar(&attr);
+	}
+
+	if (!attr.authorized_macs)
+		return 0;
+
+	pos = attr.authorized_macs;
+	for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) {
+		if (os_memcmp(pos, addr, ETH_ALEN) == 0)
+			return 2;
+		if (os_memcmp(pos, bcast, ETH_ALEN) == 0)
+			return 1;
+		pos += ETH_ALEN;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wps_ap_priority_compar - Prioritize WPS IE from two APs
+ * @wps_a: WPS IE contents from Beacon or Probe Response frame
+ * @wps_b: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if wps_b is considered more likely selection for WPS
+ * provisioning, -1 if wps_a is considered more like, or 0 if no preference
+ */
+int wps_ap_priority_compar(const struct wpabuf *wps_a,
+			   const struct wpabuf *wps_b)
+{
+	struct wps_parse_attr attr;
+	int sel_a, sel_b;
+
+	if (wps_a == NULL || wps_parse_msg(wps_a, &attr) < 0)
+		return 1;
+	sel_a = attr.selected_registrar && *attr.selected_registrar != 0;
+
+	if (wps_b == NULL || wps_parse_msg(wps_b, &attr) < 0)
+		return -1;
+	sel_b = attr.selected_registrar && *attr.selected_registrar != 0;
+
+	if (sel_a && !sel_b)
+		return -1;
+	if (!sel_a && sel_b)
+		return 1;
+
+	return 0;
+}
+
+
+/**
+ * wps_get_uuid_e - Get UUID-E from WPS IE
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: Pointer to UUID-E or %NULL if not included
+ *
+ * The returned pointer is to the msg contents and it remains valid only as
+ * long as the msg buffer is valid.
+ */
+const u8 * wps_get_uuid_e(const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return NULL;
+	return attr.uuid_e;
+}
+
+
+/**
+ * wps_is_20 - Check whether WPS attributes claim support for WPS 2.0
+ */
+int wps_is_20(const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	if (msg == NULL || wps_parse_msg(msg, &attr) < 0)
+		return 0;
+	return attr.version2 != NULL;
+}
+
+
+/**
+ * wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request
+ * @req_type: Value for Request Type attribute
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type)
+{
+	struct wpabuf *ie;
+	u8 *len;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
+		   "Request");
+	ie = wpabuf_alloc(100);
+	if (ie == NULL)
+		return NULL;
+
+	wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+	len = wpabuf_put(ie, 1);
+	wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+	if (wps_build_version(ie) ||
+	    wps_build_req_type(ie, req_type) ||
+	    wps_build_wfa_ext(ie, 0, NULL, 0)) {
+		wpabuf_free(ie);
+		return NULL;
+	}
+
+	*len = wpabuf_len(ie) - 2;
+
+	return ie;
+}
+
+
+/**
+ * wps_build_assoc_resp_ie - Build WPS IE for (Re)Association Response
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_assoc_resp_ie(void)
+{
+	struct wpabuf *ie;
+	u8 *len;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
+		   "Response");
+	ie = wpabuf_alloc(100);
+	if (ie == NULL)
+		return NULL;
+
+	wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+	len = wpabuf_put(ie, 1);
+	wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+	if (wps_build_version(ie) ||
+	    wps_build_resp_type(ie, WPS_RESP_AP) ||
+	    wps_build_wfa_ext(ie, 0, NULL, 0)) {
+		wpabuf_free(ie);
+		return NULL;
+	}
+
+	*len = wpabuf_len(ie) - 2;
+
+	return ie;
+}
+
+
+/**
+ * wps_build_probe_req_ie - Build WPS IE for Probe Request
+ * @pw_id: Password ID (DEV_PW_PUSHBUTTON for active PBC and DEV_PW_DEFAULT for
+ * most other use cases)
+ * @dev: Device attributes
+ * @uuid: Own UUID
+ * @req_type: Value for Request Type attribute
+ * @num_req_dev_types: Number of requested device types
+ * @req_dev_types: Requested device types (8 * num_req_dev_types octets) or
+ *	%NULL if none
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
+				       const u8 *uuid,
+				       enum wps_request_type req_type,
+				       unsigned int num_req_dev_types,
+				       const u8 *req_dev_types)
+{
+	struct wpabuf *ie;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request");
+
+	ie = wpabuf_alloc(500);
+	if (ie == NULL)
+		return NULL;
+
+	if (wps_build_version(ie) ||
+	    wps_build_req_type(ie, req_type) ||
+	    wps_build_config_methods(ie, dev->config_methods) ||
+	    wps_build_uuid_e(ie, uuid) ||
+	    wps_build_primary_dev_type(dev, ie) ||
+	    wps_build_rf_bands(dev, ie, 0) ||
+	    wps_build_assoc_state(NULL, ie) ||
+	    wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
+	    wps_build_dev_password_id(ie, pw_id) ||
+	    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) ||
+	    wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
+	    ||
+	    wps_build_secondary_dev_type(dev, ie)
+		) {
+		wpabuf_free(ie);
+		return NULL;
+	}
+
+	return wps_ie_encapsulate(ie);
+}
+
+
+void wps_free_pending_msgs(struct upnp_pending_message *msgs)
+{
+	struct upnp_pending_message *p, *prev;
+	p = msgs;
+	while (p) {
+		prev = p;
+		p = p->next;
+		wpabuf_free(prev->msg);
+		os_free(prev);
+	}
+}
+
+
+int wps_attr_text(struct wpabuf *data, char *buf, char *end)
+{
+	struct wps_parse_attr attr;
+	char *pos = buf;
+	int ret;
+
+	if (wps_parse_msg(data, &attr) < 0)
+		return -1;
+
+	if (attr.wps_state) {
+		if (*attr.wps_state == WPS_STATE_NOT_CONFIGURED)
+			ret = os_snprintf(pos, end - pos,
+					  "wps_state=unconfigured\n");
+		else if (*attr.wps_state == WPS_STATE_CONFIGURED)
+			ret = os_snprintf(pos, end - pos,
+					  "wps_state=configured\n");
+		else
+			ret = 0;
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (attr.ap_setup_locked && *attr.ap_setup_locked) {
+		ret = os_snprintf(pos, end - pos,
+				  "wps_ap_setup_locked=1\n");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (attr.selected_registrar && *attr.selected_registrar) {
+		ret = os_snprintf(pos, end - pos,
+				  "wps_selected_registrar=1\n");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (attr.dev_password_id) {
+		ret = os_snprintf(pos, end - pos,
+				  "wps_device_password_id=%u\n",
+				  WPA_GET_BE16(attr.dev_password_id));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (attr.sel_reg_config_methods) {
+		ret = os_snprintf(pos, end - pos,
+				  "wps_selected_registrar_config_methods="
+				  "0x%04x\n",
+				  WPA_GET_BE16(attr.sel_reg_config_methods));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (attr.primary_dev_type) {
+		char devtype[WPS_DEV_TYPE_BUFSIZE];
+		ret = os_snprintf(pos, end - pos,
+				  "wps_primary_device_type=%s\n",
+				  wps_dev_type_bin2str(attr.primary_dev_type,
+						       devtype,
+						       sizeof(devtype)));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (attr.dev_name) {
+		char *str = os_malloc(attr.dev_name_len + 1);
+		size_t i;
+		if (str == NULL)
+			return pos - buf;
+		for (i = 0; i < attr.dev_name_len; i++) {
+			if (attr.dev_name[i] == 0 ||
+			    is_ctrl_char(attr.dev_name[i]))
+				str[i] = '_';
+			else
+				str[i] = attr.dev_name[i];
+		}
+		str[i] = '\0';
+		ret = os_snprintf(pos, end - pos, "wps_device_name=%s\n", str);
+		os_free(str);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (attr.config_methods) {
+		ret = os_snprintf(pos, end - pos,
+				  "wps_config_methods=0x%04x\n",
+				  WPA_GET_BE16(attr.config_methods));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+const char * wps_ei_str(enum wps_error_indication ei)
+{
+	switch (ei) {
+	case WPS_EI_NO_ERROR:
+		return "No Error";
+	case WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED:
+		return "TKIP Only Prohibited";
+	case WPS_EI_SECURITY_WEP_PROHIBITED:
+		return "WEP Prohibited";
+	case WPS_EI_AUTH_FAILURE:
+		return "Authentication Failure";
+	default:
+		return "Unknown";
+	}
+}
diff --git a/hostap/src/wps/wps.h b/hostap/src/wps/wps.h
new file mode 100644
index 0000000..2c91d16
--- /dev/null
+++ b/hostap/src/wps/wps.h
@@ -0,0 +1,1041 @@
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_H
+#define WPS_H
+
+#include "common/ieee802_11_defs.h"
+#include "wps_defs.h"
+
+/**
+ * enum wsc_op_code - EAP-WSC OP-Code values
+ */
+enum wsc_op_code {
+	WSC_UPnP = 0 /* No OP Code in UPnP transport */,
+	WSC_Start = 0x01,
+	WSC_ACK = 0x02,
+	WSC_NACK = 0x03,
+	WSC_MSG = 0x04,
+	WSC_Done = 0x05,
+	WSC_FRAG_ACK = 0x06
+};
+
+struct wps_registrar;
+struct upnp_wps_device_sm;
+struct wps_er;
+struct wps_parse_attr;
+
+/**
+ * struct wps_credential - WPS Credential
+ * @ssid: SSID
+ * @ssid_len: Length of SSID
+ * @auth_type: Authentication Type (WPS_AUTH_OPEN, .. flags)
+ * @encr_type: Encryption Type (WPS_ENCR_NONE, .. flags)
+ * @key_idx: Key index
+ * @key: Key
+ * @key_len: Key length in octets
+ * @mac_addr: MAC address of the Credential receiver
+ * @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
+ */
+struct wps_credential {
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	u16 auth_type;
+	u16 encr_type;
+	u8 key_idx;
+	u8 key[64];
+	size_t key_len;
+	u8 mac_addr[ETH_ALEN];
+	const u8 *cred_attr;
+	size_t cred_attr_len;
+};
+
+#define WPS_DEV_TYPE_LEN 8
+#define WPS_DEV_TYPE_BUFSIZE 21
+#define WPS_SEC_DEV_TYPE_MAX_LEN 128
+/* maximum number of advertised WPS vendor extension attributes */
+#define MAX_WPS_VENDOR_EXTENSIONS 10
+/* maximum size of WPS Vendor extension attribute */
+#define WPS_MAX_VENDOR_EXT_LEN 1024
+/* maximum number of parsed WPS vendor extension attributes */
+#define MAX_WPS_PARSE_VENDOR_EXT 10
+
+/**
+ * struct wps_device_data - WPS Device Data
+ * @mac_addr: Device MAC address
+ * @device_name: Device Name (0..32 octets encoded in UTF-8)
+ * @manufacturer: Manufacturer (0..64 octets encoded in UTF-8)
+ * @model_name: Model Name (0..32 octets encoded in UTF-8)
+ * @model_number: Model Number (0..32 octets encoded in UTF-8)
+ * @serial_number: Serial Number (0..32 octets encoded in UTF-8)
+ * @pri_dev_type: Primary Device Type
+ * @sec_dev_type: Array of secondary device types
+ * @num_sec_dev_type: Number of secondary device types
+ * @os_version: OS Version
+ * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ, WPS_RF_60GHZ flags)
+ * @p2p: Whether the device is a P2P device
+ */
+struct wps_device_data {
+	u8 mac_addr[ETH_ALEN];
+	char *device_name;
+	char *manufacturer;
+	char *model_name;
+	char *model_number;
+	char *serial_number;
+	u8 pri_dev_type[WPS_DEV_TYPE_LEN];
+#define WPS_SEC_DEVICE_TYPES 5
+	u8 sec_dev_type[WPS_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN];
+	u8 num_sec_dev_types;
+	u32 os_version;
+	u8 rf_bands;
+	u16 config_methods;
+	struct wpabuf *vendor_ext_m1;
+	struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+
+	int p2p;
+};
+
+/**
+ * struct wps_config - WPS configuration for a single registration protocol run
+ */
+struct wps_config {
+	/**
+	 * wps - Pointer to long term WPS context
+	 */
+	struct wps_context *wps;
+
+	/**
+	 * registrar - Whether this end is a Registrar
+	 */
+	int registrar;
+
+	/**
+	 * pin - Enrollee Device Password (%NULL for Registrar or PBC)
+	 */
+	const u8 *pin;
+
+	/**
+	 * pin_len - Length on pin in octets
+	 */
+	size_t pin_len;
+
+	/**
+	 * pbc - Whether this is protocol run uses PBC
+	 */
+	int pbc;
+
+	/**
+	 * assoc_wps_ie: (Re)AssocReq WPS IE (in AP; %NULL if not AP)
+	 */
+	const struct wpabuf *assoc_wps_ie;
+
+	/**
+	 * new_ap_settings - New AP settings (%NULL if not used)
+	 *
+	 * This parameter provides new AP settings when using a wireless
+	 * stations as a Registrar to configure the AP. %NULL means that AP
+	 * will not be reconfigured, i.e., the station will only learn the
+	 * current AP settings by using AP PIN.
+	 */
+	const struct wps_credential *new_ap_settings;
+
+	/**
+	 * peer_addr: MAC address of the peer in AP; %NULL if not AP
+	 */
+	const u8 *peer_addr;
+
+	/**
+	 * use_psk_key - Use PSK format key in Credential
+	 *
+	 * Force PSK format to be used instead of ASCII passphrase when
+	 * building Credential for an Enrollee. The PSK value is set in
+	 * struct wpa_context::psk.
+	 */
+	int use_psk_key;
+
+	/**
+	 * dev_pw_id - Device Password ID for Enrollee when PIN is used
+	 */
+	u16 dev_pw_id;
+
+	/**
+	 * p2p_dev_addr - P2P Device Address from (Re)Association Request
+	 *
+	 * On AP/GO, this is set to the P2P Device Address of the associating
+	 * P2P client if a P2P IE is included in the (Re)Association Request
+	 * frame and the P2P Device Address is included. Otherwise, this is set
+	 * to %NULL to indicate the station does not have a P2P Device Address.
+	 */
+	const u8 *p2p_dev_addr;
+
+	/**
+	 * pbc_in_m1 - Do not remove PushButton config method in M1 (AP)
+	 *
+	 * This can be used to enable a workaround to allow Windows 7 to use
+	 * 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);
+
+void wps_deinit(struct wps_data *data);
+
+/**
+ * enum wps_process_res - WPS message processing result
+ */
+enum wps_process_res {
+	/**
+	 * WPS_DONE - Processing done
+	 */
+	WPS_DONE,
+
+	/**
+	 * WPS_CONTINUE - Processing continues
+	 */
+	WPS_CONTINUE,
+
+	/**
+	 * WPS_FAILURE - Processing failed
+	 */
+	WPS_FAILURE,
+
+	/**
+	 * WPS_PENDING - Processing continues, but waiting for an external
+	 *	event (e.g., UPnP message from an external Registrar)
+	 */
+	WPS_PENDING
+};
+enum wps_process_res wps_process_msg(struct wps_data *wps,
+				     enum wsc_op_code op_code,
+				     const struct wpabuf *msg);
+
+struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code);
+
+int wps_is_selected_pbc_registrar(const struct wpabuf *msg);
+int wps_is_selected_pin_registrar(const struct wpabuf *msg);
+int wps_ap_priority_compar(const struct wpabuf *wps_a,
+			   const struct wpabuf *wps_b);
+int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
+			   int ver1_compat);
+const u8 * wps_get_uuid_e(const struct wpabuf *msg);
+int wps_is_20(const struct wpabuf *msg);
+
+struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type);
+struct wpabuf * wps_build_assoc_resp_ie(void);
+struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
+				       const u8 *uuid,
+				       enum wps_request_type req_type,
+				       unsigned int num_req_dev_types,
+				       const u8 *req_dev_types);
+
+
+/**
+ * struct wps_registrar_config - WPS Registrar configuration
+ */
+struct wps_registrar_config {
+	/**
+	 * new_psk_cb - Callback for new PSK
+	 * @ctx: Higher layer context data (cb_ctx)
+	 * @mac_addr: MAC address of the Enrollee
+	 * @p2p_dev_addr: P2P Device Address of the Enrollee or all zeros if not
+	 * @psk: The new PSK
+	 * @psk_len: The length of psk in octets
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This callback is called when a new per-device PSK is provisioned.
+	 */
+	int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr,
+			  const u8 *psk, size_t psk_len);
+
+	/**
+	 * set_ie_cb - Callback for WPS IE changes
+	 * @ctx: Higher layer context data (cb_ctx)
+	 * @beacon_ie: WPS IE for Beacon
+	 * @probe_resp_ie: WPS IE for Probe Response
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This callback is called whenever the WPS IE in Beacon or Probe
+	 * Response frames needs to be changed (AP only). Callee is responsible
+	 * for freeing the buffers.
+	 */
+	int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
+			 struct wpabuf *probe_resp_ie);
+
+	/**
+	 * pin_needed_cb - Callback for requesting a PIN
+	 * @ctx: Higher layer context data (cb_ctx)
+	 * @uuid_e: UUID-E of the unknown Enrollee
+	 * @dev: Device Data from the unknown Enrollee
+	 *
+	 * This callback is called whenever an unknown Enrollee requests to use
+	 * PIN method and a matching PIN (Device Password) is not found in
+	 * Registrar data.
+	 */
+	void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
+			      const struct wps_device_data *dev);
+
+	/**
+	 * reg_success_cb - Callback for reporting successful registration
+	 * @ctx: Higher layer context data (cb_ctx)
+	 * @mac_addr: MAC address of the Enrollee
+	 * @uuid_e: UUID-E of the Enrollee
+	 * @dev_pw: Device Password (PIN) used during registration
+	 * @dev_pw_len: Length of dev_pw in octets
+	 *
+	 * This callback is called whenever an Enrollee completes registration
+	 * successfully.
+	 */
+	void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
+			       const u8 *uuid_e, const u8 *dev_pw,
+			       size_t dev_pw_len);
+
+	/**
+	 * set_sel_reg_cb - Callback for reporting selected registrar changes
+	 * @ctx: Higher layer context data (cb_ctx)
+	 * @sel_reg: Whether the Registrar is selected
+	 * @dev_passwd_id: Device Password ID to indicate with method or
+	 *	specific password the Registrar intends to use
+	 * @sel_reg_config_methods: Bit field of active config methods
+	 *
+	 * This callback is called whenever the Selected Registrar state
+	 * changes (e.g., a new PIN becomes available or PBC is invoked). This
+	 * callback is only used by External Registrar implementation;
+	 * set_ie_cb() is used by AP implementation in similar caes, but it
+	 * provides the full WPS IE data instead of just the minimal Registrar
+	 * state information.
+	 */
+	void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
+			       u16 sel_reg_config_methods);
+
+	/**
+	 * enrollee_seen_cb - Callback for reporting Enrollee based on ProbeReq
+	 * @ctx: Higher layer context data (cb_ctx)
+	 * @addr: MAC address of the Enrollee
+	 * @uuid_e: UUID of the Enrollee
+	 * @pri_dev_type: Primary device type
+	 * @config_methods: Config Methods
+	 * @dev_password_id: Device Password ID
+	 * @request_type: Request Type
+	 * @dev_name: Device Name (if available)
+	 */
+	void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
+				 const u8 *pri_dev_type, u16 config_methods,
+				 u16 dev_password_id, u8 request_type,
+				 const char *dev_name);
+
+	/**
+	 * cb_ctx: Higher layer context data for Registrar callbacks
+	 */
+	void *cb_ctx;
+
+	/**
+	 * skip_cred_build: Do not build credential
+	 *
+	 * This option can be used to disable internal code that builds
+	 * Credential attribute into M8 based on the current network
+	 * configuration and Enrollee capabilities. The extra_cred data will
+	 * then be used as the Credential(s).
+	 */
+	int skip_cred_build;
+
+	/**
+	 * extra_cred: Additional Credential attribute(s)
+	 *
+	 * This optional data (set to %NULL to disable) can be used to add
+	 * Credential attribute(s) for other networks into M8. If
+	 * skip_cred_build is set, this will also override the automatically
+	 * generated Credential attribute.
+	 */
+	const u8 *extra_cred;
+
+	/**
+	 * extra_cred_len: Length of extra_cred in octets
+	 */
+	size_t extra_cred_len;
+
+	/**
+	 * disable_auto_conf - Disable auto-configuration on first registration
+	 *
+	 * By default, the AP that is started in not configured state will
+	 * generate a random PSK and move to configured state when the first
+	 * registration protocol run is completed successfully. This option can
+	 * be used to disable this functionality and leave it up to an external
+	 * program to take care of configuration. This requires the extra_cred
+	 * to be set with a suitable Credential and skip_cred_build being used.
+	 */
+	int disable_auto_conf;
+
+	/**
+	 * static_wep_only - Whether the BSS supports only static WEP
+	 */
+	int static_wep_only;
+
+	/**
+	 * dualband - Whether this is a concurrent dualband AP
+	 */
+	int dualband;
+
+	/**
+	 * force_per_enrollee_psk - Force per-Enrollee random PSK
+	 *
+	 * This forces per-Enrollee random PSK to be generated even if a default
+	 * PSK is set for a network.
+	 */
+	int force_per_enrollee_psk;
+};
+
+
+/**
+ * enum wps_event - WPS event types
+ */
+enum wps_event {
+	/**
+	 * WPS_EV_M2D - M2D received (Registrar did not know us)
+	 */
+	WPS_EV_M2D,
+
+	/**
+	 * WPS_EV_FAIL - Registration failed
+	 */
+	WPS_EV_FAIL,
+
+	/**
+	 * WPS_EV_SUCCESS - Registration succeeded
+	 */
+	WPS_EV_SUCCESS,
+
+	/**
+	 * WPS_EV_PWD_AUTH_FAIL - Password authentication failed
+	 */
+	WPS_EV_PWD_AUTH_FAIL,
+
+	/**
+	 * WPS_EV_PBC_OVERLAP - PBC session overlap detected
+	 */
+	WPS_EV_PBC_OVERLAP,
+
+	/**
+	 * WPS_EV_PBC_TIMEOUT - PBC walktime expired before protocol run start
+	 */
+	WPS_EV_PBC_TIMEOUT,
+
+	/**
+	 * WPS_EV_PBC_ACTIVE - PBC mode was activated
+	 */
+	WPS_EV_PBC_ACTIVE,
+
+	/**
+	 * WPS_EV_PBC_DISABLE - PBC mode was disabled
+	 */
+	WPS_EV_PBC_DISABLE,
+
+	/**
+	 * WPS_EV_ER_AP_ADD - ER: AP added
+	 */
+	WPS_EV_ER_AP_ADD,
+
+	/**
+	 * WPS_EV_ER_AP_REMOVE - ER: AP removed
+	 */
+	WPS_EV_ER_AP_REMOVE,
+
+	/**
+	 * WPS_EV_ER_ENROLLEE_ADD - ER: Enrollee added
+	 */
+	WPS_EV_ER_ENROLLEE_ADD,
+
+	/**
+	 * WPS_EV_ER_ENROLLEE_REMOVE - ER: Enrollee removed
+	 */
+	WPS_EV_ER_ENROLLEE_REMOVE,
+
+	/**
+	 * WPS_EV_ER_AP_SETTINGS - ER: AP Settings learned
+	 */
+	WPS_EV_ER_AP_SETTINGS,
+
+	/**
+	 * WPS_EV_ER_SET_SELECTED_REGISTRAR - ER: SetSelectedRegistrar event
+	 */
+	WPS_EV_ER_SET_SELECTED_REGISTRAR,
+
+	/**
+	 * WPS_EV_AP_PIN_SUCCESS - External Registrar used correct AP PIN
+	 */
+	WPS_EV_AP_PIN_SUCCESS
+};
+
+/**
+ * union wps_event_data - WPS event data
+ */
+union wps_event_data {
+	/**
+	 * struct wps_event_m2d - M2D event data
+	 */
+	struct wps_event_m2d {
+		u16 config_methods;
+		const u8 *manufacturer;
+		size_t manufacturer_len;
+		const u8 *model_name;
+		size_t model_name_len;
+		const u8 *model_number;
+		size_t model_number_len;
+		const u8 *serial_number;
+		size_t serial_number_len;
+		const u8 *dev_name;
+		size_t dev_name_len;
+		const u8 *primary_dev_type; /* 8 octets */
+		u16 config_error;
+		u16 dev_password_id;
+	} m2d;
+
+	/**
+	 * struct wps_event_fail - Registration failure information
+	 * @msg: enum wps_msg_type
+	 */
+	struct wps_event_fail {
+		int msg;
+		u16 config_error;
+		u16 error_indication;
+		u8 peer_macaddr[ETH_ALEN];
+	} fail;
+
+	struct wps_event_success {
+		u8 peer_macaddr[ETH_ALEN];
+	} success;
+
+	struct wps_event_pwd_auth_fail {
+		int enrollee;
+		int part;
+		u8 peer_macaddr[ETH_ALEN];
+	} pwd_auth_fail;
+
+	struct wps_event_er_ap {
+		const u8 *uuid;
+		const u8 *mac_addr;
+		const char *friendly_name;
+		const char *manufacturer;
+		const char *manufacturer_url;
+		const char *model_description;
+		const char *model_name;
+		const char *model_number;
+		const char *model_url;
+		const char *serial_number;
+		const char *upc;
+		const u8 *pri_dev_type;
+		u8 wps_state;
+	} ap;
+
+	struct wps_event_er_enrollee {
+		const u8 *uuid;
+		const u8 *mac_addr;
+		int m1_received;
+		u16 config_methods;
+		u16 dev_passwd_id;
+		const u8 *pri_dev_type;
+		const char *dev_name;
+		const char *manufacturer;
+		const char *model_name;
+		const char *model_number;
+		const char *serial_number;
+	} enrollee;
+
+	struct wps_event_er_ap_settings {
+		const u8 *uuid;
+		const struct wps_credential *cred;
+	} ap_settings;
+
+	struct wps_event_er_set_selected_registrar {
+		const u8 *uuid;
+		int sel_reg;
+		u16 dev_passwd_id;
+		u16 sel_reg_config_methods;
+		enum {
+			WPS_ER_SET_SEL_REG_START,
+			WPS_ER_SET_SEL_REG_DONE,
+			WPS_ER_SET_SEL_REG_FAILED
+		} state;
+	} set_sel_reg;
+};
+
+/**
+ * struct upnp_pending_message - Pending PutWLANResponse messages
+ * @next: Pointer to next pending message or %NULL
+ * @addr: NewWLANEventMAC
+ * @msg: NewMessage
+ * @type: Message Type
+ */
+struct upnp_pending_message {
+	struct upnp_pending_message *next;
+	u8 addr[ETH_ALEN];
+	struct wpabuf *msg;
+	enum wps_msg_type type;
+};
+
+/**
+ * struct wps_context - Long term WPS context data
+ *
+ * This data is stored at the higher layer Authenticator or Supplicant data
+ * structures and it is maintained over multiple registration protocol runs.
+ */
+struct wps_context {
+	/**
+	 * ap - Whether the local end is an access point
+	 */
+	int ap;
+
+	/**
+	 * registrar - Pointer to WPS registrar data from wps_registrar_init()
+	 */
+	struct wps_registrar *registrar;
+
+	/**
+	 * wps_state - Current WPS state
+	 */
+	enum wps_state wps_state;
+
+	/**
+	 * ap_setup_locked - Whether AP setup is locked (only used at AP)
+	 */
+	int ap_setup_locked;
+
+	/**
+	 * uuid - Own UUID
+	 */
+	u8 uuid[16];
+
+	/**
+	 * ssid - SSID
+	 *
+	 * This SSID is used by the Registrar to fill in information for
+	 * Credentials. In addition, AP uses it when acting as an Enrollee to
+	 * notify Registrar of the current configuration.
+	 */
+	u8 ssid[SSID_MAX_LEN];
+
+	/**
+	 * ssid_len - Length of ssid in octets
+	 */
+	size_t ssid_len;
+
+	/**
+	 * dev - Own WPS device data
+	 */
+	struct wps_device_data dev;
+
+	/**
+	 * dh_ctx - Context data for Diffie-Hellman operation
+	 */
+	void *dh_ctx;
+
+	/**
+	 * dh_privkey - Diffie-Hellman private key
+	 */
+	struct wpabuf *dh_privkey;
+
+	/**
+	 * dh_pubkey_oob - Diffie-Hellman public key
+	 */
+	struct wpabuf *dh_pubkey;
+
+	/**
+	 * config_methods - Enabled configuration methods
+	 *
+	 * Bit field of WPS_CONFIG_*
+	 */
+	u16 config_methods;
+
+	/**
+	 * encr_types - Enabled encryption types (bit field of WPS_ENCR_*)
+	 */
+	u16 encr_types;
+
+	/**
+	 * auth_types - Authentication types (bit field of WPS_AUTH_*)
+	 */
+	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
+	 * uses this when acting as an Enrollee to notify Registrar of the
+	 * current configuration.
+	 *
+	 * When using WPA/WPA2-Person, this key can be either the ASCII
+	 * passphrase (8..63 characters) or the 32-octet PSK (64 hex
+	 * characters). When this is set to the ASCII passphrase, the PSK can
+	 * be provided in the psk buffer and used per-Enrollee to control which
+	 * key type is included in the Credential (e.g., to reduce calculation
+	 * need on low-powered devices by provisioning PSK while still allowing
+	 * other devices to get the passphrase).
+	 */
+	u8 *network_key;
+
+	/**
+	 * network_key_len - Length of network_key in octets
+	 */
+	size_t network_key_len;
+
+	/**
+	 * psk - The current network PSK
+	 *
+	 * This optional value can be used to provide the current PSK if
+	 * network_key is set to the ASCII passphrase.
+	 */
+	u8 psk[32];
+
+	/**
+	 * psk_set - Whether psk value is set
+	 */
+	int psk_set;
+
+	/**
+	 * ap_settings - AP Settings override for M7 (only used at AP)
+	 *
+	 * If %NULL, AP Settings attributes will be generated based on the
+	 * current network configuration.
+	 */
+	u8 *ap_settings;
+
+	/**
+	 * ap_settings_len - Length of ap_settings in octets
+	 */
+	size_t ap_settings_len;
+
+	/**
+	 * friendly_name - Friendly Name (required for UPnP)
+	 */
+	char *friendly_name;
+
+	/**
+	 * manufacturer_url - Manufacturer URL (optional for UPnP)
+	 */
+	char *manufacturer_url;
+
+	/**
+	 * model_description - Model Description (recommended for UPnP)
+	 */
+	char *model_description;
+
+	/**
+	 * model_url - Model URL (optional for UPnP)
+	 */
+	char *model_url;
+
+	/**
+	 * upc - Universal Product Code (optional for UPnP)
+	 */
+	char *upc;
+
+	/**
+	 * cred_cb - Callback to notify that new Credentials were received
+	 * @ctx: Higher layer context data (cb_ctx)
+	 * @cred: The received Credential
+	 * Return: 0 on success, -1 on failure
+	 */
+	int (*cred_cb)(void *ctx, const struct wps_credential *cred);
+
+	/**
+	 * event_cb - Event callback (state information about progress)
+	 * @ctx: Higher layer context data (cb_ctx)
+	 * @event: Event type
+	 * @data: Event data
+	 */
+	void (*event_cb)(void *ctx, enum wps_event event,
+			 union wps_event_data *data);
+
+	/**
+	 * rf_band_cb - Fetch currently used RF band
+	 * @ctx: Higher layer context data (cb_ctx)
+	 * Return: Current used RF band or 0 if not known
+	 */
+	int (*rf_band_cb)(void *ctx);
+
+	/**
+	 * cb_ctx: Higher layer context data for callbacks
+	 */
+	void *cb_ctx;
+
+	struct upnp_wps_device_sm *wps_upnp;
+
+	/* Pending messages from UPnP PutWLANResponse */
+	struct upnp_pending_message *upnp_msgs;
+
+	u16 ap_nfc_dev_pw_id;
+	struct wpabuf *ap_nfc_dh_pubkey;
+	struct wpabuf *ap_nfc_dh_privkey;
+	struct wpabuf *ap_nfc_dev_pw;
+};
+
+struct wps_registrar *
+wps_registrar_init(struct wps_context *wps,
+		   const struct wps_registrar_config *cfg);
+void wps_registrar_deinit(struct wps_registrar *reg);
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
+			  const u8 *uuid, const u8 *pin, size_t pin_len,
+			  int timeout);
+int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid);
+int wps_registrar_wps_cancel(struct wps_registrar *reg);
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid);
+int wps_registrar_button_pushed(struct wps_registrar *reg,
+				const u8 *p2p_dev_addr);
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
+			    const u8 *dev_pw, size_t dev_pw_len);
+void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
+				const struct wpabuf *wps_data,
+				int p2p_wildcard);
+int wps_registrar_update_ie(struct wps_registrar *reg);
+int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
+			   char *buf, size_t buflen);
+int wps_registrar_config_ap(struct wps_registrar *reg,
+			    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,
+				   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);
+void wps_registrar_flush(struct wps_registrar *reg);
+
+int wps_build_credential_wrap(struct wpabuf *msg,
+			      const struct wps_credential *cred);
+
+unsigned int wps_pin_checksum(unsigned int pin);
+unsigned int wps_pin_valid(unsigned int pin);
+unsigned int wps_generate_pin(void);
+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, 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);
+
+struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname,
+			    const char *filter);
+void wps_er_refresh(struct wps_er *er);
+void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx);
+void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
+			u16 sel_reg_config_methods);
+int wps_er_pbc(struct wps_er *er, const u8 *uuid, const u8 *addr);
+const u8 * wps_er_get_sta_uuid(struct wps_er *er, const u8 *addr);
+int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *addr,
+		 const u8 *pin, size_t pin_len);
+int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr,
+		      const struct wps_credential *cred);
+int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr,
+		  const u8 *pin, size_t pin_len,
+		  const struct wps_credential *cred);
+struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps,
+					      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,
+			    size_t buf_len);
+void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
+u16 wps_config_methods_str2bin(const char *str);
+struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
+				       const struct wpabuf *pubkey,
+				       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_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);
+int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
+				   const u8 *addr);
+int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr);
+int wps_validate_assoc_req(const struct wpabuf *wps_ie);
+int wps_validate_assoc_resp(const struct wpabuf *wps_ie);
+int wps_validate_m1(const struct wpabuf *tlvs);
+int wps_validate_m2(const struct wpabuf *tlvs);
+int wps_validate_m2d(const struct wpabuf *tlvs);
+int wps_validate_m3(const struct wpabuf *tlvs);
+int wps_validate_m4(const struct wpabuf *tlvs);
+int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m5(const struct wpabuf *tlvs);
+int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m6(const struct wpabuf *tlvs);
+int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m7(const struct wpabuf *tlvs);
+int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2);
+int wps_validate_m8(const struct wpabuf *tlvs);
+int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2);
+int wps_validate_wsc_ack(const struct wpabuf *tlvs);
+int wps_validate_wsc_nack(const struct wpabuf *tlvs);
+int wps_validate_wsc_done(const struct wpabuf *tlvs);
+int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs);
+#else /* CONFIG_WPS_STRICT */
+static inline int wps_validate_beacon(const struct wpabuf *wps_ie){
+	return 0;
+}
+
+static inline int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie,
+						 int probe, const u8 *addr)
+{
+	return 0;
+}
+
+static inline int wps_validate_probe_req(const struct wpabuf *wps_ie,
+					 const u8 *addr)
+{
+	return 0;
+}
+
+static inline int wps_validate_assoc_req(const struct wpabuf *wps_ie)
+{
+	return 0;
+}
+
+static inline int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
+{
+	return 0;
+}
+
+static inline int wps_validate_m1(const struct wpabuf *tlvs)
+{
+	return 0;
+}
+
+static inline int wps_validate_m2(const struct wpabuf *tlvs)
+{
+	return 0;
+}
+
+static inline int wps_validate_m2d(const struct wpabuf *tlvs)
+{
+	return 0;
+}
+
+static inline int wps_validate_m3(const struct wpabuf *tlvs)
+{
+	return 0;
+}
+
+static inline int wps_validate_m4(const struct wpabuf *tlvs)
+{
+	return 0;
+}
+
+static inline int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
+{
+	return 0;
+}
+
+static inline int wps_validate_m5(const struct wpabuf *tlvs)
+{
+	return 0;
+}
+
+static inline int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
+{
+	return 0;
+}
+
+static inline int wps_validate_m6(const struct wpabuf *tlvs)
+{
+	return 0;
+}
+
+static inline int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
+{
+	return 0;
+}
+
+static inline int wps_validate_m7(const struct wpabuf *tlvs)
+{
+	return 0;
+}
+
+static inline int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap,
+				       int wps2)
+{
+	return 0;
+}
+
+static inline int wps_validate_m8(const struct wpabuf *tlvs)
+{
+	return 0;
+}
+
+static inline int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap,
+				       int wps2)
+{
+	return 0;
+}
+
+static inline int wps_validate_wsc_ack(const struct wpabuf *tlvs)
+{
+	return 0;
+}
+
+static inline int wps_validate_wsc_nack(const struct wpabuf *tlvs)
+{
+	return 0;
+}
+
+static inline int wps_validate_wsc_done(const struct wpabuf *tlvs)
+{
+	return 0;
+}
+
+static inline int wps_validate_upnp_set_selected_registrar(
+	const struct wpabuf *tlvs)
+{
+	return 0;
+}
+#endif /* CONFIG_WPS_STRICT */
+
+#endif /* WPS_H */
diff --git a/hostap/src/wps/wps_attr_build.c b/hostap/src/wps/wps_attr_build.c
new file mode 100644
index 0000000..b689357
--- /dev/null
+++ b/hostap/src/wps/wps_attr_build.c
@@ -0,0 +1,485 @@
+/*
+ * Wi-Fi Protected Setup - attribute building
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_group5.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "wps_i.h"
+
+
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
+{
+	struct wpabuf *pubkey;
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Public Key");
+	wpabuf_free(wps->dh_privkey);
+	wps->dh_privkey = NULL;
+	if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey &&
+	    wps->wps->dh_ctx) {
+		wpa_printf(MSG_DEBUG, "WPS: Using pre-configured DH keys");
+		if (wps->wps->dh_pubkey == NULL) {
+			wpa_printf(MSG_DEBUG,
+				   "WPS: wps->wps->dh_pubkey == NULL");
+			return -1;
+		}
+		wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey);
+		wps->dh_ctx = wps->wps->dh_ctx;
+		wps->wps->dh_ctx = NULL;
+		pubkey = wpabuf_dup(wps->wps->dh_pubkey);
+#ifdef CONFIG_WPS_NFC
+	} 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,
+				   "WPS: wps->wps->ap_nfc_dh_privkey == NULL");
+			return -1;
+		}
+		if (wps->wps->ap_nfc_dh_pubkey == NULL) {
+			wpa_printf(MSG_DEBUG,
+				   "WPS: wps->wps->ap_nfc_dh_pubkey == NULL");
+			return -1;
+		}
+		wps->dh_privkey = wpabuf_dup(wps->wps->ap_nfc_dh_privkey);
+		pubkey = wpabuf_dup(wps->wps->ap_nfc_dh_pubkey);
+		wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey);
+#endif /* CONFIG_WPS_NFC */
+	} else {
+		wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys");
+		dh5_free(wps->dh_ctx);
+		wps->dh_ctx = dh5_init(&wps->dh_privkey, &pubkey);
+		pubkey = wpabuf_zeropad(pubkey, 192);
+	}
+	if (wps->dh_ctx == NULL || wps->dh_privkey == NULL || pubkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to initialize "
+			   "Diffie-Hellman handshake");
+		wpabuf_free(pubkey);
+		return -1;
+	}
+	wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: DH own Public Key", pubkey);
+
+	wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
+	wpabuf_put_be16(msg, wpabuf_len(pubkey));
+	wpabuf_put_buf(msg, pubkey);
+
+	if (wps->registrar) {
+		wpabuf_free(wps->dh_pubkey_r);
+		wps->dh_pubkey_r = pubkey;
+	} else {
+		wpabuf_free(wps->dh_pubkey_e);
+		wps->dh_pubkey_e = pubkey;
+	}
+
+	return 0;
+}
+
+
+int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Request Type");
+	wpabuf_put_be16(msg, ATTR_REQUEST_TYPE);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, type);
+	return 0;
+}
+
+
+int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Response Type (%d)", type);
+	wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, type);
+	return 0;
+}
+
+
+int wps_build_config_methods(struct wpabuf *msg, u16 methods)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Config Methods (%x)", methods);
+	wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, methods);
+	return 0;
+}
+
+
+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);
+	wpabuf_put_data(msg, uuid, WPS_UUID_LEN);
+	return 0;
+}
+
+
+int wps_build_dev_password_id(struct wpabuf *msg, u16 id)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Device Password ID (%d)", id);
+	wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, id);
+	return 0;
+}
+
+
+int wps_build_config_error(struct wpabuf *msg, u16 err)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Configuration Error (%d)", err);
+	wpabuf_put_be16(msg, ATTR_CONFIG_ERROR);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, err);
+	return 0;
+}
+
+
+int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg)
+{
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[2];
+	size_t len[2];
+
+	if (wps->last_msg == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
+			   "building authenticator");
+		return -1;
+	}
+
+	/* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
+	 * (M_curr* is M_curr without the Authenticator attribute)
+	 */
+	addr[0] = wpabuf_head(wps->last_msg);
+	len[0] = wpabuf_len(wps->last_msg);
+	addr[1] = wpabuf_head(msg);
+	len[1] = wpabuf_len(msg);
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Authenticator");
+	wpabuf_put_be16(msg, ATTR_AUTHENTICATOR);
+	wpabuf_put_be16(msg, WPS_AUTHENTICATOR_LEN);
+	wpabuf_put_data(msg, hash, WPS_AUTHENTICATOR_LEN);
+
+	return 0;
+}
+
+
+int wps_build_version(struct wpabuf *msg)
+{
+	/*
+	 * Note: This attribute is deprecated and set to hardcoded 0x10 for
+	 * 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);
+	wpabuf_put_u8(msg, 0x10);
+	return 0;
+}
+
+
+int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
+		      const u8 *auth_macs, size_t auth_macs_count)
+{
+	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);
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Version2 (0x%x)", WPS_VERSION);
+	wpabuf_put_u8(msg, WFA_ELEM_VERSION2);
+	wpabuf_put_u8(msg, 1);
+	wpabuf_put_u8(msg, WPS_VERSION);
+
+	if (req_to_enroll) {
+		wpa_printf(MSG_DEBUG, "WPS:  * Request to Enroll (1)");
+		wpabuf_put_u8(msg, WFA_ELEM_REQUEST_TO_ENROLL);
+		wpabuf_put_u8(msg, 1);
+		wpabuf_put_u8(msg, 1);
+	}
+
+	if (auth_macs && auth_macs_count) {
+		size_t i;
+		wpa_printf(MSG_DEBUG, "WPS:  * AuthorizedMACs (count=%d)",
+			   (int) auth_macs_count);
+		wpabuf_put_u8(msg, WFA_ELEM_AUTHORIZEDMACS);
+		wpabuf_put_u8(msg, auth_macs_count * ETH_ALEN);
+		wpabuf_put_data(msg, auth_macs, auth_macs_count * ETH_ALEN);
+		for (i = 0; i < auth_macs_count; i++)
+			wpa_printf(MSG_DEBUG, "WPS:    AuthorizedMAC: " MACSTR,
+				   MAC2STR(&auth_macs[i * ETH_ALEN]));
+	}
+
+	WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
+
+#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);
+		wpabuf_put_be16(msg, 1);
+		wpabuf_put_u8(msg, 42);
+	}
+#endif /* CONFIG_WPS_TESTING */
+	return 0;
+}
+
+
+int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Message Type (%d)", msg_type);
+	wpabuf_put_be16(msg, ATTR_MSG_TYPE);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, msg_type);
+	return 0;
+}
+
+
+int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Enrollee Nonce");
+	wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
+	wpabuf_put_be16(msg, WPS_NONCE_LEN);
+	wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN);
+	return 0;
+}
+
+
+int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Registrar Nonce");
+	wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
+	wpabuf_put_be16(msg, WPS_NONCE_LEN);
+	wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN);
+	return 0;
+}
+
+
+int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+	u16 auth_types = WPS_AUTH_TYPES;
+	/* WPA/WPA2-Enterprise enrollment not supported through WPS */
+	auth_types &= ~WPS_AUTH_WPA;
+	auth_types &= ~WPS_AUTH_WPA2;
+	auth_types &= ~WPS_AUTH_SHARED;
+	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type Flags");
+	wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, auth_types);
+	return 0;
+}
+
+
+int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+	u16 encr_types = WPS_ENCR_TYPES;
+	encr_types &= ~WPS_ENCR_WEP;
+	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type Flags");
+	wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, encr_types);
+	return 0;
+}
+
+
+int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Connection Type Flags");
+	wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, WPS_CONN_ESS);
+	return 0;
+}
+
+
+int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Association State");
+	wpabuf_put_be16(msg, ATTR_ASSOC_STATE);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC);
+	return 0;
+}
+
+
+int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg)
+{
+	u8 hash[SHA256_MAC_LEN];
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Key Wrap Authenticator");
+	hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg),
+		    wpabuf_len(msg), hash);
+
+	wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH);
+	wpabuf_put_be16(msg, WPS_KWA_LEN);
+	wpabuf_put_data(msg, hash, WPS_KWA_LEN);
+	return 0;
+}
+
+
+int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
+			    struct wpabuf *plain)
+{
+	size_t pad_len;
+	const size_t block_size = 16;
+	u8 *iv, *data;
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Encrypted Settings");
+
+	/* PKCS#5 v2.0 pad */
+	pad_len = block_size - wpabuf_len(plain) % block_size;
+	os_memset(wpabuf_put(plain, pad_len), pad_len, pad_len);
+
+	wpabuf_put_be16(msg, ATTR_ENCR_SETTINGS);
+	wpabuf_put_be16(msg, block_size + wpabuf_len(plain));
+
+	iv = wpabuf_put(msg, block_size);
+	if (random_get_bytes(iv, block_size) < 0)
+		return -1;
+
+	data = wpabuf_put(msg, 0);
+	wpabuf_put_buf(msg, plain);
+	if (aes_128_cbc_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain)))
+		return -1;
+
+	return 0;
+}
+
+
+#ifdef CONFIG_WPS_OOB
+int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
+			 const struct wpabuf *pubkey, const u8 *dev_pw,
+			 size_t dev_pw_len)
+{
+	size_t hash_len;
+	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);
+	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;
+}
+#endif /* CONFIG_WPS_OOB */
+
+
+/* Encapsulate WPS IE data with one (or more, if needed) IE headers */
+struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
+{
+	struct wpabuf *ie;
+	const u8 *pos, *end;
+
+	ie = wpabuf_alloc(wpabuf_len(data) + 100);
+	if (ie == NULL) {
+		wpabuf_free(data);
+		return NULL;
+	}
+
+	pos = wpabuf_head(data);
+	end = pos + wpabuf_len(data);
+
+	while (end > pos) {
+		size_t frag_len = end - pos;
+		if (frag_len > 251)
+			frag_len = 251;
+		wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+		wpabuf_put_u8(ie, 4 + frag_len);
+		wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+		wpabuf_put_data(ie, pos, frag_len);
+		pos += frag_len;
+	}
+
+	wpabuf_free(data);
+
+	return ie;
+}
+
+
+int wps_build_mac_addr(struct wpabuf *msg, const u8 *addr)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * MAC Address (" MACSTR ")",
+		   MAC2STR(addr));
+	wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+	wpabuf_put_be16(msg, ETH_ALEN);
+	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/hostap/src/wps/wps_attr_parse.c b/hostap/src/wps/wps_attr_parse.c
new file mode 100644
index 0000000..11a967b
--- /dev/null
+++ b/hostap/src/wps/wps_attr_parse.c
@@ -0,0 +1,663 @@
+/*
+ * Wi-Fi Protected Setup - attribute parsing
+ * Copyright (c) 2008, 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 "wps_defs.h"
+#include "wps_attr_parse.h"
+
+#ifndef CONFIG_WPS_STRICT
+#define WPS_WORKAROUNDS
+#endif /* CONFIG_WPS_STRICT */
+
+
+static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
+					  u8 id, u8 len, const u8 *pos)
+{
+	wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u",
+		   id, len);
+	switch (id) {
+	case WFA_ELEM_VERSION2:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
+				   "%u", len);
+			return -1;
+		}
+		attr->version2 = pos;
+		break;
+	case WFA_ELEM_AUTHORIZEDMACS:
+		attr->authorized_macs = pos;
+		attr->authorized_macs_len = len;
+		break;
+	case WFA_ELEM_NETWORK_KEY_SHAREABLE:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
+				   "Shareable length %u", len);
+			return -1;
+		}
+		attr->network_key_shareable = pos;
+		break;
+	case WFA_ELEM_REQUEST_TO_ENROLL:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
+				   "length %u", len);
+			return -1;
+		}
+		attr->request_to_enroll = pos;
+		break;
+	case WFA_ELEM_SETTINGS_DELAY_TIME:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
+				   "Time length %u", len);
+			return -1;
+		}
+		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);
+		break;
+	}
+
+	return 0;
+}
+
+
+static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
+				    u16 len)
+{
+	const u8 *end = pos + len;
+	u8 id, elen;
+
+	while (pos + 2 <= end) {
+		id = *pos++;
+		elen = *pos++;
+		if (pos + elen > end)
+			break;
+		if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
+			return -1;
+		pos += elen;
+	}
+
+	return 0;
+}
+
+
+static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
+				u16 len)
+{
+	u32 vendor_id;
+
+	if (len < 3) {
+		wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
+		return 0;
+	}
+
+	vendor_id = WPA_GET_BE24(pos);
+	switch (vendor_id) {
+	case WPS_VENDOR_ID_WFA:
+		return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
+	}
+
+	/* Handle unknown vendor extensions */
+
+	wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
+		   vendor_id);
+
+	if (len > WPS_MAX_VENDOR_EXT_LEN) {
+		wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
+			   len);
+		return -1;
+	}
+
+	if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
+		wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
+			   "attribute (max %d vendor extensions)",
+			   MAX_WPS_PARSE_VENDOR_EXT);
+		return -1;
+	}
+	attr->vendor_ext[attr->num_vendor_ext] = pos;
+	attr->vendor_ext_len[attr->num_vendor_ext] = len;
+	attr->num_vendor_ext++;
+
+	return 0;
+}
+
+
+static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
+			const u8 *pos, u16 len)
+{
+	switch (type) {
+	case ATTR_VERSION:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
+				   len);
+			return -1;
+		}
+		attr->version = pos;
+		break;
+	case ATTR_MSG_TYPE:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
+				   "length %u", len);
+			return -1;
+		}
+		attr->msg_type = pos;
+		break;
+	case ATTR_ENROLLEE_NONCE:
+		if (len != WPS_NONCE_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
+				   "length %u", len);
+			return -1;
+		}
+		attr->enrollee_nonce = pos;
+		break;
+	case ATTR_REGISTRAR_NONCE:
+		if (len != WPS_NONCE_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
+				   "length %u", len);
+			return -1;
+		}
+		attr->registrar_nonce = pos;
+		break;
+	case ATTR_UUID_E:
+		if (len != WPS_UUID_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
+				   len);
+			return -1;
+		}
+		attr->uuid_e = pos;
+		break;
+	case ATTR_UUID_R:
+		if (len != WPS_UUID_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
+				   len);
+			return -1;
+		}
+		attr->uuid_r = pos;
+		break;
+	case ATTR_AUTH_TYPE_FLAGS:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
+				   "Type Flags length %u", len);
+			return -1;
+		}
+		attr->auth_type_flags = pos;
+		break;
+	case ATTR_ENCR_TYPE_FLAGS:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
+				   "Flags length %u", len);
+			return -1;
+		}
+		attr->encr_type_flags = pos;
+		break;
+	case ATTR_CONN_TYPE_FLAGS:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
+				   "Flags length %u", len);
+			return -1;
+		}
+		attr->conn_type_flags = pos;
+		break;
+	case ATTR_CONFIG_METHODS:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
+				   "length %u", len);
+			return -1;
+		}
+		attr->config_methods = pos;
+		break;
+	case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
+				   "Registrar Config Methods length %u", len);
+			return -1;
+		}
+		attr->sel_reg_config_methods = pos;
+		break;
+	case ATTR_PRIMARY_DEV_TYPE:
+		if (len != WPS_DEV_TYPE_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
+				   "Type length %u", len);
+			return -1;
+		}
+		attr->primary_dev_type = pos;
+		break;
+	case ATTR_RF_BANDS:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
+				   "%u", len);
+			return -1;
+		}
+		attr->rf_bands = pos;
+		break;
+	case ATTR_ASSOC_STATE:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
+				   "length %u", len);
+			return -1;
+		}
+		attr->assoc_state = pos;
+		break;
+	case ATTR_CONFIG_ERROR:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
+				   "Error length %u", len);
+			return -1;
+		}
+		attr->config_error = pos;
+		break;
+	case ATTR_DEV_PASSWORD_ID:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
+				   "ID length %u", len);
+			return -1;
+		}
+		attr->dev_password_id = pos;
+		break;
+	case ATTR_OOB_DEVICE_PASSWORD:
+		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
+		    len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
+		    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;
+		}
+		attr->oob_dev_password = pos;
+		attr->oob_dev_password_len = len;
+		break;
+	case ATTR_OS_VERSION:
+		if (len != 4) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
+				   "%u", len);
+			return -1;
+		}
+		attr->os_version = pos;
+		break;
+	case ATTR_WPS_STATE:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
+				   "Setup State length %u", len);
+			return -1;
+		}
+		attr->wps_state = pos;
+		break;
+	case ATTR_AUTHENTICATOR:
+		if (len != WPS_AUTHENTICATOR_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
+				   "length %u", len);
+			return -1;
+		}
+		attr->authenticator = pos;
+		break;
+	case ATTR_R_HASH1:
+		if (len != WPS_HASH_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
+				   len);
+			return -1;
+		}
+		attr->r_hash1 = pos;
+		break;
+	case ATTR_R_HASH2:
+		if (len != WPS_HASH_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
+				   len);
+			return -1;
+		}
+		attr->r_hash2 = pos;
+		break;
+	case ATTR_E_HASH1:
+		if (len != WPS_HASH_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
+				   len);
+			return -1;
+		}
+		attr->e_hash1 = pos;
+		break;
+	case ATTR_E_HASH2:
+		if (len != WPS_HASH_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
+				   len);
+			return -1;
+		}
+		attr->e_hash2 = pos;
+		break;
+	case ATTR_R_SNONCE1:
+		if (len != WPS_SECRET_NONCE_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
+				   "%u", len);
+			return -1;
+		}
+		attr->r_snonce1 = pos;
+		break;
+	case ATTR_R_SNONCE2:
+		if (len != WPS_SECRET_NONCE_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
+				   "%u", len);
+			return -1;
+		}
+		attr->r_snonce2 = pos;
+		break;
+	case ATTR_E_SNONCE1:
+		if (len != WPS_SECRET_NONCE_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
+				   "%u", len);
+			return -1;
+		}
+		attr->e_snonce1 = pos;
+		break;
+	case ATTR_E_SNONCE2:
+		if (len != WPS_SECRET_NONCE_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
+				   "%u", len);
+			return -1;
+		}
+		attr->e_snonce2 = pos;
+		break;
+	case ATTR_KEY_WRAP_AUTH:
+		if (len != WPS_KWA_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
+				   "Authenticator length %u", len);
+			return -1;
+		}
+		attr->key_wrap_auth = pos;
+		break;
+	case ATTR_AUTH_TYPE:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
+				   "Type length %u", len);
+			return -1;
+		}
+		attr->auth_type = pos;
+		break;
+	case ATTR_ENCR_TYPE:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
+				   "Type length %u", len);
+			return -1;
+		}
+		attr->encr_type = pos;
+		break;
+	case ATTR_NETWORK_INDEX:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
+				   "length %u", len);
+			return -1;
+		}
+		attr->network_idx = pos;
+		break;
+	case ATTR_NETWORK_KEY_INDEX:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
+				   "length %u", len);
+			return -1;
+		}
+		attr->network_key_idx = pos;
+		break;
+	case ATTR_MAC_ADDR:
+		if (len != ETH_ALEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
+				   "length %u", len);
+			return -1;
+		}
+		attr->mac_addr = pos;
+		break;
+	case ATTR_SELECTED_REGISTRAR:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
+				   " length %u", len);
+			return -1;
+		}
+		attr->selected_registrar = pos;
+		break;
+	case ATTR_REQUEST_TYPE:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
+				   "length %u", len);
+			return -1;
+		}
+		attr->request_type = pos;
+		break;
+	case ATTR_RESPONSE_TYPE:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
+				   "length %u", len);
+			return -1;
+		}
+		attr->response_type = pos;
+		break;
+	case ATTR_MANUFACTURER:
+		attr->manufacturer = pos;
+		if (len > WPS_MANUFACTURER_MAX_LEN)
+			attr->manufacturer_len = WPS_MANUFACTURER_MAX_LEN;
+		else
+			attr->manufacturer_len = len;
+		break;
+	case ATTR_MODEL_NAME:
+		attr->model_name = pos;
+		if (len > WPS_MODEL_NAME_MAX_LEN)
+			attr->model_name_len = WPS_MODEL_NAME_MAX_LEN;
+		else
+			attr->model_name_len = len;
+		break;
+	case ATTR_MODEL_NUMBER:
+		attr->model_number = pos;
+		if (len > WPS_MODEL_NUMBER_MAX_LEN)
+			attr->model_number_len = WPS_MODEL_NUMBER_MAX_LEN;
+		else
+			attr->model_number_len = len;
+		break;
+	case ATTR_SERIAL_NUMBER:
+		attr->serial_number = pos;
+		if (len > WPS_SERIAL_NUMBER_MAX_LEN)
+			attr->serial_number_len = WPS_SERIAL_NUMBER_MAX_LEN;
+		else
+			attr->serial_number_len = len;
+		break;
+	case ATTR_DEV_NAME:
+		if (len > WPS_DEV_NAME_MAX_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "WPS: Ignore too long Device Name (len=%u)",
+				   len);
+			break;
+		}
+		attr->dev_name = pos;
+		attr->dev_name_len = len;
+		break;
+	case ATTR_PUBLIC_KEY:
+		/*
+		 * The Public Key attribute is supposed to be exactly 192 bytes
+		 * in length. Allow couple of bytes shorter one to try to
+		 * interoperate with implementations that do not use proper
+		 * zero-padding.
+		 */
+		if (len < 190 || len > 192) {
+			wpa_printf(MSG_DEBUG,
+				   "WPS: Ignore Public Key with unexpected length %u",
+				   len);
+			break;
+		}
+		attr->public_key = pos;
+		attr->public_key_len = len;
+		break;
+	case ATTR_ENCR_SETTINGS:
+		attr->encr_settings = pos;
+		attr->encr_settings_len = len;
+		break;
+	case ATTR_CRED:
+		if (attr->num_cred >= MAX_CRED_COUNT) {
+			wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
+				   "attribute (max %d credentials)",
+				   MAX_CRED_COUNT);
+			break;
+		}
+		attr->cred[attr->num_cred] = pos;
+		attr->cred_len[attr->num_cred] = len;
+		attr->num_cred++;
+		break;
+	case ATTR_SSID:
+		if (len > SSID_MAX_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "WPS: Ignore too long SSID (len=%u)", len);
+			break;
+		}
+		attr->ssid = pos;
+		attr->ssid_len = len;
+		break;
+	case ATTR_NETWORK_KEY:
+		attr->network_key = pos;
+		attr->network_key_len = len;
+		break;
+	case ATTR_AP_SETUP_LOCKED:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
+				   "length %u", len);
+			return -1;
+		}
+		attr->ap_setup_locked = pos;
+		break;
+	case ATTR_REQUESTED_DEV_TYPE:
+		if (len != WPS_DEV_TYPE_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
+				   "Type length %u", len);
+			return -1;
+		}
+		if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
+			wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
+				   "Type attribute (max %u types)",
+				   MAX_REQ_DEV_TYPE_COUNT);
+			break;
+		}
+		attr->req_dev_type[attr->num_req_dev_type] = pos;
+		attr->num_req_dev_type++;
+		break;
+	case ATTR_SECONDARY_DEV_TYPE_LIST:
+		if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
+		    (len % WPS_DEV_TYPE_LEN) > 0) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
+				   "Type length %u", len);
+			return -1;
+		}
+		attr->sec_dev_type_list = pos;
+		attr->sec_dev_type_list_len = len;
+		break;
+	case ATTR_VENDOR_EXT:
+		if (wps_parse_vendor_ext(attr, pos, len) < 0)
+			return -1;
+		break;
+	case ATTR_AP_CHANNEL:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel "
+				   "length %u", len);
+			return -1;
+		}
+		attr->ap_channel = pos;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
+			   "len=%u", type, len);
+		break;
+	}
+
+	return 0;
+}
+
+
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
+{
+	const u8 *pos, *end;
+	u16 type, len;
+#ifdef WPS_WORKAROUNDS
+	u16 prev_type = 0;
+#endif /* WPS_WORKAROUNDS */
+
+	os_memset(attr, 0, sizeof(*attr));
+	pos = wpabuf_head(msg);
+	end = pos + wpabuf_len(msg);
+
+	while (pos < end) {
+		if (end - pos < 4) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
+				   "%lu bytes remaining",
+				   (unsigned long) (end - pos));
+			return -1;
+		}
+
+		type = WPA_GET_BE16(pos);
+		pos += 2;
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u",
+			   type, len);
+		if (len > end - pos) {
+			wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
+			wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
+#ifdef WPS_WORKAROUNDS
+			/*
+			 * Some deployed APs seem to have a bug in encoding of
+			 * Network Key attribute in the Credential attribute
+			 * where they add an extra octet after the Network Key
+			 * attribute at least when open network is being
+			 * provisioned.
+			 */
+			if ((type & 0xff00) != 0x1000 &&
+			    prev_type == ATTR_NETWORK_KEY) {
+				wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
+					   "to skip unexpected octet after "
+					   "Network Key");
+				pos -= 3;
+				continue;
+			}
+#endif /* WPS_WORKAROUNDS */
+			return -1;
+		}
+
+#ifdef WPS_WORKAROUNDS
+		if (type == 0 && len == 0) {
+			/*
+			 * Mac OS X 10.6 seems to be adding 0x00 padding to the
+			 * end of M1. Skip those to avoid interop issues.
+			 */
+			int i;
+			for (i = 0; i < end - pos; i++) {
+				if (pos[i])
+					break;
+			}
+			if (i == end - pos) {
+				wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
+					   "unexpected message padding");
+				break;
+			}
+		}
+#endif /* WPS_WORKAROUNDS */
+
+		if (wps_set_attr(attr, type, pos, len) < 0)
+			return -1;
+
+#ifdef WPS_WORKAROUNDS
+		prev_type = type;
+#endif /* WPS_WORKAROUNDS */
+		pos += len;
+	}
+
+	return 0;
+}
diff --git a/hostap/src/wps/wps_attr_parse.h b/hostap/src/wps/wps_attr_parse.h
new file mode 100644
index 0000000..8188fe9
--- /dev/null
+++ b/hostap/src/wps/wps_attr_parse.h
@@ -0,0 +1,104 @@
+/*
+ * Wi-Fi Protected Setup - attribute parsing
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_ATTR_PARSE_H
+#define WPS_ATTR_PARSE_H
+
+#include "wps.h"
+
+struct wps_parse_attr {
+	/* fixed length fields */
+	const u8 *version; /* 1 octet */
+	const u8 *version2; /* 1 octet */
+	const u8 *msg_type; /* 1 octet */
+	const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
+	const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
+	const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
+	const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
+	const u8 *auth_type_flags; /* 2 octets */
+	const u8 *encr_type_flags; /* 2 octets */
+	const u8 *conn_type_flags; /* 1 octet */
+	const u8 *config_methods; /* 2 octets */
+	const u8 *sel_reg_config_methods; /* 2 octets */
+	const u8 *primary_dev_type; /* 8 octets */
+	const u8 *rf_bands; /* 1 octet */
+	const u8 *assoc_state; /* 2 octets */
+	const u8 *config_error; /* 2 octets */
+	const u8 *dev_password_id; /* 2 octets */
+	const u8 *os_version; /* 4 octets */
+	const u8 *wps_state; /* 1 octet */
+	const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
+	const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
+	const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
+	const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
+	const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
+	const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
+	const u8 *auth_type; /* 2 octets */
+	const u8 *encr_type; /* 2 octets */
+	const u8 *network_idx; /* 1 octet */
+	const u8 *network_key_idx; /* 1 octet */
+	const u8 *mac_addr; /* ETH_ALEN (6) octets */
+	const u8 *selected_registrar; /* 1 octet (Bool) */
+	const u8 *request_type; /* 1 octet */
+	const u8 *response_type; /* 1 octet */
+	const u8 *ap_setup_locked; /* 1 octet */
+	const u8 *settings_delay_time; /* 1 octet */
+	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;
+	const u8 *model_name;
+	const u8 *model_number;
+	const u8 *serial_number;
+	const u8 *dev_name;
+	const u8 *public_key;
+	const u8 *encr_settings;
+	const u8 *ssid; /* <= 32 octets */
+	const u8 *network_key; /* <= 64 octets */
+	const u8 *authorized_macs; /* <= 30 octets */
+	const u8 *sec_dev_type_list; /* <= 128 octets */
+	const u8 *oob_dev_password; /* 38..54 octets */
+	u16 manufacturer_len;
+	u16 model_name_len;
+	u16 model_number_len;
+	u16 serial_number_len;
+	u16 dev_name_len;
+	u16 public_key_len;
+	u16 encr_settings_len;
+	u16 ssid_len;
+	u16 network_key_len;
+	u16 authorized_macs_len;
+	u16 sec_dev_type_list_len;
+	u16 oob_dev_password_len;
+
+	/* attributes that can occur multiple times */
+#define MAX_CRED_COUNT 10
+#define MAX_REQ_DEV_TYPE_COUNT 10
+
+	unsigned int num_cred;
+	unsigned int num_req_dev_type;
+	unsigned int num_vendor_ext;
+
+	u16 cred_len[MAX_CRED_COUNT];
+	u16 vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
+
+	const u8 *cred[MAX_CRED_COUNT];
+	const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
+	const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
+};
+
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
+
+#endif /* WPS_ATTR_PARSE_H */
diff --git a/hostap/src/wps/wps_attr_process.c b/hostap/src/wps/wps_attr_process.c
new file mode 100644
index 0000000..eadb22f
--- /dev/null
+++ b/hostap/src/wps/wps_attr_process.c
@@ -0,0 +1,272 @@
+/*
+ * Wi-Fi Protected Setup - attribute processing
+ * Copyright (c) 2008, 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/sha256.h"
+#include "wps_i.h"
+
+
+int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
+			      const struct wpabuf *msg)
+{
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[2];
+	size_t len[2];
+
+	if (authenticator == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Authenticator attribute "
+			   "included");
+		return -1;
+	}
+
+	if (wps->last_msg == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
+			   "validating authenticator");
+		return -1;
+	}
+
+	/* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
+	 * (M_curr* is M_curr without the Authenticator attribute)
+	 */
+	addr[0] = wpabuf_head(wps->last_msg);
+	len[0] = wpabuf_len(wps->last_msg);
+	addr[1] = wpabuf_head(msg);
+	len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN;
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
+
+	if (os_memcmp_const(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
+			      const u8 *key_wrap_auth)
+{
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *head;
+	size_t len;
+
+	if (key_wrap_auth == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No KWA in decrypted attribute");
+		return -1;
+	}
+
+	head = wpabuf_head(msg);
+	len = wpabuf_len(msg) - 4 - WPS_KWA_LEN;
+	if (head + len != key_wrap_auth - 4) {
+		wpa_printf(MSG_DEBUG, "WPS: KWA not in the end of the "
+			   "decrypted attribute");
+		return -1;
+	}
+
+	hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
+	if (os_memcmp_const(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid KWA");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wps_process_cred_network_idx(struct wps_credential *cred,
+					const u8 *idx)
+{
+	if (idx == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+			   "Network Index");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Network Index: %d", *idx);
+
+	return 0;
+}
+
+
+static int wps_process_cred_ssid(struct wps_credential *cred, const u8 *ssid,
+				 size_t ssid_len)
+{
+	if (ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Credential did not include SSID");
+		return -1;
+	}
+
+	/* Remove zero-padding since some Registrar implementations seem to use
+	 * hardcoded 32-octet length for this attribute */
+	while (ssid_len > 0 && ssid[ssid_len - 1] == 0)
+		ssid_len--;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", ssid, ssid_len);
+	if (ssid_len <= sizeof(cred->ssid)) {
+		os_memcpy(cred->ssid, ssid, ssid_len);
+		cred->ssid_len = ssid_len;
+	}
+
+	return 0;
+}
+
+
+static int wps_process_cred_auth_type(struct wps_credential *cred,
+				      const u8 *auth_type)
+{
+	if (auth_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+			   "Authentication Type");
+		return -1;
+	}
+
+	cred->auth_type = WPA_GET_BE16(auth_type);
+	wpa_printf(MSG_DEBUG, "WPS: Authentication Type: 0x%x",
+		   cred->auth_type);
+
+	return 0;
+}
+
+
+static int wps_process_cred_encr_type(struct wps_credential *cred,
+				      const u8 *encr_type)
+{
+	if (encr_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+			   "Encryption Type");
+		return -1;
+	}
+
+	cred->encr_type = WPA_GET_BE16(encr_type);
+	wpa_printf(MSG_DEBUG, "WPS: Encryption Type: 0x%x",
+		   cred->encr_type);
+
+	return 0;
+}
+
+
+static int wps_process_cred_network_key_idx(struct wps_credential *cred,
+					    const u8 *key_idx)
+{
+	if (key_idx == NULL)
+		return 0; /* optional attribute */
+
+	wpa_printf(MSG_DEBUG, "WPS: Network Key Index: %d", *key_idx);
+	cred->key_idx = *key_idx;
+
+	return 0;
+}
+
+
+static int wps_process_cred_network_key(struct wps_credential *cred,
+					const u8 *key, size_t key_len)
+{
+	if (key == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+			   "Network Key");
+		if (cred->auth_type == WPS_AUTH_OPEN &&
+		    cred->encr_type == WPS_ENCR_NONE) {
+			wpa_printf(MSG_DEBUG, "WPS: Workaround - Allow "
+				   "missing mandatory Network Key attribute "
+				   "for open network");
+			return 0;
+		}
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", key, key_len);
+	if (key_len <= sizeof(cred->key)) {
+		os_memcpy(cred->key, key, key_len);
+		cred->key_len = key_len;
+	}
+
+	return 0;
+}
+
+
+static int wps_process_cred_mac_addr(struct wps_credential *cred,
+				     const u8 *mac_addr)
+{
+	if (mac_addr == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+			   "MAC Address");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, MAC2STR(mac_addr));
+	os_memcpy(cred->mac_addr, mac_addr, ETH_ALEN);
+
+	return 0;
+}
+
+
+static int wps_workaround_cred_key(struct wps_credential *cred)
+{
+	if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
+	    cred->key_len > 8 && cred->key_len < 64 &&
+	    cred->key[cred->key_len - 1] == 0) {
+#ifdef CONFIG_WPS_STRICT
+		wpa_printf(MSG_INFO, "WPS: WPA/WPA2-Personal passphrase uses "
+			   "forbidden NULL termination");
+		wpa_hexdump_ascii_key(MSG_INFO, "WPS: Network Key",
+				      cred->key, cred->key_len);
+		return -1;
+#else /* CONFIG_WPS_STRICT */
+		/*
+		 * A deployed external registrar is known to encode ASCII
+		 * passphrases incorrectly. Remove the extra NULL termination
+		 * to fix the encoding.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS: Workaround - remove NULL "
+			   "termination from ASCII passphrase");
+		cred->key_len--;
+#endif /* CONFIG_WPS_STRICT */
+	}
+	return 0;
+}
+
+
+int wps_process_cred(struct wps_parse_attr *attr,
+		     struct wps_credential *cred)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Process Credential");
+
+	/* TODO: support multiple Network Keys */
+	if (wps_process_cred_network_idx(cred, attr->network_idx) ||
+	    wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
+	    wps_process_cred_auth_type(cred, attr->auth_type) ||
+	    wps_process_cred_encr_type(cred, attr->encr_type) ||
+	    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))
+		return -1;
+
+	return wps_workaround_cred_key(cred);
+}
+
+
+int wps_process_ap_settings(struct wps_parse_attr *attr,
+			    struct wps_credential *cred)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Processing AP Settings");
+	os_memset(cred, 0, sizeof(*cred));
+	/* TODO: optional attributes New Password and Device Password ID */
+	if (wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
+	    wps_process_cred_auth_type(cred, attr->auth_type) ||
+	    wps_process_cred_encr_type(cred, attr->encr_type) ||
+	    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))
+		return -1;
+
+	return wps_workaround_cred_key(cred);
+}
diff --git a/hostap/src/wps/wps_common.c b/hostap/src/wps/wps_common.c
new file mode 100644
index 0000000..88f85fe
--- /dev/null
+++ b/hostap/src/wps/wps_common.c
@@ -0,0 +1,909 @@
+/*
+ * Wi-Fi Protected Setup - common functionality
+ * Copyright (c) 2008-2012, 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 "common/defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_group5.h"
+#include "crypto/sha1.h"
+#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,
+	     const char *label, u8 *res, size_t res_len)
+{
+	u8 i_buf[4], key_bits[4];
+	const u8 *addr[4];
+	size_t len[4];
+	int i, iter;
+	u8 hash[SHA256_MAC_LEN], *opos;
+	size_t left;
+
+	WPA_PUT_BE32(key_bits, res_len * 8);
+
+	addr[0] = i_buf;
+	len[0] = sizeof(i_buf);
+	addr[1] = label_prefix;
+	len[1] = label_prefix_len;
+	addr[2] = (const u8 *) label;
+	len[2] = os_strlen(label);
+	addr[3] = key_bits;
+	len[3] = sizeof(key_bits);
+
+	iter = (res_len + SHA256_MAC_LEN - 1) / SHA256_MAC_LEN;
+	opos = res;
+	left = res_len;
+
+	for (i = 1; i <= iter; i++) {
+		WPA_PUT_BE32(i_buf, i);
+		hmac_sha256_vector(key, SHA256_MAC_LEN, 4, addr, len, hash);
+		if (i < iter) {
+			os_memcpy(opos, hash, SHA256_MAC_LEN);
+			opos += SHA256_MAC_LEN;
+			left -= SHA256_MAC_LEN;
+		} else
+			os_memcpy(opos, hash, left);
+	}
+}
+
+
+int wps_derive_keys(struct wps_data *wps)
+{
+	struct wpabuf *pubkey, *dh_shared;
+	u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN];
+	const u8 *addr[3];
+	size_t len[3];
+	u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN];
+
+	if (wps->dh_privkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available");
+		return -1;
+	}
+
+	pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r;
+	if (pubkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Peer DH public key not available");
+		return -1;
+	}
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey);
+	dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey);
+	dh5_free(wps->dh_ctx);
+	wps->dh_ctx = NULL;
+	dh_shared = wpabuf_zeropad(dh_shared, 192);
+	if (dh_shared == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key");
+		return -1;
+	}
+
+	/* Own DH private key is not needed anymore */
+	wpabuf_free(wps->dh_privkey);
+	wps->dh_privkey = NULL;
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared);
+
+	/* DHKey = SHA-256(g^AB mod p) */
+	addr[0] = wpabuf_head(dh_shared);
+	len[0] = wpabuf_len(dh_shared);
+	sha256_vector(1, addr, len, dhkey);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey));
+	wpabuf_free(dh_shared);
+
+	/* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */
+	addr[0] = wps->nonce_e;
+	len[0] = WPS_NONCE_LEN;
+	addr[1] = wps->mac_addr_e;
+	len[1] = ETH_ALEN;
+	addr[2] = wps->nonce_r;
+	len[2] = WPS_NONCE_LEN;
+	hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, len, kdk);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk));
+
+	wps_kdf(kdk, NULL, 0, "Wi-Fi Easy and Secure Key Derivation",
+		keys, sizeof(keys));
+	os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN);
+	os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN);
+	os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN,
+		  WPS_EMSK_LEN);
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey",
+			wps->authkey, WPS_AUTHKEY_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey",
+			wps->keywrapkey, WPS_KEYWRAPKEY_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN);
+
+	return 0;
+}
+
+
+void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+		    size_t dev_passwd_len)
+{
+	u8 hash[SHA256_MAC_LEN];
+
+	hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd,
+		    (dev_passwd_len + 1) / 2, hash);
+	os_memcpy(wps->psk1, hash, WPS_PSK_LEN);
+	hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN,
+		    dev_passwd + (dev_passwd_len + 1) / 2,
+		    dev_passwd_len / 2, hash);
+	os_memcpy(wps->psk2, hash, WPS_PSK_LEN);
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password",
+			      dev_passwd, dev_passwd_len);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN);
+}
+
+
+struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
+					  size_t encr_len)
+{
+	struct wpabuf *decrypted;
+	const size_t block_size = 16;
+	size_t i;
+	u8 pad;
+	const u8 *pos;
+
+	/* AES-128-CBC */
+	if (encr == NULL || encr_len < 2 * block_size || encr_len % block_size)
+	{
+		wpa_printf(MSG_DEBUG, "WPS: No Encrypted Settings received");
+		return NULL;
+	}
+
+	decrypted = wpabuf_alloc(encr_len - block_size);
+	if (decrypted == NULL)
+		return NULL;
+
+	wpa_hexdump(MSG_MSGDUMP, "WPS: Encrypted Settings", encr, encr_len);
+	wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size);
+	if (aes_128_cbc_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted),
+				wpabuf_len(decrypted))) {
+		wpabuf_free(decrypted);
+		return NULL;
+	}
+
+	wpa_hexdump_buf_key(MSG_MSGDUMP, "WPS: Decrypted Encrypted Settings",
+			    decrypted);
+
+	pos = wpabuf_head_u8(decrypted) + wpabuf_len(decrypted) - 1;
+	pad = *pos;
+	if (pad > wpabuf_len(decrypted)) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value");
+		wpabuf_free(decrypted);
+		return NULL;
+	}
+	for (i = 0; i < pad; i++) {
+		if (*pos-- != pad) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad "
+				   "string");
+			wpabuf_free(decrypted);
+			return NULL;
+		}
+	}
+	decrypted->used -= pad;
+
+	return decrypted;
+}
+
+
+/**
+ * wps_pin_checksum - Compute PIN checksum
+ * @pin: Seven digit PIN (i.e., eight digit PIN without the checksum digit)
+ * Returns: Checksum digit
+ */
+unsigned int wps_pin_checksum(unsigned int pin)
+{
+	unsigned int accum = 0;
+	while (pin) {
+		accum += 3 * (pin % 10);
+		pin /= 10;
+		accum += pin % 10;
+		pin /= 10;
+	}
+
+	return (10 - accum % 10) % 10;
+}
+
+
+/**
+ * wps_pin_valid - Check whether a PIN has a valid checksum
+ * @pin: Eight digit PIN (i.e., including the checksum digit)
+ * Returns: 1 if checksum digit is valid, or 0 if not
+ */
+unsigned int wps_pin_valid(unsigned int pin)
+{
+	return wps_pin_checksum(pin / 10) == (pin % 10);
+}
+
+
+/**
+ * wps_generate_pin - Generate a random PIN
+ * Returns: Eight digit PIN (i.e., including the checksum digit)
+ */
+unsigned int wps_generate_pin(void)
+{
+	unsigned int val;
+
+	/* Generate seven random digits for the PIN */
+	if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) {
+		struct os_time now;
+		os_get_time(&now);
+		val = os_random() ^ now.sec ^ now.usec;
+	}
+	val %= 10000000;
+
+	/* Append checksum digit */
+	return val * 10 + wps_pin_checksum(val);
+}
+
+
+int wps_pin_str_valid(const char *pin)
+{
+	const char *p;
+	size_t len;
+
+	p = pin;
+	while (*p >= '0' && *p <= '9')
+		p++;
+	if (*p != '\0')
+		return 0;
+
+	len = p - pin;
+	return len == 4 || len == 8;
+}
+
+
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+		    u16 config_error, u16 error_indication, const u8 *mac_addr)
+{
+	union wps_event_data data;
+
+	if (wps->event_cb == NULL)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	data.fail.msg = msg;
+	data.fail.config_error = config_error;
+	data.fail.error_indication = error_indication;
+	os_memcpy(data.fail.peer_macaddr, mac_addr, ETH_ALEN);
+	wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data);
+}
+
+
+void wps_success_event(struct wps_context *wps, const u8 *mac_addr)
+{
+	union wps_event_data data;
+
+	if (wps->event_cb == NULL)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.success.peer_macaddr, mac_addr, ETH_ALEN);
+	wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, &data);
+}
+
+
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part,
+			     const u8 *mac_addr)
+{
+	union wps_event_data data;
+
+	if (wps->event_cb == NULL)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	data.pwd_auth_fail.enrollee = enrollee;
+	data.pwd_auth_fail.part = part;
+	os_memcpy(data.pwd_auth_fail.peer_macaddr, mac_addr, ETH_ALEN);
+	wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data);
+}
+
+
+void wps_pbc_overlap_event(struct wps_context *wps)
+{
+	if (wps->event_cb == NULL)
+		return;
+
+	wps->event_cb(wps->cb_ctx, WPS_EV_PBC_OVERLAP, NULL);
+}
+
+
+void wps_pbc_timeout_event(struct wps_context *wps)
+{
+	if (wps->event_cb == NULL)
+		return;
+
+	wps->event_cb(wps->cb_ctx, WPS_EV_PBC_TIMEOUT, NULL);
+}
+
+
+void wps_pbc_active_event(struct wps_context *wps)
+{
+	if (wps->event_cb == NULL)
+		return;
+
+	wps->event_cb(wps->cb_ctx, WPS_EV_PBC_ACTIVE, NULL);
+}
+
+
+void wps_pbc_disable_event(struct wps_context *wps)
+{
+	if (wps->event_cb == NULL)
+		return;
+
+	wps->event_cb(wps->cb_ctx, WPS_EV_PBC_DISABLE, NULL);
+}
+
+
+#ifdef CONFIG_WPS_OOB
+
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+				 int channel)
+{
+	struct wps_data data;
+	struct wpabuf *plain;
+
+	plain = wpabuf_alloc(500);
+	if (plain == NULL) {
+		wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+			   "credential");
+		return NULL;
+	}
+
+	os_memset(&data, 0, sizeof(data));
+	data.wps = wps;
+	data.auth_type = wps->auth_types;
+	data.encr_type = wps->encr_types;
+	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);
+		return NULL;
+	}
+
+	if (wps->wps_state == WPS_STATE_NOT_CONFIGURED && data.new_psk &&
+	    wps->ap) {
+		struct wps_credential cred;
+
+		wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based "
+			   "on credential token generation");
+
+		os_memset(&cred, 0, sizeof(cred));
+		os_memcpy(cred.ssid, wps->ssid, wps->ssid_len);
+		cred.ssid_len = wps->ssid_len;
+		cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK;
+		cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES;
+		os_memcpy(cred.key, data.new_psk, data.new_psk_len);
+		cred.key_len = data.new_psk_len;
+
+		wps->wps_state = WPS_STATE_CONFIGURED;
+		wpa_hexdump_ascii_key(MSG_DEBUG,
+				      "WPS: Generated random passphrase",
+				      data.new_psk, data.new_psk_len);
+		if (wps->cred_cb)
+			wps->cred_cb(wps->cb_ctx, &cred);
+	}
+
+	os_free(data.new_psk);
+
+	return plain;
+}
+
+
+struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
+				       const struct wpabuf *pubkey,
+				       const struct wpabuf *dev_pw)
+{
+	struct wpabuf *data;
+
+	data = wpabuf_alloc(200);
+	if (data == NULL)
+		return NULL;
+
+	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 "
+			   "token");
+		wpabuf_free(data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr)
+{
+	struct wpabuf msg;
+	size_t i;
+
+	for (i = 0; i < attr->num_cred; i++) {
+		struct wps_credential local_cred;
+		struct wps_parse_attr cattr;
+
+		os_memset(&local_cred, 0, sizeof(local_cred));
+		wpabuf_set(&msg, attr->cred[i], attr->cred_len[i]);
+		if (wps_parse_msg(&msg, &cattr) < 0 ||
+		    wps_process_cred(&cattr, &local_cred)) {
+			wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB "
+				   "credential");
+			return -1;
+		}
+		wps->cred_cb(wps->cb_ctx, &local_cred);
+	}
+
+	return 0;
+}
+
+
+#endif /* CONFIG_WPS_OOB */
+
+
+int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN])
+{
+	const char *pos;
+
+	/* <categ>-<OUI>-<subcateg> */
+	WPA_PUT_BE16(dev_type, atoi(str));
+	pos = os_strchr(str, '-');
+	if (pos == NULL)
+		return -1;
+	pos++;
+	if (hexstr2bin(pos, &dev_type[2], 4))
+		return -1;
+	pos = os_strchr(pos, '-');
+	if (pos == NULL)
+		return -1;
+	pos++;
+	WPA_PUT_BE16(&dev_type[6], atoi(pos));
+
+
+	return 0;
+}
+
+
+char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
+			    size_t buf_len)
+{
+	int ret;
+
+	ret = os_snprintf(buf, buf_len, "%u-%08X-%u",
+			  WPA_GET_BE16(dev_type), WPA_GET_BE32(&dev_type[2]),
+			  WPA_GET_BE16(&dev_type[6]));
+	if (os_snprintf_error(buf_len, ret))
+		return NULL;
+
+	return buf;
+}
+
+
+void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid)
+{
+	const u8 *addr[2];
+	size_t len[2];
+	u8 hash[SHA1_MAC_LEN];
+	u8 nsid[16] = {
+		0x52, 0x64, 0x80, 0xf8,
+		0xc9, 0x9b,
+		0x4b, 0xe5,
+		0xa6, 0x55,
+		0x58, 0xed, 0x5f, 0x5d, 0x60, 0x84
+	};
+
+	addr[0] = nsid;
+	len[0] = sizeof(nsid);
+	addr[1] = mac_addr;
+	len[1] = 6;
+	sha1_vector(2, addr, len, hash);
+	os_memcpy(uuid, hash, 16);
+
+	/* Version: 5 = named-based version using SHA-1 */
+	uuid[6] = (5 << 4) | (uuid[6] & 0x0f);
+
+	/* Variant specified in RFC 4122 */
+	uuid[8] = 0x80 | (uuid[8] & 0x3f);
+}
+
+
+u16 wps_config_methods_str2bin(const char *str)
+{
+	u16 methods = 0;
+
+	if (str == NULL || str[0] == '\0') {
+		/* Default to enabling methods based on build configuration */
+		methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+		methods |= WPS_CONFIG_VIRT_DISPLAY;
+#ifdef CONFIG_WPS_NFC
+		methods |= WPS_CONFIG_NFC_INTERFACE;
+#endif /* CONFIG_WPS_NFC */
+#ifdef CONFIG_P2P
+		methods |= WPS_CONFIG_P2PS;
+#endif /* CONFIG_P2P */
+	} else {
+		if (os_strstr(str, "ethernet"))
+			methods |= WPS_CONFIG_ETHERNET;
+		if (os_strstr(str, "label"))
+			methods |= WPS_CONFIG_LABEL;
+		if (os_strstr(str, "display"))
+			methods |= WPS_CONFIG_DISPLAY;
+		if (os_strstr(str, "ext_nfc_token"))
+			methods |= WPS_CONFIG_EXT_NFC_TOKEN;
+		if (os_strstr(str, "int_nfc_token"))
+			methods |= WPS_CONFIG_INT_NFC_TOKEN;
+		if (os_strstr(str, "nfc_interface"))
+			methods |= WPS_CONFIG_NFC_INTERFACE;
+		if (os_strstr(str, "push_button"))
+			methods |= WPS_CONFIG_PUSHBUTTON;
+		if (os_strstr(str, "keypad"))
+			methods |= WPS_CONFIG_KEYPAD;
+		if (os_strstr(str, "virtual_display"))
+			methods |= WPS_CONFIG_VIRT_DISPLAY;
+		if (os_strstr(str, "physical_display"))
+			methods |= WPS_CONFIG_PHY_DISPLAY;
+		if (os_strstr(str, "virtual_push_button"))
+			methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+		if (os_strstr(str, "physical_push_button"))
+			methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+		if (os_strstr(str, "p2ps"))
+			methods |= WPS_CONFIG_P2PS;
+	}
+
+	return methods;
+}
+
+
+struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_WSC_ACK) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_registrar_nonce(wps, msg) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_WSC_NACK) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_registrar_nonce(wps, msg) ||
+	    wps_build_config_error(msg, wps->config_error) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey,
+				    struct wpabuf *dev_pw)
+{
+	struct wpabuf *ret;
+
+	if (pubkey == NULL || dev_pw == NULL)
+		return NULL;
+
+	ret = wps_build_nfc_pw_token(id, pubkey, dev_pw);
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+}
+
+
+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 *pw;
+	u16 val;
+
+	pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
+	if (pw == NULL)
+		return NULL;
+
+	if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN),
+			     WPS_OOB_DEVICE_PASSWORD_LEN) ||
+	    random_get_bytes((u8 *) &val, sizeof(val))) {
+		wpabuf_free(pw);
+		return NULL;
+	}
+
+	if (wps_nfc_gen_dh(pubkey, privkey) < 0) {
+		wpabuf_free(pw);
+		return NULL;
+	}
+
+	*id = 0x10 + val % 0xfff0;
+	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 if (mode == HOSTAPD_MODE_IEEE80211AD)
+		rf_band = WPS_RF_60GHZ;
+	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/hostap/src/wps/wps_defs.h b/hostap/src/wps/wps_defs.h
new file mode 100644
index 0000000..a23b979
--- /dev/null
+++ b/hostap/src/wps/wps_defs.h
@@ -0,0 +1,379 @@
+/*
+ * Wi-Fi Protected Setup - message definitions
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_DEFS_H
+#define WPS_DEFS_H
+
+#ifdef CONFIG_WPS_TESTING
+
+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 */
+
+#define WPS_VERSION 0x20
+
+#endif /* CONFIG_WPS_TESTING */
+
+/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */
+#define WPS_DH_GROUP 5
+
+#define WPS_UUID_LEN 16
+#define WPS_NONCE_LEN 16
+#define WPS_AUTHENTICATOR_LEN 8
+#define WPS_AUTHKEY_LEN 32
+#define WPS_KEYWRAPKEY_LEN 16
+#define WPS_EMSK_LEN 32
+#define WPS_PSK_LEN 16
+#define WPS_SECRET_NONCE_LEN 16
+#define WPS_HASH_LEN 32
+#define WPS_KWA_LEN 8
+#define WPS_MGMTAUTHKEY_LEN 32
+#define WPS_MGMTENCKEY_LEN 16
+#define WPS_MGMT_KEY_ID_LEN 16
+#define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16
+#define WPS_OOB_DEVICE_PASSWORD_LEN 32
+#define WPS_OOB_PUBKEY_HASH_LEN 20
+#define WPS_DEV_NAME_MAX_LEN 32
+#define WPS_MANUFACTURER_MAX_LEN 64
+#define WPS_MODEL_NAME_MAX_LEN 32
+#define WPS_MODEL_NUMBER_MAX_LEN 32
+#define WPS_SERIAL_NUMBER_MAX_LEN 32
+
+/* Attribute Types */
+enum wps_attribute {
+	ATTR_AP_CHANNEL = 0x1001,
+	ATTR_ASSOC_STATE = 0x1002,
+	ATTR_AUTH_TYPE = 0x1003,
+	ATTR_AUTH_TYPE_FLAGS = 0x1004,
+	ATTR_AUTHENTICATOR = 0x1005,
+	ATTR_CONFIG_METHODS = 0x1008,
+	ATTR_CONFIG_ERROR = 0x1009,
+	ATTR_CONFIRM_URL4 = 0x100a,
+	ATTR_CONFIRM_URL6 = 0x100b,
+	ATTR_CONN_TYPE = 0x100c,
+	ATTR_CONN_TYPE_FLAGS = 0x100d,
+	ATTR_CRED = 0x100e,
+	ATTR_ENCR_TYPE = 0x100f,
+	ATTR_ENCR_TYPE_FLAGS = 0x1010,
+	ATTR_DEV_NAME = 0x1011,
+	ATTR_DEV_PASSWORD_ID = 0x1012,
+	ATTR_E_HASH1 = 0x1014,
+	ATTR_E_HASH2 = 0x1015,
+	ATTR_E_SNONCE1 = 0x1016,
+	ATTR_E_SNONCE2 = 0x1017,
+	ATTR_ENCR_SETTINGS = 0x1018,
+	ATTR_ENROLLEE_NONCE = 0x101a,
+	ATTR_FEATURE_ID = 0x101b,
+	ATTR_IDENTITY = 0x101c,
+	ATTR_IDENTITY_PROOF = 0x101d,
+	ATTR_KEY_WRAP_AUTH = 0x101e,
+	ATTR_KEY_ID = 0x101f,
+	ATTR_MAC_ADDR = 0x1020,
+	ATTR_MANUFACTURER = 0x1021,
+	ATTR_MSG_TYPE = 0x1022,
+	ATTR_MODEL_NAME = 0x1023,
+	ATTR_MODEL_NUMBER = 0x1024,
+	ATTR_NETWORK_INDEX = 0x1026,
+	ATTR_NETWORK_KEY = 0x1027,
+	ATTR_NETWORK_KEY_INDEX = 0x1028,
+	ATTR_NEW_DEVICE_NAME = 0x1029,
+	ATTR_NEW_PASSWORD = 0x102a,
+	ATTR_OOB_DEVICE_PASSWORD = 0x102c,
+	ATTR_OS_VERSION = 0x102d,
+	ATTR_POWER_LEVEL = 0x102f,
+	ATTR_PSK_CURRENT = 0x1030,
+	ATTR_PSK_MAX = 0x1031,
+	ATTR_PUBLIC_KEY = 0x1032,
+	ATTR_RADIO_ENABLE = 0x1033,
+	ATTR_REBOOT = 0x1034,
+	ATTR_REGISTRAR_CURRENT = 0x1035,
+	ATTR_REGISTRAR_ESTABLISHED = 0x1036,
+	ATTR_REGISTRAR_LIST = 0x1037,
+	ATTR_REGISTRAR_MAX = 0x1038,
+	ATTR_REGISTRAR_NONCE = 0x1039,
+	ATTR_REQUEST_TYPE = 0x103a,
+	ATTR_RESPONSE_TYPE = 0x103b,
+	ATTR_RF_BANDS = 0x103c,
+	ATTR_R_HASH1 = 0x103d,
+	ATTR_R_HASH2 = 0x103e,
+	ATTR_R_SNONCE1 = 0x103f,
+	ATTR_R_SNONCE2 = 0x1040,
+	ATTR_SELECTED_REGISTRAR = 0x1041,
+	ATTR_SERIAL_NUMBER = 0x1042,
+	ATTR_WPS_STATE = 0x1044,
+	ATTR_SSID = 0x1045,
+	ATTR_TOTAL_NETWORKS = 0x1046,
+	ATTR_UUID_E = 0x1047,
+	ATTR_UUID_R = 0x1048,
+	ATTR_VENDOR_EXT = 0x1049,
+	ATTR_VERSION = 0x104a,
+	ATTR_X509_CERT_REQ = 0x104b,
+	ATTR_X509_CERT = 0x104c,
+	ATTR_EAP_IDENTITY = 0x104d,
+	ATTR_MSG_COUNTER = 0x104e,
+	ATTR_PUBKEY_HASH = 0x104f,
+	ATTR_REKEY_KEY = 0x1050,
+	ATTR_KEY_LIFETIME = 0x1051,
+	ATTR_PERMITTED_CFG_METHODS = 0x1052,
+	ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053,
+	ATTR_PRIMARY_DEV_TYPE = 0x1054,
+	ATTR_SECONDARY_DEV_TYPE_LIST = 0x1055,
+	ATTR_PORTABLE_DEV = 0x1056,
+	ATTR_AP_SETUP_LOCKED = 0x1057,
+	ATTR_APPLICATION_EXT = 0x1058,
+	ATTR_EAP_TYPE = 0x1059,
+	ATTR_IV = 0x1060,
+	ATTR_KEY_PROVIDED_AUTO = 0x1061,
+	ATTR_802_1X_ENABLED = 0x1062,
+	ATTR_APPSESSIONKEY = 0x1063,
+	ATTR_WEPTRANSMITKEY = 0x1064,
+	ATTR_REQUESTED_DEV_TYPE = 0x106a,
+	ATTR_EXTENSIBILITY_TEST = 0x10fa /* _NOT_ defined in the spec */
+};
+
+#define WPS_VENDOR_ID_WFA 14122
+
+/* WFA Vendor Extension subelements */
+enum {
+	WFA_ELEM_VERSION2 = 0x00,
+	WFA_ELEM_AUTHORIZEDMACS = 0x01,
+	WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
+	WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
+	WFA_ELEM_SETTINGS_DELAY_TIME = 0x04,
+	WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05
+};
+
+/* Device Password ID */
+enum wps_dev_password_id {
+	DEV_PW_DEFAULT = 0x0000,
+	DEV_PW_USER_SPECIFIED = 0x0001,
+	DEV_PW_MACHINE_SPECIFIED = 0x0002,
+	DEV_PW_REKEY = 0x0003,
+	DEV_PW_PUSHBUTTON = 0x0004,
+	DEV_PW_REGISTRAR_SPECIFIED = 0x0005,
+	DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007,
+	DEV_PW_P2PS_DEFAULT = 0x0008
+};
+
+/* Message Type */
+enum wps_msg_type {
+	WPS_Beacon = 0x01,
+	WPS_ProbeRequest = 0x02,
+	WPS_ProbeResponse = 0x03,
+	WPS_M1 = 0x04,
+	WPS_M2 = 0x05,
+	WPS_M2D = 0x06,
+	WPS_M3 = 0x07,
+	WPS_M4 = 0x08,
+	WPS_M5 = 0x09,
+	WPS_M6 = 0x0a,
+	WPS_M7 = 0x0b,
+	WPS_M8 = 0x0c,
+	WPS_WSC_ACK = 0x0d,
+	WPS_WSC_NACK = 0x0e,
+	WPS_WSC_DONE = 0x0f
+};
+
+/* Authentication Type Flags */
+#define WPS_AUTH_OPEN 0x0001
+#define WPS_AUTH_WPAPSK 0x0002
+#define WPS_AUTH_SHARED 0x0004 /* deprecated */
+#define WPS_AUTH_WPA 0x0008
+#define WPS_AUTH_WPA2 0x0010
+#define WPS_AUTH_WPA2PSK 0x0020
+#define WPS_AUTH_TYPES (WPS_AUTH_OPEN | WPS_AUTH_WPAPSK | WPS_AUTH_SHARED | \
+			WPS_AUTH_WPA | WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)
+
+/* Encryption Type Flags */
+#define WPS_ENCR_NONE 0x0001
+#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 | \
+			WPS_ENCR_AES)
+
+/* Configuration Error */
+enum wps_config_error {
+	WPS_CFG_NO_ERROR = 0,
+	WPS_CFG_OOB_IFACE_READ_ERROR = 1,
+	WPS_CFG_DECRYPTION_CRC_FAILURE = 2,
+	WPS_CFG_24_CHAN_NOT_SUPPORTED = 3,
+	WPS_CFG_50_CHAN_NOT_SUPPORTED = 4,
+	WPS_CFG_SIGNAL_TOO_WEAK = 5,
+	WPS_CFG_NETWORK_AUTH_FAILURE = 6,
+	WPS_CFG_NETWORK_ASSOC_FAILURE = 7,
+	WPS_CFG_NO_DHCP_RESPONSE = 8,
+	WPS_CFG_FAILED_DHCP_CONFIG = 9,
+	WPS_CFG_IP_ADDR_CONFLICT = 10,
+	WPS_CFG_NO_CONN_TO_REGISTRAR = 11,
+	WPS_CFG_MULTIPLE_PBC_DETECTED = 12,
+	WPS_CFG_ROGUE_SUSPECTED = 13,
+	WPS_CFG_DEVICE_BUSY = 14,
+	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_60G_CHAN_NOT_SUPPORTED = 19,
+	WPS_CFG_PUBLIC_KEY_HASH_MISMATCH = 20
+};
+
+/* Vendor specific Error Indication for WPS event messages */
+enum wps_error_indication {
+	WPS_EI_NO_ERROR,
+	WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED,
+	WPS_EI_SECURITY_WEP_PROHIBITED,
+	WPS_EI_AUTH_FAILURE,
+	NUM_WPS_EI_VALUES
+};
+
+/* RF Bands */
+#define WPS_RF_24GHZ 0x01
+#define WPS_RF_50GHZ 0x02
+#define WPS_RF_60GHZ 0x04
+
+/* Config Methods */
+#define WPS_CONFIG_USBA 0x0001
+#define WPS_CONFIG_ETHERNET 0x0002
+#define WPS_CONFIG_LABEL 0x0004
+#define WPS_CONFIG_DISPLAY 0x0008
+#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010
+#define WPS_CONFIG_INT_NFC_TOKEN 0x0020
+#define WPS_CONFIG_NFC_INTERFACE 0x0040
+#define WPS_CONFIG_PUSHBUTTON 0x0080
+#define WPS_CONFIG_KEYPAD 0x0100
+#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
+#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
+#define WPS_CONFIG_P2PS 0x1000
+#define WPS_CONFIG_VIRT_DISPLAY 0x2008
+#define WPS_CONFIG_PHY_DISPLAY 0x4008
+
+/* Connection Type Flags */
+#define WPS_CONN_ESS 0x01
+#define WPS_CONN_IBSS 0x02
+
+/* Wi-Fi Protected Setup State */
+enum wps_state {
+	WPS_STATE_NOT_CONFIGURED = 1,
+	WPS_STATE_CONFIGURED = 2
+};
+
+/* Association State */
+enum wps_assoc_state {
+	WPS_ASSOC_NOT_ASSOC = 0,
+	WPS_ASSOC_CONN_SUCCESS = 1,
+	WPS_ASSOC_CFG_FAILURE = 2,
+	WPS_ASSOC_FAILURE = 3,
+	WPS_ASSOC_IP_FAILURE = 4
+};
+
+
+#define WPS_DEV_OUI_WFA 0x0050f204
+
+enum wps_dev_categ {
+	WPS_DEV_COMPUTER = 1,
+	WPS_DEV_INPUT = 2,
+	WPS_DEV_PRINTER = 3,
+	WPS_DEV_CAMERA = 4,
+	WPS_DEV_STORAGE = 5,
+	WPS_DEV_NETWORK_INFRA = 6,
+	WPS_DEV_DISPLAY = 7,
+	WPS_DEV_MULTIMEDIA = 8,
+	WPS_DEV_GAMING = 9,
+	WPS_DEV_PHONE = 10,
+	WPS_DEV_AUDIO = 11,
+};
+
+enum wps_dev_subcateg {
+	WPS_DEV_COMPUTER_PC = 1,
+	WPS_DEV_COMPUTER_SERVER = 2,
+	WPS_DEV_COMPUTER_MEDIA_CENTER = 3,
+	WPS_DEV_COMPUTER_ULTRA_MOBILE = 4,
+	WPS_DEV_COMPUTER_NOTEBOOK = 5,
+	WPS_DEV_COMPUTER_DESKTOP = 6,
+	WPS_DEV_COMPUTER_MID = 7,
+	WPS_DEV_COMPUTER_NETBOOK = 8,
+	WPS_DEV_COMPUTER_TABLET = 9,
+	WPS_DEV_INPUT_KEYBOARD = 1,
+	WPS_DEV_INPUT_MOUSE = 2,
+	WPS_DEV_INPUT_JOYSTICK = 3,
+	WPS_DEV_INPUT_TRACKBALL = 4,
+	WPS_DEV_INPUT_GAMING = 5,
+	WPS_DEV_INPUT_REMOTE = 6,
+	WPS_DEV_INPUT_TOUCHSCREEN = 7,
+	WPS_DEV_INPUT_BIOMETRIC_READER = 8,
+	WPS_DEV_INPUT_BARCODE_READER = 9,
+	WPS_DEV_PRINTER_PRINTER = 1,
+	WPS_DEV_PRINTER_SCANNER = 2,
+	WPS_DEV_PRINTER_FAX = 3,
+	WPS_DEV_PRINTER_COPIER = 4,
+	WPS_DEV_PRINTER_ALL_IN_ONE = 5,
+	WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1,
+	WPS_DEV_CAMERA_VIDEO = 2,
+	WPS_DEV_CAMERA_WEB = 3,
+	WPS_DEV_CAMERA_SECURITY = 4,
+	WPS_DEV_STORAGE_NAS = 1,
+	WPS_DEV_NETWORK_INFRA_AP = 1,
+	WPS_DEV_NETWORK_INFRA_ROUTER = 2,
+	WPS_DEV_NETWORK_INFRA_SWITCH = 3,
+	WPS_DEV_NETWORK_INFRA_GATEWAY = 4,
+	WPS_DEV_NETWORK_INFRA_BRIDGE = 5,
+	WPS_DEV_DISPLAY_TV = 1,
+	WPS_DEV_DISPLAY_PICTURE_FRAME = 2,
+	WPS_DEV_DISPLAY_PROJECTOR = 3,
+	WPS_DEV_DISPLAY_MONITOR = 4,
+	WPS_DEV_MULTIMEDIA_DAR = 1,
+	WPS_DEV_MULTIMEDIA_PVR = 2,
+	WPS_DEV_MULTIMEDIA_MCX = 3,
+	WPS_DEV_MULTIMEDIA_SET_TOP_BOX = 4,
+	WPS_DEV_MULTIMEDIA_MEDIA_SERVER = 5,
+	WPS_DEV_MULTIMEDIA_PORTABLE_VIDEO_PLAYER = 6,
+	WPS_DEV_GAMING_XBOX = 1,
+	WPS_DEV_GAMING_XBOX360 = 2,
+	WPS_DEV_GAMING_PLAYSTATION = 3,
+	WPS_DEV_GAMING_GAME_CONSOLE = 4,
+	WPS_DEV_GAMING_PORTABLE_DEVICE = 5,
+	WPS_DEV_PHONE_WINDOWS_MOBILE = 1,
+	WPS_DEV_PHONE_SINGLE_MODE = 2,
+	WPS_DEV_PHONE_DUAL_MODE = 3,
+	WPS_DEV_PHONE_SP_SINGLE_MODE = 4,
+	WPS_DEV_PHONE_SP_DUAL_MODE = 5,
+	WPS_DEV_AUDIO_TUNER_RECV = 1,
+	WPS_DEV_AUDIO_SPEAKERS = 2,
+	WPS_DEV_AUDIO_PMP = 3,
+	WPS_DEV_AUDIO_HEADSET = 4,
+	WPS_DEV_AUDIO_HEADPHONES = 5,
+	WPS_DEV_AUDIO_MICROPHONE = 6,
+	WPS_DEV_AUDIO_HOME_THEATRE = 7,
+};
+
+
+/* Request Type */
+enum wps_request_type {
+	WPS_REQ_ENROLLEE_INFO = 0,
+	WPS_REQ_ENROLLEE = 1,
+	WPS_REQ_REGISTRAR = 2,
+	WPS_REQ_WLAN_MANAGER_REGISTRAR = 3
+};
+
+/* Response Type */
+enum wps_response_type {
+	WPS_RESP_ENROLLEE_INFO = 0,
+	WPS_RESP_ENROLLEE = 1,
+	WPS_RESP_REGISTRAR = 2,
+	WPS_RESP_AP = 3
+};
+
+/* Walk Time for push button configuration (in seconds) */
+#define WPS_PBC_WALK_TIME 120
+
+#define WPS_MAX_AUTHORIZED_MACS 5
+
+#endif /* WPS_DEFS_H */
diff --git a/hostap/src/wps/wps_dev_attr.c b/hostap/src/wps/wps_dev_attr.c
new file mode 100644
index 0000000..0d01211
--- /dev/null
+++ b/hostap/src/wps/wps_dev_attr.c
@@ -0,0 +1,419 @@
+/*
+ * Wi-Fi Protected Setup - device attributes
+ * Copyright (c) 2008, 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 "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	size_t len;
+	wpa_printf(MSG_DEBUG, "WPS:  * Manufacturer");
+	wpabuf_put_be16(msg, ATTR_MANUFACTURER);
+	len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0;
+#ifndef CONFIG_WPS_STRICT
+	if (len == 0) {
+		/*
+		 * Some deployed WPS implementations fail to parse zero-length
+		 * attributes. As a workaround, send a space character if the
+		 * device attribute string is empty.
+		 */
+		wpabuf_put_be16(msg, 1);
+		wpabuf_put_u8(msg, ' ');
+		return 0;
+	}
+#endif /* CONFIG_WPS_STRICT */
+	wpabuf_put_be16(msg, len);
+	wpabuf_put_data(msg, dev->manufacturer, len);
+	return 0;
+}
+
+
+int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	size_t len;
+	wpa_printf(MSG_DEBUG, "WPS:  * Model Name");
+	wpabuf_put_be16(msg, ATTR_MODEL_NAME);
+	len = dev->model_name ? os_strlen(dev->model_name) : 0;
+#ifndef CONFIG_WPS_STRICT
+	if (len == 0) {
+		/*
+		 * Some deployed WPS implementations fail to parse zero-length
+		 * attributes. As a workaround, send a space character if the
+		 * device attribute string is empty.
+		 */
+		wpabuf_put_be16(msg, 1);
+		wpabuf_put_u8(msg, ' ');
+		return 0;
+	}
+#endif /* CONFIG_WPS_STRICT */
+	wpabuf_put_be16(msg, len);
+	wpabuf_put_data(msg, dev->model_name, len);
+	return 0;
+}
+
+
+int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	size_t len;
+	wpa_printf(MSG_DEBUG, "WPS:  * Model Number");
+	wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
+	len = dev->model_number ? os_strlen(dev->model_number) : 0;
+#ifndef CONFIG_WPS_STRICT
+	if (len == 0) {
+		/*
+		 * Some deployed WPS implementations fail to parse zero-length
+		 * attributes. As a workaround, send a space character if the
+		 * device attribute string is empty.
+		 */
+		wpabuf_put_be16(msg, 1);
+		wpabuf_put_u8(msg, ' ');
+		return 0;
+	}
+#endif /* CONFIG_WPS_STRICT */
+	wpabuf_put_be16(msg, len);
+	wpabuf_put_data(msg, dev->model_number, len);
+	return 0;
+}
+
+
+int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	size_t len;
+	wpa_printf(MSG_DEBUG, "WPS:  * Serial Number");
+	wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
+	len = dev->serial_number ? os_strlen(dev->serial_number) : 0;
+#ifndef CONFIG_WPS_STRICT
+	if (len == 0) {
+		/*
+		 * Some deployed WPS implementations fail to parse zero-length
+		 * attributes. As a workaround, send a space character if the
+		 * device attribute string is empty.
+		 */
+		wpabuf_put_be16(msg, 1);
+		wpabuf_put_u8(msg, ' ');
+		return 0;
+	}
+#endif /* CONFIG_WPS_STRICT */
+	wpabuf_put_be16(msg, len);
+	wpabuf_put_data(msg, dev->serial_number, len);
+	return 0;
+}
+
+
+int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Primary Device Type");
+	wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE);
+	wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
+	wpabuf_put_data(msg, dev->pri_dev_type, WPS_DEV_TYPE_LEN);
+	return 0;
+}
+
+
+int wps_build_secondary_dev_type(struct wps_device_data *dev,
+				  struct wpabuf *msg)
+{
+	if (!dev->num_sec_dev_types)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Secondary Device Type");
+	wpabuf_put_be16(msg, ATTR_SECONDARY_DEV_TYPE_LIST);
+	wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
+	wpabuf_put_data(msg, dev->sec_dev_type,
+			WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
+
+	return 0;
+}
+
+
+int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
+			   unsigned int num_req_dev_types,
+			   const u8 *req_dev_types)
+{
+	unsigned int i;
+
+	for (i = 0; i < num_req_dev_types; i++) {
+		wpa_hexdump(MSG_DEBUG, "WPS: * Requested Device Type",
+			    req_dev_types + i * WPS_DEV_TYPE_LEN,
+			    WPS_DEV_TYPE_LEN);
+		wpabuf_put_be16(msg, ATTR_REQUESTED_DEV_TYPE);
+		wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
+		wpabuf_put_data(msg, req_dev_types + i * WPS_DEV_TYPE_LEN,
+				WPS_DEV_TYPE_LEN);
+	}
+
+	return 0;
+}
+
+
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	size_t len;
+	wpa_printf(MSG_DEBUG, "WPS:  * Device Name");
+	wpabuf_put_be16(msg, ATTR_DEV_NAME);
+	len = dev->device_name ? os_strlen(dev->device_name) : 0;
+#ifndef CONFIG_WPS_STRICT
+	if (len == 0) {
+		/*
+		 * Some deployed WPS implementations fail to parse zero-length
+		 * attributes. As a workaround, send a space character if the
+		 * device attribute string is empty.
+		 */
+		wpabuf_put_be16(msg, 1);
+		wpabuf_put_u8(msg, ' ');
+		return 0;
+	}
+#endif /* CONFIG_WPS_STRICT */
+	wpabuf_put_be16(msg, len);
+	wpabuf_put_data(msg, dev->device_name, len);
+	return 0;
+}
+
+
+int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	if (wps_build_manufacturer(dev, msg) ||
+	    wps_build_model_name(dev, msg) ||
+	    wps_build_model_number(dev, msg) ||
+	    wps_build_serial_number(dev, msg) ||
+	    wps_build_primary_dev_type(dev, msg) ||
+	    wps_build_dev_name(dev, msg))
+		return -1;
+	return 0;
+}
+
+
+int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * OS Version");
+	wpabuf_put_be16(msg, ATTR_OS_VERSION);
+	wpabuf_put_be16(msg, 4);
+	wpabuf_put_be32(msg, 0x80000000 | dev->os_version);
+	return 0;
+}
+
+
+int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	if (dev->vendor_ext_m1 != NULL) {
+		wpa_hexdump(MSG_DEBUG, "WPS:  * Vendor Extension M1",
+			    wpabuf_head_u8(dev->vendor_ext_m1),
+			    wpabuf_len(dev->vendor_ext_m1));
+		wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+		wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext_m1));
+		wpabuf_put_buf(msg, dev->vendor_ext_m1);
+	}
+	return 0;
+}
+
+
+int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg,
+		       u8 rf_band)
+{
+	return wps_build_rf_bands_attr(msg, rf_band ? rf_band : dev->rf_bands);
+}
+
+
+int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	int i;
+
+	for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+		if (dev->vendor_ext[i] == NULL)
+			continue;
+		wpa_hexdump(MSG_DEBUG, "WPS:  * Vendor Extension",
+			    wpabuf_head_u8(dev->vendor_ext[i]),
+			    wpabuf_len(dev->vendor_ext[i]));
+		wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+		wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext[i]));
+		wpabuf_put_buf(msg, dev->vendor_ext[i]);
+	}
+
+	return 0;
+}
+
+
+static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str,
+				    size_t str_len)
+{
+	if (str == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Manufacturer received");
+		return -1;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len);
+
+	os_free(dev->manufacturer);
+	dev->manufacturer = dup_binstr(str, str_len);
+	if (dev->manufacturer == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+static int wps_process_model_name(struct wps_device_data *dev, const u8 *str,
+				  size_t str_len)
+{
+	if (str == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Model Name received");
+		return -1;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len);
+
+	os_free(dev->model_name);
+	dev->model_name = dup_binstr(str, str_len);
+	if (dev->model_name == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+static int wps_process_model_number(struct wps_device_data *dev, const u8 *str,
+				    size_t str_len)
+{
+	if (str == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Model Number received");
+		return -1;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len);
+
+	os_free(dev->model_number);
+	dev->model_number = dup_binstr(str, str_len);
+	if (dev->model_number == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+static int wps_process_serial_number(struct wps_device_data *dev,
+				     const u8 *str, size_t str_len)
+{
+	if (str == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Serial Number received");
+		return -1;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len);
+
+	os_free(dev->serial_number);
+	dev->serial_number = dup_binstr(str, str_len);
+	if (dev->serial_number == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+static int wps_process_dev_name(struct wps_device_data *dev, const u8 *str,
+				size_t str_len)
+{
+	if (str == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Device Name received");
+		return -1;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len);
+
+	os_free(dev->device_name);
+	dev->device_name = dup_binstr(str, str_len);
+	if (dev->device_name == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+static int wps_process_primary_dev_type(struct wps_device_data *dev,
+					const u8 *dev_type)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+	char devtype[WPS_DEV_TYPE_BUFSIZE];
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+	if (dev_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Primary Device Type received");
+		return -1;
+	}
+
+	os_memcpy(dev->pri_dev_type, dev_type, WPS_DEV_TYPE_LEN);
+	wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: %s",
+		   wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+					sizeof(devtype)));
+
+	return 0;
+}
+
+
+int wps_process_device_attrs(struct wps_device_data *dev,
+			     struct wps_parse_attr *attr)
+{
+	if (wps_process_manufacturer(dev, attr->manufacturer,
+				     attr->manufacturer_len) ||
+	    wps_process_model_name(dev, attr->model_name,
+				   attr->model_name_len) ||
+	    wps_process_model_number(dev, attr->model_number,
+				     attr->model_number_len) ||
+	    wps_process_serial_number(dev, attr->serial_number,
+				      attr->serial_number_len) ||
+	    wps_process_primary_dev_type(dev, attr->primary_dev_type) ||
+	    wps_process_dev_name(dev, attr->dev_name, attr->dev_name_len))
+		return -1;
+	return 0;
+}
+
+
+int wps_process_os_version(struct wps_device_data *dev, const u8 *ver)
+{
+	if (ver == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No OS Version received");
+		return -1;
+	}
+
+	dev->os_version = WPA_GET_BE32(ver);
+	wpa_printf(MSG_DEBUG, "WPS: OS Version %08x", dev->os_version);
+
+	return 0;
+}
+
+
+int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands)
+{
+	if (bands == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No RF Bands received");
+		return -1;
+	}
+
+	dev->rf_bands = *bands;
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee RF Bands 0x%x", dev->rf_bands);
+
+	return 0;
+}
+
+
+void wps_device_data_free(struct wps_device_data *dev)
+{
+	os_free(dev->device_name);
+	dev->device_name = NULL;
+	os_free(dev->manufacturer);
+	dev->manufacturer = NULL;
+	os_free(dev->model_name);
+	dev->model_name = NULL;
+	os_free(dev->model_number);
+	dev->model_number = NULL;
+	os_free(dev->serial_number);
+	dev->serial_number = NULL;
+}
diff --git a/hostap/src/wps/wps_dev_attr.h b/hostap/src/wps/wps_dev_attr.h
new file mode 100644
index 0000000..c9034ad
--- /dev/null
+++ b/hostap/src/wps/wps_dev_attr.h
@@ -0,0 +1,39 @@
+/*
+ * Wi-Fi Protected Setup - device attributes
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_DEV_ATTR_H
+#define WPS_DEV_ATTR_H
+
+struct wps_parse_attr;
+
+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);
+int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg,
+		       u8 rf_band);
+int wps_build_primary_dev_type(struct wps_device_data *dev,
+			       struct wpabuf *msg);
+int wps_build_secondary_dev_type(struct wps_device_data *dev,
+				 struct wpabuf *msg);
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_process_device_attrs(struct wps_device_data *dev,
+			     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_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,
+			   unsigned int num_req_dev_types,
+			   const u8 *req_dev_types);
+
+#endif /* WPS_DEV_ATTR_H */
diff --git a/hostap/src/wps/wps_enrollee.c b/hostap/src/wps/wps_enrollee.c
new file mode 100644
index 0000000..9321b72
--- /dev/null
+++ b/hostap/src/wps/wps_enrollee.c
@@ -0,0 +1,1508 @@
+/*
+ * Wi-Fi Protected Setup - Enrollee
+ * Copyright (c) 2008, 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/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg)
+{
+	u8 state;
+	if (wps->wps->ap)
+		state = wps->wps->wps_state;
+	else
+		state = WPS_STATE_NOT_CONFIGURED;
+	wpa_printf(MSG_DEBUG, "WPS:  * Wi-Fi Protected Setup State (%d)",
+		   state);
+	wpabuf_put_be16(msg, ATTR_WPS_STATE);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, state);
+	return 0;
+}
+
+
+static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
+{
+	u8 *hash;
+	const u8 *addr[4];
+	size_t len[4];
+
+	if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+		return -1;
+	wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
+		    wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
+
+	if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
+			   "E-Hash derivation");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS:  * E-Hash1");
+	wpabuf_put_be16(msg, ATTR_E_HASH1);
+	wpabuf_put_be16(msg, SHA256_MAC_LEN);
+	hash = wpabuf_put(msg, SHA256_MAC_LEN);
+	/* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
+	addr[0] = wps->snonce;
+	len[0] = WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk1;
+	len[1] = WPS_PSK_LEN;
+	addr[2] = wpabuf_head(wps->dh_pubkey_e);
+	len[2] = wpabuf_len(wps->dh_pubkey_e);
+	addr[3] = wpabuf_head(wps->dh_pubkey_r);
+	len[3] = wpabuf_len(wps->dh_pubkey_r);
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN);
+
+	wpa_printf(MSG_DEBUG, "WPS:  * E-Hash2");
+	wpabuf_put_be16(msg, ATTR_E_HASH2);
+	wpabuf_put_be16(msg, SHA256_MAC_LEN);
+	hash = wpabuf_put(msg, SHA256_MAC_LEN);
+	/* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
+	addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk2;
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN);
+
+	return 0;
+}
+
+
+static int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce1");
+	wpabuf_put_be16(msg, ATTR_E_SNONCE1);
+	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+	wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
+	return 0;
+}
+
+
+static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce2");
+	wpabuf_put_be16(msg, ATTR_E_SNONCE2);
+	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+	wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
+			WPS_SECRET_NONCE_LEN);
+	return 0;
+}
+
+
+static struct wpabuf * wps_build_m1(struct wps_data *wps)
+{
+	struct wpabuf *msg;
+	u16 config_methods;
+
+	if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
+		return NULL;
+	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
+		    wps->nonce_e, WPS_NONCE_LEN);
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M1");
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	config_methods = wps->wps->config_methods;
+	if (wps->wps->ap && !wps->pbc_in_m1 &&
+	    (wps->dev_password_len != 0 ||
+	     (config_methods & WPS_CONFIG_DISPLAY))) {
+		/*
+		 * These are the methods that the AP supports as an Enrollee
+		 * for adding external Registrars, so remove PushButton.
+		 *
+		 * As a workaround for Windows 7 mechanism for probing WPS
+		 * capabilities from M1, leave PushButton option if no PIN
+		 * method is available or if WPS configuration enables PBC
+		 * workaround.
+		 */
+		config_methods &= ~WPS_CONFIG_PUSHBUTTON;
+		config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+				    WPS_CONFIG_PHY_PUSHBUTTON);
+	}
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M1) ||
+	    wps_build_uuid_e(msg, wps->uuid_e) ||
+	    wps_build_mac_addr(msg, wps->mac_addr_e) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_public_key(wps, msg) ||
+	    wps_build_auth_type_flags(wps, msg) ||
+	    wps_build_encr_type_flags(wps, msg) ||
+	    wps_build_conn_type_flags(wps, msg) ||
+	    wps_build_config_methods(msg, config_methods) ||
+	    wps_build_wps_state(wps, msg) ||
+	    wps_build_device_attrs(&wps->wps->dev, msg) ||
+	    wps_build_rf_bands(&wps->wps->dev, msg,
+			       wps->wps->rf_band_cb(wps->wps->cb_ctx)) ||
+	    wps_build_assoc_state(wps, msg) ||
+	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
+	    wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
+	    wps_build_os_version(&wps->wps->dev, msg) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	wps->state = RECV_M2;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_m3(struct wps_data *wps)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M3");
+
+	if (wps->dev_password == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Device Password available");
+		return NULL;
+	}
+	wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+
+	if (wps->wps->ap && random_pool_ready() != 1) {
+		wpa_printf(MSG_INFO,
+			   "WPS: Not enough entropy in random pool to proceed - do not allow AP PIN to be used");
+		return NULL;
+	}
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M3) ||
+	    wps_build_registrar_nonce(wps, msg) ||
+	    wps_build_e_hash(wps, msg) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_authenticator(wps, msg)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	wps->state = RECV_M4;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_m5(struct wps_data *wps)
+{
+	struct wpabuf *msg, *plain;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M5");
+
+	plain = wpabuf_alloc(200);
+	if (plain == NULL)
+		return NULL;
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL) {
+		wpabuf_free(plain);
+		return NULL;
+	}
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M5) ||
+	    wps_build_registrar_nonce(wps, msg) ||
+	    wps_build_e_snonce1(wps, plain) ||
+	    wps_build_key_wrap_auth(wps, plain) ||
+	    wps_build_encr_settings(wps, msg, plain) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_authenticator(wps, msg)) {
+		wpabuf_free(plain);
+		wpabuf_free(msg);
+		return NULL;
+	}
+	wpabuf_free(plain);
+
+	wps->state = RECV_M6;
+	return msg;
+}
+
+
+static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * SSID");
+	wpabuf_put_be16(msg, ATTR_SSID);
+	wpabuf_put_be16(msg, wps->wps->ssid_len);
+	wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
+	return 0;
+}
+
+
+static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
+{
+	u16 auth_type = wps->wps->ap_auth_type;
+
+	/*
+	 * Work around issues with Windows 7 WPS implementation not liking
+	 * multiple Authentication Type bits in M7 AP Settings attribute by
+	 * showing only the most secure option from current configuration.
+	 */
+	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;
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)", auth_type);
+	wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, auth_type);
+	return 0;
+}
+
+
+static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
+{
+	u16 encr_type = wps->wps->ap_encr_type;
+
+	/*
+	 * Work around issues with Windows 7 WPS implementation not liking
+	 * multiple Encryption Type bits in M7 AP Settings attribute by
+	 * showing only the most secure option from current configuration.
+	 */
+	if (wps->wps->ap_auth_type & (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;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)", encr_type);
+	wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, encr_type);
+	return 0;
+}
+
+
+static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
+{
+	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_pool_ready() != 1 ||
+		    random_get_bytes(psk, sizeof(psk)) < 0) {
+			wpa_printf(MSG_INFO,
+				   "WPS: Could not generate random PSK");
+			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);
+	return 0;
+}
+
+
+static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * MAC Address (AP BSSID)");
+	wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+	wpabuf_put_be16(msg, ETH_ALEN);
+	wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN);
+	return 0;
+}
+
+
+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,
+				wps->wps->ap_settings_len);
+		return 0;
+	}
+
+	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;
+}
+
+
+static struct wpabuf * wps_build_m7(struct wps_data *wps)
+{
+	struct wpabuf *msg, *plain;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M7");
+
+	plain = wpabuf_alloc(500 + wps->wps->ap_settings_len);
+	if (plain == NULL)
+		return NULL;
+
+	msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len);
+	if (msg == NULL) {
+		wpabuf_free(plain);
+		return NULL;
+	}
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M7) ||
+	    wps_build_registrar_nonce(wps, msg) ||
+	    wps_build_e_snonce2(wps, plain) ||
+	    (wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
+	    wps_build_key_wrap_auth(wps, plain) ||
+	    wps_build_encr_settings(wps, msg, plain) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_authenticator(wps, msg)) {
+		wpabuf_free(plain);
+		wpabuf_free(msg);
+		return NULL;
+	}
+	wpabuf_free(plain);
+
+	if (wps->wps->ap && wps->wps->registrar) {
+		/*
+		 * If the Registrar is only learning our current configuration,
+		 * it may not continue protocol run to successful completion.
+		 * Store information here to make sure it remains available.
+		 */
+		wps_device_store(wps->wps->registrar, &wps->peer_dev,
+				 wps->uuid_r);
+	}
+
+	wps->state = RECV_M8;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done");
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_WSC_DONE) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_registrar_nonce(wps, msg) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	if (wps->wps->ap)
+		wps->state = RECV_ACK;
+	else {
+		wps_success_event(wps->wps, wps->peer_dev.mac_addr);
+		wps->state = WPS_FINISHED;
+	}
+	return msg;
+}
+
+
+struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
+				     enum wsc_op_code *op_code)
+{
+	struct wpabuf *msg;
+
+	switch (wps->state) {
+	case SEND_M1:
+		msg = wps_build_m1(wps);
+		*op_code = WSC_MSG;
+		break;
+	case SEND_M3:
+		msg = wps_build_m3(wps);
+		*op_code = WSC_MSG;
+		break;
+	case SEND_M5:
+		msg = wps_build_m5(wps);
+		*op_code = WSC_MSG;
+		break;
+	case SEND_M7:
+		msg = wps_build_m7(wps);
+		*op_code = WSC_MSG;
+		break;
+	case RECEIVED_M2D:
+		if (wps->wps->ap) {
+			msg = wps_build_wsc_nack(wps);
+			*op_code = WSC_NACK;
+			break;
+		}
+		msg = wps_build_wsc_ack(wps);
+		*op_code = WSC_ACK;
+		if (msg) {
+			/* Another M2/M2D may be received */
+			wps->state = RECV_M2;
+		}
+		break;
+	case SEND_WSC_NACK:
+		msg = wps_build_wsc_nack(wps);
+		*op_code = WSC_NACK;
+		break;
+	case WPS_MSG_DONE:
+		msg = wps_build_wsc_done(wps);
+		*op_code = WSC_Done;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
+			   "a message", wps->state);
+		msg = NULL;
+		break;
+	}
+
+	if (*op_code == WSC_MSG && msg) {
+		/* Save a copy of the last message for Authenticator derivation
+		 */
+		wpabuf_free(wps->last_msg);
+		wps->last_msg = wpabuf_dup(msg);
+	}
+
+	return msg;
+}
+
+
+static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
+{
+	if (r_nonce == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
+		return -1;
+	}
+
+	os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
+		    wps->nonce_r, WPS_NONCE_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
+{
+	if (e_nonce == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
+		return -1;
+	}
+
+	if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r)
+{
+	if (uuid_r == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No UUID-R received");
+		return -1;
+	}
+
+	os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
+			      size_t pk_len)
+{
+	if (pk == NULL || pk_len == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
+		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)
+		return -1;
+
+	if (wps_derive_keys(wps) < 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1)
+{
+	if (r_hash1 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received");
+		return -1;
+	}
+
+	os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2)
+{
+	if (r_hash2 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received");
+		return -1;
+	}
+
+	os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
+{
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+
+	if (r_snonce1 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received");
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1,
+			WPS_SECRET_NONCE_LEN);
+
+	/* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
+	addr[0] = r_snonce1;
+	len[0] = WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk1;
+	len[1] = WPS_PSK_LEN;
+	addr[2] = wpabuf_head(wps->dh_pubkey_e);
+	len[2] = wpabuf_len(wps->dh_pubkey_e);
+	addr[3] = wpabuf_head(wps->dh_pubkey_r);
+	len[3] = wpabuf_len(wps->dh_pubkey_r);
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+	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;
+		wps_pwd_auth_fail_event(wps->wps, 1, 1, wps->peer_dev.mac_addr);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first "
+		   "half of the device password");
+
+	return 0;
+}
+
+
+static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
+{
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+
+	if (r_snonce2 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received");
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2,
+			WPS_SECRET_NONCE_LEN);
+
+	/* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
+	addr[0] = r_snonce2;
+	len[0] = WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk2;
+	len[1] = WPS_PSK_LEN;
+	addr[2] = wpabuf_head(wps->dh_pubkey_e);
+	len[2] = wpabuf_len(wps->dh_pubkey_e);
+	addr[3] = wpabuf_head(wps->dh_pubkey_r);
+	len[3] = wpabuf_len(wps->dh_pubkey_r);
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+	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;
+		wps_pwd_auth_fail_event(wps->wps, 1, 2, wps->peer_dev.mac_addr);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second "
+		   "half of the device password");
+
+	return 0;
+}
+
+
+static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
+			      size_t cred_len, int wps2)
+{
+	struct wps_parse_attr attr;
+	struct wpabuf msg;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received Credential");
+	os_memset(&wps->cred, 0, sizeof(wps->cred));
+	wpabuf_set(&msg, cred, cred_len);
+	if (wps_parse_msg(&msg, &attr) < 0 ||
+	    wps_process_cred(&attr, &wps->cred))
+		return -1;
+
+	if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
+	    0) {
+		wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential ("
+			   MACSTR ") does not match with own address (" MACSTR
+			   ")", MAC2STR(wps->cred.mac_addr),
+			   MAC2STR(wps->wps->dev.mac_addr));
+		/*
+		 * In theory, this could be consider fatal error, but there are
+		 * number of deployed implementations using other address here
+		 * due to unclarity in the specification. For interoperability
+		 * reasons, allow this to be processed since we do not really
+		 * use the MAC Address information for anything.
+		 */
+#ifdef CONFIG_WPS_STRICT
+		if (wps2) {
+			wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
+				   "MAC Address in AP Settings");
+			return -1;
+		}
+#endif /* CONFIG_WPS_STRICT */
+	}
+
+	if (!(wps->cred.encr_type &
+	      (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
+		if (wps->cred.encr_type & WPS_ENCR_WEP) {
+			wpa_printf(MSG_INFO, "WPS: Reject Credential "
+				   "due to WEP configuration");
+			wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
+			return -2;
+		}
+
+		wpa_printf(MSG_INFO, "WPS: Reject Credential due to "
+			   "invalid encr_type 0x%x", wps->cred.encr_type);
+		return -1;
+	}
+
+	if (wps->wps->cred_cb) {
+		wps->cred.cred_attr = cred - 4;
+		wps->cred.cred_attr_len = cred_len + 4;
+		ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
+		wps->cred.cred_attr = NULL;
+		wps->cred.cred_attr_len = 0;
+	}
+
+	return ret;
+}
+
+
+static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
+			     u16 cred_len[], unsigned int num_cred, int wps2)
+{
+	size_t i;
+	int ok = 0;
+
+	if (wps->wps->ap)
+		return 0;
+
+	if (num_cred == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: No Credential attributes "
+			   "received");
+		return -1;
+	}
+
+	for (i = 0; i < num_cred; i++) {
+		int res;
+		res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2);
+		if (res == 0)
+			ok++;
+		else if (res == -2)
+			wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped");
+		else
+			return -1;
+	}
+
+	if (ok == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute "
+			   "received");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wps_process_ap_settings_e(struct wps_data *wps,
+				     struct wps_parse_attr *attr,
+				     struct wpabuf *attrs, int wps2)
+{
+	struct wps_credential cred;
+	int ret = 0;
+
+	if (!wps->wps->ap)
+		return 0;
+
+	if (wps_process_ap_settings(attr, &cred) < 0)
+		return -1;
+
+	wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
+		   "Registrar");
+
+	if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
+	    0) {
+		wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings ("
+			   MACSTR ") does not match with own address (" MACSTR
+			   ")", MAC2STR(cred.mac_addr),
+			   MAC2STR(wps->wps->dev.mac_addr));
+		/*
+		 * In theory, this could be consider fatal error, but there are
+		 * number of deployed implementations using other address here
+		 * due to unclarity in the specification. For interoperability
+		 * reasons, allow this to be processed since we do not really
+		 * use the MAC Address information for anything.
+		 */
+#ifdef CONFIG_WPS_STRICT
+		if (wps2) {
+			wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
+				   "MAC Address in AP Settings");
+			return -1;
+		}
+#endif /* CONFIG_WPS_STRICT */
+	}
+
+	if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
+	{
+		if (cred.encr_type & WPS_ENCR_WEP) {
+			wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
+				   "due to WEP configuration");
+			wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
+			return -1;
+		}
+
+		wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
+			   "invalid encr_type 0x%x", cred.encr_type);
+		return -1;
+	}
+
+#ifdef CONFIG_WPS_STRICT
+	if (wps2) {
+		if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
+		    WPS_ENCR_TKIP ||
+		    (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+		    WPS_AUTH_WPAPSK) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 "
+				   "AP Settings: WPA-Personal/TKIP only");
+			wps->error_indication =
+				WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED;
+			return -1;
+		}
+	}
+#endif /* CONFIG_WPS_STRICT */
+
+	if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
+	{
+		wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
+			   "TKIP+AES");
+		cred.encr_type |= WPS_ENCR_AES;
+	}
+
+	if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+	    WPS_AUTH_WPAPSK) {
+		wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
+			   "WPAPSK+WPA2PSK");
+		cred.auth_type |= WPS_AUTH_WPA2PSK;
+	}
+
+	if (wps->wps->cred_cb) {
+		cred.cred_attr = wpabuf_head(attrs);
+		cred.cred_attr_len = wpabuf_len(attrs);
+		ret = wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+	}
+
+	return ret;
+}
+
+
+static int wps_process_dev_pw_id(struct wps_data *wps, const u8 *dev_pw_id)
+{
+	u16 id;
+
+	if (dev_pw_id == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Device Password ID");
+		return -1;
+	}
+
+	id = WPA_GET_BE16(dev_pw_id);
+	if (wps->dev_pw_id == id) {
+		wpa_printf(MSG_DEBUG, "WPS: Device Password ID %u", id);
+		return 0;
+	}
+
+#ifdef CONFIG_P2P
+	if ((id == DEV_PW_DEFAULT &&
+	     wps->dev_pw_id == DEV_PW_REGISTRAR_SPECIFIED) ||
+	    (id == DEV_PW_REGISTRAR_SPECIFIED &&
+	     wps->dev_pw_id == DEV_PW_DEFAULT)) {
+		/*
+		 * Common P2P use cases indicate whether the PIN is from the
+		 * client or GO using Device Password Id in M1/M2 in a way that
+		 * does not look fully compliant with WSC specification. Anyway,
+		 * this is deployed and needs to be allowed, so ignore changes
+		 * between Registrar-Specified and Default PIN.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS: Allow PIN Device Password ID "
+			   "change");
+		return 0;
+	}
+#endif /* CONFIG_P2P */
+
+	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");
+		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;
+		wps->alt_dev_password = NULL;
+		wps->alt_dev_password_len = 0;
+		return 0;
+	}
+
+	return -1;
+}
+
+
+static enum wps_process_res wps_process_m2(struct wps_data *wps,
+					   const struct wpabuf *msg,
+					   struct wps_parse_attr *attr)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Received M2");
+
+	if (wps->state != RECV_M2) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M2", wps->state);
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+	    wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+	    wps_process_uuid_r(wps, attr->uuid_r) ||
+	    wps_process_dev_pw_id(wps, attr->dev_password_id)) {
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	/*
+	 * Stop here on an AP as an Enrollee if AP Setup is locked unless the
+	 * special locked mode is used to allow protocol run up to M7 in order
+	 * to support external Registrars that only learn the current AP
+	 * configuration without changing it.
+	 */
+	if (wps->wps->ap &&
+	    ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) ||
+	     wps->dev_password == NULL)) {
+		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
+			   "registration of a new Registrar");
+		wps->config_error = WPS_CFG_SETUP_LOCKED;
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
+	    wps_process_authenticator(wps, attr->authenticator, msg) ||
+	    wps_process_device_attrs(&wps->peer_dev, attr)) {
+		wps->state = SEND_WSC_NACK;
+		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;
+}
+
+
+static enum wps_process_res wps_process_m2d(struct wps_data *wps,
+					    struct wps_parse_attr *attr)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Received M2D");
+
+	if (wps->state != RECV_M2) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M2D", wps->state);
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
+			  attr->manufacturer, attr->manufacturer_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
+			  attr->model_name, attr->model_name_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
+			  attr->model_number, attr->model_number_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
+			  attr->serial_number, attr->serial_number_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
+			  attr->dev_name, attr->dev_name_len);
+
+	if (wps->wps->event_cb) {
+		union wps_event_data data;
+		struct wps_event_m2d *m2d = &data.m2d;
+		os_memset(&data, 0, sizeof(data));
+		if (attr->config_methods)
+			m2d->config_methods =
+				WPA_GET_BE16(attr->config_methods);
+		m2d->manufacturer = attr->manufacturer;
+		m2d->manufacturer_len = attr->manufacturer_len;
+		m2d->model_name = attr->model_name;
+		m2d->model_name_len = attr->model_name_len;
+		m2d->model_number = attr->model_number;
+		m2d->model_number_len = attr->model_number_len;
+		m2d->serial_number = attr->serial_number;
+		m2d->serial_number_len = attr->serial_number_len;
+		m2d->dev_name = attr->dev_name;
+		m2d->dev_name_len = attr->dev_name_len;
+		m2d->primary_dev_type = attr->primary_dev_type;
+		if (attr->config_error)
+			m2d->config_error =
+				WPA_GET_BE16(attr->config_error);
+		if (attr->dev_password_id)
+			m2d->dev_password_id =
+				WPA_GET_BE16(attr->dev_password_id);
+		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data);
+	}
+
+	wps->state = RECEIVED_M2D;
+	return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m4(struct wps_data *wps,
+					   const struct wpabuf *msg,
+					   struct wps_parse_attr *attr)
+{
+	struct wpabuf *decrypted;
+	struct wps_parse_attr eattr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received M4");
+
+	if (wps->state != RECV_M4) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M4", wps->state);
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+	    wps_process_authenticator(wps, attr->authenticator, msg) ||
+	    wps_process_r_hash1(wps, attr->r_hash1) ||
+	    wps_process_r_hash2(wps, attr->r_hash2)) {
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+					      attr->encr_settings_len);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+			   "Settings attribute");
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) {
+		wpabuf_free(decrypted);
+		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_r_snonce1(wps, eattr.r_snonce1)) {
+		wpabuf_free(decrypted);
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+	wpabuf_free(decrypted);
+
+	wps->state = SEND_M5;
+	return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m6(struct wps_data *wps,
+					   const struct wpabuf *msg,
+					   struct wps_parse_attr *attr)
+{
+	struct wpabuf *decrypted;
+	struct wps_parse_attr eattr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received M6");
+
+	if (wps->state != RECV_M6) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M6", wps->state);
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+	    wps_process_authenticator(wps, attr->authenticator, msg)) {
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+					      attr->encr_settings_len);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+			   "Settings attribute");
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) {
+		wpabuf_free(decrypted);
+		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_r_snonce2(wps, eattr.r_snonce2)) {
+		wpabuf_free(decrypted);
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+	wpabuf_free(decrypted);
+
+	if (wps->wps->ap)
+		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS,
+				   NULL);
+
+	wps->state = SEND_M7;
+	return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m8(struct wps_data *wps,
+					   const struct wpabuf *msg,
+					   struct wps_parse_attr *attr)
+{
+	struct wpabuf *decrypted;
+	struct wps_parse_attr eattr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received M8");
+
+	if (wps->state != RECV_M8) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M8", wps->state);
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+	    wps_process_authenticator(wps, attr->authenticator, msg)) {
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	if (wps->wps->ap && wps->wps->ap_setup_locked) {
+		/*
+		 * Stop here if special ap_setup_locked == 2 mode allowed the
+		 * protocol to continue beyond M2. This allows ER to learn the
+		 * current AP settings without changing them.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
+			   "registration of a new Registrar");
+		wps->config_error = WPS_CFG_SETUP_LOCKED;
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+					      attr->encr_settings_len);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+			   "Settings attribute");
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	if (wps_validate_m8_encr(decrypted, wps->wps->ap,
+				 attr->version2 != NULL) < 0) {
+		wpabuf_free(decrypted);
+		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) ||
+	    wps_process_ap_settings_e(wps, &eattr, decrypted,
+				      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;
+}
+
+
+static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
+						const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+	enum wps_process_res ret = WPS_CONTINUE;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return WPS_FAILURE;
+
+	if (attr.enrollee_nonce == NULL ||
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+		return WPS_FAILURE;
+	}
+
+	if (attr.msg_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	switch (*attr.msg_type) {
+	case WPS_M2:
+		if (wps_validate_m2(msg) < 0)
+			return WPS_FAILURE;
+		ret = wps_process_m2(wps, msg, &attr);
+		break;
+	case WPS_M2D:
+		if (wps_validate_m2d(msg) < 0)
+			return WPS_FAILURE;
+		ret = wps_process_m2d(wps, &attr);
+		break;
+	case WPS_M4:
+		if (wps_validate_m4(msg) < 0)
+			return WPS_FAILURE;
+		ret = wps_process_m4(wps, msg, &attr);
+		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+			wps_fail_event(wps->wps, WPS_M4, wps->config_error,
+				       wps->error_indication,
+				       wps->peer_dev.mac_addr);
+		break;
+	case WPS_M6:
+		if (wps_validate_m6(msg) < 0)
+			return WPS_FAILURE;
+		ret = wps_process_m6(wps, msg, &attr);
+		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+			wps_fail_event(wps->wps, WPS_M6, wps->config_error,
+				       wps->error_indication,
+				       wps->peer_dev.mac_addr);
+		break;
+	case WPS_M8:
+		if (wps_validate_m8(msg) < 0)
+			return WPS_FAILURE;
+		ret = wps_process_m8(wps, msg, &attr);
+		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+			wps_fail_event(wps->wps, WPS_M8, wps->config_error,
+				       wps->error_indication,
+				       wps->peer_dev.mac_addr);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
+			   *attr.msg_type);
+		return WPS_FAILURE;
+	}
+
+	/*
+	 * Save a copy of the last message for Authenticator derivation if we
+	 * are continuing. However, skip M2D since it is not authenticated and
+	 * neither is the ACK/NACK response frame. This allows the possibly
+	 * following M2 to be processed correctly by using the previously sent
+	 * M1 in Authenticator derivation.
+	 */
+	if (ret == WPS_CONTINUE && *attr.msg_type != WPS_M2D) {
+		/* Save a copy of the last message for Authenticator derivation
+		 */
+		wpabuf_free(wps->last_msg);
+		wps->last_msg = wpabuf_dup(msg);
+	}
+
+	return ret;
+}
+
+
+static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
+						const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return WPS_FAILURE;
+
+	if (attr.msg_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+		return WPS_FAILURE;
+	}
+
+	if (*attr.msg_type != WPS_WSC_ACK) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+			   *attr.msg_type);
+		return WPS_FAILURE;
+	}
+
+	if (attr.registrar_nonce == NULL ||
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
+	{
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+		return WPS_FAILURE;
+	}
+
+	if (attr.enrollee_nonce == NULL ||
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+		return WPS_FAILURE;
+	}
+
+	if (wps->state == RECV_ACK && wps->wps->ap) {
+		wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
+			   "completed successfully");
+		wps_success_event(wps->wps, wps->peer_dev.mac_addr);
+		wps->state = WPS_FINISHED;
+		return WPS_DONE;
+	}
+
+	return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
+						 const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+	u16 config_error;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return WPS_FAILURE;
+
+	if (attr.msg_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+		return WPS_FAILURE;
+	}
+
+	if (*attr.msg_type != WPS_WSC_NACK) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+			   *attr.msg_type);
+		return WPS_FAILURE;
+	}
+
+	if (attr.registrar_nonce == NULL ||
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
+	{
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+		wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
+			    attr.registrar_nonce, WPS_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
+			    wps->nonce_r, WPS_NONCE_LEN);
+		return WPS_FAILURE;
+	}
+
+	if (attr.enrollee_nonce == NULL ||
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+		wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
+			    attr.enrollee_nonce, WPS_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
+			    wps->nonce_e, WPS_NONCE_LEN);
+		return WPS_FAILURE;
+	}
+
+	if (attr.config_error == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
+			   "in WSC_NACK");
+		return WPS_FAILURE;
+	}
+
+	config_error = WPA_GET_BE16(attr.config_error);
+	wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
+		   "Configuration Error %d", config_error);
+
+	switch (wps->state) {
+	case RECV_M4:
+		wps_fail_event(wps->wps, WPS_M3, config_error,
+			       wps->error_indication, wps->peer_dev.mac_addr);
+		break;
+	case RECV_M6:
+		wps_fail_event(wps->wps, WPS_M5, config_error,
+			       wps->error_indication, wps->peer_dev.mac_addr);
+		break;
+	case RECV_M8:
+		wps_fail_event(wps->wps, WPS_M7, config_error,
+			       wps->error_indication, wps->peer_dev.mac_addr);
+		break;
+	default:
+		break;
+	}
+
+	/* Followed by NACK if Enrollee is Supplicant or EAP-Failure if
+	 * Enrollee is Authenticator */
+	wps->state = SEND_WSC_NACK;
+
+	return WPS_FAILURE;
+}
+
+
+enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
+					      enum wsc_op_code op_code,
+					      const struct wpabuf *msg)
+{
+
+	wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
+		   "op_code=%d)",
+		   (unsigned long) wpabuf_len(msg), op_code);
+
+	if (op_code == WSC_UPnP) {
+		/* Determine the OpCode based on message type attribute */
+		struct wps_parse_attr attr;
+		if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
+			if (*attr.msg_type == WPS_WSC_ACK)
+				op_code = WSC_ACK;
+			else if (*attr.msg_type == WPS_WSC_NACK)
+				op_code = WSC_NACK;
+		}
+	}
+
+	switch (op_code) {
+	case WSC_MSG:
+	case WSC_UPnP:
+		return wps_process_wsc_msg(wps, msg);
+	case WSC_ACK:
+		if (wps_validate_wsc_ack(msg) < 0)
+			return WPS_FAILURE;
+		return wps_process_wsc_ack(wps, msg);
+	case WSC_NACK:
+		if (wps_validate_wsc_nack(msg) < 0)
+			return WPS_FAILURE;
+		return wps_process_wsc_nack(wps, msg);
+	default:
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
+		return WPS_FAILURE;
+	}
+}
diff --git a/hostap/src/wps/wps_er.c b/hostap/src/wps/wps_er.c
new file mode 100644
index 0000000..b840acd
--- /dev/null
+++ b/hostap/src/wps/wps_er.c
@@ -0,0 +1,2108 @@
+/*
+ * Wi-Fi Protected Setup - External Registrar
+ * Copyright (c) 2009-2013, 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 "base64.h"
+#include "uuid.h"
+#include "eloop.h"
+#include "httpread.h"
+#include "http_client.h"
+#include "http_server.h"
+#include "upnp_xml.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+#include "wps_er.h"
+
+
+static void wps_er_deinit_finish(void *eloop_data, void *user_ctx);
+static void wps_er_ap_timeout(void *eloop_data, void *user_ctx);
+static void wps_er_sta_timeout(void *eloop_data, void *user_ctx);
+static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg);
+static int wps_er_send_get_device_info(struct wps_er_ap *ap,
+				       void (*m1_handler)(struct wps_er_ap *ap,
+							  struct wpabuf *m1));
+
+
+static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta,
+			     enum wps_event event)
+{
+	union wps_event_data data;
+	struct wps_event_er_enrollee *ev = &data.enrollee;
+
+	if (wps->event_cb == NULL)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	ev->uuid = sta->uuid;
+	ev->mac_addr = sta->addr;
+	ev->m1_received = sta->m1_received;
+	ev->config_methods = sta->config_methods;
+	ev->dev_passwd_id = sta->dev_passwd_id;
+	ev->pri_dev_type = sta->pri_dev_type;
+	ev->dev_name = sta->dev_name;
+	ev->manufacturer = sta->manufacturer;
+	ev->model_name = sta->model_name;
+	ev->model_number = sta->model_number;
+	ev->serial_number = sta->serial_number;
+	wps->event_cb(wps->cb_ctx, event, &data);
+}
+
+
+static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr,
+					  const u8 *uuid)
+{
+	struct wps_er_sta *sta;
+	dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) {
+		if ((addr == NULL ||
+		     os_memcmp(sta->addr, addr, ETH_ALEN) == 0) &&
+		    (uuid == NULL ||
+		     os_memcmp(uuid, sta->uuid, WPS_UUID_LEN) == 0))
+			return sta;
+	}
+	return NULL;
+}
+
+
+static void wps_er_sta_free(struct wps_er_sta *sta)
+{
+	wps_er_sta_event(sta->ap->er->wps, sta, WPS_EV_ER_ENROLLEE_REMOVE);
+	if (sta->wps)
+		wps_deinit(sta->wps);
+	os_free(sta->manufacturer);
+	os_free(sta->model_name);
+	os_free(sta->model_number);
+	os_free(sta->serial_number);
+	os_free(sta->dev_name);
+	http_client_free(sta->http);
+	eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
+	os_free(sta->cred);
+	os_free(sta);
+}
+
+
+static void wps_er_sta_remove_all(struct wps_er_ap *ap)
+{
+	struct wps_er_sta *prev, *sta;
+	dl_list_for_each_safe(sta, prev, &ap->sta, struct wps_er_sta, list)
+		wps_er_sta_free(sta);
+}
+
+
+static struct wps_er_ap * wps_er_ap_get(struct wps_er *er,
+					struct in_addr *addr, const u8 *uuid,
+					const u8 *mac_addr)
+{
+	struct wps_er_ap *ap;
+	dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+		if ((addr == NULL || ap->addr.s_addr == addr->s_addr) &&
+		    (uuid == NULL ||
+		     os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0) &&
+		    (mac_addr == NULL ||
+		     os_memcmp(mac_addr, ap->mac_addr, ETH_ALEN) == 0))
+			return ap;
+	}
+	return NULL;
+}
+
+
+static struct wps_er_ap * wps_er_ap_get_id(struct wps_er *er, unsigned int id)
+{
+	struct wps_er_ap *ap;
+	dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+		if (ap->id == id)
+			return ap;
+	}
+	return NULL;
+}
+
+
+static void wps_er_ap_event(struct wps_context *wps, struct wps_er_ap *ap,
+			    enum wps_event event)
+{
+	union wps_event_data data;
+	struct wps_event_er_ap *evap = &data.ap;
+
+	if (wps->event_cb == NULL)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	evap->uuid = ap->uuid;
+	evap->friendly_name = ap->friendly_name;
+	evap->manufacturer = ap->manufacturer;
+	evap->manufacturer_url = ap->manufacturer_url;
+	evap->model_description = ap->model_description;
+	evap->model_name = ap->model_name;
+	evap->model_number = ap->model_number;
+	evap->model_url = ap->model_url;
+	evap->serial_number = ap->serial_number;
+	evap->upc = ap->upc;
+	evap->pri_dev_type = ap->pri_dev_type;
+	evap->wps_state = ap->wps_state;
+	evap->mac_addr = ap->mac_addr;
+	wps->event_cb(wps->cb_ctx, event, &data);
+}
+
+
+static void wps_er_ap_free(struct wps_er_ap *ap)
+{
+	http_client_free(ap->http);
+	ap->http = NULL;
+
+	os_free(ap->location);
+	os_free(ap->friendly_name);
+	os_free(ap->manufacturer);
+	os_free(ap->manufacturer_url);
+	os_free(ap->model_description);
+	os_free(ap->model_name);
+	os_free(ap->model_number);
+	os_free(ap->model_url);
+	os_free(ap->serial_number);
+	os_free(ap->udn);
+	os_free(ap->upc);
+
+	os_free(ap->scpd_url);
+	os_free(ap->control_url);
+	os_free(ap->event_sub_url);
+
+	os_free(ap->ap_settings);
+
+	os_free(ap);
+}
+
+
+static void wps_er_ap_unsubscribed(struct wps_er *er, struct wps_er_ap *ap)
+{
+	wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from AP %s (%s)",
+		   inet_ntoa(ap->addr), ap->location);
+	dl_list_del(&ap->list);
+	wps_er_ap_free(ap);
+
+	if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing))
+		wps_er_deinit_finish(er, NULL);
+}
+
+
+static void wps_er_http_unsubscribe_cb(void *ctx, struct http_client *c,
+				       enum http_client_event event)
+{
+	struct wps_er_ap *ap = ctx;
+
+	switch (event) {
+	case HTTP_CLIENT_OK:
+		wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from events");
+		ap->subscribed = 0;
+		break;
+	case HTTP_CLIENT_FAILED:
+	case HTTP_CLIENT_INVALID_REPLY:
+	case HTTP_CLIENT_TIMEOUT:
+		wpa_printf(MSG_DEBUG, "WPS ER: Failed to unsubscribe from "
+			   "events");
+		break;
+	}
+	http_client_free(ap->http);
+	ap->http = NULL;
+
+	/*
+	 * Need to get rid of the AP entry regardless of whether we managed to
+	 * unsubscribe cleanly or not.
+	 */
+	wps_er_ap_unsubscribed(ap->er, ap);
+}
+
+
+static void wps_er_ap_unsubscribe(struct wps_er *er, struct wps_er_ap *ap)
+{
+	struct wpabuf *req;
+	struct sockaddr_in dst;
+	char *url, *path;
+	char sid[100];
+
+	if (ap->event_sub_url == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot "
+			   "subscribe");
+		goto fail;
+	}
+	if (ap->http) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot "
+			   "send subscribe request");
+		goto fail;
+	}
+
+	url = http_client_url_parse(ap->event_sub_url, &dst, &path);
+	if (url == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL");
+		goto fail;
+	}
+
+	req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000);
+	if (req == NULL) {
+		os_free(url);
+		goto fail;
+	}
+	uuid_bin2str(ap->sid, sid, sizeof(sid));
+	wpabuf_printf(req,
+		      "UNSUBSCRIBE %s HTTP/1.1\r\n"
+		      "HOST: %s:%d\r\n"
+		      "SID: uuid:%s\r\n"
+		      "\r\n",
+		      path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port), sid);
+	os_free(url);
+	wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Unsubscription request",
+			  wpabuf_head(req), wpabuf_len(req));
+
+	ap->http = http_client_addr(&dst, req, 1000,
+				    wps_er_http_unsubscribe_cb, ap);
+	if (ap->http == NULL) {
+		wpabuf_free(req);
+		goto fail;
+	}
+	return;
+
+fail:
+	/*
+	 * Need to get rid of the AP entry even when we fail to unsubscribe
+	 * cleanly.
+	 */
+	wps_er_ap_unsubscribed(ap->er, ap);
+}
+
+
+static struct wps_er_ap_settings * wps_er_ap_get_settings(struct wps_er *er,
+							  const u8 *uuid)
+{
+	struct wps_er_ap_settings *s;
+	dl_list_for_each(s, &er->ap_settings, struct wps_er_ap_settings, list)
+		if (os_memcmp(uuid, s->uuid, WPS_UUID_LEN) == 0)
+			return s;
+	return NULL;
+}
+
+
+int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr)
+{
+	struct wps_er_ap *ap;
+	struct wps_er_ap_settings *settings;
+
+	ap = wps_er_ap_get(er, addr, NULL, NULL);
+	if (ap == NULL || ap->ap_settings == NULL)
+		return -1;
+
+	settings = wps_er_ap_get_settings(er, ap->uuid);
+	if (!settings) {
+		settings = os_zalloc(sizeof(*settings));
+		if (settings == NULL)
+			return -1;
+		os_memcpy(settings->uuid, ap->uuid, WPS_UUID_LEN);
+		dl_list_add(&er->ap_settings, &settings->list);
+	}
+	os_memcpy(&settings->ap_settings, ap->ap_settings,
+		  sizeof(struct wps_credential));
+
+	return 0;
+}
+
+
+static int wps_er_ap_use_cached_settings(struct wps_er *er,
+					 struct wps_er_ap *ap)
+{
+	struct wps_er_ap_settings *s;
+
+	if (ap->ap_settings)
+		return 0;
+
+	s = wps_er_ap_get_settings(ap->er, ap->uuid);
+	if (!s)
+		return -1;
+
+	ap->ap_settings = os_malloc(sizeof(*ap->ap_settings));
+	if (ap->ap_settings == NULL)
+		return -1;
+
+	os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings));
+	wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings");
+	return 0;
+}
+
+
+static void wps_er_ap_remove_entry(struct wps_er *er, struct wps_er_ap *ap)
+{
+	wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)",
+		   inet_ntoa(ap->addr), ap->location);
+	eloop_cancel_timeout(wps_er_ap_timeout, er, ap);
+	wps_er_sta_remove_all(ap);
+	wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_REMOVE);
+	http_client_free(ap->http);
+	ap->http = NULL;
+	if (ap->wps) {
+		wps_deinit(ap->wps);
+		ap->wps = NULL;
+	}
+
+	dl_list_del(&ap->list);
+	if (ap->subscribed) {
+		dl_list_add(&er->ap_unsubscribing, &ap->list);
+		wps_er_ap_unsubscribe(er, ap);
+	} else
+		wps_er_ap_free(ap);
+}
+
+
+static void wps_er_ap_timeout(void *eloop_data, void *user_ctx)
+{
+	struct wps_er *er = eloop_data;
+	struct wps_er_ap *ap = user_ctx;
+	wpa_printf(MSG_DEBUG, "WPS ER: AP advertisement timed out");
+	wps_er_ap_remove_entry(er, ap);
+}
+
+
+static int wps_er_get_sid(struct wps_er_ap *ap, char *sid)
+{
+	char *pos;
+	char txt[100];
+
+	if (!sid) {
+		wpa_printf(MSG_DEBUG, "WPS ER: No SID received from %s (%s)",
+			   inet_ntoa(ap->addr), ap->location);
+		return -1;
+	}
+
+	pos = os_strstr(sid, "uuid:");
+	if (!pos) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from "
+			   "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location,
+			   sid);
+		return -1;
+	}
+
+	pos += 5;
+	if (uuid_str2bin(pos, ap->sid) < 0) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from "
+			   "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location,
+			   sid);
+		return -1;
+	}
+
+	uuid_bin2str(ap->sid, txt, sizeof(txt));
+	wpa_printf(MSG_DEBUG, "WPS ER: SID for subscription with %s (%s): %s",
+		   inet_ntoa(ap->addr), ap->location, txt);
+
+	return 0;
+}
+
+
+static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c,
+				     enum http_client_event event)
+{
+	struct wps_er_ap *ap = ctx;
+
+	switch (event) {
+	case HTTP_CLIENT_OK:
+		wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events");
+		ap->subscribed = 1;
+		wps_er_get_sid(ap, http_client_get_hdr_line(c, "SID"));
+		wps_er_ap_use_cached_settings(ap->er, ap);
+		wps_er_ap_event(ap->er->wps, ap, WPS_EV_ER_AP_ADD);
+		break;
+	case HTTP_CLIENT_FAILED:
+	case HTTP_CLIENT_INVALID_REPLY:
+	case HTTP_CLIENT_TIMEOUT:
+		wpa_printf(MSG_DEBUG, "WPS ER: Failed to subscribe to events");
+		break;
+	}
+	http_client_free(ap->http);
+	ap->http = NULL;
+}
+
+
+static void wps_er_subscribe(struct wps_er_ap *ap)
+{
+	struct wpabuf *req;
+	struct sockaddr_in dst;
+	char *url, *path;
+
+	if (ap->event_sub_url == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot "
+			   "subscribe");
+		return;
+	}
+	if (ap->http) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot "
+			   "send subscribe request");
+		return;
+	}
+
+	url = http_client_url_parse(ap->event_sub_url, &dst, &path);
+	if (url == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL");
+		return;
+	}
+
+	req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000);
+	if (req == NULL) {
+		os_free(url);
+		return;
+	}
+	wpabuf_printf(req,
+		      "SUBSCRIBE %s HTTP/1.1\r\n"
+		      "HOST: %s:%d\r\n"
+		      "CALLBACK: <http://%s:%d/event/%u/%u>\r\n"
+		      "NT: upnp:event\r\n"
+		      "TIMEOUT: Second-%d\r\n"
+		      "\r\n",
+		      path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port),
+		      ap->er->ip_addr_text, ap->er->http_port,
+		      ap->er->event_id, ap->id, 1800);
+	os_free(url);
+	wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Subscription request",
+			  wpabuf_head(req), wpabuf_len(req));
+
+	ap->http = http_client_addr(&dst, req, 1000, wps_er_http_subscribe_cb,
+				    ap);
+	if (ap->http == NULL)
+		wpabuf_free(req);
+}
+
+
+static void wps_er_ap_get_m1(struct wps_er_ap *ap, struct wpabuf *m1)
+{
+	struct wps_parse_attr attr;
+
+	if (wps_parse_msg(m1, &attr) < 0) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse M1");
+		return;
+	}
+	if (attr.primary_dev_type)
+		os_memcpy(ap->pri_dev_type, attr.primary_dev_type, 8);
+	if (attr.wps_state)
+		ap->wps_state = *attr.wps_state;
+	if (attr.mac_addr)
+		os_memcpy(ap->mac_addr, attr.mac_addr, ETH_ALEN);
+
+	wps_er_subscribe(ap);
+}
+
+
+static void wps_er_get_device_info(struct wps_er_ap *ap)
+{
+	wps_er_send_get_device_info(ap, wps_er_ap_get_m1);
+}
+
+
+static const char * wps_er_find_wfadevice(const char *data)
+{
+	const char *tag, *tagname, *end;
+	char *val;
+	int found = 0;
+
+	while (!found) {
+		/* Find next <device> */
+		for (;;) {
+			if (xml_next_tag(data, &tag, &tagname, &end))
+				return NULL;
+			data = end;
+			if (!os_strncasecmp(tagname, "device", 6) &&
+			    *tag != '/' &&
+			    (tagname[6] == '>' || !isgraph(tagname[6]))) {
+				break;
+			}
+		}
+
+		/* Check whether deviceType is WFADevice */
+		val = xml_get_first_item(data, "deviceType");
+		if (val == NULL)
+			return NULL;
+		wpa_printf(MSG_DEBUG, "WPS ER: Found deviceType '%s'", val);
+		found = os_strcasecmp(val, "urn:schemas-wifialliance-org:"
+				      "device:WFADevice:1") == 0;
+		os_free(val);
+	}
+
+	return data;
+}
+
+
+static void wps_er_parse_device_description(struct wps_er_ap *ap,
+					    struct wpabuf *reply)
+{
+	/* Note: reply includes null termination after the buffer data */
+	const char *tmp, *data = wpabuf_head(reply);
+	char *pos;
+
+	wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info",
+			  wpabuf_head(reply), wpabuf_len(reply));
+
+	/*
+	 * The root device description may include multiple devices, so first
+	 * find the beginning of the WFADevice description to allow the
+	 * simplistic parser to pick the correct entries.
+	 */
+	tmp = wps_er_find_wfadevice(data);
+	if (tmp == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: WFADevice:1 device not found - "
+			   "trying to parse invalid data");
+	} else
+		data = tmp;
+
+	ap->friendly_name = xml_get_first_item(data, "friendlyName");
+	wpa_printf(MSG_DEBUG, "WPS ER: friendlyName='%s'", ap->friendly_name);
+
+	ap->manufacturer = xml_get_first_item(data, "manufacturer");
+	wpa_printf(MSG_DEBUG, "WPS ER: manufacturer='%s'", ap->manufacturer);
+
+	ap->manufacturer_url = xml_get_first_item(data, "manufacturerURL");
+	wpa_printf(MSG_DEBUG, "WPS ER: manufacturerURL='%s'",
+		   ap->manufacturer_url);
+
+	ap->model_description = xml_get_first_item(data, "modelDescription");
+	wpa_printf(MSG_DEBUG, "WPS ER: modelDescription='%s'",
+		   ap->model_description);
+
+	ap->model_name = xml_get_first_item(data, "modelName");
+	wpa_printf(MSG_DEBUG, "WPS ER: modelName='%s'", ap->model_name);
+
+	ap->model_number = xml_get_first_item(data, "modelNumber");
+	wpa_printf(MSG_DEBUG, "WPS ER: modelNumber='%s'", ap->model_number);
+
+	ap->model_url = xml_get_first_item(data, "modelURL");
+	wpa_printf(MSG_DEBUG, "WPS ER: modelURL='%s'", ap->model_url);
+
+	ap->serial_number = xml_get_first_item(data, "serialNumber");
+	wpa_printf(MSG_DEBUG, "WPS ER: serialNumber='%s'", ap->serial_number);
+
+	ap->udn = xml_get_first_item(data, "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");
+	wpa_printf(MSG_DEBUG, "WPS ER: UPC='%s'", ap->upc);
+
+	ap->scpd_url = http_link_update(
+		xml_get_first_item(data, "SCPDURL"), ap->location);
+	wpa_printf(MSG_DEBUG, "WPS ER: SCPDURL='%s'", ap->scpd_url);
+
+	ap->control_url = http_link_update(
+		xml_get_first_item(data, "controlURL"), ap->location);
+	wpa_printf(MSG_DEBUG, "WPS ER: controlURL='%s'", ap->control_url);
+
+	ap->event_sub_url = http_link_update(
+		xml_get_first_item(data, "eventSubURL"), ap->location);
+	wpa_printf(MSG_DEBUG, "WPS ER: eventSubURL='%s'", ap->event_sub_url);
+}
+
+
+static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c,
+				    enum http_client_event event)
+{
+	struct wps_er_ap *ap = ctx;
+	struct wpabuf *reply;
+	int ok = 0;
+
+	switch (event) {
+	case HTTP_CLIENT_OK:
+		reply = http_client_get_body(c);
+		if (reply == NULL)
+			break;
+		wps_er_parse_device_description(ap, reply);
+		ok = 1;
+		break;
+	case HTTP_CLIENT_FAILED:
+	case HTTP_CLIENT_INVALID_REPLY:
+	case HTTP_CLIENT_TIMEOUT:
+		wpa_printf(MSG_DEBUG, "WPS ER: Failed to fetch device info");
+		break;
+	}
+	http_client_free(ap->http);
+	ap->http = NULL;
+	if (ok)
+		wps_er_get_device_info(ap);
+}
+
+
+void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr,
+		   const char *location, int max_age)
+{
+	struct wps_er_ap *ap;
+
+	ap = wps_er_ap_get(er, addr, uuid, NULL);
+	if (ap) {
+		/* Update advertisement timeout */
+		eloop_cancel_timeout(wps_er_ap_timeout, er, ap);
+		eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap);
+		return;
+	}
+
+	ap = os_zalloc(sizeof(*ap));
+	if (ap == NULL)
+		return;
+	dl_list_init(&ap->sta);
+	ap->er = er;
+	ap->id = ++er->next_ap_id;
+	ap->location = os_strdup(location);
+	if (ap->location == NULL) {
+		os_free(ap);
+		return;
+	}
+	dl_list_add(&er->ap, &ap->list);
+
+	ap->addr.s_addr = addr->s_addr;
+	os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
+	eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap);
+
+	wpa_printf(MSG_DEBUG, "WPS ER: Added AP entry for %s (%s)",
+		   inet_ntoa(ap->addr), ap->location);
+
+	/* Fetch device description */
+	ap->http = http_client_url(ap->location, NULL, 10000,
+				   wps_er_http_dev_desc_cb, ap);
+}
+
+
+void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr)
+{
+	struct wps_er_ap *ap;
+	dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+		if (ap->addr.s_addr == addr->s_addr) {
+			wps_er_ap_remove_entry(er, ap);
+			return;
+		}
+	}
+}
+
+
+static void wps_er_ap_remove_all(struct wps_er *er)
+{
+	struct wps_er_ap *prev, *ap;
+	struct wps_er_ap_settings *prev_s, *s;
+	dl_list_for_each_safe(ap, prev, &er->ap, struct wps_er_ap, list)
+		wps_er_ap_remove_entry(er, ap);
+	dl_list_for_each_safe(s, prev_s, &er->ap_settings,
+			      struct wps_er_ap_settings, list)
+		os_free(s);
+}
+
+
+static void http_put_date(struct wpabuf *buf)
+{
+	wpabuf_put_str(buf, "Date: ");
+	format_date(buf);
+	wpabuf_put_str(buf, "\r\n");
+}
+
+
+static void wps_er_http_resp_not_found(struct http_request *req)
+{
+	struct wpabuf *buf;
+	buf = wpabuf_alloc(200);
+	if (buf == NULL) {
+		http_request_deinit(req);
+		return;
+	}
+
+	wpabuf_put_str(buf,
+		       "HTTP/1.1 404 Not Found\r\n"
+		       "Server: unspecified, UPnP/1.0, unspecified\r\n"
+		       "Connection: close\r\n");
+	http_put_date(buf);
+	wpabuf_put_str(buf, "\r\n");
+	http_request_send_and_deinit(req, buf);
+}
+
+
+static void wps_er_http_resp_ok(struct http_request *req)
+{
+	struct wpabuf *buf;
+	buf = wpabuf_alloc(200);
+	if (buf == NULL) {
+		http_request_deinit(req);
+		return;
+	}
+
+	wpabuf_put_str(buf,
+		       "HTTP/1.1 200 OK\r\n"
+		       "Server: unspecified, UPnP/1.0, unspecified\r\n"
+		       "Connection: close\r\n"
+		       "Content-Length: 0\r\n");
+	http_put_date(buf);
+	wpabuf_put_str(buf, "\r\n");
+	http_request_send_and_deinit(req, buf);
+}
+
+
+static void wps_er_sta_timeout(void *eloop_data, void *user_ctx)
+{
+	struct wps_er_sta *sta = eloop_data;
+	wpa_printf(MSG_DEBUG, "WPS ER: STA entry timed out");
+	dl_list_del(&sta->list);
+	wps_er_sta_free(sta);
+}
+
+
+static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap,
+					       const u8 *addr,
+					       struct wps_parse_attr *attr,
+					       int probe_req)
+{
+	struct wps_er_sta *sta = wps_er_sta_get(ap, addr, NULL);
+	int new_sta = 0;
+	int m1;
+
+	m1 = !probe_req && attr->msg_type && *attr->msg_type == WPS_M1;
+
+	if (sta == NULL) {
+		/*
+		 * Only allow new STA entry to be added based on Probe Request
+		 * or M1. This will filter out bogus events and anything that
+		 * may have been ongoing at the time ER subscribed for events.
+		 */
+		if (!probe_req && !m1)
+			return NULL;
+
+		sta = os_zalloc(sizeof(*sta));
+		if (sta == NULL)
+			return NULL;
+		os_memcpy(sta->addr, addr, ETH_ALEN);
+		sta->ap = ap;
+		dl_list_add(&ap->sta, &sta->list);
+		new_sta = 1;
+	}
+
+	if (m1)
+		sta->m1_received = 1;
+
+	if (attr->config_methods && (!probe_req || !sta->m1_received))
+		sta->config_methods = WPA_GET_BE16(attr->config_methods);
+	if (attr->uuid_e && (!probe_req || !sta->m1_received))
+		os_memcpy(sta->uuid, attr->uuid_e, WPS_UUID_LEN);
+	if (attr->primary_dev_type && (!probe_req || !sta->m1_received))
+		os_memcpy(sta->pri_dev_type, attr->primary_dev_type, 8);
+	if (attr->dev_password_id && (!probe_req || !sta->m1_received))
+		sta->dev_passwd_id = WPA_GET_BE16(attr->dev_password_id);
+
+	if (attr->manufacturer) {
+		os_free(sta->manufacturer);
+		sta->manufacturer = dup_binstr(attr->manufacturer,
+					       attr->manufacturer_len);
+	}
+
+	if (attr->model_name) {
+		os_free(sta->model_name);
+		sta->model_name = dup_binstr(attr->model_name,
+					     attr->model_name_len);
+	}
+
+	if (attr->model_number) {
+		os_free(sta->model_number);
+		sta->model_number = dup_binstr(attr->model_number,
+					       attr->model_number_len);
+	}
+
+	if (attr->serial_number) {
+		os_free(sta->serial_number);
+		sta->serial_number = dup_binstr(attr->serial_number,
+						attr->serial_number_len);
+	}
+
+	if (attr->dev_name) {
+		os_free(sta->dev_name);
+		sta->dev_name = dup_binstr(attr->dev_name, attr->dev_name_len);
+	}
+
+	eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
+	eloop_register_timeout(300, 0, wps_er_sta_timeout, sta, NULL);
+
+	if (m1 || new_sta)
+		wps_er_sta_event(ap->er->wps, sta, WPS_EV_ER_ENROLLEE_ADD);
+
+	return sta;
+}
+
+
+static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap,
+					       const u8 *addr,
+					       struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - Probe Request - from "
+		   MACSTR, MAC2STR(addr));
+	wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
+			"(TLVs from Probe Request)", msg);
+
+	if (wps_validate_probe_req(msg, addr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: ER: Ignore invalid proxied "
+			   "Probe Request frame from " MACSTR, MAC2STR(addr));
+		return;
+	}
+
+	if (wps_parse_msg(msg, &attr) < 0) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
+			   "WLANEvent message");
+		return;
+	}
+
+	wps_er_add_sta_data(ap, addr, &attr, 1);
+	wps_registrar_probe_req_rx(ap->er->wps->registrar, addr, msg, 0);
+}
+
+
+static void wps_er_http_put_wlan_response_cb(void *ctx, struct http_client *c,
+					     enum http_client_event event)
+{
+	struct wps_er_sta *sta = ctx;
+
+	switch (event) {
+	case HTTP_CLIENT_OK:
+		wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse OK");
+		break;
+	case HTTP_CLIENT_FAILED:
+	case HTTP_CLIENT_INVALID_REPLY:
+	case HTTP_CLIENT_TIMEOUT:
+		wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse failed");
+		break;
+	}
+	http_client_free(sta->http);
+	sta->http = NULL;
+}
+
+
+static const char *soap_prefix =
+	"<?xml version=\"1.0\"?>\n"
+	"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+	"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
+	"<s:Body>\n";
+static const char *soap_postfix =
+	"</s:Body>\n</s:Envelope>\n";
+static const char *urn_wfawlanconfig =
+	"urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
+
+static struct wpabuf * wps_er_soap_hdr(const struct wpabuf *msg,
+				       const char *name, const char *arg_name,
+				       const char *path,
+				       const struct sockaddr_in *dst,
+				       char **len_ptr, char **body_ptr)
+{
+	unsigned char *encoded;
+	size_t encoded_len;
+	struct wpabuf *buf;
+
+	if (msg) {
+		encoded = base64_encode(wpabuf_head(msg), wpabuf_len(msg),
+					&encoded_len);
+		if (encoded == NULL)
+			return NULL;
+	} else {
+		encoded = NULL;
+		encoded_len = 0;
+	}
+
+	buf = wpabuf_alloc(1000 + encoded_len);
+	if (buf == NULL) {
+		os_free(encoded);
+		return NULL;
+	}
+
+	wpabuf_printf(buf,
+		      "POST %s HTTP/1.1\r\n"
+		      "Host: %s:%d\r\n"
+		      "Content-Type: text/xml; charset=\"utf-8\"\r\n"
+		      "Content-Length: ",
+		      path, inet_ntoa(dst->sin_addr), ntohs(dst->sin_port));
+
+	*len_ptr = wpabuf_put(buf, 0);
+	wpabuf_printf(buf,
+		      "        \r\n"
+		      "SOAPACTION: \"%s#%s\"\r\n"
+		      "\r\n",
+		      urn_wfawlanconfig, name);
+
+	*body_ptr = wpabuf_put(buf, 0);
+
+	wpabuf_put_str(buf, soap_prefix);
+	wpabuf_printf(buf, "<u:%s xmlns:u=\"", name);
+	wpabuf_put_str(buf, urn_wfawlanconfig);
+	wpabuf_put_str(buf, "\">\n");
+	if (encoded) {
+		wpabuf_printf(buf, "<%s>%s</%s>\n",
+			      arg_name, (char *) encoded, arg_name);
+		os_free(encoded);
+	}
+
+	return buf;
+}
+
+
+static void wps_er_soap_end(struct wpabuf *buf, const char *name,
+			    char *len_ptr, char *body_ptr)
+{
+	char len_buf[10];
+	wpabuf_printf(buf, "</u:%s>\n", name);
+	wpabuf_put_str(buf, soap_postfix);
+	os_snprintf(len_buf, sizeof(len_buf), "%d",
+		    (int) ((char *) wpabuf_put(buf, 0) - body_ptr));
+	os_memcpy(len_ptr, len_buf, os_strlen(len_buf));
+}
+
+
+static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg)
+{
+	struct wpabuf *buf;
+	char *len_ptr, *body_ptr;
+	struct sockaddr_in dst;
+	char *url, *path;
+
+	if (sta->http) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for STA - "
+			   "ignore new request");
+		wpabuf_free(msg);
+		return;
+	}
+
+	if (sta->ap->control_url == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
+		wpabuf_free(msg);
+		return;
+	}
+
+	url = http_client_url_parse(sta->ap->control_url, &dst, &path);
+	if (url == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
+		wpabuf_free(msg);
+		return;
+	}
+
+	buf = wps_er_soap_hdr(msg, "PutWLANResponse", "NewMessage", path, &dst,
+			      &len_ptr, &body_ptr);
+	wpabuf_free(msg);
+	os_free(url);
+	if (buf == NULL)
+		return;
+	wpabuf_printf(buf, "<NewWLANEventType>%d</NewWLANEventType>\n",
+		      UPNP_WPS_WLANEVENT_TYPE_EAP);
+	wpabuf_printf(buf, "<NewWLANEventMAC>" MACSTR "</NewWLANEventMAC>\n",
+		      MAC2STR(sta->addr));
+
+	wps_er_soap_end(buf, "PutWLANResponse", len_ptr, body_ptr);
+
+	sta->http = http_client_addr(&dst, buf, 1000,
+				     wps_er_http_put_wlan_response_cb, sta);
+	if (sta->http == NULL)
+		wpabuf_free(buf);
+}
+
+
+static void wps_er_sta_process(struct wps_er_sta *sta, struct wpabuf *msg,
+			       enum wsc_op_code op_code)
+{
+	enum wps_process_res res;
+
+	res = wps_process_msg(sta->wps, op_code, msg);
+	if (res == WPS_CONTINUE) {
+		struct wpabuf *next = wps_get_msg(sta->wps, &op_code);
+		if (next)
+			wps_er_sta_send_msg(sta, next);
+	} else {
+		wpa_printf(MSG_DEBUG, "WPS ER: Protocol run %s with the "
+			   "enrollee (res=%d)",
+			   res == WPS_DONE ? "succeeded" : "failed", res);
+		wps_deinit(sta->wps);
+		sta->wps = NULL;
+		if (res == WPS_DONE) {
+			/* Remove the STA entry after short timeout */
+			eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
+			eloop_register_timeout(10, 0, wps_er_sta_timeout, sta,
+					       NULL);
+		}
+	}
+}
+
+
+static void wps_er_sta_start(struct wps_er_sta *sta, struct wpabuf *msg)
+{
+	struct wps_config cfg;
+
+	if (sta->wps)
+		wps_deinit(sta->wps);
+
+	os_memset(&cfg, 0, sizeof(cfg));
+	cfg.wps = sta->ap->er->wps;
+	cfg.registrar = 1;
+	cfg.peer_addr = sta->addr;
+
+	sta->wps = wps_init(&cfg);
+	if (sta->wps == NULL)
+		return;
+	sta->wps->er = 1;
+	sta->wps->use_cred = sta->ap->ap_settings;
+	if (sta->ap->ap_settings) {
+		os_free(sta->cred);
+		sta->cred = os_malloc(sizeof(*sta->cred));
+		if (sta->cred) {
+			os_memcpy(sta->cred, sta->ap->ap_settings,
+				  sizeof(*sta->cred));
+			sta->cred->cred_attr = NULL;
+			os_memcpy(sta->cred->mac_addr, sta->addr, ETH_ALEN);
+			sta->wps->use_cred = sta->cred;
+		}
+	}
+
+	wps_er_sta_process(sta, msg, WSC_MSG);
+}
+
+
+static void wps_er_process_wlanevent_eap(struct wps_er_ap *ap, const u8 *addr,
+					 struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+	struct wps_er_sta *sta;
+
+	wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - EAP - from " MACSTR,
+		   MAC2STR(addr));
+	wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
+			"(TLVs from EAP-WSC)", msg);
+
+	if (wps_parse_msg(msg, &attr) < 0) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
+			   "WLANEvent message");
+		return;
+	}
+
+	sta = wps_er_add_sta_data(ap, addr, &attr, 0);
+	if (sta == NULL)
+		return;
+
+	if (attr.msg_type && *attr.msg_type == WPS_M1)
+		wps_er_sta_start(sta, msg);
+	else if (sta->wps) {
+		enum wsc_op_code op_code = WSC_MSG;
+		if (attr.msg_type) {
+			switch (*attr.msg_type) {
+			case WPS_WSC_ACK:
+				op_code = WSC_ACK;
+				break;
+			case WPS_WSC_NACK:
+				op_code = WSC_NACK;
+				break;
+			case WPS_WSC_DONE:
+				op_code = WSC_Done;
+				break;
+			}
+		}
+		wps_er_sta_process(sta, msg, op_code);
+	}
+}
+
+
+static void wps_er_process_wlanevent(struct wps_er_ap *ap,
+				     struct wpabuf *event)
+{
+	u8 *data;
+	u8 wlan_event_type;
+	u8 wlan_event_mac[ETH_ALEN];
+	struct wpabuf msg;
+
+	wpa_hexdump(MSG_MSGDUMP, "WPS ER: Received WLANEvent",
+		    wpabuf_head(event), wpabuf_len(event));
+	if (wpabuf_len(event) < 1 + 17) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Too short WLANEvent");
+		return;
+	}
+
+	data = wpabuf_mhead(event);
+	wlan_event_type = data[0];
+	if (hwaddr_aton((char *) data + 1, wlan_event_mac) < 0) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Invalid WLANEventMAC in "
+			   "WLANEvent");
+		return;
+	}
+
+	wpabuf_set(&msg, data + 1 + 17, wpabuf_len(event) - (1 + 17));
+
+	switch (wlan_event_type) {
+	case 1:
+		wps_er_process_wlanevent_probe_req(ap, wlan_event_mac, &msg);
+		break;
+	case 2:
+		wps_er_process_wlanevent_eap(ap, wlan_event_mac, &msg);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "WPS ER: Unknown WLANEventType %d",
+			   wlan_event_type);
+		break;
+	}
+}
+
+
+static void wps_er_http_event(struct wps_er *er, struct http_request *req,
+			      unsigned int ap_id)
+{
+	struct wps_er_ap *ap = wps_er_ap_get_id(er, ap_id);
+	struct wpabuf *event;
+	enum http_reply_code ret;
+
+	if (ap == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: HTTP event from unknown AP id "
+			   "%u", ap_id);
+		wps_er_http_resp_not_found(req);
+		return;
+	}
+	wpa_printf(MSG_MSGDUMP, "WPS ER: HTTP event from AP id %u: %s",
+		   ap_id, http_request_get_data(req));
+
+	event = xml_get_base64_item(http_request_get_data(req), "WLANEvent",
+				    &ret);
+	if (event == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Could not extract WLANEvent "
+			   "from the event notification");
+		/*
+		 * Reply with OK anyway to avoid getting unregistered from
+		 * events.
+		 */
+		wps_er_http_resp_ok(req);
+		return;
+	}
+
+	wps_er_process_wlanevent(ap, event);
+
+	wpabuf_free(event);
+	wps_er_http_resp_ok(req);
+}
+
+
+static void wps_er_http_notify(struct wps_er *er, struct http_request *req)
+{
+	char *uri = http_request_get_uri(req);
+
+	if (os_strncmp(uri, "/event/", 7) == 0) {
+		unsigned int event_id;
+		char *pos;
+		event_id = atoi(uri + 7);
+		if (event_id != er->event_id) {
+			wpa_printf(MSG_DEBUG, "WPS ER: HTTP event for an "
+				   "unknown event id %u", event_id);
+			return;
+		}
+		pos = os_strchr(uri + 7, '/');
+		if (pos == NULL)
+			return;
+		pos++;
+		wps_er_http_event(er, req, atoi(pos));
+	} else {
+		wpa_printf(MSG_DEBUG, "WPS ER: Unknown HTTP NOTIFY for '%s'",
+			   uri);
+		wps_er_http_resp_not_found(req);
+	}
+}
+
+
+static void wps_er_http_req(void *ctx, struct http_request *req)
+{
+	struct wps_er *er = ctx;
+	struct sockaddr_in *cli = http_request_get_cli_addr(req);
+	enum httpread_hdr_type type = http_request_get_type(req);
+	struct wpabuf *buf;
+
+	wpa_printf(MSG_DEBUG, "WPS ER: HTTP request: '%s' (type %d) from "
+		   "%s:%d",
+		   http_request_get_uri(req), type,
+		   inet_ntoa(cli->sin_addr), ntohs(cli->sin_port));
+
+	switch (type) {
+	case HTTPREAD_HDR_TYPE_NOTIFY:
+		wps_er_http_notify(er, req);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "WPS ER: Unsupported HTTP request type "
+			   "%d", type);
+		buf = wpabuf_alloc(200);
+		if (buf == NULL) {
+			http_request_deinit(req);
+			return;
+		}
+		wpabuf_put_str(buf,
+			       "HTTP/1.1 501 Unimplemented\r\n"
+			       "Connection: close\r\n");
+		http_put_date(buf);
+		wpabuf_put_str(buf, "\r\n");
+		http_request_send_and_deinit(req, buf);
+		break;
+	}
+}
+
+
+struct wps_er *
+wps_er_init(struct wps_context *wps, const char *ifname, const char *filter)
+{
+	struct wps_er *er;
+	struct in_addr addr;
+
+	er = os_zalloc(sizeof(*er));
+	if (er == NULL)
+		return NULL;
+	dl_list_init(&er->ap);
+	dl_list_init(&er->ap_unsubscribing);
+	dl_list_init(&er->ap_settings);
+
+	er->multicast_sd = -1;
+	er->ssdp_sd = -1;
+
+	os_strlcpy(er->ifname, ifname, sizeof(er->ifname));
+	er->wps = wps;
+	if (os_get_random((unsigned char *) &er->event_id,
+			  sizeof(er->event_id)) < 0) {
+		wps_er_deinit(er, NULL, NULL);
+		return NULL;
+	}
+	/* Limit event_id to < 32 bits to avoid issues with atoi() */
+	er->event_id &= 0x0fffffff;
+
+	if (filter && os_strncmp(filter, "ifname=", 7) == 0) {
+		const char *pos, *end;
+		pos = filter + 7;
+		end = os_strchr(pos, ' ');
+		if (end) {
+			size_t len = end - pos;
+			os_strlcpy(er->ifname, pos, len < sizeof(er->ifname) ?
+				   len + 1 : sizeof(er->ifname));
+			filter = end + 1;
+		} else {
+			os_strlcpy(er->ifname, pos, sizeof(er->ifname));
+			filter = NULL;
+		}
+		er->forced_ifname = 1;
+	}
+
+	if (filter) {
+		if (inet_aton(filter, &er->filter_addr) == 0) {
+			wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter "
+				   "address %s", filter);
+			wps_er_deinit(er, NULL, NULL);
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections "
+			   "with %s", filter);
+	}
+	if (get_netif_info(er->ifname, &er->ip_addr, &er->ip_addr_text,
+			   er->mac_addr)) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
+			   "for %s. Does it have IP address?", er->ifname);
+		wps_er_deinit(er, NULL, NULL);
+		return NULL;
+	}
+
+	if (wps_er_ssdp_init(er) < 0) {
+		wpa_printf(MSG_INFO, "WPS UPnP: SSDP initialization failed");
+		wps_er_deinit(er, NULL, NULL);
+		return NULL;
+	}
+
+	addr.s_addr = er->ip_addr;
+	er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er);
+	if (er->http_srv == NULL) {
+		wpa_printf(MSG_INFO, "WPS UPnP: HTTP initialization failed");
+		wps_er_deinit(er, NULL, NULL);
+		return NULL;
+	}
+	er->http_port = http_server_get_port(er->http_srv);
+
+	wpa_printf(MSG_DEBUG, "WPS ER: Start (ifname=%s ip_addr=%s)",
+		   er->ifname, er->ip_addr_text);
+
+	return er;
+}
+
+
+void wps_er_refresh(struct wps_er *er)
+{
+	struct wps_er_ap *ap;
+	struct wps_er_sta *sta;
+
+	dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+		wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_ADD);
+		dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list)
+			wps_er_sta_event(er->wps, sta, WPS_EV_ER_ENROLLEE_ADD);
+	}
+
+	wps_er_send_ssdp_msearch(er);
+}
+
+
+static void wps_er_deinit_finish(void *eloop_data, void *user_ctx)
+{
+	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);
+	os_free(er);
+
+	if (deinit_done_cb)
+		deinit_done_cb(deinit_done_ctx);
+}
+
+
+void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx)
+{
+	if (er == NULL)
+		return;
+	http_server_deinit(er->http_srv);
+	wps_er_ap_remove_all(er);
+	wps_er_ssdp_deinit(er);
+	eloop_register_timeout(dl_list_empty(&er->ap_unsubscribing) ? 0 : 5, 0,
+			       wps_er_deinit_finish, er, NULL);
+	wpa_printf(MSG_DEBUG, "WPS ER: Finish deinit from timeout");
+	er->deinitializing = 1;
+	er->deinit_done_cb = cb;
+	er->deinit_done_ctx = ctx;
+}
+
+
+static void wps_er_http_set_sel_reg_cb(void *ctx, struct http_client *c,
+				       enum http_client_event event)
+{
+	struct wps_er_ap *ap = ctx;
+	union wps_event_data data;
+
+	os_memset(&data, 0, sizeof(data));
+
+	switch (event) {
+	case HTTP_CLIENT_OK:
+		wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK");
+		data.set_sel_reg.state = WPS_ER_SET_SEL_REG_DONE;
+		data.set_sel_reg.uuid = ap->uuid;
+		break;
+	case HTTP_CLIENT_FAILED:
+	case HTTP_CLIENT_INVALID_REPLY:
+	case HTTP_CLIENT_TIMEOUT:
+		wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed");
+		data.set_sel_reg.state = WPS_ER_SET_SEL_REG_FAILED;
+		data.set_sel_reg.uuid = ap->uuid;
+		break;
+	}
+	http_client_free(ap->http);
+	ap->http = NULL;
+
+	if (data.set_sel_reg.uuid)
+		ap->er->wps->event_cb(ap->er->wps->cb_ctx,
+				      WPS_EV_ER_SET_SELECTED_REGISTRAR, &data);
+}
+
+
+static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg)
+{
+	struct wpabuf *buf;
+	char *len_ptr, *body_ptr;
+	struct sockaddr_in dst;
+	char *url, *path;
+
+	if (ap->control_url == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
+		return;
+	}
+
+	if (ap->http) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for AP - "
+			   "ignore new request");
+		return;
+	}
+
+	if (ap->wps) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Pending WPS operation for AP - "
+			   "skip SetSelectedRegistrar");
+		return;
+	}
+
+	url = http_client_url_parse(ap->control_url, &dst, &path);
+	if (url == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
+		return;
+	}
+
+	buf = wps_er_soap_hdr(msg, "SetSelectedRegistrar", "NewMessage", path,
+			      &dst, &len_ptr, &body_ptr);
+	os_free(url);
+	if (buf == NULL)
+		return;
+
+	wps_er_soap_end(buf, "SetSelectedRegistrar", len_ptr, body_ptr);
+
+	ap->http = http_client_addr(&dst, buf, 1000,
+				    wps_er_http_set_sel_reg_cb, ap);
+	if (ap->http == NULL)
+		wpabuf_free(buf);
+}
+
+
+static int wps_er_build_selected_registrar(struct wpabuf *msg, int sel_reg)
+{
+	wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, !!sel_reg);
+	return 0;
+}
+
+
+static int wps_er_build_dev_password_id(struct wpabuf *msg, u16 dev_passwd_id)
+{
+	wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, dev_passwd_id);
+	return 0;
+}
+
+
+static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg,
+					       u16 sel_reg_config_methods)
+{
+	wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, sel_reg_config_methods);
+	return 0;
+}
+
+
+static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r)
+{
+	wpabuf_put_be16(msg, ATTR_UUID_R);
+	wpabuf_put_be16(msg, WPS_UUID_LEN);
+	wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN);
+	return 0;
+}
+
+
+void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
+			u16 sel_reg_config_methods)
+{
+	struct wpabuf *msg;
+	struct wps_er_ap *ap;
+	struct wps_registrar *reg = er->wps->registrar;
+	const u8 *auth_macs;
+	u8 bcast[ETH_ALEN];
+	size_t count;
+	union wps_event_data data;
+
+	if (er->skip_set_sel_reg) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Skip SetSelectedRegistrar");
+		return;
+	}
+
+	msg = wpabuf_alloc(500);
+	if (msg == NULL)
+		return;
+
+	auth_macs = wps_authorized_macs(reg, &count);
+	if (count == 0) {
+		os_memset(bcast, 0xff, ETH_ALEN);
+		auth_macs = bcast;
+		count = 1;
+	}
+
+	if (wps_build_version(msg) ||
+	    wps_er_build_selected_registrar(msg, sel_reg) ||
+	    wps_er_build_dev_password_id(msg, dev_passwd_id) ||
+	    wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) ||
+	    wps_build_wfa_ext(msg, 0, auth_macs, count) ||
+	    wps_er_build_uuid_r(msg, er->wps->uuid)) {
+		wpabuf_free(msg);
+		return;
+	}
+
+	os_memset(&data, 0, sizeof(data));
+	data.set_sel_reg.sel_reg = sel_reg;
+	data.set_sel_reg.dev_passwd_id = dev_passwd_id;
+	data.set_sel_reg.sel_reg_config_methods = sel_reg_config_methods;
+	data.set_sel_reg.state = WPS_ER_SET_SEL_REG_START;
+
+	dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+		if (er->set_sel_reg_uuid_filter &&
+		    os_memcmp(ap->uuid, er->set_sel_reg_uuid_filter,
+			      WPS_UUID_LEN) != 0)
+			continue;
+		data.set_sel_reg.uuid = ap->uuid;
+		er->wps->event_cb(er->wps->cb_ctx,
+				  WPS_EV_ER_SET_SELECTED_REGISTRAR, &data);
+		wps_er_send_set_sel_reg(ap, msg);
+	}
+
+	wpabuf_free(msg);
+}
+
+
+int wps_er_pbc(struct wps_er *er, const u8 *uuid, const u8 *addr)
+{
+	int res;
+	struct wps_er_ap *ap;
+
+	if (er == NULL || er->wps == NULL)
+		return -1;
+
+	if (wps_registrar_pbc_overlap(er->wps->registrar, NULL, NULL)) {
+		wpa_printf(MSG_DEBUG, "WPS ER: PBC overlap - do not start PBC "
+			   "mode");
+		return -2;
+	}
+
+	if (uuid)
+		ap = wps_er_ap_get(er, NULL, uuid, NULL);
+	else
+		ap = NULL;
+	if (ap == NULL) {
+		struct wps_er_sta *sta = NULL;
+		dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+			sta = wps_er_sta_get(ap, addr, uuid);
+			if (sta) {
+				uuid = ap->uuid;
+				break;
+			}
+		}
+		if (sta == NULL)
+			return -3; /* Unknown UUID */
+	}
+
+	if (ap->ap_settings == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: AP settings not known");
+		return -4;
+	}
+
+	er->set_sel_reg_uuid_filter = uuid;
+	res = wps_registrar_button_pushed(er->wps->registrar, NULL);
+	er->set_sel_reg_uuid_filter = NULL;
+	if (res)
+		return -1;
+
+	return 0;
+}
+
+
+static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred)
+{
+	struct wps_er_ap *ap = ctx;
+	union wps_event_data data;
+
+	wpa_printf(MSG_DEBUG, "WPS ER: AP Settings received");
+	os_free(ap->ap_settings);
+	ap->ap_settings = os_malloc(sizeof(*cred));
+	if (ap->ap_settings) {
+		os_memcpy(ap->ap_settings, cred, sizeof(*cred));
+		ap->ap_settings->cred_attr = NULL;
+	}
+
+	os_memset(&data, 0, sizeof(data));
+	data.ap_settings.uuid = ap->uuid;
+	data.ap_settings.cred = cred;
+	ap->er->wps->event_cb(ap->er->wps->cb_ctx, WPS_EV_ER_AP_SETTINGS,
+			      &data);
+}
+
+
+const u8 * wps_er_get_sta_uuid(struct wps_er *er, const u8 *addr)
+{
+	struct wps_er_ap *ap;
+	dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+		struct wps_er_sta *sta;
+		sta = wps_er_sta_get(ap, addr, NULL);
+		if (sta)
+			return sta->uuid;
+	}
+	return NULL;
+}
+
+
+static void wps_er_http_put_message_cb(void *ctx, struct http_client *c,
+				       enum http_client_event event)
+{
+	struct wps_er_ap *ap = ctx;
+	struct wpabuf *reply;
+	char *msg = NULL;
+
+	switch (event) {
+	case HTTP_CLIENT_OK:
+		wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK");
+		reply = http_client_get_body(c);
+		if (reply)
+			msg = os_zalloc(wpabuf_len(reply) + 1);
+		if (msg == NULL) {
+			if (ap->wps) {
+				wps_deinit(ap->wps);
+				ap->wps = NULL;
+			}
+			break;
+		}
+		os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply));
+		break;
+	case HTTP_CLIENT_FAILED:
+	case HTTP_CLIENT_INVALID_REPLY:
+	case HTTP_CLIENT_TIMEOUT:
+		wpa_printf(MSG_DEBUG, "WPS ER: PutMessage failed");
+		if (ap->wps) {
+			wps_deinit(ap->wps);
+			ap->wps = NULL;
+		}
+		break;
+	}
+	http_client_free(ap->http);
+	ap->http = NULL;
+
+	if (msg) {
+		struct wpabuf *buf;
+		enum http_reply_code ret;
+		buf = xml_get_base64_item(msg, "NewOutMessage", &ret);
+		os_free(msg);
+		if (buf == NULL) {
+			wpa_printf(MSG_DEBUG, "WPS ER: Could not extract "
+				   "NewOutMessage from PutMessage response");
+			wps_deinit(ap->wps);
+			ap->wps = NULL;
+			return;
+		}
+		wps_er_ap_process(ap, buf);
+		wpabuf_free(buf);
+	}
+}
+
+
+static void wps_er_ap_put_message(struct wps_er_ap *ap,
+				  const struct wpabuf *msg)
+{
+	struct wpabuf *buf;
+	char *len_ptr, *body_ptr;
+	struct sockaddr_in dst;
+	char *url, *path;
+
+	if (ap->http) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing "
+			   "with the AP - cannot continue learn");
+		return;
+	}
+
+	if (ap->control_url == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
+		return;
+	}
+
+	url = http_client_url_parse(ap->control_url, &dst, &path);
+	if (url == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
+		goto fail;
+	}
+
+	buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst,
+			      &len_ptr, &body_ptr);
+	os_free(url);
+	if (buf == NULL)
+		goto fail;
+
+	wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr);
+
+	ap->http = http_client_addr(&dst, buf, 10000,
+				    wps_er_http_put_message_cb, ap);
+	if (ap->http == NULL) {
+		wpabuf_free(buf);
+		goto fail;
+	}
+	return;
+
+fail:
+	if (ap->wps) {
+		wps_deinit(ap->wps);
+		ap->wps = NULL;
+	}
+}
+
+
+static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg)
+{
+	enum wps_process_res res;
+	struct wps_parse_attr attr;
+	enum wsc_op_code op_code;
+
+	op_code = WSC_MSG;
+	if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
+		switch (*attr.msg_type) {
+		case WPS_WSC_ACK:
+			op_code = WSC_ACK;
+			break;
+		case WPS_WSC_NACK:
+			op_code = WSC_NACK;
+			break;
+		case WPS_WSC_DONE:
+			op_code = WSC_Done;
+			break;
+		}
+	}
+
+	res = wps_process_msg(ap->wps, op_code, msg);
+	if (res == WPS_CONTINUE) {
+		struct wpabuf *next = wps_get_msg(ap->wps, &op_code);
+		if (next) {
+			wps_er_ap_put_message(ap, next);
+			wpabuf_free(next);
+		} else {
+			wpa_printf(MSG_DEBUG, "WPS ER: Failed to build "
+				   "message");
+			wps_deinit(ap->wps);
+			ap->wps = NULL;
+		}
+	} else if (res == WPS_DONE) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Protocol run done");
+		wps_deinit(ap->wps);
+		ap->wps = NULL;
+	} else {
+		wpa_printf(MSG_DEBUG, "WPS ER: Failed to process message from "
+			   "AP (res=%d)", res);
+		wps_deinit(ap->wps);
+		ap->wps = NULL;
+	}
+}
+
+
+static void wps_er_ap_learn_m1(struct wps_er_ap *ap, struct wpabuf *m1)
+{
+	struct wps_config cfg;
+
+	if (ap->wps) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in "
+			   "progress with this AP");
+		return;
+	}
+
+	os_memset(&cfg, 0, sizeof(cfg));
+	cfg.wps = ap->er->wps;
+	cfg.registrar = 1;
+	ap->wps = wps_init(&cfg);
+	if (ap->wps == NULL)
+		return;
+	ap->wps->ap_settings_cb = wps_er_ap_settings_cb;
+	ap->wps->ap_settings_cb_ctx = ap;
+
+	wps_er_ap_process(ap, m1);
+}
+
+
+static void wps_er_ap_learn(struct wps_er_ap *ap, const char *dev_info)
+{
+	struct wpabuf *info;
+	enum http_reply_code ret;
+
+	wpa_printf(MSG_DEBUG, "WPS ER: Received GetDeviceInfo response (M1) "
+		   "from the AP");
+	info = xml_get_base64_item(dev_info, "NewDeviceInfo", &ret);
+	if (info == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Could not extract "
+			   "NewDeviceInfo from GetDeviceInfo response");
+		return;
+	}
+
+	ap->m1_handler(ap, info);
+	wpabuf_free(info);
+}
+
+
+static void wps_er_http_get_dev_info_cb(void *ctx, struct http_client *c,
+					enum http_client_event event)
+{
+	struct wps_er_ap *ap = ctx;
+	struct wpabuf *reply;
+	char *dev_info = NULL;
+
+	switch (event) {
+	case HTTP_CLIENT_OK:
+		wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo OK");
+		reply = http_client_get_body(c);
+		if (reply == NULL)
+			break;
+		dev_info = os_zalloc(wpabuf_len(reply) + 1);
+		if (dev_info == NULL)
+			break;
+		os_memcpy(dev_info, wpabuf_head(reply), wpabuf_len(reply));
+		break;
+	case HTTP_CLIENT_FAILED:
+	case HTTP_CLIENT_INVALID_REPLY:
+	case HTTP_CLIENT_TIMEOUT:
+		wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo failed");
+		break;
+	}
+	http_client_free(ap->http);
+	ap->http = NULL;
+
+	if (dev_info) {
+		wps_er_ap_learn(ap, dev_info);
+		os_free(dev_info);
+	}
+}
+
+
+static int wps_er_send_get_device_info(struct wps_er_ap *ap,
+				       void (*m1_handler)(struct wps_er_ap *ap,
+							  struct wpabuf *m1))
+{
+	struct wpabuf *buf;
+	char *len_ptr, *body_ptr;
+	struct sockaddr_in dst;
+	char *url, *path;
+
+	if (ap->http) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing "
+			   "with the AP - cannot get device info");
+		return -1;
+	}
+
+	if (ap->control_url == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
+		return -1;
+	}
+
+	url = http_client_url_parse(ap->control_url, &dst, &path);
+	if (url == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
+		return -1;
+	}
+
+	buf = wps_er_soap_hdr(NULL, "GetDeviceInfo", NULL, path, &dst,
+			      &len_ptr, &body_ptr);
+	os_free(url);
+	if (buf == NULL)
+		return -1;
+
+	wps_er_soap_end(buf, "GetDeviceInfo", len_ptr, body_ptr);
+
+	ap->http = http_client_addr(&dst, buf, 10000,
+				    wps_er_http_get_dev_info_cb, ap);
+	if (ap->http == NULL) {
+		wpabuf_free(buf);
+		return -1;
+	}
+
+	ap->m1_handler = m1_handler;
+
+	return 0;
+}
+
+
+int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *addr,
+		 const u8 *pin, size_t pin_len)
+{
+	struct wps_er_ap *ap;
+
+	if (er == NULL)
+		return -1;
+
+	ap = wps_er_ap_get(er, NULL, uuid, addr);
+	if (ap == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: AP not found for learn "
+			   "request");
+		return -1;
+	}
+	if (uuid == NULL)
+		uuid = ap->uuid;
+	if (ap->wps) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing "
+			   "with the AP - cannot start learn");
+		return -1;
+	}
+
+	if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0)
+		return -1;
+
+	er->skip_set_sel_reg = 1;
+	wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0);
+	er->skip_set_sel_reg = 0;
+
+	return 0;
+}
+
+
+int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr,
+		      const struct wps_credential *cred)
+{
+	struct wps_er_ap *ap;
+
+	if (er == NULL)
+		return -1;
+
+	ap = wps_er_ap_get(er, NULL, uuid, addr);
+	if (ap == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: AP not found for set config "
+			   "request");
+		return -1;
+	}
+
+	os_free(ap->ap_settings);
+	ap->ap_settings = os_malloc(sizeof(*cred));
+	if (ap->ap_settings == NULL)
+		return -1;
+	os_memcpy(ap->ap_settings, cred, sizeof(*cred));
+	ap->ap_settings->cred_attr = NULL;
+	wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set "
+		   "config request");
+
+	return 0;
+}
+
+
+static void wps_er_ap_config_m1(struct wps_er_ap *ap, struct wpabuf *m1)
+{
+	struct wps_config cfg;
+
+	if (ap->wps) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in "
+			   "progress with this AP");
+		return;
+	}
+
+	os_memset(&cfg, 0, sizeof(cfg));
+	cfg.wps = ap->er->wps;
+	cfg.registrar = 1;
+	cfg.new_ap_settings = ap->ap_settings;
+	ap->wps = wps_init(&cfg);
+	if (ap->wps == NULL)
+		return;
+	ap->wps->ap_settings_cb = NULL;
+	ap->wps->ap_settings_cb_ctx = NULL;
+
+	wps_er_ap_process(ap, m1);
+}
+
+
+int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr,
+		  const u8 *pin, size_t pin_len,
+		  const struct wps_credential *cred)
+{
+	struct wps_er_ap *ap;
+
+	if (er == NULL)
+		return -1;
+
+	ap = wps_er_ap_get(er, NULL, uuid, addr);
+	if (ap == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config "
+			   "request");
+		return -1;
+	}
+	if (uuid == NULL)
+		uuid = ap->uuid;
+	if (ap->wps) {
+		wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing "
+			   "with the AP - cannot start config");
+		return -1;
+	}
+
+	os_free(ap->ap_settings);
+	ap->ap_settings = os_malloc(sizeof(*cred));
+	if (ap->ap_settings == NULL)
+		return -1;
+	os_memcpy(ap->ap_settings, cred, sizeof(*cred));
+	ap->ap_settings->cred_attr = NULL;
+
+	if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0)
+		return -1;
+
+	er->skip_set_sel_reg = 1;
+	wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0);
+	er->skip_set_sel_reg = 0;
+
+	return 0;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps,
+					      struct wps_credential *cred)
+{
+	struct wpabuf *ret;
+	struct wps_data data;
+
+	ret = wpabuf_alloc(500);
+	if (ret == NULL)
+		return NULL;
+
+	os_memset(&data, 0, sizeof(data));
+	data.wps = wps;
+	data.use_cred = cred;
+	if (wps_build_cred(&data, ret) ||
+	    wps_build_wfa_ext(ret, 0, NULL, 0)) {
+		wpabuf_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+
+struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid,
+					const u8 *addr)
+{
+	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;
+	}
+
+	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/hostap/src/wps/wps_er.h b/hostap/src/wps/wps_er.h
new file mode 100644
index 0000000..4b48ff6
--- /dev/null
+++ b/hostap/src/wps/wps_er.h
@@ -0,0 +1,112 @@
+/*
+ * Wi-Fi Protected Setup - External Registrar
+ * 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.
+ */
+
+#ifndef WPS_ER_H
+#define WPS_ER_H
+
+#include "utils/list.h"
+
+struct wps_er_sta {
+	struct dl_list list;
+	struct wps_er_ap *ap;
+	u8 addr[ETH_ALEN];
+	u16 config_methods;
+	u8 uuid[WPS_UUID_LEN];
+	u8 pri_dev_type[8];
+	u16 dev_passwd_id;
+	int m1_received;
+	char *manufacturer;
+	char *model_name;
+	char *model_number;
+	char *serial_number;
+	char *dev_name;
+	struct wps_data *wps;
+	struct http_client *http;
+	struct wps_credential *cred;
+};
+
+struct wps_er_ap {
+	struct dl_list list;
+	struct wps_er *er;
+	struct dl_list sta; /* list of STAs/Enrollees using this AP */
+	struct in_addr addr;
+	char *location;
+	struct http_client *http;
+	struct wps_data *wps;
+
+	u8 uuid[WPS_UUID_LEN];
+	u8 pri_dev_type[8];
+	u8 wps_state;
+	u8 mac_addr[ETH_ALEN];
+	char *friendly_name;
+	char *manufacturer;
+	char *manufacturer_url;
+	char *model_description;
+	char *model_name;
+	char *model_number;
+	char *model_url;
+	char *serial_number;
+	char *udn;
+	char *upc;
+
+	char *scpd_url;
+	char *control_url;
+	char *event_sub_url;
+
+	int subscribed;
+	u8 sid[WPS_UUID_LEN];
+	unsigned int id;
+
+	struct wps_credential *ap_settings;
+
+	void (*m1_handler)(struct wps_er_ap *ap, struct wpabuf *m1);
+};
+
+struct wps_er_ap_settings {
+	struct dl_list list;
+	u8 uuid[WPS_UUID_LEN];
+	struct wps_credential ap_settings;
+};
+
+struct wps_er {
+	struct wps_context *wps;
+	char ifname[17];
+	int forced_ifname;
+	u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
+	char *ip_addr_text; /* IP address of network i.f. we use */
+	unsigned ip_addr; /* IP address of network i.f. we use (host order) */
+	int multicast_sd;
+	int ssdp_sd;
+	struct dl_list ap;
+	struct dl_list ap_unsubscribing;
+	struct dl_list ap_settings;
+	struct http_server *http_srv;
+	int http_port;
+	unsigned int next_ap_id;
+	unsigned int event_id;
+	int deinitializing;
+	void (*deinit_done_cb)(void *ctx);
+	void *deinit_done_ctx;
+	struct in_addr filter_addr;
+	int skip_set_sel_reg;
+	const u8 *set_sel_reg_uuid_filter;
+};
+
+
+/* wps_er.c */
+void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr,
+		   const char *location, int max_age);
+void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr);
+int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr);
+
+/* wps_er_ssdp.c */
+int wps_er_ssdp_init(struct wps_er *er);
+void wps_er_ssdp_deinit(struct wps_er *er);
+void wps_er_send_ssdp_msearch(struct wps_er *er);
+
+#endif /* WPS_ER_H */
diff --git a/hostap/src/wps/wps_er_ssdp.c b/hostap/src/wps/wps_er_ssdp.c
new file mode 100644
index 0000000..280b2b3
--- /dev/null
+++ b/hostap/src/wps/wps_er_ssdp.c
@@ -0,0 +1,205 @@
+/*
+ * Wi-Fi Protected Setup - External Registrar (SSDP)
+ * 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 "uuid.h"
+#include "eloop.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+#include "wps_er.h"
+
+
+static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx)
+{
+	struct wps_er *er = eloop_ctx;
+	struct sockaddr_in addr; /* client address */
+	socklen_t addr_len;
+	int nread;
+	char buf[MULTICAST_MAX_READ], *pos, *pos2, *start;
+	int wfa = 0, byebye = 0;
+	int max_age = -1;
+	char *location = NULL;
+	u8 uuid[WPS_UUID_LEN];
+
+	addr_len = sizeof(addr);
+	nread = recvfrom(sd, buf, sizeof(buf) - 1, 0,
+			 (struct sockaddr *) &addr, &addr_len);
+	if (nread <= 0)
+		return;
+	buf[nread] = '\0';
+	if (er->filter_addr.s_addr &&
+	    er->filter_addr.s_addr != addr.sin_addr.s_addr)
+		return;
+
+	wpa_printf(MSG_DEBUG, "WPS ER: Received SSDP from %s",
+		   inet_ntoa(addr.sin_addr));
+	wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Received SSDP contents",
+			  (u8 *) buf, nread);
+
+	if (sd == er->multicast_sd) {
+		/* Reply to M-SEARCH */
+		if (os_strncmp(buf, "HTTP/1.1 200 OK", 15) != 0)
+			return; /* unexpected response header */
+	} else {
+		/* Unsolicited message (likely NOTIFY or M-SEARCH) */
+		if (os_strncmp(buf, "NOTIFY ", 7) != 0)
+			return; /* only process notifications */
+	}
+
+	os_memset(uuid, 0, sizeof(uuid));
+
+	for (start = buf; start && *start; start = pos) {
+		pos = os_strchr(start, '\n');
+		if (pos) {
+			if (pos[-1] == '\r')
+				pos[-1] = '\0';
+			*pos++ = '\0';
+		}
+		if (os_strstr(start, "schemas-wifialliance-org:device:"
+			      "WFADevice:1"))
+			wfa = 1;
+		if (os_strstr(start, "schemas-wifialliance-org:service:"
+			      "WFAWLANConfig:1"))
+			wfa = 1;
+		if (os_strncasecmp(start, "LOCATION:", 9) == 0) {
+			start += 9;
+			while (*start == ' ')
+				start++;
+			location = start;
+		} else if (os_strncasecmp(start, "NTS:", 4) == 0) {
+			if (os_strstr(start, "ssdp:byebye"))
+				byebye = 1;
+		} else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) {
+			start += 14;
+			pos2 = os_strstr(start, "max-age=");
+			if (pos2 == NULL)
+				continue;
+			pos2 += 8;
+			max_age = atoi(pos2);
+		} else if (os_strncasecmp(start, "USN:", 4) == 0) {
+			start += 4;
+			pos2 = os_strstr(start, "uuid:");
+			if (pos2) {
+				pos2 += 5;
+				while (*pos2 == ' ')
+					pos2++;
+				if (uuid_str2bin(pos2, uuid) < 0) {
+					wpa_printf(MSG_DEBUG, "WPS ER: "
+						   "Invalid UUID in USN: %s",
+						   pos2);
+					return;
+				}
+			}
+		}
+	}
+
+	if (!wfa)
+		return; /* Not WPS advertisement/reply */
+
+	if (byebye) {
+		wps_er_ap_cache_settings(er, &addr.sin_addr);
+		wps_er_ap_remove(er, &addr.sin_addr);
+		return;
+	}
+
+	if (!location)
+		return; /* Unknown location */
+
+	if (max_age < 1)
+		return; /* No max-age reported */
+
+	wpa_printf(MSG_DEBUG, "WPS ER: AP discovered: %s "
+		   "(packet source: %s  max-age: %d)",
+		   location, inet_ntoa(addr.sin_addr), max_age);
+
+	wps_er_ap_add(er, uuid, &addr.sin_addr, location, max_age);
+}
+
+
+void wps_er_send_ssdp_msearch(struct wps_er *er)
+{
+	struct wpabuf *msg;
+	struct sockaddr_in dest;
+
+	msg = wpabuf_alloc(500);
+	if (msg == NULL)
+		return;
+
+	wpabuf_put_str(msg,
+		       "M-SEARCH * HTTP/1.1\r\n"
+		       "HOST: 239.255.255.250:1900\r\n"
+		       "MAN: \"ssdp:discover\"\r\n"
+		       "MX: 3\r\n"
+		       "ST: urn:schemas-wifialliance-org:device:WFADevice:1"
+		       "\r\n"
+		       "\r\n");
+
+	os_memset(&dest, 0, sizeof(dest));
+	dest.sin_family = AF_INET;
+	dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+	dest.sin_port = htons(UPNP_MULTICAST_PORT);
+
+	if (sendto(er->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
+		   (struct sockaddr *) &dest, sizeof(dest)) < 0)
+		wpa_printf(MSG_DEBUG, "WPS ER: M-SEARCH sendto failed: "
+			   "%d (%s)", errno, strerror(errno));
+
+	wpabuf_free(msg);
+}
+
+
+int wps_er_ssdp_init(struct wps_er *er)
+{
+	if (add_ssdp_network(er->ifname)) {
+		wpa_printf(MSG_INFO, "WPS ER: Failed to add routing entry for "
+			   "SSDP");
+		return -1;
+	}
+
+	er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr,
+						    er->forced_ifname ?
+						    er->ifname : NULL);
+	if (er->multicast_sd < 0) {
+		wpa_printf(MSG_INFO, "WPS ER: Failed to open multicast socket "
+			   "for SSDP");
+		return -1;
+	}
+
+	er->ssdp_sd = ssdp_listener_open();
+	if (er->ssdp_sd < 0) {
+		wpa_printf(MSG_INFO, "WPS ER: Failed to open SSDP listener "
+			   "socket");
+		return -1;
+	}
+
+	if (eloop_register_sock(er->multicast_sd, EVENT_TYPE_READ,
+				wps_er_ssdp_rx, er, NULL) ||
+	    eloop_register_sock(er->ssdp_sd, EVENT_TYPE_READ,
+				wps_er_ssdp_rx, er, NULL))
+		return -1;
+
+	wps_er_send_ssdp_msearch(er);
+
+	return 0;
+}
+
+
+void wps_er_ssdp_deinit(struct wps_er *er)
+{
+	if (er->multicast_sd >= 0) {
+		eloop_unregister_sock(er->multicast_sd, EVENT_TYPE_READ);
+		close(er->multicast_sd);
+	}
+	if (er->ssdp_sd >= 0) {
+		eloop_unregister_sock(er->ssdp_sd, EVENT_TYPE_READ);
+		close(er->ssdp_sd);
+	}
+}
diff --git a/hostap/src/wps/wps_i.h b/hostap/src/wps/wps_i.h
new file mode 100644
index 0000000..f7154f8
--- /dev/null
+++ b/hostap/src/wps/wps_i.h
@@ -0,0 +1,218 @@
+/*
+ * Wi-Fi Protected Setup - internal definitions
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_I_H
+#define WPS_I_H
+
+#include "wps.h"
+#include "wps_attr_parse.h"
+
+struct wps_nfc_pw_token;
+
+/**
+ * struct wps_data - WPS registration protocol data
+ *
+ * This data is stored at the EAP-WSC server/peer method and it is kept for a
+ * single registration protocol run.
+ */
+struct wps_data {
+	/**
+	 * wps - Pointer to long term WPS context
+	 */
+	struct wps_context *wps;
+
+	/**
+	 * registrar - Whether this end is a Registrar
+	 */
+	int registrar;
+
+	/**
+	 * er - Whether the local end is an external registrar
+	 */
+	int er;
+
+	enum {
+		/* Enrollee states */
+		SEND_M1, RECV_M2, SEND_M3, RECV_M4, SEND_M5, RECV_M6, SEND_M7,
+		RECV_M8, RECEIVED_M2D, WPS_MSG_DONE, RECV_ACK, WPS_FINISHED,
+		SEND_WSC_NACK,
+
+		/* Registrar states */
+		RECV_M1, SEND_M2, RECV_M3, SEND_M4, RECV_M5, SEND_M6,
+		RECV_M7, SEND_M8, RECV_DONE, SEND_M2D, RECV_M2D_ACK
+	} state;
+
+	u8 uuid_e[WPS_UUID_LEN];
+	u8 uuid_r[WPS_UUID_LEN];
+	u8 mac_addr_e[ETH_ALEN];
+	u8 nonce_e[WPS_NONCE_LEN];
+	u8 nonce_r[WPS_NONCE_LEN];
+	u8 psk1[WPS_PSK_LEN];
+	u8 psk2[WPS_PSK_LEN];
+	u8 snonce[2 * WPS_SECRET_NONCE_LEN];
+	u8 peer_hash1[WPS_HASH_LEN];
+	u8 peer_hash2[WPS_HASH_LEN];
+
+	struct wpabuf *dh_privkey;
+	struct wpabuf *dh_pubkey_e;
+	struct wpabuf *dh_pubkey_r;
+	u8 authkey[WPS_AUTHKEY_LEN];
+	u8 keywrapkey[WPS_KEYWRAPKEY_LEN];
+	u8 emsk[WPS_EMSK_LEN];
+
+	struct wpabuf *last_msg;
+
+	u8 *dev_password;
+	size_t dev_password_len;
+	u16 dev_pw_id;
+	int pbc;
+	u8 *alt_dev_password;
+	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
+	 */
+	u8 request_type;
+
+	/**
+	 * encr_type - Available encryption types
+	 */
+	u16 encr_type;
+
+	/**
+	 * auth_type - Available authentication types
+	 */
+	u16 auth_type;
+
+	u8 *new_psk;
+	size_t new_psk_len;
+
+	int wps_pin_revealed;
+	struct wps_credential cred;
+
+	struct wps_device_data peer_dev;
+
+	/**
+	 * config_error - Configuration Error value to be used in NACK
+	 */
+	u16 config_error;
+	u16 error_indication;
+
+	int ext_reg;
+	int int_reg;
+
+	struct wps_credential *new_ap_settings;
+
+	void *dh_ctx;
+
+	void (*ap_settings_cb)(void *ctx, const struct wps_credential *cred);
+	void *ap_settings_cb_ctx;
+
+	struct wps_credential *use_cred;
+
+	int use_psk_key;
+	u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or
+				    * 00:00:00:00:00:00 if not a P2p client */
+	int pbc_in_m1;
+
+	struct wps_nfc_pw_token *nfc_pw_token;
+};
+
+
+/* wps_common.c */
+void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
+	     const char *label, u8 *res, size_t res_len);
+int wps_derive_keys(struct wps_data *wps);
+void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+		    size_t dev_passwd_len);
+struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
+					  size_t encr_len);
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+		    u16 config_error, u16 error_indication, const u8 *mac_addr);
+void wps_success_event(struct wps_context *wps, const u8 *mac_addr);
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part,
+			     const u8 *mac_addr);
+void wps_pbc_overlap_event(struct wps_context *wps);
+void wps_pbc_timeout_event(struct wps_context *wps);
+void wps_pbc_active_event(struct wps_context *wps);
+void wps_pbc_disable_event(struct wps_context *wps);
+
+struct wpabuf * wps_build_wsc_ack(struct wps_data *wps);
+struct wpabuf * wps_build_wsc_nack(struct wps_data *wps);
+
+/* wps_attr_build.c */
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type);
+int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type);
+int wps_build_config_methods(struct wpabuf *msg, u16 methods);
+int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid);
+int wps_build_dev_password_id(struct wpabuf *msg, u16 id);
+int wps_build_config_error(struct wpabuf *msg, u16 err);
+int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
+			    struct wpabuf *plain);
+int wps_build_version(struct wpabuf *msg);
+int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
+		      const u8 *auth_macs, size_t auth_macs_count);
+int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type);
+int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
+			 const struct wpabuf *pubkey, const u8 *dev_pw,
+			 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,
+			      const struct wpabuf *msg);
+int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
+			      const u8 *key_wrap_auth);
+int wps_process_cred(struct wps_parse_attr *attr,
+		     struct wps_credential *cred);
+int wps_process_ap_settings(struct wps_parse_attr *attr,
+			    struct wps_credential *cred);
+
+/* wps_enrollee.c */
+struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
+				     enum wsc_op_code *op_code);
+enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
+					      enum wsc_op_code op_code,
+					      const struct wpabuf *msg);
+
+/* wps_registrar.c */
+struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
+				      enum wsc_op_code *op_code);
+enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
+					       enum wsc_op_code op_code,
+					       const struct wpabuf *msg);
+int wps_build_cred(struct wps_data *wps, struct wpabuf *msg);
+int wps_device_store(struct wps_registrar *reg,
+		     struct wps_device_data *dev, const u8 *uuid);
+void wps_registrar_selected_registrar_changed(struct wps_registrar *reg,
+					      u16 dev_pw_id);
+const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count);
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+			      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/hostap/src/wps/wps_module_tests.c b/hostap/src/wps/wps_module_tests.c
new file mode 100644
index 0000000..3506307
--- /dev/null
+++ b/hostap/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;
+};
+
+const 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;
+		const 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/hostap/src/wps/wps_registrar.c b/hostap/src/wps/wps_registrar.c
new file mode 100644
index 0000000..4ca3a42
--- /dev/null
+++ b/hostap/src/wps/wps_registrar.c
@@ -0,0 +1,3670 @@
+/*
+ * Wi-Fi Protected Setup - Registrar
+ * Copyright (c) 2008-2013, 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/base64.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
+#include "utils/list.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+#ifndef CONFIG_WPS_STRICT
+#define WPS_WORKAROUNDS
+#endif /* CONFIG_WPS_STRICT */
+
+#ifdef CONFIG_WPS_NFC
+
+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);
+	bin_clear_free(token, sizeof(*token));
+}
+
+
+static void wps_free_nfc_pw_tokens(struct dl_list *tokens, u16 pw_id)
+{
+	struct wps_nfc_pw_token *token, *prev;
+	dl_list_for_each_safe(token, prev, tokens, struct wps_nfc_pw_token,
+			      list) {
+		if (pw_id == 0 || pw_id == token->pw_id)
+			wps_remove_nfc_pw_token(token);
+	}
+}
+
+
+static struct wps_nfc_pw_token * wps_get_nfc_pw_token(struct dl_list *tokens,
+						      u16 pw_id)
+{
+	struct wps_nfc_pw_token *token;
+	dl_list_for_each(token, tokens, struct wps_nfc_pw_token, list) {
+		if (pw_id == token->pw_id)
+			return token;
+	}
+	return NULL;
+}
+
+#else /* CONFIG_WPS_NFC */
+
+#define wps_free_nfc_pw_tokens(t, p) do { } while (0)
+
+#endif /* CONFIG_WPS_NFC */
+
+
+struct wps_uuid_pin {
+	struct dl_list list;
+	u8 uuid[WPS_UUID_LEN];
+	int wildcard_uuid;
+	u8 *pin;
+	size_t pin_len;
+#define PIN_LOCKED BIT(0)
+#define PIN_EXPIRES BIT(1)
+	int flags;
+	struct os_reltime expiration;
+	u8 enrollee_addr[ETH_ALEN];
+};
+
+
+static void wps_free_pin(struct wps_uuid_pin *pin)
+{
+	bin_clear_free(pin->pin, pin->pin_len);
+	os_free(pin);
+}
+
+
+static void wps_remove_pin(struct wps_uuid_pin *pin)
+{
+	dl_list_del(&pin->list);
+	wps_free_pin(pin);
+}
+
+
+static void wps_free_pins(struct dl_list *pins)
+{
+	struct wps_uuid_pin *pin, *prev;
+	dl_list_for_each_safe(pin, prev, pins, struct wps_uuid_pin, list)
+		wps_remove_pin(pin);
+}
+
+
+struct wps_pbc_session {
+	struct wps_pbc_session *next;
+	u8 addr[ETH_ALEN];
+	u8 uuid_e[WPS_UUID_LEN];
+	struct os_reltime timestamp;
+};
+
+
+static void wps_free_pbc_sessions(struct wps_pbc_session *pbc)
+{
+	struct wps_pbc_session *prev;
+
+	while (pbc) {
+		prev = pbc;
+		pbc = pbc->next;
+		os_free(prev);
+	}
+}
+
+
+struct wps_registrar_device {
+	struct wps_registrar_device *next;
+	struct wps_device_data dev;
+	u8 uuid[WPS_UUID_LEN];
+};
+
+
+struct wps_registrar {
+	struct wps_context *wps;
+
+	int pbc;
+	int selected_registrar;
+
+	int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr,
+			  const u8 *psk, size_t psk_len);
+	int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
+			 struct wpabuf *probe_resp_ie);
+	void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
+			      const struct wps_device_data *dev);
+	void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
+			       const u8 *uuid_e, const u8 *dev_pw,
+			       size_t dev_pw_len);
+	void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
+			       u16 sel_reg_config_methods);
+	void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
+				 const u8 *pri_dev_type, u16 config_methods,
+				 u16 dev_password_id, u8 request_type,
+				 const char *dev_name);
+	void *cb_ctx;
+
+	struct dl_list pins;
+	struct dl_list nfc_pw_tokens;
+	struct wps_pbc_session *pbc_sessions;
+
+	int skip_cred_build;
+	struct wpabuf *extra_cred;
+	int disable_auto_conf;
+	int sel_reg_union;
+	int sel_reg_dev_password_id_override;
+	int sel_reg_config_methods_override;
+	int static_wep_only;
+	int dualband;
+	int force_per_enrollee_psk;
+
+	struct wps_registrar_device *devices;
+
+	int force_pbc_overlap;
+
+	u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+	u8 authorized_macs_union[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+
+	u8 p2p_dev_addr[ETH_ALEN];
+
+	u8 pbc_ignore_uuid[WPS_UUID_LEN];
+#ifdef WPS_WORKAROUNDS
+	struct os_reltime pbc_ignore_start;
+#endif /* WPS_WORKAROUNDS */
+};
+
+
+static int wps_set_ie(struct wps_registrar *reg);
+static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wps_registrar_set_selected_timeout(void *eloop_ctx,
+					       void *timeout_ctx);
+static void wps_registrar_remove_pin(struct wps_registrar *reg,
+				     struct wps_uuid_pin *pin);
+
+
+static void wps_registrar_add_authorized_mac(struct wps_registrar *reg,
+					     const u8 *addr)
+{
+	int i;
+	wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC " MACSTR,
+		   MAC2STR(addr));
+	for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
+		if (os_memcmp(reg->authorized_macs[i], addr, ETH_ALEN) == 0) {
+			wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was "
+				   "already in the list");
+			return; /* already in list */
+		}
+	for (i = WPS_MAX_AUTHORIZED_MACS - 1; i > 0; i--)
+		os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i - 1],
+			  ETH_ALEN);
+	os_memcpy(reg->authorized_macs[0], addr, ETH_ALEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
+		    (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
+}
+
+
+static void wps_registrar_remove_authorized_mac(struct wps_registrar *reg,
+						const u8 *addr)
+{
+	int i;
+	wpa_printf(MSG_DEBUG, "WPS: Remove authorized MAC " MACSTR,
+		   MAC2STR(addr));
+	for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) {
+		if (os_memcmp(reg->authorized_macs, addr, ETH_ALEN) == 0)
+			break;
+	}
+	if (i == WPS_MAX_AUTHORIZED_MACS) {
+		wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was not in the "
+			   "list");
+		return; /* not in the list */
+	}
+	for (; i + 1 < WPS_MAX_AUTHORIZED_MACS; i++)
+		os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i + 1],
+			  ETH_ALEN);
+	os_memset(reg->authorized_macs[WPS_MAX_AUTHORIZED_MACS - 1], 0,
+		  ETH_ALEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
+		    (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
+}
+
+
+static void wps_free_devices(struct wps_registrar_device *dev)
+{
+	struct wps_registrar_device *prev;
+
+	while (dev) {
+		prev = dev;
+		dev = dev->next;
+		wps_device_data_free(&prev->dev);
+		os_free(prev);
+	}
+}
+
+
+static struct wps_registrar_device * wps_device_get(struct wps_registrar *reg,
+						    const u8 *addr)
+{
+	struct wps_registrar_device *dev;
+
+	for (dev = reg->devices; dev; dev = dev->next) {
+		if (os_memcmp(dev->dev.mac_addr, addr, ETH_ALEN) == 0)
+			return dev;
+	}
+	return NULL;
+}
+
+
+static void wps_device_clone_data(struct wps_device_data *dst,
+				  struct wps_device_data *src)
+{
+	os_memcpy(dst->mac_addr, src->mac_addr, ETH_ALEN);
+	os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
+
+#define WPS_STRDUP(n) \
+	os_free(dst->n); \
+	dst->n = src->n ? os_strdup(src->n) : NULL
+
+	WPS_STRDUP(device_name);
+	WPS_STRDUP(manufacturer);
+	WPS_STRDUP(model_name);
+	WPS_STRDUP(model_number);
+	WPS_STRDUP(serial_number);
+#undef WPS_STRDUP
+}
+
+
+int wps_device_store(struct wps_registrar *reg,
+		     struct wps_device_data *dev, const u8 *uuid)
+{
+	struct wps_registrar_device *d;
+
+	d = wps_device_get(reg, dev->mac_addr);
+	if (d == NULL) {
+		d = os_zalloc(sizeof(*d));
+		if (d == NULL)
+			return -1;
+		d->next = reg->devices;
+		reg->devices = d;
+	}
+
+	wps_device_clone_data(&d->dev, dev);
+	os_memcpy(d->uuid, uuid, WPS_UUID_LEN);
+
+	return 0;
+}
+
+
+static void wps_registrar_add_pbc_session(struct wps_registrar *reg,
+					  const u8 *addr, const u8 *uuid_e)
+{
+	struct wps_pbc_session *pbc, *prev = NULL;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+
+	pbc = reg->pbc_sessions;
+	while (pbc) {
+		if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
+		    os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
+			if (prev)
+				prev->next = pbc->next;
+			else
+				reg->pbc_sessions = pbc->next;
+			break;
+		}
+		prev = pbc;
+		pbc = pbc->next;
+	}
+
+	if (!pbc) {
+		pbc = os_zalloc(sizeof(*pbc));
+		if (pbc == NULL)
+			return;
+		os_memcpy(pbc->addr, addr, ETH_ALEN);
+		if (uuid_e)
+			os_memcpy(pbc->uuid_e, uuid_e, WPS_UUID_LEN);
+	}
+
+	pbc->next = reg->pbc_sessions;
+	reg->pbc_sessions = pbc;
+	pbc->timestamp = now;
+
+	/* remove entries that have timed out */
+	prev = pbc;
+	pbc = pbc->next;
+
+	while (pbc) {
+		if (os_reltime_expired(&now, &pbc->timestamp,
+				       WPS_PBC_WALK_TIME)) {
+			prev->next = NULL;
+			wps_free_pbc_sessions(pbc);
+			break;
+		}
+		prev = pbc;
+		pbc = pbc->next;
+	}
+}
+
+
+static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
+					     const u8 *uuid_e,
+					     const u8 *p2p_dev_addr)
+{
+	struct wps_pbc_session *pbc, *prev = NULL, *tmp;
+
+	pbc = reg->pbc_sessions;
+	while (pbc) {
+		if (os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0 ||
+		    (p2p_dev_addr && !is_zero_ether_addr(reg->p2p_dev_addr) &&
+		     os_memcmp(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
+		     0)) {
+			if (prev)
+				prev->next = pbc->next;
+			else
+				reg->pbc_sessions = pbc->next;
+			tmp = pbc;
+			pbc = pbc->next;
+			wpa_printf(MSG_DEBUG, "WPS: Removing PBC session for "
+				   "addr=" MACSTR, MAC2STR(tmp->addr));
+			wpa_hexdump(MSG_DEBUG, "WPS: Removed UUID-E",
+				    tmp->uuid_e, WPS_UUID_LEN);
+			os_free(tmp);
+			continue;
+		}
+		prev = pbc;
+		pbc = pbc->next;
+	}
+}
+
+
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+			      const u8 *addr, const u8 *uuid_e)
+{
+	int count = 0;
+	struct wps_pbc_session *pbc;
+	struct wps_pbc_session *first = NULL;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+
+	wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap");
+
+	if (uuid_e) {
+		wpa_printf(MSG_DEBUG, "WPS: Add one for the requested UUID");
+		wpa_hexdump(MSG_DEBUG, "WPS: Requested UUID",
+			    uuid_e, WPS_UUID_LEN);
+		count++;
+	}
+
+	for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) {
+		wpa_printf(MSG_DEBUG, "WPS: Consider PBC session with " MACSTR,
+			   MAC2STR(pbc->addr));
+		wpa_hexdump(MSG_DEBUG, "WPS: UUID-E",
+			    pbc->uuid_e, WPS_UUID_LEN);
+		if (os_reltime_expired(&now, &pbc->timestamp,
+				       WPS_PBC_WALK_TIME)) {
+			wpa_printf(MSG_DEBUG, "WPS: PBC walk time has expired");
+			break;
+		}
+		if (first &&
+		    os_memcmp(pbc->uuid_e, first->uuid_e, WPS_UUID_LEN) == 0) {
+			wpa_printf(MSG_DEBUG, "WPS: Same Enrollee");
+			continue; /* same Enrollee */
+		}
+		if (uuid_e == NULL ||
+		    os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN)) {
+			wpa_printf(MSG_DEBUG, "WPS: New Enrollee");
+			count++;
+		}
+		if (first == NULL)
+			first = pbc;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: %u active PBC session(s) found", count);
+
+	return count > 1 ? 1 : 0;
+}
+
+
+static int wps_build_wps_state(struct wps_context *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Wi-Fi Protected Setup State (%d)",
+		   wps->wps_state);
+	wpabuf_put_be16(msg, ATTR_WPS_STATE);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, wps->wps_state);
+	return 0;
+}
+
+
+#ifdef CONFIG_WPS_UPNP
+static void wps_registrar_free_pending_m2(struct wps_context *wps)
+{
+	struct upnp_pending_message *p, *p2, *prev = NULL;
+	p = wps->upnp_msgs;
+	while (p) {
+		if (p->type == WPS_M2 || p->type == WPS_M2D) {
+			if (prev == NULL)
+				wps->upnp_msgs = p->next;
+			else
+				prev->next = p->next;
+			wpa_printf(MSG_DEBUG, "WPS UPnP: Drop pending M2/M2D");
+			p2 = p;
+			p = p->next;
+			wpabuf_free(p2->msg);
+			os_free(p2);
+			continue;
+		}
+		prev = p;
+		p = p->next;
+	}
+}
+#endif /* CONFIG_WPS_UPNP */
+
+
+static int wps_build_ap_setup_locked(struct wps_context *wps,
+				     struct wpabuf *msg)
+{
+	if (wps->ap_setup_locked && wps->ap_setup_locked != 2) {
+		wpa_printf(MSG_DEBUG, "WPS:  * AP Setup Locked");
+		wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED);
+		wpabuf_put_be16(msg, 1);
+		wpabuf_put_u8(msg, 1);
+	}
+	return 0;
+}
+
+
+static int wps_build_selected_registrar(struct wps_registrar *reg,
+					struct wpabuf *msg)
+{
+	if (!reg->sel_reg_union)
+		return 0;
+	wpa_printf(MSG_DEBUG, "WPS:  * Selected Registrar");
+	wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, 1);
+	return 0;
+}
+
+
+static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg,
+					     struct wpabuf *msg)
+{
+	u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
+	if (!reg->sel_reg_union)
+		return 0;
+	if (reg->sel_reg_dev_password_id_override >= 0)
+		id = reg->sel_reg_dev_password_id_override;
+	wpa_printf(MSG_DEBUG, "WPS:  * Device Password ID (%d)", id);
+	wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, id);
+	return 0;
+}
+
+
+static int wps_build_sel_pbc_reg_uuid_e(struct wps_registrar *reg,
+					struct wpabuf *msg)
+{
+	u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
+	if (!reg->sel_reg_union)
+		return 0;
+	if (reg->sel_reg_dev_password_id_override >= 0)
+		id = reg->sel_reg_dev_password_id_override;
+	if (id != DEV_PW_PUSHBUTTON || !reg->dualband)
+		return 0;
+	return wps_build_uuid_e(msg, reg->wps->uuid);
+}
+
+
+static void wps_set_pushbutton(u16 *methods, u16 conf_methods)
+{
+	*methods |= WPS_CONFIG_PUSHBUTTON;
+	if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) ==
+	    WPS_CONFIG_VIRT_PUSHBUTTON)
+		*methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+	if ((conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) ==
+	    WPS_CONFIG_PHY_PUSHBUTTON)
+		*methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+	if ((*methods & WPS_CONFIG_VIRT_PUSHBUTTON) !=
+	    WPS_CONFIG_VIRT_PUSHBUTTON &&
+	    (*methods & WPS_CONFIG_PHY_PUSHBUTTON) !=
+	    WPS_CONFIG_PHY_PUSHBUTTON) {
+		/*
+		 * Required to include virtual/physical flag, but we were not
+		 * configured with push button type, so have to default to one
+		 * of them.
+		 */
+		*methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+	}
+}
+
+
+static int wps_build_sel_reg_config_methods(struct wps_registrar *reg,
+					    struct wpabuf *msg)
+{
+	u16 methods;
+	if (!reg->sel_reg_union)
+		return 0;
+	methods = reg->wps->config_methods;
+	methods &= ~WPS_CONFIG_PUSHBUTTON;
+	methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+		     WPS_CONFIG_PHY_PUSHBUTTON);
+	if (reg->pbc)
+		wps_set_pushbutton(&methods, reg->wps->config_methods);
+	if (reg->sel_reg_config_methods_override >= 0)
+		methods = reg->sel_reg_config_methods_override;
+	wpa_printf(MSG_DEBUG, "WPS:  * Selected Registrar Config Methods (%x)",
+		   methods);
+	wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, methods);
+	return 0;
+}
+
+
+static int wps_build_probe_config_methods(struct wps_registrar *reg,
+					  struct wpabuf *msg)
+{
+	u16 methods;
+	/*
+	 * These are the methods that the AP supports as an Enrollee for adding
+	 * external Registrars.
+	 */
+	methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+	methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+		     WPS_CONFIG_PHY_PUSHBUTTON);
+	wpa_printf(MSG_DEBUG, "WPS:  * Config Methods (%x)", methods);
+	wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, methods);
+	return 0;
+}
+
+
+static int wps_build_config_methods_r(struct wps_registrar *reg,
+				      struct wpabuf *msg)
+{
+	return wps_build_config_methods(msg, reg->wps->config_methods);
+}
+
+
+const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count)
+{
+	*count = 0;
+
+	while (*count < WPS_MAX_AUTHORIZED_MACS) {
+		if (is_zero_ether_addr(reg->authorized_macs_union[*count]))
+			break;
+		(*count)++;
+	}
+
+	return (const u8 *) reg->authorized_macs_union;
+}
+
+
+/**
+ * wps_registrar_init - Initialize WPS Registrar data
+ * @wps: Pointer to longterm WPS context
+ * @cfg: Registrar configuration
+ * Returns: Pointer to allocated Registrar data or %NULL on failure
+ *
+ * This function is used to initialize WPS Registrar functionality. It can be
+ * used for a single Registrar run (e.g., when run in a supplicant) or multiple
+ * runs (e.g., when run as an internal Registrar in an AP). Caller is
+ * responsible for freeing the returned data with wps_registrar_deinit() when
+ * Registrar functionality is not needed anymore.
+ */
+struct wps_registrar *
+wps_registrar_init(struct wps_context *wps,
+		   const struct wps_registrar_config *cfg)
+{
+	struct wps_registrar *reg = os_zalloc(sizeof(*reg));
+	if (reg == NULL)
+		return NULL;
+
+	dl_list_init(&reg->pins);
+	dl_list_init(&reg->nfc_pw_tokens);
+	reg->wps = wps;
+	reg->new_psk_cb = cfg->new_psk_cb;
+	reg->set_ie_cb = cfg->set_ie_cb;
+	reg->pin_needed_cb = cfg->pin_needed_cb;
+	reg->reg_success_cb = cfg->reg_success_cb;
+	reg->set_sel_reg_cb = cfg->set_sel_reg_cb;
+	reg->enrollee_seen_cb = cfg->enrollee_seen_cb;
+	reg->cb_ctx = cfg->cb_ctx;
+	reg->skip_cred_build = cfg->skip_cred_build;
+	if (cfg->extra_cred) {
+		reg->extra_cred = wpabuf_alloc_copy(cfg->extra_cred,
+						    cfg->extra_cred_len);
+		if (reg->extra_cred == NULL) {
+			os_free(reg);
+			return NULL;
+		}
+	}
+	reg->disable_auto_conf = cfg->disable_auto_conf;
+	reg->sel_reg_dev_password_id_override = -1;
+	reg->sel_reg_config_methods_override = -1;
+	reg->static_wep_only = cfg->static_wep_only;
+	reg->dualband = cfg->dualband;
+	reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk;
+
+	if (wps_set_ie(reg)) {
+		wps_registrar_deinit(reg);
+		return NULL;
+	}
+
+	return reg;
+}
+
+
+void wps_registrar_flush(struct wps_registrar *reg)
+{
+	if (reg == NULL)
+		return;
+	wps_free_pins(&reg->pins);
+	wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, 0);
+	wps_free_pbc_sessions(reg->pbc_sessions);
+	reg->pbc_sessions = NULL;
+	wps_free_devices(reg->devices);
+	reg->devices = NULL;
+#ifdef WPS_WORKAROUNDS
+	reg->pbc_ignore_start.sec = 0;
+#endif /* WPS_WORKAROUNDS */
+}
+
+
+/**
+ * wps_registrar_deinit - Deinitialize WPS Registrar data
+ * @reg: Registrar data from wps_registrar_init()
+ */
+void wps_registrar_deinit(struct wps_registrar *reg)
+{
+	if (reg == NULL)
+		return;
+	eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+	eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+	wps_registrar_flush(reg);
+	wpabuf_free(reg->extra_cred);
+	os_free(reg);
+}
+
+
+static void wps_registrar_invalidate_unused(struct wps_registrar *reg)
+{
+	struct wps_uuid_pin *pin;
+
+	dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+		if (pin->wildcard_uuid == 1 && !(pin->flags & PIN_LOCKED)) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalidate previously "
+				   "configured wildcard PIN");
+			wps_registrar_remove_pin(reg, pin);
+			break;
+		}
+	}
+}
+
+
+/**
+ * wps_registrar_add_pin - Configure a new PIN for Registrar
+ * @reg: Registrar data from wps_registrar_init()
+ * @addr: Enrollee MAC address or %NULL if not known
+ * @uuid: UUID-E or %NULL for wildcard (any UUID)
+ * @pin: PIN (Device Password)
+ * @pin_len: Length of pin in octets
+ * @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout
+ * Returns: 0 on success, -1 on failure
+ */
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
+			  const u8 *uuid, const u8 *pin, size_t pin_len,
+			  int timeout)
+{
+	struct wps_uuid_pin *p;
+
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL)
+		return -1;
+	if (addr)
+		os_memcpy(p->enrollee_addr, addr, ETH_ALEN);
+	if (uuid == NULL)
+		p->wildcard_uuid = 1;
+	else
+		os_memcpy(p->uuid, uuid, WPS_UUID_LEN);
+	p->pin = os_malloc(pin_len);
+	if (p->pin == NULL) {
+		os_free(p);
+		return -1;
+	}
+	os_memcpy(p->pin, pin, pin_len);
+	p->pin_len = pin_len;
+
+	if (timeout) {
+		p->flags |= PIN_EXPIRES;
+		os_get_reltime(&p->expiration);
+		p->expiration.sec += timeout;
+	}
+
+	if (p->wildcard_uuid)
+		wps_registrar_invalidate_unused(reg);
+
+	dl_list_add(&reg->pins, &p->list);
+
+	wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)",
+		   timeout);
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID", uuid, WPS_UUID_LEN);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len);
+	reg->selected_registrar = 1;
+	reg->pbc = 0;
+	if (addr)
+		wps_registrar_add_authorized_mac(reg, addr);
+	else
+		wps_registrar_add_authorized_mac(
+			reg, (u8 *) "\xff\xff\xff\xff\xff\xff");
+	wps_registrar_selected_registrar_changed(reg, 0);
+	eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+	eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+			       wps_registrar_set_selected_timeout,
+			       reg, NULL);
+
+	return 0;
+}
+
+
+static void wps_registrar_remove_pin(struct wps_registrar *reg,
+				     struct wps_uuid_pin *pin)
+{
+	u8 *addr;
+	u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+	if (is_zero_ether_addr(pin->enrollee_addr))
+		addr = bcast;
+	else
+		addr = pin->enrollee_addr;
+	wps_registrar_remove_authorized_mac(reg, addr);
+	wps_remove_pin(pin);
+	wps_registrar_selected_registrar_changed(reg, 0);
+}
+
+
+static void wps_registrar_expire_pins(struct wps_registrar *reg)
+{
+	struct wps_uuid_pin *pin, *prev;
+	struct os_reltime 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_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);
+		}
+	}
+}
+
+
+/**
+ * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN
+ * @reg: Registrar data from wps_registrar_init()
+ * @dev_pw: PIN to search for or %NULL to match any
+ * @dev_pw_len: Length of dev_pw in octets
+ * Returns: 0 on success, -1 if not wildcard PIN is enabled
+ */
+static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg,
+						 const u8 *dev_pw,
+						 size_t dev_pw_len)
+{
+	struct wps_uuid_pin *pin, *prev;
+
+	dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
+	{
+		if (dev_pw && pin->pin &&
+		    (dev_pw_len != pin->pin_len ||
+		     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",
+				    pin->uuid, WPS_UUID_LEN);
+			wps_registrar_remove_pin(reg, pin);
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+/**
+ * wps_registrar_invalidate_pin - Invalidate a PIN for a specific UUID-E
+ * @reg: Registrar data from wps_registrar_init()
+ * @uuid: UUID-E
+ * Returns: 0 on success, -1 on failure (e.g., PIN not found)
+ */
+int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid)
+{
+	struct wps_uuid_pin *pin, *prev;
+
+	dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
+	{
+		if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+			wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
+				    pin->uuid, WPS_UUID_LEN);
+			wps_registrar_remove_pin(reg, pin);
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+static const u8 * wps_registrar_get_pin(struct wps_registrar *reg,
+					const u8 *uuid, size_t *pin_len)
+{
+	struct wps_uuid_pin *pin, *found = NULL;
+
+	wps_registrar_expire_pins(reg);
+
+	dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+		if (!pin->wildcard_uuid &&
+		    os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+			found = pin;
+			break;
+		}
+	}
+
+	if (!found) {
+		/* Check for wildcard UUIDs since none of the UUID-specific
+		 * PINs matched */
+		dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+			if (pin->wildcard_uuid == 1 ||
+			    pin->wildcard_uuid == 2) {
+				wpa_printf(MSG_DEBUG, "WPS: Found a wildcard "
+					   "PIN. Assigned it for this UUID-E");
+				pin->wildcard_uuid++;
+				os_memcpy(pin->uuid, uuid, WPS_UUID_LEN);
+				found = pin;
+				break;
+			}
+		}
+	}
+
+	if (!found)
+		return NULL;
+
+	/*
+	 * Lock the PIN to avoid attacks based on concurrent re-use of the PIN
+	 * that could otherwise avoid PIN invalidations.
+	 */
+	if (found->flags & PIN_LOCKED) {
+		wpa_printf(MSG_DEBUG, "WPS: Selected PIN locked - do not "
+			   "allow concurrent re-use");
+		return NULL;
+	}
+	*pin_len = found->pin_len;
+	found->flags |= PIN_LOCKED;
+	return found->pin;
+}
+
+
+/**
+ * wps_registrar_unlock_pin - Unlock a PIN for a specific UUID-E
+ * @reg: Registrar data from wps_registrar_init()
+ * @uuid: UUID-E
+ * Returns: 0 on success, -1 on failure
+ *
+ * PINs are locked to enforce only one concurrent use. This function unlocks a
+ * PIN to allow it to be used again. If the specified PIN was configured using
+ * a wildcard UUID, it will be removed instead of allowing multiple uses.
+ */
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid)
+{
+	struct wps_uuid_pin *pin;
+
+	dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+		if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+			if (pin->wildcard_uuid == 3) {
+				wpa_printf(MSG_DEBUG, "WPS: Invalidating used "
+					   "wildcard PIN");
+				return wps_registrar_invalidate_pin(reg, uuid);
+			}
+			pin->flags &= ~PIN_LOCKED;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+static void wps_registrar_stop_pbc(struct wps_registrar *reg)
+{
+	reg->selected_registrar = 0;
+	reg->pbc = 0;
+	os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
+	wps_registrar_remove_authorized_mac(reg,
+					    (u8 *) "\xff\xff\xff\xff\xff\xff");
+	wps_registrar_selected_registrar_changed(reg, 0);
+}
+
+
+static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wps_registrar *reg = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "WPS: PBC timed out - disable PBC mode");
+	wps_pbc_timeout_event(reg->wps);
+	wps_registrar_stop_pbc(reg);
+}
+
+
+/**
+ * wps_registrar_button_pushed - Notify Registrar that AP button was pushed
+ * @reg: Registrar data from wps_registrar_init()
+ * @p2p_dev_addr: Limit allowed PBC devices to the specified P2P device, %NULL
+ *	indicates no such filtering
+ * Returns: 0 on success, -1 on failure, -2 on session overlap
+ *
+ * This function is called on an AP when a push button is pushed to activate
+ * PBC mode. The PBC mode will be stopped after walk time (2 minutes) timeout
+ * or when a PBC registration is completed. If more than one Enrollee in active
+ * PBC mode has been detected during the monitor time (previous 2 minutes), the
+ * PBC mode is not activated and -2 is returned to indicate session overlap.
+ * This is skipped if a specific Enrollee is selected.
+ */
+int wps_registrar_button_pushed(struct wps_registrar *reg,
+				const u8 *p2p_dev_addr)
+{
+	if (p2p_dev_addr == NULL &&
+	    wps_registrar_pbc_overlap(reg, NULL, NULL)) {
+		wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC "
+			   "mode");
+		wps_pbc_overlap_event(reg->wps);
+		return -2;
+	}
+	wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started");
+	reg->force_pbc_overlap = 0;
+	reg->selected_registrar = 1;
+	reg->pbc = 1;
+	if (p2p_dev_addr)
+		os_memcpy(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
+	else
+		os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
+	wps_registrar_add_authorized_mac(reg,
+					 (u8 *) "\xff\xff\xff\xff\xff\xff");
+	wps_registrar_selected_registrar_changed(reg, 0);
+
+	wps_pbc_active_event(reg->wps);
+	eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+	eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout,
+			       reg, NULL);
+	return 0;
+}
+
+
+static void wps_registrar_pbc_completed(struct wps_registrar *reg)
+{
+	wpa_printf(MSG_DEBUG, "WPS: PBC completed - stopping PBC mode");
+	eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+	wps_registrar_stop_pbc(reg);
+	wps_pbc_disable_event(reg->wps);
+}
+
+
+static void wps_registrar_pin_completed(struct wps_registrar *reg)
+{
+	wpa_printf(MSG_DEBUG, "WPS: PIN completed using internal Registrar");
+	eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+	reg->selected_registrar = 0;
+	wps_registrar_selected_registrar_changed(reg, 0);
+}
+
+
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
+			    const u8 *dev_pw, size_t dev_pw_len)
+{
+	if (registrar->pbc) {
+		wps_registrar_remove_pbc_session(registrar,
+						 uuid_e, NULL);
+		wps_registrar_pbc_completed(registrar);
+#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);
+	}
+
+	if (dev_pw &&
+	    wps_registrar_invalidate_wildcard_pin(registrar, dev_pw,
+						  dev_pw_len) == 0) {
+		wpa_hexdump_key(MSG_DEBUG, "WPS: Invalidated wildcard PIN",
+				dev_pw, dev_pw_len);
+	}
+}
+
+
+int wps_registrar_wps_cancel(struct wps_registrar *reg)
+{
+	if (reg->pbc) {
+		wpa_printf(MSG_DEBUG, "WPS: PBC is set - cancelling it");
+		wps_registrar_pbc_timeout(reg, NULL);
+		eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+		return 1;
+	} else if (reg->selected_registrar) {
+		/* PIN Method */
+		wpa_printf(MSG_DEBUG, "WPS: PIN is set - cancelling it");
+		wps_registrar_pin_completed(reg);
+		wps_registrar_invalidate_wildcard_pin(reg, NULL, 0);
+		return 1;
+	}
+	return 0;
+}
+
+
+/**
+ * wps_registrar_probe_req_rx - Notify Registrar of Probe Request
+ * @reg: Registrar data from wps_registrar_init()
+ * @addr: MAC address of the Probe Request sender
+ * @wps_data: WPS IE contents
+ *
+ * This function is called on an AP when a Probe Request with WPS IE is
+ * received. This is used to track PBC mode use and to detect possible overlap
+ * situation with other WPS APs.
+ */
+void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
+				const struct wpabuf *wps_data,
+				int p2p_wildcard)
+{
+	struct wps_parse_attr attr;
+	int skip_add = 0;
+
+	wpa_hexdump_buf(MSG_MSGDUMP,
+			"WPS: Probe Request with WPS data received",
+			wps_data);
+
+	if (wps_parse_msg(wps_data, &attr) < 0)
+		return;
+
+	if (attr.config_methods == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in "
+			   "Probe Request");
+		return;
+	}
+
+	if (attr.dev_password_id == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Device Password Id attribute "
+			   "in Probe Request");
+		return;
+	}
+
+	if (reg->enrollee_seen_cb && attr.uuid_e &&
+	    attr.primary_dev_type && attr.request_type && !p2p_wildcard) {
+		char *dev_name = NULL;
+		if (attr.dev_name) {
+			dev_name = os_zalloc(attr.dev_name_len + 1);
+			if (dev_name) {
+				os_memcpy(dev_name, attr.dev_name,
+					  attr.dev_name_len);
+			}
+		}
+		reg->enrollee_seen_cb(reg->cb_ctx, addr, attr.uuid_e,
+				      attr.primary_dev_type,
+				      WPA_GET_BE16(attr.config_methods),
+				      WPA_GET_BE16(attr.dev_password_id),
+				      *attr.request_type, dev_name);
+		os_free(dev_name);
+	}
+
+	if (WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
+		return; /* Not PBC */
+
+	wpa_printf(MSG_DEBUG, "WPS: Probe Request for PBC received from "
+		   MACSTR, MAC2STR(addr));
+	if (attr.uuid_e == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Probe Request WPS IE: No "
+			   "UUID-E included");
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID-E from Probe Request", attr.uuid_e,
+		    WPS_UUID_LEN);
+
+#ifdef WPS_WORKAROUNDS
+	if (reg->pbc_ignore_start.sec &&
+	    os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) {
+		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 "
+				   "that just completed PBC provisioning");
+			skip_add = 1;
+		} else
+			reg->pbc_ignore_start.sec = 0;
+	}
+#endif /* WPS_WORKAROUNDS */
+
+	if (!skip_add)
+		wps_registrar_add_pbc_session(reg, addr, attr.uuid_e);
+	if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) {
+		wpa_printf(MSG_DEBUG, "WPS: PBC session overlap detected");
+		reg->force_pbc_overlap = 1;
+		wps_pbc_overlap_event(reg->wps);
+	}
+}
+
+
+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;
+
+	return reg->new_psk_cb(reg->cb_ctx, mac_addr, p2p_dev_addr, psk,
+			       psk_len);
+}
+
+
+static void wps_cb_pin_needed(struct wps_registrar *reg, const u8 *uuid_e,
+			      const struct wps_device_data *dev)
+{
+	if (reg->pin_needed_cb == NULL)
+		return;
+
+	reg->pin_needed_cb(reg->cb_ctx, uuid_e, dev);
+}
+
+
+static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr,
+			       const u8 *uuid_e, const u8 *dev_pw,
+			       size_t dev_pw_len)
+{
+	if (reg->reg_success_cb == NULL)
+		return;
+
+	reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e, dev_pw, dev_pw_len);
+}
+
+
+static int wps_cb_set_ie(struct wps_registrar *reg, struct wpabuf *beacon_ie,
+			 struct wpabuf *probe_resp_ie)
+{
+	return reg->set_ie_cb(reg->cb_ctx, beacon_ie, probe_resp_ie);
+}
+
+
+static void wps_cb_set_sel_reg(struct wps_registrar *reg)
+{
+	u16 methods = 0;
+	if (reg->set_sel_reg_cb == NULL)
+		return;
+
+	if (reg->selected_registrar) {
+		methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+		methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+			     WPS_CONFIG_PHY_PUSHBUTTON);
+		if (reg->pbc)
+			wps_set_pushbutton(&methods, reg->wps->config_methods);
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: wps_cb_set_sel_reg: sel_reg=%d "
+		   "config_methods=0x%x pbc=%d methods=0x%x",
+		   reg->selected_registrar, reg->wps->config_methods,
+		   reg->pbc, methods);
+
+	reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar,
+			    reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT,
+			    methods);
+}
+
+
+static int wps_set_ie(struct wps_registrar *reg)
+{
+	struct wpabuf *beacon;
+	struct wpabuf *probe;
+	const u8 *auth_macs;
+	size_t count;
+	size_t vendor_len = 0;
+	int i;
+
+	if (reg->set_ie_cb == NULL)
+		return 0;
+
+	for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+		if (reg->wps->dev.vendor_ext[i]) {
+			vendor_len += 2 + 2;
+			vendor_len += wpabuf_len(reg->wps->dev.vendor_ext[i]);
+		}
+	}
+
+	beacon = wpabuf_alloc(400 + vendor_len);
+	if (beacon == NULL)
+		return -1;
+	probe = wpabuf_alloc(500 + vendor_len);
+	if (probe == NULL) {
+		wpabuf_free(beacon);
+		return -1;
+	}
+
+	auth_macs = wps_authorized_macs(reg, &count);
+
+	wpa_printf(MSG_DEBUG, "WPS: Build Beacon IEs");
+
+	if (wps_build_version(beacon) ||
+	    wps_build_wps_state(reg->wps, beacon) ||
+	    wps_build_ap_setup_locked(reg->wps, beacon) ||
+	    wps_build_selected_registrar(reg, beacon) ||
+	    wps_build_sel_reg_dev_password_id(reg, beacon) ||
+	    wps_build_sel_reg_config_methods(reg, beacon) ||
+	    wps_build_sel_pbc_reg_uuid_e(reg, beacon) ||
+	    (reg->dualband && wps_build_rf_bands(&reg->wps->dev, beacon, 0)) ||
+	    wps_build_wfa_ext(beacon, 0, auth_macs, count) ||
+	    wps_build_vendor_ext(&reg->wps->dev, beacon)) {
+		wpabuf_free(beacon);
+		wpabuf_free(probe);
+		return -1;
+	}
+
+#ifdef CONFIG_P2P
+	if (wps_build_dev_name(&reg->wps->dev, beacon) ||
+	    wps_build_primary_dev_type(&reg->wps->dev, beacon)) {
+		wpabuf_free(beacon);
+		wpabuf_free(probe);
+		return -1;
+	}
+#endif /* CONFIG_P2P */
+
+	wpa_printf(MSG_DEBUG, "WPS: Build Probe Response IEs");
+
+	if (wps_build_version(probe) ||
+	    wps_build_wps_state(reg->wps, probe) ||
+	    wps_build_ap_setup_locked(reg->wps, probe) ||
+	    wps_build_selected_registrar(reg, probe) ||
+	    wps_build_sel_reg_dev_password_id(reg, probe) ||
+	    wps_build_sel_reg_config_methods(reg, probe) ||
+	    wps_build_resp_type(probe, reg->wps->ap ? WPS_RESP_AP :
+				WPS_RESP_REGISTRAR) ||
+	    wps_build_uuid_e(probe, reg->wps->uuid) ||
+	    wps_build_device_attrs(&reg->wps->dev, probe) ||
+	    wps_build_probe_config_methods(reg, probe) ||
+	    (reg->dualband && wps_build_rf_bands(&reg->wps->dev, probe, 0)) ||
+	    wps_build_wfa_ext(probe, 0, auth_macs, count) ||
+	    wps_build_vendor_ext(&reg->wps->dev, probe)) {
+		wpabuf_free(beacon);
+		wpabuf_free(probe);
+		return -1;
+	}
+
+	beacon = wps_ie_encapsulate(beacon);
+	probe = wps_ie_encapsulate(probe);
+
+	if (!beacon || !probe) {
+		wpabuf_free(beacon);
+		wpabuf_free(probe);
+		return -1;
+	}
+
+	if (reg->static_wep_only) {
+		/*
+		 * Windows XP and Vista clients can get confused about
+		 * EAP-Identity/Request when they probe the network with
+		 * EAPOL-Start. In such a case, they may assume the network is
+		 * using IEEE 802.1X and prompt user for a certificate while
+		 * the correct (non-WPS) behavior would be to ask for the
+		 * static WEP key. As a workaround, use Microsoft Provisioning
+		 * IE to advertise that legacy 802.1X is not supported.
+		 */
+		const u8 ms_wps[7] = {
+			WLAN_EID_VENDOR_SPECIFIC, 5,
+			/* Microsoft Provisioning IE (00:50:f2:5) */
+			0x00, 0x50, 0xf2, 5,
+			0x00 /* no legacy 802.1X or MS WPS */
+		};
+		wpa_printf(MSG_DEBUG, "WPS: Add Microsoft Provisioning IE "
+			   "into Beacon/Probe Response frames");
+		wpabuf_put_data(beacon, ms_wps, sizeof(ms_wps));
+		wpabuf_put_data(probe, ms_wps, sizeof(ms_wps));
+	}
+
+	return wps_cb_set_ie(reg, beacon, probe);
+}
+
+
+static int wps_get_dev_password(struct wps_data *wps)
+{
+	const u8 *pin;
+	size_t pin_len = 0;
+
+	bin_clear_free(wps->dev_password, wps->dev_password_len);
+	wps->dev_password = NULL;
+
+	if (wps->pbc) {
+		wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC");
+		pin = (const u8 *) "00000000";
+		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,
+					    &pin_len);
+		if (pin && wps->dev_pw_id >= 0x10) {
+			wpa_printf(MSG_DEBUG, "WPS: No match for OOB Device "
+				   "Password ID, but PIN found");
+			/*
+			 * See whether Enrollee is willing to use PIN instead.
+			 */
+			wps->dev_pw_id = DEV_PW_DEFAULT;
+		}
+	}
+	if (pin == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Device Password available for "
+			   "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;
+	}
+
+	wps->dev_password = os_malloc(pin_len);
+	if (wps->dev_password == NULL)
+		return -1;
+	os_memcpy(wps->dev_password, pin, pin_len);
+	wps->dev_password_len = pin_len;
+
+	return 0;
+}
+
+
+static int wps_build_uuid_r(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * UUID-R");
+	wpabuf_put_be16(msg, ATTR_UUID_R);
+	wpabuf_put_be16(msg, WPS_UUID_LEN);
+	wpabuf_put_data(msg, wps->uuid_r, WPS_UUID_LEN);
+	return 0;
+}
+
+
+static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg)
+{
+	u8 *hash;
+	const u8 *addr[4];
+	size_t len[4];
+
+	if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+		return -1;
+	wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: R-S2",
+		    wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
+
+	if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
+			   "R-Hash derivation");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS:  * R-Hash1");
+	wpabuf_put_be16(msg, ATTR_R_HASH1);
+	wpabuf_put_be16(msg, SHA256_MAC_LEN);
+	hash = wpabuf_put(msg, SHA256_MAC_LEN);
+	/* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
+	addr[0] = wps->snonce;
+	len[0] = WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk1;
+	len[1] = WPS_PSK_LEN;
+	addr[2] = wpabuf_head(wps->dh_pubkey_e);
+	len[2] = wpabuf_len(wps->dh_pubkey_e);
+	addr[3] = wpabuf_head(wps->dh_pubkey_r);
+	len[3] = wpabuf_len(wps->dh_pubkey_r);
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", hash, SHA256_MAC_LEN);
+
+	wpa_printf(MSG_DEBUG, "WPS:  * R-Hash2");
+	wpabuf_put_be16(msg, ATTR_R_HASH2);
+	wpabuf_put_be16(msg, SHA256_MAC_LEN);
+	hash = wpabuf_put(msg, SHA256_MAC_LEN);
+	/* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
+	addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk2;
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", hash, SHA256_MAC_LEN);
+
+	return 0;
+}
+
+
+static int wps_build_r_snonce1(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * R-SNonce1");
+	wpabuf_put_be16(msg, ATTR_R_SNONCE1);
+	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+	wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
+	return 0;
+}
+
+
+static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * R-SNonce2");
+	wpabuf_put_be16(msg, ATTR_R_SNONCE2);
+	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+	wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
+			WPS_SECRET_NONCE_LEN);
+	return 0;
+}
+
+
+static int wps_build_cred_network_idx(struct wpabuf *msg,
+				      const struct wps_credential *cred)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Network Index (1)");
+	wpabuf_put_be16(msg, ATTR_NETWORK_INDEX);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, 1);
+	return 0;
+}
+
+
+static int wps_build_cred_ssid(struct wpabuf *msg,
+			       const struct wps_credential *cred)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * SSID");
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID for Credential",
+			  cred->ssid, cred->ssid_len);
+	wpabuf_put_be16(msg, ATTR_SSID);
+	wpabuf_put_be16(msg, cred->ssid_len);
+	wpabuf_put_data(msg, cred->ssid, cred->ssid_len);
+	return 0;
+}
+
+
+static int wps_build_cred_auth_type(struct wpabuf *msg,
+				    const struct wps_credential *cred)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)",
+		   cred->auth_type);
+	wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, cred->auth_type);
+	return 0;
+}
+
+
+static int wps_build_cred_encr_type(struct wpabuf *msg,
+				    const struct wps_credential *cred)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)",
+		   cred->encr_type);
+	wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, cred->encr_type);
+	return 0;
+}
+
+
+static int wps_build_cred_network_key(struct wpabuf *msg,
+				      const struct wps_credential *cred)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%d)",
+		   (int) cred->key_len);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
+			cred->key, cred->key_len);
+	wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+	wpabuf_put_be16(msg, cred->key_len);
+	wpabuf_put_data(msg, cred->key, cred->key_len);
+	return 0;
+}
+
+
+static int wps_build_credential(struct wpabuf *msg,
+				const struct wps_credential *cred)
+{
+	if (wps_build_cred_network_idx(msg, cred) ||
+	    wps_build_cred_ssid(msg, cred) ||
+	    wps_build_cred_auth_type(msg, cred) ||
+	    wps_build_cred_encr_type(msg, cred) ||
+	    wps_build_cred_network_key(msg, cred) ||
+	    wps_build_mac_addr(msg, cred->mac_addr))
+		return -1;
+	return 0;
+}
+
+
+int wps_build_credential_wrap(struct wpabuf *msg,
+			      const struct wps_credential *cred)
+{
+	struct wpabuf *wbuf;
+	wbuf = wpabuf_alloc(200);
+	if (wbuf == NULL)
+		return -1;
+	if (wps_build_credential(wbuf, cred)) {
+		wpabuf_free(wbuf);
+		return -1;
+	}
+	wpabuf_put_be16(msg, ATTR_CRED);
+	wpabuf_put_be16(msg, wpabuf_len(wbuf));
+	wpabuf_put_buf(msg, wbuf);
+	wpabuf_free(wbuf);
+	return 0;
+}
+
+
+int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
+{
+	struct wpabuf *cred;
+
+	if (wps->wps->registrar->skip_cred_build)
+		goto skip_cred_build;
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Credential");
+	if (wps->use_cred) {
+		os_memcpy(&wps->cred, wps->use_cred, sizeof(wps->cred));
+		goto use_provided;
+	}
+	os_memset(&wps->cred, 0, sizeof(wps->cred));
+
+	os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
+	wps->cred.ssid_len = wps->wps->ssid_len;
+
+	/* Select the best authentication and encryption type */
+	if (wps->auth_type & WPS_AUTH_WPA2PSK)
+		wps->auth_type = WPS_AUTH_WPA2PSK;
+	else if (wps->auth_type & WPS_AUTH_WPAPSK)
+		wps->auth_type = WPS_AUTH_WPAPSK;
+	else if (wps->auth_type & WPS_AUTH_OPEN)
+		wps->auth_type = WPS_AUTH_OPEN;
+	else {
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported auth_type 0x%x",
+			   wps->auth_type);
+		return -1;
+	}
+	wps->cred.auth_type = wps->auth_type;
+
+	if (wps->auth_type == WPS_AUTH_WPA2PSK ||
+	    wps->auth_type == WPS_AUTH_WPAPSK) {
+		if (wps->encr_type & WPS_ENCR_AES)
+			wps->encr_type = WPS_ENCR_AES;
+		else if (wps->encr_type & WPS_ENCR_TKIP)
+			wps->encr_type = WPS_ENCR_TKIP;
+		else {
+			wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
+				   "type for WPA/WPA2");
+			return -1;
+		}
+	} else {
+		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");
+			return -1;
+		}
+	}
+	wps->cred.encr_type = wps->encr_type;
+	/*
+	 * Set MAC address in the Credential to be the Enrollee's MAC address
+	 */
+	os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN);
+
+	if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->wps->ap &&
+	    !wps->wps->registrar->disable_auto_conf) {
+		u8 r[16];
+		/* Generate a random passphrase */
+		if (random_pool_ready() != 1 ||
+		    random_get_bytes(r, sizeof(r)) < 0) {
+			wpa_printf(MSG_INFO,
+				   "WPS: Could not generate random PSK");
+			return -1;
+		}
+		os_free(wps->new_psk);
+		wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len);
+		if (wps->new_psk == NULL)
+			return -1;
+		wps->new_psk_len--; /* remove newline */
+		while (wps->new_psk_len &&
+		       wps->new_psk[wps->new_psk_len - 1] == '=')
+			wps->new_psk_len--;
+		wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Generated passphrase",
+				      wps->new_psk, wps->new_psk_len);
+		os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len);
+		wps->cred.key_len = wps->new_psk_len;
+	} else if (!wps->wps->registrar->force_per_enrollee_psk &&
+		   wps->use_psk_key && wps->wps->psk_set) {
+		char hex[65];
+		wpa_printf(MSG_DEBUG, "WPS: Use PSK format for Network Key");
+		wpa_snprintf_hex(hex, sizeof(hex), wps->wps->psk, 32);
+		os_memcpy(wps->cred.key, hex, 32 * 2);
+		wps->cred.key_len = 32 * 2;
+	} else if (!wps->wps->registrar->force_per_enrollee_psk &&
+		   wps->wps->network_key) {
+		os_memcpy(wps->cred.key, wps->wps->network_key,
+			  wps->wps->network_key_len);
+		wps->cred.key_len = wps->wps->network_key_len;
+	} else if (wps->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
+		char hex[65];
+		/* Generate a random per-device PSK */
+		os_free(wps->new_psk);
+		wps->new_psk_len = 32;
+		wps->new_psk = os_malloc(wps->new_psk_len);
+		if (wps->new_psk == NULL)
+			return -1;
+		if (random_pool_ready() != 1 ||
+		    random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) {
+			wpa_printf(MSG_INFO,
+				   "WPS: Could not generate random PSK");
+			os_free(wps->new_psk);
+			wps->new_psk = NULL;
+			return -1;
+		}
+		wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
+				wps->new_psk, wps->new_psk_len);
+		wpa_snprintf_hex(hex, sizeof(hex), wps->new_psk,
+				 wps->new_psk_len);
+		os_memcpy(wps->cred.key, hex, wps->new_psk_len * 2);
+		wps->cred.key_len = wps->new_psk_len * 2;
+	}
+
+use_provided:
+#ifdef CONFIG_WPS_TESTING
+	if (wps_testing_dummy_cred)
+		cred = wpabuf_alloc(200);
+	else
+		cred = NULL;
+	if (cred) {
+		struct wps_credential dummy;
+		wpa_printf(MSG_DEBUG, "WPS: Add dummy credential");
+		os_memset(&dummy, 0, sizeof(dummy));
+		os_memcpy(dummy.ssid, "dummy", 5);
+		dummy.ssid_len = 5;
+		dummy.auth_type = WPS_AUTH_WPA2PSK;
+		dummy.encr_type = WPS_ENCR_AES;
+		os_memcpy(dummy.key, "dummy psk", 9);
+		dummy.key_len = 9;
+		os_memcpy(dummy.mac_addr, wps->mac_addr_e, ETH_ALEN);
+		wps_build_credential(cred, &dummy);
+		wpa_hexdump_buf(MSG_DEBUG, "WPS: Dummy Credential", cred);
+
+		wpabuf_put_be16(msg, ATTR_CRED);
+		wpabuf_put_be16(msg, wpabuf_len(cred));
+		wpabuf_put_buf(msg, cred);
+
+		wpabuf_free(cred);
+	}
+#endif /* CONFIG_WPS_TESTING */
+
+	cred = wpabuf_alloc(200);
+	if (cred == NULL)
+		return -1;
+
+	if (wps_build_credential(cred, &wps->cred)) {
+		wpabuf_free(cred);
+		return -1;
+	}
+
+	wpabuf_put_be16(msg, ATTR_CRED);
+	wpabuf_put_be16(msg, wpabuf_len(cred));
+	wpabuf_put_buf(msg, cred);
+	wpabuf_free(cred);
+
+skip_cred_build:
+	if (wps->wps->registrar->extra_cred) {
+		wpa_printf(MSG_DEBUG, "WPS:  * Credential (pre-configured)");
+		wpabuf_put_buf(msg, wps->wps->registrar->extra_cred);
+	}
+
+	return 0;
+}
+
+
+static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * AP Settings");
+
+	if (wps_build_credential(msg, &wps->cred))
+		return -1;
+
+	return 0;
+}
+
+
+static struct wpabuf * wps_build_ap_cred(struct wps_data *wps)
+{
+	struct wpabuf *msg, *plain;
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	plain = wpabuf_alloc(200);
+	if (plain == NULL) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	if (wps_build_ap_settings(wps, plain)) {
+		wpabuf_free(plain);
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	wpabuf_put_be16(msg, ATTR_CRED);
+	wpabuf_put_be16(msg, wpabuf_len(plain));
+	wpabuf_put_buf(msg, plain);
+	wpabuf_free(plain);
+
+	return msg;
+}
+
+
+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;
+	wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
+		    wps->nonce_r, WPS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M2");
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M2) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_registrar_nonce(wps, msg) ||
+	    wps_build_uuid_r(wps, msg) ||
+	    wps_build_public_key(wps, msg) ||
+	    wps_derive_keys(wps) ||
+	    wps_build_auth_type_flags(wps, msg) ||
+	    wps_build_encr_type_flags(wps, msg) ||
+	    wps_build_conn_type_flags(wps, msg) ||
+	    wps_build_config_methods_r(wps->wps->registrar, msg) ||
+	    wps_build_device_attrs(&wps->wps->dev, msg) ||
+	    wps_build_rf_bands(&wps->wps->dev, msg,
+			       wps->wps->rf_band_cb(wps->wps->cb_ctx)) ||
+	    wps_build_assoc_state(wps, msg) ||
+	    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)) {
+		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 = config_in_m2 ? RECV_DONE : RECV_M3;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_m2d(struct wps_data *wps)
+{
+	struct wpabuf *msg;
+	u16 err = wps->config_error;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M2D");
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	if (wps->wps->ap && wps->wps->ap_setup_locked &&
+	    err == WPS_CFG_NO_ERROR)
+		err = WPS_CFG_SETUP_LOCKED;
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M2D) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_registrar_nonce(wps, msg) ||
+	    wps_build_uuid_r(wps, msg) ||
+	    wps_build_auth_type_flags(wps, msg) ||
+	    wps_build_encr_type_flags(wps, msg) ||
+	    wps_build_conn_type_flags(wps, msg) ||
+	    wps_build_config_methods_r(wps->wps->registrar, msg) ||
+	    wps_build_device_attrs(&wps->wps->dev, msg) ||
+	    wps_build_rf_bands(&wps->wps->dev, msg,
+			       wps->wps->rf_band_cb(wps->wps->cb_ctx)) ||
+	    wps_build_assoc_state(wps, msg) ||
+	    wps_build_config_error(msg, err) ||
+	    wps_build_os_version(&wps->wps->dev, msg) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	wps->state = RECV_M2D_ACK;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_m4(struct wps_data *wps)
+{
+	struct wpabuf *msg, *plain;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M4");
+
+	wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+
+	plain = wpabuf_alloc(200);
+	if (plain == NULL)
+		return NULL;
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL) {
+		wpabuf_free(plain);
+		return NULL;
+	}
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M4) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_r_hash(wps, msg) ||
+	    wps_build_r_snonce1(wps, plain) ||
+	    wps_build_key_wrap_auth(wps, plain) ||
+	    wps_build_encr_settings(wps, msg, plain) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_authenticator(wps, msg)) {
+		wpabuf_free(plain);
+		wpabuf_free(msg);
+		return NULL;
+	}
+	wpabuf_free(plain);
+
+	wps->state = RECV_M5;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_m6(struct wps_data *wps)
+{
+	struct wpabuf *msg, *plain;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M6");
+
+	plain = wpabuf_alloc(200);
+	if (plain == NULL)
+		return NULL;
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL) {
+		wpabuf_free(plain);
+		return NULL;
+	}
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M6) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_r_snonce2(wps, plain) ||
+	    wps_build_key_wrap_auth(wps, plain) ||
+	    wps_build_encr_settings(wps, msg, plain) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_authenticator(wps, msg)) {
+		wpabuf_free(plain);
+		wpabuf_free(msg);
+		return NULL;
+	}
+	wpabuf_free(plain);
+
+	wps->wps_pin_revealed = 1;
+	wps->state = RECV_M7;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_m8(struct wps_data *wps)
+{
+	struct wpabuf *msg, *plain;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M8");
+
+	plain = wpabuf_alloc(500);
+	if (plain == NULL)
+		return NULL;
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL) {
+		wpabuf_free(plain);
+		return NULL;
+	}
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M8) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    ((wps->wps->ap || wps->er) && wps_build_cred(wps, plain)) ||
+	    (!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) ||
+	    wps_build_key_wrap_auth(wps, plain) ||
+	    wps_build_encr_settings(wps, msg, plain) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_authenticator(wps, msg)) {
+		wpabuf_free(plain);
+		wpabuf_free(msg);
+		return NULL;
+	}
+	wpabuf_free(plain);
+
+	wps->state = RECV_DONE;
+	return msg;
+}
+
+
+struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
+				      enum wsc_op_code *op_code)
+{
+	struct wpabuf *msg;
+
+#ifdef CONFIG_WPS_UPNP
+	if (!wps->int_reg && wps->wps->wps_upnp) {
+		struct upnp_pending_message *p, *prev = NULL;
+		if (wps->ext_reg > 1)
+			wps_registrar_free_pending_m2(wps->wps);
+		p = wps->wps->upnp_msgs;
+		/* TODO: check pending message MAC address */
+		while (p && p->next) {
+			prev = p;
+			p = p->next;
+		}
+		if (p) {
+			wpa_printf(MSG_DEBUG, "WPS: Use pending message from "
+				   "UPnP");
+			if (prev)
+				prev->next = NULL;
+			else
+				wps->wps->upnp_msgs = NULL;
+			msg = p->msg;
+			switch (p->type) {
+			case WPS_WSC_ACK:
+				*op_code = WSC_ACK;
+				break;
+			case WPS_WSC_NACK:
+				*op_code = WSC_NACK;
+				break;
+			default:
+				*op_code = WSC_MSG;
+				break;
+			}
+			os_free(p);
+			if (wps->ext_reg == 0)
+				wps->ext_reg = 1;
+			return msg;
+		}
+	}
+	if (wps->ext_reg) {
+		wpa_printf(MSG_DEBUG, "WPS: Using external Registrar, but no "
+			   "pending message available");
+		return NULL;
+	}
+#endif /* CONFIG_WPS_UPNP */
+
+	switch (wps->state) {
+	case SEND_M2:
+		if (wps_get_dev_password(wps) < 0)
+			msg = wps_build_m2d(wps);
+		else
+			msg = wps_build_m2(wps);
+		*op_code = WSC_MSG;
+		break;
+	case SEND_M2D:
+		msg = wps_build_m2d(wps);
+		*op_code = WSC_MSG;
+		break;
+	case SEND_M4:
+		msg = wps_build_m4(wps);
+		*op_code = WSC_MSG;
+		break;
+	case SEND_M6:
+		msg = wps_build_m6(wps);
+		*op_code = WSC_MSG;
+		break;
+	case SEND_M8:
+		msg = wps_build_m8(wps);
+		*op_code = WSC_MSG;
+		break;
+	case RECV_DONE:
+		msg = wps_build_wsc_ack(wps);
+		*op_code = WSC_ACK;
+		break;
+	case SEND_WSC_NACK:
+		msg = wps_build_wsc_nack(wps);
+		*op_code = WSC_NACK;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
+			   "a message", wps->state);
+		msg = NULL;
+		break;
+	}
+
+	if (*op_code == WSC_MSG && msg) {
+		/* Save a copy of the last message for Authenticator derivation
+		 */
+		wpabuf_free(wps->last_msg);
+		wps->last_msg = wpabuf_dup(msg);
+	}
+
+	return msg;
+}
+
+
+static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
+{
+	if (e_nonce == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
+		return -1;
+	}
+
+	os_memcpy(wps->nonce_e, e_nonce, WPS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
+		    wps->nonce_e, WPS_NONCE_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
+{
+	if (r_nonce == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
+		return -1;
+	}
+
+	if (os_memcmp(wps->nonce_r, r_nonce, WPS_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce received");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wps_process_uuid_e(struct wps_data *wps, const u8 *uuid_e)
+{
+	if (uuid_e == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No UUID-E received");
+		return -1;
+	}
+
+	os_memcpy(wps->uuid_e, uuid_e, WPS_UUID_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", wps->uuid_e, WPS_UUID_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_dev_password_id(struct wps_data *wps, const u8 *pw_id)
+{
+	if (pw_id == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Device Password ID received");
+		return -1;
+	}
+
+	wps->dev_pw_id = WPA_GET_BE16(pw_id);
+	wpa_printf(MSG_DEBUG, "WPS: Device Password ID %d", wps->dev_pw_id);
+
+	return 0;
+}
+
+
+static int wps_process_e_hash1(struct wps_data *wps, const u8 *e_hash1)
+{
+	if (e_hash1 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No E-Hash1 received");
+		return -1;
+	}
+
+	os_memcpy(wps->peer_hash1, e_hash1, WPS_HASH_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", wps->peer_hash1, WPS_HASH_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_e_hash2(struct wps_data *wps, const u8 *e_hash2)
+{
+	if (e_hash2 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No E-Hash2 received");
+		return -1;
+	}
+
+	os_memcpy(wps->peer_hash2, e_hash2, WPS_HASH_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", wps->peer_hash2, WPS_HASH_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_e_snonce1(struct wps_data *wps, const u8 *e_snonce1)
+{
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+
+	if (e_snonce1 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No E-SNonce1 received");
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce1", e_snonce1,
+			WPS_SECRET_NONCE_LEN);
+
+	/* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
+	addr[0] = e_snonce1;
+	len[0] = WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk1;
+	len[1] = WPS_PSK_LEN;
+	addr[2] = wpabuf_head(wps->dh_pubkey_e);
+	len[2] = wpabuf_len(wps->dh_pubkey_e);
+	addr[3] = wpabuf_head(wps->dh_pubkey_r);
+	len[3] = wpabuf_len(wps->dh_pubkey_r);
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+	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;
+		wps_pwd_auth_fail_event(wps->wps, 0, 1, wps->mac_addr_e);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the first "
+		   "half of the device password");
+
+	return 0;
+}
+
+
+static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2)
+{
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+
+	if (e_snonce2 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No E-SNonce2 received");
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce2", e_snonce2,
+			WPS_SECRET_NONCE_LEN);
+
+	/* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
+	addr[0] = e_snonce2;
+	len[0] = WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk2;
+	len[1] = WPS_PSK_LEN;
+	addr[2] = wpabuf_head(wps->dh_pubkey_e);
+	len[2] = wpabuf_len(wps->dh_pubkey_e);
+	addr[3] = wpabuf_head(wps->dh_pubkey_r);
+	len[3] = wpabuf_len(wps->dh_pubkey_r);
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+	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);
+		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+		wps_pwd_auth_fail_event(wps->wps, 0, 2, wps->mac_addr_e);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the second "
+		   "half of the device password");
+	wps->wps_pin_revealed = 0;
+	wps_registrar_unlock_pin(wps->wps->registrar, wps->uuid_e);
+
+	/*
+	 * In case wildcard PIN is used and WPS handshake succeeds in the first
+	 * attempt, wps_registrar_unlock_pin() would not free the PIN, so make
+	 * sure the PIN gets invalidated here.
+	 */
+	wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
+
+	return 0;
+}
+
+
+static int wps_process_mac_addr(struct wps_data *wps, const u8 *mac_addr)
+{
+	if (mac_addr == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No MAC Address received");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee MAC Address " MACSTR,
+		   MAC2STR(mac_addr));
+	os_memcpy(wps->mac_addr_e, mac_addr, ETH_ALEN);
+	os_memcpy(wps->peer_dev.mac_addr, mac_addr, ETH_ALEN);
+
+	return 0;
+}
+
+
+static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
+			      size_t pk_len)
+{
+	if (pk == NULL || pk_len == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
+		return -1;
+	}
+
+	wpabuf_free(wps->dh_pubkey_e);
+	wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len);
+	if (wps->dh_pubkey_e == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth)
+{
+	u16 auth_types;
+
+	if (auth == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Authentication Type flags "
+			   "received");
+		return -1;
+	}
+
+	auth_types = WPA_GET_BE16(auth);
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee Authentication Type flags 0x%x",
+		   auth_types);
+	wps->auth_type = wps->wps->auth_types & auth_types;
+	if (wps->auth_type == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: No match in supported "
+			   "authentication types (own 0x%x Enrollee 0x%x)",
+			   wps->wps->auth_types, auth_types);
+#ifdef WPS_WORKAROUNDS
+		/*
+		 * Some deployed implementations seem to advertise incorrect
+		 * information in this attribute. For example, Linksys WRT350N
+		 * seems to have a byteorder bug that breaks this negotiation.
+		 * In order to interoperate with existing implementations,
+		 * assume that the Enrollee supports everything we do.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee "
+			   "does not advertise supported authentication types "
+			   "correctly");
+		wps->auth_type = wps->wps->auth_types;
+#else /* WPS_WORKAROUNDS */
+		return -1;
+#endif /* WPS_WORKAROUNDS */
+	}
+
+	return 0;
+}
+
+
+static int wps_process_encr_type_flags(struct wps_data *wps, const u8 *encr)
+{
+	u16 encr_types;
+
+	if (encr == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Encryption Type flags "
+			   "received");
+		return -1;
+	}
+
+	encr_types = WPA_GET_BE16(encr);
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee Encryption Type flags 0x%x",
+		   encr_types);
+	wps->encr_type = wps->wps->encr_types & encr_types;
+	if (wps->encr_type == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: No match in supported "
+			   "encryption types (own 0x%x Enrollee 0x%x)",
+			   wps->wps->encr_types, encr_types);
+#ifdef WPS_WORKAROUNDS
+		/*
+		 * Some deployed implementations seem to advertise incorrect
+		 * information in this attribute. For example, Linksys WRT350N
+		 * seems to have a byteorder bug that breaks this negotiation.
+		 * In order to interoperate with existing implementations,
+		 * assume that the Enrollee supports everything we do.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee "
+			   "does not advertise supported encryption types "
+			   "correctly");
+		wps->encr_type = wps->wps->encr_types;
+#else /* WPS_WORKAROUNDS */
+		return -1;
+#endif /* WPS_WORKAROUNDS */
+	}
+
+	return 0;
+}
+
+
+static int wps_process_conn_type_flags(struct wps_data *wps, const u8 *conn)
+{
+	if (conn == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Connection Type flags "
+			   "received");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee Connection Type flags 0x%x",
+		   *conn);
+
+	return 0;
+}
+
+
+static int wps_process_config_methods(struct wps_data *wps, const u8 *methods)
+{
+	u16 m;
+
+	if (methods == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Config Methods received");
+		return -1;
+	}
+
+	m = WPA_GET_BE16(methods);
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee Config Methods 0x%x"
+		   "%s%s%s%s%s%s%s%s%s", m,
+		   m & WPS_CONFIG_USBA ? " [USBA]" : "",
+		   m & WPS_CONFIG_ETHERNET ? " [Ethernet]" : "",
+		   m & WPS_CONFIG_LABEL ? " [Label]" : "",
+		   m & WPS_CONFIG_DISPLAY ? " [Display]" : "",
+		   m & WPS_CONFIG_EXT_NFC_TOKEN ? " [Ext NFC Token]" : "",
+		   m & WPS_CONFIG_INT_NFC_TOKEN ? " [Int NFC Token]" : "",
+		   m & WPS_CONFIG_NFC_INTERFACE ? " [NFC]" : "",
+		   m & WPS_CONFIG_PUSHBUTTON ? " [PBC]" : "",
+		   m & WPS_CONFIG_KEYPAD ? " [Keypad]" : "");
+
+	if (!(m & WPS_CONFIG_DISPLAY) && !wps->use_psk_key) {
+		/*
+		 * The Enrollee does not have a display so it is unlikely to be
+		 * able to show the passphrase to a user and as such, could
+		 * benefit from receiving PSK to reduce key derivation time.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS: Prefer PSK format key due to "
+			   "Enrollee not supporting display");
+		wps->use_psk_key = 1;
+	}
+
+	return 0;
+}
+
+
+static int wps_process_wps_state(struct wps_data *wps, const u8 *state)
+{
+	if (state == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Wi-Fi Protected Setup State "
+			   "received");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee Wi-Fi Protected Setup State %d",
+		   *state);
+
+	return 0;
+}
+
+
+static int wps_process_assoc_state(struct wps_data *wps, const u8 *assoc)
+{
+	u16 a;
+
+	if (assoc == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Association State received");
+		return -1;
+	}
+
+	a = WPA_GET_BE16(assoc);
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee Association State %d", a);
+
+	return 0;
+}
+
+
+static int wps_process_config_error(struct wps_data *wps, const u8 *err)
+{
+	u16 e;
+
+	if (err == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Configuration Error received");
+		return -1;
+	}
+
+	e = WPA_GET_BE16(err);
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee Configuration Error %d", e);
+
+	return 0;
+}
+
+
+static int wps_registrar_p2p_dev_addr_match(struct wps_data *wps)
+{
+#ifdef CONFIG_P2P
+	struct wps_registrar *reg = wps->wps->registrar;
+
+	if (is_zero_ether_addr(reg->p2p_dev_addr))
+		return 1; /* no filtering in use */
+
+	if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: No match on P2P Device Address "
+			   "filtering for PBC: expected " MACSTR " was "
+			   MACSTR " - indicate PBC session overlap",
+			   MAC2STR(reg->p2p_dev_addr),
+			   MAC2STR(wps->p2p_dev_addr));
+		return 0;
+	}
+#endif /* CONFIG_P2P */
+	return 1;
+}
+
+
+static int wps_registrar_skip_overlap(struct wps_data *wps)
+{
+#ifdef CONFIG_P2P
+	struct wps_registrar *reg = wps->wps->registrar;
+
+	if (is_zero_ether_addr(reg->p2p_dev_addr))
+		return 0; /* no specific Enrollee selected */
+
+	if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Skip PBC overlap due to selected "
+			   "Enrollee match");
+		return 1;
+	}
+#endif /* CONFIG_P2P */
+	return 0;
+}
+
+
+static enum wps_process_res wps_process_m1(struct wps_data *wps,
+					   struct wps_parse_attr *attr)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Received M1");
+
+	if (wps->state != RECV_M1) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M1", wps->state);
+		return WPS_FAILURE;
+	}
+
+	if (wps_process_uuid_e(wps, attr->uuid_e) ||
+	    wps_process_mac_addr(wps, attr->mac_addr) ||
+	    wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+	    wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
+	    wps_process_auth_type_flags(wps, attr->auth_type_flags) ||
+	    wps_process_encr_type_flags(wps, attr->encr_type_flags) ||
+	    wps_process_conn_type_flags(wps, attr->conn_type_flags) ||
+	    wps_process_config_methods(wps, attr->config_methods) ||
+	    wps_process_wps_state(wps, attr->wps_state) ||
+	    wps_process_device_attrs(&wps->peer_dev, attr) ||
+	    wps_process_rf_bands(&wps->peer_dev, attr->rf_bands) ||
+	    wps_process_assoc_state(wps, attr->assoc_state) ||
+	    wps_process_dev_password_id(wps, attr->dev_password_id) ||
+	    wps_process_config_error(wps, attr->config_error) ||
+	    wps_process_os_version(&wps->peer_dev, attr->os_version))
+		return WPS_FAILURE;
+
+	if (wps->dev_pw_id < 0x10 &&
+	    wps->dev_pw_id != DEV_PW_DEFAULT &&
+	    wps->dev_pw_id != DEV_PW_P2PS_DEFAULT &&
+	    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",
+			   wps->dev_pw_id);
+		wps->state = SEND_M2D;
+		return WPS_CONTINUE;
+	}
+
+#ifdef CONFIG_WPS_NFC
+	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 && token->peer_pk_hash_known) {
+			size_t len;
+
+			wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
+				   "Password Token");
+			dl_list_del(&token->list);
+			wps->nfc_pw_token = token;
+
+			addr[0] = attr->public_key;
+			len = attr->public_key_len;
+			sha256_vector(1, addr, &len, hash);
+			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");
+				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 */
+
+	if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) {
+		if ((wps->wps->registrar->force_pbc_overlap ||
+		     wps_registrar_pbc_overlap(wps->wps->registrar,
+					       wps->mac_addr_e, wps->uuid_e) ||
+		     !wps_registrar_p2p_dev_addr_match(wps)) &&
+		    !wps_registrar_skip_overlap(wps)) {
+			wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC "
+				   "negotiation");
+			wps->state = SEND_M2D;
+			wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+			wps_pbc_overlap_event(wps->wps);
+			wps_fail_event(wps->wps, WPS_M1,
+				       WPS_CFG_MULTIPLE_PBC_DETECTED,
+				       WPS_EI_NO_ERROR, wps->mac_addr_e);
+			wps->wps->registrar->force_pbc_overlap = 1;
+			return WPS_CONTINUE;
+		}
+		wps_registrar_add_pbc_session(wps->wps->registrar,
+					      wps->mac_addr_e, wps->uuid_e);
+		wps->pbc = 1;
+	}
+
+#ifdef WPS_WORKAROUNDS
+	/*
+	 * It looks like Mac OS X 10.6.3 and 10.6.4 do not like Network Key in
+	 * passphrase format. To avoid interop issues, force PSK format to be
+	 * used.
+	 */
+	if (!wps->use_psk_key &&
+	    wps->peer_dev.manufacturer &&
+	    os_strncmp(wps->peer_dev.manufacturer, "Apple ", 6) == 0 &&
+	    wps->peer_dev.model_name &&
+	    os_strcmp(wps->peer_dev.model_name, "AirPort") == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Workaround - Force Network Key in "
+			   "PSK format");
+		wps->use_psk_key = 1;
+	}
+#endif /* WPS_WORKAROUNDS */
+
+	wps->state = SEND_M2;
+	return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m3(struct wps_data *wps,
+					   const struct wpabuf *msg,
+					   struct wps_parse_attr *attr)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Received M3");
+
+	if (wps->state != RECV_M3) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M3", wps->state);
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+	    !wps_registrar_skip_overlap(wps)) {
+		wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
+			   "session overlap");
+		wps->state = SEND_WSC_NACK;
+		wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+		return WPS_CONTINUE;
+	}
+
+	if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+	    wps_process_authenticator(wps, attr->authenticator, msg) ||
+	    wps_process_e_hash1(wps, attr->e_hash1) ||
+	    wps_process_e_hash2(wps, attr->e_hash2)) {
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	wps->state = SEND_M4;
+	return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m5(struct wps_data *wps,
+					   const struct wpabuf *msg,
+					   struct wps_parse_attr *attr)
+{
+	struct wpabuf *decrypted;
+	struct wps_parse_attr eattr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received M5");
+
+	if (wps->state != RECV_M5) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M5", wps->state);
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+	    !wps_registrar_skip_overlap(wps)) {
+		wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
+			   "session overlap");
+		wps->state = SEND_WSC_NACK;
+		wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+		return WPS_CONTINUE;
+	}
+
+	if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+	    wps_process_authenticator(wps, attr->authenticator, msg)) {
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+					      attr->encr_settings_len);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+			   "Settings attribute");
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	if (wps_validate_m5_encr(decrypted, attr->version2 != NULL) < 0) {
+		wpabuf_free(decrypted);
+		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_e_snonce1(wps, eattr.e_snonce1)) {
+		wpabuf_free(decrypted);
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+	wpabuf_free(decrypted);
+
+	wps->state = SEND_M6;
+	return WPS_CONTINUE;
+}
+
+
+static void wps_sta_cred_cb(struct wps_data *wps)
+{
+	/*
+	 * Update credential to only include a single authentication and
+	 * encryption type in case the AP configuration includes more than one
+	 * option.
+	 */
+	if (wps->cred.auth_type & WPS_AUTH_WPA2PSK)
+		wps->cred.auth_type = WPS_AUTH_WPA2PSK;
+	else if (wps->cred.auth_type & WPS_AUTH_WPAPSK)
+		wps->cred.auth_type = WPS_AUTH_WPAPSK;
+	if (wps->cred.encr_type & WPS_ENCR_AES)
+		wps->cred.encr_type = WPS_ENCR_AES;
+	else if (wps->cred.encr_type & WPS_ENCR_TKIP)
+		wps->cred.encr_type = WPS_ENCR_TKIP;
+	wpa_printf(MSG_DEBUG, "WPS: Update local configuration based on the "
+		   "AP configuration");
+	if (wps->wps->cred_cb)
+		wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
+}
+
+
+static void wps_cred_update(struct wps_credential *dst,
+			    struct wps_credential *src)
+{
+	os_memcpy(dst->ssid, src->ssid, sizeof(dst->ssid));
+	dst->ssid_len = src->ssid_len;
+	dst->auth_type = src->auth_type;
+	dst->encr_type = src->encr_type;
+	dst->key_idx = src->key_idx;
+	os_memcpy(dst->key, src->key, sizeof(dst->key));
+	dst->key_len = src->key_len;
+}
+
+
+static int wps_process_ap_settings_r(struct wps_data *wps,
+				     struct wps_parse_attr *attr)
+{
+	struct wpabuf *msg;
+
+	if (wps->wps->ap || wps->er)
+		return 0;
+
+	/* AP Settings Attributes in M7 when Enrollee is an AP */
+	if (wps_process_ap_settings(attr, &wps->cred) < 0)
+		return -1;
+
+	wpa_printf(MSG_INFO, "WPS: Received old AP configuration from AP");
+
+	if (wps->new_ap_settings) {
+		wpa_printf(MSG_INFO, "WPS: Update AP configuration based on "
+			   "new settings");
+		wps_cred_update(&wps->cred, wps->new_ap_settings);
+		return 0;
+	} else {
+		/*
+		 * Use the AP PIN only to receive the current AP settings, not
+		 * to reconfigure the AP.
+		 */
+
+		/*
+		 * Clear selected registrar here since we do not get to
+		 * WSC_Done in this protocol run.
+		 */
+		wps_registrar_pin_completed(wps->wps->registrar);
+
+		msg = wps_build_ap_cred(wps);
+		if (msg == NULL)
+			return -1;
+		wps->cred.cred_attr = wpabuf_head(msg);
+		wps->cred.cred_attr_len = wpabuf_len(msg);
+
+		if (wps->ap_settings_cb) {
+			wps->ap_settings_cb(wps->ap_settings_cb_ctx,
+					    &wps->cred);
+			wpabuf_free(msg);
+			return 1;
+		}
+		wps_sta_cred_cb(wps);
+
+		wps->cred.cred_attr = NULL;
+		wps->cred.cred_attr_len = 0;
+		wpabuf_free(msg);
+
+		return 1;
+	}
+}
+
+
+static enum wps_process_res wps_process_m7(struct wps_data *wps,
+					   const struct wpabuf *msg,
+					   struct wps_parse_attr *attr)
+{
+	struct wpabuf *decrypted;
+	struct wps_parse_attr eattr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received M7");
+
+	if (wps->state != RECV_M7) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M7", wps->state);
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+	    !wps_registrar_skip_overlap(wps)) {
+		wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
+			   "session overlap");
+		wps->state = SEND_WSC_NACK;
+		wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+		return WPS_CONTINUE;
+	}
+
+	if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+	    wps_process_authenticator(wps, attr->authenticator, msg)) {
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	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;
+	}
+
+	if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er,
+				 attr->version2 != NULL) < 0) {
+		wpabuf_free(decrypted);
+		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_e_snonce2(wps, eattr.e_snonce2) ||
+	    wps_process_ap_settings_r(wps, &eattr)) {
+		wpabuf_free(decrypted);
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	wpabuf_free(decrypted);
+
+	wps->state = SEND_M8;
+	return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
+						const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+	enum wps_process_res ret = WPS_CONTINUE;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return WPS_FAILURE;
+
+	if (attr.msg_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	if (*attr.msg_type != WPS_M1 &&
+	    (attr.registrar_nonce == NULL ||
+	     os_memcmp(wps->nonce_r, attr.registrar_nonce,
+		       WPS_NONCE_LEN) != 0)) {
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+		return WPS_FAILURE;
+	}
+
+	switch (*attr.msg_type) {
+	case WPS_M1:
+		if (wps_validate_m1(msg) < 0)
+			return WPS_FAILURE;
+#ifdef CONFIG_WPS_UPNP
+		if (wps->wps->wps_upnp && attr.mac_addr) {
+			/* Remove old pending messages when starting new run */
+			wps_free_pending_msgs(wps->wps->upnp_msgs);
+			wps->wps->upnp_msgs = NULL;
+
+			upnp_wps_device_send_wlan_event(
+				wps->wps->wps_upnp, attr.mac_addr,
+				UPNP_WPS_WLANEVENT_TYPE_EAP, msg);
+		}
+#endif /* CONFIG_WPS_UPNP */
+		ret = wps_process_m1(wps, &attr);
+		break;
+	case WPS_M3:
+		if (wps_validate_m3(msg) < 0)
+			return WPS_FAILURE;
+		ret = wps_process_m3(wps, msg, &attr);
+		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+			wps_fail_event(wps->wps, WPS_M3, wps->config_error,
+				       wps->error_indication, wps->mac_addr_e);
+		break;
+	case WPS_M5:
+		if (wps_validate_m5(msg) < 0)
+			return WPS_FAILURE;
+		ret = wps_process_m5(wps, msg, &attr);
+		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+			wps_fail_event(wps->wps, WPS_M5, wps->config_error,
+				       wps->error_indication, wps->mac_addr_e);
+		break;
+	case WPS_M7:
+		if (wps_validate_m7(msg) < 0)
+			return WPS_FAILURE;
+		ret = wps_process_m7(wps, msg, &attr);
+		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+			wps_fail_event(wps->wps, WPS_M7, wps->config_error,
+				       wps->error_indication, wps->mac_addr_e);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
+			   *attr.msg_type);
+		return WPS_FAILURE;
+	}
+
+	if (ret == WPS_CONTINUE) {
+		/* Save a copy of the last message for Authenticator derivation
+		 */
+		wpabuf_free(wps->last_msg);
+		wps->last_msg = wpabuf_dup(msg);
+	}
+
+	return ret;
+}
+
+
+static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
+						const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return WPS_FAILURE;
+
+	if (attr.msg_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+		return WPS_FAILURE;
+	}
+
+	if (*attr.msg_type != WPS_WSC_ACK) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+			   *attr.msg_type);
+		return WPS_FAILURE;
+	}
+
+#ifdef CONFIG_WPS_UPNP
+	if (wps->wps->wps_upnp && wps->ext_reg && wps->state == RECV_M2D_ACK &&
+	    upnp_wps_subscribers(wps->wps->wps_upnp)) {
+		if (wps->wps->upnp_msgs)
+			return WPS_CONTINUE;
+		wpa_printf(MSG_DEBUG, "WPS: Wait for response from an "
+			   "external Registrar");
+		return WPS_PENDING;
+	}
+#endif /* CONFIG_WPS_UPNP */
+
+	if (attr.registrar_nonce == NULL ||
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
+	{
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+		return WPS_FAILURE;
+	}
+
+	if (attr.enrollee_nonce == NULL ||
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+		return WPS_FAILURE;
+	}
+
+	if (wps->state == RECV_M2D_ACK) {
+#ifdef CONFIG_WPS_UPNP
+		if (wps->wps->wps_upnp &&
+		    upnp_wps_subscribers(wps->wps->wps_upnp)) {
+			if (wps->wps->upnp_msgs)
+				return WPS_CONTINUE;
+			if (wps->ext_reg == 0)
+				wps->ext_reg = 1;
+			wpa_printf(MSG_DEBUG, "WPS: Wait for response from an "
+				   "external Registrar");
+			return WPS_PENDING;
+		}
+#endif /* CONFIG_WPS_UPNP */
+
+		wpa_printf(MSG_DEBUG, "WPS: No more registrars available - "
+			   "terminate negotiation");
+	}
+
+	return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
+						 const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+	int old_state;
+	u16 config_error;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
+
+	old_state = wps->state;
+	wps->state = SEND_WSC_NACK;
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return WPS_FAILURE;
+
+	if (attr.msg_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+		return WPS_FAILURE;
+	}
+
+	if (*attr.msg_type != WPS_WSC_NACK) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+			   *attr.msg_type);
+		return WPS_FAILURE;
+	}
+
+#ifdef CONFIG_WPS_UPNP
+	if (wps->wps->wps_upnp && wps->ext_reg) {
+		wpa_printf(MSG_DEBUG, "WPS: Negotiation using external "
+			   "Registrar terminated by the Enrollee");
+		return WPS_FAILURE;
+	}
+#endif /* CONFIG_WPS_UPNP */
+
+	if (attr.registrar_nonce == NULL ||
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
+	{
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+		return WPS_FAILURE;
+	}
+
+	if (attr.enrollee_nonce == NULL ||
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+		return WPS_FAILURE;
+	}
+
+	if (attr.config_error == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
+			   "in WSC_NACK");
+		return WPS_FAILURE;
+	}
+
+	config_error = WPA_GET_BE16(attr.config_error);
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with "
+		   "Configuration Error %d", config_error);
+
+	switch (old_state) {
+	case RECV_M3:
+		wps_fail_event(wps->wps, WPS_M2, config_error,
+			       wps->error_indication, wps->mac_addr_e);
+		break;
+	case RECV_M5:
+		wps_fail_event(wps->wps, WPS_M4, config_error,
+			       wps->error_indication, wps->mac_addr_e);
+		break;
+	case RECV_M7:
+		wps_fail_event(wps->wps, WPS_M6, config_error,
+			       wps->error_indication, wps->mac_addr_e);
+		break;
+	case RECV_DONE:
+		wps_fail_event(wps->wps, WPS_M8, config_error,
+			       wps->error_indication, wps->mac_addr_e);
+		break;
+	default:
+		break;
+	}
+
+	return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
+						 const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received WSC_Done");
+
+	if (wps->state != RECV_DONE &&
+	    (!wps->wps->wps_upnp || !wps->ext_reg)) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving WSC_Done", wps->state);
+		return WPS_FAILURE;
+	}
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return WPS_FAILURE;
+
+	if (attr.msg_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+		return WPS_FAILURE;
+	}
+
+	if (*attr.msg_type != WPS_WSC_DONE) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+			   *attr.msg_type);
+		return WPS_FAILURE;
+	}
+
+#ifdef CONFIG_WPS_UPNP
+	if (wps->wps->wps_upnp && wps->ext_reg) {
+		wpa_printf(MSG_DEBUG, "WPS: Negotiation using external "
+			   "Registrar completed successfully");
+		wps_device_store(wps->wps->registrar, &wps->peer_dev,
+				 wps->uuid_e);
+		return WPS_DONE;
+	}
+#endif /* CONFIG_WPS_UPNP */
+
+	if (attr.registrar_nonce == NULL ||
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
+	{
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+		return WPS_FAILURE;
+	}
+
+	if (attr.enrollee_nonce == NULL ||
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+		return WPS_FAILURE;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Negotiation completed successfully");
+	wps_device_store(wps->wps->registrar, &wps->peer_dev,
+			 wps->uuid_e);
+
+	if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->new_psk &&
+	    wps->wps->ap && !wps->wps->registrar->disable_auto_conf) {
+		struct wps_credential cred;
+
+		wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based "
+			   "on first Enrollee connection");
+
+		os_memset(&cred, 0, sizeof(cred));
+		os_memcpy(cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
+		cred.ssid_len = wps->wps->ssid_len;
+		if (wps->wps->rf_band_cb(wps->wps->cb_ctx) == WPS_RF_60GHZ) {
+			cred.auth_type = WPS_AUTH_WPA2PSK;
+			cred.encr_type = WPS_ENCR_AES;
+		} else {
+			cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK;
+			cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES;
+		}
+		os_memcpy(cred.key, wps->new_psk, wps->new_psk_len);
+		cred.key_len = wps->new_psk_len;
+
+		wps->wps->wps_state = WPS_STATE_CONFIGURED;
+		wpa_hexdump_ascii_key(MSG_DEBUG,
+				      "WPS: Generated random passphrase",
+				      wps->new_psk, wps->new_psk_len);
+		if (wps->wps->cred_cb)
+			wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+
+		os_free(wps->new_psk);
+		wps->new_psk = NULL;
+	}
+
+	if (!wps->wps->ap && !wps->er)
+		wps_sta_cred_cb(wps);
+
+	if (wps->new_psk) {
+		if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e,
+				   wps->p2p_dev_addr, wps->new_psk,
+				   wps->new_psk_len)) {
+			wpa_printf(MSG_DEBUG, "WPS: Failed to configure the "
+				   "new PSK");
+		}
+		os_free(wps->new_psk);
+		wps->new_psk = NULL;
+	}
+
+	wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e,
+			   wps->dev_password, wps->dev_password_len);
+
+	if (wps->pbc) {
+		wps_registrar_remove_pbc_session(wps->wps->registrar,
+						 wps->uuid_e,
+						 wps->p2p_dev_addr);
+		wps_registrar_pbc_completed(wps->wps->registrar);
+#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 {
+		wps_registrar_pin_completed(wps->wps->registrar);
+	}
+	/* TODO: maintain AuthorizedMACs somewhere separately for each ER and
+	 * merge them into APs own list.. */
+
+	wps_success_event(wps->wps, wps->mac_addr_e);
+
+	return WPS_DONE;
+}
+
+
+enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
+					       enum wsc_op_code op_code,
+					       const struct wpabuf *msg)
+{
+	enum wps_process_res ret;
+
+	wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
+		   "op_code=%d)",
+		   (unsigned long) wpabuf_len(msg), op_code);
+
+#ifdef CONFIG_WPS_UPNP
+	if (wps->wps->wps_upnp && op_code == WSC_MSG && wps->ext_reg == 1) {
+		struct wps_parse_attr attr;
+		if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type &&
+		    *attr.msg_type == WPS_M3)
+			wps->ext_reg = 2; /* past M2/M2D phase */
+	}
+	if (wps->ext_reg > 1)
+		wps_registrar_free_pending_m2(wps->wps);
+	if (wps->wps->wps_upnp && wps->ext_reg &&
+	    wps->wps->upnp_msgs == NULL &&
+	    (op_code == WSC_MSG || op_code == WSC_Done || op_code == WSC_NACK))
+	{
+		struct wps_parse_attr attr;
+		int type;
+		if (wps_parse_msg(msg, &attr) < 0 || attr.msg_type == NULL)
+			type = -1;
+		else
+			type = *attr.msg_type;
+		wpa_printf(MSG_DEBUG, "WPS: Sending received message (type %d)"
+			   " to external Registrar for processing", type);
+		upnp_wps_device_send_wlan_event(wps->wps->wps_upnp,
+						wps->mac_addr_e,
+						UPNP_WPS_WLANEVENT_TYPE_EAP,
+						msg);
+		if (op_code == WSC_MSG)
+			return WPS_PENDING;
+	} else if (wps->wps->wps_upnp && wps->ext_reg && op_code == WSC_MSG) {
+		wpa_printf(MSG_DEBUG, "WPS: Skip internal processing - using "
+			   "external Registrar");
+		return WPS_CONTINUE;
+	}
+#endif /* CONFIG_WPS_UPNP */
+
+	switch (op_code) {
+	case WSC_MSG:
+		return wps_process_wsc_msg(wps, msg);
+	case WSC_ACK:
+		if (wps_validate_wsc_ack(msg) < 0)
+			return WPS_FAILURE;
+		return wps_process_wsc_ack(wps, msg);
+	case WSC_NACK:
+		if (wps_validate_wsc_nack(msg) < 0)
+			return WPS_FAILURE;
+		return wps_process_wsc_nack(wps, msg);
+	case WSC_Done:
+		if (wps_validate_wsc_done(msg) < 0)
+			return WPS_FAILURE;
+		ret = wps_process_wsc_done(wps, msg);
+		if (ret == WPS_FAILURE) {
+			wps->state = SEND_WSC_NACK;
+			wps_fail_event(wps->wps, WPS_WSC_DONE,
+				       wps->config_error,
+				       wps->error_indication, wps->mac_addr_e);
+		}
+		return ret;
+	default:
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
+		return WPS_FAILURE;
+	}
+}
+
+
+int wps_registrar_update_ie(struct wps_registrar *reg)
+{
+	return wps_set_ie(reg);
+}
+
+
+static void wps_registrar_set_selected_timeout(void *eloop_ctx,
+					       void *timeout_ctx)
+{
+	struct wps_registrar *reg = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "WPS: Selected Registrar timeout - "
+		   "unselect internal Registrar");
+	reg->selected_registrar = 0;
+	reg->pbc = 0;
+	wps_registrar_selected_registrar_changed(reg, 0);
+}
+
+
+#ifdef CONFIG_WPS_UPNP
+static void wps_registrar_sel_reg_add(struct wps_registrar *reg,
+				      struct subscription *s)
+{
+	int i, j;
+	wpa_printf(MSG_DEBUG, "WPS: External Registrar selected (dev_pw_id=%d "
+		   "config_methods=0x%x)",
+		   s->dev_password_id, s->config_methods);
+	reg->sel_reg_union = 1;
+	if (reg->sel_reg_dev_password_id_override != DEV_PW_PUSHBUTTON)
+		reg->sel_reg_dev_password_id_override = s->dev_password_id;
+	if (reg->sel_reg_config_methods_override == -1)
+		reg->sel_reg_config_methods_override = 0;
+	reg->sel_reg_config_methods_override |= s->config_methods;
+	for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
+		if (is_zero_ether_addr(reg->authorized_macs_union[i]))
+			break;
+	for (j = 0; i < WPS_MAX_AUTHORIZED_MACS && j < WPS_MAX_AUTHORIZED_MACS;
+	     j++) {
+		if (is_zero_ether_addr(s->authorized_macs[j]))
+			break;
+		wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC into union: "
+			   MACSTR, MAC2STR(s->authorized_macs[j]));
+		os_memcpy(reg->authorized_macs_union[i],
+			  s->authorized_macs[j], ETH_ALEN);
+		i++;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union",
+		    (u8 *) reg->authorized_macs_union,
+		    sizeof(reg->authorized_macs_union));
+}
+#endif /* CONFIG_WPS_UPNP */
+
+
+static void wps_registrar_sel_reg_union(struct wps_registrar *reg)
+{
+#ifdef CONFIG_WPS_UPNP
+	struct subscription *s;
+
+	if (reg->wps->wps_upnp == NULL)
+		return;
+
+	dl_list_for_each(s, &reg->wps->wps_upnp->subscriptions,
+			 struct subscription, list) {
+		struct subscr_addr *sa;
+		sa = dl_list_first(&s->addr_list, struct subscr_addr, list);
+		if (sa) {
+			wpa_printf(MSG_DEBUG, "WPS: External Registrar %s:%d",
+				   inet_ntoa(sa->saddr.sin_addr),
+				   ntohs(sa->saddr.sin_port));
+		}
+		if (s->selected_registrar)
+			wps_registrar_sel_reg_add(reg, s);
+		else
+			wpa_printf(MSG_DEBUG, "WPS: External Registrar not "
+				   "selected");
+	}
+#endif /* CONFIG_WPS_UPNP */
+}
+
+
+/**
+ * wps_registrar_selected_registrar_changed - SetSelectedRegistrar change
+ * @reg: Registrar data from wps_registrar_init()
+ *
+ * This function is called when selected registrar state changes, e.g., when an
+ * AP receives a SetSelectedRegistrar UPnP message.
+ */
+void wps_registrar_selected_registrar_changed(struct wps_registrar *reg,
+					      u16 dev_pw_id)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Selected registrar information changed");
+
+	reg->sel_reg_union = reg->selected_registrar;
+	reg->sel_reg_dev_password_id_override = -1;
+	reg->sel_reg_config_methods_override = -1;
+	os_memcpy(reg->authorized_macs_union, reg->authorized_macs,
+		  WPS_MAX_AUTHORIZED_MACS * ETH_ALEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union (start with own)",
+		    (u8 *) reg->authorized_macs_union,
+		    sizeof(reg->authorized_macs_union));
+	if (reg->selected_registrar) {
+		u16 methods;
+
+		methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+		methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+			     WPS_CONFIG_PHY_PUSHBUTTON);
+		if (reg->pbc) {
+			reg->sel_reg_dev_password_id_override =
+				DEV_PW_PUSHBUTTON;
+			wps_set_pushbutton(&methods, reg->wps->config_methods);
+		} else if (dev_pw_id)
+			reg->sel_reg_dev_password_id_override = dev_pw_id;
+		wpa_printf(MSG_DEBUG, "WPS: Internal Registrar selected "
+			   "(pbc=%d)", reg->pbc);
+		reg->sel_reg_config_methods_override = methods;
+	} else
+		wpa_printf(MSG_DEBUG, "WPS: Internal Registrar not selected");
+
+	wps_registrar_sel_reg_union(reg);
+
+	wps_set_ie(reg);
+	wps_cb_set_sel_reg(reg);
+}
+
+
+int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
+			   char *buf, size_t buflen)
+{
+	struct wps_registrar_device *d;
+	int len = 0, ret;
+	char uuid[40];
+	char devtype[WPS_DEV_TYPE_BUFSIZE];
+
+	d = wps_device_get(reg, addr);
+	if (d == NULL)
+		return 0;
+	if (uuid_bin2str(d->uuid, uuid, sizeof(uuid)))
+		return 0;
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "wpsUuid=%s\n"
+			  "wpsPrimaryDeviceType=%s\n"
+			  "wpsDeviceName=%s\n"
+			  "wpsManufacturer=%s\n"
+			  "wpsModelName=%s\n"
+			  "wpsModelNumber=%s\n"
+			  "wpsSerialNumber=%s\n",
+			  uuid,
+			  wps_dev_type_bin2str(d->dev.pri_dev_type, devtype,
+					       sizeof(devtype)),
+			  d->dev.device_name ? d->dev.device_name : "",
+			  d->dev.manufacturer ? d->dev.manufacturer : "",
+			  d->dev.model_name ? d->dev.model_name : "",
+			  d->dev.model_number ? d->dev.model_number : "",
+			  d->dev.serial_number ? d->dev.serial_number : "");
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	return len;
+}
+
+
+int wps_registrar_config_ap(struct wps_registrar *reg,
+			    struct wps_credential *cred)
+{
+	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) {
+			wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
+				   "due to WEP configuration");
+			return -1;
+		}
+
+		wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
+			   "invalid encr_type 0x%x", cred->encr_type);
+		return -1;
+	}
+
+	if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
+	    WPS_ENCR_TKIP) {
+		wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
+			   "TKIP+AES");
+		cred->encr_type |= WPS_ENCR_AES;
+	}
+
+	if ((cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+	    WPS_AUTH_WPAPSK) {
+		wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
+			   "WPAPSK+WPA2PSK");
+		cred->auth_type |= WPS_AUTH_WPA2PSK;
+	}
+
+	if (reg->wps->cred_cb)
+		return reg->wps->cred_cb(reg->wps->cb_ctx, cred);
+
+	return -1;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+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,
+				   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;
+
+	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;
+	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);
+
+	reg->selected_registrar = 1;
+	reg->pbc = 0;
+	wps_registrar_add_authorized_mac(reg,
+					 (u8 *) "\xff\xff\xff\xff\xff\xff");
+	wps_registrar_selected_registrar_changed(reg, pw_id);
+	eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+	eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+			       wps_registrar_set_selected_timeout,
+			       reg, NULL);
+
+	wpa_printf(MSG_DEBUG, "WPS: Added NFC Device Password %u to Registrar",
+		   pw_id);
+
+	return 0;
+}
+
+
+int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
+					 const u8 *oob_dev_pw,
+					 size_t oob_dev_pw_len)
+{
+	const u8 *pos, *hash, *dev_pw;
+	u16 id;
+	size_t dev_pw_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;
+
+	hash = oob_dev_pw;
+	pos = oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN;
+	id = WPA_GET_BE16(pos);
+	dev_pw = pos + 2;
+	dev_pw_len = oob_dev_pw + oob_dev_pw_len - dev_pw;
+
+	wpa_printf(MSG_DEBUG, "WPS: Add NFC Password Token for Password ID %u",
+		   id);
+
+	wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash",
+		    hash, WPS_OOB_PUBKEY_HASH_LEN);
+	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, 0);
+}
+
+
+void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
+				       struct wps_nfc_pw_token *token)
+{
+	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/hostap/src/wps/wps_upnp.c b/hostap/src/wps/wps_upnp.c
new file mode 100644
index 0000000..44318e0
--- /dev/null
+++ b/hostap/src/wps/wps_upnp.c
@@ -0,0 +1,1212 @@
+/*
+ * UPnP WPS Device
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * See below for more details on licensing and code history.
+ */
+
+/*
+ * This has been greatly stripped down from the original file
+ * (upnp_wps_device.c) by Ted Merrill, Atheros Communications
+ * in order to eliminate use of the bulky libupnp library etc.
+ *
+ * History:
+ * upnp_wps_device.c is/was a shim layer between wps_opt_upnp.c and
+ * the libupnp library.
+ * The layering (by Sony) was well done; only a very minor modification
+ * to API of upnp_wps_device.c was required.
+ * libupnp was found to be undesirable because:
+ * -- It consumed too much code and data space
+ * -- It uses multiple threads, making debugging more difficult
+ *      and possibly reducing reliability.
+ * -- It uses static variables and only supports one instance.
+ * The shim and libupnp are here replaced by special code written
+ * specifically for the needs of hostapd.
+ * Various shortcuts can and are taken to keep the code size small.
+ * Generally, execution time is not as crucial.
+ *
+ * BUGS:
+ * -- UPnP requires that we be able to resolve domain names.
+ * While uncommon, if we have to do it then it will stall the entire
+ * hostapd program, which is bad.
+ * This is because we use the standard linux getaddrinfo() function
+ * which is syncronous.
+ * An asyncronous solution would be to use the free "ares" library.
+ * -- Does not have a robust output buffering scheme.  Uses a single
+ * fixed size output buffer per TCP/HTTP connection, with possible (although
+ * unlikely) possibility of overflow and likely excessive use of RAM.
+ * A better solution would be to write the HTTP output as a buffered stream,
+ * using chunking: (handle header specially, then) generate data with
+ * a printf-like function into a buffer, catching buffer full condition,
+ * then send it out surrounded by http chunking.
+ * -- There is some code that could be separated out into the common
+ * library to be shared with wpa_supplicant.
+ * -- Needs renaming with module prefix to avoid polluting the debugger
+ * namespace and causing possible collisions with other static fncs
+ * and structure declarations when using the debugger.
+ * -- The http error code generation is pretty bogus, hopefully no one cares.
+ *
+ * Author: Ted Merrill, Atheros Communications, based upon earlier work
+ * as explained above and below.
+ *
+ * Copyright:
+ * Copyright 2008 Atheros Communications.
+ *
+ * The original header (of upnp_wps_device.c) reads:
+ *
+ *  Copyright (c) 2006-2007 Sony Corporation. All Rights Reserved.
+ *
+ *  File Name: upnp_wps_device.c
+ *  Description: EAP-WPS UPnP device source
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * 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.
+ *     * Neither the name of Sony Corporation 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.
+ *
+ * Portions from Intel libupnp files, e.g. genlib/net/http/httpreadwrite.c
+ * typical header:
+ *
+ * Copyright (c) 2000-2003 Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither name of Intel Corporation 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 INTEL 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.
+*/
+
+/*
+ * Overview of WPS over UPnP:
+ *
+ * UPnP is a protocol that allows devices to discover each other and control
+ * each other. In UPnP terminology, a device is either a "device" (a server
+ * that provides information about itself and allows itself to be controlled)
+ * or a "control point" (a client that controls "devices") or possibly both.
+ * This file implements a UPnP "device".
+ *
+ * For us, we use mostly basic UPnP discovery, but the control part of interest
+ * is WPS carried via UPnP messages. There is quite a bit of basic UPnP
+ * discovery to do before we can get to WPS, however.
+ *
+ * UPnP discovery begins with "devices" send out multicast UDP packets to a
+ * certain fixed multicast IP address and port, and "control points" sending
+ * out other such UDP packets.
+ *
+ * The packets sent by devices are NOTIFY packets (not to be confused with TCP
+ * NOTIFY packets that are used later) and those sent by control points are
+ * M-SEARCH packets. These packets contain a simple HTTP style header. The
+ * packets are sent redundantly to get around packet loss. Devices respond to
+ * M-SEARCH packets with HTTP-like UDP packets containing HTTP/1.1 200 OK
+ * messages, which give similar information as the UDP NOTIFY packets.
+ *
+ * The above UDP packets advertise the (arbitrary) TCP ports that the
+ * respective parties will listen to. The control point can then do a HTTP
+ * SUBSCRIBE (something like an HTTP PUT) after which the device can do a
+ * separate HTTP NOTIFY (also like an HTTP PUT) to do event messaging.
+ *
+ * The control point will also do HTTP GET of the "device file" listed in the
+ * original UDP information from the device (see UPNP_WPS_DEVICE_XML_FILE
+ * data), and based on this will do additional GETs... HTTP POSTs are done to
+ * cause an action.
+ *
+ * Beyond some basic information in HTTP headers, additional information is in
+ * the HTTP bodies, in a format set by the SOAP and XML standards, a markup
+ * language related to HTML used for web pages. This language is intended to
+ * provide the ultimate in self-documentation by providing a universal
+ * namespace based on pseudo-URLs called URIs. Note that although a URI looks
+ * like a URL (a web address), they are never accessed as such but are used
+ * only as identifiers.
+ *
+ * The POST of a GetDeviceInfo gets information similar to what might be
+ * obtained from a probe request or response on Wi-Fi. WPS messages M1-M8
+ * are passed via a POST of a PutMessage; the M1-M8 WPS messages are converted
+ * to a bin64 ascii representation for encapsulation. When proxying messages,
+ * WLANEvent and PutWLANResponse are used.
+ *
+ * This of course glosses over a lot of details.
+ */
+
+#include "includes.h"
+
+#include <time.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+
+#include "common.h"
+#include "uuid.h"
+#include "base64.h"
+#include "wps.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+
+/*
+ * UPnP allows a client ("control point") to send a server like us ("device")
+ * a domain name for registration, and we are supposed to resolve it. This is
+ * bad because, using the standard Linux library, we will stall the entire
+ * hostapd waiting for resolution.
+ *
+ * The "correct" solution would be to use an event driven library for domain
+ * name resolution such as "ares". However, this would increase code size
+ * further. Since it is unlikely that we'll actually see such domain names, we
+ * can just refuse to accept them.
+ */
+#define NO_DOMAIN_NAME_RESOLUTION 1  /* 1 to allow only dotted ip addresses */
+
+
+/*
+ * UPnP does not scale well. If we were in a room with thousands of control
+ * points then potentially we could be expected to handle subscriptions for
+ * each of them, which would exhaust our memory. So we must set a limit. In
+ * practice we are unlikely to see more than one or two.
+ */
+#define MAX_SUBSCRIPTIONS 4    /* how many subscribing clients we handle */
+#define MAX_ADDR_PER_SUBSCRIPTION 8
+
+/* Maximum number of Probe Request events per second */
+#define MAX_EVENTS_PER_SEC 5
+
+
+static struct upnp_wps_device_sm *shared_upnp_device = NULL;
+
+
+/* Write the current date/time per RFC */
+void format_date(struct wpabuf *buf)
+{
+	const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
+	const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
+		"Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
+	struct tm *date;
+	time_t t;
+
+	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,
+		      date->tm_hour, date->tm_min, date->tm_sec);
+}
+
+
+/***************************************************************************
+ * UUIDs (unique identifiers)
+ *
+ * These are supposed to be unique in all the world.
+ * Sometimes permanent ones are used, sometimes temporary ones
+ * based on random numbers... there are different rules for valid content
+ * of different types.
+ * Each uuid is 16 bytes long.
+ **************************************************************************/
+
+/* uuid_make -- construct a random UUID
+ * The UPnP documents don't seem to offer any guidelines as to which method to
+ * use for constructing UUIDs for subscriptions. Presumably any method from
+ * rfc4122 is good enough; I've chosen random number method.
+ */
+static int uuid_make(u8 uuid[UUID_LEN])
+{
+	if (os_get_random(uuid, UUID_LEN) < 0)
+		return -1;
+
+	/* Replace certain bits as specified in rfc4122 or X.667 */
+	uuid[6] &= 0x0f; uuid[6] |= (4 << 4);   /* version 4 == random gen */
+	uuid[8] &= 0x3f; uuid[8] |= 0x80;
+
+	return 0;
+}
+
+
+/*
+ * Subscriber address handling.
+ * Since a subscriber may have an arbitrary number of addresses, we have to
+ * add a bunch of code to handle them.
+ *
+ * Addresses are passed in text, and MAY be domain names instead of the (usual
+ * and expected) dotted IP addresses. Resolving domain names consumes a lot of
+ * resources. Worse, we are currently using the standard Linux getaddrinfo()
+ * which will block the entire program until complete or timeout! The proper
+ * solution would be to use the "ares" library or similar with more state
+ * machine steps etc. or just disable domain name resolution by setting
+ * NO_DOMAIN_NAME_RESOLUTION to 1 at top of this file.
+ */
+
+/* subscr_addr_delete -- delete single unlinked subscriber address
+ * (be sure to unlink first if need be)
+ */
+void subscr_addr_delete(struct subscr_addr *a)
+{
+	/*
+	 * Note: do NOT free domain_and_port or path because they point to
+	 * memory within the allocation of "a".
+	 */
+	os_free(a);
+}
+
+
+/* subscr_addr_free_all -- unlink and delete list of subscriber addresses. */
+static void subscr_addr_free_all(struct subscription *s)
+{
+	struct subscr_addr *a, *tmp;
+	dl_list_for_each_safe(a, tmp, &s->addr_list, struct subscr_addr, list)
+	{
+		dl_list_del(&a->list);
+		subscr_addr_delete(a);
+	}
+}
+
+
+/* subscr_addr_add_url -- add address(es) for one url to subscription */
+static void subscr_addr_add_url(struct subscription *s, const char *url,
+				size_t url_len)
+{
+	int alloc_len;
+	char *scratch_mem = NULL;
+	char *mem;
+	char *host;
+	char *delim;
+	char *path;
+	int port = 80;  /* port to send to (default is port 80) */
+	struct addrinfo hints;
+	struct addrinfo *result = NULL;
+	struct addrinfo *rp;
+	int rerr;
+	size_t host_len, path_len;
+
+	/* url MUST begin with http: */
+	if (url_len < 7 || os_strncasecmp(url, "http://", 7))
+		goto fail;
+	url += 7;
+	url_len -= 7;
+
+	/* Make a copy of the string to allow modification during parsing */
+	scratch_mem = dup_binstr(url, url_len);
+	if (scratch_mem == NULL)
+		goto fail;
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem);
+	host = scratch_mem;
+	path = os_strchr(host, '/');
+	if (path)
+		*path++ = '\0'; /* null terminate host */
+
+	/* Process and remove optional port component */
+	delim = os_strchr(host, ':');
+	if (delim) {
+		*delim = '\0'; /* null terminate host name for now */
+		if (isdigit(delim[1]))
+			port = atol(delim + 1);
+	}
+
+	/*
+	 * getaddrinfo does the right thing with dotted decimal notations, or
+	 * will resolve domain names. Resolving domain names will unfortunately
+	 * hang the entire program until it is resolved or it times out
+	 * internal to getaddrinfo; fortunately we think that the use of actual
+	 * domain names (vs. dotted decimal notations) should be uncommon.
+	 */
+	os_memset(&hints, 0, sizeof(struct addrinfo));
+	hints.ai_family = AF_INET;      /* IPv4 */
+	hints.ai_socktype = SOCK_STREAM;
+#if NO_DOMAIN_NAME_RESOLUTION
+	/* Suppress domain name resolutions that would halt
+	 * the program for periods of time
+	 */
+	hints.ai_flags = AI_NUMERICHOST;
+#else
+	/* Allow domain name resolution. */
+	hints.ai_flags = 0;
+#endif
+	hints.ai_protocol = 0;          /* Any protocol? */
+	rerr = getaddrinfo(host, NULL /* fill in port ourselves */,
+			   &hints, &result);
+	if (rerr) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s",
+			   rerr, gai_strerror(rerr), host);
+		goto fail;
+	}
+
+	if (delim)
+		*delim = ':'; /* Restore port */
+
+	host_len = os_strlen(host);
+	path_len = path ? os_strlen(path) : 0;
+	alloc_len = host_len + 1 + 1 + path_len + 1;
+
+	for (rp = result; rp; rp = rp->ai_next) {
+		struct subscr_addr *a;
+
+		/* Limit no. of address to avoid denial of service attack */
+		if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) {
+			wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: "
+				   "Ignoring excessive addresses");
+			break;
+		}
+
+		a = os_zalloc(sizeof(*a) + alloc_len);
+		if (a == NULL)
+			break;
+		mem = (char *) (a + 1);
+		a->domain_and_port = mem;
+		os_memcpy(mem, host, host_len);
+		mem += host_len + 1;
+		a->path = mem;
+		if (path == NULL || path[0] != '/')
+			*mem++ = '/';
+		if (path)
+			os_memcpy(mem, path, path_len);
+		os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
+		a->saddr.sin_port = htons(port);
+
+		dl_list_add(&s->addr_list, &a->list);
+	}
+
+fail:
+	if (result)
+		freeaddrinfo(result);
+	os_free(scratch_mem);
+}
+
+
+/* subscr_addr_list_create -- create list from urls in string.
+ *      Each url is enclosed by angle brackets.
+ */
+static void subscr_addr_list_create(struct subscription *s,
+				    const char *url_list)
+{
+	const char *end;
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Parsing URL list '%s'", url_list);
+	for (;;) {
+		while (*url_list == ' ' || *url_list == '\t')
+			url_list++;
+		if (*url_list != '<')
+			break;
+		url_list++;
+		end = os_strchr(url_list, '>');
+		if (end == NULL)
+			break;
+		subscr_addr_add_url(s, url_list, end - url_list);
+		url_list = end + 1;
+	}
+}
+
+
+static void wpabuf_put_property(struct wpabuf *buf, const char *name,
+				const char *value)
+{
+	wpabuf_put_str(buf, "<e:property>");
+	wpabuf_printf(buf, "<%s>", name);
+	if (value)
+		wpabuf_put_str(buf, value);
+	wpabuf_printf(buf, "</%s>", name);
+	wpabuf_put_str(buf, "</e:property>\n");
+}
+
+
+/**
+ * upnp_wps_device_send_event - Queue event messages for subscribers
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ *
+ * This function queues the last WLANEvent to be sent for all currently
+ * subscribed UPnP control points. sm->wlanevent must have been set with the
+ * encoded data before calling this function.
+ */
+static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
+{
+	/* Enqueue event message for all subscribers */
+	struct wpabuf *buf; /* holds event message */
+	int buf_size = 0;
+	struct subscription *s, *tmp;
+	/* Actually, utf-8 is the default, but it doesn't hurt to specify it */
+	const char *format_head =
+		"<?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_reltime now;
+
+	if (dl_list_empty(&sm->subscriptions)) {
+		/* optimize */
+		return;
+	}
+
+	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;
+		} else {
+			sm->num_events_in_sec++;
+			/*
+			 * In theory, this should apply to all WLANEvent
+			 * notifications, but EAP messages are of much higher
+			 * priority and Probe Request notifications should not
+			 * be allowed to drop EAP messages, so only throttle
+			 * Probe Request notifications.
+			 */
+			if (sm->num_events_in_sec > MAX_EVENTS_PER_SEC &&
+			    sm->wlanevent_type ==
+			    UPNP_WPS_WLANEVENT_TYPE_PROBE) {
+				wpa_printf(MSG_DEBUG, "WPS UPnP: Throttle "
+					   "event notifications (%u seen "
+					   "during one second)",
+					   sm->num_events_in_sec);
+				return;
+			}
+		}
+	}
+
+	/* Determine buffer size needed first */
+	buf_size += os_strlen(format_head);
+	buf_size += 50 + 2 * os_strlen("WLANEvent");
+	if (sm->wlanevent)
+		buf_size += os_strlen(sm->wlanevent);
+	buf_size += os_strlen(format_tail);
+
+	buf = wpabuf_alloc(buf_size);
+	if (buf == NULL)
+		return;
+	wpabuf_put_str(buf, format_head);
+	wpabuf_put_property(buf, "WLANEvent", sm->wlanevent);
+	wpabuf_put_str(buf, format_tail);
+
+	wpa_printf(MSG_MSGDUMP, "WPS UPnP: WLANEvent message:\n%s",
+		   (char *) wpabuf_head(buf));
+
+	dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
+			      list) {
+		event_add(s, buf,
+			  sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE);
+	}
+
+	wpabuf_free(buf);
+}
+
+
+/*
+ * Event subscription (subscriber machines register with us to receive event
+ * messages).
+ * This is the result of an incoming HTTP over TCP SUBSCRIBE request.
+ */
+
+/* subscription_destroy -- destroy an unlinked subscription
+ * Be sure to unlink first if necessary.
+ */
+void subscription_destroy(struct subscription *s)
+{
+	struct upnp_wps_device_interface *iface;
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s);
+	subscr_addr_free_all(s);
+	event_delete_all(s);
+	dl_list_for_each(iface, &s->sm->interfaces,
+			 struct upnp_wps_device_interface, list)
+		upnp_er_remove_notification(iface->wps->registrar, s);
+	os_free(s);
+}
+
+
+/* subscription_list_age -- remove expired subscriptions */
+static void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now)
+{
+	struct subscription *s, *tmp;
+	dl_list_for_each_safe(s, tmp, &sm->subscriptions,
+			      struct subscription, list) {
+		if (s->timeout_time > now)
+			break;
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Removing aged subscription");
+		dl_list_del(&s->list);
+		subscription_destroy(s);
+	}
+}
+
+
+/* subscription_find -- return existing subscription matching uuid, if any
+ * returns NULL if not found
+ */
+struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
+					const u8 uuid[UUID_LEN])
+{
+	struct subscription *s;
+	dl_list_for_each(s, &sm->subscriptions, struct subscription, list) {
+		if (os_memcmp(s->uuid, uuid, UUID_LEN) == 0)
+			return s; /* Found match */
+	}
+	return NULL;
+}
+
+
+static struct wpabuf * build_fake_wsc_ack(void)
+{
+	struct wpabuf *msg = wpabuf_alloc(100);
+	if (msg == NULL)
+		return NULL;
+	wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP);
+	wpabuf_put_str(msg, "00:00:00:00:00:00");
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_WSC_ACK)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+	/* Enrollee Nonce */
+	wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
+	wpabuf_put_be16(msg, WPS_NONCE_LEN);
+	wpabuf_put(msg, WPS_NONCE_LEN);
+	/* Registrar Nonce */
+	wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
+	wpabuf_put_be16(msg, WPS_NONCE_LEN);
+	wpabuf_put(msg, WPS_NONCE_LEN);
+	if (wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+	return msg;
+}
+
+
+/* subscription_first_event -- send format/queue event that is automatically
+ * sent on a new subscription.
+ */
+static int subscription_first_event(struct subscription *s)
+{
+	/*
+	 * Actually, utf-8 is the default, but it doesn't hurt to specify it.
+	 *
+	 * APStatus is apparently a bit set,
+	 * 0x1 = configuration change (but is always set?)
+	 * 0x10 = ap is locked
+	 *
+	 * Per UPnP spec, we send out the last value of each variable, even
+	 * for WLANEvent, whatever it was.
+	 */
+	char *wlan_event;
+	struct wpabuf *buf;
+	int ap_status = 1;      /* TODO: add 0x10 if access point is locked */
+	const char *head =
+		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+		"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
+	const char *tail = "</e:propertyset>\n";
+	char txt[10];
+	int ret;
+
+	if (s->sm->wlanevent == NULL) {
+		/*
+		 * There has been no events before the subscription. However,
+		 * UPnP device architecture specification requires all the
+		 * evented variables to be included, so generate a dummy event
+		 * for this particular case using a WSC_ACK and all-zeros
+		 * nonces. The ER (UPnP control point) will ignore this, but at
+		 * least it will learn that WLANEvent variable will be used in
+		 * event notifications in the future.
+		 */
+		struct wpabuf *msg;
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Use a fake WSC_ACK as the "
+			   "initial WLANEvent");
+		msg = build_fake_wsc_ack();
+		if (msg) {
+			s->sm->wlanevent = (char *)
+				base64_encode(wpabuf_head(msg),
+					      wpabuf_len(msg), NULL);
+			wpabuf_free(msg);
+		}
+	}
+
+	wlan_event = s->sm->wlanevent;
+	if (wlan_event == NULL || *wlan_event == '\0') {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: WLANEvent not known for "
+			   "initial event message");
+		wlan_event = "";
+	}
+	buf = wpabuf_alloc(500 + os_strlen(wlan_event));
+	if (buf == NULL)
+		return -1;
+
+	wpabuf_put_str(buf, head);
+	wpabuf_put_property(buf, "STAStatus", "1");
+	os_snprintf(txt, sizeof(txt), "%d", ap_status);
+	wpabuf_put_property(buf, "APStatus", txt);
+	if (*wlan_event)
+		wpabuf_put_property(buf, "WLANEvent", wlan_event);
+	wpabuf_put_str(buf, tail);
+
+	ret = event_add(s, buf, 0);
+	if (ret) {
+		wpabuf_free(buf);
+		return ret;
+	}
+	wpabuf_free(buf);
+
+	return 0;
+}
+
+
+/**
+ * subscription_start - Remember a UPnP control point to send events to.
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @callback_urls: Callback URLs
+ * Returns: %NULL on error, or pointer to new subscription structure.
+ */
+struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
+					 const char *callback_urls)
+{
+	struct subscription *s;
+	time_t now = time(NULL);
+	time_t expire = now + UPNP_SUBSCRIBE_SEC;
+	char str[80];
+
+	/* Get rid of expired subscriptions so we have room */
+	subscription_list_age(sm, now);
+
+	/* If too many subscriptions, remove oldest */
+	if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) {
+		s = dl_list_first(&sm->subscriptions, struct subscription,
+				  list);
+		if (s) {
+			wpa_printf(MSG_INFO,
+				   "WPS UPnP: Too many subscriptions, trashing oldest");
+			dl_list_del(&s->list);
+			subscription_destroy(s);
+		}
+	}
+
+	s = os_zalloc(sizeof(*s));
+	if (s == NULL)
+		return NULL;
+	dl_list_init(&s->addr_list);
+	dl_list_init(&s->event_queue);
+
+	s->sm = sm;
+	s->timeout_time = expire;
+	if (uuid_make(s->uuid) < 0) {
+		subscription_destroy(s);
+		return NULL;
+	}
+	subscr_addr_list_create(s, callback_urls);
+	if (dl_list_empty(&s->addr_list)) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in "
+			   "'%s' - drop subscription", callback_urls);
+		subscription_destroy(s);
+		return NULL;
+	}
+
+	/* Add to end of list, since it has the highest expiration time */
+	dl_list_add_tail(&sm->subscriptions, &s->list);
+	/* Queue up immediate event message (our last event)
+	 * as required by UPnP spec.
+	 */
+	if (subscription_first_event(s)) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Dropping subscriber due to "
+			   "event backlog");
+		dl_list_del(&s->list);
+		subscription_destroy(s);
+		return NULL;
+	}
+	uuid_bin2str(s->uuid, str, sizeof(str));
+	wpa_printf(MSG_DEBUG,
+		   "WPS UPnP: Subscription %p (SID %s) started with %s",
+		   s, str, callback_urls);
+	/* Schedule sending this */
+	event_send_all_later(sm);
+	return s;
+}
+
+
+/* subscription_renew -- find subscription and reset timeout */
+struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
+					 const u8 uuid[UUID_LEN])
+{
+	time_t now = time(NULL);
+	time_t expire = now + UPNP_SUBSCRIBE_SEC;
+	struct subscription *s = subscription_find(sm, uuid);
+	if (s == NULL)
+		return NULL;
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewed");
+	dl_list_del(&s->list);
+	s->timeout_time = expire;
+	/* add back to end of list, since it now has highest expiry */
+	dl_list_add_tail(&sm->subscriptions, &s->list);
+	return s;
+}
+
+
+/**
+ * upnp_wps_device_send_wlan_event - Event notification
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @from_mac_addr: Source (Enrollee) MAC address for the event
+ * @ev_type: Event type
+ * @msg: Event data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Tell external Registrars (UPnP control points) that something happened. In
+ * particular, events include WPS messages from clients that are proxied to
+ * external Registrars.
+ */
+int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
+				    const u8 from_mac_addr[ETH_ALEN],
+				    enum upnp_wps_wlanevent_type ev_type,
+				    const struct wpabuf *msg)
+{
+	int ret = -1;
+	char type[2];
+	const u8 *mac = from_mac_addr;
+	char mac_text[18];
+	u8 *raw = NULL;
+	size_t raw_len;
+	char *val;
+	size_t val_len;
+	int pos = 0;
+
+	if (!sm)
+		goto fail;
+
+	os_snprintf(type, sizeof(type), "%1u", ev_type);
+
+	raw_len = 1 + 17 + (msg ? wpabuf_len(msg) : 0);
+	raw = os_zalloc(raw_len);
+	if (!raw)
+		goto fail;
+
+	*(raw + pos) = (u8) ev_type;
+	pos += 1;
+	os_snprintf(mac_text, sizeof(mac_text), MACSTR, MAC2STR(mac));
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Proxying WLANEvent from %s",
+		   mac_text);
+	os_memcpy(raw + pos, mac_text, 17);
+	pos += 17;
+	if (msg) {
+		os_memcpy(raw + pos, wpabuf_head(msg), wpabuf_len(msg));
+		pos += wpabuf_len(msg);
+	}
+	raw_len = pos;
+
+	val = (char *) base64_encode(raw, raw_len, &val_len);
+	if (val == NULL)
+		goto fail;
+
+	os_free(sm->wlanevent);
+	sm->wlanevent = val;
+	sm->wlanevent_type = ev_type;
+	upnp_wps_device_send_event(sm);
+
+	ret = 0;
+
+fail:
+	os_free(raw);
+
+	return ret;
+}
+
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <sys/sysctl.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+static int eth_get(const char *device, u8 ea[ETH_ALEN])
+{
+	struct if_msghdr *ifm;
+	struct sockaddr_dl *sdl;
+	u_char *p, *buf;
+	size_t len;
+	int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
+
+	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+		return -1;
+	if ((buf = os_malloc(len)) == NULL)
+		return -1;
+	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+		os_free(buf);
+		return -1;
+	}
+	for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
+		ifm = (struct if_msghdr *)p;
+		sdl = (struct sockaddr_dl *)(ifm + 1);
+		if (ifm->ifm_type != RTM_IFINFO ||
+		    (ifm->ifm_addrs & RTA_IFP) == 0)
+			continue;
+		if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
+		    os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
+			continue;
+		os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
+		break;
+	}
+	os_free(buf);
+
+	if (p >= buf + len) {
+		errno = ESRCH;
+		return -1;
+	}
+	return 0;
+}
+#endif /* __FreeBSD__ */
+
+
+/**
+ * get_netif_info - Get hw and IP addresses for network device
+ * @net_if: Selected network interface name
+ * @ip_addr: Buffer for returning IP address in network byte order
+ * @ip_addr_text: Buffer for returning a pointer to allocated IP address text
+ * @mac: Buffer for returning MAC address
+ * Returns: 0 on success, -1 on failure
+ */
+int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
+		   u8 mac[ETH_ALEN])
+{
+	struct ifreq req;
+	int sock = -1;
+	struct sockaddr_in *addr;
+	struct in_addr in_addr;
+
+	*ip_addr_text = os_zalloc(16);
+	if (*ip_addr_text == NULL)
+		goto fail;
+
+	sock = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock < 0)
+		goto fail;
+
+	os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
+	if (ioctl(sock, SIOCGIFADDR, &req) < 0) {
+		wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFADDR failed: %d (%s)",
+			   errno, strerror(errno));
+		goto fail;
+	}
+	addr = (void *) &req.ifr_addr;
+	*ip_addr = addr->sin_addr.s_addr;
+	in_addr.s_addr = *ip_addr;
+	os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr));
+
+#ifdef __linux__
+	os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
+	if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) {
+		wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFHWADDR failed: "
+			   "%d (%s)", errno, strerror(errno));
+		goto fail;
+	}
+	os_memcpy(mac, req.ifr_addr.sa_data, 6);
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+	if (eth_get(net_if, mac) < 0) {
+		wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address");
+		goto fail;
+	}
+#else
+#error MAC address fetch not implemented
+#endif
+
+	close(sock);
+	return 0;
+
+fail:
+	if (sock >= 0)
+		close(sock);
+	os_free(*ip_addr_text);
+	*ip_addr_text = NULL;
+	return -1;
+}
+
+
+static void upnp_wps_free_msearchreply(struct dl_list *head)
+{
+	struct advertisement_state_machine *a, *tmp;
+	dl_list_for_each_safe(a, tmp, head, struct advertisement_state_machine,
+			      list)
+		msearchreply_state_machine_stop(a);
+}
+
+
+static void upnp_wps_free_subscriptions(struct dl_list *head,
+					struct wps_registrar *reg)
+{
+	struct subscription *s, *tmp;
+	dl_list_for_each_safe(s, tmp, head, struct subscription, list) {
+		if (reg && s->reg != reg)
+			continue;
+		dl_list_del(&s->list);
+		subscription_destroy(s);
+	}
+}
+
+
+/**
+ * upnp_wps_device_stop - Stop WPS UPnP operations on an interface
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ */
+static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
+{
+	if (!sm || !sm->started)
+		return;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
+	web_listener_stop(sm);
+	ssdp_listener_stop(sm);
+	upnp_wps_free_msearchreply(&sm->msearch_replies);
+	upnp_wps_free_subscriptions(&sm->subscriptions, NULL);
+
+	advertisement_state_machine_stop(sm, 1);
+
+	event_send_stop_all(sm);
+	os_free(sm->wlanevent);
+	sm->wlanevent = NULL;
+	os_free(sm->ip_addr_text);
+	sm->ip_addr_text = NULL;
+	if (sm->multicast_sd >= 0)
+		close(sm->multicast_sd);
+	sm->multicast_sd = -1;
+
+	sm->started = 0;
+}
+
+
+/**
+ * upnp_wps_device_start - Start WPS UPnP operations on an interface
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @net_if: Selected network interface name
+ * Returns: 0 on success, -1 on failure
+ */
+static int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
+{
+	if (!sm || !net_if)
+		return -1;
+
+	if (sm->started)
+		upnp_wps_device_stop(sm);
+
+	sm->multicast_sd = -1;
+	sm->ssdp_sd = -1;
+	sm->started = 1;
+	sm->advertise_count = 0;
+
+	/* Fix up linux multicast handling */
+	if (add_ssdp_network(net_if))
+		goto fail;
+
+	/* Determine which IP and mac address we're using */
+	if (get_netif_info(net_if, &sm->ip_addr, &sm->ip_addr_text,
+			   sm->mac_addr)) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
+			   "for %s. Does it have IP address?", net_if);
+		goto fail;
+	}
+
+	/* Listen for incoming TCP connections so that others
+	 * can fetch our "xml files" from us.
+	 */
+	if (web_listener_start(sm))
+		goto fail;
+
+	/* Set up for receiving discovery (UDP) packets */
+	if (ssdp_listener_start(sm))
+		goto fail;
+
+	/* Set up for sending multicast */
+	if (ssdp_open_multicast(sm) < 0)
+		goto fail;
+
+	/*
+	 * Broadcast NOTIFY messages to let the world know we exist.
+	 * This is done via a state machine since the messages should not be
+	 * all sent out at once.
+	 */
+	if (advertisement_state_machine_start(sm))
+		goto fail;
+
+	return 0;
+
+fail:
+	upnp_wps_device_stop(sm);
+	return -1;
+}
+
+
+static struct upnp_wps_device_interface *
+upnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv)
+{
+	struct upnp_wps_device_interface *iface;
+	dl_list_for_each(iface, &sm->interfaces,
+			 struct upnp_wps_device_interface, list) {
+		if (iface->priv == priv)
+			return iface;
+	}
+	return NULL;
+}
+
+
+/**
+ * upnp_wps_device_deinit - Deinitialize WPS UPnP
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @priv: External context data that was used in upnp_wps_device_init() call
+ */
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv)
+{
+	struct upnp_wps_device_interface *iface;
+
+	if (!sm)
+		return;
+
+	iface = upnp_wps_get_iface(sm, priv);
+	if (iface == NULL) {
+		wpa_printf(MSG_ERROR, "WPS UPnP: Could not find the interface "
+			   "instance to deinit");
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Deinit interface instance %p", iface);
+	if (dl_list_len(&sm->interfaces) == 1) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Deinitializing last instance "
+			   "- free global device instance");
+		upnp_wps_device_stop(sm);
+	} else
+		upnp_wps_free_subscriptions(&sm->subscriptions,
+					    iface->wps->registrar);
+	dl_list_del(&iface->list);
+
+	if (iface->peer.wps)
+		wps_deinit(iface->peer.wps);
+	os_free(iface->ctx->ap_pin);
+	os_free(iface->ctx);
+	os_free(iface);
+
+	if (dl_list_empty(&sm->interfaces)) {
+		os_free(sm->root_dir);
+		os_free(sm->desc_url);
+		os_free(sm);
+		shared_upnp_device = NULL;
+	}
+}
+
+
+/**
+ * upnp_wps_device_init - Initialize WPS UPnP
+ * @ctx: callback table; we must eventually free it
+ * @wps: Pointer to longterm WPS context
+ * @priv: External context data that will be used in callbacks
+ * @net_if: Selected network interface name
+ * Returns: WPS UPnP state or %NULL on failure
+ */
+struct upnp_wps_device_sm *
+upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
+		     void *priv, char *net_if)
+{
+	struct upnp_wps_device_sm *sm;
+	struct upnp_wps_device_interface *iface;
+	int start = 0;
+
+	iface = os_zalloc(sizeof(*iface));
+	if (iface == NULL) {
+		os_free(ctx->ap_pin);
+		os_free(ctx);
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface);
+
+	iface->ctx = ctx;
+	iface->wps = wps;
+	iface->priv = priv;
+
+	if (shared_upnp_device) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Share existing device "
+			   "context");
+		sm = shared_upnp_device;
+	} else {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Initialize device context");
+		sm = os_zalloc(sizeof(*sm));
+		if (!sm) {
+			wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init "
+				   "failed");
+			os_free(iface);
+			os_free(ctx->ap_pin);
+			os_free(ctx);
+			return NULL;
+		}
+		shared_upnp_device = sm;
+
+		dl_list_init(&sm->msearch_replies);
+		dl_list_init(&sm->subscriptions);
+		dl_list_init(&sm->interfaces);
+		start = 1;
+	}
+
+	dl_list_add(&sm->interfaces, &iface->list);
+
+	if (start && upnp_wps_device_start(sm, net_if)) {
+		upnp_wps_device_deinit(sm, priv);
+		return NULL;
+	}
+
+
+	return sm;
+}
+
+
+/**
+ * upnp_wps_subscribers - Check whether there are any event subscribers
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 if no subscribers, 1 if subscribers
+ */
+int upnp_wps_subscribers(struct upnp_wps_device_sm *sm)
+{
+	return !dl_list_empty(&sm->subscriptions);
+}
+
+
+int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin)
+{
+	struct upnp_wps_device_interface *iface;
+	if (sm == NULL)
+		return 0;
+
+	dl_list_for_each(iface, &sm->interfaces,
+			 struct upnp_wps_device_interface, list) {
+		os_free(iface->ctx->ap_pin);
+		if (ap_pin) {
+			iface->ctx->ap_pin = os_strdup(ap_pin);
+			if (iface->ctx->ap_pin == NULL)
+				return -1;
+		} else
+			iface->ctx->ap_pin = NULL;
+	}
+
+	return 0;
+}
diff --git a/hostap/src/wps/wps_upnp.h b/hostap/src/wps/wps_upnp.h
new file mode 100644
index 0000000..87b7ab1
--- /dev/null
+++ b/hostap/src/wps/wps_upnp.h
@@ -0,0 +1,48 @@
+/*
+ * UPnP WPS Device
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef WPS_UPNP_H
+#define WPS_UPNP_H
+
+struct upnp_wps_device_sm;
+struct wps_context;
+struct wps_data;
+
+struct upnp_wps_peer {
+	struct wps_data *wps;
+};
+
+enum upnp_wps_wlanevent_type {
+	UPNP_WPS_WLANEVENT_TYPE_PROBE = 1,
+	UPNP_WPS_WLANEVENT_TYPE_EAP = 2
+};
+
+struct upnp_wps_device_ctx {
+	int (*rx_req_put_wlan_response)(
+		void *priv, enum upnp_wps_wlanevent_type ev_type,
+		const u8 *mac_addr, const struct wpabuf *msg,
+		enum wps_msg_type msg_type);
+
+	char *ap_pin;
+};
+
+struct upnp_wps_device_sm *
+upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
+		     void *priv, char *net_if);
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv);
+
+int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
+				    const u8 from_mac_addr[ETH_ALEN],
+				    enum upnp_wps_wlanevent_type ev_type,
+				    const struct wpabuf *msg);
+int upnp_wps_subscribers(struct upnp_wps_device_sm *sm);
+int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin);
+
+#endif /* WPS_UPNP_H */
diff --git a/hostap/src/wps/wps_upnp_ap.c b/hostap/src/wps/wps_upnp_ap.c
new file mode 100644
index 0000000..cca3905
--- /dev/null
+++ b/hostap/src/wps/wps_upnp_ap.c
@@ -0,0 +1,83 @@
+/*
+ * Wi-Fi Protected Setup - UPnP AP functionality
+ * 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 "eloop.h"
+#include "uuid.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+
+static void upnp_er_set_selected_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct subscription *s = eloop_ctx;
+	struct wps_registrar *reg = timeout_ctx;
+	wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar from ER timed out");
+	s->selected_registrar = 0;
+	wps_registrar_selected_registrar_changed(reg, 0);
+}
+
+
+int upnp_er_set_selected_registrar(struct wps_registrar *reg,
+				   struct subscription *s,
+				   const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes",
+			msg);
+	if (wps_validate_upnp_set_selected_registrar(msg) < 0 ||
+	    wps_parse_msg(msg, &attr) < 0)
+		return -1;
+
+	s->reg = reg;
+	eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg);
+
+	os_memset(s->authorized_macs, 0, sizeof(s->authorized_macs));
+	if (attr.selected_registrar == NULL || *attr.selected_registrar == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable "
+			   "Selected Registrar");
+		s->selected_registrar = 0;
+	} else {
+		s->selected_registrar = 1;
+		s->dev_password_id = attr.dev_password_id ?
+			WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT;
+		s->config_methods = attr.sel_reg_config_methods ?
+			WPA_GET_BE16(attr.sel_reg_config_methods) : -1;
+		if (attr.authorized_macs) {
+			int count = attr.authorized_macs_len / ETH_ALEN;
+			if (count > WPS_MAX_AUTHORIZED_MACS)
+				count = WPS_MAX_AUTHORIZED_MACS;
+			os_memcpy(s->authorized_macs, attr.authorized_macs,
+				  count * ETH_ALEN);
+		} else if (!attr.version2) {
+			wpa_printf(MSG_DEBUG, "WPS: Add broadcast "
+				   "AuthorizedMACs for WPS 1.0 ER");
+			os_memset(s->authorized_macs, 0xff, ETH_ALEN);
+		}
+		eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+				       upnp_er_set_selected_timeout, s, reg);
+	}
+
+	wps_registrar_selected_registrar_changed(reg, 0);
+
+	return 0;
+}
+
+
+void upnp_er_remove_notification(struct wps_registrar *reg,
+				 struct subscription *s)
+{
+	s->selected_registrar = 0;
+	eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg);
+	if (reg)
+		wps_registrar_selected_registrar_changed(reg, 0);
+}
diff --git a/hostap/src/wps/wps_upnp_event.c b/hostap/src/wps/wps_upnp_event.c
new file mode 100644
index 0000000..94aae75
--- /dev/null
+++ b/hostap/src/wps/wps_upnp_event.c
@@ -0,0 +1,421 @@
+/*
+ * UPnP WPS Device - Event processing
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+#include <assert.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "uuid.h"
+#include "http_client.h"
+#include "wps_defs.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+/*
+ * Event message generation (to subscribers)
+ *
+ * We make a separate copy for each message for each subscriber. This memory
+ * wasted could be limited (adding code complexity) by sharing copies, keeping
+ * a usage count and freeing when zero.
+ *
+ * Sending a message requires using a HTTP over TCP NOTIFY
+ * (like a PUT) which requires a number of states..
+ */
+
+#define MAX_EVENTS_QUEUED 20   /* How far behind queued events */
+#define MAX_FAILURES 10 /* Drop subscription after this many failures */
+
+/* How long to wait before sending event */
+#define EVENT_DELAY_SECONDS 0
+#define EVENT_DELAY_MSEC 0
+
+/*
+ * Event information that we send to each subscriber is remembered in this
+ * struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP
+ * over TCP transaction which requires various states.. It may also need to be
+ * retried at a different address (if more than one is available).
+ *
+ * TODO: As an optimization we could share data between subscribers.
+ */
+struct wps_event_ {
+	struct dl_list list;
+	struct subscription *s;         /* parent */
+	unsigned subscriber_sequence;   /* which event for this subscription*/
+	unsigned int retry;             /* which retry */
+	struct subscr_addr *addr;       /* address to connect to */
+	struct wpabuf *data;            /* event data to send */
+	struct http_client *http_event;
+};
+
+
+/* event_clean -- clean sockets etc. of event
+ * Leaves data, retry count etc. alone.
+ */
+static void event_clean(struct wps_event_ *e)
+{
+	if (e->s->current_event == e)
+		e->s->current_event = NULL;
+	http_client_free(e->http_event);
+	e->http_event = NULL;
+}
+
+
+/* event_delete -- delete single unqueued event
+ * (be sure to dequeue first if need be)
+ */
+static void event_delete(struct wps_event_ *e)
+{
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e);
+	event_clean(e);
+	wpabuf_free(e->data);
+	os_free(e);
+}
+
+
+/* event_dequeue -- get next event from the queue
+ * Returns NULL if empty.
+ */
+static struct wps_event_ *event_dequeue(struct subscription *s)
+{
+	struct wps_event_ *e;
+	e = dl_list_first(&s->event_queue, struct wps_event_, list);
+	if (e) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for "
+			   "subscription %p", e, s);
+		dl_list_del(&e->list);
+	}
+	return e;
+}
+
+
+/* event_delete_all -- delete entire event queue and current event */
+void event_delete_all(struct subscription *s)
+{
+	struct wps_event_ *e;
+	while ((e = event_dequeue(s)) != NULL)
+		event_delete(e);
+	if (s->current_event) {
+		event_delete(s->current_event);
+		/* will set: s->current_event = NULL;  */
+	}
+}
+
+
+/**
+ * event_retry - Called when we had a failure delivering event msg
+ * @e: Event
+ * @do_next_address: skip address e.g. on connect fail
+ */
+static void event_retry(struct wps_event_ *e, int do_next_address)
+{
+	struct subscription *s = e->s;
+	struct upnp_wps_device_sm *sm = s->sm;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p",
+		   e, s);
+	event_clean(e);
+	/* will set: s->current_event = NULL; */
+
+	if (do_next_address) {
+		e->retry++;
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry);
+	}
+	if (e->retry >= dl_list_len(&s->addr_list)) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
+			   "for %s", e->addr->domain_and_port);
+		event_delete(e);
+		s->last_event_failed = 1;
+		if (!dl_list_empty(&s->event_queue))
+			event_send_all_later(s->sm);
+		return;
+	}
+	dl_list_add(&s->event_queue, &e->list);
+	event_send_all_later(sm);
+}
+
+
+static struct wpabuf * event_build_message(struct wps_event_ *e)
+{
+	struct wpabuf *buf;
+	char *b;
+
+	buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
+	if (buf == NULL)
+		return NULL;
+	wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
+	wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
+	wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
+	wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
+		       "NT: upnp:event\r\n"
+		       "NTS: upnp:propchange\r\n");
+	wpabuf_put_str(buf, "SID: uuid:");
+	b = wpabuf_put(buf, 0);
+	uuid_bin2str(e->s->uuid, b, 80);
+	wpabuf_put(buf, os_strlen(b));
+	wpabuf_put_str(buf, "\r\n");
+	wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
+	wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
+		      (int) wpabuf_len(e->data));
+	wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
+	wpabuf_put_buf(buf, e->data);
+	return buf;
+}
+
+
+static void event_addr_failure(struct wps_event_ *e)
+{
+	struct subscription *s = e->s;
+
+	e->addr->num_failures++;
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s "
+		   "(num_failures=%u)",
+		   e, e->addr->domain_and_port, e->addr->num_failures);
+
+	if (e->addr->num_failures < MAX_FAILURES) {
+		/* Try other addresses, if available */
+		event_retry(e, 1);
+		return;
+	}
+
+	/*
+	 * If other side doesn't like what we say, forget about them.
+	 * (There is no way to tell other side that we are dropping them...).
+	 */
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p "
+		   "address %s due to errors", s, e->addr->domain_and_port);
+	dl_list_del(&e->addr->list);
+	subscr_addr_delete(e->addr);
+	e->addr = NULL;
+
+	if (dl_list_empty(&s->addr_list)) {
+		/* if we've given up on all addresses */
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p "
+			   "with no addresses", s);
+		dl_list_del(&s->list);
+		subscription_destroy(s);
+		return;
+	}
+
+	/* Try other addresses, if available */
+	event_retry(e, 0);
+}
+
+
+static void event_http_cb(void *ctx, struct http_client *c,
+			  enum http_client_event event)
+{
+	struct wps_event_ *e = ctx;
+	struct subscription *s = e->s;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p "
+		   "event=%d", e, c, event);
+	switch (event) {
+	case HTTP_CLIENT_OK:
+		wpa_printf(MSG_DEBUG,
+			   "WPS UPnP: Got event %p reply OK from %s",
+			   e, e->addr->domain_and_port);
+		e->addr->num_failures = 0;
+		s->last_event_failed = 0;
+		event_delete(e);
+
+		/* Schedule sending more if there is more to send */
+		if (!dl_list_empty(&s->event_queue))
+			event_send_all_later(s->sm);
+		break;
+	case HTTP_CLIENT_FAILED:
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
+		event_addr_failure(e);
+		break;
+	case HTTP_CLIENT_INVALID_REPLY:
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply");
+		event_addr_failure(e);
+		break;
+	case HTTP_CLIENT_TIMEOUT:
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
+		event_addr_failure(e);
+		break;
+	}
+}
+
+
+/* event_send_start -- prepare to send a event message to subscriber
+ *
+ * This gets complicated because:
+ * -- The message is sent via TCP and we have to keep the stream open
+ *      for 30 seconds to get a response... then close it.
+ * -- But we might have other event happen in the meantime...
+ *      we have to queue them, if we lose them then the subscriber will
+ *      be forced to unsubscribe and subscribe again.
+ * -- If multiple URLs are provided then we are supposed to try successive
+ *      ones after 30 second timeout.
+ * -- The URLs might use domain names instead of dotted decimal addresses,
+ *      and resolution of those may cause unwanted sleeping.
+ * -- Doing the initial TCP connect can take a while, so we have to come
+ *      back after connection and then send the data.
+ *
+ * Returns nonzero on error;
+ *
+ * Prerequisite: No current event send (s->current_event == NULL)
+ *      and non-empty queue.
+ */
+static int event_send_start(struct subscription *s)
+{
+	struct wps_event_ *e;
+	unsigned int itry;
+	struct wpabuf *buf;
+
+	/*
+	 * Assume we are called ONLY with no current event and ONLY with
+	 * nonempty event queue and ONLY with at least one address to send to.
+	 */
+	if (dl_list_empty(&s->addr_list) ||
+	    s->current_event ||
+	    dl_list_empty(&s->event_queue))
+		return -1;
+
+	s->current_event = e = event_dequeue(s);
+
+	/* Use address according to number of retries */
+	itry = 0;
+	dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list)
+		if (itry++ == e->retry)
+			break;
+	if (itry < e->retry)
+		return -1;
+
+	buf = event_build_message(e);
+	if (buf == NULL) {
+		event_retry(e, 0);
+		return -1;
+	}
+
+	e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
+					 event_http_cb, e);
+	if (e->http_event == NULL) {
+		wpabuf_free(buf);
+		event_retry(e, 0);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/* event_send_all_later_handler -- actually send events as needed */
+static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
+{
+	struct upnp_wps_device_sm *sm = user_ctx;
+	struct subscription *s, *tmp;
+	int nerrors = 0;
+
+	sm->event_send_all_queued = 0;
+	dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
+			      list) {
+		if (s->current_event == NULL /* not busy */ &&
+		    !dl_list_empty(&s->event_queue) /* more to do */) {
+			if (event_send_start(s))
+				nerrors++;
+		}
+	}
+
+	if (nerrors) {
+		/* Try again later */
+		event_send_all_later(sm);
+	}
+}
+
+
+/* event_send_all_later -- schedule sending events to all subscribers
+ * that need it.
+ * This avoids two problems:
+ * -- After getting a subscription, we should not send the first event
+ *      until after our reply is fully queued to be sent back,
+ * -- Possible stack depth or infinite recursion issues.
+ */
+void event_send_all_later(struct upnp_wps_device_sm *sm)
+{
+	/*
+	 * The exact time in the future isn't too important. Waiting a bit
+	 * might let us do several together.
+	 */
+	if (sm->event_send_all_queued)
+		return;
+	sm->event_send_all_queued = 1;
+	eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
+			       event_send_all_later_handler, NULL, sm);
+}
+
+
+/* event_send_stop_all -- cleanup */
+void event_send_stop_all(struct upnp_wps_device_sm *sm)
+{
+	if (sm->event_send_all_queued)
+		eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
+	sm->event_send_all_queued = 0;
+}
+
+
+/**
+ * event_add - Add a new event to a queue
+ * @s: Subscription
+ * @data: Event data (is copied; caller retains ownership)
+ * @probereq: Whether this is a Probe Request event
+ * Returns: 0 on success, -1 on error, 1 on max event queue limit reached
+ */
+int event_add(struct subscription *s, const struct wpabuf *data, int probereq)
+{
+	struct wps_event_ *e;
+	unsigned int len;
+
+	len = dl_list_len(&s->event_queue);
+	if (len >= MAX_EVENTS_QUEUED) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
+			   "subscriber %p", s);
+		if (probereq)
+			return 1;
+
+		/* Drop oldest entry to allow EAP event to be stored. */
+		e = event_dequeue(s);
+		if (!e)
+			return 1;
+		event_delete(e);
+	}
+
+	if (s->last_event_failed && probereq && len > 0) {
+		/*
+		 * Avoid queuing frames for subscribers that may have left
+		 * without unsubscribing.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe "
+			   "Request frames for subscription %p since last "
+			   "delivery failed", s);
+		return -1;
+	}
+
+	e = os_zalloc(sizeof(*e));
+	if (e == NULL)
+		return -1;
+	dl_list_init(&e->list);
+	e->s = s;
+	e->data = wpabuf_dup(data);
+	if (e->data == NULL) {
+		os_free(e);
+		return -1;
+	}
+	e->subscriber_sequence = s->next_subscriber_sequence++;
+	if (s->next_subscriber_sequence == 0)
+		s->next_subscriber_sequence++;
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
+		   "(queue len %u)", e, s, len + 1);
+	dl_list_add_tail(&s->event_queue, &e->list);
+	event_send_all_later(s->sm);
+	return 0;
+}
diff --git a/hostap/src/wps/wps_upnp_i.h b/hostap/src/wps/wps_upnp_i.h
new file mode 100644
index 0000000..f289fe6
--- /dev/null
+++ b/hostap/src/wps/wps_upnp_i.h
@@ -0,0 +1,193 @@
+/*
+ * UPnP for WPS / internal definitions
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef WPS_UPNP_I_H
+#define WPS_UPNP_I_H
+
+#include "utils/list.h"
+#include "http.h"
+
+#define UPNP_MULTICAST_ADDRESS  "239.255.255.250" /* for UPnP multicasting */
+#define UPNP_MULTICAST_PORT 1900 /* UDP port to monitor for UPnP */
+
+/* min subscribe time per UPnP standard */
+#define UPNP_SUBSCRIBE_SEC_MIN 1800
+/* subscribe time we use */
+#define UPNP_SUBSCRIBE_SEC (UPNP_SUBSCRIBE_SEC_MIN + 1)
+
+/* "filenames" used in URLs that we service via our "web server": */
+#define UPNP_WPS_DEVICE_XML_FILE "wps_device.xml"
+#define UPNP_WPS_SCPD_XML_FILE   "wps_scpd.xml"
+#define UPNP_WPS_DEVICE_CONTROL_FILE "wps_control"
+#define UPNP_WPS_DEVICE_EVENT_FILE "wps_event"
+
+#define MULTICAST_MAX_READ 1600 /* max bytes we'll read for UPD request */
+
+
+struct upnp_wps_device_sm;
+struct wps_registrar;
+
+
+enum advertisement_type_enum {
+	ADVERTISE_UP = 0,
+	ADVERTISE_DOWN = 1,
+	MSEARCH_REPLY = 2
+};
+
+/*
+ * Advertisements are broadcast via UDP NOTIFYs, and are also the essence of
+ * the reply to UDP M-SEARCH requests. This struct handles both cases.
+ *
+ * A state machine is needed because a number of variant forms must be sent in
+ * separate packets and spread out in time to avoid congestion.
+ */
+struct advertisement_state_machine {
+	struct dl_list list;
+	enum advertisement_type_enum type;
+	int state;
+	int nerrors;
+	struct sockaddr_in client; /* for M-SEARCH replies */
+};
+
+
+/*
+ * An address of a subscriber (who may have multiple addresses). We are
+ * supposed to send (via TCP) updates to each subscriber, trying each address
+ * for a subscriber until we find one that seems to work.
+ */
+struct subscr_addr {
+	struct dl_list list;
+	char *domain_and_port; /* domain and port part of url */
+	char *path; /* "filepath" part of url (from "mem") */
+	struct sockaddr_in saddr; /* address for doing connect */
+	unsigned num_failures;
+};
+
+
+/*
+ * Subscribers to our events are recorded in this struct. This includes a max
+ * of one outgoing connection (sending an "event message") per subscriber. We
+ * also have to age out subscribers unless they renew.
+ */
+struct subscription {
+	struct dl_list list;
+	struct upnp_wps_device_sm *sm; /* parent */
+	time_t timeout_time; /* when to age out the subscription */
+	unsigned next_subscriber_sequence; /* number our messages */
+	/*
+	 * This uuid identifies the subscription and is randomly generated by
+	 * us and given to the subscriber when the subscription is accepted;
+	 * and is then included with each event sent to the subscriber.
+	 */
+	u8 uuid[UUID_LEN];
+	/* Linked list of address alternatives (rotate through on failure) */
+	struct dl_list addr_list;
+	struct dl_list event_queue; /* Queued event messages. */
+	struct wps_event_ *current_event; /* non-NULL if being sent (not in q)
+					   */
+	int last_event_failed; /* Whether delivery of last event failed */
+
+	/* Information from SetSelectedRegistrar action */
+	u8 selected_registrar;
+	u16 dev_password_id;
+	u16 config_methods;
+	u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+	struct wps_registrar *reg;
+};
+
+
+struct upnp_wps_device_interface {
+	struct dl_list list;
+	struct upnp_wps_device_ctx *ctx; /* callback table */
+	struct wps_context *wps;
+	void *priv;
+
+	/* FIX: maintain separate structures for each UPnP peer */
+	struct upnp_wps_peer peer;
+};
+
+/*
+ * Our instance data corresponding to the AP device. Note that there may be
+ * multiple wireless interfaces sharing the same UPnP device instance. Each
+ * such interface is stored in the list of struct upnp_wps_device_interface
+ * instances.
+ *
+ * This is known as an opaque struct declaration to users of the WPS UPnP code.
+ */
+struct upnp_wps_device_sm {
+	struct dl_list interfaces; /* struct upnp_wps_device_interface */
+	char *root_dir;
+	char *desc_url;
+	int started; /* nonzero if we are active */
+	u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
+	char *ip_addr_text; /* IP address of network i.f. we use */
+	unsigned ip_addr; /* IP address of network i.f. we use (host order) */
+	int multicast_sd; /* send multicast messages over this socket */
+	int ssdp_sd; /* receive discovery UPD packets on socket */
+	int ssdp_sd_registered; /* nonzero if we must unregister */
+	unsigned advertise_count; /* how many advertisements done */
+	struct advertisement_state_machine advertisement;
+	struct dl_list msearch_replies;
+	int web_port; /* our port that others get xml files from */
+	struct http_server *web_srv;
+	/* Note: subscriptions are kept in expiry order */
+	struct dl_list subscriptions;
+	int event_send_all_queued; /* if we are scheduled to send events soon
+				    */
+
+	char *wlanevent; /* the last WLANEvent data */
+	enum upnp_wps_wlanevent_type wlanevent_type;
+	os_time_t last_event_sec;
+	unsigned int num_events_in_sec;
+};
+
+/* wps_upnp.c */
+void format_date(struct wpabuf *buf);
+struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
+					 const char *callback_urls);
+struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
+					 const u8 uuid[UUID_LEN]);
+void subscription_destroy(struct subscription *s);
+struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
+					const u8 uuid[UUID_LEN]);
+void subscr_addr_delete(struct subscr_addr *a);
+int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
+		   u8 mac[ETH_ALEN]);
+
+/* wps_upnp_ssdp.c */
+void msearchreply_state_machine_stop(struct advertisement_state_machine *a);
+int advertisement_state_machine_start(struct upnp_wps_device_sm *sm);
+void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm,
+				      int send_byebye);
+void ssdp_listener_stop(struct upnp_wps_device_sm *sm);
+int ssdp_listener_start(struct upnp_wps_device_sm *sm);
+int ssdp_listener_open(void);
+int add_ssdp_network(const char *net_if);
+int ssdp_open_multicast_sock(u32 ip_addr, const char *forced_ifname);
+int ssdp_open_multicast(struct upnp_wps_device_sm *sm);
+
+/* wps_upnp_web.c */
+int web_listener_start(struct upnp_wps_device_sm *sm);
+void web_listener_stop(struct upnp_wps_device_sm *sm);
+
+/* wps_upnp_event.c */
+int event_add(struct subscription *s, const struct wpabuf *data, int probereq);
+void event_delete_all(struct subscription *s);
+void event_send_all_later(struct upnp_wps_device_sm *sm);
+void event_send_stop_all(struct upnp_wps_device_sm *sm);
+
+/* wps_upnp_ap.c */
+int upnp_er_set_selected_registrar(struct wps_registrar *reg,
+				   struct subscription *s,
+				   const struct wpabuf *msg);
+void upnp_er_remove_notification(struct wps_registrar *reg,
+				 struct subscription *s);
+
+#endif /* WPS_UPNP_I_H */
diff --git a/hostap/src/wps/wps_upnp_ssdp.c b/hostap/src/wps/wps_upnp_ssdp.c
new file mode 100644
index 0000000..968fc03
--- /dev/null
+++ b/hostap/src/wps/wps_upnp_ssdp.c
@@ -0,0 +1,948 @@
+/*
+ * UPnP SSDP for WPS
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009-2013, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <net/route.h>
+#ifdef __linux__
+#include <net/if.h>
+#endif /* __linux__ */
+
+#include "common.h"
+#include "uuid.h"
+#include "eloop.h"
+#include "wps.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+#define UPNP_CACHE_SEC (UPNP_CACHE_SEC_MIN + 1) /* cache time we use */
+#define UPNP_CACHE_SEC_MIN 1800 /* min cachable time per UPnP standard */
+#define UPNP_ADVERTISE_REPEAT 2 /* no more than 3 */
+#define MAX_MSEARCH 20          /* max simultaneous M-SEARCH replies ongoing */
+#define SSDP_TARGET  "239.0.0.0"
+#define SSDP_NETMASK "255.0.0.0"
+
+
+/* Check tokens for equality, where tokens consist of letters, digits,
+ * underscore and hyphen, and are matched case insensitive.
+ */
+static int token_eq(const char *s1, const char *s2)
+{
+	int c1;
+	int c2;
+	int end1 = 0;
+	int end2 = 0;
+	for (;;) {
+		c1 = *s1++;
+		c2 = *s2++;
+		if (isalpha(c1) && isupper(c1))
+			c1 = tolower(c1);
+		if (isalpha(c2) && isupper(c2))
+			c2 = tolower(c2);
+		end1 = !(isalnum(c1) || c1 == '_' || c1 == '-');
+		end2 = !(isalnum(c2) || c2 == '_' || c2 == '-');
+		if (end1 || end2 || c1 != c2)
+			break;
+	}
+	return end1 && end2; /* reached end of both words? */
+}
+
+
+/* Return length of token (see above for definition of token) */
+static int token_length(const char *s)
+{
+	const char *begin = s;
+	for (;; s++) {
+		int c = *s;
+		int end = !(isalnum(c) || c == '_' || c == '-');
+		if (end)
+			break;
+	}
+	return s - begin;
+}
+
+
+/* return length of interword separation.
+ * This accepts only spaces/tabs and thus will not traverse a line
+ * or buffer ending.
+ */
+static int word_separation_length(const char *s)
+{
+	const char *begin = s;
+	for (;; s++) {
+		int c = *s;
+		if (c == ' ' || c == '\t')
+			continue;
+		break;
+	}
+	return s - begin;
+}
+
+
+/* No. of chars through (including) end of line */
+static int line_length(const char *l)
+{
+	const char *lp = l;
+	while (*lp && *lp != '\n')
+		lp++;
+	if (*lp == '\n')
+		lp++;
+	return lp - l;
+}
+
+
+static int str_starts(const char *str, const char *start)
+{
+	return os_strncmp(str, start, os_strlen(start)) == 0;
+}
+
+
+/***************************************************************************
+ * Advertisements.
+ * These are multicast to the world to tell them we are here.
+ * The individual packets are spread out in time to limit loss,
+ * and then after a much longer period of time the whole sequence
+ * is repeated again (for NOTIFYs only).
+ **************************************************************************/
+
+/**
+ * next_advertisement - Build next message and advance the state machine
+ * @a: Advertisement state
+ * @islast: Buffer for indicating whether this is the last message (= 1)
+ * Returns: The new message (caller is responsible for freeing this)
+ *
+ * Note: next_advertisement is shared code with msearchreply_* functions
+ */
+static struct wpabuf *
+next_advertisement(struct upnp_wps_device_sm *sm,
+		   struct advertisement_state_machine *a, int *islast)
+{
+	struct wpabuf *msg;
+	char *NTString = "";
+	char uuid_string[80];
+	struct upnp_wps_device_interface *iface;
+
+	*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)
+		return NULL;
+	switch (a->type) {
+	case ADVERTISE_UP:
+	case ADVERTISE_DOWN:
+		NTString = "NT";
+		wpabuf_put_str(msg, "NOTIFY * HTTP/1.1\r\n");
+		wpabuf_printf(msg, "HOST: %s:%d\r\n",
+			      UPNP_MULTICAST_ADDRESS, UPNP_MULTICAST_PORT);
+		wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
+			      UPNP_CACHE_SEC);
+		wpabuf_printf(msg, "NTS: %s\r\n",
+			      (a->type == ADVERTISE_UP ?
+			       "ssdp:alive" : "ssdp:byebye"));
+		break;
+	case MSEARCH_REPLY:
+		NTString = "ST";
+		wpabuf_put_str(msg, "HTTP/1.1 200 OK\r\n");
+		wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
+			      UPNP_CACHE_SEC);
+
+		wpabuf_put_str(msg, "DATE: ");
+		format_date(msg);
+		wpabuf_put_str(msg, "\r\n");
+
+		wpabuf_put_str(msg, "EXT:\r\n");
+		break;
+	}
+
+	if (a->type != ADVERTISE_DOWN) {
+		/* Where others may get our XML files from */
+		wpabuf_printf(msg, "LOCATION: http://%s:%d/%s\r\n",
+			      sm->ip_addr_text, sm->web_port,
+			      UPNP_WPS_DEVICE_XML_FILE);
+	}
+
+	/* The SERVER line has three comma-separated fields:
+	 *      operating system / version
+	 *      upnp version
+	 *      software package / version
+	 * However, only the UPnP version is really required, the
+	 * others can be place holders... for security reasons
+	 * it is better to NOT provide extra information.
+	 */
+	wpabuf_put_str(msg, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
+
+	switch (a->state / UPNP_ADVERTISE_REPEAT) {
+	case 0:
+		wpabuf_printf(msg, "%s: upnp:rootdevice\r\n", NTString);
+		wpabuf_printf(msg, "USN: uuid:%s::upnp:rootdevice\r\n",
+			      uuid_string);
+		break;
+	case 1:
+		wpabuf_printf(msg, "%s: uuid:%s\r\n", NTString, uuid_string);
+		wpabuf_printf(msg, "USN: uuid:%s\r\n", uuid_string);
+		break;
+	case 2:
+		wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:device:"
+			      "WFADevice:1\r\n", NTString);
+		wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
+			      "org:device:WFADevice:1\r\n", uuid_string);
+		break;
+	case 3:
+		wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:service:"
+			      "WFAWLANConfig:1\r\n", NTString);
+		wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
+			      "org:service:WFAWLANConfig:1\r\n", uuid_string);
+		break;
+	}
+	wpabuf_put_str(msg, "\r\n");
+
+	if (a->state + 1 >= 4 * UPNP_ADVERTISE_REPEAT)
+		*islast = 1;
+
+	return msg;
+}
+
+
+static void advertisement_state_machine_handler(void *eloop_data,
+						void *user_ctx);
+
+
+/**
+ * advertisement_state_machine_stop - Stop SSDP advertisements
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @send_byebye: Send byebye advertisement messages immediately
+ */
+void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm,
+				      int send_byebye)
+{
+	struct advertisement_state_machine *a = &sm->advertisement;
+	int islast = 0;
+	struct wpabuf *msg;
+	struct sockaddr_in dest;
+
+	eloop_cancel_timeout(advertisement_state_machine_handler, NULL, sm);
+	if (!send_byebye || sm->multicast_sd < 0)
+		return;
+
+	a->type = ADVERTISE_DOWN;
+	a->state = 0;
+
+	os_memset(&dest, 0, sizeof(dest));
+	dest.sin_family = AF_INET;
+	dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+	dest.sin_port = htons(UPNP_MULTICAST_PORT);
+
+	while (!islast) {
+		msg = next_advertisement(sm, a, &islast);
+		if (msg == NULL)
+			break;
+		if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg),
+			   0, (struct sockaddr *) &dest, sizeof(dest)) < 0) {
+			wpa_printf(MSG_INFO, "WPS UPnP: Advertisement sendto "
+				   "failed: %d (%s)", errno, strerror(errno));
+		}
+		wpabuf_free(msg);
+		a->state++;
+	}
+}
+
+
+static void advertisement_state_machine_handler(void *eloop_data,
+						void *user_ctx)
+{
+	struct upnp_wps_device_sm *sm = user_ctx;
+	struct advertisement_state_machine *a = &sm->advertisement;
+	struct wpabuf *msg;
+	int next_timeout_msec = 100;
+	int next_timeout_sec = 0;
+	struct sockaddr_in dest;
+	int islast = 0;
+
+	/*
+	 * Each is sent twice (in case lost) w/ 100 msec delay between;
+	 * spec says no more than 3 times.
+	 * One pair for rootdevice, one pair for uuid, and a pair each for
+	 * each of the two urns.
+	 * The entire sequence must be repeated before cache control timeout
+	 * (which  is min  1800 seconds),
+	 * recommend random portion of half of the advertised cache control age
+	 * to ensure against loss... perhaps 1800/4 + rand*1800/4 ?
+	 * Delay random interval < 100 msec prior to initial sending.
+	 * TTL of 4
+	 */
+
+	wpa_printf(MSG_MSGDUMP, "WPS UPnP: Advertisement state=%d", a->state);
+	msg = next_advertisement(sm, a, &islast);
+	if (msg == NULL)
+		return;
+
+	os_memset(&dest, 0, sizeof(dest));
+	dest.sin_family = AF_INET;
+	dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+	dest.sin_port = htons(UPNP_MULTICAST_PORT);
+
+	if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
+		   (struct sockaddr *) &dest, sizeof(dest)) == -1) {
+		wpa_printf(MSG_ERROR, "WPS UPnP: Advertisement sendto failed:"
+			   "%d (%s)", errno, strerror(errno));
+		next_timeout_msec = 0;
+		next_timeout_sec = 10; /* ... later */
+	} else if (islast) {
+		a->state = 0; /* wrap around */
+		if (a->type == ADVERTISE_DOWN) {
+			wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_DOWN->UP");
+			a->type = ADVERTISE_UP;
+			/* do it all over again right away */
+		} else {
+			u16 r;
+			/*
+			 * Start over again after a long timeout
+			 * (see notes above)
+			 */
+			next_timeout_msec = 0;
+			if (os_get_random((void *) &r, sizeof(r)) < 0)
+				r = 32768;
+			next_timeout_sec = UPNP_CACHE_SEC / 4 +
+				(((UPNP_CACHE_SEC / 4) * r) >> 16);
+			sm->advertise_count++;
+			wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_UP (#%u); "
+				   "next in %d sec",
+				   sm->advertise_count, next_timeout_sec);
+		}
+	} else {
+		a->state++;
+	}
+
+	wpabuf_free(msg);
+
+	eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+			       advertisement_state_machine_handler, NULL, sm);
+}
+
+
+/**
+ * advertisement_state_machine_start - Start SSDP advertisements
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int advertisement_state_machine_start(struct upnp_wps_device_sm *sm)
+{
+	struct advertisement_state_machine *a = &sm->advertisement;
+	int next_timeout_msec;
+
+	advertisement_state_machine_stop(sm, 0);
+
+	/*
+	 * Start out advertising down, this automatically switches
+	 * to advertising up which signals our restart.
+	 */
+	a->type = ADVERTISE_DOWN;
+	a->state = 0;
+	/* (other fields not used here) */
+
+	/* First timeout should be random interval < 100 msec */
+	next_timeout_msec = (100 * (os_random() & 0xFF)) >> 8;
+	return eloop_register_timeout(0, next_timeout_msec,
+				      advertisement_state_machine_handler,
+				      NULL, sm);
+}
+
+
+/***************************************************************************
+ * M-SEARCH replies
+ * These are very similar to the multicast advertisements, with some
+ * small changes in data content; and they are sent (UDP) to a specific
+ * unicast address instead of multicast.
+ * They are sent in response to a UDP M-SEARCH packet.
+ **************************************************************************/
+
+/**
+ * msearchreply_state_machine_stop - Stop M-SEARCH reply state machine
+ * @a: Selected advertisement/reply state
+ */
+void msearchreply_state_machine_stop(struct advertisement_state_machine *a)
+{
+	wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH stop");
+	dl_list_del(&a->list);
+	os_free(a);
+}
+
+
+static void msearchreply_state_machine_handler(void *eloop_data,
+					       void *user_ctx)
+{
+	struct advertisement_state_machine *a = user_ctx;
+	struct upnp_wps_device_sm *sm = eloop_data;
+	struct wpabuf *msg;
+	int next_timeout_msec = 100;
+	int next_timeout_sec = 0;
+	int islast = 0;
+
+	/*
+	 * Each response is sent twice (in case lost) w/ 100 msec delay
+	 * between; spec says no more than 3 times.
+	 * One pair for rootdevice, one pair for uuid, and a pair each for
+	 * each of the two urns.
+	 */
+
+	/* TODO: should only send the requested response types */
+
+	wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply state=%d (%s:%d)",
+		   a->state, inet_ntoa(a->client.sin_addr),
+		   ntohs(a->client.sin_port));
+	msg = next_advertisement(sm, a, &islast);
+	if (msg == NULL)
+		return;
+
+	/*
+	 * Send it on the multicast socket to avoid having to set up another
+	 * socket.
+	 */
+	if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
+		   (struct sockaddr *) &a->client, sizeof(a->client)) < 0) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply sendto "
+			   "errno %d (%s) for %s:%d",
+			   errno, strerror(errno),
+			   inet_ntoa(a->client.sin_addr),
+			   ntohs(a->client.sin_port));
+		/* Ignore error and hope for the best */
+	}
+	wpabuf_free(msg);
+	if (islast) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply done");
+		msearchreply_state_machine_stop(a);
+		return;
+	}
+	a->state++;
+
+	wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply in %d.%03d sec",
+		   next_timeout_sec, next_timeout_msec);
+	eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+			       msearchreply_state_machine_handler, sm, a);
+}
+
+
+/**
+ * msearchreply_state_machine_start - Reply to M-SEARCH discovery request
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @client: Client address
+ * @mx: Maximum delay in seconds
+ *
+ * Use TTL of 4 (this was done when socket set up).
+ * A response should be given in randomized portion of min(MX,120) seconds
+ *
+ * UPnP-arch-DeviceArchitecture, 1.2.3:
+ * To be found, a device must send a UDP response to the source IP address and
+ * port that sent the request to the multicast channel. Devices respond if the
+ * ST header of the M-SEARCH request is "ssdp:all", "upnp:rootdevice", "uuid:"
+ * followed by a UUID that exactly matches one advertised by the device.
+ */
+static void msearchreply_state_machine_start(struct upnp_wps_device_sm *sm,
+					     struct sockaddr_in *client,
+					     int mx)
+{
+	struct advertisement_state_machine *a;
+	int next_timeout_sec;
+	int next_timeout_msec;
+	int replies;
+
+	replies = dl_list_len(&sm->msearch_replies);
+	wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply start (%d "
+		   "outstanding)", replies);
+	if (replies >= MAX_MSEARCH) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Too many outstanding "
+			   "M-SEARCH replies");
+		return;
+	}
+
+	a = os_zalloc(sizeof(*a));
+	if (a == NULL)
+		return;
+	a->type = MSEARCH_REPLY;
+	a->state = 0;
+	os_memcpy(&a->client, client, sizeof(*client));
+	/* Wait time depending on MX value */
+	next_timeout_msec = (1000 * mx * (os_random() & 0xFF)) >> 8;
+	next_timeout_sec = next_timeout_msec / 1000;
+	next_timeout_msec = next_timeout_msec % 1000;
+	if (eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+				   msearchreply_state_machine_handler, sm,
+				   a)) {
+		/* No way to recover (from malloc failure) */
+		goto fail;
+	}
+	/* Remember for future cleanup */
+	dl_list_add(&sm->msearch_replies, &a->list);
+	return;
+
+fail:
+	wpa_printf(MSG_INFO, "WPS UPnP: M-SEARCH reply failure!");
+	eloop_cancel_timeout(msearchreply_state_machine_handler, sm, a);
+	os_free(a);
+}
+
+
+/**
+ * ssdp_parse_msearch - Process a received M-SEARCH
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @client: Client address
+ * @data: NULL terminated M-SEARCH message
+ *
+ * Given that we have received a header w/ M-SEARCH, act upon it
+ *
+ * Format of M-SEARCH (case insensitive!):
+ *
+ * First line must be:
+ *      M-SEARCH * HTTP/1.1
+ * Other lines in arbitrary order:
+ *      HOST:239.255.255.250:1900
+ *      ST:<varies -- must match>
+ *      MAN:"ssdp:discover"
+ *      MX:<varies>
+ *
+ * It should be noted that when Microsoft Vista is still learning its IP
+ * address, it sends out host lines like: HOST:[FF02::C]:1900
+ */
+static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
+			       struct sockaddr_in *client, const char *data)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+	const char *start = data;
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+	int got_host = 0;
+	int got_st = 0, st_match = 0;
+	int got_man = 0;
+	int got_mx = 0;
+	int mx = 0;
+
+	/*
+	 * Skip first line M-SEARCH * HTTP/1.1
+	 * (perhaps we should check remainder of the line for syntax)
+	 */
+	data += line_length(data);
+
+	/* Parse remaining lines */
+	for (; *data != '\0'; data += line_length(data)) {
+		if (token_eq(data, "host")) {
+			/* The host line indicates who the packet
+			 * is addressed to... but do we really care?
+			 * Note that Microsoft sometimes does funny
+			 * stuff with the HOST: line.
+			 */
+#if 0   /* could be */
+			data += token_length(data);
+			data += word_separation_length(data);
+			if (*data != ':')
+				goto bad;
+			data++;
+			data += word_separation_length(data);
+			/* UPNP_MULTICAST_ADDRESS */
+			if (!str_starts(data, "239.255.255.250"))
+				goto bad;
+			data += os_strlen("239.255.255.250");
+			if (*data == ':') {
+				if (!str_starts(data, ":1900"))
+					goto bad;
+			}
+#endif  /* could be */
+			got_host = 1;
+			continue;
+		} else if (token_eq(data, "st")) {
+			/* There are a number of forms; we look
+			 * for one that matches our case.
+			 */
+			got_st = 1;
+			data += token_length(data);
+			data += word_separation_length(data);
+			if (*data != ':')
+				continue;
+			data++;
+			data += word_separation_length(data);
+			if (str_starts(data, "ssdp:all")) {
+				st_match = 1;
+				continue;
+			}
+			if (str_starts(data, "upnp:rootdevice")) {
+				st_match = 1;
+				continue;
+			}
+			if (str_starts(data, "uuid:")) {
+				char uuid_string[80];
+				struct upnp_wps_device_interface *iface;
+				iface = dl_list_first(
+					&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));
+				if (str_starts(data, uuid_string))
+					st_match = 1;
+				continue;
+			}
+#if 0
+			/* FIX: should we really reply to IGD string? */
+			if (str_starts(data, "urn:schemas-upnp-org:device:"
+				       "InternetGatewayDevice:1")) {
+				st_match = 1;
+				continue;
+			}
+#endif
+			if (str_starts(data, "urn:schemas-wifialliance-org:"
+				       "service:WFAWLANConfig:1")) {
+				st_match = 1;
+				continue;
+			}
+			if (str_starts(data, "urn:schemas-wifialliance-org:"
+				       "device:WFADevice:1")) {
+				st_match = 1;
+				continue;
+			}
+			continue;
+		} else if (token_eq(data, "man")) {
+			data += token_length(data);
+			data += word_separation_length(data);
+			if (*data != ':')
+				continue;
+			data++;
+			data += word_separation_length(data);
+			if (!str_starts(data, "\"ssdp:discover\"")) {
+				wpa_printf(MSG_DEBUG, "WPS UPnP: Unexpected "
+					   "M-SEARCH man-field");
+				goto bad;
+			}
+			got_man = 1;
+			continue;
+		} else if (token_eq(data, "mx")) {
+			data += token_length(data);
+			data += word_separation_length(data);
+			if (*data != ':')
+				continue;
+			data++;
+			data += word_separation_length(data);
+			mx = atol(data);
+			got_mx = 1;
+			continue;
+		}
+		/* ignore anything else */
+	}
+	if (!got_host || !got_st || !got_man || !got_mx || mx < 0) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid M-SEARCH: %d %d %d "
+			   "%d mx=%d", got_host, got_st, got_man, got_mx, mx);
+		goto bad;
+	}
+	if (!st_match) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored M-SEARCH (no ST "
+			   "match)");
+		return;
+	}
+	if (mx > 120)
+		mx = 120; /* UPnP-arch-DeviceArchitecture, 1.2.3 */
+	msearchreply_state_machine_start(sm, client, mx);
+	return;
+
+bad:
+	wpa_printf(MSG_INFO, "WPS UPnP: Failed to parse M-SEARCH");
+	wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH data:\n%s", start);
+}
+
+
+/* Listening for (UDP) discovery (M-SEARCH) packets */
+
+/**
+ * ssdp_listener_stop - Stop SSDP listered
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ *
+ * This function stops the SSDP listener that was started by calling
+ * ssdp_listener_start().
+ */
+void ssdp_listener_stop(struct upnp_wps_device_sm *sm)
+{
+	if (sm->ssdp_sd_registered) {
+		eloop_unregister_sock(sm->ssdp_sd, EVENT_TYPE_READ);
+		sm->ssdp_sd_registered = 0;
+	}
+
+	if (sm->ssdp_sd != -1) {
+		close(sm->ssdp_sd);
+		sm->ssdp_sd = -1;
+	}
+
+	eloop_cancel_timeout(msearchreply_state_machine_handler, sm,
+			     ELOOP_ALL_CTX);
+}
+
+
+static void ssdp_listener_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+	struct upnp_wps_device_sm *sm = sock_ctx;
+	struct sockaddr_in addr; /* client address */
+	socklen_t addr_len;
+	int nread;
+	char buf[MULTICAST_MAX_READ], *pos;
+
+	addr_len = sizeof(addr);
+	nread = recvfrom(sm->ssdp_sd, buf, sizeof(buf) - 1, 0,
+			 (struct sockaddr *) &addr, &addr_len);
+	if (nread <= 0)
+		return;
+	buf[nread] = '\0'; /* need null termination for algorithm */
+
+	if (str_starts(buf, "NOTIFY ")) {
+		/*
+		 * Silently ignore NOTIFYs to avoid filling debug log with
+		 * unwanted messages.
+		 */
+		return;
+	}
+
+	pos = os_strchr(buf, '\n');
+	if (pos)
+		*pos = '\0';
+	wpa_printf(MSG_MSGDUMP, "WPS UPnP: Received SSDP packet from %s:%d: "
+		   "%s", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), buf);
+	if (pos)
+		*pos = '\n';
+
+	/* Parse first line */
+	if (os_strncasecmp(buf, "M-SEARCH", os_strlen("M-SEARCH")) == 0 &&
+	    !isgraph(buf[strlen("M-SEARCH")])) {
+		ssdp_parse_msearch(sm, &addr, buf);
+		return;
+	}
+
+	/* Ignore anything else */
+}
+
+
+int ssdp_listener_open(void)
+{
+	struct sockaddr_in addr;
+	struct ip_mreq mcast_addr;
+	int on = 1;
+	/* per UPnP spec, keep IP packet time to live (TTL) small */
+	unsigned char ttl = 4;
+	int sd;
+
+	sd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sd < 0 ||
+	    fcntl(sd, F_SETFL, O_NONBLOCK) != 0 ||
+	    setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
+		goto fail;
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = htonl(INADDR_ANY);
+	addr.sin_port = htons(UPNP_MULTICAST_PORT);
+	if (bind(sd, (struct sockaddr *) &addr, sizeof(addr)))
+		goto fail;
+	os_memset(&mcast_addr, 0, sizeof(mcast_addr));
+	mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY);
+	mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+	if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+		       (char *) &mcast_addr, sizeof(mcast_addr)) ||
+	    setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
+		       &ttl, sizeof(ttl)))
+		goto fail;
+
+	return sd;
+
+fail:
+	if (sd >= 0)
+		close(sd);
+	return -1;
+}
+
+
+/**
+ * ssdp_listener_start - Set up for receiving discovery (UDP) packets
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * The SSDP listener is stopped by calling ssdp_listener_stop().
+ */
+int ssdp_listener_start(struct upnp_wps_device_sm *sm)
+{
+	sm->ssdp_sd = ssdp_listener_open();
+
+	if (eloop_register_sock(sm->ssdp_sd, EVENT_TYPE_READ,
+				ssdp_listener_handler, NULL, sm))
+		goto fail;
+	sm->ssdp_sd_registered = 1;
+	return 0;
+
+fail:
+	/* Error */
+	wpa_printf(MSG_ERROR, "WPS UPnP: ssdp_listener_start failed");
+	ssdp_listener_stop(sm);
+	return -1;
+}
+
+
+/**
+ * add_ssdp_network - Add routing entry for SSDP
+ * @net_if: Selected network interface name
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function assures that the multicast address will be properly
+ * handled by Linux networking code (by a modification to routing tables).
+ * This must be done per network interface. It really only needs to be done
+ * once after booting up, but it does not hurt to call this more frequently
+ * "to be safe".
+ */
+int add_ssdp_network(const char *net_if)
+{
+#ifdef __linux__
+	int ret = -1;
+	int sock = -1;
+	struct rtentry rt;
+	struct sockaddr_in *sin;
+
+	if (!net_if)
+		goto fail;
+
+	os_memset(&rt, 0, sizeof(rt));
+	sock = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock < 0)
+		goto fail;
+
+	rt.rt_dev = (char *) net_if;
+	sin = aliasing_hide_typecast(&rt.rt_dst, struct sockaddr_in);
+	sin->sin_family = AF_INET;
+	sin->sin_port = 0;
+	sin->sin_addr.s_addr = inet_addr(SSDP_TARGET);
+	sin = aliasing_hide_typecast(&rt.rt_genmask, struct sockaddr_in);
+	sin->sin_family = AF_INET;
+	sin->sin_port = 0;
+	sin->sin_addr.s_addr = inet_addr(SSDP_NETMASK);
+	rt.rt_flags = RTF_UP;
+	if (ioctl(sock, SIOCADDRT, &rt) < 0) {
+		if (errno == EPERM) {
+			wpa_printf(MSG_DEBUG, "add_ssdp_network: No "
+				   "permissions to add routing table entry");
+			/* Continue to allow testing as non-root */
+		} else if (errno != EEXIST) {
+			wpa_printf(MSG_INFO, "add_ssdp_network() ioctl errno "
+				   "%d (%s)", errno, strerror(errno));
+			goto fail;
+		}
+	}
+
+	ret = 0;
+
+fail:
+	if (sock >= 0)
+		close(sock);
+
+	return ret;
+#else /* __linux__ */
+	return 0;
+#endif /* __linux__ */
+}
+
+
+int ssdp_open_multicast_sock(u32 ip_addr, const char *forced_ifname)
+{
+	int sd;
+	 /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet
+	  * time to live (TTL) small */
+	unsigned char ttl = 4;
+
+	sd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sd < 0)
+		return -1;
+
+	if (forced_ifname) {
+#ifdef __linux__
+		struct ifreq req;
+		os_memset(&req, 0, sizeof(req));
+		os_strlcpy(req.ifr_name, forced_ifname, sizeof(req.ifr_name));
+		if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &req,
+			       sizeof(req)) < 0) {
+			wpa_printf(MSG_INFO, "WPS UPnP: Failed to bind "
+				   "multicast socket to ifname %s: %s",
+				   forced_ifname, strerror(errno));
+			close(sd);
+			return -1;
+		}
+#endif /* __linux__ */
+	}
+
+#if 0   /* maybe ok if we sometimes block on writes */
+	if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) {
+		close(sd);
+		return -1;
+	}
+#endif
+
+	if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
+		       &ip_addr, sizeof(ip_addr))) {
+		wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_IF) %x: "
+			   "%d (%s)", ip_addr, errno, strerror(errno));
+		close(sd);
+		return -1;
+	}
+	if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
+		       &ttl, sizeof(ttl))) {
+		wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): "
+			   "%d (%s)", errno, strerror(errno));
+		close(sd);
+		return -1;
+	}
+
+#if 0   /* not needed, because we don't receive using multicast_sd */
+	{
+		struct ip_mreq mreq;
+		mreq.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+		mreq.imr_interface.s_addr = ip_addr;
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Multicast addr 0x%x if addr "
+			   "0x%x",
+			   mreq.imr_multiaddr.s_addr,
+			   mreq.imr_interface.s_addr);
+		if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+				sizeof(mreq))) {
+			wpa_printf(MSG_ERROR,
+				   "WPS UPnP: setsockopt "
+				   "IP_ADD_MEMBERSHIP errno %d (%s)",
+				   errno, strerror(errno));
+			close(sd);
+			return -1;
+		}
+	}
+#endif  /* not needed */
+
+	/*
+	 * TODO: What about IP_MULTICAST_LOOP? It seems to be on by default?
+	 * which aids debugging I suppose but isn't really necessary?
+	 */
+
+	return sd;
+}
+
+
+/**
+ * ssdp_open_multicast - Open socket for sending multicast SSDP messages
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int ssdp_open_multicast(struct upnp_wps_device_sm *sm)
+{
+	sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr, NULL);
+	if (sm->multicast_sd < 0)
+		return -1;
+	return 0;
+}
diff --git a/hostap/src/wps/wps_upnp_web.c b/hostap/src/wps/wps_upnp_web.c
new file mode 100644
index 0000000..d5b0b5b
--- /dev/null
+++ b/hostap/src/wps/wps_upnp_web.c
@@ -0,0 +1,1350 @@
+/*
+ * UPnP WPS Device - Web connections
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "uuid.h"
+#include "httpread.h"
+#include "http_server.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+#include "upnp_xml.h"
+
+/***************************************************************************
+ * Web connections (we serve pages of info about ourselves, handle
+ * requests, etc. etc.).
+ **************************************************************************/
+
+#define WEB_CONNECTION_TIMEOUT_SEC 30   /* Drop web connection after t.o. */
+#define WEB_CONNECTION_MAX_READ 8000    /* Max we'll read for TCP request */
+#define MAX_WEB_CONNECTIONS 10          /* max simultaneous web connects */
+
+
+static const char *urn_wfawlanconfig =
+	"urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
+static const char *http_server_hdr =
+	"Server: unspecified, UPnP/1.0, unspecified\r\n";
+static const char *http_connection_close =
+	"Connection: close\r\n";
+
+/*
+ * "Files" that we serve via HTTP. The format of these files is given by
+ * WFA WPS specifications. Extra white space has been removed to save space.
+ */
+
+static const char wps_scpd_xml[] =
+"<?xml version=\"1.0\"?>\n"
+"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n"
+"<specVersion><major>1</major><minor>0</minor></specVersion>\n"
+"<actionList>\n"
+"<action>\n"
+"<name>GetDeviceInfo</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewDeviceInfo</name>\n"
+"<direction>out</direction>\n"
+"<relatedStateVariable>DeviceInfo</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>PutMessage</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewInMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>InMessage</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewOutMessage</name>\n"
+"<direction>out</direction>\n"
+"<relatedStateVariable>OutMessage</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>PutWLANResponse</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>Message</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewWLANEventType</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewWLANEventMAC</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>SetSelectedRegistrar</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>Message</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"</actionList>\n"
+"<serviceStateTable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>Message</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>InMessage</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>OutMessage</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>DeviceInfo</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>APStatus</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>STAStatus</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>WLANEvent</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANEventType</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANEventMAC</name>\n"
+"<dataType>string</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANResponse</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"</serviceStateTable>\n"
+"</scpd>\n"
+;
+
+
+static const char *wps_device_xml_prefix =
+	"<?xml version=\"1.0\"?>\n"
+	"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
+	"<specVersion>\n"
+	"<major>1</major>\n"
+	"<minor>0</minor>\n"
+	"</specVersion>\n"
+	"<device>\n"
+	"<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
+	"</deviceType>\n";
+
+static const char *wps_device_xml_postfix =
+	"<serviceList>\n"
+	"<service>\n"
+	"<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
+	"</serviceType>\n"
+	"<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
+	"\n"
+	"<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n"
+	"<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n"
+	"<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n"
+	"</service>\n"
+	"</serviceList>\n"
+	"</device>\n"
+	"</root>\n";
+
+
+/* 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_interface *iface,
+				  struct upnp_wps_device_sm *sm,
+				  struct wpabuf *buf)
+{
+	const char *s;
+	char uuid_string[80];
+
+	wpabuf_put_str(buf, wps_device_xml_prefix);
+
+	/*
+	 * Add required fields with default values if not configured. Add
+	 * optional and recommended fields only if configured.
+	 */
+	s = iface->wps->friendly_name;
+	s = ((s && *s) ? s : "WPS Access Point");
+	xml_add_tagged_data(buf, "friendlyName", s);
+
+	s = iface->wps->dev.manufacturer;
+	s = ((s && *s) ? s : "");
+	xml_add_tagged_data(buf, "manufacturer", s);
+
+	if (iface->wps->manufacturer_url)
+		xml_add_tagged_data(buf, "manufacturerURL",
+				    iface->wps->manufacturer_url);
+
+	if (iface->wps->model_description)
+		xml_add_tagged_data(buf, "modelDescription",
+				    iface->wps->model_description);
+
+	s = iface->wps->dev.model_name;
+	s = ((s && *s) ? s : "");
+	xml_add_tagged_data(buf, "modelName", s);
+
+	if (iface->wps->dev.model_number)
+		xml_add_tagged_data(buf, "modelNumber",
+				    iface->wps->dev.model_number);
+
+	if (iface->wps->model_url)
+		xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
+
+	if (iface->wps->dev.serial_number)
+		xml_add_tagged_data(buf, "serialNumber",
+				    iface->wps->dev.serial_number);
+
+	uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
+	s = uuid_string;
+	/* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
+	 * easily...
+	 */
+	wpabuf_put_str(buf, "<UDN>uuid:");
+	xml_data_encode(buf, s, os_strlen(s));
+	wpabuf_put_str(buf, "</UDN>\n");
+
+	if (iface->wps->upc)
+		xml_add_tagged_data(buf, "UPC", iface->wps->upc);
+
+	wpabuf_put_str(buf, wps_device_xml_postfix);
+}
+
+
+static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
+{
+	wpabuf_put_str(buf, "HTTP/1.1 ");
+	switch (code) {
+	case HTTP_OK:
+		wpabuf_put_str(buf, "200 OK\r\n");
+		break;
+	case HTTP_BAD_REQUEST:
+		wpabuf_put_str(buf, "400 Bad request\r\n");
+		break;
+	case HTTP_PRECONDITION_FAILED:
+		wpabuf_put_str(buf, "412 Precondition failed\r\n");
+		break;
+	case HTTP_UNIMPLEMENTED:
+		wpabuf_put_str(buf, "501 Unimplemented\r\n");
+		break;
+	case HTTP_INTERNAL_SERVER_ERROR:
+	default:
+		wpabuf_put_str(buf, "500 Internal server error\r\n");
+		break;
+	}
+}
+
+
+static void http_put_date(struct wpabuf *buf)
+{
+	wpabuf_put_str(buf, "Date: ");
+	format_date(buf);
+	wpabuf_put_str(buf, "\r\n");
+}
+
+
+static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
+{
+	http_put_reply_code(buf, code);
+	wpabuf_put_str(buf, http_server_hdr);
+	wpabuf_put_str(buf, http_connection_close);
+	wpabuf_put_str(buf, "Content-Length: 0\r\n"
+		       "\r\n");
+}
+
+
+/* Given that we have received a header w/ GET, act upon it
+ *
+ * Format of GET (case-insensitive):
+ *
+ * First line must be:
+ *      GET /<file> HTTP/1.1
+ * Since we don't do anything fancy we just ignore other lines.
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Connection: close
+ * Content-Type: text/xml
+ * Date: <rfc1123-date>
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
+				     struct http_request *hreq, char *filename)
+{
+	struct wpabuf *buf; /* output buffer, allocated */
+	char *put_length_here;
+	char *body_start;
+	enum {
+		GET_DEVICE_XML_FILE,
+		GET_SCPD_XML_FILE
+	} req;
+	size_t extra_len = 0;
+	int body_length;
+	char len_buf[10];
+	struct upnp_wps_device_interface *iface;
+
+	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 (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;
+		extra_len = 3000;
+		if (iface->wps->friendly_name)
+			extra_len += os_strlen(iface->wps->friendly_name);
+		if (iface->wps->manufacturer_url)
+			extra_len += os_strlen(iface->wps->manufacturer_url);
+		if (iface->wps->model_description)
+			extra_len += os_strlen(iface->wps->model_description);
+		if (iface->wps->model_url)
+			extra_len += os_strlen(iface->wps->model_url);
+		if (iface->wps->upc)
+			extra_len += os_strlen(iface->wps->upc);
+	} else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
+		req = GET_SCPD_XML_FILE;
+		extra_len = os_strlen(wps_scpd_xml);
+	} else {
+		/* File not found */
+		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
+			   filename);
+		buf = wpabuf_alloc(200);
+		if (buf == NULL) {
+			http_request_deinit(hreq);
+			return;
+		}
+		wpabuf_put_str(buf,
+			       "HTTP/1.1 404 Not Found\r\n"
+			       "Connection: close\r\n");
+
+		http_put_date(buf);
+
+		/* terminating empty line */
+		wpabuf_put_str(buf, "\r\n");
+
+		goto send_buf;
+	}
+
+	buf = wpabuf_alloc(1000 + extra_len);
+	if (buf == NULL) {
+		http_request_deinit(hreq);
+		return;
+	}
+
+	wpabuf_put_str(buf,
+		       "HTTP/1.1 200 OK\r\n"
+		       "Content-Type: text/xml; charset=\"utf-8\"\r\n");
+	wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
+	wpabuf_put_str(buf, "Connection: close\r\n");
+	wpabuf_put_str(buf, "Content-Length: ");
+	/*
+	 * We will paste the length in later, leaving some extra whitespace.
+	 * HTTP code is supposed to be tolerant of extra whitespace.
+	 */
+	put_length_here = wpabuf_put(buf, 0);
+	wpabuf_put_str(buf, "        \r\n");
+
+	http_put_date(buf);
+
+	/* terminating empty line */
+	wpabuf_put_str(buf, "\r\n");
+
+	body_start = wpabuf_put(buf, 0);
+
+	switch (req) {
+	case GET_DEVICE_XML_FILE:
+		format_wps_device_xml(iface, sm, buf);
+		break;
+	case GET_SCPD_XML_FILE:
+		wpabuf_put_str(buf, wps_scpd_xml);
+		break;
+	}
+
+	/* Now patch in the content length at the end */
+	body_length = (char *) wpabuf_put(buf, 0) - body_start;
+	os_snprintf(len_buf, 10, "%d", body_length);
+	os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
+
+send_buf:
+	http_request_send_and_deinit(hreq, buf);
+}
+
+
+static enum http_reply_code
+web_process_get_device_info(struct upnp_wps_device_sm *sm,
+			    struct wpabuf **reply, const char **replyname)
+{
+	static const char *name = "NewDeviceInfo";
+	struct wps_config cfg;
+	struct upnp_wps_device_interface *iface;
+	struct upnp_wps_peer *peer;
+
+	iface = dl_list_first(&sm->interfaces,
+			      struct upnp_wps_device_interface, list);
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
+
+	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
+	 * be noted that this is frequently used just to get the device data,
+	 * i.e., there may not be any intent to actually complete the
+	 * registration.
+	 */
+
+	if (peer->wps)
+		wps_deinit(peer->wps);
+
+	os_memset(&cfg, 0, sizeof(cfg));
+	cfg.wps = iface->wps;
+	cfg.pin = (u8 *) iface->ctx->ap_pin;
+	cfg.pin_len = os_strlen(iface->ctx->ap_pin);
+	peer->wps = wps_init(&cfg);
+	if (peer->wps) {
+		enum wsc_op_code op_code;
+		*reply = wps_get_msg(peer->wps, &op_code);
+		if (*reply == NULL) {
+			wps_deinit(peer->wps);
+			peer->wps = NULL;
+		}
+	} else
+		*reply = NULL;
+	if (*reply == NULL) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
+		return HTTP_INTERNAL_SERVER_ERROR;
+	}
+	*replyname = name;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
+			struct wpabuf **reply, const char **replyname)
+{
+	struct wpabuf *msg;
+	static const char *name = "NewOutMessage";
+	enum http_reply_code ret;
+	enum wps_process_res res;
+	enum wsc_op_code op_code;
+	struct upnp_wps_device_interface *iface;
+
+	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
+	 * operation with the access point itself; as compared with
+	 * PutWLANResponse which is for proxying.
+	 */
+	wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
+	msg = xml_get_base64_item(data, "NewInMessage", &ret);
+	if (msg == NULL)
+		return ret;
+	res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
+	if (res == WPS_FAILURE)
+		*reply = NULL;
+	else
+		*reply = wps_get_msg(iface->peer.wps, &op_code);
+	wpabuf_free(msg);
+	if (*reply == NULL)
+		return HTTP_INTERNAL_SERVER_ERROR;
+	*replyname = name;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
+			      struct wpabuf **reply, const char **replyname)
+{
+	struct wpabuf *msg;
+	enum http_reply_code ret;
+	u8 macaddr[ETH_ALEN];
+	int ev_type;
+	int type;
+	char *val;
+	struct upnp_wps_device_interface *iface;
+	int ok = 0;
+
+	/*
+	 * External UPnP-based Registrar is passing us a message to be proxied
+	 * over to a Wi-Fi -based client of ours.
+	 */
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
+	msg = xml_get_base64_item(data, "NewMessage", &ret);
+	if (msg == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage "
+			   "from PutWLANResponse");
+		return ret;
+	}
+	val = xml_get_first_item(data, "NewWLANEventType");
+	if (val == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventType in "
+			   "PutWLANResponse");
+		wpabuf_free(msg);
+		return UPNP_ARG_VALUE_INVALID;
+	}
+	ev_type = atol(val);
+	os_free(val);
+	val = xml_get_first_item(data, "NewWLANEventMAC");
+	if (val == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventMAC in "
+			   "PutWLANResponse");
+		wpabuf_free(msg);
+		return UPNP_ARG_VALUE_INVALID;
+	}
+	if (hwaddr_aton(val, macaddr)) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in "
+			   "PutWLANResponse: '%s'", val);
+#ifdef CONFIG_WPS_STRICT
+		{
+			struct wps_parse_attr attr;
+			if (wps_parse_msg(msg, &attr) < 0 || attr.version2) {
+				wpabuf_free(msg);
+				os_free(val);
+				return UPNP_ARG_VALUE_INVALID;
+			}
+		}
+#endif /* CONFIG_WPS_STRICT */
+		if (hwaddr_aton2(val, macaddr) > 0) {
+			/*
+			 * At least some versions of Intel PROset seem to be
+			 * using dot-deliminated MAC address format here.
+			 */
+			wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
+				   "incorrect MAC address format in "
+				   "NewWLANEventMAC: %s -> " MACSTR,
+				   val, MAC2STR(macaddr));
+		} else {
+			wpabuf_free(msg);
+			os_free(val);
+			return UPNP_ARG_VALUE_INVALID;
+		}
+	}
+	os_free(val);
+	if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
+		struct wps_parse_attr attr;
+		if (wps_parse_msg(msg, &attr) < 0 ||
+		    attr.msg_type == NULL)
+			type = -1;
+		else
+			type = *attr.msg_type;
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
+	} else
+		type = -1;
+	dl_list_for_each(iface, &sm->interfaces,
+			 struct upnp_wps_device_interface, list) {
+		if (iface->ctx->rx_req_put_wlan_response &&
+		    iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
+							 macaddr, msg, type)
+		    == 0)
+			ok = 1;
+	}
+
+	if (!ok) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
+			   "rx_req_put_wlan_response");
+		wpabuf_free(msg);
+		return HTTP_INTERNAL_SERVER_ERROR;
+	}
+	wpabuf_free(msg);
+	*replyname = NULL;
+	*reply = NULL;
+	return HTTP_OK;
+}
+
+
+static int find_er_addr(struct subscription *s, struct sockaddr_in *cli)
+{
+	struct subscr_addr *a;
+
+	dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) {
+		if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr)
+			return 1;
+	}
+	return 0;
+}
+
+
+static struct subscription * find_er(struct upnp_wps_device_sm *sm,
+				     struct sockaddr_in *cli)
+{
+	struct subscription *s;
+	dl_list_for_each(s, &sm->subscriptions, struct subscription, list)
+		if (find_er_addr(s, cli))
+			return s;
+	return NULL;
+}
+
+
+static enum http_reply_code
+web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
+				   struct sockaddr_in *cli, char *data,
+				   struct wpabuf **reply,
+				   const char **replyname)
+{
+	struct wpabuf *msg;
+	enum http_reply_code ret;
+	struct subscription *s;
+	struct upnp_wps_device_interface *iface;
+	int err = 0;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
+	s = find_er(sm, cli);
+	if (s == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar "
+			   "from unknown ER");
+		return UPNP_ACTION_FAILED;
+	}
+	msg = xml_get_base64_item(data, "NewMessage", &ret);
+	if (msg == NULL)
+		return ret;
+	dl_list_for_each(iface, &sm->interfaces,
+			 struct upnp_wps_device_interface, list) {
+		if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
+						   msg))
+			err = 1;
+	}
+	wpabuf_free(msg);
+	if (err)
+		return HTTP_INTERNAL_SERVER_ERROR;
+	*replyname = NULL;
+	*reply = NULL;
+	return HTTP_OK;
+}
+
+
+static const char *soap_prefix =
+	"<?xml version=\"1.0\"?>\n"
+	"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+	"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
+	"<s:Body>\n";
+static const char *soap_postfix =
+	"</s:Body>\n</s:Envelope>\n";
+
+static const char *soap_error_prefix =
+	"<s:Fault>\n"
+	"<faultcode>s:Client</faultcode>\n"
+	"<faultstring>UPnPError</faultstring>\n"
+	"<detail>\n"
+	"<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
+static const char *soap_error_postfix =
+	"<errorDescription>Error</errorDescription>\n"
+	"</UPnPError>\n"
+	"</detail>\n"
+	"</s:Fault>\n";
+
+static void web_connection_send_reply(struct http_request *req,
+				      enum http_reply_code ret,
+				      const char *action, int action_len,
+				      const struct wpabuf *reply,
+				      const char *replyname)
+{
+	struct wpabuf *buf;
+	char *replydata;
+	char *put_length_here = NULL;
+	char *body_start = NULL;
+
+	if (reply) {
+		size_t len;
+		replydata = (char *) base64_encode(wpabuf_head(reply),
+						   wpabuf_len(reply), &len);
+	} else
+		replydata = NULL;
+
+	/* Parameters of the response:
+	 *      action(action_len) -- action we are responding to
+	 *      replyname -- a name we need for the reply
+	 *      replydata -- NULL or null-terminated string
+	 */
+	buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
+			   (action_len > 0 ? action_len * 2 : 0));
+	if (buf == NULL) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
+			   "POST");
+		os_free(replydata);
+		http_request_deinit(req);
+		return;
+	}
+
+	/*
+	 * Assuming we will be successful, put in the output header first.
+	 * Note: we do not keep connections alive (and httpread does
+	 * not support it)... therefore we must have Connection: close.
+	 */
+	if (ret == HTTP_OK) {
+		wpabuf_put_str(buf,
+			       "HTTP/1.1 200 OK\r\n"
+			       "Content-Type: text/xml; "
+			       "charset=\"utf-8\"\r\n");
+	} else {
+		wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
+	}
+	wpabuf_put_str(buf, http_connection_close);
+
+	wpabuf_put_str(buf, "Content-Length: ");
+	/*
+	 * We will paste the length in later, leaving some extra whitespace.
+	 * HTTP code is supposed to be tolerant of extra whitespace.
+	 */
+	put_length_here = wpabuf_put(buf, 0);
+	wpabuf_put_str(buf, "        \r\n");
+
+	http_put_date(buf);
+
+	/* terminating empty line */
+	wpabuf_put_str(buf, "\r\n");
+
+	body_start = wpabuf_put(buf, 0);
+
+	if (ret == HTTP_OK) {
+		wpabuf_put_str(buf, soap_prefix);
+		wpabuf_put_str(buf, "<u:");
+		wpabuf_put_data(buf, action, action_len);
+		wpabuf_put_str(buf, "Response xmlns:u=\"");
+		wpabuf_put_str(buf, urn_wfawlanconfig);
+		wpabuf_put_str(buf, "\">\n");
+		if (replydata && replyname) {
+			/* TODO: might possibly need to escape part of reply
+			 * data? ...
+			 * probably not, unlikely to have ampersand(&) or left
+			 * angle bracket (<) in it...
+			 */
+			wpabuf_printf(buf, "<%s>", replyname);
+			wpabuf_put_str(buf, replydata);
+			wpabuf_printf(buf, "</%s>\n", replyname);
+		}
+		wpabuf_put_str(buf, "</u:");
+		wpabuf_put_data(buf, action, action_len);
+		wpabuf_put_str(buf, "Response>\n");
+		wpabuf_put_str(buf, soap_postfix);
+	} else {
+		/* Error case */
+		wpabuf_put_str(buf, soap_prefix);
+		wpabuf_put_str(buf, soap_error_prefix);
+		wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
+		wpabuf_put_str(buf, soap_error_postfix);
+		wpabuf_put_str(buf, soap_postfix);
+	}
+	os_free(replydata);
+
+	/* Now patch in the content length at the end */
+	if (body_start && put_length_here) {
+		int body_length = (char *) wpabuf_put(buf, 0) - body_start;
+		char len_buf[10];
+		os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
+		os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
+	}
+
+	http_request_send_and_deinit(req, buf);
+}
+
+
+static const char * web_get_action(struct http_request *req,
+				   size_t *action_len)
+{
+	const char *match;
+	int match_len;
+	char *b;
+	char *action;
+
+	*action_len = 0;
+	/* The SOAPAction line of the header tells us what we want to do */
+	b = http_request_get_hdr_line(req, "SOAPAction:");
+	if (b == NULL)
+		return NULL;
+	if (*b == '"')
+		b++;
+	else
+		return NULL;
+	match = urn_wfawlanconfig;
+	match_len = os_strlen(urn_wfawlanconfig) - 1;
+	if (os_strncasecmp(b, match, match_len))
+		return NULL;
+	b += match_len;
+	/* skip over version */
+	while (isgraph(*b) && *b != '#')
+		b++;
+	if (*b != '#')
+		return NULL;
+	b++;
+	/* Following the sharp(#) should be the action and a double quote */
+	action = b;
+	while (isgraph(*b) && *b != '"')
+		b++;
+	if (*b != '"')
+		return NULL;
+	*action_len = b - action;
+	return action;
+}
+
+
+/* Given that we have received a header w/ POST, act upon it
+ *
+ * Format of POST (case-insensitive):
+ *
+ * First line must be:
+ *      POST /<file> HTTP/1.1
+ * Since we don't do anything fancy we just ignore other lines.
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Connection: close
+ * Content-Type: text/xml
+ * Date: <rfc1123-date>
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_post(struct upnp_wps_device_sm *sm,
+				      struct sockaddr_in *cli,
+				      struct http_request *req,
+				      const char *filename)
+{
+	enum http_reply_code ret;
+	char *data = http_request_get_data(req); /* body of http msg */
+	const char *action = NULL;
+	size_t action_len = 0;
+	const char *replyname = NULL; /* argument name for the reply */
+	struct wpabuf *reply = NULL; /* data for the reply */
+
+	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
+			   filename);
+		ret = HTTP_NOT_FOUND;
+		goto bad;
+	}
+
+	ret = UPNP_INVALID_ACTION;
+	action = web_get_action(req, &action_len);
+	if (action == NULL)
+		goto bad;
+
+	if (!os_strncasecmp("GetDeviceInfo", action, action_len))
+		ret = web_process_get_device_info(sm, &reply, &replyname);
+	else if (!os_strncasecmp("PutMessage", action, action_len))
+		ret = web_process_put_message(sm, data, &reply, &replyname);
+	else if (!os_strncasecmp("PutWLANResponse", action, action_len))
+		ret = web_process_put_wlan_response(sm, data, &reply,
+						    &replyname);
+	else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
+		ret = web_process_set_selected_registrar(sm, cli, data, &reply,
+							 &replyname);
+	else
+		wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
+
+bad:
+	if (ret != HTTP_OK)
+		wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
+	web_connection_send_reply(req, ret, action, action_len, reply,
+				  replyname);
+	wpabuf_free(reply);
+}
+
+
+/* Given that we have received a header w/ SUBSCRIBE, act upon it
+ *
+ * Format of SUBSCRIBE (case-insensitive):
+ *
+ * First line must be:
+ *      SUBSCRIBE /wps_event HTTP/1.1
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Server: xx, UPnP/1.0, xx
+ * SID: uuid:xxxxxxxxx
+ * Timeout: Second-<n>
+ * Content-Length: 0
+ * Date: xxxx
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
+					   struct http_request *req,
+					   const char *filename)
+{
+	struct wpabuf *buf;
+	char *b;
+	char *hdr = http_request_get_hdr(req);
+	char *h;
+	char *match;
+	int match_len;
+	char *end;
+	int len;
+	int got_nt = 0;
+	u8 uuid[UUID_LEN];
+	int got_uuid = 0;
+	char *callback_urls = NULL;
+	struct subscription *s = NULL;
+	enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
+
+	buf = wpabuf_alloc(1000);
+	if (buf == NULL) {
+		http_request_deinit(req);
+		return;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
+			  (u8 *) hdr, os_strlen(hdr));
+
+	/* Parse/validate headers */
+	h = hdr;
+	/* First line: SUBSCRIBE /wps_event HTTP/1.1
+	 * has already been parsed.
+	 */
+	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
+		ret = HTTP_PRECONDITION_FAILED;
+		goto error;
+	}
+	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
+	end = os_strchr(h, '\n');
+
+	while (end) {
+		/* Option line by option line */
+		h = end + 1;
+		end = os_strchr(h, '\n');
+		if (end == NULL)
+			break; /* no unterminated lines allowed */
+
+		/* NT assures that it is our type of subscription;
+		 * not used for a renewal.
+		 **/
+		match = "NT:";
+		match_len = os_strlen(match);
+		if (os_strncasecmp(h, match, match_len) == 0) {
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			match = "upnp:event";
+			match_len = os_strlen(match);
+			if (os_strncasecmp(h, match, match_len) != 0) {
+				ret = HTTP_BAD_REQUEST;
+				goto error;
+			}
+			got_nt = 1;
+			continue;
+		}
+		/* HOST should refer to us */
+#if 0
+		match = "HOST:";
+		match_len = os_strlen(match);
+		if (os_strncasecmp(h, match, match_len) == 0) {
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			.....
+		}
+#endif
+		/* CALLBACK gives one or more URLs for NOTIFYs
+		 * to be sent as a result of the subscription.
+		 * Each URL is enclosed in angle brackets.
+		 */
+		match = "CALLBACK:";
+		match_len = os_strlen(match);
+		if (os_strncasecmp(h, match, match_len) == 0) {
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			len = end - h;
+			os_free(callback_urls);
+			callback_urls = dup_binstr(h, len);
+			if (callback_urls == NULL) {
+				ret = HTTP_INTERNAL_SERVER_ERROR;
+				goto error;
+			}
+			if (len > 0 && callback_urls[len - 1] == '\r')
+				callback_urls[len - 1] = '\0';
+			continue;
+		}
+		/* SID is only for renewal */
+		match = "SID:";
+		match_len = os_strlen(match);
+		if (os_strncasecmp(h, match, match_len) == 0) {
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			match = "uuid:";
+			match_len = os_strlen(match);
+			if (os_strncasecmp(h, match, match_len) != 0) {
+				ret = HTTP_BAD_REQUEST;
+				goto error;
+			}
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			if (uuid_str2bin(h, uuid)) {
+				ret = HTTP_BAD_REQUEST;
+				goto error;
+			}
+			got_uuid = 1;
+			continue;
+		}
+		/* TIMEOUT is requested timeout, but apparently we can
+		 * just ignore this.
+		 */
+	}
+
+	if (got_uuid) {
+		/* renewal */
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
+		if (callback_urls) {
+			ret = HTTP_BAD_REQUEST;
+			goto error;
+		}
+		s = subscription_renew(sm, uuid);
+		if (s == NULL) {
+			char str[80];
+			uuid_bin2str(uuid, str, sizeof(str));
+			wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
+				   "SID %s", str);
+			ret = HTTP_PRECONDITION_FAILED;
+			goto error;
+		}
+	} else if (callback_urls) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
+		if (!got_nt) {
+			ret = HTTP_PRECONDITION_FAILED;
+			goto error;
+		}
+		s = subscription_start(sm, callback_urls);
+		if (s == NULL) {
+			ret = HTTP_INTERNAL_SERVER_ERROR;
+			goto error;
+		}
+	} else {
+		ret = HTTP_PRECONDITION_FAILED;
+		goto error;
+	}
+
+	/* success */
+	http_put_reply_code(buf, HTTP_OK);
+	wpabuf_put_str(buf, http_server_hdr);
+	wpabuf_put_str(buf, http_connection_close);
+	wpabuf_put_str(buf, "Content-Length: 0\r\n");
+	wpabuf_put_str(buf, "SID: uuid:");
+	/* subscription id */
+	b = wpabuf_put(buf, 0);
+	uuid_bin2str(s->uuid, b, 80);
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
+	wpabuf_put(buf, os_strlen(b));
+	wpabuf_put_str(buf, "\r\n");
+	wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
+	http_put_date(buf);
+	/* And empty line to terminate header: */
+	wpabuf_put_str(buf, "\r\n");
+
+	os_free(callback_urls);
+	http_request_send_and_deinit(req, buf);
+	return;
+
+error:
+	/* Per UPnP spec:
+	* Errors
+	* Incompatible headers
+	*   400 Bad Request. If SID header and one of NT or CALLBACK headers
+	*     are present, the publisher must respond with HTTP error
+	*     400 Bad Request.
+	* Missing or invalid CALLBACK
+	*   412 Precondition Failed. If CALLBACK header is missing or does not
+	*     contain a valid HTTP URL, the publisher must respond with HTTP
+	*     error 412 Precondition Failed.
+	* Invalid NT
+	*   412 Precondition Failed. If NT header does not equal upnp:event,
+	*     the publisher must respond with HTTP error 412 Precondition
+	*     Failed.
+	* [For resubscription, use 412 if unknown uuid].
+	* Unable to accept subscription
+	*   5xx. If a publisher is not able to accept a subscription (such as
+	*     due to insufficient resources), it must respond with a
+	*     HTTP 500-series error code.
+	*   599 Too many subscriptions (not a standard HTTP error)
+	*/
+	wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
+	http_put_empty(buf, ret);
+	http_request_send_and_deinit(req, buf);
+	os_free(callback_urls);
+}
+
+
+/* Given that we have received a header w/ UNSUBSCRIBE, act upon it
+ *
+ * Format of UNSUBSCRIBE (case-insensitive):
+ *
+ * First line must be:
+ *      UNSUBSCRIBE /wps_event HTTP/1.1
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Content-Length: 0
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
+					     struct http_request *req,
+					     const char *filename)
+{
+	struct wpabuf *buf;
+	char *hdr = http_request_get_hdr(req);
+	char *h;
+	char *match;
+	int match_len;
+	char *end;
+	u8 uuid[UUID_LEN];
+	int got_uuid = 0;
+	struct subscription *s = NULL;
+	enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
+
+	/* Parse/validate headers */
+	h = hdr;
+	/* First line: UNSUBSCRIBE /wps_event HTTP/1.1
+	 * has already been parsed.
+	 */
+	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
+		ret = HTTP_PRECONDITION_FAILED;
+		goto send_msg;
+	}
+	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
+	end = os_strchr(h, '\n');
+
+	while (end) {
+		/* Option line by option line */
+		h = end + 1;
+		end = os_strchr(h, '\n');
+		if (end == NULL)
+			break; /* no unterminated lines allowed */
+
+		/* HOST should refer to us */
+#if 0
+		match = "HOST:";
+		match_len = os_strlen(match);
+		if (os_strncasecmp(h, match, match_len) == 0) {
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			.....
+		}
+#endif
+		match = "SID:";
+		match_len = os_strlen(match);
+		if (os_strncasecmp(h, match, match_len) == 0) {
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			match = "uuid:";
+			match_len = os_strlen(match);
+			if (os_strncasecmp(h, match, match_len) != 0) {
+				ret = HTTP_BAD_REQUEST;
+				goto send_msg;
+			}
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			if (uuid_str2bin(h, uuid)) {
+				ret = HTTP_BAD_REQUEST;
+				goto send_msg;
+			}
+			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) {
+		char str[80];
+
+		uuid_bin2str(uuid, str, sizeof(str));
+
+		s = subscription_find(sm, uuid);
+		if (s) {
+			struct subscr_addr *sa;
+			sa = dl_list_first(&s->addr_list, struct subscr_addr,
+					   list);
+			wpa_printf(MSG_DEBUG,
+				   "WPS UPnP: Unsubscribing %p (SID %s) %s",
+				   s, str, (sa && sa->domain_and_port) ?
+				   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 (SID %s)",
+				   str);
+			ret = HTTP_PRECONDITION_FAILED;
+			goto send_msg;
+		}
+	} else {
+		wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
+			   "found)");
+		ret = HTTP_PRECONDITION_FAILED;
+		goto send_msg;
+	}
+
+	ret = HTTP_OK;
+
+send_msg:
+	buf = wpabuf_alloc(200);
+	if (buf == NULL) {
+		http_request_deinit(req);
+		return;
+	}
+	http_put_empty(buf, ret);
+	http_request_send_and_deinit(req, buf);
+}
+
+
+/* Send error in response to unknown requests */
+static void web_connection_unimplemented(struct http_request *req)
+{
+	struct wpabuf *buf;
+	buf = wpabuf_alloc(200);
+	if (buf == NULL) {
+		http_request_deinit(req);
+		return;
+	}
+	http_put_empty(buf, HTTP_UNIMPLEMENTED);
+	http_request_send_and_deinit(req, buf);
+}
+
+
+
+/* Called when we have gotten an apparently valid http request.
+ */
+static void web_connection_check_data(void *ctx, struct http_request *req)
+{
+	struct upnp_wps_device_sm *sm = ctx;
+	enum httpread_hdr_type htype = http_request_get_type(req);
+	char *filename = http_request_get_uri(req);
+	struct sockaddr_in *cli = http_request_get_cli_addr(req);
+
+	if (!filename) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
+		http_request_deinit(req);
+		return;
+	}
+	/* Trim leading slashes from filename */
+	while (*filename == '/')
+		filename++;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
+		   htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
+
+	switch (htype) {
+	case HTTPREAD_HDR_TYPE_GET:
+		web_connection_parse_get(sm, req, filename);
+		break;
+	case HTTPREAD_HDR_TYPE_POST:
+		web_connection_parse_post(sm, cli, req, filename);
+		break;
+	case HTTPREAD_HDR_TYPE_SUBSCRIBE:
+		web_connection_parse_subscribe(sm, req, filename);
+		break;
+	case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
+		web_connection_parse_unsubscribe(sm, req, filename);
+		break;
+
+		/* We are not required to support M-POST; just plain
+		 * POST is supposed to work, so we only support that.
+		 * If for some reason we need to support M-POST, it is
+		 * mostly the same as POST, with small differences.
+		 */
+	default:
+		/* Send 501 for anything else */
+		web_connection_unimplemented(req);
+		break;
+	}
+}
+
+
+/*
+ * Listening for web connections
+ * We have a single TCP listening port, and hand off connections as we get
+ * them.
+ */
+
+void web_listener_stop(struct upnp_wps_device_sm *sm)
+{
+	http_server_deinit(sm->web_srv);
+	sm->web_srv = NULL;
+}
+
+
+int web_listener_start(struct upnp_wps_device_sm *sm)
+{
+	struct in_addr addr;
+	addr.s_addr = sm->ip_addr;
+	sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
+				       sm);
+	if (sm->web_srv == NULL) {
+		web_listener_stop(sm);
+		return -1;
+	}
+	sm->web_port = http_server_get_port(sm->web_srv);
+
+	return 0;
+}
diff --git a/hostap/src/wps/wps_validate.c b/hostap/src/wps/wps_validate.c
new file mode 100644
index 0000000..267b565
--- /dev/null
+++ b/hostap/src/wps/wps_validate.c
@@ -0,0 +1,1977 @@
+/*
+ * Wi-Fi Protected Setup - Strict protocol validation routines
+ * Copyright (c) 2010, Atheros Communications, 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 "wps_i.h"
+#include "wps.h"
+
+
+#ifndef WPS_STRICT_ALL
+#define WPS_STRICT_WPS2
+#endif /* WPS_STRICT_ALL */
+
+
+static int wps_validate_version(const u8 *version, int mandatory)
+{
+	if (version == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Version attribute "
+				   "missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (*version != 0x10) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version attribute "
+			   "value 0x%x", *version);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_version2(const u8 *version2, int mandatory)
+{
+	if (version2 == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Version2 attribute "
+				   "missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (*version2 < 0x20) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version2 attribute "
+			   "value 0x%x", *version2);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_request_type(const u8 *request_type, int mandatory)
+{
+	if (request_type == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Request Type "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (*request_type > 0x03) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request Type "
+			   "attribute value 0x%x", *request_type);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_response_type(const u8 *response_type, int mandatory)
+{
+	if (response_type == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Response Type "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (*response_type > 0x03) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Response Type "
+			   "attribute value 0x%x", *response_type);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int valid_config_methods(u16 val, int wps2)
+{
+	if (wps2) {
+		if ((val & 0x6000) && !(val & WPS_CONFIG_DISPLAY)) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual "
+				   "Display flag without old Display flag "
+				   "set");
+			return 0;
+		}
+		if (!(val & 0x6000) && (val & WPS_CONFIG_DISPLAY)) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Display flag "
+				   "without Physical/Virtual Display flag");
+			return 0;
+		}
+		if ((val & 0x0600) && !(val & WPS_CONFIG_PUSHBUTTON)) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual "
+				   "PushButton flag without old PushButton "
+				   "flag set");
+			return 0;
+		}
+		if (!(val & 0x0600) && (val & WPS_CONFIG_PUSHBUTTON)) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: PushButton flag "
+				   "without Physical/Virtual PushButton flag");
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+
+static int wps_validate_config_methods(const u8 *config_methods, int wps2,
+				       int mandatory)
+{
+	u16 val;
+
+	if (config_methods == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Configuration "
+				   "Methods attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+
+	val = WPA_GET_BE16(config_methods);
+	if (!valid_config_methods(val, wps2)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration "
+			   "Methods attribute value 0x%04x", val);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_ap_config_methods(const u8 *config_methods, int wps2,
+					  int mandatory)
+{
+	u16 val;
+
+	if (wps_validate_config_methods(config_methods, wps2, mandatory) < 0)
+		return -1;
+	if (config_methods == NULL)
+		return 0;
+	val = WPA_GET_BE16(config_methods);
+	if (val & WPS_CONFIG_PUSHBUTTON) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration "
+			   "Methods attribute value 0x%04x in AP info "
+			   "(PushButton not allowed for registering new ER)",
+			   val);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_uuid_e(const u8 *uuid_e, int mandatory)
+{
+	if (uuid_e == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: UUID-E "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_uuid_r(const u8 *uuid_r, int mandatory)
+{
+	if (uuid_r == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: UUID-R "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_primary_dev_type(const u8 *primary_dev_type,
+					 int mandatory)
+{
+	if (primary_dev_type == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Primary Device Type "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_rf_bands(const u8 *rf_bands, int mandatory)
+{
+	if (rf_bands == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: RF Bands "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (*rf_bands != WPS_RF_24GHZ && *rf_bands != WPS_RF_50GHZ &&
+	    *rf_bands != WPS_RF_60GHZ &&
+	    *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ | WPS_RF_60GHZ) &&
+	    *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Rf Bands "
+			   "attribute value 0x%x", *rf_bands);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_assoc_state(const u8 *assoc_state, int mandatory)
+{
+	u16 val;
+	if (assoc_state == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Association State "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	val = WPA_GET_BE16(assoc_state);
+	if (val > 4) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Association State "
+			   "attribute value 0x%04x", val);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_config_error(const u8 *config_error, int mandatory)
+{
+	u16 val;
+
+	if (config_error == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Configuration Error "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	val = WPA_GET_BE16(config_error);
+	if (val > 20) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error "
+			   "attribute value 0x%04x", val);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_dev_password_id(const u8 *dev_password_id,
+					int mandatory)
+{
+	u16 val;
+
+	if (dev_password_id == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Device Password ID "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	val = WPA_GET_BE16(dev_password_id);
+	if (val >= 0x0008 && val <= 0x000f) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID "
+			   "attribute value 0x%04x", val);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_manufacturer(const u8 *manufacturer, size_t len,
+				     int mandatory)
+{
+	if (manufacturer == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Manufacturer "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (len > 0 && manufacturer[len - 1] == 0) {
+		wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Manufacturer "
+			   "attribute value", manufacturer, len);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_model_name(const u8 *model_name, size_t len,
+				   int mandatory)
+{
+	if (model_name == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Model Name "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (len > 0 && model_name[len - 1] == 0) {
+		wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Name "
+			   "attribute value", model_name, len);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_model_number(const u8 *model_number, size_t len,
+				     int mandatory)
+{
+	if (model_number == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Model Number "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (len > 0 && model_number[len - 1] == 0) {
+		wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Number "
+			   "attribute value", model_number, len);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_serial_number(const u8 *serial_number, size_t len,
+				      int mandatory)
+{
+	if (serial_number == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Serial Number "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (len > 0 && serial_number[len - 1] == 0) {
+		wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Serial "
+				  "Number attribute value",
+				  serial_number, len);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_dev_name(const u8 *dev_name, size_t len,
+				 int mandatory)
+{
+	if (dev_name == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Device Name "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (len > 0 && dev_name[len - 1] == 0) {
+		wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Device Name "
+			   "attribute value", dev_name, len);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_request_to_enroll(const u8 *request_to_enroll,
+					  int mandatory)
+{
+	if (request_to_enroll == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Request to Enroll "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (*request_to_enroll > 0x01) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request to Enroll "
+			   "attribute value 0x%x", *request_to_enroll);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_req_dev_type(const u8 *req_dev_type[], size_t num,
+				     int mandatory)
+{
+	if (num == 0) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Requested Device "
+				   "Type attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_wps_state(const u8 *wps_state, int mandatory)
+{
+	if (wps_state == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Wi-Fi Protected "
+				   "Setup State attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (*wps_state != WPS_STATE_NOT_CONFIGURED &&
+	    *wps_state != WPS_STATE_CONFIGURED) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Wi-Fi Protected "
+			   "Setup State attribute value 0x%x", *wps_state);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_ap_setup_locked(const u8 *ap_setup_locked,
+					int mandatory)
+{
+	if (ap_setup_locked == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: AP Setup Locked "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (*ap_setup_locked > 1) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid AP Setup Locked "
+			   "attribute value 0x%x", *ap_setup_locked);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_selected_registrar(const u8 *selected_registrar,
+					   int mandatory)
+{
+	if (selected_registrar == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (*selected_registrar > 1) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar "
+			   "attribute value 0x%x", *selected_registrar);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_sel_reg_config_methods(const u8 *config_methods,
+					       int wps2, int mandatory)
+{
+	u16 val;
+
+	if (config_methods == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar "
+				   "Configuration Methods attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+
+	val = WPA_GET_BE16(config_methods);
+	if (!valid_config_methods(val, wps2)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar "
+			   "Configuration Methods attribute value 0x%04x",
+			   val);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_authorized_macs(const u8 *authorized_macs, size_t len,
+					int mandatory)
+{
+	if (authorized_macs == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Authorized MACs "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (len > 30 && (len % ETH_ALEN) != 0) {
+		wpa_hexdump(MSG_INFO, "WPS-STRICT: Invalid Authorized "
+			    "MACs attribute value", authorized_macs, len);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_msg_type(const u8 *msg_type, int mandatory)
+{
+	if (msg_type == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Message Type "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (*msg_type < WPS_Beacon || *msg_type > WPS_WSC_DONE) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Message Type "
+			   "attribute value 0x%x", *msg_type);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_mac_addr(const u8 *mac_addr, int mandatory)
+{
+	if (mac_addr == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: MAC Address "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (mac_addr[0] & 0x01) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid MAC Address "
+			   "attribute value " MACSTR, MAC2STR(mac_addr));
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_enrollee_nonce(const u8 *enrollee_nonce, int mandatory)
+{
+	if (enrollee_nonce == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Enrollee Nonce "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_registrar_nonce(const u8 *registrar_nonce,
+					int mandatory)
+{
+	if (registrar_nonce == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Registrar Nonce "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_public_key(const u8 *public_key, size_t len,
+				   int mandatory)
+{
+	if (public_key == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Public Key "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (len != 192) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Public Key "
+			   "attribute length %d", (int) len);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int num_bits_set(u16 val)
+{
+	int c;
+	for (c = 0; val; c++)
+		val &= val - 1;
+	return c;
+}
+
+
+static int wps_validate_auth_type_flags(const u8 *flags, int mandatory)
+{
+	u16 val;
+
+	if (flags == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type "
+				   "Flags attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	val = WPA_GET_BE16(flags);
+	if ((val & ~WPS_AUTH_TYPES) || !(val & WPS_AUTH_WPA2PSK)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type "
+			   "Flags attribute value 0x%04x", val);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_auth_type(const u8 *type, int mandatory)
+{
+	u16 val;
+
+	if (type == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	val = WPA_GET_BE16(type);
+	if ((val & ~WPS_AUTH_TYPES) || val == 0 ||
+	    (num_bits_set(val) > 1 &&
+	     val != (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type "
+			   "attribute value 0x%04x", val);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_encr_type_flags(const u8 *flags, int mandatory)
+{
+	u16 val;
+
+	if (flags == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type "
+				   "Flags attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	val = WPA_GET_BE16(flags);
+	if ((val & ~WPS_ENCR_TYPES) || !(val & WPS_ENCR_AES)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type "
+			   "Flags attribute value 0x%04x", val);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_encr_type(const u8 *type, int mandatory)
+{
+	u16 val;
+
+	if (type == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	val = WPA_GET_BE16(type);
+	if ((val & ~WPS_ENCR_TYPES) || val == 0 ||
+	    (num_bits_set(val) > 1 && val != (WPS_ENCR_TKIP | WPS_ENCR_AES))) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type "
+			   "attribute value 0x%04x", val);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_conn_type_flags(const u8 *flags, int mandatory)
+{
+	if (flags == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Connection Type "
+				   "Flags attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if ((*flags & ~(WPS_CONN_ESS | WPS_CONN_IBSS)) ||
+	    !(*flags & WPS_CONN_ESS)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Connection Type "
+			   "Flags attribute value 0x%02x", *flags);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_os_version(const u8 *os_version, int mandatory)
+{
+	if (os_version == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: OS Version "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_authenticator(const u8 *authenticator, int mandatory)
+{
+	if (authenticator == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Authenticator "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_e_hash1(const u8 *hash, int mandatory)
+{
+	if (hash == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash1 "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_e_hash2(const u8 *hash, int mandatory)
+{
+	if (hash == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash2 "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_r_hash1(const u8 *hash, int mandatory)
+{
+	if (hash == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash1 "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_r_hash2(const u8 *hash, int mandatory)
+{
+	if (hash == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash2 "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_encr_settings(const u8 *encr_settings, size_t len,
+				   int mandatory)
+{
+	if (encr_settings == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Encrypted Settings "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (len < 16) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encrypted Settings "
+			   "attribute length %d", (int) len);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_settings_delay_time(const u8 *delay, int mandatory)
+{
+	if (delay == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Settings Delay Time "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_r_snonce1(const u8 *nonce, int mandatory)
+{
+	if (nonce == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce1 "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_r_snonce2(const u8 *nonce, int mandatory)
+{
+	if (nonce == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce2 "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_e_snonce1(const u8 *nonce, int mandatory)
+{
+	if (nonce == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce1 "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_e_snonce2(const u8 *nonce, int mandatory)
+{
+	if (nonce == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce2 "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_key_wrap_auth(const u8 *auth, int mandatory)
+{
+	if (auth == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Key Wrap "
+				   "Authenticator attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_ssid(const u8 *ssid, size_t ssid_len, int mandatory)
+{
+	if (ssid == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: SSID "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (ssid_len == 0 || ssid[ssid_len - 1] == 0) {
+		wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid SSID "
+				  "attribute value", ssid, ssid_len);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_network_key_index(const u8 *idx, int mandatory)
+{
+	if (idx == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Network Key Index "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_network_idx(const u8 *idx, int mandatory)
+{
+	if (idx == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Network Index "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+
+static int wps_validate_network_key(const u8 *key, size_t key_len,
+				    const u8 *encr_type, int mandatory)
+{
+	if (key == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Network Key "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (((encr_type == NULL || WPA_GET_BE16(encr_type) != WPS_ENCR_WEP) &&
+	     key_len > 8 && key_len < 64 && key[key_len - 1] == 0) ||
+	    key_len > 64) {
+		wpa_hexdump_ascii_key(MSG_INFO, "WPS-STRICT: Invalid Network "
+				      "Key attribute value", key, key_len);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_network_key_shareable(const u8 *val, int mandatory)
+{
+	if (val == NULL) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Network Key "
+				   "Shareable attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+	if (*val > 1) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Network Key "
+			   "Shareable attribute value 0x%x", *val);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wps_validate_cred(const u8 *cred, size_t len)
+{
+	struct wps_parse_attr attr;
+	struct wpabuf buf;
+
+	if (cred == NULL)
+		return -1;
+	wpabuf_set(&buf, cred, len);
+	if (wps_parse_msg(&buf, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse Credential");
+		return -1;
+	}
+
+	if (wps_validate_network_idx(attr.network_idx, 1) ||
+	    wps_validate_ssid(attr.ssid, attr.ssid_len, 1) ||
+	    wps_validate_auth_type(attr.auth_type, 1) ||
+	    wps_validate_encr_type(attr.encr_type, 1) ||
+	    wps_validate_network_key_index(attr.network_key_idx, 0) ||
+	    wps_validate_network_key(attr.network_key, attr.network_key_len,
+				     attr.encr_type, 1) ||
+	    wps_validate_mac_addr(attr.mac_addr, 1) ||
+	    wps_validate_network_key_shareable(attr.network_key_shareable, 0))
+	{
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Credential");
+		return -1;
+	}
+
+
+	return 0;
+}
+
+
+static int wps_validate_credential(const u8 *cred[], size_t len[], size_t num,
+				   int mandatory)
+{
+	size_t i;
+
+	if (num == 0) {
+		if (mandatory) {
+			wpa_printf(MSG_INFO, "WPS-STRICT: Credential "
+				   "attribute missing");
+			return -1;
+		}
+		return 0;
+	}
+
+	for (i = 0; i < num; i++) {
+		if (wps_validate_cred(cred[i], len[i]) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+int wps_validate_beacon(const struct wpabuf *wps_ie)
+{
+	struct wps_parse_attr attr;
+	int wps2, sel_reg;
+
+	if (wps_ie == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in Beacon frame");
+		return -1;
+	}
+	if (wps_parse_msg(wps_ie, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+			   "Beacon frame");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	sel_reg = attr.selected_registrar != NULL &&
+		*attr.selected_registrar != 0;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_wps_state(attr.wps_state, 1) ||
+	    wps_validate_ap_setup_locked(attr.ap_setup_locked, 0) ||
+	    wps_validate_selected_registrar(attr.selected_registrar, 0) ||
+	    wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
+	    wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
+						wps2, sel_reg) ||
+	    wps_validate_uuid_e(attr.uuid_e, 0) ||
+	    wps_validate_rf_bands(attr.rf_bands, 0) ||
+	    wps_validate_version2(attr.version2, wps2) ||
+	    wps_validate_authorized_macs(attr.authorized_macs,
+					 attr.authorized_macs_len, 0)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Beacon frame");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
+				   const u8 *addr)
+{
+	struct wps_parse_attr attr;
+	int wps2, sel_reg;
+
+	if (wps_ie == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+			   "%sProbe Response frame", probe ? "" : "Beacon/");
+		return -1;
+	}
+	if (wps_parse_msg(wps_ie, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+			   "%sProbe Response frame", probe ? "" : "Beacon/");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	sel_reg = attr.selected_registrar != NULL &&
+		*attr.selected_registrar != 0;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_wps_state(attr.wps_state, 1) ||
+	    wps_validate_ap_setup_locked(attr.ap_setup_locked, 0) ||
+	    wps_validate_selected_registrar(attr.selected_registrar, 0) ||
+	    wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
+	    wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
+						wps2, sel_reg) ||
+	    wps_validate_response_type(attr.response_type, probe) ||
+	    wps_validate_uuid_e(attr.uuid_e, probe) ||
+	    wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+				      probe) ||
+	    wps_validate_model_name(attr.model_name, attr.model_name_len,
+				    probe) ||
+	    wps_validate_model_number(attr.model_number, attr.model_number_len,
+				      probe) ||
+	    wps_validate_serial_number(attr.serial_number,
+				       attr.serial_number_len, probe) ||
+	    wps_validate_primary_dev_type(attr.primary_dev_type, probe) ||
+	    wps_validate_dev_name(attr.dev_name, attr.dev_name_len, probe) ||
+	    wps_validate_ap_config_methods(attr.config_methods, wps2, probe) ||
+	    wps_validate_rf_bands(attr.rf_bands, 0) ||
+	    wps_validate_version2(attr.version2, wps2) ||
+	    wps_validate_authorized_macs(attr.authorized_macs,
+					 attr.authorized_macs_len, 0)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid %sProbe Response "
+			   "frame from " MACSTR, probe ? "" : "Beacon/",
+			   MAC2STR(addr));
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (wps_ie == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+			   "Probe Request frame");
+		return -1;
+	}
+	if (wps_parse_msg(wps_ie, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+			   "Probe Request frame");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_request_type(attr.request_type, 1) ||
+	    wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+	    wps_validate_uuid_e(attr.uuid_e, attr.uuid_r == NULL) ||
+	    wps_validate_uuid_r(attr.uuid_r, attr.uuid_e == NULL) ||
+	    wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+	    wps_validate_rf_bands(attr.rf_bands, 1) ||
+	    wps_validate_assoc_state(attr.assoc_state, 1) ||
+	    wps_validate_config_error(attr.config_error, 1) ||
+	    wps_validate_dev_password_id(attr.dev_password_id, 1) ||
+	    wps_validate_version2(attr.version2, wps2) ||
+	    wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+				      wps2) ||
+	    wps_validate_model_name(attr.model_name, attr.model_name_len,
+				    wps2) ||
+	    wps_validate_model_number(attr.model_number, attr.model_number_len,
+				      wps2) ||
+	    wps_validate_dev_name(attr.dev_name, attr.dev_name_len, wps2) ||
+	    wps_validate_request_to_enroll(attr.request_to_enroll, 0) ||
+	    wps_validate_req_dev_type(attr.req_dev_type, attr.num_req_dev_type,
+				      0)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Probe Request "
+			   "frame from " MACSTR, MAC2STR(addr));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int wps_validate_assoc_req(const struct wpabuf *wps_ie)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (wps_ie == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+			   "(Re)Association Request frame");
+		return -1;
+	}
+	if (wps_parse_msg(wps_ie, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+			   "(Re)Association Request frame");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_request_type(attr.request_type, 1) ||
+	    wps_validate_version2(attr.version2, wps2)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association "
+			   "Request frame");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (wps_ie == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+			   "(Re)Association Response frame");
+		return -1;
+	}
+	if (wps_parse_msg(wps_ie, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+			   "(Re)Association Response frame");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_response_type(attr.response_type, 1) ||
+	    wps_validate_version2(attr.version2, wps2)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association "
+			   "Response frame");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int wps_validate_m1(const struct wpabuf *tlvs)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M1");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in M1");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_msg_type(attr.msg_type, 1) ||
+	    wps_validate_uuid_e(attr.uuid_e, 1) ||
+	    wps_validate_mac_addr(attr.mac_addr, 1) ||
+	    wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+	    wps_validate_public_key(attr.public_key, attr.public_key_len, 1) ||
+	    wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
+	    wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
+	    wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
+	    wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+	    wps_validate_wps_state(attr.wps_state, 1) ||
+	    wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+				      1) ||
+	    wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
+	    wps_validate_model_number(attr.model_number, attr.model_number_len,
+				      1) ||
+	    wps_validate_serial_number(attr.serial_number,
+				       attr.serial_number_len, 1) ||
+	    wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+	    wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
+	    wps_validate_rf_bands(attr.rf_bands, 1) ||
+	    wps_validate_assoc_state(attr.assoc_state, 1) ||
+	    wps_validate_dev_password_id(attr.dev_password_id, 1) ||
+	    wps_validate_config_error(attr.config_error, 1) ||
+	    wps_validate_os_version(attr.os_version, 1) ||
+	    wps_validate_version2(attr.version2, wps2) ||
+	    wps_validate_request_to_enroll(attr.request_to_enroll, 0)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M1");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_m2(const struct wpabuf *tlvs)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in M2");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_msg_type(attr.msg_type, 1) ||
+	    wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+	    wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+	    wps_validate_uuid_r(attr.uuid_r, 1) ||
+	    wps_validate_public_key(attr.public_key, attr.public_key_len, 1) ||
+	    wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
+	    wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
+	    wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
+	    wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+	    wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+				      1) ||
+	    wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
+	    wps_validate_model_number(attr.model_number, attr.model_number_len,
+				      1) ||
+	    wps_validate_serial_number(attr.serial_number,
+				       attr.serial_number_len, 1) ||
+	    wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+	    wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
+	    wps_validate_rf_bands(attr.rf_bands, 1) ||
+	    wps_validate_assoc_state(attr.assoc_state, 1) ||
+	    wps_validate_config_error(attr.config_error, 1) ||
+	    wps_validate_dev_password_id(attr.dev_password_id, 1) ||
+	    wps_validate_os_version(attr.os_version, 1) ||
+	    wps_validate_version2(attr.version2, wps2) ||
+	    wps_validate_authenticator(attr.authenticator, 1)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_m2d(const struct wpabuf *tlvs)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2D");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in M2D");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_msg_type(attr.msg_type, 1) ||
+	    wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+	    wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+	    wps_validate_uuid_r(attr.uuid_r, 1) ||
+	    wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
+	    wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
+	    wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
+	    wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+	    wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+				      1) ||
+	    wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
+	    wps_validate_model_number(attr.model_number, attr.model_number_len,
+				      1) ||
+	    wps_validate_serial_number(attr.serial_number,
+				       attr.serial_number_len, 1) ||
+	    wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+	    wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
+	    wps_validate_rf_bands(attr.rf_bands, 1) ||
+	    wps_validate_assoc_state(attr.assoc_state, 1) ||
+	    wps_validate_config_error(attr.config_error, 1) ||
+	    wps_validate_os_version(attr.os_version, 1) ||
+	    wps_validate_version2(attr.version2, wps2)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2D");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_m3(const struct wpabuf *tlvs)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M3");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in M3");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_msg_type(attr.msg_type, 1) ||
+	    wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+	    wps_validate_e_hash1(attr.e_hash1, 1) ||
+	    wps_validate_e_hash2(attr.e_hash2, 1) ||
+	    wps_validate_version2(attr.version2, wps2) ||
+	    wps_validate_authenticator(attr.authenticator, 1)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M3");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_m4(const struct wpabuf *tlvs)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in M4");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_msg_type(attr.msg_type, 1) ||
+	    wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+	    wps_validate_r_hash1(attr.r_hash1, 1) ||
+	    wps_validate_r_hash2(attr.r_hash2, 1) ||
+	    wps_validate_encr_settings(attr.encr_settings,
+				       attr.encr_settings_len, 1) ||
+	    wps_validate_version2(attr.version2, wps2) ||
+	    wps_validate_authenticator(attr.authenticator, 1)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
+{
+	struct wps_parse_attr attr;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4 encrypted "
+			   "settings");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in M4 encrypted settings");
+		return -1;
+	}
+
+	if (wps_validate_r_snonce1(attr.r_snonce1, 1) ||
+	    wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4 encrypted "
+			   "settings");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_m5(const struct wpabuf *tlvs)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in M5");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_msg_type(attr.msg_type, 1) ||
+	    wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+	    wps_validate_encr_settings(attr.encr_settings,
+				       attr.encr_settings_len, 1) ||
+	    wps_validate_version2(attr.version2, wps2) ||
+	    wps_validate_authenticator(attr.authenticator, 1)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
+{
+	struct wps_parse_attr attr;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5 encrypted "
+			   "settings");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in M5 encrypted settings");
+		return -1;
+	}
+
+	if (wps_validate_e_snonce1(attr.e_snonce1, 1) ||
+	    wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5 encrypted "
+			   "settings");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_m6(const struct wpabuf *tlvs)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in M6");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_msg_type(attr.msg_type, 1) ||
+	    wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+	    wps_validate_encr_settings(attr.encr_settings,
+				       attr.encr_settings_len, 1) ||
+	    wps_validate_version2(attr.version2, wps2) ||
+	    wps_validate_authenticator(attr.authenticator, 1)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
+{
+	struct wps_parse_attr attr;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6 encrypted "
+			   "settings");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in M6 encrypted settings");
+		return -1;
+	}
+
+	if (wps_validate_r_snonce2(attr.r_snonce2, 1) ||
+	    wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6 encrypted "
+			   "settings");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_m7(const struct wpabuf *tlvs)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in M7");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_msg_type(attr.msg_type, 1) ||
+	    wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+	    wps_validate_encr_settings(attr.encr_settings,
+				       attr.encr_settings_len, 1) ||
+	    wps_validate_settings_delay_time(attr.settings_delay_time, 0) ||
+	    wps_validate_version2(attr.version2, wps2) ||
+	    wps_validate_authenticator(attr.authenticator, 1)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2)
+{
+	struct wps_parse_attr attr;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7 encrypted "
+			   "settings");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in M7 encrypted settings");
+		return -1;
+	}
+
+	if (wps_validate_e_snonce2(attr.e_snonce2, 1) ||
+	    wps_validate_ssid(attr.ssid, attr.ssid_len, !ap) ||
+	    wps_validate_mac_addr(attr.mac_addr, !ap) ||
+	    wps_validate_auth_type(attr.auth_type, !ap) ||
+	    wps_validate_encr_type(attr.encr_type, !ap) ||
+	    wps_validate_network_key_index(attr.network_key_idx, 0) ||
+	    wps_validate_network_key(attr.network_key, attr.network_key_len,
+				     attr.encr_type, !ap) ||
+	    wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7 encrypted "
+			   "settings");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_m8(const struct wpabuf *tlvs)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in M8");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_msg_type(attr.msg_type, 1) ||
+	    wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+	    wps_validate_encr_settings(attr.encr_settings,
+				       attr.encr_settings_len, 1) ||
+	    wps_validate_version2(attr.version2, wps2) ||
+	    wps_validate_authenticator(attr.authenticator, 1)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2)
+{
+	struct wps_parse_attr attr;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8 encrypted "
+			   "settings");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in M8 encrypted settings");
+		return -1;
+	}
+
+	if (wps_validate_ssid(attr.ssid, attr.ssid_len, ap) ||
+	    wps_validate_auth_type(attr.auth_type, ap) ||
+	    wps_validate_encr_type(attr.encr_type, ap) ||
+	    wps_validate_network_key_index(attr.network_key_idx, 0) ||
+	    wps_validate_mac_addr(attr.mac_addr, ap) ||
+	    wps_validate_credential(attr.cred, attr.cred_len, attr.num_cred,
+				    !ap) ||
+	    wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8 encrypted "
+			   "settings");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_wsc_ack(const struct wpabuf *tlvs)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_ACK");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in WSC_ACK");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_msg_type(attr.msg_type, 1) ||
+	    wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+	    wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+	    wps_validate_version2(attr.version2, wps2)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_ACK");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_wsc_nack(const struct wpabuf *tlvs)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_NACK");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in WSC_NACK");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_msg_type(attr.msg_type, 1) ||
+	    wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+	    wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+	    wps_validate_config_error(attr.config_error, 1) ||
+	    wps_validate_version2(attr.version2, wps2)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_NACK");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_wsc_done(const struct wpabuf *tlvs)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_Done");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in WSC_Done");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_msg_type(attr.msg_type, 1) ||
+	    wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+	    wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+	    wps_validate_version2(attr.version2, wps2)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_Done");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
+
+
+int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs)
+{
+	struct wps_parse_attr attr;
+	int wps2;
+	int sel_reg;
+
+	if (tlvs == NULL) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in "
+			   "SetSelectedRegistrar");
+		return -1;
+	}
+	if (wps_parse_msg(tlvs, &attr) < 0) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+			   "in SetSelectedRegistrar");
+		return -1;
+	}
+
+	wps2 = attr.version2 != NULL;
+	sel_reg = attr.selected_registrar != NULL &&
+		*attr.selected_registrar != 0;
+	if (wps_validate_version(attr.version, 1) ||
+	    wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
+	    wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
+						wps2, sel_reg) ||
+	    wps_validate_version2(attr.version2, wps2) ||
+	    wps_validate_authorized_macs(attr.authorized_macs,
+					 attr.authorized_macs_len, wps2) ||
+	    wps_validate_uuid_r(attr.uuid_r, wps2)) {
+		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid "
+			   "SetSelectedRegistrar");
+#ifdef WPS_STRICT_WPS2
+		if (wps2)
+			return -1;
+#else /* WPS_STRICT_WPS2 */
+		return -1;
+#endif /* WPS_STRICT_WPS2 */
+	}
+
+	return 0;
+}
diff --git a/hostap/tests/Makefile b/hostap/tests/Makefile
new file mode 100644
index 0000000..782396a
--- /dev/null
+++ b/hostap/tests/Makefile
@@ -0,0 +1,102 @@
+TESTS=test-base64 test-md4 test-milenage \
+	test-rsa-sig-ver \
+	test-sha1 \
+	test-sha256 test-aes test-asn1 test-x509 test-x509v3 test-list test-rc4
+
+all: $(TESTS)
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+CFLAGS += -I../src
+CFLAGS += -I../src/utils
+
+SLIBS = ../src/utils/libutils.a
+
+DLIBS = ../src/crypto/libcrypto.a \
+	../src/tls/libtls.a
+
+LIBS = $(SLIBS) $(DLIBS)
+LLIBS = -Wl,--start-group $(DLIBS) -Wl,--end-group $(SLIBS)
+
+# glibc < 2.17 needs -lrt for clock_gettime()
+LLIBS += -lrt
+
+../src/utils/libutils.a:
+	$(MAKE) -C ../src/utils
+
+../src/crypto/libcrypto.a:
+	$(MAKE) -C ../src/crypto
+
+../src/tls/libtls.a:
+	$(MAKE) -C ../src/tls
+
+
+test-aes: test-aes.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-asn1: test-asn1.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-base64: test-base64.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-https: test-https.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
+
+test-list: test-list.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-md4: test-md4.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-milenage: test-milenage.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-rc4: test-rc4.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-rsa-sig-ver: test-rsa-sig-ver.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
+
+test-sha1: test-sha1.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-sha256: test-sha256.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-x509: test-x509.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
+
+test-x509v3: test-x509v3.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
+
+
+run-tests: $(TESTS)
+	./test-aes
+	./test-list
+	./test-md4
+	./test-milenage
+	./test-rsa-sig-ver
+	./test-sha1
+	./test-sha256
+	@echo
+	@echo All tests completed successfully.
+
+clean:
+	$(MAKE) -C ../src clean
+	rm -f $(TESTS) *~ *.o *.d
+	rm -f test-https
+	rm -f test_x509v3_nist.out.*
+	rm -f test_x509v3_nist2.out.*
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/tests/ap-mgmt-fuzzer/Makefile b/hostap/tests/ap-mgmt-fuzzer/Makefile
new file mode 100644
index 0000000..141a6f6
--- /dev/null
+++ b/hostap/tests/ap-mgmt-fuzzer/Makefile
@@ -0,0 +1,81 @@
+all: ap-mgmt-fuzzer
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+SRC=../../src
+
+CFLAGS += -I$(SRC)
+CFLAGS += -I$(SRC)/utils
+CFLAGS += -DCONFIG_WNM
+CFLAGS += -DCONFIG_INTERWORKING
+CFLAGS += -DCONFIG_GAS
+CFLAGS += -DCONFIG_HS20
+CFLAGS += -DIEEE8021X_EAPOL
+CFLAGS += -DNEED_AP_MLME
+
+$(SRC)/utils/libutils.a:
+	$(MAKE) -C $(SRC)/utils
+
+$(SRC)/common/libcommon.a:
+	$(MAKE) -C $(SRC)/common
+
+$(SRC)/crypto/libcrypto.a:
+	$(MAKE) -C $(SRC)/crypto
+
+$(SRC)/tls/libtls.a:
+	$(MAKE) -C $(SRC)/tls
+
+$(SRC)/wps/libwps.a:
+	$(MAKE) -C $(SRC)/wps
+
+$(SRC)/eap_common/libeap_common.a:
+	$(MAKE) -C $(SRC)/eap_common
+
+$(SRC)/eap_server/libeap_server.a:
+	$(MAKE) -C $(SRC)/eap_server
+
+$(SRC)/l2_packet/libl2_packet.a:
+	$(MAKE) -C $(SRC)/l2_packet
+
+$(SRC)/eapol_auth/libeapol_auth.a:
+	$(MAKE) -C $(SRC)/eapol_auth
+
+$(SRC)/ap/libap.a:
+	$(MAKE) -C $(SRC)/ap
+
+$(SRC)/radius/libradius.a:
+	$(MAKE) -C $(SRC)/radius
+
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/tls/libtls.a
+LIBS += $(SRC)/wps/libwps.a
+LIBS += $(SRC)/eap_server/libeap_server.a
+LIBS += $(SRC)/eap_common/libeap_common.a
+LIBS += $(SRC)/l2_packet/libl2_packet.a
+LIBS += $(SRC)/ap/libap.a
+LIBS += $(SRC)/eapol_auth/libeapol_auth.a
+LIBS += $(SRC)/radius/libradius.a
+LIBS += $(SRC)/utils/libutils.a
+
+ELIBS += $(SRC)/crypto/libcrypto.a
+ELIBS += $(SRC)/tls/libtls.a
+
+ap-mgmt-fuzzer: ap-mgmt-fuzzer.o $(OBJS) $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $^ $(LIBS) $(ELIBS)
+
+clean:
+	$(MAKE) -C $(SRC) clean
+	rm -f ap-mgmt-fuzzer *~ *.o *.d
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/tests/ap-mgmt-fuzzer/ap-mgmt-fuzzer.c b/hostap/tests/ap-mgmt-fuzzer/ap-mgmt-fuzzer.c
new file mode 100644
index 0000000..dd06144
--- /dev/null
+++ b/hostap/tests/ap-mgmt-fuzzer/ap-mgmt-fuzzer.c
@@ -0,0 +1,123 @@
+/*
+ * hostapd - Management frame fuzzer
+ * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "ap/hostapd.h"
+#include "ap/ieee802_11.h"
+#include "ap/sta_info.h"
+
+
+const struct wpa_driver_ops *const wpa_drivers[] =
+{
+	NULL
+};
+
+
+struct arg_ctx {
+	const char *fname;
+	struct hostapd_iface iface;
+	struct hostapd_data hapd;
+	struct wpa_driver_ops driver;
+	struct hostapd_config iconf;
+	struct hostapd_bss_config conf;
+};
+
+
+static void test_send_mgmt(void *eloop_data, void *user_ctx)
+{
+	struct arg_ctx *ctx = eloop_data;
+	char *data;
+	size_t len;
+	struct hostapd_frame_info fi;
+
+	wpa_printf(MSG_INFO, "ap-mgmt-fuzzer: Send '%s'", ctx->fname);
+
+	data = os_readfile(ctx->fname, &len);
+	if (!data) {
+		wpa_printf(MSG_ERROR, "Could not read '%s'", ctx->fname);
+		goto out;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "fuzzer - WNM", data, len);
+
+	os_memset(&fi, 0, sizeof(fi));
+	ieee802_11_mgmt(&ctx->hapd, (u8 *) data, len, &fi);
+
+out:
+	os_free(data);
+	eloop_terminate();
+}
+
+
+static int init_hapd(struct arg_ctx *ctx)
+{
+	struct hostapd_data *hapd = &ctx->hapd;
+	struct sta_info *sta;
+
+	hapd->driver = &ctx->driver;
+	os_memcpy(hapd->own_addr, "\x02\x00\x00\x00\x03\x00", ETH_ALEN);
+	hapd->iface = &ctx->iface;
+	hapd->iface->conf = hostapd_config_defaults();;
+	if (!hapd->iface->conf)
+		return -1;
+	hapd->iconf = hapd->iface->conf;
+	hapd->conf = hapd->iconf->bss[0];
+	hostapd_config_defaults_bss(hapd->conf);
+
+	sta = ap_sta_add(hapd, (u8 *) "\x02\x00\x00\x00\x00\x00");
+	if (sta)
+		sta->flags |= WLAN_STA_ASSOC | WLAN_STA_WMM;
+
+	return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct arg_ctx ctx;
+	int ret = -1;
+
+	if (argc < 2) {
+		printf("usage: %s <file>\n", argv[0]);
+		return -1;
+	}
+
+	if (os_program_init())
+		return -1;
+
+	wpa_debug_level = 0;
+	wpa_debug_show_keys = 1;
+
+	if (eloop_init()) {
+		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+		return -1;
+	}
+
+	os_memset(&ctx, 0, sizeof(ctx));
+	ctx.fname = argv[1];
+	if (init_hapd(&ctx))
+		goto fail;
+
+	eloop_register_timeout(0, 0, test_send_mgmt, &ctx, NULL);
+
+	wpa_printf(MSG_DEBUG, "Starting eloop");
+	eloop_run();
+	wpa_printf(MSG_DEBUG, "eloop done");
+	hostapd_free_stas(&ctx.hapd);
+
+	ret = 0;
+fail:
+	hostapd_config_free(ctx.hapd.iconf);
+	eloop_destroy();
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/hostap/tests/ap-mgmt-fuzzer/auth.dat b/hostap/tests/ap-mgmt-fuzzer/auth.dat
new file mode 100644
index 0000000..0eb36e5
--- /dev/null
+++ b/hostap/tests/ap-mgmt-fuzzer/auth.dat
Binary files differ
diff --git a/hostap/tests/ap-mgmt-fuzzer/probe-req.dat b/hostap/tests/ap-mgmt-fuzzer/probe-req.dat
new file mode 100644
index 0000000..a5fba77
--- /dev/null
+++ b/hostap/tests/ap-mgmt-fuzzer/probe-req.dat
Binary files differ
diff --git a/hostap/tests/eapol-fuzzer/Makefile b/hostap/tests/eapol-fuzzer/Makefile
new file mode 100644
index 0000000..f5a9a57
--- /dev/null
+++ b/hostap/tests/eapol-fuzzer/Makefile
@@ -0,0 +1,67 @@
+all: eapol-fuzzer
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+SRC=../../src
+
+CFLAGS += -I$(SRC)
+CFLAGS += -DIEEE8021X_EAPOL
+
+$(SRC)/utils/libutils.a:
+	$(MAKE) -C $(SRC)/utils
+
+$(SRC)/common/libcommon.a:
+	$(MAKE) -C $(SRC)/common
+
+$(SRC)/crypto/libcrypto.a:
+	$(MAKE) -C $(SRC)/crypto
+
+$(SRC)/tls/libtls.a:
+	$(MAKE) -C $(SRC)/tls
+
+$(SRC)/rsn_supp/librsn_supp.a:
+	$(MAKE) -C $(SRC)/rsn_supp
+
+$(SRC)/eapol_supp/libeapol_supp.a:
+	$(MAKE) -C $(SRC)/eapol_supp
+
+$(SRC)/eap_peer/libeap_peer.a:
+	$(MAKE) -C $(SRC)/eap_peer
+
+$(SRC)/eap_common/libeap_common.a:
+	$(MAKE) -C $(SRC)/eap_common
+
+$(SRC)/l2_packet/libl2_packet.a:
+	$(MAKE) -C $(SRC)/l2_packet
+
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/tls/libtls.a
+LIBS += $(SRC)/rsn_supp/librsn_supp.a
+LIBS += $(SRC)/eapol_supp/libeapol_supp.a
+LIBS += $(SRC)/eap_peer/libeap_peer.a
+LIBS += $(SRC)/eap_common/libeap_common.a
+LIBS += $(SRC)/l2_packet/libl2_packet.a
+LIBS += $(SRC)/utils/libutils.a
+
+ELIBS += $(SRC)/crypto/libcrypto.a
+ELIBS += $(SRC)/tls/libtls.a
+
+eapol-fuzzer: eapol-fuzzer.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $^ $(LIBS) $(ELIBS)
+
+clean:
+	$(MAKE) -C $(SRC) clean
+	rm -f eapol-fuzzer *~ *.o *.d
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/tests/eapol-fuzzer/eap-req-identity.dat b/hostap/tests/eapol-fuzzer/eap-req-identity.dat
new file mode 100644
index 0000000..768b277
--- /dev/null
+++ b/hostap/tests/eapol-fuzzer/eap-req-identity.dat
Binary files differ
diff --git a/hostap/tests/eapol-fuzzer/eap-req-sim.dat b/hostap/tests/eapol-fuzzer/eap-req-sim.dat
new file mode 100644
index 0000000..eb854aa
--- /dev/null
+++ b/hostap/tests/eapol-fuzzer/eap-req-sim.dat
Binary files differ
diff --git a/hostap/tests/eapol-fuzzer/eapol-fuzzer.c b/hostap/tests/eapol-fuzzer/eapol-fuzzer.c
new file mode 100644
index 0000000..7429ee3
--- /dev/null
+++ b/hostap/tests/eapol-fuzzer/eapol-fuzzer.c
@@ -0,0 +1,211 @@
+/*
+ * wpa_supplicant - EAPOL fuzzer
+ * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+
+
+struct arg_ctx {
+	const char *fname;
+	struct wpa_sm *wpa;
+	struct eapol_sm *eapol;
+};
+
+
+static void test_send_eapol(void *eloop_data, void *user_ctx)
+{
+	struct arg_ctx *ctx = eloop_data;
+	char *data;
+	size_t len;
+	u8 src[ETH_ALEN] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x01 };
+	u8 wpa_ie[200];
+	size_t wpa_ie_len;
+
+	wpa_printf(MSG_INFO, "eapol-fuzzer: Send '%s'", ctx->fname);
+
+	data = os_readfile(ctx->fname, &len);
+	if (!data) {
+		wpa_printf(MSG_ERROR, "Could not read '%s'", ctx->fname);
+		goto out;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "fuzzer - EAPOL", data, len);
+
+	eapol_sm_notify_portEnabled(ctx->eapol, TRUE);
+
+	wpa_sm_set_param(ctx->wpa, WPA_PARAM_PROTO, WPA_PROTO_RSN);
+	wpa_sm_set_param(ctx->wpa, WPA_PARAM_RSN_ENABLED, 1);
+	wpa_sm_set_param(ctx->wpa, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK);
+	wpa_sm_set_param(ctx->wpa, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP);
+	wpa_sm_set_param(ctx->wpa, WPA_PARAM_GROUP, WPA_CIPHER_CCMP);
+
+	wpa_ie_len = sizeof(wpa_ie);
+	wpa_sm_set_assoc_wpa_ie_default(ctx->wpa, wpa_ie, &wpa_ie_len);
+
+	if (eapol_sm_rx_eapol(ctx->eapol, src, (u8 *) data, len) <= 0)
+		wpa_sm_rx_eapol(ctx->wpa, src, (u8 *) data, len);
+
+out:
+	os_free(data);
+	eloop_terminate();
+}
+
+
+static void * get_network_ctx(void *arg)
+{
+	return (void *) 1;
+}
+
+
+static void set_state(void *arg, enum wpa_states state)
+{
+}
+
+
+static void deauthenticate(void *arg, int reason_code)
+{
+}
+
+
+static u8 * alloc_eapol(void *arg, u8 type,
+			const void *data, u16 data_len,
+			size_t *msg_len, void **data_pos)
+{
+	struct ieee802_1x_hdr *hdr;
+
+	*msg_len = sizeof(*hdr) + data_len;
+	hdr = os_malloc(*msg_len);
+	if (hdr == NULL)
+		return NULL;
+
+	hdr->version = 2;
+	hdr->type = type;
+	hdr->length = host_to_be16(data_len);
+
+	if (data)
+		os_memcpy(hdr + 1, data, data_len);
+	else
+		os_memset(hdr + 1, 0, data_len);
+
+	if (data_pos)
+		*data_pos = hdr + 1;
+
+	return (u8 *) hdr;
+}
+
+
+static int ether_send(void *arg, const u8 *dest, u16 proto,
+		      const u8 *buf, size_t len)
+{
+	return 0;
+}
+
+
+static int get_bssid(void *ctx, u8 *bssid)
+{
+	return -1;
+}
+
+
+static int eapol_send(void *ctx, int type, const u8 *buf, size_t len)
+{
+	return 0;
+}
+
+
+static int init_wpa(struct arg_ctx *arg)
+{
+	struct wpa_sm_ctx *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to allocate WPA context.");
+		return -1;
+	}
+
+	ctx->ctx = arg;
+	ctx->msg_ctx = arg;
+	ctx->get_network_ctx = get_network_ctx;
+	ctx->set_state = set_state;
+	ctx->deauthenticate = deauthenticate;
+	ctx->alloc_eapol = alloc_eapol;
+	ctx->ether_send = ether_send;
+	ctx->get_bssid = get_bssid;
+
+	arg->wpa = wpa_sm_init(ctx);
+	return arg->wpa ? 0 : -1;
+}
+
+
+static int init_eapol(struct arg_ctx *arg)
+{
+	struct eapol_ctx *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to allocate EAPOL context.");
+		return -1;
+	}
+
+	ctx->ctx = arg;
+	ctx->msg_ctx = arg;
+	ctx->eapol_send = eapol_send;
+
+	arg->eapol = eapol_sm_init(ctx);
+	return arg->eapol ? 0 : -1;
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct arg_ctx ctx;
+	int ret = -1;
+
+	if (argc < 2) {
+		printf("usage: %s <file>\n", argv[0]);
+		return -1;
+	}
+
+	if (os_program_init())
+		return -1;
+
+	wpa_debug_level = 0;
+	wpa_debug_show_keys = 1;
+
+	if (eloop_init()) {
+		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+		return -1;
+	}
+
+	os_memset(&ctx, 0, sizeof(ctx));
+	ctx.fname = argv[1];
+	if (init_wpa(&ctx) || init_eapol(&ctx))
+		goto fail;
+
+	eloop_register_timeout(0, 0, test_send_eapol, &ctx, NULL);
+
+	wpa_printf(MSG_DEBUG, "Starting eloop");
+	eloop_run();
+	wpa_printf(MSG_DEBUG, "eloop done");
+
+	ret = 0;
+fail:
+	if (ctx.wpa)
+		wpa_sm_deinit(ctx.wpa);
+	if (ctx.eapol)
+		eapol_sm_deinit(ctx.eapol);
+
+	eloop_destroy();
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/hostap/tests/eapol-fuzzer/eapol-key-m1.dat b/hostap/tests/eapol-fuzzer/eapol-key-m1.dat
new file mode 100644
index 0000000..937721c
--- /dev/null
+++ b/hostap/tests/eapol-fuzzer/eapol-key-m1.dat
Binary files differ
diff --git a/hostap/tests/hwsim/README b/hostap/tests/hwsim/README
new file mode 100644
index 0000000..e68b0dc
--- /dev/null
+++ b/hostap/tests/hwsim/README
@@ -0,0 +1,220 @@
+Automated hostapd/wpa_supplicant testing with mac80211_hwsim
+------------------------------------------------------------
+
+This directory contains testing infrastructure and test cases to run
+automated tests of full hostapd and wpa_supplicant functionality. This
+testing is done with the help of mac80211_hwsim which is Linux kernel
+driver that simulates IEEE 802.11 radios without requiring any
+additional hardware. This setup most of the hostapd and wpa_supplicant
+functionality (and large parts of the Linux cfg80211 and mac80211
+functionality for that matter) to be tested.
+
+mac80211_hwsim is loaded with five simulated radios to allow different
+device combinations to be tested. wlantest is used analyze raw packets
+captured through the hwsim0 monitor interface that capture all frames
+sent on all channels. wlantest is used to store the frames for
+analysis. Three wpa_supplicant processes are used to control three
+virtual radios and one hostapd process is used to dynamically control
+the other two virtual radios. wpa_supplicant/hostapd test functionality
+is used to verify that data connection (both unicast and broadcast)
+works between two netdevs.
+
+The python scripts and tools in this directory control test case
+execution. They interact wpa_supplicant and hostapd through control
+interfaces to perform the operations. In addition, wlantest_cli is used
+to verify that operations have been performed correctly and that the
+network connection works in the expected way.
+
+These test cases are run automatically against the hostap.git commits
+for regression testing and to help in keeping the hostap.git master
+branch in stable state. Results from these tests are available here:
+http://buildbot.w1.fi/hwsim/
+
+
+Building binaries for testing
+-----------------------------
+
+You will need to build (or use already built) components to be
+tested. These are available in the hostap.git repository and can be
+built for example as follows:
+
+cd ../../wpa_supplicant
+cp ../tests/hwsim/example-wpa_supplicant.config .config
+make clean
+make
+cd ../hostapd
+cp ../tests/hwsim/example-hostapd.config .config
+make clean
+make hostapd hlr_auc_gw
+cd ../wlantest
+make clean
+make
+
+Alternatively, the build.sh script here can be used to run these steps
+with conditional creation of .config files only if they do not exist.
+
+The test scripts can find the binaries in the locations where they were
+built. It is also possible to install wlantest_cli somewhere on the path
+to use pre-built tools.
+
+Please note that some of the configuration parameters used to enable
+more testing coverage may require development packages that may not be
+installed by default in many distributions. For example, following
+Debian/Ubuntu packages are likely to be needed:
+- binutils-dev
+- libsqlite3-dev
+- libpcap-dev
+
+example-setup.txt provides more complete step-by-step example on how a
+test setup can be built.
+
+
+wpaspy
+------
+
+The python scripts use wpaspy.py to interact with the wpa_supplicant
+control interface, but the run-tests.py script adds the (relative)
+path into the environment so it doesn't need to be installed.
+
+
+mac80211_hwsim
+--------------
+
+mac80211_hwsim kernel module is available from the upstream Linux
+kernel. Some Linux distributions enable it by default. If that's not the
+case, you can either enable it in the kernel configuration
+(CONFIG_MAC80211_HWSIM=m) and rebuild your kernel or use Backports with
+CPTCFG_MAC80211_HWSIM=m to replace the wireless LAN components in the
+base kernel.
+
+
+sudo
+----
+
+Some parts of the testing process requires root privileges. The test
+scripts are currently using sudo to achieve this. To be able to run the
+tests, you'll probably want to enable sudo with a timeout to not expire
+password entry very quickly. For example, use this in the sudoers file:
+
+Defaults        env_reset,timestamp_timeout=180
+
+Or on a dedicated test system, you could even disable password prompting
+with this in sudoers:
+
+%sudo   ALL=NOPASSWD: ALL
+
+
+Other network interfaces
+------------------------
+
+Some of the test scripts are still using hardcoded interface names, so
+the easiest way of making things work is to avoid using other network
+devices that may use conflicting interface names. For example, unload
+any wireless LAN driver before running the tests and make sure that
+wlan0..4 gets assigned as the interface names for the mac80211_hwsim
+radios. It may also be possible to rename the interface expectations in
+run-tests.py to allow other names to be used.
+
+Please also note that some commonly enabled tools, like NetworkManager,
+may end up trying to control new network interfaces automatically. This
+can result in conflicts with the test scripts and you may need to
+disable such network services or at least mark the mac80211_hwsim wlan#
+interfaces as umanaged. As an example, this can be done in
+/etc/NetworkManager/NetworkManager.conf with following addition:
+
+[keyfile]
+unmanaged-devices=mac:02:00:00:00:00:00;mac:02:00:00:00:01:00;mac:02:00:00:00:02:00;mac:02:00:00:00:03:00;mac:02:00:00:00:04:00
+
+
+Running tests
+-------------
+
+Simplest way to run a full set of the test cases is by running
+run-all.sh in tests/hwsim directory. This will use start.sh to load the
+mac80211_hwsim module and start wpa_supplicant, hostapd, and various
+test tools. run-tests.sh is then used to run through all the defined
+test cases and stop.sh to stop the programs and unload the kernel
+module.
+
+run-all.sh can be used to run the same test cases under different
+conditions:
+
+# run normal test cases
+./run-all.sh
+
+# run normal test cases under valgrind
+./run-all.sh valgrind
+
+# run normal test cases with Linux tracing
+./run-all.sh trace
+
+# run normal test cases with multi channel support (see details below)
+./run-all.sh channels=<num of channels>
+
+run-all.sh directs debug logs into the logs subdirectory (or $LOGDIR if
+present in the environment). Log file names include the current UNIX
+timestamp and a postfix to identify the specific log:
+- *.log0 = wpa_supplicant debug log for the first radio
+- *.log1 = wpa_supplicant debug log for the second radio
+- *.log2 = wpa_supplicant debug log for the third radio
+- *.hostapd = hostapd debug log
+- hwsim0 = wlantest debug log
+- hwsim0.pcapng = capture with all frames exchanged during the tests
+- *.log = debug prints from the test scripts
+- trace.dat = Linux tracing record (if enabled)
+- hlr_auc_gw - hlr_auc_gw (EAP-SIM/AKA/AKA' authentication) log
+- auth_serv - hostapd as RADIUS authentication server log
+
+
+For manual testing, ./start.sh can be used to initialize interfaces and
+programs and run-tests.py to execute one or more test
+cases. run-tests.py output verbosity can be controlled with -d (more
+verbose debug output) and -q (less verbose output) on the command
+line. "-f <module name>" (pointing to file test_<module name>.py) can be
+used to specify that all test cases from a single file are to be
+run. Test name as the last command line argument can be specified that a
+single test case is to be run (e.g., "./run-tests.py ap_pmf_required").
+
+Notice that some tests require the driver to support concurrent
+operation on multi channels in order to run. These tests will be skipped
+in case the driver does not support multi channels. To enable support
+for multi channel, the number of supported channel is passed as an
+argument to run-all.sh or start.sh
+
+
+Adding/modifying test cases
+---------------------------
+
+All the test cases are defined in the test_*.py files. These are python
+scripts that can use the local helper classes to interact with the test
+components. While various python constructs can be used in the scripts,
+only a minimal level of python knowledge should really be needed to
+modify and add new test cases. The easiest starting point for this is
+likely to take a look at some of the example scripts. When working on a
+new test, run-tests.py with -d and the test case name on the command
+line is a convenient way of verifying functionality.
+
+run-tests.py will automatically import all test cases from the test_*.py
+files in this directory. All functions starting with the "test_" prefix
+in these files are assumed to be test cases. Each test case is named by
+the function name following the "test_" prefix.
+
+
+Results database
+----------------
+
+run-tests.py can be requested to write results from the execution of
+each test case into an sqlite database. The "-S <path to database>" and
+"-b <build id>" command line arguments can be used to do that. The
+database must have been prepared before this, e.g., with following:
+
+cat | sqlite3 /tmp/example.db <<EOF
+CREATE TABLE results (test,result,run,time,duration,build,commitid);
+CREATE INDEX results_idx ON results (test);
+CREATE INDEX results_idx2 ON results (run);
+CREATE TABLE tests (test,description);
+CREATE UNIQUE INDEX tests_idx ON tests (test);
+CREATE TABLE logs (test,run,type,contents);
+CREATE INDEX logs_idx ON logs (test);
+CREATE INDEX logs_idx2 ON logs (run);
+EOF
diff --git a/hostap/tests/hwsim/auth_serv/as.conf b/hostap/tests/hwsim/auth_serv/as.conf
new file mode 100644
index 0000000..0d89b92
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/as.conf
@@ -0,0 +1,21 @@
+driver=none
+radius_server_clients=auth_serv/radius_clients.conf
+radius_server_acct_port=1813
+eap_server=1
+eap_user_file=auth_serv/eap_user.conf
+
+interface=as
+ctrl_interface=/var/run/hostapd
+ctrl_interface_group=admin
+
+ca_cert=auth_serv/ca.pem
+server_cert=auth_serv/server.pem
+private_key=auth_serv/server.key
+ocsp_stapling_response=LOGDIR/ocsp-server-cache.der
+server_id=server.w1.fi
+eap_sim_db=unix:/tmp/hlr_auc_gw.sock
+dh_file=auth_serv/dh.conf
+pac_opaque_encr_key=000102030405060708090a0b0c0d0e0f
+eap_fast_a_id=101112131415161718191a1b1c1d1e1f
+eap_fast_a_id_info=test server
+eap_sim_aka_result_ind=1
diff --git a/hostap/tests/hwsim/auth_serv/as2.conf b/hostap/tests/hwsim/auth_serv/as2.conf
new file mode 100644
index 0000000..d9ee031
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/as2.conf
@@ -0,0 +1,21 @@
+driver=none
+radius_server_clients=auth_serv/radius_clients.conf
+radius_server_auth_port=1814
+eap_server=1
+eap_user_file=auth_serv/eap_user.conf
+
+interface=as2
+ctrl_interface=/var/run/hostapd
+ctrl_interface_group=admin
+
+ca_cert=auth_serv/ca.pem
+server_cert=auth_serv/server.pem
+private_key=auth_serv/server.key
+ocsp_stapling_response=LOGDIR/ocsp-server-cache.der
+server_id=server2.w1.fi
+eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=LOGDIR/hostapd.db
+dh_file=auth_serv/dh.conf
+pac_opaque_encr_key=000102030405060708090a0b0c0d0e0f
+eap_fast_a_id=101112131415161718191a1b1c1d1e1f
+eap_fast_a_id_info=test server2
+eap_sim_aka_result_ind=1
diff --git a/hostap/tests/hwsim/auth_serv/ca-and-crl.pem b/hostap/tests/hwsim/auth_serv/ca-and-crl.pem
new file mode 100644
index 0000000..ba79339
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ca-and-crl.pem
@@ -0,0 +1,64 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162817 (0xd8d3e3a6cbe3ccc1)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Jun 29 16:41:22 2013 GMT
+            Not After : Jun 27 16:41:22 2023 GMT
+        Subject: C=FI, O=w1.fi, CN=Root CA
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:be:1e:86:e4:79:03:c1:d1:94:d5:d4:b3:b1:28:
+                    90:76:fb:b8:a6:cd:6d:1c:d1:48:f4:08:9a:67:ff:
+                    f9:a6:54:b1:19:29:df:29:1b:cd:f1:6f:66:01:e7:
+                    db:79:ce:c0:39:2a:25:13:26:94:0c:2c:7b:5a:2c:
+                    81:0f:94:ee:51:d0:75:e6:46:db:17:46:a7:15:8b:
+                    0e:57:0f:b0:54:76:63:12:ca:86:18:bc:1a:c3:16:
+                    c0:70:09:d6:6b:43:39:b8:98:29:46:ac:cb:6a:ad:
+                    38:88:3b:07:dc:81:cd:3a:f6:1d:f6:2f:ef:1d:d7:
+                    ae:8a:b6:d1:e7:b3:15:02:b9
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            X509v3 Basic Constraints: 
+                CA:TRUE
+    Signature Algorithm: sha1WithRSAEncryption
+         1a:cf:77:60:44:43:c4:55:0e:99:e0:89:aa:b9:d3:7b:32:b7:
+         5c:9c:7c:ca:fe:8c:d4:94:c6:5e:f3:83:19:5f:29:59:68:a4:
+         4f:dc:04:2e:b8:71:c0:6d:3b:ae:01:e4:b9:88:99:cc:ce:82:
+         be:6a:28:c2:ac:6a:94:c6:87:90:ed:85:3c:10:71:c5:ff:3c:
+         70:64:e2:41:62:31:ea:86:7b:11:8c:93:ea:c6:f3:f3:4e:f9:
+         d4:f2:81:90:d7:f4:fa:a1:91:6e:d4:dd:15:3e:26:3b:ac:1e:
+         c3:c2:1f:ed:bb:34:bf:cb:b2:67:c6:c6:51:e8:51:22:b4:f3:
+         92:e8
+-----BEGIN CERTIFICATE-----
+MIICLDCCAZWgAwIBAgIJANjT46bL48zBMA0GCSqGSIb3DQEBBQUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xMzA2
+MjkxNjQxMjJaFw0yMzA2MjcxNjQxMjJaMC8xCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAvh6G5HkDwdGU1dSzsSiQdvu4ps1tHNFI9AiaZ//5plSxGSnfKRvN8W9m
+Aefbec7AOSolEyaUDCx7WiyBD5TuUdB15kbbF0anFYsOVw+wVHZjEsqGGLwawxbA
+cAnWa0M5uJgpRqzLaq04iDsH3IHNOvYd9i/vHdeuirbR57MVArkCAwEAAaNQME4w
+HQYDVR0OBBYEFLiS3v2KGLMww59V8zNdtMgpikEUMB8GA1UdIwQYMBaAFLiS3v2K
+GLMww59V8zNdtMgpikEUMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEA
+Gs93YERDxFUOmeCJqrnTezK3XJx8yv6M1JTGXvODGV8pWWikT9wELrhxwG07rgHk
+uYiZzM6CvmoowqxqlMaHkO2FPBBxxf88cGTiQWIx6oZ7EYyT6sbz80751PKBkNf0
++qGRbtTdFT4mO6wew8If7bs0v8uyZ8bGUehRIrTzkug=
+-----END CERTIFICATE-----
+-----BEGIN X509 CRL-----
+MIIBJTCBjwIBATANBgkqhkiG9w0BAQsFADAvMQswCQYDVQQGEwJGSTEOMAwGA1UE
+CgwFdzEuZmkxEDAOBgNVBAMMB1Jvb3QgQ0EXDTE1MDYyOTE5MDU1OVoXDTIzMDYy
+NzE5MDU1OVowHDAaAgkA2NPjpsvjzMMXDTEzMDYyOTE2NDEyMlqgDjAMMAoGA1Ud
+FAQDAgEKMA0GCSqGSIb3DQEBCwUAA4GBALN3DQj9bNTuulU/o8MH2wAATisnDSYt
+WD7W9S/26AgQDK2qySvp0+vz/Li0BMafbUd+opMu1smdyirjA6rDSjC8scaoVwUo
+kY2fFo7qNuUU1N3T25/UCfGu3/E3ynrBZWiQoSCX/8NvY+pzEEf8ZOKt5837VKmk
+EB1U3PrnNi7m
+-----END X509 CRL-----
diff --git a/hostap/tests/hwsim/auth_serv/ca-incorrect.pem b/hostap/tests/hwsim/auth_serv/ca-incorrect.pem
new file mode 100644
index 0000000..2e9a492
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ca-incorrect.pem
@@ -0,0 +1,55 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 10855188644662735910 (0x96a5608f1ef9f426)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FI, CN=TEST - Incorrect Root CA
+        Validity
+            Not Before: Oct 20 16:30:06 2013 GMT
+            Not After : Oct 18 16:30:06 2023 GMT
+        Subject: C=FI, CN=TEST - Incorrect Root CA
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:bc:0c:8e:61:1e:5b:ea:b2:6b:cc:8a:8c:38:85:
+                    6d:79:e0:7a:28:d1:b5:55:65:52:f8:e2:2c:74:c1:
+                    00:15:c6:15:84:56:08:f5:e9:eb:bc:07:8d:b7:97:
+                    b6:73:7f:46:77:86:31:d0:f0:7f:95:d6:4a:7c:35:
+                    07:85:43:41:5e:f4:07:84:e6:52:cb:52:38:ef:fe:
+                    6a:16:84:22:45:2e:c1:a1:16:8d:d2:b3:62:c2:05:
+                    77:43:04:2e:d0:52:ee:db:78:10:79:44:49:92:35:
+                    ee:99:83:aa:a0:1d:e6:3d:c3:c6:a2:8e:b6:4d:7f:
+                    d8:11:a9:a3:bc:68:1d:a2:6f
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                3E:49:CB:A7:6A:A7:08:4F:DA:99:E4:3C:64:A2:AC:96:BE:99:E4:F2
+            X509v3 Authority Key Identifier: 
+                keyid:3E:49:CB:A7:6A:A7:08:4F:DA:99:E4:3C:64:A2:AC:96:BE:99:E4:F2
+
+            X509v3 Basic Constraints: 
+                CA:TRUE
+    Signature Algorithm: sha1WithRSAEncryption
+         31:98:35:4b:d8:d2:8e:55:7a:af:06:f8:ef:6b:24:13:11:12:
+         b0:77:81:b9:ab:50:20:d6:78:99:3f:bc:3d:89:d4:b2:bd:7a:
+         54:03:fc:a7:a4:9f:2b:09:da:75:c9:8d:4c:65:90:c5:df:fc:
+         6b:48:52:f1:0a:aa:57:8a:b1:f5:fe:35:87:87:32:39:b9:ad:
+         80:f0:8e:36:72:63:d5:97:20:e5:b6:06:64:31:5a:66:66:15:
+         85:68:b7:9d:26:8b:46:7f:e8:1b:09:f5:c2:4a:35:7c:49:e2:
+         b2:dc:59:b2:91:8d:85:33:07:09:ca:78:7a:db:b3:e5:58:2c:
+         cc:6a
+-----BEGIN CERTIFICATE-----
+MIICLjCCAZegAwIBAgIJAJalYI8e+fQmMA0GCSqGSIb3DQEBBQUAMDAxCzAJBgNV
+BAYTAkZJMSEwHwYDVQQDDBhURVNUIC0gSW5jb3JyZWN0IFJvb3QgQ0EwHhcNMTMx
+MDIwMTYzMDA2WhcNMjMxMDE4MTYzMDA2WjAwMQswCQYDVQQGEwJGSTEhMB8GA1UE
+AwwYVEVTVCAtIEluY29ycmVjdCBSb290IENBMIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQC8DI5hHlvqsmvMiow4hW154Hoo0bVVZVL44ix0wQAVxhWEVgj16eu8
+B423l7Zzf0Z3hjHQ8H+V1kp8NQeFQ0Fe9AeE5lLLUjjv/moWhCJFLsGhFo3Ss2LC
+BXdDBC7QUu7beBB5REmSNe6Zg6qgHeY9w8aijrZNf9gRqaO8aB2ibwIDAQABo1Aw
+TjAdBgNVHQ4EFgQUPknLp2qnCE/ameQ8ZKKslr6Z5PIwHwYDVR0jBBgwFoAUPknL
+p2qnCE/ameQ8ZKKslr6Z5PIwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOB
+gQAxmDVL2NKOVXqvBvjvayQTERKwd4G5q1Ag1niZP7w9idSyvXpUA/ynpJ8rCdp1
+yY1MZZDF3/xrSFLxCqpXirH1/jWHhzI5ua2A8I42cmPVlyDltgZkMVpmZhWFaLed
+JotGf+gbCfXCSjV8SeKy3FmykY2FMwcJynh627PlWCzMag==
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/ca.der b/hostap/tests/hwsim/auth_serv/ca.der
new file mode 100644
index 0000000..09d5fa0
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ca.der
Binary files differ
diff --git a/hostap/tests/hwsim/auth_serv/ca.pem b/hostap/tests/hwsim/auth_serv/ca.pem
new file mode 100644
index 0000000..b128893
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ca.pem
@@ -0,0 +1,55 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162817 (0xd8d3e3a6cbe3ccc1)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Jun 29 16:41:22 2013 GMT
+            Not After : Jun 27 16:41:22 2023 GMT
+        Subject: C=FI, O=w1.fi, CN=Root CA
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:be:1e:86:e4:79:03:c1:d1:94:d5:d4:b3:b1:28:
+                    90:76:fb:b8:a6:cd:6d:1c:d1:48:f4:08:9a:67:ff:
+                    f9:a6:54:b1:19:29:df:29:1b:cd:f1:6f:66:01:e7:
+                    db:79:ce:c0:39:2a:25:13:26:94:0c:2c:7b:5a:2c:
+                    81:0f:94:ee:51:d0:75:e6:46:db:17:46:a7:15:8b:
+                    0e:57:0f:b0:54:76:63:12:ca:86:18:bc:1a:c3:16:
+                    c0:70:09:d6:6b:43:39:b8:98:29:46:ac:cb:6a:ad:
+                    38:88:3b:07:dc:81:cd:3a:f6:1d:f6:2f:ef:1d:d7:
+                    ae:8a:b6:d1:e7:b3:15:02:b9
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            X509v3 Basic Constraints: 
+                CA:TRUE
+    Signature Algorithm: sha1WithRSAEncryption
+         1a:cf:77:60:44:43:c4:55:0e:99:e0:89:aa:b9:d3:7b:32:b7:
+         5c:9c:7c:ca:fe:8c:d4:94:c6:5e:f3:83:19:5f:29:59:68:a4:
+         4f:dc:04:2e:b8:71:c0:6d:3b:ae:01:e4:b9:88:99:cc:ce:82:
+         be:6a:28:c2:ac:6a:94:c6:87:90:ed:85:3c:10:71:c5:ff:3c:
+         70:64:e2:41:62:31:ea:86:7b:11:8c:93:ea:c6:f3:f3:4e:f9:
+         d4:f2:81:90:d7:f4:fa:a1:91:6e:d4:dd:15:3e:26:3b:ac:1e:
+         c3:c2:1f:ed:bb:34:bf:cb:b2:67:c6:c6:51:e8:51:22:b4:f3:
+         92:e8
+-----BEGIN CERTIFICATE-----
+MIICLDCCAZWgAwIBAgIJANjT46bL48zBMA0GCSqGSIb3DQEBBQUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xMzA2
+MjkxNjQxMjJaFw0yMzA2MjcxNjQxMjJaMC8xCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAvh6G5HkDwdGU1dSzsSiQdvu4ps1tHNFI9AiaZ//5plSxGSnfKRvN8W9m
+Aefbec7AOSolEyaUDCx7WiyBD5TuUdB15kbbF0anFYsOVw+wVHZjEsqGGLwawxbA
+cAnWa0M5uJgpRqzLaq04iDsH3IHNOvYd9i/vHdeuirbR57MVArkCAwEAAaNQME4w
+HQYDVR0OBBYEFLiS3v2KGLMww59V8zNdtMgpikEUMB8GA1UdIwQYMBaAFLiS3v2K
+GLMww59V8zNdtMgpikEUMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEA
+Gs93YERDxFUOmeCJqrnTezK3XJx8yv6M1JTGXvODGV8pWWikT9wELrhxwG07rgHk
+uYiZzM6CvmoowqxqlMaHkO2FPBBxxf88cGTiQWIx6oZ7EYyT6sbz80751PKBkNf0
++qGRbtTdFT4mO6wew8If7bs0v8uyZ8bGUehRIrTzkug=
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/dh.conf b/hostap/tests/hwsim/auth_serv/dh.conf
new file mode 100644
index 0000000..7bc8325
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/dh.conf
@@ -0,0 +1,5 @@
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAP3V8IHq3H2DUlYywsvjYNuS17eCdt0mJo6/os6PHqdhgkMrPxF9u4Gr
+qKXq9e6GqmZYdjta30N3FkXaV924BJ0xOqb2TntiKg4u50/l6hSUneWt6UFBaizd
+XrqjNFIme/5RXMZ7RglXliBpCepAaFLMcKhOS4ulUyYYHSy+oqRjAgEC
+-----END DH PARAMETERS-----
diff --git a/hostap/tests/hwsim/auth_serv/dh2.conf b/hostap/tests/hwsim/auth_serv/dh2.conf
new file mode 100644
index 0000000..5532efe
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/dh2.conf
@@ -0,0 +1,8 @@
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEAnMarPft+gvX8Ul5WKDn3rSa67dCNNhIivHnHBTn7I6LFE4pf3NY6
+KCUcVgJtOl55+58GxkpFsTZEmcykrbTjtJIyNfXFx6n/JKZTNYT0Vv7xmpSN3v53
+208v8rY91OiqO3T8L1PAsENMwuvMZL65IdLiMmVpAktgLGCafektBkaHj29bYCGS
+oGwz65iypzZGKGZmzET168lbh1SIuZkq3JOFEvE0ZJS5XhLrVUw14uZV/7lPRE+E
+dtza3kVlJXbkgnkrBsiuBlmWiga7EjPtD2o18WhPThI8zX/FoAyQUem4DkhfSpS8
+FrJUrODwQQycS5AaexDmZqHJ/L4GdlHcAwIBAg==
+-----END DH PARAMETERS-----
diff --git a/hostap/tests/hwsim/auth_serv/dsaparam.pem b/hostap/tests/hwsim/auth_serv/dsaparam.pem
new file mode 100644
index 0000000..54ba66a
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/dsaparam.pem
@@ -0,0 +1,11 @@
+-----BEGIN DSA PARAMETERS-----
+MIIBngKBwQCKGwQSQa8/VqJBvFDwpbJTEgQ2myTzbDCZqZof9EBBO5KlbUfgFZfY
+szSaZAWPok0mG9BRGoJgtajJruLU6lvoZ94FgGPhRrOZWd0wT6kiySRz4nv9kEMK
+Ch83zLQ+i1IdRFozP4k+9YHlAVL354zU+O5USxNMuhPVab4MPsl9I7gZmEQmisHA
+fwQOLppTrILuqNtVOqpRogKhVnvQITqV3VsRYV0JVersx48olOqtPGQKKewiJFJp
+n7fwGC208/sCFQD4vIUetIr4LGLwm3D/Y5HxNMyFFwKBwC7b0nT/lb1+z86vKg3v
+Cyy40D03ezYgmlwV+xObadfMmciwxK98DX763dFsY9omd6csFho1xGfSFRj8Bkv6
+rbumVtoZGurdRxU4ADJf4SF5MuK2rJ9jg4Wz6F1BhHEKtoubINlk3fyZWPx0sjkm
+t2IJlFY4rfbTI8ETrPrE+zb53tiaundB72cUWmZY/gRuaXh7lRzpMVr71+OedLU6
+VkIHXOG/nnb48hZfGohKvWTAp/wkb2w/dCdeGKgVZn3FzA==
+-----END DSA PARAMETERS-----
diff --git a/hostap/tests/hwsim/auth_serv/eap_user.conf b/hostap/tests/hwsim/auth_serv/eap_user.conf
new file mode 100644
index 0000000..3738e6c
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/eap_user.conf
@@ -0,0 +1,105 @@
+"pwd user"	PWD	"secret password"
+"pwd-hash"	PWD	hash:e3718ece8ab74792cbbfffd316d2d19a
+"pwd.user@test123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.example.com"	PWD	"secret password"
+"gpsk user"	GPSK	"abcdefghijklmnop0123456789abcdef"
+"sake user"	SAKE	0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+"eke user"	EKE	"hello"
+"ikev2 user"	IKEV2	"ike password"
+"pax.user@example.com"	PAX	0123456789abcdef0123456789abcdef
+"psk.user@example.com"	PSK	0123456789abcdef0123456789abcdef
+"vendor-test"	VENDOR-TEST	"foo"
+"osen@example.com"	WFA-UNAUTH-TLS
+"unauth-tls"	UNAUTH-TLS
+
+"erp-fast@example.com"	FAST
+"erp-fast@example.com"	GTC	"password"	[2]
+"erp-gpsk@example.com"	GPSK	"abcdefghijklmnop0123456789abcdef"
+"erp-eke@example.com"	EKE	"hello"
+"erp-pax@example.com"	PAX	0123456789abcdef0123456789abcdef
+"erp-peap@example.com"	PEAP
+"erp-peap@example.com"	MSCHAPV2	"password"	[2]
+"erp-psk@example.com"	PSK	0123456789abcdef0123456789abcdef
+"erp-pwd@example.com"	PWD	"secret password"
+"erp-sake@example.com"	SAKE	0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+"erp-tls@example.com"	TLS
+"erp-ttls@example.com"	TTLS
+"erp-ttls@example.com"	TTLS-PAP	"password"	[2]
+"erp-ikev2@example.com"	IKEV2	"password"
+
+"vlan1"	PAX	0123456789abcdef0123456789abcdef
+radius_accept_attr=64:d:13
+radius_accept_attr=65:d:6
+radius_accept_attr=81:s:1
+
+"vlan2"	PAX	0123456789abcdef0123456789abcdef
+radius_accept_attr=64:d:13
+radius_accept_attr=65:d:6
+radius_accept_attr=81:s:2
+
+"test-class"	PAX	0123456789abcdef0123456789abcdef
+radius_accept_attr=25:x:00112233445566778899
+
+"gpsk-cui"	GPSK	"abcdefghijklmnop0123456789abcdef"
+radius_accept_attr=89:s:gpsk-chargeable-user-identity
+radius_accept_attr=25:x:00112233445566778899aa
+
+"gpsk-user-session-timeout"	GPSK	"abcdefghijklmnop0123456789abcdef"
+radius_accept_attr=27:d:3
+
+"020000000000"	MACACL	"020000000000"
+
+"020000000100"	MACACL	"020000000100"
+radius_accept_attr=1:s:test-user
+radius_accept_attr=89:s:macacl-cui-test
+
+"0232010000000000@ttls"	TTLS,AKA
+"0232010000000000@peap"	PEAP,AKA
+"0232010000000000@fast"	FAST,AKA
+"6555444333222111@both" AKA',AKA
+
+"0"*		AKA
+"1"*		SIM
+"2"*		AKA
+"3"*		SIM
+"4"*		AKA
+"5"*		SIM
+"6"*		AKA'
+"7"*		AKA'
+"8"*		AKA'
+*		TTLS,TLS,PEAP,FAST,SIM,AKA',AKA
+
+"0"*		AKA	[2]
+"1"*		SIM	[2]
+"2"*		AKA	[2]
+"3"*		SIM	[2]
+"4"*		AKA	[2]
+"5"*		SIM	[2]
+"6"*		AKA'	[2]
+"7"*		AKA'	[2]
+"8"*		AKA'	[2]
+
+"pap user"	TTLS-PAP	"password"	[2]
+"pap-secret"	TTLS-PAP	"63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25"	[2]
+"pap-secret@example.com"	TTLS-PAP	"63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25"	[2]
+"chap user"	TTLS-CHAP	"password"	[2]
+"mschap user"	TTLS-MSCHAP	"password"	[2]
+"DOMAIN\mschapv2 user"	TTLS-MSCHAPV2	hash:8846f7eaee8fb117ad06bdd830b7586c	[2]
+"hs20-test"	TTLS-MSCHAPV2	"password"	[2]
+"utf8-user"	TTLS-MSCHAPV2	"secret-åäö-€-password"	[2]
+"utf8-user-hash"	TTLS-MSCHAPV2	hash:bd5844fad2489992da7fe8c5a01559cf	[2]
+
+"user"	MSCHAPV2,MD5,GTC	"password"	[2]
+"user2"	MSCHAPV2,MD5,GTC	"password"	[2]
+"DOMAIN\user3"	MSCHAPV2	"password"	[2]
+"user-no-passwd"	MSCHAPV2,MD5,GTC	[2]
+"cert user"	TLS	[2]
+
+"hs20-deauth-test"	TTLS-MSCHAPV2	"password"	[2]
+radius_accept_attr=26:x:00009f680405016400
+
+"hs20-subrem-test"	TTLS-MSCHAPV2	"password"	[2]
+radius_accept_attr=26:x:00009f6801170168747470733a2f2f6578616d706c652e636f6d2f
+
+"hs20-session-info-test"	TTLS-MSCHAPV2	"password"	[2]
+radius_accept_attr=27:d:63
+radius_accept_attr=26:x:00009f6805170168747470733a2f2f6578616d706c652e636f6d2f
diff --git a/hostap/tests/hwsim/auth_serv/eap_user_vlan.conf b/hostap/tests/hwsim/auth_serv/eap_user_vlan.conf
new file mode 100644
index 0000000..e5ad0d7
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/eap_user_vlan.conf
@@ -0,0 +1,4 @@
+"vlan1"	PAX	0123456789abcdef0123456789abcdef
+radius_accept_attr=64:d:13
+radius_accept_attr=65:d:6
+radius_accept_attr=81:s:2
diff --git a/hostap/tests/hwsim/auth_serv/ec-ca-openssl.cnf b/hostap/tests/hwsim/auth_serv/ec-ca-openssl.cnf
new file mode 100644
index 0000000..c803dd3
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ec-ca-openssl.cnf
@@ -0,0 +1,111 @@
+# OpenSSL configuration file for Suite B
+
+HOME			= .
+RANDFILE		= $ENV::HOME/.rnd
+oid_section		= new_oids
+
+[ new_oids ]
+
+[ ca ]
+default_ca	= CA_default
+
+[ CA_default ]
+
+dir		= ./ec-ca
+certs		= $dir/certs
+crl_dir		= $dir/crl
+database	= $dir/index.txt
+#unique_subject	= no
+new_certs_dir	= $dir/newcerts
+certificate	= $dir/cacert.pem
+serial		= $dir/serial
+crlnumber	= $dir/crlnumber
+crl		= $dir/crl.pem
+private_key	= $dir/private/cakey.pem
+RANDFILE	= $dir/private/.rand
+
+x509_extensions	= ext_client
+
+name_opt 	= ca_default
+cert_opt 	= ca_default
+
+copy_extensions = copy
+
+default_days	= 365
+default_crl_days= 30
+default_md	= default
+preserve	= no
+
+policy		= policy_match
+
+[ 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 ]
+distinguished_name	= req_distinguished_name
+attributes		= req_attributes
+x509_extensions	= v3_ca
+
+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		= Helsinki
+
+0.organizationName		= Organization Name (eg, company)
+0.organizationName_default	= w1.fi
+
+commonName			= Common Name (e.g. server FQDN or YOUR name)
+#@CN@
+commonName_max			= 64
+
+[ req_attributes ]
+
+[ v3_ca ]
+
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer
+basicConstraints = critical, CA:true, pathlen:0
+keyUsage = critical, cRLSign, keyCertSign
+
+[ crl_ext ]
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ ext_client ]
+
+basicConstraints=CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+#@ALTNAME@
+extendedKeyUsage = clientAuth
+keyUsage = digitalSignature, keyEncipherment
+
+[ ext_server ]
+
+basicConstraints=critical, CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+#@ALTNAME@
+extendedKeyUsage = critical, serverAuth
+keyUsage = digitalSignature, keyEncipherment
diff --git a/hostap/tests/hwsim/auth_serv/ec-ca.pem b/hostap/tests/hwsim/auth_serv/ec-ca.pem
new file mode 100644
index 0000000..a04b886
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ec-ca.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICAjCCAaegAwIBAgIJANry4MnEh6ybMAoGCCqGSM49BAMCMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTI4LWJpdCBSb290IENBMB4XDTE1MDEyNTExMjk1M1oXDTI1MDEy
+MjExMjk1M1owUjELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMQ4wDAYD
+VQQKDAV3MS5maTEgMB4GA1UEAwwXU3VpdGUgQiAxMjgtYml0IFJvb3QgQ0EwWTAT
+BgcqhkjOPQIBBggqhkjOPQMBBwNCAASqUNEASvF83W/PA2xqq/2fhIgZeLdSnnLc
+0yLcjku5WvpLHGy/pLhRsvghtjWjTsgqBqfeW8tq0ywsUdY0ylsNo2YwZDAdBgNV
+HQ4EFgQU/IP6SzTrGV4cfeWF7Mf8IfXodWgwHwYDVR0jBBgwFoAU/IP6SzTrGV4c
+feWF7Mf8IfXodWgwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYw
+CgYIKoZIzj0EAwIDSQAwRgIhAIfEWvUO4+28moKfVL8RXbKKexTZk82UCRL2yi01
+c81AAiEAxBGPZU0vnwxjAaCOhRIH+5X9PDkdLSs25S4ua6BicT8=
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/ec-generate.sh b/hostap/tests/hwsim/auth_serv/ec-generate.sh
new file mode 100755
index 0000000..c9fdabc
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ec-generate.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+OPENSSL=openssl
+
+CURVE=prime256v1
+DIGEST="-sha256"
+DIGEST_CA="-md sha256"
+
+echo
+echo "---[ Root CA ]----------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+	sed "s/#@CN@/commonName_default = Suite B 128-bit Root CA/" \
+	> ec-ca-openssl.cnf.tmp
+$OPENSSL ecparam -out ec-ca.key -name $CURVE -genkey
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -x509 -new -key ec-ca.key -out ec-ca.pem -outform PEM -days 3650 $DIGEST
+mkdir -p ec-ca/certs ec-ca/crl ec-ca/newcerts ec-ca/private
+touch ec-ca/index.txt
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ Server ]-----------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+	sed "s/#@CN@/commonName_default = server.w1.fi/" |
+	sed "s/#@ALTNAME@/subjectAltName=critical,DNS:server.w1.fi/" \
+	> ec-ca-openssl.cnf.tmp
+$OPENSSL ecparam -out ec-server.key -name $CURVE -genkey
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -new -nodes -key ec-server.key -out ec-server.req -outform PEM $DIGEST
+$OPENSSL ca -config ec-ca-openssl.cnf.tmp -batch -keyfile ec-ca.key -cert ec-ca.pem -create_serial -in ec-server.req -out ec-server.pem -extensions ext_server $DIGEST_CA
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ User ]-------------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+	sed "s/#@CN@/commonName_default = user/" |
+	sed "s/#@ALTNAME@/subjectAltName=email:user@w1.fi/" \
+	> ec-ca-openssl.cnf.tmp
+$OPENSSL ecparam -out ec-user.key -name $CURVE -genkey
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -new -nodes -key ec-user.key -out ec-user.req -outform PEM -extensions ext_client $DIGEST
+$OPENSSL ca -config ec-ca-openssl.cnf.tmp -batch -keyfile ec-ca.key -cert ec-ca.pem -create_serial -in ec-user.req -out ec-user.pem -extensions ext_client $DIGEST_CA
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ Verify ]-----------------------------------------------------------"
+echo
+
+$OPENSSL verify -CAfile ec-ca.pem ec-server.pem
+$OPENSSL verify -CAfile ec-ca.pem ec-user.pem
diff --git a/hostap/tests/hwsim/auth_serv/ec-server.key b/hostap/tests/hwsim/auth_serv/ec-server.key
new file mode 100644
index 0000000..391e9ed
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ec-server.key
@@ -0,0 +1,8 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIN/qNiKLsQDpQWumSiRRF6LM7TP7GTwdS8vG7xP8vKz/oAoGCCqGSM49
+AwEHoUQDQgAEvl8WCLIK1vIZbxQZ7yDyKzzgvoxlhl+VwbuQNuzcWTq6QJqdEXbH
+gFohTPzAXxlSyHi45Uz6yWrR/uq2OldcmQ==
+-----END EC PRIVATE KEY-----
diff --git a/hostap/tests/hwsim/auth_serv/ec-server.pem b/hostap/tests/hwsim/auth_serv/ec-server.pem
new file mode 100644
index 0000000..4222b1e
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ec-server.pem
@@ -0,0 +1,53 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 9573410140069116734 (0x84db95ccdff13b3e)
+    Signature Algorithm: ecdsa-with-SHA256
+        Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B 128-bit Root CA
+        Validity
+            Not Before: Jan 25 11:29:53 2015 GMT
+            Not After : Jan 25 11:29:53 2016 GMT
+        Subject: C=FI, O=w1.fi, CN=server.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: id-ecPublicKey
+                Public-Key: (256 bit)
+                pub: 
+                    04:be:5f:16:08:b2:0a:d6:f2:19:6f:14:19:ef:20:
+                    f2:2b:3c:e0:be:8c:65:86:5f:95:c1:bb:90:36:ec:
+                    dc:59:3a:ba:40:9a:9d:11:76:c7:80:5a:21:4c:fc:
+                    c0:5f:19:52:c8:78:b8:e5:4c:fa:c9:6a:d1:fe:ea:
+                    b6:3a:57:5c:99
+                ASN1 OID: prime256v1
+        X509v3 extensions:
+            X509v3 Basic Constraints: critical
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                6E:21:26:96:72:29:39:BF:8B:EF:EB:65:CD:E0:4E:97:6F:1A:2C:E5
+            X509v3 Authority Key Identifier: 
+                keyid:FC:83:FA:4B:34:EB:19:5E:1C:7D:E5:85:EC:C7:FC:21:F5:E8:75:68
+
+            X509v3 Subject Alternative Name: critical
+                DNS:server.w1.fi
+            X509v3 Extended Key Usage: critical
+                TLS Web Server Authentication
+            X509v3 Key Usage: 
+                Digital Signature, Key Encipherment
+    Signature Algorithm: ecdsa-with-SHA256
+         30:44:02:20:47:b1:5e:57:ae:6c:0b:df:78:11:79:5c:b2:60:
+         fd:0c:9c:37:18:19:fe:c1:b6:ca:f6:4f:62:63:13:ff:ff:64:
+         02:20:07:1f:3b:1d:c7:d8:fe:ff:26:0b:68:d0:85:bc:01:15:
+         62:e4:7f:f4:c7:e4:ad:d5:da:40:44:5a:0b:f5:72:9e
+-----BEGIN CERTIFICATE-----
+MIICDzCCAbagAwIBAgIJAITblczf8Ts+MAoGCCqGSM49BAMCMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTI4LWJpdCBSb290IENBMB4XDTE1MDEyNTExMjk1M1oXDTE2MDEy
+NTExMjk1M1owNDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMRUwEwYDVQQD
+DAxzZXJ2ZXIudzEuZmkwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS+XxYIsgrW
+8hlvFBnvIPIrPOC+jGWGX5XBu5A27NxZOrpAmp0RdseAWiFM/MBfGVLIeLjlTPrJ
+atH+6rY6V1yZo4GSMIGPMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFG4hJpZyKTm/
+i+/rZc3gTpdvGizlMB8GA1UdIwQYMBaAFPyD+ks06xleHH3lhezH/CH16HVoMBoG
+A1UdEQEB/wQQMA6CDHNlcnZlci53MS5maTAWBgNVHSUBAf8EDDAKBggrBgEFBQcD
+ATALBgNVHQ8EBAMCBaAwCgYIKoZIzj0EAwIDRwAwRAIgR7FeV65sC994EXlcsmD9
+DJw3GBn+wbbK9k9iYxP//2QCIAcfOx3H2P7/Jgto0IW8ARVi5H/0x+St1dpARFoL
+9XKe
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/ec-user.key b/hostap/tests/hwsim/auth_serv/ec-user.key
new file mode 100644
index 0000000..e390c06
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ec-user.key
@@ -0,0 +1,8 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIL52ZfaYm8GAzhot94BCQriTmQEq2+JPkS+HCwUpLuwaoAoGCCqGSM49
+AwEHoUQDQgAEnE2sSN8ZOateUoi3Ao0VewSH+1ceTf+NkiJpoymO6U6q0CSlG2bp
+dZyBk+6UIOD9WiCi2tN+QGbvPnPrlLfBOg==
+-----END EC PRIVATE KEY-----
diff --git a/hostap/tests/hwsim/auth_serv/ec-user.pem b/hostap/tests/hwsim/auth_serv/ec-user.pem
new file mode 100644
index 0000000..9a6aba8
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ec-user.pem
@@ -0,0 +1,52 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 9573410140069116735 (0x84db95ccdff13b3f)
+    Signature Algorithm: ecdsa-with-SHA256
+        Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B 128-bit Root CA
+        Validity
+            Not Before: Jan 25 11:29:53 2015 GMT
+            Not After : Jan 25 11:29:53 2016 GMT
+        Subject: C=FI, O=w1.fi, CN=user
+        Subject Public Key Info:
+            Public Key Algorithm: id-ecPublicKey
+                Public-Key: (256 bit)
+                pub: 
+                    04:9c:4d:ac:48:df:19:39:ab:5e:52:88:b7:02:8d:
+                    15:7b:04:87:fb:57:1e:4d:ff:8d:92:22:69:a3:29:
+                    8e:e9:4e:aa:d0:24:a5:1b:66:e9:75:9c:81:93:ee:
+                    94:20:e0:fd:5a:20:a2:da:d3:7e:40:66:ef:3e:73:
+                    eb:94:b7:c1:3a
+                ASN1 OID: prime256v1
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                89:28:76:9A:42:DB:B6:F8:36:97:63:8F:7D:0A:EA:0B:FE:66:2B:CD
+            X509v3 Authority Key Identifier: 
+                keyid:FC:83:FA:4B:34:EB:19:5E:1C:7D:E5:85:EC:C7:FC:21:F5:E8:75:68
+
+            X509v3 Subject Alternative Name: 
+                email:user@w1.fi
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication
+            X509v3 Key Usage: 
+                Digital Signature, Key Encipherment
+    Signature Algorithm: ecdsa-with-SHA256
+         30:45:02:20:26:84:14:f6:50:ac:ed:da:88:27:6d:18:d5:b3:
+         2c:c8:59:ea:2a:c3:ae:69:03:79:0d:66:5e:5f:a5:52:27:92:
+         02:21:00:db:8d:fd:58:e5:22:9b:17:32:57:34:e9:2e:30:da:
+         1d:77:4c:15:18:9b:7d:e4:5d:bc:64:cd:21:ff:57:df:16
+-----BEGIN CERTIFICATE-----
+MIIB/TCCAaOgAwIBAgIJAITblczf8Ts/MAoGCCqGSM49BAMCMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTI4LWJpdCBSb290IENBMB4XDTE1MDEyNTExMjk1M1oXDTE2MDEy
+NTExMjk1M1owLDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMQ0wCwYDVQQD
+DAR1c2VyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnE2sSN8ZOateUoi3Ao0V
+ewSH+1ceTf+NkiJpoymO6U6q0CSlG2bpdZyBk+6UIOD9WiCi2tN+QGbvPnPrlLfB
+OqOBhzCBhDAJBgNVHRMEAjAAMB0GA1UdDgQWBBSJKHaaQtu2+DaXY499CuoL/mYr
+zTAfBgNVHSMEGDAWgBT8g/pLNOsZXhx95YXsx/wh9eh1aDAVBgNVHREEDjAMgQp1
+c2VyQHcxLmZpMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAsGA1UdDwQEAwIFoDAKBggq
+hkjOPQQDAgNIADBFAiAmhBT2UKzt2ognbRjVsyzIWeoqw65pA3kNZl5fpVInkgIh
+ANuN/VjlIpsXMlc06S4w2h13TBUYm33kXbxkzSH/V98W
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/ec2-ca.pem b/hostap/tests/hwsim/auth_serv/ec2-ca.pem
new file mode 100644
index 0000000..0054ba8
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ec2-ca.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICPTCCAcSgAwIBAgIJAL63h7lu0KZpMAoGCCqGSM49BAMDMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTkyLWJpdCBSb290IENBMB4XDTE1MDEyNTExMzIwM1oXDTI1MDEy
+MjExMzIwM1owUjELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMQ4wDAYD
+VQQKDAV3MS5maTEgMB4GA1UEAwwXU3VpdGUgQiAxOTItYml0IFJvb3QgQ0EwdjAQ
+BgcqhkjOPQIBBgUrgQQAIgNiAAQjdOMC9bqcDR9/SaOhxNbmQLQTGZfhtmoxHkJL
+5GG3bwW5hYA2jYHWU84H+mR6om6fg78G+IxjLly2OWiByYUeWDcsYqLGj3UHHaVv
+rIitaRPyg3dExemnmK3zjgXnoaajZjBkMB0GA1UdDgQWBBSuBbynInvH0vn8IKZc
+MbtBTo9svTAfBgNVHSMEGDAWgBSuBbynInvH0vn8IKZcMbtBTo9svTASBgNVHRMB
+Af8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjBZ
+vEbGRDQNvzAY3nfYrsrE2Dd11smT6zv0mIvDeQCktbISGpStRBQjAaFjcCyjDEkC
+MH7ywcqJe+mpWDt5xFJvB52iZ7rX7rO0OX0qmjI38PC0IOo7euJdfcC1gHdSoAW3
+bA==
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/ec2-generate.sh b/hostap/tests/hwsim/auth_serv/ec2-generate.sh
new file mode 100755
index 0000000..5a8d2d2
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ec2-generate.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+OPENSSL=openssl
+
+CURVE=secp384r1
+DIGEST="-sha384"
+DIGEST_CA="-md sha384"
+
+echo
+echo "---[ Root CA ]----------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+	sed "s/#@CN@/commonName_default = Suite B 192-bit Root CA/" \
+	> ec-ca-openssl.cnf.tmp
+$OPENSSL ecparam -out ec2-ca.key -name $CURVE -genkey
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -x509 -new -key ec2-ca.key -out ec2-ca.pem -outform PEM -days 3650 $DIGEST
+mkdir -p ec-ca/certs ec-ca/crl ec-ca/newcerts ec-ca/private
+touch ec-ca/index.txt
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ Server ]-----------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+	sed "s/#@CN@/commonName_default = server.w1.fi/" |
+	sed "s/#@ALTNAME@/subjectAltName=critical,DNS:server.w1.fi/" \
+	> ec-ca-openssl.cnf.tmp
+$OPENSSL ecparam -out ec2-server.key -name $CURVE -genkey
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -new -nodes -key ec2-server.key -out ec2-server.req -outform PEM $DIGEST
+$OPENSSL ca -config ec-ca-openssl.cnf.tmp -batch -keyfile ec2-ca.key -cert ec2-ca.pem -create_serial -in ec2-server.req -out ec2-server.pem -extensions ext_server $DIGEST_CA
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ User ]-------------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+	sed "s/#@CN@/commonName_default = user/" |
+	sed "s/#@ALTNAME@/subjectAltName=email:user@w1.fi/" \
+	> ec-ca-openssl.cnf.tmp
+$OPENSSL ecparam -out ec2-user.key -name $CURVE -genkey
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -new -nodes -key ec2-user.key -out ec2-user.req -outform PEM -extensions ext_client $DIGEST
+$OPENSSL ca -config ec-ca-openssl.cnf.tmp -batch -keyfile ec2-ca.key -cert ec2-ca.pem -create_serial -in ec2-user.req -out ec2-user.pem -extensions ext_client $DIGEST_CA
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ Verify ]-----------------------------------------------------------"
+echo
+
+$OPENSSL verify -CAfile ec2-ca.pem ec2-server.pem
+$OPENSSL verify -CAfile ec2-ca.pem ec2-user.pem
diff --git a/hostap/tests/hwsim/auth_serv/ec2-server.key b/hostap/tests/hwsim/auth_serv/ec2-server.key
new file mode 100644
index 0000000..9cd76ee
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ec2-server.key
@@ -0,0 +1,9 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDCjaz/zVDXqNO/XprtliomKOC6QjbBFgsF2YwUAtKB5ukL4miVGNyCu
+jIlq9eUD1x6gBwYFK4EEACKhZANiAARWq1ut1b6ctOFBkEOjULL3VjJFP15g0gk+
+sBTMBogU5WRN7Qod/jfem5k4O7FKYZNAarDFMh2yDMXZvRooiNyL0AH2wk0qzN5u
+n02JOt9Q76TVYflE91C5DTxjgLOgxBw=
+-----END EC PRIVATE KEY-----
diff --git a/hostap/tests/hwsim/auth_serv/ec2-server.pem b/hostap/tests/hwsim/auth_serv/ec2-server.pem
new file mode 100644
index 0000000..a7cc37e
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ec2-server.pem
@@ -0,0 +1,58 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 9347590364512421238 (0x81b94fe92ea08576)
+    Signature Algorithm: ecdsa-with-SHA384
+        Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B 192-bit Root CA
+        Validity
+            Not Before: Jan 25 11:32:03 2015 GMT
+            Not After : Jan 25 11:32:03 2016 GMT
+        Subject: C=FI, O=w1.fi, CN=server.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: id-ecPublicKey
+                Public-Key: (384 bit)
+                pub: 
+                    04:56:ab:5b:ad:d5:be:9c:b4:e1:41:90:43:a3:50:
+                    b2:f7:56:32:45:3f:5e:60:d2:09:3e:b0:14:cc:06:
+                    88:14:e5:64:4d:ed:0a:1d:fe:37:de:9b:99:38:3b:
+                    b1:4a:61:93:40:6a:b0:c5:32:1d:b2:0c:c5:d9:bd:
+                    1a:28:88:dc:8b:d0:01:f6:c2:4d:2a:cc:de:6e:9f:
+                    4d:89:3a:df:50:ef:a4:d5:61:f9:44:f7:50:b9:0d:
+                    3c:63:80:b3:a0:c4:1c
+                ASN1 OID: secp384r1
+        X509v3 extensions:
+            X509v3 Basic Constraints: critical
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                19:D7:57:D0:3B:91:84:A4:AF:93:03:32:3C:AB:C4:F9:A7:B0:27:19
+            X509v3 Authority Key Identifier: 
+                keyid:AE:05:BC:A7:22:7B:C7:D2:F9:FC:20:A6:5C:31:BB:41:4E:8F:6C:BD
+
+            X509v3 Subject Alternative Name: critical
+                DNS:server.w1.fi
+            X509v3 Extended Key Usage: critical
+                TLS Web Server Authentication
+            X509v3 Key Usage: 
+                Digital Signature, Key Encipherment
+    Signature Algorithm: ecdsa-with-SHA384
+         30:65:02:30:79:68:37:af:eb:46:e8:77:35:13:77:d5:db:eb:
+         f9:75:40:cd:d4:3d:0a:03:ec:67:a0:22:fe:65:f5:d7:ca:53:
+         4a:85:f5:14:4b:41:f9:b9:98:a6:85:8b:ac:e0:c8:6c:02:31:
+         00:83:12:02:be:93:2b:c2:00:74:ec:cb:fc:5a:8c:a6:5e:52:
+         ee:20:76:3d:73:2b:fb:fe:60:4c:52:f3:bc:1e:4c:e8:f9:ea:
+         f6:e2:f6:ca:c6:a8:3b:2d:9a:17:eb:4d:0a
+-----BEGIN CERTIFICATE-----
+MIICTTCCAdOgAwIBAgIJAIG5T+kuoIV2MAoGCCqGSM49BAMDMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTkyLWJpdCBSb290IENBMB4XDTE1MDEyNTExMzIwM1oXDTE2MDEy
+NTExMzIwM1owNDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMRUwEwYDVQQD
+DAxzZXJ2ZXIudzEuZmkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARWq1ut1b6ctOFB
+kEOjULL3VjJFP15g0gk+sBTMBogU5WRN7Qod/jfem5k4O7FKYZNAarDFMh2yDMXZ
+vRooiNyL0AH2wk0qzN5un02JOt9Q76TVYflE91C5DTxjgLOgxByjgZIwgY8wDAYD
+VR0TAQH/BAIwADAdBgNVHQ4EFgQUGddX0DuRhKSvkwMyPKvE+aewJxkwHwYDVR0j
+BBgwFoAUrgW8pyJ7x9L5/CCmXDG7QU6PbL0wGgYDVR0RAQH/BBAwDoIMc2VydmVy
+LncxLmZpMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMBMAsGA1UdDwQEAwIFoDAKBggq
+hkjOPQQDAwNoADBlAjB5aDev60bodzUTd9Xb6/l1QM3UPQoD7GegIv5l9dfKU0qF
+9RRLQfm5mKaFi6zgyGwCMQCDEgK+kyvCAHTsy/xajKZeUu4gdj1zK/v+YExS87we
+TOj56vbi9srGqDstmhfrTQo=
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/ec2-user.key b/hostap/tests/hwsim/auth_serv/ec2-user.key
new file mode 100644
index 0000000..adfa937
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ec2-user.key
@@ -0,0 +1,9 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDB7tpaHBuZZG+MVYjRVpZvZvZxxFOu/reH2Ms3DiBH5DHW7dLP7T4Gs
+X+yw8bQZwCqgBwYFK4EEACKhZANiAATJYVk5woo/LAFd+znRAoMOClGXtfO2yZlp
+3n6jYUsG48W03XOlYd2/aCJVtGp6SwRVxumfYT4TejEj/Ky44vOlmQ9pasNfMYYN
+kpHcAWJ8sV7mP7LM9YVksksfhon91+E=
+-----END EC PRIVATE KEY-----
diff --git a/hostap/tests/hwsim/auth_serv/ec2-user.pem b/hostap/tests/hwsim/auth_serv/ec2-user.pem
new file mode 100644
index 0000000..ef86cd1
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ec2-user.pem
@@ -0,0 +1,57 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 9347590364512421239 (0x81b94fe92ea08577)
+    Signature Algorithm: ecdsa-with-SHA384
+        Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B 192-bit Root CA
+        Validity
+            Not Before: Jan 25 11:32:03 2015 GMT
+            Not After : Jan 25 11:32:03 2016 GMT
+        Subject: C=FI, O=w1.fi, CN=user
+        Subject Public Key Info:
+            Public Key Algorithm: id-ecPublicKey
+                Public-Key: (384 bit)
+                pub: 
+                    04:c9:61:59:39:c2:8a:3f:2c:01:5d:fb:39:d1:02:
+                    83:0e:0a:51:97:b5:f3:b6:c9:99:69:de:7e:a3:61:
+                    4b:06:e3:c5:b4:dd:73:a5:61:dd:bf:68:22:55:b4:
+                    6a:7a:4b:04:55:c6:e9:9f:61:3e:13:7a:31:23:fc:
+                    ac:b8:e2:f3:a5:99:0f:69:6a:c3:5f:31:86:0d:92:
+                    91:dc:01:62:7c:b1:5e:e6:3f:b2:cc:f5:85:64:b2:
+                    4b:1f:86:89:fd:d7:e1
+                ASN1 OID: secp384r1
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                75:EA:7B:CE:8A:99:D2:E7:77:B4:3B:80:68:59:E9:B6:88:B2:FA:F6
+            X509v3 Authority Key Identifier: 
+                keyid:AE:05:BC:A7:22:7B:C7:D2:F9:FC:20:A6:5C:31:BB:41:4E:8F:6C:BD
+
+            X509v3 Subject Alternative Name: 
+                email:user@w1.fi
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication
+            X509v3 Key Usage: 
+                Digital Signature, Key Encipherment
+    Signature Algorithm: ecdsa-with-SHA384
+         30:65:02:31:00:c2:b7:35:4e:5e:d1:da:7f:35:a0:ac:54:92:
+         18:08:0d:9c:86:e9:4e:cf:3a:09:48:23:eb:4d:56:77:e5:d0:
+         e7:b0:55:b3:0e:91:2d:f8:3e:1c:4e:0d:b7:32:dc:11:1b:02:
+         30:49:c2:6b:63:39:3c:4b:d9:e9:8d:b9:ce:6e:8e:9f:88:43:
+         03:e0:5f:7e:75:44:12:66:f8:c6:ae:8e:f1:da:10:02:36:8c:
+         7b:a2:89:a0:05:3b:c6:39:d6:e1:7a:b7:85
+-----BEGIN CERTIFICATE-----
+MIICOjCCAcCgAwIBAgIJAIG5T+kuoIV3MAoGCCqGSM49BAMDMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTkyLWJpdCBSb290IENBMB4XDTE1MDEyNTExMzIwM1oXDTE2MDEy
+NTExMzIwM1owLDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMQ0wCwYDVQQD
+DAR1c2VyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEyWFZOcKKPywBXfs50QKDDgpR
+l7XztsmZad5+o2FLBuPFtN1zpWHdv2giVbRqeksEVcbpn2E+E3oxI/ysuOLzpZkP
+aWrDXzGGDZKR3AFifLFe5j+yzPWFZLJLH4aJ/dfho4GHMIGEMAkGA1UdEwQCMAAw
+HQYDVR0OBBYEFHXqe86KmdLnd7Q7gGhZ6baIsvr2MB8GA1UdIwQYMBaAFK4FvKci
+e8fS+fwgplwxu0FOj2y9MBUGA1UdEQQOMAyBCnVzZXJAdzEuZmkwEwYDVR0lBAww
+CgYIKwYBBQUHAwIwCwYDVR0PBAQDAgWgMAoGCCqGSM49BAMDA2gAMGUCMQDCtzVO
+XtHafzWgrFSSGAgNnIbpTs86CUgj601Wd+XQ57BVsw6RLfg+HE4NtzLcERsCMEnC
+a2M5PEvZ6Y25zm6On4hDA+BffnVEEmb4xq6O8doQAjaMe6KJoAU7xjnW4Xq3hQ==
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/hlr_auc_gw.gsm b/hostap/tests/hwsim/auth_serv/hlr_auc_gw.gsm
new file mode 100644
index 0000000..b67aeca
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/hlr_auc_gw.gsm
@@ -0,0 +1,17 @@
+# Test triplets generated with GSM-Milenage using
+# Ki = 90dca4eda45b53cf0f12d7c9c3bc6a89
+# OPc = cb9cccc4b9258e6dca4760379fb82581
+
+# GSM authentication triplet file for EAP-SIM authenticator
+# IMSI:Kc:SRES:RAND
+# IMSI: ASCII string (numbers)
+# Kc: hex, 8 octets
+# SRES: hex, 4 octets
+# RAND: hex, 16 octets
+
+232010000000001:79747302dd684291:fbe55c44:d29b2f51f1fd20304ad0c447b4dcdc37
+232010000000001:2f2eaa1d83e43813:6e2e3ea3:e19a8e96255b88e8a8be104637d165b2
+232010000000001:b7c935bfb51f2c5a:257581f5:8079c338eb4195d0fe2d46b357979054
+232010000000001:bc93df6af0412a69:dae1faa0:a48b8e2a59b8bed468ea3d57ef9ee118
+232010000000001:626db3b0e9e321c3:a3e33208:38e7e65d0c0ef82185d1697410f2b31a
+232010000000001:df3cab53d00c622e:0b785f5d:d8a4a9efe1689d232468f316d2a84270
diff --git a/hostap/tests/hwsim/auth_serv/hlr_auc_gw.milenage_db b/hostap/tests/hwsim/auth_serv/hlr_auc_gw.milenage_db
new file mode 100644
index 0000000..ecd06d7
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/hlr_auc_gw.milenage_db
@@ -0,0 +1,13 @@
+# Parameters for Milenage (Example algorithms for AKA).
+# The example Ki, OPc, and AMF values here are from 3GPP TS 35.208 v6.0.0
+# 4.3.20 Test Set 20. SQN is the last used SQN value.
+# These values can be used for both UMTS (EAP-AKA) and GSM (EAP-SIM)
+# authentication. In case of GSM/EAP-SIM, AMF and SQN values are not used, but
+# dummy values will need to be included in this file.
+
+# IMSI Ki OPc AMF SQN
+232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000
+
+# These values are from Test Set 19 which has the AMF separation bit set to 1
+# and as such, is suitable for EAP-AKA' test.
+555444333222111 5122250214c33e723a5dd523fc145fc0 981d464c7c52eb6e5036234984ad0bcf c3ab 16f3b3f70fc1
diff --git a/hostap/tests/hwsim/auth_serv/index-revoked.txt b/hostap/tests/hwsim/auth_serv/index-revoked.txt
new file mode 100644
index 0000000..95b052e
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/index-revoked.txt
@@ -0,0 +1,8 @@
+V	230627164122Z		D8D3E3A6CBE3CCC1	unknown	/C=FI/O=w1.fi/CN=Root CA
+V	150215075930Z		D8D3E3A6CBE3CCC9	unknown	/C=FI/O=w1.fi/CN=server3.w1.fi
+V	140102000000Z		D8D3E3A6CBE3CCCA	unknown	/C=FI/O=w1.fi/CN=server4.w1.fi
+V	150215083008Z		D8D3E3A6CBE3CCCB	unknown	/C=FI/O=w1.fi/CN=server5.w1.fi
+V	150228224144Z		D8D3E3A6CBE3CCCC	unknown	/C=FI/O=w1.fi/CN=server6.w1.fi
+V	160111185024Z		D8D3E3A6CBE3CCCD	unknown	/C=FI/O=w1.fi/CN=ocsp.w1.fi
+R	150929211122Z	160111185024Z	D8D3E3A6CBE3CCD0	unknown	/C=FI/O=w1.fi/CN=server.w1.fi
+R	150929211300Z	160111185024Z	D8D3E3A6CBE3CCD1	unknown	/C=FI/O=w1.fi/CN=Test User
diff --git a/hostap/tests/hwsim/auth_serv/index-unknown.txt b/hostap/tests/hwsim/auth_serv/index-unknown.txt
new file mode 100644
index 0000000..97dfbba
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/index-unknown.txt
@@ -0,0 +1 @@
+V	230627164122Z		D8D3E3A6CBE3CCC1	unknown	/C=FI/O=w1.fi/CN=Root CA
diff --git a/hostap/tests/hwsim/auth_serv/index.txt b/hostap/tests/hwsim/auth_serv/index.txt
new file mode 100644
index 0000000..52c8e0c
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/index.txt
@@ -0,0 +1,8 @@
+V	230627164122Z		D8D3E3A6CBE3CCC1	unknown	/C=FI/O=w1.fi/CN=Root CA
+V	150215075930Z		D8D3E3A6CBE3CCC9	unknown	/C=FI/O=w1.fi/CN=server3.w1.fi
+V	140102000000Z		D8D3E3A6CBE3CCCA	unknown	/C=FI/O=w1.fi/CN=server4.w1.fi
+V	150215083008Z		D8D3E3A6CBE3CCCB	unknown	/C=FI/O=w1.fi/CN=server5.w1.fi
+V	150228224144Z		D8D3E3A6CBE3CCCC	unknown	/C=FI/O=w1.fi/CN=server6.w1.fi
+V	160111185024Z		D8D3E3A6CBE3CCCD	unknown	/C=FI/O=w1.fi/CN=ocsp.w1.fi
+V	150929211122Z		D8D3E3A6CBE3CCD0	unknown	/C=FI/O=w1.fi/CN=server.w1.fi
+V	150929211300Z		D8D3E3A6CBE3CCD1	unknown	/C=FI/O=w1.fi/CN=Test User
diff --git a/hostap/tests/hwsim/auth_serv/ocsp-req.der b/hostap/tests/hwsim/auth_serv/ocsp-req.der
new file mode 100644
index 0000000..20999b9
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ocsp-req.der
Binary files differ
diff --git a/hostap/tests/hwsim/auth_serv/ocsp-responder.key b/hostap/tests/hwsim/auth_serv/ocsp-responder.key
new file mode 100644
index 0000000..fb866fb
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ocsp-responder.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALJeLx3nLPZsq7AW
+nvoSL7JMyCN7aAh2OIOX9T8FrF3ZganOdZKhvJbGyADuHtfw2orY58DXQsMlYufH
+YPqogkwbznOaq42z/j22fwH+WWRCdagEGActImQnufGvAbTtv6bqkXjRnDD1YTf/
++Rv4Fl9rdzL51+OdDNXDuUMW8DrDAgMBAAECgYAja1yD3aIqFQ5K21MaaX4bM/AS
+S7Eu7Prv9r72ktPVlxmOdLcYNRHUBwk0VhS94NAk/kmXG6fgRI5NZGQ3ojqtOXLV
+VhlcitYAfJvNpyKmFKgdGZQIxaaQr/F2X8tH5yFdIt+6mDOGptTb/S3ljQwNsg59
+7t/jYzSe5mK/Gbw4MQJBAN3sZqGz6ABygLTuTiXhE9sCXDSGy4d8ZWMaajuD7N6k
+sAGKsaiVozeIvg0JNiCMm02A8M/cWjGedDWFxrnvvF8CQQDNwagUpozfXMboibHI
+BNwpUzyri/5bqJ/dU7/sAOA1AZ9yoO5s2WlNutXkG3mDoQCzseG/pNxU403dU0jQ
+wpwdAkEAk5lbWUkSkNmXCL9GcqMUVaFoOfc8/suZkyRKa3L+48Wc2imop3t+przn
+yjvKKDPcRtvvThA8XKwKll53Ict0+QJBAKj7o09Sed/4EmRosdnUI/zMn8dD8mLU
+2narkbQCBCGEc69w/F/pLtLn30K4TdQNJsZuETmT7GDLTee3vtW0/wECQCtyVgw/
+aZ0QTac8ut1oG072qOA2cFGhEuDELlX8JcNy28ygmzn0KS8uiTsq6YVu8V7WCj4X
+EkAZMm19nY5ZE+A=
+-----END PRIVATE KEY-----
diff --git a/hostap/tests/hwsim/auth_serv/ocsp-responder.pem b/hostap/tests/hwsim/auth_serv/ocsp-responder.pem
new file mode 100644
index 0000000..bbde1e8
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ocsp-responder.pem
@@ -0,0 +1,54 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162829 (0xd8d3e3a6cbe3cccd)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Jan 11 18:50:24 2015 GMT
+            Not After : Jan 11 18:50:24 2016 GMT
+        Subject: C=FI, O=w1.fi, CN=ocsp.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:b2:5e:2f:1d:e7:2c:f6:6c:ab:b0:16:9e:fa:12:
+                    2f:b2:4c:c8:23:7b:68:08:76:38:83:97:f5:3f:05:
+                    ac:5d:d9:81:a9:ce:75:92:a1:bc:96:c6:c8:00:ee:
+                    1e:d7:f0:da:8a:d8:e7:c0:d7:42:c3:25:62:e7:c7:
+                    60:fa:a8:82:4c:1b:ce:73:9a:ab:8d:b3:fe:3d:b6:
+                    7f:01:fe:59:64:42:75:a8:04:18:07:2d:22:64:27:
+                    b9:f1:af:01:b4:ed:bf:a6:ea:91:78:d1:9c:30:f5:
+                    61:37:ff:f9:1b:f8:16:5f:6b:77:32:f9:d7:e3:9d:
+                    0c:d5:c3:b9:43:16:f0:3a:c3
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Extended Key Usage: 
+                OCSP Signing
+    Signature Algorithm: sha256WithRSAEncryption
+         41:42:b6:70:4a:70:1f:ad:d9:25:f7:02:94:bd:91:b7:69:ad:
+         31:59:c6:2a:4e:5e:4a:ed:5d:c1:24:09:98:94:15:42:86:2c:
+         b2:9d:62:7a:e0:ec:60:39:47:93:c9:c7:61:01:b5:2c:00:53:
+         86:6e:66:99:ee:b3:57:5d:fb:83:6b:d3:77:26:0c:c7:2d:16:
+         ea:84:69:59:b7:a8:de:35:61:0b:7a:f3:62:1e:1a:94:91:c4:
+         bd:85:4a:63:10:09:11:88:75:c9:f5:57:84:9a:ef:d1:78:29:
+         5e:76:fc:33:76:84:b2:b5:f6:88:cc:fb:f9:cf:9f:b4:88:29:
+         3c:9d
+-----BEGIN CERTIFICATE-----
+MIICDjCCAXegAwIBAgIJANjT46bL48zNMA0GCSqGSIb3DQEBCwUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNTAx
+MTExODUwMjRaFw0xNjAxMTExODUwMjRaMDIxCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTETMBEGA1UEAwwKb2NzcC53MS5maTCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEAsl4vHecs9myrsBae+hIvskzII3toCHY4g5f1PwWsXdmBqc51kqG8
+lsbIAO4e1/DaitjnwNdCwyVi58dg+qiCTBvOc5qrjbP+PbZ/Af5ZZEJ1qAQYBy0i
+ZCe58a8BtO2/puqReNGcMPVhN//5G/gWX2t3MvnX450M1cO5QxbwOsMCAwEAAaMv
+MC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwEwYDVR0lBAwwCgYIKwYBBQUHAwkw
+DQYJKoZIhvcNAQELBQADgYEAQUK2cEpwH63ZJfcClL2Rt2mtMVnGKk5eSu1dwSQJ
+mJQVQoYssp1ieuDsYDlHk8nHYQG1LABThm5mme6zV137g2vTdyYMxy0W6oRpWbeo
+3jVhC3rzYh4alJHEvYVKYxAJEYh1yfVXhJrv0XgpXnb8M3aEsrX2iMz7+c+ftIgp
+PJ0=
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/ocsp-server-cache.der b/hostap/tests/hwsim/auth_serv/ocsp-server-cache.der
new file mode 100644
index 0000000..33e6753
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ocsp-server-cache.der
Binary files differ
diff --git a/hostap/tests/hwsim/auth_serv/ocsp-server-cache.der-invalid b/hostap/tests/hwsim/auth_serv/ocsp-server-cache.der-invalid
new file mode 100644
index 0000000..218bd03
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/ocsp-server-cache.der-invalid
Binary files differ
diff --git a/hostap/tests/hwsim/auth_serv/radius_clients.conf b/hostap/tests/hwsim/auth_serv/radius_clients.conf
new file mode 100644
index 0000000..7e34015
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/radius_clients.conf
@@ -0,0 +1 @@
+0.0.0.0/0	radius
diff --git a/hostap/tests/hwsim/auth_serv/radius_clients_ipv6.conf b/hostap/tests/hwsim/auth_serv/radius_clients_ipv6.conf
new file mode 100644
index 0000000..8723efc
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/radius_clients_ipv6.conf
@@ -0,0 +1 @@
+::1	radius
diff --git a/hostap/tests/hwsim/auth_serv/server-eku-client-server.key b/hostap/tests/hwsim/auth_serv/server-eku-client-server.key
new file mode 100644
index 0000000..ce2e5f2
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/server-eku-client-server.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMowHv0TagIoUZoO
+qR5yfudayMsMfoqZgY0FswmwqYbnrkT64Mfu8xi0MWXjBW9mTuPkhYGbR39ftRYr
+sFmRnMVV09PKLIHO8CeoVN4OT9jwEb0LEFY4Jt+pOpUVk6YW7dIetLXAqGGOrhAE
+/eYmykoNkEu5rMmU8rFrl2tgJOq9AgMBAAECgYAdONdBvIyVwz4IBhZrUCEHTxe2
+QRgI8CbJOwmlXOMjnFiTn67dNqvr5h89mpIuh5rfVSf2k3rB7hM+IRJb36/Ik7qg
+GdktPSEIK/ktUcfofVLaLn+ehG7vXhkkB6juBR7jaXDZRBPvFM+TCtirlaZ5sQ0u
+TbSw7m9NcFD2APxgAQJBAPIoCxZCJGpMvh+5ta8EJQVQKhJeMWmDlUQvscKTauWb
+aTz0z+OMBGpZH7DWCTww4+/3fjqZt/TURuPSh0ZcACUCQQDVvyPTO3h3R5fig/zV
+NV8E0/dCYH6kwsFk0AUIRbMHdaN3sEHWszKG9nTNyPyHhDo8i9jguSjkb9MwdgR7
+BJC5AkBB6/bAs3bYXVXwqwyzvWwamy0o3O2UrNaIvnck4h7arMkkZ/zkFCzriqGe
+8VWIRkL3A6ggadJzWwqFYL2kwMzlAkEAhfEdFgUyXCy09PEYwtKLFI9vZlzpf327
+it0ACksDAS2qnhoJZ+0rQH+4eiv0c0dc5wwLf+cHxP5+LOQHsr8NoQJAcsRe+KyX
+G0TLKZg/J5E+zJMH6M19BZ4BC32UIMTJWe1xzp+9XrCWflagRJMJ+DOWtHzu/Opo
+Ty4OiT0uZUxcMw==
+-----END PRIVATE KEY-----
diff --git a/hostap/tests/hwsim/auth_serv/server-eku-client-server.pem b/hostap/tests/hwsim/auth_serv/server-eku-client-server.pem
new file mode 100644
index 0000000..7ed4014
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/server-eku-client-server.pem
@@ -0,0 +1,62 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162841 (0xd8d3e3a6cbe3ccd9)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Feb 19 12:18:22 2015 GMT
+            Not After : Feb 19 12:18:22 2016 GMT
+        Subject: C=FI, O=w1.fi, CN=server6.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:ca:30:1e:fd:13:6a:02:28:51:9a:0e:a9:1e:72:
+                    7e:e7:5a:c8:cb:0c:7e:8a:99:81:8d:05:b3:09:b0:
+                    a9:86:e7:ae:44:fa:e0:c7:ee:f3:18:b4:31:65:e3:
+                    05:6f:66:4e:e3:e4:85:81:9b:47:7f:5f:b5:16:2b:
+                    b0:59:91:9c:c5:55:d3:d3:ca:2c:81:ce:f0:27:a8:
+                    54:de:0e:4f:d8:f0:11:bd:0b:10:56:38:26:df:a9:
+                    3a:95:15:93:a6:16:ed:d2:1e:b4:b5:c0:a8:61:8e:
+                    ae:10:04:fd:e6:26:ca:4a:0d:90:4b:b9:ac:c9:94:
+                    f2:b1:6b:97:6b:60:24:ea:bd
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                C7:C6:EF:F5:61:D2:A0:08:81:6A:6B:44:2C:F5:72:F7:DA:DE:5B:B9
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            Authority Information Access: 
+                OCSP - URI:http://server.w1.fi:8888/
+
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication, TLS Web Server Authentication
+    Signature Algorithm: sha1WithRSAEncryption
+         b8:35:8c:b6:18:ac:e9:f6:cb:2c:df:d8:50:88:48:50:33:50:
+         fa:92:21:94:b7:82:04:0c:c2:c4:16:bc:dd:2f:75:f8:9d:1a:
+         d5:1a:50:dc:7c:97:78:10:25:33:ba:01:6d:47:b8:28:4f:7c:
+         d8:c4:a6:85:0d:f5:84:5f:31:68:1e:44:13:16:fd:ce:8b:98:
+         39:46:03:de:aa:62:16:ee:ae:aa:15:27:4a:f2:9d:9c:1f:5a:
+         61:04:ef:3a:7b:c4:5a:3d:a1:d0:2e:f6:31:ea:92:7e:5f:6b:
+         4d:d3:06:d2:ee:a9:4a:56:68:38:76:25:58:e0:07:fb:d5:d8:
+         fa:b0
+-----BEGIN CERTIFICATE-----
+MIIChzCCAfCgAwIBAgIJANjT46bL48zZMA0GCSqGSIb3DQEBBQUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNTAy
+MTkxMjE4MjJaFw0xNjAyMTkxMjE4MjJaMDUxCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEWMBQGA1UEAwwNc2VydmVyNi53MS5maTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAyjAe/RNqAihRmg6pHnJ+51rIywx+ipmBjQWzCbCphueuRPrg
+x+7zGLQxZeMFb2ZO4+SFgZtHf1+1FiuwWZGcxVXT08osgc7wJ6hU3g5P2PARvQsQ
+Vjgm36k6lRWTphbt0h60tcCoYY6uEAT95ibKSg2QS7msyZTysWuXa2Ak6r0CAwEA
+AaOBpDCBoTAJBgNVHRMEAjAAMB0GA1UdDgQWBBTHxu/1YdKgCIFqa0Qs9XL32t5b
+uTAfBgNVHSMEGDAWgBS4kt79ihizMMOfVfMzXbTIKYpBFDA1BggrBgEFBQcBAQQp
+MCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9zZXJ2ZXIudzEuZmk6ODg4OC8wHQYDVR0l
+BBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA0GCSqGSIb3DQEBBQUAA4GBALg1jLYY
+rOn2yyzf2FCISFAzUPqSIZS3ggQMwsQWvN0vdfidGtUaUNx8l3gQJTO6AW1HuChP
+fNjEpoUN9YRfMWgeRBMW/c6LmDlGA96qYhburqoVJ0rynZwfWmEE7zp7xFo9odAu
+9jHqkn5fa03TBtLuqUpWaDh2JVjgB/vV2Pqw
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/server-eku-client.key b/hostap/tests/hwsim/auth_serv/server-eku-client.key
new file mode 100644
index 0000000..f2a99cd
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/server-eku-client.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKOZ6eLhF2A7cDQa
+dFxG47i9u6rJ8+77EjCgacN0OIA6uiNSx8Fqz7rdQePSaTWkpmBsMR+FvVZsewlj
+zadRa4RAkHd+l2h7OLXEFTt0NzQounri14RTeHZNFre43wly54cmdCwEysXOKfW0
+ztso60VHQo/tiFqjI0mbe7w54QFTAgMBAAECgYAngwCtvtc6cqCCtPDtaGGPOKOe
+d+/mA9U80UE551POBGD4LwH3gKhy5QUI1MR8JCvalca3akF0IfcFKYl9o3hnsZ73
+3wGzxM8BEf9wEVtVC2CTRVoIupleaEk3j8dgaUs/O54WkmAoHF1avXAMSGOUDxCO
+Ggpn2tei78Csdj78IQJBANF7a7RaJsXh6xMI7hlrVrUsIbBvsBo1wbbGCwNRvgzL
+I1mq1O+Go7Aao0pDK7sOUa86j6ECZ5pzqcdPaF22tJ8CQQDH7kTy6ERBbLFxs/Wd
+YLDEh1GIGyGW10tuJTOl2R1TKSBXRzPAeI+jcC+AC00238p4MO899WOVeLvaERZa
+IuLNAkAtlxXGp4Qett9JQj1HbPPu9A7U7km+OorRM2K8MzMQZ7lmz2YORxgiwHlf
+NSU0TZZ7c1xE51gS5i9CAEcvdg7zAkAKIZfa20xCKHjhcyYaIIE0pErMY9uS4jwP
+S9FPMS5cPXRHF/OWaEWXGaM+kNQL2NFQv+IPuLSgKWsThNQmIyhtAkEAiQq1HdN7
+3l8YhUuJtxg7nrh2s0V4UcSNOZxVf/85AKrTu1IfjdwmXFeoRB/y9Ef4h1bcXgzj
+clIVhie7r0JNLw==
+-----END PRIVATE KEY-----
diff --git a/hostap/tests/hwsim/auth_serv/server-eku-client.pem b/hostap/tests/hwsim/auth_serv/server-eku-client.pem
new file mode 100644
index 0000000..cbb8437
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/server-eku-client.pem
@@ -0,0 +1,62 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162840 (0xd8d3e3a6cbe3ccd8)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Feb 19 12:18:03 2015 GMT
+            Not After : Feb 19 12:18:03 2016 GMT
+        Subject: C=FI, O=w1.fi, CN=server5.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:a3:99:e9:e2:e1:17:60:3b:70:34:1a:74:5c:46:
+                    e3:b8:bd:bb:aa:c9:f3:ee:fb:12:30:a0:69:c3:74:
+                    38:80:3a:ba:23:52:c7:c1:6a:cf:ba:dd:41:e3:d2:
+                    69:35:a4:a6:60:6c:31:1f:85:bd:56:6c:7b:09:63:
+                    cd:a7:51:6b:84:40:90:77:7e:97:68:7b:38:b5:c4:
+                    15:3b:74:37:34:28:ba:7a:e2:d7:84:53:78:76:4d:
+                    16:b7:b8:df:09:72:e7:87:26:74:2c:04:ca:c5:ce:
+                    29:f5:b4:ce:db:28:eb:45:47:42:8f:ed:88:5a:a3:
+                    23:49:9b:7b:bc:39:e1:01:53
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                33:16:9D:3B:17:15:82:2B:34:6E:38:E8:CC:22:BF:49:A7:5E:2A:2B
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            Authority Information Access: 
+                OCSP - URI:http://server.w1.fi:8888/
+
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication
+    Signature Algorithm: sha1WithRSAEncryption
+         49:88:dd:04:23:86:22:49:cb:73:57:9a:f7:d8:71:cf:33:62:
+         cc:24:a8:e9:ae:cd:22:91:16:2f:17:86:fc:09:8f:79:c0:93:
+         22:e6:77:25:21:8c:45:38:a3:4d:07:6e:d0:c3:ca:49:8a:3e:
+         86:1f:4e:dd:72:93:f3:47:7d:c3:5b:95:f1:98:50:a5:4e:36:
+         ed:71:94:3a:55:ee:ae:21:e3:27:3d:90:df:4a:be:f5:93:a5:
+         e2:0b:1a:3d:7d:c7:02:98:c1:17:67:9e:9b:a2:1d:65:fc:81:
+         59:72:2b:89:a9:47:31:ad:30:c0:82:39:47:a2:d2:eb:0e:71:
+         d6:3c
+-----BEGIN CERTIFICATE-----
+MIICfTCCAeagAwIBAgIJANjT46bL48zYMA0GCSqGSIb3DQEBBQUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNTAy
+MTkxMjE4MDNaFw0xNjAyMTkxMjE4MDNaMDUxCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEWMBQGA1UEAwwNc2VydmVyNS53MS5maTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAo5np4uEXYDtwNBp0XEbjuL27qsnz7vsSMKBpw3Q4gDq6I1LH
+wWrPut1B49JpNaSmYGwxH4W9Vmx7CWPNp1FrhECQd36XaHs4tcQVO3Q3NCi6euLX
+hFN4dk0Wt7jfCXLnhyZ0LATKxc4p9bTO2yjrRUdCj+2IWqMjSZt7vDnhAVMCAwEA
+AaOBmjCBlzAJBgNVHRMEAjAAMB0GA1UdDgQWBBQzFp07FxWCKzRuOOjMIr9Jp14q
+KzAfBgNVHSMEGDAWgBS4kt79ihizMMOfVfMzXbTIKYpBFDA1BggrBgEFBQcBAQQp
+MCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9zZXJ2ZXIudzEuZmk6ODg4OC8wEwYDVR0l
+BAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEASYjdBCOGIknLc1ea99hx
+zzNizCSo6a7NIpEWLxeG/AmPecCTIuZ3JSGMRTijTQdu0MPKSYo+hh9O3XKT80d9
+w1uV8ZhQpU427XGUOlXuriHjJz2Q30q+9ZOl4gsaPX3HApjBF2eem6IdZfyBWXIr
+ialHMa0wwII5R6LS6w5x1jw=
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/server-expired.key b/hostap/tests/hwsim/auth_serv/server-expired.key
new file mode 100644
index 0000000..882d645
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/server-expired.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANA7a4aeP7QOYEhU
+Tcbci7lrddDkYPChQwuv+cR3aRGEUr6efXG0qoAf6+bAN95J9IVDrk1S8+swc67m
+GAQUj8JjMKQM6/XWy/SvHU/WOkN4FDLe5YilNL6rmqSj3muE43iTHBwpx/xrzGjX
+7sBd1z2RiIFWulQRnk7ogIPgbMrxAgMBAAECgYEArWSNSO+FRD2kVxY8HZeQkbm1
+xVgmkLj3x0elx79XMkrpS+lVs9UpFL+ABAmTe/pBLqcJAUJN8k3KRp066krk2QyQ
+uilRkugON0vBJzLse9HryXilx0aWEVl3xZBKu1E3G4mcCl2LoPaASCZtjQXd/XCd
+zdBR24qe123ofMpIo0ECQQDooUnHsruInBX9bRP11xXs7bI5298ZLCWHFAhGa/Tb
+KvVXkXnzPVYhRi2w0Leqb0lht/4GX9MB06xcHs5TLvltAkEA5SasURCjxXc7svGJ
+yP1s779DxYWoEBvGiRPygtyO40cnkOuupXKLaSkSuNUGag+6UxNzxGSUx9aiadse
+oxOJFQJAL6y2SSXZBxMt8oUDPTO6O5cvGmp0G12Px1IUrBH92VjBdRPMUUw1tZYD
+USRFL7mk6VDiz32d6dbukOaDVErhNQJASwnoAb/WMXLDHO0VtriudLAIbGVBTM0b
+rYXXs1yweeKyJTXYghtJZc1qcRZpPFAcLto+3cAmLG6vzsRPew2JpQJBAN8krD5c
+RYAGuXtslPkH7BWypJXI+K3brZkKBiyXVB/fbwnpXI1KTbzeBSly60JrjuymY9+X
+NKs5A4HSiCtQjSk=
+-----END PRIVATE KEY-----
diff --git a/hostap/tests/hwsim/auth_serv/server-expired.pem b/hostap/tests/hwsim/auth_serv/server-expired.pem
new file mode 100644
index 0000000..f279aae
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/server-expired.pem
@@ -0,0 +1,62 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162826 (0xd8d3e3a6cbe3ccca)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Jan  1 00:00:00 2014 GMT
+            Not After : Jan  2 00:00:00 2014 GMT
+        Subject: C=FI, O=w1.fi, CN=server4.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:d0:3b:6b:86:9e:3f:b4:0e:60:48:54:4d:c6:dc:
+                    8b:b9:6b:75:d0:e4:60:f0:a1:43:0b:af:f9:c4:77:
+                    69:11:84:52:be:9e:7d:71:b4:aa:80:1f:eb:e6:c0:
+                    37:de:49:f4:85:43:ae:4d:52:f3:eb:30:73:ae:e6:
+                    18:04:14:8f:c2:63:30:a4:0c:eb:f5:d6:cb:f4:af:
+                    1d:4f:d6:3a:43:78:14:32:de:e5:88:a5:34:be:ab:
+                    9a:a4:a3:de:6b:84:e3:78:93:1c:1c:29:c7:fc:6b:
+                    cc:68:d7:ee:c0:5d:d7:3d:91:88:81:56:ba:54:11:
+                    9e:4e:e8:80:83:e0:6c:ca:f1
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                75:B0:65:1F:2F:A9:BE:D7:D0:EE:9D:42:8F:8B:13:5F:D0:AD:13:7B
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            Authority Information Access: 
+                OCSP - URI:http://server.w1.fi:8888/
+
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+    Signature Algorithm: sha1WithRSAEncryption
+         12:e7:8a:e1:3d:d9:fd:36:ce:71:66:b3:74:48:c1:f0:38:75:
+         30:56:c7:2c:9c:0d:da:d0:68:19:47:a2:37:38:0d:db:4f:f9:
+         b9:cc:0d:25:b1:35:ed:df:19:8c:4b:bd:f0:08:11:13:4b:e9:
+         a7:d7:50:2e:fa:7a:16:e1:4f:0f:5a:b4:42:34:ff:43:08:5c:
+         3c:04:6a:f8:44:8d:f6:e5:a7:82:38:60:d0:5c:d1:59:f9:02:
+         84:7f:da:ae:6c:e9:55:c8:f5:0e:da:55:70:f3:77:48:30:1f:
+         ab:60:39:a1:77:49:29:e3:51:54:62:72:c7:78:ae:17:14:c5:
+         dd:2c
+-----BEGIN CERTIFICATE-----
+MIICfTCCAeagAwIBAgIJANjT46bL48zKMA0GCSqGSIb3DQEBBQUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNDAx
+MDEwMDAwMDBaFw0xNDAxMDIwMDAwMDBaMDUxCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEWMBQGA1UEAwwNc2VydmVyNC53MS5maTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEA0Dtrhp4/tA5gSFRNxtyLuWt10ORg8KFDC6/5xHdpEYRSvp59
+cbSqgB/r5sA33kn0hUOuTVLz6zBzruYYBBSPwmMwpAzr9dbL9K8dT9Y6Q3gUMt7l
+iKU0vquapKPea4TjeJMcHCnH/GvMaNfuwF3XPZGIgVa6VBGeTuiAg+BsyvECAwEA
+AaOBmjCBlzAJBgNVHRMEAjAAMB0GA1UdDgQWBBR1sGUfL6m+19DunUKPixNf0K0T
+ezAfBgNVHSMEGDAWgBS4kt79ihizMMOfVfMzXbTIKYpBFDA1BggrBgEFBQcBAQQp
+MCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9zZXJ2ZXIudzEuZmk6ODg4OC8wEwYDVR0l
+BAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADgYEAEueK4T3Z/TbOcWazdEjB
+8Dh1MFbHLJwN2tBoGUeiNzgN20/5ucwNJbE17d8ZjEu98AgRE0vpp9dQLvp6FuFP
+D1q0QjT/QwhcPARq+ESN9uWngjhg0FzRWfkChH/armzpVcj1DtpVcPN3SDAfq2A5
+oXdJKeNRVGJyx3iuFxTF3Sw=
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/server-long-duration.key b/hostap/tests/hwsim/auth_serv/server-long-duration.key
new file mode 100644
index 0000000..8578515
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/server-long-duration.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQDA+hcn645Hf2Vx
+DuNGR2791hnx4L0bfpzsiEDZ3ssv6WTfzMgvc8k30TkenlA4Zxs9Mg/i3CmnAItV
+6XqlGf3e1TtSmolOk0fPVoQ+NiPoVeQJ9buycFYHaj+l44o80s7+i66q6GrBc/MQ
+qulNJPdpyzGE0MtwPTVJ6R8tNoEV3gt1dEPCIDmpJ4XVYhWVvNcicLL2CWpH37gj
+LNS3Bf/ApeB6skdzsESmaCaSk5JuNm6zdpZnVQU+ipPNlrvq7y1k0j5kFZsJ57As
+CwJPkppLigUL3TUnT8LGvoGw42+gZFhYENqoeSdXop2k0+Ct0i3Tmy+60fuYu6UL
+66dVPcJGOCel05gHJD3D8xCCC+9Cr60kZgjsJwtN7aqybAjI4dSqiMwbswLvxDco
+kMfW8kDpbfU0yRdtkRr33y5iQ7FIB2qygH6waRsfmLKtsyfFCCNo/B208xs6geyb
+TvAGnpy8v2L+5/1dos87rdGFZiiXmG1zEhGcPe9Cjlj22e/ULqo3HP7FhfvCOTzE
+fZg1hPGwFff0U3r8tnfWiwXcEDse5EULCPBhzsv7XkmV5jDd/kc5U1j9WsbUZSVN
+xKVmEEAiHePt/ONQDi18ERjUxotFu7LF+/G94Q4Sd6rQtFTDnctlPj5y8MhgEqr+
++4pLwptKxVktypPimbMeEBOMwKUxZQIDAQABAoICAFAo0muTrmNGqQWgkInF7HXm
+dJqDpyn/mwGpy350zGQXh/5q2xDTANd9wkNUsa/sLGVsISs0Ft03HanPNVHVjR39
+5wl2Beksxpx6rK4G4T2GSBZXC+OgRCwKV2v6K5b+Xltf/Gsj5gx7tJDM3eJco6U/
+cU9hR9BUrIkKHBg01sQVmx7r76V4q1r3fuFXP+4WJis5My1+VbaLWLrLyYRJKqaE
+gM2WtpV4t9uD0dn7EM1VMQC9FtbPe0EyYRj6Lw700R9bMe1zCBbCNezlmc0h8ZPF
+PjliZszwldDmm4q1vs2aQRQ71Ag9YzlE5ETuehrZ/o0ynk31IZSdBX8BIV4ILGUy
+10obeXFpgSjIGBnR1wl30l9DbEF3CciSJMWA323A7l81ztLf2dS6fB8vUU4rlaeY
+vcJi1Qcbjt21GFxX1biMvGvYVFmnWR4LvUkVoNdhyBHHacCrupD5y/M7xycIK3Uo
+6JPaMZKxFTbe1bWW4GVI4KbFKKrqxaZkj2gxa8r+M3zd5C/g2McvhqGAtqkGb/Sk
+QmuBZhFaPYNUvYS62AYo9VRmJ8beZ+SsACJYBu4CJk+vievRPtRLAy3+fmkqXUIg
+n3VhM30B5VmngO3Zh/RJqCsHXmf90uNNHNbS0usDnUhNnUqsKHM3DHqyWUit72ie
+tIczuD5bnAghqGH8ZTyVAoIBAQD5YmjjxdyU5wllRH7BlqktN34D6BKOSkkqfBFk
+aZ1K+sz8m7QmD/fbtWH3Ej/5CD4V9a31YohoUMjkQ5sL5UHAtK3BjEvFaz744DHf
+yTgzkso/Tt1qp6oykXoMnj/VQQBIh0sEQ+FbJap2AmjiVOkY1krzPUuoLTDm+B3N
+WS0zO9uXTdwZ6u/XlTcFwGK2YOJ8qvogQ7MDLa50S7KCgNA9F0foyS5wQbFLMwub
+tWgRrRcB//spuR25k4zJVd3Lp7hbEftj0sxH1TvB0UPlIQODOipi8w4ByXOmGwoQ
+jqdAWm1ApgJOf0VRzmILuviOQxbi6WmhtoWoa1+3nrKaw0FrAoIBAQDGGJzboDRx
+ngtnzn8LjuT1LZ2tZD0ftxUbYJXcg+vppBo89i3XqkfnS5Wv42jxYVbPJCAHQSKB
+Gep7rtC7lz8n1EPgQ0G1AtC7s45jvMROlaVVn9TbmQMxZWhyz6+aZodWtYQcZbem
+QZb5AUIH2N77XTHm9QoSMYMSPFg1is8ZT9dsXbfrjnJw3yD97jT9vqfGUPBFdp/6
+OVGCm/GFtWhmnfJDKXBERKdEcOrA9VVu7TCo9RIl+ESOcPjaUXtqQO8qJMkq/BZz
+rCWYRp9K0JohjyRtbqxybfCHQFoYPhYPN7NEvTcw8xzO4Fbe3UeuyAcQm6Bim/yt
+UxsUVv0t5HxvAoIBABH8xiH/qHxY+bWDLj1aV5N6WC2B9KyssQBOeJasg3rgAh8K
+QAEqz67aM1m1yEV3EbtVwBld5QbZJNRKCAngnFCA97NpIiH4LU+YJCDHC7W0lb/h
+OVkzBLSnB42XcHU6PgH9OCzzlHKVkej3HYeNpk5zTm6i8i0MLWXLaaLhFsCGRb1x
+EToRVwFOsuxIRxZugIeLcYUYPPYyuk2LFDIThnGRl080C5RvOJF6+hBeEkaMHIe5
+Ze1Je9phyRrQhbHlxAdVkbNOhgt8VtYQ+Puy2OUDMgF3FICOfjfx5VTyZR/gJdcu
+4iW1SlOvNiUwdDMWpbTyLvtKBfPy4rGE0c8V3EcCggEADkXjt2dZn/4VMEuOl9SZ
+5ph8zet1A/37BVf2NEdFDjaGtXG/Z9SbDa1CgcPO4eVzjcU6kYs94zNs07CK1ZlW
+MZZ3tYbjCxnC1GNhlQoa0taxbPPxMElcE4kM94jDTEqq4bJ+bFVf4BjFufc/PLWB
+i3bxi4SITztGCGBUsrbnqRk5mlM+VziSQlOxWtp48vdzuzRFu5uKd0r99n0p78pc
+VVh4BiLMNf8EVkgUggarOyzX3rSZtCapOeIl7AUG8WbwVIcF/5TeQUNcCha8j3Vs
+RZK8uy3IOw500Cnw0+JVUI9g2UkQ2vQJVVaQq8ohyhQbfjTYX4KBuDNOti52W4Ik
+/QKCAQAStLzR2L2AWb4pw7azKxPBoyf3r8B31xOT2Q4LhcIzuS5VzE8vOOEprsQ3
+lP9shiaEgn6D2ifkfdUC3qEekamKFiXoPuaLdopP7u9C3Qzyu8fF9sFA5xD0vR1m
+U6Cf6KOuwoEI4woaOm6+I6WDLwo6oQeMtUijycJp/FZmpz0am2z5YeynBL9mvPij
+oh1zgP7q8EzF/LGvaLZASMeoEktvHodzSqEBvi+FrVjr2Rox0l8F34xZpNVzJxrm
+aehPzOWdaWbhR8cC5BFf7xvpFVBwy0Weg1qua+1FPaKw/Vuohqn97N3wRgALC9Pv
+Wn3OoMMoOXVSeqpHuMye16F61vko
+-----END PRIVATE KEY-----
diff --git a/hostap/tests/hwsim/auth_serv/server-long-duration.pem b/hostap/tests/hwsim/auth_serv/server-long-duration.pem
new file mode 100644
index 0000000..442a054
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/server-long-duration.pem
@@ -0,0 +1,96 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162842 (0xd8d3e3a6cbe3ccda)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: May 24 08:13:55 2015 GMT
+            Not After : May 11 08:13:55 2065 GMT
+        Subject: C=FI, O=w1.fi, CN=server7.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (4096 bit)
+                Modulus:
+                    00:c0:fa:17:27:eb:8e:47:7f:65:71:0e:e3:46:47:
+                    6e:fd:d6:19:f1:e0:bd:1b:7e:9c:ec:88:40:d9:de:
+                    cb:2f:e9:64:df:cc:c8:2f:73:c9:37:d1:39:1e:9e:
+                    50:38:67:1b:3d:32:0f:e2:dc:29:a7:00:8b:55:e9:
+                    7a:a5:19:fd:de:d5:3b:52:9a:89:4e:93:47:cf:56:
+                    84:3e:36:23:e8:55:e4:09:f5:bb:b2:70:56:07:6a:
+                    3f:a5:e3:8a:3c:d2:ce:fe:8b:ae:aa:e8:6a:c1:73:
+                    f3:10:aa:e9:4d:24:f7:69:cb:31:84:d0:cb:70:3d:
+                    35:49:e9:1f:2d:36:81:15:de:0b:75:74:43:c2:20:
+                    39:a9:27:85:d5:62:15:95:bc:d7:22:70:b2:f6:09:
+                    6a:47:df:b8:23:2c:d4:b7:05:ff:c0:a5:e0:7a:b2:
+                    47:73:b0:44:a6:68:26:92:93:92:6e:36:6e:b3:76:
+                    96:67:55:05:3e:8a:93:cd:96:bb:ea:ef:2d:64:d2:
+                    3e:64:15:9b:09:e7:b0:2c:0b:02:4f:92:9a:4b:8a:
+                    05:0b:dd:35:27:4f:c2:c6:be:81:b0:e3:6f:a0:64:
+                    58:58:10:da:a8:79:27:57:a2:9d:a4:d3:e0:ad:d2:
+                    2d:d3:9b:2f:ba:d1:fb:98:bb:a5:0b:eb:a7:55:3d:
+                    c2:46:38:27:a5:d3:98:07:24:3d:c3:f3:10:82:0b:
+                    ef:42:af:ad:24:66:08:ec:27:0b:4d:ed:aa:b2:6c:
+                    08:c8:e1:d4:aa:88:cc:1b:b3:02:ef:c4:37:28:90:
+                    c7:d6:f2:40:e9:6d:f5:34:c9:17:6d:91:1a:f7:df:
+                    2e:62:43:b1:48:07:6a:b2:80:7e:b0:69:1b:1f:98:
+                    b2:ad:b3:27:c5:08:23:68:fc:1d:b4:f3:1b:3a:81:
+                    ec:9b:4e:f0:06:9e:9c:bc:bf:62:fe:e7:fd:5d:a2:
+                    cf:3b:ad:d1:85:66:28:97:98:6d:73:12:11:9c:3d:
+                    ef:42:8e:58:f6:d9:ef:d4:2e:aa:37:1c:fe:c5:85:
+                    fb:c2:39:3c:c4:7d:98:35:84:f1:b0:15:f7:f4:53:
+                    7a:fc:b6:77:d6:8b:05:dc:10:3b:1e:e4:45:0b:08:
+                    f0:61:ce:cb:fb:5e:49:95:e6:30:dd:fe:47:39:53:
+                    58:fd:5a:c6:d4:65:25:4d:c4:a5:66:10:40:22:1d:
+                    e3:ed:fc:e3:50:0e:2d:7c:11:18:d4:c6:8b:45:bb:
+                    b2:c5:fb:f1:bd:e1:0e:12:77:aa:d0:b4:54:c3:9d:
+                    cb:65:3e:3e:72:f0:c8:60:12:aa:fe:fb:8a:4b:c2:
+                    9b:4a:c5:59:2d:ca:93:e2:99:b3:1e:10:13:8c:c0:
+                    a5:31:65
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                B9:82:B8:B0:E0:95:0E:21:A8:12:1B:41:EE:FA:DC:2E:3E:17:D2:57
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            Authority Information Access: 
+                OCSP - URI:http://server.w1.fi:8888/
+
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         8a:09:20:6d:71:d0:16:13:6d:0e:19:30:5d:70:8f:8a:0c:ab:
+         67:da:8f:40:51:f7:2b:d8:01:2b:9e:b4:ee:cf:95:79:e1:4f:
+         05:87:27:8f:cf:84:93:28:60:3c:1c:6e:c6:3e:62:4f:d4:de:
+         78:74:d2:da:f4:8d:a7:63:40:a3:21:bb:78:28:02:53:41:ac:
+         40:f1:3c:77:69:c6:81:51:49:90:41:80:3a:03:f2:8c:d5:bc:
+         4c:c1:70:4b:a1:c4:66:26:0b:cb:d2:43:69:89:64:c2:69:af:
+         5d:3d:4b:51:d5:51:40:3d:2d:c5:a5:ef:a0:5a:42:53:2d:e1:
+         11:1b
+-----BEGIN CERTIFICATE-----
+MIIEAzCCA2ygAwIBAgIJANjT46bL48zaMA0GCSqGSIb3DQEBCwUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAgFw0xNTA1
+MjQwODEzNTVaGA8yMDY1MDUxMTA4MTM1NVowNTELMAkGA1UEBhMCRkkxDjAMBgNV
+BAoMBXcxLmZpMRYwFAYDVQQDDA1zZXJ2ZXI3LncxLmZpMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAwPoXJ+uOR39lcQ7jRkdu/dYZ8eC9G36c7IhA2d7L
+L+lk38zIL3PJN9E5Hp5QOGcbPTIP4twppwCLVel6pRn93tU7UpqJTpNHz1aEPjYj
+6FXkCfW7snBWB2o/peOKPNLO/ouuquhqwXPzEKrpTST3acsxhNDLcD01SekfLTaB
+Fd4LdXRDwiA5qSeF1WIVlbzXInCy9glqR9+4IyzUtwX/wKXgerJHc7BEpmgmkpOS
+bjZus3aWZ1UFPoqTzZa76u8tZNI+ZBWbCeewLAsCT5KaS4oFC901J0/Cxr6BsONv
+oGRYWBDaqHknV6KdpNPgrdIt05svutH7mLulC+unVT3CRjgnpdOYByQ9w/MQggvv
+Qq+tJGYI7CcLTe2qsmwIyOHUqojMG7MC78Q3KJDH1vJA6W31NMkXbZEa998uYkOx
+SAdqsoB+sGkbH5iyrbMnxQgjaPwdtPMbOoHsm07wBp6cvL9i/uf9XaLPO63RhWYo
+l5htcxIRnD3vQo5Y9tnv1C6qNxz+xYX7wjk8xH2YNYTxsBX39FN6/LZ31osF3BA7
+HuRFCwjwYc7L+15JleYw3f5HOVNY/VrG1GUlTcSlZhBAIh3j7fzjUA4tfBEY1MaL
+RbuyxfvxveEOEneq0LRUw53LZT4+cvDIYBKq/vuKS8KbSsVZLcqT4pmzHhATjMCl
+MWUCAwEAAaOBmjCBlzAJBgNVHRMEAjAAMB0GA1UdDgQWBBS5griw4JUOIagSG0Hu
++twuPhfSVzAfBgNVHSMEGDAWgBS4kt79ihizMMOfVfMzXbTIKYpBFDA1BggrBgEF
+BQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9zZXJ2ZXIudzEuZmk6ODg4OC8w
+EwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADgYEAigkgbXHQFhNt
+DhkwXXCPigyrZ9qPQFH3K9gBK5607s+VeeFPBYcnj8+EkyhgPBxuxj5iT9TeeHTS
+2vSNp2NAoyG7eCgCU0GsQPE8d2nGgVFJkEGAOgPyjNW8TMFwS6HEZiYLy9JDaYlk
+wmmvXT1LUdVRQD0txaXvoFpCUy3hERs=
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/server-no-dnsname.key b/hostap/tests/hwsim/auth_serv/server-no-dnsname.key
new file mode 100644
index 0000000..fd0ad39
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/server-no-dnsname.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANv8D6FIh2iGxJ56
++Bgod22jWA/bvmvUQ0PEuhc3m6j/lqJzFBMcrhkPgVQ1EGSU42RlvpsLFtKekph3
+h+KamfwdVwyKDUwhL65n12Nh65FbWC+tZ2Zl5IMHymo2peYg9lyZJ9tj5YbYK3wd
+kESBIiF3CgMFw+tjYbNMMsCHhzpHAgMBAAECgYEAu0p2MDWk+4xKGDfPxBmn3JOG
+ZTIMhJeakTcLzLqOb6rzn+lkPQVdAH8f+AaZp1jP5OlvB2fAjZ9uZhrWeUpxMA3a
+TTEJqvttF1R+PjQ7hxWByPf+cFtPfJnXmJg8DuCBpc4TbPd0MMqtu37K9m41iO7K
+H5Lj6J+wp4lhv1Y4oaECQQDv0bvCgrGpSMLHigsdVcsFyjZr25+9y1J2Gnm1Hm/Z
+dbUtS9cOihYh8qh3YyGAKS5psCVzdeMXGKDN05pOhEGxAkEA6tO8Bhh+YA/oG+pl
+Ps9W9XjWwBCByVI+Hub6/Y9NcWckmBP+41DN1Oi7cKsSyMJ74WD5r+QYqS258tC6
+YDsBdwJBAJ8OEWN+XuqRsW26Joj8P7zFUrbSYO32Dej6wkHXwAMQSGuUYzvnZap6
+UDVub+eaaIf8JbqgM088LFqWvz7YBOECQHBlN7GTN6my812pKxyNEQoc9GypefVq
+L+GKnMeQN3j37UP9DhqvKlWlr1GWED+XFsQhLmFJw6P2BvJ5hTtaArECQHBSy14H
+6K7lnk1UNaz4By9MOJPbHkKUl1FCrwtQ1UhJsur1pUCbud2thz4YXQh3NyJ3X0m0
+G3R+tt7p2kJzdlU=
+-----END PRIVATE KEY-----
diff --git a/hostap/tests/hwsim/auth_serv/server-no-dnsname.pem b/hostap/tests/hwsim/auth_serv/server-no-dnsname.pem
new file mode 100644
index 0000000..93d0ec2
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/server-no-dnsname.pem
@@ -0,0 +1,62 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162838 (0xd8d3e3a6cbe3ccd6)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Feb 19 12:16:15 2015 GMT
+            Not After : Feb 19 12:16:15 2016 GMT
+        Subject: C=FI, O=w1.fi, CN=server3.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:db:fc:0f:a1:48:87:68:86:c4:9e:7a:f8:18:28:
+                    77:6d:a3:58:0f:db:be:6b:d4:43:43:c4:ba:17:37:
+                    9b:a8:ff:96:a2:73:14:13:1c:ae:19:0f:81:54:35:
+                    10:64:94:e3:64:65:be:9b:0b:16:d2:9e:92:98:77:
+                    87:e2:9a:99:fc:1d:57:0c:8a:0d:4c:21:2f:ae:67:
+                    d7:63:61:eb:91:5b:58:2f:ad:67:66:65:e4:83:07:
+                    ca:6a:36:a5:e6:20:f6:5c:99:27:db:63:e5:86:d8:
+                    2b:7c:1d:90:44:81:22:21:77:0a:03:05:c3:eb:63:
+                    61:b3:4c:32:c0:87:87:3a:47
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                8E:9A:4F:4D:46:AD:59:AC:7F:4C:9C:BE:6D:5B:D7:99:63:8D:C7:70
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            Authority Information Access: 
+                OCSP - URI:http://server.w1.fi:8888/
+
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+    Signature Algorithm: sha1WithRSAEncryption
+         91:6b:2b:90:41:55:93:9a:70:d9:56:57:21:56:08:6e:fb:04:
+         e3:40:0d:eb:25:b5:56:21:7d:7b:81:12:97:22:e8:38:95:bc:
+         ba:cb:db:97:a8:6f:dd:da:61:40:dd:fc:0d:82:e6:a0:b5:b0:
+         13:d9:dc:ee:84:d7:27:f2:b3:be:01:31:11:5b:23:29:6d:37:
+         8b:24:3c:d9:6c:21:7c:cc:d6:a2:68:32:39:40:00:5f:04:af:
+         db:32:f7:10:af:e8:53:c4:d3:2f:03:61:cb:fa:67:c5:18:20:
+         63:f9:d9:42:01:34:c9:eb:9d:33:c8:a3:7e:b5:fb:fe:6b:5f:
+         f8:86
+-----BEGIN CERTIFICATE-----
+MIICfTCCAeagAwIBAgIJANjT46bL48zWMA0GCSqGSIb3DQEBBQUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNTAy
+MTkxMjE2MTVaFw0xNjAyMTkxMjE2MTVaMDUxCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEWMBQGA1UEAwwNc2VydmVyMy53MS5maTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEA2/wPoUiHaIbEnnr4GCh3baNYD9u+a9RDQ8S6FzebqP+WonMU
+ExyuGQ+BVDUQZJTjZGW+mwsW0p6SmHeH4pqZ/B1XDIoNTCEvrmfXY2HrkVtYL61n
+ZmXkgwfKajal5iD2XJkn22PlhtgrfB2QRIEiIXcKAwXD62Nhs0wywIeHOkcCAwEA
+AaOBmjCBlzAJBgNVHRMEAjAAMB0GA1UdDgQWBBSOmk9NRq1ZrH9MnL5tW9eZY43H
+cDAfBgNVHSMEGDAWgBS4kt79ihizMMOfVfMzXbTIKYpBFDA1BggrBgEFBQcBAQQp
+MCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9zZXJ2ZXIudzEuZmk6ODg4OC8wEwYDVR0l
+BAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADgYEAkWsrkEFVk5pw2VZXIVYI
+bvsE40AN6yW1ViF9e4ESlyLoOJW8usvbl6hv3dphQN38DYLmoLWwE9nc7oTXJ/Kz
+vgExEVsjKW03iyQ82WwhfMzWomgyOUAAXwSv2zL3EK/oU8TTLwNhy/pnxRggY/nZ
+QgE0yeudM8ijfrX7/mtf+IY=
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/server.key b/hostap/tests/hwsim/auth_serv/server.key
new file mode 100644
index 0000000..1416327
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/server.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALqgd1UiFIVVZZtk
+LK3tm91lMcnaYFDOONY03Oi8G54w5xLjU2zJ7UgDeYFpmM6KuHdHNkXPxuDxex5x
+iVT3AcwiraBCsag1nmCqOphR0P8f7r6NCmP7ojkX8mRh9mUCMnl04Z/RiWVVqcMg
+nr9pVrP3Tz+pVMLajzyv8nVU+n6BAgMBAAECgYBH8LlvcM62QyAC0Y/DkBeINY0G
+wY5lN8mDESei83g196XriwPKqOA15Vj+QOVtoN3Q5PuP17NTXOLX7m5A+WKQVK/O
+Cl0uBCEqS9YvPN6Fp9va5VonhWxGpLdZcrxETTpxjHhVBGS9C8wBday65r2nDfo6
+uWlCebceUBuuSzwybQJBAOAwS7ZY8xY/bCNDzvfnNuPsPQDEQWVx6A9mv9BnepT/
+8bQcvfkUbXyWy5NsPN6yt/tqmjdbUEFAuNJlI23I2wsCQQDVG7poTL8KPa7UZge7
+W79FyyEoL5but1VPTAN6JJNTMpp9k2LBWFjUSmTiTkeccHfbvKxMjUuI7NQwya41
+hSQjAkApSRuYUBcsIK/kaqdhxeW44Zd2Xa4BZZGrzGtEkNnlOKElXympBhcHm6mP
+053+EQGKvl36FcnYynd+33s/y35zAkAZ5ZC1c/4TJIPGU8/EuNV5icGxvHa+85Bu
+XnJduWwdxBx5/hsWG8JPqeqwhYq2PASUs0zM0K7JKN5wP1HoNxG5AkEAlaumCfLv
+vA/b3HVZD/b0nxkl/F7g3nACPVJ48FU2BneB+bU75zqeI3B7xGd9CKamkuutH6or
+fe17ZI8ZeLCLqA==
+-----END PRIVATE KEY-----
diff --git a/hostap/tests/hwsim/auth_serv/server.pem b/hostap/tests/hwsim/auth_serv/server.pem
new file mode 100644
index 0000000..fa3e0ae
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/server.pem
@@ -0,0 +1,64 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162832 (0xd8d3e3a6cbe3ccd0)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Sep 29 21:11:22 2014 GMT
+            Not After : Sep 29 21:11:22 2015 GMT
+        Subject: C=FI, O=w1.fi, CN=server.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:ba:a0:77:55:22:14:85:55:65:9b:64:2c:ad:ed:
+                    9b:dd:65:31:c9:da:60:50:ce:38:d6:34:dc:e8:bc:
+                    1b:9e:30:e7:12:e3:53:6c:c9:ed:48:03:79:81:69:
+                    98:ce:8a:b8:77:47:36:45:cf:c6:e0:f1:7b:1e:71:
+                    89:54:f7:01:cc:22:ad:a0:42:b1:a8:35:9e:60:aa:
+                    3a:98:51:d0:ff:1f:ee:be:8d:0a:63:fb:a2:39:17:
+                    f2:64:61:f6:65:02:32:79:74:e1:9f:d1:89:65:55:
+                    a9:c3:20:9e:bf:69:56:b3:f7:4f:3f:a9:54:c2:da:
+                    8f:3c:af:f2:75:54:fa:7e:81
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                31:4F:10:5C:67:9F:BE:4E:88:D6:DC:C5:AB:9E:12:88:86:69:02:4F
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            Authority Information Access: 
+                OCSP - URI:http://server.w1.fi:8888/
+
+            X509v3 Subject Alternative Name: 
+                DNS:server.w1.fi
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         92:b7:19:2f:15:84:00:c6:68:01:ba:96:67:11:df:7d:0c:1e:
+         45:eb:59:e5:64:ad:db:f0:23:ce:22:af:a0:35:a2:6f:99:96:
+         9d:2d:bc:b5:8d:58:36:c7:71:f4:fb:c8:a5:e8:44:45:52:7e:
+         1e:44:dd:99:3b:1c:40:f1:f7:73:ec:f9:b7:fc:06:cc:a9:a5:
+         37:41:d1:20:2b:b5:93:75:26:1b:46:2e:3d:25:a3:5e:e9:7e:
+         73:37:9d:e7:71:6f:bb:21:22:cc:31:3e:a2:3f:18:05:ca:35:
+         d2:98:b8:53:6b:92:ac:73:10:8d:8a:09:a4:e3:46:ad:28:72:
+         ab:51
+-----BEGIN CERTIFICATE-----
+MIIClTCCAf6gAwIBAgIJANjT46bL48zQMA0GCSqGSIb3DQEBCwUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNDA5
+MjkyMTExMjJaFw0xNTA5MjkyMTExMjJaMDQxCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEVMBMGA1UEAwwMc2VydmVyLncxLmZpMIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQC6oHdVIhSFVWWbZCyt7ZvdZTHJ2mBQzjjWNNzovBueMOcS41Ns
+ye1IA3mBaZjOirh3RzZFz8bg8XsecYlU9wHMIq2gQrGoNZ5gqjqYUdD/H+6+jQpj
++6I5F/JkYfZlAjJ5dOGf0YllVanDIJ6/aVaz908/qVTC2o88r/J1VPp+gQIDAQAB
+o4GzMIGwMAkGA1UdEwQCMAAwHQYDVR0OBBYEFDFPEFxnn75OiNbcxaueEoiGaQJP
+MB8GA1UdIwQYMBaAFLiS3v2KGLMww59V8zNdtMgpikEUMDUGCCsGAQUFBwEBBCkw
+JzAlBggrBgEFBQcwAYYZaHR0cDovL3NlcnZlci53MS5maTo4ODg4LzAXBgNVHREE
+EDAOggxzZXJ2ZXIudzEuZmkwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcN
+AQELBQADgYEAkrcZLxWEAMZoAbqWZxHffQweRetZ5WSt2/AjziKvoDWib5mWnS28
+tY1YNsdx9PvIpehERVJ+HkTdmTscQPH3c+z5t/wGzKmlN0HRICu1k3UmG0YuPSWj
+Xul+czed53FvuyEizDE+oj8YBco10pi4U2uSrHMQjYoJpONGrShyq1E=
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/server.pkcs12 b/hostap/tests/hwsim/auth_serv/server.pkcs12
new file mode 100644
index 0000000..7061fd7
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/server.pkcs12
Binary files differ
diff --git a/hostap/tests/hwsim/auth_serv/user.key b/hostap/tests/hwsim/auth_serv/user.key
new file mode 100644
index 0000000..b9fd702
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/user.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAKaWLpsijN+UvouJ
+SfZ4dqJgfhSV85b+qxklAzRkdAE+qJ988UdhYEyCkih8K6AOy4e/WevX82EiOxTz
+qzH2WpUfuHq4LDypYVN4m+g+UOzC1kTnQ828Pk7nRv6SnsOYDylYyMuJAXVH6ZVX
+D3bFLwVexx4N8jwSY125VBmvf0BrAgMBAAECgYEAkEoS0kKJ3Hqc1IW0r6xFrX2A
+l1oOpCGvl1bswKuloxJfwczZu+cHHx4VdMWgj8Fg3xKJ03K4FtEsdYhdJyhn6c6G
+YsKF7HHGo2WA61VHxgqRB/CZzALy2JR/3rzElvrVQ5ZVh15DipNpwfwP9bW6P99A
+omPQVnZ3p1HgU5WK68kCQQDXHbFUYX3I9SYlR4JhPy5ov2Q8WHu4p9rWXGBO75uS
+7f3FZCbGULKZEOsiVFbloyUdpvLId7wvb343a1EAOnC9AkEAxj9UqsKMAdlXTDrT
+9NcQmJKWt568gEV4/45fjpTzbdndEOtCMwWBWEv/SyiWgWdwPeBViRGEyPrkLV/S
+teesRwJBAIfN6QuaWKyrh591W6xFFOlwGrm2KrVS0ucNfoeW4SKLOPCK36fHflj/
+w1Hy6MEkk+P6Z7+DR7yyqH4YNBTu0AkCQA7uZioWTQU2oWSUabJfIFjdcYyS4A+p
+K9vTlU7f2RXE+ulzTqEZIQzNbIT0oaFNcR637rlMIHwiqVzhgrVApbECQQDK5QqX
+E6Z2VHTNEnCki9YvkgjPhLxSihQMDSaR0ENkre0OctFUufbwzH7DEhzV6CQ1Uw+9
+Au5AOFzcb1tfGczP
+-----END PRIVATE KEY-----
diff --git a/hostap/tests/hwsim/auth_serv/user.pem b/hostap/tests/hwsim/auth_serv/user.pem
new file mode 100644
index 0000000..4bc2e1a
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/user.pem
@@ -0,0 +1,62 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162833 (0xd8d3e3a6cbe3ccd1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Sep 29 21:13:00 2014 GMT
+            Not After : Sep 29 21:13:00 2015 GMT
+        Subject: C=FI, O=w1.fi, CN=Test User
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:a6:96:2e:9b:22:8c:df:94:be:8b:89:49:f6:78:
+                    76:a2:60:7e:14:95:f3:96:fe:ab:19:25:03:34:64:
+                    74:01:3e:a8:9f:7c:f1:47:61:60:4c:82:92:28:7c:
+                    2b:a0:0e:cb:87:bf:59:eb:d7:f3:61:22:3b:14:f3:
+                    ab:31:f6:5a:95:1f:b8:7a:b8:2c:3c:a9:61:53:78:
+                    9b:e8:3e:50:ec:c2:d6:44:e7:43:cd:bc:3e:4e:e7:
+                    46:fe:92:9e:c3:98:0f:29:58:c8:cb:89:01:75:47:
+                    e9:95:57:0f:76:c5:2f:05:5e:c7:1e:0d:f2:3c:12:
+                    63:5d:b9:54:19:af:7f:40:6b
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                81:DE:DF:E9:5A:00:1A:CA:67:D6:06:DD:65:B2:4E:C5:9A:04:43:7D
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            Authority Information Access: 
+                OCSP - URI:http://server.w1.fi:8888/
+
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         76:24:5a:f8:de:ef:8b:65:02:67:ab:8f:3a:42:88:22:35:40:
+         48:df:97:91:9b:5a:d4:60:af:61:ef:53:7a:2c:76:04:5f:80:
+         27:79:7e:1f:0a:ed:ab:71:0c:6e:90:7a:69:04:4e:21:cb:31:
+         47:ee:e9:36:87:a9:f4:a1:dd:e9:8a:fd:41:cc:d2:ae:dd:47:
+         66:d1:71:08:b2:e8:5e:e3:36:9e:98:c8:66:51:5b:41:95:02:
+         29:fd:b6:46:d2:40:ec:0e:46:40:92:b1:b7:e2:28:6e:85:17:
+         1e:8d:52:40:c8:20:ca:9b:ab:f0:10:30:8c:0b:5d:91:91:8c:
+         ff:ca
+-----BEGIN CERTIFICATE-----
+MIICeTCCAeKgAwIBAgIJANjT46bL48zRMA0GCSqGSIb3DQEBCwUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNDA5
+MjkyMTEzMDBaFw0xNTA5MjkyMTEzMDBaMDExCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTESMBAGA1UEAwwJVGVzdCBVc2VyMIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQCmli6bIozflL6LiUn2eHaiYH4UlfOW/qsZJQM0ZHQBPqiffPFHYWBM
+gpIofCugDsuHv1nr1/NhIjsU86sx9lqVH7h6uCw8qWFTeJvoPlDswtZE50PNvD5O
+50b+kp7DmA8pWMjLiQF1R+mVVw92xS8FXsceDfI8EmNduVQZr39AawIDAQABo4Ga
+MIGXMAkGA1UdEwQCMAAwHQYDVR0OBBYEFIHe3+laABrKZ9YG3WWyTsWaBEN9MB8G
+A1UdIwQYMBaAFLiS3v2KGLMww59V8zNdtMgpikEUMDUGCCsGAQUFBwEBBCkwJzAl
+BggrBgEFBQcwAYYZaHR0cDovL3NlcnZlci53MS5maTo4ODg4LzATBgNVHSUEDDAK
+BggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOBgQB2JFr43u+LZQJnq486QogiNUBI
+35eRm1rUYK9h71N6LHYEX4AneX4fCu2rcQxukHppBE4hyzFH7uk2h6n0od3piv1B
+zNKu3Udm0XEIsuhe4zaemMhmUVtBlQIp/bZG0kDsDkZAkrG34ihuhRcejVJAyCDK
+m6vwEDCMC12RkYz/yg==
+-----END CERTIFICATE-----
diff --git a/hostap/tests/hwsim/auth_serv/user.pkcs12 b/hostap/tests/hwsim/auth_serv/user.pkcs12
new file mode 100644
index 0000000..0d50201
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/user.pkcs12
Binary files differ
diff --git a/hostap/tests/hwsim/auth_serv/user.rsa-key b/hostap/tests/hwsim/auth_serv/user.rsa-key
new file mode 100644
index 0000000..4c3cfbf
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/user.rsa-key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQCmli6bIozflL6LiUn2eHaiYH4UlfOW/qsZJQM0ZHQBPqiffPFH
+YWBMgpIofCugDsuHv1nr1/NhIjsU86sx9lqVH7h6uCw8qWFTeJvoPlDswtZE50PN
+vD5O50b+kp7DmA8pWMjLiQF1R+mVVw92xS8FXsceDfI8EmNduVQZr39AawIDAQAB
+AoGBAJBKEtJCidx6nNSFtK+sRa19gJdaDqQhr5dW7MCrpaMSX8HM2bvnBx8eFXTF
+oI/BYN8SidNyuBbRLHWIXScoZ+nOhmLChexxxqNlgOtVR8YKkQfwmcwC8tiUf968
+xJb61UOWVYdeQ4qTacH8D/W1uj/fQKJj0FZ2d6dR4FOViuvJAkEA1x2xVGF9yPUm
+JUeCYT8uaL9kPFh7uKfa1lxgTu+bku39xWQmxlCymRDrIlRW5aMlHabyyHe8L29+
+N2tRADpwvQJBAMY/VKrCjAHZV0w60/TXEJiSlreevIBFeP+OX46U823Z3RDrQjMF
+gVhL/0soloFncD3gVYkRhMj65C1f0rXnrEcCQQCHzekLmlisq4efdVusRRTpcBq5
+tiq1UtLnDX6HluEiizjwit+nx35Y/8NR8ujBJJPj+me/g0e8sqh+GDQU7tAJAkAO
+7mYqFk0FNqFklGmyXyBY3XGMkuAPqSvb05VO39kVxPrpc06hGSEMzWyE9KGhTXEe
+t+65TCB8Iqlc4YK1QKWxAkEAyuUKlxOmdlR0zRJwpIvWL5IIz4S8UooUDA0mkdBD
+ZK3tDnLRVLn28Mx+wxIc1egkNVMPvQLuQDhc3G9bXxnMzw==
+-----END RSA PRIVATE KEY-----
diff --git a/hostap/tests/hwsim/auth_serv/user2.pkcs12 b/hostap/tests/hwsim/auth_serv/user2.pkcs12
new file mode 100644
index 0000000..eb17d9c
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/user2.pkcs12
Binary files differ
diff --git a/hostap/tests/hwsim/auth_serv/user3.pkcs12 b/hostap/tests/hwsim/auth_serv/user3.pkcs12
new file mode 100644
index 0000000..953d7cb
--- /dev/null
+++ b/hostap/tests/hwsim/auth_serv/user3.pkcs12
Binary files differ
diff --git a/hostap/tests/hwsim/bss-1.conf b/hostap/tests/hwsim/bss-1.conf
new file mode 100644
index 0000000..b465dcb
--- /dev/null
+++ b/hostap/tests/hwsim/bss-1.conf
@@ -0,0 +1,11 @@
+driver=nl80211
+
+hw_mode=g
+channel=1
+ieee80211n=1
+
+interface=wlan3
+bssid=02:00:00:00:03:00
+ctrl_interface=/var/run/hostapd
+
+ssid=bss-1
diff --git a/hostap/tests/hwsim/bss-2.conf b/hostap/tests/hwsim/bss-2.conf
new file mode 100644
index 0000000..7a8c64c
--- /dev/null
+++ b/hostap/tests/hwsim/bss-2.conf
@@ -0,0 +1,11 @@
+driver=nl80211
+
+hw_mode=g
+channel=1
+ieee80211n=1
+
+interface=wlan3-2
+bssid=02:00:00:00:03:01
+ctrl_interface=/var/run/hostapd
+
+ssid=bss-2
diff --git a/hostap/tests/hwsim/bss-3.conf b/hostap/tests/hwsim/bss-3.conf
new file mode 100644
index 0000000..fe671de
--- /dev/null
+++ b/hostap/tests/hwsim/bss-3.conf
@@ -0,0 +1,11 @@
+driver=nl80211
+
+hw_mode=g
+channel=1
+ieee80211n=1
+
+interface=wlan3-3
+bssid=02:00:00:00:03:02
+ctrl_interface=/var/run/hostapd
+
+ssid=bss-3
diff --git a/hostap/tests/hwsim/bss-ht40-1.conf b/hostap/tests/hwsim/bss-ht40-1.conf
new file mode 100644
index 0000000..a338c6b
--- /dev/null
+++ b/hostap/tests/hwsim/bss-ht40-1.conf
@@ -0,0 +1,12 @@
+driver=nl80211
+
+hw_mode=g
+channel=1
+ieee80211n=1
+ht_capab=[HT40+]
+
+interface=wlan3
+bssid=02:00:00:00:03:00
+ctrl_interface=/var/run/hostapd
+
+ssid=bss-1
diff --git a/hostap/tests/hwsim/bss-ht40-2.conf b/hostap/tests/hwsim/bss-ht40-2.conf
new file mode 100644
index 0000000..c7e27ce
--- /dev/null
+++ b/hostap/tests/hwsim/bss-ht40-2.conf
@@ -0,0 +1,12 @@
+driver=nl80211
+
+hw_mode=g
+channel=1
+ieee80211n=1
+ht_capab=[HT40+]
+
+interface=wlan3-2
+bssid=02:00:00:00:03:01
+ctrl_interface=/var/run/hostapd
+
+ssid=bss-2
diff --git a/hostap/tests/hwsim/build.sh b/hostap/tests/hwsim/build.sh
new file mode 100755
index 0000000..b35e0f1
--- /dev/null
+++ b/hostap/tests/hwsim/build.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+set -e
+
+cd $(dirname $0)
+
+usage()
+{
+	echo "$0 [-c | --codecov] [-f | --force-config]"
+	exit 1
+}
+
+use_lcov=0
+force_config=0
+while [ "$1" != "" ]; do
+	case $1 in
+		-c | --codecov ) shift
+			echo "$0: use code coverage specified"
+			use_lcov=1
+			;;
+		-f | --force-config ) shift
+			force_config=1
+			echo "$0: force copy config specified"
+			;;
+		* ) usage
+	esac
+done
+
+echo "Building TNC testing tools"
+cd tnc
+make clean > /dev/null
+make QUIET=1 -j8
+
+echo "Building wlantest"
+cd ../../../wlantest
+make clean > /dev/null
+make QUIET=1 -j8 > /dev/null
+
+echo "Building hostapd"
+cd ../hostapd
+if [ ! -e .config -o $force_config -eq 1 ]; then
+    cp ../tests/hwsim/example-hostapd.config .config
+else
+    echo "hostapd config file exists"
+fi
+
+if [ $use_lcov -eq 1 ]; then
+    if ! grep -q CONFIG_CODE_COVERAGE .config; then
+	    echo CONFIG_CODE_COVERAGE=y >> .config
+    else
+	    echo "CONFIG_CODE_COVERAGE already exists in hostapd/.config. Ignore"
+    fi
+fi
+
+make clean > /dev/null
+make QUIET=1 -j8 hostapd hostapd_cli hlr_auc_gw
+
+echo "Building wpa_supplicant"
+cd ../wpa_supplicant
+if [ ! -e .config -o $force_config -eq 1 ]; then
+    cp ../tests/hwsim/example-wpa_supplicant.config .config
+else
+    echo "wpa_supplicant config file exists"
+fi
+
+if [ $use_lcov -eq 1 ]; then
+    if ! grep -q CONFIG_CODE_COVERAGE .config; then
+	    echo CONFIG_CODE_COVERAGE=y >> .config
+    else
+	    echo "CONFIG_CODE_COVERAGE already exists in wpa_supplicant/.config. Ignore"
+    fi
+fi
+
+make clean > /dev/null
+if [ -z $FIPSLD_CC ]; then
+export FIPSLD_CC=gcc
+fi
+make QUIET=1 -j8
diff --git a/hostap/tests/hwsim/check_kernel.py b/hostap/tests/hwsim/check_kernel.py
new file mode 100644
index 0000000..15e5856
--- /dev/null
+++ b/hostap/tests/hwsim/check_kernel.py
@@ -0,0 +1,31 @@
+# kernel message checker module
+#
+# Copyright (c) 2013, Intel Corporation
+#
+# Author: Johannes Berg <johannes@sipsolutions.net>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+#
+"""
+Tests for kernel messages to find if there were any issues in them.
+"""
+
+import re
+
+lockdep_messages = [
+  'possible circular locking dependency',
+  '.*-safe -> .*unsafe lock order detected',
+  'possible recursive locking detected',
+  'inconsistent lock state',
+  'possible irq lock inversion dependency',
+  'suspicious RCU usage',
+]
+lockdep = r'(\[\s*)?INFO: (%s)' % ('|'.join(lockdep_messages), )
+issue = re.compile('(\[[0-9 .]*\] )?(WARNING:|BUG:|%s|RTNL: assertion failed).*' % lockdep)
+
+def check_kernel(logfile):
+    for line in open(logfile, 'r'):
+        if issue.match(line):
+            return False
+    return True
diff --git a/hostap/tests/hwsim/dictionary.radius b/hostap/tests/hwsim/dictionary.radius
new file mode 100644
index 0000000..295ccd3
--- /dev/null
+++ b/hostap/tests/hwsim/dictionary.radius
@@ -0,0 +1,17 @@
+ATTRIBUTE	User-Name		1	string
+ATTRIBUTE	User-Password		2	string
+ATTRIBUTE	NAS-IP-Address		4	ipaddr
+ATTRIBUTE	State			24	octets
+ATTRIBUTE	Calling-Station-Id	31	string
+ATTRIBUTE	NAS-Identifier		32	string
+ATTRIBUTE	Acct-Session-Id		44	string
+ATTRIBUTE	Acct-Multi-Session-Id	50	string
+ATTRIBUTE	Event-Timestamp		55	date
+ATTRIBUTE	Tunnel-Type		64	integer
+ATTRIBUTE	Tunnel-Medium-Type	65	integer
+ATTRIBUTE	Tunnel-Password		69	octets
+ATTRIBUTE	EAP-Message		79	string
+ATTRIBUTE	Message-Authenticator	80	octets
+ATTRIBUTE	Tunnel-Private-Group-ID	81	string
+ATTRIBUTE	Chargeable-User-Identity 89	string
+ATTRIBUTE	Error-Cause		101	integer
diff --git a/hostap/tests/hwsim/example-hostapd.config b/hostap/tests/hwsim/example-hostapd.config
new file mode 100644
index 0000000..085092a
--- /dev/null
+++ b/hostap/tests/hwsim/example-hostapd.config
@@ -0,0 +1,101 @@
+#CC=ccache gcc
+
+CONFIG_DRIVER_NONE=y
+CONFIG_DRIVER_NL80211=y
+CONFIG_RSN_PREAUTH=y
+
+#CONFIG_TLS=internal
+#CONFIG_INTERNAL_LIBTOMMATH=y
+#CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+CONFIG_TLS=openssl
+
+CONFIG_EAP=y
+CONFIG_ERP=y
+CONFIG_EAP_MD5=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_EAP_GPSK=y
+CONFIG_EAP_GPSK_SHA256=y
+CONFIG_EAP_SAKE=y
+CONFIG_EAP_PAX=y
+CONFIG_EAP_PSK=y
+CONFIG_EAP_VENDOR_TEST=y
+CONFIG_EAP_FAST=y
+CONFIG_EAP_IKEV2=y
+CONFIG_EAP_TNC=y
+CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
+LIBS += -rdynamic
+CONFIG_EAP_UNAUTH_TLS=y
+ifeq ($(CONFIG_TLS), openssl)
+CONFIG_EAP_PWD=y
+endif
+CONFIG_EAP_EKE=y
+CONFIG_PKCS12=y
+CONFIG_RADIUS_SERVER=y
+CONFIG_IPV6=y
+CONFIG_TLSV11=y
+CONFIG_TLSV12=y
+
+CONFIG_FULL_DYNAMIC_VLAN=y
+CONFIG_LIBNL32=y
+CONFIG_LIBNL3_ROUTE=y
+CONFIG_PEERKEY=y
+CONFIG_IEEE80211W=y
+CONFIG_IEEE80211R=y
+CONFIG_IEEE80211N=y
+CONFIG_IEEE80211AC=y
+
+CONFIG_WPS=y
+CONFIG_WPS_UPNP=y
+CONFIG_WPS_NFC=y
+#CONFIG_WPS_STRICT=y
+CONFIG_WPA_TRACE=y
+CONFIG_WPA_TRACE_BFD=y
+
+CONFIG_P2P_MANAGER=y
+CONFIG_DEBUG_FILE=y
+CONFIG_DEBUG_LINUX_TRACING=y
+CONFIG_WPA_CLI_EDIT=y
+CONFIG_ACS=y
+CONFIG_NO_RANDOM_POOL=y
+CONFIG_WNM=y
+CONFIG_INTERWORKING=y
+CONFIG_HS20=y
+CONFIG_SQLITE=y
+CONFIG_SAE=y
+CFLAGS += -DALL_DH_GROUPS
+
+CONFIG_FST=y
+CONFIG_FST_TEST=y
+
+CONFIG_TESTING_OPTIONS=y
+CONFIG_MODULE_TESTS=y
+
+CONFIG_SUITEB=y
+
+# AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
+# This can be used as a more efficient memory error detector than valgrind
+# (though, with still some CPU and memory cost, so VM cases will need more
+# memory allocated for the guest).
+#CFLAGS += -fsanitize=address -O1 -fno-omit-frame-pointer -g
+#LIBS += -fsanitize=address -fno-omit-frame-pointer -g
+#LIBS_h += -fsanitize=address -fno-omit-frame-pointer -g
+#LIBS_n += -fsanitize=address -fno-omit-frame-pointer -g
+#LIBS_c += -fsanitize=address -fno-omit-frame-pointer -g
+
+# Undefined Behavior Sanitizer (UBSan) can be enabled by uncommenting the
+# following lines.
+#CFLAGS += -Wno-format-nonliteral
+#CFLAGS += -fsanitize=undefined
+##CFLAGS += -fno-sanitize-recover
+#LIBS += -fsanitize=undefined
+##LIBS += -fno-sanitize-recover
+#LIBS_h += -fsanitize=undefined
+#LIBS_n += -fsanitize=undefined
+#LIBS_c += -fsanitize=undefined
diff --git a/hostap/tests/hwsim/example-setup.txt b/hostap/tests/hwsim/example-setup.txt
new file mode 100644
index 0000000..2396b97
--- /dev/null
+++ b/hostap/tests/hwsim/example-setup.txt
@@ -0,0 +1,177 @@
+Step-by-step guide for setting up hostapd/wpa_supplicant test framework
+-----------------------------------------------------------------------
+
+This document can be used as a quick guide for getting started with
+hostapd/wpa_supplicant test framework with mac80211_hwsim. While the
+example here uses Ubuntu 14.04.1 server to have a list of exact steps,
+there are no requirements for using that specific distribution in the
+testing setup.
+
+The steps here describe how to run a full Linux installation in a
+virtual machine with any host system (e.g., Linux, Windows, or OS X as
+the host and using kvm, VirtualBox, etc. for running the virtual guest
+system). For more advanced (and significantly faster and with more
+testing coverage) configuration on a Linux host system, parallel virtual
+machines can be used as an alternative setup. See tests/hwsim/vm/README
+for more details on that.
+
+
+Install Ubuntu Server 14.04.1 in the virtual machine
+
+- download installation image, e.g.,
+  http://releases.ubuntu.com/14.04.1/ubuntu-14.04.1-server-amd64.iso
+- use virtualization software specific steps to create a new VM and
+  install the the guest system with default settings (i.e., no need to
+  select any extra packages during initial installation)
+- if the host system has multiple CPU cores, it is likely a good idea to
+  enabled at least two CPUs in the guest; 1024 MB of RAM should be enough
+  for testing purposes
+- 8 GB of virtual hard driver should be fine for this purpose
+- boot to the installed operating system
+
+
+Install the prerequisite packages that may not have been installed by default
+
+sudo apt-get install build-essential git libpcap-dev libsqlite3-dev binutils-dev libnl-3-dev libnl-genl-3-dev libnl-route-3-dev libssl-dev libiberty-dev libdbus-1-dev iw bridge-utils python-pyrad python-crypto
+
+
+Install a recent kernel wireless components (mac80211_hwsim, mac80211,
+cfg80211)
+
+For this step, the kernel version may be updated, but the simpler option
+is to install the latest version of Backports package. For example:
+
+wget http://www.kernel.org/pub/linux/kernel/projects/backports/stable/v3.19-rc1/backports-3.19-rc1-1.tar.xz
+tar xJf backports-3.19-rc1-1.tar.xz
+cd backports-3.19-rc1-1
+
+cat > defconfigs/mac80211_hwsim <<EOF
+CPTCFG_CFG80211=m
+CPTCFG_CFG80211_WEXT=y
+CPTCFG_MAC80211=m
+CPTCFG_MAC80211_LEDS=y
+CPTCFG_MAC80211_MESH=y
+CPTCFG_WLAN=y
+CPTCFG_MAC80211_HWSIM=m
+EOF
+
+make defconfig-mac80211_hwsim
+make
+sudo make install
+cd ..
+
+
+Update iw based on custom iw.git build
+
+Couple of the test cases expect iw to have support for requesting
+cfg80211 scan results to be flushed. That functionality is not included
+in the version that Ubuntu 14.04.1 includes (iw v3.4). Following steps
+can be used to replace that version with a custom build. This is
+optional, i.e., most test cases will work with the old iw version, but
+some test cases are skipped and some are more likely to fail if iw does
+not get updated.
+
+wget https://www.kernel.org/pub/software/network/iw/iw-3.17.tar.gz
+tar xf iw-3.17.tar.gz
+cd iw-3.17
+make
+sudo mv /sbin/iw{,-distro}
+sudo cp iw /sbin/iw
+cd ..
+
+
+Update wireless-regdb
+
+Number of VHT and DFS test cases are skipped if the old wireless-regdb
+version from Ubuntu 14.04 (2013.02.13) is used. Following steps can
+optionally be used to update wireless-regdb to a newer snapshot to
+enable additional test cases:
+
+wget http://kernel.org/pub/software/network/wireless-regdb/wireless-regdb-2014.10.07.tar.xz
+tar xJf wireless-regdb-2014.10.07.tar.xz
+sudo mv /lib/crda/regulatory.bin{,-distro}
+sudo cp wireless-regdb-2014.10.07/regulatory.bin /lib/crda/regulatory.bin
+
+# following command can be used to verify that the new version is trusted
+regdbdump /lib/crda/regulatory.bin
+
+
+Download a snapshot of the hostap.git repository and build the programs
+
+git clone git://w1.fi/hostap.git
+cd hostap/tests/hwsim
+./build.sh
+
+
+Setup is now ready for testing. You can run a quick test to confirm that
+things work as expected:
+
+# load mac80211_hwsim and start test software
+./start.sh
+
+# run a single test case ap_open
+sudo ./run-tests.py ap_open
+
+This should print out following style results:
+
+DEV: wlan0: 02:00:00:00:00:00
+DEV: wlan1: 02:00:00:00:01:00
+DEV: wlan2: 02:00:00:00:02:00
+APDEV: wlan3
+APDEV: wlan4
+START ap_open 1/1
+Test: AP with open mode (no security) configuration
+Starting AP wlan3
+Connect STA wlan0 to AP
+PASS ap_open 0.175895 2015-01-17 20:12:07.486006
+passed all 1 test case(s)
+
+(If that "PASS ap_open" line does not show up, something unexpected has
+happened and the setup is not in working condition.)
+
+# to stop test software and unload mac80211_hwsim
+./stop.sh
+
+
+To run all available test cases (about thousand or so), you can run following:
+
+./run-all.sh
+
+This will take about half an hour to hour to run (if that sounds long, see
+vm/README for information on how parallel VMs can be used to speed this
+up; e.g., a 4-core i7-4770K can run these in under 10 minutes with 7
+parallel VMs).
+
+The results may look something like this:
+
+START grpform_goneg_fail_with_group_iface 1/981
+PASS grpform_goneg_fail_with_group_iface 0.371424 2015-01-17 22:17:16.659803
+START grpform2 2/981
+PASS grpform2 1.476142 2015-01-17 22:17:18.136539
+...
+START ext_password_psk_not_found 981/981
+PASS ext_password_psk_not_found 1.544709 2015-01-17 22:46:56.489764
+failed tests: wext_wpa2_psk wext_wep_open_auth wext_open wext_rfkill wext_scan_hidden wext_pmksa_cache wext_wep_shared_key_auth
+
+
+In this example, about 860 test cases passed and about 100 were skipped.
+
+Most of the skipped test cases are in following categories:
+- D-Bus (requires kvm-based test run, see vm/README)
+- VHT 80 and 160 MHz channels (requires wireless-regdb update)
+- DFS (requires wireless-regdb updates)
+
+The following test failed every time (i.e., other failed cases could be
+passed on second attempt):
+
+wext_pmf wext_wpa2_psk wext_wep_open_auth wext_open wext_rfkill wext_scan_hidden wext_pmksa_cache wext_wep_shared_key_auth
+
+WEXT failures are due to the specific cfg80211/mac80211 version from
+Backports not allowing WEXT support to be enabled. A newer build
+addresses that and these WEXT test cases pass, e.g., with this snapshot
+build:
+http://buildbot.w1.fi/backports-wireless-testing/backports-wireless-testing-2015-01-18-ba3f765.tar.bz2
+
+With that version, ibss_rsn is failing due to a known cfg80211
+regression in the specific snapshot build. All other test cases passed
+at least on retry or were skipped due to missing testing capability.
diff --git a/hostap/tests/hwsim/example-wpa_supplicant.config b/hostap/tests/hwsim/example-wpa_supplicant.config
new file mode 100644
index 0000000..67b767c
--- /dev/null
+++ b/hostap/tests/hwsim/example-wpa_supplicant.config
@@ -0,0 +1,144 @@
+#CC=ccache gcc
+
+CONFIG_TLS=openssl
+#CONFIG_TLS=internal
+#CONFIG_INTERNAL_LIBTOMMATH=y
+#CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+
+CONFIG_IEEE8021X_EAPOL=y
+
+CONFIG_ERP=y
+CONFIG_EAP_MD5=y
+CONFIG_MSCHAPV2=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_OTP=y
+CONFIG_EAP_PSK=y
+CONFIG_EAP_PAX=y
+CONFIG_EAP_LEAP=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_AKA=y
+CONFIG_EAP_AKA_PRIME=y
+CONFIG_EAP_VENDOR_TEST=y
+CONFIG_EAP_TLV=y
+CONFIG_EAP_SAKE=y
+CONFIG_EAP_GPSK=y
+CONFIG_EAP_GPSK_SHA256=y
+CONFIG_EAP_EKE=y
+CONFIG_EAP_TNC=y
+CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
+LIBS += -rdynamic
+CONFIG_EAP_FAST=y
+CONFIG_EAP_IKEV2=y
+
+ifeq ($(CONFIG_TLS), openssl)
+CONFIG_EAP_PWD=y
+endif
+
+CONFIG_USIM_SIMULATOR=y
+CONFIG_SIM_SIMULATOR=y
+
+#CONFIG_PCSC=y
+CONFIG_IPV6=y
+CONFIG_DRIVER_NONE=y
+CONFIG_PKCS12=y
+CONFIG_CTRL_IFACE=unix
+
+CONFIG_WPA_CLI_EDIT=y
+
+CONFIG_OCSP=y
+
+#CONFIG_ELOOP_POLL=y
+
+CONFIG_CTRL_IFACE_DBUS=y
+CONFIG_CTRL_IFACE_DBUS_NEW=y
+CONFIG_CTRL_IFACE_DBUS_INTRO=y
+
+CONFIG_PEERKEY=y
+CONFIG_IEEE80211W=y
+CONFIG_IEEE80211R=y
+CONFIG_IEEE80211N=y
+
+CONFIG_DEBUG_FILE=y
+
+CONFIG_WPS=y
+#CONFIG_WPS_STRICT=y
+CONFIG_WPS_UPNP=y
+CONFIG_WPS_NFC=y
+CONFIG_WPS_ER=y
+#CONFIG_WPS_REG_DISABLE_OPEN=y
+
+CONFIG_DRIVER_WEXT=y
+
+CONFIG_DRIVER_NL80211=y
+CFLAGS += -I/usr/include/libnl3
+CONFIG_LIBNL32=y
+
+CONFIG_IBSS_RSN=y
+
+CONFIG_AP=y
+CONFIG_MESH=y
+CONFIG_P2P=y
+CONFIG_WIFI_DISPLAY=y
+
+CONFIG_BGSCAN_SIMPLE=y
+CONFIG_BGSCAN_LEARN=y
+
+CONFIG_WPA_TRACE=y
+CONFIG_WPA_TRACE_BFD=y
+
+CONFIG_TDLS=y
+CONFIG_TDLS_TESTING=y
+CONFIG_NO_RANDOM_POOL=y
+
+CONFIG_TLSV11=y
+CONFIG_TLSV12=y
+
+CONFIG_HT_OVERRIDES=y
+CONFIG_VHT_OVERRIDES=y
+
+CONFIG_DEBUG_LINUX_TRACING=y
+
+CONFIG_INTERWORKING=y
+CONFIG_HS20=y
+
+CONFIG_AUTOSCAN_EXPONENTIAL=y
+CONFIG_AUTOSCAN_PERIODIC=y
+
+CONFIG_EXT_PASSWORD_TEST=y
+
+CONFIG_EAP_UNAUTH_TLS=y
+
+CONFIG_SAE=y
+CFLAGS += -DALL_DH_GROUPS
+
+CONFIG_WNM=y
+
+CONFIG_FST=y
+CONFIG_FST_TEST=y
+
+CONFIG_TESTING_OPTIONS=y
+CONFIG_MODULE_TESTS=y
+
+CONFIG_SUITEB=y
+
+# AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
+# This can be used as a more efficient memory error detector than valgrind
+# (though, with still some CPU and memory cost, so VM cases will need more
+# memory allocated for the guest).
+#CFLAGS += -fsanitize=address -O1 -fno-omit-frame-pointer -g
+#LIBS += -fsanitize=address -fno-omit-frame-pointer -g
+#LIBS_c += -fsanitize=address -fno-omit-frame-pointer -g
+#LIBS_p += -fsanitize=address -fno-omit-frame-pointer -g
+
+# Undefined Behavior Sanitizer (UBSan) can be enabled by uncommenting the
+# following lines.
+#CFLAGS += -Wno-format-nonliteral
+#CFLAGS += -fsanitize=undefined
+##CFLAGS += -fno-sanitize-recover
+#LIBS += -fsanitize=undefined
+##LIBS += -fno-sanitize-recover
+#LIBS_c += -fsanitize=undefined
+#LIBS_p += -fsanitize=undefined
diff --git a/hostap/tests/hwsim/fst_module_aux.py b/hostap/tests/hwsim/fst_module_aux.py
new file mode 100644
index 0000000..286fa85
--- /dev/null
+++ b/hostap/tests/hwsim/fst_module_aux.py
@@ -0,0 +1,801 @@
+# FST tests related classes
+# Copyright (c) 2015, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+import subprocess
+import os
+import signal
+import time
+import re
+
+import hostapd
+import wpaspy
+import utils
+from wpasupplicant import WpaSupplicant
+
+import fst_test_common
+
+logger = logging.getLogger()
+
+def parse_fst_iface_event(ev):
+    """Parses FST iface event that comes as a string, e.g.
+    "<3>FST-EVENT-IFACE attached ifname=wlan9 group=fstg0"
+    Returns a dictionary with parsed "event_type", "ifname", and "group"; or
+    None if not an FST event or can't be parsed."""
+    event = {}
+    if ev.find("FST-EVENT-IFACE") == -1:
+        return None
+    if ev.find("attached") != -1:
+        event['event_type'] = 'attached'
+    elif ev.find("detached") != -1:
+        event['event_type'] = 'detached'
+    else:
+        return None
+    f = re.search("ifname=(\S+)", ev)
+    if f is not None:
+        event['ifname'] = f.group(1)
+    f = re.search("group=(\S+)", ev)
+    if f is not None:
+        event['group'] = f.group(1)
+    return event
+
+def parse_fst_session_event(ev):
+    """Parses FST session event that comes as a string, e.g.
+    "<3>FST-EVENT-SESSION event_type=EVENT_FST_SESSION_STATE session_id=0 reason=REASON_STT"
+    Returns a dictionary with parsed "type", "id", and "reason"; or None if not
+    a FST event or can't be parsed"""
+    event = {}
+    if ev.find("FST-EVENT-SESSION") == -1:
+        return None
+    event['new_state'] = '' # The field always exists in the dictionary
+    f = re.search("event_type=(\S+)", ev)
+    if f is None:
+        return None
+    event['type'] = f.group(1)
+    f = re.search("session_id=(\d+)", ev)
+    if f is not None:
+        event['id'] = f.group(1)
+    f = re.search("old_state=(\S+)", ev)
+    if f is not None:
+        event['old_state'] = f.group(1)
+    f = re.search("new_state=(\S+)", ev)
+    if f is not None:
+        event['new_state'] = f.group(1)
+    f = re.search("reason=(\S+)", ev)
+    if f is not None:
+        event['reason'] = f.group(1)
+    return event
+
+def start_two_ap_sta_pairs(apdev, rsn=False):
+    """auxiliary function that creates two pairs of APs and STAs"""
+    ap1 = FstAP(apdev[0]['ifname'], 'fst_11a', 'a',
+                fst_test_common.fst_test_def_chan_a,
+                fst_test_common.fst_test_def_group,
+                fst_test_common.fst_test_def_prio_low,
+                fst_test_common.fst_test_def_llt, rsn=rsn)
+    ap1.start()
+    ap2 = FstAP(apdev[1]['ifname'], 'fst_11g', 'g',
+                fst_test_common.fst_test_def_chan_g,
+                fst_test_common.fst_test_def_group,
+                fst_test_common.fst_test_def_prio_high,
+                fst_test_common.fst_test_def_llt, rsn=rsn)
+    ap2.start()
+
+    sta1 = FstSTA('wlan5',
+                  fst_test_common.fst_test_def_group,
+                  fst_test_common.fst_test_def_prio_low,
+                  fst_test_common.fst_test_def_llt, rsn=rsn)
+    sta1.start()
+    sta2 = FstSTA('wlan6',
+                  fst_test_common.fst_test_def_group,
+                  fst_test_common.fst_test_def_prio_high,
+                  fst_test_common.fst_test_def_llt, rsn=rsn)
+    sta2.start()
+
+    return ap1, ap2, sta1, sta2
+
+def stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2):
+    sta1.stop()
+    sta2.stop()
+    ap1.stop()
+    ap2.stop()
+
+def connect_two_ap_sta_pairs(ap1, ap2, dev1, dev2, rsn=False):
+    """Connects a pair of stations, each one to a separate AP"""
+    dev1.scan(freq=fst_test_common.fst_test_def_freq_a)
+    dev2.scan(freq=fst_test_common.fst_test_def_freq_g)
+
+    if rsn:
+        dev1.connect(ap1, psk="12345678",
+                     scan_freq=fst_test_common.fst_test_def_freq_a)
+        dev2.connect(ap2, psk="12345678",
+                     scan_freq=fst_test_common.fst_test_def_freq_g)
+    else:
+        dev1.connect(ap1, key_mgmt="NONE",
+                     scan_freq=fst_test_common.fst_test_def_freq_a)
+        dev2.connect(ap2, key_mgmt="NONE",
+                     scan_freq=fst_test_common.fst_test_def_freq_g)
+
+def disconnect_two_ap_sta_pairs(ap1, ap2, dev1, dev2):
+    dev1.disconnect()
+    dev2.disconnect()
+
+def external_sta_connect(sta, ap, **kwargs):
+    """Connects the external station to the given AP"""
+    if not isinstance(sta, WpaSupplicant):
+        raise Exception("Bad STA object")
+    if not isinstance(ap, FstAP):
+        raise Exception("Bad AP object to connect to")
+    hap = ap.get_instance()
+    sta.connect(ap.get_ssid(), **kwargs)
+
+def disconnect_external_sta(sta, ap, check_disconnect=True):
+    """Disconnects the external station from the AP"""
+    if not isinstance(sta, WpaSupplicant):
+        raise Exception("Bad STA object")
+    if not isinstance(ap, FstAP):
+        raise Exception("Bad AP object to connect to")
+    sta.request("DISCONNECT")
+    if check_disconnect:
+        hap = ap.get_instance()
+        ev = hap.wait_event([ "AP-STA-DISCONNECTED" ], timeout=10)
+        if ev is None:
+            raise Exception("No disconnection event received from %s" % ap.get_ssid())
+
+#
+# FstDevice class
+# This is the parent class for the AP (FstAP) and STA (FstSTA) that implements
+# FST functionality.
+#
+class FstDevice:
+    def __init__(self, iface, fst_group, fst_pri, fst_llt=None, rsn=False):
+        self.iface = iface
+        self.fst_group = fst_group
+        self.fst_pri = fst_pri
+        self.fst_llt = fst_llt  # None llt means no llt parameter will be set
+        self.instance = None    # Hostapd/WpaSupplicant instance
+        self.peer_obj = None    # Peer object, must be a FstDevice child object
+        self.new_peer_addr = None # Peer MAC address for new session iface
+        self.old_peer_addr = None # Peer MAC address for old session iface
+        self.role = 'initiator' # Role: initiator/responder
+        s = self.grequest("FST-MANAGER TEST_REQUEST IS_SUPPORTED")
+        if not s.startswith('OK'):
+            raise utils.HwsimSkip("FST not supported")
+        self.rsn = rsn
+
+    def ifname(self):
+        return self.iface
+
+    def get_instance(self):
+        """Gets the Hostapd/WpaSupplicant instance"""
+        raise Exception("Virtual get_instance() called!")
+
+    def get_own_mac_address(self):
+        """Gets the device's own MAC address"""
+        raise Exception("Virtual get_own_mac_address() called!")
+
+    def get_new_peer_addr(self):
+        return self.new_peer_addr
+
+    def get_old_peer_addr(self):
+        return self.old_peer_addr
+
+    def get_actual_peer_addr(self):
+        """Gets the peer address. A connected AP/station address is returned."""
+        raise Exception("Virtual get_actual_peer_addr() called!")
+
+    def grequest(self, req):
+        """Send request on the global control interface"""
+        raise Exception, "Virtual grequest() called!"
+
+    def wait_gevent(self, events, timeout=None):
+        """Wait for a list of events on the global interface"""
+        raise Exception("Virtual wait_gevent() called!")
+
+    def request(self, req):
+        """Issue a request to the control interface"""
+        h = self.get_instance()
+        return h.request(req)
+
+    def wait_event(self, events, timeout=None):
+        """Wait for an event from the control interface"""
+        h = self.get_instance()
+        if timeout is not None:
+            return h.wait_event(events, timeout=timeout)
+        else:
+            return h.wait_event(events)
+
+    def set_old_peer_addr(self, peer_addr=None):
+        """Sets the peer address"""
+        if peer_addr is not None:
+            self.old_peer_addr = peer_addr
+        else:
+            self.old_peer_addr = self.get_actual_peer_addr()
+
+    def set_new_peer_addr(self, peer_addr=None):
+        """Sets the peer address"""
+        if peer_addr is not None:
+            self.new_peer_addr = peer_addr
+        else:
+            self.new_peer_addr = self.get_actual_peer_addr()
+
+    def add_peer(self, obj, old_peer_addr=None, new_peer_addr=None):
+        """Add peer for FST session(s). 'obj' is a FstDevice subclass object.
+        The method must be called before add_session().
+        If peer_addr is not specified, the address of the currently connected
+        station is used."""
+        if not isinstance(obj, FstDevice):
+            raise Exception("Peer must be a FstDevice object")
+        self.peer_obj = obj
+        self.set_old_peer_addr(old_peer_addr)
+        self.set_new_peer_addr(new_peer_addr)
+
+    def get_peer(self):
+        """Returns peer object"""
+        return self.peer_obj
+
+    def set_fst_parameters(self, group_id=None, pri=None, llt=None):
+        """Change/set new FST parameters. Can be used to start FST sessions with
+        different FST parameters than defined in the configuration file."""
+        if group_id is not None:
+            self.fst_group = group_id
+        if pri is not None:
+            self.fst_pri = pri
+        if llt is not None:
+            self.fst_llt = llt
+
+    def get_local_mbies(self, ifname=None):
+        if_name = ifname if ifname is not None else self.iface
+        return self.grequest("FST-MANAGER TEST_REQUEST GET_LOCAL_MBIES " + if_name)
+
+    def add_session(self):
+        """Adds an FST session. add_peer() must be called calling this
+        function"""
+        if self.peer_obj is None:
+            raise Exception("Peer wasn't added before starting session")
+        grp = ' ' + self.fst_group if self.fst_group != '' else ''
+        sid = self.grequest("FST-MANAGER SESSION_ADD" + grp)
+        sid = sid.strip()
+        if sid.startswith("FAIL"):
+            raise Exception("Cannot add FST session with groupid ==" + grp)
+        return sid
+
+    def set_session_param(self, params):
+        request = "FST-MANAGER SESSION_SET"
+        if params is not None and params != '':
+            request = request + ' ' + params
+        return self.grequest(request)
+
+    def get_session_params(self, sid):
+        request = "FST-MANAGER SESSION_GET " + sid
+        res = self.grequest(request)
+        if res.startswith("FAIL"):
+            return None
+        params = {}
+        for i in res.splitlines():
+            p = i.split('=')
+            params[p[0]] = p[1]
+        return params
+
+    def iface_peers(self, ifname):
+        grp = self.fst_group if self.fst_group != '' else ''
+        res = self.grequest("FST-MANAGER IFACE_PEERS " + grp + ' ' + ifname)
+        if res.startswith("FAIL"):
+            return None
+        return res.splitlines()
+
+    def get_peer_mbies(self, ifname, peer_addr):
+        return self.grequest("FST-MANAGER GET_PEER_MBIES %s %s" % (ifname, peer_addr))
+
+    def list_ifaces(self):
+        grp = self.fst_group if self.fst_group != '' else ''
+        res = self.grequest("FST-MANAGER LIST_IFACES " + grp)
+        if res.startswith("FAIL"):
+            return None
+        ifaces = []
+        for i in res.splitlines():
+            p = i.split(':')
+            iface = {}
+            iface['name'] = p[0]
+            iface['priority'] = p[1]
+            iface['llt'] = p[2]
+            ifaces.append(iface)
+        return ifaces
+
+    def list_groups(self):
+        res = self.grequest("FST-MANAGER LIST_GROUPS")
+        if res.startswith("FAIL"):
+            return None
+        return res.splitlines()
+
+    def configure_session(self, sid, new_iface, old_iface = None):
+        """Calls session_set for a number of parameters some of which are stored
+        in "self" while others are passed to this function explicitly. If
+        old_iface is None, current iface is used; if old_iface is an empty
+        string."""
+        oldiface = old_iface if old_iface is not None else self.iface
+        s = self.set_session_param(sid + ' old_ifname=' + oldiface)
+        if not s.startswith("OK"):
+            raise Exception("Cannot set FST session old_ifname: " + s)
+        if new_iface is not None:
+            s = self.set_session_param(sid + " new_ifname=" + new_iface)
+            if not s.startswith("OK"):
+                raise Exception("Cannot set FST session new_ifname:" + s)
+        if self.new_peer_addr is not None and self.new_peer_addr != '':
+            s = self.set_session_param(sid + " new_peer_addr=" + self.new_peer_addr)
+            if not s.startswith("OK"):
+                raise Exception("Cannot set FST session peer address:" + s + " (new)")
+        if self.old_peer_addr is not None and self.old_peer_addr != '':
+            s = self.set_session_param(sid + " old_peer_addr=" + self.old_peer_addr)
+            if not s.startswith("OK"):
+                raise Exception("Cannot set FST session peer address:" + s + " (old)")
+        if self.fst_llt is not None and self.fst_llt != '':
+            s = self.set_session_param(sid + " llt=" + self.fst_llt)
+            if not s.startswith("OK"):
+                raise Exception("Cannot set FST session llt:" + s)
+
+    def send_iface_attach_request(self, ifname, group, llt, priority):
+        request = "FST-ATTACH " + ifname + ' ' + group
+        if llt is not None:
+            request += " llt=" + llt
+        if priority is not None:
+            request += " priority=" + priority
+        res = self.grequest(request)
+        if not res.startswith("OK"):
+            raise Exception("Cannot attach FST iface: " + res)
+
+    def send_iface_detach_request(self, ifname):
+        res = self.grequest("FST-DETACH " + ifname)
+        if not res.startswith("OK"):
+            raise Exception("Cannot detach FST iface: " + res)
+
+    def send_session_setup_request(self, sid):
+        s = self.grequest("FST-MANAGER SESSION_INITIATE " + sid)
+        if not s.startswith('OK'):
+            raise Exception("Cannot send setup request: %s" % s)
+        return s
+
+    def send_session_setup_response(self, sid, response):
+        request = "FST-MANAGER SESSION_RESPOND " + sid + " " + response
+        s = self.grequest(request)
+        if not s.startswith('OK'):
+            raise Exception("Cannot send setup response: %s" % s)
+        return s
+
+    def send_test_session_setup_request(self, fsts_id,
+                                        additional_parameter = None):
+        request = "FST-MANAGER TEST_REQUEST SEND_SETUP_REQUEST " + fsts_id
+        if additional_parameter is not None:
+            request += " " + additional_parameter
+        s = self.grequest(request)
+        if not s.startswith('OK'):
+            raise Exception("Cannot send FST setup request: %s" % s)
+        return s
+
+    def send_test_session_setup_response(self, fsts_id,
+                                         response, additional_parameter = None):
+        request = "FST-MANAGER TEST_REQUEST SEND_SETUP_RESPONSE " + fsts_id + " " + response
+        if additional_parameter is not None:
+            request += " " + additional_parameter
+        s = self.grequest(request)
+        if not s.startswith('OK'):
+            raise Exception("Cannot send FST setup response: %s" % s)
+        return s
+
+    def send_test_ack_request(self, fsts_id):
+        s = self.grequest("FST-MANAGER TEST_REQUEST SEND_ACK_REQUEST " + fsts_id)
+        if not s.startswith('OK'):
+            raise Exception("Cannot send FST ack request: %s" % s)
+        return s
+
+    def send_test_ack_response(self, fsts_id):
+        s = self.grequest("FST-MANAGER TEST_REQUEST SEND_ACK_RESPONSE " + fsts_id)
+        if not s.startswith('OK'):
+            raise Exception("Cannot send FST ack response: %s" % s)
+        return s
+
+    def send_test_tear_down(self, fsts_id):
+        s = self.grequest("FST-MANAGER TEST_REQUEST SEND_TEAR_DOWN " + fsts_id)
+        if not s.startswith('OK'):
+            raise Exception("Cannot send FST tear down: %s" % s)
+        return s
+
+    def get_fsts_id_by_sid(self, sid):
+        s = self.grequest("FST-MANAGER TEST_REQUEST GET_FSTS_ID " + sid)
+        if s == ' ' or s.startswith('FAIL'):
+            raise Exception("Cannot get fsts_id for sid == %s" % sid)
+        return int(s)
+
+    def wait_for_iface_event(self, timeout):
+        while True:
+            ev = self.wait_gevent(["FST-EVENT-IFACE"], timeout)
+            if ev is None:
+                raise Exception("No FST-EVENT-IFACE received")
+            event = parse_fst_iface_event(ev)
+            if event is None:
+                # We can't parse so it's not our event, wait for next one
+                continue
+            return event
+
+    def wait_for_session_event(self, timeout, events_to_ignore=[],
+                               events_to_count=[]):
+        while True:
+            ev = self.wait_gevent(["FST-EVENT-SESSION"], timeout)
+            if ev is None:
+                raise Exception("No FST-EVENT-SESSION received")
+            event = parse_fst_session_event(ev)
+            if event is None:
+                # We can't parse so it's not our event, wait for next one
+                continue
+            if len(events_to_ignore) > 0:
+                if event['type'] in events_to_ignore:
+                    continue
+            elif len(events_to_count) > 0:
+                if not event['type'] in events_to_count:
+                    continue
+            return event
+
+    def initiate_session(self, sid, response="accept"):
+        """Initiates FST session with given session id 'sid'.
+        'response' is the session respond answer: "accept", "reject", or a
+        special "timeout" value to skip the response in order to test session
+        timeouts.
+        Returns: "OK" - session has been initiated, otherwise the reason for the
+        reset: REASON_REJECT, REASON_STT."""
+        strsid = ' ' + sid if sid != '' else ''
+        s = self.grequest("FST-MANAGER SESSION_INITIATE"+ strsid)
+        if not s.startswith('OK'):
+            raise Exception("Cannot initiate fst session: %s" % s)
+        ev = self.peer_obj.wait_gevent([ "FST-EVENT-SESSION" ], timeout=5)
+        if ev is None:
+            raise Exception("No FST-EVENT-SESSION received")
+        # We got FST event
+        event = parse_fst_session_event(ev)
+        if event == None:
+            raise Exception("Unrecognized FST event: " % ev)
+        if event['type'] != 'EVENT_FST_SETUP':
+            raise Exception("Expected FST_SETUP event, got: " + event['type'])
+        ev = self.peer_obj.wait_gevent(["FST-EVENT-SESSION"], timeout=5)
+        if ev is None:
+            raise Exception("No FST-EVENT-SESSION received")
+        event = parse_fst_session_event(ev)
+        if event == None:
+            raise Exception("Unrecognized FST event: " % ev)
+        if event['type'] != 'EVENT_FST_SESSION_STATE':
+            raise Exception("Expected EVENT_FST_SESSION_STATE event, got: " + event['type'])
+        if event['new_state'] != "SETUP_COMPLETION":
+            raise Exception("Expected new state SETUP_COMPLETION, got: " + event['new_state'])
+        if response == '':
+            return 'OK'
+        if response != "timeout":
+            s = self.peer_obj.grequest("FST-MANAGER SESSION_RESPOND "+ event['id'] + " " + response)  # Or reject
+            if not s.startswith('OK'):
+                raise Exception("Error session_respond: %s" % s)
+        # Wait for EVENT_FST_SESSION_STATE events. We should get at least 2
+        # events. The 1st event will be EVENT_FST_SESSION_STATE
+        # old_state=INITIAL new_state=SETUP_COMPLETED. The 2nd event will be
+        # either EVENT_FST_ESTABLISHED with the session id or
+        # EVENT_FST_SESSION_STATE with new_state=INITIAL if the session was
+        # reset, the reason field will tell why.
+        result = ''
+        while result == '':
+            ev = self.wait_gevent(["FST-EVENT-SESSION"], timeout=5)
+            if ev is None:
+                break # No session event received
+            event = parse_fst_session_event(ev)
+            if event == None:
+                # We can't parse so it's not our event, wait for next one
+                continue
+            if event['type'] == 'EVENT_FST_ESTABLISHED':
+                result = "OK"
+                break
+            elif event['type'] == "EVENT_FST_SESSION_STATE":
+                if event['new_state'] == "INITIAL":
+                    # Session was reset, the only reason to get back to initial
+                    # state.
+                    result = event['reason']
+                    break
+        if result == '':
+            raise Exception("No event for session respond")
+        return result
+
+    def transfer_session(self, sid):
+        """Transfers the session. 'sid' is the session id. 'hsta' is the
+        station-responder object.
+        Returns: REASON_SWITCH - the session has been transferred successfully
+        or a REASON_... reported by the reset event."""
+        request = "FST-MANAGER SESSION_TRANSFER"
+        if sid != '':
+            request += ' ' + sid
+        s = self.grequest(request)
+        if not s.startswith('OK'):
+            raise Exception("Cannot transfer fst session: %s" % s)
+        result = ''
+        while result == '':
+            ev = self.peer_obj.wait_gevent([ "FST-EVENT-SESSION" ], timeout=5)
+            if ev is None:
+                raise Exception("Missing session transfer event")
+            # We got FST event. We expect TRANSITION_CONFIRMED state and then
+            # INITIAL (reset) with the reason (e.g. "REASON_SWITCH").
+            # Right now we'll be waiting for the reset event and record the
+            # reason.
+            event = parse_fst_session_event(ev)
+            if event == None:
+                raise Exception("Unrecognized FST event: " % ev)
+            if event['new_state'] == 'INITIAL':
+                result = event['reason']
+        return result
+
+    def wait_for_tear_down(self):
+        ev = self.wait_gevent([ "FST-EVENT-SESSION" ], timeout=5)
+        if ev is None:
+            raise Exception("No FST-EVENT-SESSION received")
+        # We got FST event
+        event = parse_fst_session_event(ev)
+        if event == None:
+            raise Exception("Unrecognized FST event: " % ev)
+        if event['type'] != 'EVENT_FST_SESSION_STATE':
+            raise Exception("Expected EVENT_FST_SESSION_STATE event, got: " + event['type'])
+        if event['new_state'] != "INITIAL":
+            raise Exception("Expected new state INITIAL, got: " + event['new_state'])
+        if event['reason'] != 'REASON_TEARDOWN':
+            raise Exception("Expected reason REASON_TEARDOWN, got: " + event['reason'])
+
+    def teardown_session(self, sid):
+        """Tears down FST session with a given session id ('sid')"""
+        strsid = ' ' + sid if sid != '' else ''
+        s = self.grequest("FST-MANAGER SESSION_TEARDOWN" + strsid)
+        if not s.startswith('OK'):
+            raise Exception("Cannot tear down fst session: %s" % s)
+        self.peer_obj.wait_for_tear_down()
+
+
+    def remove_session(self, sid, wait_for_tear_down=True):
+        """Removes FST session with a given session id ('sid')"""
+        strsid = ' ' + sid if sid != '' else ''
+        s = self.grequest("FST-MANAGER SESSION_REMOVE" + strsid)
+        if not s.startswith('OK'):
+            raise Exception("Cannot remove fst session: %s" % s)
+        if wait_for_tear_down == True:
+            self.peer_obj.wait_for_tear_down()
+
+    def remove_all_sessions(self):
+        """Removes FST session with a given session id ('sid')"""
+        grp = ' ' + self.fst_group if self.fst_group != '' else ''
+        s = self.grequest("FST-MANAGER LIST_SESSIONS" + grp)
+        if not s.startswith('FAIL'):
+            for sid in s.splitlines():
+                sid = sid.strip()
+                if len(sid) != 0:
+                    self.remove_session(sid, wait_for_tear_down=False)
+
+
+#
+# FstAP class
+#
+class FstAP (FstDevice):
+    def __init__(self, iface, ssid, mode, chan, fst_group, fst_pri,
+                 fst_llt=None, rsn=False):
+        """If fst_group is empty, then FST parameters will not be set
+        If fst_llt is empty, the parameter will not be set and the default value
+        is expected to be configured."""
+        self.ssid = ssid
+        self.mode = mode
+        self.chan = chan
+        self.reg_ctrl = fst_test_common.HapdRegCtrl()
+        self.reg_ctrl.add_ap(iface, self.chan)
+        self.global_instance = hostapd.HostapdGlobal()
+        FstDevice.__init__(self, iface, fst_group, fst_pri, fst_llt, rsn)
+
+    def start(self, return_early=False):
+        """Starts AP the "standard" way as it was intended by hostapd tests.
+        This will work only when FST supports fully dynamically loading
+        parameters in hostapd."""
+        params = {}
+        params['ssid'] = self.ssid
+        params['hw_mode'] = self.mode
+        params['channel'] = self.chan
+        params['country_code'] = 'US'
+        if self.rsn:
+            params['wpa'] = '2'
+            params['wpa_key_mgmt'] = 'WPA-PSK'
+            params['rsn_pairwise'] = 'CCMP'
+            params['wpa_passphrase'] = '12345678'
+        self.hapd=hostapd.add_ap(self.iface, params)
+        if not self.hapd.ping():
+            raise Exception("Could not ping FST hostapd")
+        self.reg_ctrl.start()
+        self.get_global_instance()
+        if return_early:
+            return self.hapd
+        if len(self.fst_group) != 0:
+            self.send_iface_attach_request(self.iface, self.fst_group,
+                                           self.fst_llt, self.fst_pri)
+        return self.hapd
+
+    def stop(self):
+        """Removes the AP, To be used when dynamic fst APs are implemented in
+        hostapd."""
+        if len(self.fst_group) != 0:
+            self.remove_all_sessions()
+            self.send_iface_detach_request(self.iface)
+        self.reg_ctrl.stop()
+        del self.global_instance
+        self.global_instance = None
+
+    def get_instance(self):
+        """Return the Hostapd/WpaSupplicant instance"""
+        if self.instance is None:
+            self.instance = hostapd.Hostapd(self.iface)
+        return self.instance
+
+    def get_global_instance(self):
+        return self.global_instance
+
+    def get_own_mac_address(self):
+        """Gets the device's own MAC address"""
+        h = self.get_instance()
+        status = h.get_status()
+        return status['bssid[0]']
+
+    def get_actual_peer_addr(self):
+        """Gets the peer address. A connected station address is returned."""
+        # Use the device instance, the global control interface doesn't have
+        # station address
+        h = self.get_instance()
+        sta = h.get_sta(None)
+        if sta is None or 'addr' not in sta:
+            # Maybe station is not connected?
+            addr = None
+        else:
+            addr=sta['addr']
+        return addr
+
+    def grequest(self, req):
+        """Send request on the global control interface"""
+        logger.debug("FstAP::grequest: " + req)
+        h = self.get_global_instance()
+        return h.request(req)
+
+    def wait_gevent(self, events, timeout=None):
+        """Wait for a list of events on the global interface"""
+        h = self.get_global_instance()
+        if timeout is not None:
+            return h.wait_event(events, timeout=timeout)
+        else:
+            return h.wait_event(events)
+
+    def get_ssid(self):
+        return self.ssid
+
+#
+# FstSTA class
+#
+class FstSTA (FstDevice):
+    def __init__(self, iface, fst_group, fst_pri, fst_llt=None, rsn=False):
+        """If fst_group is empty, then FST parameters will not be set
+        If fst_llt is empty, the parameter will not be set and the default value
+        is expected to be configured."""
+        FstDevice.__init__(self, iface, fst_group, fst_pri, fst_llt, rsn)
+        self.connected = None # FstAP object the station is connected to
+
+    def start(self):
+        """Current implementation involves running another instance of
+        wpa_supplicant with fixed FST STAs configurations. When any type of
+        dynamic STA loading is implemented, rewrite the function similarly to
+        FstAP."""
+        h = self.get_instance()
+        h.interface_add(self.iface, drv_params="force_connect_cmd=1")
+        if not h.global_ping():
+            raise Exception("Could not ping FST wpa_supplicant")
+        if len(self.fst_group) != 0:
+            self.send_iface_attach_request(self.iface, self.fst_group,
+                                           self.fst_llt, self.fst_pri)
+        return None
+
+    def stop(self):
+        """Removes the STA. In a static (temporary) implementation does nothing,
+        the STA will be removed when the fst wpa_supplicant process is killed by
+        fstap.cleanup()."""
+        h = self.get_instance()
+        if len(self.fst_group) != 0:
+            self.remove_all_sessions()
+            self.send_iface_detach_request(self.iface)
+        h.interface_remove(self.iface)
+        h.close_ctrl()
+        del h
+        self.instance = None
+
+    def get_instance(self):
+        """Return the Hostapd/WpaSupplicant instance"""
+        if self.instance is None:
+             self.instance = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        return self.instance
+
+    def get_own_mac_address(self):
+        """Gets the device's own MAC address"""
+        h = self.get_instance()
+        status = h.get_status()
+        return status['address']
+
+    def get_actual_peer_addr(self):
+        """Gets the peer address. A connected station address is returned"""
+        h = self.get_instance()
+        status = h.get_status()
+        return status['bssid']
+
+    def grequest(self, req):
+        """Send request on the global control interface"""
+        logger.debug("FstSTA::grequest: " + req)
+        h = self.get_instance()
+        return h.global_request(req)
+
+    def wait_gevent(self, events, timeout=None):
+        """Wait for a list of events on the global interface"""
+        h = self.get_instance()
+        if timeout is not None:
+            return h.wait_global_event(events, timeout=timeout)
+        else:
+            return h.wait_global_event(events)
+
+    def scan(self, freq=None, no_wait=False, only_new=False):
+        """Issue Scan with given parameters. Returns the BSS dictionary for the
+        AP found (the 1st BSS found. TODO: What if the AP required is not the
+        1st in list?) or None if no BSS found. None call be also a result of
+        no_wait=True. Note, request("SCAN_RESULTS") can be used to get all the
+        results at once."""
+        h = self.get_instance()
+        h.scan(None, freq, no_wait, only_new)
+        r = h.get_bss('0')
+        return r
+
+    def connect(self, ap, **kwargs):
+        """Connects to the given AP"""
+        if not isinstance(ap, FstAP):
+            raise Exception("Bad AP object to connect to")
+        h = self.get_instance()
+        hap = ap.get_instance()
+        h.connect(ap.get_ssid(), **kwargs)
+        self.connected = ap
+
+    def connect_to_external_ap(self, ap, ssid, check_connection=True, **kwargs):
+        """Connects to the given external AP"""
+        if not isinstance(ap, hostapd.Hostapd):
+            raise Exception("Bad AP object to connect to")
+        h = self.get_instance()
+        h.connect(ssid, **kwargs)
+        self.connected = ap
+        if check_connection:
+            ev = ap.wait_event([ "AP-STA-CONNECTED" ], timeout=10)
+            if ev is None:
+                self.connected = None
+                raise Exception("No connection event received from %s" % ssid)
+
+    def disconnect(self, check_disconnect=True):
+        """Disconnects from the AP the station is currently connected to"""
+        if self.connected is not None:
+            h = self.get_instance()
+            h.request("DISCONNECT")
+            if check_disconnect:
+                hap = self.connected.get_instance()
+                ev = hap.wait_event([ "AP-STA-DISCONNECTED" ], timeout=10)
+                if ev is None:
+                    raise Exception("No disconnection event received from %s" % self.connected.get_ssid())
+            self.connected = None
+
+
+    def disconnect_from_external_ap(self, check_disconnect=True):
+        """Disconnects from the external AP the station is currently connected
+        to"""
+        if self.connected is not None:
+            h = self.get_instance()
+            h.request("DISCONNECT")
+            if check_disconnect:
+                hap = self.connected
+                ev = hap.wait_event([ "AP-STA-DISCONNECTED" ], timeout=10)
+                if ev is None:
+                    raise Exception("No disconnection event received from AP")
+            self.connected = None
diff --git a/hostap/tests/hwsim/fst_test_common.py b/hostap/tests/hwsim/fst_test_common.py
new file mode 100644
index 0000000..22d0ff6
--- /dev/null
+++ b/hostap/tests/hwsim/fst_test_common.py
@@ -0,0 +1,88 @@
+# FST tests related definitions
+# Copyright (c) 2015, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import subprocess
+import logging
+
+import hostapd
+
+logger = logging.getLogger()
+
+fst_test_def_group='fstg0'
+fst_test_def_freq_g='2412' # Channel 1
+fst_test_def_freq_a='5180' # Channel 36
+fst_test_def_chan_g='1'
+fst_test_def_chan_a='36'
+fst_test_def_prio_low='100'
+fst_test_def_prio_high='110'
+fst_test_def_llt='100'
+fst_test_def_reg_domain='00'
+
+class HapdRegCtrl:
+    def __init__(self):
+        self.refcnt = 0
+        self.ifname = None
+        self.changed = False
+
+    def __del__(self):
+        if self.refcnt != 0 and self.changed == True:
+            self.restore_reg_domain()
+
+    def start(self):
+        if self.ifname != None:
+             hapd = hostapd.Hostapd(self.ifname)
+             self.changed = self.wait_hapd_reg_change(hapd)
+
+    def stop(self):
+        if self.changed == True:
+            self.restore_reg_domain()
+            self.changed = False
+
+    def add_ap(self, ifname, chan):
+        if self.changed == False and self.channel_may_require_reg_change(chan):
+             self.ifname = ifname
+
+    @staticmethod
+    def channel_may_require_reg_change(chan):
+        if int(chan) > 14:
+            return True
+        return False
+
+    @staticmethod
+    def wait_hapd_reg_change(hapd):
+        state = hapd.get_status_field("state")
+        if state != "COUNTRY_UPDATE":
+            state = hapd.get_status_field("state")
+            if state != "ENABLED":
+                raise Exception("Unexpected interface state - expected COUNTRY_UPDATE")
+            else:
+                logger.debug("fst hostapd: regulatory domain already set")
+                return True
+
+        logger.debug("fst hostapd: waiting for regulatory domain to be set...")
+
+        ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+        if not ev:
+            raise Exception("AP setup timed out")
+
+        logger.debug("fst hostapd: regulatory domain set")
+
+        state = hapd.get_status_field("state")
+        if state != "ENABLED":
+            raise Exception("Unexpected interface state - expected ENABLED")
+
+        logger.debug("fst hostapd: regulatory domain ready")
+        return True
+
+    @staticmethod
+    def restore_reg_domain():
+        logger.debug("fst hostapd: waiting for regulatory domain to be restored...")
+
+        res = subprocess.call(['iw', 'reg', 'set', fst_test_def_reg_domain])
+        if res != 0:
+            raise Exception("Cannot restore regulatory domain")
+
+        logger.debug("fst hostapd: regulatory domain ready")
diff --git a/hostap/tests/hwsim/hostapd.accept b/hostap/tests/hwsim/hostapd.accept
new file mode 100644
index 0000000..ce455b1
--- /dev/null
+++ b/hostap/tests/hwsim/hostapd.accept
@@ -0,0 +1,2 @@
+02:00:00:00:00:00	1
+02:00:00:00:01:00	2
diff --git a/hostap/tests/hwsim/hostapd.macaddr b/hostap/tests/hwsim/hostapd.macaddr
new file mode 100644
index 0000000..b39390d
--- /dev/null
+++ b/hostap/tests/hwsim/hostapd.macaddr
@@ -0,0 +1,5 @@
+02:00:00:00:00:00
+02:00:00:00:00:12
+02:00:00:00:00:34
+-02:00:00:00:00:12
+-02:00:00:00:00:34
diff --git a/hostap/tests/hwsim/hostapd.py b/hostap/tests/hwsim/hostapd.py
new file mode 100644
index 0000000..33cfa62
--- /dev/null
+++ b/hostap/tests/hwsim/hostapd.py
@@ -0,0 +1,390 @@
+# Python class for controlling hostapd
+# Copyright (c) 2013-2014, 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 time
+import logging
+import binascii
+import struct
+import wpaspy
+
+logger = logging.getLogger()
+hapd_ctrl = '/var/run/hostapd'
+hapd_global = '/var/run/hostapd-global'
+
+def mac2tuple(mac):
+    return struct.unpack('6B', binascii.unhexlify(mac.replace(':','')))
+
+class HostapdGlobal:
+    def __init__(self):
+        self.ctrl = wpaspy.Ctrl(hapd_global)
+        self.mon = wpaspy.Ctrl(hapd_global)
+        self.mon.attach()
+
+    def request(self, cmd):
+        return self.ctrl.request(cmd)
+
+    def wait_event(self, events, timeout):
+        start = os.times()[4]
+        while True:
+            while self.mon.pending():
+                ev = self.mon.recv()
+                logger.debug("(global): " + ev)
+                for event in events:
+                    if event in ev:
+                        return ev
+            now = os.times()[4]
+            remaining = start + timeout - now
+            if remaining <= 0:
+                break
+            if not self.mon.pending(timeout=remaining):
+                break
+        return None
+
+    def request(self, cmd):
+        return self.ctrl.request(cmd)
+
+    def add(self, ifname, driver=None):
+        cmd = "ADD " + ifname + " " + hapd_ctrl
+        if driver:
+            cmd += " " + driver
+        res = self.ctrl.request(cmd)
+        if not "OK" in res:
+            raise Exception("Could not add hostapd interface " + ifname)
+
+    def add_iface(self, ifname, confname):
+        res = self.ctrl.request("ADD " + ifname + " config=" + confname)
+        if not "OK" in res:
+            raise Exception("Could not add hostapd interface")
+
+    def add_bss(self, phy, confname, ignore_error=False):
+        res = self.ctrl.request("ADD bss_config=" + phy + ":" + confname)
+        if not "OK" in res:
+            if not ignore_error:
+                raise Exception("Could not add hostapd BSS")
+
+    def remove(self, ifname):
+        self.ctrl.request("REMOVE " + ifname, timeout=30)
+
+    def relog(self):
+        self.ctrl.request("RELOG")
+
+    def flush(self):
+        self.ctrl.request("FLUSH")
+
+
+class Hostapd:
+    def __init__(self, ifname, bssidx=0):
+        self.ifname = ifname
+        self.ctrl = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
+        self.mon = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
+        self.mon.attach()
+        self.bssid = None
+        self.bssidx = bssidx
+
+    def own_addr(self):
+        if self.bssid is None:
+            self.bssid = self.get_status_field('bssid[%d]' % self.bssidx)
+        return self.bssid
+
+    def request(self, cmd):
+        logger.debug(self.ifname + ": CTRL: " + cmd)
+        return self.ctrl.request(cmd)
+
+    def ping(self):
+        return "PONG" in self.request("PING")
+
+    def set(self, field, value):
+        if not "OK" in self.request("SET " + field + " " + value):
+            raise Exception("Failed to set hostapd parameter " + field)
+
+    def set_defaults(self):
+        self.set("driver", "nl80211")
+        self.set("hw_mode", "g")
+        self.set("channel", "1")
+        self.set("ieee80211n", "1")
+        self.set("logger_stdout", "-1")
+        self.set("logger_stdout_level", "0")
+
+    def set_open(self, ssid):
+        self.set_defaults()
+        self.set("ssid", ssid)
+
+    def set_wpa2_psk(self, ssid, passphrase):
+        self.set_defaults()
+        self.set("ssid", ssid)
+        self.set("wpa_passphrase", passphrase)
+        self.set("wpa", "2")
+        self.set("wpa_key_mgmt", "WPA-PSK")
+        self.set("rsn_pairwise", "CCMP")
+
+    def set_wpa_psk(self, ssid, passphrase):
+        self.set_defaults()
+        self.set("ssid", ssid)
+        self.set("wpa_passphrase", passphrase)
+        self.set("wpa", "1")
+        self.set("wpa_key_mgmt", "WPA-PSK")
+        self.set("wpa_pairwise", "TKIP")
+
+    def set_wpa_psk_mixed(self, ssid, passphrase):
+        self.set_defaults()
+        self.set("ssid", ssid)
+        self.set("wpa_passphrase", passphrase)
+        self.set("wpa", "3")
+        self.set("wpa_key_mgmt", "WPA-PSK")
+        self.set("wpa_pairwise", "TKIP")
+        self.set("rsn_pairwise", "CCMP")
+
+    def set_wep(self, ssid, key):
+        self.set_defaults()
+        self.set("ssid", ssid)
+        self.set("wep_key0", key)
+
+    def enable(self):
+        if not "OK" in self.request("ENABLE"):
+            raise Exception("Failed to enable hostapd interface " + self.ifname)
+
+    def disable(self):
+        if not "OK" in self.request("DISABLE"):
+            raise Exception("Failed to disable hostapd interface " + self.ifname)
+
+    def dump_monitor(self):
+        while self.mon.pending():
+            ev = self.mon.recv()
+            logger.debug(self.ifname + ": " + ev)
+
+    def wait_event(self, events, timeout):
+        start = os.times()[4]
+        while True:
+            while self.mon.pending():
+                ev = self.mon.recv()
+                logger.debug(self.ifname + ": " + ev)
+                for event in events:
+                    if event in ev:
+                        return ev
+            now = os.times()[4]
+            remaining = start + timeout - now
+            if remaining <= 0:
+                break
+            if not self.mon.pending(timeout=remaining):
+                break
+        return None
+
+    def get_status(self):
+        res = self.request("STATUS")
+        lines = res.splitlines()
+        vals = dict()
+        for l in lines:
+            [name,value] = l.split('=', 1)
+            vals[name] = value
+        return vals
+
+    def get_status_field(self, field):
+        vals = self.get_status()
+        if field in vals:
+            return vals[field]
+        return None
+
+    def get_driver_status(self):
+        res = self.request("STATUS-DRIVER")
+        lines = res.splitlines()
+        vals = dict()
+        for l in lines:
+            [name,value] = l.split('=', 1)
+            vals[name] = value
+        return vals
+
+    def get_driver_status_field(self, field):
+        vals = self.get_driver_status()
+        if field in vals:
+            return vals[field]
+        return None
+
+    def get_config(self):
+        res = self.request("GET_CONFIG")
+        lines = res.splitlines()
+        vals = dict()
+        for l in lines:
+            [name,value] = l.split('=', 1)
+            vals[name] = value
+        return vals
+
+    def mgmt_rx(self, timeout=5):
+        ev = self.wait_event(["MGMT-RX"], timeout=timeout)
+        if ev is None:
+            return None
+        msg = {}
+        frame = binascii.unhexlify(ev.split(' ')[1])
+        msg['frame'] = frame
+
+        hdr = struct.unpack('<HH6B6B6BH', frame[0:24])
+        msg['fc'] = hdr[0]
+        msg['subtype'] = (hdr[0] >> 4) & 0xf
+        hdr = hdr[1:]
+        msg['duration'] = hdr[0]
+        hdr = hdr[1:]
+        msg['da'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
+        hdr = hdr[6:]
+        msg['sa'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
+        hdr = hdr[6:]
+        msg['bssid'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
+        hdr = hdr[6:]
+        msg['seq_ctrl'] = hdr[0]
+        msg['payload'] = frame[24:]
+
+        return msg
+
+    def mgmt_tx(self, msg):
+        t = (msg['fc'], 0) + mac2tuple(msg['da']) + mac2tuple(msg['sa']) + mac2tuple(msg['bssid']) + (0,)
+        hdr = struct.pack('<HH6B6B6BH', *t)
+        self.request("MGMT_TX " + binascii.hexlify(hdr + msg['payload']))
+
+    def get_sta(self, addr, info=None, next=False):
+        cmd = "STA-NEXT " if next else "STA "
+        if addr is None:
+            res = self.request("STA-FIRST")
+        elif info:
+            res = self.request(cmd + addr + " " + info)
+        else:
+            res = self.request(cmd + addr)
+        lines = res.splitlines()
+        vals = dict()
+        first = True
+        for l in lines:
+            if first and '=' not in l:
+                vals['addr'] = l
+                first = False
+            else:
+                [name,value] = l.split('=', 1)
+                vals[name] = value
+        return vals
+
+    def get_mib(self, param=None):
+        if param:
+            res = self.request("MIB " + param)
+        else:
+            res = self.request("MIB")
+        lines = res.splitlines()
+        vals = dict()
+        for l in lines:
+            name_val = l.split('=', 1)
+            if len(name_val) > 1:
+                vals[name_val[0]] = name_val[1]
+        return vals
+
+def add_ap(ifname, params, wait_enabled=True, no_enable=False):
+        logger.info("Starting AP " + ifname)
+        hapd_global = HostapdGlobal()
+        hapd_global.remove(ifname)
+        hapd_global.add(ifname)
+        hapd = Hostapd(ifname)
+        if not hapd.ping():
+            raise Exception("Could not ping hostapd")
+        hapd.set_defaults()
+        fields = [ "ssid", "wpa_passphrase", "nas_identifier", "wpa_key_mgmt",
+                   "wpa",
+                   "wpa_pairwise", "rsn_pairwise", "auth_server_addr",
+                   "acct_server_addr", "osu_server_uri" ]
+        for field in fields:
+            if field in params:
+                hapd.set(field, params[field])
+        for f,v in params.items():
+            if f in fields:
+                continue
+            if isinstance(v, list):
+                for val in v:
+                    hapd.set(f, val)
+            else:
+                hapd.set(f, v)
+        if no_enable:
+            return hapd
+        hapd.enable()
+        if wait_enabled:
+            ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=30)
+            if ev is None:
+                raise Exception("AP startup timed out")
+            if "AP-ENABLED" not in ev:
+                raise Exception("AP startup failed")
+        return hapd
+
+def add_bss(phy, ifname, confname, ignore_error=False):
+    logger.info("Starting BSS phy=" + phy + " ifname=" + ifname)
+    hapd_global = HostapdGlobal()
+    hapd_global.add_bss(phy, confname, ignore_error)
+    hapd = Hostapd(ifname)
+    if not hapd.ping():
+        raise Exception("Could not ping hostapd")
+
+def add_iface(ifname, confname):
+    logger.info("Starting interface " + ifname)
+    hapd_global = HostapdGlobal()
+    hapd_global.add_iface(ifname, confname)
+    hapd = Hostapd(ifname)
+    if not hapd.ping():
+        raise Exception("Could not ping hostapd")
+
+def remove_bss(ifname):
+    logger.info("Removing BSS " + ifname)
+    hapd_global = HostapdGlobal()
+    hapd_global.remove(ifname)
+
+def wpa2_params(ssid=None, passphrase=None):
+    params = { "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK",
+               "rsn_pairwise": "CCMP" }
+    if ssid:
+        params["ssid"] = ssid
+    if passphrase:
+        params["wpa_passphrase"] = passphrase
+    return params
+
+def wpa_params(ssid=None, passphrase=None):
+    params = { "wpa": "1",
+               "wpa_key_mgmt": "WPA-PSK",
+               "wpa_pairwise": "TKIP" }
+    if ssid:
+        params["ssid"] = ssid
+    if passphrase:
+        params["wpa_passphrase"] = passphrase
+    return params
+
+def wpa_mixed_params(ssid=None, passphrase=None):
+    params = { "wpa": "3",
+               "wpa_key_mgmt": "WPA-PSK",
+               "wpa_pairwise": "TKIP",
+               "rsn_pairwise": "CCMP" }
+    if ssid:
+        params["ssid"] = ssid
+    if passphrase:
+        params["wpa_passphrase"] = passphrase
+    return params
+
+def radius_params():
+    params = { "auth_server_addr": "127.0.0.1",
+               "auth_server_port": "1812",
+               "auth_server_shared_secret": "radius",
+               "nas_identifier": "nas.w1.fi" }
+    return params
+
+def wpa_eap_params(ssid=None):
+    params = radius_params()
+    params["wpa"] = "1"
+    params["wpa_key_mgmt"] = "WPA-EAP"
+    params["wpa_pairwise"] = "TKIP"
+    params["ieee8021x"] = "1"
+    if ssid:
+        params["ssid"] = ssid
+    return params
+
+def wpa2_eap_params(ssid=None):
+    params = radius_params()
+    params["wpa"] = "2"
+    params["wpa_key_mgmt"] = "WPA-EAP"
+    params["rsn_pairwise"] = "CCMP"
+    params["ieee8021x"] = "1"
+    if ssid:
+        params["ssid"] = ssid
+    return params
diff --git a/hostap/tests/hwsim/hostapd.vlan b/hostap/tests/hwsim/hostapd.vlan
new file mode 100644
index 0000000..b0e905b
--- /dev/null
+++ b/hostap/tests/hwsim/hostapd.vlan
@@ -0,0 +1,2 @@
+1	hwsimvlan1
+*	testvlan#
diff --git a/hostap/tests/hwsim/hostapd.wlan3.vlan b/hostap/tests/hwsim/hostapd.wlan3.vlan
new file mode 100644
index 0000000..3155e26
--- /dev/null
+++ b/hostap/tests/hwsim/hostapd.wlan3.vlan
@@ -0,0 +1 @@
+1	wlan3.1
diff --git a/hostap/tests/hwsim/hostapd.wlan4.vlan b/hostap/tests/hwsim/hostapd.wlan4.vlan
new file mode 100644
index 0000000..75ac704
--- /dev/null
+++ b/hostap/tests/hwsim/hostapd.wlan4.vlan
@@ -0,0 +1 @@
+1	wlan4.1
diff --git a/hostap/tests/hwsim/hostapd.wpa_psk b/hostap/tests/hwsim/hostapd.wpa_psk
new file mode 100644
index 0000000..7644f89
--- /dev/null
+++ b/hostap/tests/hwsim/hostapd.wpa_psk
@@ -0,0 +1,5 @@
+00:00:00:00:00:00 secret passphrase
+02:00:00:00:00:00 very secret
+00:11:22:33:44:55 another passphrase
+00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+00:00:00:00:00:00 another passphrase for all STAs
diff --git a/hostap/tests/hwsim/hwsim.py b/hostap/tests/hwsim/hwsim.py
new file mode 100644
index 0000000..e21c814
--- /dev/null
+++ b/hostap/tests/hwsim/hwsim.py
@@ -0,0 +1,114 @@
+#
+# HWSIM generic netlink controller code
+# Copyright (c) 2014	Intel Corporation
+#
+# Author: Johannes Berg <johannes.berg@intel.com>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import netlink, os
+
+# constants
+HWSIM_CMD_CREATE_RADIO		= 4
+HWSIM_CMD_DESTROY_RADIO		= 5
+
+HWSIM_ATTR_CHANNELS		= 9
+HWSIM_ATTR_RADIO_ID		= 10
+HWSIM_ATTR_SUPPORT_P2P_DEVICE	= 14
+HWSIM_ATTR_USE_CHANCTX		= 15
+
+# the controller class
+class HWSimController(object):
+    def __init__(self):
+        self._conn = netlink.Connection(netlink.NETLINK_GENERIC)
+        self._fid = netlink.genl_controller.get_family_id('MAC80211_HWSIM')
+
+    def create_radio(self, n_channels=None, use_chanctx=False,
+                     use_p2p_device=False):
+        attrs = []
+        if n_channels:
+            attrs.append(netlink.U32Attr(HWSIM_ATTR_CHANNELS, n_channels))
+        if use_chanctx:
+            attrs.append(netlink.FlagAttr(HWSIM_ATTR_USE_CHANCTX))
+        if use_p2p_device:
+            attrs.append(netlink.FlagAttr(HWSIM_ATTR_SUPPORT_P2P_DEVICE))
+
+        msg = netlink.GenlMessage(self._fid, HWSIM_CMD_CREATE_RADIO,
+                                  flags = netlink.NLM_F_REQUEST |
+                                          netlink.NLM_F_ACK,
+                                  attrs = attrs)
+        return msg.send_and_recv(self._conn).ret
+
+    def destroy_radio(self, radio_id):
+        attrs = [netlink.U32Attr(HWSIM_ATTR_RADIO_ID, radio_id)]
+        msg = netlink.GenlMessage(self._fid, HWSIM_CMD_DESTROY_RADIO,
+                                  flags = netlink.NLM_F_REQUEST |
+                                          netlink.NLM_F_ACK,
+                                  attrs = attrs)
+        msg.send_and_recv(self._conn)
+
+class HWSimRadio(object):
+    def __init__(self, n_channels=None, use_chanctx=False,
+                 use_p2p_device=False):
+        self._controller = HWSimController()
+        self._n_channels = n_channels
+        self._use_chanctx = use_chanctx
+        self._use_p2p_dev = use_p2p_device
+
+    def __enter__(self):
+        self._radio_id = self._controller.create_radio(
+              n_channels=self._n_channels,
+              use_chanctx=self._use_chanctx,
+              use_p2p_device=self._use_p2p_dev)
+        if self._radio_id < 0:
+            raise Exception("Failed to create radio (err:%d)" % self._radio_id)
+        try:
+            iface = os.listdir('/sys/class/mac80211_hwsim/hwsim%d/net/' % self._radio_id)[0]
+        except Exception,e:
+            self._controller.destroy_radio(self._radio_id)
+            raise e
+        return self._radio_id, iface
+
+    def __exit__(self, type, value, traceback):
+        self._controller.destroy_radio(self._radio_id)
+
+
+def create(args):
+    print 'Created radio %d' % c.create_radio(n_channels=args.channels,
+                                              use_chanctx=args.chanctx)
+
+def destroy(args):
+    print c.destroy_radio(args.radio)
+
+if __name__ == '__main__':
+    import argparse
+    c = HWSimController()
+
+    parser = argparse.ArgumentParser(description='send hwsim control commands')
+    subparsers = parser.add_subparsers(help="Commands", dest='command')
+    parser_create = subparsers.add_parser('create', help='create a radio')
+    parser_create.add_argument('--channels', metavar='<number_of_channels>', type=int,
+                               default=0,
+                               help='Number of concurrent channels supported ' +
+                               'by the radio. If not specified, the number ' +
+                               'of channels specified in the ' +
+                               'mac80211_hwsim.channels module parameter is ' +
+                               'used')
+    parser_create.add_argument('--chanctx', action="store_true",
+                               help='Use channel contexts, regardless of ' +
+                               'whether the number of channels is 1 or ' +
+                               'greater. By default channel contexts are ' +
+                               'only used if the number of channels is ' +
+                               'greater than 1.')
+    parser_create.set_defaults(func=create)
+
+    parser_destroy = subparsers.add_parser('destroy', help='destroy a radio')
+    parser_destroy.add_argument('radio', metavar='<radio>', type=int,
+                                default=0,
+                                help='The number of the radio to be ' +
+                                'destroyed (i.e., 0 for phy0, 1 for phy1...)')
+    parser_destroy.set_defaults(func=destroy)
+
+    args = parser.parse_args()
+    args.func(args)
diff --git a/hostap/tests/hwsim/hwsim_utils.py b/hostap/tests/hwsim/hwsim_utils.py
new file mode 100644
index 0000000..85f54a2
--- /dev/null
+++ b/hostap/tests/hwsim/hwsim_utils.py
@@ -0,0 +1,160 @@
+# hwsim testing utilities
+# Copyright (c) 2013-2014, 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 subprocess
+import time
+import logging
+logger = logging.getLogger()
+
+from wpasupplicant import WpaSupplicant
+
+def run_connectivity_test(dev1, dev2, tos, dev1group=False, dev2group=False,
+                          ifname1=None, ifname2=None, config=True, timeout=5):
+    addr1 = dev1.own_addr()
+    if not dev1group and isinstance(dev1, WpaSupplicant):
+        addr1 = dev1.get_driver_status_field('addr')
+
+    addr2 = dev2.own_addr()
+    if not dev2group and isinstance(dev2, WpaSupplicant):
+        addr2 = dev2.get_driver_status_field('addr')
+
+    dev1.dump_monitor()
+    dev2.dump_monitor()
+
+    try:
+        if config:
+            cmd = "DATA_TEST_CONFIG 1"
+            if ifname1:
+                cmd = cmd + " ifname=" + ifname1
+            if dev1group:
+                res = dev1.group_request(cmd)
+            else:
+                res = dev1.request(cmd)
+            if "OK" not in res:
+                raise Exception("Failed to enable data test functionality")
+
+            cmd = "DATA_TEST_CONFIG 1"
+            if ifname2:
+                cmd = cmd + " ifname=" + ifname2
+            if dev2group:
+                res = dev2.group_request(cmd)
+            else:
+                res = dev2.request(cmd)
+            if "OK" not in res:
+                raise Exception("Failed to enable data test functionality")
+
+        cmd = "DATA_TEST_TX {} {} {}".format(addr2, addr1, tos)
+        if dev1group:
+            dev1.group_request(cmd)
+        else:
+            dev1.request(cmd)
+        if dev2group:
+            ev = dev2.wait_group_event(["DATA-TEST-RX"], timeout=timeout)
+        else:
+            ev = dev2.wait_event(["DATA-TEST-RX"], timeout=timeout)
+        if ev is None:
+            raise Exception("dev1->dev2 unicast data delivery failed")
+        if "DATA-TEST-RX {} {}".format(addr2, addr1) not in ev:
+            raise Exception("Unexpected dev1->dev2 unicast data result")
+
+        cmd = "DATA_TEST_TX ff:ff:ff:ff:ff:ff {} {}".format(addr1, tos)
+        if dev1group:
+            dev1.group_request(cmd)
+        else:
+            dev1.request(cmd)
+        if dev2group:
+            ev = dev2.wait_group_event(["DATA-TEST-RX"], timeout=timeout)
+        else:
+            ev = dev2.wait_event(["DATA-TEST-RX"], timeout=timeout)
+        if ev is None:
+            raise Exception("dev1->dev2 broadcast data delivery failed")
+        if "DATA-TEST-RX ff:ff:ff:ff:ff:ff {}".format(addr1) not in ev:
+            raise Exception("Unexpected dev1->dev2 broadcast data result")
+
+        cmd = "DATA_TEST_TX {} {} {}".format(addr1, addr2, tos)
+        if dev2group:
+            dev2.group_request(cmd)
+        else:
+            dev2.request(cmd)
+        if dev1group:
+            ev = dev1.wait_group_event(["DATA-TEST-RX"], timeout=timeout)
+        else:
+            ev = dev1.wait_event(["DATA-TEST-RX"], timeout=timeout)
+        if ev is None:
+            raise Exception("dev2->dev1 unicast data delivery failed")
+        if "DATA-TEST-RX {} {}".format(addr1, addr2) not in ev:
+            raise Exception("Unexpected dev2->dev1 unicast data result")
+
+        cmd = "DATA_TEST_TX ff:ff:ff:ff:ff:ff {} {}".format(addr2, tos)
+        if dev2group:
+            dev2.group_request(cmd)
+        else:
+            dev2.request(cmd)
+        if dev1group:
+            ev = dev1.wait_group_event(["DATA-TEST-RX"], timeout=timeout)
+        else:
+            ev = dev1.wait_event(["DATA-TEST-RX"], timeout=timeout)
+        if ev is None:
+            raise Exception("dev2->dev1 broadcast data delivery failed")
+        if "DATA-TEST-RX ff:ff:ff:ff:ff:ff {}".format(addr2) not in ev:
+            raise Exception("Unexpected dev2->dev1 broadcast data result")
+    finally:
+        if config:
+            if dev1group:
+                dev1.group_request("DATA_TEST_CONFIG 0")
+            else:
+                dev1.request("DATA_TEST_CONFIG 0")
+            if dev2group:
+                dev2.group_request("DATA_TEST_CONFIG 0")
+            else:
+                dev2.request("DATA_TEST_CONFIG 0")
+
+def test_connectivity(dev1, dev2, dscp=None, tos=None, max_tries=1,
+                      dev1group=False, dev2group=False,
+                      ifname1=None, ifname2=None, config=True, timeout=5):
+    if dscp:
+        tos = dscp << 2
+    if not tos:
+        tos = 0
+
+    success = False
+    last_err = None
+    for i in range(0, max_tries):
+        try:
+            run_connectivity_test(dev1, dev2, tos, dev1group, dev2group,
+                                  ifname1, ifname2, config=config,
+                                  timeout=timeout)
+            success = True
+            break
+        except Exception, e:
+            last_err = e
+            if i + 1 < max_tries:
+                time.sleep(1)
+    if not success:
+        raise Exception(last_err)
+
+def test_connectivity_iface(dev1, dev2, ifname, dscp=None, tos=None,
+                            max_tries=1):
+    test_connectivity(dev1, dev2, dscp, tos, ifname2=ifname,
+                      max_tries=max_tries)
+
+def test_connectivity_p2p(dev1, dev2, dscp=None, tos=None):
+    test_connectivity(dev1, dev2, dscp, tos, dev1group=True, dev2group=True)
+
+def test_connectivity_p2p_sta(dev1, dev2, dscp=None, tos=None):
+    test_connectivity(dev1, dev2, dscp, tos, dev1group=True, dev2group=False)
+
+def test_connectivity_sta(dev1, dev2, dscp=None, tos=None):
+    test_connectivity(dev1, dev2, dscp, tos)
+
+(PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL) = range(4)
+
+def set_powersave(dev, val):
+    phy = dev.get_driver_status_field("phyname")
+    psf = open('/sys/kernel/debug/ieee80211/%s/hwsim/ps' % phy, 'w')
+    psf.write('%d\n' % val)
+    psf.close()
diff --git a/hostap/tests/hwsim/multi-bss-acs.conf b/hostap/tests/hwsim/multi-bss-acs.conf
new file mode 100644
index 0000000..4e1db46
--- /dev/null
+++ b/hostap/tests/hwsim/multi-bss-acs.conf
@@ -0,0 +1,28 @@
+driver=nl80211
+
+hw_mode=g
+channel=0
+ieee80211n=1
+
+interface=wlan3
+ctrl_interface=/var/run/hostapd
+
+ssid=bss-1
+
+bss=wlan3-2
+bssid=02:00:00:00:03:01
+ctrl_interface=/var/run/hostapd
+ssid=bss-2
+wpa=2
+wpa_key_mgmt=WPA-PSK
+rsn_pairwise=CCMP
+wpa_passphrase=12345678
+
+bss=wlan3-3
+bssid=02:00:00:00:03:02
+ctrl_interface=/var/run/hostapd
+ssid=bss-3
+wpa=1
+wpa_key_mgmt=WPA-PSK
+rsn_pairwise=TKIP
+wpa_passphrase=qwertyuiop
diff --git a/hostap/tests/hwsim/multi-bss-iface.conf b/hostap/tests/hwsim/multi-bss-iface.conf
new file mode 100644
index 0000000..6b6167f
--- /dev/null
+++ b/hostap/tests/hwsim/multi-bss-iface.conf
@@ -0,0 +1,40 @@
+driver=nl80211
+
+hw_mode=g
+channel=1
+ieee80211n=1
+
+interface=wlan3
+ctrl_interface=/var/run/hostapd
+
+ssid=bss-1
+dynamic_vlan=1
+vlan_tagged_interface=dummy0
+vlan_bridge=brvlan
+wpa=2
+wpa_key_mgmt=WPA-EAP
+rsn_pairwise=CCMP
+ieee8021x=1
+auth_server_addr=127.0.0.1
+auth_server_port=18128
+auth_server_shared_secret=radius
+nas_identifier=nas.w1.fi
+vlan_naming=1
+
+bss=wlan3-2
+bssid=02:00:00:00:03:01
+ctrl_interface=/var/run/hostapd
+ssid=bss-2
+
+dynamic_vlan=1
+vlan_tagged_interface=dummy0
+vlan_bridge=brvlan
+wpa=2
+wpa_key_mgmt=WPA-EAP
+rsn_pairwise=CCMP
+ieee8021x=1
+auth_server_addr=127.0.0.1
+auth_server_port=18128
+auth_server_shared_secret=radius
+nas_identifier=nas.w1.fi
+vlan_naming=1
diff --git a/hostap/tests/hwsim/multi-bss.conf b/hostap/tests/hwsim/multi-bss.conf
new file mode 100644
index 0000000..64584b6
--- /dev/null
+++ b/hostap/tests/hwsim/multi-bss.conf
@@ -0,0 +1,21 @@
+driver=nl80211
+
+hw_mode=g
+channel=1
+ieee80211n=1
+
+interface=wlan3
+ctrl_interface=/var/run/hostapd
+
+ssid=bss-1
+
+
+bss=wlan3-2
+bssid=02:00:00:00:03:01
+ctrl_interface=/var/run/hostapd
+ssid=bss-2
+
+bss=wlan3-3
+bssid=02:00:00:00:03:02
+ctrl_interface=/var/run/hostapd
+ssid=bss-3
diff --git a/hostap/tests/hwsim/netlink.py b/hostap/tests/hwsim/netlink.py
new file mode 100644
index 0000000..82b6fa2
--- /dev/null
+++ b/hostap/tests/hwsim/netlink.py
@@ -0,0 +1,238 @@
+#
+# (Generic) Netlink message generation/parsing
+# Copyright (c) 2007	Johannes Berg <johannes@sipsolutions.net>
+# Copyright (c) 2014	Intel Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import struct, socket
+
+# flags
+NLM_F_REQUEST = 1
+NLM_F_MULTI = 2
+NLM_F_ACK = 4
+NLM_F_ECHO = 8
+
+# types
+NLMSG_NOOP	= 1
+NLMSG_ERROR	= 2
+NLMSG_DONE	= 3
+NLMSG_OVERRUN	= 4
+NLMSG_MIN_TYPE	= 0x10
+
+class Attr(object):
+    def __init__(self, attr_type, data, *values):
+        self._type = attr_type
+        if len(values):
+            self._data = struct.pack(data, *values)
+        else:
+            self._data = data
+
+    def _dump(self):
+        hdr = struct.pack("HH", len(self._data) + 4, self._type)
+        length = len(self._data)
+        pad = ((length + 4 - 1) & ~3 ) - length
+        return hdr + self._data + '\0' * pad
+
+    def __repr__(self):
+        return '<Attr type %d, data "%s">' % (self._type, repr(self._data))
+
+    def u16(self):
+        return struct.unpack('H', self._data)[0]
+    def s16(self):
+        return struct.unpack('h', self._data)[0]
+    def u32(self):
+        return struct.unpack('I', self._data)[0]
+    def s32(self):
+        return struct.unpack('i', self._data)[0]
+    def str(self):
+        return self._data
+    def nulstr(self):
+        return self._data.split('\0')[0]
+    def nested(self):
+        return parse_attributes(self._data)
+
+class StrAttr(Attr):
+    def __init__(self, attr_type, data):
+        Attr.__init__(self, attr_type, "%ds" % len(data), data)
+
+class NulStrAttr(Attr):
+    def __init__(self, attr_type, data):
+        Attr.__init__(self, attr_type, "%dsB" % len(data), data, 0)
+
+class U32Attr(Attr):
+    def __init__(self, attr_type, val):
+        Attr.__init__(self, attr_type, "I", val)
+
+class U8Attr(Attr):
+    def __init__(self, attr_type, val):
+        Attr.__init__(self, attr_type, "B", val)
+
+class FlagAttr(Attr):
+    def __init__(self, attr_type):
+        Attr.__init__(self, attr_type, "")
+
+class Nested(Attr):
+    def __init__(self, attr_type, attrs):
+        self.attrs = attrs
+        self.type = attr_type
+
+    def _dump(self):
+        contents = []
+        for attr in self.attrs:
+            contents.append(attr._dump())
+        contents = ''.join(contents)
+        length = len(contents)
+        hdr = struct.pack("HH", length+4, self.type)
+        return hdr + contents
+
+NETLINK_ROUTE		= 0
+NETLINK_UNUSED		= 1
+NETLINK_USERSOCK	= 2
+NETLINK_FIREWALL	= 3
+NETLINK_INET_DIAG	= 4
+NETLINK_NFLOG		= 5
+NETLINK_XFRM		= 6
+NETLINK_SELINUX		= 7
+NETLINK_ISCSI		= 8
+NETLINK_AUDIT		= 9
+NETLINK_FIB_LOOKUP	= 10
+NETLINK_CONNECTOR	= 11
+NETLINK_NETFILTER	= 12
+NETLINK_IP6_FW		= 13
+NETLINK_DNRTMSG		= 14
+NETLINK_KOBJECT_UEVENT	= 15
+NETLINK_GENERIC 	= 16
+
+class Message(object):
+    def __init__(self, msg_type, flags=0, seq=-1, payload=None):
+        self.type = msg_type
+        self.flags = flags
+        self.seq = seq
+        self.pid = -1
+        payload = payload or []
+        if isinstance(payload, list):
+            contents = []
+            for attr in payload:
+                contents.append(attr._dump())
+            self.payload = ''.join(contents)
+        else:
+            self.payload = payload
+
+    def send(self, conn):
+        if self.seq == -1:
+            self.seq = conn.seq()
+
+        self.pid = conn.pid
+        length = len(self.payload)
+
+        hdr = struct.pack("IHHII", length + 4*4, self.type,
+                          self.flags, self.seq, self.pid)
+        conn.send(hdr + self.payload)
+
+    def __repr__(self):
+        return '<netlink.Message type=%d, pid=%d, seq=%d, flags=0x%x "%s">' % (
+            self.type, self.pid, self.seq, self.flags, repr(self.payload))
+
+    @property
+    def ret(self):
+        assert self.type == NLMSG_ERROR
+        return struct.unpack("i", self.payload[:4])[0]
+
+    def send_and_recv(self, conn):
+        self.send(conn)
+        while True:
+            m = conn.recv()
+            if m.seq == self.seq:
+                return m
+
+class Connection(object):
+    def __init__(self, nltype, groups=0, unexpected_msg_handler=None):
+        self.descriptor = socket.socket(socket.AF_NETLINK,
+                                        socket.SOCK_RAW, nltype)
+        self.descriptor.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)
+        self.descriptor.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536)
+        self.descriptor.bind((0, groups))
+        self.pid, self.groups = self.descriptor.getsockname()
+        self._seq = 0
+        self.unexpected = unexpected_msg_handler
+    def send(self, msg):
+        self.descriptor.send(msg)
+    def recv(self):
+        contents = self.descriptor.recv(16384)
+        # XXX: python doesn't give us message flags, check
+        #      len(contents) vs. msglen for TRUNC
+        msglen, msg_type, flags, seq, pid = struct.unpack("IHHII", 
+                                                          contents[:16])
+        msg = Message(msg_type, flags, seq, contents[16:])
+        msg.pid = pid
+        if msg.type == NLMSG_ERROR:
+            import os
+            errno = msg.ret
+            if errno < 0:
+                err = OSError("Netlink error: %s (%d)" % (
+                    os.strerror(-errno), -errno))
+                err.errno = -errno
+                raise err
+        return msg
+    def seq(self):
+        self._seq += 1
+        return self._seq
+
+def parse_attributes(data):
+    attrs = {}
+    while len(data):
+        attr_len, attr_type = struct.unpack("HH", data[:4])
+        attrs[attr_type] = Attr(attr_type, data[4:attr_len])
+        attr_len = ((attr_len + 4 - 1) & ~3 )
+        data = data[attr_len:]
+    return attrs
+
+
+
+CTRL_CMD_UNSPEC		= 0
+CTRL_CMD_NEWFAMILY	= 1
+CTRL_CMD_DELFAMILY	= 2
+CTRL_CMD_GETFAMILY	= 3
+CTRL_CMD_NEWOPS		= 4
+CTRL_CMD_DELOPS		= 5
+CTRL_CMD_GETOPS		= 6
+
+CTRL_ATTR_UNSPEC	= 0
+CTRL_ATTR_FAMILY_ID	= 1
+CTRL_ATTR_FAMILY_NAME	= 2
+CTRL_ATTR_VERSION	= 3
+CTRL_ATTR_HDRSIZE	= 4
+CTRL_ATTR_MAXATTR	= 5
+CTRL_ATTR_OPS		= 6
+
+class GenlHdr(object):
+    def __init__(self, cmd, version = 0):
+        self.cmd = cmd
+        self.version = version
+    def _dump(self):
+        return struct.pack("BBxx", self.cmd, self.version)
+
+def _genl_hdr_parse(data):
+    return GenlHdr(*struct.unpack("BBxx", data))
+
+GENL_ID_CTRL		= NLMSG_MIN_TYPE
+
+class GenlMessage(Message):
+    def __init__(self, family, cmd, attrs=[], flags=0):
+        Message.__init__(self, family, flags=flags, payload=[GenlHdr(cmd)] + attrs)
+
+class GenlController(object):
+    def __init__(self, conn):
+        self.conn = conn
+    def get_family_id(self, family):
+        a = NulStrAttr(CTRL_ATTR_FAMILY_NAME, family)
+        m = GenlMessage(GENL_ID_CTRL, CTRL_CMD_GETFAMILY, flags=NLM_F_REQUEST, attrs=[a])
+        m.send(self.conn)
+        m = self.conn.recv()
+        gh = _genl_hdr_parse(m.payload[:4])
+        attrs = parse_attributes(m.payload[4:])
+        return attrs[CTRL_ATTR_FAMILY_ID].u16()
+
+genl_controller = GenlController(Connection(NETLINK_GENERIC))
diff --git a/hostap/tests/hwsim/nl80211.py b/hostap/tests/hwsim/nl80211.py
new file mode 100644
index 0000000..440c820
--- /dev/null
+++ b/hostap/tests/hwsim/nl80211.py
@@ -0,0 +1,355 @@
+# nl80211 definitions
+# 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.
+
+import binascii
+import struct
+
+nl80211_cmd = {
+    'GET_WIPHY': 1,
+    'SET_WIPHY': 2,
+    'NEW_WIPHY': 3,
+    'DEL_WIPHY': 4,
+    'GET_INTERFACE': 5,
+    'SET_INTERFACE': 6,
+    'NEW_INTERFACE': 7,
+    'DEL_INTERFACE': 8,
+    'GET_KEY': 9,
+    'SET_KEY': 10,
+    'NEW_KEY': 11,
+    'DEL_KEY': 12,
+    'GET_BEACON': 13,
+    'SET_BEACON': 14,
+    'START_AP': 15,
+    'STOP_AP': 16,
+    'GET_STATION': 17,
+    'SET_STATION': 18,
+    'NEW_STATION': 19,
+    'DEL_STATION': 20,
+    'GET_MPATH': 21,
+    'SET_MPATH': 22,
+    'NEW_MPATH': 23,
+    'DEL_MPATH': 24,
+    'SET_BSS': 25,
+    'SET_REG': 26,
+    'REQ_SET_REG': 27,
+    'GET_MESH_CONFIG': 28,
+    'SET_MESH_CONFIG': 29,
+    'SET_MGMT_EXTRA_IE[RESERVED]': 30,
+    'GET_REG': 31,
+    'GET_SCAN': 32,
+    'TRIGGER_SCAN': 33,
+    'NEW_SCAN_RESULTS': 34,
+    'SCAN_ABORTED': 35,
+    'REG_CHANGE': 36,
+    'AUTHENTICATE': 37,
+    'ASSOCIATE': 38,
+    'DEAUTHENTICATE': 39,
+    'DISASSOCIATE': 40,
+    'MICHAEL_MIC_FAILURE': 41,
+    'REG_BEACON_HINT': 42,
+    'JOIN_IBSS': 43,
+    'LEAVE_IBSS': 44,
+    'TESTMODE': 45,
+    'CONNECT': 46,
+    'ROAM': 47,
+    'DISCONNECT': 48,
+    'SET_WIPHY_NETNS': 49,
+    'GET_SURVEY': 50,
+    'NEW_SURVEY_RESULTS': 51,
+    'SET_PMKSA': 52,
+    'DEL_PMKSA': 53,
+    'FLUSH_PMKSA': 54,
+    'REMAIN_ON_CHANNEL': 55,
+    'CANCEL_REMAIN_ON_CHANNEL': 56,
+    'SET_TX_BITRATE_MASK': 57,
+    'REGISTER_FRAME': 58,
+    'FRAME': 59,
+    'FRAME_TX_STATUS': 60,
+    'SET_POWER_SAVE': 61,
+    'GET_POWER_SAVE': 62,
+    'SET_CQM': 63,
+    'NOTIFY_CQM': 64,
+    'SET_CHANNEL': 65,
+    'SET_WDS_PEER': 66,
+    'FRAME_WAIT_CANCEL': 67,
+    'JOIN_MESH': 68,
+    'LEAVE_MESH': 69,
+    'UNPROT_DEAUTHENTICATE': 70,
+    'UNPROT_DISASSOCIATE': 71,
+    'NEW_PEER_CANDIDATE': 72,
+    'GET_WOWLAN': 73,
+    'SET_WOWLAN': 74,
+    'START_SCHED_SCAN': 75,
+    'STOP_SCHED_SCAN': 76,
+    'SCHED_SCAN_RESULTS': 77,
+    'SCHED_SCAN_STOPPED': 78,
+    'SET_REKEY_OFFLOAD': 79,
+    'PMKSA_CANDIDATE': 80,
+    'TDLS_OPER': 81,
+    'TDLS_MGMT': 82,
+    'UNEXPECTED_FRAME': 83,
+    'PROBE_CLIENT': 84,
+    'REGISTER_BEACONS': 85,
+    'UNEXPECTED_4ADDR_FRAME': 86,
+    'SET_NOACK_MAP': 87,
+    'CH_SWITCH_NOTIFY': 88,
+    'START_P2P_DEVICE': 89,
+    'STOP_P2P_DEVICE': 90,
+    'CONN_FAILED': 91,
+    'SET_MCAST_RATE': 92,
+    'SET_MAC_ACL': 93,
+    'RADAR_DETECT': 94,
+    'GET_PROTOCOL_FEATURES': 95,
+    'UPDATE_FT_IES': 96,
+    'FT_EVENT': 97,
+    'CRIT_PROTOCOL_START': 98,
+    'CRIT_PROTOCOL_STOP': 99,
+    'GET_COALESCE': 100,
+    'SET_COALESCE': 101,
+    'CHANNEL_SWITCH': 102,
+    'VENDOR': 103,
+    'SET_QOS_MAP': 104,
+}
+
+nl80211_attr = {
+    'WIPHY': 1,
+    'WIPHY_NAME': 2,
+    'IFINDEX': 3,
+    'IFNAME': 4,
+    'IFTYPE': 5,
+    'MAC': 6,
+    'KEY_DATA': 7,
+    'KEY_IDX': 8,
+    'KEY_CIPHER': 9,
+    'KEY_SEQ': 10,
+    'KEY_DEFAULT': 11,
+    'BEACON_INTERVAL': 12,
+    'DTIM_PERIOD': 13,
+    'BEACON_HEAD': 14,
+    'BEACON_TAIL': 15,
+    'STA_AID': 16,
+    'STA_FLAGS': 17,
+    'STA_LISTEN_INTERVAL': 18,
+    'STA_SUPPORTED_RATES': 19,
+    'STA_VLAN': 20,
+    'STA_INFO': 21,
+    'WIPHY_BANDS': 22,
+    'MNTR_FLAGS': 23,
+    'MESH_ID': 24,
+    'STA_PLINK_ACTION': 25,
+    'MPATH_NEXT_HOP': 26,
+    'MPATH_INFO': 27,
+    'BSS_CTS_PROT': 28,
+    'BSS_SHORT_PREAMBLE': 29,
+    'BSS_SHORT_SLOT_TIME': 30,
+    'HT_CAPABILITY': 31,
+    'SUPPORTED_IFTYPES': 32,
+    'REG_ALPHA2': 33,
+    'REG_RULES': 34,
+    'MESH_CONFIG': 35,
+    'BSS_BASIC_RATES': 36,
+    'WIPHY_TXQ_PARAMS': 37,
+    'WIPHY_FREQ': 38,
+    'WIPHY_CHANNEL_TYPE': 39,
+    'KEY_DEFAULT_MGMT': 40,
+    'MGMT_SUBTYPE': 41,
+    'IE': 42,
+    'MAX_NUM_SCAN_SSIDS': 43,
+    'SCAN_FREQUENCIES': 44,
+    'SCAN_SSIDS': 45,
+    'GENERATION': 46,
+    'BSS': 47,
+    'REG_INITIATOR': 48,
+    'REG_TYPE': 49,
+    'SUPPORTED_COMMANDS': 50,
+    'FRAME': 51,
+    'SSID': 52,
+    'AUTH_TYPE': 53,
+    'REASON_CODE': 54,
+    'KEY_TYPE': 55,
+    'MAX_SCAN_IE_LEN': 56,
+    'CIPHER_SUITES': 57,
+    'FREQ_BEFORE': 58,
+    'FREQ_AFTER': 59,
+    'FREQ_FIXED': 60,
+    'WIPHY_RETRY_SHORT': 61,
+    'WIPHY_RETRY_LONG': 62,
+    'WIPHY_FRAG_THRESHOLD': 63,
+    'WIPHY_RTS_THRESHOLD': 64,
+    'TIMED_OUT': 65,
+    'USE_MFP': 66,
+    'STA_FLAGS2': 67,
+    'CONTROL_PORT': 68,
+    'TESTDATA': 69,
+    'PRIVACY': 70,
+    'DISCONNECTED_BY_AP': 71,
+    'STATUS_CODE': 72,
+    'CIPHER_SUITES_PAIRWISE': 73,
+    'CIPHER_SUITE_GROUP': 74,
+    'WPA_VERSIONS': 75,
+    'AKM_SUITES': 76,
+    'REQ_IE': 77,
+    'RESP_IE': 78,
+    'PREV_BSSID': 79,
+    'KEY': 80,
+    'KEYS': 81,
+    'PID': 82,
+    '4ADDR': 83,
+    'SURVEY_INFO': 84,
+    'PMKID': 85,
+    'MAX_NUM_PMKIDS': 86,
+    'DURATION': 87,
+    'COOKIE': 88,
+    'WIPHY_COVERAGE_CLASS': 89,
+    'TX_RATES': 90,
+    'FRAME_MATCH': 91,
+    'ACK': 92,
+    'PS_STATE': 93,
+    'CQM': 94,
+    'LOCAL_STATE_CHANGE': 95,
+    'AP_ISOLATE': 96,
+    'WIPHY_TX_POWER_SETTING': 97,
+    'WIPHY_TX_POWER_LEVEL': 98,
+    'TX_FRAME_TYPES': 99,
+    'RX_FRAME_TYPES': 100,
+    'FRAME_TYPE': 101,
+    'CONTROL_PORT_ETHERTYPE': 102,
+    'CONTROL_PORT_NO_ENCRYPT': 103,
+    'SUPPORT_IBSS_RSN': 104,
+    'WIPHY_ANTENNA_TX': 105,
+    'WIPHY_ANTENNA_RX': 106,
+    'MCAST_RATE': 107,
+    'OFFCHANNEL_TX_OK': 108,
+    'BSS_HT_OPMODE': 109,
+    'KEY_DEFAULT_TYPES': 110,
+    'MAX_REMAIN_ON_CHANNEL_DURATION': 111,
+    'MESH_SETUP': 112,
+    'WIPHY_ANTENNA_AVAIL_TX': 113,
+    'WIPHY_ANTENNA_AVAIL_RX': 114,
+    'SUPPORT_MESH_AUTH': 115,
+    'STA_PLINK_STATE': 116,
+    'WOWLAN_TRIGGERS': 117,
+    'WOWLAN_TRIGGERS_SUPPORTED': 118,
+    'SCHED_SCAN_INTERVAL': 119,
+    'INTERFACE_COMBINATIONS': 120,
+    'SOFTWARE_IFTYPES': 121,
+    'REKEY_DATA': 122,
+    'MAX_NUM_SCHED_SCAN_SSIDS': 123,
+    'MAX_SCHED_SCAN_IE_LEN': 124,
+    'SCAN_SUPP_RATES': 125,
+    'HIDDEN_SSID': 126,
+    'IE_PROBE_RESP': 127,
+    'IE_ASSOC_RESP': 128,
+    'STA_WME': 129,
+    'SUPPORT_AP_UAPSD': 130,
+    'ROAM_SUPPORT': 131,
+    'SCHED_SCAN_MATCH': 132,
+    'MAX_MATCH_SETS': 133,
+    'PMKSA_CANDIDATE': 134,
+    'TX_NO_CCK_RATE': 135,
+    'TDLS_ACTION': 136,
+    'TDLS_DIALOG_TOKEN': 137,
+    'TDLS_OPERATION': 138,
+    'TDLS_SUPPORT': 139,
+    'TDLS_EXTERNAL_SETUP': 140,
+    'DEVICE_AP_SME': 141,
+    'DONT_WAIT_FOR_ACK': 142,
+    'FEATURE_FLAGS': 143,
+    'PROBE_RESP_OFFLOAD': 144,
+    'PROBE_RESP': 145,
+    'DFS_REGION': 146,
+    'DISABLE_HT': 147,
+    'HT_CAPABILITY_MASK': 148,
+    'NOACK_MAP': 149,
+    'INACTIVITY_TIMEOUT': 150,
+    'RX_SIGNAL_DBM': 151,
+    'BG_SCAN_PERIOD': 152,
+    'WDEV': 153,
+    'USER_REG_HINT_TYPE': 154,
+    'CONN_FAILED_REASON': 155,
+    'SAE_DATA': 156,
+    'VHT_CAPABILITY': 157,
+    'SCAN_FLAGS': 158,
+    'CHANNEL_WIDTH': 159,
+    'CENTER_FREQ1': 160,
+    'CENTER_FREQ2': 161,
+    'P2P_CTWINDOW': 162,
+    'P2P_OPPPS': 163,
+    'LOCAL_MESH_POWER_MODE': 164,
+    'ACL_POLICY': 165,
+    'MAC_ADDRS': 166,
+    'MAC_ACL_MAX': 167,
+    'RADAR_EVENT': 168,
+    'EXT_CAPA': 169,
+    'EXT_CAPA_MASK': 170,
+    'STA_CAPABILITY': 171,
+    'STA_EXT_CAPABILITY': 172,
+    'PROTOCOL_FEATURES': 173,
+    'SPLIT_WIPHY_DUMP': 174,
+    'DISABLE_VHT': 175,
+    'VHT_CAPABILITY_MASK': 176,
+    'MDID': 177,
+    'IE_RIC': 178,
+    'CRIT_PROT_ID': 179,
+    'MAX_CRIT_PROT_DURATION': 180,
+    'PEER_AID': 181,
+    'COALESCE_RULE': 182,
+    'CH_SWITCH_COUNT': 183,
+    'CH_SWITCH_BLOCK_TX': 184,
+    'CSA_IES': 185,
+    'CSA_C_OFF_BEACON': 186,
+    'CSA_C_OFF_PRESP': 187,
+    'RXMGMT_FLAGS': 188,
+    'STA_SUPPORTED_CHANNELS': 189,
+    'STA_SUPPORTED_OPER_CLASSES': 190,
+    'HANDLE_DFS': 191,
+    'SUPPORT_5_MHZ': 192,
+    'SUPPORT_10_MHZ': 193,
+    'OPMODE_NOTIF': 194,
+    'VENDOR_ID': 195,
+    'VENDOR_SUBCMD': 196,
+    'VENDOR_DATA': 197,
+    'VENDOR_EVENTS': 198,
+    'QOS_MAP': 199,
+    'MAC_HINT': 200,
+    'WIPHY_FREQ_HINT': 201,
+    'MAX_AP_ASSOC_STA': 202,
+}
+
+def build_nl80211_attr(id, val):
+    return struct.pack("@HH", 4 + len(val), nl80211_attr[id]) + val
+
+def build_nl80211_attr_u32(id, val):
+    return build_nl80211_attr(id, struct.pack("@I", val))
+
+def build_nl80211_attr_u16(id, val):
+    return build_nl80211_attr(id, struct.pack("@HH", val, 0))
+
+def build_nl80211_attr_u8(id, val):
+    return build_nl80211_attr(id, struct.pack("@4B", val, 0, 0, 0))
+
+def build_nl80211_attr_flag(id):
+    return build_nl80211_attr(id, '')
+
+def build_nl80211_attr_mac(id, val):
+    addr = struct.unpack('6B', binascii.unhexlify(val.replace(':','')))
+    t = addr + (0, 0)
+    aval = struct.pack('<6BBB', *t)
+    return build_nl80211_attr(id, aval)
+
+def parse_nl80211_attrs(msg):
+    attrs = {}
+    while len(msg) >= 4:
+        alen,attr = struct.unpack("@HH", msg[0:4])
+        if alen < 4:
+            raise Exception("Too short nl80211 attribute")
+        alen -= 4;
+        msg = msg[4:]
+        if alen > len(msg):
+            raise Exception("nl80211 attribute underflow")
+        attrs[attr] = msg[0:alen]
+        msg = msg[alen:]
+    return attrs
diff --git a/hostap/tests/hwsim/p2p0.conf b/hostap/tests/hwsim/p2p0.conf
new file mode 100644
index 0000000..9482bdc
--- /dev/null
+++ b/hostap/tests/hwsim/p2p0.conf
@@ -0,0 +1,3 @@
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=admin
+device_name=Device A
+p2p_no_group_iface=1
diff --git a/hostap/tests/hwsim/p2p1.conf b/hostap/tests/hwsim/p2p1.conf
new file mode 100644
index 0000000..3622b15
--- /dev/null
+++ b/hostap/tests/hwsim/p2p1.conf
@@ -0,0 +1,3 @@
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=admin
+device_name=Device B
+p2p_no_group_iface=1
diff --git a/hostap/tests/hwsim/p2p2.conf b/hostap/tests/hwsim/p2p2.conf
new file mode 100644
index 0000000..eda52e1
--- /dev/null
+++ b/hostap/tests/hwsim/p2p2.conf
@@ -0,0 +1,3 @@
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=admin
+device_name=Device C
+p2p_no_group_iface=1
diff --git a/hostap/tests/hwsim/radius_das.py b/hostap/tests/hwsim/radius_das.py
new file mode 100644
index 0000000..300681a
--- /dev/null
+++ b/hostap/tests/hwsim/radius_das.py
@@ -0,0 +1,44 @@
+# RADIUS DAS extensions to pyrad
+# 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.
+
+import hashlib
+import random
+import struct
+import pyrad.packet
+
+class DisconnectPacket(pyrad.packet.Packet):
+    def __init__(self, code=pyrad.packet.DisconnectRequest, id=None,
+                 secret=None, authenticator=None, **attributes):
+        pyrad.packet.Packet.__init__(self, code, id, secret, authenticator,
+                                     **attributes)
+
+    def RequestPacket(self):
+        attr = self._PktEncodeAttributes()
+
+        if self.id is None:
+            self.id = random.randrange(0, 256)
+
+        header = struct.pack('!BBH', self.code, self.id, (20 + len(attr)))
+        self.authenticator = hashlib.md5(header[0:4] + 16 * b'\x00' + attr
+            + self.secret).digest()
+        return header + self.authenticator + attr
+
+class CoAPacket(pyrad.packet.Packet):
+    def __init__(self, code=pyrad.packet.CoARequest, id=None,
+                 secret=None, authenticator=None, **attributes):
+        pyrad.packet.Packet.__init__(self, code, id, secret, authenticator,
+                                     **attributes)
+
+    def RequestPacket(self):
+        attr = self._PktEncodeAttributes()
+
+        if self.id is None:
+            self.id = random.randrange(0, 256)
+
+        header = struct.pack('!BBH', self.code, self.id, (20 + len(attr)))
+        self.authenticator = hashlib.md5(header[0:4] + 16 * b'\x00' + attr
+            + self.secret).digest()
+        return header + self.authenticator + attr
diff --git a/hostap/tests/hwsim/rfkill.py b/hostap/tests/hwsim/rfkill.py
new file mode 100755
index 0000000..cf73f4f
--- /dev/null
+++ b/hostap/tests/hwsim/rfkill.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python
+#
+# rfkill control code
+#
+# Copyright (c) 2015	Intel Corporation
+#
+# Author: Johannes Berg <johannes.berg@intel.com>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import struct
+import fcntl
+import os
+
+(TYPE_ALL,
+ TYPE_WLAN,
+ TYPE_BLUETOOTH,
+ TYPE_UWB,
+ TYPE_WIMAX,
+ TYPE_WWAN,
+ TYPE_GPS,
+ TYPE_FM,
+ TYPE_NFC) = range(9)
+
+(_OP_ADD,
+ _OP_DEL,
+ _OP_CHANGE,
+ _OP_CHANGE_ALL) = range(4)
+
+_type_names = {
+    TYPE_ALL: "all",
+    TYPE_WLAN: "Wireless LAN",
+    TYPE_BLUETOOTH: "Bluetooth",
+    TYPE_UWB: "Ultra-Wideband",
+    TYPE_WIMAX: "WiMAX",
+    TYPE_WWAN: "Wireless WAN",
+    TYPE_GPS: "GPS",
+    TYPE_FM: "FM",
+    TYPE_NFC: "NFC",
+}
+
+# idx, type, op, soft, hard
+_event_struct = '@IBBBB'
+_event_sz = struct.calcsize(_event_struct)
+
+class RFKillException(Exception):
+    pass
+
+class RFKill(object):
+    def __init__(self, idx):
+        self._idx = idx
+        self._type = None
+
+    @property
+    def idx(self):
+        return self._idx
+
+    @property
+    def name(self):
+        return open('/sys/class/rfkill/rfkill%d/name' % self._idx, 'r').read().rstrip()
+
+    @property
+    def type(self):
+        if not self._type:
+            for r, s, h in RFKill.list():
+                if r.idx == self.idx:
+                    self._type = r._type
+                    break
+        return self._type
+
+    @property
+    def type_name(self):
+        return _type_names.get(self._type, "unknown")
+
+    @property
+    def blocked(self):
+        l = RFKill.list()
+        for r, s, h in l:
+            if r.idx == self.idx:
+                return (s, h)
+        raise RFKillException("RFKill instance no longer exists")
+
+    @property
+    def soft_blocked(self):
+        return self.blocked[0]
+        
+    @soft_blocked.setter
+    def soft_blocked(self, block):
+        if block:
+            self.block()
+        else:
+            self.unblock()
+
+    @property
+    def hard_blocked(self):
+        return self.blocked[1]
+
+    def block(self):
+        rfk = open('/dev/rfkill', 'w')
+        s = struct.pack(_event_struct, self.idx, TYPE_ALL, _OP_CHANGE, 1, 0)
+        rfk.write(s)
+        rfk.close()
+
+    def unblock(self):
+        rfk = open('/dev/rfkill', 'w')
+        s = struct.pack(_event_struct, self.idx, TYPE_ALL, _OP_CHANGE, 0, 0)
+        rfk.write(s)
+        rfk.close()
+
+    @classmethod
+    def block_all(cls, t=TYPE_ALL):
+        rfk = open('/dev/rfkill', 'w')
+        print rfk
+        s = struct.pack(_event_struct, 0, t, _OP_CHANGE_ALL, 1, 0)
+        rfk.write(s)
+        rfk.close()
+
+    @classmethod
+    def unblock_all(cls, t=TYPE_ALL):
+        rfk = open('/dev/rfkill', 'w')
+        s = struct.pack(_event_struct, 0, t, _OP_CHANGE_ALL, 0, 0)
+        rfk.write(s)
+        rfk.close()
+
+    @classmethod
+    def list(cls):
+        res = []
+        rfk = open('/dev/rfkill', 'r')
+        fd = rfk.fileno()
+        flgs = fcntl.fcntl(fd, fcntl.F_GETFL)
+        fcntl.fcntl(fd, fcntl.F_SETFL, flgs | os.O_NONBLOCK)
+        while True:
+            try:
+                d = rfk.read(_event_sz)
+                _idx, _t, _op, _s, _h = struct.unpack(_event_struct, d)
+                if _op != _OP_ADD:
+                    continue
+                r = RFKill(_idx)
+                r._type = _t
+                res.append((r, _s, _h))
+            except IOError:
+                break
+        return res
+
+if __name__ == "__main__":
+    for r, s, h in RFKill.list():
+        print "%d: %s: %s" % (r.idx, r.name, r.type_name)
+        print "\tSoft blocked: %s" % ("yes" if s else "no")
+        print "\tHard blocked: %s" % ("yes" if h else "no")
diff --git a/hostap/tests/hwsim/run-all.sh b/hostap/tests/hwsim/run-all.sh
new file mode 100755
index 0000000..5cf56a9
--- /dev/null
+++ b/hostap/tests/hwsim/run-all.sh
@@ -0,0 +1,142 @@
+#!/bin/sh
+
+errors=0
+umask 0002
+
+DATE="$(date +%s)"
+unset LOGBASEDIR
+if [ -z "$LOGDIR" ]; then
+	LOGBASEDIR=logs
+	LOGDIR=$LOGBASEDIR/$DATE
+	mkdir -p $LOGDIR
+fi
+export LOGDIR
+
+if [ -z "$DBFILE" ]; then
+    DB=""
+else
+    DB="-S $DBFILE --commit $(git rev-parse HEAD)"
+    if [ -n "$BUILD" ]; then
+	DB="$DB -b $BUILD"
+    fi
+    if [ "$PREFILL_DB" = "y" ] ; then
+        DB="$DB --prefill-tests"
+    fi
+fi
+
+usage()
+{
+	echo "$0 [-v | --valgrind | valgrind] [-t | --trace | trace]"
+	echo "\t[-n <num> | --channels <num>] [-B | --build]"
+	echo "\t[-c | --codecov ] [run-tests.py parameters]"
+	exit 1
+}
+
+unset VALGRIND
+unset TRACE
+unset TRACE_ARGS
+unset RUN_TEST_ARGS
+unset BUILD
+unset BUILD_ARGS
+unset CODECOV
+while [ "$1" != "" ]; do
+	case $1 in
+		-v | --valgrind | valgrind)
+			shift
+			echo "$0: using valgrind"
+			VALGRIND=valgrind
+			;;
+		-t | --trace | trace)
+			shift
+			echo "$0: using Trace"
+			TRACE=trace
+			;;
+		-n | --channels)
+			shift
+			NUM_CH=$1
+			shift
+			echo "$0: using channels=$NUM_CH"
+			;;
+		-B | --build)
+			shift
+			echo "$0: build before running tests"
+			BUILD=build
+			;;
+		-c | --codecov)
+			shift
+			echo "$0: using code coverage"
+			CODECOV=lcov
+			BUILD_ARGS=-c
+			;;
+		-h | --help)
+			usage
+			;;
+		*)
+			RUN_TEST_ARGS="$RUN_TEST_ARGS$1 "
+			shift
+			;;
+	esac
+done
+
+if [ ! -z "$RUN_TEST_ARGS" ]; then
+	echo "$0: passing the following args to run-tests.py: $RUN_TEST_ARGS"
+fi
+
+unset SUFFIX
+if [ ! -z "$BUILD" ]; then
+	SUFFIX=-build
+fi
+
+if [ ! -z "$VALGRIND" ]; then
+	SUFFIX=$SUFFIX-valgrind
+fi
+
+if [ ! -z "$TRACE" ]; then
+	SUFFIX=$SUFFIX-trace
+	TRACE_ARGS="-T"
+fi
+
+if [ ! -z "$CODECOV" ]; then
+	SUFFIX=$SUFFIX-codecov
+fi
+
+if [ ! -z "$BUILD" ]; then
+    echo "Building with args=$BUILD_ARGS"
+    if ! ./build.sh $BUILD_ARGS; then
+	    echo "Failed building components"
+	    exit 1
+    fi
+fi
+
+if ! ./start.sh $VALGRIND $TRACE channels=$NUM_CH; then
+	if ! [ -z "$LOGBASEDIR" ] ; then
+		echo "Could not start test environment" > $LOGDIR/run
+	fi
+	exit 1
+fi
+
+sudo ./run-tests.py -D --logdir "$LOGDIR" $TRACE_ARGS -q $DB $RUN_TEST_ARGS || errors=1
+
+./stop.sh
+
+if [ ! -z "$VALGRIND" ] ; then
+    failures=`grep "ERROR SUMMARY" $LOGDIR/valgrind-* | grep -v " 0 errors" | wc -l`
+    if [ $failures -gt 0 ]; then
+	echo "Mark as failed due to valgrind errors"
+	errors=1
+    fi
+fi
+
+if [ ! -z "$CODECOV" ] ; then
+	lcov -q --capture --directory ../../wpa_supplicant --output-file $LOGDIR/wpas_lcov.info
+	genhtml -q $LOGDIR/wpas_lcov.info --output-directory $LOGDIR/wpas_lcov
+	lcov -q --capture --directory ../../hostapd --output-file $LOGDIR/hostapd_lcov.info
+	genhtml -q $LOGDIR/hostapd_lcov.info --output-directory $LOGDIR/hostapd_lcov
+fi
+
+if [ $errors -gt 0 ]; then
+    tar czf /tmp/hwsim-tests-$DATE-FAILED$SUFFIX.tar.gz $LOGDIR/
+    exit 1
+fi
+
+echo "ALL-PASSED"
diff --git a/hostap/tests/hwsim/run-tests.py b/hostap/tests/hwsim/run-tests.py
new file mode 100755
index 0000000..13e360b
--- /dev/null
+++ b/hostap/tests/hwsim/run-tests.py
@@ -0,0 +1,563 @@
+#!/usr/bin/env python2
+#
+# Test case executor
+# Copyright (c) 2013-2015, 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 re
+import sys
+import time
+from datetime import datetime
+import argparse
+import subprocess
+import termios
+
+import logging
+logger = logging.getLogger()
+
+try:
+    import sqlite3
+    sqlite3_imported = True
+except ImportError:
+    sqlite3_imported = False
+
+scriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
+sys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy'))
+
+from wpasupplicant import WpaSupplicant
+from hostapd import HostapdGlobal
+from check_kernel import check_kernel
+from wlantest import Wlantest
+from utils import HwsimSkip
+
+def set_term_echo(fd, enabled):
+    [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] = termios.tcgetattr(fd)
+    if enabled:
+        lflag |= termios.ECHO
+    else:
+        lflag &= ~termios.ECHO
+    termios.tcsetattr(fd, termios.TCSANOW,
+                      [iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
+
+def reset_devs(dev, apdev):
+    ok = True
+    for d in dev:
+        try:
+            d.reset()
+        except Exception, e:
+            logger.info("Failed to reset device " + d.ifname)
+            print str(e)
+            ok = False
+
+    wpas = None
+    try:
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        ifaces = wpas.global_request("INTERFACES").splitlines()
+        for iface in ifaces:
+            if iface.startswith("wlan"):
+                wpas.interface_remove(iface)
+    except Exception, e:
+        pass
+    if wpas:
+        wpas.close_ctrl()
+
+    try:
+        hapd = HostapdGlobal()
+        hapd.flush()
+        hapd.remove('wlan3-3')
+        hapd.remove('wlan3-2')
+        for ap in apdev:
+            hapd.remove(ap['ifname'])
+    except Exception, e:
+        logger.info("Failed to remove hostapd interface")
+        print str(e)
+        ok = False
+    return ok
+
+def add_log_file(conn, test, run, type, path):
+    if not os.path.exists(path):
+        return
+    contents = None
+    with open(path, 'r') as f:
+        contents = f.read()
+    if contents is None:
+        return
+    sql = "INSERT INTO logs(test,run,type,contents) VALUES(?, ?, ?, ?)"
+    params = (test, run, type, sqlite3.Binary(contents))
+    try:
+        conn.execute(sql, params)
+        conn.commit()
+    except Exception, e:
+        print "sqlite: " + str(e)
+        print "sql: %r" % (params, )
+
+def report(conn, prefill, build, commit, run, test, result, duration, logdir,
+           sql_commit=True):
+    if conn:
+        if not build:
+            build = ''
+        if not commit:
+            commit = ''
+        if prefill:
+            conn.execute('DELETE FROM results WHERE test=? AND run=? AND result=?', (test, run, 'NOTRUN'))
+        sql = "INSERT INTO results(test,result,run,time,duration,build,commitid) VALUES(?, ?, ?, ?, ?, ?, ?)"
+        params = (test, result, run, time.time(), duration, build, commit)
+        try:
+            conn.execute(sql, params)
+            if sql_commit:
+                conn.commit()
+        except Exception, e:
+            print "sqlite: " + str(e)
+            print "sql: %r" % (params, )
+
+        if result == "FAIL":
+            for log in [ "log", "log0", "log1", "log2", "log3", "log5",
+                         "hostapd", "dmesg", "hwsim0", "hwsim0.pcapng" ]:
+                add_log_file(conn, test, run, log,
+                             logdir + "/" + test + "." + log)
+
+class DataCollector(object):
+    def __init__(self, logdir, testname, tracing, dmesg):
+        self._logdir = logdir
+        self._testname = testname
+        self._tracing = tracing
+        self._dmesg = dmesg
+    def __enter__(self):
+        if self._tracing:
+            output = os.path.abspath(os.path.join(self._logdir, '%s.dat' % (self._testname, )))
+            self._trace_cmd = subprocess.Popen(['trace-cmd', 'record', '-o', output, '-e', 'mac80211', '-e', 'cfg80211', '-e', 'printk', 'sh', '-c', 'echo STARTED ; read l'],
+                                               stdin=subprocess.PIPE,
+                                               stdout=subprocess.PIPE,
+                                               stderr=open('/dev/null', 'w'),
+                                               cwd=self._logdir)
+            l = self._trace_cmd.stdout.read(7)
+            while self._trace_cmd.poll() is None and not 'STARTED' in l:
+                l += self._trace_cmd.stdout.read(1)
+            res = self._trace_cmd.returncode
+            if res:
+                print "Failed calling trace-cmd: returned exit status %d" % res
+                sys.exit(1)
+    def __exit__(self, type, value, traceback):
+        if self._tracing:
+            self._trace_cmd.stdin.write('DONE\n')
+            self._trace_cmd.wait()
+        if self._dmesg:
+            output = os.path.join(self._logdir, '%s.dmesg' % (self._testname, ))
+            num = 0
+            while os.path.exists(output):
+                output = os.path.join(self._logdir, '%s.dmesg-%d' % (self._testname, num))
+                num += 1
+            subprocess.call(['dmesg', '-c'], stdout=open(output, 'w'))
+
+def rename_log(logdir, basename, testname, dev):
+    try:
+        import getpass
+        srcname = os.path.join(logdir, basename)
+        dstname = os.path.join(logdir, testname + '.' + basename)
+        num = 0
+        while os.path.exists(dstname):
+            dstname = os.path.join(logdir,
+                                   testname + '.' + basename + '-' + str(num))
+            num = num + 1
+        os.rename(srcname, dstname)
+        if dev:
+            dev.relog()
+            subprocess.call(['chown', '-f', getpass.getuser(), srcname])
+    except Exception, e:
+        logger.info("Failed to rename log files")
+        logger.info(e)
+
+def main():
+    tests = []
+    test_modules = []
+    files = os.listdir(scriptsdir)
+    for t in files:
+        m = re.match(r'(test_.*)\.py$', t)
+        if m:
+            logger.debug("Import test cases from " + t)
+            mod = __import__(m.group(1))
+            test_modules.append(mod.__name__.replace('test_', '', 1))
+            for key,val in mod.__dict__.iteritems():
+                if key.startswith("test_"):
+                    tests.append(val)
+    test_names = list(set([t.__name__.replace('test_', '', 1) for t in tests]))
+
+    run = None
+
+    parser = argparse.ArgumentParser(description='hwsim test runner')
+    parser.add_argument('--logdir', metavar='<directory>',
+                        help='log output directory for all other options, ' +
+                             'must be given if other log options are used')
+    group = parser.add_mutually_exclusive_group()
+    group.add_argument('-d', const=logging.DEBUG, action='store_const',
+                       dest='loglevel', default=logging.INFO,
+                       help="verbose debug output")
+    group.add_argument('-q', const=logging.WARNING, action='store_const',
+                       dest='loglevel', help="be quiet")
+
+    parser.add_argument('-S', metavar='<sqlite3 db>', dest='database',
+                        help='database to write results to')
+    parser.add_argument('--prefill-tests', action='store_true', dest='prefill',
+                        help='prefill test database with NOTRUN before all tests')
+    parser.add_argument('--commit', metavar='<commit id>',
+                        help='commit ID, only for database')
+    parser.add_argument('-b', metavar='<build>', dest='build', help='build ID')
+    parser.add_argument('-L', action='store_true', dest='update_tests_db',
+                        help='List tests (and update descriptions in DB)')
+    parser.add_argument('-T', action='store_true', dest='tracing',
+                        help='collect tracing per test case (in log directory)')
+    parser.add_argument('-D', action='store_true', dest='dmesg',
+                        help='collect dmesg per test case (in log directory)')
+    parser.add_argument('--shuffle-tests', action='store_true',
+                        dest='shuffle_tests',
+                        help='Shuffle test cases to randomize order')
+    parser.add_argument('--split', help='split tests for parallel execution (<server number>/<total servers>)')
+    parser.add_argument('--no-reset', action='store_true', dest='no_reset',
+                        help='Do not reset devices at the end of the test')
+    parser.add_argument('--long', action='store_true',
+                        help='Include test cases that take long time')
+    parser.add_argument('-f', dest='testmodules', metavar='<test module>',
+                        help='execute only tests from these test modules',
+                        type=str, choices=[[]] + test_modules, nargs='+')
+    parser.add_argument('-l', metavar='<modules file>', dest='mfile',
+                        help='test modules file name')
+    parser.add_argument('-i', action='store_true', dest='stdin_ctrl',
+                        help='stdin-controlled test case execution')
+    parser.add_argument('tests', metavar='<test>', nargs='*', type=str,
+                        help='tests to run (only valid without -f)',
+                        choices=[[]] + test_names)
+
+    args = parser.parse_args()
+
+    if (args.tests and args.testmodules) or (args.tests and args.mfile) or (args.testmodules and args.mfile):
+        print 'Invalid arguments - only one of (test, test modules, modules file) can be given.'
+        sys.exit(2)
+
+    if args.database:
+        if not sqlite3_imported:
+            print "No sqlite3 module found"
+            sys.exit(2)
+        conn = sqlite3.connect(args.database)
+        conn.execute('CREATE TABLE IF NOT EXISTS results (test,result,run,time,duration,build,commitid)')
+        conn.execute('CREATE TABLE IF NOT EXISTS tests (test,description)')
+        conn.execute('CREATE TABLE IF NOT EXISTS logs (test,run,type,contents)')
+    else:
+        conn = None
+
+    if conn:
+        run = int(time.time())
+
+    # read the modules from the modules file
+    if args.mfile:
+	args.testmodules = []
+	with open(args.mfile) as f:
+	    for line in f.readlines():
+		line = line.strip()
+		if not line or line.startswith('#'):
+		    continue
+	        args.testmodules.append(line)
+
+    tests_to_run = []
+    if args.tests:
+        for selected in args.tests:
+            for t in tests:
+                name = t.__name__.replace('test_', '', 1)
+                if name == selected:
+                    tests_to_run.append(t)
+    else:
+        for t in tests:
+            name = t.__name__.replace('test_', '', 1)
+            if args.testmodules:
+                if not t.__module__.replace('test_', '', 1) in args.testmodules:
+                    continue
+            tests_to_run.append(t)
+
+    if args.update_tests_db:
+        for t in tests_to_run:
+            name = t.__name__.replace('test_', '', 1)
+            if t.__doc__ is None:
+                print name + " - MISSING DESCRIPTION"
+            else:
+                print name + " - " + t.__doc__
+            if conn:
+                sql = 'INSERT OR REPLACE INTO tests(test,description) VALUES (?, ?)'
+                params = (name, t.__doc__)
+                try:
+                    conn.execute(sql, params)
+                except Exception, e:
+                    print "sqlite: " + str(e)
+                    print "sql: %r" % (params,)
+        if conn:
+            conn.commit()
+            conn.close()
+        sys.exit(0)
+
+    if not args.logdir:
+        if os.path.exists('logs/current'):
+            args.logdir = 'logs/current'
+        else:
+            args.logdir = 'logs'
+
+    # Write debug level log to a file and configurable verbosity to stdout
+    logger.setLevel(logging.DEBUG)
+
+    stdout_handler = logging.StreamHandler()
+    stdout_handler.setLevel(args.loglevel)
+    logger.addHandler(stdout_handler)
+
+    file_name = os.path.join(args.logdir, 'run-tests.log')
+    log_handler = logging.FileHandler(file_name)
+    log_handler.setLevel(logging.DEBUG)
+    fmt = "%(asctime)s %(levelname)s %(message)s"
+    log_formatter = logging.Formatter(fmt)
+    log_handler.setFormatter(log_formatter)
+    logger.addHandler(log_handler)
+
+    dev0 = WpaSupplicant('wlan0', '/tmp/wpas-wlan0')
+    dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+    dev2 = WpaSupplicant('wlan2', '/tmp/wpas-wlan2')
+    dev = [ dev0, dev1, dev2 ]
+    apdev = [ ]
+    apdev.append({"ifname": 'wlan3', "bssid": "02:00:00:00:03:00"})
+    apdev.append({"ifname": 'wlan4', "bssid": "02:00:00:00:04:00"})
+
+    for d in dev:
+        if not d.ping():
+            logger.info(d.ifname + ": No response from wpa_supplicant")
+            return
+        logger.info("DEV: " + d.ifname + ": " + d.p2p_dev_addr())
+    for ap in apdev:
+        logger.info("APDEV: " + ap['ifname'])
+
+    passed = []
+    skipped = []
+    failed = []
+
+    # make sure nothing is left over from previous runs
+    # (if there were any other manual runs or we crashed)
+    if not reset_devs(dev, apdev):
+        if conn:
+            conn.close()
+            conn = None
+        sys.exit(1)
+
+    if args.dmesg:
+        subprocess.call(['dmesg', '-c'], stdout=open('/dev/null', 'w'))
+
+    if conn and args.prefill:
+        for t in tests_to_run:
+            name = t.__name__.replace('test_', '', 1)
+            report(conn, False, args.build, args.commit, run, name, 'NOTRUN', 0,
+                   args.logdir, sql_commit=False)
+        conn.commit()
+
+    if args.split:
+        vals = args.split.split('/')
+        split_server = int(vals[0])
+        split_total = int(vals[1])
+        logger.info("Parallel execution - %d/%d" % (split_server, split_total))
+        split_server -= 1
+        tests_to_run.sort(key=lambda t: t.__name__)
+        tests_to_run = [x for i,x in enumerate(tests_to_run) if i % split_total == split_server]
+
+    if args.shuffle_tests:
+        from random import shuffle
+        shuffle(tests_to_run)
+
+    count = 0
+    if args.stdin_ctrl:
+        print "READY"
+        sys.stdout.flush()
+        num_tests = 0
+    else:
+        num_tests = len(tests_to_run)
+    if args.stdin_ctrl:
+        set_term_echo(sys.stdin.fileno(), False)
+    while True:
+        if args.stdin_ctrl:
+            test = sys.stdin.readline()
+            if not test:
+                break
+            test = test.splitlines()[0]
+            if test == '':
+                break
+            t = None
+            for tt in tests:
+                name = tt.__name__.replace('test_', '', 1)
+                if name == test:
+                    t = tt
+                    break
+            if not t:
+                print "NOT-FOUND"
+                sys.stdout.flush()
+                continue
+        else:
+            if len(tests_to_run) == 0:
+                break
+            t = tests_to_run.pop(0)
+
+        name = t.__name__.replace('test_', '', 1)
+        if log_handler:
+            log_handler.stream.close()
+            logger.removeHandler(log_handler)
+            file_name = os.path.join(args.logdir, name + '.log')
+            log_handler = logging.FileHandler(file_name)
+            log_handler.setLevel(logging.DEBUG)
+            log_handler.setFormatter(log_formatter)
+            logger.addHandler(log_handler)
+
+        reset_ok = True
+        with DataCollector(args.logdir, name, args.tracing, args.dmesg):
+            count = count + 1
+            msg = "START {} {}/{}".format(name, count, num_tests)
+            logger.info(msg)
+            if args.loglevel == logging.WARNING:
+                print msg
+                sys.stdout.flush()
+            if t.__doc__:
+                logger.info("Test: " + t.__doc__)
+            start = datetime.now()
+            for d in dev:
+                try:
+                    d.dump_monitor()
+                    if not d.ping():
+                        raise Exception("PING failed for {}".format(d.ifname))
+                    if not d.global_ping():
+                        raise Exception("Global PING failed for {}".format(d.ifname))
+                    d.request("NOTE TEST-START " + name)
+                except Exception, e:
+                    logger.info("Failed to issue TEST-START before " + name + " for " + d.ifname)
+                    logger.info(e)
+                    print "FAIL " + name + " - could not start test"
+                    if conn:
+                        conn.close()
+                        conn = None
+                    if args.stdin_ctrl:
+                        set_term_echo(sys.stdin.fileno(), True)
+                    sys.exit(1)
+            try:
+                if t.func_code.co_argcount > 2:
+                    params = {}
+                    params['logdir'] = args.logdir
+                    params['long'] = args.long
+                    t(dev, apdev, params)
+                elif t.func_code.co_argcount > 1:
+                    t(dev, apdev)
+                else:
+                    t(dev)
+                result = "PASS"
+            except HwsimSkip, e:
+                logger.info("Skip test case: %s" % e)
+                result = "SKIP"
+            except Exception, e:
+                logger.info(e)
+                if args.loglevel == logging.WARNING:
+                    print "Exception: " + str(e)
+                result = "FAIL"
+            for d in dev:
+                try:
+                    d.dump_monitor()
+                    d.request("NOTE TEST-STOP " + name)
+                except Exception, e:
+                    logger.info("Failed to issue TEST-STOP after {} for {}".format(name, d.ifname))
+                    logger.info(e)
+                    result = "FAIL"
+            wpas = None
+            try:
+                wpas = WpaSupplicant(global_iface="/tmp/wpas-wlan5")
+                rename_log(args.logdir, 'log5', name, wpas)
+                if not args.no_reset:
+                    wpas.remove_ifname()
+            except Exception, e:
+                pass
+            if wpas:
+                wpas.close_ctrl()
+            if args.no_reset:
+                print "Leaving devices in current state"
+            else:
+                reset_ok = reset_devs(dev, apdev)
+
+            for i in range(0, 3):
+                rename_log(args.logdir, 'log' + str(i), name, dev[i])
+            try:
+                hapd = HostapdGlobal()
+            except Exception, e:
+                print "Failed to connect to hostapd interface"
+                print str(e)
+                reset_ok = False
+                result = "FAIL"
+                hapd = None
+            rename_log(args.logdir, 'hostapd', name, hapd)
+            if hapd:
+                del hapd
+                hapd = None
+
+            wt = Wlantest()
+            rename_log(args.logdir, 'hwsim0.pcapng', name, wt)
+            rename_log(args.logdir, 'hwsim0', name, wt)
+            if os.path.exists(os.path.join(args.logdir, 'fst-wpa_supplicant')):
+                rename_log(args.logdir, 'fst-wpa_supplicant', name, None)
+            if os.path.exists(os.path.join(args.logdir, 'fst-hostapd')):
+                rename_log(args.logdir, 'fst-hostapd', name, None)
+
+        end = datetime.now()
+        diff = end - start
+
+        if result == 'PASS' and args.dmesg:
+            if not check_kernel(os.path.join(args.logdir, name + '.dmesg')):
+                logger.info("Kernel issue found in dmesg - mark test failed")
+                result = 'FAIL'
+
+        if result == 'PASS':
+            passed.append(name)
+        elif result == 'SKIP':
+            skipped.append(name)
+        else:
+            failed.append(name)
+
+        report(conn, args.prefill, args.build, args.commit, run, name, result,
+               diff.total_seconds(), args.logdir)
+        result = "{} {} {} {}".format(result, name, diff.total_seconds(), end)
+        logger.info(result)
+        if args.loglevel == logging.WARNING:
+            print result
+            sys.stdout.flush()
+
+        if not reset_ok:
+            print "Terminating early due to device reset failure"
+            break
+    if args.stdin_ctrl:
+        set_term_echo(sys.stdin.fileno(), True)
+
+    if log_handler:
+        log_handler.stream.close()
+        logger.removeHandler(log_handler)
+        file_name = os.path.join(args.logdir, 'run-tests.log')
+        log_handler = logging.FileHandler(file_name)
+        log_handler.setLevel(logging.DEBUG)
+        log_handler.setFormatter(log_formatter)
+        logger.addHandler(log_handler)
+
+    if conn:
+        conn.close()
+
+    if len(failed):
+        logger.info("passed {} test case(s)".format(len(passed)))
+        logger.info("skipped {} test case(s)".format(len(skipped)))
+        logger.info("failed tests: " + ' '.join(failed))
+        if args.loglevel == logging.WARNING:
+            print "failed tests: " + ' '.join(failed)
+        sys.exit(1)
+    logger.info("passed all {} test case(s)".format(len(passed)))
+    if len(skipped):
+        logger.info("skipped {} test case(s)".format(len(skipped)))
+    if args.loglevel == logging.WARNING:
+        print "passed all {} test case(s)".format(len(passed))
+        if len(skipped):
+            print "skipped {} test case(s)".format(len(skipped))
+
+if __name__ == "__main__":
+    main()
diff --git a/hostap/tests/hwsim/start.sh b/hostap/tests/hwsim/start.sh
new file mode 100755
index 0000000..f71f64f
--- /dev/null
+++ b/hostap/tests/hwsim/start.sh
@@ -0,0 +1,205 @@
+#!/bin/sh
+
+DIR="$( cd "$( dirname "$0" )" && pwd )"
+WPAS=$DIR/../../wpa_supplicant/wpa_supplicant
+WPACLI=$DIR/../../wpa_supplicant/wpa_cli
+HAPD=$DIR/../../hostapd/hostapd
+HAPD_AS=$DIR/../../hostapd/hostapd
+HAPDCLI=$DIR/../../hostapd/hostapd_cli
+WLANTEST=$DIR/../../wlantest/wlantest
+HLR_AUC_GW=$DIR/../../hostapd/hlr_auc_gw
+DATE="$(date +%s)"
+
+if [ -z "$LOGDIR" ] ; then
+    LOGDIR="$DIR/logs/$DATE"
+    mkdir -p $LOGDIR
+else
+    if [ -e $LOGDIR/alt-wpa_supplicant/wpa_supplicant/wpa_supplicant ]; then
+	WPAS=$LOGDIR/alt-wpa_supplicant/wpa_supplicant/wpa_supplicant
+	WPACLI=$LOGDIR/alt-wpa_supplicant/wpa_supplicant/wpa_cli
+	# extra code coverage
+	$WPAS > /dev/null 2>&1
+	$WPAS -efoo -Ifoo -mfoo -ofoo -Ofoo -pfoo -Pfoo -h > /dev/null 2>&1
+	$WPAS -bfoo -B -Cfoo -q -W -N -L > /dev/null 2>&1
+	$WPAS -T -v > /dev/null 2>&1
+	$WPAS -u -z > /dev/null 2>&1
+    fi
+    if [ -e $LOGDIR/alt-hostapd/hostapd/hostapd ]; then
+	HAPD=$LOGDIR/alt-hostapd/hostapd/hostapd
+	HAPDCLI=$LOGDIR/alt-hostapd/hostapd/hostapd_cli
+	# extra code coverage
+	$HAPD > /dev/null 2>&1
+	$HAPD -v > /dev/null 2>&1
+	$HAPD -B -efoo -Pfoo -T -bfoo -h > /dev/null 2>&1
+	$HAPD -ufoo > /dev/null 2>&1
+	$HAPD -u00:11:22:33:44:55 > /dev/null 2>&1
+	$HAPD -gfoo > /dev/null 2>&1
+	$HAPD -Gfoo-not-exists > /dev/null 2>&1
+	$HAPD -z > /dev/null 2>&1
+    fi
+    if [ -e $LOGDIR/alt-hostapd-as/hostapd/hostapd ]; then
+	HAPD_AS=$LOGDIR/alt-hostapd-as/hostapd/hostapd
+    fi
+    if [ -e $LOGDIR/alt-hlr_auc_gw/hostapd/hlr_auc_gw ]; then
+	HLR_AUC_GW=$LOGDIR/alt-hlr_auc_gw/hostapd/hlr_auc_gw
+	# extra code coverage
+	$HLR_AUC_GW > /dev/null 2>&1
+	$HLR_AUC_GW -Dfoo -i7 -sfoo -h > /dev/null 2>&1
+	$HLR_AUC_GW -i100 > /dev/null 2>&1
+	$HLR_AUC_GW -z > /dev/null 2>&1
+    fi
+fi
+
+if test -w "$DIR/logs" ; then
+    rm -rf $DIR/logs/current
+    ln -sf $DATE $DIR/logs/current
+fi
+
+if groups | tr ' ' "\n" | grep -q ^admin$; then
+    GROUP=admin
+else
+    GROUP=adm
+fi
+
+for i in 0 1 2; do
+    sed "s/ GROUP=.*$/ GROUP=$GROUP/" "$DIR/p2p$i.conf" > "$LOGDIR/p2p$i.conf"
+done
+
+sed "s/group=admin/group=$GROUP/;s%LOGDIR%$LOGDIR%g" "$DIR/auth_serv/as.conf" > "$LOGDIR/as.conf"
+sed "s/group=admin/group=$GROUP/;s%LOGDIR%$LOGDIR%g" "$DIR/auth_serv/as2.conf" > "$LOGDIR/as2.conf"
+
+if [ "$1" = "valgrind" ]; then
+    VALGRIND=y
+    VALGRIND_WPAS="valgrind --log-file=$LOGDIR/valgrind-wlan%d"
+    VALGRIND_HAPD="valgrind --log-file=$LOGDIR/valgrind-hostapd"
+    chmod -f a+rx $WPAS
+    chmod -f a+rx $HAPD
+    chmod -f a+rx $HAPD_AS
+    HAPD_AS="valgrind --log-file=$LOGDIR/valgrind-auth-serv $HAPD_AS"
+    shift
+else
+    unset VALGRIND
+    VALGRIND_WPAS=
+    VALGRIND_HAPD=
+fi
+
+if [ "$1" = "trace" ]; then
+    TRACE="T"
+    shift
+else
+    TRACE=""
+fi
+
+$DIR/stop.sh
+
+TMP=$1
+if [ x${TMP%=[0-9]*} = "xchannels" ]; then
+	NUM_CH=${TMP#channels=}
+	shift
+else
+	NUM_CH=1
+fi
+
+test -f /proc/modules && sudo modprobe mac80211_hwsim radios=7 channels=$NUM_CH support_p2p_device=0
+
+sudo ifconfig hwsim0 up
+sudo $WLANTEST -i hwsim0 -n $LOGDIR/hwsim0.pcapng -c -dt -L $LOGDIR/hwsim0 &
+for i in 0 1 2; do
+    DBUSARG=""
+    if [ $i = "0" -a -r /var/run/dbus/pid -a -r /var/run/dbus/hwsim-test ]; then
+	if $WPAS | grep -q -- -u; then
+	    DBUSARG="-u"
+	fi
+    fi
+    sudo $(printf -- "$VALGRIND_WPAS" $i) $WPAS -g /tmp/wpas-wlan$i -G$GROUP -Dnl80211 -iwlan$i -c $LOGDIR/p2p$i.conf \
+         -ddKt$TRACE -f $LOGDIR/log$i $DBUSARG &
+done
+sudo $(printf -- "$VALGRIND_WPAS" 5) $WPAS -g /tmp/wpas-wlan5 -G$GROUP \
+    -ddKt$TRACE -f $LOGDIR/log5 &
+sudo $VALGRIND_HAPD $HAPD -ddKt$TRACE -g /var/run/hostapd-global -G $GROUP -f $LOGDIR/hostapd &
+
+if [ -x $HLR_AUC_GW ]; then
+    cp $DIR/auth_serv/hlr_auc_gw.milenage_db $LOGDIR/hlr_auc_gw.milenage_db
+    sudo $HLR_AUC_GW -u -m $LOGDIR/hlr_auc_gw.milenage_db -g $DIR/auth_serv/hlr_auc_gw.gsm > $LOGDIR/hlr_auc_gw &
+fi
+
+openssl ocsp -index $DIR/auth_serv/index.txt \
+    -rsigner $DIR/auth_serv/ocsp-responder.pem \
+    -rkey $DIR/auth_serv/ocsp-responder.key \
+    -CA $DIR/auth_serv/ca.pem \
+    -issuer $DIR/auth_serv/ca.pem \
+    -verify_other $DIR/auth_serv/ca.pem -trust_other \
+    -ndays 7 \
+    -reqin $DIR/auth_serv/ocsp-req.der \
+    -respout $LOGDIR/ocsp-server-cache.der > $LOGDIR/ocsp.log 2>&1
+if [ ! -r $LOGDIR/ocsp-server-cache.der ]; then
+    cp $DIR/auth_serv/ocsp-server-cache.der $LOGDIR/ocsp-server-cache.der
+fi
+
+for i in unknown revoked; do
+    openssl ocsp -index $DIR/auth_serv/index-$i.txt \
+	-rsigner $DIR/auth_serv/ocsp-responder.pem \
+	-rkey $DIR/auth_serv/ocsp-responder.key \
+	-CA $DIR/auth_serv/ca.pem \
+	-issuer $DIR/auth_serv/ca.pem \
+	-verify_other $DIR/auth_serv/ca.pem -trust_other \
+	-ndays 7 \
+	-reqin $DIR/auth_serv/ocsp-req.der \
+	-respout $LOGDIR/ocsp-server-cache-$i.der >> $LOGDIR/ocsp.log 2>&1
+done
+touch $LOGDIR/hostapd.db
+sudo $HAPD_AS -ddKt $LOGDIR/as.conf $LOGDIR/as2.conf > $LOGDIR/auth_serv &
+
+# wait for programs to be fully initialized
+for i in 0 1 2 3 4 5 6 7 8 9; do
+    if [ -e /tmp/wpas-wlan0 ]; then
+	break
+    fi
+    sleep 0.05
+done
+for i in 0 1 2; do
+    for j in `seq 1 10`; do
+	if $WPACLI -g /tmp/wpas-wlan$i ping | grep -q PONG; then
+	    break
+	fi
+	if [ $j = "10" ]; then
+	    echo "Could not connect to /tmp/wpas-wlan$i"
+	    exit 1
+	fi
+	sleep 1
+    done
+done
+
+for j in `seq 1 10`; do
+    if $WPACLI -g /var/run/hostapd-global ping | grep -q PONG; then
+	break
+    fi
+    if [ $j = "10" ]; then
+	echo "Could not connect to /var/run/hostapd-global"
+	exit 1
+    fi
+    sleep 1
+done
+
+for j in `seq 1 10`; do
+    if $HAPDCLI -i as ping | grep -q PONG; then
+	break
+    fi
+    if [ $j = "10" ]; then
+	echo "Could not connect to hostapd-as-RADIUS-server"
+	exit 1
+    fi
+    sleep 1
+done
+
+if [ $USER = "0" -o $USER = "root" ]; then
+    exit 0
+fi
+
+sleep 0.75
+sudo chown -f $USER $LOGDIR/hwsim0.pcapng $LOGDIR/hwsim0 $LOGDIR/log* $LOGDIR/hostapd
+if [ "x$VALGRIND" = "xy" ]; then
+    sudo chown -f $USER $LOGDIR/*valgrind*
+fi
+
+exit 0
diff --git a/hostap/tests/hwsim/stop.sh b/hostap/tests/hwsim/stop.sh
new file mode 100755
index 0000000..5d23b5b
--- /dev/null
+++ b/hostap/tests/hwsim/stop.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+if pidof wpa_supplicant hostapd valgrind.bin hlr_auc_gw > /dev/null; then
+    RUNNING=yes
+else
+    RUNNING=no
+fi
+
+sudo killall -q hostapd
+sudo killall -q wpa_supplicant
+for i in `pidof valgrind.bin`; do
+    if ps $i | grep -q -E "wpa_supplicant|hostapd"; then
+	sudo kill $i
+    fi
+done
+sudo killall -q wlantest
+if grep -q hwsim0 /proc/net/dev; then
+    sudo ifconfig hwsim0 down
+fi
+
+sudo killall -q hlr_auc_gw
+
+if [ "$RUNNING" = "yes" ]; then
+    # give some time for hostapd and wpa_supplicant to complete deinit
+    for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
+	if ! pidof wpa_supplicant hostapd valgrind.bin hlr_auc_gw > /dev/null; then
+	    break
+	fi
+	if [ $i -gt 10 ]; then
+	    echo "Waiting for processes to exit (1)"
+	    sleep 1
+	else
+	    sleep 0.06
+	fi
+    done
+fi
+
+if pidof wpa_supplicant hostapd hlr_auc_gw > /dev/null; then
+    echo "wpa_supplicant/hostapd/hlr_auc_gw did not exit - try to force them to die"
+    sudo killall -9 -q hostapd
+    sudo killall -9 -q wpa_supplicant
+    sudo killall -9 -q hlr_auc_gw
+    for i in `seq 1 5`; do
+	if pidof wpa_supplicant hostapd hlr_auc_gw > /dev/null; then
+	    echo "Waiting for processes to exit (2)"
+	    sleep 1
+	else
+	    break
+	fi
+    done
+fi
+
+for i in `pidof valgrind.bin`; do
+    if ps $i | grep -q -E "wpa_supplicant|hostapd"; then
+	echo "wpa_supplicant/hostapd(valgrind) did not exit - try to force it to die"
+	sudo kill -9 $i
+    fi
+done
+
+count=0
+for i in /tmp/wpas-wlan0 /tmp/wpas-wlan1 /tmp/wpas-wlan2 /tmp/wpas-wlan5 /var/run/hostapd-global /tmp/hlr_auc_gw.sock /tmp/wpa_ctrl_* /tmp/eap_sim_db_*; do
+    count=$(($count + 1))
+    if [ $count -lt 7 -a -e $i ]; then
+	echo "Waiting for ctrl_iface $i to disappear"
+	sleep 1
+    fi
+    if [ -e $i ]; then
+	echo "Control interface file $i exists - remove it"
+	sudo rm $i
+    fi
+done
+
+if grep -q mac80211_hwsim /proc/modules 2>/dev/null ; then
+    sudo rmmod mac80211_hwsim
+    sudo rmmod mac80211
+    sudo rmmod cfg80211
+    # wait at the end to avoid issues starting something new immediately after
+    # this script returns
+    sleep 1
+fi
diff --git a/hostap/tests/hwsim/test_ap_acs.py b/hostap/tests/hwsim/test_ap_acs.py
new file mode 100644
index 0000000..a4a4e30
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_acs.py
@@ -0,0 +1,227 @@
+# Test cases for automatic channel selection with hostapd
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import subprocess
+import time
+
+import hostapd
+from utils import skip_with_fips
+from test_ap_ht import clear_scan_cache
+
+def force_prev_ap_on_24g(ap):
+    # For now, make sure the last operating channel was on 2.4 GHz band to get
+    # sufficient survey data from mac80211_hwsim.
+    hostapd.add_ap(ap['ifname'], { "ssid": "open" })
+    time.sleep(0.1)
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.remove(ap['ifname'])
+
+def force_prev_ap_on_5g(ap):
+    # For now, make sure the last operating channel was on 5 GHz band to get
+    # sufficient survey data from mac80211_hwsim.
+    hostapd.add_ap(ap['ifname'], { "ssid": "open", "hw_mode": "a",
+                                   "channel": "36", "country_code": "US" })
+    time.sleep(0.1)
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.remove(ap['ifname'])
+
+def wait_acs(hapd):
+    ev = hapd.wait_event(["ACS-STARTED", "ACS-COMPLETED", "ACS-FAILED",
+                          "AP-ENABLED", "AP-DISABLED"], timeout=5)
+    if not ev:
+        raise Exception("ACS start timed out")
+    if "ACS-STARTED" not in ev:
+        raise Exception("Unexpected ACS event: " + ev)
+
+    state = hapd.get_status_field("state")
+    if state != "ACS":
+        raise Exception("Unexpected interface state")
+
+    ev = hapd.wait_event(["ACS-COMPLETED", "ACS-FAILED", "AP-ENABLED",
+                          "AP-DISABLED"], timeout=20)
+    if not ev:
+        raise Exception("ACS timed out")
+    if "ACS-COMPLETED" not in ev:
+        raise Exception("Unexpected ACS event: " + ev)
+
+    ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=5)
+    if not ev:
+        raise Exception("AP setup timed out")
+    if "AP-ENABLED" not in ev:
+        raise Exception("Unexpected ACS event: " + ev)
+
+    state = hapd.get_status_field("state")
+    if state != "ENABLED":
+        raise Exception("Unexpected interface state")
+
+def test_ap_acs(dev, apdev):
+    """Automatic channel selection"""
+    force_prev_ap_on_24g(apdev[0])
+    params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+    params['channel'] = '0'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+    wait_acs(hapd)
+
+    freq = hapd.get_status_field("freq")
+    if int(freq) < 2400:
+        raise Exception("Unexpected frequency")
+
+    dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+
+def test_ap_acs_chanlist(dev, apdev):
+    """Automatic channel selection with chanlist set"""
+    force_prev_ap_on_24g(apdev[0])
+    params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+    params['channel'] = '0'
+    params['chanlist'] = '1 6 11'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+    wait_acs(hapd)
+
+    freq = hapd.get_status_field("freq")
+    if int(freq) < 2400:
+        raise Exception("Unexpected frequency")
+
+    dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+
+def test_ap_multi_bss_acs(dev, apdev):
+    """hostapd start with a multi-BSS configuration file using ACS"""
+    skip_with_fips(dev[0])
+    ifname = apdev[0]['ifname']
+    force_prev_ap_on_24g(apdev[0])
+
+    # start the actual test
+    hostapd.add_iface(ifname, 'multi-bss-acs.conf')
+    hapd = hostapd.Hostapd(ifname)
+    hapd.enable()
+    wait_acs(hapd)
+
+    freq = hapd.get_status_field("freq")
+    if int(freq) < 2400:
+        raise Exception("Unexpected frequency")
+
+    dev[0].connect("bss-1", key_mgmt="NONE", scan_freq=freq)
+    dev[1].connect("bss-2", psk="12345678", scan_freq=freq)
+    dev[2].connect("bss-3", psk="qwertyuiop", scan_freq=freq)
+
+def test_ap_acs_40mhz(dev, apdev):
+    """Automatic channel selection for 40 MHz channel"""
+    clear_scan_cache(apdev[0]['ifname'])
+    force_prev_ap_on_24g(apdev[0])
+    params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+    params['channel'] = '0'
+    params['ht_capab'] = '[HT40+]'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+    wait_acs(hapd)
+
+    freq = hapd.get_status_field("freq")
+    if int(freq) < 2400:
+        raise Exception("Unexpected frequency")
+    sec = hapd.get_status_field("secondary_channel")
+    if int(sec) == 0:
+        raise Exception("Secondary channel not set")
+
+    dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+
+def test_ap_acs_5ghz(dev, apdev):
+    """Automatic channel selection on 5 GHz"""
+    try:
+        hapd = None
+        force_prev_ap_on_5g(apdev[0])
+        params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+        params['hw_mode'] = 'a'
+        params['channel'] = '0'
+        params['country_code'] = 'US'
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+        wait_acs(hapd)
+        freq = hapd.get_status_field("freq")
+        if int(freq) < 5000:
+            raise Exception("Unexpected frequency")
+
+        dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_ap_acs_5ghz_40mhz(dev, apdev):
+    """Automatic channel selection on 5 GHz for 40 MHz channel"""
+    try:
+        hapd = None
+        force_prev_ap_on_5g(apdev[0])
+        params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+        params['hw_mode'] = 'a'
+        params['channel'] = '0'
+        params['ht_capab'] = '[HT40+]'
+        params['country_code'] = 'US'
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+        wait_acs(hapd)
+        freq = hapd.get_status_field("freq")
+        if int(freq) < 5000:
+            raise Exception("Unexpected frequency")
+
+        sec = hapd.get_status_field("secondary_channel")
+        if int(sec) == 0:
+            raise Exception("Secondary channel not set")
+
+        dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_ap_acs_vht(dev, apdev):
+    """Automatic channel selection for VHT"""
+    try:
+        hapd = None
+        force_prev_ap_on_5g(apdev[0])
+        params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+        params['hw_mode'] = 'a'
+        params['channel'] = '0'
+        params['ht_capab'] = '[HT40+]'
+        params['country_code'] = 'US'
+        params['ieee80211ac'] = '1'
+        params['vht_oper_chwidth'] = '1'
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+        wait_acs(hapd)
+        freq = hapd.get_status_field("freq")
+        if int(freq) < 5000:
+            raise Exception("Unexpected frequency")
+
+        sec = hapd.get_status_field("secondary_channel")
+        if int(sec) == 0:
+            raise Exception("Secondary channel not set")
+
+        dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_ap_acs_bias(dev, apdev):
+    """Automatic channel selection with bias values"""
+    force_prev_ap_on_24g(apdev[0])
+    params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+    params['channel'] = '0'
+    params['acs_chan_bias'] = '1:0.8 3:1.2 6:0.7 11:0.8'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+    wait_acs(hapd)
+
+    freq = hapd.get_status_field("freq")
+    if int(freq) < 2400:
+        raise Exception("Unexpected frequency")
+
+    dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
diff --git a/hostap/tests/hwsim/test_ap_ciphers.py b/hostap/tests/hwsim/test_ap_ciphers.py
new file mode 100644
index 0000000..9d4e868
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_ciphers.py
@@ -0,0 +1,209 @@
+# Cipher suite tests
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import logging
+logger = logging.getLogger()
+import os.path
+
+import hwsim_utils
+import hostapd
+from utils import HwsimSkip, skip_with_fips
+from wlantest import Wlantest
+
+def check_cipher(dev, ap, cipher):
+    if cipher not in dev.get_capability("pairwise"):
+        raise HwsimSkip("Cipher %s not supported" % cipher)
+    params = { "ssid": "test-wpa2-psk",
+               "wpa_passphrase": "12345678",
+               "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK",
+               "rsn_pairwise": cipher }
+    hapd = hostapd.add_ap(ap['ifname'], params)
+    dev.connect("test-wpa2-psk", psk="12345678",
+                pairwise=cipher, group=cipher, scan_freq="2412")
+    hwsim_utils.test_connectivity(dev, hapd)
+
+def check_group_mgmt_cipher(dev, ap, cipher):
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+
+    if cipher not in dev.get_capability("group_mgmt"):
+        raise HwsimSkip("Cipher %s not supported" % cipher)
+    params = { "ssid": "test-wpa2-psk-pmf",
+               "wpa_passphrase": "12345678",
+               "wpa": "2",
+               "ieee80211w": "2",
+               "wpa_key_mgmt": "WPA-PSK-SHA256",
+               "rsn_pairwise": "CCMP",
+               "group_mgmt_cipher": cipher }
+    hapd = hostapd.add_ap(ap['ifname'], params)
+    dev.connect("test-wpa2-psk-pmf", psk="12345678", ieee80211w="2",
+                key_mgmt="WPA-PSK-SHA256",
+                pairwise="CCMP", group="CCMP", scan_freq="2412")
+    hwsim_utils.test_connectivity(dev, hapd)
+    hapd.request("DEAUTHENTICATE ff:ff:ff:ff:ff:ff")
+    dev.wait_disconnected()
+    if wt.get_bss_counter('valid_bip_mmie', ap['bssid']) < 1:
+        raise Exception("No valid BIP MMIE seen")
+    if wt.get_bss_counter('bip_deauth', ap['bssid']) < 1:
+        raise Exception("No valid BIP deauth seen")
+
+    if cipher == "AES-128-CMAC":
+        group_mgmt = "BIP"
+    else:
+        group_mgmt = cipher
+    res =  wt.info_bss('group_mgmt', ap['bssid']).strip()
+    if res != group_mgmt:
+        raise Exception("Unexpected group mgmt cipher: " + res)
+
+def test_ap_cipher_tkip(dev, apdev):
+    """WPA2-PSK/TKIP connection"""
+    skip_with_fips(dev[0])
+    check_cipher(dev[0], apdev[0], "TKIP")
+
+def test_ap_cipher_tkip_countermeasures_ap(dev, apdev):
+    """WPA-PSK/TKIP countermeasures (detected by AP)"""
+    skip_with_fips(dev[0])
+    testfile = "/sys/kernel/debug/ieee80211/%s/netdev:%s/tkip_mic_test" % (dev[0].get_driver_status_field("phyname"), dev[0].ifname)
+    if not os.path.exists(testfile):
+        raise HwsimSkip("tkip_mic_test not supported in mac80211")
+
+    params = { "ssid": "tkip-countermeasures",
+               "wpa_passphrase": "12345678",
+               "wpa": "1",
+               "wpa_key_mgmt": "WPA-PSK",
+               "wpa_pairwise": "TKIP" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("tkip-countermeasures", psk="12345678",
+                   pairwise="TKIP", group="TKIP", scan_freq="2412")
+
+    dev[0].dump_monitor()
+    with open(testfile, "w") as f:
+        f.write(apdev[0]['bssid'])
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected disconnection on first Michael MIC failure")
+
+    with open(testfile, "w") as f:
+        f.write("ff:ff:ff:ff:ff:ff")
+    ev = dev[0].wait_disconnected(timeout=10,
+                                  error="No disconnection after two Michael MIC failures")
+    if "reason=14" not in ev:
+        raise Exception("Unexpected disconnection reason: " + ev)
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected connection during TKIP countermeasures")
+
+def test_ap_cipher_tkip_countermeasures_sta(dev, apdev):
+    """WPA-PSK/TKIP countermeasures (detected by STA)"""
+    skip_with_fips(dev[0])
+    params = { "ssid": "tkip-countermeasures",
+               "wpa_passphrase": "12345678",
+               "wpa": "1",
+               "wpa_key_mgmt": "WPA-PSK",
+               "wpa_pairwise": "TKIP" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    testfile = "/sys/kernel/debug/ieee80211/%s/netdev:%s/tkip_mic_test" % (hapd.get_driver_status_field("phyname"), apdev[0]['ifname'])
+    if not os.path.exists(testfile):
+        raise HwsimSkip("tkip_mic_test not supported in mac80211")
+
+    dev[0].connect("tkip-countermeasures", psk="12345678",
+                   pairwise="TKIP", group="TKIP", scan_freq="2412")
+
+    dev[0].dump_monitor()
+    with open(testfile, "w") as f:
+        f.write(dev[0].p2p_dev_addr())
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected disconnection on first Michael MIC failure")
+
+    with open(testfile, "w") as f:
+        f.write("ff:ff:ff:ff:ff:ff")
+    ev = dev[0].wait_disconnected(timeout=10,
+                                  error="No disconnection after two Michael MIC failures")
+    if "reason=14 locally_generated=1" not in ev:
+        raise Exception("Unexpected disconnection reason: " + ev)
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected connection during TKIP countermeasures")
+
+def test_ap_cipher_ccmp(dev, apdev):
+    """WPA2-PSK/CCMP connection"""
+    check_cipher(dev[0], apdev[0], "CCMP")
+
+def test_ap_cipher_gcmp(dev, apdev):
+    """WPA2-PSK/GCMP connection"""
+    check_cipher(dev[0], apdev[0], "GCMP")
+
+def test_ap_cipher_ccmp_256(dev, apdev):
+    """WPA2-PSK/CCMP-256 connection"""
+    check_cipher(dev[0], apdev[0], "CCMP-256")
+
+def test_ap_cipher_gcmp_256(dev, apdev):
+    """WPA2-PSK/GCMP-256 connection"""
+    check_cipher(dev[0], apdev[0], "GCMP-256")
+
+def test_ap_cipher_mixed_wpa_wpa2(dev, apdev):
+    """WPA2-PSK/CCMP/ and WPA-PSK/TKIP mixed configuration"""
+    skip_with_fips(dev[0])
+    ssid = "test-wpa-wpa2-psk"
+    passphrase = "12345678"
+    params = { "ssid": ssid,
+               "wpa_passphrase": passphrase,
+               "wpa": "3",
+               "wpa_key_mgmt": "WPA-PSK",
+               "rsn_pairwise": "CCMP",
+               "wpa_pairwise": "TKIP" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, proto="WPA2",
+                   pairwise="CCMP", group="TKIP", scan_freq="2412")
+    status = dev[0].get_status()
+    if status['key_mgmt'] != 'WPA2-PSK':
+        raise Exception("Incorrect key_mgmt reported")
+    if status['pairwise_cipher'] != 'CCMP':
+        raise Exception("Incorrect pairwise_cipher reported")
+    if status['group_cipher'] != 'TKIP':
+        raise Exception("Incorrect group_cipher reported")
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    if bss['ssid'] != ssid:
+        raise Exception("Unexpected SSID in the BSS entry")
+    if "[WPA-PSK-TKIP]" not in bss['flags']:
+        raise Exception("Missing BSS flag WPA-PSK-TKIP")
+    if "[WPA2-PSK-CCMP]" not in bss['flags']:
+        raise Exception("Missing BSS flag WPA2-PSK-CCMP")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+    dev[1].connect(ssid, psk=passphrase, proto="WPA",
+                   pairwise="TKIP", group="TKIP", scan_freq="2412")
+    status = dev[1].get_status()
+    if status['key_mgmt'] != 'WPA-PSK':
+        raise Exception("Incorrect key_mgmt reported")
+    if status['pairwise_cipher'] != 'TKIP':
+        raise Exception("Incorrect pairwise_cipher reported")
+    if status['group_cipher'] != 'TKIP':
+        raise Exception("Incorrect group_cipher reported")
+    hwsim_utils.test_connectivity(dev[1], hapd)
+    hwsim_utils.test_connectivity(dev[0], dev[1])
+
+def test_ap_cipher_bip(dev, apdev):
+    """WPA2-PSK with BIP"""
+    check_group_mgmt_cipher(dev[0], apdev[0], "AES-128-CMAC")
+
+def test_ap_cipher_bip_gmac_128(dev, apdev):
+    """WPA2-PSK with BIP-GMAC-128"""
+    check_group_mgmt_cipher(dev[0], apdev[0], "BIP-GMAC-128")
+
+def test_ap_cipher_bip_gmac_256(dev, apdev):
+    """WPA2-PSK with BIP-GMAC-256"""
+    check_group_mgmt_cipher(dev[0], apdev[0], "BIP-GMAC-256")
+
+def test_ap_cipher_bip_cmac_256(dev, apdev):
+    """WPA2-PSK with BIP-CMAC-256"""
+    check_group_mgmt_cipher(dev[0], apdev[0], "BIP-CMAC-256")
diff --git a/hostap/tests/hwsim/test_ap_config.py b/hostap/tests/hwsim/test_ap_config.py
new file mode 100644
index 0000000..aec70f7
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_config.py
@@ -0,0 +1,80 @@
+# hostapd configuration 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.
+
+import hostapd
+
+def test_ap_config_errors(dev, apdev):
+    """Various hostapd configuration errors"""
+    hapd_global = hostapd.HostapdGlobal()
+    ifname = apdev[0]['ifname']
+
+    # IEEE 802.11d without country code
+    params = { "ssid": "foo", "ieee80211d": "1" }
+    hapd = hostapd.add_ap(ifname, params, no_enable=True)
+    if "FAIL" not in hapd.request("ENABLE"):
+        raise Exception("Unexpected ENABLE success (ieee80211d without country_code)")
+    hapd_global.remove(ifname)
+
+    # IEEE 802.11h without IEEE 802.11d
+    params = { "ssid": "foo", "ieee80211h": "1" }
+    hapd = hostapd.add_ap(ifname, params, no_enable=True)
+    if "FAIL" not in hapd.request("ENABLE"):
+        raise Exception("Unexpected ENABLE success (ieee80211h without ieee80211d")
+    hapd_global.remove(ifname)
+
+    # Power Constraint without IEEE 802.11d
+    params = { "ssid": "foo", "local_pwr_constraint": "1" }
+    hapd = hostapd.add_ap(ifname, params, no_enable=True)
+    if "FAIL" not in hapd.request("ENABLE"):
+        raise Exception("Unexpected ENABLE success (local_pwr_constraint without ieee80211d)")
+    hapd_global.remove(ifname)
+
+    # Spectrum management without Power Constraint
+    params = { "ssid": "foo", "spectrum_mgmt_required": "1" }
+    hapd = hostapd.add_ap(ifname, params, no_enable=True)
+    if "FAIL" not in hapd.request("ENABLE"):
+        raise Exception("Unexpected ENABLE success (spectrum_mgmt_required without local_pwr_constraint)")
+    hapd_global.remove(ifname)
+
+    # IEEE 802.1X without authentication server
+    params = { "ssid": "foo", "ieee8021x": "1" }
+    hapd = hostapd.add_ap(ifname, params, no_enable=True)
+    if "FAIL" not in hapd.request("ENABLE"):
+        raise Exception("Unexpected ENABLE success (ieee8021x)")
+    hapd_global.remove(ifname)
+
+    # RADIUS-PSK without macaddr_acl=2
+    params = hostapd.wpa2_params(ssid="foo", passphrase="12345678")
+    params["wpa_psk_radius"] = "1"
+    hapd = hostapd.add_ap(ifname, params, no_enable=True)
+    if "FAIL" not in hapd.request("ENABLE"):
+        raise Exception("Unexpected ENABLE success (wpa_psk_radius)")
+    hapd_global.remove(ifname)
+
+    # FT without NAS-Identifier
+    params = { "wpa": "2",
+               "wpa_key_mgmt": "FT-PSK",
+               "rsn_pairwise": "CCMP",
+               "wpa_passphrase": "12345678" }
+    hapd = hostapd.add_ap(ifname, params, no_enable=True)
+    if "FAIL" not in hapd.request("ENABLE"):
+        raise Exception("Unexpected ENABLE success (FT without nas_identifier)")
+    hapd_global.remove(ifname)
+
+    # Hotspot 2.0 without WPA2/CCMP
+    params = hostapd.wpa2_params(ssid="foo")
+    params['wpa_key_mgmt'] = "WPA-EAP"
+    params['ieee8021x'] = "1"
+    params['auth_server_addr'] = "127.0.0.1"
+    params['auth_server_port'] = "1812"
+    params['auth_server_shared_secret'] = "radius"
+    params['interworking'] = "1"
+    params['hs20'] = "1"
+    params['wpa'] = "1"
+    hapd = hostapd.add_ap(ifname, params, no_enable=True)
+    if "FAIL" not in hapd.request("ENABLE"):
+        raise Exception("Unexpected ENABLE success (HS 2.0 without WPA2/CCMP)")
+    hapd_global.remove(ifname)
diff --git a/hostap/tests/hwsim/test_ap_csa.py b/hostap/tests/hwsim/test_ap_csa.py
new file mode 100644
index 0000000..c88e517
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_csa.py
@@ -0,0 +1,111 @@
+# AP CSA tests
+# Copyright (c) 2013, Luciano Coelho <luciano.coelho@intel.com>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import hostapd
+from utils import HwsimSkip
+
+def connect(dev, apdev):
+    params = { "ssid": "ap-csa",
+               "channel": "1" }
+    ap = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev.connect("ap-csa", key_mgmt="NONE")
+    return ap
+
+def switch_channel(ap, count, freq):
+    ap.request("CHAN_SWITCH " + str(count) + " " + str(freq))
+    ev = ap.wait_event(["AP-CSA-FINISHED"], timeout=10)
+    if ev is None:
+        raise Exception("CSA finished event timed out")
+    if "freq=" + str(freq) not in ev:
+        raise Exception("Unexpected channel in CSA finished event")
+    time.sleep(0.1)
+
+# This function checks whether the provided dev, which may be either
+# WpaSupplicant or Hostapd supports CSA.
+def csa_supported(dev):
+    res = dev.get_driver_status()
+    if (int(res['capa.flags'], 0) & 0x80000000) == 0:
+        raise HwsimSkip("CSA not supported")
+
+def test_ap_csa_1_switch(dev, apdev):
+    """AP Channel Switch, one switch"""
+    csa_supported(dev[0])
+    ap = connect(dev[0], apdev)
+
+    hwsim_utils.test_connectivity(dev[0], ap)
+    switch_channel(ap, 10, 2462)
+    hwsim_utils.test_connectivity(dev[0], ap)
+
+def test_ap_csa_2_switches(dev, apdev):
+    """AP Channel Switch, two switches"""
+    csa_supported(dev[0])
+    ap = connect(dev[0], apdev)
+
+    hwsim_utils.test_connectivity(dev[0], ap)
+    switch_channel(ap, 10, 2462)
+    hwsim_utils.test_connectivity(dev[0], ap)
+    switch_channel(ap, 10, 2412)
+    hwsim_utils.test_connectivity(dev[0], ap)
+
+def test_ap_csa_1_switch_count_0(dev, apdev):
+    """AP Channel Switch, one switch with count 0"""
+    csa_supported(dev[0])
+    ap = connect(dev[0], apdev)
+
+    hwsim_utils.test_connectivity(dev[0], ap)
+    switch_channel(ap, 0, 2462)
+    # this does not result in CSA currently, so do not bother checking
+    # connectivity
+
+def test_ap_csa_2_switches_count_0(dev, apdev):
+    """AP Channel Switch, two switches with count 0"""
+    csa_supported(dev[0])
+    ap = connect(dev[0], apdev)
+
+    hwsim_utils.test_connectivity(dev[0], ap)
+    switch_channel(ap, 0, 2462)
+    # this does not result in CSA currently, so do not bother checking
+    # connectivity
+    switch_channel(ap, 0, 2412)
+    # this does not result in CSA currently, so do not bother checking
+    # connectivity
+
+def test_ap_csa_1_switch_count_1(dev, apdev):
+    """AP Channel Switch, one switch with count 1"""
+    csa_supported(dev[0])
+    ap = connect(dev[0], apdev)
+
+    hwsim_utils.test_connectivity(dev[0], ap)
+    switch_channel(ap, 1, 2462)
+    # this does not result in CSA currently, so do not bother checking
+    # connectivity
+
+def test_ap_csa_2_switches_count_1(dev, apdev):
+    """AP Channel Switch, two switches with count 1"""
+    csa_supported(dev[0])
+    ap = connect(dev[0], apdev)
+
+    hwsim_utils.test_connectivity(dev[0], ap)
+    switch_channel(ap, 1, 2462)
+    # this does not result in CSA currently, so do not bother checking
+    # connectivity
+    switch_channel(ap, 1, 2412)
+    # this does not result in CSA currently, so do not bother checking
+    # connectivity
+
+def test_ap_csa_1_switch_count_2(dev, apdev):
+    """AP Channel Switch, one switch with count 2"""
+    csa_supported(dev[0])
+    ap = connect(dev[0], apdev)
+
+    hwsim_utils.test_connectivity(dev[0], ap)
+    switch_channel(ap, 2, 2462)
+    hwsim_utils.test_connectivity(dev[0], ap)
diff --git a/hostap/tests/hwsim/test_ap_dynamic.py b/hostap/tests/hwsim/test_ap_dynamic.py
new file mode 100644
index 0000000..19eb9c4
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_dynamic.py
@@ -0,0 +1,485 @@
+# Test cases for dynamic BSS changes with hostapd
+# Copyright (c) 2013, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+import os
+
+import hwsim_utils
+import hostapd
+from utils import alloc_fail
+from test_ap_acs import force_prev_ap_on_24g
+
+def test_ap_change_ssid(dev, apdev):
+    """Dynamic SSID change with hostapd and WPA2-PSK"""
+    params = hostapd.wpa2_params(ssid="test-wpa2-psk-start",
+                                 passphrase="12345678")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    id = dev[0].connect("test-wpa2-psk-start", psk="12345678",
+                        scan_freq="2412")
+    dev[0].request("DISCONNECT")
+
+    logger.info("Change SSID dynamically")
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    res = hapd.request("SET ssid test-wpa2-psk-new")
+    if "OK" not in res:
+        raise Exception("SET command failed")
+    res = hapd.request("RELOAD")
+    if "OK" not in res:
+        raise Exception("RELOAD command failed")
+
+    dev[0].set_network_quoted(id, "ssid", "test-wpa2-psk-new")
+    dev[0].connect_network(id)
+
+def multi_check(dev, check, scan_opt=True):
+    id = []
+    num_bss = len(check)
+    for i in range(0, num_bss):
+        dev[i].request("BSS_FLUSH 0")
+        dev[i].dump_monitor()
+    for i in range(0, num_bss):
+        if check[i]:
+            continue
+        id.append(dev[i].connect("bss-" + str(i + 1), key_mgmt="NONE",
+                                 scan_freq="2412", wait_connect=False))
+    for i in range(num_bss):
+        if not check[i]:
+            continue
+        bssid = '02:00:00:00:03:0' + str(i)
+        if scan_opt:
+            dev[i].scan_for_bss(bssid, freq=2412)
+        id.append(dev[i].connect("bss-" + str(i + 1), key_mgmt="NONE",
+                                 scan_freq="2412", wait_connect=True))
+    first = True
+    for i in range(num_bss):
+        if not check[i]:
+            timeout=0.2 if first else 0.01
+            first = False
+            ev = dev[i].wait_event(["CTRL-EVENT-CONNECTED"], timeout=timeout)
+            if ev:
+                raise Exception("Unexpected connection")
+
+    for i in range(0, num_bss):
+        dev[i].remove_network(id[i])
+    for i in range(num_bss):
+        if check[i]:
+            dev[i].wait_disconnected(timeout=5)
+
+    res = ''
+    for i in range(0, num_bss):
+        res = res + dev[i].request("BSS RANGE=ALL MASK=0x2")
+
+    for i in range(0, num_bss):
+        if not check[i]:
+            bssid = '02:00:00:00:03:0' + str(i)
+            if bssid in res:
+                raise Exception("Unexpected BSS" + str(i) + " in scan results")
+
+def test_ap_bss_add_remove(dev, apdev):
+    """Dynamic BSS add/remove operations with hostapd"""
+    try:
+        _test_ap_bss_add_remove(dev, apdev)
+    finally:
+        for i in range(3):
+            dev[i].request("SCAN_INTERVAL 5")
+
+def _test_ap_bss_add_remove(dev, apdev):
+    for i in range(3):
+        dev[i].request("SCAN_INTERVAL 1")
+    ifname1 = apdev[0]['ifname']
+    ifname2 = apdev[0]['ifname'] + '-2'
+    ifname3 = apdev[0]['ifname'] + '-3'
+    logger.info("Set up three BSSes one by one")
+    hostapd.add_bss('phy3', ifname1, 'bss-1.conf')
+    multi_check(dev, [ True, False, False ])
+    hostapd.add_bss('phy3', ifname2, 'bss-2.conf')
+    multi_check(dev, [ True, True, False ])
+    hostapd.add_bss('phy3', ifname3, 'bss-3.conf')
+    multi_check(dev, [ True, True, True ])
+
+    logger.info("Remove the last BSS and re-add it")
+    hostapd.remove_bss(ifname3)
+    multi_check(dev, [ True, True, False ])
+    hostapd.add_bss('phy3', ifname3, 'bss-3.conf')
+    multi_check(dev, [ True, True, True ])
+
+    logger.info("Remove the middle BSS and re-add it")
+    hostapd.remove_bss(ifname2)
+    multi_check(dev, [ True, False, True ])
+    hostapd.add_bss('phy3', ifname2, 'bss-2.conf')
+    multi_check(dev, [ True, True, True ])
+
+    logger.info("Remove the first BSS and re-add it and other BSSs")
+    hostapd.remove_bss(ifname1)
+    multi_check(dev, [ False, False, False ])
+    hostapd.add_bss('phy3', ifname1, 'bss-1.conf')
+    hostapd.add_bss('phy3', ifname2, 'bss-2.conf')
+    hostapd.add_bss('phy3', ifname3, 'bss-3.conf')
+    multi_check(dev, [ True, True, True ])
+
+    logger.info("Remove two BSSes and re-add them")
+    hostapd.remove_bss(ifname2)
+    multi_check(dev, [ True, False, True ])
+    hostapd.remove_bss(ifname3)
+    multi_check(dev, [ True, False, False ])
+    hostapd.add_bss('phy3', ifname2, 'bss-2.conf')
+    multi_check(dev, [ True, True, False ])
+    hostapd.add_bss('phy3', ifname3, 'bss-3.conf')
+    multi_check(dev, [ True, True, True ])
+
+    logger.info("Remove three BSSes in and re-add them")
+    hostapd.remove_bss(ifname3)
+    multi_check(dev, [ True, True, False ])
+    hostapd.remove_bss(ifname2)
+    multi_check(dev, [ True, False, False ])
+    hostapd.remove_bss(ifname1)
+    multi_check(dev, [ False, False, False ])
+    hostapd.add_bss('phy3', ifname1, 'bss-1.conf')
+    multi_check(dev, [ True, False, False ])
+    hostapd.add_bss('phy3', ifname2, 'bss-2.conf')
+    multi_check(dev, [ True, True, False ])
+    hostapd.add_bss('phy3', ifname3, 'bss-3.conf')
+    multi_check(dev, [ True, True, True ])
+
+    logger.info("Test error handling if a duplicate ifname is tried")
+    hostapd.add_bss('phy3', ifname3, 'bss-3.conf', ignore_error=True)
+    multi_check(dev, [ True, True, True ])
+
+def test_ap_bss_add_remove_during_ht_scan(dev, apdev):
+    """Dynamic BSS add during HT40 co-ex scan"""
+    ifname1 = apdev[0]['ifname']
+    ifname2 = apdev[0]['ifname'] + '-2'
+    hostapd.add_bss('phy3', ifname1, 'bss-ht40-1.conf')
+    hostapd.add_bss('phy3', ifname2, 'bss-ht40-2.conf')
+    multi_check(dev, [ True, True ], scan_opt=False)
+    hostapd.remove_bss(ifname2)
+    hostapd.remove_bss(ifname1)
+
+    hostapd.add_bss('phy3', ifname1, 'bss-ht40-1.conf')
+    hostapd.add_bss('phy3', ifname2, 'bss-ht40-2.conf')
+    hostapd.remove_bss(ifname2)
+    multi_check(dev, [ True, False ], scan_opt=False)
+    hostapd.remove_bss(ifname1)
+
+    hostapd.add_bss('phy3', ifname1, 'bss-ht40-1.conf')
+    hostapd.add_bss('phy3', ifname2, 'bss-ht40-2.conf')
+    hostapd.remove_bss(ifname1)
+    multi_check(dev, [ False, False ])
+
+def test_ap_multi_bss_config(dev, apdev):
+    """hostapd start with a multi-BSS configuration file"""
+    ifname1 = apdev[0]['ifname']
+    ifname2 = apdev[0]['ifname'] + '-2'
+    ifname3 = apdev[0]['ifname'] + '-3'
+    logger.info("Set up three BSSes with one configuration file")
+    hostapd.add_iface(ifname1, 'multi-bss.conf')
+    hapd = hostapd.Hostapd(ifname1)
+    hapd.enable()
+    multi_check(dev, [ True, True, True ])
+    hostapd.remove_bss(ifname2)
+    multi_check(dev, [ True, False, True ])
+    hostapd.remove_bss(ifname3)
+    multi_check(dev, [ True, False, False ])
+    hostapd.remove_bss(ifname1)
+    multi_check(dev, [ False, False, False ])
+
+    hostapd.add_iface(ifname1, 'multi-bss.conf')
+    hapd = hostapd.Hostapd(ifname1)
+    hapd.enable()
+    hostapd.remove_bss(ifname1)
+    multi_check(dev, [ False, False, False ])
+
+def invalid_ap(hapd_global, ifname):
+    logger.info("Trying to start AP " + ifname + " with invalid configuration")
+    hapd_global.remove(ifname)
+    hapd_global.add(ifname)
+    hapd = hostapd.Hostapd(ifname)
+    if not hapd.ping():
+        raise Exception("Could not ping hostapd")
+    hapd.set_defaults()
+    hapd.set("ssid", "invalid-config")
+    hapd.set("channel", "12345")
+    try:
+        hapd.enable()
+        started = True
+    except Exception, e:
+        started = False
+    if started:
+        raise Exception("ENABLE command succeeded unexpectedly")
+    return hapd
+
+def test_ap_invalid_config(dev, apdev):
+    """Try to start AP with invalid configuration and fix configuration"""
+    hapd_global = hostapd.HostapdGlobal()
+    ifname = apdev[0]['ifname']
+    hapd = invalid_ap(hapd_global, ifname)
+
+    logger.info("Fix configuration and start AP again")
+    hapd.set("channel", "1")
+    hapd.enable()
+    dev[0].connect("invalid-config", key_mgmt="NONE", scan_freq="2412")
+
+def test_ap_invalid_config2(dev, apdev):
+    """Try to start AP with invalid configuration and remove interface"""
+    hapd_global = hostapd.HostapdGlobal()
+    ifname = apdev[0]['ifname']
+    hapd = invalid_ap(hapd_global, ifname)
+    logger.info("Remove interface with failed configuration")
+    hapd_global.remove(ifname)
+
+def test_ap_remove_during_acs(dev, apdev):
+    """Remove interface during ACS"""
+    force_prev_ap_on_24g(apdev[0])
+    params = hostapd.wpa2_params(ssid="test-acs-remove", passphrase="12345678")
+    params['channel'] = '0'
+    ifname = apdev[0]['ifname']
+    hapd = hostapd.HostapdGlobal()
+    hostapd.add_ap(ifname, params)
+    hapd.remove(ifname)
+
+def test_ap_remove_during_acs2(dev, apdev):
+    """Remove BSS during ACS in multi-BSS configuration"""
+    force_prev_ap_on_24g(apdev[0])
+    ifname = apdev[0]['ifname']
+    ifname2 = ifname + "-2"
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.add(ifname)
+    hapd = hostapd.Hostapd(ifname)
+    hapd.set_defaults()
+    hapd.set("ssid", "test-acs-remove")
+    hapd.set("channel", "0")
+    hapd.set("bss", ifname2)
+    hapd.set("ssid", "test-acs-remove2")
+    hapd.enable()
+    hapd_global.remove(ifname)
+
+def test_ap_remove_during_acs3(dev, apdev):
+    """Remove second BSS during ACS in multi-BSS configuration"""
+    force_prev_ap_on_24g(apdev[0])
+    ifname = apdev[0]['ifname']
+    ifname2 = ifname + "-2"
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.add(ifname)
+    hapd = hostapd.Hostapd(ifname)
+    hapd.set_defaults()
+    hapd.set("ssid", "test-acs-remove")
+    hapd.set("channel", "0")
+    hapd.set("bss", ifname2)
+    hapd.set("ssid", "test-acs-remove2")
+    hapd.enable()
+    hapd_global.remove(ifname2)
+
+def test_ap_remove_during_ht_coex_scan(dev, apdev):
+    """Remove interface during HT co-ex scan"""
+    params = hostapd.wpa2_params(ssid="test-ht-remove", passphrase="12345678")
+    params['channel'] = '1'
+    params['ht_capab'] = "[HT40+]"
+    ifname = apdev[0]['ifname']
+    hapd = hostapd.HostapdGlobal()
+    hostapd.add_ap(ifname, params)
+    hapd.remove(ifname)
+
+def test_ap_remove_during_ht_coex_scan2(dev, apdev):
+    """Remove BSS during HT co-ex scan in multi-BSS configuration"""
+    ifname = apdev[0]['ifname']
+    ifname2 = ifname + "-2"
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.add(ifname)
+    hapd = hostapd.Hostapd(ifname)
+    hapd.set_defaults()
+    hapd.set("ssid", "test-ht-remove")
+    hapd.set("channel", "1")
+    hapd.set("ht_capab", "[HT40+]")
+    hapd.set("bss", ifname2)
+    hapd.set("ssid", "test-ht-remove2")
+    hapd.enable()
+    hapd_global.remove(ifname)
+
+def test_ap_remove_during_ht_coex_scan3(dev, apdev):
+    """Remove second BSS during HT co-ex scan in multi-BSS configuration"""
+    ifname = apdev[0]['ifname']
+    ifname2 = ifname + "-2"
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.add(ifname)
+    hapd = hostapd.Hostapd(ifname)
+    hapd.set_defaults()
+    hapd.set("ssid", "test-ht-remove")
+    hapd.set("channel", "1")
+    hapd.set("ht_capab", "[HT40+]")
+    hapd.set("bss", ifname2)
+    hapd.set("ssid", "test-ht-remove2")
+    hapd.enable()
+    hapd_global.remove(ifname2)
+
+def test_ap_enable_disable_reenable(dev, apdev):
+    """Enable, disable, re-enable AP"""
+    ifname = apdev[0]['ifname']
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.add(ifname)
+    hapd = hostapd.Hostapd(ifname)
+    hapd.set_defaults()
+    hapd.set("ssid", "dynamic")
+    hapd.enable()
+    ev = hapd.wait_event(["AP-ENABLED"], timeout=30)
+    if ev is None:
+        raise Exception("AP startup timed out")
+    dev[0].connect("dynamic", key_mgmt="NONE", scan_freq="2412")
+    hapd.disable()
+    ev = hapd.wait_event(["AP-DISABLED"], timeout=30)
+    if ev is None:
+        raise Exception("AP disabling timed out")
+    dev[0].wait_disconnected(timeout=10)
+    hapd.enable()
+    ev = hapd.wait_event(["AP-ENABLED"], timeout=30)
+    if ev is None:
+        raise Exception("AP startup timed out")
+    dev[1].connect("dynamic", key_mgmt="NONE", scan_freq="2412")
+    dev[0].wait_connected(timeout=10)
+
+def test_ap_double_disable(dev, apdev):
+    """Double DISABLE regression test"""
+    hostapd.add_bss('phy3', apdev[0]['ifname'], 'bss-1.conf')
+    hostapd.add_bss('phy3', apdev[0]['ifname'] + '-2', 'bss-2.conf')
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    hapd.disable()
+    if "FAIL" not in hapd.request("DISABLE"):
+        raise Exception("Second DISABLE accepted unexpectedly")
+    hapd.enable()
+    hapd.disable()
+    if "FAIL" not in hapd.request("DISABLE"):
+        raise Exception("Second DISABLE accepted unexpectedly")
+
+def test_ap_bss_add_many(dev, apdev):
+    """Large number of BSS add operations with hostapd"""
+    try:
+        _test_ap_bss_add_many(dev, apdev)
+    finally:
+        dev[0].request("SCAN_INTERVAL 5")
+        ifname = apdev[0]['ifname']
+        hapd = hostapd.HostapdGlobal()
+        hapd.flush()
+        for i in range(16):
+            ifname2 = ifname + '-' + str(i)
+            hapd.remove(ifname2)
+        try:
+            os.remove('/tmp/hwsim-bss.conf')
+        except:
+            pass
+
+def _test_ap_bss_add_many(dev, apdev):
+    ifname = apdev[0]['ifname']
+    phy = 'phy3'
+    hostapd.add_bss(phy, ifname, 'bss-1.conf')
+    hapd = hostapd.HostapdGlobal()
+    fname = '/tmp/hwsim-bss.conf'
+    for i in range(16):
+        ifname2 = ifname + '-' + str(i)
+        with open(fname, 'w') as f:
+            f.write("driver=nl80211\n")
+            f.write("hw_mode=g\n")
+            f.write("channel=1\n")
+            f.write("ieee80211n=1\n")
+            f.write("interface=%s\n" % ifname2)
+            f.write("bssid=02:00:00:00:03:%02x\n" % (i + 1))
+            f.write("ctrl_interface=/var/run/hostapd\n")
+            f.write("ssid=test-%d\n" % i)
+        hostapd.add_bss(phy, ifname2, fname)
+        os.remove(fname)
+
+    dev[0].request("SCAN_INTERVAL 1")
+    dev[0].connect("bss-1", key_mgmt="NONE", scan_freq="2412")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=5)
+    for i in range(16):
+        dev[0].connect("test-%d" % i, key_mgmt="NONE", scan_freq="2412")
+        dev[0].request("DISCONNECT")
+        dev[0].wait_disconnected(timeout=5)
+        ifname2 = ifname + '-' + str(i)
+        hapd.remove(ifname2)
+
+def test_ap_bss_add_reuse_existing(dev, apdev):
+    """Dynamic BSS add operation reusing existing interface"""
+    ifname1 = apdev[0]['ifname']
+    ifname2 = apdev[0]['ifname'] + '-2'
+    hostapd.add_bss('phy3', ifname1, 'bss-1.conf')
+    subprocess.check_call(["iw", "dev", ifname1, "interface", "add", ifname2,
+                           "type", "__ap"])
+    hostapd.add_bss('phy3', ifname2, 'bss-2.conf')
+    hostapd.remove_bss(ifname2)
+    subprocess.check_call(["iw", "dev", ifname2, "del"])
+
+def hapd_bss_out_of_mem(hapd, phy, confname, count, func):
+    with alloc_fail(hapd, count, func):
+        hapd_global = hostapd.HostapdGlobal()
+        res = hapd_global.ctrl.request("ADD bss_config=" + phy + ":" + confname)
+        if "OK" in res:
+            raise Exception("add_bss succeeded")
+
+def test_ap_bss_add_out_of_memory(dev, apdev):
+    """Running out of memory while adding a BSS"""
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], { "ssid": "open" })
+
+    ifname1 = apdev[0]['ifname']
+    ifname2 = apdev[0]['ifname'] + '-2'
+
+    hapd_bss_out_of_mem(hapd2, 'phy3', 'bss-1.conf', 1, 'hostapd_add_iface')
+    for i in range(1, 3):
+        hapd_bss_out_of_mem(hapd2, 'phy3', 'bss-1.conf',
+                            i, 'hostapd_interface_init_bss')
+    hapd_bss_out_of_mem(hapd2, 'phy3', 'bss-1.conf',
+                        1, 'ieee802_11_build_ap_params')
+
+    hostapd.add_bss('phy3', ifname1, 'bss-1.conf')
+
+    hapd_bss_out_of_mem(hapd2, 'phy3', 'bss-2.conf',
+                        1, 'hostapd_interface_init_bss')
+    hapd_bss_out_of_mem(hapd2, 'phy3', 'bss-2.conf',
+                        1, 'ieee802_11_build_ap_params')
+
+    hostapd.add_bss('phy3', ifname2, 'bss-2.conf')
+    hostapd.remove_bss(ifname2)
+    hostapd.remove_bss(ifname1)
+
+def test_ap_multi_bss(dev, apdev):
+    """Multiple BSSes with hostapd"""
+    ifname1 = apdev[0]['ifname']
+    ifname2 = apdev[0]['ifname'] + '-2'
+    hostapd.add_bss('phy3', ifname1, 'bss-1.conf')
+    hostapd.add_bss('phy3', ifname2, 'bss-2.conf')
+    dev[0].connect("bss-1", key_mgmt="NONE", scan_freq="2412")
+    dev[1].connect("bss-2", key_mgmt="NONE", scan_freq="2412")
+
+    hapd1 = hostapd.Hostapd(ifname1)
+    hapd2 = hostapd.Hostapd(ifname2)
+
+    hwsim_utils.test_connectivity(dev[0], hapd1)
+    hwsim_utils.test_connectivity(dev[1], hapd2)
+
+    sta0 = hapd1.get_sta(dev[0].own_addr())
+    sta1 = hapd2.get_sta(dev[1].own_addr())
+    if 'rx_packets' not in sta0 or int(sta0['rx_packets']) < 1:
+        raise Exception("sta0 did not report receiving packets")
+    if 'rx_packets' not in sta1 or int(sta1['rx_packets']) < 1:
+        raise Exception("sta1 did not report receiving packets")
+
+def test_ap_add_with_driver(dev, apdev):
+    """Add hostapd interface with driver specified"""
+    ifname = apdev[0]['ifname']
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.add(ifname, driver="nl80211")
+    hapd = hostapd.Hostapd(ifname)
+    hapd.set_defaults()
+    hapd.set("ssid", "dynamic")
+    hapd.enable()
+    ev = hapd.wait_event(["AP-ENABLED"], timeout=30)
+    if ev is None:
+        raise Exception("AP startup timed out")
+    dev[0].connect("dynamic", key_mgmt="NONE", scan_freq="2412")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    hapd.disable()
diff --git a/hostap/tests/hwsim/test_ap_eap.py b/hostap/tests/hwsim/test_ap_eap.py
new file mode 100644
index 0000000..f7d910e
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_eap.py
@@ -0,0 +1,3833 @@
+# -*- coding: utf-8 -*-
+# WPA2-Enterprise tests
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import base64
+import binascii
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+import os
+
+import hwsim_utils
+import hostapd
+from utils import HwsimSkip, alloc_fail, fail_test, skip_with_fips
+from wpasupplicant import WpaSupplicant
+from test_ap_psk import check_mib, find_wpas_process, read_process_memory, verify_not_present, get_key_locations
+
+def check_hlr_auc_gw_support():
+    if not os.path.exists("/tmp/hlr_auc_gw.sock"):
+        raise HwsimSkip("No hlr_auc_gw available")
+
+def check_eap_capa(dev, method):
+    res = dev.get_capability("eap")
+    if method not in res:
+        raise HwsimSkip("EAP method %s not supported in the build" % method)
+
+def check_subject_match_support(dev):
+    tls = dev.request("GET tls_library")
+    if not tls.startswith("OpenSSL"):
+        raise HwsimSkip("subject_match not supported with this TLS library: " + tls)
+
+def check_altsubject_match_support(dev):
+    tls = dev.request("GET tls_library")
+    if not tls.startswith("OpenSSL"):
+        raise HwsimSkip("altsubject_match not supported with this TLS library: " + tls)
+
+def check_domain_match_full(dev):
+    tls = dev.request("GET tls_library")
+    if not tls.startswith("OpenSSL"):
+        raise HwsimSkip("domain_suffix_match requires full match with this TLS library: " + tls)
+
+def check_cert_probe_support(dev):
+    tls = dev.request("GET tls_library")
+    if not tls.startswith("OpenSSL"):
+        raise HwsimSkip("Certificate probing not supported with this TLS library: " + tls)
+
+def check_ocsp_support(dev):
+    tls = dev.request("GET tls_library")
+    if "BoringSSL" in tls:
+        raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
+
+def read_pem(fname):
+    with open(fname, "r") as f:
+        lines = f.readlines()
+        copy = False
+        cert = ""
+        for l in lines:
+            if "-----END" in l:
+                break
+            if copy:
+                cert = cert + l
+            if "-----BEGIN" in l:
+                copy = True
+    return base64.b64decode(cert)
+
+def eap_connect(dev, ap, method, identity,
+                sha256=False, expect_failure=False, local_error_report=False,
+                maybe_local_error=False, **kwargs):
+    hapd = hostapd.Hostapd(ap['ifname'])
+    id = dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+                     eap=method, identity=identity,
+                     wait_connect=False, scan_freq="2412", ieee80211w="1",
+                     **kwargs)
+    eap_check_auth(dev, method, True, sha256=sha256,
+                   expect_failure=expect_failure,
+                   local_error_report=local_error_report,
+                   maybe_local_error=maybe_local_error)
+    if expect_failure:
+        return id
+    ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
+    if ev is None:
+        raise Exception("No connection event received from hostapd")
+    return id
+
+def eap_check_auth(dev, method, initial, rsn=True, sha256=False,
+                   expect_failure=False, local_error_report=False,
+                   maybe_local_error=False):
+    ev = dev.wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+    if ev is None:
+        raise Exception("Association and EAP start timed out")
+    ev = dev.wait_event(["CTRL-EVENT-EAP-METHOD",
+                         "CTRL-EVENT-EAP-FAILURE"], timeout=10)
+    if ev is None:
+        raise Exception("EAP method selection timed out")
+    if "CTRL-EVENT-EAP-FAILURE" in ev:
+        if maybe_local_error:
+            return
+        raise Exception("Could not select EAP method")
+    if method not in ev:
+        raise Exception("Unexpected EAP method")
+    if expect_failure:
+        ev = dev.wait_event(["CTRL-EVENT-EAP-FAILURE"])
+        if ev is None:
+            raise Exception("EAP failure timed out")
+        ev = dev.wait_disconnected(timeout=10)
+        if maybe_local_error and "locally_generated=1" in ev:
+            return
+        if not local_error_report:
+            if "reason=23" not in ev:
+                raise Exception("Proper reason code for disconnection not reported")
+        return
+    ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+
+    if initial:
+        ev = dev.wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
+    else:
+        ev = dev.wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("Association with the AP timed out")
+    status = dev.get_status()
+    if status["wpa_state"] != "COMPLETED":
+        raise Exception("Connection not completed")
+
+    if status["suppPortStatus"] != "Authorized":
+        raise Exception("Port not authorized")
+    if method not in status["selectedMethod"]:
+        raise Exception("Incorrect EAP method status")
+    if sha256:
+        e = "WPA2-EAP-SHA256"
+    elif rsn:
+        e = "WPA2/IEEE 802.1X/EAP"
+    else:
+        e = "WPA/IEEE 802.1X/EAP"
+    if status["key_mgmt"] != e:
+        raise Exception("Unexpected key_mgmt status: " + status["key_mgmt"])
+    return status
+
+def eap_reauth(dev, method, rsn=True, sha256=False, expect_failure=False):
+    dev.request("REAUTHENTICATE")
+    return eap_check_auth(dev, method, False, rsn=rsn, sha256=sha256,
+                          expect_failure=expect_failure)
+
+def test_ap_wpa2_eap_sim(dev, apdev):
+    """WPA2-Enterprise connection using EAP-SIM"""
+    check_hlr_auc_gw_support()
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "SIM", "1232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "SIM")
+
+    eap_connect(dev[1], apdev[0], "SIM", "1232010000000001",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+    eap_connect(dev[2], apdev[0], "SIM", "1232010000000002",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+                expect_failure=True)
+
+    logger.info("Negative test with incorrect key")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "SIM", "1232010000000000",
+                password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+                expect_failure=True)
+
+    logger.info("Invalid GSM-Milenage key")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "SIM", "1232010000000000",
+                password="ffdca4eda45b53cf0f12d7c9c3bc6a",
+                expect_failure=True)
+
+    logger.info("Invalid GSM-Milenage key(2)")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "SIM", "1232010000000000",
+                password="ffdca4eda45b53cf0f12d7c9c3bc6a8q:cb9cccc4b9258e6dca4760379fb82581",
+                expect_failure=True)
+
+    logger.info("Invalid GSM-Milenage key(3)")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "SIM", "1232010000000000",
+                password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb8258q",
+                expect_failure=True)
+
+    logger.info("Invalid GSM-Milenage key(4)")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "SIM", "1232010000000000",
+                password="ffdca4eda45b53cf0f12d7c9c3bc6a89qcb9cccc4b9258e6dca4760379fb82581",
+                expect_failure=True)
+
+    logger.info("Missing key configuration")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "SIM", "1232010000000000",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_sim_sql(dev, apdev, params):
+    """WPA2-Enterprise connection using EAP-SIM (SQL)"""
+    check_hlr_auc_gw_support()
+    try:
+        import sqlite3
+    except ImportError:
+        raise HwsimSkip("No sqlite3 module available")
+    con = sqlite3.connect(os.path.join(params['logdir'], "hostapd.db"))
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params['auth_server_port'] = "1814"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "SIM", "1232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+
+    logger.info("SIM fast re-authentication")
+    eap_reauth(dev[0], "SIM")
+
+    logger.info("SIM full auth with pseudonym")
+    with con:
+        cur = con.cursor()
+        cur.execute("DELETE FROM reauth WHERE permanent='1232010000000000'")
+    eap_reauth(dev[0], "SIM")
+
+    logger.info("SIM full auth with permanent identity")
+    with con:
+        cur = con.cursor()
+        cur.execute("DELETE FROM reauth WHERE permanent='1232010000000000'")
+        cur.execute("DELETE FROM pseudonyms WHERE permanent='1232010000000000'")
+    eap_reauth(dev[0], "SIM")
+
+    logger.info("SIM reauth with mismatching MK")
+    with con:
+        cur = con.cursor()
+        cur.execute("UPDATE reauth SET mk='0000000000000000000000000000000000000000' WHERE permanent='1232010000000000'")
+    eap_reauth(dev[0], "SIM", expect_failure=True)
+    dev[0].request("REMOVE_NETWORK all")
+
+    eap_connect(dev[0], apdev[0], "SIM", "1232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+    with con:
+        cur = con.cursor()
+        cur.execute("UPDATE reauth SET counter='10' WHERE permanent='1232010000000000'")
+    eap_reauth(dev[0], "SIM")
+    with con:
+        cur = con.cursor()
+        cur.execute("UPDATE reauth SET counter='10' WHERE permanent='1232010000000000'")
+    logger.info("SIM reauth with mismatching counter")
+    eap_reauth(dev[0], "SIM")
+    dev[0].request("REMOVE_NETWORK all")
+
+    eap_connect(dev[0], apdev[0], "SIM", "1232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+    with con:
+        cur = con.cursor()
+        cur.execute("UPDATE reauth SET counter='1001' WHERE permanent='1232010000000000'")
+    logger.info("SIM reauth with max reauth count reached")
+    eap_reauth(dev[0], "SIM")
+
+def test_ap_wpa2_eap_sim_config(dev, apdev):
+    """EAP-SIM configuration options"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="SIM",
+                   identity="1232010000000000",
+                   password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+                   phase1="sim_min_num_chal=1",
+                   wait_connect=False, scan_freq="2412")
+    ev = dev[0].wait_event(["EAP: Failed to initialize EAP method: vendor 0 method 18 (SIM)"], timeout=10)
+    if ev is None:
+        raise Exception("No EAP error message seen")
+    dev[0].request("REMOVE_NETWORK all")
+
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="SIM",
+                   identity="1232010000000000",
+                   password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+                   phase1="sim_min_num_chal=4",
+                   wait_connect=False, scan_freq="2412")
+    ev = dev[0].wait_event(["EAP: Failed to initialize EAP method: vendor 0 method 18 (SIM)"], timeout=10)
+    if ev is None:
+        raise Exception("No EAP error message seen (2)")
+    dev[0].request("REMOVE_NETWORK all")
+
+    eap_connect(dev[0], apdev[0], "SIM", "1232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+                phase1="sim_min_num_chal=2")
+    eap_connect(dev[1], apdev[0], "SIM", "1232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+                anonymous_identity="345678")
+
+def test_ap_wpa2_eap_sim_ext(dev, apdev):
+    """WPA2-Enterprise connection using EAP-SIM and external GSM auth"""
+    try:
+        _test_ap_wpa2_eap_sim_ext(dev, apdev)
+    finally:
+        dev[0].request("SET external_sim 0")
+
+def _test_ap_wpa2_eap_sim_ext(dev, apdev):
+    check_hlr_auc_gw_support()
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].request("SET external_sim 1")
+    id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
+                        identity="1232010000000000",
+                        wait_connect=False, scan_freq="2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
+    if ev is None:
+        raise Exception("Network connected timed out")
+
+    ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+    if ev is None:
+        raise Exception("Wait for external SIM processing request timed out")
+    p = ev.split(':', 2)
+    if p[1] != "GSM-AUTH":
+        raise Exception("Unexpected CTRL-REQ-SIM type")
+    rid = p[0].split('-')[3]
+
+    # IK:CK:RES
+    resp = "00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:0011223344"
+    # This will fail during processing, but the ctrl_iface command succeeds
+    dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-AUTH:" + resp)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+    if ev is None:
+        raise Exception("EAP failure not reported")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    time.sleep(0.1)
+
+    dev[0].select_network(id, freq="2412")
+    ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+    if ev is None:
+        raise Exception("Wait for external SIM processing request timed out")
+    p = ev.split(':', 2)
+    if p[1] != "GSM-AUTH":
+        raise Exception("Unexpected CTRL-REQ-SIM type")
+    rid = p[0].split('-')[3]
+    # This will fail during GSM auth validation
+    if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:q"):
+        raise Exception("CTRL-RSP-SIM failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+    if ev is None:
+        raise Exception("EAP failure not reported")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    time.sleep(0.1)
+
+    dev[0].select_network(id, freq="2412")
+    ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+    if ev is None:
+        raise Exception("Wait for external SIM processing request timed out")
+    p = ev.split(':', 2)
+    if p[1] != "GSM-AUTH":
+        raise Exception("Unexpected CTRL-REQ-SIM type")
+    rid = p[0].split('-')[3]
+    # This will fail during GSM auth validation
+    if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:34"):
+        raise Exception("CTRL-RSP-SIM failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+    if ev is None:
+        raise Exception("EAP failure not reported")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    time.sleep(0.1)
+
+    dev[0].select_network(id, freq="2412")
+    ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+    if ev is None:
+        raise Exception("Wait for external SIM processing request timed out")
+    p = ev.split(':', 2)
+    if p[1] != "GSM-AUTH":
+        raise Exception("Unexpected CTRL-REQ-SIM type")
+    rid = p[0].split('-')[3]
+    # This will fail during GSM auth validation
+    if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:0011223344556677"):
+        raise Exception("CTRL-RSP-SIM failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+    if ev is None:
+        raise Exception("EAP failure not reported")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    time.sleep(0.1)
+
+    dev[0].select_network(id, freq="2412")
+    ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+    if ev is None:
+        raise Exception("Wait for external SIM processing request timed out")
+    p = ev.split(':', 2)
+    if p[1] != "GSM-AUTH":
+        raise Exception("Unexpected CTRL-REQ-SIM type")
+    rid = p[0].split('-')[3]
+    # This will fail during GSM auth validation
+    if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:0011223344556677:q"):
+        raise Exception("CTRL-RSP-SIM failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+    if ev is None:
+        raise Exception("EAP failure not reported")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    time.sleep(0.1)
+
+    dev[0].select_network(id, freq="2412")
+    ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+    if ev is None:
+        raise Exception("Wait for external SIM processing request timed out")
+    p = ev.split(':', 2)
+    if p[1] != "GSM-AUTH":
+        raise Exception("Unexpected CTRL-REQ-SIM type")
+    rid = p[0].split('-')[3]
+    # This will fail during GSM auth validation
+    if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:0011223344556677:00112233"):
+        raise Exception("CTRL-RSP-SIM failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+    if ev is None:
+        raise Exception("EAP failure not reported")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    time.sleep(0.1)
+
+    dev[0].select_network(id, freq="2412")
+    ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+    if ev is None:
+        raise Exception("Wait for external SIM processing request timed out")
+    p = ev.split(':', 2)
+    if p[1] != "GSM-AUTH":
+        raise Exception("Unexpected CTRL-REQ-SIM type")
+    rid = p[0].split('-')[3]
+    # This will fail during GSM auth validation
+    if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:0011223344556677:00112233:q"):
+        raise Exception("CTRL-RSP-SIM failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+    if ev is None:
+        raise Exception("EAP failure not reported")
+
+def test_ap_wpa2_eap_sim_oom(dev, apdev):
+    """EAP-SIM and OOM"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    tests = [ (1, "milenage_f2345"),
+              (2, "milenage_f2345"),
+              (3, "milenage_f2345"),
+              (4, "milenage_f2345"),
+              (5, "milenage_f2345"),
+              (6, "milenage_f2345"),
+              (7, "milenage_f2345"),
+              (8, "milenage_f2345"),
+              (9, "milenage_f2345"),
+              (10, "milenage_f2345"),
+              (11, "milenage_f2345"),
+              (12, "milenage_f2345") ]
+    for count, func in tests:
+        with alloc_fail(dev[0], count, func):
+            dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="SIM",
+                           identity="1232010000000000",
+                           password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+                           wait_connect=False, scan_freq="2412")
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+            if ev is None:
+                raise Exception("EAP method not selected")
+            dev[0].wait_disconnected()
+            dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_aka(dev, apdev):
+    """WPA2-Enterprise connection using EAP-AKA"""
+    check_hlr_auc_gw_support()
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "AKA", "0232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "AKA")
+
+    logger.info("Negative test with incorrect key")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "AKA", "0232010000000000",
+                password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+                expect_failure=True)
+
+    logger.info("Invalid Milenage key")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "AKA", "0232010000000000",
+                password="ffdca4eda45b53cf0f12d7c9c3bc6a",
+                expect_failure=True)
+
+    logger.info("Invalid Milenage key(2)")
+    eap_connect(dev[0], apdev[0], "AKA", "0232010000000000",
+                password="ffdca4eda45b53cf0f12d7c9c3bc6a8q:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+                expect_failure=True)
+
+    logger.info("Invalid Milenage key(3)")
+    eap_connect(dev[0], apdev[0], "AKA", "0232010000000000",
+                password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb8258q:000000000123",
+                expect_failure=True)
+
+    logger.info("Invalid Milenage key(4)")
+    eap_connect(dev[0], apdev[0], "AKA", "0232010000000000",
+                password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:00000000012q",
+                expect_failure=True)
+
+    logger.info("Invalid Milenage key(5)")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "AKA", "0232010000000000",
+                password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581q000000000123",
+                expect_failure=True)
+
+    logger.info("Invalid Milenage key(6)")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "AKA", "0232010000000000",
+                password="ffdca4eda45b53cf0f12d7c9c3bc6a89qcb9cccc4b9258e6dca4760379fb82581q000000000123",
+                expect_failure=True)
+
+    logger.info("Missing key configuration")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "AKA", "0232010000000000",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_aka_sql(dev, apdev, params):
+    """WPA2-Enterprise connection using EAP-AKA (SQL)"""
+    check_hlr_auc_gw_support()
+    try:
+        import sqlite3
+    except ImportError:
+        raise HwsimSkip("No sqlite3 module available")
+    con = sqlite3.connect(os.path.join(params['logdir'], "hostapd.db"))
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params['auth_server_port'] = "1814"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "AKA", "0232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+
+    logger.info("AKA fast re-authentication")
+    eap_reauth(dev[0], "AKA")
+
+    logger.info("AKA full auth with pseudonym")
+    with con:
+        cur = con.cursor()
+        cur.execute("DELETE FROM reauth WHERE permanent='0232010000000000'")
+    eap_reauth(dev[0], "AKA")
+
+    logger.info("AKA full auth with permanent identity")
+    with con:
+        cur = con.cursor()
+        cur.execute("DELETE FROM reauth WHERE permanent='0232010000000000'")
+        cur.execute("DELETE FROM pseudonyms WHERE permanent='0232010000000000'")
+    eap_reauth(dev[0], "AKA")
+
+    logger.info("AKA reauth with mismatching MK")
+    with con:
+        cur = con.cursor()
+        cur.execute("UPDATE reauth SET mk='0000000000000000000000000000000000000000' WHERE permanent='0232010000000000'")
+    eap_reauth(dev[0], "AKA", expect_failure=True)
+    dev[0].request("REMOVE_NETWORK all")
+
+    eap_connect(dev[0], apdev[0], "AKA", "0232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+    with con:
+        cur = con.cursor()
+        cur.execute("UPDATE reauth SET counter='10' WHERE permanent='0232010000000000'")
+    eap_reauth(dev[0], "AKA")
+    with con:
+        cur = con.cursor()
+        cur.execute("UPDATE reauth SET counter='10' WHERE permanent='0232010000000000'")
+    logger.info("AKA reauth with mismatching counter")
+    eap_reauth(dev[0], "AKA")
+    dev[0].request("REMOVE_NETWORK all")
+
+    eap_connect(dev[0], apdev[0], "AKA", "0232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+    with con:
+        cur = con.cursor()
+        cur.execute("UPDATE reauth SET counter='1001' WHERE permanent='0232010000000000'")
+    logger.info("AKA reauth with max reauth count reached")
+    eap_reauth(dev[0], "AKA")
+
+def test_ap_wpa2_eap_aka_config(dev, apdev):
+    """EAP-AKA configuration options"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "AKA", "0232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+                anonymous_identity="2345678")
+
+def test_ap_wpa2_eap_aka_ext(dev, apdev):
+    """WPA2-Enterprise connection using EAP-AKA and external UMTS auth"""
+    try:
+        _test_ap_wpa2_eap_aka_ext(dev, apdev)
+    finally:
+        dev[0].request("SET external_sim 0")
+
+def _test_ap_wpa2_eap_aka_ext(dev, apdev):
+    check_hlr_auc_gw_support()
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].request("SET external_sim 1")
+    id = dev[0].connect("test-wpa2-eap", eap="AKA", key_mgmt="WPA-EAP",
+                        identity="0232010000000000",
+                        password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+                        wait_connect=False, scan_freq="2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
+    if ev is None:
+        raise Exception("Network connected timed out")
+
+    ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+    if ev is None:
+        raise Exception("Wait for external SIM processing request timed out")
+    p = ev.split(':', 2)
+    if p[1] != "UMTS-AUTH":
+        raise Exception("Unexpected CTRL-REQ-SIM type")
+    rid = p[0].split('-')[3]
+
+    # IK:CK:RES
+    resp = "00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:0011223344"
+    # This will fail during processing, but the ctrl_iface command succeeds
+    dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+    if ev is None:
+        raise Exception("EAP failure not reported")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    time.sleep(0.1)
+
+    dev[0].select_network(id, freq="2412")
+    ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+    if ev is None:
+        raise Exception("Wait for external SIM processing request timed out")
+    p = ev.split(':', 2)
+    if p[1] != "UMTS-AUTH":
+        raise Exception("Unexpected CTRL-REQ-SIM type")
+    rid = p[0].split('-')[3]
+    # This will fail during UMTS auth validation
+    if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-AUTS:112233445566778899aabbccddee"):
+        raise Exception("CTRL-RSP-SIM failed")
+    ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+    if ev is None:
+        raise Exception("Wait for external SIM processing request timed out")
+    p = ev.split(':', 2)
+    if p[1] != "UMTS-AUTH":
+        raise Exception("Unexpected CTRL-REQ-SIM type")
+    rid = p[0].split('-')[3]
+    # This will fail during UMTS auth validation
+    if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-AUTS:12"):
+        raise Exception("CTRL-RSP-SIM failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+    if ev is None:
+        raise Exception("EAP failure not reported")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    time.sleep(0.1)
+
+    tests = [ ":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:0011223344",
+              ":UMTS-AUTH:34",
+              ":UMTS-AUTH:00112233445566778899aabbccddeeff.00112233445566778899aabbccddeeff:0011223344",
+              ":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddee:0011223344",
+              ":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff.0011223344",
+              ":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff0011223344",
+              ":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:001122334q" ]
+    for t in tests:
+        dev[0].select_network(id, freq="2412")
+        ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+        if ev is None:
+            raise Exception("Wait for external SIM processing request timed out")
+        p = ev.split(':', 2)
+        if p[1] != "UMTS-AUTH":
+            raise Exception("Unexpected CTRL-REQ-SIM type")
+        rid = p[0].split('-')[3]
+        # This will fail during UMTS auth validation
+        if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + t):
+            raise Exception("CTRL-RSP-SIM failed")
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+        if ev is None:
+            raise Exception("EAP failure not reported")
+        dev[0].request("DISCONNECT")
+        dev[0].wait_disconnected()
+        time.sleep(0.1)
+
+def test_ap_wpa2_eap_aka_prime(dev, apdev):
+    """WPA2-Enterprise connection using EAP-AKA'"""
+    check_hlr_auc_gw_support()
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "AKA'", "6555444333222111",
+                password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "AKA'")
+
+    logger.info("EAP-AKA' bidding protection when EAP-AKA enabled as well")
+    dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="AKA' AKA",
+                   identity="6555444333222111@both",
+                   password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
+                   wait_connect=False, scan_freq="2412")
+    dev[1].wait_connected(timeout=15)
+
+    logger.info("Negative test with incorrect key")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "AKA'", "6555444333222111",
+                password="ff22250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_aka_prime_sql(dev, apdev, params):
+    """WPA2-Enterprise connection using EAP-AKA' (SQL)"""
+    check_hlr_auc_gw_support()
+    try:
+        import sqlite3
+    except ImportError:
+        raise HwsimSkip("No sqlite3 module available")
+    con = sqlite3.connect(os.path.join(params['logdir'], "hostapd.db"))
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params['auth_server_port'] = "1814"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "AKA'", "6555444333222111",
+                password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+
+    logger.info("AKA' fast re-authentication")
+    eap_reauth(dev[0], "AKA'")
+
+    logger.info("AKA' full auth with pseudonym")
+    with con:
+        cur = con.cursor()
+        cur.execute("DELETE FROM reauth WHERE permanent='6555444333222111'")
+    eap_reauth(dev[0], "AKA'")
+
+    logger.info("AKA' full auth with permanent identity")
+    with con:
+        cur = con.cursor()
+        cur.execute("DELETE FROM reauth WHERE permanent='6555444333222111'")
+        cur.execute("DELETE FROM pseudonyms WHERE permanent='6555444333222111'")
+    eap_reauth(dev[0], "AKA'")
+
+    logger.info("AKA' reauth with mismatching k_aut")
+    with con:
+        cur = con.cursor()
+        cur.execute("UPDATE reauth SET k_aut='0000000000000000000000000000000000000000000000000000000000000000' WHERE permanent='6555444333222111'")
+    eap_reauth(dev[0], "AKA'", expect_failure=True)
+    dev[0].request("REMOVE_NETWORK all")
+
+    eap_connect(dev[0], apdev[0], "AKA'", "6555444333222111",
+                password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+    with con:
+        cur = con.cursor()
+        cur.execute("UPDATE reauth SET counter='10' WHERE permanent='6555444333222111'")
+    eap_reauth(dev[0], "AKA'")
+    with con:
+        cur = con.cursor()
+        cur.execute("UPDATE reauth SET counter='10' WHERE permanent='6555444333222111'")
+    logger.info("AKA' reauth with mismatching counter")
+    eap_reauth(dev[0], "AKA'")
+    dev[0].request("REMOVE_NETWORK all")
+
+    eap_connect(dev[0], apdev[0], "AKA'", "6555444333222111",
+                password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+    with con:
+        cur = con.cursor()
+        cur.execute("UPDATE reauth SET counter='1001' WHERE permanent='6555444333222111'")
+    logger.info("AKA' reauth with max reauth count reached")
+    eap_reauth(dev[0], "AKA'")
+
+def test_ap_wpa2_eap_ttls_pap(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/PAP"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    key_mgmt = hapd.get_config()['key_mgmt']
+    if key_mgmt.split(' ')[0] != "WPA-EAP":
+        raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "TTLS")
+    check_mib(dev[0], [ ("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-1"),
+                        ("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-1") ])
+
+def test_ap_wpa2_eap_ttls_pap_subject_match(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/PAP and (alt)subject_match"""
+    check_subject_match_support(dev[0])
+    check_altsubject_match_support(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+                subject_match="/C=FI/O=w1.fi/CN=server.w1.fi",
+                altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/")
+    eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_ttls_pap_incorrect_password(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/PAP - incorrect password"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="wrong",
+                ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+                expect_failure=True)
+    eap_connect(dev[1], apdev[0], "TTLS", "user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_chap(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/CHAP"""
+    skip_with_fips(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "chap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.der", phase2="auth=CHAP")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_ttls_chap_altsubject_match(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/CHAP"""
+    skip_with_fips(dev[0])
+    check_altsubject_match_support(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "chap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.der", phase2="auth=CHAP",
+                altsubject_match="EMAIL:noone@example.com;URI:http://example.com/;DNS:server.w1.fi")
+    eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_ttls_chap_incorrect_password(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/CHAP - incorrect password"""
+    skip_with_fips(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "chap user",
+                anonymous_identity="ttls", password="wrong",
+                ca_cert="auth_serv/ca.pem", phase2="auth=CHAP",
+                expect_failure=True)
+    eap_connect(dev[1], apdev[0], "TTLS", "user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=CHAP",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_mschap(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/MSCHAP"""
+    skip_with_fips(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "mschap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                domain_suffix_match="server.w1.fi")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "TTLS")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "TTLS", "mschap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                fragment_size="200")
+
+def test_ap_wpa2_eap_ttls_mschap_incorrect_password(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/MSCHAP - incorrect password"""
+    skip_with_fips(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "mschap user",
+                anonymous_identity="ttls", password="wrong",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                expect_failure=True)
+    eap_connect(dev[1], apdev[0], "TTLS", "user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                expect_failure=True)
+    eap_connect(dev[2], apdev[0], "TTLS", "no such user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_mschapv2(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    eap_connect(dev[0], apdev[0], "TTLS", "DOMAIN\mschapv2 user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                domain_suffix_match="server.w1.fi")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    sta1 = hapd.get_sta(dev[0].p2p_interface_addr())
+    eapol1 = hapd.get_sta(dev[0].p2p_interface_addr(), info="eapol")
+    eap_reauth(dev[0], "TTLS")
+    sta2 = hapd.get_sta(dev[0].p2p_interface_addr())
+    eapol2 = hapd.get_sta(dev[0].p2p_interface_addr(), info="eapol")
+    if int(sta2['dot1xAuthEapolFramesRx']) <= int(sta1['dot1xAuthEapolFramesRx']):
+        raise Exception("dot1xAuthEapolFramesRx did not increase")
+    if int(eapol2['authAuthEapStartsWhileAuthenticated']) < 1:
+        raise Exception("authAuthEapStartsWhileAuthenticated did not increase")
+    if int(eapol2['backendAuthSuccesses']) <= int(eapol1['backendAuthSuccesses']):
+        raise Exception("backendAuthSuccesses did not increase")
+
+    logger.info("Password as hash value")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "TTLS", "DOMAIN\mschapv2 user",
+                anonymous_identity="ttls",
+                password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+
+def test_ap_wpa2_eap_ttls_mschapv2_suffix_match(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2"""
+    check_domain_match_full(dev[0])
+    skip_with_fips(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    eap_connect(dev[0], apdev[0], "TTLS", "DOMAIN\mschapv2 user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                domain_suffix_match="w1.fi")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_ttls_mschapv2_domain_match(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2 (domain_match)"""
+    skip_with_fips(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    eap_connect(dev[0], apdev[0], "TTLS", "DOMAIN\mschapv2 user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                domain_match="Server.w1.fi")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_ttls_mschapv2_incorrect_password(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2 - incorrect password"""
+    skip_with_fips(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "DOMAIN\mschapv2 user",
+                anonymous_identity="ttls", password="password1",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                expect_failure=True)
+    eap_connect(dev[1], apdev[0], "TTLS", "user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_mschapv2_utf8(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2 and UTF-8 password"""
+    skip_with_fips(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    eap_connect(dev[0], apdev[0], "TTLS", "utf8-user-hash",
+                anonymous_identity="ttls", password="secret-åäö-€-password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+    eap_connect(dev[1], apdev[0], "TTLS", "utf8-user",
+                anonymous_identity="ttls",
+                password_hex="hash:bd5844fad2489992da7fe8c5a01559cf",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+
+def test_ap_wpa2_eap_ttls_eap_gtc(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/EAP-GTC"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="autheap=GTC")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_ttls_eap_gtc_incorrect_password(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/EAP-GTC - incorrect password"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "user",
+                anonymous_identity="ttls", password="wrong",
+                ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_eap_gtc_no_password(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/EAP-GTC - no password"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "user-no-passwd",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_eap_gtc_server_oom(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/EAP-GTC - server OOM"""
+    params = int_eap_server_params()
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    with alloc_fail(hapd, 1, "eap_gtc_init"):
+        eap_connect(dev[0], apdev[0], "TTLS", "user",
+                    anonymous_identity="ttls", password="password",
+                    ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+                    expect_failure=True)
+        dev[0].request("REMOVE_NETWORK all")
+
+    with alloc_fail(hapd, 1, "eap_gtc_buildReq"):
+        dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+                       eap="TTLS", identity="user",
+                       anonymous_identity="ttls", password="password",
+                       ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+                       wait_connect=False, scan_freq="2412")
+        # This would eventually time out, but we can stop after having reached
+        # the allocation failure.
+        for i in range(20):
+            time.sleep(0.1)
+            if hapd.request("GET_ALLOC_FAIL").startswith('0'):
+                break
+
+def test_ap_wpa2_eap_ttls_eap_md5(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/EAP-MD5"""
+    check_eap_capa(dev[0], "MD5")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="autheap=MD5")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_ttls_eap_md5_incorrect_password(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/EAP-MD5 - incorrect password"""
+    check_eap_capa(dev[0], "MD5")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "user",
+                anonymous_identity="ttls", password="wrong",
+                ca_cert="auth_serv/ca.pem", phase2="autheap=MD5",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_eap_md5_no_password(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/EAP-MD5 - no password"""
+    check_eap_capa(dev[0], "MD5")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "user-no-passwd",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="autheap=MD5",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_eap_md5_server_oom(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/EAP-MD5 - server OOM"""
+    check_eap_capa(dev[0], "MD5")
+    params = int_eap_server_params()
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    with alloc_fail(hapd, 1, "eap_md5_init"):
+        eap_connect(dev[0], apdev[0], "TTLS", "user",
+                    anonymous_identity="ttls", password="password",
+                    ca_cert="auth_serv/ca.pem", phase2="autheap=MD5",
+                    expect_failure=True)
+        dev[0].request("REMOVE_NETWORK all")
+
+    with alloc_fail(hapd, 1, "eap_md5_buildReq"):
+        dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+                       eap="TTLS", identity="user",
+                       anonymous_identity="ttls", password="password",
+                       ca_cert="auth_serv/ca.pem", phase2="autheap=MD5",
+                       wait_connect=False, scan_freq="2412")
+        # This would eventually time out, but we can stop after having reached
+        # the allocation failure.
+        for i in range(20):
+            time.sleep(0.1)
+            if hapd.request("GET_ALLOC_FAIL").startswith('0'):
+                break
+
+def test_ap_wpa2_eap_ttls_eap_mschapv2(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/EAP-MSCHAPv2"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "TTLS")
+
+    logger.info("Negative test with incorrect password")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "TTLS", "user",
+                anonymous_identity="ttls", password="password1",
+                ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_eap_mschapv2_no_password(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/EAP-MSCHAPv2 - no password"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "user-no-passwd",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_eap_mschapv2_server_oom(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/EAP-MSCHAPv2 - server OOM"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = int_eap_server_params()
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    with alloc_fail(hapd, 1, "eap_mschapv2_init"):
+        eap_connect(dev[0], apdev[0], "TTLS", "user",
+                    anonymous_identity="ttls", password="password",
+                    ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
+                    expect_failure=True)
+        dev[0].request("REMOVE_NETWORK all")
+
+    with alloc_fail(hapd, 1, "eap_mschapv2_build_challenge"):
+        dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+                       eap="TTLS", identity="user",
+                       anonymous_identity="ttls", password="password",
+                       ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
+                       wait_connect=False, scan_freq="2412")
+        # This would eventually time out, but we can stop after having reached
+        # the allocation failure.
+        for i in range(20):
+            time.sleep(0.1)
+            if hapd.request("GET_ALLOC_FAIL").startswith('0'):
+                break
+        dev[0].request("REMOVE_NETWORK all")
+
+    with alloc_fail(hapd, 1, "eap_mschapv2_build_success_req"):
+        dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+                       eap="TTLS", identity="user",
+                       anonymous_identity="ttls", password="password",
+                       ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
+                       wait_connect=False, scan_freq="2412")
+        # This would eventually time out, but we can stop after having reached
+        # the allocation failure.
+        for i in range(20):
+            time.sleep(0.1)
+            if hapd.request("GET_ALLOC_FAIL").startswith('0'):
+                break
+        dev[0].request("REMOVE_NETWORK all")
+
+    with alloc_fail(hapd, 1, "eap_mschapv2_build_failure_req"):
+        dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+                       eap="TTLS", identity="user",
+                       anonymous_identity="ttls", password="wrong",
+                       ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
+                       wait_connect=False, scan_freq="2412")
+        # This would eventually time out, but we can stop after having reached
+        # the allocation failure.
+        for i in range(20):
+            time.sleep(0.1)
+            if hapd.request("GET_ALLOC_FAIL").startswith('0'):
+                break
+        dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_ttls_eap_aka(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/EAP-AKA"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "0232010000000000",
+                anonymous_identity="0232010000000000@ttls",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+                ca_cert="auth_serv/ca.pem", phase2="autheap=AKA")
+
+def test_ap_wpa2_eap_peap_eap_aka(dev, apdev):
+    """WPA2-Enterprise connection using EAP-PEAP/EAP-AKA"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PEAP", "0232010000000000",
+                anonymous_identity="0232010000000000@peap",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+                ca_cert="auth_serv/ca.pem", phase2="auth=AKA")
+
+def test_ap_wpa2_eap_fast_eap_aka(dev, apdev):
+    """WPA2-Enterprise connection using EAP-FAST/EAP-AKA"""
+    check_eap_capa(dev[0], "FAST")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "FAST", "0232010000000000",
+                anonymous_identity="0232010000000000@fast",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+                phase1="fast_provisioning=2",
+                pac_file="blob://fast_pac_auth_aka",
+                ca_cert="auth_serv/ca.pem", phase2="auth=AKA")
+
+def test_ap_wpa2_eap_peap_eap_mschapv2(dev, apdev):
+    """WPA2-Enterprise connection using EAP-PEAP/EAP-MSCHAPv2"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PEAP", "user",
+                anonymous_identity="peap", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "PEAP")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "PEAP", "user",
+                anonymous_identity="peap", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                fragment_size="200")
+
+    logger.info("Password as hash value")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "PEAP", "user",
+                anonymous_identity="peap",
+                password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+
+    logger.info("Negative test with incorrect password")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "PEAP", "user",
+                anonymous_identity="peap", password="password1",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_peap_eap_mschapv2_domain(dev, apdev):
+    """WPA2-Enterprise connection using EAP-PEAP/EAP-MSCHAPv2 with domain"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PEAP", "DOMAIN\user3",
+                anonymous_identity="peap", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "PEAP")
+
+def test_ap_wpa2_eap_peap_eap_mschapv2_incorrect_password(dev, apdev):
+    """WPA2-Enterprise connection using EAP-PEAP/EAP-MSCHAPv2 - incorrect password"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PEAP", "user",
+                anonymous_identity="peap", password="wrong",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_peap_crypto_binding(dev, apdev):
+    """WPA2-Enterprise connection using EAP-PEAPv0/EAP-MSCHAPv2 and crypto binding"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PEAP", "user", password="password",
+                ca_cert="auth_serv/ca.pem",
+                phase1="peapver=0 crypto_binding=2",
+                phase2="auth=MSCHAPV2")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "PEAP")
+
+    eap_connect(dev[1], apdev[0], "PEAP", "user", password="password",
+                ca_cert="auth_serv/ca.pem",
+                phase1="peapver=0 crypto_binding=1",
+                phase2="auth=MSCHAPV2")
+    eap_connect(dev[2], apdev[0], "PEAP", "user", password="password",
+                ca_cert="auth_serv/ca.pem",
+                phase1="peapver=0 crypto_binding=0",
+                phase2="auth=MSCHAPV2")
+
+def test_ap_wpa2_eap_peap_crypto_binding_server_oom(dev, apdev):
+    """WPA2-Enterprise connection using EAP-PEAPv0/EAP-MSCHAPv2 and crypto binding with server OOM"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = int_eap_server_params()
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    with alloc_fail(hapd, 1, "eap_mschapv2_getKey"):
+        eap_connect(dev[0], apdev[0], "PEAP", "user", password="password",
+                    ca_cert="auth_serv/ca.pem",
+                    phase1="peapver=0 crypto_binding=2",
+                    phase2="auth=MSCHAPV2",
+                    expect_failure=True, local_error_report=True)
+
+def test_ap_wpa2_eap_peap_params(dev, apdev):
+    """WPA2-Enterprise connection using EAP-PEAPv0/EAP-MSCHAPv2 and various parameters"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PEAP", "user",
+                anonymous_identity="peap", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                phase1="peapver=0 peaplabel=1",
+                expect_failure=True)
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[1], apdev[0], "PEAP", "user", password="password",
+                ca_cert="auth_serv/ca.pem",
+                phase1="peap_outer_success=1",
+                phase2="auth=MSCHAPV2")
+    eap_connect(dev[2], apdev[0], "PEAP", "user", password="password",
+                ca_cert="auth_serv/ca.pem",
+                phase1="peap_outer_success=2",
+                phase2="auth=MSCHAPV2")
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
+                   identity="user",
+                   anonymous_identity="peap", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                   phase1="peapver=1 peaplabel=1",
+                   wait_connect=False, scan_freq="2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("No EAP success seen")
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected connection")
+
+def test_ap_wpa2_eap_peap_eap_tls(dev, apdev):
+    """WPA2-Enterprise connection using EAP-PEAP/EAP-TLS"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PEAP", "cert user",
+                ca_cert="auth_serv/ca.pem", phase2="auth=TLS",
+                ca_cert2="auth_serv/ca.pem",
+                client_cert2="auth_serv/user.pem",
+                private_key2="auth_serv/user.key")
+    eap_reauth(dev[0], "PEAP")
+
+def test_ap_wpa2_eap_tls(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TLS"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+                client_cert="auth_serv/user.pem",
+                private_key="auth_serv/user.key")
+    eap_reauth(dev[0], "TLS")
+
+def test_ap_wpa2_eap_tls_blob(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TLS and config blobs"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    cert = read_pem("auth_serv/ca.pem")
+    if "OK" not in dev[0].request("SET blob cacert " + cert.encode("hex")):
+        raise Exception("Could not set cacert blob")
+    cert = read_pem("auth_serv/user.pem")
+    if "OK" not in dev[0].request("SET blob usercert " + cert.encode("hex")):
+        raise Exception("Could not set usercert blob")
+    key = read_pem("auth_serv/user.rsa-key")
+    if "OK" not in dev[0].request("SET blob userkey " + key.encode("hex")):
+        raise Exception("Could not set cacert blob")
+    eap_connect(dev[0], apdev[0], "TLS", "tls user", ca_cert="blob://cacert",
+                client_cert="blob://usercert",
+                private_key="blob://userkey")
+
+def test_ap_wpa2_eap_tls_pkcs12(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TLS and PKCS#12"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+                private_key="auth_serv/user.pkcs12",
+                private_key_passwd="whatever")
+    dev[0].request("REMOVE_NETWORK all")
+    dev[0].wait_disconnected()
+
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                   identity="tls user",
+                   ca_cert="auth_serv/ca.pem",
+                   private_key="auth_serv/user.pkcs12",
+                   wait_connect=False, scan_freq="2412")
+    ev = dev[0].wait_event(["CTRL-REQ-PASSPHRASE"])
+    if ev is None:
+        raise Exception("Request for private key passphrase timed out")
+    id = ev.split(':')[0].split('-')[-1]
+    dev[0].request("CTRL-RSP-PASSPHRASE-" + id + ":whatever")
+    dev[0].wait_connected(timeout=10)
+    dev[0].request("REMOVE_NETWORK all")
+    dev[0].wait_disconnected()
+
+    # Run this twice to verify certificate chain handling with OpenSSL. Use two
+    # different files to cover both cases of the extra certificate being the
+    # one that signed the client certificate and it being unrelated to the
+    # client certificate.
+    for pkcs12 in "auth_serv/user2.pkcs12", "auth_serv/user3.pkcs12":
+        for i in range(2):
+            eap_connect(dev[0], apdev[0], "TLS", "tls user",
+                        ca_cert="auth_serv/ca.pem",
+                        private_key=pkcs12,
+                        private_key_passwd="whatever")
+            dev[0].request("REMOVE_NETWORK all")
+            dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_tls_pkcs12_blob(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TLS and PKCS#12 from configuration blob"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    cert = read_pem("auth_serv/ca.pem")
+    if "OK" not in dev[0].request("SET blob cacert " + cert.encode("hex")):
+        raise Exception("Could not set cacert blob")
+    with open("auth_serv/user.pkcs12", "rb") as f:
+        if "OK" not in dev[0].request("SET blob pkcs12 " + f.read().encode("hex")):
+            raise Exception("Could not set pkcs12 blob")
+    eap_connect(dev[0], apdev[0], "TLS", "tls user", ca_cert="blob://cacert",
+                private_key="blob://pkcs12",
+                private_key_passwd="whatever")
+
+def test_ap_wpa2_eap_tls_neg_incorrect_trust_root(dev, apdev):
+    """WPA2-Enterprise negative test - incorrect trust root"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    cert = read_pem("auth_serv/ca-incorrect.pem")
+    if "OK" not in dev[0].request("SET blob cacert " + cert.encode("hex")):
+        raise Exception("Could not set cacert blob")
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+                   password="password", phase2="auth=MSCHAPV2",
+                   ca_cert="blob://cacert",
+                   wait_connect=False, scan_freq="2412")
+    dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+                   password="password", phase2="auth=MSCHAPV2",
+                   ca_cert="auth_serv/ca-incorrect.pem",
+                   wait_connect=False, scan_freq="2412")
+
+    for dev in (dev[0], dev[1]):
+        ev = dev.wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+        if ev is None:
+            raise Exception("Association and EAP start timed out")
+
+        ev = dev.wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
+        if ev is None:
+            raise Exception("EAP method selection timed out")
+        if "TTLS" not in ev:
+            raise Exception("Unexpected EAP method")
+
+        ev = dev.wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
+                             "CTRL-EVENT-EAP-SUCCESS",
+                             "CTRL-EVENT-EAP-FAILURE",
+                             "CTRL-EVENT-CONNECTED",
+                             "CTRL-EVENT-DISCONNECTED"], timeout=10)
+        if ev is None:
+            raise Exception("EAP result timed out")
+        if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
+            raise Exception("TLS certificate error not reported")
+
+        ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS",
+                             "CTRL-EVENT-EAP-FAILURE",
+                             "CTRL-EVENT-CONNECTED",
+                             "CTRL-EVENT-DISCONNECTED"], timeout=10)
+        if ev is None:
+            raise Exception("EAP result(2) timed out")
+        if "CTRL-EVENT-EAP-FAILURE" not in ev:
+            raise Exception("EAP failure not reported")
+
+        ev = dev.wait_event(["CTRL-EVENT-CONNECTED",
+                             "CTRL-EVENT-DISCONNECTED"], timeout=10)
+        if ev is None:
+            raise Exception("EAP result(3) timed out")
+        if "CTRL-EVENT-DISCONNECTED" not in ev:
+            raise Exception("Disconnection not reported")
+
+        ev = dev.wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+        if ev is None:
+            raise Exception("Network block disabling not reported")
+
+def test_ap_wpa2_eap_tls_diff_ca_trust(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/PAP and different CA trust"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="pap user", anonymous_identity="ttls",
+                   password="password", phase2="auth=PAP",
+                   ca_cert="auth_serv/ca.pem",
+                   wait_connect=True, scan_freq="2412")
+    id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                        identity="pap user", anonymous_identity="ttls",
+                        password="password", phase2="auth=PAP",
+                        ca_cert="auth_serv/ca-incorrect.pem",
+                        only_add_network=True, scan_freq="2412")
+
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    dev[0].dump_monitor()
+    dev[0].select_network(id, freq="2412")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21"], timeout=15)
+    if ev is None:
+        raise Exception("EAP-TTLS not re-started")
+    
+    ev = dev[0].wait_disconnected(timeout=15)
+    if "reason=23" not in ev:
+        raise Exception("Proper reason code for disconnection not reported")
+
+def test_ap_wpa2_eap_tls_diff_ca_trust2(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/PAP and different CA trust"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="pap user", anonymous_identity="ttls",
+                   password="password", phase2="auth=PAP",
+                   wait_connect=True, scan_freq="2412")
+    id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                        identity="pap user", anonymous_identity="ttls",
+                        password="password", phase2="auth=PAP",
+                        ca_cert="auth_serv/ca-incorrect.pem",
+                        only_add_network=True, scan_freq="2412")
+
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    dev[0].dump_monitor()
+    dev[0].select_network(id, freq="2412")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21"], timeout=15)
+    if ev is None:
+        raise Exception("EAP-TTLS not re-started")
+    
+    ev = dev[0].wait_disconnected(timeout=15)
+    if "reason=23" not in ev:
+        raise Exception("Proper reason code for disconnection not reported")
+
+def test_ap_wpa2_eap_tls_diff_ca_trust3(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/PAP and different CA trust"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                        identity="pap user", anonymous_identity="ttls",
+                        password="password", phase2="auth=PAP",
+                        ca_cert="auth_serv/ca.pem",
+                        wait_connect=True, scan_freq="2412")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    dev[0].dump_monitor()
+    dev[0].set_network_quoted(id, "ca_cert", "auth_serv/ca-incorrect.pem")
+    dev[0].select_network(id, freq="2412")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21"], timeout=15)
+    if ev is None:
+        raise Exception("EAP-TTLS not re-started")
+    
+    ev = dev[0].wait_disconnected(timeout=15)
+    if "reason=23" not in ev:
+        raise Exception("Proper reason code for disconnection not reported")
+
+def test_ap_wpa2_eap_tls_neg_suffix_match(dev, apdev):
+    """WPA2-Enterprise negative test - domain suffix mismatch"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+                   password="password", phase2="auth=MSCHAPV2",
+                   ca_cert="auth_serv/ca.pem",
+                   domain_suffix_match="incorrect.example.com",
+                   wait_connect=False, scan_freq="2412")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+    if ev is None:
+        raise Exception("Association and EAP start timed out")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
+    if ev is None:
+        raise Exception("EAP method selection timed out")
+    if "TTLS" not in ev:
+        raise Exception("Unexpected EAP method")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
+                            "CTRL-EVENT-EAP-SUCCESS",
+                            "CTRL-EVENT-EAP-FAILURE",
+                            "CTRL-EVENT-CONNECTED",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("EAP result timed out")
+    if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
+        raise Exception("TLS certificate error not reported")
+    if "Domain suffix mismatch" not in ev:
+        raise Exception("Domain suffix mismatch not reported")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS",
+                            "CTRL-EVENT-EAP-FAILURE",
+                            "CTRL-EVENT-CONNECTED",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("EAP result(2) timed out")
+    if "CTRL-EVENT-EAP-FAILURE" not in ev:
+        raise Exception("EAP failure not reported")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("EAP result(3) timed out")
+    if "CTRL-EVENT-DISCONNECTED" not in ev:
+        raise Exception("Disconnection not reported")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+    if ev is None:
+        raise Exception("Network block disabling not reported")
+
+def test_ap_wpa2_eap_tls_neg_domain_match(dev, apdev):
+    """WPA2-Enterprise negative test - domain mismatch"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+                   password="password", phase2="auth=MSCHAPV2",
+                   ca_cert="auth_serv/ca.pem",
+                   domain_match="w1.fi",
+                   wait_connect=False, scan_freq="2412")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+    if ev is None:
+        raise Exception("Association and EAP start timed out")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
+    if ev is None:
+        raise Exception("EAP method selection timed out")
+    if "TTLS" not in ev:
+        raise Exception("Unexpected EAP method")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
+                            "CTRL-EVENT-EAP-SUCCESS",
+                            "CTRL-EVENT-EAP-FAILURE",
+                            "CTRL-EVENT-CONNECTED",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("EAP result timed out")
+    if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
+        raise Exception("TLS certificate error not reported")
+    if "Domain mismatch" not in ev:
+        raise Exception("Domain mismatch not reported")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS",
+                            "CTRL-EVENT-EAP-FAILURE",
+                            "CTRL-EVENT-CONNECTED",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("EAP result(2) timed out")
+    if "CTRL-EVENT-EAP-FAILURE" not in ev:
+        raise Exception("EAP failure not reported")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("EAP result(3) timed out")
+    if "CTRL-EVENT-DISCONNECTED" not in ev:
+        raise Exception("Disconnection not reported")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+    if ev is None:
+        raise Exception("Network block disabling not reported")
+
+def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
+    """WPA2-Enterprise negative test - subject mismatch"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+                   password="password", phase2="auth=MSCHAPV2",
+                   ca_cert="auth_serv/ca.pem",
+                   subject_match="/C=FI/O=w1.fi/CN=example.com",
+                   wait_connect=False, scan_freq="2412")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+    if ev is None:
+        raise Exception("Association and EAP start timed out")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD",
+                            "EAP: Failed to initialize EAP method"], timeout=10)
+    if ev is None:
+        raise Exception("EAP method selection timed out")
+    if "EAP: Failed to initialize EAP method" in ev:
+        tls = dev[0].request("GET tls_library")
+        if tls.startswith("OpenSSL"):
+            raise Exception("Failed to select EAP method")
+        logger.info("subject_match not supported - connection failed, so test succeeded")
+        return
+    if "TTLS" not in ev:
+        raise Exception("Unexpected EAP method")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
+                            "CTRL-EVENT-EAP-SUCCESS",
+                            "CTRL-EVENT-EAP-FAILURE",
+                            "CTRL-EVENT-CONNECTED",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("EAP result timed out")
+    if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
+        raise Exception("TLS certificate error not reported")
+    if "Subject mismatch" not in ev:
+        raise Exception("Subject mismatch not reported")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS",
+                            "CTRL-EVENT-EAP-FAILURE",
+                            "CTRL-EVENT-CONNECTED",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("EAP result(2) timed out")
+    if "CTRL-EVENT-EAP-FAILURE" not in ev:
+        raise Exception("EAP failure not reported")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("EAP result(3) timed out")
+    if "CTRL-EVENT-DISCONNECTED" not in ev:
+        raise Exception("Disconnection not reported")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+    if ev is None:
+        raise Exception("Network block disabling not reported")
+
+def test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev):
+    """WPA2-Enterprise negative test - altsubject mismatch"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    tests = [ "incorrect.example.com",
+              "DNS:incorrect.example.com",
+              "DNS:w1.fi",
+              "DNS:erver.w1.fi" ]
+    for match in tests:
+        _test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev, match)
+
+def _test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev, match):
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+                   password="password", phase2="auth=MSCHAPV2",
+                   ca_cert="auth_serv/ca.pem",
+                   altsubject_match=match,
+                   wait_connect=False, scan_freq="2412")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+    if ev is None:
+        raise Exception("Association and EAP start timed out")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD",
+                            "EAP: Failed to initialize EAP method"], timeout=10)
+    if ev is None:
+        raise Exception("EAP method selection timed out")
+    if "EAP: Failed to initialize EAP method" in ev:
+        tls = dev[0].request("GET tls_library")
+        if tls.startswith("OpenSSL"):
+            raise Exception("Failed to select EAP method")
+        logger.info("altsubject_match not supported - connection failed, so test succeeded")
+        return
+    if "TTLS" not in ev:
+        raise Exception("Unexpected EAP method")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
+                            "CTRL-EVENT-EAP-SUCCESS",
+                            "CTRL-EVENT-EAP-FAILURE",
+                            "CTRL-EVENT-CONNECTED",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("EAP result timed out")
+    if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
+        raise Exception("TLS certificate error not reported")
+    if "AltSubject mismatch" not in ev:
+        raise Exception("altsubject mismatch not reported")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS",
+                            "CTRL-EVENT-EAP-FAILURE",
+                            "CTRL-EVENT-CONNECTED",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("EAP result(2) timed out")
+    if "CTRL-EVENT-EAP-FAILURE" not in ev:
+        raise Exception("EAP failure not reported")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("EAP result(3) timed out")
+    if "CTRL-EVENT-DISCONNECTED" not in ev:
+        raise Exception("Disconnection not reported")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+    if ev is None:
+        raise Exception("Network block disabling not reported")
+
+    dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_unauth_tls(dev, apdev):
+    """WPA2-Enterprise connection using UNAUTH-TLS"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "UNAUTH-TLS", "unauth-tls",
+                ca_cert="auth_serv/ca.pem")
+    eap_reauth(dev[0], "UNAUTH-TLS")
+
+def test_ap_wpa2_eap_ttls_server_cert_hash(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS and server certificate hash"""
+    check_cert_probe_support(dev[0])
+    skip_with_fips(dev[0])
+    srv_cert_hash = "1477c9cd88391609444b83eca45c4f9f324e3051c5c31fc233ac6aede30ce7cd"
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="probe", ca_cert="probe://",
+                   wait_connect=False, scan_freq="2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+    if ev is None:
+        raise Exception("Association and EAP start timed out")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-PEER-CERT depth=0"], timeout=10)
+    if ev is None:
+        raise Exception("No peer server certificate event seen")
+    if "hash=" + srv_cert_hash not in ev:
+        raise Exception("Expected server certificate hash not reported")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"], timeout=10)
+    if ev is None:
+        raise Exception("EAP result timed out")
+    if "Server certificate chain probe" not in ev:
+        raise Exception("Server certificate probe not reported")
+    dev[0].wait_disconnected(timeout=10)
+    dev[0].request("REMOVE_NETWORK all")
+
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+                   password="password", phase2="auth=MSCHAPV2",
+                   ca_cert="hash://server/sha256/5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a",
+                   wait_connect=False, scan_freq="2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+    if ev is None:
+        raise Exception("Association and EAP start timed out")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"], timeout=10)
+    if ev is None:
+        raise Exception("EAP result timed out")
+    if "Server certificate mismatch" not in ev:
+        raise Exception("Server certificate mismatch not reported")
+    dev[0].wait_disconnected(timeout=10)
+    dev[0].request("REMOVE_NETWORK all")
+
+    eap_connect(dev[0], apdev[0], "TTLS", "DOMAIN\mschapv2 user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="hash://server/sha256/" + srv_cert_hash,
+                phase2="auth=MSCHAPV2")
+
+def test_ap_wpa2_eap_ttls_server_cert_hash_invalid(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS and server certificate hash (invalid config)"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+                   password="password", phase2="auth=MSCHAPV2",
+                   ca_cert="hash://server/md5/5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a",
+                   wait_connect=False, scan_freq="2412")
+    dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+                   password="password", phase2="auth=MSCHAPV2",
+                   ca_cert="hash://server/sha256/5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca",
+                   wait_connect=False, scan_freq="2412")
+    dev[2].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+                   password="password", phase2="auth=MSCHAPV2",
+                   ca_cert="hash://server/sha256/5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6Q",
+                   wait_connect=False, scan_freq="2412")
+    for i in range(0, 3):
+        ev = dev[i].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+        if ev is None:
+            raise Exception("Association and EAP start timed out")
+        ev = dev[i].wait_event(["EAP: Failed to initialize EAP method: vendor 0 method 21 (TTLS)"], timeout=5)
+        if ev is None:
+            raise Exception("Did not report EAP method initialization failure")
+
+def test_ap_wpa2_eap_pwd(dev, apdev):
+    """WPA2-Enterprise connection using EAP-pwd"""
+    check_eap_capa(dev[0], "PWD")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PWD", "pwd user", password="secret password")
+    eap_reauth(dev[0], "PWD")
+    dev[0].request("REMOVE_NETWORK all")
+
+    eap_connect(dev[1], apdev[0], "PWD",
+                "pwd.user@test123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.example.com",
+                password="secret password",
+                fragment_size="90")
+
+    logger.info("Negative test with incorrect password")
+    eap_connect(dev[2], apdev[0], "PWD", "pwd user", password="secret-password",
+                expect_failure=True, local_error_report=True)
+
+    eap_connect(dev[0], apdev[0], "PWD",
+                "pwd.user@test123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.example.com",
+                password="secret password",
+                fragment_size="31")
+
+def test_ap_wpa2_eap_pwd_nthash(dev, apdev):
+    """WPA2-Enterprise connection using EAP-pwd and NTHash"""
+    check_eap_capa(dev[0], "PWD")
+    skip_with_fips(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PWD", "pwd-hash", password="secret password")
+    eap_connect(dev[1], apdev[0], "PWD", "pwd-hash",
+                password_hex="hash:e3718ece8ab74792cbbfffd316d2d19a")
+    eap_connect(dev[2], apdev[0], "PWD", "pwd user",
+                password_hex="hash:e3718ece8ab74792cbbfffd316d2d19a",
+                expect_failure=True, local_error_report=True)
+
+def test_ap_wpa2_eap_pwd_groups(dev, apdev):
+    """WPA2-Enterprise connection using various EAP-pwd groups"""
+    check_eap_capa(dev[0], "PWD")
+    tls = dev[0].request("GET tls_library")
+    params = { "ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
+               "rsn_pairwise": "CCMP", "ieee8021x": "1",
+               "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf" }
+    for i in [ 19, 20, 21, 25, 26 ]:
+        params['pwd_group'] = str(i)
+        hostapd.add_ap(apdev[0]['ifname'], params)
+        dev[0].request("REMOVE_NETWORK all")
+        try:
+            eap_connect(dev[0], apdev[0], "PWD", "pwd user",
+                        password="secret password")
+        except:
+            if "BoringSSL" in tls and i in [ 25 ]:
+                logger.info("Ignore connection failure with group %d with BoringSSL" % i)
+                dev[0].request("DISCONNECT")
+                time.sleep(0.1)
+                continue
+            raise
+
+def test_ap_wpa2_eap_pwd_invalid_group(dev, apdev):
+    """WPA2-Enterprise connection using invalid EAP-pwd group"""
+    check_eap_capa(dev[0], "PWD")
+    params = { "ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
+               "rsn_pairwise": "CCMP", "ieee8021x": "1",
+               "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf" }
+    params['pwd_group'] = "0"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PWD",
+                   identity="pwd user", password="secret password",
+                   scan_freq="2412", wait_connect=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_pwd_as_frag(dev, apdev):
+    """WPA2-Enterprise connection using EAP-pwd with server fragmentation"""
+    check_eap_capa(dev[0], "PWD")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params = { "ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
+               "rsn_pairwise": "CCMP", "ieee8021x": "1",
+               "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf",
+               "pwd_group": "19", "fragment_size": "40" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PWD", "pwd user", password="secret password")
+
+def test_ap_wpa2_eap_gpsk(dev, apdev):
+    """WPA2-Enterprise connection using EAP-GPSK"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    id = eap_connect(dev[0], apdev[0], "GPSK", "gpsk user",
+                     password="abcdefghijklmnop0123456789abcdef")
+    eap_reauth(dev[0], "GPSK")
+
+    logger.info("Test forced algorithm selection")
+    for phase1 in [ "cipher=1", "cipher=2" ]:
+        dev[0].set_network_quoted(id, "phase1", phase1)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+        if ev is None:
+            raise Exception("EAP success timed out")
+        dev[0].wait_connected(timeout=10)
+
+    logger.info("Test failed algorithm negotiation")
+    dev[0].set_network_quoted(id, "phase1", "cipher=9")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+    if ev is None:
+        raise Exception("EAP failure timed out")
+
+    logger.info("Negative test with incorrect password")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "GPSK", "gpsk user",
+                password="ffcdefghijklmnop0123456789abcdef",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_sake(dev, apdev):
+    """WPA2-Enterprise connection using EAP-SAKE"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "SAKE", "sake user",
+                password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
+    eap_reauth(dev[0], "SAKE")
+
+    logger.info("Negative test with incorrect password")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "SAKE", "sake user",
+                password_hex="ff23456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_eke(dev, apdev):
+    """WPA2-Enterprise connection using EAP-EKE"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    id = eap_connect(dev[0], apdev[0], "EKE", "eke user", password="hello")
+    eap_reauth(dev[0], "EKE")
+
+    logger.info("Test forced algorithm selection")
+    for phase1 in [ "dhgroup=5 encr=1 prf=2 mac=2",
+                    "dhgroup=4 encr=1 prf=2 mac=2",
+                    "dhgroup=3 encr=1 prf=2 mac=2",
+                    "dhgroup=3 encr=1 prf=1 mac=1" ]:
+        dev[0].set_network_quoted(id, "phase1", phase1)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+        if ev is None:
+            raise Exception("EAP success timed out")
+        dev[0].wait_connected(timeout=10)
+
+    logger.info("Test failed algorithm negotiation")
+    dev[0].set_network_quoted(id, "phase1", "dhgroup=9 encr=9 prf=9 mac=9")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+    if ev is None:
+        raise Exception("EAP failure timed out")
+
+    logger.info("Negative test with incorrect password")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "EKE", "eke user", password="hello1",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_eke_serverid_nai(dev, apdev):
+    """WPA2-Enterprise connection using EAP-EKE with serverid NAI"""
+    params = int_eap_server_params()
+    params['server_id'] = 'example.server@w1.fi'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "EKE", "eke user", password="hello")
+
+def test_ap_wpa2_eap_eke_server_oom(dev, apdev):
+    """WPA2-Enterprise connection using EAP-EKE with server OOM"""
+    params = int_eap_server_params()
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+
+    for count,func in [ (1, "eap_eke_build_commit"),
+                        (2, "eap_eke_build_commit"),
+                        (3, "eap_eke_build_commit"),
+                        (1, "eap_eke_build_confirm"),
+                        (2, "eap_eke_build_confirm"),
+                        (1, "eap_eke_process_commit"),
+                        (2, "eap_eke_process_commit"),
+                        (1, "eap_eke_process_confirm"),
+                        (1, "eap_eke_process_identity"),
+                        (2, "eap_eke_process_identity"),
+                        (3, "eap_eke_process_identity"),
+                        (4, "eap_eke_process_identity") ]:
+        with alloc_fail(hapd, count, func):
+            eap_connect(dev[0], apdev[0], "EKE", "eke user", password="hello",
+                        expect_failure=True)
+            dev[0].request("REMOVE_NETWORK all")
+
+    for count,func,pw in [ (1, "eap_eke_init", "hello"),
+                           (1, "eap_eke_get_session_id", "hello"),
+                           (1, "eap_eke_getKey", "hello"),
+                           (1, "eap_eke_build_msg", "hello"),
+                           (1, "eap_eke_build_failure", "wrong"),
+                           (1, "eap_eke_build_identity", "hello"),
+                           (2, "eap_eke_build_identity", "hello") ]:
+        with alloc_fail(hapd, count, func):
+            dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+                           eap="EKE", identity="eke user", password=pw,
+                           wait_connect=False, scan_freq="2412")
+            # This would eventually time out, but we can stop after having
+            # reached the allocation failure.
+            for i in range(20):
+                time.sleep(0.1)
+                if hapd.request("GET_ALLOC_FAIL").startswith('0'):
+                    break
+            dev[0].request("REMOVE_NETWORK all")
+
+    for count in range(1, 1000):
+        try:
+            with alloc_fail(hapd, count, "eap_server_sm_step"):
+                dev[0].connect("test-wpa2-eap",
+                               key_mgmt="WPA-EAP WPA-EAP-SHA256",
+                               eap="EKE", identity="eke user", password=pw,
+                               wait_connect=False, scan_freq="2412")
+                # This would eventually time out, but we can stop after having
+                # reached the allocation failure.
+                for i in range(10):
+                    time.sleep(0.1)
+                    if hapd.request("GET_ALLOC_FAIL").startswith('0'):
+                        break
+                dev[0].request("REMOVE_NETWORK all")
+        except Exception, e:
+            if str(e) == "Allocation failure did not trigger":
+                if count < 30:
+                    raise Exception("Too few allocation failures")
+                logger.info("%d allocation failures tested" % (count - 1))
+                break
+            raise e
+
+def test_ap_wpa2_eap_ikev2(dev, apdev):
+    """WPA2-Enterprise connection using EAP-IKEv2"""
+    check_eap_capa(dev[0], "IKEV2")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "IKEV2", "ikev2 user",
+                password="ike password")
+    eap_reauth(dev[0], "IKEV2")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "IKEV2", "ikev2 user",
+                password="ike password", fragment_size="50")
+
+    logger.info("Negative test with incorrect password")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "IKEV2", "ikev2 user",
+                password="ike-password", expect_failure=True)
+
+def test_ap_wpa2_eap_ikev2_as_frag(dev, apdev):
+    """WPA2-Enterprise connection using EAP-IKEv2 with server fragmentation"""
+    check_eap_capa(dev[0], "IKEV2")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params = { "ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
+               "rsn_pairwise": "CCMP", "ieee8021x": "1",
+               "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf",
+               "fragment_size": "50" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "IKEV2", "ikev2 user",
+                password="ike password")
+    eap_reauth(dev[0], "IKEV2")
+
+def test_ap_wpa2_eap_ikev2_oom(dev, apdev):
+    """WPA2-Enterprise connection using EAP-IKEv2 and OOM"""
+    check_eap_capa(dev[0], "IKEV2")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    tests = [ (1, "dh_init"),
+              (2, "dh_init"),
+              (1, "dh_derive_shared") ]
+    for count, func in tests:
+        with alloc_fail(dev[0], count, func):
+            dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="IKEV2",
+                           identity="ikev2 user", password="ike password",
+                           wait_connect=False, scan_freq="2412")
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+            if ev is None:
+                raise Exception("EAP method not selected")
+            for i in range(10):
+                if "0:" in dev[0].request("GET_ALLOC_FAIL"):
+                    break
+                time.sleep(0.02)
+            dev[0].request("REMOVE_NETWORK all")
+
+    tests = [ (1, "os_get_random;dh_init") ]
+    for count, func in tests:
+        with fail_test(dev[0], count, func):
+            dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="IKEV2",
+                           identity="ikev2 user", password="ike password",
+                           wait_connect=False, scan_freq="2412")
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+            if ev is None:
+                raise Exception("EAP method not selected")
+            for i in range(10):
+                if "0:" in dev[0].request("GET_FAIL"):
+                    break
+                time.sleep(0.02)
+            dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_pax(dev, apdev):
+    """WPA2-Enterprise connection using EAP-PAX"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PAX", "pax.user@example.com",
+                password_hex="0123456789abcdef0123456789abcdef")
+    eap_reauth(dev[0], "PAX")
+
+    logger.info("Negative test with incorrect password")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "PAX", "pax.user@example.com",
+                password_hex="ff23456789abcdef0123456789abcdef",
+                expect_failure=True)
+
+def test_ap_wpa2_eap_psk(dev, apdev):
+    """WPA2-Enterprise connection using EAP-PSK"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params["wpa_key_mgmt"] = "WPA-EAP-SHA256"
+    params["ieee80211w"] = "2"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PSK", "psk.user@example.com",
+                password_hex="0123456789abcdef0123456789abcdef", sha256=True)
+    eap_reauth(dev[0], "PSK", sha256=True)
+    check_mib(dev[0], [ ("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-5"),
+                        ("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-5") ])
+
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    if 'flags' not in bss:
+        raise Exception("Could not get BSS flags from BSS table")
+    if "[WPA2-EAP-SHA256-CCMP]" not in bss['flags']:
+        raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+    logger.info("Negative test with incorrect password")
+    dev[0].request("REMOVE_NETWORK all")
+    eap_connect(dev[0], apdev[0], "PSK", "psk.user@example.com",
+                password_hex="ff23456789abcdef0123456789abcdef", sha256=True,
+                expect_failure=True)
+
+def test_ap_wpa2_eap_psk_oom(dev, apdev):
+    """WPA2-Enterprise connection using EAP-PSK and OOM"""
+    skip_with_fips(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    tests = [ (1, "aes_128_ctr_encrypt;aes_128_eax_encrypt"),
+              (1, "omac1_aes_128;aes_128_eax_encrypt"),
+              (2, "omac1_aes_128;aes_128_eax_encrypt"),
+              (3, "omac1_aes_128;aes_128_eax_encrypt"),
+              (1, "=aes_128_eax_encrypt"),
+              (1, "omac1_aes_vector"),
+              (1, "aes_128_ctr_encrypt;aes_128_eax_decrypt"),
+              (1, "omac1_aes_128;aes_128_eax_decrypt"),
+              (2, "omac1_aes_128;aes_128_eax_decrypt"),
+              (3, "omac1_aes_128;aes_128_eax_decrypt"),
+              (1, "=aes_128_eax_decrypt") ]
+    for count, func in tests:
+        with alloc_fail(dev[0], count, func):
+            dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PSK",
+                           identity="psk.user@example.com",
+                           password_hex="0123456789abcdef0123456789abcdef",
+                           wait_connect=False, scan_freq="2412")
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+            if ev is None:
+                raise Exception("EAP method not selected")
+            for i in range(10):
+                if "0:" in dev[0].request("GET_ALLOC_FAIL"):
+                    break
+                time.sleep(0.02)
+            dev[0].request("REMOVE_NETWORK all")
+
+    with alloc_fail(dev[0], 1, "aes_128_encrypt_block"):
+            dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PSK",
+                           identity="psk.user@example.com",
+                           password_hex="0123456789abcdef0123456789abcdef",
+                           wait_connect=False, scan_freq="2412")
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+            if ev is None:
+                raise Exception("EAP method failure not reported")
+            dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa_eap_peap_eap_mschapv2(dev, apdev):
+    """WPA-Enterprise connection using EAP-PEAP/EAP-MSCHAPv2"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hostapd.wpa_eap_params(ssid="test-wpa-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="PEAP",
+                   identity="user", password="password", phase2="auth=MSCHAPV2",
+                   ca_cert="auth_serv/ca.pem", wait_connect=False,
+                   scan_freq="2412")
+    eap_check_auth(dev[0], "PEAP", True, rsn=False)
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    eap_reauth(dev[0], "PEAP", rsn=False)
+    check_mib(dev[0], [ ("dot11RSNAAuthenticationSuiteRequested", "00-50-f2-1"),
+                        ("dot11RSNAAuthenticationSuiteSelected", "00-50-f2-1") ])
+    status = dev[0].get_status(extra="VERBOSE")
+    if 'portControl' not in status:
+        raise Exception("portControl missing from STATUS-VERBOSE")
+    if status['portControl'] != 'Auto':
+        raise Exception("Unexpected portControl value: " + status['portControl'])
+    if 'eap_session_id' not in status:
+        raise Exception("eap_session_id missing from STATUS-VERBOSE")
+    if not status['eap_session_id'].startswith("19"):
+        raise Exception("Unexpected eap_session_id value: " + status['eap_session_id'])
+
+def test_ap_wpa2_eap_interactive(dev, apdev):
+    """WPA2-Enterprise connection using interactive identity/password entry"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    tests = [ ("Connection with dynamic TTLS/MSCHAPv2 password entry",
+               "TTLS", "ttls", "DOMAIN\mschapv2 user", "auth=MSCHAPV2",
+               None, "password"),
+              ("Connection with dynamic TTLS/MSCHAPv2 identity and password entry",
+               "TTLS", "ttls", None, "auth=MSCHAPV2",
+               "DOMAIN\mschapv2 user", "password"),
+              ("Connection with dynamic TTLS/EAP-MSCHAPv2 password entry",
+               "TTLS", "ttls", "user", "autheap=MSCHAPV2", None, "password"),
+              ("Connection with dynamic TTLS/EAP-MD5 password entry",
+               "TTLS", "ttls", "user", "autheap=MD5", None, "password"),
+              ("Connection with dynamic PEAP/EAP-MSCHAPv2 password entry",
+               "PEAP", None, "user", "auth=MSCHAPV2", None, "password"),
+              ("Connection with dynamic PEAP/EAP-GTC password entry",
+               "PEAP", None, "user", "auth=GTC", None, "password") ]
+    for [desc,eap,anon,identity,phase2,req_id,req_pw] in tests:
+        logger.info(desc)
+        dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap=eap,
+                       anonymous_identity=anon, identity=identity,
+                       ca_cert="auth_serv/ca.pem", phase2=phase2,
+                       wait_connect=False, scan_freq="2412")
+        if req_id:
+            ev = dev[0].wait_event(["CTRL-REQ-IDENTITY"])
+            if ev is None:
+                raise Exception("Request for identity timed out")
+            id = ev.split(':')[0].split('-')[-1]
+            dev[0].request("CTRL-RSP-IDENTITY-" + id + ":" + req_id)
+        ev = dev[0].wait_event(["CTRL-REQ-PASSWORD","CTRL-REQ-OTP"])
+        if ev is None:
+            raise Exception("Request for password timed out")
+        id = ev.split(':')[0].split('-')[-1]
+        type = "OTP" if "CTRL-REQ-OTP" in ev else "PASSWORD"
+        dev[0].request("CTRL-RSP-" + type + "-" + id + ":" + req_pw)
+        dev[0].wait_connected(timeout=10)
+        dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_ext_enable_network_while_connected(dev, apdev):
+    """WPA2-Enterprise interactive identity entry and ENABLE_NETWORK"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    id_other = dev[0].connect("other", key_mgmt="NONE", scan_freq="2412",
+                              only_add_network=True)
+
+    req_id = "DOMAIN\mschapv2 user"
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   anonymous_identity="ttls", identity=None,
+                   password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                   wait_connect=False, scan_freq="2412")
+    ev = dev[0].wait_event(["CTRL-REQ-IDENTITY"])
+    if ev is None:
+        raise Exception("Request for identity timed out")
+    id = ev.split(':')[0].split('-')[-1]
+    dev[0].request("CTRL-RSP-IDENTITY-" + id + ":" + req_id)
+    dev[0].wait_connected(timeout=10)
+
+    if "OK" not in dev[0].request("ENABLE_NETWORK " + str(id_other)):
+        raise Exception("Failed to enable network")
+    ev = dev[0].wait_event(["SME: Trying to authenticate"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected reconnection attempt on ENABLE_NETWORK")
+    dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_vendor_test(dev, apdev):
+    """WPA2-Enterprise connection using EAP vendor test"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "VENDOR-TEST", "vendor-test")
+    eap_reauth(dev[0], "VENDOR-TEST")
+    eap_connect(dev[1], apdev[0], "VENDOR-TEST", "vendor-test",
+                password="pending")
+
+def test_ap_wpa2_eap_fast_mschapv2_unauth_prov(dev, apdev):
+    """WPA2-Enterprise connection using EAP-FAST/MSCHAPv2 and unauthenticated provisioning"""
+    check_eap_capa(dev[0], "FAST")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "FAST", "user",
+                anonymous_identity="FAST", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                phase1="fast_provisioning=1", pac_file="blob://fast_pac")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    res = eap_reauth(dev[0], "FAST")
+    if res['tls_session_reused'] != '1':
+        raise Exception("EAP-FAST could not use PAC session ticket")
+
+def test_ap_wpa2_eap_fast_pac_file(dev, apdev, params):
+    """WPA2-Enterprise connection using EAP-FAST/MSCHAPv2 and PAC file"""
+    check_eap_capa(dev[0], "FAST")
+    pac_file = os.path.join(params['logdir'], "fast.pac")
+    pac_file2 = os.path.join(params['logdir'], "fast-bin.pac")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    try:
+        eap_connect(dev[0], apdev[0], "FAST", "user",
+                    anonymous_identity="FAST", password="password",
+                    ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                    phase1="fast_provisioning=1", pac_file=pac_file)
+        with open(pac_file, "r") as f:
+            data = f.read()
+            if "wpa_supplicant EAP-FAST PAC file - version 1" not in data:
+                raise Exception("PAC file header missing")
+            if "PAC-Key=" not in data:
+                raise Exception("PAC-Key missing from PAC file")
+        dev[0].request("REMOVE_NETWORK all")
+        eap_connect(dev[0], apdev[0], "FAST", "user",
+                    anonymous_identity="FAST", password="password",
+                    ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                    pac_file=pac_file)
+
+        eap_connect(dev[1], apdev[0], "FAST", "user",
+                    anonymous_identity="FAST", password="password",
+                    ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                    phase1="fast_provisioning=1 fast_pac_format=binary",
+                    pac_file=pac_file2)
+        dev[1].request("REMOVE_NETWORK all")
+        eap_connect(dev[1], apdev[0], "FAST", "user",
+                    anonymous_identity="FAST", password="password",
+                    ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                    phase1="fast_pac_format=binary",
+                    pac_file=pac_file2)
+    finally:
+        try:
+            os.remove(pac_file)
+        except:
+            pass
+        try:
+            os.remove(pac_file2)
+        except:
+            pass
+
+def test_ap_wpa2_eap_fast_binary_pac(dev, apdev):
+    """WPA2-Enterprise connection using EAP-FAST and binary PAC format"""
+    check_eap_capa(dev[0], "FAST")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "FAST", "user",
+                anonymous_identity="FAST", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                phase1="fast_provisioning=1 fast_max_pac_list_len=1 fast_pac_format=binary",
+                pac_file="blob://fast_pac_bin")
+    res = eap_reauth(dev[0], "FAST")
+    if res['tls_session_reused'] != '1':
+        raise Exception("EAP-FAST could not use PAC session ticket")
+
+def test_ap_wpa2_eap_fast_missing_pac_config(dev, apdev):
+    """WPA2-Enterprise connection using EAP-FAST and missing PAC config"""
+    check_eap_capa(dev[0], "FAST")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+                   identity="user", anonymous_identity="FAST",
+                   password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                   pac_file="blob://fast_pac_not_in_use",
+                   wait_connect=False, scan_freq="2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("Timeout on EAP failure report")
+    dev[0].request("REMOVE_NETWORK all")
+
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+                   identity="user", anonymous_identity="FAST",
+                   password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                   wait_connect=False, scan_freq="2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_fast_gtc_auth_prov(dev, apdev):
+    """WPA2-Enterprise connection using EAP-FAST/GTC and authenticated provisioning"""
+    check_eap_capa(dev[0], "FAST")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "FAST", "user",
+                anonymous_identity="FAST", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
+                phase1="fast_provisioning=2", pac_file="blob://fast_pac_auth")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    res = eap_reauth(dev[0], "FAST")
+    if res['tls_session_reused'] != '1':
+        raise Exception("EAP-FAST could not use PAC session ticket")
+
+def test_ap_wpa2_eap_fast_gtc_identity_change(dev, apdev):
+    """WPA2-Enterprise connection using EAP-FAST/GTC and identity changing"""
+    check_eap_capa(dev[0], "FAST")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    id = eap_connect(dev[0], apdev[0], "FAST", "user",
+                     anonymous_identity="FAST", password="password",
+                     ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
+                     phase1="fast_provisioning=2",
+                     pac_file="blob://fast_pac_auth")
+    dev[0].set_network_quoted(id, "identity", "user2")
+    dev[0].wait_disconnected()
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
+    if ev is None:
+        raise Exception("EAP-FAST not started")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+    if ev is None:
+        raise Exception("EAP failure not reported")
+    dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_fast_prf_oom(dev, apdev):
+    """WPA2-Enterprise connection using EAP-FAST and OOM in PRF"""
+    check_eap_capa(dev[0], "FAST")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    with alloc_fail(dev[0], 2, "openssl_tls_prf"):
+        dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+                       identity="user", anonymous_identity="FAST",
+                       password="password", ca_cert="auth_serv/ca.pem",
+                       phase2="auth=GTC",
+                       phase1="fast_provisioning=2",
+                       pac_file="blob://fast_pac_auth",
+                       wait_connect=False, scan_freq="2412")
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+        if ev is None:
+            raise Exception("EAP failure not reported")
+    dev[0].request("DISCONNECT")
+
+def test_ap_wpa2_eap_fast_server_oom(dev, apdev):
+    """EAP-FAST/MSCHAPv2 and server OOM"""
+    check_eap_capa(dev[0], "FAST")
+
+    params = int_eap_server_params()
+    params['dh_file'] = 'auth_serv/dh.conf'
+    params['pac_opaque_encr_key'] = '000102030405060708090a0b0c0d0e0f'
+    params['eap_fast_a_id'] = '1011'
+    params['eap_fast_a_id_info'] = 'another test server'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    with alloc_fail(hapd, 1, "tls_session_ticket_ext_cb"):
+        id = eap_connect(dev[0], apdev[0], "FAST", "user",
+                         anonymous_identity="FAST", password="password",
+                         ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                         phase1="fast_provisioning=1",
+                         pac_file="blob://fast_pac",
+                         expect_failure=True)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+        if ev is None:
+            raise Exception("No EAP failure reported")
+        dev[0].wait_disconnected()
+        dev[0].request("DISCONNECT")
+
+    dev[0].select_network(id, freq="2412")
+
+def test_ap_wpa2_eap_tls_ocsp(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TLS and verifying OCSP"""
+    check_ocsp_support(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+                private_key="auth_serv/user.pkcs12",
+                private_key_passwd="whatever", ocsp=2)
+
+def int_eap_server_params():
+    params = { "ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
+               "rsn_pairwise": "CCMP", "ieee8021x": "1",
+               "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf",
+               "ca_cert": "auth_serv/ca.pem",
+               "server_cert": "auth_serv/server.pem",
+               "private_key": "auth_serv/server.key" }
+    return params
+
+def test_ap_wpa2_eap_tls_ocsp_invalid_data(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TLS and invalid OCSP data"""
+    check_ocsp_support(dev[0])
+    params = int_eap_server_params()
+    params["ocsp_stapling_response"] = "auth_serv/ocsp-req.der"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                   identity="tls user", ca_cert="auth_serv/ca.pem",
+                   private_key="auth_serv/user.pkcs12",
+                   private_key_passwd="whatever", ocsp=2,
+                   wait_connect=False, scan_freq="2412")
+    count = 0
+    while True:
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
+        if ev is None:
+            raise Exception("Timeout on EAP status")
+        if 'bad certificate status response' in ev:
+            break
+        count = count + 1
+        if count > 10:
+            raise Exception("Unexpected number of EAP status messages")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_tls_ocsp_invalid(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TLS and invalid OCSP response"""
+    check_ocsp_support(dev[0])
+    params = int_eap_server_params()
+    params["ocsp_stapling_response"] = "auth_serv/ocsp-server-cache.der-invalid"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                   identity="tls user", ca_cert="auth_serv/ca.pem",
+                   private_key="auth_serv/user.pkcs12",
+                   private_key_passwd="whatever", ocsp=2,
+                   wait_connect=False, scan_freq="2412")
+    count = 0
+    while True:
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
+        if ev is None:
+            raise Exception("Timeout on EAP status")
+        if 'bad certificate status response' in ev:
+            break
+        count = count + 1
+        if count > 10:
+            raise Exception("Unexpected number of EAP status messages")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_tls_ocsp_unknown_sign(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TLS and unknown OCSP signer"""
+    check_ocsp_support(dev[0])
+    params = int_eap_server_params()
+    params["ocsp_stapling_response"] = "auth_serv/ocsp-server-cache.der-unknown-sign"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                   identity="tls user", ca_cert="auth_serv/ca.pem",
+                   private_key="auth_serv/user.pkcs12",
+                   private_key_passwd="whatever", ocsp=2,
+                   wait_connect=False, scan_freq="2412")
+    count = 0
+    while True:
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
+        if ev is None:
+            raise Exception("Timeout on EAP status")
+        if 'bad certificate status response' in ev:
+            break
+        count = count + 1
+        if count > 10:
+            raise Exception("Unexpected number of EAP status messages")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_ttls_ocsp_revoked(dev, apdev, params):
+    """WPA2-Enterprise connection using EAP-TTLS and OCSP status revoked"""
+    check_ocsp_support(dev[0])
+    ocsp = os.path.join(params['logdir'], "ocsp-server-cache-revoked.der")
+    if not os.path.exists(ocsp):
+        raise HwsimSkip("No OCSP response available")
+    params = int_eap_server_params()
+    params["ocsp_stapling_response"] = ocsp
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="pap user", ca_cert="auth_serv/ca.pem",
+                   anonymous_identity="ttls", password="password",
+                   phase2="auth=PAP", ocsp=2,
+                   wait_connect=False, scan_freq="2412")
+    count = 0
+    while True:
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
+        if ev is None:
+            raise Exception("Timeout on EAP status")
+        if 'bad certificate status response' in ev:
+            break
+        if 'certificate revoked' in ev:
+            break
+        count = count + 1
+        if count > 10:
+            raise Exception("Unexpected number of EAP status messages")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_ttls_ocsp_unknown(dev, apdev, params):
+    """WPA2-Enterprise connection using EAP-TTLS and OCSP status revoked"""
+    check_ocsp_support(dev[0])
+    ocsp = os.path.join(params['logdir'], "ocsp-server-cache-unknown.der")
+    if not os.path.exists(ocsp):
+        raise HwsimSkip("No OCSP response available")
+    params = int_eap_server_params()
+    params["ocsp_stapling_response"] = ocsp
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="pap user", ca_cert="auth_serv/ca.pem",
+                   anonymous_identity="ttls", password="password",
+                   phase2="auth=PAP", ocsp=2,
+                   wait_connect=False, scan_freq="2412")
+    count = 0
+    while True:
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
+        if ev is None:
+            raise Exception("Timeout on EAP status")
+        if 'bad certificate status response' in ev:
+            break
+        count = count + 1
+        if count > 10:
+            raise Exception("Unexpected number of EAP status messages")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_ttls_optional_ocsp_unknown(dev, apdev, params):
+    """WPA2-Enterprise connection using EAP-TTLS and OCSP status revoked"""
+    ocsp = os.path.join(params['logdir'], "ocsp-server-cache-unknown.der")
+    if not os.path.exists(ocsp):
+        raise HwsimSkip("No OCSP response available")
+    params = int_eap_server_params()
+    params["ocsp_stapling_response"] = ocsp
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="pap user", ca_cert="auth_serv/ca.pem",
+                   anonymous_identity="ttls", password="password",
+                   phase2="auth=PAP", ocsp=1, scan_freq="2412")
+
+def test_ap_wpa2_eap_tls_domain_suffix_match_cn_full(dev, apdev):
+    """WPA2-Enterprise using EAP-TLS and domain suffix match (CN)"""
+    params = int_eap_server_params()
+    params["server_cert"] = "auth_serv/server-no-dnsname.pem"
+    params["private_key"] = "auth_serv/server-no-dnsname.key"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                   identity="tls user", ca_cert="auth_serv/ca.pem",
+                   private_key="auth_serv/user.pkcs12",
+                   private_key_passwd="whatever",
+                   domain_suffix_match="server3.w1.fi",
+                   scan_freq="2412")
+
+def test_ap_wpa2_eap_tls_domain_match_cn(dev, apdev):
+    """WPA2-Enterprise using EAP-TLS and domainmatch (CN)"""
+    params = int_eap_server_params()
+    params["server_cert"] = "auth_serv/server-no-dnsname.pem"
+    params["private_key"] = "auth_serv/server-no-dnsname.key"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                   identity="tls user", ca_cert="auth_serv/ca.pem",
+                   private_key="auth_serv/user.pkcs12",
+                   private_key_passwd="whatever",
+                   domain_match="server3.w1.fi",
+                   scan_freq="2412")
+
+def test_ap_wpa2_eap_tls_domain_suffix_match_cn(dev, apdev):
+    """WPA2-Enterprise using EAP-TLS and domain suffix match (CN)"""
+    check_domain_match_full(dev[0])
+    params = int_eap_server_params()
+    params["server_cert"] = "auth_serv/server-no-dnsname.pem"
+    params["private_key"] = "auth_serv/server-no-dnsname.key"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                   identity="tls user", ca_cert="auth_serv/ca.pem",
+                   private_key="auth_serv/user.pkcs12",
+                   private_key_passwd="whatever",
+                   domain_suffix_match="w1.fi",
+                   scan_freq="2412")
+
+def test_ap_wpa2_eap_tls_domain_suffix_mismatch_cn(dev, apdev):
+    """WPA2-Enterprise using EAP-TLS and domain suffix mismatch (CN)"""
+    params = int_eap_server_params()
+    params["server_cert"] = "auth_serv/server-no-dnsname.pem"
+    params["private_key"] = "auth_serv/server-no-dnsname.key"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                   identity="tls user", ca_cert="auth_serv/ca.pem",
+                   private_key="auth_serv/user.pkcs12",
+                   private_key_passwd="whatever",
+                   domain_suffix_match="example.com",
+                   wait_connect=False,
+                   scan_freq="2412")
+    dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                   identity="tls user", ca_cert="auth_serv/ca.pem",
+                   private_key="auth_serv/user.pkcs12",
+                   private_key_passwd="whatever",
+                   domain_suffix_match="erver3.w1.fi",
+                   wait_connect=False,
+                   scan_freq="2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("Timeout on EAP failure report")
+    ev = dev[1].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("Timeout on EAP failure report (2)")
+
+def test_ap_wpa2_eap_tls_domain_mismatch_cn(dev, apdev):
+    """WPA2-Enterprise using EAP-TLS and domain mismatch (CN)"""
+    params = int_eap_server_params()
+    params["server_cert"] = "auth_serv/server-no-dnsname.pem"
+    params["private_key"] = "auth_serv/server-no-dnsname.key"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                   identity="tls user", ca_cert="auth_serv/ca.pem",
+                   private_key="auth_serv/user.pkcs12",
+                   private_key_passwd="whatever",
+                   domain_match="example.com",
+                   wait_connect=False,
+                   scan_freq="2412")
+    dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                   identity="tls user", ca_cert="auth_serv/ca.pem",
+                   private_key="auth_serv/user.pkcs12",
+                   private_key_passwd="whatever",
+                   domain_match="w1.fi",
+                   wait_connect=False,
+                   scan_freq="2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("Timeout on EAP failure report")
+    ev = dev[1].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("Timeout on EAP failure report (2)")
+
+def test_ap_wpa2_eap_ttls_expired_cert(dev, apdev):
+    """WPA2-Enterprise using EAP-TTLS and expired certificate"""
+    skip_with_fips(dev[0])
+    params = int_eap_server_params()
+    params["server_cert"] = "auth_serv/server-expired.pem"
+    params["private_key"] = "auth_serv/server-expired.key"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="mschap user", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                   wait_connect=False,
+                   scan_freq="2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"])
+    if ev is None:
+        raise Exception("Timeout on EAP certificate error report")
+    if "reason=4" not in ev or "certificate has expired" not in ev:
+        raise Exception("Unexpected failure reason: " + ev)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_ttls_ignore_expired_cert(dev, apdev):
+    """WPA2-Enterprise using EAP-TTLS and ignore certificate expiration"""
+    skip_with_fips(dev[0])
+    params = int_eap_server_params()
+    params["server_cert"] = "auth_serv/server-expired.pem"
+    params["private_key"] = "auth_serv/server-expired.key"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="mschap user", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                   phase1="tls_disable_time_checks=1",
+                   scan_freq="2412")
+
+def test_ap_wpa2_eap_ttls_long_duration(dev, apdev):
+    """WPA2-Enterprise using EAP-TTLS and long certificate duration"""
+    skip_with_fips(dev[0])
+    params = int_eap_server_params()
+    params["server_cert"] = "auth_serv/server-long-duration.pem"
+    params["private_key"] = "auth_serv/server-long-duration.key"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="mschap user", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                   scan_freq="2412")
+
+def test_ap_wpa2_eap_ttls_server_cert_eku_client(dev, apdev):
+    """WPA2-Enterprise using EAP-TTLS and server cert with client EKU"""
+    skip_with_fips(dev[0])
+    params = int_eap_server_params()
+    params["server_cert"] = "auth_serv/server-eku-client.pem"
+    params["private_key"] = "auth_serv/server-eku-client.key"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="mschap user", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                   wait_connect=False,
+                   scan_freq="2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_ttls_server_cert_eku_client_server(dev, apdev):
+    """WPA2-Enterprise using EAP-TTLS and server cert with client and server EKU"""
+    skip_with_fips(dev[0])
+    params = int_eap_server_params()
+    params["server_cert"] = "auth_serv/server-eku-client-server.pem"
+    params["private_key"] = "auth_serv/server-eku-client-server.key"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="mschap user", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                   scan_freq="2412")
+
+def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
+    """WPA2-Enterprise using EAP-TTLS and server PKCS#12 file"""
+    skip_with_fips(dev[0])
+    params = int_eap_server_params()
+    del params["server_cert"]
+    params["private_key"] = "auth_serv/server.pkcs12"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="mschap user", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                   scan_freq="2412")
+
+def test_ap_wpa2_eap_ttls_dh_params(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/CHAP and setting DH params"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.der", phase2="auth=PAP",
+                dh_file="auth_serv/dh.conf")
+
+def test_ap_wpa2_eap_ttls_dh_params_dsa(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS and setting DH params (DSA)"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.der", phase2="auth=PAP",
+                dh_file="auth_serv/dsaparam.pem")
+
+def test_ap_wpa2_eap_ttls_dh_params_not_found(dev, apdev):
+    """EAP-TTLS and DH params file not found"""
+    skip_with_fips(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="mschap user", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                   dh_file="auth_serv/dh-no-such-file.conf",
+                   scan_freq="2412", wait_connect=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("EAP failure timed out")
+    dev[0].request("REMOVE_NETWORK all")
+    dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_ttls_dh_params_invalid(dev, apdev):
+    """EAP-TTLS and invalid DH params file"""
+    skip_with_fips(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="mschap user", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                   dh_file="auth_serv/ca.pem",
+                   scan_freq="2412", wait_connect=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("EAP failure timed out")
+    dev[0].request("REMOVE_NETWORK all")
+    dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_ttls_dh_params_blob(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS/CHAP and setting DH params from blob"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dh = read_pem("auth_serv/dh2.conf")
+    if "OK" not in dev[0].request("SET blob dhparams " + dh.encode("hex")):
+        raise Exception("Could not set dhparams blob")
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.der", phase2="auth=PAP",
+                dh_file="blob://dhparams")
+
+def test_ap_wpa2_eap_ttls_dh_params_server(dev, apdev):
+    """WPA2-Enterprise using EAP-TTLS and alternative server dhparams"""
+    params = int_eap_server_params()
+    params["dh_file"] = "auth_serv/dh2.conf"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.der", phase2="auth=PAP")
+
+def test_ap_wpa2_eap_ttls_dh_params_dsa_server(dev, apdev):
+    """WPA2-Enterprise using EAP-TTLS and alternative server dhparams (DSA)"""
+    params = int_eap_server_params()
+    params["dh_file"] = "auth_serv/dsaparam.pem"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.der", phase2="auth=PAP")
+
+def test_ap_wpa2_eap_ttls_dh_params_not_found(dev, apdev):
+    """EAP-TLS server and dhparams file not found"""
+    params = int_eap_server_params()
+    params["dh_file"] = "auth_serv/dh-no-such-file.conf"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, no_enable=True)
+    if "FAIL" not in hapd.request("ENABLE"):
+        raise Exception("Invalid configuration accepted")
+
+def test_ap_wpa2_eap_ttls_dh_params_invalid(dev, apdev):
+    """EAP-TLS server and invalid dhparams file"""
+    params = int_eap_server_params()
+    params["dh_file"] = "auth_serv/ca.pem"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, no_enable=True)
+    if "FAIL" not in hapd.request("ENABLE"):
+        raise Exception("Invalid configuration accepted")
+
+def test_ap_wpa2_eap_reauth(dev, apdev):
+    """WPA2-Enterprise and Authenticator forcing reauthentication"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params['eap_reauth_period'] = '2'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PAX", "pax.user@example.com",
+                password_hex="0123456789abcdef0123456789abcdef")
+    logger.info("Wait for reauthentication")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on reauthentication")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on reauthentication")
+    for i in range(0, 20):
+        state = dev[0].get_status_field("wpa_state")
+        if state == "COMPLETED":
+            break
+        time.sleep(0.1)
+    if state != "COMPLETED":
+        raise Exception("Reauthentication did not complete")
+
+def test_ap_wpa2_eap_request_identity_message(dev, apdev):
+    """Optional displayable message in EAP Request-Identity"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params['eap_message'] = 'hello\\0networkid=netw,nasid=foo,portid=0,NAIRealms=example.com'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PAX", "pax.user@example.com",
+                password_hex="0123456789abcdef0123456789abcdef")
+
+def test_ap_wpa2_eap_sim_aka_result_ind(dev, apdev):
+    """WPA2-Enterprise using EAP-SIM/AKA and protected result indication"""
+    check_hlr_auc_gw_support()
+    params = int_eap_server_params()
+    params['eap_sim_db'] = "unix:/tmp/hlr_auc_gw.sock"
+    params['eap_sim_aka_result_ind'] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    eap_connect(dev[0], apdev[0], "SIM", "1232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+                phase1="result_ind=1")
+    eap_reauth(dev[0], "SIM")
+    eap_connect(dev[1], apdev[0], "SIM", "1232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+
+    dev[0].request("REMOVE_NETWORK all")
+    dev[1].request("REMOVE_NETWORK all")
+
+    eap_connect(dev[0], apdev[0], "AKA", "0232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+                phase1="result_ind=1")
+    eap_reauth(dev[0], "AKA")
+    eap_connect(dev[1], apdev[0], "AKA", "0232010000000000",
+                password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+
+    dev[0].request("REMOVE_NETWORK all")
+    dev[1].request("REMOVE_NETWORK all")
+
+    eap_connect(dev[0], apdev[0], "AKA'", "6555444333222111",
+                password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
+                phase1="result_ind=1")
+    eap_reauth(dev[0], "AKA'")
+    eap_connect(dev[1], apdev[0], "AKA'", "6555444333222111",
+                password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+
+def test_ap_wpa2_eap_too_many_roundtrips(dev, apdev):
+    """WPA2-Enterprise connection resulting in too many EAP roundtrips"""
+    skip_with_fips(dev[0])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+                   eap="TTLS", identity="mschap user",
+                   wait_connect=False, scan_freq="2412", ieee80211w="1",
+                   anonymous_identity="ttls", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                   fragment_size="10")
+    ev = dev[0].wait_event(["EAP: more than"], timeout=20)
+    if ev is None:
+        raise Exception("EAP roundtrip limit not reached")
+
+def test_ap_wpa2_eap_expanded_nak(dev, apdev):
+    """WPA2-Enterprise connection with EAP resulting in expanded NAK"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+                   eap="PSK", identity="vendor-test",
+                   password_hex="ff23456789abcdef0123456789abcdef",
+                   wait_connect=False)
+
+    found = False
+    for i in range(0, 5):
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"], timeout=10)
+        if ev is None:
+            raise Exception("Association and EAP start timed out")
+        if "refuse proposed method" in ev:
+            found = True
+            break
+    if not found:
+        raise Exception("Unexpected EAP status: " + ev)
+
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+    if ev is None:
+        raise Exception("EAP failure timed out")
+
+def test_ap_wpa2_eap_sql(dev, apdev, params):
+    """WPA2-Enterprise connection using SQLite for user DB"""
+    skip_with_fips(dev[0])
+    try:
+        import sqlite3
+    except ImportError:
+        raise HwsimSkip("No sqlite3 module available")
+    dbfile = os.path.join(params['logdir'], "eap-user.db")
+    try:
+        os.remove(dbfile)
+    except:
+        pass
+    con = sqlite3.connect(dbfile)
+    with con:
+        cur = con.cursor()
+        cur.execute("CREATE TABLE users(identity TEXT PRIMARY KEY, methods TEXT, password TEXT, remediation TEXT, phase2 INTEGER)")
+        cur.execute("CREATE TABLE wildcards(identity TEXT PRIMARY KEY, methods TEXT)")
+        cur.execute("INSERT INTO users(identity,methods,password,phase2) VALUES ('user-pap','TTLS-PAP','password',1)")
+        cur.execute("INSERT INTO users(identity,methods,password,phase2) VALUES ('user-chap','TTLS-CHAP','password',1)")
+        cur.execute("INSERT INTO users(identity,methods,password,phase2) VALUES ('user-mschap','TTLS-MSCHAP','password',1)")
+        cur.execute("INSERT INTO users(identity,methods,password,phase2) VALUES ('user-mschapv2','TTLS-MSCHAPV2','password',1)")
+        cur.execute("INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS')")
+        cur.execute("CREATE TABLE authlog(timestamp TEXT, session TEXT, nas_ip TEXT, username TEXT, note TEXT)")
+
+    try:
+        params = int_eap_server_params()
+        params["eap_user_file"] = "sqlite:" + dbfile
+        hostapd.add_ap(apdev[0]['ifname'], params)
+        eap_connect(dev[0], apdev[0], "TTLS", "user-mschapv2",
+                    anonymous_identity="ttls", password="password",
+                    ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+        dev[0].request("REMOVE_NETWORK all")
+        eap_connect(dev[1], apdev[0], "TTLS", "user-mschap",
+                    anonymous_identity="ttls", password="password",
+                    ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP")
+        dev[1].request("REMOVE_NETWORK all")
+        eap_connect(dev[0], apdev[0], "TTLS", "user-chap",
+                    anonymous_identity="ttls", password="password",
+                    ca_cert="auth_serv/ca.pem", phase2="auth=CHAP")
+        eap_connect(dev[1], apdev[0], "TTLS", "user-pap",
+                    anonymous_identity="ttls", password="password",
+                    ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+    finally:
+        os.remove(dbfile)
+
+def test_ap_wpa2_eap_non_ascii_identity(dev, apdev):
+    """WPA2-Enterprise connection attempt using non-ASCII identity"""
+    params = int_eap_server_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="\x80", password="password", wait_connect=False)
+    dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="a\x80", password="password", wait_connect=False)
+    for i in range(0, 2):
+        ev = dev[i].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+        if ev is None:
+            raise Exception("Association and EAP start timed out")
+        ev = dev[i].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
+        if ev is None:
+            raise Exception("EAP method selection timed out")
+
+def test_ap_wpa2_eap_non_ascii_identity2(dev, apdev):
+    """WPA2-Enterprise connection attempt using non-ASCII identity"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="\x80", password="password", wait_connect=False)
+    dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="a\x80", password="password", wait_connect=False)
+    for i in range(0, 2):
+        ev = dev[i].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+        if ev is None:
+            raise Exception("Association and EAP start timed out")
+        ev = dev[i].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
+        if ev is None:
+            raise Exception("EAP method selection timed out")
+
+def test_openssl_cipher_suite_config_wpas(dev, apdev):
+    """OpenSSL cipher suite configuration on wpa_supplicant"""
+    tls = dev[0].request("GET tls_library")
+    if not tls.startswith("OpenSSL"):
+        raise HwsimSkip("TLS library is not OpenSSL: " + tls)
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                openssl_ciphers="AES128",
+                ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+    eap_connect(dev[1], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                openssl_ciphers="EXPORT",
+                ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+                expect_failure=True, maybe_local_error=True)
+    dev[2].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="pap user", anonymous_identity="ttls",
+                   password="password",
+                   openssl_ciphers="FOO",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+                   wait_connect=False)
+    ev = dev[2].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+    if ev is None:
+        raise Exception("EAP failure after invalid openssl_ciphers not reported")
+    dev[2].request("DISCONNECT")
+
+def test_openssl_cipher_suite_config_hapd(dev, apdev):
+    """OpenSSL cipher suite configuration on hostapd"""
+    tls = dev[0].request("GET tls_library")
+    if not tls.startswith("OpenSSL"):
+        raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL: " + tls)
+    params = int_eap_server_params()
+    params['openssl_ciphers'] = "AES256"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    tls = hapd.request("GET tls_library")
+    if not tls.startswith("OpenSSL"):
+        raise HwsimSkip("hostapd TLS library is not OpenSSL: " + tls)
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+    eap_connect(dev[1], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                openssl_ciphers="AES128",
+                ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+                expect_failure=True)
+    eap_connect(dev[2], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                openssl_ciphers="HIGH:!ADH",
+                ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+
+    params['openssl_ciphers'] = "FOO"
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params, no_enable=True)
+    if "FAIL" not in hapd2.request("ENABLE"):
+        raise Exception("Invalid openssl_ciphers value accepted")
+
+def test_wpa2_eap_ttls_pap_key_lifetime_in_memory(dev, apdev, params):
+    """Key lifetime in memory with WPA2-Enterprise using EAP-TTLS/PAP"""
+    p = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], p)
+    password = "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25"
+    pid = find_wpas_process(dev[0])
+    id = eap_connect(dev[0], apdev[0], "TTLS", "pap-secret",
+                     anonymous_identity="ttls", password=password,
+                     ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+    time.sleep(1)
+    buf = read_process_memory(pid, password)
+
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+
+    dev[0].relog()
+    msk = None
+    emsk = None
+    pmk = None
+    ptk = None
+    gtk = None
+    with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
+        for l in f.readlines():
+            if "EAP-TTLS: Derived key - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                msk = binascii.unhexlify(val)
+            if "EAP-TTLS: Derived EMSK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                emsk = binascii.unhexlify(val)
+            if "WPA: PMK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                pmk = binascii.unhexlify(val)
+            if "WPA: PTK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                ptk = binascii.unhexlify(val)
+            if "WPA: Group Key - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                gtk = binascii.unhexlify(val)
+    if not msk or not emsk or not pmk or not ptk or not gtk:
+        raise Exception("Could not find keys from debug log")
+    if len(gtk) != 16:
+        raise Exception("Unexpected GTK length")
+
+    kck = ptk[0:16]
+    kek = ptk[16:32]
+    tk = ptk[32:48]
+
+    fname = os.path.join(params['logdir'],
+                         'wpa2_eap_ttls_pap_key_lifetime_in_memory.memctx-')
+
+    logger.info("Checking keys in memory while associated")
+    get_key_locations(buf, password, "Password")
+    get_key_locations(buf, pmk, "PMK")
+    get_key_locations(buf, msk, "MSK")
+    get_key_locations(buf, emsk, "EMSK")
+    if password not in buf:
+        raise HwsimSkip("Password not found while associated")
+    if pmk not in buf:
+        raise HwsimSkip("PMK not found while associated")
+    if kck not in buf:
+        raise Exception("KCK not found while associated")
+    if kek not in buf:
+        raise Exception("KEK not found while associated")
+    if tk in buf:
+        raise Exception("TK found from memory")
+    if gtk in buf:
+        raise Exception("GTK found from memory")
+
+    logger.info("Checking keys in memory after disassociation")
+    buf = read_process_memory(pid, password)
+
+    # Note: Password is still present in network configuration
+    # Note: PMK is in PMKSA cache and EAP fast re-auth data
+
+    get_key_locations(buf, password, "Password")
+    get_key_locations(buf, pmk, "PMK")
+    get_key_locations(buf, msk, "MSK")
+    get_key_locations(buf, emsk, "EMSK")
+    verify_not_present(buf, kck, fname, "KCK")
+    verify_not_present(buf, kek, fname, "KEK")
+    verify_not_present(buf, tk, fname, "TK")
+    verify_not_present(buf, gtk, fname, "GTK")
+
+    dev[0].request("PMKSA_FLUSH")
+    dev[0].set_network_quoted(id, "identity", "foo")
+    logger.info("Checking keys in memory after PMKSA cache and EAP fast reauth flush")
+    buf = read_process_memory(pid, password)
+    get_key_locations(buf, password, "Password")
+    get_key_locations(buf, pmk, "PMK")
+    get_key_locations(buf, msk, "MSK")
+    get_key_locations(buf, emsk, "EMSK")
+    verify_not_present(buf, pmk, fname, "PMK")
+
+    dev[0].request("REMOVE_NETWORK all")
+
+    logger.info("Checking keys in memory after network profile removal")
+    buf = read_process_memory(pid, password)
+
+    get_key_locations(buf, password, "Password")
+    get_key_locations(buf, pmk, "PMK")
+    get_key_locations(buf, msk, "MSK")
+    get_key_locations(buf, emsk, "EMSK")
+    verify_not_present(buf, password, fname, "password")
+    verify_not_present(buf, pmk, fname, "PMK")
+    verify_not_present(buf, kck, fname, "KCK")
+    verify_not_present(buf, kek, fname, "KEK")
+    verify_not_present(buf, tk, fname, "TK")
+    verify_not_present(buf, gtk, fname, "GTK")
+    verify_not_present(buf, msk, fname, "MSK")
+    verify_not_present(buf, emsk, fname, "EMSK")
+
+def test_ap_wpa2_eap_unexpected_wep_eapol_key(dev, apdev):
+    """WPA2-Enterprise connection and unexpected WEP EAPOL-Key"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+
+    # Send unexpected WEP EAPOL-Key; this gets dropped
+    res = dev[0].request("EAPOL_RX " + bssid + " 0203002c0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
+    if "OK" not in res:
+        raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+def test_ap_wpa2_eap_in_bridge(dev, apdev):
+    """WPA2-EAP and wpas interface in a bridge"""
+    br_ifname='sta-br0'
+    ifname='wlan5'
+    try:
+        _test_ap_wpa2_eap_in_bridge(dev, apdev)
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down'])
+        subprocess.call(['brctl', 'delif', br_ifname, ifname])
+        subprocess.call(['brctl', 'delbr', br_ifname])
+        subprocess.call(['iw', ifname, 'set', '4addr', 'off'])
+
+def _test_ap_wpa2_eap_in_bridge(dev, apdev):
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    br_ifname='sta-br0'
+    ifname='wlan5'
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    subprocess.call(['brctl', 'addbr', br_ifname])
+    subprocess.call(['brctl', 'setfd', br_ifname, '0'])
+    subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
+    subprocess.call(['iw', ifname, 'set', '4addr', 'on'])
+    subprocess.check_call(['brctl', 'addif', br_ifname, ifname])
+    wpas.interface_add(ifname, br_ifname=br_ifname)
+
+    id = eap_connect(wpas, apdev[0], "PAX", "pax.user@example.com",
+                     password_hex="0123456789abcdef0123456789abcdef")
+    eap_reauth(wpas, "PAX")
+    # Try again as a regression test for packet socket workaround
+    eap_reauth(wpas, "PAX")
+    wpas.request("DISCONNECT")
+    wpas.wait_disconnected()
+    wpas.request("RECONNECT")
+    wpas.wait_connected()
+
+def test_ap_wpa2_eap_session_ticket(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS and TLS session ticket enabled"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    key_mgmt = hapd.get_config()['key_mgmt']
+    if key_mgmt.split(' ')[0] != "WPA-EAP":
+        raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem",
+                phase1="tls_disable_session_ticket=0", phase2="auth=PAP")
+    eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_no_workaround(dev, apdev):
+    """WPA2-Enterprise connection using EAP-TTLS and eap_workaround=0"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    key_mgmt = hapd.get_config()['key_mgmt']
+    if key_mgmt.split(' ')[0] != "WPA-EAP":
+        raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", eap_workaround='0',
+                phase2="auth=PAP")
+    eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_tls_check_crl(dev, apdev):
+    """EAP-TLS and server checking CRL"""
+    params = int_eap_server_params()
+    params['check_crl'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    # check_crl=1 and no CRL available --> reject connection
+    eap_connect(dev[0], apdev[0], "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+                client_cert="auth_serv/user.pem",
+                private_key="auth_serv/user.key", expect_failure=True)
+    dev[0].request("REMOVE_NETWORK all")
+
+    hapd.disable()
+    hapd.set("ca_cert", "auth_serv/ca-and-crl.pem")
+    hapd.enable()
+
+    # check_crl=1 and valid CRL --> accept
+    eap_connect(dev[0], apdev[0], "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+                client_cert="auth_serv/user.pem",
+                private_key="auth_serv/user.key")
+    dev[0].request("REMOVE_NETWORK all")
+
+    hapd.disable()
+    hapd.set("check_crl", "2")
+    hapd.enable()
+
+    # check_crl=2 and valid CRL --> accept
+    eap_connect(dev[0], apdev[0], "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+                client_cert="auth_serv/user.pem",
+                private_key="auth_serv/user.key")
+    dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_tls_oom(dev, apdev):
+    """EAP-TLS and OOM"""
+    check_subject_match_support(dev[0])
+    check_altsubject_match_support(dev[0])
+    check_domain_match_full(dev[0])
+
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    tests = [ (1, "tls_connection_set_subject_match"),
+              (2, "tls_connection_set_subject_match"),
+              (3, "tls_connection_set_subject_match"),
+              (4, "tls_connection_set_subject_match") ]
+    for count, func in tests:
+        with alloc_fail(dev[0], count, func):
+            dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                           identity="tls user", ca_cert="auth_serv/ca.pem",
+                           client_cert="auth_serv/user.pem",
+                           private_key="auth_serv/user.key",
+                           subject_match="/C=FI/O=w1.fi/CN=server.w1.fi",
+                           altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/",
+                           domain_suffix_match="server.w1.fi",
+                           domain_match="server.w1.fi",
+                           wait_connect=False, scan_freq="2412")
+            # TLS parameter configuration error results in CTRL-REQ-PASSPHRASE
+            ev = dev[0].wait_event(["CTRL-REQ-PASSPHRASE"], timeout=5)
+            if ev is None:
+                raise Exception("No passphrase request")
+            dev[0].request("REMOVE_NETWORK all")
+            dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_tls_macacl(dev, apdev):
+    """WPA2-Enterprise connection using MAC ACL"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params["macaddr_acl"] = "2"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[1], apdev[0], "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+                client_cert="auth_serv/user.pem",
+                private_key="auth_serv/user.key")
+
+def test_ap_wpa2_eap_oom(dev, apdev):
+    """EAP server and OOM"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+
+    with alloc_fail(hapd, 1, "eapol_auth_alloc"):
+        # The first attempt fails, but STA will send EAPOL-Start to retry and
+        # that succeeds.
+        dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                       identity="tls user", ca_cert="auth_serv/ca.pem",
+                       client_cert="auth_serv/user.pem",
+                       private_key="auth_serv/user.key",
+                       scan_freq="2412")
+
+def check_tls_ver(dev, ap, phase1, expected):
+    eap_connect(dev, ap, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+                client_cert="auth_serv/user.pem",
+                private_key="auth_serv/user.key",
+                phase1=phase1)
+    ver = dev.get_status_field("eap_tls_version")
+    if ver != expected:
+        raise Exception("Unexpected TLS version (expected %s): %s" % (expected, ver))
+
+def test_ap_wpa2_eap_tls_versions(dev, apdev):
+    """EAP-TLS and TLS version configuration"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    tls = dev[0].request("GET tls_library")
+    if tls.startswith("OpenSSL"):
+        if "build=OpenSSL 1.0.2" in tls and "run=OpenSSL 1.0.2" in tls:
+            check_tls_ver(dev[0], apdev[0],
+                          "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1",
+                          "TLSv1.2")
+    check_tls_ver(dev[1], apdev[0],
+                  "tls_disable_tlsv1_0=1 tls_disable_tlsv1_2=1", "TLSv1.1")
+    check_tls_ver(dev[2], apdev[0],
+                  "tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
+
+def test_rsn_ie_proto_eap_sta(dev, apdev):
+    """RSN element protocol testing for EAP cases on STA side"""
+    bssid = apdev[0]['bssid']
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    # This is the RSN element used normally by hostapd
+    params['own_ie_override'] = '30140100000fac040100000fac040100000fac010c00'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="GPSK",
+                        identity="gpsk user",
+                        password="abcdefghijklmnop0123456789abcdef",
+                        scan_freq="2412")
+
+    tests = [ ('No RSN Capabilities field',
+               '30120100000fac040100000fac040100000fac01'),
+              ('No AKM Suite fields',
+               '300c0100000fac040100000fac04'),
+              ('No Pairwise Cipher Suite fields',
+               '30060100000fac04'),
+              ('No Group Data Cipher Suite field',
+               '30020100') ]
+    for txt,ie in tests:
+        dev[0].request("DISCONNECT")
+        dev[0].wait_disconnected()
+        logger.info(txt)
+        hapd.disable()
+        hapd.set('own_ie_override', ie)
+        hapd.enable()
+        dev[0].request("BSS_FLUSH 0")
+        dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
+        dev[0].select_network(id, freq=2412)
+        dev[0].wait_connected()
+
+def check_tls_session_resumption_capa(dev, hapd):
+    tls = hapd.request("GET tls_library")
+    if not tls.startswith("OpenSSL"):
+        raise HwsimSkip("hostapd TLS library is not OpenSSL: " + tls)
+
+    tls = dev.request("GET tls_library")
+    if not tls.startswith("OpenSSL"):
+        raise HwsimSkip("Session resumption not supported with this TLS library: " + tls)
+
+def test_eap_ttls_pap_session_resumption(dev, apdev):
+    """EAP-TTLS/PAP session resumption"""
+    params = int_eap_server_params()
+    params['tls_session_lifetime'] = '60'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    check_tls_session_resumption_capa(dev[0], hapd)
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", eap_workaround='0',
+                phase2="auth=PAP")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the first connection")
+
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("Key handshake with the AP timed out")
+    if dev[0].get_status_field("tls_session_reused") != '1':
+        raise Exception("Session resumption not used on the second connection")
+
+def test_eap_ttls_chap_session_resumption(dev, apdev):
+    """EAP-TTLS/CHAP session resumption"""
+    params = int_eap_server_params()
+    params['tls_session_lifetime'] = '60'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    check_tls_session_resumption_capa(dev[0], hapd)
+    eap_connect(dev[0], apdev[0], "TTLS", "chap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.der", phase2="auth=CHAP")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the first connection")
+
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("Key handshake with the AP timed out")
+    if dev[0].get_status_field("tls_session_reused") != '1':
+        raise Exception("Session resumption not used on the second connection")
+
+def test_eap_ttls_mschap_session_resumption(dev, apdev):
+    """EAP-TTLS/MSCHAP session resumption"""
+    params = int_eap_server_params()
+    params['tls_session_lifetime'] = '60'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    check_tls_session_resumption_capa(dev[0], hapd)
+    eap_connect(dev[0], apdev[0], "TTLS", "mschap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+                domain_suffix_match="server.w1.fi")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the first connection")
+
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("Key handshake with the AP timed out")
+    if dev[0].get_status_field("tls_session_reused") != '1':
+        raise Exception("Session resumption not used on the second connection")
+
+def test_eap_ttls_mschapv2_session_resumption(dev, apdev):
+    """EAP-TTLS/MSCHAPv2 session resumption"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = int_eap_server_params()
+    params['tls_session_lifetime'] = '60'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    check_tls_session_resumption_capa(dev[0], hapd)
+    eap_connect(dev[0], apdev[0], "TTLS", "DOMAIN\mschapv2 user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                domain_suffix_match="server.w1.fi")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the first connection")
+
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("Key handshake with the AP timed out")
+    if dev[0].get_status_field("tls_session_reused") != '1':
+        raise Exception("Session resumption not used on the second connection")
+
+def test_eap_ttls_eap_gtc_session_resumption(dev, apdev):
+    """EAP-TTLS/EAP-GTC session resumption"""
+    params = int_eap_server_params()
+    params['tls_session_lifetime'] = '60'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    check_tls_session_resumption_capa(dev[0], hapd)
+    eap_connect(dev[0], apdev[0], "TTLS", "user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="autheap=GTC")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the first connection")
+
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("Key handshake with the AP timed out")
+    if dev[0].get_status_field("tls_session_reused") != '1':
+        raise Exception("Session resumption not used on the second connection")
+
+def test_eap_ttls_no_session_resumption(dev, apdev):
+    """EAP-TTLS session resumption disabled on server"""
+    params = int_eap_server_params()
+    params['tls_session_lifetime'] = '0'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TTLS", "pap user",
+                anonymous_identity="ttls", password="password",
+                ca_cert="auth_serv/ca.pem", eap_workaround='0',
+                phase2="auth=PAP")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the first connection")
+
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("Key handshake with the AP timed out")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the second connection")
+
+def test_eap_peap_session_resumption(dev, apdev):
+    """EAP-PEAP session resumption"""
+    params = int_eap_server_params()
+    params['tls_session_lifetime'] = '60'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    check_tls_session_resumption_capa(dev[0], hapd)
+    eap_connect(dev[0], apdev[0], "PEAP", "user",
+                anonymous_identity="peap", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the first connection")
+
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("Key handshake with the AP timed out")
+    if dev[0].get_status_field("tls_session_reused") != '1':
+        raise Exception("Session resumption not used on the second connection")
+
+def test_eap_peap_no_session_resumption(dev, apdev):
+    """EAP-PEAP session resumption disabled on server"""
+    params = int_eap_server_params()
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PEAP", "user",
+                anonymous_identity="peap", password="password",
+                ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the first connection")
+
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("Key handshake with the AP timed out")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the second connection")
+
+def test_eap_tls_session_resumption(dev, apdev):
+    """EAP-TLS session resumption"""
+    params = int_eap_server_params()
+    params['tls_session_lifetime'] = '60'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    check_tls_session_resumption_capa(dev[0], hapd)
+    eap_connect(dev[0], apdev[0], "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+                client_cert="auth_serv/user.pem",
+                private_key="auth_serv/user.key")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the first connection")
+
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("Key handshake with the AP timed out")
+    if dev[0].get_status_field("tls_session_reused") != '1':
+        raise Exception("Session resumption not used on the second connection")
+
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("Key handshake with the AP timed out")
+    if dev[0].get_status_field("tls_session_reused") != '1':
+        raise Exception("Session resumption not used on the third connection")
+
+def test_eap_tls_session_resumption_expiration(dev, apdev):
+    """EAP-TLS session resumption"""
+    params = int_eap_server_params()
+    params['tls_session_lifetime'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    check_tls_session_resumption_capa(dev[0], hapd)
+    eap_connect(dev[0], apdev[0], "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+                client_cert="auth_serv/user.pem",
+                private_key="auth_serv/user.key")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the first connection")
+
+    # Allow multiple attempts since OpenSSL may not expire the cached entry
+    # immediately.
+    for i in range(10):
+        time.sleep(1.2)
+
+        dev[0].request("REAUTHENTICATE")
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+        if ev is None:
+            raise Exception("EAP success timed out")
+        ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+        if ev is None:
+            raise Exception("Key handshake with the AP timed out")
+        if dev[0].get_status_field("tls_session_reused") == '0':
+            break
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Session resumption used after lifetime expiration")
+
+def test_eap_tls_no_session_resumption(dev, apdev):
+    """EAP-TLS session resumption disabled on server"""
+    params = int_eap_server_params()
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+                client_cert="auth_serv/user.pem",
+                private_key="auth_serv/user.key")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the first connection")
+
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("Key handshake with the AP timed out")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the second connection")
+
+def test_eap_tls_session_resumption_radius(dev, apdev):
+    """EAP-TLS session resumption (RADIUS)"""
+    params = { "ssid": "as", "beacon_int": "2000",
+               "radius_server_clients": "auth_serv/radius_clients.conf",
+               "radius_server_auth_port": '18128',
+               "eap_server": "1",
+               "eap_user_file": "auth_serv/eap_user.conf",
+               "ca_cert": "auth_serv/ca.pem",
+               "server_cert": "auth_serv/server.pem",
+               "private_key": "auth_serv/server.key",
+               "tls_session_lifetime": "60" }
+    authsrv = hostapd.add_ap(apdev[1]['ifname'], params)
+    check_tls_session_resumption_capa(dev[0], authsrv)
+
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params['auth_server_port'] = "18128"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+                client_cert="auth_serv/user.pem",
+                private_key="auth_serv/user.key")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the first connection")
+
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("Key handshake with the AP timed out")
+    if dev[0].get_status_field("tls_session_reused") != '1':
+        raise Exception("Session resumption not used on the second connection")
+
+def test_eap_tls_no_session_resumption_radius(dev, apdev):
+    """EAP-TLS session resumption disabled (RADIUS)"""
+    params = { "ssid": "as", "beacon_int": "2000",
+               "radius_server_clients": "auth_serv/radius_clients.conf",
+               "radius_server_auth_port": '18128',
+               "eap_server": "1",
+               "eap_user_file": "auth_serv/eap_user.conf",
+               "ca_cert": "auth_serv/ca.pem",
+               "server_cert": "auth_serv/server.pem",
+               "private_key": "auth_serv/server.key",
+               "tls_session_lifetime": "0" }
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params['auth_server_port'] = "18128"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+                client_cert="auth_serv/user.pem",
+                private_key="auth_serv/user.key")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the first connection")
+
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("Key handshake with the AP timed out")
+    if dev[0].get_status_field("tls_session_reused") != '0':
+        raise Exception("Unexpected session resumption on the second connection")
diff --git a/hostap/tests/hwsim/test_ap_ft.py b/hostap/tests/hwsim/test_ap_ft.py
new file mode 100644
index 0000000..f95966c
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_ft.py
@@ -0,0 +1,983 @@
+# Fast BSS Transition tests
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import os
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import hostapd
+from utils import HwsimSkip, alloc_fail, fail_test, skip_with_fips
+from wlantest import Wlantest
+from test_ap_psk import check_mib, find_wpas_process, read_process_memory, verify_not_present, get_key_locations
+
+def ft_base_rsn():
+    params = { "wpa": "2",
+               "wpa_key_mgmt": "FT-PSK",
+               "rsn_pairwise": "CCMP" }
+    return params
+
+def ft_base_mixed():
+    params = { "wpa": "3",
+               "wpa_key_mgmt": "WPA-PSK FT-PSK",
+               "wpa_pairwise": "TKIP",
+               "rsn_pairwise": "CCMP" }
+    return params
+
+def ft_params(rsn=True, ssid=None, passphrase=None):
+    if rsn:
+        params = ft_base_rsn()
+    else:
+        params = ft_base_mixed()
+    if ssid:
+        params["ssid"] = ssid
+    if passphrase:
+        params["wpa_passphrase"] = passphrase
+
+    params["mobility_domain"] = "a1b2"
+    params["r0_key_lifetime"] = "10000"
+    params["pmk_r1_push"] = "1"
+    params["reassociation_deadline"] = "1000"
+    return params
+
+def ft_params1(rsn=True, ssid=None, passphrase=None):
+    params = ft_params(rsn, ssid, passphrase)
+    params['nas_identifier'] = "nas1.w1.fi"
+    params['r1_key_holder'] = "000102030405"
+    params['r0kh'] = [ "02:00:00:00:03:00 nas1.w1.fi 100102030405060708090a0b0c0d0e0f",
+                       "02:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f" ]
+    params['r1kh'] = "02:00:00:00:04:00 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f"
+    return params
+
+def ft_params2(rsn=True, ssid=None, passphrase=None):
+    params = ft_params(rsn, ssid, passphrase)
+    params['nas_identifier'] = "nas2.w1.fi"
+    params['r1_key_holder'] = "000102030406"
+    params['r0kh'] = [ "02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f",
+                       "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f" ]
+    params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0e0f"
+    return params
+
+def ft_params1_r0kh_mismatch(rsn=True, ssid=None, passphrase=None):
+    params = ft_params(rsn, ssid, passphrase)
+    params['nas_identifier'] = "nas1.w1.fi"
+    params['r1_key_holder'] = "000102030405"
+    params['r0kh'] = [ "02:00:00:00:03:00 nas1.w1.fi 100102030405060708090a0b0c0d0e0f",
+                       "12:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f" ]
+    params['r1kh'] = "12:00:00:00:04:00 10:01:02:03:04:06 200102030405060708090a0b0c0d0e0f"
+    return params
+
+def ft_params2_incorrect_rrb_key(rsn=True, ssid=None, passphrase=None):
+    params = ft_params(rsn, ssid, passphrase)
+    params['nas_identifier'] = "nas2.w1.fi"
+    params['r1_key_holder'] = "000102030406"
+    params['r0kh'] = [ "02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0ef1",
+                       "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0ef2" ]
+    params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0ef3"
+    return params
+
+def ft_params2_r0kh_mismatch(rsn=True, ssid=None, passphrase=None):
+    params = ft_params(rsn, ssid, passphrase)
+    params['nas_identifier'] = "nas2.w1.fi"
+    params['r1_key_holder'] = "000102030406"
+    params['r0kh'] = [ "12:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f",
+                       "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f" ]
+    params['r1kh'] = "12:00:00:00:03:00 10:01:02:03:04:05 300102030405060708090a0b0c0d0e0f"
+    return params
+
+def run_roams(dev, apdev, hapd0, hapd1, ssid, passphrase, over_ds=False,
+              sae=False, eap=False, fail_test=False, roams=1,
+              pairwise_cipher="CCMP", group_cipher="TKIP CCMP"):
+    logger.info("Connect to first AP")
+    if eap:
+        dev.connect(ssid, key_mgmt="FT-EAP", proto="WPA2", ieee80211w="1",
+                    eap="GPSK", identity="gpsk user",
+                    password="abcdefghijklmnop0123456789abcdef",
+                    scan_freq="2412",
+                    pairwise=pairwise_cipher, group=group_cipher)
+    else:
+        if sae:
+            key_mgmt="FT-SAE"
+        else:
+            key_mgmt="FT-PSK"
+        dev.connect(ssid, psk=passphrase, key_mgmt=key_mgmt, proto="WPA2",
+                    ieee80211w="1", scan_freq="2412",
+                    pairwise=pairwise_cipher, group=group_cipher)
+    if dev.get_status_field('bssid') == apdev[0]['bssid']:
+        ap1 = apdev[0]
+        ap2 = apdev[1]
+        hapd1ap = hapd0
+        hapd2ap = hapd1
+    else:
+        ap1 = apdev[1]
+        ap2 = apdev[0]
+        hapd1ap = hapd1
+        hapd2ap = hapd0
+    hwsim_utils.test_connectivity(dev, hapd1ap)
+
+    dev.scan_for_bss(ap2['bssid'], freq="2412")
+
+    for i in range(0, roams):
+        logger.info("Roam to the second AP")
+        if over_ds:
+            dev.roam_over_ds(ap2['bssid'], fail_test=fail_test)
+        else:
+            dev.roam(ap2['bssid'], fail_test=fail_test)
+        if fail_test:
+            return
+        if dev.get_status_field('bssid') != ap2['bssid']:
+            raise Exception("Did not connect to correct AP")
+        if i == 0 or i == roams - 1:
+            hwsim_utils.test_connectivity(dev, hapd2ap)
+
+        logger.info("Roam back to the first AP")
+        if over_ds:
+            dev.roam_over_ds(ap1['bssid'])
+        else:
+            dev.roam(ap1['bssid'])
+        if dev.get_status_field('bssid') != ap1['bssid']:
+            raise Exception("Did not connect to correct AP")
+        if i == 0 or i == roams - 1:
+            hwsim_utils.test_connectivity(dev, hapd1ap)
+
+def test_ap_ft(dev, apdev):
+    """WPA2-PSK-FT AP"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase)
+    if "[WPA2-FT/PSK-CCMP]" not in dev[0].request("SCAN_RESULTS"):
+        raise Exception("Scan results missing RSN element info")
+
+def test_ap_ft_many(dev, apdev):
+    """WPA2-PSK-FT AP multiple times"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, roams=50)
+
+def test_ap_ft_mixed(dev, apdev):
+    """WPA2-PSK-FT mixed-mode AP"""
+    ssid = "test-ft-mixed"
+    passphrase="12345678"
+
+    params = ft_params1(rsn=False, ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    key_mgmt = hapd.get_config()['key_mgmt']
+    vals = key_mgmt.split(' ')
+    if vals[0] != "WPA-PSK" or vals[1] != "FT-PSK":
+        raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+    params = ft_params2(rsn=False, ssid=ssid, passphrase=passphrase)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd, hapd1, ssid, passphrase)
+
+def test_ap_ft_pmf(dev, apdev):
+    """WPA2-PSK-FT AP with PMF"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase)
+
+def test_ap_ft_over_ds(dev, apdev):
+    """WPA2-PSK-FT AP over DS"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True)
+    check_mib(dev[0], [ ("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-4"),
+                        ("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-4") ])
+
+def test_ap_ft_over_ds_many(dev, apdev):
+    """WPA2-PSK-FT AP over DS multiple times"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+              roams=50)
+
+def test_ap_ft_over_ds_unknown_target(dev, apdev):
+    """WPA2-PSK-FT AP"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                   scan_freq="2412")
+    dev[0].roam_over_ds("02:11:22:33:44:55", fail_test=True)
+
+def test_ap_ft_over_ds_unexpected(dev, apdev):
+    """WPA2-PSK-FT AP over DS and unexpected response"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                   scan_freq="2412")
+    if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
+        ap1 = apdev[0]
+        ap2 = apdev[1]
+        hapd1ap = hapd0
+        hapd2ap = hapd1
+    else:
+        ap1 = apdev[1]
+        ap2 = apdev[0]
+        hapd1ap = hapd1
+        hapd2ap = hapd0
+
+    addr = dev[0].own_addr()
+    hapd1ap.set("ext_mgmt_frame_handling", "1")
+    logger.info("Foreign STA address")
+    msg = {}
+    msg['fc'] = 13 << 4
+    msg['da'] = addr
+    msg['sa'] = ap1['bssid']
+    msg['bssid'] = ap1['bssid']
+    msg['payload'] = binascii.unhexlify("06021122334455660102030405060000")
+    hapd1ap.mgmt_tx(msg)
+
+    logger.info("No over-the-DS in progress")
+    msg['payload'] = binascii.unhexlify("0602" + addr.replace(':', '') + "0102030405060000")
+    hapd1ap.mgmt_tx(msg)
+
+    logger.info("Non-zero status code")
+    msg['payload'] = binascii.unhexlify("0602" + addr.replace(':', '') + "0102030405060100")
+    hapd1ap.mgmt_tx(msg)
+
+    hapd1ap.dump_monitor()
+
+    dev[0].scan_for_bss(ap2['bssid'], freq="2412")
+    if "OK" not in dev[0].request("FT_DS " + ap2['bssid']):
+            raise Exception("FT_DS failed")
+
+    req = hapd1ap.mgmt_rx()
+
+    logger.info("Foreign Target AP")
+    msg['payload'] = binascii.unhexlify("0602" + addr.replace(':', '') + "0102030405060000")
+    hapd1ap.mgmt_tx(msg)
+
+    addrs = addr.replace(':', '') + ap2['bssid'].replace(':', '')
+
+    logger.info("No IEs")
+    msg['payload'] = binascii.unhexlify("0602" + addrs + "0000")
+    hapd1ap.mgmt_tx(msg)
+
+    logger.info("Invalid IEs (trigger parsing failure)")
+    msg['payload'] = binascii.unhexlify("0602" + addrs + "00003700")
+    hapd1ap.mgmt_tx(msg)
+
+    logger.info("Too short MDIE")
+    msg['payload'] = binascii.unhexlify("0602" + addrs + "000036021122")
+    hapd1ap.mgmt_tx(msg)
+
+    logger.info("Mobility domain mismatch")
+    msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603112201")
+    hapd1ap.mgmt_tx(msg)
+
+    logger.info("No FTIE")
+    msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b201")
+    hapd1ap.mgmt_tx(msg)
+
+    logger.info("FTIE SNonce mismatch")
+    msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b201375e0000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "1000000000000000000000000000000000000000000000000000000000000001" + "030a6e6173322e77312e6669")
+    hapd1ap.mgmt_tx(msg)
+
+    logger.info("No R0KH-ID subelem in FTIE")
+    snonce = binascii.hexlify(req['payload'][111:111+32])
+    msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b20137520000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + snonce)
+    hapd1ap.mgmt_tx(msg)
+
+    logger.info("No R0KH-ID subelem mismatch in FTIE")
+    snonce = binascii.hexlify(req['payload'][111:111+32])
+    msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b201375e0000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + snonce + "030a11223344556677889900")
+    hapd1ap.mgmt_tx(msg)
+
+    logger.info("No R1KH-ID subelem in FTIE")
+    r0khid = binascii.hexlify(req['payload'][145:145+10])
+    msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b201375e0000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + snonce + "030a" + r0khid)
+    hapd1ap.mgmt_tx(msg)
+
+    logger.info("No RSNE")
+    r0khid = binascii.hexlify(req['payload'][145:145+10])
+    msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b20137660000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + snonce + "030a" + r0khid + "0106000102030405")
+    hapd1ap.mgmt_tx(msg)
+
+def test_ap_ft_pmf_over_ds(dev, apdev):
+    """WPA2-PSK-FT AP over DS with PMF"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True)
+
+def test_ap_ft_over_ds_pull(dev, apdev):
+    """WPA2-PSK-FT AP over DS (pull PMK)"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["pmk_r1_push"] = "0"
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["pmk_r1_push"] = "0"
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True)
+
+def test_ap_ft_sae(dev, apdev):
+    """WPA2-PSK-FT-SAE AP"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params['wpa_key_mgmt'] = "FT-SAE"
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params['wpa_key_mgmt'] = "FT-SAE"
+    hapd = hostapd.add_ap(apdev[1]['ifname'], params)
+    key_mgmt = hapd.get_config()['key_mgmt']
+    if key_mgmt.split(' ')[0] != "FT-SAE":
+        raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+
+    dev[0].request("SET sae_groups ")
+    run_roams(dev[0], apdev, hapd0, hapd, ssid, passphrase, sae=True)
+
+def test_ap_ft_sae_over_ds(dev, apdev):
+    """WPA2-PSK-FT-SAE AP over DS"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params['wpa_key_mgmt'] = "FT-SAE"
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params['wpa_key_mgmt'] = "FT-SAE"
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].request("SET sae_groups ")
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, sae=True,
+              over_ds=True)
+
+def test_ap_ft_eap(dev, apdev):
+    """WPA2-EAP-FT AP"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    radius = hostapd.radius_params()
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params['wpa_key_mgmt'] = "FT-EAP"
+    params["ieee8021x"] = "1"
+    params = dict(radius.items() + params.items())
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    key_mgmt = hapd.get_config()['key_mgmt']
+    if key_mgmt.split(' ')[0] != "FT-EAP":
+        raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params['wpa_key_mgmt'] = "FT-EAP"
+    params["ieee8021x"] = "1"
+    params = dict(radius.items() + params.items())
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd, hapd1, ssid, passphrase, eap=True)
+    if "[WPA2-FT/EAP-CCMP]" not in dev[0].request("SCAN_RESULTS"):
+        raise Exception("Scan results missing RSN element info")
+    check_mib(dev[0], [ ("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-3"),
+                        ("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-3") ])
+
+def test_ap_ft_eap_pull(dev, apdev):
+    """WPA2-EAP-FT AP (pull PMK)"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    radius = hostapd.radius_params()
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params['wpa_key_mgmt'] = "FT-EAP"
+    params["ieee8021x"] = "1"
+    params["pmk_r1_push"] = "0"
+    params = dict(radius.items() + params.items())
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    key_mgmt = hapd.get_config()['key_mgmt']
+    if key_mgmt.split(' ')[0] != "FT-EAP":
+        raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params['wpa_key_mgmt'] = "FT-EAP"
+    params["ieee8021x"] = "1"
+    params["pmk_r1_push"] = "0"
+    params = dict(radius.items() + params.items())
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd, hapd1, ssid, passphrase, eap=True)
+
+def test_ap_ft_mismatching_rrb_key_push(dev, apdev):
+    """WPA2-PSK-FT AP over DS with mismatching RRB key (push)"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2_incorrect_rrb_key(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+              fail_test=True)
+
+def test_ap_ft_mismatching_rrb_key_pull(dev, apdev):
+    """WPA2-PSK-FT AP over DS with mismatching RRB key (pull)"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["pmk_r1_push"] = "0"
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2_incorrect_rrb_key(ssid=ssid, passphrase=passphrase)
+    params["pmk_r1_push"] = "0"
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+              fail_test=True)
+
+def test_ap_ft_mismatching_r0kh_id_pull(dev, apdev):
+    """WPA2-PSK-FT AP over DS with mismatching R0KH-ID (pull)"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["pmk_r1_push"] = "0"
+    params["nas_identifier"] = "nas0.w1.fi"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                   scan_freq="2412")
+
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["pmk_r1_push"] = "0"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+    dev[0].roam_over_ds(apdev[1]['bssid'], fail_test=True)
+
+def test_ap_ft_mismatching_rrb_r0kh_push(dev, apdev):
+    """WPA2-PSK-FT AP over DS with mismatching R0KH key (push)"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2_r0kh_mismatch(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+              fail_test=True)
+
+def test_ap_ft_mismatching_rrb_r0kh_pull(dev, apdev):
+    """WPA2-PSK-FT AP over DS with mismatching R0KH key (pull)"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1_r0kh_mismatch(ssid=ssid, passphrase=passphrase)
+    params["pmk_r1_push"] = "0"
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["pmk_r1_push"] = "0"
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+              fail_test=True)
+
+def test_ap_ft_gtk_rekey(dev, apdev):
+    """WPA2-PSK-FT AP and GTK rekey"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params['wpa_group_rekey'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                   ieee80211w="1", scan_freq="2412")
+
+    ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+    if ev is None:
+        raise Exception("GTK rekey timed out after initial association")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params['wpa_group_rekey'] = '1'
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+    dev[0].roam(apdev[1]['bssid'])
+    if dev[0].get_status_field('bssid') != apdev[1]['bssid']:
+        raise Exception("Did not connect to correct AP")
+    hwsim_utils.test_connectivity(dev[0], hapd1)
+
+    ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+    if ev is None:
+        raise Exception("GTK rekey timed out after FT protocol")
+    hwsim_utils.test_connectivity(dev[0], hapd1)
+
+def test_ft_psk_key_lifetime_in_memory(dev, apdev, params):
+    """WPA2-PSK-FT and key lifetime in memory"""
+    ssid = "test-ft"
+    passphrase="04c2726b4b8d5f1b4db9c07aa4d9e9d8f765cb5d25ec817e6cc4fcdd5255db0"
+    psk = '93c90846ff67af9037ed83fb72b63dbeddaa81d47f926c20909b5886f1d9358d'
+    pmk = binascii.unhexlify(psk)
+    p = ft_params1(ssid=ssid, passphrase=passphrase)
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], p)
+    p = ft_params2(ssid=ssid, passphrase=passphrase)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], p)
+
+    pid = find_wpas_process(dev[0])
+
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                   scan_freq="2412")
+    time.sleep(1)
+
+    buf = read_process_memory(pid, pmk)
+
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+
+    dev[0].relog()
+    pmkr0 = None
+    pmkr1 = None
+    ptk = None
+    gtk = None
+    with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
+        for l in f.readlines():
+            if "FT: PMK-R0 - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                pmkr0 = binascii.unhexlify(val)
+            if "FT: PMK-R1 - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                pmkr1 = binascii.unhexlify(val)
+            if "FT: KCK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                kck = binascii.unhexlify(val)
+            if "FT: KEK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                kek = binascii.unhexlify(val)
+            if "FT: TK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                tk = binascii.unhexlify(val)
+            if "WPA: Group Key - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                gtk = binascii.unhexlify(val)
+    if not pmkr0 or not pmkr1 or not kck or not kek or not tk or not gtk:
+        raise Exception("Could not find keys from debug log")
+    if len(gtk) != 16:
+        raise Exception("Unexpected GTK length")
+
+    logger.info("Checking keys in memory while associated")
+    get_key_locations(buf, pmk, "PMK")
+    get_key_locations(buf, pmkr0, "PMK-R0")
+    get_key_locations(buf, pmkr1, "PMK-R1")
+    if pmk not in buf:
+        raise HwsimSkip("PMK not found while associated")
+    if pmkr0 not in buf:
+        raise HwsimSkip("PMK-R0 not found while associated")
+    if pmkr1 not in buf:
+        raise HwsimSkip("PMK-R1 not found while associated")
+    if kck not in buf:
+        raise Exception("KCK not found while associated")
+    if kek not in buf:
+        raise Exception("KEK not found while associated")
+    if tk in buf:
+        raise Exception("TK found from memory")
+    if gtk in buf:
+        raise Exception("GTK found from memory")
+
+    logger.info("Checking keys in memory after disassociation")
+    buf = read_process_memory(pid, pmk)
+    get_key_locations(buf, pmk, "PMK")
+    get_key_locations(buf, pmkr0, "PMK-R0")
+    get_key_locations(buf, pmkr1, "PMK-R1")
+
+    # Note: PMK/PSK is still present in network configuration
+
+    fname = os.path.join(params['logdir'],
+                         'ft_psk_key_lifetime_in_memory.memctx-')
+    verify_not_present(buf, pmkr0, fname, "PMK-R0")
+    verify_not_present(buf, pmkr1, fname, "PMK-R1")
+    verify_not_present(buf, kck, fname, "KCK")
+    verify_not_present(buf, kek, fname, "KEK")
+    verify_not_present(buf, tk, fname, "TK")
+    verify_not_present(buf, gtk, fname, "GTK")
+
+    dev[0].request("REMOVE_NETWORK all")
+
+    logger.info("Checking keys in memory after network profile removal")
+    buf = read_process_memory(pid, pmk)
+    get_key_locations(buf, pmk, "PMK")
+    get_key_locations(buf, pmkr0, "PMK-R0")
+    get_key_locations(buf, pmkr1, "PMK-R1")
+
+    verify_not_present(buf, pmk, fname, "PMK")
+    verify_not_present(buf, pmkr0, fname, "PMK-R0")
+    verify_not_present(buf, pmkr1, fname, "PMK-R1")
+    verify_not_present(buf, kck, fname, "KCK")
+    verify_not_present(buf, kek, fname, "KEK")
+    verify_not_present(buf, tk, fname, "TK")
+    verify_not_present(buf, gtk, fname, "GTK")
+
+def test_ap_ft_invalid_resp(dev, apdev):
+    """WPA2-PSK-FT AP and invalid response IEs"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                   scan_freq="2412")
+
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    tests = [
+        # Various IEs for test coverage. The last one is FTIE with invalid
+        # R1KH-ID subelement.
+        "020002000000" + "3800" + "38051122334455" + "3754000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010100",
+        # FTIE with invalid R0KH-ID subelement (len=0).
+        "020002000000" + "3754000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010300",
+        # FTIE with invalid R0KH-ID subelement (len=49).
+        "020002000000" + "378500010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001033101020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849",
+        # Invalid RSNE.
+        "020002000000" + "3000",
+        # Required IEs missing from protected IE count.
+        "020002000000" + "3603a1b201" + "375200010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001" + "3900",
+        # RIC missing from protected IE count.
+        "020002000000" + "3603a1b201" + "375200020203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001" + "3900",
+        # Protected IE missing.
+        "020002000000" + "3603a1b201" + "375200ff0203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001" + "3900" + "0000" ]
+    for t in tests:
+        dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+        hapd1.set("ext_mgmt_frame_handling", "1")
+        hapd1.dump_monitor()
+        if "OK" not in dev[0].request("ROAM " + apdev[1]['bssid']):
+            raise Exception("ROAM failed")
+        auth = None
+        for i in range(20):
+            msg = hapd1.mgmt_rx()
+            if msg['subtype'] == 11:
+                auth = msg
+                break
+        if not auth:
+            raise Exception("Authentication frame not seen")
+
+        resp = {}
+        resp['fc'] = auth['fc']
+        resp['da'] = auth['sa']
+        resp['sa'] = auth['da']
+        resp['bssid'] = auth['bssid']
+        resp['payload'] = binascii.unhexlify(t)
+        hapd1.mgmt_tx(resp)
+        hapd1.set("ext_mgmt_frame_handling", "0")
+        dev[0].wait_disconnected()
+
+        dev[0].request("RECONNECT")
+        dev[0].wait_connected()
+
+def test_ap_ft_gcmp_256(dev, apdev):
+    """WPA2-PSK-FT AP with GCMP-256 cipher"""
+    if "GCMP-256" not in dev[0].get_capability("pairwise"):
+        raise HwsimSkip("Cipher GCMP-256 not supported")
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params['rsn_pairwise'] = "GCMP-256"
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params['rsn_pairwise'] = "GCMP-256"
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
+              pairwise_cipher="GCMP-256", group_cipher="GCMP-256")
+
+def test_ap_ft_oom(dev, apdev):
+    """WPA2-PSK-FT and OOM"""
+    skip_with_fips(dev[0])
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                   scan_freq="2412")
+    if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
+        dst = apdev[1]['bssid']
+    else:
+        dst = apdev[0]['bssid']
+
+    dev[0].scan_for_bss(dst, freq="2412")
+    with alloc_fail(dev[0], 1, "wpa_ft_gen_req_ies"):
+        dev[0].roam(dst)
+    with alloc_fail(dev[0], 1, "wpa_ft_mic"):
+        dev[0].roam(dst, fail_test=True)
+    with fail_test(dev[0], 1, "os_get_random;wpa_ft_prepare_auth_request"):
+        dev[0].roam(dst, fail_test=True)
+
+def test_ap_ft_over_ds_proto(dev, apdev):
+    """WPA2-PSK-FT AP over DS protocol testing"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                   scan_freq="2412")
+
+    # FT Action Response while no FT-over-DS in progress
+    msg = {}
+    msg['fc'] = 13 << 4
+    msg['da'] = dev[0].own_addr()
+    msg['sa'] = apdev[0]['bssid']
+    msg['bssid'] = apdev[0]['bssid']
+    msg['payload'] = binascii.unhexlify("06020200000000000200000004000000")
+    hapd0.mgmt_tx(msg)
+
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+    dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+    hapd0.set("ext_mgmt_frame_handling", "1")
+    hapd0.dump_monitor()
+    dev[0].request("FT_DS " + apdev[1]['bssid'])
+    for i in range(0, 10):
+        req = hapd0.mgmt_rx()
+        if req is None:
+            raise Exception("MGMT RX wait timed out")
+        if req['subtype'] == 13:
+            break
+        req = None
+    if not req:
+        raise Exception("FT Action frame not received")
+
+    # FT Action Response for unexpected Target AP
+    msg['payload'] = binascii.unhexlify("0602020000000000" + "f20000000400" + "0000")
+    hapd0.mgmt_tx(msg)
+
+    # FT Action Response without MDIE
+    msg['payload'] = binascii.unhexlify("0602020000000000" + "020000000400" + "0000")
+    hapd0.mgmt_tx(msg)
+
+    # FT Action Response without FTIE
+    msg['payload'] = binascii.unhexlify("0602020000000000" + "020000000400" + "0000" + "3603a1b201")
+    hapd0.mgmt_tx(msg)
+
+    # FT Action Response with FTIE SNonce mismatch
+    msg['payload'] = binascii.unhexlify("0602020000000000" + "020000000400" + "0000" + "3603a1b201" + "3766000000000000000000000000000000000000c4e67ac1999bebd00ff4ae4d5dcaf87896bb060b469f7c78d49623fb395c3455ffffff6b693fe6f8d8c5dfac0a22344750775bd09437f98b238c9f87b97f790c0106000102030406030a6e6173312e77312e6669")
+    hapd0.mgmt_tx(msg)
+
+def test_ap_ft_rrb(dev, apdev):
+    """WPA2-PSK-FT RRB protocol testing"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                   scan_freq="2412")
+
+    _dst_ll = binascii.unhexlify(apdev[0]['bssid'].replace(':',''))
+    _src_ll = binascii.unhexlify(dev[0].own_addr().replace(':',''))
+    proto = '\x89\x0d'
+    ehdr = _dst_ll + _src_ll + proto
+
+    # Too short RRB frame
+    pkt = ehdr + '\x01'
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # RRB discarded frame wikth unrecognized type
+    pkt = ehdr + '\x02' + '\x02' + '\x01\x00' + _src_ll
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # RRB frame too short for action frame
+    pkt = ehdr + '\x01' + '\x02' + '\x01\x00' + _src_ll
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Too short RRB frame (not enough room for Action Frame body)
+    pkt = ehdr + '\x01' + '\x02' + '\x00\x00' + _src_ll
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Unexpected Action frame category
+    pkt = ehdr + '\x01' + '\x02' + '\x0e\x00' + _src_ll + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Unexpected Action in RRB Request
+    pkt = ehdr + '\x01' + '\x00' + '\x0e\x00' + _src_ll + '\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Target AP address in RRB Request does not match with own address
+    pkt = ehdr + '\x01' + '\x00' + '\x0e\x00' + _src_ll + '\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Not enough room for status code in RRB Response
+    pkt = ehdr + '\x01' + '\x01' + '\x0e\x00' + _src_ll + '\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # RRB discarded frame with unknown packet_type
+    pkt = ehdr + '\x01' + '\x02' + '\x0e\x00' + _src_ll + '\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # RRB Response with non-zero status code; no STA match
+    pkt = ehdr + '\x01' + '\x01' + '\x10\x00' + _src_ll + '\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\xff\xff'
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # RRB Response with zero status code and extra data; STA match
+    pkt = ehdr + '\x01' + '\x01' + '\x11\x00' + _src_ll + '\x06\x01' + _src_ll + '\x00\x00\x00\x00\x00\x00' + '\x00\x00' + '\x00'
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Too short PMK-R1 pull
+    pkt = ehdr + '\x01' + '\xc8' + '\x0e\x00' + _src_ll + '\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Too short PMK-R1 resp
+    pkt = ehdr + '\x01' + '\xc9' + '\x0e\x00' + _src_ll + '\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Too short PMK-R1 push
+    pkt = ehdr + '\x01' + '\xca' + '\x0e\x00' + _src_ll + '\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # No matching R0KH address found for PMK-R0 pull response
+    pkt = ehdr + '\x01' + '\xc9' + '\x5a\x00' + _src_ll + '\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + 76*'\00'
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+def test_rsn_ie_proto_ft_psk_sta(dev, apdev):
+    """RSN element protocol testing for FT-PSK + PMF cases on STA side"""
+    bssid = apdev[0]['bssid']
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "1";
+    # This is the RSN element used normally by hostapd
+    params['own_ie_override'] = '30140100000fac040100000fac040100000fac048c00' + '3603a1b201'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    id = dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                        ieee80211w="1", scan_freq="2412",
+                        pairwise="CCMP", group="CCMP")
+
+    tests = [ ('PMKIDCount field included',
+               '30160100000fac040100000fac040100000fac048c000000' + '3603a1b201'),
+              ('Extra IE before RSNE',
+               'dd0400000000' + '30140100000fac040100000fac040100000fac048c00' + '3603a1b201'),
+              ('PMKIDCount and Group Management Cipher suite fields included',
+               '301a0100000fac040100000fac040100000fac048c000000000fac06' + '3603a1b201'),
+              ('Extra octet after defined fields (future extensibility)',
+               '301b0100000fac040100000fac040100000fac048c000000000fac0600' + '3603a1b201'),
+              ('No RSN Capabilities field (PMF disabled in practice)',
+               '30120100000fac040100000fac040100000fac04' + '3603a1b201') ]
+    for txt,ie in tests:
+        dev[0].request("DISCONNECT")
+        dev[0].wait_disconnected()
+        logger.info(txt)
+        hapd.disable()
+        hapd.set('own_ie_override', ie)
+        hapd.enable()
+        dev[0].request("BSS_FLUSH 0")
+        dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
+        dev[0].select_network(id, freq=2412)
+        dev[0].wait_connected()
+
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+
+    logger.info('Invalid RSNE causing internal hostapd error')
+    hapd.disable()
+    hapd.set('own_ie_override', '30130100000fac040100000fac040100000fac048c' + '3603a1b201')
+    hapd.enable()
+    dev[0].request("BSS_FLUSH 0")
+    dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
+    dev[0].select_network(id, freq=2412)
+    # hostapd fails to generate EAPOL-Key msg 3/4, so this connection cannot
+    # complete.
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected connection")
+    dev[0].request("DISCONNECT")
+
+    logger.info('Unexpected PMKID causing internal hostapd error')
+    hapd.disable()
+    hapd.set('own_ie_override', '30260100000fac040100000fac040100000fac048c000100ffffffffffffffffffffffffffffffff' + '3603a1b201')
+    hapd.enable()
+    dev[0].request("BSS_FLUSH 0")
+    dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
+    dev[0].select_network(id, freq=2412)
+    # hostapd fails to generate EAPOL-Key msg 3/4, so this connection cannot
+    # complete.
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected connection")
+    dev[0].request("DISCONNECT")
diff --git a/hostap/tests/hwsim/test_ap_hs20.py b/hostap/tests/hwsim/test_ap_hs20.py
new file mode 100644
index 0000000..27f9e87
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_hs20.py
@@ -0,0 +1,3570 @@
+# Hotspot 2.0 tests
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import struct
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+import os
+import os.path
+import socket
+import subprocess
+
+import hostapd
+from utils import HwsimSkip, skip_with_fips
+import hwsim_utils
+from tshark import run_tshark
+from wlantest import Wlantest
+from wpasupplicant import WpaSupplicant
+from test_ap_eap import check_eap_capa, check_domain_match_full
+
+def hs20_ap_params(ssid="test-hs20"):
+    params = hostapd.wpa2_params(ssid=ssid)
+    params['wpa_key_mgmt'] = "WPA-EAP"
+    params['ieee80211w'] = "1"
+    params['ieee8021x'] = "1"
+    params['auth_server_addr'] = "127.0.0.1"
+    params['auth_server_port'] = "1812"
+    params['auth_server_shared_secret'] = "radius"
+    params['interworking'] = "1"
+    params['access_network_type'] = "14"
+    params['internet'] = "1"
+    params['asra'] = "0"
+    params['esr'] = "0"
+    params['uesa'] = "0"
+    params['venue_group'] = "7"
+    params['venue_type'] = "1"
+    params['venue_name'] = [ "eng:Example venue", "fin:Esimerkkipaikka" ]
+    params['roaming_consortium'] = [ "112233", "1020304050", "010203040506",
+                                     "fedcba" ]
+    params['domain_name'] = "example.com,another.example.com"
+    params['nai_realm'] = [ "0,example.com,13[5:6],21[2:4][5:7]",
+                            "0,another.example.com" ]
+    params['hs20'] = "1"
+    params['hs20_wan_metrics'] = "01:8000:1000:80:240:3000"
+    params['hs20_conn_capab'] = [ "1:0:2", "6:22:1", "17:5060:0" ]
+    params['hs20_operating_class'] = "5173"
+    params['anqp_3gpp_cell_net'] = "244,91"
+    return params
+
+def check_auto_select(dev, bssid):
+    dev.scan_for_bss(bssid, freq="2412")
+    dev.request("INTERWORKING_SELECT auto freq=2412")
+    ev = dev.wait_connected(timeout=15)
+    if bssid not in ev:
+        raise Exception("Connected to incorrect network")
+    dev.request("REMOVE_NETWORK all")
+    dev.wait_disconnected()
+
+def interworking_select(dev, bssid, type=None, no_match=False, freq=None):
+    dev.dump_monitor()
+    if bssid and freq and not no_match:
+        dev.scan_for_bss(bssid, freq=freq)
+    freq_extra = " freq=" + str(freq) if freq else ""
+    dev.request("INTERWORKING_SELECT" + freq_extra)
+    ev = dev.wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH"],
+                        timeout=15)
+    if ev is None:
+        raise Exception("Network selection timed out");
+    if no_match:
+        if "INTERWORKING-NO-MATCH" not in ev:
+            raise Exception("Unexpected network match")
+        return
+    if "INTERWORKING-NO-MATCH" in ev:
+        logger.info("Matching network not found - try again")
+        dev.dump_monitor()
+        dev.request("INTERWORKING_SELECT" + freq_extra)
+        ev = dev.wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH"],
+                            timeout=15)
+        if ev is None:
+            raise Exception("Network selection timed out");
+        if "INTERWORKING-NO-MATCH" in ev:
+            raise Exception("Matching network not found")
+    if bssid and bssid not in ev:
+        raise Exception("Unexpected BSSID in match")
+    if type and "type=" + type not in ev:
+        raise Exception("Network type not recognized correctly")
+
+def check_sp_type(dev, sp_type):
+    type = dev.get_status_field("sp_type")
+    if type is None:
+        raise Exception("sp_type not available")
+    if type != sp_type:
+        raise Exception("sp_type did not indicate home network")
+
+def hlr_auc_gw_available():
+    if not os.path.exists("/tmp/hlr_auc_gw.sock"):
+        raise HwsimSkip("No hlr_auc_gw socket available")
+    if not os.path.exists("../../hostapd/hlr_auc_gw"):
+        raise HwsimSkip("No hlr_auc_gw available")
+
+def interworking_ext_sim_connect(dev, bssid, method):
+    dev.request("INTERWORKING_CONNECT " + bssid)
+    interworking_ext_sim_auth(dev, method)
+
+def interworking_ext_sim_auth(dev, method):
+    ev = dev.wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
+    if ev is None:
+        raise Exception("Network connected timed out")
+    if "(" + method + ")" not in ev:
+        raise Exception("Unexpected EAP method selection")
+
+    ev = dev.wait_event(["CTRL-REQ-SIM"], timeout=15)
+    if ev is None:
+        raise Exception("Wait for external SIM processing request timed out")
+    p = ev.split(':', 2)
+    if p[1] != "GSM-AUTH":
+        raise Exception("Unexpected CTRL-REQ-SIM type")
+    id = p[0].split('-')[3]
+    rand = p[2].split(' ')[0]
+
+    res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
+                                   "-m",
+                                   "auth_serv/hlr_auc_gw.milenage_db",
+                                   "GSM-AUTH-REQ 232010000000000 " + rand])
+    if "GSM-AUTH-RESP" not in res:
+        raise Exception("Unexpected hlr_auc_gw response")
+    resp = res.split(' ')[2].rstrip()
+
+    dev.request("CTRL-RSP-SIM-" + id + ":GSM-AUTH:" + resp)
+    dev.wait_connected(timeout=15)
+
+def interworking_connect(dev, bssid, method):
+    dev.request("INTERWORKING_CONNECT " + bssid)
+    interworking_auth(dev, method)
+
+def interworking_auth(dev, method):
+    ev = dev.wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
+    if ev is None:
+        raise Exception("Network connected timed out")
+    if "(" + method + ")" not in ev:
+        raise Exception("Unexpected EAP method selection")
+
+    dev.wait_connected(timeout=15)
+
+def check_probe_resp(wt, bssid_unexpected, bssid_expected):
+    if bssid_unexpected:
+        count = wt.get_bss_counter("probe_response", bssid_unexpected)
+        if count > 0:
+            raise Exception("Unexpected Probe Response frame from AP")
+
+    if bssid_expected:
+        count = wt.get_bss_counter("probe_response", bssid_expected)
+        if count == 0:
+            raise Exception("No Probe Response frame from AP")
+
+def test_ap_anqp_sharing(dev, apdev):
+    """ANQP sharing within ESS and explicit unshare"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    dev[0].flush_scan_cache()
+
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['nai_realm'] = [ "0,example.com,13[5:6],21[2:4][5:7]" ]
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com", 'username': "test",
+                                  'password': "secret",
+                                  'domain': "example.com" })
+    logger.info("Normal network selection with shared ANQP results")
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].scan_for_bss(bssid2, freq="2412")
+    interworking_select(dev[0], None, "home", freq="2412")
+    dev[0].dump_monitor()
+
+    logger.debug("BSS entries:\n" + dev[0].request("BSS RANGE=ALL"))
+    res1 = dev[0].get_bss(bssid)
+    res2 = dev[0].get_bss(bssid2)
+    if 'anqp_nai_realm' not in res1:
+        raise Exception("anqp_nai_realm not found for AP1")
+    if 'anqp_nai_realm' not in res2:
+        raise Exception("anqp_nai_realm not found for AP2")
+    if res1['anqp_nai_realm'] != res2['anqp_nai_realm']:
+        raise Exception("ANQP results were not shared between BSSes")
+
+    logger.info("Explicit ANQP request to unshare ANQP results")
+    dev[0].request("ANQP_GET " + bssid + " 263")
+    ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+    if ev is None:
+        raise Exception("ANQP operation timed out")
+
+    dev[0].request("ANQP_GET " + bssid2 + " 263")
+    ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+    if ev is None:
+        raise Exception("ANQP operation timed out")
+
+    res1 = dev[0].get_bss(bssid)
+    res2 = dev[0].get_bss(bssid2)
+    if res1['anqp_nai_realm'] == res2['anqp_nai_realm']:
+        raise Exception("ANQP results were not unshared")
+
+def test_ap_nai_home_realm_query(dev, apdev):
+    """NAI Home Realm Query"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com,13[5:6],21[2:4][5:7]",
+                            "0,another.example.org" ]
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].scan(freq="2412")
+    dev[0].request("HS20_GET_NAI_HOME_REALM_LIST " + bssid + " realm=example.com")
+    ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+    if ev is None:
+        raise Exception("ANQP operation timed out")
+    nai1 = dev[0].get_bss(bssid)['anqp_nai_realm']
+    dev[0].dump_monitor()
+
+    dev[0].request("ANQP_GET " + bssid + " 263")
+    ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+    if ev is None:
+        raise Exception("ANQP operation timed out")
+    nai2 = dev[0].get_bss(bssid)['anqp_nai_realm']
+
+    if len(nai1) >= len(nai2):
+        raise Exception("Unexpected NAI Realm list response lengths")
+    if "example.com".encode('hex') not in nai1:
+        raise Exception("Home realm not reported")
+    if "example.org".encode('hex') in nai1:
+        raise Exception("Non-home realm reported")
+    if "example.com".encode('hex') not in nai2:
+        raise Exception("Home realm not reported in wildcard query")
+    if "example.org".encode('hex') not in nai2:
+        raise Exception("Non-home realm not reported in wildcard query ")
+
+    cmds = [ "foo",
+             "00:11:22:33:44:55 123",
+             "00:11:22:33:44:55 qq" ]
+    for cmd in cmds:
+        if "FAIL" not in dev[0].request("HS20_GET_NAI_HOME_REALM_LIST " + cmd):
+            raise Exception("Invalid HS20_GET_NAI_HOME_REALM_LIST accepted: " + cmd)
+
+    dev[0].dump_monitor()
+    if "OK" not in dev[0].request("HS20_GET_NAI_HOME_REALM_LIST " + bssid):
+        raise Exception("HS20_GET_NAI_HOME_REALM_LIST failed")
+    ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+    if ev is None:
+        raise Exception("ANQP operation timed out")
+    ev = dev[0].wait_event(["RX-ANQP"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected ANQP response: " + ev)
+
+    dev[0].dump_monitor()
+    if "OK" not in dev[0].request("HS20_GET_NAI_HOME_REALM_LIST " + bssid + " 01000b6578616d706c652e636f6d"):
+        raise Exception("HS20_GET_NAI_HOME_REALM_LIST failed")
+    ev = dev[0].wait_event(["RX-ANQP"], timeout=10)
+    if ev is None:
+        raise Exception("No ANQP response")
+    if "NAI Realm list" not in ev:
+        raise Exception("Missing NAI Realm list: " + ev)
+
+    dev[0].add_cred_values({ 'realm': "example.com", 'username': "test",
+                             'password': "secret",
+                             'domain': "example.com" })
+    dev[0].dump_monitor()
+    if "OK" not in dev[0].request("HS20_GET_NAI_HOME_REALM_LIST " + bssid):
+        raise Exception("HS20_GET_NAI_HOME_REALM_LIST failed")
+    ev = dev[0].wait_event(["RX-ANQP"], timeout=10)
+    if ev is None:
+        raise Exception("No ANQP response")
+    if "NAI Realm list" not in ev:
+        raise Exception("Missing NAI Realm list: " + ev)
+
+def test_ap_interworking_scan_filtering(dev, apdev):
+    """Interworking scan filtering with HESSID and access network type"""
+    try:
+        _test_ap_interworking_scan_filtering(dev, apdev)
+    finally:
+        dev[0].request("SET hessid 00:00:00:00:00:00")
+        dev[0].request("SET access_network_type 15")
+
+def _test_ap_interworking_scan_filtering(dev, apdev):
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    ssid = "test-hs20-ap1"
+    params['ssid'] = ssid
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params()
+    ssid2 = "test-hs20-ap2"
+    params['ssid'] = ssid2
+    params['hessid'] = bssid2
+    params['access_network_type'] = "1"
+    del params['venue_group']
+    del params['venue_type']
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].hs20_enable()
+
+    wt = Wlantest()
+    wt.flush()
+
+    logger.info("Check probe request filtering based on HESSID")
+
+    dev[0].request("SET hessid " + bssid2)
+    dev[0].scan(freq="2412")
+    time.sleep(0.03)
+    check_probe_resp(wt, bssid, bssid2)
+
+    logger.info("Check probe request filtering based on access network type")
+
+    wt.clear_bss_counters(bssid)
+    wt.clear_bss_counters(bssid2)
+    dev[0].request("SET hessid 00:00:00:00:00:00")
+    dev[0].request("SET access_network_type 14")
+    dev[0].scan(freq="2412")
+    time.sleep(0.03)
+    check_probe_resp(wt, bssid2, bssid)
+
+    wt.clear_bss_counters(bssid)
+    wt.clear_bss_counters(bssid2)
+    dev[0].request("SET hessid 00:00:00:00:00:00")
+    dev[0].request("SET access_network_type 1")
+    dev[0].scan(freq="2412")
+    time.sleep(0.03)
+    check_probe_resp(wt, bssid, bssid2)
+
+    logger.info("Check probe request filtering based on HESSID and ANT")
+
+    wt.clear_bss_counters(bssid)
+    wt.clear_bss_counters(bssid2)
+    dev[0].request("SET hessid " + bssid)
+    dev[0].request("SET access_network_type 14")
+    dev[0].scan(freq="2412")
+    time.sleep(0.03)
+    check_probe_resp(wt, bssid2, bssid)
+
+    wt.clear_bss_counters(bssid)
+    wt.clear_bss_counters(bssid2)
+    dev[0].request("SET hessid " + bssid2)
+    dev[0].request("SET access_network_type 14")
+    dev[0].scan(freq="2412")
+    time.sleep(0.03)
+    check_probe_resp(wt, bssid, None)
+    check_probe_resp(wt, bssid2, None)
+
+    wt.clear_bss_counters(bssid)
+    wt.clear_bss_counters(bssid2)
+    dev[0].request("SET hessid " + bssid)
+    dev[0].request("SET access_network_type 1")
+    dev[0].scan(freq="2412")
+    time.sleep(0.03)
+    check_probe_resp(wt, bssid, None)
+    check_probe_resp(wt, bssid2, None)
+
+def test_ap_hs20_select(dev, apdev):
+    """Hotspot 2.0 network selection"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com", 'username': "test",
+                                  'password': "secret",
+                                  'domain': "example.com" })
+    interworking_select(dev[0], bssid, "home")
+
+    dev[0].remove_cred(id)
+    id = dev[0].add_cred_values({ 'realm': "example.com", 'username': "test",
+                                  'password': "secret",
+                                  'domain': "no.match.example.com" })
+    interworking_select(dev[0], bssid, "roaming", freq="2412")
+
+    dev[0].set_cred_quoted(id, "realm", "no.match.example.com");
+    interworking_select(dev[0], bssid, no_match=True, freq="2412")
+
+    res = dev[0].request("SCAN_RESULTS")
+    if "[HS20]" not in res:
+        raise Exception("HS20 flag missing from scan results: " + res)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.org,21" ]
+    params['hessid'] = bssid2
+    params['domain_name'] = "example.org"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    dev[0].remove_cred(id)
+    id = dev[0].add_cred_values({ 'realm': "example.org", 'username': "test",
+                                  'password': "secret",
+                                  'domain': "example.org" })
+    interworking_select(dev[0], bssid2, "home", freq="2412")
+
+def hs20_simulated_sim(dev, ap, method):
+    bssid = ap['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['anqp_3gpp_cell_net'] = "555,444"
+    params['domain_name'] = "wlan.mnc444.mcc555.3gppnetwork.org"
+    hostapd.add_ap(ap['ifname'], params)
+
+    dev.hs20_enable()
+    dev.add_cred_values({ 'imsi': "555444-333222111", 'eap': method,
+                          'milenage': "5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123"})
+    interworking_select(dev, "home", freq="2412")
+    interworking_connect(dev, bssid, method)
+    check_sp_type(dev, "home")
+
+def test_ap_hs20_sim(dev, apdev):
+    """Hotspot 2.0 with simulated SIM and EAP-SIM"""
+    hlr_auc_gw_available()
+    hs20_simulated_sim(dev[0], apdev[0], "SIM")
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    ev = dev[0].wait_event(["INTERWORKING-ALREADY-CONNECTED"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on already-connected event")
+
+def test_ap_hs20_aka(dev, apdev):
+    """Hotspot 2.0 with simulated USIM and EAP-AKA"""
+    hlr_auc_gw_available()
+    hs20_simulated_sim(dev[0], apdev[0], "AKA")
+
+def test_ap_hs20_aka_prime(dev, apdev):
+    """Hotspot 2.0 with simulated USIM and EAP-AKA'"""
+    hlr_auc_gw_available()
+    hs20_simulated_sim(dev[0], apdev[0], "AKA'")
+
+def test_ap_hs20_ext_sim(dev, apdev):
+    """Hotspot 2.0 with external SIM processing"""
+    hlr_auc_gw_available()
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['anqp_3gpp_cell_net'] = "232,01"
+    params['domain_name'] = "wlan.mnc001.mcc232.3gppnetwork.org"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    try:
+        dev[0].request("SET external_sim 1")
+        dev[0].add_cred_values({ 'imsi': "23201-0000000000", 'eap': "SIM" })
+        interworking_select(dev[0], "home", freq="2412")
+        interworking_ext_sim_connect(dev[0], bssid, "SIM")
+        check_sp_type(dev[0], "home")
+    finally:
+        dev[0].request("SET external_sim 0")
+
+def test_ap_hs20_ext_sim_roaming(dev, apdev):
+    """Hotspot 2.0 with external SIM processing in roaming network"""
+    hlr_auc_gw_available()
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['anqp_3gpp_cell_net'] = "244,91;310,026;232,01;234,56"
+    params['domain_name'] = "wlan.mnc091.mcc244.3gppnetwork.org"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    try:
+        dev[0].request("SET external_sim 1")
+        dev[0].add_cred_values({ 'imsi': "23201-0000000000", 'eap': "SIM" })
+        interworking_select(dev[0], "roaming", freq="2412")
+        interworking_ext_sim_connect(dev[0], bssid, "SIM")
+        check_sp_type(dev[0], "roaming")
+    finally:
+        dev[0].request("SET external_sim 0")
+
+def test_ap_hs20_username(dev, apdev):
+    """Hotspot 2.0 connection in username/password credential"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['disable_dgaf'] = '1'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'domain': "example.com",
+                                  'update_identifier': "1234" })
+    interworking_select(dev[0], bssid, "home", freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+    check_sp_type(dev[0], "home")
+    status = dev[0].get_status()
+    if status['pairwise_cipher'] != "CCMP":
+        raise Exception("Unexpected pairwise cipher")
+    if status['hs20'] != "2":
+        raise Exception("Unexpected HS 2.0 support indication")
+
+    dev[1].connect("test-hs20", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="hs20-test", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                   scan_freq="2412")
+
+def test_ap_hs20_connect_api(dev, apdev):
+    """Hotspot 2.0 connection with connect API"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['disable_dgaf'] = '1'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+    wpas.hs20_enable()
+    wpas.flush_scan_cache()
+    id = wpas.add_cred_values({ 'realm': "example.com",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'domain': "example.com",
+                                  'update_identifier': "1234" })
+    interworking_select(wpas, bssid, "home", freq="2412")
+    interworking_connect(wpas, bssid, "TTLS")
+    check_sp_type(wpas, "home")
+    status = wpas.get_status()
+    if status['pairwise_cipher'] != "CCMP":
+        raise Exception("Unexpected pairwise cipher")
+    if status['hs20'] != "2":
+        raise Exception("Unexpected HS 2.0 support indication")
+
+def test_ap_hs20_auto_interworking(dev, apdev):
+    """Hotspot 2.0 connection with auto_interworking=1"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['disable_dgaf'] = '1'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable(auto_interworking=True)
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'domain': "example.com",
+                                  'update_identifier': "1234" })
+    dev[0].request("REASSOCIATE")
+    dev[0].wait_connected(timeout=15)
+    check_sp_type(dev[0], "home")
+    status = dev[0].get_status()
+    if status['pairwise_cipher'] != "CCMP":
+        raise Exception("Unexpected pairwise cipher")
+    if status['hs20'] != "2":
+        raise Exception("Unexpected HS 2.0 support indication")
+
+def test_ap_hs20_auto_interworking_no_match(dev, apdev):
+    """Hotspot 2.0 connection with auto_interworking=1 and no matching network"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "mismatch" })
+
+    dev[0].hs20_enable(auto_interworking=True)
+    id = dev[0].connect("mismatch", psk="12345678", scan_freq="2412",
+                        only_add_network=True)
+    dev[0].request("ENABLE_NETWORK " + str(id) + " no-connect")
+
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'domain': "example.com",
+                                  'update_identifier': "1234" })
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    time.sleep(0.1)
+    dev[0].dump_monitor()
+    for i in range(5):
+        logger.info("start ping")
+        if "PONG" not in dev[0].ctrl.request("PING", timeout=2):
+            raise Exception("PING failed")
+        logger.info("ping done")
+        fetch = 0
+        scan = 0
+        for j in range(15):
+            ev = dev[0].wait_event([ "ANQP fetch completed",
+                                     "CTRL-EVENT-SCAN-RESULTS" ], timeout=0.05)
+            if ev is None:
+                break
+            if "ANQP fetch completed" in ev:
+                fetch += 1
+            else:
+                scan += 1
+        if fetch > 2 * scan + 3:
+            raise Exception("Too many ANQP fetch iterations")
+        dev[0].dump_monitor()
+    dev[0].request("DISCONNECT")
+
+def test_ap_hs20_auto_interworking_no_cred_match(dev, apdev):
+    """Hotspot 2.0 connection with auto_interworking=1 but no cred match"""
+    bssid = apdev[0]['bssid']
+    params = { "ssid": "test" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable(auto_interworking=True)
+    dev[0].add_cred_values({ 'realm': "example.com",
+                             'username': "hs20-test",
+                             'password': "password",
+                             'ca_cert': "auth_serv/ca.pem",
+                             'domain': "example.com" })
+
+    id = dev[0].connect("test", psk="12345678", only_add_network=True)
+    dev[0].request("ENABLE_NETWORK %s" % id)
+    logger.info("Verify that scanning continues when there is partial network block match")
+    for i in range(0, 2):
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+        if ev is None:
+            raise Exception("Scan timed out")
+        logger.info("Scan completed")
+
+def eap_test(dev, ap, eap_params, method, user):
+    bssid = ap['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com," + eap_params ]
+    hostapd.add_ap(ap['ifname'], params)
+
+    dev.hs20_enable()
+    dev.add_cred_values({ 'realm': "example.com",
+                          'ca_cert': "auth_serv/ca.pem",
+                          'username': user,
+                          'password': "password" })
+    interworking_select(dev, bssid, freq="2412")
+    interworking_connect(dev, bssid, method)
+
+def test_ap_hs20_eap_unknown(dev, apdev):
+    """Hotspot 2.0 connection with unknown EAP method"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = "0,example.com,99"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].add_cred_values(default_cred())
+    interworking_select(dev[0], None, no_match=True, freq="2412")
+
+def test_ap_hs20_eap_peap_mschapv2(dev, apdev):
+    """Hotspot 2.0 connection with PEAP/MSCHAPV2"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    eap_test(dev[0], apdev[0], "25[3:26]", "PEAP", "user")
+
+def test_ap_hs20_eap_peap_default(dev, apdev):
+    """Hotspot 2.0 connection with PEAP/MSCHAPV2 (as default)"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    eap_test(dev[0], apdev[0], "25", "PEAP", "user")
+
+def test_ap_hs20_eap_peap_gtc(dev, apdev):
+    """Hotspot 2.0 connection with PEAP/GTC"""
+    eap_test(dev[0], apdev[0], "25[3:6]", "PEAP", "user")
+
+def test_ap_hs20_eap_peap_unknown(dev, apdev):
+    """Hotspot 2.0 connection with PEAP/unknown"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = "0,example.com,25[3:99]"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].add_cred_values(default_cred())
+    interworking_select(dev[0], None, no_match=True, freq="2412")
+
+def test_ap_hs20_eap_ttls_chap(dev, apdev):
+    """Hotspot 2.0 connection with TTLS/CHAP"""
+    skip_with_fips(dev[0])
+    eap_test(dev[0], apdev[0], "21[2:2]", "TTLS", "chap user")
+
+def test_ap_hs20_eap_ttls_mschap(dev, apdev):
+    """Hotspot 2.0 connection with TTLS/MSCHAP"""
+    skip_with_fips(dev[0])
+    eap_test(dev[0], apdev[0], "21[2:3]", "TTLS", "mschap user")
+
+def test_ap_hs20_eap_ttls_eap_mschapv2(dev, apdev):
+    """Hotspot 2.0 connection with TTLS/EAP-MSCHAPv2"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    eap_test(dev[0], apdev[0], "21[3:26][6:7][99:99]", "TTLS", "user")
+
+def test_ap_hs20_eap_ttls_eap_unknown(dev, apdev):
+    """Hotspot 2.0 connection with TTLS/EAP-unknown"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = "0,example.com,21[3:99]"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].add_cred_values(default_cred())
+    interworking_select(dev[0], None, no_match=True, freq="2412")
+
+def test_ap_hs20_eap_ttls_eap_unsupported(dev, apdev):
+    """Hotspot 2.0 connection with TTLS/EAP-OTP(unsupported)"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = "0,example.com,21[3:5]"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].add_cred_values(default_cred())
+    interworking_select(dev[0], None, no_match=True, freq="2412")
+
+def test_ap_hs20_eap_ttls_unknown(dev, apdev):
+    """Hotspot 2.0 connection with TTLS/unknown"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = "0,example.com,21[2:5]"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].add_cred_values(default_cred())
+    interworking_select(dev[0], None, no_match=True, freq="2412")
+
+def test_ap_hs20_eap_fast_mschapv2(dev, apdev):
+    """Hotspot 2.0 connection with FAST/EAP-MSCHAPV2"""
+    check_eap_capa(dev[0], "FAST")
+    eap_test(dev[0], apdev[0], "43[3:26]", "FAST", "user")
+
+def test_ap_hs20_eap_fast_gtc(dev, apdev):
+    """Hotspot 2.0 connection with FAST/EAP-GTC"""
+    check_eap_capa(dev[0], "FAST")
+    eap_test(dev[0], apdev[0], "43[3:6]", "FAST", "user")
+
+def test_ap_hs20_eap_tls(dev, apdev):
+    """Hotspot 2.0 connection with EAP-TLS"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com,13[5:6]" ]
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].add_cred_values({ 'realm': "example.com",
+                             'username': "certificate-user",
+                             'ca_cert': "auth_serv/ca.pem",
+                             'client_cert': "auth_serv/user.pem",
+                             'private_key': "auth_serv/user.key"})
+    interworking_select(dev[0], bssid, freq="2412")
+    interworking_connect(dev[0], bssid, "TLS")
+
+def test_ap_hs20_eap_cert_unknown(dev, apdev):
+    """Hotspot 2.0 connection with certificate, but unknown EAP method"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com,99[5:6]" ]
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].add_cred_values({ 'realm': "example.com",
+                             'username': "certificate-user",
+                             'ca_cert': "auth_serv/ca.pem",
+                             'client_cert': "auth_serv/user.pem",
+                             'private_key': "auth_serv/user.key"})
+    interworking_select(dev[0], None, no_match=True, freq="2412")
+
+def test_ap_hs20_eap_cert_unsupported(dev, apdev):
+    """Hotspot 2.0 connection with certificate, but unsupported TTLS"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com,21[5:6]" ]
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].add_cred_values({ 'realm': "example.com",
+                             'username': "certificate-user",
+                             'ca_cert': "auth_serv/ca.pem",
+                             'client_cert': "auth_serv/user.pem",
+                             'private_key': "auth_serv/user.key"})
+    interworking_select(dev[0], None, no_match=True, freq="2412")
+
+def test_ap_hs20_eap_invalid_cred(dev, apdev):
+    """Hotspot 2.0 connection with invalid cred configuration"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].add_cred_values({ 'realm': "example.com",
+                             'username': "certificate-user",
+                             'client_cert': "auth_serv/user.pem" })
+    interworking_select(dev[0], None, no_match=True, freq="2412")
+
+def test_ap_hs20_nai_realms(dev, apdev):
+    """Hotspot 2.0 connection and multiple NAI realms and TTLS/PAP"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['nai_realm'] = [ "0,no.match.here;example.com;no.match.here.either,21[2:1][5:7]" ]
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'username': "pap user",
+                                  'password': "password",
+                                  'domain': "example.com" })
+    interworking_select(dev[0], bssid, "home", freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+    check_sp_type(dev[0], "home")
+
+def test_ap_hs20_roaming_consortium(dev, apdev):
+    """Hotspot 2.0 connection based on roaming consortium match"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    for consortium in [ "112233", "1020304050", "010203040506", "fedcba" ]:
+        id = dev[0].add_cred_values({ 'username': "user",
+                                      'password': "password",
+                                      'domain': "example.com",
+                                      'ca_cert': "auth_serv/ca.pem",
+                                      'roaming_consortium': consortium,
+                                      'eap': "PEAP" })
+        interworking_select(dev[0], bssid, "home", freq="2412")
+        interworking_connect(dev[0], bssid, "PEAP")
+        check_sp_type(dev[0], "home")
+        dev[0].request("INTERWORKING_SELECT auto freq=2412")
+        ev = dev[0].wait_event(["INTERWORKING-ALREADY-CONNECTED"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on already-connected event")
+        dev[0].remove_cred(id)
+
+def test_ap_hs20_username_roaming(dev, apdev):
+    """Hotspot 2.0 connection in username/password credential (roaming)"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com,13[5:6],21[2:4][5:7]",
+                            "0,roaming.example.com,21[2:4][5:7]",
+                            "0,another.example.com" ]
+    params['domain_name'] = "another.example.com"
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "roaming.example.com",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'domain': "example.com" })
+    interworking_select(dev[0], bssid, "roaming", freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+    check_sp_type(dev[0], "roaming")
+
+def test_ap_hs20_username_unknown(dev, apdev):
+    """Hotspot 2.0 connection in username/password credential (no domain in cred)"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'username': "hs20-test",
+                                  'password': "password" })
+    interworking_select(dev[0], bssid, "unknown", freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+    check_sp_type(dev[0], "unknown")
+
+def test_ap_hs20_username_unknown2(dev, apdev):
+    """Hotspot 2.0 connection in username/password credential (no domain advertized)"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    del params['domain_name']
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'domain': "example.com" })
+    interworking_select(dev[0], bssid, "unknown", freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+    check_sp_type(dev[0], "unknown")
+
+def test_ap_hs20_gas_while_associated(dev, apdev):
+    """Hotspot 2.0 connection with GAS query while associated"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'domain': "example.com" })
+    interworking_select(dev[0], bssid, "home", freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+
+    logger.info("Verifying GAS query while associated")
+    dev[0].request("FETCH_ANQP")
+    for i in range(0, 6):
+        ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+        if ev is None:
+            raise Exception("Operation timed out")
+
+def test_ap_hs20_gas_while_associated_with_pmf(dev, apdev):
+    """Hotspot 2.0 connection with GAS query while associated and using PMF"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    try:
+        _test_ap_hs20_gas_while_associated_with_pmf(dev, apdev)
+    finally:
+        dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_gas_while_associated_with_pmf(dev, apdev):
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid2
+    params['nai_realm'] = [ "0,no-match.example.org,13[5:6],21[2:4][5:7]" ]
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].request("SET pmf 2")
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'domain': "example.com" })
+    interworking_select(dev[0], bssid, "home", freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+
+    logger.info("Verifying GAS query while associated")
+    dev[0].request("FETCH_ANQP")
+    for i in range(0, 2 * 6):
+        ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+        if ev is None:
+            raise Exception("Operation timed out")
+
+def test_ap_hs20_gas_frag_while_associated(dev, apdev):
+    """Hotspot 2.0 connection with fragmented GAS query while associated"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    hapd.set("gas_frag_limit", "50")
+
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'domain': "example.com" })
+    interworking_select(dev[0], bssid, "home", freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+
+    logger.info("Verifying GAS query while associated")
+    dev[0].request("FETCH_ANQP")
+    for i in range(0, 6):
+        ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+        if ev is None:
+            raise Exception("Operation timed out")
+
+def test_ap_hs20_multiple_connects(dev, apdev):
+    """Hotspot 2.0 connection through multiple network selections"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    values = { 'realm': "example.com",
+               'ca_cert': "auth_serv/ca.pem",
+               'username': "hs20-test",
+               'password': "password",
+               'domain': "example.com" }
+    id = dev[0].add_cred_values(values)
+
+    dev[0].scan_for_bss(bssid, freq="2412")
+
+    for i in range(0, 3):
+        logger.info("Starting Interworking network selection")
+        dev[0].request("INTERWORKING_SELECT auto freq=2412")
+        while True:
+            ev = dev[0].wait_event(["INTERWORKING-NO-MATCH",
+                                    "INTERWORKING-ALREADY-CONNECTED",
+                                    "CTRL-EVENT-CONNECTED"], timeout=15)
+            if ev is None:
+                raise Exception("Connection timed out")
+            if "INTERWORKING-NO-MATCH" in ev:
+                raise Exception("Matching AP not found")
+            if "CTRL-EVENT-CONNECTED" in ev:
+                break
+            if i == 2 and "INTERWORKING-ALREADY-CONNECTED" in ev:
+                break
+        if i == 0:
+            dev[0].request("DISCONNECT")
+        dev[0].dump_monitor()
+
+    networks = dev[0].list_networks()
+    if len(networks) > 1:
+        raise Exception("Duplicated network block detected")
+
+def test_ap_hs20_disallow_aps(dev, apdev):
+    """Hotspot 2.0 connection and disallow_aps"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    values = { 'realm': "example.com",
+               'ca_cert': "auth_serv/ca.pem",
+               'username': "hs20-test",
+               'password': "password",
+               'domain': "example.com" }
+    id = dev[0].add_cred_values(values)
+
+    dev[0].scan_for_bss(bssid, freq="2412")
+
+    logger.info("Verify disallow_aps bssid")
+    dev[0].request("SET disallow_aps bssid " + bssid.translate(None, ':'))
+    dev[0].request("INTERWORKING_SELECT auto")
+    ev = dev[0].wait_event(["INTERWORKING-NO-MATCH"], timeout=15)
+    if ev is None:
+        raise Exception("Network selection timed out")
+    dev[0].dump_monitor()
+
+    logger.info("Verify disallow_aps ssid")
+    dev[0].request("SET disallow_aps ssid 746573742d68733230")
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    ev = dev[0].wait_event(["INTERWORKING-NO-MATCH"], timeout=15)
+    if ev is None:
+        raise Exception("Network selection timed out")
+    dev[0].dump_monitor()
+
+    logger.info("Verify disallow_aps clear")
+    dev[0].request("SET disallow_aps ")
+    interworking_select(dev[0], bssid, "home", freq="2412")
+
+    dev[0].request("SET disallow_aps bssid " + bssid.translate(None, ':'))
+    ret = dev[0].request("INTERWORKING_CONNECT " + bssid)
+    if "FAIL" not in ret:
+        raise Exception("INTERWORKING_CONNECT to disallowed BSS not rejected")
+
+    if "FAIL" not in dev[0].request("INTERWORKING_CONNECT foo"):
+        raise Exception("Invalid INTERWORKING_CONNECT not rejected")
+    if "FAIL" not in dev[0].request("INTERWORKING_CONNECT 00:11:22:33:44:55"):
+        raise Exception("Invalid INTERWORKING_CONNECT not rejected")
+
+def policy_test(dev, ap, values, only_one=True):
+    dev.dump_monitor()
+    if ap:
+        logger.info("Verify network selection to AP " + ap['ifname'])
+        bssid = ap['bssid']
+        dev.scan_for_bss(bssid, freq="2412")
+    else:
+        logger.info("Verify network selection")
+        bssid = None
+    dev.hs20_enable()
+    id = dev.add_cred_values(values)
+    dev.request("INTERWORKING_SELECT auto freq=2412")
+    events = []
+    while True:
+        ev = dev.wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH",
+                             "INTERWORKING-BLACKLISTED",
+                             "INTERWORKING-SELECTED"], timeout=15)
+        if ev is None:
+            raise Exception("Network selection timed out")
+        events.append(ev)
+        if "INTERWORKING-NO-MATCH" in ev:
+            raise Exception("Matching AP not found")
+        if bssid and only_one and "INTERWORKING-AP" in ev and bssid not in ev:
+            raise Exception("Unexpected AP claimed acceptable")
+        if "INTERWORKING-SELECTED" in ev:
+            if bssid and bssid not in ev:
+                raise Exception("Selected incorrect BSS")
+            break
+
+    ev = dev.wait_connected(timeout=15)
+    if bssid and bssid not in ev:
+        raise Exception("Connected to incorrect BSS")
+
+    conn_bssid = dev.get_status_field("bssid")
+    if bssid and conn_bssid != bssid:
+        raise Exception("bssid information points to incorrect BSS")
+
+    dev.remove_cred(id)
+    dev.dump_monitor()
+    return events
+
+def default_cred(domain=None, user="hs20-test"):
+    cred = { 'realm': "example.com",
+             'ca_cert': "auth_serv/ca.pem",
+             'username': user,
+             'password': "password" }
+    if domain:
+        cred['domain'] = domain
+    return cred
+
+def test_ap_hs20_prefer_home(dev, apdev):
+    """Hotspot 2.0 required roaming consortium"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hs20_ap_params()
+    params['domain_name'] = "example.org"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    params = hs20_ap_params()
+    params['ssid'] = "test-hs20-other"
+    params['domain_name'] = "example.com"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    values = default_cred()
+    values['domain'] = "example.com"
+    policy_test(dev[0], apdev[1], values, only_one=False)
+    values['domain'] = "example.org"
+    policy_test(dev[0], apdev[0], values, only_one=False)
+
+def test_ap_hs20_req_roaming_consortium(dev, apdev):
+    """Hotspot 2.0 required roaming consortium"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    params = hs20_ap_params()
+    params['ssid'] = "test-hs20-other"
+    params['roaming_consortium'] = [ "223344" ]
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    values = default_cred()
+    values['required_roaming_consortium'] = "223344"
+    policy_test(dev[0], apdev[1], values)
+    values['required_roaming_consortium'] = "112233"
+    policy_test(dev[0], apdev[0], values)
+
+    id = dev[0].add_cred()
+    dev[0].set_cred(id, "required_roaming_consortium", "112233")
+    dev[0].set_cred(id, "required_roaming_consortium", "112233445566778899aabbccddeeff")
+
+    for val in [ "", "1", "11", "1122", "1122334", "112233445566778899aabbccddeeff00" ]:
+        if "FAIL" not in dev[0].request('SET_CRED {} required_roaming_consortium {}'.format(id, val)):
+            raise Exception("Invalid roaming consortium value accepted: " + val)
+
+def test_ap_hs20_excluded_ssid(dev, apdev):
+    """Hotspot 2.0 exclusion based on SSID"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hs20_ap_params()
+    params['roaming_consortium'] = [ "223344" ]
+    params['anqp_3gpp_cell_net'] = "555,444"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    params = hs20_ap_params()
+    params['ssid'] = "test-hs20-other"
+    params['roaming_consortium'] = [ "223344" ]
+    params['anqp_3gpp_cell_net'] = "555,444"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    values = default_cred()
+    values['excluded_ssid'] = "test-hs20"
+    events = policy_test(dev[0], apdev[1], values)
+    ev = [e for e in events if "INTERWORKING-BLACKLISTED " + apdev[0]['bssid'] in e]
+    if len(ev) != 1:
+        raise Exception("Excluded network not reported")
+    values['excluded_ssid'] = "test-hs20-other"
+    events = policy_test(dev[0], apdev[0], values)
+    ev = [e for e in events if "INTERWORKING-BLACKLISTED " + apdev[1]['bssid'] in e]
+    if len(ev) != 1:
+        raise Exception("Excluded network not reported")
+
+    values = default_cred()
+    values['roaming_consortium'] = "223344"
+    values['eap'] = "TTLS"
+    values['phase2'] = "auth=MSCHAPV2"
+    values['excluded_ssid'] = "test-hs20"
+    events = policy_test(dev[0], apdev[1], values)
+    ev = [e for e in events if "INTERWORKING-BLACKLISTED " + apdev[0]['bssid'] in e]
+    if len(ev) != 1:
+        raise Exception("Excluded network not reported")
+
+    values = { 'imsi': "555444-333222111", 'eap': "SIM",
+               'milenage': "5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
+               'excluded_ssid': "test-hs20" }
+    events = policy_test(dev[0], apdev[1], values)
+    ev = [e for e in events if "INTERWORKING-BLACKLISTED " + apdev[0]['bssid'] in e]
+    if len(ev) != 1:
+        raise Exception("Excluded network not reported")
+
+def test_ap_hs20_roam_to_higher_prio(dev, apdev):
+    """Hotspot 2.0 and roaming from current to higher priority network"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params(ssid="test-hs20-visited")
+    params['domain_name'] = "visited.example.org"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'domain': "example.com" })
+    logger.info("Connect to the only network option")
+    interworking_select(dev[0], bssid, "roaming", freq="2412")
+    dev[0].dump_monitor()
+    interworking_connect(dev[0], bssid, "TTLS")
+
+    logger.info("Start another AP (home operator) and reconnect")
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params(ssid="test-hs20-home")
+    params['domain_name'] = "example.com"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].scan_for_bss(bssid2, freq="2412", force_scan=True)
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    ev = dev[0].wait_event(["INTERWORKING-NO-MATCH",
+                            "INTERWORKING-ALREADY-CONNECTED",
+                            "CTRL-EVENT-CONNECTED"], timeout=15)
+    if ev is None:
+        raise Exception("Connection timed out")
+    if "INTERWORKING-NO-MATCH" in ev:
+        raise Exception("Matching AP not found")
+    if "INTERWORKING-ALREADY-CONNECTED" in ev:
+        raise Exception("Unexpected AP selected")
+    if bssid2 not in ev:
+        raise Exception("Unexpected BSSID after reconnection")
+
+def test_ap_hs20_domain_suffix_match_full(dev, apdev):
+    """Hotspot 2.0 and domain_suffix_match"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'domain': "example.com",
+                                  'domain_suffix_match': "server.w1.fi" })
+    interworking_select(dev[0], bssid, "home", freq="2412")
+    dev[0].dump_monitor()
+    interworking_connect(dev[0], bssid, "TTLS")
+    dev[0].request("REMOVE_NETWORK all")
+    dev[0].dump_monitor()
+
+    dev[0].set_cred_quoted(id, "domain_suffix_match", "no-match.example.com")
+    interworking_select(dev[0], bssid, "home", freq="2412")
+    dev[0].dump_monitor()
+    dev[0].request("INTERWORKING_CONNECT " + bssid)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"])
+    if ev is None:
+        raise Exception("TLS certificate error not reported")
+    if "Domain suffix mismatch" not in ev:
+        raise Exception("Domain suffix mismatch not reported")
+
+def test_ap_hs20_domain_suffix_match(dev, apdev):
+    """Hotspot 2.0 and domain_suffix_match"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    check_domain_match_full(dev[0])
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'domain': "example.com",
+                                  'domain_suffix_match': "w1.fi" })
+    interworking_select(dev[0], bssid, "home", freq="2412")
+    dev[0].dump_monitor()
+    interworking_connect(dev[0], bssid, "TTLS")
+
+def test_ap_hs20_roaming_partner_preference(dev, apdev):
+    """Hotspot 2.0 and roaming partner preference"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hs20_ap_params()
+    params['domain_name'] = "roaming.example.org"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    params = hs20_ap_params()
+    params['ssid'] = "test-hs20-other"
+    params['domain_name'] = "roaming.example.net"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    logger.info("Verify default vs. specified preference")
+    values = default_cred()
+    values['roaming_partner'] = "roaming.example.net,1,127,*"
+    policy_test(dev[0], apdev[1], values, only_one=False)
+    values['roaming_partner'] = "roaming.example.net,1,129,*"
+    policy_test(dev[0], apdev[0], values, only_one=False)
+
+    logger.info("Verify partial FQDN match")
+    values['roaming_partner'] = "example.net,0,0,*"
+    policy_test(dev[0], apdev[1], values, only_one=False)
+    values['roaming_partner'] = "example.net,0,255,*"
+    policy_test(dev[0], apdev[0], values, only_one=False)
+
+def test_ap_hs20_max_bss_load(dev, apdev):
+    """Hotspot 2.0 and maximum BSS load"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hs20_ap_params()
+    params['bss_load_test'] = "12:200:20000"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    params = hs20_ap_params()
+    params['ssid'] = "test-hs20-other"
+    params['bss_load_test'] = "5:20:10000"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    logger.info("Verify maximum BSS load constraint")
+    values = default_cred()
+    values['domain'] = "example.com"
+    values['max_bss_load'] = "100"
+    events = policy_test(dev[0], apdev[1], values, only_one=False)
+
+    ev = [e for e in events if "INTERWORKING-AP " + apdev[0]['bssid'] in e]
+    if len(ev) != 1 or "over_max_bss_load=1" not in ev[0]:
+        raise Exception("Maximum BSS Load case not noticed")
+    ev = [e for e in events if "INTERWORKING-AP " + apdev[1]['bssid'] in e]
+    if len(ev) != 1 or "over_max_bss_load=1" in ev[0]:
+        raise Exception("Maximum BSS Load case reported incorrectly")
+
+    logger.info("Verify maximum BSS load does not prevent connection")
+    values['max_bss_load'] = "1"
+    events = policy_test(dev[0], None, values)
+
+    ev = [e for e in events if "INTERWORKING-AP " + apdev[0]['bssid'] in e]
+    if len(ev) != 1 or "over_max_bss_load=1" not in ev[0]:
+        raise Exception("Maximum BSS Load case not noticed")
+    ev = [e for e in events if "INTERWORKING-AP " + apdev[1]['bssid'] in e]
+    if len(ev) != 1 or "over_max_bss_load=1" not in ev[0]:
+        raise Exception("Maximum BSS Load case not noticed")
+
+def test_ap_hs20_max_bss_load2(dev, apdev):
+    """Hotspot 2.0 and maximum BSS load with one AP not advertising"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = hs20_ap_params()
+    params['bss_load_test'] = "12:200:20000"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    params = hs20_ap_params()
+    params['ssid'] = "test-hs20-other"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    logger.info("Verify maximum BSS load constraint with AP advertisement")
+    values = default_cred()
+    values['domain'] = "example.com"
+    values['max_bss_load'] = "100"
+    events = policy_test(dev[0], apdev[1], values, only_one=False)
+
+    ev = [e for e in events if "INTERWORKING-AP " + apdev[0]['bssid'] in e]
+    if len(ev) != 1 or "over_max_bss_load=1" not in ev[0]:
+        raise Exception("Maximum BSS Load case not noticed")
+    ev = [e for e in events if "INTERWORKING-AP " + apdev[1]['bssid'] in e]
+    if len(ev) != 1 or "over_max_bss_load=1" in ev[0]:
+        raise Exception("Maximum BSS Load case reported incorrectly")
+
+def test_ap_hs20_multi_cred_sp_prio(dev, apdev):
+    """Hotspot 2.0 multi-cred sp_priority"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    try:
+        _test_ap_hs20_multi_cred_sp_prio(dev, apdev)
+    finally:
+        dev[0].request("SET external_sim 0")
+
+def _test_ap_hs20_multi_cred_sp_prio(dev, apdev):
+    hlr_auc_gw_available()
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    del params['domain_name']
+    params['anqp_3gpp_cell_net'] = "232,01"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].request("SET external_sim 1")
+    id1 = dev[0].add_cred_values({ 'imsi': "23201-0000000000", 'eap': "SIM",
+                                   'provisioning_sp': "example.com",
+                                   'sp_priority' :"1" })
+    id2 = dev[0].add_cred_values({ 'realm': "example.com",
+                                   'ca_cert': "auth_serv/ca.pem",
+                                   'username': "hs20-test",
+                                   'password': "password",
+                                   'domain': "example.com",
+                                   'provisioning_sp': "example.com",
+                                   'sp_priority': "2" })
+    dev[0].dump_monitor()
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    interworking_ext_sim_auth(dev[0], "SIM")
+    check_sp_type(dev[0], "unknown")
+    dev[0].request("REMOVE_NETWORK all")
+
+    dev[0].set_cred(id1, "sp_priority", "2")
+    dev[0].set_cred(id2, "sp_priority", "1")
+    dev[0].dump_monitor()
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    interworking_auth(dev[0], "TTLS")
+    check_sp_type(dev[0], "unknown")
+
+def test_ap_hs20_multi_cred_sp_prio2(dev, apdev):
+    """Hotspot 2.0 multi-cred sp_priority with two BSSes"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    try:
+        _test_ap_hs20_multi_cred_sp_prio2(dev, apdev)
+    finally:
+        dev[0].request("SET external_sim 0")
+
+def _test_ap_hs20_multi_cred_sp_prio2(dev, apdev):
+    hlr_auc_gw_available()
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    del params['nai_realm']
+    del params['domain_name']
+    params['anqp_3gpp_cell_net'] = "232,01"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params()
+    params['ssid'] = "test-hs20-other"
+    params['hessid'] = bssid2
+    del params['domain_name']
+    del params['anqp_3gpp_cell_net']
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].request("SET external_sim 1")
+    id1 = dev[0].add_cred_values({ 'imsi': "23201-0000000000", 'eap': "SIM",
+                                   'provisioning_sp': "example.com",
+                                   'sp_priority': "1" })
+    id2 = dev[0].add_cred_values({ 'realm': "example.com",
+                                   'ca_cert': "auth_serv/ca.pem",
+                                   'username': "hs20-test",
+                                   'password': "password",
+                                   'domain': "example.com",
+                                   'provisioning_sp': "example.com",
+                                   'sp_priority': "2" })
+    dev[0].dump_monitor()
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].scan_for_bss(bssid2, freq="2412")
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    interworking_ext_sim_auth(dev[0], "SIM")
+    check_sp_type(dev[0], "unknown")
+    conn_bssid = dev[0].get_status_field("bssid")
+    if conn_bssid != bssid:
+        raise Exception("Connected to incorrect BSS")
+    dev[0].request("REMOVE_NETWORK all")
+
+    dev[0].set_cred(id1, "sp_priority", "2")
+    dev[0].set_cred(id2, "sp_priority", "1")
+    dev[0].dump_monitor()
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    interworking_auth(dev[0], "TTLS")
+    check_sp_type(dev[0], "unknown")
+    conn_bssid = dev[0].get_status_field("bssid")
+    if conn_bssid != bssid2:
+        raise Exception("Connected to incorrect BSS")
+
+def check_conn_capab_selection(dev, type, missing):
+    dev.request("INTERWORKING_SELECT freq=2412")
+    ev = dev.wait_event(["INTERWORKING-AP"])
+    if ev is None:
+        raise Exception("Network selection timed out");
+    if "type=" + type not in ev:
+        raise Exception("Unexpected network type")
+    if missing and "conn_capab_missing=1" not in ev:
+        raise Exception("conn_capab_missing not reported")
+    if not missing and "conn_capab_missing=1" in ev:
+        raise Exception("conn_capab_missing reported unexpectedly")
+
+def conn_capab_cred(domain=None, req_conn_capab=None):
+    cred = default_cred(domain=domain)
+    if req_conn_capab:
+        cred['req_conn_capab'] = req_conn_capab
+    return cred
+
+def test_ap_hs20_req_conn_capab(dev, apdev):
+    """Hotspot 2.0 network selection with req_conn_capab"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].scan_for_bss(bssid, freq="2412")
+    logger.info("Not used in home network")
+    values = conn_capab_cred(domain="example.com", req_conn_capab="6:1234")
+    id = dev[0].add_cred_values(values)
+    check_conn_capab_selection(dev[0], "home", False)
+
+    logger.info("Used in roaming network")
+    dev[0].remove_cred(id)
+    values = conn_capab_cred(domain="example.org", req_conn_capab="6:1234")
+    id = dev[0].add_cred_values(values)
+    check_conn_capab_selection(dev[0], "roaming", True)
+
+    logger.info("Verify that req_conn_capab does not prevent connection if no other network is available")
+    check_auto_select(dev[0], bssid)
+
+    logger.info("Additional req_conn_capab checks")
+
+    dev[0].remove_cred(id)
+    values = conn_capab_cred(domain="example.org", req_conn_capab="1:0")
+    id = dev[0].add_cred_values(values)
+    check_conn_capab_selection(dev[0], "roaming", True)
+
+    dev[0].remove_cred(id)
+    values = conn_capab_cred(domain="example.org", req_conn_capab="17:5060")
+    id = dev[0].add_cred_values(values)
+    check_conn_capab_selection(dev[0], "roaming", True)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params(ssid="test-hs20b")
+    params['hs20_conn_capab'] = [ "1:0:2", "6:22:1", "17:5060:0", "50:0:1" ]
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].remove_cred(id)
+    values = conn_capab_cred(domain="example.org", req_conn_capab="50")
+    id = dev[0].add_cred_values(values)
+    dev[0].set_cred(id, "req_conn_capab", "6:22")
+    dev[0].scan_for_bss(bssid2, freq="2412")
+    dev[0].request("INTERWORKING_SELECT freq=2412")
+    for i in range(0, 2):
+        ev = dev[0].wait_event(["INTERWORKING-AP"])
+        if ev is None:
+            raise Exception("Network selection timed out");
+        if bssid in ev and "conn_capab_missing=1" not in ev:
+            raise Exception("Missing protocol connection capability not reported")
+        if bssid2 in ev and "conn_capab_missing=1" in ev:
+            raise Exception("Protocol connection capability not reported correctly")
+
+def test_ap_hs20_req_conn_capab_and_roaming_partner_preference(dev, apdev):
+    """Hotspot 2.0 and req_conn_capab with roaming partner preference"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['domain_name'] = "roaming.example.org"
+    params['hs20_conn_capab'] = [ "1:0:2", "6:22:1", "17:5060:0", "50:0:1" ]
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params(ssid="test-hs20-b")
+    params['domain_name'] = "roaming.example.net"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    values = default_cred()
+    values['roaming_partner'] = "roaming.example.net,1,127,*"
+    id = dev[0].add_cred_values(values)
+    check_auto_select(dev[0], bssid2)
+
+    dev[0].set_cred(id, "req_conn_capab", "50")
+    check_auto_select(dev[0], bssid)
+
+    dev[0].remove_cred(id)
+    id = dev[0].add_cred_values(values)
+    dev[0].set_cred(id, "req_conn_capab", "51")
+    check_auto_select(dev[0], bssid2)
+
+def check_bandwidth_selection(dev, type, below):
+    dev.request("INTERWORKING_SELECT freq=2412")
+    ev = dev.wait_event(["INTERWORKING-AP"])
+    if ev is None:
+        raise Exception("Network selection timed out");
+    logger.debug("BSS entries:\n" + dev.request("BSS RANGE=ALL"))
+    if "type=" + type not in ev:
+        raise Exception("Unexpected network type")
+    if below and "below_min_backhaul=1" not in ev:
+        raise Exception("below_min_backhaul not reported")
+    if not below and "below_min_backhaul=1" in ev:
+        raise Exception("below_min_backhaul reported unexpectedly")
+
+def bw_cred(domain=None, dl_home=None, ul_home=None, dl_roaming=None, ul_roaming=None):
+    cred = default_cred(domain=domain)
+    if dl_home:
+        cred['min_dl_bandwidth_home'] = str(dl_home)
+    if ul_home:
+        cred['min_ul_bandwidth_home'] = str(ul_home)
+    if dl_roaming:
+        cred['min_dl_bandwidth_roaming'] = str(dl_roaming)
+    if ul_roaming:
+        cred['min_ul_bandwidth_roaming'] = str(ul_roaming)
+    return cred
+
+def test_ap_hs20_min_bandwidth_home(dev, apdev):
+    """Hotspot 2.0 network selection with min bandwidth (home)"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].scan_for_bss(bssid, freq="2412")
+    values = bw_cred(domain="example.com", dl_home=5490, ul_home=58)
+    id = dev[0].add_cred_values(values)
+    check_bandwidth_selection(dev[0], "home", False)
+    dev[0].remove_cred(id)
+
+    values = bw_cred(domain="example.com", dl_home=5491, ul_home=58)
+    id = dev[0].add_cred_values(values)
+    check_bandwidth_selection(dev[0], "home", True)
+    dev[0].remove_cred(id)
+
+    values = bw_cred(domain="example.com", dl_home=5490, ul_home=59)
+    id = dev[0].add_cred_values(values)
+    check_bandwidth_selection(dev[0], "home", True)
+    dev[0].remove_cred(id)
+
+    values = bw_cred(domain="example.com", dl_home=5491, ul_home=59)
+    id = dev[0].add_cred_values(values)
+    check_bandwidth_selection(dev[0], "home", True)
+    check_auto_select(dev[0], bssid)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params(ssid="test-hs20-b")
+    params['hs20_wan_metrics'] = "01:8000:1000:1:1:3000"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    check_auto_select(dev[0], bssid2)
+
+def test_ap_hs20_min_bandwidth_home_hidden_ssid_in_scan_res(dev, apdev):
+    """Hotspot 2.0 network selection with min bandwidth (home) while hidden SSID is included in scan results"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": 'secret',
+                                                "ignore_broadcast_ssid": "1" })
+    dev[0].scan_for_bss(bssid, freq=2412)
+    hapd.disable()
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.flush()
+    hapd_global.remove(apdev[0]['ifname'])
+
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].scan_for_bss(bssid, freq="2412")
+    values = bw_cred(domain="example.com", dl_home=5490, ul_home=58)
+    id = dev[0].add_cred_values(values)
+    check_bandwidth_selection(dev[0], "home", False)
+    dev[0].remove_cred(id)
+
+    values = bw_cred(domain="example.com", dl_home=5491, ul_home=58)
+    id = dev[0].add_cred_values(values)
+    check_bandwidth_selection(dev[0], "home", True)
+    dev[0].remove_cred(id)
+
+    values = bw_cred(domain="example.com", dl_home=5490, ul_home=59)
+    id = dev[0].add_cred_values(values)
+    check_bandwidth_selection(dev[0], "home", True)
+    dev[0].remove_cred(id)
+
+    values = bw_cred(domain="example.com", dl_home=5491, ul_home=59)
+    id = dev[0].add_cred_values(values)
+    check_bandwidth_selection(dev[0], "home", True)
+    check_auto_select(dev[0], bssid)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params(ssid="test-hs20-b")
+    params['hs20_wan_metrics'] = "01:8000:1000:1:1:3000"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    check_auto_select(dev[0], bssid2)
+
+    dev[0].flush_scan_cache()
+
+def test_ap_hs20_min_bandwidth_roaming(dev, apdev):
+    """Hotspot 2.0 network selection with min bandwidth (roaming)"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].scan_for_bss(bssid, freq="2412")
+    values = bw_cred(domain="example.org", dl_roaming=5490, ul_roaming=58)
+    id = dev[0].add_cred_values(values)
+    check_bandwidth_selection(dev[0], "roaming", False)
+    dev[0].remove_cred(id)
+
+    values = bw_cred(domain="example.org", dl_roaming=5491, ul_roaming=58)
+    id = dev[0].add_cred_values(values)
+    check_bandwidth_selection(dev[0], "roaming", True)
+    dev[0].remove_cred(id)
+
+    values = bw_cred(domain="example.org", dl_roaming=5490, ul_roaming=59)
+    id = dev[0].add_cred_values(values)
+    check_bandwidth_selection(dev[0], "roaming", True)
+    dev[0].remove_cred(id)
+
+    values = bw_cred(domain="example.org", dl_roaming=5491, ul_roaming=59)
+    id = dev[0].add_cred_values(values)
+    check_bandwidth_selection(dev[0], "roaming", True)
+    check_auto_select(dev[0], bssid)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params(ssid="test-hs20-b")
+    params['hs20_wan_metrics'] = "01:8000:1000:1:1:3000"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    check_auto_select(dev[0], bssid2)
+
+def test_ap_hs20_min_bandwidth_and_roaming_partner_preference(dev, apdev):
+    """Hotspot 2.0 and minimum bandwidth with roaming partner preference"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['domain_name'] = "roaming.example.org"
+    params['hs20_wan_metrics'] = "01:8000:1000:1:1:3000"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params(ssid="test-hs20-b")
+    params['domain_name'] = "roaming.example.net"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    values = default_cred()
+    values['roaming_partner'] = "roaming.example.net,1,127,*"
+    id = dev[0].add_cred_values(values)
+    check_auto_select(dev[0], bssid2)
+
+    dev[0].set_cred(id, "min_dl_bandwidth_roaming", "6000")
+    check_auto_select(dev[0], bssid)
+
+    dev[0].set_cred(id, "min_dl_bandwidth_roaming", "10000")
+    check_auto_select(dev[0], bssid2)
+
+def test_ap_hs20_min_bandwidth_no_wan_metrics(dev, apdev):
+    """Hotspot 2.0 network selection with min bandwidth but no WAN Metrics"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    del params['hs20_wan_metrics']
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].scan_for_bss(bssid, freq="2412")
+    values = bw_cred(domain="example.com", dl_home=10000, ul_home=10000,
+                     dl_roaming=10000, ul_roaming=10000)
+    dev[0].add_cred_values(values)
+    check_bandwidth_selection(dev[0], "home", False)
+
+def test_ap_hs20_deauth_req_ess(dev, apdev):
+    """Hotspot 2.0 connection and deauthentication request for ESS"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    try:
+        _test_ap_hs20_deauth_req_ess(dev, apdev)
+    finally:
+        dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_deauth_req_ess(dev, apdev):
+    dev[0].request("SET pmf 2")
+    eap_test(dev[0], apdev[0], "21[3:26]", "TTLS", "user")
+    dev[0].dump_monitor()
+    addr = dev[0].p2p_interface_addr()
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    hapd.request("HS20_DEAUTH_REQ " + addr + " 1 120 http://example.com/")
+    ev = dev[0].wait_event(["HS20-DEAUTH-IMMINENT-NOTICE"])
+    if ev is None:
+        raise Exception("Timeout on deauth imminent notice")
+    if "1 120 http://example.com/" not in ev:
+        raise Exception("Unexpected deauth imminent notice: " + ev)
+    hapd.request("DEAUTHENTICATE " + addr)
+    dev[0].wait_disconnected(timeout=10)
+    if "[TEMP-DISABLED]" not in dev[0].list_networks()[0]['flags']:
+        raise Exception("Network not marked temporarily disabled")
+    ev = dev[0].wait_event(["SME: Trying to authenticate",
+                            "Trying to associate",
+                            "CTRL-EVENT-CONNECTED"], timeout=5)
+    if ev is not None:
+        raise Exception("Unexpected connection attempt")
+
+def test_ap_hs20_deauth_req_bss(dev, apdev):
+    """Hotspot 2.0 connection and deauthentication request for BSS"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    try:
+        _test_ap_hs20_deauth_req_bss(dev, apdev)
+    finally:
+        dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_deauth_req_bss(dev, apdev):
+    dev[0].request("SET pmf 2")
+    eap_test(dev[0], apdev[0], "21[3:26]", "TTLS", "user")
+    dev[0].dump_monitor()
+    addr = dev[0].p2p_interface_addr()
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    hapd.request("HS20_DEAUTH_REQ " + addr + " 0 120 http://example.com/")
+    ev = dev[0].wait_event(["HS20-DEAUTH-IMMINENT-NOTICE"])
+    if ev is None:
+        raise Exception("Timeout on deauth imminent notice")
+    if "0 120 http://example.com/" not in ev:
+        raise Exception("Unexpected deauth imminent notice: " + ev)
+    hapd.request("DEAUTHENTICATE " + addr + " reason=4")
+    ev = dev[0].wait_disconnected(timeout=10)
+    if "reason=4" not in ev:
+        raise Exception("Unexpected disconnection reason")
+    if "[TEMP-DISABLED]" not in dev[0].list_networks()[0]['flags']:
+        raise Exception("Network not marked temporarily disabled")
+    ev = dev[0].wait_event(["SME: Trying to authenticate",
+                            "Trying to associate",
+                            "CTRL-EVENT-CONNECTED"], timeout=5)
+    if ev is not None:
+        raise Exception("Unexpected connection attempt")
+
+def test_ap_hs20_deauth_req_from_radius(dev, apdev):
+    """Hotspot 2.0 connection and deauthentication request from RADIUS"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    try:
+        _test_ap_hs20_deauth_req_from_radius(dev, apdev)
+    finally:
+        dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_deauth_req_from_radius(dev, apdev):
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com,21[2:4]" ]
+    params['hs20_deauth_req_timeout'] = "2"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SET pmf 2")
+    dev[0].hs20_enable()
+    dev[0].add_cred_values({ 'realm': "example.com",
+                             'username': "hs20-deauth-test",
+                             'password': "password" })
+    interworking_select(dev[0], bssid, freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+    ev = dev[0].wait_event(["HS20-DEAUTH-IMMINENT-NOTICE"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on deauth imminent notice")
+    if " 1 100" not in ev:
+        raise Exception("Unexpected deauth imminent contents")
+    dev[0].wait_disconnected(timeout=3)
+
+def test_ap_hs20_remediation_required(dev, apdev):
+    """Hotspot 2.0 connection and remediation required from RADIUS"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    try:
+        _test_ap_hs20_remediation_required(dev, apdev)
+    finally:
+        dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_remediation_required(dev, apdev):
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com,21[2:4]" ]
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SET pmf 1")
+    dev[0].hs20_enable()
+    dev[0].add_cred_values({ 'realm': "example.com",
+                             'username': "hs20-subrem-test",
+                             'password': "password" })
+    interworking_select(dev[0], bssid, freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+    ev = dev[0].wait_event(["HS20-SUBSCRIPTION-REMEDIATION"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on subscription remediation notice")
+    if " 1 https://example.com/" not in ev:
+        raise Exception("Unexpected subscription remediation event contents")
+
+def test_ap_hs20_remediation_required_ctrl(dev, apdev):
+    """Hotspot 2.0 connection and subrem from ctrl_iface"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    try:
+        _test_ap_hs20_remediation_required_ctrl(dev, apdev)
+    finally:
+        dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_remediation_required_ctrl(dev, apdev):
+    bssid = apdev[0]['bssid']
+    addr = dev[0].own_addr()
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com,21[2:4]" ]
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SET pmf 1")
+    dev[0].hs20_enable()
+    dev[0].add_cred_values(default_cred())
+    interworking_select(dev[0], bssid, freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+
+    hapd.request("HS20_WNM_NOTIF " + addr + " https://example.com/")
+    ev = dev[0].wait_event(["HS20-SUBSCRIPTION-REMEDIATION"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on subscription remediation notice")
+    if " 1 https://example.com/" not in ev:
+        raise Exception("Unexpected subscription remediation event contents")
+
+    hapd.request("HS20_WNM_NOTIF " + addr)
+    ev = dev[0].wait_event(["HS20-SUBSCRIPTION-REMEDIATION"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on subscription remediation notice")
+    if not ev.endswith("HS20-SUBSCRIPTION-REMEDIATION "):
+        raise Exception("Unexpected subscription remediation event contents: " + ev)
+
+    if "FAIL" not in hapd.request("HS20_WNM_NOTIF "):
+        raise Exception("Unexpected HS20_WNM_NOTIF success")
+    if "FAIL" not in hapd.request("HS20_WNM_NOTIF foo"):
+        raise Exception("Unexpected HS20_WNM_NOTIF success")
+    if "FAIL" not in hapd.request("HS20_WNM_NOTIF " + addr + " https://12345678923456789842345678456783456712345678923456789842345678456783456712345678923456789842345678456783456712345678923456789842345678456783456712345678923456789842345678456783456712345678923456789842345678456783456712345678923456789842345678456783456712345678927.very.long.example.com/"):
+        raise Exception("Unexpected HS20_WNM_NOTIF success")
+
+def test_ap_hs20_session_info(dev, apdev):
+    """Hotspot 2.0 connection and session information from RADIUS"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    try:
+        _test_ap_hs20_session_info(dev, apdev)
+    finally:
+        dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_session_info(dev, apdev):
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com,21[2:4]" ]
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SET pmf 1")
+    dev[0].hs20_enable()
+    dev[0].add_cred_values({ 'realm': "example.com",
+                             'username': "hs20-session-info-test",
+                             'password': "password" })
+    interworking_select(dev[0], bssid, freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+    ev = dev[0].wait_event(["ESS-DISASSOC-IMMINENT"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on ESS disassociation imminent notice")
+    if " 1 59904 https://example.com/" not in ev:
+        raise Exception("Unexpected ESS disassociation imminent event contents")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
+    if ev is None:
+        raise Exception("Scan not started")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=30)
+    if ev is None:
+        raise Exception("Scan not completed")
+
+def test_ap_hs20_osen(dev, apdev):
+    """Hotspot 2.0 OSEN connection"""
+    params = { 'ssid': "osen",
+               'osen': "1",
+               'auth_server_addr': "127.0.0.1",
+               'auth_server_port': "1812",
+               'auth_server_shared_secret': "radius" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[1].connect("osen", key_mgmt="NONE", scan_freq="2412",
+                   wait_connect=False)
+    dev[2].connect("osen", key_mgmt="NONE", wep_key0='"hello"',
+                   scan_freq="2412", wait_connect=False)
+    dev[0].flush_scan_cache()
+    dev[0].connect("osen", proto="OSEN", key_mgmt="OSEN", pairwise="CCMP",
+                   group="GTK_NOT_USED",
+                   eap="WFA-UNAUTH-TLS", identity="osen@example.com",
+                   ca_cert="auth_serv/ca.pem",
+                   scan_freq="2412")
+    res = dev[0].get_bss(apdev[0]['bssid'])['flags']
+    if "[OSEN-OSEN-CCMP]" not in res:
+        raise Exception("OSEN not reported in BSS")
+    if "[WEP]" in res:
+        raise Exception("WEP reported in BSS")
+    res = dev[0].request("SCAN_RESULTS")
+    if "[OSEN-OSEN-CCMP]" not in res:
+        raise Exception("OSEN not reported in SCAN_RESULTS")
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+    wpas.connect("osen", proto="OSEN", key_mgmt="OSEN", pairwise="CCMP",
+                 group="GTK_NOT_USED",
+                 eap="WFA-UNAUTH-TLS", identity="osen@example.com",
+                 ca_cert="auth_serv/ca.pem",
+                 scan_freq="2412")
+    wpas.request("DISCONNECT")
+
+def test_ap_hs20_network_preference(dev, apdev):
+    """Hotspot 2.0 network selection with preferred home network"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    values = { 'realm': "example.com",
+               'username': "hs20-test",
+               'password': "password",
+               'domain': "example.com" }
+    dev[0].add_cred_values(values)
+
+    id = dev[0].add_network()
+    dev[0].set_network_quoted(id, "ssid", "home")
+    dev[0].set_network_quoted(id, "psk", "12345678")
+    dev[0].set_network(id, "priority", "1")
+    dev[0].request("ENABLE_NETWORK %s no-connect" % id)
+
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    ev = dev[0].wait_connected(timeout=15)
+    if bssid not in ev:
+        raise Exception("Unexpected network selected")
+
+    bssid2 = apdev[1]['bssid']
+    params = hostapd.wpa2_params(ssid="home", passphrase="12345678")
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].scan_for_bss(bssid2, freq="2412")
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+                            "INTERWORKING-ALREADY-CONNECTED" ], timeout=15)
+    if ev is None:
+        raise Exception("Connection timed out")
+    if "INTERWORKING-ALREADY-CONNECTED" in ev:
+        raise Exception("No roam to higher priority network")
+    if bssid2 not in ev:
+        raise Exception("Unexpected network selected")
+
+def test_ap_hs20_network_preference2(dev, apdev):
+    """Hotspot 2.0 network selection with preferred credential"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid2 = apdev[1]['bssid']
+    params = hostapd.wpa2_params(ssid="home", passphrase="12345678")
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].hs20_enable()
+    values = { 'realm': "example.com",
+               'username': "hs20-test",
+               'password': "password",
+               'domain': "example.com",
+               'priority': "1" }
+    dev[0].add_cred_values(values)
+
+    id = dev[0].add_network()
+    dev[0].set_network_quoted(id, "ssid", "home")
+    dev[0].set_network_quoted(id, "psk", "12345678")
+    dev[0].request("ENABLE_NETWORK %s no-connect" % id)
+
+    dev[0].scan_for_bss(bssid2, freq="2412")
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    ev = dev[0].wait_connected(timeout=15)
+    if bssid2 not in ev:
+        raise Exception("Unexpected network selected")
+
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+                            "INTERWORKING-ALREADY-CONNECTED" ], timeout=15)
+    if ev is None:
+        raise Exception("Connection timed out")
+    if "INTERWORKING-ALREADY-CONNECTED" in ev:
+        raise Exception("No roam to higher priority network")
+    if bssid not in ev:
+        raise Exception("Unexpected network selected")
+
+def test_ap_hs20_network_preference3(dev, apdev):
+    """Hotspot 2.0 network selection with two credential (one preferred)"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params(ssid="test-hs20b")
+    params['nai_realm'] = "0,example.org,13[5:6],21[2:4][5:7]"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].hs20_enable()
+    values = { 'realm': "example.com",
+               'username': "hs20-test",
+               'password': "password",
+               'priority': "1" }
+    dev[0].add_cred_values(values)
+    values = { 'realm': "example.org",
+               'username': "hs20-test",
+               'password': "password" }
+    id = dev[0].add_cred_values(values)
+
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].scan_for_bss(bssid2, freq="2412")
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    ev = dev[0].wait_connected(timeout=15)
+    if bssid not in ev:
+        raise Exception("Unexpected network selected")
+
+    dev[0].set_cred(id, "priority", "2")
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+                            "INTERWORKING-ALREADY-CONNECTED" ], timeout=15)
+    if ev is None:
+        raise Exception("Connection timed out")
+    if "INTERWORKING-ALREADY-CONNECTED" in ev:
+        raise Exception("No roam to higher priority network")
+    if bssid2 not in ev:
+        raise Exception("Unexpected network selected")
+
+def test_ap_hs20_network_preference4(dev, apdev):
+    """Hotspot 2.0 network selection with username vs. SIM credential"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params(ssid="test-hs20b")
+    params['hessid'] = bssid2
+    params['anqp_3gpp_cell_net'] = "555,444"
+    params['domain_name'] = "wlan.mnc444.mcc555.3gppnetwork.org"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].hs20_enable()
+    values = { 'realm': "example.com",
+               'username': "hs20-test",
+               'password': "password",
+               'priority': "1" }
+    dev[0].add_cred_values(values)
+    values = { 'imsi': "555444-333222111",
+               'eap': "SIM",
+               'milenage': "5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123" }
+    id = dev[0].add_cred_values(values)
+
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].scan_for_bss(bssid2, freq="2412")
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    ev = dev[0].wait_connected(timeout=15)
+    if bssid not in ev:
+        raise Exception("Unexpected network selected")
+
+    dev[0].set_cred(id, "priority", "2")
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+                            "INTERWORKING-ALREADY-CONNECTED" ], timeout=15)
+    if ev is None:
+        raise Exception("Connection timed out")
+    if "INTERWORKING-ALREADY-CONNECTED" in ev:
+        raise Exception("No roam to higher priority network")
+    if bssid2 not in ev:
+        raise Exception("Unexpected network selected")
+
+def test_ap_hs20_interworking_select_blocking_scan(dev, apdev):
+    """Ongoing INTERWORKING_SELECT blocking SCAN"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    values = { 'realm': "example.com",
+               'username': "hs20-test",
+               'password': "password",
+               'domain': "example.com" }
+    dev[0].add_cred_values(values)
+
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].request("INTERWORKING_SELECT auto freq=2412")
+    if "FAIL-BUSY" not in dev[0].request("SCAN"):
+        raise Exception("Unexpected SCAN command result")
+    dev[0].wait_connected(timeout=15)
+
+def test_ap_hs20_fetch_osu(dev, apdev):
+    """Hotspot 2.0 OSU provider and icon fetch"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hs20_icon'] = "128:80:zxx:image/png:w1fi_logo:w1fi_logo.png"
+    params['osu_ssid'] = '"HS 2.0 OSU open"'
+    params['osu_method_list'] = "1"
+    params['osu_friendly_name'] = [ "eng:Test OSU", "fin:Testi-OSU" ]
+    params['osu_icon'] = "w1fi_logo"
+    params['osu_service_desc'] = [ "eng:Example services", "fin:Esimerkkipalveluja" ]
+    params['osu_server_uri'] = "https://example.com/osu/"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params(ssid="test-hs20b")
+    params['hessid'] = bssid2
+    params['hs20_icon'] = "128:80:zxx:image/png:w1fi_logo:w1fi_logo.png"
+    params['osu_ssid'] = '"HS 2.0 OSU OSEN"'
+    params['osu_method_list'] = "0"
+    params['osu_nai'] = "osen@example.com"
+    params['osu_friendly_name'] = [ "eng:Test2 OSU", "fin:Testi2-OSU" ]
+    params['osu_icon'] = "w1fi_logo"
+    params['osu_service_desc'] = [ "eng:Example services2", "fin:Esimerkkipalveluja2" ]
+    params['osu_server_uri'] = "https://example.org/osu/"
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    with open("w1fi_logo.png", "r") as f:
+        orig_logo = f.read()
+    dev[0].hs20_enable()
+    dir = "/tmp/osu-fetch"
+    if os.path.isdir(dir):
+       files = [ f for f in os.listdir(dir) if f.startswith("osu-") ]
+       for f in files:
+           os.remove(dir + "/" + f)
+    else:
+        try:
+            os.makedirs(dir)
+        except:
+            pass
+    try:
+        dev[1].scan_for_bss(bssid, freq="2412")
+        dev[0].request("SET osu_dir " + dir)
+        dev[0].request("FETCH_OSU")
+        if "FAIL" not in dev[1].request("HS20_ICON_REQUEST foo w1fi_logo"):
+            raise Exception("Invalid HS20_ICON_REQUEST accepted")
+        if "OK" not in dev[1].request("HS20_ICON_REQUEST " + bssid + " w1fi_logo"):
+            raise Exception("HS20_ICON_REQUEST failed")
+        icons = 0
+        while True:
+            ev = dev[0].wait_event(["OSU provider fetch completed",
+                                    "RX-HS20-ANQP-ICON"], timeout=15)
+            if ev is None:
+                raise Exception("Timeout on OSU fetch")
+            if "OSU provider fetch completed" in ev:
+                break
+            if "RX-HS20-ANQP-ICON" in ev:
+                with open(ev.split(' ')[1], "r") as f:
+                    logo = f.read()
+                    if logo == orig_logo:
+                        icons += 1
+
+        with open(dir + "/osu-providers.txt", "r") as f:
+            prov = f.read()
+            logger.debug("osu-providers.txt: " + prov)
+        if "OSU-PROVIDER " + bssid not in prov:
+            raise Exception("Missing OSU_PROVIDER(1)")
+        if "OSU-PROVIDER " + bssid2 not in prov:
+            raise Exception("Missing OSU_PROVIDER(2)")
+    finally:
+        files = [ f for f in os.listdir(dir) if f.startswith("osu-") ]
+        for f in files:
+            os.remove(dir + "/" + f)
+        os.rmdir(dir)
+
+    if icons != 2:
+        raise Exception("Unexpected number of icons fetched")
+
+    ev = dev[1].wait_event(["GAS-QUERY-START"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on GAS-QUERY-DONE")
+    ev = dev[1].wait_event(["GAS-QUERY-DONE"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on GAS-QUERY-DONE")
+    if "freq=2412 status_code=0 result=SUCCESS" not in ev:
+        raise Exception("Unexpected GAS-QUERY-DONE: " + ev)
+    ev = dev[1].wait_event(["RX-HS20-ANQP"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on icon fetch")
+    if "Icon Binary File" not in ev:
+        raise Exception("Unexpected ANQP element")
+
+def test_ap_hs20_fetch_osu_stop(dev, apdev):
+    """Hotspot 2.0 OSU provider fetch stopped"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hs20_icon'] = "128:80:zxx:image/png:w1fi_logo:w1fi_logo.png"
+    params['osu_ssid'] = '"HS 2.0 OSU open"'
+    params['osu_method_list'] = "1"
+    params['osu_friendly_name'] = [ "eng:Test OSU", "fin:Testi-OSU" ]
+    params['osu_icon'] = "w1fi_logo"
+    params['osu_service_desc'] = [ "eng:Example services", "fin:Esimerkkipalveluja" ]
+    params['osu_server_uri'] = "https://example.com/osu/"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dir = "/tmp/osu-fetch"
+    if os.path.isdir(dir):
+       files = [ f for f in os.listdir(dir) if f.startswith("osu-") ]
+       for f in files:
+           os.remove(dir + "/" + f)
+    else:
+        try:
+            os.makedirs(dir)
+        except:
+            pass
+    try:
+        dev[0].request("SET osu_dir " + dir)
+        dev[0].request("SCAN freq=2412-2462")
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=10)
+        if ev is None:
+            raise Exception("Scan did not start")
+        if "FAIL" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU accepted while scanning")
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+        if ev is None:
+            raise Exception("Scan timed out")
+        hapd.set("ext_mgmt_frame_handling", "1")
+        dev[0].request("FETCH_ANQP")
+        if "FAIL" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU accepted while in FETCH_ANQP")
+        dev[0].request("STOP_FETCH_ANQP")
+        dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5)
+        dev[0].dump_monitor()
+        hapd.dump_monitor()
+        dev[0].request("INTERWORKING_SELECT freq=2412")
+        for i in range(5):
+            msg = hapd.mgmt_rx()
+            if msg['subtype'] == 13:
+                break
+        if "FAIL" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU accepted while in INTERWORKING_SELECT")
+        ev = dev[0].wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH"],
+                               timeout=15)
+        if ev is None:
+            raise Exception("Network selection timed out");
+
+        dev[0].dump_monitor()
+        if "OK" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU failed")
+        dev[0].request("CANCEL_FETCH_OSU")
+
+        for i in range(15):
+            time.sleep(0.5)
+            if dev[0].get_driver_status_field("scan_state") == "SCAN_COMPLETED":
+                break
+
+        dev[0].dump_monitor()
+        if "OK" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU failed")
+        if "FAIL" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU accepted while in FETCH_OSU")
+        ev = dev[0].wait_event(["GAS-QUERY-START"], 10)
+        if ev is None:
+            raise Exception("GAS timed out")
+        if "FAIL" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU accepted while in FETCH_OSU")
+        dev[0].request("CANCEL_FETCH_OSU")
+        ev = dev[0].wait_event(["GAS-QUERY-DONE"], 10)
+        if ev is None:
+            raise Exception("GAS event timed out after CANCEL_FETCH_OSU")
+    finally:
+        files = [ f for f in os.listdir(dir) if f.startswith("osu-") ]
+        for f in files:
+            os.remove(dir + "/" + f)
+        os.rmdir(dir)
+
+def test_ap_hs20_ft(dev, apdev):
+    """Hotspot 2.0 connection with FT"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['wpa_key_mgmt'] = "FT-EAP"
+    params['nas_identifier'] = "nas1.w1.fi"
+    params['r1_key_holder'] = "000102030405"
+    params["mobility_domain"] = "a1b2"
+    params["reassociation_deadline"] = "1000"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'domain': "example.com",
+                                  'update_identifier': "1234" })
+    interworking_select(dev[0], bssid, "home", freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+
+def test_ap_hs20_remediation_sql(dev, apdev, params):
+    """Hotspot 2.0 connection and remediation required using SQLite for user DB"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    try:
+        import sqlite3
+    except ImportError:
+        raise HwsimSkip("No sqlite3 module available")
+    dbfile = os.path.join(params['logdir'], "eap-user.db")
+    try:
+        os.remove(dbfile)
+    except:
+        pass
+    con = sqlite3.connect(dbfile)
+    with con:
+        cur = con.cursor()
+        cur.execute("CREATE TABLE users(identity TEXT PRIMARY KEY, methods TEXT, password TEXT, remediation TEXT, phase2 INTEGER)")
+        cur.execute("CREATE TABLE wildcards(identity TEXT PRIMARY KEY, methods TEXT)")
+        cur.execute("INSERT INTO users(identity,methods,password,phase2,remediation) VALUES ('user-mschapv2','TTLS-MSCHAPV2','password',1,'user')")
+        cur.execute("INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS')")
+        cur.execute("CREATE TABLE authlog(timestamp TEXT, session TEXT, nas_ip TEXT, username TEXT, note TEXT)")
+
+    try:
+        params = { "ssid": "as", "beacon_int": "2000",
+                   "radius_server_clients": "auth_serv/radius_clients.conf",
+                   "radius_server_auth_port": '18128',
+                   "eap_server": "1",
+                   "eap_user_file": "sqlite:" + dbfile,
+                   "ca_cert": "auth_serv/ca.pem",
+                   "server_cert": "auth_serv/server.pem",
+                   "private_key": "auth_serv/server.key",
+                   "subscr_remediation_url": "https://example.org/",
+                   "subscr_remediation_method": "1" }
+        hostapd.add_ap(apdev[1]['ifname'], params)
+
+        bssid = apdev[0]['bssid']
+        params = hs20_ap_params()
+        params['auth_server_port'] = "18128"
+        hostapd.add_ap(apdev[0]['ifname'], params)
+
+        dev[0].request("SET pmf 1")
+        dev[0].hs20_enable()
+        id = dev[0].add_cred_values({ 'realm': "example.com",
+                                      'username': "user-mschapv2",
+                                      'password': "password",
+                                      'ca_cert': "auth_serv/ca.pem" })
+        interworking_select(dev[0], bssid, freq="2412")
+        interworking_connect(dev[0], bssid, "TTLS")
+        ev = dev[0].wait_event(["HS20-SUBSCRIPTION-REMEDIATION"], timeout=5)
+        if ev is None:
+            raise Exception("Timeout on subscription remediation notice")
+        if " 1 https://example.org/" not in ev:
+            raise Exception("Unexpected subscription remediation event contents")
+
+        with con:
+            cur = con.cursor()
+            cur.execute("SELECT * from authlog")
+            rows = cur.fetchall()
+            if len(rows) < 1:
+                raise Exception("No authlog entries")
+
+    finally:
+        os.remove(dbfile)
+        dev[0].request("SET pmf 0")
+
+def test_ap_hs20_external_selection(dev, apdev):
+    """Hotspot 2.0 connection using external network selection and creation"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['disable_dgaf'] = '1'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].connect("test-hs20", proto="RSN", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="hs20-test", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                   scan_freq="2412", update_identifier="54321")
+    if dev[0].get_status_field("hs20") != "2":
+        raise Exception("Unexpected hs20 indication")
+
+def test_ap_hs20_random_mac_addr(dev, apdev):
+    """Hotspot 2.0 connection with random MAC address"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['disable_dgaf'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    addr = wpas.p2p_interface_addr()
+    wpas.request("SET mac_addr 1")
+    wpas.request("SET preassoc_mac_addr 1")
+    wpas.request("SET rand_addr_lifetime 60")
+    wpas.hs20_enable()
+    wpas.flush_scan_cache()
+    id = wpas.add_cred_values({ 'realm': "example.com",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'domain': "example.com",
+                                  'update_identifier': "1234" })
+    interworking_select(wpas, bssid, "home", freq="2412")
+    interworking_connect(wpas, bssid, "TTLS")
+    addr1 = wpas.get_driver_status_field("addr")
+    if addr == addr1:
+        raise Exception("Did not use random MAC address")
+
+    sta = hapd.get_sta(addr)
+    if sta['addr'] != "FAIL":
+        raise Exception("Unexpected STA association with permanent address")
+    sta = hapd.get_sta(addr1)
+    if sta['addr'] != addr1:
+        raise Exception("STA association with random address not found")
+
+def test_ap_hs20_multi_network_and_cred_removal(dev, apdev):
+    """Multiple networks and cred removal"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com,25[3:26]"]
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].add_network()
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'username': "user",
+                                  'password': "password" })
+    interworking_select(dev[0], bssid, freq="2412")
+    interworking_connect(dev[0], bssid, "PEAP")
+    dev[0].add_network()
+
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=10)
+
+    hapd.disable()
+    hapd.set("ssid", "another ssid")
+    hapd.enable()
+
+    interworking_select(dev[0], bssid, freq="2412")
+    interworking_connect(dev[0], bssid, "PEAP")
+    dev[0].add_network()
+    if len(dev[0].list_networks()) != 5:
+        raise Exception("Unexpected number of networks prior to remove_crec")
+
+    dev[0].dump_monitor()
+    dev[0].remove_cred(id)
+    if len(dev[0].list_networks()) != 3:
+        raise Exception("Unexpected number of networks after to remove_crec")
+    dev[0].wait_disconnected(timeout=10)
+
+def test_ap_hs20_interworking_add_network(dev, apdev):
+    """Hotspot 2.0 connection using INTERWORKING_ADD_NETWORK"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com,21[3:26][6:7][99:99]" ]
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].add_cred_values(default_cred(user="user"))
+    interworking_select(dev[0], bssid, freq=2412)
+    id = dev[0].interworking_add_network(bssid)
+    dev[0].select_network(id, freq=2412)
+    dev[0].wait_connected()
+
+def _test_ap_hs20_proxyarp(dev, apdev):
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['disable_dgaf'] = '0'
+    params['proxy_arp'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, no_enable=True)
+    if "OK" in hapd.request("ENABLE"):
+        raise Exception("Incomplete hostapd configuration was accepted")
+    hapd.set("ap_isolate", "1")
+    if "OK" in hapd.request("ENABLE"):
+        raise Exception("Incomplete hostapd configuration was accepted")
+    hapd.set('bridge', 'ap-br0')
+    hapd.dump_monitor()
+    try:
+        hapd.enable()
+    except:
+        # For now, do not report failures due to missing kernel support
+        raise HwsimSkip("Could not start hostapd - assume proxyarp not supported in kernel version")
+    ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
+    if ev is None:
+        raise Exception("AP startup timed out")
+    if "AP-ENABLED" not in ev:
+        raise Exception("AP startup failed")
+
+    dev[0].hs20_enable()
+    subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+    subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'domain': "example.com",
+                                  'update_identifier': "1234" })
+    interworking_select(dev[0], bssid, "home", freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+
+    dev[1].connect("test-hs20", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="hs20-test", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                   scan_freq="2412")
+    time.sleep(0.1)
+
+    addr0 = dev[0].p2p_interface_addr()
+    addr1 = dev[1].p2p_interface_addr()
+
+    src_ll_opt0 = "\x01\x01" + binascii.unhexlify(addr0.replace(':',''))
+    src_ll_opt1 = "\x01\x01" + binascii.unhexlify(addr1.replace(':',''))
+
+    pkt = build_ns(src_ll=addr0, ip_src="aaaa:bbbb:cccc::2",
+                   ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:cccc::2",
+                   opt=src_ll_opt0)
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    pkt = build_ns(src_ll=addr1, ip_src="aaaa:bbbb:dddd::2",
+                   ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:dddd::2",
+                   opt=src_ll_opt1)
+    if "OK" not in dev[1].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    pkt = build_ns(src_ll=addr1, ip_src="aaaa:bbbb:eeee::2",
+                   ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:eeee::2",
+                   opt=src_ll_opt1)
+    if "OK" not in dev[1].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    matches = get_permanent_neighbors("ap-br0")
+    logger.info("After connect: " + str(matches))
+    if len(matches) != 3:
+        raise Exception("Unexpected number of neighbor entries after connect")
+    if 'aaaa:bbbb:cccc::2 dev ap-br0 lladdr 02:00:00:00:00:00 PERMANENT' not in matches:
+        raise Exception("dev0 addr missing")
+    if 'aaaa:bbbb:dddd::2 dev ap-br0 lladdr 02:00:00:00:01:00 PERMANENT' not in matches:
+        raise Exception("dev1 addr(1) missing")
+    if 'aaaa:bbbb:eeee::2 dev ap-br0 lladdr 02:00:00:00:01:00 PERMANENT' not in matches:
+        raise Exception("dev1 addr(2) missing")
+    dev[0].request("DISCONNECT")
+    dev[1].request("DISCONNECT")
+    time.sleep(0.5)
+    matches = get_permanent_neighbors("ap-br0")
+    logger.info("After disconnect: " + str(matches))
+    if len(matches) > 0:
+        raise Exception("Unexpected neighbor entries after disconnect")
+
+def test_ap_hs20_hidden_ssid_in_scan_res(dev, apdev):
+    """Hotspot 2.0 connection with hidden SSId in scan results"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": 'secret',
+                                                "ignore_broadcast_ssid": "1" })
+    dev[0].scan_for_bss(bssid, freq=2412)
+    hapd.disable()
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.flush()
+    hapd_global.remove(apdev[0]['ifname'])
+
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'domain': "example.com" })
+    interworking_select(dev[0], bssid, "home", freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+
+    # clear BSS table to avoid issues in following test cases
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    hapd.disable()
+    dev[0].flush_scan_cache()
+    dev[0].flush_scan_cache()
+
+def test_ap_hs20_proxyarp(dev, apdev):
+    """Hotspot 2.0 and ProxyARP"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    try:
+        _test_ap_hs20_proxyarp(dev, apdev)
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['brctl', 'delbr', 'ap-br0'],
+                        stderr=open('/dev/null', 'w'))
+
+def _test_ap_hs20_proxyarp_dgaf(dev, apdev, disabled):
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['disable_dgaf'] = '1' if disabled else '0'
+    params['proxy_arp'] = '1'
+    params['na_mcast_to_ucast'] = '1'
+    params['ap_isolate'] = '1'
+    params['bridge'] = 'ap-br0'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, no_enable=True)
+    try:
+        hapd.enable()
+    except:
+        # For now, do not report failures due to missing kernel support
+        raise HwsimSkip("Could not start hostapd - assume proxyarp not supported in kernel version")
+    ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+    if ev is None:
+        raise Exception("AP startup timed out")
+
+    dev[0].hs20_enable()
+    subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+    subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'domain': "example.com",
+                                  'update_identifier': "1234" })
+    interworking_select(dev[0], bssid, "home", freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+
+    dev[1].connect("test-hs20", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="hs20-test", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                   scan_freq="2412")
+    time.sleep(0.1)
+
+    addr0 = dev[0].p2p_interface_addr()
+
+    src_ll_opt0 = "\x01\x01" + binascii.unhexlify(addr0.replace(':',''))
+
+    pkt = build_ns(src_ll=addr0, ip_src="aaaa:bbbb:cccc::2",
+                   ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:cccc::2",
+                   opt=src_ll_opt0)
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    pkt = build_ra(src_ll=apdev[0]['bssid'], ip_src="aaaa:bbbb:cccc::33",
+                   ip_dst="ff01::1")
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    pkt = build_na(src_ll=apdev[0]['bssid'], ip_src="aaaa:bbbb:cccc::44",
+                   ip_dst="ff01::1", target="aaaa:bbbb:cccc::55")
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    pkt = build_dhcp_ack(dst_ll="ff:ff:ff:ff:ff:ff", src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.123", chaddr=addr0)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+    # another copy for additional code coverage
+    pkt = build_dhcp_ack(dst_ll=addr0, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.123", chaddr=addr0)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    matches = get_permanent_neighbors("ap-br0")
+    logger.info("After connect: " + str(matches))
+    if len(matches) != 2:
+        raise Exception("Unexpected number of neighbor entries after connect")
+    if 'aaaa:bbbb:cccc::2 dev ap-br0 lladdr 02:00:00:00:00:00 PERMANENT' not in matches:
+        raise Exception("dev0 addr missing")
+    if '192.168.1.123 dev ap-br0 lladdr 02:00:00:00:00:00 PERMANENT' not in matches:
+        raise Exception("dev0 IPv4 addr missing")
+    dev[0].request("DISCONNECT")
+    dev[1].request("DISCONNECT")
+    time.sleep(0.5)
+    matches = get_permanent_neighbors("ap-br0")
+    logger.info("After disconnect: " + str(matches))
+    if len(matches) > 0:
+        raise Exception("Unexpected neighbor entries after disconnect")
+
+def test_ap_hs20_proxyarp_disable_dgaf(dev, apdev):
+    """Hotspot 2.0 and ProxyARP with DGAF disabled"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    try:
+        _test_ap_hs20_proxyarp_dgaf(dev, apdev, True)
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['brctl', 'delbr', 'ap-br0'],
+                        stderr=open('/dev/null', 'w'))
+
+def test_ap_hs20_proxyarp_enable_dgaf(dev, apdev):
+    """Hotspot 2.0 and ProxyARP with DGAF enabled"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    try:
+        _test_ap_hs20_proxyarp_dgaf(dev, apdev, False)
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['brctl', 'delbr', 'ap-br0'],
+                        stderr=open('/dev/null', 'w'))
+
+def ip_checksum(buf):
+    sum = 0
+    if len(buf) & 0x01:
+        buf += '\0x00'
+    for i in range(0, len(buf), 2):
+        val, = struct.unpack('H', buf[i:i+2])
+        sum += val
+    while (sum >> 16):
+        sum = (sum & 0xffff) + (sum >> 16)
+    return struct.pack('H', ~sum & 0xffff)
+
+def ipv6_solicited_node_mcaddr(target):
+    prefix = socket.inet_pton(socket.AF_INET6, "ff02::1:ff00:0")
+    mask = socket.inet_pton(socket.AF_INET6, "::ff:ffff")
+    _target = socket.inet_pton(socket.AF_INET6, target)
+    p = struct.unpack('4I', prefix)
+    m = struct.unpack('4I', mask)
+    t = struct.unpack('4I', _target)
+    res = (p[0] | (t[0] & m[0]),
+           p[1] | (t[1] & m[1]),
+           p[2] | (t[2] & m[2]),
+           p[3] | (t[3] & m[3]))
+    return socket.inet_ntop(socket.AF_INET6, struct.pack('4I', *res))
+
+def build_icmpv6(ipv6_addrs, type, code, payload):
+    start = struct.pack("BB", type, code)
+    end = payload
+    icmp = start + '\x00\x00' + end
+    pseudo = ipv6_addrs + struct.pack(">LBBBB", len(icmp), 0, 0, 0, 58)
+    csum = ip_checksum(pseudo + icmp)
+    return start + csum + end
+
+def build_ra(src_ll, ip_src, ip_dst, cur_hop_limit=0, router_lifetime=0,
+             reachable_time=0, retrans_timer=0, opt=None):
+    link_mc = binascii.unhexlify("3333ff000002")
+    _src_ll = binascii.unhexlify(src_ll.replace(':',''))
+    proto = '\x86\xdd'
+    ehdr = link_mc + _src_ll + proto
+    _ip_src = socket.inet_pton(socket.AF_INET6, ip_src)
+    _ip_dst = socket.inet_pton(socket.AF_INET6, ip_dst)
+
+    adv = struct.pack('>BBHLL', cur_hop_limit, 0, router_lifetime,
+                      reachable_time, retrans_timer)
+    if opt:
+        payload = adv + opt
+    else:
+        payload = adv
+    icmp = build_icmpv6(_ip_src + _ip_dst, 134, 0, payload)
+
+    ipv6 = struct.pack('>BBBBHBB', 0x60, 0, 0, 0, len(icmp), 58, 255)
+    ipv6 += _ip_src + _ip_dst
+
+    return ehdr + ipv6 + icmp
+
+def build_ns(src_ll, ip_src, ip_dst, target, opt=None):
+    link_mc = binascii.unhexlify("3333ff000002")
+    _src_ll = binascii.unhexlify(src_ll.replace(':',''))
+    proto = '\x86\xdd'
+    ehdr = link_mc + _src_ll + proto
+    _ip_src = socket.inet_pton(socket.AF_INET6, ip_src)
+    if ip_dst is None:
+        ip_dst = ipv6_solicited_node_mcaddr(target)
+    _ip_dst = socket.inet_pton(socket.AF_INET6, ip_dst)
+
+    reserved = '\x00\x00\x00\x00'
+    _target = socket.inet_pton(socket.AF_INET6, target)
+    if opt:
+        payload = reserved + _target + opt
+    else:
+        payload = reserved + _target
+    icmp = build_icmpv6(_ip_src + _ip_dst, 135, 0, payload)
+
+    ipv6 = struct.pack('>BBBBHBB', 0x60, 0, 0, 0, len(icmp), 58, 255)
+    ipv6 += _ip_src + _ip_dst
+
+    return ehdr + ipv6 + icmp
+
+def send_ns(dev, src_ll=None, target=None, ip_src=None, ip_dst=None, opt=None,
+            hapd_bssid=None):
+    if hapd_bssid:
+        if src_ll is None:
+            src_ll = hapd_bssid
+        cmd = "DATA_TEST_FRAME ifname=ap-br0 "
+    else:
+        if src_ll is None:
+            src_ll = dev.p2p_interface_addr()
+        cmd = "DATA_TEST_FRAME "
+
+    if opt is None:
+        opt = "\x01\x01" + binascii.unhexlify(src_ll.replace(':',''))
+
+    pkt = build_ns(src_ll=src_ll, ip_src=ip_src, ip_dst=ip_dst, target=target,
+                   opt=opt)
+    if "OK" not in dev.request(cmd + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+def build_na(src_ll, ip_src, ip_dst, target, opt=None):
+    link_mc = binascii.unhexlify("3333ff000002")
+    _src_ll = binascii.unhexlify(src_ll.replace(':',''))
+    proto = '\x86\xdd'
+    ehdr = link_mc + _src_ll + proto
+    _ip_src = socket.inet_pton(socket.AF_INET6, ip_src)
+    _ip_dst = socket.inet_pton(socket.AF_INET6, ip_dst)
+
+    reserved = '\x00\x00\x00\x00'
+    _target = socket.inet_pton(socket.AF_INET6, target)
+    if opt:
+        payload = reserved + _target + opt
+    else:
+        payload = reserved + _target
+    icmp = build_icmpv6(_ip_src + _ip_dst, 136, 0, payload)
+
+    ipv6 = struct.pack('>BBBBHBB', 0x60, 0, 0, 0, len(icmp), 58, 255)
+    ipv6 += _ip_src + _ip_dst
+
+    return ehdr + ipv6 + icmp
+
+def send_na(dev, src_ll=None, target=None, ip_src=None, ip_dst=None, opt=None,
+            hapd_bssid=None):
+    if hapd_bssid:
+        if src_ll is None:
+            src_ll = hapd_bssid
+        cmd = "DATA_TEST_FRAME ifname=ap-br0 "
+    else:
+        if src_ll is None:
+            src_ll = dev.p2p_interface_addr()
+        cmd = "DATA_TEST_FRAME "
+
+    pkt = build_na(src_ll=src_ll, ip_src=ip_src, ip_dst=ip_dst, target=target,
+                   opt=opt)
+    if "OK" not in dev.request(cmd + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+def build_dhcp_ack(dst_ll, src_ll, ip_src, ip_dst, yiaddr, chaddr,
+                   subnet_mask="255.255.255.0", truncated_opt=False,
+                   wrong_magic=False, force_tot_len=None, no_dhcp=False):
+    _dst_ll = binascii.unhexlify(dst_ll.replace(':',''))
+    _src_ll = binascii.unhexlify(src_ll.replace(':',''))
+    proto = '\x08\x00'
+    ehdr = _dst_ll + _src_ll + proto
+    _ip_src = socket.inet_pton(socket.AF_INET, ip_src)
+    _ip_dst = socket.inet_pton(socket.AF_INET, ip_dst)
+    _subnet_mask = socket.inet_pton(socket.AF_INET, subnet_mask)
+
+    _ciaddr = '\x00\x00\x00\x00'
+    _yiaddr = socket.inet_pton(socket.AF_INET, yiaddr)
+    _siaddr = '\x00\x00\x00\x00'
+    _giaddr = '\x00\x00\x00\x00'
+    _chaddr = binascii.unhexlify(chaddr.replace(':','') + "00000000000000000000")
+    payload = struct.pack('>BBBBL3BB', 2, 1, 6, 0, 12345, 0, 0, 0, 0)
+    payload += _ciaddr + _yiaddr + _siaddr + _giaddr + _chaddr + 192*'\x00'
+    # magic
+    if wrong_magic:
+        payload += '\x63\x82\x53\x00'
+    else:
+        payload += '\x63\x82\x53\x63'
+    if truncated_opt:
+        payload += '\x22\xff\x00'
+    # Option: DHCP Message Type = ACK
+    payload += '\x35\x01\x05'
+    # Pad Option
+    payload += '\x00'
+    # Option: Subnet Mask
+    payload += '\x01\x04' + _subnet_mask
+    # Option: Time Offset
+    payload += struct.pack('>BBL', 2, 4, 0)
+    # End Option
+    payload += '\xff'
+    # Pad Option
+    payload += '\x00\x00\x00\x00'
+
+    if no_dhcp:
+        payload = struct.pack('>BBBBL3BB', 2, 1, 6, 0, 12345, 0, 0, 0, 0)
+        payload += _ciaddr + _yiaddr + _siaddr + _giaddr + _chaddr + 192*'\x00'
+
+    udp = struct.pack('>HHHH', 67, 68, 8 + len(payload), 0) + payload
+
+    if force_tot_len:
+        tot_len = force_tot_len
+    else:
+        tot_len = 20 + len(udp)
+    start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
+    ipv4 = start + '\x00\x00' + _ip_src + _ip_dst
+    csum = ip_checksum(ipv4)
+    ipv4 = start + csum + _ip_src + _ip_dst
+
+    return ehdr + ipv4 + udp
+
+def build_arp(dst_ll, src_ll, opcode, sender_mac, sender_ip,
+              target_mac, target_ip):
+    _dst_ll = binascii.unhexlify(dst_ll.replace(':',''))
+    _src_ll = binascii.unhexlify(src_ll.replace(':',''))
+    proto = '\x08\x06'
+    ehdr = _dst_ll + _src_ll + proto
+
+    _sender_mac = binascii.unhexlify(sender_mac.replace(':',''))
+    _sender_ip = socket.inet_pton(socket.AF_INET, sender_ip)
+    _target_mac = binascii.unhexlify(target_mac.replace(':',''))
+    _target_ip = socket.inet_pton(socket.AF_INET, target_ip)
+
+    arp = struct.pack('>HHBBH', 1, 0x0800, 6, 4, opcode)
+    arp += _sender_mac + _sender_ip
+    arp += _target_mac + _target_ip
+
+    return ehdr + arp
+
+def send_arp(dev, dst_ll="ff:ff:ff:ff:ff:ff", src_ll=None, opcode=1,
+             sender_mac=None, sender_ip="0.0.0.0",
+             target_mac="00:00:00:00:00:00", target_ip="0.0.0.0",
+             hapd_bssid=None):
+    if hapd_bssid:
+        if src_ll is None:
+            src_ll = hapd_bssid
+        if sender_mac is None:
+            sender_mac = hapd_bssid
+        cmd = "DATA_TEST_FRAME ifname=ap-br0 "
+    else:
+        if src_ll is None:
+            src_ll = dev.p2p_interface_addr()
+        if sender_mac is None:
+            sender_mac = dev.p2p_interface_addr()
+        cmd = "DATA_TEST_FRAME "
+
+    pkt = build_arp(dst_ll=dst_ll, src_ll=src_ll, opcode=opcode,
+                    sender_mac=sender_mac, sender_ip=sender_ip,
+                    target_mac=target_mac, target_ip=target_ip)
+    if "OK" not in dev.request(cmd + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+def get_permanent_neighbors(ifname):
+    cmd = subprocess.Popen(['ip', 'nei'], stdout=subprocess.PIPE)
+    res = cmd.stdout.read()
+    cmd.stdout.close()
+    return [ line for line in res.splitlines() if "PERMANENT" in line and ifname in line ]
+
+def get_bridge_macs(ifname):
+    cmd = subprocess.Popen(['brctl', 'showmacs', ifname],
+                           stdout=subprocess.PIPE)
+    res = cmd.stdout.read()
+    cmd.stdout.close()
+    return res
+
+def tshark_get_arp(cap, filter):
+    res = run_tshark(cap, filter,
+                     [ "eth.dst", "eth.src",
+                       "arp.src.hw_mac", "arp.src.proto_ipv4",
+                       "arp.dst.hw_mac", "arp.dst.proto_ipv4" ],
+                     wait=False)
+    frames = []
+    for l in res.splitlines():
+        frames.append(l.split('\t'))
+    return frames
+
+def tshark_get_ns(cap):
+    res = run_tshark(cap, "icmpv6.type == 135",
+                     [ "eth.dst", "eth.src",
+                       "ipv6.src", "ipv6.dst",
+                       "icmpv6.nd.ns.target_address",
+                       "icmpv6.opt.linkaddr" ],
+                     wait=False)
+    frames = []
+    for l in res.splitlines():
+        frames.append(l.split('\t'))
+    return frames
+
+def tshark_get_na(cap):
+    res = run_tshark(cap, "icmpv6.type == 136",
+                     [ "eth.dst", "eth.src",
+                       "ipv6.src", "ipv6.dst",
+                       "icmpv6.nd.na.target_address",
+                       "icmpv6.opt.linkaddr" ],
+                     wait=False)
+    frames = []
+    for l in res.splitlines():
+        frames.append(l.split('\t'))
+    return frames
+
+def _test_proxyarp_open(dev, apdev, params, ebtables=False):
+    prefix = "proxyarp_open"
+    if ebtables:
+        prefix += "_ebtables"
+    cap_br = os.path.join(params['logdir'], prefix + ".ap-br0.pcap")
+    cap_dev0 = os.path.join(params['logdir'],
+                            prefix + ".%s.pcap" % dev[0].ifname)
+    cap_dev1 = os.path.join(params['logdir'],
+                            prefix + ".%s.pcap" % dev[1].ifname)
+    cap_dev2 = os.path.join(params['logdir'],
+                            prefix + ".%s.pcap" % dev[2].ifname)
+
+    bssid = apdev[0]['bssid']
+    params = { 'ssid': 'open' }
+    params['proxy_arp'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, no_enable=True)
+    hapd.set("ap_isolate", "1")
+    hapd.set('bridge', 'ap-br0')
+    hapd.dump_monitor()
+    try:
+        hapd.enable()
+    except:
+        # For now, do not report failures due to missing kernel support
+        raise HwsimSkip("Could not start hostapd - assume proxyarp not supported in kernel version")
+    ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
+    if ev is None:
+        raise Exception("AP startup timed out")
+    if "AP-ENABLED" not in ev:
+        raise Exception("AP startup failed")
+
+    params2 = { 'ssid': 'another' }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params2, no_enable=True)
+    hapd2.set('bridge', 'ap-br0')
+    hapd2.enable()
+
+    subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+    subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+
+    if ebtables:
+        for chain in [ 'FORWARD', 'OUTPUT' ]:
+            subprocess.call(['ebtables', '-A', chain, '-p', 'ARP',
+                             '-d', 'Broadcast', '-o', apdev[0]['ifname'],
+                             '-j', 'DROP'])
+            subprocess.call(['ebtables', '-A', chain, '-d', 'Multicast',
+                             '-p', 'IPv6', '--ip6-protocol', 'ipv6-icmp',
+                             '--ip6-icmp-type', 'neighbor-solicitation',
+                             '-o', apdev[0]['ifname'], '-j', 'DROP'])
+            subprocess.call(['ebtables', '-A', chain, '-d', 'Multicast',
+                             '-p', 'IPv6', '--ip6-protocol', 'ipv6-icmp',
+                             '--ip6-icmp-type', 'neighbor-advertisement',
+                             '-o', apdev[0]['ifname'], '-j', 'DROP'])
+            subprocess.call(['ebtables', '-A', chain,
+                             '-p', 'IPv6', '--ip6-protocol', 'ipv6-icmp',
+                             '--ip6-icmp-type', 'router-solicitation',
+                             '-o', apdev[0]['ifname'], '-j', 'DROP'])
+            # Multicast Listener Report Message
+            subprocess.call(['ebtables', '-A', chain, '-d', 'Multicast',
+                             '-p', 'IPv6', '--ip6-protocol', 'ipv6-icmp',
+                             '--ip6-icmp-type', '143',
+                             '-o', apdev[0]['ifname'], '-j', 'DROP'])
+
+    time.sleep(0.5)
+    cmd = {}
+    cmd[0] = subprocess.Popen(['tcpdump', '-p', '-U', '-i', 'ap-br0',
+                               '-w', cap_br, '-s', '2000'],
+                              stderr=open('/dev/null', 'w'))
+    cmd[1] = subprocess.Popen(['tcpdump', '-p', '-U', '-i', dev[0].ifname,
+                               '-w', cap_dev0, '-s', '2000'],
+                              stderr=open('/dev/null', 'w'))
+    cmd[2] = subprocess.Popen(['tcpdump', '-p', '-U', '-i', dev[1].ifname,
+                               '-w', cap_dev1, '-s', '2000'],
+                              stderr=open('/dev/null', 'w'))
+    cmd[3] = subprocess.Popen(['tcpdump', '-p', '-U', '-i', dev[2].ifname,
+                               '-w', cap_dev2, '-s', '2000'],
+                              stderr=open('/dev/null', 'w'))
+
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+    dev[1].connect("open", key_mgmt="NONE", scan_freq="2412")
+    dev[2].connect("another", key_mgmt="NONE", scan_freq="2412")
+    time.sleep(0.1)
+
+    brcmd = subprocess.Popen(['brctl', 'show'], stdout=subprocess.PIPE)
+    res = brcmd.stdout.read()
+    brcmd.stdout.close()
+    logger.info("Bridge setup: " + res)
+
+    brcmd = subprocess.Popen(['brctl', 'showstp', 'ap-br0'],
+                             stdout=subprocess.PIPE)
+    res = brcmd.stdout.read()
+    brcmd.stdout.close()
+    logger.info("Bridge showstp: " + res)
+
+    addr0 = dev[0].p2p_interface_addr()
+    addr1 = dev[1].p2p_interface_addr()
+    addr2 = dev[2].p2p_interface_addr()
+
+    src_ll_opt0 = "\x01\x01" + binascii.unhexlify(addr0.replace(':',''))
+    src_ll_opt1 = "\x01\x01" + binascii.unhexlify(addr1.replace(':',''))
+
+    # DAD NS
+    send_ns(dev[0], ip_src="::", target="aaaa:bbbb:cccc::2")
+
+    send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2")
+    # test frame without source link-layer address option
+    send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2",
+            opt='')
+    # test frame with bogus option
+    send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2",
+            opt="\x70\x01\x01\x02\x03\x04\x05\x05")
+    # test frame with truncated source link-layer address option
+    send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2",
+            opt="\x01\x01\x01\x02\x03\x04")
+    # test frame with foreign source link-layer address option
+    send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2",
+            opt="\x01\x01\x01\x02\x03\x04\x05\x06")
+
+    send_ns(dev[1], ip_src="aaaa:bbbb:dddd::2", target="aaaa:bbbb:dddd::2")
+
+    send_ns(dev[1], ip_src="aaaa:bbbb:eeee::2", target="aaaa:bbbb:eeee::2")
+    # another copy for additional code coverage
+    send_ns(dev[1], ip_src="aaaa:bbbb:eeee::2", target="aaaa:bbbb:eeee::2")
+
+    pkt = build_dhcp_ack(dst_ll="ff:ff:ff:ff:ff:ff", src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.124", chaddr=addr0)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+    # Change address and verify unicast
+    pkt = build_dhcp_ack(dst_ll=addr0, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.123", chaddr=addr0)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Not-associated client MAC address
+    pkt = build_dhcp_ack(dst_ll="ff:ff:ff:ff:ff:ff", src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.125", chaddr="22:33:44:55:66:77")
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # No IP address
+    pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="0.0.0.0", chaddr=addr1)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Zero subnet mask
+    pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.126", chaddr=addr1,
+                         subnet_mask="0.0.0.0")
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Truncated option
+    pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.127", chaddr=addr1,
+                         truncated_opt=True)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Wrong magic
+    pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.128", chaddr=addr1,
+                         wrong_magic=True)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Wrong IPv4 total length
+    pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.129", chaddr=addr1,
+                         force_tot_len=1000)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # BOOTP
+    pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.129", chaddr=addr1,
+                         no_dhcp=True)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    macs = get_bridge_macs("ap-br0")
+    logger.info("After connect (showmacs): " + str(macs))
+
+    matches = get_permanent_neighbors("ap-br0")
+    logger.info("After connect: " + str(matches))
+    if len(matches) != 4:
+        raise Exception("Unexpected number of neighbor entries after connect")
+    if 'aaaa:bbbb:cccc::2 dev ap-br0 lladdr 02:00:00:00:00:00 PERMANENT' not in matches:
+        raise Exception("dev0 addr missing")
+    if 'aaaa:bbbb:dddd::2 dev ap-br0 lladdr 02:00:00:00:01:00 PERMANENT' not in matches:
+        raise Exception("dev1 addr(1) missing")
+    if 'aaaa:bbbb:eeee::2 dev ap-br0 lladdr 02:00:00:00:01:00 PERMANENT' not in matches:
+        raise Exception("dev1 addr(2) missing")
+    if '192.168.1.123 dev ap-br0 lladdr 02:00:00:00:00:00 PERMANENT' not in matches:
+        raise Exception("dev0 IPv4 addr missing")
+
+    targets = [ "192.168.1.123", "192.168.1.124", "192.168.1.125",
+                "192.168.1.126" ]
+    for target in targets:
+        send_arp(dev[1], sender_ip="192.168.1.100", target_ip=target)
+
+    for target in targets:
+        send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.101",
+                 target_ip=target)
+
+    for target in targets:
+        send_arp(dev[2], sender_ip="192.168.1.103", target_ip=target)
+
+    # ARP Probe from wireless STA
+    send_arp(dev[1], target_ip="192.168.1.127")
+    # ARP Announcement from wireless STA
+    send_arp(dev[1], sender_ip="192.168.1.127", target_ip="192.168.1.127")
+    send_arp(dev[1], sender_ip="192.168.1.127", target_ip="192.168.1.127",
+             opcode=2)
+
+    macs = get_bridge_macs("ap-br0")
+    logger.info("After ARP Probe + Announcement (showmacs): " + str(macs))
+
+    matches = get_permanent_neighbors("ap-br0")
+    logger.info("After ARP Probe + Announcement: " + str(matches))
+
+    # ARP Request for the newly introduced IP address from wireless STA
+    send_arp(dev[0], sender_ip="192.168.1.123", target_ip="192.168.1.127")
+
+    # ARP Request for the newly introduced IP address from bridge
+    send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.102",
+             target_ip="192.168.1.127")
+    send_arp(dev[2], sender_ip="192.168.1.103", target_ip="192.168.1.127")
+
+    # ARP Probe from bridge
+    send_arp(hapd, hapd_bssid=bssid, target_ip="192.168.1.130")
+    send_arp(dev[2], target_ip="192.168.1.131")
+    # ARP Announcement from bridge (not to be learned by AP for proxyarp)
+    send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.130",
+             target_ip="192.168.1.130")
+    send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.130",
+             target_ip="192.168.1.130", opcode=2)
+    send_arp(dev[2], sender_ip="192.168.1.131", target_ip="192.168.1.131")
+    send_arp(dev[2], sender_ip="192.168.1.131", target_ip="192.168.1.131",
+             opcode=2)
+
+    macs = get_bridge_macs("ap-br0")
+    logger.info("After ARP Probe + Announcement (showmacs): " + str(macs))
+
+    matches = get_permanent_neighbors("ap-br0")
+    logger.info("After ARP Probe + Announcement: " + str(matches))
+
+    # ARP Request for the newly introduced IP address from wireless STA
+    send_arp(dev[0], sender_ip="192.168.1.123", target_ip="192.168.1.130")
+    # ARP Response from bridge (AP does not proxy for non-wireless devices)
+    send_arp(hapd, hapd_bssid=bssid, dst_ll=addr0, sender_ip="192.168.1.130",
+             target_ip="192.168.1.123", opcode=2)
+
+    # ARP Request for the newly introduced IP address from wireless STA
+    send_arp(dev[0], sender_ip="192.168.1.123", target_ip="192.168.1.131")
+    # ARP Response from bridge (AP does not proxy for non-wireless devices)
+    send_arp(dev[2], dst_ll=addr0, sender_ip="192.168.1.131",
+             target_ip="192.168.1.123", opcode=2)
+
+    # ARP Request for the newly introduced IP address from bridge
+    send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.102",
+             target_ip="192.168.1.130")
+    send_arp(dev[2], sender_ip="192.168.1.104", target_ip="192.168.1.131")
+
+    # ARP Probe from wireless STA (duplicate address; learned through DHCP)
+    send_arp(dev[1], target_ip="192.168.1.123")
+    # ARP Probe from wireless STA (duplicate address; learned through ARP)
+    send_arp(dev[0], target_ip="192.168.1.127")
+
+    # Gratuitous ARP Reply for another STA's IP address
+    send_arp(dev[0], opcode=2, sender_mac=addr0, sender_ip="192.168.1.127",
+             target_mac=addr1, target_ip="192.168.1.127")
+    send_arp(dev[1], opcode=2, sender_mac=addr1, sender_ip="192.168.1.123",
+             target_mac=addr0, target_ip="192.168.1.123")
+    # ARP Request to verify previous mapping
+    send_arp(dev[1], sender_ip="192.168.1.127", target_ip="192.168.1.123")
+    send_arp(dev[0], sender_ip="192.168.1.123", target_ip="192.168.1.127")
+
+    time.sleep(0.1)
+
+    send_ns(dev[0], target="aaaa:bbbb:dddd::2", ip_src="aaaa:bbbb:cccc::2")
+    time.sleep(0.1)
+    send_ns(dev[1], target="aaaa:bbbb:cccc::2", ip_src="aaaa:bbbb:dddd::2")
+    time.sleep(0.1)
+    send_ns(hapd, hapd_bssid=bssid, target="aaaa:bbbb:dddd::2",
+            ip_src="aaaa:bbbb:ffff::2")
+    time.sleep(0.1)
+    send_ns(dev[2], target="aaaa:bbbb:cccc::2", ip_src="aaaa:bbbb:ff00::2")
+    time.sleep(0.1)
+    send_ns(dev[2], target="aaaa:bbbb:dddd::2", ip_src="aaaa:bbbb:ff00::2")
+    time.sleep(0.1)
+    send_ns(dev[2], target="aaaa:bbbb:eeee::2", ip_src="aaaa:bbbb:ff00::2")
+    time.sleep(0.1)
+
+    # Try to probe for an already assigned address
+    send_ns(dev[1], target="aaaa:bbbb:cccc::2", ip_src="::")
+    time.sleep(0.1)
+    send_ns(hapd, hapd_bssid=bssid, target="aaaa:bbbb:cccc::2", ip_src="::")
+    time.sleep(0.1)
+    send_ns(dev[2], target="aaaa:bbbb:cccc::2", ip_src="::")
+    time.sleep(0.1)
+
+    # Unsolicited NA
+    send_na(dev[1], target="aaaa:bbbb:cccc:aeae::3",
+            ip_src="aaaa:bbbb:cccc:aeae::3", ip_dst="ff02::1")
+    send_na(hapd, hapd_bssid=bssid, target="aaaa:bbbb:cccc:aeae::4",
+            ip_src="aaaa:bbbb:cccc:aeae::4", ip_dst="ff02::1")
+    send_na(dev[2], target="aaaa:bbbb:cccc:aeae::5",
+            ip_src="aaaa:bbbb:cccc:aeae::5", ip_dst="ff02::1")
+
+    try:
+        hwsim_utils.test_connectivity_iface(dev[0], hapd, "ap-br0")
+    except Exception, e:
+        logger.info("test_connectibity_iface failed: " + str(e))
+        raise HwsimSkip("Assume kernel did not have the required patches for proxyarp")
+    hwsim_utils.test_connectivity_iface(dev[1], hapd, "ap-br0")
+    hwsim_utils.test_connectivity(dev[0], dev[1])
+
+    dev[0].request("DISCONNECT")
+    dev[1].request("DISCONNECT")
+    time.sleep(0.5)
+    for i in range(len(cmd)):
+        cmd[i].terminate()
+    macs = get_bridge_macs("ap-br0")
+    logger.info("After disconnect (showmacs): " + str(macs))
+    matches = get_permanent_neighbors("ap-br0")
+    logger.info("After disconnect: " + str(matches))
+    if len(matches) > 0:
+        raise Exception("Unexpected neighbor entries after disconnect")
+    if ebtables:
+        cmd = subprocess.Popen(['ebtables', '-L', '--Lc'],
+                               stdout=subprocess.PIPE)
+        res = cmd.stdout.read()
+        cmd.stdout.close()
+        logger.info("ebtables results:\n" + res)
+
+    # Verify that expected ARP messages were seen and no unexpected
+    # ARP messages were seen.
+
+    arp_req = tshark_get_arp(cap_dev0, "arp.opcode == 1")
+    arp_reply = tshark_get_arp(cap_dev0, "arp.opcode == 2")
+    logger.info("dev0 seen ARP requests:\n" + str(arp_req))
+    logger.info("dev0 seen ARP replies:\n" + str(arp_reply))
+
+    if [ 'ff:ff:ff:ff:ff:ff', addr1,
+         addr1, '192.168.1.100',
+         '00:00:00:00:00:00', '192.168.1.123' ] in arp_req:
+        raise Exception("dev0 saw ARP request from dev1")
+    if [ 'ff:ff:ff:ff:ff:ff', addr2,
+         addr2, '192.168.1.103',
+         '00:00:00:00:00:00', '192.168.1.123' ] in arp_req:
+        raise Exception("dev0 saw ARP request from dev2")
+    # TODO: Uncomment once fixed in kernel
+    #if [ 'ff:ff:ff:ff:ff:ff', bssid,
+    #     bssid, '192.168.1.101',
+    #     '00:00:00:00:00:00', '192.168.1.123' ] in arp_req:
+    #    raise Exception("dev0 saw ARP request from br")
+
+    if ebtables:
+        for req in arp_req:
+            if req[1] != addr0:
+                raise Exception("Unexpected foreign ARP request on dev0")
+
+    arp_req = tshark_get_arp(cap_dev1, "arp.opcode == 1")
+    arp_reply = tshark_get_arp(cap_dev1, "arp.opcode == 2")
+    logger.info("dev1 seen ARP requests:\n" + str(arp_req))
+    logger.info("dev1 seen ARP replies:\n" + str(arp_reply))
+
+    if [ 'ff:ff:ff:ff:ff:ff', addr2,
+         addr2, '192.168.1.103',
+         '00:00:00:00:00:00', '192.168.1.123' ] in arp_req:
+        raise Exception("dev1 saw ARP request from dev2")
+    if [addr1, addr0, addr0, '192.168.1.123', addr1, '192.168.1.100'] not in arp_reply:
+        raise Exception("dev1 did not get ARP response for 192.168.1.123")
+
+    if ebtables:
+        for req in arp_req:
+            if req[1] != addr1:
+                raise Exception("Unexpected foreign ARP request on dev1")
+
+    arp_req = tshark_get_arp(cap_dev2, "arp.opcode == 1")
+    arp_reply = tshark_get_arp(cap_dev2, "arp.opcode == 2")
+    logger.info("dev2 seen ARP requests:\n" + str(arp_req))
+    logger.info("dev2 seen ARP replies:\n" + str(arp_reply))
+
+    if [ addr2, addr0,
+         addr0, '192.168.1.123',
+         addr2, '192.168.1.103' ] not in arp_reply:
+        raise Exception("dev2 did not get ARP response for 192.168.1.123")
+
+    arp_req = tshark_get_arp(cap_br, "arp.opcode == 1")
+    arp_reply = tshark_get_arp(cap_br, "arp.opcode == 2")
+    logger.info("br seen ARP requests:\n" + str(arp_req))
+    logger.info("br seen ARP replies:\n" + str(arp_reply))
+
+    # TODO: Uncomment once fixed in kernel
+    #if [ bssid, addr0,
+    #     addr0, '192.168.1.123',
+    #     bssid, '192.168.1.101' ] not in arp_reply:
+    #    raise Exception("br did not get ARP response for 192.168.1.123")
+
+    ns = tshark_get_ns(cap_dev0)
+    logger.info("dev0 seen NS: " + str(ns))
+    na = tshark_get_na(cap_dev0)
+    logger.info("dev0 seen NA: " + str(na))
+
+    if [ addr0, addr1, 'aaaa:bbbb:dddd::2', 'aaaa:bbbb:cccc::2',
+         'aaaa:bbbb:dddd::2', addr1 ] not in na:
+        raise Exception("dev0 did not get NA for aaaa:bbbb:dddd::2")
+
+    if ebtables:
+        for req in ns:
+            if req[1] != addr0:
+                raise Exception("Unexpected foreign NS on dev0: " + str(req))
+
+    ns = tshark_get_ns(cap_dev1)
+    logger.info("dev1 seen NS: " + str(ns))
+    na = tshark_get_na(cap_dev1)
+    logger.info("dev1 seen NA: " + str(na))
+
+    if [ addr1, addr0, 'aaaa:bbbb:cccc::2', 'aaaa:bbbb:dddd::2',
+         'aaaa:bbbb:cccc::2', addr0 ] not in na:
+        raise Exception("dev1 did not get NA for aaaa:bbbb:cccc::2")
+
+    if ebtables:
+        for req in ns:
+            if req[1] != addr1:
+                raise Exception("Unexpected foreign NS on dev1: " + str(req))
+
+    ns = tshark_get_ns(cap_dev2)
+    logger.info("dev2 seen NS: " + str(ns))
+    na = tshark_get_na(cap_dev2)
+    logger.info("dev2 seen NA: " + str(na))
+
+    # FIX: enable once kernel implementation for proxyarp IPv6 is fixed
+    #if [ addr2, addr0, 'aaaa:bbbb:cccc::2', 'aaaa:bbbb:ff00::2',
+    #     'aaaa:bbbb:cccc::2', addr0 ] not in na:
+    #    raise Exception("dev2 did not get NA for aaaa:bbbb:cccc::2")
+    #if [ addr2, addr1, 'aaaa:bbbb:dddd::2', 'aaaa:bbbb:ff00::2',
+    #     'aaaa:bbbb:dddd::2', addr1 ] not in na:
+    #    raise Exception("dev2 did not get NA for aaaa:bbbb:dddd::2")
+    #if [ addr2, addr1, 'aaaa:bbbb:eeee::2', 'aaaa:bbbb:ff00::2',
+    #     'aaaa:bbbb:eeee::2', addr1 ] not in na:
+    #    raise Exception("dev2 did not get NA for aaaa:bbbb:eeee::2")
+
+def test_proxyarp_open(dev, apdev, params):
+    """ProxyARP with open network"""
+    try:
+        _test_proxyarp_open(dev, apdev, params)
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['brctl', 'delbr', 'ap-br0'],
+                        stderr=open('/dev/null', 'w'))
+
+def test_proxyarp_open_ebtables(dev, apdev, params):
+    """ProxyARP with open network"""
+    try:
+        _test_proxyarp_open(dev, apdev, params, ebtables=True)
+    finally:
+        try:
+            subprocess.call(['ebtables', '-F', 'FORWARD'])
+            subprocess.call(['ebtables', '-F', 'OUTPUT'])
+        except:
+            pass
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['brctl', 'delbr', 'ap-br0'],
+                        stderr=open('/dev/null', 'w'))
+
+def test_ap_hs20_connect_deinit(dev, apdev):
+    """Hotspot 2.0 connection interrupted with deinit"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="")
+    wpas.hs20_enable()
+    wpas.flush_scan_cache()
+    wpas.add_cred_values({ 'realm': "example.com",
+                           'username': "hs20-test",
+                           'password': "password",
+                           'ca_cert': "auth_serv/ca.pem",
+                           'domain': "example.com" })
+
+    wpas.scan_for_bss(bssid, freq=2412)
+    hapd.disable()
+
+    wpas.request("INTERWORKING_SELECT freq=2412")
+
+    id = wpas.request("RADIO_WORK add block-work")
+    ev = wpas.wait_event(["GAS-QUERY-START", "EXT-RADIO-WORK-START"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to start")
+    ev = wpas.wait_event(["GAS-QUERY-START", "EXT-RADIO-WORK-START"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to start (2)")
+
+    # Remove the interface while the gas-query radio work is still pending and
+    # GAS query has not yet been started.
+    wpas.interface_remove("wlan5")
diff --git a/hostap/tests/hwsim/test_ap_ht.py b/hostap/tests/hwsim/test_ap_ht.py
new file mode 100644
index 0000000..8a8aa9f
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_ht.py
@@ -0,0 +1,1128 @@
+# Test cases for HT operations with hostapd
+# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import logging
+logger = logging.getLogger()
+import struct
+import subprocess
+
+import hostapd
+from utils import HwsimSkip, alloc_fail
+import hwsim_utils
+from test_ap_csa import csa_supported
+
+def clear_scan_cache(ifname):
+    subprocess.call(['ifconfig', ifname, 'up'])
+    subprocess.call(['iw', ifname, 'scan', 'trigger', 'freq', '2412', 'flush'])
+    time.sleep(0.1)
+    subprocess.call(['ifconfig', ifname, 'down'])
+
+def test_ap_ht40_scan(dev, apdev):
+    """HT40 co-ex scan"""
+    clear_scan_cache(apdev[0]['ifname'])
+    params = { "ssid": "test-ht40",
+               "channel": "5",
+               "ht_capab": "[HT40-]"}
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+
+    state = hapd.get_status_field("state")
+    if state != "HT_SCAN":
+        time.sleep(0.1)
+        state = hapd.get_status_field("state")
+        if state != "HT_SCAN":
+            raise Exception("Unexpected interface state - expected HT_SCAN")
+
+    ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+    if not ev:
+        raise Exception("AP setup timed out")
+
+    state = hapd.get_status_field("state")
+    if state != "ENABLED":
+        raise Exception("Unexpected interface state - expected ENABLED")
+
+    freq = hapd.get_status_field("freq")
+    if freq != "2432":
+        raise Exception("Unexpected frequency")
+    pri = hapd.get_status_field("channel")
+    if pri != "5":
+        raise Exception("Unexpected primary channel")
+    sec = hapd.get_status_field("secondary_channel")
+    if sec != "-1":
+        raise Exception("Unexpected secondary channel")
+
+    dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+def test_ap_ht40_scan_conflict(dev, apdev):
+    """HT40 co-ex scan conflict"""
+    clear_scan_cache(apdev[0]['ifname'])
+    params = { "ssid": "test-ht40",
+               "channel": "6",
+               "ht_capab": "[HT40+]"}
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    params = { "ssid": "test-ht40",
+               "channel": "5",
+               "ht_capab": "[HT40-]"}
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+
+    state = hapd.get_status_field("state")
+    if state != "HT_SCAN":
+        time.sleep(0.1)
+        state = hapd.get_status_field("state")
+        if state != "HT_SCAN":
+            raise Exception("Unexpected interface state - expected HT_SCAN")
+
+    ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+    if not ev:
+        raise Exception("AP setup timed out")
+
+    state = hapd.get_status_field("state")
+    if state != "ENABLED":
+        raise Exception("Unexpected interface state - expected ENABLED")
+
+    freq = hapd.get_status_field("freq")
+    if freq != "2432":
+        raise Exception("Unexpected frequency")
+    pri = hapd.get_status_field("channel")
+    if pri != "5":
+        raise Exception("Unexpected primary channel")
+    sec = hapd.get_status_field("secondary_channel")
+    if sec != "0":
+        raise Exception("Unexpected secondary channel: " + sec)
+
+    dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+def test_ap_ht40_scan_conflict2(dev, apdev):
+    """HT40 co-ex scan conflict (HT40-)"""
+    clear_scan_cache(apdev[0]['ifname'])
+    params = { "ssid": "test-ht40",
+               "channel": "11",
+               "ht_capab": "[HT40-]"}
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    params = { "ssid": "test-ht40",
+               "channel": "1",
+               "ht_capab": "[HT40+]"}
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+
+    state = hapd.get_status_field("state")
+    if state != "HT_SCAN":
+        time.sleep(0.1)
+        state = hapd.get_status_field("state")
+        if state != "HT_SCAN":
+            raise Exception("Unexpected interface state - expected HT_SCAN")
+
+    ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+    if not ev:
+        raise Exception("AP setup timed out")
+
+    state = hapd.get_status_field("state")
+    if state != "ENABLED":
+        raise Exception("Unexpected interface state - expected ENABLED")
+
+    freq = hapd.get_status_field("freq")
+    if freq != "2412":
+        raise Exception("Unexpected frequency")
+    pri = hapd.get_status_field("channel")
+    if pri != "1":
+        raise Exception("Unexpected primary channel")
+    sec = hapd.get_status_field("secondary_channel")
+    if sec != "0":
+        raise Exception("Unexpected secondary channel: " + sec)
+
+    dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+def test_ap_ht40_scan_not_affected(dev, apdev):
+    """HT40 co-ex scan and other BSS not affected"""
+    clear_scan_cache(apdev[0]['ifname'])
+    params = { "ssid": "test-ht20",
+               "channel": "11" }
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    subprocess.call(['ifconfig', apdev[0]['ifname'], 'up'])
+    subprocess.call(['iw', apdev[0]['ifname'], 'scan', 'trigger', 'freq', '2462'])
+    time.sleep(0.5)
+    subprocess.call(['iw', apdev[0]['ifname'], 'scan', 'dump'],
+                    stdout=open('/dev/null', 'w'))
+    time.sleep(0.1)
+    subprocess.call(['ifconfig', apdev[0]['ifname'], 'down'])
+
+    params = { "ssid": "test-ht40",
+               "channel": "1",
+               "ht_capab": "[HT40+]"}
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+
+    state = hapd.get_status_field("state")
+    if state != "HT_SCAN":
+        time.sleep(0.1)
+        state = hapd.get_status_field("state")
+        if state != "HT_SCAN":
+            raise Exception("Unexpected interface state - expected HT_SCAN")
+
+    ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+    if not ev:
+        raise Exception("AP setup timed out")
+
+    state = hapd.get_status_field("state")
+    if state != "ENABLED":
+        raise Exception("Unexpected interface state - expected ENABLED")
+
+    freq = hapd.get_status_field("freq")
+    if freq != "2412":
+        raise Exception("Unexpected frequency")
+    pri = hapd.get_status_field("channel")
+    if pri != "1":
+        raise Exception("Unexpected primary channel")
+    sec = hapd.get_status_field("secondary_channel")
+    if sec != "1":
+        raise Exception("Unexpected secondary channel: " + sec)
+
+    dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+def test_ap_ht40_scan_legacy_conflict(dev, apdev):
+    """HT40 co-ex scan conflict with legacy 20 MHz AP"""
+    clear_scan_cache(apdev[0]['ifname'])
+    params = { "ssid": "legacy-20",
+               "channel": "7", "ieee80211n": "0" }
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    params = { "ssid": "test-ht40",
+               "channel": "5",
+               "ht_capab": "[HT40-]"}
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+
+    state = hapd.get_status_field("state")
+    if state != "HT_SCAN":
+        time.sleep(0.1)
+        state = hapd.get_status_field("state")
+        if state != "HT_SCAN":
+            raise Exception("Unexpected interface state - expected HT_SCAN")
+
+    ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+    if not ev:
+        raise Exception("AP setup timed out")
+
+    state = hapd.get_status_field("state")
+    if state != "ENABLED":
+        raise Exception("Unexpected interface state - expected ENABLED")
+
+    freq = hapd.get_status_field("freq")
+    if freq != "2432":
+        raise Exception("Unexpected frequency: " + freq)
+    pri = hapd.get_status_field("channel")
+    if pri != "5":
+        raise Exception("Unexpected primary channel: " + pri)
+    sec = hapd.get_status_field("secondary_channel")
+    if sec != "0":
+        raise Exception("Unexpected secondary channel: " + sec)
+
+    dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+def test_ap_ht40_scan_ht20_conflict(dev, apdev):
+    """HT40 co-ex scan conflict with HT 20 MHz AP"""
+    clear_scan_cache(apdev[0]['ifname'])
+    params = { "ssid": "ht-20",
+               "channel": "7", "ieee80211n": "1" }
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    params = { "ssid": "test-ht40",
+               "channel": "5",
+               "ht_capab": "[HT40-]"}
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+
+    state = hapd.get_status_field("state")
+    if state != "HT_SCAN":
+        time.sleep(0.1)
+        state = hapd.get_status_field("state")
+        if state != "HT_SCAN":
+            raise Exception("Unexpected interface state - expected HT_SCAN")
+
+    ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+    if not ev:
+        raise Exception("AP setup timed out")
+
+    state = hapd.get_status_field("state")
+    if state != "ENABLED":
+        raise Exception("Unexpected interface state - expected ENABLED")
+
+    freq = hapd.get_status_field("freq")
+    if freq != "2432":
+        raise Exception("Unexpected frequency: " + freq)
+    pri = hapd.get_status_field("channel")
+    if pri != "5":
+        raise Exception("Unexpected primary channel: " + pri)
+    sec = hapd.get_status_field("secondary_channel")
+    if sec != "0":
+        raise Exception("Unexpected secondary channel: " + sec)
+
+    dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+def test_ap_ht40_scan_intolerant(dev, apdev):
+    """HT40 co-ex scan finding an AP advertising 40 MHz intolerant"""
+    clear_scan_cache(apdev[0]['ifname'])
+    params = { "ssid": "another-bss",
+               "channel": "1",
+               "ht_capab": "[40-INTOLERANT]" }
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    params = { "ssid": "test-ht40",
+               "channel": "1",
+               "ht_capab": "[HT40+]"}
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+
+    state = hapd.get_status_field("state")
+    if state != "HT_SCAN":
+        time.sleep(0.1)
+        state = hapd.get_status_field("state")
+        if state != "HT_SCAN":
+            raise Exception("Unexpected interface state - expected HT_SCAN")
+
+    ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+    if not ev:
+        raise Exception("AP setup timed out")
+
+    state = hapd.get_status_field("state")
+    if state != "ENABLED":
+        raise Exception("Unexpected interface state - expected ENABLED")
+
+    freq = hapd.get_status_field("freq")
+    if freq != "2412":
+        raise Exception("Unexpected frequency: " + freq)
+    pri = hapd.get_status_field("channel")
+    if pri != "1":
+        raise Exception("Unexpected primary channel: " + pri)
+    sec = hapd.get_status_field("secondary_channel")
+    if sec != "0":
+        raise Exception("Unexpected secondary channel: " + sec)
+
+    dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+def test_ap_ht40_scan_match(dev, apdev):
+    """HT40 co-ex scan matching configuration"""
+    clear_scan_cache(apdev[0]['ifname'])
+    params = { "ssid": "test-ht40",
+               "channel": "5",
+               "ht_capab": "[HT40-]"}
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    params = { "ssid": "test-ht40",
+               "channel": "5",
+               "ht_capab": "[HT40-]"}
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+
+    state = hapd.get_status_field("state")
+    if state != "HT_SCAN":
+        time.sleep(0.1)
+        state = hapd.get_status_field("state")
+        if state != "HT_SCAN":
+            raise Exception("Unexpected interface state - expected HT_SCAN")
+
+    ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+    if not ev:
+        raise Exception("AP setup timed out")
+
+    state = hapd.get_status_field("state")
+    if state != "ENABLED":
+        raise Exception("Unexpected interface state - expected ENABLED")
+
+    freq = hapd.get_status_field("freq")
+    if freq != "2432":
+        raise Exception("Unexpected frequency")
+    pri = hapd.get_status_field("channel")
+    if pri != "5":
+        raise Exception("Unexpected primary channel")
+    sec = hapd.get_status_field("secondary_channel")
+    if sec != "-1":
+        raise Exception("Unexpected secondary channel: " + sec)
+
+    dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+def test_ap_ht40_5ghz_match(dev, apdev):
+    """HT40 co-ex scan on 5 GHz with matching pri/sec channel"""
+    clear_scan_cache(apdev[0]['ifname'])
+    try:
+        hapd = None
+        hapd2 = None
+        params = { "ssid": "test-ht40",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "country_code": "US",
+                   "ht_capab": "[HT40+]"}
+        hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+        params = { "ssid": "test-ht40",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ht_capab": "[HT40+]"}
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+
+        state = hapd.get_status_field("state")
+        if state != "HT_SCAN":
+            time.sleep(0.1)
+            state = hapd.get_status_field("state")
+            if state != "HT_SCAN":
+                raise Exception("Unexpected interface state - expected HT_SCAN")
+
+        ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+        if not ev:
+            raise Exception("AP setup timed out")
+
+        state = hapd.get_status_field("state")
+        if state != "ENABLED":
+            raise Exception("Unexpected interface state - expected ENABLED")
+
+        freq = hapd.get_status_field("freq")
+        if freq != "5180":
+            raise Exception("Unexpected frequency")
+        pri = hapd.get_status_field("channel")
+        if pri != "36":
+            raise Exception("Unexpected primary channel")
+        sec = hapd.get_status_field("secondary_channel")
+        if sec != "1":
+            raise Exception("Unexpected secondary channel: " + sec)
+
+        dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        if hapd2:
+            hapd2.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_ap_ht40_5ghz_switch(dev, apdev):
+    """HT40 co-ex scan on 5 GHz switching pri/sec channel"""
+    clear_scan_cache(apdev[0]['ifname'])
+    try:
+        hapd = None
+        hapd2 = None
+        params = { "ssid": "test-ht40",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "country_code": "US",
+                   "ht_capab": "[HT40+]"}
+        hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+        params = { "ssid": "test-ht40",
+                   "hw_mode": "a",
+                   "channel": "40",
+                   "ht_capab": "[HT40-]"}
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+
+        state = hapd.get_status_field("state")
+        if state != "HT_SCAN":
+            time.sleep(0.1)
+            state = hapd.get_status_field("state")
+            if state != "HT_SCAN":
+                raise Exception("Unexpected interface state - expected HT_SCAN")
+
+        ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+        if not ev:
+            raise Exception("AP setup timed out")
+
+        state = hapd.get_status_field("state")
+        if state != "ENABLED":
+            raise Exception("Unexpected interface state - expected ENABLED")
+
+        freq = hapd.get_status_field("freq")
+        if freq != "5180":
+            raise Exception("Unexpected frequency: " + freq)
+        pri = hapd.get_status_field("channel")
+        if pri != "36":
+            raise Exception("Unexpected primary channel: " + pri)
+        sec = hapd.get_status_field("secondary_channel")
+        if sec != "1":
+            raise Exception("Unexpected secondary channel: " + sec)
+
+        dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        if hapd2:
+            hapd2.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+
+def test_ap_ht40_5ghz_switch2(dev, apdev):
+    """HT40 co-ex scan on 5 GHz switching pri/sec channel (2)"""
+    clear_scan_cache(apdev[0]['ifname'])
+    try:
+        hapd = None
+        hapd2 = None
+        params = { "ssid": "test-ht40",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "country_code": "US",
+                   "ht_capab": "[HT40+]"}
+        hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+        id = dev[0].add_network()
+        dev[0].set_network(id, "mode", "2")
+        dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+        dev[0].set_network(id, "key_mgmt", "NONE")
+        dev[0].set_network(id, "frequency", "5200")
+        dev[0].set_network(id, "scan_freq", "5200")
+        dev[0].select_network(id)
+        time.sleep(1)
+
+        params = { "ssid": "test-ht40",
+                   "hw_mode": "a",
+                   "channel": "40",
+                   "ht_capab": "[HT40-]"}
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+
+        state = hapd.get_status_field("state")
+        if state != "HT_SCAN":
+            time.sleep(0.1)
+            state = hapd.get_status_field("state")
+            if state != "HT_SCAN":
+                raise Exception("Unexpected interface state - expected HT_SCAN")
+
+        ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+        if not ev:
+            raise Exception("AP setup timed out")
+
+        state = hapd.get_status_field("state")
+        if state != "ENABLED":
+            raise Exception("Unexpected interface state - expected ENABLED")
+
+        freq = hapd.get_status_field("freq")
+        if freq != "5180":
+            raise Exception("Unexpected frequency: " + freq)
+        pri = hapd.get_status_field("channel")
+        if pri != "36":
+            raise Exception("Unexpected primary channel: " + pri)
+        sec = hapd.get_status_field("secondary_channel")
+        if sec != "1":
+            raise Exception("Unexpected secondary channel: " + sec)
+
+        dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        if hapd2:
+            hapd2.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_obss_scan(dev, apdev):
+    """Overlapping BSS scan request"""
+    params = { "ssid": "obss-scan",
+               "channel": "6",
+               "ht_capab": "[HT40-]",
+               "obss_interval": "10" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    params = { "ssid": "another-bss",
+               "channel": "9",
+               "ieee80211n": "0" }
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].connect("obss-scan", key_mgmt="NONE", scan_freq="2437")
+    hapd.set("ext_mgmt_frame_handling", "1")
+    logger.info("Waiting for OBSS scan to occur")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=15)
+    if ev is None:
+        raise Exception("Timed out while waiting for OBSS scan to start")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+    if ev is None:
+        raise Exception("Timed out while waiting for OBSS scan results")
+    received = False
+    for i in range(0, 4):
+        frame = hapd.mgmt_rx(timeout=5)
+        if frame is None:
+            raise Exception("MGMT RX wait timed out")
+        if frame['subtype'] != 13:
+            continue
+        payload = frame['payload']
+        if len(payload) < 3:
+            continue
+        (category, action, ie) = struct.unpack('BBB', payload[0:3])
+        if category != 4:
+            continue
+        if action != 0:
+            continue
+        if ie == 72:
+            logger.info("20/40 BSS Coexistence report received")
+            received = True
+            break
+    if not received:
+        raise Exception("20/40 BSS Coexistence report not seen")
+
+def test_obss_scan_40_intolerant(dev, apdev):
+    """Overlapping BSS scan request with 40 MHz intolerant AP"""
+    params = { "ssid": "obss-scan",
+               "channel": "6",
+               "ht_capab": "[HT40-]",
+               "obss_interval": "10" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    params = { "ssid": "another-bss",
+               "channel": "7",
+               "ht_capab": "[40-INTOLERANT]" }
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].connect("obss-scan", key_mgmt="NONE", scan_freq="2437")
+    hapd.set("ext_mgmt_frame_handling", "1")
+    logger.info("Waiting for OBSS scan to occur")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=15)
+    if ev is None:
+        raise Exception("Timed out while waiting for OBSS scan to start")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+    if ev is None:
+        raise Exception("Timed out while waiting for OBSS scan results")
+    received = False
+    for i in range(0, 4):
+        frame = hapd.mgmt_rx(timeout=5)
+        if frame is None:
+            raise Exception("MGMT RX wait timed out")
+        if frame['subtype'] != 13:
+            continue
+        payload = frame['payload']
+        if len(payload) < 3:
+            continue
+        (category, action, ie) = struct.unpack('BBB', payload[0:3])
+        if category != 4:
+            continue
+        if action != 0:
+            continue
+        if ie == 72:
+            logger.info("20/40 BSS Coexistence report received")
+            received = True
+            break
+    if not received:
+        raise Exception("20/40 BSS Coexistence report not seen")
+
+def test_obss_coex_report_handling(dev, apdev):
+    """Overlapping BSS scan report handling with obss_interval=0"""
+    clear_scan_cache(apdev[0]['ifname'])
+    params = { "ssid": "obss-scan",
+               "channel": "6",
+               "ht_capab": "[HT40-]" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    dev[0].connect("obss-scan", key_mgmt="NONE", scan_freq="2437")
+
+    sec = hapd.get_status_field("secondary_channel")
+    if sec != "-1":
+        raise Exception("AP is not using 40 MHz channel")
+
+    # 20/40 MHz co-ex report tests: number of invalid reports and a valid report
+    # that forces 20 MHz channel.
+    tests = [ '0400', '040048', '04004801', '0400480000', '0400490100',
+              '040048ff0000', '04004801ff49ff00', '04004801004900',
+              '0400480100490101', '0400480100490201ff',
+              '040048010449020005' ]
+    for msg in tests:
+        req = "MGMT_TX {} {} freq=2437 action={}".format(bssid, bssid, msg)
+        if "OK" not in dev[0].request(req):
+            raise Exception("Could not send management frame")
+    time.sleep(0.5)
+    sec = hapd.get_status_field("secondary_channel")
+    if sec != "0":
+        raise Exception("AP did not move to 20 MHz channel")
+
+def test_obss_coex_report_handling1(dev, apdev):
+    """Overlapping BSS scan report handling with obss_interval=1"""
+    clear_scan_cache(apdev[0]['ifname'])
+    params = { "ssid": "obss-scan",
+               "channel": "6",
+               "ht_capab": "[HT40+]",
+               "obss_interval": "1" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    dev[0].connect("obss-scan", key_mgmt="NONE", scan_freq="2437")
+
+    sec = hapd.get_status_field("secondary_channel")
+    if sec != "1":
+        raise Exception("AP is not using 40 MHz channel")
+
+    # 20/40 MHz co-ex report forcing 20 MHz channel
+    msg = '040048010449020005'
+    req = "MGMT_TX {} {} freq=2437 action={}".format(bssid, bssid, msg)
+    if "OK" not in dev[0].request(req):
+        raise Exception("Could not send management frame")
+    time.sleep(0.5)
+    sec = hapd.get_status_field("secondary_channel")
+    if sec != "0":
+        raise Exception("AP did not move to 20 MHz channel")
+
+    # No 20/40 MHz co-ex reports forcing 20 MHz channel during next interval
+    for i in range(20):
+        sec = hapd.get_status_field("secondary_channel")
+        if sec == "1":
+            break
+        time.sleep(0.5)
+    if sec != "1":
+        raise Exception("AP did not return to 40 MHz channel")
+
+def test_olbc(dev, apdev):
+    """OLBC detection"""
+    params = { "ssid": "test-olbc",
+               "channel": "6",
+               "ht_capab": "[HT40-]",
+               "ap_table_expiration_time": "2" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    status = hapd.get_status()
+    if status['olbc'] != '0' or status['olbc_ht'] != '0':
+        raise Exception("Unexpected OLBC information")
+
+    params = { "ssid": "olbc-ap",
+               "hw_mode": "b",
+               "channel": "6",
+               "wmm_enabled": "0" }
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    time.sleep(0.5)
+    status = hapd.get_status()
+    if status['olbc'] != '1' or status['olbc_ht'] != '1':
+        raise Exception("Missing OLBC information")
+
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.remove(apdev[1]['ifname'])
+
+    logger.info("Waiting for OLBC state to time out")
+    cleared = False
+    for i in range(0, 15):
+        time.sleep(1)
+        status = hapd.get_status()
+        if status['olbc'] == '0' and status['olbc_ht'] == '0':
+            cleared = True
+            break
+    if not cleared:
+        raise Exception("OLBC state did nto time out")
+
+def test_olbc_table_limit(dev, apdev):
+    """OLBC AP table size limit"""
+    ifname1 = apdev[0]['ifname']
+    ifname2 = apdev[0]['ifname'] + '-2'
+    ifname3 = apdev[0]['ifname'] + '-3'
+    hostapd.add_bss('phy3', ifname1, 'bss-1.conf')
+    hostapd.add_bss('phy3', ifname2, 'bss-2.conf')
+    hostapd.add_bss('phy3', ifname3, 'bss-3.conf')
+
+    params = { "ssid": "test-olbc",
+               "channel": "1",
+               "ap_table_max_size": "2" }
+    hapd = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    time.sleep(0.3)
+    with alloc_fail(hapd, 1, "ap_list_process_beacon"):
+        time.sleep(0.3)
+    hapd.set("ap_table_max_size", "1")
+    time.sleep(0.3)
+    hapd.set("ap_table_max_size", "0")
+    time.sleep(0.3)
+
+def test_olbc_5ghz(dev, apdev):
+    """OLBC detection on 5 GHz"""
+    try:
+        hapd = None
+        hapd2 = None
+        params = { "ssid": "test-olbc",
+                   "country_code": "FI",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ht_capab": "[HT40+]" }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        status = hapd.get_status()
+        if status['olbc'] != '0' or status['olbc_ht'] != '0':
+            raise Exception("Unexpected OLBC information")
+
+        params = { "ssid": "olbc-ap",
+                   "country_code": "FI",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ieee80211n": "0",
+                   "wmm_enabled": "0" }
+        hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+        found = False
+        for i in range(20):
+            time.sleep(0.1)
+            status = hapd.get_status()
+            logger.debug('olbc_ht: ' + status['olbc_ht'])
+            if status['olbc_ht'] == '1':
+                found = True
+                break
+        if not found:
+            raise Exception("Missing OLBC information")
+    finally:
+        if hapd:
+            hapd.request("DISABLE")
+        if hapd2:
+            hapd2.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+
+def test_ap_require_ht(dev, apdev):
+    """Require HT"""
+    params = { "ssid": "require-ht",
+               "require_ht": "1" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+
+    dev[1].connect("require-ht", key_mgmt="NONE", scan_freq="2412",
+                   disable_ht="1", wait_connect=False)
+    dev[0].connect("require-ht", key_mgmt="NONE", scan_freq="2412")
+    ev = dev[1].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
+    if ev is None:
+        raise Exception("Association rejection timed out")
+    if "status_code=27" not in ev:
+        raise Exception("Unexpected rejection status code")
+    dev[2].connect("require-ht", key_mgmt="NONE", scan_freq="2412",
+                   ht_mcs="0x01 00 00 00 00 00 00 00 00 00",
+                   disable_max_amsdu="1", ampdu_factor="2",
+                   ampdu_density="1", disable_ht40="1", disable_sgi="1",
+                   disable_ldpc="1")
+
+def test_ap_require_ht_limited_rates(dev, apdev):
+    """Require HT with limited supported rates"""
+    params = { "ssid": "require-ht",
+               "supported_rates": "60 120 240 360 480 540",
+               "require_ht": "1" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+
+    dev[1].connect("require-ht", key_mgmt="NONE", scan_freq="2412",
+                   disable_ht="1", wait_connect=False)
+    dev[0].connect("require-ht", key_mgmt="NONE", scan_freq="2412")
+    ev = dev[1].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
+    if ev is None:
+        raise Exception("Association rejection timed out")
+    if "status_code=27" not in ev:
+        raise Exception("Unexpected rejection status code")
+
+def test_ap_ht_capab_not_supported(dev, apdev):
+    """HT configuration with driver not supporting all ht_capab entries"""
+    params = { "ssid": "test-ht40",
+               "channel": "5",
+               "ht_capab": "[HT40-][LDPC][SMPS-STATIC][SMPS-DYNAMIC][GF][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1][RX-STBC12][RX-STBC123][DELAYED-BA][MAX-AMSDU-7935][DSSS_CCK-40][LSIG-TXOP-PROT]"}
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, no_enable=True)
+    if "FAIL" not in hapd.request("ENABLE"):
+        raise Exception("Unexpected ENABLE success")
+
+def test_ap_ht_40mhz_intolerant_sta(dev, apdev):
+    """Associated STA indicating 40 MHz intolerant"""
+    clear_scan_cache(apdev[0]['ifname'])
+    params = { "ssid": "intolerant",
+               "channel": "6",
+               "ht_capab": "[HT40-]" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    if hapd.get_status_field("num_sta_ht40_intolerant") != "0":
+        raise Exception("Unexpected num_sta_ht40_intolerant value")
+    if hapd.get_status_field("secondary_channel") != "-1":
+        raise Exception("Unexpected secondary_channel")
+
+    dev[0].connect("intolerant", key_mgmt="NONE", scan_freq="2437")
+    if hapd.get_status_field("num_sta_ht40_intolerant") != "0":
+        raise Exception("Unexpected num_sta_ht40_intolerant value")
+    if hapd.get_status_field("secondary_channel") != "-1":
+        raise Exception("Unexpected secondary_channel")
+
+    dev[2].connect("intolerant", key_mgmt="NONE", scan_freq="2437",
+                   ht40_intolerant="1")
+    time.sleep(1)
+    if hapd.get_status_field("num_sta_ht40_intolerant") != "1":
+        raise Exception("Unexpected num_sta_ht40_intolerant value (expected 1)")
+    if hapd.get_status_field("secondary_channel") != "0":
+        raise Exception("Unexpected secondary_channel (did not disable 40 MHz)")
+
+    dev[2].request("DISCONNECT")
+    time.sleep(1)
+    if hapd.get_status_field("num_sta_ht40_intolerant") != "0":
+        raise Exception("Unexpected num_sta_ht40_intolerant value (expected 0)")
+    if hapd.get_status_field("secondary_channel") != "-1":
+        raise Exception("Unexpected secondary_channel (did not re-enable 40 MHz)")
+
+def test_ap_ht_40mhz_intolerant_ap(dev, apdev):
+    """Associated STA reports 40 MHz intolerant AP after association"""
+    clear_scan_cache(apdev[0]['ifname'])
+    params = { "ssid": "ht",
+               "channel": "6",
+               "ht_capab": "[HT40-]",
+               "obss_interval": "3" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("ht", key_mgmt="NONE", scan_freq="2437")
+
+    if hapd.get_status_field("secondary_channel") != "-1":
+        raise Exception("Unexpected secondary channel information")
+
+    logger.info("Start 40 MHz intolerant AP")
+    params = { "ssid": "intolerant",
+               "channel": "5",
+               "ht_capab": "[40-INTOLERANT]" }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    logger.info("Waiting for co-ex report from STA")
+    ok = False
+    for i in range(0, 20):
+        time.sleep(1)
+        if hapd.get_status_field("secondary_channel") == "0":
+            logger.info("AP moved to 20 MHz channel")
+            ok = True
+            break
+    if not ok:
+        raise Exception("AP did not move to 20 MHz channel")
+
+    if "OK" not in hapd2.request("DISABLE"):
+        raise Exception("Failed to disable 40 MHz intolerant AP")
+
+    # make sure the intolerant AP disappears from scan results more quickly
+    dev[0].scan(type="ONLY", freq="2432", only_new=True)
+    dev[0].scan(type="ONLY", freq="2432", only_new=True)
+    dev[0].dump_monitor()
+
+    logger.info("Waiting for AP to move back to 40 MHz channel")
+    ok = False
+    for i in range(0, 30):
+        time.sleep(1)
+        if hapd.get_status_field("secondary_channel") == "-1":
+            logger.info("AP moved to 40 MHz channel")
+            ok = True
+            break
+    if not ok:
+        raise Exception("AP did not move to 40 MHz channel")
+
+def test_ap_ht40_csa(dev, apdev):
+    """HT with 40 MHz channel width and CSA"""
+    csa_supported(dev[0])
+    try:
+        hapd = None
+        params = { "ssid": "ht",
+                   "country_code": "US",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ht_capab": "[HT40+]",
+                   "ieee80211n": "1" }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+        dev[0].connect("ht", key_mgmt="NONE", scan_freq="5180")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+
+        hapd.request("CHAN_SWITCH 5 5200 ht sec_channel_offset=-1 bandwidth=40")
+        ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+        if ev is None:
+            raise Exception("CSA finished event timed out")
+        if "freq=5200" not in ev:
+            raise Exception("Unexpected channel in CSA finished event")
+        ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+        if ev is not None:
+            raise Exception("Unexpected STA disconnection during CSA")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+
+        hapd.request("CHAN_SWITCH 5 5180 ht sec_channel_offset=1 bandwidth=40")
+        ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+        if ev is None:
+            raise Exception("CSA finished event timed out")
+        if "freq=5180" not in ev:
+            raise Exception("Unexpected channel in CSA finished event")
+        ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+        if ev is not None:
+            raise Exception("Unexpected STA disconnection during CSA")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_ap_ht40_csa2(dev, apdev):
+    """HT with 40 MHz channel width and CSA"""
+    csa_supported(dev[0])
+    try:
+        hapd = None
+        params = { "ssid": "ht",
+                   "country_code": "US",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ht_capab": "[HT40+]",
+                   "ieee80211n": "1" }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+        dev[0].connect("ht", key_mgmt="NONE", scan_freq="5180")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+
+        hapd.request("CHAN_SWITCH 5 5220 ht sec_channel_offset=1 bandwidth=40")
+        ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+        if ev is None:
+            raise Exception("CSA finished event timed out")
+        if "freq=5220" not in ev:
+            raise Exception("Unexpected channel in CSA finished event")
+        ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+        if ev is not None:
+            raise Exception("Unexpected STA disconnection during CSA")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+
+        hapd.request("CHAN_SWITCH 5 5180 ht sec_channel_offset=1 bandwidth=40")
+        ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+        if ev is None:
+            raise Exception("CSA finished event timed out")
+        if "freq=5180" not in ev:
+            raise Exception("Unexpected channel in CSA finished event")
+        ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+        if ev is not None:
+            raise Exception("Unexpected STA disconnection during CSA")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_ap_ht40_csa3(dev, apdev):
+    """HT with 40 MHz channel width and CSA"""
+    csa_supported(dev[0])
+    try:
+        hapd = None
+        params = { "ssid": "ht",
+                   "country_code": "US",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ht_capab": "[HT40+]",
+                   "ieee80211n": "1" }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+        dev[0].connect("ht", key_mgmt="NONE", scan_freq="5180")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+
+        hapd.request("CHAN_SWITCH 5 5240 ht sec_channel_offset=-1 bandwidth=40")
+        ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+        if ev is None:
+            raise Exception("CSA finished event timed out")
+        if "freq=5240" not in ev:
+            raise Exception("Unexpected channel in CSA finished event")
+        ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+        if ev is not None:
+            raise Exception("Unexpected STA disconnection during CSA")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+
+        hapd.request("CHAN_SWITCH 5 5180 ht sec_channel_offset=1 bandwidth=40")
+        ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+        if ev is None:
+            raise Exception("CSA finished event timed out")
+        if "freq=5180" not in ev:
+            raise Exception("Unexpected channel in CSA finished event")
+        ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+        if ev is not None:
+            raise Exception("Unexpected STA disconnection during CSA")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_ap_ht_smps(dev, apdev):
+    """SMPS AP configuration options"""
+    params = { "ssid": "ht1", "ht_capab": "[SMPS-STATIC]" }
+    try:
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    except:
+        raise HwsimSkip("Assume mac80211_hwsim was not recent enough to support SMPS")
+    params = { "ssid": "ht2", "ht_capab": "[SMPS-DYNAMIC]" }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].connect("ht1", key_mgmt="NONE", scan_freq="2412")
+    dev[1].connect("ht2", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    hwsim_utils.test_connectivity(dev[1], hapd2)
+
+def test_prefer_ht20(dev, apdev):
+    """Preference on HT20 over no-HT"""
+    params = { "ssid": "test",
+               "channel": "1",
+               "ieee80211n": "0" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    params = { "ssid": "test",
+               "channel": "1",
+               "ieee80211n": "1" }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq=2412)
+    dev[0].scan_for_bss(bssid2, freq=2412)
+    dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+    if dev[0].get_status_field('bssid') != bssid2:
+        raise Exception("Unexpected BSS selected")
+
+    est = dev[0].get_bss(bssid)['est_throughput']
+    if est != "54000":
+        raise Exception("Unexpected BSS0 est_throughput: " + est)
+
+    est = dev[0].get_bss(bssid2)['est_throughput']
+    if est != "65000":
+        raise Exception("Unexpected BSS1 est_throughput: " + est)
+
+def test_prefer_ht40(dev, apdev):
+    """Preference on HT40 over HT20"""
+    params = { "ssid": "test",
+               "channel": "1",
+               "ieee80211n": "1" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    params = { "ssid": "test",
+               "channel": "1",
+               "ieee80211n": "1",
+               "ht_capab": "[HT40+]" }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq=2412)
+    dev[0].scan_for_bss(bssid2, freq=2412)
+    dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+    if dev[0].get_status_field('bssid') != bssid2:
+        raise Exception("Unexpected BSS selected")
+
+    est = dev[0].get_bss(bssid)['est_throughput']
+    if est != "65000":
+        raise Exception("Unexpected BSS0 est_throughput: " + est)
+
+    est = dev[0].get_bss(bssid2)['est_throughput']
+    if est != "135000":
+        raise Exception("Unexpected BSS1 est_throughput: " + est)
+
+def test_prefer_ht20_during_roam(dev, apdev):
+    """Preference on HT20 over no-HT in roaming consideration"""
+    params = { "ssid": "test",
+               "channel": "1",
+               "ieee80211n": "0" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq=2412)
+    dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+
+    params = { "ssid": "test",
+               "channel": "1",
+               "ieee80211n": "1" }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+    dev[0].scan_for_bss(bssid2, freq=2412)
+    dev[0].scan(freq=2412)
+    dev[0].wait_connected()
+    
+    if dev[0].get_status_field('bssid') != bssid2:
+        raise Exception("Unexpected BSS selected")
+
+def test_ap_ht40_5ghz_invalid_pair(dev, apdev):
+    """HT40 on 5 GHz with invalid channel pair"""
+    clear_scan_cache(apdev[0]['ifname'])
+    try:
+        params = { "ssid": "test-ht40",
+                   "hw_mode": "a",
+                   "channel": "40",
+                   "country_code": "US",
+                   "ht_capab": "[HT40+]"}
+        hapd = hostapd.add_ap(apdev[1]['ifname'], params, wait_enabled=False)
+        ev = hapd.wait_event(["AP-DISABLED"], timeout=10)
+        if not ev:
+            raise Exception("AP setup failure timed out")
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
diff --git a/hostap/tests/hwsim/test_ap_mixed.py b/hostap/tests/hwsim/test_ap_mixed.py
new file mode 100644
index 0000000..c342d39
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_mixed.py
@@ -0,0 +1,99 @@
+# Mixed AP module parameters enabled
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+
+import hostapd
+import hwsim_utils
+from utils import skip_with_fips
+
+def test_ap_mixed_security(dev, apdev):
+    """WPA/WPA2 with PSK, EAP, SAE, FT in a single BSS"""
+    skip_with_fips(dev[0])
+    dev[0].flush_scan_cache()
+    sae = "SAE" in dev[0].get_capability("auth_alg")
+    ssid = "test-mixed"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa_mixed_params(ssid=ssid, passphrase=passphrase)
+    params['wpa_key_mgmt'] = "WPA-PSK WPA-PSK-SHA256 WPA-EAP WPA-EAP-SHA256 SAE FT-PSK FT-EAP FT-SAE"
+    params["ieee8021x"] = "1"
+    params["eap_server"] = "1"
+    params["eap_user_file"] = "auth_serv/eap_user.conf"
+    params['nas_identifier'] = "nas1.w1.fi"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect(ssid, key_mgmt="WPA-PSK", proto="WPA", pairwise="TKIP",
+                   psk=passphrase, scan_freq="2412")
+    dev[1].connect(ssid, key_mgmt="WPA-EAP-SHA256", proto="WPA2", eap="GPSK",
+                   identity="gpsk user",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+    if sae:
+        dev[2].connect(ssid, psk=passphrase, key_mgmt="SAE", scan_freq="2412")
+
+    logger.debug(dev[0].request("SCAN_RESULTS"))
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    logger.debug(bss)
+    if "[WPA-EAP+PSK-TKIP]" not in bss['flags']:
+        raise Exception("Unexpected flags (WPA): " + bss['flags'])
+    if sae and "[WPA2-EAP+PSK+SAE+FT/EAP+FT/PSK+FT/SAE+EAP-SHA256+PSK-SHA256-CCMP]" not in bss['flags']:
+        raise Exception("Unexpected flags (WPA2): " + bss['flags'])
+
+    if dev[0].get_status_field("key_mgmt") != "WPA-PSK":
+        raise Exception("Unexpected key_mgmt(1)")
+    if dev[0].get_status_field("pairwise_cipher") != "TKIP":
+        raise Exception("Unexpected pairwise(1)")
+    if dev[1].get_status_field("key_mgmt") != "WPA2-EAP-SHA256":
+        raise Exception("Unexpected key_mgmt(2)")
+    if sae and dev[2].get_status_field("key_mgmt") != "SAE":
+        raise Exception("Unexpected key_mgmt(3)")
+
+    hwsim_utils.test_connectivity(dev[0], dev[1])
+    if sae:
+        hwsim_utils.test_connectivity(dev[1], dev[2])
+        hwsim_utils.test_connectivity(dev[0], dev[2])
+    for i in range(3):
+        if i < 2 or sae:
+            hwsim_utils.test_connectivity(dev[i], hapd)
+        dev[i].request("DISCONNECT")
+
+    dev[0].connect(ssid, key_mgmt="WPA-PSK WPA-PSK-SHA256", psk=passphrase,
+                   scan_freq="2412")
+    dev[1].connect(ssid, key_mgmt="WPA-EAP", proto="WPA", eap="GPSK",
+                   identity="gpsk user",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+    if sae:
+        dev[2].connect(ssid, key_mgmt="WPA-PSK WPA-PSK-SHA256 SAE",
+                       psk=passphrase, scan_freq="2412")
+
+    if dev[0].get_status_field("key_mgmt") != "WPA2-PSK-SHA256":
+        raise Exception("Unexpected key_mgmt(1b)")
+    if dev[0].get_status_field("pairwise_cipher") != "CCMP":
+        raise Exception("Unexpected pairwise(1b)")
+    if dev[1].get_status_field("key_mgmt") != "WPA/IEEE 802.1X/EAP":
+        raise Exception("Unexpected key_mgmt(2b)")
+    if sae and dev[2].get_status_field("key_mgmt") != "SAE":
+        raise Exception("Unexpected key_mgmt(3b)")
+
+    for i in range(3):
+        dev[i].request("DISCONNECT")
+
+    dev[0].connect(ssid, key_mgmt="FT-PSK", psk=passphrase, scan_freq="2412")
+    dev[1].connect(ssid, key_mgmt="FT-EAP", eap="GPSK", identity="gpsk user",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+    if sae:
+        dev[2].connect(ssid, psk=passphrase, key_mgmt="FT-SAE",
+                       scan_freq="2412")
+
+    if dev[0].get_status_field("key_mgmt") != "FT-PSK":
+        raise Exception("Unexpected key_mgmt(1c)")
+    if dev[1].get_status_field("key_mgmt") != "FT-EAP":
+        raise Exception("Unexpected key_mgmt(2c)")
+    if sae and dev[2].get_status_field("key_mgmt") != "FT-SAE":
+        raise Exception("Unexpected key_mgmt(3c)")
diff --git a/hostap/tests/hwsim/test_ap_open.py b/hostap/tests/hwsim/test_ap_open.py
new file mode 100644
index 0000000..139756e
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_open.py
@@ -0,0 +1,484 @@
+# Open mode AP tests
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import struct
+import subprocess
+import time
+import os
+
+import hostapd
+import hwsim_utils
+from tshark import run_tshark
+from utils import alloc_fail
+from wpasupplicant import WpaSupplicant
+
+def test_ap_open(dev, apdev):
+    """AP with open mode (no security) configuration"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+                   bg_scan_period="0")
+    ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
+    if ev is None:
+        raise Exception("No connection event received from hostapd")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+    dev[0].request("DISCONNECT")
+    ev = hapd.wait_event([ "AP-STA-DISCONNECTED" ], timeout=5)
+    if ev is None:
+        raise Exception("No disconnection event received from hostapd")
+
+def test_ap_open_packet_loss(dev, apdev):
+    """AP with open mode configuration and large packet loss"""
+    params = { "ssid": "open",
+               "ignore_probe_probability": "0.5",
+               "ignore_auth_probability": "0.5",
+               "ignore_assoc_probability": "0.5",
+               "ignore_reassoc_probability": "0.5" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    for i in range(0, 3):
+        dev[i].connect("open", key_mgmt="NONE", scan_freq="2412",
+                       wait_connect=False)
+    for i in range(0, 3):
+        dev[i].wait_connected(timeout=20)
+
+def test_ap_open_unknown_action(dev, apdev):
+    """AP with open mode configuration and unknown Action frame"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+    bssid = apdev[0]['bssid']
+    cmd = "MGMT_TX {} {} freq=2412 action=765432".format(bssid, bssid)
+    if "FAIL" in dev[0].request(cmd):
+        raise Exception("Could not send test Action frame")
+    ev = dev[0].wait_event(["MGMT-TX-STATUS"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on MGMT-TX-STATUS")
+    if "result=SUCCESS" not in ev:
+        raise Exception("AP did not ack Action frame")
+
+def test_ap_open_invalid_wmm_action(dev, apdev):
+    """AP with open mode configuration and invalid WMM Action frame"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+    bssid = apdev[0]['bssid']
+    cmd = "MGMT_TX {} {} freq=2412 action=1100".format(bssid, bssid)
+    if "FAIL" in dev[0].request(cmd):
+        raise Exception("Could not send test Action frame")
+    ev = dev[0].wait_event(["MGMT-TX-STATUS"], timeout=10)
+    if ev is None or "result=SUCCESS" not in ev:
+        raise Exception("AP did not ack Action frame")
+
+def test_ap_open_reconnect_on_inactivity_disconnect(dev, apdev):
+    """Reconnect to open mode AP after inactivity related disconnection"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+    hapd.request("DEAUTHENTICATE " + dev[0].p2p_interface_addr() + " reason=4")
+    dev[0].wait_disconnected(timeout=5)
+    dev[0].wait_connected(timeout=2, error="Timeout on reconnection")
+
+def test_ap_open_assoc_timeout(dev, apdev):
+    """AP timing out association"""
+    ssid = "test"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].scan(freq="2412")
+    hapd.set("ext_mgmt_frame_handling", "1")
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+                   wait_connect=False)
+    for i in range(0, 10):
+        req = hapd.mgmt_rx()
+        if req is None:
+            raise Exception("MGMT RX wait timed out")
+        if req['subtype'] == 11:
+            break
+        req = None
+    if not req:
+        raise Exception("Authentication frame not received")
+
+    resp = {}
+    resp['fc'] = req['fc']
+    resp['da'] = req['sa']
+    resp['sa'] = req['da']
+    resp['bssid'] = req['bssid']
+    resp['payload'] = struct.pack('<HHH', 0, 2, 0)
+    hapd.mgmt_tx(resp)
+
+    assoc = 0
+    for i in range(0, 10):
+        req = hapd.mgmt_rx()
+        if req is None:
+            raise Exception("MGMT RX wait timed out")
+        if req['subtype'] == 0:
+            assoc += 1
+            if assoc == 3:
+                break
+    if assoc != 3:
+        raise Exception("Association Request frames not received: assoc=%d" % assoc)
+    hapd.set("ext_mgmt_frame_handling", "0")
+    dev[0].wait_connected(timeout=15)
+
+def test_ap_open_id_str(dev, apdev):
+    """AP with open mode and id_str"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412", id_str="foo",
+                   wait_connect=False)
+    ev = dev[0].wait_connected(timeout=10)
+    if "id_str=foo" not in ev:
+        raise Exception("CTRL-EVENT-CONNECT did not have matching id_str: " + ev)
+    if dev[0].get_status_field("id_str") != "foo":
+        raise Exception("id_str mismatch")
+
+def test_ap_open_select_any(dev, apdev):
+    """AP with open mode and select any network"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    id = dev[0].connect("unknown", key_mgmt="NONE", scan_freq="2412",
+                        only_add_network=True)
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+                   only_add_network=True)
+    dev[0].select_network(id)
+    ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND",
+                            "CTRL-EVENT-CONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("No result reported")
+    if "CTRL-EVENT-CONNECTED" in ev:
+        raise Exception("Unexpected connection")
+
+    dev[0].select_network("any")
+    dev[0].wait_connected(timeout=10)
+
+def test_ap_open_unexpected_assoc_event(dev, apdev):
+    """AP with open mode and unexpected association event"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=15)
+    dev[0].dump_monitor()
+    # This will be accepted due to matching network
+    subprocess.call(['iw', 'dev', dev[0].ifname, 'connect', 'open', "2412",
+                     apdev[0]['bssid']])
+    dev[0].wait_connected(timeout=15)
+    dev[0].dump_monitor()
+
+    dev[0].request("REMOVE_NETWORK all")
+    dev[0].wait_disconnected(timeout=5)
+    dev[0].dump_monitor()
+    # This will result in disconnection due to no matching network
+    subprocess.call(['iw', 'dev', dev[0].ifname, 'connect', 'open', "2412",
+                     apdev[0]['bssid']])
+    dev[0].wait_disconnected(timeout=15)
+
+def test_ap_bss_load(dev, apdev):
+    """AP with open mode (no security) configuration"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": "open",
+                            "bss_load_update_period": "10" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+    # this does not really get much useful output with mac80211_hwsim currently,
+    # but run through the channel survey update couple of times
+    for i in range(0, 10):
+        hwsim_utils.test_connectivity(dev[0], hapd)
+        hwsim_utils.test_connectivity(dev[0], hapd)
+        hwsim_utils.test_connectivity(dev[0], hapd)
+        time.sleep(0.15)
+
+def hapd_out_of_mem(hapd, apdev, count, func):
+    with alloc_fail(hapd, count, func):
+        started = False
+        try:
+            hostapd.add_ap(apdev['ifname'], { "ssid": "open" })
+            started = True
+        except:
+            pass
+        if started:
+            raise Exception("hostapd interface started even with memory allocation failure: " + arg)
+
+def test_ap_open_out_of_memory(dev, apdev):
+    """hostapd failing to setup interface due to allocation failure"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    hapd_out_of_mem(hapd, apdev[1], 1, "hostapd_alloc_bss_data")
+
+    for i in range(1, 3):
+        hapd_out_of_mem(hapd, apdev[1], i, "hostapd_iface_alloc")
+
+    for i in range(1, 5):
+        hapd_out_of_mem(hapd, apdev[1], i, "hostapd_config_defaults;hostapd_config_alloc")
+
+    hapd_out_of_mem(hapd, apdev[1], 1, "hostapd_config_alloc")
+
+    hapd_out_of_mem(hapd, apdev[1], 1, "hostapd_driver_init")
+
+    for i in range(1, 4):
+        hapd_out_of_mem(hapd, apdev[1], i, "=wpa_driver_nl80211_drv_init")
+
+    # eloop_register_read_sock() call from i802_init()
+    hapd_out_of_mem(hapd, apdev[1], 1, "eloop_sock_table_add_sock;eloop_register_sock;?eloop_register_read_sock;=i802_init")
+
+    # verify that a new interface can still be added when memory allocation does
+    # not fail
+    hostapd.add_ap(apdev[1]['ifname'], { "ssid": "open" })
+
+def test_bssid_black_white_list(dev, apdev):
+    """BSSID black/white list"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], { "ssid": "open" })
+
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+                   bssid_whitelist=apdev[1]['bssid'])
+    dev[1].connect("open", key_mgmt="NONE", scan_freq="2412",
+                   bssid_blacklist=apdev[1]['bssid'])
+    dev[2].connect("open", key_mgmt="NONE", scan_freq="2412",
+                   bssid_whitelist="00:00:00:00:00:00/00:00:00:00:00:00",
+                   bssid_blacklist=apdev[1]['bssid'])
+    if dev[0].get_status_field('bssid') != apdev[1]['bssid']:
+        raise Exception("dev[0] connected to unexpected AP")
+    if dev[1].get_status_field('bssid') != apdev[0]['bssid']:
+        raise Exception("dev[1] connected to unexpected AP")
+    if dev[2].get_status_field('bssid') != apdev[0]['bssid']:
+        raise Exception("dev[2] connected to unexpected AP")
+    dev[0].request("REMOVE_NETWORK all")
+    dev[1].request("REMOVE_NETWORK all")
+    dev[2].request("REMOVE_NETWORK all")
+
+    dev[2].connect("open", key_mgmt="NONE", scan_freq="2412",
+                   bssid_whitelist="00:00:00:00:00:00", wait_connect=False)
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+                   bssid_whitelist="11:22:33:44:55:66/ff:00:00:00:00:00 " + apdev[1]['bssid'] + " aa:bb:cc:dd:ee:ff")
+    dev[1].connect("open", key_mgmt="NONE", scan_freq="2412",
+                   bssid_blacklist="11:22:33:44:55:66/ff:00:00:00:00:00 " + apdev[1]['bssid'] + " aa:bb:cc:dd:ee:ff")
+    if dev[0].get_status_field('bssid') != apdev[1]['bssid']:
+        raise Exception("dev[0] connected to unexpected AP")
+    if dev[1].get_status_field('bssid') != apdev[0]['bssid']:
+        raise Exception("dev[1] connected to unexpected AP")
+    dev[0].request("REMOVE_NETWORK all")
+    dev[1].request("REMOVE_NETWORK all")
+    ev = dev[2].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected dev[2] connectin")
+    dev[2].request("REMOVE_NETWORK all")
+
+def test_ap_open_wpas_in_bridge(dev, apdev):
+    """Open mode AP and wpas interface in a bridge"""
+    br_ifname='sta-br0'
+    ifname='wlan5'
+    try:
+        _test_ap_open_wpas_in_bridge(dev, apdev)
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down'])
+        subprocess.call(['brctl', 'delif', br_ifname, ifname])
+        subprocess.call(['brctl', 'delbr', br_ifname])
+        subprocess.call(['iw', ifname, 'set', '4addr', 'off'])
+
+def _test_ap_open_wpas_in_bridge(dev, apdev):
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+
+    br_ifname='sta-br0'
+    ifname='wlan5'
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    # First, try a failure case of adding an interface
+    try:
+        wpas.interface_add(ifname, br_ifname=br_ifname)
+        raise Exception("Interface addition succeeded unexpectedly")
+    except Exception, e:
+        if "Failed to add" in str(e):
+            logger.info("Ignore expected interface_add failure due to missing bridge interface: " + str(e))
+        else:
+            raise
+
+    # Next, add the bridge interface and add the interface again
+    subprocess.call(['brctl', 'addbr', br_ifname])
+    subprocess.call(['brctl', 'setfd', br_ifname, '0'])
+    subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
+    subprocess.call(['iw', ifname, 'set', '4addr', 'on'])
+    subprocess.check_call(['brctl', 'addif', br_ifname, ifname])
+    wpas.interface_add(ifname, br_ifname=br_ifname)
+
+    wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
+
+def test_ap_open_start_disabled(dev, apdev):
+    """AP with open mode and beaconing disabled"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open",
+                                                "start_disabled": "1" })
+    bssid = apdev[0]['bssid']
+
+    dev[0].flush_scan_cache()
+    dev[0].scan(freq=2412, only_new=True)
+    if dev[0].get_bss(bssid) is not None:
+        raise Exception("AP was seen beaconing")
+    if "OK" not in hapd.request("RELOAD"):
+        raise Exception("RELOAD failed")
+    dev[0].scan_for_bss(bssid, freq=2412)
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+def test_ap_open_start_disabled2(dev, apdev):
+    """AP with open mode and beaconing disabled (2)"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open",
+                                                "start_disabled": "1" })
+    bssid = apdev[0]['bssid']
+
+    dev[0].flush_scan_cache()
+    dev[0].scan(freq=2412, only_new=True)
+    if dev[0].get_bss(bssid) is not None:
+        raise Exception("AP was seen beaconing")
+    if "OK" not in hapd.request("UPDATE_BEACON"):
+        raise Exception("UPDATE_BEACON failed")
+    dev[0].scan_for_bss(bssid, freq=2412)
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+    if "OK" not in hapd.request("UPDATE_BEACON"):
+        raise Exception("UPDATE_BEACON failed")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    dev[0].request("RECONNECT")
+    dev[0].wait_connected()
+
+def test_ap_open_ifdown(dev, apdev):
+    """AP with open mode and external ifconfig down"""
+    params = { "ssid": "open",
+               "ap_max_inactivity": "1" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+    dev[1].connect("open", key_mgmt="NONE", scan_freq="2412")
+    subprocess.call(['ip', 'link', 'set', 'dev', apdev[0]['ifname'], 'down'])
+    ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on AP-STA-DISCONNECTED (1)")
+    ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on AP-STA-DISCONNECTED (2)")
+    ev = hapd.wait_event(["INTERFACE-DISABLED"], timeout=5)
+    if ev is None:
+        raise Exception("No INTERFACE-DISABLED event")
+    # The following wait tests beacon loss detection in mac80211 on dev0.
+    # dev1 is used to test stopping of AP side functionality on client polling.
+    dev[1].request("REMOVE_NETWORK all")
+    subprocess.call(['ip', 'link', 'set', 'dev', apdev[0]['ifname'], 'up'])
+    dev[0].wait_disconnected()
+    dev[1].wait_disconnected()
+    ev = hapd.wait_event(["INTERFACE-ENABLED"], timeout=10)
+    if ev is None:
+        raise Exception("No INTERFACE-ENABLED event")
+    dev[0].wait_connected()
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_open_disconnect_in_ps(dev, apdev, params):
+    """Disconnect with the client in PS to regression-test a kernel bug"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+                   bg_scan_period="0")
+    ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
+    if ev is None:
+        raise Exception("No connection event received from hostapd")
+
+    time.sleep(0.2)
+    hwsim_utils.set_powersave(dev[0], hwsim_utils.PS_MANUAL_POLL)
+    try:
+        # inject some traffic
+        sa = hapd.own_addr()
+        da = dev[0].own_addr()
+        hapd.request('DATA_TEST_CONFIG 1')
+        hapd.request('DATA_TEST_TX {} {} 0'.format(da, sa))
+        hapd.request('DATA_TEST_CONFIG 0')
+
+        # let the AP send couple of Beacon frames
+        time.sleep(0.3)
+
+        # disconnect - with traffic pending - shouldn't cause kernel warnings
+        dev[0].request("DISCONNECT")
+    finally:
+        hwsim_utils.set_powersave(dev[0], hwsim_utils.PS_DISABLED)
+
+    time.sleep(0.2)
+    out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+                     "wlan_mgt.tim.partial_virtual_bitmap",
+                     ["wlan_mgt.tim.partial_virtual_bitmap"])
+    if out is not None:
+        state = 0
+        for l in out.splitlines():
+            pvb = int(l, 16)
+            if pvb > 0 and state == 0:
+                state = 1
+            elif pvb == 0 and state == 1:
+                state = 2
+        if state != 2:
+            raise Exception("Didn't observe TIM bit getting set and unset (state=%d)" % state)
+
+def test_ap_open_select_network(dev, apdev):
+    """Open mode connection and SELECT_NETWORK to change network"""
+    hapd1 = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    bssid1 = apdev[0]['bssid']
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], { "ssid": "open2" })
+    bssid2 = apdev[1]['bssid']
+
+    id1 = dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+                         only_add_network=True)
+    id2 = dev[0].connect("open2", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd2)
+
+    dev[0].select_network(id1)
+    dev[0].wait_connected()
+    res = dev[0].request("BLACKLIST")
+    if bssid1 in res or bssid2 in res:
+        raise Exception("Unexpected blacklist entry")
+    hwsim_utils.test_connectivity(dev[0], hapd1)
+
+    dev[0].select_network(id2)
+    dev[0].wait_connected()
+    hwsim_utils.test_connectivity(dev[0], hapd2)
+    res = dev[0].request("BLACKLIST")
+    if bssid1 in res or bssid2 in res:
+        raise Exception("Unexpected blacklist entry(2)")
+
+def test_ap_open_disable_enable(dev, apdev):
+    """AP with open mode getting disabled and re-enabled"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+                   bg_scan_period="0")
+
+    for i in range(2):
+        hapd.request("DISABLE")
+        dev[0].wait_disconnected()
+        hapd.request("ENABLE")
+        dev[0].wait_connected()
+        hwsim_utils.test_connectivity(dev[0], hapd)
+
+def sta_enable_disable(dev, bssid):
+    dev.scan_for_bss(bssid, freq=2412)
+    work_id = dev.request("RADIO_WORK add block-work")
+    ev = dev.wait_event(["EXT-RADIO-WORK-START"])
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to start")
+    id = dev.connect("open", key_mgmt="NONE", scan_freq="2412",
+                     only_add_network=True)
+    dev.request("ENABLE_NETWORK %d" % id)
+    if "connect@" not in dev.request("RADIO_WORK show"):
+        raise Exception("connect radio work missing")
+    dev.request("DISABLE_NETWORK %d" % id)
+    dev.request("RADIO_WORK done " + work_id)
+
+    ok = False
+    for i in range(30):
+        if "connect@" not in dev.request("RADIO_WORK show"):
+            ok = True
+            break
+        time.sleep(0.1)
+    if not ok:
+        raise Exception("connect radio work not completed")
+    ev = dev.wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected connection")
+    dev.request("DISCONNECT")
+
+def test_ap_open_sta_enable_disable(dev, apdev):
+    """AP with open mode and wpa_supplicant ENABLE/DISABLE_NETWORK"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    bssid = apdev[0]['bssid']
+
+    sta_enable_disable(dev[0], bssid)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+    sta_enable_disable(wpas, bssid)
diff --git a/hostap/tests/hwsim/test_ap_params.py b/hostap/tests/hwsim/test_ap_params.py
new file mode 100644
index 0000000..517f2a7
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_params.py
@@ -0,0 +1,267 @@
+# Test various AP mode parameters
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import subprocess
+
+import hwsim_utils
+import hostapd
+
+def test_ap_fragmentation_rts_set_high(dev, apdev):
+    """WPA2-PSK AP with fragmentation and RTS thresholds larger than frame length"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['rts_threshold'] = "1000"
+    params['fragm_threshold'] = "2000"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_fragmentation_open(dev, apdev):
+    """Open AP with fragmentation threshold"""
+    ssid = "fragmentation"
+    params = {}
+    params['ssid'] = ssid
+    params['fragm_threshold'] = "1000"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_fragmentation_wpa2(dev, apdev):
+    """WPA2-PSK AP with fragmentation threshold"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['fragm_threshold'] = "1000"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_vendor_elements(dev, apdev):
+    """WPA2-PSK AP with vendor elements added"""
+    bssid = apdev[0]['bssid']
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['vendor_elements'] = "dd0411223301"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    bss = dev[0].get_bss(bssid)
+    if "dd0411223301" not in bss['ie']:
+        raise Exception("Vendor element not shown in scan results")
+
+    hapd.set('vendor_elements', 'dd051122330203')
+    if "OK" not in hapd.request("UPDATE_BEACON"):
+        raise Exception("UPDATE_BEACON failed")
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    bss = dev[1].get_bss(bssid)
+    if "dd0411223301" in bss['ie']:
+        raise Exception("Old vendor element still in scan results")
+    if "dd051122330203" not in bss['ie']:
+        raise Exception("New vendor element not shown in scan results")
+
+def test_ap_country(dev, apdev):
+    """WPA2-PSK AP setting country code and using 5 GHz band"""
+    try:
+        hapd = None
+        bssid = apdev[0]['bssid']
+        ssid = "test-wpa2-psk"
+        passphrase = 'qwertyuiop'
+        params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+        params['country_code'] = 'FI'
+        params['ieee80211d'] = '1'
+        params['hw_mode'] = 'a'
+        params['channel'] = '36'
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        dev[0].connect(ssid, psk=passphrase, scan_freq="5180")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_ap_acl_accept(dev, apdev):
+    """MAC ACL accept list"""
+    ssid = "acl"
+    params = {}
+    params['ssid'] = ssid
+    params['accept_mac_file'] = "hostapd.macaddr"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    dev[0].request("REMOVE_NETWORK all")
+    dev[1].request("REMOVE_NETWORK all")
+    hapd.request("SET macaddr_acl 1")
+    dev[1].dump_monitor()
+    dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412", wait_connect=False)
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected association")
+
+def test_ap_acl_deny(dev, apdev):
+    """MAC ACL deny list"""
+    ssid = "acl"
+    params = {}
+    params['ssid'] = ssid
+    params['deny_mac_file'] = "hostapd.macaddr"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412", wait_connect=False)
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected association")
+
+def test_ap_wds_sta(dev, apdev):
+    """WPA2-PSK AP with STA using 4addr mode"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['wds_sta'] = "1"
+    params['wds_bridge'] = "wds-br0"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    try:
+        subprocess.call(['brctl', 'addbr', 'wds-br0'])
+        subprocess.call(['brctl', 'setfd', 'wds-br0', '0'])
+        subprocess.call(['ip', 'link', 'set', 'dev', 'wds-br0', 'up'])
+        subprocess.call(['iw', dev[0].ifname, 'set', '4addr', 'on'])
+        dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+        hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
+                                            max_tries=15)
+    finally:
+        subprocess.call(['iw', dev[0].ifname, 'set', '4addr', 'off'])
+        subprocess.call(['ip', 'link', 'set', 'dev', 'wds-br0', 'down'])
+        subprocess.call(['brctl', 'delbr', 'wds-br0'])
+
+def test_ap_inactivity_poll(dev, apdev):
+    """AP using inactivity poll"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['ap_max_inactivity'] = "1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    hapd.set("ext_mgmt_frame_handling", "1")
+    dev[0].request("DISCONNECT")
+    ev = hapd.wait_event(["MGMT-RX"], timeout=5)
+    if ev is None:
+        raise Exception("MGMT RX wait timed out for Deauth")
+    hapd.set("ext_mgmt_frame_handling", "0")
+    ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=30)
+    if ev is None:
+        raise Exception("STA disconnection on inactivity was not reported")
+
+def test_ap_inactivity_disconnect(dev, apdev):
+    """AP using inactivity disconnect"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['ap_max_inactivity'] = "1"
+    params['skip_inactivity_poll'] = "1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    hapd.set("ext_mgmt_frame_handling", "1")
+    dev[0].request("DISCONNECT")
+    ev = hapd.wait_event(["MGMT-RX"], timeout=5)
+    if ev is None:
+        raise Exception("MGMT RX wait timed out for Deauth")
+    hapd.set("ext_mgmt_frame_handling", "0")
+    ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=30)
+    if ev is None:
+        raise Exception("STA disconnection on inactivity was not reported")
+
+def test_ap_basic_rates(dev, apdev):
+    """Open AP with lots of basic rates"""
+    ssid = "basic rates"
+    params = {}
+    params['ssid'] = ssid
+    params['basic_rates'] = "10 20 55 110 60 90 120 180 240 360 480 540"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+
+def test_ap_short_preamble(dev, apdev):
+    """Open AP with short preamble"""
+    ssid = "short preamble"
+    params = {}
+    params['ssid'] = ssid
+    params['preamble'] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+
+def test_ap_spectrum_management_required(dev, apdev):
+    """Open AP with spectrum management required"""
+    ssid = "spectrum mgmt"
+    params = {}
+    params['ssid'] = ssid
+    params["country_code"] = "JP"
+    params["hw_mode"] = "a"
+    params["channel"] = "36"
+    params["ieee80211d"] = "1"
+    params["local_pwr_constraint"] = "3"
+    params['spectrum_mgmt_required'] = "1"
+    try:
+        hapd = None
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        dev[0].connect(ssid, key_mgmt="NONE", scan_freq="5180")
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_ap_max_listen_interval(dev, apdev):
+    """Open AP with maximum listen interval limit"""
+    ssid = "listen"
+    params = {}
+    params['ssid'] = ssid
+    params['max_listen_interval'] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412", wait_connect=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
+    if ev is None:
+        raise Exception("Association rejection not reported")
+    if "status_code=51" not in ev:
+        raise Exception("Unexpected ASSOC-REJECT reason")
+
+def test_ap_max_num_sta(dev, apdev):
+    """Open AP with maximum STA count"""
+    ssid = "max"
+    params = {}
+    params['ssid'] = ssid
+    params['max_num_sta'] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412", wait_connect=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected association")
+
+def test_ap_tx_queue_params(dev, apdev):
+    """Open AP with TX queue params set"""
+    ssid = "tx"
+    params = {}
+    params['ssid'] = ssid
+    params['tx_queue_data2_aifs'] = "4"
+    params['tx_queue_data2_cwmin'] = "7"
+    params['tx_queue_data2_cwmax'] = "1023"
+    params['tx_queue_data2_burst'] = "4.2"
+    params['tx_queue_data1_aifs'] = "4"
+    params['tx_queue_data1_cwmin'] = "7"
+    params['tx_queue_data1_cwmax'] = "1023"
+    params['tx_queue_data1_burst'] = "2"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
diff --git a/hostap/tests/hwsim/test_ap_pmf.py b/hostap/tests/hwsim/test_ap_pmf.py
new file mode 100644
index 0000000..62306b5
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_pmf.py
@@ -0,0 +1,408 @@
+# Protected management frames tests
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import hostapd
+from wlantest import Wlantest
+from wpasupplicant import WpaSupplicant
+from test_ap_eap import eap_connect
+
+def test_ap_pmf_required(dev, apdev):
+    """WPA2-PSK AP with PMF required"""
+    ssid = "test-pmf-required"
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+    params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+    params["wpa_key_mgmt"] = "WPA-PSK-SHA256";
+    params["ieee80211w"] = "2";
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    key_mgmt = hapd.get_config()['key_mgmt']
+    if key_mgmt.split(' ')[0] != "WPA-PSK-SHA256":
+        raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+    dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+    if "[WPA2-PSK-SHA256-CCMP]" not in dev[0].request("SCAN_RESULTS"):
+        raise Exception("Scan results missing RSN element info")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    dev[1].connect(ssid, psk="12345678", ieee80211w="2",
+                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[1], hapd)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    hapd.request("SA_QUERY " + dev[0].p2p_interface_addr())
+    hapd.request("SA_QUERY " + dev[1].p2p_interface_addr())
+    wt.require_ap_pmf_mandatory(apdev[0]['bssid'])
+    wt.require_sta_pmf(apdev[0]['bssid'], dev[0].p2p_interface_addr())
+    wt.require_sta_pmf_mandatory(apdev[0]['bssid'], dev[1].p2p_interface_addr())
+    time.sleep(0.1)
+    if wt.get_sta_counter("valid_saqueryresp_tx", apdev[0]['bssid'],
+                          dev[0].p2p_interface_addr()) < 1:
+        raise Exception("STA did not reply to SA Query")
+    if wt.get_sta_counter("valid_saqueryresp_tx", apdev[0]['bssid'],
+                          dev[1].p2p_interface_addr()) < 1:
+        raise Exception("STA did not reply to SA Query")
+
+def test_ap_pmf_optional(dev, apdev):
+    """WPA2-PSK AP with PMF optional"""
+    ssid = "test-pmf-optional"
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+    params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+    params["wpa_key_mgmt"] = "WPA-PSK";
+    params["ieee80211w"] = "1";
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    dev[1].connect(ssid, psk="12345678", ieee80211w="2",
+                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[1], hapd)
+    wt.require_ap_pmf_optional(apdev[0]['bssid'])
+    wt.require_sta_pmf(apdev[0]['bssid'], dev[0].p2p_interface_addr())
+    wt.require_sta_pmf_mandatory(apdev[0]['bssid'], dev[1].p2p_interface_addr())
+
+def test_ap_pmf_optional_2akm(dev, apdev):
+    """WPA2-PSK AP with PMF optional (2 AKMs)"""
+    ssid = "test-pmf-optional-2akm"
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+    params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+    params["wpa_key_mgmt"] = "WPA-PSK WPA-PSK-SHA256";
+    params["ieee80211w"] = "1";
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    dev[1].connect(ssid, psk="12345678", ieee80211w="2",
+                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[1], hapd)
+    wt.require_ap_pmf_optional(apdev[0]['bssid'])
+    wt.require_sta_pmf(apdev[0]['bssid'], dev[0].p2p_interface_addr())
+    wt.require_sta_key_mgmt(apdev[0]['bssid'], dev[0].p2p_interface_addr(),
+                            "PSK-SHA256")
+    wt.require_sta_pmf_mandatory(apdev[0]['bssid'], dev[1].p2p_interface_addr())
+    wt.require_sta_key_mgmt(apdev[0]['bssid'], dev[1].p2p_interface_addr(),
+                            "PSK-SHA256")
+
+def test_ap_pmf_negative(dev, apdev):
+    """WPA2-PSK AP without PMF (negative test)"""
+    ssid = "test-pmf-negative"
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+    params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    try:
+        dev[1].connect(ssid, psk="12345678", ieee80211w="2",
+                       key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                       scan_freq="2412")
+        hwsim_utils.test_connectivity(dev[1], hapd)
+        raise Exception("PMF required STA connected to no PMF AP")
+    except Exception, e:
+        logger.debug("Ignore expected exception: " + str(e))
+    wt.require_ap_no_pmf(apdev[0]['bssid'])
+
+def test_ap_pmf_assoc_comeback(dev, apdev):
+    """WPA2-PSK AP with PMF association comeback"""
+    ssid = "assoc-comeback"
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+    params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+    params["wpa_key_mgmt"] = "WPA-PSK-SHA256";
+    params["ieee80211w"] = "2";
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+    hapd.set("ext_mgmt_frame_handling", "1")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=10)
+    hapd.set("ext_mgmt_frame_handling", "0")
+    dev[0].request("REASSOCIATE")
+    dev[0].wait_connected(timeout=10, error="Timeout on re-connection")
+    if wt.get_sta_counter("assocresp_comeback", apdev[0]['bssid'],
+                          dev[0].p2p_interface_addr()) < 1:
+        raise Exception("AP did not use association comeback request")
+
+def test_ap_pmf_assoc_comeback2(dev, apdev):
+    """WPA2-PSK AP with PMF association comeback (using DROP_SA)"""
+    ssid = "assoc-comeback"
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+    params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+    params["wpa_key_mgmt"] = "WPA-PSK";
+    params["ieee80211w"] = "1";
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk="12345678", ieee80211w="2",
+                   key_mgmt="WPA-PSK", proto="WPA2", scan_freq="2412")
+    if "OK" not in dev[0].request("DROP_SA"):
+        raise Exception("DROP_SA failed")
+    dev[0].request("REASSOCIATE")
+    dev[0].wait_connected(timeout=10, error="Timeout on re-connection")
+    if wt.get_sta_counter("reassocresp_comeback", apdev[0]['bssid'],
+                          dev[0].p2p_interface_addr()) < 1:
+        raise Exception("AP did not use reassociation comeback request")
+
+def test_ap_pmf_sta_sa_query(dev, apdev):
+    """WPA2-PSK AP with station using SA Query"""
+    ssid = "assoc-comeback"
+    addr = dev[0].own_addr()
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="use_monitor=1")
+    id = wpas.add_network()
+    wpas.set_network(id, "mode", "2")
+    wpas.set_network_quoted(id, "ssid", ssid)
+    wpas.set_network(id, "proto", "WPA2")
+    wpas.set_network(id, "key_mgmt", "WPA-PSK-SHA256")
+    wpas.set_network(id, "ieee80211w", "2")
+    wpas.set_network_quoted(id, "psk", "12345678")
+    wpas.set_network(id, "pairwise", "CCMP")
+    wpas.set_network(id, "group", "CCMP")
+    wpas.set_network(id, "frequency", "2412")
+    wpas.connect_network(id)
+    bssid = wpas.own_addr()
+
+    dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+    wpas.request("DEAUTHENTICATE " + addr + " test=0")
+    wpas.request("DISASSOCIATE " + addr + " test=0")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected disconnection")
+
+    wpas.request("DEAUTHENTICATE " + addr + " reason=6 test=0")
+    wpas.request("DISASSOCIATE " + addr + " reason=7 test=0")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected disconnection")
+    if wt.get_sta_counter("valid_saqueryreq_tx", bssid, addr) < 1:
+        raise Exception("STA did not send SA Query")
+    if wt.get_sta_counter("valid_saqueryresp_rx", bssid, addr) < 1:
+        raise Exception("AP did not reply to SA Query")
+
+def test_ap_pmf_sta_sa_query_no_response(dev, apdev):
+    """WPA2-PSK AP with station using SA Query and getting no response"""
+    ssid = "assoc-comeback"
+    addr = dev[0].own_addr()
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="use_monitor=1")
+    id = wpas.add_network()
+    wpas.set_network(id, "mode", "2")
+    wpas.set_network_quoted(id, "ssid", ssid)
+    wpas.set_network(id, "proto", "WPA2")
+    wpas.set_network(id, "key_mgmt", "WPA-PSK-SHA256")
+    wpas.set_network(id, "ieee80211w", "2")
+    wpas.set_network_quoted(id, "psk", "12345678")
+    wpas.set_network(id, "pairwise", "CCMP")
+    wpas.set_network(id, "group", "CCMP")
+    wpas.set_network(id, "frequency", "2412")
+    wpas.connect_network(id)
+    bssid = wpas.own_addr()
+
+    dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+    wpas.request("DEAUTHENTICATE " + addr + " test=0")
+    wpas.request("DISASSOCIATE " + addr + " test=0")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected disconnection")
+
+    wpas.request("SET ext_mgmt_frame_handling 1")
+    wpas.request("DEAUTHENTICATE " + addr + " reason=6 test=0")
+    wpas.request("DISASSOCIATE " + addr + " reason=7 test=0")
+    dev[0].wait_disconnected()
+    wpas.request("SET ext_mgmt_frame_handling 0")
+    dev[0].wait_connected()
+
+def test_ap_pmf_sta_unprot_deauth_burst(dev, apdev):
+    """WPA2-PSK AP with station receiving burst of unprotected Deauthentication frames"""
+    ssid = "deauth-attack"
+    addr = dev[0].own_addr()
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="use_monitor=1")
+    id = wpas.add_network()
+    wpas.set_network(id, "mode", "2")
+    wpas.set_network_quoted(id, "ssid", ssid)
+    wpas.set_network(id, "proto", "WPA2")
+    wpas.set_network(id, "key_mgmt", "WPA-PSK-SHA256")
+    wpas.set_network(id, "ieee80211w", "2")
+    wpas.set_network_quoted(id, "psk", "12345678")
+    wpas.set_network(id, "pairwise", "CCMP")
+    wpas.set_network(id, "group", "CCMP")
+    wpas.set_network(id, "frequency", "2412")
+    wpas.connect_network(id)
+    bssid = wpas.own_addr()
+
+    dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+
+    for i in range(0, 10):
+        wpas.request("DEAUTHENTICATE " + addr + " reason=6 test=0")
+        wpas.request("DISASSOCIATE " + addr + " reason=7 test=0")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected disconnection")
+    num_req = wt.get_sta_counter("valid_saqueryreq_tx", bssid, addr)
+    num_resp = wt.get_sta_counter("valid_saqueryresp_rx", bssid, addr)
+    if num_req < 1:
+        raise Exception("STA did not send SA Query")
+    if num_resp < 1:
+        raise Exception("AP did not reply to SA Query")
+    if num_req > 1:
+        raise Exception("STA initiated too many SA Query procedures (%d)" % num_req)
+
+    time.sleep(10)
+    for i in range(0, 5):
+        wpas.request("DEAUTHENTICATE " + addr + " reason=6 test=0")
+        wpas.request("DISASSOCIATE " + addr + " reason=7 test=0")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected disconnection")
+    num_req = wt.get_sta_counter("valid_saqueryreq_tx", bssid, addr)
+    num_resp = wt.get_sta_counter("valid_saqueryresp_rx", bssid, addr)
+    if num_req != 2 or num_resp != 2:
+        raise Exception("Unexpected number of SA Query procedures (req=%d resp=%d)" % (num_req, num_resp))
+
+def test_ap_pmf_required_eap(dev, apdev):
+    """WPA2-EAP AP with PMF required"""
+    ssid = "test-pmf-required-eap"
+    params = hostapd.wpa2_eap_params(ssid=ssid)
+    params["wpa_key_mgmt"] = "WPA-EAP-SHA256";
+    params["ieee80211w"] = "2";
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    key_mgmt = hapd.get_config()['key_mgmt']
+    if key_mgmt.split(' ')[0] != "WPA-EAP-SHA256":
+        raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+    dev[0].connect("test-pmf-required-eap", key_mgmt="WPA-EAP-SHA256",
+                   ieee80211w="2", eap="PSK", identity="psk.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412")
+    dev[1].connect("test-pmf-required-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+                   ieee80211w="1", eap="PSK", identity="psk.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412")
+
+def test_ap_pmf_optional_eap(dev, apdev):
+    """WPA2EAP AP with PMF optional"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params["ieee80211w"] = "1";
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="pap user", anonymous_identity="ttls",
+                   password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+                   ieee80211w="1", scan_freq="2412")
+    dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+                   eap="TTLS", identity="pap user", anonymous_identity="ttls",
+                   password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+                   ieee80211w="2", scan_freq="2412")
+
+def test_ap_pmf_required_sha1(dev, apdev):
+    """WPA2-PSK AP with PMF required with SHA1 AKM"""
+    ssid = "test-pmf-required-sha1"
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+    params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+    params["wpa_key_mgmt"] = "WPA-PSK";
+    params["ieee80211w"] = "2";
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    key_mgmt = hapd.get_config()['key_mgmt']
+    if key_mgmt.split(' ')[0] != "WPA-PSK":
+        raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+    dev[0].connect(ssid, psk="12345678", ieee80211w="2",
+                   key_mgmt="WPA-PSK", proto="WPA2", scan_freq="2412")
+    if "[WPA2-PSK-CCMP]" not in dev[0].request("SCAN_RESULTS"):
+        raise Exception("Scan results missing RSN element info")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_pmf_toggle(dev, apdev):
+    """WPA2-PSK AP with PMF optional and changing PMF on reassociation"""
+    try:
+        _test_ap_pmf_toggle(dev, apdev)
+    finally:
+        dev[0].request("SET reassoc_same_bss_optim 0")
+
+def _test_ap_pmf_toggle(dev, apdev):
+    ssid = "test-pmf-optional"
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+    params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+    params["wpa_key_mgmt"] = "WPA-PSK";
+    params["ieee80211w"] = "1";
+    params["assoc_sa_query_max_timeout"] = "1"
+    params["assoc_sa_query_retry_timeout"] = "1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    addr = dev[0].own_addr()
+    dev[0].request("SET reassoc_same_bss_optim 1")
+    id = dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+                        key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                        scan_freq="2412")
+    wt.require_ap_pmf_optional(bssid)
+    wt.require_sta_pmf(bssid, addr)
+    sta = hapd.get_sta(addr)
+    if '[MFP]' not in sta['flags']:
+        raise Exception("MFP flag not present for STA")
+
+    dev[0].set_network(id, "ieee80211w", "0")
+    dev[0].request("REASSOCIATE")
+    dev[0].wait_connected()
+    wt.require_sta_no_pmf(bssid, addr)
+    sta = hapd.get_sta(addr)
+    if '[MFP]' in sta['flags']:
+        raise Exception("MFP flag unexpectedly present for STA")
+    cmd = subprocess.Popen(['iw', 'dev', apdev[0]['ifname'], 'station', 'get',
+                            addr], stdout=subprocess.PIPE)
+    (data,err) = cmd.communicate()
+    if "yes" in [l for l in data.splitlines() if "MFP" in l][0]:
+        raise Exception("Kernel STA entry had MFP enabled")
+
+    dev[0].set_network(id, "ieee80211w", "1")
+    dev[0].request("REASSOCIATE")
+    dev[0].wait_connected()
+    wt.require_sta_pmf(bssid, addr)
+    sta = hapd.get_sta(addr)
+    if '[MFP]' not in sta['flags']:
+        raise Exception("MFP flag not present for STA")
+    cmd = subprocess.Popen(['iw', 'dev', apdev[0]['ifname'], 'station', 'get',
+                            addr], stdout=subprocess.PIPE)
+    (data,err) = cmd.communicate()
+    if "yes" not in [l for l in data.splitlines() if "MFP" in l][0]:
+        raise Exception("Kernel STA entry did not have MFP enabled")
diff --git a/hostap/tests/hwsim/test_ap_psk.py b/hostap/tests/hwsim/test_ap_psk.py
new file mode 100644
index 0000000..281d54b
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_psk.py
@@ -0,0 +1,2065 @@
+# WPA2-Personal tests
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+from Crypto.Cipher import AES
+import hashlib
+import hmac
+import logging
+logger = logging.getLogger()
+import os
+import re
+import struct
+import subprocess
+import time
+
+import hostapd
+from utils import HwsimSkip, fail_test, skip_with_fips
+import hwsim_utils
+from wpasupplicant import WpaSupplicant
+
+def check_mib(dev, vals):
+    mib = dev.get_mib()
+    for v in vals:
+        if mib[v[0]] != v[1]:
+            raise Exception("Unexpected {} = {} (expected {})".format(v[0], mib[v[0]], v[1]))
+
+def test_ap_wpa2_psk(dev, apdev):
+    """WPA2-PSK AP with PSK instead of passphrase"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+    params = hostapd.wpa2_params(ssid=ssid)
+    params['wpa_psk'] = psk
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    key_mgmt = hapd.get_config()['key_mgmt']
+    if key_mgmt.split(' ')[0] != "WPA-PSK":
+        raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+    dev[0].connect(ssid, raw_psk=psk, scan_freq="2412")
+    dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+
+    sig = dev[0].request("SIGNAL_POLL").splitlines()
+    pkt = dev[0].request("PKTCNT_POLL").splitlines()
+    if "FREQUENCY=2412" not in sig:
+        raise Exception("Unexpected SIGNAL_POLL value: " + str(sig))
+    if "TXBAD=0" not in pkt:
+        raise Exception("Unexpected TXBAD value: " + str(pkt))
+
+def test_ap_wpa2_psk_file(dev, apdev):
+    """WPA2-PSK AP with PSK from a file"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['wpa_psk_file'] = 'hostapd.wpa_psk'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[1].connect(ssid, psk="very secret", scan_freq="2412", wait_connect=False)
+    dev[2].connect(ssid, raw_psk=psk, scan_freq="2412")
+    dev[2].request("REMOVE_NETWORK all")
+    dev[0].connect(ssid, psk="very secret", scan_freq="2412")
+    dev[0].request("REMOVE_NETWORK all")
+    dev[2].connect(ssid, psk="another passphrase for all STAs", scan_freq="2412")
+    dev[0].connect(ssid, psk="another passphrase for all STAs", scan_freq="2412")
+    ev = dev[1].wait_event(["WPA: 4-Way Handshake failed"], timeout=10)
+    if ev is None:
+        raise Exception("Timed out while waiting for failure report")
+    dev[1].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_psk_mem(dev, apdev):
+    """WPA2-PSK AP with passphrase only in memory"""
+    try:
+        _test_ap_wpa2_psk_mem(dev, apdev)
+    finally:
+        dev[0].request("SCAN_INTERVAL 5")
+        dev[1].request("SCAN_INTERVAL 5")
+
+def _test_ap_wpa2_psk_mem(dev, apdev):
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+    params = hostapd.wpa2_params(ssid=ssid)
+    params['wpa_psk'] = psk
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect(ssid, mem_only_psk="1", scan_freq="2412", wait_connect=False)
+    dev[0].request("SCAN_INTERVAL 1")
+    ev = dev[0].wait_event(["CTRL-REQ-PSK_PASSPHRASE"], timeout=10)
+    if ev is None:
+        raise Exception("Request for PSK/passphrase timed out")
+    id = ev.split(':')[0].split('-')[-1]
+    dev[0].request("CTRL-RSP-PSK_PASSPHRASE-" + id + ':"' + passphrase + '"')
+    dev[0].wait_connected(timeout=10)
+
+    dev[1].connect(ssid, mem_only_psk="1", scan_freq="2412", wait_connect=False)
+    dev[1].request("SCAN_INTERVAL 1")
+    ev = dev[1].wait_event(["CTRL-REQ-PSK_PASSPHRASE"], timeout=10)
+    if ev is None:
+        raise Exception("Request for PSK/passphrase timed out(2)")
+    id = ev.split(':')[0].split('-')[-1]
+    dev[1].request("CTRL-RSP-PSK_PASSPHRASE-" + id + ':' + psk)
+    dev[1].wait_connected(timeout=10)
+
+def test_ap_wpa2_ptk_rekey(dev, apdev):
+    """WPA2-PSK AP and PTK rekey enforced by station"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, wpa_ptk_rekey="1", scan_freq="2412")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"])
+    if ev is None:
+        raise Exception("PTK rekey timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_ptk_rekey_ap(dev, apdev):
+    """WPA2-PSK AP and PTK rekey enforced by AP"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['wpa_ptk_rekey'] = '2'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"])
+    if ev is None:
+        raise Exception("PTK rekey timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_sha256_ptk_rekey(dev, apdev):
+    """WPA2-PSK/SHA256 AKM AP and PTK rekey enforced by station"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK-SHA256",
+                   wpa_ptk_rekey="1", scan_freq="2412")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"])
+    if ev is None:
+        raise Exception("PTK rekey timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    check_mib(dev[0], [ ("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-6"),
+                        ("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-6") ])
+
+def test_ap_wpa2_sha256_ptk_rekey_ap(dev, apdev):
+    """WPA2-PSK/SHA256 AKM AP and PTK rekey enforced by AP"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+    params['wpa_ptk_rekey'] = '2'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK-SHA256",
+                   scan_freq="2412")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"])
+    if ev is None:
+        raise Exception("PTK rekey timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    check_mib(dev[0], [ ("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-6"),
+                        ("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-6") ])
+
+def test_ap_wpa_ptk_rekey(dev, apdev):
+    """WPA-PSK/TKIP AP and PTK rekey enforced by station"""
+    skip_with_fips(dev[0])
+    ssid = "test-wpa-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, wpa_ptk_rekey="1", scan_freq="2412")
+    if "[WPA-PSK-TKIP]" not in dev[0].request("SCAN_RESULTS"):
+        raise Exception("Scan results missing WPA element info")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"])
+    if ev is None:
+        raise Exception("PTK rekey timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa_ptk_rekey_ap(dev, apdev):
+    """WPA-PSK/TKIP AP and PTK rekey enforced by AP"""
+    skip_with_fips(dev[0])
+    ssid = "test-wpa-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
+    params['wpa_ptk_rekey'] = '2'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+    if ev is None:
+        raise Exception("PTK rekey timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa_ccmp(dev, apdev):
+    """WPA-PSK/CCMP"""
+    ssid = "test-wpa-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
+    params['wpa_pairwise'] = "CCMP"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    check_mib(dev[0], [ ("dot11RSNAConfigGroupCipherSize", "128"),
+                        ("dot11RSNAGroupCipherRequested", "00-50-f2-4"),
+                        ("dot11RSNAPairwiseCipherRequested", "00-50-f2-4"),
+                        ("dot11RSNAAuthenticationSuiteRequested", "00-50-f2-2"),
+                        ("dot11RSNAGroupCipherSelected", "00-50-f2-4"),
+                        ("dot11RSNAPairwiseCipherSelected", "00-50-f2-4"),
+                        ("dot11RSNAAuthenticationSuiteSelected", "00-50-f2-2"),
+                        ("dot1xSuppSuppControlledPortStatus", "Authorized") ])
+
+def test_ap_wpa2_psk_file(dev, apdev):
+    """WPA2-PSK AP with various PSK file error and success cases"""
+    addr0 = dev[0].own_addr()
+    addr1 = dev[1].own_addr()
+    addr2 = dev[2].own_addr()
+    ssid = "psk"
+    pskfile = "/tmp/ap_wpa2_psk_file_errors.psk_file"
+    try:
+        os.remove(pskfile)
+    except:
+        pass
+
+    params = { "ssid": ssid, "wpa": "2", "wpa_key_mgmt": "WPA-PSK",
+               "rsn_pairwise": "CCMP", "wpa_psk_file": pskfile }
+
+    try:
+        # missing PSK file
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params, no_enable=True)
+        if "FAIL" not in hapd.request("ENABLE"):
+            raise Exception("Unexpected ENABLE success")
+        hapd.request("DISABLE")
+
+        # invalid MAC address
+        with open(pskfile, "w") as f:
+            f.write("\n")
+            f.write("foo\n")
+        if "FAIL" not in hapd.request("ENABLE"):
+            raise Exception("Unexpected ENABLE success")
+        hapd.request("DISABLE")
+
+        # no PSK on line
+        with open(pskfile, "w") as f:
+            f.write("00:11:22:33:44:55\n")
+        if "FAIL" not in hapd.request("ENABLE"):
+            raise Exception("Unexpected ENABLE success")
+        hapd.request("DISABLE")
+
+        # invalid PSK
+        with open(pskfile, "w") as f:
+            f.write("00:11:22:33:44:55 1234567\n")
+        if "FAIL" not in hapd.request("ENABLE"):
+            raise Exception("Unexpected ENABLE success")
+        hapd.request("DISABLE")
+
+        # valid PSK file
+        with open(pskfile, "w") as f:
+            f.write("00:11:22:33:44:55 12345678\n")
+            f.write(addr0 + " 123456789\n")
+            f.write(addr1 + " 123456789a\n")
+            f.write(addr2 + " 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n")
+        if "FAIL" in hapd.request("ENABLE"):
+            raise Exception("Unexpected ENABLE failure")
+
+        dev[0].connect(ssid, psk="123456789", scan_freq="2412")
+        dev[1].connect(ssid, psk="123456789a", scan_freq="2412")
+        dev[2].connect(ssid, raw_psk="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", scan_freq="2412")
+
+    finally:
+        try:
+            os.remove(pskfile)
+        except:
+            pass
+
+def test_ap_wpa2_psk_wildcard_ssid(dev, apdev):
+    """WPA2-PSK AP and wildcard SSID configuration"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("", bssid=apdev[0]['bssid'], psk=passphrase,
+                   scan_freq="2412")
+    dev[1].connect("", bssid=apdev[0]['bssid'], raw_psk=psk, scan_freq="2412")
+
+def test_ap_wpa2_gtk_rekey(dev, apdev):
+    """WPA2-PSK AP and GTK rekey enforced by AP"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['wpa_group_rekey'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+    if ev is None:
+        raise Exception("GTK rekey timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa_gtk_rekey(dev, apdev):
+    """WPA-PSK/TKIP AP and GTK rekey enforced by AP"""
+    skip_with_fips(dev[0])
+    ssid = "test-wpa-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
+    params['wpa_group_rekey'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+    if ev is None:
+        raise Exception("GTK rekey timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_gmk_rekey(dev, apdev):
+    """WPA2-PSK AP and GMK and GTK rekey enforced by AP"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['wpa_group_rekey'] = '1'
+    params['wpa_gmk_rekey'] = '2'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    for i in range(0, 3):
+        ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+        if ev is None:
+            raise Exception("GTK rekey timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_strict_rekey(dev, apdev):
+    """WPA2-PSK AP and strict GTK rekey enforced by AP"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['wpa_strict_rekey'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+    dev[1].request("DISCONNECT")
+    ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+    if ev is None:
+        raise Exception("GTK rekey timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_bridge_fdb(dev, apdev):
+    """Bridge FDB entry removal"""
+    try:
+        ssid = "test-wpa2-psk"
+        passphrase = "12345678"
+        params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+        params['bridge'] = 'ap-br0'
+        hostapd.add_ap(apdev[0]['ifname'], params)
+        subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+        dev[0].connect(ssid, psk=passphrase, scan_freq="2412",
+                       bssid=apdev[0]['bssid'])
+        dev[1].connect(ssid, psk=passphrase, scan_freq="2412",
+                       bssid=apdev[0]['bssid'])
+        addr0 = dev[0].p2p_interface_addr()
+        hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+        cmd = subprocess.Popen(['brctl', 'showmacs', 'ap-br0'],
+                               stdout=subprocess.PIPE)
+        macs1 = cmd.stdout.read()
+        dev[0].request("DISCONNECT")
+        dev[1].request("DISCONNECT")
+        time.sleep(1)
+        cmd = subprocess.Popen(['brctl', 'showmacs', 'ap-br0'],
+                               stdout=subprocess.PIPE)
+        macs2 = cmd.stdout.read()
+
+        addr1 = dev[1].p2p_interface_addr()
+        if addr0 not in macs1 or addr1 not in macs1:
+            raise Exception("Bridge FDB entry missing")
+        if addr0 in macs2 or addr1 in macs2:
+            raise Exception("Bridge FDB entry was not removed")
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'])
+        subprocess.call(['brctl', 'delbr', 'ap-br0'])
+
+def test_ap_wpa2_already_in_bridge(dev, apdev):
+    """hostapd behavior with interface already in bridge"""
+    ifname = apdev[0]['ifname']
+    br_ifname = 'ext-ap-br0'
+    try:
+        ssid = "test-wpa2-psk"
+        passphrase = "12345678"
+        subprocess.call(['brctl', 'addbr', br_ifname])
+        subprocess.call(['brctl', 'setfd', br_ifname, '0'])
+        subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
+        subprocess.call(['iw', ifname, 'set', 'type', '__ap'])
+        subprocess.call(['brctl', 'addif', br_ifname, ifname])
+        params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+        hapd = hostapd.add_ap(ifname, params)
+        if hapd.get_driver_status_field('brname') != br_ifname:
+            raise Exception("Bridge name not identified correctly")
+        dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down'])
+        subprocess.call(['brctl', 'delif', br_ifname, ifname])
+        subprocess.call(['iw', ifname, 'set', 'type', 'station'])
+        subprocess.call(['brctl', 'delbr', br_ifname])
+
+def test_ap_wpa2_in_different_bridge(dev, apdev):
+    """hostapd behavior with interface in different bridge"""
+    ifname = apdev[0]['ifname']
+    br_ifname = 'ext-ap-br0'
+    try:
+        ssid = "test-wpa2-psk"
+        passphrase = "12345678"
+        subprocess.call(['brctl', 'addbr', br_ifname])
+        subprocess.call(['brctl', 'setfd', br_ifname, '0'])
+        subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
+        subprocess.call(['iw', ifname, 'set', 'type', '__ap'])
+        subprocess.call(['brctl', 'addif', br_ifname, ifname])
+        time.sleep(0.5)
+        params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+        params['bridge'] = 'ap-br0'
+        hapd = hostapd.add_ap(ifname, params)
+        subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+        brname = hapd.get_driver_status_field('brname')
+        if brname != 'ap-br0':
+            raise Exception("Incorrect bridge: " + brname)
+        dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+        hwsim_utils.test_connectivity_iface(dev[0], hapd, "ap-br0")
+        if hapd.get_driver_status_field("added_bridge") != "1":
+            raise Exception("Unexpected added_bridge value")
+        if hapd.get_driver_status_field("added_if_into_bridge") != "1":
+            raise Exception("Unexpected added_if_into_bridge value")
+        dev[0].request("DISCONNECT")
+        hapd.disable()
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down'])
+        subprocess.call(['brctl', 'delif', br_ifname, ifname],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['brctl', 'delbr', br_ifname])
+
+def test_ap_wpa2_ext_add_to_bridge(dev, apdev):
+    """hostapd behavior with interface added to bridge externally"""
+    ifname = apdev[0]['ifname']
+    br_ifname = 'ext-ap-br0'
+    try:
+        ssid = "test-wpa2-psk"
+        passphrase = "12345678"
+        params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+        hapd = hostapd.add_ap(ifname, params)
+
+        subprocess.call(['brctl', 'addbr', br_ifname])
+        subprocess.call(['brctl', 'setfd', br_ifname, '0'])
+        subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
+        subprocess.call(['brctl', 'addif', br_ifname, ifname])
+        dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+        if hapd.get_driver_status_field('brname') != br_ifname:
+            raise Exception("Bridge name not identified correctly")
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down'])
+        subprocess.call(['brctl', 'delif', br_ifname, ifname])
+        subprocess.call(['brctl', 'delbr', br_ifname])
+
+def test_ap_wpa2_psk_ext(dev, apdev):
+    """WPA2-PSK AP using external EAPOL I/O"""
+    bssid = apdev[0]['bssid']
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+    params = hostapd.wpa2_params(ssid=ssid)
+    params['wpa_psk'] = psk
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd.request("SET ext_eapol_frame_io 1")
+    dev[0].request("SET ext_eapol_frame_io 1")
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412", wait_connect=False)
+    addr = dev[0].p2p_interface_addr()
+    while True:
+        ev = hapd.wait_event(["EAPOL-TX", "AP-STA-CONNECTED"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAPOL-TX from hostapd")
+        if "AP-STA-CONNECTED" in ev:
+            dev[0].wait_connected(timeout=15)
+            break
+        res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+        if "OK" not in res:
+            raise Exception("EAPOL_RX to wpa_supplicant failed")
+        ev = dev[0].wait_event(["EAPOL-TX", "CTRL-EVENT-CONNECTED"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+        if "CTRL-EVENT-CONNECTED" in ev:
+            break
+        res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+        if "OK" not in res:
+            raise Exception("EAPOL_RX to hostapd failed")
+
+def parse_eapol(data):
+    (version, type, length) = struct.unpack('>BBH', data[0:4])
+    payload = data[4:]
+    if length > len(payload):
+        raise Exception("Invalid EAPOL length")
+    if length < len(payload):
+        payload = payload[0:length]
+    eapol = {}
+    eapol['version'] = version
+    eapol['type'] = type
+    eapol['length'] = length
+    eapol['payload'] = payload
+    if type == 3:
+        # EAPOL-Key
+        (eapol['descr_type'],) = struct.unpack('B', payload[0:1])
+        payload = payload[1:]
+        if eapol['descr_type'] == 2 or eapol['descr_type'] == 254:
+            # RSN EAPOL-Key
+            (key_info, key_len) = struct.unpack('>HH', payload[0:4])
+            eapol['rsn_key_info'] = key_info
+            eapol['rsn_key_len'] = key_len
+            eapol['rsn_replay_counter'] = payload[4:12]
+            eapol['rsn_key_nonce'] = payload[12:44]
+            eapol['rsn_key_iv'] = payload[44:60]
+            eapol['rsn_key_rsc'] = payload[60:68]
+            eapol['rsn_key_id'] = payload[68:76]
+            eapol['rsn_key_mic'] = payload[76:92]
+            payload = payload[92:]
+            (eapol['rsn_key_data_len'],) = struct.unpack('>H', payload[0:2])
+            payload = payload[2:]
+            eapol['rsn_key_data'] = payload
+    return eapol
+
+def build_eapol(msg):
+    data = struct.pack(">BBH", msg['version'], msg['type'], msg['length'])
+    if msg['type'] == 3:
+        data += struct.pack('>BHH', msg['descr_type'], msg['rsn_key_info'],
+                            msg['rsn_key_len'])
+        data += msg['rsn_replay_counter']
+        data += msg['rsn_key_nonce']
+        data += msg['rsn_key_iv']
+        data += msg['rsn_key_rsc']
+        data += msg['rsn_key_id']
+        data += msg['rsn_key_mic']
+        data += struct.pack('>H', msg['rsn_key_data_len'])
+        data += msg['rsn_key_data']
+    else:
+        data += msg['payload']
+    return data
+
+def sha1_prf(key, label, data, outlen):
+    res = ''
+    counter = 0
+    while outlen > 0:
+        m = hmac.new(key, label, hashlib.sha1)
+        m.update(struct.pack('B', 0))
+        m.update(data)
+        m.update(struct.pack('B', counter))
+        counter += 1
+        hash = m.digest()
+        if outlen > len(hash):
+            res += hash
+            outlen -= len(hash)
+        else:
+            res += hash[0:outlen]
+            outlen = 0
+    return res
+
+def pmk_to_ptk(pmk, addr1, addr2, nonce1, nonce2):
+    if addr1 < addr2:
+        data = binascii.unhexlify(addr1.replace(':','')) + binascii.unhexlify(addr2.replace(':',''))
+    else:
+        data = binascii.unhexlify(addr2.replace(':','')) + binascii.unhexlify(addr1.replace(':',''))
+    if nonce1 < nonce2:
+        data += nonce1 + nonce2
+    else:
+        data += nonce2 + nonce1
+    label = "Pairwise key expansion"
+    ptk = sha1_prf(pmk, label, data, 48)
+    kck = ptk[0:16]
+    kek = ptk[16:32]
+    return (ptk, kck, kek)
+
+def eapol_key_mic(kck, msg):
+    msg['rsn_key_mic'] = binascii.unhexlify('00000000000000000000000000000000')
+    data = build_eapol(msg)
+    m = hmac.new(kck, data, hashlib.sha1)
+    msg['rsn_key_mic'] = m.digest()[0:16]
+
+def rsn_eapol_key_set(msg, key_info, key_len, nonce, data):
+    msg['rsn_key_info'] = key_info
+    msg['rsn_key_len'] = key_len
+    if nonce:
+        msg['rsn_key_nonce'] = nonce
+    else:
+        msg['rsn_key_nonce'] = binascii.unhexlify('0000000000000000000000000000000000000000000000000000000000000000')
+    if data:
+        msg['rsn_key_data_len'] = len(data)
+        msg['rsn_key_data'] = data
+        msg['length'] = 95 + len(data)
+    else:
+        msg['rsn_key_data_len'] = 0
+        msg['rsn_key_data'] = ''
+        msg['length'] = 95
+
+def recv_eapol(hapd):
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX from hostapd")
+    eapol = binascii.unhexlify(ev.split(' ')[2])
+    return parse_eapol(eapol)
+
+def send_eapol(hapd, addr, data):
+    res = hapd.request("EAPOL_RX " + addr + " " + binascii.hexlify(data))
+    if "OK" not in res:
+        raise Exception("EAPOL_RX to hostapd failed")
+
+def reply_eapol(info, hapd, addr, msg, key_info, nonce, data, kck):
+    logger.info("Send EAPOL-Key msg " + info)
+    rsn_eapol_key_set(msg, key_info, 0, nonce, data)
+    eapol_key_mic(kck, msg)
+    send_eapol(hapd, addr, build_eapol(msg))
+
+def hapd_connected(hapd):
+    ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
+
+def eapol_test(apdev, dev, wpa2=True):
+    bssid = apdev['bssid']
+    if wpa2:
+        ssid = "test-wpa2-psk"
+    else:
+        ssid = "test-wpa-psk"
+    psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+    pmk = binascii.unhexlify(psk)
+    if wpa2:
+        params = hostapd.wpa2_params(ssid=ssid)
+    else:
+        params = hostapd.wpa_params(ssid=ssid)
+    params['wpa_psk'] = psk
+    hapd = hostapd.add_ap(apdev['ifname'], params)
+    hapd.request("SET ext_eapol_frame_io 1")
+    dev.request("SET ext_eapol_frame_io 1")
+    dev.connect(ssid, raw_psk=psk, scan_freq="2412", wait_connect=False)
+    addr = dev.p2p_interface_addr()
+    if wpa2:
+        rsne = binascii.unhexlify('30140100000fac040100000fac040100000fac020000')
+    else:
+        rsne = binascii.unhexlify('dd160050f20101000050f20201000050f20201000050f202')
+    snonce = binascii.unhexlify('1111111111111111111111111111111111111111111111111111111111111111')
+    return (bssid,ssid,hapd,snonce,pmk,addr,rsne)
+
+def test_ap_wpa2_psk_ext_eapol(dev, apdev):
+    """WPA2-PSK AP using external EAPOL supplicant"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    msg = recv_eapol(hapd)
+    anonce = msg['rsn_key_nonce']
+    logger.info("Replay same data back")
+    send_eapol(hapd, addr, build_eapol(msg))
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.info("Truncated Key Data in EAPOL-Key msg 2/4")
+    rsn_eapol_key_set(msg, 0x0101, 0, snonce, rsne)
+    msg['length'] = 95 + 22 - 1
+    send_eapol(hapd, addr, build_eapol(msg))
+
+    reply_eapol("2/4", hapd, addr, msg, 0x010a, snonce, rsne, kck)
+
+    msg = recv_eapol(hapd)
+    if anonce != msg['rsn_key_nonce']:
+        raise Exception("ANonce changed")
+    logger.info("Replay same data back")
+    send_eapol(hapd, addr, build_eapol(msg))
+
+    reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+    hapd_connected(hapd)
+
+def test_ap_wpa2_psk_ext_eapol_retry1(dev, apdev):
+    """WPA2 4-way handshake with EAPOL-Key 1/4 retransmitted"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    msg1 = recv_eapol(hapd)
+    anonce = msg1['rsn_key_nonce']
+
+    msg2 = recv_eapol(hapd)
+    if anonce != msg2['rsn_key_nonce']:
+        raise Exception("ANonce changed")
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.info("Send EAPOL-Key msg 2/4")
+    msg = msg2
+    rsn_eapol_key_set(msg, 0x010a, 0, snonce, rsne)
+    eapol_key_mic(kck, msg)
+    send_eapol(hapd, addr, build_eapol(msg))
+
+    msg = recv_eapol(hapd)
+    if anonce != msg['rsn_key_nonce']:
+        raise Exception("ANonce changed")
+
+    reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+    hapd_connected(hapd)
+
+def test_ap_wpa2_psk_ext_eapol_retry1b(dev, apdev):
+    """WPA2 4-way handshake with EAPOL-Key 1/4 and 2/4 retransmitted"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    msg1 = recv_eapol(hapd)
+    anonce = msg1['rsn_key_nonce']
+    msg2 = recv_eapol(hapd)
+    if anonce != msg2['rsn_key_nonce']:
+        raise Exception("ANonce changed")
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+    reply_eapol("2/4 (a)", hapd, addr, msg1, 0x010a, snonce, rsne, kck)
+    reply_eapol("2/4 (b)", hapd, addr, msg2, 0x010a, snonce, rsne, kck)
+
+    msg = recv_eapol(hapd)
+    if anonce != msg['rsn_key_nonce']:
+        raise Exception("ANonce changed")
+
+    reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+    hapd_connected(hapd)
+
+def test_ap_wpa2_psk_ext_eapol_retry1c(dev, apdev):
+    """WPA2 4-way handshake with EAPOL-Key 1/4 and 2/4 retransmitted and SNonce changing"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    msg1 = recv_eapol(hapd)
+    anonce = msg1['rsn_key_nonce']
+
+    msg2 = recv_eapol(hapd)
+    if anonce != msg2['rsn_key_nonce']:
+        raise Exception("ANonce changed")
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+    reply_eapol("2/4 (a)", hapd, addr, msg1, 0x010a, snonce, rsne, kck)
+
+    snonce2 = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce2, anonce)
+    reply_eapol("2/4 (b)", hapd, addr, msg2, 0x010a, snonce2, rsne, kck)
+
+    msg = recv_eapol(hapd)
+    if anonce != msg['rsn_key_nonce']:
+        raise Exception("ANonce changed")
+    reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+    hapd_connected(hapd)
+
+def test_ap_wpa2_psk_ext_eapol_retry1d(dev, apdev):
+    """WPA2 4-way handshake with EAPOL-Key 1/4 and 2/4 retransmitted and SNonce changing and older used"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    msg1 = recv_eapol(hapd)
+    anonce = msg1['rsn_key_nonce']
+    msg2 = recv_eapol(hapd)
+    if anonce != msg2['rsn_key_nonce']:
+        raise Exception("ANonce changed")
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+    reply_eapol("2/4 (a)", hapd, addr, msg1, 0x010a, snonce, rsne, kck)
+
+    snonce2 = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    (ptk2, kck2, kek2) = pmk_to_ptk(pmk, addr, bssid, snonce2, anonce)
+
+    reply_eapol("2/4 (b)", hapd, addr, msg2, 0x010a, snonce2, rsne, kck2)
+    msg = recv_eapol(hapd)
+    if anonce != msg['rsn_key_nonce']:
+        raise Exception("ANonce changed")
+    reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+    hapd_connected(hapd)
+
+def test_ap_wpa2_psk_ext_eapol_type_diff(dev, apdev):
+    """WPA2 4-way handshake using external EAPOL supplicant"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    msg = recv_eapol(hapd)
+    anonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    # Incorrect descriptor type (frame dropped)
+    msg['descr_type'] = 253
+    rsn_eapol_key_set(msg, 0x010a, 0, snonce, rsne)
+    eapol_key_mic(kck, msg)
+    send_eapol(hapd, addr, build_eapol(msg))
+
+    # Incorrect descriptor type, but with a workaround (frame processed)
+    msg['descr_type'] = 254
+    rsn_eapol_key_set(msg, 0x010a, 0, snonce, rsne)
+    eapol_key_mic(kck, msg)
+    send_eapol(hapd, addr, build_eapol(msg))
+
+    msg = recv_eapol(hapd)
+    if anonce != msg['rsn_key_nonce']:
+        raise Exception("ANonce changed")
+    logger.info("Replay same data back")
+    send_eapol(hapd, addr, build_eapol(msg))
+
+    reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+    hapd_connected(hapd)
+
+def test_ap_wpa_psk_ext_eapol(dev, apdev):
+    """WPA2-PSK AP using external EAPOL supplicant"""
+    (bssid,ssid,hapd,snonce,pmk,addr,wpae) = eapol_test(apdev[0], dev[0],
+                                                        wpa2=False)
+
+    msg = recv_eapol(hapd)
+    anonce = msg['rsn_key_nonce']
+    logger.info("Replay same data back")
+    send_eapol(hapd, addr, build_eapol(msg))
+    logger.info("Too short data")
+    send_eapol(hapd, addr, build_eapol(msg)[0:98])
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+    msg['descr_type'] = 2
+    reply_eapol("2/4(invalid type)", hapd, addr, msg, 0x010a, snonce, wpae, kck)
+    msg['descr_type'] = 254
+    reply_eapol("2/4", hapd, addr, msg, 0x010a, snonce, wpae, kck)
+
+    msg = recv_eapol(hapd)
+    if anonce != msg['rsn_key_nonce']:
+        raise Exception("ANonce changed")
+    logger.info("Replay same data back")
+    send_eapol(hapd, addr, build_eapol(msg))
+
+    reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+    hapd_connected(hapd)
+
+def test_ap_wpa2_psk_ext_eapol_key_info(dev, apdev):
+    """WPA2-PSK 4-way handshake with strange key info values"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    msg = recv_eapol(hapd)
+    anonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+    rsn_eapol_key_set(msg, 0x0000, 0, snonce, rsne)
+    send_eapol(hapd, addr, build_eapol(msg))
+    rsn_eapol_key_set(msg, 0xffff, 0, snonce, rsne)
+    send_eapol(hapd, addr, build_eapol(msg))
+    # SMK M1
+    rsn_eapol_key_set(msg, 0x2802, 0, snonce, rsne)
+    send_eapol(hapd, addr, build_eapol(msg))
+    # SMK M3
+    rsn_eapol_key_set(msg, 0x2002, 0, snonce, rsne)
+    send_eapol(hapd, addr, build_eapol(msg))
+    # Request
+    rsn_eapol_key_set(msg, 0x0902, 0, snonce, rsne)
+    send_eapol(hapd, addr, build_eapol(msg))
+    # Request
+    rsn_eapol_key_set(msg, 0x0902, 0, snonce, rsne)
+    tmp_kck = binascii.unhexlify('00000000000000000000000000000000')
+    eapol_key_mic(tmp_kck, msg)
+    send_eapol(hapd, addr, build_eapol(msg))
+
+    reply_eapol("2/4", hapd, addr, msg, 0x010a, snonce, rsne, kck)
+
+    msg = recv_eapol(hapd)
+    if anonce != msg['rsn_key_nonce']:
+        raise Exception("ANonce changed")
+
+    # Request (valic MIC)
+    rsn_eapol_key_set(msg, 0x0902, 0, snonce, rsne)
+    eapol_key_mic(kck, msg)
+    send_eapol(hapd, addr, build_eapol(msg))
+    # Request (valid MIC, replayed counter)
+    rsn_eapol_key_set(msg, 0x0902, 0, snonce, rsne)
+    eapol_key_mic(kck, msg)
+    send_eapol(hapd, addr, build_eapol(msg))
+
+    reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+    hapd_connected(hapd)
+
+def build_eapol_key_1_4(anonce, replay_counter=1, key_data='', key_len=16):
+    msg = {}
+    msg['version'] = 2
+    msg['type'] = 3
+    msg['length'] = 95 + len(key_data)
+
+    msg['descr_type'] = 2
+    msg['rsn_key_info'] = 0x8a
+    msg['rsn_key_len'] = key_len
+    msg['rsn_replay_counter'] = struct.pack('>Q', replay_counter)
+    msg['rsn_key_nonce'] = anonce
+    msg['rsn_key_iv'] = binascii.unhexlify('00000000000000000000000000000000')
+    msg['rsn_key_rsc'] = binascii.unhexlify('0000000000000000')
+    msg['rsn_key_id'] = binascii.unhexlify('0000000000000000')
+    msg['rsn_key_mic'] = binascii.unhexlify('00000000000000000000000000000000')
+    msg['rsn_key_data_len'] = len(key_data)
+    msg['rsn_key_data'] = key_data
+    return msg
+
+def build_eapol_key_3_4(anonce, kck, key_data, replay_counter=2,
+                        key_info=0x13ca, extra_len=0, descr_type=2, key_len=16):
+    msg = {}
+    msg['version'] = 2
+    msg['type'] = 3
+    msg['length'] = 95 + len(key_data) + extra_len
+
+    msg['descr_type'] = descr_type
+    msg['rsn_key_info'] = key_info
+    msg['rsn_key_len'] = key_len
+    msg['rsn_replay_counter'] = struct.pack('>Q', replay_counter)
+    msg['rsn_key_nonce'] = anonce
+    msg['rsn_key_iv'] = binascii.unhexlify('00000000000000000000000000000000')
+    msg['rsn_key_rsc'] = binascii.unhexlify('0000000000000000')
+    msg['rsn_key_id'] = binascii.unhexlify('0000000000000000')
+    msg['rsn_key_data_len'] = len(key_data)
+    msg['rsn_key_data'] = key_data
+    eapol_key_mic(kck, msg)
+    return msg
+
+def aes_wrap(kek, plain):
+    n = len(plain) / 8
+    a = 0xa6a6a6a6a6a6a6a6
+    enc = AES.new(kek).encrypt
+    r = [plain[i * 8:(i + 1) * 8] for i in range(0, n)]
+    for j in range(6):
+        for i in range(1, n + 1):
+            b = enc(struct.pack('>Q', a) + r[i - 1])
+            a = struct.unpack('>Q', b[:8])[0] ^ (n * j + i)
+            r[i - 1] =b[8:]
+    return struct.pack('>Q', a) + ''.join(r)
+
+def pad_key_data(plain):
+    pad_len = len(plain) % 8
+    if pad_len:
+        pad_len = 8 - pad_len
+        plain += '\xdd'
+        pad_len -= 1
+        plain += pad_len * '\0'
+    return plain
+
+def test_ap_wpa2_psk_supp_proto(dev, apdev):
+    """WPA2-PSK 4-way handshake protocol testing for supplicant"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("Invalid AES wrap data length 0")
+    dev[0].dump_monitor()
+    msg = build_eapol_key_3_4(anonce, kck, '', replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: Unsupported AES-WRAP len 0"])
+    if ev is None:
+        raise Exception("Unsupported AES-WRAP len 0 not reported")
+
+    logger.debug("Invalid AES wrap data length 1")
+    dev[0].dump_monitor()
+    msg = build_eapol_key_3_4(anonce, kck, '1', replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: Unsupported AES-WRAP len 1"])
+    if ev is None:
+        raise Exception("Unsupported AES-WRAP len 1 not reported")
+
+    logger.debug("Invalid AES wrap data length 9")
+    dev[0].dump_monitor()
+    msg = build_eapol_key_3_4(anonce, kck, '123456789', replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: Unsupported AES-WRAP len 9"])
+    if ev is None:
+        raise Exception("Unsupported AES-WRAP len 9 not reported")
+
+    logger.debug("Invalid AES wrap data payload")
+    dev[0].dump_monitor()
+    msg = build_eapol_key_3_4(anonce, kck, '12345678', replay_counter=counter)
+    # do not increment counter to test replay protection
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: AES unwrap failed"])
+    if ev is None:
+        raise Exception("AES unwrap failure not reported")
+
+    logger.debug("Replay Count not increasing")
+    dev[0].dump_monitor()
+    msg = build_eapol_key_3_4(anonce, kck, '12345678', replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: EAPOL-Key Replay Counter did not increase"])
+    if ev is None:
+        raise Exception("Replay Counter replay not reported")
+
+    logger.debug("Missing Ack bit in key info")
+    dev[0].dump_monitor()
+    msg = build_eapol_key_3_4(anonce, kck, '12345678', replay_counter=counter,
+                              key_info=0x134a)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: No Ack bit in key_info"])
+    if ev is None:
+        raise Exception("Missing Ack bit not reported")
+
+    logger.debug("Unexpected Request bit in key info")
+    dev[0].dump_monitor()
+    msg = build_eapol_key_3_4(anonce, kck, '12345678', replay_counter=counter,
+                              key_info=0x1bca)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: EAPOL-Key with Request bit"])
+    if ev is None:
+        raise Exception("Request bit not reported")
+
+    logger.debug("Unsupported key descriptor version 0")
+    dev[0].dump_monitor()
+    msg = build_eapol_key_3_4(anonce, kck, '0123456789abcdef',
+                              replay_counter=counter, key_info=0x13c8)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: Unsupported EAPOL-Key descriptor version 0"])
+    if ev is None:
+        raise Exception("Unsupported EAPOL-Key descriptor version 0 not reported")
+
+    logger.debug("Key descriptor version 1 not allowed with CCMP")
+    dev[0].dump_monitor()
+    msg = build_eapol_key_3_4(anonce, kck, '0123456789abcdef',
+                              replay_counter=counter, key_info=0x13c9)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: CCMP is used, but EAPOL-Key descriptor version (1) is not 2"])
+    if ev is None:
+        raise Exception("Not allowed EAPOL-Key descriptor version not reported")
+
+    logger.debug("Invalid AES wrap payload with key descriptor version 2")
+    dev[0].dump_monitor()
+    msg = build_eapol_key_3_4(anonce, kck, '0123456789abcdef',
+                              replay_counter=counter, key_info=0x13ca)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: AES unwrap failed"])
+    if ev is None:
+        raise Exception("AES unwrap failure not reported")
+
+    logger.debug("Key descriptor version 3 workaround")
+    dev[0].dump_monitor()
+    msg = build_eapol_key_3_4(anonce, kck, '0123456789abcdef',
+                              replay_counter=counter, key_info=0x13cb)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: CCMP is used, but EAPOL-Key descriptor version (3) is not 2"])
+    if ev is None:
+        raise Exception("CCMP key descriptor mismatch not reported")
+    ev = dev[0].wait_event(["WPA: Interoperability workaround"])
+    if ev is None:
+        raise Exception("AES-128-CMAC workaround not reported")
+    ev = dev[0].wait_event(["WPA: Invalid EAPOL-Key MIC - dropping packet"])
+    if ev is None:
+        raise Exception("MIC failure with AES-128-CMAC workaround not reported")
+
+    logger.debug("Unsupported key descriptor version 4")
+    dev[0].dump_monitor()
+    msg = build_eapol_key_3_4(anonce, kck, '0123456789abcdef',
+                              replay_counter=counter, key_info=0x13cc)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: Unsupported EAPOL-Key descriptor version 4"])
+    if ev is None:
+        raise Exception("Unsupported EAPOL-Key descriptor version 4 not reported")
+
+    logger.debug("Unsupported key descriptor version 7")
+    dev[0].dump_monitor()
+    msg = build_eapol_key_3_4(anonce, kck, '0123456789abcdef',
+                              replay_counter=counter, key_info=0x13cf)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: Unsupported EAPOL-Key descriptor version 7"])
+    if ev is None:
+        raise Exception("Unsupported EAPOL-Key descriptor version 7 not reported")
+
+    logger.debug("Too short EAPOL header length")
+    dev[0].dump_monitor()
+    msg = build_eapol_key_3_4(anonce, kck, '12345678', replay_counter=counter,
+                              extra_len=-1)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: Invalid EAPOL-Key frame - key_data overflow (8 > 7)"])
+    if ev is None:
+        raise Exception("Key data overflow not reported")
+
+    logger.debug("Too long EAPOL header length")
+    msg = build_eapol_key_3_4(anonce, kck, '12345678', replay_counter=counter,
+                              extra_len=1)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+
+    logger.debug("Unsupported descriptor type 0")
+    msg = build_eapol_key_3_4(anonce, kck, '12345678', replay_counter=counter,
+                              descr_type=0)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+
+    logger.debug("WPA descriptor type 0")
+    msg = build_eapol_key_3_4(anonce, kck, '12345678', replay_counter=counter,
+                              descr_type=254)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+
+    logger.debug("Non-zero key index for pairwise key")
+    dev[0].dump_monitor()
+    wrapped = aes_wrap(kek, 16*'z')
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
+                              key_info=0x13ea)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: Ignored EAPOL-Key (Pairwise) with non-zero key index"])
+    if ev is None:
+        raise Exception("Non-zero key index not reported")
+
+    logger.debug("Invalid Key Data plaintext payload --> disconnect")
+    dev[0].dump_monitor()
+    wrapped = aes_wrap(kek, 16*'z')
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_no_ie(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: IE not included"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("No IEs in msg 3/4 --> disconnect")
+    dev[0].dump_monitor()
+    wrapped = aes_wrap(kek, 16*'\0')
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_ie_mismatch(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: IE mismatch"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("Msg 3/4 with mismatching IE")
+    dev[0].dump_monitor()
+    wrapped = aes_wrap(kek, pad_key_data(binascii.unhexlify('30060100000fac04dd16000fac010100dc11188831bf4aa4a8678d2b41498618')))
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_ok(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: success"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("Valid EAPOL-Key msg 3/4")
+    dev[0].dump_monitor()
+    plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010100dc11188831bf4aa4a8678d2b41498618')
+    wrapped = aes_wrap(kek, pad_key_data(plain))
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    dev[0].wait_connected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_no_gtk(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: no GTK"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("EAPOL-Key msg 3/4 without GTK KDE")
+    dev[0].dump_monitor()
+    plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00')
+    wrapped = aes_wrap(kek, pad_key_data(plain))
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected connection completion reported")
+
+def test_ap_wpa2_psk_supp_proto_anonce_change(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: ANonce change"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("Valid EAPOL-Key msg 3/4")
+    dev[0].dump_monitor()
+    anonce2 = binascii.unhexlify('3333333333333333333333333333333333333333333333333333333333333333')
+    plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010100dc11188831bf4aa4a8678d2b41498618')
+    wrapped = aes_wrap(kek, pad_key_data(plain))
+    msg = build_eapol_key_3_4(anonce2, kck, wrapped, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: ANonce from message 1 of 4-Way Handshake differs from 3 of 4-Way Handshake"])
+    if ev is None:
+        raise Exception("ANonce change not reported")
+
+def test_ap_wpa2_psk_supp_proto_unexpected_group_msg(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: unexpected group message"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("Group key 1/2 instead of msg 3/4")
+    dev[0].dump_monitor()
+    wrapped = aes_wrap(kek, binascii.unhexlify('dd16000fac010100dc11188831bf4aa4a8678d2b41498618'))
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
+                              key_info=0x13c2)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: Group Key Handshake started prior to completion of 4-way handshake"])
+    if ev is None:
+        raise Exception("Unexpected group key message not reported")
+    dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_msg_1_invalid_kde(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: invalid KDE in msg 1/4"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4 with invalid KDE
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter,
+                              key_data=binascii.unhexlify('5555'))
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_wrong_pairwise_key_len(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: wrong pairwise key length"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("Valid EAPOL-Key msg 3/4")
+    dev[0].dump_monitor()
+    plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010100dc11188831bf4aa4a8678d2b41498618')
+    wrapped = aes_wrap(kek, pad_key_data(plain))
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
+                              key_len=15)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: Invalid CCMP key length 15"])
+    if ev is None:
+        raise Exception("Invalid CCMP key length not reported")
+    dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_wrong_group_key_len(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: wrong group key length"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("Valid EAPOL-Key msg 3/4")
+    dev[0].dump_monitor()
+    plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd15000fac010100dc11188831bf4aa4a8678d2b414986')
+    wrapped = aes_wrap(kek, pad_key_data(plain))
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: Unsupported CCMP Group Cipher key length 15"])
+    if ev is None:
+        raise Exception("Invalid CCMP key length not reported")
+    dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_gtk_tx_bit_workaround(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: GTK TX bit workaround"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("Valid EAPOL-Key msg 3/4")
+    dev[0].dump_monitor()
+    plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010500dc11188831bf4aa4a8678d2b41498618')
+    wrapped = aes_wrap(kek, pad_key_data(plain))
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: Tx bit set for GTK, but pairwise keys are used - ignore Tx bit"])
+    if ev is None:
+        raise Exception("GTK Tx bit workaround not reported")
+    dev[0].wait_connected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_gtk_keyidx_0_and_3(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: GTK key index 0 and 3"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("Valid EAPOL-Key msg 3/4 (GTK keyidx 0)")
+    dev[0].dump_monitor()
+    plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010000dc11188831bf4aa4a8678d2b41498618')
+    wrapped = aes_wrap(kek, pad_key_data(plain))
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    dev[0].wait_connected(timeout=1)
+
+    logger.debug("Valid EAPOL-Key group msg 1/2 (GTK keyidx 3)")
+    dev[0].dump_monitor()
+    plain = binascii.unhexlify('dd16000fac010300dc11188831bf4aa4a8678d2b41498618')
+    wrapped = aes_wrap(kek, pad_key_data(plain))
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
+                              key_info=0x13c2)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    ev = dev[0].wait_event(["WPA: Group rekeying completed"])
+    if ev is None:
+        raise Exception("GTK rekeing not reported")
+
+    logger.debug("Unencrypted GTK KDE in group msg 1/2")
+    dev[0].dump_monitor()
+    plain = binascii.unhexlify('dd16000fac010300dc11188831bf4aa4a8678d2b41498618')
+    msg = build_eapol_key_3_4(anonce, kck, plain, replay_counter=counter,
+                              key_info=0x03c2)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: GTK IE in unencrypted key data"])
+    if ev is None:
+        raise Exception("Unencrypted GTK KDE not reported")
+    dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_no_gtk_in_group_msg(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: GTK KDE missing from group msg"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("Valid EAPOL-Key msg 3/4 (GTK keyidx 0)")
+    dev[0].dump_monitor()
+    plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010000dc11188831bf4aa4a8678d2b41498618')
+    wrapped = aes_wrap(kek, pad_key_data(plain))
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    dev[0].wait_connected(timeout=1)
+
+    logger.debug("No GTK KDE in EAPOL-Key group msg 1/2")
+    dev[0].dump_monitor()
+    plain = binascii.unhexlify('dd00dd00dd00dd00dd00dd00dd00dd00')
+    wrapped = aes_wrap(kek, pad_key_data(plain))
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
+                              key_info=0x13c2)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: No GTK IE in Group Key msg 1/2"])
+    if ev is None:
+        raise Exception("Missing GTK KDE not reported")
+    dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_too_long_gtk_in_group_msg(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: too long GTK KDE in group msg"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("Valid EAPOL-Key msg 3/4 (GTK keyidx 0)")
+    dev[0].dump_monitor()
+    plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010000dc11188831bf4aa4a8678d2b41498618')
+    wrapped = aes_wrap(kek, pad_key_data(plain))
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    dev[0].wait_connected(timeout=1)
+
+    logger.debug("EAPOL-Key group msg 1/2 with too long GTK KDE")
+    dev[0].dump_monitor()
+    plain = binascii.unhexlify('dd27000fac010100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
+    wrapped = aes_wrap(kek, pad_key_data(plain))
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
+                              key_info=0x13c2)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: Unsupported CCMP Group Cipher key length 33"])
+    if ev is None:
+        raise Exception("Too long GTK KDE not reported")
+    dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_too_long_gtk_kde(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: too long GTK KDE"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("EAPOL-Key msg 3/4 with too short GTK KDE")
+    dev[0].dump_monitor()
+    plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd27000fac010100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
+    wrapped = aes_wrap(kek, pad_key_data(plain))
+    msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_gtk_not_encrypted(dev, apdev):
+    """WPA2-PSK supplicant protocol testing: GTK KDE not encrypted"""
+    (bssid,ssid,hapd,snonce,pmk,addr,rsne) = eapol_test(apdev[0], dev[0])
+
+    # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+    msg = recv_eapol(hapd)
+    dev[0].dump_monitor()
+
+    # Build own EAPOL-Key msg 1/4
+    anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+    counter = 1
+    msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    msg = recv_eapol(dev[0])
+    snonce = msg['rsn_key_nonce']
+
+    (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+    logger.debug("Valid EAPOL-Key msg 3/4")
+    dev[0].dump_monitor()
+    plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010100dc11188831bf4aa4a8678d2b41498618')
+    msg = build_eapol_key_3_4(anonce, kck, plain, replay_counter=counter,
+                              key_info=0x03ca)
+    counter += 1
+    send_eapol(dev[0], addr, build_eapol(msg))
+    ev = dev[0].wait_event(["WPA: GTK IE in unencrypted key data"])
+    if ev is None:
+        raise Exception("Unencrypted GTK KDE not reported")
+    dev[0].wait_disconnected(timeout=1)
+
+def find_wpas_process(dev):
+    ifname = dev.ifname
+    cmd = subprocess.Popen(['ps', 'ax'], stdout=subprocess.PIPE)
+    (data,err) = cmd.communicate()
+    for l in data.splitlines():
+        if "wpa_supplicant" not in l:
+            continue
+        if "-i" + ifname not in l:
+            continue
+        return int(l.strip().split(' ')[0])
+    raise Exception("Could not find wpa_supplicant process")
+
+def read_process_memory(pid, key=None):
+    buf = bytes()
+    with open('/proc/%d/maps' % pid, 'r') as maps, \
+         open('/proc/%d/mem' % pid, 'r') as mem:
+        for l in maps.readlines():
+            m = re.match(r'([0-9a-f]+)-([0-9a-f]+) ([-r][-w][-x][-p])', l)
+            if not m:
+                continue
+            start = int(m.group(1), 16)
+            end = int(m.group(2), 16)
+            perm = m.group(3)
+            if start > 0xffffffffffff:
+                continue
+            if end < start:
+                continue
+            if not perm.startswith('rw'):
+                continue
+            mem.seek(start)
+            data = mem.read(end - start)
+            buf += data
+            if key and key in data:
+                logger.info("Key found in " + l)
+    return buf
+
+def verify_not_present(buf, key, fname, keyname):
+    pos = buf.find(key)
+    if pos < 0:
+        return
+
+    prefix = 2048 if pos > 2048 else pos
+    with open(fname + keyname, 'w') as f:
+        f.write(buf[pos - prefix:pos + 2048])
+    raise Exception(keyname + " found after disassociation")
+
+def get_key_locations(buf, key, keyname):
+    count = 0
+    pos = 0
+    while True:
+        pos = buf.find(key, pos)
+        if pos < 0:
+            break
+        logger.info("Found %s at %d" % (keyname, pos))
+        count += 1
+        pos += len(key)
+    return count
+
+def test_wpa2_psk_key_lifetime_in_memory(dev, apdev, params):
+    """WPA2-PSK and PSK/PTK lifetime in memory"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+    pmk = binascii.unhexlify(psk)
+    p = hostapd.wpa2_params(ssid=ssid)
+    p['wpa_psk'] = psk
+    hapd = hostapd.add_ap(apdev[0]['ifname'], p)
+
+    pid = find_wpas_process(dev[0])
+
+    id = dev[0].connect(ssid, raw_psk=psk, scan_freq="2412",
+                        only_add_network=True)
+
+    logger.info("Checking keys in memory after network profile configuration")
+    buf = read_process_memory(pid, pmk)
+    get_key_locations(buf, pmk, "PMK")
+
+    dev[0].request("REMOVE_NETWORK all")
+    logger.info("Checking keys in memory after network profile removal")
+    buf = read_process_memory(pid, pmk)
+    get_key_locations(buf, pmk, "PMK")
+
+    id = dev[0].connect(ssid, psk=passphrase, scan_freq="2412",
+                        only_add_network=True)
+
+    logger.info("Checking keys in memory before connection")
+    buf = read_process_memory(pid, pmk)
+    get_key_locations(buf, pmk, "PMK")
+
+    dev[0].connect_network(id, timeout=20)
+    time.sleep(1)
+
+    buf = read_process_memory(pid, pmk)
+
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+
+    dev[0].relog()
+    ptk = None
+    gtk = None
+    with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
+        for l in f.readlines():
+            if "WPA: PTK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                ptk = binascii.unhexlify(val)
+            if "WPA: Group Key - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                gtk = binascii.unhexlify(val)
+    if not pmk or not ptk or not gtk:
+        raise Exception("Could not find keys from debug log")
+    if len(gtk) != 16:
+        raise Exception("Unexpected GTK length")
+
+    kck = ptk[0:16]
+    kek = ptk[16:32]
+    tk = ptk[32:48]
+
+    logger.info("Checking keys in memory while associated")
+    get_key_locations(buf, pmk, "PMK")
+    if pmk not in buf:
+        raise HwsimSkip("PMK not found while associated")
+    if kck not in buf:
+        raise Exception("KCK not found while associated")
+    if kek not in buf:
+        raise Exception("KEK not found while associated")
+    if tk in buf:
+        raise Exception("TK found from memory")
+    if gtk in buf:
+        raise Exception("GTK found from memory")
+
+    logger.info("Checking keys in memory after disassociation")
+    buf = read_process_memory(pid, pmk)
+    get_key_locations(buf, pmk, "PMK")
+
+    # Note: PMK/PSK is still present in network configuration
+
+    fname = os.path.join(params['logdir'],
+                         'wpa2_psk_key_lifetime_in_memory.memctx-')
+    verify_not_present(buf, kck, fname, "KCK")
+    verify_not_present(buf, kek, fname, "KEK")
+    verify_not_present(buf, tk, fname, "TK")
+    verify_not_present(buf, gtk, fname, "GTK")
+
+    dev[0].request("REMOVE_NETWORK all")
+
+    logger.info("Checking keys in memory after network profile removal")
+    buf = read_process_memory(pid, pmk)
+    get_key_locations(buf, pmk, "PMK")
+
+    verify_not_present(buf, pmk, fname, "PMK")
+    verify_not_present(buf, kck, fname, "KCK")
+    verify_not_present(buf, kek, fname, "KEK")
+    verify_not_present(buf, tk, fname, "TK")
+    verify_not_present(buf, gtk, fname, "GTK")
+
+def test_ap_wpa2_psk_wep(dev, apdev):
+    """WPA2-PSK AP and WEP enabled"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    try:
+        hapd.set('wep_key0', '"hello"')
+        raise Exception("WEP key accepted to WPA2 network")
+    except Exception:
+        pass
+
+def test_ap_wpa2_psk_wpas_in_bridge(dev, apdev):
+    """WPA2-PSK AP and wpas interface in a bridge"""
+    br_ifname='sta-br0'
+    ifname='wlan5'
+    try:
+        _test_ap_wpa2_psk_wpas_in_bridge(dev, apdev)
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down'])
+        subprocess.call(['brctl', 'delif', br_ifname, ifname])
+        subprocess.call(['brctl', 'delbr', br_ifname])
+        subprocess.call(['iw', ifname, 'set', '4addr', 'off'])
+
+def _test_ap_wpa2_psk_wpas_in_bridge(dev, apdev):
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    br_ifname='sta-br0'
+    ifname='wlan5'
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    subprocess.call(['brctl', 'addbr', br_ifname])
+    subprocess.call(['brctl', 'setfd', br_ifname, '0'])
+    subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
+    subprocess.call(['iw', ifname, 'set', '4addr', 'on'])
+    subprocess.check_call(['brctl', 'addif', br_ifname, ifname])
+    wpas.interface_add(ifname, br_ifname=br_ifname)
+
+    wpas.connect(ssid, psk=passphrase, scan_freq="2412")
+
+def test_ap_wpa2_psk_ifdown(dev, apdev):
+    """AP with open mode and external ifconfig down"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    subprocess.call(['ip', 'link', 'set', 'dev', apdev[0]['ifname'], 'down'])
+    ev = hapd.wait_event(["INTERFACE-DISABLED"], timeout=10)
+    if ev is None:
+        raise Exception("No INTERFACE-DISABLED event")
+    # this wait tests beacon loss detection in mac80211
+    dev[0].wait_disconnected()
+    subprocess.call(['ip', 'link', 'set', 'dev', apdev[0]['ifname'], 'up'])
+    ev = hapd.wait_event(["INTERFACE-ENABLED"], timeout=10)
+    if ev is None:
+        raise Exception("No INTERFACE-ENABLED event")
+    dev[0].wait_connected()
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_psk_drop_first_msg_4(dev, apdev):
+    """WPA2-PSK and first EAPOL-Key msg 4/4 dropped"""
+    bssid = apdev[0]['bssid']
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+    params = hostapd.wpa2_params(ssid=ssid)
+    params['wpa_psk'] = psk
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd.request("SET ext_eapol_frame_io 1")
+    dev[0].request("SET ext_eapol_frame_io 1")
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412", wait_connect=False)
+    addr = dev[0].own_addr()
+
+    # EAPOL-Key msg 1/4
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX from hostapd")
+    res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+    if "OK" not in res:
+        raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+    # EAPOL-Key msg 2/4
+    ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+    res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+    if "OK" not in res:
+        raise Exception("EAPOL_RX to hostapd failed")
+
+    # EAPOL-Key msg 3/4
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX from hostapd")
+    res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+    if "OK" not in res:
+        raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+    # EAPOL-Key msg 4/4
+    ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+    logger.info("Drop the first EAPOL-Key msg 4/4")
+
+    # wpa_supplicant believes now that 4-way handshake succeeded; hostapd
+    # doesn't. Use normal EAPOL TX/RX to handle retries.
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    dev[0].wait_connected()
+
+    ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.1)
+    if ev is not None:
+        logger.info("Disconnection detected")
+        # The EAPOL-Key retries are supposed to allow the connection to be
+        # established without having to reassociate. However, this does not
+        # currently work since mac80211 ends up encrypting EAPOL-Key msg 4/4
+        # after the pairwise key has been configured and AP will drop those and
+        # disconnect the station after reaching retransmission limit. Connection
+        # is then established after reassociation. Once that behavior has been
+        # optimized to prevent EAPOL-Key frame encryption for retransmission
+        # case, this exception can be uncommented here.
+        #raise Exception("Unexpected disconnection")
+
+def test_ap_wpa2_psk_disable_enable(dev, apdev):
+    """WPA2-PSK AP getting disabled and re-enabled"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+    params = hostapd.wpa2_params(ssid=ssid)
+    params['wpa_psk'] = psk
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, raw_psk=psk, scan_freq="2412")
+
+    for i in range(2):
+        hapd.request("DISABLE")
+        dev[0].wait_disconnected()
+        hapd.request("ENABLE")
+        dev[0].wait_connected()
+        hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_psk_incorrect_passphrase(dev, apdev):
+    """WPA2-PSK AP and station using incorrect passphrase"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk="incorrect passphrase", scan_freq="2412",
+                   wait_connect=False)
+    ev = hapd.wait_event(["AP-STA-POSSIBLE-PSK-MISMATCH"], timeout=10)
+    if ev is None:
+        raise Exception("No AP-STA-POSSIBLE-PSK-MISMATCH reported")
+    dev[0].dump_monitor()
+
+    hapd.disable()
+    hapd.set("wpa_passphrase", "incorrect passphrase")
+    hapd.enable()
+
+    dev[0].wait_connected(timeout=20)
+
+def test_ap_wpa_ie_parsing(dev, apdev):
+    """WPA IE parsing"""
+    skip_with_fips(dev[0])
+    ssid = "test-wpa-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    id = dev[0].connect(ssid, psk=passphrase, scan_freq="2412",
+                        only_add_network=True)
+
+    tests = [ "dd040050f201",
+              "dd050050f20101",
+              "dd060050f2010100",
+              "dd060050f2010001",
+              "dd070050f201010000",
+              "dd080050f20101000050",
+              "dd090050f20101000050f2",
+              "dd0a0050f20101000050f202",
+              "dd0b0050f20101000050f20201",
+              "dd0c0050f20101000050f2020100",
+              "dd0c0050f20101000050f2020000",
+              "dd0c0050f20101000050f202ffff",
+              "dd0d0050f20101000050f202010000",
+              "dd0e0050f20101000050f20201000050",
+              "dd0f0050f20101000050f20201000050f2",
+              "dd100050f20101000050f20201000050f202",
+              "dd110050f20101000050f20201000050f20201",
+              "dd120050f20101000050f20201000050f2020100",
+              "dd120050f20101000050f20201000050f2020000",
+              "dd120050f20101000050f20201000050f202ffff",
+              "dd130050f20101000050f20201000050f202010000",
+              "dd140050f20101000050f20201000050f20201000050",
+              "dd150050f20101000050f20201000050f20201000050f2" ]
+    for t in tests:
+        try:
+            if "OK" not in dev[0].request("VENDOR_ELEM_ADD 13 " + t):
+                raise Exception("VENDOR_ELEM_ADD failed")
+            dev[0].select_network(id)
+            ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+            if ev is None:
+                raise Exception("Association rejection not reported")
+            dev[0].request("DISCONNECT")
+        finally:
+            dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+    tests = [ "dd170050f20101000050f20201000050f20201000050f202ff",
+              "dd180050f20101000050f20201000050f20201000050f202ffff",
+              "dd190050f20101000050f20201000050f20201000050f202ffffff" ]
+    for t in tests:
+        try:
+            if "OK" not in dev[0].request("VENDOR_ELEM_ADD 13 " + t):
+                raise Exception("VENDOR_ELEM_ADD failed")
+            dev[0].select_network(id)
+            dev[0].wait_connected()
+            dev[0].request("DISCONNECT")
+        finally:
+            dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+def test_ap_wpa2_psk_no_random(dev, apdev):
+    """WPA2-PSK AP and no random numbers available"""
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+    params = hostapd.wpa2_params(ssid=ssid)
+    params['wpa_psk'] = psk
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    with fail_test(hapd, 1, "wpa_gmk_to_gtk"):
+        id = dev[0].connect(ssid, raw_psk=psk, scan_freq="2412",
+                            wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=15)
+        if ev is None:
+            raise Exception("Disconnection event not reported")
+        dev[0].request("DISCONNECT")
+        dev[0].select_network(id, freq=2412)
+        dev[0].wait_connected()
+
+def test_rsn_ie_proto_psk_sta(dev, apdev):
+    """RSN element protocol testing for PSK cases on STA side"""
+    bssid = apdev[0]['bssid']
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    # This is the RSN element used normally by hostapd
+    params['own_ie_override'] = '30140100000fac040100000fac040100000fac020c00'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    if "FAIL" not in hapd.request("SET own_ie_override qwerty"):
+        raise Exception("Invalid own_ie_override value accepted")
+    id = dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+
+    tests = [ ('No RSN Capabilities field',
+               '30120100000fac040100000fac040100000fac02'),
+              ('Reserved RSN Capabilities bits set',
+               '30140100000fac040100000fac040100000fac023cff'),
+              ('Extra pairwise cipher suite (unsupported)',
+               '30180100000fac040200ffffffff000fac040100000fac020c00'),
+              ('Extra AKM suite (unsupported)',
+               '30180100000fac040100000fac040200ffffffff000fac020c00'),
+              ('PMKIDCount field included',
+               '30160100000fac040100000fac040100000fac020c000000'),
+              ('Unexpected Group Management Cipher Suite with PMF disabled',
+               '301a0100000fac040100000fac040100000fac020c000000000fac06'),
+              ('Extra octet after defined fields (future extensibility)',
+               '301b0100000fac040100000fac040100000fac020c000000000fac0600') ]
+    for txt,ie in tests:
+        dev[0].request("DISCONNECT")
+        dev[0].wait_disconnected()
+        logger.info(txt)
+        hapd.disable()
+        hapd.set('own_ie_override', ie)
+        hapd.enable()
+        dev[0].request("BSS_FLUSH 0")
+        dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
+        dev[0].select_network(id, freq=2412)
+        dev[0].wait_connected()
+
+def test_ap_cli_order(dev, apdev):
+    ssid = "test-rsn-setup"
+    passphrase = 'zzzzzzzz'
+    ifname = apdev[0]['ifname']
+
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.remove(ifname)
+    hapd_global.add(ifname)
+
+    hapd = hostapd.Hostapd(ifname)
+    hapd.set_defaults()
+    hapd.set('ssid', ssid)
+    hapd.set('wpa_passphrase', passphrase)
+    hapd.set('rsn_pairwise', 'CCMP')
+    hapd.set('wpa_key_mgmt', 'WPA-PSK')
+    hapd.set('wpa', '2')
+    hapd.enable()
+    cfg = hapd.get_config()
+    if cfg['group_cipher'] != 'CCMP':
+        raise Exception("Unexpected group_cipher: " + cfg['group_cipher'])
+    if cfg['rsn_pairwise_cipher'] != 'CCMP':
+        raise Exception("Unexpected rsn_pairwise_cipher: " + cfg['rsn_pairwise_cipher'])
+
+    ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=30)
+    if ev is None:
+        raise Exception("AP startup timed out")
+    if "AP-ENABLED" not in ev:
+        raise Exception("AP startup failed")
+
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
diff --git a/hostap/tests/hwsim/test_ap_qosmap.py b/hostap/tests/hwsim/test_ap_qosmap.py
new file mode 100644
index 0000000..67604b2
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_qosmap.py
@@ -0,0 +1,151 @@
+# QoS Mapping tests
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import hostapd
+from utils import HwsimSkip
+from wlantest import Wlantest
+
+def check_qos_map(ap, hapd, dev, sta, dscp, tid, ap_tid=None):
+    if not ap_tid:
+        ap_tid = tid
+    bssid = ap['bssid']
+    wt = Wlantest()
+    wt.clear_sta_counters(bssid, sta)
+    hwsim_utils.test_connectivity(dev, hapd, dscp=dscp, config=False)
+    time.sleep(0.02)
+    tx = wt.get_tx_tid(bssid, sta, tid)
+    if tx == 0:
+        [ tx, rx ] = wt.get_tid_counters(bssid, sta)
+        logger.info("Expected TX DSCP " + str(dscp) + " with TID " + str(tid) + " but counters: " + str(tx))
+        raise Exception("No STA->AP data frame using the expected TID")
+    rx = wt.get_rx_tid(bssid, sta, ap_tid)
+    if rx == 0:
+        [ tx, rx ] = wt.get_tid_counters(bssid, sta)
+        logger.info("Expected RX DSCP " + str(dscp) + " with TID " + str(ap_tid) + " but counters: " + str(rx))
+        raise Exception("No AP->STA data frame using the expected TID")
+
+def test_ap_qosmap(dev, apdev):
+    """QoS mapping"""
+    drv_flags = dev[0].get_driver_status_field("capa.flags")
+    if int(drv_flags, 0) & 0x40000000 == 0:
+        raise HwsimSkip("Driver does not support QoS Map")
+    ssid = "test-qosmap"
+    params = { "ssid": ssid }
+    params['qos_map_set'] = '53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,48,55'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    time.sleep(0.1)
+    addr = dev[0].p2p_interface_addr()
+    dev[0].request("DATA_TEST_CONFIG 1")
+    hapd.request("DATA_TEST_CONFIG 1")
+    check_qos_map(apdev[0], hapd, dev[0], addr, 53, 2)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 22, 6)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 8, 0)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 15, 0)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 0, 1)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 7, 1)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 16, 3)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 31, 3)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 32, 4)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 39, 4)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 40, 6)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 47, 6)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 48, 7)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 55, 7)
+    hapd.request("SET_QOS_MAP_SET 22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,48,55")
+    hapd.request("SEND_QOS_MAP_CONF " + dev[0].get_status_field("address"))
+    check_qos_map(apdev[0], hapd, dev[0], addr, 53, 7)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 22, 6)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 48, 7)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 55, 7)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 56, 56 >> 3)
+    check_qos_map(apdev[0], hapd, dev[0], addr, 63, 63 >> 3)
+    dev[0].request("DATA_TEST_CONFIG 0")
+    hapd.request("DATA_TEST_CONFIG 0")
+
+def test_ap_qosmap_default(dev, apdev):
+    """QoS mapping with default values"""
+    ssid = "test-qosmap-default"
+    params = { "ssid": ssid }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    addr = dev[0].p2p_interface_addr()
+    dev[0].request("DATA_TEST_CONFIG 1")
+    hapd.request("DATA_TEST_CONFIG 1")
+    for dscp in [ 0, 7, 8, 15, 16, 23, 24, 31, 32, 39, 40, 47, 48, 55, 56, 63]:
+        check_qos_map(apdev[0], hapd, dev[0], addr, dscp, dscp >> 3)
+    dev[0].request("DATA_TEST_CONFIG 0")
+    hapd.request("DATA_TEST_CONFIG 0")
+
+def test_ap_qosmap_default_acm(dev, apdev):
+    """QoS mapping with default values and ACM=1 for VO/VI"""
+    ssid = "test-qosmap-default"
+    params = { "ssid": ssid,
+               "wmm_ac_bk_aifs": "7",
+               "wmm_ac_bk_cwmin": "4",
+               "wmm_ac_bk_cwmax": "10",
+               "wmm_ac_bk_txop_limit": "0",
+               "wmm_ac_bk_acm": "0",
+               "wmm_ac_be_aifs": "3",
+               "wmm_ac_be_cwmin": "4",
+               "wmm_ac_be_cwmax": "10",
+               "wmm_ac_be_txop_limit": "0",
+               "wmm_ac_be_acm": "0",
+               "wmm_ac_vi_aifs": "2",
+               "wmm_ac_vi_cwmin": "3",
+               "wmm_ac_vi_cwmax": "4",
+               "wmm_ac_vi_txop_limit": "94",
+               "wmm_ac_vi_acm": "1",
+               "wmm_ac_vo_aifs": "2",
+               "wmm_ac_vo_cwmin": "2",
+               "wmm_ac_vo_cwmax": "2",
+               "wmm_ac_vo_txop_limit": "47",
+               "wmm_ac_vo_acm": "1"  }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    addr = dev[0].p2p_interface_addr()
+    dev[0].request("DATA_TEST_CONFIG 1")
+    hapd.request("DATA_TEST_CONFIG 1")
+    for dscp in [ 0, 7, 8, 15, 16, 23, 24, 31, 32, 39, 40, 47, 48, 55, 56, 63]:
+        ap_tid = dscp >> 3
+        tid = ap_tid
+        # downgrade VI/VO to BE
+        if tid in [ 4, 5, 6, 7 ]:
+            tid = 3
+        check_qos_map(apdev[0], hapd, dev[0], addr, dscp, tid, ap_tid)
+    dev[0].request("DATA_TEST_CONFIG 0")
+    hapd.request("DATA_TEST_CONFIG 0")
+
+def test_ap_qosmap_invalid(dev, apdev):
+    """QoS mapping ctrl_iface error handling"""
+    ssid = "test-qosmap"
+    params = { "ssid": ssid }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    if "FAIL" not in hapd.request("SEND_QOS_MAP_CONF 00:11:22:33:44:55"):
+        raise Exception("Unexpected SEND_QOS_MAP_CONF success")
+    if "FAIL" not in hapd.request("SET_QOS_MAP_SET "):
+        raise Exception("Unexpected SET_QOS_MAP_SET success")
+    if "FAIL" not in hapd.request("SET_QOS_MAP_SET 1,2,3"):
+        raise Exception("Unexpected SET_QOS_MAP_SET success")
+    if "FAIL" not in hapd.request("SET_QOS_MAP_SET 1,-2,3"):
+        raise Exception("Unexpected SET_QOS_MAP_SET success")
+    if "FAIL" not in hapd.request("SET_QOS_MAP_SET 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59"):
+        raise Exception("Unexpected SET_QOS_MAP_SET success")
+    if "FAIL" not in hapd.request("SET_QOS_MAP_SET 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21"):
+        raise Exception("Unexpected SET_QOS_MAP_SET success")
+
+    if "FAIL" in hapd.request("SET_QOS_MAP_SET 22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,48,55"):
+        raise Exception("Unexpected SET_QOS_MAP_SET failure")
+    if "FAIL" not in hapd.request("SEND_QOS_MAP_CONF 00:11:22:33:44:55"):
+        raise Exception("Unexpected SEND_QOS_MAP_CONF success")
+    if "FAIL" not in hapd.request("SEND_QOS_MAP_CONF 00:11:22:33:44"):
+        raise Exception("Unexpected SEND_QOS_MAP_CONF success")
diff --git a/hostap/tests/hwsim/test_ap_roam.py b/hostap/tests/hwsim/test_ap_roam.py
new file mode 100644
index 0000000..f41e272
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_roam.py
@@ -0,0 +1,67 @@
+# Roaming tests
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import hostapd
+
+def test_ap_roam_open(dev, apdev):
+    """Roam between two open APs"""
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    dev[0].connect("test-open", key_mgmt="NONE")
+    hwsim_utils.test_connectivity(dev[0], hapd0)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], { "ssid": "test-open" })
+    dev[0].scan(type="ONLY")
+    dev[0].roam(apdev[1]['bssid'])
+    hwsim_utils.test_connectivity(dev[0], hapd1)
+    dev[0].roam(apdev[0]['bssid'])
+    hwsim_utils.test_connectivity(dev[0], hapd0)
+
+def test_ap_roam_wpa2_psk(dev, apdev):
+    """Roam between two WPA2-PSK APs"""
+    params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wpa2-psk", psk="12345678")
+    hwsim_utils.test_connectivity(dev[0], hapd0)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+    dev[0].scan(type="ONLY")
+    dev[0].roam(apdev[1]['bssid'])
+    hwsim_utils.test_connectivity(dev[0], hapd1)
+    dev[0].roam(apdev[0]['bssid'])
+    hwsim_utils.test_connectivity(dev[0], hapd0)
+
+def test_ap_reassociation_to_same_bss(dev, apdev):
+    """Reassociate to the same BSS"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    dev[0].connect("test-open", key_mgmt="NONE")
+
+    dev[0].request("REASSOCIATE")
+    dev[0].wait_connected(timeout=10, error="Reassociation timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+    dev[0].request("REATTACH")
+    dev[0].wait_connected(timeout=10, error="Reattach timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_roam_set_bssid(dev, apdev):
+    """Roam control"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    hostapd.add_ap(apdev[1]['ifname'], { "ssid": "test-open" })
+    id = dev[0].connect("test-open", key_mgmt="NONE", bssid=apdev[1]['bssid'],
+                        scan_freq="2412")
+    if dev[0].get_status_field('bssid') != apdev[1]['bssid']:
+        raise Exception("Unexpected BSS")
+    # for now, these are just verifying that the code path to indicate
+    # within-ESS roaming changes can be executed; the actual results of those
+    # operations are not currently verified (that would require a test driver
+    # that does BSS selection)
+    dev[0].set_network(id, "bssid", "")
+    dev[0].set_network(id, "bssid", apdev[0]['bssid'])
+    dev[0].set_network(id, "bssid", apdev[1]['bssid'])
diff --git a/hostap/tests/hwsim/test_ap_tdls.py b/hostap/tests/hwsim/test_ap_tdls.py
new file mode 100644
index 0000000..bcfa3cc
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_tdls.py
@@ -0,0 +1,408 @@
+# TDLS tests
+# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import logging
+logger = logging.getLogger()
+import subprocess
+
+import hwsim_utils
+from hostapd import HostapdGlobal
+from hostapd import Hostapd
+import hostapd
+from utils import HwsimSkip, skip_with_fips
+from wlantest import Wlantest
+
+def start_ap_wpa2_psk(ifname):
+    params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+    return hostapd.add_ap(ifname, params)
+
+def connectivity(dev, hapd):
+    hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    hwsim_utils.test_connectivity(dev[1], hapd)
+
+def connect_2sta(dev, ssid, hapd):
+    dev[0].connect(ssid, psk="12345678", scan_freq="2412")
+    dev[1].connect(ssid, psk="12345678", scan_freq="2412")
+    connectivity(dev, hapd)
+
+def connect_2sta_wpa2_psk(dev, hapd):
+    connect_2sta(dev, "test-wpa2-psk", hapd)
+
+def connect_2sta_wpa_psk(dev, hapd):
+    connect_2sta(dev, "test-wpa-psk", hapd)
+
+def connect_2sta_wpa_psk_mixed(dev, hapd):
+    dev[0].connect("test-wpa-mixed-psk", psk="12345678", proto="WPA",
+                   scan_freq="2412")
+    dev[1].connect("test-wpa-mixed-psk", psk="12345678", proto="WPA2",
+                   scan_freq="2412")
+    connectivity(dev, hapd)
+
+def connect_2sta_wep(dev, hapd):
+    dev[0].connect("test-wep", key_mgmt="NONE", wep_key0='"hello"',
+                   scan_freq="2412")
+    dev[1].connect("test-wep", key_mgmt="NONE", wep_key0='"hello"',
+                   scan_freq="2412")
+    connectivity(dev, hapd)
+
+def connect_2sta_open(dev, hapd, scan_freq="2412"):
+    dev[0].connect("test-open", key_mgmt="NONE", scan_freq=scan_freq)
+    dev[1].connect("test-open", key_mgmt="NONE", scan_freq=scan_freq)
+    connectivity(dev, hapd)
+
+def wlantest_setup():
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+    wt.add_wepkey("68656c6c6f")
+
+def wlantest_tdls_packet_counters(bssid, addr0, addr1):
+    wt = Wlantest()
+    dl = wt.get_tdls_counter("valid_direct_link", bssid, addr0, addr1)
+    inv_dl = wt.get_tdls_counter("invalid_direct_link", bssid, addr0, addr1)
+    ap = wt.get_tdls_counter("valid_ap_path", bssid, addr0, addr1)
+    inv_ap = wt.get_tdls_counter("invalid_ap_path", bssid, addr0, addr1)
+    return [dl,inv_dl,ap,inv_ap]
+
+def tdls_check_dl(sta0, sta1, bssid, addr0, addr1):
+    wt = Wlantest()
+    wt.tdls_clear(bssid, addr0, addr1)
+    hwsim_utils.test_connectivity_sta(sta0, sta1)
+    [dl,inv_dl,ap,inv_ap] = wlantest_tdls_packet_counters(bssid, addr0, addr1)
+    if dl == 0:
+        raise Exception("No valid frames through direct link")
+    if inv_dl > 0:
+        raise Exception("Invalid frames through direct link")
+    if ap > 0:
+        raise Exception("Unexpected frames through AP path")
+    if inv_ap > 0:
+        raise Exception("Invalid frames through AP path")
+
+def tdls_check_ap(sta0, sta1, bssid, addr0, addr1):
+    wt = Wlantest()
+    wt.tdls_clear(bssid, addr0, addr1);
+    hwsim_utils.test_connectivity_sta(sta0, sta1)
+    [dl,inv_dl,ap,inv_ap] = wlantest_tdls_packet_counters(bssid, addr0, addr1)
+    if dl > 0:
+        raise Exception("Unexpected frames through direct link")
+    if inv_dl > 0:
+        raise Exception("Invalid frames through direct link")
+    if ap == 0:
+        raise Exception("No valid frames through AP path")
+    if inv_ap > 0:
+        raise Exception("Invalid frames through AP path")
+
+def check_connectivity(sta0, sta1, hapd):
+    hwsim_utils.test_connectivity_sta(sta0, sta1)
+    hwsim_utils.test_connectivity(sta0, hapd)
+    hwsim_utils.test_connectivity(sta1, hapd)
+
+def setup_tdls(sta0, sta1, ap, reverse=False, expect_fail=False):
+    logger.info("Setup TDLS")
+    hapd = hostapd.Hostapd(ap['ifname'])
+    check_connectivity(sta0, sta1, hapd)
+    bssid = ap['bssid']
+    addr0 = sta0.p2p_interface_addr()
+    addr1 = sta1.p2p_interface_addr()
+    wt = Wlantest()
+    wt.tdls_clear(bssid, addr0, addr1);
+    wt.tdls_clear(bssid, addr1, addr0);
+    sta0.tdls_setup(addr1)
+    time.sleep(1)
+    if expect_fail:
+        tdls_check_ap(sta0, sta1, bssid, addr0, addr1)
+        return
+    if reverse:
+        addr1 = sta0.p2p_interface_addr()
+        addr0 = sta1.p2p_interface_addr()
+    conf = wt.get_tdls_counter("setup_conf_ok", bssid, addr0, addr1);
+    if conf == 0:
+        raise Exception("No TDLS Setup Confirm (success) seen")
+    tdls_check_dl(sta0, sta1, bssid, addr0, addr1)
+    check_connectivity(sta0, sta1, hapd)
+
+def teardown_tdls(sta0, sta1, ap, responder=False, wildcard=False):
+    logger.info("Teardown TDLS")
+    hapd = hostapd.Hostapd(ap['ifname'])
+    check_connectivity(sta0, sta1, hapd)
+    bssid = ap['bssid']
+    addr0 = sta0.p2p_interface_addr()
+    addr1 = sta1.p2p_interface_addr()
+    if responder:
+        sta1.tdls_teardown(addr0)
+    elif wildcard:
+        sta0.tdls_teardown("*")
+    else:
+        sta0.tdls_teardown(addr1)
+    time.sleep(1)
+    wt = Wlantest()
+    teardown = wt.get_tdls_counter("teardown", bssid, addr0, addr1);
+    if teardown == 0:
+        raise Exception("No TDLS Setup Teardown seen")
+    tdls_check_ap(sta0, sta1, bssid, addr0, addr1)
+    check_connectivity(sta0, sta1, hapd)
+
+def check_tdls_link(sta0, sta1, connected=True):
+    addr0 = sta0.own_addr()
+    addr1 = sta1.own_addr()
+    status0 = sta0.tdls_link_status(addr1).rstrip()
+    status1 = sta1.tdls_link_status(addr0).rstrip()
+    logger.info("%s: %s" % (sta0.ifname, status0))
+    logger.info("%s: %s" % (sta1.ifname, status1))
+    if status0 != status1:
+        raise Exception("TDLS link status differs between stations")
+    if "status: connected" in status0:
+        if not connected:
+            raise Exception("Expected TDLS link status NOT to be connected")
+    else:
+        if connected:
+            raise Exception("Expected TDLS link status to be connected")
+
+def test_ap_tdls_discovery(dev, apdev):
+    """WPA2-PSK AP and two stations using TDLS discovery"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    connect_2sta_wpa2_psk(dev, hapd)
+    dev[0].request("TDLS_DISCOVER " + dev[1].p2p_interface_addr())
+    time.sleep(0.2)
+
+def test_ap_wpa2_tdls(dev, apdev):
+    """WPA2-PSK AP and two stations using TDLS"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    connect_2sta_wpa2_psk(dev, hapd)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    teardown_tdls(dev[0], dev[1], apdev[0])
+    setup_tdls(dev[1], dev[0], apdev[0])
+    #teardown_tdls(dev[0], dev[1], apdev[0])
+
+def test_ap_wpa2_tdls_concurrent_init(dev, apdev):
+    """Concurrent TDLS setup initiation"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    connect_2sta_wpa2_psk(dev, hapd)
+    dev[0].request("SET tdls_testing 0x80")
+    setup_tdls(dev[1], dev[0], apdev[0], reverse=True)
+
+def test_ap_wpa2_tdls_concurrent_init2(dev, apdev):
+    """Concurrent TDLS setup initiation (reverse)"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    connect_2sta_wpa2_psk(dev, hapd)
+    dev[1].request("SET tdls_testing 0x80")
+    setup_tdls(dev[0], dev[1], apdev[0])
+
+def test_ap_wpa2_tdls_decline_resp(dev, apdev):
+    """Decline TDLS Setup Response"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    connect_2sta_wpa2_psk(dev, hapd)
+    dev[1].request("SET tdls_testing 0x200")
+    setup_tdls(dev[1], dev[0], apdev[0], expect_fail=True)
+
+def test_ap_wpa2_tdls_long_lifetime(dev, apdev):
+    """TDLS with long TPK lifetime"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    connect_2sta_wpa2_psk(dev, hapd)
+    dev[1].request("SET tdls_testing 0x40")
+    setup_tdls(dev[1], dev[0], apdev[0])
+
+def test_ap_wpa2_tdls_long_frame(dev, apdev):
+    """TDLS with long setup/teardown frames"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    connect_2sta_wpa2_psk(dev, hapd)
+    dev[0].request("SET tdls_testing 0x1")
+    dev[1].request("SET tdls_testing 0x1")
+    setup_tdls(dev[1], dev[0], apdev[0])
+    teardown_tdls(dev[1], dev[0], apdev[0])
+    setup_tdls(dev[0], dev[1], apdev[0])
+
+def test_ap_wpa2_tdls_reneg(dev, apdev):
+    """Renegotiate TDLS link"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    connect_2sta_wpa2_psk(dev, hapd)
+    setup_tdls(dev[1], dev[0], apdev[0])
+    setup_tdls(dev[0], dev[1], apdev[0])
+
+def test_ap_wpa2_tdls_wrong_lifetime_resp(dev, apdev):
+    """Incorrect TPK lifetime in TDLS Setup Response"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    connect_2sta_wpa2_psk(dev, hapd)
+    dev[1].request("SET tdls_testing 0x10")
+    setup_tdls(dev[0], dev[1], apdev[0], expect_fail=True)
+
+def test_ap_wpa2_tdls_diff_rsnie(dev, apdev):
+    """TDLS with different RSN IEs"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    connect_2sta_wpa2_psk(dev, hapd)
+    dev[1].request("SET tdls_testing 0x2")
+    setup_tdls(dev[1], dev[0], apdev[0])
+    teardown_tdls(dev[1], dev[0], apdev[0])
+
+def test_ap_wpa2_tdls_wrong_tpk_m2_mic(dev, apdev):
+    """Incorrect MIC in TDLS Setup Response"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    connect_2sta_wpa2_psk(dev, hapd)
+    dev[0].request("SET tdls_testing 0x800")
+    addr0 = dev[0].p2p_interface_addr()
+    dev[1].tdls_setup(addr0)
+    time.sleep(1)
+
+def test_ap_wpa2_tdls_wrong_tpk_m3_mic(dev, apdev):
+    """Incorrect MIC in TDLS Setup Confirm"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    connect_2sta_wpa2_psk(dev, hapd)
+    dev[1].request("SET tdls_testing 0x800")
+    addr0 = dev[0].p2p_interface_addr()
+    dev[1].tdls_setup(addr0)
+    time.sleep(1)
+
+def test_ap_wpa_tdls(dev, apdev):
+    """WPA-PSK AP and two stations using TDLS"""
+    skip_with_fips(dev[0])
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          hostapd.wpa_params(ssid="test-wpa-psk",
+                                             passphrase="12345678"))
+    wlantest_setup()
+    connect_2sta_wpa_psk(dev, hapd)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    teardown_tdls(dev[0], dev[1], apdev[0])
+    setup_tdls(dev[1], dev[0], apdev[0])
+
+def test_ap_wpa_mixed_tdls(dev, apdev):
+    """WPA+WPA2-PSK AP and two stations using TDLS"""
+    skip_with_fips(dev[0])
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          hostapd.wpa_mixed_params(ssid="test-wpa-mixed-psk",
+                                                   passphrase="12345678"))
+    wlantest_setup()
+    connect_2sta_wpa_psk_mixed(dev, hapd)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    teardown_tdls(dev[0], dev[1], apdev[0])
+    setup_tdls(dev[1], dev[0], apdev[0])
+
+def test_ap_wep_tdls(dev, apdev):
+    """WEP AP and two stations using TDLS"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": "test-wep", "wep_key0": '"hello"' })
+    wlantest_setup()
+    connect_2sta_wep(dev, hapd)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    teardown_tdls(dev[0], dev[1], apdev[0])
+    setup_tdls(dev[1], dev[0], apdev[0])
+
+def test_ap_open_tdls(dev, apdev):
+    """Open AP and two stations using TDLS"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    wlantest_setup()
+    connect_2sta_open(dev, hapd)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    teardown_tdls(dev[0], dev[1], apdev[0])
+    setup_tdls(dev[1], dev[0], apdev[0])
+    teardown_tdls(dev[1], dev[0], apdev[0], wildcard=True)
+
+def test_ap_wpa2_tdls_bssid_mismatch(dev, apdev):
+    """TDLS failure due to BSSID mismatch"""
+    try:
+        ssid = "test-wpa2-psk"
+        passphrase = "12345678"
+        params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+        params['bridge'] = 'ap-br0'
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        hostapd.add_ap(apdev[1]['ifname'], params)
+        wlantest_setup()
+        subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+        dev[0].connect(ssid, psk=passphrase, scan_freq="2412",
+                       bssid=apdev[0]['bssid'])
+        dev[1].connect(ssid, psk=passphrase, scan_freq="2412",
+                       bssid=apdev[1]['bssid'])
+        hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+        hwsim_utils.test_connectivity_iface(dev[0], hapd, "ap-br0")
+        hwsim_utils.test_connectivity_iface(dev[1], hapd, "ap-br0")
+
+        addr0 = dev[0].p2p_interface_addr()
+        dev[1].tdls_setup(addr0)
+        time.sleep(1)
+        hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'])
+        subprocess.call(['brctl', 'delbr', 'ap-br0'])
+
+def test_ap_wpa2_tdls_responder_teardown(dev, apdev):
+    """TDLS teardown from responder with WPA2-PSK AP"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    connect_2sta_wpa2_psk(dev, hapd)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    teardown_tdls(dev[0], dev[1], apdev[0], responder=True)
+
+def test_ap_open_tdls_vht(dev, apdev):
+    """Open AP and two stations using TDLS"""
+    params = { "ssid": "test-open",
+               "country_code": "DE",
+               "hw_mode": "a",
+               "channel": "36",
+               "ieee80211n": "1",
+               "ieee80211ac": "1",
+               "ht_capab": "",
+               "vht_capab": "",
+               "vht_oper_chwidth": "0",
+               "vht_oper_centr_freq_seg0_idx": "0" }
+    try:
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        wlantest_setup()
+        connect_2sta_open(dev, hapd, scan_freq="5180")
+        setup_tdls(dev[0], dev[1], apdev[0])
+        teardown_tdls(dev[0], dev[1], apdev[0])
+        setup_tdls(dev[1], dev[0], apdev[0])
+        teardown_tdls(dev[1], dev[0], apdev[0], wildcard=True)
+    finally:
+        dev[0].request("DISCONNECT")
+        dev[1].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+        dev[1].flush_scan_cache()
+
+def test_tdls_chan_switch(dev, apdev):
+    """Open AP and two stations using TDLS"""
+    flags = int(dev[0].get_driver_status_field('capa.flags'), 16)
+    if flags & 0x800000000 == 0:
+        raise HwsimSkip("Driver does not support TDLS channel switching")
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    connect_2sta_open(dev, hapd)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    if "OK" not in dev[0].request("TDLS_CHAN_SWITCH " + dev[1].own_addr() + " 81 2462"):
+        raise Exception("Failed to enable TDLS channel switching")
+    if "OK" not in dev[0].request("TDLS_CANCEL_CHAN_SWITCH " + dev[1].own_addr()):
+        raise Exception("Could not disable TDLS channel switching")
+    if "FAIL" not in dev[0].request("TDLS_CANCEL_CHAN_SWITCH " + dev[1].own_addr()):
+        raise Exception("TDLS_CANCEL_CHAN_SWITCH accepted even though channel switching was already disabled")
+
+def test_ap_tdls_link_status(dev, apdev):
+    """Check TDLS link status between two stations"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    connect_2sta_wpa2_psk(dev, hapd)
+    check_tdls_link(dev[0], dev[1], connected=False)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    check_tdls_link(dev[0], dev[1], connected=True)
+    teardown_tdls(dev[0], dev[1], apdev[0])
+    check_tdls_link(dev[0], dev[1], connected=False)
+    if "FAIL" not in dev[0].request("TDLS_LINK_STATUS foo"):
+        raise Exception("Unexpected TDLS_LINK_STATUS response for invalid argument")
diff --git a/hostap/tests/hwsim/test_ap_track.py b/hostap/tests/hwsim/test_ap_track.py
new file mode 100644
index 0000000..5ca2b60
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_track.py
@@ -0,0 +1,285 @@
+# Test cases for hostapd tracking unconnected stations
+# Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import subprocess
+import time
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+
+def test_ap_track_sta(dev, apdev):
+    """Dualband AP tracking unconnected stations"""
+    try:
+        _test_ap_track_sta(dev, apdev)
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+
+def _test_ap_track_sta(dev, apdev):
+    params = { "ssid": "track",
+               "country_code": "US",
+               "hw_mode": "g",
+               "channel": "6",
+               "track_sta_max_num": "2" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    params = { "ssid": "track",
+               "country_code": "US",
+               "hw_mode": "a",
+               "channel": "40",
+               "track_sta_max_num": "100",
+               "track_sta_max_age": "1" }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    for i in range(2):
+        dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
+        dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
+        dev[1].scan_for_bss(bssid, freq=2437, force_scan=True)
+        dev[2].scan_for_bss(bssid2, freq=5200, force_scan=True)
+
+    addr0 = dev[0].own_addr()
+    addr1 = dev[1].own_addr()
+    addr2 = dev[2].own_addr()
+
+    track = hapd.request("TRACK_STA_LIST")
+    if addr0 not in track or addr1 not in track:
+        raise Exception("Station missing from 2.4 GHz tracking")
+    if addr2 in track:
+        raise Exception("Unexpected station included in 2.4 GHz tracking")
+    
+    track = hapd2.request("TRACK_STA_LIST")
+    if addr0 not in track or addr2 not in track:
+        raise Exception("Station missing from 5 GHz tracking")
+    if addr1 in track:
+        raise Exception("Unexpected station included in 5 GHz tracking")
+
+    # Test expiration
+    time.sleep(1.1)
+    track = hapd.request("TRACK_STA_LIST")
+    if addr0 not in track or addr1 not in track:
+        raise Exception("Station missing from 2.4 GHz tracking (expiration)")
+    track = hapd2.request("TRACK_STA_LIST")
+    if addr0 in track or addr2 in track:
+        raise Exception("Station not expired from 5 GHz tracking")
+
+    # Test maximum list length
+    dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
+    dev[1].scan_for_bss(bssid, freq=2437, force_scan=True)
+    dev[2].scan_for_bss(bssid, freq=2437, force_scan=True)
+    track = hapd.request("TRACK_STA_LIST")
+    if len(track.splitlines()) != 2:
+        raise Exception("Unexpected number of entries: %d" % len(track.splitlines()))
+    if addr1 not in track or addr2 not in track:
+        raise Exception("Station missing from 2.4 GHz tracking (max limit)")
+
+def test_ap_track_sta_no_probe_resp(dev, apdev):
+    """Dualband AP not replying to probes from dualband STA on 2.4 GHz"""
+    try:
+        _test_ap_track_sta_no_probe_resp(dev, apdev)
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+
+def _test_ap_track_sta_no_probe_resp(dev, apdev):
+    dev[0].flush_scan_cache()
+
+    params = { "ssid": "track",
+               "country_code": "US",
+               "hw_mode": "g",
+               "channel": "6",
+               "beacon_int": "10000",
+               "no_probe_resp_if_seen_on": apdev[1]['ifname'] }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    params = { "ssid": "track",
+               "country_code": "US",
+               "hw_mode": "a",
+               "channel": "40",
+               "track_sta_max_num": "100" }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
+    dev[1].scan_for_bss(bssid, freq=2437, force_scan=True)
+    dev[0].scan(freq=2437, type="ONLY")
+    dev[0].scan(freq=2437, type="ONLY")
+
+    if dev[0].get_bss(bssid):
+        raise Exception("2.4 GHz AP found unexpectedly")
+
+def test_ap_track_sta_no_auth(dev, apdev):
+    """Dualband AP rejecting authentication from dualband STA on 2.4 GHz"""
+    try:
+        _test_ap_track_sta_no_auth(dev, apdev)
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+
+def _test_ap_track_sta_no_auth(dev, apdev):
+    params = { "ssid": "track",
+               "country_code": "US",
+               "hw_mode": "g",
+               "channel": "6",
+               "track_sta_max_num": "100",
+               "no_auth_if_seen_on": apdev[1]['ifname'] }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    params = { "ssid": "track",
+               "country_code": "US",
+               "hw_mode": "a",
+               "channel": "40",
+               "track_sta_max_num": "100" }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
+    dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
+    dev[1].scan_for_bss(bssid, freq=2437, force_scan=True)
+
+    dev[1].connect("track", key_mgmt="NONE", scan_freq="2437")
+
+    dev[0].connect("track", key_mgmt="NONE", scan_freq="2437",
+                   freq_list="2437", wait_connect=False)
+    dev[1].request("DISCONNECT")
+    ev = dev[0].wait_event([ "CTRL-EVENT-CONNECTED",
+                             "CTRL-EVENT-AUTH-REJECT" ], timeout=10)
+    if ev is None:
+        raise Exception("Unknown connection result")
+    if "CTRL-EVENT-CONNECTED" in ev:
+        raise Exception("Unexpected connection")
+    if "status_code=82" not in ev:
+        raise Exception("Unexpected rejection reason: " + ev)
+    if "ie=34" not in ev:
+        raise Exception("No Neighbor Report element: " + ev)
+    dev[0].request("DISCONNECT")
+
+def test_ap_track_sta_no_auth_passive(dev, apdev):
+    """AP rejecting authentication from dualband STA on 2.4 GHz (passive)"""
+    try:
+        _test_ap_track_sta_no_auth_passive(dev, apdev)
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+
+def _test_ap_track_sta_no_auth_passive(dev, apdev):
+    dev[0].flush_scan_cache()
+
+    params = { "ssid": "track",
+               "country_code": "US",
+               "hw_mode": "g",
+               "channel": "6",
+               "no_auth_if_seen_on": apdev[1]['ifname'] }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    params = { "ssid": "track",
+               "country_code": "US",
+               "hw_mode": "a",
+               "channel": "40",
+               "interworking": "1",
+               "venue_name": "eng:Venue",
+               "track_sta_max_num": "100" }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
+    for i in range(10):
+        dev[0].request("SCAN freq=5200 passive=1")
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=5)
+        if ev is None:
+            raise Exception("Scan did not complete")
+        if dev[0].get_bss(bssid2):
+            break
+        if i == 9:
+            raise Exception("AP not found with passive scans")
+
+    if "OK" not in dev[0].request("ANQP_GET " + bssid2 + " 258"):
+        raise Exception("ANQP_GET command failed")
+    ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
+    if ev is None or "Venue Name" not in ev:
+        raise Exception("Did not receive Venue Name")
+
+    dev[0].connect("track", key_mgmt="NONE", scan_freq="2437",
+                   freq_list="2437", wait_connect=False)
+    ev = dev[0].wait_event([ "CTRL-EVENT-CONNECTED",
+                             "CTRL-EVENT-AUTH-REJECT" ], timeout=10)
+    if ev is None:
+        raise Exception("Unknown connection result")
+    if "CTRL-EVENT-CONNECTED" in ev:
+        raise Exception("Unexpected connection")
+    if "status_code=82" not in ev:
+        raise Exception("Unexpected rejection reason: " + ev)
+    dev[0].request("DISCONNECT")
+
+def test_ap_track_sta_force_5ghz(dev, apdev):
+    """Dualband AP forcing dualband STA to connect on 5 GHz"""
+    try:
+        _test_ap_track_sta_force_5ghz(dev, apdev)
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+
+def _test_ap_track_sta_force_5ghz(dev, apdev):
+    params = { "ssid": "track",
+               "country_code": "US",
+               "hw_mode": "g",
+               "channel": "6",
+               "no_probe_resp_if_seen_on": apdev[1]['ifname'],
+               "no_auth_if_seen_on": apdev[1]['ifname'] }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    params = { "ssid": "track",
+               "country_code": "US",
+               "hw_mode": "a",
+               "channel": "40",
+               "track_sta_max_num": "100" }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
+    dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
+
+    dev[0].connect("track", key_mgmt="NONE", scan_freq="2437 5200")
+    freq = dev[0].get_status_field('freq')
+    if freq != '5200':
+        raise Exception("Unexpected operating channel")
+    dev[0].request("DISCONNECT")
+
+def test_ap_track_sta_force_2ghz(dev, apdev):
+    """Dualband AP forcing dualband STA to connect on 2.4 GHz"""
+    try:
+        _test_ap_track_sta_force_2ghz(dev, apdev)
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+
+def _test_ap_track_sta_force_2ghz(dev, apdev):
+    params = { "ssid": "track",
+               "country_code": "US",
+               "hw_mode": "g",
+               "channel": "6",
+               "track_sta_max_num": "100" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    params = { "ssid": "track",
+               "country_code": "US",
+               "hw_mode": "a",
+               "channel": "40",
+               "no_probe_resp_if_seen_on": apdev[0]['ifname'],
+               "no_auth_if_seen_on": apdev[0]['ifname'] }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
+    dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
+
+    dev[0].connect("track", key_mgmt="NONE", scan_freq="2437 5200")
+    freq = dev[0].get_status_field('freq')
+    if freq != '2437':
+        raise Exception("Unexpected operating channel")
+    dev[0].request("DISCONNECT")
diff --git a/hostap/tests/hwsim/test_ap_vht.py b/hostap/tests/hwsim/test_ap_vht.py
new file mode 100644
index 0000000..d883114
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_vht.py
@@ -0,0 +1,537 @@
+# Test cases for VHT operations with hostapd
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+# Copyright (c) 2013, Intel Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import os
+import subprocess, time
+
+import hwsim_utils
+import hostapd
+from utils import HwsimSkip
+from test_dfs import wait_dfs_event
+from test_ap_csa import csa_supported
+
+def vht_supported():
+    cmd = subprocess.Popen(["iw", "reg", "get"], stdout=subprocess.PIPE)
+    reg = cmd.stdout.read()
+    if "@ 80)" in reg or "@ 160)" in reg:
+        return True
+    return False
+
+def test_ap_vht80(dev, apdev):
+    """VHT with 80 MHz channel width"""
+    try:
+        hapd = None
+        params = { "ssid": "vht",
+                   "country_code": "FI",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ht_capab": "[HT40+]",
+                   "ieee80211n": "1",
+                   "ieee80211ac": "1",
+                   "vht_oper_chwidth": "1",
+                   "vht_oper_centr_freq_seg0_idx": "42" }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        bssid = apdev[0]['bssid']
+
+        dev[0].connect("vht", key_mgmt="NONE", scan_freq="5180")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+        est = dev[0].get_bss(bssid)['est_throughput']
+        if est != "390001":
+            raise Exception("Unexpected BSS est_throughput: " + est)
+    except Exception, e:
+        if isinstance(e, Exception) and str(e) == "AP startup failed":
+            if not vht_supported():
+                raise HwsimSkip("80 MHz channel not supported in regulatory information")
+        raise
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def vht80_test(apdev, dev, channel, ht_capab):
+    try:
+        hapd = None
+        params = { "ssid": "vht",
+                   "country_code": "FI",
+                   "hw_mode": "a",
+                   "channel": str(channel),
+                   "ht_capab": ht_capab,
+                   "ieee80211n": "1",
+                   "ieee80211ac": "1",
+                   "vht_oper_chwidth": "1",
+                   "vht_oper_centr_freq_seg0_idx": "42" }
+        hapd = hostapd.add_ap(apdev['ifname'], params)
+        bssid = apdev['bssid']
+
+        dev.connect("vht", key_mgmt="NONE", scan_freq=str(5000 + 5 * channel))
+        hwsim_utils.test_connectivity(dev, hapd)
+    except Exception, e:
+        if isinstance(e, Exception) and str(e) == "AP startup failed":
+            if not vht_supported():
+                raise HwsimSkip("80 MHz channel not supported in regulatory information")
+        raise
+    finally:
+        dev.request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev.flush_scan_cache()
+
+def test_ap_vht80b(dev, apdev):
+    """VHT with 80 MHz channel width (HT40- channel 40)"""
+    vht80_test(apdev[0], dev[0], 40, "[HT40-]")
+
+def test_ap_vht80c(dev, apdev):
+    """VHT with 80 MHz channel width (HT40+ channel 44)"""
+    vht80_test(apdev[0], dev[0], 44, "[HT40+]")
+
+def test_ap_vht80d(dev, apdev):
+    """VHT with 80 MHz channel width (HT40- channel 48)"""
+    vht80_test(apdev[0], dev[0], 48, "[HT40-]")
+
+def test_ap_vht80_params(dev, apdev):
+    """VHT with 80 MHz channel width and number of optional features enabled"""
+    try:
+        hapd = None
+        params = { "ssid": "vht",
+                   "country_code": "FI",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ht_capab": "[HT40+][SHORT-GI-40][DSS_CCK-40]",
+                   "ieee80211n": "1",
+                   "ieee80211ac": "1",
+                   "vht_oper_chwidth": "1",
+                   "vht_capab": "[MAX-MPDU-11454][RXLDPC][SHORT-GI-80][TX-STBC-2BY1][RX-STBC-1][MAX-A-MPDU-LEN-EXP0]",
+                   "vht_oper_centr_freq_seg0_idx": "42",
+                   "require_vht": "1" }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+        dev[1].connect("vht", key_mgmt="NONE", scan_freq="5180",
+                       disable_vht="1", wait_connect=False)
+        dev[0].connect("vht", key_mgmt="NONE", scan_freq="5180")
+        ev = dev[1].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
+        if ev is None:
+            raise Exception("Association rejection timed out")
+        if "status_code=104" not in ev:
+            raise Exception("Unexpected rejection status code")
+        dev[1].request("DISCONNECT")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+    except Exception, e:
+        if isinstance(e, Exception) and str(e) == "AP startup failed":
+            if not vht_supported():
+                raise HwsimSkip("80 MHz channel not supported in regulatory information")
+        raise
+    finally:
+        dev[0].request("DISCONNECT")
+        dev[1].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+        dev[1].flush_scan_cache()
+
+def test_ap_vht_20(devs, apdevs):
+    """VHT and 20 MHz channel"""
+    dev = devs[0]
+    ap = apdevs[0]
+    try:
+        hapd = None
+        params = { "ssid": "test-vht20",
+                   "country_code": "DE",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ieee80211n": "1",
+                   "ieee80211ac": "1",
+                   "ht_capab": "",
+                   "vht_capab": "",
+                   "vht_oper_chwidth": "0",
+                   "vht_oper_centr_freq_seg0_idx": "0",
+                   "supported_rates": "60 120 240 360 480 540",
+                   "require_vht": "1",
+                 }
+        hapd = hostapd.add_ap(ap['ifname'], params)
+        dev.connect("test-vht20", scan_freq="5180", key_mgmt="NONE")
+        hwsim_utils.test_connectivity(dev, hapd)
+    finally:
+        dev.request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev.flush_scan_cache()
+
+def test_ap_vht_40(devs, apdevs):
+    """VHT and 40 MHz channel"""
+    dev = devs[0]
+    ap = apdevs[0]
+    try:
+        hapd = None
+        params = { "ssid": "test-vht40",
+                   "country_code": "DE",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ieee80211n": "1",
+                   "ieee80211ac": "1",
+                   "ht_capab": "[HT40+]",
+                   "vht_capab": "",
+                   "vht_oper_chwidth": "0",
+                   "vht_oper_centr_freq_seg0_idx": "0",
+                 }
+        hapd = hostapd.add_ap(ap['ifname'], params)
+        dev.connect("test-vht40", scan_freq="5180", key_mgmt="NONE")
+        hwsim_utils.test_connectivity(dev, hapd)
+    finally:
+        dev.request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev.flush_scan_cache()
+
+def test_ap_vht_capab_not_supported(dev, apdev):
+    """VHT configuration with driver not supporting all vht_capab entries"""
+    try:
+        params = { "ssid": "vht",
+                   "country_code": "FI",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ht_capab": "[HT40+][SHORT-GI-40][DSS_CCK-40]",
+                   "ieee80211n": "1",
+                   "ieee80211ac": "1",
+                   "vht_oper_chwidth": "1",
+                   "vht_capab": "[MAX-MPDU-7991][MAX-MPDU-11454][VHT160][VHT160-80PLUS80][RXLDPC][SHORT-GI-80][SHORT-GI-160][TX-STBC-2BY1][RX-STBC-1][RX-STBC-12][RX-STBC-123][RX-STBC-1234][SU-BEAMFORMER][SU-BEAMFORMEE][BF-ANTENNA-2][BF-ANTENNA-3][BF-ANTENNA-4][SOUNDING-DIMENSION-2][SOUNDING-DIMENSION-3][SOUNDING-DIMENSION-4][MU-BEAMFORMER][VHT-TXOP-PS][HTC-VHT][MAX-A-MPDU-LEN-EXP0][MAX-A-MPDU-LEN-EXP7][VHT-LINK-ADAPT2][VHT-LINK-ADAPT3][RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN]",
+                   "vht_oper_centr_freq_seg0_idx": "42",
+                   "require_vht": "1" }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+        ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
+        if ev is None:
+            raise Exception("Startup failure not reported")
+        for i in range(1, 7):
+            if "OK" not in hapd.request("SET vht_capab [MAX-A-MPDU-LEN-EXP%d]" % i):
+                raise Exception("Unexpected SET failure")
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+
+def test_ap_vht160(dev, apdev):
+    """VHT with 160 MHz channel width"""
+    try:
+        hapd = None
+        hapd2 = None
+        params = { "ssid": "vht",
+                   "country_code": "FI",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ht_capab": "[HT40+]",
+                   "ieee80211n": "1",
+                   "ieee80211ac": "1",
+                   "vht_oper_chwidth": "2",
+                   "vht_oper_centr_freq_seg0_idx": "50",
+                   'ieee80211d': '1',
+                   'ieee80211h': '1' }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+
+        ev = wait_dfs_event(hapd, "DFS-CAC-START", 5)
+        if "DFS-CAC-START" not in ev:
+            raise Exception("Unexpected DFS event")
+
+        state = hapd.get_status_field("state")
+        if state != "DFS":
+            if state == "DISABLED" and not os.path.exists("dfs"):
+                # Not all systems have recent enough CRDA version and
+                # wireless-regdb changes to support 160 MHz and DFS. For now,
+                # do not report failures for this test case.
+                raise HwsimSkip("CRDA or wireless-regdb did not support 160 MHz")
+            raise Exception("Unexpected interface state: " + state)
+
+        params = { "ssid": "vht2",
+                   "country_code": "FI",
+                   "hw_mode": "a",
+                   "channel": "104",
+                   "ht_capab": "[HT40-]",
+                   "ieee80211n": "1",
+                   "ieee80211ac": "1",
+                   "vht_oper_chwidth": "2",
+                   "vht_oper_centr_freq_seg0_idx": "114",
+                   'ieee80211d': '1',
+                   'ieee80211h': '1' }
+        hapd2 = hostapd.add_ap(apdev[1]['ifname'], params, wait_enabled=False)
+
+        ev = wait_dfs_event(hapd2, "DFS-CAC-START", 5)
+        if "DFS-CAC-START" not in ev:
+            raise Exception("Unexpected DFS event(2)")
+
+        state = hapd2.get_status_field("state")
+        if state != "DFS":
+            raise Exception("Unexpected interface state(2): " + state)
+
+        logger.info("Waiting for CAC to complete")
+
+        ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+        if "success=1" not in ev:
+            raise Exception("CAC failed")
+        if "freq=5180" not in ev:
+            raise Exception("Unexpected DFS freq result")
+
+        ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+        if not ev:
+            raise Exception("AP setup timed out")
+
+        state = hapd.get_status_field("state")
+        if state != "ENABLED":
+            raise Exception("Unexpected interface state")
+
+        ev = wait_dfs_event(hapd2, "DFS-CAC-COMPLETED", 70)
+        if "success=1" not in ev:
+            raise Exception("CAC failed(2)")
+        if "freq=5520" not in ev:
+            raise Exception("Unexpected DFS freq result(2)")
+
+        ev = hapd2.wait_event(["AP-ENABLED"], timeout=5)
+        if not ev:
+            raise Exception("AP setup timed out(2)")
+
+        state = hapd2.get_status_field("state")
+        if state != "ENABLED":
+            raise Exception("Unexpected interface state(2)")
+
+        freq = hapd2.get_status_field("freq")
+        if freq != "5520":
+            raise Exception("Unexpected frequency(2)")
+
+        dev[0].connect("vht", key_mgmt="NONE", scan_freq="5180")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+        sig = dev[0].request("SIGNAL_POLL").splitlines()
+        if "FREQUENCY=5180" not in sig:
+            raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+        if "WIDTH=160 MHz" not in sig:
+            raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+        dev[1].connect("vht2", key_mgmt="NONE", scan_freq="5520")
+        hwsim_utils.test_connectivity(dev[1], hapd2)
+        sig = dev[1].request("SIGNAL_POLL").splitlines()
+        if "FREQUENCY=5520" not in sig:
+            raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+        if "WIDTH=160 MHz" not in sig:
+            raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+    except Exception, e:
+        if isinstance(e, Exception) and str(e) == "AP startup failed":
+            if not vht_supported():
+                raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+        raise
+    finally:
+        dev[0].request("DISCONNECT")
+        dev[1].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        if hapd2:
+            hapd2.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+        dev[1].flush_scan_cache()
+
+def test_ap_vht80plus80(dev, apdev):
+    """VHT with 80+80 MHz channel width"""
+    try:
+        hapd = None
+        hapd2 = None
+        params = { "ssid": "vht",
+                   "country_code": "US",
+                   "hw_mode": "a",
+                   "channel": "52",
+                   "ht_capab": "[HT40+]",
+                   "ieee80211n": "1",
+                   "ieee80211ac": "1",
+                   "vht_oper_chwidth": "3",
+                   "vht_oper_centr_freq_seg0_idx": "58",
+                   "vht_oper_centr_freq_seg1_idx": "155",
+                   'ieee80211d': '1',
+                   'ieee80211h': '1' }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params, wait_enabled=False)
+        # This will actually fail since DFS on 80+80 is not yet supported
+        ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
+        # ignore result to avoid breaking the test once 80+80 DFS gets enabled
+
+        params = { "ssid": "vht2",
+                   "country_code": "US",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ht_capab": "[HT40+]",
+                   "ieee80211n": "1",
+                   "ieee80211ac": "1",
+                   "vht_oper_chwidth": "3",
+                   "vht_oper_centr_freq_seg0_idx": "42",
+                   "vht_oper_centr_freq_seg1_idx": "155" }
+        hapd2 = hostapd.add_ap(apdev[1]['ifname'], params, wait_enabled=False)
+
+        ev = hapd2.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=5)
+        if not ev:
+            raise Exception("AP setup timed out(2)")
+        if "AP-DISABLED" in ev:
+            # Assume this failed due to missing regulatory update for now
+            raise HwsimSkip("80+80 MHz channel not supported in regulatory information")
+
+        state = hapd2.get_status_field("state")
+        if state != "ENABLED":
+            raise Exception("Unexpected interface state(2)")
+
+        dev[1].connect("vht2", key_mgmt="NONE", scan_freq="5180")
+        hwsim_utils.test_connectivity(dev[1], hapd2)
+        sig = dev[1].request("SIGNAL_POLL").splitlines()
+        if "FREQUENCY=5180" not in sig:
+            raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+        if "WIDTH=80+80 MHz" not in sig:
+            raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+        if "CENTER_FRQ1=5210" not in sig:
+            raise Exception("Unexpected SIGNAL_POLL value(3): " + str(sig))
+        if "CENTER_FRQ2=5775" not in sig:
+            raise Exception("Unexpected SIGNAL_POLL value(4): " + str(sig))
+    except Exception, e:
+        if isinstance(e, Exception) and str(e) == "AP startup failed":
+            if not vht_supported():
+                raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+        raise
+    finally:
+        dev[0].request("DISCONNECT")
+        dev[1].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        if hapd2:
+            hapd2.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+        dev[1].flush_scan_cache()
+
+def test_ap_vht80_csa(dev, apdev):
+    """VHT with 80 MHz channel width and CSA"""
+    csa_supported(dev[0])
+    try:
+        hapd = None
+        params = { "ssid": "vht",
+                   "country_code": "US",
+                   "hw_mode": "a",
+                   "channel": "149",
+                   "ht_capab": "[HT40+]",
+                   "ieee80211n": "1",
+                   "ieee80211ac": "1",
+                   "vht_oper_chwidth": "1",
+                   "vht_oper_centr_freq_seg0_idx": "155" }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+        dev[0].connect("vht", key_mgmt="NONE", scan_freq="5745")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+
+        hapd.request("CHAN_SWITCH 5 5180 ht vht blocktx center_freq1=5210 sec_channel_offset=1 bandwidth=80")
+        ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+        if ev is None:
+            raise Exception("CSA finished event timed out")
+        if "freq=5180" not in ev:
+            raise Exception("Unexpected channel in CSA finished event")
+        time.sleep(0.5)
+        hwsim_utils.test_connectivity(dev[0], hapd)
+
+        hapd.request("CHAN_SWITCH 5 5745")
+        ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+        if ev is None:
+            raise Exception("CSA finished event timed out")
+        if "freq=5745" not in ev:
+            raise Exception("Unexpected channel in CSA finished event")
+        time.sleep(0.5)
+        hwsim_utils.test_connectivity(dev[0], hapd)
+
+        # This CSA to same channel will fail in kernel, so use this only for
+        # extra code coverage.
+        hapd.request("CHAN_SWITCH 5 5745")
+        hapd.wait_event(["AP-CSA-FINISHED"], timeout=1)
+    except Exception, e:
+        if isinstance(e, Exception) and str(e) == "AP startup failed":
+            if not vht_supported():
+                raise HwsimSkip("80 MHz channel not supported in regulatory information")
+        raise
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_ap_vht_on_24ghz(dev, apdev):
+    """Subset of VHT features on 2.4 GHz"""
+    hapd = None
+    params = { "ssid": "test-vht-2g",
+               "hw_mode": "g",
+               "channel": "1",
+               "ieee80211n": "1",
+               "vendor_vht": "1",
+               "vht_capab": "[MAX-MPDU-11454]",
+               "vht_oper_chwidth": "0",
+               "vht_oper_centr_freq_seg0_idx": "1"
+    }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    try:
+        if "OK" not in dev[0].request("VENDOR_ELEM_ADD 13 dd1300904c0400bf0c3240820feaff0000eaff0000"):
+            raise Exception("Failed to add vendor element")
+        dev[0].connect("test-vht-2g", scan_freq="2412", key_mgmt="NONE")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+        sta = hapd.get_sta(dev[0].own_addr())
+        if '[VENDOR_VHT]' not in sta['flags']:
+            raise Exception("No VENDOR_VHT STA flag")
+
+        dev[1].connect("test-vht-2g", scan_freq="2412", key_mgmt="NONE")
+        sta = hapd.get_sta(dev[1].own_addr())
+        if '[VENDOR_VHT]' in sta['flags']:
+            raise Exception("Unexpected VENDOR_VHT STA flag")
+    finally:
+        dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+def test_prefer_vht40(dev, apdev):
+    """Preference on VHT40 over HT40"""
+    try:
+        hapd2 = None
+
+        params = { "ssid": "test",
+                   "country_code": "FI",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ieee80211n": "1",
+                   "ht_capab": "[HT40+]" }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        bssid = apdev[0]['bssid']
+
+        params = { "ssid": "test",
+                   "country_code": "FI",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "ieee80211n": "1",
+                   "ieee80211ac": "1",
+                   "ht_capab": "[HT40+]",
+                   "vht_capab": "",
+                   "vht_oper_chwidth": "0",
+                   "vht_oper_centr_freq_seg0_idx": "0",
+                 }
+        hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+        bssid2 = apdev[1]['bssid']
+
+        dev[0].scan_for_bss(bssid, freq=5180)
+        dev[0].scan_for_bss(bssid2, freq=5180)
+        dev[0].connect("test", scan_freq="5180", key_mgmt="NONE")
+        if dev[0].get_status_field('bssid') != bssid2:
+            raise Exception("Unexpected BSS selected")
+
+        est = dev[0].get_bss(bssid)['est_throughput']
+        if est != "135000":
+            raise Exception("Unexpected BSS0 est_throughput: " + est)
+
+        est = dev[0].get_bss(bssid2)['est_throughput']
+        if est != "135001":
+            raise Exception("Unexpected BSS1 est_throughput: " + est)
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd2:
+            hapd2.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
diff --git a/hostap/tests/hwsim/test_ap_vlan.py b/hostap/tests/hwsim/test_ap_vlan.py
new file mode 100644
index 0000000..822bf99
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_vlan.py
@@ -0,0 +1,384 @@
+#!/usr/bin/python
+#
+# Test cases for AP VLAN
+# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import subprocess
+import logging
+logger = logging.getLogger(__name__)
+
+try:
+    import netifaces
+    netifaces_imported = True
+except ImportError:
+    netifaces_imported = False
+
+import hwsim_utils
+import hostapd
+from utils import iface_is_in_bridge, HwsimSkip
+
+def test_ap_vlan_open(dev, apdev):
+    """AP VLAN with open network"""
+    params = { "ssid": "test-vlan-open",
+               "dynamic_vlan": "1",
+               "accept_mac_file": "hostapd.accept" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+    dev[1].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+    dev[2].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+    hwsim_utils.test_connectivity_iface(dev[1], hapd, "brvlan2")
+    hwsim_utils.test_connectivity(dev[2], hapd)
+
+def test_ap_vlan_file_open(dev, apdev):
+    """AP VLAN with open network and vlan_file mapping"""
+    params = { "ssid": "test-vlan-open",
+               "dynamic_vlan": "1",
+               "vlan_file": "hostapd.vlan",
+               "accept_mac_file": "hostapd.accept" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+    dev[1].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+    dev[2].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+    hwsim_utils.test_connectivity_iface(dev[1], hapd, "brvlan2")
+    hwsim_utils.test_connectivity(dev[2], hapd)
+
+def test_ap_vlan_wpa2(dev, apdev):
+    """AP VLAN with WPA2-PSK"""
+    params = hostapd.wpa2_params(ssid="test-vlan",
+                                 passphrase="12345678")
+    params['dynamic_vlan'] = "1";
+    params['accept_mac_file'] = "hostapd.accept";
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-vlan", psk="12345678", scan_freq="2412")
+    dev[1].connect("test-vlan", psk="12345678", scan_freq="2412")
+    dev[2].connect("test-vlan", psk="12345678", scan_freq="2412")
+    hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+    hwsim_utils.test_connectivity_iface(dev[1], hapd, "brvlan2")
+    hwsim_utils.test_connectivity(dev[2], hapd)
+
+def test_ap_vlan_wpa2_radius(dev, apdev):
+    """AP VLAN with WPA2-Enterprise and RADIUS attributes"""
+    params = hostapd.wpa2_eap_params(ssid="test-vlan")
+    params['dynamic_vlan'] = "1";
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+                   identity="vlan1",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412")
+    dev[1].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+                   identity="vlan2",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412")
+    dev[2].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+                   identity="pax.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+    hwsim_utils.test_connectivity_iface(dev[1], hapd, "brvlan2")
+    hwsim_utils.test_connectivity(dev[2], hapd)
+
+def test_ap_vlan_wpa2_radius_id_change(dev, apdev):
+    """AP VLAN with WPA2-Enterprise and RADIUS attributes changing VLANID"""
+    as_params = { "ssid": "as",
+                  "beacon_int": "2000",
+                  "radius_server_clients": "auth_serv/radius_clients.conf",
+                  "radius_server_auth_port": '18128',
+                  "eap_server": "1",
+                  "eap_user_file": "auth_serv/eap_user.conf",
+                  "ca_cert": "auth_serv/ca.pem",
+                  "server_cert": "auth_serv/server.pem",
+                  "private_key": "auth_serv/server.key" }
+    authserv = hostapd.add_ap(apdev[1]['ifname'], as_params)
+
+    params = hostapd.wpa2_eap_params(ssid="test-vlan")
+    params['dynamic_vlan'] = "1";
+    params['auth_server_port'] = "18128"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+                   identity="vlan1",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+
+    logger.info("VLAN-ID -> 2")
+
+    authserv.disable()
+    authserv.set('eap_user_file', "auth_serv/eap_user_vlan.conf")
+    authserv.enable()
+
+    dev[0].dump_monitor()
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("EAP reauthentication timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=5)
+    if ev is None:
+        raise Exception("4-way handshake after reauthentication timed out")
+    state = dev[0].get_status_field('wpa_state')
+    if state != "COMPLETED":
+        raise Exception("Unexpected state after reauth: " + state)
+    sta = hapd.get_sta(dev[0].own_addr())
+    if 'vlan_id' not in sta:
+        raise Exception("No VLAN ID in STA info")
+    if sta['vlan_id'] != '2':
+        raise Exception("Unexpected VLAN ID: " + sta['vlan_id'])
+    hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan2")
+
+    logger.info("VLAN-ID -> 1")
+    time.sleep(1)
+
+    authserv.disable()
+    authserv.set('eap_user_file', "auth_serv/eap_user.conf")
+    authserv.enable()
+
+    dev[0].dump_monitor()
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("EAP reauthentication timed out")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=5)
+    if ev is None:
+        raise Exception("4-way handshake after reauthentication timed out")
+    state = dev[0].get_status_field('wpa_state')
+    if state != "COMPLETED":
+        raise Exception("Unexpected state after reauth: " + state)
+    sta = hapd.get_sta(dev[0].own_addr())
+    if 'vlan_id' not in sta:
+        raise Exception("No VLAN ID in STA info")
+    if sta['vlan_id'] != '1':
+        raise Exception("Unexpected VLAN ID: " + sta['vlan_id'])
+    time.sleep(0.2)
+    try:
+        hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+    except Exception, e:
+        # It is possible for new bridge setup to not be ready immediately, so
+        # try again to avoid reporting issues related to that.
+        logger.info("First VLAN-ID 1 data test failed - try again")
+        hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+
+def test_ap_vlan_wpa2_radius_required(dev, apdev):
+    """AP VLAN with WPA2-Enterprise and RADIUS attributes required"""
+    params = hostapd.wpa2_eap_params(ssid="test-vlan")
+    params['dynamic_vlan'] = "2";
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+                   identity="vlan1",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412")
+    dev[2].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+                   identity="pax.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412", wait_connect=False)
+    ev = dev[2].wait_event(["CTRL-EVENT-CONNECTED",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=20)
+    if ev is None:
+        raise Exception("Timeout on connection attempt")
+    if "CTRL-EVENT-CONNECTED" in ev:
+        raise Exception("Unexpected success without tunnel parameters")
+
+def test_ap_vlan_tagged(dev, apdev):
+    """AP VLAN with tagged interface"""
+    params = { "ssid": "test-vlan-open",
+               "dynamic_vlan": "1",
+               "vlan_tagged_interface": "lo",
+               "accept_mac_file": "hostapd.accept" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+    dev[1].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+    dev[2].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity_iface(dev[0], hapd, "brlo.1")
+    hwsim_utils.test_connectivity_iface(dev[1], hapd, "brlo.2")
+    hwsim_utils.test_connectivity(dev[2], hapd)
+
+def ap_vlan_iface_cleanup_multibss_cleanup():
+    subprocess.call(['ifconfig', 'dummy0', 'down'],
+                    stderr=open('/dev/null', 'w'))
+    ifnames = [ 'wlan3.1', 'wlan3.2', 'wlan3-2.1', 'wlan3-2.2', 'dummy0.2',
+                'dummy0.1', 'dummy0', 'brvlan1', 'brvlan2' ]
+    for ifname in ifnames:
+        subprocess.call(['ip', 'link', 'del', ifname],
+                        stderr=open('/dev/null', 'w'))
+
+def ap_vlan_iface_test_and_prepare_environ():
+    ifaces = netifaces.interfaces()
+    if "dummy0" in ifaces:
+        raise Exception("dummy0 already exists before")
+    ifaces = netifaces.interfaces()
+    if "dummy0.1" in ifaces:
+        raise Exception("dummy0.1 already exists before")
+
+    subprocess.call(['ip', 'link', 'add', 'dummy0', 'type', 'dummy'])
+    subprocess.call(['ifconfig', 'dummy0', 'up'])
+
+    ifaces = netifaces.interfaces()
+    if not("dummy0" in ifaces):
+        raise HwsimSkip("failed to add dummy0 - missing kernel config DUMMY ?")
+
+    subprocess.call(['ip', 'link', 'add', 'link', 'dummy0', 'name', 'dummy0.1',
+                     'type', 'vlan', 'id', '1'])
+
+    ifaces = netifaces.interfaces()
+    if not("dummy0.1" in ifaces):
+        raise HwsimSkip("failed to add dummy0.1 - missing kernel config VLAN_8021Q ?")
+
+    subprocess.call(['ip', 'link', 'del', 'dummy0.1'])
+
+    ifaces = netifaces.interfaces()
+    if "dummy0.1" in ifaces:
+        raise Exception("dummy0.1 was not removed before testing")
+
+def test_ap_vlan_iface_cleanup_multibss(dev, apdev):
+    """AP VLAN operation in multi-BSS multi-VLAN case"""
+
+    # AP VLAN with WPA2-Enterprise and RADIUS attributes changing VLANID
+    # check that multiple bss do not interfere with each other with respect
+    # to deletion of bridge and tagged interface.
+
+    if not netifaces_imported:
+        raise HwsimSkip("python module netifaces not available")
+
+    try:
+        ap_vlan_iface_cleanup_multibss_cleanup()
+        ap_vlan_iface_test_and_prepare_environ()
+
+        as_params = { "ssid": "as",
+                      "beacon_int": "2000",
+                      "radius_server_clients": "auth_serv/radius_clients.conf",
+                      "radius_server_auth_port": '18128',
+                      "eap_server": "1",
+                      "eap_user_file": "auth_serv/eap_user.conf",
+                      "ca_cert": "auth_serv/ca.pem",
+                      "server_cert": "auth_serv/server.pem",
+                      "private_key": "auth_serv/server.key",
+                      "vlan_naming": "1" }
+        authserv = hostapd.add_ap(apdev[1]['ifname'], as_params)
+
+        ifname = apdev[0]['ifname']
+
+        # start the actual test
+        hostapd.add_iface(ifname, 'multi-bss-iface.conf')
+        hapd = hostapd.Hostapd(ifname)
+        hapd1 = hostapd.Hostapd("wlan3-2", 1)
+        hapd1.enable()
+
+        ifaces = netifaces.interfaces()
+        if "brvlan1" in ifaces:
+            raise Exception("bridge brvlan1 already exists before")
+        if "brvlan2" in ifaces:
+            raise Exception("bridge brvlan2 already exists before")
+
+        dev[0].connect("bss-1", key_mgmt="WPA-EAP", eap="PAX",
+                       identity="vlan1",
+                       password_hex="0123456789abcdef0123456789abcdef",
+                       scan_freq="2412")
+
+        ifaces = netifaces.interfaces()
+        if not("brvlan1" in ifaces):
+            raise Exception("bridge brvlan1 was not created")
+
+        hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+        if not iface_is_in_bridge("brvlan1", "dummy0.1"):
+            raise Exception("dummy0.1 not in brvlan1")
+
+        dev[1].connect("bss-2", key_mgmt="WPA-EAP", eap="PAX",
+                       identity="vlan1",
+                       password_hex="0123456789abcdef0123456789abcdef",
+                       scan_freq="2412")
+
+        hwsim_utils.test_connectivity_iface(dev[1], hapd1, "brvlan1")
+        if not iface_is_in_bridge("brvlan1", "dummy0.1"):
+            raise Exception("dummy0.1 not in brvlan1")
+
+        authserv.disable()
+        authserv.set('eap_user_file', "auth_serv/eap_user_vlan.conf")
+        authserv.enable()
+
+        logger.info("wlan0 -> VLAN 2")
+
+        dev[0].dump_monitor()
+        dev[0].request("REAUTHENTICATE")
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+        if ev is None:
+            raise Exception("EAP reauthentication timed out")
+        ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=5)
+        if ev is None:
+            raise Exception("4-way handshake after reauthentication timed out")
+        state = dev[0].get_status_field('wpa_state')
+        if state != "COMPLETED":
+            raise Exception("Unexpected state after reauth: " + state)
+
+        ifaces = netifaces.interfaces()
+        if not ("brvlan1" in ifaces):
+            raise Exception("bridge brvlan1 has been removed too early")
+
+        hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan2",
+                                            max_tries=5)
+
+        if not iface_is_in_bridge("brvlan2", "dummy0.2"):
+            raise Exception("dummy0.2 not in brvlan2")
+
+        logger.info("test wlan1 == VLAN 1")
+        hwsim_utils.test_connectivity_iface(dev[1], hapd1, "brvlan1")
+        if not iface_is_in_bridge("brvlan1", "dummy0.1"):
+            raise Exception("dummy0.1 not in brvlan1")
+
+        logger.info("wlan1 -> VLAN 2")
+
+        dev[1].dump_monitor()
+        dev[1].request("REAUTHENTICATE")
+        ev = dev[1].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+        if ev is None:
+            raise Exception("EAP reauthentication timed out")
+        ev = dev[1].wait_event(["WPA: Key negotiation completed"], timeout=5)
+        if ev is None:
+            raise Exception("4-way handshake after reauthentication timed out")
+        state = dev[1].get_status_field('wpa_state')
+        if state != "COMPLETED":
+            raise Exception("Unexpected state after reauth: " + state)
+
+        # it can take some time for data connectivity to be updated
+        hwsim_utils.test_connectivity_iface(dev[1], hapd1, "brvlan2",
+                                            max_tries=5)
+        logger.info("test wlan0 == VLAN 2")
+        hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan2")
+
+        if not iface_is_in_bridge("brvlan2", "dummy0.2"):
+            raise Exception("dummy0.2 not in brvlan2")
+
+        ifaces = netifaces.interfaces()
+        if "brvlan1" in ifaces:
+            raise Exception("bridge brvlan1 has not been cleaned up")
+
+        # disconnect dev0 first to test a corner case
+        dev[0].request("DISCONNECT")
+        dev[0].wait_disconnected()
+        dev[1].request("DISCONNECT")
+        dev[1].wait_disconnected()
+
+        # station removal needs some time
+        for i in range(5):
+            time.sleep(1)
+            ifaces = netifaces.interfaces()
+            if "brvlan2" not in ifaces:
+                break
+
+        ifaces = netifaces.interfaces()
+        if "brvlan2" in ifaces:
+            raise Exception("bridge brvlan2 has not been cleaned up")
+
+        hapd.request("DISABLE")
+    finally:
+        ap_vlan_iface_cleanup_multibss_cleanup()
diff --git a/hostap/tests/hwsim/test_ap_wps.py b/hostap/tests/hwsim/test_ap_wps.py
new file mode 100644
index 0000000..1561792
--- /dev/null
+++ b/hostap/tests/hwsim/test_ap_wps.py
@@ -0,0 +1,9066 @@
+# WPS tests
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import base64
+import binascii
+from Crypto.Cipher import AES
+import hashlib
+import hmac
+import os
+import time
+import stat
+import subprocess
+import logging
+logger = logging.getLogger()
+import re
+import socket
+import struct
+import httplib
+import urlparse
+import urllib
+import xml.etree.ElementTree as ET
+import StringIO
+import SocketServer
+
+import hwsim_utils
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import HwsimSkip, alloc_fail, fail_test, skip_with_fips
+
+def wps_start_ap(apdev, ssid="test-wps-conf"):
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP" }
+    return hostapd.add_ap(apdev['ifname'], params)
+
+def test_ap_wps_init(dev, apdev):
+    """Initial AP configuration with first WPS Enrollee"""
+    ssid = "test-wps"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step")
+    hapd.request("WPS_PBC")
+    if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
+        raise Exception("PBC status not shown correctly")
+
+    id = dev[0].add_network()
+    dev[0].set_network_quoted(id, "ssid", "home")
+    dev[0].set_network_quoted(id, "psk", "12345678")
+    dev[0].request("ENABLE_NETWORK %s no-connect" % id)
+
+    id = dev[0].add_network()
+    dev[0].set_network_quoted(id, "ssid", "home2")
+    dev[0].set_network(id, "bssid", "00:11:22:33:44:55")
+    dev[0].set_network(id, "key_mgmt", "NONE")
+    dev[0].request("ENABLE_NETWORK %s no-connect" % id)
+
+    dev[0].request("WPS_PBC")
+    dev[0].wait_connected(timeout=30)
+    status = dev[0].get_status()
+    if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+        raise Exception("Not fully connected")
+    if status['ssid'] != ssid:
+        raise Exception("Unexpected SSID")
+    if status['pairwise_cipher'] != 'CCMP':
+        raise Exception("Unexpected encryption configuration")
+    if status['key_mgmt'] != 'WPA2-PSK':
+        raise Exception("Unexpected key_mgmt")
+
+    status = hapd.request("WPS_GET_STATUS")
+    if "PBC Status: Disabled" not in status:
+        raise Exception("PBC status not shown correctly")
+    if "Last WPS result: Success" not in status:
+        raise Exception("Last WPS result not shown correctly")
+    if "Peer Address: " + dev[0].p2p_interface_addr() not in status:
+        raise Exception("Peer address not shown correctly")
+    conf = hapd.request("GET_CONFIG")
+    if "wps_state=configured" not in conf:
+        raise Exception("AP not in WPS configured state")
+    if "wpa=3" not in conf:
+        raise Exception("AP not in WPA+WPA2 configuration")
+    if "rsn_pairwise_cipher=CCMP TKIP" not in conf:
+        raise Exception("Unexpected rsn_pairwise_cipher")
+    if "wpa_pairwise_cipher=CCMP TKIP" not in conf:
+        raise Exception("Unexpected wpa_pairwise_cipher")
+    if "group_cipher=TKIP" not in conf:
+        raise Exception("Unexpected group_cipher")
+
+    if len(dev[0].list_networks()) != 3:
+        raise Exception("Unexpected number of network blocks")
+
+def test_ap_wps_init_2ap_pbc(dev, apdev):
+    """Initial two-radio AP configuration with first WPS PBC Enrollee"""
+    ssid = "test-wps"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "1" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step")
+    hapd.request("WPS_PBC")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+    dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    if "[WPS-PBC]" not in bss['flags']:
+        raise Exception("WPS-PBC flag missing from AP1")
+    bss = dev[0].get_bss(apdev[1]['bssid'])
+    if "[WPS-PBC]" not in bss['flags']:
+        raise Exception("WPS-PBC flag missing from AP2")
+    dev[0].dump_monitor()
+    dev[0].request("SET wps_cred_processing 2")
+    dev[0].request("WPS_PBC")
+    ev = dev[0].wait_event(["WPS-CRED-RECEIVED"], timeout=30)
+    dev[0].request("SET wps_cred_processing 0")
+    if ev is None:
+        raise Exception("WPS cred event not seen")
+    if "100e" not in ev:
+        raise Exception("WPS attributes not included in the cred event")
+    dev[0].wait_connected(timeout=30)
+
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+    dev[1].scan_for_bss(apdev[1]['bssid'], freq="2412")
+    bss = dev[1].get_bss(apdev[0]['bssid'])
+    if "[WPS-PBC]" in bss['flags']:
+        raise Exception("WPS-PBC flag not cleared from AP1")
+    bss = dev[1].get_bss(apdev[1]['bssid'])
+    if "[WPS-PBC]" in bss['flags']:
+        raise Exception("WPS-PBC flag not cleared from AP2")
+
+def test_ap_wps_init_2ap_pin(dev, apdev):
+    """Initial two-radio AP configuration with first WPS PIN Enrollee"""
+    ssid = "test-wps"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "1" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step")
+    pin = dev[0].wps_read_pin()
+    hapd.request("WPS_PIN any " + pin)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+    dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    if "[WPS-AUTH]" not in bss['flags']:
+        raise Exception("WPS-AUTH flag missing from AP1")
+    bss = dev[0].get_bss(apdev[1]['bssid'])
+    if "[WPS-AUTH]" not in bss['flags']:
+        raise Exception("WPS-AUTH flag missing from AP2")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_PIN any " + pin)
+    dev[0].wait_connected(timeout=30)
+
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+    dev[1].scan_for_bss(apdev[1]['bssid'], freq="2412")
+    bss = dev[1].get_bss(apdev[0]['bssid'])
+    if "[WPS-AUTH]" in bss['flags']:
+        raise Exception("WPS-AUTH flag not cleared from AP1")
+    bss = dev[1].get_bss(apdev[1]['bssid'])
+    if "[WPS-AUTH]" in bss['flags']:
+        raise Exception("WPS-AUTH flag not cleared from AP2")
+
+def test_ap_wps_init_through_wps_config(dev, apdev):
+    """Initial AP configuration using wps_config command"""
+    ssid = "test-wps-init-config"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    if "FAIL" in hapd.request("WPS_CONFIG " + ssid.encode("hex") + " WPA2PSK CCMP " + "12345678".encode("hex")):
+        raise Exception("WPS_CONFIG command failed")
+    ev = hapd.wait_event(["WPS-NEW-AP-SETTINGS"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on WPS-NEW-AP-SETTINGS events")
+    # It takes some time for the AP to update Beacon and Probe Response frames,
+    # so wait here before requesting the scan to be started to avoid adding
+    # extra five second wait to the test due to fetching obsolete scan results.
+    hapd.ping()
+    time.sleep(0.2)
+    dev[0].connect(ssid, psk="12345678", scan_freq="2412", proto="WPA2",
+                   pairwise="CCMP", group="CCMP")
+
+def test_ap_wps_init_through_wps_config_2(dev, apdev):
+    """AP configuration using wps_config and wps_cred_processing=2"""
+    ssid = "test-wps-init-config"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1",
+                     "wps_cred_processing": "2" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    if "FAIL" in hapd.request("WPS_CONFIG " + ssid.encode("hex") + " WPA2PSK CCMP " + "12345678".encode("hex")):
+        raise Exception("WPS_CONFIG command failed")
+    ev = hapd.wait_event(["WPS-NEW-AP-SETTINGS"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on WPS-NEW-AP-SETTINGS events")
+    if "100e" not in ev:
+        raise Exception("WPS-NEW-AP-SETTINGS did not include Credential")
+
+def test_ap_wps_invalid_wps_config_passphrase(dev, apdev):
+    """AP configuration using wps_config command with invalid passphrase"""
+    ssid = "test-wps-init-config"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    if "FAIL" not in hapd.request("WPS_CONFIG " + ssid.encode("hex") + " WPA2PSK CCMP " + "1234567".encode("hex")):
+        raise Exception("Invalid WPS_CONFIG command accepted")
+
+def test_ap_wps_conf(dev, apdev):
+    """WPS PBC provisioning with configured AP"""
+    ssid = "test-wps-conf"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step")
+    hapd.request("WPS_PBC")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+    dev[0].wait_connected(timeout=30)
+    status = dev[0].get_status()
+    if status['wpa_state'] != 'COMPLETED':
+        raise Exception("Not fully connected")
+    if status['bssid'] != apdev[0]['bssid']:
+        raise Exception("Unexpected BSSID")
+    if status['ssid'] != ssid:
+        raise Exception("Unexpected SSID")
+    if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
+        raise Exception("Unexpected encryption configuration")
+    if status['key_mgmt'] != 'WPA2-PSK':
+        raise Exception("Unexpected key_mgmt")
+
+    sta = hapd.get_sta(dev[0].p2p_interface_addr())
+    if 'wpsDeviceName' not in sta or sta['wpsDeviceName'] != "Device A":
+        raise Exception("Device name not available in STA command")
+
+def test_ap_wps_conf_5ghz(dev, apdev):
+    """WPS PBC provisioning with configured AP on 5 GHz band"""
+    try:
+        hapd = None
+        ssid = "test-wps-conf"
+        params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                   "wpa_passphrase": "12345678", "wpa": "2",
+                   "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                   "country_code": "FI", "hw_mode": "a", "channel": "36" }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        logger.info("WPS provisioning step")
+        hapd.request("WPS_PBC")
+        dev[0].scan_for_bss(apdev[0]['bssid'], freq="5180")
+        dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+        dev[0].wait_connected(timeout=30)
+
+        sta = hapd.get_sta(dev[0].p2p_interface_addr())
+        if 'wpsDeviceName' not in sta or sta['wpsDeviceName'] != "Device A":
+            raise Exception("Device name not available in STA command")
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_ap_wps_conf_chan14(dev, apdev):
+    """WPS PBC provisioning with configured AP on channel 14"""
+    try:
+        hapd = None
+        ssid = "test-wps-conf"
+        params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                   "wpa_passphrase": "12345678", "wpa": "2",
+                   "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                   "country_code": "JP", "hw_mode": "b", "channel": "14" }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        logger.info("WPS provisioning step")
+        hapd.request("WPS_PBC")
+        dev[0].request("WPS_PBC")
+        dev[0].wait_connected(timeout=30)
+
+        sta = hapd.get_sta(dev[0].p2p_interface_addr())
+        if 'wpsDeviceName' not in sta or sta['wpsDeviceName'] != "Device A":
+            raise Exception("Device name not available in STA command")
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_ap_wps_twice(dev, apdev):
+    """WPS provisioning with twice to change passphrase"""
+    ssid = "test-wps-twice"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step")
+    hapd.request("WPS_PBC")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+    dev[0].wait_connected(timeout=30)
+    dev[0].request("DISCONNECT")
+
+    logger.info("Restart AP with different passphrase and re-run WPS")
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.remove(apdev[0]['ifname'])
+    params['wpa_passphrase'] = 'another passphrase'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step")
+    hapd.request("WPS_PBC")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+    dev[0].wait_connected(timeout=30)
+    networks = dev[0].list_networks()
+    if len(networks) > 1:
+        raise Exception("Unexpected duplicated network block present")
+
+def test_ap_wps_incorrect_pin(dev, apdev):
+    """WPS PIN provisioning with incorrect PIN"""
+    ssid = "test-wps-incorrect-pin"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    logger.info("WPS provisioning attempt 1")
+    hapd.request("WPS_PIN any 12345670")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_PIN %s 55554444" % apdev[0]['bssid'])
+    ev = dev[0].wait_event(["WPS-FAIL"], timeout=30)
+    if ev is None:
+        raise Exception("WPS operation timed out")
+    if "config_error=18" not in ev:
+        raise Exception("Incorrect config_error reported")
+    if "msg=8" not in ev:
+        raise Exception("PIN error detected on incorrect message")
+    dev[0].wait_disconnected(timeout=10)
+    dev[0].request("WPS_CANCEL")
+    # if a scan was in progress, wait for it to complete before trying WPS again
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+
+    status = hapd.request("WPS_GET_STATUS")
+    if "Last WPS result: Failed" not in status:
+        raise Exception("WPS failure result not shown correctly")
+
+    logger.info("WPS provisioning attempt 2")
+    hapd.request("WPS_PIN any 12345670")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_PIN %s 12344444" % apdev[0]['bssid'])
+    ev = dev[0].wait_event(["WPS-FAIL"], timeout=30)
+    if ev is None:
+        raise Exception("WPS operation timed out")
+    if "config_error=18" not in ev:
+        raise Exception("Incorrect config_error reported")
+    if "msg=10" not in ev:
+        raise Exception("PIN error detected on incorrect message")
+    dev[0].wait_disconnected(timeout=10)
+
+def test_ap_wps_conf_pin(dev, apdev):
+    """WPS PIN provisioning with configured AP"""
+    ssid = "test-wps-conf-pin"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step")
+    pin = dev[0].wps_read_pin()
+    hapd.request("WPS_PIN any " + pin)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    dev[0].wait_connected(timeout=30)
+    status = dev[0].get_status()
+    if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+        raise Exception("Not fully connected")
+    if status['ssid'] != ssid:
+        raise Exception("Unexpected SSID")
+    if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
+        raise Exception("Unexpected encryption configuration")
+    if status['key_mgmt'] != 'WPA2-PSK':
+        raise Exception("Unexpected key_mgmt")
+
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+    bss = dev[1].get_bss(apdev[0]['bssid'])
+    if "[WPS-AUTH]" in bss['flags']:
+        raise Exception("WPS-AUTH flag not cleared")
+    logger.info("Try to connect from another station using the same PIN")
+    pin = dev[1].request("WPS_PIN " + apdev[0]['bssid'])
+    ev = dev[1].wait_event(["WPS-M2D","CTRL-EVENT-CONNECTED"], timeout=30)
+    if ev is None:
+        raise Exception("Operation timed out")
+    if "WPS-M2D" not in ev:
+        raise Exception("Unexpected WPS operation started")
+    hapd.request("WPS_PIN any " + pin)
+    dev[1].wait_connected(timeout=30)
+
+def test_ap_wps_conf_pin_v1(dev, apdev):
+    """WPS PIN provisioning with configured WPS v1.0 AP"""
+    ssid = "test-wps-conf-pin-v1"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step")
+    pin = dev[0].wps_read_pin()
+    hapd.request("SET wps_version_number 0x10")
+    hapd.request("WPS_PIN any " + pin)
+    found = False
+    for i in range(0, 10):
+        dev[0].scan(freq="2412")
+        if "[WPS-PIN]" in dev[0].request("SCAN_RESULTS"):
+            found = True
+            break
+    if not found:
+        hapd.request("SET wps_version_number 0x20")
+        raise Exception("WPS-PIN flag not seen in scan results")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    dev[0].wait_connected(timeout=30)
+    hapd.request("SET wps_version_number 0x20")
+
+def test_ap_wps_conf_pin_2sta(dev, apdev):
+    """Two stations trying to use WPS PIN at the same time"""
+    ssid = "test-wps-conf-pin2"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step")
+    pin = "12345670"
+    pin2 = "55554444"
+    hapd.request("WPS_PIN " + dev[0].get_status_field("uuid") + " " + pin)
+    hapd.request("WPS_PIN " + dev[1].get_status_field("uuid") + " " + pin)
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    dev[0].wait_connected(timeout=30)
+    dev[1].wait_connected(timeout=30)
+
+def test_ap_wps_conf_pin_timeout(dev, apdev):
+    """WPS PIN provisioning with configured AP timing out PIN"""
+    ssid = "test-wps-conf-pin"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    addr = dev[0].p2p_interface_addr()
+    pin = dev[0].wps_read_pin()
+    if "FAIL" not in hapd.request("WPS_PIN "):
+        raise Exception("Unexpected success on invalid WPS_PIN")
+    hapd.request("WPS_PIN any " + pin + " 1")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    time.sleep(1.1)
+    dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    ev = hapd.wait_event(["WPS-PIN-NEEDED"], timeout=20)
+    if ev is None:
+        raise Exception("WPS-PIN-NEEDED event timed out")
+    ev = dev[0].wait_event(["WPS-M2D"])
+    if ev is None:
+        raise Exception("M2D not reported")
+    dev[0].request("WPS_CANCEL")
+
+    hapd.request("WPS_PIN any " + pin + " 20 " + addr)
+    dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    dev[0].wait_connected(timeout=30)
+
+def test_ap_wps_reg_connect(dev, apdev):
+    """WPS registrar using AP PIN to connect"""
+    ssid = "test-wps-reg-ap-pin"
+    appin = "12345670"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "ap_pin": appin})
+    logger.info("WPS provisioning step")
+    dev[0].dump_monitor()
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], appin)
+    status = dev[0].get_status()
+    if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+        raise Exception("Not fully connected")
+    if status['ssid'] != ssid:
+        raise Exception("Unexpected SSID")
+    if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
+        raise Exception("Unexpected encryption configuration")
+    if status['key_mgmt'] != 'WPA2-PSK':
+        raise Exception("Unexpected key_mgmt")
+
+def test_ap_wps_reg_connect_mixed_mode(dev, apdev):
+    """WPS registrar using AP PIN to connect (WPA+WPA2)"""
+    ssid = "test-wps-reg-ap-pin"
+    appin = "12345670"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "3",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "wpa_pairwise": "TKIP", "ap_pin": appin})
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], appin)
+    status = dev[0].get_status()
+    if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+        raise Exception("Not fully connected")
+    if status['ssid'] != ssid:
+        raise Exception("Unexpected SSID")
+    if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP':
+        raise Exception("Unexpected encryption configuration")
+    if status['key_mgmt'] != 'WPA2-PSK':
+        raise Exception("Unexpected key_mgmt")
+
+def test_ap_wps_reg_override_ap_settings(dev, apdev):
+    """WPS registrar and ap_settings override"""
+    ap_settings = "/tmp/ap_wps_reg_override_ap_settings"
+    try:
+        os.remove(ap_settings)
+    except:
+        pass
+    # Override AP Settings with values that point to another AP
+    data = build_wsc_attr(ATTR_NETWORK_INDEX, '\x01')
+    data += build_wsc_attr(ATTR_SSID, "test")
+    data += build_wsc_attr(ATTR_AUTH_TYPE, '\x00\x01')
+    data += build_wsc_attr(ATTR_ENCR_TYPE, '\x00\x01')
+    data += build_wsc_attr(ATTR_NETWORK_KEY, '')
+    data += build_wsc_attr(ATTR_MAC_ADDR, binascii.unhexlify(apdev[1]['bssid'].replace(':', '')))
+    with open(ap_settings, "w") as f:
+        f.write(data)
+    ssid = "test-wps-reg-ap-pin"
+    appin = "12345670"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "ap_pin": appin, "ap_settings": ap_settings })
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], { "ssid": "test" })
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].scan_for_bss(apdev[1]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], appin)
+    ev = hapd2.wait_event(['AP-STA-CONNECTED'], timeout=10)
+    os.remove(ap_settings)
+    if ev is None:
+        raise Exception("No connection with the other AP")
+
+def check_wps_reg_failure(dev, ap, appin):
+    dev.request("WPS_REG " + ap['bssid'] + " " + appin)
+    ev = dev.wait_event(["WPS-SUCCESS", "WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("WPS operation timed out")
+    if "WPS-SUCCESS" in ev:
+        raise Exception("WPS operation succeeded unexpectedly")
+    if "config_error=15" not in ev:
+        raise Exception("WPS setup locked state was not reported correctly")
+
+def test_ap_wps_random_ap_pin(dev, apdev):
+    """WPS registrar using random AP PIN"""
+    ssid = "test-wps-reg-random-ap-pin"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "device_name": "Wireless AP", "manufacturer": "Company",
+                     "model_name": "WAP", "model_number": "123",
+                     "serial_number": "12345", "device_type": "6-0050F204-1",
+                     "os_version": "01020300",
+                     "config_methods": "label push_button",
+                     "uuid": ap_uuid, "upnp_iface": "lo" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    appin = hapd.request("WPS_AP_PIN random")
+    if "FAIL" in appin:
+        raise Exception("Could not generate random AP PIN")
+    if appin not in hapd.request("WPS_AP_PIN get"):
+        raise Exception("Could not fetch current AP PIN")
+    logger.info("WPS provisioning step")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], appin)
+
+    hapd.request("WPS_AP_PIN disable")
+    logger.info("WPS provisioning step with AP PIN disabled")
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    check_wps_reg_failure(dev[1], apdev[0], appin)
+
+    logger.info("WPS provisioning step with AP PIN reset")
+    appin = "12345670"
+    hapd.request("WPS_AP_PIN set " + appin)
+    dev[1].wps_reg(apdev[0]['bssid'], appin)
+    dev[0].request("REMOVE_NETWORK all")
+    dev[1].request("REMOVE_NETWORK all")
+    dev[0].wait_disconnected(timeout=10)
+    dev[1].wait_disconnected(timeout=10)
+
+    logger.info("WPS provisioning step after AP PIN timeout")
+    hapd.request("WPS_AP_PIN disable")
+    appin = hapd.request("WPS_AP_PIN random 1")
+    time.sleep(1.1)
+    if "FAIL" not in hapd.request("WPS_AP_PIN get"):
+        raise Exception("AP PIN unexpectedly still enabled")
+    check_wps_reg_failure(dev[0], apdev[0], appin)
+
+    logger.info("WPS provisioning step after AP PIN timeout(2)")
+    hapd.request("WPS_AP_PIN disable")
+    appin = "12345670"
+    hapd.request("WPS_AP_PIN set " + appin + " 1")
+    time.sleep(1.1)
+    if "FAIL" not in hapd.request("WPS_AP_PIN get"):
+        raise Exception("AP PIN unexpectedly still enabled")
+    check_wps_reg_failure(dev[1], apdev[0], appin)
+
+    with fail_test(hapd, 1, "os_get_random;wps_generate_pin"):
+        if "FAIL" in hapd.request("WPS_AP_PIN random 1"):
+            raise Exception("Failed to generate PIN during OOM")
+        hapd.request("WPS_AP_PIN disable")
+
+    with alloc_fail(hapd, 1, "upnp_wps_set_ap_pin"):
+        hapd.request("WPS_AP_PIN set 12345670")
+        hapd.request("WPS_AP_PIN disable")
+
+def test_ap_wps_reg_config(dev, apdev):
+    """WPS registrar configuring an AP using AP PIN"""
+    ssid = "test-wps-init-ap-pin"
+    appin = "12345670"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "ap_pin": appin})
+    logger.info("WPS configuration step")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].dump_monitor()
+    new_ssid = "wps-new-ssid"
+    new_passphrase = "1234567890"
+    dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK", "CCMP",
+                   new_passphrase)
+    status = dev[0].get_status()
+    if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+        raise Exception("Not fully connected")
+    if status['ssid'] != new_ssid:
+        raise Exception("Unexpected SSID")
+    if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
+        raise Exception("Unexpected encryption configuration")
+    if status['key_mgmt'] != 'WPA2-PSK':
+        raise Exception("Unexpected key_mgmt")
+
+    logger.info("Re-configure back to open")
+    dev[0].request("REMOVE_NETWORK all")
+    dev[0].flush_scan_cache()
+    dev[0].dump_monitor()
+    dev[0].wps_reg(apdev[0]['bssid'], appin, "wps-open", "OPEN", "NONE", "")
+    status = dev[0].get_status()
+    if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+        raise Exception("Not fully connected")
+    if status['ssid'] != "wps-open":
+        raise Exception("Unexpected SSID")
+    if status['key_mgmt'] != 'NONE':
+        raise Exception("Unexpected key_mgmt")
+
+def test_ap_wps_reg_config_ext_processing(dev, apdev):
+    """WPS registrar configuring an AP with external config processing"""
+    ssid = "test-wps-init-ap-pin"
+    appin = "12345670"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wps_cred_processing": "1", "ap_pin": appin}
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    new_ssid = "wps-new-ssid"
+    new_passphrase = "1234567890"
+    dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK", "CCMP",
+                   new_passphrase, no_wait=True)
+    ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("WPS registrar operation timed out")
+    ev = hapd.wait_event(["WPS-NEW-AP-SETTINGS"], timeout=15)
+    if ev is None:
+        raise Exception("WPS configuration timed out")
+    if "1026" not in ev:
+        raise Exception("AP Settings missing from event")
+    hapd.request("SET wps_cred_processing 0")
+    if "FAIL" in hapd.request("WPS_CONFIG " + new_ssid.encode("hex") + " WPA2PSK CCMP " + new_passphrase.encode("hex")):
+        raise Exception("WPS_CONFIG command failed")
+    dev[0].wait_connected(timeout=15)
+
+def test_ap_wps_reg_config_tkip(dev, apdev):
+    """WPS registrar configuring AP to use TKIP and AP upgrading to TKIP+CCMP"""
+    skip_with_fips(dev[0])
+    ssid = "test-wps-init-ap"
+    appin = "12345670"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1",
+                     "ap_pin": appin})
+    logger.info("WPS configuration step")
+    dev[0].request("SET wps_version_number 0x10")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].dump_monitor()
+    new_ssid = "wps-new-ssid-with-tkip"
+    new_passphrase = "1234567890"
+    dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPAPSK", "TKIP",
+                   new_passphrase)
+    logger.info("Re-connect to verify WPA2 mixed mode")
+    dev[0].request("DISCONNECT")
+    id = 0
+    dev[0].set_network(id, "pairwise", "CCMP")
+    dev[0].set_network(id, "proto", "RSN")
+    dev[0].connect_network(id)
+    status = dev[0].get_status()
+    if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+        raise Exception("Not fully connected: wpa_state={} bssid={}".format(status['wpa_state'], status['bssid']))
+    if status['ssid'] != new_ssid:
+        raise Exception("Unexpected SSID")
+    if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP':
+        raise Exception("Unexpected encryption configuration")
+    if status['key_mgmt'] != 'WPA2-PSK':
+        raise Exception("Unexpected key_mgmt")
+
+def test_ap_wps_setup_locked(dev, apdev):
+    """WPS registrar locking up AP setup on AP PIN failures"""
+    ssid = "test-wps-incorrect-ap-pin"
+    appin = "12345670"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "ap_pin": appin})
+    new_ssid = "wps-new-ssid-test"
+    new_passphrase = "1234567890"
+
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    ap_setup_locked=False
+    for pin in ["55554444", "1234", "12345678", "00000000", "11111111"]:
+        dev[0].dump_monitor()
+        logger.info("Try incorrect AP PIN - attempt " + pin)
+        dev[0].wps_reg(apdev[0]['bssid'], pin, new_ssid, "WPA2PSK",
+                       "CCMP", new_passphrase, no_wait=True)
+        ev = dev[0].wait_event(["WPS-FAIL", "CTRL-EVENT-CONNECTED"])
+        if ev is None:
+            raise Exception("Timeout on receiving WPS operation failure event")
+        if "CTRL-EVENT-CONNECTED" in ev:
+            raise Exception("Unexpected connection")
+        if "config_error=15" in ev:
+            logger.info("AP Setup Locked")
+            ap_setup_locked=True
+        elif "config_error=18" not in ev:
+            raise Exception("config_error=18 not reported")
+        dev[0].wait_disconnected(timeout=10)
+        time.sleep(0.1)
+    if not ap_setup_locked:
+        raise Exception("AP setup was not locked")
+    dev[0].request("WPS_CANCEL")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412, force_scan=True,
+                        only_new=True)
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    if 'wps_ap_setup_locked' not in bss or bss['wps_ap_setup_locked'] != '1':
+        logger.info("BSS: " + str(bss))
+        raise Exception("AP Setup Locked not indicated in scan results")
+
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    status = hapd.request("WPS_GET_STATUS")
+    if "Last WPS result: Failed" not in status:
+        raise Exception("WPS failure result not shown correctly")
+    if "Peer Address: " + dev[0].p2p_interface_addr() not in status:
+        raise Exception("Peer address not shown correctly")
+
+    time.sleep(0.5)
+    dev[0].dump_monitor()
+    logger.info("WPS provisioning step")
+    pin = dev[0].wps_read_pin()
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    hapd.request("WPS_PIN any " + pin)
+    dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=30)
+    if ev is None:
+        raise Exception("WPS success was not reported")
+    dev[0].wait_connected(timeout=30)
+
+    appin = hapd.request("WPS_AP_PIN random")
+    if "FAIL" in appin:
+        raise Exception("Could not generate random AP PIN")
+    ev = hapd.wait_event(["WPS-AP-SETUP-UNLOCKED"], timeout=10)
+    if ev is None:
+        raise Exception("Failed to unlock AP PIN")
+
+def test_ap_wps_setup_locked_timeout(dev, apdev):
+    """WPS re-enabling AP PIN after timeout"""
+    ssid = "test-wps-incorrect-ap-pin"
+    appin = "12345670"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "ap_pin": appin})
+    new_ssid = "wps-new-ssid-test"
+    new_passphrase = "1234567890"
+
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    ap_setup_locked=False
+    for pin in ["55554444", "1234", "12345678", "00000000", "11111111"]:
+        dev[0].dump_monitor()
+        logger.info("Try incorrect AP PIN - attempt " + pin)
+        dev[0].wps_reg(apdev[0]['bssid'], pin, new_ssid, "WPA2PSK",
+                       "CCMP", new_passphrase, no_wait=True)
+        ev = dev[0].wait_event(["WPS-FAIL", "CTRL-EVENT-CONNECTED"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on receiving WPS operation failure event")
+        if "CTRL-EVENT-CONNECTED" in ev:
+            raise Exception("Unexpected connection")
+        if "config_error=15" in ev:
+            logger.info("AP Setup Locked")
+            ap_setup_locked=True
+            break
+        elif "config_error=18" not in ev:
+            raise Exception("config_error=18 not reported")
+        dev[0].wait_disconnected(timeout=10)
+        time.sleep(0.1)
+    if not ap_setup_locked:
+        raise Exception("AP setup was not locked")
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    ev = hapd.wait_event(["WPS-AP-SETUP-UNLOCKED"], timeout=80)
+    if ev is None:
+        raise Exception("AP PIN did not get unlocked on 60 second timeout")
+
+def test_ap_wps_setup_locked_2(dev, apdev):
+    """WPS AP configured for special ap_setup_locked=2 mode"""
+    ssid = "test-wps-ap-pin"
+    appin = "12345670"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+               "ap_pin": appin, "ap_setup_locked": "2" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    new_ssid = "wps-new-ssid-test"
+    new_passphrase = "1234567890"
+
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], appin)
+    dev[0].request("REMOVE_NETWORK all")
+    dev[0].wait_disconnected()
+
+    hapd.dump_monitor()
+    dev[0].dump_monitor()
+    dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK",
+                   "CCMP", new_passphrase, no_wait=True)
+
+    ev = hapd.wait_event(["WPS-FAIL"], timeout=5)
+    if ev is None:
+        raise Exception("hostapd did not report WPS failure")
+    if "msg=12 config_error=15" not in ev:
+        raise Exception("Unexpected failure reason (AP): " + ev)
+
+    ev = dev[0].wait_event(["WPS-FAIL", "CTRL-EVENT-CONNECTED"])
+    if ev is None:
+        raise Exception("Timeout on receiving WPS operation failure event")
+    if "CTRL-EVENT-CONNECTED" in ev:
+        raise Exception("Unexpected connection")
+    if "config_error=15" not in ev:
+        raise Exception("Unexpected failure reason (STA): " + ev)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+
+def test_ap_wps_pbc_overlap_2ap(dev, apdev):
+    """WPS PBC session overlap with two active APs"""
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": "wps1", "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "wps_independent": "1"})
+    hostapd.add_ap(apdev[1]['ifname'],
+                   { "ssid": "wps2", "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "123456789", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "wps_independent": "1"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    hapd.request("WPS_PBC")
+    hapd2 = hostapd.Hostapd(apdev[1]['ifname'])
+    hapd2.request("WPS_PBC")
+    logger.info("WPS provisioning step")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+    dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+    dev[0].request("WPS_PBC")
+    ev = dev[0].wait_event(["WPS-OVERLAP-DETECTED"], timeout=15)
+    if ev is None:
+        raise Exception("PBC session overlap not detected")
+    hapd.request("DISABLE")
+    hapd2.request("DISABLE")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_pbc_overlap_2sta(dev, apdev):
+    """WPS PBC session overlap with two active STAs"""
+    ssid = "test-wps-pbc-overlap"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step")
+    hapd.request("WPS_PBC")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].dump_monitor()
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[1].dump_monitor()
+    dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+    dev[1].request("WPS_PBC " + apdev[0]['bssid'])
+    ev = dev[0].wait_event(["WPS-M2D"], timeout=15)
+    if ev is None:
+        raise Exception("PBC session overlap not detected (dev0)")
+    if "config_error=12" not in ev:
+        raise Exception("PBC session overlap not correctly reported (dev0)")
+    dev[0].request("WPS_CANCEL")
+    dev[0].request("DISCONNECT")
+    ev = dev[1].wait_event(["WPS-M2D"], timeout=15)
+    if ev is None:
+        raise Exception("PBC session overlap not detected (dev1)")
+    if "config_error=12" not in ev:
+        raise Exception("PBC session overlap not correctly reported (dev1)")
+    dev[1].request("WPS_CANCEL")
+    dev[1].request("DISCONNECT")
+    hapd.request("WPS_CANCEL")
+    ret = hapd.request("WPS_PBC")
+    if "FAIL" not in ret:
+        raise Exception("PBC mode allowed to be started while PBC overlap still active")
+    hapd.request("DISABLE")
+    dev[0].flush_scan_cache()
+    dev[1].flush_scan_cache()
+
+def test_ap_wps_cancel(dev, apdev):
+    """WPS AP cancelling enabled config method"""
+    ssid = "test-wps-ap-cancel"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP" })
+    bssid = apdev[0]['bssid']
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    logger.info("Verify PBC enable/cancel")
+    hapd.request("WPS_PBC")
+    dev[0].scan(freq="2412")
+    dev[0].scan(freq="2412")
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    if "[WPS-PBC]" not in bss['flags']:
+        raise Exception("WPS-PBC flag missing")
+    if "FAIL" in hapd.request("WPS_CANCEL"):
+        raise Exception("WPS_CANCEL failed")
+    dev[0].scan(freq="2412")
+    dev[0].scan(freq="2412")
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    if "[WPS-PBC]" in bss['flags']:
+        raise Exception("WPS-PBC flag not cleared")
+
+    logger.info("Verify PIN enable/cancel")
+    hapd.request("WPS_PIN any 12345670")
+    dev[0].scan(freq="2412")
+    dev[0].scan(freq="2412")
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    if "[WPS-AUTH]" not in bss['flags']:
+        raise Exception("WPS-AUTH flag missing")
+    if "FAIL" in hapd.request("WPS_CANCEL"):
+        raise Exception("WPS_CANCEL failed")
+    dev[0].scan(freq="2412")
+    dev[0].scan(freq="2412")
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    if "[WPS-AUTH]" in bss['flags']:
+        raise Exception("WPS-AUTH flag not cleared")
+
+def test_ap_wps_er_add_enrollee(dev, apdev):
+    """WPS ER configuring AP and adding a new enrollee using PIN"""
+    try:
+        _test_ap_wps_er_add_enrollee(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_add_enrollee(dev, apdev):
+    ssid = "wps-er-add-enrollee"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1",
+                     "device_name": "Wireless AP", "manufacturer": "Company",
+                     "model_name": "WAP", "model_number": "123",
+                     "serial_number": "12345", "device_type": "6-0050F204-1",
+                     "os_version": "01020300",
+                     'friendly_name': "WPS AP - <>&'\" - TEST",
+                     "config_methods": "label push_button",
+                     "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+    logger.info("WPS configuration step")
+    new_passphrase = "1234567890"
+    dev[0].dump_monitor()
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], ap_pin, ssid, "WPA2PSK", "CCMP",
+                   new_passphrase)
+    status = dev[0].get_status()
+    if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+        raise Exception("Not fully connected")
+    if status['ssid'] != ssid:
+        raise Exception("Unexpected SSID")
+    if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
+        raise Exception("Unexpected encryption configuration")
+    if status['key_mgmt'] != 'WPA2-PSK':
+        raise Exception("Unexpected key_mgmt")
+
+    logger.info("Start ER")
+    dev[0].request("WPS_ER_START ifname=lo")
+    ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+    if ev is None:
+        raise Exception("AP discovery timed out")
+    if ap_uuid not in ev:
+        raise Exception("Expected AP UUID not found")
+    if "|WPS AP - &lt;&gt;&amp;&apos;&quot; - TEST|Company|" not in ev:
+        raise Exception("Expected friendly name not found")
+
+    logger.info("Learn AP configuration through UPnP")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+    ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+    if ev is None:
+        raise Exception("AP learn timed out")
+    if ap_uuid not in ev:
+        raise Exception("Expected AP UUID not in settings")
+    if "ssid=" + ssid not in ev:
+        raise Exception("Expected SSID not in settings")
+    if "key=" + new_passphrase not in ev:
+        raise Exception("Expected passphrase not in settings")
+    ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("WPS-FAIL after AP learn timed out")
+    time.sleep(0.1)
+
+    logger.info("Add Enrollee using ER")
+    pin = dev[1].wps_read_pin()
+    dev[0].dump_monitor()
+    dev[0].request("WPS_ER_PIN any " + pin + " " + dev[1].p2p_interface_addr())
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[1].dump_monitor()
+    dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=30)
+    if ev is None:
+        raise Exception("Enrollee did not report success")
+    dev[1].wait_connected(timeout=15)
+    ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("WPS ER did not report success")
+    hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+
+    logger.info("Add a specific Enrollee using ER")
+    pin = dev[2].wps_read_pin()
+    addr2 = dev[2].p2p_interface_addr()
+    dev[0].dump_monitor()
+    dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[2].dump_monitor()
+    dev[2].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=10)
+    if ev is None:
+        raise Exception("Enrollee not seen")
+    if addr2 not in ev:
+        raise Exception("Unexpected Enrollee MAC address")
+    dev[0].request("WPS_ER_PIN " + addr2 + " " + pin + " " + addr2)
+    dev[2].wait_connected(timeout=30)
+    ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("WPS ER did not report success")
+
+    logger.info("Verify registrar selection behavior")
+    dev[0].request("WPS_ER_PIN any " + pin + " " + dev[1].p2p_interface_addr())
+    dev[1].request("DISCONNECT")
+    dev[1].wait_disconnected(timeout=10)
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[1].scan(freq="2412")
+    bss = dev[1].get_bss(apdev[0]['bssid'])
+    if "[WPS-AUTH]" not in bss['flags']:
+        # It is possible for scan to miss an update especially when running
+        # tests under load with multiple VMs, so allow another attempt.
+        dev[1].scan(freq="2412")
+        bss = dev[1].get_bss(apdev[0]['bssid'])
+        if "[WPS-AUTH]" not in bss['flags']:
+            raise Exception("WPS-AUTH flag missing")
+
+    logger.info("Stop ER")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_ER_STOP")
+    ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"])
+    if ev is None:
+        raise Exception("WPS ER unsubscription timed out")
+    # It takes some time for the UPnP UNSUBSCRIBE command to go through, so wait
+    # a bit before verifying that the scan results have changed.
+    time.sleep(0.2)
+
+    for i in range(0, 10):
+        dev[1].request("BSS_FLUSH 0")
+        dev[1].scan(freq="2412", only_new=True)
+        bss = dev[1].get_bss(apdev[0]['bssid'])
+        if bss and 'flags' in bss and "[WPS-AUTH]" not in bss['flags']:
+            break
+        logger.debug("WPS-AUTH flag was still in place - wait a bit longer")
+        time.sleep(0.1)
+    if "[WPS-AUTH]" in bss['flags']:
+        raise Exception("WPS-AUTH flag not removed")
+
+def test_ap_wps_er_add_enrollee_uuid(dev, apdev):
+    """WPS ER adding a new enrollee identified by UUID"""
+    try:
+        _test_ap_wps_er_add_enrollee_uuid(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_add_enrollee_uuid(dev, apdev):
+    ssid = "wps-er-add-enrollee"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "device_name": "Wireless AP", "manufacturer": "Company",
+                     "model_name": "WAP", "model_number": "123",
+                     "serial_number": "12345", "device_type": "6-0050F204-1",
+                     "os_version": "01020300",
+                     "config_methods": "label push_button",
+                     "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+    logger.info("WPS configuration step")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+
+    logger.info("Start ER")
+    dev[0].request("WPS_ER_START ifname=lo")
+    ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+    if ev is None:
+        raise Exception("AP discovery timed out")
+    if ap_uuid not in ev:
+        raise Exception("Expected AP UUID not found")
+
+    logger.info("Learn AP configuration through UPnP")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+    ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+    if ev is None:
+        raise Exception("AP learn timed out")
+    if ap_uuid not in ev:
+        raise Exception("Expected AP UUID not in settings")
+    ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("WPS-FAIL after AP learn timed out")
+    time.sleep(0.1)
+
+    logger.info("Add a specific Enrollee using ER (PBC/UUID)")
+    addr1 = dev[1].p2p_interface_addr()
+    dev[0].dump_monitor()
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[1].dump_monitor()
+    dev[1].request("WPS_PBC %s" % apdev[0]['bssid'])
+    ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=10)
+    if ev is None:
+        raise Exception("Enrollee not seen")
+    if addr1 not in ev:
+        raise Exception("Unexpected Enrollee MAC address")
+    uuid = ev.split(' ')[1]
+    dev[0].request("WPS_ER_PBC " + uuid)
+    dev[1].wait_connected(timeout=30)
+    ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("WPS ER did not report success")
+
+    logger.info("Add a specific Enrollee using ER (PIN/UUID)")
+    pin = dev[2].wps_read_pin()
+    addr2 = dev[2].p2p_interface_addr()
+    dev[0].dump_monitor()
+    dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[2].dump_monitor()
+    dev[2].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=10)
+    if ev is None:
+        raise Exception("Enrollee not seen")
+    if addr2 not in ev:
+        raise Exception("Unexpected Enrollee MAC address")
+    uuid = ev.split(' ')[1]
+    dev[0].request("WPS_ER_PIN " + uuid + " " + pin)
+    dev[2].wait_connected(timeout=30)
+    ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("WPS ER did not report success")
+
+    ev = dev[0].wait_event(["WPS-ER-ENROLLEE-REMOVE"], timeout=15)
+    if ev is None:
+        raise Exception("No Enrollee STA entry timeout seen")
+
+    logger.info("Stop ER")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_ER_STOP")
+
+def test_ap_wps_er_multi_add_enrollee(dev, apdev):
+    """Multiple WPS ERs adding a new enrollee using PIN"""
+    try:
+        _test_ap_wps_er_multi_add_enrollee(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_multi_add_enrollee(dev, apdev):
+    ssid = "wps-er-add-enrollee"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "device_name": "Wireless AP", "manufacturer": "Company",
+                     "model_name": "WAP", "model_number": "123",
+                     "serial_number": "12345", "device_type": "6-0050F204-1",
+                     "os_version": "01020300",
+                     'friendly_name': "WPS AP",
+                     "config_methods": "label push_button",
+                     "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+
+    for i in range(2):
+        dev[i].scan_for_bss(apdev[0]['bssid'], freq=2412)
+        dev[i].wps_reg(apdev[0]['bssid'], ap_pin)
+        dev[i].request("WPS_ER_START ifname=lo")
+    for i in range(2):
+        ev = dev[i].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+        if ev is None:
+            raise Exception("AP discovery timed out")
+        dev[i].dump_monitor()
+        dev[i].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+        ev = dev[i].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+        if ev is None:
+            raise Exception("AP learn timed out")
+        ev = dev[i].wait_event(["WPS-FAIL"], timeout=15)
+        if ev is None:
+            raise Exception("WPS-FAIL after AP learn timed out")
+
+    time.sleep(0.1)
+
+    pin = dev[2].wps_read_pin()
+    addr = dev[2].own_addr()
+    dev[0].dump_monitor()
+    dev[0].request("WPS_ER_PIN any " + pin + " " + addr)
+    dev[1].dump_monitor()
+    dev[1].request("WPS_ER_PIN any " + pin + " " + addr)
+
+    dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[2].dump_monitor()
+    dev[2].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    ev = dev[2].wait_event(["WPS-SUCCESS"], timeout=30)
+    if ev is None:
+        raise Exception("Enrollee did not report success")
+    dev[2].wait_connected(timeout=15)
+
+def test_ap_wps_er_add_enrollee_pbc(dev, apdev):
+    """WPS ER connected to AP and adding a new enrollee using PBC"""
+    try:
+        _test_ap_wps_er_add_enrollee_pbc(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_add_enrollee_pbc(dev, apdev):
+    ssid = "wps-er-add-enrollee-pbc"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "device_name": "Wireless AP", "manufacturer": "Company",
+                     "model_name": "WAP", "model_number": "123",
+                     "serial_number": "12345", "device_type": "6-0050F204-1",
+                     "os_version": "01020300",
+                     "config_methods": "label push_button",
+                     "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+    logger.info("Learn AP configuration")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].dump_monitor()
+    dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+    status = dev[0].get_status()
+    if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+        raise Exception("Not fully connected")
+
+    logger.info("Start ER")
+    dev[0].request("WPS_ER_START ifname=lo")
+    ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+    if ev is None:
+        raise Exception("AP discovery timed out")
+    if ap_uuid not in ev:
+        raise Exception("Expected AP UUID not found")
+
+    enrollee = dev[1].p2p_interface_addr()
+
+    if "FAIL-UNKNOWN-UUID" not in dev[0].request("WPS_ER_PBC " + enrollee):
+        raise Exception("Unknown UUID not reported")
+
+    logger.info("Add Enrollee using ER and PBC")
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    dev[1].request("WPS_PBC")
+
+    for i in range(0, 2):
+        ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=15)
+        if ev is None:
+            raise Exception("Enrollee discovery timed out")
+        if enrollee in ev:
+            break
+        if i == 1:
+            raise Exception("Expected Enrollee not found")
+    if "FAIL-NO-AP-SETTINGS" not in dev[0].request("WPS_ER_PBC " + enrollee):
+        raise Exception("Unknown UUID not reported")
+    logger.info("Use learned network configuration on ER")
+    dev[0].request("WPS_ER_SET_CONFIG " + ap_uuid + " 0")
+    if "OK" not in dev[0].request("WPS_ER_PBC " + enrollee):
+        raise Exception("WPS_ER_PBC failed")
+
+    ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("Enrollee did not report success")
+    dev[1].wait_connected(timeout=15)
+    ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("WPS ER did not report success")
+    hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+
+def test_ap_wps_er_pbc_overlap(dev, apdev):
+    """WPS ER connected to AP and PBC session overlap"""
+    try:
+        _test_ap_wps_er_pbc_overlap(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_pbc_overlap(dev, apdev):
+    ssid = "wps-er-add-enrollee-pbc"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "device_name": "Wireless AP", "manufacturer": "Company",
+                     "model_name": "WAP", "model_number": "123",
+                     "serial_number": "12345", "device_type": "6-0050F204-1",
+                     "os_version": "01020300",
+                     "config_methods": "label push_button",
+                     "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].dump_monitor()
+    dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[2].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    # avoid leaving dev 1 or 2 as the last Probe Request to the AP
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412, force_scan=True)
+
+    dev[0].dump_monitor()
+    dev[0].request("WPS_ER_START ifname=lo")
+
+    ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+    if ev is None:
+        raise Exception("AP discovery timed out")
+    if ap_uuid not in ev:
+        raise Exception("Expected AP UUID not found")
+
+    # verify BSSID selection of the AP instead of UUID
+    if "FAIL" in dev[0].request("WPS_ER_SET_CONFIG " + apdev[0]['bssid'] + " 0"):
+        raise Exception("Could not select AP based on BSSID")
+
+    dev[0].dump_monitor()
+    dev[1].request("WPS_PBC " + apdev[0]['bssid'])
+    dev[2].request("WPS_PBC " + apdev[0]['bssid'])
+    ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+    if ev is None:
+        raise Exception("PBC scan failed")
+    ev = dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+    if ev is None:
+        raise Exception("PBC scan failed")
+    found1 = False
+    found2 = False
+    addr1 = dev[1].own_addr()
+    addr2 = dev[2].own_addr()
+    for i in range(3):
+        ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=15)
+        if ev is None:
+            raise Exception("Enrollee discovery timed out")
+        if addr1 in ev:
+            found1 = True
+            if found2:
+                break
+        if addr2 in ev:
+            found2 = True
+            if found1:
+                break
+    if dev[0].request("WPS_ER_PBC " + ap_uuid) != "FAIL-PBC-OVERLAP\n":
+        raise Exception("PBC overlap not reported")
+    dev[1].request("WPS_CANCEL")
+    dev[2].request("WPS_CANCEL")
+    if dev[0].request("WPS_ER_PBC foo") != "FAIL\n":
+        raise Exception("Invalid WPS_ER_PBC accepted")
+
+def test_ap_wps_er_v10_add_enrollee_pin(dev, apdev):
+    """WPS v1.0 ER connected to AP and adding a new enrollee using PIN"""
+    try:
+        _test_ap_wps_er_v10_add_enrollee_pin(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_v10_add_enrollee_pin(dev, apdev):
+    ssid = "wps-er-add-enrollee-pbc"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "device_name": "Wireless AP", "manufacturer": "Company",
+                     "model_name": "WAP", "model_number": "123",
+                     "serial_number": "12345", "device_type": "6-0050F204-1",
+                     "os_version": "01020300",
+                     "config_methods": "label push_button",
+                     "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+    logger.info("Learn AP configuration")
+    dev[0].request("SET wps_version_number 0x10")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].dump_monitor()
+    dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+    status = dev[0].get_status()
+    if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+        raise Exception("Not fully connected")
+
+    logger.info("Start ER")
+    dev[0].request("WPS_ER_START ifname=lo")
+    ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+    if ev is None:
+        raise Exception("AP discovery timed out")
+    if ap_uuid not in ev:
+        raise Exception("Expected AP UUID not found")
+
+    logger.info("Use learned network configuration on ER")
+    dev[0].request("WPS_ER_SET_CONFIG " + ap_uuid + " 0")
+
+    logger.info("Add Enrollee using ER and PIN")
+    enrollee = dev[1].p2p_interface_addr()
+    pin = dev[1].wps_read_pin()
+    dev[0].dump_monitor()
+    dev[0].request("WPS_ER_PIN any " + pin + " " + enrollee)
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[1].dump_monitor()
+    dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    dev[1].wait_connected(timeout=30)
+    ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("WPS ER did not report success")
+
+def test_ap_wps_er_config_ap(dev, apdev):
+    """WPS ER configuring AP over UPnP"""
+    try:
+        _test_ap_wps_er_config_ap(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_config_ap(dev, apdev):
+    ssid = "wps-er-ap-config"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "device_name": "Wireless AP", "manufacturer": "Company",
+                     "model_name": "WAP", "model_number": "123",
+                     "serial_number": "12345", "device_type": "6-0050F204-1",
+                     "os_version": "01020300",
+                     "config_methods": "label push_button",
+                     "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+
+    logger.info("Connect ER to the AP")
+    dev[0].connect(ssid, psk="12345678", scan_freq="2412")
+
+    logger.info("WPS configuration step")
+    dev[0].request("WPS_ER_START ifname=lo")
+    ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+    if ev is None:
+        raise Exception("AP discovery timed out")
+    if ap_uuid not in ev:
+        raise Exception("Expected AP UUID not found")
+    new_passphrase = "1234567890"
+    dev[0].request("WPS_ER_CONFIG " + apdev[0]['bssid'] + " " + ap_pin + " " +
+                   ssid.encode("hex") + " WPA2PSK CCMP " +
+                   new_passphrase.encode("hex"))
+    ev = dev[0].wait_event(["WPS-SUCCESS"])
+    if ev is None:
+        raise Exception("WPS ER configuration operation timed out")
+    dev[0].wait_disconnected(timeout=10)
+    dev[0].connect(ssid, psk="1234567890", scan_freq="2412")
+
+    logger.info("WPS ER restart")
+    dev[0].request("WPS_ER_START")
+    ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+    if ev is None:
+        raise Exception("AP discovery timed out on ER restart")
+    if ap_uuid not in ev:
+        raise Exception("Expected AP UUID not found on ER restart")
+    if "OK" not in dev[0].request("WPS_ER_STOP"):
+        raise Exception("WPS_ER_STOP failed")
+    if "OK" not in dev[0].request("WPS_ER_STOP"):
+        raise Exception("WPS_ER_STOP failed")
+
+def test_ap_wps_er_cache_ap_settings(dev, apdev):
+    """WPS ER caching AP settings"""
+    try:
+        _test_ap_wps_er_cache_ap_settings(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_cache_ap_settings(dev, apdev):
+    ssid = "wps-er-add-enrollee"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+               "device_name": "Wireless AP", "manufacturer": "Company",
+               "model_name": "WAP", "model_number": "123",
+               "serial_number": "12345", "device_type": "6-0050F204-1",
+               "os_version": "01020300",
+               "config_methods": "label push_button",
+               "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+    id = int(dev[0].list_networks()[0]['id'])
+    dev[0].set_network(id, "scan_freq", "2412")
+
+    dev[0].request("WPS_ER_START ifname=lo")
+    ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+    if ev is None:
+        raise Exception("AP discovery timed out")
+    if ap_uuid not in ev:
+        raise Exception("Expected AP UUID not found")
+
+    dev[0].dump_monitor()
+    dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+    ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+    if ev is None:
+        raise Exception("AP learn timed out")
+    ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("WPS-FAIL after AP learn timed out")
+    time.sleep(0.1)
+
+    hapd.disable()
+
+    for i in range(2):
+        ev = dev[0].wait_event([ "WPS-ER-AP-REMOVE",
+                                 "CTRL-EVENT-DISCONNECTED" ],
+                               timeout=15)
+        if ev is None:
+            raise Exception("AP removal or disconnection timed out")
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    for i in range(2):
+        ev = dev[0].wait_event([ "WPS-ER-AP-ADD", "CTRL-EVENT-CONNECTED" ],
+                               timeout=15)
+        if ev is None:
+            raise Exception("AP discovery or connection timed out")
+
+    pin = dev[1].wps_read_pin()
+    dev[0].dump_monitor()
+    dev[0].request("WPS_ER_PIN any " + pin + " " + dev[1].p2p_interface_addr())
+
+    time.sleep(0.2)
+
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[1].dump_monitor()
+    dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=30)
+    if ev is None:
+        raise Exception("Enrollee did not report success")
+    dev[1].wait_connected(timeout=15)
+    ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("WPS ER did not report success")
+
+    dev[0].dump_monitor()
+    dev[0].request("WPS_ER_STOP")
+
+def test_ap_wps_er_cache_ap_settings_oom(dev, apdev):
+    """WPS ER caching AP settings (OOM)"""
+    try:
+        _test_ap_wps_er_cache_ap_settings_oom(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_cache_ap_settings_oom(dev, apdev):
+    ssid = "wps-er-add-enrollee"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+               "device_name": "Wireless AP", "manufacturer": "Company",
+               "model_name": "WAP", "model_number": "123",
+               "serial_number": "12345", "device_type": "6-0050F204-1",
+               "os_version": "01020300",
+               "config_methods": "label push_button",
+               "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+    id = int(dev[0].list_networks()[0]['id'])
+    dev[0].set_network(id, "scan_freq", "2412")
+
+    dev[0].request("WPS_ER_START ifname=lo")
+    ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+    if ev is None:
+        raise Exception("AP discovery timed out")
+    if ap_uuid not in ev:
+        raise Exception("Expected AP UUID not found")
+
+    dev[0].dump_monitor()
+    dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+    ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+    if ev is None:
+        raise Exception("AP learn timed out")
+    ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("WPS-FAIL after AP learn timed out")
+    time.sleep(0.1)
+
+    with alloc_fail(dev[0], 1, "=wps_er_ap_use_cached_settings"):
+        hapd.disable()
+
+        for i in range(2):
+            ev = dev[0].wait_event([ "WPS-ER-AP-REMOVE",
+                                     "CTRL-EVENT-DISCONNECTED" ],
+                                   timeout=15)
+            if ev is None:
+                raise Exception("AP removal or disconnection timed out")
+
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        for i in range(2):
+            ev = dev[0].wait_event([ "WPS-ER-AP-ADD", "CTRL-EVENT-CONNECTED" ],
+                                   timeout=15)
+            if ev is None:
+                raise Exception("AP discovery or connection timed out")
+
+    dev[0].request("WPS_ER_STOP")
+
+def test_ap_wps_er_cache_ap_settings_oom2(dev, apdev):
+    """WPS ER caching AP settings (OOM 2)"""
+    try:
+        _test_ap_wps_er_cache_ap_settings_oom2(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_cache_ap_settings_oom2(dev, apdev):
+    ssid = "wps-er-add-enrollee"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+               "device_name": "Wireless AP", "manufacturer": "Company",
+               "model_name": "WAP", "model_number": "123",
+               "serial_number": "12345", "device_type": "6-0050F204-1",
+               "os_version": "01020300",
+               "config_methods": "label push_button",
+               "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+    id = int(dev[0].list_networks()[0]['id'])
+    dev[0].set_network(id, "scan_freq", "2412")
+
+    dev[0].request("WPS_ER_START ifname=lo")
+    ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+    if ev is None:
+        raise Exception("AP discovery timed out")
+    if ap_uuid not in ev:
+        raise Exception("Expected AP UUID not found")
+
+    dev[0].dump_monitor()
+    dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+    ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+    if ev is None:
+        raise Exception("AP learn timed out")
+    ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("WPS-FAIL after AP learn timed out")
+    time.sleep(0.1)
+
+    with alloc_fail(dev[0], 1, "=wps_er_ap_cache_settings"):
+        hapd.disable()
+
+        for i in range(2):
+            ev = dev[0].wait_event([ "WPS-ER-AP-REMOVE",
+                                     "CTRL-EVENT-DISCONNECTED" ],
+                                   timeout=15)
+            if ev is None:
+                raise Exception("AP removal or disconnection timed out")
+
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        for i in range(2):
+            ev = dev[0].wait_event([ "WPS-ER-AP-ADD", "CTRL-EVENT-CONNECTED" ],
+                                   timeout=15)
+            if ev is None:
+                raise Exception("AP discovery or connection timed out")
+
+    dev[0].request("WPS_ER_STOP")
+
+def test_ap_wps_er_subscribe_oom(dev, apdev):
+    """WPS ER subscribe OOM"""
+    try:
+        _test_ap_wps_er_subscribe_oom(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_subscribe_oom(dev, apdev):
+    ssid = "wps-er-add-enrollee"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+               "device_name": "Wireless AP", "manufacturer": "Company",
+               "model_name": "WAP", "model_number": "123",
+               "serial_number": "12345", "device_type": "6-0050F204-1",
+               "os_version": "01020300",
+               "config_methods": "label push_button",
+               "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+    id = int(dev[0].list_networks()[0]['id'])
+    dev[0].set_network(id, "scan_freq", "2412")
+
+    with alloc_fail(dev[0], 1, "http_client_addr;wps_er_subscribe"):
+        dev[0].request("WPS_ER_START ifname=lo")
+        for i in range(50):
+            res = dev[0].request("GET_ALLOC_FAIL")
+            if res.startswith("0:"):
+                break
+            time.sleep(0.1)
+        ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=0)
+        if ev:
+            raise Exception("Unexpected AP discovery during OOM")
+
+    dev[0].request("WPS_ER_STOP")
+
+def test_ap_wps_er_set_sel_reg_oom(dev, apdev):
+    """WPS ER SetSelectedRegistrar OOM"""
+    try:
+        _test_ap_wps_er_set_sel_reg_oom(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_set_sel_reg_oom(dev, apdev):
+    ssid = "wps-er-add-enrollee"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+               "device_name": "Wireless AP", "manufacturer": "Company",
+               "model_name": "WAP", "model_number": "123",
+               "serial_number": "12345", "device_type": "6-0050F204-1",
+               "os_version": "01020300",
+               "config_methods": "label push_button",
+               "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+
+    dev[0].request("WPS_ER_START ifname=lo")
+    ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=10)
+    if ev is None:
+        raise Exception("AP not discovered")
+
+    dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+    ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+    if ev is None:
+        raise Exception("AP learn timed out")
+    ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("WPS-FAIL timed out")
+    time.sleep(0.1)
+
+    for func in [ "http_client_url_parse;wps_er_send_set_sel_reg",
+                  "wps_er_soap_hdr;wps_er_send_set_sel_reg",
+                  "http_client_addr;wps_er_send_set_sel_reg",
+                  "wpabuf_alloc;wps_er_set_sel_reg" ]:
+        with alloc_fail(dev[0], 1, func):
+            if "OK" not in dev[0].request("WPS_ER_PBC " + ap_uuid):
+                raise Exception("WPS_ER_PBC failed")
+            ev = dev[0].wait_event(["WPS-PBC-ACTIVE"], timeout=3)
+            if ev is None:
+                raise Exception("WPS-PBC-ACTIVE not seen")
+
+    dev[0].request("WPS_ER_STOP")
+
+def test_ap_wps_er_learn_oom(dev, apdev):
+    """WPS ER learn OOM"""
+    try:
+        _test_ap_wps_er_learn_oom(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_learn_oom(dev, apdev):
+    ssid = "wps-er-add-enrollee"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+               "device_name": "Wireless AP", "manufacturer": "Company",
+               "model_name": "WAP", "model_number": "123",
+               "serial_number": "12345", "device_type": "6-0050F204-1",
+               "os_version": "01020300",
+               "config_methods": "label push_button",
+               "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+
+    dev[0].request("WPS_ER_START ifname=lo")
+    ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=10)
+    if ev is None:
+        raise Exception("AP not discovered")
+
+    for func in [ "wps_er_http_put_message_cb",
+                  "xml_get_base64_item;wps_er_http_put_message_cb",
+                  "http_client_url_parse;wps_er_ap_put_message",
+                  "wps_er_soap_hdr;wps_er_ap_put_message",
+                  "http_client_addr;wps_er_ap_put_message" ]:
+        with alloc_fail(dev[0], 1, func):
+            dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+            ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=1)
+            if ev is not None:
+                raise Exception("AP learn succeeded during OOM")
+
+    dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+    ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=10)
+    if ev is None:
+        raise Exception("AP learn did not succeed")
+
+    if "FAIL" not in dev[0].request("WPS_ER_LEARN 00000000-9e5c-4e73-bd82-f89cbcd10d7e " + ap_pin):
+        raise Exception("WPS_ER_LEARN for unknown AP accepted")
+
+    dev[0].request("WPS_ER_STOP")
+
+def test_ap_wps_fragmentation(dev, apdev):
+    """WPS with fragmentation in EAP-WSC and mixed mode WPA+WPA2"""
+    ssid = "test-wps-fragmentation"
+    appin = "12345670"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "3",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "wpa_pairwise": "TKIP", "ap_pin": appin,
+                     "fragment_size": "50" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step (PBC)")
+    hapd.request("WPS_PBC")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].dump_monitor()
+    dev[0].request("SET wps_fragment_size 50")
+    dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+    dev[0].wait_connected(timeout=30)
+    status = dev[0].get_status()
+    if status['wpa_state'] != 'COMPLETED':
+        raise Exception("Not fully connected")
+    if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP':
+        raise Exception("Unexpected encryption configuration")
+    if status['key_mgmt'] != 'WPA2-PSK':
+        raise Exception("Unexpected key_mgmt")
+
+    logger.info("WPS provisioning step (PIN)")
+    pin = dev[1].wps_read_pin()
+    hapd.request("WPS_PIN any " + pin)
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[1].request("SET wps_fragment_size 50")
+    dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    dev[1].wait_connected(timeout=30)
+    status = dev[1].get_status()
+    if status['wpa_state'] != 'COMPLETED':
+        raise Exception("Not fully connected")
+    if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP':
+        raise Exception("Unexpected encryption configuration")
+    if status['key_mgmt'] != 'WPA2-PSK':
+        raise Exception("Unexpected key_mgmt")
+
+    logger.info("WPS connection as registrar")
+    dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[2].request("SET wps_fragment_size 50")
+    dev[2].wps_reg(apdev[0]['bssid'], appin)
+    status = dev[2].get_status()
+    if status['wpa_state'] != 'COMPLETED':
+        raise Exception("Not fully connected")
+    if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP':
+        raise Exception("Unexpected encryption configuration")
+    if status['key_mgmt'] != 'WPA2-PSK':
+        raise Exception("Unexpected key_mgmt")
+
+def test_ap_wps_new_version_sta(dev, apdev):
+    """WPS compatibility with new version number on the station"""
+    ssid = "test-wps-ver"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step")
+    hapd.request("WPS_PBC")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].dump_monitor()
+    dev[0].request("SET wps_version_number 0x43")
+    dev[0].request("SET wps_vendor_ext_m1 000137100100020001")
+    dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+    dev[0].wait_connected(timeout=30)
+
+def test_ap_wps_new_version_ap(dev, apdev):
+    """WPS compatibility with new version number on the AP"""
+    ssid = "test-wps-ver"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step")
+    if "FAIL" in hapd.request("SET wps_version_number 0x43"):
+        raise Exception("Failed to enable test functionality")
+    hapd.request("WPS_PBC")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+    dev[0].wait_connected(timeout=30)
+    hapd.request("SET wps_version_number 0x20")
+
+def test_ap_wps_check_pin(dev, apdev):
+    """Verify PIN checking through control interface"""
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": "wps", "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    for t in [ ("12345670", "12345670"),
+               ("12345678", "FAIL-CHECKSUM"),
+               ("12345", "FAIL"),
+               ("123456789", "FAIL"),
+               ("1234-5670", "12345670"),
+               ("1234 5670", "12345670"),
+               ("1-2.3:4 5670", "12345670") ]:
+        res = hapd.request("WPS_CHECK_PIN " + t[0]).rstrip('\n')
+        res2 = dev[0].request("WPS_CHECK_PIN " + t[0]).rstrip('\n')
+        if res != res2:
+            raise Exception("Unexpected difference in WPS_CHECK_PIN responses")
+        if res != t[1]:
+            raise Exception("Incorrect WPS_CHECK_PIN response {} (expected {})".format(res, t[1]))
+
+    if "FAIL" not in hapd.request("WPS_CHECK_PIN 12345"):
+        raise Exception("Unexpected WPS_CHECK_PIN success")
+    if "FAIL" not in hapd.request("WPS_CHECK_PIN 123456789"):
+        raise Exception("Unexpected WPS_CHECK_PIN success")
+
+    for i in range(0, 10):
+        pin = dev[0].request("WPS_PIN get")
+        rpin = dev[0].request("WPS_CHECK_PIN " + pin).rstrip('\n')
+        if pin != rpin:
+            raise Exception("Random PIN validation failed for " + pin)
+
+def test_ap_wps_wep_config(dev, apdev):
+    """WPS 2.0 AP rejecting WEP configuration"""
+    ssid = "test-wps-config"
+    appin = "12345670"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "ap_pin": appin})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], appin, "wps-new-ssid-wep", "OPEN", "WEP",
+                   "hello", no_wait=True)
+    ev = hapd.wait_event(["WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("WPS-FAIL timed out")
+    if "reason=2" not in ev:
+        raise Exception("Unexpected reason code in WPS-FAIL")
+    status = hapd.request("WPS_GET_STATUS")
+    if "Last WPS result: Failed" not in status:
+        raise Exception("WPS failure result not shown correctly")
+    if "Failure Reason: WEP Prohibited" not in status:
+        raise Exception("Failure reason not reported correctly")
+    if "Peer Address: " + dev[0].p2p_interface_addr() not in status:
+        raise Exception("Peer address not shown correctly")
+
+def test_ap_wps_wep_enroll(dev, apdev):
+    """WPS 2.0 STA rejecting WEP configuration"""
+    ssid = "test-wps-wep"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "skip_cred_build": "1", "extra_cred": "wps-wep-cred" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    hapd.request("WPS_PBC")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+    ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("WPS-FAIL event timed out")
+    if "msg=12" not in ev or "reason=2 (WEP Prohibited)" not in ev:
+        raise Exception("Unexpected WPS-FAIL event: " + ev)
+
+def test_ap_wps_ie_fragmentation(dev, apdev):
+    """WPS AP using fragmented WPS IE"""
+    ssid = "test-wps-ie-fragmentation"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+               "device_name": "1234567890abcdef1234567890abcdef",
+               "manufacturer": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
+               "model_name": "1234567890abcdef1234567890abcdef",
+               "model_number": "1234567890abcdef1234567890abcdef",
+               "serial_number": "1234567890abcdef1234567890abcdef" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    hapd.request("WPS_PBC")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+    dev[0].wait_connected(timeout=30)
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    if "wps_device_name" not in bss or bss['wps_device_name'] != "1234567890abcdef1234567890abcdef":
+        logger.info("Device Name not received correctly")
+        logger.info(bss)
+        # This can fail if Probe Response frame is missed and Beacon frame was
+        # used to fill in the BSS entry. This can happen, e.g., during heavy
+        # load every now and then and is not really an error, so try to
+        # workaround by runnign another scan.
+        dev[0].scan(freq="2412", only_new=True)
+        bss = dev[0].get_bss(apdev[0]['bssid'])
+        if not bss or "wps_device_name" not in bss or bss['wps_device_name'] != "1234567890abcdef1234567890abcdef":
+            logger.info(bss)
+            raise Exception("Device Name not received correctly")
+    if len(re.findall("dd..0050f204", bss['ie'])) != 2:
+        raise Exception("Unexpected number of WPS IEs")
+
+def get_psk(pskfile):
+    psks = {}
+    with open(pskfile, "r") as f:
+        lines = f.read().splitlines()
+        for l in lines:
+            if l == "# WPA PSKs":
+                continue
+            (addr,psk) = l.split(' ')
+            psks[addr] = psk
+    return psks
+
+def test_ap_wps_per_station_psk(dev, apdev):
+    """WPS PBC provisioning with per-station PSK"""
+    addr0 = dev[0].own_addr()
+    addr1 = dev[1].own_addr()
+    addr2 = dev[2].own_addr()
+    ssid = "wps"
+    appin = "12345670"
+    pskfile = "/tmp/ap_wps_per_enrollee_psk.psk_file"
+    try:
+        os.remove(pskfile)
+    except:
+        pass
+
+    try:
+        with open(pskfile, "w") as f:
+            f.write("# WPA PSKs\n")
+
+        params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                   "wpa": "2", "wpa_key_mgmt": "WPA-PSK",
+                   "rsn_pairwise": "CCMP", "ap_pin": appin,
+                   "wpa_psk_file": pskfile }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+        logger.info("First enrollee")
+        hapd.request("WPS_PBC")
+        dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+        dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+        dev[0].wait_connected(timeout=30)
+
+        logger.info("Second enrollee")
+        hapd.request("WPS_PBC")
+        dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+        dev[1].request("WPS_PBC " + apdev[0]['bssid'])
+        dev[1].wait_connected(timeout=30)
+
+        logger.info("External registrar")
+        dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+        dev[2].wps_reg(apdev[0]['bssid'], appin)
+
+        logger.info("Verifying PSK results")
+        psks = get_psk(pskfile)
+        if addr0 not in psks:
+            raise Exception("No PSK recorded for sta0")
+        if addr1 not in psks:
+            raise Exception("No PSK recorded for sta1")
+        if addr2 not in psks:
+            raise Exception("No PSK recorded for sta2")
+        if psks[addr0] == psks[addr1]:
+            raise Exception("Same PSK recorded for sta0 and sta1")
+        if psks[addr0] == psks[addr2]:
+            raise Exception("Same PSK recorded for sta0 and sta2")
+        if psks[addr1] == psks[addr2]:
+            raise Exception("Same PSK recorded for sta1 and sta2")
+
+        dev[0].request("REMOVE_NETWORK all")
+        logger.info("Second external registrar")
+        dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+        dev[0].wps_reg(apdev[0]['bssid'], appin)
+        psks2 = get_psk(pskfile)
+        if addr0 not in psks2:
+            raise Exception("No PSK recorded for sta0(reg)")
+        if psks[addr0] == psks2[addr0]:
+            raise Exception("Same PSK recorded for sta0(enrollee) and sta0(reg)")
+    finally:
+        os.remove(pskfile)
+
+def test_ap_wps_per_station_psk_failure(dev, apdev):
+    """WPS PBC provisioning with per-station PSK (file not writable)"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    addr2 = dev[2].p2p_dev_addr()
+    ssid = "wps"
+    appin = "12345670"
+    pskfile = "/tmp/ap_wps_per_enrollee_psk.psk_file"
+    try:
+        os.remove(pskfile)
+    except:
+        pass
+
+    try:
+        with open(pskfile, "w") as f:
+            f.write("# WPA PSKs\n")
+
+        params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                   "wpa": "2", "wpa_key_mgmt": "WPA-PSK",
+                   "rsn_pairwise": "CCMP", "ap_pin": appin,
+                   "wpa_psk_file": pskfile }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        if "FAIL" in hapd.request("SET wpa_psk_file /tmp/does/not/exists/ap_wps_per_enrollee_psk_failure.psk_file"):
+            raise Exception("Failed to set wpa_psk_file")
+
+        logger.info("First enrollee")
+        hapd.request("WPS_PBC")
+        dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+        dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+        dev[0].wait_connected(timeout=30)
+
+        logger.info("Second enrollee")
+        hapd.request("WPS_PBC")
+        dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+        dev[1].request("WPS_PBC " + apdev[0]['bssid'])
+        dev[1].wait_connected(timeout=30)
+
+        logger.info("External registrar")
+        dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+        dev[2].wps_reg(apdev[0]['bssid'], appin)
+
+        logger.info("Verifying PSK results")
+        psks = get_psk(pskfile)
+        if len(psks) > 0:
+            raise Exception("PSK recorded unexpectedly")
+    finally:
+        os.remove(pskfile)
+
+def test_ap_wps_pin_request_file(dev, apdev):
+    """WPS PIN provisioning with configured AP"""
+    ssid = "wps"
+    pinfile = "/tmp/ap_wps_pin_request_file.log"
+    if os.path.exists(pinfile):
+        os.remove(pinfile)
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wps_pin_requests": pinfile,
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    uuid = dev[0].get_status_field("uuid")
+    pin = dev[0].wps_read_pin()
+    try:
+        dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+        dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+        ev = hapd.wait_event(["WPS-PIN-NEEDED"], timeout=15)
+        if ev is None:
+            raise Exception("PIN needed event not shown")
+        if uuid not in ev:
+            raise Exception("UUID mismatch")
+        dev[0].request("WPS_CANCEL")
+        success = False
+        with open(pinfile, "r") as f:
+            lines = f.readlines()
+            for l in lines:
+                if uuid in l:
+                    success = True
+                    break
+        if not success:
+            raise Exception("PIN request entry not in the log file")
+    finally:
+        try:
+            os.remove(pinfile)
+        except:
+            pass
+
+def test_ap_wps_auto_setup_with_config_file(dev, apdev):
+    """WPS auto-setup with configuration file"""
+    conffile = "/tmp/ap_wps_auto_setup_with_config_file.conf"
+    ifname = apdev[0]['ifname']
+    try:
+        with open(conffile, "w") as f:
+            f.write("driver=nl80211\n")
+            f.write("hw_mode=g\n")
+            f.write("channel=1\n")
+            f.write("ieee80211n=1\n")
+            f.write("interface=%s\n" % ifname)
+            f.write("ctrl_interface=/var/run/hostapd\n")
+            f.write("ssid=wps\n")
+            f.write("eap_server=1\n")
+            f.write("wps_state=1\n")
+        hostapd.add_bss('phy3', ifname, conffile)
+        hapd = hostapd.Hostapd(ifname)
+        hapd.request("WPS_PBC")
+        dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+        dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+        dev[0].wait_connected(timeout=30)
+        with open(conffile, "r") as f:
+            lines = f.read().splitlines()
+            vals = dict()
+            for l in lines:
+                try:
+                    [name,value] = l.split('=', 1)
+                    vals[name] = value
+                except ValueError, e:
+                    if "# WPS configuration" in l:
+                        pass
+                    else:
+                        raise Exception("Unexpected configuration line: " + l)
+        if vals['ieee80211n'] != '1' or vals['wps_state'] != '2' or "WPA-PSK" not in vals['wpa_key_mgmt']:
+            raise Exception("Incorrect configuration: " + str(vals))
+    finally:
+        try:
+            os.remove(conffile)
+        except:
+            pass
+
+def test_ap_wps_pbc_timeout(dev, apdev, params):
+    """wpa_supplicant PBC walk time and WPS ER SelReg timeout [long]"""
+    if not params['long']:
+        raise HwsimSkip("Skip test case with long duration due to --long not specified")
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hapd = add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    location = ssdp_get_location(ap_uuid)
+    urls = upnp_get_urls(location)
+    eventurl = urlparse.urlparse(urls['event_sub_url'])
+    ctrlurl = urlparse.urlparse(urls['control_url'])
+
+    url = urlparse.urlparse(location)
+    conn = httplib.HTTPConnection(url.netloc)
+
+    class WPSERHTTPServer(SocketServer.StreamRequestHandler):
+        def handle(self):
+            data = self.rfile.readline().strip()
+            logger.debug(data)
+            self.wfile.write(gen_wps_event())
+
+    server = MyTCPServer(("127.0.0.1", 12345), WPSERHTTPServer)
+    server.timeout = 1
+
+    headers = { "callback": '<http://127.0.0.1:12345/event>',
+                "NT": "upnp:event",
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+    sid = resp.getheader("sid")
+    logger.debug("Subscription SID " + sid)
+
+    msg = '''<?xml version="1.0"?>
+<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+<s:Body>
+<u:SetSelectedRegistrar xmlns:u="urn:schemas-wifialliance-org:service:WFAWLANConfig:1">
+<NewMessage>EEoAARAQQQABARASAAIAABBTAAIxSBBJAA4ANyoAASABBv///////xBIABA2LbR7pTpRkYj7
+VFi5hrLk
+</NewMessage>
+</u:SetSelectedRegistrar>
+</s:Body>
+</s:Envelope>'''
+    headers = { "Content-type": 'text/xml; charset="utf-8"' }
+    headers["SOAPAction"] = '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#%s"' % "SetSelectedRegistrar"
+    conn.request("POST", ctrlurl.path, msg, headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    server.handle_request()
+
+    logger.info("Start WPS_PBC and wait for PBC walk time expiration")
+    if "OK" not in dev[0].request("WPS_PBC"):
+        raise Exception("WPS_PBC failed")
+
+    start = os.times()[4]
+
+    server.handle_request()
+    dev[1].request("BSS_FLUSH 0")
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True,
+                        only_new=True)
+    bss = dev[1].get_bss(apdev[0]['bssid'])
+    logger.debug("BSS: " + str(bss))
+    if '[WPS-AUTH]' not in bss['flags']:
+        raise Exception("WPS not indicated authorized")
+
+    server.handle_request()
+
+    wps_timeout_seen = False
+
+    while True:
+        hapd.dump_monitor()
+        dev[1].dump_monitor()
+        if not wps_timeout_seen:
+            ev = dev[0].wait_event(["WPS-TIMEOUT"], timeout=0)
+            if ev is not None:
+                logger.info("PBC timeout seen")
+                wps_timeout_seen = True
+        else:
+            dev[0].dump_monitor()
+        now = os.times()[4]
+        if now - start > 130:
+            raise Exception("Selected registration information not removed")
+        dev[1].request("BSS_FLUSH 0")
+        dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True,
+                            only_new=True)
+        bss = dev[1].get_bss(apdev[0]['bssid'])
+        logger.debug("BSS: " + str(bss))
+        if '[WPS-AUTH]' not in bss['flags']:
+            break
+        server.handle_request()
+
+    server.server_close()
+
+    if wps_timeout_seen:
+        return
+
+    now = os.times()[4]
+    if now < start + 150:
+        dur = start + 150 - now
+    else:
+        dur = 1
+    logger.info("Continue waiting for PBC timeout (%d sec)" % dur)
+    ev = dev[0].wait_event(["WPS-TIMEOUT"], timeout=dur)
+    if ev is None:
+        raise Exception("WPS-TIMEOUT not reported")
+
+def add_ssdp_ap(ifname, ap_uuid):
+    ssid = "wps-ssdp"
+    ap_pin = "12345670"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+               "device_name": "Wireless AP", "manufacturer": "Company",
+               "model_name": "WAP", "model_number": "123",
+               "serial_number": "12345", "device_type": "6-0050F204-1",
+               "os_version": "01020300",
+               "config_methods": "label push_button",
+               "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo",
+               "friendly_name": "WPS Access Point",
+               "manufacturer_url": "http://www.example.com/",
+               "model_description": "Wireless Access Point",
+               "model_url": "http://www.example.com/model/",
+               "upc": "123456789012" }
+    return hostapd.add_ap(ifname, params)
+
+def ssdp_send(msg, no_recv=False):
+    socket.setdefaulttimeout(1)
+    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
+    sock.bind(("127.0.0.1", 0))
+    sock.sendto(msg, ("239.255.255.250", 1900))
+    if no_recv:
+        return None
+    return sock.recv(1000)
+
+def ssdp_send_msearch(st, no_recv=False):
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MX: 1',
+            'MAN: "ssdp:discover"',
+            'ST: ' + st,
+            '', ''])
+    return ssdp_send(msg, no_recv=no_recv)
+
+def test_ap_wps_ssdp_msearch(dev, apdev):
+    """WPS AP and SSDP M-SEARCH messages"""
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'Host: 239.255.255.250:1900',
+            'Mx: 1',
+            'Man: "ssdp:discover"',
+            'St: urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    ssdp_send(msg)
+
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'host:\t239.255.255.250:1900\t\t\t\t \t\t',
+            'mx: \t1\t\t   ',
+            'man: \t \t "ssdp:discover"   ',
+            'st: urn:schemas-wifialliance-org:device:WFADevice:1\t\t',
+            '', ''])
+    ssdp_send(msg)
+
+    ssdp_send_msearch("ssdp:all")
+    ssdp_send_msearch("upnp:rootdevice")
+    ssdp_send_msearch("uuid:" + ap_uuid)
+    ssdp_send_msearch("urn:schemas-wifialliance-org:service:WFAWLANConfig:1")
+    ssdp_send_msearch("urn:schemas-wifialliance-org:device:WFADevice:1");
+
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST:\t239.255.255.250:1900',
+            'MAN: "ssdp:discover"',
+            'MX: 130',
+            'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    ssdp_send(msg, no_recv=True)
+
+def test_ap_wps_ssdp_invalid_msearch(dev, apdev):
+    """WPS AP and invalid SSDP M-SEARCH messages"""
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    socket.setdefaulttimeout(1)
+    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
+    sock.bind(("127.0.0.1", 0))
+
+    logger.debug("Missing MX")
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MAN: "ssdp:discover"',
+            'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+
+    logger.debug("Negative MX")
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MX: -1',
+            'MAN: "ssdp:discover"',
+            'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+
+    logger.debug("Invalid MX")
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MX; 1',
+            'MAN: "ssdp:discover"',
+            'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+
+    logger.debug("Missing MAN")
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MX: 1',
+            'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+
+    logger.debug("Invalid MAN")
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MX: 1',
+            'MAN: foo',
+            'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MX: 1',
+            'MAN; "ssdp:discover"',
+            'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+
+    logger.debug("Missing HOST")
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'MAN: "ssdp:discover"',
+            'MX: 1',
+            'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+
+    logger.debug("Missing ST")
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MAN: "ssdp:discover"',
+            'MX: 1',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+
+    logger.debug("Mismatching ST")
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MAN: "ssdp:discover"',
+            'MX: 1',
+            'ST: uuid:16d5f8a9-4ee4-4f5e-81f9-cc6e2f47f42d',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MAN: "ssdp:discover"',
+            'MX: 1',
+            'ST: foo:bar',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MAN: "ssdp:discover"',
+            'MX: 1',
+            'ST: foobar',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+
+    logger.debug("Invalid ST")
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MAN: "ssdp:discover"',
+            'MX: 1',
+            'ST; urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+
+    logger.debug("Invalid M-SEARCH")
+    msg = '\r\n'.join([
+            'M+SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MAN: "ssdp:discover"',
+            'MX: 1',
+            'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+    msg = '\r\n'.join([
+            'M-SEARCH-* HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MAN: "ssdp:discover"',
+            'MX: 1',
+            'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+
+    logger.debug("Invalid message format")
+    sock.sendto("NOTIFY * HTTP/1.1", ("239.255.255.250", 1900))
+    msg = '\r'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MAN: "ssdp:discover"',
+            'MX: 1',
+            'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+
+    try:
+        r = sock.recv(1000)
+        raise Exception("Unexpected M-SEARCH response: " + r)
+    except socket.timeout:
+        pass
+
+    logger.debug("Valid M-SEARCH")
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MAN: "ssdp:discover"',
+            'MX: 1',
+            'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    sock.sendto(msg, ("239.255.255.250", 1900))
+
+    try:
+        r = sock.recv(1000)
+        pass
+    except socket.timeout:
+        raise Exception("No SSDP response")
+
+def test_ap_wps_ssdp_burst(dev, apdev):
+    """WPS AP and SSDP burst"""
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    msg = '\r\n'.join([
+            'M-SEARCH * HTTP/1.1',
+            'HOST: 239.255.255.250:1900',
+            'MAN: "ssdp:discover"',
+            'MX: 1',
+            'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+            '', ''])
+    socket.setdefaulttimeout(1)
+    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
+    sock.bind(("127.0.0.1", 0))
+    for i in range(0, 25):
+        sock.sendto(msg, ("239.255.255.250", 1900))
+    resp = 0
+    while True:
+        try:
+            r = sock.recv(1000)
+            if not r.startswith("HTTP/1.1 200 OK\r\n"):
+                raise Exception("Unexpected message: " + r)
+            resp += 1
+        except socket.timeout:
+            break
+    if resp < 20:
+        raise Exception("Too few SSDP responses")
+
+    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
+    sock.bind(("127.0.0.1", 0))
+    for i in range(0, 25):
+        sock.sendto(msg, ("239.255.255.250", 1900))
+    while True:
+        try:
+            r = sock.recv(1000)
+            if ap_uuid in r:
+                break
+        except socket.timeout:
+            raise Exception("No SSDP response")
+
+def ssdp_get_location(uuid):
+    res = ssdp_send_msearch("uuid:" + uuid)
+    location = None
+    for l in res.splitlines():
+        if l.lower().startswith("location:"):
+            location = l.split(':', 1)[1].strip()
+            break
+    if location is None:
+        raise Exception("No UPnP location found")
+    return location
+
+def upnp_get_urls(location):
+    conn = urllib.urlopen(location)
+    tree = ET.parse(conn)
+    root = tree.getroot()
+    urn = '{urn:schemas-upnp-org:device-1-0}'
+    service = root.find("./" + urn + "device/" + urn + "serviceList/" + urn + "service")
+    res = {}
+    res['scpd_url'] = urlparse.urljoin(location, service.find(urn + 'SCPDURL').text)
+    res['control_url'] = urlparse.urljoin(location, service.find(urn + 'controlURL').text)
+    res['event_sub_url'] = urlparse.urljoin(location, service.find(urn + 'eventSubURL').text)
+    return res
+
+def upnp_soap_action(conn, path, action, include_soap_action=True, soap_action_override=None):
+    soapns = 'http://schemas.xmlsoap.org/soap/envelope/'
+    wpsns = 'urn:schemas-wifialliance-org:service:WFAWLANConfig:1'
+    ET.register_namespace('soapenv', soapns)
+    ET.register_namespace('wfa', wpsns)
+    attrib = {}
+    attrib['{%s}encodingStyle' % soapns] = 'http://schemas.xmlsoap.org/soap/encoding/'
+    root = ET.Element("{%s}Envelope" % soapns, attrib=attrib)
+    body = ET.SubElement(root, "{%s}Body" % soapns)
+    act = ET.SubElement(body, "{%s}%s" % (wpsns, action))
+    tree = ET.ElementTree(root)
+    soap = StringIO.StringIO()
+    tree.write(soap, xml_declaration=True, encoding='utf-8')
+
+    headers = { "Content-type": 'text/xml; charset="utf-8"' }
+    if include_soap_action:
+        headers["SOAPAction"] = '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#%s"' % action
+    elif soap_action_override:
+        headers["SOAPAction"] = soap_action_override
+    conn.request("POST", path, soap.getvalue(), headers)
+    return conn.getresponse()
+
+def test_ap_wps_upnp(dev, apdev):
+    """WPS AP and UPnP operations"""
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    location = ssdp_get_location(ap_uuid)
+    urls = upnp_get_urls(location)
+
+    conn = urllib.urlopen(urls['scpd_url'])
+    scpd = conn.read()
+
+    conn = urllib.urlopen(urlparse.urljoin(location, "unknown.html"))
+    if conn.getcode() != 404:
+        raise Exception("Unexpected HTTP response to GET unknown URL")
+
+    url = urlparse.urlparse(location)
+    conn = httplib.HTTPConnection(url.netloc)
+    #conn.set_debuglevel(1)
+    headers = { "Content-type": 'text/xml; charset="utf-8"',
+                "SOAPAction": '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#GetDeviceInfo"' }
+    conn.request("POST", "hello", "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 404:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    conn.request("UNKNOWN", "hello", "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 501:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    headers = { "Content-type": 'text/xml; charset="utf-8"',
+                "SOAPAction": '"urn:some-unknown-action#GetDeviceInfo"' }
+    ctrlurl = urlparse.urlparse(urls['control_url'])
+    conn.request("POST", ctrlurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 401:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("GetDeviceInfo without SOAPAction header")
+    resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo",
+                            include_soap_action=False)
+    if resp.status != 401:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("GetDeviceInfo with invalid SOAPAction header")
+    for act in [ "foo",
+                 "urn:schemas-wifialliance-org:service:WFAWLANConfig:1#GetDeviceInfo",
+                 '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1"',
+                 '"urn:schemas-wifialliance-org:service:WFAWLANConfig:123#GetDevice']:
+        resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo",
+                                include_soap_action=False,
+                                soap_action_override=act)
+        if resp.status != 401:
+            raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+    dev = resp.read()
+    if "NewDeviceInfo" not in dev:
+        raise Exception("Unexpected GetDeviceInfo response")
+
+    logger.debug("PutMessage without required parameters")
+    resp = upnp_soap_action(conn, ctrlurl.path, "PutMessage")
+    if resp.status != 600:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("PutWLANResponse without required parameters")
+    resp = upnp_soap_action(conn, ctrlurl.path, "PutWLANResponse")
+    if resp.status != 600:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("SetSelectedRegistrar from unregistered ER")
+    resp = upnp_soap_action(conn, ctrlurl.path, "SetSelectedRegistrar")
+    if resp.status != 501:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("Unknown action")
+    resp = upnp_soap_action(conn, ctrlurl.path, "Unknown")
+    if resp.status != 401:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+def test_ap_wps_upnp_subscribe(dev, apdev):
+    """WPS AP and UPnP event subscription"""
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hapd = add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    location = ssdp_get_location(ap_uuid)
+    urls = upnp_get_urls(location)
+    eventurl = urlparse.urlparse(urls['event_sub_url'])
+
+    url = urlparse.urlparse(location)
+    conn = httplib.HTTPConnection(url.netloc)
+    #conn.set_debuglevel(1)
+    headers = { "callback": '<http://127.0.0.1:12345/event>',
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", "hello", "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 412:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 412:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    headers = { "NT": "upnp:event",
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 412:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    headers = { "callback": '<http://127.0.0.1:12345/event>',
+                "NT": "upnp:foobar",
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 400:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("Valid subscription")
+    headers = { "callback": '<http://127.0.0.1:12345/event>',
+                "NT": "upnp:event",
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+    sid = resp.getheader("sid")
+    logger.debug("Subscription SID " + sid)
+
+    logger.debug("Invalid re-subscription")
+    headers = { "NT": "upnp:event",
+                "sid": "123456734567854",
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 400:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("Invalid re-subscription")
+    headers = { "NT": "upnp:event",
+                "sid": "uuid:123456734567854",
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 400:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("Invalid re-subscription")
+    headers = { "callback": '<http://127.0.0.1:12345/event>',
+                "NT": "upnp:event",
+                "sid": sid,
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 400:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("SID mismatch in re-subscription")
+    headers = { "NT": "upnp:event",
+                "sid": "uuid:4c2bca79-1ff4-4e43-85d4-952a2b8a51fb",
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 412:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("Valid re-subscription")
+    headers = { "NT": "upnp:event",
+                "sid": sid,
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+    sid2 = resp.getheader("sid")
+    logger.debug("Subscription SID " + sid2)
+
+    if sid != sid2:
+        raise Exception("Unexpected SID change")
+
+    logger.debug("Valid re-subscription")
+    headers = { "NT": "upnp:event",
+                "sid": "uuid: \t \t" + sid.split(':')[1],
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("Invalid unsubscription")
+    headers = { "sid": sid }
+    conn.request("UNSUBSCRIBE", "/hello", "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 412:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+    headers = { "foo": "bar" }
+    conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 412:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("Valid unsubscription")
+    headers = { "sid": sid }
+    conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("Unsubscription for not existing SID")
+    headers = { "sid": sid }
+    conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 412:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("Invalid unsubscription")
+    headers = { "sid": " \t \tfoo" }
+    conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 400:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("Invalid unsubscription")
+    headers = { "sid": "uuid:\t \tfoo" }
+    conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 400:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("Invalid unsubscription")
+    headers = { "NT": "upnp:event",
+                "sid": sid }
+    conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 400:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+    headers = { "callback": '<http://127.0.0.1:12345/event>',
+                "sid": sid }
+    conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 400:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.debug("Valid subscription with multiple callbacks")
+    headers = { "callback": '<http://127.0.0.1:12345/event> <http://127.0.0.1:12345/event>\t<http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event>',
+                "NT": "upnp:event",
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+    sid = resp.getheader("sid")
+    logger.debug("Subscription SID " + sid)
+
+    # Force subscription to be deleted due to errors
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    with alloc_fail(hapd, 1, "event_build_message"):
+        for i in range(10):
+            dev[1].dump_monitor()
+            dev[2].dump_monitor()
+            dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+            dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+            dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+            dev[1].request("WPS_CANCEL")
+            dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+            dev[2].request("WPS_CANCEL")
+            if i % 4 == 1:
+                time.sleep(1)
+            else:
+                time.sleep(0.1)
+    time.sleep(0.2)
+
+    headers = { "sid": sid }
+    conn.request("UNSUBSCRIBE", eventurl.path, "", headers)
+    resp = conn.getresponse()
+    if resp.status != 200 and resp.status != 412:
+        raise Exception("Unexpected HTTP response for UNSUBSCRIBE: %d" % resp.status)
+
+    headers = { "callback": '<http://127.0.0.1:12345/event>',
+                "NT": "upnp:event",
+                "timeout": "Second-1234" }
+    with alloc_fail(hapd, 1, "http_client_addr;event_send_start"):
+        conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+        resp = conn.getresponse()
+        if resp.status != 200:
+            raise Exception("Unexpected HTTP response for SUBSCRIBE: %d" % resp.status)
+        sid = resp.getheader("sid")
+        logger.debug("Subscription SID " + sid)
+
+    headers = { "sid": sid }
+    conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response for UNSUBSCRIBE: %d" % resp.status)
+
+    headers = { "callback": '<http://127.0.0.1:12345/event>',
+                "NT": "upnp:event",
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+    sid = resp.getheader("sid")
+    logger.debug("Subscription SID " + sid)
+
+    with alloc_fail(hapd, 1, "=event_add"):
+        for i in range(2):
+            dev[1].dump_monitor()
+            dev[2].dump_monitor()
+            dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+            dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+            dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+            dev[1].request("WPS_CANCEL")
+            dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+            dev[2].request("WPS_CANCEL")
+            if i == 0:
+                time.sleep(1)
+            else:
+                time.sleep(0.1)
+
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    with alloc_fail(hapd, 1, "wpabuf_dup;event_add"):
+        dev[1].dump_monitor()
+        dev[2].dump_monitor()
+        dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+        dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+        dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+        dev[1].request("WPS_CANCEL")
+        dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+        dev[2].request("WPS_CANCEL")
+        time.sleep(0.1)
+
+    with fail_test(hapd, 1, "os_get_random;uuid_make;subscription_start"):
+        conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+        resp = conn.getresponse()
+        if resp.status != 500:
+            raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    with alloc_fail(hapd, 1, "=subscription_start"):
+        conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+        resp = conn.getresponse()
+        if resp.status != 500:
+            raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    headers = { "callback": '',
+                "NT": "upnp:event",
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 500:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    headers = { "callback": ' <',
+                "NT": "upnp:event",
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 500:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    headers = { "callback": '<http://127.0.0.1:12345/event>',
+                "NT": "upnp:event",
+                "timeout": "Second-1234" }
+    with alloc_fail(hapd, 1, "wpabuf_alloc;subscription_first_event"):
+        conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+        resp = conn.getresponse()
+        if resp.status != 500:
+            raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    with alloc_fail(hapd, 1, "event_add;subscription_first_event"):
+        conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+        resp = conn.getresponse()
+        if resp.status != 500:
+            raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    with alloc_fail(hapd, 1, "subscr_addr_add_url"):
+        conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+        resp = conn.getresponse()
+        if resp.status != 500:
+            raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    with alloc_fail(hapd, 2, "subscr_addr_add_url"):
+        conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+        resp = conn.getresponse()
+        if resp.status != 500:
+            raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    for i in range(6):
+        headers = { "callback": '<http://127.0.0.1:%d/event>' % (12345 + i),
+                    "NT": "upnp:event",
+                    "timeout": "Second-1234" }
+        conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+        resp = conn.getresponse()
+        if resp.status != 200:
+            raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    with alloc_fail(hapd, 1, "=upnp_wps_device_send_wlan_event"):
+        dev[1].dump_monitor()
+        dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+        dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+        dev[1].request("WPS_CANCEL")
+        time.sleep(0.1)
+
+    with alloc_fail(hapd, 1, "wpabuf_alloc;upnp_wps_device_send_event"):
+        dev[1].dump_monitor()
+        dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+        dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+        dev[1].request("WPS_CANCEL")
+        time.sleep(0.1)
+
+    with alloc_fail(hapd, 1, "base64_encode;upnp_wps_device_send_wlan_event"):
+        dev[1].dump_monitor()
+        dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+        dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+        dev[1].request("WPS_CANCEL")
+        time.sleep(0.1)
+
+    hapd.disable()
+    with alloc_fail(hapd, 1, "get_netif_info"):
+        if "FAIL" not in hapd.request("ENABLE"):
+            raise Exception("ENABLE succeeded during OOM")
+
+def test_ap_wps_upnp_subscribe_events(dev, apdev):
+    """WPS AP and UPnP event subscription and many events"""
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hapd = add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    location = ssdp_get_location(ap_uuid)
+    urls = upnp_get_urls(location)
+    eventurl = urlparse.urlparse(urls['event_sub_url'])
+
+    class WPSERHTTPServer(SocketServer.StreamRequestHandler):
+        def handle(self):
+            data = self.rfile.readline().strip()
+            logger.debug(data)
+            self.wfile.write(gen_wps_event())
+
+    server = MyTCPServer(("127.0.0.1", 12345), WPSERHTTPServer)
+    server.timeout = 1
+
+    url = urlparse.urlparse(location)
+    conn = httplib.HTTPConnection(url.netloc)
+
+    headers = { "callback": '<http://127.0.0.1:12345/event>',
+                "NT": "upnp:event",
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+    sid = resp.getheader("sid")
+    logger.debug("Subscription SID " + sid)
+
+    # Fetch the first event message
+    server.handle_request()
+
+    # Force subscription event queue to reach the maximum length by generating
+    # new proxied events without the ER fetching any of the pending events.
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    for i in range(16):
+        dev[1].dump_monitor()
+        dev[2].dump_monitor()
+        dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+        dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+        dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+        dev[1].request("WPS_CANCEL")
+        dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+        dev[2].request("WPS_CANCEL")
+        if i % 4 == 1:
+            time.sleep(1)
+        else:
+            time.sleep(0.1)
+
+    hapd.request("WPS_PIN any 12345670")
+    dev[1].dump_monitor()
+    dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+    ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("WPS success not reported")
+
+    # Close the WPS ER HTTP server without fetching all the pending events.
+    # This tests hostapd code path that clears subscription and the remaining
+    # event queue when the interface is deinitialized.
+    server.handle_request()
+    server.server_close()
+
+    dev[1].wait_connected()
+
+def test_ap_wps_upnp_http_proto(dev, apdev):
+    """WPS AP and UPnP/HTTP protocol testing"""
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    location = ssdp_get_location(ap_uuid)
+
+    url = urlparse.urlparse(location)
+    conn = httplib.HTTPConnection(url.netloc, timeout=0.2)
+    #conn.set_debuglevel(1)
+
+    conn.request("HEAD", "hello")
+    resp = conn.getresponse()
+    if resp.status != 501:
+        raise Exception("Unexpected response to HEAD: " + str(resp.status))
+    conn.close()
+
+    for cmd in [ "PUT", "DELETE", "TRACE", "CONNECT", "M-SEARCH", "M-POST" ]:
+        try:
+            conn.request(cmd, "hello")
+            resp = conn.getresponse()
+        except Exception, e:
+            pass
+        conn.close()
+
+    headers = { "Content-Length": 'abc' }
+    conn.request("HEAD", "hello", "\r\n\r\n", headers)
+    try:
+        resp = conn.getresponse()
+    except Exception, e:
+        pass
+    conn.close()
+
+    headers = { "Content-Length": '-10' }
+    conn.request("HEAD", "hello", "\r\n\r\n", headers)
+    try:
+        resp = conn.getresponse()
+    except Exception, e:
+        pass
+    conn.close()
+
+    headers = { "Content-Length": '10000000000000' }
+    conn.request("HEAD", "hello", "\r\n\r\nhello", headers)
+    try:
+        resp = conn.getresponse()
+    except Exception, e:
+        pass
+    conn.close()
+
+    headers = { "Transfer-Encoding": 'abc' }
+    conn.request("HEAD", "hello", "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 501:
+        raise Exception("Unexpected response to HEAD: " + str(resp.status))
+    conn.close()
+
+    headers = { "Transfer-Encoding": 'chunked' }
+    conn.request("HEAD", "hello", "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 501:
+        raise Exception("Unexpected response to HEAD: " + str(resp.status))
+    conn.close()
+
+    # Too long a header
+    conn.request("HEAD", 5000 * 'A')
+    try:
+        resp = conn.getresponse()
+    except Exception, e:
+        pass
+    conn.close()
+
+    # Long URL but within header length limits
+    conn.request("HEAD", 3000 * 'A')
+    resp = conn.getresponse()
+    if resp.status != 501:
+        raise Exception("Unexpected response to HEAD: " + str(resp.status))
+    conn.close()
+
+    headers = { "Content-Length": '20' }
+    conn.request("POST", "hello", 10 * 'A' + "\r\n\r\n", headers)
+    try:
+        resp = conn.getresponse()
+    except Exception, e:
+        pass
+    conn.close()
+
+    conn.request("POST", "hello", 5000 * 'A' + "\r\n\r\n")
+    resp = conn.getresponse()
+    if resp.status != 404:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+    conn.close()
+
+    conn.request("POST", "hello", 60000 * 'A' + "\r\n\r\n")
+    try:
+        resp = conn.getresponse()
+    except Exception, e:
+        pass
+    conn.close()
+
+def test_ap_wps_upnp_http_proto_chunked(dev, apdev):
+    """WPS AP and UPnP/HTTP protocol testing for chunked encoding"""
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    location = ssdp_get_location(ap_uuid)
+
+    url = urlparse.urlparse(location)
+    conn = httplib.HTTPConnection(url.netloc)
+    #conn.set_debuglevel(1)
+
+    headers = { "Transfer-Encoding": 'chunked' }
+    conn.request("POST", "hello",
+                 "a\r\nabcdefghij\r\n" + "2\r\nkl\r\n" + "0\r\n\r\n",
+                 headers)
+    resp = conn.getresponse()
+    if resp.status != 404:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+    conn.close()
+
+    conn.putrequest("POST", "hello")
+    conn.putheader('Transfer-Encoding', 'chunked')
+    conn.endheaders()
+    conn.send("a\r\nabcdefghij\r\n")
+    time.sleep(0.1)
+    conn.send("2\r\nkl\r\n")
+    conn.send("0\r\n\r\n")
+    resp = conn.getresponse()
+    if resp.status != 404:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+    conn.close()
+
+    conn.putrequest("POST", "hello")
+    conn.putheader('Transfer-Encoding', 'chunked')
+    conn.endheaders()
+    completed = False
+    try:
+        for i in range(20000):
+            conn.send("1\r\nZ\r\n")
+        conn.send("0\r\n\r\n")
+        resp = conn.getresponse()
+        completed = True
+    except Exception, e:
+        pass
+    conn.close()
+    if completed:
+        raise Exception("Too long chunked request did not result in connection reset")
+
+    headers = { "Transfer-Encoding": 'chunked' }
+    conn.request("POST", "hello", "80000000\r\na", headers)
+    try:
+        resp = conn.getresponse()
+    except Exception, e:
+        pass
+    conn.close()
+
+    conn.request("POST", "hello", "10000000\r\na", headers)
+    try:
+        resp = conn.getresponse()
+    except Exception, e:
+        pass
+    conn.close()
+
+def test_ap_wps_disabled(dev, apdev):
+    """WPS operations while WPS is disabled"""
+    ssid = "test-wps-disabled"
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": ssid })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    if "FAIL" not in hapd.request("WPS_PBC"):
+        raise Exception("WPS_PBC succeeded unexpectedly")
+    if "FAIL" not in hapd.request("WPS_CANCEL"):
+        raise Exception("WPS_CANCEL succeeded unexpectedly")
+
+def test_ap_wps_mixed_cred(dev, apdev):
+    """WPS 2.0 STA merging mixed mode WPA/WPA2 credentials"""
+    ssid = "test-wps-wep"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "skip_cred_build": "1", "extra_cred": "wps-mixed-cred" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    hapd.request("WPS_PBC")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+    ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=30)
+    if ev is None:
+        raise Exception("WPS-SUCCESS event timed out")
+    nets = dev[0].list_networks()
+    if len(nets) != 1:
+        raise Exception("Unexpected number of network blocks")
+    id = nets[0]['id']
+    proto = dev[0].get_network(id, "proto")
+    if proto != "WPA RSN":
+        raise Exception("Unexpected merged proto field value: " + proto)
+    pairwise = dev[0].get_network(id, "pairwise")
+    if pairwise != "CCMP TKIP" and pairwise != "CCMP GCMP TKIP":
+        raise Exception("Unexpected merged pairwise field value: " + pairwise)
+
+def test_ap_wps_while_connected(dev, apdev):
+    """WPS PBC provisioning while connected to another AP"""
+    ssid = "test-wps-conf"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    hostapd.add_ap(apdev[1]['ifname'], { "ssid": "open" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+    logger.info("WPS provisioning step")
+    hapd.request("WPS_PBC")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+    dev[0].wait_connected(timeout=30)
+    status = dev[0].get_status()
+    if status['bssid'] != apdev[0]['bssid']:
+        raise Exception("Unexpected BSSID")
+
+def test_ap_wps_while_connected_no_autoconnect(dev, apdev):
+    """WPS PBC provisioning while connected to another AP and STA_AUTOCONNECT disabled"""
+    ssid = "test-wps-conf"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    hostapd.add_ap(apdev[1]['ifname'], { "ssid": "open" })
+
+    try:
+        dev[0].request("STA_AUTOCONNECT 0")
+        dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+        logger.info("WPS provisioning step")
+        hapd.request("WPS_PBC")
+        dev[0].dump_monitor()
+        dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+        dev[0].wait_connected(timeout=30)
+        status = dev[0].get_status()
+        if status['bssid'] != apdev[0]['bssid']:
+            raise Exception("Unexpected BSSID")
+    finally:
+        dev[0].request("STA_AUTOCONNECT 1")
+
+def test_ap_wps_from_event(dev, apdev):
+    """WPS PBC event on AP to enable PBC"""
+    ssid = "test-wps-conf"
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                            "wpa_passphrase": "12345678", "wpa": "2",
+                            "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].dump_monitor()
+    hapd.dump_monitor()
+    dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+
+    ev = hapd.wait_event(['WPS-ENROLLEE-SEEN'], timeout=15)
+    if ev is None:
+        raise Exception("No WPS-ENROLLEE-SEEN event on AP")
+    vals = ev.split(' ')
+    if vals[1] != dev[0].p2p_interface_addr():
+        raise Exception("Unexpected enrollee address: " + vals[1])
+    if vals[5] != '4':
+        raise Exception("Unexpected Device Password Id: " + vals[5])
+    hapd.request("WPS_PBC")
+    dev[0].wait_connected(timeout=30)
+
+def test_ap_wps_ap_scan_2(dev, apdev):
+    """AP_SCAN 2 for WPS"""
+    ssid = "test-wps-conf"
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                            "wpa_passphrase": "12345678", "wpa": "2",
+                            "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd.request("WPS_PBC")
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+
+    if "OK" not in wpas.request("AP_SCAN 2"):
+        raise Exception("Failed to set AP_SCAN 2")
+
+    wpas.flush_scan_cache()
+    wpas.scan_for_bss(apdev[0]['bssid'], freq="2412")
+    wpas.request("WPS_PBC " + apdev[0]['bssid'])
+    ev = wpas.wait_event(["WPS-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("WPS-SUCCESS event timed out")
+    wpas.wait_connected(timeout=30)
+    wpas.request("DISCONNECT")
+    wpas.request("BSS_FLUSH 0")
+    wpas.dump_monitor()
+    wpas.request("REASSOCIATE")
+    wpas.wait_connected(timeout=30)
+
+def test_ap_wps_eapol_workaround(dev, apdev):
+    """EAPOL workaround code path for 802.1X header length mismatch"""
+    ssid = "test-wps"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    bssid = apdev[0]['bssid']
+    hapd.request("SET ext_eapol_frame_io 1")
+    dev[0].request("SET ext_eapol_frame_io 1")
+    hapd.request("WPS_PBC")
+    dev[0].request("WPS_PBC")
+
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX from hostapd")
+
+    res = dev[0].request("EAPOL_RX " + bssid + " 020000040193000501FFFF")
+    if "OK" not in res:
+        raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+def test_ap_wps_iteration(dev, apdev):
+    """WPS PIN and iterate through APs without selected registrar"""
+    ssid = "test-wps-conf"
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                            "wpa_passphrase": "12345678", "wpa": "2",
+                            "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+
+    ssid2 = "test-wps-conf2"
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'],
+                           { "ssid": ssid2, "eap_server": "1", "wps_state": "2",
+                             "wpa_passphrase": "12345678", "wpa": "2",
+                             "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+    dev[0].dump_monitor()
+    pin = dev[0].request("WPS_PIN any")
+
+    # Wait for iteration through all WPS APs to happen before enabling any
+    # Registrar.
+    for i in range(2):
+        ev = dev[0].wait_event(["Associated with"], timeout=30)
+        if ev is None:
+            raise Exception("No association seen")
+        ev = dev[0].wait_event(["WPS-M2D"], timeout=10)
+        if ev is None:
+            raise Exception("No M2D from AP")
+        dev[0].wait_disconnected()
+
+    # Verify that each AP requested PIN
+    ev = hapd.wait_event(["WPS-PIN-NEEDED"], timeout=1)
+    if ev is None:
+        raise Exception("No WPS-PIN-NEEDED event from AP")
+    ev = hapd2.wait_event(["WPS-PIN-NEEDED"], timeout=1)
+    if ev is None:
+        raise Exception("No WPS-PIN-NEEDED event from AP2")
+
+    # Provide PIN to one of the APs and verify that connection gets formed
+    hapd.request("WPS_PIN any " + pin)
+    dev[0].wait_connected(timeout=30)
+
+def test_ap_wps_iteration_error(dev, apdev):
+    """WPS AP iteration on no Selected Registrar and error case with an AP"""
+    ssid = "test-wps-conf-pin"
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                            "wpa_passphrase": "12345678", "wpa": "2",
+                            "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                            "wps_independent": "1" })
+    hapd.request("SET ext_eapol_frame_io 1")
+    bssid = apdev[0]['bssid']
+    pin = dev[0].wps_read_pin()
+    dev[0].request("WPS_PIN any " + pin)
+
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+    if ev is None:
+        raise Exception("No EAPOL-TX (EAP-Request/Identity) from hostapd")
+    dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+    if ev is None:
+        raise Exception("No EAPOL-TX (EAP-WSC/Start) from hostapd")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+    if ev is None:
+        raise Exception("No CTRL-EVENT-EAP-STARTED")
+
+    # Do not forward any more EAPOL frames to test wpa_supplicant behavior for
+    # a case with an incorrectly behaving WPS AP.
+
+    # Start the real target AP and activate registrar on it.
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'],
+                          { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                            "wpa_passphrase": "12345678", "wpa": "2",
+                            "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                            "wps_independent": "1" })
+    hapd2.request("WPS_PIN any " + pin)
+
+    dev[0].wait_disconnected(timeout=15)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=15)
+    if ev is None:
+        raise Exception("No CTRL-EVENT-EAP-STARTED for the second AP")
+    ev = dev[0].wait_event(["WPS-CRED-RECEIVED"], timeout=15)
+    if ev is None:
+        raise Exception("No WPS-CRED-RECEIVED for the second AP")
+    dev[0].wait_connected(timeout=15)
+
+def test_ap_wps_priority(dev, apdev):
+    """WPS PIN provisioning with configured AP and wps_priority"""
+    ssid = "test-wps-conf-pin"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step")
+    pin = dev[0].wps_read_pin()
+    hapd.request("WPS_PIN any " + pin)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].dump_monitor()
+    try:
+        dev[0].request("SET wps_priority 6")
+        dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+        dev[0].wait_connected(timeout=30)
+        netw = dev[0].list_networks()
+        prio = dev[0].get_network(netw[0]['id'], 'priority')
+        if prio != '6':
+            raise Exception("Unexpected network priority: " + prio)
+    finally:
+        dev[0].request("SET wps_priority 0")
+
+def test_ap_wps_and_non_wps(dev, apdev):
+    """WPS and non-WPS AP in single hostapd process"""
+    params = { "ssid": "wps", "eap_server": "1", "wps_state": "1" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    params = { "ssid": "no wps" }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    appin = hapd.request("WPS_AP_PIN random")
+    if "FAIL" in appin:
+        raise Exception("Could not generate random AP PIN")
+    if appin not in hapd.request("WPS_AP_PIN get"):
+        raise Exception("Could not fetch current AP PIN")
+
+    if "FAIL" in hapd.request("WPS_PBC"):
+        raise Exception("WPS_PBC failed")
+    if "FAIL" in hapd.request("WPS_CANCEL"):
+        raise Exception("WPS_CANCEL failed")
+
+def test_ap_wps_init_oom(dev, apdev):
+    """Initial AP configuration and OOM during PSK generation"""
+    ssid = "test-wps"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "1" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    with alloc_fail(hapd, 1, "base64_encode;wps_build_cred"):
+        pin = dev[0].wps_read_pin()
+        hapd.request("WPS_PIN any " + pin)
+        dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+        dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+        dev[0].wait_disconnected()
+
+    hapd.request("WPS_PIN any " + pin)
+    dev[0].wait_connected(timeout=30)
+
+def test_ap_wps_er_oom(dev, apdev):
+    """WPS ER OOM in XML processing"""
+    try:
+        _test_ap_wps_er_oom(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+        dev[1].request("WPS_CANCEL")
+        dev[0].request("DISCONNECT")
+
+def _test_ap_wps_er_oom(dev, apdev):
+    ssid = "wps-er-ap-config"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "device_name": "Wireless AP", "manufacturer": "Company",
+                     "model_name": "WAP", "model_number": "123",
+                     "serial_number": "12345", "device_type": "6-0050F204-1",
+                     "os_version": "01020300",
+                     "config_methods": "label push_button",
+                     "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+
+    dev[0].connect(ssid, psk="12345678", scan_freq="2412")
+
+    with alloc_fail(dev[0], 1, "base64_decode;xml_get_base64_item"):
+        dev[0].request("WPS_ER_START ifname=lo")
+        ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=3)
+        if ev is not None:
+            raise Exception("Unexpected AP discovery")
+
+    dev[0].request("WPS_ER_STOP")
+    dev[0].request("WPS_ER_START ifname=lo")
+    ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=10)
+    if ev is None:
+        raise Exception("AP discovery timed out")
+
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    with alloc_fail(dev[0], 1, "base64_decode;xml_get_base64_item"):
+        dev[1].request("WPS_PBC " + apdev[0]['bssid'])
+        ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+        if ev is None:
+            raise Exception("PBC scan failed")
+        ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=15)
+        if ev is None:
+            raise Exception("Enrollee discovery timed out")
+
+def test_ap_wps_er_init_oom(dev, apdev):
+    """WPS ER and OOM during init"""
+    try:
+        _test_ap_wps_er_init_oom(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_init_oom(dev, apdev):
+    with alloc_fail(dev[0], 1, "wps_er_init"):
+        if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
+            raise Exception("WPS_ER_START succeeded during OOM")
+    with alloc_fail(dev[0], 1, "http_server_init"):
+        if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
+            raise Exception("WPS_ER_START succeeded during OOM")
+    with alloc_fail(dev[0], 2, "http_server_init"):
+        if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
+            raise Exception("WPS_ER_START succeeded during OOM")
+    with alloc_fail(dev[0], 1, "eloop_register_sock;wps_er_ssdp_init"):
+        if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
+            raise Exception("WPS_ER_START succeeded during OOM")
+    with fail_test(dev[0], 1, "os_get_random;wps_er_init"):
+        if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
+            raise Exception("WPS_ER_START succeeded during os_get_random failure")
+
+def test_ap_wps_er_init_fail(dev, apdev):
+    """WPS ER init failure"""
+    if "FAIL" not in dev[0].request("WPS_ER_START ifname=does-not-exist"):
+        dev[0].request("WPS_ER_STOP")
+        raise Exception("WPS_ER_START with non-existing ifname succeeded")
+
+def test_ap_wps_wpa_cli_action(dev, apdev, test_params):
+    """WPS events and wpa_cli action script"""
+    logdir = os.path.abspath(test_params['logdir'])
+    pidfile = os.path.join(logdir, 'ap_wps_wpa_cli_action.wpa_cli.pid')
+    logfile = os.path.join(logdir, 'ap_wps_wpa_cli_action.wpa_cli.res')
+    actionfile = os.path.join(logdir, 'ap_wps_wpa_cli_action.wpa_cli.action.sh')
+
+    with open(actionfile, 'w') as f:
+        f.write('#!/bin/sh\n')
+        f.write('echo $* >> %s\n' % logfile)
+        # Kill the process and wait some time before returning to allow all the
+        # pending events to be processed with some of this happening after the
+        # eloop SIGALRM signal has been scheduled.
+        f.write('if [ $2 = "WPS-SUCCESS" -a -r %s ]; then kill `cat %s`; sleep 1; fi\n' % (pidfile, pidfile))
+
+    os.chmod(actionfile, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC |
+             stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
+
+    ssid = "test-wps-conf"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    prg = os.path.join(test_params['logdir'],
+                       'alt-wpa_supplicant/wpa_supplicant/wpa_cli')
+    if not os.path.exists(prg):
+        prg = '../../wpa_supplicant/wpa_cli'
+    arg = [ prg, '-P', pidfile, '-B', '-i', dev[0].ifname, '-a', actionfile ]
+    subprocess.call(arg)
+
+    arg = [ 'ps', 'ax' ]
+    cmd = subprocess.Popen(arg, stdout=subprocess.PIPE)
+    out = cmd.communicate()[0]
+    cmd.wait()
+    logger.debug("Processes:\n" + out)
+    if "wpa_cli -P %s -B -i %s" % (pidfile, dev[0].ifname) not in out:
+        raise Exception("Did not see wpa_cli running")
+
+    hapd.request("WPS_PIN any 12345670")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].dump_monitor()
+    dev[0].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+    dev[0].wait_connected(timeout=30)
+
+    for i in range(30):
+        if not os.path.exists(pidfile):
+            break
+        time.sleep(0.1)
+
+    if not os.path.exists(logfile):
+        raise Exception("wpa_cli action results file not found")
+    with open(logfile, 'r') as f:
+        res = f.read()
+    if "WPS-SUCCESS" not in res:
+        raise Exception("WPS-SUCCESS event not seen in action file")
+
+    arg = [ 'ps', 'ax' ]
+    cmd = subprocess.Popen(arg, stdout=subprocess.PIPE)
+    out = cmd.communicate()[0]
+    cmd.wait()
+    logger.debug("Remaining processes:\n" + out)
+    if "wpa_cli -P %s -B -i %s" % (pidfile, dev[0].ifname) in out:
+        raise Exception("wpa_cli still running")
+
+    if os.path.exists(pidfile):
+        raise Exception("PID file not removed")
+
+def test_ap_wps_er_ssdp_proto(dev, apdev):
+    """WPS ER SSDP protocol testing"""
+    try:
+        _test_ap_wps_er_ssdp_proto(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_ssdp_proto(dev, apdev):
+    socket.setdefaulttimeout(1)
+    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.bind(("239.255.255.250", 1900))
+    if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo foo"):
+        raise Exception("Invalid filter accepted")
+    if "OK" not in dev[0].request("WPS_ER_START ifname=lo 1.2.3.4"):
+        raise Exception("WPS_ER_START with filter failed")
+    (msg,addr) = sock.recvfrom(1000)
+    logger.debug("Received SSDP message from %s: %s" % (str(addr), msg))
+    if "M-SEARCH" not in msg:
+        raise Exception("Not an M-SEARCH")
+    sock.sendto("FOO", addr)
+    time.sleep(0.1)
+    dev[0].request("WPS_ER_STOP")
+
+    dev[0].request("WPS_ER_START ifname=lo")
+    (msg,addr) = sock.recvfrom(1000)
+    logger.debug("Received SSDP message from %s: %s" % (str(addr), msg))
+    if "M-SEARCH" not in msg:
+        raise Exception("Not an M-SEARCH")
+    sock.sendto("FOO", addr)
+    sock.sendto("HTTP/1.1 200 OK\r\nFOO\r\n\r\n", addr)
+    sock.sendto("HTTP/1.1 200 OK\r\nNTS:foo\r\n\r\n", addr)
+    sock.sendto("HTTP/1.1 200 OK\r\nNTS:ssdp:byebye\r\n\r\n", addr)
+    sock.sendto("HTTP/1.1 200 OK\r\ncache-control:   foo=1\r\n\r\n", addr)
+    sock.sendto("HTTP/1.1 200 OK\r\ncache-control:   max-age=1\r\n\r\n", addr)
+    sock.sendto("HTTP/1.1 200 OK\r\nusn:\r\n\r\n", addr)
+    sock.sendto("HTTP/1.1 200 OK\r\nusn:foo\r\n\r\n", addr)
+    sock.sendto("HTTP/1.1 200 OK\r\nusn:   uuid:\r\n\r\n", addr)
+    sock.sendto("HTTP/1.1 200 OK\r\nusn:   uuid:     \r\n\r\n", addr)
+    sock.sendto("HTTP/1.1 200 OK\r\nusn:   uuid:     foo\r\n\r\n", addr)
+    sock.sendto("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\n\r\n", addr)
+    sock.sendto("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nNTS:ssdp:byebye\r\n\r\n", addr)
+    sock.sendto("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:foo\r\n\r\n", addr)
+    with alloc_fail(dev[0], 1, "wps_er_ap_add"):
+        sock.sendto("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:foo\r\ncache-control:max-age=1\r\n\r\n", addr)
+        time.sleep(0.1)
+    with alloc_fail(dev[0], 2, "wps_er_ap_add"):
+        sock.sendto("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:foo\r\ncache-control:max-age=1\r\n\r\n", addr)
+        time.sleep(0.1)
+
+    # Add an AP with bogus URL
+    sock.sendto("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:foo\r\ncache-control:max-age=1\r\n\r\n", addr)
+    # Update timeout on AP without updating URL
+    sock.sendto("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://127.0.0.1:12345/foo.xml\r\ncache-control:max-age=1\r\n\r\n", addr)
+    ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=5)
+    if ev is None:
+        raise Exception("No WPS-ER-AP-REMOVE event on max-age timeout")
+
+    # Add an AP with a valid URL (but no server listing to it)
+    sock.sendto("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://127.0.0.1:12345/foo.xml\r\ncache-control:max-age=1\r\n\r\n", addr)
+    ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=5)
+    if ev is None:
+        raise Exception("No WPS-ER-AP-REMOVE event on max-age timeout")
+
+    sock.close()
+
+wps_event_url = None
+
+def gen_upnp_info(eventSubURL='wps_event', controlURL='wps_control',
+                  udn='uuid:27ea801a-9e5c-4e73-bd82-f89cbcd10d7e'):
+    payload = '''<?xml version="1.0"?>
+<root xmlns="urn:schemas-upnp-org:device-1-0">
+<specVersion>
+<major>1</major>
+<minor>0</minor>
+</specVersion>
+<device>
+<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1</deviceType>
+<friendlyName>WPS Access Point</friendlyName>
+<manufacturer>Company</manufacturer>
+<modelName>WAP</modelName>
+<modelNumber>123</modelNumber>
+<serialNumber>12345</serialNumber>
+'''
+    if udn:
+        payload += '<UDN>' + udn + '</UDN>'
+    payload += '''<serviceList>
+<service>
+<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1</serviceType>
+<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>
+<SCPDURL>wps_scpd.xml</SCPDURL>
+'''
+    if controlURL:
+        payload += '<controlURL>' + controlURL + '</controlURL>\n'
+    if eventSubURL:
+        payload += '<eventSubURL>' + eventSubURL + '</eventSubURL>\n'
+    payload += '''</service>
+</serviceList>
+</device>
+</root>
+'''
+    hdr = 'HTTP/1.1 200 OK\r\n' + \
+          'Content-Type: text/xml; charset="utf-8"\r\n' + \
+          'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
+          'Connection: close\r\n' + \
+          'Content-Length: ' + str(len(payload)) + '\r\n' + \
+          'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
+    return hdr + payload
+
+def gen_wps_control(payload_override=None):
+    payload = '''<?xml version="1.0"?>
+<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+<s:Body>
+<u:GetDeviceInfoResponse xmlns:u="urn:schemas-wifialliance-org:service:WFAWLANConfig:1">
+<NewDeviceInfo>EEoAARAQIgABBBBHABAn6oAanlxOc72C+Jy80Q1+ECAABgIAAAADABAaABCJZ7DPtbU3Ust9
+Z3wJF07WEDIAwH45D3i1OqB7eJGwTzqeapS71h3KyXncK2xJZ+xqScrlorNEg6LijBJzG2Ca
++FZli0iliDJd397yAx/jk4nFXco3q5ylBSvSw9dhJ5u1xBKSnTilKGlUHPhLP75PUqM3fot9
+7zwtFZ4bx6x1sBA6oEe2d0aUJmLumQGCiKEIWlnxs44zego/2tAe81bDzdPBM7o5HH/FUhD+
+KoGzFXp51atP+1n9Vta6AkI0Vye99JKLcC6Md9dMJltSVBgd4Xc4lRAEAAIAIxAQAAIADRAN
+AAEBEAgAAgAEEEQAAQIQIQAHQ29tcGFueRAjAANXQVAQJAADMTIzEEIABTEyMzQ1EFQACAAG
+AFDyBAABEBEAC1dpcmVsZXNzIEFQEDwAAQEQAgACAAAQEgACAAAQCQACAAAQLQAEgQIDABBJ
+AAYANyoAASA=
+</NewDeviceInfo>
+</u:GetDeviceInfoResponse>
+</s:Body>
+</s:Envelope>
+'''
+    if payload_override:
+        payload = payload_override
+    hdr = 'HTTP/1.1 200 OK\r\n' + \
+          'Content-Type: text/xml; charset="utf-8"\r\n' + \
+          'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
+          'Connection: close\r\n' + \
+          'Content-Length: ' + str(len(payload)) + '\r\n' + \
+          'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
+    return hdr + payload
+
+def gen_wps_event(sid='uuid:7eb3342a-8a5f-47fe-a585-0785bfec6d8a'):
+    payload = ""
+    hdr = 'HTTP/1.1 200 OK\r\n' + \
+          'Content-Type: text/xml; charset="utf-8"\r\n' + \
+          'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
+          'Connection: close\r\n' + \
+          'Content-Length: ' + str(len(payload)) + '\r\n'
+    if sid:
+        hdr += 'SID: ' + sid + '\r\n'
+    hdr += 'Timeout: Second-1801\r\n' + \
+          'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
+    return hdr + payload
+
+class WPSAPHTTPServer(SocketServer.StreamRequestHandler):
+    def handle(self):
+        data = self.rfile.readline().strip()
+        logger.info("HTTP server received: " + data)
+        while True:
+            hdr = self.rfile.readline().strip()
+            if len(hdr) == 0:
+                break
+            logger.info("HTTP header: " + hdr)
+            if "CALLBACK:" in hdr:
+                global wps_event_url
+                wps_event_url = hdr.split(' ')[1].strip('<>')
+
+        if "GET /foo.xml" in data:
+            self.handle_upnp_info()
+        elif "POST /wps_control" in data:
+            self.handle_wps_control()
+        elif "SUBSCRIBE /wps_event" in data:
+            self.handle_wps_event()
+        else:
+            self.handle_others(data)
+
+    def handle_upnp_info(self):
+        self.wfile.write(gen_upnp_info())
+
+    def handle_wps_control(self):
+        self.wfile.write(gen_wps_control())
+
+    def handle_wps_event(self):
+        self.wfile.write(gen_wps_event())
+
+    def handle_others(self, data):
+        logger.info("Ignore HTTP request: " + data)
+
+class MyTCPServer(SocketServer.TCPServer):
+    def __init__(self, addr, handler):
+        self.allow_reuse_address = True
+        SocketServer.TCPServer.__init__(self, addr, handler)
+
+def wps_er_start(dev, http_server, max_age=1, wait_m_search=False,
+                 location_url=None):
+    socket.setdefaulttimeout(1)
+    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.bind(("239.255.255.250", 1900))
+    dev.request("WPS_ER_START ifname=lo")
+    for i in range(100):
+        (msg,addr) = sock.recvfrom(1000)
+        logger.debug("Received SSDP message from %s: %s" % (str(addr), msg))
+        if "M-SEARCH" in msg:
+            break
+        if not wait_m_search:
+            raise Exception("Not an M-SEARCH")
+        if i == 99:
+            raise Exception("No M-SEARCH seen")
+
+    # Add an AP with a valid URL and server listing to it
+    server = MyTCPServer(("127.0.0.1", 12345), http_server)
+    if not location_url:
+        location_url = 'http://127.0.0.1:12345/foo.xml'
+    sock.sendto("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:%s\r\ncache-control:max-age=%d\r\n\r\n" % (location_url, max_age), addr)
+    server.timeout = 1
+    return server,sock
+
+def wps_er_stop(dev, sock, server, on_alloc_fail=False):
+    sock.close()
+    server.server_close()
+
+    if on_alloc_fail:
+        done = False
+        for i in range(50):
+            res = dev.request("GET_ALLOC_FAIL")
+            if res.startswith("0:"):
+                done = True
+                break
+            time.sleep(0.1)
+        if not done:
+            raise Exception("No allocation failure reported")
+    else:
+        ev = dev.wait_event(["WPS-ER-AP-REMOVE"], timeout=5)
+        if ev is None:
+            raise Exception("No WPS-ER-AP-REMOVE event on max-age timeout")
+    dev.request("WPS_ER_STOP")
+
+def run_wps_er_proto_test(dev, handler, no_event_url=False, location_url=None):
+    try:
+        uuid = '27ea801a-9e5c-4e73-bd82-f89cbcd10d7e'
+        server,sock = wps_er_start(dev, handler, location_url=location_url)
+        global wps_event_url
+        wps_event_url = None
+        server.handle_request()
+        server.handle_request()
+        server.handle_request()
+        server.server_close()
+        if no_event_url:
+            if wps_event_url:
+                raise Exception("Received event URL unexpectedly")
+            return
+        if wps_event_url is None:
+            raise Exception("Did not get event URL")
+        logger.info("Event URL: " + wps_event_url)
+    finally:
+            dev.request("WPS_ER_STOP")
+
+def send_wlanevent(url, uuid, data, no_response=False):
+    conn = httplib.HTTPConnection(url.netloc)
+    payload = '''<?xml version="1.0" encoding="utf-8"?>
+<e:propertyset xmlns:e="urn:schemas-upnp-org:event-1-0">
+<e:property><STAStatus>1</STAStatus></e:property>
+<e:property><APStatus>1</APStatus></e:property>
+<e:property><WLANEvent>'''
+    payload += base64.b64encode(data)
+    payload += '</WLANEvent></e:property></e:propertyset>'
+    headers = { "Content-type": 'text/xml; charset="utf-8"',
+                "Server": "Unspecified, UPnP/1.0, Unspecified",
+                "HOST": url.netloc,
+                "NT": "upnp:event",
+                "SID": "uuid:" + uuid,
+                "SEQ": "0",
+                "Content-Length": str(len(payload)) }
+    conn.request("NOTIFY", url.path, payload, headers)
+    if no_response:
+        try:
+            conn.getresponse()
+        except Exception, e:
+            pass
+        return
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+def test_ap_wps_er_http_proto(dev, apdev):
+    """WPS ER HTTP protocol testing"""
+    try:
+        _test_ap_wps_er_http_proto(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_http_proto(dev, apdev):
+    uuid = '27ea801a-9e5c-4e73-bd82-f89cbcd10d7e'
+    server,sock = wps_er_start(dev[0], WPSAPHTTPServer, max_age=15)
+    global wps_event_url
+    wps_event_url = None
+    server.handle_request()
+    server.handle_request()
+    server.handle_request()
+    server.server_close()
+    if wps_event_url is None:
+        raise Exception("Did not get event URL")
+    logger.info("Event URL: " + wps_event_url)
+
+    ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=10)
+    if ev is None:
+        raise Exception("No WPS-ER-AP-ADD event")
+    if uuid not in ev:
+        raise Exception("UUID mismatch")
+
+    sock.close()
+
+    logger.info("Valid Probe Request notification")
+    url = urlparse.urlparse(wps_event_url)
+    conn = httplib.HTTPConnection(url.netloc)
+    payload = '''<?xml version="1.0" encoding="utf-8"?>
+<e:propertyset xmlns:e="urn:schemas-upnp-org:event-1-0">
+<e:property><STAStatus>1</STAStatus></e:property>
+<e:property><APStatus>1</APStatus></e:property>
+<e:property><WLANEvent>ATAyOjAwOjAwOjAwOjAwOjAwEEoAARAQOgABAhAIAAIxSBBHABA2LbR7pTpRkYj7VFi5hrLk
+EFQACAAAAAAAAAAAEDwAAQMQAgACAAAQCQACAAAQEgACAAAQIQABIBAjAAEgECQAASAQEQAI
+RGV2aWNlIEEQSQAGADcqAAEg
+</WLANEvent></e:property>
+</e:propertyset>
+'''
+    headers = { "Content-type": 'text/xml; charset="utf-8"',
+                "Server": "Unspecified, UPnP/1.0, Unspecified",
+                "HOST": url.netloc,
+                "NT": "upnp:event",
+                "SID": "uuid:" + uuid,
+                "SEQ": "0",
+                "Content-Length": str(len(payload)) }
+    conn.request("NOTIFY", url.path, payload, headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=5)
+    if ev is None:
+        raise Exception("No WPS-ER-ENROLLEE-ADD event")
+    if "362db47b-a53a-5191-88fb-5458b986b2e4" not in ev:
+        raise Exception("No Enrollee UUID match")
+
+    logger.info("Incorrect event URL AP id")
+    conn = httplib.HTTPConnection(url.netloc)
+    conn.request("NOTIFY", url.path + '123', payload, headers)
+    resp = conn.getresponse()
+    if resp.status != 404:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.info("Missing AP id")
+    conn = httplib.HTTPConnection(url.netloc)
+    conn.request("NOTIFY", '/event/' + url.path.split('/')[2],
+                 payload, headers)
+    time.sleep(0.1)
+
+    logger.info("Incorrect event URL event id")
+    conn = httplib.HTTPConnection(url.netloc)
+    conn.request("NOTIFY", '/event/123456789/123', payload, headers)
+    time.sleep(0.1)
+
+    logger.info("Incorrect event URL prefix")
+    conn = httplib.HTTPConnection(url.netloc)
+    conn.request("NOTIFY", '/foobar/123456789/123', payload, headers)
+    resp = conn.getresponse()
+    if resp.status != 404:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.info("Unsupported request")
+    conn = httplib.HTTPConnection(url.netloc)
+    conn.request("FOOBAR", '/foobar/123456789/123', payload, headers)
+    resp = conn.getresponse()
+    if resp.status != 501:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    logger.info("Unsupported request and OOM")
+    with alloc_fail(dev[0], 1, "wps_er_http_req"):
+        conn = httplib.HTTPConnection(url.netloc)
+        conn.request("FOOBAR", '/foobar/123456789/123', payload, headers)
+        time.sleep(0.5)
+
+    logger.info("Too short WLANEvent")
+    data = '\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("Invalid WLANEventMAC")
+    data = '\x00qwertyuiopasdfghjklzxcvbnm'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("Unknown WLANEventType")
+    data = '\xff02:00:00:00:00:00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("Probe Request notification without any attributes")
+    data = '\x0102:00:00:00:00:00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("Probe Request notification with invalid attribute")
+    data = '\x0102:00:00:00:00:00\xff'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message without any attributes")
+    data = '\x0202:00:00:00:00:00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message with invalid attribute")
+    data = '\x0202:00:00:00:00:00\xff'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message from new STA and not M1")
+    data = '\x0202:ff:ff:ff:ff:ff' + '\x10\x22\x00\x01\x05'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1")
+    data = '\x0202:00:00:00:00:00'
+    data += '\x10\x22\x00\x01\x04'
+    data += '\x10\x47\x00\x10' + 16*'\x00'
+    data += '\x10\x20\x00\x06\x02\x00\x00\x00\x00\x00'
+    data += '\x10\x1a\x00\x10' + 16*'\x00'
+    data += '\x10\x32\x00\xc0' + 192*'\x00'
+    data += '\x10\x04\x00\x02\x00\x00'
+    data += '\x10\x10\x00\x02\x00\x00'
+    data += '\x10\x0d\x00\x01\x00'
+    data += '\x10\x08\x00\x02\x00\x00'
+    data += '\x10\x44\x00\x01\x00'
+    data += '\x10\x21\x00\x00'
+    data += '\x10\x23\x00\x00'
+    data += '\x10\x24\x00\x00'
+    data += '\x10\x42\x00\x00'
+    data += '\x10\x54\x00\x08' + 8*'\x00'
+    data += '\x10\x11\x00\x00'
+    data += '\x10\x3c\x00\x01\x00'
+    data += '\x10\x02\x00\x02\x00\x00'
+    data += '\x10\x12\x00\x02\x00\x00'
+    data += '\x10\x09\x00\x02\x00\x00'
+    data += '\x10\x2d\x00\x04\x00\x00\x00\x00'
+    m1 = data
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: WSC_ACK")
+    data = '\x0202:00:00:00:00:00' + '\x10\x22\x00\x01\x0d'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1")
+    send_wlanevent(url, uuid, m1)
+
+    logger.info("EAP message: WSC_NACK")
+    data = '\x0202:00:00:00:00:00' + '\x10\x22\x00\x01\x0e'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 - Too long attribute values")
+    data = '\x0202:00:00:00:00:00'
+    data += '\x10\x11\x00\x21' + 33*'\x00'
+    data += '\x10\x45\x00\x21' + 33*'\x00'
+    data += '\x10\x42\x00\x21' + 33*'\x00'
+    data += '\x10\x24\x00\x21' + 33*'\x00'
+    data += '\x10\x23\x00\x21' + 33*'\x00'
+    data += '\x10\x21\x00\x41' + 65*'\x00'
+    data += '\x10\x49\x00\x09\x00\x37\x2a\x05\x02\x00\x00\x05\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing UUID-E")
+    data = '\x0202:00:00:00:00:00'
+    data += '\x10\x22\x00\x01\x04'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing MAC Address")
+    data += '\x10\x47\x00\x10' + 16*'\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Enrollee Nonce")
+    data += '\x10\x20\x00\x06\x02\x00\x00\x00\x00\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Public Key")
+    data += '\x10\x1a\x00\x10' + 16*'\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Authentication Type flags")
+    data += '\x10\x32\x00\xc0' + 192*'\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Encryption Type Flags")
+    data += '\x10\x04\x00\x02\x00\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Connection Type flags")
+    data += '\x10\x10\x00\x02\x00\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Config Methods")
+    data += '\x10\x0d\x00\x01\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Wi-Fi Protected Setup State")
+    data += '\x10\x08\x00\x02\x00\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Manufacturer")
+    data += '\x10\x44\x00\x01\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Model Name")
+    data += '\x10\x21\x00\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Model Number")
+    data += '\x10\x23\x00\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Serial Number")
+    data += '\x10\x24\x00\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Primary Device Type")
+    data += '\x10\x42\x00\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Device Name")
+    data += '\x10\x54\x00\x08' + 8*'\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing RF Bands")
+    data += '\x10\x11\x00\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Association State")
+    data += '\x10\x3c\x00\x01\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Device Password ID")
+    data += '\x10\x02\x00\x02\x00\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing Configuration Error")
+    data += '\x10\x12\x00\x02\x00\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("EAP message: M1 missing OS Version")
+    data += '\x10\x09\x00\x02\x00\x00'
+    send_wlanevent(url, uuid, data)
+
+    logger.info("Check max concurrent requests")
+    addr = (url.hostname, url.port)
+    socks = {}
+    for i in range(20):
+        socks[i] = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
+                                 socket.IPPROTO_TCP)
+        socks[i].connect(addr)
+    for i in range(20):
+        socks[i].send("GET / HTTP/1.1\r\n\r\n")
+    count = 0
+    for i in range(20):
+        try:
+            res = socks[i].recv(100)
+            if "HTTP/1" in res:
+                count += 1
+        except:
+            pass
+        socks[i].close()
+    logger.info("%d concurrent HTTP GET operations returned response" % count)
+    if count < 10:
+        raise Exception("Too few concurrent HTTP connections accepted")
+
+    logger.info("OOM in HTTP server")
+    for func in [ "http_request_init", "httpread_create",
+                  "eloop_register_timeout;httpread_create",
+                  "eloop_register_sock;httpread_create",
+                  "httpread_hdr_analyze" ]:
+        with alloc_fail(dev[0], 1, func):
+            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
+                                 socket.IPPROTO_TCP)
+            sock.connect(addr)
+            sock.send("GET / HTTP/1.1\r\n\r\n")
+            try:
+                sock.recv(100)
+            except:
+                pass
+            sock.close()
+
+    logger.info("Invalid HTTP header")
+    for req in [ " GET / HTTP/1.1\r\n\r\n",
+                 "HTTP/1.1 200 OK\r\n\r\n",
+                 "HTTP/\r\n\r\n",
+                 "GET %%a%aa% HTTP/1.1\r\n\r\n",
+                 "GET / HTTP/1.1\r\n FOO\r\n\r\n",
+                 "NOTIFY / HTTP/1.1\r\n" + 4097*'a' + '\r\n\r\n',
+                 "NOTIFY / HTTP/1.1\r\n\r\n" + 8193*'a',
+                 "POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n foo\r\n",
+                 "POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n1\r\nfoo\r\n",
+                 "POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n0\r\n",
+                 "POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n0\r\naa\ra\r\n\ra" ]:
+        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
+                             socket.IPPROTO_TCP)
+        sock.settimeout(0.1)
+        sock.connect(addr)
+        sock.send(req)
+        try:
+            sock.recv(100)
+        except:
+            pass
+        sock.close()
+
+    with alloc_fail(dev[0], 2, "httpread_read_handler"):
+        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
+                             socket.IPPROTO_TCP)
+        sock.connect(addr)
+        sock.send("NOTIFY / HTTP/1.1\r\n\r\n" + 4500*'a')
+        try:
+            sock.recv(100)
+        except:
+            pass
+        sock.close()
+
+    conn = httplib.HTTPConnection(url.netloc)
+    payload = '<foo'
+    headers = { "Content-type": 'text/xml; charset="utf-8"',
+                "Server": "Unspecified, UPnP/1.0, Unspecified",
+                "HOST": url.netloc,
+                "NT": "upnp:event",
+                "SID": "uuid:" + uuid,
+                "SEQ": "0",
+                "Content-Length": str(len(payload)) }
+    conn.request("NOTIFY", url.path, payload, headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    conn = httplib.HTTPConnection(url.netloc)
+    payload = '<WLANEvent foo></WLANEvent>'
+    headers = { "Content-type": 'text/xml; charset="utf-8"',
+                "Server": "Unspecified, UPnP/1.0, Unspecified",
+                "HOST": url.netloc,
+                "NT": "upnp:event",
+                "SID": "uuid:" + uuid,
+                "SEQ": "0",
+                "Content-Length": str(len(payload)) }
+    conn.request("NOTIFY", url.path, payload, headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    with alloc_fail(dev[0], 1, "xml_get_first_item"):
+        send_wlanevent(url, uuid, '')
+
+    with alloc_fail(dev[0], 1, "wpabuf_alloc_ext_data;xml_get_base64_item"):
+        send_wlanevent(url, uuid, 'foo')
+
+    for func in [ "wps_init",
+                  "wps_process_manufacturer",
+                  "wps_process_model_name",
+                  "wps_process_model_number",
+                  "wps_process_serial_number",
+                  "wps_process_dev_name" ]:
+        with alloc_fail(dev[0], 1, func):
+            send_wlanevent(url, uuid, m1)
+
+    with alloc_fail(dev[0], 1, "wps_er_http_resp_ok"):
+        send_wlanevent(url, uuid, m1, no_response=True)
+
+    with alloc_fail(dev[0], 1, "wps_er_http_resp_not_found"):
+        url2 = urlparse.urlparse(wps_event_url.replace('/event/', '/notfound/'))
+        send_wlanevent(url2, uuid, m1, no_response=True)
+
+    logger.info("EAP message: M1")
+    data = '\x0202:11:22:00:00:00'
+    data += '\x10\x22\x00\x01\x04'
+    data += '\x10\x47\x00\x10' + 16*'\x00'
+    data += '\x10\x20\x00\x06\x02\x00\x00\x00\x00\x00'
+    data += '\x10\x1a\x00\x10' + 16*'\x00'
+    data += '\x10\x32\x00\xc0' + 192*'\x00'
+    data += '\x10\x04\x00\x02\x00\x00'
+    data += '\x10\x10\x00\x02\x00\x00'
+    data += '\x10\x0d\x00\x01\x00'
+    data += '\x10\x08\x00\x02\x00\x00'
+    data += '\x10\x44\x00\x01\x00'
+    data += '\x10\x21\x00\x00'
+    data += '\x10\x23\x00\x00'
+    data += '\x10\x24\x00\x00'
+    data += '\x10\x42\x00\x00'
+    data += '\x10\x54\x00\x08' + 8*'\x00'
+    data += '\x10\x11\x00\x00'
+    data += '\x10\x3c\x00\x01\x00'
+    data += '\x10\x02\x00\x02\x00\x00'
+    data += '\x10\x12\x00\x02\x00\x00'
+    data += '\x10\x09\x00\x02\x00\x00'
+    data += '\x10\x2d\x00\x04\x00\x00\x00\x00'
+    dev[0].dump_monitor()
+    with alloc_fail(dev[0], 1, "wps_er_add_sta_data"):
+        send_wlanevent(url, uuid, data)
+        ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=0.1)
+        if ev is not None:
+            raise Exception("Unexpected enrollee add event")
+    send_wlanevent(url, uuid, data)
+    ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=2)
+    if ev is None:
+        raise Exception("Enrollee add event not seen")
+
+    with alloc_fail(dev[0], 1, "base64_encode;wps_er_soap_hdr"):
+        send_wlanevent(url, uuid, data)
+
+    with alloc_fail(dev[0], 1, "wpabuf_alloc;wps_er_soap_hdr"):
+        send_wlanevent(url, uuid, data)
+
+    with alloc_fail(dev[0], 1, "http_client_url_parse;wps_er_sta_send_msg"):
+        send_wlanevent(url, uuid, data)
+
+    with alloc_fail(dev[0], 1, "http_client_addr;wps_er_sta_send_msg"):
+        send_wlanevent(url, uuid, data)
+
+def test_ap_wps_er_http_proto_no_event_sub_url(dev, apdev):
+    """WPS ER HTTP protocol testing - no eventSubURL"""
+    class WPSAPHTTPServer_no_event_sub_url(WPSAPHTTPServer):
+        def handle_upnp_info(self):
+            self.wfile.write(gen_upnp_info(eventSubURL=None))
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_event_sub_url,
+                          no_event_url=True)
+
+def test_ap_wps_er_http_proto_event_sub_url_dns(dev, apdev):
+    """WPS ER HTTP protocol testing - DNS name in eventSubURL"""
+    class WPSAPHTTPServer_event_sub_url_dns(WPSAPHTTPServer):
+        def handle_upnp_info(self):
+            self.wfile.write(gen_upnp_info(eventSubURL='http://example.com/wps_event'))
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_event_sub_url_dns,
+                          no_event_url=True)
+
+def test_ap_wps_er_http_proto_subscribe_oom(dev, apdev):
+    """WPS ER HTTP protocol testing - subscribe OOM"""
+    try:
+        _test_ap_wps_er_http_proto_subscribe_oom(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_http_proto_subscribe_oom(dev, apdev):
+    tests = [ (1, "http_client_url_parse"),
+              (1, "wpabuf_alloc;wps_er_subscribe"),
+              (1, "http_client_addr"),
+              (1, "eloop_register_sock;http_client_addr"),
+              (1, "eloop_register_timeout;http_client_addr") ]
+    for count,func in tests:
+        with alloc_fail(dev[0], count, func):
+            server,sock = wps_er_start(dev[0], WPSAPHTTPServer)
+            server.handle_request()
+            server.handle_request()
+            wps_er_stop(dev[0], sock, server, on_alloc_fail=True)
+
+def test_ap_wps_er_http_proto_no_sid(dev, apdev):
+    """WPS ER HTTP protocol testing - no SID"""
+    class WPSAPHTTPServer_no_sid(WPSAPHTTPServer):
+        def handle_wps_event(self):
+            self.wfile.write(gen_wps_event(sid=None))
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_sid)
+
+def test_ap_wps_er_http_proto_invalid_sid_no_uuid(dev, apdev):
+    """WPS ER HTTP protocol testing - invalid SID - no UUID"""
+    class WPSAPHTTPServer_invalid_sid_no_uuid(WPSAPHTTPServer):
+        def handle_wps_event(self):
+            self.wfile.write(gen_wps_event(sid='FOO'))
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_invalid_sid_no_uuid)
+
+def test_ap_wps_er_http_proto_invalid_sid_uuid(dev, apdev):
+    """WPS ER HTTP protocol testing - invalid SID UUID"""
+    class WPSAPHTTPServer_invalid_sid_uuid(WPSAPHTTPServer):
+        def handle_wps_event(self):
+            self.wfile.write(gen_wps_event(sid='uuid:FOO'))
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_invalid_sid_uuid)
+
+def test_ap_wps_er_http_proto_subscribe_failing(dev, apdev):
+    """WPS ER HTTP protocol testing - SUBSCRIBE failing"""
+    class WPSAPHTTPServer_fail_subscribe(WPSAPHTTPServer):
+        def handle_wps_event(self):
+            payload = ""
+            hdr = 'HTTP/1.1 404 Not Found\r\n' + \
+                  'Content-Type: text/xml; charset="utf-8"\r\n' + \
+                  'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
+                  'Connection: close\r\n' + \
+                  'Content-Length: ' + str(len(payload)) + '\r\n' + \
+                  'Timeout: Second-1801\r\n' + \
+                  'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
+            self.wfile.write(hdr + payload)
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_fail_subscribe)
+
+def test_ap_wps_er_http_proto_subscribe_invalid_response(dev, apdev):
+    """WPS ER HTTP protocol testing - SUBSCRIBE and invalid response"""
+    class WPSAPHTTPServer_subscribe_invalid_response(WPSAPHTTPServer):
+        def handle_wps_event(self):
+            payload = ""
+            hdr = 'HTTP/1.1 FOO\r\n' + \
+                  'Content-Type: text/xml; charset="utf-8"\r\n' + \
+                  'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
+                  'Connection: close\r\n' + \
+                  'Content-Length: ' + str(len(payload)) + '\r\n' + \
+                  'Timeout: Second-1801\r\n' + \
+                  'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
+            self.wfile.write(hdr + payload)
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_subscribe_invalid_response)
+
+def test_ap_wps_er_http_proto_subscribe_invalid_response(dev, apdev):
+    """WPS ER HTTP protocol testing - SUBSCRIBE and invalid response"""
+    class WPSAPHTTPServer_invalid_m1(WPSAPHTTPServer):
+        def handle_wps_control(self):
+            payload = '''<?xml version="1.0"?>
+<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+<s:Body>
+<u:GetDeviceInfoResponse xmlns:u="urn:schemas-wifialliance-org:service:WFAWLANConfig:1">
+<NewDeviceInfo>Rk9P</NewDeviceInfo>
+</u:GetDeviceInfoResponse>
+</s:Body>
+</s:Envelope>
+'''
+            self.wfile.write(gen_wps_control(payload_override=payload))
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_invalid_m1, no_event_url=True)
+
+def test_ap_wps_er_http_proto_upnp_info_no_device(dev, apdev):
+    """WPS ER HTTP protocol testing - No device in UPnP info"""
+    class WPSAPHTTPServer_no_device(WPSAPHTTPServer):
+        def handle_upnp_info(self):
+            payload = '''<?xml version="1.0"?>
+<root xmlns="urn:schemas-upnp-org:device-1-0">
+<specVersion>
+<major>1</major>
+<minor>0</minor>
+</specVersion>
+</root>
+'''
+            hdr = 'HTTP/1.1 200 OK\r\n' + \
+                  'Content-Type: text/xml; charset="utf-8"\r\n' + \
+                  'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
+                  'Connection: close\r\n' + \
+                  'Content-Length: ' + str(len(payload)) + '\r\n' + \
+                  'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
+            self.wfile.write(hdr + payload)
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_device, no_event_url=True)
+
+def test_ap_wps_er_http_proto_upnp_info_no_device_type(dev, apdev):
+    """WPS ER HTTP protocol testing - No deviceType in UPnP info"""
+    class WPSAPHTTPServer_no_device(WPSAPHTTPServer):
+        def handle_upnp_info(self):
+            payload = '''<?xml version="1.0"?>
+<root xmlns="urn:schemas-upnp-org:device-1-0">
+<specVersion>
+<major>1</major>
+<minor>0</minor>
+</specVersion>
+<device>
+</device>
+</root>
+'''
+            hdr = 'HTTP/1.1 200 OK\r\n' + \
+                  'Content-Type: text/xml; charset="utf-8"\r\n' + \
+                  'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
+                  'Connection: close\r\n' + \
+                  'Content-Length: ' + str(len(payload)) + '\r\n' + \
+                  'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
+            self.wfile.write(hdr + payload)
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_device, no_event_url=True)
+
+def test_ap_wps_er_http_proto_upnp_info_invalid_udn_uuid(dev, apdev):
+    """WPS ER HTTP protocol testing - Invalid UDN UUID"""
+    class WPSAPHTTPServer_invalid_udn_uuid(WPSAPHTTPServer):
+        def handle_upnp_info(self):
+            self.wfile.write(gen_upnp_info(udn='uuid:foo'))
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_invalid_udn_uuid)
+
+def test_ap_wps_er_http_proto_no_control_url(dev, apdev):
+    """WPS ER HTTP protocol testing - no controlURL"""
+    class WPSAPHTTPServer_no_control_url(WPSAPHTTPServer):
+        def handle_upnp_info(self):
+            self.wfile.write(gen_upnp_info(controlURL=None))
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_control_url,
+                          no_event_url=True)
+
+def test_ap_wps_er_http_proto_control_url_dns(dev, apdev):
+    """WPS ER HTTP protocol testing - DNS name in controlURL"""
+    class WPSAPHTTPServer_control_url_dns(WPSAPHTTPServer):
+        def handle_upnp_info(self):
+            self.wfile.write(gen_upnp_info(controlURL='http://example.com/wps_control'))
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_control_url_dns,
+                          no_event_url=True)
+
+def test_ap_wps_http_timeout(dev, apdev):
+    """WPS AP/ER and HTTP timeout"""
+    try:
+        _test_ap_wps_http_timeout(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_http_timeout(dev, apdev):
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    location = ssdp_get_location(ap_uuid)
+    url = urlparse.urlparse(location)
+    addr = (url.hostname, url.port)
+    logger.debug("Open HTTP connection to hostapd, but do not complete request")
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
+                         socket.IPPROTO_TCP)
+    sock.connect(addr)
+    sock.send("G")
+
+    class DummyServer(SocketServer.StreamRequestHandler):
+        def handle(self):
+            logger.debug("DummyServer - start 31 sec wait")
+            time.sleep(31)
+            logger.debug("DummyServer - wait done")
+
+    logger.debug("Start WPS ER")
+    server,sock2 = wps_er_start(dev[0], DummyServer, max_age=40,
+                                wait_m_search=True)
+
+    logger.debug("Start server to accept, but not complete, HTTP connection from WPS ER")
+    # This will wait for 31 seconds..
+    server.handle_request()
+
+    logger.debug("Complete HTTP connection with hostapd (that should have already closed the connection)")
+    try:
+        sock.send("ET / HTTP/1.1\r\n\r\n")
+        res = sock.recv(100)
+        sock.close()
+    except:
+        pass
+
+def test_ap_wps_er_url_parse(dev, apdev):
+    """WPS ER and URL parsing special cases"""
+    try:
+        _test_ap_wps_er_url_parse(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_url_parse(dev, apdev):
+    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+    sock.settimeout(1)
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.bind(("239.255.255.250", 1900))
+    dev[0].request("WPS_ER_START ifname=lo")
+    (msg,addr) = sock.recvfrom(1000)
+    logger.debug("Received SSDP message from %s: %s" % (str(addr), msg))
+    if "M-SEARCH" not in msg:
+        raise Exception("Not an M-SEARCH")
+    sock.sendto("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://127.0.0.1\r\ncache-control:max-age=1\r\n\r\n", addr)
+    ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=2)
+    sock.sendto("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://127.0.0.1/:foo\r\ncache-control:max-age=1\r\n\r\n", addr)
+    ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=2)
+    sock.sendto("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://255.255.255.255:0/foo.xml\r\ncache-control:max-age=1\r\n\r\n", addr)
+    ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=2)
+
+    sock.close()
+
+def test_ap_wps_er_link_update(dev, apdev):
+    """WPS ER and link update special cases"""
+    class WPSAPHTTPServer_link_update(WPSAPHTTPServer):
+        def handle_upnp_info(self):
+            self.wfile.write(gen_upnp_info(controlURL='/wps_control'))
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_link_update)
+
+    class WPSAPHTTPServer_link_update2(WPSAPHTTPServer):
+        def handle_others(self, data):
+            if "GET / " in data:
+                self.wfile.write(gen_upnp_info(controlURL='/wps_control'))
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_link_update2,
+                          location_url='http://127.0.0.1:12345')
+
+def test_ap_wps_er_http_client(dev, apdev):
+    """WPS ER and HTTP client special cases"""
+    with alloc_fail(dev[0], 1, "http_link_update"):
+        run_wps_er_proto_test(dev[0], WPSAPHTTPServer)
+
+    with alloc_fail(dev[0], 1, "wpabuf_alloc;http_client_url"):
+        run_wps_er_proto_test(dev[0], WPSAPHTTPServer, no_event_url=True)
+
+    with alloc_fail(dev[0], 1, "httpread_create;http_client_tx_ready"):
+        run_wps_er_proto_test(dev[0], WPSAPHTTPServer, no_event_url=True)
+
+    class WPSAPHTTPServer_req_as_resp(WPSAPHTTPServer):
+        def handle_upnp_info(self):
+            self.wfile.write("GET / HTTP/1.1\r\n\r\n")
+    run_wps_er_proto_test(dev[0], WPSAPHTTPServer_req_as_resp,
+                          no_event_url=True)
+
+def test_ap_wps_init_oom(dev, apdev):
+    """wps_init OOM cases"""
+    ssid = "test-wps"
+    appin = "12345670"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "ap_pin": appin }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    pin = dev[0].wps_read_pin()
+
+    with alloc_fail(hapd, 1, "wps_init"):
+        hapd.request("WPS_PIN any " + pin)
+        dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+        dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+        ev = hapd.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+        if ev is None:
+            raise Exception("No EAP failure reported")
+        dev[0].request("WPS_CANCEL")
+
+    with alloc_fail(dev[0], 2, "wps_init"):
+        hapd.request("WPS_PIN any " + pin)
+        dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+        dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+        ev = hapd.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+        if ev is None:
+            raise Exception("No EAP failure reported")
+        dev[0].request("WPS_CANCEL")
+
+    with alloc_fail(dev[0], 2, "wps_init"):
+        hapd.request("WPS_PBC")
+        dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+        dev[0].request("WPS_PBC %s" % (apdev[0]['bssid']))
+        ev = hapd.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+        if ev is None:
+            raise Exception("No EAP failure reported")
+        dev[0].request("WPS_CANCEL")
+
+    dev[0].dump_monitor()
+    new_ssid = "wps-new-ssid"
+    new_passphrase = "1234567890"
+    with alloc_fail(dev[0], 3, "wps_init"):
+        dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK", "CCMP",
+                       new_passphrase, no_wait=True)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+        if ev is None:
+            raise Exception("No EAP failure reported")
+
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_invalid_assoc_req_elem(dev, apdev):
+    """WPS and invalid IE in Association Request frame"""
+    ssid = "test-wps"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    pin = "12345670"
+    hapd.request("WPS_PIN any " + pin)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    try:
+        dev[0].request("VENDOR_ELEM_ADD 13 dd050050f20410")
+        dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+        for i in range(5):
+            ev = hapd.wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=10)
+            if ev and "vendor=14122" in ev:
+                break
+        if ev is None or "vendor=14122" not in ev:
+            raise Exception("EAP-WSC not started")
+        dev[0].request("WPS_CANCEL")
+    finally:
+        dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+def test_ap_wps_pbc_pin_mismatch(dev, apdev):
+    """WPS PBC/PIN mismatch"""
+    ssid = "test-wps"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd.request("SET wps_version_number 0x10")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    hapd.request("WPS_PBC")
+    pin = dev[0].wps_read_pin()
+    dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+    if ev is None:
+        raise Exception("Scan did not complete")
+    dev[0].request("WPS_CANCEL")
+
+    hapd.request("WPS_CANCEL")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_ie_invalid(dev, apdev):
+    """WPS PIN attempt with AP that has invalid WSC IE"""
+    ssid = "test-wps"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "vendor_elements": "dd050050f20410" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = { 'ssid': "another", "vendor_elements": "dd050050f20410" }
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    pin = dev[0].wps_read_pin()
+    dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+    if ev is None:
+        raise Exception("Scan did not complete")
+    dev[0].request("WPS_CANCEL")
+
+def test_ap_wps_scan_prio_order(dev, apdev):
+    """WPS scan priority ordering"""
+    ssid = "test-wps"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = { 'ssid': "another", "vendor_elements": "dd050050f20410" }
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+    pin = dev[0].wps_read_pin()
+    dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+    if ev is None:
+        raise Exception("Scan did not complete")
+    dev[0].request("WPS_CANCEL")
+
+def test_ap_wps_probe_req_ie_oom(dev, apdev):
+    """WPS ProbeReq IE OOM"""
+    ssid = "test-wps"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    pin = dev[0].wps_read_pin()
+    hapd.request("WPS_PIN any " + pin)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    with alloc_fail(dev[0], 1, "wps_build_probe_req_ie"):
+        dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+        ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=10)
+        if ev is None:
+            raise Exception("Association not seen")
+    dev[0].request("WPS_CANCEL")
+
+    with alloc_fail(dev[0], 1, "wps_ie_encapsulate"):
+        dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+        ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=10)
+        if ev is None:
+            raise Exception("Association not seen")
+    dev[0].request("WPS_CANCEL")
+
+def test_ap_wps_assoc_req_ie_oom(dev, apdev):
+    """WPS AssocReq IE OOM"""
+    ssid = "test-wps"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    pin = dev[0].wps_read_pin()
+    hapd.request("WPS_PIN any " + pin)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    with alloc_fail(dev[0], 1, "wps_build_assoc_req_ie"):
+        dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+        ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=10)
+        if ev is None:
+            raise Exception("Association not seen")
+    dev[0].request("WPS_CANCEL")
+
+def test_ap_wps_assoc_resp_ie_oom(dev, apdev):
+    """WPS AssocResp IE OOM"""
+    ssid = "test-wps"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    pin = dev[0].wps_read_pin()
+    hapd.request("WPS_PIN any " + pin)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    with alloc_fail(hapd, 1, "wps_build_assoc_resp_ie"):
+        dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+        ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=10)
+        if ev is None:
+            raise Exception("Association not seen")
+    dev[0].request("WPS_CANCEL")
+
+def test_ap_wps_bss_info_errors(dev, apdev):
+    """WPS BSS info errors"""
+    params = { "ssid": "1",
+               "vendor_elements": "dd0e0050f20410440001ff101100010a" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    params = { 'ssid': "2", "vendor_elements": "dd050050f20410" }
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    logger.info("BSS: " + str(bss))
+    if "wps_state" in bss:
+        raise Exception("Unexpected wps_state in BSS info")
+    if 'wps_device_name' not in bss:
+        raise Exception("No wps_device_name in BSS info")
+    if bss['wps_device_name'] != '_':
+        raise Exception("Unexpected wps_device_name value")
+    bss = dev[0].get_bss(apdev[1]['bssid'])
+    logger.info("BSS: " + str(bss))
+
+    with alloc_fail(dev[0], 1, "=wps_attr_text"):
+        bss = dev[0].get_bss(apdev[0]['bssid'])
+        logger.info("BSS(OOM): " + str(bss))
+
+def wps_run_pbc_fail_ap(apdev, dev, hapd):
+    hapd.request("WPS_PBC")
+    dev.scan_for_bss(apdev['bssid'], freq="2412")
+    dev.request("WPS_PBC " + apdev['bssid'])
+    ev = dev.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+    if ev is None:
+        raise Exception("No EAP failure reported")
+    dev.request("WPS_CANCEL")
+    dev.wait_disconnected()
+    for i in range(5):
+        try:
+            dev.flush_scan_cache()
+            break
+        except Exception, e:
+            if str(e).startswith("Failed to trigger scan"):
+                # Try again
+                time.sleep(1)
+            else:
+                raise
+
+def wps_run_pbc_fail(apdev, dev):
+    hapd = wps_start_ap(apdev)
+    wps_run_pbc_fail_ap(apdev, dev, hapd)
+
+def test_ap_wps_pk_oom(dev, apdev):
+    """WPS and public key OOM"""
+    with alloc_fail(dev[0], 1, "wps_build_public_key"):
+        wps_run_pbc_fail(apdev[0], dev[0])
+
+def test_ap_wps_pk_oom_ap(dev, apdev):
+    """WPS and public key OOM on AP"""
+    hapd = wps_start_ap(apdev[0])
+    with alloc_fail(hapd, 1, "wps_build_public_key"):
+        wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
+
+def test_ap_wps_encr_oom_ap(dev, apdev):
+    """WPS and encrypted settings decryption OOM on AP"""
+    hapd = wps_start_ap(apdev[0])
+    pin = dev[0].wps_read_pin()
+    hapd.request("WPS_PIN any " + pin)
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    with alloc_fail(hapd, 1, "wps_decrypt_encr_settings"):
+        dev[0].request("WPS_PIN " + apdev[0]['bssid'] + " " + pin)
+        ev = hapd.wait_event(["WPS-FAIL"], timeout=10)
+        if ev is None:
+            raise Exception("No WPS-FAIL reported")
+        dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+
+def test_ap_wps_encr_no_random_ap(dev, apdev):
+    """WPS and no random data available for encryption on AP"""
+    hapd = wps_start_ap(apdev[0])
+    with fail_test(hapd, 1, "os_get_random;wps_build_encr_settings"):
+        wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
+
+def test_ap_wps_e_hash_no_random_sta(dev, apdev):
+    """WPS and no random data available for e-hash on STA"""
+    with fail_test(dev[0], 1, "os_get_random;wps_build_e_hash"):
+        wps_run_pbc_fail(apdev[0], dev[0])
+
+def test_ap_wps_m1_no_random(dev, apdev):
+    """WPS and no random for M1 on STA"""
+    with fail_test(dev[0], 1, "os_get_random;wps_build_m1"):
+        wps_run_pbc_fail(apdev[0], dev[0])
+
+def test_ap_wps_m1_oom(dev, apdev):
+    """WPS and OOM for M1 on STA"""
+    with alloc_fail(dev[0], 1, "wps_build_m1"):
+        wps_run_pbc_fail(apdev[0], dev[0])
+
+def test_ap_wps_m3_oom(dev, apdev):
+    """WPS and OOM for M3 on STA"""
+    with alloc_fail(dev[0], 1, "wps_build_m3"):
+        wps_run_pbc_fail(apdev[0], dev[0])
+
+def test_ap_wps_m5_oom(dev, apdev):
+    """WPS and OOM for M5 on STA"""
+    hapd = wps_start_ap(apdev[0])
+    hapd.request("WPS_PBC")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    for i in range(1, 3):
+        with alloc_fail(dev[0], i, "wps_build_m5"):
+            dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+            if ev is None:
+                raise Exception("No EAP failure reported")
+            dev[0].request("WPS_CANCEL")
+            dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m5_no_random(dev, apdev):
+    """WPS and no random for M5 on STA"""
+    with fail_test(dev[0], 1,
+                   "os_get_random;wps_build_encr_settings;wps_build_m5"):
+        wps_run_pbc_fail(apdev[0], dev[0])
+
+def test_ap_wps_m7_oom(dev, apdev):
+    """WPS and OOM for M7 on STA"""
+    hapd = wps_start_ap(apdev[0])
+    hapd.request("WPS_PBC")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    for i in range(1, 3):
+        with alloc_fail(dev[0], i, "wps_build_m7"):
+            dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+            if ev is None:
+                raise Exception("No EAP failure reported")
+            dev[0].request("WPS_CANCEL")
+            dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m7_no_random(dev, apdev):
+    """WPS and no random for M7 on STA"""
+    with fail_test(dev[0], 1,
+                   "os_get_random;wps_build_encr_settings;wps_build_m7"):
+        wps_run_pbc_fail(apdev[0], dev[0])
+
+def test_ap_wps_wsc_done_oom(dev, apdev):
+    """WPS and OOM for WSC_Done on STA"""
+    with alloc_fail(dev[0], 1, "wps_build_wsc_done"):
+        wps_run_pbc_fail(apdev[0], dev[0])
+
+def test_ap_wps_random_psk_fail(dev, apdev):
+    """WPS and no random for PSK on AP"""
+    ssid = "test-wps"
+    pskfile = "/tmp/ap_wps_per_enrollee_psk.psk_file"
+    appin = "12345670"
+    try:
+        os.remove(pskfile)
+    except:
+        pass
+
+    try:
+        with open(pskfile, "w") as f:
+            f.write("# WPA PSKs\n")
+
+        params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                   "wpa": "2", "wpa_key_mgmt": "WPA-PSK",
+                   "rsn_pairwise": "CCMP", "ap_pin": appin,
+                   "wpa_psk_file": pskfile }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+        dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+        with fail_test(hapd, 1, "os_get_random;wps_build_cred_network_key"):
+            dev[0].request("WPS_REG " + apdev[0]['bssid'] + " " + appin)
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+            if ev is None:
+                raise Exception("No EAP failure reported")
+            dev[0].request("WPS_CANCEL")
+        dev[0].wait_disconnected()
+
+        with fail_test(hapd, 1, "os_get_random;wps_build_cred"):
+            wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
+
+        with alloc_fail(hapd, 1, "wps_build_cred"):
+            wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
+
+        with alloc_fail(hapd, 2, "wps_build_cred"):
+            wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
+    finally:
+        os.remove(pskfile)
+
+def wps_ext_eap_identity_req(dev, hapd, bssid):
+    logger.debug("EAP-Identity/Request")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX from hostapd")
+    res = dev.request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+    if "OK" not in res:
+        raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+def wps_ext_eap_identity_resp(hapd, dev, addr):
+    ev = dev.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+    res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+    if "OK" not in res:
+        raise Exception("EAPOL_RX to hostapd failed")
+
+def wps_ext_eap_wsc(dst, src, src_addr, msg):
+    logger.debug(msg)
+    ev = src.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    res = dst.request("EAPOL_RX " + src_addr + " " + ev.split(' ')[2])
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+
+def wps_start_ext(apdev, dev, pbc=False, pin=None):
+    addr = dev.own_addr()
+    bssid = apdev['bssid']
+    ssid = "test-wps-conf"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
+    hapd = hostapd.add_ap(apdev['ifname'], params)
+
+    if pbc:
+        hapd.request("WPS_PBC")
+    else:
+        if pin is None:
+            pin = dev.wps_read_pin()
+        hapd.request("WPS_PIN any " + pin)
+    dev.scan_for_bss(bssid, freq="2412")
+    hapd.request("SET ext_eapol_frame_io 1")
+    dev.request("SET ext_eapol_frame_io 1")
+
+    if pbc:
+        dev.request("WPS_PBC " + bssid)
+    else:
+        dev.request("WPS_PIN " + bssid + " " + pin)
+    return addr,bssid,hapd
+
+def wps_auth_corrupt(dst, src, addr):
+    ev = src.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    src.request("SET ext_eapol_frame_io 0")
+    dst.request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[-24:-16] != '10050008':
+        raise Exception("Could not find Authenticator attribute")
+    # Corrupt Authenticator value
+    msg = msg[:-1] + '%x' % ((int(msg[-1], 16) + 1) % 16)
+    res = dst.request("EAPOL_RX " + addr + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+
+def wps_fail_finish(hapd, dev, fail_str):
+    ev = hapd.wait_event(["WPS-FAIL"], timeout=5)
+    if ev is None:
+        raise Exception("WPS-FAIL not indicated")
+    if fail_str not in ev:
+        raise Exception("Unexpected WPS-FAIL value: " + ev)
+    dev.request("WPS_CANCEL")
+    dev.wait_disconnected()
+
+def wps_auth_corrupt_from_ap(dev, hapd, bssid, fail_str):
+    wps_auth_corrupt(dev, hapd, bssid)
+    wps_fail_finish(hapd, dev, fail_str)
+
+def wps_auth_corrupt_to_ap(dev, hapd, addr, fail_str):
+    wps_auth_corrupt(hapd, dev, addr)
+    wps_fail_finish(hapd, dev, fail_str)
+
+def test_ap_wps_authenticator_mismatch_m2(dev, apdev):
+    """WPS and Authenticator attribute mismatch in M2"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    wps_auth_corrupt_from_ap(dev[0], hapd, bssid, "msg=5")
+
+def test_ap_wps_authenticator_mismatch_m3(dev, apdev):
+    """WPS and Authenticator attribute mismatch in M3"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
+    logger.debug("M3")
+    wps_auth_corrupt_to_ap(dev[0], hapd, addr, "msg=7")
+
+def test_ap_wps_authenticator_mismatch_m4(dev, apdev):
+    """WPS and Authenticator attribute mismatch in M4"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
+    logger.debug("M4")
+    wps_auth_corrupt_from_ap(dev[0], hapd, bssid, "msg=8")
+
+def test_ap_wps_authenticator_mismatch_m5(dev, apdev):
+    """WPS and Authenticator attribute mismatch in M5"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "M4")
+    logger.debug("M5")
+    wps_auth_corrupt_to_ap(dev[0], hapd, addr, "msg=9")
+
+def test_ap_wps_authenticator_mismatch_m6(dev, apdev):
+    """WPS and Authenticator attribute mismatch in M6"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "M4")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M5")
+    logger.debug("M6")
+    wps_auth_corrupt_from_ap(dev[0], hapd, bssid, "msg=10")
+
+def test_ap_wps_authenticator_mismatch_m7(dev, apdev):
+    """WPS and Authenticator attribute mismatch in M7"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "M4")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M5")
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "M6")
+    logger.debug("M7")
+    wps_auth_corrupt_to_ap(dev[0], hapd, addr, "msg=11")
+
+def test_ap_wps_authenticator_mismatch_m8(dev, apdev):
+    """WPS and Authenticator attribute mismatch in M8"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "M4")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M5")
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "M6")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M7")
+    logger.debug("M8")
+    wps_auth_corrupt_from_ap(dev[0], hapd, bssid, "msg=12")
+
+def test_ap_wps_authenticator_missing_m2(dev, apdev):
+    """WPS and Authenticator attribute missing from M2"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[-24:-16] != '10050008':
+        raise Exception("Could not find Authenticator attribute")
+    # Remove Authenticator value
+    msg = msg[:-24]
+    mlen = "%04x" % (int(msg[4:8], 16) - 12)
+    msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    wps_fail_finish(hapd, dev[0], "msg=5")
+
+def test_ap_wps_m2_dev_passwd_id_p2p(dev, apdev):
+    """WPS and M2 with different Device Password ID (P2P)"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[722:730] != '10120002':
+        raise Exception("Could not find Device Password ID attribute")
+    # Replace Device Password ID value. This will fail Authenticator check, but
+    # allows the code path in wps_process_dev_pw_id() to be checked from debug
+    # log.
+    msg = msg[0:730] + "0005" + msg[734:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    wps_fail_finish(hapd, dev[0], "msg=5")
+
+def test_ap_wps_m2_dev_passwd_id_change_pin_to_pbc(dev, apdev):
+    """WPS and M2 with different Device Password ID (PIN to PBC)"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[722:730] != '10120002':
+        raise Exception("Could not find Device Password ID attribute")
+    # Replace Device Password ID value (PIN --> PBC). This will be rejected.
+    msg = msg[0:730] + "0004" + msg[734:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    wps_fail_finish(hapd, dev[0], "msg=5")
+
+def test_ap_wps_m2_dev_passwd_id_change_pbc_to_pin(dev, apdev):
+    """WPS and M2 with different Device Password ID (PBC to PIN)"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[722:730] != '10120002':
+        raise Exception("Could not find Device Password ID attribute")
+    # Replace Device Password ID value. This will fail Authenticator check, but
+    # allows the code path in wps_process_dev_pw_id() to be checked from debug
+    # log.
+    msg = msg[0:730] + "0000" + msg[734:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    wps_fail_finish(hapd, dev[0], "msg=5")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_missing_dev_passwd_id(dev, apdev):
+    """WPS and M2 without Device Password ID"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[722:730] != '10120002':
+        raise Exception("Could not find Device Password ID attribute")
+    # Remove Device Password ID value. This will fail Authenticator check, but
+    # allows the code path in wps_process_dev_pw_id() to be checked from debug
+    # log.
+    mlen = "%04x" % (int(msg[4:8], 16) - 6)
+    msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:722] + msg[734:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    wps_fail_finish(hapd, dev[0], "msg=5")
+
+def test_ap_wps_m2_missing_registrar_nonce(dev, apdev):
+    """WPS and M2 without Registrar Nonce"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[96:104] != '10390010':
+        raise Exception("Could not find Registrar Nonce attribute")
+    # Remove Registrar Nonce. This will fail Authenticator check, but
+    # allows the code path in wps_process_registrar_nonce() to be checked from
+    # the debug log.
+    mlen = "%04x" % (int(msg[4:8], 16) - 20)
+    msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:96] + msg[136:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+    if ev is None:
+        raise Exception("Disconnect event not seen")
+    dev[0].request("WPS_CANCEL")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_missing_enrollee_nonce(dev, apdev):
+    """WPS and M2 without Enrollee Nonce"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[56:64] != '101a0010':
+        raise Exception("Could not find enrollee Nonce attribute")
+    # Remove Enrollee Nonce. This will fail Authenticator check, but
+    # allows the code path in wps_process_enrollee_nonce() to be checked from
+    # the debug log.
+    mlen = "%04x" % (int(msg[4:8], 16) - 20)
+    msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:56] + msg[96:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+    if ev is None:
+        raise Exception("Disconnect event not seen")
+    dev[0].request("WPS_CANCEL")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_missing_uuid_r(dev, apdev):
+    """WPS and M2 without UUID-R"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[136:144] != '10480010':
+        raise Exception("Could not find enrollee Nonce attribute")
+    # Remove UUID-R. This will fail Authenticator check, but allows the code
+    # path in wps_process_uuid_r() to be checked from the debug log.
+    mlen = "%04x" % (int(msg[4:8], 16) - 20)
+    msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:136] + msg[176:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+    if ev is None:
+        raise Exception("Disconnect event not seen")
+    dev[0].request("WPS_CANCEL")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_invalid(dev, apdev):
+    """WPS and M2 parsing failure"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[136:144] != '10480010':
+        raise Exception("Could not find enrollee Nonce attribute")
+    # Remove UUID-R. This will fail Authenticator check, but allows the code
+    # path in wps_process_uuid_r() to be checked from the debug log.
+    mlen = "%04x" % (int(msg[4:8], 16) - 1)
+    msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:-2]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+    if ev is None:
+        raise Exception("Disconnect event not seen")
+    dev[0].request("WPS_CANCEL")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_missing_msg_type(dev, apdev):
+    """WPS and M2 without Message Type"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[46:54] != '10220001':
+        raise Exception("Could not find Message Type attribute")
+    # Remove Message Type. This will fail Authenticator check, but allows the
+    # code path in wps_process_wsc_msg() to be checked from the debug log.
+    mlen = "%04x" % (int(msg[4:8], 16) - 5)
+    msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:46] + msg[56:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+    if ev is None:
+        raise Exception("Disconnect event not seen")
+    dev[0].request("WPS_CANCEL")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_unknown_msg_type(dev, apdev):
+    """WPS and M2 but unknown Message Type"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[46:54] != '10220001':
+        raise Exception("Could not find Message Type attribute")
+    # Replace Message Type value. This will be rejected.
+    msg = msg[0:54] + "00" + msg[56:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+    if ev is None:
+        raise Exception("Disconnect event not seen")
+    dev[0].request("WPS_CANCEL")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_unknown_opcode(dev, apdev):
+    """WPS and M2 but unknown opcode"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    # Replace opcode. This will be discarded in EAP-WSC processing.
+    msg = msg[0:32] + "00" + msg[34:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_unknown_opcode2(dev, apdev):
+    """WPS and M2 but unknown opcode (WSC_Start)"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    # Replace opcode. This will be discarded in EAP-WSC processing.
+    msg = msg[0:32] + "01" + msg[34:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_unknown_opcode3(dev, apdev):
+    """WPS and M2 but unknown opcode (WSC_Done)"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    # Replace opcode. This will be discarded in WPS Enrollee processing.
+    msg = msg[0:32] + "05" + msg[34:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def wps_m2_but_other(dev, apdev, title, msgtype):
+    addr,bssid,hapd = wps_start_ext(apdev, dev)
+    wps_ext_eap_identity_req(dev, hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev, addr)
+    wps_ext_eap_wsc(dev, hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev, addr, "M1")
+    logger.debug(title)
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev.request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[46:54] != '10220001':
+        raise Exception("Could not find Message Type attribute")
+    # Replace Message Type value. This will be rejected.
+    msg = msg[0:54] + msgtype + msg[56:]
+    res = dev.request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = dev.wait_event(["WPS-FAIL"], timeout=5)
+    if ev is None:
+        raise Exception("WPS-FAIL event not seen")
+    dev.request("WPS_CANCEL")
+    dev.wait_disconnected()
+
+def wps_m4_but_other(dev, apdev, title, msgtype):
+    addr,bssid,hapd = wps_start_ext(apdev, dev)
+    wps_ext_eap_identity_req(dev, hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev, addr)
+    wps_ext_eap_wsc(dev, hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev, addr, "M1")
+    wps_ext_eap_wsc(dev, hapd, bssid, "M2")
+    wps_ext_eap_wsc(hapd, dev, addr, "M3")
+    logger.debug(title)
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev.request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[46:54] != '10220001':
+        raise Exception("Could not find Message Type attribute")
+    # Replace Message Type value. This will be rejected.
+    msg = msg[0:54] + msgtype + msg[56:]
+    res = dev.request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = hapd.wait_event(["WPS-FAIL"], timeout=5)
+    if ev is None:
+        raise Exception("WPS-FAIL event not seen")
+    dev.request("WPS_CANCEL")
+    dev.wait_disconnected()
+
+def test_ap_wps_m2_msg_type_m4(dev, apdev):
+    """WPS and M2 but Message Type M4"""
+    wps_m2_but_other(dev[0], apdev[0], "M2/M4", "08")
+
+def test_ap_wps_m2_msg_type_m6(dev, apdev):
+    """WPS and M2 but Message Type M6"""
+    wps_m2_but_other(dev[0], apdev[0], "M2/M6", "0a")
+
+def test_ap_wps_m2_msg_type_m8(dev, apdev):
+    """WPS and M2 but Message Type M8"""
+    wps_m2_but_other(dev[0], apdev[0], "M2/M8", "0c")
+
+def test_ap_wps_m4_msg_type_m2(dev, apdev):
+    """WPS and M4 but Message Type M2"""
+    wps_m4_but_other(dev[0], apdev[0], "M4/M2", "05")
+
+def test_ap_wps_m4_msg_type_m2d(dev, apdev):
+    """WPS and M4 but Message Type M2D"""
+    wps_m4_but_other(dev[0], apdev[0], "M4/M2D", "06")
+
+def test_ap_wps_config_methods(dev, apdev):
+    """WPS configuration method parsing"""
+    ssid = "test-wps-conf"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+               "config_methods": "ethernet display ext_nfc_token int_nfc_token physical_display physical_push_button" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+               "config_methods": "display push_button" }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+def test_ap_wps_set_selected_registrar_proto(dev, apdev):
+    """WPS UPnP SetSelectedRegistrar protocol testing"""
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hapd = add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    location = ssdp_get_location(ap_uuid)
+    urls = upnp_get_urls(location)
+    eventurl = urlparse.urlparse(urls['event_sub_url'])
+    ctrlurl = urlparse.urlparse(urls['control_url'])
+    url = urlparse.urlparse(location)
+    conn = httplib.HTTPConnection(url.netloc)
+
+    class WPSERHTTPServer(SocketServer.StreamRequestHandler):
+        def handle(self):
+            data = self.rfile.readline().strip()
+            logger.debug(data)
+            self.wfile.write(gen_wps_event())
+
+    server = MyTCPServer(("127.0.0.1", 12345), WPSERHTTPServer)
+    server.timeout = 1
+
+    headers = { "callback": '<http://127.0.0.1:12345/event>',
+                "NT": "upnp:event",
+                "timeout": "Second-1234" }
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+    sid = resp.getheader("sid")
+    logger.debug("Subscription SID " + sid)
+    server.handle_request()
+
+    tests = [ (500, "10"),
+              (200, "104a000110" + "1041000101" + "101200020000" +
+               "105300023148" +
+               "1049002c00372a0001200124111111111111222222222222333333333333444444444444555555555555666666666666" +
+               "10480010362db47ba53a519188fb5458b986b2e4"),
+              (200, "104a000110" + "1041000100" + "101200020000" +
+               "105300020000"),
+              (200, "104a000110" + "1041000100"),
+              (200, "104a000110") ]
+    for status,test in tests:
+        tlvs = binascii.unhexlify(test)
+        newmsg = base64.b64encode(tlvs)
+        msg = '<?xml version="1.0"?>\n'
+        msg += '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'
+        msg += '<s:Body>'
+        msg += '<u:SetSelectedRegistrar xmlns:u="urn:schemas-wifialliance-org:service:WFAWLANConfig:1">'
+        msg += '<NewMessage>'
+        msg += newmsg
+        msg += "</NewMessage></u:SetSelectedRegistrar></s:Body></s:Envelope>"
+        headers = { "Content-type": 'text/xml; charset="utf-8"' }
+        headers["SOAPAction"] = '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#%s"' % "SetSelectedRegistrar"
+        conn.request("POST", ctrlurl.path, msg, headers)
+        resp = conn.getresponse()
+        if resp.status != status:
+            raise Exception("Unexpected HTTP response: %d (expected %d)" % (resp.status, status))
+
+def test_ap_wps_adv_oom(dev, apdev):
+    """WPS AP and advertisement OOM"""
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hapd = add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    with alloc_fail(hapd, 1, "=msearchreply_state_machine_start"):
+        ssdp_send_msearch("urn:schemas-wifialliance-org:service:WFAWLANConfig:1",
+                          no_recv=True)
+        time.sleep(0.2)
+
+    with alloc_fail(hapd, 1, "eloop_register_timeout;msearchreply_state_machine_start"):
+        ssdp_send_msearch("urn:schemas-wifialliance-org:service:WFAWLANConfig:1",
+                          no_recv=True)
+        time.sleep(0.2)
+
+    with alloc_fail(hapd, 1,
+                    "next_advertisement;advertisement_state_machine_stop"):
+        hapd.disable()
+
+    with alloc_fail(hapd, 1, "ssdp_listener_start"):
+        if "FAIL" not in hapd.request("ENABLE"):
+            raise Exception("ENABLE succeeded during OOM")
+
+def test_wps_config_methods(dev):
+    """WPS config method update"""
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    if "OK" not in wpas.request("SET config_methods display label"):
+        raise Exception("Failed to set config_methods")
+    if wpas.request("GET config_methods").strip() != "display label":
+        raise Exception("config_methods were not updated")
+    if "OK" not in wpas.request("SET config_methods "):
+        raise Exception("Failed to clear config_methods")
+    if wpas.request("GET config_methods").strip() != "":
+        raise Exception("config_methods were not cleared")
+
+WPS_VENDOR_ID_WFA = 14122
+WPS_VENDOR_TYPE = 1
+
+# EAP-WSC Op-Code values
+WSC_Start = 0x01
+WSC_ACK = 0x02
+WSC_NACK = 0x03
+WSC_MSG = 0x04
+WSC_Done = 0x05
+WSC_FRAG_ACK = 0x06
+
+ATTR_AP_CHANNEL = 0x1001
+ATTR_ASSOC_STATE = 0x1002
+ATTR_AUTH_TYPE = 0x1003
+ATTR_AUTH_TYPE_FLAGS = 0x1004
+ATTR_AUTHENTICATOR = 0x1005
+ATTR_CONFIG_METHODS = 0x1008
+ATTR_CONFIG_ERROR = 0x1009
+ATTR_CONFIRM_URL4 = 0x100a
+ATTR_CONFIRM_URL6 = 0x100b
+ATTR_CONN_TYPE = 0x100c
+ATTR_CONN_TYPE_FLAGS = 0x100d
+ATTR_CRED = 0x100e
+ATTR_ENCR_TYPE = 0x100f
+ATTR_ENCR_TYPE_FLAGS = 0x1010
+ATTR_DEV_NAME = 0x1011
+ATTR_DEV_PASSWORD_ID = 0x1012
+ATTR_E_HASH1 = 0x1014
+ATTR_E_HASH2 = 0x1015
+ATTR_E_SNONCE1 = 0x1016
+ATTR_E_SNONCE2 = 0x1017
+ATTR_ENCR_SETTINGS = 0x1018
+ATTR_ENROLLEE_NONCE = 0x101a
+ATTR_FEATURE_ID = 0x101b
+ATTR_IDENTITY = 0x101c
+ATTR_IDENTITY_PROOF = 0x101d
+ATTR_KEY_WRAP_AUTH = 0x101e
+ATTR_KEY_ID = 0x101f
+ATTR_MAC_ADDR = 0x1020
+ATTR_MANUFACTURER = 0x1021
+ATTR_MSG_TYPE = 0x1022
+ATTR_MODEL_NAME = 0x1023
+ATTR_MODEL_NUMBER = 0x1024
+ATTR_NETWORK_INDEX = 0x1026
+ATTR_NETWORK_KEY = 0x1027
+ATTR_NETWORK_KEY_INDEX = 0x1028
+ATTR_NEW_DEVICE_NAME = 0x1029
+ATTR_NEW_PASSWORD = 0x102a
+ATTR_OOB_DEVICE_PASSWORD = 0x102c
+ATTR_OS_VERSION = 0x102d
+ATTR_POWER_LEVEL = 0x102f
+ATTR_PSK_CURRENT = 0x1030
+ATTR_PSK_MAX = 0x1031
+ATTR_PUBLIC_KEY = 0x1032
+ATTR_RADIO_ENABLE = 0x1033
+ATTR_REBOOT = 0x1034
+ATTR_REGISTRAR_CURRENT = 0x1035
+ATTR_REGISTRAR_ESTABLISHED = 0x1036
+ATTR_REGISTRAR_LIST = 0x1037
+ATTR_REGISTRAR_MAX = 0x1038
+ATTR_REGISTRAR_NONCE = 0x1039
+ATTR_REQUEST_TYPE = 0x103a
+ATTR_RESPONSE_TYPE = 0x103b
+ATTR_RF_BANDS = 0x103c
+ATTR_R_HASH1 = 0x103d
+ATTR_R_HASH2 = 0x103e
+ATTR_R_SNONCE1 = 0x103f
+ATTR_R_SNONCE2 = 0x1040
+ATTR_SELECTED_REGISTRAR = 0x1041
+ATTR_SERIAL_NUMBER = 0x1042
+ATTR_WPS_STATE = 0x1044
+ATTR_SSID = 0x1045
+ATTR_TOTAL_NETWORKS = 0x1046
+ATTR_UUID_E = 0x1047
+ATTR_UUID_R = 0x1048
+ATTR_VENDOR_EXT = 0x1049
+ATTR_VERSION = 0x104a
+ATTR_X509_CERT_REQ = 0x104b
+ATTR_X509_CERT = 0x104c
+ATTR_EAP_IDENTITY = 0x104d
+ATTR_MSG_COUNTER = 0x104e
+ATTR_PUBKEY_HASH = 0x104f
+ATTR_REKEY_KEY = 0x1050
+ATTR_KEY_LIFETIME = 0x1051
+ATTR_PERMITTED_CFG_METHODS = 0x1052
+ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053
+ATTR_PRIMARY_DEV_TYPE = 0x1054
+ATTR_SECONDARY_DEV_TYPE_LIST = 0x1055
+ATTR_PORTABLE_DEV = 0x1056
+ATTR_AP_SETUP_LOCKED = 0x1057
+ATTR_APPLICATION_EXT = 0x1058
+ATTR_EAP_TYPE = 0x1059
+ATTR_IV = 0x1060
+ATTR_KEY_PROVIDED_AUTO = 0x1061
+ATTR_802_1X_ENABLED = 0x1062
+ATTR_APPSESSIONKEY = 0x1063
+ATTR_WEPTRANSMITKEY = 0x1064
+ATTR_REQUESTED_DEV_TYPE = 0x106a
+
+# Message Type
+WPS_Beacon = 0x01
+WPS_ProbeRequest = 0x02
+WPS_ProbeResponse = 0x03
+WPS_M1 = 0x04
+WPS_M2 = 0x05
+WPS_M2D = 0x06
+WPS_M3 = 0x07
+WPS_M4 = 0x08
+WPS_M5 = 0x09
+WPS_M6 = 0x0a
+WPS_M7 = 0x0b
+WPS_M8 = 0x0c
+WPS_WSC_ACK = 0x0d
+WPS_WSC_NACK = 0x0e
+WPS_WSC_DONE = 0x0f
+
+def get_wsc_msg(dev):
+    ev = dev.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    data = binascii.unhexlify(ev.split(' ')[2])
+    msg = {}
+
+    # Parse EAPOL header
+    if len(data) < 4:
+        raise Exception("No room for EAPOL header")
+    version,type,length = struct.unpack('>BBH', data[0:4])
+    msg['eapol_version'] = version
+    msg['eapol_type'] = type
+    msg['eapol_length'] = length
+    data = data[4:]
+    if length != len(data):
+        raise Exception("EAPOL header length mismatch (%d != %d)" % (length, len(data)))
+    if type != 0:
+        raise Exception("Unexpected EAPOL header type: %d" % type)
+
+    # Parse EAP header
+    if len(data) < 4:
+        raise Exception("No room for EAP header")
+    code,identifier,length = struct.unpack('>BBH', data[0:4])
+    msg['eap_code'] = code
+    msg['eap_identifier'] = identifier
+    msg['eap_length'] = length
+    data = data[4:]
+    if msg['eapol_length'] != msg['eap_length']:
+        raise Exception("EAP header length mismatch (%d != %d)" % (msg['eapol_length'], length))
+
+    # Parse EAP expanded header
+    if len(data) < 1:
+        raise Exception("No EAP type included")
+    msg['eap_type'], = struct.unpack('B', data[0])
+    data = data[1:]
+
+    if msg['eap_type'] == 254:
+        if len(data) < 3 + 4:
+            raise Exception("Truncated EAP expanded header")
+        msg['eap_vendor_id'], msg['eap_vendor_type'] = struct.unpack('>LL', '\0' + data[0:7])
+        data = data[7:]
+    else:
+        raise Exception("Unexpected EAP type")
+
+    if msg['eap_vendor_id'] != WPS_VENDOR_ID_WFA:
+        raise Exception("Unexpected Vendor-Id")
+    if msg['eap_vendor_type'] != WPS_VENDOR_TYPE:
+        raise Exception("Unexpected Vendor-Type")
+
+    # Parse EAP-WSC header
+    if len(data) < 2:
+        raise Exception("Truncated EAP-WSC header")
+    msg['wsc_opcode'], msg['wsc_flags'] = struct.unpack('BB', data[0:2])
+    data = data[2:]
+
+    # Parse WSC attributes
+    msg['raw_attrs'] = data
+    attrs = {}
+    while len(data) > 0:
+        if len(data) < 4:
+            raise Exception("Truncated attribute header")
+        attr,length = struct.unpack('>HH', data[0:4])
+        data = data[4:]
+        if length > len(data):
+            raise Exception("Truncated attribute 0x%04x" % attr)
+        attrs[attr] = data[0:length]
+        data = data[length:]
+    msg['wsc_attrs'] = attrs
+
+    if ATTR_MSG_TYPE in attrs:
+        msg['wsc_msg_type'], = struct.unpack('B', attrs[ATTR_MSG_TYPE])
+
+    return msg
+
+def recv_wsc_msg(dev, opcode, msg_type):
+    msg = get_wsc_msg(dev)
+    if msg['wsc_opcode'] != opcode or msg['wsc_msg_type'] != msg_type:
+        raise Exception("Unexpected Op-Code/MsgType")
+    return msg, msg['wsc_attrs'], msg['raw_attrs']
+
+def build_wsc_attr(attr, payload):
+    return struct.pack('>HH', attr, len(payload)) + payload
+
+def build_attr_msg_type(msg_type):
+    return build_wsc_attr(ATTR_MSG_TYPE, struct.pack('B', msg_type))
+
+def build_eap_wsc(eap_code, eap_id, payload, opcode=WSC_MSG):
+    length = 4 + 8 + 2 + len(payload)
+    # EAPOL header
+    msg = struct.pack('>BBH', 2, 0, length)
+    # EAP header
+    msg += struct.pack('>BBH', eap_code, eap_id, length)
+    # EAP expanded header for EAP-WSC
+    msg += struct.pack('B', 254)
+    msg += struct.pack('>L', WPS_VENDOR_ID_WFA)[1:4]
+    msg += struct.pack('>L', WPS_VENDOR_TYPE)
+    # EAP-WSC header
+    msg += struct.pack('BB', opcode, 0)
+    # WSC attributes
+    msg += payload
+    return msg
+
+def build_eap_success(eap_id):
+    length = 4
+    # EAPOL header
+    msg = struct.pack('>BBH', 2, 0, length)
+    # EAP header
+    msg += struct.pack('>BBH', 3, eap_id, length)
+    return msg
+
+def build_eap_failure(eap_id):
+    length = 4
+    # EAPOL header
+    msg = struct.pack('>BBH', 2, 0, length)
+    # EAP header
+    msg += struct.pack('>BBH', 4, eap_id, length)
+    return msg
+
+def send_wsc_msg(dev, src, msg):
+    res = dev.request("EAPOL_RX " + src + " " + binascii.hexlify(msg))
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+
+group_5_prime = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF
+group_5_generator = 2
+
+def wsc_kdf(key, label, bits):
+    result = ''
+    i = 1
+    while len(result) * 8 < bits:
+        data = struct.pack('>L', i) + label + struct.pack('>L', bits)
+        m = hmac.new(key, data, hashlib.sha256)
+        result += m.digest()
+        i += 1
+    return result[0:bits / 8]
+
+def wsc_keys(kdk):
+    keys = wsc_kdf(kdk, "Wi-Fi Easy and Secure Key Derivation", 640)
+    authkey = keys[0:32]
+    keywrapkey = keys[32:48]
+    emsk = keys[48:80]
+    return authkey,keywrapkey,emsk
+
+def wsc_dev_pw_half_psk(authkey, dev_pw):
+    m = hmac.new(authkey, dev_pw, hashlib.sha256)
+    return m.digest()[0:16]
+
+def wsc_dev_pw_psk(authkey, dev_pw):
+    dev_pw_1 = dev_pw[0:len(dev_pw) / 2]
+    dev_pw_2 = dev_pw[len(dev_pw) / 2:]
+    psk1 = wsc_dev_pw_half_psk(authkey, dev_pw_1)
+    psk2 = wsc_dev_pw_half_psk(authkey, dev_pw_2)
+    return psk1,psk2
+
+def build_attr_authenticator(authkey, prev_msg, curr_msg):
+    m = hmac.new(authkey, prev_msg + curr_msg, hashlib.sha256)
+    auth = m.digest()[0:8]
+    return build_wsc_attr(ATTR_AUTHENTICATOR, auth)
+
+def build_attr_encr_settings(authkey, keywrapkey, data):
+    m = hmac.new(authkey, data, hashlib.sha256)
+    kwa = m.digest()[0:8]
+    data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, kwa)
+    iv = 16*'\x99'
+    aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
+    pad_len = 16 - len(data) % 16
+    ps = pad_len * struct.pack('B', pad_len)
+    data += ps
+    wrapped = aes.encrypt(data)
+    return build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
+
+def decrypt_attr_encr_settings(authkey, keywrapkey, data):
+    if len(data) < 32 or len(data) % 16 != 0:
+        raise Exception("Unexpected Encrypted Settings length: %d" % len(data))
+    iv = data[0:16]
+    encr = data[16:]
+    aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
+    decrypted = aes.decrypt(encr)
+    pad_len, = struct.unpack('B', decrypted[-1])
+    if pad_len > len(decrypted):
+        raise Exception("Invalid padding in Encrypted Settings")
+    for i in range(-pad_len, -1):
+        if decrypted[i] != decrypted[-1]:
+            raise Exception("Invalid PS value in Encrypted Settings")
+    
+    decrypted = decrypted[0:len(decrypted) - pad_len]
+    if len(decrypted) < 12:
+        raise Exception("Truncated Encrypted Settings plaintext")
+    kwa = decrypted[-12:]
+    attr,length = struct.unpack(">HH", kwa[0:4])
+    if attr != ATTR_KEY_WRAP_AUTH or length != 8:
+        raise Exception("Invalid KWA header")
+    kwa = kwa[4:]
+    decrypted = decrypted[0:len(decrypted) - 12]
+
+    m = hmac.new(authkey, decrypted, hashlib.sha256)
+    calc_kwa = m.digest()[0:8]
+    if kwa != calc_kwa:
+        raise Exception("KWA mismatch")
+
+    return decrypted
+
+def zeropad_str(val, pad_len):
+    while len(val) < pad_len * 2:
+        val = '0' + val
+    return val
+
+def wsc_dh_init():
+    # For now, use a hardcoded private key. In theory, this is supposed to be
+    # randomly selected.
+    own_private = 0x123456789
+    own_public = pow(group_5_generator, own_private, group_5_prime)
+    pk = binascii.unhexlify(zeropad_str(format(own_public, '02x'), 192))
+    return own_private, pk
+
+def wsc_dh_kdf(peer_pk, own_private, mac_addr, e_nonce, r_nonce):
+    peer_public = long(binascii.hexlify(peer_pk), 16)
+    if peer_public < 2 or peer_public >= group_5_prime:
+        raise Exception("Invalid peer public key")
+    if pow(peer_public, (group_5_prime - 1) / 2, group_5_prime) != 1:
+        raise Exception("Unexpected Legendre symbol for peer public key")
+
+    shared_secret = pow(peer_public, own_private, group_5_prime)
+    ss = zeropad_str(format(shared_secret, "02x"), 192)
+    logger.debug("DH shared secret: " + ss)
+
+    dhkey = hashlib.sha256(binascii.unhexlify(ss)).digest()
+    logger.debug("DHKey: " + binascii.hexlify(dhkey))
+
+    m = hmac.new(dhkey, e_nonce + mac_addr + r_nonce, hashlib.sha256)
+    kdk = m.digest()
+    logger.debug("KDK: " + binascii.hexlify(kdk))
+    authkey,keywrapkey,emsk = wsc_keys(kdk)
+    logger.debug("AuthKey: " + binascii.hexlify(authkey))
+    logger.debug("KeyWrapKey: " + binascii.hexlify(keywrapkey))
+    logger.debug("EMSK: " + binascii.hexlify(emsk))
+    return authkey,keywrapkey
+
+def wsc_dev_pw_hash(authkey, dev_pw, e_pk, r_pk):
+    psk1,psk2 = wsc_dev_pw_psk(authkey, dev_pw)
+    logger.debug("PSK1: " + binascii.hexlify(psk1))
+    logger.debug("PSK2: " + binascii.hexlify(psk2))
+
+    # Note: Secret values are supposed to be random, but hardcoded values are
+    # fine for testing.
+    s1 = 16*'\x77'
+    m = hmac.new(authkey, s1 + psk1 + e_pk + r_pk, hashlib.sha256)
+    hash1 = m.digest()
+    logger.debug("Hash1: " + binascii.hexlify(hash1))
+
+    s2 = 16*'\x88'
+    m = hmac.new(authkey, s2 + psk2 + e_pk + r_pk, hashlib.sha256)
+    hash2 = m.digest()
+    logger.debug("Hash2: " + binascii.hexlify(hash2))
+    return s1,s2,hash1,hash2
+
+def build_m1(eap_id, uuid_e, mac_addr, e_nonce, e_pk,
+             manufacturer='', model_name='', config_methods='\x00\x00'):
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M1)
+    attrs += build_wsc_attr(ATTR_UUID_E, uuid_e)
+    attrs += build_wsc_attr(ATTR_MAC_ADDR, mac_addr)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    attrs += build_wsc_attr(ATTR_PUBLIC_KEY, e_pk)
+    attrs += build_wsc_attr(ATTR_AUTH_TYPE_FLAGS, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_ENCR_TYPE_FLAGS, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_CONN_TYPE_FLAGS, '\x00')
+    attrs += build_wsc_attr(ATTR_CONFIG_METHODS, config_methods)
+    attrs += build_wsc_attr(ATTR_WPS_STATE, '\x00')
+    attrs += build_wsc_attr(ATTR_MANUFACTURER, manufacturer)
+    attrs += build_wsc_attr(ATTR_MODEL_NAME, model_name)
+    attrs += build_wsc_attr(ATTR_MODEL_NUMBER, '')
+    attrs += build_wsc_attr(ATTR_SERIAL_NUMBER, '')
+    attrs += build_wsc_attr(ATTR_PRIMARY_DEV_TYPE, 8*'\x00')
+    attrs += build_wsc_attr(ATTR_DEV_NAME, '')
+    attrs += build_wsc_attr(ATTR_RF_BANDS, '\x00')
+    attrs += build_wsc_attr(ATTR_ASSOC_STATE, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_DEV_PASSWORD_ID, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_CONFIG_ERROR, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_OS_VERSION, '\x00\x00\x00\x00')
+    m1 = build_eap_wsc(2, eap_id, attrs)
+    return m1, attrs
+
+def build_m2(authkey, m1, eap_id, e_nonce, r_nonce, uuid_r, r_pk,
+             dev_pw_id='\x00\x00', eap_code=1):
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M2)
+    if e_nonce:
+        attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    if r_nonce:
+        attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    attrs += build_wsc_attr(ATTR_UUID_R, uuid_r)
+    if r_pk:
+        attrs += build_wsc_attr(ATTR_PUBLIC_KEY, r_pk)
+    attrs += build_wsc_attr(ATTR_AUTH_TYPE_FLAGS, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_ENCR_TYPE_FLAGS, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_CONN_TYPE_FLAGS, '\x00')
+    attrs += build_wsc_attr(ATTR_CONFIG_METHODS, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_MANUFACTURER, '')
+    attrs += build_wsc_attr(ATTR_MODEL_NAME, '')
+    attrs += build_wsc_attr(ATTR_MODEL_NUMBER, '')
+    attrs += build_wsc_attr(ATTR_SERIAL_NUMBER, '')
+    attrs += build_wsc_attr(ATTR_PRIMARY_DEV_TYPE, 8*'\x00')
+    attrs += build_wsc_attr(ATTR_DEV_NAME, '')
+    attrs += build_wsc_attr(ATTR_RF_BANDS, '\x00')
+    attrs += build_wsc_attr(ATTR_ASSOC_STATE, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_CONFIG_ERROR, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_DEV_PASSWORD_ID, dev_pw_id)
+    attrs += build_wsc_attr(ATTR_OS_VERSION, '\x00\x00\x00\x00')
+    attrs += build_attr_authenticator(authkey, m1, attrs)
+    m2 = build_eap_wsc(eap_code, eap_id, attrs)
+    return m2, attrs
+
+def build_m2d(m1, eap_id, e_nonce, r_nonce, uuid_r, dev_pw_id=None, eap_code=1):
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M2D)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    attrs += build_wsc_attr(ATTR_UUID_R, uuid_r)
+    attrs += build_wsc_attr(ATTR_AUTH_TYPE_FLAGS, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_ENCR_TYPE_FLAGS, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_CONN_TYPE_FLAGS, '\x00')
+    attrs += build_wsc_attr(ATTR_CONFIG_METHODS, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_MANUFACTURER, '')
+    attrs += build_wsc_attr(ATTR_MODEL_NAME, '')
+    #attrs += build_wsc_attr(ATTR_MODEL_NUMBER, '')
+    attrs += build_wsc_attr(ATTR_SERIAL_NUMBER, '')
+    attrs += build_wsc_attr(ATTR_PRIMARY_DEV_TYPE, 8*'\x00')
+    attrs += build_wsc_attr(ATTR_DEV_NAME, '')
+    attrs += build_wsc_attr(ATTR_RF_BANDS, '\x00')
+    attrs += build_wsc_attr(ATTR_ASSOC_STATE, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_CONFIG_ERROR, '\x00\x00')
+    attrs += build_wsc_attr(ATTR_OS_VERSION, '\x00\x00\x00\x00')
+    if dev_pw_id:
+        attrs += build_wsc_attr(ATTR_DEV_PASSWORD_ID, dev_pw_id)
+    m2d = build_eap_wsc(eap_code, eap_id, attrs)
+    return m2d, attrs
+
+def build_ack(eap_id, e_nonce, r_nonce, msg_type=WPS_WSC_ACK, eap_code=1):
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    if msg_type is not None:
+        attrs += build_attr_msg_type(msg_type)
+    if e_nonce:
+        attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    if r_nonce:
+        attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    msg = build_eap_wsc(eap_code, eap_id, attrs, opcode=WSC_ACK)
+    return msg, attrs
+
+def build_nack(eap_id, e_nonce, r_nonce, config_error='\x00\x00',
+               msg_type=WPS_WSC_NACK, eap_code=1):
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    if msg_type is not None:
+        attrs += build_attr_msg_type(msg_type)
+    if e_nonce:
+        attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    if r_nonce:
+        attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    if config_error:
+        attrs += build_wsc_attr(ATTR_CONFIG_ERROR, config_error)
+    msg = build_eap_wsc(eap_code, eap_id, attrs, opcode=WSC_NACK)
+    return msg, attrs
+
+def test_wps_ext(dev, apdev):
+    """WPS against external implementation"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+    wsc_start_id = msg['eap_identifier']
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+
+    authkey,keywrapkey = wsc_dh_kdf(m2_attrs[ATTR_PUBLIC_KEY], own_private,
+                                    mac_addr, e_nonce,
+                                    m2_attrs[ATTR_REGISTRAR_NONCE])
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk,
+                                                m2_attrs[ATTR_PUBLIC_KEY])
+
+    logger.debug("Send M3 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M3)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE,
+                            m2_attrs[ATTR_REGISTRAR_NONCE])
+    attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+    attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m3)
+
+    logger.debug("Receive M4 from AP")
+    msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+    logger.debug("Send M5 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M5)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE,
+                            m2_attrs[ATTR_REGISTRAR_NONCE])
+    data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+    raw_m5_attrs = attrs
+    m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m5)
+
+    logger.debug("Receive M6 from AP")
+    msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
+
+    logger.debug("Send M7 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M7)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE,
+                            m2_attrs[ATTR_REGISTRAR_NONCE])
+    data = build_wsc_attr(ATTR_E_SNONCE2, e_s2)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
+    m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    raw_m7_attrs = attrs
+    send_wsc_msg(hapd, addr, m7)
+
+    logger.debug("Receive M8 from AP")
+    msg, m8_attrs, raw_m8_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M8)
+    m8_cred = decrypt_attr_encr_settings(authkey, keywrapkey,
+                                         m8_attrs[ATTR_ENCR_SETTINGS])
+    logger.debug("M8 Credential: " + binascii.hexlify(m8_cred))
+
+    logger.debug("Prepare WSC_Done")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_WSC_DONE)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE,
+                            m2_attrs[ATTR_REGISTRAR_NONCE])
+    wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
+    # Do not send WSC_Done yet to allow exchangw with STA complete before the
+    # AP disconnects.
+
+    uuid_r = 16*'\x33'
+    r_nonce = 16*'\x44'
+
+    eap_id = wsc_start_id
+    logger.debug("Send WSC/Start to STA")
+    wsc_start = build_eap_wsc(1, eap_id, "", opcode=WSC_Start)
+    send_wsc_msg(dev[0], bssid, wsc_start)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M1 from STA")
+    msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+
+    authkey,keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+                                    mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+                                    r_nonce)
+    r_s1,r_s2,r_hash1,r_hash2 = wsc_dev_pw_hash(authkey, pin,
+                                                m1_attrs[ATTR_PUBLIC_KEY], e_pk)
+
+    logger.debug("Send M2 to STA")
+    m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+                                m1_attrs[ATTR_ENROLLEE_NONCE],
+                                r_nonce, uuid_r, e_pk)
+    send_wsc_msg(dev[0], bssid, m2)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M3 from STA")
+    msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
+
+    logger.debug("Send M4 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M4)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
+    attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+    attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+    data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
+    raw_m4_attrs = attrs
+    m4 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m4)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M5 from STA")
+    msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M5)
+
+    logger.debug("Send M6 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M6)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE,
+                            m1_attrs[ATTR_ENROLLEE_NONCE])
+    data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m5_attrs, attrs)
+    raw_m6_attrs = attrs
+    m6 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m6)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M7 from STA")
+    msg, m7_attrs, raw_m7_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M7)
+
+    logger.debug("Send M8 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M8)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE,
+                            m1_attrs[ATTR_ENROLLEE_NONCE])
+    attrs += build_attr_encr_settings(authkey, keywrapkey, m8_cred)
+    attrs += build_attr_authenticator(authkey, raw_m7_attrs, attrs)
+    raw_m8_attrs = attrs
+    m8 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m8)
+    eap_id = (eap_id + 1) % 256
+
+    ev = dev[0].wait_event(["WPS-CRED-RECEIVED"], timeout=5)
+    if ev is None:
+        raise Exception("wpa_supplicant did not report credential")
+
+    logger.debug("Receive WSC_Done from STA")
+    msg = get_wsc_msg(dev[0])
+    if msg['wsc_opcode'] != WSC_Done or msg['wsc_msg_type'] != WPS_WSC_DONE:
+        raise Exception("Unexpected Op-Code/MsgType for WSC_Done")
+
+    logger.debug("Send WSC_Done to AP")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    send_wsc_msg(hapd, addr, wsc_done)
+
+    ev = hapd.wait_event(["WPS-REG-SUCCESS"], timeout=5)
+    if ev is None:
+        raise Exception("hostapd did not report WPS success")
+
+    dev[0].wait_connected()
+
+def wps_start_kwa(dev, apdev):
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_r = 16*'\x33'
+    r_nonce = 16*'\x44'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Receive M1 from STA")
+    msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+    eap_id = (msg['eap_identifier'] + 1) % 256
+
+    authkey,keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+                                    mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+                                    r_nonce)
+    r_s1,r_s2,r_hash1,r_hash2 = wsc_dev_pw_hash(authkey, pin,
+                                                m1_attrs[ATTR_PUBLIC_KEY], e_pk)
+
+    logger.debug("Send M2 to STA")
+    m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+                                m1_attrs[ATTR_ENROLLEE_NONCE],
+                                r_nonce, uuid_r, e_pk)
+    send_wsc_msg(dev[0], bssid, m2)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M3 from STA")
+    msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
+
+    logger.debug("Send M4 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M4)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
+    attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+    attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+
+    return r_s1, keywrapkey, authkey, raw_m3_attrs, eap_id, bssid, attrs
+
+def wps_stop_kwa(dev, bssid, attrs, authkey, raw_m3_attrs, eap_id):
+    attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
+    m4 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m4)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M5 from STA")
+    msg = get_wsc_msg(dev[0])
+    if msg['wsc_opcode'] != WSC_NACK:
+        raise Exception("Unexpected message - expected WSC_Nack")
+
+    dev[0].request("WPS_CANCEL")
+    send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+    dev[0].wait_disconnected()
+
+def test_wps_ext_kwa_proto_no_kwa(dev, apdev):
+    """WPS and KWA error: No KWA attribute"""
+    r_s1,keywrapkey,authkey,raw_m3_attrs,eap_id,bssid,attrs = wps_start_kwa(dev, apdev)
+    data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+    # Encrypted Settings without KWA
+    iv = 16*'\x99'
+    aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
+    pad_len = 16 - len(data) % 16
+    ps = pad_len * struct.pack('B', pad_len)
+    data += ps
+    wrapped = aes.encrypt(data)
+    attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
+    wps_stop_kwa(dev, bssid, attrs, authkey, raw_m3_attrs, eap_id)
+
+def test_wps_ext_kwa_proto_data_after_kwa(dev, apdev):
+    """WPS and KWA error: Data after KWA"""
+    r_s1,keywrapkey,authkey,raw_m3_attrs,eap_id,bssid,attrs = wps_start_kwa(dev, apdev)
+    data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+    # Encrypted Settings and data after KWA
+    m = hmac.new(authkey, data, hashlib.sha256)
+    kwa = m.digest()[0:8]
+    data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, kwa)
+    data += build_wsc_attr(ATTR_VENDOR_EXT, "1234567890")
+    iv = 16*'\x99'
+    aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
+    pad_len = 16 - len(data) % 16
+    ps = pad_len * struct.pack('B', pad_len)
+    data += ps
+    wrapped = aes.encrypt(data)
+    attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
+    wps_stop_kwa(dev, bssid, attrs, authkey, raw_m3_attrs, eap_id)
+
+def test_wps_ext_kwa_proto_kwa_mismatch(dev, apdev):
+    """WPS and KWA error: KWA mismatch"""
+    r_s1,keywrapkey,authkey,raw_m3_attrs,eap_id,bssid,attrs = wps_start_kwa(dev, apdev)
+    data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+    # Encrypted Settings and KWA with incorrect value
+    data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, 8*'\x00')
+    iv = 16*'\x99'
+    aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
+    pad_len = 16 - len(data) % 16
+    ps = pad_len * struct.pack('B', pad_len)
+    data += ps
+    wrapped = aes.encrypt(data)
+    attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
+    wps_stop_kwa(dev, bssid, attrs, authkey, raw_m3_attrs, eap_id)
+
+def wps_run_cred_proto(dev, apdev, m8_cred, connect=False, no_connect=False):
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_r = 16*'\x33'
+    r_nonce = 16*'\x44'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Receive M1 from STA")
+    msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+    eap_id = (msg['eap_identifier'] + 1) % 256
+
+    authkey,keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+                                    mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+                                    r_nonce)
+    r_s1,r_s2,r_hash1,r_hash2 = wsc_dev_pw_hash(authkey, pin,
+                                                m1_attrs[ATTR_PUBLIC_KEY], e_pk)
+
+    logger.debug("Send M2 to STA")
+    m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+                                m1_attrs[ATTR_ENROLLEE_NONCE],
+                                r_nonce, uuid_r, e_pk)
+    send_wsc_msg(dev[0], bssid, m2)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M3 from STA")
+    msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
+
+    logger.debug("Send M4 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M4)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
+    attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+    attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+    data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
+    raw_m4_attrs = attrs
+    m4 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m4)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M5 from STA")
+    msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M5)
+
+    logger.debug("Send M6 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M6)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE,
+                            m1_attrs[ATTR_ENROLLEE_NONCE])
+    data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m5_attrs, attrs)
+    raw_m6_attrs = attrs
+    m6 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m6)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M7 from STA")
+    msg, m7_attrs, raw_m7_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M7)
+
+    logger.debug("Send M8 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M8)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE,
+                            m1_attrs[ATTR_ENROLLEE_NONCE])
+    attrs += build_attr_encr_settings(authkey, keywrapkey, m8_cred)
+    attrs += build_attr_authenticator(authkey, raw_m7_attrs, attrs)
+    raw_m8_attrs = attrs
+    m8 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m8)
+    eap_id = (eap_id + 1) % 256
+
+    if no_connect:
+        logger.debug("Receive WSC_Done from STA")
+        msg = get_wsc_msg(dev[0])
+        if msg['wsc_opcode'] != WSC_Done or msg['wsc_msg_type'] != WPS_WSC_DONE:
+            raise Exception("Unexpected Op-Code/MsgType for WSC_Done")
+
+        hapd.request("SET ext_eapol_frame_io 0")
+        dev[0].request("SET ext_eapol_frame_io 0")
+
+        send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+
+        dev[0].wait_disconnected()
+        dev[0].request("REMOVE_NETWORK all")
+    elif connect:
+        logger.debug("Receive WSC_Done from STA")
+        msg = get_wsc_msg(dev[0])
+        if msg['wsc_opcode'] != WSC_Done or msg['wsc_msg_type'] != WPS_WSC_DONE:
+            raise Exception("Unexpected Op-Code/MsgType for WSC_Done")
+
+        hapd.request("SET ext_eapol_frame_io 0")
+        dev[0].request("SET ext_eapol_frame_io 0")
+
+        send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+
+        dev[0].wait_connected()
+    else:
+        # Verify STA NACK's the credential
+        msg = get_wsc_msg(dev[0])
+        if msg['wsc_opcode'] != WSC_NACK:
+            raise Exception("Unexpected message - expected WSC_Nack")
+        dev[0].request("WPS_CANCEL")
+        send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+        dev[0].wait_disconnected()
+
+def build_cred(nw_idx='\x01', ssid='test-wps-conf', auth_type='\x00\x20',
+               encr_type='\x00\x08', nw_key="12345678",
+               mac_addr='\x00\x00\x00\x00\x00\x00'):
+    attrs = ''
+    if nw_idx is not None:
+        attrs += build_wsc_attr(ATTR_NETWORK_INDEX, nw_idx)
+    if ssid is not None:
+        attrs += build_wsc_attr(ATTR_SSID, ssid)
+    if auth_type is not None:
+        attrs += build_wsc_attr(ATTR_AUTH_TYPE, auth_type)
+    if encr_type is not None:
+        attrs += build_wsc_attr(ATTR_ENCR_TYPE, encr_type)
+    if nw_key is not None:
+        attrs += build_wsc_attr(ATTR_NETWORK_KEY, nw_key)
+    if mac_addr is not None:
+        attrs += build_wsc_attr(ATTR_MAC_ADDR, mac_addr)
+    return build_wsc_attr(ATTR_CRED, attrs)
+
+def test_wps_ext_cred_proto_success(dev, apdev):
+    """WPS and Credential: success"""
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    m8_cred = build_cred(mac_addr=mac_addr)
+    wps_run_cred_proto(dev, apdev, m8_cred, connect=True)
+
+def test_wps_ext_cred_proto_mac_addr_mismatch(dev, apdev):
+    """WPS and Credential: MAC Address mismatch"""
+    m8_cred = build_cred()
+    wps_run_cred_proto(dev, apdev, m8_cred, connect=True)
+
+def test_wps_ext_cred_proto_zero_padding(dev, apdev):
+    """WPS and Credential: zeropadded attributes"""
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    m8_cred = build_cred(mac_addr=mac_addr, ssid='test-wps-conf\x00',
+                         nw_key="12345678\x00")
+    wps_run_cred_proto(dev, apdev, m8_cred, connect=True)
+
+def test_wps_ext_cred_proto_ssid_missing(dev, apdev):
+    """WPS and Credential: SSID missing"""
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    m8_cred = build_cred(mac_addr=mac_addr, ssid=None)
+    wps_run_cred_proto(dev, apdev, m8_cred)
+
+def test_wps_ext_cred_proto_ssid_zero_len(dev, apdev):
+    """WPS and Credential: Zero-length SSID"""
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    m8_cred = build_cred(mac_addr=mac_addr, ssid="")
+    wps_run_cred_proto(dev, apdev, m8_cred, no_connect=True)
+
+def test_wps_ext_cred_proto_auth_type_missing(dev, apdev):
+    """WPS and Credential: Auth Type missing"""
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    m8_cred = build_cred(mac_addr=mac_addr, auth_type=None)
+    wps_run_cred_proto(dev, apdev, m8_cred)
+
+def test_wps_ext_cred_proto_encr_type_missing(dev, apdev):
+    """WPS and Credential: Encr Type missing"""
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    m8_cred = build_cred(mac_addr=mac_addr, encr_type=None)
+    wps_run_cred_proto(dev, apdev, m8_cred)
+
+def test_wps_ext_cred_proto_network_key_missing(dev, apdev):
+    """WPS and Credential: Network Key missing"""
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    m8_cred = build_cred(mac_addr=mac_addr, nw_key=None)
+    wps_run_cred_proto(dev, apdev, m8_cred)
+
+def test_wps_ext_cred_proto_network_key_missing_open(dev, apdev):
+    """WPS and Credential: Network Key missing (open)"""
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    m8_cred = build_cred(mac_addr=mac_addr, auth_type='\x00\x01',
+                         encr_type='\x00\x01', nw_key=None, ssid="foo")
+    wps_run_cred_proto(dev, apdev, m8_cred, no_connect=True)
+
+def test_wps_ext_cred_proto_mac_addr_missing(dev, apdev):
+    """WPS and Credential: MAC Address missing"""
+    m8_cred = build_cred(mac_addr=None)
+    wps_run_cred_proto(dev, apdev, m8_cred)
+
+def test_wps_ext_cred_proto_invalid_encr_type(dev, apdev):
+    """WPS and Credential: Invalid Encr Type"""
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    m8_cred = build_cred(mac_addr=mac_addr, encr_type='\x00\x00')
+    wps_run_cred_proto(dev, apdev, m8_cred)
+
+def test_wps_ext_cred_proto_missing_cred(dev, apdev):
+    """WPS and Credential: Missing Credential"""
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    m8_cred = ''
+    wps_run_cred_proto(dev, apdev, m8_cred)
+
+def test_wps_ext_proto_m2_no_public_key(dev, apdev):
+    """WPS and no Public Key in M2"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_r = 16*'\x33'
+    r_nonce = 16*'\x44'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Receive M1 from STA")
+    msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+    eap_id = (msg['eap_identifier'] + 1) % 256
+
+    authkey,keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+                                    mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+                                    r_nonce)
+    r_s1,r_s2,r_hash1,r_hash2 = wsc_dev_pw_hash(authkey, pin,
+                                                m1_attrs[ATTR_PUBLIC_KEY], e_pk)
+
+    logger.debug("Send M2 to STA")
+    m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+                                m1_attrs[ATTR_ENROLLEE_NONCE],
+                                r_nonce, uuid_r, None)
+    send_wsc_msg(dev[0], bssid, m2)
+    eap_id = (eap_id + 1) % 256
+
+    # Verify STA NACK's the credential
+    msg = get_wsc_msg(dev[0])
+    if msg['wsc_opcode'] != WSC_NACK:
+        raise Exception("Unexpected message - expected WSC_Nack")
+    dev[0].request("WPS_CANCEL")
+    send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+    dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m2_invalid_public_key(dev, apdev):
+    """WPS and invalid Public Key in M2"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_r = 16*'\x33'
+    r_nonce = 16*'\x44'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Receive M1 from STA")
+    msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+    eap_id = (msg['eap_identifier'] + 1) % 256
+
+    authkey,keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+                                    mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+                                    r_nonce)
+    r_s1,r_s2,r_hash1,r_hash2 = wsc_dev_pw_hash(authkey, pin,
+                                                m1_attrs[ATTR_PUBLIC_KEY], e_pk)
+
+    logger.debug("Send M2 to STA")
+    m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+                                m1_attrs[ATTR_ENROLLEE_NONCE],
+                                r_nonce, uuid_r, 192*'\xff')
+    send_wsc_msg(dev[0], bssid, m2)
+    eap_id = (eap_id + 1) % 256
+
+    # Verify STA NACK's the credential
+    msg = get_wsc_msg(dev[0])
+    if msg['wsc_opcode'] != WSC_NACK:
+        raise Exception("Unexpected message - expected WSC_Nack")
+    dev[0].request("WPS_CANCEL")
+    send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+    dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m2_public_key_oom(dev, apdev):
+    """WPS and Public Key OOM in M2"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_r = 16*'\x33'
+    r_nonce = 16*'\x44'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Receive M1 from STA")
+    msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+    eap_id = (msg['eap_identifier'] + 1) % 256
+
+    authkey,keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+                                    mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+                                    r_nonce)
+    r_s1,r_s2,r_hash1,r_hash2 = wsc_dev_pw_hash(authkey, pin,
+                                                m1_attrs[ATTR_PUBLIC_KEY], e_pk)
+
+    logger.debug("Send M2 to STA")
+    m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+                                m1_attrs[ATTR_ENROLLEE_NONCE],
+                                r_nonce, uuid_r, e_pk)
+    with alloc_fail(dev[0], 1, "wpabuf_alloc_copy;wps_process_pubkey"):
+        send_wsc_msg(dev[0], bssid, m2)
+        eap_id = (eap_id + 1) % 256
+
+        # Verify STA NACK's the credential
+        msg = get_wsc_msg(dev[0])
+        if msg['wsc_opcode'] != WSC_NACK:
+            raise Exception("Unexpected message - expected WSC_Nack")
+        dev[0].request("WPS_CANCEL")
+        send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+        dev[0].wait_disconnected()
+
+def test_wps_ext_proto_nack_m3(dev, apdev):
+    """WPS and NACK M3"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_r = 16*'\x33'
+    r_nonce = 16*'\x44'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Receive M1 from STA")
+    msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+    eap_id = (msg['eap_identifier'] + 1) % 256
+
+    authkey,keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+                                    mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+                                    r_nonce)
+    r_s1,r_s2,r_hash1,r_hash2 = wsc_dev_pw_hash(authkey, pin,
+                                                m1_attrs[ATTR_PUBLIC_KEY], e_pk)
+
+    logger.debug("Send M2 to STA")
+    m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+                                m1_attrs[ATTR_ENROLLEE_NONCE],
+                                r_nonce, uuid_r, e_pk)
+    send_wsc_msg(dev[0], bssid, m2)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M3 from STA")
+    msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
+
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_nack(eap_id, m1_attrs[ATTR_ENROLLEE_NONCE],
+                            r_nonce, config_error='\x01\x23')
+    send_wsc_msg(dev[0], bssid, msg)
+    ev = dev[0].wait_event(["WPS-FAIL"], timeout=5)
+    if ev is None:
+        raise Exception("Failure not reported")
+    if "msg=7 config_error=291" not in ev:
+        raise Exception("Unexpected failure reason: " + ev)
+
+def test_wps_ext_proto_nack_m5(dev, apdev):
+    """WPS and NACK M5"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_r = 16*'\x33'
+    r_nonce = 16*'\x44'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Receive M1 from STA")
+    msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+    eap_id = (msg['eap_identifier'] + 1) % 256
+
+    authkey,keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+                                    mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+                                    r_nonce)
+    r_s1,r_s2,r_hash1,r_hash2 = wsc_dev_pw_hash(authkey, pin,
+                                                m1_attrs[ATTR_PUBLIC_KEY], e_pk)
+
+    logger.debug("Send M2 to STA")
+    m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+                                m1_attrs[ATTR_ENROLLEE_NONCE],
+                                r_nonce, uuid_r, e_pk)
+    send_wsc_msg(dev[0], bssid, m2)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M3 from STA")
+    msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
+
+    logger.debug("Send M4 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M4)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
+    attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+    attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+    data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
+    raw_m4_attrs = attrs
+    m4 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m4)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M5 from STA")
+    msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M5)
+
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_nack(eap_id, m1_attrs[ATTR_ENROLLEE_NONCE],
+                            r_nonce, config_error='\x01\x24')
+    send_wsc_msg(dev[0], bssid, msg)
+    ev = dev[0].wait_event(["WPS-FAIL"], timeout=5)
+    if ev is None:
+        raise Exception("Failure not reported")
+    if "msg=9 config_error=292" not in ev:
+        raise Exception("Unexpected failure reason: " + ev)
+
+def wps_nack_m3(dev, apdev):
+    pin = "00000000"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_r = 16*'\x33'
+    r_nonce = 16*'\x44'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Receive M1 from STA")
+    msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+    eap_id = (msg['eap_identifier'] + 1) % 256
+
+    authkey,keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+                                    mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+                                    r_nonce)
+    r_s1,r_s2,r_hash1,r_hash2 = wsc_dev_pw_hash(authkey, pin,
+                                                m1_attrs[ATTR_PUBLIC_KEY], e_pk)
+
+    logger.debug("Send M2 to STA")
+    m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+                                m1_attrs[ATTR_ENROLLEE_NONCE],
+                                r_nonce, uuid_r, e_pk, dev_pw_id='\x00\x04')
+    send_wsc_msg(dev[0], bssid, m2)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M3 from STA")
+    msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
+    return eap_id, m1_attrs[ATTR_ENROLLEE_NONCE], r_nonce, bssid
+
+def test_wps_ext_proto_nack_m3_no_config_error(dev, apdev):
+    """WPS and NACK M3 missing Config Error"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_nack(eap_id, e_nonce, r_nonce, config_error=None)
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_nack_m3_no_e_nonce(dev, apdev):
+    """WPS and NACK M3 missing E-Nonce"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_nack(eap_id, None, r_nonce)
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_nack_m3_e_nonce_mismatch(dev, apdev):
+    """WPS and NACK M3 E-Nonce mismatch"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_nack(eap_id, 16*'\x00', r_nonce)
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_nack_m3_no_r_nonce(dev, apdev):
+    """WPS and NACK M3 missing R-Nonce"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_nack(eap_id, e_nonce, None)
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_nack_m3_r_nonce_mismatch(dev, apdev):
+    """WPS and NACK M3 R-Nonce mismatch"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_nack(eap_id, e_nonce, 16*'\x00')
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_nack_m3_no_msg_type(dev, apdev):
+    """WPS and NACK M3 no Message Type"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_nack(eap_id, e_nonce, r_nonce, msg_type=None)
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_nack_m3_invalid_msg_type(dev, apdev):
+    """WPS and NACK M3 invalid Message Type"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_nack(eap_id, e_nonce, r_nonce, msg_type=123)
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_nack_m3_invalid_attr(dev, apdev):
+    """WPS and NACK M3 invalid attribute"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send NACK to STA")
+    attrs = '\x10\x10\x00'
+    msg = build_eap_wsc(1, eap_id, attrs, opcode=WSC_NACK)
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3_no_e_nonce(dev, apdev):
+    """WPS and ACK M3 missing E-Nonce"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_ack(eap_id, None, r_nonce)
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3_e_nonce_mismatch(dev, apdev):
+    """WPS and ACK M3 E-Nonce mismatch"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_ack(eap_id, 16*'\x00', r_nonce)
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3_no_r_nonce(dev, apdev):
+    """WPS and ACK M3 missing R-Nonce"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_ack(eap_id, e_nonce, None)
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3_r_nonce_mismatch(dev, apdev):
+    """WPS and ACK M3 R-Nonce mismatch"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_ack(eap_id, e_nonce, 16*'\x00')
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3_no_msg_type(dev, apdev):
+    """WPS and ACK M3 no Message Type"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_ack(eap_id, e_nonce, r_nonce, msg_type=None)
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3_invalid_msg_type(dev, apdev):
+    """WPS and ACK M3 invalid Message Type"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send NACK to STA")
+    msg, attrs = build_ack(eap_id, e_nonce, r_nonce, msg_type=123)
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3_invalid_attr(dev, apdev):
+    """WPS and ACK M3 invalid attribute"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send ACK to STA")
+    attrs = '\x10\x10\x00'
+    msg = build_eap_wsc(1, eap_id, attrs, opcode=WSC_ACK)
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3(dev, apdev):
+    """WPS and ACK M3"""
+    eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+    logger.debug("Send ACK to STA")
+    msg, attrs = build_ack(eap_id, e_nonce, r_nonce)
+    send_wsc_msg(dev[0], bssid, msg)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def wps_to_m3_helper(dev, apdev):
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_r = 16*'\x33'
+    r_nonce = 16*'\x44'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Receive M1 from STA")
+    msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+    eap_id = (msg['eap_identifier'] + 1) % 256
+
+    authkey,keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+                                    mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+                                    r_nonce)
+    r_s1,r_s2,r_hash1,r_hash2 = wsc_dev_pw_hash(authkey, pin,
+                                                m1_attrs[ATTR_PUBLIC_KEY], e_pk)
+
+    logger.debug("Send M2 to STA")
+    m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+                                m1_attrs[ATTR_ENROLLEE_NONCE],
+                                r_nonce, uuid_r, e_pk)
+    send_wsc_msg(dev[0], bssid, m2)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M3 from STA")
+    msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
+    return eap_id, m1_attrs, r_nonce, bssid, r_hash1, r_hash2, r_s1, r_s2, raw_m3_attrs, authkey, keywrapkey
+
+def wps_to_m3(dev, apdev):
+    eap_id, m1_attrs, r_nonce, bssid, r_hash1, r_hash2, r_s1, r_s2, raw_m3_attrs, authkey, keywrapkey = wps_to_m3_helper(dev, apdev)
+    return eap_id, m1_attrs[ATTR_ENROLLEE_NONCE], r_nonce, bssid, r_hash1, r_hash2, r_s1, raw_m3_attrs, authkey, keywrapkey
+
+def wps_to_m5(dev, apdev):
+    eap_id, m1_attrs, r_nonce, bssid, r_hash1, r_hash2, r_s1, r_s2, raw_m3_attrs, authkey, keywrapkey = wps_to_m3_helper(dev, apdev)
+
+    logger.debug("Send M4 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M4)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
+    attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+    attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+    data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
+    raw_m4_attrs = attrs
+    m4 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m4)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M5 from STA")
+    msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M5)
+
+    return eap_id, m1_attrs[ATTR_ENROLLEE_NONCE], r_nonce, bssid, r_hash1, r_hash2, r_s2, raw_m5_attrs, authkey, keywrapkey
+
+def test_wps_ext_proto_m4_missing_r_hash1(dev, apdev):
+    """WPS and no R-Hash1 in M4"""
+    eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
+
+    logger.debug("Send M4 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M4)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    #attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+    attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+    data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, m3, attrs)
+    m4 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m4)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M5 (NACK) from STA")
+    msg = get_wsc_msg(dev[0])
+    if msg['wsc_opcode'] != WSC_NACK:
+        raise Exception("Unexpected message - expected WSC_Nack")
+
+    dev[0].request("WPS_CANCEL")
+    send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+    dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m4_missing_r_hash2(dev, apdev):
+    """WPS and no R-Hash2 in M4"""
+    eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
+
+    logger.debug("Send M4 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M4)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+    #attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+    data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, m3, attrs)
+    m4 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m4)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M5 (NACK) from STA")
+    msg = get_wsc_msg(dev[0])
+    if msg['wsc_opcode'] != WSC_NACK:
+        raise Exception("Unexpected message - expected WSC_Nack")
+
+    dev[0].request("WPS_CANCEL")
+    send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+    dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m4_missing_r_snonce1(dev, apdev):
+    """WPS and no R-SNonce1 in M4"""
+    eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
+
+    logger.debug("Send M4 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M4)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+    attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+    #data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+    data = ''
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, m3, attrs)
+    m4 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m4)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M5 (NACK) from STA")
+    msg = get_wsc_msg(dev[0])
+    if msg['wsc_opcode'] != WSC_NACK:
+        raise Exception("Unexpected message - expected WSC_Nack")
+
+    dev[0].request("WPS_CANCEL")
+    send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+    dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m4_invalid_pad_string(dev, apdev):
+    """WPS and invalid pad string in M4"""
+    eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
+
+    logger.debug("Send M4 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M4)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+    attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+    data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+
+    m = hmac.new(authkey, data, hashlib.sha256)
+    kwa = m.digest()[0:8]
+    data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, kwa)
+    iv = 16*'\x99'
+    aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
+    pad_len = 16 - len(data) % 16
+    ps = (pad_len - 1) * struct.pack('B', pad_len) + struct.pack('B', pad_len - 1)
+    data += ps
+    wrapped = aes.encrypt(data)
+    attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
+
+    attrs += build_attr_authenticator(authkey, m3, attrs)
+    m4 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m4)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M5 (NACK) from STA")
+    msg = get_wsc_msg(dev[0])
+    if msg['wsc_opcode'] != WSC_NACK:
+        raise Exception("Unexpected message - expected WSC_Nack")
+
+    dev[0].request("WPS_CANCEL")
+    send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+    dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m4_invalid_pad_value(dev, apdev):
+    """WPS and invalid pad value in M4"""
+    eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
+
+    logger.debug("Send M4 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M4)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+    attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+    data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+
+    m = hmac.new(authkey, data, hashlib.sha256)
+    kwa = m.digest()[0:8]
+    data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, kwa)
+    iv = 16*'\x99'
+    aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
+    pad_len = 16 - len(data) % 16
+    ps = (pad_len - 1) * struct.pack('B', pad_len) + struct.pack('B', 255)
+    data += ps
+    wrapped = aes.encrypt(data)
+    attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
+
+    attrs += build_attr_authenticator(authkey, m3, attrs)
+    m4 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m4)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M5 (NACK) from STA")
+    msg = get_wsc_msg(dev[0])
+    if msg['wsc_opcode'] != WSC_NACK:
+        raise Exception("Unexpected message - expected WSC_Nack")
+
+    dev[0].request("WPS_CANCEL")
+    send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+    dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m4_no_encr_settings(dev, apdev):
+    """WPS and no Encr Settings in M4"""
+    eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
+
+    logger.debug("Send M4 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M4)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+    attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+    attrs += build_attr_authenticator(authkey, m3, attrs)
+    m4 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m4)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M5 (NACK) from STA")
+    msg = get_wsc_msg(dev[0])
+    if msg['wsc_opcode'] != WSC_NACK:
+        raise Exception("Unexpected message - expected WSC_Nack")
+
+    dev[0].request("WPS_CANCEL")
+    send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+    dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m6_missing_r_snonce2(dev, apdev):
+    """WPS and no R-SNonce2 in M6"""
+    eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s2, m5, authkey, keywrapkey = wps_to_m5(dev, apdev)
+
+    logger.debug("Send M6 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M6)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    #data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
+    data = ''
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, m5, attrs)
+    m6 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m6)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M7 (NACK) from STA")
+    msg = get_wsc_msg(dev[0])
+    if msg['wsc_opcode'] != WSC_NACK:
+        raise Exception("Unexpected message - expected WSC_Nack")
+
+    dev[0].request("WPS_CANCEL")
+    send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+    dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m6_no_encr_settings(dev, apdev):
+    """WPS and no Encr Settings in M6"""
+    eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s2, m5, authkey, keywrapkey = wps_to_m5(dev, apdev)
+
+    logger.debug("Send M6 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M6)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
+    #attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, m5, attrs)
+    m6 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m6)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M7 (NACK) from STA")
+    msg = get_wsc_msg(dev[0])
+    if msg['wsc_opcode'] != WSC_NACK:
+        raise Exception("Unexpected message - expected WSC_Nack")
+
+    dev[0].request("WPS_CANCEL")
+    send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+    dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m8_no_encr_settings(dev, apdev):
+    """WPS and no Encr Settings in M6"""
+    eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s2, m5, authkey, keywrapkey = wps_to_m5(dev, apdev)
+
+    logger.debug("Send M6 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M6)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, m5, attrs)
+    raw_m6_attrs = attrs
+    m6 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m6)
+    eap_id = (eap_id + 1) % 256
+
+    logger.debug("Receive M7 from STA")
+    msg, m7_attrs, raw_m7_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M7)
+
+    logger.debug("Send M8 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M8)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    #attrs += build_attr_encr_settings(authkey, keywrapkey, m8_cred)
+    attrs += build_attr_authenticator(authkey, raw_m7_attrs, attrs)
+    raw_m8_attrs = attrs
+    m8 = build_eap_wsc(1, eap_id, attrs)
+    send_wsc_msg(dev[0], bssid, m8)
+
+    logger.debug("Receive WSC_Done (NACK) from STA")
+    msg = get_wsc_msg(dev[0])
+    if msg['wsc_opcode'] != WSC_NACK:
+        raise Exception("Unexpected message - expected WSC_Nack")
+
+    dev[0].request("WPS_CANCEL")
+    send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+    dev[0].wait_disconnected()
+
+def wps_start_ext_reg(apdev, dev):
+    addr = dev.own_addr()
+    bssid = apdev['bssid']
+    ssid = "test-wps-conf"
+    appin = "12345670"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+               "ap_pin": appin }
+    hapd = hostapd.add_ap(apdev['ifname'], params)
+
+    dev.scan_for_bss(bssid, freq="2412")
+    hapd.request("SET ext_eapol_frame_io 1")
+    dev.request("SET ext_eapol_frame_io 1")
+
+    dev.request("WPS_REG " + bssid + " " + appin)
+
+    return addr,bssid,hapd
+
+def wps_run_ap_settings_proto(dev, apdev, ap_settings, success):
+    addr,bssid,hapd = wps_start_ext_reg(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive M1 from AP")
+    msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M1)
+    mac_addr = m1_attrs[ATTR_MAC_ADDR]
+    e_nonce = m1_attrs[ATTR_ENROLLEE_NONCE]
+    e_pk = m1_attrs[ATTR_PUBLIC_KEY]
+
+    appin = '12345670'
+    uuid_r = 16*'\x33'
+    r_nonce = 16*'\x44'
+    own_private, r_pk = wsc_dh_init()
+    authkey,keywrapkey = wsc_dh_kdf(e_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    r_s1,r_s2,r_hash1,r_hash2 = wsc_dev_pw_hash(authkey, appin, e_pk, r_pk)
+
+    logger.debug("Send M2 to AP")
+    m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, msg['eap_identifier'],
+                                e_nonce, r_nonce, uuid_r, r_pk, eap_code=2)
+    send_wsc_msg(hapd, addr, m2)
+
+    logger.debug("Receive M3 from AP")
+    msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M3)
+
+    logger.debug("Send M4 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M4)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+    attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+    data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
+    raw_m4_attrs = attrs
+    m4 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m4)
+
+    logger.debug("Receive M5 from AP")
+    msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M5)
+
+    logger.debug("Send M6 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M6)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m5_attrs, attrs)
+    raw_m6_attrs = attrs
+    m6 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m6)
+
+    logger.debug("Receive M7 from AP")
+    msg, m7_attrs, raw_m7_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M7)
+
+    logger.debug("Send M8 to STA")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M8)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    if ap_settings:
+        attrs += build_attr_encr_settings(authkey, keywrapkey, ap_settings)
+    attrs += build_attr_authenticator(authkey, raw_m7_attrs, attrs)
+    raw_m8_attrs = attrs
+    m8 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m8)
+
+    if success:
+        ev = hapd.wait_event(["WPS-NEW-AP-SETTINGS"], timeout=5)
+        if ev is None:
+            raise Exception("New AP settings not reported")
+        logger.debug("Receive WSC_Done from AP")
+        msg = get_wsc_msg(hapd)
+        if msg['wsc_opcode'] != WSC_Done:
+            raise Exception("Unexpected message - expected WSC_Done")
+
+        logger.debug("Send WSC_ACK to AP")
+        ack,attrs = build_ack(msg['eap_identifier'], e_nonce, r_nonce,
+                              eap_code=2)
+        send_wsc_msg(hapd, addr, ack)
+        dev[0].wait_disconnected()
+    else:
+        ev = hapd.wait_event(["WPS-FAIL"], timeout=5)
+        if ev is None:
+            raise Exception("WPS failure not reported")
+        logger.debug("Receive WSC_NACK from AP")
+        msg = get_wsc_msg(hapd)
+        if msg['wsc_opcode'] != WSC_NACK:
+            raise Exception("Unexpected message - expected WSC_NACK")
+
+        logger.debug("Send WSC_NACK to AP")
+        nack,attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
+                                eap_code=2)
+        send_wsc_msg(hapd, addr, nack)
+        dev[0].wait_disconnected()
+
+def test_wps_ext_ap_settings_success(dev, apdev):
+    """WPS and AP Settings: success"""
+    ap_settings = build_wsc_attr(ATTR_NETWORK_INDEX, '\x01')
+    ap_settings += build_wsc_attr(ATTR_SSID, "test")
+    ap_settings += build_wsc_attr(ATTR_AUTH_TYPE, '\x00\x01')
+    ap_settings += build_wsc_attr(ATTR_ENCR_TYPE, '\x00\x01')
+    ap_settings += build_wsc_attr(ATTR_NETWORK_KEY, '')
+    ap_settings += build_wsc_attr(ATTR_MAC_ADDR, binascii.unhexlify(apdev[0]['bssid'].replace(':', '')))
+    wps_run_ap_settings_proto(dev, apdev, ap_settings, True)
+
+def test_wps_ext_ap_settings_missing(dev, apdev):
+    """WPS and AP Settings: missing"""
+    wps_run_ap_settings_proto(dev, apdev, None, False)
+
+def test_wps_ext_ap_settings_mac_addr_mismatch(dev, apdev):
+    """WPS and AP Settings: MAC Address mismatch"""
+    ap_settings = build_wsc_attr(ATTR_NETWORK_INDEX, '\x01')
+    ap_settings += build_wsc_attr(ATTR_SSID, "test")
+    ap_settings += build_wsc_attr(ATTR_AUTH_TYPE, '\x00\x01')
+    ap_settings += build_wsc_attr(ATTR_ENCR_TYPE, '\x00\x01')
+    ap_settings += build_wsc_attr(ATTR_NETWORK_KEY, '')
+    ap_settings += build_wsc_attr(ATTR_MAC_ADDR, '\x00\x00\x00\x00\x00\x00')
+    wps_run_ap_settings_proto(dev, apdev, ap_settings, True)
+
+def test_wps_ext_ap_settings_mac_addr_missing(dev, apdev):
+    """WPS and AP Settings: missing MAC Address"""
+    ap_settings = build_wsc_attr(ATTR_NETWORK_INDEX, '\x01')
+    ap_settings += build_wsc_attr(ATTR_SSID, "test")
+    ap_settings += build_wsc_attr(ATTR_AUTH_TYPE, '\x00\x01')
+    ap_settings += build_wsc_attr(ATTR_ENCR_TYPE, '\x00\x01')
+    ap_settings += build_wsc_attr(ATTR_NETWORK_KEY, '')
+    wps_run_ap_settings_proto(dev, apdev, ap_settings, False)
+
+def test_wps_ext_ap_settings_reject_encr_type(dev, apdev):
+    """WPS and AP Settings: reject Encr Type"""
+    ap_settings = build_wsc_attr(ATTR_NETWORK_INDEX, '\x01')
+    ap_settings += build_wsc_attr(ATTR_SSID, "test")
+    ap_settings += build_wsc_attr(ATTR_AUTH_TYPE, '\x00\x01')
+    ap_settings += build_wsc_attr(ATTR_ENCR_TYPE, '\x00\x00')
+    ap_settings += build_wsc_attr(ATTR_NETWORK_KEY, '')
+    ap_settings += build_wsc_attr(ATTR_MAC_ADDR, binascii.unhexlify(apdev[0]['bssid'].replace(':', '')))
+    wps_run_ap_settings_proto(dev, apdev, ap_settings, False)
+
+def test_wps_ext_ap_settings_m2d(dev, apdev):
+    """WPS and AP Settings: M2D"""
+    addr,bssid,hapd = wps_start_ext_reg(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive M1 from AP")
+    msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M1)
+    e_nonce = m1_attrs[ATTR_ENROLLEE_NONCE]
+
+    r_nonce = 16*'\x44'
+    uuid_r = 16*'\x33'
+
+    logger.debug("Send M2D to AP")
+    m2d, raw_m2d_attrs = build_m2d(raw_m1_attrs, msg['eap_identifier'],
+                                   e_nonce, r_nonce, uuid_r,
+                                   dev_pw_id='\x00\x00', eap_code=2)
+    send_wsc_msg(hapd, addr, m2d)
+
+    ev = hapd.wait_event(["WPS-M2D"], timeout=5)
+    if ev is None:
+        raise Exception("M2D not reported")
+
+    wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+def wps_wait_ap_nack(hapd, dev, e_nonce, r_nonce):
+    logger.debug("Receive WSC_NACK from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_NACK:
+        raise Exception("Unexpected message - expected WSC_NACK")
+
+    logger.debug("Send WSC_NACK to AP")
+    nack,attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
+                            eap_code=2)
+    send_wsc_msg(hapd, dev.own_addr(), nack)
+    dev.wait_disconnected()
+
+def test_wps_ext_m3_missing_e_hash1(dev, apdev):
+    """WPS proto: M3 missing E-Hash1"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send M3 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M3)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    #attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+    attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m3)
+
+    wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+def test_wps_ext_m3_missing_e_hash2(dev, apdev):
+    """WPS proto: M3 missing E-Hash2"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send M3 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M3)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+    #attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m3)
+
+    wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+def test_wps_ext_m5_missing_e_snonce1(dev, apdev):
+    """WPS proto: M5 missing E-SNonce1"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send M3 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M3)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+    attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m3)
+
+    logger.debug("Receive M4 from AP")
+    msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+    logger.debug("Send M5 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M5)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    #data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
+    data = ''
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+    raw_m5_attrs = attrs
+    m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m5)
+
+    wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+def test_wps_ext_m5_e_snonce1_mismatch(dev, apdev):
+    """WPS proto: M5 E-SNonce1 mismatch"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send M3 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M3)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+    attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m3)
+
+    logger.debug("Receive M4 from AP")
+    msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+    logger.debug("Send M5 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M5)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    data = build_wsc_attr(ATTR_E_SNONCE1, 16*'\x00')
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+    raw_m5_attrs = attrs
+    m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m5)
+
+    wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+def test_wps_ext_m7_missing_e_snonce2(dev, apdev):
+    """WPS proto: M7 missing E-SNonce2"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send M3 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M3)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+    attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m3)
+
+    logger.debug("Receive M4 from AP")
+    msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+    logger.debug("Send M5 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M5)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+    raw_m5_attrs = attrs
+    m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m5)
+
+    logger.debug("Receive M6 from AP")
+    msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
+
+    logger.debug("Send M7 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M7)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    #data = build_wsc_attr(ATTR_E_SNONCE2, e_s2)
+    data = ''
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
+    m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    raw_m7_attrs = attrs
+    send_wsc_msg(hapd, addr, m7)
+
+    wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+def test_wps_ext_m7_e_snonce2_mismatch(dev, apdev):
+    """WPS proto: M7 E-SNonce2 mismatch"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send M3 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M3)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+    attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m3)
+
+    logger.debug("Receive M4 from AP")
+    msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+    logger.debug("Send M5 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M5)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+    raw_m5_attrs = attrs
+    m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m5)
+
+    logger.debug("Receive M6 from AP")
+    msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
+
+    logger.debug("Send M7 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M7)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    data = build_wsc_attr(ATTR_E_SNONCE2, 16*'\x00')
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
+    m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    raw_m7_attrs = attrs
+    send_wsc_msg(hapd, addr, m7)
+
+    wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+def test_wps_ext_m1_pubkey_oom(dev, apdev):
+    """WPS proto: M1 PubKey OOM"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    with alloc_fail(hapd, 1, "wpabuf_alloc_copy;wps_process_pubkey"):
+        m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                    e_nonce, e_pk)
+        send_wsc_msg(hapd, addr, m1)
+        wps_wait_eap_failure(hapd, dev[0])
+
+def wps_wait_eap_failure(hapd, dev):
+    ev = hapd.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+    if ev is None:
+        raise Exception("EAP-Failure not reported")
+    dev.wait_disconnected()
+
+def test_wps_ext_m3_m1(dev, apdev):
+    """WPS proto: M3 replaced with M1"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send M3(M1) to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M1)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+    attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m3)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m5_m3(dev, apdev):
+    """WPS proto: M5 replaced with M3"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send M3 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M3)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+    attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m3)
+
+    logger.debug("Receive M4 from AP")
+    msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+    logger.debug("Send M5(M3) to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M3)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+    raw_m5_attrs = attrs
+    m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m5)
+
+    wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+def test_wps_ext_m3_m2(dev, apdev):
+    """WPS proto: M3 replaced with M2"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send M3(M2) to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M2)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m3)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m3_m5(dev, apdev):
+    """WPS proto: M3 replaced with M5"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send M3(M5) to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M5)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+    attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m3)
+
+    wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+def test_wps_ext_m3_m7(dev, apdev):
+    """WPS proto: M3 replaced with M7"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send M3(M7) to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M7)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+    attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m3)
+
+    wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+def test_wps_ext_m3_done(dev, apdev):
+    """WPS proto: M3 replaced with WSC_Done"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send M3(WSC_Done) to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_WSC_DONE)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
+    send_wsc_msg(hapd, addr, m3)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m2_nack_invalid(dev, apdev):
+    """WPS proto: M2 followed by invalid NACK"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send WSC_NACK to AP")
+    attrs = '\x10\x00\x00'
+    nack = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_NACK)
+    send_wsc_msg(hapd, addr, nack)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m2_nack_no_msg_type(dev, apdev):
+    """WPS proto: M2 followed by NACK without Msg Type"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send WSC_NACK to AP")
+    nack,attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
+                            msg_type=None, eap_code=2)
+    send_wsc_msg(hapd, addr, nack)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m2_nack_invalid_msg_type(dev, apdev):
+    """WPS proto: M2 followed by NACK with invalid Msg Type"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send WSC_NACK to AP")
+    nack,attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
+                            msg_type=WPS_WSC_ACK, eap_code=2)
+    send_wsc_msg(hapd, addr, nack)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m2_nack_e_nonce_mismatch(dev, apdev):
+    """WPS proto: M2 followed by NACK with e-nonce mismatch"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send WSC_NACK to AP")
+    nack,attrs = build_nack(msg['eap_identifier'], 16*'\x00', r_nonce,
+                            eap_code=2)
+    send_wsc_msg(hapd, addr, nack)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m2_nack_no_config_error(dev, apdev):
+    """WPS proto: M2 followed by NACK without Config Error"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send WSC_NACK to AP")
+    nack,attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
+                            config_error=None, eap_code=2)
+    send_wsc_msg(hapd, addr, nack)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m2_ack_invalid(dev, apdev):
+    """WPS proto: M2 followed by invalid ACK"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send WSC_ACK to AP")
+    attrs = '\x10\x00\x00'
+    ack = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_ACK)
+    send_wsc_msg(hapd, addr, ack)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m2_ack(dev, apdev):
+    """WPS proto: M2 followed by ACK"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send WSC_ACK to AP")
+    ack,attrs = build_ack(msg['eap_identifier'], e_nonce, r_nonce, eap_code=2)
+    send_wsc_msg(hapd, addr, ack)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m2_ack_no_msg_type(dev, apdev):
+    """WPS proto: M2 followed by ACK missing Msg Type"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send WSC_ACK to AP")
+    ack,attrs = build_ack(msg['eap_identifier'], e_nonce, r_nonce,
+                          msg_type=None, eap_code=2)
+    send_wsc_msg(hapd, addr, ack)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m2_ack_invalid_msg_type(dev, apdev):
+    """WPS proto: M2 followed by ACK with invalid Msg Type"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send WSC_ACK to AP")
+    ack,attrs = build_ack(msg['eap_identifier'], e_nonce, r_nonce,
+                          msg_type=WPS_WSC_NACK, eap_code=2)
+    send_wsc_msg(hapd, addr, ack)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m2_ack_e_nonce_mismatch(dev, apdev):
+    """WPS proto: M2 followed by ACK with e-nonce mismatch"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send WSC_ACK to AP")
+    ack,attrs = build_ack(msg['eap_identifier'], 16*'\x00', r_nonce,
+                          eap_code=2)
+    send_wsc_msg(hapd, addr, ack)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m1_invalid(dev, apdev):
+    """WPS proto: M1 failing parsing"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    logger.debug("Send M1 to AP")
+    attrs = '\x10\x00\x00'
+    m1 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m1)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m1_missing_msg_type(dev, apdev):
+    """WPS proto: M1 missing Msg Type"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    logger.debug("Send M1 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    m1 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m1)
+
+    wps_wait_ap_nack(hapd, dev[0], 16*'\x00', 16*'\x00')
+
+def wps_ext_wsc_done(dev, apdev):
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send M3 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M3)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+    attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m3)
+
+    logger.debug("Receive M4 from AP")
+    msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+    logger.debug("Send M5 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M5)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+    raw_m5_attrs = attrs
+    m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m5)
+
+    logger.debug("Receive M6 from AP")
+    msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
+
+    logger.debug("Send M7 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M7)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    data = build_wsc_attr(ATTR_E_SNONCE2, e_s2)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
+    m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    raw_m7_attrs = attrs
+    send_wsc_msg(hapd, addr, m7)
+
+    logger.debug("Receive M8 from AP")
+    msg, m8_attrs, raw_m8_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M8)
+    return hapd, msg, e_nonce, r_nonce
+
+def test_wps_ext_wsc_done_invalid(dev, apdev):
+    """WPS proto: invalid WSC_Done"""
+    hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
+
+    logger.debug("Send WSC_Done to AP")
+    attrs = '\x10\x00\x00'
+    wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
+    send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_wsc_done_no_msg_type(dev, apdev):
+    """WPS proto: invalid WSC_Done"""
+    hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
+
+    logger.debug("Send WSC_Done to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    #attrs += build_attr_msg_type(WPS_WSC_DONE)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
+    send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_wsc_done_wrong_msg_type(dev, apdev):
+    """WPS proto: WSC_Done with wrong Msg Type"""
+    hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
+
+    logger.debug("Send WSC_Done to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_WSC_ACK)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
+    send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_wsc_done_no_e_nonce(dev, apdev):
+    """WPS proto: WSC_Done without e_nonce"""
+    hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
+
+    logger.debug("Send WSC_Done to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_WSC_DONE)
+    #attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
+    send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_wsc_done_no_r_nonce(dev, apdev):
+    """WPS proto: WSC_Done without r_nonce"""
+    hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
+
+    logger.debug("Send WSC_Done to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_WSC_DONE)
+    attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+    #attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
+    send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
+
+    wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m7_no_encr_settings(dev, apdev):
+    """WPS proto: M7 without Encr Settings"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk)
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+    r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+    r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+    authkey,keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+                                    r_nonce)
+    e_s1,e_s2,e_hash1,e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+    logger.debug("Send M3 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M3)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+    attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+    attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+    raw_m3_attrs = attrs
+    m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m3)
+
+    logger.debug("Receive M4 from AP")
+    msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+    logger.debug("Send M5 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M5)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
+    attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+    raw_m5_attrs = attrs
+    m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    send_wsc_msg(hapd, addr, m5)
+
+    logger.debug("Receive M6 from AP")
+    msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
+
+    logger.debug("Send M7 to AP")
+    attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+    attrs += build_attr_msg_type(WPS_M7)
+    attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+    #data = build_wsc_attr(ATTR_E_SNONCE2, e_s2)
+    #attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+    attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
+    m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+    raw_m7_attrs = attrs
+    send_wsc_msg(hapd, addr, m7)
+
+    wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+def test_wps_ext_m1_workaround(dev, apdev):
+    """WPS proto: M1 Manufacturer/Model workaround"""
+    pin = "12345670"
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+    logger.debug("Receive WSC/Start from AP")
+    msg = get_wsc_msg(hapd)
+    if msg['wsc_opcode'] != WSC_Start:
+        raise Exception("Unexpected Op-Code for WSC/Start")
+
+    mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+    uuid_e = 16*'\x11'
+    e_nonce = 16*'\x22'
+    own_private, e_pk = wsc_dh_init()
+
+    logger.debug("Send M1 to AP")
+    m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+                                e_nonce, e_pk, manufacturer='Apple TEST',
+                                model_name='AirPort', config_methods='\xff\xff')
+    send_wsc_msg(hapd, addr, m1)
+
+    logger.debug("Receive M2 from AP")
+    msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+
+def test_ap_wps_disable_enable(dev, apdev):
+    """WPS and DISABLE/ENABLE AP"""
+    hapd = wps_start_ap(apdev[0])
+    hapd.disable()
+    hapd.enable()
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
diff --git a/hostap/tests/hwsim/test_autoscan.py b/hostap/tests/hwsim/test_autoscan.py
new file mode 100644
index 0000000..97d29e9
--- /dev/null
+++ b/hostap/tests/hwsim/test_autoscan.py
@@ -0,0 +1,79 @@
+# autoscan 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.
+
+import time
+import logging
+logger = logging.getLogger()
+import os
+
+import hostapd
+
+def test_autoscan_periodic(dev, apdev):
+    """autoscan_periodic"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "autoscan" })
+
+    try:
+        if "OK" not in dev[0].request("AUTOSCAN periodic:1"):
+            raise Exception("Failed to set autoscan")
+        id = dev[0].connect("not-used", key_mgmt="NONE", scan_freq="2412",
+                            wait_connect=False)
+        times = {}
+        for i in range(0, 3):
+            logger.info("Waiting for scan to start")
+            start = os.times()[4]
+            ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+            if ev is None:
+                raise Exception("did not start a scan")
+            stop = os.times()[4]
+            times[i] = stop - start
+            logger.info("Waiting for scan to complete")
+            ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+            if ev is None:
+                raise Exception("did not complete a scan")
+        if times[0] > 1 or times[1] < 0.5 or times[1] > 1.5 or times[2] < 0.5 or times[2] > 1.5:
+            raise Exception("Unexpected scan timing: " + str(times))
+
+        # scan some more channels to allow some more time for reseting AUTOSCAN
+        # while a scan is in progress
+        dev[0].set_network(id, "scan_freq", "2412 2437 2462 5180 5200 5220 5240")
+        dev[0].dump_monitor()
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+        if ev is None:
+            raise Exception("did not start a scan")
+        if "OK" not in dev[0].request("AUTOSCAN periodic:2"):
+            raise Exception("Failed to (re)set autoscan")
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+        if ev is None:
+            raise Exception("did not complete a scan")
+    finally:
+        dev[0].request("AUTOSCAN ")
+
+def test_autoscan_exponential(dev, apdev):
+    """autoscan_exponential"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "autoscan" })
+
+    try:
+        if "OK" not in dev[0].request("AUTOSCAN exponential:2:10"):
+            raise Exception("Failed to set autoscan")
+        dev[0].connect("not-used", key_mgmt="NONE", scan_freq="2412",
+                       wait_connect=False)
+        times = {}
+        for i in range(0, 3):
+            logger.info("Waiting for scan to start")
+            start = os.times()[4]
+            ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+            if ev is None:
+                raise Exception("did not start a scan")
+            stop = os.times()[4]
+            times[i] = stop - start
+            logger.info("Waiting for scan to complete")
+            ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+            if ev is None:
+                raise Exception("did not complete a scan")
+        if times[0] > 1 or times[1] < 1 or times[1] > 3 or times[2] < 3 or times[2] > 5:
+            raise Exception("Unexpected scan timing: " + str(times))
+    finally:
+        dev[0].request("AUTOSCAN ")
diff --git a/hostap/tests/hwsim/test_bgscan.py b/hostap/tests/hwsim/test_bgscan.py
new file mode 100644
index 0000000..c73fdeb
--- /dev/null
+++ b/hostap/tests/hwsim/test_bgscan.py
@@ -0,0 +1,167 @@
+# bgscan 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.
+
+import time
+import logging
+logger = logging.getLogger()
+import os
+
+import hostapd
+
+def test_bgscan_simple(dev, apdev):
+    """bgscan_simple"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "bgscan" })
+    hostapd.add_ap(apdev[1]['ifname'], { "ssid": "bgscan" })
+
+    dev[0].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+                   bgscan="simple:1:-20:2")
+    dev[1].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+                   bgscan="simple:1:-45:2")
+
+    dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+                   bgscan="simple:1:-45")
+    dev[2].request("REMOVE_NETWORK all")
+    dev[2].wait_disconnected()
+
+    dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+                   bgscan="simple:0:0")
+    dev[2].request("REMOVE_NETWORK all")
+    dev[2].wait_disconnected()
+
+    dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+                   bgscan="simple")
+    dev[2].request("REMOVE_NETWORK all")
+    dev[2].wait_disconnected()
+
+    dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+                   bgscan="simple:1")
+    dev[2].request("REMOVE_NETWORK all")
+    dev[2].wait_disconnected()
+
+    ev = dev[0].wait_event(["CTRL-EVENT-SIGNAL-CHANGE"], timeout=10)
+    if ev is None:
+        raise Exception("dev0 did not indicate signal change event")
+    if "above=0" not in ev:
+        raise Exception("Unexpected signal change event contents from dev0: " + ev)
+
+    ev = dev[1].wait_event(["CTRL-EVENT-SIGNAL-CHANGE"], timeout=10)
+    if ev is None:
+        raise Exception("dev1 did not indicate signal change event")
+    if "above=1" not in ev:
+        raise Exception("Unexpected signal change event contents from dev1: " + ev)
+
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=3)
+    if ev is None:
+        raise Exception("dev0 did not start a scan")
+
+    ev = dev[1].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=3)
+    if ev is None:
+        raise Exception("dev1 did not start a scan")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+    if ev is None:
+        raise Exception("dev0 did not complete a scan")
+    ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+    if ev is None:
+        raise Exception("dev1 did not complete a scan")
+
+def test_bgscan_learn(dev, apdev):
+    """bgscan_learn"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "bgscan" })
+    hostapd.add_ap(apdev[1]['ifname'], { "ssid": "bgscan" })
+
+    try:
+        os.remove("/tmp/test_bgscan_learn.bgscan")
+    except:
+        pass
+
+    try:
+        dev[0].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+                       bgscan="learn:1:-20:2")
+        id = dev[1].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+                            bgscan="learn:1:-45:2:/tmp/test_bgscan_learn.bgscan")
+
+        dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+                       bgscan="learn:1:-45")
+        dev[2].request("REMOVE_NETWORK all")
+        dev[2].wait_disconnected()
+
+        dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+                       bgscan="learn:0:0")
+        dev[2].request("REMOVE_NETWORK all")
+        dev[2].wait_disconnected()
+
+        dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+                       bgscan="learn")
+        dev[2].request("REMOVE_NETWORK all")
+        dev[2].wait_disconnected()
+
+        dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+                       bgscan="learn:1")
+        dev[2].request("REMOVE_NETWORK all")
+        dev[2].wait_disconnected()
+
+        ev = dev[0].wait_event(["CTRL-EVENT-SIGNAL-CHANGE"], timeout=10)
+        if ev is None:
+            raise Exception("dev0 did not indicate signal change event")
+        if "above=0" not in ev:
+            raise Exception("Unexpected signal change event contents from dev0: " + ev)
+
+        ev = dev[1].wait_event(["CTRL-EVENT-SIGNAL-CHANGE"], timeout=10)
+        if ev is None:
+            raise Exception("dev1 did not indicate signal change event")
+        if "above=1" not in ev:
+            raise Exception("Unexpected signal change event contents from dev1: " + ev)
+
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=3)
+        if ev is None:
+            raise Exception("dev0 did not start a scan")
+
+        ev = dev[1].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=3)
+        if ev is None:
+            raise Exception("dev1 did not start a scan")
+
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+        if ev is None:
+            raise Exception("dev0 did not complete a scan")
+        ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+        if ev is None:
+            raise Exception("dev1 did not complete a scan")
+
+        dev[0].request("DISCONNECT")
+        dev[1].request("DISCONNECT")
+        dev[0].request("REMOVE_NETWORK all")
+
+        with open("/tmp/test_bgscan_learn.bgscan", "r") as f:
+            lines = f.read().splitlines()
+        if lines[0] != "wpa_supplicant-bgscan-learn":
+            raise Exception("Unexpected bgscan header line")
+        if 'BSS 02:00:00:00:03:00 2412' not in lines:
+            raise Exception("Missing BSS1")
+        if 'BSS 02:00:00:00:04:00 2412' not in lines:
+            raise Exception("Missing BSS2")
+        if 'NEIGHBOR 02:00:00:00:03:00 02:00:00:00:04:00' not in lines:
+            raise Exception("Missing BSS1->BSS2 neighbor entry")
+        if 'NEIGHBOR 02:00:00:00:04:00 02:00:00:00:03:00' not in lines:
+            raise Exception("Missing BSS2->BSS1 neighbor entry")
+
+        dev[1].set_network(id, "scan_freq", "")
+        dev[1].connect_network(id)
+
+        ev = dev[1].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=10)
+        if ev is None:
+            raise Exception("dev1 did not start a scan")
+
+        ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+        if ev is None:
+            raise Exception("dev1 did not complete a scan")
+
+        dev[1].request("REMOVE_NETWORK all")
+    finally:
+        try:
+            os.remove("/tmp/test_bgscan_learn.bgscan")
+        except:
+            pass
diff --git a/hostap/tests/hwsim/test_cfg80211.py b/hostap/tests/hwsim/test_cfg80211.py
new file mode 100644
index 0000000..e67ad98
--- /dev/null
+++ b/hostap/tests/hwsim/test_cfg80211.py
@@ -0,0 +1,143 @@
+# cfg80211 test cases
+# 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.
+
+import logging
+logger = logging.getLogger()
+import binascii
+import os
+import subprocess
+import time
+
+import hostapd
+import hwsim_utils
+from tshark import run_tshark
+from nl80211 import *
+
+def nl80211_command(dev, cmd, attr):
+    res = dev.request("VENDOR ffffffff {} {}".format(nl80211_cmd[cmd],
+                                                     binascii.hexlify(attr)))
+    if "FAIL" in res:
+        raise Exception("nl80211 command failed")
+    return binascii.unhexlify(res)
+
+def test_cfg80211_disassociate(dev, apdev):
+    """cfg80211 disassociation command"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+    ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
+    if ev is None:
+        raise Exception("No connection event received from hostapd")
+
+    ifindex = int(dev[0].get_driver_status_field("ifindex"))
+    attrs = build_nl80211_attr_u32('IFINDEX', ifindex)
+    attrs += build_nl80211_attr_u16('REASON_CODE', 1)
+    attrs += build_nl80211_attr_mac('MAC', apdev[0]['bssid'])
+    nl80211_command(dev[0], 'DISASSOCIATE', attrs)
+
+    ev = hapd.wait_event([ "AP-STA-DISCONNECTED" ], timeout=5)
+    if ev is None:
+        raise Exception("No disconnection event received from hostapd")
+
+def nl80211_frame(dev, ifindex, frame, freq=None, duration=None, offchannel_tx_ok=False):
+    attrs = build_nl80211_attr_u32('IFINDEX', ifindex)
+    if freq is not None:
+        attrs += build_nl80211_attr_u32('WIPHY_FREQ', freq)
+    if duration is not None:
+        attrs += build_nl80211_attr_u32('DURATION', duration)
+    if offchannel_tx_ok:
+        attrs += build_nl80211_attr_flag('OFFCHANNEL_TX_OK')
+    attrs += build_nl80211_attr('FRAME', frame)
+    return parse_nl80211_attrs(nl80211_command(dev, 'FRAME', attrs))
+
+def nl80211_frame_wait_cancel(dev, ifindex, cookie):
+    attrs = build_nl80211_attr_u32('IFINDEX', ifindex)
+    attrs += build_nl80211_attr('COOKIE', cookie)
+    return nl80211_command(dev, 'FRAME_WAIT_CANCEL', attrs)
+
+def nl80211_remain_on_channel(dev, ifindex, freq, duration):
+    attrs = build_nl80211_attr_u32('IFINDEX', ifindex)
+    attrs += build_nl80211_attr_u32('WIPHY_FREQ', freq)
+    attrs += build_nl80211_attr_u32('DURATION', duration)
+    return nl80211_command(dev, 'REMAIN_ON_CHANNEL', attrs)
+
+def test_cfg80211_tx_frame(dev, apdev, params):
+    """cfg80211 offchannel TX frame command"""
+    ifindex = int(dev[0].get_driver_status_field("ifindex"))
+
+    frame = binascii.unhexlify("d000000002000000010002000000000002000000010000000409506f9a090001dd5e506f9a0902020025080401001f0502006414060500585804510b0906000200000000000b1000585804510b0102030405060708090a0b0d1d000200000000000108000000000000000000101100084465766963652041110500585804510bdd190050f204104a0001101012000200011049000600372a000120")
+
+    dev[0].request("P2P_GROUP_ADD freq=2412")
+    res = nl80211_frame(dev[0], ifindex, frame, freq=2422, duration=500,
+                        offchannel_tx_ok=True)
+    time.sleep(0.1)
+
+    # note: Uncommenting this seems to remove the incorrect channel issue
+    #nl80211_frame_wait_cancel(dev[0], ifindex, res[nl80211_attr['COOKIE']])
+
+    # note: this Action frame ends up getting sent incorrectly on 2422 MHz
+    nl80211_frame(dev[0], ifindex, frame, freq=2412)
+    time.sleep(1.5)
+    # note: also the Deauthenticate frame sent by the GO going down ends up
+    # being transmitted incorrectly on 2422 MHz.
+
+    out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+                     "wlan.fc.type_subtype == 13", ["radiotap.channel.freq"])
+    if out is not None:
+        freq = out.splitlines()
+        if len(freq) != 2:
+            raise Exception("Unexpected number of Action frames (%d)" % len(freq))
+        if freq[0] != "2422":
+            raise Exception("First Action frame on unexpected channel: %s MHz" % freq[0])
+        if freq[1] != "2412":
+            raise Exception("Second Action frame on unexpected channel: %s MHz" % freq[1])
+
+def test_cfg80211_wep_key_idx_change(dev, apdev):
+    """WEP Shared Key authentication and key index change without deauth"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": "wep-shared-key",
+                            "wep_key0": '"hello12345678"',
+                            "wep_key1": '"other12345678"',
+                            "auth_algs": "2" })
+    id = dev[0].connect("wep-shared-key", key_mgmt="NONE", auth_alg="SHARED",
+                        wep_key0='"hello12345678"',
+                        wep_key1='"other12345678"',
+                        wep_tx_keyidx="0",
+                        scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+    dev[0].set_network(id, "wep_tx_keyidx", "1")
+
+    # clear cfg80211 auth state to allow new auth without deauth frame
+    ifindex = int(dev[0].get_driver_status_field("ifindex"))
+    attrs = build_nl80211_attr_u32('IFINDEX', ifindex)
+    attrs += build_nl80211_attr_u16('REASON_CODE', 1)
+    attrs += build_nl80211_attr_mac('MAC', apdev[0]['bssid'])
+    attrs += build_nl80211_attr_flag('LOCAL_STATE_CHANGE')
+    nl80211_command(dev[0], 'DEAUTHENTICATE', attrs)
+    dev[0].wait_disconnected(timeout=5, error="Local-deauth timed out")
+
+    # the previous command results in deauth event followed by auto-reconnect
+    dev[0].wait_connected(timeout=10, error="Reassociation timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_cfg80211_hostapd_ext_sta_remove(dev, apdev):
+    """cfg80211 DEL_STATION issued externally to hostapd"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": "open" })
+    id = dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+    ifindex = int(hapd.get_driver_status_field("ifindex"))
+    attrs = build_nl80211_attr_u32('IFINDEX', ifindex)
+    attrs += build_nl80211_attr_u16('REASON_CODE', 1)
+    attrs += build_nl80211_attr_u8('MGMT_SUBTYPE', 12)
+    attrs += build_nl80211_attr_mac('MAC', dev[0].own_addr())
+    nl80211_command(hapd, 'DEL_STATION', attrs)
+
+    # Currently, hostapd ignores the NL80211_CMD_DEL_STATION event if
+    # drv->device_ap_sme == 0 (which is the case with mac80211_hwsim), so no
+    # further action happens here. If that event were to be used to remove the
+    # STA entry from hostapd even in device_ap_sme == 0 case, this test case
+    # could be extended to cover additional operations.
diff --git a/hostap/tests/hwsim/test_connect_cmd.py b/hostap/tests/hwsim/test_connect_cmd.py
new file mode 100644
index 0000000..3f4c42b
--- /dev/null
+++ b/hostap/tests/hwsim/test_connect_cmd.py
@@ -0,0 +1,153 @@
+# cfg80211 connect command (SME in the driver/firmware)
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+
+import hwsim_utils
+import hostapd
+from wpasupplicant import WpaSupplicant
+from test_p2p_grpform import go_neg_pin_authorized
+from test_p2p_grpform import check_grpform_results
+from test_p2p_grpform import remove_group
+
+def test_connect_cmd_open(dev, apdev):
+    """Open connection using cfg80211 connect command"""
+    params = { "ssid": "sta-connect",
+               "manage_p2p": "1",
+               "allow_cross_connection": "1" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+    wpas.connect("sta-connect", key_mgmt="NONE", scan_freq="2412",
+                 bg_scan_period="1")
+    wpas.request("DISCONNECT")
+
+def test_connect_cmd_wep(dev, apdev):
+    """WEP Open System using cfg80211 connect command"""
+    params = { "ssid": "sta-connect-wep", "wep_key0": '"hello"' }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+    wpas.connect("sta-connect-wep", key_mgmt="NONE", scan_freq="2412",
+                 wep_key0='"hello"')
+    hwsim_utils.test_connectivity(wpas, hapd)
+    wpas.request("DISCONNECT")
+
+def test_connect_cmd_wep_shared(dev, apdev):
+    """WEP Shared key using cfg80211 connect command"""
+    params = { "ssid": "sta-connect-wep", "wep_key0": '"hello"',
+               "auth_algs": "2" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+    id = wpas.connect("sta-connect-wep", key_mgmt="NONE", scan_freq="2412",
+                      auth_alg="SHARED", wep_key0='"hello"')
+    hwsim_utils.test_connectivity(wpas, hapd)
+    wpas.request("DISCONNECT")
+    wpas.remove_network(id)
+    wpas.connect("sta-connect-wep", key_mgmt="NONE", scan_freq="2412",
+                 auth_alg="OPEN SHARED", wep_key0='"hello"')
+    hwsim_utils.test_connectivity(wpas, hapd)
+    wpas.request("DISCONNECT")
+
+def test_connect_cmd_p2p_management(dev, apdev):
+    """Open connection using cfg80211 connect command and AP using P2P management"""
+    params = { "ssid": "sta-connect",
+               "manage_p2p": "1",
+               "allow_cross_connection": "0" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+    wpas.connect("sta-connect", key_mgmt="NONE", scan_freq="2412")
+    wpas.request("DISCONNECT")
+
+def test_connect_cmd_wpa2_psk(dev, apdev):
+    """WPA2-PSK connection using cfg80211 connect command"""
+    params = hostapd.wpa2_params(ssid="sta-connect", passphrase="12345678")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+    wpas.connect("sta-connect", psk="12345678", scan_freq="2412")
+    wpas.request("DISCONNECT")
+
+def test_connect_cmd_concurrent_grpform_while_connecting(dev, apdev):
+    """Concurrent P2P group formation while connecting to an AP using cfg80211 connect command"""
+    logger.info("Start connection to an infrastructure AP")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+    wpas.connect("test-open", key_mgmt="NONE", wait_connect=False)
+
+    logger.info("Form a P2P group while connecting to an AP")
+    wpas.request("SET p2p_no_group_iface 0")
+
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_freq=2412,
+                                           r_dev=wpas, r_freq=2412)
+    check_grpform_results(i_res, r_res)
+    remove_group(dev[0], wpas)
+
+    logger.info("Confirm AP connection after P2P group removal")
+    hwsim_utils.test_connectivity(wpas, hapd)
+
+def test_connect_cmd_reject_assoc(dev, apdev):
+    """Connection using cfg80211 connect command getting rejected"""
+    params = { "ssid": "sta-connect",
+               "require_ht": "1" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+    wpas.connect("sta-connect", key_mgmt="NONE", scan_freq="2412",
+                 disable_ht="1", wait_connect=False)
+    # Reject event gets reported twice since we force connect command to be used
+    # with a driver that supports auth+assoc for testing purposes.
+    for i in range(0, 2):
+        ev = wpas.wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=15)
+        if ev is None:
+            raise Exception("Association rejection timed out")
+        if "status_code=27" not in ev:
+            raise Exception("Unexpected rejection status code")
+
+def test_connect_cmd_disconnect_event(dev, apdev):
+    """Connection using cfg80211 connect command getting disconnected by the AP"""
+    params = { "ssid": "sta-connect" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+    wpas.connect("sta-connect", key_mgmt="NONE", scan_freq="2412")
+
+    if "OK" not in hapd.request("DEAUTHENTICATE " + wpas.p2p_interface_addr()):
+        raise Exception("DEAUTHENTICATE command failed")
+    ev = wpas.wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+    if ev is None:
+        raise Exception("Disconnection event timed out")
+    # This event was actually based on deauthenticate event since we force
+    # connect command to be used with a driver that supports auth+assoc for
+    # testing purposes. Anyway, wait some time to allow the debug log to capture
+    # the following NL80211_CMD_DISCONNECT event.
+    time.sleep(0.1)
+
+def test_connect_cmd_roam(dev, apdev):
+    """cfg80211 connect command to trigger roam"""
+    params = { "ssid": "sta-connect" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+    wpas.connect("sta-connect", key_mgmt="NONE", scan_freq="2412")
+
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    wpas.scan_for_bss(apdev[1]['bssid'], freq=2412)
+    wpas.roam(apdev[1]['bssid'])
diff --git a/hostap/tests/hwsim/test_dbus.py b/hostap/tests/hwsim/test_dbus.py
new file mode 100644
index 0000000..dba899e
--- /dev/null
+++ b/hostap/tests/hwsim/test_dbus.py
@@ -0,0 +1,4918 @@
+# wpa_supplicant D-Bus interface tests
+# Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import logging
+logger = logging.getLogger()
+import subprocess
+import time
+
+try:
+    import gobject
+    import dbus
+    dbus_imported = True
+except ImportError:
+    dbus_imported = False
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import HwsimSkip, alloc_fail, fail_test
+from test_ap_tdls import connect_2sta_open
+
+WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_PATH = "/fi/w1/wpa_supplicant1"
+WPAS_DBUS_IFACE = "fi.w1.wpa_supplicant1.Interface"
+WPAS_DBUS_IFACE_WPS = WPAS_DBUS_IFACE + ".WPS"
+WPAS_DBUS_NETWORK = "fi.w1.wpa_supplicant1.Network"
+WPAS_DBUS_BSS = "fi.w1.wpa_supplicant1.BSS"
+WPAS_DBUS_IFACE_P2PDEVICE = WPAS_DBUS_IFACE + ".P2PDevice"
+WPAS_DBUS_P2P_PEER = "fi.w1.wpa_supplicant1.Peer"
+WPAS_DBUS_GROUP = "fi.w1.wpa_supplicant1.Group"
+WPAS_DBUS_PERSISTENT_GROUP = "fi.w1.wpa_supplicant1.PersistentGroup"
+
+def prepare_dbus(dev):
+    if not dbus_imported:
+        logger.info("No dbus module available")
+        raise HwsimSkip("No dbus module available")
+    try:
+        from dbus.mainloop.glib import DBusGMainLoop
+        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+        bus = dbus.SystemBus()
+        wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_PATH)
+        wpas = dbus.Interface(wpas_obj, WPAS_DBUS_SERVICE)
+        path = wpas.GetInterface(dev.ifname)
+        if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+        return (bus,wpas_obj,path,if_obj)
+    except Exception, e:
+        raise HwsimSkip("Could not connect to D-Bus: %s" % e)
+
+class TestDbus(object):
+    def __init__(self, bus):
+        self.loop = gobject.MainLoop()
+        self.signals = []
+        self.bus = bus
+
+    def __exit__(self, type, value, traceback):
+        for s in self.signals:
+            s.remove()
+
+    def add_signal(self, handler, interface, name, byte_arrays=False):
+        s = self.bus.add_signal_receiver(handler, dbus_interface=interface,
+                                         signal_name=name,
+                                         byte_arrays=byte_arrays)
+        self.signals.append(s)
+
+    def timeout(self, *args):
+        logger.debug("timeout")
+        self.loop.quit()
+        return False
+
+class alloc_fail_dbus(object):
+    def __init__(self, dev, count, funcs, operation="Operation",
+                 expected="NoMemory"):
+        self._dev = dev
+        self._count = count
+        self._funcs = funcs
+        self._operation = operation
+        self._expected = expected
+    def __enter__(self):
+        cmd = "TEST_ALLOC_FAIL %d:%s" % (self._count, self._funcs)
+        if "OK" not in self._dev.request(cmd):
+            raise HwsimSkip("TEST_ALLOC_FAIL not supported")
+    def __exit__(self, type, value, traceback):
+        if type is None:
+            raise Exception("%s succeeded during out-of-memory" % self._operation)
+        if type == dbus.exceptions.DBusException and self._expected in str(value):
+            return True
+        if self._dev.request("GET_ALLOC_FAIL") != "0:%s" % self._funcs:
+            raise Exception("%s did not trigger allocation failure" % self._operation)
+        return False
+
+def start_ap(ap, ssid="test-wps",
+             ap_uuid="27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"):
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+               "ap_pin": "12345670", "uuid": ap_uuid}
+    return hostapd.add_ap(ap['ifname'], params)
+
+def test_dbus_getall(dev, apdev):
+    """D-Bus GetAll"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    props = wpas_obj.GetAll(WPAS_DBUS_SERVICE,
+                            dbus_interface=dbus.PROPERTIES_IFACE)
+    logger.debug("GetAll(fi.w1.wpa.supplicant1, /fi/w1/wpa_supplicant1) ==> " + str(props))
+
+    props = if_obj.GetAll(WPAS_DBUS_IFACE,
+                          dbus_interface=dbus.PROPERTIES_IFACE)
+    logger.debug("GetAll(%s, %s): %s" % (WPAS_DBUS_IFACE, path, str(props)))
+
+    props = if_obj.GetAll(WPAS_DBUS_IFACE_WPS,
+                          dbus_interface=dbus.PROPERTIES_IFACE)
+    logger.debug("GetAll(%s, %s): %s" % (WPAS_DBUS_IFACE_WPS, path, str(props)))
+
+    res = if_obj.Get(WPAS_DBUS_IFACE, 'BSSs',
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if len(res) != 0:
+        raise Exception("Unexpected BSSs entry: " + str(res))
+
+    res = if_obj.Get(WPAS_DBUS_IFACE, 'Networks',
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if len(res) != 0:
+        raise Exception("Unexpected Networks entry: " + str(res))
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    bssid = apdev[0]['bssid']
+    dev[0].scan_for_bss(bssid, freq=2412)
+    id = dev[0].add_network()
+    dev[0].set_network(id, "disabled", "0")
+    dev[0].set_network_quoted(id, "ssid", "test")
+
+    res = if_obj.Get(WPAS_DBUS_IFACE, 'BSSs',
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if len(res) != 1:
+        raise Exception("Missing BSSs entry: " + str(res))
+    bss_obj = bus.get_object(WPAS_DBUS_SERVICE, res[0])
+    props = bss_obj.GetAll(WPAS_DBUS_BSS, dbus_interface=dbus.PROPERTIES_IFACE)
+    logger.debug("GetAll(%s, %s): %s" % (WPAS_DBUS_BSS, res[0], str(props)))
+    bssid_str = ''
+    for item in props['BSSID']:
+        if len(bssid_str) > 0:
+            bssid_str += ':'
+        bssid_str += '%02x' % item
+    if bssid_str != bssid:
+        raise Exception("Unexpected BSSID in BSSs entry")
+
+    res = if_obj.Get(WPAS_DBUS_IFACE, 'Networks',
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if len(res) != 1:
+        raise Exception("Missing Networks entry: " + str(res))
+    net_obj = bus.get_object(WPAS_DBUS_SERVICE, res[0])
+    props = net_obj.GetAll(WPAS_DBUS_NETWORK,
+                           dbus_interface=dbus.PROPERTIES_IFACE)
+    logger.debug("GetAll(%s, %s): %s" % (WPAS_DBUS_NETWORK, res[0], str(props)))
+    ssid = props['Properties']['ssid']
+    if ssid != '"test"':
+        raise Exception("Unexpected SSID in network entry")
+
+def dbus_get(dbus, wpas_obj, prop, expect=None, byte_arrays=False):
+    val = wpas_obj.Get(WPAS_DBUS_SERVICE, prop,
+                       dbus_interface=dbus.PROPERTIES_IFACE,
+                       byte_arrays=byte_arrays)
+    if expect is not None and val != expect:
+        raise Exception("Unexpected %s: %s (expected: %s)" %
+                        (prop, str(val), str(expect)))
+    return val
+
+def dbus_set(dbus, wpas_obj, prop, val):
+    wpas_obj.Set(WPAS_DBUS_SERVICE, prop, val,
+                 dbus_interface=dbus.PROPERTIES_IFACE)
+
+def test_dbus_properties(dev, apdev):
+    """D-Bus Get/Set fi.w1.wpa_supplicant1 properties"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    dbus_get(dbus, wpas_obj, "DebugLevel", expect="msgdump")
+    dbus_set(dbus, wpas_obj, "DebugLevel", "debug")
+    dbus_get(dbus, wpas_obj, "DebugLevel", expect="debug")
+    for (val,err) in [ (3, "Error.Failed: wrong property type"),
+                       ("foo", "Error.Failed: wrong debug level value") ]:
+        try:
+            dbus_set(dbus, wpas_obj, "DebugLevel", val)
+            raise Exception("Invalid DebugLevel value accepted: " + str(val))
+        except dbus.exceptions.DBusException, e:
+            if err not in str(e):
+                raise Exception("Unexpected error message: " + str(e))
+    dbus_set(dbus, wpas_obj, "DebugLevel", "msgdump")
+    dbus_get(dbus, wpas_obj, "DebugLevel", expect="msgdump")
+
+    dbus_get(dbus, wpas_obj, "DebugTimestamp", expect=True)
+    dbus_set(dbus, wpas_obj, "DebugTimestamp", False)
+    dbus_get(dbus, wpas_obj, "DebugTimestamp", expect=False)
+    try:
+        dbus_set(dbus, wpas_obj, "DebugTimestamp", "foo")
+        raise Exception("Invalid DebugTimestamp value accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: wrong property type" not in str(e):
+            raise Exception("Unexpected error message: " + str(e))
+    dbus_set(dbus, wpas_obj, "DebugTimestamp", True)
+    dbus_get(dbus, wpas_obj, "DebugTimestamp", expect=True)
+
+    dbus_get(dbus, wpas_obj, "DebugShowKeys", expect=True)
+    dbus_set(dbus, wpas_obj, "DebugShowKeys", False)
+    dbus_get(dbus, wpas_obj, "DebugShowKeys", expect=False)
+    try:
+        dbus_set(dbus, wpas_obj, "DebugShowKeys", "foo")
+        raise Exception("Invalid DebugShowKeys value accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: wrong property type" not in str(e):
+            raise Exception("Unexpected error message: " + str(e))
+    dbus_set(dbus, wpas_obj, "DebugShowKeys", True)
+    dbus_get(dbus, wpas_obj, "DebugShowKeys", expect=True)
+
+    res = dbus_get(dbus, wpas_obj, "Interfaces")
+    if len(res) != 1:
+        raise Exception("Unexpected Interfaces value: " + str(res))
+
+    res = dbus_get(dbus, wpas_obj, "EapMethods")
+    if len(res) < 5 or "TTLS" not in res:
+        raise Exception("Unexpected EapMethods value: " + str(res))
+
+    res = dbus_get(dbus, wpas_obj, "Capabilities")
+    if len(res) < 2 or "p2p" not in res:
+        raise Exception("Unexpected Capabilities value: " + str(res))
+
+    dbus_get(dbus, wpas_obj, "WFDIEs", byte_arrays=True)
+    val = binascii.unhexlify("010006020304050608")
+    dbus_set(dbus, wpas_obj, "WFDIEs", dbus.ByteArray(val))
+    res = dbus_get(dbus, wpas_obj, "WFDIEs", byte_arrays=True)
+    if val != res:
+        raise Exception("WFDIEs value changed")
+    try:
+        dbus_set(dbus, wpas_obj, "WFDIEs", dbus.ByteArray('\x00'))
+        raise Exception("Invalid WFDIEs value accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message: " + str(e))
+    dbus_set(dbus, wpas_obj, "WFDIEs", dbus.ByteArray(''))
+    dbus_set(dbus, wpas_obj, "WFDIEs", dbus.ByteArray(val))
+    dbus_set(dbus, wpas_obj, "WFDIEs", dbus.ByteArray(''))
+    res = dbus_get(dbus, wpas_obj, "WFDIEs", byte_arrays=True)
+    if len(res) != 0:
+        raise Exception("WFDIEs not cleared properly")
+
+    res = dbus_get(dbus, wpas_obj, "EapMethods")
+    try:
+        dbus_set(dbus, wpas_obj, "EapMethods", res)
+        raise Exception("Invalid Set accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs: Property is read-only" not in str(e):
+            raise Exception("Unexpected error message: " + str(e))
+
+    try:
+        wpas_obj.SetFoo(WPAS_DBUS_SERVICE, "DebugShowKeys", True,
+                        dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Unknown method accepted")
+    except dbus.exceptions.DBusException, e:
+        if "UnknownMethod" not in str(e):
+            raise Exception("Unexpected error message: " + str(e))
+
+    try:
+        wpas_obj.Get("foo", "DebugShowKeys",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Get accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs: No such property" not in str(e):
+            raise Exception("Unexpected error message: " + str(e))
+
+    test_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_PATH,
+                              introspect=False)
+    try:
+        test_obj.Get(123, "DebugShowKeys",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Get accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs: Invalid arguments" not in str(e):
+            raise Exception("Unexpected error message: " + str(e))
+    try:
+        test_obj.Get(WPAS_DBUS_SERVICE, 123,
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Get accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs: Invalid arguments" not in str(e):
+            raise Exception("Unexpected error message: " + str(e))
+
+    try:
+        wpas_obj.Set(WPAS_DBUS_SERVICE, "WFDIEs",
+                     dbus.ByteArray('', variant_level=2),
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Set accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs: invalid message format" not in str(e):
+            raise Exception("Unexpected error message: " + str(e))
+
+def test_dbus_invalid_method(dev, apdev):
+    """D-Bus invalid method"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+    try:
+        wps.Foo()
+        raise Exception("Unknown method accepted")
+    except dbus.exceptions.DBusException, e:
+        if "UnknownMethod" not in str(e):
+            raise Exception("Unexpected error message: " + str(e))
+
+    test_obj = bus.get_object(WPAS_DBUS_SERVICE, path, introspect=False)
+    test_wps = dbus.Interface(test_obj, WPAS_DBUS_IFACE_WPS)
+    try:
+        test_wps.Start(123)
+        raise Exception("WPS.Start with incorrect signature accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs: Invalid arg" not in str(e):
+            raise Exception("Unexpected error message: " + str(e))
+
+def test_dbus_get_set_wps(dev, apdev):
+    """D-Bus Get/Set for WPS properties"""
+    try:
+        _test_dbus_get_set_wps(dev, apdev)
+    finally:
+        dev[0].request("SET wps_cred_processing 0")
+        dev[0].request("SET config_methods display keypad virtual_display nfc_interface p2ps")
+
+def _test_dbus_get_set_wps(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    if_obj.Get(WPAS_DBUS_IFACE_WPS, "ConfigMethods",
+               dbus_interface=dbus.PROPERTIES_IFACE)
+
+    val = "display keypad virtual_display nfc_interface"
+    dev[0].request("SET config_methods " + val)
+
+    config = if_obj.Get(WPAS_DBUS_IFACE_WPS, "ConfigMethods",
+                        dbus_interface=dbus.PROPERTIES_IFACE)
+    if config != val:
+        raise Exception("Unexpected Get(ConfigMethods) result: " + config)
+
+    val2 = "push_button display"
+    if_obj.Set(WPAS_DBUS_IFACE_WPS, "ConfigMethods", val2,
+               dbus_interface=dbus.PROPERTIES_IFACE)
+    config = if_obj.Get(WPAS_DBUS_IFACE_WPS, "ConfigMethods",
+                        dbus_interface=dbus.PROPERTIES_IFACE)
+    if config != val2:
+        raise Exception("Unexpected Get(ConfigMethods) result after Set: " + config)
+
+    dev[0].request("SET config_methods " + val)
+
+    for i in range(3):
+        dev[0].request("SET wps_cred_processing " + str(i))
+        val = if_obj.Get(WPAS_DBUS_IFACE_WPS, "ProcessCredentials",
+                         dbus_interface=dbus.PROPERTIES_IFACE)
+        expected_val = False if i == 1 else True
+        if val != expected_val:
+            raise Exception("Unexpected Get(ProcessCredentials) result({}): {}".format(i, val))
+
+    class TestDbusGetSet(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.signal_received = False
+            self.signal_received_deprecated = False
+            self.sets_done = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_sets)
+            gobject.timeout_add(1000, self.timeout)
+            self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE_WPS,
+                            "PropertiesChanged")
+            self.add_signal(self.propertiesChanged2, dbus.PROPERTIES_IFACE,
+                            "PropertiesChanged")
+            self.loop.run()
+            return self
+
+        def propertiesChanged(self, properties):
+            logger.debug("PropertiesChanged: " + str(properties))
+            if properties.has_key("ProcessCredentials"):
+                self.signal_received_deprecated = True
+                if self.sets_done and self.signal_received:
+                    self.loop.quit()
+
+        def propertiesChanged2(self, interface_name, changed_properties,
+                               invalidated_properties):
+            logger.debug("propertiesChanged2: interface_name=%s changed_properties=%s invalidated_properties=%s" % (interface_name, str(changed_properties), str(invalidated_properties)))
+            if interface_name != WPAS_DBUS_IFACE_WPS:
+                return
+            if changed_properties.has_key("ProcessCredentials"):
+                self.signal_received = True
+                if self.sets_done and self.signal_received_deprecated:
+                    self.loop.quit()
+
+        def run_sets(self, *args):
+            logger.debug("run_sets")
+            if_obj.Set(WPAS_DBUS_IFACE_WPS, "ProcessCredentials",
+                       dbus.Boolean(1),
+                       dbus_interface=dbus.PROPERTIES_IFACE)
+            if if_obj.Get(WPAS_DBUS_IFACE_WPS, "ProcessCredentials",
+                          dbus_interface=dbus.PROPERTIES_IFACE) != True:
+                raise Exception("Unexpected Get(ProcessCredentials) result after Set");
+            if_obj.Set(WPAS_DBUS_IFACE_WPS, "ProcessCredentials",
+                       dbus.Boolean(0),
+                       dbus_interface=dbus.PROPERTIES_IFACE)
+            if if_obj.Get(WPAS_DBUS_IFACE_WPS, "ProcessCredentials",
+                          dbus_interface=dbus.PROPERTIES_IFACE) != False:
+                raise Exception("Unexpected Get(ProcessCredentials) result after Set");
+
+            self.dbus_sets_done = True
+            return False
+
+        def success(self):
+            return self.signal_received and self.signal_received_deprecated
+
+    with TestDbusGetSet(bus) as t:
+        if not t.success():
+            raise Exception("No signal received for ProcessCredentials change")
+
+def test_dbus_wps_invalid(dev, apdev):
+    """D-Bus invaldi WPS operation"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+    failures = [ {'Role': 'foo', 'Type': 'pbc'},
+                 {'Role': 123, 'Type': 'pbc'},
+                 {'Type': 'pbc'},
+                 {'Role': 'enrollee'},
+                 {'Role': 'registrar'},
+                 {'Role': 'enrollee', 'Type': 123},
+                 {'Role': 'enrollee', 'Type': 'foo'},
+                 {'Role': 'enrollee', 'Type': 'pbc',
+                  'Bssid': '02:33:44:55:66:77'},
+                 {'Role': 'enrollee', 'Type': 'pin', 'Pin': 123},
+                 {'Role': 'enrollee', 'Type': 'pbc',
+                  'Bssid': dbus.ByteArray('12345')},
+                 {'Role': 'enrollee', 'Type': 'pbc',
+                  'P2PDeviceAddress': 12345},
+                 {'Role': 'enrollee', 'Type': 'pbc',
+                  'P2PDeviceAddress': dbus.ByteArray('12345')},
+                 {'Role': 'enrollee', 'Type': 'pbc', 'Foo': 'bar'} ]
+    for args in failures:
+        try:
+            wps.Start(args)
+            raise Exception("Invalid WPS.Start() arguments accepted: " + str(args))
+        except dbus.exceptions.DBusException, e:
+            if not str(e).startswith("fi.w1.wpa_supplicant1.InvalidArgs"):
+                raise Exception("Unexpected error message: " + str(e))
+
+def test_dbus_wps_oom(dev, apdev):
+    """D-Bus WPS operation (OOM)"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+    with alloc_fail_dbus(dev[0], 1, "=wpas_dbus_getter_state", "Get"):
+        if_obj.Get(WPAS_DBUS_IFACE, "State",
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    bssid = apdev[0]['bssid']
+    dev[0].scan_for_bss(bssid, freq=2412)
+
+    for i in range(1, 3):
+        with alloc_fail_dbus(dev[0], i, "=wpas_dbus_getter_bsss", "Get"):
+            if_obj.Get(WPAS_DBUS_IFACE, "BSSs",
+                       dbus_interface=dbus.PROPERTIES_IFACE)
+
+    res = if_obj.Get(WPAS_DBUS_IFACE, 'BSSs',
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    bss_obj = bus.get_object(WPAS_DBUS_SERVICE, res[0])
+    with alloc_fail_dbus(dev[0], 1, "=wpas_dbus_getter_bss_rates", "Get"):
+        bss_obj.Get(WPAS_DBUS_BSS, "Rates",
+                    dbus_interface=dbus.PROPERTIES_IFACE)
+
+    id = dev[0].add_network()
+    dev[0].set_network(id, "disabled", "0")
+    dev[0].set_network_quoted(id, "ssid", "test")
+
+    for i in range(1, 3):
+        with alloc_fail_dbus(dev[0], i, "=wpas_dbus_getter_networks", "Get"):
+            if_obj.Get(WPAS_DBUS_IFACE, "Networks",
+                       dbus_interface=dbus.PROPERTIES_IFACE)
+
+    with alloc_fail_dbus(dev[0], 1, "wpas_dbus_getter_interfaces", "Get"):
+        dbus_get(dbus, wpas_obj, "Interfaces")
+
+    for i in range(1, 6):
+        with alloc_fail_dbus(dev[0], i, "=eap_get_names_as_string_array;wpas_dbus_getter_eap_methods", "Get"):
+            dbus_get(dbus, wpas_obj, "EapMethods")
+
+    with alloc_fail_dbus(dev[0], 1, "wpas_dbus_setter_config_methods", "Set",
+                         expected="Error.Failed: Failed to set property"):
+        val2 = "push_button display"
+        if_obj.Set(WPAS_DBUS_IFACE_WPS, "ConfigMethods", val2,
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+
+    with alloc_fail_dbus(dev[0], 1, "=wpa_config_add_network;wpas_dbus_handler_wps_start",
+                         "WPS.Start",
+                         expected="UnknownError: WPS start failed"):
+        wps.Start({'Role': 'enrollee', 'Type': 'pin', 'Pin': '12345670'})
+
+def test_dbus_wps_pbc(dev, apdev):
+    """D-Bus WPS/PBC operation and signals"""
+    try:
+        _test_dbus_wps_pbc(dev, apdev)
+    finally:
+        dev[0].request("SET wps_cred_processing 0")
+
+def _test_dbus_wps_pbc(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+    hapd = start_ap(apdev[0])
+    hapd.request("WPS_PBC")
+    bssid = apdev[0]['bssid']
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].request("SET wps_cred_processing 2")
+
+    res = if_obj.Get(WPAS_DBUS_IFACE, 'BSSs',
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if len(res) != 1:
+        raise Exception("Missing BSSs entry: " + str(res))
+    bss_obj = bus.get_object(WPAS_DBUS_SERVICE, res[0])
+    props = bss_obj.GetAll(WPAS_DBUS_BSS, dbus_interface=dbus.PROPERTIES_IFACE)
+    logger.debug("GetAll(%s, %s): %s" % (WPAS_DBUS_BSS, res[0], str(props)))
+    if 'WPS' not in props:
+        raise Exception("No WPS information in the BSS entry")
+    if 'Type' not in props['WPS']:
+        raise Exception("No Type field in the WPS dictionary")
+    if props['WPS']['Type'] != 'pbc':
+        raise Exception("Unexpected WPS Type: " + props['WPS']['Type'])
+
+    class TestDbusWps(TestDbus):
+        def __init__(self, bus, wps):
+            TestDbus.__init__(self, bus)
+            self.success_seen = False
+            self.credentials_received = False
+            self.wps = wps
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.start_pbc)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.wpsEvent, WPAS_DBUS_IFACE_WPS, "Event")
+            self.add_signal(self.credentials, WPAS_DBUS_IFACE_WPS,
+                            "Credentials")
+            self.loop.run()
+            return self
+
+        def wpsEvent(self, name, args):
+            logger.debug("wpsEvent: %s args='%s'" % (name, str(args)))
+            if name == "success":
+                self.success_seen = True
+                if self.credentials_received:
+                    self.loop.quit()
+
+        def credentials(self, args):
+            logger.debug("credentials: " + str(args))
+            self.credentials_received = True
+            if self.success_seen:
+                self.loop.quit()
+
+        def start_pbc(self, *args):
+            logger.debug("start_pbc")
+            self.wps.Start({'Role': 'enrollee', 'Type': 'pbc'})
+            return False
+
+        def success(self):
+            return self.success_seen and self.credentials_received
+
+    with TestDbusWps(bus, wps) as t:
+        if not t.success():
+            raise Exception("Failure in D-Bus operations")
+
+    dev[0].wait_connected(timeout=10)
+    dev[0].request("DISCONNECT")
+    hapd.disable()
+    dev[0].flush_scan_cache()
+
+def test_dbus_wps_pbc_overlap(dev, apdev):
+    """D-Bus WPS/PBC operation and signal for PBC overlap"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+    hapd = start_ap(apdev[0])
+    hapd2 = start_ap(apdev[1], ssid="test-wps2",
+                     ap_uuid="27ea801a-9e5c-4e73-bd82-f89cbcd10d7f")
+    hapd.request("WPS_PBC")
+    hapd2.request("WPS_PBC")
+    bssid = apdev[0]['bssid']
+    dev[0].scan_for_bss(bssid, freq="2412")
+    bssid2 = apdev[1]['bssid']
+    dev[0].scan_for_bss(bssid2, freq="2412")
+
+    class TestDbusWps(TestDbus):
+        def __init__(self, bus, wps):
+            TestDbus.__init__(self, bus)
+            self.overlap_seen = False
+            self.wps = wps
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.start_pbc)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.wpsEvent, WPAS_DBUS_IFACE_WPS, "Event")
+            self.loop.run()
+            return self
+
+        def wpsEvent(self, name, args):
+            logger.debug("wpsEvent: %s args='%s'" % (name, str(args)))
+            if name == "pbc-overlap":
+                self.overlap_seen = True
+                self.loop.quit()
+
+        def start_pbc(self, *args):
+            logger.debug("start_pbc")
+            self.wps.Start({'Role': 'enrollee', 'Type': 'pbc'})
+            return False
+
+        def success(self):
+            return self.overlap_seen
+
+    with TestDbusWps(bus, wps) as t:
+        if not t.success():
+            raise Exception("Failure in D-Bus operations")
+
+    dev[0].request("WPS_CANCEL")
+    dev[0].request("DISCONNECT")
+    hapd.disable()
+    dev[0].flush_scan_cache()
+
+def test_dbus_wps_pin(dev, apdev):
+    """D-Bus WPS/PIN operation and signals"""
+    try:
+        _test_dbus_wps_pin(dev, apdev)
+    finally:
+        dev[0].request("SET wps_cred_processing 0")
+
+def _test_dbus_wps_pin(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+    hapd = start_ap(apdev[0])
+    hapd.request("WPS_PIN any 12345670")
+    bssid = apdev[0]['bssid']
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].request("SET wps_cred_processing 2")
+
+    class TestDbusWps(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.success_seen = False
+            self.credentials_received = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.start_pin)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.wpsEvent, WPAS_DBUS_IFACE_WPS, "Event")
+            self.add_signal(self.credentials, WPAS_DBUS_IFACE_WPS,
+                            "Credentials")
+            self.loop.run()
+            return self
+
+        def wpsEvent(self, name, args):
+            logger.debug("wpsEvent: %s args='%s'" % (name, str(args)))
+            if name == "success":
+                self.success_seen = True
+                if self.credentials_received:
+                    self.loop.quit()
+
+        def credentials(self, args):
+            logger.debug("credentials: " + str(args))
+            self.credentials_received = True
+            if self.success_seen:
+                self.loop.quit()
+
+        def start_pin(self, *args):
+            logger.debug("start_pin")
+            bssid_ay = dbus.ByteArray(bssid.replace(':','').decode('hex'))
+            wps.Start({'Role': 'enrollee', 'Type': 'pin', 'Pin': '12345670',
+                       'Bssid': bssid_ay})
+            return False
+
+        def success(self):
+            return self.success_seen and self.credentials_received
+
+    with TestDbusWps(bus) as t:
+        if not t.success():
+            raise Exception("Failure in D-Bus operations")
+
+    dev[0].wait_connected(timeout=10)
+
+def test_dbus_wps_pin2(dev, apdev):
+    """D-Bus WPS/PIN operation and signals (PIN from wpa_supplicant)"""
+    try:
+        _test_dbus_wps_pin2(dev, apdev)
+    finally:
+        dev[0].request("SET wps_cred_processing 0")
+
+def _test_dbus_wps_pin2(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].request("SET wps_cred_processing 2")
+
+    class TestDbusWps(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.success_seen = False
+            self.failed = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.start_pin)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.wpsEvent, WPAS_DBUS_IFACE_WPS, "Event")
+            self.add_signal(self.credentials, WPAS_DBUS_IFACE_WPS,
+                            "Credentials")
+            self.loop.run()
+            return self
+
+        def wpsEvent(self, name, args):
+            logger.debug("wpsEvent: %s args='%s'" % (name, str(args)))
+            if name == "success":
+                self.success_seen = True
+                if self.credentials_received:
+                    self.loop.quit()
+
+        def credentials(self, args):
+            logger.debug("credentials: " + str(args))
+            self.credentials_received = True
+            if self.success_seen:
+                self.loop.quit()
+
+        def start_pin(self, *args):
+            logger.debug("start_pin")
+            bssid_ay = dbus.ByteArray(bssid.replace(':','').decode('hex'))
+            res = wps.Start({'Role': 'enrollee', 'Type': 'pin',
+                             'Bssid': bssid_ay})
+            pin = res['Pin']
+            h = hostapd.Hostapd(apdev[0]['ifname'])
+            h.request("WPS_PIN any " + pin)
+            return False
+
+        def success(self):
+            return self.success_seen and self.credentials_received
+
+    with TestDbusWps(bus) as t:
+        if not t.success():
+            raise Exception("Failure in D-Bus operations")
+
+    dev[0].wait_connected(timeout=10)
+
+def test_dbus_wps_pin_m2d(dev, apdev):
+    """D-Bus WPS/PIN operation and signals with M2D"""
+    try:
+        _test_dbus_wps_pin_m2d(dev, apdev)
+    finally:
+        dev[0].request("SET wps_cred_processing 0")
+
+def _test_dbus_wps_pin_m2d(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].request("SET wps_cred_processing 2")
+
+    class TestDbusWps(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.success_seen = False
+            self.credentials_received = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.start_pin)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.wpsEvent, WPAS_DBUS_IFACE_WPS, "Event")
+            self.add_signal(self.credentials, WPAS_DBUS_IFACE_WPS,
+                            "Credentials")
+            self.loop.run()
+            return self
+
+        def wpsEvent(self, name, args):
+            logger.debug("wpsEvent: %s args='%s'" % (name, str(args)))
+            if name == "success":
+                self.success_seen = True
+                if self.credentials_received:
+                    self.loop.quit()
+            elif name == "m2d":
+                h = hostapd.Hostapd(apdev[0]['ifname'])
+                h.request("WPS_PIN any 12345670")
+
+        def credentials(self, args):
+            logger.debug("credentials: " + str(args))
+            self.credentials_received = True
+            if self.success_seen:
+                self.loop.quit()
+
+        def start_pin(self, *args):
+            logger.debug("start_pin")
+            bssid_ay = dbus.ByteArray(bssid.replace(':','').decode('hex'))
+            wps.Start({'Role': 'enrollee', 'Type': 'pin', 'Pin': '12345670',
+                       'Bssid': bssid_ay})
+            return False
+
+        def success(self):
+            return self.success_seen and self.credentials_received
+
+    with TestDbusWps(bus) as t:
+        if not t.success():
+            raise Exception("Failure in D-Bus operations")
+
+    dev[0].wait_connected(timeout=10)
+
+def test_dbus_wps_reg(dev, apdev):
+    """D-Bus WPS/Registrar operation and signals"""
+    try:
+        _test_dbus_wps_reg(dev, apdev)
+    finally:
+        dev[0].request("SET wps_cred_processing 0")
+
+def _test_dbus_wps_reg(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+    hapd = start_ap(apdev[0])
+    hapd.request("WPS_PIN any 12345670")
+    bssid = apdev[0]['bssid']
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].request("SET wps_cred_processing 2")
+
+    class TestDbusWps(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.credentials_received = False
+
+        def __enter__(self):
+            gobject.timeout_add(100, self.start_reg)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.wpsEvent, WPAS_DBUS_IFACE_WPS, "Event")
+            self.add_signal(self.credentials, WPAS_DBUS_IFACE_WPS,
+                            "Credentials")
+            self.loop.run()
+            return self
+
+        def wpsEvent(self, name, args):
+            logger.debug("wpsEvent: %s args='%s'" % (name, str(args)))
+
+        def credentials(self, args):
+            logger.debug("credentials: " + str(args))
+            self.credentials_received = True
+            self.loop.quit()
+
+        def start_reg(self, *args):
+            logger.debug("start_reg")
+            bssid_ay = dbus.ByteArray(bssid.replace(':','').decode('hex'))
+            wps.Start({'Role': 'registrar', 'Type': 'pin',
+                       'Pin': '12345670', 'Bssid': bssid_ay})
+            return False
+
+        def success(self):
+            return self.credentials_received
+
+    with TestDbusWps(bus) as t:
+        if not t.success():
+            raise Exception("Failure in D-Bus operations")
+
+    dev[0].wait_connected(timeout=10)
+
+def test_dbus_wps_cancel(dev, apdev):
+    """D-Bus WPS Cancel operation"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+
+    wps.Cancel()
+    dev[0].scan_for_bss(bssid, freq="2412")
+    bssid_ay = dbus.ByteArray(bssid.replace(':','').decode('hex'))
+    wps.Start({'Role': 'enrollee', 'Type': 'pin', 'Pin': '12345670',
+               'Bssid': bssid_ay})
+    wps.Cancel()
+    dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 1)
+
+def test_dbus_scan_invalid(dev, apdev):
+    """D-Bus invalid scan method"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    tests = [ ({}, "InvalidArgs"),
+              ({'Type': 123}, "InvalidArgs"),
+              ({'Type': 'foo'}, "InvalidArgs"),
+              ({'Type': 'active', 'Foo': 'bar'}, "InvalidArgs"),
+              ({'Type': 'active', 'SSIDs': 'foo'}, "InvalidArgs"),
+              ({'Type': 'active', 'SSIDs': ['foo']}, "InvalidArgs"),
+              ({'Type': 'active',
+                'SSIDs': [ dbus.ByteArray("1"), dbus.ByteArray("2"),
+                           dbus.ByteArray("3"), dbus.ByteArray("4"),
+                           dbus.ByteArray("5"), dbus.ByteArray("6"),
+                           dbus.ByteArray("7"), dbus.ByteArray("8"),
+                           dbus.ByteArray("9"), dbus.ByteArray("10"),
+                           dbus.ByteArray("11"), dbus.ByteArray("12"),
+                           dbus.ByteArray("13"), dbus.ByteArray("14"),
+                           dbus.ByteArray("15"), dbus.ByteArray("16"),
+                           dbus.ByteArray("17") ]},
+               "InvalidArgs"),
+              ({'Type': 'active',
+                'SSIDs': [ dbus.ByteArray("1234567890abcdef1234567890abcdef1") ]},
+               "InvalidArgs"),
+              ({'Type': 'active', 'IEs': 'foo'}, "InvalidArgs"),
+              ({'Type': 'active', 'IEs': ['foo']}, "InvalidArgs"),
+              ({'Type': 'active', 'Channels': 2412 }, "InvalidArgs"),
+              ({'Type': 'active', 'Channels': [ 2412 ] }, "InvalidArgs"),
+              ({'Type': 'active',
+                'Channels': [ (dbus.Int32(2412), dbus.UInt32(20)) ] },
+               "InvalidArgs"),
+              ({'Type': 'active',
+                'Channels': [ (dbus.UInt32(2412), dbus.Int32(20)) ] },
+               "InvalidArgs"),
+              ({'Type': 'active', 'AllowRoam': "yes" }, "InvalidArgs"),
+              ({'Type': 'passive', 'IEs': [ dbus.ByteArray("\xdd\x00") ]},
+               "InvalidArgs"),
+              ({'Type': 'passive', 'SSIDs': [ dbus.ByteArray("foo") ]},
+               "InvalidArgs")]
+    for (t,err) in tests:
+        try:
+            iface.Scan(t)
+            raise Exception("Invalid Scan() arguments accepted: " + str(t))
+        except dbus.exceptions.DBusException, e:
+            if err not in str(e):
+                raise Exception("Unexpected error message for invalid Scan(%s): %s" % (str(t), str(e)))
+
+def test_dbus_scan_oom(dev, apdev):
+    """D-Bus scan method and OOM"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    with alloc_fail_dbus(dev[0], 1,
+                         "wpa_scan_clone_params;wpas_dbus_handler_scan",
+                         "Scan", expected="ScanError: Scan request rejected"):
+        iface.Scan({ 'Type': 'passive',
+                     'Channels': [ (dbus.UInt32(2412), dbus.UInt32(20)) ] })
+
+    with alloc_fail_dbus(dev[0], 1,
+                         "=wpas_dbus_get_scan_channels;wpas_dbus_handler_scan",
+                         "Scan"):
+        iface.Scan({ 'Type': 'passive',
+                     'Channels': [ (dbus.UInt32(2412), dbus.UInt32(20)) ] })
+
+    with alloc_fail_dbus(dev[0], 1,
+                         "=wpas_dbus_get_scan_ies;wpas_dbus_handler_scan",
+                         "Scan"):
+        iface.Scan({ 'Type': 'active',
+                     'IEs': [ dbus.ByteArray("\xdd\x00") ],
+                     'Channels': [ (dbus.UInt32(2412), dbus.UInt32(20)) ] })
+
+    with alloc_fail_dbus(dev[0], 1,
+                         "=wpas_dbus_get_scan_ssids;wpas_dbus_handler_scan",
+                         "Scan"):
+        iface.Scan({ 'Type': 'active',
+                     'SSIDs': [ dbus.ByteArray("open"),
+                                dbus.ByteArray() ],
+                     'Channels': [ (dbus.UInt32(2412), dbus.UInt32(20)) ] })
+
+def test_dbus_scan(dev, apdev):
+    """D-Bus scan and related signals"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+
+    class TestDbusScan(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.scan_completed = 0
+            self.bss_added = False
+            self.fail_reason = None
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_scan)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.scanDone, WPAS_DBUS_IFACE, "ScanDone")
+            self.add_signal(self.bssAdded, WPAS_DBUS_IFACE, "BSSAdded")
+            self.add_signal(self.bssRemoved, WPAS_DBUS_IFACE, "BSSRemoved")
+            self.loop.run()
+            return self
+
+        def scanDone(self, success):
+            logger.debug("scanDone: success=%s" % success)
+            self.scan_completed += 1
+            if self.scan_completed == 1:
+                iface.Scan({'Type': 'passive',
+                            'AllowRoam': True,
+                            'Channels': [(dbus.UInt32(2412), dbus.UInt32(20))]})
+            elif self.scan_completed == 2:
+                iface.Scan({'Type': 'passive',
+                            'AllowRoam': False})
+            elif self.bss_added and self.scan_completed == 3:
+                self.loop.quit()
+
+        def bssAdded(self, bss, properties):
+            logger.debug("bssAdded: %s" % bss)
+            logger.debug(str(properties))
+            if 'WPS' in properties:
+                if 'Type' in properties['WPS']:
+                    self.fail_reason = "Unexpected WPS dictionary entry in non-WPS BSS"
+                    self.loop.quit()
+            self.bss_added = True
+            if self.scan_completed == 3:
+                self.loop.quit()
+
+        def bssRemoved(self, bss):
+            logger.debug("bssRemoved: %s" % bss)
+
+        def run_scan(self, *args):
+            logger.debug("run_scan")
+            iface.Scan({'Type': 'active',
+                        'SSIDs': [ dbus.ByteArray("open"),
+                                   dbus.ByteArray() ],
+                        'IEs': [ dbus.ByteArray("\xdd\x00"),
+                                 dbus.ByteArray() ],
+                        'AllowRoam': False,
+                        'Channels': [(dbus.UInt32(2412), dbus.UInt32(20))]})
+            return False
+
+        def success(self):
+            return self.scan_completed == 3 and self.bss_added
+
+    with TestDbusScan(bus) as t:
+        if t.fail_reason:
+            raise Exception(t.fail_reason)
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+    res = if_obj.Get(WPAS_DBUS_IFACE, "BSSs",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if len(res) < 1:
+        raise Exception("Scan result not in BSSs property")
+    iface.FlushBSS(0)
+    res = if_obj.Get(WPAS_DBUS_IFACE, "BSSs",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if len(res) != 0:
+        raise Exception("FlushBSS() did not remove scan results from BSSs property")
+    iface.FlushBSS(1)
+
+def test_dbus_scan_busy(dev, apdev):
+    """D-Bus scan trigger rejection when busy with previous scan"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    if "OK" not in dev[0].request("SCAN freq=2412-2462"):
+        raise Exception("Failed to start scan")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], 15)
+    if ev is None:
+        raise Exception("Scan start timed out")
+
+    try:
+        iface.Scan({'Type': 'active', 'AllowRoam': False})
+        raise Exception("Scan() accepted when busy")
+    except dbus.exceptions.DBusException, e:
+        if "ScanError: Scan request reject" not in str(e):
+            raise Exception("Unexpected error message: " + str(e))
+
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+    if ev is None:
+        raise Exception("Scan timed out")
+
+def test_dbus_connect(dev, apdev):
+    """D-Bus AddNetwork and connect"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    class TestDbusConnect(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.network_added = False
+            self.network_selected = False
+            self.network_removed = False
+            self.state = 0
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_connect)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.networkAdded, WPAS_DBUS_IFACE, "NetworkAdded")
+            self.add_signal(self.networkRemoved, WPAS_DBUS_IFACE,
+                            "NetworkRemoved")
+            self.add_signal(self.networkSelected, WPAS_DBUS_IFACE,
+                            "NetworkSelected")
+            self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+                            "PropertiesChanged")
+            self.loop.run()
+            return self
+
+        def networkAdded(self, network, properties):
+            logger.debug("networkAdded: %s" % str(network))
+            logger.debug(str(properties))
+            self.network_added = True
+
+        def networkRemoved(self, network):
+            logger.debug("networkRemoved: %s" % str(network))
+            self.network_removed = True
+
+        def networkSelected(self, network):
+            logger.debug("networkSelected: %s" % str(network))
+            self.network_selected = True
+
+        def propertiesChanged(self, properties):
+            logger.debug("propertiesChanged: %s" % str(properties))
+            if 'State' in properties and properties['State'] == "completed":
+                if self.state == 0:
+                    self.state = 1
+                    iface.Disconnect()
+                elif self.state == 2:
+                    self.state = 3
+                    iface.Disconnect()
+                elif self.state == 4:
+                    self.state = 5
+                    iface.Reattach()
+                elif self.state == 5:
+                    self.state = 6
+                    iface.Disconnect()
+                elif self.state == 7:
+                    self.state = 8
+                    res = iface.SignalPoll()
+                    logger.debug("SignalPoll: " + str(res))
+                    if 'frequency' not in res or res['frequency'] != 2412:
+                        self.state = -1
+                        logger.info("Unexpected SignalPoll result")
+                    iface.RemoveNetwork(self.netw)
+            if 'State' in properties and properties['State'] == "disconnected":
+                if self.state == 1:
+                    self.state = 2
+                    iface.SelectNetwork(self.netw)
+                elif self.state == 3:
+                    self.state = 4
+                    iface.Reassociate()
+                elif self.state == 6:
+                    self.state = 7
+                    iface.Reconnect()
+                elif self.state == 8:
+                    self.state = 9
+                    self.loop.quit()
+
+        def run_connect(self, *args):
+            logger.debug("run_connect")
+            args = dbus.Dictionary({ 'ssid': ssid,
+                                     'key_mgmt': 'WPA-PSK',
+                                     'psk': passphrase,
+                                     'scan_freq': 2412 },
+                                   signature='sv')
+            self.netw = iface.AddNetwork(args)
+            iface.SelectNetwork(self.netw)
+            return False
+
+        def success(self):
+            if not self.network_added or \
+               not self.network_removed or \
+               not self.network_selected:
+                return False
+            return self.state == 9
+
+    with TestDbusConnect(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_connect_psk_mem(dev, apdev):
+    """D-Bus AddNetwork and connect with memory-only PSK"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    class TestDbusConnect(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.connected = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_connect)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+                            "PropertiesChanged")
+            self.add_signal(self.networkRequest, WPAS_DBUS_IFACE,
+                            "NetworkRequest")
+            self.loop.run()
+            return self
+
+        def propertiesChanged(self, properties):
+            logger.debug("propertiesChanged: %s" % str(properties))
+            if 'State' in properties and properties['State'] == "completed":
+                self.connected = True
+                self.loop.quit()
+
+        def networkRequest(self, path, field, txt):
+            logger.debug("networkRequest: %s %s %s" % (path, field, txt))
+            if field == "PSK_PASSPHRASE":
+                iface.NetworkReply(path, field, '"' + passphrase + '"')
+
+        def run_connect(self, *args):
+            logger.debug("run_connect")
+            args = dbus.Dictionary({ 'ssid': ssid,
+                                     'key_mgmt': 'WPA-PSK',
+                                     'mem_only_psk': 1,
+                                     'scan_freq': 2412 },
+                                   signature='sv')
+            self.netw = iface.AddNetwork(args)
+            iface.SelectNetwork(self.netw)
+            return False
+
+        def success(self):
+            return self.connected
+
+    with TestDbusConnect(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_connect_oom(dev, apdev):
+    """D-Bus AddNetwork and connect when out-of-memory"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    if "OK" not in dev[0].request("TEST_ALLOC_FAIL 0:"):
+        raise HwsimSkip("TEST_ALLOC_FAIL not supported in the build")
+
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    class TestDbusConnect(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.network_added = False
+            self.network_selected = False
+            self.network_removed = False
+            self.state = 0
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_connect)
+            gobject.timeout_add(1500, self.timeout)
+            self.add_signal(self.networkAdded, WPAS_DBUS_IFACE, "NetworkAdded")
+            self.add_signal(self.networkRemoved, WPAS_DBUS_IFACE,
+                            "NetworkRemoved")
+            self.add_signal(self.networkSelected, WPAS_DBUS_IFACE,
+                            "NetworkSelected")
+            self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+                            "PropertiesChanged")
+            self.loop.run()
+            return self
+
+        def networkAdded(self, network, properties):
+            logger.debug("networkAdded: %s" % str(network))
+            logger.debug(str(properties))
+            self.network_added = True
+
+        def networkRemoved(self, network):
+            logger.debug("networkRemoved: %s" % str(network))
+            self.network_removed = True
+
+        def networkSelected(self, network):
+            logger.debug("networkSelected: %s" % str(network))
+            self.network_selected = True
+
+        def propertiesChanged(self, properties):
+            logger.debug("propertiesChanged: %s" % str(properties))
+            if 'State' in properties and properties['State'] == "completed":
+                if self.state == 0:
+                    self.state = 1
+                    iface.Disconnect()
+                elif self.state == 2:
+                    self.state = 3
+                    iface.Disconnect()
+                elif self.state == 4:
+                    self.state = 5
+                    iface.Reattach()
+                elif self.state == 5:
+                    self.state = 6
+                    res = iface.SignalPoll()
+                    logger.debug("SignalPoll: " + str(res))
+                    if 'frequency' not in res or res['frequency'] != 2412:
+                        self.state = -1
+                        logger.info("Unexpected SignalPoll result")
+                    iface.RemoveNetwork(self.netw)
+            if 'State' in properties and properties['State'] == "disconnected":
+                if self.state == 1:
+                    self.state = 2
+                    iface.SelectNetwork(self.netw)
+                elif self.state == 3:
+                    self.state = 4
+                    iface.Reassociate()
+                elif self.state == 6:
+                    self.state = 7
+                    self.loop.quit()
+
+        def run_connect(self, *args):
+            logger.debug("run_connect")
+            args = dbus.Dictionary({ 'ssid': ssid,
+                                     'key_mgmt': 'WPA-PSK',
+                                     'psk': passphrase,
+                                     'scan_freq': 2412 },
+                                   signature='sv')
+            try:
+                self.netw = iface.AddNetwork(args)
+            except Exception, e:
+                logger.info("Exception on AddNetwork: " + str(e))
+                self.loop.quit()
+                return False
+            try:
+                iface.SelectNetwork(self.netw)
+            except Exception, e:
+                logger.info("Exception on SelectNetwork: " + str(e))
+                self.loop.quit()
+
+            return False
+
+        def success(self):
+            if not self.network_added or \
+               not self.network_removed or \
+               not self.network_selected:
+                return False
+            return self.state == 7
+
+    count = 0
+    for i in range(1, 1000):
+        for j in range(3):
+            dev[j].dump_monitor()
+        dev[0].request("TEST_ALLOC_FAIL %d:main" % i)
+        try:
+            with TestDbusConnect(bus) as t:
+                if not t.success():
+                    logger.info("Iteration %d - Expected signals not seen" % i)
+                else:
+                    logger.info("Iteration %d - success" % i)
+
+            state = dev[0].request('GET_ALLOC_FAIL')
+            logger.info("GET_ALLOC_FAIL: " + state)
+            dev[0].dump_monitor()
+            dev[0].request("TEST_ALLOC_FAIL 0:")
+            if i < 3:
+                raise Exception("Connection succeeded during out-of-memory")
+            if not state.startswith('0:'):
+                count += 1
+                if count == 5:
+                    break
+        except:
+            pass
+
+def test_dbus_while_not_connected(dev, apdev):
+    """D-Bus invalid operations while not connected"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    try:
+        iface.Disconnect()
+        raise Exception("Disconnect() accepted when not connected")
+    except dbus.exceptions.DBusException, e:
+        if "NotConnected" not in str(e):
+            raise Exception("Unexpected error message for invalid Disconnect: " + str(e))
+
+    try:
+        iface.Reattach()
+        raise Exception("Reattach() accepted when not connected")
+    except dbus.exceptions.DBusException, e:
+        if "NotConnected" not in str(e):
+            raise Exception("Unexpected error message for invalid Reattach: " + str(e))
+
+def test_dbus_connect_eap(dev, apdev):
+    """D-Bus AddNetwork and connect to EAP network"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    ssid = "ieee8021x-open"
+    params = hostapd.radius_params()
+    params["ssid"] = ssid
+    params["ieee8021x"] = "1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    class TestDbusConnect(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.certification_received = False
+            self.eap_status = False
+            self.state = 0
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_connect)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+                            "PropertiesChanged")
+            self.add_signal(self.certification, WPAS_DBUS_IFACE,
+                            "Certification", byte_arrays=True)
+            self.add_signal(self.networkRequest, WPAS_DBUS_IFACE,
+                            "NetworkRequest")
+            self.add_signal(self.eap, WPAS_DBUS_IFACE, "EAP")
+            self.loop.run()
+            return self
+
+        def propertiesChanged(self, properties):
+            logger.debug("propertiesChanged: %s" % str(properties))
+            if 'State' in properties and properties['State'] == "completed":
+                if self.state == 0:
+                    self.state = 1
+                    iface.EAPLogoff()
+                    logger.info("Set dNSName constraint")
+                    net_obj = bus.get_object(WPAS_DBUS_SERVICE, self.netw)
+                    args = dbus.Dictionary({ 'altsubject_match':
+                                             self.server_dnsname },
+                                           signature='sv')
+                    net_obj.Set(WPAS_DBUS_NETWORK, "Properties", args,
+                                dbus_interface=dbus.PROPERTIES_IFACE)
+                elif self.state == 2:
+                    self.state = 3
+                    iface.Disconnect()
+                    logger.info("Set non-matching dNSName constraint")
+                    net_obj = bus.get_object(WPAS_DBUS_SERVICE, self.netw)
+                    args = dbus.Dictionary({ 'altsubject_match':
+                                             self.server_dnsname + "FOO" },
+                                           signature='sv')
+                    net_obj.Set(WPAS_DBUS_NETWORK, "Properties", args,
+                                dbus_interface=dbus.PROPERTIES_IFACE)
+            if 'State' in properties and properties['State'] == "disconnected":
+                if self.state == 1:
+                    self.state = 2
+                    iface.EAPLogon()
+                    iface.SelectNetwork(self.netw)
+                if self.state == 3:
+                    self.state = 4
+                    iface.SelectNetwork(self.netw)
+
+        def certification(self, args):
+            logger.debug("certification: %s" % str(args))
+            self.certification_received = True
+            if args['depth'] == 0:
+                # The test server certificate is supposed to have dNSName
+                if len(args['altsubject']) < 1:
+                    raise Exception("Missing dNSName")
+                dnsname = args['altsubject'][0]
+                if not dnsname.startswith("DNS:"):
+                    raise Exception("Expected dNSName not found: " + dnsname)
+                logger.info("altsubject: " + dnsname)
+                self.server_dnsname = dnsname
+
+        def eap(self, status, parameter):
+            logger.debug("EAP: status=%s parameter=%s" % (status, parameter))
+            if status == 'completion' and parameter == 'success':
+                self.eap_status = True
+            if self.state == 4 and status == 'remote certificate verification' and parameter == 'AltSubject mismatch':
+                self.state = 5
+                self.loop.quit()
+
+        def networkRequest(self, path, field, txt):
+            logger.debug("networkRequest: %s %s %s" % (path, field, txt))
+            if field == "PASSWORD":
+                iface.NetworkReply(path, field, "password")
+
+        def run_connect(self, *args):
+            logger.debug("run_connect")
+            args = dbus.Dictionary({ 'ssid': ssid,
+                                     'key_mgmt': 'IEEE8021X',
+                                     'eapol_flags': 0,
+                                     'eap': 'TTLS',
+                                     'anonymous_identity': 'ttls',
+                                     'identity': 'pap user',
+                                     'ca_cert': 'auth_serv/ca.pem',
+                                     'phase2': 'auth=PAP',
+                                     'scan_freq': 2412 },
+                                   signature='sv')
+            self.netw = iface.AddNetwork(args)
+            iface.SelectNetwork(self.netw)
+            return False
+
+        def success(self):
+            if not self.eap_status or not self.certification_received:
+                return False
+            return self.state == 5
+
+    with TestDbusConnect(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_network(dev, apdev):
+    """D-Bus AddNetwork/RemoveNetwork parameters and error cases"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    args = dbus.Dictionary({ 'ssid': "foo",
+                             'key_mgmt': 'WPA-PSK',
+                             'psk': "12345678",
+                             'identity': dbus.ByteArray([ 1, 2 ]),
+                             'priority': dbus.Int32(0),
+                             'scan_freq': dbus.UInt32(2412) },
+                           signature='sv')
+    netw = iface.AddNetwork(args)
+    id = int(dev[0].list_networks()[0]['id'])
+    val = dev[0].get_network(id, "scan_freq")
+    if val != "2412":
+        raise Exception("Invalid scan_freq value: " + str(val))
+    iface.RemoveNetwork(netw)
+
+    args = dbus.Dictionary({ 'ssid': "foo",
+                             'key_mgmt': 'NONE',
+                             'scan_freq': "2412 2432",
+                             'freq_list': "2412 2417 2432" },
+                           signature='sv')
+    netw = iface.AddNetwork(args)
+    id = int(dev[0].list_networks()[0]['id'])
+    val = dev[0].get_network(id, "scan_freq")
+    if val != "2412 2432":
+        raise Exception("Invalid scan_freq value (2): " + str(val))
+    val = dev[0].get_network(id, "freq_list")
+    if val != "2412 2417 2432":
+        raise Exception("Invalid freq_list value: " + str(val))
+    iface.RemoveNetwork(netw)
+    try:
+        iface.RemoveNetwork(netw)
+        raise Exception("Invalid RemoveNetwork() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "NetworkUnknown" not in str(e):
+            raise Exception("Unexpected error message for invalid RemoveNetwork: " + str(e))
+    try:
+        iface.SelectNetwork(netw)
+        raise Exception("Invalid SelectNetwork() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "NetworkUnknown" not in str(e):
+            raise Exception("Unexpected error message for invalid RemoveNetwork: " + str(e))
+
+    args = dbus.Dictionary({ 'ssid': "foo1", 'key_mgmt': 'NONE',
+                             'identity': "testuser", 'scan_freq': '2412' },
+                           signature='sv')
+    netw1 = iface.AddNetwork(args)
+    args = dbus.Dictionary({ 'ssid': "foo2", 'key_mgmt': 'NONE' },
+                           signature='sv')
+    netw2 = iface.AddNetwork(args)
+    res = if_obj.Get(WPAS_DBUS_IFACE, "Networks",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if len(res) != 2:
+        raise Exception("Unexpected number of networks")
+
+    net_obj = bus.get_object(WPAS_DBUS_SERVICE, netw1)
+    res = net_obj.Get(WPAS_DBUS_NETWORK, "Enabled",
+                      dbus_interface=dbus.PROPERTIES_IFACE)
+    if res != False:
+        raise Exception("Added network was unexpectedly enabled by default")
+    net_obj.Set(WPAS_DBUS_NETWORK, "Enabled", dbus.Boolean(True),
+                dbus_interface=dbus.PROPERTIES_IFACE)
+    res = net_obj.Get(WPAS_DBUS_NETWORK, "Enabled",
+                      dbus_interface=dbus.PROPERTIES_IFACE)
+    if res != True:
+        raise Exception("Set(Enabled,True) did not seem to change property value")
+    net_obj.Set(WPAS_DBUS_NETWORK, "Enabled", dbus.Boolean(False),
+                dbus_interface=dbus.PROPERTIES_IFACE)
+    res = net_obj.Get(WPAS_DBUS_NETWORK, "Enabled",
+                      dbus_interface=dbus.PROPERTIES_IFACE)
+    if res != False:
+        raise Exception("Set(Enabled,False) did not seem to change property value")
+    try:
+        net_obj.Set(WPAS_DBUS_NETWORK, "Enabled", dbus.UInt32(1),
+                    dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Set(Enabled,1) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: wrong property type" not in str(e):
+            raise Exception("Unexpected error message for invalid Set(Enabled,1): " + str(e))
+
+    args = dbus.Dictionary({ 'ssid': "foo1new" }, signature='sv')
+    net_obj.Set(WPAS_DBUS_NETWORK, "Properties", args,
+                dbus_interface=dbus.PROPERTIES_IFACE)
+    res = net_obj.Get(WPAS_DBUS_NETWORK, "Properties",
+                      dbus_interface=dbus.PROPERTIES_IFACE)
+    if res['ssid'] != '"foo1new"':
+        raise Exception("Set(Properties) failed to update ssid")
+    if res['identity'] != '"testuser"':
+        raise Exception("Set(Properties) unexpectedly changed unrelated parameter")
+
+    iface.RemoveAllNetworks()
+    res = if_obj.Get(WPAS_DBUS_IFACE, "Networks",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if len(res) != 0:
+        raise Exception("Unexpected number of networks")
+    iface.RemoveAllNetworks()
+
+    tests = [ dbus.Dictionary({ 'psk': "1234567" }, signature='sv'),
+              dbus.Dictionary({ 'identity': dbus.ByteArray() },
+                              signature='sv'),
+              dbus.Dictionary({ 'identity': dbus.Byte(1) }, signature='sv'),
+              dbus.Dictionary({ 'identity': "" }, signature='sv') ]
+    for args in tests:
+        try:
+            iface.AddNetwork(args)
+            raise Exception("Invalid AddNetwork args accepted: " + str(args))
+        except dbus.exceptions.DBusException, e:
+            if "InvalidArgs" not in str(e):
+                raise Exception("Unexpected error message for invalid AddNetwork: " + str(e))
+
+def test_dbus_network_oom(dev, apdev):
+    """D-Bus AddNetwork/RemoveNetwork parameters and OOM error cases"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    args = dbus.Dictionary({ 'ssid': "foo1", 'key_mgmt': 'NONE',
+                             'identity': "testuser", 'scan_freq': '2412' },
+                           signature='sv')
+    netw1 = iface.AddNetwork(args)
+    net_obj = bus.get_object(WPAS_DBUS_SERVICE, netw1)
+
+    with alloc_fail_dbus(dev[0], 1,
+                         "wpa_config_get_all;wpas_dbus_getter_network_properties",
+                         "Get"):
+        net_obj.Get(WPAS_DBUS_NETWORK, "Properties",
+                    dbus_interface=dbus.PROPERTIES_IFACE)
+
+    iface.RemoveAllNetworks()
+
+    with alloc_fail_dbus(dev[0], 1,
+                         "wpas_dbus_new_decompose_object_path;wpas_dbus_handler_remove_network",
+                         "RemoveNetwork", "InvalidArgs"):
+        iface.RemoveNetwork(dbus.ObjectPath("/fi/w1/wpa_supplicant1/Interfaces/1234/Networks/1234"))
+
+    with alloc_fail(dev[0], 1, "wpa_dbus_register_object_per_iface;wpas_dbus_register_network"):
+        args = dbus.Dictionary({ 'ssid': "foo2", 'key_mgmt': 'NONE' },
+                               signature='sv')
+        try:
+            netw = iface.AddNetwork(args)
+            # Currently, AddNetwork() succeeds even if os_strdup() for path
+            # fails, so remove the network if that occurs.
+            iface.RemoveNetwork(netw)
+        except dbus.exceptions.DBusException, e:
+            pass
+
+    for i in range(1, 3):
+        with alloc_fail(dev[0], i, "=wpas_dbus_register_network"):
+            try:
+                netw = iface.AddNetwork(args)
+                # Currently, AddNetwork() succeeds even if network registration
+                # fails, so remove the network if that occurs.
+                iface.RemoveNetwork(netw)
+            except dbus.exceptions.DBusException, e:
+                pass
+
+    with alloc_fail_dbus(dev[0], 1,
+                         "=wpa_config_add_network;wpas_dbus_handler_add_network",
+                         "AddNetwork",
+                         "UnknownError: wpa_supplicant could not add a network"):
+        args = dbus.Dictionary({ 'ssid': "foo2", 'key_mgmt': 'NONE' },
+                               signature='sv')
+        netw = iface.AddNetwork(args)
+
+    tests = [ (1,
+               'wpa_dbus_dict_get_entry;set_network_properties;wpas_dbus_handler_add_network',
+               dbus.Dictionary({ 'ssid': dbus.ByteArray(' ') },
+                               signature='sv')),
+              (1, '=set_network_properties;wpas_dbus_handler_add_network',
+               dbus.Dictionary({ 'ssid': 'foo' }, signature='sv')),
+              (1, '=set_network_properties;wpas_dbus_handler_add_network',
+               dbus.Dictionary({ 'eap': 'foo' }, signature='sv')),
+              (1, '=set_network_properties;wpas_dbus_handler_add_network',
+               dbus.Dictionary({ 'priority': dbus.UInt32(1) },
+                               signature='sv')),
+              (1, '=set_network_properties;wpas_dbus_handler_add_network',
+               dbus.Dictionary({ 'priority': dbus.Int32(1) },
+                               signature='sv')),
+              (1, '=set_network_properties;wpas_dbus_handler_add_network',
+               dbus.Dictionary({ 'ssid': dbus.ByteArray(' ') },
+                               signature='sv')) ]
+    for (count,funcs,args) in tests:
+        with alloc_fail_dbus(dev[0], count, funcs, "AddNetwork", "InvalidArgs"):
+            netw = iface.AddNetwork(args)
+
+    if len(if_obj.Get(WPAS_DBUS_IFACE, 'Networks',
+                      dbus_interface=dbus.PROPERTIES_IFACE)) > 0:
+        raise Exception("Unexpected network block added")
+    if len(dev[0].list_networks()) > 0:
+        raise Exception("Unexpected network block visible")
+
+def test_dbus_interface(dev, apdev):
+    """D-Bus CreateInterface/GetInterface/RemoveInterface parameters and error cases"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wpas = dbus.Interface(wpas_obj, WPAS_DBUS_SERVICE)
+
+    params = dbus.Dictionary({ 'Ifname': 'lo', 'Driver': 'none' },
+                             signature='sv')
+    path = wpas.CreateInterface(params)
+    logger.debug("New interface path: " + str(path))
+    path2 = wpas.GetInterface("lo")
+    if path != path2:
+        raise Exception("Interface object mismatch")
+
+    params = dbus.Dictionary({ 'Ifname': 'lo',
+                               'Driver': 'none',
+                               'ConfigFile': 'foo',
+                               'BridgeIfname': 'foo', },
+                             signature='sv')
+    try:
+        wpas.CreateInterface(params)
+        raise Exception("Invalid CreateInterface() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InterfaceExists" not in str(e):
+            raise Exception("Unexpected error message for invalid CreateInterface: " + str(e))
+
+    wpas.RemoveInterface(path)
+    try:
+        wpas.RemoveInterface(path)
+        raise Exception("Invalid RemoveInterface() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InterfaceUnknown" not in str(e):
+            raise Exception("Unexpected error message for invalid RemoveInterface: " + str(e))
+
+    params = dbus.Dictionary({ 'Ifname': 'lo', 'Driver': 'none',
+                               'Foo': 123 },
+                             signature='sv')
+    try:
+        wpas.CreateInterface(params)
+        raise Exception("Invalid CreateInterface() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid CreateInterface: " + str(e))
+
+    params = dbus.Dictionary({ 'Driver': 'none' }, signature='sv')
+    try:
+        wpas.CreateInterface(params)
+        raise Exception("Invalid CreateInterface() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid CreateInterface: " + str(e))
+
+    try:
+        wpas.GetInterface("lo")
+        raise Exception("Invalid GetInterface() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InterfaceUnknown" not in str(e):
+            raise Exception("Unexpected error message for invalid RemoveInterface: " + str(e))
+
+def test_dbus_interface_oom(dev, apdev):
+    """D-Bus CreateInterface/GetInterface/RemoveInterface OOM error cases"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wpas = dbus.Interface(wpas_obj, WPAS_DBUS_SERVICE)
+
+    with alloc_fail_dbus(dev[0], 1, "wpa_dbus_dict_get_entry;wpas_dbus_handler_create_interface", "CreateInterface", "InvalidArgs"):
+        params = dbus.Dictionary({ 'Ifname': 'lo', 'Driver': 'none' },
+                                 signature='sv')
+        wpas.CreateInterface(params)
+
+    for i in range(1, 1000):
+        dev[0].request("TEST_ALLOC_FAIL %d:wpa_supplicant_add_iface;wpas_dbus_handler_create_interface" % i)
+        params = dbus.Dictionary({ 'Ifname': 'lo', 'Driver': 'none' },
+                                 signature='sv')
+        try:
+            npath = wpas.CreateInterface(params)
+            wpas.RemoveInterface(npath)
+            logger.info("CreateInterface succeeds after %d allocation failures" % i)
+            state = dev[0].request('GET_ALLOC_FAIL')
+            logger.info("GET_ALLOC_FAIL: " + state)
+            dev[0].dump_monitor()
+            dev[0].request("TEST_ALLOC_FAIL 0:")
+            if i < 5:
+                raise Exception("CreateInterface succeeded during out-of-memory")
+            if not state.startswith('0:'):
+                break
+        except dbus.exceptions.DBusException, e:
+            pass
+
+    for arg in [ 'Driver', 'Ifname', 'ConfigFile', 'BridgeIfname' ]:
+        with alloc_fail_dbus(dev[0], 1, "=wpas_dbus_handler_create_interface",
+                             "CreateInterface"):
+            params = dbus.Dictionary({ arg: 'foo' }, signature='sv')
+            wpas.CreateInterface(params)
+
+def test_dbus_blob(dev, apdev):
+    """D-Bus AddNetwork/RemoveNetwork parameters and error cases"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    blob = dbus.ByteArray("\x01\x02\x03")
+    iface.AddBlob('blob1', blob)
+    try:
+        iface.AddBlob('blob1', dbus.ByteArray("\x01\x02\x04"))
+        raise Exception("Invalid AddBlob() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "BlobExists" not in str(e):
+            raise Exception("Unexpected error message for invalid AddBlob: " + str(e))
+    res = iface.GetBlob('blob1')
+    if len(res) != len(blob):
+        raise Exception("Unexpected blob data length")
+    for i in range(len(res)):
+        if res[i] != dbus.Byte(blob[i]):
+            raise Exception("Unexpected blob data")
+    res = if_obj.Get(WPAS_DBUS_IFACE, "Blobs",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if 'blob1' not in res:
+        raise Exception("Added blob missing from Blobs property")
+    iface.RemoveBlob('blob1')
+    try:
+        iface.RemoveBlob('blob1')
+        raise Exception("Invalid RemoveBlob() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "BlobUnknown" not in str(e):
+            raise Exception("Unexpected error message for invalid RemoveBlob: " + str(e))
+    try:
+        iface.GetBlob('blob1')
+        raise Exception("Invalid GetBlob() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "BlobUnknown" not in str(e):
+            raise Exception("Unexpected error message for invalid GetBlob: " + str(e))
+
+    class TestDbusBlob(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.blob_added = False
+            self.blob_removed = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_blob)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.blobAdded, WPAS_DBUS_IFACE, "BlobAdded")
+            self.add_signal(self.blobRemoved, WPAS_DBUS_IFACE, "BlobRemoved")
+            self.loop.run()
+            return self
+
+        def blobAdded(self, blobName):
+            logger.debug("blobAdded: %s" % blobName)
+            if blobName == 'blob2':
+                self.blob_added = True
+
+        def blobRemoved(self, blobName):
+            logger.debug("blobRemoved: %s" % blobName)
+            if blobName == 'blob2':
+                self.blob_removed = True
+                self.loop.quit()
+
+        def run_blob(self, *args):
+            logger.debug("run_blob")
+            iface.AddBlob('blob2', dbus.ByteArray("\x01\x02\x04"))
+            iface.RemoveBlob('blob2')
+            return False
+
+        def success(self):
+            return self.blob_added and self.blob_removed
+
+    with TestDbusBlob(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_blob_oom(dev, apdev):
+    """D-Bus AddNetwork/RemoveNetwork OOM error cases"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    for i in range(1, 4):
+        with alloc_fail_dbus(dev[0], i, "wpas_dbus_handler_add_blob",
+                             "AddBlob"):
+            iface.AddBlob('blob_no_mem', dbus.ByteArray("\x01\x02\x03\x04"))
+
+def test_dbus_autoscan(dev, apdev):
+    """D-Bus Autoscan()"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    iface.AutoScan("foo")
+    iface.AutoScan("periodic:1")
+    iface.AutoScan("")
+    dev[0].request("AUTOSCAN ")
+
+def test_dbus_autoscan_oom(dev, apdev):
+    """D-Bus Autoscan() OOM"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    with alloc_fail_dbus(dev[0], 1, "wpas_dbus_handler_autoscan", "AutoScan"):
+        iface.AutoScan("foo")
+    dev[0].request("AUTOSCAN ")
+
+def test_dbus_tdls_invalid(dev, apdev):
+    """D-Bus invalid TDLS operations"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    connect_2sta_open(dev, hapd)
+    addr1 = dev[1].p2p_interface_addr()
+
+    try:
+        iface.TDLSDiscover("foo")
+        raise Exception("Invalid TDLSDiscover() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid TDLSDiscover: " + str(e))
+
+    try:
+        iface.TDLSStatus("foo")
+        raise Exception("Invalid TDLSStatus() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid TDLSStatus: " + str(e))
+
+    res = iface.TDLSStatus(addr1)
+    if res != "peer does not exist":
+        raise Exception("Unexpected TDLSStatus response")
+
+    try:
+        iface.TDLSSetup("foo")
+        raise Exception("Invalid TDLSSetup() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid TDLSSetup: " + str(e))
+
+    try:
+        iface.TDLSTeardown("foo")
+        raise Exception("Invalid TDLSTeardown() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid TDLSTeardown: " + str(e))
+
+    try:
+        iface.TDLSTeardown("00:11:22:33:44:55")
+        raise Exception("TDLSTeardown accepted for unknown peer")
+    except dbus.exceptions.DBusException, e:
+        if "UnknownError: error performing TDLS teardown" not in str(e):
+            raise Exception("Unexpected error message: " + str(e))
+
+def test_dbus_tdls_oom(dev, apdev):
+    """D-Bus TDLS operations during OOM"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    with alloc_fail_dbus(dev[0], 1, "wpa_tdls_add_peer", "TDLSSetup",
+                         "UnknownError: error performing TDLS setup"):
+        iface.TDLSSetup("00:11:22:33:44:55")
+
+def test_dbus_tdls(dev, apdev):
+    """D-Bus TDLS"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    connect_2sta_open(dev, hapd)
+
+    addr1 = dev[1].p2p_interface_addr()
+
+    class TestDbusTdls(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.tdls_setup = False
+            self.tdls_teardown = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_tdls)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+                            "PropertiesChanged")
+            self.loop.run()
+            return self
+
+        def propertiesChanged(self, properties):
+            logger.debug("propertiesChanged: %s" % str(properties))
+
+        def run_tdls(self, *args):
+            logger.debug("run_tdls")
+            iface.TDLSDiscover(addr1)
+            gobject.timeout_add(100, self.run_tdls2)
+            return False
+
+        def run_tdls2(self, *args):
+            logger.debug("run_tdls2")
+            iface.TDLSSetup(addr1)
+            gobject.timeout_add(500, self.run_tdls3)
+            return False
+
+        def run_tdls3(self, *args):
+            logger.debug("run_tdls3")
+            res = iface.TDLSStatus(addr1)
+            if res == "connected":
+                self.tdls_setup = True
+            else:
+                logger.info("Unexpected TDLSStatus: " + res)
+            iface.TDLSTeardown(addr1)
+            gobject.timeout_add(200, self.run_tdls4)
+            return False
+
+        def run_tdls4(self, *args):
+            logger.debug("run_tdls4")
+            res = iface.TDLSStatus(addr1)
+            if res == "peer does not exist":
+                self.tdls_teardown = True
+            else:
+                logger.info("Unexpected TDLSStatus: " + res)
+            self.loop.quit()
+            return False
+
+        def success(self):
+            return self.tdls_setup and self.tdls_teardown
+
+    with TestDbusTdls(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_pkcs11(dev, apdev):
+    """D-Bus SetPKCS11EngineAndModulePath()"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    try:
+        iface.SetPKCS11EngineAndModulePath("foo", "bar")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: Reinit of the EAPOL" not in str(e):
+            raise Exception("Unexpected error message for invalid SetPKCS11EngineAndModulePath: " + str(e))
+
+    try:
+        iface.SetPKCS11EngineAndModulePath("foo", "")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: Reinit of the EAPOL" not in str(e):
+            raise Exception("Unexpected error message for invalid SetPKCS11EngineAndModulePath: " + str(e))
+
+    iface.SetPKCS11EngineAndModulePath("", "bar")
+    res = if_obj.Get(WPAS_DBUS_IFACE, "PKCS11EnginePath",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if res != "":
+        raise Exception("Unexpected PKCS11EnginePath value: " + res)
+    res = if_obj.Get(WPAS_DBUS_IFACE, "PKCS11ModulePath",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if res != "bar":
+        raise Exception("Unexpected PKCS11ModulePath value: " + res)
+
+    iface.SetPKCS11EngineAndModulePath("", "")
+    res = if_obj.Get(WPAS_DBUS_IFACE, "PKCS11EnginePath",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if res != "":
+        raise Exception("Unexpected PKCS11EnginePath value: " + res)
+    res = if_obj.Get(WPAS_DBUS_IFACE, "PKCS11ModulePath",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if res != "":
+        raise Exception("Unexpected PKCS11ModulePath value: " + res)
+
+def test_dbus_apscan(dev, apdev):
+    """D-Bus Get/Set ApScan"""
+    try:
+        _test_dbus_apscan(dev, apdev)
+    finally:
+        dev[0].request("AP_SCAN 1")
+
+def _test_dbus_apscan(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    res = if_obj.Get(WPAS_DBUS_IFACE, "ApScan",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if res != 1:
+        raise Exception("Unexpected initial ApScan value: %d" % res)
+
+    for i in range(3):
+        if_obj.Set(WPAS_DBUS_IFACE, "ApScan", dbus.UInt32(i),
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+        res = if_obj.Get(WPAS_DBUS_IFACE, "ApScan",
+                         dbus_interface=dbus.PROPERTIES_IFACE)
+        if res != i:
+            raise Exception("Unexpected ApScan value %d (expected %d)" % (res, i))
+
+    try:
+        if_obj.Set(WPAS_DBUS_IFACE, "ApScan", dbus.Int16(-1),
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Set(ApScan,-1) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: wrong property type" not in str(e):
+            raise Exception("Unexpected error message for invalid Set(ApScan,-1): " + str(e))
+
+    try:
+        if_obj.Set(WPAS_DBUS_IFACE, "ApScan", dbus.UInt32(123),
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Set(ApScan,123) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: ap_scan must be 0, 1, or 2" not in str(e):
+            raise Exception("Unexpected error message for invalid Set(ApScan,123): " + str(e))
+
+    if_obj.Set(WPAS_DBUS_IFACE, "ApScan", dbus.UInt32(1),
+               dbus_interface=dbus.PROPERTIES_IFACE)
+
+def test_dbus_fastreauth(dev, apdev):
+    """D-Bus Get/Set FastReauth"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    res = if_obj.Get(WPAS_DBUS_IFACE, "FastReauth",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if res != True:
+        raise Exception("Unexpected initial FastReauth value: " + str(res))
+
+    for i in [ False, True ]:
+        if_obj.Set(WPAS_DBUS_IFACE, "FastReauth", dbus.Boolean(i),
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+        res = if_obj.Get(WPAS_DBUS_IFACE, "FastReauth",
+                         dbus_interface=dbus.PROPERTIES_IFACE)
+        if res != i:
+            raise Exception("Unexpected FastReauth value %d (expected %d)" % (res, i))
+
+    try:
+        if_obj.Set(WPAS_DBUS_IFACE, "FastReauth", dbus.Int16(-1),
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Set(FastReauth,-1) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: wrong property type" not in str(e):
+            raise Exception("Unexpected error message for invalid Set(ApScan,-1): " + str(e))
+
+    if_obj.Set(WPAS_DBUS_IFACE, "FastReauth", dbus.Boolean(True),
+               dbus_interface=dbus.PROPERTIES_IFACE)
+
+def test_dbus_bss_expire(dev, apdev):
+    """D-Bus Get/Set BSSExpireAge and BSSExpireCount"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireAge", dbus.UInt32(179),
+               dbus_interface=dbus.PROPERTIES_IFACE)
+    res = if_obj.Get(WPAS_DBUS_IFACE, "BSSExpireAge",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if res != 179:
+        raise Exception("Unexpected BSSExpireAge value %d (expected %d)" % (res, i))
+
+    if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireCount", dbus.UInt32(3),
+               dbus_interface=dbus.PROPERTIES_IFACE)
+    res = if_obj.Get(WPAS_DBUS_IFACE, "BSSExpireCount",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if res != 3:
+        raise Exception("Unexpected BSSExpireCount value %d (expected %d)" % (res, i))
+
+    try:
+        if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireAge", dbus.Int16(-1),
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Set(BSSExpireAge,-1) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: wrong property type" not in str(e):
+            raise Exception("Unexpected error message for invalid Set(BSSExpireAge,-1): " + str(e))
+
+    try:
+        if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireAge", dbus.UInt32(9),
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Set(BSSExpireAge,9) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: BSSExpireAge must be >= 10" not in str(e):
+            raise Exception("Unexpected error message for invalid Set(BSSExpireAge,9): " + str(e))
+
+    try:
+        if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireCount", dbus.Int16(-1),
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Set(BSSExpireCount,-1) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: wrong property type" not in str(e):
+            raise Exception("Unexpected error message for invalid Set(BSSExpireCount,-1): " + str(e))
+
+    try:
+        if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireCount", dbus.UInt32(0),
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Set(BSSExpireCount,0) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: BSSExpireCount must be > 0" not in str(e):
+            raise Exception("Unexpected error message for invalid Set(BSSExpireCount,0): " + str(e))
+
+    if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireAge", dbus.UInt32(180),
+               dbus_interface=dbus.PROPERTIES_IFACE)
+    if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireCount", dbus.UInt32(2),
+               dbus_interface=dbus.PROPERTIES_IFACE)
+
+def test_dbus_country(dev, apdev):
+    """D-Bus Get/Set Country"""
+    try:
+        _test_dbus_country(dev, apdev)
+    finally:
+        dev[0].request("SET country 00")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+
+def _test_dbus_country(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    # work around issues with possible pending regdom event from the end of
+    # the previous test case
+    time.sleep(0.2)
+    dev[0].dump_monitor()
+
+    if_obj.Set(WPAS_DBUS_IFACE, "Country", "FI",
+               dbus_interface=dbus.PROPERTIES_IFACE)
+    res = if_obj.Get(WPAS_DBUS_IFACE, "Country",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if res != "FI":
+        raise Exception("Unexpected Country value %s (expected FI)" % res)
+
+    ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"])
+    if ev is None:
+        # For now, work around separate P2P Device interface event delivery
+        ev = dev[0].wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
+        if ev is None:
+            raise Exception("regdom change event not seen")
+    if "init=USER type=COUNTRY alpha2=FI" not in ev:
+        raise Exception("Unexpected event contents: " + ev)
+
+    try:
+        if_obj.Set(WPAS_DBUS_IFACE, "Country", dbus.Int16(-1),
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Set(Country,-1) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: wrong property type" not in str(e):
+            raise Exception("Unexpected error message for invalid Set(Country,-1): " + str(e))
+
+    try:
+        if_obj.Set(WPAS_DBUS_IFACE, "Country", "F",
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Set(Country,F) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: invalid country code" not in str(e):
+            raise Exception("Unexpected error message for invalid Set(Country,F): " + str(e))
+
+    if_obj.Set(WPAS_DBUS_IFACE, "Country", "00",
+               dbus_interface=dbus.PROPERTIES_IFACE)
+
+    ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"])
+    if ev is None:
+        # For now, work around separate P2P Device interface event delivery
+        ev = dev[0].wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
+        if ev is None:
+            raise Exception("regdom change event not seen")
+    if "init=CORE type=WORLD" not in ev:
+        raise Exception("Unexpected event contents: " + ev)
+
+def test_dbus_scan_interval(dev, apdev):
+    """D-Bus Get/Set ScanInterval"""
+    try:
+        _test_dbus_scan_interval(dev, apdev)
+    finally:
+        dev[0].request("SCAN_INTERVAL 5")
+
+def _test_dbus_scan_interval(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    if_obj.Set(WPAS_DBUS_IFACE, "ScanInterval", dbus.Int32(3),
+               dbus_interface=dbus.PROPERTIES_IFACE)
+    res = if_obj.Get(WPAS_DBUS_IFACE, "ScanInterval",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if res != 3:
+        raise Exception("Unexpected ScanInterval value %d (expected %d)" % (res, i))
+
+    try:
+        if_obj.Set(WPAS_DBUS_IFACE, "ScanInterval", dbus.UInt16(100),
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Set(ScanInterval,100) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: wrong property type" not in str(e):
+            raise Exception("Unexpected error message for invalid Set(ScanInterval,100): " + str(e))
+
+    try:
+        if_obj.Set(WPAS_DBUS_IFACE, "ScanInterval", dbus.Int32(-1),
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Set(ScanInterval,-1) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: scan_interval must be >= 0" not in str(e):
+            raise Exception("Unexpected error message for invalid Set(ScanInterval,-1): " + str(e))
+
+    if_obj.Set(WPAS_DBUS_IFACE, "ScanInterval", dbus.Int32(5),
+               dbus_interface=dbus.PROPERTIES_IFACE)
+
+def test_dbus_probe_req_reporting(dev, apdev):
+    """D-Bus Probe Request reporting"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    dev[1].p2p_find(social=True)
+
+    class TestDbusProbe(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.reported = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupStarted")
+            self.add_signal(self.probeRequest, WPAS_DBUS_IFACE, "ProbeRequest",
+                            byte_arrays=True)
+            self.loop.run()
+            return self
+
+        def groupStarted(self, properties):
+            logger.debug("groupStarted: " + str(properties))
+            g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                      properties['interface_object'])
+            self.iface = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE)
+            self.iface.SubscribeProbeReq()
+            self.group_p2p = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+        def probeRequest(self, args):
+            logger.debug("probeRequest: args=%s" % str(args))
+            self.reported = True
+            self.loop.quit()
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+            params = dbus.Dictionary({ 'frequency': 2412 })
+            p2p.GroupAdd(params)
+            return False
+
+        def success(self):
+            return self.reported
+
+    with TestDbusProbe(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+        t.iface.UnsubscribeProbeReq()
+        try:
+            t.iface.UnsubscribeProbeReq()
+            raise Exception("Invalid UnsubscribeProbeReq() accepted")
+        except dbus.exceptions.DBusException, e:
+            if "NoSubscription" not in str(e):
+                raise Exception("Unexpected error message for invalid UnsubscribeProbeReq(): " + str(e))
+        t.group_p2p.Disconnect()
+
+    with TestDbusProbe(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+        # On purpose, leave ProbeReq subscription in place to test automatic
+        # cleanup.
+
+    dev[1].p2p_stop_find()
+
+def test_dbus_probe_req_reporting_oom(dev, apdev):
+    """D-Bus Probe Request reporting (OOM)"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    # Need to make sure this process has not already subscribed to avoid false
+    # failures due to the operation succeeding due to os_strdup() not even
+    # getting called.
+    try:
+        iface.UnsubscribeProbeReq()
+        was_subscribed = True
+    except dbus.exceptions.DBusException, e:
+        was_subscribed = False
+        pass
+
+    with alloc_fail_dbus(dev[0], 1, "wpas_dbus_handler_subscribe_preq",
+                         "SubscribeProbeReq"):
+        iface.SubscribeProbeReq()
+
+    if was_subscribed:
+        # On purpose, leave ProbeReq subscription in place to test automatic
+        # cleanup.
+        iface.SubscribeProbeReq()
+
+def test_dbus_p2p_invalid(dev, apdev):
+    """D-Bus invalid P2P operations"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+    try:
+        p2p.RejectPeer(path + "/Peers/00112233445566")
+        raise Exception("Invalid RejectPeer accepted")
+    except dbus.exceptions.DBusException, e:
+        if "UnknownError: Failed to call wpas_p2p_reject" not in str(e):
+            raise Exception("Unexpected error message for invalid RejectPeer(): " + str(e))
+
+    try:
+        p2p.RejectPeer("/foo")
+        raise Exception("Invalid RejectPeer accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid RejectPeer(): " + str(e))
+
+    tests = [ { },
+              { 'peer': 'foo' },
+              { 'foo': "bar" },
+              { 'iface': "abc" },
+              { 'iface': 123 } ]
+    for t in tests:
+        try:
+            p2p.RemoveClient(t)
+            raise Exception("Invalid RemoveClient accepted")
+        except dbus.exceptions.DBusException, e:
+            if "InvalidArgs" not in str(e):
+                raise Exception("Unexpected error message for invalid RemoveClient(): " + str(e))
+
+    tests = [ {'DiscoveryType': 'foo'},
+              {'RequestedDeviceTypes': 'foo'},
+              {'RequestedDeviceTypes': ['foo']},
+              {'RequestedDeviceTypes': ['1','2','3','4','5','6','7','8','9',
+                                        '10','11','12','13','14','15','16',
+                                        '17']},
+              {'RequestedDeviceTypes': dbus.Array([], signature="s")},
+              {'RequestedDeviceTypes': dbus.Array([['foo']], signature="as")},
+              {'RequestedDeviceTypes': dbus.Array([], signature="i")},
+              {'RequestedDeviceTypes': [dbus.ByteArray('12345678'),
+                                        dbus.ByteArray('1234567')]},
+              {'Foo': dbus.Int16(1)},
+              {'Foo': dbus.UInt16(1)},
+              {'Foo': dbus.Int64(1)},
+              {'Foo': dbus.UInt64(1)},
+              {'Foo': dbus.Double(1.23)},
+              {'Foo': dbus.Signature('s')},
+              {'Foo': 'bar'}]
+    for t in tests:
+        try:
+            p2p.Find(dbus.Dictionary(t))
+            raise Exception("Invalid Find accepted")
+        except dbus.exceptions.DBusException, e:
+            if "InvalidArgs" not in str(e):
+                raise Exception("Unexpected error message for invalid Find(): " + str(e))
+
+    for p in [ "/foo",
+               "/fi/w1/wpa_supplicant1/Interfaces/1234",
+               "/fi/w1/wpa_supplicant1/Interfaces/1234/Networks/1234" ]:
+        try:
+            p2p.RemovePersistentGroup(dbus.ObjectPath(p))
+            raise Exception("Invalid RemovePersistentGroup accepted")
+        except dbus.exceptions.DBusException, e:
+            if "InvalidArgs" not in str(e):
+                raise Exception("Unexpected error message for invalid RemovePersistentGroup: " + str(e))
+
+    try:
+        dev[0].request("P2P_SET disabled 1")
+        p2p.Listen(5)
+        raise Exception("Invalid Listen accepted")
+    except dbus.exceptions.DBusException, e:
+        if "UnknownError: Could not start P2P listen" not in str(e):
+            raise Exception("Unexpected error message for invalid Listen: " + str(e))
+    finally:
+        dev[0].request("P2P_SET disabled 0")
+
+    test_obj = bus.get_object(WPAS_DBUS_SERVICE, path, introspect=False)
+    test_p2p = dbus.Interface(test_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+    try:
+        test_p2p.Listen("foo")
+        raise Exception("Invalid Listen accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid Listen: " + str(e))
+
+    try:
+        dev[0].request("P2P_SET disabled 1")
+        p2p.ExtendedListen(dbus.Dictionary({}))
+        raise Exception("Invalid ExtendedListen accepted")
+    except dbus.exceptions.DBusException, e:
+        if "UnknownError: failed to initiate a p2p_ext_listen" not in str(e):
+            raise Exception("Unexpected error message for invalid ExtendedListen: " + str(e))
+    finally:
+        dev[0].request("P2P_SET disabled 0")
+
+    try:
+        dev[0].request("P2P_SET disabled 1")
+        args = { 'duration1': 30000, 'interval1': 102400,
+                 'duration2': 20000, 'interval2': 102400 }
+        p2p.PresenceRequest(args)
+        raise Exception("Invalid PresenceRequest accepted")
+    except dbus.exceptions.DBusException, e:
+        if "UnknownError: Failed to invoke presence request" not in str(e):
+            raise Exception("Unexpected error message for invalid PresenceRequest: " + str(e))
+    finally:
+        dev[0].request("P2P_SET disabled 0")
+
+    try:
+        params = dbus.Dictionary({'frequency': dbus.Int32(-1)})
+        p2p.GroupAdd(params)
+        raise Exception("Invalid GroupAdd accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid GroupAdd: " + str(e))
+
+    try:
+        params = dbus.Dictionary({'persistent_group_object':
+                                  dbus.ObjectPath(path),
+                                  'frequency': 2412})
+        p2p.GroupAdd(params)
+        raise Exception("Invalid GroupAdd accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid GroupAdd: " + str(e))
+
+    try:
+        p2p.Disconnect()
+        raise Exception("Invalid Disconnect accepted")
+    except dbus.exceptions.DBusException, e:
+        if "UnknownError: failed to disconnect" not in str(e):
+            raise Exception("Unexpected error message for invalid Disconnect: " + str(e))
+
+    try:
+        dev[0].request("P2P_SET disabled 1")
+        p2p.Flush()
+        raise Exception("Invalid Flush accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: P2P is not available for this interface" not in str(e):
+            raise Exception("Unexpected error message for invalid Flush: " + str(e))
+    finally:
+        dev[0].request("P2P_SET disabled 0")
+
+    try:
+        dev[0].request("P2P_SET disabled 1")
+        args = { 'peer': path,
+                 'join': True,
+                 'wps_method': 'pbc',
+                 'frequency': 2412 }
+        pin = p2p.Connect(args)
+        raise Exception("Invalid Connect accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: P2P is not available for this interface" not in str(e):
+            raise Exception("Unexpected error message for invalid Connect: " + str(e))
+    finally:
+        dev[0].request("P2P_SET disabled 0")
+
+    tests = [ { 'frequency': dbus.Int32(-1) },
+              { 'wps_method': 'pbc' },
+              { 'wps_method': 'foo' } ]
+    for args in tests:
+        try:
+            pin = p2p.Connect(args)
+            raise Exception("Invalid Connect accepted")
+        except dbus.exceptions.DBusException, e:
+            if "InvalidArgs" not in str(e):
+                raise Exception("Unexpected error message for invalid Connect: " + str(e))
+
+    try:
+        dev[0].request("P2P_SET disabled 1")
+        args = { 'peer': path }
+        pin = p2p.Invite(args)
+        raise Exception("Invalid Invite accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: P2P is not available for this interface" not in str(e):
+            raise Exception("Unexpected error message for invalid Invite: " + str(e))
+    finally:
+        dev[0].request("P2P_SET disabled 0")
+
+    try:
+        args = { 'foo': 'bar' }
+        pin = p2p.Invite(args)
+        raise Exception("Invalid Invite accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid Connect: " + str(e))
+
+    tests = [ (path, 'display', "InvalidArgs"),
+              (dbus.ObjectPath(path + "/Peers/00112233445566"),
+               'display',
+               "UnknownError: Failed to send provision discovery request"),
+              (dbus.ObjectPath(path + "/Peers/00112233445566"),
+               'keypad',
+               "UnknownError: Failed to send provision discovery request"),
+              (dbus.ObjectPath(path + "/Peers/00112233445566"),
+               'pbc',
+               "UnknownError: Failed to send provision discovery request"),
+              (dbus.ObjectPath(path + "/Peers/00112233445566"),
+               'pushbutton',
+               "UnknownError: Failed to send provision discovery request"),
+              (dbus.ObjectPath(path + "/Peers/00112233445566"),
+               'foo', "InvalidArgs") ]
+    for (p,method,err) in tests:
+        try:
+            p2p.ProvisionDiscoveryRequest(p, method)
+            raise Exception("Invalid ProvisionDiscoveryRequest accepted")
+        except dbus.exceptions.DBusException, e:
+            if err not in str(e):
+                raise Exception("Unexpected error message for invalid ProvisionDiscoveryRequest: " + str(e))
+
+    try:
+        dev[0].request("P2P_SET disabled 1")
+        if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "Peers",
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Get(Peers) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: P2P is not available for this interface" not in str(e):
+            raise Exception("Unexpected error message for invalid Get(Peers): " + str(e))
+    finally:
+        dev[0].request("P2P_SET disabled 0")
+
+def test_dbus_p2p_oom(dev, apdev):
+    """D-Bus P2P operations and OOM"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+    with alloc_fail_dbus(dev[0], 1, "_wpa_dbus_dict_entry_get_string_array",
+                         "Find", "InvalidArgs"):
+        p2p.Find(dbus.Dictionary({ 'Foo': [ 'bar' ] }))
+
+    with alloc_fail_dbus(dev[0], 2, "_wpa_dbus_dict_entry_get_string_array",
+                         "Find", "InvalidArgs"):
+        p2p.Find(dbus.Dictionary({ 'Foo': [ 'bar' ] }))
+
+    with alloc_fail_dbus(dev[0], 10, "_wpa_dbus_dict_entry_get_string_array",
+                         "Find", "InvalidArgs"):
+        p2p.Find(dbus.Dictionary({ 'Foo': [ '1','2','3','4','5','6','7','8','9' ] }))
+
+    with alloc_fail_dbus(dev[0], 1, ":=_wpa_dbus_dict_entry_get_binarray",
+                         "Find", "InvalidArgs"):
+        p2p.Find(dbus.Dictionary({ 'Foo': [ dbus.ByteArray('123') ] }))
+
+    with alloc_fail_dbus(dev[0], 1, "_wpa_dbus_dict_entry_get_byte_array;_wpa_dbus_dict_entry_get_binarray",
+                         "Find", "InvalidArgs"):
+        p2p.Find(dbus.Dictionary({ 'Foo': [ dbus.ByteArray('123') ] }))
+
+    with alloc_fail_dbus(dev[0], 2, "=_wpa_dbus_dict_entry_get_binarray",
+                         "Find", "InvalidArgs"):
+        p2p.Find(dbus.Dictionary({ 'Foo': [ dbus.ByteArray('123'),
+                                            dbus.ByteArray('123'),
+                                            dbus.ByteArray('123'),
+                                            dbus.ByteArray('123'),
+                                            dbus.ByteArray('123'),
+                                            dbus.ByteArray('123'),
+                                            dbus.ByteArray('123'),
+                                            dbus.ByteArray('123'),
+                                            dbus.ByteArray('123'),
+                                            dbus.ByteArray('123'),
+                                            dbus.ByteArray('123') ] }))
+
+    with alloc_fail_dbus(dev[0], 1, "wpabuf_alloc_ext_data;_wpa_dbus_dict_entry_get_binarray",
+                         "Find", "InvalidArgs"):
+        p2p.Find(dbus.Dictionary({ 'Foo': [ dbus.ByteArray('123') ] }))
+
+    with alloc_fail_dbus(dev[0], 1, "_wpa_dbus_dict_fill_value_from_variant;wpas_dbus_handler_p2p_find",
+                         "Find", "InvalidArgs"):
+        p2p.Find(dbus.Dictionary({ 'Foo': path }))
+
+    with alloc_fail_dbus(dev[0], 1, "_wpa_dbus_dict_entry_get_byte_array",
+                         "AddService", "InvalidArgs"):
+        args = { 'service_type': 'bonjour',
+                 'response': dbus.ByteArray(500*'b') }
+        p2p.AddService(args)
+
+    with alloc_fail_dbus(dev[0], 2, "_wpa_dbus_dict_entry_get_byte_array",
+                         "AddService", "InvalidArgs"):
+        p2p.AddService(args)
+
+def test_dbus_p2p_discovery(dev, apdev):
+    """D-Bus P2P discovery"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+    addr0 = dev[0].p2p_dev_addr()
+
+    dev[1].request("SET sec_device_type 1-0050F204-2")
+    dev[1].request("VENDOR_ELEM_ADD 1 dd0c0050f2041049000411223344")
+    dev[1].p2p_listen()
+    addr1 = dev[1].p2p_dev_addr()
+    a1 = binascii.unhexlify(addr1.replace(':',''))
+
+    wfd_devinfo = "00001c440028"
+    dev[2].request("SET wifi_display 1")
+    dev[2].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo)
+    wfd = binascii.unhexlify('000006' + wfd_devinfo)
+    dev[2].p2p_listen()
+    addr2 = dev[2].p2p_dev_addr()
+    a2 = binascii.unhexlify(addr2.replace(':',''))
+
+    res = if_obj.GetAll(WPAS_DBUS_IFACE_P2PDEVICE,
+                        dbus_interface=dbus.PROPERTIES_IFACE)
+    if 'Peers' not in res:
+        raise Exception("GetAll result missing Peers")
+    if len(res['Peers']) != 0:
+        raise Exception("Unexpected peer(s) in the list")
+
+    args = {'DiscoveryType': 'social',
+            'RequestedDeviceTypes': [dbus.ByteArray('12345678')],
+            'Timeout': dbus.Int32(1) }
+    p2p.Find(dbus.Dictionary(args))
+    p2p.StopFind()
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.found = False
+            self.found2 = False
+            self.lost = False
+            self.find_stopped = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceFound")
+            self.add_signal(self.deviceLost, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceLost")
+            self.add_signal(self.provisionDiscoveryResponseEnterPin,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "ProvisionDiscoveryResponseEnterPin")
+            self.add_signal(self.findStopped, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "FindStopped")
+            self.loop.run()
+            return self
+
+        def deviceFound(self, path):
+            logger.debug("deviceFound: path=%s" % path)
+            res = if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "Peers",
+                             dbus_interface=dbus.PROPERTIES_IFACE)
+            if len(res) < 1:
+                raise Exception("Unexpected number of peers")
+            if path not in res:
+                raise Exception("Mismatch in peer object path")
+            peer_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+            res = peer_obj.GetAll(WPAS_DBUS_P2P_PEER,
+                                  dbus_interface=dbus.PROPERTIES_IFACE,
+                                  byte_arrays=True)
+            logger.debug("peer properties: " + str(res))
+
+            if res['DeviceAddress'] == a1:
+                if 'SecondaryDeviceTypes' not in res:
+                    raise Exception("Missing SecondaryDeviceTypes")
+                sec = res['SecondaryDeviceTypes']
+                if len(sec) < 1:
+                    raise Exception("Secondary device type missing")
+                if "\x00\x01\x00\x50\xF2\x04\x00\x02" not in sec:
+                    raise Exception("Secondary device type mismatch")
+
+                if 'VendorExtension' not in res:
+                    raise Exception("Missing VendorExtension")
+                vendor = res['VendorExtension']
+                if len(vendor) < 1:
+                    raise Exception("Vendor extension missing")
+                if "\x11\x22\x33\x44" not in vendor:
+                    raise Exception("Secondary device type mismatch")
+
+                self.found = True
+            elif res['DeviceAddress'] == a2:
+                if 'IEs' not in res:
+                    raise Exception("IEs missing")
+                if res['IEs'] != wfd:
+                    raise Exception("IEs mismatch")
+                self.found2 = True
+            else:
+                raise Exception("Unexpected peer device address")
+
+            if self.found and self.found2:
+                p2p.StopFind()
+                p2p.RejectPeer(path)
+                p2p.ProvisionDiscoveryRequest(path, 'display')
+
+        def deviceLost(self, path):
+            logger.debug("deviceLost: path=%s" % path)
+            self.lost = True
+            try:
+                p2p.RejectPeer(path)
+                raise Exception("Invalid RejectPeer accepted")
+            except dbus.exceptions.DBusException, e:
+                if "UnknownError: Failed to call wpas_p2p_reject" not in str(e):
+                    raise Exception("Unexpected error message for invalid RejectPeer(): " + str(e))
+            self.loop.quit()
+
+        def provisionDiscoveryResponseEnterPin(self, peer_object):
+            logger.debug("provisionDiscoveryResponseEnterPin - peer=%s" % peer_object)
+            p2p.Flush()
+
+        def findStopped(self):
+            logger.debug("findStopped")
+            self.find_stopped = True
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            p2p.Find(dbus.Dictionary({'DiscoveryType': 'social',
+                                      'Timeout': dbus.Int32(10)}))
+            return False
+
+        def success(self):
+            return self.found and self.lost and self.found2 and self.find_stopped
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+    dev[1].request("VENDOR_ELEM_REMOVE 1 *")
+    dev[1].p2p_stop_find()
+
+    p2p.Listen(1)
+    dev[2].p2p_stop_find()
+    dev[2].request("P2P_FLUSH")
+    if not dev[2].discover_peer(addr0):
+        raise Exception("Peer not found")
+    p2p.StopFind()
+    dev[2].p2p_stop_find()
+
+    try:
+        p2p.ExtendedListen(dbus.Dictionary({'foo': 100}))
+        raise Exception("Invalid ExtendedListen accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid ExtendedListen(): " + str(e))
+
+    p2p.ExtendedListen(dbus.Dictionary({'period': 100, 'interval': 1000}))
+    p2p.ExtendedListen(dbus.Dictionary({}))
+    dev[0].global_request("P2P_EXT_LISTEN")
+
+def test_dbus_p2p_service_discovery(dev, apdev):
+    """D-Bus P2P service discovery"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+
+    bonjour_query = dbus.ByteArray(binascii.unhexlify('0b5f6166706f766572746370c00c000c01'))
+    bonjour_response = dbus.ByteArray(binascii.unhexlify('074578616d706c65c027'))
+                                   
+    args = { 'service_type': 'bonjour',
+             'query': bonjour_query,
+             'response': bonjour_response }
+    p2p.AddService(args)
+    p2p.FlushService()
+    p2p.AddService(args)
+
+    try:
+        p2p.DeleteService(args)
+        raise Exception("Invalid DeleteService() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid DeleteService(): " + str(e))
+
+    args = { 'service_type': 'bonjour',
+             'query': bonjour_query }
+    p2p.DeleteService(args)
+    try:
+        p2p.DeleteService(args)
+        raise Exception("Invalid DeleteService() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid DeleteService(): " + str(e))
+
+    args = { 'service_type': 'upnp',
+             'version': 0x10,
+             'service': 'uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice' }
+    p2p.AddService(args)
+    p2p.DeleteService(args)
+    try:
+        p2p.DeleteService(args)
+        raise Exception("Invalid DeleteService() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid DeleteService(): " + str(e))
+
+    tests = [ { 'service_type': 'foo' },
+              { 'service_type': 'foo', 'query': bonjour_query },
+              { 'service_type': 'upnp' },
+              { 'service_type': 'upnp', 'version': 0x10 },
+              { 'service_type': 'upnp',
+                'service': 'uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice' },
+              { 'version': 0x10,
+                'service': 'uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice' },
+              { 'service_type': 'upnp', 'foo': 'bar' },
+              { 'service_type': 'bonjour' },
+              { 'service_type': 'bonjour', 'query': 'foo' },
+              { 'service_type': 'bonjour', 'foo': 'bar' } ]
+    for args in tests:
+        try:
+            p2p.DeleteService(args)
+            raise Exception("Invalid DeleteService() accepted")
+        except dbus.exceptions.DBusException, e:
+            if "InvalidArgs" not in str(e):
+                raise Exception("Unexpected error message for invalid DeleteService(): " + str(e))
+
+    tests = [ { 'service_type': 'foo' },
+              { 'service_type': 'upnp' },
+              { 'service_type': 'upnp', 'version': 0x10 },
+              { 'service_type': 'upnp',
+                'service': 'uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice' },
+              { 'version': 0x10,
+                'service': 'uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice' },
+              { 'service_type': 'upnp', 'foo': 'bar' },
+              { 'service_type': 'bonjour' },
+              { 'service_type': 'bonjour', 'query': 'foo' },
+              { 'service_type': 'bonjour', 'response': 'foo' },
+              { 'service_type': 'bonjour', 'query': bonjour_query },
+              { 'service_type': 'bonjour', 'response': bonjour_response },
+              { 'service_type': 'bonjour', 'query': dbus.ByteArray(500*'a') },
+              { 'service_type': 'bonjour', 'foo': 'bar' } ]
+    for args in tests:
+        try:
+            p2p.AddService(args)
+            raise Exception("Invalid AddService() accepted")
+        except dbus.exceptions.DBusException, e:
+            if "InvalidArgs" not in str(e):
+                raise Exception("Unexpected error message for invalid AddService(): " + str(e))
+
+    args = { 'tlv': dbus.ByteArray("\x02\x00\x00\x01") }
+    ref = p2p.ServiceDiscoveryRequest(args)
+    p2p.ServiceDiscoveryCancelRequest(ref)
+    try:
+        p2p.ServiceDiscoveryCancelRequest(ref)
+        raise Exception("Invalid ServiceDiscoveryCancelRequest() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid AddService(): " + str(e))
+    try:
+        p2p.ServiceDiscoveryCancelRequest(dbus.UInt64(0))
+        raise Exception("Invalid ServiceDiscoveryCancelRequest() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid AddService(): " + str(e))
+
+    args = { 'service_type': 'upnp',
+             'version': 0x10,
+             'service': 'ssdp:foo' }
+    ref = p2p.ServiceDiscoveryRequest(args)
+    p2p.ServiceDiscoveryCancelRequest(ref)
+
+    tests =  [ { 'service_type': 'foo' },
+               { 'foo': 'bar' },
+               { 'tlv': 'foo' },
+               { },
+               { 'version': 0 },
+               { 'service_type': 'upnp',
+                 'service': 'ssdp:foo' },
+               { 'service_type': 'upnp',
+                 'version': 0x10 },
+               { 'service_type': 'upnp',
+                 'version': 0x10,
+                 'service': 'ssdp:foo',
+                 'peer_object': dbus.ObjectPath(path + "/Peers") },
+               { 'service_type': 'upnp',
+                 'version': 0x10,
+                 'service': 'ssdp:foo',
+                 'peer_object': path + "/Peers" },
+               { 'service_type': 'upnp',
+                 'version': 0x10,
+                 'service': 'ssdp:foo',
+                 'peer_object': dbus.ObjectPath(path + "/Peers/00112233445566") } ]
+    for args in tests:
+        try:
+            p2p.ServiceDiscoveryRequest(args)
+            raise Exception("Invalid ServiceDiscoveryRequest accepted")
+        except dbus.exceptions.DBusException, e:
+            if "InvalidArgs" not in str(e):
+                raise Exception("Unexpected error message for invalid ServiceDiscoveryRequest(): " + str(e))
+
+    args = { 'foo': 'bar' }
+    try:
+        p2p.ServiceDiscoveryResponse(dbus.Dictionary(args, signature='sv'))
+        raise Exception("Invalid ServiceDiscoveryResponse accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidArgs" not in str(e):
+            raise Exception("Unexpected error message for invalid ServiceDiscoveryResponse(): " + str(e))
+
+def test_dbus_p2p_service_discovery_query(dev, apdev):
+    """D-Bus P2P service discovery query"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+    addr0 = dev[0].p2p_dev_addr()
+    dev[1].request("P2P_SERVICE_ADD bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027")
+    dev[1].p2p_listen()
+    addr1 = dev[1].p2p_dev_addr()
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.done = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceFound")
+            self.add_signal(self.serviceDiscoveryResponse,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "ServiceDiscoveryResponse", byte_arrays=True)
+            self.loop.run()
+            return self
+
+        def deviceFound(self, path):
+            logger.debug("deviceFound: path=%s" % path)
+            args = { 'peer_object': path,
+                     'tlv': dbus.ByteArray("\x02\x00\x00\x01") }
+            p2p.ServiceDiscoveryRequest(args)
+
+        def serviceDiscoveryResponse(self, sd_request):
+            logger.debug("serviceDiscoveryResponse: sd_request=%s" % str(sd_request))
+            self.done = True
+            self.loop.quit()
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            p2p.Find(dbus.Dictionary({'DiscoveryType': 'social',
+                                      'Timeout': dbus.Int32(10)}))
+            return False
+
+        def success(self):
+            return self.done
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+    dev[1].p2p_stop_find()
+
+def test_dbus_p2p_service_discovery_external(dev, apdev):
+    """D-Bus P2P service discovery with external response"""
+    try:
+        _test_dbus_p2p_service_discovery_external(dev, apdev)
+    finally:
+        dev[0].request("P2P_SERV_DISC_EXTERNAL 0")
+
+def _test_dbus_p2p_service_discovery_external(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    resp = "0300000101"
+
+    dev[1].request("P2P_FLUSH")
+    dev[1].request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+    dev[1].p2p_find(social=True)
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.sd = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceFound")
+            self.add_signal(self.serviceDiscoveryRequest,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "ServiceDiscoveryRequest")
+            self.loop.run()
+            return self
+
+        def deviceFound(self, path):
+            logger.debug("deviceFound: path=%s" % path)
+
+        def serviceDiscoveryRequest(self, sd_request):
+            logger.debug("serviceDiscoveryRequest: sd_request=%s" % str(sd_request))
+            self.sd = True
+            args = { 'peer_object': sd_request['peer_object'],
+                     'frequency': sd_request['frequency'],
+                     'dialog_token': sd_request['dialog_token'],
+                     'tlvs': dbus.ByteArray(binascii.unhexlify(resp)) }
+            p2p.ServiceDiscoveryResponse(dbus.Dictionary(args, signature='sv'))
+            self.loop.quit()
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            p2p.ServiceDiscoveryExternal(1)
+            p2p.ServiceUpdate()
+            p2p.Listen(15)
+            return False
+
+        def success(self):
+            return self.sd
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+    ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=5)
+    if ev is None:
+        raise Exception("Service discovery timed out")
+    if addr0 not in ev:
+        raise Exception("Unexpected address in SD Response: " + ev)
+    if ev.split(' ')[4] != resp:
+        raise Exception("Unexpected response data SD Response: " + ev)
+    dev[1].p2p_stop_find()
+
+    p2p.StopFind()
+    p2p.ServiceDiscoveryExternal(0)
+
+def test_dbus_p2p_autogo(dev, apdev):
+    """D-Bus P2P autonomous GO"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+    addr0 = dev[0].p2p_dev_addr()
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.first = True
+            self.waiting_end = False
+            self.exceptions = False
+            self.deauthorized = False
+            self.done = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceFound")
+            self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupStarted")
+            self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupFinished")
+            self.add_signal(self.persistentGroupAdded,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "PersistentGroupAdded")
+            self.add_signal(self.persistentGroupRemoved,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "PersistentGroupRemoved")
+            self.add_signal(self.provisionDiscoveryRequestDisplayPin,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "ProvisionDiscoveryRequestDisplayPin")
+            self.add_signal(self.staAuthorized, WPAS_DBUS_IFACE,
+                            "StaAuthorized")
+            self.add_signal(self.staDeauthorized, WPAS_DBUS_IFACE,
+                            "StaDeauthorized")
+            self.loop.run()
+            return self
+
+        def groupStarted(self, properties):
+            logger.debug("groupStarted: " + str(properties))
+            self.group = properties['group_object']
+            self.g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                           properties['interface_object'])
+            role = self.g_if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "Role",
+                                     dbus_interface=dbus.PROPERTIES_IFACE)
+            if role != "GO":
+                self.exceptions = True
+                raise Exception("Unexpected role reported: " + role)
+            group = self.g_if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "Group",
+                                      dbus_interface=dbus.PROPERTIES_IFACE)
+            if group != properties['group_object']:
+                self.exceptions = True
+                raise Exception("Unexpected Group reported: " + str(group))
+            go = self.g_if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "PeerGO",
+                                   dbus_interface=dbus.PROPERTIES_IFACE)
+            if go != '/':
+                self.exceptions = True
+                raise Exception("Unexpected PeerGO value: " + str(go))
+            if self.first:
+                self.first = False
+                logger.info("Remove persistent group instance")
+                group_p2p = dbus.Interface(self.g_if_obj,
+                                           WPAS_DBUS_IFACE_P2PDEVICE)
+                group_p2p.Disconnect()
+            else:
+                dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+                dev1.global_request("P2P_CONNECT " + addr0 + " 12345670 join")
+
+        def groupFinished(self, properties):
+            logger.debug("groupFinished: " + str(properties))
+            if self.waiting_end:
+                logger.info("Remove persistent group")
+                p2p.RemovePersistentGroup(self.persistent)
+            else:
+                logger.info("Re-start persistent group")
+                params = dbus.Dictionary({'persistent_group_object':
+                                          self.persistent,
+                                          'frequency': 2412})
+                p2p.GroupAdd(params)
+
+        def persistentGroupAdded(self, path, properties):
+            logger.debug("persistentGroupAdded: %s %s" % (path, str(properties)))
+            self.persistent = path
+
+        def persistentGroupRemoved(self, path):
+            logger.debug("persistentGroupRemoved: %s" % path)
+            self.done = True
+            self.loop.quit()
+
+        def deviceFound(self, path):
+            logger.debug("deviceFound: path=%s" % path)
+            peer_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+            self.peer = peer_obj.GetAll(WPAS_DBUS_P2P_PEER,
+                                        dbus_interface=dbus.PROPERTIES_IFACE,
+                                        byte_arrays=True)
+            logger.debug('peer properties: ' + str(self.peer))
+
+        def provisionDiscoveryRequestDisplayPin(self, peer_object, pin):
+            logger.debug("provisionDiscoveryRequestDisplayPin - peer=%s pin=%s" % (peer_object, pin))
+            self.peer_path = peer_object
+            peer = binascii.unhexlify(peer_object.split('/')[-1])
+            addr = ""
+            for p in peer:
+                if len(addr) > 0:
+                    addr += ':'
+                addr += '%02x' % ord(p)
+
+            params = { 'Role': 'registrar',
+                       'P2PDeviceAddress': self.peer['DeviceAddress'],
+                       'Bssid': self.peer['DeviceAddress'],
+                       'Type': 'pin' }
+            wps = dbus.Interface(self.g_if_obj, WPAS_DBUS_IFACE_WPS)
+            try:
+                wps.Start(params)
+                self.exceptions = True
+                raise Exception("Invalid WPS.Start() accepted")
+            except dbus.exceptions.DBusException, e:
+                if "InvalidArgs" not in str(e):
+                    self.exceptions = True
+                    raise Exception("Unexpected error message: " + str(e))
+            params = { 'Role': 'registrar',
+                       'P2PDeviceAddress': self.peer['DeviceAddress'],
+                       'Type': 'pin',
+                       'Pin': '12345670' }
+            logger.info("Authorize peer to connect to the group")
+            wps.Start(params)
+
+        def staAuthorized(self, name):
+            logger.debug("staAuthorized: " + name)
+            peer_obj = bus.get_object(WPAS_DBUS_SERVICE, self.peer_path)
+            res = peer_obj.GetAll(WPAS_DBUS_P2P_PEER,
+                                  dbus_interface=dbus.PROPERTIES_IFACE,
+                                  byte_arrays=True)
+            logger.debug("Peer properties: " + str(res))
+            if 'Groups' not in res or len(res['Groups']) != 1:
+                self.exceptions = True
+                raise Exception("Unexpected number of peer Groups entries")
+            if res['Groups'][0] != self.group:
+                self.exceptions = True
+                raise Exception("Unexpected peer Groups[0] value")
+
+            g_obj = bus.get_object(WPAS_DBUS_SERVICE, self.group)
+            res = g_obj.GetAll(WPAS_DBUS_GROUP,
+                               dbus_interface=dbus.PROPERTIES_IFACE,
+                               byte_arrays=True)
+            logger.debug("Group properties: " + str(res))
+            if 'Members' not in res or len(res['Members']) != 1:
+                self.exceptions = True
+                raise Exception("Unexpected number of group members")
+
+            ext = dbus.ByteArray("\x11\x22\x33\x44")
+            # Earlier implementation of this interface was a bit strange. The
+            # property is defined to have aay signature and that is what the
+            # getter returned. However, the setter expected there to be a
+            # dictionary with 'WPSVendorExtensions' as the key surrounding these
+            # values.. The current implementations maintains support for that
+            # for backwards compability reasons. Verify that encoding first.
+            vals = dbus.Dictionary({ 'WPSVendorExtensions': [ ext ]},
+                                   signature='sv')
+            g_obj.Set(WPAS_DBUS_GROUP, 'WPSVendorExtensions', vals,
+                      dbus_interface=dbus.PROPERTIES_IFACE)
+            res = g_obj.Get(WPAS_DBUS_GROUP, 'WPSVendorExtensions',
+                               dbus_interface=dbus.PROPERTIES_IFACE,
+                               byte_arrays=True)
+            if len(res) != 1:
+                self.exceptions = True
+                raise Exception("Unexpected number of vendor extensions")
+            if res[0] != ext:
+                self.exceptions = True
+                raise Exception("Vendor extension value changed")
+
+            # And now verify that the more appropriate encoding is accepted as
+            # well.
+            res.append(dbus.ByteArray('\xaa\xbb\xcc\xdd\xee\xff'))
+            g_obj.Set(WPAS_DBUS_GROUP, 'WPSVendorExtensions', res,
+                      dbus_interface=dbus.PROPERTIES_IFACE)
+            res2 = g_obj.Get(WPAS_DBUS_GROUP, 'WPSVendorExtensions',
+                             dbus_interface=dbus.PROPERTIES_IFACE,
+                             byte_arrays=True)
+            if len(res) != 2:
+                self.exceptions = True
+                raise Exception("Unexpected number of vendor extensions")
+            if res[0] != res2[0] or res[1] != res2[1]:
+                self.exceptions = True
+                raise Exception("Vendor extension value changed")
+
+            for i in range(10):
+                res.append(dbus.ByteArray('\xaa\xbb'))
+            try:
+                g_obj.Set(WPAS_DBUS_GROUP, 'WPSVendorExtensions', res,
+                          dbus_interface=dbus.PROPERTIES_IFACE)
+                self.exceptions = True
+                raise Exception("Invalid Set(WPSVendorExtensions) accepted")
+            except dbus.exceptions.DBusException, e:
+                if "Error.Failed" not in str(e):
+                    self.exceptions = True
+                    raise Exception("Unexpected error message for invalid Set(WPSVendorExtensions): " + str(e))
+
+            vals = dbus.Dictionary({ 'Foo': [ ext ]}, signature='sv')
+            try:
+                g_obj.Set(WPAS_DBUS_GROUP, 'WPSVendorExtensions', vals,
+                          dbus_interface=dbus.PROPERTIES_IFACE)
+                self.exceptions = True
+                raise Exception("Invalid Set(WPSVendorExtensions) accepted")
+            except dbus.exceptions.DBusException, e:
+                if "InvalidArgs" not in str(e):
+                    self.exceptions = True
+                    raise Exception("Unexpected error message for invalid Set(WPSVendorExtensions): " + str(e))
+
+            vals = [ "foo" ]
+            try:
+                g_obj.Set(WPAS_DBUS_GROUP, 'WPSVendorExtensions', vals,
+                          dbus_interface=dbus.PROPERTIES_IFACE)
+                self.exceptions = True
+                raise Exception("Invalid Set(WPSVendorExtensions) accepted")
+            except dbus.exceptions.DBusException, e:
+                if "Error.Failed" not in str(e):
+                    self.exceptions = True
+                    raise Exception("Unexpected error message for invalid Set(WPSVendorExtensions): " + str(e))
+
+            vals = [ [ "foo" ] ]
+            try:
+                g_obj.Set(WPAS_DBUS_GROUP, 'WPSVendorExtensions', vals,
+                          dbus_interface=dbus.PROPERTIES_IFACE)
+                self.exceptions = True
+                raise Exception("Invalid Set(WPSVendorExtensions) accepted")
+            except dbus.exceptions.DBusException, e:
+                if "Error.Failed" not in str(e):
+                    self.exceptions = True
+                    raise Exception("Unexpected error message for invalid Set(WPSVendorExtensions): " + str(e))
+
+            p2p.RemoveClient({ 'peer': self.peer_path })
+
+            self.waiting_end = True
+            group_p2p = dbus.Interface(self.g_if_obj,
+                                       WPAS_DBUS_IFACE_P2PDEVICE)
+            group_p2p.Disconnect()
+
+        def staDeauthorized(self, name):
+            logger.debug("staDeauthorized: " + name)
+            self.deauthorized = True
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            params = dbus.Dictionary({'persistent': True,
+                                      'frequency': 2412})
+            logger.info("Add a persistent group")
+            p2p.GroupAdd(params)
+            return False
+
+        def success(self):
+            return self.done and self.deauthorized and not self.exceptions
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+    dev[1].wait_go_ending_session()
+
+def test_dbus_p2p_autogo_pbc(dev, apdev):
+    """D-Bus P2P autonomous GO and PBC"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+    addr0 = dev[0].p2p_dev_addr()
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.first = True
+            self.waiting_end = False
+            self.done = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceFound")
+            self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupStarted")
+            self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupFinished")
+            self.add_signal(self.provisionDiscoveryPBCRequest,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "ProvisionDiscoveryPBCRequest")
+            self.add_signal(self.staAuthorized, WPAS_DBUS_IFACE,
+                            "StaAuthorized")
+            self.loop.run()
+            return self
+
+        def groupStarted(self, properties):
+            logger.debug("groupStarted: " + str(properties))
+            self.group = properties['group_object']
+            self.g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                           properties['interface_object'])
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            dev1.global_request("P2P_CONNECT " + addr0 + " pbc join")
+
+        def groupFinished(self, properties):
+            logger.debug("groupFinished: " + str(properties))
+            self.done = True
+            self.loop.quit()
+
+        def deviceFound(self, path):
+            logger.debug("deviceFound: path=%s" % path)
+            peer_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+            self.peer = peer_obj.GetAll(WPAS_DBUS_P2P_PEER,
+                                        dbus_interface=dbus.PROPERTIES_IFACE,
+                                        byte_arrays=True)
+            logger.debug('peer properties: ' + str(self.peer))
+
+        def provisionDiscoveryPBCRequest(self, peer_object):
+            logger.debug("provisionDiscoveryPBCRequest - peer=%s" % peer_object)
+            self.peer_path = peer_object
+            peer = binascii.unhexlify(peer_object.split('/')[-1])
+            addr = ""
+            for p in peer:
+                if len(addr) > 0:
+                    addr += ':'
+                addr += '%02x' % ord(p)
+            params = { 'Role': 'registrar',
+                       'P2PDeviceAddress': self.peer['DeviceAddress'],
+                       'Type': 'pbc' }
+            logger.info("Authorize peer to connect to the group")
+            wps = dbus.Interface(self.g_if_obj, WPAS_DBUS_IFACE_WPS)
+            wps.Start(params)
+
+        def staAuthorized(self, name):
+            logger.debug("staAuthorized: " + name)
+            group_p2p = dbus.Interface(self.g_if_obj,
+                                       WPAS_DBUS_IFACE_P2PDEVICE)
+            group_p2p.Disconnect()
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            params = dbus.Dictionary({'frequency': 2412})
+            p2p.GroupAdd(params)
+            return False
+
+        def success(self):
+            return self.done
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+    dev[1].wait_go_ending_session()
+    dev[1].flush_scan_cache()
+
+def test_dbus_p2p_autogo_legacy(dev, apdev):
+    """D-Bus P2P autonomous GO and legacy STA"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+    addr0 = dev[0].p2p_dev_addr()
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.done = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupStarted")
+            self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupFinished")
+            self.add_signal(self.staAuthorized, WPAS_DBUS_IFACE,
+                            "StaAuthorized")
+            self.loop.run()
+            return self
+
+        def groupStarted(self, properties):
+            logger.debug("groupStarted: " + str(properties))
+            g_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                   properties['group_object'])
+            res = g_obj.GetAll(WPAS_DBUS_GROUP,
+                               dbus_interface=dbus.PROPERTIES_IFACE,
+                               byte_arrays=True)
+            bssid = ':'.join([binascii.hexlify(l) for l in res['BSSID']])
+
+            pin = '12345670'
+            params = { 'Role': 'enrollee',
+                       'Type': 'pin',
+                       'Pin': pin }
+            g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                      properties['interface_object'])
+            wps = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE_WPS)
+            wps.Start(params)
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            dev1.scan_for_bss(bssid, freq=2412)
+            dev1.request("WPS_PIN " + bssid + " " + pin)
+            self.group_p2p = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+        def groupFinished(self, properties):
+            logger.debug("groupFinished: " + str(properties))
+            self.done = True
+            self.loop.quit()
+
+        def staAuthorized(self, name):
+            logger.debug("staAuthorized: " + name)
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            dev1.request("DISCONNECT")
+            self.group_p2p.Disconnect()
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            params = dbus.Dictionary({'frequency': 2412})
+            p2p.GroupAdd(params)
+            return False
+
+        def success(self):
+            return self.done
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_join(dev, apdev):
+    """D-Bus P2P join an autonomous GO"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+    addr1 = dev[1].p2p_dev_addr()
+    addr2 = dev[2].p2p_dev_addr()
+    dev[1].p2p_start_go(freq=2412)
+    dev1_group_ifname = dev[1].group_ifname
+    dev[2].p2p_listen()
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.done = False
+            self.peer = None
+            self.go = None
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceFound")
+            self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupStarted")
+            self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupFinished")
+            self.add_signal(self.invitationResult, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "InvitationResult")
+            self.loop.run()
+            return self
+
+        def deviceFound(self, path):
+            logger.debug("deviceFound: path=%s" % path)
+            peer_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+            res = peer_obj.GetAll(WPAS_DBUS_P2P_PEER,
+                                  dbus_interface=dbus.PROPERTIES_IFACE,
+                                  byte_arrays=True)
+            logger.debug('peer properties: ' + str(res))
+            if addr2.replace(':','') in path:
+                self.peer = path
+            elif addr1.replace(':','') in path:
+                self.go = path
+            if self.peer and self.go:
+                logger.info("Join the group")
+                p2p.StopFind()
+                args = { 'peer': self.go,
+                         'join': True,
+                         'wps_method': 'pin',
+                         'frequency': 2412 }
+                pin = p2p.Connect(args)
+
+                dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+                dev1.group_ifname = dev1_group_ifname
+                dev1.group_request("WPS_PIN any " + pin)
+
+        def groupStarted(self, properties):
+            logger.debug("groupStarted: " + str(properties))
+            g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                      properties['interface_object'])
+            role = g_if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "Role",
+                                dbus_interface=dbus.PROPERTIES_IFACE)
+            if role != "client":
+                raise Exception("Unexpected role reported: " + role)
+            group = g_if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "Group",
+                                 dbus_interface=dbus.PROPERTIES_IFACE)
+            if group != properties['group_object']:
+                raise Exception("Unexpected Group reported: " + str(group))
+            go = g_if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "PeerGO",
+                              dbus_interface=dbus.PROPERTIES_IFACE)
+            if go != self.go:
+                raise Exception("Unexpected PeerGO value: " + str(go))
+
+            g_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                   properties['group_object'])
+            res = g_obj.GetAll(WPAS_DBUS_GROUP,
+                               dbus_interface=dbus.PROPERTIES_IFACE,
+                               byte_arrays=True)
+            logger.debug("Group properties: " + str(res))
+
+            ext = dbus.ByteArray("\x11\x22\x33\x44")
+            try:
+                # Set(WPSVendorExtensions) not allowed for P2P Client
+                g_obj.Set(WPAS_DBUS_GROUP, 'WPSVendorExtensions', res,
+                          dbus_interface=dbus.PROPERTIES_IFACE)
+                raise Exception("Invalid Set(WPSVendorExtensions) accepted")
+            except dbus.exceptions.DBusException, e:
+                if "Error.Failed: Failed to set property" not in str(e):
+                    raise Exception("Unexpected error message for invalid Set(WPSVendorExtensions): " + str(e))
+
+            group_p2p = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+            args = { 'duration1': 30000, 'interval1': 102400,
+                     'duration2': 20000, 'interval2': 102400 }
+            group_p2p.PresenceRequest(args)
+
+            args = { 'peer': self.peer }
+            group_p2p.Invite(args)
+
+        def groupFinished(self, properties):
+            logger.debug("groupFinished: " + str(properties))
+            self.done = True
+            self.loop.quit()
+
+        def invitationResult(self, result):
+            logger.debug("invitationResult: " + str(result))
+            if result['status'] != 1:
+                raise Exception("Unexpected invitation result: " + str(result))
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            dev1.group_ifname = dev1_group_ifname
+            dev1.remove_group()
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            p2p.Find(dbus.Dictionary({'DiscoveryType': 'social'}))
+            return False
+
+        def success(self):
+            return self.done
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+    dev[2].p2p_stop_find()
+
+def test_dbus_p2p_config(dev, apdev):
+    """D-Bus Get/Set P2PDeviceConfig"""
+    try:
+        _test_dbus_p2p_config(dev, apdev)
+    finally:
+        dev[0].request("P2P_SET ssid_postfix ")
+
+def _test_dbus_p2p_config(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+    res = if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+                     dbus_interface=dbus.PROPERTIES_IFACE,
+                     byte_arrays=True)
+    if_obj.Set(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig", res,
+               dbus_interface=dbus.PROPERTIES_IFACE)
+    res2 = if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+                      dbus_interface=dbus.PROPERTIES_IFACE,
+                      byte_arrays=True)
+
+    if len(res) != len(res2):
+        raise Exception("Different number of parameters")
+    for k in res:
+        if res[k] != res2[k]:
+            raise Exception("Parameter %s value changes" % k)
+
+    changes = { 'SsidPostfix': 'foo',
+                'VendorExtension': [ dbus.ByteArray('\x11\x22\x33\x44') ],
+                'SecondaryDeviceTypes': [ dbus.ByteArray('\x11\x22\x33\x44\x55\x66\x77\x88') ]}
+    if_obj.Set(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+               dbus.Dictionary(changes, signature='sv'),
+               dbus_interface=dbus.PROPERTIES_IFACE)
+
+    res2 = if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+                      dbus_interface=dbus.PROPERTIES_IFACE,
+                      byte_arrays=True)
+    logger.debug("P2PDeviceConfig: " + str(res2))
+    if 'VendorExtension' not in res2 or len(res2['VendorExtension']) != 1:
+        raise Exception("VendorExtension does not match")
+    if 'SecondaryDeviceTypes' not in res2 or len(res2['SecondaryDeviceTypes']) != 1:
+        raise Exception("SecondaryDeviceType does not match")
+
+    changes = { 'SsidPostfix': '',
+                'VendorExtension': dbus.Array([], signature="ay"),
+                'SecondaryDeviceTypes': dbus.Array([], signature="ay") }
+    if_obj.Set(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+               dbus.Dictionary(changes, signature='sv'),
+               dbus_interface=dbus.PROPERTIES_IFACE)
+
+    res3 = if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+                      dbus_interface=dbus.PROPERTIES_IFACE,
+                      byte_arrays=True)
+    logger.debug("P2PDeviceConfig: " + str(res3))
+    if 'VendorExtension' in res3:
+        raise Exception("VendorExtension not removed")
+    if 'SecondaryDeviceTypes' in res3:
+        raise Exception("SecondaryDeviceType not removed")
+
+    try:
+        dev[0].request("P2P_SET disabled 1")
+        if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+                   dbus_interface=dbus.PROPERTIES_IFACE,
+                   byte_arrays=True)
+        raise Exception("Invalid Get(P2PDeviceConfig) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: P2P is not available for this interface" not in str(e):
+            raise Exception("Unexpected error message for invalid Invite: " + str(e))
+    finally:
+        dev[0].request("P2P_SET disabled 0")
+
+    try:
+        dev[0].request("P2P_SET disabled 1")
+        changes = { 'SsidPostfix': 'foo' }
+        if_obj.Set(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+                   dbus.Dictionary(changes, signature='sv'),
+                   dbus_interface=dbus.PROPERTIES_IFACE)
+        raise Exception("Invalid Set(P2PDeviceConfig) accepted")
+    except dbus.exceptions.DBusException, e:
+        if "Error.Failed: P2P is not available for this interface" not in str(e):
+            raise Exception("Unexpected error message for invalid Invite: " + str(e))
+    finally:
+        dev[0].request("P2P_SET disabled 0")
+
+    tests = [ { 'DeviceName': 123 },
+              { 'SsidPostfix': 123 },
+              { 'Foo': 'Bar' } ]
+    for changes in tests:
+        try:
+            if_obj.Set(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+                       dbus.Dictionary(changes, signature='sv'),
+                       dbus_interface=dbus.PROPERTIES_IFACE)
+            raise Exception("Invalid Set(P2PDeviceConfig) accepted")
+        except dbus.exceptions.DBusException, e:
+            if "InvalidArgs" not in str(e):
+                raise Exception("Unexpected error message for invalid Invite: " + str(e))
+
+def test_dbus_p2p_persistent(dev, apdev):
+    """D-Bus P2P persistent group"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupStarted")
+            self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupFinished")
+            self.add_signal(self.persistentGroupAdded,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "PersistentGroupAdded")
+            self.loop.run()
+            return self
+
+        def groupStarted(self, properties):
+            logger.debug("groupStarted: " + str(properties))
+            g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                      properties['interface_object'])
+            group_p2p = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+            group_p2p.Disconnect()
+
+        def groupFinished(self, properties):
+            logger.debug("groupFinished: " + str(properties))
+            self.loop.quit()
+
+        def persistentGroupAdded(self, path, properties):
+            logger.debug("persistentGroupAdded: %s %s" % (path, str(properties)))
+            self.persistent = path
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            params = dbus.Dictionary({'persistent': True,
+                                      'frequency': 2412})
+            logger.info("Add a persistent group")
+            p2p.GroupAdd(params)
+            return False
+
+        def success(self):
+            return True
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+        persistent = t.persistent
+
+    p_obj = bus.get_object(WPAS_DBUS_SERVICE, persistent)
+    res = p_obj.Get(WPAS_DBUS_PERSISTENT_GROUP, "Properties",
+                    dbus_interface=dbus.PROPERTIES_IFACE, byte_arrays=True)
+    logger.info("Persistent group Properties: " + str(res))
+    vals = dbus.Dictionary({ 'ssid': 'DIRECT-foo' }, signature='sv')
+    p_obj.Set(WPAS_DBUS_PERSISTENT_GROUP, "Properties", vals,
+              dbus_interface=dbus.PROPERTIES_IFACE)
+    res2 = p_obj.Get(WPAS_DBUS_PERSISTENT_GROUP, "Properties",
+                     dbus_interface=dbus.PROPERTIES_IFACE)
+    if len(res) != len(res2):
+        raise Exception("Different number of parameters")
+    for k in res:
+        if k != 'ssid' and res[k] != res2[k]:
+            raise Exception("Parameter %s value changes" % k)
+    if res2['ssid'] != '"DIRECT-foo"':
+        raise Exception("Unexpected ssid")
+
+    args = dbus.Dictionary({ 'ssid': 'DIRECT-testing',
+                             'psk': '1234567890' }, signature='sv')
+    group = p2p.AddPersistentGroup(args)
+
+    groups = if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "PersistentGroups",
+                        dbus_interface=dbus.PROPERTIES_IFACE)
+    if len(groups) != 2:
+        raise Exception("Unexpected number of persistent groups: " + str(groups))
+
+    p2p.RemoveAllPersistentGroups()
+
+    groups = if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "PersistentGroups",
+                        dbus_interface=dbus.PROPERTIES_IFACE)
+    if len(groups) != 0:
+        raise Exception("Unexpected number of persistent groups: " + str(groups))
+
+    try:
+        p2p.RemovePersistentGroup(persistent)
+        raise Exception("Invalid RemovePersistentGroup accepted")
+    except dbus.exceptions.DBusException, e:
+        if "NetworkUnknown: There is no such persistent group" not in str(e):
+            raise Exception("Unexpected error message for invalid RemovePersistentGroup: " + str(e))
+
+def test_dbus_p2p_reinvoke_persistent(dev, apdev):
+    """D-Bus P2P reinvoke persistent group"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+    addr0 = dev[0].p2p_dev_addr()
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.first = True
+            self.waiting_end = False
+            self.done = False
+            self.invited = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceFound")
+            self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupStarted")
+            self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupFinished")
+            self.add_signal(self.persistentGroupAdded,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "PersistentGroupAdded")
+            self.add_signal(self.provisionDiscoveryRequestDisplayPin,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "ProvisionDiscoveryRequestDisplayPin")
+            self.add_signal(self.staAuthorized, WPAS_DBUS_IFACE,
+                            "StaAuthorized")
+            self.loop.run()
+            return self
+
+        def groupStarted(self, properties):
+            logger.debug("groupStarted: " + str(properties))
+            self.g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                           properties['interface_object'])
+            if not self.invited:
+                g_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                       properties['group_object'])
+                res = g_obj.GetAll(WPAS_DBUS_GROUP,
+                                   dbus_interface=dbus.PROPERTIES_IFACE,
+                                   byte_arrays=True)
+                bssid = ':'.join([binascii.hexlify(l) for l in res['BSSID']])
+                dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+                dev1.scan_for_bss(bssid, freq=2412)
+                dev1.global_request("P2P_CONNECT " + addr0 + " 12345670 join")
+
+        def groupFinished(self, properties):
+            logger.debug("groupFinished: " + str(properties))
+            if self.invited:
+                self.done = True
+                self.loop.quit()
+            else:
+                dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+                dev1.global_request("SET persistent_reconnect 1")
+                dev1.p2p_listen()
+
+                args = { 'persistent_group_object': dbus.ObjectPath(path),
+                         'peer': self.peer_path }
+                try:
+                    pin = p2p.Invite(args)
+                    raise Exception("Invalid Invite accepted")
+                except dbus.exceptions.DBusException, e:
+                    if "InvalidArgs" not in str(e):
+                        raise Exception("Unexpected error message for invalid Invite: " + str(e))
+
+                args = { 'persistent_group_object': self.persistent,
+                         'peer': self.peer_path }
+                pin = p2p.Invite(args)
+                self.invited = True
+
+                self.sta_group_ev = dev1.wait_global_event(["P2P-GROUP-STARTED"],
+                                                           timeout=15)
+                if self.sta_group_ev is None:
+                    raise Exception("P2P-GROUP-STARTED event not seen")
+
+        def persistentGroupAdded(self, path, properties):
+            logger.debug("persistentGroupAdded: %s %s" % (path, str(properties)))
+            self.persistent = path
+
+        def deviceFound(self, path):
+            logger.debug("deviceFound: path=%s" % path)
+            peer_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+            self.peer = peer_obj.GetAll(WPAS_DBUS_P2P_PEER,
+                                        dbus_interface=dbus.PROPERTIES_IFACE,
+                                        byte_arrays=True)
+
+        def provisionDiscoveryRequestDisplayPin(self, peer_object, pin):
+            logger.debug("provisionDiscoveryRequestDisplayPin - peer=%s pin=%s" % (peer_object, pin))
+            self.peer_path = peer_object
+            peer = binascii.unhexlify(peer_object.split('/')[-1])
+            addr = ""
+            for p in peer:
+                if len(addr) > 0:
+                    addr += ':'
+                addr += '%02x' % ord(p)
+            params = { 'Role': 'registrar',
+                       'P2PDeviceAddress': self.peer['DeviceAddress'],
+                       'Bssid': self.peer['DeviceAddress'],
+                       'Type': 'pin',
+                       'Pin': '12345670' }
+            logger.info("Authorize peer to connect to the group")
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            wps = dbus.Interface(self.g_if_obj, WPAS_DBUS_IFACE_WPS)
+            wps.Start(params)
+            self.sta_group_ev = dev1.wait_global_event(["P2P-GROUP-STARTED"],
+                                                       timeout=15)
+            if self.sta_group_ev is None:
+                raise Exception("P2P-GROUP-STARTED event not seen")
+
+        def staAuthorized(self, name):
+            logger.debug("staAuthorized: " + name)
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            dev1.group_form_result(self.sta_group_ev)
+            dev1.remove_group()
+            ev = dev1.wait_global_event(["P2P-GROUP-REMOVED"], timeout=10)
+            if ev is None:
+                raise Exception("Group removal timed out")
+            group_p2p = dbus.Interface(self.g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+            group_p2p.Disconnect()
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            params = dbus.Dictionary({'persistent': True,
+                                      'frequency': 2412})
+            logger.info("Add a persistent group")
+            p2p.GroupAdd(params)
+            return False
+
+        def success(self):
+            return self.done
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_go_neg_rx(dev, apdev):
+    """D-Bus P2P GO Negotiation receive"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+    addr0 = dev[0].p2p_dev_addr()
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.done = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceFound")
+            self.add_signal(self.goNegotiationRequest,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GONegotiationRequest",
+                            byte_arrays=True)
+            self.add_signal(self.goNegotiationSuccess,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GONegotiationSuccess",
+                            byte_arrays=True)
+            self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupStarted")
+            self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupFinished")
+            self.loop.run()
+            return self
+
+        def deviceFound(self, path):
+            logger.debug("deviceFound: path=%s" % path)
+
+        def goNegotiationRequest(self, path, dev_passwd_id, go_intent=0):
+            logger.debug("goNegotiationRequest: path=%s dev_passwd_id=%d go_intent=%d" % (path, dev_passwd_id, go_intent))
+            if dev_passwd_id != 1:
+                raise Exception("Unexpected dev_passwd_id=%d" % dev_passwd_id)
+            args = { 'peer': path, 'wps_method': 'display', 'pin': '12345670',
+                     'go_intent': 15, 'persistent': False, 'frequency': 5175 }
+            try:
+                p2p.Connect(args)
+                raise Exception("Invalid Connect accepted")
+            except dbus.exceptions.DBusException, e:
+                if "ConnectChannelUnsupported" not in str(e):
+                    raise Exception("Unexpected error message for invalid Connect: " + str(e))
+
+            args = { 'peer': path, 'wps_method': 'display', 'pin': '12345670',
+                     'go_intent': 15, 'persistent': False }
+            p2p.Connect(args)
+
+        def goNegotiationSuccess(self, properties):
+            logger.debug("goNegotiationSuccess: properties=%s" % str(properties))
+
+        def groupStarted(self, properties):
+            logger.debug("groupStarted: " + str(properties))
+            g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                      properties['interface_object'])
+            group_p2p = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+            group_p2p.Disconnect()
+
+        def groupFinished(self, properties):
+            logger.debug("groupFinished: " + str(properties))
+            self.done = True
+            self.loop.quit()
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            p2p.Listen(10)
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            if not dev1.discover_peer(addr0):
+                raise Exception("Peer not found")
+            dev1.global_request("P2P_CONNECT " + addr0 + " 12345670 enter")
+            return False
+
+        def success(self):
+            return self.done
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_go_neg_auth(dev, apdev):
+    """D-Bus P2P GO Negotiation authorized"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+    addr0 = dev[0].p2p_dev_addr()
+    dev[1].p2p_listen()
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.done = False
+            self.peer_joined = False
+            self.peer_disconnected = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceFound")
+            self.add_signal(self.goNegotiationSuccess,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GONegotiationSuccess",
+                            byte_arrays=True)
+            self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupStarted")
+            self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupFinished")
+            self.add_signal(self.staDeauthorized, WPAS_DBUS_IFACE,
+                            "StaDeauthorized")
+            self.add_signal(self.peerJoined, WPAS_DBUS_GROUP,
+                            "PeerJoined")
+            self.add_signal(self.peerDisconnected, WPAS_DBUS_GROUP,
+                            "PeerDisconnected")
+            self.loop.run()
+            return self
+
+        def deviceFound(self, path):
+            logger.debug("deviceFound: path=%s" % path)
+            args = { 'peer': path, 'wps_method': 'keypad',
+                     'go_intent': 15, 'authorize_only': True }
+            try:
+                p2p.Connect(args)
+                raise Exception("Invalid Connect accepted")
+            except dbus.exceptions.DBusException, e:
+                if "InvalidArgs" not in str(e):
+                    raise Exception("Unexpected error message for invalid Connect: " + str(e))
+
+            args = { 'peer': path, 'wps_method': 'keypad', 'pin': '12345670',
+                     'go_intent': 15, 'authorize_only': True }
+            p2p.Connect(args)
+            p2p.Listen(10)
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            if not dev1.discover_peer(addr0):
+                raise Exception("Peer not found")
+            dev1.global_request("P2P_CONNECT " + addr0 + " 12345670 display go_intent=0")
+            ev = dev1.wait_global_event(["P2P-GROUP-STARTED"], timeout=15);
+            if ev is None:
+                raise Exception("Group formation timed out")
+            self.sta_group_ev = ev
+
+        def goNegotiationSuccess(self, properties):
+            logger.debug("goNegotiationSuccess: properties=%s" % str(properties))
+
+        def groupStarted(self, properties):
+            logger.debug("groupStarted: " + str(properties))
+            self.g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                           properties['interface_object'])
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            dev1.group_form_result(self.sta_group_ev)
+            dev1.remove_group()
+
+        def staDeauthorized(self, name):
+            logger.debug("staDeuthorized: " + name)
+            group_p2p = dbus.Interface(self.g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+            group_p2p.Disconnect()
+
+        def peerJoined(self, peer):
+            logger.debug("peerJoined: " + peer)
+            self.peer_joined = True
+
+        def peerDisconnected(self, peer):
+            logger.debug("peerDisconnected: " + peer)
+            self.peer_disconnected = True
+
+        def groupFinished(self, properties):
+            logger.debug("groupFinished: " + str(properties))
+            self.done = True
+            self.loop.quit()
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            p2p.Find(dbus.Dictionary({'DiscoveryType': 'social'}))
+            return False
+
+        def success(self):
+            return self.done and self.peer_joined and self.peer_disconnected
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_go_neg_init(dev, apdev):
+    """D-Bus P2P GO Negotiation initiation"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+    addr0 = dev[0].p2p_dev_addr()
+    dev[1].p2p_listen()
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.done = False
+            self.peer_group_added = False
+            self.peer_group_removed = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceFound")
+            self.add_signal(self.goNegotiationSuccess,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GONegotiationSuccess",
+                            byte_arrays=True)
+            self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupStarted")
+            self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupFinished")
+            self.add_signal(self.propertiesChanged, dbus.PROPERTIES_IFACE,
+                            "PropertiesChanged")
+            self.loop.run()
+            return self
+
+        def deviceFound(self, path):
+            logger.debug("deviceFound: path=%s" % path)
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            args = { 'peer': path, 'wps_method': 'keypad', 'pin': '12345670',
+                     'go_intent': 0 }
+            p2p.Connect(args)
+
+            ev = dev1.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+            if ev is None:
+                raise Exception("Timeout while waiting for GO Neg Request")
+            dev1.global_request("P2P_CONNECT " + addr0 + " 12345670 display go_intent=15")
+            ev = dev1.wait_global_event(["P2P-GROUP-STARTED"], timeout=15);
+            if ev is None:
+                raise Exception("Group formation timed out")
+            self.sta_group_ev = ev
+
+        def goNegotiationSuccess(self, properties):
+            logger.debug("goNegotiationSuccess: properties=%s" % str(properties))
+
+        def groupStarted(self, properties):
+            logger.debug("groupStarted: " + str(properties))
+            g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                      properties['interface_object'])
+            group_p2p = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+            group_p2p.Disconnect()
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            dev1.group_form_result(self.sta_group_ev)
+            dev1.remove_group()
+
+        def groupFinished(self, properties):
+            logger.debug("groupFinished: " + str(properties))
+            self.done = True
+
+        def propertiesChanged(self, interface_name, changed_properties,
+                              invalidated_properties):
+            logger.debug("propertiesChanged: interface_name=%s changed_properties=%s invalidated_properties=%s" % (interface_name, str(changed_properties), str(invalidated_properties)))
+            if interface_name != WPAS_DBUS_P2P_PEER:
+                return
+            if "Groups" not in changed_properties:
+                return
+            if len(changed_properties["Groups"]) > 0:
+                self.peer_group_added = True
+            if len(changed_properties["Groups"]) == 0:
+                self.peer_group_removed = True
+                self.loop.quit()
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            p2p.Find(dbus.Dictionary({'DiscoveryType': 'social'}))
+            return False
+
+        def success(self):
+            return self.done and self.peer_group_added and self.peer_group_removed
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_group_termination_by_go(dev, apdev):
+    """D-Bus P2P group removal on GO terminating the group"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+    addr0 = dev[0].p2p_dev_addr()
+    dev[1].p2p_listen()
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.done = False
+            self.peer_group_added = False
+            self.peer_group_removed = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceFound")
+            self.add_signal(self.goNegotiationSuccess,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GONegotiationSuccess",
+                            byte_arrays=True)
+            self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupStarted")
+            self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupFinished")
+            self.add_signal(self.propertiesChanged, dbus.PROPERTIES_IFACE,
+                            "PropertiesChanged")
+            self.loop.run()
+            return self
+
+        def deviceFound(self, path):
+            logger.debug("deviceFound: path=%s" % path)
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            args = { 'peer': path, 'wps_method': 'keypad', 'pin': '12345670',
+                     'go_intent': 0 }
+            p2p.Connect(args)
+
+            ev = dev1.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+            if ev is None:
+                raise Exception("Timeout while waiting for GO Neg Request")
+            dev1.global_request("P2P_CONNECT " + addr0 + " 12345670 display go_intent=15")
+            ev = dev1.wait_global_event(["P2P-GROUP-STARTED"], timeout=15);
+            if ev is None:
+                raise Exception("Group formation timed out")
+            self.sta_group_ev = ev
+
+        def goNegotiationSuccess(self, properties):
+            logger.debug("goNegotiationSuccess: properties=%s" % str(properties))
+
+        def groupStarted(self, properties):
+            logger.debug("groupStarted: " + str(properties))
+            g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                      properties['interface_object'])
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            dev1.group_form_result(self.sta_group_ev)
+            dev1.remove_group()
+
+        def groupFinished(self, properties):
+            logger.debug("groupFinished: " + str(properties))
+            self.done = True
+
+        def propertiesChanged(self, interface_name, changed_properties,
+                              invalidated_properties):
+            logger.debug("propertiesChanged: interface_name=%s changed_properties=%s invalidated_properties=%s" % (interface_name, str(changed_properties), str(invalidated_properties)))
+            if interface_name != WPAS_DBUS_P2P_PEER:
+                return
+            if "Groups" not in changed_properties:
+                return
+            if len(changed_properties["Groups"]) > 0:
+                self.peer_group_added = True
+            if len(changed_properties["Groups"]) == 0 and self.peer_group_added:
+                self.peer_group_removed = True
+                self.loop.quit()
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            p2p.Find(dbus.Dictionary({'DiscoveryType': 'social'}))
+            return False
+
+        def success(self):
+            return self.done and self.peer_group_added and self.peer_group_removed
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_group_idle_timeout(dev, apdev):
+    """D-Bus P2P group removal on idle timeout"""
+    try:
+        dev[0].global_request("SET p2p_group_idle 1")
+        _test_dbus_p2p_group_idle_timeout(dev, apdev)
+    finally:
+        dev[0].global_request("SET p2p_group_idle 0")
+
+def _test_dbus_p2p_group_idle_timeout(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+    addr0 = dev[0].p2p_dev_addr()
+    dev[1].p2p_listen()
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.done = False
+            self.peer_group_added = False
+            self.peer_group_removed = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceFound")
+            self.add_signal(self.goNegotiationSuccess,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GONegotiationSuccess",
+                            byte_arrays=True)
+            self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupStarted")
+            self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupFinished")
+            self.add_signal(self.propertiesChanged, dbus.PROPERTIES_IFACE,
+                            "PropertiesChanged")
+            self.loop.run()
+            return self
+
+        def deviceFound(self, path):
+            logger.debug("deviceFound: path=%s" % path)
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            args = { 'peer': path, 'wps_method': 'keypad', 'pin': '12345670',
+                     'go_intent': 0 }
+            p2p.Connect(args)
+
+            ev = dev1.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+            if ev is None:
+                raise Exception("Timeout while waiting for GO Neg Request")
+            dev1.global_request("P2P_CONNECT " + addr0 + " 12345670 display go_intent=15")
+            ev = dev1.wait_global_event(["P2P-GROUP-STARTED"], timeout=15);
+            if ev is None:
+                raise Exception("Group formation timed out")
+            self.sta_group_ev = ev
+
+        def goNegotiationSuccess(self, properties):
+            logger.debug("goNegotiationSuccess: properties=%s" % str(properties))
+
+        def groupStarted(self, properties):
+            logger.debug("groupStarted: " + str(properties))
+            g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                      properties['interface_object'])
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            dev1.group_form_result(self.sta_group_ev)
+            ifaddr = dev1.group_request("STA-FIRST").splitlines()[0]
+            # Force disassociation with different reason code so that the
+            # P2P Client using D-Bus does not get normal group termination event
+            # from the GO.
+            dev1.group_request("DEAUTHENTICATE " + ifaddr + " reason=0 test=0")
+            dev1.remove_group()
+
+        def groupFinished(self, properties):
+            logger.debug("groupFinished: " + str(properties))
+            self.done = True
+
+        def propertiesChanged(self, interface_name, changed_properties,
+                              invalidated_properties):
+            logger.debug("propertiesChanged: interface_name=%s changed_properties=%s invalidated_properties=%s" % (interface_name, str(changed_properties), str(invalidated_properties)))
+            if interface_name != WPAS_DBUS_P2P_PEER:
+                return
+            if "Groups" not in changed_properties:
+                return
+            if len(changed_properties["Groups"]) > 0:
+                self.peer_group_added = True
+            if len(changed_properties["Groups"]) == 0:
+                self.peer_group_removed = True
+                self.loop.quit()
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            p2p.Find(dbus.Dictionary({'DiscoveryType': 'social'}))
+            return False
+
+        def success(self):
+            return self.done and self.peer_group_added and self.peer_group_removed
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_wps_failure(dev, apdev):
+    """D-Bus P2P WPS failure"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+    addr0 = dev[0].p2p_dev_addr()
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.wps_failed = False
+            self.formation_failure = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.goNegotiationRequest,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GONegotiationRequest",
+                            byte_arrays=True)
+            self.add_signal(self.goNegotiationSuccess,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GONegotiationSuccess",
+                            byte_arrays=True)
+            self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupStarted")
+            self.add_signal(self.wpsFailed, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "WpsFailed")
+            self.add_signal(self.groupFormationFailure,
+                            WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupFormationFailure")
+            self.loop.run()
+            return self
+
+        def goNegotiationRequest(self, path, dev_passwd_id, go_intent=0):
+            logger.debug("goNegotiationRequest: path=%s dev_passwd_id=%d go_intent=%d" % (path, dev_passwd_id, go_intent))
+            if dev_passwd_id != 1:
+                raise Exception("Unexpected dev_passwd_id=%d" % dev_passwd_id)
+            args = { 'peer': path, 'wps_method': 'display', 'pin': '12345670',
+                     'go_intent': 15 }
+            p2p.Connect(args)
+
+        def goNegotiationSuccess(self, properties):
+            logger.debug("goNegotiationSuccess: properties=%s" % str(properties))
+
+        def groupStarted(self, properties):
+            logger.debug("groupStarted: " + str(properties))
+            raise Exception("Unexpected GroupStarted")
+
+        def wpsFailed(self, name, args):
+            logger.debug("wpsFailed - name=%s args=%s" % (name, str(args)))
+            self.wps_failed = True
+            if self.formation_failure:
+                self.loop.quit()
+
+        def groupFormationFailure(self, reason):
+            logger.debug("groupFormationFailure - reason=%s" % reason)
+            self.formation_failure = True
+            if self.wps_failed:
+                self.loop.quit()
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            p2p.Listen(10)
+            dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+            if not dev1.discover_peer(addr0):
+                raise Exception("Peer not found")
+            dev1.global_request("P2P_CONNECT " + addr0 + " 87654321 enter")
+            return False
+
+        def success(self):
+            return self.wps_failed and self.formation_failure
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_two_groups(dev, apdev):
+    """D-Bus P2P with two concurrent groups"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+    dev[0].request("SET p2p_no_group_iface 0")
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    addr2 = dev[2].p2p_dev_addr()
+    dev[1].p2p_start_go(freq=2412)
+    dev1_group_ifname = dev[1].group_ifname
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.done = False
+            self.peer = None
+            self.go = None
+            self.group1 = None
+            self.group2 = None
+            self.groups_removed = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.propertiesChanged, dbus.PROPERTIES_IFACE,
+                            "PropertiesChanged", byte_arrays=True)
+            self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceFound")
+            self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupStarted")
+            self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "GroupFinished")
+            self.add_signal(self.peerJoined, WPAS_DBUS_GROUP,
+                            "PeerJoined")
+            self.loop.run()
+            return self
+
+        def propertiesChanged(self, interface_name, changed_properties,
+                              invalidated_properties):
+            logger.debug("propertiesChanged: interface_name=%s changed_properties=%s invalidated_properties=%s" % (interface_name, str(changed_properties), str(invalidated_properties)))
+
+        def deviceFound(self, path):
+            logger.debug("deviceFound: path=%s" % path)
+            if addr2.replace(':','') in path:
+                self.peer = path
+            elif addr1.replace(':','') in path:
+                self.go = path
+            if self.go and not self.group1:
+                logger.info("Join the group")
+                p2p.StopFind()
+                pin = '12345670'
+                dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+                dev1.group_ifname = dev1_group_ifname
+                dev1.group_request("WPS_PIN any " + pin)
+                args = { 'peer': self.go,
+                         'join': True,
+                         'wps_method': 'pin',
+                         'pin': pin,
+                         'frequency': 2412 }
+                p2p.Connect(args)
+
+        def groupStarted(self, properties):
+            logger.debug("groupStarted: " + str(properties))
+            prop = if_obj.GetAll(WPAS_DBUS_IFACE_P2PDEVICE,
+                                 dbus_interface=dbus.PROPERTIES_IFACE)
+            logger.debug("p2pdevice properties: " + str(prop))
+
+            g_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                   properties['group_object'])
+            res = g_obj.GetAll(WPAS_DBUS_GROUP,
+                               dbus_interface=dbus.PROPERTIES_IFACE,
+                               byte_arrays=True)
+            logger.debug("Group properties: " + str(res))
+
+            if not self.group1:
+                self.group1 = properties['group_object']
+                self.group1iface = properties['interface_object']
+                self.g1_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                                self.group1iface)
+
+                logger.info("Start autonomous GO")
+                params = dbus.Dictionary({ 'frequency': 2412 })
+                p2p.GroupAdd(params)
+            elif not self.group2:
+                self.group2 = properties['group_object']
+                self.group2iface = properties['interface_object']
+                self.g2_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+                                                self.group2iface)
+                self.g2_bssid = res['BSSID']
+
+            if self.group1 and self.group2:
+                logger.info("Authorize peer to join the group")
+                a2 = binascii.unhexlify(addr2.replace(':',''))
+                params = { 'Role': 'enrollee',
+                           'P2PDeviceAddress': dbus.ByteArray(a2),
+                           'Bssid': dbus.ByteArray(a2),
+                           'Type': 'pin',
+                           'Pin': '12345670' }
+                g_wps = dbus.Interface(self.g2_if_obj, WPAS_DBUS_IFACE_WPS)
+                g_wps.Start(params)
+
+                bssid = ':'.join([binascii.hexlify(l) for l in self.g2_bssid])
+                dev2 = WpaSupplicant('wlan2', '/tmp/wpas-wlan2')
+                dev2.scan_for_bss(bssid, freq=2412)
+                dev2.global_request("P2P_CONNECT " + bssid + " 12345670 join freq=2412")
+                ev = dev2.wait_global_event(["P2P-GROUP-STARTED"], timeout=15);
+                if ev is None:
+                    raise Exception("Group join timed out")
+                self.dev2_group_ev = ev
+
+        def groupFinished(self, properties):
+            logger.debug("groupFinished: " + str(properties))
+
+            if self.group1 == properties['group_object']:
+                self.group1 = None
+            elif self.group2 == properties['group_object']:
+                self.group2 = None
+
+            if not self.group1 and not self.group2:
+                self.done = True
+                self.loop.quit()
+
+        def peerJoined(self, peer):
+            logger.debug("peerJoined: " + peer)
+            if self.groups_removed:
+                return
+            self.check_results()
+
+            dev2 = WpaSupplicant('wlan2', '/tmp/wpas-wlan2')
+            dev2.group_form_result(self.dev2_group_ev)
+            dev2.remove_group()
+
+            logger.info("Disconnect group2")
+            group_p2p = dbus.Interface(self.g2_if_obj,
+                                       WPAS_DBUS_IFACE_P2PDEVICE)
+            group_p2p.Disconnect()
+
+            logger.info("Disconnect group1")
+            group_p2p = dbus.Interface(self.g1_if_obj,
+                                       WPAS_DBUS_IFACE_P2PDEVICE)
+            group_p2p.Disconnect()
+            self.groups_removed = True
+
+        def check_results(self):
+            logger.info("Check results with two concurrent groups in operation")
+
+            g1_obj = bus.get_object(WPAS_DBUS_SERVICE, self.group1)
+            res1 = g1_obj.GetAll(WPAS_DBUS_GROUP,
+                                 dbus_interface=dbus.PROPERTIES_IFACE,
+                                 byte_arrays=True)
+
+            g2_obj = bus.get_object(WPAS_DBUS_SERVICE, self.group2)
+            res2 = g2_obj.GetAll(WPAS_DBUS_GROUP,
+                                 dbus_interface=dbus.PROPERTIES_IFACE,
+                                 byte_arrays=True)
+
+            logger.info("group1 = " + self.group1)
+            logger.debug("Group properties: " + str(res1))
+
+            logger.info("group2 = " + self.group2)
+            logger.debug("Group properties: " + str(res2))
+
+            prop = if_obj.GetAll(WPAS_DBUS_IFACE_P2PDEVICE,
+                                 dbus_interface=dbus.PROPERTIES_IFACE)
+            logger.debug("p2pdevice properties: " + str(prop))
+
+            if res1['Role'] != 'client':
+                raise Exception("Group1 role reported incorrectly: " + res1['Role'])
+            if res2['Role'] != 'GO':
+                raise Exception("Group2 role reported incorrectly: " + res2['Role'])
+            if prop['Role'] != 'device':
+                raise Exception("p2pdevice role reported incorrectly: " + prop['Role'])
+
+            if len(res2['Members']) != 1:
+                   raise Exception("Unexpected Members value for group 2")
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            p2p.Find(dbus.Dictionary({'DiscoveryType': 'social'}))
+            return False
+
+        def success(self):
+            return self.done
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+    dev[1].remove_group()
+
+def test_dbus_p2p_cancel(dev, apdev):
+    """D-Bus P2P Cancel"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+    try:
+        p2p.Cancel()
+        raise Exception("Unexpected p2p.Cancel() success")
+    except dbus.exceptions.DBusException, e:
+        pass
+
+    addr0 = dev[0].p2p_dev_addr()
+    dev[1].p2p_listen()
+
+    class TestDbusP2p(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.done = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_test)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+                            "DeviceFound")
+            self.loop.run()
+            return self
+
+        def deviceFound(self, path):
+            logger.debug("deviceFound: path=%s" % path)
+            args = { 'peer': path, 'wps_method': 'keypad', 'pin': '12345670',
+                     'go_intent': 0 }
+            p2p.Connect(args)
+            p2p.Cancel()
+            self.done = True
+            self.loop.quit()
+
+        def run_test(self, *args):
+            logger.debug("run_test")
+            p2p.Find(dbus.Dictionary({'DiscoveryType': 'social'}))
+            return False
+
+        def success(self):
+            return self.done
+
+    with TestDbusP2p(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_introspect(dev, apdev):
+    """D-Bus introspection"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    res = if_obj.Introspect(WPAS_DBUS_IFACE,
+                            dbus_interface=dbus.INTROSPECTABLE_IFACE)
+    logger.info("Initial Introspect: " + str(res))
+    if res is None or "Introspectable" not in res or "GroupStarted" not in res:
+        raise Exception("Unexpected initial Introspect response: " + str(res))
+
+    with alloc_fail(dev[0], 1, "wpa_dbus_introspect"):
+        res2 = if_obj.Introspect(WPAS_DBUS_IFACE,
+                                 dbus_interface=dbus.INTROSPECTABLE_IFACE)
+        logger.info("Introspect: " + str(res2))
+        if res2 is not None:
+            raise Exception("Unexpected Introspect response")
+
+    with alloc_fail(dev[0], 1, "=add_interface;wpa_dbus_introspect"):
+        res2 = if_obj.Introspect(WPAS_DBUS_IFACE,
+                                 dbus_interface=dbus.INTROSPECTABLE_IFACE)
+        logger.info("Introspect: " + str(res2))
+        if res2 is None:
+            raise Exception("No Introspect response")
+        if len(res2) >= len(res):
+            raise Exception("Unexpected Introspect response")
+
+    with alloc_fail(dev[0], 1, "wpabuf_alloc;add_interface;wpa_dbus_introspect"):
+        res2 = if_obj.Introspect(WPAS_DBUS_IFACE,
+                                 dbus_interface=dbus.INTROSPECTABLE_IFACE)
+        logger.info("Introspect: " + str(res2))
+        if res2 is None:
+            raise Exception("No Introspect response")
+        if len(res2) >= len(res):
+            raise Exception("Unexpected Introspect response")
+
+    with alloc_fail(dev[0], 2, "=add_interface;wpa_dbus_introspect"):
+        res2 = if_obj.Introspect(WPAS_DBUS_IFACE,
+                                 dbus_interface=dbus.INTROSPECTABLE_IFACE)
+        logger.info("Introspect: " + str(res2))
+        if res2 is None:
+            raise Exception("No Introspect response")
+        if len(res2) >= len(res):
+            raise Exception("Unexpected Introspect response")
+
+def test_dbus_ap(dev, apdev):
+    """D-Bus AddNetwork for AP mode"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+
+    class TestDbusConnect(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.started = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_connect)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.networkAdded, WPAS_DBUS_IFACE, "NetworkAdded")
+            self.add_signal(self.networkSelected, WPAS_DBUS_IFACE,
+                            "NetworkSelected")
+            self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+                            "PropertiesChanged")
+            self.loop.run()
+            return self
+
+        def networkAdded(self, network, properties):
+            logger.debug("networkAdded: %s" % str(network))
+            logger.debug(str(properties))
+
+        def networkSelected(self, network):
+            logger.debug("networkSelected: %s" % str(network))
+            self.network_selected = True
+
+        def propertiesChanged(self, properties):
+            logger.debug("propertiesChanged: %s" % str(properties))
+            if 'State' in properties and properties['State'] == "completed":
+                self.started = True
+                self.loop.quit()
+
+        def run_connect(self, *args):
+            logger.debug("run_connect")
+            args = dbus.Dictionary({ 'ssid': ssid,
+                                     'key_mgmt': 'WPA-PSK',
+                                     'psk': passphrase,
+                                     'mode': 2,
+                                     'frequency': 2412 },
+                                   signature='sv')
+            self.netw = iface.AddNetwork(args)
+            iface.SelectNetwork(self.netw)
+            return False
+
+        def success(self):
+            return self.started
+
+    with TestDbusConnect(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+        dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+
+def test_dbus_connect_wpa_eap(dev, apdev):
+    """D-Bus AddNetwork and connection with WPA+WPA2-Enterprise AP"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    ssid = "test-wpa-eap"
+    params = hostapd.wpa_eap_params(ssid=ssid)
+    params["wpa"] = "3"
+    params["rsn_pairwise"] = "CCMP"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    class TestDbusConnect(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.done = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_connect)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+                            "PropertiesChanged")
+            self.add_signal(self.eap, WPAS_DBUS_IFACE, "EAP")
+            self.loop.run()
+            return self
+
+        def propertiesChanged(self, properties):
+            logger.debug("propertiesChanged: %s" % str(properties))
+            if 'State' in properties and properties['State'] == "completed":
+                self.done = True
+                self.loop.quit()
+
+        def eap(self, status, parameter):
+            logger.debug("EAP: status=%s parameter=%s" % (status, parameter))
+
+        def run_connect(self, *args):
+            logger.debug("run_connect")
+            args = dbus.Dictionary({ 'ssid': ssid,
+                                     'key_mgmt': 'WPA-EAP',
+                                     'eap': 'PEAP',
+                                     'identity': 'user',
+                                     'password': 'password',
+                                     'ca_cert': 'auth_serv/ca.pem',
+                                     'phase2': 'auth=MSCHAPV2',
+                                     'scan_freq': 2412 },
+                                   signature='sv')
+            self.netw = iface.AddNetwork(args)
+            iface.SelectNetwork(self.netw)
+            return False
+
+        def success(self):
+            return self.done
+
+    with TestDbusConnect(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_ap_scan_2_ap_mode_scan(dev, apdev):
+    """AP_SCAN 2 AP mode and D-Bus Scan()"""
+    try:
+        _test_dbus_ap_scan_2_ap_mode_scan(dev, apdev)
+    finally:
+        dev[0].request("AP_SCAN 1")
+
+def _test_dbus_ap_scan_2_ap_mode_scan(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    if "OK" not in dev[0].request("AP_SCAN 2"):
+        raise Exception("Failed to set AP_SCAN 2")
+
+    id = dev[0].add_network()
+    dev[0].set_network(id, "mode", "2")
+    dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+    dev[0].set_network(id, "key_mgmt", "NONE")
+    dev[0].set_network(id, "frequency", "2412")
+    dev[0].set_network(id, "scan_freq", "2412")
+    dev[0].set_network(id, "disabled", "0")
+    dev[0].select_network(id)
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=5)
+    if ev is None:
+        raise Exception("AP failed to start")
+
+    with fail_test(dev[0], 1, "wpa_driver_nl80211_scan"):
+        iface.Scan({'Type': 'active',
+                    'AllowRoam': True,
+                    'Channels': [(dbus.UInt32(2412), dbus.UInt32(20))]})
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED",
+                                "AP-DISABLED"], timeout=5)
+        if ev is None:
+            raise Exception("CTRL-EVENT-SCAN-FAILED not seen")
+        if "AP-DISABLED" in ev:
+            raise Exception("Unexpected AP-DISABLED event")
+        if "retry=1" in ev:
+            # Wait for the retry to scan happen
+            ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED",
+                                    "AP-DISABLED"], timeout=5)
+            if ev is None:
+                raise Exception("CTRL-EVENT-SCAN-FAILED not seen - retry")
+            if "AP-DISABLED" in ev:
+                raise Exception("Unexpected AP-DISABLED event - retry")
+
+    dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+    dev[1].request("DISCONNECT")
+    dev[1].wait_disconnected()
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
diff --git a/hostap/tests/hwsim/test_dbus_old.py b/hostap/tests/hwsim/test_dbus_old.py
new file mode 100644
index 0000000..c7c7e19
--- /dev/null
+++ b/hostap/tests/hwsim/test_dbus_old.py
@@ -0,0 +1,854 @@
+# wpa_supplicant D-Bus old interface tests
+# Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+
+try:
+    import gobject
+    import dbus
+    dbus_imported = True
+except ImportError:
+    dbus_imported = False
+
+import hostapd
+from utils import HwsimSkip
+from test_dbus import TestDbus, alloc_fail_dbus, start_ap
+
+WPAS_DBUS_OLD_SERVICE = "fi.epitest.hostap.WPASupplicant"
+WPAS_DBUS_OLD_PATH = "/fi/epitest/hostap/WPASupplicant"
+WPAS_DBUS_OLD_IFACE = "fi.epitest.hostap.WPASupplicant.Interface"
+WPAS_DBUS_OLD_BSSID = "fi.epitest.hostap.WPASupplicant.BSSID"
+WPAS_DBUS_OLD_NETWORK = "fi.epitest.hostap.WPASupplicant.Network"
+
+def prepare_dbus(dev):
+    if not dbus_imported:
+        raise HwsimSkip("No dbus module available")
+    try:
+        from dbus.mainloop.glib import DBusGMainLoop
+        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+        bus = dbus.SystemBus()
+        wpas_obj = bus.get_object(WPAS_DBUS_OLD_SERVICE, WPAS_DBUS_OLD_PATH)
+        wpas = dbus.Interface(wpas_obj, WPAS_DBUS_OLD_SERVICE)
+        path = wpas.getInterface(dev.ifname)
+        if_obj = bus.get_object(WPAS_DBUS_OLD_SERVICE, path)
+        return (bus,wpas_obj,path,if_obj)
+    except Exception, e:
+        raise HwsimSkip("Could not connect to D-Bus: %s" % e)
+
+class TestDbusOldWps(TestDbus):
+    def __init__(self, bus):
+        TestDbus.__init__(self, bus)
+        self.event_ok = False
+
+    def __enter__(self):
+        gobject.timeout_add(1, self.run_wps)
+        gobject.timeout_add(15000, self.timeout)
+        self.add_signal(self.wpsCred, WPAS_DBUS_OLD_IFACE, "WpsCred")
+        self.loop.run()
+        return self
+
+    def wpsCred(self, cred):
+        logger.debug("wpsCred: " + str(cred))
+        self.event_ok = True
+        self.loop.quit()
+
+    def success(self):
+        return self.event_ok
+
+def test_dbus_old(dev, apdev):
+    """The old D-Bus interface"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    res = if_obj.capabilities(dbus_interface=WPAS_DBUS_OLD_IFACE)
+    logger.debug("capabilities(): " + str(res))
+    if 'auth_alg' not in res or "OPEN" not in res['auth_alg']:
+        raise Exception("Unexpected capabilities")
+    res2 = if_obj.capabilities(dbus.Boolean(True),
+                               dbus_interface=WPAS_DBUS_OLD_IFACE)
+    logger.debug("capabilities(strict): " + str(res2))
+    res = if_obj.state(dbus_interface=WPAS_DBUS_OLD_IFACE)
+    logger.debug("State: " + res)
+
+    res = if_obj.scanning(dbus_interface=WPAS_DBUS_OLD_IFACE)
+    if res != 0:
+        raise Exception("Unexpected scanning: " + str(res))
+
+    if_obj.setAPScan(dbus.UInt32(1), dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+    for t in [ dbus.UInt32(123), "foo" ]:
+        try:
+            if_obj.setAPScan(t, dbus_interface=WPAS_DBUS_OLD_IFACE)
+            raise Exception("Invalid setAPScan() accepted")
+        except dbus.exceptions.DBusException, e:
+            if "InvalidOptions" not in str(e):
+                raise Exception("Unexpected error message for invalid setAPScan: " + str(e))
+
+    for p in [ path + "/Networks/12345",
+               path + "/Networks/foo" ]:
+        obj = bus.get_object(WPAS_DBUS_OLD_SERVICE, p)
+        try:
+            obj.disable(dbus_interface=WPAS_DBUS_OLD_NETWORK)
+            raise Exception("Invalid disable() accepted")
+        except dbus.exceptions.DBusException, e:
+            if "InvalidNetwork" not in str(e):
+                raise Exception("Unexpected error message for invalid disable: " + str(e))
+
+    for p in [ path + "/BSSIDs/foo",
+               path + "/BSSIDs/001122334455"]:
+        obj = bus.get_object(WPAS_DBUS_OLD_SERVICE, p)
+        try:
+            obj.properties(dbus_interface=WPAS_DBUS_OLD_BSSID)
+            raise Exception("Invalid properties() accepted")
+        except dbus.exceptions.DBusException, e:
+            if "InvalidBSSID" not in str(e):
+                raise Exception("Unexpected error message for invalid properties: " + str(e))
+
+def test_dbus_old_scan(dev, apdev):
+    """The old D-Bus interface - scanning"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params['wpa'] = '3'
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    class TestDbusScan(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.scan_completed = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_scan)
+            gobject.timeout_add(7000, self.timeout)
+            self.add_signal(self.scanDone, WPAS_DBUS_OLD_IFACE,
+                            "ScanResultsAvailable")
+            self.loop.run()
+            return self
+
+        def scanDone(self):
+            logger.debug("scanDone")
+            self.scan_completed = True
+            self.loop.quit()
+
+        def run_scan(self, *args):
+            logger.debug("run_scan")
+            if not if_obj.scan(dbus_interface=WPAS_DBUS_OLD_IFACE):
+                raise Exception("Failed to trigger scan")
+            return False
+
+        def success(self):
+            return self.scan_completed
+
+    with TestDbusScan(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+    res = if_obj.scanResults(dbus_interface=WPAS_DBUS_OLD_IFACE)
+    if len(res) != 2:
+        raise Exception("Unexpected number of scan results: " + str(res))
+    for i in range(2):
+        logger.debug("Scan result BSS path: " + res[i])
+        bss_obj = bus.get_object(WPAS_DBUS_OLD_SERVICE, res[i])
+        bss = bss_obj.properties(dbus_interface=WPAS_DBUS_OLD_BSSID,
+                                 byte_arrays=True)
+        logger.debug("BSS: " + str(bss))
+
+    obj = bus.get_object(WPAS_DBUS_OLD_SERVICE, res[0])
+    try:
+        bss_obj.properties2(dbus_interface=WPAS_DBUS_OLD_BSSID)
+        raise Exception("Unknown BSSID method accepted")
+    except Exception, e:
+        logger.debug("Unknown BSSID method exception: " + str(e))
+
+    if not if_obj.flush(0, dbus_interface=WPAS_DBUS_OLD_IFACE):
+        raise Exception("Failed to issue flush(0)")
+    res = if_obj.scanResults(dbus_interface=WPAS_DBUS_OLD_IFACE)
+    if len(res) != 0:
+        raise Exception("Unexpected BSS entry after flush")
+    if not if_obj.flush(1, dbus_interface=WPAS_DBUS_OLD_IFACE):
+        raise Exception("Failed to issue flush(1)")
+    try:
+        if_obj.flush("foo", dbus_interface=WPAS_DBUS_OLD_IFACE)
+        raise Exception("Invalid flush arguments accepted")
+    except dbus.exceptions.DBusException, e:
+        if not str(e).startswith("fi.epitest.hostap.WPASupplicant.InvalidOptions"):
+            raise Exception("Unexpected error message for invalid flush: " + str(e))
+    try:
+        bss_obj.properties(dbus_interface=WPAS_DBUS_OLD_BSSID,
+                           byte_arrays=True)
+    except dbus.exceptions.DBusException, e:
+        if not str(e).startswith("fi.epitest.hostap.WPASupplicant.Interface.InvalidBSSID"):
+            raise Exception("Unexpected error message for invalid BSS: " + str(e))
+
+def test_dbus_old_debug(dev, apdev):
+    """The old D-Bus interface - debug"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wpas = dbus.Interface(wpas_obj, WPAS_DBUS_OLD_SERVICE)
+
+    try:
+        wpas.setDebugParams(123)
+        raise Exception("Invalid setDebugParams accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidOptions" not in str(e):
+            raise Exception("Unexpected error message for invalid setDebugParam: " + str(e))
+
+    try:
+        wpas.setDebugParams(123, True, True)
+        raise Exception("Invalid setDebugParams accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidOptions" not in str(e):
+            raise Exception("Unexpected error message for invalid setDebugParam: " + str(e))
+
+    wpas.setDebugParams(1, True, True)
+    dev[0].request("LOG_LEVEL MSGDUMP")
+
+def test_dbus_old_smartcard(dev, apdev):
+    """The old D-Bus interface - smartcard"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    params = dbus.Dictionary(signature='sv')
+    if_obj.setSmartcardModules(params, dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+    params = dbus.Dictionary({ 'opensc_engine_path': "foobar1",
+                               'pkcs11_engine_path': "foobar2",
+                               'pkcs11_module_path': "foobar3",
+                               'foo': 'bar' },
+                             signature='sv')
+    params2 = dbus.Dictionary({ 'pkcs11_engine_path': "foobar2",
+                                'foo': 'bar' },
+                              signature='sv')
+    params3 = dbus.Dictionary({ 'pkcs11_module_path': "foobar3",
+                                'foo2': 'bar' },
+                              signature='sv')
+    params4 = dbus.Dictionary({ 'opensc_engine_path': "foobar4",
+                                'foo3': 'bar' },
+                              signature='sv')
+    tests = [ 1, params, params2, params3, params4 ]
+    for t in tests:
+        try:
+            if_obj.setSmartcardModules(t, dbus_interface=WPAS_DBUS_OLD_IFACE)
+            raise Exception("Invalid setSmartcardModules accepted: " + str(t))
+        except dbus.exceptions.DBusException, e:
+            if not str(e).startswith("fi.epitest.hostap.WPASupplicant.InvalidOptions"):
+                raise Exception("Unexpected error message for invalid setSmartcardModules(%s): %s" % (str(t), str(e)))
+
+def test_dbus_old_smartcard_oom(dev, apdev):
+    """The old D-Bus interface - smartcard (OOM)"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    for arg in [ 'opensc_engine_path', 'pkcs11_engine_path', 'pkcs11_module_path' ]:
+        with alloc_fail_dbus(dev[0], 1,
+                             "=wpas_dbus_iface_set_smartcard_modules",
+                             "setSmartcardModules",
+                             "InvalidOptions"):
+            params = dbus.Dictionary({ arg : "foo", }, signature='sv')
+            if_obj.setSmartcardModules(params,
+                                       dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+    with alloc_fail_dbus(dev[0], 1, "=_wpa_dbus_dict_fill_value_from_variant;wpas_dbus_iface_set_smartcard_modules",
+                         "setSmartcardModules", "InvalidOptions"):
+        params = dbus.Dictionary({ arg : "foo", }, signature='sv')
+        if_obj.setSmartcardModules(params, dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+def test_dbus_old_interface(dev, apdev):
+    """The old D-Bus interface - interface get/add/remove"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wpas = dbus.Interface(wpas_obj, WPAS_DBUS_OLD_SERVICE)
+
+    tests = [ (123, "InvalidOptions"),
+              ("foo", "InvalidInterface") ]
+    for (ifname,err) in tests:
+        try:
+            wpas.getInterface(ifname)
+            raise Exception("Invalid getInterface accepted")
+        except dbus.exceptions.DBusException, e:
+            if err not in str(e):
+                raise Exception("Unexpected error message for invalid getInterface: " + str(e))
+
+    params = dbus.Dictionary({ 'driver': 'none' }, signature='sv')
+    wpas.addInterface("lo", params)
+    path = wpas.getInterface("lo")
+    logger.debug("New interface path: " + str(path))
+    wpas.removeInterface(path)
+    try:
+        wpas.removeInterface(path)
+        raise Exception("Invalid removeInterface() accepted")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidInterface" not in str(e):
+            raise Exception("Unexpected error message for invalid removeInterface: " + str(e))
+
+    params1 = dbus.Dictionary({ 'driver': 'foo',
+                                'driver-params': 'foo',
+                                'config-file': 'foo',
+                                'bridge-ifname': 'foo' },
+                              signature='sv')
+    params2 = dbus.Dictionary({ 'foo': 'bar' }, signature='sv')
+    tests = [ (123, None, "InvalidOptions"),
+              ("", None, "InvalidOptions"),
+              ("foo", None, "AddError"),
+              ("foo", params1, "AddError"),
+              ("foo", params2, "InvalidOptions"),
+              ("foo", 1234, "InvalidOptions"),
+              (dev[0].ifname, None, "ExistsError" ) ]
+    for (ifname,params,err) in tests:
+        try:
+            if params is None:
+                wpas.addInterface(ifname)
+            else:
+                wpas.addInterface(ifname, params)
+            raise Exception("Invalid addInterface accepted: " + str(params))
+        except dbus.exceptions.DBusException, e:
+            if err not in str(e):
+                raise Exception("Unexpected error message for invalid addInterface(%s): %s" % (str(params), str(e)))
+
+    try:
+        wpas.removeInterface(123)
+        raise Exception("Invalid removeInterface accepted")
+    except dbus.exceptions.DBusException, e:
+        if not str(e).startswith("fi.epitest.hostap.WPASupplicant.InvalidOptions"):
+            raise Exception("Unexpected error message for invalid removeInterface: " + str(e))
+
+def test_dbus_old_interface_oom(dev, apdev):
+    """The old D-Bus interface - interface get/add/remove (OOM)"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    wpas = dbus.Interface(wpas_obj, WPAS_DBUS_OLD_SERVICE)
+
+    with alloc_fail_dbus(dev[0], 1, "=_wpa_dbus_dict_fill_value_from_variant;wpas_dbus_global_add_interface",
+                         "addInterface", "InvalidOptions"):
+        params = dbus.Dictionary({ 'driver': 'none' }, signature='sv')
+        wpas.addInterface("lo", params)
+
+    for arg in [ "driver", "driver-params", "config-file", "bridge-ifname" ]:
+        with alloc_fail_dbus(dev[0], 1, "=wpas_dbus_global_add_interface",
+                             "addInterface", "InvalidOptions"):
+            params = dbus.Dictionary({ arg: 'foo' }, signature='sv')
+            wpas.addInterface("lo", params)
+
+def test_dbus_old_blob(dev, apdev):
+    """The old D-Bus interface - blob operations"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    param1 = dbus.Dictionary({ 'blob3': 123 }, signature='sv')
+    param2 = dbus.Dictionary({ 'blob3': "foo" })
+    param3 = dbus.Dictionary({ '': dbus.ByteArray([ 1, 2 ]) },
+                             signature='sv')
+    tests = [ (1, "InvalidOptions"),
+              (param1, "InvalidOptions"),
+              (param2, "InvalidOptions"),
+              (param3, "InvalidOptions") ]
+    for (arg,err) in tests:
+        try:
+            if_obj.setBlobs(arg, dbus_interface=WPAS_DBUS_OLD_IFACE)
+            raise Exception("Invalid setBlobs() accepted: " + str(arg))
+        except dbus.exceptions.DBusException, e:
+            logger.debug("setBlobs(%s): %s" % (str(arg), str(e)))
+            if err not in str(e):
+                raise Exception("Unexpected error message for invalid setBlobs: " + str(e))
+
+    tests = [ (["foo"], "RemoveError: Error removing blob"),
+              ([""], "RemoveError: Invalid blob name"),
+              ([1], "InvalidOptions"),
+              ("foo", "InvalidOptions") ]
+    for (arg,err) in tests:
+        try:
+            if_obj.removeBlobs(arg, dbus_interface=WPAS_DBUS_OLD_IFACE)
+            raise Exception("Invalid removeBlobs() accepted: " + str(arg))
+        except dbus.exceptions.DBusException, e:
+            logger.debug("removeBlobs(%s): %s" % (str(arg), str(e)))
+            if err not in str(e):
+                raise Exception("Unexpected error message for invalid removeBlobs: " + str(e))
+
+    blobs = dbus.Dictionary({ 'blob1': dbus.ByteArray([ 1, 2, 3 ]),
+                              'blob2': dbus.ByteArray([ 1, 2 ]) },
+                            signature='sv')
+    if_obj.setBlobs(blobs, dbus_interface=WPAS_DBUS_OLD_IFACE)
+    if_obj.removeBlobs(['blob1', 'blob2'], dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+def test_dbus_old_blob_oom(dev, apdev):
+    """The old D-Bus interface - blob operations (OOM)"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    blobs = dbus.Dictionary({ 'blob1': dbus.ByteArray([ 1, 2, 3 ]),
+                              'blob2': dbus.ByteArray([ 1, 2 ]) },
+                            signature='sv')
+
+    with alloc_fail_dbus(dev[0], 1, "=wpas_dbus_iface_set_blobs", "setBlobs",
+                         "AddError: Not enough memory to add blob"):
+        if_obj.setBlobs(blobs, dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+    with alloc_fail_dbus(dev[0], 2, "=wpas_dbus_iface_set_blobs", "setBlobs",
+                         "AddError: Not enough memory to add blob data"):
+        if_obj.setBlobs(blobs, dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+    with alloc_fail_dbus(dev[0], 3, "=wpas_dbus_iface_set_blobs", "setBlobs",
+                         "AddError: Error adding blob"):
+        if_obj.setBlobs(blobs, dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+    with alloc_fail_dbus(dev[0], 1, "=wpas_dbus_decompose_object_path;wpas_iface_message_handler",
+                         "setBlobs",
+                         "InvalidInterface: wpa_supplicant knows nothing about this interface"):
+        if_obj.setBlobs(blobs, dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+def test_dbus_old_connect(dev, apdev):
+    """The old D-Bus interface - add a network and connect"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    for p in [ "/no/where/to/be/found",
+               path + "/Networks/12345",
+               path + "/Networks/foo",
+               "/fi/epitest/hostap/WPASupplicant/Interfaces",
+               "/fi/epitest/hostap/WPASupplicant/Interfaces/12345/Networks/0" ]:
+        obj = bus.get_object(WPAS_DBUS_OLD_SERVICE, p)
+        try:
+            if_obj.removeNetwork(obj, dbus_interface=WPAS_DBUS_OLD_IFACE)
+            raise Exception("Invalid removeNetwork accepted: " + p)
+        except dbus.exceptions.DBusException, e:
+            if not str(e).startswith("fi.epitest.hostap.WPASupplicant.Interface.InvalidNetwork"):
+                raise Exception("Unexpected error message for invalid removeNetwork: " + str(e))
+
+    try:
+        if_obj.removeNetwork("foo", dbus_interface=WPAS_DBUS_OLD_IFACE)
+        raise Exception("Invalid removeNetwork accepted")
+    except dbus.exceptions.DBusException, e:
+        if not str(e).startswith("fi.epitest.hostap.WPASupplicant.InvalidOptions"):
+            raise Exception("Unexpected error message for invalid removeNetwork: " + str(e))
+
+    try:
+        if_obj.removeNetwork(path, dbus_interface=WPAS_DBUS_OLD_IFACE)
+        raise Exception("Invalid removeNetwork accepted")
+    except dbus.exceptions.DBusException, e:
+        if not str(e).startswith("fi.epitest.hostap.WPASupplicant.Interface.InvalidNetwork"):
+            raise Exception("Unexpected error message for invalid removeNetwork: " + str(e))
+
+    tests = [ (path, "InvalidNetwork"),
+              (bus.get_object(WPAS_DBUS_OLD_SERVICE, "/no/where"),
+               "InvalidInterface"),
+              (bus.get_object(WPAS_DBUS_OLD_SERVICE, path + "/Networks/1234"),
+               "InvalidNetwork"),
+              (bus.get_object(WPAS_DBUS_OLD_SERVICE, path + "/Networks/foo"),
+               "InvalidNetwork"),
+              (1, "InvalidOptions") ]
+    for t,err in tests:
+        try:
+            if_obj.selectNetwork(t, dbus_interface=WPAS_DBUS_OLD_IFACE)
+            raise Exception("Invalid selectNetwork accepted: " + str(t))
+        except dbus.exceptions.DBusException, e:
+            if err not in str(e):
+                raise Exception("Unexpected error message for invalid selectNetwork(%s): %s" % (str(t), str(e)))
+
+    npath = if_obj.addNetwork(dbus_interface=WPAS_DBUS_OLD_IFACE)
+    if not npath.startswith(WPAS_DBUS_OLD_PATH):
+        raise Exception("Unexpected addNetwork result: " + path)
+    netw_obj = bus.get_object(WPAS_DBUS_OLD_SERVICE, npath)
+    tests = [ 123,
+              dbus.Dictionary({ 'foo': 'bar' }, signature='sv') ]
+    for t in tests:
+        try:
+            netw_obj.set(t, dbus_interface=WPAS_DBUS_OLD_NETWORK)
+            raise Exception("Invalid set() accepted: " + str(t))
+        except dbus.exceptions.DBusException, e:
+            if "InvalidOptions" not in str(e):
+                raise Exception("Unexpected error message for invalid set: " + str(e))
+    params = dbus.Dictionary({ 'ssid': ssid,
+                               'key_mgmt': 'WPA-PSK',
+                               'psk': passphrase,
+                               'identity': dbus.ByteArray([ 1, 2 ]),
+                               'priority': dbus.Int32(0),
+                               'scan_freq': dbus.UInt32(2412) },
+                             signature='sv')
+    netw_obj.set(params, dbus_interface=WPAS_DBUS_OLD_NETWORK)
+    id = int(dev[0].list_networks()[0]['id'])
+    val = dev[0].get_network(id, "scan_freq")
+    if val != "2412":
+        raise Exception("Invalid scan_freq value: " + str(val))
+    params = dbus.Dictionary({ 'scan_freq': "2412 2432",
+                               'freq_list': "2412 2417 2432" },
+                             signature='sv')
+    netw_obj.set(params, dbus_interface=WPAS_DBUS_OLD_NETWORK)
+    val = dev[0].get_network(id, "scan_freq")
+    if val != "2412 2432":
+        raise Exception("Invalid scan_freq value (2): " + str(val))
+    val = dev[0].get_network(id, "freq_list")
+    if val != "2412 2417 2432":
+        raise Exception("Invalid freq_list value: " + str(val))
+    if_obj.removeNetwork(npath, dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+    class TestDbusConnect(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.state = 0
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_connect)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.scanDone, WPAS_DBUS_OLD_IFACE,
+                            "ScanResultsAvailable")
+            self.add_signal(self.stateChange, WPAS_DBUS_OLD_IFACE,
+                            "StateChange")
+            self.loop.run()
+            return self
+
+        def scanDone(self):
+            logger.debug("scanDone")
+
+        def stateChange(self, new, old):
+            logger.debug("stateChange(%d): %s --> %s" % (self.state, old, new))
+            if new == "COMPLETED":
+                if self.state == 0:
+                    self.state = 1
+                    self.netw_obj.disable(dbus_interface=WPAS_DBUS_OLD_NETWORK)
+                elif self.state == 2:
+                    self.state = 3
+                    if_obj.disconnect(dbus_interface=WPAS_DBUS_OLD_IFACE)
+                elif self.state == 4:
+                    self.state = 5
+                    if_obj.disconnect(dbus_interface=WPAS_DBUS_OLD_IFACE)
+                elif self.state == 6:
+                    self.state = 7
+                    if_obj.removeNetwork(self.path,
+                                         dbus_interface=WPAS_DBUS_OLD_IFACE)
+                    try:
+                        if_obj.removeNetwork(self.path,
+                                             dbus_interface=WPAS_DBUS_OLD_IFACE)
+                        raise Exception("Invalid removeNetwork accepted")
+                    except dbus.exceptions.DBusException, e:
+                        if not str(e).startswith("fi.epitest.hostap.WPASupplicant.Interface.InvalidNetwork"):
+                            raise Exception("Unexpected error message for invalid wpsPbc: " + str(e))
+
+                    self.loop.quit()
+            elif new == "DISCONNECTED":
+                if self.state == 1:
+                    self.state = 2
+                    self.netw_obj.enable(dbus_interface=WPAS_DBUS_OLD_NETWORK)
+                elif self.state == 3:
+                    self.state = 4
+                    if_obj.selectNetwork(dbus_interface=WPAS_DBUS_OLD_IFACE)
+                elif self.state == 5:
+                    self.state = 6
+                    if_obj.selectNetwork(self.path,
+                                         dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+        def run_connect(self, *args):
+            logger.debug("run_connect")
+            path = if_obj.addNetwork(dbus_interface=WPAS_DBUS_OLD_IFACE)
+            netw_obj = bus.get_object(WPAS_DBUS_OLD_SERVICE, path)
+            netw_obj.disable(dbus_interface=WPAS_DBUS_OLD_NETWORK)
+            params = dbus.Dictionary({ 'ssid': ssid,
+                                       'key_mgmt': 'WPA-PSK',
+                                       'psk': passphrase,
+                                       'scan_freq': 2412 },
+                                     signature='sv')
+            netw_obj.set(params, dbus_interface=WPAS_DBUS_OLD_NETWORK)
+            netw_obj.enable(dbus_interface=WPAS_DBUS_OLD_NETWORK)
+            self.path = path
+            self.netw_obj = netw_obj
+            return False
+
+        def success(self):
+            return self.state == 7
+
+    with TestDbusConnect(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+    if len(dev[0].list_networks()) != 0:
+        raise Exception("Unexpected network")
+
+def test_dbus_old_connect_eap(dev, apdev):
+    """The old D-Bus interface - add an EAP network and connect"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    ssid = "test-wpa2-eap"
+    params = hostapd.wpa2_eap_params(ssid=ssid)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    class TestDbusConnect(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.connected = False
+            self.certification_received = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_connect)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.stateChange, WPAS_DBUS_OLD_IFACE,
+                            "StateChange")
+            self.add_signal(self.certification, WPAS_DBUS_OLD_IFACE,
+                            "Certification")
+            self.loop.run()
+            return self
+
+        def stateChange(self, new, old):
+            logger.debug("stateChange: %s --> %s" % (old, new))
+            if new == "COMPLETED":
+                self.connected = True
+                self.loop.quit()
+
+        def certification(self, depth, subject, hash, cert_hex):
+            logger.debug("certification: depth={} subject={} hash={} cert_hex={}".format(depth, subject, hash, cert_hex))
+            self.certification_received = True
+
+        def run_connect(self, *args):
+            logger.debug("run_connect")
+            path = if_obj.addNetwork(dbus_interface=WPAS_DBUS_OLD_IFACE)
+            netw_obj = bus.get_object(WPAS_DBUS_OLD_SERVICE, path)
+            params = dbus.Dictionary({ 'ssid': ssid,
+                                       'key_mgmt': 'WPA-EAP',
+                                       'eap': 'TTLS',
+                                       'anonymous_identity': 'ttls',
+                                       'identity': 'pap user',
+                                       'ca_cert': 'auth_serv/ca.pem',
+                                       'phase2': 'auth=PAP',
+                                       'password': 'password',
+                                       'scan_freq': 2412 },
+                                     signature='sv')
+            netw_obj.set(params, dbus_interface=WPAS_DBUS_OLD_NETWORK)
+            netw_obj.enable(dbus_interface=WPAS_DBUS_OLD_NETWORK)
+            self.path = path
+            self.netw_obj = netw_obj
+            return False
+
+        def success(self):
+            return self.connected and self.certification_received
+
+    with TestDbusConnect(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_old_network_set(dev, apdev):
+    """The old D-Bus interface and network set method"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    path = if_obj.addNetwork(dbus_interface=WPAS_DBUS_OLD_IFACE)
+    netw_obj = bus.get_object(WPAS_DBUS_OLD_SERVICE, path)
+    netw_obj.disable(dbus_interface=WPAS_DBUS_OLD_NETWORK)
+
+    params = dbus.Dictionary({ 'priority': dbus.UInt64(1) }, signature='sv')
+    try:
+        netw_obj.set(params, dbus_interface=WPAS_DBUS_OLD_NETWORK)
+        raise Exception("set succeeded with unexpected type")
+    except dbus.exceptions.DBusException, e:
+        if "InvalidOptions" not in str(e):
+            raise Exception("Unexpected error message for unexpected type: " + str(e))
+
+def test_dbus_old_wps_pbc(dev, apdev):
+    """The old D-Bus interface and WPS/PBC"""
+    try:
+        _test_dbus_old_wps_pbc(dev, apdev)
+    finally:
+        dev[0].request("SET wps_cred_processing 0")
+
+def _test_dbus_old_wps_pbc(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    dev[0].flush_scan_cache()
+    hapd = start_ap(apdev[0])
+    hapd.request("WPS_PBC")
+    bssid = apdev[0]['bssid']
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].request("SET wps_cred_processing 2")
+
+    for arg in [ 123, "123" ]:
+        try:
+            if_obj.wpsPbc(arg, dbus_interface=WPAS_DBUS_OLD_IFACE)
+            raise Exception("Invalid wpsPbc arguments accepted: " + str(arg))
+        except dbus.exceptions.DBusException, e:
+            if not str(e).startswith("fi.epitest.hostap.WPASupplicant.InvalidOptions"):
+                raise Exception("Unexpected error message for invalid wpsPbc: " + str(e))
+
+    class TestDbusWps(TestDbusOldWps):
+        def __init__(self, bus, pbc_param):
+            TestDbusOldWps.__init__(self, bus)
+            self.pbc_param = pbc_param
+
+        def run_wps(self, *args):
+            logger.debug("run_wps: pbc_param=" + self.pbc_param)
+            if_obj.wpsPbc(self.pbc_param, dbus_interface=WPAS_DBUS_OLD_IFACE)
+            return False
+
+    with TestDbusWps(bus, "any") as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+    res = if_obj.scanResults(dbus_interface=WPAS_DBUS_OLD_IFACE)
+    if len(res) != 1:
+        raise Exception("Unexpected number of scan results: " + str(res))
+    for i in range(1):
+        logger.debug("Scan result BSS path: " + res[i])
+        bss_obj = bus.get_object(WPAS_DBUS_OLD_SERVICE, res[i])
+        bss = bss_obj.properties(dbus_interface=WPAS_DBUS_OLD_BSSID,
+                                 byte_arrays=True)
+        logger.debug("BSS: " + str(bss))
+
+    dev[0].wait_connected(timeout=10)
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=10)
+    dev[0].request("FLUSH")
+
+    hapd.request("WPS_PBC")
+    dev[0].scan_for_bss(bssid, freq="2412")
+
+    with TestDbusWps(bus, bssid) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+    dev[0].wait_connected(timeout=10)
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=10)
+
+    hapd.disable()
+    dev[0].flush_scan_cache()
+
+def test_dbus_old_wps_pin(dev, apdev):
+    """The old D-Bus interface and WPS/PIN"""
+    try:
+        _test_dbus_old_wps_pin(dev, apdev)
+    finally:
+        dev[0].request("SET wps_cred_processing 0")
+
+def _test_dbus_old_wps_pin(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    hapd = start_ap(apdev[0])
+    hapd.request("WPS_PIN any 12345670")
+    bssid = apdev[0]['bssid']
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].request("SET wps_cred_processing 2")
+
+    for arg in [ (123, "12345670"),
+                 ("123", "12345670") ]:
+        try:
+            if_obj.wpsPin(arg[0], arg[1], dbus_interface=WPAS_DBUS_OLD_IFACE)
+            raise Exception("Invalid wpsPin arguments accepted: " + str(arg))
+        except dbus.exceptions.DBusException, e:
+            if not str(e).startswith("fi.epitest.hostap.WPASupplicant.InvalidOptions"):
+                raise Exception("Unexpected error message for invalid wpsPbc: " + str(e))
+
+    class TestDbusWps(TestDbusOldWps):
+        def __init__(self, bus, bssid, pin):
+            TestDbusOldWps.__init__(self, bus)
+            self.bssid = bssid
+            self.pin = pin
+
+        def run_wps(self, *args):
+            logger.debug("run_wps %s %s" % (self.bssid, self.pin))
+            pin = if_obj.wpsPin(self.bssid, self.pin,
+                                dbus_interface=WPAS_DBUS_OLD_IFACE)
+            if len(self.pin) == 0:
+                h = hostapd.Hostapd(apdev[0]['ifname'])
+                h.request("WPS_PIN any " + pin)
+            return False
+
+    with TestDbusWps(bus, bssid, "12345670") as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+    dev[0].wait_connected(timeout=10)
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=10)
+    dev[0].request("FLUSH")
+
+    dev[0].scan_for_bss(bssid, freq="2412")
+
+    with TestDbusWps(bus, "any", "") as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+def test_dbus_old_wps_reg(dev, apdev):
+    """The old D-Bus interface and WPS/Registar"""
+    try:
+        _test_dbus_old_wps_reg(dev, apdev)
+    finally:
+        dev[0].request("SET wps_cred_processing 0")
+
+def _test_dbus_old_wps_reg(dev, apdev):
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+    dev[0].scan_for_bss(bssid, freq="2412")
+    dev[0].request("SET wps_cred_processing 2")
+
+    for arg in [ (123, "12345670"),
+                 ("123", "12345670") ]:
+        try:
+            if_obj.wpsReg(arg[0], arg[1], dbus_interface=WPAS_DBUS_OLD_IFACE)
+            raise Exception("Invalid wpsReg arguments accepted: " + str(arg))
+        except dbus.exceptions.DBusException, e:
+            if not str(e).startswith("fi.epitest.hostap.WPASupplicant.InvalidOptions"):
+                raise Exception("Unexpected error message for invalid wpsPbc: " + str(e))
+
+    class TestDbusWps(TestDbusOldWps):
+        def run_wps(self, *args):
+            logger.debug("run_wps")
+            if_obj.wpsReg(bssid, "12345670", dbus_interface=WPAS_DBUS_OLD_IFACE)
+            return False
+
+    with TestDbusWps(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
+
+    dev[0].wait_connected(timeout=10)
+
+def test_dbus_old_wps_oom(dev, apdev):
+    """The old D-Bus interface and WPS (OOM)"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+    bssid = apdev[0]['bssid']
+
+    with alloc_fail_dbus(dev[0], 1,
+                         "=wpa_config_add_network;wpas_dbus_iface_wps_pbc",
+                         "wpsPbc",
+                         "WpsPbcError: Could not start PBC negotiation"):
+        if_obj.wpsPbc("any", dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+    with alloc_fail_dbus(dev[0], 1,
+                         "=wpa_config_add_network;wpas_dbus_iface_wps_pin",
+                         "wpsPin", "WpsPinError: Could not init PIN"):
+        if_obj.wpsPin("any", "", dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+    with alloc_fail_dbus(dev[0], 1,
+                         "=wpa_config_add_network;wpas_dbus_iface_wps_reg",
+                         "wpsReg",
+                         "WpsRegError: Could not request credentials"):
+        if_obj.wpsReg(bssid, "12345670", dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+def test_dbus_old_network_set_oom(dev, apdev):
+    """The old D-Bus interface and network set method (OOM)"""
+    (bus,wpas_obj,path,if_obj) = prepare_dbus(dev[0])
+
+    with alloc_fail_dbus(dev[0], 1,
+                         "=wpa_config_add_network;wpas_dbus_iface_add_network",
+                         "addNetwork",
+                         "AddNetworkError: wpa_supplicant could not add"):
+        if_obj.addNetwork(dbus_interface=WPAS_DBUS_OLD_IFACE)
+
+    path = if_obj.addNetwork(dbus_interface=WPAS_DBUS_OLD_IFACE)
+    netw_obj = bus.get_object(WPAS_DBUS_OLD_SERVICE, path)
+    netw_obj.disable(dbus_interface=WPAS_DBUS_OLD_NETWORK)
+
+    with alloc_fail_dbus(dev[0], 1,
+                         "_wpa_dbus_dict_fill_value_from_variant;wpas_dbus_iface_set_network",
+                         "set", "InvalidOptions"):
+        params = dbus.Dictionary({ 'ssid': "foo" }, signature='sv')
+        netw_obj.set(params, dbus_interface=WPAS_DBUS_OLD_NETWORK)
+
+    tests = [ { 'identity': dbus.ByteArray([ 1, 2 ]) },
+              { 'scan_freq': dbus.UInt32(2412) },
+              { 'priority': dbus.Int32(0) },
+              { 'identity': "user" },
+              { 'eap': "TLS" }]
+    for arg in tests:
+        with alloc_fail_dbus(dev[0], 1, "=wpas_dbus_iface_set_network",
+                             "set", "InvalidOptions"):
+            params = dbus.Dictionary(arg, signature='sv')
+            netw_obj.set(params, dbus_interface=WPAS_DBUS_OLD_NETWORK)
diff --git a/hostap/tests/hwsim/test_dfs.py b/hostap/tests/hwsim/test_dfs.py
new file mode 100644
index 0000000..592736e
--- /dev/null
+++ b/hostap/tests/hwsim/test_dfs.py
@@ -0,0 +1,402 @@
+# Test cases for DFS
+# Copyright (c) 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 subprocess
+import time
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import hostapd
+from utils import HwsimSkip
+
+def wait_dfs_event(hapd, event, timeout):
+    dfs_events = [ "DFS-RADAR-DETECTED", "DFS-NEW-CHANNEL",
+                   "DFS-CAC-START", "DFS-CAC-COMPLETED",
+                   "DFS-NOP-FINISHED", "AP-ENABLED", "AP-CSA-FINISHED" ]
+    ev = hapd.wait_event(dfs_events, timeout=timeout)
+    if not ev:
+        raise Exception("DFS event timed out")
+    if event and event not in ev:
+        raise Exception("Unexpected DFS event")
+    return ev
+
+def start_dfs_ap(ap, allow_failure=False, ssid="dfs", ht=True, ht40=False,
+                 ht40minus=False, vht80=False, vht20=False, chanlist=None):
+    ifname = ap['ifname']
+    logger.info("Starting AP " + ifname + " on DFS channel")
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.remove(ifname)
+    hapd_global.add(ifname)
+    hapd = hostapd.Hostapd(ifname)
+    if not hapd.ping():
+        raise Exception("Could not ping hostapd")
+    hapd.set_defaults()
+    hapd.set("ssid", ssid)
+    hapd.set("country_code", "FI")
+    hapd.set("ieee80211d", "1")
+    hapd.set("ieee80211h", "1")
+    hapd.set("hw_mode", "a")
+    hapd.set("channel", "52")
+    if not ht:
+        hapd.set("ieee80211n", "0")
+    if ht40:
+        hapd.set("ht_capab", "[HT40+]")
+    elif ht40minus:
+        hapd.set("ht_capab", "[HT40-]")
+        hapd.set("channel", "56")
+    if vht80:
+        hapd.set("ieee80211ac", "1")
+        hapd.set("vht_oper_chwidth", "1")
+        hapd.set("vht_oper_centr_freq_seg0_idx", "58")
+    if vht20:
+        hapd.set("ieee80211ac", "1")
+        hapd.set("vht_oper_chwidth", "0")
+        hapd.set("vht_oper_centr_freq_seg0_idx", "0")
+    if chanlist:
+        hapd.set("chanlist", chanlist)
+    hapd.enable()
+
+    ev = wait_dfs_event(hapd, "DFS-CAC-START", 5)
+    if "DFS-CAC-START" not in ev:
+        raise Exception("Unexpected DFS event")
+
+    state = hapd.get_status_field("state")
+    if state != "DFS":
+        if allow_failure:
+            logger.info("Interface state not DFS: " + state)
+            if not os.path.exists("dfs"):
+                raise HwsimSkip("Assume DFS testing not supported")
+            raise Exception("Failed to start DFS AP")
+        raise Exception("Unexpected interface state: " + state)
+
+    return hapd
+
+def dfs_simulate_radar(hapd):
+    logger.info("Trigger a simulated radar event")
+    phyname = hapd.get_driver_status_field("phyname")
+    radar_file = '/sys/kernel/debug/ieee80211/' + phyname + '/hwsim/dfs_simulate_radar'
+    with open(radar_file, 'w') as f:
+        f.write('1')
+
+def test_dfs(dev, apdev):
+    """DFS CAC functionality on clear channel"""
+    try:
+        hapd = None
+        hapd = start_dfs_ap(apdev[0], allow_failure=True)
+
+        ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+        if "success=1" not in ev:
+            raise Exception("CAC failed")
+        if "freq=5260" not in ev:
+            raise Exception("Unexpected DFS freq result")
+
+        ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+        if not ev:
+            raise Exception("AP setup timed out")
+
+        state = hapd.get_status_field("state")
+        if state != "ENABLED":
+            raise Exception("Unexpected interface state")
+
+        freq = hapd.get_status_field("freq")
+        if freq != "5260":
+            raise Exception("Unexpected frequency")
+
+        dev[0].connect("dfs", key_mgmt="NONE")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+
+        hapd.request("RADAR DETECTED freq=5260 ht_enabled=1 chan_width=1")
+        ev = hapd.wait_event(["DFS-RADAR-DETECTED"], timeout=10)
+        if ev is None:
+            raise Exception("DFS-RADAR-DETECTED event not reported")
+        if "freq=5260" not in ev:
+            raise Exception("Incorrect frequency in radar detected event: " + ev);
+        ev = hapd.wait_event(["DFS-NEW-CHANNEL"], timeout=70)
+        if ev is None:
+            raise Exception("DFS-NEW-CHANNEL event not reported")
+        if "freq=5260" in ev:
+            raise Exception("Channel did not change after radar was detected");
+
+        ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=70)
+        if ev is None:
+            raise Exception("AP-CSA-FINISHED event not reported")
+        if "freq=5260" in ev:
+            raise Exception("Channel did not change after radar was detected(2)");
+        time.sleep(1)
+        hwsim_utils.test_connectivity(dev[0], hapd)
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_dfs_radar(dev, apdev):
+    """DFS CAC functionality with radar detected"""
+    try:
+        hapd = None
+        hapd2 = None
+        hapd = start_dfs_ap(apdev[0], allow_failure=True)
+        time.sleep(1)
+
+        dfs_simulate_radar(hapd)
+
+        hapd2 = start_dfs_ap(apdev[1], ssid="dfs2", ht40=True)
+
+        ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+        if ev is None:
+            raise Exception("Timeout on DFS aborted event")
+        if "success=0 freq=5260" not in ev:
+            raise Exception("Unexpected DFS aborted event contents: " + ev)
+
+        ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+        if "freq=5260" not in ev:
+            raise Exception("Unexpected DFS radar detection freq")
+
+        ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+        if "freq=5260" in ev:
+            raise Exception("Unexpected DFS new freq")
+
+        ev = wait_dfs_event(hapd, None, 5)
+        if "AP-ENABLED" in ev:
+            logger.info("Started AP on non-DFS channel")
+        else:
+            logger.info("Trying to start AP on another DFS channel")
+            if "DFS-CAC-START" not in ev:
+                raise Exception("Unexpected DFS event")
+            if "freq=5260" in ev:
+                raise Exception("Unexpected DFS CAC freq")
+
+            ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+            if "success=1" not in ev:
+                raise Exception("CAC failed")
+            if "freq=5260" in ev:
+                raise Exception("Unexpected DFS freq result - radar channel")
+
+            ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+            if not ev:
+                raise Exception("AP setup timed out")
+
+            state = hapd.get_status_field("state")
+            if state != "ENABLED":
+                raise Exception("Unexpected interface state")
+
+            freq = hapd.get_status_field("freq")
+            if freq == "5260":
+                raise Exception("Unexpected frequency: " + freq)
+
+        dev[0].connect("dfs", key_mgmt="NONE")
+
+        ev = hapd2.wait_event(["AP-ENABLED"], timeout=70)
+        if not ev:
+            raise Exception("AP2 setup timed out")
+
+        dfs_simulate_radar(hapd2)
+
+        ev = wait_dfs_event(hapd2, "DFS-RADAR-DETECTED", 5)
+        if "freq=5260 ht_enabled=1 chan_offset=1 chan_width=2" not in ev:
+            raise Exception("Unexpected DFS radar detection freq from AP2")
+
+        ev = wait_dfs_event(hapd2, "DFS-NEW-CHANNEL", 5)
+        if "freq=5260" in ev:
+            raise Exception("Unexpected DFS new freq for AP2")
+
+        wait_dfs_event(hapd2, None, 5)
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        if hapd2:
+            hapd2.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_dfs_radar_on_non_dfs_channel(dev, apdev):
+    """DFS radar detection test code on non-DFS channel"""
+    params = { "ssid": "radar" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    hapd.request("RADAR DETECTED freq=5260 ht_enabled=1 chan_width=1")
+    hapd.request("RADAR DETECTED freq=2412 ht_enabled=1 chan_width=1")
+
+def test_dfs_radar_chanlist(dev, apdev):
+    """DFS chanlist when radar is detected"""
+    try:
+        hapd = None
+        hapd = start_dfs_ap(apdev[0], chanlist="40 44", allow_failure=True)
+        time.sleep(1)
+
+        dfs_simulate_radar(hapd)
+
+        ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+        if ev is None:
+            raise Exception("Timeout on DFS aborted event")
+        if "success=0 freq=5260" not in ev:
+            raise Exception("Unexpected DFS aborted event contents: " + ev)
+
+        ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+        if "freq=5260" not in ev:
+            raise Exception("Unexpected DFS radar detection freq")
+
+        ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+        if "freq=5200 chan=40" not in ev and "freq=5220 chan=44" not in ev:
+            raise Exception("Unexpected DFS new freq: " + ev)
+
+        ev = wait_dfs_event(hapd, None, 5)
+        if "AP-ENABLED" not in ev:
+            raise Exception("Unexpected DFS event")
+        dev[0].connect("dfs", key_mgmt="NONE")
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_dfs_radar_chanlist_vht80(dev, apdev):
+    """DFS chanlist when radar is detected and VHT80 configured"""
+    try:
+        hapd = None
+        hapd = start_dfs_ap(apdev[0], chanlist="36", ht40=True, vht80=True,
+                            allow_failure=True)
+        time.sleep(1)
+
+        dfs_simulate_radar(hapd)
+
+        ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+        if ev is None:
+            raise Exception("Timeout on DFS aborted event")
+        if "success=0 freq=5260" not in ev:
+            raise Exception("Unexpected DFS aborted event contents: " + ev)
+
+        ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+        if "freq=5260" not in ev:
+            raise Exception("Unexpected DFS radar detection freq")
+
+        ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+        if "freq=5180 chan=36 sec_chan=1" not in ev:
+            raise Exception("Unexpected DFS new freq: " + ev)
+
+        ev = wait_dfs_event(hapd, None, 5)
+        if "AP-ENABLED" not in ev:
+            raise Exception("Unexpected DFS event")
+        dev[0].connect("dfs", key_mgmt="NONE")
+
+        if hapd.get_status_field('vht_oper_centr_freq_seg0_idx') != "42":
+            raise Exception("Unexpected seg0 idx")
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_dfs_radar_chanlist_vht20(dev, apdev):
+    """DFS chanlist when radar is detected and VHT40 configured"""
+    try:
+        hapd = None
+        hapd = start_dfs_ap(apdev[0], chanlist="36", vht20=True,
+                            allow_failure=True)
+        time.sleep(1)
+
+        dfs_simulate_radar(hapd)
+
+        ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+        if ev is None:
+            raise Exception("Timeout on DFS aborted event")
+        if "success=0 freq=5260" not in ev:
+            raise Exception("Unexpected DFS aborted event contents: " + ev)
+
+        ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+        if "freq=5260" not in ev:
+            raise Exception("Unexpected DFS radar detection freq")
+
+        ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+        if "freq=5180 chan=36 sec_chan=0" not in ev:
+            raise Exception("Unexpected DFS new freq: " + ev)
+
+        ev = wait_dfs_event(hapd, None, 5)
+        if "AP-ENABLED" not in ev:
+            raise Exception("Unexpected DFS event")
+        dev[0].connect("dfs", key_mgmt="NONE")
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_dfs_radar_no_ht(dev, apdev):
+    """DFS chanlist when radar is detected and no HT configured"""
+    try:
+        hapd = None
+        hapd = start_dfs_ap(apdev[0], chanlist="36", ht=False,
+                            allow_failure=True)
+        time.sleep(1)
+
+        dfs_simulate_radar(hapd)
+
+        ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+        if ev is None:
+            raise Exception("Timeout on DFS aborted event")
+        if "success=0 freq=5260" not in ev:
+            raise Exception("Unexpected DFS aborted event contents: " + ev)
+
+        ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+        if "freq=5260 ht_enabled=0" not in ev:
+            raise Exception("Unexpected DFS radar detection freq: " + ev)
+
+        ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+        if "freq=5180 chan=36 sec_chan=0" not in ev:
+            raise Exception("Unexpected DFS new freq: " + ev)
+
+        ev = wait_dfs_event(hapd, None, 5)
+        if "AP-ENABLED" not in ev:
+            raise Exception("Unexpected DFS event")
+        dev[0].connect("dfs", key_mgmt="NONE")
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_dfs_radar_ht40minus(dev, apdev):
+    """DFS chanlist when radar is detected and HT40- configured"""
+    try:
+        hapd = None
+        hapd = start_dfs_ap(apdev[0], chanlist="36", ht40minus=True,
+                            allow_failure=True)
+        time.sleep(1)
+
+        dfs_simulate_radar(hapd)
+
+        ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+        if ev is None:
+            raise Exception("Timeout on DFS aborted event")
+        if "success=0 freq=5280 ht_enabled=1 chan_offset=-1" not in ev:
+            raise Exception("Unexpected DFS aborted event contents: " + ev)
+
+        ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+        if "freq=5280 ht_enabled=1 chan_offset=-1" not in ev:
+            raise Exception("Unexpected DFS radar detection freq: " + ev)
+
+        ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+        if "freq=5180 chan=36 sec_chan=1" not in ev:
+            raise Exception("Unexpected DFS new freq: " + ev)
+
+        ev = wait_dfs_event(hapd, None, 5)
+        if "AP-ENABLED" not in ev:
+            raise Exception("Unexpected DFS event")
+        dev[0].connect("dfs", key_mgmt="NONE")
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
diff --git a/hostap/tests/hwsim/test_eap_proto.py b/hostap/tests/hwsim/test_eap_proto.py
new file mode 100644
index 0000000..f0c8a52
--- /dev/null
+++ b/hostap/tests/hwsim/test_eap_proto.py
@@ -0,0 +1,4013 @@
+# EAP protocol tests
+# Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import hmac
+import logging
+logger = logging.getLogger()
+import select
+import struct
+import threading
+import time
+
+import hostapd
+from utils import HwsimSkip
+from test_ap_eap import check_eap_capa
+
+EAP_CODE_REQUEST = 1
+EAP_CODE_RESPONSE = 2
+EAP_CODE_SUCCESS = 3
+EAP_CODE_FAILURE = 4
+
+EAP_TYPE_IDENTITY = 1
+EAP_TYPE_NOTIFICATION = 2
+EAP_TYPE_NAK = 3
+EAP_TYPE_MD5 = 4
+EAP_TYPE_OTP = 5
+EAP_TYPE_GTC = 6
+EAP_TYPE_TLS = 13
+EAP_TYPE_LEAP = 17
+EAP_TYPE_SIM = 18
+EAP_TYPE_TTLS = 21
+EAP_TYPE_AKA = 23
+EAP_TYPE_PEAP = 25
+EAP_TYPE_MSCHAPV2 = 26
+EAP_TYPE_TLV = 33
+EAP_TYPE_TNC = 38
+EAP_TYPE_FAST = 43
+EAP_TYPE_PAX = 46
+EAP_TYPE_PSK = 47
+EAP_TYPE_SAKE = 48
+EAP_TYPE_IKEV2 = 49
+EAP_TYPE_AKA_PRIME = 50
+EAP_TYPE_GPSK = 51
+EAP_TYPE_PWD = 52
+EAP_TYPE_EKE = 53
+
+def run_pyrad_server(srv, t_stop, eap_handler):
+    srv.RunWithStop(t_stop, eap_handler)
+
+def start_radius_server(eap_handler):
+    try:
+        import pyrad.server
+        import pyrad.packet
+        import pyrad.dictionary
+    except ImportError:
+        raise HwsimSkip("No pyrad modules available")
+
+    class TestServer(pyrad.server.Server):
+        def _HandleAuthPacket(self, pkt):
+            pyrad.server.Server._HandleAuthPacket(self, pkt)
+            if len(pkt[79]) > 1:
+                logger.info("Multiple EAP-Message attributes")
+                # TODO: reassemble
+            eap = pkt[79][0]
+            eap_req = self.eap_handler(self.ctx, eap)
+            reply = self.CreateReplyPacket(pkt)
+            if eap_req:
+                while True:
+                    if len(eap_req) > 253:
+                        reply.AddAttribute("EAP-Message", eap_req[0:253])
+                        eap_req = eap_req[253:]
+                    else:
+                        reply.AddAttribute("EAP-Message", eap_req)
+                        break
+            else:
+                logger.info("No EAP request available")
+            reply.code = pyrad.packet.AccessChallenge
+
+            hmac_obj = hmac.new(reply.secret)
+            hmac_obj.update(struct.pack("B", reply.code))
+            hmac_obj.update(struct.pack("B", reply.id))
+
+            # reply attributes
+            reply.AddAttribute("Message-Authenticator",
+                               "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
+            attrs = reply._PktEncodeAttributes()
+
+            # Length
+            flen = 4 + 16 + len(attrs)
+            hmac_obj.update(struct.pack(">H", flen))
+            hmac_obj.update(pkt.authenticator)
+            hmac_obj.update(attrs)
+            del reply[80]
+            reply.AddAttribute("Message-Authenticator", hmac_obj.digest())
+
+            self.SendReplyPacket(pkt.fd, reply)
+
+        def RunWithStop(self, t_stop, eap_handler):
+            self._poll = select.poll()
+            self._fdmap = {}
+            self._PrepareSockets()
+            self.t_stop = t_stop
+            self.eap_handler = eap_handler
+            self.ctx = {}
+
+            while not t_stop.is_set():
+                for (fd, event) in self._poll.poll(1000):
+                    if event == select.POLLIN:
+                        try:
+                            fdo = self._fdmap[fd]
+                            self._ProcessInput(fdo)
+                        except pyrad.server.ServerPacketError as err:
+                            logger.info("pyrad server dropping packet: " + str(err))
+                        except pyrad.packet.PacketError as err:
+                            logger.info("pyrad server received invalid packet: " + str(err))
+                    else:
+                        logger.error("Unexpected event in pyrad server main loop")
+
+    srv = TestServer(dict=pyrad.dictionary.Dictionary("dictionary.radius"),
+                     authport=18138, acctport=18139)
+    srv.hosts["127.0.0.1"] = pyrad.server.RemoteHost("127.0.0.1",
+                                                     "radius",
+                                                     "localhost")
+    srv.BindToAddress("")
+    t_stop = threading.Event()
+    t = threading.Thread(target=run_pyrad_server, args=(srv, t_stop, eap_handler))
+    t.start()
+
+    return { 'srv': srv, 'stop': t_stop, 'thread': t }
+
+def stop_radius_server(srv):
+    srv['stop'].set()
+    srv['thread'].join()
+
+def start_ap(ifname):
+    params = hostapd.wpa2_eap_params(ssid="eap-test")
+    params['auth_server_port'] = "18138"
+    hapd = hostapd.add_ap(ifname, params)
+    return hapd
+
+def test_eap_proto(dev, apdev):
+    """EAP protocol tests"""
+    check_eap_capa(dev[0], "MD5")
+    def eap_handler(ctx, req):
+        logger.info("eap_handler - RX " + req.encode("hex"))
+        if 'num' not in ctx:
+            ctx['num'] = 0
+        ctx['num'] = ctx['num'] + 1
+        if 'id' not in ctx:
+            ctx['id'] = 1
+        ctx['id'] = (ctx['id'] + 1) % 256
+        idx = 0
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: MD5 challenge")
+            return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_MD5,
+                               1, 0xaa, ord('n'))
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Success - id off by 2")
+            return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] + 1, 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: MD5 challenge")
+            return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_MD5,
+                               1, 0xaa, ord('n'))
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Success - id off by 3")
+            return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] + 2, 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: MD5 challenge")
+            return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_MD5,
+                               1, 0xaa, ord('n'))
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Notification/Request")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_NOTIFICATION,
+                               ord('A'))
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Success")
+            return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] - 1, 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Notification/Request")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_NOTIFICATION,
+                               ord('B'))
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: MD5 challenge")
+            return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_MD5,
+                               1, 0xaa, ord('n'))
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Success")
+            return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] - 1, 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Notification/Request")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_NOTIFICATION,
+                               ord('C'))
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: MD5 challenge")
+            return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_MD5,
+                               1, 0xaa, ord('n'))
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Notification/Request")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_NOTIFICATION,
+                               ord('D'))
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Success")
+            return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] - 1, 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Notification/Request")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_NOTIFICATION,
+                               ord('E'))
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Notification/Request (same id)")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'] - 1,
+                               4 + 1 + 1,
+                               EAP_TYPE_NOTIFICATION,
+                               ord('F'))
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected EAP-Success")
+            return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] - 2, 4)
+
+        return None
+
+    srv = start_radius_server(eap_handler)
+
+    try:
+        hapd = start_ap(apdev[0]['ifname'])
+
+        dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                       eap="MD5", identity="user", password="password",
+                       wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP start")
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP success")
+        dev[0].request("REMOVE_NETWORK all")
+
+        dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                       eap="MD5", identity="user", password="password",
+                       wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP start")
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=1)
+        if ev is not None:
+            raise Exception("Unexpected EAP success")
+        dev[0].request("REMOVE_NETWORK all")
+
+        dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                       eap="MD5", identity="user", password="password",
+                       wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP start")
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
+        if ev is None:
+            raise Exception("Timeout on EAP notification")
+        if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION A":
+            raise Exception("Unexpected notification contents: " + ev)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP success")
+        dev[0].request("REMOVE_NETWORK all")
+
+        dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                       eap="MD5", identity="user", password="password",
+                       wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
+        if ev is None:
+            raise Exception("Timeout on EAP notification")
+        if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION B":
+            raise Exception("Unexpected notification contents: " + ev)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP start")
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP success")
+        dev[0].request("REMOVE_NETWORK all")
+
+        dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                       eap="MD5", identity="user", password="password",
+                       wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
+        if ev is None:
+            raise Exception("Timeout on EAP notification")
+        if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION C":
+            raise Exception("Unexpected notification contents: " + ev)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP start")
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
+        if ev is None:
+            raise Exception("Timeout on EAP notification")
+        if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION D":
+            raise Exception("Unexpected notification contents: " + ev)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP success")
+        dev[0].request("REMOVE_NETWORK all")
+
+        dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                       eap="MD5", identity="user", password="password",
+                       wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
+        if ev is None:
+            raise Exception("Timeout on EAP notification")
+        if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION E":
+            raise Exception("Unexpected notification contents: " + ev)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
+        if ev is None:
+            raise Exception("Timeout on EAP notification")
+        if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION F":
+            raise Exception("Unexpected notification contents: " + ev)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP failure")
+        dev[0].request("REMOVE_NETWORK all")
+    finally:
+        stop_radius_server(srv)
+
+EAP_SAKE_VERSION = 2
+
+EAP_SAKE_SUBTYPE_CHALLENGE = 1
+EAP_SAKE_SUBTYPE_CONFIRM = 2
+EAP_SAKE_SUBTYPE_AUTH_REJECT = 3
+EAP_SAKE_SUBTYPE_IDENTITY = 4
+
+EAP_SAKE_AT_RAND_S = 1
+EAP_SAKE_AT_RAND_P = 2
+EAP_SAKE_AT_MIC_S = 3
+EAP_SAKE_AT_MIC_P = 4
+EAP_SAKE_AT_SERVERID = 5
+EAP_SAKE_AT_PEERID = 6
+EAP_SAKE_AT_SPI_S = 7
+EAP_SAKE_AT_SPI_P = 8
+EAP_SAKE_AT_ANY_ID_REQ = 9
+EAP_SAKE_AT_PERM_ID_REQ = 10
+EAP_SAKE_AT_ENCR_DATA = 128
+EAP_SAKE_AT_IV = 129
+EAP_SAKE_AT_PADDING = 130
+EAP_SAKE_AT_NEXT_TMPID = 131
+EAP_SAKE_AT_MSK_LIFE = 132
+
+def test_eap_proto_sake(dev, apdev):
+    """EAP-SAKE protocol tests"""
+    def sake_challenge(ctx):
+        logger.info("Test: Challenge subtype")
+        return struct.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST, ctx['id'],
+                           4 + 1 + 3 + 18,
+                           EAP_TYPE_SAKE,
+                           EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CHALLENGE,
+                           EAP_SAKE_AT_RAND_S, 18, 0, 0, 0, 0)
+
+    def sake_handler(ctx, req):
+        logger.info("sake_handler - RX " + req.encode("hex"))
+        if 'num' not in ctx:
+            ctx['num'] = 0
+        ctx['num'] = ctx['num'] + 1
+        if 'id' not in ctx:
+            ctx['id'] = 1
+        ctx['id'] = (ctx['id'] + 1) % 256
+
+        if ctx['num'] == 1:
+            logger.info("Test: Missing payload")
+            return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'], 4 + 1,
+                               EAP_TYPE_SAKE)
+
+        if ctx['num'] == 2:
+            logger.info("Test: Identity subtype without any attributes")
+            return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_SAKE,
+                               EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY)
+
+        if ctx['num'] == 3:
+            logger.info("Test: Identity subtype")
+            return struct.pack(">BBHBBBBBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_SAKE,
+                               EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY,
+                               EAP_SAKE_AT_ANY_ID_REQ, 4, 0)
+        if ctx['num'] == 4:
+            logger.info("Test: Identity subtype (different session id)")
+            return struct.pack(">BBHBBBBBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_SAKE,
+                               EAP_SAKE_VERSION, 1, EAP_SAKE_SUBTYPE_IDENTITY,
+                               EAP_SAKE_AT_PERM_ID_REQ, 4, 0)
+
+        if ctx['num'] == 5:
+            logger.info("Test: Identity subtype with too short attribute")
+            return struct.pack(">BBHBBBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 2,
+                               EAP_TYPE_SAKE,
+                               EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY,
+                               EAP_SAKE_AT_ANY_ID_REQ, 2)
+
+        if ctx['num'] == 6:
+            logger.info("Test: Identity subtype with truncated attribute")
+            return struct.pack(">BBHBBBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 2,
+                               EAP_TYPE_SAKE,
+                               EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY,
+                               EAP_SAKE_AT_ANY_ID_REQ, 4)
+
+        if ctx['num'] == 7:
+            logger.info("Test: Unknown subtype")
+            return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_SAKE,
+                               EAP_SAKE_VERSION, 0, 123)
+
+        if ctx['num'] == 8:
+            logger.info("Test: Challenge subtype without any attributes")
+            return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_SAKE,
+                               EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CHALLENGE)
+
+        if ctx['num'] == 9:
+            logger.info("Test: Challenge subtype with too short AT_RAND_S")
+            return struct.pack(">BBHBBBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 2,
+                               EAP_TYPE_SAKE,
+                               EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CHALLENGE,
+                               EAP_SAKE_AT_RAND_S, 2)
+
+        if ctx['num'] == 10:
+            return sake_challenge(ctx)
+        if ctx['num'] == 11:
+            logger.info("Test: Unexpected Identity subtype")
+            return struct.pack(">BBHBBBBBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_SAKE,
+                               EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY,
+                               EAP_SAKE_AT_ANY_ID_REQ, 4, 0)
+
+        if ctx['num'] == 12:
+            return sake_challenge(ctx)
+        if ctx['num'] == 13:
+            logger.info("Test: Unexpected Challenge subtype")
+            return struct.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 18,
+                               EAP_TYPE_SAKE,
+                               EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CHALLENGE,
+                               EAP_SAKE_AT_RAND_S, 18, 0, 0, 0, 0)
+
+        if ctx['num'] == 14:
+            return sake_challenge(ctx)
+        if ctx['num'] == 15:
+            logger.info("Test: Confirm subtype without any attributes")
+            return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_SAKE,
+                               EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CONFIRM)
+
+        if ctx['num'] == 16:
+            return sake_challenge(ctx)
+        if ctx['num'] == 17:
+            logger.info("Test: Confirm subtype with too short AT_MIC_S")
+            return struct.pack(">BBHBBBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 2,
+                               EAP_TYPE_SAKE,
+                               EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CONFIRM,
+                               EAP_SAKE_AT_MIC_S, 2)
+
+        if ctx['num'] == 18:
+            logger.info("Test: Unexpected Confirm subtype")
+            return struct.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 18,
+                               EAP_TYPE_SAKE,
+                               EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CONFIRM,
+                               EAP_SAKE_AT_MIC_S, 18, 0, 0, 0, 0)
+
+        if ctx['num'] == 19:
+            return sake_challenge(ctx)
+        if ctx['num'] == 20:
+            logger.info("Test: Confirm subtype with incorrect AT_MIC_S")
+            return struct.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 18,
+                               EAP_TYPE_SAKE,
+                               EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CONFIRM,
+                               EAP_SAKE_AT_MIC_S, 18, 0, 0, 0, 0)
+
+        return sake_challenge(ctx)
+
+    srv = start_radius_server(sake_handler)
+
+    try:
+        hapd = start_ap(apdev[0]['ifname'])
+
+        for i in range(0, 14):
+            dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                           eap="SAKE", identity="sake user",
+                           password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+                           wait_connect=False)
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+            if ev is None:
+                raise Exception("Timeout on EAP start")
+            time.sleep(0.1)
+            dev[0].request("REMOVE_NETWORK all")
+
+        logger.info("Too short password")
+        dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                       eap="SAKE", identity="sake user",
+                       password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd",
+                       wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP start")
+        time.sleep(0.1)
+    finally:
+        stop_radius_server(srv)
+
+def test_eap_proto_leap(dev, apdev):
+    """EAP-LEAP protocol tests"""
+    check_eap_capa(dev[0], "LEAP")
+    def leap_handler(ctx, req):
+        logger.info("leap_handler - RX " + req.encode("hex"))
+        if 'num' not in ctx:
+            ctx['num'] = 0
+        ctx['num'] = ctx['num'] + 1
+        if 'id' not in ctx:
+            ctx['id'] = 1
+        ctx['id'] = (ctx['id'] + 1) % 256
+
+        if ctx['num'] == 1:
+            logger.info("Test: Missing payload")
+            return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_LEAP)
+
+        if ctx['num'] == 2:
+            logger.info("Test: Unexpected version")
+            return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_LEAP,
+                               0, 0, 0)
+
+        if ctx['num'] == 3:
+            logger.info("Test: Invalid challenge length")
+            return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_LEAP,
+                               1, 0, 0)
+
+        if ctx['num'] == 4:
+            logger.info("Test: Truncated challenge")
+            return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_LEAP,
+                               1, 0, 8)
+
+        if ctx['num'] == 5:
+            logger.info("Test: Valid challenge")
+            return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_LEAP,
+                               1, 0, 8, 0, 0)
+        if ctx['num'] == 6:
+            logger.info("Test: Missing payload in Response")
+            return struct.pack(">BBHB", EAP_CODE_RESPONSE, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_LEAP)
+
+        if ctx['num'] == 7:
+            logger.info("Test: Valid challenge")
+            return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_LEAP,
+                               1, 0, 8, 0, 0)
+        if ctx['num'] == 8:
+            logger.info("Test: Unexpected version in Response")
+            return struct.pack(">BBHBBBB", EAP_CODE_RESPONSE, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_LEAP,
+                               0, 0, 8)
+
+        if ctx['num'] == 9:
+            logger.info("Test: Valid challenge")
+            return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_LEAP,
+                               1, 0, 8, 0, 0)
+        if ctx['num'] == 10:
+            logger.info("Test: Invalid challenge length in Response")
+            return struct.pack(">BBHBBBB", EAP_CODE_RESPONSE, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_LEAP,
+                               1, 0, 0)
+
+        if ctx['num'] == 11:
+            logger.info("Test: Valid challenge")
+            return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_LEAP,
+                               1, 0, 8, 0, 0)
+        if ctx['num'] == 12:
+            logger.info("Test: Truncated challenge in Response")
+            return struct.pack(">BBHBBBB", EAP_CODE_RESPONSE, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_LEAP,
+                               1, 0, 24)
+
+        if ctx['num'] == 13:
+            logger.info("Test: Valid challenge")
+            return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_LEAP,
+                               1, 0, 8, 0, 0)
+        if ctx['num'] == 14:
+            logger.info("Test: Invalid challange value in Response")
+            return struct.pack(">BBHBBBB6L", EAP_CODE_RESPONSE, ctx['id'],
+                               4 + 1 + 3 + 24,
+                               EAP_TYPE_LEAP,
+                               1, 0, 24,
+                               0, 0, 0, 0, 0, 0)
+
+        if ctx['num'] == 15:
+            logger.info("Test: Valid challenge")
+            return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_LEAP,
+                               1, 0, 8, 0, 0)
+        if ctx['num'] == 16:
+            logger.info("Test: Valid challange value in Response")
+            return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
+                               4 + 1 + 3 + 24,
+                               EAP_TYPE_LEAP,
+                               1, 0, 24,
+                               0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
+                               0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
+                               0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
+
+        if ctx['num'] == 17:
+            logger.info("Test: Valid challenge")
+            return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_LEAP,
+                               1, 0, 8, 0, 0)
+        if ctx['num'] == 18:
+            logger.info("Test: Success")
+            return struct.pack(">BBHB", EAP_CODE_SUCCESS, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_LEAP)
+        # hostapd will drop the next frame in the sequence
+
+        if ctx['num'] == 19:
+            logger.info("Test: Valid challenge")
+            return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_LEAP,
+                               1, 0, 8, 0, 0)
+        if ctx['num'] == 20:
+            logger.info("Test: Failure")
+            return struct.pack(">BBHB", EAP_CODE_FAILURE, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_LEAP)
+
+        return None
+
+    srv = start_radius_server(leap_handler)
+
+    try:
+        hapd = start_ap(apdev[0]['ifname'])
+
+        for i in range(0, 12):
+            dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                           eap="LEAP", identity="user", password="password",
+                           wait_connect=False)
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+            if ev is None:
+                raise Exception("Timeout on EAP start")
+            time.sleep(0.1)
+            if i == 10:
+                logger.info("Wait for additional roundtrip")
+                time.sleep(1)
+            dev[0].request("REMOVE_NETWORK all")
+    finally:
+        stop_radius_server(srv)
+
+def test_eap_proto_md5(dev, apdev):
+    """EAP-MD5 protocol tests"""
+    check_eap_capa(dev[0], "MD5")
+
+    def md5_handler(ctx, req):
+        logger.info("md5_handler - RX " + req.encode("hex"))
+        if 'num' not in ctx:
+            ctx['num'] = 0
+        ctx['num'] = ctx['num'] + 1
+        if 'id' not in ctx:
+            ctx['id'] = 1
+        ctx['id'] = (ctx['id'] + 1) % 256
+
+        if ctx['num'] == 1:
+            logger.info("Test: Missing payload")
+            return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_MD5)
+
+        if ctx['num'] == 2:
+            logger.info("Test: Zero-length challenge")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_MD5,
+                               0)
+
+        if ctx['num'] == 3:
+            logger.info("Test: Truncated challenge")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_MD5,
+                               1)
+
+        if ctx['num'] == 4:
+            logger.info("Test: Shortest possible challenge and name")
+            return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_MD5,
+                               1, 0xaa, ord('n'))
+
+        return None
+
+    srv = start_radius_server(md5_handler)
+
+    try:
+        hapd = start_ap(apdev[0]['ifname'])
+
+        for i in range(0, 4):
+            dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                           eap="MD5", identity="user", password="password",
+                           wait_connect=False)
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+            if ev is None:
+                raise Exception("Timeout on EAP start")
+            time.sleep(0.1)
+            dev[0].request("REMOVE_NETWORK all")
+    finally:
+        stop_radius_server(srv)
+
+def test_eap_proto_otp(dev, apdev):
+    """EAP-OTP protocol tests"""
+    def otp_handler(ctx, req):
+        logger.info("otp_handler - RX " + req.encode("hex"))
+        if 'num' not in ctx:
+            ctx['num'] = 0
+        ctx['num'] = ctx['num'] + 1
+        if 'id' not in ctx:
+            ctx['id'] = 1
+        ctx['id'] = (ctx['id'] + 1) % 256
+
+        if ctx['num'] == 1:
+            logger.info("Test: Empty payload")
+            return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_OTP)
+        if ctx['num'] == 2:
+            logger.info("Test: Success")
+            return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'],
+                               4)
+
+        if ctx['num'] == 3:
+            logger.info("Test: Challenge included")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_OTP,
+                               ord('A'))
+        if ctx['num'] == 4:
+            logger.info("Test: Success")
+            return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'],
+                               4)
+
+        return None
+
+    srv = start_radius_server(otp_handler)
+
+    try:
+        hapd = start_ap(apdev[0]['ifname'])
+
+        for i in range(0, 1):
+            dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                           eap="OTP", identity="user", password="password",
+                           wait_connect=False)
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+                                   timeout=15)
+            if ev is None:
+                raise Exception("Timeout on EAP start")
+            time.sleep(0.1)
+            dev[0].request("REMOVE_NETWORK all")
+
+        dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                       eap="OTP", identity="user", wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-REQ-OTP"])
+        if ev is None:
+            raise Exception("Request for password timed out")
+        id = ev.split(':')[0].split('-')[-1]
+        dev[0].request("CTRL-RSP-OTP-" + id + ":password")
+        ev = dev[0].wait_event("CTRL-EVENT-EAP-SUCCESS")
+        if ev is None:
+            raise Exception("Success not reported")
+    finally:
+        stop_radius_server(srv)
+
+EAP_GPSK_OPCODE_GPSK_1 = 1
+EAP_GPSK_OPCODE_GPSK_2 = 2
+EAP_GPSK_OPCODE_GPSK_3 = 3
+EAP_GPSK_OPCODE_GPSK_4 = 4
+EAP_GPSK_OPCODE_FAIL = 5
+EAP_GPSK_OPCODE_PROTECTED_FAIL = 6
+
+def test_eap_proto_gpsk(dev, apdev):
+    """EAP-GPSK protocol tests"""
+    def gpsk_handler(ctx, req):
+        logger.info("gpsk_handler - RX " + req.encode("hex"))
+        if 'num' not in ctx:
+            ctx['num'] = 0
+        ctx['num'] = ctx['num'] + 1
+        if 'id' not in ctx:
+            ctx['id'] = 1
+        ctx['id'] = (ctx['id'] + 1) % 256
+
+        idx = 0
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Missing payload")
+            return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_GPSK)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unknown opcode")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_GPSK,
+                               255)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected GPSK-3")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_3)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Too short GPSK-1")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Truncated ID_Server")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 1)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Missing RAND_Server")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Missing CSuite_List")
+            return struct.pack(">BBHBBH8L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Truncated CSuite_List")
+            return struct.pack(">BBHBBH8LH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               1)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Empty CSuite_List")
+            return struct.pack(">BBHBBH8LH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Invalid CSuite_List")
+            return struct.pack(">BBHBBH8LHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 1,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               1, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 No supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected GPSK-1")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite but too short key")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too short GPSK-3")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_3)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-3 Mismatch in RAND_Peer")
+            return struct.pack(">BBHBB8L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 32,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_3,
+                               0, 0, 0, 0, 0, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-3 Missing RAND_Server")
+            msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                              4 + 1 + 1 + 32,
+                              EAP_TYPE_GPSK,
+                              EAP_GPSK_OPCODE_GPSK_3)
+            msg += req[14:46]
+            return msg
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-3 Mismatch in RAND_Server")
+            msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                              4 + 1 + 1 + 32 + 32,
+                              EAP_TYPE_GPSK,
+                              EAP_GPSK_OPCODE_GPSK_3)
+            msg += req[14:46]
+            msg += struct.pack(">8L", 1, 1, 1, 1, 1, 1, 1, 1)
+            return msg
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-3 Missing ID_Server")
+            msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                              4 + 1 + 1 + 32 + 32,
+                              EAP_TYPE_GPSK,
+                              EAP_GPSK_OPCODE_GPSK_3)
+            msg += req[14:46]
+            msg += struct.pack(">8L", 0, 0, 0, 0, 0, 0, 0, 0)
+            return msg
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-3 Truncated ID_Server")
+            msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                              4 + 1 + 1 + 32 + 32 + 2,
+                              EAP_TYPE_GPSK,
+                              EAP_GPSK_OPCODE_GPSK_3)
+            msg += req[14:46]
+            msg += struct.pack(">8LH", 0, 0, 0, 0, 0, 0, 0, 0, 1)
+            return msg
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-3 Mismatch in ID_Server")
+            msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                              4 + 1 + 1 + 32 + 32 + 3,
+                              EAP_TYPE_GPSK,
+                              EAP_GPSK_OPCODE_GPSK_3)
+            msg += req[14:46]
+            msg += struct.pack(">8LHB", 0, 0, 0, 0, 0, 0, 0, 0, 1, ord('B'))
+            return msg
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBHB8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 3 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 1, ord('A'),
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-3 Mismatch in ID_Server (same length)")
+            msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                              4 + 1 + 1 + 32 + 32 + 3,
+                              EAP_TYPE_GPSK,
+                              EAP_GPSK_OPCODE_GPSK_3)
+            msg += req[15:47]
+            msg += struct.pack(">8LHB", 0, 0, 0, 0, 0, 0, 0, 0, 1, ord('B'))
+            return msg
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-3 Missing CSuite_Sel")
+            msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                              4 + 1 + 1 + 32 + 32 + 2,
+                              EAP_TYPE_GPSK,
+                              EAP_GPSK_OPCODE_GPSK_3)
+            msg += req[14:46]
+            msg += struct.pack(">8LH", 0, 0, 0, 0, 0, 0, 0, 0, 0)
+            return msg
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-3 Mismatch in CSuite_Sel")
+            msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                              4 + 1 + 1 + 32 + 32 + 2 + 6,
+                              EAP_TYPE_GPSK,
+                              EAP_GPSK_OPCODE_GPSK_3)
+            msg += req[14:46]
+            msg += struct.pack(">8LHLH", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2)
+            return msg
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-3 Missing len(PD_Payload_Block)")
+            msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                              4 + 1 + 1 + 32 + 32 + 2 + 6,
+                              EAP_TYPE_GPSK,
+                              EAP_GPSK_OPCODE_GPSK_3)
+            msg += req[14:46]
+            msg += struct.pack(">8LHLH", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
+            return msg
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-3 Truncated PD_Payload_Block")
+            msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                              4 + 1 + 1 + 32 + 32 + 2 + 6 + 2,
+                              EAP_TYPE_GPSK,
+                              EAP_GPSK_OPCODE_GPSK_3)
+            msg += req[14:46]
+            msg += struct.pack(">8LHLHH", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1)
+            return msg
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-3 Missing MAC")
+            msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                              4 + 1 + 1 + 32 + 32 + 2 + 6 + 3,
+                              EAP_TYPE_GPSK,
+                              EAP_GPSK_OPCODE_GPSK_3)
+            msg += req[14:46]
+            msg += struct.pack(">8LHLHHB",
+                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 123)
+            return msg
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-1 Supported CSuite")
+            return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 32 + 2 + 6,
+                               EAP_TYPE_GPSK,
+                               EAP_GPSK_OPCODE_GPSK_1, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               6, 0, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: GPSK-3 Incorrect MAC")
+            msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                              4 + 1 + 1 + 32 + 32 + 2 + 6 + 3 + 16,
+                              EAP_TYPE_GPSK,
+                              EAP_GPSK_OPCODE_GPSK_3)
+            msg += req[14:46]
+            msg += struct.pack(">8LHLHHB4L",
+                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 123,
+                               0, 0, 0, 0)
+            return msg
+
+        return None
+
+    srv = start_radius_server(gpsk_handler)
+
+    try:
+        hapd = start_ap(apdev[0]['ifname'])
+
+        for i in range(0, 27):
+            if i == 12:
+                pw = "short"
+            else:
+                pw = "abcdefghijklmnop0123456789abcdef"
+            dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                           eap="GPSK", identity="user", password=pw,
+                           wait_connect=False)
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+                                   timeout=15)
+            if ev is None:
+                raise Exception("Timeout on EAP start")
+            time.sleep(0.05)
+            dev[0].request("REMOVE_NETWORK all")
+    finally:
+        stop_radius_server(srv)
+
+EAP_EKE_ID = 1
+EAP_EKE_COMMIT = 2
+EAP_EKE_CONFIRM = 3
+EAP_EKE_FAILURE = 4
+
+def test_eap_proto_eke(dev, apdev):
+    """EAP-EKE protocol tests"""
+    def eke_handler(ctx, req):
+        logger.info("eke_handler - RX " + req.encode("hex"))
+        if 'num' not in ctx:
+            ctx['num'] = 0
+        ctx['num'] = ctx['num'] + 1
+        if 'id' not in ctx:
+            ctx['id'] = 1
+        ctx['id'] = (ctx['id'] + 1) % 256
+
+        idx = 0
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Missing payload")
+            return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_EKE)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unknown exchange")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_EKE,
+                               255)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: No NumProposals in EAP-EKE-ID/Request")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_ID)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: NumProposals=0 in EAP-EKE-ID/Request")
+            return struct.pack(">BBHBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 1,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_ID,
+                               0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Truncated Proposals list in EAP-EKE-ID/Request")
+            return struct.pack(">BBHBBBB4B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 4,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_ID,
+                               2, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unsupported proposals in EAP-EKE-ID/Request")
+            return struct.pack(">BBHBBBB4B4B4B4B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 4 * 4,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_ID,
+                               4, 0,
+                               0, 0, 0, 0,
+                               3, 0, 0, 0,
+                               3, 1, 0, 0,
+                               3, 1, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Missing IDType/Identity in EAP-EKE-ID/Request")
+            return struct.pack(">BBHBBBB4B4B4B4B4B",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 5 * 4,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_ID,
+                               5, 0,
+                               0, 0, 0, 0,
+                               3, 0, 0, 0,
+                               3, 1, 0, 0,
+                               3, 1, 1, 0,
+                               3, 1, 1, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid EAP-EKE-ID/Request")
+            return struct.pack(">BBHBBBB4BB",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 4 + 1,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_ID,
+                               1, 0,
+                               3, 1, 1, 1,
+                               255)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected EAP-EKE-ID/Request")
+            return struct.pack(">BBHBBBB4BB",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 4 + 1,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_ID,
+                               1, 0,
+                               3, 1, 1, 1,
+                               255)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid EAP-EKE-ID/Request")
+            return struct.pack(">BBHBBBB4BB",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 4 + 1,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_ID,
+                               1, 0,
+                               3, 1, 1, 1,
+                               255)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected EAP-EKE-Confirm/Request")
+            return struct.pack(">BBHBB",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_CONFIRM)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too short EAP-EKE-Failure/Request")
+            return struct.pack(">BBHBB",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_FAILURE)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected EAP-EKE-Commit/Request")
+            return struct.pack(">BBHBB",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_COMMIT)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid EAP-EKE-ID/Request")
+            return struct.pack(">BBHBBBB4BB",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 4 + 1,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_ID,
+                               1, 0,
+                               3, 1, 1, 1,
+                               255)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too short EAP-EKE-Commit/Request")
+            return struct.pack(">BBHBB",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_COMMIT)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid EAP-EKE-ID/Request")
+            return struct.pack(">BBHBBBB4BB",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 4 + 1,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_ID,
+                               1, 0,
+                               1, 1, 1, 1,
+                               255)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: All zeroes DHComponent_S and empty CBvalue in EAP-EKE-Commit/Request")
+            return struct.pack(">BBHBB4L32L",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 16 + 128,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_COMMIT,
+                               0, 0, 0, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too short EAP-EKE-Confirm/Request")
+            return struct.pack(">BBHBB",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_CONFIRM)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid EAP-EKE-ID/Request")
+            return struct.pack(">BBHBBBB4BB",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2 + 4 + 1,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_ID,
+                               1, 0,
+                               1, 1, 1, 1,
+                               255)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: All zeroes DHComponent_S and empty CBvalue in EAP-EKE-Commit/Request")
+            return struct.pack(">BBHBB4L32L",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 16 + 128,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_COMMIT,
+                               0, 0, 0, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid PNonce_PS and Auth_S values in EAP-EKE-Confirm/Request")
+            return struct.pack(">BBHBB4L8L5L5L",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 16 + 2 * 16 + 20 + 20,
+                               EAP_TYPE_EKE,
+                               EAP_EKE_CONFIRM,
+                               0, 0, 0, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               0, 0, 0, 0, 0,
+                               0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        return None
+
+    srv = start_radius_server(eke_handler)
+
+    try:
+        hapd = start_ap(apdev[0]['ifname'])
+
+        for i in range(0, 14):
+            dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                           eap="EKE", identity="user", password="password",
+                           wait_connect=False)
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+                                   timeout=15)
+            if ev is None:
+                raise Exception("Timeout on EAP start")
+            if i in [ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ]:
+                ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
+                                       timeout=10)
+                if ev is None:
+                    raise Exception("Timeout on EAP failure")
+            else:
+                time.sleep(0.05)
+            dev[0].request("REMOVE_NETWORK all")
+            dev[0].dump_monitor()
+    finally:
+        stop_radius_server(srv)
+
+EAP_PAX_OP_STD_1 = 0x01
+EAP_PAX_OP_STD_2 = 0x02
+EAP_PAX_OP_STD_3 = 0x03
+EAP_PAX_OP_SEC_1 = 0x11
+EAP_PAX_OP_SEC_2 = 0x12
+EAP_PAX_OP_SEC_3 = 0x13
+EAP_PAX_OP_SEC_4 = 0x14
+EAP_PAX_OP_SEC_5 = 0x15
+EAP_PAX_OP_ACK = 0x21
+
+EAP_PAX_FLAGS_MF = 0x01
+EAP_PAX_FLAGS_CE = 0x02
+EAP_PAX_FLAGS_AI = 0x04
+
+EAP_PAX_MAC_HMAC_SHA1_128 = 0x01
+EAP_PAX_HMAC_SHA256_128 = 0x02
+
+EAP_PAX_DH_GROUP_NONE = 0x00
+EAP_PAX_DH_GROUP_2048_MODP = 0x01
+EAP_PAX_DH_GROUP_3072_MODP = 0x02
+EAP_PAX_DH_GROUP_NIST_ECC_P_256 = 0x03
+
+EAP_PAX_PUBLIC_KEY_NONE = 0x00
+EAP_PAX_PUBLIC_KEY_RSAES_OAEP = 0x01
+EAP_PAX_PUBLIC_KEY_RSA_PKCS1_V1_5 = 0x02
+EAP_PAX_PUBLIC_KEY_EL_GAMAL_NIST_ECC = 0x03
+
+EAP_PAX_ADE_VENDOR_SPECIFIC = 0x01
+EAP_PAX_ADE_CLIENT_CHANNEL_BINDING = 0x02
+EAP_PAX_ADE_SERVER_CHANNEL_BINDING = 0x03
+
+def test_eap_proto_pax(dev, apdev):
+    """EAP-PAX protocol tests"""
+    def pax_std_1(ctx):
+            logger.info("Test: STD-1")
+            ctx['id'] = 10
+            return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 2 + 32 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+                               32, 0, 0, 0, 0, 0, 0, 0, 0,
+                               0x16, 0xc9, 0x08, 0x9d, 0x98, 0xa5, 0x6e, 0x1f,
+                               0xf0, 0xac, 0xcf, 0xc4, 0x66, 0xcd, 0x2d, 0xbf)
+
+    def pax_handler(ctx, req):
+        logger.info("pax_handler - RX " + req.encode("hex"))
+        if 'num' not in ctx:
+            ctx['num'] = 0
+        ctx['num'] = ctx['num'] + 1
+        if 'id' not in ctx:
+            ctx['id'] = 1
+        ctx['id'] = (ctx['id'] + 1) % 256
+
+        idx = 0
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Missing payload")
+            return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_PAX)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Minimum length payload")
+            return struct.pack(">BBHB4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 16,
+                               EAP_TYPE_PAX,
+                               0, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unsupported MAC ID")
+            return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, 0, 255, EAP_PAX_DH_GROUP_NONE,
+                               EAP_PAX_PUBLIC_KEY_NONE,
+                               0, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unsupported DH Group ID")
+            return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+                               255, EAP_PAX_PUBLIC_KEY_NONE,
+                               0, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unsupported Public Key ID")
+            return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_NONE, 255,
+                               0, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: More fragments")
+            return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, EAP_PAX_FLAGS_MF,
+                               EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+                               0, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid ICV")
+            return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+                               0, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid ICV in short frame")
+            return struct.pack(">BBHBBBBBB3L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 12,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+                               0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Correct ICV - unsupported op_code")
+            ctx['id'] = 10
+            return struct.pack(">BBHBBBBBB16B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 16,
+                               EAP_TYPE_PAX,
+                               255, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+                               0x90, 0x78, 0x97, 0x38, 0x29, 0x94, 0x32, 0xd4,
+                               0x81, 0x27, 0xe0, 0xf6, 0x3b, 0x0d, 0xb2, 0xb2)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Correct ICV - CE flag in STD-1")
+            ctx['id'] = 10
+            return struct.pack(">BBHBBBBBB16B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, EAP_PAX_FLAGS_CE,
+                               EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+                               0x9c, 0x98, 0xb4, 0x0b, 0x94, 0x90, 0xde, 0x88,
+                               0xb7, 0x72, 0x63, 0x44, 0x1d, 0xe3, 0x7c, 0x5c)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Correct ICV - too short STD-1 payload")
+            ctx['id'] = 10
+            return struct.pack(">BBHBBBBBB16B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+                               0xda, 0xab, 0x2c, 0xe7, 0x84, 0x41, 0xb5, 0x5c,
+                               0xee, 0xcf, 0x62, 0x03, 0xc5, 0x69, 0xcb, 0xf4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Correct ICV - incorrect A length in STD-1")
+            ctx['id'] = 10
+            return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 2 + 32 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+                               0, 0, 0, 0, 0, 0, 0, 0, 0,
+                               0xc4, 0xb0, 0x81, 0xe4, 0x6c, 0x8c, 0x20, 0x23,
+                               0x60, 0x46, 0x89, 0xea, 0x94, 0x60, 0xf3, 0x2a)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Correct ICV - extra data in STD-1")
+            ctx['id'] = 10
+            return struct.pack(">BBHBBBBBBH8LB16B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 2 + 32 + 1 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+                               32, 0, 0, 0, 0, 0, 0, 0, 0,
+                               1,
+                               0x61, 0x49, 0x65, 0x37, 0x21, 0xe8, 0xd8, 0xbf,
+                               0xf3, 0x02, 0x01, 0xe5, 0x42, 0x51, 0xd3, 0x34)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected STD-1")
+            return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 2 + 32 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+                               32, 0, 0, 0, 0, 0, 0, 0, 0,
+                               0xe5, 0x1d, 0xbf, 0xb8, 0x70, 0x20, 0x5c, 0xba,
+                               0x41, 0xbb, 0x34, 0xda, 0x1a, 0x08, 0xe6, 0x8d)
+
+        idx += 1
+        if ctx['num'] == idx:
+            return pax_std_1(ctx)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: MAC ID changed during session")
+            return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 2 + 32 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, 0, EAP_PAX_HMAC_SHA256_128,
+                               EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+                               32, 0, 0, 0, 0, 0, 0, 0, 0,
+                               0xee, 0x00, 0xbf, 0xb8, 0x70, 0x20, 0x5c, 0xba,
+                               0x41, 0xbb, 0x34, 0xda, 0x1a, 0x08, 0xe6, 0x8d)
+
+        idx += 1
+        if ctx['num'] == idx:
+            return pax_std_1(ctx)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: DH Group ID changed during session")
+            return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 2 + 32 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_2048_MODP,
+                               EAP_PAX_PUBLIC_KEY_NONE,
+                               32, 0, 0, 0, 0, 0, 0, 0, 0,
+                               0xee, 0x01, 0xbf, 0xb8, 0x70, 0x20, 0x5c, 0xba,
+                               0x41, 0xbb, 0x34, 0xda, 0x1a, 0x08, 0xe6, 0x8d)
+
+        idx += 1
+        if ctx['num'] == idx:
+            return pax_std_1(ctx)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Public Key ID changed during session")
+            return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 2 + 32 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_NONE,
+                               EAP_PAX_PUBLIC_KEY_RSAES_OAEP,
+                               32, 0, 0, 0, 0, 0, 0, 0, 0,
+                               0xee, 0x02, 0xbf, 0xb8, 0x70, 0x20, 0x5c, 0xba,
+                               0x41, 0xbb, 0x34, 0xda, 0x1a, 0x08, 0xe6, 0x8d)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected STD-3")
+            ctx['id'] = 10
+            return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 2 + 32 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_3, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+                               32, 0, 0, 0, 0, 0, 0, 0, 0,
+                               0x47, 0xbb, 0xc0, 0xf9, 0xb9, 0x69, 0xf5, 0xcb,
+                               0x3a, 0xe8, 0xe7, 0xd6, 0x80, 0x28, 0xf2, 0x59)
+
+        idx += 1
+        if ctx['num'] == idx:
+            return pax_std_1(ctx)
+        idx += 1
+        if ctx['num'] == idx:
+            # TODO: MAC calculation; for now, this gets dropped due to incorrect
+            # ICV
+            logger.info("Test: STD-3 with CE flag")
+            return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 5 + 2 + 32 + 16,
+                               EAP_TYPE_PAX,
+                               EAP_PAX_OP_STD_3, EAP_PAX_FLAGS_CE,
+                               EAP_PAX_MAC_HMAC_SHA1_128,
+                               EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+                               32, 0, 0, 0, 0, 0, 0, 0, 0,
+                               0x8a, 0xc2, 0xf9, 0xf4, 0x8b, 0x75, 0x72, 0xa2,
+                               0x4d, 0xd3, 0x1e, 0x54, 0x77, 0x04, 0x05, 0xe2)
+
+        idx += 1
+        if ctx['num'] & 0x1 == idx & 0x1:
+            logger.info("Test: Default request")
+            return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_PAX)
+        else:
+            logger.info("Test: Default EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+    srv = start_radius_server(pax_handler)
+
+    try:
+        hapd = start_ap(apdev[0]['ifname'])
+
+        for i in range(0, 18):
+            dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                           eap="PAX", identity="user",
+                           password_hex="0123456789abcdef0123456789abcdef",
+                           wait_connect=False)
+            logger.info("Waiting for EAP method to start")
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+                                   timeout=15)
+            if ev is None:
+                raise Exception("Timeout on EAP start")
+            time.sleep(0.05)
+            dev[0].request("REMOVE_NETWORK all")
+            dev[0].dump_monitor()
+
+        logger.info("Too short password")
+        dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                       eap="PAX", identity="user",
+                       password_hex="0123456789abcdef0123456789abcd",
+                       wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP start")
+        time.sleep(0.1)
+        dev[0].request("REMOVE_NETWORK all")
+        dev[0].dump_monitor()
+
+        logger.info("No password")
+        dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                       eap="PAX", identity="user",
+                       wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP start")
+        time.sleep(0.1)
+        dev[0].request("REMOVE_NETWORK all")
+        dev[0].dump_monitor()
+    finally:
+        stop_radius_server(srv)
+
+def test_eap_proto_psk(dev, apdev):
+    """EAP-PSK protocol tests"""
+    def psk_handler(ctx, req):
+        logger.info("psk_handler - RX " + req.encode("hex"))
+        if 'num' not in ctx:
+            ctx['num'] = 0
+        ctx['num'] = ctx['num'] + 1
+        if 'id' not in ctx:
+            ctx['id'] = 1
+        ctx['id'] = (ctx['id'] + 1) % 256
+
+        idx = 0
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Missing payload")
+            return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_PSK)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Non-zero T in first message")
+            return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 16,
+                               EAP_TYPE_PSK, 0xc0, 0, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid first message")
+            return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 16,
+                               EAP_TYPE_PSK, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too short third message")
+            return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_PSK)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid first message")
+            return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 16,
+                               EAP_TYPE_PSK, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Incorrect T in third message")
+            return struct.pack(">BBHBB4L4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 16 + 16,
+                               EAP_TYPE_PSK, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid first message")
+            return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 16,
+                               EAP_TYPE_PSK, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Missing PCHANNEL in third message")
+            return struct.pack(">BBHBB4L4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 16 + 16,
+                               EAP_TYPE_PSK, 0x80, 0, 0, 0, 0, 0, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid first message")
+            return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 16,
+                               EAP_TYPE_PSK, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalic MAC_S in third message")
+            return struct.pack(">BBHBB4L4L5LB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 16 + 16 + 21,
+                               EAP_TYPE_PSK, 0x80, 0, 0, 0, 0, 0, 0, 0, 0,
+                               0, 0, 0, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid first message")
+            return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 16,
+                               EAP_TYPE_PSK, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        return None
+
+    srv = start_radius_server(psk_handler)
+
+    try:
+        hapd = start_ap(apdev[0]['ifname'])
+
+        for i in range(0, 6):
+            dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                           eap="PSK", identity="user",
+                           password_hex="0123456789abcdef0123456789abcdef",
+                           wait_connect=False)
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+                                   timeout=15)
+            if ev is None:
+                raise Exception("Timeout on EAP start")
+            time.sleep(0.1)
+            dev[0].request("REMOVE_NETWORK all")
+
+        logger.info("Test: Invalid PSK length")
+        dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                       eap="PSK", identity="user",
+                       password_hex="0123456789abcdef0123456789abcd",
+                       wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+                               timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP start")
+        time.sleep(0.1)
+        dev[0].request("REMOVE_NETWORK all")
+    finally:
+        stop_radius_server(srv)
+
+EAP_SIM_SUBTYPE_START = 10
+EAP_SIM_SUBTYPE_CHALLENGE = 11
+EAP_SIM_SUBTYPE_NOTIFICATION = 12
+EAP_SIM_SUBTYPE_REAUTHENTICATION = 13
+EAP_SIM_SUBTYPE_CLIENT_ERROR = 14
+
+EAP_AKA_SUBTYPE_CHALLENGE = 1
+EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT = 2
+EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE = 4
+EAP_AKA_SUBTYPE_IDENTITY = 5
+EAP_AKA_SUBTYPE_NOTIFICATION = 12
+EAP_AKA_SUBTYPE_REAUTHENTICATION = 13
+EAP_AKA_SUBTYPE_CLIENT_ERROR = 14
+
+EAP_SIM_AT_RAND = 1
+EAP_SIM_AT_AUTN = 2
+EAP_SIM_AT_RES = 3
+EAP_SIM_AT_AUTS = 4
+EAP_SIM_AT_PADDING = 6
+EAP_SIM_AT_NONCE_MT = 7
+EAP_SIM_AT_PERMANENT_ID_REQ = 10
+EAP_SIM_AT_MAC = 11
+EAP_SIM_AT_NOTIFICATION = 12
+EAP_SIM_AT_ANY_ID_REQ = 13
+EAP_SIM_AT_IDENTITY = 14
+EAP_SIM_AT_VERSION_LIST = 15
+EAP_SIM_AT_SELECTED_VERSION = 16
+EAP_SIM_AT_FULLAUTH_ID_REQ = 17
+EAP_SIM_AT_COUNTER = 19
+EAP_SIM_AT_COUNTER_TOO_SMALL = 20
+EAP_SIM_AT_NONCE_S = 21
+EAP_SIM_AT_CLIENT_ERROR_CODE = 22
+EAP_SIM_AT_KDF_INPUT = 23
+EAP_SIM_AT_KDF = 24
+EAP_SIM_AT_IV = 129
+EAP_SIM_AT_ENCR_DATA = 130
+EAP_SIM_AT_NEXT_PSEUDONYM = 132
+EAP_SIM_AT_NEXT_REAUTH_ID = 133
+EAP_SIM_AT_CHECKCODE = 134
+EAP_SIM_AT_RESULT_IND = 135
+EAP_SIM_AT_BIDDING = 136
+
+def test_eap_proto_aka(dev, apdev):
+    """EAP-AKA protocol tests"""
+    def aka_handler(ctx, req):
+        logger.info("aka_handler - RX " + req.encode("hex"))
+        if 'num' not in ctx:
+            ctx['num'] = 0
+        ctx['num'] = ctx['num'] + 1
+        if 'id' not in ctx:
+            ctx['id'] = 1
+        ctx['id'] = (ctx['id'] + 1) % 256
+
+        idx = 0
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Missing payload")
+            return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_AKA)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unknown subtype")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_AKA, 255, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Client Error")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CLIENT_ERROR, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too short attribute header")
+            return struct.pack(">BBHBBHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 3,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0, 255)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Truncated attribute")
+            return struct.pack(">BBHBBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0, 255,
+                               255)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too short attribute data")
+            return struct.pack(">BBHBBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0, 255,
+                               0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Skippable/non-skippable unrecognzized attribute")
+            return struct.pack(">BBHBBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 10,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               255, 1, 0, 127, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request without ID type")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request ANY_ID")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request ANY_ID (duplicate)")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request ANY_ID")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request FULLAUTH_ID")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request FULLAUTH_ID (duplicate)")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request ANY_ID")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request FULLAUTH_ID")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request PERMANENT_ID")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_PERMANENT_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request PERMANENT_ID (duplicate)")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_PERMANENT_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with no attributes")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: AKA Challenge with BIDDING")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_BIDDING, 1, 0x8000)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification with no attributes")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification indicating success, but no MAC")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
+                               EAP_SIM_AT_NOTIFICATION, 1, 32768)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification indicating success, but invalid MAC value")
+            return struct.pack(">BBHBBHBBHBBH4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4 + 20,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
+                               EAP_SIM_AT_NOTIFICATION, 1, 32768,
+                               EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification indicating success with zero-key MAC")
+            return struct.pack(">BBHBBHBBHBBH16B", EAP_CODE_REQUEST,
+                               ctx['id'] - 2,
+                               4 + 1 + 3 + 4 + 20,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
+                               EAP_SIM_AT_NOTIFICATION, 1, 32768,
+                               EAP_SIM_AT_MAC, 5, 0,
+                               0xbe, 0x2e, 0xbb, 0xa9, 0xfa, 0x2e, 0x82, 0x36,
+                               0x37, 0x8c, 0x32, 0x41, 0xb7, 0xc7, 0x58, 0xa3)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Success")
+            return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification before auth")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
+                               EAP_SIM_AT_NOTIFICATION, 1, 16384)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification before auth")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
+                               EAP_SIM_AT_NOTIFICATION, 1, 16385)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification with unrecognized non-failure")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
+                               EAP_SIM_AT_NOTIFICATION, 1, 0xc000)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification before auth (duplicate)")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
+                               EAP_SIM_AT_NOTIFICATION, 1, 0xc000)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Re-authentication (unexpected) with no attributes")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_REAUTHENTICATION,
+                               0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: AKA Challenge with Checkcode claiming identity round was used")
+            return struct.pack(">BBHBBHBBH5L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 24,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_CHECKCODE, 6, 0, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request ANY_ID")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: AKA Challenge with Checkcode claiming no identity round was used")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_CHECKCODE, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request ANY_ID")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: AKA Challenge with mismatching Checkcode value")
+            return struct.pack(">BBHBBHBBH5L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 24,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_CHECKCODE, 6, 0, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Re-authentication (unexpected) with Checkcode claimin identity round was used")
+            return struct.pack(">BBHBBHBBH5L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 24,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_REAUTHENTICATION,
+                               0,
+                               EAP_SIM_AT_CHECKCODE, 6, 0, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_RAND length")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_RAND, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_AUTN length")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_AUTN, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unencrypted AT_PADDING")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_PADDING, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_NONCE_MT length")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_NONCE_MT, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_MAC length")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_MAC, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_NOTIFICATION length")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_NOTIFICATION, 2, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: AT_IDENTITY overflow")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_IDENTITY, 1, 0xffff)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected AT_VERSION_LIST")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_VERSION_LIST, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_SELECTED_VERSION length")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_SELECTED_VERSION, 2, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unencrypted AT_COUNTER")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_COUNTER, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unencrypted AT_COUNTER_TOO_SMALL")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_COUNTER_TOO_SMALL, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unencrypted AT_NONCE_S")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_NONCE_S, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_CLIENT_ERROR_CODE length")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_CLIENT_ERROR_CODE, 2, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_IV length")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_IV, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_ENCR_DATA length")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_ENCR_DATA, 2, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unencrypted AT_NEXT_PSEUDONYM")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_NEXT_PSEUDONYM, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unencrypted AT_NEXT_REAUTH_ID")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_NEXT_REAUTH_ID, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_RES length")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_RES, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_RES length")
+            return struct.pack(">BBHBBHBBH5L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 24,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_RES, 6, 0xffff, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_AUTS length")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_AUTS, 2, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_CHECKCODE length")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_CHECKCODE, 2, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_RESULT_IND length")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_RESULT_IND, 2, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected AT_KDF_INPUT")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected AT_KDF")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_KDF, 2, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_BIDDING length")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_BIDDING, 2, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        return None
+
+    srv = start_radius_server(aka_handler)
+
+    try:
+        hapd = start_ap(apdev[0]['ifname'])
+
+        for i in range(0, 49):
+            eap = "AKA AKA'" if i == 11 else "AKA"
+            dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                           eap=eap, identity="0232010000000000",
+                           password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+                           wait_connect=False)
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+                                   timeout=15)
+            if ev is None:
+                raise Exception("Timeout on EAP start")
+            if i in [ 0, 15 ]:
+                time.sleep(0.1)
+            else:
+                ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
+                                       timeout=10)
+                if ev is None:
+                    raise Exception("Timeout on EAP failure")
+            dev[0].request("REMOVE_NETWORK all")
+            dev[0].dump_monitor()
+    finally:
+        stop_radius_server(srv)
+
+def test_eap_proto_aka_prime(dev, apdev):
+    """EAP-AKA' protocol tests"""
+    def aka_prime_handler(ctx, req):
+        logger.info("aka_prime_handler - RX " + req.encode("hex"))
+        if 'num' not in ctx:
+            ctx['num'] = 0
+        ctx['num'] = ctx['num'] + 1
+        if 'id' not in ctx:
+            ctx['id'] = 1
+        ctx['id'] = (ctx['id'] + 1) % 256
+
+        idx = 0
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Missing payload")
+            return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_AKA_PRIME)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with no attributes")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with empty AT_KDF_INPUT")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with AT_KDF_INPUT")
+            return struct.pack(">BBHBBHBBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+                               ord('c'), ord('d'))
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with duplicated KDF")
+            return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 3 * 4,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+                               ord('c'), ord('d'),
+                               EAP_SIM_AT_KDF, 1, 1,
+                               EAP_SIM_AT_KDF, 1, 2,
+                               EAP_SIM_AT_KDF, 1, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with multiple KDF proposals")
+            return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 3 * 4,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+                               ord('c'), ord('d'),
+                               EAP_SIM_AT_KDF, 1, 255,
+                               EAP_SIM_AT_KDF, 1, 254,
+                               EAP_SIM_AT_KDF, 1, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with incorrect KDF selected")
+            return struct.pack(">BBHBBHBBHBBBBBBHBBHBBHBBH",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4 * 4,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+                               ord('c'), ord('d'),
+                               EAP_SIM_AT_KDF, 1, 255,
+                               EAP_SIM_AT_KDF, 1, 255,
+                               EAP_SIM_AT_KDF, 1, 254,
+                               EAP_SIM_AT_KDF, 1, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with multiple KDF proposals")
+            return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 3 * 4,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+                               ord('c'), ord('d'),
+                               EAP_SIM_AT_KDF, 1, 255,
+                               EAP_SIM_AT_KDF, 1, 254,
+                               EAP_SIM_AT_KDF, 1, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with selected KDF not duplicated")
+            return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 3 * 4,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+                               ord('c'), ord('d'),
+                               EAP_SIM_AT_KDF, 1, 1,
+                               EAP_SIM_AT_KDF, 1, 255,
+                               EAP_SIM_AT_KDF, 1, 254)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with multiple KDF proposals")
+            return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 3 * 4,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+                               ord('c'), ord('d'),
+                               EAP_SIM_AT_KDF, 1, 255,
+                               EAP_SIM_AT_KDF, 1, 254,
+                               EAP_SIM_AT_KDF, 1, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with selected KDF duplicated (missing MAC, RAND, AUTN)")
+            return struct.pack(">BBHBBHBBHBBBBBBHBBHBBHBBH",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4 * 4,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+                               ord('c'), ord('d'),
+                               EAP_SIM_AT_KDF, 1, 1,
+                               EAP_SIM_AT_KDF, 1, 255,
+                               EAP_SIM_AT_KDF, 1, 254,
+                               EAP_SIM_AT_KDF, 1, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with multiple unsupported KDF proposals")
+            return struct.pack(">BBHBBHBBHBBBBBBHBBH",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 2 * 4,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+                               ord('c'), ord('d'),
+                               EAP_SIM_AT_KDF, 1, 255,
+                               EAP_SIM_AT_KDF, 1, 254)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with multiple KDF proposals")
+            return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 3 * 4,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+                               ord('c'), ord('d'),
+                               EAP_SIM_AT_KDF, 1, 255,
+                               EAP_SIM_AT_KDF, 1, 254,
+                               EAP_SIM_AT_KDF, 1, 1)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with invalid MAC, RAND, AUTN values)")
+            return struct.pack(">BBHBBHBBHBBBBBBHBBHBBHBBHBBH4LBBH4LBBH4L",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4 * 4 + 20 + 20 + 20,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+                               ord('c'), ord('d'),
+                               EAP_SIM_AT_KDF, 1, 1,
+                               EAP_SIM_AT_KDF, 1, 255,
+                               EAP_SIM_AT_KDF, 1, 254,
+                               EAP_SIM_AT_KDF, 1, 1,
+                               EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0,
+                               EAP_SIM_AT_RAND, 5, 0, 0, 0, 0, 0,
+                               EAP_SIM_AT_AUTN, 5, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge - AMF separation bit not set)")
+            return struct.pack(">BBHBBHBBHBBBBBBHBBH4LBBH4LBBH4L",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4 + 20 + 20 + 20,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+                               ord('c'), ord('d'),
+                               EAP_SIM_AT_KDF, 1, 1,
+                               EAP_SIM_AT_MAC, 5, 0, 1, 2, 3, 4,
+                               EAP_SIM_AT_RAND, 5, 0, 5, 6, 7, 8,
+                               EAP_SIM_AT_AUTN, 5, 0, 9, 10,
+                               0x2fda8ef7, 0xbba518cc)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge - Invalid MAC")
+            return struct.pack(">BBHBBHBBHBBBBBBHBBH4LBBH4LBBH4L",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4 + 20 + 20 + 20,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+                               ord('c'), ord('d'),
+                               EAP_SIM_AT_KDF, 1, 1,
+                               EAP_SIM_AT_MAC, 5, 0, 1, 2, 3, 4,
+                               EAP_SIM_AT_RAND, 5, 0, 5, 6, 7, 8,
+                               EAP_SIM_AT_AUTN, 5, 0, 0xffffffff, 0xffffffff,
+                               0xd1f90322, 0x40514cb4)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge - Valid MAC")
+            return struct.pack(">BBHBBHBBHBBBBBBHBBH4LBBH4LBBH4L",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4 + 20 + 20 + 20,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+                               ord('c'), ord('d'),
+                               EAP_SIM_AT_KDF, 1, 1,
+                               EAP_SIM_AT_MAC, 5, 0,
+                               0xf4a3c1d3, 0x7c901401, 0x34bd8b01, 0x6f7fa32f,
+                               EAP_SIM_AT_RAND, 5, 0, 5, 6, 7, 8,
+                               EAP_SIM_AT_AUTN, 5, 0, 0xffffffff, 0xffffffff,
+                               0xd1f90322, 0x40514cb4)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_KDF_INPUT length")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_KDF_INPUT, 2, 0xffff, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid AT_KDF length")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_IDENTITY, 0,
+                               EAP_SIM_AT_KDF, 2, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Challenge with large number of KDF proposals")
+            return struct.pack(">BBHBBHBBHBBHBBHBBHBBHBBHBBHBBHBBHBBHBBHBBH",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 12 * 4,
+                               EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_KDF, 1, 255,
+                               EAP_SIM_AT_KDF, 1, 254,
+                               EAP_SIM_AT_KDF, 1, 253,
+                               EAP_SIM_AT_KDF, 1, 252,
+                               EAP_SIM_AT_KDF, 1, 251,
+                               EAP_SIM_AT_KDF, 1, 250,
+                               EAP_SIM_AT_KDF, 1, 249,
+                               EAP_SIM_AT_KDF, 1, 248,
+                               EAP_SIM_AT_KDF, 1, 247,
+                               EAP_SIM_AT_KDF, 1, 246,
+                               EAP_SIM_AT_KDF, 1, 245,
+                               EAP_SIM_AT_KDF, 1, 244)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        return None
+
+    srv = start_radius_server(aka_prime_handler)
+
+    try:
+        hapd = start_ap(apdev[0]['ifname'])
+
+        for i in range(0, 16):
+            dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                           eap="AKA'", identity="6555444333222111",
+                           password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
+                           wait_connect=False)
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+                                   timeout=15)
+            if ev is None:
+                raise Exception("Timeout on EAP start")
+            if i in [ 0 ]:
+                time.sleep(0.1)
+            else:
+                ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
+                                       timeout=10)
+                if ev is None:
+                    raise Exception("Timeout on EAP failure")
+            dev[0].request("REMOVE_NETWORK all")
+            dev[0].dump_monitor()
+    finally:
+        stop_radius_server(srv)
+
+def test_eap_proto_sim(dev, apdev):
+    """EAP-SIM protocol tests"""
+    def sim_handler(ctx, req):
+        logger.info("sim_handler - RX " + req.encode("hex"))
+        if 'num' not in ctx:
+            ctx['num'] = 0
+        ctx['num'] = ctx['num'] + 1
+        if 'id' not in ctx:
+            ctx['id'] = 1
+        ctx['id'] = (ctx['id'] + 1) % 256
+
+        idx = 0
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Missing payload")
+            return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_SIM)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected AT_AUTN")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_AUTN, 2, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too short AT_VERSION_LIST")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_VERSION_LIST, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: AT_VERSION_LIST overflow")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_VERSION_LIST, 1, 0xffff)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected AT_AUTS")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_AUTS, 2, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected AT_CHECKCODE")
+            return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_CHECKCODE, 2, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: No AT_VERSION_LIST in Start")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: No support version in AT_VERSION_LIST")
+            return struct.pack(">BBHBBHBBH4B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_VERSION_LIST, 2, 3, 2, 3, 4, 5)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request without ID type")
+            return struct.pack(">BBHBBHBBH2H", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request ANY_ID")
+            return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+                               EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request ANY_ID (duplicate)")
+            return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+                               EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request ANY_ID")
+            return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+                               EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request FULLAUTH_ID")
+            return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+                               EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request FULLAUTH_ID (duplicate)")
+            return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+                               EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request ANY_ID")
+            return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+                               EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request FULLAUTH_ID")
+            return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+                               EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request PERMANENT_ID")
+            return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+                               EAP_SIM_AT_PERMANENT_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Identity request PERMANENT_ID (duplicate)")
+            return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 8 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+                               EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+                               EAP_SIM_AT_PERMANENT_ID_REQ, 1, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: No AT_MAC and AT_RAND in Challenge")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: No AT_RAND in Challenge")
+            return struct.pack(">BBHBBHBBH4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 20,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Insufficient number of challenges in Challenge")
+            return struct.pack(">BBHBBHBBH4LBBH4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 20 + 20,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_RAND, 5, 0, 0, 0, 0, 0,
+                               EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too many challenges in Challenge")
+            return struct.pack(">BBHBBHBBH4L4L4L4LBBH4L", EAP_CODE_REQUEST,
+                               ctx['id'],
+                               4 + 1 + 3 + 4 + 4 * 16 + 20,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_RAND, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                               0, 0, 0, 0, 0, 0, 0, 0,
+                               EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Same RAND multiple times in Challenge")
+            return struct.pack(">BBHBBHBBH4L4L4LBBH4L", EAP_CODE_REQUEST,
+                               ctx['id'],
+                               4 + 1 + 3 + 4 + 3 * 16 + 20,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0,
+                               EAP_SIM_AT_RAND, 13, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+                               0, 0, 0, 0,
+                               EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification with no attributes")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification indicating success, but no MAC")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
+                               EAP_SIM_AT_NOTIFICATION, 1, 32768)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification indicating success, but invalid MAC value")
+            return struct.pack(">BBHBBHBBHBBH4L", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4 + 20,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
+                               EAP_SIM_AT_NOTIFICATION, 1, 32768,
+                               EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification before auth")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
+                               EAP_SIM_AT_NOTIFICATION, 1, 16384)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification before auth")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
+                               EAP_SIM_AT_NOTIFICATION, 1, 16385)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification with unrecognized non-failure")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
+                               EAP_SIM_AT_NOTIFICATION, 1, 0xc000)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Notification before auth (duplicate)")
+            return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3 + 4,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
+                               EAP_SIM_AT_NOTIFICATION, 1, 0xc000)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Re-authentication (unexpected) with no attributes")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_REAUTHENTICATION,
+                               0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Client Error")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CLIENT_ERROR, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unknown subtype")
+            return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 3,
+                               EAP_TYPE_SIM, 255, 0)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        return None
+
+    srv = start_radius_server(sim_handler)
+
+    try:
+        hapd = start_ap(apdev[0]['ifname'])
+
+        for i in range(0, 25):
+            dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                           eap="SIM", identity="1232010000000000",
+                           password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+                           wait_connect=False)
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+                                   timeout=15)
+            if ev is None:
+                raise Exception("Timeout on EAP start")
+            if i in [ 0 ]:
+                time.sleep(0.1)
+            else:
+                ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
+                                       timeout=10)
+                if ev is None:
+                    raise Exception("Timeout on EAP failure")
+            dev[0].request("REMOVE_NETWORK all")
+            dev[0].dump_monitor()
+    finally:
+        stop_radius_server(srv)
+
+def test_eap_proto_ikev2(dev, apdev):
+    """EAP-IKEv2 protocol tests"""
+    check_eap_capa(dev[0], "IKEV2")
+    def ikev2_handler(ctx, req):
+        logger.info("ikev2_handler - RX " + req.encode("hex"))
+        if 'num' not in ctx:
+            ctx['num'] = 0
+        ctx['num'] = ctx['num'] + 1
+        if 'id' not in ctx:
+            ctx['id'] = 1
+        ctx['id'] = (ctx['id'] + 1) % 256
+
+        idx = 0
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Missing payload")
+            return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1,
+                               EAP_TYPE_IKEV2)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Truncated Message Length field")
+            return struct.pack(">BBHBB3B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 3,
+                               EAP_TYPE_IKEV2, 0x80, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too short Message Length value")
+            return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 4 + 1,
+                               EAP_TYPE_IKEV2, 0x80, 0, 1)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Truncated message")
+            return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 4,
+                               EAP_TYPE_IKEV2, 0x80, 1)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Truncated message(2)")
+            return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 4,
+                               EAP_TYPE_IKEV2, 0x80, 0xffffffff)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Truncated message(3)")
+            return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 4,
+                               EAP_TYPE_IKEV2, 0xc0, 0xffffffff)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Truncated message(4)")
+            return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 4,
+                               EAP_TYPE_IKEV2, 0xc0, 10000000)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too long fragments (first fragment)")
+            return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 4 + 1,
+                               EAP_TYPE_IKEV2, 0xc0, 2, 1)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too long fragments (second fragment)")
+            return struct.pack(">BBHBB2B", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 2,
+                               EAP_TYPE_IKEV2, 0x00, 2, 3)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: No Message Length field in first fragment")
+            return struct.pack(">BBHBBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 1,
+                               EAP_TYPE_IKEV2, 0x40, 1)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: ICV before keys")
+            return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_IKEV2, 0x20)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unsupported IKEv2 header version")
+            return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 28,
+                               EAP_TYPE_IKEV2, 0x00,
+                               0, 0, 0, 0,
+                               0, 0, 0, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Incorrect IKEv2 header Length")
+            return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 28,
+                               EAP_TYPE_IKEV2, 0x00,
+                               0, 0, 0, 0,
+                               0, 0x20, 0, 0, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected IKEv2 Exchange Type in SA_INIT state")
+            return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 28,
+                               EAP_TYPE_IKEV2, 0x00,
+                               0, 0, 0, 0,
+                               0, 0x20, 0, 0, 0, 28)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected IKEv2 Message ID in SA_INIT state")
+            return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 28,
+                               EAP_TYPE_IKEV2, 0x00,
+                               0, 0, 0, 0,
+                               0, 0x20, 34, 0, 1, 28)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected IKEv2 Flags value")
+            return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 28,
+                               EAP_TYPE_IKEV2, 0x00,
+                               0, 0, 0, 0,
+                               0, 0x20, 34, 0, 0, 28)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected IKEv2 Flags value(2)")
+            return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 28,
+                               EAP_TYPE_IKEV2, 0x00,
+                               0, 0, 0, 0,
+                               0, 0x20, 34, 0x20, 0, 28)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: No SAi1 in SA_INIT")
+            return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1 + 28,
+                               EAP_TYPE_IKEV2, 0x00,
+                               0, 0, 0, 0,
+                               0, 0x20, 34, 0x08, 0, 28)
+
+        def build_ike(id, next=0, exch_type=34, flags=0x00, ike=''):
+            return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, id,
+                               4 + 1 + 1 + 28 + len(ike),
+                               EAP_TYPE_IKEV2, flags,
+                               0, 0, 0, 0,
+                               next, 0x20, exch_type, 0x08, 0,
+                               28 + len(ike)) + ike
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected extra data after payloads")
+            return build_ike(ctx['id'], ike=struct.pack(">B", 1))
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Truncated payload header")
+            return build_ike(ctx['id'], next=128, ike=struct.pack(">B", 1))
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too small payload header length")
+            ike = struct.pack(">BBH", 0, 0, 3)
+            return build_ike(ctx['id'], next=128, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too large payload header length")
+            ike = struct.pack(">BBH", 0, 0, 5)
+            return build_ike(ctx['id'], next=128, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unsupported payload (non-critical and critical)")
+            ike = struct.pack(">BBHBBH", 129, 0, 4, 0, 0x01, 4)
+            return build_ike(ctx['id'], next=128, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Certificate and empty SAi1")
+            ike = struct.pack(">BBHBBH", 33, 0, 4, 0, 0, 4)
+            return build_ike(ctx['id'], next=37, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too short proposal")
+            ike = struct.pack(">BBHBBHBBB", 0, 0, 4 + 7,
+                              0, 0, 7, 0, 0, 0)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too small proposal length in SAi1")
+            ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+                              0, 0, 7, 0, 0, 0, 0)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too large proposal length in SAi1")
+            ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+                              0, 0, 9, 0, 0, 0, 0)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected proposal type in SAi1")
+            ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+                              1, 0, 8, 0, 0, 0, 0)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected Protocol ID in SAi1")
+            ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+                              0, 0, 8, 0, 0, 0, 0)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected proposal number in SAi1")
+            ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+                              0, 0, 8, 0, 1, 0, 0)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Not enough room for SPI in SAi1")
+            ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+                              0, 0, 8, 1, 1, 1, 0)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected SPI in SAi1")
+            ike = struct.pack(">BBHBBHBBBBB", 0, 0, 4 + 9,
+                              0, 0, 9, 1, 1, 1, 0, 1)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: No transforms in SAi1")
+            ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+                              0, 0, 8, 1, 1, 0, 0)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too short transform in SAi1")
+            ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+                              0, 0, 8, 1, 1, 0, 1)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too small transform length in SAi1")
+            ike = struct.pack(">BBHBBHBBBBBBHBBH", 0, 0, 4 + 8 + 8,
+                              0, 0, 8 + 8, 1, 1, 0, 1,
+                              0, 0, 7, 0, 0, 0)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too large transform length in SAi1")
+            ike = struct.pack(">BBHBBHBBBBBBHBBH", 0, 0, 4 + 8 + 8,
+                              0, 0, 8 + 8, 1, 1, 0, 1,
+                              0, 0, 9, 0, 0, 0)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Unexpected Transform type in SAi1")
+            ike = struct.pack(">BBHBBHBBBBBBHBBH", 0, 0, 4 + 8 + 8,
+                              0, 0, 8 + 8, 1, 1, 0, 1,
+                              1, 0, 8, 0, 0, 0)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: No transform attributes in SAi1")
+            ike = struct.pack(">BBHBBHBBBBBBHBBH", 0, 0, 4 + 8 + 8,
+                              0, 0, 8 + 8, 1, 1, 0, 1,
+                              0, 0, 8, 0, 0, 0)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: No transform attr for AES and unexpected data after transforms in SAi1")
+            tlen1 = 8 + 3
+            tlen2 = 8 + 4
+            tlen3 = 8 + 4
+            tlen = tlen1 + tlen2 + tlen3
+            ike = struct.pack(">BBHBBHBBBBBBHBBH3BBBHBBHHHBBHBBHHHB",
+                              0, 0, 4 + 8 + tlen + 1,
+                              0, 0, 8 + tlen + 1, 1, 1, 0, 3,
+                              3, 0, tlen1, 1, 0, 12, 1, 2, 3,
+                              3, 0, tlen2, 1, 0, 12, 0, 128,
+                              0, 0, tlen3, 1, 0, 12, 0x8000 | 14, 127,
+                              1)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        def build_sa(next=0):
+            tlen = 5 * 8
+            return struct.pack(">BBHBBHBBBBBBHBBHBBHBBHBBHBBHBBHBBHBBHBBH",
+                               next, 0, 4 + 8 + tlen,
+                               0, 0, 8 + tlen, 1, 1, 0, 5,
+                               3, 0, 8, 1, 0, 3,
+                               3, 0, 8, 2, 0, 1,
+                               3, 0, 8, 3, 0, 1,
+                               3, 0, 8, 4, 0, 5,
+                               0, 0, 8, 241, 0, 0)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid proposal, but no KEi in SAi1")
+            ike = build_sa()
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Empty KEi in SAi1")
+            ike = build_sa(next=34) + struct.pack(">BBH", 0, 0, 4)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Mismatch in DH Group in SAi1")
+            ike = build_sa(next=34)
+            ike += struct.pack(">BBHHH", 0, 0, 4 + 4 + 96, 12345, 0)
+            ike += 96*'\x00'
+            return build_ike(ctx['id'], next=33, ike=ike)
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid DH public value length in SAi1")
+            ike = build_sa(next=34)
+            ike += struct.pack(">BBHHH", 0, 0, 4 + 4 + 96, 5, 0)
+            ike += 96*'\x00'
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        def build_ke(next=0):
+            ke = struct.pack(">BBHHH", next, 0, 4 + 4 + 192, 5, 0)
+            ke += 192*'\x00'
+            return ke
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid proposal and KEi, but no Ni in SAi1")
+            ike = build_sa(next=34)
+            ike += build_ke()
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too short Ni in SAi1")
+            ike = build_sa(next=34)
+            ike += build_ke(next=40)
+            ike += struct.pack(">BBH", 0, 0, 4)
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Too long Ni in SAi1")
+            ike = build_sa(next=34)
+            ike += build_ke(next=40)
+            ike += struct.pack(">BBH", 0, 0, 4 + 257) + 257*'\x00'
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        def build_ni(next=0):
+            return struct.pack(">BBH", next, 0, 4 + 256) + 256*'\x00'
+
+        def build_sai1(id):
+            ike = build_sa(next=34)
+            ike += build_ke(next=40)
+            ike += build_ni()
+            return build_ike(ctx['id'], next=33, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid proposal, KEi, and Ni in SAi1")
+            return build_sai1(ctx['id'])
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: EAP-Failure")
+            return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid proposal, KEi, and Ni in SAi1")
+            return build_sai1(ctx['id'])
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: No integrity checksum")
+            ike = ''
+            return build_ike(ctx['id'], next=37, ike=ike)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid proposal, KEi, and Ni in SAi1")
+            return build_sai1(ctx['id'])
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Truncated integrity checksum")
+            return struct.pack(">BBHBB",
+                               EAP_CODE_REQUEST, ctx['id'],
+                               4 + 1 + 1,
+                               EAP_TYPE_IKEV2, 0x20)
+
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Valid proposal, KEi, and Ni in SAi1")
+            return build_sai1(ctx['id'])
+        idx += 1
+        if ctx['num'] == idx:
+            logger.info("Test: Invalid integrity checksum")
+            ike = ''
+            return build_ike(ctx['id'], next=37, flags=0x20, ike=ike)
+
+        return None
+
+    srv = start_radius_server(ikev2_handler)
+
+    try:
+        hapd = start_ap(apdev[0]['ifname'])
+
+        for i in range(49):
+            dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+                           eap="IKEV2", identity="user",
+                           password="password",
+                           wait_connect=False)
+            ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+                                   timeout=15)
+            if ev is None:
+                raise Exception("Timeout on EAP start")
+            if i in [ 40, 45 ]:
+                ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
+                                       timeout=10)
+                if ev is None:
+                    raise Exception("Timeout on EAP failure")
+            else:
+                time.sleep(0.05)
+            dev[0].request("REMOVE_NETWORK all")
+    finally:
+        stop_radius_server(srv)
diff --git a/hostap/tests/hwsim/test_erp.py b/hostap/tests/hwsim/test_erp.py
new file mode 100644
index 0000000..c4f2fa5
--- /dev/null
+++ b/hostap/tests/hwsim/test_erp.py
@@ -0,0 +1,412 @@
+# EAP Re-authentication Protocol (ERP) tests
+# Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import logging
+logger = logging.getLogger()
+import os
+import time
+
+import hostapd
+from utils import HwsimSkip
+from test_ap_eap import int_eap_server_params
+from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
+
+def check_erp_capa(dev):
+    capab = dev.get_capability("erp")
+    if not capab or 'ERP' not in capab:
+        raise HwsimSkip("ERP not supported in the build")
+
+def test_erp_initiate_reauth_start(dev, apdev):
+    """Authenticator sending EAP-Initiate/Re-auth-Start, but ERP disabled on peer"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params['erp_send_reauth_start'] = '1'
+    params['erp_domain'] = 'example.com'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("ERP_FLUSH")
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="PAX", identity="pax.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412")
+
+def test_erp_enabled_on_server(dev, apdev):
+    """ERP enabled on internal EAP server, but disabled on peer"""
+    params = int_eap_server_params()
+    params['erp_send_reauth_start'] = '1'
+    params['erp_domain'] = 'example.com'
+    params['eap_server_erp'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("ERP_FLUSH")
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="PAX", identity="pax.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412")
+
+def test_erp(dev, apdev):
+    """ERP enabled on server and peer"""
+    check_erp_capa(dev[0])
+    params = int_eap_server_params()
+    params['erp_send_reauth_start'] = '1'
+    params['erp_domain'] = 'example.com'
+    params['eap_server_erp'] = '1'
+    params['disable_pmksa_caching'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("ERP_FLUSH")
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="PSK", identity="psk.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   erp="1", scan_freq="2412")
+    for i in range(3):
+        dev[0].request("DISCONNECT")
+        dev[0].wait_disconnected(timeout=15)
+        dev[0].request("RECONNECT")
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+        if ev is None:
+            raise Exception("EAP success timed out")
+        if "EAP re-authentication completed successfully" not in ev:
+            raise Exception("Did not use ERP")
+        dev[0].wait_connected(timeout=15, error="Reconnection timed out")
+
+def test_erp_server_no_match(dev, apdev):
+    """ERP enabled on server and peer, but server has no key match"""
+    check_erp_capa(dev[0])
+    params = int_eap_server_params()
+    params['erp_send_reauth_start'] = '1'
+    params['erp_domain'] = 'example.com'
+    params['eap_server_erp'] = '1'
+    params['disable_pmksa_caching'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("ERP_FLUSH")
+    id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                        eap="PSK", identity="psk.user@example.com",
+                        password_hex="0123456789abcdef0123456789abcdef",
+                        erp="1", scan_freq="2412")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=15)
+    hapd.request("ERP_FLUSH")
+    dev[0].request("RECONNECT")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS",
+                            "CTRL-EVENT-EAP-FAILURE"], timeout=15)
+    if ev is None:
+        raise Exception("EAP result timed out")
+    if "CTRL-EVENT-EAP-SUCCESS" in ev:
+        raise Exception("Unexpected EAP success")
+    dev[0].request("DISCONNECT")
+    dev[0].select_network(id)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    if "EAP re-authentication completed successfully" in ev:
+        raise Exception("Unexpected use of ERP")
+    dev[0].wait_connected(timeout=15, error="Reconnection timed out")
+
+def start_erp_as(apdev):
+    params = { "ssid": "as", "beacon_int": "2000",
+               "radius_server_clients": "auth_serv/radius_clients.conf",
+               "radius_server_auth_port": '18128',
+               "eap_server": "1",
+               "eap_user_file": "auth_serv/eap_user.conf",
+               "ca_cert": "auth_serv/ca.pem",
+               "server_cert": "auth_serv/server.pem",
+               "private_key": "auth_serv/server.key",
+               "eap_sim_db": "unix:/tmp/hlr_auc_gw.sock",
+               "dh_file": "auth_serv/dh.conf",
+               "pac_opaque_encr_key": "000102030405060708090a0b0c0d0e0f",
+               "eap_fast_a_id": "101112131415161718191a1b1c1d1e1f",
+               "eap_fast_a_id_info": "test server",
+               "eap_server_erp": "1",
+               "erp_domain": "example.com" }
+    hostapd.add_ap(apdev['ifname'], params)
+
+def test_erp_radius(dev, apdev):
+    """ERP enabled on RADIUS server and peer"""
+    check_erp_capa(dev[0])
+    start_erp_as(apdev[1])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params['auth_server_port'] = "18128"
+    params['erp_send_reauth_start'] = '1'
+    params['erp_domain'] = 'example.com'
+    params['disable_pmksa_caching'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("ERP_FLUSH")
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="PSK", identity="psk.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   erp="1", scan_freq="2412")
+    for i in range(3):
+        dev[0].request("DISCONNECT")
+        dev[0].wait_disconnected(timeout=15)
+        dev[0].request("RECONNECT")
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+        if ev is None:
+            raise Exception("EAP success timed out")
+        if "EAP re-authentication completed successfully" not in ev:
+            raise Exception("Did not use ERP")
+        dev[0].wait_connected(timeout=15, error="Reconnection timed out")
+
+def erp_test(dev, hapd, **kwargs):
+    res = dev.get_capability("eap")
+    if kwargs['eap'] not in res:
+        logger.info("Skip ERP test with %s due to missing support" % kwargs['eap'])
+        return
+    hapd.dump_monitor()
+    dev.dump_monitor()
+    dev.request("ERP_FLUSH")
+    id = dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", erp="1",
+                     scan_freq="2412", **kwargs)
+    dev.request("DISCONNECT")
+    dev.wait_disconnected(timeout=15)
+    hapd.dump_monitor()
+    dev.request("RECONNECT")
+    ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    if "EAP re-authentication completed successfully" not in ev:
+        raise Exception("Did not use ERP")
+    dev.wait_connected(timeout=15, error="Reconnection timed out")
+    ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
+    if ev is None:
+        raise Exception("No connection event received from hostapd")
+    dev.request("DISCONNECT")
+
+def test_erp_radius_eap_methods(dev, apdev):
+    """ERP enabled on RADIUS server and peer"""
+    check_erp_capa(dev[0])
+    eap_methods = dev[0].get_capability("eap")
+    start_erp_as(apdev[1])
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params['auth_server_port'] = "18128"
+    params['erp_send_reauth_start'] = '1'
+    params['erp_domain'] = 'example.com'
+    params['disable_pmksa_caching'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    erp_test(dev[0], hapd, eap="AKA", identity="0232010000000000@example.com",
+             password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+    erp_test(dev[0], hapd, eap="AKA'", identity="6555444333222111@example.com",
+             password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+    erp_test(dev[0], hapd, eap="EKE", identity="erp-eke@example.com",
+             password="hello")
+    if "FAST" in eap_methods:
+        erp_test(dev[0], hapd, eap="FAST", identity="erp-fast@example.com",
+                 password="password", ca_cert="auth_serv/ca.pem",
+                 phase2="auth=GTC",
+                 phase1="fast_provisioning=2",
+                 pac_file="blob://fast_pac_auth_erp")
+    erp_test(dev[0], hapd, eap="GPSK", identity="erp-gpsk@example.com",
+             password="abcdefghijklmnop0123456789abcdef")
+    erp_test(dev[0], hapd, eap="IKEV2", identity="erp-ikev2@example.com",
+             password="password")
+    erp_test(dev[0], hapd, eap="PAX", identity="erp-pax@example.com",
+             password_hex="0123456789abcdef0123456789abcdef")
+    # TODO: PEAP (EMSK)
+    #if "MSCHAPV2" in eap_methods:
+    #    erp_test(dev[0], hapd, eap="PEAP", identity="erp-peap@example.com",
+    #             password="password", ca_cert="auth_serv/ca.pem",
+    #             phase2="auth=MSCHAPV2")
+    erp_test(dev[0], hapd, eap="PSK", identity="erp-psk@example.com",
+             password_hex="0123456789abcdef0123456789abcdef")
+    if "PWD" in eap_methods:
+        erp_test(dev[0], hapd, eap="PWD", identity="erp-pwd@example.com",
+                 password="secret password")
+    erp_test(dev[0], hapd, eap="SAKE", identity="erp-sake@example.com",
+             password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
+    erp_test(dev[0], hapd, eap="SIM", identity="1232010000000000@example.com",
+             password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+    erp_test(dev[0], hapd, eap="TLS", identity="erp-tls@example.com",
+             ca_cert="auth_serv/ca.pem", client_cert="auth_serv/user.pem",
+             private_key="auth_serv/user.key")
+    erp_test(dev[0], hapd, eap="TTLS", identity="erp-ttls@example.com",
+             password="password", ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+
+def test_erp_key_lifetime_in_memory(dev, apdev, params):
+    """ERP and key lifetime in memory"""
+    check_erp_capa(dev[0])
+    p = int_eap_server_params()
+    p['erp_send_reauth_start'] = '1'
+    p['erp_domain'] = 'example.com'
+    p['eap_server_erp'] = '1'
+    p['disable_pmksa_caching'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], p)
+    password = "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25"
+
+    pid = find_wpas_process(dev[0])
+
+    dev[0].request("ERP_FLUSH")
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="pap-secret@example.com", password=password,
+                   ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+                   erp="1", scan_freq="2412")
+
+    time.sleep(1)
+    buf = read_process_memory(pid, password)
+
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=15)
+
+    dev[0].relog()
+    msk = None
+    emsk = None
+    rRK = None
+    rIK = None
+    pmk = None
+    ptk = None
+    gtk = None
+    with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
+        for l in f.readlines():
+            if "EAP-TTLS: Derived key - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                msk = binascii.unhexlify(val)
+            if "EAP-TTLS: Derived EMSK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                emsk = binascii.unhexlify(val)
+            if "EAP: ERP rRK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                rRK = binascii.unhexlify(val)
+            if "EAP: ERP rIK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                rIK = binascii.unhexlify(val)
+            if "WPA: PMK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                pmk = binascii.unhexlify(val)
+            if "WPA: PTK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                ptk = binascii.unhexlify(val)
+            if "WPA: Group Key - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                gtk = binascii.unhexlify(val)
+    if not msk or not emsk or not rIK or not rRK or not pmk or not ptk or not gtk:
+        raise Exception("Could not find keys from debug log")
+    if len(gtk) != 16:
+        raise Exception("Unexpected GTK length")
+
+    kck = ptk[0:16]
+    kek = ptk[16:32]
+    tk = ptk[32:48]
+
+    fname = os.path.join(params['logdir'],
+                         'erp_key_lifetime_in_memory.memctx-')
+
+    logger.info("Checking keys in memory while associated")
+    get_key_locations(buf, password, "Password")
+    get_key_locations(buf, pmk, "PMK")
+    get_key_locations(buf, msk, "MSK")
+    get_key_locations(buf, emsk, "EMSK")
+    get_key_locations(buf, rRK, "rRK")
+    get_key_locations(buf, rIK, "rIK")
+    if password not in buf:
+        raise HwsimSkip("Password not found while associated")
+    if pmk not in buf:
+        raise HwsimSkip("PMK not found while associated")
+    if kck not in buf:
+        raise Exception("KCK not found while associated")
+    if kek not in buf:
+        raise Exception("KEK not found while associated")
+    if tk in buf:
+        raise Exception("TK found from memory")
+    if gtk in buf:
+        raise Exception("GTK found from memory")
+
+    logger.info("Checking keys in memory after disassociation")
+    buf = read_process_memory(pid, password)
+
+    # Note: Password is still present in network configuration
+    # Note: PMK is in EAP fast re-auth data
+
+    get_key_locations(buf, password, "Password")
+    get_key_locations(buf, pmk, "PMK")
+    get_key_locations(buf, msk, "MSK")
+    get_key_locations(buf, emsk, "EMSK")
+    get_key_locations(buf, rRK, "rRK")
+    get_key_locations(buf, rIK, "rIK")
+    verify_not_present(buf, kck, fname, "KCK")
+    verify_not_present(buf, kek, fname, "KEK")
+    verify_not_present(buf, tk, fname, "TK")
+    verify_not_present(buf, gtk, fname, "GTK")
+
+    dev[0].request("RECONNECT")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    if "EAP re-authentication completed successfully" not in ev:
+        raise Exception("Did not use ERP")
+    dev[0].wait_connected(timeout=15, error="Reconnection timed out")
+
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=15)
+
+    dev[0].relog()
+    pmk = None
+    ptk = None
+    gtk = None
+    with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
+        for l in f.readlines():
+            if "WPA: PMK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                pmk = binascii.unhexlify(val)
+            if "WPA: PTK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                ptk = binascii.unhexlify(val)
+            if "WPA: GTK in EAPOL-Key - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                gtk = binascii.unhexlify(val)
+    if not pmk or not ptk or not gtk:
+        raise Exception("Could not find keys from debug log")
+
+    kck = ptk[0:16]
+    kek = ptk[16:32]
+    tk = ptk[32:48]
+
+    logger.info("Checking keys in memory after ERP and disassociation")
+    buf = read_process_memory(pid, password)
+
+    # Note: Password is still present in network configuration
+
+    get_key_locations(buf, password, "Password")
+    get_key_locations(buf, pmk, "PMK")
+    get_key_locations(buf, msk, "MSK")
+    get_key_locations(buf, emsk, "EMSK")
+    get_key_locations(buf, rRK, "rRK")
+    get_key_locations(buf, rIK, "rIK")
+    verify_not_present(buf, kck, fname, "KCK")
+    verify_not_present(buf, kek, fname, "KEK")
+    verify_not_present(buf, tk, fname, "TK")
+    verify_not_present(buf, gtk, fname, "GTK")
+
+    dev[0].request("REMOVE_NETWORK all")
+
+    logger.info("Checking keys in memory after network profile removal")
+    buf = read_process_memory(pid, password)
+
+    # Note: rRK and rIK are still in memory
+
+    get_key_locations(buf, password, "Password")
+    get_key_locations(buf, pmk, "PMK")
+    get_key_locations(buf, msk, "MSK")
+    get_key_locations(buf, emsk, "EMSK")
+    get_key_locations(buf, rRK, "rRK")
+    get_key_locations(buf, rIK, "rIK")
+    verify_not_present(buf, password, fname, "password")
+    verify_not_present(buf, pmk, fname, "PMK")
+    verify_not_present(buf, kck, fname, "KCK")
+    verify_not_present(buf, kek, fname, "KEK")
+    verify_not_present(buf, tk, fname, "TK")
+    verify_not_present(buf, gtk, fname, "GTK")
+    verify_not_present(buf, msk, fname, "MSK")
+    verify_not_present(buf, emsk, fname, "EMSK")
+
+    dev[0].request("ERP_FLUSH")
+    logger.info("Checking keys in memory after ERP_FLUSH")
+    buf = read_process_memory(pid, password)
+    get_key_locations(buf, rRK, "rRK")
+    get_key_locations(buf, rIK, "rIK")
+    verify_not_present(buf, rRK, fname, "rRK")
+    verify_not_present(buf, rIK, fname, "rIK")
diff --git a/hostap/tests/hwsim/test_ext_password.py b/hostap/tests/hwsim/test_ext_password.py
new file mode 100644
index 0000000..c70895c
--- /dev/null
+++ b/hostap/tests/hwsim/test_ext_password.py
@@ -0,0 +1,79 @@
+# External password storage
+# 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.
+
+import logging
+logger = logging.getLogger()
+
+import hostapd
+from utils import skip_with_fips
+from wpasupplicant import WpaSupplicant
+from test_ap_hs20 import hs20_ap_params
+from test_ap_hs20 import interworking_select
+from test_ap_hs20 import interworking_connect
+
+def test_ext_password_psk(dev, apdev):
+    """External password storage for PSK"""
+    params = hostapd.wpa2_params(ssid="ext-pw-psk", passphrase="12345678")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].request("SET ext_password_backend test:psk1=12345678")
+    dev[0].connect("ext-pw-psk", raw_psk="ext:psk1", scan_freq="2412")
+
+def test_ext_password_psk_not_found(dev, apdev):
+    """External password storage for PSK and PSK not found"""
+    params = hostapd.wpa2_params(ssid="ext-pw-psk", passphrase="12345678")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].request("SET ext_password_backend test:psk1=12345678")
+    dev[0].connect("ext-pw-psk", raw_psk="ext:psk2", scan_freq="2412",
+                   wait_connect=False)
+    dev[1].request("SET ext_password_backend test:psk1=1234567")
+    dev[1].connect("ext-pw-psk", raw_psk="ext:psk1", scan_freq="2412",
+                   wait_connect=False)
+    dev[2].request("SET ext_password_backend test:psk1=1234567890123456789012345678901234567890123456789012345678901234567890")
+    dev[2].connect("ext-pw-psk", raw_psk="ext:psk1", scan_freq="2412",
+                   wait_connect=False)
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    wpas.request("SET ext_password_backend test:psk1=123456789012345678901234567890123456789012345678901234567890123q")
+    wpas.connect("ext-pw-psk", raw_psk="ext:psk1", scan_freq="2412",
+                 wait_connect=False)
+
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected association")
+    ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected association")
+    ev = dev[2].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected association")
+    ev = wpas.wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected association")
+
+def test_ext_password_eap(dev, apdev):
+    """External password storage for EAP password"""
+    params = hostapd.wpa2_eap_params(ssid="ext-pw-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].request("SET ext_password_backend test:pw0=hello|pw1=password|pw2=secret")
+    dev[0].connect("ext-pw-eap", key_mgmt="WPA-EAP", eap="PEAP",
+                   identity="user", password_hex="ext:pw1",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                   scan_freq="2412")
+
+def test_ext_password_interworking(dev, apdev):
+    """External password storage for Interworking network selection"""
+    skip_with_fips(dev[0])
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].request("SET ext_password_backend test:pw1=password")
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'username': "hs20-test" })
+    dev[0].set_cred(id, "password", "ext:pw1")
+    interworking_select(dev[0], bssid, freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
diff --git a/hostap/tests/hwsim/test_fst_config.py b/hostap/tests/hwsim/test_fst_config.py
new file mode 100644
index 0000000..938cf9a
--- /dev/null
+++ b/hostap/tests/hwsim/test_fst_config.py
@@ -0,0 +1,536 @@
+# FST configuration tests
+# Copyright (c) 2015, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import subprocess
+import time
+import os
+import signal
+import hostapd
+import wpasupplicant
+import utils
+
+import fst_test_common
+
+class FstLauncherConfig:
+    """FstLauncherConfig class represents configuration to be used for
+    FST config tests related hostapd/wpa_supplicant instances"""
+    def __init__(self, iface, fst_group, fst_pri, fst_llt=None):
+        self.iface = iface
+        self.fst_group = fst_group
+        self.fst_pri = fst_pri
+        self.fst_llt = fst_llt # None llt means no llt parameter will be set
+
+    def ifname(self):
+        return self.iface
+
+    def is_ap(self):
+        """Returns True if the configuration is for AP, otherwise - False"""
+        raise Exception("Virtual is_ap() called!")
+
+    def to_file(self, pathname):
+        """Creates configuration file to be used by FST config tests related
+        hostapd/wpa_supplicant instances"""
+        raise Exception("Virtual to_file() called!")
+
+class FstLauncherConfigAP(FstLauncherConfig):
+    """FstLauncherConfigAP class represents configuration to be used for
+    FST config tests related hostapd instance"""
+    def __init__(self, iface, ssid, mode, chan, fst_group, fst_pri,
+                 fst_llt=None):
+        self.ssid = ssid
+        self.mode = mode
+        self.chan = chan
+        FstLauncherConfig.__init__(self, iface, fst_group, fst_pri, fst_llt)
+
+    def is_ap(self):
+        return True
+
+    def get_channel(self):
+        return self.chan
+
+    def to_file(self, pathname):
+        """Creates configuration file to be used by FST config tests related
+        hostapd instance"""
+        with open(pathname, "w") as f:
+            f.write("country_code=US\n"
+                    "interface=%s\n"
+                    "ctrl_interface=/var/run/hostapd\n"
+                    "ssid=%s\n"
+                    "channel=%s\n"
+                    "hw_mode=%s\n"
+                    "ieee80211n=1\n" % (self.iface, self.ssid, self.chan,
+                                        self.mode))
+            if len(self.fst_group) != 0:
+                f.write("fst_group_id=%s\n"
+                        "fst_priority=%s\n" % (self.fst_group, self.fst_pri))
+                if self.fst_llt is not None:
+                    f.write("fst_llt=%s\n" % self.fst_llt)
+        with open(pathname, "r") as f:
+            logger.debug("wrote hostapd config file %s:\n%s" % (pathname,
+                                                                f.read()))
+
+class FstLauncherConfigSTA(FstLauncherConfig):
+    """FstLauncherConfig class represents configuration to be used for
+    FST config tests related wpa_supplicant instance"""
+    def __init__(self, iface, fst_group, fst_pri, fst_llt=None):
+        FstLauncherConfig.__init__(self, iface, fst_group, fst_pri, fst_llt)
+
+    def is_ap(self):
+        return False
+
+    def to_file(self, pathname):
+        """Creates configuration file to be used by FST config tests related
+        wpa_supplicant instance"""
+        with open(pathname, "w") as f:
+            f.write("ctrl_interface=DIR=/var/run/wpa_supplicant\n"
+                "p2p_no_group_iface=1\n")
+            if len(self.fst_group) != 0:
+                f.write("fst_group_id=%s\n"
+                    "fst_priority=%s\n" % (self.fst_group, self.fst_pri))
+                if self.fst_llt is not None:
+                    f.write("fst_llt=%s\n" % self.fst_llt)
+        with open(pathname, "r") as f:
+            logger.debug("wrote wpa_supplicant config file %s:\n%s" % (pathname, f.read()))
+
+class FstLauncher:
+    """FstLauncher class is responsible for launching and cleaning up of FST
+    config tests related hostapd/wpa_supplicant instances"""
+    def __init__(self, logpath):
+        self.logger = logging.getLogger()
+        self.fst_logpath = logpath
+        self.cfgs_to_run = []
+        self.hapd_fst_global = '/var/run/hostapd-fst-global'
+        self.wsup_fst_global = '/tmp/fststa'
+        self.nof_aps = 0
+        self.nof_stas = 0
+        self.reg_ctrl = fst_test_common.HapdRegCtrl()
+        self.test_is_supported()
+
+    def __del__(self):
+        self.cleanup()
+
+    @staticmethod
+    def test_is_supported():
+        h = hostapd.HostapdGlobal()
+        resp = h.request("FST-MANAGER TEST_REQUEST IS_SUPPORTED")
+        if not resp.startswith("OK"):
+            raise utils.HwsimSkip("FST not supported")
+        w = wpasupplicant.WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        resp = w.global_request("FST-MANAGER TEST_REQUEST IS_SUPPORTED")
+        if not resp.startswith("OK"):
+            raise utils.HwsimSkip("FST not supported")
+
+    def get_cfg_pathname(self, cfg):
+        """Returns pathname of ifname based configuration file"""
+        return self.fst_logpath +'/'+ cfg.ifname() + '.conf'
+
+    def add_cfg(self, cfg):
+        """Adds configuration to be used for launching hostapd/wpa_supplicant
+        instances"""
+        if cfg not in self.cfgs_to_run:
+            self.cfgs_to_run.append(cfg)
+        if cfg.is_ap() == True:
+            self.nof_aps += 1
+        else:
+            self.nof_stas += 1
+
+    def remove_cfg(self, cfg):
+        """Removes configuration previously added with add_cfg"""
+        if cfg in self.cfgs_to_run:
+            self.cfgs_to_run.remove(cfg)
+        if cfg.is_ap() == True:
+            self.nof_aps -= 1
+        else:
+            self.nof_stas -= 1
+        config_file = self.get_cfg_pathname(cfg);
+        if os.path.exists(config_file):
+            os.remove(config_file)
+
+    def run_hostapd(self):
+        """Lauches hostapd with interfaces configured according to
+        FstLauncherConfigAP configurations added"""
+        if self.nof_aps == 0:
+            raise Exception("No FST APs to start")
+        pidfile = self.fst_logpath + '/' + 'myhostapd.pid'
+        mylogfile = self.fst_logpath + '/' + 'fst-hostapd'
+        prg = os.path.join(self.fst_logpath,
+                           'alt-hostapd/hostapd/hostapd')
+        if not os.path.exists(prg):
+            prg = '../../hostapd/hostapd'
+        cmd = [ prg, '-B', '-ddd',
+                '-P', pidfile, '-f', mylogfile, '-g', self.hapd_fst_global]
+        for i in range(0, len(self.cfgs_to_run)):
+            cfg = self.cfgs_to_run[i]
+            if cfg.is_ap() == True:
+                cfgfile = self.get_cfg_pathname(cfg)
+                cfg.to_file(cfgfile)
+                cmd.append(cfgfile)
+                self.reg_ctrl.add_ap(cfg.ifname(), cfg.get_channel())
+        self.logger.debug("Starting fst hostapd: " + ' '.join(cmd))
+        res = subprocess.call(cmd)
+        self.logger.debug("fst hostapd start result: %d" % res)
+        if res == 0:
+            self.reg_ctrl.start()
+        return res
+
+    def run_wpa_supplicant(self):
+        """Lauches wpa_supplicant with interfaces configured according to
+        FstLauncherConfigSTA configurations added"""
+        if self.nof_stas == 0:
+            raise Exception("No FST STAs to start")
+        pidfile = self.fst_logpath + '/' + 'mywpa_supplicant.pid'
+        mylogfile = self.fst_logpath + '/' + 'fst-wpa_supplicant'
+        prg = os.path.join(self.fst_logpath,
+                           'alt-wpa_supplicant/wpa_supplicant/wpa_supplicant')
+        if not os.path.exists(prg):
+            prg = '../../wpa_supplicant/wpa_supplicant'
+        cmd = [ prg, '-B', '-ddd',
+                '-P' + pidfile, '-f', mylogfile, '-g', self.wsup_fst_global ]
+        sta_no = 0
+        for i in range(0, len(self.cfgs_to_run)):
+            cfg = self.cfgs_to_run[i]
+            if cfg.is_ap() == False:
+                cfgfile = self.get_cfg_pathname(cfg)
+                cfg.to_file(cfgfile)
+                cmd.append('-c' + cfgfile)
+                cmd.append('-i' + cfg.ifname())
+                cmd.append('-Dnl80211')
+                if sta_no != self.nof_stas -1:
+                    cmd.append('-N')    # Next station configuration
+                sta_no += 1
+        self.logger.debug("Starting fst supplicant: " + ' '.join(cmd))
+        res = subprocess.call(cmd)
+        self.logger.debug("fst supplicant start result: %d" % res)
+        return res
+
+    def cleanup(self):
+        """Terminates hostapd/wpa_supplicant processes previously launched with
+        run_hostapd/run_wpa_supplicant"""
+        pidfile = self.fst_logpath + '/' + 'myhostapd.pid'
+        self.kill_pid(pidfile)
+        pidfile = self.fst_logpath + '/' + 'mywpa_supplicant.pid'
+        self.kill_pid(pidfile)
+        self.reg_ctrl.stop()
+        while len(self.cfgs_to_run) != 0:
+            cfg = self.cfgs_to_run[0]
+            self.remove_cfg(cfg)
+
+    def kill_pid(self, pidfile):
+        """Kills process by PID file"""
+        if not os.path.exists(pidfile):
+            return
+        pid = -1
+        try:
+            pf = file(pidfile, 'r')
+            pid = int(pf.read().strip())
+            pf.close()
+            self.logger.debug("kill_pid %s --> pid %d" % (pidfile, pid))
+            os.kill(pid, signal.SIGTERM)
+            for i in range(10):
+                try:
+                    # Poll the pid (Is the process still existing?)
+                    os.kill(pid, 0)
+                except OSError:
+                    # No, already done
+                    break
+                # Wait and check again
+                time.sleep(1)
+        except Exception, e:
+            self.logger.debug("Didn't stop the pid=%d. Was it stopped already? (%s)" % (pid, str(e)))
+
+
+def parse_ies(iehex, el=-1):
+    """Parses the information elements hex string 'iehex' in format
+    "0a0b0c0d0e0f". If no 'el' defined just checks the IE string for integrity.
+    If 'el' is defined returns the list of hex values of the specific IE (or
+    empty list if the element is not in the string."""
+    iel = [iehex[i:i + 2] for i in range(0, len(iehex), 2)]
+    for i in range(0, len(iel)):
+         iel[i] = int(iel[i], 16)
+    # Sanity check
+    i = 0
+    res = []
+    while i < len(iel):
+        logger.debug("IE found: %x" % iel[i])
+        if el != -1 and el == iel[i]:
+            res = iel[i + 2:i + 2 + iel[i + 1]]
+        i += 2 + iel[i + 1]
+    if i != len(iel):
+        logger.error("Bad IE string: " + iehex)
+        res = []
+    return res
+
+def scan_and_get_bss(dev, frq):
+    """Issues a scan on given device on given frequency, returns the bss info
+    dictionary ('ssid','ie','flags', etc.) or None. Note, the function
+    implies there is only one AP on the given channel. If not a case,
+    the function must be changed to call dev.get_bss() till the AP with the
+    [b]ssid that we need is found"""
+    dev.scan(freq=frq)
+    return dev.get_bss('0')
+
+
+# AP configuration tests
+
+def run_test_ap_configuration(apdev, test_params,
+                              fst_group = fst_test_common.fst_test_def_group,
+                              fst_pri = fst_test_common.fst_test_def_prio_high,
+                              fst_llt = fst_test_common.fst_test_def_llt):
+    """Runs FST hostapd where the 1st AP configuration is fixed, the 2nd fst
+    configuration is provided by the parameters. Returns the result of the run:
+    0 - no errors discovered, an error otherwise. The function is used for
+    simplek "bad configuration" tests."""
+    logdir = test_params['logdir']
+    fst_launcher = FstLauncher(logdir)
+    ap1 = FstLauncherConfigAP(apdev[0]['ifname'], 'fst_goodconf', 'a',
+                              fst_test_common.fst_test_def_chan_a,
+                              fst_test_common.fst_test_def_group,
+                              fst_test_common.fst_test_def_prio_low,
+                              fst_test_common.fst_test_def_llt)
+    ap2 = FstLauncherConfigAP(apdev[1]['ifname'], 'fst_badconf', 'b',
+                              fst_test_common.fst_test_def_chan_g, fst_group,
+                              fst_pri, fst_llt)
+    fst_launcher.add_cfg(ap1)
+    fst_launcher.add_cfg(ap2)
+    res = fst_launcher.run_hostapd()
+    return res
+
+def run_test_sta_configuration(test_params,
+                               fst_group = fst_test_common.fst_test_def_group,
+                               fst_pri = fst_test_common.fst_test_def_prio_high,
+                               fst_llt = fst_test_common.fst_test_def_llt):
+    """Runs FST wpa_supplicant where the 1st STA configuration is fixed, the
+    2nd fst configuration is provided by the parameters. Returns the result of
+    the run: 0 - no errors discovered, an error otherwise. The function is used
+    for simple "bad configuration" tests."""
+    logdir = test_params['logdir']
+    fst_launcher = FstLauncher(logdir)
+    sta1 = FstLauncherConfigSTA('wlan5',
+                                fst_test_common.fst_test_def_group,
+                                fst_test_common.fst_test_def_prio_low,
+                                fst_test_common.fst_test_def_llt)
+    sta2 = FstLauncherConfigSTA('wlan6', fst_group, fst_pri, fst_llt)
+    fst_launcher.add_cfg(sta1)
+    fst_launcher.add_cfg(sta2)
+    res = fst_launcher.run_wpa_supplicant()
+    return res
+
+def test_fst_ap_config_llt_neg(dev, apdev, test_params):
+    """FST AP configuration negative LLT"""
+    res = run_test_ap_configuration(apdev, test_params, fst_llt = '-1')
+    if res == 0:
+        raise Exception("hostapd started with a negative llt")
+
+def test_fst_ap_config_llt_zero(dev, apdev, test_params):
+    """FST AP configuration zero LLT"""
+    res = run_test_ap_configuration(apdev, test_params, fst_llt = '0')
+    if res == 0:
+        raise Exception("hostapd started with a zero llt")
+
+def test_fst_ap_config_llt_too_big(dev, apdev, test_params):
+    """FST AP configuration LLT is too big"""
+    res = run_test_ap_configuration(apdev, test_params,
+                                    fst_llt = '4294967296') #0x100000000
+    if res == 0:
+        raise Exception("hostapd started with llt that is too big")
+
+def test_fst_ap_config_llt_nan(dev, apdev, test_params):
+    """FST AP configuration LLT is not a number"""
+    res = run_test_ap_configuration(apdev, test_params, fst_llt = 'nan')
+    if res == 0:
+        raise Exception("hostapd started with llt not a number")
+
+def test_fst_ap_config_pri_neg(dev, apdev, test_params):
+    """FST AP configuration Priority negative"""
+    res = run_test_ap_configuration(apdev, test_params, fst_pri = '-1')
+    if res == 0:
+        raise Exception("hostapd started with a negative fst priority")
+
+def test_fst_ap_config_pri_zero(dev, apdev, test_params):
+    """FST AP configuration Priority zero"""
+    res = run_test_ap_configuration(apdev, test_params, fst_pri = '0')
+    if res == 0:
+        raise Exception("hostapd started with a zero fst priority")
+
+def test_fst_ap_config_pri_large(dev, apdev, test_params):
+    """FST AP configuration Priority too large"""
+    res = run_test_ap_configuration(apdev, test_params, fst_pri = '256')
+    if res == 0:
+        raise Exception("hostapd started with too large fst priority")
+
+def test_fst_ap_config_pri_nan(dev, apdev, test_params):
+    """FST AP configuration Priority not a number"""
+    res = run_test_ap_configuration(apdev, test_params, fst_pri = 'nan')
+    if res == 0:
+        raise Exception("hostapd started with fst priority not a number")
+
+def test_fst_ap_config_group_len(dev, apdev, test_params):
+    """FST AP configuration Group max length"""
+    res = run_test_ap_configuration(apdev, test_params,
+                                    fst_group = 'fstg5678abcd34567')
+    if res == 0:
+        raise Exception("hostapd started with fst_group length too big")
+
+def test_fst_ap_config_good(dev, apdev, test_params):
+    """FST AP configuration good parameters"""
+    res = run_test_ap_configuration(apdev, test_params)
+    if res != 0:
+        raise Exception("hostapd didn't start with valid config parameters")
+
+def test_fst_ap_config_default(dev, apdev, test_params):
+    """FST AP configuration default parameters"""
+    res = run_test_ap_configuration(apdev, test_params, fst_llt = None)
+    if res != 0:
+        raise Exception("hostapd didn't start with valid config parameters")
+
+
+# STA configuration tests
+
+def test_fst_sta_config_llt_neg(dev, apdev, test_params):
+    """FST STA configuration negative LLT"""
+    res = run_test_sta_configuration(test_params, fst_llt = '-1')
+    if res == 0:
+        raise Exception("wpa_supplicant started with a negative llt")
+
+def test_fst_sta_config_llt_zero(dev, apdev, test_params):
+    """FST STA configuration zero LLT"""
+    res = run_test_sta_configuration(test_params, fst_llt = '0')
+    if res == 0:
+        raise Exception("wpa_supplicant started with a zero llt")
+
+def test_fst_sta_config_llt_large(dev, apdev, test_params):
+    """FST STA configuration LLT is too large"""
+    res = run_test_sta_configuration(test_params,
+                                     fst_llt = '4294967296') #0x100000000
+    if res == 0:
+        raise Exception("wpa_supplicant started with llt that is too large")
+
+def test_fst_sta_config_llt_nan(dev, apdev, test_params):
+    """FST STA configuration LLT is not a number"""
+    res = run_test_sta_configuration(test_params, fst_llt = 'nan')
+    if res == 0:
+        raise Exception("wpa_supplicant started with llt not a number")
+
+def test_fst_sta_config_pri_neg(dev, apdev, test_params):
+    """FST STA configuration Priority negative"""
+    res = run_test_sta_configuration(test_params, fst_pri = '-1')
+    if res == 0:
+        raise Exception("wpa_supplicant started with a negative fst priority")
+
+def test_fst_sta_config_pri_zero(dev, apdev, test_params):
+    """FST STA configuration Priority zero"""
+    res = run_test_sta_configuration(test_params, fst_pri = '0')
+    if res == 0:
+        raise Exception("wpa_supplicant started with a zero fst priority")
+
+def test_fst_sta_config_pri_big(dev, apdev, test_params):
+    """FST STA configuration Priority too large"""
+    res = run_test_sta_configuration(test_params, fst_pri = '256')
+    if res == 0:
+        raise Exception("wpa_supplicant started with too large fst priority")
+
+def test_fst_sta_config_pri_nan(dev, apdev, test_params):
+    """FST STA configuration Priority not a number"""
+    res = run_test_sta_configuration(test_params, fst_pri = 'nan')
+    if res == 0:
+        raise Exception("wpa_supplicant started with fst priority not a number")
+
+def test_fst_sta_config_group_len(dev, apdev, test_params):
+    """FST STA configuration Group max length"""
+    res = run_test_sta_configuration(test_params,
+                                     fst_group = 'fstg5678abcd34567')
+    if res == 0:
+        raise Exception("wpa_supplicant started with fst_group length too big")
+
+def test_fst_sta_config_good(dev, apdev, test_params):
+    """FST STA configuration good parameters"""
+    res = run_test_sta_configuration(test_params)
+    if res != 0:
+        raise Exception("wpa_supplicant didn't start with valid config parameters")
+
+def test_fst_sta_config_default(dev, apdev, test_params):
+    """FST STA configuration default parameters"""
+    res = run_test_sta_configuration(test_params, fst_llt = None)
+    if res != 0:
+        raise Exception("wpa_supplicant didn't start with valid config parameters")
+
+def test_fst_scan_mb(dev, apdev, test_params):
+    """FST scan valid MB IE presence with normal start"""
+    logdir = test_params['logdir']
+
+    # Test valid MB IE in scan results
+    fst_launcher = FstLauncher(logdir)
+    ap1 = FstLauncherConfigAP(apdev[0]['ifname'], 'fst_11a', 'a',
+                              fst_test_common.fst_test_def_chan_a,
+                              fst_test_common.fst_test_def_group,
+                              fst_test_common.fst_test_def_prio_high)
+    ap2 = FstLauncherConfigAP(apdev[1]['ifname'], 'fst_11g', 'b',
+                              fst_test_common.fst_test_def_chan_g,
+                              fst_test_common.fst_test_def_group,
+                              fst_test_common.fst_test_def_prio_low)
+    fst_launcher.add_cfg(ap1)
+    fst_launcher.add_cfg(ap2)
+    res = fst_launcher.run_hostapd()
+    if res != 0:
+        raise Exception("hostapd didn't start properly")
+    try:
+        mbie1=[]
+        flags1 = ''
+        mbie2=[]
+        flags2 = ''
+        # Scan 1st AP
+        vals1 = scan_and_get_bss(dev[0], fst_test_common.fst_test_def_freq_a)
+        if vals1 != None:
+            if 'ie' in vals1:
+                mbie1 = parse_ies(vals1['ie'], 0x9e)
+            if 'flags' in vals1:
+                flags1 = vals1['flags']
+        # Scan 2nd AP
+        vals2 = scan_and_get_bss(dev[2], fst_test_common.fst_test_def_freq_g)
+        if vals2 != None:
+            if 'ie' in vals2:
+                mbie2 = parse_ies(vals2['ie'],0x9e)
+            if 'flags' in vals2:
+                flags2 = vals2['flags']
+    finally:
+         fst_launcher.cleanup()
+
+    if len(mbie1) == 0:
+        raise Exception("No MB IE created by 1st AP")
+    if len(mbie2) == 0:
+        raise Exception("No MB IE created by 2nd AP")
+
+def test_fst_scan_nomb(dev, apdev, test_params):
+    """FST scan no MB IE presence with 1 AP start"""
+    logdir = test_params['logdir']
+
+    # Test valid MB IE in scan results
+    fst_launcher = FstLauncher(logdir)
+    ap1 = FstLauncherConfigAP(apdev[0]['ifname'], 'fst_11a', 'a',
+                              fst_test_common.fst_test_def_chan_a,
+                              fst_test_common.fst_test_def_group,
+                              fst_test_common.fst_test_def_prio_high)
+    fst_launcher.add_cfg(ap1)
+    res = fst_launcher.run_hostapd()
+    if res != 0:
+        raise Exception("Hostapd didn't start properly")
+    try:
+        time.sleep(2)
+        mbie1=[]
+        flags1 = ''
+        vals1 = scan_and_get_bss(dev[0], fst_test_common.fst_test_def_freq_a)
+        if vals1 != None:
+            if 'ie' in vals1:
+                mbie1 = parse_ies(vals1['ie'], 0x9e)
+            if 'flags' in vals1:
+                flags1 = vals1['flags']
+    finally:
+        fst_launcher.cleanup()
+
+    if len(mbie1) != 0:
+        raise Exception("MB IE exists with 1 AP")
diff --git a/hostap/tests/hwsim/test_fst_module.py b/hostap/tests/hwsim/test_fst_module.py
new file mode 100644
index 0000000..1f62db1
--- /dev/null
+++ b/hostap/tests/hwsim/test_fst_module.py
@@ -0,0 +1,2815 @@
+# FST functionality tests
+# Copyright (c) 2015, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import struct
+import subprocess
+import time
+import os
+import re
+
+import hwsim_utils
+from hwsim import HWSimRadio
+import hostapd
+from wpasupplicant import WpaSupplicant
+import fst_test_common
+import fst_module_aux
+from utils import alloc_fail, HwsimSkip
+
+#enum - bad parameter types
+bad_param_none = 0
+bad_param_session_add_no_params = 1
+bad_param_group_id = 2
+bad_param_session_set_no_params = 3
+bad_param_session_set_unknown_param = 4
+bad_param_session_id = 5
+bad_param_old_iface = 6
+bad_param_new_iface = 7
+bad_param_negative_llt = 8
+bad_param_zero_llt = 9
+bad_param_llt_too_big = 10
+bad_param_llt_nan = 11
+bad_param_peer_addr = 12
+bad_param_session_initiate_no_params = 13
+bad_param_session_initiate_bad_session_id = 14
+bad_param_session_initiate_with_no_new_iface_set = 15
+bad_param_session_initiate_with_bad_peer_addr_set = 16
+bad_param_session_initiate_request_with_bad_stie = 17
+bad_param_session_initiate_response_with_reject = 18
+bad_param_session_initiate_response_with_bad_stie = 19
+bad_param_session_initiate_response_with_zero_llt = 20
+bad_param_session_initiate_stt_no_response = 21
+bad_param_session_initiate_concurrent_setup_request = 22
+bad_param_session_transfer_no_params = 23
+bad_param_session_transfer_bad_session_id = 24
+bad_param_session_transfer_setup_skipped = 25
+bad_param_session_teardown_no_params = 26
+bad_param_session_teardown_bad_session_id = 27
+bad_param_session_teardown_setup_skipped = 28
+bad_param_session_teardown_bad_fsts_id = 29
+
+bad_param_names = ("None",
+                   "No params passed to session add",
+                   "Group ID",
+                   "No params passed to session set",
+                   "Unknown param passed to session set",
+                   "Session ID",
+                   "Old interface name",
+                   "New interface name",
+                   "Negative LLT",
+                   "Zero LLT",
+                   "LLT too big",
+                   "LLT is not a number",
+                   "Peer address",
+                   "No params passed to session initiate",
+                   "Session ID",
+                   "No new_iface was set",
+                   "Peer address",
+                   "Request with bad st ie",
+                   "Response with reject",
+                   "Response with bad st ie",
+                   "Response with zero llt",
+                   "No response, STT",
+                   "Concurrent setup request",
+                   "No params passed to session transfer",
+                   "Session ID",
+                   "Session setup skipped",
+                   "No params passed to session teardown",
+                   "Bad session",
+                   "Session setup skipped",
+                   "Bad fsts_id")
+
+def fst_start_session(apdev, test_params, bad_param_type, start_on_ap,
+                      peer_addr = None):
+    """This function makes the necessary preparations and the adds and sets a
+    session using either correct or incorrect parameters depending on the value
+    of bad_param_type. If the call ends as expected (with session being
+    successfully added and set in case of correct parameters or with the
+    expected exception in case of incorrect parameters), the function silently
+    exits. Otherwise, it throws an exception thus failing the test."""
+
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    bad_parameter_detected = False
+    exception_already_raised = False
+    try:
+        fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        if start_on_ap:
+            initiator = ap1
+            responder = sta1
+            new_iface = ap2.ifname()
+            new_peer_addr = ap2.get_actual_peer_addr()
+        else:
+            initiator = sta1
+            responder = ap1
+            new_iface = sta2.ifname()
+            new_peer_addr = sta2.get_actual_peer_addr()
+        initiator.add_peer(responder, peer_addr, new_peer_addr)
+        group_id = None
+        if bad_param_type == bad_param_group_id:
+            group_id = '-1'
+        elif bad_param_type == bad_param_session_add_no_params:
+            group_id = ''
+        initiator.set_fst_parameters(group_id=group_id)
+        sid = initiator.add_session()
+        if bad_param_type == bad_param_session_set_no_params:
+            res = initiator.set_session_param(None)
+            if not res.startswith("OK"):
+                raise Exception("Session set operation failed")
+        elif bad_param_type == bad_param_session_set_unknown_param:
+            res = initiator.set_session_param("bad_param=1")
+            if not res.startswith("OK"):
+                raise Exception("Session set operation failed")
+        else:
+            if bad_param_type == bad_param_session_initiate_with_no_new_iface_set:
+                new_iface = None
+            elif bad_param_type == bad_param_new_iface:
+                new_iface = 'wlan12'
+            old_iface = None if bad_param_type != bad_param_old_iface else 'wlan12'
+            llt = None
+            if bad_param_type == bad_param_negative_llt:
+                llt = '-1'
+            elif bad_param_type == bad_param_zero_llt:
+                llt = '0'
+            elif bad_param_type == bad_param_llt_too_big:
+                llt = '4294967296'    #0x100000000
+            elif bad_param_type == bad_param_llt_nan:
+                llt = 'nan'
+            elif bad_param_type == bad_param_session_id:
+                sid = '-1'
+            initiator.set_fst_parameters(llt=llt)
+            initiator.configure_session(sid, new_iface, old_iface)
+    except Exception, e:
+        if e.args[0].startswith("Cannot add FST session with groupid"):
+            if bad_param_type == bad_param_group_id or bad_param_type == bad_param_session_add_no_params:
+                bad_parameter_detected = True
+        elif e.args[0].startswith("Cannot set FST session new_ifname:"):
+            if bad_param_type == bad_param_new_iface:
+                bad_parameter_detected = True
+        elif e.args[0].startswith("Session set operation failed"):
+            if (bad_param_type == bad_param_session_set_no_params or
+                bad_param_type == bad_param_session_set_unknown_param):
+                bad_parameter_detected = True
+        elif e.args[0].startswith("Cannot set FST session old_ifname:"):
+            if (bad_param_type == bad_param_old_iface or
+                bad_param_type == bad_param_session_id or
+                bad_param_type == bad_param_session_set_no_params):
+                bad_parameter_detected = True
+        elif e.args[0].startswith("Cannot set FST session llt:"):
+            if (bad_param_type == bad_param_negative_llt or
+                bad_param_type == bad_param_llt_too_big or
+                bad_param_type == bad_param_llt_nan):
+                bad_parameter_detected = True
+        elif e.args[0].startswith("Cannot set FST session peer address:"):
+            if bad_param_type == bad_param_peer_addr:
+                bad_parameter_detected = True
+        if not bad_parameter_detected:
+            # The exception was unexpected
+            logger.info(e)
+            exception_already_raised = True
+            raise
+    finally:
+        fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        if not exception_already_raised:
+            if bad_parameter_detected:
+                logger.info("Success. Bad parameter was detected (%s)" % bad_param_names[bad_param_type])
+            else:
+                if bad_param_type == bad_param_none or bad_param_type == bad_param_zero_llt:
+                    logger.info("Success. Session added and set")
+                else:
+                    exception_text = ""
+                    if bad_param_type == bad_param_peer_addr:
+                        exception_text = "Failure. Bad parameter was not detected (Peer address == %s)" % ap1.get_new_peer_addr()
+                    else:
+                        exception_text = "Failure. Bad parameter was not detected (%s)" % bad_param_names[bad_param_type]
+                    raise Exception(exception_text)
+        else:
+            print "Failure. Unexpected exception"
+
+def fst_initiate_session(apdev, test_params, bad_param_type, init_on_ap):
+    """This function makes the necessary preparations and then adds, sets and
+    initiates a session using either correct or incorrect parameters at each
+    stage depending on the value of bad_param_type. If the call ends as expected
+    (with session being successfully added, set and initiated in case of correct
+    parameters or with the expected exception in case of incorrect parameters),
+    the function silently exits. Otherwise it throws an exception thus failing
+    the test."""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    bad_parameter_detected = False
+    exception_already_raised = False
+    try:
+        fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        # This call makes sure FstHostapd singleton object is created and, as a
+        # result, the global control interface is registered (this is done from
+        # the constructor).
+        ap1.get_global_instance()
+        if init_on_ap:
+            initiator = ap1
+            responder = sta1
+            new_iface = ap2.ifname() if bad_param_type != bad_param_session_initiate_with_no_new_iface_set else None
+            new_peer_addr = ap2.get_actual_peer_addr()
+            resp_newif = sta2.ifname()
+        else:
+            initiator = sta1
+            responder = ap1
+            new_iface = sta2.ifname() if bad_param_type != bad_param_session_initiate_with_no_new_iface_set else None
+            new_peer_addr = sta2.get_actual_peer_addr()
+            resp_newif = ap2.ifname()
+        peeraddr = None if bad_param_type != bad_param_session_initiate_with_bad_peer_addr_set else '10:DE:AD:DE:AD:11'
+        initiator.add_peer(responder, peeraddr, new_peer_addr)
+        if bad_param_type == bad_param_session_initiate_response_with_zero_llt:
+            initiator.set_fst_parameters(llt='0')
+        sid = initiator.add_session()
+        initiator.configure_session(sid, new_iface)
+        if bad_param_type == bad_param_session_initiate_no_params:
+            sid = ''
+        elif bad_param_type == bad_param_session_initiate_bad_session_id:
+            sid = '-1'
+        if bad_param_type == bad_param_session_initiate_request_with_bad_stie:
+            actual_fsts_id = initiator.get_fsts_id_by_sid(sid)
+            initiator.send_test_session_setup_request(str(actual_fsts_id), "bad_new_band")
+            responder.wait_for_session_event(5)
+        elif bad_param_type == bad_param_session_initiate_response_with_reject:
+            initiator.send_session_setup_request(sid)
+            initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+            setup_event = responder.wait_for_session_event(5, [],
+                                                           ['EVENT_FST_SETUP'])
+            if not 'id' in setup_event:
+                raise Exception("No session id in FST setup event")
+            responder.send_session_setup_response(str(setup_event['id']),
+                                                  "reject")
+            event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+            if event['new_state'] != "INITIAL" or event['reason'] != "REASON_REJECT":
+                raise Exception("Response with reject not handled as expected")
+            bad_parameter_detected = True
+        elif bad_param_type == bad_param_session_initiate_response_with_bad_stie:
+            initiator.send_session_setup_request(sid)
+            initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+            responder.wait_for_session_event(5, [], ['EVENT_FST_SETUP'])
+            actual_fsts_id = initiator.get_fsts_id_by_sid(sid)
+            responder.send_test_session_setup_response(str(actual_fsts_id),
+                                                       "accept", "bad_new_band")
+            event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+            if event['new_state'] != "INITIAL" or event['reason'] != "REASON_ERROR_PARAMS":
+                raise Exception("Response with bad STIE not handled as expected")
+            bad_parameter_detected = True
+        elif bad_param_type == bad_param_session_initiate_response_with_zero_llt:
+            initiator.initiate_session(sid, "accept")
+            event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+            if event['new_state'] != "TRANSITION_DONE":
+                raise Exception("Response reception for a session with llt=0 not handled as expected")
+            bad_parameter_detected = True
+        elif bad_param_type == bad_param_session_initiate_stt_no_response:
+            initiator.send_session_setup_request(sid)
+            initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+            responder.wait_for_session_event(5, [], ['EVENT_FST_SETUP'])
+            event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+            if event['new_state'] != "INITIAL" or event['reason'] != "REASON_STT":
+                raise Exception("No response scenario not handled as expected")
+            bad_parameter_detected = True
+        elif bad_param_type == bad_param_session_initiate_concurrent_setup_request:
+            responder.add_peer(initiator)
+            resp_sid = responder.add_session()
+            responder.configure_session(resp_sid, resp_newif)
+            initiator.send_session_setup_request(sid)
+            actual_fsts_id = initiator.get_fsts_id_by_sid(sid)
+            responder.send_test_session_setup_request(str(actual_fsts_id))
+            event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+            initiator_addr = initiator.get_own_mac_address()
+            responder_addr = responder.get_own_mac_address()
+            if initiator_addr < responder_addr:
+                event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+                if event['new_state'] != "INITIAL" or event['reason'] != "REASON_SETUP":
+                    raise Exception("Concurrent setup scenario not handled as expected")
+                event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SETUP"])
+                # The incoming setup request received by the initiator has
+                # priority over the one sent previously by the initiator itself
+                # because the initiator's MAC address is numerically lower than
+                # the one of the responder. Thus, the initiator should generate
+                # an FST_SETUP event.
+            else:
+                event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+                if event['new_state'] != "INITIAL" or event['reason'] != "REASON_STT":
+                    raise Exception("Concurrent setup scenario not handled as expected")
+                # The incoming setup request was dropped at the initiator
+                # because its MAC address is numerically bigger than the one of
+                # the responder. Thus, the initiator continue to wait for a
+                # setup response until the STT event fires.
+            bad_parameter_detected = True
+        else:
+            initiator.initiate_session(sid, "accept")
+    except Exception, e:
+        if e.args[0].startswith("Cannot initiate fst session"):
+            if bad_param_type != bad_param_none:
+                bad_parameter_detected = True
+        elif e.args[0].startswith("No FST-EVENT-SESSION received"):
+            if bad_param_type == bad_param_session_initiate_request_with_bad_stie:
+                bad_parameter_detected = True
+        if not bad_parameter_detected:
+            #The exception was unexpected
+            logger.info(e)
+            exception_already_raised = True
+            raise
+    finally:
+        fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        if not exception_already_raised:
+            if bad_parameter_detected:
+                logger.info("Success. Bad parameter was detected (%s)" % bad_param_names[bad_param_type])
+            else:
+                if bad_param_type == bad_param_none:
+                    logger.info("Success. Session initiated")
+                else:
+                    raise Exception("Failure. Bad parameter was not detected (%s)" % bad_param_names[bad_param_type])
+        else:
+            print "Failure. Unexpected exception"
+
+def fst_transfer_session(apdev, test_params, bad_param_type, init_on_ap,
+                         rsn=False):
+    """This function makes the necessary preparations and then adds, sets,
+    initiates and attempts to transfer a session using either correct or
+    incorrect parameters at each stage depending on the value of bad_param_type.
+    If the call ends as expected the function silently exits. Otherwise, it
+    throws an exception thus failing the test."""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev, rsn=rsn)
+    bad_parameter_detected = False
+    exception_already_raised = False
+    try:
+        fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2, rsn=rsn)
+        # This call makes sure FstHostapd singleton object is created and, as a
+        # result, the global control interface is registered (this is done from
+        # the constructor).
+        ap1.get_global_instance()
+        if init_on_ap:
+            initiator = ap1
+            responder = sta1
+            new_iface = ap2.ifname()
+            new_peer_addr = ap2.get_actual_peer_addr()
+        else:
+            initiator = sta1
+            responder = ap1
+            new_iface = sta2.ifname()
+            new_peer_addr = sta2.get_actual_peer_addr()
+        initiator.add_peer(responder, new_peer_addr = new_peer_addr)
+        sid = initiator.add_session()
+        initiator.configure_session(sid, new_iface)
+        if bad_param_type != bad_param_session_transfer_setup_skipped:
+            initiator.initiate_session(sid, "accept")
+        if bad_param_type == bad_param_session_transfer_no_params:
+            sid = ''
+        elif bad_param_type == bad_param_session_transfer_bad_session_id:
+            sid = '-1'
+        initiator.transfer_session(sid)
+    except Exception, e:
+        if e.args[0].startswith("Cannot transfer fst session"):
+            if bad_param_type != bad_param_none:
+                bad_parameter_detected = True
+        if not bad_parameter_detected:
+            # The exception was unexpected
+            logger.info(e)
+            exception_already_raised = True
+            raise
+    finally:
+        fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        if not exception_already_raised:
+            if bad_parameter_detected:
+                logger.info("Success. Bad parameter was detected (%s)" % bad_param_names[bad_param_type])
+            else:
+                if bad_param_type == bad_param_none:
+                    logger.info("Success. Session transferred")
+                else:
+                    raise Exception("Failure. Bad parameter was not detected (%s)" % bad_param_names[bad_param_type])
+        else:
+            print "Failure. Unexpected exception"
+
+
+def fst_tear_down_session(apdev, test_params, bad_param_type, init_on_ap):
+    """This function makes the necessary preparations and then adds, sets, and
+    initiates a session. It then issues a tear down command using either
+    correct or incorrect parameters at each stage. If the call ends as expected,
+    the function silently exits. Otherwise, it throws an exception thus failing
+    the test."""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    bad_parameter_detected = False
+    exception_already_raised = False
+    try:
+        fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        # This call makes sure FstHostapd singleton object is created and, as a
+        # result, the global control interface is registered (this is done from
+        # the constructor).
+        ap1.get_global_instance()
+        if init_on_ap:
+            initiator = ap1
+            responder = sta1
+            new_iface = ap2.ifname()
+            new_peer_addr = ap2.get_actual_peer_addr()
+        else:
+            initiator = sta1
+            responder = ap1
+            new_iface = sta2.ifname()
+            new_peer_addr = sta2.get_actual_peer_addr()
+        initiator.add_peer(responder, new_peer_addr = new_peer_addr)
+        sid = initiator.add_session()
+        initiator.configure_session(sid, new_iface)
+        if bad_param_type != bad_param_session_teardown_setup_skipped:
+            initiator.initiate_session(sid, "accept")
+        if bad_param_type == bad_param_session_teardown_bad_fsts_id:
+            initiator.send_test_tear_down('-1')
+            responder.wait_for_session_event(5)
+        else:
+            if bad_param_type == bad_param_session_teardown_no_params:
+                sid = ''
+            elif bad_param_type == bad_param_session_teardown_bad_session_id:
+                sid = '-1'
+            initiator.teardown_session(sid)
+    except Exception, e:
+        if e.args[0].startswith("Cannot tear down fst session"):
+            if (bad_param_type == bad_param_session_teardown_no_params or
+                bad_param_type == bad_param_session_teardown_bad_session_id or
+                bad_param_type == bad_param_session_teardown_setup_skipped):
+                bad_parameter_detected = True
+        elif e.args[0].startswith("No FST-EVENT-SESSION received"):
+            if bad_param_type == bad_param_session_teardown_bad_fsts_id:
+                bad_parameter_detected = True
+        if not bad_parameter_detected:
+            # The exception was unexpected
+            logger.info(e)
+            exception_already_raised = True
+            raise
+    finally:
+        fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        if not exception_already_raised:
+            if bad_parameter_detected:
+                logger.info("Success. Bad parameter was detected (%s)" % bad_param_names[bad_param_type])
+            else:
+                if bad_param_type == bad_param_none:
+                    logger.info("Success. Session torn down")
+                else:
+                    raise Exception("Failure. Bad parameter was not detected (%s)" % bad_param_names[bad_param_type])
+        else:
+            print "Failure. Unexpected exception"
+
+
+#enum - remove session scenarios
+remove_scenario_no_params = 0
+remove_scenario_bad_session_id = 1
+remove_scenario_non_established_session = 2
+remove_scenario_established_session = 3
+
+remove_scenario_names = ("No params",
+                         "Bad session id",
+                         "Remove non-established session",
+                         "Remove established session")
+
+
+def fst_remove_session(apdev, test_params, remove_session_scenario, init_on_ap):
+    """This function attempts to remove a session at various stages of its
+    formation, depending on the value of remove_session_scenario. If the call
+    ends as expected, the function silently exits. Otherwise, it throws an
+    exception thus failing the test."""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    bad_parameter_detected = False
+    exception_already_raised = False
+    try:
+        fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        # This call makes sure FstHostapd singleton object is created and, as a
+        # result, the global control interface is registered (this is done from
+        # the constructor).
+        ap1.get_global_instance()
+        if init_on_ap:
+            initiator = ap1
+            responder = sta1
+            new_iface = ap2.ifname()
+            new_peer_addr = ap2.get_actual_peer_addr()
+        else:
+            initiator = sta1
+            responder = ap1
+            new_iface = sta2.ifname()
+            new_peer_addr = sta2.get_actual_peer_addr()
+        initiator.add_peer(responder, new_peer_addr = new_peer_addr)
+        sid = initiator.add_session()
+        initiator.configure_session(sid, new_iface)
+        if remove_session_scenario != remove_scenario_no_params:
+            if remove_session_scenario != remove_scenario_non_established_session:
+                initiator.initiate_session(sid, "accept")
+        if remove_session_scenario == remove_scenario_no_params:
+            sid = ''
+        elif remove_session_scenario == remove_scenario_bad_session_id:
+            sid = '-1'
+        initiator.remove_session(sid)
+    except Exception, e:
+        if e.args[0].startswith("Cannot remove fst session"):
+            if (remove_session_scenario == remove_scenario_no_params or
+                remove_session_scenario == remove_scenario_bad_session_id):
+                bad_parameter_detected = True
+        elif e.args[0].startswith("No FST-EVENT-SESSION received"):
+            if remove_session_scenario == remove_scenario_non_established_session:
+                bad_parameter_detected = True
+        if not bad_parameter_detected:
+            #The exception was unexpected
+            logger.info(e)
+            exception_already_raised = True
+            raise
+    finally:
+        fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        if not exception_already_raised:
+            if bad_parameter_detected:
+                logger.info("Success. Remove scenario ended as expected (%s)" % remove_scenario_names[remove_session_scenario])
+            else:
+                if remove_session_scenario == remove_scenario_established_session:
+                    logger.info("Success. Session removed")
+                else:
+                    raise Exception("Failure. Remove scenario ended in an unexpected way (%s)" % remove_scenario_names[remove_session_scenario])
+        else:
+            print "Failure. Unexpected exception"
+
+
+#enum - frame types
+frame_type_session_request = 0
+frame_type_session_response = 1
+frame_type_ack_request = 2
+frame_type_ack_response = 3
+frame_type_tear_down = 4
+
+frame_type_names = ("Session request",
+                    "Session Response",
+                    "Ack request",
+                    "Ack response",
+                    "Tear down")
+
+def fst_send_unexpected_frame(apdev, test_params, frame_type, send_from_ap, additional_param = ''):
+    """This function creates two pairs of APs and stations, makes them connect
+    and then causes one side to send an unexpected FST frame of the specified
+    type to the other. The other side should then identify and ignore the
+    frame."""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    exception_already_raised = False
+    frame_receive_timeout = False
+    try:
+        fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        # This call makes sure FstHostapd singleton object is created and, as a
+        # result, the global control interface is registered (this is done from
+        # the constructor).
+        ap1.get_global_instance()
+        if send_from_ap:
+            sender = ap1
+            receiver = sta1
+            new_iface = ap2.ifname()
+            new_peer_addr = ap2.get_actual_peer_addr()
+        else:
+            sender = sta1
+            receiver = ap1
+            new_iface = sta2.ifname()
+            new_peer_addr = sta2.get_actual_peer_addr()
+        sender.add_peer(receiver, new_peer_addr = new_peer_addr)
+        sid=sender.add_session()
+        sender.configure_session(sid, new_iface)
+        if frame_type == frame_type_session_request:
+            sender.send_session_setup_request(sid)
+            event = receiver.wait_for_session_event(5)
+            if event['type'] != 'EVENT_FST_SETUP':
+                raise Exception("Unexpected indication: " + event['type'])
+        elif frame_type == frame_type_session_response:
+            #fsts_id doesn't matter, no actual session exists
+            sender.send_test_session_setup_response('0', additional_param)
+            receiver.wait_for_session_event(5)
+        elif frame_type == frame_type_ack_request:
+            #fsts_id doesn't matter, no actual session exists
+            sender.send_test_ack_request('0')
+            receiver.wait_for_session_event(5)
+        elif frame_type == frame_type_ack_response:
+            #fsts_id doesn't matter, no actual session exists
+            sender.send_test_ack_response('0')
+            receiver.wait_for_session_event(5)
+        elif frame_type == frame_type_tear_down:
+            #fsts_id doesn't matter, no actual session exists
+            sender.send_test_tear_down('0')
+            receiver.wait_for_session_event(5)
+    except Exception, e:
+        if e.args[0].startswith("No FST-EVENT-SESSION received"):
+            if frame_type != frame_type_session_request:
+                frame_receive_timeout = True
+        else:
+            logger.info(e)
+            exception_already_raised = True
+            raise
+    finally:
+        fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        if not exception_already_raised:
+            if frame_receive_timeout:
+                logger.info("Success. Frame was ignored (%s)" % frame_type_names[frame_type])
+            else:
+                if frame_type == frame_type_session_request:
+                    logger.info("Success. Frame received, session created")
+                else:
+                    raise Exception("Failure. Frame was not ignored (%s)" % frame_type_names[frame_type])
+        else:
+            print "Failure. Unexpected exception"
+
+
+#enum - bad session transfer scenarios
+bad_scenario_none = 0
+bad_scenario_ack_req_session_not_set_up = 1
+bad_scenario_ack_req_session_not_established_init_side = 2
+bad_scenario_ack_req_session_not_established_resp_side = 3
+bad_scenario_ack_req_bad_fsts_id = 4
+bad_scenario_ack_resp_session_not_set_up = 5
+bad_scenario_ack_resp_session_not_established_init_side = 6
+bad_scenario_ack_resp_session_not_established_resp_side = 7
+bad_scenario_ack_resp_no_ack_req = 8
+bad_scenario_ack_resp_bad_fsts_id = 9
+
+bad_scenario_names = ("None",
+                      "Ack request received before the session was set up",
+                      "Ack request received on the initiator side before session was established",
+                      "Ack request received on the responder side before session was established",
+                      "Ack request received with bad fsts_id",
+                      "Ack response received before the session was set up",
+                      "Ack response received on the initiator side before session was established",
+                      "Ack response received on the responder side before session was established",
+                      "Ack response received before ack request was sent",
+                      "Ack response received with bad fsts_id")
+
+def fst_bad_transfer(apdev, test_params, bad_scenario_type, init_on_ap):
+    """This function makes the necessary preparations and then adds and sets a
+    session. It then initiates and it unless instructed otherwise) and attempts
+    to send one of the frames involved in the session transfer protocol,
+    skipping or distorting one of the stages according to the value of
+    bad_scenario_type parameter."""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    bad_parameter_detected = False
+    exception_already_raised = False
+    try:
+        fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        # This call makes sure FstHostapd singleton object is created and, as a
+        # result, the global control interface is registered (this is done from
+        # the constructor).
+        ap1.get_global_instance()
+        if init_on_ap:
+            initiator = ap1
+            responder = sta1
+            new_iface = ap2.ifname()
+            new_peer_addr = ap2.get_actual_peer_addr()
+        else:
+            initiator = sta1
+            responder = ap1
+            new_iface = sta2.ifname()
+            new_peer_addr = sta2.get_actual_peer_addr()
+        initiator.add_peer(responder, new_peer_addr = new_peer_addr)
+        sid = initiator.add_session()
+        initiator.configure_session(sid, new_iface)
+        if (bad_scenario_type != bad_scenario_ack_req_session_not_set_up and
+            bad_scenario_type != bad_scenario_ack_resp_session_not_set_up):
+            if (bad_scenario_type != bad_scenario_ack_req_session_not_established_init_side and
+                bad_scenario_type != bad_scenario_ack_resp_session_not_established_init_side and
+                bad_scenario_type != bad_scenario_ack_req_session_not_established_resp_side and
+                bad_scenario_type != bad_scenario_ack_resp_session_not_established_resp_side):
+                response =  "accept"
+            else:
+                response = ''
+            initiator.initiate_session(sid, response)
+        if bad_scenario_type == bad_scenario_ack_req_session_not_set_up:
+            #fsts_id doesn't matter, no actual session exists
+            responder.send_test_ack_request('0')
+            initiator.wait_for_session_event(5)
+            # We want to send the unexpected frame to the side that already has
+            # a session created
+        elif bad_scenario_type == bad_scenario_ack_resp_session_not_set_up:
+            #fsts_id doesn't matter, no actual session exists
+            responder.send_test_ack_response('0')
+            initiator.wait_for_session_event(5)
+            # We want to send the unexpected frame to the side that already has
+            # a session created
+        elif bad_scenario_type == bad_scenario_ack_req_session_not_established_init_side:
+            #fsts_id doesn't matter, no actual session exists
+            initiator.send_test_ack_request('0')
+            responder.wait_for_session_event(5, ["EVENT_FST_SESSION_STATE"])
+        elif bad_scenario_type == bad_scenario_ack_req_session_not_established_resp_side:
+            #fsts_id doesn't matter, no actual session exists
+            responder.send_test_ack_request('0')
+            initiator.wait_for_session_event(5, ["EVENT_FST_SESSION_STATE"])
+        elif bad_scenario_type == bad_scenario_ack_resp_session_not_established_init_side:
+            #fsts_id doesn't matter, no actual session exists
+            initiator.send_test_ack_response('0')
+            responder.wait_for_session_event(5, ["EVENT_FST_SESSION_STATE"])
+        elif bad_scenario_type == bad_scenario_ack_resp_session_not_established_resp_side:
+            #fsts_id doesn't matter, no actual session exists
+            responder.send_test_ack_response('0')
+            initiator.wait_for_session_event(5, ["EVENT_FST_SESSION_STATE"])
+        elif bad_scenario_type == bad_scenario_ack_req_bad_fsts_id:
+            initiator.send_test_ack_request('-1')
+            responder.wait_for_session_event(5, ["EVENT_FST_SESSION_STATE"])
+        elif bad_scenario_type == bad_scenario_ack_resp_bad_fsts_id:
+            initiator.send_test_ack_response('-1')
+            responder.wait_for_session_event(5, ["EVENT_FST_SESSION_STATE"])
+        elif bad_scenario_type == bad_scenario_ack_resp_no_ack_req:
+            actual_fsts_id = initiator.get_fsts_id_by_sid(sid)
+            initiator.send_test_ack_response(str(actual_fsts_id))
+            responder.wait_for_session_event(5, ["EVENT_FST_SESSION_STATE"])
+        else:
+            raise Exception("Unknown bad scenario identifier")
+    except Exception, e:
+        if e.args[0].startswith("No FST-EVENT-SESSION received"):
+            bad_parameter_detected = True
+        if not bad_parameter_detected:
+            # The exception was unexpected
+            logger.info(e)
+            exception_already_raised = True
+            raise
+    finally:
+        fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        if not exception_already_raised:
+            if bad_parameter_detected:
+                logger.info("Success. Bad scenario was handled correctly (%s)" % bad_scenario_names[bad_scenario_type])
+            else:
+                raise Exception("Failure. Bad scenario was handled incorrectly (%s)" % bad_scenario_names[bad_scenario_type])
+        else:
+            print "Failure. Unexpected exception"
+
+def test_fst_sta_connect_to_non_fst_ap(dev, apdev, test_params):
+    """FST STA connecting to non-FST AP"""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    with HWSimRadio() as (radio, iface):
+        non_fst_ap = hostapd.add_ap(iface, { "ssid": "non_fst_11g" })
+        try:
+            orig_sta1_mbies = sta1.get_local_mbies()
+            orig_sta2_mbies = sta2.get_local_mbies()
+            vals = sta2.scan()
+            freq = vals['freq']
+            sta2.connect_to_external_ap(non_fst_ap, ssid="non_fst_11g",
+                                        key_mgmt="NONE", scan_freq=freq)
+            time.sleep(2)
+            res_sta1_mbies = sta1.get_local_mbies()
+            res_sta2_mbies = sta2.get_local_mbies()
+            if (orig_sta1_mbies.startswith("FAIL") or
+                orig_sta2_mbies.startswith("FAIL") or
+                not res_sta1_mbies.startswith("FAIL") or
+                not res_sta2_mbies.startswith("FAIL")):
+                raise Exception("Failure. MB IEs have not been removed on the stations")
+        except Exception, e:
+            logger.info(e)
+            raise
+        finally:
+            sta2.disconnect_from_external_ap()
+            fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+            hostapd.HostapdGlobal().remove(iface)
+
+def test_fst_sta_connect_to_fst_ap(dev, apdev, test_params):
+    """FST STA connecting to FST AP"""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    try:
+        orig_sta2_mbies = sta2.get_local_mbies()
+        vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+        sta1.connect(ap1, key_mgmt="NONE",
+                     scan_freq=fst_test_common.fst_test_def_freq_a)
+        time.sleep(2)
+        res_sta2_mbies = sta2.get_local_mbies()
+        if res_sta2_mbies == orig_sta2_mbies:
+            raise Exception("Failure. MB IEs have not been updated")
+    except Exception, e:
+        logger.info(e)
+        raise
+    finally:
+        sta1.disconnect()
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_ap_connect_to_fst_sta(dev, apdev, test_params):
+    """FST AP connecting to FST STA"""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    try:
+        orig_ap_mbies = ap1.get_local_mbies()
+        vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+        sta1.connect(ap1, key_mgmt="NONE",
+                     scan_freq=fst_test_common.fst_test_def_freq_a)
+        time.sleep(2)
+        res_ap_mbies = ap1.get_local_mbies()
+        if res_ap_mbies != orig_ap_mbies:
+            raise Exception("Failure. MB IEs have been unexpectedly updated on the AP")
+    except Exception, e:
+        logger.info(e)
+        raise
+    finally:
+        sta1.disconnect()
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_ap_connect_to_non_fst_sta(dev, apdev, test_params):
+    """FST AP connecting to non-FST STA"""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    try:
+        orig_ap_mbies = ap2.get_local_mbies()
+        vals = dev[0].scan(None, fst_test_common.fst_test_def_freq_g)
+        fst_module_aux.external_sta_connect(dev[0], ap2, key_mgmt="NONE",
+                                            scan_freq=fst_test_common.fst_test_def_freq_g)
+        time.sleep(2)
+        res_ap_mbies = ap2.get_local_mbies()
+        if res_ap_mbies != orig_ap_mbies:
+            raise Exception("Failure. MB IEs have been unexpectedly updated on the AP")
+    except Exception, e:
+        logger.info(e)
+        raise
+    finally:
+        fst_module_aux.disconnect_external_sta(dev[0], ap2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_second_sta_connect_to_non_fst_ap(dev, apdev, test_params):
+    """FST STA 2nd connecting to non-FST AP"""
+    fst_ap1, fst_ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    with HWSimRadio() as (radio, iface):
+        non_fst_ap = hostapd.add_ap(iface, { "ssid": "non_fst_11g" })
+        try:
+            vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+            sta1.connect(fst_ap1, key_mgmt="NONE", scan_freq=fst_test_common.fst_test_def_freq_a)
+            time.sleep(2)
+            orig_sta1_mbies = sta1.get_local_mbies()
+            orig_sta2_mbies = sta2.get_local_mbies()
+            vals = sta2.scan()
+            freq = vals['freq']
+            sta2.connect_to_external_ap(non_fst_ap, ssid="non_fst_11g", key_mgmt="NONE", scan_freq=freq)
+            time.sleep(2)
+            res_sta1_mbies = sta1.get_local_mbies()
+            res_sta2_mbies = sta2.get_local_mbies()
+            if (orig_sta1_mbies.startswith("FAIL") or
+                orig_sta2_mbies.startswith("FAIL") or
+                not res_sta1_mbies.startswith("FAIL") or
+                not res_sta2_mbies.startswith("FAIL")):
+                raise Exception("Failure. MB IEs have not been removed on the stations")
+        except Exception, e:
+            logger.info(e)
+            raise
+        finally:
+            sta1.disconnect()
+            sta2.disconnect_from_external_ap()
+            fst_module_aux.stop_two_ap_sta_pairs(fst_ap1, fst_ap2, sta1, sta2)
+            hostapd.HostapdGlobal().remove(iface)
+
+def test_fst_second_sta_connect_to_fst_ap(dev, apdev, test_params):
+    """FST STA 2nd connecting to FST AP"""
+    fst_ap1, fst_ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    with HWSimRadio() as (radio, iface):
+        non_fst_ap = hostapd.add_ap(iface, { "ssid": "non_fst_11g" })
+        try:
+            vals = sta2.scan()
+            freq = vals['freq']
+            sta2.connect_to_external_ap(non_fst_ap, ssid="non_fst_11g", key_mgmt="NONE", scan_freq=freq)
+            time.sleep(2)
+            orig_sta1_mbies = sta1.get_local_mbies()
+            orig_sta2_mbies = sta2.get_local_mbies()
+            vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+            sta1.connect(fst_ap1, key_mgmt="NONE", scan_freq=fst_test_common.fst_test_def_freq_a)
+            time.sleep(2)
+            res_sta1_mbies = sta1.get_local_mbies()
+            res_sta2_mbies = sta2.get_local_mbies()
+            if (not orig_sta1_mbies.startswith("FAIL") or
+                not orig_sta2_mbies.startswith("FAIL") or
+                not res_sta1_mbies.startswith("FAIL") or
+                not res_sta2_mbies.startswith("FAIL")):
+                raise Exception("Failure. MB IEs should have stayed non-present on the stations")
+        except Exception, e:
+            logger.info(e)
+            raise
+        finally:
+            sta1.disconnect()
+            sta2.disconnect_from_external_ap()
+            fst_module_aux.stop_two_ap_sta_pairs(fst_ap1, fst_ap2, sta1, sta2)
+            hostapd.HostapdGlobal().remove(iface)
+
+def test_fst_disconnect_1_of_2_stas_from_non_fst_ap(dev, apdev, test_params):
+    """FST disconnect 1 of 2 STAs from non-FST AP"""
+    fst_ap1, fst_ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    with HWSimRadio() as (radio, iface):
+        non_fst_ap = hostapd.add_ap(iface, { "ssid": "non_fst_11g" })
+        try:
+            vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+            sta1.connect(fst_ap1, key_mgmt="NONE", scan_freq=fst_test_common.fst_test_def_freq_a)
+            vals = sta2.scan()
+            freq = vals['freq']
+            sta2.connect_to_external_ap(non_fst_ap, ssid="non_fst_11g", key_mgmt="NONE", scan_freq=freq)
+            time.sleep(2)
+            orig_sta1_mbies = sta1.get_local_mbies()
+            orig_sta2_mbies = sta2.get_local_mbies()
+            sta2.disconnect_from_external_ap()
+            time.sleep(2)
+            res_sta1_mbies = sta1.get_local_mbies()
+            res_sta2_mbies = sta2.get_local_mbies()
+            if (not orig_sta1_mbies.startswith("FAIL") or
+                not orig_sta2_mbies.startswith("FAIL") or
+                res_sta1_mbies.startswith("FAIL") or
+                res_sta2_mbies.startswith("FAIL")):
+                raise Exception("Failure. MB IEs haven't reappeared on the stations")
+        except Exception, e:
+            logger.info(e)
+            raise
+        finally:
+            sta1.disconnect()
+            sta2.disconnect_from_external_ap()
+            fst_module_aux.stop_two_ap_sta_pairs(fst_ap1, fst_ap2, sta1, sta2)
+            hostapd.HostapdGlobal().remove(iface)
+
+def test_fst_disconnect_1_of_2_stas_from_fst_ap(dev, apdev, test_params):
+    """FST disconnect 1 of 2 STAs from FST AP"""
+    fst_ap1, fst_ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    with HWSimRadio() as (radio, iface):
+        non_fst_ap = hostapd.add_ap(iface, { "ssid": "non_fst_11g" })
+        try:
+            vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+            sta1.connect(fst_ap1, key_mgmt="NONE", scan_freq=fst_test_common.fst_test_def_freq_a)
+            vals = sta2.scan()
+            freq = vals['freq']
+            sta2.connect_to_external_ap(non_fst_ap, ssid="non_fst_11g", key_mgmt="NONE", scan_freq=freq)
+            time.sleep(2)
+            orig_sta1_mbies = sta1.get_local_mbies()
+            orig_sta2_mbies = sta2.get_local_mbies()
+            sta1.disconnect()
+            time.sleep(2)
+            res_sta1_mbies = sta1.get_local_mbies()
+            res_sta2_mbies = sta2.get_local_mbies()
+            if (not orig_sta1_mbies.startswith("FAIL") or
+                not orig_sta2_mbies.startswith("FAIL") or
+                not res_sta1_mbies.startswith("FAIL") or
+                not res_sta2_mbies.startswith("FAIL")):
+                raise Exception("Failure. MB IEs should have stayed non-present on the stations")
+        except Exception, e:
+            logger.info(e)
+            raise
+        finally:
+            sta1.disconnect()
+            sta2.disconnect_from_external_ap()
+            fst_module_aux.stop_two_ap_sta_pairs(fst_ap1, fst_ap2, sta1, sta2)
+            hostapd.HostapdGlobal().remove(iface)
+
+def test_fst_disconnect_2_of_2_stas_from_non_fst_ap(dev, apdev, test_params):
+    """FST disconnect 2 of 2 STAs from non-FST AP"""
+    fst_ap1, fst_ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    with HWSimRadio() as (radio, iface):
+        non_fst_ap = hostapd.add_ap(iface, { "ssid": "non_fst_11g" })
+        try:
+            vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+            sta1.connect(fst_ap1, key_mgmt="NONE", scan_freq=fst_test_common.fst_test_def_freq_a)
+            vals = sta2.scan()
+            freq = vals['freq']
+            sta2.connect_to_external_ap(non_fst_ap, ssid="non_fst_11g", key_mgmt="NONE", scan_freq=freq)
+            time.sleep(2)
+            sta1.disconnect()
+            time.sleep(2)
+            orig_sta1_mbies = sta1.get_local_mbies()
+            orig_sta2_mbies = sta2.get_local_mbies()
+            sta2.disconnect_from_external_ap()
+            time.sleep(2)
+            res_sta1_mbies = sta1.get_local_mbies()
+            res_sta2_mbies = sta2.get_local_mbies()
+            if (not orig_sta1_mbies.startswith("FAIL") or
+                not orig_sta2_mbies.startswith("FAIL") or
+                res_sta1_mbies.startswith("FAIL") or
+                res_sta2_mbies.startswith("FAIL")):
+                raise Exception("Failure. MB IEs haven't reappeared on the stations")
+        except Exception, e:
+            logger.info(e)
+            raise
+        finally:
+            sta1.disconnect()
+            sta2.disconnect_from_external_ap()
+            fst_module_aux.stop_two_ap_sta_pairs(fst_ap1, fst_ap2, sta1, sta2)
+            hostapd.HostapdGlobal().remove(iface)
+
+def test_fst_disconnect_2_of_2_stas_from_fst_ap(dev, apdev, test_params):
+    """FST disconnect 2 of 2 STAs from FST AP"""
+    fst_ap1, fst_ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    with HWSimRadio() as (radio, iface):
+        non_fst_ap = hostapd.add_ap(iface, { "ssid": "non_fst_11g"})
+        try:
+            vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+            sta1.connect(fst_ap1, key_mgmt="NONE", scan_freq=fst_test_common.fst_test_def_freq_a)
+            vals = sta2.scan()
+            freq = vals['freq']
+            sta2.connect_to_external_ap(non_fst_ap, ssid="non_fst_11g", key_mgmt="NONE", scan_freq=freq)
+            time.sleep(2)
+            sta2.disconnect_from_external_ap()
+            time.sleep(2)
+            orig_sta1_mbies = sta1.get_local_mbies()
+            orig_sta2_mbies = sta2.get_local_mbies()
+            sta1.disconnect()
+            time.sleep(2)
+            res_sta1_mbies = sta1.get_local_mbies()
+            res_sta2_mbies = sta2.get_local_mbies()
+            if (orig_sta1_mbies.startswith("FAIL") or
+                orig_sta2_mbies.startswith("FAIL") or
+                res_sta1_mbies.startswith("FAIL") or
+                res_sta2_mbies.startswith("FAIL")):
+                raise Exception("Failure. MB IEs should have stayed present on both stations")
+            # Mandatory part of 8.4.2.140 Multi-band element is 24 bytes = 48 hex chars
+            basic_sta1_mbies = res_sta1_mbies[0:48] + res_sta1_mbies[60:108]
+            basic_sta2_mbies = res_sta2_mbies[0:48] + res_sta2_mbies[60:108]
+            if (basic_sta1_mbies != basic_sta2_mbies):
+                raise Exception("Failure. Basic MB IEs should have become identical on both stations")
+            addr_sta1_str = sta1.get_own_mac_address().replace(":", "")
+            addr_sta2_str = sta2.get_own_mac_address().replace(":", "")
+            # Mandatory part of 8.4.2.140 Multi-band element is followed by STA MAC Address field (6 bytes = 12 hex chars)
+            addr_sta1_mbie1 = res_sta1_mbies[48:60]
+            addr_sta1_mbie2 = res_sta1_mbies[108:120]
+            addr_sta2_mbie1 = res_sta2_mbies[48:60]
+            addr_sta2_mbie2 = res_sta2_mbies[108:120]
+            if (addr_sta1_mbie1 != addr_sta1_mbie2 or
+                addr_sta1_mbie1 != addr_sta2_str or
+                addr_sta2_mbie1 != addr_sta2_mbie2 or
+                addr_sta2_mbie1 != addr_sta1_str):
+                raise Exception("Failure. STA Address in MB IEs should have been same as the other STA's")
+        except Exception, e:
+            logger.info(e)
+            raise
+        finally:
+            sta1.disconnect()
+            sta2.disconnect_from_external_ap()
+            fst_module_aux.stop_two_ap_sta_pairs(fst_ap1, fst_ap2, sta1, sta2)
+            hostapd.HostapdGlobal().remove(iface)
+
+def test_fst_disconnect_non_fst_sta(dev, apdev, test_params):
+    """FST disconnect non-FST STA"""
+    ap1, ap2, fst_sta1, fst_sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    external_sta_connected = False
+    try:
+        vals = fst_sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+        fst_sta1.connect(ap1, key_mgmt="NONE",
+                         scan_freq=fst_test_common.fst_test_def_freq_a)
+        vals = dev[0].scan(None, fst_test_common.fst_test_def_freq_g)
+        fst_module_aux.external_sta_connect(dev[0], ap2, key_mgmt="NONE",
+                                            scan_freq=fst_test_common.fst_test_def_freq_g)
+        external_sta_connected = True
+        time.sleep(2)
+        fst_sta1.disconnect()
+        time.sleep(2)
+        orig_ap_mbies = ap2.get_local_mbies()
+        fst_module_aux.disconnect_external_sta(dev[0], ap2)
+        external_sta_connected = False
+        time.sleep(2)
+        res_ap_mbies = ap2.get_local_mbies()
+        if res_ap_mbies != orig_ap_mbies:
+            raise Exception("Failure. MB IEs have been unexpectedly updated on the AP")
+    except Exception, e:
+        logger.info(e)
+        raise
+    finally:
+        fst_sta1.disconnect()
+        if external_sta_connected:
+            fst_module_aux.disconnect_external_sta(dev[0], ap2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, fst_sta1, fst_sta2)
+
+def test_fst_disconnect_fst_sta(dev, apdev, test_params):
+    """FST disconnect FST STA"""
+    ap1, ap2, fst_sta1, fst_sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    external_sta_connected = False;
+    try:
+        vals = fst_sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+        fst_sta1.connect(ap1, key_mgmt="NONE",
+                         scan_freq=fst_test_common.fst_test_def_freq_a)
+        vals = dev[0].scan(None, fst_test_common.fst_test_def_freq_g)
+        fst_module_aux.external_sta_connect(dev[0], ap2, key_mgmt="NONE",
+                                            scan_freq=fst_test_common.fst_test_def_freq_g)
+        external_sta_connected = True
+        time.sleep(2)
+        fst_module_aux.disconnect_external_sta(dev[0], ap2)
+        external_sta_connected = False
+        time.sleep(2)
+        orig_ap_mbies = ap2.get_local_mbies()
+        fst_sta1.disconnect()
+        time.sleep(2)
+        res_ap_mbies = ap2.get_local_mbies()
+        if res_ap_mbies != orig_ap_mbies:
+            raise Exception("Failure. MB IEs have been unexpectedly updated on the AP")
+    except Exception, e:
+        logger.info(e)
+        raise
+    finally:
+        fst_sta1.disconnect()
+        if external_sta_connected:
+            fst_module_aux.disconnect_external_sta(dev[0], ap2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, fst_sta1, fst_sta2)
+
+def test_fst_dynamic_iface_attach(dev, apdev, test_params):
+    """FST dynamic interface attach"""
+    ap1 = fst_module_aux.FstAP(apdev[0]['ifname'], 'fst_11a', 'a',
+                               fst_test_common.fst_test_def_chan_a,
+                               fst_test_common.fst_test_def_group,
+                               fst_test_common.fst_test_def_prio_low,
+                               fst_test_common.fst_test_def_llt)
+    ap1.start()
+    ap2 = fst_module_aux.FstAP(apdev[1]['ifname'], 'fst_11g', 'b',
+                               fst_test_common.fst_test_def_chan_g,
+                               '', '', '')
+    ap2.start()
+
+    sta1 = fst_module_aux.FstSTA('wlan5',
+                                 fst_test_common.fst_test_def_group,
+                                 fst_test_common.fst_test_def_prio_low,
+                                 fst_test_common.fst_test_def_llt)
+    sta1.start()
+    sta2 = fst_module_aux.FstSTA('wlan6', '', '', '')
+    sta2.start()
+
+    try:
+        orig_sta2_mbies = sta2.get_local_mbies()
+        orig_ap2_mbies = ap2.get_local_mbies()
+        sta2.send_iface_attach_request(sta2.ifname(),
+                                       fst_test_common.fst_test_def_group,
+                                       '52', '27')
+        event = sta2.wait_for_iface_event(5)
+        if event['event_type'] != 'attached':
+            raise Exception("Failure. Iface was not properly attached")
+        ap2.send_iface_attach_request(ap2.ifname(),
+                                      fst_test_common.fst_test_def_group,
+                                      '102', '77')
+        event = ap2.wait_for_iface_event(5)
+        if event['event_type'] != 'attached':
+            raise Exception("Failure. Iface was not properly attached")
+        time.sleep(2)
+        res_sta2_mbies = sta2.get_local_mbies()
+        res_ap2_mbies = ap2.get_local_mbies()
+        sta2.send_iface_detach_request(sta2.ifname())
+        event = sta2.wait_for_iface_event(5)
+        if event['event_type'] != 'detached':
+            raise Exception("Failure. Iface was not properly detached")
+        ap2.send_iface_detach_request(ap2.ifname())
+        event = ap2.wait_for_iface_event(5)
+        if event['event_type'] != 'detached':
+            raise Exception("Failure. Iface was not properly detached")
+        if (not orig_sta2_mbies.startswith("FAIL") or
+            not orig_ap2_mbies.startswith("FAIL") or
+            res_sta2_mbies.startswith("FAIL") or
+            res_ap2_mbies.startswith("FAIL")):
+            raise Exception("Failure. MB IEs should have appeared on the station and on the AP")
+    except Exception, e:
+        logger.info(e)
+        raise
+    finally:
+        ap1.stop()
+        ap2.stop()
+        sta1.stop()
+        sta2.stop()
+
+# AP side FST module tests
+
+def test_fst_ap_start_session(dev, apdev, test_params):
+    """FST AP start session"""
+    fst_start_session(apdev, test_params, bad_param_none, True)
+
+def test_fst_ap_start_session_no_add_params(dev, apdev, test_params):
+    """FST AP start session - no add params"""
+    fst_start_session(apdev, test_params, bad_param_session_add_no_params, True)
+
+def test_fst_ap_start_session_bad_group_id(dev, apdev, test_params):
+    """FST AP start session - bad group id"""
+    fst_start_session(apdev, test_params, bad_param_group_id, True)
+
+def test_fst_ap_start_session_no_set_params(dev, apdev, test_params):
+    """FST AP start session - no set params"""
+    fst_start_session(apdev, test_params, bad_param_session_set_no_params, True)
+
+def test_fst_ap_start_session_set_unknown_param(dev, apdev, test_params):
+    """FST AP start session - set unknown param"""
+    fst_start_session(apdev, test_params, bad_param_session_set_unknown_param,
+                      True)
+
+def test_fst_ap_start_session_bad_session_id(dev, apdev, test_params):
+    """FST AP start session - bad session id"""
+    fst_start_session(apdev, test_params, bad_param_session_id, True)
+
+def test_fst_ap_start_session_bad_new_iface(dev, apdev, test_params):
+    """FST AP start session - bad new iface"""
+    fst_start_session(apdev, test_params, bad_param_new_iface, True)
+
+def test_fst_ap_start_session_bad_old_iface(dev, apdev, test_params):
+    """FST AP start session - bad old iface"""
+    fst_start_session(apdev, test_params, bad_param_old_iface, True)
+
+def test_fst_ap_start_session_negative_llt(dev, apdev, test_params):
+    """FST AP start session - negative llt"""
+    fst_start_session(apdev, test_params, bad_param_negative_llt, True)
+
+def test_fst_ap_start_session_zero_llt(dev, apdev, test_params):
+    """FST AP start session - zero llt"""
+    fst_start_session(apdev, test_params, bad_param_zero_llt, True)
+
+def test_fst_ap_start_session_llt_too_big(dev, apdev, test_params):
+    """FST AP start session - llt too large"""
+    fst_start_session(apdev, test_params, bad_param_llt_too_big, True)
+
+def test_fst_ap_start_session_invalid_peer_addr(dev, apdev, test_params):
+    """FST AP start session - invalid peer address"""
+    fst_start_session(apdev, test_params, bad_param_peer_addr, True,
+                      'GG:GG:GG:GG:GG:GG')
+
+def test_fst_ap_start_session_multicast_peer_addr(dev, apdev, test_params):
+    """FST AP start session - multicast peer address"""
+    fst_start_session(apdev, test_params, bad_param_peer_addr, True,
+                      '01:00:11:22:33:44')
+
+def test_fst_ap_start_session_broadcast_peer_addr(dev, apdev, test_params):
+    """FST AP start session - broadcast peer address"""
+    fst_start_session(apdev, test_params, bad_param_peer_addr, True,
+                      'FF:FF:FF:FF:FF:FF')
+
+def test_fst_ap_initiate_session(dev, apdev, test_params):
+    """FST AP initiate session"""
+    fst_initiate_session(apdev, test_params, bad_param_none, True)
+
+def test_fst_ap_initiate_session_no_params(dev, apdev, test_params):
+    """FST AP initiate session - no params"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_no_params, True)
+
+def test_fst_ap_initiate_session_invalid_session_id(dev, apdev, test_params):
+    """FST AP initiate session - invalid session id"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_bad_session_id, True)
+
+def test_fst_ap_initiate_session_no_new_iface(dev, apdev, test_params):
+    """FST AP initiate session - no new iface"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_with_no_new_iface_set, True)
+
+def test_fst_ap_initiate_session_bad_peer_addr(dev, apdev, test_params):
+    """FST AP initiate session - bad peer address"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_with_bad_peer_addr_set,
+                         True)
+
+def test_fst_ap_initiate_session_request_with_bad_stie(dev, apdev, test_params):
+    """FST AP initiate session - request with bad stie"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_request_with_bad_stie, True)
+
+def test_fst_ap_initiate_session_response_with_reject(dev, apdev, test_params):
+    """FST AP initiate session - response with reject"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_response_with_reject, True)
+
+def test_fst_ap_initiate_session_response_with_bad_stie(dev, apdev,
+                                                        test_params):
+    """FST AP initiate session - response with bad stie"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_response_with_bad_stie,
+                         True)
+
+def test_fst_ap_initiate_session_response_with_zero_llt(dev, apdev,
+                                                        test_params):
+    """FST AP initiate session - zero llt"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_response_with_zero_llt,
+                         True)
+
+def test_fst_ap_initiate_session_stt_no_response(dev, apdev, test_params):
+    """FST AP initiate session - stt no response"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_stt_no_response, True)
+
+def test_fst_ap_initiate_session_concurrent_setup_request(dev, apdev,
+                                                          test_params):
+    """FST AP initiate session - concurrent setup request"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_concurrent_setup_request,
+                         True)
+
+def test_fst_ap_session_request_with_no_session(dev, apdev, test_params):
+    """FST AP session request with no session"""
+    fst_send_unexpected_frame(apdev, test_params, frame_type_session_request,
+                              True)
+
+def test_fst_ap_session_response_accept_with_no_session(dev, apdev,
+                                                        test_params):
+    """FST AP session response accept with no session"""
+    fst_send_unexpected_frame(apdev, test_params, frame_type_session_response,
+                              True, "accept")
+
+def test_fst_ap_session_response_reject_with_no_session(dev, apdev,
+                                                        test_params):
+    """FST AP session response reject with no session"""
+    fst_send_unexpected_frame(apdev, test_params, frame_type_session_response,
+                              True, "reject")
+
+def test_fst_ap_ack_request_with_no_session(dev, apdev, test_params):
+    """FST AP ack request with no session"""
+    fst_send_unexpected_frame(apdev, test_params, frame_type_ack_request, True)
+
+def test_fst_ap_ack_response_with_no_session(dev, apdev, test_params):
+    """FST AP ack response with no session"""
+    fst_send_unexpected_frame(apdev, test_params, frame_type_ack_response, True)
+
+def test_fst_ap_tear_down_response_with_no_session(dev, apdev, test_params):
+    """FST AP tear down response with no session"""
+    fst_send_unexpected_frame(apdev, test_params, frame_type_tear_down, True)
+
+def test_fst_ap_transfer_session(dev, apdev, test_params):
+    """FST AP transfer session"""
+    fst_transfer_session(apdev, test_params, bad_param_none, True)
+
+def test_fst_ap_transfer_session_no_params(dev, apdev, test_params):
+    """FST AP transfer session - no params"""
+    fst_transfer_session(apdev, test_params,
+                         bad_param_session_transfer_no_params, True)
+
+def test_fst_ap_transfer_session_bad_session_id(dev, apdev, test_params):
+    """FST AP transfer session - bad session id"""
+    fst_transfer_session(apdev, test_params,
+                         bad_param_session_transfer_bad_session_id, True)
+
+def test_fst_ap_transfer_session_setup_skipped(dev, apdev, test_params):
+    """FST AP transfer session - setup skipped"""
+    fst_transfer_session(apdev, test_params,
+                         bad_param_session_transfer_setup_skipped, True)
+
+def test_fst_ap_ack_request_with_session_not_set_up(dev, apdev, test_params):
+    """FST AP ack request with session not set up"""
+    fst_bad_transfer(apdev, test_params,
+                     bad_scenario_ack_req_session_not_set_up, True)
+
+def test_fst_ap_ack_request_with_session_not_established_init_side(dev, apdev,
+                                                                   test_params):
+    """FST AP ack request with session not established init side"""
+    fst_bad_transfer(apdev, test_params,
+                     bad_scenario_ack_req_session_not_established_init_side,
+                     True)
+
+def test_fst_ap_ack_request_with_session_not_established_resp_side(dev, apdev,
+                                                                   test_params):
+    """FST AP ack request with session not established resp side"""
+    fst_bad_transfer(apdev, test_params,
+                     bad_scenario_ack_req_session_not_established_resp_side,
+                     True)
+
+def test_fst_ap_ack_request_with_bad_fsts_id(dev, apdev, test_params):
+    """FST AP ack request with bad fsts id"""
+    fst_bad_transfer(apdev, test_params, bad_scenario_ack_req_bad_fsts_id, True)
+
+def test_fst_ap_ack_response_with_session_not_set_up(dev, apdev, test_params):
+    """FST AP ack response with session not set up"""
+    fst_bad_transfer(apdev, test_params,
+                     bad_scenario_ack_resp_session_not_set_up, True)
+
+def test_fst_ap_ack_response_with_session_not_established_init_side(dev, apdev, test_params):
+    """FST AP ack response with session not established init side"""
+    fst_bad_transfer(apdev, test_params,
+                     bad_scenario_ack_resp_session_not_established_init_side,
+                     True)
+
+def test_fst_ap_ack_response_with_session_not_established_resp_side(dev, apdev, test_params):
+    """FST AP ack response with session not established resp side"""
+    fst_bad_transfer(apdev, test_params,
+                     bad_scenario_ack_resp_session_not_established_resp_side,
+                     True)
+
+def test_fst_ap_ack_response_with_no_ack_request(dev, apdev, test_params):
+    """FST AP ack response with no ack request"""
+    fst_bad_transfer(apdev, test_params, bad_scenario_ack_resp_no_ack_req, True)
+
+def test_fst_ap_tear_down_session(dev, apdev, test_params):
+    """FST AP tear down session"""
+    fst_tear_down_session(apdev, test_params, bad_param_none, True)
+
+def test_fst_ap_tear_down_session_no_params(dev, apdev, test_params):
+    """FST AP tear down session - no params"""
+    fst_tear_down_session(apdev, test_params,
+                          bad_param_session_teardown_no_params, True)
+
+def test_fst_ap_tear_down_session_bad_session_id(dev, apdev, test_params):
+    """FST AP tear down session - bad session id"""
+    fst_tear_down_session(apdev, test_params,
+                          bad_param_session_teardown_bad_session_id, True)
+
+def test_fst_ap_tear_down_session_setup_skipped(dev, apdev, test_params):
+    """FST AP tear down session - setup skipped"""
+    fst_tear_down_session(apdev, test_params,
+                          bad_param_session_teardown_setup_skipped, True)
+
+def test_fst_ap_tear_down_session_bad_fsts_id(dev, apdev, test_params):
+    """FST AP tear down session - bad fsts id"""
+    fst_tear_down_session(apdev, test_params,
+                          bad_param_session_teardown_bad_fsts_id, True)
+
+def test_fst_ap_remove_session_not_established(dev, apdev, test_params):
+    """FST AP remove session - not established"""
+    fst_remove_session(apdev, test_params,
+                       remove_scenario_non_established_session, True)
+
+def test_fst_ap_remove_session_established(dev, apdev, test_params):
+    """FST AP remove session - established"""
+    fst_remove_session(apdev, test_params,
+                       remove_scenario_established_session, True)
+
+def test_fst_ap_remove_session_no_params(dev, apdev, test_params):
+    """FST AP remove session - no params"""
+    fst_remove_session(apdev, test_params, remove_scenario_no_params, True)
+
+def test_fst_ap_remove_session_bad_session_id(dev, apdev, test_params):
+    """FST AP remove session - bad session id"""
+    fst_remove_session(apdev, test_params, remove_scenario_bad_session_id, True)
+
+def test_fst_ap_ctrl_iface(dev, apdev, test_params):
+    """FST control interface behavior"""
+    hglobal = hostapd.HostapdGlobal()
+    start_num_groups = 0
+    res = hglobal.request("FST-MANAGER LIST_GROUPS")
+    del hglobal
+    if "FAIL" not in res:
+        start_num_groups = len(res.splitlines())
+
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    try:
+        fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        initiator = ap1
+        responder = sta1
+        initiator.add_peer(responder, None)
+        initiator.set_fst_parameters(group_id=None)
+        sid = initiator.add_session()
+        res = initiator.get_session_params(sid)
+        logger.info("Initial session params:\n" + str(res))
+        if res['state'] != 'INITIAL':
+            raise Exception("Unexpected state: " + res['state'])
+        initiator.set_fst_parameters(llt=None)
+        initiator.configure_session(sid, ap2.ifname(), None)
+        res = initiator.get_session_params(sid)
+        logger.info("Session params after configuration:\n" + str(res))
+        res = initiator.iface_peers(initiator.ifname())
+        logger.info("Interface peers: " + str(res))
+        if len(res) != 1:
+            raise Exception("Unexpected number of peers")
+        res = initiator.get_peer_mbies(initiator.ifname(),
+                                       initiator.get_new_peer_addr())
+        logger.info("Peer MB IEs: " + str(res))
+        res = initiator.list_ifaces()
+        logger.info("Interfaces: " + str(res))
+        if len(res) != 2:
+            raise Exception("Unexpected number of interfaces")
+        res = initiator.list_groups()
+        logger.info("Groups: " + str(res))
+        if len(res) != 1 + start_num_groups:
+            raise Exception("Unexpected number of groups")
+
+        tests = [ "LIST_IFACES unknown",
+                  "LIST_IFACES     unknown2",
+                  "SESSION_GET 12345678",
+                  "SESSION_SET " + sid + " unknown=foo",
+                  "SESSION_RESPOND 12345678 foo",
+                  "SESSION_RESPOND " + sid,
+                  "SESSION_RESPOND " + sid + " foo",
+                  "TEST_REQUEST foo",
+                  "TEST_REQUEST SEND_SETUP_REQUEST",
+                  "TEST_REQUEST SEND_SETUP_REQUEST foo",
+                  "TEST_REQUEST SEND_SETUP_RESPONSE",
+                  "TEST_REQUEST SEND_SETUP_RESPONSE foo",
+                  "TEST_REQUEST SEND_ACK_REQUEST",
+                  "TEST_REQUEST SEND_ACK_REQUEST foo",
+                  "TEST_REQUEST SEND_ACK_RESPONSE",
+                  "TEST_REQUEST SEND_ACK_RESPONSE foo",
+                  "TEST_REQUEST SEND_TEAR_DOWN",
+                  "TEST_REQUEST SEND_TEAR_DOWN foo",
+                  "TEST_REQUEST GET_FSTS_ID",
+                  "TEST_REQUEST GET_FSTS_ID foo",
+                  "TEST_REQUEST GET_LOCAL_MBIES",
+                  "TEST_REQUEST GET_LOCAL_MBIES foo",
+                  "GET_PEER_MBIES",
+                  "GET_PEER_MBIES ",
+                  "GET_PEER_MBIES unknown",
+                  "GET_PEER_MBIES unknown unknown",
+                  "GET_PEER_MBIES unknown  " + initiator.get_new_peer_addr(),
+                  "GET_PEER_MBIES " + initiator.ifname() + " 01:ff:ff:ff:ff:ff",
+                  "GET_PEER_MBIES " + initiator.ifname() + " 00:ff:ff:ff:ff:ff",
+                  "GET_PEER_MBIES " + initiator.ifname() + " 00:00:00:00:00:00",
+                  "IFACE_PEERS",
+                  "IFACE_PEERS ",
+                  "IFACE_PEERS unknown",
+                  "IFACE_PEERS unknown unknown",
+                  "IFACE_PEERS " + initiator.fst_group,
+                  "IFACE_PEERS " + initiator.fst_group + " unknown" ]
+        for t in tests:
+            if "FAIL" not in initiator.grequest("FST-MANAGER " + t):
+                raise Exception("Unexpected response for invalid FST-MANAGER command " + t)
+        if "UNKNOWN FST COMMAND" not in initiator.grequest("FST-MANAGER unknown"):
+            raise Exception("Unexpected response for unknown FST-MANAGER command")
+
+        tests = [ "FST-DETACH", "FST-DETACH ", "FST-DETACH unknown",
+                  "FST-ATTACH", "FST-ATTACH ", "FST-ATTACH unknown",
+                  "FST-ATTACH unknown unknown" ]
+        for t in tests:
+            if "FAIL" not in initiator.grequest(t):
+                raise Exception("Unexpected response for invalid command " + t)
+
+        try:
+            # Trying to add same interface again needs to fail.
+            ap1.send_iface_attach_request(ap1.iface, ap1.fst_group,
+                                          ap1.fst_llt, ap1.fst_pri)
+            raise Exception("Duplicate FST-ATTACH succeeded")
+        except Exception, e:
+            if not str(e).startswith("Cannot attach"):
+                raise
+
+        try:
+            ap1.get_fsts_id_by_sid("123")
+        except Exception, e:
+            if not str(e).startswith("Cannot get fsts_id for sid"):
+                raise
+    finally:
+        fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_ap_start_session_oom(dev, apdev, test_params):
+    """FST AP setup failing due to OOM"""
+    ap1 = fst_module_aux.FstAP(apdev[0]['ifname'], 'fst_11a', 'a',
+                               fst_test_common.fst_test_def_chan_a,
+                               fst_test_common.fst_test_def_group,
+                               fst_test_common.fst_test_def_prio_low,
+                               fst_test_common.fst_test_def_llt)
+    ap1.start()
+    with alloc_fail(ap1, 1, "fst_iface_create"):
+        ap2_started = False
+        try:
+            ap2 = fst_module_aux.FstAP(apdev[1]['ifname'], 'fst_11g', 'b',
+                                       fst_test_common.fst_test_def_chan_g,
+                                       fst_test_common.fst_test_def_group,
+                                       fst_test_common.fst_test_def_prio_high,
+                                       fst_test_common.fst_test_def_llt)
+            try:
+                # This will fail in fst_iface_create() OOM
+                ap2.start()
+            except:
+                pass
+        finally:
+            ap1.stop()
+            try:
+                ap2.stop()
+            except:
+                pass
+
+# STA side FST module tests
+
+def test_fst_sta_start_session(dev, apdev, test_params):
+    """FST STA start session"""
+    fst_start_session(apdev, test_params, bad_param_none, False)
+
+def test_fst_sta_start_session_no_add_params(dev, apdev, test_params):
+    """FST STA start session - no add params"""
+    fst_start_session(apdev, test_params, bad_param_session_add_no_params,
+                      False)
+
+def test_fst_sta_start_session_bad_group_id(dev, apdev, test_params):
+    """FST STA start session - bad group id"""
+    fst_start_session(apdev, test_params, bad_param_group_id, False)
+
+def test_fst_sta_start_session_no_set_params(dev, apdev, test_params):
+    """FST STA start session - no set params"""
+    fst_start_session(apdev, test_params, bad_param_session_set_no_params,
+                      False)
+
+def test_fst_sta_start_session_set_unknown_param(dev, apdev, test_params):
+    """FST STA start session - set unknown param"""
+    fst_start_session(apdev, test_params, bad_param_session_set_unknown_param,
+                      False)
+
+def test_fst_sta_start_session_bad_session_id(dev, apdev, test_params):
+    """FST STA start session - bad session id"""
+    fst_start_session(apdev, test_params, bad_param_session_id, False)
+
+def test_fst_sta_start_session_bad_new_iface(dev, apdev, test_params):
+    """FST STA start session - bad new iface"""
+    fst_start_session(apdev, test_params, bad_param_new_iface, False)
+
+def test_fst_sta_start_session_bad_old_iface(dev, apdev, test_params):
+    """FST STA start session - bad old iface"""
+    fst_start_session(apdev, test_params, bad_param_old_iface, False)
+
+def test_fst_sta_start_session_negative_llt(dev, apdev, test_params):
+    """FST STA start session - negative llt"""
+    fst_start_session(apdev, test_params, bad_param_negative_llt, False)
+
+def test_fst_sta_start_session_zero_llt(dev, apdev, test_params):
+    """FST STA start session - zero llt"""
+    fst_start_session(apdev, test_params, bad_param_zero_llt, False)
+
+def test_fst_sta_start_session_llt_too_big(dev, apdev, test_params):
+    """FST STA start session - llt too large"""
+    fst_start_session(apdev, test_params, bad_param_llt_too_big, False)
+
+def test_fst_sta_start_session_invalid_peer_addr(dev, apdev, test_params):
+    """FST STA start session - invalid peer address"""
+    fst_start_session(apdev, test_params, bad_param_peer_addr, False,
+                      'GG:GG:GG:GG:GG:GG')
+
+def test_fst_sta_start_session_multicast_peer_addr(dev, apdev, test_params):
+    """FST STA start session - multicast peer address"""
+    fst_start_session(apdev, test_params, bad_param_peer_addr, False,
+                      '11:00:11:22:33:44')
+
+def test_fst_sta_start_session_broadcast_peer_addr(dev, apdev, test_params):
+    """FST STA start session - broadcast peer addr"""
+    fst_start_session(apdev, test_params, bad_param_peer_addr, False,
+                      'FF:FF:FF:FF:FF:FF')
+
+def test_fst_sta_initiate_session(dev, apdev, test_params):
+    """FST STA initiate session"""
+    fst_initiate_session(apdev, test_params, bad_param_none, False)
+
+def test_fst_sta_initiate_session_no_params(dev, apdev, test_params):
+    """FST STA initiate session - no params"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_no_params, False)
+
+def test_fst_sta_initiate_session_invalid_session_id(dev, apdev, test_params):
+    """FST STA initiate session - invalid session id"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_bad_session_id, False)
+
+def test_fst_sta_initiate_session_no_new_iface(dev, apdev, test_params):
+    """FST STA initiate session - no new iface"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_with_no_new_iface_set,
+                         False)
+
+def test_fst_sta_initiate_session_bad_peer_addr(dev, apdev, test_params):
+    """FST STA initiate session - bad peer address"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_with_bad_peer_addr_set,
+                         False)
+
+def test_fst_sta_initiate_session_request_with_bad_stie(dev, apdev,
+                                                        test_params):
+    """FST STA initiate session - request with bad stie"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_request_with_bad_stie,
+                         False)
+
+def test_fst_sta_initiate_session_response_with_reject(dev, apdev, test_params):
+    """FST STA initiate session - response with reject"""
+    fst_initiate_session(apdev, test_params, bad_param_session_initiate_response_with_reject, False)
+
+def test_fst_sta_initiate_session_response_with_bad_stie(dev, apdev, test_params):
+    """FST STA initiate session - response with bad stie"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_response_with_bad_stie,
+                         False)
+
+def test_fst_sta_initiate_session_response_with_zero_llt(dev, apdev,
+                                                         test_params):
+    """FST STA initiate session - response with zero llt"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_response_with_zero_llt,
+                         False)
+
+def test_fst_sta_initiate_session_stt_no_response(dev, apdev, test_params):
+    """FST STA initiate session - stt no response"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_stt_no_response, False)
+
+def test_fst_sta_initiate_session_concurrent_setup_request(dev, apdev,
+                                                           test_params):
+    """FST STA initiate session - concurrent setup request"""
+    fst_initiate_session(apdev, test_params,
+                         bad_param_session_initiate_concurrent_setup_request,
+                         False)
+
+def test_fst_sta_session_request_with_no_session(dev, apdev, test_params):
+    """FST STA session request with no session"""
+    fst_send_unexpected_frame(apdev, test_params, frame_type_session_request,
+                              False)
+
+def test_fst_sta_session_response_accept_with_no_session(dev, apdev,
+                                                         test_params):
+    """FST STA session response accept with no session"""
+    fst_send_unexpected_frame(apdev, test_params, frame_type_session_response,
+                              False, "accept")
+
+def test_fst_sta_session_response_reject_with_no_session(dev, apdev,
+                                                         test_params):
+    """FST STA session response reject with no session"""
+    fst_send_unexpected_frame(apdev, test_params, frame_type_session_response,
+                              False, "reject")
+
+def test_fst_sta_ack_request_with_no_session(dev, apdev, test_params):
+    """FST STA ack request with no session"""
+    fst_send_unexpected_frame(apdev, test_params, frame_type_ack_request, False)
+
+def test_fst_sta_ack_response_with_no_session(dev, apdev, test_params):
+    """FST STA ack response with no session"""
+    fst_send_unexpected_frame(apdev, test_params, frame_type_ack_response,
+                              False)
+
+def test_fst_sta_tear_down_response_with_no_session(dev, apdev, test_params):
+    """FST STA tear down response with no session"""
+    fst_send_unexpected_frame(apdev, test_params, frame_type_tear_down, False)
+
+def test_fst_sta_transfer_session(dev, apdev, test_params):
+    """FST STA transfer session"""
+    fst_transfer_session(apdev, test_params, bad_param_none, False)
+
+def test_fst_sta_transfer_session_no_params(dev, apdev, test_params):
+    """FST STA transfer session - no params"""
+    fst_transfer_session(apdev, test_params,
+                         bad_param_session_transfer_no_params, False)
+
+def test_fst_sta_transfer_session_bad_session_id(dev, apdev, test_params):
+    """FST STA transfer session - bad session id"""
+    fst_transfer_session(apdev, test_params,
+                         bad_param_session_transfer_bad_session_id, False)
+
+def test_fst_sta_transfer_session_setup_skipped(dev, apdev, test_params):
+    """FST STA transfer session - setup skipped"""
+    fst_transfer_session(apdev, test_params,
+                         bad_param_session_transfer_setup_skipped, False)
+
+def test_fst_sta_ack_request_with_session_not_set_up(dev, apdev, test_params):
+    """FST STA ack request with session not set up"""
+    fst_bad_transfer(apdev, test_params,
+                     bad_scenario_ack_req_session_not_set_up, False)
+
+def test_fst_sta_ack_request_with_session_not_established_init_side(dev, apdev, test_params):
+    """FST STA ack request with session not established init side"""
+    fst_bad_transfer(apdev, test_params,
+                     bad_scenario_ack_req_session_not_established_init_side,
+                     False)
+
+def test_fst_sta_ack_request_with_session_not_established_resp_side(dev, apdev, test_params):
+    """FST STA ack request with session not established resp side"""
+    fst_bad_transfer(apdev, test_params,
+                     bad_scenario_ack_req_session_not_established_resp_side,
+                     False)
+
+def test_fst_sta_ack_request_with_bad_fsts_id(dev, apdev, test_params):
+    """FST STA ack request with bad fsts id"""
+    fst_bad_transfer(apdev, test_params, bad_scenario_ack_req_bad_fsts_id,
+                     False)
+
+def test_fst_sta_ack_response_with_session_not_set_up(dev, apdev, test_params):
+    """FST STA ack response with session not set up"""
+    fst_bad_transfer(apdev, test_params,
+                     bad_scenario_ack_resp_session_not_set_up, False)
+
+def test_fst_sta_ack_response_with_session_not_established_init_side(dev, apdev, test_params):
+    """FST STA ack response with session not established init side"""
+    fst_bad_transfer(apdev, test_params,
+                     bad_scenario_ack_resp_session_not_established_init_side,
+                     False)
+
+def test_fst_sta_ack_response_with_session_not_established_resp_side(dev, apdev, test_params):
+    """FST STA ack response with session not established resp side"""
+    fst_bad_transfer(apdev, test_params,
+                     bad_scenario_ack_resp_session_not_established_resp_side,
+                     False)
+
+def test_fst_sta_ack_response_with_no_ack_request(dev, apdev, test_params):
+    """FST STA ack response with no ack request"""
+    fst_bad_transfer(apdev, test_params, bad_scenario_ack_resp_no_ack_req,
+                     False)
+
+def test_fst_sta_tear_down_session(dev, apdev, test_params):
+    """FST STA tear down session"""
+    fst_tear_down_session(apdev, test_params, bad_param_none, False)
+
+def test_fst_sta_tear_down_session_no_params(dev, apdev, test_params):
+    """FST STA tear down session - no params"""
+    fst_tear_down_session(apdev, test_params,
+                          bad_param_session_teardown_no_params, False)
+
+def test_fst_sta_tear_down_session_bad_session_id(dev, apdev, test_params):
+    """FST STA tear down session - bad session id"""
+    fst_tear_down_session(apdev, test_params,
+                          bad_param_session_teardown_bad_session_id, False)
+
+def test_fst_sta_tear_down_session_setup_skipped(dev, apdev, test_params):
+    """FST STA tear down session - setup skipped"""
+    fst_tear_down_session(apdev, test_params,
+                          bad_param_session_teardown_setup_skipped, False)
+
+def test_fst_sta_tear_down_session_bad_fsts_id(dev, apdev, test_params):
+    """FST STA tear down session - bad fsts id"""
+    fst_tear_down_session(apdev, test_params,
+                          bad_param_session_teardown_bad_fsts_id, False)
+
+def test_fst_sta_remove_session_not_established(dev, apdev, test_params):
+    """FST STA tear down session - not established"""
+    fst_remove_session(apdev, test_params,
+                       remove_scenario_non_established_session, False)
+
+def test_fst_sta_remove_session_established(dev, apdev, test_params):
+    """FST STA remove session - established"""
+    fst_remove_session(apdev, test_params,
+                       remove_scenario_established_session, False)
+
+def test_fst_sta_remove_session_no_params(dev, apdev, test_params):
+    """FST STA remove session - no params"""
+    fst_remove_session(apdev, test_params, remove_scenario_no_params, False)
+
+def test_fst_sta_remove_session_bad_session_id(dev, apdev, test_params):
+    """FST STA remove session - bad session id"""
+    fst_remove_session(apdev, test_params, remove_scenario_bad_session_id,
+                       False)
+
+def test_fst_rsn_ap_transfer_session(dev, apdev, test_params):
+    """FST RSN AP transfer session"""
+    fst_transfer_session(apdev, test_params, bad_param_none, True, rsn=True)
+
+MGMT_SUBTYPE_ACTION = 13
+ACTION_CATEG_FST = 18
+FST_ACTION_SETUP_REQUEST = 0
+FST_ACTION_SETUP_RESPONSE = 1
+FST_ACTION_TEAR_DOWN = 2
+FST_ACTION_ACK_REQUEST = 3
+FST_ACTION_ACK_RESPONSE = 4
+FST_ACTION_ON_CHANNEL_TUNNEL = 5
+
+def hostapd_tx_and_status(hapd, msg):
+    hapd.set("ext_mgmt_frame_handling", "1")
+    hapd.mgmt_tx(msg)
+    ev = hapd.wait_event([ "MGMT-TX-STATUS" ], timeout=1)
+    if ev is None or "ok=1" not in ev:
+        raise Exception("No ACK")
+    hapd.set("ext_mgmt_frame_handling", "0")
+
+def test_fst_proto(dev, apdev, test_params):
+    """FST protocol testing"""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    try:
+        fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        hapd = ap1.get_instance()
+        sta = sta1.get_instance()
+        dst = sta.own_addr()
+        src = apdev[0]['bssid']
+
+        msg = {}
+        msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+        msg['da'] = dst
+        msg['sa'] = src
+        msg['bssid'] = src
+
+        # unknown FST Action (255) received!
+        msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST, 255)
+        hostapd_tx_and_status(hapd, msg)
+
+        # FST Request dropped: too short
+        msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_REQUEST)
+        hostapd_tx_and_status(hapd, msg)
+
+        # FST Request dropped: invalid STIE (EID)
+        msg['payload'] = struct.pack("<BBBLBBLBBBBBBB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_REQUEST, 0, 0,
+                                     163, 11, 0, 0, 0, 0, 0, 0, 0, 0)
+        hostapd_tx_and_status(hapd, msg)
+
+        # FST Request dropped: invalid STIE (Len)
+        msg['payload'] = struct.pack("<BBBLBBLBBBBBBB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_REQUEST, 0, 0,
+                                     164, 10, 0, 0, 0, 0, 0, 0, 0, 0)
+        hostapd_tx_and_status(hapd, msg)
+
+        # FST Request dropped: new and old band IDs are the same
+        msg['payload'] = struct.pack("<BBBLBBLBBBBBBB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_REQUEST, 0, 0,
+                                     164, 11, 0, 0, 0, 0, 0, 0, 0, 0)
+        hostapd_tx_and_status(hapd, msg)
+
+        ifaces = sta1.list_ifaces()
+        id = int(ifaces[0]['name'].split('|')[1])
+        # FST Request dropped: new iface not found (new_band_id mismatch)
+        msg['payload'] = struct.pack("<BBBLBBLBBBBBBB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_REQUEST, 0, 0,
+                                     164, 11, 0, 0, id + 1, 0, 0, 0, 0, 0)
+        hostapd_tx_and_status(hapd, msg)
+
+        # FST Action 'Setup Response' dropped: no session in progress found
+        msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_RESPONSE)
+        hostapd_tx_and_status(hapd, msg)
+
+        # Create session
+        initiator = ap1
+        responder = sta1
+        new_iface = ap2.ifname()
+        new_peer_addr = ap2.get_actual_peer_addr()
+        resp_newif = sta2.ifname()
+        peeraddr = None
+        initiator.add_peer(responder, peeraddr, new_peer_addr)
+        sid = initiator.add_session()
+        initiator.configure_session(sid, new_iface)
+        initiator.initiate_session(sid, "accept")
+
+        # FST Response dropped due to wrong state: SETUP_COMPLETION
+        msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_RESPONSE)
+        hostapd_tx_and_status(hapd, msg)
+
+        # Too short FST Tear Down dropped
+        msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+                                     FST_ACTION_TEAR_DOWN)
+        hostapd_tx_and_status(hapd, msg)
+
+        # tear down for wrong FST Setup ID (0)
+        msg['payload'] = struct.pack("<BBL", ACTION_CATEG_FST,
+                                     FST_ACTION_TEAR_DOWN, 0)
+        hostapd_tx_and_status(hapd, msg)
+
+        # Ack received on wrong interface
+        msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+                                     FST_ACTION_ACK_REQUEST)
+        hostapd_tx_and_status(hapd, msg)
+
+        # Ack Response in inappropriate session state (SETUP_COMPLETION)
+        msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+                                     FST_ACTION_ACK_RESPONSE)
+        hostapd_tx_and_status(hapd, msg)
+
+        # Unsupported FST Action frame (On channel tunnel)
+        msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+                                     FST_ACTION_ON_CHANNEL_TUNNEL)
+        hostapd_tx_and_status(hapd, msg)
+
+        # FST Request dropped: new iface not found (new_band_id match)
+        # FST Request dropped due to MAC comparison
+        msg['payload'] = struct.pack("<BBBLBBLBBBBBBB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_REQUEST, 0, 0,
+                                     164, 11, 0, 0, id, 0, 0, 0, 0, 0)
+        hostapd_tx_and_status(hapd, msg)
+
+        hapd2 = ap2.get_instance()
+        dst2 = sta2.get_instance().own_addr()
+        src2 = apdev[1]['bssid']
+
+        msg2 = {}
+        msg2['fc'] = MGMT_SUBTYPE_ACTION << 4
+        msg2['da'] = dst2
+        msg2['sa'] = src2
+        msg2['bssid'] = src2
+        # FST Response dropped: wlan6 is not the old iface
+        msg2['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+                                      FST_ACTION_SETUP_RESPONSE)
+        hostapd_tx_and_status(hapd2, msg2)
+
+        sta.dump_monitor()
+
+        group = ap1.fst_group
+        ap1.send_iface_detach_request(ap1.iface)
+
+        sta.flush_scan_cache()
+        sta.request("REASSOCIATE")
+        sta.wait_connected()
+
+        # FST Request dropped due to no interface connection
+        msg['payload'] = struct.pack("<BBBLBBLBBBBBBB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_REQUEST, 0, 0,
+                                     164, 11, 0, 0, id, 0, 0, 0, 0, 0)
+        hostapd_tx_and_status(hapd, msg)
+    finally:
+        fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        try:
+            fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        except:
+            pass
+
+def test_fst_setup_response_proto(dev, apdev, test_params):
+    """FST protocol testing for Setup Response"""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    try:
+        fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        hapd = ap1.get_instance()
+        sta = sta1.get_instance()
+        dst = sta.own_addr()
+        src = apdev[0]['bssid']
+
+        sta1.add_peer(ap1, None, sta2.get_actual_peer_addr())
+        sta1.set_fst_parameters(llt='0')
+        sid = sta1.add_session()
+        sta1.configure_session(sid, sta2.ifname())
+        sta1.initiate_session(sid, "")
+
+        msg = {}
+        msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+        msg['da'] = dst
+        msg['sa'] = src
+        msg['bssid'] = src
+
+        # Too short FST Response dropped
+        msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_RESPONSE)
+        hostapd_tx_and_status(hapd, msg)
+
+        # FST Response dropped: invalid STIE (EID)
+        dialog_token = 1
+        status_code = 0
+        id = 0
+        msg['payload'] = struct.pack("<BBBBBBLBBBBBBB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_RESPONSE, dialog_token,
+                                     status_code,
+                                     163, 11, 0, 0, id, 0, 0, 0, 0, 0)
+        hostapd_tx_and_status(hapd, msg)
+
+        # FST Response dropped: invalid STIE (Len)
+        dialog_token = 1
+        status_code = 0
+        id = 0
+        msg['payload'] = struct.pack("<BBBBBBLBBBBBBB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_RESPONSE, dialog_token,
+                                     status_code,
+                                     164, 10, 0, 0, id, 0, 0, 0, 0, 0)
+        hostapd_tx_and_status(hapd, msg)
+
+        # FST Response dropped due to wrong dialog token
+        dialog_token = 123
+        status_code = 0
+        id = 0
+        msg['payload'] = struct.pack("<BBBBBBLBBBBBBB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_RESPONSE, dialog_token,
+                                     status_code,
+                                     164, 11, 0, 0, id, 0, 0, 0, 0, 0)
+        hostapd_tx_and_status(hapd, msg)
+
+        # FST Response dropped due to wrong FST Session ID
+        dialog_token = 1
+        status_code = 0
+        id = 1
+        msg['payload'] = struct.pack("<BBBBBBLBBBBBBB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_RESPONSE, dialog_token,
+                                     status_code,
+                                     164, 11, int(sid) + 123456,
+                                     0, id, 0, 0, 0, 0, 0)
+        hostapd_tx_and_status(hapd, msg)
+
+        # FST Response with non-zero status code
+        dialog_token = 1
+        status_code = 1
+        id = 1
+        msg['payload'] = struct.pack("<BBBBBBLBBBBBBB", ACTION_CATEG_FST,
+                                     FST_ACTION_SETUP_RESPONSE, dialog_token,
+                                     status_code,
+                                     164, 11, int(sid), 0, id, 0, 0, 0, 0, 0)
+        hostapd_tx_and_status(hapd, msg)
+    finally:
+        fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_ack_response_proto(dev, apdev, test_params):
+    """FST protocol testing for Ack Response"""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    try:
+        fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        hapd = ap2.get_instance()
+        sta = sta2.get_instance()
+        dst = sta.own_addr()
+        src = apdev[1]['bssid']
+
+        sta1.add_peer(ap1, None, sta2.get_actual_peer_addr())
+        sta1.set_fst_parameters(llt='0')
+        sid = sta1.add_session()
+        sta1.configure_session(sid, sta2.ifname())
+
+        s = sta1.grequest("FST-MANAGER SESSION_INITIATE "+ sid)
+        if not s.startswith('OK'):
+            raise Exception("Cannot initiate fst session: %s" % s)
+        ev = sta1.peer_obj.wait_gevent([ "FST-EVENT-SESSION" ], timeout=5)
+        if ev is None:
+            raise Exception("No FST-EVENT-SESSION received")
+        event = fst_module_aux.parse_fst_session_event(ev)
+        if event == None:
+            raise Exception("Unrecognized FST event: " % ev)
+        if event['type'] != 'EVENT_FST_SETUP':
+            raise Exception("Expected FST_SETUP event, got: " + event['type'])
+        ev = sta1.peer_obj.wait_gevent(["FST-EVENT-SESSION"], timeout=5)
+        if ev is None:
+            raise Exception("No FST-EVENT-SESSION received")
+        event = fst_module_aux.parse_fst_session_event(ev)
+        if event == None:
+            raise Exception("Unrecognized FST event: " % ev)
+        if event['type'] != 'EVENT_FST_SESSION_STATE':
+            raise Exception("Expected EVENT_FST_SESSION_STATE event, got: " + event['type'])
+        if event['new_state'] != "SETUP_COMPLETION":
+            raise Exception("Expected new state SETUP_COMPLETION, got: " + event['new_state'])
+
+        hapd.set("ext_mgmt_frame_handling", "1")
+        s = sta1.peer_obj.grequest("FST-MANAGER SESSION_RESPOND "+ event['id'] + " accept")
+        if not s.startswith('OK'):
+            raise Exception("Error session_respond: %s" % s)
+        req = hapd.mgmt_rx()
+        if req is None:
+            raise Exception("No Ack Request seen")
+        msg = {}
+        msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+        msg['da'] = dst
+        msg['sa'] = src
+        msg['bssid'] = src
+
+        # Too short FST Ack Response dropped
+        msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+                                     FST_ACTION_ACK_RESPONSE)
+        hapd.mgmt_tx(msg)
+        ev = hapd.wait_event([ "MGMT-TX-STATUS" ], timeout=1)
+        if ev is None or "ok=1" not in ev:
+            raise Exception("No ACK")
+
+        # Ack Response for wrong FSt Setup ID
+        msg['payload'] = struct.pack("<BBBL", ACTION_CATEG_FST,
+                                     FST_ACTION_ACK_RESPONSE,
+                                     0, int(sid) + 123456)
+        hostapd_tx_and_status(hapd, msg)
+    finally:
+        fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_ap_config_oom(dev, apdev, test_params):
+    """FST AP configuration and OOM"""
+    ap1 = fst_module_aux.FstAP(apdev[0]['ifname'], 'fst_11a', 'a',
+                               fst_test_common.fst_test_def_chan_a,
+                               fst_test_common.fst_test_def_group,
+                               fst_test_common.fst_test_def_prio_low)
+    hapd = ap1.start(return_early=True)
+    with alloc_fail(hapd, 1, "fst_group_create"):
+        res = ap1.grequest("FST-ATTACH %s %s" % (ap1.iface, ap1.fst_group))
+        if not res.startswith("FAIL"):
+            raise Exception("FST-ATTACH succeeded unexpectedly")
+
+    with alloc_fail(hapd, 1, "fst_iface_create"):
+        res = ap1.grequest("FST-ATTACH %s %s" % (ap1.iface, ap1.fst_group))
+        if not res.startswith("FAIL"):
+            raise Exception("FST-ATTACH succeeded unexpectedly")
+
+    with alloc_fail(hapd, 1, "fst_group_create_mb_ie"):
+        res = ap1.grequest("FST-ATTACH %s %s" % (ap1.iface, ap1.fst_group))
+        # This is allowed to complete currently
+
+    ap1.stop()
+
+def test_fst_send_oom(dev, apdev, test_params):
+    """FST send action OOM"""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    try:
+        fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        hapd = ap1.get_instance()
+        sta = sta1.get_instance()
+        dst = sta.own_addr()
+        src = apdev[0]['bssid']
+
+        # Create session
+        initiator = ap1
+        responder = sta1
+        new_iface = ap2.ifname()
+        new_peer_addr = ap2.get_actual_peer_addr()
+        resp_newif = sta2.ifname()
+        peeraddr = None
+        initiator.add_peer(responder, peeraddr, new_peer_addr)
+        sid = initiator.add_session()
+        initiator.configure_session(sid, new_iface)
+        with alloc_fail(hapd, 1, "fst_session_send_action"):
+            res = initiator.grequest("FST-MANAGER SESSION_INITIATE " + sid)
+            if not res.startswith("FAIL"):
+                raise Exception("Unexpected SESSION_INITIATE result")
+
+        res = initiator.grequest("FST-MANAGER SESSION_INITIATE " + sid)
+        if not res.startswith("OK"):
+            raise Exception("SESSION_INITIATE failed")
+
+        tests = [ "", "foo", sid, sid + " foo", sid + " foo=bar" ]
+        for t in tests:
+            res = initiator.grequest("FST-MANAGER SESSION_SET " + t)
+            if not res.startswith("FAIL"):
+                raise Exception("Invalid SESSION_SET accepted")
+
+        with alloc_fail(hapd, 1, "fst_session_send_action"):
+            res = initiator.grequest("FST-MANAGER SESSION_TEARDOWN " + sid)
+            if not res.startswith("FAIL"):
+                raise Exception("Unexpected SESSION_TEARDOWN result")
+    finally:
+        fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_session_oom(dev, apdev, test_params):
+    """FST session create OOM"""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    try:
+        fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        hapd = ap1.get_instance()
+        sta = sta1.get_instance()
+        dst = sta.own_addr()
+        src = apdev[0]['bssid']
+
+        # Create session
+        initiator = ap1
+        responder = sta1
+        new_iface = ap2.ifname()
+        new_peer_addr = ap2.get_actual_peer_addr()
+        resp_newif = sta2.ifname()
+        peeraddr = None
+        initiator.add_peer(responder, peeraddr, new_peer_addr)
+        with alloc_fail(hapd, 1, "fst_session_create"):
+            sid = initiator.grequest("FST-MANAGER SESSION_ADD " + initiator.fst_group)
+            if not sid.startswith("FAIL"):
+                raise Exception("Unexpected SESSION_ADD success")
+        sid = initiator.add_session()
+        initiator.configure_session(sid, new_iface)
+        with alloc_fail(sta, 1, "fst_session_create"):
+            res = initiator.grequest("FST-MANAGER SESSION_INITIATE " + sid)
+            if not res.startswith("OK"):
+                raise Exception("Unexpected SESSION_INITIATE result")
+    finally:
+        fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_attach_zero_llt(dev, apdev):
+    """FST attach with llt=0"""
+    sta1 = fst_module_aux.FstSTA('wlan5', fst_test_common.fst_test_def_group,
+                                 "100", "0")
+    sta1.start()
+    sta1.stop()
+
+def test_fst_session_respond_fail(dev, apdev, test_params):
+    """FST-MANAGER SESSION_RESPOND failure"""
+    ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+    try:
+        fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        sta1.add_peer(ap1, None, sta2.get_actual_peer_addr())
+        sid = sta1.add_session()
+        sta1.configure_session(sid, sta2.ifname())
+        sta1.send_session_setup_request(sid)
+        sta1.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+        ev = ap1.wait_for_session_event(5, [], ['EVENT_FST_SETUP'])
+        if not 'id' in ev:
+            raise Exception("No session id in FST setup event")
+        # Disconnect STA to make SESSION_RESPOND fail due to no peer found
+        sta = sta1.get_instance()
+        sta.request("DISCONNECT")
+        sta.wait_disconnected()
+        req = "FST-MANAGER SESSION_RESPOND %s reject" % ev['id']
+        s = ap1.grequest(req)
+        if not s.startswith("FAIL"):
+            raise Exception("SESSION_RESPOND succeeded unexpectedly")
+    finally:
+        fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+        fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def fst_session_set(dev, sid, param, value):
+    cmd = "FST-MANAGER SESSION_SET %s %s=%s" % (sid, param, value)
+    if "OK" not in dev.global_request(cmd):
+        raise Exception(cmd + " failed")
+
+def fst_session_set_ap(dev, sid, param, value):
+    cmd = "FST-MANAGER SESSION_SET %s %s=%s" % (sid, param, value)
+    if "OK" not in dev.request(cmd):
+        raise Exception(cmd + " failed")
+
+def fst_attach_ap(dev, ifname, group):
+    cmd = "FST-ATTACH %s %s" % (ifname, group)
+    if "OK" not in dev.request(cmd):
+        raise Exception("FST-ATTACH (AP) failed")
+    ev = dev.wait_event(['FST-EVENT-IFACE'], timeout=5)
+    if ev is None:
+        raise Exception("No FST-EVENT-IFACE attached (AP)")
+    for t in [ "attached", "ifname=" + ifname, "group=" + group ]:
+        if t not in ev:
+            raise Exception("Unexpected FST-EVENT-IFACE data (AP): " + ev)
+
+def fst_attach_sta(dev, ifname, group):
+    if "OK" not in dev.global_request("FST-ATTACH %s %s" % (ifname, group)):
+        raise Exception("FST-ATTACH (STA) failed")
+    ev = dev.wait_global_event(['FST-EVENT-IFACE'], timeout=5)
+    if ev is None:
+        raise Exception("No FST-EVENT-IFACE attached (STA)")
+    for t in [ "attached", "ifname=" + ifname, "group=" + group ]:
+        if t not in ev:
+            raise Exception("Unexpected FST-EVENT-IFACE data (STA): " + ev)
+
+def fst_detach_ap(dev, ifname, group):
+    if "OK" not in dev.request("FST-DETACH " + ifname):
+        raise Exception("FST-DETACH (AP) failed for " + ifname)
+    ev = dev.wait_event(['FST-EVENT-IFACE'], timeout=5)
+    if ev is None:
+        raise Exception("No FST-EVENT-IFACE detached (AP) for " + ifname)
+    for t in [ "detached", "ifname=" + ifname, "group=" + group ]:
+        if t not in ev:
+            raise Exception("Unexpected FST-EVENT-IFACE data (AP): " + ev)
+
+def fst_detach_sta(dev, ifname, group):
+    dev.dump_monitor()
+    if "OK" not in dev.global_request("FST-DETACH " + ifname):
+        raise Exception("FST-DETACH (STA) failed for " + ifname)
+    ev = dev.wait_global_event(['FST-EVENT-IFACE'], timeout=5)
+    if ev is None:
+        raise Exception("No FST-EVENT-IFACE detached (STA) for " + ifname)
+    for t in [ "detached", "ifname=" + ifname, "group=" + group ]:
+        if t not in ev:
+            raise Exception("Unexpected FST-EVENT-IFACE data (STA): " + ev)
+
+def fst_wait_event_peer_ap(dev, event, ifname, addr):
+    ev = dev.wait_event(['FST-EVENT-PEER'], timeout=5)
+    if ev is None:
+        raise Exception("No FST-EVENT-PEER connected (AP)")
+    for t in [ " " + event + " ", "ifname=" + ifname, "peer_addr=" + addr ]:
+        if t not in ev:
+            raise Exception("Unexpected FST-EVENT-PEER data (AP): " + ev)
+
+def fst_wait_event_peer_sta(dev, event, ifname, addr):
+    ev = dev.wait_global_event(['FST-EVENT-PEER'], timeout=5)
+    if ev is None:
+        raise Exception("No FST-EVENT-PEER connected (STA)")
+    for t in [ " " + event + " ", "ifname=" + ifname, "peer_addr=" + addr ]:
+        if t not in ev:
+            raise Exception("Unexpected FST-EVENT-PEER data (STA): " + ev)
+
+def fst_setup_req(dev, hglobal, freq, dst, req, stie, mbie="", no_wait=False):
+    act = req + stie + mbie
+    dev.request("MGMT_TX %s %s freq=%d action=%s" % (dst, dst, freq, act))
+    ev = dev.wait_event(['MGMT-TX-STATUS'], timeout=5)
+    if ev is None or "result=SUCCESS" not in ev:
+        raise Exception("FST Action frame not ACKed")
+
+    if no_wait:
+        return
+    while True:
+        ev = hglobal.wait_event(['FST-EVENT-SESSION'], timeout=5)
+        if ev is None:
+            raise Exception("No FST-EVENT-SESSION (AP)")
+        if "new_state=SETUP_COMPLETION" in ev:
+            break
+
+def fst_start_and_connect(apdev, group, sgroup):
+    hglobal = hostapd.HostapdGlobal()
+    if "OK" not in hglobal.request("FST-MANAGER TEST_REQUEST IS_SUPPORTED"):
+        raise HwsimSkip("No FST testing support")
+
+    params = { "ssid": "fst_11a", "hw_mode": "a", "channel": "36",
+               "country_code": "US" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    fst_attach_ap(hglobal, apdev[0]['ifname'], group)
+
+    cmd = "FST-ATTACH %s %s" % (apdev[0]['ifname'], group)
+    if "FAIL" not in hglobal.request(cmd):
+        raise Exception("Duplicated FST-ATTACH (AP) accepted")
+
+    params = { "ssid": "fst_11g", "hw_mode": "g", "channel": "1",
+               "country_code": "US" }
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+    fst_attach_ap(hglobal, apdev[1]['ifname'], group)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    fst_attach_sta(wpas, wpas.ifname, sgroup)
+
+    wpas.interface_add("wlan6", set_ifname=False)
+    wpas2 = WpaSupplicant(ifname="wlan6")
+    fst_attach_sta(wpas, wpas2.ifname, sgroup)
+
+    wpas.connect("fst_11a", key_mgmt="NONE", scan_freq="5180",
+                 wait_connect=False)
+    wpas.wait_connected()
+
+    fst_wait_event_peer_sta(wpas, "connected", wpas.ifname, apdev[0]['bssid'])
+    fst_wait_event_peer_ap(hglobal, "connected", apdev[0]['ifname'],
+                           wpas.own_addr())
+
+    wpas2.connect("fst_11g", key_mgmt="NONE", scan_freq="2412",
+                  wait_connect=False)
+    wpas2.wait_connected()
+
+    fst_wait_event_peer_sta(wpas, "connected", wpas2.ifname, apdev[1]['bssid'])
+    fst_wait_event_peer_ap(hglobal, "connected", apdev[1]['ifname'],
+                           wpas2.own_addr())
+    return hglobal, wpas, wpas2, hapd, hapd2
+
+def test_fst_test_setup(dev, apdev, test_params):
+    """FST setup using separate commands"""
+    try:
+        _test_fst_test_setup(dev, apdev, test_params)
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+        dev[1].flush_scan_cache()
+
+def _test_fst_test_setup(dev, apdev, test_params):
+    group = "fstg0b"
+    sgroup = "fstg1b"
+    hglobal, wpas, wpas2, hapd, hapd2 = fst_start_and_connect(apdev, group, sgroup)
+
+    sid = wpas.global_request("FST-MANAGER SESSION_ADD " + sgroup).strip()
+    if "FAIL" in sid:
+        raise Exception("FST-MANAGER SESSION_ADD (STA) failed")
+
+    fst_session_set(wpas, sid, "old_ifname", wpas.ifname)
+    fst_session_set(wpas, sid, "old_peer_addr", apdev[0]['bssid'])
+    fst_session_set(wpas, sid, "new_ifname", wpas2.ifname)
+    fst_session_set(wpas, sid, "new_peer_addr", apdev[1]['bssid'])
+
+    if "OK" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+        raise Exception("FST-MANAGER SESSION_INITIATE failed")
+
+    while True:
+        ev = hglobal.wait_event(['FST-EVENT-SESSION'], timeout=5)
+        if ev is None:
+            raise Exception("No FST-EVENT-SESSION (AP)")
+        if "new_state=SETUP_COMPLETION" in ev:
+            f = re.search("session_id=(\d+)", ev)
+            if f is None:
+                raise Exception("No session_id in FST-EVENT-SESSION")
+            sid_ap = f.group(1)
+            cmd = "FST-MANAGER SESSION_RESPOND %s accept" % sid_ap
+            if "OK" not in hglobal.request(cmd):
+                raise Exception("FST-MANAGER SESSION_RESPOND failed on AP")
+            break
+
+    ev = wpas.wait_global_event(["FST-EVENT-SESSION"], timeout=5)
+    if ev is None:
+        raise Exception("No FST-EVENT-SESSION")
+    if "new_state=SETUP_COMPLETION" not in ev:
+        raise Exception("Unexpected FST-EVENT-SESSION data: " + ev)
+
+    ev = wpas.wait_global_event(["FST-EVENT-SESSION"], timeout=5)
+    if ev is None:
+        raise Exception("No FST-EVENT-SESSION")
+    if "event_type=EVENT_FST_ESTABLISHED" not in ev:
+        raise Exception("Unexpected FST-EVENT-SESSION data: " + ev)
+
+    cmd = "FST-MANAGER SESSION_REMOVE " + sid
+    if "OK" not in wpas.global_request(cmd):
+        raise Exception("FST-MANAGER SESSION_REMOVE failed")
+    ev = wpas.wait_global_event(["FST-EVENT-SESSION"], timeout=5)
+    if ev is None:
+        raise Exception("No FST-EVENT-SESSION")
+    if "new_state=INITIAL" not in ev:
+        raise Exception("Unexpected FST-EVENT-SESSION data (STA): " + ev)
+
+    ev = hglobal.wait_event(['FST-EVENT-SESSION'], timeout=5)
+    if ev is None:
+        raise Exception("No FST-EVENT-SESSION (AP)")
+    if "new_state=INITIAL" not in ev:
+        raise Exception("Unexpected FST-EVENT-SESSION data (AP): " + ev)
+
+    if "FAIL" not in wpas.global_request(cmd):
+        raise Exception("Duplicated FST-MANAGER SESSION_REMOVE accepted")
+
+    hglobal.request("FST-MANAGER SESSION_REMOVE " + sid_ap)
+
+    wpas.request("DISCONNECT")
+    wpas.wait_disconnected()
+    fst_wait_event_peer_sta(wpas, "disconnected", wpas.ifname,
+                            apdev[0]['bssid'])
+    fst_wait_event_peer_ap(hglobal, "disconnected", apdev[0]['ifname'],
+                           wpas.own_addr())
+
+    wpas2.request("DISCONNECT")
+    wpas2.wait_disconnected()
+    fst_wait_event_peer_sta(wpas, "disconnected", wpas2.ifname,
+                            apdev[1]['bssid'])
+    fst_wait_event_peer_ap(hglobal, "disconnected", apdev[1]['ifname'],
+                           wpas2.own_addr())
+
+    fst_detach_ap(hglobal, apdev[0]['ifname'], group)
+    if "FAIL" not in hglobal.request("FST-DETACH " + apdev[0]['ifname']):
+        raise Exception("Duplicated FST-DETACH (AP) accepted")
+    hapd.disable()
+
+    fst_detach_ap(hglobal, apdev[1]['ifname'], group)
+    hapd2.disable()
+
+    fst_detach_sta(wpas, wpas.ifname, sgroup)
+    fst_detach_sta(wpas, wpas2.ifname, sgroup)
+
+def test_fst_setup_mbie_diff(dev, apdev, test_params):
+    """FST setup and different MBIE in FST Setup Request"""
+    try:
+        _test_fst_setup_mbie_diff(dev, apdev, test_params)
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+        dev[1].flush_scan_cache()
+
+def _test_fst_setup_mbie_diff(dev, apdev, test_params):
+    group = "fstg0c"
+    sgroup = "fstg1c"
+    hglobal, wpas, wpas2, hapd, hapd2 = fst_start_and_connect(apdev, group, sgroup)
+
+    # FST Setup Request: Category, FST Action, Dialog Token (non-zero),
+    # LLT (32 bits, see 10.32), Session Transition (see 8.4.2.147),
+    # Multi-band element (optional, see 8.4.2.140)
+
+    # Session Transition: EID, Len, FSTS ID(4), Session Control,
+    # New Band (Band ID, Setup, Operation), Old Band (Band ID, Setup, Operation)
+
+    # Multi-band element: EID, Len, Multi-band Control, Band ID,
+    # Operating Class, Channel Number, BSSID (6), Beacon Interval (2),
+    # TSF Offset (8), Multi-band Connection Capability, FSTSessionTimeOut,
+    # STA MAC Address (6, optional), Pairwise Cipher Suite Count (2, optional),
+    # Pairwise Cipher Suite List (4xm, optional)
+
+    # MBIE with the non-matching STA MAC Address:
+    req = "1200011a060000"
+    stie = "a40b0100000000020001040001"
+    mbie = "9e1c0c0200010200000004000000000000000000000000ff0200000006ff"
+    fst_setup_req(wpas, hglobal, 5180, apdev[0]['bssid'], req, stie, mbie)
+
+    # MBIE without the STA MAC Address:
+    req = "1200011a060000"
+    stie = "a40b0100000000020001040001"
+    mbie = "9e16040200010200000004000000000000000000000000ff"
+    fst_setup_req(wpas, hglobal, 5180, apdev[0]['bssid'], req, stie, mbie)
+
+    # MBIE with unsupported STA Role:
+    req = "1200011a060000"
+    stie = "a40b0100000000020001040001"
+    mbie = "9e16070200010200000004000000000000000000000000ff"
+    fst_setup_req(wpas, hglobal, 5180, apdev[0]['bssid'], req, stie, mbie)
+
+    # MBIE with unsupported Band ID:
+    req = "1200011a060000"
+    stie = "a40b0100000000020001040001"
+    mbie = "9e1604ff00010200000004000000000000000000000000ff"
+    fst_setup_req(wpas, hglobal, 5180, apdev[0]['bssid'], req, stie, mbie)
+
+    # FST Setup Request without MBIE (different FSTS ID):
+    req = "1200011a060000"
+    stie = "a40b0200000000020001040001"
+    fst_setup_req(wpas, hglobal, 5180, apdev[0]['bssid'], req, stie)
+
+    # MBIE update OOM on AP
+    req = "1200011a060000"
+    stie = "a40b0100000000020001040001"
+    mbie = "9e16040200010200000004000000000000000000000000ff"
+    with alloc_fail(hapd, 1, "mb_ies_by_info"):
+        fst_setup_req(wpas, hglobal, 5180, apdev[0]['bssid'], req, stie, mbie,
+                      no_wait=True)
+
+def test_fst_many_setup(dev, apdev, test_params):
+    """FST setup multiple times"""
+    try:
+        _test_fst_many_setup(dev, apdev, test_params)
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+        dev[1].flush_scan_cache()
+
+def _test_fst_many_setup(dev, apdev, test_params):
+    group = "fstg0d"
+    sgroup = "fstg1d"
+    hglobal, wpas, wpas2, hapd, hapd2 = fst_start_and_connect(apdev, group, sgroup)
+
+    sid = wpas.global_request("FST-MANAGER SESSION_ADD " + sgroup).strip()
+    if "FAIL" in sid:
+        raise Exception("FST-MANAGER SESSION_ADD (STA) failed")
+
+    fst_session_set(wpas, sid, "old_ifname", wpas.ifname)
+    fst_session_set(wpas, sid, "old_peer_addr", apdev[0]['bssid'])
+    fst_session_set(wpas, sid, "new_ifname", wpas2.ifname)
+    fst_session_set(wpas, sid, "new_peer_addr", apdev[1]['bssid'])
+
+    for i in range(257):
+        if "OK" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+            raise Exception("FST-MANAGER SESSION_INITIATE failed")
+
+        while True:
+            ev = hglobal.wait_event(['FST-EVENT-SESSION'], timeout=5)
+            if ev is None:
+                raise Exception("No FST-EVENT-SESSION (AP)")
+            if "new_state=SETUP_COMPLETION" in ev:
+                f = re.search("session_id=(\d+)", ev)
+                if f is None:
+                    raise Exception("No session_id in FST-EVENT-SESSION")
+                sid_ap = f.group(1)
+                cmd = "FST-MANAGER SESSION_RESPOND %s accept" % sid_ap
+                if "OK" not in hglobal.request(cmd):
+                    raise Exception("FST-MANAGER SESSION_RESPOND failed on AP")
+                break
+
+        ev = wpas.wait_global_event(["FST-EVENT-SESSION"], timeout=5)
+        if ev is None:
+            raise Exception("No FST-EVENT-SESSION (STA)")
+        if "new_state=SETUP_COMPLETION" not in ev:
+            raise Exception("Unexpected FST-EVENT-SESSION data: " + ev)
+
+        ev = wpas.wait_global_event(["FST-EVENT-SESSION"], timeout=5)
+        if ev is None:
+            raise Exception("No FST-EVENT-SESSION (STA)")
+        if "event_type=EVENT_FST_ESTABLISHED" not in ev:
+            raise Exception("Unexpected FST-EVENT-SESSION data: " + ev)
+
+        if "OK" not in wpas.global_request("FST-MANAGER SESSION_TEARDOWN " + sid):
+            raise Exception("FST-MANAGER SESSION_INITIATE failed")
+
+        if i == 0:
+            if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_TEARDOWN " + sid):
+                raise Exception("Duplicate FST-MANAGER SESSION_TEARDOWN accepted")
+
+        ev = wpas.wait_global_event(["FST-EVENT-SESSION"], timeout=5)
+        if ev is None:
+            raise Exception("No FST-EVENT-SESSION (STA teardown -->initial)")
+        if "new_state=INITIAL" not in ev:
+            raise Exception("Unexpected FST-EVENT-SESSION data (STA): " + ev)
+
+        ev = hglobal.wait_event(['FST-EVENT-SESSION'], timeout=5)
+        if ev is None:
+            raise Exception("No FST-EVENT-SESSION (AP teardown -->initial)")
+        if "new_state=INITIAL" not in ev:
+            raise Exception("Unexpected FST-EVENT-SESSION data (AP): " + ev)
+
+        if "OK" not in hglobal.request("FST-MANAGER SESSION_REMOVE " + sid_ap):
+            raise Exception("FST-MANAGER SESSION_REMOVE (AP) failed")
+
+    if "OK" not in wpas.global_request("FST-MANAGER SESSION_REMOVE " + sid):
+        raise Exception("FST-MANAGER SESSION_REMOVE failed")
+
+    wpas.request("DISCONNECT")
+    wpas.wait_disconnected()
+    fst_wait_event_peer_sta(wpas, "disconnected", wpas.ifname,
+                            apdev[0]['bssid'])
+    fst_wait_event_peer_ap(hglobal, "disconnected", apdev[0]['ifname'],
+                           wpas.own_addr())
+
+    wpas2.request("DISCONNECT")
+    wpas2.wait_disconnected()
+    fst_wait_event_peer_sta(wpas, "disconnected", wpas2.ifname,
+                            apdev[1]['bssid'])
+    fst_wait_event_peer_ap(hglobal, "disconnected", apdev[1]['ifname'],
+                           wpas2.own_addr())
+
+    fst_detach_ap(hglobal, apdev[0]['ifname'], group)
+    fst_detach_ap(hglobal, apdev[1]['ifname'], group)
+    hapd.disable()
+    hapd2.disable()
+
+    fst_detach_sta(wpas, wpas.ifname, sgroup)
+    fst_detach_sta(wpas, wpas2.ifname, sgroup)
+
+def test_fst_attach_wpas_error(dev, apdev, test_params):
+    """FST attach errors in wpa_supplicant"""
+    if "OK" not in dev[0].global_request("FST-MANAGER TEST_REQUEST IS_SUPPORTED"):
+        raise HwsimSkip("No FST testing support")
+    group = "fstg0"
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    fst_attach_sta(wpas, wpas.ifname, group)
+    if "FAIL" not in wpas.global_request("FST-ATTACH %s %s" % (wpas.ifname,
+                                                               group)):
+        raise Exception("Duplicated FST-ATTACH accepted")
+    if "FAIL" not in wpas.global_request("FST-ATTACH %s %s" % ("foofoo",
+                                                               group)):
+        raise Exception("FST-ATTACH for unknown interface accepted")
+
+def test_fst_session_initiate_errors(dev, apdev, test_params):
+    """FST SESSION_INITIATE error cases"""
+    try:
+        _test_fst_session_initiate_errors(dev, apdev, test_params)
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+        dev[1].flush_scan_cache()
+
+def _test_fst_session_initiate_errors(dev, apdev, test_params):
+    group = "fstg0"
+    sgroup = "fstg1"
+    hglobal, wpas, wpas2, hapd, hapd2 = fst_start_and_connect(apdev, group, sgroup)
+
+    sid = wpas.global_request("FST-MANAGER SESSION_ADD " + sgroup).strip()
+    if "FAIL" in sid:
+        raise Exception("FST-MANAGER SESSION_ADD (STA) failed")
+
+    # No old peer MAC address
+    if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+        raise Exception("Invalid FST-MANAGER SESSION_INITIATE accepted")
+
+    fst_session_set(wpas, sid, "old_peer_addr", "00:ff:ff:ff:ff:ff")
+    # No new peer MAC address
+    if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+        raise Exception("Invalid FST-MANAGER SESSION_INITIATE accepted")
+
+    fst_session_set(wpas, sid, "new_peer_addr", "00:ff:ff:ff:ff:fe")
+    # No old interface defined
+    if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+        raise Exception("Invalid FST-MANAGER SESSION_INITIATE accepted")
+
+    fst_session_set(wpas, sid, "old_ifname", wpas.ifname)
+    # No new interface defined
+    if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+        raise Exception("Invalid FST-MANAGER SESSION_INITIATE accepted")
+
+    fst_session_set(wpas, sid, "new_ifname", wpas.ifname)
+    # Same interface set as old and new
+    if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+        raise Exception("Invalid FST-MANAGER SESSION_INITIATE accepted")
+
+    fst_session_set(wpas, sid, "new_ifname", wpas2.ifname)
+    # The preset old peer address is not connected
+    if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+        raise Exception("Invalid FST-MANAGER SESSION_INITIATE accepted")
+
+    fst_session_set(wpas, sid, "old_peer_addr", apdev[0]['bssid'])
+    # The preset new peer address is not connected
+    if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+        raise Exception("Invalid FST-MANAGER SESSION_INITIATE accepted")
+
+    fst_session_set(wpas, sid, "new_peer_addr", apdev[1]['bssid'])
+    # Initiate session setup
+    if "OK" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+        raise Exception("FST-MANAGER SESSION_INITIATE failed")
+
+    # Session in progress
+    if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+        raise Exception("Duplicated FST-MANAGER SESSION_INITIATE accepted")
+
+    sid2 = wpas.global_request("FST-MANAGER SESSION_ADD " + sgroup).strip()
+    if "FAIL" in sid:
+        raise Exception("FST-MANAGER SESSION_ADD (STA) failed")
+    fst_session_set(wpas, sid2, "old_ifname", wpas.ifname)
+    fst_session_set(wpas, sid2, "old_peer_addr", apdev[0]['bssid'])
+    fst_session_set(wpas, sid2, "new_ifname", wpas2.ifname)
+    fst_session_set(wpas, sid2, "new_peer_addr", apdev[1]['bssid'])
+
+    # There is another session in progress (old)
+    if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid2):
+        raise Exception("Duplicated FST-MANAGER SESSION_INITIATE accepted")
+
+    if "OK" not in wpas.global_request("FST-MANAGER SESSION_REMOVE " + sid):
+        raise Exception("FST-MANAGER SESSION_REMOVE failed")
+
+    while True:
+        ev = hglobal.wait_event(['FST-EVENT-SESSION'], timeout=5)
+        if ev is None:
+            raise Exception("No FST-EVENT-SESSION (AP)")
+        if "new_state=SETUP_COMPLETION" in ev:
+            f = re.search("session_id=(\d+)", ev)
+            if f is None:
+                raise Exception("No session_id in FST-EVENT-SESSION")
+            sid_ap = f.group(1)
+            break
+    if "OK" not in hglobal.request("FST-MANAGER SESSION_REMOVE " + sid_ap):
+        raise Exception("FST-MANAGER SESSION_REMOVE (AP) failed")
+
+    if "OK" not in wpas.global_request("FST-MANAGER SESSION_REMOVE " + sid2):
+        raise Exception("FST-MANAGER SESSION_REMOVE failed")
+
+def test_fst_session_respond_errors(dev, apdev, test_params):
+    """FST SESSION_RESPOND error cases"""
+    try:
+        _test_fst_session_respond_errors(dev, apdev, test_params)
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+        dev[1].flush_scan_cache()
+
+def _test_fst_session_respond_errors(dev, apdev, test_params):
+    group = "fstg0b"
+    sgroup = "fstg1b"
+    hglobal, wpas, wpas2, hapd, hapd2 = fst_start_and_connect(apdev, group, sgroup)
+
+    sid = wpas.global_request("FST-MANAGER SESSION_ADD " + sgroup).strip()
+    if "FAIL" in sid:
+        raise Exception("FST-MANAGER SESSION_ADD (STA) failed")
+
+    fst_session_set(wpas, sid, "old_ifname", wpas.ifname)
+    fst_session_set(wpas, sid, "old_peer_addr", apdev[0]['bssid'])
+    fst_session_set(wpas, sid, "new_ifname", wpas2.ifname)
+    fst_session_set(wpas, sid, "new_peer_addr", apdev[1]['bssid'])
+
+    if "OK" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+        raise Exception("FST-MANAGER SESSION_INITIATE failed")
+
+    while True:
+        ev = hglobal.wait_event(['FST-EVENT-SESSION'], timeout=5)
+        if ev is None:
+            raise Exception("No FST-EVENT-SESSION (AP)")
+        if "new_state=SETUP_COMPLETION" in ev:
+            f = re.search("session_id=(\d+)", ev)
+            if f is None:
+                raise Exception("No session_id in FST-EVENT-SESSION")
+            sid_ap = f.group(1)
+            break
+
+    # The preset peer address is not in the peer list
+    fst_session_set_ap(hglobal, sid_ap, "old_peer_addr", "00:00:00:00:00:01")
+    cmd = "FST-MANAGER SESSION_RESPOND %s accept" % sid_ap
+    if "FAIL" not in hglobal.request(cmd):
+        raise Exception("Invalid FST-MANAGER SESSION_RESPOND accepted")
+
+    # Same interface set as old and new
+    fst_session_set_ap(hglobal, sid_ap, "old_peer_addr", wpas.own_addr())
+    fst_session_set_ap(hglobal, sid_ap, "old_ifname", apdev[1]['ifname'])
+    cmd = "FST-MANAGER SESSION_RESPOND %s accept" % sid_ap
+    if "FAIL" not in hglobal.request(cmd):
+        raise Exception("Invalid FST-MANAGER SESSION_RESPOND accepted")
+
+    # valid command
+    fst_session_set_ap(hglobal, sid_ap, "old_ifname", apdev[0]['ifname'])
+    cmd = "FST-MANAGER SESSION_RESPOND %s accept" % sid_ap
+    if "OK" not in hglobal.request(cmd):
+        raise Exception("FST-MANAGER SESSION_RESPOND failed")
+
+    # incorrect state
+    cmd = "FST-MANAGER SESSION_RESPOND %s accept" % sid_ap
+    if "FAIL" not in hglobal.request(cmd):
+        raise Exception("Invalid FST-MANAGER SESSION_RESPOND accepted")
+
+    cmd = "FST-MANAGER SESSION_REMOVE " + sid
+    if "OK" not in wpas.global_request(cmd):
+        raise Exception("FST-MANAGER SESSION_REMOVE (STA) failed")
+
+    cmd = "FST-MANAGER SESSION_REMOVE %s" % sid_ap
+    if "OK" not in hglobal.request(cmd):
+        raise Exception("FST-MANAGER SESSION_REMOVE (AP) failed")
diff --git a/hostap/tests/hwsim/test_gas.py b/hostap/tests/hwsim/test_gas.py
new file mode 100644
index 0000000..416d616
--- /dev/null
+++ b/hostap/tests/hwsim/test_gas.py
@@ -0,0 +1,943 @@
+# GAS tests
+# Copyright (c) 2013, Qualcomm Atheros, Inc.
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import binascii
+import logging
+logger = logging.getLogger()
+import re
+import struct
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import alloc_fail, skip_with_fips
+
+def hs20_ap_params():
+    params = hostapd.wpa2_params(ssid="test-gas")
+    params['wpa_key_mgmt'] = "WPA-EAP"
+    params['ieee80211w'] = "1"
+    params['ieee8021x'] = "1"
+    params['auth_server_addr'] = "127.0.0.1"
+    params['auth_server_port'] = "1812"
+    params['auth_server_shared_secret'] = "radius"
+    params['interworking'] = "1"
+    params['access_network_type'] = "14"
+    params['internet'] = "1"
+    params['asra'] = "0"
+    params['esr'] = "0"
+    params['uesa'] = "0"
+    params['venue_group'] = "7"
+    params['venue_type'] = "1"
+    params['venue_name'] = [ "eng:Example venue", "fin:Esimerkkipaikka" ]
+    params['roaming_consortium'] = [ "112233", "1020304050", "010203040506",
+                                     "fedcba" ]
+    params['domain_name'] = "example.com,another.example.com"
+    params['nai_realm'] = [ "0,example.com,13[5:6],21[2:4][5:7]",
+                            "0,another.example.com" ]
+    params['anqp_3gpp_cell_net'] = "244,91"
+    params['network_auth_type'] = "02http://www.example.com/redirect/me/here/"
+    params['ipaddr_type_availability'] = "14"
+    params['hs20'] = "1"
+    params['hs20_oper_friendly_name'] = [ "eng:Example operator", "fin:Esimerkkioperaattori" ]
+    params['hs20_wan_metrics'] = "01:8000:1000:80:240:3000"
+    params['hs20_conn_capab'] = [ "1:0:2", "6:22:1", "17:5060:0" ]
+    params['hs20_operating_class'] = "5173"
+    return params
+
+def start_ap(ap):
+    params = hs20_ap_params()
+    params['hessid'] = ap['bssid']
+    hostapd.add_ap(ap['ifname'], params)
+    return hostapd.Hostapd(ap['ifname'])
+
+def get_gas_response(dev, bssid, info, allow_fetch_failure=False,
+                     extra_test=False):
+    exp = r'<.>(GAS-RESPONSE-INFO) addr=([0-9a-f:]*) dialog_token=([0-9]*) status_code=([0-9]*) resp_len=([\-0-9]*)'
+    res = re.split(exp, info)
+    if len(res) < 6:
+        raise Exception("Could not parse GAS-RESPONSE-INFO")
+    if res[2] != bssid:
+        raise Exception("Unexpected BSSID in response")
+    token = res[3]
+    status = res[4]
+    if status != "0":
+        raise Exception("GAS query failed")
+    resp_len = res[5]
+    if resp_len == "-1":
+        raise Exception("GAS query reported invalid response length")
+    if int(resp_len) > 2000:
+        raise Exception("Unexpected long GAS response")
+
+    if extra_test:
+        if "FAIL" not in dev.request("GAS_RESPONSE_GET " + bssid + " 123456"):
+            raise Exception("Invalid dialog token accepted")
+        if "FAIL-Invalid range" not in dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 10000,10001"):
+            raise Exception("Invalid range accepted")
+        if "FAIL-Invalid range" not in dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 0,10000"):
+            raise Exception("Invalid range accepted")
+        if "FAIL" not in dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 0"):
+            raise Exception("Invalid GAS_RESPONSE_GET accepted")
+
+        res1_2 = dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 1,2")
+        res5_3 = dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 5,3")
+
+    resp = dev.request("GAS_RESPONSE_GET " + bssid + " " + token)
+    if "FAIL" in resp:
+        if allow_fetch_failure:
+            logger.debug("GAS response was not available anymore")
+            return
+        raise Exception("Could not fetch GAS response")
+    if len(resp) != int(resp_len) * 2:
+        raise Exception("Unexpected GAS response length")
+    logger.debug("GAS response: " + resp)
+    if extra_test:
+        if resp[2:6] != res1_2:
+            raise Exception("Unexpected response substring res1_2: " + res1_2)
+        if resp[10:16] != res5_3:
+            raise Exception("Unexpected response substring res5_3: " + res5_3)
+
+def test_gas_generic(dev, apdev):
+    """Generic GAS query"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    cmds = [ "foo",
+             "00:11:22:33:44:55",
+             "00:11:22:33:44:55 ",
+             "00:11:22:33:44:55  ",
+             "00:11:22:33:44:55 1",
+             "00:11:22:33:44:55 1 1234",
+             "00:11:22:33:44:55 qq",
+             "00:11:22:33:44:55 qq 1234",
+             "00:11:22:33:44:55 00      1",
+             "00:11:22:33:44:55 00 123",
+             "00:11:22:33:44:55 00 ",
+             "00:11:22:33:44:55 00 qq" ]
+    for cmd in cmds:
+        if "FAIL" not in dev[0].request("GAS_REQUEST " + cmd):
+            raise Exception("Invalid GAS_REQUEST accepted: " + cmd)
+
+    dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+    req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101")
+    if "FAIL" in req:
+        raise Exception("GAS query request rejected")
+    ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10)
+    if ev is None:
+        raise Exception("GAS query timed out")
+    get_gas_response(dev[0], bssid, ev, extra_test=True)
+
+    if "FAIL" not in dev[0].request("GAS_RESPONSE_GET ff"):
+        raise Exception("Invalid GAS_RESPONSE_GET accepted")
+
+def test_gas_concurrent_scan(dev, apdev):
+    """Generic GAS queries with concurrent scan operation"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    # get BSS entry available to allow GAS query
+    dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+
+    logger.info("Request concurrent operations")
+    req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101")
+    if "FAIL" in req:
+        raise Exception("GAS query request rejected")
+    req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000801")
+    if "FAIL" in req:
+        raise Exception("GAS query request rejected")
+    dev[0].scan(no_wait=True)
+    req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000201")
+    if "FAIL" in req:
+        raise Exception("GAS query request rejected")
+    req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000501")
+    if "FAIL" in req:
+        raise Exception("GAS query request rejected")
+
+    responses = 0
+    for i in range(0, 5):
+        ev = dev[0].wait_event(["GAS-RESPONSE-INFO", "CTRL-EVENT-SCAN-RESULTS"],
+                               timeout=10)
+        if ev is None:
+            raise Exception("Operation timed out")
+        if "GAS-RESPONSE-INFO" in ev:
+            responses = responses + 1
+            get_gas_response(dev[0], bssid, ev, allow_fetch_failure=True)
+
+    if responses != 4:
+        raise Exception("Unexpected number of GAS responses")
+
+def test_gas_concurrent_connect(dev, apdev):
+    """Generic GAS queries with concurrent connection operation"""
+    skip_with_fips(dev[0])
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+
+    logger.debug("Start concurrent connect and GAS request")
+    dev[0].connect("test-gas", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+                   password="password", phase2="auth=MSCHAPV2",
+                   ca_cert="auth_serv/ca.pem", wait_connect=False,
+                   scan_freq="2412")
+    req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101")
+    if "FAIL" in req:
+        raise Exception("GAS query request rejected")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "GAS-RESPONSE-INFO"],
+                           timeout=20)
+    if ev is None:
+        raise Exception("Operation timed out")
+    if "CTRL-EVENT-CONNECTED" not in ev:
+        raise Exception("Unexpected operation order")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "GAS-RESPONSE-INFO"],
+                           timeout=20)
+    if ev is None:
+        raise Exception("Operation timed out")
+    if "GAS-RESPONSE-INFO" not in ev:
+        raise Exception("Unexpected operation order")
+    get_gas_response(dev[0], bssid, ev)
+
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=5)
+
+    logger.debug("Wait six seconds for expiration of connect-without-scan")
+    time.sleep(6)
+    dev[0].dump_monitor()
+
+    logger.debug("Start concurrent GAS request and connect")
+    req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101")
+    if "FAIL" in req:
+        raise Exception("GAS query request rejected")
+    dev[0].request("RECONNECT")
+
+    ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10)
+    if ev is None:
+        raise Exception("Operation timed out")
+    get_gas_response(dev[0], bssid, ev)
+
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=20)
+    if ev is None:
+        raise Exception("No new scan results reported")
+
+    ev = dev[0].wait_connected(timeout=20, error="Operation tiemd out")
+    if "CTRL-EVENT-CONNECTED" not in ev:
+        raise Exception("Unexpected operation order")
+
+def test_gas_fragment(dev, apdev):
+    """GAS fragmentation"""
+    hapd = start_ap(apdev[0])
+    hapd.set("gas_frag_limit", "50")
+
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+    dev[0].request("FETCH_ANQP")
+    ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=1)
+    if ev is None:
+        raise Exception("No GAS-QUERY-DONE event")
+    if "result=SUCCESS" not in ev:
+        raise Exception("Unexpected GAS result: " + ev)
+    for i in range(0, 13):
+        ev = dev[0].wait_event(["RX-ANQP", "RX-HS20-ANQP"], timeout=5)
+        if ev is None:
+            raise Exception("Operation timed out")
+    ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=1)
+    if ev is None:
+        raise Exception("No ANQP-QUERY-DONE event")
+    if "result=SUCCESS" not in ev:
+        raise Exception("Unexpected ANQP result: " + ev)
+
+def test_gas_comeback_delay(dev, apdev):
+    """GAS comeback delay"""
+    hapd = start_ap(apdev[0])
+    hapd.set("gas_comeback_delay", "500")
+
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+    dev[0].request("FETCH_ANQP")
+    if "FAIL-BUSY" not in dev[0].request("SCAN"):
+        raise Exception("SCAN accepted during FETCH_ANQP")
+    for i in range(0, 6):
+        ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+        if ev is None:
+            raise Exception("Operation timed out")
+
+def test_gas_stop_fetch_anqp(dev, apdev):
+    """Stop FETCH_ANQP operation"""
+    hapd = start_ap(apdev[0])
+
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+    hapd.set("ext_mgmt_frame_handling", "1")
+    dev[0].request("FETCH_ANQP")
+    dev[0].request("STOP_FETCH_ANQP")
+    hapd.set("ext_mgmt_frame_handling", "0")
+    ev = dev[0].wait_event(["RX-ANQP", "GAS-QUERY-DONE"], timeout=10)
+    if ev is None:
+        raise Exception("GAS-QUERY-DONE timed out")
+    if "RX-ANQP" in ev:
+        raise Exception("Unexpected ANQP response received")
+
+def test_gas_anqp_get(dev, apdev):
+    """GAS/ANQP query for both IEEE 802.11 and Hotspot 2.0 elements"""
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+    if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258,268,hs20:3,hs20:4"):
+        raise Exception("ANQP_GET command failed")
+
+    ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+    if ev is None:
+        raise Exception("GAS query start timed out")
+
+    ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+    if ev is None:
+        raise Exception("GAS query timed out")
+
+    ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
+    if ev is None or "Venue Name" not in ev:
+        raise Exception("Did not receive Venue Name")
+
+    ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
+    if ev is None or "Domain Name list" not in ev:
+        raise Exception("Did not receive Domain Name list")
+
+    ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
+    if ev is None or "Operator Friendly Name" not in ev:
+        raise Exception("Did not receive Operator Friendly Name")
+
+    ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
+    if ev is None or "WAN Metrics" not in ev:
+        raise Exception("Did not receive WAN Metrics")
+
+    ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+    if ev is None:
+        raise Exception("ANQP-QUERY-DONE event not seen")
+    if "result=SUCCESS" not in ev:
+        raise Exception("Unexpected result: " + ev)
+
+    if "OK" not in dev[0].request("HS20_ANQP_GET " + bssid + " 3,4"):
+        raise Exception("ANQP_GET command failed")
+
+    ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
+    if ev is None or "Operator Friendly Name" not in ev:
+        raise Exception("Did not receive Operator Friendly Name")
+
+    ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
+    if ev is None or "WAN Metrics" not in ev:
+        raise Exception("Did not receive WAN Metrics")
+
+    cmds = [ "",
+             "foo",
+             "00:11:22:33:44:55 258,hs20:-1",
+             "00:11:22:33:44:55 258,hs20:0",
+             "00:11:22:33:44:55 258,hs20:32",
+             "00:11:22:33:44:55 hs20:-1",
+             "00:11:22:33:44:55 hs20:0",
+             "00:11:22:33:44:55 hs20:32",
+             "00:11:22:33:44:55",
+             "00:11:22:33:44:55 ",
+             "00:11:22:33:44:55 0" ]
+    for cmd in cmds:
+        if "FAIL" not in dev[0].request("ANQP_GET " + cmd):
+            raise Exception("Invalid ANQP_GET accepted")
+
+    cmds = [ "",
+             "foo",
+             "00:11:22:33:44:55 -1",
+             "00:11:22:33:44:55 0",
+             "00:11:22:33:44:55 32",
+             "00:11:22:33:44:55",
+             "00:11:22:33:44:55 ",
+             "00:11:22:33:44:55 0" ]
+    for cmd in cmds:
+        if "FAIL" not in dev[0].request("HS20_ANQP_GET " + cmd):
+            raise Exception("Invalid HS20_ANQP_GET accepted")
+
+def expect_gas_result(dev, result, status=None):
+    ev = dev.wait_event(["GAS-QUERY-DONE"], timeout=10)
+    if ev is None:
+        raise Exception("GAS query timed out")
+    if "result=" + result not in ev:
+        raise Exception("Unexpected GAS query result")
+    if status and "status_code=" + str(status) + ' ' not in ev:
+        raise Exception("Unexpected GAS status code")
+
+def anqp_get(dev, bssid, id):
+    if "OK" not in dev.request("ANQP_GET " + bssid + " " + str(id)):
+        raise Exception("ANQP_GET command failed")
+    ev = dev.wait_event(["GAS-QUERY-START"], timeout=5)
+    if ev is None:
+        raise Exception("GAS query start timed out")
+
+def test_gas_timeout(dev, apdev):
+    """GAS timeout"""
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+    hapd.set("ext_mgmt_frame_handling", "1")
+
+    anqp_get(dev[0], bssid, 263)
+
+    ev = hapd.wait_event(["MGMT-RX"], timeout=5)
+    if ev is None:
+        raise Exception("MGMT RX wait timed out")
+
+    expect_gas_result(dev[0], "TIMEOUT")
+
+MGMT_SUBTYPE_ACTION = 13
+ACTION_CATEG_PUBLIC = 4
+
+GAS_INITIAL_REQUEST = 10
+GAS_INITIAL_RESPONSE = 11
+GAS_COMEBACK_REQUEST = 12
+GAS_COMEBACK_RESPONSE = 13
+GAS_ACTIONS = [ GAS_INITIAL_REQUEST, GAS_INITIAL_RESPONSE,
+                GAS_COMEBACK_REQUEST, GAS_COMEBACK_RESPONSE ]
+
+def anqp_adv_proto():
+    return struct.pack('BBBB', 108, 2, 127, 0)
+
+def anqp_initial_resp(dialog_token, status_code, comeback_delay=0):
+    return struct.pack('<BBBHH', ACTION_CATEG_PUBLIC, GAS_INITIAL_RESPONSE,
+                       dialog_token, status_code, comeback_delay) + anqp_adv_proto()
+
+def anqp_comeback_resp(dialog_token, status_code=0, id=0, more=False, comeback_delay=0, bogus_adv_proto=False):
+    if more:
+        id |= 0x80
+    if bogus_adv_proto:
+        adv = struct.pack('BBBB', 108, 2, 127, 1)
+    else:
+        adv = anqp_adv_proto()
+    return struct.pack('<BBBHBH', ACTION_CATEG_PUBLIC, GAS_COMEBACK_RESPONSE,
+                       dialog_token, status_code, id, comeback_delay) + adv
+
+def gas_rx(hapd):
+    count = 0
+    while count < 30:
+        count = count + 1
+        query = hapd.mgmt_rx()
+        if query is None:
+            raise Exception("Action frame not received")
+        if query['subtype'] != MGMT_SUBTYPE_ACTION:
+            continue
+        payload = query['payload']
+        if len(payload) < 2:
+            continue
+        (category, action) = struct.unpack('BB', payload[0:2])
+        if category != ACTION_CATEG_PUBLIC or action not in GAS_ACTIONS:
+            continue
+        return query
+    raise Exception("No Action frame received")
+
+def parse_gas(payload):
+    pos = payload
+    (category, action, dialog_token) = struct.unpack('BBB', pos[0:3])
+    if category != ACTION_CATEG_PUBLIC:
+        return None
+    if action not in GAS_ACTIONS:
+        return None
+    gas = {}
+    gas['action'] = action
+    pos = pos[3:]
+
+    if len(pos) < 1 and action != GAS_COMEBACK_REQUEST:
+        return None
+
+    gas['dialog_token'] = dialog_token
+
+    if action == GAS_INITIAL_RESPONSE:
+        if len(pos) < 4:
+            return None
+        (status_code, comeback_delay) = struct.unpack('<HH', pos[0:4])
+        gas['status_code'] = status_code
+        gas['comeback_delay'] = comeback_delay
+
+    if action == GAS_COMEBACK_RESPONSE:
+        if len(pos) < 5:
+            return None
+        (status_code, frag, comeback_delay) = struct.unpack('<HBH', pos[0:5])
+        gas['status_code'] = status_code
+        gas['frag'] = frag
+        gas['comeback_delay'] = comeback_delay
+
+    return gas
+
+def action_response(req):
+    resp = {}
+    resp['fc'] = req['fc']
+    resp['da'] = req['sa']
+    resp['sa'] = req['da']
+    resp['bssid'] = req['bssid']
+    return resp
+
+def send_gas_resp(hapd, resp):
+    hapd.mgmt_tx(resp)
+    ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+    if ev is None:
+        raise Exception("Missing TX status for GAS response")
+    if "ok=1" not in ev:
+        raise Exception("GAS response not acknowledged")
+
+def test_gas_invalid_response_type(dev, apdev):
+    """GAS invalid response type"""
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+    hapd.set("ext_mgmt_frame_handling", "1")
+
+    anqp_get(dev[0], bssid, 263)
+
+    query = gas_rx(hapd)
+    gas = parse_gas(query['payload'])
+
+    resp = action_response(query)
+    # GAS Comeback Response instead of GAS Initial Response
+    resp['payload'] = anqp_comeback_resp(gas['dialog_token']) + struct.pack('<H', 0)
+    send_gas_resp(hapd, resp)
+
+    # station drops the invalid frame, so this needs to result in GAS timeout
+    expect_gas_result(dev[0], "TIMEOUT")
+
+def test_gas_failure_status_code(dev, apdev):
+    """GAS failure status code"""
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+    hapd.set("ext_mgmt_frame_handling", "1")
+
+    anqp_get(dev[0], bssid, 263)
+
+    query = gas_rx(hapd)
+    gas = parse_gas(query['payload'])
+
+    resp = action_response(query)
+    resp['payload'] = anqp_initial_resp(gas['dialog_token'], 61) + struct.pack('<H', 0)
+    send_gas_resp(hapd, resp)
+
+    expect_gas_result(dev[0], "FAILURE")
+
+def test_gas_malformed(dev, apdev):
+    """GAS malformed response frames"""
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+    hapd.set("ext_mgmt_frame_handling", "1")
+
+    anqp_get(dev[0], bssid, 263)
+
+    query = gas_rx(hapd)
+    gas = parse_gas(query['payload'])
+
+    resp = action_response(query)
+
+    resp['payload'] = struct.pack('<BBBH', ACTION_CATEG_PUBLIC,
+                                  GAS_COMEBACK_RESPONSE,
+                                  gas['dialog_token'], 0)
+    hapd.mgmt_tx(resp)
+
+    resp['payload'] = struct.pack('<BBBHB', ACTION_CATEG_PUBLIC,
+                                  GAS_COMEBACK_RESPONSE,
+                                  gas['dialog_token'], 0, 0)
+    hapd.mgmt_tx(resp)
+
+    hdr = struct.pack('<BBBHH', ACTION_CATEG_PUBLIC, GAS_INITIAL_RESPONSE,
+                      gas['dialog_token'], 0, 0)
+    resp['payload'] = hdr + struct.pack('B', 108)
+    hapd.mgmt_tx(resp)
+    resp['payload'] = hdr + struct.pack('BB', 108, 0)
+    hapd.mgmt_tx(resp)
+    resp['payload'] = hdr + struct.pack('BB', 108, 1)
+    hapd.mgmt_tx(resp)
+    resp['payload'] = hdr + struct.pack('BB', 108, 255)
+    hapd.mgmt_tx(resp)
+    resp['payload'] = hdr + struct.pack('BBB', 108, 1, 127)
+    hapd.mgmt_tx(resp)
+    resp['payload'] = hdr + struct.pack('BBB', 108, 2, 127)
+    hapd.mgmt_tx(resp)
+    resp['payload'] = hdr + struct.pack('BBBB', 0, 2, 127, 0)
+    hapd.mgmt_tx(resp)
+
+    resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<H', 1)
+    hapd.mgmt_tx(resp)
+
+    resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<HB', 2, 0)
+    hapd.mgmt_tx(resp)
+
+    resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<H', 65535)
+    hapd.mgmt_tx(resp)
+
+    resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<HBB', 1, 0, 0)
+    hapd.mgmt_tx(resp)
+
+    # Station drops invalid frames, but the last of the responses is valid from
+    # GAS view point even though it has an extra octet in the end and the ANQP
+    # part of the response is not valid. This is reported as successfully
+    # completed GAS exchange.
+    expect_gas_result(dev[0], "SUCCESS")
+
+    ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=5)
+    if ev is None:
+        raise Exception("ANQP-QUERY-DONE not reported")
+    if "result=INVALID_FRAME" not in ev:
+        raise Exception("Unexpected result: " + ev)
+
+def init_gas(hapd, bssid, dev):
+    anqp_get(dev, bssid, 263)
+    query = gas_rx(hapd)
+    gas = parse_gas(query['payload'])
+    dialog_token = gas['dialog_token']
+
+    resp = action_response(query)
+    resp['payload'] = anqp_initial_resp(dialog_token, 0, comeback_delay=1) + struct.pack('<H', 0)
+    send_gas_resp(hapd, resp)
+
+    query = gas_rx(hapd)
+    gas = parse_gas(query['payload'])
+    if gas['action'] != GAS_COMEBACK_REQUEST:
+        raise Exception("Unexpected request action")
+    if gas['dialog_token'] != dialog_token:
+        raise Exception("Unexpected dialog token change")
+    return query, dialog_token
+
+def test_gas_malformed_comeback_resp(dev, apdev):
+    """GAS malformed comeback response frames"""
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+    hapd.set("ext_mgmt_frame_handling", "1")
+
+    logger.debug("Non-zero status code in comeback response")
+    query, dialog_token = init_gas(hapd, bssid, dev[0])
+    resp = action_response(query)
+    resp['payload'] = anqp_comeback_resp(dialog_token, status_code=2) + struct.pack('<H', 0)
+    send_gas_resp(hapd, resp)
+    expect_gas_result(dev[0], "FAILURE", status=2)
+
+    logger.debug("Different advertisement protocol in comeback response")
+    query, dialog_token = init_gas(hapd, bssid, dev[0])
+    resp = action_response(query)
+    resp['payload'] = anqp_comeback_resp(dialog_token, bogus_adv_proto=True) + struct.pack('<H', 0)
+    send_gas_resp(hapd, resp)
+    expect_gas_result(dev[0], "PEER_ERROR")
+
+    logger.debug("Non-zero frag id and comeback delay in comeback response")
+    query, dialog_token = init_gas(hapd, bssid, dev[0])
+    resp = action_response(query)
+    resp['payload'] = anqp_comeback_resp(dialog_token, id=1, comeback_delay=1) + struct.pack('<H', 0)
+    send_gas_resp(hapd, resp)
+    expect_gas_result(dev[0], "PEER_ERROR")
+
+    logger.debug("Unexpected frag id in comeback response")
+    query, dialog_token = init_gas(hapd, bssid, dev[0])
+    resp = action_response(query)
+    resp['payload'] = anqp_comeback_resp(dialog_token, id=1) + struct.pack('<H', 0)
+    send_gas_resp(hapd, resp)
+    expect_gas_result(dev[0], "PEER_ERROR")
+
+    logger.debug("Empty fragment and replay in comeback response")
+    query, dialog_token = init_gas(hapd, bssid, dev[0])
+    resp = action_response(query)
+    resp['payload'] = anqp_comeback_resp(dialog_token, more=True) + struct.pack('<H', 0)
+    send_gas_resp(hapd, resp)
+    query = gas_rx(hapd)
+    gas = parse_gas(query['payload'])
+    if gas['action'] != GAS_COMEBACK_REQUEST:
+        raise Exception("Unexpected request action")
+    if gas['dialog_token'] != dialog_token:
+        raise Exception("Unexpected dialog token change")
+    resp = action_response(query)
+    resp['payload'] = anqp_comeback_resp(dialog_token) + struct.pack('<H', 0)
+    send_gas_resp(hapd, resp)
+    resp['payload'] = anqp_comeback_resp(dialog_token, id=1) + struct.pack('<H', 0)
+    send_gas_resp(hapd, resp)
+    expect_gas_result(dev[0], "SUCCESS")
+
+    logger.debug("Unexpected initial response when waiting for comeback response")
+    query, dialog_token = init_gas(hapd, bssid, dev[0])
+    resp = action_response(query)
+    resp['payload'] = anqp_initial_resp(dialog_token, 0) + struct.pack('<H', 0)
+    send_gas_resp(hapd, resp)
+    ev = hapd.wait_event(["MGMT-RX"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected management frame")
+    expect_gas_result(dev[0], "TIMEOUT")
+
+    logger.debug("Too short comeback response")
+    query, dialog_token = init_gas(hapd, bssid, dev[0])
+    resp = action_response(query)
+    resp['payload'] = struct.pack('<BBBH', ACTION_CATEG_PUBLIC,
+                                  GAS_COMEBACK_RESPONSE, dialog_token, 0)
+    send_gas_resp(hapd, resp)
+    ev = hapd.wait_event(["MGMT-RX"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected management frame")
+    expect_gas_result(dev[0], "TIMEOUT")
+
+    logger.debug("Too short comeback response(2)")
+    query, dialog_token = init_gas(hapd, bssid, dev[0])
+    resp = action_response(query)
+    resp['payload'] = struct.pack('<BBBHBB', ACTION_CATEG_PUBLIC,
+                                  GAS_COMEBACK_RESPONSE, dialog_token, 0, 0x80,
+                                  0)
+    send_gas_resp(hapd, resp)
+    ev = hapd.wait_event(["MGMT-RX"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected management frame")
+    expect_gas_result(dev[0], "TIMEOUT")
+
+    logger.debug("Maximum comeback response fragment claiming more fragments")
+    query, dialog_token = init_gas(hapd, bssid, dev[0])
+    resp = action_response(query)
+    resp['payload'] = anqp_comeback_resp(dialog_token, more=True) + struct.pack('<H', 0)
+    send_gas_resp(hapd, resp)
+    for i in range(1, 129):
+        query = gas_rx(hapd)
+        gas = parse_gas(query['payload'])
+        if gas['action'] != GAS_COMEBACK_REQUEST:
+            raise Exception("Unexpected request action")
+        if gas['dialog_token'] != dialog_token:
+            raise Exception("Unexpected dialog token change")
+        resp = action_response(query)
+        resp['payload'] = anqp_comeback_resp(dialog_token, id=i, more=True) + struct.pack('<H', 0)
+        send_gas_resp(hapd, resp)
+    expect_gas_result(dev[0], "PEER_ERROR")
+
+def test_gas_comeback_resp_additional_delay(dev, apdev):
+    """GAS comeback response requesting additional delay"""
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+    hapd.set("ext_mgmt_frame_handling", "1")
+
+    query, dialog_token = init_gas(hapd, bssid, dev[0])
+    for i in range(0, 2):
+        resp = action_response(query)
+        resp['payload'] = anqp_comeback_resp(dialog_token, status_code=95, comeback_delay=50) + struct.pack('<H', 0)
+        send_gas_resp(hapd, resp)
+        query = gas_rx(hapd)
+        gas = parse_gas(query['payload'])
+        if gas['action'] != GAS_COMEBACK_REQUEST:
+            raise Exception("Unexpected request action")
+        if gas['dialog_token'] != dialog_token:
+            raise Exception("Unexpected dialog token change")
+    resp = action_response(query)
+    resp['payload'] = anqp_comeback_resp(dialog_token, status_code=0) + struct.pack('<H', 0)
+    send_gas_resp(hapd, resp)
+    expect_gas_result(dev[0], "SUCCESS")
+
+def test_gas_unknown_adv_proto(dev, apdev):
+    """Unknown advertisement protocol id"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+    req = dev[0].request("GAS_REQUEST " + bssid + " 42 000102000101")
+    if "FAIL" in req:
+        raise Exception("GAS query request rejected")
+    expect_gas_result(dev[0], "FAILURE", "59")
+    ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10)
+    if ev is None:
+        raise Exception("GAS query timed out")
+    exp = r'<.>(GAS-RESPONSE-INFO) addr=([0-9a-f:]*) dialog_token=([0-9]*) status_code=([0-9]*) resp_len=([\-0-9]*)'
+    res = re.split(exp, ev)
+    if len(res) < 6:
+        raise Exception("Could not parse GAS-RESPONSE-INFO")
+    if res[2] != bssid:
+        raise Exception("Unexpected BSSID in response")
+    status = res[4]
+    if status != "59":
+        raise Exception("Unexpected GAS-RESPONSE-INFO status")
+
+def test_gas_max_pending(dev, apdev):
+    """GAS and maximum pending query limit"""
+    hapd = start_ap(apdev[0])
+    hapd.set("gas_frag_limit", "50")
+    bssid = apdev[0]['bssid']
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    if "OK" not in wpas.request("P2P_SET listen_channel 1"):
+        raise Exception("Failed to set listen channel")
+    if "OK" not in wpas.p2p_listen():
+        raise Exception("Failed to start listen state")
+    if "FAIL" in wpas.request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+
+    anqp_query = struct.pack('<HHHHHHHHHH', 256, 16, 257, 258, 260, 261, 262, 263, 264, 268)
+    gas = struct.pack('<H', len(anqp_query)) + anqp_query
+
+    for dialog_token in range(1, 10):
+        msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST,
+                          dialog_token) + anqp_adv_proto() + gas
+        req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(msg))
+        if "OK" not in wpas.request(req):
+            raise Exception("Could not send management frame")
+        resp = wpas.mgmt_rx()
+        if resp is None:
+            raise Exception("MGMT-RX timeout")
+        if 'payload' not in resp:
+            raise Exception("Missing payload")
+        gresp = parse_gas(resp['payload'])
+        if gresp['dialog_token'] != dialog_token:
+            raise Exception("Dialog token mismatch")
+        status_code = gresp['status_code']
+        if dialog_token < 9 and status_code != 0:
+            raise Exception("Unexpected failure status code {} for dialog token {}".format(status_code, dialog_token))
+        if dialog_token > 8 and status_code == 0:
+            raise Exception("Unexpected success status code {} for dialog token {}".format(status_code, dialog_token))
+
+def test_gas_no_pending(dev, apdev):
+    """GAS and no pending query for comeback request"""
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    if "OK" not in wpas.request("P2P_SET listen_channel 1"):
+        raise Exception("Failed to set listen channel")
+    if "OK" not in wpas.p2p_listen():
+        raise Exception("Failed to start listen state")
+    if "FAIL" in wpas.request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+
+    msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_COMEBACK_REQUEST, 1)
+    req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(msg))
+    if "OK" not in wpas.request(req):
+        raise Exception("Could not send management frame")
+    resp = wpas.mgmt_rx()
+    if resp is None:
+        raise Exception("MGMT-RX timeout")
+    if 'payload' not in resp:
+        raise Exception("Missing payload")
+    gresp = parse_gas(resp['payload'])
+    status_code = gresp['status_code']
+    if status_code != 60:
+        raise Exception("Unexpected status code {} (expected 60)".format(status_code))
+
+def test_gas_missing_payload(dev, apdev):
+    """No action code in the query frame"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+
+    cmd = "MGMT_TX {} {} freq=2412 action=040A".format(bssid, bssid)
+    if "FAIL" in dev[0].request(cmd):
+        raise Exception("Could not send test Action frame")
+    ev = dev[0].wait_event(["MGMT-TX-STATUS"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on MGMT-TX-STATUS")
+    if "result=SUCCESS" not in ev:
+        raise Exception("AP did not ack Action frame")
+
+    cmd = "MGMT_TX {} {} freq=2412 action=04".format(bssid, bssid)
+    if "FAIL" in dev[0].request(cmd):
+        raise Exception("Could not send test Action frame")
+    ev = dev[0].wait_event(["MGMT-TX-STATUS"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on MGMT-TX-STATUS")
+    if "result=SUCCESS" not in ev:
+        raise Exception("AP did not ack Action frame")
+
+def test_gas_query_deinit(dev, apdev):
+    """Pending GAS/ANQP query during deinit"""
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+
+    wpas.scan_for_bss(bssid, freq="2412", force_scan=True)
+    id = wpas.request("RADIO_WORK add block-work")
+    if "OK" not in wpas.request("ANQP_GET " + bssid + " 258"):
+        raise Exception("ANQP_GET command failed")
+
+    ev = wpas.wait_event(["GAS-QUERY-START", "EXT-RADIO-WORK-START"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to start")
+    ev = wpas.wait_event(["GAS-QUERY-START", "EXT-RADIO-WORK-START"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to start (2)")
+
+    # Remove the interface while the gas-query radio work is still pending and
+    # GAS query has not yet been started.
+    wpas.interface_remove("wlan5")
+
+def test_gas_anqp_oom_wpas(dev, apdev):
+    """GAS/ANQP query and OOM in wpa_supplicant"""
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+
+    with alloc_fail(dev[0], 1, "gas_build_req"):
+        if "FAIL" not in dev[0].request("ANQP_GET " + bssid + " 258"):
+            raise Exception("Unexpected ANQP_GET command success (OOM)")
+
+def test_gas_anqp_oom_hapd(dev, apdev):
+    """GAS/ANQP query and OOM in hostapd"""
+    hapd = start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+
+    with alloc_fail(hapd, 1, "gas_build_resp"):
+        # This query will time out due to the AP not sending a response (OOM).
+        if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"):
+            raise Exception("ANQP_GET command failed")
+
+        ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+        if ev is None:
+            raise Exception("GAS query start timed out")
+
+        ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+        if ev is None:
+            raise Exception("GAS query timed out")
+        if "result=TIMEOUT" not in ev:
+            raise Exception("Unexpected result: " + ev)
+
+        ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+        if ev is None:
+            raise Exception("ANQP-QUERY-DONE event not seen")
+        if "result=FAILURE" not in ev:
+            raise Exception("Unexpected result: " + ev)
+
+    with alloc_fail(hapd, 1, "gas_anqp_build_comeback_resp"):
+        hapd.set("gas_frag_limit", "50")
+
+        # This query will time out due to the AP not sending a response (OOM).
+        print dev[0].request("FETCH_ANQP")
+        ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+        if ev is None:
+            raise Exception("GAS query start timed out")
+
+        ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+        if ev is None:
+            raise Exception("GAS query timed out")
+        if "result=TIMEOUT" not in ev:
+            raise Exception("Unexpected result: " + ev)
+
+        ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+        if ev is None:
+            raise Exception("ANQP-QUERY-DONE event not seen")
+        if "result=FAILURE" not in ev:
+            raise Exception("Unexpected result: " + ev)
diff --git a/hostap/tests/hwsim/test_hapd_ctrl.py b/hostap/tests/hwsim/test_hapd_ctrl.py
new file mode 100644
index 0000000..e45eca0
--- /dev/null
+++ b/hostap/tests/hwsim/test_hapd_ctrl.py
@@ -0,0 +1,557 @@
+# hostapd control interface
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import hostapd
+from utils import skip_with_fips
+
+def test_hapd_ctrl_status(dev, apdev):
+    """hostapd ctrl_iface STATUS commands"""
+    ssid = "hapd-ctrl"
+    bssid = apdev[0]['bssid']
+    params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    status = hapd.get_status()
+    driver = hapd.get_driver_status()
+
+    if status['bss[0]'] != apdev[0]['ifname']:
+        raise Exception("Unexpected bss[0]")
+    if status['ssid[0]'] != ssid:
+        raise Exception("Unexpected ssid[0]")
+    if status['bssid[0]'] != bssid:
+        raise Exception("Unexpected bssid[0]")
+    if status['freq'] != "2412":
+        raise Exception("Unexpected freq")
+
+    if driver['beacon_set'] != "1":
+        raise Exception("Unexpected beacon_set")
+    if driver['addr'] != bssid:
+        raise Exception("Unexpected addr")
+
+def test_hapd_ctrl_p2p_manager(dev, apdev):
+    """hostapd as P2P Device manager"""
+    ssid = "hapd-p2p-mgr"
+    passphrase = "12345678"
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['manage_p2p'] = '1'
+    params['allow_cross_connection'] = '0'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    addr = dev[0].own_addr()
+    if "OK" not in hapd.request("DEAUTHENTICATE " + addr + " p2p=2"):
+        raise Exception("DEAUTHENTICATE command failed")
+    dev[0].wait_disconnected(timeout=5)
+    dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+    if "OK" not in hapd.request("DISASSOCIATE " + addr + " p2p=2"):
+        raise Exception("DISASSOCIATE command failed")
+    dev[0].wait_disconnected(timeout=5)
+    dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+def test_hapd_ctrl_sta(dev, apdev):
+    """hostapd and STA ctrl_iface commands"""
+    ssid = "hapd-ctrl-sta"
+    passphrase = "12345678"
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    addr = dev[0].own_addr()
+    if "FAIL" in hapd.request("STA " + addr):
+        raise Exception("Unexpected STA failure")
+    if "FAIL" not in hapd.request("STA " + addr + " eapol"):
+        raise Exception("Unexpected STA-eapol success")
+    if "FAIL" not in hapd.request("STA 00:11:22:33:44"):
+        raise Exception("Unexpected STA success")
+    if "FAIL" not in hapd.request("STA 00:11:22:33:44:55"):
+        raise Exception("Unexpected STA success")
+
+    if len(hapd.request("STA-NEXT " + addr).splitlines()) > 0:
+        raise Exception("Unexpected STA-NEXT result")
+    if "FAIL" not in hapd.request("STA-NEXT 00:11:22:33:44"):
+        raise Exception("Unexpected STA-NEXT success")
+
+def test_hapd_ctrl_disconnect(dev, apdev):
+    """hostapd and disconnection ctrl_iface commands"""
+    ssid = "hapd-ctrl"
+    passphrase = "12345678"
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    addr = dev[0].p2p_dev_addr()
+
+    if "FAIL" not in hapd.request("DEAUTHENTICATE 00:11:22:33:44"):
+        raise Exception("Unexpected DEAUTHENTICATE success")
+
+    if "OK" not in hapd.request("DEAUTHENTICATE ff:ff:ff:ff:ff:ff"):
+        raise Exception("Unexpected DEAUTHENTICATE failure")
+    dev[0].wait_disconnected(timeout=5)
+    dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+    if "FAIL" not in hapd.request("DISASSOCIATE 00:11:22:33:44"):
+        raise Exception("Unexpected DISASSOCIATE success")
+
+    if "OK" not in hapd.request("DISASSOCIATE ff:ff:ff:ff:ff:ff"):
+        raise Exception("Unexpected DISASSOCIATE failure")
+    dev[0].wait_disconnected(timeout=5)
+    dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+def test_hapd_ctrl_chan_switch(dev, apdev):
+    """hostapd and CHAN_SWITCH ctrl_iface command"""
+    ssid = "hapd-ctrl"
+    params = { "ssid": ssid }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    if "FAIL" not in hapd.request("CHAN_SWITCH "):
+        raise Exception("Unexpected CHAN_SWITCH success")
+    if "FAIL" not in hapd.request("CHAN_SWITCH qwerty 2422"):
+        raise Exception("Unexpected CHAN_SWITCH success")
+    if "FAIL" not in hapd.request("CHAN_SWITCH 5 qwerty"):
+        raise Exception("Unexpected CHAN_SWITCH success")
+    if "FAIL" not in hapd.request("CHAN_SWITCH 0 2432 center_freq1=123 center_freq2=234 bandwidth=1000 sec_channel_offset=20 ht vht"):
+        raise Exception("Unexpected CHAN_SWITCH success")
+
+def test_hapd_ctrl_level(dev, apdev):
+    """hostapd and LEVEL ctrl_iface command"""
+    ssid = "hapd-ctrl"
+    params = { "ssid": ssid }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    if "FAIL" not in hapd.request("LEVEL 0"):
+        raise Exception("Unexpected LEVEL success on non-monitor interface")
+
+def test_hapd_ctrl_new_sta(dev, apdev):
+    """hostapd and NEW_STA ctrl_iface command"""
+    ssid = "hapd-ctrl"
+    params = { "ssid": ssid }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    if "FAIL" not in hapd.request("NEW_STA 00:11:22:33:44"):
+        raise Exception("Unexpected NEW_STA success")
+    if "OK" not in hapd.request("NEW_STA 00:11:22:33:44:55"):
+        raise Exception("Unexpected NEW_STA failure")
+    if "AUTHORIZED" not in hapd.request("STA 00:11:22:33:44:55"):
+        raise Exception("Unexpected NEW_STA STA status")
+
+def test_hapd_ctrl_get(dev, apdev):
+    """hostapd and GET ctrl_iface command"""
+    ssid = "hapd-ctrl"
+    params = { "ssid": ssid }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    if "FAIL" not in hapd.request("GET foo"):
+        raise Exception("Unexpected GET success")
+    if "FAIL" in hapd.request("GET version"):
+        raise Exception("Unexpected GET version failure")
+
+def test_hapd_ctrl_unknown(dev, apdev):
+    """hostapd and unknown ctrl_iface command"""
+    ssid = "hapd-ctrl"
+    params = { "ssid": ssid }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    if "UNKNOWN COMMAND" not in hapd.request("FOO"):
+        raise Exception("Unexpected response")
+
+def test_hapd_ctrl_hs20_wnm_notif(dev, apdev):
+    """hostapd and HS20_WNM_NOTIF ctrl_iface command"""
+    ssid = "hapd-ctrl"
+    params = { "ssid": ssid }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    if "FAIL" not in hapd.request("HS20_WNM_NOTIF 00:11:22:33:44 http://example.com/"):
+        raise Exception("Unexpected HS20_WNM_NOTIF success")
+    if "FAIL" not in hapd.request("HS20_WNM_NOTIF 00:11:22:33:44:55http://example.com/"):
+        raise Exception("Unexpected HS20_WNM_NOTIF success")
+
+def test_hapd_ctrl_hs20_deauth_req(dev, apdev):
+    """hostapd and HS20_DEAUTH_REQ ctrl_iface command"""
+    ssid = "hapd-ctrl"
+    params = { "ssid": ssid }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    if "FAIL" not in hapd.request("HS20_DEAUTH_REQ 00:11:22:33:44 1 120 http://example.com/"):
+        raise Exception("Unexpected HS20_DEAUTH_REQ success")
+    if "FAIL" not in hapd.request("HS20_DEAUTH_REQ 00:11:22:33:44:55"):
+        raise Exception("Unexpected HS20_DEAUTH_REQ success")
+    if "FAIL" not in hapd.request("HS20_DEAUTH_REQ 00:11:22:33:44:55 1"):
+        raise Exception("Unexpected HS20_DEAUTH_REQ success")
+
+def test_hapd_ctrl_disassoc_imminent(dev, apdev):
+    """hostapd and DISASSOC_IMMINENT ctrl_iface command"""
+    ssid = "hapd-ctrl"
+    params = { "ssid": ssid }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    if "FAIL" not in hapd.request("DISASSOC_IMMINENT 00:11:22:33:44"):
+        raise Exception("Unexpected DISASSOC_IMMINENT success")
+    if "FAIL" not in hapd.request("DISASSOC_IMMINENT 00:11:22:33:44:55"):
+        raise Exception("Unexpected DISASSOC_IMMINENT success")
+    if "FAIL" not in hapd.request("DISASSOC_IMMINENT 00:11:22:33:44:55 2"):
+        raise Exception("Unexpected DISASSOC_IMMINENT success")
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    addr = dev[0].p2p_interface_addr()
+    if "OK" not in hapd.request("DISASSOC_IMMINENT " + addr + " 2"):
+        raise Exception("Unexpected DISASSOC_IMMINENT failure")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+    if ev is None:
+        raise Exception("Scan timed out")
+
+def test_hapd_ctrl_ess_disassoc(dev, apdev):
+    """hostapd and ESS_DISASSOC ctrl_iface command"""
+    ssid = "hapd-ctrl"
+    params = { "ssid": ssid }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    if "FAIL" not in hapd.request("ESS_DISASSOC 00:11:22:33:44"):
+        raise Exception("Unexpected ESS_DISASSOCT success")
+    if "FAIL" not in hapd.request("ESS_DISASSOC 00:11:22:33:44:55"):
+        raise Exception("Unexpected ESS_DISASSOC success")
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    addr = dev[0].p2p_interface_addr()
+    if "FAIL" not in hapd.request("ESS_DISASSOC " + addr):
+        raise Exception("Unexpected ESS_DISASSOC success")
+    if "FAIL" not in hapd.request("ESS_DISASSOC " + addr + " -1"):
+        raise Exception("Unexpected ESS_DISASSOC success")
+    if "FAIL" not in hapd.request("ESS_DISASSOC " + addr + " 1"):
+        raise Exception("Unexpected ESS_DISASSOC success")
+    if "OK" not in hapd.request("ESS_DISASSOC " + addr + " 20 http://example.com/"):
+        raise Exception("Unexpected ESS_DISASSOC failure")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+    if ev is None:
+        raise Exception("Scan timed out")
+
+def test_hapd_ctrl_set_deny_mac_file(dev, apdev):
+    """hostapd and SET deny_mac_file ctrl_iface command"""
+    ssid = "hapd-ctrl"
+    params = { "ssid": ssid }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    if "OK" not in hapd.request("SET deny_mac_file hostapd.macaddr"):
+        raise Exception("Unexpected SET failure")
+    dev[0].wait_disconnected(timeout=15)
+    ev = dev[1].wait_event(["CTRL-EVENT-DISCONNECTED"], 1)
+    if ev is not None:
+        raise Exception("Unexpected disconnection")
+
+def test_hapd_ctrl_set_accept_mac_file(dev, apdev):
+    """hostapd and SET accept_mac_file ctrl_iface command"""
+    ssid = "hapd-ctrl"
+    params = { "ssid": ssid }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+    hapd.request("SET macaddr_acl 1")
+    if "OK" not in hapd.request("SET accept_mac_file hostapd.macaddr"):
+        raise Exception("Unexpected SET failure")
+    dev[1].wait_disconnected(timeout=15)
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], 1)
+    if ev is not None:
+        raise Exception("Unexpected disconnection")
+
+def test_hapd_ctrl_set_error_cases(dev, apdev):
+    """hostapd and SET error cases"""
+    ssid = "hapd-ctrl"
+    params = { "ssid": ssid }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    errors = [ "wpa_key_mgmt FOO",
+               "wpa_key_mgmt WPA-PSK   \t  FOO",
+               "wpa_key_mgmt    \t  ",
+               "wpa_pairwise FOO",
+               "wpa_pairwise   \t   ",
+               'wep_key0 "',
+               'wep_key0 "abcde',
+               "wep_key0 1",
+               "wep_key0 12q3456789",
+               "wep_key_len_broadcast 20",
+               "wep_rekey_period -1",
+               "wep_default_key 4",
+               "r0kh 02:00:00:00:03:0q nas1.w1.fi 100102030405060708090a0b0c0d0e0f",
+               "r0kh 02:00:00:00:03:00 12345678901234567890123456789012345678901234567890.nas1.w1.fi 100102030405060708090a0b0c0d0e0f",
+               "r0kh 02:00:00:00:03:00 nas1.w1.fi 100q02030405060708090a0b0c0d0e0f",
+               "r1kh 02:00:00:00:04:q0 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f",
+               "r1kh 02:00:00:00:04:00 00:01:02:03:04:q6 200102030405060708090a0b0c0d0e0f",
+               "r1kh 02:00:00:00:04:00 00:01:02:03:04:06 2q0102030405060708090a0b0c0d0e0f",
+               "roaming_consortium 1",
+               "roaming_consortium 12",
+               "roaming_consortium 112233445566778899aabbccddeeff00",
+               'venue_name P"engExample venue"',
+               'venue_name P"engExample venue',
+               "venue_name engExample venue",
+               "venue_name e:Example venue",
+               "venue_name eng1:Example venue",
+               "venue_name eng:Example venue 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
+               "anqp_3gpp_cell_net abc",
+               "anqp_3gpp_cell_net ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",
+               "anqp_3gpp_cell_net 244",
+               "anqp_3gpp_cell_net 24,123",
+               "anqp_3gpp_cell_net 244,1",
+               "anqp_3gpp_cell_net 244,1234",
+               "nai_realm 0",
+               "nai_realm 0,1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.nas1.w1.fi",
+               "nai_realm 0,example.org,1,2,3,4,5,6,7,8",
+               "nai_realm 0,example.org,1[1:1][2:2][3:3][4:4][5:5]",
+               "nai_realm 0,example.org,1[1]",
+               "nai_realm 0,example.org,1[1:1",
+               "nai_realm 0,a.example.org;b.example.org;c.example.org;d.example.org;e.example.org;f.example.org;g.example.org;h.example.org;i.example.org;j.example.org;k.example.org",
+               "qos_map_set 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60",
+               "qos_map_set 53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,255,300",
+               "qos_map_set 53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,255,-1",
+               "qos_map_set 53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,255,255,1",
+               "qos_map_set 1",
+               "qos_map_set 1,2",
+               "hs20_conn_capab 1",
+               "hs20_conn_capab 6:22",
+               "hs20_wan_metrics 0q:8000:1000:80:240:3000",
+               "hs20_wan_metrics 01",
+               "hs20_wan_metrics 01:8000",
+               "hs20_wan_metrics 01:8000:1000",
+               "hs20_wan_metrics 01:8000:1000:80",
+               "hs20_wan_metrics 01:8000:1000:80:240",
+               "hs20_oper_friendly_name eng1:Example",
+               "hs20_icon 32",
+               "hs20_icon 32:32",
+               "hs20_icon 32:32:eng",
+               "hs20_icon 32:32:eng:image/png",
+               "hs20_icon 32:32:eng:image/png:icon32",
+               "hs20_icon 32:32:eng:image/png:123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890:/tmp/icon32.png",
+               "hs20_icon 32:32:eng:image/png:name:/tmp/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.png",
+               "osu_ssid ",
+               "osu_ssid P",
+               'osu_ssid P"abc',
+               'osu_ssid "1234567890123456789012345678901234567890"',
+               "osu_friendly_name eng:Example",
+               "osu_nai anonymous@example.com",
+               "osu_method_list 1 0",
+               "osu_icon foo",
+               "osu_service_desc eng:Example services",
+               "ssid 1234567890123456789012345678901234567890",
+               "pac_opaque_encr_key 123456",
+               "eap_fast_a_id 12345",
+               "eap_fast_a_id 12345q",
+               "own_ip_addr foo",
+               "auth_server_addr foo2",
+               "auth_server_shared_secret ",
+               "acct_server_addr foo3",
+               "acct_server_shared_secret ",
+               "radius_auth_req_attr 123::",
+               "radius_acct_req_attr 123::",
+               "radius_das_client 192.168.1.123",
+               "radius_das_client 192.168.1.1a foo",
+               "auth_algs 0",
+               "max_num_sta -1",
+               "max_num_sta 1000000",
+               "wpa_passphrase 1234567",
+               "wpa_passphrase 1234567890123456789012345678901234567890123456789012345678901234",
+               "wpa_psk 1234567890123456789012345678901234567890123456789012345678901234a",
+               "wpa_psk 12345678901234567890123456789012345678901234567890123456789012",
+               "wpa_psk_radius 123",
+               "wpa_pairwise NONE",
+               "wpa_pairwise WEP40",
+               "wpa_pairwise WEP104",
+               "rsn_pairwise NONE",
+               "rsn_pairwise WEP40",
+               "rsn_pairwise WEP104",
+               "mobility_domain 01",
+               "r1_key_holder 0011223344",
+               "ctrl_interface_group nosuchgrouphere",
+               "hw_mode foo",
+               "wps_rf_bands foo",
+               "beacon_int 0",
+               "beacon_int 65536",
+               "acs_num_scans 0",
+               "acs_num_scans 101",
+               "rts_threshold -1",
+               "rts_threshold 2348",
+               "fragm_threshold -1",
+               "fragm_threshold 2347",
+               "send_probe_response -1",
+               "send_probe_response 2",
+               "vlan_naming -1",
+               "vlan_naming 10000000",
+               "group_mgmt_cipher FOO",
+               "assoc_sa_query_max_timeout 0",
+               "assoc_sa_query_retry_timeout 0",
+               "wps_state -1",
+               "wps_state 3",
+               "uuid FOO",
+               "device_name 1234567890123456789012345678901234567890",
+               "manufacturer 1234567890123456789012345678901234567890123456789012345678901234567890",
+               "model_name 1234567890123456789012345678901234567890",
+               "model_number 1234567890123456789012345678901234567890",
+               "serial_number 1234567890123456789012345678901234567890",
+               "device_type FOO",
+               "os_version 1",
+               "ap_settings /tmp/does/not/exist/ap-settings.foo",
+               "wps_nfc_dev_pw_id 4",
+               "wps_nfc_dev_pw_id 100000",
+               "time_zone A",
+               "access_network_type -1",
+               "access_network_type 16",
+               "hessid 00:11:22:33:44",
+               "network_auth_type 0q",
+               "ipaddr_type_availability 1q",
+               "hs20_operating_class 0",
+               "hs20_operating_class 0q",
+               "bss_load_test ",
+               "bss_load_test 12",
+               "bss_load_test 12:80",
+               "vendor_elements 0",
+               "vendor_elements 0q",
+               "local_pwr_constraint -1",
+               "local_pwr_constraint 256",
+               "wmm_ac_bk_cwmin -1",
+               "wmm_ac_be_cwmin 16",
+               "wmm_ac_vi_cwmax -1",
+               "wmm_ac_vo_cwmax 16",
+               "wmm_ac_foo_cwmax 6",
+               "wmm_ac_bk_aifs 0",
+               "wmm_ac_bk_aifs 256",
+               "wmm_ac_bk_txop_limit -1",
+               "wmm_ac_bk_txop_limit 65536",
+               "wmm_ac_bk_acm -1",
+               "wmm_ac_bk_acm 2",
+               "wmm_ac_bk_foo 2",
+               "tx_queue_foo_aifs 3",
+               "tx_queue_data3_cwmin 4",
+               "tx_queue_data3_cwmax 4",
+               "tx_queue_data3_aifs -4",
+               "tx_queue_data3_foo 1" ]
+    for e in errors:
+        if "FAIL" not in hapd.request("SET " + e):
+            raise Exception("Unexpected SET success: '%s'" % e)
+
+    if "OK" not in hapd.request("SET osu_server_uri https://example.com/"):
+        raise Exception("Unexpected SET osu_server_uri failure")
+    if "OK" not in hapd.request("SET osu_friendly_name eng:Example"):
+        raise Exception("Unexpected SET osu_friendly_name failure")
+
+    errors = [ "osu_friendly_name eng1:Example",
+               "osu_service_desc eng1:Example services" ]
+    for e in errors:
+        if "FAIL" not in hapd.request("SET " + e):
+            raise Exception("Unexpected SET success: '%s'" % e)
+
+    no_err = [ "wps_nfc_dh_pubkey 0",
+               "wps_nfc_dh_privkey 0q",
+               "wps_nfc_dev_pw 012",
+               "manage_p2p 0",
+               "disassoc_low_ack 0",
+               "network_auth_type 01",
+               "tdls_prohibit 0",
+               "tdls_prohibit_chan_switch 0" ]
+    for e in no_err:
+        if "OK" not in hapd.request("SET " + e):
+            raise Exception("Unexpected SET failure: '%s'" % e)
+
+def test_hapd_ctrl_global(dev, apdev):
+    """hostapd and GET ctrl_iface command"""
+    ssid = "hapd-ctrl"
+    params = { "ssid": ssid }
+    ifname = apdev[0]['ifname']
+    hapd = hostapd.add_ap(ifname, params)
+    hapd_global = hostapd.HostapdGlobal()
+    res = hapd_global.request("IFNAME=" + ifname + " PING")
+    if "PONG" not in res:
+            raise Exception("Could not ping hostapd interface " + ifname + " via global control interface")
+    res = hapd_global.request("IFNAME=" + ifname + " GET version")
+    if "FAIL" in res:
+           raise Exception("Could not get hostapd version for " + ifname + " via global control interface")
+
+def dup_network(hapd_global, src, dst, param):
+    res = hapd_global.request("DUP_NETWORK %s %s %s" % (src, dst, param))
+    if "OK" not in res:
+        raise Exception("Could not dup %s param from %s to %s" % (param, src,
+                                                                  dst))
+
+def test_hapd_dup_network_global_wpa2(dev, apdev):
+    """hostapd and DUP_NETWORK command (WPA2"""
+    passphrase="12345678"
+    src_ssid = "hapd-ctrl-src"
+    dst_ssid = "hapd-ctrl-dst"
+
+    src_params = hostapd.wpa2_params(ssid=src_ssid, passphrase=passphrase)
+    src_ifname = apdev[0]['ifname']
+    src_hapd = hostapd.add_ap(src_ifname, src_params)
+
+    dst_params = { "ssid": dst_ssid }
+    dst_ifname = apdev[1]['ifname']
+    dst_hapd = hostapd.add_ap(dst_ifname, dst_params, no_enable=True)
+
+    hapd_global = hostapd.HostapdGlobal()
+
+    for param in [ "wpa", "wpa_passphrase", "wpa_key_mgmt", "rsn_pairwise" ]:
+        dup_network(hapd_global, src_ifname, dst_ifname, param)
+
+    dst_hapd.enable()
+
+    dev[0].connect(dst_ssid, psk=passphrase, proto="RSN", pairwise="CCMP",
+                   scan_freq="2412")
+    addr = dev[0].own_addr()
+    if "FAIL" in dst_hapd.request("STA " + addr):
+            raise Exception("Could not connect using duplicated wpa params")
+
+def test_hapd_dup_network_global_wpa(dev, apdev):
+    """hostapd and DUP_NETWORK command (WPA)"""
+    skip_with_fips(dev[0])
+    psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+    src_ssid = "hapd-ctrl-src"
+    dst_ssid = "hapd-ctrl-dst"
+
+    src_params = hostapd.wpa_params(ssid=src_ssid)
+    src_params['wpa_psk'] = psk
+    src_ifname = apdev[0]['ifname']
+    src_hapd = hostapd.add_ap(src_ifname, src_params)
+
+    dst_params = { "ssid": dst_ssid }
+    dst_ifname = apdev[1]['ifname']
+    dst_hapd = hostapd.add_ap(dst_ifname, dst_params, no_enable=True)
+
+    hapd_global = hostapd.HostapdGlobal()
+
+    for param in [ "wpa", "wpa_psk", "wpa_key_mgmt", "wpa_pairwise" ]:
+        dup_network(hapd_global, src_ifname, dst_ifname, param)
+
+    dst_hapd.enable()
+
+    dev[0].connect(dst_ssid, raw_psk=psk, proto="WPA", pairwise="TKIP",
+                   scan_freq="2412")
+    addr = dev[0].own_addr()
+    if "FAIL" in dst_hapd.request("STA " + addr):
+            raise Exception("Could not connect using duplicated wpa params")
+
+def test_hapd_ctrl_log_level(dev, apdev):
+    """hostapd ctrl_iface LOG_LEVEL"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    level = hapd.request("LOG_LEVEL")
+    if "Current level: MSGDUMP" not in level:
+        raise Exception("Unexpected debug level(1): " + level)
+    if "Timestamp: 1" not in level:
+        raise Exception("Unexpected timestamp(1): " + level)
+
+    if "OK" not in hapd.request("LOG_LEVEL  MSGDUMP  0"):
+        raise Exception("LOG_LEVEL failed")
+    level = hapd.request("LOG_LEVEL")
+    if "Current level: MSGDUMP" not in level:
+        raise Exception("Unexpected debug level(2): " + level)
+    if "Timestamp: 0" not in level:
+        raise Exception("Unexpected timestamp(2): " + level)
+
+    if "OK" not in hapd.request("LOG_LEVEL  MSGDUMP  1"):
+        raise Exception("LOG_LEVEL failed")
+    level = hapd.request("LOG_LEVEL")
+    if "Current level: MSGDUMP" not in level:
+        raise Exception("Unexpected debug level(3): " + level)
+    if "Timestamp: 1" not in level:
+        raise Exception("Unexpected timestamp(3): " + level)
+
+    if "FAIL" not in hapd.request("LOG_LEVEL FOO"):
+        raise Exception("Invalid LOG_LEVEL accepted")
+
+    for lev in [ "EXCESSIVE", "MSGDUMP", "DEBUG", "INFO", "WARNING", "ERROR" ]:
+        if "OK" not in hapd.request("LOG_LEVEL " + lev):
+            raise Exception("LOG_LEVEL failed for " + lev)
+        level = hapd.request("LOG_LEVEL")
+        if "Current level: " + lev not in level:
+            raise Exception("Unexpected debug level: " + level)
+
+    if "OK" not in hapd.request("LOG_LEVEL  MSGDUMP  1"):
+        raise Exception("LOG_LEVEL failed")
+    level = hapd.request("LOG_LEVEL")
+    if "Current level: MSGDUMP" not in level:
+        raise Exception("Unexpected debug level(3): " + level)
+    if "Timestamp: 1" not in level:
+        raise Exception("Unexpected timestamp(3): " + level)
diff --git a/hostap/tests/hwsim/test_hostapd_oom.py b/hostap/tests/hwsim/test_hostapd_oom.py
new file mode 100644
index 0000000..cb91225
--- /dev/null
+++ b/hostap/tests/hwsim/test_hostapd_oom.py
@@ -0,0 +1,155 @@
+# hostapd and out-of-memory error paths
+# Copyright (c) 2015, Jouni Malinen
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+
+import hostapd
+from utils import HwsimSkip
+
+def hostapd_oom_loop(apdev, params, start_func="main"):
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "ctrl" })
+    hapd_global = hostapd.HostapdGlobal()
+
+    count = 0
+    for i in range(1, 1000):
+        if "OK" not in hapd.request("TEST_ALLOC_FAIL %d:%s" % (i, start_func)):
+            raise HwsimSkip("TEST_ALLOC_FAIL not supported")
+        try:
+            hostapd.add_ap(apdev[1]['ifname'], params)
+            logger.info("Iteration %d - success" % i)
+            hapd_global.remove(apdev[1]['ifname'])
+
+            state = hapd.request('GET_ALLOC_FAIL')
+            logger.info("GET_ALLOC_FAIL: " + state)
+            hapd.request("TEST_ALLOC_FAIL 0:")
+            if i < 3:
+                raise Exception("AP setup succeeded during out-of-memory")
+            if state.startswith('0:'):
+                count = 0
+            else:
+                count += 1
+                if count == 5:
+                    break
+        except Exception, e:
+            logger.info("Iteration %d - %s" % (i, str(e)))
+
+def test_hostapd_oom_open(dev, apdev):
+    """hostapd failing to setup open mode due to OOM"""
+    params = { "ssid": "open" }
+    hostapd_oom_loop(apdev, params)
+
+def test_hostapd_oom_wpa2_psk(dev, apdev):
+    """hostapd failing to setup WPA2-PSK mode due to OOM"""
+    params = hostapd.wpa2_params(ssid="test", passphrase="12345678")
+    params['wpa_psk_file'] = 'hostapd.wpa_psk'
+    hostapd_oom_loop(apdev, params)
+
+def test_hostapd_oom_wpa2_eap(dev, apdev):
+    """hostapd failing to setup WPA2-EAP mode due to OOM"""
+    params = hostapd.wpa2_eap_params(ssid="test")
+    params['acct_server_addr'] = "127.0.0.1"
+    params['acct_server_port'] = "1813"
+    params['acct_server_shared_secret'] = "radius"
+    hostapd_oom_loop(apdev, params)
+
+def test_hostapd_oom_wpa2_eap_radius(dev, apdev):
+    """hostapd failing to setup WPA2-EAP mode due to OOM in RADIUS"""
+    params = hostapd.wpa2_eap_params(ssid="test")
+    params['acct_server_addr'] = "127.0.0.1"
+    params['acct_server_port'] = "1813"
+    params['acct_server_shared_secret'] = "radius"
+    hostapd_oom_loop(apdev, params, start_func="accounting_init")
+
+def test_hostapd_oom_wpa2_psk_connect(dev, apdev):
+    """hostapd failing during WPA2-PSK mode connection due to OOM"""
+    params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SCAN_INTERVAL 1")
+    count = 0
+    for i in range(1, 1000):
+        logger.info("Iteration %d" % i)
+        if "OK" not in hapd.request("TEST_ALLOC_FAIL %d:main" % i):
+            raise HwsimSkip("TEST_ALLOC_FAIL not supported")
+        id = dev[0].connect("test-wpa2-psk", psk="12345678",
+                            scan_freq="2412", wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+                                "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=5)
+        if ev is None:
+            logger.info("Timeout while waiting for connection in iteration %d" % i)
+            dev[0].request("REMOVE_NETWORK all")
+            time.sleep(0.1)
+        else:
+            if "CTRL-EVENT-SSID-TEMP-DISABLED" in ev:
+                logger.info("Re-select to avoid long wait for temp disavle")
+                dev[0].select_network(id)
+                dev[0].wait_connected()
+            dev[0].request("REMOVE_NETWORK all")
+            dev[0].wait_disconnected()
+        for i in range(3):
+            dev[i].dump_monitor()
+        hapd.dump_monitor()
+
+        state = hapd.request('GET_ALLOC_FAIL')
+        logger.info("GET_ALLOC_FAIL: " + state)
+        hapd.request("TEST_ALLOC_FAIL 0:")
+        if state.startswith('0:'):
+            count = 0
+        else:
+            count += 1
+            if count == 5:
+                break
+    dev[0].request("SCAN_INTERVAL 5")
+
+def test_hostapd_oom_wpa2_eap_connect(dev, apdev, params):
+    """hostapd failing during WPA2-EAP mode connection due to OOM"""
+    if not params['long']:
+        raise HwsimSkip("Skip test case with long duration due to --long not specified")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params['acct_server_addr'] = "127.0.0.1"
+    params['acct_server_port'] = "1813"
+    params['acct_server_shared_secret'] = "radius"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SCAN_INTERVAL 1")
+    count = 0
+    for i in range(1, 1000):
+        logger.info("Iteration %d" % i)
+        if "OK" not in hapd.request("TEST_ALLOC_FAIL %d:main" % i):
+            raise HwsimSkip("TEST_ALLOC_FAIL not supported")
+        id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                            eap="GPSK", identity="gpsk user",
+                            password="abcdefghijklmnop0123456789abcdef",
+                            scan_freq="2412", wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+                                "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=5)
+        if ev is None:
+            logger.info("Timeout while waiting for connection in iteration %d" % i)
+            dev[0].request("REMOVE_NETWORK all")
+            time.sleep(0.1)
+        else:
+            if "CTRL-EVENT-SSID-TEMP-DISABLED" in ev:
+                logger.info("Re-select to avoid long wait for temp disavle")
+                dev[0].select_network(id)
+                dev[0].wait_connected()
+            dev[0].request("REMOVE_NETWORK all")
+            dev[0].wait_disconnected()
+        for i in range(3):
+            dev[i].dump_monitor()
+        hapd.dump_monitor()
+
+        state = hapd.request('GET_ALLOC_FAIL')
+        logger.info("GET_ALLOC_FAIL: " + state)
+        hapd.request("TEST_ALLOC_FAIL 0:")
+        if state.startswith('0:'):
+            count = 0
+        else:
+            count += 1
+            if count == 5:
+                break
+    dev[0].request("SCAN_INTERVAL 5")
diff --git a/hostap/tests/hwsim/test_ibss.py b/hostap/tests/hwsim/test_ibss.py
new file mode 100644
index 0000000..59faa8c
--- /dev/null
+++ b/hostap/tests/hwsim/test_ibss.py
@@ -0,0 +1,397 @@
+# IBSS test cases
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+import re
+import subprocess
+
+import hwsim_utils
+from utils import alloc_fail
+
+def connect_ibss_cmd(dev, id, freq=2412):
+    dev.dump_monitor()
+    dev.select_network(id, freq=str(freq))
+
+def wait_ibss_connection(dev):
+    logger.info(dev.ifname + " waiting for IBSS start/join to complete")
+    ev = dev.wait_connected(timeout=20,
+                            error="Connection to the IBSS timed out")
+    exp = r'<.>(CTRL-EVENT-CONNECTED) - Connection to ([0-9a-f:]*) completed.*'
+    s = re.split(exp, ev)
+    if len(s) < 3:
+        return None
+    return s[2]
+
+def wait_4way_handshake(dev1, dev2):
+    logger.info(dev1.ifname + " waiting for 4-way handshake completion with " + dev2.ifname + " " + dev2.p2p_interface_addr())
+    ev = dev1.wait_event(["IBSS-RSN-COMPLETED " + dev2.p2p_interface_addr()],
+                         timeout=20)
+    if ev is None:
+        raise Exception("4-way handshake in IBSS timed out")
+
+def wait_4way_handshake2(dev1, dev2, dev3):
+    logger.info(dev1.ifname + " waiting for 4-way handshake completion with " + dev2.ifname + " " + dev2.p2p_interface_addr() + " and " + dev3.p2p_interface_addr())
+    ev = dev1.wait_event(["IBSS-RSN-COMPLETED " + dev2.p2p_interface_addr(),
+                          "IBSS-RSN-COMPLETED " + dev3.p2p_interface_addr()],
+                         timeout=20)
+    if ev is None:
+        raise Exception("4-way handshake in IBSS timed out")
+    ev = dev1.wait_event(["IBSS-RSN-COMPLETED " + dev2.p2p_interface_addr(),
+                          "IBSS-RSN-COMPLETED " + dev3.p2p_interface_addr()],
+                         timeout=20)
+    if ev is None:
+        raise Exception("4-way handshake in IBSS timed out")
+
+def add_ibss(dev, ssid, psk=None, proto=None, key_mgmt=None, pairwise=None,
+             group=None, beacon_int=None, bssid=None, scan_freq=None,
+             wep_key0=None, freq=2412):
+    id = dev.add_network()
+    dev.set_network(id, "mode", "1")
+    dev.set_network(id, "frequency", str(freq))
+    if scan_freq:
+        dev.set_network(id, "scan_freq", str(scan_freq))
+    dev.set_network_quoted(id, "ssid", ssid)
+    if psk:
+        dev.set_network_quoted(id, "psk", psk)
+    if proto:
+        dev.set_network(id, "proto", proto)
+    if key_mgmt:
+        dev.set_network(id, "key_mgmt", key_mgmt)
+    if pairwise:
+        dev.set_network(id, "pairwise", pairwise)
+    if group:
+        dev.set_network(id, "group", group)
+    if beacon_int:
+        dev.set_network(id, "beacon_int", beacon_int)
+    if bssid:
+        dev.set_network(id, "bssid", bssid)
+    if wep_key0:
+        dev.set_network(id, "wep_key0", wep_key0)
+    dev.request("ENABLE_NETWORK " + str(id) + " no-connect")
+    return id
+
+def add_ibss_rsn(dev, ssid):
+    return add_ibss(dev, ssid, "12345678", "RSN", "WPA-PSK", "CCMP", "CCMP")
+
+def add_ibss_rsn_tkip(dev, ssid):
+    return add_ibss(dev, ssid, "12345678", "RSN", "WPA-PSK", "TKIP", "TKIP")
+
+def add_ibss_wpa_none(dev, ssid):
+    return add_ibss(dev, ssid, "12345678", "WPA", "WPA-NONE", "TKIP", "TKIP")
+
+def add_ibss_wpa_none_ccmp(dev, ssid):
+    return add_ibss(dev, ssid, "12345678", "WPA", "WPA-NONE", "CCMP", "CCMP")
+
+def test_ibss_rsn(dev):
+    """IBSS RSN"""
+    ssid="ibss-rsn"
+
+    logger.info("Start IBSS on the first STA")
+    id = add_ibss_rsn(dev[0], ssid)
+    connect_ibss_cmd(dev[0], id)
+    bssid0 = wait_ibss_connection(dev[0])
+
+    logger.info("Join two STAs to the IBSS")
+
+    id = add_ibss_rsn(dev[1], ssid)
+    connect_ibss_cmd(dev[1], id)
+    bssid1 = wait_ibss_connection(dev[1])
+    if bssid0 != bssid1:
+        logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+        # try to merge with a scan
+        dev[1].scan()
+    wait_4way_handshake(dev[0], dev[1])
+    wait_4way_handshake(dev[1], dev[0])
+
+    id = add_ibss_rsn(dev[2], ssid)
+    connect_ibss_cmd(dev[2], id)
+    bssid2 = wait_ibss_connection(dev[2])
+    if bssid0 != bssid2:
+        logger.info("STA0 BSSID " + bssid0 + " differs from STA2 BSSID " + bssid2)
+        # try to merge with a scan
+        dev[2].scan()
+    wait_4way_handshake(dev[0], dev[2])
+    wait_4way_handshake2(dev[2], dev[0], dev[1])
+
+    # Allow some time for all peers to complete key setup
+    time.sleep(3)
+    hwsim_utils.test_connectivity(dev[0], dev[1])
+    hwsim_utils.test_connectivity(dev[0], dev[2])
+    hwsim_utils.test_connectivity(dev[1], dev[2])
+
+    dev[1].request("REMOVE_NETWORK all")
+    time.sleep(1)
+    id = add_ibss_rsn(dev[1], ssid)
+    connect_ibss_cmd(dev[1], id)
+    bssid1 = wait_ibss_connection(dev[1])
+    if bssid0 != bssid1:
+        logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+        # try to merge with a scan
+        dev[1].scan()
+    wait_4way_handshake(dev[0], dev[1])
+    wait_4way_handshake(dev[1], dev[0])
+    time.sleep(3)
+    hwsim_utils.test_connectivity(dev[0], dev[1])
+
+    if "OK" not in dev[0].request("IBSS_RSN " + dev[1].p2p_interface_addr()):
+        raise Exception("IBSS_RSN command failed")
+
+def test_ibss_wpa_none(dev):
+    """IBSS WPA-None"""
+    ssid="ibss-wpa-none"
+
+    logger.info("Start IBSS on the first STA")
+    id = add_ibss_wpa_none(dev[0], ssid)
+    connect_ibss_cmd(dev[0], id)
+    bssid0 = wait_ibss_connection(dev[0])
+
+    # This is a bit ugly, but no one really cares about WPA-None, so there may
+    # not be enough justification to clean this up.. For now, wpa_supplicant
+    # will show two connection events with mac80211_hwsim where the first one
+    # comes with all zeros address.
+    if bssid0 == "00:00:00:00:00:00":
+        logger.info("Waiting for real BSSID on the first STA")
+        bssid0 = wait_ibss_connection(dev[0])
+
+    logger.info("Join two STAs to the IBSS")
+
+    id = add_ibss_wpa_none(dev[1], ssid)
+    connect_ibss_cmd(dev[1], id)
+    id = add_ibss_wpa_none(dev[2], ssid)
+    connect_ibss_cmd(dev[2], id)
+
+    bssid1 = wait_ibss_connection(dev[1])
+    if bssid0 != bssid1:
+        logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+        bssid1 = wait_ibss_connection(dev[1])
+
+    bssid2 = wait_ibss_connection(dev[2])
+    if bssid0 != bssid2:
+        logger.info("STA0 BSSID " + bssid0 + " differs from STA2 BSSID " + bssid2)
+        bssid2 = wait_ibss_connection(dev[2])
+
+    logger.info("bssid0=%s bssid1=%s bssid2=%s" % (bssid0, bssid1, bssid2))
+
+    bss = dev[0].get_bss(bssid0)
+    if not bss:
+        bss = dev[1].get_bss(bssid1)
+        if not bss:
+            raise Exception("Could not find BSS entry for IBSS")
+    if 'flags' not in bss:
+        raise Exception("Could not get BSS flags from BSS table")
+    if "[WPA-None-TKIP]" not in bss['flags']:
+        raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+    # Allow some time for all peers to complete key setup
+    time.sleep(1)
+
+    # This is supposed to work, but looks like WPA-None does not work with
+    # mac80211 currently..
+    try:
+        hwsim_utils.test_connectivity(dev[0], dev[1])
+    except Exception, e:
+        logger.info("Ignoring known connectivity failure: " + str(e))
+    try:
+        hwsim_utils.test_connectivity(dev[0], dev[2])
+    except Exception, e:
+        logger.info("Ignoring known connectivity failure: " + str(e))
+    try:
+        hwsim_utils.test_connectivity(dev[1], dev[2])
+    except Exception, e:
+        logger.info("Ignoring known connectivity failure: " + str(e))
+
+def test_ibss_wpa_none_ccmp(dev):
+    """IBSS WPA-None/CCMP"""
+    ssid="ibss-wpa-none"
+
+    logger.info("Start IBSS on the first STA")
+    id = add_ibss_wpa_none(dev[0], ssid)
+    connect_ibss_cmd(dev[0], id)
+    bssid0 = wait_ibss_connection(dev[0])
+
+    # This is a bit ugly, but no one really cares about WPA-None, so there may
+    # not be enough justification to clean this up.. For now, wpa_supplicant
+    # will show two connection events with mac80211_hwsim where the first one
+    # comes with all zeros address.
+    if bssid0 == "00:00:00:00:00:00":
+        logger.info("Waiting for real BSSID on the first STA")
+        bssid0 = wait_ibss_connection(dev[0])
+
+
+    logger.info("Join a STA to the IBSS")
+    id = add_ibss_wpa_none(dev[1], ssid)
+    connect_ibss_cmd(dev[1], id)
+
+    bssid1 = wait_ibss_connection(dev[1])
+    if bssid0 != bssid1:
+        logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+        bssid1 = wait_ibss_connection(dev[1])
+
+    logger.info("bssid0=%s bssid1=%s" % (bssid0, bssid1))
+
+    # Allow some time for all peers to complete key setup
+    time.sleep(1)
+
+    # This is supposed to work, but looks like WPA-None does not work with
+    # mac80211 currently..
+    try:
+        hwsim_utils.test_connectivity(dev[0], dev[1])
+    except Exception, e:
+        logger.info("Ignoring known connectivity failure: " + str(e))
+
+def test_ibss_open(dev):
+    """IBSS open (no security)"""
+    ssid="ibss"
+    id = add_ibss(dev[0], ssid, key_mgmt="NONE", beacon_int="150")
+    connect_ibss_cmd(dev[0], id)
+    bssid0 = wait_ibss_connection(dev[0])
+
+    id = add_ibss(dev[1], ssid, key_mgmt="NONE", beacon_int="200")
+    connect_ibss_cmd(dev[1], id)
+    bssid1 = wait_ibss_connection(dev[1])
+    if bssid0 != bssid1:
+        logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+
+    res = dev[0].request("SCAN_RESULTS")
+    if "[IBSS]" not in res:
+        res = dev[1].request("SCAN_RESULTS")
+        if "[IBSS]" not in res:
+            raise Exception("IBSS flag missing from scan results: " + res)
+    bss = dev[0].get_bss(bssid0)
+    if not bss:
+        bss = dev[1].get_bss(bssid1)
+        if not bss:
+            raise Exception("Could not find BSS entry for IBSS")
+    if 'flags' not in bss:
+        raise Exception("Could not get BSS flags from BSS table")
+    if "[IBSS]" not in bss['flags']:
+        raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+def test_ibss_open_fixed_bssid(dev):
+    """IBSS open (no security) and fixed BSSID"""
+    ssid="ibss"
+    bssid="02:11:22:33:44:55"
+    try:
+        dev[0].request("AP_SCAN 2")
+        add_ibss(dev[0], ssid, key_mgmt="NONE", bssid=bssid, beacon_int="150")
+        dev[0].request("REASSOCIATE")
+
+        dev[1].request("AP_SCAN 2")
+        add_ibss(dev[1], ssid, key_mgmt="NONE", bssid=bssid, beacon_int="200")
+        dev[1].request("REASSOCIATE")
+
+        bssid0 = wait_ibss_connection(dev[0])
+        bssid1 = wait_ibss_connection(dev[1])
+        if bssid0 != bssid:
+            raise Exception("STA0 BSSID " + bssid0 + " differs from fixed BSSID " + bssid)
+        if bssid1 != bssid:
+            raise Exception("STA0 BSSID " + bssid0 + " differs from fixed BSSID " + bssid)
+    finally:
+        dev[0].request("AP_SCAN 1")
+        dev[1].request("AP_SCAN 1")
+
+def test_ibss_open_retry(dev):
+    """IBSS open (no security) with cfg80211 retry workaround"""
+    subprocess.check_call(['iw', 'dev', dev[0].ifname, 'set', 'type', 'adhoc'])
+    subprocess.check_call(['iw', 'dev', dev[0].ifname, 'ibss', 'join',
+                           'ibss-test', '2412', 'HT20', 'fixed-freq',
+                           '02:22:33:44:55:66'])
+    ssid="ibss"
+    try:
+        dev[0].request("AP_SCAN 2")
+        id = add_ibss(dev[0], ssid, key_mgmt="NONE", beacon_int="150",
+                      bssid="02:33:44:55:66:77", scan_freq=2412)
+        #connect_ibss_cmd(dev[0], id)
+        dev[0].request("REASSOCIATE")
+        bssid0 = wait_ibss_connection(dev[0])
+
+        subprocess.check_call(['iw', 'dev', dev[0].ifname, 'ibss', 'leave'])
+        time.sleep(1)
+        dev[0].request("DISCONNECT")
+    finally:
+        dev[0].request("AP_SCAN 1")
+
+def test_ibss_rsn_tkip(dev):
+    """IBSS RSN with TKIP as the cipher"""
+    ssid="ibss-rsn-tkip"
+
+    id = add_ibss_rsn_tkip(dev[0], ssid)
+    connect_ibss_cmd(dev[0], id)
+    bssid0 = wait_ibss_connection(dev[0])
+
+    id = add_ibss_rsn_tkip(dev[1], ssid)
+    connect_ibss_cmd(dev[1], id)
+    bssid1 = wait_ibss_connection(dev[1])
+    if bssid0 != bssid1:
+        logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+        # try to merge with a scan
+        dev[1].scan()
+    wait_4way_handshake(dev[0], dev[1])
+    wait_4way_handshake(dev[1], dev[0])
+
+def test_ibss_wep(dev):
+    """IBSS with WEP"""
+    ssid="ibss-wep"
+
+    id = add_ibss(dev[0], ssid, key_mgmt="NONE", wep_key0='"hello"')
+    connect_ibss_cmd(dev[0], id)
+    bssid0 = wait_ibss_connection(dev[0])
+
+    id = add_ibss(dev[1], ssid, key_mgmt="NONE", wep_key0='"hello"')
+    connect_ibss_cmd(dev[1], id)
+    bssid1 = wait_ibss_connection(dev[1])
+
+def test_ibss_rsn_error_case(dev):
+    """IBSS RSN regression test for IBSS_RSN prior IBSS setup"""
+    if "FAIL" not in dev[0].request("IBSS_RSN 02:03:04:05:06:07"):
+        raise Exception("Unexpected IBSS_RSN result")
+
+def test_ibss_5ghz(dev):
+    """IBSS on 5 GHz band"""
+    try:
+        _test_ibss_5ghz(dev)
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+        dev[1].flush_scan_cache()
+
+def _test_ibss_5ghz(dev):
+    subprocess.call(['iw', 'reg', 'set', 'US'])
+    for i in range(2):
+        for j in range(5):
+            ev = dev[i].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
+            if ev is None:
+                raise Exception("No regdom change event")
+            if "alpha2=US" in ev:
+                break
+        dev[i].dump_monitor()
+
+    ssid="ibss"
+    id = add_ibss(dev[0], ssid, key_mgmt="NONE", beacon_int="150", freq=5180)
+    connect_ibss_cmd(dev[0], id, freq=5180)
+    bssid0 = wait_ibss_connection(dev[0])
+
+    dev[1].scan_for_bss(bssid0, freq=5180)
+    id = add_ibss(dev[1], ssid, key_mgmt="NONE", beacon_int="200", freq=5180)
+    connect_ibss_cmd(dev[1], id, freq=5180)
+    bssid1 = wait_ibss_connection(dev[1])
+    if bssid0 != bssid1:
+        logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+
+    dev[0].request("DISCONNECT")
+    dev[1].request("DISCONNECT")
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+
+def test_ibss_rsn_oom(dev):
+    """IBSS RSN OOM during wpa_init"""
+    with alloc_fail(dev[0], 1, "wpa_init"):
+        ssid="ibss-rsn"
+        id = add_ibss_rsn(dev[0], ssid)
+        connect_ibss_cmd(dev[0], id)
+        bssid0 = wait_ibss_connection(dev[0])
diff --git a/hostap/tests/hwsim/test_ieee8021x.py b/hostap/tests/hwsim/test_ieee8021x.py
new file mode 100644
index 0000000..d35a4d4
--- /dev/null
+++ b/hostap/tests/hwsim/test_ieee8021x.py
@@ -0,0 +1,393 @@
+# IEEE 802.1X tests
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import hmac
+import logging
+import time
+
+import hostapd
+import hwsim_utils
+from utils import skip_with_fips
+
+logger = logging.getLogger()
+
+def test_ieee8021x_wep104(dev, apdev):
+    """IEEE 802.1X connection using dynamic WEP104"""
+    skip_with_fips(dev[0])
+    params = hostapd.radius_params()
+    params["ssid"] = "ieee8021x-wep"
+    params["ieee8021x"] = "1"
+    params["wep_key_len_broadcast"] = "13"
+    params["wep_key_len_unicast"] = "13"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X", eap="PSK",
+                   identity="psk.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ieee8021x_wep40(dev, apdev):
+    """IEEE 802.1X connection using dynamic WEP40"""
+    skip_with_fips(dev[0])
+    params = hostapd.radius_params()
+    params["ssid"] = "ieee8021x-wep"
+    params["ieee8021x"] = "1"
+    params["wep_key_len_broadcast"] = "5"
+    params["wep_key_len_unicast"] = "5"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X", eap="PSK",
+                   identity="psk.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ieee8021x_open(dev, apdev):
+    """IEEE 802.1X connection using open network"""
+    params = hostapd.radius_params()
+    params["ssid"] = "ieee8021x-open"
+    params["ieee8021x"] = "1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    id = dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+                        eap="PSK", identity="psk.user@example.com",
+                        password_hex="0123456789abcdef0123456789abcdef",
+                        scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+    logger.info("Test EAPOL-Logoff")
+    dev[0].request("LOGOFF")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"])
+    if ev is None:
+        raise Exception("Did not get disconnected")
+    if "reason=23" not in ev:
+        raise Exception("Unexpected disconnection reason")
+
+    dev[0].request("LOGON")
+    dev[0].connect_network(id)
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ieee8021x_static_wep40(dev, apdev):
+    """IEEE 802.1X connection using static WEP40"""
+    params = hostapd.radius_params()
+    params["ssid"] = "ieee8021x-wep"
+    params["ieee8021x"] = "1"
+    params["wep_key0"] = '"hello"'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X", eap="PSK",
+                   identity="psk.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   wep_key0='"hello"', eapol_flags="0",
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ieee8021x_proto(dev, apdev):
+    """IEEE 802.1X and EAPOL supplicant protocol testing"""
+    params = hostapd.radius_params()
+    params["ssid"] = "ieee8021x-open"
+    params["ieee8021x"] = "1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    dev[1].request("SET ext_eapol_frame_io 1")
+    dev[1].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+                   eap="PSK", identity="psk.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412", wait_connect=False)
+    id = dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+                        eap="PSK", identity="psk.user@example.com",
+                        password_hex="0123456789abcdef0123456789abcdef",
+                        scan_freq="2412")
+    ev = dev[1].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+
+    start = dev[0].get_mib()
+
+    tests = [ "11",
+              "11223344",
+              "020000050a93000501",
+              "020300050a93000501",
+              "0203002c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+              "0203002c0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+              "0203002c0100050000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+              "02aa00050a93000501" ]
+    for frame in tests:
+        res = dev[0].request("EAPOL_RX " + bssid + " " + frame)
+        if "OK" not in res:
+            raise Exception("EAPOL_RX to wpa_supplicant failed")
+        dev[1].request("EAPOL_RX " + bssid + " " + frame)
+
+    stop = dev[0].get_mib()
+
+    logger.info("MIB before test frames: " + str(start))
+    logger.info("MIB after test frames: " + str(stop))
+
+    vals = [ 'dot1xSuppInvalidEapolFramesRx',
+             'dot1xSuppEapLengthErrorFramesRx' ]
+    for val in vals:
+        if int(stop[val]) <= int(start[val]):
+            raise Exception(val + " did not increase")
+
+def test_ieee8021x_eapol_start(dev, apdev):
+    """IEEE 802.1X and EAPOL-Start retransmissions"""
+    params = hostapd.radius_params()
+    params["ssid"] = "ieee8021x-open"
+    params["ieee8021x"] = "1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    addr0 = dev[0].own_addr()
+
+    hapd.set("ext_eapol_frame_io", "1")
+    try:
+        dev[0].request("SET EAPOL::startPeriod 1")
+        dev[0].request("SET EAPOL::maxStart 1")
+        dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+                       eap="PSK", identity="psk.user@example.com",
+                       password_hex="0123456789abcdef0123456789abcdef",
+                       scan_freq="2412", wait_connect=False)
+        held = False
+        for i in range(30):
+            pae = dev[0].get_status_field('Supplicant PAE state')
+            if pae == "HELD":
+                mib = hapd.get_sta(addr0, info="eapol")
+                if mib['auth_pae_state'] != 'AUTHENTICATING':
+                    raise Exception("Unexpected Auth PAE state: " + mib['auth_pae_state'])
+                held = True
+                break
+            time.sleep(0.25)
+        if not held:
+            raise Exception("PAE state HELD not reached")
+        dev[0].wait_disconnected()
+    finally:
+        dev[0].request("SET EAPOL::startPeriod 30")
+        dev[0].request("SET EAPOL::maxStart 3")
+
+def test_ieee8021x_held(dev, apdev):
+    """IEEE 802.1X and HELD state"""
+    params = hostapd.radius_params()
+    params["ssid"] = "ieee8021x-open"
+    params["ieee8021x"] = "1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    hapd.set("ext_eapol_frame_io", "1")
+    try:
+        dev[0].request("SET EAPOL::startPeriod 1")
+        dev[0].request("SET EAPOL::maxStart 0")
+        dev[0].request("SET EAPOL::heldPeriod 1")
+        dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+                       eap="PSK", identity="psk.user@example.com",
+                       password_hex="0123456789abcdef0123456789abcdef",
+                       scan_freq="2412", wait_connect=False)
+        held = False
+        for i in range(30):
+            pae = dev[0].get_status_field('Supplicant PAE state')
+            if pae == "HELD":
+                held = True
+                break
+            time.sleep(0.25)
+        if not held:
+            raise Exception("PAE state HELD not reached")
+
+        hapd.set("ext_eapol_frame_io", "0")
+        for i in range(30):
+            pae = dev[0].get_status_field('Supplicant PAE state')
+            if pae != "HELD":
+                held = False
+                break
+            time.sleep(0.25)
+        if held:
+            raise Exception("PAE state HELD not left")
+        ev = dev[0].wait_event([ "CTRL-EVENT-CONNECTED",
+                                 "CTRL-EVENT-DISCONNECTED" ], timeout=10)
+        if ev is None:
+            raise Exception("Connection timed out")
+        if "CTRL-EVENT-DISCONNECTED" in ev:
+            raise Exception("Unexpected disconnection")
+    finally:
+        dev[0].request("SET EAPOL::startPeriod 30")
+        dev[0].request("SET EAPOL::maxStart 3")
+        dev[0].request("SET EAPOL::heldPeriod 60")
+
+def send_eapol_key(dev, bssid, signkey, frame_start, frame_end):
+    zero_sign = "00000000000000000000000000000000"
+    frame = frame_start + zero_sign + frame_end
+    hmac_obj = hmac.new(binascii.unhexlify(signkey))
+    hmac_obj.update(binascii.unhexlify(frame))
+    sign = hmac_obj.digest()
+    frame = frame_start + binascii.hexlify(sign) + frame_end
+    dev.request("EAPOL_RX " + bssid + " " + frame)
+
+def test_ieee8021x_eapol_key(dev, apdev):
+    """IEEE 802.1X connection and EAPOL-Key protocol tests"""
+    skip_with_fips(dev[0])
+    params = hostapd.radius_params()
+    params["ssid"] = "ieee8021x-wep"
+    params["ieee8021x"] = "1"
+    params["wep_key_len_broadcast"] = "5"
+    params["wep_key_len_unicast"] = "5"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X", eap="VENDOR-TEST",
+                   identity="vendor-test", scan_freq="2412")
+
+    # Hardcoded MSK from VENDOR-TEST
+    encrkey = "1111111111111111111111111111111111111111111111111111111111111111"
+    signkey = "2222222222222222222222222222222222222222222222222222222222222222"
+
+    # EAPOL-Key replay counter does not increase
+    send_eapol_key(dev[0], bssid, signkey,
+                   "02030031" + "010005" + "0000000000000000" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
+                   "1c636a30a4")
+
+    # EAPOL-Key too large Key Length field value
+    send_eapol_key(dev[0], bssid, signkey,
+                   "02030031" + "010021" + "ffffffffffffffff" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
+                   "1c636a30a4")
+
+    # EAPOL-Key too much key data
+    send_eapol_key(dev[0], bssid, signkey,
+                   "0203004d" + "010005" + "ffffffffffffffff" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
+                   33*"ff")
+
+    # EAPOL-Key too little key data
+    send_eapol_key(dev[0], bssid, signkey,
+                   "02030030" + "010005" + "ffffffffffffffff" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
+                   "1c636a30")
+
+    # EAPOL-Key with no key data and too long WEP key length
+    send_eapol_key(dev[0], bssid, signkey,
+                   "0203002c" + "010020" + "ffffffffffffffff" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
+                   "")
+
+def test_ieee8021x_reauth(dev, apdev):
+    """IEEE 802.1X and EAPOL_REAUTH request"""
+    params = hostapd.radius_params()
+    params["ssid"] = "ieee8021x-open"
+    params["ieee8021x"] = "1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+                   eap="PSK", identity="psk.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412")
+
+    hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+    if ev is None:
+        raise Exception("EAP authentication did not start")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+    if ev is None:
+        raise Exception("EAP authentication did not succeed")
+    time.sleep(0.1)
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ieee8021x_set_conf(dev, apdev):
+    """IEEE 802.1X and EAPOL_SET command"""
+    params = hostapd.radius_params()
+    params["ssid"] = "ieee8021x-open"
+    params["ieee8021x"] = "1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+                   eap="PSK", identity="psk.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412")
+
+    addr0 = dev[0].own_addr()
+    tests = [ "EAPOL_SET 1",
+              "EAPOL_SET %sfoo bar" % addr0,
+              "EAPOL_SET %s foo" % addr0,
+              "EAPOL_SET %s foo bar" % addr0,
+              "EAPOL_SET %s AdminControlledDirections bar" % addr0,
+              "EAPOL_SET %s AdminControlledPortControl bar" % addr0,
+              "EAPOL_SET %s reAuthEnabled bar" % addr0,
+              "EAPOL_SET %s KeyTransmissionEnabled bar" % addr0,
+              "EAPOL_SET 11:22:33:44:55:66 AdminControlledDirections Both" ]
+    for t in tests:
+        if "FAIL" not in hapd.request(t):
+            raise Exception("Invalid EAPOL_SET command accepted: " + t)
+
+    tests = [ ("AdminControlledDirections", "adminControlledDirections", "In"),
+              ("AdminControlledDirections", "adminControlledDirections",
+               "Both"),
+              ("quietPeriod", "quietPeriod", "13"),
+              ("serverTimeout", "serverTimeout", "7"),
+              ("reAuthPeriod", "reAuthPeriod", "1234"),
+              ("reAuthEnabled", "reAuthEnabled", "FALSE"),
+              ("reAuthEnabled", "reAuthEnabled", "TRUE"),
+              ("KeyTransmissionEnabled", "keyTxEnabled", "TRUE"),
+              ("KeyTransmissionEnabled", "keyTxEnabled", "FALSE"),
+              ("AdminControlledPortControl", "portControl", "ForceAuthorized"),
+              ("AdminControlledPortControl", "portControl",
+               "ForceUnauthorized"),
+              ("AdminControlledPortControl", "portControl", "Auto") ]
+    for param,mibparam,val in tests:
+        if "OK" not in hapd.request("EAPOL_SET %s %s %s" % (addr0, param, val)):
+            raise Exception("Failed to set %s %s" % (param, val))
+        mib = hapd.get_sta(addr0, info="eapol")
+        if mib[mibparam] != val:
+            raise Exception("Unexpected %s value: %s (expected %s)" % (param, mib[mibparam], val))
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+    if ev is None:
+        raise Exception("EAP authentication did not succeed")
+    time.sleep(0.1)
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ieee8021x_auth_awhile(dev, apdev):
+    """IEEE 802.1X and EAPOL Authenticator aWhile handling"""
+    params = hostapd.radius_params()
+    params["ssid"] = "ieee8021x-open"
+    params["ieee8021x"] = "1"
+    params['auth_server_port'] = "18129"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    addr0 = dev[0].own_addr()
+
+    params = {}
+    params['ssid'] = 'as'
+    params['beacon_int'] = '2000'
+    params['radius_server_clients'] = 'auth_serv/radius_clients.conf'
+    params['radius_server_auth_port'] = '18129'
+    params['eap_server'] = '1'
+    params['eap_user_file'] = 'auth_serv/eap_user.conf'
+    params['ca_cert'] = 'auth_serv/ca.pem'
+    params['server_cert'] = 'auth_serv/server.pem'
+    params['private_key'] = 'auth_serv/server.key'
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+                   eap="PSK", identity="psk.user@example.com",
+                   password_hex="0123456789abcdef0123456789abcdef",
+                   scan_freq="2412")
+    hapd1.disable()
+    if "OK" not in hapd.request("EAPOL_SET %s serverTimeout 1" % addr0):
+        raise Exception("Failed to set serverTimeout")
+    hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+
+    for i in range(40):
+        mib = hapd.get_sta(addr0, info="eapol")
+        val = int(mib['aWhile'])
+        if val > 0:
+            break
+        time.sleep(1)
+    if val == 0:
+        raise Exception("aWhile did not increase")
+
+    hapd.dump_monitor()
+    for i in range(40):
+        mib = hapd.get_sta(addr0, info="eapol")
+        val = int(mib['aWhile'])
+        if val < 5:
+            break
+        time.sleep(1)
+    ev = hapd.wait_event(["CTRL-EVENT-EAP-PROPOSED"], timeout=10)
+    if ev is None:
+        raise Exception("Authentication restart not seen")
diff --git a/hostap/tests/hwsim/test_module_tests.py b/hostap/tests/hwsim/test_module_tests.py
new file mode 100644
index 0000000..2e96c45
--- /dev/null
+++ b/hostap/tests/hwsim/test_module_tests.py
@@ -0,0 +1,28 @@
+# 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.
+
+import os
+import time
+
+import hostapd
+
+def test_module_wpa_supplicant(dev, apdev, params):
+    """wpa_supplicant module tests"""
+    if "OK" not in dev[0].global_request("MODULE_TESTS"):
+        raise Exception("Module tests failed")
+    # allow eloop test to complete
+    time.sleep(0.75)
+    dev[0].relog()
+    with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
+        res = f.read()
+        if "FAIL - should not have called this function" in res:
+            raise Exception("eloop test failed")
+
+def test_module_hostapd(dev):
+    """hostapd module tests"""
+    hapd_global = hostapd.HostapdGlobal()
+    if "OK" not in hapd_global.ctrl.request("MODULE_TESTS"):
+        raise Exception("Module tests failed")
diff --git a/hostap/tests/hwsim/test_monitor_interface.py b/hostap/tests/hwsim/test_monitor_interface.py
new file mode 100644
index 0000000..7af8724
--- /dev/null
+++ b/hostap/tests/hwsim/test_monitor_interface.py
@@ -0,0 +1,79 @@
+# AP mode using the older monitor interface design
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+
+import hwsim_utils
+import hostapd
+from wpasupplicant import WpaSupplicant
+
+def test_monitor_iface_open(dev, apdev):
+    """Open connection using cfg80211 monitor interface on AP"""
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="use_monitor=1")
+    id = wpas.add_network()
+    wpas.set_network(id, "mode", "2")
+    wpas.set_network_quoted(id, "ssid", "monitor-iface")
+    wpas.set_network(id, "key_mgmt", "NONE")
+    wpas.set_network(id, "frequency", "2412")
+    wpas.connect_network(id)
+
+    dev[0].connect("monitor-iface", key_mgmt="NONE", scan_freq="2412")
+
+def test_monitor_iface_wpa2_psk(dev, apdev):
+    """WPA2-PSK connection using cfg80211 monitor interface on AP"""
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="use_monitor=1")
+    id = wpas.add_network()
+    wpas.set_network(id, "mode", "2")
+    wpas.set_network_quoted(id, "ssid", "monitor-iface-wpa2")
+    wpas.set_network(id, "proto", "WPA2")
+    wpas.set_network(id, "key_mgmt", "WPA-PSK")
+    wpas.set_network_quoted(id, "psk", "12345678")
+    wpas.set_network(id, "pairwise", "CCMP")
+    wpas.set_network(id, "group", "CCMP")
+    wpas.set_network(id, "frequency", "2412")
+    wpas.connect_network(id)
+
+    dev[0].connect("monitor-iface-wpa2", psk="12345678", scan_freq="2412")
+
+def test_monitor_iface_multi_bss(dev, apdev):
+    """AP mode mmonitor interface with hostapd multi-BSS setup"""
+    params = { "ssid": "monitor-iface", "driver_params": "use_monitor=1" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    hostapd.add_bss('phy3', apdev[0]['ifname'] + '-2', 'bss-2.conf')
+    dev[0].connect("monitor-iface", key_mgmt="NONE", scan_freq="2412")
+    dev[1].connect("bss-2", key_mgmt="NONE", scan_freq="2412")
+
+def test_monitor_iface_unknown_sta(dev, apdev):
+    """AP mode monitor interface and Data frame from unknown STA"""
+    ssid = "monitor-iface-pmf"
+    passphrase = "12345678"
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+    params["ieee80211w"] = "2"
+    params['driver_params'] = "use_monitor=1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    bssid = apdev[0]['bssid']
+    addr = dev[0].p2p_interface_addr()
+    dev[0].connect(ssid, psk=passphrase, ieee80211w="2",
+                   key_mgmt="WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+    dev[0].request("DROP_SA")
+    # This protected Deauth will be ignored by the STA
+    hapd.request("DEAUTHENTICATE " + addr)
+    # But the unprotected Deauth from TX frame-from-unassoc-STA will now be
+    # processed
+    dev[0].request("DATA_TEST_CONFIG 1")
+    dev[0].request("DATA_TEST_TX " + bssid + " " + addr + " 0")
+    dev[0].request("DATA_TEST_CONFIG 0")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+    if ev is None:
+        raise Exception("No disconnection")
+    dev[0].request("DISCONNECT")
diff --git a/hostap/tests/hwsim/test_nfc_p2p.py b/hostap/tests/hwsim/test_nfc_p2p.py
new file mode 100644
index 0000000..7b5ddae
--- /dev/null
+++ b/hostap/tests/hwsim/test_nfc_p2p.py
@@ -0,0 +1,787 @@
+# P2P+NFC tests
+# Copyright (c) 2013, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import subprocess
+import logging
+logger = logging.getLogger(__name__)
+
+import hwsim_utils
+from utils import alloc_fail
+
+grpform_events = ["P2P-GROUP-STARTED",
+                  "P2P-GO-NEG-FAILURE",
+                  "P2P-GROUP-FORMATION-FAILURE",
+                  "WPS-PIN-NEEDED",
+                  "WPS-M2D",
+                  "WPS-FAIL"]
+
+def set_ip_addr_info(dev):
+    dev.global_request("SET ip_addr_go 192.168.42.1")
+    dev.global_request("SET ip_addr_mask 255.255.255.0")
+    dev.global_request("SET ip_addr_start 192.168.42.100")
+    dev.global_request("SET ip_addr_end 192.168.42.199")
+
+def check_ip_addr(res):
+    if 'ip_addr' not in res:
+        raise Exception("Did not receive IP address from GO")
+    if '192.168.42.' not in res['ip_addr']:
+        raise Exception("Unexpected IP address received from GO")
+    if 'ip_mask' not in res:
+        raise Exception("Did not receive IP address mask from GO")
+    if '255.255.255.' not in res['ip_mask']:
+        raise Exception("Unexpected IP address mask received from GO")
+    if 'go_ip_addr' not in res:
+        raise Exception("Did not receive GO IP address from GO")
+    if '192.168.42.' not in res['go_ip_addr']:
+        raise Exception("Unexpected GO IP address received from GO")
+
+def test_nfc_p2p_go_neg(dev):
+    """NFC connection handover to form a new P2P group (initiator becomes GO)"""
+    set_ip_addr_info(dev[0])
+    ip = dev[0].request("GET ip_addr_go")
+    if ip != "192.168.42.1":
+        raise Exception("Unexpected ip_addr_go returned: " + ip)
+    dev[0].global_request("SET p2p_go_intent 10")
+    logger.info("Perform NFC connection handover")
+    req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+    res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+    ev = dev[0].wait_global_event(["P2P-GROUP-STARTED",
+                                   "P2P-GO-NEG-FAILURE",
+                                   "P2P-GROUP-FORMATION-FAILURE",
+                                   "WPS-PIN-NEEDED"], timeout=15)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res0 = dev[0].group_form_result(ev)
+
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED",
+                                   "P2P-GO-NEG-FAILURE",
+                                   "P2P-GROUP-FORMATION-FAILURE",
+                                   "WPS-PIN-NEEDED"], timeout=1)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res1 = dev[1].group_form_result(ev)
+    logger.info("Group formed")
+
+    if res1['role'] != 'client' or res0['role'] != 'GO':
+        raise Exception("Unexpected roles negotiated")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    check_ip_addr(res1)
+
+def test_nfc_p2p_go_neg_ip_pool_oom(dev):
+    """NFC connection handover to form a new P2P group and IP pool OOM"""
+    set_ip_addr_info(dev[0])
+    ip = dev[0].request("GET ip_addr_go")
+    if ip != "192.168.42.1":
+        raise Exception("Unexpected ip_addr_go returned: " + ip)
+    dev[0].global_request("SET p2p_go_intent 10")
+    logger.info("Perform NFC connection handover")
+    req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+
+    with alloc_fail(dev[0], 1, "bitfield_alloc;wpa_init"):
+        res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+        if "FAIL" in res:
+            raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+        res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+        if "FAIL" in res:
+            raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+        ev = dev[0].wait_global_event(["P2P-GROUP-STARTED",
+                                       "P2P-GO-NEG-FAILURE",
+                                       "P2P-GROUP-FORMATION-FAILURE",
+                                       "WPS-PIN-NEEDED"], timeout=15)
+        if ev is None:
+            raise Exception("Group formation timed out")
+        res0 = dev[0].group_form_result(ev)
+
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED",
+                                   "P2P-GO-NEG-FAILURE",
+                                   "P2P-GROUP-FORMATION-FAILURE",
+                                   "WPS-PIN-NEEDED"], timeout=1)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res1 = dev[1].group_form_result(ev)
+    logger.info("Group formed")
+
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    if 'ip_addr' in res1:
+        raise Exception("Unexpectedly received IP address from GO")
+
+def test_nfc_p2p_go_neg_reverse(dev):
+    """NFC connection handover to form a new P2P group (responder becomes GO)"""
+    set_ip_addr_info(dev[1])
+    dev[0].global_request("SET p2p_go_intent 3")
+    logger.info("Perform NFC connection handover")
+    req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+    res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+    ev = dev[0].wait_global_event(["P2P-GROUP-STARTED",
+                                   "P2P-GO-NEG-FAILURE",
+                                   "P2P-GROUP-FORMATION-FAILURE",
+                                   "WPS-PIN-NEEDED"], timeout=15)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res0 = dev[0].group_form_result(ev)
+
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED",
+                                   "P2P-GO-NEG-FAILURE",
+                                   "P2P-GROUP-FORMATION-FAILURE",
+                                   "WPS-PIN-NEEDED"], timeout=1)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res1 = dev[1].group_form_result(ev)
+    logger.info("Group formed")
+
+    if res0['role'] != 'client' or res1['role'] != 'GO':
+        raise Exception("Unexpected roles negotiated")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    check_ip_addr(res0)
+
+def test_nfc_p2p_initiator_go(dev):
+    """NFC connection handover with initiator already GO"""
+    set_ip_addr_info(dev[0])
+    logger.info("Start autonomous GO")
+    dev[0].p2p_start_go()
+    logger.info("Perform NFC connection handover")
+    req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+    res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+    if ev is None:
+        raise Exception("Connection to the group timed out")
+    res1 = dev[1].group_form_result(ev)
+    if res1['result'] != 'success':
+        raise Exception("Unexpected connection failure")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    check_ip_addr(res1)
+
+def test_nfc_p2p_responder_go(dev):
+    """NFC connection handover with responder already GO"""
+    set_ip_addr_info(dev[1])
+    logger.info("Start autonomous GO")
+    dev[1].p2p_start_go()
+    logger.info("Perform NFC connection handover")
+    req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+    res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+    ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+    if ev is None:
+        raise Exception("Connection to the group timed out")
+    res0 = dev[0].group_form_result(ev)
+    if res0['result'] != 'success':
+        raise Exception("Unexpected connection failure")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    check_ip_addr(res0)
+
+def test_nfc_p2p_both_go(dev):
+    """NFC connection handover with both devices already GOs"""
+    set_ip_addr_info(dev[0])
+    set_ip_addr_info(dev[1])
+    logger.info("Start autonomous GOs")
+    dev[0].p2p_start_go()
+    dev[1].p2p_start_go()
+    logger.info("Perform NFC connection handover")
+    req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = dev[1].request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    res = dev[1].request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+    res = dev[0].request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+    ev = dev[0].wait_event(["P2P-NFC-BOTH-GO"], timeout=15)
+    if ev is None:
+        raise Exception("Time out waiting for P2P-NFC-BOTH-GO (dev0)")
+    ev = dev[1].wait_event(["P2P-NFC-BOTH-GO"], timeout=1)
+    if ev is None:
+        raise Exception("Time out waiting for P2P-NFC-BOTH-GO (dev1)")
+    dev[0].remove_group()
+    dev[1].remove_group()
+
+def test_nfc_p2p_client(dev):
+    """NFC connection handover when one device is P2P client"""
+    logger.info("Start autonomous GOs")
+    dev[0].p2p_start_go()
+    logger.info("Connect one device as a P2P client")
+    pin = dev[1].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60)
+    logger.info("Client connected")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+
+    logger.info("NFC connection handover between P2P client and P2P device")
+    req = dev[1].request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = dev[2].request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    dev[1].dump_monitor()
+    dev[2].dump_monitor()
+    res = dev[2].request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+    res = dev[1].request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+    ev = dev[1].wait_event(["P2P-NFC-WHILE-CLIENT"], timeout=15)
+    if ev is None:
+        raise Exception("Time out waiting for P2P-NFC-WHILE-CLIENT")
+    ev = dev[2].wait_event(["P2P-NFC-PEER-CLIENT"], timeout=1)
+    if ev is None:
+        raise Exception("Time out waiting for P2P-NFC-PEER-CLIENT")
+
+    logger.info("Connect to group based on upper layer trigger")
+    pin = dev[2].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    dev[2].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60)
+    logger.info("Client connected")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+    dev[2].remove_group()
+    dev[1].remove_group()
+    dev[0].remove_group()
+
+def test_nfc_p2p_static_handover_tagdev_client(dev):
+    """NFC static handover to form a new P2P group (NFC Tag device becomes P2P Client)"""
+
+    set_ip_addr_info(dev[0])
+
+    logger.info("Perform NFC connection handover")
+
+    res = dev[1].global_request("SET p2p_listen_reg_class 81")
+    res2 = dev[1].global_request("SET p2p_listen_channel 6")
+    if "FAIL" in res or "FAIL" in res2:
+        raise Exception("Could not set Listen channel")
+    pw = dev[1].global_request("WPS_NFC_TOKEN NDEF").rstrip()
+    if "FAIL" in pw:
+        raise Exception("Failed to generate password token")
+    res = dev[1].global_request("P2P_SET nfc_tag 1").rstrip()
+    if "FAIL" in res:
+        raise Exception("Failed to enable NFC Tag for P2P static handover")
+    sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    res = dev[1].global_request("P2P_LISTEN")
+    if "FAIL" in res:
+        raise Exception("Failed to start Listen mode")
+    dev[1].dump_monitor()
+
+    dev[0].dump_monitor()
+    dev[0].global_request("SET p2p_go_intent 10")
+    res = dev[0].global_request("WPS_NFC_TAG_READ " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+    ev = dev[0].wait_global_event(grpform_events, timeout=15)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res0 = dev[0].group_form_result(ev)
+
+    ev = dev[1].wait_global_event(grpform_events, timeout=1)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res1 = dev[1].group_form_result(ev)
+    logger.info("Group formed")
+
+    if res1['role'] != 'client' or res0['role'] != 'GO':
+        raise Exception("Unexpected roles negotiated")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    check_ip_addr(res1)
+
+def test_nfc_p2p_static_handover_tagdev_client_group_iface(dev):
+    """NFC static handover to form a new P2P group (NFC Tag device becomes P2P Client with group iface)"""
+
+    set_ip_addr_info(dev[0])
+
+    logger.info("Perform NFC connection handover")
+
+    res = dev[1].global_request("SET p2p_listen_reg_class 81")
+    res2 = dev[1].global_request("SET p2p_listen_channel 6")
+    if "FAIL" in res or "FAIL" in res2:
+        raise Exception("Could not set Listen channel")
+    pw = dev[1].global_request("WPS_NFC_TOKEN NDEF").rstrip()
+    if "FAIL" in pw:
+        raise Exception("Failed to generate password token")
+    dev[1].global_request("SET p2p_no_group_iface 0")
+    res = dev[1].global_request("P2P_SET nfc_tag 1").rstrip()
+    if "FAIL" in res:
+        raise Exception("Failed to enable NFC Tag for P2P static handover")
+    sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    res = dev[1].global_request("P2P_LISTEN")
+    if "FAIL" in res:
+        raise Exception("Failed to start Listen mode")
+    dev[1].dump_monitor()
+
+    dev[0].dump_monitor()
+    dev[0].global_request("SET p2p_go_intent 10")
+    res = dev[0].global_request("WPS_NFC_TAG_READ " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+    ev = dev[0].wait_global_event(grpform_events, timeout=15)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res0 = dev[0].group_form_result(ev)
+
+    ev = dev[1].wait_global_event(grpform_events, timeout=1)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res1 = dev[1].group_form_result(ev)
+    logger.info("Group formed")
+
+    if res1['role'] != 'client' or res0['role'] != 'GO':
+        raise Exception("Unexpected roles negotiated")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    check_ip_addr(res1)
+
+def test_nfc_p2p_static_handover_tagdev_go(dev):
+    """NFC static handover to form a new P2P group (NFC Tag device becomes GO)"""
+
+    set_ip_addr_info(dev[1])
+
+    logger.info("Perform NFC connection handover")
+
+    res = dev[1].global_request("SET p2p_listen_reg_class 81")
+    res2 = dev[1].global_request("SET p2p_listen_channel 6")
+    if "FAIL" in res or "FAIL" in res2:
+        raise Exception("Could not set Listen channel")
+    pw = dev[1].global_request("WPS_NFC_TOKEN NDEF").rstrip()
+    if "FAIL" in pw:
+        raise Exception("Failed to generate password token")
+    res = dev[1].global_request("P2P_SET nfc_tag 1").rstrip()
+    if "FAIL" in res:
+        raise Exception("Failed to enable NFC Tag for P2P static handover")
+    sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    res = dev[1].global_request("P2P_LISTEN")
+    if "FAIL" in res:
+        raise Exception("Failed to start Listen mode")
+    dev[1].dump_monitor()
+
+    dev[0].dump_monitor()
+    dev[0].global_request("SET p2p_go_intent 3")
+    res = dev[0].global_request("WPS_NFC_TAG_READ " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+    ev = dev[0].wait_global_event(grpform_events, timeout=15)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res0 = dev[0].group_form_result(ev)
+
+    ev = dev[1].wait_global_event(grpform_events, timeout=1)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res1 = dev[1].group_form_result(ev)
+    logger.info("Group formed")
+
+    if res0['role'] != 'client' or res1['role'] != 'GO':
+        raise Exception("Unexpected roles negotiated")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    check_ip_addr(res0)
+
+def test_nfc_p2p_static_handover_tagdev_go_forced_freq(dev):
+    """NFC static handover to form a new P2P group on forced channel (NFC Tag device becomes GO)"""
+
+    set_ip_addr_info(dev[1])
+
+    logger.info("Perform NFC connection handover")
+
+    res = dev[1].global_request("SET p2p_listen_reg_class 81")
+    res2 = dev[1].global_request("SET p2p_listen_channel 6")
+    if "FAIL" in res or "FAIL" in res2:
+        raise Exception("Could not set Listen channel")
+    pw = dev[1].global_request("WPS_NFC_TOKEN NDEF").rstrip()
+    if "FAIL" in pw:
+        raise Exception("Failed to generate password token")
+    res = dev[1].global_request("P2P_SET nfc_tag 1").rstrip()
+    if "FAIL" in res:
+        raise Exception("Failed to enable NFC Tag for P2P static handover")
+    sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    res = dev[1].global_request("P2P_LISTEN")
+    if "FAIL" in res:
+        raise Exception("Failed to start Listen mode")
+    dev[1].dump_monitor()
+
+    dev[0].dump_monitor()
+    dev[0].global_request("SET p2p_go_intent 3")
+    res = dev[0].global_request("WPS_NFC_TAG_READ " + sel + " freq=2442")
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+    ev = dev[0].wait_global_event(grpform_events, timeout=15)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res0 = dev[0].group_form_result(ev)
+
+    ev = dev[1].wait_global_event(grpform_events, timeout=1)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res1 = dev[1].group_form_result(ev)
+    logger.info("Group formed")
+
+    if res0['role'] != 'client' or res1['role'] != 'GO':
+        raise Exception("Unexpected roles negotiated")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    check_ip_addr(res0)
+
+def test_nfc_p2p_static_handover_join_tagdev_go(dev):
+    """NFC static handover to join a P2P group (NFC Tag device is the GO)"""
+
+    logger.info("Start autonomous GO")
+    set_ip_addr_info(dev[0])
+    dev[0].p2p_start_go()
+
+    logger.info("Write NFC Tag on the GO")
+    pw = dev[0].request("WPS_NFC_TOKEN NDEF").rstrip()
+    if "FAIL" in pw:
+        raise Exception("Failed to generate password token")
+    res = dev[0].request("P2P_SET nfc_tag 1").rstrip()
+    if "FAIL" in res:
+        raise Exception("Failed to enable NFC Tag for P2P static handover")
+    sel = dev[0].request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+
+    logger.info("Read NFC Tag on a P2P Device to join a group")
+    res = dev[1].request("WPS_NFC_TAG_READ " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+    ev = dev[1].wait_event(grpform_events, timeout=30)
+    if ev is None:
+        raise Exception("Joining the group timed out")
+    res = dev[1].group_form_result(ev)
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    check_ip_addr(res)
+
+    logger.info("Read NFC Tag on another P2P Device to join a group")
+    res = dev[2].request("WPS_NFC_TAG_READ " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+    ev = dev[2].wait_event(grpform_events, timeout=30)
+    if ev is None:
+        raise Exception("Joining the group timed out")
+    res = dev[2].group_form_result(ev)
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[2])
+    check_ip_addr(res)
+
+def test_nfc_p2p_static_handover_join_tagdev_client(dev):
+    """NFC static handover to join a P2P group (NFC Tag device is the P2P Client)"""
+    set_ip_addr_info(dev[0])
+    logger.info("Start autonomous GO")
+    dev[0].p2p_start_go()
+
+    dev[1].global_request("SET ignore_old_scan_res 1")
+    dev[2].global_request("SET ignore_old_scan_res 1")
+
+    logger.info("Write NFC Tag on the P2P Client")
+    res = dev[1].global_request("P2P_LISTEN")
+    if "FAIL" in res:
+        raise Exception("Failed to start Listen mode")
+    pw = dev[1].global_request("WPS_NFC_TOKEN NDEF").rstrip()
+    if "FAIL" in pw:
+        raise Exception("Failed to generate password token")
+    res = dev[1].global_request("P2P_SET nfc_tag 1").rstrip()
+    if "FAIL" in res:
+        raise Exception("Failed to enable NFC Tag for P2P static handover")
+    sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+
+    logger.info("Read NFC Tag on the GO to trigger invitation")
+    res = dev[0].global_request("WPS_NFC_TAG_READ " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+    ev = dev[1].wait_global_event(grpform_events, timeout=30)
+    if ev is None:
+        raise Exception("Joining the group timed out")
+    res = dev[1].group_form_result(ev)
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    check_ip_addr(res)
+
+    logger.info("Write NFC Tag on another P2P Client")
+    res = dev[2].global_request("P2P_LISTEN")
+    if "FAIL" in res:
+        raise Exception("Failed to start Listen mode")
+    pw = dev[2].global_request("WPS_NFC_TOKEN NDEF").rstrip()
+    if "FAIL" in pw:
+        raise Exception("Failed to generate password token")
+    res = dev[2].global_request("P2P_SET nfc_tag 1").rstrip()
+    if "FAIL" in res:
+        raise Exception("Failed to enable NFC Tag for P2P static handover")
+    sel = dev[2].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+
+    logger.info("Read NFC Tag on the GO to trigger invitation")
+    res = dev[0].global_request("WPS_NFC_TAG_READ " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+    ev = dev[2].wait_global_event(grpform_events, timeout=30)
+    if ev is None:
+        raise Exception("Joining the group timed out")
+    res = dev[2].group_form_result(ev)
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[2])
+    check_ip_addr(res)
+
+def test_nfc_p2p_go_legacy_config_token(dev):
+    """NFC config token from P2P GO to legacy WPS STA"""
+    logger.info("Start autonomous GOs")
+    dev[0].p2p_start_go()
+    logger.info("Connect legacy WPS STA with configuration token")
+    conf = dev[0].group_request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip()
+    if "FAIL" in conf:
+        raise Exception("Failed to generate configuration token")
+    dev[1].dump_monitor()
+    res = dev[1].request("WPS_NFC_TAG_READ " + conf)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+    dev[1].wait_connected(timeout=15, error="Joining the group timed out")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    dev[1].request("DISCONNECT")
+    dev[0].remove_group()
+
+def test_nfc_p2p_go_legacy_handover(dev):
+    """NFC token from legacy WPS STA to P2P GO"""
+    logger.info("Start autonomous GOs")
+    dev[0].p2p_start_go()
+    logger.info("Connect legacy WPS STA with connection handover")
+    req = dev[1].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = dev[0].group_request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    res = dev[0].group_request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant (GO)")
+    dev[1].dump_monitor()
+    res = dev[1].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant (legacy STA)")
+    dev[1].wait_connected(timeout=15, error="Joining the group timed out")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    dev[1].request("DISCONNECT")
+    dev[0].remove_group()
+
+def test_nfc_p2p_ip_addr_assignment(dev):
+    """NFC connection handover and legacy station IP address assignment"""
+    set_ip_addr_info(dev[1])
+    dev[0].global_request("SET p2p_go_intent 3")
+    logger.info("Perform NFC connection handover")
+    req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+    res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+    ev = dev[0].wait_global_event(["P2P-GROUP-STARTED",
+                                   "P2P-GO-NEG-FAILURE",
+                                   "P2P-GROUP-FORMATION-FAILURE",
+                                   "WPS-PIN-NEEDED"], timeout=15)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res0 = dev[0].group_form_result(ev)
+
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED",
+                                   "P2P-GO-NEG-FAILURE",
+                                   "P2P-GROUP-FORMATION-FAILURE",
+                                   "WPS-PIN-NEEDED"], timeout=1)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res1 = dev[1].group_form_result(ev)
+    logger.info("Group formed")
+
+    if res0['role'] != 'client' or res1['role'] != 'GO':
+        raise Exception("Unexpected roles negotiated")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    check_ip_addr(res0)
+
+    logger.info("Connect legacy P2P client that does not use new IP address assignment")
+    res = dev[2].global_request("P2P_SET disable_ip_addr_req 1")
+    if "FAIL" in res:
+        raise Exception("Failed to disable IP address assignment request")
+    pin = dev[2].wps_read_pin()
+    dev[1].p2p_go_authorize_client(pin)
+    res = dev[2].p2p_connect_group(dev[1].p2p_dev_addr(), pin, timeout=60)
+    logger.info("Client connected")
+    res = dev[2].global_request("P2P_SET disable_ip_addr_req 0")
+    hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+    if 'ip_addr' in res:
+        raise Exception("Unexpected IP address assignment")
+
+def test_nfc_p2p_ip_addr_assignment2(dev):
+    """NFC connection handover and IP address assignment for two clients"""
+    set_ip_addr_info(dev[1])
+    dev[0].global_request("SET p2p_go_intent 3")
+    logger.info("Perform NFC connection handover")
+    req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+    res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+    ev = dev[0].wait_global_event(["P2P-GROUP-STARTED",
+                                   "P2P-GO-NEG-FAILURE",
+                                   "P2P-GROUP-FORMATION-FAILURE",
+                                   "WPS-PIN-NEEDED"], timeout=15)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res0 = dev[0].group_form_result(ev)
+
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED",
+                                   "P2P-GO-NEG-FAILURE",
+                                   "P2P-GROUP-FORMATION-FAILURE",
+                                   "WPS-PIN-NEEDED"], timeout=1)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    res1 = dev[1].group_form_result(ev)
+    logger.info("Group formed")
+
+    if res0['role'] != 'client' or res1['role'] != 'GO':
+        raise Exception("Unexpected roles negotiated")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    check_ip_addr(res0)
+    logger.info("Client 1 IP address: " + res0['ip_addr'])
+
+    logger.info("Connect a P2P client")
+    pin = dev[2].wps_read_pin()
+    dev[1].p2p_go_authorize_client(pin)
+    res = dev[2].p2p_connect_group(dev[1].p2p_dev_addr(), pin, timeout=60)
+    logger.info("Client connected")
+    hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+    check_ip_addr(res)
+    logger.info("Client 2 IP address: " + res['ip_addr'])
+    if res['ip_addr'] == res0['ip_addr']:
+        raise Exception("Same IP address assigned to both clients")
+
+def test_nfc_p2p_tag_enable_disable(dev):
+    """NFC tag enable/disable for P2P"""
+    if "FAIL" in dev[0].request("WPS_NFC_TOKEN NDEF").rstrip():
+        raise Exception("Failed to generate password token")
+    if "OK" not in dev[0].request("P2P_SET nfc_tag 1"):
+        raise Exception("Failed to enable NFC Tag for P2P static handover")
+    if "OK" not in dev[0].request("P2P_SET nfc_tag 0"):
+        raise Exception("Failed to disable NFC Tag for P2P static handover")
+
+    dev[0].request("SET p2p_no_group_iface 0")
+    if "OK" not in dev[0].request("P2P_SET nfc_tag 1"):
+        raise Exception("Failed to enable NFC Tag for P2P static handover")
+    if "OK" not in dev[0].request("P2P_SET nfc_tag 0"):
+        raise Exception("Failed to disable NFC Tag for P2P static handover")
+    if "OK" not in dev[0].request("P2P_SET nfc_tag 1"):
+        raise Exception("Failed to enable NFC Tag for P2P static handover")
+    if "OK" not in dev[0].request("P2P_SET nfc_tag 0"):
+        raise Exception("Failed to disable NFC Tag for P2P static handover")
+
+def test_nfc_p2p_static_handover_invalid(dev):
+    """NFC static handover with invalid contents"""
+    logger.info("Unknown OOB GO Neg channel")
+    sel = "D217A36170706C69636174696F6E2F766E642E7766612E7032700071102100012010230001201024000120102C0036C3B2ADB8D26F53CE1CB7F000BEEDA762922FF5307E87CCE484EF4B5DAD440D0A4752579767610AD1293F7A76A66B09A7C9D58A66994E103C000103104200012010470010572CF82FC95756539B16B5CFB298ABF11049000600372A000120002E02020025000D1D000200000001001108000000000000000000101100084465766963652042130600585804ff0B00"
+    if "FAIL" not in dev[0].global_request("WPS_NFC_TAG_READ " + sel):
+        raise Exception("Invalid tag contents accepted (1)")
+
+    logger.info("No OOB GO Neg channel attribute")
+    sel = "D2179A6170706C69636174696F6E2F766E642E7766612E7032700071102100012010230001201024000120102C0036C3B2ADB8D26F53CE1CB7F000BEEDA762922FF5307E87CCE484EF4B5DAD440D0A4752579767610AD1293F7A76A66B09A7C9D58A66994E103C000103104200012010470010572CF82FC95756539B16B5CFB298ABF11049000600372A000120002502020025000D1D000200000001001108000000000000000000101100084465766963652042"
+    if "FAIL" not in dev[0].global_request("WPS_NFC_TAG_READ " + sel):
+        raise Exception("Invalid tag contents accepted (2)")
+
+    logger.info("No Device Info attribute")
+    sel = "D217836170706C69636174696F6E2F766E642E7766612E7032700071102100012010230001201024000120102C0036C3B2ADB8D26F53CE1CB7F000BEEDA762922FF5307E87CCE484EF4B5DAD440D0A4752579767610AD1293F7A76A66B09A7C9D58A66994E103C000103104200012010470010572CF82FC95756539B16B5CFB298ABF11049000600372A000120000E0202002500130600585804510B00"
+    if "FAIL" not in dev[0].global_request("WPS_NFC_TAG_READ " + sel):
+        raise Exception("Invalid tag contents accepted (3)")
diff --git a/hostap/tests/hwsim/test_nfc_wps.py b/hostap/tests/hwsim/test_nfc_wps.py
new file mode 100644
index 0000000..22d599b
--- /dev/null
+++ b/hostap/tests/hwsim/test_nfc_wps.py
@@ -0,0 +1,590 @@
+# WPS+NFC tests
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import hostapd
+from utils import alloc_fail, fail_test
+
+def check_wpa2_connection(sta, ap, hapd, ssid, mixed=False):
+    status = sta.get_status()
+    if status['wpa_state'] != 'COMPLETED':
+        raise Exception("Not fully connected")
+    if status['bssid'] != ap['bssid']:
+        raise Exception("Unexpected BSSID")
+    if status['ssid'] != ssid:
+        raise Exception("Unexpected SSID")
+    if status['pairwise_cipher'] != 'CCMP':
+        raise Exception("Unexpected encryption configuration")
+    if status['group_cipher'] != 'CCMP' and not mixed:
+        raise Exception("Unexpected encryption configuration")
+    if status['key_mgmt'] != 'WPA2-PSK':
+        raise Exception("Unexpected key_mgmt")
+    hwsim_utils.test_connectivity(sta, hapd)
+
+def ap_wps_params(ssid):
+    return { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+             "wpa_passphrase": "12345678", "wpa": "2",
+             "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
+
+def test_nfc_wps_password_token_sta(dev, apdev):
+    """NFC tag with password token on the station/Enrollee"""
+    ssid = "test-wps-nfc-pw-token-conf"
+    params = ap_wps_params(ssid)
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step using password token from station")
+    wps = dev[0].request("WPS_NFC_TOKEN WPS").rstrip()
+    if "FAIL" in wps:
+        raise Exception("Failed to generate password token (WPS only)")
+    pw = dev[0].request("WPS_NFC_TOKEN NDEF").rstrip()
+    if "FAIL" in pw:
+        raise Exception("Failed to generate password token")
+    res = hapd.request("WPS_NFC_TAG_READ " + pw)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to hostapd")
+    dev[0].dump_monitor()
+    res = dev[0].request("WPS_NFC")
+    if "FAIL" in res:
+        raise Exception("Failed to start Enrollee using NFC password token")
+    dev[0].wait_connected(timeout=30)
+    check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
+
+def test_nfc_wps_config_token(dev, apdev):
+    """NFC tag with configuration token from AP"""
+    ssid = "test-wps-nfc-conf-token"
+    params = ap_wps_params(ssid)
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("NFC configuration token from AP to station")
+    conf = hapd.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip()
+    if "FAIL" in conf:
+        raise Exception("Failed to generate configuration token")
+    dev[0].dump_monitor()
+    res = dev[0].request("WPS_NFC_TAG_READ " + conf)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+    dev[0].wait_connected(timeout=15)
+    check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
+
+    with alloc_fail(hapd, 1, "wps_get_oob_cred"):
+        conf = hapd.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip()
+        if "FAIL" not in conf:
+            raise Exception("Unexpected configuration token received during OOM")
+
+def test_nfc_wps_config_token_init(dev, apdev):
+    """NFC tag with configuration token from AP with auto configuration"""
+    ssid = "test-wps-nfc-conf-token-init"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("NFC configuration token from AP to station")
+    conf = hapd.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip()
+    if "FAIL" in conf:
+        raise Exception("Failed to generate configuration token")
+    dev[0].dump_monitor()
+    res = dev[0].request("WPS_NFC_TAG_READ " + conf)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+    dev[0].wait_connected(timeout=15)
+    check_wpa2_connection(dev[0], apdev[0], hapd, ssid, mixed=True)
+
+def test_nfc_wps_password_token_sta_init(dev, apdev):
+    """Initial AP configuration with first WPS NFC Enrollee"""
+    ssid = "test-wps-nfc-pw-token-init"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step using password token from station")
+    pw = dev[0].request("WPS_NFC_TOKEN NDEF").rstrip()
+    if "FAIL" in pw:
+        raise Exception("Failed to generate password token")
+    res = hapd.request("WPS_NFC_TAG_READ " + pw)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to hostapd")
+    dev[0].dump_monitor()
+    res = dev[0].request("WPS_NFC")
+    if "FAIL" in res:
+        raise Exception("Failed to start Enrollee using NFC password token")
+    dev[0].wait_connected(timeout=30)
+    check_wpa2_connection(dev[0], apdev[0], hapd, ssid, mixed=True)
+
+def test_nfc_wps_password_token_ap(dev, apdev):
+    """WPS registrar configuring an AP using AP password token"""
+    ssid = "test-wps-nfc-pw-token-init"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS configuration step")
+    pw = hapd.request("WPS_NFC_TOKEN NDEF").rstrip()
+    if "FAIL" in pw:
+        raise Exception("Failed to generate password token")
+    res = hapd.request("WPS_NFC_TOKEN enable")
+    if "FAIL" in pw:
+        raise Exception("Failed to enable AP password token")
+    res = dev[0].request("WPS_NFC_TAG_READ " + pw)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+    dev[0].dump_monitor()
+    new_ssid = "test-wps-nfc-pw-token-new-ssid"
+    new_passphrase = "1234567890"
+    res = dev[0].request("WPS_REG " + apdev[0]['bssid'] + " nfc-pw " + new_ssid.encode("hex") + " WPA2PSK CCMP " + new_passphrase.encode("hex"))
+    if "FAIL" in res:
+        raise Exception("Failed to start Registrar using NFC password token")
+    dev[0].wait_connected(timeout=30)
+    check_wpa2_connection(dev[0], apdev[0], hapd, new_ssid, mixed=True)
+    if "FAIL" in hapd.request("WPS_NFC_TOKEN disable"):
+        raise Exception("Failed to disable AP password token")
+    if "FAIL" in hapd.request("WPS_NFC_TOKEN WPS"):
+        raise Exception("Unexpected WPS_NFC_TOKEN WPS failure")
+
+    with fail_test(hapd, 1, "os_get_random;wps_nfc_token_gen"):
+        if "FAIL" not in hapd.request("WPS_NFC_TOKEN WPS"):
+            raise Exception("Unexpected WPS_NFC_TOKEN success")
+    with fail_test(hapd, 2, "os_get_random;wps_nfc_token_gen"):
+        if "FAIL" not in hapd.request("WPS_NFC_TOKEN WPS"):
+            raise Exception("Unexpected WPS_NFC_TOKEN success")
+
+def test_nfc_wps_handover_init(dev, apdev):
+    """Connect to WPS AP with NFC connection handover and move to configured state"""
+    dev[0].request("SET ignore_old_scan_res 1")
+    ssid = "test-wps-nfc-handover-init"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("NFC connection handover")
+    req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to hostapd")
+    dev[0].dump_monitor()
+    res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+    dev[0].wait_connected(timeout=15)
+    check_wpa2_connection(dev[0], apdev[0], hapd, ssid, mixed=True)
+
+    with alloc_fail(hapd, 1, "wps_build_nfc_handover_sel"):
+        if "FAIL" not in hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR"):
+            raise Exception("Unexpected NFC_GET_HANDOVER_SEL success during OOM")
+
+def test_nfc_wps_handover_errors(dev, apdev):
+    """WPS AP NFC handover report error cases"""
+    ssid = "test-wps-nfc-handover"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER "):
+        raise Exception("Unexpected handover report success")
+    if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP"):
+        raise Exception("Unexpected handover report success")
+    if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS"):
+        raise Exception("Unexpected handover report success")
+    if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS 001122"):
+        raise Exception("Unexpected handover report success")
+    if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS 001122 00"):
+        raise Exception("Unexpected handover report success")
+    if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS 0 00"):
+        raise Exception("Unexpected handover report success")
+    if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS 001122 0"):
+        raise Exception("Unexpected handover report success")
+    if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS 00q122 001122"):
+        raise Exception("Unexpected handover report success")
+    if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS 001122 001q22"):
+        raise Exception("Unexpected handover report success")
+    if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP FOO 001122 00"):
+        raise Exception("Unexpected handover report success")
+
+def test_nfc_wps_handover(dev, apdev):
+    """Connect to WPS AP with NFC connection handover"""
+    ssid = "test-wps-nfc-handover"
+    params = ap_wps_params(ssid)
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("NFC connection handover")
+    req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to hostapd")
+    dev[0].dump_monitor()
+    res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+    dev[0].wait_connected(timeout=30)
+    check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
+
+def test_nfc_wps_handover_5ghz(dev, apdev):
+    """Connect to WPS AP with NFC connection handover on 5 GHz band"""
+    try:
+        ssid = "test-wps-nfc-handover"
+        params = ap_wps_params(ssid)
+        params["country_code"] = "FI"
+        params["hw_mode"] = "a"
+        params["channel"] = "36"
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        logger.info("NFC connection handover")
+        req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+        if "FAIL" in req:
+            raise Exception("Failed to generate NFC connection handover request")
+        sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+        if "FAIL" in sel:
+            raise Exception("Failed to generate NFC connection handover select")
+        res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+        if "FAIL" in res:
+            raise Exception("Failed to report NFC connection handover to to hostapd")
+        dev[0].dump_monitor()
+        res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+        if "FAIL" in res:
+            raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+        dev[0].wait_connected(timeout=30)
+        check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_nfc_wps_handover_chan14(dev, apdev):
+    """Connect to WPS AP with NFC connection handover on channel 14"""
+    try:
+        ssid = "test-wps-nfc-handover"
+        params = ap_wps_params(ssid)
+        params["country_code"] = "JP"
+        params["hw_mode"] = "b"
+        params["channel"] = "14"
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        logger.info("NFC connection handover")
+        req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+        if "FAIL" in req:
+            raise Exception("Failed to generate NFC connection handover request")
+        sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+        if "FAIL" in sel:
+            raise Exception("Failed to generate NFC connection handover select")
+        res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+        if "FAIL" in res:
+            raise Exception("Failed to report NFC connection handover to to hostapd")
+        dev[0].dump_monitor()
+        res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+        if "FAIL" in res:
+            raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+        dev[0].wait_connected(timeout=30)
+        check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_nfc_wps_handover_with_pw_token_set(dev, apdev):
+    """Connect to WPS AP with NFC connection handover (wps_nfc_* set)"""
+    ssid = "test-wps-nfc-handover2"
+    params = ap_wps_params(ssid)
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    # enable a password token (which won't be used in this test case)
+    pw = hapd.request("WPS_NFC_TOKEN NDEF").rstrip()
+    if "FAIL" in pw:
+        raise Exception("Failed to generate password token")
+    res = hapd.request("WPS_NFC_TOKEN enable")
+    if "FAIL" in pw:
+        raise Exception("Failed to enable AP password token")
+    logger.info("NFC connection handover")
+    req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to hostapd")
+    dev[0].dump_monitor()
+    res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+    dev[0].wait_connected(timeout=15)
+    check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
+
+def test_nfc_wps_handover_pk_hash_mismatch_sta(dev, apdev):
+    """WPS NFC connection handover with invalid pkhash from station (negative)"""
+    ssid = "wps-nfc-handover-pkhash-sta"
+    if "FAIL" in dev[0].request("SET wps_corrupt_pkhash 1"):
+        raise Exception("Could not enable wps_corrupt_pkhash")
+    params = ap_wps_params(ssid)
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("NFC connection handover")
+    req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to hostapd")
+    dev[0].dump_monitor()
+    res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("Timed out")
+    if "WPS-FAIL" not in ev:
+        raise Exception("Public key hash mismatch not detected")
+
+def test_nfc_wps_handover_pk_hash_mismatch_ap(dev, apdev):
+    """WPS NFC connection handover with invalid pkhash from AP (negative)"""
+    ssid = "wps-nfc-handover-pkhash-ap"
+    params = ap_wps_params(ssid)
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    if "FAIL" in hapd.request("SET wps_corrupt_pkhash 1"):
+        raise Exception("Could not enable wps_corrupt_pkhash")
+    logger.info("NFC connection handover")
+    req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to hostapd")
+    dev[0].dump_monitor()
+    res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("Timed out")
+    if "WPS-FAIL" not in ev:
+        raise Exception("Public key hash mismatch not detected")
+
+def start_ap_er(er, ap, ssid):
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hostapd.add_ap(ap['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+                     "device_name": "Wireless AP", "manufacturer": "Company",
+                     "model_name": "WAP", "model_number": "123",
+                     "serial_number": "12345", "device_type": "6-0050F204-1",
+                     "os_version": "01020300",
+                     "config_methods": "label push_button",
+                     "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+    logger.info("Learn AP configuration")
+    er.dump_monitor()
+    er.request("SET ignore_old_scan_res 1")
+    er.wps_reg(ap['bssid'], ap_pin)
+
+    logger.info("Start ER")
+    er.request("WPS_ER_STOP")
+    time.sleep(1)
+    er.request("WPS_ER_START ifname=lo")
+    ev = er.wait_event(["WPS-ER-AP-ADD"], timeout=15)
+    if ev is None:
+        raise Exception("AP discovery timed out")
+    if ap_uuid not in ev:
+        raise Exception("Expected AP UUID not found")
+
+    logger.info("Use learned network configuration on ER")
+    er.request("WPS_ER_SET_CONFIG " + ap_uuid + " 0")
+
+def test_nfc_wps_er_pw_token(dev, apdev):
+    """WPS NFC password token from Enrollee to ER"""
+    try:
+        _test_nfc_wps_er_pw_token(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_nfc_wps_er_pw_token(dev, apdev):
+    ssid = "wps-nfc-er-pw-token"
+    start_ap_er(dev[0], apdev[0], ssid)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step using password token from station")
+    dev[1].request("SET ignore_old_scan_res 1")
+    pw = dev[1].request("WPS_NFC_TOKEN NDEF").rstrip()
+    if "FAIL" in pw:
+        raise Exception("Failed to generate password token")
+    res = dev[0].request("WPS_NFC_TAG_READ " + pw)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to WPS ER")
+    dev[0].dump_monitor()
+    res = dev[1].request("WPS_NFC")
+    if "FAIL" in res:
+        raise Exception("Failed to start Enrollee using NFC password token")
+    ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("WPS ER did not report success")
+    dev[1].wait_connected(timeout=15)
+    check_wpa2_connection(dev[1], apdev[0], hapd, ssid)
+
+def test_nfc_wps_er_config_token(dev, apdev):
+    """WPS NFC configuration token from ER to Enrollee"""
+    try:
+        _test_nfc_wps_er_config_token(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_nfc_wps_er_config_token(dev, apdev):
+    ssid = "wps-nfc-er-config-token"
+    start_ap_er(dev[0], apdev[0], ssid)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step using configuration token from ER")
+    wps = dev[0].request("WPS_ER_NFC_CONFIG_TOKEN WPS " + apdev[0]['bssid']).rstrip()
+    if "FAIL" in wps:
+        raise Exception("Failed to generate configuration token (WPS format)")
+    conf = dev[0].request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + apdev[0]['bssid']).rstrip()
+    if "FAIL" in conf:
+        raise Exception("Failed to generate configuration token")
+    dev[1].request("SET ignore_old_scan_res 1")
+    res = dev[1].request("WPS_NFC_TAG_READ " + conf)
+    if "FAIL" in res:
+        raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+    dev[1].wait_connected(timeout=15)
+    check_wpa2_connection(dev[1], apdev[0], hapd, ssid)
+
+def test_nfc_wps_er_handover(dev, apdev):
+    """WPS NFC connection handover between Enrollee and ER"""
+    try:
+        _test_nfc_wps_er_handover(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_nfc_wps_er_handover(dev, apdev):
+    ssid = "wps-nfc-er-handover"
+    start_ap_er(dev[0], apdev[0], ssid)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step using connection handover")
+    req = dev[1].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = dev[0].request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + apdev[0]['bssid']).rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    res = dev[0].request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to hostapd")
+    dev[1].dump_monitor()
+    res = dev[1].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+    dev[1].wait_connected(timeout=15)
+    check_wpa2_connection(dev[1], apdev[0], hapd, ssid)
+
+def test_nfc_wps_er_handover_pk_hash_mismatch_sta(dev, apdev):
+    """WPS NFC connection handover with invalid pkhash from station to ER (negative)"""
+    try:
+        _test_nfc_wps_er_handover_pk_hash_mismatch_sta(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_nfc_wps_er_handover_pk_hash_mismatch_sta(dev, apdev):
+    ssid = "wps-nfc-er-handover-pkhash-sta"
+    start_ap_er(dev[0], apdev[0], ssid)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step using connection handover")
+    if "FAIL" in dev[1].request("SET wps_corrupt_pkhash 1"):
+        raise Exception("Could not enable wps_corrupt_pkhash")
+    dev[1].request("SET ignore_old_scan_res 1")
+    req = dev[1].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = dev[0].request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + apdev[0]['bssid']).rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    res = dev[0].request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to hostapd")
+    dev[1].dump_monitor()
+    res = dev[1].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+    ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED", "WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("Timed out")
+    if "WPS-FAIL" not in ev:
+        raise Exception("Public key hash mismatch not detected")
+
+def test_nfc_wps_er_handover_pk_hash_mismatch_er(dev, apdev):
+    """WPS NFC connection handover with invalid pkhash from ER to station (negative)"""
+    try:
+        _test_nfc_wps_er_handover_pk_hash_mismatch_er(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_nfc_wps_er_handover_pk_hash_mismatch_er(dev, apdev):
+    ssid = "wps-nfc-er-handover-pkhash-er"
+    start_ap_er(dev[0], apdev[0], ssid)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    logger.info("WPS provisioning step using connection handover")
+    if "FAIL" in dev[0].request("SET wps_corrupt_pkhash 1"):
+        raise Exception("Could not enable wps_corrupt_pkhash")
+    dev[1].request("SET ignore_old_scan_res 1")
+    req = dev[1].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+    if "FAIL" in req:
+        raise Exception("Failed to generate NFC connection handover request")
+    sel = dev[0].request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + apdev[0]['bssid']).rstrip()
+    if "FAIL" in sel:
+        raise Exception("Failed to generate NFC connection handover select")
+    res = dev[0].request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to hostapd")
+    dev[1].dump_monitor()
+    res = dev[1].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+    if "FAIL" in res:
+        raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+    ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED", "WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("Timed out")
+    if "WPS-FAIL" not in ev:
+        raise Exception("Public key hash mismatch not detected")
+
+def test_nfc_invalid_ndef_record(dev, apdev):
+    """Invalid NFC NDEF record handling"""
+    tests = [ "11223344",
+              "00112233",
+              "0000112233445566",
+              "0800112233445566",
+              "080011223344",
+              "18000000",
+              "18010000",
+              "90000050",
+              "9000005000",
+              "9001013344",
+              "98010101334455",
+              "0017ffffffe3",
+              "0017ffffffe4",
+              "0017ffffffe9",
+              "0000fffffffa",
+              "0017ffffffe46170706c69636174696f6e2f766e642e7766612e777363",
+              "0017ffffffff6170706c69636174696f6e2f766e642e7766612e777363",
+              "0017000000006170706c69636174696f6e2f766e642e7766612e7773ff",
+              "080000000000" ]
+    for test in tests:
+        if "FAIL" not in dev[0].request("WPS_NFC_TAG_READ " + test):
+            raise Exception("Invalid tag accepted: " + test)
diff --git a/hostap/tests/hwsim/test_offchannel_tx.py b/hostap/tests/hwsim/test_offchannel_tx.py
new file mode 100644
index 0000000..6d49392
--- /dev/null
+++ b/hostap/tests/hwsim/test_offchannel_tx.py
@@ -0,0 +1,52 @@
+# cfg80211 offchannel TX using remain-on-channel
+# 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.
+
+import logging
+logger = logging.getLogger()
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+from test_gas import start_ap
+from test_gas import anqp_get
+from test_p2p_grpform import go_neg_pin_authorized
+from test_p2p_grpform import check_grpform_results
+from test_p2p_grpform import remove_group
+
+def test_offchannel_tx_roc_gas(dev, apdev):
+    """GAS using cfg80211 remain-on-channel for offchannel TX"""
+    start_ap(apdev[0])
+    bssid = apdev[0]['bssid']
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="no_offchannel_tx=1")
+    wpas.flush_scan_cache()
+    wpas.scan_for_bss(bssid, freq=2412)
+    anqp_get(wpas, bssid, 263)
+    ev = wpas.wait_event(["GAS-QUERY-DONE"], timeout=10)
+    if ev is None:
+        raise Exception("GAS query timed out")
+    if "result=SUCCESS" not in ev:
+        raise Exception("Unexpected GAS query result")
+
+def test_offchannel_tx_roc_grpform(dev, apdev):
+    """P2P group formation using cfg80211 remain-on-channel for offchannel TX"""
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="no_offchannel_tx=1")
+
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_freq=2412,
+                                           r_dev=wpas, r_freq=2412)
+    check_grpform_results(i_res, r_res)
+    remove_group(dev[0], wpas)
+
+def test_offchannel_tx_roc_grpform2(dev, apdev):
+    """P2P group formation(2) using cfg80211 remain-on-channel for offchannel TX"""
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="no_offchannel_tx=1")
+
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=wpas, i_freq=2412,
+                                           r_dev=dev[0], r_freq=2412)
+    check_grpform_results(i_res, r_res)
+    remove_group(dev[0], wpas)
diff --git a/hostap/tests/hwsim/test_p2p_autogo.py b/hostap/tests/hwsim/test_p2p_autogo.py
new file mode 100644
index 0000000..c92119b
--- /dev/null
+++ b/hostap/tests/hwsim/test_p2p_autogo.py
@@ -0,0 +1,703 @@
+# P2P autonomous GO test cases
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import utils
+from utils import HwsimSkip
+from wlantest import Wlantest
+from wpasupplicant import WpaSupplicant
+
+def autogo(go, freq=None, persistent=None):
+    logger.info("Start autonomous GO " + go.ifname)
+    res = go.p2p_start_go(freq=freq, persistent=persistent)
+    logger.debug("res: " + str(res))
+    return res
+
+def connect_cli(go, client, social=False, freq=None):
+    logger.info("Try to connect the client to the GO")
+    pin = client.wps_read_pin()
+    go.p2p_go_authorize_client(pin)
+    res = client.p2p_connect_group(go.p2p_dev_addr(), pin, timeout=60,
+                                   social=social, freq=freq)
+    logger.info("Client connected")
+    hwsim_utils.test_connectivity_p2p(go, client)
+    return res
+
+def test_autogo(dev):
+    """P2P autonomous GO and client joining group"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr2 = dev[2].p2p_dev_addr()
+    res = autogo(dev[0])
+    if "p2p-wlan" in res['ifname']:
+        raise Exception("Unexpected group interface name on GO")
+    res = connect_cli(dev[0], dev[1])
+    if "p2p-wlan" in res['ifname']:
+        raise Exception("Unexpected group interface name on client")
+    bss = dev[1].get_bss("p2p_dev_addr=" + addr0, res['ifname'])
+    if not bss or bss['bssid'] != dev[0].p2p_interface_addr():
+        raise Exception("Unexpected BSSID in the BSS entry for the GO")
+    id = bss['id']
+    bss = dev[1].get_bss("ID-" + id, res['ifname'])
+    if not bss or bss['id'] != id:
+        raise Exception("Could not find BSS entry based on id")
+    res = dev[1].group_request("BSS RANGE=" + id + "- MASK=0x1")
+    if "id=" + id not in res:
+        raise Exception("Could not find BSS entry based on id range")
+
+    res = dev[1].request("SCAN_RESULTS")
+    if "[P2P]" not in res:
+        raise Exception("P2P flag missing from scan results: " + res)
+
+    # Presence request to increase testing coverage
+    if "FAIL" not in dev[1].group_request("P2P_PRESENCE_REQ 30000"):
+        raise Exception("Invald P2P_PRESENCE_REQ accepted")
+    if "FAIL" not in dev[1].group_request("P2P_PRESENCE_REQ 30000 102400 30001"):
+        raise Exception("Invald P2P_PRESENCE_REQ accepted")
+    if "FAIL" in dev[1].group_request("P2P_PRESENCE_REQ 30000 102400"):
+        raise Exception("Could not send presence request")
+    ev = dev[1].wait_group_event(["P2P-PRESENCE-RESPONSE"], 10)
+    if ev is None:
+        raise Exception("Timeout while waiting for Presence Response")
+    if "FAIL" in dev[1].group_request("P2P_PRESENCE_REQ 30000 102400 20000 102400"):
+        raise Exception("Could not send presence request")
+    ev = dev[1].wait_group_event(["P2P-PRESENCE-RESPONSE"])
+    if ev is None:
+        raise Exception("Timeout while waiting for Presence Response")
+    if "FAIL" in dev[1].group_request("P2P_PRESENCE_REQ"):
+        raise Exception("Could not send presence request")
+    ev = dev[1].wait_group_event(["P2P-PRESENCE-RESPONSE"])
+    if ev is None:
+        raise Exception("Timeout while waiting for Presence Response")
+
+    if not dev[2].discover_peer(addr0):
+        raise Exception("Could not discover GO")
+    dev[0].dump_monitor()
+    dev[2].global_request("P2P_PROV_DISC " + addr0 + " display join")
+    ev = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=10)
+    if ev is None:
+        raise Exception("GO did not report P2P-PROV-DISC-SHOW-PIN")
+    if "p2p_dev_addr=" + addr2 not in ev:
+        raise Exception("Unexpected P2P Device Address in event: " + ev)
+    if "group=" + dev[0].group_ifname not in ev:
+        raise Exception("Unexpected group interface in event: " + ev)
+    ev = dev[2].wait_global_event(["P2P-PROV-DISC-ENTER-PIN"], timeout=10)
+    if ev is None:
+        raise Exception("P2P-PROV-DISC-ENTER-PIN not reported")
+
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+
+def test_autogo2(dev):
+    """P2P autonomous GO with a separate group interface and client joining group"""
+    dev[0].request("SET p2p_no_group_iface 0")
+    res = autogo(dev[0], freq=2437)
+    if "p2p-wlan" not in res['ifname']:
+        raise Exception("Unexpected group interface name on GO")
+    if res['ifname'] not in utils.get_ifnames():
+        raise Exception("Could not find group interface netdev")
+    connect_cli(dev[0], dev[1], social=True, freq=2437)
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+    if res['ifname'] in utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
+
+def test_autogo3(dev):
+    """P2P autonomous GO and client with a separate group interface joining group"""
+    dev[1].request("SET p2p_no_group_iface 0")
+    autogo(dev[0], freq=2462)
+    res = connect_cli(dev[0], dev[1], social=True, freq=2462)
+    if "p2p-wlan" not in res['ifname']:
+        raise Exception("Unexpected group interface name on client")
+    if res['ifname'] not in utils.get_ifnames():
+        raise Exception("Could not find group interface netdev")
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+    dev[1].ping()
+    if res['ifname'] in utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
+
+def test_autogo4(dev):
+    """P2P autonomous GO and client joining group (both with a separate group interface)"""
+    dev[0].request("SET p2p_no_group_iface 0")
+    dev[1].request("SET p2p_no_group_iface 0")
+    res1 = autogo(dev[0], freq=2412)
+    res2 = connect_cli(dev[0], dev[1], social=True, freq=2412)
+    if "p2p-wlan" not in res1['ifname']:
+        raise Exception("Unexpected group interface name on GO")
+    if "p2p-wlan" not in res2['ifname']:
+        raise Exception("Unexpected group interface name on client")
+    ifnames = utils.get_ifnames()
+    if res1['ifname'] not in ifnames:
+        raise Exception("Could not find GO group interface netdev")
+    if res2['ifname'] not in ifnames:
+        raise Exception("Could not find client group interface netdev")
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+    dev[1].ping()
+    ifnames = utils.get_ifnames()
+    if res1['ifname'] in ifnames:
+        raise Exception("GO group interface netdev was not removed")
+    if res2['ifname'] in ifnames:
+        raise Exception("Client group interface netdev was not removed")
+
+def test_autogo_m2d(dev):
+    """P2P autonomous GO and clients not authorized"""
+    autogo(dev[0], freq=2412)
+    go_addr = dev[0].p2p_dev_addr()
+
+    dev[1].request("SET p2p_no_group_iface 0")
+    if not dev[1].discover_peer(go_addr, social=True):
+        raise Exception("GO " + go_addr + " not found")
+    dev[1].dump_monitor()
+
+    if not dev[2].discover_peer(go_addr, social=True):
+        raise Exception("GO " + go_addr + " not found")
+    dev[2].dump_monitor()
+
+    logger.info("Trying to join the group when GO has not authorized the client")
+    pin = dev[1].wps_read_pin()
+    cmd = "P2P_CONNECT " + go_addr + " " + pin + " join"
+    if "OK" not in dev[1].global_request(cmd):
+        raise Exception("P2P_CONNECT join failed")
+
+    pin = dev[2].wps_read_pin()
+    cmd = "P2P_CONNECT " + go_addr + " " + pin + " join"
+    if "OK" not in dev[2].global_request(cmd):
+        raise Exception("P2P_CONNECT join failed")
+
+    ev = dev[1].wait_global_event(["WPS-M2D"], timeout=16)
+    if ev is None:
+        raise Exception("No global M2D event")
+    ifaces = dev[1].request("INTERFACES").splitlines()
+    iface = ifaces[0] if "p2p-wlan" in ifaces[0] else ifaces[1]
+    wpas = WpaSupplicant(ifname=iface)
+    ev = wpas.wait_event(["WPS-M2D"], timeout=10)
+    if ev is None:
+        raise Exception("No M2D event on group interface")
+
+    ev = dev[2].wait_global_event(["WPS-M2D"], timeout=10)
+    if ev is None:
+        raise Exception("No global M2D event (2)")
+    ev = dev[2].wait_event(["WPS-M2D"], timeout=10)
+    if ev is None:
+        raise Exception("No M2D event on group interface (2)")
+
+def test_autogo_fail(dev):
+    """P2P autonomous GO and incorrect PIN"""
+    autogo(dev[0], freq=2412)
+    go_addr = dev[0].p2p_dev_addr()
+    dev[0].p2p_go_authorize_client("00000000")
+
+    dev[1].request("SET p2p_no_group_iface 0")
+    if not dev[1].discover_peer(go_addr, social=True):
+        raise Exception("GO " + go_addr + " not found")
+    dev[1].dump_monitor()
+
+    logger.info("Trying to join the group when GO has not authorized the client")
+    pin = dev[1].wps_read_pin()
+    cmd = "P2P_CONNECT " + go_addr + " " + pin + " join"
+    if "OK" not in dev[1].global_request(cmd):
+        raise Exception("P2P_CONNECT join failed")
+
+    ev = dev[1].wait_global_event(["WPS-FAIL"], timeout=10)
+    if ev is None:
+        raise Exception("No global WPS-FAIL event")
+
+def test_autogo_2cli(dev):
+    """P2P autonomous GO and two clients joining group"""
+    autogo(dev[0], freq=2412)
+    connect_cli(dev[0], dev[1], social=True, freq=2412)
+    connect_cli(dev[0], dev[2], social=True, freq=2412)
+    hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+    dev[0].global_request("P2P_REMOVE_CLIENT " + dev[1].p2p_dev_addr())
+    dev[1].wait_go_ending_session()
+    dev[0].global_request("P2P_REMOVE_CLIENT iface=" + dev[2].p2p_interface_addr())
+    dev[2].wait_go_ending_session()
+    if "FAIL" not in dev[0].global_request("P2P_REMOVE_CLIENT foo"):
+        raise Exception("Invalid P2P_REMOVE_CLIENT command accepted")
+    dev[0].remove_group()
+
+def test_autogo_pbc(dev):
+    """P2P autonomous GO and PBC"""
+    dev[1].global_request("SET p2p_no_group_iface 0")
+    autogo(dev[0], freq=2412)
+    if "FAIL" not in dev[0].group_request("WPS_PBC p2p_dev_addr=00:11:22:33:44"):
+        raise Exception("Invalid WPS_PBC succeeded")
+    if "OK" not in dev[0].group_request("WPS_PBC p2p_dev_addr=" + dev[1].p2p_dev_addr()):
+        raise Exception("WPS_PBC failed")
+    dev[2].p2p_connect_group(dev[0].p2p_dev_addr(), "pbc", timeout=0,
+                             social=True)
+    ev = dev[2].wait_global_event(["WPS-M2D"], timeout=15)
+    if ev is None:
+        raise Exception("WPS-M2D not reported")
+    if "config_error=12" not in ev:
+        raise Exception("Unexpected config_error: " + ev)
+    dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), "pbc", timeout=15,
+                             social=True)
+
+def test_autogo_tdls(dev):
+    """P2P autonomous GO and two clients using TDLS"""
+    wt = Wlantest()
+    go = dev[0]
+    logger.info("Start autonomous GO with fixed parameters " + go.ifname)
+    id = go.add_network()
+    go.set_network_quoted(id, "ssid", "DIRECT-tdls")
+    go.set_network_quoted(id, "psk", "12345678")
+    go.set_network(id, "mode", "3")
+    go.set_network(id, "disabled", "2")
+    res = go.p2p_start_go(persistent=id, freq="2462")
+    logger.debug("res: " + str(res))
+    wt.flush()
+    wt.add_passphrase("12345678")
+    connect_cli(go, dev[1], social=True, freq=2462)
+    connect_cli(go, dev[2], social=True, freq=2462)
+    hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+    bssid = dev[0].p2p_interface_addr()
+    addr1 = dev[1].p2p_interface_addr()
+    addr2 = dev[2].p2p_interface_addr()
+    dev[1].tdls_setup(addr2)
+    time.sleep(1)
+    hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+    conf = wt.get_tdls_counter("setup_conf_ok", bssid, addr1, addr2);
+    if conf == 0:
+        raise Exception("No TDLS Setup Confirm (success) seen")
+    dl = wt.get_tdls_counter("valid_direct_link", bssid, addr1, addr2);
+    if dl == 0:
+        raise Exception("No valid frames through direct link")
+    wt.tdls_clear(bssid, addr1, addr2);
+    dev[1].tdls_teardown(addr2)
+    time.sleep(1)
+    teardown = wt.get_tdls_counter("teardown", bssid, addr1, addr2);
+    if teardown == 0:
+        raise Exception("No TDLS Setup Teardown seen")
+    wt.tdls_clear(bssid, addr1, addr2);
+    hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+    ap_path = wt.get_tdls_counter("valid_ap_path", bssid, addr1, addr2);
+    if ap_path == 0:
+        raise Exception("No valid frames via AP path")
+    direct_link = wt.get_tdls_counter("valid_direct_link", bssid, addr1, addr2);
+    if direct_link > 0:
+        raise Exception("Unexpected frames through direct link")
+    idirect_link = wt.get_tdls_counter("invalid_direct_link", bssid, addr1,
+                                       addr2);
+    if idirect_link > 0:
+        raise Exception("Unexpected frames through direct link (invalid)")
+    dev[2].remove_group()
+    dev[1].remove_group()
+    dev[0].remove_group()
+
+def test_autogo_legacy(dev):
+    """P2P autonomous GO and legacy clients"""
+    res = autogo(dev[0], freq=2462)
+    if dev[0].get_group_status_field("passphrase", extra="WPS") != res['passphrase']:
+        raise Exception("passphrase mismatch")
+    if dev[0].group_request("P2P_GET_PASSPHRASE") != res['passphrase']:
+        raise Exception("passphrase mismatch(2)")
+
+    logger.info("Connect P2P client")
+    connect_cli(dev[0], dev[1], social=True, freq=2462)
+
+    if "FAIL" not in dev[1].request("P2P_GET_PASSPHRASE"):
+        raise Exception("P2P_GET_PASSPHRASE succeeded on P2P Client")
+
+    logger.info("Connect legacy WPS client")
+    pin = dev[2].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    dev[2].request("P2P_SET disabled 1")
+    dev[2].dump_monitor()
+    dev[2].request("WPS_PIN any " + pin)
+    dev[2].wait_connected(timeout=30)
+    status = dev[2].get_status()
+    if status['wpa_state'] != 'COMPLETED':
+        raise Exception("Not fully connected")
+    hwsim_utils.test_connectivity_p2p_sta(dev[1], dev[2])
+    dev[2].request("DISCONNECT")
+
+    logger.info("Connect legacy non-WPS client")
+    dev[2].request("FLUSH")
+    dev[2].request("P2P_SET disabled 1")
+    dev[2].connect(ssid=res['ssid'], psk=res['passphrase'], proto='RSN',
+                   key_mgmt='WPA-PSK', pairwise='CCMP', group='CCMP',
+                   scan_freq=res['freq'])
+    hwsim_utils.test_connectivity_p2p_sta(dev[1], dev[2])
+    dev[2].request("DISCONNECT")
+
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+
+def test_autogo_chan_switch(dev):
+    """P2P autonomous GO switching channels"""
+    autogo(dev[0], freq=2417)
+    connect_cli(dev[0], dev[1])
+    res = dev[0].request("CHAN_SWITCH 5 2422")
+    if "FAIL" in res:
+        # for now, skip test since mac80211_hwsim support is not yet widely
+        # deployed
+        raise HwsimSkip("Assume mac80211_hwsim did not support channel switching")
+    ev = dev[0].wait_event(["AP-CSA-FINISHED"], timeout=10)
+    if ev is None:
+        raise Exception("CSA finished event timed out")
+    if "freq=2422" not in ev:
+        raise Exception("Unexpected cahnnel in CSA finished event")
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    time.sleep(0.1)
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+
+def test_autogo_extra_cred(dev):
+    """P2P autonomous GO sending two WPS credentials"""
+    if "FAIL" in dev[0].request("SET wps_testing_dummy_cred 1"):
+        raise Exception("Failed to enable test mode")
+    autogo(dev[0], freq=2412)
+    connect_cli(dev[0], dev[1], social=True, freq=2412)
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+
+def test_autogo_ifdown(dev):
+    """P2P autonomous GO and external ifdown"""
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    res = autogo(wpas)
+    wpas.dump_monitor()
+    wpas.interface_remove("wlan5")
+    wpas.interface_add("wlan5")
+    res = autogo(wpas)
+    wpas.dump_monitor()
+    subprocess.call(['ifconfig', res['ifname'], 'down'])
+    ev = wpas.wait_global_event(["P2P-GROUP-REMOVED"], timeout=10)
+    if ev is None:
+        raise Exception("Group removal not reported")
+    if res['ifname'] not in ev:
+        raise Exception("Unexpected group removal event: " + ev)
+
+def test_autogo_start_during_scan(dev):
+    """P2P autonomous GO started during ongoing manual scan"""
+    try:
+        # use autoscan to set scan_req = MANUAL_SCAN_REQ
+        if "OK" not in dev[0].request("AUTOSCAN periodic:1"):
+            raise Exception("Failed to set autoscan")
+        autogo(dev[0], freq=2462)
+        connect_cli(dev[0], dev[1], social=True, freq=2462)
+        dev[0].remove_group()
+        dev[1].wait_go_ending_session()
+    finally:
+        dev[0].request("AUTOSCAN ")
+
+def test_autogo_passphrase_len(dev):
+    """P2P autonomous GO and longer passphrase"""
+    try:
+        if "OK" not in dev[0].request("SET p2p_passphrase_len 13"):
+            raise Exception("Failed to set passphrase length")
+        res = autogo(dev[0], freq=2412)
+        if len(res['passphrase']) != 13:
+            raise Exception("Unexpected passphrase length")
+        if dev[0].get_group_status_field("passphrase", extra="WPS") != res['passphrase']:
+            raise Exception("passphrase mismatch")
+
+        logger.info("Connect P2P client")
+        connect_cli(dev[0], dev[1], social=True, freq=2412)
+
+        logger.info("Connect legacy WPS client")
+        pin = dev[2].wps_read_pin()
+        dev[0].p2p_go_authorize_client(pin)
+        dev[2].request("P2P_SET disabled 1")
+        dev[2].dump_monitor()
+        dev[2].request("WPS_PIN any " + pin)
+        dev[2].wait_connected(timeout=30)
+        status = dev[2].get_status()
+        if status['wpa_state'] != 'COMPLETED':
+            raise Exception("Not fully connected")
+        dev[2].request("DISCONNECT")
+
+        logger.info("Connect legacy non-WPS client")
+        dev[2].request("FLUSH")
+        dev[2].request("P2P_SET disabled 1")
+        dev[2].connect(ssid=res['ssid'], psk=res['passphrase'], proto='RSN',
+                       key_mgmt='WPA-PSK', pairwise='CCMP', group='CCMP',
+                       scan_freq=res['freq'])
+        hwsim_utils.test_connectivity_p2p_sta(dev[1], dev[2])
+        dev[2].request("DISCONNECT")
+
+        dev[0].remove_group()
+        dev[1].wait_go_ending_session()
+    finally:
+        dev[0].request("SET p2p_passphrase_len 8")
+
+def test_autogo_bridge(dev):
+    """P2P autonomous GO in a bridge"""
+    try:
+        # use autoscan to set scan_req = MANUAL_SCAN_REQ
+        if "OK" not in dev[0].request("AUTOSCAN periodic:1"):
+            raise Exception("Failed to set autoscan")
+        autogo(dev[0])
+        ifname = dev[0].get_group_ifname()
+        subprocess.call(['brctl', 'addbr', 'p2p-br0'])
+        subprocess.call(['brctl', 'setfd', 'p2p-br0', '0'])
+        subprocess.call(['brctl', 'addif', 'p2p-br0', ifname])
+        subprocess.call(['ip', 'link', 'set', 'dev', 'p2p-br0', 'up'])
+        time.sleep(0.1)
+        subprocess.call(['brctl', 'delif', 'p2p-br0', ifname])
+        time.sleep(0.1)
+        subprocess.call(['ip', 'link', 'set', 'dev', 'p2p-br0', 'down'])
+        time.sleep(0.1)
+        subprocess.call(['brctl', 'delbr', 'p2p-br0'])
+        ev = dev[0].wait_global_event(["P2P-GROUP-REMOVED"], timeout=1)
+        if ev is not None:
+            raise Exception("P2P group removed unexpectedly")
+        if dev[0].get_group_status_field('wpa_state') != "COMPLETED":
+            raise Exception("Unexpected wpa_state")
+        dev[0].remove_group()
+    finally:
+        dev[0].request("AUTOSCAN ")
+        subprocess.Popen(['brctl', 'delif', 'p2p-br0', ifname],
+                         stderr=open('/dev/null', 'w'))
+        subprocess.Popen(['ip', 'link', 'set', 'dev', 'p2p-br0', 'down'],
+                         stderr=open('/dev/null', 'w'))
+        subprocess.Popen(['brctl', 'delbr', 'p2p-br0'],
+                         stderr=open('/dev/null', 'w'))
+
+def test_presence_req_on_group_interface(dev):
+    """P2P_PRESENCE_REQ on group interface"""
+    dev[1].request("SET p2p_no_group_iface 0")
+    res = autogo(dev[0], freq=2437)
+    res = connect_cli(dev[0], dev[1], social=True, freq=2437)
+    if "FAIL" in dev[1].group_request("P2P_PRESENCE_REQ 30000 102400"):
+        raise Exception("Could not send presence request")
+    ev = dev[1].wait_group_event(["P2P-PRESENCE-RESPONSE"])
+    if ev is None:
+        raise Exception("Timeout while waiting for Presence Response")
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+
+def test_autogo_join_auto_go_not_found(dev):
+    """P2P_CONNECT-auto not finding GO"""
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    wpas.request("P2P_SET listen_channel 1")
+    wpas.global_request("SET p2p_no_group_iface 0")
+    autogo(wpas, freq=2412)
+    addr = wpas.p2p_dev_addr()
+    bssid = wpas.p2p_interface_addr()
+
+    dev[1].global_request("SET p2p_no_group_iface 0")
+    dev[1].scan_for_bss(bssid, freq=2412)
+    # This makes the GO not show up in the scan iteration following the
+    # P2P_CONNECT command by stopping beaconing and handling Probe Request
+    # frames externally (but not really replying to them). P2P listen mode is
+    # needed to keep the GO listening on the operating channel for the PD
+    # exchange.
+    if "OK" not in wpas.group_request("STOP_AP"):
+        raise Exception("STOP_AP failed")
+    wpas.group_request("SET ext_mgmt_frame_handling 1")
+    wpas.p2p_listen()
+    time.sleep(0.02)
+    dev[1].global_request("P2P_CONNECT " + addr + " pbc auto")
+
+    ev = dev[1].wait_global_event(["P2P-FALLBACK-TO-GO-NEG-ENABLED"], 15)
+    if ev is None:
+        raise Exception("Could not trigger old-scan-only case")
+        return
+
+    ev = dev[1].wait_global_event(["P2P-FALLBACK-TO-GO-NEG"], 15)
+    wpas.remove_group()
+    if ev is None:
+        raise Exception("Fallback to GO Negotiation not seen")
+    if "reason=GO-not-found" not in ev:
+        raise Exception("Unexpected reason for fallback: " + ev)
+
+def test_autogo_join_auto(dev):
+    """P2P_CONNECT-auto joining a group"""
+    autogo(dev[0])
+    addr = dev[0].p2p_dev_addr()
+    if "OK" not in dev[1].global_request("P2P_CONNECT " + addr + " pbc auto"):
+        raise Exception("P2P_CONNECT failed")
+
+    ev = dev[0].wait_global_event(["P2P-PROV-DISC-PBC-REQ"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on P2P-PROV-DISC-PBC-REQ")
+    if "group=" + dev[0].group_ifname not in ev:
+        raise Exception("Unexpected PD event contents: " + ev)
+    dev[0].group_request("WPS_PBC")
+
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+    if ev is None:
+        raise Exception("Joining the group timed out")
+    dev[1].group_form_result(ev)
+
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+    dev[1].flush_scan_cache()
+
+def test_autogo_join_auto_go_neg(dev):
+    """P2P_CONNECT-auto fallback to GO Neg"""
+    dev[1].flush_scan_cache()
+    dev[0].p2p_listen()
+    addr = dev[0].p2p_dev_addr()
+    if "OK" not in dev[1].global_request("P2P_CONNECT " + addr + " pbc auto"):
+        raise Exception("P2P_CONNECT failed")
+
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on P2P-GO-NEG-REQUEST")
+    peer = ev.split(' ')[1]
+    dev[0].p2p_go_neg_init(peer, None, "pbc", timeout=15, go_intent=15)
+
+    ev = dev[1].wait_global_event(["P2P-FALLBACK-TO-GO-NEG"], timeout=1)
+    if ev is None:
+        raise Exception("No P2P-FALLBACK-TO-GO-NEG event seen")
+    if "P2P-FALLBACK-TO-GO-NEG-ENABLED" in ev:
+        ev = dev[1].wait_global_event(["P2P-FALLBACK-TO-GO-NEG"], timeout=1)
+        if ev is None:
+            raise Exception("No P2P-FALLBACK-TO-GO-NEG event seen")
+    if "reason=peer-not-running-GO" not in ev:
+        raise Exception("Unexpected reason: " + ev)
+
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+    if ev is None:
+        raise Exception("Joining the group timed out")
+    dev[1].group_form_result(ev)
+
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+    dev[1].flush_scan_cache()
+
+def test_autogo_join_auto_go_neg_after_seeing_go(dev):
+    """P2P_CONNECT-auto fallback to GO Neg after seeing GO"""
+    autogo(dev[0], freq=2412)
+    addr = dev[0].p2p_dev_addr()
+    bssid = dev[0].p2p_interface_addr()
+    dev[1].scan_for_bss(bssid, freq=2412)
+    dev[0].remove_group()
+    dev[0].p2p_listen()
+
+    if "OK" not in dev[1].global_request("P2P_CONNECT " + addr + " pbc auto"):
+        raise Exception("P2P_CONNECT failed")
+
+    ev = dev[1].wait_global_event(["P2P-FALLBACK-TO-GO-NEG-ENABLED"],
+                                  timeout=15)
+    if ev is None:
+        raise Exception("No P2P-FALLBACK-TO-GO-NEG-ENABLED event seen")
+
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on P2P-GO-NEG-REQUEST")
+    peer = ev.split(' ')[1]
+    dev[0].p2p_go_neg_init(peer, None, "pbc", timeout=15, go_intent=15)
+
+    ev = dev[1].wait_global_event(["P2P-FALLBACK-TO-GO-NEG"], timeout=1)
+    if ev is None:
+        raise Exception("No P2P-FALLBACK-TO-GO-NEG event seen")
+    if "reason=no-ACK-to-PD-Req" not in ev and "reason=PD-failed" not in ev:
+        raise Exception("Unexpected reason: " + ev)
+
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+    if ev is None:
+        raise Exception("Joining the group timed out")
+    dev[1].group_form_result(ev)
+
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+    dev[1].flush_scan_cache()
+
+def test_go_search_non_social(dev):
+    """P2P_FIND with freq parameter to scan a single channel"""
+    addr0 = dev[0].p2p_dev_addr()
+    autogo(dev[0], freq=2422)
+    dev[1].p2p_find(freq=2422)
+    ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=3.5)
+    if ev is None:
+        raise Exception("Did not find GO quickly enough")
+    dev[2].p2p_listen()
+    ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+    if ev is None:
+        raise Exception("Did not find peer")
+    dev[2].p2p_stop_find()
+    dev[1].p2p_stop_find()
+    dev[0].remove_group()
+
+def test_autogo_many(dev):
+    """P2P autonomous GO with large number of GO instances"""
+    dev[0].request("SET p2p_no_group_iface 0")
+    for i in range(100):
+        if "OK" not in dev[0].global_request("P2P_GROUP_ADD freq=2412"):
+            logger.info("Was able to add %d groups" % i)
+            if i < 5:
+                raise Exception("P2P_GROUP_ADD failed")
+            stop_ev = dev[0].wait_global_event(["P2P-GROUP-REMOVE"], timeout=1)
+            if stop_ev is not None:
+                raise Exception("Unexpected P2P-GROUP-REMOVE event")
+            break
+        ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
+        if ev is None:
+            raise Exception("GO start up timed out")
+        dev[0].group_form_result(ev)
+
+    for i in dev[0].global_request("INTERFACES").splitlines():
+        dev[0].request("P2P_GROUP_REMOVE " + i)
+        dev[0].dump_monitor()
+    dev[0].request("P2P_GROUP_REMOVE *")
+
+def test_autogo_many_clients(dev):
+    """P2P autonomous GO and many clients (P2P IE fragmentation)"""
+    try:
+        _test_autogo_many_clients(dev)
+    finally:
+        dev[0].global_request("SET device_name Device A")
+        dev[1].global_request("SET device_name Device B")
+        dev[2].global_request("SET device_name Device C")
+
+def _test_autogo_many_clients(dev):
+    # These long device names will push the P2P IE contents beyond the limit
+    # that requires fragmentation.
+    name0 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    name1 = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+    name2 = "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
+    name3 = "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
+    dev[0].global_request("SET device_name " + name0)
+    dev[1].global_request("SET device_name " + name1)
+    dev[2].global_request("SET device_name " + name2)
+
+    addr0 = dev[0].p2p_dev_addr()
+    res = autogo(dev[0], freq=2412)
+    bssid = dev[0].p2p_interface_addr()
+
+    connect_cli(dev[0], dev[1], social=True, freq=2412)
+    dev[0].dump_monitor()
+    connect_cli(dev[0], dev[2], social=True, freq=2412)
+    dev[0].dump_monitor()
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    wpas.global_request("SET device_name " + name3)
+    wpas.global_request("SET sec_device_type 1-11111111-1")
+    wpas.global_request("SET sec_device_type 2-22222222-2")
+    wpas.global_request("SET sec_device_type 3-33333333-3")
+    wpas.global_request("SET sec_device_type 4-44444444-4")
+    wpas.global_request("SET sec_device_type 5-55555555-5")
+    connect_cli(dev[0], wpas, social=True, freq=2412)
+    dev[0].dump_monitor()
+
+    dev[1].dump_monitor()
+    dev[1].p2p_find(freq=2412)
+    ev1 = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+    if ev1 is None:
+        raise Exception("Could not find peer (1)")
+    ev2 = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+    if ev2 is None:
+        raise Exception("Could not find peer (2)")
+    ev3 = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+    if ev3 is None:
+        raise Exception("Could not find peer (3)")
+    dev[1].p2p_stop_find()
+
+    for i in [ name0, name2, name3 ]:
+        if i not in ev1 and i not in ev2 and i not in ev3:
+            raise Exception('name "%s" not found' % i)
diff --git a/hostap/tests/hwsim/test_p2p_channel.py b/hostap/tests/hwsim/test_p2p_channel.py
new file mode 100644
index 0000000..8fa80a4
--- /dev/null
+++ b/hostap/tests/hwsim/test_p2p_channel.py
@@ -0,0 +1,912 @@
+# P2P channel selection test cases
+# 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.
+
+import logging
+logger = logging.getLogger()
+import os
+import subprocess
+import time
+
+import hostapd
+import hwsim_utils
+from utils import HwsimSkip
+from tshark import run_tshark
+from wpasupplicant import WpaSupplicant
+from hwsim import HWSimRadio
+from test_p2p_grpform import go_neg_pin_authorized
+from test_p2p_grpform import check_grpform_results
+from test_p2p_grpform import remove_group
+from test_p2p_grpform import go_neg_pbc
+from test_p2p_autogo import autogo
+
+def set_country(country, dev=None):
+    subprocess.call(['iw', 'reg', 'set', country])
+    time.sleep(0.1)
+    if dev:
+        for i in range(10):
+            ev = dev.wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=15)
+            if ev is None:
+                raise Exception("No regdom change event seen")
+            if "type=COUNTRY alpha2=" + country in ev:
+                return
+        raise Exception("No matching regdom event seen for set_country(%s)" % country)
+
+def test_p2p_channel_5ghz(dev):
+    """P2P group formation with 5 GHz preference"""
+    try:
+        set_country("US", dev[0])
+        [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                               r_dev=dev[1], r_intent=0,
+                                               test_data=False)
+        check_grpform_results(i_res, r_res)
+        freq = int(i_res['freq'])
+        if freq < 5000:
+            raise Exception("Unexpected channel %d MHz - did not follow 5 GHz preference" % freq)
+        remove_group(dev[0], dev[1])
+    finally:
+        set_country("00")
+        dev[1].flush_scan_cache()
+
+def test_p2p_channel_5ghz_no_vht(dev):
+    """P2P group formation with 5 GHz preference when VHT channels are disallowed"""
+    try:
+        set_country("US", dev[0])
+        dev[0].request("P2P_SET disallow_freq 5180-5240")
+        [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                               r_dev=dev[1], r_intent=0,
+                                               test_data=False)
+        check_grpform_results(i_res, r_res)
+        freq = int(i_res['freq'])
+        if freq < 5000:
+            raise Exception("Unexpected channel %d MHz - did not follow 5 GHz preference" % freq)
+        remove_group(dev[0], dev[1])
+    finally:
+        set_country("00")
+        dev[0].request("P2P_SET disallow_freq ")
+        dev[1].flush_scan_cache()
+
+def test_p2p_channel_random_social(dev):
+    """P2P group formation with 5 GHz preference but all 5 GHz channels disabled"""
+    try:
+        set_country("US", dev[0])
+        dev[0].request("SET p2p_oper_channel 11")
+        dev[0].request("P2P_SET disallow_freq 5000-6000,2462")
+        [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                               r_dev=dev[1], r_intent=0,
+                                               test_data=False)
+        check_grpform_results(i_res, r_res)
+        freq = int(i_res['freq'])
+        if freq not in [ 2412, 2437, 2462 ]:
+            raise Exception("Unexpected channel %d MHz - did not pick random social channel" % freq)
+        remove_group(dev[0], dev[1])
+    finally:
+        set_country("00")
+        dev[0].request("P2P_SET disallow_freq ")
+        dev[1].flush_scan_cache()
+
+def test_p2p_channel_random(dev):
+    """P2P group formation with 5 GHz preference but all 5 GHz channels and all social channels disabled"""
+    try:
+        set_country("US", dev[0])
+        dev[0].request("SET p2p_oper_channel 11")
+        dev[0].request("P2P_SET disallow_freq 5000-6000,2412,2437,2462")
+        [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                               r_dev=dev[1], r_intent=0,
+                                               test_data=False)
+        check_grpform_results(i_res, r_res)
+        freq = int(i_res['freq'])
+        if freq > 2500 or freq in [ 2412, 2437, 2462 ]:
+            raise Exception("Unexpected channel %d MHz" % freq)
+        remove_group(dev[0], dev[1])
+    finally:
+        set_country("00")
+        dev[0].request("P2P_SET disallow_freq ")
+        dev[1].flush_scan_cache()
+
+def test_p2p_channel_random_social_with_op_class_change(dev, apdev, params):
+    """P2P group formation using random social channel with oper class change needed"""
+    try:
+        set_country("US", dev[0])
+        logger.info("Start group on 5 GHz")
+        [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                               r_dev=dev[1], r_intent=0,
+                                               test_data=False)
+        check_grpform_results(i_res, r_res)
+        freq = int(i_res['freq'])
+        if freq < 5000:
+            raise Exception("Unexpected channel %d MHz - did not pick 5 GHz preference" % freq)
+        remove_group(dev[0], dev[1])
+
+        logger.info("Disable 5 GHz and try to re-start group based on 5 GHz preference")
+        dev[0].request("SET p2p_oper_reg_class 115")
+        dev[0].request("SET p2p_oper_channel 36")
+        dev[0].request("P2P_SET disallow_freq 5000-6000")
+        [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                               r_dev=dev[1], r_intent=0,
+                                               test_data=False)
+        check_grpform_results(i_res, r_res)
+        freq = int(i_res['freq'])
+        if freq not in [ 2412, 2437, 2462 ]:
+            raise Exception("Unexpected channel %d MHz - did not pick random social channel" % freq)
+        remove_group(dev[0], dev[1])
+
+        out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+                         "wifi_p2p.public_action.subtype == 0")
+        if out is not None:
+            last = None
+            for l in out.splitlines():
+                if "Operating Channel:" not in l:
+                    continue
+                last = l
+            if last is None:
+                raise Exception("Could not find GO Negotiation Request")
+            if "Operating Class 81" not in last:
+                raise Exception("Unexpected operating class: " + last.strip())
+    finally:
+        set_country("00")
+        dev[0].request("P2P_SET disallow_freq ")
+        dev[0].request("SET p2p_oper_reg_class 0")
+        dev[0].request("SET p2p_oper_channel 0")
+        dev[1].flush_scan_cache()
+
+def test_p2p_channel_avoid(dev):
+    """P2P and avoid frequencies driver event"""
+    try:
+        set_country("US", dev[0])
+        if "OK" not in dev[0].request("DRIVER_EVENT AVOID_FREQUENCIES 5000-6000,2412,2437,2462"):
+            raise Exception("Could not simulate driver event")
+        ev = dev[0].wait_event(["CTRL-EVENT-AVOID-FREQ"], timeout=10)
+        if ev is None:
+            raise Exception("No CTRL-EVENT-AVOID-FREQ event")
+        [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                               r_dev=dev[1], r_intent=0,
+                                               test_data=False)
+        check_grpform_results(i_res, r_res)
+        freq = int(i_res['freq'])
+        if freq > 2500 or freq in [ 2412, 2437, 2462 ]:
+            raise Exception("Unexpected channel %d MHz" % freq)
+
+        if "OK" not in dev[0].request("DRIVER_EVENT AVOID_FREQUENCIES"):
+            raise Exception("Could not simulate driver event(2)")
+        ev = dev[0].wait_event(["CTRL-EVENT-AVOID-FREQ"], timeout=10)
+        if ev is None:
+            raise Exception("No CTRL-EVENT-AVOID-FREQ event")
+        ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP"], timeout=1)
+        if ev is not None:
+            raise Exception("Unexpected P2P-REMOVE-AND-REFORM-GROUP event")
+
+        if "OK" not in dev[0].request("DRIVER_EVENT AVOID_FREQUENCIES " + str(freq)):
+            raise Exception("Could not simulate driver event(3)")
+        ev = dev[0].wait_event(["CTRL-EVENT-AVOID-FREQ"], timeout=10)
+        if ev is None:
+            raise Exception("No CTRL-EVENT-AVOID-FREQ event")
+        ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP"],
+                                     timeout=10)
+        if ev is None:
+            raise Exception("No P2P-REMOVE-AND-REFORM-GROUP event")
+    finally:
+        set_country("00")
+        dev[0].request("DRIVER_EVENT AVOID_FREQUENCIES")
+        dev[1].flush_scan_cache()
+
+def test_autogo_following_bss(dev, apdev):
+    """P2P autonomous GO operate on the same channel as station interface"""
+    if dev[0].get_mcc() > 1:
+        logger.info("test mode: MCC")
+
+    dev[0].request("SET p2p_no_group_iface 0")
+
+    channels = { 3 : "2422", 5 : "2432", 9 : "2452" }
+    for key in channels:
+        hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid" : 'ap-test',
+                                                    "channel" : str(key) })
+        dev[0].connect("ap-test", key_mgmt="NONE",
+                       scan_freq=str(channels[key]))
+        res_go = autogo(dev[0])
+        if res_go['freq'] != channels[key]:
+            raise Exception("Group operation channel is not the same as on connected station interface")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+        dev[0].remove_group(res_go['ifname'])
+
+def test_go_neg_with_bss_connected(dev, apdev):
+    """P2P channel selection: GO negotiation when station interface is connected"""
+
+    dev[0].flush_scan_cache()
+    dev[1].flush_scan_cache()
+    dev[0].request("SET p2p_no_group_iface 0")
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": 'bss-2.4ghz', "channel": '5' })
+    dev[0].connect("bss-2.4ghz", key_mgmt="NONE", scan_freq="2432")
+    #dev[0] as GO
+    [i_res, r_res] = go_neg_pbc(i_dev=dev[0], i_intent=10, r_dev=dev[1],
+                                r_intent=1)
+    check_grpform_results(i_res, r_res)
+    if i_res['role'] != "GO":
+       raise Exception("GO not selected according to go_intent")
+    if i_res['freq'] != "2432":
+       raise Exception("Group formed on a different frequency than BSS")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    dev[0].remove_group(i_res['ifname'])
+    dev[1].wait_go_ending_session()
+
+    if dev[0].get_mcc() > 1:
+        logger.info("Skip as-client case due to MCC being enabled")
+        return;
+
+    #dev[0] as client
+    [i_res2, r_res2] = go_neg_pbc(i_dev=dev[0], i_intent=1, r_dev=dev[1],
+                                  r_intent=10)
+    check_grpform_results(i_res2, r_res2)
+    if i_res2['role'] != "client":
+       raise Exception("GO not selected according to go_intent")
+    if i_res2['freq'] != "2432":
+       raise Exception("Group formed on a different frequency than BSS")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    dev[1].remove_group(r_res2['ifname'])
+    dev[0].wait_go_ending_session()
+    dev[0].request("DISCONNECT")
+    hapd.disable()
+    dev[0].flush_scan_cache()
+    dev[1].flush_scan_cache()
+
+def test_autogo_with_bss_on_disallowed_chan(dev, apdev):
+    """P2P channel selection: Autonomous GO with BSS on a disallowed channel"""
+
+    with HWSimRadio(n_channels=2) as (radio, iface):
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        wpas.interface_add(iface)
+
+        wpas.request("SET p2p_no_group_iface 0")
+
+        if wpas.get_mcc() < 2:
+           raise Exception("New radio does not support MCC")
+
+        try:
+            hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": 'bss-2.4ghz',
+                                                        "channel": '1' })
+            wpas.request("P2P_SET disallow_freq 2412")
+            wpas.connect("bss-2.4ghz", key_mgmt="NONE", scan_freq="2412")
+            res = autogo(wpas)
+            if res['freq'] == "2412":
+               raise Exception("GO set on a disallowed channel")
+            hwsim_utils.test_connectivity(wpas, hapd)
+        finally:
+            wpas.request("P2P_SET disallow_freq ")
+
+def test_go_neg_with_bss_on_disallowed_chan(dev, apdev):
+    """P2P channel selection: GO negotiation with station interface on a disallowed channel"""
+
+    with HWSimRadio(n_channels=2) as (radio, iface):
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        wpas.interface_add(iface)
+
+        wpas.request("SET p2p_no_group_iface 0")
+
+        if wpas.get_mcc() < 2:
+           raise Exception("New radio does not support MCC")
+
+        try:
+            hapd = hostapd.add_ap(apdev[0]['ifname'],
+                                  { "ssid": 'bss-2.4ghz', "channel": '1' })
+            # make sure PBC overlap from old test cases is not maintained
+            dev[1].flush_scan_cache()
+            wpas.connect("bss-2.4ghz", key_mgmt="NONE", scan_freq="2412")
+            wpas.request("P2P_SET disallow_freq 2412")
+
+            #wpas as GO
+            [i_res, r_res] = go_neg_pbc(i_dev=wpas, i_intent=10, r_dev=dev[1],
+                                        r_intent=1)
+            check_grpform_results(i_res, r_res)
+            if i_res['role'] != "GO":
+               raise Exception("GO not selected according to go_intent")
+            if i_res['freq'] == "2412":
+               raise Exception("Group formed on a disallowed channel")
+            hwsim_utils.test_connectivity(wpas, hapd)
+            wpas.remove_group(i_res['ifname'])
+            dev[1].wait_go_ending_session()
+            dev[1].flush_scan_cache()
+
+            wpas.dump_monitor()
+            dev[1].dump_monitor()
+
+            #wpas as client
+            [i_res2, r_res2] = go_neg_pbc(i_dev=wpas, i_intent=1, r_dev=dev[1],
+                                          r_intent=10)
+            check_grpform_results(i_res2, r_res2)
+            if i_res2['role'] != "client":
+               raise Exception("GO not selected according to go_intent")
+            if i_res2['freq'] == "2412":
+               raise Exception("Group formed on a disallowed channel")
+            hwsim_utils.test_connectivity(wpas, hapd)
+            dev[1].remove_group(r_res2['ifname'])
+            wpas.wait_go_ending_session()
+            ev = dev[1].wait_global_event(["P2P-GROUP-REMOVED"], timeout=5)
+            if ev is None:
+                raise Exception("Group removal not indicated")
+            wpas.request("DISCONNECT")
+            hapd.disable()
+        finally:
+            wpas.request("P2P_SET disallow_freq ")
+
+def test_autogo_force_diff_channel(dev, apdev):
+    """P2P autonomous GO and station interface operate on different channels"""
+    with HWSimRadio(n_channels=2) as (radio, iface):
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        wpas.interface_add(iface)
+
+        if wpas.get_mcc() < 2:
+           raise Exception("New radio does not support MCC")
+
+        wpas.request("SET p2p_no_group_iface 0")
+
+        hapd = hostapd.add_ap(apdev[0]['ifname'],
+                              {"ssid" : 'ap-test', "channel" : '1'})
+        wpas.connect("ap-test", key_mgmt = "NONE", scan_freq = "2412")
+        channels = { 2 : 2417, 5 : 2432, 9 : 2452 }
+        for key in channels:
+            res_go = autogo(wpas, channels[key])
+            hwsim_utils.test_connectivity(wpas, hapd)
+            if int(res_go['freq']) == 2412:
+                raise Exception("Group operation channel is: 2412 excepted: " + res_go['freq'])
+            wpas.remove_group(res_go['ifname'])
+
+def test_go_neg_forced_freq_diff_than_bss_freq(dev, apdev):
+    """P2P channel selection: GO negotiation with forced freq different than station interface"""
+    with HWSimRadio(n_channels=2) as (radio, iface):
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        wpas.interface_add(iface)
+
+        if wpas.get_mcc() < 2:
+           raise Exception("New radio does not support MCC")
+
+        # Clear possible PBC session overlap from previous test case
+        dev[1].flush_scan_cache()
+
+        wpas.request("SET p2p_no_group_iface 0")
+
+        hapd = hostapd.add_ap(apdev[0]['ifname'],
+                              { "country_code": 'US',
+                                "ssid": 'bss-5ghz', "hw_mode": 'a',
+                                "channel": '40' })
+        wpas.connect("bss-5ghz", key_mgmt="NONE", scan_freq="5200")
+
+        # GO and peer force the same freq, different than BSS freq,
+        # wpas to become GO
+        [i_res, r_res] = go_neg_pbc(i_dev=dev[1], i_intent=1, i_freq=5180,
+                                    r_dev=wpas, r_intent=14, r_freq=5180)
+        check_grpform_results(i_res, r_res)
+        if i_res['freq'] != "5180":
+           raise Exception("P2P group formed on unexpected frequency: " + i_res['freq'])
+        if r_res['role'] != "GO":
+           raise Exception("GO not selected according to go_intent")
+        hwsim_utils.test_connectivity(wpas, hapd)
+        wpas.remove_group(r_res['ifname'])
+        dev[1].wait_go_ending_session()
+        dev[1].flush_scan_cache()
+
+        # GO and peer force the same freq, different than BSS freq, wpas to
+        # become client
+        [i_res2, r_res2] = go_neg_pbc(i_dev=dev[1], i_intent=14, i_freq=2422,
+                                      r_dev=wpas, r_intent=1, r_freq=2422)
+        check_grpform_results(i_res2, r_res2)
+        if i_res2['freq'] != "2422":
+           raise Exception("P2P group formed on unexpected frequency: " + i_res2['freq'])
+        if r_res2['role'] != "client":
+           raise Exception("GO not selected according to go_intent")
+        hwsim_utils.test_connectivity(wpas, hapd)
+
+        wpas.request("DISCONNECT")
+        hapd.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        wpas.flush_scan_cache()
+
+def test_go_pref_chan_bss_on_diff_chan(dev, apdev):
+    """P2P channel selection: Station on different channel than GO configured pref channel"""
+
+    dev[0].request("SET p2p_no_group_iface 0")
+
+    try:
+        hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": 'bss-2.4ghz',
+                                                    "channel": '1' })
+        dev[0].request("SET p2p_pref_chan 81:2")
+        dev[0].connect("bss-2.4ghz", key_mgmt="NONE", scan_freq="2412")
+        res = autogo(dev[0])
+        if res['freq'] != "2412":
+           raise Exception("GO channel did not follow BSS")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+    finally:
+        dev[0].request("SET p2p_pref_chan ")
+
+def test_go_pref_chan_bss_on_disallowed_chan(dev, apdev):
+    """P2P channel selection: Station interface on different channel than GO configured pref channel, and station channel is disallowed"""
+    with HWSimRadio(n_channels=2) as (radio, iface):
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        wpas.interface_add(iface)
+
+        if wpas.get_mcc() < 2:
+           raise Exception("New radio does not support MCC")
+
+        wpas.request("SET p2p_no_group_iface 0")
+
+        try:
+            hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": 'bss-2.4ghz',
+                                                        "channel": '1' })
+            wpas.request("P2P_SET disallow_freq 2412")
+            wpas.request("SET p2p_pref_chan 81:2")
+            wpas.connect("bss-2.4ghz", key_mgmt="NONE", scan_freq="2412")
+            res2 = autogo(wpas)
+            if res2['freq'] != "2417":
+               raise Exception("GO channel did not follow pref_chan configuration")
+            hwsim_utils.test_connectivity(wpas, hapd)
+        finally:
+            wpas.request("P2P_SET disallow_freq ")
+            wpas.request("SET p2p_pref_chan ")
+
+def test_no_go_freq(dev, apdev):
+    """P2P channel selection: no GO freq"""
+    try:
+       dev[0].request("SET p2p_no_go_freq 2412")
+       # dev[0] as client, channel 1 is ok
+       [i_res, r_res] = go_neg_pbc(i_dev=dev[0], i_intent=1,
+                                   r_dev=dev[1], r_intent=14, r_freq=2412)
+       check_grpform_results(i_res, r_res)
+       if i_res['freq'] != "2412":
+          raise Exception("P2P group not formed on forced freq")
+
+       dev[1].remove_group(r_res['ifname'])
+       dev[0].wait_go_ending_session()
+       dev[0].flush_scan_cache()
+
+       fail = False
+       # dev[0] as GO, channel 1 is not allowed
+       try:
+          dev[0].request("SET p2p_no_go_freq 2412")
+          [i_res2, r_res2] = go_neg_pbc(i_dev=dev[0], i_intent=14,
+                                        r_dev=dev[1], r_intent=1, r_freq=2412)
+          check_grpform_results(i_res2, r_res2)
+          fail = True
+       except:
+           pass
+       if fail:
+           raise Exception("GO set on a disallowed freq")
+    finally:
+       dev[0].request("SET p2p_no_go_freq ")
+
+def test_go_neg_peers_force_diff_freq(dev, apdev):
+    """P2P channel selection when peers for different frequency"""
+    try:
+       [i_res2, r_res2] = go_neg_pbc(i_dev=dev[0], i_intent=14, i_freq=5180,
+                                     r_dev=dev[1], r_intent=0, r_freq=5200)
+    except Exception, e:
+        return
+    raise Exception("Unexpected group formation success")
+
+def test_autogo_random_channel(dev, apdev):
+    """P2P channel selection: GO instantiated on random channel 1, 6, 11"""
+    freqs = []
+    go_freqs = ["2412", "2437", "2462"]
+    for i in range(0, 20):
+        result = autogo(dev[0])
+        if result['freq'] not in go_freqs:
+           raise Exception("Unexpected frequency selected: " + result['freq'])
+        if result['freq'] not in freqs:
+            freqs.append(result['freq'])
+        if len(freqs) == 3:
+            break
+        dev[0].remove_group(result['ifname'])
+    if i == 20:
+       raise Exception("GO created 20 times and not all social channels were selected. freqs not selected: " + str(list(set(go_freqs) - set(freqs))))
+
+def test_p2p_autogo_pref_chan_disallowed(dev, apdev):
+    """P2P channel selection: GO preferred channels are disallowed"""
+    try:
+       dev[0].request("SET p2p_pref_chan 81:1,81:3,81:6,81:9,81:11")
+       dev[0].request("P2P_SET disallow_freq 2412,2422,2437,2452,2462")
+       for i in range(0, 5):
+           res = autogo(dev[0])
+           if res['freq'] in [ "2412", "2422", "2437", "2452", "2462" ]:
+               raise Exception("GO channel is disallowed")
+           dev[0].remove_group(res['ifname'])
+    finally:
+       dev[0].request("P2P_SET disallow_freq ")
+       dev[0].request("SET p2p_pref_chan ")
+
+def test_p2p_autogo_pref_chan_not_in_regulatory(dev, apdev):
+    """P2P channel selection: GO preferred channel not allowed in the regulatory rules"""
+    try:
+        set_country("US", dev[0])
+        dev[0].request("SET p2p_pref_chan 124:149")
+        res = autogo(dev[0], persistent=True)
+        if res['freq'] != "5745":
+            raise Exception("Unexpected channel selected: " + res['freq'])
+        dev[0].remove_group(res['ifname'])
+
+        netw = dev[0].list_networks(p2p=True)
+        if len(netw) != 1:
+            raise Exception("Unexpected number of network blocks: " + str(netw))
+        id = netw[0]['id']
+
+        set_country("DE", dev[0])
+        res = autogo(dev[0], persistent=id)
+        if res['freq'] == "5745":
+            raise Exception("Unexpected channel selected(2): " + res['freq'])
+        dev[0].remove_group(res['ifname'])
+    finally:
+        dev[0].request("SET p2p_pref_chan ")
+        set_country("00")
+
+def run_autogo(dev, param):
+    if "OK" not in dev.global_request("P2P_GROUP_ADD " + param):
+        raise Exception("P2P_GROUP_ADD failed: " + param)
+    ev = dev.wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+    if ev is None:
+        raise Exception("GO start up timed out")
+    res = dev.group_form_result(ev)
+    dev.remove_group()
+    return res
+
+def _test_autogo_ht_vht(dev):
+    res = run_autogo(dev[0], "ht40")
+
+    res = run_autogo(dev[0], "vht")
+
+    res = run_autogo(dev[0], "freq=2")
+    freq = int(res['freq'])
+    if freq < 2412 or freq > 2462:
+        raise Exception("Unexpected freq=2 channel: " + str(freq))
+
+    res = run_autogo(dev[0], "freq=5")
+    freq = int(res['freq'])
+    if freq < 5000 or freq >= 6000:
+        raise Exception("Unexpected freq=5 channel: " + str(freq))
+
+    res = run_autogo(dev[0], "freq=5 ht40 vht")
+    logger.info(str(res))
+    freq = int(res['freq'])
+    if freq < 5000 or freq >= 6000:
+        raise Exception("Unexpected freq=5 ht40 vht channel: " + str(freq))
+
+def test_autogo_ht_vht(dev):
+    """P2P autonomous GO with HT/VHT parameters"""
+    try:
+        set_country("US", dev[0])
+        _test_autogo_ht_vht(dev)
+    finally:
+        set_country("00")
+
+def test_p2p_listen_chan_optimize(dev, apdev):
+    """P2P listen channel optimization"""
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    addr5 = wpas.p2p_dev_addr()
+    try:
+        if "OK" not in wpas.request("SET p2p_optimize_listen_chan 1"):
+            raise Exception("Failed to set p2p_optimize_listen_chan")
+        wpas.p2p_listen()
+        if not dev[0].discover_peer(addr5):
+            raise Exception("Could not discover peer")
+        peer = dev[0].get_peer(addr5)
+        lfreq = peer['listen_freq']
+        wpas.p2p_stop_find()
+        dev[0].p2p_stop_find()
+
+        channel = "1" if lfreq != '2412' else "6"
+        freq = "2412" if lfreq != '2412' else "2437"
+        params = { "ssid": "test-open", "channel": channel }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+        id = wpas.connect("test-open", key_mgmt="NONE", scan_freq=freq)
+        wpas.p2p_listen()
+
+        if "OK" not in dev[0].request("P2P_FLUSH"):
+            raise Exception("P2P_FLUSH failed")
+        if not dev[0].discover_peer(addr5):
+            raise Exception("Could not discover peer")
+        peer = dev[0].get_peer(addr5)
+        lfreq2 = peer['listen_freq']
+        if lfreq == lfreq2:
+            raise Exception("Listen channel did not change")
+        if lfreq2 != freq:
+            raise Exception("Listen channel not on AP's operating channel")
+        wpas.p2p_stop_find()
+        dev[0].p2p_stop_find()
+
+        wpas.request("DISCONNECT")
+        wpas.wait_disconnected()
+
+        # for larger coverage, cover case of current channel matching
+        wpas.select_network(id)
+        wpas.wait_connected()
+        wpas.request("DISCONNECT")
+        wpas.wait_disconnected()
+
+        lchannel = "1" if channel != "1" else "6"
+        lfreq3 = "2412" if channel != "1" else "2437"
+        if "OK" not in wpas.request("P2P_SET listen_channel " + lchannel):
+            raise Exception("Failed to set listen channel")
+
+        wpas.select_network(id)
+        wpas.wait_connected()
+        wpas.p2p_listen()
+
+        if "OK" not in dev[0].request("P2P_FLUSH"):
+            raise Exception("P2P_FLUSH failed")
+        if not dev[0].discover_peer(addr5):
+            raise Exception("Could not discover peer")
+        peer = dev[0].get_peer(addr5)
+        lfreq4 = peer['listen_freq']
+        if lfreq4 != lfreq3:
+            raise Exception("Unexpected Listen channel after configuration")
+        wpas.p2p_stop_find()
+        dev[0].p2p_stop_find()
+    finally:
+        wpas.request("SET p2p_optimize_listen_chan 0")
+
+def test_p2p_channel_5ghz_only(dev):
+    """P2P GO start with only 5 GHz band allowed"""
+    try:
+        set_country("US", dev[0])
+        dev[0].request("P2P_SET disallow_freq 2400-2500")
+        res = autogo(dev[0])
+        freq = int(res['freq'])
+        if freq < 5000:
+            raise Exception("Unexpected channel %d MHz" % freq)
+        dev[0].remove_group()
+    finally:
+        set_country("00")
+        dev[0].request("P2P_SET disallow_freq ")
+
+def test_p2p_channel_5ghz_165_169_us(dev):
+    """P2P GO and 5 GHz channels 165 (allowed) and 169 (disallowed) in US"""
+    try:
+        set_country("US", dev[0])
+        res = dev[0].p2p_start_go(freq=5825)
+        if res['freq'] != "5825":
+            raise Exception("Unexpected frequency: " + res['freq'])
+        dev[0].remove_group()
+
+        res = dev[0].global_request("P2P_GROUP_ADD freq=5845")
+        if "FAIL" not in res:
+            raise Exception("GO on channel 169 allowed unexpectedly")
+    finally:
+        set_country("00")
+
+def test_p2p_go_move_reg_change(dev, apdev, params):
+    """P2P GO move due to regulatory change [long]"""
+    if not params['long']:
+        raise HwsimSkip("Skip test case with long duration due to --long not specified")
+
+    try:
+        set_country("US")
+        dev[0].global_request("P2P_SET disallow_freq 2400-5000")
+        res = autogo(dev[0])
+        freq1 = int(res['freq'])
+        if freq1 < 5000:
+            raise Exception("Unexpected channel %d MHz" % freq1)
+
+        dev[0].global_request("P2P_SET disallow_freq ")
+
+        # GO move is not allowed while waiting for initial client connection
+        time.sleep(20)
+        set_country("00")
+        ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP"],
+                                     timeout=10)
+        if ev is None:
+            raise Exception("P2P-REMOVE-AND-REFORM-GROUP not seen")
+
+        freq2 = dev[0].get_group_status_field('freq')
+        if freq1 == freq2:
+            raise Exception("Unexpected freq after group reform=" + freq2)
+
+        dev[0].remove_group()
+    finally:
+        dev[0].global_request("P2P_SET disallow_freq ")
+        set_country("00")
+
+def test_p2p_go_move_active(dev, apdev, params):
+    """P2P GO stays in freq although SCM is possible [long]"""
+    if dev[0].get_mcc() <= 1:
+        raise HwsimSkip("Skip due to MCC not being enabled")
+
+    if not params['long']:
+        raise HwsimSkip("Skip test case with long duration due to --long not specified")
+
+    dev[0].request("SET p2p_no_group_iface 0")
+    try:
+        dev[0].global_request("P2P_SET disallow_freq 2430-6000")
+        hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid" : 'ap-test',
+                                                    "channel" : '11' })
+        dev[0].connect("ap-test", key_mgmt="NONE",
+                       scan_freq="2462")
+
+        res = autogo(dev[0])
+        freq = int(res['freq'])
+        if freq > 2430:
+            raise Exception("Unexpected channel %d MHz" % freq)
+
+        # GO move is not allowed while waiting for initial client connection
+        time.sleep(20)
+        dev[0].global_request("P2P_SET disallow_freq ")
+
+        ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP"],
+                                     timeout=10)
+        if ev is not None:
+            raise Exception("Unexpected P2P-REMOVE-AND-REFORM-GROUP seen")
+
+        dev[0].remove_group()
+    finally:
+        dev[0].global_request("P2P_SET disallow_freq ")
+
+def test_p2p_go_move_scm(dev, apdev, params):
+    """P2P GO move due to SCM operation preference [long]"""
+    if dev[0].get_mcc() <= 1:
+        raise HwsimSkip("Skip due to MCC not being enabled")
+
+    if not params['long']:
+        raise HwsimSkip("Skip test case with long duration due to --long not specified")
+
+    dev[0].request("SET p2p_no_group_iface 0")
+    try:
+        dev[0].global_request("P2P_SET disallow_freq 2430-6000")
+        hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid" : 'ap-test',
+                                                    "channel" : '11' })
+        dev[0].connect("ap-test", key_mgmt="NONE",
+                       scan_freq="2462")
+
+        dev[0].global_request("SET p2p_go_freq_change_policy 0")
+        res = autogo(dev[0])
+        freq = int(res['freq'])
+        if freq > 2430:
+            raise Exception("Unexpected channel %d MHz" % freq)
+
+        # GO move is not allowed while waiting for initial client connection
+        time.sleep(20)
+        dev[0].global_request("P2P_SET disallow_freq ")
+
+        ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP"], timeout=3)
+        if ev is None:
+            raise Exception("P2P-REMOVE-AND-REFORM-GROUP not seen")
+
+        freq = dev[0].get_group_status_field('freq')
+        if freq != '2462':
+            raise Exception("Unexpected freq after group reform=" + freq)
+
+        dev[0].remove_group()
+    finally:
+        dev[0].global_request("P2P_SET disallow_freq ")
+        dev[0].global_request("SET p2p_go_freq_change_policy 2")
+
+def test_p2p_go_move_scm_peer_supports(dev, apdev, params):
+    """P2P GO move due to SCM operation preference (peer supports) [long]"""
+    if dev[0].get_mcc() <= 1:
+        raise HwsimSkip("Skip due to MCC not being enabled")
+
+    if not params['long']:
+        raise HwsimSkip("Skip test case with long duration due to --long not specified")
+
+    try:
+        dev[0].global_request("SET p2p_go_freq_change_policy 1")
+        set_country("US", dev[0])
+
+        dev[0].request("SET p2p_no_group_iface 0")
+        [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                               r_dev=dev[1], r_intent=0,
+                                               test_data=False)
+        check_grpform_results(i_res, r_res)
+        freq = int(i_res['freq'])
+        if freq < 5000:
+            raise Exception("Unexpected channel %d MHz - did not follow 5 GHz preference" % freq)
+
+        hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid" : 'ap-test',
+                                                    "channel" : '11' })
+        logger.info('Connecting client to to an AP on channel 11');
+        dev[0].connect("ap-test", key_mgmt="NONE",
+                       scan_freq="2462")
+
+        ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP"], timeout=3)
+        if ev is None:
+            raise Exception("P2P-REMOVE-AND-REFORM-GROUP not seen")
+
+        freq = dev[0].get_group_status_field('freq')
+        if freq != '2462':
+            raise Exception("Unexpected freq after group reform=" + freq)
+
+        dev[0].remove_group()
+    finally:
+        dev[0].global_request("SET p2p_go_freq_change_policy 2")
+        set_country("00")
+
+def test_p2p_go_move_scm_peer_does_not_support(dev, apdev, params):
+    """No P2P GO move due to SCM operation (peer does not supports) [long]"""
+    if dev[0].get_mcc() <= 1:
+        raise HwsimSkip("Skip due to MCC not being enabled")
+
+    if not params['long']:
+        raise HwsimSkip("Skip test case with long duration due to --long not specified")
+
+    try:
+        dev[0].global_request("SET p2p_go_freq_change_policy 1")
+        set_country("US", dev[0])
+
+        dev[0].request("SET p2p_no_group_iface 0")
+        if "OK" not in dev[1].request("DRIVER_EVENT AVOID_FREQUENCIES 2400-2500"):
+            raise Exception("Could not simulate driver event")
+        [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                               r_dev=dev[1], r_intent=0,
+                                               test_data=False)
+        check_grpform_results(i_res, r_res)
+        freq = int(i_res['freq'])
+        if freq < 5000:
+            raise Exception("Unexpected channel %d MHz - did not follow 5 GHz preference" % freq)
+
+        hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid" : 'ap-test',
+                                                    "channel" : '11' })
+        logger.info('Connecting client to to an AP on channel 11');
+        dev[0].connect("ap-test", key_mgmt="NONE",
+                       scan_freq="2462")
+
+        ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP"],
+                                     timeout=10)
+        if ev is not None:
+            raise Exception("Unexpected P2P-REMOVE-AND-REFORM-GROUP seen")
+
+        dev[0].remove_group()
+    finally:
+        dev[0].global_request("SET p2p_go_freq_change_policy 2")
+        set_country("00")
+
+def test_p2p_go_move_scm_multi(dev, apdev, params):
+    """P2P GO move due to SCM operation preference multiple times [long]"""
+    if dev[0].get_mcc() <= 1:
+        raise HwsimSkip("Skip due to MCC not being enabled")
+
+    if not params['long']:
+        raise HwsimSkip("Skip test case with long duration due to --long not specified")
+
+    dev[0].request("SET p2p_no_group_iface 0")
+    try:
+        dev[0].global_request("P2P_SET disallow_freq 2430-6000")
+        hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid" : 'ap-test-1',
+                                                    "channel" : '11' })
+        dev[0].connect("ap-test-1", key_mgmt="NONE",
+                       scan_freq="2462")
+
+        dev[0].global_request("SET p2p_go_freq_change_policy 0")
+        res = autogo(dev[0])
+        freq = int(res['freq'])
+        if freq > 2430:
+            raise Exception("Unexpected channel %d MHz" % freq)
+
+        # GO move is not allowed while waiting for initial client connection
+        time.sleep(20)
+        dev[0].global_request("P2P_SET disallow_freq ")
+
+        ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP"], timeout=3)
+        if ev is None:
+            raise Exception("P2P-REMOVE-AND-REFORM-GROUP not seen")
+
+        freq = dev[0].get_group_status_field('freq')
+        if freq != '2462':
+            raise Exception("Unexpected freq after group reform=" + freq)
+
+        hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid" : 'ap-test-2',
+                                                    "channel" : '6' })
+        dev[0].connect("ap-test-2", key_mgmt="NONE",
+                       scan_freq="2437")
+
+        ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP"], timeout=5)
+        if ev is None:
+            raise Exception("(2) P2P-REMOVE-AND-REFORM-GROUP not seen")
+
+        freq = dev[0].get_group_status_field('freq')
+        if freq != '2437':
+            raise Exception("(2) Unexpected freq after group reform=" + freq)
+
+        dev[0].remove_group()
+    finally:
+        dev[0].global_request("P2P_SET disallow_freq ")
+        dev[0].global_request("SET p2p_go_freq_change_policy 2")
diff --git a/hostap/tests/hwsim/test_p2p_concurrency.py b/hostap/tests/hwsim/test_p2p_concurrency.py
new file mode 100644
index 0000000..16be8c6
--- /dev/null
+++ b/hostap/tests/hwsim/test_p2p_concurrency.py
@@ -0,0 +1,285 @@
+# P2P concurrency test cases
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import subprocess
+import time
+
+import hwsim_utils
+import hostapd
+from test_p2p_grpform import go_neg_pin_authorized
+from test_p2p_grpform import go_neg_pbc
+from test_p2p_grpform import check_grpform_results
+from test_p2p_grpform import remove_group
+from test_p2p_persistent import form
+from test_p2p_persistent import invite_from_cli
+from test_p2p_persistent import invite_from_go
+from test_p2p_persistent import invite
+from test_ap_ht import clear_scan_cache
+from utils import HwsimSkip
+
+def test_concurrent_autogo(dev, apdev):
+    """Concurrent P2P autonomous GO"""
+    logger.info("Connect to an infrastructure AP")
+    dev[0].request("P2P_SET cross_connect 0")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+    logger.info("Start a P2P group while associated to an AP")
+    dev[0].request("SET p2p_no_group_iface 0")
+    dev[0].p2p_start_go()
+    pin = dev[1].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60,
+                             social=True)
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+
+    logger.info("Confirm AP connection after P2P group removal")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_concurrent_autogo_5ghz_ht40(dev, apdev):
+    """Concurrent P2P autonomous GO on 5 GHz and HT40 co-ex"""
+    clear_scan_cache(apdev[1]['ifname'])
+    try:
+        hapd = None
+        hapd2 = None
+        params = { "ssid": "ht40",
+                   "hw_mode": "a",
+                   "channel": "153",
+                   "country_code": "US",
+                   "ht_capab": "[HT40-]" }
+        hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+        params = { "ssid": "test-open-5",
+                   "hw_mode": "a",
+                   "channel": "149",
+                   "country_code": "US" }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+        dev[0].request("P2P_SET cross_connect 0")
+        dev[0].scan_for_bss(apdev[0]['bssid'], freq=5745)
+        dev[0].scan_for_bss(apdev[1]['bssid'], freq=5765)
+        dev[0].connect("test-open-5", key_mgmt="NONE", scan_freq="5745")
+
+        dev[0].request("SET p2p_no_group_iface 0")
+        if "OK" not in dev[0].global_request("P2P_GROUP_ADD ht40"):
+            raise Exception("P2P_GROUP_ADD failed")
+        ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
+        if ev is None:
+            raise Exception("GO start up timed out")
+        dev[0].group_form_result(ev)
+
+        pin = dev[1].wps_read_pin()
+        dev[0].p2p_go_authorize_client(pin)
+        dev[1].p2p_find(freq=5745)
+        addr0 = dev[0].p2p_dev_addr()
+        count = 0
+        while count < 10:
+            time.sleep(0.25)
+            count += 1
+            if dev[1].peer_known(addr0):
+                break
+        dev[1].p2p_connect_group(addr0, pin, timeout=60)
+
+        dev[0].remove_group()
+        dev[1].wait_go_ending_session()
+    finally:
+        dev[0].request("REMOVE_NETWORK all")
+        if hapd:
+            hapd.request("DISABLE")
+        if hapd2:
+            hapd2.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+        dev[1].flush_scan_cache()
+
+def test_concurrent_autogo_crossconnect(dev, apdev):
+    """Concurrent P2P autonomous GO"""
+    dev[0].global_request("P2P_SET cross_connect 1")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+
+    dev[0].global_request("SET p2p_no_group_iface 0")
+    dev[0].p2p_start_go(no_event_clear=True)
+    ev = dev[0].wait_global_event("P2P-CROSS-CONNECT-ENABLE", timeout=10)
+    if ev is None:
+        raise Exception("Timeout on cross connection enabled event")
+    if dev[0].group_ifname + " " + dev[0].ifname not in ev:
+        raise Exception("Unexpected interfaces: " + ev)
+    dev[0].dump_monitor()
+
+    dev[0].global_request("P2P_SET cross_connect 0")
+    ev = dev[0].wait_global_event("P2P-CROSS-CONNECT-DISABLE", timeout=10)
+    if ev is None:
+        raise Exception("Timeout on cross connection disabled event")
+    if dev[0].group_ifname + " " + dev[0].ifname not in ev:
+        raise Exception("Unexpected interfaces: " + ev)
+    dev[0].remove_group()
+
+    dev[0].global_request("P2P_SET cross_connect 1")
+    dev[0].p2p_start_go(no_event_clear=True)
+    ev = dev[0].wait_global_event("P2P-CROSS-CONNECT-ENABLE", timeout=10)
+    if ev is None:
+        raise Exception("Timeout on cross connection enabled event")
+    if dev[0].group_ifname + " " + dev[0].ifname not in ev:
+        raise Exception("Unexpected interfaces: " + ev)
+    dev[0].dump_monitor()
+    dev[0].remove_group()
+    ev = dev[0].wait_global_event("P2P-CROSS-CONNECT-DISABLE", timeout=10)
+    if ev is None:
+        raise Exception("Timeout on cross connection disabled event")
+    dev[0].global_request("P2P_SET cross_connect 0")
+
+def test_concurrent_p2pcli(dev, apdev):
+    """Concurrent P2P client join"""
+    logger.info("Connect to an infrastructure AP")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+    logger.info("Join a P2P group while associated to an AP")
+    dev[0].request("SET p2p_no_group_iface 0")
+    dev[1].p2p_start_go(freq=2412)
+    pin = dev[0].wps_read_pin()
+    dev[1].p2p_go_authorize_client(pin)
+    dev[0].p2p_connect_group(dev[1].p2p_dev_addr(), pin, timeout=60,
+                             social=True)
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    dev[1].remove_group()
+    dev[0].wait_go_ending_session()
+
+    logger.info("Confirm AP connection after P2P group removal")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_concurrent_grpform_go(dev, apdev):
+    """Concurrent P2P group formation to become GO"""
+    logger.info("Connect to an infrastructure AP")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+    logger.info("Form a P2P group while associated to an AP")
+    dev[0].request("SET p2p_no_group_iface 0")
+
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                           r_dev=dev[1], r_intent=0)
+    check_grpform_results(i_res, r_res)
+    remove_group(dev[0], dev[1])
+
+    logger.info("Confirm AP connection after P2P group removal")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_concurrent_grpform_cli(dev, apdev):
+    """Concurrent P2P group formation to become P2P Client"""
+    logger.info("Connect to an infrastructure AP")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+    logger.info("Form a P2P group while associated to an AP")
+    dev[0].request("SET p2p_no_group_iface 0")
+
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+                                           r_dev=dev[1], r_intent=15)
+    check_grpform_results(i_res, r_res)
+    remove_group(dev[0], dev[1])
+
+    logger.info("Confirm AP connection after P2P group removal")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_concurrent_grpform_while_connecting(dev, apdev):
+    """Concurrent P2P group formation while connecting to an AP"""
+    logger.info("Start connection to an infrastructure AP")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    dev[0].connect("test-open", key_mgmt="NONE", wait_connect=False)
+
+    logger.info("Form a P2P group while connecting to an AP")
+    dev[0].request("SET p2p_no_group_iface 0")
+
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_freq=2412,
+                                           r_dev=dev[1], r_freq=2412)
+    check_grpform_results(i_res, r_res)
+    remove_group(dev[0], dev[1])
+
+    logger.info("Confirm AP connection after P2P group removal")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_concurrent_grpform_while_connecting2(dev, apdev):
+    """Concurrent P2P group formation while connecting to an AP (2)"""
+    logger.info("Start connection to an infrastructure AP")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    dev[0].connect("test-open", key_mgmt="NONE", wait_connect=False)
+    dev[1].flush_scan_cache()
+
+    logger.info("Form a P2P group while connecting to an AP")
+    dev[0].request("SET p2p_no_group_iface 0")
+
+    [i_res, r_res] = go_neg_pbc(i_dev=dev[0], i_intent=15, i_freq=2412,
+                                r_dev=dev[1], r_intent=0, r_freq=2412)
+    check_grpform_results(i_res, r_res)
+    remove_group(dev[0], dev[1])
+
+    logger.info("Confirm AP connection after P2P group removal")
+    dev[0].wait_completed()
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_concurrent_grpform_while_connecting3(dev, apdev):
+    """Concurrent P2P group formation while connecting to an AP (3)"""
+    logger.info("Start connection to an infrastructure AP")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    dev[0].connect("test-open", key_mgmt="NONE", wait_connect=False)
+
+    logger.info("Form a P2P group while connecting to an AP")
+    dev[0].request("SET p2p_no_group_iface 0")
+
+    [i_res, r_res] = go_neg_pbc(i_dev=dev[1], i_intent=15, i_freq=2412,
+                                r_dev=dev[0], r_intent=0, r_freq=2412)
+    check_grpform_results(i_res, r_res)
+    remove_group(dev[0], dev[1])
+
+    logger.info("Confirm AP connection after P2P group removal")
+    dev[0].wait_completed()
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_concurrent_persistent_group(dev, apdev):
+    """Concurrent P2P persistent group"""
+    logger.info("Connect to an infrastructure AP")
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open", "channel": "2" })
+    dev[0].global_request("SET p2p_no_group_iface 0")
+    dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2417")
+
+    logger.info("Run persistent group test while associated to an AP")
+    form(dev[0], dev[1])
+    [go_res, cli_res] = invite_from_cli(dev[0], dev[1])
+    if go_res['freq'] != '2417':
+        raise Exception("Unexpected channel selected: " + go_res['freq'])
+    [go_res, cli_res] = invite_from_go(dev[0], dev[1])
+    if go_res['freq'] != '2417':
+        raise Exception("Unexpected channel selected: " + go_res['freq'])
+
+def test_concurrent_invitation_channel_mismatch(dev, apdev):
+    """P2P persistent group invitation and channel mismatch"""
+    if dev[0].get_mcc() > 1:
+        raise HwsimSkip("Skip due to MCC being enabled")
+
+    form(dev[0], dev[1])
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+
+    logger.info("Connect to an infrastructure AP")
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open", "channel": "2" })
+    dev[0].global_request("SET p2p_no_group_iface 0")
+    dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2417")
+    invite(dev[1], dev[0], extra="freq=2412")
+    ev = dev[1].wait_global_event(["P2P-INVITATION-RESULT"], timeout=15)
+    if ev is None:
+        raise Exception("P2P invitation result not received")
+    if "status=7" not in ev:
+        raise Exception("Unexpected P2P invitation result: " + ev)
diff --git a/hostap/tests/hwsim/test_p2p_device.py b/hostap/tests/hwsim/test_p2p_device.py
new file mode 100644
index 0000000..c7e6299
--- /dev/null
+++ b/hostap/tests/hwsim/test_p2p_device.py
@@ -0,0 +1,227 @@
+# cfg80211 P2P Device
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+
+from wpasupplicant import WpaSupplicant
+from test_p2p_grpform import go_neg_pin_authorized
+from test_p2p_grpform import check_grpform_results
+from test_p2p_grpform import remove_group
+from test_nfc_p2p import set_ip_addr_info, check_ip_addr, grpform_events
+from hwsim import HWSimRadio
+import hostapd
+import hwsim_utils
+
+def test_p2p_device_grpform(dev, apdev):
+    """P2P group formation with driver using cfg80211 P2P Device"""
+    with HWSimRadio(use_p2p_device=True) as (radio, iface):
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        wpas.interface_add(iface)
+        [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                               r_dev=wpas, r_intent=0)
+        check_grpform_results(i_res, r_res)
+        remove_group(dev[0], wpas)
+
+        res = wpas.global_request("IFNAME=p2p-dev-" + iface + " STATUS-DRIVER")
+        lines = res.splitlines()
+        found = False
+        for l in lines:
+            try:
+                [name,value] = l.split('=', 1)
+                if name == "wdev_id":
+                    found = True
+                    break
+            except ValueError:
+                pass
+        if not found:
+            raise Exception("wdev_id not found")
+
+def test_p2p_device_grpform2(dev, apdev):
+    """P2P group formation with driver using cfg80211 P2P Device (reverse)"""
+    with HWSimRadio(use_p2p_device=True) as (radio, iface):
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        wpas.interface_add(iface)
+        [i_res, r_res] = go_neg_pin_authorized(i_dev=wpas, i_intent=15,
+                                               r_dev=dev[0], r_intent=0)
+        check_grpform_results(i_res, r_res)
+        remove_group(wpas, dev[0])
+
+def test_p2p_device_group_remove(dev, apdev):
+    """P2P group removal via the P2P ctrl interface with driver using cfg80211 P2P Device"""
+    with HWSimRadio(use_p2p_device=True) as (radio, iface):
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        wpas.interface_add(iface)
+        [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                               r_dev=wpas, r_intent=0)
+        check_grpform_results(i_res, r_res)
+        # Issue the remove request on the interface which will be removed
+        p2p_iface_wpas = WpaSupplicant(ifname=r_res['ifname'])
+        res = p2p_iface_wpas.request("P2P_GROUP_REMOVE *")
+        if "OK" not in res:
+            raise Exception("Failed to remove P2P group")
+        ev = wpas.wait_global_event(["P2P-GROUP-REMOVED"], timeout=10)
+        if ev is None:
+            raise Exception("Group removal event not received")
+        if not wpas.global_ping():
+            raise Exception("Could not ping global ctrl_iface after group removal")
+
+def test_p2p_device_concurrent_scan(dev, apdev):
+    """Concurrent P2P and station mode scans with driver using cfg80211 P2P Device"""
+    with HWSimRadio(use_p2p_device=True) as (radio, iface):
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        wpas.interface_add(iface)
+        wpas.p2p_find()
+        time.sleep(0.1)
+        wpas.request("SCAN")
+        ev = wpas.wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=15)
+        if ev is None:
+            raise Exception("Station mode scan did not start")
+
+def test_p2p_device_nfc_invite(dev, apdev):
+    """P2P NFC invitiation with driver using cfg80211 P2P Device"""
+    with HWSimRadio(use_p2p_device=True) as (radio, iface):
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        wpas.interface_add(iface)
+
+        set_ip_addr_info(dev[0])
+        logger.info("Start autonomous GO")
+        dev[0].p2p_start_go()
+
+        logger.info("Write NFC Tag on the P2P Client")
+        res = wpas.global_request("P2P_LISTEN")
+        if "FAIL" in res:
+            raise Exception("Failed to start Listen mode")
+        pw = wpas.global_request("WPS_NFC_TOKEN NDEF").rstrip()
+        if "FAIL" in pw:
+            raise Exception("Failed to generate password token")
+        res = wpas.global_request("P2P_SET nfc_tag 1").rstrip()
+        if "FAIL" in res:
+            raise Exception("Failed to enable NFC Tag for P2P static handover")
+        sel = wpas.global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+        if "FAIL" in sel:
+            raise Exception("Failed to generate NFC connection handover select")
+
+        logger.info("Read NFC Tag on the GO to trigger invitation")
+        res = dev[0].global_request("WPS_NFC_TAG_READ " + sel)
+        if "FAIL" in res:
+            raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+        ev = wpas.wait_global_event(grpform_events, timeout=20)
+        if ev is None:
+            raise Exception("Joining the group timed out")
+        res = wpas.group_form_result(ev)
+        hwsim_utils.test_connectivity_p2p(dev[0], wpas)
+        check_ip_addr(res)
+
+def test_p2p_device_misuses(dev, apdev):
+    """cfg80211 P2P Device misuses"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    with HWSimRadio(use_p2p_device=True) as (radio, iface):
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        wpas.interface_add(iface)
+
+        # Add a normal network profile to the P2P Device management only
+        # interface to verify that it does not get used.
+        id = int(wpas.global_request('IFNAME=p2p-dev-%s ADD_NETWORK' % iface).strip())
+        wpas.global_request('IFNAME=p2p-dev-%s SET_NETWORK %d ssid "open"' % (iface, id))
+        wpas.global_request('IFNAME=p2p-dev-%s SET_NETWORK %d key_mgmt NONE' % (iface, id))
+        wpas.global_request('IFNAME=p2p-dev-%s ENABLE_NETWORK %d' % (iface, id))
+
+        # Scan requests get ignored on p2p-dev
+        wpas.global_request('IFNAME=p2p-dev-%s SCAN' % iface)
+
+        dev[0].p2p_start_go(freq=2412)
+        addr = dev[0].p2p_interface_addr()
+        wpas.scan_for_bss(addr, freq=2412)
+        wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
+        hwsim_utils.test_connectivity(wpas, hapd)
+
+        pin = wpas.wps_read_pin()
+        dev[0].p2p_go_authorize_client(pin)
+        res = wpas.p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60,
+                                     social=True, freq=2412)
+        hwsim_utils.test_connectivity_p2p(dev[0], wpas)
+
+        # Optimize scan-after-disconnect
+        wpas.group_request("SET_NETWORK 0 scan_freq 2412")
+
+        dev[0].group_request("DISASSOCIATE " + wpas.p2p_interface_addr())
+        ev = wpas.wait_group_event(["CTRL-EVENT-DISCONNECT"])
+        if ev is None:
+            raise Exception("Did not see disconnect event on P2P group interface")
+        dev[0].remove_group()
+
+        ev = wpas.wait_group_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+        if ev is None:
+            raise Exception("Scan not started")
+        ev = wpas.wait_group_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=15)
+        if ev is None:
+            raise Exception("Scan not completed")
+        time.sleep(1)
+        hwsim_utils.test_connectivity(wpas, hapd)
+
+        ev = hapd.wait_event([ "AP-STA-DISCONNECTED" ], timeout=0.1)
+        if ev is not None:
+            raise Exception("Unexpected disconnection event received from hostapd")
+        ev = wpas.wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.1)
+        if ev is not None:
+            raise Exception("Unexpected disconnection event received from wpa_supplicant")
+
+        wpas.request("DISCONNECT")
+        wpas.wait_disconnected()
+
+def test_p2p_device_incorrect_command_interface(dev, apdev):
+    """cfg80211 P2P Device and P2P_* command on incorrect interface"""
+    with HWSimRadio(use_p2p_device=True) as (radio, iface):
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        wpas.interface_add(iface)
+
+        dev[0].p2p_listen()
+        wpas.request('P2P_FIND type=social')
+        ev = wpas.wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+        if ev is None:
+            raise Exception("Peer not found")
+        ev = wpas.wait_event(["P2P-DEVICE-FOUND"], timeout=0.1)
+        if ev is not None:
+            raise Exception("Unexpected P2P-DEVICE-FOUND event on station interface")
+
+        pin = wpas.wps_read_pin()
+        dev[0].p2p_go_neg_auth(wpas.p2p_dev_addr(), pin, "enter", go_intent=14,
+                               freq=2412)
+        wpas.request('P2P_STOP_FIND')
+        if "OK" not in wpas.request('P2P_CONNECT ' + dev[0].p2p_dev_addr() + ' ' + pin + ' display go_intent=1'):
+            raise Exception("P2P_CONNECT failed")
+
+        ev = wpas.wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+        if ev is None:
+            raise Exception("Group formation timed out")
+        wpas.group_form_result(ev)
+
+        ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+        if ev is None:
+            raise Exception("Group formation timed out(2)")
+        dev[0].group_form_result(ev)
+
+        dev[0].remove_group()
+        wpas.wait_go_ending_session()
+
+def test_p2p_device_incorrect_command_interface2(dev, apdev):
+    """cfg80211 P2P Device and P2P_GROUP_ADD command on incorrect interface"""
+    with HWSimRadio(use_p2p_device=True) as (radio, iface):
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        wpas.interface_add(iface)
+
+        print wpas.request('P2P_GROUP_ADD')
+        ev = wpas.wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+        if ev is None:
+            raise Exception("Group formation timed out")
+        res = wpas.group_form_result(ev)
+        logger.info("Group results: " + str(res))
+        wpas.remove_group()
+        if not res['ifname'].startswith('p2p-' + iface + '-'):
+            raise Exception("Unexpected group ifname: " + res['ifname'])
diff --git a/hostap/tests/hwsim/test_p2p_discovery.py b/hostap/tests/hwsim/test_p2p_discovery.py
new file mode 100644
index 0000000..fbd3f83
--- /dev/null
+++ b/hostap/tests/hwsim/test_p2p_discovery.py
@@ -0,0 +1,477 @@
+# P2P device discovery test cases
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+
+import hwsim_utils
+from wpasupplicant import WpaSupplicant
+
+def test_discovery(dev):
+    """P2P device discovery and provision discovery"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    logger.info("Start device discovery")
+    dev[0].p2p_find(social=True, delay=1)
+    if not dev[1].discover_peer(addr0):
+        raise Exception("Device discovery timed out")
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Device discovery timed out")
+
+    logger.info("Test provision discovery for display")
+    dev[0].global_request("P2P_PROV_DISC " + addr1 + " display")
+    ev1 = dev[1].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=15)
+    if ev1 is None:
+        raise Exception("Provision discovery timed out (display/dev1)")
+    if addr0 not in ev1:
+        raise Exception("Dev0 not in provision discovery event")
+    ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-ENTER-PIN",
+                                    "P2P-PROV-DISC-FAILURE"], timeout=15)
+    if ev0 is None:
+        raise Exception("Provision discovery timed out (display/dev0)")
+    if "P2P-PROV-DISC-FAILURE" in ev0:
+        raise Exception("Provision discovery failed (display/dev0)")
+    if addr1 not in ev0:
+        raise Exception("Dev1 not in provision discovery event")
+
+    logger.info("Test provision discovery for keypad")
+    dev[0].global_request("P2P_PROV_DISC " + addr1 + " keypad")
+    ev1 = dev[1].wait_global_event(["P2P-PROV-DISC-ENTER-PIN"], timeout=15)
+    if ev1 is None:
+        raise Exception("Provision discovery timed out (keypad/dev1)")
+    if addr0 not in ev1:
+        raise Exception("Dev0 not in provision discovery event")
+    ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN",
+                                    "P2P-PROV-DISC-FAILURE"],
+                                   timeout=15)
+    if ev0 is None:
+        raise Exception("Provision discovery timed out (keypad/dev0)")
+    if "P2P-PROV-DISC-FAILURE" in ev0:
+        raise Exception("Provision discovery failed (keypad/dev0)")
+    if addr1 not in ev0:
+        raise Exception("Dev1 not in provision discovery event")
+
+    logger.info("Test provision discovery for push button")
+    dev[0].global_request("P2P_PROV_DISC " + addr1 + " pbc")
+    ev1 = dev[1].wait_global_event(["P2P-PROV-DISC-PBC-REQ"], timeout=15)
+    if ev1 is None:
+        raise Exception("Provision discovery timed out (pbc/dev1)")
+    if addr0 not in ev1:
+        raise Exception("Dev0 not in provision discovery event")
+    ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-PBC-RESP",
+                                    "P2P-PROV-DISC-FAILURE"],
+                                   timeout=15)
+    if ev0 is None:
+        raise Exception("Provision discovery timed out (pbc/dev0)")
+    if "P2P-PROV-DISC-FAILURE" in ev0:
+        raise Exception("Provision discovery failed (pbc/dev0)")
+    if addr1 not in ev0:
+        raise Exception("Dev1 not in provision discovery event")
+
+    dev[0].p2p_stop_find
+    dev[1].p2p_stop_find
+
+    if "FAIL" not in dev[0].p2p_find(dev_id="foo"):
+        raise Exception("P2P_FIND with invalid dev_id accepted")
+    if "FAIL" not in dev[0].p2p_find(dev_type="foo"):
+        raise Exception("P2P_FIND with invalid dev_type accepted")
+    if "FAIL" not in dev[0].p2p_find(dev_type="1-foo-2"):
+        raise Exception("P2P_FIND with invalid dev_type accepted")
+    if "FAIL" not in dev[0].p2p_find(dev_type="1-11223344"):
+        raise Exception("P2P_FIND with invalid dev_type accepted")
+
+    if "FAIL" not in dev[0].global_request("P2P_PROV_DISC foo pbc"):
+        raise Exception("Invalid P2P_PROV_DISC accepted")
+    if "FAIL" not in dev[0].global_request("P2P_PROV_DISC 00:11:22:33:44:55"):
+        raise Exception("Invalid P2P_PROV_DISC accepted")
+    if "FAIL" not in dev[0].global_request("P2P_PROV_DISC 00:11:22:33:44:55 pbc join"):
+        raise Exception("Invalid P2P_PROV_DISC accepted")
+    if "FAIL" not in dev[0].global_request("P2P_PROV_DISC 00:11:22:33:44:55 foo"):
+        raise Exception("Invalid P2P_PROV_DISC accepted")
+
+def test_discovery_pd_retries(dev):
+    """P2P device discovery and provision discovery retries"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Device discovery timed out")
+    dev[1].p2p_stop_find()
+    dev[0].p2p_stop_find()
+    dev[0].global_request("P2P_PROV_DISC " + addr1 + " display")
+    ev = dev[0].wait_global_event(["P2P-PROV-DISC-FAILURE"], timeout=60)
+    if ev is None:
+        raise Exception("No PD failure reported")
+
+def test_discovery_group_client(dev):
+    """P2P device discovery for a client in a group"""
+    logger.info("Start autonomous GO " + dev[0].ifname)
+    res = dev[0].p2p_start_go(freq="2422")
+    logger.debug("res: " + str(res))
+    logger.info("Connect a client to the GO")
+    pin = dev[1].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60)
+    logger.info("Client connected")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+    logger.info("Try to discover a P2P client in a group")
+    if not dev[2].discover_peer(dev[1].p2p_dev_addr(), social=False, timeout=10):
+        if not dev[2].discover_peer(dev[1].p2p_dev_addr(), social=False, timeout=10):
+            if not dev[2].discover_peer(dev[1].p2p_dev_addr(), social=False, timeout=10):
+                raise Exception("Could not discover group client")
+
+    # This is not really perfect, but something to get a bit more testing
+    # coverage.. For proper discoverability mechanism validation, the P2P
+    # client would need to go to sleep to avoid acknowledging the GO Negotiation
+    # Request frame. Offchannel Listen mode operation on the P2P Client with
+    # mac80211_hwsim is apparently not enough to avoid the acknowledgement on
+    # the operating channel, so need to disconnect from the group which removes
+    # the GO-to-P2P Client part of the discoverability exchange in practice.
+
+    pin = dev[2].wps_read_pin()
+    # make group client non-responsive on operating channel
+    dev[1].dump_monitor()
+    dev[1].group_request("DISCONNECT")
+    ev = dev[1].wait_group_event(["CTRL-EVENT-DISCONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on waiting disconnection")
+    dev[2].request("P2P_CONNECT {} {} display".format(dev[1].p2p_dev_addr(),
+                                                      pin))
+    ev = dev[1].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=2)
+    if ev:
+        raise Exception("Unexpected frame RX on P2P client")
+    # make group client available on operating channe
+    dev[1].group_request("REASSOCIATE")
+    ev = dev[1].wait_global_event(["CTRL-EVENT-CONNECTED",
+                                   "P2P-GO-NEG-REQUEST"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on reconnection to group")
+    if "P2P-GO-NEG-REQUEST" not in ev:
+        ev = dev[1].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=10)
+        if ev is None:
+            raise Exception("Timeout on waiting for GO Negotiation Request")
+
+def test_discovery_dev_type(dev):
+    """P2P device discovery with Device Type filter"""
+    dev[1].request("SET sec_device_type 1-0050F204-2")
+    dev[1].p2p_listen()
+    dev[0].p2p_find(social=True, dev_type="5-0050F204-1")
+    ev = dev[0].wait_global_event(['P2P-DEVICE-FOUND'], timeout=1)
+    if ev:
+        raise Exception("Unexpected P2P device found")
+    dev[0].p2p_find(social=True, dev_type="1-0050F204-2")
+    ev = dev[0].wait_global_event(['P2P-DEVICE-FOUND'], timeout=2)
+    if ev is None:
+        raise Exception("P2P device not found")
+    peer = dev[0].get_peer(dev[1].p2p_dev_addr())
+    if "1-0050F204-2" not in peer['sec_dev_type']:
+        raise Exception("sec_device_type not reported properly")
+
+def test_discovery_dev_type_go(dev):
+    """P2P device discovery with Device Type filter on GO"""
+    addr1 = dev[1].p2p_dev_addr()
+    dev[1].request("SET sec_device_type 1-0050F204-2")
+    res = dev[0].p2p_start_go(freq="2412")
+    pin = dev[1].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60)
+
+    dev[2].p2p_find(social=True, dev_type="5-0050F204-1")
+    ev = dev[2].wait_global_event(['P2P-DEVICE-FOUND'], timeout=1)
+    if ev:
+        raise Exception("Unexpected P2P device found")
+    dev[2].p2p_find(social=True, dev_type="1-0050F204-2")
+    ev = dev[2].wait_global_event(['P2P-DEVICE-FOUND ' + addr1], timeout=2)
+    if ev is None:
+        raise Exception("P2P device not found")
+
+def test_discovery_dev_id(dev):
+    """P2P device discovery with Device ID filter"""
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    wpas.request("P2P_LISTEN 1")
+    status = wpas.global_request("STATUS")
+    if "p2p_state=LISTEN_ONLY" not in status:
+        raise Exception("Unexpected status: " + status)
+    addr1 = dev[1].p2p_dev_addr()
+    dev[1].p2p_listen()
+    dev[0].p2p_find(social=True, dev_id="02:03:04:05:06:07")
+    ev = dev[0].wait_global_event(['P2P-DEVICE-FOUND'], timeout=1)
+    if ev:
+        raise Exception("Unexpected P2P device found")
+    dev[0].p2p_find(social=True, dev_id=addr1)
+    ev = dev[0].wait_global_event(['P2P-DEVICE-FOUND'], timeout=5)
+    if ev is None:
+        raise Exception("P2P device not found")
+    if addr1 not in ev:
+        raise Exception("Unexpected P2P peer found")
+    status = wpas.global_request("STATUS")
+    for i in range(0, 2):
+        if "p2p_state=IDLE" in status:
+            break
+        time.sleep(0.5)
+        status = wpas.global_request("STATUS")
+    if "p2p_state=IDLE" not in status:
+        raise Exception("Unexpected status: " + status)
+
+def test_discovery_dev_id_go(dev):
+    """P2P device discovery with Device ID filter on GO"""
+    addr1 = dev[1].p2p_dev_addr()
+    res = dev[0].p2p_start_go(freq="2412")
+    pin = dev[1].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60)
+
+    dev[2].p2p_find(social=True, dev_id="02:03:04:05:06:07")
+    ev = dev[2].wait_global_event(['P2P-DEVICE-FOUND'], timeout=1)
+    if ev:
+        raise Exception("Unexpected P2P device found")
+    dev[2].p2p_find(social=True, dev_id=addr1)
+    ev = dev[2].wait_global_event(['P2P-DEVICE-FOUND ' + addr1], timeout=2)
+    if ev is None:
+        raise Exception("P2P device not found")
+
+def test_discovery_social_plus_one(dev):
+    """P2P device discovery with social-plus-one"""
+    logger.info("Start autonomous GO " + dev[0].ifname)
+    dev[1].p2p_find(social=True)
+    dev[0].p2p_find(progressive=True)
+    logger.info("Wait for initial progressive find phases")
+    dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
+    dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
+    go = dev[2].p2p_dev_addr()
+    dev[2].p2p_start_go(freq="2422")
+    logger.info("Verify whether the GO on non-social channel can be found")
+    ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+    if ev is None:
+        raise Exception("Peer not found")
+    if go not in ev:
+        ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+        if ev is None:
+            raise Exception("Peer not found")
+    ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+    if ev is None:
+        raise Exception("Peer not found")
+    dev[0].p2p_stop_find()
+    dev[1].p2p_stop_find()
+    if not dev[0].peer_known(go):
+        raise Exception("GO not found in progressive scan")
+    if dev[1].peer_known(go):
+        raise Exception("GO found in social-only scan")
+
+def test_discovery_and_interface_disabled(dev):
+    """P2P device discovery with interface getting didabled"""
+    try:
+        if "OK" not in dev[0].p2p_find():
+            raise Exception("Failed to start P2P find")
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
+        if ev is None:
+            raise Exception("Scan did not start")
+        dev[0].request("DRIVER_EVENT INTERFACE_DISABLED")
+        time.sleep(1)
+
+        # verify that P2P_FIND is rejected
+        if "FAIL" not in dev[0].p2p_find():
+            raise Exception("New P2P_FIND request was accepted unexpectedly")
+
+        dev[0].request("DRIVER_EVENT INTERFACE_ENABLED")
+        time.sleep(3)
+        dev[0].scan(freq="2412")
+        if "OK" not in dev[0].p2p_find():
+            raise Exception("Failed to start P2P find")
+        dev[0].dump_monitor()
+        dev[1].p2p_listen()
+        ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+        if ev is None:
+            raise Exception("Peer not found")
+    finally:
+        dev[0].request("DRIVER_EVENT INTERFACE_ENABLED")
+
+def test_discovery_auto(dev):
+    """P2P device discovery and provision discovery with auto GO/dev selection"""
+    dev[0].flush_scan_cache()
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    addr2 = dev[2].p2p_dev_addr()
+    dev[2].p2p_start_go(freq="2412")
+    logger.info("Start device discovery")
+    dev[0].p2p_listen()
+    if not dev[1].discover_peer(addr0):
+        raise Exception("Device discovery timed out")
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Device discovery timed out")
+    if not dev[0].discover_peer(addr2):
+        raise Exception("Device discovery timed out")
+
+    logger.info("Test provision discovery for display (device)")
+    dev[0].global_request("P2P_PROV_DISC " + addr1 + " display auto")
+    ev1 = dev[1].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=15)
+    if ev1 is None:
+        raise Exception("Provision discovery timed out (display/dev1)")
+    if addr0 not in ev1:
+        raise Exception("Dev0 not in provision discovery event")
+    if " group=" in ev1:
+        raise Exception("Unexpected group parameter from non-GO")
+    ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-ENTER-PIN",
+                                    "P2P-PROV-DISC-FAILURE"], timeout=15)
+    if ev0 is None:
+        raise Exception("Provision discovery timed out (display/dev0)")
+    if "P2P-PROV-DISC-FAILURE" in ev0:
+        raise Exception("Provision discovery failed (display/dev0)")
+    if addr1 not in ev0:
+        raise Exception("Dev1 not in provision discovery event")
+    if "peer_go=0" not in ev0:
+        raise Exception("peer_go incorrect in PD response from non-GO")
+
+    logger.info("Test provision discovery for display (GO)")
+    dev[0].global_request("P2P_PROV_DISC " + addr2 + " display auto")
+    ev2 = dev[2].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=15)
+    if ev2 is None:
+        raise Exception("Provision discovery timed out (display/dev2)")
+    if addr0 not in ev2:
+        raise Exception("Dev0 not in provision discovery event")
+    if " group=" not in ev2:
+        raise Exception("Group parameter missing from GO")
+    ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-ENTER-PIN",
+                                    "P2P-PROV-DISC-FAILURE"], timeout=15)
+    if ev0 is None:
+        raise Exception("Provision discovery timed out (display/dev0)")
+    if "P2P-PROV-DISC-FAILURE" in ev0:
+        raise Exception("Provision discovery failed (display/dev0)")
+    if addr2 not in ev0:
+        raise Exception("Dev1 not in provision discovery event")
+    if "peer_go=1" not in ev0:
+        raise Exception("peer_go incorrect in PD response from GO")
+
+def test_discovery_stop(dev):
+    """P2P device discovery and p2p_stop_find"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    dev[1].p2p_listen()
+    dev[2].p2p_listen()
+
+    dev[0].p2p_find(social=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.5)
+    if ev is None:
+        logger.info("No CTRL-EVENT-SCAN-STARTED event")
+    dev[0].p2p_stop_find()
+    ev = dev[0].wait_global_event(["P2P-FIND-STOPPED"], timeout=1)
+    if ev is None:
+        raise Exception("P2P_STOP not reported")
+    ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+    if ev is not None:
+        raise Exception("Peer found unexpectedly: " + ev)
+
+    dev[0].p2p_find(social=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.5)
+    if ev is None:
+        logger.info("No CTRL-EVENT-SCAN-STARTED event")
+    dev[0].global_request("P2P_FLUSH")
+    ev = dev[0].wait_global_event(["P2P-FIND-STOPPED"], timeout=1)
+    if ev is None:
+        raise Exception("P2P_STOP not reported")
+    ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+    if ev is not None:
+        raise Exception("Peer found unexpectedly: " + ev)
+
+def test_p2p_peer_command(dev):
+    """P2P_PEER command"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    addr2 = dev[2].p2p_dev_addr()
+    dev[1].p2p_listen()
+    dev[2].p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Device discovery timed out")
+    if not dev[0].discover_peer(addr2):
+        raise Exception("Device discovery timed out")
+    dev[0].p2p_stop_find()
+    dev[1].p2p_stop_find()
+    dev[2].p2p_stop_find()
+
+    res0 = dev[0].request("P2P_PEER FIRST")
+    peer = res0.splitlines()[0]
+    if peer not in [ addr1, addr2 ]:
+        raise Exception("Unexpected P2P_PEER FIRST address")
+    res1 = dev[0].request("P2P_PEER NEXT-" + peer)
+    peer2 = res1.splitlines()[0]
+    if peer2 not in [ addr1, addr2 ] or peer == peer2:
+        raise Exception("Unexpected P2P_PEER NEXT address")
+
+    if "FAIL" not in dev[0].request("P2P_PEER NEXT-foo"):
+        raise Exception("Invalid P2P_PEER command accepted")
+    if "FAIL" not in dev[0].request("P2P_PEER foo"):
+        raise Exception("Invalid P2P_PEER command accepted")
+    if "FAIL" not in dev[0].request("P2P_PEER 00:11:22:33:44:55"):
+        raise Exception("P2P_PEER command for unknown peer accepted")
+
+def test_p2p_listen_and_offchannel_tx(dev):
+    """P2P_LISTEN behavior with offchannel TX"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    addr2 = dev[2].p2p_dev_addr()
+
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Device discovery timed out")
+
+    dev[0].p2p_listen()
+    dev[0].global_request("P2P_PROV_DISC " + addr1 + " display")
+    ev = dev[0].wait_global_event(["P2P-PROV-DISC-ENTER-PIN"], timeout=15)
+    if ev is None:
+        raise Exception("No PD result reported")
+    dev[1].p2p_stop_find()
+
+    if not dev[2].discover_peer(addr0):
+        raise Exception("Device discovery timed out after PD exchange")
+    dev[2].p2p_stop_find()
+    dev[0].p2p_stop_find()
+
+def test_p2p_listen_and_scan(dev):
+    """P2P_LISTEN and scan"""
+    dev[0].p2p_listen()
+    if "OK" not in dev[0].request("SCAN freq=2412"):
+        raise Exception("Failed to request a scan")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 3)
+    if ev is not None:
+        raise Exception("Unexpected scan results")
+    dev[0].p2p_stop_find()
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+    if ev is None:
+        raise Exception("Scan timed out")
+
+def test_p2p_config_methods(dev):
+    """P2P and WPS config method update"""
+    addr0 = dev[0].p2p_dev_addr()
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    addr1 = wpas.p2p_dev_addr()
+
+    if "OK" not in wpas.request("SET config_methods keypad virtual_push_button"):
+        raise Exception("Failed to set config_methods")
+
+    wpas.p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Device discovery timed out")
+    dev[0].p2p_stop_find()
+    peer = dev[0].get_peer(addr1)
+    if peer['config_methods'] != '0x180':
+        raise Exception("Unexpected peer config methods(1): " + peer['config_methods'])
+    dev[0].global_request("P2P_FLUSH")
+
+    if "OK" not in wpas.request("SET config_methods virtual_display"):
+        raise Exception("Failed to set config_methods")
+
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Device discovery timed out")
+    dev[0].p2p_stop_find()
+    peer = dev[0].get_peer(addr1)
+    if peer['config_methods'] != '0x8':
+        raise Exception("Unexpected peer config methods(2): " + peer['config_methods'])
+
+    wpas.p2p_stop_find()
diff --git a/hostap/tests/hwsim/test_p2p_ext.py b/hostap/tests/hwsim/test_p2p_ext.py
new file mode 100644
index 0000000..7385e97
--- /dev/null
+++ b/hostap/tests/hwsim/test_p2p_ext.py
@@ -0,0 +1,361 @@
+# P2P vendor specific extension tests
+# Copyright (c) 2014-2015, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import os
+
+from tshark import run_tshark
+from test_p2p_persistent import form
+
+def test_p2p_ext_discovery(dev):
+    """P2P device discovery with vendor specific extensions"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+
+    try:
+        if "OK" not in dev[0].request("VENDOR_ELEM_ADD 1 dd050011223344"):
+            raise Exception("VENDOR_ELEM_ADD failed")
+        res = dev[0].request("VENDOR_ELEM_GET 1")
+        if res != "dd050011223344":
+            raise Exception("Unexpected VENDOR_ELEM_GET result: " + res)
+        if "OK" not in dev[0].request("VENDOR_ELEM_ADD 1 dd06001122335566"):
+            raise Exception("VENDOR_ELEM_ADD failed")
+        res = dev[0].request("VENDOR_ELEM_GET 1")
+        if res != "dd050011223344dd06001122335566":
+            raise Exception("Unexpected VENDOR_ELEM_GET result(2): " + res)
+        res = dev[0].request("VENDOR_ELEM_GET 2")
+        if res != "":
+            raise Exception("Unexpected VENDOR_ELEM_GET result(3): " + res)
+        if "OK" not in dev[0].request("VENDOR_ELEM_REMOVE 1 dd050011223344"):
+            raise Exception("VENDOR_ELEM_REMOVE failed")
+        res = dev[0].request("VENDOR_ELEM_GET 1")
+        if res != "dd06001122335566":
+            raise Exception("Unexpected VENDOR_ELEM_GET result(4): " + res)
+        if "OK" not in dev[0].request("VENDOR_ELEM_REMOVE 1 dd06001122335566"):
+            raise Exception("VENDOR_ELEM_REMOVE failed")
+        res = dev[0].request("VENDOR_ELEM_GET 1")
+        if res != "":
+            raise Exception("Unexpected VENDOR_ELEM_GET result(5): " + res)
+        if "OK" not in dev[0].request("VENDOR_ELEM_ADD 1 dd050011223344dd06001122335566"):
+            raise Exception("VENDOR_ELEM_ADD failed(2)")
+
+        if "FAIL" not in dev[0].request("VENDOR_ELEM_REMOVE 1 dd051122334455"):
+            raise Exception("Unexpected VENDOR_ELEM_REMOVE success")
+        if "FAIL" not in dev[0].request("VENDOR_ELEM_REMOVE 1 dd"):
+            raise Exception("Unexpected VENDOR_ELEM_REMOVE success(2)")
+        if "FAIL" not in dev[0].request("VENDOR_ELEM_ADD 1 ddff"):
+            raise Exception("Unexpected VENDOR_ELEM_ADD success(3)")
+
+        dev[0].p2p_listen()
+        if not dev[1].discover_peer(addr0):
+            raise Exception("Device discovery timed out")
+        if not dev[0].discover_peer(addr1):
+            raise Exception("Device discovery timed out")
+
+        peer = dev[1].get_peer(addr0)
+        if peer['vendor_elems'] != "dd050011223344dd06001122335566":
+            raise Exception("Vendor elements not reported correctly")
+
+        res = dev[0].request("VENDOR_ELEM_GET 1")
+        if res != "dd050011223344dd06001122335566":
+            raise Exception("Unexpected VENDOR_ELEM_GET result(6): " + res)
+        if "OK" not in dev[0].request("VENDOR_ELEM_REMOVE 1 dd06001122335566"):
+            raise Exception("VENDOR_ELEM_REMOVE failed")
+        res = dev[0].request("VENDOR_ELEM_GET 1")
+        if res != "dd050011223344":
+            raise Exception("Unexpected VENDOR_ELEM_GET result(7): " + res)
+    finally:
+        dev[0].request("VENDOR_ELEM_REMOVE 1 *")
+
+def test_p2p_ext_discovery_go(dev):
+    """P2P device discovery with vendor specific extensions for GO"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+
+    try:
+        if "OK" not in dev[0].request("VENDOR_ELEM_ADD 2 dd050011223344dd06001122335566"):
+            raise Exception("VENDOR_ELEM_ADD failed")
+        if "OK" not in dev[0].request("VENDOR_ELEM_ADD 3 dd050011223344dd06001122335566"):
+            raise Exception("VENDOR_ELEM_ADD failed")
+        if "OK" not in dev[0].request("VENDOR_ELEM_ADD 12 dd050011223344dd06001122335566"):
+            raise Exception("VENDOR_ELEM_ADD failed")
+
+        dev[0].p2p_start_go(freq="2412")
+        if not dev[1].discover_peer(addr0):
+            raise Exception("Device discovery timed out")
+        peer = dev[1].get_peer(addr0)
+        if peer['vendor_elems'] != "dd050011223344dd06001122335566":
+            print peer['vendor_elems']
+            raise Exception("Vendor elements not reported correctly")
+    finally:
+        dev[0].request("VENDOR_ELEM_REMOVE 2 *")
+        dev[0].request("VENDOR_ELEM_REMOVE 3 *")
+        dev[0].request("VENDOR_ELEM_REMOVE 12 *")
+
+def test_p2p_ext_vendor_elem_probe_req(dev):
+    """VENDOR_ELEM in P2P Probe Request frames"""
+    try:
+        _test_p2p_ext_vendor_elem_probe_req(dev)
+    finally:
+        dev[0].request("VENDOR_ELEM_REMOVE 0 *")
+
+def _test_p2p_ext_vendor_elem_probe_req(dev):
+    addr1 = dev[1].p2p_dev_addr()
+    if "OK" not in dev[0].request("VENDOR_ELEM_ADD 0 dd050011223300"):
+        raise Exception("VENDOR_ELEM_ADD failed")
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Device discovery timed out")
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+    ev = dev[1].wait_event(["MGMT-RX"], timeout=5)
+    if ev is None:
+        raise Exception("MGMT-RX timeout")
+    if " 40" not in ev:
+        raise Exception("Not a Probe Request frame")
+    if "dd050011223300" not in ev:
+        raise Exception("Vendor element not found from Probe Request frame")
+    dev[0].p2p_stop_find()
+    dev[1].p2p_stop_find()
+
+def test_p2p_ext_vendor_elem_pd_req(dev):
+    """VENDOR_ELEM in PD Request frames"""
+    try:
+        _test_p2p_ext_vendor_elem_pd_req(dev)
+    finally:
+        dev[0].request("VENDOR_ELEM_REMOVE 4 *")
+
+def _test_p2p_ext_vendor_elem_pd_req(dev):
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    if "OK" not in dev[0].request("VENDOR_ELEM_ADD 4 dd050011223301"):
+        raise Exception("VENDOR_ELEM_ADD failed")
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Device discovery timed out")
+    dev[0].p2p_stop_find()
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+    dev[0].global_request("P2P_PROV_DISC " + addr1 + " display")
+    for i in range(5):
+        ev = dev[1].wait_event(["MGMT-RX"], timeout=5)
+        if ev is None:
+            raise Exception("MGMT-RX timeout")
+        if " d0" in ev:
+            break
+    if "dd050011223301" not in ev:
+        raise Exception("Vendor element not found from PD Request frame")
+    dev[1].p2p_stop_find()
+    dev[0].p2p_stop_find()
+
+def test_p2p_ext_vendor_elem_pd_resp(dev):
+    """VENDOR_ELEM in PD Response frames"""
+    try:
+        _test_p2p_ext_vendor_elem_pd_resp(dev)
+    finally:
+        dev[0].request("VENDOR_ELEM_REMOVE 5 *")
+
+def _test_p2p_ext_vendor_elem_pd_resp(dev):
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    if "OK" not in dev[0].request("VENDOR_ELEM_ADD 5 dd050011223302"):
+        raise Exception("VENDOR_ELEM_ADD failed")
+    dev[0].p2p_listen()
+    if not dev[1].discover_peer(addr0):
+        raise Exception("Device discovery timed out")
+    dev[1].p2p_stop_find()
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+    dev[1].global_request("P2P_PROV_DISC " + addr0 + " display")
+    for i in range(5):
+        ev = dev[1].wait_event(["MGMT-RX"], timeout=5)
+        if ev is None:
+            raise Exception("MGMT-RX timeout")
+        if " d0" in ev:
+            break
+    if "dd050011223302" not in ev:
+        raise Exception("Vendor element not found from PD Response frame")
+    dev[0].p2p_stop_find()
+    dev[1].p2p_stop_find()
+
+def test_p2p_ext_vendor_elem_go_neg_req(dev):
+    """VENDOR_ELEM in GO Negotiation Request frames"""
+    try:
+        _test_p2p_ext_vendor_elem_go_neg_req(dev)
+    finally:
+        dev[0].request("VENDOR_ELEM_REMOVE 6 *")
+
+def _test_p2p_ext_vendor_elem_go_neg_req(dev):
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    if "OK" not in dev[0].request("VENDOR_ELEM_ADD 6 dd050011223303"):
+        raise Exception("VENDOR_ELEM_ADD failed")
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Device discovery timed out")
+    dev[0].p2p_stop_find()
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+    dev[0].global_request("P2P_CONNECT " + addr1 + " 12345670 display")
+    for i in range(5):
+        ev = dev[1].wait_event(["MGMT-RX"], timeout=5)
+        if ev is None:
+            raise Exception("MGMT-RX timeout")
+        if " d0" in ev:
+            break
+    if "dd050011223303" not in ev:
+        raise Exception("Vendor element not found from GO Negotiation Request frame")
+    dev[1].p2p_stop_find()
+    dev[0].p2p_stop_find()
+
+def test_p2p_ext_vendor_elem_go_neg_resp(dev):
+    """VENDOR_ELEM in GO Negotiation Response frames"""
+    try:
+        _test_p2p_ext_vendor_elem_go_neg_resp(dev)
+    finally:
+        dev[0].request("VENDOR_ELEM_REMOVE 7 *")
+
+def _test_p2p_ext_vendor_elem_go_neg_resp(dev):
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    if "OK" not in dev[0].request("VENDOR_ELEM_ADD 7 dd050011223304"):
+        raise Exception("VENDOR_ELEM_ADD failed")
+    dev[0].p2p_listen()
+    if not dev[1].discover_peer(addr0):
+        raise Exception("Device discovery timed out")
+    dev[1].p2p_stop_find()
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+    dev[1].global_request("P2P_CONNECT " + addr0 + " 12345670 display")
+    for i in range(5):
+        ev = dev[1].wait_event(["MGMT-RX"], timeout=5)
+        if ev is None:
+            raise Exception("MGMT-RX timeout")
+        if " d0" in ev:
+            break
+    if "dd050011223304" not in ev:
+        raise Exception("Vendor element not found from GO Negotiation Response frame")
+    dev[0].p2p_stop_find()
+    dev[1].p2p_stop_find()
+
+def test_p2p_ext_vendor_elem_go_neg_conf(dev, apdev, params):
+    """VENDOR_ELEM in GO Negotiation Confirm frames"""
+    try:
+        _test_p2p_ext_vendor_elem_go_neg_conf(dev, apdev, params)
+    finally:
+        dev[0].request("VENDOR_ELEM_REMOVE 8 *")
+
+def _test_p2p_ext_vendor_elem_go_neg_conf(dev, apdev, params):
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    if "OK" not in dev[0].request("VENDOR_ELEM_ADD 8 dd050011223305"):
+        raise Exception("VENDOR_ELEM_ADD failed")
+    dev[0].p2p_listen()
+    dev[1].p2p_listen()
+    dev[1].p2p_go_neg_auth(addr0, "12345670", "enter")
+    dev[0].p2p_go_neg_init(addr1, "12345678", "display")
+    dev[1].p2p_go_neg_auth_result(expect_failure=True)
+
+    out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+                     "wifi_p2p.public_action.subtype == 2")
+    if "Vendor Specific Data: 3305" not in out:
+        raise Exception("Vendor element not found from GO Negotiation Confirm frame")
+
+def test_p2p_ext_vendor_elem_invitation(dev):
+    """VENDOR_ELEM in Invitation frames"""
+    try:
+        _test_p2p_ext_vendor_elem_invitation(dev)
+    finally:
+        dev[0].request("VENDOR_ELEM_REMOVE 9 *")
+        dev[0].request("VENDOR_ELEM_REMOVE 10 *")
+
+def _test_p2p_ext_vendor_elem_invitation(dev):
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    form(dev[0], dev[1])
+    if "OK" not in dev[0].request("VENDOR_ELEM_ADD 9 dd050011223306"):
+        raise Exception("VENDOR_ELEM_ADD failed")
+    if "OK" not in dev[0].request("VENDOR_ELEM_ADD 10 dd050011223307"):
+        raise Exception("VENDOR_ELEM_ADD failed")
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Device discovery timed out")
+    peer = dev[0].get_peer(addr1)
+    dev[0].p2p_stop_find()
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+    dev[0].global_request("P2P_INVITE persistent=" + peer['persistent'] + " peer=" + addr1)
+    for i in range(5):
+        ev = dev[1].wait_event(["MGMT-RX"], timeout=5)
+        if ev is None:
+            raise Exception("MGMT-RX timeout")
+        if " d0" in ev:
+            break
+    if "dd050011223306" not in ev:
+        raise Exception("Vendor element not found from Invitation Request frame")
+    dev[0].p2p_stop_find()
+    dev[1].p2p_stop_find()
+
+    dev[0].p2p_listen()
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 0"):
+        raise Exception("Failed to disable external management frame handling")
+    if not dev[1].discover_peer(addr0):
+        raise Exception("Device discovery timed out")
+    peer = dev[1].get_peer(addr0)
+    dev[1].p2p_stop_find()
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+    dev[1].global_request("P2P_INVITE persistent=" + peer['persistent'] + " peer=" + addr0)
+    for i in range(5):
+        ev = dev[1].wait_event(["MGMT-RX"], timeout=5)
+        if ev is None:
+            raise Exception("MGMT-RX timeout")
+        if " d0" in ev:
+            break
+    if "dd050011223307" not in ev:
+        raise Exception("Vendor element not found from Invitation Response frame")
+    dev[0].p2p_stop_find()
+    dev[1].p2p_stop_find()
+
+def test_p2p_ext_vendor_elem_assoc(dev, apdev, params):
+    """VENDOR_ELEM in Association frames"""
+    try:
+        _test_p2p_ext_vendor_elem_assoc(dev, apdev, params)
+    finally:
+        dev[0].request("VENDOR_ELEM_REMOVE 11 *")
+        dev[1].request("VENDOR_ELEM_REMOVE 12 *")
+        dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+def _test_p2p_ext_vendor_elem_assoc(dev, apdev, params):
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    if "OK" not in dev[0].request("VENDOR_ELEM_ADD 11 dd050011223308"):
+        raise Exception("VENDOR_ELEM_ADD failed")
+    if "OK" not in dev[1].request("VENDOR_ELEM_ADD 12 dd050011223309"):
+        raise Exception("VENDOR_ELEM_ADD failed")
+    if "OK" not in dev[0].request("VENDOR_ELEM_ADD 13 dd05001122330a"):
+        raise Exception("VENDOR_ELEM_ADD failed")
+    dev[0].p2p_listen()
+    dev[1].p2p_listen()
+    dev[1].p2p_go_neg_auth(addr0, "12345670", "enter", go_intent=15)
+    dev[0].p2p_go_neg_init(addr1, "12345670", "display", go_intent=0,
+                           timeout=15)
+    dev[1].p2p_go_neg_auth_result()
+    dev[1].remove_group()
+    dev[0].wait_go_ending_session()
+
+    out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+                     "wlan.fc.type_subtype == 0x00", wait=False)
+    if "Vendor Specific Data: 3308" not in out:
+        raise Exception("Vendor element (P2P) not found from Association Request frame")
+    if "Vendor Specific Data: 330a" not in out:
+        raise Exception("Vendor element (non-P2P) not found from Association Request frame")
+
+    out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+                     "wlan.fc.type_subtype == 0x01", wait=False)
+    if "Vendor Specific Data: 3309" not in out:
+        raise Exception("Vendor element not found from Association Response frame")
diff --git a/hostap/tests/hwsim/test_p2p_grpform.py b/hostap/tests/hwsim/test_p2p_grpform.py
new file mode 100644
index 0000000..f86637d
--- /dev/null
+++ b/hostap/tests/hwsim/test_p2p_grpform.py
@@ -0,0 +1,1052 @@
+# P2P group formation test cases
+# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+import threading
+import Queue
+import os
+
+import hostapd
+import hwsim_utils
+import utils
+from utils import HwsimSkip
+from wpasupplicant import WpaSupplicant
+
+def check_grpform_results(i_res, r_res):
+    if i_res['result'] != 'success' or r_res['result'] != 'success':
+        raise Exception("Failed group formation")
+    if i_res['ssid'] != r_res['ssid']:
+        raise Exception("SSID mismatch")
+    if i_res['freq'] != r_res['freq']:
+        raise Exception("freq mismatch")
+    if 'go_neg_freq' in r_res and i_res['go_neg_freq'] != r_res['go_neg_freq']:
+        raise Exception("go_neg_freq mismatch")
+    if i_res['freq'] != i_res['go_neg_freq']:
+        raise Exception("freq/go_neg_freq mismatch")
+    if i_res['role'] != i_res['go_neg_role']:
+        raise Exception("role/go_neg_role mismatch")
+    if 'go_neg_role' in r_res and r_res['role'] != r_res['go_neg_role']:
+        raise Exception("role/go_neg_role mismatch")
+    if i_res['go_dev_addr'] != r_res['go_dev_addr']:
+        raise Exception("GO Device Address mismatch")
+
+def go_neg_init(i_dev, r_dev, pin, i_method, i_intent, res):
+    logger.debug("Initiate GO Negotiation from i_dev")
+    try:
+        i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), pin, i_method, timeout=20, go_intent=i_intent)
+        logger.debug("i_res: " + str(i_res))
+    except Exception, e:
+        i_res = None
+        logger.info("go_neg_init thread caught an exception from p2p_go_neg_init: " + str(e))
+    res.put(i_res)
+
+def go_neg_pin(i_dev, r_dev, i_intent=None, r_intent=None, i_method='enter', r_method='display'):
+    r_dev.p2p_listen()
+    i_dev.p2p_listen()
+    pin = r_dev.wps_read_pin()
+    logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
+    r_dev.dump_monitor()
+    res = Queue.Queue()
+    t = threading.Thread(target=go_neg_init, args=(i_dev, r_dev, pin, i_method, i_intent, res))
+    t.start()
+    logger.debug("Wait for GO Negotiation Request on r_dev")
+    ev = r_dev.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+    if ev is None:
+        raise Exception("GO Negotiation timed out")
+    r_dev.dump_monitor()
+    logger.debug("Re-initiate GO Negotiation from r_dev")
+    r_res = r_dev.p2p_go_neg_init(i_dev.p2p_dev_addr(), pin, r_method, go_intent=r_intent, timeout=20)
+    logger.debug("r_res: " + str(r_res))
+    r_dev.dump_monitor()
+    t.join()
+    i_res = res.get()
+    if i_res is None:
+        raise Exception("go_neg_init thread failed")
+    logger.debug("i_res: " + str(i_res))
+    logger.info("Group formed")
+    hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
+    i_dev.dump_monitor()
+    return [i_res, r_res]
+
+def go_neg_pin_authorized(i_dev, r_dev, i_intent=None, r_intent=None, expect_failure=False, i_go_neg_status=None, i_method='enter', r_method='display', test_data=True, i_freq=None, r_freq=None):
+    i_dev.p2p_listen()
+    pin = r_dev.wps_read_pin()
+    logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
+    r_dev.p2p_go_neg_auth(i_dev.p2p_dev_addr(), pin, r_method, go_intent=r_intent, freq=r_freq)
+    r_dev.p2p_listen()
+    i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), pin, i_method, timeout=20, go_intent=i_intent, expect_failure=expect_failure, freq=i_freq)
+    r_res = r_dev.p2p_go_neg_auth_result(expect_failure=expect_failure)
+    logger.debug("i_res: " + str(i_res))
+    logger.debug("r_res: " + str(r_res))
+    r_dev.dump_monitor()
+    i_dev.dump_monitor()
+    if i_go_neg_status:
+        if i_res['result'] != 'go-neg-failed':
+            raise Exception("Expected GO Negotiation failure not reported")
+        if i_res['status'] != i_go_neg_status:
+            raise Exception("Expected GO Negotiation status not seen")
+    if expect_failure:
+        return
+    logger.info("Group formed")
+    if test_data:
+        hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
+    return [i_res, r_res]
+
+def go_neg_init_pbc(i_dev, r_dev, i_intent, res, freq, provdisc):
+    logger.debug("Initiate GO Negotiation from i_dev")
+    try:
+        i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), None, "pbc",
+                                      timeout=20, go_intent=i_intent, freq=freq,
+                                      provdisc=provdisc)
+        logger.debug("i_res: " + str(i_res))
+    except Exception, e:
+        i_res = None
+        logger.info("go_neg_init_pbc thread caught an exception from p2p_go_neg_init: " + str(e))
+    res.put(i_res)
+
+def go_neg_pbc(i_dev, r_dev, i_intent=None, r_intent=None, i_freq=None, r_freq=None, provdisc=False, r_listen=False):
+    if r_listen:
+        r_dev.p2p_listen()
+    else:
+        r_dev.p2p_find(social=True)
+    i_dev.p2p_find(social=True)
+    logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
+    r_dev.dump_monitor()
+    res = Queue.Queue()
+    t = threading.Thread(target=go_neg_init_pbc, args=(i_dev, r_dev, i_intent, res, i_freq, provdisc))
+    t.start()
+    logger.debug("Wait for GO Negotiation Request on r_dev")
+    ev = r_dev.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+    if ev is None:
+        raise Exception("GO Negotiation timed out")
+    r_dev.dump_monitor()
+    # Allow some time for the GO Neg Resp to go out before initializing new
+    # GO Negotiation.
+    time.sleep(0.2)
+    logger.debug("Re-initiate GO Negotiation from r_dev")
+    r_res = r_dev.p2p_go_neg_init(i_dev.p2p_dev_addr(), None, "pbc",
+                                  go_intent=r_intent, timeout=20, freq=r_freq)
+    logger.debug("r_res: " + str(r_res))
+    r_dev.dump_monitor()
+    t.join()
+    i_res = res.get()
+    if i_res is None:
+        raise Exception("go_neg_init_pbc thread failed")
+    logger.debug("i_res: " + str(i_res))
+    logger.info("Group formed")
+    hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
+    i_dev.dump_monitor()
+    return [i_res, r_res]
+
+def go_neg_pbc_authorized(i_dev, r_dev, i_intent=None, r_intent=None,
+                          expect_failure=False, i_freq=None, r_freq=None):
+    i_dev.p2p_listen()
+    logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
+    r_dev.p2p_go_neg_auth(i_dev.p2p_dev_addr(), None, "pbc",
+                          go_intent=r_intent, freq=r_freq)
+    r_dev.p2p_listen()
+    i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), None, "pbc", timeout=20,
+                                  go_intent=i_intent,
+                                  expect_failure=expect_failure, freq=i_freq)
+    r_res = r_dev.p2p_go_neg_auth_result(expect_failure=expect_failure)
+    logger.debug("i_res: " + str(i_res))
+    logger.debug("r_res: " + str(r_res))
+    r_dev.dump_monitor()
+    i_dev.dump_monitor()
+    if expect_failure:
+        return
+    logger.info("Group formed")
+    return [i_res, r_res]
+
+def remove_group(dev1, dev2):
+    dev1.remove_group()
+    try:
+        dev2.remove_group()
+    except:
+        pass
+
+def test_grpform(dev):
+    """P2P group formation using PIN and authorized connection (init -> GO)"""
+    try:
+        dev[0].global_request("SET p2p_group_idle 2")
+        [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                               r_dev=dev[1], r_intent=0)
+        check_grpform_results(i_res, r_res)
+        dev[1].remove_group()
+        ev = dev[0].wait_global_event(["P2P-GROUP-REMOVED"], timeout=10)
+        if ev is None:
+            raise Exception("GO did not remove group on idle timeout")
+        if "GO reason=IDLE" not in ev:
+            raise Exception("Unexpected group removal event: " + ev)
+    finally:
+        dev[0].global_request("SET p2p_group_idle 0")
+
+def test_grpform_a(dev):
+    """P2P group formation using PIN and authorized connection (init -> GO) (init: group iface)"""
+    dev[0].global_request("SET p2p_no_group_iface 0")
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                           r_dev=dev[1], r_intent=0)
+    if "p2p-wlan" not in i_res['ifname']:
+        raise Exception("Unexpected group interface name")
+    check_grpform_results(i_res, r_res)
+    remove_group(dev[0], dev[1])
+    if i_res['ifname'] in utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
+
+def test_grpform_b(dev):
+    """P2P group formation using PIN and authorized connection (init -> GO) (resp: group iface)"""
+    dev[1].global_request("SET p2p_no_group_iface 0")
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                           r_dev=dev[1], r_intent=0)
+    if "p2p-wlan" not in r_res['ifname']:
+        raise Exception("Unexpected group interface name")
+    check_grpform_results(i_res, r_res)
+    remove_group(dev[0], dev[1])
+    if r_res['ifname'] in utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
+
+def test_grpform_c(dev):
+    """P2P group formation using PIN and authorized connection (init -> GO) (group iface)"""
+    dev[0].global_request("SET p2p_no_group_iface 0")
+    dev[1].global_request("SET p2p_no_group_iface 0")
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                           r_dev=dev[1], r_intent=0)
+    if "p2p-wlan" not in i_res['ifname']:
+        raise Exception("Unexpected group interface name")
+    if "p2p-wlan" not in r_res['ifname']:
+        raise Exception("Unexpected group interface name")
+    check_grpform_results(i_res, r_res)
+    remove_group(dev[0], dev[1])
+    if i_res['ifname'] in utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
+    if r_res['ifname'] in utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
+
+def test_grpform2(dev):
+    """P2P group formation using PIN and authorized connection (resp -> GO)"""
+    go_neg_pin_authorized(i_dev=dev[0], i_intent=0, r_dev=dev[1], r_intent=15)
+    remove_group(dev[0], dev[1])
+
+def test_grpform2_c(dev):
+    """P2P group formation using PIN and authorized connection (resp -> GO) (group iface)"""
+    dev[0].global_request("SET p2p_no_group_iface 0")
+    dev[1].global_request("SET p2p_no_group_iface 0")
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0, r_dev=dev[1], r_intent=15)
+    remove_group(dev[0], dev[1])
+    if i_res['ifname'] in utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
+    if r_res['ifname'] in utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
+
+def test_grpform3(dev):
+    """P2P group formation using PIN and re-init GO Negotiation"""
+    go_neg_pin(i_dev=dev[0], i_intent=15, r_dev=dev[1], r_intent=0)
+    remove_group(dev[0], dev[1])
+
+def test_grpform3_c(dev):
+    """P2P group formation using PIN and re-init GO Negotiation (group iface)"""
+    dev[0].global_request("SET p2p_no_group_iface 0")
+    dev[1].global_request("SET p2p_no_group_iface 0")
+    [i_res, r_res] = go_neg_pin(i_dev=dev[0], i_intent=15, r_dev=dev[1], r_intent=0)
+    remove_group(dev[0], dev[1])
+    if i_res['ifname'] in utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
+    if r_res['ifname'] in utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
+
+def test_grpform4(dev):
+    """P2P group formation response during p2p_find"""
+    addr1 = dev[1].p2p_dev_addr()
+    dev[1].p2p_listen()
+    dev[0].discover_peer(addr1)
+    dev[1].p2p_find(social=True)
+    time.sleep(0.4)
+    dev[0].global_request("P2P_CONNECT " + addr1 + " 12345670 display")
+    ev = dev[1].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+    if ev is None:
+        raise Exception("GO Negotiation RX timed out")
+    time.sleep(0.5)
+    dev[1].p2p_stop_find()
+    dev[0].p2p_stop_find()
+
+def test_grpform_pbc(dev):
+    """P2P group formation using PBC and re-init GO Negotiation"""
+    [i_res, r_res] = go_neg_pbc(i_dev=dev[0], i_intent=15, r_dev=dev[1], r_intent=0)
+    check_grpform_results(i_res, r_res)
+    if i_res['role'] != 'GO' or r_res['role'] != 'client':
+        raise Exception("Unexpected device roles")
+    remove_group(dev[0], dev[1])
+
+def test_grpform_pd(dev):
+    """P2P group formation with PD-before-GO-Neg workaround"""
+    [i_res, r_res] = go_neg_pbc(i_dev=dev[0], provdisc=True, r_dev=dev[1], r_listen=True)
+    check_grpform_results(i_res, r_res)
+    remove_group(dev[0], dev[1])
+
+def test_grpform_ext_listen(dev):
+    """P2P group formation with extended listen timing enabled"""
+    addr0 = dev[0].p2p_dev_addr()
+    try:
+        if "FAIL" not in dev[0].global_request("P2P_EXT_LISTEN 100"):
+            raise Exception("Invalid P2P_EXT_LISTEN accepted")
+        if "OK" not in dev[0].global_request("P2P_EXT_LISTEN 300 1000"):
+            raise Exception("Failed to set extended listen timing")
+        if "OK" not in dev[1].global_request("P2P_EXT_LISTEN 200 40000"):
+            raise Exception("Failed to set extended listen timing")
+        [i_res, r_res] = go_neg_pbc(i_dev=dev[0], provdisc=True, r_dev=dev[1],
+                                    r_listen=True, i_freq="2417", r_freq="2417",
+                                    i_intent=1, r_intent=15)
+        check_grpform_results(i_res, r_res)
+        peer1 = dev[0].get_peer(dev[1].p2p_dev_addr())
+        if peer1['ext_listen_interval'] != "40000":
+            raise Exception("Extended listen interval not discovered correctly")
+        if peer1['ext_listen_period'] != "200":
+            raise Exception("Extended listen period not discovered correctly")
+        peer0 = dev[1].get_peer(dev[0].p2p_dev_addr())
+        if peer0['ext_listen_interval'] != "1000":
+            raise Exception("Extended listen interval not discovered correctly")
+        if peer0['ext_listen_period'] != "300":
+            raise Exception("Extended listen period not discovered correctly")
+        if not dev[2].discover_peer(addr0):
+            raise Exception("Could not discover peer during ext listen")
+        remove_group(dev[0], dev[1])
+    finally:
+        if "OK" not in dev[0].global_request("P2P_EXT_LISTEN"):
+            raise Exception("Failed to clear extended listen timing")
+        if "OK" not in dev[1].global_request("P2P_EXT_LISTEN"):
+            raise Exception("Failed to clear extended listen timing")
+
+def test_grpform_ext_listen_oper(dev):
+    """P2P extended listen timing operations"""
+    try:
+        _test_grpform_ext_listen_oper(dev)
+    finally:
+        dev[0].global_request("P2P_EXT_LISTEN")
+
+def _test_grpform_ext_listen_oper(dev):
+    addr0 = dev[0].p2p_dev_addr()
+    dev[0].global_request("SET p2p_no_group_iface 0")
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    addr1 = wpas.p2p_dev_addr()
+    wpas.request("P2P_SET listen_channel 1")
+    wpas.global_request("SET p2p_no_group_iface 0")
+    wpas.request("P2P_LISTEN")
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Could not discover peer")
+    dev[0].request("P2P_LISTEN")
+    if not wpas.discover_peer(addr0):
+        raise Exception("Could not discover peer (2)")
+
+    dev[0].global_request("P2P_EXT_LISTEN 300 500")
+    dev[0].global_request("P2P_CONNECT " + addr1 + " 12345670 display auth go_intent=0 freq=2417")
+    wpas.global_request("P2P_CONNECT " + addr0 + " 12345670 enter go_intent=15 freq=2417")
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("GO Negotiation failed")
+    ifaces = wpas.request("INTERFACES").splitlines()
+    iface = ifaces[0] if "p2p-wlan" in ifaces[0] else ifaces[1]
+    wpas.group_ifname = iface
+    if "OK" not in wpas.group_request("STOP_AP"):
+        raise Exception("STOP_AP failed")
+    wpas.group_request("SET ext_mgmt_frame_handling 1")
+    dev[1].p2p_find(social=True)
+    time.sleep(1)
+    if dev[1].peer_known(addr0):
+        raise Exception("Unexpected peer discovery")
+    ifaces = dev[0].request("INTERFACES").splitlines()
+    iface = ifaces[0] if "p2p-wlan" in ifaces[0] else ifaces[1]
+    if "OK" not in dev[0].global_request("P2P_GROUP_REMOVE " + iface):
+        raise Exception("Failed to request group removal")
+    wpas.remove_group()
+
+    count = 0
+    timeout = 15
+    found = False
+    while count < timeout * 4:
+        time.sleep(0.25)
+        count = count + 1
+        if dev[1].peer_known(addr0):
+            found = True
+            break
+    dev[1].p2p_stop_find()
+    if not found:
+        raise Exception("Could not discover peer that was supposed to use extended listen")
+
+def test_both_go_intent_15(dev):
+    """P2P GO Negotiation with both devices using GO intent 15"""
+    go_neg_pin_authorized(i_dev=dev[0], i_intent=15, r_dev=dev[1], r_intent=15, expect_failure=True, i_go_neg_status=9)
+
+def test_both_go_neg_display(dev):
+    """P2P GO Negotiation with both devices trying to display PIN"""
+    go_neg_pin_authorized(i_dev=dev[0], r_dev=dev[1], expect_failure=True, i_go_neg_status=10, i_method='display', r_method='display')
+
+def test_both_go_neg_enter(dev):
+    """P2P GO Negotiation with both devices trying to enter PIN"""
+    go_neg_pin_authorized(i_dev=dev[0], r_dev=dev[1], expect_failure=True, i_go_neg_status=10, i_method='enter', r_method='enter')
+
+def test_go_neg_pbc_vs_pin(dev):
+    """P2P GO Negotiation with one device using PBC and the other PIN"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Could not discover peer")
+    dev[0].p2p_listen()
+    if "OK" not in dev[0].request("P2P_CONNECT " + addr1 + " pbc auth"):
+        raise Exception("Failed to authorize GO Neg")
+    if not dev[1].discover_peer(addr0):
+        raise Exception("Could not discover peer")
+    if "OK" not in dev[1].request("P2P_CONNECT " + addr0 + " 12345670 display"):
+        raise Exception("Failed to initiate GO Neg")
+    ev = dev[1].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=10)
+    if ev is None:
+        raise Exception("GO Negotiation failure timed out")
+    if "status=10" not in ev:
+        raise Exception("Unexpected failure reason: " + ev)
+
+def test_go_neg_pin_vs_pbc(dev):
+    """P2P GO Negotiation with one device using PIN and the other PBC"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Could not discover peer")
+    dev[0].p2p_listen()
+    if "OK" not in dev[0].request("P2P_CONNECT " + addr1 + " 12345670 display auth"):
+        raise Exception("Failed to authorize GO Neg")
+    if not dev[1].discover_peer(addr0):
+        raise Exception("Could not discover peer")
+    if "OK" not in dev[1].request("P2P_CONNECT " + addr0 + " pbc"):
+        raise Exception("Failed to initiate GO Neg")
+    ev = dev[1].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=10)
+    if ev is None:
+        raise Exception("GO Negotiation failure timed out")
+    if "status=10" not in ev:
+        raise Exception("Unexpected failure reason: " + ev)
+
+def test_grpform_per_sta_psk(dev):
+    """P2P group formation with per-STA PSKs"""
+    dev[0].global_request("P2P_SET per_sta_psk 1")
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15, r_dev=dev[1], r_intent=0)
+    check_grpform_results(i_res, r_res)
+
+    pin = dev[2].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    c_res = dev[2].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60)
+    check_grpform_results(i_res, c_res)
+
+    if r_res['psk'] == c_res['psk']:
+        raise Exception("Same PSK assigned for both clients")
+
+    hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+    dev[2].wait_go_ending_session()
+
+def test_grpform_per_sta_psk_wps(dev):
+    """P2P group formation with per-STA PSKs with non-P2P WPS STA"""
+    dev[0].global_request("P2P_SET per_sta_psk 1")
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15, r_dev=dev[1], r_intent=0)
+    check_grpform_results(i_res, r_res)
+
+    dev[0].p2p_go_authorize_client_pbc()
+    dev[2].request("WPS_PBC")
+    dev[2].wait_connected(timeout=30)
+
+    hwsim_utils.test_connectivity_p2p_sta(dev[1], dev[2])
+
+    dev[0].remove_group()
+    dev[2].request("DISCONNECT")
+    dev[1].wait_go_ending_session()
+
+def test_grpform_force_chan_go(dev):
+    """P2P group formation forced channel selection by GO"""
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                           i_freq=2432,
+                                           r_dev=dev[1], r_intent=0,
+                                           test_data=False)
+    check_grpform_results(i_res, r_res)
+    if i_res['freq'] != "2432":
+        raise Exception("Unexpected channel - did not follow GO's forced channel")
+    remove_group(dev[0], dev[1])
+
+def test_grpform_force_chan_cli(dev):
+    """P2P group formation forced channel selection by client"""
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+                                           i_freq=2417,
+                                           r_dev=dev[1], r_intent=15,
+                                           test_data=False)
+    check_grpform_results(i_res, r_res)
+    if i_res['freq'] != "2417":
+        raise Exception("Unexpected channel - did not follow GO's forced channel")
+    remove_group(dev[0], dev[1])
+
+def test_grpform_force_chan_conflict(dev):
+    """P2P group formation fails due to forced channel mismatch"""
+    go_neg_pin_authorized(i_dev=dev[0], i_intent=0, i_freq=2422,
+                          r_dev=dev[1], r_intent=15, r_freq=2427,
+                          expect_failure=True, i_go_neg_status=7)
+
+def test_grpform_pref_chan_go(dev):
+    """P2P group formation preferred channel selection by GO"""
+    dev[0].request("SET p2p_pref_chan 81:7")
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                           r_dev=dev[1], r_intent=0,
+                                           test_data=False)
+    check_grpform_results(i_res, r_res)
+    if i_res['freq'] != "2442":
+        raise Exception("Unexpected channel - did not follow GO's p2p_pref_chan")
+    remove_group(dev[0], dev[1])
+
+def test_grpform_pref_chan_go_overridden(dev):
+    """P2P group formation preferred channel selection by GO overridden by client"""
+    dev[1].request("SET p2p_pref_chan 81:7")
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+                                           i_freq=2422,
+                                           r_dev=dev[1], r_intent=15,
+                                           test_data=False)
+    check_grpform_results(i_res, r_res)
+    if i_res['freq'] != "2422":
+        raise Exception("Unexpected channel - did not follow client's forced channel")
+    remove_group(dev[0], dev[1])
+
+def test_grpform_no_go_freq_forcing_chan(dev):
+    """P2P group formation with no-GO freq forcing channel"""
+    dev[1].request("SET p2p_no_go_freq 100-200,300,4000-6000")
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+                                           r_dev=dev[1], r_intent=15,
+                                           test_data=False)
+    check_grpform_results(i_res, r_res)
+    if int(i_res['freq']) > 4000:
+        raise Exception("Unexpected channel - did not follow no-GO freq")
+    remove_group(dev[0], dev[1])
+
+def test_grpform_no_go_freq_conflict(dev):
+    """P2P group formation fails due to no-GO range forced by client"""
+    dev[1].request("SET p2p_no_go_freq 2000-3000")
+    go_neg_pin_authorized(i_dev=dev[0], i_intent=0, i_freq=2422,
+                          r_dev=dev[1], r_intent=15,
+                          expect_failure=True, i_go_neg_status=7)
+
+def test_grpform_no_5ghz_world_roaming(dev):
+    """P2P group formation with world roaming regulatory"""
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+                                           r_dev=dev[1], r_intent=15,
+                                           test_data=False)
+    check_grpform_results(i_res, r_res)
+    if int(i_res['freq']) > 4000:
+        raise Exception("Unexpected channel - did not follow world roaming rules")
+    remove_group(dev[0], dev[1])
+
+def test_grpform_no_5ghz_add_cli(dev):
+    """P2P group formation with passive scan 5 GHz and p2p_add_cli_chan=1"""
+    dev[0].request("SET p2p_add_cli_chan 1")
+    dev[1].request("SET p2p_add_cli_chan 1")
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+                                           r_dev=dev[1], r_intent=14,
+                                           test_data=False)
+    check_grpform_results(i_res, r_res)
+    if int(i_res['freq']) > 4000:
+        raise Exception("Unexpected channel - did not follow world roaming rules")
+    remove_group(dev[0], dev[1])
+
+def test_grpform_no_5ghz_add_cli2(dev):
+    """P2P group formation with passive scan 5 GHz and p2p_add_cli_chan=1 (reverse)"""
+    dev[0].request("SET p2p_add_cli_chan 1")
+    dev[1].request("SET p2p_add_cli_chan 1")
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=14,
+                                           r_dev=dev[1], r_intent=0,
+                                           test_data=False)
+    check_grpform_results(i_res, r_res)
+    if int(i_res['freq']) > 4000:
+        raise Exception("Unexpected channel - did not follow world roaming rules")
+    remove_group(dev[0], dev[1])
+
+def test_grpform_no_5ghz_add_cli3(dev):
+    """P2P group formation with passive scan 5 GHz and p2p_add_cli_chan=1 (intent 15)"""
+    dev[0].request("SET p2p_add_cli_chan 1")
+    dev[1].request("SET p2p_add_cli_chan 1")
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+                                           r_dev=dev[1], r_intent=15,
+                                           test_data=False)
+    check_grpform_results(i_res, r_res)
+    if int(i_res['freq']) > 4000:
+        raise Exception("Unexpected channel - did not follow world roaming rules")
+    remove_group(dev[0], dev[1])
+
+def test_grpform_no_5ghz_add_cli4(dev):
+    """P2P group formation with passive scan 5 GHz and p2p_add_cli_chan=1 (reverse; intent 15)"""
+    dev[0].request("SET p2p_add_cli_chan 1")
+    dev[1].request("SET p2p_add_cli_chan 1")
+    [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+                                           r_dev=dev[1], r_intent=0,
+                                           test_data=False)
+    check_grpform_results(i_res, r_res)
+    if int(i_res['freq']) > 4000:
+        raise Exception("Unexpected channel - did not follow world roaming rules")
+    remove_group(dev[0], dev[1])
+
+def test_grpform_incorrect_pin(dev):
+    """P2P GO Negotiation with incorrect PIN"""
+    dev[1].p2p_listen()
+    addr1 = dev[1].p2p_dev_addr()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Peer not found")
+    res = dev[1].global_request("P2P_CONNECT " + dev[0].p2p_dev_addr() + " pin auth go_intent=0")
+    if "FAIL" in res:
+        raise Exception("P2P_CONNECT failed to generate PIN")
+    logger.info("PIN from P2P_CONNECT: " + res)
+    dev[0].global_request("P2P_CONNECT " + addr1 + " 00000000 enter go_intent=15")
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("GO Negotiation did not complete successfully(0)")
+    ev = dev[1].wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=15)
+    if ev is None:
+        raise Exception("GO Negotiation did not complete successfully(1)")
+    ev = dev[1].wait_global_event(["WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("WPS failure not reported(1)")
+    if "msg=8 config_error=18" not in ev:
+        raise Exception("Unexpected WPS failure(1): " + ev)
+    ev = dev[0].wait_global_event(["WPS-FAIL"], timeout=15)
+    if ev is None:
+        raise Exception("WPS failure not reported")
+    if "msg=8 config_error=18" not in ev:
+        raise Exception("Unexpected WPS failure: " + ev)
+    ev = dev[1].wait_global_event(["P2P-GROUP-FORMATION-FAILURE"], timeout=10)
+    if ev is None:
+        raise Exception("Group formation failure timed out")
+    ev = dev[0].wait_global_event(["P2P-GROUP-FORMATION-FAILURE"], timeout=5)
+    if ev is None:
+        raise Exception("Group formation failure timed out")
+
+def test_grpform_reject(dev):
+    """User rejecting group formation attempt by a P2P peer"""
+    addr0 = dev[0].p2p_dev_addr()
+    dev[0].p2p_listen()
+    dev[1].p2p_go_neg_init(addr0, None, "pbc")
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+    if ev is None:
+        raise Exception("GO Negotiation timed out")
+    if "OK" in dev[0].global_request("P2P_REJECT foo"):
+        raise Exception("Invalid P2P_REJECT accepted")
+    if "FAIL" in dev[0].global_request("P2P_REJECT " + ev.split(' ')[1]):
+        raise Exception("P2P_REJECT failed")
+    dev[1].request("P2P_STOP_FIND")
+    dev[1].p2p_go_neg_init(addr0, None, "pbc")
+    ev = dev[1].wait_global_event(["GO-NEG-FAILURE"], timeout=10)
+    if ev is None:
+        raise Exception("Rejection not reported")
+    if "status=11" not in ev:
+        raise Exception("Unexpected status code in rejection")
+
+def test_grpform_pd_no_probe_resp(dev):
+    """GO Negotiation after PD, but no Probe Response"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    dev[0].p2p_listen()
+    if not dev[1].discover_peer(addr0):
+        raise Exception("Peer not found")
+    dev[1].p2p_stop_find()
+    dev[0].p2p_stop_find()
+    peer = dev[0].get_peer(addr1)
+    if peer['listen_freq'] == '0':
+        raise Exception("Peer listen frequency not learned from Probe Request")
+    time.sleep(0.3)
+    dev[0].request("P2P_FLUSH")
+    dev[0].p2p_listen()
+    dev[1].global_request("P2P_PROV_DISC " + addr0 + " display")
+    ev = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=5)
+    if ev is None:
+        raise Exception("PD Request timed out")
+    ev = dev[1].wait_global_event(["P2P-PROV-DISC-ENTER-PIN"], timeout=5)
+    if ev is None:
+        raise Exception("PD Response timed out")
+    peer = dev[0].get_peer(addr1)
+    if peer['listen_freq'] != '0':
+        raise Exception("Peer listen frequency learned unexpectedly from PD Request")
+
+    pin = dev[0].wps_read_pin()
+    if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " " + pin + " enter"):
+        raise Exception("P2P_CONNECT on initiator failed")
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=5)
+    if ev is None:
+        raise Exception("GO Negotiation start timed out")
+    peer = dev[0].get_peer(addr1)
+    if peer['listen_freq'] == '0':
+        raise Exception("Peer listen frequency not learned from PD followed by GO Neg Req")
+    if "FAIL" in dev[0].global_request("P2P_CONNECT " + addr1 + " " + pin + " display"):
+        raise Exception("P2P_CONNECT on responder failed")
+    ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+    if ev is None:
+        raise Exception("Group formation timed out")
+
+def test_go_neg_two_peers(dev):
+    """P2P GO Negotiation rejected due to already started negotiation with another peer"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    addr2 = dev[2].p2p_dev_addr()
+    dev[1].p2p_listen()
+    dev[2].p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Could not discover peer")
+    if not dev[0].discover_peer(addr2):
+        raise Exception("Could not discover peer")
+    if "OK" not in dev[0].request("P2P_CONNECT " + addr2 + " pbc auth"):
+        raise Exception("Failed to authorize GO Neg")
+    dev[0].p2p_listen()
+    if not dev[2].discover_peer(addr0):
+        raise Exception("Could not discover peer")
+    if "OK" not in dev[0].request("P2P_CONNECT " + addr1 + " pbc"):
+        raise Exception("Failed to initiate GO Neg")
+    ev = dev[1].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=5)
+    if ev is None:
+        raise Exception("timeout on GO Neg RX event")
+    dev[2].request("P2P_CONNECT " + addr0 + " pbc")
+    ev = dev[2].wait_global_event(["GO-NEG-FAILURE"], timeout=10)
+    if ev is None:
+        raise Exception("Rejection not reported")
+    if "status=5" not in ev:
+        raise Exception("Unexpected status code in rejection: " + ev)
+
+def clear_pbc_overlap(dev, ifname):
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.remove(ifname)
+    dev[0].request("P2P_CANCEL")
+    dev[1].request("P2P_CANCEL")
+    dev[0].p2p_stop_find()
+    dev[1].p2p_stop_find()
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    time.sleep(0.1)
+    dev[0].flush_scan_cache()
+    dev[1].flush_scan_cache()
+    time.sleep(0.1)
+
+def test_grpform_pbc_overlap(dev, apdev):
+    """P2P group formation during PBC overlap"""
+    params = { "ssid": "wps", "eap_server": "1", "wps_state": "1" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd.request("WPS_PBC")
+    time.sleep(0.1)
+
+    # Since P2P Client scan case is now optimzied to use a specific SSID, the
+    # WPS AP will not reply to that and the scan after GO Negotiation can quite
+    # likely miss the AP due to dwell time being short enoguh to miss the Beacon
+    # frame. This has made the test case somewhat pointless, but keep it here
+    # for now with an additional scan to confirm that PBC detection works if
+    # there is a BSS entry for a overlapping AP.
+    for i in range(0, 5):
+        dev[0].scan(freq="2412")
+        if dev[0].get_bss(apdev[0]['bssid']) is not None:
+            break
+
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    dev[0].p2p_listen()
+    if not dev[1].discover_peer(addr0):
+        raise Exception("Could not discover peer")
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Could not discover peer")
+    dev[0].p2p_listen()
+    if "OK" not in dev[0].global_request("P2P_CONNECT " + addr1 + " pbc auth go_intent=0"):
+        raise Exception("Failed to authorize GO Neg")
+    if "OK" not in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc go_intent=15 freq=2412"):
+        raise Exception("Failed to initiate GO Neg")
+    ev = dev[0].wait_global_event(["WPS-OVERLAP-DETECTED"], timeout=15)
+    if ev is None:
+        raise Exception("PBC overlap not reported")
+
+    clear_pbc_overlap(dev, apdev[0]['ifname'])
+
+def test_grpform_pbc_overlap_group_iface(dev, apdev):
+    """P2P group formation during PBC overlap using group interfaces"""
+    # Note: Need to include P2P IE from the AP to get the P2P interface BSS
+    # update use this information.
+    params = { "ssid": "wps", "eap_server": "1", "wps_state": "1",
+               "beacon_int": "15", 'manage_p2p': '1' }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd.request("WPS_PBC")
+
+    dev[0].request("SET p2p_no_group_iface 0")
+    dev[1].request("SET p2p_no_group_iface 0")
+
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    dev[0].p2p_listen()
+    if not dev[1].discover_peer(addr0):
+        raise Exception("Could not discover peer")
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Could not discover peer")
+    dev[0].p2p_stop_find()
+    dev[0].scan(freq="2412")
+    dev[0].p2p_listen()
+    if "OK" not in dev[0].global_request("P2P_CONNECT " + addr1 + " pbc auth go_intent=0"):
+        raise Exception("Failed to authorize GO Neg")
+    if "OK" not in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc go_intent=15 freq=2412"):
+        raise Exception("Failed to initiate GO Neg")
+    ev = dev[0].wait_global_event(["WPS-OVERLAP-DETECTED",
+                                   "P2P-GROUP-FORMATION-SUCCESS"], timeout=15)
+    if ev is None or "WPS-OVERLAP-DETECTED" not in ev:
+        # Do not report this as failure since the P2P group formation case
+        # using a separate group interface has limited chances of "seeing" the
+        # overlapping AP due to a per-SSID scan and no prior scan operations on
+        # the group interface.
+        logger.info("PBC overlap not reported")
+
+    clear_pbc_overlap(dev, apdev[0]['ifname'])
+
+def test_grpform_goneg_fail_with_group_iface(dev):
+    """P2P group formation fails while using group interface"""
+    dev[0].request("SET p2p_no_group_iface 0")
+    dev[1].p2p_listen()
+    peer = dev[1].p2p_dev_addr()
+    if not dev[0].discover_peer(peer):
+        raise Exception("Peer " + peer + " not found")
+    if "OK" not in dev[1].request("P2P_REJECT " + dev[0].p2p_dev_addr()):
+        raise Exception("P2P_REJECT failed")
+    if "OK" not in dev[0].request("P2P_CONNECT " + peer + " pbc"):
+        raise Exception("P2P_CONNECT failed")
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=10)
+    if ev is None:
+        raise Exception("GO Negotiation failure timed out")
+
+def test_grpform_cred_ready_timeout(dev, apdev, params):
+    """P2P GO Negotiation wait for credentials to become ready [long]"""
+    if not params['long']:
+        raise HwsimSkip("Skip test case with long duration due to --long not specified")
+
+    dev[1].p2p_listen()
+    addr1 = dev[1].p2p_dev_addr()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Peer " + addr1 + " not found")
+    if not dev[2].discover_peer(addr1):
+        raise Exception("Peer " + addr1 + " not found(2)")
+
+    start = os.times()[4]
+
+    cmd = "P2P_CONNECT " + addr1 + " 12345670 display"
+    if "OK" not in dev[0].global_request(cmd):
+        raise Exception("Failed to initiate GO Neg")
+
+    if "OK" not in dev[2].global_request(cmd):
+        raise Exception("Failed to initiate GO Neg(2)")
+
+    # First, check with p2p_find
+    ev = dev[2].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=30)
+    if ev is not None:
+        raise Exception("Too early GO Negotiation timeout reported(2)")
+    dev[2].dump_monitor()
+    logger.info("Starting p2p_find to change state")
+    dev[2].p2p_find()
+    ev = dev[2].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=100)
+    if ev is None:
+        raise Exception("GO Negotiation failure timed out(2)")
+    dev[2].dump_monitor()
+    end = os.times()[4]
+    logger.info("GO Negotiation wait time: {} seconds(2)".format(end - start))
+    if end - start < 120:
+        raise Exception("Too short GO Negotiation wait time(2): {}".format(end - start))
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+
+    wpas.p2p_listen()
+    ev = dev[2].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+    if ev is None:
+        raise Exception("Did not discover new device after GO Negotiation failure")
+    if wpas.p2p_dev_addr() not in ev:
+        raise Exception("Unexpected device found: " + ev)
+    dev[2].p2p_stop_find()
+    wpas.p2p_stop_find()
+
+    # Finally, verify without p2p_find
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=120)
+    if ev is None:
+        raise Exception("GO Negotiation failure timed out")
+    end = os.times()[4]
+    logger.info("GO Negotiation wait time: {} seconds".format(end - start))
+    if end - start < 120:
+        raise Exception("Too short GO Negotiation wait time: {}".format(end - start))
+
+def test_grpform_no_wsc_done(dev):
+    """P2P group formation with WSC-Done not sent"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+
+    for i in range(0, 2):
+        dev[0].request("SET ext_eapol_frame_io 1")
+        dev[1].request("SET ext_eapol_frame_io 1")
+        dev[0].p2p_listen()
+        dev[1].p2p_go_neg_auth(addr0, "12345670", "display", 0)
+        dev[1].p2p_listen()
+        dev[0].p2p_go_neg_init(addr1, "12345670", "enter", timeout=20,
+                               go_intent=15, wait_group=False)
+
+        mode = None
+        while True:
+            ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+            if ev is None:
+                raise Exception("Timeout on EAPOL-TX from GO")
+            if not mode:
+                mode = dev[0].get_status_field("mode")
+            res = dev[1].request("EAPOL_RX " + addr0 + " " + ev.split(' ')[2])
+            if "OK" not in res:
+                raise Exception("EAPOL_RX failed")
+            ev = dev[1].wait_event(["EAPOL-TX"], timeout=15)
+            if ev is None:
+                raise Exception("Timeout on EAPOL-TX from P2P Client")
+            msg = ev.split(' ')[2]
+            if msg[46:56] == "102200010f":
+                logger.info("Drop WSC_Done")
+                dev[0].request("SET ext_eapol_frame_io 0")
+                dev[1].request("SET ext_eapol_frame_io 0")
+                # Fake EAP-Failure to complete session on the client
+                id = msg[10:12]
+                dev[1].request("EAPOL_RX " + addr0 + " 0300000404" + id + "0004")
+                break
+            res = dev[0].request("EAPOL_RX " + addr1 + " " + msg)
+            if "OK" not in res:
+                raise Exception("EAPOL_RX failed")
+
+        ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+        if ev is None:
+            raise Exception("Group formation timed out on GO")
+        ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+        if ev is None:
+            raise Exception("Group formation timed out on P2P Client")
+        dev[0].remove_group()
+        dev[1].wait_go_ending_session()
+
+        if mode != "P2P GO - group formation":
+            raise Exception("Unexpected mode on GO during group formation: " + mode)
+
+def test_grpform_wait_peer(dev):
+    """P2P group formation wait for peer to become ready"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1):
+        raise Exception("Peer " + addr1 + " not found")
+    dev[0].request("SET extra_roc_dur 500")
+    if "OK" not in dev[0].request("P2P_CONNECT " + addr1 + " 12345670 display go_intent=15"):
+        raise Exception("Failed to initiate GO Neg")
+    time.sleep(3)
+    dev[1].request("P2P_CONNECT " + addr0 + " 12345670 enter go_intent=0")
+
+    ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    dev[0].group_form_result(ev)
+
+    dev[0].request("SET extra_roc_dur 0")
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    dev[0].remove_group()
+
+def test_invalid_p2p_connect_command(dev):
+    """P2P_CONNECT error cases"""
+    id = dev[0].add_network()
+    for cmd in [ "foo",
+                 "00:11:22:33:44:55",
+                 "00:11:22:33:44:55 pbc persistent=123",
+                 "00:11:22:33:44:55 pbc persistent=%d" % id,
+                 "00:11:22:33:44:55 pbc go_intent=-1",
+                 "00:11:22:33:44:55 pbc go_intent=16",
+                 "00:11:22:33:44:55 pin",
+                 "00:11:22:33:44:55 pbc freq=0" ]:
+        if "FAIL" not in dev[0].request("P2P_CONNECT " + cmd):
+            raise Exception("Invalid P2P_CONNECT command accepted: " + cmd)
+
+    if "FAIL-INVALID-PIN" not in dev[0].request("P2P_CONNECT 00:11:22:33:44:55 1234567"):
+        raise Exception("Invalid PIN was not rejected")
+    if "FAIL-INVALID-PIN" not in dev[0].request("P2P_CONNECT 00:11:22:33:44:55 12345678a"):
+        raise Exception("Invalid PIN was not rejected")
+
+    if "FAIL-CHANNEL-UNSUPPORTED" not in dev[0].request("P2P_CONNECT 00:11:22:33:44:55 pin freq=3000"):
+        raise Exception("Unsupported channel not reported")
+
+def test_p2p_unauthorize(dev):
+    """P2P_UNAUTHORIZE to unauthorize a peer"""
+    if "FAIL" not in dev[0].request("P2P_UNAUTHORIZE foo"):
+        raise Exception("Invalid P2P_UNAUTHORIZE accepted")
+    if "FAIL" not in dev[0].request("P2P_UNAUTHORIZE 00:11:22:33:44:55"):
+        raise Exception("P2P_UNAUTHORIZE for unknown peer accepted")
+
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    dev[1].p2p_listen()
+    pin = dev[0].wps_read_pin()
+    dev[0].p2p_go_neg_auth(addr1, pin, "display")
+    dev[0].p2p_listen()
+    if "OK" not in dev[0].request("P2P_UNAUTHORIZE " + addr1):
+        raise Exception("P2P_UNAUTHORIZE failed")
+    dev[1].p2p_go_neg_init(addr0, pin, "keypad", timeout=0)
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=10)
+    if ev is None:
+        raise Exception("No GO Negotiation Request RX reported")
+
+def test_grpform_pbc_multiple(dev):
+    """P2P group formation using PBC multiple times in a row"""
+    try:
+        dev[1].request("SET passive_scan 1")
+        for i in range(5):
+            [i_res, r_res] = go_neg_pbc_authorized(i_dev=dev[0], i_intent=15,
+                                                   r_dev=dev[1], r_intent=0)
+            remove_group(dev[0], dev[1])
+    finally:
+        dev[1].request("SET passive_scan 0")
+        dev[1].flush_scan_cache()
+
+def test_grpform_not_ready(dev):
+    """Not ready for GO Negotiation (listen)"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr2 = dev[2].p2p_dev_addr()
+    dev[0].p2p_listen()
+    if not dev[1].discover_peer(addr0):
+        raise Exception("Could not discover peer")
+    dev[1].global_request("P2P_CONNECT " + addr0 + " pbc")
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=5)
+    if ev is None:
+        raise Exception("No P2P-GO-NEG-REQUEST event")
+    dev[0].dump_monitor()
+    time.sleep(5)
+    if not dev[2].discover_peer(addr0):
+        raise Exception("Could not discover peer(2)")
+    for i in range(3):
+        dev[i].p2p_stop_find()
+
+def test_grpform_not_ready2(dev):
+    """Not ready for GO Negotiation (search)"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr2 = dev[2].p2p_dev_addr()
+    dev[0].p2p_find(social=True)
+    if not dev[1].discover_peer(addr0):
+        raise Exception("Could not discover peer")
+    dev[1].global_request("P2P_CONNECT " + addr0 + " pbc")
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=5)
+    if ev is None:
+        raise Exception("No P2P-GO-NEG-REQUEST event")
+    dev[0].dump_monitor()
+    time.sleep(1)
+    dev[2].p2p_listen()
+    ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+    if ev is None:
+        raise Exception("Peer not discovered after GO Neg Resp(status=1) TX")
+    if addr2 not in ev:
+        raise Exception("Unexpected peer discovered: " + ev)
+    for i in range(3):
+        dev[i].p2p_stop_find()
diff --git a/hostap/tests/hwsim/test_p2p_invitation.py b/hostap/tests/hwsim/test_p2p_invitation.py
new file mode 100644
index 0000000..ffe8437
--- /dev/null
+++ b/hostap/tests/hwsim/test_p2p_invitation.py
@@ -0,0 +1,190 @@
+# P2P invitation test cases
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+
+def test_p2p_go_invite(dev):
+    """P2P GO inviting a client to join"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+
+    logger.info("Generate BSS table entry for old group")
+    # this adds more coverage to testing by forcing the GO to be found with an
+    # older entry in the BSS table and with that entry having a different
+    # operating channel.
+    dev[0].p2p_start_go(freq=2422)
+    dev[1].scan()
+    dev[0].remove_group()
+
+    logger.info("Discover peer")
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1, social=True):
+        raise Exception("Peer " + addr1 + " not found")
+
+    logger.info("Start GO on non-social channel")
+    res = dev[0].p2p_start_go(freq=2417)
+    logger.debug("res: " + str(res))
+
+    logger.info("Invite peer to join the group")
+    dev[0].global_request("P2P_INVITE group=" + dev[0].group_ifname + " peer=" + addr1)
+    ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on invitation on peer")
+    ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on invitation on GO")
+    if "status=1" not in ev:
+        raise Exception("Unexpected invitation result")
+
+    logger.info("Join the group")
+    pin = dev[1].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    dev[1].p2p_connect_group(addr0, pin, timeout=60)
+    logger.info("Client connected")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+
+    logger.info("Terminate group")
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+
+def test_p2p_go_invite_auth(dev):
+    """P2P GO inviting a client to join (authorized invitation)"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+
+    logger.info("Generate BSS table entry for old group")
+    # this adds more coverage to testing by forcing the GO to be found with an
+    # older entry in the BSS table and with that entry having a different
+    # operating channel.
+    dev[0].p2p_start_go(freq=2432)
+    dev[1].scan()
+    dev[0].remove_group()
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+
+    logger.info("Discover peer")
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1, social=True):
+        raise Exception("Peer " + addr1 + " not found")
+    dev[0].p2p_listen()
+    if not dev[1].discover_peer(addr0, social=True):
+        raise Exception("Peer " + addr0 + " not found")
+    dev[1].p2p_listen()
+
+    logger.info("Authorize invitation")
+    pin = dev[1].wps_read_pin()
+    dev[1].global_request("P2P_CONNECT " + addr0 + " " + pin + " join auth")
+
+    logger.info("Start GO on non-social channel")
+    res = dev[0].p2p_start_go(freq=2427)
+    logger.debug("res: " + str(res))
+
+    logger.info("Invite peer to join the group")
+    dev[0].p2p_go_authorize_client(pin)
+    dev[0].global_request("P2P_INVITE group=" + dev[0].group_ifname + " peer=" + addr1)
+    ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED",
+                                   "P2P-GROUP-STARTED"], timeout=20)
+    if ev is None:
+        raise Exception("Timeout on invitation on peer")
+    if "P2P-INVITATION-RECEIVED" in ev:
+        raise Exception("Unexpected request to accept pre-authorized invitaton")
+    dev[1].group_form_result(ev)
+    dev[0].dump_monitor()
+
+    logger.info("Client connected")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+
+    logger.info("Terminate group")
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+
+def test_p2p_go_invite_unknown(dev):
+    """P2P GO inviting a client that has not discovered the GO"""
+    try:
+        addr0 = dev[0].p2p_dev_addr()
+        addr1 = dev[1].p2p_dev_addr()
+
+        dev[1].p2p_listen()
+        if not dev[0].discover_peer(addr1, social=True):
+            raise Exception("Peer " + addr1 + " not found")
+        dev[1].global_request("P2P_FLUSH")
+        dev[1].p2p_listen()
+
+        dev[0].p2p_start_go(freq=2412)
+
+        logger.info("Invite peer to join the group")
+        # Prevent peer entry from being added for testing coverage
+        if "OK" not in dev[1].global_request("P2P_SET peer_filter 00:11:22:33:44:55"):
+            raise Exception("Failed to set peer_filter")
+        dev[0].p2p_go_authorize_client("12345670")
+        dev[0].global_request("P2P_INVITE group=" + dev[0].group_ifname + " peer=" + addr1)
+        ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=15)
+        if ev is None:
+            raise Exception("Invitation Request not received")
+        ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=15)
+        if ev is None:
+            raise Exception("Invitation Response not received")
+        if "status=1" not in ev:
+            raise Exception("Unexpected invitation result: " + ev)
+    finally:
+        dev[1].global_request("P2P_SET peer_filter 00:00:00:00:00:00")
+
+def test_p2p_cli_invite(dev):
+    """P2P Client inviting a device to join"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    addr2 = dev[2].p2p_dev_addr()
+
+    dev[0].p2p_start_go(freq=2412)
+    pin = dev[1].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    dev[1].p2p_connect_group(addr0, pin, timeout=60)
+
+    dev[2].p2p_listen()
+    if not dev[1].discover_peer(addr2, social=True):
+        raise Exception("Peer " + addr2 + " not found")
+
+    if "OK" not in dev[1].global_request("P2P_INVITE group=" + dev[1].group_ifname + " peer=" + addr2):
+        raise Exception("Unexpected failure of P2P_INVITE to known peer")
+    ev = dev[2].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on invitation invited peer")
+    if "sa=" + addr1 not in ev:
+        raise Exception("Incorrect source address")
+    if "go_dev_addr=" + addr0 not in ev:
+        raise Exception("Incorrect GO address")
+    ev = dev[1].wait_global_event(["P2P-INVITATION-RESULT"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on invitation on inviting client")
+    if "status=1" not in ev:
+        raise Exception("Unexpected invitation result")
+
+    pin = dev[2].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    dev[2].p2p_connect_group(addr0, pin, timeout=60)
+
+    if "FAIL" not in dev[1].global_request("P2P_INVITE group=" + dev[1].group_ifname + " peer=00:11:22:33:44:55"):
+        raise Exception("Unexpected success of P2P_INVITE to unknown peer")
+
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+    dev[2].wait_go_ending_session()
+
+def test_p2p_invite_invalid(dev):
+    """Invalid parameters to P2P_INVITE"""
+    id = dev[0].add_network()
+    for cmd in [ "foo=bar",
+                 "persistent=123 peer=foo",
+                 "persistent=123",
+                 "persistent=%d" % id,
+                 "group=foo",
+                 "group=foo peer=foo",
+                 "group=foo peer=00:11:22:33:44:55 go_dev_addr=foo" ]:
+        if "FAIL" not in dev[0].request("P2P_INVITE " + cmd):
+            raise Exception("Invalid P2P_INVITE accepted: " + cmd)
diff --git a/hostap/tests/hwsim/test_p2p_messages.py b/hostap/tests/hwsim/test_p2p_messages.py
new file mode 100644
index 0000000..19d551b
--- /dev/null
+++ b/hostap/tests/hwsim/test_p2p_messages.py
@@ -0,0 +1,1951 @@
+# P2P protocol tests for various messages
+# 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.
+
+import binascii
+import struct
+import time
+import logging
+logger = logging.getLogger()
+
+import hostapd
+from test_p2p_persistent import form
+from test_p2p_persistent import invite
+
+MGMT_SUBTYPE_PROBE_REQ = 4
+MGMT_SUBTYPE_ACTION = 13
+ACTION_CATEG_PUBLIC = 4
+
+P2P_GO_NEG_REQ = 0
+P2P_GO_NEG_RESP = 1
+P2P_GO_NEG_CONF = 2
+P2P_INVITATION_REQ = 3
+P2P_INVITATION_RESP = 4
+P2P_DEV_DISC_REQ = 5
+P2P_DEV_DISC_RESP = 6
+P2P_PROV_DISC_REQ = 7
+P2P_PROV_DISC_RESP = 8
+
+P2P_ATTR_STATUS = 0
+P2P_ATTR_MINOR_REASON_CODE = 1
+P2P_ATTR_CAPABILITY = 2
+P2P_ATTR_DEVICE_ID = 3
+P2P_ATTR_GROUP_OWNER_INTENT = 4
+P2P_ATTR_CONFIGURATION_TIMEOUT = 5
+P2P_ATTR_LISTEN_CHANNEL = 6
+P2P_ATTR_GROUP_BSSID = 7
+P2P_ATTR_EXT_LISTEN_TIMING = 8
+P2P_ATTR_INTENDED_INTERFACE_ADDR = 9
+P2P_ATTR_MANAGEABILITY = 10
+P2P_ATTR_CHANNEL_LIST = 11
+P2P_ATTR_NOTICE_OF_ABSENCE = 12
+P2P_ATTR_DEVICE_INFO = 13
+P2P_ATTR_GROUP_INFO = 14
+P2P_ATTR_GROUP_ID = 15
+P2P_ATTR_INTERFACE = 16
+P2P_ATTR_OPERATING_CHANNEL = 17
+P2P_ATTR_INVITATION_FLAGS = 18
+P2P_ATTR_OOB_GO_NEG_CHANNEL = 19
+P2P_ATTR_SERVICE_HASH = 21
+P2P_ATTR_SESSION_INFORMATION_DATA = 22
+P2P_ATTR_CONNECTION_CAPABILITY = 23
+P2P_ATTR_ADVERTISEMENT_ID = 24
+P2P_ATTR_ADVERTISED_SERVICE = 25
+P2P_ATTR_SESSION_ID = 26
+P2P_ATTR_FEATURE_CAPABILITY = 27
+P2P_ATTR_PERSISTENT_GROUP = 28
+P2P_ATTR_VENDOR_SPECIFIC = 221
+
+P2P_SC_SUCCESS = 0
+P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1
+P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2
+P2P_SC_FAIL_LIMIT_REACHED = 3
+P2P_SC_FAIL_INVALID_PARAMS = 4
+P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5
+P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6
+P2P_SC_FAIL_NO_COMMON_CHANNELS = 7
+P2P_SC_FAIL_UNKNOWN_GROUP = 8
+P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9
+P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10
+P2P_SC_FAIL_REJECTED_BY_USER = 11
+
+WSC_ATTR_CONFIG_METHODS = 0x1008
+
+WLAN_EID_SSID = 0
+WLAN_EID_SUPP_RATES = 1
+WLAN_EID_VENDOR_SPECIFIC = 221
+
+def ie_ssid(ssid):
+    return struct.pack("<BB", WLAN_EID_SSID, len(ssid)) + ssid
+
+def ie_supp_rates():
+    return struct.pack("<BBBBBBBBBB", WLAN_EID_SUPP_RATES, 8,
+                       2*6, 2*9, 2*12, 2*18, 2*24, 2*36, 2*48, 2*54)
+
+def ie_p2p(attrs):
+    return struct.pack("<BBBBBB", WLAN_EID_VENDOR_SPECIFIC, 4 + len(attrs),
+                       0x50, 0x6f, 0x9a, 9) + attrs
+
+def ie_wsc(attrs):
+    return struct.pack("<BBBBBB", WLAN_EID_VENDOR_SPECIFIC, 4 + len(attrs),
+                       0x00, 0x50, 0xf2, 4) + attrs
+
+def wsc_attr_config_methods(methods=0):
+    return struct.pack(">HHH", WSC_ATTR_CONFIG_METHODS, 2, methods)
+
+def p2p_attr_status(status=P2P_SC_SUCCESS):
+    return struct.pack("<BHB", P2P_ATTR_STATUS, 1, status)
+
+def p2p_attr_minor_reason_code(code=0):
+    return struct.pack("<BHB", P2P_ATTR_MINOR_REASON_CODE, 1, code)
+
+def p2p_attr_capability(dev_capab=0, group_capab=0):
+    return struct.pack("<BHBB", P2P_ATTR_CAPABILITY, 2, dev_capab, group_capab)
+
+def p2p_attr_device_id(addr):
+    val = struct.unpack('6B', binascii.unhexlify(addr.replace(':','')))
+    t = (P2P_ATTR_DEVICE_ID, 6) + val
+    return struct.pack('<BH6B', *t)
+
+def p2p_attr_go_intent(go_intent=0, tie_breaker=0):
+    return struct.pack("<BHB", P2P_ATTR_GROUP_OWNER_INTENT, 1,
+                       (go_intent << 1) | (tie_breaker & 0x01))
+
+def p2p_attr_config_timeout(go_config_timeout=0, client_config_timeout=0):
+    return struct.pack("<BHBB", P2P_ATTR_CONFIGURATION_TIMEOUT, 2,
+                       go_config_timeout, client_config_timeout)
+
+def p2p_attr_listen_channel(op_class=81, chan=1):
+    return struct.pack("<BHBBBBB", P2P_ATTR_LISTEN_CHANNEL, 5,
+                       0x58, 0x58, 0x04, op_class, chan)
+
+def p2p_attr_group_bssid(addr):
+    val = struct.unpack('6B', binascii.unhexlify(addr.replace(':','')))
+    t = (P2P_ATTR_GROUP_BSSID, 6) + val
+    return struct.pack('<BH6B', *t)
+
+def p2p_attr_ext_listen_timing(period=0, interval=0):
+    return struct.pack("<BHHH", P2P_ATTR_EXT_LISTEN_TIMING, 4, period, interval)
+
+def p2p_attr_intended_interface_addr(addr):
+    val = struct.unpack('6B', binascii.unhexlify(addr.replace(':','')))
+    t = (P2P_ATTR_INTENDED_INTERFACE_ADDR, 6) + val
+    return struct.pack('<BH6B', *t)
+
+def p2p_attr_manageability(bitmap=0):
+    return struct.pack("<BHB", P2P_ATTR_MANAGEABILITY, 1, bitmap)
+
+def p2p_attr_channel_list():
+    return struct.pack("<BH3BBB11B", P2P_ATTR_CHANNEL_LIST, 16,
+                       0x58, 0x58, 0x04,
+                       81, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
+
+def p2p_attr_device_info(addr, name="Test", config_methods=0, dev_type="00010050F2040001"):
+    val = struct.unpack('6B', binascii.unhexlify(addr.replace(':','')))
+    val2 = struct.unpack('8B', binascii.unhexlify(dev_type))
+    t = (P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1 + 4 + len(name)) + val + (config_methods,) + val2 + (0,)
+    return struct.pack("<BH6BH8BB", *t) + struct.pack('>HH', 0x1011, len(name)) +name
+
+def p2p_attr_group_id(addr, ssid):
+    val = struct.unpack('6B', binascii.unhexlify(addr.replace(':','')))
+    t = (P2P_ATTR_GROUP_ID, 6 + len(ssid)) + val
+    return struct.pack('<BH6B', *t) + ssid
+
+def p2p_attr_operating_channel(op_class=81, chan=1):
+    return struct.pack("<BHBBBBB", P2P_ATTR_OPERATING_CHANNEL, 5,
+                       0x58, 0x58, 0x04, op_class, chan)
+
+def p2p_attr_invitation_flags(bitmap=0):
+    return struct.pack("<BHB", P2P_ATTR_INVITATION_FLAGS, 1, bitmap)
+
+def p2p_hdr_helper(dst, src, type=None, dialog_token=1, req=True):
+    msg = {}
+    msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+    msg['da'] = dst
+    msg['sa'] = src
+    if req:
+        msg['bssid'] = dst
+    else:
+        msg['bssid'] = src
+    msg['payload'] = struct.pack("<BBBBBB",
+                                 ACTION_CATEG_PUBLIC, 9, 0x50, 0x6f, 0x9a, 9)
+    if type is not None:
+        msg['payload'] += struct.pack("<B", type)
+        if dialog_token:
+            msg['payload'] += struct.pack("<B", dialog_token)
+    return msg
+
+def p2p_hdr(dst, src, type=None, dialog_token=1):
+    return p2p_hdr_helper(dst, src, type, dialog_token, True)
+
+def p2p_hdr_resp(dst, src, type=None, dialog_token=1):
+    return p2p_hdr_helper(dst, src, type, dialog_token, False)
+
+def start_p2p(dev, apdev):
+    addr0 = dev[0].p2p_dev_addr()
+    dev[0].p2p_listen()
+    dev[1].p2p_find(social=True)
+    ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+    if ev is None:
+        raise Exception("Device discovery timed out")
+    dev[1].p2p_stop_find()
+    peer = dev[1].get_peer(addr0)
+
+    bssid = apdev[0]['bssid']
+    params = { 'ssid': "test", 'beacon_int': "2000" }
+    if peer['listen_freq'] == "2412":
+        params['channel'] = '1'
+    elif peer['listen_freq'] == "2437":
+        params['channel'] = '6'
+    elif peer['listen_freq'] == "2462":
+        params['channel'] = '11'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd.set("ext_mgmt_frame_handling", "1")
+    return addr0, bssid, hapd, int(params['channel'])
+
+def p2p_probe(hapd, src, chan=1):
+    msg = {}
+    msg['fc'] = MGMT_SUBTYPE_PROBE_REQ << 4
+    msg['da'] = "ff:ff:ff:ff:ff:ff"
+    msg['sa'] = src
+    msg['bssid'] = "ff:ff:ff:ff:ff:ff"
+    attrs = p2p_attr_listen_channel(chan=chan)
+    msg['payload'] = ie_ssid("DIRECT-") + ie_supp_rates() + ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+def parse_p2p_public_action(payload):
+    pos = payload
+    (category, action) = struct.unpack('BB', pos[0:2])
+    if category != ACTION_CATEG_PUBLIC:
+        return None
+    if action != 9:
+        return None
+    pos = pos[2:]
+    (oui1,oui2,oui3,subtype) = struct.unpack('BBBB', pos[0:4])
+    if oui1 != 0x50 or oui2 != 0x6f or oui3 != 0x9a or subtype != 9:
+        return None
+    pos = pos[4:]
+    (subtype,dialog_token) = struct.unpack('BB', pos[0:2])
+    p2p = {}
+    p2p['subtype'] = subtype
+    p2p['dialog_token'] = dialog_token
+    pos = pos[2:]
+    p2p['elements'] = pos
+    while len(pos) > 2:
+        (id,elen) = struct.unpack('BB', pos[0:2])
+        pos = pos[2:]
+        if elen > len(pos):
+            raise Exception("Truncated IE in P2P Public Action frame (elen=%d left=%d)" % (elen, len(pos)))
+        if id == WLAN_EID_VENDOR_SPECIFIC:
+            if elen < 4:
+                raise Exception("Too short vendor specific IE in P2P Public Action frame (elen=%d)" % elen)
+            (oui1,oui2,oui3,subtype) = struct.unpack('BBBB', pos[0:4])
+            if oui1 == 0x50 and oui2 == 0x6f and oui3 == 0x9a and subtype == 9:
+                if 'p2p' in p2p:
+                    p2p['p2p'] += pos[4:elen]
+                else:
+                    p2p['p2p'] = pos[4:elen]
+            if oui1 == 0x00 and oui2 == 0x50 and oui3 == 0xf2 and subtype == 4:
+                p2p['wsc'] = pos[4:elen]
+        pos = pos[elen:]
+    if len(pos) > 0:
+        raise Exception("Invalid element in P2P Public Action frame")
+
+    if 'p2p' in p2p:
+        p2p['p2p_attrs'] = {}
+        pos = p2p['p2p']
+        while len(pos) >= 3:
+            (id,alen) = struct.unpack('<BH', pos[0:3])
+            pos = pos[3:]
+            if alen > len(pos):
+                logger.info("P2P payload: " + binascii.hexlify(p2p['p2p']))
+                raise Exception("Truncated P2P attribute in P2P Public Action frame (alen=%d left=%d p2p-payload=%d)" % (alen, len(pos), len(p2p['p2p'])))
+            p2p['p2p_attrs'][id] = pos[0:alen]
+            pos = pos[alen:]
+        if P2P_ATTR_STATUS in p2p['p2p_attrs']:
+            p2p['p2p_status'] = struct.unpack('B', p2p['p2p_attrs'][P2P_ATTR_STATUS])[0]
+
+    if 'wsc' in p2p:
+        p2p['wsc_attrs'] = {}
+        pos = p2p['wsc']
+        while len(pos) >= 4:
+            (id,alen) = struct.unpack('>HH', pos[0:4])
+            pos = pos[4:]
+            if alen > len(pos):
+                logger.info("WSC payload: " + binascii.hexlify(p2p['wsc']))
+                raise Exception("Truncated WSC attribute in P2P Public Action frame (alen=%d left=%d wsc-payload=%d)" % (alen, len(pos), len(p2p['wsc'])))
+            p2p['wsc_attrs'][id] = pos[0:alen]
+            pos = pos[alen:]
+
+    return p2p
+
+def test_p2p_msg_empty(dev, apdev):
+    """P2P protocol test: empty P2P Public Action frame"""
+    dst, src, hapd, channel = start_p2p(dev, apdev)
+    msg = p2p_hdr(dst, src)
+    hapd.mgmt_tx(msg)
+
+def test_p2p_msg_long_ssid(dev, apdev):
+    """P2P protocol test: Too long SSID in P2P Public Action frame"""
+    dst, src, hapd, channel = start_p2p(dev, apdev)
+
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=1)
+    attrs = p2p_attr_config_timeout()
+    attrs += p2p_attr_invitation_flags()
+    attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_bssid(src)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_group_id(src, 'DIRECT-foo')
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    msg['payload'] += ie_ssid(255 * 'A')
+    hapd.mgmt_tx(msg)
+    ev = dev[0].wait_event(["P2P-DEVICE-FOUND"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on device found event")
+
+def test_p2p_msg_long_dev_name(dev, apdev):
+    """P2P protocol test: Too long Device Name in P2P Public Action frame"""
+    dst, src, hapd, channel = start_p2p(dev, apdev)
+
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=1)
+    attrs = p2p_attr_config_timeout()
+    attrs += p2p_attr_invitation_flags()
+    attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_bssid(src)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_group_id(src, 'DIRECT-foo')
+    attrs += p2p_attr_device_info(src, config_methods=0x0108,
+                                  name="123456789012345678901234567890123")
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    ev = dev[0].wait_event(["P2P-DEVICE-FOUND"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected device found event")
+
+def test_p2p_msg_invitation_req(dev, apdev):
+    """P2P protocol tests for invitation request processing"""
+    dst, src, hapd, channel = start_p2p(dev, apdev)
+
+    # Empty P2P Invitation Request (missing dialog token)
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=None)
+    hapd.mgmt_tx(msg)
+    dialog_token = 0
+
+    # Various p2p_parse() failure cases due to invalid attributes
+
+    # Too short attribute header
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BB", P2P_ATTR_CAPABILITY, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Minimal attribute underflow
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH", P2P_ATTR_CAPABILITY, 1)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Large attribute underflow
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BHB", P2P_ATTR_CAPABILITY, 0xffff, 1)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short Capability attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BHB", P2P_ATTR_CAPABILITY, 1, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short Device ID attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    val = struct.unpack('5B', binascii.unhexlify("1122334455"))
+    t = (P2P_ATTR_DEVICE_ID, 5) + val
+    attrs = struct.pack('<BH5B', *t)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short GO Intent attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH", P2P_ATTR_GROUP_OWNER_INTENT, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short Status attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH", P2P_ATTR_STATUS, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # null Listen channel and too short Listen Channel attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH", P2P_ATTR_LISTEN_CHANNEL, 0)
+    attrs += struct.pack("<BHB", P2P_ATTR_LISTEN_CHANNEL, 1, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # null Operating channel and too short Operating Channel attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH", P2P_ATTR_OPERATING_CHANNEL, 0)
+    attrs += struct.pack("<BHB", P2P_ATTR_OPERATING_CHANNEL, 1, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short Channel List attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BHBB", P2P_ATTR_CHANNEL_LIST, 2, 1, 2)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short Device Info attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BHBB", P2P_ATTR_DEVICE_INFO, 2, 1, 2)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Truncated Secondary Device Types in Device Info attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH6BH8BB", P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1,
+                        0, 0, 0, 0, 0, 0,
+                        0,
+                        0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22,
+                        255)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Missing Device Name in Device Info attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH6BH8BB8B", P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1 + 8,
+                        0, 0, 0, 0, 0, 0,
+                        0,
+                        0, 0, 0, 0, 0, 0, 0, 0,
+                        1,
+                        1, 2, 3, 4, 5, 6, 7, 8)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Invalid Device Name header in Device Info attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH6BH8BB8B4B", P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1 + 8 + 4,
+                        0, 0, 0, 0, 0, 0,
+                        0,
+                        0, 0, 0, 0, 0, 0, 0, 0,
+                        1,
+                        1, 2, 3, 4, 5, 6, 7, 8,
+                        0x11, 0x12, 0, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Invalid Device Name header length in Device Info attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH6BH8BB8B4B", P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1 + 8 + 4,
+                        0, 0, 0, 0, 0, 0,
+                        0,
+                        0, 0, 0, 0, 0, 0, 0, 0,
+                        1,
+                        1, 2, 3, 4, 5, 6, 7, 8,
+                        0x10, 0x11, 0xff, 0xff)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Invalid Device Name header length in Device Info attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    devname = 'A'
+    attrs = struct.pack("<BH6BH8BB8B4B", P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1 + 8 + 4 + len(devname),
+                        0, 0, 0, 0, 0, 0,
+                        0,
+                        0, 0, 0, 0, 0, 0, 0, 0,
+                        1,
+                        1, 2, 3, 4, 5, 6, 7, 8,
+                        0x10, 0x11, 0, len(devname) + 1) + devname
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Device Name filtering and too long Device Name in Device Info attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH6BH8BB8B4B4B", P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1 + 8 + 4 + 4,
+                        0, 0, 0, 0, 0, 0,
+                        0,
+                        0, 0, 0, 0, 0, 0, 0, 0,
+                        1,
+                        1, 2, 3, 4, 5, 6, 7, 8,
+                        0x10, 0x11, 0, 4,
+                        64, 9, 0, 64)
+    devname = '123456789012345678901234567890123'
+    attrs += struct.pack("<BH6BH8BB8B4B", P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1 + 8 + 4 + len(devname),
+                         0, 0, 0, 0, 0, 0,
+                         0,
+                         0, 0, 0, 0, 0, 0, 0, 0,
+                         1,
+                         1, 2, 3, 4, 5, 6, 7, 8,
+                         0x10, 0x11, 0, len(devname)) + devname
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short Configuration Timeout attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BHB", P2P_ATTR_CONFIGURATION_TIMEOUT, 1, 1)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short Intended P2P Interface Address attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BHB", P2P_ATTR_INTENDED_INTERFACE_ADDR, 1, 1)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short P2P Group BSSID attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BHB", P2P_ATTR_GROUP_BSSID, 1, 1)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short P2P Group ID attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BHB", P2P_ATTR_GROUP_ID, 1, 1)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too long P2P Group ID attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH6B", P2P_ATTR_GROUP_ID, 6 + 33, 0, 0, 0, 0, 0, 0) + "123456789012345678901234567890123"
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short Invitation Flags attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH", P2P_ATTR_INVITATION_FLAGS, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Valid and too short Manageability attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_manageability()
+    attrs += struct.pack("<BH", P2P_ATTR_MANAGEABILITY, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short NoA attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BHB", P2P_ATTR_NOTICE_OF_ABSENCE, 1, 1)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Valid and too short Extended Listen Timing attributes
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_ext_listen_timing(period=100, interval=50)
+    attrs += struct.pack("<BHBBB", P2P_ATTR_EXT_LISTEN_TIMING, 3, 0, 0, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Valid and too short Minor Reason Code attributes
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_minor_reason_code(code=2)
+    attrs += struct.pack("<BH", P2P_ATTR_MINOR_REASON_CODE, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Unknown attribute and too short OOB GO Negotiation Channel attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BHB", 99, 1, 1)
+    attrs += struct.pack("<BHB", P2P_ATTR_OOB_GO_NEG_CHANNEL, 1, 1)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short Service Hash attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH5B", P2P_ATTR_SERVICE_HASH, 5, 1, 2, 3, 4, 5)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short Connection Capability attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH", P2P_ATTR_CONNECTION_CAPABILITY, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short Advertisement ID attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH9B", P2P_ATTR_ADVERTISEMENT_ID, 9, 1, 2, 3, 4, 5,
+                        6, 7, 8, 9)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Truncated and too short Service Instance attributes
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH8B", P2P_ATTR_ADVERTISED_SERVICE, 8, 1, 2, 3, 4, 5,
+                        6, 2, 8)
+    attrs += struct.pack("<BH7B", P2P_ATTR_ADVERTISED_SERVICE, 7, 1, 2, 3, 4, 5,
+                         6, 7)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short Session ID attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH4B", P2P_ATTR_SESSION_ID, 4, 1, 2, 3, 4)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short Feature Capability attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH", P2P_ATTR_FEATURE_CAPABILITY, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too short Persistent Group attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH5B", P2P_ATTR_PERSISTENT_GROUP, 5, 1, 2, 3, 4, 5)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    # Too long Persistent Group attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BH9L3B", P2P_ATTR_PERSISTENT_GROUP, 6 + 32 + 1,
+                        1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    if hapd.mgmt_rx(timeout=0.5) is not None:
+        raise Exception("Unexpected management frame received")
+
+    dev[0].dump_monitor()
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_config_timeout()
+    attrs += p2p_attr_invitation_flags()
+    attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_bssid(src)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_group_id(src, "DIRECT-foo")
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on device found event")
+    ev = dev[0].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on invitation event " + str(dialog_token))
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No invitation response " + str(dialog_token))
+
+    time.sleep(0.1)
+    dev[0].dump_monitor()
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_config_timeout()
+    attrs += p2p_attr_invitation_flags()
+    attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_bssid(src)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_group_id(src, "DIRECT-foo")
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    ev = dev[0].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on invitation event " + str(dialog_token))
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No invitation response " + str(dialog_token))
+
+    time.sleep(0.1)
+    dev[0].dump_monitor()
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    #attrs = p2p_attr_config_timeout()
+    attrs = p2p_attr_invitation_flags()
+    attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_bssid(src)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_group_id(src, "DIRECT-foo")
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No invitation response " + str(dialog_token))
+
+    time.sleep(0.1)
+    dev[0].dump_monitor()
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_config_timeout()
+    #attrs = p2p_attr_invitation_flags()
+    attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_bssid(src)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_group_id(src, "DIRECT-foo")
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No invitation response " + str(dialog_token))
+
+    time.sleep(0.1)
+    dev[0].dump_monitor()
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_config_timeout()
+    attrs = p2p_attr_invitation_flags()
+    #attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_bssid(src)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_group_id(src, "DIRECT-foo")
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No invitation response " + str(dialog_token))
+
+    time.sleep(0.1)
+    dev[0].dump_monitor()
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_config_timeout()
+    attrs = p2p_attr_invitation_flags()
+    attrs += p2p_attr_operating_channel()
+    #attrs += p2p_attr_group_bssid(src)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_group_id(src, "DIRECT-foo")
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No invitation response " + str(dialog_token))
+
+    time.sleep(0.1)
+    dev[0].dump_monitor()
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_config_timeout()
+    attrs = p2p_attr_invitation_flags()
+    attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_bssid(src)
+    #attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_group_id(src, "DIRECT-foo")
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No invitation response " + str(dialog_token))
+
+    time.sleep(0.1)
+    dev[0].dump_monitor()
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_config_timeout()
+    attrs = p2p_attr_invitation_flags()
+    attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_bssid(src)
+    attrs += p2p_attr_channel_list()
+    #attrs += p2p_attr_group_id(src, "DIRECT-foo")
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No invitation response " + str(dialog_token))
+
+    time.sleep(0.1)
+    dev[0].dump_monitor()
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_config_timeout()
+    attrs = p2p_attr_invitation_flags()
+    attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_bssid(src)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_group_id(src, "DIRECT-foo")
+    #attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No invitation response " + str(dialog_token))
+
+    time.sleep(0.1)
+    dev[0].dump_monitor()
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No invitation response " + str(dialog_token))
+
+    # Unusable peer operating channel preference
+    time.sleep(0.1)
+    dev[0].dump_monitor()
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_config_timeout()
+    attrs = p2p_attr_invitation_flags()
+    attrs += p2p_attr_operating_channel(chan=15)
+    attrs += p2p_attr_group_bssid(src)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_group_id(src, "DIRECT-foo")
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No invitation response " + str(dialog_token))
+
+def test_p2p_msg_invitation_req_to_go(dev, apdev):
+    """P2P protocol tests for invitation request processing on GO device"""
+    res = form(dev[0], dev[1])
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    peer = dev[1].get_peer(addr0)
+    listen_freq = peer['listen_freq']
+
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+
+    networks = dev[0].list_networks()
+    if len(networks) != 1:
+        raise Exception("Unexpected number of networks")
+    if "[P2P-PERSISTENT]" not in networks[0]['flags']:
+        raise Exception("Not the persistent group data")
+    dev[0].p2p_start_go(persistent=networks[0]['id'], freq=listen_freq)
+
+    dialog_token = 0
+
+    # Unusable peer operating channel preference
+    dialog_token += 1
+    msg = p2p_hdr(addr0, addr1, type=P2P_INVITATION_REQ,
+                  dialog_token=dialog_token)
+    attrs = p2p_attr_config_timeout()
+    attrs = p2p_attr_invitation_flags(bitmap=1)
+    attrs += p2p_attr_operating_channel(chan=15)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_group_id(res['go_dev_addr'], res['ssid'])
+    attrs += p2p_attr_device_info(addr1, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+
+    mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, peer['listen_freq'], binascii.hexlify(msg['payload'])))
+
+    rx_msg = dev[1].mgmt_rx()
+    if rx_msg is None:
+        raise Exception("MGMT-RX timeout")
+    p2p = parse_p2p_public_action(rx_msg['payload'])
+    if p2p is None:
+        raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+    if p2p['subtype'] != P2P_INVITATION_RESP:
+        raise Exception("Unexpected subtype %d" % p2p['subtype'])
+    if p2p['p2p_status'] != 0:
+        raise Exception("Unexpected status %d" % p2p['p2p_status'])
+
+    # Forced channel re-selection due to channel list
+    dialog_token += 1
+    msg = p2p_hdr(addr0, addr1, type=P2P_INVITATION_REQ,
+                  dialog_token=dialog_token)
+    attrs = p2p_attr_config_timeout()
+    attrs = p2p_attr_invitation_flags(bitmap=1)
+    attrs += struct.pack("<BH3BBBB", P2P_ATTR_CHANNEL_LIST, 6,
+                         0x58, 0x58, 0x04,
+                         81, 1, 3)
+    attrs += p2p_attr_group_id(res['go_dev_addr'], res['ssid'])
+    attrs += p2p_attr_device_info(addr1, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+
+    mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, peer['listen_freq'], binascii.hexlify(msg['payload'])))
+
+    rx_msg = dev[1].mgmt_rx()
+    if rx_msg is None:
+        raise Exception("MGMT-RX timeout")
+    p2p = parse_p2p_public_action(rx_msg['payload'])
+    if p2p is None:
+        raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+    if p2p['subtype'] != P2P_INVITATION_RESP:
+        raise Exception("Unexpected subtype %d" % p2p['subtype'])
+    if p2p['p2p_status'] != 7 and dev[1].get_mcc() <= 1:
+        raise Exception("Unexpected status %d" % p2p['p2p_status'])
+
+def test_p2p_msg_invitation_req_unknown(dev, apdev):
+    """P2P protocol tests for invitation request from unknown peer"""
+    dst, src, hapd, channel = start_p2p(dev, apdev)
+    dialog_token = 0
+
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_config_timeout()
+    attrs += p2p_attr_invitation_flags()
+    attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_bssid(src)
+    attrs += p2p_attr_channel_list()
+    #attrs += p2p_attr_group_id(src, "DIRECT-foo")
+    #attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    ev = dev[0].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on invitation event " + str(dialog_token))
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No invitation response " + str(dialog_token))
+
+def test_p2p_msg_invitation_no_common_channels(dev, apdev):
+    """P2P protocol tests for invitation request without common channels"""
+    dst, src, hapd, channel = start_p2p(dev, apdev)
+    dialog_token = 0
+
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_config_timeout()
+    attrs += p2p_attr_invitation_flags()
+    attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_bssid(src)
+    attrs += struct.pack("<BH3BBB", P2P_ATTR_CHANNEL_LIST, 5,
+                         0x58, 0x58, 0x04,
+                         81, 0)
+    attrs += p2p_attr_group_id(src, "DIRECT-foo")
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No invitation response " + str(dialog_token))
+    ev = dev[0].wait_event(["P2P-INVITATION-RECEIVED"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected invitation event")
+
+def test_p2p_msg_invitation_resp(dev, apdev):
+    """P2P protocol tests for invitation response processing"""
+    form(dev[0], dev[1])
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+
+    dst, src, hapd, channel = start_p2p(dev, apdev)
+
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    peer = dev[1].get_peer(addr0)
+
+    # P2P Invitation Response from unknown peer
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_RESP, dialog_token=1)
+    hapd.mgmt_tx(msg)
+
+    # P2P Invitation Response from peer that is not in invitation
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_RESP, dialog_token=2)
+    attrs = p2p_attr_status()
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, peer['listen_freq'], binascii.hexlify(msg['payload'])))
+    time.sleep(0.25)
+
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+
+    invite(dev[0], dev[1])
+    rx_msg = dev[1].mgmt_rx()
+    if rx_msg is None:
+        raise Exception("MGMT-RX timeout")
+    p2p = parse_p2p_public_action(rx_msg['payload'])
+    if p2p is None:
+        raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+    if p2p['subtype'] != P2P_INVITATION_REQ:
+        raise Exception("Unexpected subtype %d" % p2p['subtype'])
+
+    # Invalid attribute to cause p2p_parse() failure
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_RESP, dialog_token=p2p['dialog_token'])
+    attrs = struct.pack("<BB", P2P_ATTR_CAPABILITY, 0)
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload'])))
+
+    invite(dev[0], dev[1])
+    rx_msg = dev[1].mgmt_rx()
+    if rx_msg is None:
+        raise Exception("MGMT-RX timeout")
+    p2p = parse_p2p_public_action(rx_msg['payload'])
+    if p2p is None:
+        raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+    if p2p['subtype'] != P2P_INVITATION_REQ:
+        raise Exception("Unexpected subtype %d" % p2p['subtype'])
+
+    # missing mandatory Status attribute
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_RESP, dialog_token=p2p['dialog_token'])
+    attrs = p2p_attr_channel_list()
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload'])))
+
+    invite(dev[0], dev[1])
+    rx_msg = dev[1].mgmt_rx()
+    if rx_msg is None:
+        raise Exception("MGMT-RX timeout")
+    p2p = parse_p2p_public_action(rx_msg['payload'])
+    if p2p is None:
+        raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+    if p2p['subtype'] != P2P_INVITATION_REQ:
+        raise Exception("Unexpected subtype %d" % p2p['subtype'])
+
+    # no channel match (no common channel found at all)
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_RESP, dialog_token=p2p['dialog_token'])
+    attrs = p2p_attr_status()
+    attrs += struct.pack("<BH3BBBB", P2P_ATTR_CHANNEL_LIST, 6,
+                         0x58, 0x58, 0x04,
+                         81, 1, 15)
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload'])))
+
+    invite(dev[0], dev[1])
+    rx_msg = dev[1].mgmt_rx()
+    if rx_msg is None:
+        raise Exception("MGMT-RX timeout")
+    p2p = parse_p2p_public_action(rx_msg['payload'])
+    if p2p is None:
+        raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+    if p2p['subtype'] != P2P_INVITATION_REQ:
+        raise Exception("Unexpected subtype %d" % p2p['subtype'])
+
+    # no channel match (no acceptable P2P channel)
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_RESP, dialog_token=p2p['dialog_token'])
+    attrs = p2p_attr_status()
+    attrs += struct.pack("<BH3BBBB", P2P_ATTR_CHANNEL_LIST, 6,
+                         0x58, 0x58, 0x04,
+                         81, 1, 12)
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload'])))
+
+    invite(dev[0], dev[1])
+    rx_msg = dev[1].mgmt_rx()
+    if rx_msg is None:
+        raise Exception("MGMT-RX timeout")
+    p2p = parse_p2p_public_action(rx_msg['payload'])
+    if p2p is None:
+        raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+    if p2p['subtype'] != P2P_INVITATION_REQ:
+        raise Exception("Unexpected subtype %d" % p2p['subtype'])
+
+    # missing mandatory Channel List attribute (ignored as a workaround)
+    msg = p2p_hdr(dst, src, type=P2P_INVITATION_RESP, dialog_token=p2p['dialog_token'])
+    attrs = p2p_attr_status()
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload'])))
+
+    ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15);
+    if ev is None:
+        raise Exception("Group was not started")
+
+def test_p2p_msg_invitation_resend(dev, apdev):
+    """P2P protocol tests for invitation resending on no-common-channels"""
+    form(dev[0], dev[1])
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+
+    logger.info("Forced channel in invitation")
+    invite(dev[0], dev[1], extra="freq=2422")
+    rx_msg = dev[1].mgmt_rx()
+    if rx_msg is None:
+        raise Exception("MGMT-RX timeout")
+    p2p = parse_p2p_public_action(rx_msg['payload'])
+    if p2p is None:
+        raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+    if p2p['subtype'] != P2P_INVITATION_REQ:
+        raise Exception("Unexpected subtype %d" % p2p['subtype'])
+    msg = p2p_hdr(addr0, addr1, type=P2P_INVITATION_RESP,
+                  dialog_token=p2p['dialog_token'])
+    attrs = p2p_attr_status(status=P2P_SC_FAIL_NO_COMMON_CHANNELS)
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload'])))
+    ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on invitation result");
+    if "status=7" not in ev:
+        raise Exception("Unexpected invitation result: " + ev)
+
+    logger.info("Any channel allowed, only preference provided in invitation");
+    invite(dev[0], dev[1], extra="pref=2422")
+    rx_msg = dev[1].mgmt_rx()
+    if rx_msg is None:
+        raise Exception("MGMT-RX timeout")
+    p2p = parse_p2p_public_action(rx_msg['payload'])
+    if p2p is None:
+        raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+    if p2p['subtype'] != P2P_INVITATION_REQ:
+        raise Exception("Unexpected subtype %d" % p2p['subtype'])
+    msg = p2p_hdr(addr0, addr1, type=P2P_INVITATION_RESP,
+                  dialog_token=p2p['dialog_token'])
+    attrs = p2p_attr_status(status=P2P_SC_FAIL_NO_COMMON_CHANNELS)
+    msg['payload'] += ie_p2p(attrs)
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 0"):
+        raise Exception("Failed to disable external management frame handling")
+    mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload'])))
+    ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on invitation result");
+    if "status=0" not in ev:
+        raise Exception("Unexpected invitation result: " + ev)
+
+    ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15);
+    if ev is None:
+        raise Exception("Group was not started on dev0")
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15);
+    if ev is None:
+        raise Exception("Group was not started on dev1")
+
+def test_p2p_msg_invitation_resend_duplicate(dev, apdev):
+    """P2P protocol tests for invitation resending on no-common-channels and duplicated response"""
+    form(dev[0], dev[1])
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+
+    logger.info("Any channel allowed, only preference provided in invitation");
+    invite(dev[0], dev[1], extra="pref=2422")
+    rx_msg = dev[1].mgmt_rx()
+    if rx_msg is None:
+        raise Exception("MGMT-RX timeout")
+    p2p = parse_p2p_public_action(rx_msg['payload'])
+    if p2p is None:
+        raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+    if p2p['subtype'] != P2P_INVITATION_REQ:
+        raise Exception("Unexpected subtype %d" % p2p['subtype'])
+    msg = p2p_hdr(addr0, addr1, type=P2P_INVITATION_RESP,
+                  dialog_token=p2p['dialog_token'])
+    attrs = p2p_attr_status(status=P2P_SC_FAIL_NO_COMMON_CHANNELS)
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload'])))
+
+    rx_msg = dev[1].mgmt_rx()
+    if rx_msg is None:
+        raise Exception("MGMT-RX timeout")
+    p2p = parse_p2p_public_action(rx_msg['payload'])
+    if p2p is None:
+        raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+    if p2p['subtype'] != P2P_INVITATION_REQ:
+        raise Exception("Unexpected subtype %d" % p2p['subtype'])
+
+    logger.info("Retransmit duplicate of previous response")
+    mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload'])))
+
+    logger.info("Transmit real response")
+    msg = p2p_hdr(addr0, addr1, type=P2P_INVITATION_RESP,
+                  dialog_token=p2p['dialog_token'])
+    attrs = p2p_attr_status(status=P2P_SC_SUCCESS)
+    attrs += p2p_attr_channel_list()
+    msg['payload'] += ie_p2p(attrs)
+    if "FAIL" in dev[1].request("MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload']))):
+        raise Exception("Failed to transmit real response")
+    dev[1].request("SET ext_mgmt_frame_handling 0")
+
+    ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on invitation result");
+    if "status=0" not in ev:
+        raise Exception("Unexpected invitation result: " + ev)
+    ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+    if ev is None:
+        raise Exception("Group formation timed out")
+    dev[0].group_form_result(ev)
+    dev[0].remove_group()
+
+def test_p2p_msg_pd_req(dev, apdev):
+    """P2P protocol tests for provision discovery request processing"""
+    dst, src, hapd, channel = start_p2p(dev, apdev)
+    dialog_token = 0
+
+    # Too short attribute header
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_PROV_DISC_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BB", P2P_ATTR_CAPABILITY, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+
+    if hapd.mgmt_rx(timeout=0.5) is not None:
+        raise Exception("Unexpected management frame received")
+
+    # No attributes
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_PROV_DISC_REQ, dialog_token=dialog_token)
+    attrs = ""
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No PD response " + str(dialog_token))
+
+    # Valid request
+    time.sleep(0.1)
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_PROV_DISC_REQ, dialog_token=dialog_token)
+    attrs = wsc_attr_config_methods(methods=0x1008)
+    msg['payload'] += ie_wsc(attrs)
+    attrs = p2p_attr_capability()
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on device found event")
+    ev = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on PD event")
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No PD response " + str(dialog_token))
+
+    # Unknown group
+    time.sleep(0.1)
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_PROV_DISC_REQ, dialog_token=dialog_token)
+    attrs = wsc_attr_config_methods(methods=0x1008)
+    msg['payload'] += ie_wsc(attrs)
+    attrs = p2p_attr_capability()
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    attrs += p2p_attr_group_id("02:02:02:02:02:02", "DIRECT-foo")
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No PD response " + str(dialog_token))
+    ev = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected PD event")
+
+    # Listen channel is not yet known
+    if "FAIL" not in dev[0].global_request("P2P_PROV_DISC " + src + " display"):
+        raise Exception("Unexpected P2P_PROV_DISC success")
+
+    # Unknown peer
+    if "FAIL" not in dev[0].global_request("P2P_PROV_DISC 02:03:04:05:06:07 display"):
+        raise Exception("Unexpected P2P_PROV_DISC success (2)")
+
+def test_p2p_msg_pd(dev, apdev):
+    """P2P protocol tests for provision discovery request processing (known)"""
+    dst, src, hapd, channel = start_p2p(dev, apdev)
+    dialog_token = 0
+
+    p2p_probe(hapd, src, chan=channel)
+    time.sleep(0.1)
+
+    # Valid request
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_PROV_DISC_REQ, dialog_token=dialog_token)
+    attrs = wsc_attr_config_methods(methods=0x1008)
+    msg['payload'] += ie_wsc(attrs)
+    attrs = p2p_attr_capability()
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on device found event")
+    ev = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on PD event")
+    if hapd.mgmt_rx(timeout=1) is None:
+        raise Exception("No PD response " + str(dialog_token))
+
+    if "FAIL" in dev[0].global_request("P2P_PROV_DISC " + src + " display"):
+        raise Exception("Unexpected P2P_PROV_DISC failure")
+    frame = hapd.mgmt_rx(timeout=1)
+    if frame is None:
+        raise Exception("No PD request " + str(dialog_token))
+    p2p = parse_p2p_public_action(frame['payload'])
+    if p2p is None:
+        raise Exception("Failed to parse PD request")
+
+    # invalid dialog token
+    msg = p2p_hdr_resp(dst, src, type=P2P_PROV_DISC_RESP,
+                       dialog_token=p2p['dialog_token'] + 1)
+    hapd.mgmt_tx(msg)
+    ev = dev[0].wait_global_event(["P2P-PROV-DISC-FAILURE"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected PD result event")
+
+    # valid dialog token
+    msg = p2p_hdr_resp(dst, src, type=P2P_PROV_DISC_RESP,
+                       dialog_token=p2p['dialog_token'])
+    hapd.mgmt_tx(msg)
+    ev = dev[0].wait_global_event(["P2P-PROV-DISC-FAILURE"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on PD result event")
+
+    # valid dialog token
+    msg = p2p_hdr_resp(dst, src, type=P2P_PROV_DISC_RESP,
+                       dialog_token=p2p['dialog_token'])
+    hapd.mgmt_tx(msg)
+    ev = dev[0].wait_global_event(["P2P-PROV-DISC-FAILURE"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected PD result event")
+
+def check_p2p_response(hapd, dialog_token, status):
+    resp = hapd.mgmt_rx(timeout=2)
+    if resp is None:
+        raise Exception("No GO Neg Response " + str(dialog_token))
+    p2p = parse_p2p_public_action(resp['payload'])
+    if p2p is None:
+        raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+    if dialog_token != p2p['dialog_token']:
+        raise Exception("Unexpected dialog token in response")
+    if p2p['p2p_status'] != status:
+        raise Exception("Unexpected status code %s in response (expected %d)" % (p2p['p2p_status'], status))
+
+def test_p2p_msg_go_neg_both_start(dev, apdev):
+    """P2P protocol test for simultaneous GO Neg initiation"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    dev[0].p2p_listen()
+    dev[1].discover_peer(addr0)
+    dev[1].p2p_listen()
+    dev[0].discover_peer(addr1)
+    dev[0].p2p_listen()
+    if "FAIL" in dev[0].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+    dev[0].request("P2P_CONNECT {} pbc".format(addr1))
+    dev[1].request("P2P_CONNECT {} pbc".format(addr0))
+    msg = dev[0].mgmt_rx()
+    if msg is None:
+        raise Exception("MGMT-RX timeout")
+    msg = dev[1].mgmt_rx()
+    if msg is None:
+        raise Exception("MGMT-RX timeout(2)")
+    if "FAIL" in dev[0].request("SET ext_mgmt_frame_handling 0"):
+        raise Exception("Failed to disable external management frame handling")
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=2)
+    if ev is not None:
+        raise Exception("Unexpected GO Neg success")
+    if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 0"):
+        raise Exception("Failed to disable external management frame handling")
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("GO Neg did not succeed")
+    ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=5);
+    if ev is None:
+        raise Exception("Group formation not succeed")
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=5);
+    if ev is None:
+        raise Exception("Group formation not succeed")
+
+def test_p2p_msg_go_neg_req(dev, apdev):
+    """P2P protocol tests for invitation request from unknown peer"""
+    dst, src, hapd, channel = start_p2p(dev, apdev)
+    dialog_token = 0
+
+    # invalid attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+    attrs = struct.pack("<BB", P2P_ATTR_CAPABILITY, 0)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    frame = hapd.mgmt_rx(timeout=0.1)
+    if frame is not None:
+        print frame
+        raise Exception("Unexpected GO Neg Response")
+
+    # missing atributes
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_capability()
+    attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    #attrs += p2p_attr_listen_channel()
+    attrs += p2p_attr_ext_listen_timing()
+    attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=2) is None:
+        raise Exception("No GO Neg Response " + str(dialog_token))
+    time.sleep(0.1)
+
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_capability()
+    attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_listen_channel()
+    attrs += p2p_attr_ext_listen_timing()
+    attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    #attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=2) is None:
+        raise Exception("No GO Neg Response " + str(dialog_token))
+    time.sleep(0.1)
+
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_capability()
+    attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_listen_channel()
+    attrs += p2p_attr_ext_listen_timing()
+    attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+    #attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=2) is None:
+        raise Exception("No GO Neg Response " + str(dialog_token))
+    time.sleep(0.1)
+
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_capability()
+    attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_listen_channel()
+    attrs += p2p_attr_ext_listen_timing()
+    #attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=2) is None:
+        raise Exception("No GO Neg Response " + str(dialog_token))
+    time.sleep(0.1)
+
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_capability()
+    attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_listen_channel()
+    attrs += p2p_attr_ext_listen_timing()
+    attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+    attrs += p2p_attr_channel_list()
+    #attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=2) is None:
+        raise Exception("No GO Neg Response " + str(dialog_token))
+    time.sleep(0.1)
+
+    # SA != P2P Device address
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_capability()
+    attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_listen_channel()
+    attrs += p2p_attr_ext_listen_timing()
+    attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info("02:02:02:02:02:02", config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=2) is None:
+        raise Exception("No GO Neg Response " + str(dialog_token))
+    time.sleep(0.1)
+
+    # unexpected Status attribute
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_capability()
+    attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_listen_channel()
+    attrs += p2p_attr_ext_listen_timing()
+    attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_status(status=P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=2) is None:
+        raise Exception("No GO Neg Response(1) " + str(dialog_token))
+    time.sleep(0.1)
+
+    # valid (with workarounds) GO Neg Req
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+    #attrs = p2p_attr_capability()
+    #attrs += p2p_attr_go_intent()
+    #attrs += p2p_attr_config_timeout()
+    attrs = p2p_attr_listen_channel()
+    attrs += p2p_attr_ext_listen_timing()
+    attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    check_p2p_response(hapd, dialog_token,
+                       P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=2)
+    if ev is None:
+        raise Exception("Timeout on GO Neg event " + str(dialog_token))
+
+    dev[0].request("P2P_CONNECT " + src + " 12345670 display auth")
+
+    # ready - missing attributes (with workarounds) GO Neg Req
+    time.sleep(0.1)
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+    #attrs = p2p_attr_capability()
+    #attrs += p2p_attr_go_intent()
+    #attrs += p2p_attr_config_timeout()
+    attrs = p2p_attr_listen_channel()
+    attrs += p2p_attr_ext_listen_timing()
+    attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    if hapd.mgmt_rx(timeout=2) is None:
+        raise Exception("No GO Neg Response " + str(dialog_token))
+
+    # ready - invalid GO Intent GO Neg Req
+    time.sleep(0.1)
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+    #attrs = p2p_attr_capability()
+    attrs = p2p_attr_go_intent(go_intent=16)
+    #attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_listen_channel()
+    attrs += p2p_attr_ext_listen_timing()
+    attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    check_p2p_response(hapd, dialog_token, P2P_SC_FAIL_INVALID_PARAMS)
+
+    # ready - invalid Channel List
+    time.sleep(0.1)
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_capability()
+    attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_listen_channel()
+    attrs += p2p_attr_ext_listen_timing()
+    attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+    attrs += struct.pack("<BH3BBB11B", P2P_ATTR_CHANNEL_LIST, 16,
+                         0x58, 0x58, 0x04,
+                         81, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    check_p2p_response(hapd, dialog_token, P2P_SC_FAIL_NO_COMMON_CHANNELS)
+
+    # ready - invalid GO Neg Req (unsupported Device Password ID)
+    time.sleep(0.1)
+    dialog_token += 1
+    msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+    attrs = p2p_attr_capability()
+    attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_listen_channel()
+    attrs += p2p_attr_ext_listen_timing()
+    attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+    # very long channel list
+    attrs += struct.pack("<BH3BBB11B30B", P2P_ATTR_CHANNEL_LIST, 46,
+                         0x58, 0x58, 0x04,
+                         81, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+                         1, 1, 1, 2, 1, 2, 3, 1, 3, 4, 1, 4, 5, 1, 5,
+                         6, 1, 6, 7, 1, 7, 8, 1, 8, 9, 1, 9, 10, 1, 10)
+    attrs += p2p_attr_device_info(src, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    hapd.mgmt_tx(msg)
+    check_p2p_response(hapd, dialog_token, P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD)
+
+def mgmt_tx(dev, msg):
+    for i in range(0, 20):
+        if "FAIL" in dev.request(msg):
+            raise Exception("Failed to send Action frame")
+        ev = dev.wait_event(["MGMT-TX-STATUS"], timeout=10)
+        if ev is None:
+            raise Exception("Timeout on MGMT-TX-STATUS")
+        if "result=SUCCESS" in ev:
+            break
+        time.sleep(0.01)
+    if "result=SUCCESS" not in ev:
+        raise Exception("Peer did not ack Action frame")
+
+def rx_go_neg_req(dev):
+    msg = dev.mgmt_rx()
+    if msg is None:
+        raise Exception("MGMT-RX timeout")
+    p2p = parse_p2p_public_action(msg['payload'])
+    if p2p is None:
+        raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+    if p2p['subtype'] != P2P_GO_NEG_REQ:
+        raise Exception("Unexpected subtype %d" % p2p['subtype'])
+    p2p['freq'] = msg['freq']
+    return p2p
+
+def rx_go_neg_conf(dev, status=None, dialog_token=None):
+    msg = dev.mgmt_rx()
+    if msg is None:
+        raise Exception("MGMT-RX timeout")
+    p2p = parse_p2p_public_action(msg['payload'])
+    if p2p is None:
+        raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+    if p2p['subtype'] != P2P_GO_NEG_CONF:
+        raise Exception("Unexpected subtype %d" % p2p['subtype'])
+    if dialog_token is not None and dialog_token != p2p['dialog_token']:
+        raise Exception("Unexpected dialog token")
+    if status is not None and p2p['p2p_status'] != status:
+        raise Exception("Unexpected status %d" % p2p['p2p_status'])
+
+def check_p2p_go_neg_fail_event(dev, status):
+    ev = dev.wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=5)
+    if ev is None:
+        raise Exception("GO Negotiation failure not reported")
+    if "status=%d" % status not in ev:
+        raise Exception("Unexpected failure reason: " + ev)
+
+def test_p2p_msg_go_neg_req_reject(dev, apdev):
+    """P2P protocol tests for user reject incorrectly in GO Neg Req"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    dev[0].p2p_listen()
+    dev[1].discover_peer(addr0)
+    dev[1].group_request("P2P_CONNECT " + addr0 + " pbc")
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on GO Neg Req")
+
+    peer = dev[0].get_peer(addr1)
+    dev[0].p2p_stop_find()
+
+    msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_REQ, dialog_token=123)
+    attrs = p2p_attr_capability()
+    attrs += p2p_attr_status(status=P2P_SC_FAIL_REJECTED_BY_USER)
+    attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_listen_channel()
+    attrs += p2p_attr_ext_listen_timing()
+    attrs += p2p_attr_intended_interface_addr(addr0)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+
+    mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=10 no_cck=1 action={}".format(addr1, addr1, peer['listen_freq'], binascii.hexlify(msg['payload'])))
+
+    ev = dev[1].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=5)
+    if ev is None:
+        raise Exception("GO Negotiation failure not reported")
+    if "status=%d" % P2P_SC_FAIL_REJECTED_BY_USER not in ev:
+        raise Exception("Unexpected failure reason: " + ev)
+
+def test_p2p_msg_unexpected_go_neg_resp(dev, apdev):
+    """P2P protocol tests for unexpected GO Neg Resp"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    dev[1].p2p_listen()
+    dev[0].discover_peer(addr1)
+    dev[0].p2p_stop_find()
+
+    peer = dev[0].get_peer(addr1)
+
+    logger.debug("GO Neg Resp without GO Neg session")
+    msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=123)
+    attrs = p2p_attr_status()
+    attrs += p2p_attr_capability()
+    attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_intended_interface_addr(addr0)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=10 no_cck=1 action={}".format(addr1, addr1, peer['listen_freq'], binascii.hexlify(msg['payload'])))
+
+    dev[0].p2p_listen()
+    dev[1].discover_peer(addr0)
+
+    logger.debug("Unexpected GO Neg Resp while waiting for new GO Neg session")
+    if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc"):
+        raise Exception("P2P_CONNECT failed")
+    ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on GO Neg Req")
+    dev[0].p2p_stop_find()
+    mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=10 no_cck=1 action={}".format(addr1, addr1, peer['listen_freq'], binascii.hexlify(msg['payload'])))
+
+    logger.debug("Invalid attribute in GO Neg Response")
+    msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=197)
+    attrs = struct.pack("<BB", P2P_ATTR_CAPABILITY, 0)
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=10 no_cck=1 action={}".format(addr1, addr1, peer['listen_freq'], binascii.hexlify(msg['payload'])))
+    frame = dev[0].mgmt_rx(timeout=0.1)
+    if frame is not None:
+        raise Exception("Unexpected GO Neg Confirm")
+
+    logger.debug("GO Neg Resp with unexpected dialog token")
+    dev[1].p2p_stop_find()
+    if "FAIL" in dev[0].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+    dev[0].p2p_listen()
+    if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc"):
+        raise Exception("P2P_CONNECT failed(2)")
+    p2p = rx_go_neg_req(dev[0])
+    dev[0].p2p_stop_find()
+    dialog_token = p2p['dialog_token']
+    if dialog_token < 255:
+        dialog_token += 1
+    else:
+        dialog_token = 1
+    msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+    attrs = p2p_attr_status()
+    attrs += p2p_attr_capability()
+    attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_intended_interface_addr(addr0)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload'])))
+
+    logger.debug("GO Neg Resp without Status")
+    dev[1].p2p_stop_find()
+    dev[0].p2p_listen()
+    if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc"):
+        raise Exception("P2P_CONNECT failed(2)")
+    p2p = rx_go_neg_req(dev[0])
+    dev[0].p2p_stop_find()
+    dialog_token = p2p['dialog_token']
+    msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+    #attrs = p2p_attr_status()
+    attrs = p2p_attr_capability()
+    attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_intended_interface_addr(addr0)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload'])))
+    check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INVALID_PARAMS)
+    rx_go_neg_conf(dev[0], P2P_SC_FAIL_INVALID_PARAMS, dialog_token)
+
+    logger.debug("GO Neg Resp without Intended Address")
+    dev[1].p2p_stop_find()
+    dev[0].p2p_listen()
+    if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc"):
+        raise Exception("P2P_CONNECT failed(2)")
+    p2p = rx_go_neg_req(dev[0])
+    dev[0].p2p_stop_find()
+    dialog_token = p2p['dialog_token']
+    msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+    attrs = p2p_attr_status()
+    #attrs += p2p_attr_capability()
+    attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    #attrs += p2p_attr_intended_interface_addr(addr0)
+    attrs += p2p_attr_channel_list()
+    #attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload'])))
+    check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INVALID_PARAMS)
+    rx_go_neg_conf(dev[0], P2P_SC_FAIL_INVALID_PARAMS, dialog_token)
+
+    logger.debug("GO Neg Resp without GO Intent")
+    dev[1].p2p_stop_find()
+    dev[0].p2p_listen()
+    if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc"):
+        raise Exception("P2P_CONNECT failed(2)")
+    p2p = rx_go_neg_req(dev[0])
+    dev[0].p2p_stop_find()
+    dialog_token = p2p['dialog_token']
+    msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+    attrs = p2p_attr_status()
+    attrs += p2p_attr_capability()
+    #attrs += p2p_attr_go_intent()
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_intended_interface_addr(addr0)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload'])))
+    check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INVALID_PARAMS)
+    rx_go_neg_conf(dev[0], P2P_SC_FAIL_INVALID_PARAMS, dialog_token)
+
+    logger.debug("GO Neg Resp with invalid GO Intent")
+    dev[1].p2p_stop_find()
+    dev[0].p2p_listen()
+    if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc"):
+        raise Exception("P2P_CONNECT failed(2)")
+    p2p = rx_go_neg_req(dev[0])
+    dev[0].p2p_stop_find()
+    dialog_token = p2p['dialog_token']
+    msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+    attrs = p2p_attr_status()
+    attrs += p2p_attr_capability()
+    attrs += p2p_attr_go_intent(go_intent=16)
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_intended_interface_addr(addr0)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload'])))
+    check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INVALID_PARAMS)
+    rx_go_neg_conf(dev[0], P2P_SC_FAIL_INVALID_PARAMS, dialog_token)
+
+    logger.debug("GO Neg Resp with incompatible GO Intent")
+    dev[1].p2p_stop_find()
+    dev[0].p2p_listen()
+    if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc go_intent=15"):
+        raise Exception("P2P_CONNECT failed(2)")
+    p2p = rx_go_neg_req(dev[0])
+    dev[0].p2p_stop_find()
+    dialog_token = p2p['dialog_token']
+    msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+    attrs = p2p_attr_status()
+    attrs += p2p_attr_capability()
+    attrs += p2p_attr_go_intent(go_intent=15)
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_intended_interface_addr(addr0)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload'])))
+    check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INCOMPATIBLE_PARAMS)
+    rx_go_neg_conf(dev[0], P2P_SC_FAIL_INCOMPATIBLE_PARAMS, dialog_token)
+
+    logger.debug("GO Neg Resp without P2P Group ID")
+    dev[1].p2p_stop_find()
+    dev[0].p2p_listen()
+    if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc go_intent=0"):
+        raise Exception("P2P_CONNECT failed(2)")
+    p2p = rx_go_neg_req(dev[0])
+    dev[0].p2p_stop_find()
+    dialog_token = p2p['dialog_token']
+    msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+    attrs = p2p_attr_status()
+    attrs += p2p_attr_capability()
+    attrs += p2p_attr_go_intent(go_intent=15)
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_intended_interface_addr(addr0)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    #attrs += p2p_attr_group_id(src, "DIRECT-foo")
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload'])))
+    check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INVALID_PARAMS)
+    rx_go_neg_conf(dev[0], P2P_SC_FAIL_INVALID_PARAMS, dialog_token)
+
+    logger.debug("GO Neg Resp without Operating Channel")
+    dev[1].p2p_stop_find()
+    dev[0].p2p_listen()
+    if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc go_intent=0"):
+        raise Exception("P2P_CONNECT failed(2)")
+    p2p = rx_go_neg_req(dev[0])
+    dev[0].p2p_stop_find()
+    dialog_token = p2p['dialog_token']
+    msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+    attrs = p2p_attr_status()
+    attrs += p2p_attr_capability()
+    attrs += p2p_attr_go_intent(go_intent=15)
+    #attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_intended_interface_addr(addr0)
+    attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+    #attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_id(addr0, "DIRECT-foo")
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload'])))
+    check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INVALID_PARAMS)
+    rx_go_neg_conf(dev[0], P2P_SC_FAIL_INVALID_PARAMS, dialog_token)
+
+    logger.debug("GO Neg Resp without Channel List")
+    dev[1].p2p_stop_find()
+    dev[0].p2p_listen()
+    if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc go_intent=0"):
+        raise Exception("P2P_CONNECT failed(2)")
+    p2p = rx_go_neg_req(dev[0])
+    dev[0].p2p_stop_find()
+    dialog_token = p2p['dialog_token']
+    msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+    attrs = p2p_attr_status()
+    attrs += p2p_attr_capability()
+    attrs += p2p_attr_go_intent(go_intent=15)
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_intended_interface_addr(addr0)
+    #attrs += p2p_attr_channel_list()
+    attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_id(addr0, "DIRECT-foo")
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload'])))
+    check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INVALID_PARAMS)
+    rx_go_neg_conf(dev[0], P2P_SC_FAIL_INVALID_PARAMS, dialog_token)
+
+    logger.debug("GO Neg Resp without common channels")
+    dev[1].p2p_stop_find()
+    dev[0].p2p_listen()
+    if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc go_intent=0"):
+        raise Exception("P2P_CONNECT failed(2)")
+    p2p = rx_go_neg_req(dev[0])
+    dev[0].p2p_stop_find()
+    dialog_token = p2p['dialog_token']
+    msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+    attrs = p2p_attr_status()
+    attrs += p2p_attr_capability()
+    attrs += p2p_attr_go_intent(go_intent=15)
+    attrs += p2p_attr_config_timeout()
+    attrs += p2p_attr_intended_interface_addr(addr0)
+    attrs += struct.pack("<BH3BBB", P2P_ATTR_CHANNEL_LIST, 5,
+                         0x58, 0x58, 0x04,
+                         81, 0)
+    attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+    attrs += p2p_attr_operating_channel()
+    attrs += p2p_attr_group_id(addr0, "DIRECT-foo")
+    msg['payload'] += ie_p2p(attrs)
+    mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload'])))
+    check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_NO_COMMON_CHANNELS)
+    rx_go_neg_conf(dev[0], P2P_SC_FAIL_NO_COMMON_CHANNELS, dialog_token)
diff --git a/hostap/tests/hwsim/test_p2p_persistent.py b/hostap/tests/hwsim/test_p2p_persistent.py
new file mode 100644
index 0000000..2752a88
--- /dev/null
+++ b/hostap/tests/hwsim/test_p2p_persistent.py
@@ -0,0 +1,654 @@
+# P2P persistent group test cases
+# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import re
+import time
+
+import hwsim_utils
+from test_p2p_autogo import connect_cli
+
+def go_neg_pin_authorized_persistent(i_dev, r_dev, i_intent=None, r_intent=None, i_method='enter', r_method='display', test_data=True):
+    r_dev.p2p_listen()
+    i_dev.p2p_listen()
+    pin = r_dev.wps_read_pin()
+    logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
+    r_dev.p2p_go_neg_auth(i_dev.p2p_dev_addr(), pin, r_method,
+                          go_intent=r_intent, persistent=True)
+    r_dev.p2p_listen()
+    i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), pin, i_method,
+                                  timeout=20, go_intent=i_intent,
+                                  persistent=True)
+    r_res = r_dev.p2p_go_neg_auth_result()
+    logger.debug("i_res: " + str(i_res))
+    logger.debug("r_res: " + str(r_res))
+    r_dev.dump_monitor()
+    i_dev.dump_monitor()
+    logger.info("Group formed")
+    if test_data:
+        hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
+    return [i_res, r_res]
+
+def terminate_group(go, cli):
+    logger.info("Terminate persistent group")
+    go.remove_group()
+    cli.wait_go_ending_session()
+
+def invite(inv, resp, extra=None, persistent_reconnect=True):
+    addr = resp.p2p_dev_addr()
+    if persistent_reconnect:
+        resp.global_request("SET persistent_reconnect 1")
+    else:
+        resp.global_request("SET persistent_reconnect 0")
+    resp.p2p_listen()
+    if not inv.discover_peer(addr, social=True):
+        raise Exception("Peer " + addr + " not found")
+    inv.dump_monitor()
+    peer = inv.get_peer(addr)
+    cmd = "P2P_INVITE persistent=" + peer['persistent'] + " peer=" + addr
+    if extra:
+        cmd = cmd + " " + extra;
+    inv.global_request(cmd)
+
+def check_result(go, cli):
+    ev = go.wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
+    if ev is None:
+        raise Exception("Timeout on group re-invocation (on GO)")
+    if "[PERSISTENT]" not in ev:
+        raise Exception("Re-invoked group not marked persistent")
+    go_res = go.group_form_result(ev)
+    if go_res['role'] != 'GO':
+        raise Exception("Persistent group GO did not become GO")
+    if not go_res['persistent']:
+        raise Exception("Persistent group not re-invoked as persistent (GO)")
+    ev = cli.wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
+    if ev is None:
+        raise Exception("Timeout on group re-invocation (on client)")
+    if "[PERSISTENT]" not in ev:
+        raise Exception("Re-invoked group not marked persistent")
+    cli_res = cli.group_form_result(ev)
+    if cli_res['role'] != 'client':
+        raise Exception("Persistent group client did not become client")
+    if not cli_res['persistent']:
+        raise Exception("Persistent group not re-invoked as persistent (cli)")
+    return [go_res, cli_res]
+
+def form(go, cli, test_data=True, reverse_init=False):
+    logger.info("Form a persistent group")
+    if reverse_init:
+        [i_res, r_res] = go_neg_pin_authorized_persistent(i_dev=cli, i_intent=0,
+                                                          r_dev=go, r_intent=15,
+                                                          test_data=test_data)
+    else:
+        [i_res, r_res] = go_neg_pin_authorized_persistent(i_dev=go, i_intent=15,
+                                                          r_dev=cli, r_intent=0,
+                                                          test_data=test_data)
+    if not i_res['persistent'] or not r_res['persistent']:
+        raise Exception("Formed group was not persistent")
+    terminate_group(go, cli)
+    if reverse_init:
+        return r_res
+    else:
+        return i_res
+
+def invite_from_cli(go, cli):
+    logger.info("Re-invoke persistent group from client")
+    invite(cli, go)
+    [go_res, cli_res] = check_result(go, cli)
+    hwsim_utils.test_connectivity_p2p(go, cli)
+    terminate_group(go, cli)
+    return [go_res, cli_res]
+
+def invite_from_go(go, cli):
+    logger.info("Re-invoke persistent group from GO")
+    invite(go, cli)
+    [go_res, cli_res] = check_result(go, cli)
+    hwsim_utils.test_connectivity_p2p(go, cli)
+    terminate_group(go, cli)
+    return [go_res, cli_res]
+
+def test_persistent_group(dev):
+    """P2P persistent group formation and re-invocation"""
+    form(dev[0], dev[1])
+    invite_from_cli(dev[0], dev[1])
+    invite_from_go(dev[0], dev[1])
+
+    logger.info("Remove group on the client and try to invite from GO")
+    id = None
+    for n in dev[0].list_networks(p2p=True):
+        if "[P2P-PERSISTENT]" in n['flags']:
+            id = n['id']
+            break
+    if id is None:
+        raise Exception("Could not find persistent group entry")
+    clients = dev[0].global_request("GET_NETWORK " + id + " p2p_client_list").rstrip()
+    if dev[1].p2p_dev_addr() not in clients:
+        raise Exception("Peer missing from client list")
+    if "FAIL" not in dev[1].request("SELECT_NETWORK " + str(id)):
+        raise Exception("SELECT_NETWORK succeeded unexpectedly")
+    if "FAIL" not in dev[1].request("SELECT_NETWORK 1234567"):
+        raise Exception("SELECT_NETWORK succeeded unexpectedly(2)")
+    if "FAIL" not in dev[1].request("ENABLE_NETWORK " + str(id)):
+        raise Exception("ENABLE_NETWORK succeeded unexpectedly")
+    if "FAIL" not in dev[1].request("ENABLE_NETWORK 1234567"):
+        raise Exception("ENABLE_NETWORK succeeded unexpectedly(2)")
+    if "FAIL" not in dev[1].request("DISABLE_NETWORK " + str(id)):
+        raise Exception("DISABLE_NETWORK succeeded unexpectedly")
+    if "FAIL" not in dev[1].request("DISABLE_NETWORK 1234567"):
+        raise Exception("DISABLE_NETWORK succeeded unexpectedly(2)")
+    if "FAIL" not in dev[1].request("REMOVE_NETWORK 1234567"):
+        raise Exception("REMOVE_NETWORK succeeded unexpectedly")
+    dev[1].global_request("REMOVE_NETWORK all")
+    if len(dev[1].list_networks(p2p=True)) > 0:
+        raise Exception("Unexpected network block remaining")
+    invite(dev[0], dev[1])
+    ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=10)
+    if ev is None:
+        raise Exception("No invitation result seen")
+    if "status=8" not in ev:
+        raise Exception("Unexpected invitation result: " + ev)
+    clients = dev[0].request("GET_NETWORK " + id + " p2p_client_list").rstrip()
+    if dev[1].p2p_dev_addr() in clients:
+        raise Exception("Peer was still in client list")
+
+def test_persistent_group2(dev):
+    """P2P persistent group formation with reverse roles"""
+    form(dev[0], dev[1], reverse_init=True)
+    invite_from_cli(dev[0], dev[1])
+    invite_from_go(dev[0], dev[1])
+
+def test_persistent_group3(dev):
+    """P2P persistent group formation and re-invocation with empty BSS table"""
+    form(dev[0], dev[1])
+    dev[1].request("BSS_FLUSH 0")
+    invite_from_cli(dev[0], dev[1])
+    dev[1].request("BSS_FLUSH 0")
+    invite_from_go(dev[0], dev[1])
+
+def test_persistent_group_per_sta_psk(dev):
+    """P2P persistent group formation and re-invocation using per-client PSK"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    addr2 = dev[2].p2p_dev_addr()
+    dev[0].global_request("P2P_SET per_sta_psk 1")
+    logger.info("Form a persistent group")
+    [i_res, r_res] = go_neg_pin_authorized_persistent(i_dev=dev[0], i_intent=15,
+                                                      r_dev=dev[1], r_intent=0)
+    if not i_res['persistent'] or not r_res['persistent']:
+        raise Exception("Formed group was not persistent")
+
+    logger.info("Join another client to the group")
+    pin = dev[2].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    social = int(i_res['freq']) in [ 2412, 2437, 2462 ]
+    c_res = dev[2].p2p_connect_group(addr0, pin, timeout=60, social=social,
+                                     freq=i_res['freq'])
+    if not c_res['persistent']:
+        raise Exception("Joining client did not recognize persistent group")
+    if r_res['psk'] == c_res['psk']:
+        raise Exception("Same PSK assigned for both clients")
+    hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+
+    logger.info("Remove persistent group and re-start it manually")
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+    dev[2].wait_go_ending_session()
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    dev[2].dump_monitor()
+
+    for i in range(0, 3):
+        networks = dev[i].list_networks(p2p=True)
+        if len(networks) != 1:
+            raise Exception("Unexpected number of networks")
+        if "[P2P-PERSISTENT]" not in networks[0]['flags']:
+            raise Exception("Not the persistent group data")
+        if i > 0:
+            # speed up testing by avoiding use of the old BSS entry since the
+            # GO may have changed channels
+            dev[i].request("BSS_FLUSH 0")
+            dev[i].scan(freq="2412", only_new=True)
+        if "OK" not in dev[i].global_request("P2P_GROUP_ADD persistent=" + networks[0]['id'] + " freq=2412"):
+            raise Exception("Could not re-start persistent group")
+        ev = dev[i].wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
+        if ev is None:
+            raise Exception("Timeout on group restart")
+        dev[i].group_form_result(ev)
+
+    logger.info("Leave persistent group and rejoin it")
+    dev[2].remove_group()
+    ev = dev[2].wait_global_event(["P2P-GROUP-REMOVED"], timeout=3)
+    if ev is None:
+        raise Exception("Group removal event timed out")
+    if not dev[2].discover_peer(addr0, social=True):
+        raise Exception("Peer " + peer + " not found")
+    dev[2].dump_monitor()
+    peer = dev[2].get_peer(addr0)
+    dev[2].global_request("P2P_GROUP_ADD persistent=" + peer['persistent'] + " freq=2412")
+    ev = dev[2].wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
+    if ev is None:
+        raise Exception("Timeout on group restart (on client)")
+    cli_res = dev[2].group_form_result(ev)
+    if not cli_res['persistent']:
+        raise Exception("Persistent group not restarted as persistent (cli)")
+    hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+
+    logger.info("Remove one of the clients from the group without removing persistent group information for the client")
+    dev[0].global_request("P2P_REMOVE_CLIENT iface=" + dev[2].p2p_interface_addr())
+    dev[2].wait_go_ending_session()
+
+    logger.info("Try to reconnect after having been removed from group (but persistent group info still present)")
+    if not dev[2].discover_peer(addr0, social=True):
+        raise Exception("Peer " + peer + " not found")
+    dev[2].dump_monitor()
+    peer = dev[2].get_peer(addr0)
+    dev[2].global_request("P2P_GROUP_ADD persistent=" + peer['persistent'] + " freq=2412")
+    ev = dev[2].wait_global_event(["P2P-GROUP-STARTED","WPA: 4-Way Handshake failed"], timeout=30)
+    if ev is None:
+        raise Exception("Timeout on group restart (on client)")
+    if "P2P-GROUP-STARTED" not in ev:
+        raise Exception("Connection failed")
+
+    logger.info("Remove one of the clients from the group")
+    dev[0].global_request("P2P_REMOVE_CLIENT " + addr2)
+    dev[2].wait_go_ending_session()
+
+    logger.info("Try to reconnect after having been removed from group")
+    if not dev[2].discover_peer(addr0, social=True):
+        raise Exception("Peer " + peer + " not found")
+    dev[2].dump_monitor()
+    peer = dev[2].get_peer(addr0)
+    dev[2].global_request("P2P_GROUP_ADD persistent=" + peer['persistent'] + " freq=2412")
+    ev = dev[2].wait_global_event(["P2P-GROUP-STARTED","WPA: 4-Way Handshake failed"], timeout=30)
+    if ev is None:
+        raise Exception("Timeout on group restart (on client)")
+    if "P2P-GROUP-STARTED" in ev:
+        raise Exception("Client managed to connect after being removed")
+
+    logger.info("Remove the remaining client from the group")
+    dev[0].global_request("P2P_REMOVE_CLIENT " + addr1)
+    dev[1].wait_go_ending_session()
+
+    logger.info("Terminate persistent group")
+    dev[0].remove_group()
+    dev[0].dump_monitor()
+
+    logger.info("Try to re-invoke persistent group from client")
+    dev[0].global_request("SET persistent_reconnect 1")
+    dev[0].p2p_listen()
+    if not dev[1].discover_peer(addr0, social=True):
+        raise Exception("Peer " + peer + " not found")
+    dev[1].dump_monitor()
+    peer = dev[1].get_peer(addr0)
+    dev[1].global_request("P2P_INVITE persistent=" + peer['persistent'] + " peer=" + addr0)
+    ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
+    dev[0].group_form_result(ev)
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED","WPA: 4-Way Handshake failed"], timeout=30)
+    if ev is None:
+        raise Exception("Timeout on group restart (on client)")
+    if "P2P-GROUP-STARTED" in ev:
+        raise Exception("Client managed to re-invoke after being removed")
+    dev[0].dump_monitor()
+
+    logger.info("Terminate persistent group")
+    dev[0].remove_group()
+    dev[0].dump_monitor()
+
+def test_persistent_group_invite_removed_client(dev):
+    """P2P persistent group client removal and re-invitation"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    dev[0].request("P2P_SET per_sta_psk 1")
+    logger.info("Form a persistent group")
+    [i_res, r_res] = go_neg_pin_authorized_persistent(i_dev=dev[0], i_intent=15,
+                                                      r_dev=dev[1], r_intent=0)
+    if not i_res['persistent'] or not r_res['persistent']:
+        raise Exception("Formed group was not persistent")
+
+    logger.info("Remove client from the group")
+    dev[0].global_request("P2P_REMOVE_CLIENT " + addr1)
+    dev[1].wait_go_ending_session()
+
+    logger.info("Re-invite the removed client to join the group")
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1, social=True):
+        raise Exception("Peer " + peer + " not found")
+    dev[0].global_request("P2P_INVITE group=" + dev[0].group_ifname + " peer=" + addr1)
+    ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on invitation")
+    if "sa=" + addr0 + " persistent=" not in ev:
+        raise Exception("Unexpected invitation event")
+    [event,addr,persistent] = ev.split(' ', 2)
+    dev[1].global_request("P2P_GROUP_ADD " + persistent)
+    ev = dev[1].wait_global_event(["P2P-PERSISTENT-PSK-FAIL"], timeout=30)
+    if ev is None:
+        raise Exception("Did not receive PSK failure report")
+    [tmp,id] = ev.split('=', 1)
+    ev = dev[1].wait_global_event(["P2P-GROUP-REMOVED"], timeout=10)
+    if ev is None:
+        raise Exception("Group removal event timed out")
+    if "reason=PSK_FAILURE" not in ev:
+        raise Exception("Unexpected group removal reason")
+    dev[1].global_request("REMOVE_NETWORK " + id)
+
+    logger.info("Re-invite after client removed persistent group info")
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1, social=True):
+        raise Exception("Peer " + peer + " not found")
+    dev[0].global_request("P2P_INVITE group=" + dev[0].group_ifname + " peer=" + addr1)
+    ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on invitation")
+    if " persistent=" in ev:
+        raise Exception("Unexpected invitation event")
+    pin = dev[1].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    c_res = dev[1].p2p_connect_group(addr0, pin, timeout=60, social=True,
+                                     freq=i_res['freq'])
+    if not c_res['persistent']:
+        raise Exception("Joining client did not recognize persistent group")
+    if r_res['psk'] == c_res['psk']:
+        raise Exception("Same PSK assigned on both times")
+    hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+
+    terminate_group(dev[0], dev[1])
+
+def test_persistent_group_channel(dev):
+    """P2P persistent group re-invocation with channel selection"""
+    form(dev[0], dev[1], test_data=False)
+
+    logger.info("Re-invoke persistent group from client with forced channel")
+    invite(dev[1], dev[0], "freq=2427")
+    [go_res, cli_res] = check_result(dev[0], dev[1])
+    if go_res['freq'] != "2427":
+        raise Exception("Persistent group client forced channel not followed")
+    terminate_group(dev[0], dev[1])
+
+    logger.info("Re-invoke persistent group from GO with forced channel")
+    invite(dev[0], dev[1], "freq=2432")
+    [go_res, cli_res] = check_result(dev[0], dev[1])
+    if go_res['freq'] != "2432":
+        raise Exception("Persistent group GO channel preference not followed")
+    terminate_group(dev[0], dev[1])
+
+    logger.info("Re-invoke persistent group from client with channel preference")
+    invite(dev[1], dev[0], "pref=2417")
+    [go_res, cli_res] = check_result(dev[0], dev[1])
+    if go_res['freq'] != "2417":
+        raise Exception("Persistent group client channel preference not followed")
+    terminate_group(dev[0], dev[1])
+
+def test_persistent_group_and_role_change(dev):
+    """P2P persistent group, auto GO in another role, and re-invocation"""
+    form(dev[0], dev[1])
+
+    logger.info("Start and stop autonomous GO on previous P2P client device")
+    dev[1].p2p_start_go()
+    dev[1].remove_group()
+    dev[1].dump_monitor()
+
+    logger.info("Re-invoke the persistent group")
+    invite_from_go(dev[0], dev[1])
+
+def test_persistent_go_client_list(dev):
+    """P2P GO and list of clients in persistent group"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    addr2 = dev[2].p2p_dev_addr()
+
+    res = dev[0].p2p_start_go(persistent=True)
+    id = None
+    for n in dev[0].list_networks(p2p=True):
+        if "[P2P-PERSISTENT]" in n['flags']:
+            id = n['id']
+            break
+    if id is None:
+        raise Exception("Could not find persistent group entry")
+
+    connect_cli(dev[0], dev[1], social=True, freq=res['freq'])
+    clients = dev[0].global_request("GET_NETWORK " + id + " p2p_client_list").rstrip()
+    if clients != addr1:
+        raise Exception("Unexpected p2p_client_list entry(2): " + clients)
+    connect_cli(dev[0], dev[2], social=True, freq=res['freq'])
+    clients = dev[0].global_request("GET_NETWORK " + id + " p2p_client_list").rstrip()
+    if clients != addr2 + " " + addr1:
+        raise Exception("Unexpected p2p_client_list entry(3): " + clients)
+
+    peer = dev[1].get_peer(res['go_dev_addr'])
+    dev[1].remove_group()
+    dev[1].global_request("P2P_GROUP_ADD persistent=" + peer['persistent'])
+    ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
+    if ev is None:
+        raise Exception("Timeout on group restart (on client)")
+    dev[1].group_form_result(ev)
+    clients = dev[0].global_request("GET_NETWORK " + id + " p2p_client_list").rstrip()
+    if clients != addr1 + " " + addr2:
+        raise Exception("Unexpected p2p_client_list entry(4): " + clients)
+
+    dev[2].remove_group()
+    dev[1].remove_group()
+    dev[0].remove_group()
+
+    clients = dev[0].global_request("GET_NETWORK " + id + " p2p_client_list").rstrip()
+    if clients != addr1 + " " + addr2:
+        raise Exception("Unexpected p2p_client_list entry(5): " + clients)
+
+    dev[1].p2p_listen()
+    dev[2].p2p_listen()
+    dev[0].request("P2P_FLUSH")
+    dev[0].discover_peer(addr1, social=True)
+    peer = dev[0].get_peer(addr1)
+    if 'persistent' not in peer or peer['persistent'] != id:
+        raise Exception("Persistent group client not recognized(1)")
+
+    dev[0].discover_peer(addr2, social=True)
+    peer = dev[0].get_peer(addr2)
+    if 'persistent' not in peer or peer['persistent'] != id:
+        raise Exception("Persistent group client not recognized(2)")
+
+def test_persistent_group_in_grpform(dev):
+    """P2P persistent group parameters re-used in group formation"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    form(dev[0], dev[1])
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr1, social=True):
+        raise Exception("Could not discover peer")
+    peer = dev[0].get_peer(addr1)
+    if "persistent" not in peer:
+        raise Exception("Could not map peer to a persistent group")
+
+    pin = dev[1].wps_read_pin()
+    dev[1].p2p_go_neg_auth(addr0, pin, "display", go_intent=0)
+    i_res = dev[0].p2p_go_neg_init(addr1, pin, "enter", timeout=20,
+                                   go_intent=15,
+                                   persistent_id=peer['persistent'])
+    r_res = dev[1].p2p_go_neg_auth_result()
+    logger.debug("i_res: " + str(i_res))
+    logger.debug("r_res: " + str(r_res))
+
+def test_persistent_group_without_persistent_reconnect(dev):
+    """P2P persistent group re-invocation without persistent reconnect"""
+    form(dev[0], dev[1])
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+
+    logger.info("Re-invoke persistent group from client")
+    invite(dev[1], dev[0], persistent_reconnect=False)
+
+    ev = dev[0].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=15)
+    if ev is None:
+        raise Exception("No invitation request reported");
+    if "persistent=" not in ev:
+        raise Exception("Invalid invitation type reported: " + ev)
+
+    ev2 = dev[1].wait_global_event(["P2P-INVITATION-RESULT"], timeout=15)
+    if ev2 is None:
+        raise Exception("No invitation response reported");
+    if "status=1" not in ev2:
+        raise Exception("Unexpected status: " + ev2)
+    dev[1].p2p_listen()
+
+    exp = r'<.>(P2P-INVITATION-RECEIVED) sa=([0-9a-f:]*) persistent=([0-9]*) freq=([0-9]*)'
+    s = re.split(exp, ev)
+    if len(s) < 5:
+        raise Exception("Could not parse invitation event")
+    sa = s[2]
+    id = s[3]
+    freq = s[4]
+    logger.info("Invalid P2P_INVITE test coverage")
+    if "FAIL" not in dev[0].global_request("P2P_INVITE persistent=" + id + " peer=" + sa + " freq=0"):
+        raise Exception("Invalid P2P_INVITE accepted")
+    if "FAIL" not in dev[0].global_request("P2P_INVITE persistent=" + id + " peer=" + sa + " pref=0"):
+        raise Exception("Invalid P2P_INVITE accepted")
+    logger.info("Re-initiate invitation based on upper layer acceptance")
+    if "OK" not in dev[0].global_request("P2P_INVITE persistent=" + id + " peer=" + sa + " freq=" + freq):
+        raise Exception("Invitation command failed")
+    [go_res, cli_res] = check_result(dev[0], dev[1])
+    if go_res['freq'] != freq:
+        raise Exception("Unexpected channel on GO: {} MHz, expected {} MHz".format(go_res['freq'], freq))
+    if cli_res['freq'] != freq:
+        raise Exception("Unexpected channel on CLI: {} MHz, expected {} MHz".format(cli_res['freq'], freq))
+    terminate_group(dev[0], dev[1])
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+
+    logger.info("Re-invoke persistent group from GO")
+    invite(dev[0], dev[1], persistent_reconnect=False)
+
+    ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=15)
+    if ev is None:
+        raise Exception("No invitation request reported");
+    if "persistent=" not in ev:
+        raise Exception("Invalid invitation type reported: " + ev)
+
+    ev2 = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=15)
+    if ev2 is None:
+        raise Exception("No invitation response reported");
+    if "status=1" not in ev2:
+        raise Exception("Unexpected status: " + ev2)
+    dev[0].p2p_listen()
+
+    exp = r'<.>(P2P-INVITATION-RECEIVED) sa=([0-9a-f:]*) persistent=([0-9]*)'
+    s = re.split(exp, ev)
+    if len(s) < 4:
+        raise Exception("Could not parse invitation event")
+    sa = s[2]
+    id = s[3]
+    logger.info("Re-initiate invitation based on upper layer acceptance")
+    if "OK" not in dev[1].global_request("P2P_INVITE persistent=" + id + " peer=" + sa + " freq=" + freq):
+        raise Exception("Invitation command failed")
+    [go_res, cli_res] = check_result(dev[0], dev[1])
+    terminate_group(dev[0], dev[1])
+
+def test_persistent_group_already_running(dev):
+    """P2P persistent group formation and invitation while GO already running"""
+    form(dev[0], dev[1])
+    peer = dev[1].get_peer(dev[0].p2p_dev_addr())
+    listen_freq = peer['listen_freq']
+    dev[0].dump_monitor()
+    dev[1].dump_monitor()
+    networks = dev[0].list_networks(p2p=True)
+    if len(networks) != 1:
+        raise Exception("Unexpected number of networks")
+    if "[P2P-PERSISTENT]" not in networks[0]['flags']:
+        raise Exception("Not the persistent group data")
+    if "OK" not in dev[0].global_request("P2P_GROUP_ADD persistent=" + networks[0]['id'] + " freq=" + listen_freq):
+        raise Exception("Could not state GO")
+    invite_from_cli(dev[0], dev[1])
+
+def test_persistent_group_add_cli_chan(dev):
+    """P2P persistent group formation and re-invocation with p2p_add_cli_chan=1"""
+    dev[0].request("SET p2p_add_cli_chan 1")
+    dev[1].request("SET p2p_add_cli_chan 1")
+    form(dev[0], dev[1])
+    dev[1].request("BSS_FLUSH 0")
+    dev[1].scan(freq="2412", only_new=True)
+    dev[1].scan(freq="2437", only_new=True)
+    dev[1].scan(freq="2462", only_new=True)
+    dev[1].request("BSS_FLUSH 0")
+    invite_from_cli(dev[0], dev[1])
+    invite_from_go(dev[0], dev[1])
+
+def test_persistent_invalid_group_add(dev):
+    """Invalid P2P_GROUP_ADD command"""
+    id = dev[0].add_network()
+    if "FAIL" not in dev[0].global_request("P2P_GROUP_ADD persistent=12345"):
+        raise Exception("Invalid P2P_GROUP_ADD accepted")
+    if "FAIL" not in dev[0].global_request("P2P_GROUP_ADD persistent=%d" % id):
+        raise Exception("Invalid P2P_GROUP_ADD accepted")
+    if "FAIL" not in dev[0].global_request("P2P_GROUP_ADD foo"):
+        raise Exception("Invalid P2P_GROUP_ADD accepted")
+
+def test_persistent_group_missed_inv_resp(dev):
+    """P2P persistent group re-invocation with invitation response getting lost"""
+    form(dev[0], dev[1])
+    addr = dev[1].p2p_dev_addr()
+    dev[1].global_request("SET persistent_reconnect 1")
+    dev[1].p2p_listen()
+    if not dev[0].discover_peer(addr, social=True):
+        raise Exception("Peer " + addr + " not found")
+    dev[0].dump_monitor()
+    peer = dev[0].get_peer(addr)
+    # Drop the first Invitation Response frame
+    if "FAIL" in dev[0].request("SET ext_mgmt_frame_handling 1"):
+        raise Exception("Failed to enable external management frame handling")
+    cmd = "P2P_INVITE persistent=" + peer['persistent'] + " peer=" + addr
+    dev[0].global_request(cmd)
+    rx_msg = dev[0].mgmt_rx()
+    if rx_msg is None:
+        raise Exception("MGMT-RX timeout (no Invitation Response)")
+    time.sleep(2)
+    # Allow following Invitation Response frame to go through
+    if "FAIL" in dev[0].request("SET ext_mgmt_frame_handling 0"):
+        raise Exception("Failed to disable external management frame handling")
+    time.sleep(1)
+    # Force the P2P Client side to be on its Listen channel for retry
+    dev[1].p2p_listen()
+    ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=15)
+    if ev is None:
+        raise Exception("Invitation result timed out")
+    # Allow P2P Client side to continue connection-to-GO attempts
+    dev[1].p2p_stop_find()
+
+    # Verify that group re-invocation goes through
+    ev = dev[1].wait_global_event([ "P2P-GROUP-STARTED",
+                                    "P2P-GROUP-FORMATION-FAILURE" ],
+                                  timeout=20)
+    if ev is None:
+        raise Exception("Group start event timed out")
+    if "P2P-GROUP-STARTED" not in ev:
+        raise Exception("Group re-invocation failed")
+    dev[0].group_form_result(ev)
+
+    ev = dev[0].wait_global_event([ "P2P-GROUP-STARTED" ], timeout=5)
+    if ev is None:
+        raise Exception("Group start event timed out on GO")
+    dev[0].group_form_result(ev)
+
+    terminate_group(dev[0], dev[1])
+
+def test_persistent_group_profile_add(dev):
+    """Create a P2P persistent group with ADD_NETWORK"""
+    passphrase="passphrase here"
+    id = dev[0].add_network()
+    dev[0].set_network_quoted(id, "ssid", "DIRECT-ab")
+    dev[0].set_network_quoted(id, "psk", passphrase)
+    dev[0].set_network(id, "mode", "3")
+    dev[0].set_network(id, "disabled", "2")
+    dev[0].p2p_start_go(persistent=id, freq=2412)
+
+    pin = dev[1].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    res = dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60,
+                                   social=True, freq=2412)
+    if res['result'] != 'success':
+        raise Exception("Joining the group did not succeed")
+
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
diff --git a/hostap/tests/hwsim/test_p2p_service.py b/hostap/tests/hwsim/test_p2p_service.py
new file mode 100644
index 0000000..ea7a8f4
--- /dev/null
+++ b/hostap/tests/hwsim/test_p2p_service.py
@@ -0,0 +1,512 @@
+# P2P service discovery test cases
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+import uuid
+
+import hwsim_utils
+
+def add_bonjour_services(dev):
+    dev.global_request("P2P_SERVICE_ADD bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027")
+    dev.global_request("P2P_SERVICE_ADD bonjour 076578616d706c650b5f6166706f766572746370c00c001001 00")
+    dev.global_request("P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027")
+    dev.global_request("P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001 09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074")
+
+def add_upnp_services(dev):
+    dev.global_request("P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice")
+    dev.global_request("P2P_SERVICE_ADD upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::upnp:rootdevice")
+    dev.global_request("P2P_SERVICE_ADD upnp 10 uuid:1122de4e-8574-59ab-9322-333456789044::urn:schemas-upnp-org:service:ContentDirectory:2")
+    dev.global_request("P2P_SERVICE_ADD upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::urn:schemas-upnp-org:service:ContentDirectory:2")
+    dev.global_request("P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp-org:device:InternetGatewayDevice:1")
+
+def add_extra_services(dev):
+    for i in range(0, 100):
+        dev.global_request("P2P_SERVICE_ADD upnp 10 uuid:" + str(uuid.uuid4()) + "::upnp:rootdevice")
+
+def run_sd(dev, dst, query, exp_query=None, fragment=False, query2=None):
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    add_bonjour_services(dev[0])
+    add_upnp_services(dev[0])
+    if fragment:
+        add_extra_services(dev[0])
+    dev[0].p2p_listen()
+
+    dev[1].global_request("P2P_FLUSH")
+    dev[1].global_request("P2P_SERV_DISC_REQ " + dst + " " + query)
+    if query2:
+        dev[1].global_request("P2P_SERV_DISC_REQ " + dst + " " + query2)
+    if not dev[1].discover_peer(addr0, social=True, force_find=True):
+        raise Exception("Peer " + addr0 + " not found")
+
+    ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=10)
+    if ev is None:
+        raise Exception("Service discovery timed out")
+    if addr1 not in ev:
+        raise Exception("Unexpected service discovery request source")
+    if exp_query is None:
+        exp_query = query
+    if exp_query not in ev and (query2 is None or query2 not in ev):
+        raise Exception("Unexpected service discovery request contents")
+
+    if query2:
+        ev_list = []
+        for i in range(0, 4):
+            ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=10)
+            if ev is None:
+                raise Exception("Service discovery timed out")
+            if addr0 in ev:
+                ev_list.append(ev)
+                if len(ev_list) == 2:
+                    break
+        return ev_list
+
+    for i in range(0, 2):
+        ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=10)
+        if ev is None:
+            raise Exception("Service discovery timed out")
+        if addr0 in ev:
+            break
+
+    dev[0].p2p_stop_find()
+    dev[1].p2p_stop_find()
+
+    if "OK" not in dev[0].global_request("P2P_SERVICE_DEL upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice"):
+        raise Exception("Failed to delete a UPnP service")
+    if "FAIL" not in dev[0].global_request("P2P_SERVICE_DEL upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice"):
+        raise Exception("Unexpected deletion success for UPnP service")
+    if "OK" not in dev[0].global_request("P2P_SERVICE_DEL bonjour 0b5f6166706f766572746370c00c000c01"):
+        raise Exception("Failed to delete a Bonjour service")
+    if "FAIL" not in dev[0].global_request("P2P_SERVICE_DEL bonjour 0b5f6166706f766572746370c00c000c01"):
+        raise Exception("Unexpected deletion success for Bonjour service")
+
+    return ev
+
+def test_p2p_service_discovery(dev):
+    """P2P service discovery"""
+    addr0 = dev[0].p2p_dev_addr()
+    for dst in [ "00:00:00:00:00:00", addr0 ]:
+        ev = run_sd(dev, dst, "02000001")
+        if "0b5f6166706f766572746370c00c000c01" not in ev:
+            raise Exception("Unexpected service discovery response contents (Bonjour)")
+        if "496e7465726e6574" not in ev:
+            raise Exception("Unexpected service discovery response contents (UPnP)")
+
+    for req in [ "foo 02000001",
+                 addr0,
+                 addr0 + " upnp qq urn:schemas-upnp-org:device:InternetGatewayDevice:1",
+                 addr0 + " upnp 10",
+                 addr0 + " 123",
+                 addr0 + " qq" ]:
+        if "FAIL" not in dev[1].global_request("P2P_SERV_DISC_REQ " + req):
+            raise Exception("Invalid P2P_SERV_DISC_REQ accepted: " + req)
+
+def test_p2p_service_discovery2(dev):
+    """P2P service discovery with one peer having no services"""
+    dev[2].p2p_listen()
+    for dst in [ "00:00:00:00:00:00", dev[0].p2p_dev_addr() ]:
+        ev = run_sd(dev, dst, "02000001")
+        if "0b5f6166706f766572746370c00c000c01" not in ev:
+            raise Exception("Unexpected service discovery response contents (Bonjour)")
+        if "496e7465726e6574" not in ev:
+            raise Exception("Unexpected service discovery response contents (UPnP)")
+
+def test_p2p_service_discovery3(dev):
+    """P2P service discovery for Bonjour with one peer having no services"""
+    dev[2].p2p_listen()
+    for dst in [ "00:00:00:00:00:00", dev[0].p2p_dev_addr() ]:
+        ev = run_sd(dev, dst, "02000101")
+        if "0b5f6166706f766572746370c00c000c01" not in ev:
+            raise Exception("Unexpected service discovery response contents (Bonjour)")
+
+def test_p2p_service_discovery4(dev):
+    """P2P service discovery for UPnP with one peer having no services"""
+    dev[2].p2p_listen()
+    for dst in [ "00:00:00:00:00:00", dev[0].p2p_dev_addr() ]:
+        ev = run_sd(dev, dst, "02000201")
+        if "496e7465726e6574" not in ev:
+            raise Exception("Unexpected service discovery response contents (UPnP)")
+
+def test_p2p_service_discovery_multiple_queries(dev):
+    """P2P service discovery with multiple queries"""
+    for dst in [ "00:00:00:00:00:00", dev[0].p2p_dev_addr() ]:
+        ev = run_sd(dev, dst, "02000201", query2="02000101")
+        if "0b5f6166706f766572746370c00c000c01" not in ev[0] + ev[1]:
+            raise Exception("Unexpected service discovery response contents (Bonjour)")
+        if "496e7465726e6574" not in ev[0] + ev[1]:
+            raise Exception("Unexpected service discovery response contents (UPnP)")
+
+def test_p2p_service_discovery_multiple_queries2(dev):
+    """P2P service discovery with multiple queries with one peer having no services"""
+    dev[2].p2p_listen()
+    for dst in [ "00:00:00:00:00:00", dev[0].p2p_dev_addr() ]:
+        ev = run_sd(dev, dst, "02000201", query2="02000101")
+        if "0b5f6166706f766572746370c00c000c01" not in ev[0] + ev[1]:
+            raise Exception("Unexpected service discovery response contents (Bonjour)")
+        if "496e7465726e6574" not in ev[0] + ev[1]:
+            raise Exception("Unexpected service discovery response contents (UPnP)")
+
+def test_p2p_service_discovery_fragmentation(dev):
+    """P2P service discovery with fragmentation"""
+    for dst in [ "00:00:00:00:00:00", dev[0].p2p_dev_addr() ]:
+        ev = run_sd(dev, dst, "02000001", fragment=True)
+        if not "long response" in ev:
+            if "0b5f6166706f766572746370c00c000c01" not in ev:
+                raise Exception("Unexpected service discovery response contents (Bonjour)")
+            if "496e7465726e6574" not in ev:
+                raise Exception("Unexpected service discovery response contents (UPnP)")
+
+def test_p2p_service_discovery_bonjour(dev):
+    """P2P service discovery (Bonjour)"""
+    ev = run_sd(dev, "00:00:00:00:00:00", "02000101")
+    if "0b5f6166706f766572746370c00c000c01" not in ev:
+        raise Exception("Unexpected service discovery response contents (Bonjour)")
+    if "045f697070c00c000c01" not in ev:
+        raise Exception("Unexpected service discovery response contents (Bonjour)")
+    if "496e7465726e6574" in ev:
+        raise Exception("Unexpected service discovery response contents (UPnP not expected)")
+
+def test_p2p_service_discovery_bonjour2(dev):
+    """P2P service discovery (Bonjour AFS)"""
+    ev = run_sd(dev, "00:00:00:00:00:00", "130001010b5f6166706f766572746370c00c000c01")
+    if "0b5f6166706f766572746370c00c000c01" not in ev:
+        raise Exception("Unexpected service discovery response contents (Bonjour)")
+    if "045f697070c00c000c01" in ev:
+        raise Exception("Unexpected service discovery response contents (Bonjour mismatching)")
+    if "496e7465726e6574" in ev:
+        raise Exception("Unexpected service discovery response contents (UPnP not expected)")
+
+def test_p2p_service_discovery_bonjour3(dev):
+    """P2P service discovery (Bonjour AFS - no match)"""
+    ev = run_sd(dev, "00:00:00:00:00:00", "130001010b5f6166706f766572746370c00c000c02")
+    if "0300010102" not in ev:
+        raise Exception("Requested-info-not-available was not indicated")
+    if "0b5f6166706f766572746370c00c000c01" in ev:
+        raise Exception("Unexpected service discovery response contents (Bonjour)")
+    if "045f697070c00c000c01" in ev:
+        raise Exception("Unexpected service discovery response contents (Bonjour mismatching)")
+    if "496e7465726e6574" in ev:
+        raise Exception("Unexpected service discovery response contents (UPnP not expected)")
+
+def test_p2p_service_discovery_upnp(dev):
+    """P2P service discovery (UPnP)"""
+    ev = run_sd(dev, "00:00:00:00:00:00", "02000201")
+    if "0b5f6166706f766572746370c00c000c01" in ev:
+        raise Exception("Unexpected service discovery response contents (Bonjour not expected)")
+    if "496e7465726e6574" not in ev:
+        raise Exception("Unexpected service discovery response contents (UPnP)")
+
+def test_p2p_service_discovery_upnp2(dev):
+    """P2P service discovery (UPnP using request helper)"""
+    ev = run_sd(dev, "00:00:00:00:00:00", "upnp 10 ssdp:all", "0b00020110737364703a616c6c")
+    if "0b5f6166706f766572746370c00c000c01" in ev:
+        raise Exception("Unexpected service discovery response contents (Bonjour not expected)")
+    if "496e7465726e6574" not in ev:
+        raise Exception("Unexpected service discovery response contents (UPnP)")
+
+def test_p2p_service_discovery_upnp3(dev):
+    """P2P service discovery (UPnP using request helper - no match)"""
+    ev = run_sd(dev, "00:00:00:00:00:00", "upnp 10 ssdp:foo", "0b00020110737364703a666f6f")
+    if "0300020102" not in ev:
+        raise Exception("Requested-info-not-available was not indicated")
+    if "0b5f6166706f766572746370c00c000c01" in ev:
+        raise Exception("Unexpected service discovery response contents (Bonjour not expected)")
+    if "496e7465726e6574" in ev:
+        raise Exception("Unexpected service discovery response contents (UPnP)")
+
+def test_p2p_service_discovery_ws(dev):
+    """P2P service discovery (WS-Discovery)"""
+    ev = run_sd(dev, "00:00:00:00:00:00", "02000301")
+    if "0b5f6166706f766572746370c00c000c01" in ev:
+        raise Exception("Unexpected service discovery response contents (Bonjour not expected)")
+    if "496e7465726e6574" in ev:
+        raise Exception("Unexpected service discovery response contents (UPnP not expected)")
+    if "0300030101" not in ev:
+        raise Exception("Unexpected service discovery response contents (WS)")
+
+def test_p2p_service_discovery_wfd(dev):
+    """P2P service discovery (Wi-Fi Display)"""
+    dev[0].global_request("SET wifi_display 1")
+    ev = run_sd(dev, "00:00:00:00:00:00", "02000401")
+    if " 030004" in ev:
+        raise Exception("Unexpected response to invalid WFD SD query")
+    dev[0].global_request("SET wifi_display 0")
+    ev = run_sd(dev, "00:00:00:00:00:00", "0300040100")
+    if "0300040101" not in ev:
+        raise Exception("Unexpected response to WFD SD query (protocol was disabled)")
+
+def test_p2p_service_discovery_req_cancel(dev):
+    """Cancel a P2P service discovery request"""
+    if "FAIL" not in dev[0].global_request("P2P_SERV_DISC_CANCEL_REQ ab"):
+        raise Exception("Unexpected SD cancel success")
+    if "FAIL" not in dev[0].global_request("P2P_SERV_DISC_CANCEL_REQ qq"):
+        raise Exception("Unexpected SD cancel success")
+    query = dev[0].global_request("P2P_SERV_DISC_REQ " + dev[1].p2p_dev_addr() + " 02000001")
+    if "OK" not in dev[0].global_request("P2P_SERV_DISC_CANCEL_REQ " + query):
+        raise Exception("Unexpected SD cancel failure")
+    query1 = dev[0].global_request("P2P_SERV_DISC_REQ " + dev[1].p2p_dev_addr() + " 02000001")
+    query2 = dev[0].global_request("P2P_SERV_DISC_REQ " + dev[1].p2p_dev_addr() + " 02000002")
+    query3 = dev[0].global_request("P2P_SERV_DISC_REQ " + dev[1].p2p_dev_addr() + " 02000003")
+    if "OK" not in dev[0].global_request("P2P_SERV_DISC_CANCEL_REQ " + query2):
+        raise Exception("Unexpected SD cancel failure")
+    if "OK" not in dev[0].global_request("P2P_SERV_DISC_CANCEL_REQ " + query1):
+        raise Exception("Unexpected SD cancel failure")
+    if "OK" not in dev[0].global_request("P2P_SERV_DISC_CANCEL_REQ " + query3):
+        raise Exception("Unexpected SD cancel failure")
+
+    query = dev[0].global_request("P2P_SERV_DISC_REQ 00:00:00:00:00:00 02000001")
+    if "OK" not in dev[0].global_request("P2P_SERV_DISC_CANCEL_REQ " + query):
+        raise Exception("Unexpected SD(broadcast) cancel failure")
+
+def test_p2p_service_discovery_go(dev):
+    """P2P service discovery from GO"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+
+    add_bonjour_services(dev[0])
+    add_upnp_services(dev[0])
+
+    dev[0].p2p_start_go(freq=2412)
+
+    dev[1].global_request("P2P_FLUSH")
+    dev[1].global_request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+    if not dev[1].discover_peer(addr0, social=True, force_find=True):
+        raise Exception("Peer " + addr0 + " not found")
+
+    ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=10)
+    if ev is None:
+        raise Exception("Service discovery timed out")
+    if addr1 not in ev:
+        raise Exception("Unexpected service discovery request source")
+
+    ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=10)
+    if ev is None:
+        raise Exception("Service discovery timed out")
+    if addr0 not in ev:
+        raise Exception("Unexpected service discovery response source")
+    if "0b5f6166706f766572746370c00c000c01" not in ev:
+        raise Exception("Unexpected service discovery response contents (Bonjour)")
+    if "496e7465726e6574" not in ev:
+        raise Exception("Unexpected service discovery response contents (UPnP)")
+    dev[1].p2p_stop_find()
+
+    dev[0].global_request("P2P_SERVICE_FLUSH")
+
+    dev[1].global_request("P2P_FLUSH")
+    dev[1].global_request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+    if not dev[1].discover_peer(addr0, social=True, force_find=True):
+        raise Exception("Peer " + addr0 + " not found")
+    ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=10)
+    if ev is None:
+        raise Exception("Service discovery timed out")
+    if addr1 not in ev:
+        raise Exception("Unexpected service discovery request source")
+
+    ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=10)
+    if ev is None:
+        raise Exception("Service discovery timed out")
+    if addr0 not in ev:
+        raise Exception("Unexpected service discovery response source")
+    if "0300000101" not in ev:
+        raise Exception("Unexpected service discovery response contents (Bonjour)")
+    dev[1].p2p_stop_find()
+
+def _test_p2p_service_discovery_external(dev):
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+
+    if "FAIL" not in dev[0].global_request("P2P_SERV_DISC_EXTERNAL 2"):
+        raise Exception("Invalid P2P_SERV_DISC_EXTERNAL accepted")
+    if "OK" not in dev[0].global_request("P2P_SERV_DISC_EXTERNAL 1"):
+        raise Exception("P2P_SERV_DISC_EXTERNAL failed")
+    dev[0].p2p_listen()
+    dev[1].global_request("P2P_FLUSH")
+    dev[1].global_request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+    if not dev[1].discover_peer(addr0, social=True, force_find=True):
+        raise Exception("Peer " + addr0 + " not found")
+
+    ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=10)
+    if ev is None:
+        raise Exception("Service discovery timed out")
+    if addr1 not in ev:
+        raise Exception("Unexpected service discovery request source")
+    arg = ev.split(' ')
+    resp = "0300000101"
+    if "OK" not in dev[0].global_request("P2P_SERV_DISC_RESP %s %s %s %s" % (arg[2], arg[3], arg[4], resp)):
+        raise Exception("P2P_SERV_DISC_RESP failed")
+
+    ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=15)
+    if ev is None:
+        raise Exception("Service discovery timed out")
+    if addr0 not in ev:
+        raise Exception("Unexpected address in SD Response: " + ev)
+    if ev.split(' ')[4] != resp:
+        raise Exception("Unexpected response data SD Response: " + ev)
+    ver = ev.split(' ')[3]
+
+    dev[0].global_request("P2P_SERVICE_UPDATE")
+
+    dev[1].global_request("P2P_FLUSH")
+    dev[1].global_request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+    if not dev[1].discover_peer(addr0, social=True, force_find=True):
+        raise Exception("Peer " + addr0 + " not found")
+
+    ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=10)
+    if ev is None:
+        raise Exception("Service discovery timed out")
+    if addr1 not in ev:
+        raise Exception("Unexpected service discovery request source")
+    arg = ev.split(' ')
+    resp = "0300000101"
+    if "OK" not in dev[0].global_request("P2P_SERV_DISC_RESP %s %s %s %s" % (arg[2], arg[3], arg[4], resp)):
+        raise Exception("P2P_SERV_DISC_RESP failed")
+
+    ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=15)
+    if ev is None:
+        raise Exception("Service discovery timed out")
+    if addr0 not in ev:
+        raise Exception("Unexpected address in SD Response: " + ev)
+    if ev.split(' ')[4] != resp:
+        raise Exception("Unexpected response data SD Response: " + ev)
+    ver2 = ev.split(' ')[3]
+    if ver == ver2:
+        raise Exception("Service list version did not change")
+
+    for cmd in [ "%s%s%s%s" % (arg[2], arg[3], arg[4], resp),
+                 "%s %s %s %s" % ("0", arg[3], arg[4], resp),
+                 "%s %s %s %s" % (arg[2], "foo", arg[4], resp),
+                 "%s %s%s%s" % (arg[2], arg[3], arg[4], resp),
+                 "%s %s %s%s" % (arg[2], arg[3], arg[4], resp),
+                 "%s %s %s %s" % (arg[2], arg[3], arg[4], "12345"),
+                 "%s %s %s %s" % (arg[2], arg[3], arg[4], "qq") ]:
+        if "FAIL" not in dev[0].global_request("P2P_SERV_DISC_RESP " + cmd):
+            raise Exception("Invalid P2P_SERV_DISC_RESP accepted: " + cmd)
+
+def test_p2p_service_discovery_external(dev):
+    """P2P service discovery using external response"""
+    try:
+        _test_p2p_service_discovery_external(dev)
+    finally:
+        dev[0].global_request("P2P_SERV_DISC_EXTERNAL 0")
+
+def test_p2p_service_discovery_invalid_commands(dev):
+    """P2P service discovery invalid commands"""
+    for cmd in [ "bonjour",
+                 "bonjour 12",
+                 "bonjour 123 12",
+                 "bonjour qq 12",
+                 "bonjour 12 123",
+                 "bonjour 12 qq",
+                 "upnp 10",
+                 "upnp qq uuid:",
+                 "foo bar" ]:
+        if "FAIL" not in dev[0].global_request("P2P_SERVICE_ADD " + cmd):
+            raise Exception("Invalid P2P_SERVICE_ADD accepted: " + cmd)
+
+    for cmd in [ "bonjour",
+                 "bonjour 123",
+                 "bonjour qq",
+                 "upnp 10",
+                 "upnp  ",
+                 "upnp qq uuid:",
+                 "foo bar" ]:
+        if "FAIL" not in dev[0].global_request("P2P_SERVICE_DEL " + cmd):
+            raise Exception("Invalid P2P_SERVICE_DEL accepted: " + cmd)
+
+def test_p2p_service_discovery_cancel_during_query(dev):
+    """P2P service discovery and cancel during query"""
+    for i in range(2):
+        add_bonjour_services(dev[i])
+        add_upnp_services(dev[i])
+        add_extra_services(dev[i])
+        dev[i].p2p_listen()
+
+    dev[2].request("P2P_FLUSH")
+    id1 = dev[2].request("P2P_SERV_DISC_REQ 00:00:00:00:00:00 02000201")
+    id2 = dev[2].request("P2P_SERV_DISC_REQ 00:00:00:00:00:00 02000101")
+    dev[2].p2p_find(social=True)
+    ev = dev[2].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+    if ev is None:
+        raise Exception("Could not discover peer")
+    if "OK" not in dev[2].request("P2P_SERV_DISC_CANCEL_REQ " + id1):
+        raise Exception("Failed to cancel req1")
+    if "OK" not in dev[2].request("P2P_SERV_DISC_CANCEL_REQ " + id2):
+        raise Exception("Failed to cancel req2")
+    ev = dev[2].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=3)
+    # we may or may not get a response depending on timing, so ignore the result
+    dev[2].p2p_stop_find()
+    dev[1].p2p_stop_find()
+    dev[0].p2p_stop_find()
+
+def get_p2p_state(dev):
+    res = dev.global_request("STATUS")
+    p2p_state = None
+    for line in res.splitlines():
+        if line.startswith("p2p_state="):
+            p2p_state = line.split('=')[1]
+            break
+    if p2p_state is None:
+        raise Exception("Could not get p2p_state")
+    return p2p_state
+
+def test_p2p_service_discovery_peer_not_listening(dev):
+    """P2P service discovery and peer not listening"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    add_bonjour_services(dev[0])
+    add_upnp_services(dev[0])
+    dev[0].p2p_listen()
+    dev[1].global_request("P2P_FIND 1 type=social")
+    ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=4)
+    if ev is None:
+        raise Exception("Peer not found")
+    dev[0].p2p_stop_find()
+    ev = dev[1].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=1)
+    ev = dev[1].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=1)
+    time.sleep(0.03)
+    dev[1].request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+    ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=1)
+    if ev is not None:
+        raise Exception("Service discovery request unexpectedly received")
+    ev = dev[1].wait_global_event(["P2P-FIND-STOPPED", "P2P-SERV-DISC-RESP"],
+                                  timeout=10)
+    if ev is None:
+        raise Exception("P2P-FIND-STOPPED event timed out")
+    if "P2P-SERV-DISC-RESP" in ev:
+        raise Exception("Unexpected SD response")
+    p2p_state = get_p2p_state(dev[1])
+    if p2p_state != "IDLE":
+        raise Exception("Unexpected p2p_state after P2P_FIND timeout: " + p2p_state)
+
+def test_p2p_service_discovery_peer_not_listening2(dev):
+    """P2P service discovery and peer not listening"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    add_bonjour_services(dev[0])
+    add_upnp_services(dev[0])
+    dev[0].p2p_listen()
+    dev[1].global_request("P2P_FIND type=social")
+    ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+    if ev is None:
+        raise Exception("Peer not found")
+    dev[0].p2p_stop_find()
+    time.sleep(0.53)
+    dev[1].request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+    ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=0.5)
+    if ev is not None:
+        raise Exception("Service discovery request unexpectedly received")
+    dev[1].p2p_stop_find()
+    ev = dev[1].wait_global_event(["P2P-FIND-STOPPED", "P2P-SERV-DISC-RESP"],
+                                  timeout=10)
+    if ev is None:
+        raise Exception("P2P-FIND-STOPPED event timed out")
+    if "P2P-SERV-DISC-RESP" in ev:
+        raise Exception("Unexpected SD response")
+    p2p_state = get_p2p_state(dev[1])
+    if p2p_state != "IDLE":
+        raise Exception("Unexpected p2p_state after P2P_FIND timeout: " + p2p_state)
diff --git a/hostap/tests/hwsim/test_p2p_set.py b/hostap/tests/hwsim/test_p2p_set.py
new file mode 100644
index 0000000..ba1e7ce
--- /dev/null
+++ b/hostap/tests/hwsim/test_p2p_set.py
@@ -0,0 +1,123 @@
+# P2P_SET test cases
+# 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.
+
+def test_p2p_set(dev):
+    """P2P_SET commands"""
+    for cmd in [ "",
+                 "foo bar",
+                 "noa 1",
+                 "noa 1,2",
+                 "noa 1,2,3",
+                 "noa -1,0,0",
+                 "noa 256,0,0",
+                 "noa 0,-1,0",
+                 "noa 0,0,-1",
+                 "noa 0,0,1",
+                 "noa 255,10,20",
+                 "ps 1",
+                 "ps 2",
+                 "oppps 1",
+                 "ctwindow 1",
+                 "conc_pref foo",
+                 "peer_filter foo",
+                 "client_apsd 0",
+                 "client_apsd 0,0",
+                 "client_apsd 0,0,0",
+                 "disc_int 1",
+                 "disc_int 1 2",
+                 "disc_int 2 1 10",
+                 "disc_int -1 0 10",
+                 "disc_int 0 -1 10",
+                 "ssid_postfix 123456789012345678901234" ]:
+        if "FAIL" not in dev[0].request("P2P_SET " + cmd):
+            raise Exception("Invalid P2P_SET accepted: " + cmd)
+
+def test_p2p_set_discoverability(dev):
+    """P2P_SET discoverability"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+
+    dev[0].p2p_start_go(freq="2412")
+    if "OK" not in dev[1].request("P2P_SET discoverability 0"):
+        raise Exception("P2P_SET discoverability 0 failed")
+    pin = dev[1].wps_read_pin()
+    dev[0].p2p_go_authorize_client(pin)
+    dev[1].p2p_connect_group(addr0, pin, timeout=20, social=True, freq="2412")
+
+    if not dev[2].discover_peer(addr1, timeout=10):
+        if not dev[2].discover_peer(addr1, timeout=10):
+            if not dev[2].discover_peer(addr1, timeout=10):
+                raise Exception("Could not discover group client")
+
+    peer = dev[2].get_peer(addr1)
+    if int(peer['dev_capab'], 16) & 0x02 != 0:
+        raise Exception("Discoverability dev_capab reported: " + peer['dev_capab'])
+    dev[2].p2p_stop_find()
+
+    if "OK" not in dev[1].request("P2P_SET discoverability 1"):
+        raise Exception("P2P_SET discoverability 1 failed")
+    dev[1].dump_monitor()
+    dev[1].group_request("REASSOCIATE")
+    ev = dev[1].wait_group_event(["CTRL-EVENT-CONNECTED"], timeout=20)
+    if ev is None:
+        raise Exception("Connection timed out")
+
+    dev[2].request("P2P_FLUSH")
+    if not dev[2].discover_peer(addr1, timeout=10):
+        if not dev[2].discover_peer(addr1, timeout=10):
+            if not dev[2].discover_peer(addr1, timeout=10):
+                raise Exception("Could not discover group client")
+
+    peer = dev[2].get_peer(addr1)
+    if int(peer['dev_capab'], 16) & 0x02 != 0x02:
+        raise Exception("Discoverability dev_capab reported: " + peer['dev_capab'])
+    dev[2].p2p_stop_find()
+
+def test_p2p_set_managed(dev):
+    """P2P_SET managed"""
+    addr0 = dev[0].p2p_dev_addr()
+
+    if "OK" not in dev[0].request("P2P_SET managed 1"):
+        raise Exception("P2P_SET managed 1 failed")
+
+    dev[0].p2p_listen()
+    if not dev[1].discover_peer(addr0):
+        raise Exception("Could not discover peer")
+    peer = dev[1].get_peer(addr0)
+    if int(peer['dev_capab'], 16) & 0x08 != 0x08:
+        raise Exception("Managed dev_capab not reported: " + peer['dev_capab'])
+    dev[1].p2p_stop_find()
+
+    if "OK" not in dev[0].request("P2P_SET managed 0"):
+        raise Exception("P2P_SET managed 0 failed")
+
+    if not dev[2].discover_peer(addr0):
+        raise Exception("Could not discover peer")
+    peer = dev[2].get_peer(addr0)
+    if int(peer['dev_capab'], 16) & 0x08 != 0:
+        raise Exception("Managed dev_capab reported: " + peer['dev_capab'])
+    dev[2].p2p_stop_find()
+    dev[0].p2p_stop_find()
+
+def test_p2p_set_ssid_postfix(dev):
+    """P2P_SET ssid_postfix"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    postfix = "12345678901234567890123"
+
+    try:
+        if "OK" not in dev[0].request("P2P_SET ssid_postfix " + postfix):
+            raise Exception("P2P_SET ssid_postfix failed")
+        dev[0].p2p_start_go(freq="2412")
+        pin = dev[1].wps_read_pin()
+        dev[0].p2p_go_authorize_client(pin)
+        dev[1].p2p_connect_group(addr0, pin, timeout=20, social=True, freq="2412")
+        if postfix not in dev[1].get_group_status_field("ssid"):
+            raise Exception("SSID postfix missing from status")
+        if postfix not in dev[1].group_request("SCAN_RESULTS"):
+            raise Exception("SSID postfix missing from scan results")
+    finally:
+        dev[0].request("P2P_SET ssid_postfix ")
diff --git a/hostap/tests/hwsim/test_p2p_wifi_display.py b/hostap/tests/hwsim/test_p2p_wifi_display.py
new file mode 100644
index 0000000..e820639
--- /dev/null
+++ b/hostap/tests/hwsim/test_p2p_wifi_display.py
@@ -0,0 +1,321 @@
+# Wi-Fi Display test cases
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+import threading
+import Queue
+
+import hwsim_utils
+import utils
+from test_p2p_autogo import connect_cli
+from test_p2p_persistent import form, invite, invite_from_cli, invite_from_go
+
+def test_wifi_display(dev):
+    """Wi-Fi Display extensions to P2P"""
+    wfd_devinfo = "00411c440028"
+    dev[0].request("SET wifi_display 1")
+    dev[0].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo)
+    if wfd_devinfo not in dev[0].request("WFD_SUBELEM_GET 0"):
+        raise Exception("Could not fetch back configured subelement")
+
+    # Associated BSSID
+    dev[0].request("WFD_SUBELEM_SET 1 0006020304050607")
+    # Coupled Sink
+    dev[0].request("WFD_SUBELEM_SET 6 000700000000000000")
+    # Session Info
+    dev[0].request("WFD_SUBELEM_SET 9 0000")
+    # WFD Extended Capability
+    dev[0].request("WFD_SUBELEM_SET 7 00020000")
+    # WFD Content Protection
+    prot = "0001" + "00"
+    dev[0].request("WFD_SUBELEM_SET 5 " + prot)
+    # WFD Video Formats
+    video = "0015" + "010203040506070809101112131415161718192021"
+    dev[0].request("WFD_SUBELEM_SET 3 " + video)
+    # WFD 3D Video Formats
+    video_3d = "0011" + "0102030405060708091011121314151617"
+    dev[0].request("WFD_SUBELEM_SET 4 " + video_3d)
+    # WFD Audio Formats
+    audio = "000f" + "010203040506070809101112131415"
+    dev[0].request("WFD_SUBELEM_SET 2 " + audio)
+
+    elems = dev[0].request("WFD_SUBELEM_GET all")
+    if wfd_devinfo not in elems:
+        raise Exception("Could not fetch back configured subelements")
+
+    wfd_devinfo2 = "00001c440028"
+    dev[1].request("SET wifi_display 1")
+    dev[1].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo2)
+    if wfd_devinfo2 not in dev[1].request("WFD_SUBELEM_GET 0"):
+        raise Exception("Could not fetch back configured subelement")
+
+    dev[0].p2p_listen()
+    if "FAIL" in dev[1].global_request("P2P_SERV_DISC_REQ " + dev[0].p2p_dev_addr() + " wifi-display [source][pri-sink] 2,3,4,5"):
+        raise Exception("Setting SD request failed")
+    dev[1].p2p_find(social=True)
+    ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=10)
+    if ev is None:
+        raise Exception("Device discovery request not reported")
+    ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+    if ev is None:
+        raise Exception("Device discovery timed out")
+    if "wfd_dev_info=0x" + wfd_devinfo not in ev:
+        raise Exception("Wi-Fi Display Info not in P2P-DEVICE-FOUND event")
+    if "new=1" not in ev:
+        raise Exception("new=1 flag missing from P2P-DEVICE-FOUND event")
+    ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=5)
+    if ev is None:
+        raise Exception("Service discovery timed out")
+    if prot not in ev:
+        raise Exception("WFD Content Protection missing from WSD response")
+    if video not in ev:
+        raise Exception("WFD Video Formats missing from WSD response")
+    if video_3d not in ev:
+        raise Exception("WFD 3D Video Formats missing from WSD response")
+    if audio not in ev:
+        raise Exception("WFD Audio Formats missing from WSD response")
+
+    dev[1].dump_monitor()
+    dev[0].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo2)
+    ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+    if ev is None:
+        raise Exception("Peer info update timed out")
+    if "new=0" not in ev:
+        raise Exception("new=0 flag missing from P2P-DEVICE-FOUND event")
+    if "wfd_dev_info=0x" + wfd_devinfo2 not in ev:
+        raise Exception("Wi-Fi Display Info not in P2P-DEVICE-FOUND event")
+    dev[1].dump_monitor()
+    dev[0].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo)
+    ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+    if ev is None:
+        raise Exception("Peer info update timed out")
+    if "new=0" not in ev:
+        raise Exception("new=0 flag missing from P2P-DEVICE-FOUND event")
+    if "wfd_dev_info=0x" + wfd_devinfo not in ev:
+        raise Exception("Wi-Fi Display Info not in P2P-DEVICE-FOUND event")
+
+    pin = dev[0].wps_read_pin()
+    dev[0].p2p_go_neg_auth(dev[1].p2p_dev_addr(), pin, 'display')
+    res1 = dev[1].p2p_go_neg_init(dev[0].p2p_dev_addr(), pin, 'enter',
+                                  timeout=20, go_intent=15, freq=2437)
+    res2 = dev[0].p2p_go_neg_auth_result()
+
+    bss = dev[0].get_bss("p2p_dev_addr=" + dev[1].p2p_dev_addr())
+    if bss['bssid'] != dev[1].p2p_interface_addr():
+        raise Exception("Unexpected BSSID in the BSS entry for the GO")
+    if wfd_devinfo2 not in bss['wfd_subelems']:
+        raise Exception("Could not see wfd_subelems in GO's BSS entry")
+    peer = dev[0].get_peer(dev[1].p2p_dev_addr())
+    if wfd_devinfo2 not in peer['wfd_subelems']:
+        raise Exception("Could not see wfd_subelems in GO's peer entry")
+    peer = dev[1].get_peer(dev[0].p2p_dev_addr())
+    if wfd_devinfo not in peer['wfd_subelems']:
+        raise Exception("Could not see wfd_subelems in client's peer entry")
+
+    wfd_devinfo3 = "00001c440028"
+    dev[2].request("SET wifi_display 1")
+    dev[2].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo3)
+    dev[2].p2p_find(social=True)
+    ev = dev[2].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+    if ev is None:
+        raise Exception("Device discovery timed out")
+    if dev[1].p2p_dev_addr() not in ev:
+        ev = dev[2].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+        if ev is None:
+            raise Exception("Device discovery timed out")
+        if dev[1].p2p_dev_addr() not in ev:
+            raise Exception("Could not discover GO")
+    if "wfd_dev_info=0x" + wfd_devinfo2 not in ev:
+        raise Exception("Wi-Fi Display Info not in P2P-DEVICE-FOUND event")
+    bss = dev[2].get_bss("p2p_dev_addr=" + dev[1].p2p_dev_addr())
+    if bss['bssid'] != dev[1].p2p_interface_addr():
+        raise Exception("Unexpected BSSID in the BSS entry for the GO")
+    if wfd_devinfo2 not in bss['wfd_subelems']:
+        raise Exception("Could not see wfd_subelems in GO's BSS entry")
+    peer = dev[2].get_peer(dev[1].p2p_dev_addr())
+    if wfd_devinfo2 not in peer['wfd_subelems']:
+        raise Exception("Could not see wfd_subelems in GO's peer entry")
+    dev[2].p2p_stop_find()
+
+    if dev[0].request("WFD_SUBELEM_GET 2") != audio:
+        raise Exception("Unexpected WFD_SUBELEM_GET 2 value")
+    if dev[0].request("WFD_SUBELEM_GET 3") != video:
+        raise Exception("Unexpected WFD_SUBELEM_GET 3 value")
+    if dev[0].request("WFD_SUBELEM_GET 4") != video_3d:
+        raise Exception("Unexpected WFD_SUBELEM_GET 42 value")
+    if dev[0].request("WFD_SUBELEM_GET 5") != prot:
+        raise Exception("Unexpected WFD_SUBELEM_GET 5 value")
+    if "FAIL" not in dev[0].request("WFD_SUBELEM_SET "):
+        raise Exception("Unexpected WFD_SUBELEM_SET success")
+    if "FAIL" not in dev[0].request("WFD_SUBELEM_SET 6"):
+        raise Exception("Unexpected WFD_SUBELEM_SET success")
+    if "OK" not in dev[0].request("WFD_SUBELEM_SET 6 "):
+        raise Exception("Unexpected WFD_SUBELEM_SET failure")
+    if "FAIL" not in dev[0].request("WFD_SUBELEM_SET 6 0"):
+        raise Exception("Unexpected WFD_SUBELEM_SET success")
+    if "FAIL" not in dev[0].request("WFD_SUBELEM_SET 6 0q"):
+        raise Exception("Unexpected WFD_SUBELEM_SET success")
+    if dev[0].request("WFD_SUBELEM_GET 6") != "":
+        raise Exception("Unexpected WFD_SUBELEM_GET 6 response")
+    if dev[0].request("WFD_SUBELEM_GET 8") != "":
+        raise Exception("Unexpected WFD_SUBELEM_GET 8 response")
+
+    if dev[0].global_request("WFD_SUBELEM_GET 2") != audio:
+        raise Exception("Unexpected WFD_SUBELEM_GET 2 value from global interface")
+    if "OK" not in dev[0].global_request("WFD_SUBELEM_SET 1 0006020304050608"):
+        raise Exception("WFD_SUBELEM_SET failed on global interface")
+    if dev[0].request("WFD_SUBELEM_GET 1") != "0006020304050608":
+        raise Exception("Unexpected WFD_SUBELEM_GET 1 value (per-interface)")
+
+    elems = dev[0].request("WFD_SUBELEM_GET all")
+    if "OK" not in dev[0].request("WFD_SUBELEM_SET all " + elems):
+        raise Exception("WFD_SUBELEM_SET all failed")
+    if dev[0].request("WFD_SUBELEM_GET all") != elems:
+        raise Exception("Mismatch in WFS_SUBELEM_SET/GET all")
+    test = "00000600411c440028"
+    if "OK" not in dev[0].request("WFD_SUBELEM_SET all " + test):
+        raise Exception("WFD_SUBELEM_SET all failed")
+    if dev[0].request("WFD_SUBELEM_GET all") != test:
+        raise Exception("Mismatch in WFS_SUBELEM_SET/GET all")
+
+    if "FAIL" not in dev[0].request("WFD_SUBELEM_SET all qwerty"):
+        raise Exception("Invalid WFD_SUBELEM_SET all succeeded")
+    if "FAIL" not in dev[0].request("WFD_SUBELEM_SET all 11"):
+        raise Exception("Invalid WFD_SUBELEM_SET all succeeded")
+    dev[0].request("WFD_SUBELEM_SET all 112233445566")
+    dev[0].request("WFD_SUBELEM_SET all ff0000fe0000fd00")
+
+    if "FAIL" not in dev[0].request("WFD_SUBELEM_SET 300 112233"):
+        raise Exception("Invalid WFD_SUBELEM_SET 300 succeeded")
+    if "FAIL" not in dev[0].request("WFD_SUBELEM_SET -1 112233"):
+        raise Exception("Invalid WFD_SUBELEM_SET -1 succeeded")
+    if "FAIL" not in dev[0].request("WFD_SUBELEM_GET 300"):
+        raise Exception("Invalid WFD_SUBELEM_GET 300 succeeded")
+    if "FAIL" not in dev[0].request("WFD_SUBELEM_GET -1"):
+        raise Exception("Invalid WFD_SUBELEM_GET -1 succeeded")
+
+    dev[0].request("SET wifi_display 0")
+    dev[1].request("SET wifi_display 0")
+    dev[2].request("SET wifi_display 0")
+
+def enable_wifi_display(dev):
+    dev.request("SET wifi_display 1")
+    dev.request("WFD_SUBELEM_SET 0 000600411c440028")
+
+def test_wifi_display_go_invite(dev):
+    """P2P GO with Wi-Fi Display inviting a client to join"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+
+    try:
+        enable_wifi_display(dev[0])
+        enable_wifi_display(dev[1])
+        enable_wifi_display(dev[2])
+
+        dev[1].p2p_listen()
+        if not dev[0].discover_peer(addr1, social=True):
+            raise Exception("Peer " + addr1 + " not found")
+        dev[0].p2p_listen()
+        if not dev[1].discover_peer(addr0, social=True):
+            raise Exception("Peer " + addr0 + " not found")
+        dev[1].p2p_listen()
+
+        logger.info("Authorize invitation")
+        pin = dev[1].wps_read_pin()
+        dev[1].global_request("P2P_CONNECT " + addr0 + " " + pin + " join auth")
+
+        dev[0].p2p_start_go(freq=2412)
+
+        # Add test client to the group
+        connect_cli(dev[0], dev[2], social=True, freq=2412)
+
+        logger.info("Invite peer to join the group")
+        dev[0].p2p_go_authorize_client(pin)
+        dev[0].global_request("P2P_INVITE group=" + dev[0].group_ifname + " peer=" + addr1)
+        ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED",
+                                       "P2P-GROUP-STARTED"], timeout=20)
+        if ev is None:
+            raise Exception("Timeout on invitation on peer")
+        if "P2P-INVITATION-RECEIVED" in ev:
+            raise Exception("Unexpected request to accept pre-authorized invitation")
+
+        dev[0].remove_group()
+        dev[1].wait_go_ending_session()
+        dev[2].wait_go_ending_session()
+
+    finally:
+        dev[0].request("SET wifi_display 0")
+        dev[1].request("SET wifi_display 0")
+        dev[2].request("SET wifi_display 0")
+
+def test_wifi_display_persistent_group(dev):
+    """P2P persistent group formation and re-invocation with Wi-Fi Display enabled"""
+    try:
+        enable_wifi_display(dev[0])
+        enable_wifi_display(dev[1])
+        enable_wifi_display(dev[2])
+
+        form(dev[0], dev[1])
+        peer = dev[1].get_peer(dev[0].p2p_dev_addr())
+        listen_freq = peer['listen_freq']
+        invite_from_cli(dev[0], dev[1])
+        invite_from_go(dev[0], dev[1])
+
+        dev[0].dump_monitor()
+        dev[1].dump_monitor()
+        networks = dev[0].list_networks(p2p=True)
+        if len(networks) != 1:
+            raise Exception("Unexpected number of networks")
+        if "[P2P-PERSISTENT]" not in networks[0]['flags']:
+            raise Exception("Not the persistent group data")
+        if "OK" not in dev[0].global_request("P2P_GROUP_ADD persistent=" + networks[0]['id'] + " freq=" + listen_freq):
+            raise Exception("Could not start GO")
+        ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=2)
+        if ev is None:
+            raise Exception("GO start up timed out")
+        dev[0].group_form_result(ev)
+
+        connect_cli(dev[0], dev[2], social=True, freq=listen_freq)
+        dev[0].dump_monitor()
+        dev[1].dump_monitor()
+        invite(dev[1], dev[0])
+        ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
+        if ev is None:
+            raise Exception("Timeout on group re-invocation (on client)")
+        dev[1].group_form_result(ev)
+
+        ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=0.1)
+        if ev is not None:
+            raise Exception("Unexpected P2P-GROUP-START on GO")
+        hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+
+    finally:
+        dev[0].request("SET wifi_display 0")
+        dev[1].request("SET wifi_display 0")
+        dev[2].request("SET wifi_display 0")
+
+def test_wifi_display_invalid_subelem(dev):
+    """Wi-Fi Display and invalid subelement parsing"""
+    addr1 = dev[1].p2p_dev_addr()
+
+    try:
+        enable_wifi_display(dev[0])
+        enable_wifi_display(dev[1])
+        dev[1].request("WFD_SUBELEM_SET 0 ffff00411c440028")
+
+        dev[1].p2p_listen()
+        dev[0].p2p_find(social=True)
+        ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+        if ev is None:
+            raise Exception("Device discovery timed out")
+        if "wfd_dev_info=" in ev:
+            raise Exception("Invalid WFD subelement was shown")
+
+    finally:
+        dev[0].request("SET wifi_display 0")
+        dev[1].request("SET wifi_display 0")
diff --git a/hostap/tests/hwsim/test_p2ps.py b/hostap/tests/hwsim/test_p2ps.py
new file mode 100644
index 0000000..5f0b13b
--- /dev/null
+++ b/hostap/tests/hwsim/test_p2ps.py
@@ -0,0 +1,983 @@
+# P2P services
+# Copyright (c) 2014-2015, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+import random
+import re
+
+import hwsim_utils
+from wpasupplicant import WpaSupplicant
+from test_p2p_grpform import check_grpform_results
+from test_p2p_grpform import remove_group
+from test_p2p_persistent import go_neg_pin_authorized_persistent
+from utils import HwsimSkip
+
+# Dev[0] -> Advertiser
+# Dev[1] -> Seeker
+# ev0 -> Event generated at advertiser side
+# ev1 -> Event generated at Seeker side
+
+def p2ps_advertise(r_dev, r_role, svc_name, srv_info, rsp_info=None, cpt=None):
+    """P2PS Advertise function"""
+    adv_id = random.randrange(1, 0xFFFFFFFF)
+    advid = hex(adv_id)[2:]
+
+    cpt_param = (" cpt=" + cpt) if cpt is not None else ""
+
+    if rsp_info is not None and srv_info is not None:
+        if "OK" not in r_dev.global_request("P2P_SERVICE_ADD asp " + str(r_role) + " " + str(advid) + " 1 1108 " + svc_name + cpt_param + " svc_info='" + srv_info + "'" + " rsp_info=" + rsp_info + "'"):
+            raise Exception("P2P_SERVICE_ADD with response info and service info failed")
+
+    if rsp_info is None and srv_info is not None:
+        if "OK" not in r_dev.global_request("P2P_SERVICE_ADD asp " + str(r_role) + " " + str(advid) + " 1 1108 " + svc_name + cpt_param + " svc_info='" + srv_info + "'"):
+            raise Exception("P2P_SERVICE_ADD with service info failed")
+
+    if rsp_info is None and srv_info is None:
+        if "OK" not in r_dev.global_request("P2P_SERVICE_ADD asp " + str(r_role) + " " + str(advid) + " 1 1108 " + svc_name + cpt_param):
+            raise Exception("P2P_SERVICE_ADD without service info and without response info failed")
+
+    if rsp_info is not None and srv_info is None:
+        if "OK" not in r_dev.global_request("P2P_SERVICE_ADD asp " + str(r_role) + " " + str(adv_id) + " 1 1108 " + svc_name + cpt_param + " svc_info='" + " rsp_info=" + rsp_info + "'"):
+            raise Exception("P2P_SERVICE_ADD with response info failed")
+
+    r_dev.p2p_listen()
+    return advid
+
+def p2ps_exact_seek(i_dev, r_dev, svc_name, srv_info=None,
+                    single_peer_expected=True):
+    """P2PS exact service seek request"""
+    if srv_info is not None:
+        ev1 = i_dev.global_request("P2P_SERV_DISC_REQ 00:00:00:00:00:00 asp 1 " + svc_name + " '" + srv_info + "'")
+        if ev1 is None:
+            raise Exception("Failed to add Service Discovery request for exact seek request")
+
+    if "OK" not in i_dev.global_request("P2P_FIND 10 type=social seek=" + svc_name):
+        raise Exception("Failed to initiate seek operation")
+
+    timeout = time.time() + 10
+    ev1 = i_dev.wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+    while ev1 is not None and not single_peer_expected:
+        if r_dev.p2p_dev_addr() in ev1 and "adv_id=" in ev1:
+            break
+        ev1 = i_dev.wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+
+        if timeout < time.time():
+            raise Exception("Device not found")
+
+    if ev1 is None:
+        raise Exception("P2P-DEVICE-FOUND timeout on seeker side")
+    if r_dev.p2p_dev_addr() not in ev1:
+        raise Exception("Unexpected peer")
+
+    if srv_info is None:
+        adv_id = ev1.split("adv_id=")[1].split(" ")[0]
+        rcvd_svc_name = ev1.split("asp_svc=")[1].split(" ")[0]
+        if rcvd_svc_name != svc_name:
+            raise Exception("service name not matching")
+    else:
+        ev1 = i_dev.wait_global_event(["P2P-SERV-ASP-RESP"], timeout=10)
+        if ev1 is None:
+            raise Exception("Failed to receive Service Discovery Response")
+        if r_dev.p2p_dev_addr() not in ev1:
+            raise Exception("Service Discovery response from Unknown Peer")
+        if srv_info is not None and srv_info not in ev1:
+            raise Exception("service info not available in Service Discovery response")
+        adv_id = ev1.split(" ")[3]
+        rcvd_svc_name = ev1.split(" ")[6]
+        if rcvd_svc_name != svc_name:
+            raise Exception("service name not matching")
+
+    return [adv_id, rcvd_svc_name]
+
+def p2ps_nonexact_seek(i_dev, r_dev, svc_name, srv_info=None, adv_num=None):
+    """P2PS nonexact service seek request"""
+    if adv_num is None:
+       adv_num = 1
+    if srv_info is not None:
+        ev1 = i_dev.global_request("P2P_SERV_DISC_REQ 00:00:00:00:00:00 asp 1 " + svc_name + " '" + srv_info + "'")
+    else:
+        ev1 = i_dev.global_request("P2P_SERV_DISC_REQ 00:00:00:00:00:00 asp 1 " + svc_name + " '")
+    if ev1 is None:
+        raise Exception("Failed to add Service Discovery request for nonexact seek request")
+    if "OK" not in i_dev.global_request("P2P_FIND 10 type=social seek="):
+        raise Exception("Failed to initiate seek")
+    ev1 = i_dev.wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+    if ev1 is None:
+        raise Exception("P2P-DEVICE-FOUND timeout on seeker side")
+    if r_dev.p2p_dev_addr() not in ev1:
+        raise Exception("Unexpected peer")
+    ev_list = []
+    for i in range (0, adv_num):
+        ev1 = i_dev.wait_global_event(["P2P-SERV-ASP-RESP"], timeout=10)
+        if ev1 is None:
+            raise Exception("Failed to receive Service Discovery Response")
+        if r_dev.p2p_dev_addr() not in ev1:
+            raise Exception("Service Discovery response from Unknown Peer")
+        if srv_info is not None and srv_info not in ev1:
+            raise Exception("service info not available in Service Discovery response")
+        adv_id = ev1.split(" ")[3]
+        rcvd_svc_name = ev1.split(" ")[6]
+        ev_list.append(''.join([adv_id, ' ', rcvd_svc_name]))
+    return ev_list
+
+def p2ps_parse_event(ev, *args):
+    ret = ()
+    for arg in args:
+        m = re.search("\s+" + arg + r"=(\S+)", ev)
+        ret += (m.group(1) if m is not None else None,)
+    return ret
+
+def p2ps_provision(seeker, advertiser, adv_id, auto_accept=True, method="1000", adv_cpt=None, seeker_cpt=None):
+    addr0 = seeker.p2p_dev_addr()
+    addr1 = advertiser.p2p_dev_addr()
+
+    seeker.asp_provision(addr1, adv_id=str(adv_id), adv_mac=addr1, session_id=1,
+                         session_mac=addr0, method=method, cpt=seeker_cpt)
+
+    if not auto_accept or method == "100":
+        pin = None
+        ev_pd_start = advertiser.wait_global_event(["P2PS-PROV-START"],
+                                                   timeout=10)
+        if ev_pd_start is None:
+            raise Exception("P2PS-PROV-START timeout on Advertiser side")
+        peer = ev_pd_start.split()[1]
+        advert_id, advert_mac, session, session_mac =\
+            p2ps_parse_event(ev_pd_start, "adv_id", "adv_mac", "session", "mac")
+
+        ev = seeker.wait_global_event(["P2P-PROV-DISC-FAILURE"], timeout=10)
+        if ev is None:
+            raise Exception("P2P-PROV-DISC-FAILURE timeout on seeker side")
+
+        if method == "100":
+            ev = advertiser.wait_global_event(["P2P-PROV-DISC-ENTER-PIN"],
+                                              timeout=10)
+            if ev is None:
+                raise Exception("P2P-PROV-DISC-ENTER-PIN timeout on advertiser side")
+            if addr0 not in ev:
+                raise Exception("Unknown peer " + addr0)
+            ev = seeker.wait_global_event(["P2P-PROV-DISC-SHOW-PIN"],
+                                          timeout=10)
+            if ev is None:
+                raise Exception("P2P-PROV-DISC-SHOW-PIN timeout on seeker side")
+            if addr1 not in ev:
+                raise Exception("Unknown peer " + addr1)
+            pin = ev.split()[2]
+        elif method == "8":
+            ev = advertiser.wait_global_event(["P2P-PROV-DISC-SHOW-PIN"],
+                                              timeout=10)
+            if ev is None:
+                raise Exception("P2P-PROV-DISC-SHOW-PIN timeout on advertiser side")
+            if addr0 not in ev:
+                raise Exception("Unknown peer " + addr0)
+            pin = ev.split()[2]
+
+        advertiser.asp_provision(peer, adv_id=advert_id, adv_mac=advert_mac,
+                                 session_id=int(session, 0),
+                                 session_mac=session_mac, status=12,
+                                 cpt=adv_cpt)
+
+        ev1 = seeker.wait_global_event(["P2PS-PROV-DONE"], timeout=10)
+        if ev1 is None:
+            raise Exception("P2PS-PROV-DONE timeout on seeker side")
+
+        ev2 = advertiser.wait_global_event(["P2PS-PROV-DONE"], timeout=10)
+        if ev2 is None:
+            raise Exception("P2PS-PROV-DONE timeout on advertiser side")
+
+        if method == "8":
+            ev = seeker.wait_global_event(["P2P-PROV-DISC-ENTER-PIN"],
+                                          timeout=10)
+            if ev is None:
+                raise Exception("P2P-PROV-DISC-ENTER-PIN failed on seeker side")
+            if addr1 not in ev:
+                raise Exception("Unknown peer " + addr1)
+
+        if pin is not None:
+            return ev1, ev2, pin
+        return ev1, ev2
+
+    # Auto-accept is true and the method is either P2PS or advertiser is DISPLAY
+    ev1 = seeker.wait_global_event(["P2PS-PROV-DONE"], timeout=10)
+    if ev1 is None:
+        raise Exception("P2PS-PROV-DONE timeout on seeker side")
+
+    ev2 = advertiser.wait_global_event(["P2PS-PROV-DONE"], timeout=10)
+    if ev2 is None:
+        raise Exception("P2PS-PROV-DONE timeout on advertiser side")
+
+    if method == "8":
+        ev = seeker.wait_global_event(["P2P-PROV-DISC-ENTER-PIN"], timeout=10)
+        if ev is None:
+            raise Exception("P2P-PROV-DISC-ENTER-PIN timeout on seeker side")
+        if addr1 not in ev:
+            raise Exception("Unknown peer " + addr1)
+        ev = advertiser.wait_global_event(["P2P-PROV-DISC-SHOW-PIN"],
+                                          timeout=10)
+        if ev is None:
+            raise Exception("P2P-PROV-DISC-SHOW-PIN timeout on advertiser side")
+        if addr0 not in ev:
+            raise Exception("Unknown peer " + addr0)
+        pin = ev.split()[2]
+        return ev1, ev2, pin
+
+    return ev1, ev2
+
+def p2ps_connect_pd(dev0, dev1, ev0, ev1, pin=None):
+    conf_methods_map = {"8": "p2ps", "1": "display", "5": "keypad"}
+    peer0 = ev0.split()[1]
+    peer1 = ev1.split()[1]
+    status0, conncap0, adv_id0, adv_mac0, mac0, session0, dev_passwd_id0, go0, join0, feature_cap0, persist0 =\
+        p2ps_parse_event(ev0, "status", "conncap", "adv_id", "adv_mac", "mac", "session", "dev_passwd_id", "go", "join", "feature_cap", "persist")
+    status1, conncap1, adv_id1, adv_mac1, mac1, session1, dev_passwd_id1, go1, join1, feature_cap1, persist1 =\
+        p2ps_parse_event(ev1, "status", "conncap", "adv_id", "adv_mac", "mac", "session", "dev_passwd_id", "go", "join", "feature_cap", "persist")
+
+    if status0 != "0" and status0 != "12":
+        raise Exception("PD failed on " + dev0.p2p_dev_addr())
+
+    if status1 != "0" and status1 != "12":
+        raise Exception("PD failed on " + dev1.p2p_dev_addr())
+
+    if status0 == "12" and status1 == "12":
+        raise Exception("Both sides have status 12 which doesn't make sense")
+
+    if adv_id0 != adv_id1 or adv_id0 is None:
+        raise Exception("Adv. IDs don't match")
+
+    if adv_mac0 != adv_mac1 or adv_mac0 is None:
+        raise Exception("Adv. MACs don't match")
+
+    if session0 != session1 or session0 is None:
+        raise Exception("Session IDs don't match")
+
+    if mac0 != mac1 or mac0 is None:
+        raise Exception("Session MACs don't match")
+
+    #TODO: Validate feature capability
+
+    if bool(persist0) != bool(persist1):
+        raise Exception("Only one peer has persistent group")
+
+    if persist0 is None and not all([conncap0, conncap1, dev_passwd_id0,
+                                     dev_passwd_id1]):
+        raise Exception("Persistent group not used but conncap/dev_passwd_id are missing")
+
+    if persist0 is not None and any([conncap0, conncap1, dev_passwd_id0,
+                                     dev_passwd_id1]):
+        raise Exception("Persistent group is used but conncap/dev_passwd_id are present")
+
+    # Persistent Connection (todo: handle frequency)
+    if persist0 is not None:
+        if "OK" not in dev0.global_request("P2P_GROUP_ADD persistent=" + persist0 + " freq=2412"):
+            raise Exception("Could not re-start persistent group")
+        ev0 = dev0.wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+        if ev0 is None:
+            raise Exception("P2P-GROUP-STARTED timeout on " + dev0.p2p_dev_addr())
+        dev0.group_form_result(ev0)
+
+        if "OK" not in dev1.global_request("P2P_GROUP_ADD persistent=" + persist1 + " freq=2412"):
+            raise Exception("Could not re-start persistent group")
+        ev1 = dev1.wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+        if ev1 is None:
+            raise Exception("P2P-GROUP-STARTED timeout on " + dev1.p2p_dev_addr())
+        dev1.group_form_result(ev1)
+        if "GO" in ev0:
+            ev = dev0.wait_global_event(["AP-STA-CONNECTED"], timeout=10)
+            if ev is None:
+                raise Exception("AP-STA-CONNECTED timeout on " + dev0.p2p_dev_addr())
+        else:
+            ev = dev1.wait_global_event(["AP-STA-CONNECTED"], timeout=10)
+            if ev is None:
+                raise Exception("AP-STA-CONNECTED timeout on " + dev1.p2p_dev_addr())
+    else:
+        try:
+            method0 = conf_methods_map[dev_passwd_id0]
+            method1 = conf_methods_map[dev_passwd_id1]
+        except KeyError:
+            raise Exception("Unsupported method")
+
+        if method0 == "p2ps":
+            pin = "12345670"
+        if pin is None:
+            raise Exception("Pin is not provided")
+
+        if conncap0 == "1" and conncap1 == "1": # NEW/NEW - GON
+            if any([join0, join1, go0, go1]):
+                raise Exception("Unexpected join/go PD attributes")
+            dev0.p2p_listen()
+            if "OK" not in dev0.global_request("P2P_CONNECT " + peer0 + " " + pin + " " + method0 + " persistent auth"):
+                raise Exception("P2P_CONNECT fails on " + dev0.p2p_dev_addr())
+            if "OK" not in dev1.global_request("P2P_CONNECT " + peer1 + " " + pin + " " + method1 + " persistent"):
+                raise Exception("P2P_CONNECT fails on " + dev1.p2p_dev_addr())
+            ev = dev0.wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=10)
+            if ev is None:
+                raise Exception("GO Neg did not succeed on " + dev0.p2p_dev_addr())
+            ev = dev1.wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=10)
+            if ev is None:
+                raise Exception("GO Neg did not succeed on " + dev1.p2p_dev_addr())
+            ev = dev0.wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+            if ev is None:
+                raise Exception("P2P-GROUP-STARTED timeout on " + dev0.p2p_dev_addr())
+            dev0.group_form_result(ev)
+            ev = dev1.wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+            if ev is None:
+                raise Exception("P2P-GROUP-STARTED timeout on " + dev1.p2p_dev_addr())
+            dev1.group_form_result(ev)
+        else:
+            if conncap0 == "2" and conncap1 == "4":  # dev0 CLI, dev1 GO
+                dev_cli, dev_go, go_if, join_address, go_method, cli_method = dev0, dev1, go1, join0, method1, method0
+            elif conncap0 == "4" and conncap1 == "2":  # dev0 GO, dev1 CLI
+                dev_cli, dev_go, go_if, join_address, go_method, cli_method = dev1, dev0, go0, join1, method0, method1
+            else:
+                raise Exception("Bad connection capabilities")
+
+            if go_if is None:
+                raise Exception("Device " + dev_go.p2p_dev_addr() + " failed to become GO")
+            if join_address is None:
+                raise Exception("Device " + dev_cli.p2p_dev_addr() + " failed to become CLI")
+            ev = dev_go.wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+            if ev is None:
+                raise Exception("P2P-GROUP-STARTED timeout on " + dev_go.p2p_dev_addr())
+            dev_go.group_form_result(ev)
+            if go_method != "p2ps":
+                ev = dev_go.group_request("WPS_PIN any " + pin)
+                if ev is None:
+                    raise Exception("Failed to initiate pin authorization on registrar side")
+            if "OK" not in dev_cli.global_request("P2P_CONNECT " + join_address + " " + pin + " " + cli_method + " persistent join"):
+                raise Exception("P2P_CONNECT failed on " + dev_cli.p2p_dev_addr())
+            ev = dev_cli.wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+            if ev is None:
+                raise Exception("P2P-GROUP-STARTED timeout on " + dev_cli.p2p_dev_addr())
+            dev_cli.group_form_result(ev)
+            ev = dev_go.wait_global_event(["AP-STA-CONNECTED"], timeout=10)
+            if ev is None:
+                raise Exception("AP-STA-CONNECTED timeout on " + dev_go.p2p_dev_addr())
+
+    hwsim_utils.test_connectivity_p2p(dev0, dev1)
+
+def set_no_group_iface(dev, enable):
+    if enable:
+        res = dev.get_driver_status()
+	if (int(res['capa.flags'], 0) & 0x20000000):
+	    raise HwsimSkip("P2P Device used. Cannot set enable no_group_iface")
+        dev.global_request("SET p2p_no_group_iface 1")
+    else:
+        dev.global_request("SET p2p_no_group_iface 0")
+
+def test_p2ps_exact_search(dev):
+    """P2PS exact service request"""
+    p2ps_advertise(r_dev=dev[0], r_role='1', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx')
+
+    ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev0 is None:
+        raise Exception("Unable to remove the advertisement instance")
+
+def test_p2ps_exact_search_srvinfo(dev):
+    """P2PS exact service request with service info"""
+    p2ps_advertise(r_dev=dev[0], r_role='0', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx',
+                                              srv_info='2 GB')
+
+    ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev0 is None:
+        raise Exception("Unable to remove the advertisement instance")
+
+def test_p2ps_nonexact_search(dev):
+    """P2PS nonexact seek request"""
+    p2ps_advertise(r_dev=dev[0], r_role='0', svc_name='org.wi-fi.wfds.play.rx',
+                   srv_info='I support Miracast Mode ')
+    ev_list = p2ps_nonexact_seek(i_dev=dev[1], r_dev=dev[0],
+                                 svc_name='org.wi-fi.wfds.play*')
+    adv_id = ev_list[0].split()[0]
+
+    ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev0 is None:
+        raise Exception("Unable to remove the advertisement instance")
+
+def test_p2ps_nonexact_search_srvinfo(dev):
+    """P2PS nonexact seek request with service info"""
+    p2ps_advertise(r_dev=dev[0], r_role='0', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    ev_list = p2ps_nonexact_seek(i_dev=dev[1], r_dev=dev[0],
+                                 svc_name='org.wi-fi.wfds.send*',
+                                 srv_info='2 GB')
+    adv_id = ev_list[0].split()[0]
+    ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev0 is None:
+        raise Exception("Unable to remove the advertisement instance")
+
+def test_p2ps_connect_p2ps_method_nonautoaccept(dev):
+    """P2PS connect for non-auto-accept and P2PS config method"""
+    p2ps_advertise(r_dev=dev[0], r_role='0', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    ev_list = p2ps_nonexact_seek(i_dev=dev[1], r_dev=dev[0],
+                                 svc_name='org.wi-fi.wfds.send*',
+                                 srv_info='2 GB')
+    adv_id = ev_list[0].split()[0]
+    ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id, auto_accept=False)
+    p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+
+    ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev0 is None:
+        raise Exception("Unable to remove the advertisement instance")
+    remove_group(dev[0], dev[1])
+
+def test_p2ps_connect_p2ps_method_autoaccept(dev):
+    """P2PS connection with P2PS default config method and auto-accept"""
+    p2ps_advertise(r_dev=dev[0], r_role='1', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx',
+                                              srv_info='2 GB')
+
+    ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+    p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+
+    ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev0 is None:
+        raise Exception("Unable to remove the advertisement instance")
+    remove_group(dev[0], dev[1])
+
+def test_p2ps_connect_keypad_method_nonautoaccept(dev):
+    """P2PS Connection with non-auto-accept and seeker having keypad method"""
+    p2ps_advertise(r_dev=dev[0], r_role='0', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    ev_list = p2ps_nonexact_seek(i_dev=dev[1], r_dev=dev[0],
+                                 svc_name='org.wi-fi.wfds.send*',
+                                 srv_info='2 GB')
+    adv_id = ev_list[0].split()[0]
+
+    ev1, ev0, pin = p2ps_provision(dev[1], dev[0], adv_id, auto_accept=False, method="8")
+    p2ps_connect_pd(dev[0], dev[1], ev0, ev1, pin)
+
+    ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev0 is None:
+        raise Exception("Unable to remove the advertisement instance")
+    remove_group(dev[0], dev[1])
+
+def test_p2ps_connect_display_method_nonautoaccept(dev):
+    """P2PS connection with non-auto-accept and seeker having display method"""
+    p2ps_advertise(r_dev=dev[0], r_role='0', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    ev_list = p2ps_nonexact_seek(i_dev=dev[1], r_dev=dev[0],
+                                 svc_name='org.wi-fi.wfds*', srv_info='2 GB')
+    adv_id = ev_list[0].split()[0]
+
+    ev1, ev0, pin = p2ps_provision(dev[1], dev[0], adv_id, auto_accept=False, method="100")
+    p2ps_connect_pd(dev[0], dev[1], ev0, ev1, pin)
+
+    ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev0 is None:
+        raise Exception("Unable to remove the advertisement instance")
+    remove_group(dev[0], dev[1])
+
+def test_p2ps_connect_keypad_method_autoaccept(dev):
+    """P2PS connection with auto-accept and keypad method on seeker side"""
+    p2ps_advertise(r_dev=dev[0], r_role='1', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx',
+                                              srv_info='2 GB')
+
+    ev1, ev0, pin = p2ps_provision(dev[1], dev[0], adv_id, method="8")
+    p2ps_connect_pd(dev[0], dev[1], ev0, ev1, pin)
+
+    ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev0 is None:
+        raise Exception("Unable to remove the advertisement instance")
+    remove_group(dev[0], dev[1])
+
+def test_p2ps_connect_display_method_autoaccept(dev):
+    """P2PS connection with auto-accept and display method on seeker side"""
+    p2ps_advertise(r_dev=dev[0], r_role='1', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx',
+                                              srv_info='2 GB')
+
+    ev1, ev0, pin = p2ps_provision(dev[1], dev[0], adv_id, method="100")
+    p2ps_connect_pd(dev[0], dev[1], ev0, ev1, pin)
+
+    ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev0 is None:
+        raise Exception("Unable to remove the advertisement instance")
+    remove_group(dev[0], dev[1])
+
+def test_p2ps_connect_adv_go_p2ps_method(dev):
+    """P2PS auto-accept connection with advertisement as GO and P2PS method"""
+    p2ps_advertise(r_dev=dev[0], r_role='4', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx',
+                                              srv_info='2 GB')
+
+    ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+    p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+
+    ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev0 is None:
+        raise Exception("Unable to remove the advertisement instance")
+    remove_group(dev[0], dev[1])
+
+def test_p2ps_connect_adv_go_p2ps_method_group_iface(dev):
+    """P2PS auto-accept connection with advertisement as GO and P2PS method using separate group interface"""
+    set_no_group_iface(dev[0], 0)
+    set_no_group_iface(dev[1], 0)
+    p2ps_advertise(r_dev=dev[0], r_role='4', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx',
+                                              srv_info='2 GB')
+
+    ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+    p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+
+    ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev0 is None:
+        raise Exception("Unable to remove the advertisement instance")
+    remove_group(dev[0], dev[1])
+
+def test_p2ps_connect_adv_client_p2ps_method(dev):
+    """P2PS auto-accept connection with advertisement as Client and P2PS method"""
+    p2ps_advertise(r_dev=dev[0], r_role='2', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx',
+                                              srv_info='2 GB')
+
+    ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+    p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+
+    ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev0 is None:
+        raise Exception("Unable to remove the advertisement instance")
+    remove_group(dev[0], dev[1])
+
+def p2ps_connect_adv_go_pin_method(dev, keep_group=False):
+    p2ps_advertise(r_dev=dev[0], r_role='4', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx',
+                                              srv_info='2 GB')
+    ev1, ev0, pin = p2ps_provision(dev[1], dev[0], adv_id, method="8")
+    p2ps_connect_pd(dev[0], dev[1], ev0, ev1, pin)
+
+    if not keep_group:
+        ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+        if ev0 is None:
+            raise Exception("Unable to remove the advertisement instance")
+        remove_group(dev[0], dev[1])
+
+def test_p2ps_connect_adv_go_pin_method(dev):
+    """P2PS advertiser as GO with keypad config method on seeker side and auto-accept"""
+    p2ps_connect_adv_go_pin_method(dev)
+
+def test_p2ps_connect_adv_client_pin_method(dev):
+    """P2PS advertiser as client with keypad config method on seeker side and auto-accept"""
+    dev[0].flush_scan_cache()
+    p2ps_advertise(r_dev=dev[0], r_role='2', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx',
+                                              srv_info='2 GB')
+
+    ev1, ev0, pin = p2ps_provision(dev[1], dev[0], adv_id, method="8")
+    p2ps_connect_pd(dev[0], dev[1], ev0, ev1, pin)
+
+    ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev0 is None:
+        raise Exception("Unable to remove the advertisement instance")
+    remove_group(dev[0], dev[1])
+
+def test_p2ps_service_discovery_multiple_queries(dev):
+    """P2P service discovery with multiple queries"""
+    addr0 = dev[0].p2p_dev_addr()
+    addr1 = dev[1].p2p_dev_addr()
+    adv_id1 = p2ps_advertise(r_dev=dev[0], r_role='0',
+                             svc_name='org.wi-fi.wfds.send.tx',
+                             srv_info='I can transfer files upto size of 2 GB')
+    adv_id2 = p2ps_advertise(r_dev=dev[0], r_role='0',
+                             svc_name='org.wi-fi.wfds.send.rx',
+                             srv_info='I can receive files upto size of 2 GB')
+    adv_id3 = p2ps_advertise(r_dev=dev[0], r_role='1',
+                             svc_name='org.wi-fi.wfds.display.tx',
+                             srv_info='Miracast Mode')
+    adv_id4 = p2ps_advertise(r_dev=dev[0], r_role='1',
+                             svc_name='org.wi-fi.wfds.display.rx',
+                             srv_info='Miracast Mode')
+
+    dev[1].global_request("P2P_SERV_DISC_REQ " + addr0 + " asp 1 org.wi-fi.wfds.display.tx 'Miracast Mode'")
+    dev[1].global_request("P2P_FIND 10 type=social seek=org.wi-fi.wfds.display.tx")
+    dev[1].global_request("P2P_SERV_DISC_REQ " + addr0 + " asp 2 org.wi-fi.wfds.send* 'size of 2 GB'")
+    dev[1].p2p_stop_find()
+    dev[1].global_request("P2P_FIND 10 type=social seek=")
+    ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+    if ev is None:
+        raise Exception("P2P Device Found timed out")
+    if addr0 not in ev:
+        raise Exception("Unexpected service discovery request source")
+    ev_list = []
+    for i in range(0, 3):
+        ev = dev[1].wait_global_event(["P2P-SERV-ASP-RESP"], timeout=10)
+        if ev is None:
+            raise Exception("P2P Service discovery timed out")
+        if addr0 in ev:
+            ev_list.append(ev)
+            if len(ev_list) == 3:
+                break
+    dev[1].p2p_stop_find()
+
+    for test in [ ("seek=org.wi-fi.wfds.display.TX",
+                   "asp_svc=org.wi-fi.wfds.display.tx"),
+                  ("seek=foo seek=org.wi-fi.wfds.display.tx seek=bar",
+                   "asp_svc=org.wi-fi.wfds.display.tx"),
+                  ("seek=1 seek=2 seek=3 seek=org.wi-fi.wfds.display.tx seek=4 seek=5 seek=6",
+                   "asp_svc=org.wi-fi.wfds.display.tx"),
+                  ("seek=not-found", None),
+                  ("seek=org.wi-fi.wfds", "asp_svc=org.wi-fi.wfds")]:
+        dev[2].global_request("P2P_FIND 10 type=social " + test[0])
+        if test[1] is None:
+            ev = dev[2].wait_global_event(["P2P-DEVICE-FOUND"], timeout=1)
+            if ev is not None:
+                raise Exception("Unexpected device found: " + ev)
+            continue
+        ev = dev[2].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+        if ev is None:
+            raise Exception("P2P device discovery timed out (dev2)")
+            if test[1] not in ev:
+                raise Exception("Expected asp_svc not reported: " + ev)
+        dev[2].p2p_stop_find()
+        dev[2].request("P2P_FLUSH")
+
+    dev[0].p2p_stop_find()
+
+    ev1 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id1))
+    if ev1 is None:
+        raise Exception("Unable to remove the advertisement instance")
+    ev2 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id2))
+    if ev2 is None:
+        raise Exception("Unable to remove the advertisement instance")
+    ev3 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id3))
+    if ev3 is None:
+        raise Exception("Unable to remove the advertisement instance")
+    ev4 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id4))
+    if ev4 is None:
+        raise Exception("Unable to remove the advertisement instance")
+
+    if "OK" not in dev[0].global_request("P2P_SERVICE_ADD asp 1 12345678 1 1108 org.wi-fi.wfds.foobar svc_info='Test'"):
+        raise Exception("P2P_SERVICE_ADD failed")
+    if "OK" not in dev[0].global_request("P2P_SERVICE_DEL asp all"):
+        raise Exception("P2P_SERVICE_DEL asp all failed")
+    if "OK" not in dev[0].global_request("P2P_SERVICE_ADD asp 1 12345678 1 1108 org.wi-fi.wfds.foobar svc_info='Test'"):
+        raise Exception("P2P_SERVICE_ADD failed")
+    if "OK" not in dev[0].global_request("P2P_SERVICE_REP asp 1 12345678 1 1108 org.wi-fi.wfds.foobar svc_info='Test'"):
+        raise Exception("P2P_SERVICE_REP failed")
+    if "FAIL" not in dev[0].global_request("P2P_SERVICE_REP asp 1 12345678 1 1108 org.wi-fi.wfds.Foo svc_info='Test'"):
+        raise Exception("Invalid P2P_SERVICE_REP accepted")
+    if "OK" not in dev[0].global_request("P2P_SERVICE_ADD asp 1 a2345678 1 1108 org.wi-fi.wfds.something svc_info='Test'"):
+        raise Exception("P2P_SERVICE_ADD failed")
+    if "OK" not in dev[0].global_request("P2P_SERVICE_ADD asp 1 a2345679 1 1108 org.wi-fi.wfds.Foo svc_info='Test'"):
+        raise Exception("P2P_SERVICE_ADD failed")
+
+def get_ifnames():
+    with open('/proc/net/dev', 'r') as f:
+        data = f.read()
+    ifnames = []
+    for line in data.splitlines():
+        ifname = line.strip().split(' ')[0]
+        if ':' not in ifname:
+            continue
+        ifname = ifname.split(':')[0]
+        ifnames.append(ifname)
+    return ifnames
+
+def p2ps_connect_p2ps_method(dev, keep_group=False):
+    dev[0].flush_scan_cache()
+    dev[1].flush_scan_cache()
+    p2ps_advertise(r_dev=dev[0], r_role='2', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx',
+                                              srv_info='2 GB')
+    ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+    ifnames = get_ifnames()
+    p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+
+    grp_ifname0 = dev[0].get_group_ifname()
+    grp_ifname1 = dev[1].get_group_ifname()
+    if not keep_group:
+        ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+        if ev0 is None:
+            raise Exception("Unable to remove the advertisement instance")
+        ifnames = ifnames + get_ifnames()
+        remove_group(dev[0], dev[1])
+        ifnames = ifnames + get_ifnames()
+
+    return grp_ifname0, grp_ifname1, ifnames
+
+def has_string_prefix(vals, prefix):
+    for val in vals:
+        if val.startswith(prefix):
+            return True
+    return False
+
+def test_p2ps_connect_p2ps_method_1(dev):
+    """P2PS connection with P2PS method - no group interface"""
+    set_no_group_iface(dev[0], 1)
+    set_no_group_iface(dev[1], 1)
+
+    (grp_ifname0, grp_ifname1, ifnames) = p2ps_connect_p2ps_method(dev)
+    if grp_ifname0 != dev[0].ifname:
+        raise Exception("unexpected dev0 group ifname: " + grp_ifname0)
+    if grp_ifname1 != dev[1].ifname:
+        raise Exception("unexpected dev1 group ifname: " + grp_ifname1)
+    if has_string_prefix(ifnames, 'p2p-' + grp_ifname0):
+        raise Exception("dev0 group interface unexpectedly present")
+    if has_string_prefix(ifnames, 'p2p-' + grp_ifname1):
+        raise Exception("dev1 group interface unexpectedly present")
+
+def test_p2ps_connect_p2ps_method_2(dev):
+    """P2PS connection with P2PS method - group interface on dev0"""
+    set_no_group_iface(dev[0], 0)
+    set_no_group_iface(dev[1], 1)
+
+    (grp_ifname0, grp_ifname1, ifnames) = p2ps_connect_p2ps_method(dev)
+    if not grp_ifname0.startswith('p2p-' + dev[0].ifname + '-'):
+        raise Exception("unexpected dev0 group ifname: " + grp_ifname0)
+    if grp_ifname1 != dev[1].ifname:
+        raise Exception("unexpected dev1 group ifname: " + grp_ifname1)
+    if has_string_prefix(ifnames, 'p2p-' + grp_ifname0):
+        raise Exception("dev0 group interface unexpectedly present")
+
+def test_p2ps_connect_p2ps_method_3(dev):
+    """P2PS connection with P2PS method - group interface on dev1"""
+    set_no_group_iface(dev[0], 1)
+    set_no_group_iface(dev[1], 0)
+
+    (grp_ifname0, grp_ifname1, ifnames) = p2ps_connect_p2ps_method(dev)
+    if grp_ifname0 != dev[0].ifname:
+        raise Exception("unexpected dev0 group ifname: " + grp_ifname0)
+    if not grp_ifname1.startswith('p2p-' + dev[1].ifname + '-'):
+        raise Exception("unexpected dev1 group ifname: " + grp_ifname1)
+    if has_string_prefix(ifnames, 'p2p-' + grp_ifname0):
+        raise Exception("dev0 group interface unexpectedly present")
+
+def test_p2ps_connect_p2ps_method_4(dev):
+    """P2PS connection with P2PS method - group interface on both"""
+    set_no_group_iface(dev[0], 0)
+    set_no_group_iface(dev[1], 0)
+
+    (grp_ifname0, grp_ifname1, ifnames) = p2ps_connect_p2ps_method(dev)
+    if not grp_ifname0.startswith('p2p-' + dev[0].ifname + '-'):
+        raise Exception("unexpected dev0 group ifname: " + res0['ifname'])
+    if not grp_ifname1.startswith('p2p-' + dev[1].ifname + '-'):
+        raise Exception("unexpected dev1 group ifname: " + res1['ifname'])
+
+def test_p2ps_connect_adv_go_persistent(dev):
+    """P2PS auto-accept connection with advertisement as GO and having persistent group"""
+    go_neg_pin_authorized_persistent(i_dev=dev[0], i_intent=15,
+                                     r_dev=dev[1], r_intent=0)
+    dev[0].remove_group()
+    dev[1].wait_go_ending_session()
+
+    p2ps_advertise(r_dev=dev[0], r_role='4', svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB')
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx',
+                                              srv_info='2 GB')
+    ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+    if "persist=" not in ev0 or "persist=" not in ev1:
+        raise Exception("Persistent group isn't used by peers")
+
+    p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+    remove_group(dev[0], dev[1])
+
+def test_p2ps_client_probe(dev):
+    """P2PS CLI discoverability on operating channel"""
+    cli_probe = dev[0].global_request("SET p2p_cli_probe 1")
+    p2ps_connect_p2ps_method(dev, keep_group=True)
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[2], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx',
+                                              single_peer_expected=False)
+    dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    remove_group(dev[0], dev[1])
+
+def test_p2ps_go_probe(dev):
+    """P2PS GO discoverability on operating channel"""
+    p2ps_connect_adv_go_pin_method(dev, keep_group=True)
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[2], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx',
+                                              single_peer_expected=False)
+    dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    remove_group(dev[0], dev[1])
+
+def test_p2ps_wildcard_p2ps(dev):
+    """P2PS wildcard SD Probe Request/Response"""
+    p2ps_wildcard = "org.wi-fi.wfds"
+
+    adv_id = p2ps_advertise(r_dev=dev[0], r_role='1',
+                            svc_name='org.foo.service',
+                            srv_info='I can do stuff')
+    adv_id2 = p2ps_advertise(r_dev=dev[0], r_role='1',
+                             svc_name='org.wi-fi.wfds.send.rx',
+                             srv_info='I can receive files upto size 2 GB')
+
+    if "OK" not in dev[1].global_request("P2P_FIND 10 type=social seek=org.foo.service seek=" + p2ps_wildcard):
+        raise Exception("Failed on P2P_FIND command")
+
+    ev1 = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+    if ev1 is None:
+        raise Exception("P2P-DEVICE-FOUND timeout on seeker side")
+    if dev[0].p2p_dev_addr() not in ev1:
+        raise Exception("Unexpected peer")
+
+    ev2 = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+    if ev2 is None:
+        raise Exception("P2P-DEVICE-FOUND timeout on seeker side (2)")
+    if dev[0].p2p_dev_addr() not in ev2:
+        raise Exception("Unexpected peer (2)")
+
+    if p2ps_wildcard not in ev1 + ev2:
+        raise Exception("P2PS Wildcard name not found in P2P-DEVICE-FOUND event")
+    if "org.foo.service" not in ev1 + ev2:
+        raise Exception("Vendor specific service name not found in P2P-DEVICE-FOUND event")
+
+    if "OK" not in dev[1].global_request("P2P_STOP_FIND"):
+        raise Exception("P2P_STOP_FIND failed")
+    dev[1].dump_monitor()
+
+    res = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if res is None:
+        raise Exception("Unable to remove the advertisement instance")
+
+    if "OK" not in dev[1].global_request("P2P_FIND 10 type=social seek=" + p2ps_wildcard):
+        raise Exception("Failed on P2P_FIND command")
+
+    ev1 = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+    if ev1 is None:
+        raise Exception("P2P-DEVICE-FOUND timeout on seeker side")
+    if dev[0].p2p_dev_addr() not in ev1:
+        raise Exception("Unexpected peer")
+    if p2ps_wildcard not in ev1:
+        raise Exception("P2PS Wildcard name not found in P2P-DEVICE-FOUND event (2)")
+    dev[1].dump_monitor()
+
+    res = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id2))
+    if res is None:
+        raise Exception("Unable to remove the advertisement instance 2")
+
+    if "OK" not in dev[1].global_request("P2P_FIND 10 type=social seek=" + p2ps_wildcard):
+        raise Exception("Failed on P2P_FIND command")
+
+    ev1 = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=2)
+    if ev1 is not None:
+        raise Exception("Unexpected P2P-DEVICE-FOUND event on seeker side")
+    dev[1].p2p_stop_find()
+    dev[1].dump_monitor()
+
+def test_p2ps_many_services_in_probe(dev):
+    """P2PS with large number of services in Probe Request/Response"""
+    long1 = 'org.example.0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.a'
+    long2 = 'org.example.0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.b'
+    long3 = 'org.example.0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.c'
+    long4 = 'org.example.0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.d'
+    long5 = 'org.example.0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.e'
+    for name in [ long1, long2, long3, long4, long5 ]:
+        p2ps_advertise(r_dev=dev[0], r_role='1',
+                       svc_name=name,
+                       srv_info='I can do stuff')
+
+    if "OK" not in dev[1].global_request("P2P_FIND 10 type=social seek=%s seek=%s seek=%s seek=%s seek=%s" % (long1, long2, long3, long4, long5)):
+        raise Exception("Failed on P2P_FIND command")
+
+    events = ""
+    # Note: Require only four events since all the services do not fit within
+    # the length limit.
+    for i in range(4):
+        ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+        if ev is None:
+            raise Exception("Missing P2P-DEVICE-FOUND")
+        events = events + ev
+    dev[1].p2p_stop_find()
+    dev[1].dump_monitor()
+    for name in [ long2, long3, long4, long5 ]:
+        if name not in events:
+            raise Exception("Service missing from peer events")
+
+def p2ps_test_feature_capability_cpt(dev, adv_cpt, seeker_cpt, adv_role,
+                                     result):
+    p2ps_advertise(r_dev=dev[0], r_role=adv_role,
+                   svc_name='org.wi-fi.wfds.send.rx',
+                   srv_info='I can receive files upto size 2 GB', cpt=adv_cpt)
+    [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+                                              svc_name='org.wi-fi.wfds.send.rx',
+                                              srv_info='2 GB')
+    auto_accept = adv_role != "0"
+    ev1, ev0, pin = p2ps_provision(dev[1], dev[0], adv_id,
+                                   auto_accept=auto_accept, adv_cpt=adv_cpt,
+                                   seeker_cpt=seeker_cpt, method="8")
+
+    status0, fcap0 = p2ps_parse_event(ev0, "status", "feature_cap")
+    status1, fcap1 = p2ps_parse_event(ev0, "status", "feature_cap")
+
+    if fcap0 is None:
+        raise Exception("Bad feature capability on Seeker side")
+    if fcap1 is None:
+        raise Exception("Bad feature capability on Advertiser side")
+    if fcap0 != fcap1:
+        raise Exception("Incompatible feature capability values")
+
+    if status0 not in ("0", "12") or status1 not in ("0", "12"):
+        raise Exception("Unexpected PD result status")
+
+    if result == "UDP" and fcap0[1] != "1":
+        raise Exception("Unexpected CPT feature capability value (expected: UDP)")
+    elif result == "MAC" and fcap0[1] != "2":
+        raise Exception("Unexpected CPT feature capability value (expected: MAC)")
+
+    ev = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+    if ev is None:
+        raise Exception("Unable to remove the advertisement instance")
+
+def test_p2ps_feature_capability_mac_autoaccept(dev):
+    """P2PS PD Feature Capability CPT: advertiser MAC, seeker UDP:MAC, autoaccept"""
+    p2ps_test_feature_capability_cpt(dev, adv_cpt="MAC", seeker_cpt="UDP:MAC",
+                                     adv_role="4", result="MAC")
+
+def test_p2ps_feature_capability_mac_nonautoaccept(dev):
+    """P2PS PD Feature Capability CPT: advertiser:MAC, seeker UDP:MAC, nonautoaccept"""
+    p2ps_test_feature_capability_cpt(dev, adv_cpt="MAC", seeker_cpt="UDP:MAC",
+                                     adv_role="0", result="MAC")
+
+def test_p2ps_feature_capability_mac_udp_autoaccept(dev):
+    """P2PS PD Feature Capability CPT: advertiser MAC:UDP, seeker UDP:MAC, autoaccept"""
+    p2ps_test_feature_capability_cpt(dev, adv_cpt="MAC:UDP",
+                                     seeker_cpt="UDP:MAC", adv_role="2",
+                                     result="MAC")
+
+def test_p2ps_feature_capability_mac_udp_nonautoaccept(dev):
+    """P2PS PD Feature Capability CPT: advertiser MAC:UDP, seeker UDP:MAC, nonautoaccept"""
+    p2ps_test_feature_capability_cpt(dev, adv_cpt="MAC:UDP",
+                                     seeker_cpt="UDP:MAC", adv_role="0",
+                                     result="UDP")
+
+def test_p2ps_feature_capability_udp_mac_autoaccept(dev):
+    """P2PS PD Feature Capability CPT: advertiser UDP:MAC, seeker MAC:UDP, autoaccept"""
+    p2ps_test_feature_capability_cpt(dev, adv_cpt="UDP:MAC",
+                                     seeker_cpt="MAC:UDP", adv_role="2",
+                                     result="UDP")
+
+def test_p2ps_feature_capability_udp_mac_nonautoaccept(dev):
+    """P2PS PD Feature Capability CPT: advertiser UDP:MAC, seeker MAC:UDP,  nonautoaccept"""
+    p2ps_test_feature_capability_cpt(dev, adv_cpt="UDP:MAC",
+                                     seeker_cpt="MAC:UDP", adv_role="0",
+                                     result="MAC")
diff --git a/hostap/tests/hwsim/test_peerkey.py b/hostap/tests/hwsim/test_peerkey.py
new file mode 100644
index 0000000..aac4473
--- /dev/null
+++ b/hostap/tests/hwsim/test_peerkey.py
@@ -0,0 +1,72 @@
+# PeerKey tests
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+
+import hwsim_utils
+import hostapd
+from utils import skip_with_fips
+from wlantest import Wlantest
+
+def test_peerkey(dev, apdev):
+    """RSN AP and PeerKey between two STAs"""
+    ssid = "test-peerkey"
+    passphrase = "12345678"
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['peerkey'] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412", peerkey=True)
+    dev[1].connect(ssid, psk=passphrase, scan_freq="2412", peerkey=True)
+    hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+
+    dev[0].request("STKSTART " + dev[1].p2p_interface_addr())
+    time.sleep(0.5)
+    # NOTE: Actual use of the direct link (DLS) is not supported in
+    # mac80211_hwsim, so this operation fails at setting the keys after
+    # successfully completed 4-way handshake. This test case does allow the
+    # key negotiation part to be tested for coverage, though.
+
+def test_peerkey_unknown_peer(dev, apdev):
+    """RSN AP and PeerKey attempt with unknown peer"""
+    ssid = "test-peerkey"
+    passphrase = "12345678"
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['peerkey'] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412", peerkey=True)
+    dev[1].connect(ssid, psk=passphrase, scan_freq="2412", peerkey=True)
+    hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+
+    dev[0].request("STKSTART " + dev[2].p2p_interface_addr())
+    time.sleep(0.5)
+
+def test_peerkey_pairwise_mismatch(dev, apdev):
+    """RSN TKIP+CCMP AP and PeerKey between two STAs using different ciphers"""
+    skip_with_fips(dev[0])
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+    ssid = "test-peerkey"
+    passphrase = "12345678"
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    params['peerkey'] = "1"
+    params['rsn_pairwise'] = "TKIP CCMP"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412", peerkey=True,
+                   pairwise="CCMP")
+    dev[1].connect(ssid, psk=passphrase, scan_freq="2412", peerkey=True,
+                   pairwise="TKIP")
+    hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+
+    dev[0].request("STKSTART " + dev[1].p2p_interface_addr())
+    time.sleep(0.5)
+    dev[1].request("STKSTART " + dev[0].p2p_interface_addr())
+    time.sleep(0.5)
diff --git a/hostap/tests/hwsim/test_pmksa_cache.py b/hostap/tests/hwsim/test_pmksa_cache.py
new file mode 100644
index 0000000..0899c84
--- /dev/null
+++ b/hostap/tests/hwsim/test_pmksa_cache.py
@@ -0,0 +1,828 @@
+# WPA2-Enterprise PMKSA caching tests
+# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import subprocess
+import time
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import alloc_fail
+from test_ap_eap import eap_connect
+
+def test_pmksa_cache_on_roam_back(dev, apdev):
+    """PMKSA cache to skip EAP on reassociation back to same AP"""
+    params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                   eap="GPSK", identity="gpsk user",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+    pmksa = dev[0].get_pmksa(bssid)
+    if pmksa is None:
+        raise Exception("No PMKSA cache entry created")
+    if pmksa['opportunistic'] != '0':
+        raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    dev[0].dump_monitor()
+    logger.info("Roam to AP2")
+    # It can take some time for the second AP to become ready to reply to Probe
+    # Request frames especially under heavy CPU load, so allow couple of rounds
+    # of scanning to avoid reporting errors incorrectly just because of scans
+    # not having seen the target AP.
+    for i in range(0, 10):
+        dev[0].scan(freq="2412")
+        if dev[0].get_bss(bssid2) is not None:
+            break
+        logger.info("Scan again to find target AP")
+    dev[0].request("ROAM " + bssid2)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    dev[0].wait_connected(timeout=10, error="Roaming timed out")
+    pmksa2 = dev[0].get_pmksa(bssid2)
+    if pmksa2 is None:
+        raise Exception("No PMKSA cache entry found")
+    if pmksa2['opportunistic'] != '0':
+        raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+    dev[0].dump_monitor()
+    logger.info("Roam back to AP1")
+    dev[0].scan(freq="2412")
+    dev[0].request("ROAM " + bssid)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+                            "CTRL-EVENT-CONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("Roaming with the AP timed out")
+    if "CTRL-EVENT-EAP-STARTED" in ev:
+        raise Exception("Unexpected EAP exchange")
+    pmksa1b = dev[0].get_pmksa(bssid)
+    if pmksa1b is None:
+        raise Exception("No PMKSA cache entry found")
+    if pmksa['pmkid'] != pmksa1b['pmkid']:
+        raise Exception("Unexpected PMKID change for AP1")
+
+    dev[0].dump_monitor()
+    if "FAIL" in dev[0].request("PMKSA_FLUSH"):
+        raise Exception("PMKSA_FLUSH failed")
+    if dev[0].get_pmksa(bssid) is not None or dev[0].get_pmksa(bssid2) is not None:
+        raise Exception("PMKSA_FLUSH did not remove PMKSA entries")
+    dev[0].wait_disconnected(timeout=5)
+    dev[0].wait_connected(timeout=15, error="Reconnection timed out")
+
+def test_pmksa_cache_opportunistic_only_on_sta(dev, apdev):
+    """Opportunistic PMKSA caching enabled only on station"""
+    params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                   eap="GPSK", identity="gpsk user",
+                   password="abcdefghijklmnop0123456789abcdef", okc=True,
+                   scan_freq="2412")
+    pmksa = dev[0].get_pmksa(bssid)
+    if pmksa is None:
+        raise Exception("No PMKSA cache entry created")
+    if pmksa['opportunistic'] != '0':
+        raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    dev[0].dump_monitor()
+    logger.info("Roam to AP2")
+    dev[0].scan(freq="2412")
+    dev[0].request("ROAM " + bssid2)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    dev[0].wait_connected(timeout=10, error="Roaming timed out")
+    pmksa2 = dev[0].get_pmksa(bssid2)
+    if pmksa2 is None:
+        raise Exception("No PMKSA cache entry found")
+    if pmksa2['opportunistic'] != '0':
+        raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+    dev[0].dump_monitor()
+    logger.info("Roam back to AP1")
+    dev[0].scan(freq="2412")
+    dev[0].request("ROAM " + bssid)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+                            "CTRL-EVENT-CONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("Roaming with the AP timed out")
+    if "CTRL-EVENT-EAP-STARTED" in ev:
+        raise Exception("Unexpected EAP exchange")
+    pmksa1b = dev[0].get_pmksa(bssid)
+    if pmksa1b is None:
+        raise Exception("No PMKSA cache entry found")
+    if pmksa['pmkid'] != pmksa1b['pmkid']:
+        raise Exception("Unexpected PMKID change for AP1")
+
+def test_pmksa_cache_opportunistic(dev, apdev):
+    """Opportunistic PMKSA caching"""
+    params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+    params['okc'] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                   eap="GPSK", identity="gpsk user",
+                   password="abcdefghijklmnop0123456789abcdef", okc=True,
+                   scan_freq="2412")
+    pmksa = dev[0].get_pmksa(bssid)
+    if pmksa is None:
+        raise Exception("No PMKSA cache entry created")
+    if pmksa['opportunistic'] != '0':
+        raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    dev[0].dump_monitor()
+    logger.info("Roam to AP2")
+    dev[0].scan(freq="2412")
+    dev[0].request("ROAM " + bssid2)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+                            "CTRL-EVENT-CONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("Roaming with the AP timed out")
+    if "CTRL-EVENT-EAP-STARTED" in ev:
+        raise Exception("Unexpected EAP exchange")
+    pmksa2 = dev[0].get_pmksa(bssid2)
+    if pmksa2 is None:
+        raise Exception("No PMKSA cache entry created")
+
+    dev[0].dump_monitor()
+    logger.info("Roam back to AP1")
+    dev[0].scan(freq="2412")
+    dev[0].request("ROAM " + bssid)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+                            "CTRL-EVENT-CONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("Roaming with the AP timed out")
+    if "CTRL-EVENT-EAP-STARTED" in ev:
+        raise Exception("Unexpected EAP exchange")
+
+    pmksa1b = dev[0].get_pmksa(bssid)
+    if pmksa1b is None:
+        raise Exception("No PMKSA cache entry found")
+    if pmksa['pmkid'] != pmksa1b['pmkid']:
+        raise Exception("Unexpected PMKID change for AP1")
+
+def test_pmksa_cache_opportunistic_connect(dev, apdev):
+    """Opportunistic PMKSA caching with connect API"""
+    params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+    params['okc'] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+    wpas.connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                 eap="GPSK", identity="gpsk user",
+                 password="abcdefghijklmnop0123456789abcdef", okc=True,
+                 scan_freq="2412")
+    pmksa = wpas.get_pmksa(bssid)
+    if pmksa is None:
+        raise Exception("No PMKSA cache entry created")
+    if pmksa['opportunistic'] != '0':
+        raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    wpas.dump_monitor()
+    logger.info("Roam to AP2")
+    wpas.scan_for_bss(bssid2, freq="2412")
+    wpas.request("ROAM " + bssid2)
+    ev = wpas.wait_event(["CTRL-EVENT-EAP-STARTED",
+                            "CTRL-EVENT-CONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("Roaming with the AP timed out")
+    if "CTRL-EVENT-EAP-STARTED" in ev:
+        raise Exception("Unexpected EAP exchange")
+    pmksa2 = wpas.get_pmksa(bssid2)
+    if pmksa2 is None:
+        raise Exception("No PMKSA cache entry created")
+
+    wpas.dump_monitor()
+    logger.info("Roam back to AP1")
+    wpas.scan(freq="2412")
+    wpas.request("ROAM " + bssid)
+    ev = wpas.wait_event(["CTRL-EVENT-EAP-STARTED",
+                            "CTRL-EVENT-CONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("Roaming with the AP timed out")
+    if "CTRL-EVENT-EAP-STARTED" in ev:
+        raise Exception("Unexpected EAP exchange")
+
+    pmksa1b = wpas.get_pmksa(bssid)
+    if pmksa1b is None:
+        raise Exception("No PMKSA cache entry found")
+    if pmksa['pmkid'] != pmksa1b['pmkid']:
+        raise Exception("Unexpected PMKID change for AP1")
+
+def test_pmksa_cache_expiration(dev, apdev):
+    """PMKSA cache entry expiration"""
+    params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    dev[0].request("SET dot11RSNAConfigPMKLifetime 10")
+    dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                   eap="GPSK", identity="gpsk user",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+    pmksa = dev[0].get_pmksa(bssid)
+    if pmksa is None:
+        raise Exception("No PMKSA cache entry created")
+    logger.info("Wait for PMKSA cache entry to expire")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=15)
+    if ev is None:
+        raise Exception("No EAP reauthentication seen")
+    if "CTRL-EVENT-DISCONNECTED" in ev:
+        raise Exception("Unexpected disconnection")
+    pmksa2 = dev[0].get_pmksa(bssid)
+    if pmksa['pmkid'] == pmksa2['pmkid']:
+        raise Exception("PMKID did not change")
+
+def test_pmksa_cache_expiration_disconnect(dev, apdev):
+    """PMKSA cache entry expiration (disconnect)"""
+    params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    dev[0].request("SET dot11RSNAConfigPMKLifetime 2")
+    dev[0].request("SET dot11RSNAConfigPMKReauthThreshold 100")
+    dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                   eap="GPSK", identity="gpsk user",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+    pmksa = dev[0].get_pmksa(bssid)
+    if pmksa is None:
+        raise Exception("No PMKSA cache entry created")
+    hapd.request("SET auth_server_shared_secret incorrect")
+    logger.info("Wait for PMKSA cache entry to expire")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed",
+                            "CTRL-EVENT-DISCONNECTED"], timeout=15)
+    if ev is None:
+        raise Exception("No EAP reauthentication seen")
+    if "CTRL-EVENT-DISCONNECTED" not in ev:
+        raise Exception("Missing disconnection")
+    hapd.request("SET auth_server_shared_secret radius")
+    ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=15)
+    if ev is None:
+        raise Exception("No EAP reauthentication seen")
+    pmksa2 = dev[0].get_pmksa(bssid)
+    if pmksa['pmkid'] == pmksa2['pmkid']:
+        raise Exception("PMKID did not change")
+
+def test_pmksa_cache_and_cui(dev, apdev):
+    """PMKSA cache and Chargeable-User-Identity"""
+    params = hostapd.wpa2_eap_params(ssid="cui")
+    params['radius_request_cui'] = '1'
+    params['acct_server_addr'] = "127.0.0.1"
+    params['acct_server_port'] = "1813"
+    params['acct_server_shared_secret'] = "radius"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    dev[0].connect("cui", proto="RSN", key_mgmt="WPA-EAP",
+                   eap="GPSK", identity="gpsk-cui",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+    pmksa = dev[0].get_pmksa(bssid)
+    if pmksa is None:
+        raise Exception("No PMKSA cache entry created")
+    ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
+    if ev is None:
+        raise Exception("No connection event received from hostapd")
+
+    dev[0].dump_monitor()
+    logger.info("Disconnect and reconnect to the same AP")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    dev[0].request("RECONNECT")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+                            "CTRL-EVENT-CONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("Reconnect timed out")
+    if "CTRL-EVENT-EAP-STARTED" in ev:
+        raise Exception("Unexpected EAP exchange")
+    pmksa1b = dev[0].get_pmksa(bssid)
+    if pmksa1b is None:
+        raise Exception("No PMKSA cache entry found")
+    if pmksa['pmkid'] != pmksa1b['pmkid']:
+        raise Exception("Unexpected PMKID change for AP1")
+
+    dev[0].request("REAUTHENTICATE")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    for i in range(0, 20):
+        state = dev[0].get_status_field("wpa_state")
+        if state == "COMPLETED":
+            break
+        time.sleep(0.1)
+    if state != "COMPLETED":
+        raise Exception("Reauthentication did not complete")
+
+def test_pmksa_cache_preauth(dev, apdev):
+    """RSN pre-authentication to generate PMKSA cache entry"""
+    try:
+        params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+        params['bridge'] = 'ap-br0'
+        hostapd.add_ap(apdev[0]['ifname'], params)
+        subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+        eap_connect(dev[0], apdev[0], "PAX", "pax.user@example.com",
+                    password_hex="0123456789abcdef0123456789abcdef")
+
+        params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+        params['bridge'] = 'ap-br0'
+        params['rsn_preauth'] = '1'
+        params['rsn_preauth_interfaces'] = 'ap-br0'
+        hostapd.add_ap(apdev[1]['ifname'], params)
+        bssid1 = apdev[1]['bssid']
+        dev[0].scan(freq="2412")
+        success = False
+        status_seen = False
+        for i in range(0, 50):
+            if not status_seen:
+                status = dev[0].request("STATUS")
+                if "Pre-authentication EAPOL state machines:" in status:
+                    status_seen = True
+            time.sleep(0.1)
+            pmksa = dev[0].get_pmksa(bssid1)
+            if pmksa:
+                success = True
+                break
+        if not success:
+            raise Exception("No PMKSA cache entry created from pre-authentication")
+        if not status_seen:
+            raise Exception("Pre-authentication EAPOL status was not available")
+
+        dev[0].scan(freq="2412")
+        if "[WPA2-EAP-CCMP-preauth]" not in dev[0].request("SCAN_RESULTS"):
+            raise Exception("Scan results missing RSN element info")
+        dev[0].request("ROAM " + bssid1)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+                                "CTRL-EVENT-CONNECTED"], timeout=10)
+        if ev is None:
+            raise Exception("Roaming with the AP timed out")
+        if "CTRL-EVENT-EAP-STARTED" in ev:
+            raise Exception("Unexpected EAP exchange")
+        pmksa2 = dev[0].get_pmksa(bssid1)
+        if pmksa2 is None:
+            raise Exception("No PMKSA cache entry")
+        if pmksa['pmkid'] != pmksa2['pmkid']:
+            raise Exception("Unexpected PMKID change")
+
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'])
+        subprocess.call(['brctl', 'delbr', 'ap-br0'])
+
+def test_pmksa_cache_preauth_vlan_enabled(dev, apdev):
+    """RSN pre-authentication to generate PMKSA cache entry (dynamic_vlan optional but station without VLAN set)"""
+    try:
+        params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+        params['bridge'] = 'ap-br0'
+        params['dynamic_vlan'] = '1'
+        hostapd.add_ap(apdev[0]['ifname'], params)
+        subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+        eap_connect(dev[0], apdev[0], "PAX", "pax.user@example.com",
+                    password_hex="0123456789abcdef0123456789abcdef")
+
+        params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+        params['bridge'] = 'ap-br0'
+        params['rsn_preauth'] = '1'
+        params['rsn_preauth_interfaces'] = 'ap-br0'
+        params['dynamic_vlan'] = '1'
+        hostapd.add_ap(apdev[1]['ifname'], params)
+        bssid1 = apdev[1]['bssid']
+        dev[0].scan(freq="2412")
+        success = False
+        status_seen = False
+        for i in range(0, 50):
+            if not status_seen:
+                status = dev[0].request("STATUS")
+                if "Pre-authentication EAPOL state machines:" in status:
+                    status_seen = True
+            time.sleep(0.1)
+            pmksa = dev[0].get_pmksa(bssid1)
+            if pmksa:
+                success = True
+                break
+        if not success:
+            raise Exception("No PMKSA cache entry created from pre-authentication")
+        if not status_seen:
+            raise Exception("Pre-authentication EAPOL status was not available")
+
+        dev[0].scan(freq="2412")
+        if "[WPA2-EAP-CCMP-preauth]" not in dev[0].request("SCAN_RESULTS"):
+            raise Exception("Scan results missing RSN element info")
+        dev[0].request("ROAM " + bssid1)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+                                "CTRL-EVENT-CONNECTED"], timeout=10)
+        if ev is None:
+            raise Exception("Roaming with the AP timed out")
+        if "CTRL-EVENT-EAP-STARTED" in ev:
+            raise Exception("Unexpected EAP exchange")
+        pmksa2 = dev[0].get_pmksa(bssid1)
+        if pmksa2 is None:
+            raise Exception("No PMKSA cache entry")
+        if pmksa['pmkid'] != pmksa2['pmkid']:
+            raise Exception("Unexpected PMKID change")
+
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'])
+        subprocess.call(['brctl', 'delbr', 'ap-br0'])
+
+def test_pmksa_cache_preauth_vlan_used(dev, apdev):
+    """RSN pre-authentication to generate PMKSA cache entry (station with VLAN set)"""
+    try:
+        subprocess.call(['brctl', 'addbr', 'brvlan1'])
+        subprocess.call(['brctl', 'setfd', 'brvlan1', '0'])
+        params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+        params['bridge'] = 'ap-br0'
+        params['dynamic_vlan'] = '1'
+        params['vlan_file'] = 'hostapd.wlan3.vlan'
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+        eap_connect(dev[0], apdev[0], "PAX", "vlan1",
+                    password_hex="0123456789abcdef0123456789abcdef")
+
+        params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+        params['bridge'] = 'ap-br0'
+        params['rsn_preauth'] = '1'
+        params['rsn_preauth_interfaces'] = 'brvlan1'
+        params['dynamic_vlan'] = '1'
+        params['vlan_file'] = 'hostapd.wlan4.vlan'
+        hostapd.add_ap(apdev[1]['ifname'], params)
+        bssid1 = apdev[1]['bssid']
+        dev[0].scan(freq="2412")
+        success = False
+        status_seen = False
+        for i in range(0, 50):
+            if not status_seen:
+                status = dev[0].request("STATUS")
+                if "Pre-authentication EAPOL state machines:" in status:
+                    status_seen = True
+            time.sleep(0.1)
+            pmksa = dev[0].get_pmksa(bssid1)
+            if pmksa:
+                success = True
+                break
+        if not success:
+            raise Exception("No PMKSA cache entry created from pre-authentication")
+        if not status_seen:
+            raise Exception("Pre-authentication EAPOL status was not available")
+
+        dev[0].scan(freq="2412")
+        if "[WPA2-EAP-CCMP-preauth]" not in dev[0].request("SCAN_RESULTS"):
+            raise Exception("Scan results missing RSN element info")
+        dev[0].request("ROAM " + bssid1)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+                                "CTRL-EVENT-CONNECTED"], timeout=10)
+        if ev is None:
+            raise Exception("Roaming with the AP timed out")
+        if "CTRL-EVENT-EAP-STARTED" in ev:
+            raise Exception("Unexpected EAP exchange")
+        pmksa2 = dev[0].get_pmksa(bssid1)
+        if pmksa2 is None:
+            raise Exception("No PMKSA cache entry")
+        if pmksa['pmkid'] != pmksa2['pmkid']:
+            raise Exception("Unexpected PMKID change")
+
+        # Disconnect the STA from both APs to avoid forceful ifdown by the
+        # test script on a VLAN that this has an associated STA. That used to
+        # trigger a mac80211 warning.
+        dev[0].request("DISCONNECT")
+        hapd.request("DISABLE")
+
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['ip', 'link', 'set', 'dev', 'brvlan1', 'down'])
+        subprocess.call(['ip', 'link', 'set', 'dev', 'wlan3.1', 'down'],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['ip', 'link', 'set', 'dev', 'wlan4.1', 'down'],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['brctl', 'delif', 'brvlan1', 'wlan3.1'],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['brctl', 'delif', 'brvlan1', 'wlan4.1'],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['brctl', 'delbr', 'ap-br0'],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['brctl', 'delbr', 'brvlan1'])
+
+def test_pmksa_cache_disabled(dev, apdev):
+    """PMKSA cache disabling on AP"""
+    params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+    params['disable_pmksa_caching'] = '1'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                   eap="GPSK", identity="gpsk user",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    dev[0].dump_monitor()
+    logger.info("Roam to AP2")
+    dev[0].scan_for_bss(bssid2, freq="2412")
+    dev[0].request("ROAM " + bssid2)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    dev[0].wait_connected(timeout=10, error="Roaming timed out")
+
+    dev[0].dump_monitor()
+    logger.info("Roam back to AP1")
+    dev[0].scan(freq="2412")
+    dev[0].request("ROAM " + bssid)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+                            "CTRL-EVENT-CONNECTED"], timeout=20)
+    if ev is None:
+        raise Exception("Roaming with the AP timed out")
+    if "CTRL-EVENT-CONNECTED" in ev:
+        raise Exception("EAP exchange missing")
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=20)
+    if ev is None:
+        raise Exception("Roaming with the AP timed out")
+
+def test_pmksa_cache_ap_expiration(dev, apdev):
+    """PMKSA cache entry expiring on AP"""
+    params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                   eap="GPSK", identity="gpsk-user-session-timeout",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+    ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
+    if ev is None:
+        raise Exception("No connection event received from hostapd")
+    dev[0].request("DISCONNECT")
+    time.sleep(5)
+    dev[0].dump_monitor()
+    dev[0].request("RECONNECT")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+                            "CTRL-EVENT-CONNECTED"], timeout=20)
+    if ev is None:
+        raise Exception("Roaming with the AP timed out")
+    if "CTRL-EVENT-CONNECTED" in ev:
+        raise Exception("EAP exchange missing")
+    dev[0].wait_connected(timeout=20, error="Reconnect timed out")
+    dev[0].dump_monitor()
+    dev[0].wait_disconnected(timeout=20)
+    dev[0].wait_connected(timeout=20, error="Reassociation timed out")
+
+def test_pmksa_cache_multiple_sta(dev, apdev):
+    """PMKSA cache with multiple stations"""
+    params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                   eap="GPSK", identity="gpsk-user-session-timeout",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+    dev[1].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                   eap="GPSK", identity="gpsk user",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+    dev[2].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                   eap="GPSK", identity="gpsk-user-session-timeout",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    wpas.connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                 eap="GPSK", identity="gpsk user",
+                 password="abcdefghijklmnop0123456789abcdef",
+                 scan_freq="2412")
+
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    logger.info("Roam to AP2")
+    for sta in [ dev[1], dev[0], dev[2], wpas ]:
+        sta.dump_monitor()
+        sta.scan_for_bss(bssid2, freq="2412")
+        sta.request("ROAM " + bssid2)
+        ev = sta.wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+        if ev is None:
+            raise Exception("EAP success timed out")
+        sta.wait_connected(timeout=10, error="Roaming timed out")
+
+    logger.info("Roam back to AP1")
+    for sta in [ dev[1], wpas, dev[0], dev[2] ]:
+        sta.dump_monitor()
+        sta.scan(freq="2412")
+        sta.dump_monitor()
+        sta.request("ROAM " + bssid)
+        sta.wait_connected(timeout=10, error="Roaming timed out")
+        sta.dump_monitor()
+
+    time.sleep(4)
+
+    logger.info("Roam back to AP2")
+    for sta in [ dev[1], wpas, dev[0], dev[2] ]:
+        sta.dump_monitor()
+        sta.scan(freq="2412")
+        sta.dump_monitor()
+        sta.request("ROAM " + bssid2)
+        sta.wait_connected(timeout=10, error="Roaming timed out")
+        sta.dump_monitor()
+
+def test_pmksa_cache_opportunistic_multiple_sta(dev, apdev):
+    """Opportunistic PMKSA caching with multiple stations"""
+    params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+    params['okc'] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    for sta in [ dev[0], dev[1], dev[2], wpas ]:
+        sta.connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                    eap="GPSK", identity="gpsk user",
+                    password="abcdefghijklmnop0123456789abcdef", okc=True,
+                    scan_freq="2412")
+
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    logger.info("Roam to AP2")
+    for sta in [ dev[2], dev[0], wpas, dev[1] ]:
+        sta.dump_monitor()
+        sta.scan_for_bss(bssid2, freq="2412")
+        if "OK" not in sta.request("ROAM " + bssid2):
+            raise Exception("ROAM command failed")
+        ev = sta.wait_event(["CTRL-EVENT-EAP-STARTED",
+                             "CTRL-EVENT-CONNECTED"], timeout=10)
+        if ev is None:
+            raise Exception("Roaming with the AP timed out")
+        if "CTRL-EVENT-EAP-STARTED" in ev:
+            raise Exception("Unexpected EAP exchange")
+        pmksa2 = sta.get_pmksa(bssid2)
+        if pmksa2 is None:
+            raise Exception("No PMKSA cache entry created")
+
+    logger.info("Roam back to AP1")
+    for sta in [ dev[0], dev[1], dev[2], wpas ]:
+        sta.dump_monitor()
+        sta.scan_for_bss(bssid, freq="2412")
+        sta.request("ROAM " + bssid)
+        ev = sta.wait_event(["CTRL-EVENT-EAP-STARTED",
+                             "CTRL-EVENT-CONNECTED"], timeout=10)
+        if ev is None:
+            raise Exception("Roaming with the AP timed out")
+        if "CTRL-EVENT-EAP-STARTED" in ev:
+            raise Exception("Unexpected EAP exchange")
+
+def test_pmksa_cache_preauth_oom(dev, apdev):
+    """RSN pre-authentication to generate PMKSA cache entry and OOM"""
+    try:
+        _test_pmksa_cache_preauth_oom(dev, apdev)
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'])
+        subprocess.call(['brctl', 'delbr', 'ap-br0'])
+
+def _test_pmksa_cache_preauth_oom(dev, apdev):
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params['bridge'] = 'ap-br0'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+    subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+    eap_connect(dev[0], apdev[0], "PAX", "pax.user@example.com",
+                password_hex="0123456789abcdef0123456789abcdef",
+                bssid=apdev[0]['bssid'])
+
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    params['bridge'] = 'ap-br0'
+    params['rsn_preauth'] = '1'
+    params['rsn_preauth_interfaces'] = 'ap-br0'
+    hapd = hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid1 = apdev[1]['bssid']
+
+    tests = [ (1, "rsn_preauth_receive"),
+              (2, "rsn_preauth_receive"),
+              (1, "rsn_preauth_send") ]
+    for test in tests:
+        with alloc_fail(hapd, test[0], test[1]):
+            dev[0].scan_for_bss(bssid1, freq="2412")
+            if "OK" not in dev[0].request("PREAUTH " + bssid1):
+                raise Exception("PREAUTH failed")
+
+            success = False
+            count = 0
+            for i in range(50):
+                time.sleep(0.1)
+                pmksa = dev[0].get_pmksa(bssid1)
+                if pmksa:
+                    success = True
+                    break
+                state = hapd.request('GET_ALLOC_FAIL')
+                if state.startswith('0:'):
+                    count += 1
+                    if count > 2:
+                        break
+            logger.info("PMKSA cache success: " + str(success))
+
+            dev[0].request("PMKSA_FLUSH")
+            dev[0].wait_disconnected()
+            dev[0].wait_connected()
+            dev[0].dump_monitor()
+
+def test_pmksa_cache_size_limit(dev, apdev):
+    """PMKSA cache size limit in wpa_supplicant"""
+    try:
+        _test_pmksa_cache_size_limit(dev, apdev)
+    finally:
+        try:
+            hapd = hostapd.HostapdGlobal()
+            hapd.flush()
+            hapd.remove(apdev[0]['ifname'])
+        except:
+            pass
+        params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+        bssid = apdev[0]['bssid']
+        params['bssid'] = bssid
+        hostapd.add_ap(apdev[0]['ifname'], params)
+
+def _test_pmksa_cache_size_limit(dev, apdev):
+    params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+    id = dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                        eap="GPSK", identity="gpsk user",
+                        password="abcdefghijklmnop0123456789abcdef",
+                        scan_freq="2412", only_add_network=True)
+    for i in range(33):
+        bssid = apdev[0]['bssid'][0:15] + "%02x" % i
+        logger.info("Iteration with BSSID " + bssid)
+        params['bssid'] = bssid
+        hostapd.add_ap(apdev[0]['ifname'], params)
+        dev[0].request("BSS_FLUSH 0")
+        dev[0].scan_for_bss(bssid, freq=2412, only_new=True)
+        dev[0].select_network(id)
+        dev[0].wait_connected()
+        dev[0].request("DISCONNECT")
+        dev[0].wait_disconnected()
+        dev[0].dump_monitor()
+        entries = len(dev[0].request("PMKSA").splitlines()) - 1
+        if i == 32:
+            if entries != 32:
+                raise Exception("Unexpected number of PMKSA entries after expected removal of the oldest entry")
+        elif i + 1 != entries:
+            raise Exception("Unexpected number of PMKSA entries")
+
+        hapd = hostapd.HostapdGlobal()
+        hapd.flush()
+        hapd.remove(apdev[0]['ifname'])
+
+def test_pmksa_cache_preauth_timeout(dev, apdev):
+    """RSN pre-authentication timing out"""
+    try:
+        _test_pmksa_cache_preauth_timeout(dev, apdev)
+    finally:
+        dev[0].request("SET dot11RSNAConfigSATimeout 60")
+
+def _test_pmksa_cache_preauth_timeout(dev, apdev):
+    dev[0].request("SET dot11RSNAConfigSATimeout 1")
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PAX", "pax.user@example.com",
+                password_hex="0123456789abcdef0123456789abcdef",
+                bssid=apdev[0]['bssid'])
+    if "OK" not in dev[0].request("PREAUTH f2:11:22:33:44:55"):
+        raise Exception("PREAUTH failed")
+    ev = dev[0].wait_event(["RSN: pre-authentication with"], timeout=5)
+    if ev is None:
+        raise Exception("No timeout event seen")
+    if "timed out" not in ev:
+        raise Exception("Unexpected event: " + ev)
+
+def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
+    """RSN pre-authentication OOM in wpa_supplicant"""
+    params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    eap_connect(dev[0], apdev[0], "PAX", "pax.user@example.com",
+                password_hex="0123456789abcdef0123456789abcdef",
+                bssid=apdev[0]['bssid'])
+    for i in range(1, 11):
+        with alloc_fail(dev[0], i, "rsn_preauth_init"):
+            res = dev[0].request("PREAUTH f2:11:22:33:44:55").strip()
+            logger.info("Iteration %d - PREAUTH command results: %s" % (i, res))
+            for j in range(10):
+                state = dev[0].request('GET_ALLOC_FAIL')
+                if state.startswith('0:'):
+                    break
+                time.sleep(0.05)
diff --git a/hostap/tests/hwsim/test_radio_work.py b/hostap/tests/hwsim/test_radio_work.py
new file mode 100644
index 0000000..83eb8f4
--- /dev/null
+++ b/hostap/tests/hwsim/test_radio_work.py
@@ -0,0 +1,104 @@
+# Radio work 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.
+
+import time
+import logging
+logger = logging.getLogger()
+import subprocess
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+
+def test_ext_radio_work(dev, apdev):
+    """External radio work item"""
+    id = dev[0].request("RADIO_WORK add test-work-a")
+    if "FAIL" in id:
+        raise Exception("Failed to add radio work")
+    id2 = dev[0].request("RADIO_WORK add test-work-b freq=2417")
+    if "FAIL" in id2:
+        raise Exception("Failed to add radio work")
+    id3 = dev[0].request("RADIO_WORK add test-work-c")
+    if "FAIL" in id3:
+        raise Exception("Failed to add radio work")
+
+    ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to start")
+    if "EXT-RADIO-WORK-START " + id not in ev:
+        raise Exception("Unexpected radio work start id")
+
+    items = dev[0].request("RADIO_WORK show")
+    if "ext:test-work-a@wlan0:0:1:" not in items:
+        logger.info("Pending radio work items:\n" + items)
+        raise Exception("Radio work item(a) missing from the list")
+    if "ext:test-work-b@wlan0:2417:0:" not in items:
+        logger.info("Pending radio work items:\n" + items)
+        raise Exception("Radio work item(b) missing from the list")
+    if "ext:test-work-c@wlan0:0:0:" not in items:
+        logger.info("Pending radio work items:\n" + items)
+        raise Exception("Radio work item(c) missing from the list")
+
+    dev[0].request("RADIO_WORK done " + id2)
+    dev[0].request("RADIO_WORK done " + id)
+
+    ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to start")
+    if "EXT-RADIO-WORK-START " + id3 not in ev:
+        raise Exception("Unexpected radio work start id")
+    dev[0].request("RADIO_WORK done " + id3)
+    items = dev[0].request("RADIO_WORK show")
+    if "ext:" in items:
+        logger.info("Pending radio work items:\n" + items)
+        raise Exception("Unexpected remaining radio work item")
+
+    id = dev[0].request("RADIO_WORK add test-work timeout=1")
+    ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to start")
+    ev = dev[0].wait_event(["EXT-RADIO-WORK-TIMEOUT"], timeout=2)
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to time out")
+    if id not in ev:
+        raise Exception("Radio work id mismatch")
+
+    for i in range(5):
+        dev[0].request(("RADIO_WORK add test-work-%d-" % i) + 100*'a')
+    ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to start")
+    if "FAIL" not in dev[0].request("RADIO_WORK done 12345678"):
+        raise Exception("Invalid RADIO_WORK done accepted");
+    if "FAIL" not in dev[0].request("RADIO_WORK foo"):
+        raise Exception("Invalid RADIO_WORK accepted");
+    dev[0].request("FLUSH")
+    items = dev[0].request("RADIO_WORK show")
+    if items != "":
+        raise Exception("Unexpected radio work remaining after FLUSH: " + items)
+
+def test_radio_work_cancel(dev, apdev):
+    """Radio work items cancelled on interface removal"""
+    params = hostapd.wpa2_params(ssid="radio", passphrase="12345678")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    wpas.scan(freq="2412")
+
+    id = wpas.request("RADIO_WORK add test-work-a")
+    if "FAIL" in id:
+        raise Exception("Failed to add radio work")
+    ev = wpas.wait_event(["EXT-RADIO-WORK-START"])
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to start")
+    if "EXT-RADIO-WORK-START " + id not in ev:
+        raise Exception("Unexpected radio work start id")
+
+    wpas.connect("radio", psk="12345678", scan_freq="2412",
+                   wait_connect=False)
+    time.sleep(1)
+    wpas.interface_remove("wlan5")
+    # add to allow log file renaming
+    wpas.interface_add("wlan5")
diff --git a/hostap/tests/hwsim/test_radius.py b/hostap/tests/hwsim/test_radius.py
new file mode 100644
index 0000000..662285c
--- /dev/null
+++ b/hostap/tests/hwsim/test_radius.py
@@ -0,0 +1,1121 @@
+# RADIUS tests
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import hashlib
+import hmac
+import logging
+logger = logging.getLogger()
+import os
+import select
+import struct
+import subprocess
+import threading
+import time
+
+import hostapd
+from utils import HwsimSkip, require_under_vm
+
+def connect(dev, ssid, wait_connect=True):
+    dev.connect(ssid, key_mgmt="WPA-EAP", scan_freq="2412",
+                eap="PSK", identity="psk.user@example.com",
+                password_hex="0123456789abcdef0123456789abcdef",
+                wait_connect=wait_connect)
+
+def test_radius_auth_unreachable(dev, apdev):
+    """RADIUS Authentication server unreachable"""
+    params = hostapd.wpa2_eap_params(ssid="radius-auth")
+    params['auth_server_port'] = "18139"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    connect(dev[0], "radius-auth", wait_connect=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"])
+    if ev is None:
+        raise Exception("Timeout on EAP start")
+    logger.info("Checking for RADIUS retries")
+    time.sleep(4)
+    mib = hapd.get_mib()
+    if "radiusAuthClientAccessRequests" not in mib:
+        raise Exception("Missing MIB fields")
+    if int(mib["radiusAuthClientAccessRetransmissions"]) < 1:
+        raise Exception("Missing RADIUS Authentication retransmission")
+    if int(mib["radiusAuthClientPendingRequests"]) < 1:
+        raise Exception("Missing pending RADIUS Authentication request")
+
+def test_radius_auth_unreachable2(dev, apdev):
+    """RADIUS Authentication server unreachable (2)"""
+    subprocess.call(['ip', 'ro', 'replace', '192.168.213.17', 'dev', 'lo'])
+    params = hostapd.wpa2_eap_params(ssid="radius-auth")
+    params['auth_server_addr'] = "192.168.213.17"
+    params['auth_server_port'] = "18139"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    subprocess.call(['ip', 'ro', 'del', '192.168.213.17', 'dev', 'lo'])
+    connect(dev[0], "radius-auth", wait_connect=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"])
+    if ev is None:
+        raise Exception("Timeout on EAP start")
+    logger.info("Checking for RADIUS retries")
+    time.sleep(4)
+    mib = hapd.get_mib()
+    if "radiusAuthClientAccessRequests" not in mib:
+        raise Exception("Missing MIB fields")
+    if int(mib["radiusAuthClientAccessRetransmissions"]) < 1:
+        raise Exception("Missing RADIUS Authentication retransmission")
+
+def test_radius_auth_unreachable3(dev, apdev):
+    """RADIUS Authentication server initially unreachable, but then available"""
+    subprocess.call(['ip', 'ro', 'replace', 'blackhole', '192.168.213.18'])
+    params = hostapd.wpa2_eap_params(ssid="radius-auth")
+    params['auth_server_addr'] = "192.168.213.18"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    connect(dev[0], "radius-auth", wait_connect=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"])
+    if ev is None:
+        raise Exception("Timeout on EAP start")
+    subprocess.call(['ip', 'ro', 'del', 'blackhole', '192.168.213.18'])
+    time.sleep(0.1)
+    dev[0].request("DISCONNECT")
+    hapd.set('auth_server_addr_replace', '127.0.0.1')
+    dev[0].request("RECONNECT")
+
+    dev[0].wait_connected()
+
+def test_radius_acct_unreachable(dev, apdev):
+    """RADIUS Accounting server unreachable"""
+    params = hostapd.wpa2_eap_params(ssid="radius-acct")
+    params['acct_server_addr'] = "127.0.0.1"
+    params['acct_server_port'] = "18139"
+    params['acct_server_shared_secret'] = "radius"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    connect(dev[0], "radius-acct")
+    logger.info("Checking for RADIUS retries")
+    time.sleep(4)
+    mib = hapd.get_mib()
+    if "radiusAccClientRetransmissions" not in mib:
+        raise Exception("Missing MIB fields")
+    if int(mib["radiusAccClientRetransmissions"]) < 2:
+        raise Exception("Missing RADIUS Accounting retransmissions")
+    if int(mib["radiusAccClientPendingRequests"]) < 2:
+        raise Exception("Missing pending RADIUS Accounting requests")
+
+def test_radius_acct_unreachable2(dev, apdev):
+    """RADIUS Accounting server unreachable(2)"""
+    subprocess.call(['ip', 'ro', 'replace', '192.168.213.17', 'dev', 'lo'])
+    params = hostapd.wpa2_eap_params(ssid="radius-acct")
+    params['acct_server_addr'] = "192.168.213.17"
+    params['acct_server_port'] = "18139"
+    params['acct_server_shared_secret'] = "radius"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    subprocess.call(['ip', 'ro', 'del', '192.168.213.17', 'dev', 'lo'])
+    connect(dev[0], "radius-acct")
+    logger.info("Checking for RADIUS retries")
+    time.sleep(4)
+    mib = hapd.get_mib()
+    if "radiusAccClientRetransmissions" not in mib:
+        raise Exception("Missing MIB fields")
+    if int(mib["radiusAccClientRetransmissions"]) < 1 and int(mib["radiusAccClientPendingRequests"]) < 1:
+        raise Exception("Missing pending or retransmitted RADIUS Accounting requests")
+
+def test_radius_acct_unreachable3(dev, apdev):
+    """RADIUS Accounting server initially unreachable, but then available"""
+    require_under_vm()
+    subprocess.call(['ip', 'ro', 'replace', 'blackhole', '192.168.213.18'])
+    as_hapd = hostapd.Hostapd("as")
+    as_mib_start = as_hapd.get_mib(param="radius_server")
+    params = hostapd.wpa2_eap_params(ssid="radius-acct")
+    params['acct_server_addr'] = "192.168.213.18"
+    params['acct_server_port'] = "1813"
+    params['acct_server_shared_secret'] = "radius"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    connect(dev[0], "radius-acct")
+    subprocess.call(['ip', 'ro', 'del', 'blackhole', '192.168.213.18'])
+    time.sleep(0.1)
+    dev[0].request("DISCONNECT")
+    hapd.set('acct_server_addr_replace', '127.0.0.1')
+    dev[0].request("RECONNECT")
+    dev[0].wait_connected()
+    time.sleep(1)
+    as_mib_end = as_hapd.get_mib(param="radius_server")
+    req_s = int(as_mib_start['radiusAccServTotalResponses'])
+    req_e = int(as_mib_end['radiusAccServTotalResponses'])
+    if req_e <= req_s:
+        raise Exception("Unexpected RADIUS server acct MIB value")
+
+def test_radius_acct_unreachable4(dev, apdev):
+    """RADIUS Accounting server unreachable and multiple STAs"""
+    params = hostapd.wpa2_eap_params(ssid="radius-acct")
+    params['acct_server_addr'] = "127.0.0.1"
+    params['acct_server_port'] = "18139"
+    params['acct_server_shared_secret'] = "radius"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    for i in range(20):
+        connect(dev[0], "radius-acct")
+        dev[0].request("REMOVE_NETWORK all")
+        dev[0].wait_disconnected()
+
+def test_radius_acct(dev, apdev):
+    """RADIUS Accounting"""
+    as_hapd = hostapd.Hostapd("as")
+    as_mib_start = as_hapd.get_mib(param="radius_server")
+    params = hostapd.wpa2_eap_params(ssid="radius-acct")
+    params['acct_server_addr'] = "127.0.0.1"
+    params['acct_server_port'] = "1813"
+    params['acct_server_shared_secret'] = "radius"
+    params['radius_auth_req_attr'] = [ "126:s:Operator", "77:s:testing" ]
+    params['radius_acct_req_attr'] = [ "126:s:Operator", "77:s:testing" ]
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    connect(dev[0], "radius-acct")
+    dev[1].connect("radius-acct", key_mgmt="WPA-EAP", scan_freq="2412",
+                   eap="PAX", identity="test-class",
+                   password_hex="0123456789abcdef0123456789abcdef")
+    dev[2].connect("radius-acct", key_mgmt="WPA-EAP",
+                   eap="GPSK", identity="gpsk-cui",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+    logger.info("Checking for RADIUS counters")
+    count = 0
+    while True:
+        mib = hapd.get_mib()
+        if int(mib['radiusAccClientResponses']) >= 3:
+            break
+        time.sleep(0.1)
+        count += 1
+        if count > 10:
+            raise Exception("Did not receive Accounting-Response packets")
+
+    if int(mib['radiusAccClientRetransmissions']) > 0:
+        raise Exception("Unexpected Accounting-Request retransmission")
+
+    as_mib_end = as_hapd.get_mib(param="radius_server")
+
+    req_s = int(as_mib_start['radiusAccServTotalRequests'])
+    req_e = int(as_mib_end['radiusAccServTotalRequests'])
+    if req_e < req_s + 2:
+        raise Exception("Unexpected RADIUS server acct MIB value")
+
+    acc_s = int(as_mib_start['radiusAuthServAccessAccepts'])
+    acc_e = int(as_mib_end['radiusAuthServAccessAccepts'])
+    if acc_e < acc_s + 1:
+        raise Exception("Unexpected RADIUS server auth MIB value")
+
+def test_radius_acct_pmksa_caching(dev, apdev):
+    """RADIUS Accounting with PMKSA caching"""
+    as_hapd = hostapd.Hostapd("as")
+    as_mib_start = as_hapd.get_mib(param="radius_server")
+    params = hostapd.wpa2_eap_params(ssid="radius-acct")
+    params['acct_server_addr'] = "127.0.0.1"
+    params['acct_server_port'] = "1813"
+    params['acct_server_shared_secret'] = "radius"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    connect(dev[0], "radius-acct")
+    dev[1].connect("radius-acct", key_mgmt="WPA-EAP", scan_freq="2412",
+                   eap="PAX", identity="test-class",
+                   password_hex="0123456789abcdef0123456789abcdef")
+    for d in [ dev[0], dev[1] ]:
+        d.request("REASSOCIATE")
+        d.wait_connected(timeout=15, error="Reassociation timed out")
+
+    count = 0
+    while True:
+        mib = hapd.get_mib()
+        if int(mib['radiusAccClientResponses']) >= 4:
+            break
+        time.sleep(0.1)
+        count += 1
+        if count > 10:
+            raise Exception("Did not receive Accounting-Response packets")
+
+    if int(mib['radiusAccClientRetransmissions']) > 0:
+        raise Exception("Unexpected Accounting-Request retransmission")
+
+    as_mib_end = as_hapd.get_mib(param="radius_server")
+
+    req_s = int(as_mib_start['radiusAccServTotalRequests'])
+    req_e = int(as_mib_end['radiusAccServTotalRequests'])
+    if req_e < req_s + 2:
+        raise Exception("Unexpected RADIUS server acct MIB value")
+
+    acc_s = int(as_mib_start['radiusAuthServAccessAccepts'])
+    acc_e = int(as_mib_end['radiusAuthServAccessAccepts'])
+    if acc_e < acc_s + 1:
+        raise Exception("Unexpected RADIUS server auth MIB value")
+
+def test_radius_acct_interim(dev, apdev):
+    """RADIUS Accounting interim update"""
+    as_hapd = hostapd.Hostapd("as")
+    params = hostapd.wpa2_eap_params(ssid="radius-acct")
+    params['acct_server_addr'] = "127.0.0.1"
+    params['acct_server_port'] = "1813"
+    params['acct_server_shared_secret'] = "radius"
+    params['radius_acct_interim_interval'] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    connect(dev[0], "radius-acct")
+    logger.info("Checking for RADIUS counters")
+    as_mib_start = as_hapd.get_mib(param="radius_server")
+    time.sleep(3.1)
+    as_mib_end = as_hapd.get_mib(param="radius_server")
+    req_s = int(as_mib_start['radiusAccServTotalRequests'])
+    req_e = int(as_mib_end['radiusAccServTotalRequests'])
+    if req_e < req_s + 3:
+        raise Exception("Unexpected RADIUS server acct MIB value")
+
+def test_radius_acct_interim_unreachable(dev, apdev):
+    """RADIUS Accounting interim update with unreachable server"""
+    params = hostapd.wpa2_eap_params(ssid="radius-acct")
+    params['acct_server_addr'] = "127.0.0.1"
+    params['acct_server_port'] = "18139"
+    params['acct_server_shared_secret'] = "radius"
+    params['radius_acct_interim_interval'] = "1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    start = hapd.get_mib()
+    connect(dev[0], "radius-acct")
+    logger.info("Waiting for interium accounting updates")
+    time.sleep(3.1)
+    end = hapd.get_mib()
+    req_s = int(start['radiusAccClientTimeouts'])
+    req_e = int(end['radiusAccClientTimeouts'])
+    if req_e < req_s + 2:
+        raise Exception("Unexpected RADIUS server acct MIB value")
+
+def send_and_check_reply(srv, req, code, error_cause=0):
+    reply = srv.SendPacket(req)
+    logger.debug("RADIUS response from hostapd")
+    for i in reply.keys():
+        logger.debug("%s: %s" % (i, reply[i]))
+    if reply.code != code:
+        raise Exception("Unexpected response code")
+    if error_cause:
+        if 'Error-Cause' not in reply:
+            raise Exception("Missing Error-Cause")
+            if reply['Error-Cause'][0] != error_cause:
+                raise Exception("Unexpected Error-Cause: {}".format(reply['Error-Cause']))
+
+def test_radius_das_disconnect(dev, apdev):
+    """RADIUS Dynamic Authorization Extensions - Disconnect"""
+    try:
+        import pyrad.client
+        import pyrad.packet
+        import pyrad.dictionary
+        import radius_das
+    except ImportError:
+        raise HwsimSkip("No pyrad modules available")
+
+    params = hostapd.wpa2_eap_params(ssid="radius-das")
+    params['radius_das_port'] = "3799"
+    params['radius_das_client'] = "127.0.0.1 secret"
+    params['radius_das_require_event_timestamp'] = "1"
+    params['own_ip_addr'] = "127.0.0.1"
+    params['nas_identifier'] = "nas.example.com"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    connect(dev[0], "radius-das")
+    addr = dev[0].p2p_interface_addr()
+    sta = hapd.get_sta(addr)
+    id = sta['dot1xAuthSessionId']
+
+    dict = pyrad.dictionary.Dictionary("dictionary.radius")
+
+    srv = pyrad.client.Client(server="127.0.0.1", acctport=3799,
+                              secret="secret", dict=dict)
+    srv.retries = 1
+    srv.timeout = 1
+
+    logger.info("Disconnect-Request with incorrect secret")
+    req = radius_das.DisconnectPacket(dict=dict, secret="incorrect",
+                                      User_Name="foo",
+                                      NAS_Identifier="localhost",
+                                      Event_Timestamp=int(time.time()))
+    logger.debug(req)
+    try:
+        reply = srv.SendPacket(req)
+        raise Exception("Unexpected response to Disconnect-Request")
+    except pyrad.client.Timeout:
+        logger.info("Disconnect-Request with incorrect secret properly ignored")
+
+    logger.info("Disconnect-Request without Event-Timestamp")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      User_Name="psk.user@example.com")
+    logger.debug(req)
+    try:
+        reply = srv.SendPacket(req)
+        raise Exception("Unexpected response to Disconnect-Request")
+    except pyrad.client.Timeout:
+        logger.info("Disconnect-Request without Event-Timestamp properly ignored")
+
+    logger.info("Disconnect-Request with non-matching Event-Timestamp")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      User_Name="psk.user@example.com",
+                                      Event_Timestamp=123456789)
+    logger.debug(req)
+    try:
+        reply = srv.SendPacket(req)
+        raise Exception("Unexpected response to Disconnect-Request")
+    except pyrad.client.Timeout:
+        logger.info("Disconnect-Request with non-matching Event-Timestamp properly ignored")
+
+    logger.info("Disconnect-Request with unsupported attribute")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      User_Name="foo",
+                                      User_Password="foo",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 401)
+
+    logger.info("Disconnect-Request with invalid Calling-Station-Id")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      User_Name="foo",
+                                      Calling_Station_Id="foo",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 407)
+
+    logger.info("Disconnect-Request with mismatching User-Name")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      User_Name="foo",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
+
+    logger.info("Disconnect-Request with mismatching Calling-Station-Id")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      Calling_Station_Id="12:34:56:78:90:aa",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
+
+    logger.info("Disconnect-Request with mismatching Acct-Session-Id")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      Acct_Session_Id="12345678-87654321",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
+
+    logger.info("Disconnect-Request with mismatching Acct-Session-Id (len)")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      Acct_Session_Id="12345678",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
+
+    logger.info("Disconnect-Request with mismatching Acct-Multi-Session-Id")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      Acct_Multi_Session_Id="12345678+87654321",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
+
+    logger.info("Disconnect-Request with mismatching Acct-Multi-Session-Id (len)")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      Acct_Multi_Session_Id="12345678",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
+
+    logger.info("Disconnect-Request with no session identification attributes")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
+
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected disconnection")
+
+    logger.info("Disconnect-Request with mismatching NAS-IP-Address")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      NAS_IP_Address="192.168.3.4",
+                                      Acct_Session_Id=id,
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 403)
+
+    logger.info("Disconnect-Request with mismatching NAS-Identifier")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      NAS_Identifier="unknown.example.com",
+                                      Acct_Session_Id=id,
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 403)
+
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected disconnection")
+
+    logger.info("Disconnect-Request with matching Acct-Session-Id")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      NAS_IP_Address="127.0.0.1",
+                                      NAS_Identifier="nas.example.com",
+                                      Acct_Session_Id=id,
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+    dev[0].wait_disconnected(timeout=10)
+    dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+    logger.info("Disconnect-Request with matching Acct-Multi-Session-Id")
+    sta = hapd.get_sta(addr)
+    multi_sess_id = sta['authMultiSessionId']
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      NAS_IP_Address="127.0.0.1",
+                                      NAS_Identifier="nas.example.com",
+                                      Acct_Multi_Session_Id=multi_sess_id,
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+    dev[0].wait_disconnected(timeout=10)
+    dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+    logger.info("Disconnect-Request with matching User-Name")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      NAS_Identifier="nas.example.com",
+                                      User_Name="psk.user@example.com",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+    dev[0].wait_disconnected(timeout=10)
+    dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+    logger.info("Disconnect-Request with matching Calling-Station-Id")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      NAS_IP_Address="127.0.0.1",
+                                      Calling_Station_Id=addr,
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+    dev[0].wait_disconnected(timeout=10)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED", "CTRL-EVENT-CONNECTED"])
+    if ev is None:
+        raise Exception("Timeout while waiting for re-connection")
+    if "CTRL-EVENT-EAP-STARTED" not in ev:
+        raise Exception("Unexpected skipping of EAP authentication in reconnection")
+    dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+    logger.info("Disconnect-Request with matching Calling-Station-Id and non-matching CUI")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      Calling_Station_Id=addr,
+                                      Chargeable_User_Identity="foo@example.com",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, error_cause=503)
+
+    logger.info("Disconnect-Request with matching CUI")
+    dev[1].connect("radius-das", key_mgmt="WPA-EAP",
+                   eap="GPSK", identity="gpsk-cui",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      Chargeable_User_Identity="gpsk-chargeable-user-identity",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+    dev[1].wait_disconnected(timeout=10)
+    dev[1].wait_connected(timeout=10, error="Re-connection timed out")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected disconnection")
+
+    connect(dev[2], "radius-das")
+
+    logger.info("Disconnect-Request with matching User-Name - multiple sessions matching")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      NAS_Identifier="nas.example.com",
+                                      User_Name="psk.user@example.com",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, error_cause=508)
+
+    logger.info("Disconnect-Request with User-Name matching multiple sessions, Calling-Station-Id only one")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      NAS_Identifier="nas.example.com",
+                                      Calling_Station_Id=addr,
+                                      User_Name="psk.user@example.com",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+    dev[0].wait_disconnected(timeout=10)
+    dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+    ev = dev[2].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected disconnection")
+
+    logger.info("Disconnect-Request with matching Acct-Multi-Session-Id after disassociation")
+    sta = hapd.get_sta(addr)
+    multi_sess_id = sta['authMultiSessionId']
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=10)
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      NAS_IP_Address="127.0.0.1",
+                                      NAS_Identifier="nas.example.com",
+                                      Acct_Multi_Session_Id=multi_sess_id,
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+    dev[0].request("RECONNECT")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on EAP start")
+    dev[0].wait_connected(timeout=15)
+
+    logger.info("Disconnect-Request with matching User-Name after disassociation")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=10)
+    dev[2].request("DISCONNECT")
+    dev[2].wait_disconnected(timeout=10)
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      NAS_IP_Address="127.0.0.1",
+                                      NAS_Identifier="nas.example.com",
+                                      User_Name="psk.user@example.com",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+    logger.info("Disconnect-Request with matching CUI after disassociation")
+    dev[1].request("DISCONNECT")
+    dev[1].wait_disconnected(timeout=10)
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      NAS_IP_Address="127.0.0.1",
+                                      NAS_Identifier="nas.example.com",
+                                      Chargeable_User_Identity="gpsk-chargeable-user-identity",
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+    logger.info("Disconnect-Request with matching Calling-Station-Id after disassociation")
+    dev[0].request("RECONNECT")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=15)
+    if ev is None:
+        raise Exception("Timeout on EAP start")
+    dev[0].wait_connected(timeout=15)
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=10)
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      NAS_IP_Address="127.0.0.1",
+                                      NAS_Identifier="nas.example.com",
+                                      Calling_Station_Id=addr,
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+    logger.info("Disconnect-Request with mismatching Calling-Station-Id after disassociation")
+    req = radius_das.DisconnectPacket(dict=dict, secret="secret",
+                                      NAS_IP_Address="127.0.0.1",
+                                      NAS_Identifier="nas.example.com",
+                                      Calling_Station_Id=addr,
+                                      Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, error_cause=503)
+
+def test_radius_das_coa(dev, apdev):
+    """RADIUS Dynamic Authorization Extensions - CoA"""
+    try:
+        import pyrad.client
+        import pyrad.packet
+        import pyrad.dictionary
+        import radius_das
+    except ImportError:
+        raise HwsimSkip("No pyrad modules available")
+
+    params = hostapd.wpa2_eap_params(ssid="radius-das")
+    params['radius_das_port'] = "3799"
+    params['radius_das_client'] = "127.0.0.1 secret"
+    params['radius_das_require_event_timestamp'] = "1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    connect(dev[0], "radius-das")
+    addr = dev[0].p2p_interface_addr()
+    sta = hapd.get_sta(addr)
+    id = sta['dot1xAuthSessionId']
+
+    dict = pyrad.dictionary.Dictionary("dictionary.radius")
+
+    srv = pyrad.client.Client(server="127.0.0.1", acctport=3799,
+                              secret="secret", dict=dict)
+    srv.retries = 1
+    srv.timeout = 1
+
+    # hostapd does not currently support CoA-Request, so NAK is expected
+    logger.info("CoA-Request with matching Acct-Session-Id")
+    req = radius_das.CoAPacket(dict=dict, secret="secret",
+                               Acct_Session_Id=id,
+                               Event_Timestamp=int(time.time()))
+    send_and_check_reply(srv, req, pyrad.packet.CoANAK, error_cause=405)
+
+def test_radius_ipv6(dev, apdev):
+    """RADIUS connection over IPv6"""
+    params = {}
+    params['ssid'] = 'as'
+    params['beacon_int'] = '2000'
+    params['radius_server_clients'] = 'auth_serv/radius_clients_ipv6.conf'
+    params['radius_server_ipv6'] = '1'
+    params['radius_server_auth_port'] = '18129'
+    params['radius_server_acct_port'] = '18139'
+    params['eap_server'] = '1'
+    params['eap_user_file'] = 'auth_serv/eap_user.conf'
+    params['ca_cert'] = 'auth_serv/ca.pem'
+    params['server_cert'] = 'auth_serv/server.pem'
+    params['private_key'] = 'auth_serv/server.key'
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    params = hostapd.wpa2_eap_params(ssid="radius-ipv6")
+    params['auth_server_addr'] = "::0"
+    params['auth_server_port'] = "18129"
+    params['acct_server_addr'] = "::0"
+    params['acct_server_port'] = "18139"
+    params['acct_server_shared_secret'] = "radius"
+    params['own_ip_addr'] = "::0"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    connect(dev[0], "radius-ipv6")
+
+def test_radius_macacl(dev, apdev):
+    """RADIUS MAC ACL"""
+    params = hostapd.radius_params()
+    params["ssid"] = "radius"
+    params["macaddr_acl"] = "2"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("radius", key_mgmt="NONE", scan_freq="2412")
+
+def test_radius_macacl_acct(dev, apdev):
+    """RADIUS MAC ACL and accounting enabled"""
+    params = hostapd.radius_params()
+    params["ssid"] = "radius"
+    params["macaddr_acl"] = "2"
+    params['acct_server_addr'] = "127.0.0.1"
+    params['acct_server_port'] = "1813"
+    params['acct_server_shared_secret'] = "radius"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("radius", key_mgmt="NONE", scan_freq="2412")
+    dev[1].connect("radius", key_mgmt="NONE", scan_freq="2412")
+    dev[1].request("DISCONNECT")
+    dev[1].wait_disconnected()
+    dev[1].request("RECONNECT")
+
+def test_radius_failover(dev, apdev):
+    """RADIUS Authentication and Accounting server failover"""
+    subprocess.call(['ip', 'ro', 'replace', '192.168.213.17', 'dev', 'lo'])
+    as_hapd = hostapd.Hostapd("as")
+    as_mib_start = as_hapd.get_mib(param="radius_server")
+    params = hostapd.wpa2_eap_params(ssid="radius-failover")
+    params["auth_server_addr"] = "192.168.213.17"
+    params["auth_server_port"] = "1812"
+    params["auth_server_shared_secret"] = "testing"
+    params['acct_server_addr'] = "192.168.213.17"
+    params['acct_server_port'] = "1813"
+    params['acct_server_shared_secret'] = "testing"
+    params['radius_retry_primary_interval'] = "20"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, no_enable=True)
+    hapd.set("auth_server_addr", "127.0.0.1")
+    hapd.set("auth_server_port", "1812")
+    hapd.set("auth_server_shared_secret", "radius")
+    hapd.set('acct_server_addr', "127.0.0.1")
+    hapd.set('acct_server_port', "1813")
+    hapd.set('acct_server_shared_secret', "radius")
+    hapd.enable()
+    ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=30)
+    if ev is None:
+        raise Exception("AP startup timed out")
+        if "AP-ENABLED" not in ev:
+            raise Exception("AP startup failed")
+    start = os.times()[4]
+
+    try:
+        subprocess.call(['ip', 'ro', 'replace', 'prohibit', '192.168.213.17'])
+        dev[0].request("SET EAPOL::authPeriod 5")
+        connect(dev[0], "radius-failover", wait_connect=False)
+        dev[0].wait_connected(timeout=20)
+    finally:
+        dev[0].request("SET EAPOL::authPeriod 30")
+        subprocess.call(['ip', 'ro', 'del', '192.168.213.17'])
+
+    as_mib_end = as_hapd.get_mib(param="radius_server")
+    req_s = int(as_mib_start['radiusAccServTotalRequests'])
+    req_e = int(as_mib_end['radiusAccServTotalRequests'])
+    if req_e <= req_s:
+        raise Exception("Unexpected RADIUS server acct MIB value")
+
+    end = os.times()[4]
+    try:
+        subprocess.call(['ip', 'ro', 'replace', 'prohibit', '192.168.213.17'])
+        dev[1].request("SET EAPOL::authPeriod 5")
+        if end - start < 21:
+            time.sleep(21 - (end - start))
+        connect(dev[1], "radius-failover", wait_connect=False)
+        dev[1].wait_connected(timeout=20)
+    finally:
+        dev[1].request("SET EAPOL::authPeriod 30")
+        subprocess.call(['ip', 'ro', 'del', '192.168.213.17'])
+
+def run_pyrad_server(srv, t_events):
+    srv.RunWithStop(t_events)
+
+def test_radius_protocol(dev, apdev):
+    """RADIUS Authentication protocol tests with a fake server"""
+    try:
+        import pyrad.server
+        import pyrad.packet
+        import pyrad.dictionary
+    except ImportError:
+        raise HwsimSkip("No pyrad modules available")
+
+    class TestServer(pyrad.server.Server):
+        def _HandleAuthPacket(self, pkt):
+            pyrad.server.Server._HandleAuthPacket(self, pkt)
+            logger.info("Received authentication request")
+            reply = self.CreateReplyPacket(pkt)
+            reply.code = pyrad.packet.AccessAccept
+            if self.t_events['msg_auth'].is_set():
+                logger.info("Add Message-Authenticator")
+                if self.t_events['wrong_secret'].is_set():
+                    logger.info("Use incorrect RADIUS shared secret")
+                    pw = "incorrect"
+                else:
+                    pw = reply.secret
+                hmac_obj = hmac.new(pw)
+                hmac_obj.update(struct.pack("B", reply.code))
+                hmac_obj.update(struct.pack("B", reply.id))
+
+                # reply attributes
+                reply.AddAttribute("Message-Authenticator",
+                                   "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
+                attrs = reply._PktEncodeAttributes()
+
+                # Length
+                flen = 4 + 16 + len(attrs)
+                hmac_obj.update(struct.pack(">H", flen))
+                hmac_obj.update(pkt.authenticator)
+                hmac_obj.update(attrs)
+                if self.t_events['double_msg_auth'].is_set():
+                    logger.info("Include two Message-Authenticator attributes")
+                else:
+                    del reply[80]
+                reply.AddAttribute("Message-Authenticator", hmac_obj.digest())
+            self.SendReplyPacket(pkt.fd, reply)
+
+        def RunWithStop(self, t_events):
+            self._poll = select.poll()
+            self._fdmap = {}
+            self._PrepareSockets()
+            self.t_events = t_events
+
+            while not t_events['stop'].is_set():
+                for (fd, event) in self._poll.poll(1000):
+                    if event == select.POLLIN:
+                        try:
+                            fdo = self._fdmap[fd]
+                            self._ProcessInput(fdo)
+                        except ServerPacketError as err:
+                            logger.info("pyrad server dropping packet: " + str(err))
+                        except pyrad.packet.PacketError as err:
+                            logger.info("pyrad server received invalid packet: " + str(err))
+                    else:
+                        logger.error("Unexpected event in pyrad server main loop")
+
+    srv = TestServer(dict=pyrad.dictionary.Dictionary("dictionary.radius"),
+                     authport=18138, acctport=18139)
+    srv.hosts["127.0.0.1"] = pyrad.server.RemoteHost("127.0.0.1",
+                                                     "radius",
+                                                     "localhost")
+    srv.BindToAddress("")
+    t_events = {}
+    t_events['stop'] = threading.Event()
+    t_events['msg_auth'] = threading.Event()
+    t_events['wrong_secret'] = threading.Event()
+    t_events['double_msg_auth'] = threading.Event()
+    t = threading.Thread(target=run_pyrad_server, args=(srv, t_events))
+    t.start()
+
+    try:
+        params = hostapd.wpa2_eap_params(ssid="radius-test")
+        params['auth_server_port'] = "18138"
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        connect(dev[0], "radius-test", wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=15)
+        if ev is None:
+            raise Exception("Timeout on EAP start")
+        time.sleep(1)
+        dev[0].request("REMOVE_NETWORK all")
+        time.sleep(0.1)
+        dev[0].dump_monitor()
+        t_events['msg_auth'].set()
+        t_events['wrong_secret'].set()
+        connect(dev[0], "radius-test", wait_connect=False)
+        time.sleep(1)
+        dev[0].request("REMOVE_NETWORK all")
+        time.sleep(0.1)
+        dev[0].dump_monitor()
+        t_events['wrong_secret'].clear()
+        connect(dev[0], "radius-test", wait_connect=False)
+        time.sleep(1)
+        dev[0].request("REMOVE_NETWORK all")
+        time.sleep(0.1)
+        dev[0].dump_monitor()
+        t_events['double_msg_auth'].set()
+        connect(dev[0], "radius-test", wait_connect=False)
+        time.sleep(1)
+    finally:
+        t_events['stop'].set()
+        t.join()
+
+def test_radius_psk(dev, apdev):
+    """WPA2 with PSK from RADIUS"""
+    try:
+        import pyrad.server
+        import pyrad.packet
+        import pyrad.dictionary
+    except ImportError:
+        raise HwsimSkip("No pyrad modules available")
+
+    class TestServer(pyrad.server.Server):
+        def _HandleAuthPacket(self, pkt):
+            pyrad.server.Server._HandleAuthPacket(self, pkt)
+            logger.info("Received authentication request")
+            reply = self.CreateReplyPacket(pkt)
+            reply.code = pyrad.packet.AccessAccept
+            a = "\xab\xcd"
+            secret = reply.secret
+            if self.t_events['long'].is_set():
+                p = b'\x10' + "0123456789abcdef" + 15 * b'\x00'
+                b = hashlib.md5(secret + pkt.authenticator + a).digest()
+                pp = bytearray(p[0:16])
+                bb = bytearray(b)
+                cc = bytearray(pp[i] ^ bb[i] for i in range(len(bb)))
+
+                b = hashlib.md5(reply.secret + bytes(cc)).digest()
+                pp = bytearray(p[16:32])
+                bb = bytearray(b)
+                cc += bytearray(pp[i] ^ bb[i] for i in range(len(bb)))
+
+                data = '\x00' + a + bytes(cc)
+            else:
+                p = b'\x08' + "12345678" + 7 * b'\x00'
+                b = hashlib.md5(secret + pkt.authenticator + a).digest()
+                pp = bytearray(p)
+                bb = bytearray(b)
+                cc = bytearray(pp[i] ^ bb[i] for i in range(len(bb)))
+                data = '\x00' + a + bytes(cc)
+            reply.AddAttribute("Tunnel-Password", data)
+            self.SendReplyPacket(pkt.fd, reply)
+
+        def RunWithStop(self, t_events):
+            self._poll = select.poll()
+            self._fdmap = {}
+            self._PrepareSockets()
+            self.t_events = t_events
+
+            while not t_events['stop'].is_set():
+                for (fd, event) in self._poll.poll(1000):
+                    if event == select.POLLIN:
+                        try:
+                            fdo = self._fdmap[fd]
+                            self._ProcessInput(fdo)
+                        except ServerPacketError as err:
+                            logger.info("pyrad server dropping packet: " + str(err))
+                        except pyrad.packet.PacketError as err:
+                            logger.info("pyrad server received invalid packet: " + str(err))
+                    else:
+                        logger.error("Unexpected event in pyrad server main loop")
+
+    srv = TestServer(dict=pyrad.dictionary.Dictionary("dictionary.radius"),
+                     authport=18138, acctport=18139)
+    srv.hosts["127.0.0.1"] = pyrad.server.RemoteHost("127.0.0.1",
+                                                     "radius",
+                                                     "localhost")
+    srv.BindToAddress("")
+    t_events = {}
+    t_events['stop'] = threading.Event()
+    t_events['long'] = threading.Event()
+    t = threading.Thread(target=run_pyrad_server, args=(srv, t_events))
+    t.start()
+
+    try:
+        ssid = "test-wpa2-psk"
+        params = hostapd.radius_params()
+        params['ssid'] = ssid
+        params["wpa"] = "2"
+        params["wpa_key_mgmt"] = "WPA-PSK"
+        params["rsn_pairwise"] = "CCMP"
+        params['macaddr_acl'] = '2'
+        params['wpa_psk_radius'] = '2'
+        params['auth_server_port'] = "18138"
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        dev[0].connect(ssid, psk="12345678", scan_freq="2412")
+        t_events['long'].set()
+        dev[1].connect(ssid, psk="0123456789abcdef", scan_freq="2412")
+    finally:
+        t_events['stop'].set()
+        t.join()
+
+def test_radius_auth_force_client_addr(dev, apdev):
+    """RADIUS client address specified"""
+    params = hostapd.wpa2_eap_params(ssid="radius-auth")
+    params['radius_client_addr'] = "127.0.0.1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    connect(dev[0], "radius-auth")
+
+def test_radius_auth_force_invalid_client_addr(dev, apdev):
+    """RADIUS client address specified and invalid address"""
+    params = hostapd.wpa2_eap_params(ssid="radius-auth")
+    #params['radius_client_addr'] = "10.11.12.14"
+    params['radius_client_addr'] = "1::2"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    connect(dev[0], "radius-auth", wait_connect=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"])
+    if ev is None:
+        raise Exception("Timeout on EAP start")
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected connection")
+
+def add_message_auth(req):
+    req.authenticator = req.CreateAuthenticator()
+    hmac_obj = hmac.new(req.secret)
+    hmac_obj.update(struct.pack("B", req.code))
+    hmac_obj.update(struct.pack("B", req.id))
+
+    # request attributes
+    req.AddAttribute("Message-Authenticator",
+                     "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
+    attrs = req._PktEncodeAttributes()
+
+    # Length
+    flen = 4 + 16 + len(attrs)
+    hmac_obj.update(struct.pack(">H", flen))
+    hmac_obj.update(req.authenticator)
+    hmac_obj.update(attrs)
+    del req[80]
+    req.AddAttribute("Message-Authenticator", hmac_obj.digest())
+
+def test_radius_server_failures(dev, apdev):
+    """RADIUS server failure cases"""
+    try:
+        import pyrad.client
+        import pyrad.packet
+        import pyrad.dictionary
+    except ImportError:
+        raise HwsimSkip("No pyrad modules available")
+
+    dict = pyrad.dictionary.Dictionary("dictionary.radius")
+    client = pyrad.client.Client(server="127.0.0.1", authport=1812,
+                                 secret="radius", dict=dict)
+    client.retries = 1
+    client.timeout = 1
+
+    # unexpected State
+    req = client.CreateAuthPacket(code=pyrad.packet.AccessRequest,
+                                  User_Name="foo")
+    req['State'] = 'foo-state'
+    add_message_auth(req)
+    reply = client.SendPacket(req)
+    if reply.code != pyrad.packet.AccessReject:
+        raise Exception("Unexpected RADIUS response code " + str(reply.code))
+
+    # no EAP-Message
+    req = client.CreateAuthPacket(code=pyrad.packet.AccessRequest,
+                                  User_Name="foo")
+    add_message_auth(req)
+    try:
+        reply = client.SendPacket(req)
+        raise Exception("Unexpected response")
+    except pyrad.client.Timeout:
+        pass
+
+def test_ap_vlan_wpa2_psk_radius_required(dev, apdev):
+    """AP VLAN with WPA2-PSK and RADIUS attributes required"""
+    try:
+        import pyrad.server
+        import pyrad.packet
+        import pyrad.dictionary
+    except ImportError:
+        raise HwsimSkip("No pyrad modules available")
+
+    class TestServer(pyrad.server.Server):
+        def _HandleAuthPacket(self, pkt):
+            pyrad.server.Server._HandleAuthPacket(self, pkt)
+            logger.info("Received authentication request")
+            reply = self.CreateReplyPacket(pkt)
+            reply.code = pyrad.packet.AccessAccept
+            secret = reply.secret
+            if self.t_events['extra'].is_set():
+                reply.AddAttribute("Chargeable-User-Identity", "test-cui")
+                reply.AddAttribute("User-Name", "test-user")
+            if self.t_events['long'].is_set():
+                reply.AddAttribute("Tunnel-Type", 13)
+                reply.AddAttribute("Tunnel-Medium-Type", 6)
+                reply.AddAttribute("Tunnel-Private-Group-ID", "1")
+            self.SendReplyPacket(pkt.fd, reply)
+
+        def RunWithStop(self, t_events):
+            self._poll = select.poll()
+            self._fdmap = {}
+            self._PrepareSockets()
+            self.t_events = t_events
+
+            while not t_events['stop'].is_set():
+                for (fd, event) in self._poll.poll(1000):
+                    if event == select.POLLIN:
+                        try:
+                            fdo = self._fdmap[fd]
+                            self._ProcessInput(fdo)
+                        except ServerPacketError as err:
+                            logger.info("pyrad server dropping packet: " + str(err))
+                        except pyrad.packet.PacketError as err:
+                            logger.info("pyrad server received invalid packet: " + str(err))
+                    else:
+                        logger.error("Unexpected event in pyrad server main loop")
+
+    srv = TestServer(dict=pyrad.dictionary.Dictionary("dictionary.radius"),
+                     authport=18138, acctport=18139)
+    srv.hosts["127.0.0.1"] = pyrad.server.RemoteHost("127.0.0.1",
+                                                     "radius",
+                                                     "localhost")
+    srv.BindToAddress("")
+    t_events = {}
+    t_events['stop'] = threading.Event()
+    t_events['long'] = threading.Event()
+    t_events['extra'] = threading.Event()
+    t = threading.Thread(target=run_pyrad_server, args=(srv, t_events))
+    t.start()
+
+    try:
+        ssid = "test-wpa2-psk"
+        params = hostapd.radius_params()
+        params['ssid'] = ssid
+        params["wpa"] = "2"
+        params["wpa_key_mgmt"] = "WPA-PSK"
+        params["rsn_pairwise"] = "CCMP"
+        params['macaddr_acl'] = '2'
+        params['dynamic_vlan'] = "2"
+        params['wpa_passphrase'] = '0123456789abcdefghi'
+        params['auth_server_port'] = "18138"
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+        logger.info("connecting without VLAN")
+        dev[0].connect(ssid, psk="0123456789abcdefghi", scan_freq="2412",
+                       wait_connect=False)
+        ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+                                "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=20)
+        if ev is None:
+            raise Exception("Timeout on connection attempt")
+        if "CTRL-EVENT-CONNECTED" in ev:
+            raise Exception("Unexpected success without vlan parameters")
+        logger.info("connecting without VLAN failed as expected")
+
+        logger.info("connecting without VLAN (CUI/User-Name)")
+        t_events['extra'].set()
+        dev[1].connect(ssid, psk="0123456789abcdefghi", scan_freq="2412",
+                       wait_connect=False)
+        ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED",
+                                "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=20)
+        if ev is None:
+            raise Exception("Timeout on connection attempt")
+        if "CTRL-EVENT-CONNECTED" in ev:
+            raise Exception("Unexpected success without vlan parameters(2)")
+        logger.info("connecting without VLAN failed as expected(2)")
+        t_events['extra'].clear()
+
+        t_events['long'].set()
+        logger.info("connecting with VLAN")
+        dev[2].connect(ssid, psk="0123456789abcdefghi", scan_freq="2412",
+                       wait_connect=False)
+        ev = dev[2].wait_event(["CTRL-EVENT-CONNECTED",
+                                "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=20)
+        if ev is None:
+            raise Exception("Timeout on connection attempt")
+        if "CTRL-EVENT-SSID-TEMP-DISABLED" in ev:
+            raise Exception("Unexpected failure with vlan parameters")
+        logger.info("connecting with VLAN succeeded as expected")
+    finally:
+        t_events['stop'].set()
+        t.join()
diff --git a/hostap/tests/hwsim/test_rfkill.py b/hostap/tests/hwsim/test_rfkill.py
new file mode 100644
index 0000000..425b40f
--- /dev/null
+++ b/hostap/tests/hwsim/test_rfkill.py
@@ -0,0 +1,176 @@
+# rfkill 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.
+
+import logging
+logger = logging.getLogger()
+import time
+
+import hostapd
+from hostapd import HostapdGlobal
+import hwsim_utils
+from wpasupplicant import WpaSupplicant
+from rfkill import RFKill
+from utils import HwsimSkip
+
+def get_rfkill(dev):
+    phy = dev.get_driver_status_field("phyname")
+    try:
+        for r, s, h in RFKill.list():
+            if r.name == phy:
+                return r
+    except Exception, e:
+        raise HwsimSkip("No rfkill available: " + str(e))
+    raise HwsimSkip("No rfkill match found for the interface")
+
+def test_rfkill_open(dev, apdev):
+    """rfkill block/unblock during open mode connection"""
+    rfk = get_rfkill(dev[0])
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+    try:
+        logger.info("rfkill block")
+        rfk.block()
+        dev[0].wait_disconnected(timeout=10,
+                                 error="Missing disconnection event on rfkill block")
+
+        if "FAIL" not in dev[0].request("REASSOCIATE"):
+            raise Exception("REASSOCIATE accepted while disabled")
+        if "FAIL" not in dev[0].request("REATTACH"):
+            raise Exception("REATTACH accepted while disabled")
+        if "FAIL" not in dev[0].request("RECONNECT"):
+            raise Exception("RECONNECT accepted while disabled")
+        if "FAIL" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU accepted while disabled")
+
+        logger.info("rfkill unblock")
+        rfk.unblock()
+        dev[0].wait_connected(timeout=10,
+                              error="Missing connection event on rfkill unblock")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+    finally:
+        rfk.unblock()
+
+def test_rfkill_wpa2_psk(dev, apdev):
+    """rfkill block/unblock during WPA2-PSK connection"""
+    rfk = get_rfkill(dev[0])
+
+    ssid = "test-wpa2-psk"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+    try:
+        logger.info("rfkill block")
+        rfk.block()
+        dev[0].wait_disconnected(timeout=10,
+                                 error="Missing disconnection event on rfkill block")
+
+        logger.info("rfkill unblock")
+        rfk.unblock()
+        dev[0].wait_connected(timeout=10,
+                              error="Missing connection event on rfkill unblock")
+        hwsim_utils.test_connectivity(dev[0], hapd)
+    finally:
+        rfk.unblock()
+
+def test_rfkill_autogo(dev, apdev):
+    """rfkill block/unblock for autonomous P2P GO"""
+    rfk0 = get_rfkill(dev[0])
+    rfk1 = get_rfkill(dev[1])
+
+    dev[0].p2p_start_go()
+    dev[1].request("SET p2p_no_group_iface 0")
+    dev[1].p2p_start_go()
+
+    try:
+        logger.info("rfkill block 0")
+        rfk0.block()
+        ev = dev[0].wait_global_event(["P2P-GROUP-REMOVED"], timeout=10)
+        if ev is None:
+            raise Exception("Group removal not reported")
+        if "reason=UNAVAILABLE" not in ev:
+            raise Exception("Unexpected group removal reason: " + ev)
+        if "FAIL" not in dev[0].request("P2P_LISTEN 1"):
+            raise Exception("P2P_LISTEN accepted unexpectedly")
+        if "FAIL" not in dev[0].request("P2P_LISTEN"):
+            raise Exception("P2P_LISTEN accepted unexpectedly")
+
+        logger.info("rfkill block 1")
+        rfk1.block()
+        ev = dev[1].wait_global_event(["P2P-GROUP-REMOVED"], timeout=10)
+        if ev is None:
+            raise Exception("Group removal not reported")
+        if "reason=UNAVAILABLE" not in ev:
+            raise Exception("Unexpected group removal reason: " + ev)
+
+        logger.info("rfkill unblock 0")
+        rfk0.unblock()
+        logger.info("rfkill unblock 1")
+        rfk1.unblock()
+        time.sleep(1)
+    finally:
+        rfk0.unblock()
+        rfk1.unblock()
+
+def test_rfkill_hostapd(dev, apdev):
+    """rfkill block/unblock during and prior to hostapd operations"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+
+    rfk = get_rfkill(hapd)
+
+    try:
+        rfk.block()
+        ev = hapd.wait_event(["INTERFACE-DISABLED"], timeout=5)
+        if ev is None:
+            raise Exception("INTERFACE-DISABLED event not seen")
+        rfk.unblock()
+        ev = hapd.wait_event(["INTERFACE-ENABLED"], timeout=5)
+        if ev is None:
+            raise Exception("INTERFACE-ENABLED event not seen")
+        # hostapd does not current re-enable beaconing automatically
+        hapd.disable()
+        hapd.enable()
+        dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+        rfk.block()
+        ev = hapd.wait_event(["INTERFACE-DISABLED"], timeout=5)
+        if ev is None:
+            raise Exception("INTERFACE-DISABLED event not seen")
+        dev[0].wait_disconnected(timeout=10)
+        dev[0].request("DISCONNECT")
+        hapd.disable()
+
+        hglobal = HostapdGlobal()
+        hglobal.flush()
+        hglobal.remove(apdev[0]['ifname'])
+
+        hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open2" },
+                              no_enable=True)
+        if "FAIL" not in hapd.request("ENABLE"):
+            raise Exception("ENABLE succeeded unexpectedly (rfkill)")
+    finally:
+        rfk.unblock()
+
+def test_rfkill_wpas(dev, apdev):
+    """rfkill block prior to wpa_supplicant start"""
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    rfk = get_rfkill(wpas)
+    wpas.interface_remove("wlan5")
+    try:
+        rfk.block()
+        wpas.interface_add("wlan5")
+        time.sleep(0.5)
+        state = wpas.get_status_field("wpa_state")
+        if state != "INTERFACE_DISABLED":
+            raise Exception("Unexpected state with rfkill blocked: " + state)
+        rfk.unblock()
+        time.sleep(0.5)
+        state = wpas.get_status_field("wpa_state")
+        if state == "INTERFACE_DISABLED":
+            raise Exception("Unexpected state with rfkill unblocked: " + state)
+    finally:
+        rfk.unblock()
diff --git a/hostap/tests/hwsim/test_sae.py b/hostap/tests/hwsim/test_sae.py
new file mode 100644
index 0000000..ba70f75
--- /dev/null
+++ b/hostap/tests/hwsim/test_sae.py
@@ -0,0 +1,709 @@
+# Test cases for SAE
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import os
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import hostapd
+from utils import HwsimSkip, alloc_fail, fail_test
+from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
+
+def test_sae(dev, apdev):
+    """SAE with default group"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae",
+                                 passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    key_mgmt = hapd.get_config()['key_mgmt']
+    if key_mgmt.split(' ')[0] != "SAE":
+        raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+
+    dev[0].request("SET sae_groups ")
+    id = dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+                        scan_freq="2412")
+    if dev[0].get_status_field('sae_group') != '19':
+            raise Exception("Expected default SAE group not used")
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    if 'flags' not in bss:
+        raise Exception("Could not get BSS flags from BSS table")
+    if "[WPA2-SAE-CCMP]" not in bss['flags']:
+        raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+def test_sae_password_ecc(dev, apdev):
+    """SAE with number of different passwords (ECC)"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae",
+                                 passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SET sae_groups 19")
+
+    for i in range(10):
+        password = "12345678-" + str(i)
+        hapd.set("wpa_passphrase", password)
+        dev[0].connect("test-sae", psk=password, key_mgmt="SAE",
+                       scan_freq="2412")
+        dev[0].request("REMOVE_NETWORK all")
+        dev[0].wait_disconnected()
+
+def test_sae_password_ffc(dev, apdev):
+    """SAE with number of different passwords (FFC)"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae",
+                                 passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    params['sae_groups'] = '22'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SET sae_groups 22")
+
+    for i in range(10):
+        password = "12345678-" + str(i)
+        hapd.set("wpa_passphrase", password)
+        dev[0].connect("test-sae", psk=password, key_mgmt="SAE",
+                       scan_freq="2412")
+        dev[0].request("REMOVE_NETWORK all")
+        dev[0].wait_disconnected()
+
+def test_sae_pmksa_caching(dev, apdev):
+    """SAE and PMKSA caching"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae",
+                                 passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SET sae_groups ")
+    dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+                   scan_freq="2412")
+    ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
+    if ev is None:
+        raise Exception("No connection event received from hostapd")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    dev[0].request("RECONNECT")
+    dev[0].wait_connected(timeout=15, error="Reconnect timed out")
+    if dev[0].get_status_field('sae_group') is not None:
+            raise Exception("SAE group claimed to have been used")
+
+def test_sae_pmksa_caching_disabled(dev, apdev):
+    """SAE and PMKSA caching disabled"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae",
+                                 passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    params['disable_pmksa_caching'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SET sae_groups ")
+    dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+                   scan_freq="2412")
+    ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
+    if ev is None:
+        raise Exception("No connection event received from hostapd")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    dev[0].request("RECONNECT")
+    dev[0].wait_connected(timeout=15, error="Reconnect timed out")
+    if dev[0].get_status_field('sae_group') != '19':
+            raise Exception("Expected default SAE group not used")
+
+def test_sae_groups(dev, apdev):
+    """SAE with all supported groups"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    # This is the full list of supported groups, but groups 14-16 (2048-4096 bit
+    # MODP) and group 21 (521-bit random ECP group) are a bit too slow on some
+    # VMs and can result in hitting the mac80211 authentication timeout, so
+    # allow them to fail and just report such failures in the debug log.
+    sae_groups = [ 19, 25, 26, 20, 21, 2, 5, 14, 15, 16, 22, 23, 24 ]
+    tls = dev[0].request("GET tls_library")
+    if tls.startswith("OpenSSL") and "build=OpenSSL 1.0.2" in tls and "run=OpenSSL 1.0.2" in tls:
+        logger.info("Add Brainpool EC groups since OpenSSL is new enough")
+        sae_groups += [ 27, 28, 29, 30 ]
+    heavy_groups = [ 14, 15, 16 ]
+    groups = [str(g) for g in sae_groups]
+    params = hostapd.wpa2_params(ssid="test-sae-groups",
+                                 passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    params['sae_groups'] = ' '.join(groups)
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    for g in groups:
+        logger.info("Testing SAE group " + g)
+        dev[0].request("SET sae_groups " + g)
+        id = dev[0].connect("test-sae-groups", psk="12345678", key_mgmt="SAE",
+                            scan_freq="2412", wait_connect=False)
+        if int(g) in heavy_groups:
+            ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=5)
+            if ev is None:
+                logger.info("No connection with heavy SAE group %s did not connect - likely hitting timeout in mac80211" % g)
+                dev[0].remove_network(id)
+                time.sleep(0.1)
+                dev[0].dump_monitor()
+                continue
+            logger.info("Connection with heavy SAE group " + g)
+        else:
+            ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
+            if ev is None:
+                if "BoringSSL" in tls and int(g) in [ 25 ]:
+                    logger.info("Ignore connection failure with group " + g + " with BoringSSL")
+                    dev[0].remove_network(id)
+                    dev[0].dump_monitor()
+                    continue
+                raise Exception("Connection timed out with group " + g)
+        if dev[0].get_status_field('sae_group') != g:
+            raise Exception("Expected SAE group not used")
+        dev[0].remove_network(id)
+        dev[0].wait_disconnected()
+        dev[0].dump_monitor()
+
+def test_sae_group_nego(dev, apdev):
+    """SAE group negotiation"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae-group-nego",
+                                 passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    params['sae_groups'] = '19'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SET sae_groups 25 26 20 19")
+    dev[0].connect("test-sae-group-nego", psk="12345678", key_mgmt="SAE",
+                   scan_freq="2412")
+    if dev[0].get_status_field('sae_group') != '19':
+        raise Exception("Expected SAE group not used")
+
+def test_sae_anti_clogging(dev, apdev):
+    """SAE anti clogging"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    params['sae_anti_clogging_threshold'] = '1'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SET sae_groups ")
+    dev[1].request("SET sae_groups ")
+    id = {}
+    for i in range(0, 2):
+        dev[i].scan(freq="2412")
+        id[i] = dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE",
+                               scan_freq="2412", only_add_network=True)
+    for i in range(0, 2):
+        dev[i].select_network(id[i])
+    for i in range(0, 2):
+        dev[i].wait_connected(timeout=10)
+
+def test_sae_forced_anti_clogging(dev, apdev):
+    """SAE anti clogging (forced)"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE WPA-PSK'
+    params['sae_anti_clogging_threshold'] = '0'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[2].connect("test-sae", psk="12345678", scan_freq="2412")
+    for i in range(0, 2):
+        dev[i].request("SET sae_groups ")
+        dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE",
+                       scan_freq="2412")
+
+def test_sae_mixed(dev, apdev):
+    """Mixed SAE and non-SAE network"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE WPA-PSK'
+    params['sae_anti_clogging_threshold'] = '0'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[2].connect("test-sae", psk="12345678", scan_freq="2412")
+    for i in range(0, 2):
+        dev[i].request("SET sae_groups ")
+        dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE",
+                       scan_freq="2412")
+
+def test_sae_missing_password(dev, apdev):
+    """SAE and missing password"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae",
+                                 passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SET sae_groups ")
+    id = dev[0].connect("test-sae",
+                        raw_psk="46b4a73b8a951ad53ebd2e0afdb9c5483257edd4c21d12b7710759da70945858",
+                        key_mgmt="SAE", scan_freq="2412", wait_connect=False)
+    ev = dev[0].wait_event(['CTRL-EVENT-SSID-TEMP-DISABLED'], timeout=10)
+    if ev is None:
+        raise Exception("Invalid network not temporarily disabled")
+
+
+def test_sae_key_lifetime_in_memory(dev, apdev, params):
+    """SAE and key lifetime in memory"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    password = "5ad144a7c1f5a5503baa6fa01dabc15b1843e8c01662d78d16b70b5cd23cf8b"
+    p = hostapd.wpa2_params(ssid="test-sae", passphrase=password)
+    p['wpa_key_mgmt'] = 'SAE'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], p)
+
+    pid = find_wpas_process(dev[0])
+
+    dev[0].request("SET sae_groups ")
+    id = dev[0].connect("test-sae", psk=password, key_mgmt="SAE",
+                        scan_freq="2412")
+
+    time.sleep(1)
+    buf = read_process_memory(pid, password)
+
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+
+    dev[0].relog()
+    sae_k = None
+    sae_keyseed = None
+    sae_kck = None
+    pmk = None
+    ptk = None
+    gtk = None
+    with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
+        for l in f.readlines():
+            if "SAE: k - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                sae_k = binascii.unhexlify(val)
+            if "SAE: keyseed - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                sae_keyseed = binascii.unhexlify(val)
+            if "SAE: KCK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                sae_kck = binascii.unhexlify(val)
+            if "SAE: PMK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                pmk = binascii.unhexlify(val)
+            if "WPA: PTK - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                ptk = binascii.unhexlify(val)
+            if "WPA: Group Key - hexdump" in l:
+                val = l.strip().split(':')[3].replace(' ', '')
+                gtk = binascii.unhexlify(val)
+    if not sae_k or not sae_keyseed or not sae_kck or not pmk or not ptk or not gtk:
+        raise Exception("Could not find keys from debug log")
+    if len(gtk) != 16:
+        raise Exception("Unexpected GTK length")
+
+    kck = ptk[0:16]
+    kek = ptk[16:32]
+    tk = ptk[32:48]
+
+    fname = os.path.join(params['logdir'],
+                         'sae_key_lifetime_in_memory.memctx-')
+
+    logger.info("Checking keys in memory while associated")
+    get_key_locations(buf, password, "Password")
+    get_key_locations(buf, pmk, "PMK")
+    if password not in buf:
+        raise HwsimSkip("Password not found while associated")
+    if pmk not in buf:
+        raise HwsimSkip("PMK not found while associated")
+    if kck not in buf:
+        raise Exception("KCK not found while associated")
+    if kek not in buf:
+        raise Exception("KEK not found while associated")
+    if tk in buf:
+        raise Exception("TK found from memory")
+    if gtk in buf:
+        raise Exception("GTK found from memory")
+    verify_not_present(buf, sae_k, fname, "SAE(k)")
+    verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)")
+    verify_not_present(buf, sae_kck, fname, "SAE(KCK)")
+
+    logger.info("Checking keys in memory after disassociation")
+    buf = read_process_memory(pid, password)
+
+    # Note: Password is still present in network configuration
+    # Note: PMK is in PMKSA cache
+
+    get_key_locations(buf, password, "Password")
+    get_key_locations(buf, pmk, "PMK")
+    verify_not_present(buf, kck, fname, "KCK")
+    verify_not_present(buf, kek, fname, "KEK")
+    verify_not_present(buf, tk, fname, "TK")
+    verify_not_present(buf, gtk, fname, "GTK")
+    verify_not_present(buf, sae_k, fname, "SAE(k)")
+    verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)")
+    verify_not_present(buf, sae_kck, fname, "SAE(KCK)")
+
+    dev[0].request("PMKSA_FLUSH")
+    logger.info("Checking keys in memory after PMKSA cache flush")
+    buf = read_process_memory(pid, password)
+    get_key_locations(buf, password, "Password")
+    get_key_locations(buf, pmk, "PMK")
+    verify_not_present(buf, pmk, fname, "PMK")
+
+    dev[0].request("REMOVE_NETWORK all")
+
+    logger.info("Checking keys in memory after network profile removal")
+    buf = read_process_memory(pid, password)
+
+    get_key_locations(buf, password, "Password")
+    get_key_locations(buf, pmk, "PMK")
+    verify_not_present(buf, password, fname, "password")
+    verify_not_present(buf, pmk, fname, "PMK")
+    verify_not_present(buf, kck, fname, "KCK")
+    verify_not_present(buf, kek, fname, "KEK")
+    verify_not_present(buf, tk, fname, "TK")
+    verify_not_present(buf, gtk, fname, "GTK")
+    verify_not_present(buf, sae_k, fname, "SAE(k)")
+    verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)")
+    verify_not_present(buf, sae_kck, fname, "SAE(KCK)")
+
+def test_sae_oom_wpas(dev, apdev):
+    """SAE and OOM in wpa_supplicant"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae",
+                                 passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SET sae_groups 25")
+    tls = dev[0].request("GET tls_library")
+    if "BoringSSL" in tls:
+        dev[0].request("SET sae_groups 26")
+    with alloc_fail(dev[0], 1, "sae_set_group"):
+        dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+                       scan_freq="2412")
+        dev[0].request("REMOVE_NETWORK all")
+
+    dev[0].request("SET sae_groups ")
+    with alloc_fail(dev[0], 2, "sae_set_group"):
+        dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+                       scan_freq="2412")
+        dev[0].request("REMOVE_NETWORK all")
+
+def test_sae_proto_ecc(dev, apdev):
+    """SAE protocol testing (ECC)"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae",
+                                 passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    dev[0].request("SET sae_groups 19")
+
+    tests = [ ("Confirm mismatch",
+               "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8",
+               "0000800edebc3f260dc1fe7e0b20888af2b8a3316252ec37388a8504e25b73dc4240"),
+              ("Commit without even full cyclic group field",
+               "13",
+               None),
+              ("Too short commit",
+               "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02",
+               None),
+              ("Invalid commit scalar (0)",
+               "1300" + "0000000000000000000000000000000000000000000000000000000000000000" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8",
+               None),
+              ("Invalid commit scalar (1)",
+               "1300" + "0000000000000000000000000000000000000000000000000000000000000001" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8",
+               None),
+              ("Invalid commit scalar (> r)",
+               "1300" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8",
+               None),
+              ("Commit element not on curve",
+               "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728d0000000000000000000000000000000000000000000000000000000000000000",
+               None),
+              ("Invalid commit element (y coordinate > P)",
+               "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+               None),
+              ("Invalid commit element (x coordinate > P)",
+               "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8",
+               None),
+              ("Different group in commit",
+               "1400" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8",
+               None),
+              ("Too short confirm",
+               "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8",
+               "0000800edebc3f260dc1fe7e0b20888af2b8a3316252ec37388a8504e25b73dc42")]
+    for (note, commit, confirm) in tests:
+        logger.info(note)
+        dev[0].scan_for_bss(bssid, freq=2412)
+        hapd.set("ext_mgmt_frame_handling", "1")
+        dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+                       scan_freq="2412", wait_connect=False)
+
+        logger.info("Commit")
+        for i in range(0, 10):
+            req = hapd.mgmt_rx()
+            if req is None:
+                raise Exception("MGMT RX wait timed out (commit)")
+            if req['subtype'] == 11:
+                break
+            req = None
+        if not req:
+            raise Exception("Authentication frame (commit) not received")
+
+        hapd.dump_monitor()
+        resp = {}
+        resp['fc'] = req['fc']
+        resp['da'] = req['sa']
+        resp['sa'] = req['da']
+        resp['bssid'] = req['bssid']
+        resp['payload'] = binascii.unhexlify("030001000000" + commit)
+        hapd.mgmt_tx(resp)
+
+        if confirm:
+            logger.info("Confirm")
+            for i in range(0, 10):
+                req = hapd.mgmt_rx()
+                if req is None:
+                    raise Exception("MGMT RX wait timed out (confirm)")
+                if req['subtype'] == 11:
+                    break
+                req = None
+            if not req:
+                raise Exception("Authentication frame (confirm) not received")
+
+            hapd.dump_monitor()
+            resp = {}
+            resp['fc'] = req['fc']
+            resp['da'] = req['sa']
+            resp['sa'] = req['da']
+            resp['bssid'] = req['bssid']
+            resp['payload'] = binascii.unhexlify("030002000000" + confirm)
+            hapd.mgmt_tx(resp)
+
+        time.sleep(0.1)
+        dev[0].request("REMOVE_NETWORK all")
+        hapd.set("ext_mgmt_frame_handling", "0")
+        hapd.dump_monitor()
+
+def test_sae_proto_ffc(dev, apdev):
+    """SAE protocol testing (FFC)"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae",
+                                 passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    dev[0].request("SET sae_groups 2")
+
+    tests = [ ("Confirm mismatch",
+               "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "a8c00117493cdffa5dd671e934bc9cb1a69f39e25e9dd9cd9afd3aea2441a0f5491211c7ba50a753563f9ce943b043557cb71193b28e86ed9544f4289c471bf91b70af5c018cf4663e004165b0fd0bc1d8f3f78adf42eee92bcbc55246fd3ee9f107ab965dc7d4986f23eb71d616ebfe6bfe0a6c1ac5dc1718acee17c9a17486",
+               "0000f3116a9731f1259622e3eb55d4b3b50ba16f8c5f5565b28e609b180c51460251"),
+              ("Too short commit",
+               "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "a8c00117493cdffa5dd671e934bc9cb1a69f39e25e9dd9cd9afd3aea2441a0f5491211c7ba50a753563f9ce943b043557cb71193b28e86ed9544f4289c471bf91b70af5c018cf4663e004165b0fd0bc1d8f3f78adf42eee92bcbc55246fd3ee9f107ab965dc7d4986f23eb71d616ebfe6bfe0a6c1ac5dc1718acee17c9a174",
+               None),
+              ("Invalid element (0) in commit",
+               "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+               None),
+              ("Invalid element (1) in commit",
+               "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
+               None),
+              ("Invalid element (> P) in commit",
+               "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+               None) ]
+    for (note, commit, confirm) in tests:
+        logger.info(note)
+        dev[0].scan_for_bss(bssid, freq=2412)
+        hapd.set("ext_mgmt_frame_handling", "1")
+        dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+                       scan_freq="2412", wait_connect=False)
+
+        logger.info("Commit")
+        for i in range(0, 10):
+            req = hapd.mgmt_rx()
+            if req is None:
+                raise Exception("MGMT RX wait timed out (commit)")
+            if req['subtype'] == 11:
+                break
+            req = None
+        if not req:
+            raise Exception("Authentication frame (commit) not received")
+
+        hapd.dump_monitor()
+        resp = {}
+        resp['fc'] = req['fc']
+        resp['da'] = req['sa']
+        resp['sa'] = req['da']
+        resp['bssid'] = req['bssid']
+        resp['payload'] = binascii.unhexlify("030001000000" + commit)
+        hapd.mgmt_tx(resp)
+
+        if confirm:
+            logger.info("Confirm")
+            for i in range(0, 10):
+                req = hapd.mgmt_rx()
+                if req is None:
+                    raise Exception("MGMT RX wait timed out (confirm)")
+                if req['subtype'] == 11:
+                    break
+                req = None
+            if not req:
+                raise Exception("Authentication frame (confirm) not received")
+
+            hapd.dump_monitor()
+            resp = {}
+            resp['fc'] = req['fc']
+            resp['da'] = req['sa']
+            resp['sa'] = req['da']
+            resp['bssid'] = req['bssid']
+            resp['payload'] = binascii.unhexlify("030002000000" + confirm)
+            hapd.mgmt_tx(resp)
+
+        time.sleep(0.1)
+        dev[0].request("REMOVE_NETWORK all")
+        hapd.set("ext_mgmt_frame_handling", "0")
+        hapd.dump_monitor()
+
+def test_sae_no_ffc_by_default(dev, apdev):
+    """SAE and default groups rejecting FFC"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SET sae_groups 5")
+    dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412",
+                   wait_connect=False)
+    ev = dev[0].wait_event(["SME: Trying to authenticate"], timeout=3)
+    if ev is None:
+        raise Exception("Did not try to authenticate")
+    ev = dev[0].wait_event(["SME: Trying to authenticate"], timeout=3)
+    if ev is None:
+        raise Exception("Did not try to authenticate (2)")
+    dev[0].request("REMOVE_NETWORK all")
+
+def sae_reflection_attack(apdev, dev, group):
+    if "SAE" not in dev.get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae",
+                                 passphrase="no-knowledge-of-passphrase")
+    params['wpa_key_mgmt'] = 'SAE'
+    hapd = hostapd.add_ap(apdev['ifname'], params)
+    bssid = apdev['bssid']
+
+    dev.scan_for_bss(bssid, freq=2412)
+    hapd.set("ext_mgmt_frame_handling", "1")
+
+    dev.request("SET sae_groups %d" % group)
+    dev.connect("test-sae", psk="reflection-attack", key_mgmt="SAE",
+                scan_freq="2412", wait_connect=False)
+
+    # Commit
+    for i in range(0, 10):
+        req = hapd.mgmt_rx()
+        if req is None:
+            raise Exception("MGMT RX wait timed out")
+        if req['subtype'] == 11:
+            break
+        req = None
+    if not req:
+        raise Exception("Authentication frame not received")
+
+    resp = {}
+    resp['fc'] = req['fc']
+    resp['da'] = req['sa']
+    resp['sa'] = req['da']
+    resp['bssid'] = req['bssid']
+    resp['payload'] = req['payload']
+    hapd.mgmt_tx(resp)
+
+    # Confirm
+    req = hapd.mgmt_rx(timeout=0.5)
+    if req is not None:
+        if req['subtype'] == 11:
+            raise Exception("Unexpected Authentication frame seen")
+
+def test_sae_reflection_attack_ecc(dev, apdev):
+    """SAE reflection attack (ECC)"""
+    sae_reflection_attack(apdev[0], dev[0], 19)
+
+def test_sae_reflection_attack_ffc(dev, apdev):
+    """SAE reflection attack (FFC)"""
+    sae_reflection_attack(apdev[0], dev[0], 5)
+
+def test_sae_anti_clogging_proto(dev, apdev):
+    """SAE anti clogging protocol testing"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae",
+                                 passphrase="no-knowledge-of-passphrase")
+    params['wpa_key_mgmt'] = 'SAE'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+
+    dev[0].scan_for_bss(bssid, freq=2412)
+    hapd.set("ext_mgmt_frame_handling", "1")
+
+    dev[0].request("SET sae_groups ")
+    dev[0].connect("test-sae", psk="anti-cloggign", key_mgmt="SAE",
+                   scan_freq="2412", wait_connect=False)
+
+    # Commit
+    for i in range(0, 10):
+        req = hapd.mgmt_rx()
+        if req is None:
+            raise Exception("MGMT RX wait timed out")
+        if req['subtype'] == 11:
+            break
+        req = None
+    if not req:
+        raise Exception("Authentication frame not received")
+
+    resp = {}
+    resp['fc'] = req['fc']
+    resp['da'] = req['sa']
+    resp['sa'] = req['da']
+    resp['bssid'] = req['bssid']
+    resp['payload'] = binascii.unhexlify("030001004c00" + "ffff00")
+    hapd.mgmt_tx(resp)
+
+    # Confirm (not received due to DH group being rejected)
+    req = hapd.mgmt_rx(timeout=0.5)
+    if req is not None:
+        if req['subtype'] == 11:
+            raise Exception("Unexpected Authentication frame seen")
+
+def test_sae_no_random(dev, apdev):
+    """SAE and no random numbers available"""
+    if "SAE" not in dev[0].get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+    params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].request("SET sae_groups ")
+    tests = [ (1, "os_get_random;sae_get_rand"),
+              (1, "os_get_random;get_rand_1_to_p_1"),
+              (1, "os_get_random;get_random_qr_qnr"),
+              (1, "os_get_random;sae_derive_pwe_ecc") ]
+    for count, func in tests:
+        with fail_test(dev[0], count, func):
+            dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+                           scan_freq="2412")
+            dev[0].request("REMOVE_NETWORK all")
+            dev[0].wait_disconnected()
diff --git a/hostap/tests/hwsim/test_scan.py b/hostap/tests/hwsim/test_scan.py
new file mode 100644
index 0000000..fa1bcc9
--- /dev/null
+++ b/hostap/tests/hwsim/test_scan.py
@@ -0,0 +1,907 @@
+# Scanning tests
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import logging
+logger = logging.getLogger()
+import os
+import subprocess
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import HwsimSkip, fail_test
+from tshark import run_tshark
+
+def check_scan(dev, params, other_started=False, test_busy=False):
+    if not other_started:
+        dev.dump_monitor()
+    id = dev.request("SCAN " + params)
+    if "FAIL" in id:
+        raise Exception("Failed to start scan")
+    id = int(id)
+
+    if test_busy:
+        if "FAIL-BUSY" not in dev.request("SCAN"):
+            raise Exception("SCAN command while already scanning not rejected")
+
+    if other_started:
+        ev = dev.wait_event(["CTRL-EVENT-SCAN-STARTED"])
+        if ev is None:
+            raise Exception("Other scan did not start")
+        if "id=" + str(id) in ev:
+            raise Exception("Own scan id unexpectedly included in start event")
+
+        ev = dev.wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+        if ev is None:
+            raise Exception("Other scan did not complete")
+        if "id=" + str(id) in ev:
+            raise Exception("Own scan id unexpectedly included in completed event")
+
+    ev = dev.wait_event(["CTRL-EVENT-SCAN-STARTED"])
+    if ev is None:
+        raise Exception("Scan did not start")
+    if "id=" + str(id) not in ev:
+        raise Exception("Scan id not included in start event")
+    if test_busy:
+        if "FAIL-BUSY" not in dev.request("SCAN"):
+            raise Exception("SCAN command while already scanning not rejected")
+
+    ev = dev.wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+    if ev is None:
+        raise Exception("Scan did not complete")
+    if "id=" + str(id) not in ev:
+        raise Exception("Scan id not included in completed event")
+
+def check_scan_retry(dev, params, bssid):
+    for i in range(0, 5):
+        check_scan(dev, "freq=2412-2462,5180 use_id=1")
+        if int(dev.get_bss(bssid)['age']) <= 1:
+            return
+    raise Exception("Unexpectedly old BSS entry")
+
+def test_scan(dev, apdev):
+    """Control interface behavior on scan parameters"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-scan" })
+    bssid = apdev[0]['bssid']
+
+    logger.info("Full scan")
+    check_scan(dev[0], "use_id=1", test_busy=True)
+
+    logger.info("Limited channel scan")
+    check_scan_retry(dev[0], "freq=2412-2462,5180 use_id=1", bssid)
+
+    # wait long enough to allow next scans to be verified not to find the AP
+    time.sleep(2)
+
+    logger.info("Passive single-channel scan")
+    check_scan(dev[0], "freq=2457 passive=1 use_id=1")
+    logger.info("Active single-channel scan")
+    check_scan(dev[0], "freq=2452 passive=0 use_id=1")
+    if int(dev[0].get_bss(bssid)['age']) < 2:
+        raise Exception("Unexpectedly updated BSS entry")
+
+    logger.info("Active single-channel scan on AP's operating channel")
+    check_scan_retry(dev[0], "freq=2412 passive=0 use_id=1", bssid)
+
+def test_scan_tsf(dev, apdev):
+    """Scan and TSF updates from Beacon/Probe Response frames"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-scan",
+                                         'beacon_int': "100" })
+    bssid = apdev[0]['bssid']
+
+    tsf = []
+    for passive in [ 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1 ]:
+        check_scan(dev[0], "freq=2412 passive=%d use_id=1" % passive)
+        bss = dev[0].get_bss(bssid)
+        if bss:
+            tsf.append(int(bss['tsf']))
+            logger.info("TSF: " + bss['tsf'])
+    if tsf[-3] <= tsf[-4]:
+        # For now, only write this in the log without failing the test case
+        # since mac80211_hwsim does not yet update the Timestamp field in
+        # Probe Response frames.
+        logger.info("Probe Response did not update TSF")
+        #raise Exception("Probe Response did not update TSF")
+    if tsf[-1] <= tsf[-3]:
+        raise Exception("Beacon did not update TSF")
+    if 0 in tsf:
+        raise Exception("0 TSF reported")
+
+def test_scan_only(dev, apdev):
+    """Control interface behavior on scan parameters with type=only"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-scan" })
+    bssid = apdev[0]['bssid']
+
+    logger.info("Full scan")
+    check_scan(dev[0], "type=only use_id=1")
+
+    logger.info("Limited channel scan")
+    check_scan_retry(dev[0], "type=only freq=2412-2462,5180 use_id=1", bssid)
+
+    # wait long enough to allow next scans to be verified not to find the AP
+    time.sleep(2)
+
+    logger.info("Passive single-channel scan")
+    check_scan(dev[0], "type=only freq=2457 passive=1 use_id=1")
+    logger.info("Active single-channel scan")
+    check_scan(dev[0], "type=only freq=2452 passive=0 use_id=1")
+    if int(dev[0].get_bss(bssid)['age']) < 2:
+        raise Exception("Unexpectedly updated BSS entry")
+
+    logger.info("Active single-channel scan on AP's operating channel")
+    check_scan_retry(dev[0], "type=only freq=2412 passive=0 use_id=1", bssid)
+
+def test_scan_external_trigger(dev, apdev):
+    """Avoid operations during externally triggered scan"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-scan" })
+    bssid = apdev[0]['bssid']
+    subprocess.call(['iw', dev[0].ifname, 'scan', 'trigger'])
+    check_scan(dev[0], "use_id=1", other_started=True)
+
+def test_scan_bss_expiration_count(dev, apdev):
+    """BSS entry expiration based on scan results without match"""
+    if "FAIL" not in dev[0].request("BSS_EXPIRE_COUNT 0"):
+        raise Exception("Invalid BSS_EXPIRE_COUNT accepted")
+    if "OK" not in dev[0].request("BSS_EXPIRE_COUNT 2"):
+        raise Exception("BSS_EXPIRE_COUNT failed")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-scan" })
+    bssid = apdev[0]['bssid']
+    dev[0].scan(freq="2412", only_new=True)
+    if bssid not in dev[0].request("SCAN_RESULTS"):
+        raise Exception("BSS not found in initial scan")
+    hapd.request("DISABLE")
+    dev[0].scan(freq="2412", only_new=True)
+    if bssid not in dev[0].request("SCAN_RESULTS"):
+        raise Exception("BSS not found in first scan without match")
+    dev[0].scan(freq="2412", only_new=True)
+    if bssid in dev[0].request("SCAN_RESULTS"):
+        raise Exception("BSS found after two scans without match")
+
+def test_scan_bss_expiration_age(dev, apdev):
+    """BSS entry expiration based on age"""
+    try:
+        if "FAIL" not in dev[0].request("BSS_EXPIRE_AGE COUNT 9"):
+            raise Exception("Invalid BSS_EXPIRE_AGE accepted")
+        if "OK" not in dev[0].request("BSS_EXPIRE_AGE 10"):
+            raise Exception("BSS_EXPIRE_AGE failed")
+        hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-scan" })
+        bssid = apdev[0]['bssid']
+        dev[0].scan(freq="2412")
+        if bssid not in dev[0].request("SCAN_RESULTS"):
+            raise Exception("BSS not found in initial scan")
+        hapd.request("DISABLE")
+        logger.info("Waiting for BSS entry to expire")
+        time.sleep(7)
+        if bssid not in dev[0].request("SCAN_RESULTS"):
+            raise Exception("BSS expired too quickly")
+        ev = dev[0].wait_event(["CTRL-EVENT-BSS-REMOVED"], timeout=15)
+        if ev is None:
+            raise Exception("BSS entry expiration timed out")
+        if bssid in dev[0].request("SCAN_RESULTS"):
+            raise Exception("BSS not removed after expiration time")
+    finally:
+        dev[0].request("BSS_EXPIRE_AGE 180")
+
+def test_scan_filter(dev, apdev):
+    """Filter scan results based on SSID"""
+    try:
+        if "OK" not in dev[0].request("SET filter_ssids 1"):
+            raise Exception("SET failed")
+        id = dev[0].connect("test-scan", key_mgmt="NONE", only_add_network=True)
+        hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-scan" })
+        bssid = apdev[0]['bssid']
+        hostapd.add_ap(apdev[1]['ifname'], { "ssid": "test-scan2" })
+        bssid2 = apdev[1]['bssid']
+        dev[0].scan(freq="2412", only_new=True)
+        if bssid not in dev[0].request("SCAN_RESULTS"):
+            raise Exception("BSS not found in scan results")
+        if bssid2 in dev[0].request("SCAN_RESULTS"):
+            raise Exception("Unexpected BSS found in scan results")
+        dev[0].set_network_quoted(id, "ssid", "")
+        dev[0].scan(freq="2412")
+        id2 = dev[0].connect("test", key_mgmt="NONE", only_add_network=True)
+        dev[0].scan(freq="2412")
+    finally:
+        dev[0].request("SET filter_ssids 0")
+
+def test_scan_int(dev, apdev):
+    """scan interval configuration"""
+    try:
+        if "FAIL" not in dev[0].request("SCAN_INTERVAL -1"):
+            raise Exception("Accepted invalid scan interval")
+        if "OK" not in dev[0].request("SCAN_INTERVAL 1"):
+            raise Exception("Failed to set scan interval")
+        dev[0].connect("not-used", key_mgmt="NONE", scan_freq="2412",
+                       wait_connect=False)
+        times = {}
+        for i in range(0, 3):
+            logger.info("Waiting for scan to start")
+            start = os.times()[4]
+            ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+            if ev is None:
+                raise Exception("did not start a scan")
+            stop = os.times()[4]
+            times[i] = stop - start
+            logger.info("Waiting for scan to complete")
+            ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+            if ev is None:
+                raise Exception("did not complete a scan")
+        logger.info("times=" + str(times))
+        if times[0] > 1 or times[1] < 0.5 or times[1] > 1.5 or times[2] < 0.5 or times[2] > 1.5:
+            raise Exception("Unexpected scan timing: " + str(times))
+    finally:
+        dev[0].request("SCAN_INTERVAL 5")
+
+def test_scan_bss_operations(dev, apdev):
+    """Control interface behavior on BSS parameters"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-scan" })
+    bssid = apdev[0]['bssid']
+    hostapd.add_ap(apdev[1]['ifname'], { "ssid": "test2-scan" })
+    bssid2 = apdev[1]['bssid']
+
+    dev[0].scan(freq="2412")
+    dev[0].scan(freq="2412")
+    dev[0].scan(freq="2412")
+
+    id1 = dev[0].request("BSS FIRST MASK=0x1").splitlines()[0].split('=')[1]
+    id2 = dev[0].request("BSS LAST MASK=0x1").splitlines()[0].split('=')[1]
+
+    res = dev[0].request("BSS RANGE=ALL MASK=0x20001")
+    if "id=" + id1 not in res:
+        raise Exception("Missing BSS " + id1)
+    if "id=" + id2 not in res:
+        raise Exception("Missing BSS " + id2)
+    if "====" not in res:
+        raise Exception("Missing delim")
+    if "####" not in res:
+        raise Exception("Missing end")
+
+    res = dev[0].request("BSS RANGE=ALL MASK=0")
+    if "id=" + id1 not in res:
+        raise Exception("Missing BSS " + id1)
+    if "id=" + id2 not in res:
+        raise Exception("Missing BSS " + id2)
+    if "====" in res:
+        raise Exception("Unexpected delim")
+    if "####" in res:
+        raise Exception("Unexpected end delim")
+
+    res = dev[0].request("BSS RANGE=ALL MASK=0x1").splitlines()
+    if len(res) != 2:
+        raise Exception("Unexpected result: " + str(res))
+    res = dev[0].request("BSS FIRST MASK=0x1")
+    if "id=" + id1 not in res:
+        raise Exception("Unexpected result: " + res)
+    res = dev[0].request("BSS LAST MASK=0x1")
+    if "id=" + id2 not in res:
+        raise Exception("Unexpected result: " + res)
+    res = dev[0].request("BSS ID-" + id1 + " MASK=0x1")
+    if "id=" + id1 not in res:
+        raise Exception("Unexpected result: " + res)
+    res = dev[0].request("BSS NEXT-" + id1 + " MASK=0x1")
+    if "id=" + id2 not in res:
+        raise Exception("Unexpected result: " + res)
+    res = dev[0].request("BSS NEXT-" + id2 + " MASK=0x1")
+    if "id=" in res:
+        raise Exception("Unexpected result: " + res)
+
+    if len(dev[0].request("BSS RANGE=" + id2 + " MASK=0x1").splitlines()) != 0:
+        raise Exception("Unexpected RANGE=1 result")
+    if len(dev[0].request("BSS RANGE=" + id1 + "- MASK=0x1").splitlines()) != 2:
+        raise Exception("Unexpected RANGE=0- result")
+    if len(dev[0].request("BSS RANGE=-" + id2 + " MASK=0x1").splitlines()) != 2:
+        raise Exception("Unexpected RANGE=-1 result")
+    if len(dev[0].request("BSS RANGE=" + id1 + "-" + id2 + " MASK=0x1").splitlines()) != 2:
+        raise Exception("Unexpected RANGE=0-1 result")
+    if len(dev[0].request("BSS RANGE=" + id2 + "-" + id2 + " MASK=0x1").splitlines()) != 1:
+        raise Exception("Unexpected RANGE=1-1 result")
+    if len(dev[0].request("BSS RANGE=" + str(int(id2) + 1) + "-" + str(int(id2) + 10) + " MASK=0x1").splitlines()) != 0:
+        raise Exception("Unexpected RANGE=2-10 result")
+    if len(dev[0].request("BSS RANGE=0-" + str(int(id2) + 10) + " MASK=0x1").splitlines()) != 2:
+        raise Exception("Unexpected RANGE=0-10 result")
+    if len(dev[0].request("BSS RANGE=" + id1 + "-" + id1 + " MASK=0x1").splitlines()) != 1:
+        raise Exception("Unexpected RANGE=0-0 result")
+
+    res = dev[0].request("BSS p2p_dev_addr=FOO")
+    if "FAIL" in res or "id=" in res:
+        raise Exception("Unexpected result: " + res)
+    res = dev[0].request("BSS p2p_dev_addr=00:11:22:33:44:55")
+    if "FAIL" in res or "id=" in res:
+        raise Exception("Unexpected result: " + res)
+
+    dev[0].request("BSS_FLUSH 1000")
+    res = dev[0].request("BSS RANGE=ALL MASK=0x1").splitlines()
+    if len(res) != 2:
+        raise Exception("Unexpected result after BSS_FLUSH 1000")
+    dev[0].request("BSS_FLUSH 0")
+    res = dev[0].request("BSS RANGE=ALL MASK=0x1").splitlines()
+    if len(res) != 0:
+        raise Exception("Unexpected result after BSS_FLUSH 0")
+
+def test_scan_and_interface_disabled(dev, apdev):
+    """Scan operation when interface gets disabled"""
+    try:
+        dev[0].request("SCAN")
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
+        if ev is None:
+            raise Exception("Scan did not start")
+        dev[0].request("DRIVER_EVENT INTERFACE_DISABLED")
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=7)
+        if ev is not None:
+            raise Exception("Scan completed unexpectedly")
+
+        # verify that scan is rejected
+        if "FAIL" not in dev[0].request("SCAN"):
+            raise Exception("New scan request was accepted unexpectedly")
+
+        dev[0].request("DRIVER_EVENT INTERFACE_ENABLED")
+        dev[0].scan(freq="2412")
+    finally:
+        dev[0].request("DRIVER_EVENT INTERFACE_ENABLED")
+
+def test_scan_for_auth(dev, apdev):
+    """cfg80211 workaround with scan-for-auth"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    # Block sme-connect radio work with an external radio work item, so that
+    # SELECT_NETWORK can decide to use fast associate without a new scan while
+    # cfg80211 still has the matching BSS entry, but the actual connection is
+    # not yet started.
+    id = dev[0].request("RADIO_WORK add block-work")
+    ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to start")
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+                   wait_connect=False)
+    dev[0].dump_monitor()
+    # Clear cfg80211 BSS table.
+    try:
+        subprocess.check_call(['iw', dev[0].ifname, 'scan', 'trigger',
+                               'freq', '2457', 'flush'])
+    except subprocess.CalledProcessError, e:
+        raise HwsimSkip("iw scan trigger flush not supported")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+    if ev is None:
+        raise Exception("External flush scan timed out")
+    # Release blocking radio work to allow connection to go through with the
+    # cfg80211 BSS entry missing.
+    dev[0].request("RADIO_WORK done " + id)
+
+    dev[0].wait_connected(timeout=15)
+
+def test_scan_for_auth_fail(dev, apdev):
+    """cfg80211 workaround with scan-for-auth failing"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    # Block sme-connect radio work with an external radio work item, so that
+    # SELECT_NETWORK can decide to use fast associate without a new scan while
+    # cfg80211 still has the matching BSS entry, but the actual connection is
+    # not yet started.
+    id = dev[0].request("RADIO_WORK add block-work")
+    ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to start")
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+                   wait_connect=False)
+    dev[0].dump_monitor()
+    hapd.disable()
+    # Clear cfg80211 BSS table.
+    try:
+        subprocess.check_call(['iw', dev[0].ifname, 'scan', 'trigger',
+                               'freq', '2457', 'flush'])
+    except subprocess.CalledProcessError, e:
+        raise HwsimSkip("iw scan trigger flush not supported")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+    if ev is None:
+        raise Exception("External flush scan timed out")
+    # Release blocking radio work to allow connection to go through with the
+    # cfg80211 BSS entry missing.
+    dev[0].request("RADIO_WORK done " + id)
+
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS",
+                            "CTRL-EVENT-CONNECTED"], 15)
+    if ev is None:
+        raise Exception("Scan event missing")
+    if "CTRL-EVENT-CONNECTED" in ev:
+        raise Exception("Unexpected connection")
+    dev[0].request("DISCONNECT")
+
+def test_scan_for_auth_wep(dev, apdev):
+    """cfg80211 scan-for-auth workaround with WEP keys"""
+    dev[0].flush_scan_cache()
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": "wep", "wep_key0": '"abcde"',
+                            "auth_algs": "2" })
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+    # Block sme-connect radio work with an external radio work item, so that
+    # SELECT_NETWORK can decide to use fast associate without a new scan while
+    # cfg80211 still has the matching BSS entry, but the actual connection is
+    # not yet started.
+    id = dev[0].request("RADIO_WORK add block-work")
+    ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to start")
+    dev[0].connect("wep", key_mgmt="NONE", wep_key0='"abcde"',
+                   auth_alg="SHARED", scan_freq="2412", wait_connect=False)
+    dev[0].dump_monitor()
+    # Clear cfg80211 BSS table.
+    try:
+        subprocess.check_call(['iw', dev[0].ifname, 'scan', 'trigger',
+                               'freq', '2457', 'flush'])
+    except subprocess.CalledProcessError, e:
+        raise HwsimSkip("iw scan trigger flush not supported")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+    if ev is None:
+        raise Exception("External flush scan timed out")
+    # Release blocking radio work to allow connection to go through with the
+    # cfg80211 BSS entry missing.
+    dev[0].request("RADIO_WORK done " + id)
+
+    dev[0].wait_connected(timeout=15)
+
+def test_scan_hidden(dev, apdev):
+    """Control interface behavior on scan parameters"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-scan",
+                                                "ignore_broadcast_ssid": "1" })
+    bssid = apdev[0]['bssid']
+
+    check_scan(dev[0], "freq=2412 use_id=1")
+    if "test-scan" in dev[0].request("SCAN_RESULTS"):
+        raise Exception("BSS unexpectedly found in initial scan")
+
+    id1 = dev[0].connect("foo", key_mgmt="NONE", scan_ssid="1",
+                         only_add_network=True)
+    id2 = dev[0].connect("test-scan", key_mgmt="NONE", scan_ssid="1",
+                         only_add_network=True)
+    id3 = dev[0].connect("bar", key_mgmt="NONE", only_add_network=True)
+
+    check_scan(dev[0], "freq=2412 use_id=1")
+    if "test-scan" in dev[0].request("SCAN_RESULTS"):
+        raise Exception("BSS unexpectedly found in scan")
+
+    # Allow multiple attempts to be more robust under heavy CPU load that can
+    # result in Probe Response frames getting sent only after the station has
+    # already stopped waiting for the response on the channel.
+    found = False
+    for i in range(10):
+        check_scan(dev[0], "scan_id=%d,%d,%d freq=2412 use_id=1" % (id1, id2, id3))
+        if "test-scan" in dev[0].request("SCAN_RESULTS"):
+            found = True
+            break
+    if not found:
+        raise Exception("BSS not found in scan")
+
+    if "FAIL" not in dev[0].request("SCAN scan_id=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17"):
+        raise Exception("Too many scan_id values accepted")
+
+    dev[0].request("REMOVE_NETWORK all")
+    hapd.disable()
+    dev[0].flush_scan_cache(freq=2432)
+    dev[0].flush_scan_cache()
+
+def test_scan_and_bss_entry_removed(dev, apdev):
+    """Last scan result and connect work processing on BSS entry update"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open",
+                                                "eap_server": "1",
+                                                "wps_state": "2" })
+    bssid = apdev[0]['bssid']
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+
+    # Add a BSS entry
+    dev[0].scan_for_bss(bssid, freq="2412")
+    wpas.scan_for_bss(bssid, freq="2412")
+
+    # Start a connect radio work with a blocking entry preventing this from
+    # proceeding; this stores a pointer to the selected BSS entry.
+    id = dev[0].request("RADIO_WORK add block-work")
+    w_id = wpas.request("RADIO_WORK add block-work")
+    dev[0].wait_event(["EXT-RADIO-WORK-START"], timeout=1)
+    wpas.wait_event(["EXT-RADIO-WORK-START"], timeout=1)
+    nid = dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+                         wait_connect=False)
+    w_nid = wpas.connect("open", key_mgmt="NONE", scan_freq="2412",
+                         wait_connect=False)
+    time.sleep(0.1)
+
+    # Remove the BSS entry
+    dev[0].request("BSS_FLUSH 0")
+    wpas.request("BSS_FLUSH 0")
+
+    # Allow the connect radio work to continue. The bss entry stored in the
+    # pending connect work is now stale. This will result in the connection
+    # attempt failing since the BSS entry does not exist.
+    dev[0].request("RADIO_WORK done " + id)
+    wpas.request("RADIO_WORK done " + w_id)
+
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected connection")
+    dev[0].remove_network(nid)
+    ev = wpas.wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected connection")
+    wpas.remove_network(w_nid)
+    time.sleep(0.5)
+    dev[0].request("BSS_FLUSH 0")
+    wpas.request("BSS_FLUSH 0")
+
+    # Add a BSS entry
+    dev[0].scan_for_bss(bssid, freq="2412")
+    wpas.scan_for_bss(bssid, freq="2412")
+
+    # Start a connect radio work with a blocking entry preventing this from
+    # proceeding; this stores a pointer to the selected BSS entry.
+    id = dev[0].request("RADIO_WORK add block-work")
+    w_id = wpas.request("RADIO_WORK add block-work")
+    dev[0].wait_event(["EXT-RADIO-WORK-START"], timeout=1)
+    wpas.wait_event(["EXT-RADIO-WORK-START"], timeout=1)
+
+    # Schedule a connection based on the current BSS entry.
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+                   wait_connect=False)
+    wpas.connect("open", key_mgmt="NONE", scan_freq="2412",
+                 wait_connect=False)
+
+    # Update scan results with results that have longer set of IEs so that new
+    # memory needs to be allocated for the BSS entry.
+    hapd.request("WPS_PBC")
+    time.sleep(0.1)
+    subprocess.call(['iw', dev[0].ifname, 'scan', 'trigger', 'freq', '2412'])
+    subprocess.call(['iw', wpas.ifname, 'scan', 'trigger', 'freq', '2412'])
+    time.sleep(0.1)
+
+    # Allow the connect radio work to continue. The bss entry stored in the
+    # pending connect work becomes stale during the scan and it must have been
+    # updated for the connection to work.
+    dev[0].request("RADIO_WORK done " + id)
+    wpas.request("RADIO_WORK done " + w_id)
+
+    dev[0].wait_connected(timeout=15, error="No connection (sme-connect)")
+    wpas.wait_connected(timeout=15, error="No connection (connect)")
+    dev[0].request("DISCONNECT")
+    wpas.request("DISCONNECT")
+    dev[0].flush_scan_cache()
+    wpas.flush_scan_cache()
+
+def test_scan_reqs_with_non_scan_radio_work(dev, apdev):
+    """SCAN commands while non-scan radio_work is in progress"""
+    id = dev[0].request("RADIO_WORK add test-work-a")
+    ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+    if ev is None:
+        raise Exception("Timeout while waiting radio work to start")
+
+    if "OK" not in dev[0].request("SCAN"):
+        raise Exception("SCAN failed")
+    if "FAIL-BUSY" not in dev[0].request("SCAN"):
+        raise Exception("SCAN accepted while one is already pending")
+    if "FAIL-BUSY" not in dev[0].request("SCAN"):
+        raise Exception("SCAN accepted while one is already pending")
+
+    res = dev[0].request("RADIO_WORK show").splitlines()
+    count = 0
+    for l in res:
+        if "scan" in l:
+            count += 1
+    if count != 1:
+        logger.info(res)
+        raise Exception("Unexpected number of scan radio work items")
+
+    dev[0].dump_monitor()
+    dev[0].request("RADIO_WORK done " + id)
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+    if ev is None:
+        raise Exception("Scan did not start")
+    if "FAIL-BUSY" not in dev[0].request("SCAN"):
+        raise Exception("SCAN accepted while one is already in progress")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+    if ev is None:
+        print "Scan did not complete"
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.2)
+    if ev is not None:
+        raise Exception("Unexpected scan started")
+
+def test_scan_setband(dev, apdev):
+    """Band selection for scan operations"""
+    try:
+        hapd = None
+        hapd2 = None
+        params = { "ssid": "test-setband",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "country_code": "US" }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        bssid = apdev[0]['bssid']
+
+        params = { "ssid": "test-setband",
+                   "hw_mode": "g",
+                   "channel": "1" }
+        hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+        bssid2 = apdev[1]['bssid']
+
+        if "FAIL" not in dev[0].request("SET setband FOO"):
+            raise Exception("Invalid set setband accepted")
+        if "OK" not in dev[0].request("SET setband AUTO"):
+            raise Exception("Failed to set setband")
+        if "OK" not in dev[1].request("SET setband 5G"):
+            raise Exception("Failed to set setband")
+        if "OK" not in dev[2].request("SET setband 2G"):
+            raise Exception("Failed to set setband")
+
+        for i in range(3):
+            dev[i].request("SCAN only_new=1")
+
+        for i in range(3):
+            ev = dev[i].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+            if ev is None:
+                raise Exception("Scan timed out")
+
+        res = dev[0].request("SCAN_RESULTS")
+        if bssid not in res or bssid2 not in res:
+            raise Exception("Missing scan result(0)")
+
+        res = dev[1].request("SCAN_RESULTS")
+        if bssid not in res:
+            raise Exception("Missing scan result(1)")
+        if bssid2 in res:
+            raise Exception("Unexpected scan result(1)")
+
+        res = dev[2].request("SCAN_RESULTS")
+        if bssid2 not in res:
+            raise Exception("Missing scan result(2)")
+        if bssid in res:
+            raise Exception("Unexpected scan result(2)")
+    finally:
+        if hapd:
+            hapd.request("DISABLE")
+        if hapd2:
+            hapd2.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        for i in range(3):
+            dev[i].request("SET setband AUTO")
+            dev[i].flush_scan_cache()
+
+def test_scan_hidden_many(dev, apdev):
+    """scan_ssid=1 with large number of profile with hidden SSID"""
+    try:
+        _test_scan_hidden_many(dev, apdev)
+    finally:
+        dev[0].flush_scan_cache(freq=2432)
+        dev[0].flush_scan_cache()
+        dev[0].request("SCAN_INTERVAL 5")
+
+def _test_scan_hidden_many(dev, apdev):
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-scan-ssid",
+                                                "ignore_broadcast_ssid": "1" })
+    bssid = apdev[0]['bssid']
+
+    dev[0].request("SCAN_INTERVAL 1")
+
+    for i in range(5):
+        id = dev[0].add_network()
+        dev[0].set_network_quoted(id, "ssid", "foo")
+        dev[0].set_network(id, "key_mgmt", "NONE")
+        dev[0].set_network(id, "disabled", "0")
+        dev[0].set_network(id, "scan_freq", "2412")
+        dev[0].set_network(id, "scan_ssid", "1")
+
+    dev[0].set_network_quoted(id, "ssid", "test-scan-ssid")
+    dev[0].set_network(id, "key_mgmt", "NONE")
+    dev[0].set_network(id, "disabled", "0")
+    dev[0].set_network(id, "scan_freq", "2412")
+    dev[0].set_network(id, "scan_ssid", "1")
+
+    for i in range(5):
+        id = dev[0].add_network()
+        dev[0].set_network_quoted(id, "ssid", "foo")
+        dev[0].set_network(id, "key_mgmt", "NONE")
+        dev[0].set_network(id, "disabled", "0")
+        dev[0].set_network(id, "scan_freq", "2412")
+        dev[0].set_network(id, "scan_ssid", "1")
+
+    dev[0].request("REASSOCIATE")
+    dev[0].wait_connected(timeout=30)
+    dev[0].request("REMOVE_NETWORK all")
+    hapd.disable()
+
+def test_scan_random_mac(dev, apdev, params):
+    """Random MAC address in scans"""
+    try:
+        _test_scan_random_mac(dev, apdev, params)
+    finally:
+        dev[0].request("MAC_RAND_SCAN all enable=0")
+
+def _test_scan_random_mac(dev, apdev, params):
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-scan" })
+    bssid = apdev[0]['bssid']
+
+    tests = [ "",
+              "addr=foo",
+              "mask=foo",
+              "enable=1",
+              "all enable=1 mask=00:11:22:33:44:55",
+              "all enable=1 addr=00:11:22:33:44:55",
+              "all enable=1 addr=01:11:22:33:44:55 mask=ff:ff:ff:ff:ff:ff",
+              "all enable=1 addr=00:11:22:33:44:55 mask=fe:ff:ff:ff:ff:ff",
+              "enable=2 scan sched pno all",
+              "pno enable=1",
+              "all enable=2",
+              "foo" ]
+    for args in tests:
+        if "FAIL" not in dev[0].request("MAC_RAND_SCAN " + args):
+            raise Exception("Invalid MAC_RAND_SCAN accepted: " + args)
+
+    if dev[0].get_driver_status_field('capa.mac_addr_rand_scan_supported') != '1':
+        raise HwsimSkip("Driver does not support random MAC address for scanning")
+
+    tests = [ "all enable=1",
+              "all enable=1 addr=f2:11:22:33:44:55 mask=ff:ff:ff:ff:ff:ff",
+              "all enable=1 addr=f2:11:33:00:00:00 mask=ff:ff:ff:00:00:00" ]
+    for args in tests:
+        dev[0].request("MAC_RAND_SCAN " + args)
+        dev[0].scan_for_bss(bssid, freq=2412, force_scan=True)
+
+    out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+                     "wlan.fc.type_subtype == 4", ["wlan.ta" ])
+    if out is not None:
+        addr = out.splitlines()
+        logger.info("Probe Request frames seen from: " + str(addr))
+        if dev[0].own_addr() in addr:
+            raise Exception("Real address used to transmit Probe Request frame")
+        if "f2:11:22:33:44:55" not in addr:
+            raise Exception("Fully configured random address not seen")
+        found = False
+        for a in addr:
+            if a.startswith('f2:11:33'):
+                found = True
+                break
+        if not found:
+            raise Exception("Fixed OUI random address not seen")
+
+def test_scan_trigger_failure(dev, apdev):
+    """Scan trigger to the driver failing"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-scan" })
+    bssid = apdev[0]['bssid']
+
+    if "OK" not in dev[0].request("SET test_failure 1"):
+        raise Exception("Failed to set test_failure")
+
+    if "OK" not in dev[0].request("SCAN"):
+        raise Exception("SCAN command failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED"], timeout=10)
+    if ev is None:
+        raise Exception("Did not receive CTRL-EVENT-SCAN-FAILED event")
+    if "retry=1" in ev:
+        raise Exception("Unexpected scan retry indicated")
+    if dev[0].get_status_field('wpa_state') == "SCANNING":
+        raise Exception("wpa_state SCANNING not cleared")
+
+    id = dev[0].connect("test-scan", key_mgmt="NONE", scan_freq="2412",
+                        only_add_network=True)
+    dev[0].select_network(id)
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED"], timeout=10)
+    if ev is None:
+        raise Exception("Did not receive CTRL-EVENT-SCAN-FAILED event")
+    if "retry=1" not in ev:
+        raise Exception("No scan retry indicated for connection")
+    if dev[0].get_status_field('wpa_state') == "SCANNING":
+        raise Exception("wpa_state SCANNING not cleared")
+    dev[0].request("SET test_failure 0")
+    dev[0].wait_connected()
+
+    dev[0].request("SET test_failure 1")
+    if "OK" not in dev[0].request("SCAN"):
+        raise Exception("SCAN command failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED"], timeout=10)
+    if ev is None:
+        raise Exception("Did not receive CTRL-EVENT-SCAN-FAILED event")
+    if "retry=1" in ev:
+        raise Exception("Unexpected scan retry indicated")
+    if dev[0].get_status_field('wpa_state') != "COMPLETED":
+        raise Exception("wpa_state COMPLETED not restored")
+    dev[0].request("SET test_failure 0")
+
+def test_scan_specify_ssid(dev, apdev):
+    """Control interface behavior on scan SSID parameter"""
+    dev[0].flush_scan_cache()
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-hidden",
+                                                "ignore_broadcast_ssid": "1" })
+    bssid = apdev[0]['bssid']
+    check_scan(dev[0], "freq=2412 use_id=1 ssid 414243")
+    bss = dev[0].get_bss(bssid)
+    if bss is not None and bss['ssid'] == 'test-hidden':
+        raise Exception("BSS entry for hidden AP present unexpectedly")
+    check_scan(dev[0], "freq=2412 ssid 414243 ssid 746573742d68696464656e ssid 616263313233 use_id=1")
+    bss = dev[0].get_bss(bssid)
+    if bss is None:
+        raise Exception("BSS entry for hidden AP not found")
+    if 'test-hidden' not in dev[0].request("SCAN_RESULTS"):
+        raise Exception("Expected SSID not included in the scan results");
+
+    hapd.disable()
+    dev[0].flush_scan_cache(freq=2432)
+    dev[0].flush_scan_cache()
+
+    if "FAIL" not in dev[0].request("SCAN ssid foo"):
+        raise Exception("Invalid SCAN command accepted")
+
+def test_scan_ap_scan_2_ap_mode(dev, apdev):
+    """AP_SCAN 2 AP mode and scan()"""
+    try:
+        _test_scan_ap_scan_2_ap_mode(dev, apdev)
+    finally:
+        dev[0].request("AP_SCAN 1")
+
+def _test_scan_ap_scan_2_ap_mode(dev, apdev):
+    if "OK" not in dev[0].request("AP_SCAN 2"):
+        raise Exception("Failed to set AP_SCAN 2")
+
+    id = dev[0].add_network()
+    dev[0].set_network(id, "mode", "2")
+    dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+    dev[0].set_network(id, "key_mgmt", "NONE")
+    dev[0].set_network(id, "frequency", "2412")
+    dev[0].set_network(id, "scan_freq", "2412")
+    dev[0].set_network(id, "disabled", "0")
+    dev[0].select_network(id)
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=5)
+    if ev is None:
+        raise Exception("AP failed to start")
+
+    with fail_test(dev[0], 1, "wpa_driver_nl80211_scan"):
+        if "OK" not in dev[0].request("SCAN freq=2412"):
+            raise Exception("SCAN command failed unexpectedly")
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED",
+                                "AP-DISABLED"], timeout=5)
+        if ev is None:
+            raise Exception("CTRL-EVENT-SCAN-FAILED not seen")
+        if "AP-DISABLED" in ev:
+            raise Exception("Unexpected AP-DISABLED event")
+        if "retry=1" in ev:
+            # Wait for the retry to scan happen
+            ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED",
+                                    "AP-DISABLED"], timeout=5)
+            if ev is None:
+                raise Exception("CTRL-EVENT-SCAN-FAILED not seen - retry")
+            if "AP-DISABLED" in ev:
+                raise Exception("Unexpected AP-DISABLED event - retry")
+
+    dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+    dev[1].request("DISCONNECT")
+    dev[1].wait_disconnected()
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+
+def test_scan_bss_expiration_on_ssid_change(dev, apdev):
+    """BSS entry expiration when AP changes SSID"""
+    dev[0].flush_scan_cache()
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-scan" })
+    bssid = apdev[0]['bssid']
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+
+    hapd.request("DISABLE")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    if "OK" not in dev[0].request("BSS_EXPIRE_COUNT 3"):
+        raise Exception("BSS_EXPIRE_COUNT failed")
+    dev[0].scan(freq="2412")
+    dev[0].scan(freq="2412")
+    if "OK" not in dev[0].request("BSS_EXPIRE_COUNT 2"):
+        raise Exception("BSS_EXPIRE_COUNT failed")
+    res = dev[0].request("SCAN_RESULTS")
+    if "test-scan" not in res:
+        raise Exception("The first SSID not in scan results")
+    if "open" not in res:
+        raise Exception("The second SSID not in scan results")
+    dev[0].connect("open", key_mgmt="NONE")
+
+    dev[0].request("BSS_FLUSH 0")
+    res = dev[0].request("SCAN_RESULTS")
+    if "test-scan" in res:
+        raise Exception("The BSS entry with the old SSID was not removed")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
diff --git a/hostap/tests/hwsim/test_ssid.py b/hostap/tests/hwsim/test_ssid.py
new file mode 100644
index 0000000..ca0479e
--- /dev/null
+++ b/hostap/tests/hwsim/test_ssid.py
@@ -0,0 +1,119 @@
+# -*- coding: utf-8 -*-
+# SSID contents and encoding tests
+# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+
+import hostapd
+
+def test_ssid_hex_encoded(dev, apdev):
+    """SSID configuration using hex encoded version"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid2": '68656c6c6f' })
+    dev[0].connect("hello", key_mgmt="NONE", scan_freq="2412")
+    dev[1].connect(ssid2="68656c6c6f", key_mgmt="NONE", scan_freq="2412")
+
+def test_ssid_printf_encoded(dev, apdev):
+    """SSID configuration using printf encoded version"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid2": 'P"\\0hello\\nthere"' })
+    dev[0].connect(ssid2="0068656c6c6f0a7468657265", key_mgmt="NONE",
+                   scan_freq="2412")
+    dev[1].connect(ssid2='P"\\x00hello\\nthere"', key_mgmt="NONE",
+                   scan_freq="2412")
+    ssid = dev[0].get_status_field("ssid")
+    bss = dev[1].get_bss(apdev[0]['bssid'])
+    if ssid != bss['ssid']:
+        raise Exception("Unexpected difference in SSID")
+    dev[2].connect(ssid2='P"' + ssid + '"', key_mgmt="NONE", scan_freq="2412")
+
+def test_ssid_1_octet(dev, apdev):
+    """SSID with one octet"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": '1' })
+    dev[0].connect("1", key_mgmt="NONE", scan_freq="2412")
+
+def test_ssid_32_octets(dev, apdev):
+    """SSID with 32 octets"""
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": '1234567890abcdef1234567890ABCDEF' })
+    dev[0].connect("1234567890abcdef1234567890ABCDEF", key_mgmt="NONE",
+                   scan_freq="2412")
+
+def test_ssid_utf8(dev, apdev):
+    """SSID with UTF8 encoding"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": 'testi-åäöÅÄÖ-testi',
+                                                "utf8_ssid": "1" })
+    dev[0].connect("testi-åäöÅÄÖ-testi", key_mgmt="NONE", scan_freq="2412")
+    dev[1].connect(ssid2="74657374692dc3a5c3a4c3b6c385c384c3962d7465737469",
+                   key_mgmt="NONE", scan_freq="2412")
+    # verify ctrl_iface for coverage
+    addrs = [ dev[0].p2p_interface_addr(), dev[1].p2p_interface_addr() ]
+    sta = hapd.get_sta(None)
+    if sta['addr'] not in addrs:
+        raise Exception("Unexpected STA address")
+    sta2 = hapd.get_sta(sta['addr'], next=True)
+    if sta2['addr'] not in addrs:
+        raise Exception("Unexpected STA2 address")
+    sta3 = hapd.get_sta(sta2['addr'], next=True)
+    if len(sta3) != 0:
+        raise Exception("Unexpected STA iteration result (did not stop)")
+
+def test_ssid_hidden(dev, apdev):
+    """Hidden SSID"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": 'secret',
+                                                "ignore_broadcast_ssid": "1" })
+    dev[1].connect("secret", key_mgmt="NONE", scan_freq="2412",
+                   wait_connect=False)
+    dev[0].connect("secret", key_mgmt="NONE", scan_freq="2412", scan_ssid="1")
+    ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected connection")
+    dev[0].request("DISCONNECT")
+    dev[1].request("DISCONNECT")
+    # clear BSS table to avoid issues in following test cases
+    hapd.disable()
+    dev[0].request("BSS_FLUSH 0")
+    dev[0].request("SCAN freq=2412 only_new=1")
+    dev[1].request("BSS_FLUSH 0")
+    dev[1].request("SCAN freq=2412 only_new=1")
+
+def test_ssid_hidden2(dev, apdev):
+    """Hidden SSID using zero octets as payload"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": 'secret2',
+                                                "ignore_broadcast_ssid": "2" })
+    dev[1].connect("secret2", key_mgmt="NONE", scan_freq="2412",
+                   wait_connect=False)
+    dev[0].connect("secret2", key_mgmt="NONE", scan_freq="2412", scan_ssid="1")
+    ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected connection")
+    dev[0].request("DISCONNECT")
+    dev[1].request("DISCONNECT")
+    # clear BSS table to avoid issues in following test cases
+    hapd.disable()
+    dev[0].request("BSS_FLUSH 0")
+    dev[0].request("SCAN freq=2412 only_new=1")
+    dev[1].request("BSS_FLUSH 0")
+    dev[1].request("SCAN freq=2412 only_new=1")
+
+def test_ssid_hidden_wpa2(dev, apdev):
+    """Hidden SSID with WPA2-PSK"""
+    params = hostapd.wpa2_params(ssid="secret", passphrase="12345678")
+    params["ignore_broadcast_ssid"] = "1"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[1].connect("secret", psk="12345678", scan_freq="2412",
+                   wait_connect=False)
+    dev[0].connect("secret", psk="12345678", scan_freq="2412", scan_ssid="1")
+    ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected connection")
+    dev[0].request("DISCONNECT")
+    dev[1].request("DISCONNECT")
+    # clear BSS table to avoid issues in following test cases
+    hapd.disable()
+    dev[0].request("BSS_FLUSH 0")
+    dev[0].request("SCAN freq=2412 only_new=1")
+    dev[1].request("BSS_FLUSH 0")
+    dev[1].request("SCAN freq=2412 only_new=1")
diff --git a/hostap/tests/hwsim/test_sta_dynamic.py b/hostap/tests/hwsim/test_sta_dynamic.py
new file mode 100644
index 0000000..49c1ae4
--- /dev/null
+++ b/hostap/tests/hwsim/test_sta_dynamic.py
@@ -0,0 +1,276 @@
+# Dynamic wpa_supplicant interface
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import subprocess
+import time
+
+import hwsim_utils
+import hostapd
+from wpasupplicant import WpaSupplicant
+
+def test_sta_dynamic(dev, apdev):
+    """Dynamically added wpa_supplicant interface"""
+    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    logger.info("Create a dynamic wpa_supplicant interface and connect")
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+
+    wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
+
+def test_sta_ap_scan_0(dev, apdev):
+    """Dynamically added wpa_supplicant interface with AP_SCAN 0 connection"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test" })
+    bssid = apdev[0]['bssid']
+
+    logger.info("Create a dynamic wpa_supplicant interface and connect")
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+
+    if "OK" not in wpas.request("AP_SCAN 0"):
+        raise Exception("Failed to set AP_SCAN 2")
+
+    id = wpas.connect("", key_mgmt="NONE", bssid=bssid,
+                      only_add_network=True)
+    wpas.request("ENABLE_NETWORK " + str(id) + " no-connect")
+    wpas.request("SCAN")
+    time.sleep(0.5)
+    subprocess.call(['iw', wpas.ifname, 'connect', 'test', '2412'])
+    wpas.wait_connected(timeout=10)
+    wpas.request("SCAN")
+    wpas.wait_connected(timeout=5)
+
+def test_sta_ap_scan_2(dev, apdev):
+    """Dynamically added wpa_supplicant interface with AP_SCAN 2 connection"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test" })
+    bssid = apdev[0]['bssid']
+
+    logger.info("Create a dynamic wpa_supplicant interface and connect")
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+
+    if "FAIL" not in wpas.request("AP_SCAN -1"):
+        raise Exception("Invalid AP_SCAN -1 accepted")
+    if "FAIL" not in wpas.request("AP_SCAN 3"):
+        raise Exception("Invalid AP_SCAN 3 accepted")
+    if "OK" not in wpas.request("AP_SCAN 2"):
+        raise Exception("Failed to set AP_SCAN 2")
+
+    id = wpas.connect("", key_mgmt="NONE", bssid=bssid,
+                      only_add_network=True)
+    wpas.request("ENABLE_NETWORK " + str(id) + " no-connect")
+    subprocess.call(['iw', wpas.ifname, 'scan', 'trigger', 'freq', '2412'])
+    time.sleep(1)
+    subprocess.call(['iw', wpas.ifname, 'connect', 'test', '2412'])
+    wpas.wait_connected(timeout=10)
+
+    wpas.request("SET disallow_aps bssid " + bssid)
+    wpas.wait_disconnected(timeout=10)
+
+    subprocess.call(['iw', wpas.ifname, 'connect', 'test', '2412'])
+    ev = wpas.wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected connection reported")
+
+def test_sta_ap_scan_2b(dev, apdev):
+    """Dynamically added wpa_supplicant interface with AP_SCAN 2 operation"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test" })
+    bssid = apdev[0]['bssid']
+
+    logger.info("Create a dynamic wpa_supplicant interface and connect")
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+
+    if "OK" not in wpas.request("AP_SCAN 2"):
+        raise Exception("Failed to set AP_SCAN 2")
+
+    id = wpas.connect("test", key_mgmt="NONE", bssid=bssid)
+    wpas.request("DISCONNECT")
+    wpas.set_network(id, "disabled", "1")
+    id2 = wpas.add_network()
+    wpas.set_network_quoted(id2, "ssid", "test2")
+    wpas.set_network(id2, "key_mgmt", "NONE")
+    wpas.set_network(id2, "disabled", "0")
+    wpas.request("REASSOCIATE")
+    ev = wpas.wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=15)
+    if ev is None:
+        raise Exception("Association rejection not reported")
+    hapd.disable()
+    wpas.set_network(id, "disabled", "0")
+    wpas.set_network(id2, "disabled", "1")
+    for i in range(3):
+        ev = wpas.wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=15)
+        if ev is None:
+            raise Exception("Association rejection not reported")
+    wpas.request("DISCONNECT")
+
+def test_sta_dynamic_down_up(dev, apdev):
+    """Dynamically added wpa_supplicant interface down/up"""
+    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    logger.info("Create a dynamic wpa_supplicant interface and connect")
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
+    hwsim_utils.test_connectivity(wpas, hapd)
+    subprocess.call(['ifconfig', wpas.ifname, 'down'])
+    wpas.wait_disconnected(timeout=10)
+    if wpas.get_status_field("wpa_state") != "INTERFACE_DISABLED":
+        raise Exception("Unexpected wpa_state")
+    subprocess.call(['ifconfig', wpas.ifname, 'up'])
+    wpas.wait_connected(timeout=15, error="Reconnection not reported")
+    hwsim_utils.test_connectivity(wpas, hapd)
+
+def test_sta_dynamic_ext_mac_addr_change(dev, apdev):
+    """Dynamically added wpa_supplicant interface with external MAC address change"""
+    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    logger.info("Create a dynamic wpa_supplicant interface and connect")
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
+    hwsim_utils.test_connectivity(wpas, hapd)
+    subprocess.call(['ifconfig', wpas.ifname, 'down'])
+    wpas.wait_disconnected(timeout=10)
+    if wpas.get_status_field("wpa_state") != "INTERFACE_DISABLED":
+        raise Exception("Unexpected wpa_state")
+    prev_addr = wpas.p2p_interface_addr()
+    new_addr = '02:11:22:33:44:55'
+    try:
+        subprocess.call(['ip', 'link', 'set', 'dev', wpas.ifname,
+                         'address', new_addr])
+        subprocess.call(['ifconfig', wpas.ifname, 'up'])
+        wpas.wait_connected(timeout=15, error="Reconnection not reported")
+        if wpas.get_driver_status_field('addr') != new_addr:
+            raise Exception("Address change not reported")
+        hwsim_utils.test_connectivity(wpas, hapd)
+        sta = hapd.get_sta(new_addr)
+        if sta['addr'] != new_addr:
+            raise Exception("STA association with new address not found")
+    finally:
+        subprocess.call(['ifconfig', wpas.ifname, 'down'])
+        subprocess.call(['ip', 'link', 'set', 'dev', wpas.ifname,
+                         'address', prev_addr])
+        subprocess.call(['ifconfig', wpas.ifname, 'up'])
+
+def test_sta_dynamic_random_mac_addr(dev, apdev):
+    """Dynamically added wpa_supplicant interface and random MAC address"""
+    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    addr0 = wpas.get_driver_status_field("addr")
+    wpas.request("SET preassoc_mac_addr 1")
+    wpas.request("SET rand_addr_lifetime 0")
+
+    id = wpas.connect("sta-dynamic", psk="12345678", mac_addr="1",
+                      scan_freq="2412")
+    addr1 = wpas.get_driver_status_field("addr")
+
+    if addr0 == addr1:
+        raise Exception("Random MAC address not used")
+
+    sta = hapd.get_sta(addr0)
+    if sta['addr'] != "FAIL":
+        raise Exception("Unexpected STA association with permanent address")
+    sta = hapd.get_sta(addr1)
+    if sta['addr'] != addr1:
+        raise Exception("STA association with random address not found")
+
+    wpas.request("DISCONNECT")
+    wpas.connect_network(id)
+    addr2 = wpas.get_driver_status_field("addr")
+    if addr1 != addr2:
+        raise Exception("Random MAC address changed unexpectedly")
+
+    wpas.remove_network(id)
+    id = wpas.connect("sta-dynamic", psk="12345678", mac_addr="1",
+                      scan_freq="2412")
+    addr2 = wpas.get_driver_status_field("addr")
+    if addr1 == addr2:
+        raise Exception("Random MAC address did not change")
+
+def test_sta_dynamic_random_mac_addr_keep_oui(dev, apdev):
+    """Dynamically added wpa_supplicant interface and random MAC address (keep OUI)"""
+    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    addr0 = wpas.get_driver_status_field("addr")
+    wpas.request("SET preassoc_mac_addr 2")
+    wpas.request("SET rand_addr_lifetime 0")
+
+    id = wpas.connect("sta-dynamic", psk="12345678", mac_addr="2",
+                      scan_freq="2412")
+    addr1 = wpas.get_driver_status_field("addr")
+
+    if addr0 == addr1:
+        raise Exception("Random MAC address not used")
+    if addr1[3:8] != addr0[3:8]:
+        raise Exception("OUI was not kept")
+
+    sta = hapd.get_sta(addr0)
+    if sta['addr'] != "FAIL":
+        raise Exception("Unexpected STA association with permanent address")
+    sta = hapd.get_sta(addr1)
+    if sta['addr'] != addr1:
+        raise Exception("STA association with random address not found")
+
+    wpas.request("DISCONNECT")
+    wpas.connect_network(id)
+    addr2 = wpas.get_driver_status_field("addr")
+    if addr1 != addr2:
+        raise Exception("Random MAC address changed unexpectedly")
+
+    wpas.remove_network(id)
+    id = wpas.connect("sta-dynamic", psk="12345678", mac_addr="2",
+                      scan_freq="2412")
+    addr2 = wpas.get_driver_status_field("addr")
+    if addr1 == addr2:
+        raise Exception("Random MAC address did not change")
+    if addr2[3:8] != addr0[3:8]:
+        raise Exception("OUI was not kept")
+
+def test_sta_dynamic_random_mac_addr_scan(dev, apdev):
+    """Dynamically added wpa_supplicant interface and random MAC address for scan"""
+    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    addr0 = wpas.get_driver_status_field("addr")
+    wpas.request("SET preassoc_mac_addr 1")
+    wpas.request("SET rand_addr_lifetime 0")
+
+    id = wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
+    addr1 = wpas.get_driver_status_field("addr")
+
+    if addr0 != addr1:
+        raise Exception("Random MAC address used unexpectedly")
+
+def test_sta_dynamic_random_mac_addr_scan_keep_oui(dev, apdev):
+    """Dynamically added wpa_supplicant interface and random MAC address for scan (keep OUI)"""
+    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    addr0 = wpas.get_driver_status_field("addr")
+    wpas.request("SET preassoc_mac_addr 2")
+    wpas.request("SET rand_addr_lifetime 0")
+
+    id = wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
+    addr1 = wpas.get_driver_status_field("addr")
+
+    if addr0 != addr1:
+        raise Exception("Random MAC address used unexpectedly")
diff --git a/hostap/tests/hwsim/test_suite_b.py b/hostap/tests/hwsim/test_suite_b.py
new file mode 100644
index 0000000..1a32c69
--- /dev/null
+++ b/hostap/tests/hwsim/test_suite_b.py
@@ -0,0 +1,213 @@
+# Suite B tests
+# Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import logging
+logger = logging.getLogger()
+
+import hostapd
+from utils import HwsimSkip
+
+def check_suite_b_capa(dev):
+    if "GCMP" not in dev[0].get_capability("pairwise"):
+        raise HwsimSkip("GCMP not supported")
+    if "BIP-GMAC-128" not in dev[0].get_capability("group_mgmt"):
+        raise HwsimSkip("BIP-GMAC-128 not supported")
+    if "WPA-EAP-SUITE-B" not in dev[0].get_capability("key_mgmt"):
+        raise HwsimSkip("WPA-EAP-SUITE-B not supported")
+    check_suite_b_tls_lib(dev)
+
+def check_suite_b_tls_lib(dev):
+    tls = dev[0].request("GET tls_library")
+    if not tls.startswith("OpenSSL"):
+        raise HwsimSkip("TLS library not supported for Suite B: " + tls);
+    supported = False
+    for ver in [ '1.0.2', '1.1.0' ]:
+        if "build=OpenSSL " + ver in tls and "run=OpenSSL " + ver in tls:
+            supported = True
+            break
+    if not supported:
+        raise HwsimSkip("OpenSSL version not supported for Suite B: " + tls)
+
+def test_suite_b(dev, apdev):
+    """WPA2/GCMP connection at Suite B 128-bit level"""
+    check_suite_b_capa(dev)
+    dev[0].flush_scan_cache()
+    params = { "ssid": "test-suite-b",
+               "wpa": "2",
+               "wpa_key_mgmt": "WPA-EAP-SUITE-B",
+               "rsn_pairwise": "GCMP",
+               "group_mgmt_cipher": "BIP-GMAC-128",
+               "ieee80211w": "2",
+               "ieee8021x": "1",
+               "openssl_ciphers": "SUITEB128",
+               #"dh_file": "auth_serv/dh.conf",
+               "eap_server": "1",
+               "eap_user_file": "auth_serv/eap_user.conf",
+               "ca_cert": "auth_serv/ec-ca.pem",
+               "server_cert": "auth_serv/ec-server.pem",
+               "private_key": "auth_serv/ec-server.key" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B", ieee80211w="2",
+                   openssl_ciphers="SUITEB128",
+                   eap="TLS", identity="tls user",
+                   ca_cert="auth_serv/ec-ca.pem",
+                   client_cert="auth_serv/ec-user.pem",
+                   private_key="auth_serv/ec-user.key",
+                   pairwise="GCMP", group="GCMP", scan_freq="2412")
+    tls_cipher = dev[0].get_status_field("EAP TLS cipher")
+    if tls_cipher != "ECDHE-ECDSA-AES128-GCM-SHA256":
+        raise Exception("Unexpected TLS cipher: " + tls_cipher)
+
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    if 'flags' not in bss:
+        raise Exception("Could not get BSS flags from BSS table")
+    if "[WPA2-EAP-SUITE-B-GCMP]" not in bss['flags']:
+        raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=20)
+    dev[0].dump_monitor()
+    dev[0].request("RECONNECT")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+                            "CTRL-EVENT-CONNECTED"], timeout=20)
+    if ev is None:
+        raise Exception("Roaming with the AP timed out")
+    if "CTRL-EVENT-EAP-STARTED" in ev:
+        raise Exception("Unexpected EAP exchange")
+
+def suite_b_as_params():
+    params = {}
+    params['ssid'] = 'as'
+    params['beacon_int'] = '2000'
+    params['radius_server_clients'] = 'auth_serv/radius_clients.conf'
+    params['radius_server_auth_port'] = '18129'
+    params['eap_server'] = '1'
+    params['eap_user_file'] = 'auth_serv/eap_user.conf'
+    params['ca_cert'] = 'auth_serv/ec-ca.pem'
+    params['server_cert'] = 'auth_serv/ec-server.pem'
+    params['private_key'] = 'auth_serv/ec-server.key'
+    params['openssl_ciphers'] = 'SUITEB128'
+    return params
+
+def test_suite_b_radius(dev, apdev):
+    """WPA2/GCMP (RADIUS) connection at Suite B 128-bit level"""
+    check_suite_b_capa(dev)
+    dev[0].flush_scan_cache()
+    params = suite_b_as_params()
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    params = { "ssid": "test-suite-b",
+               "wpa": "2",
+               "wpa_key_mgmt": "WPA-EAP-SUITE-B",
+               "rsn_pairwise": "GCMP",
+               "group_mgmt_cipher": "BIP-GMAC-128",
+               "ieee80211w": "2",
+               "ieee8021x": "1",
+               'auth_server_addr': "127.0.0.1",
+               'auth_server_port': "18129",
+               'auth_server_shared_secret': "radius",
+               'nas_identifier': "nas.w1.fi" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B", ieee80211w="2",
+                   openssl_ciphers="SUITEB128",
+                   eap="TLS", identity="tls user",
+                   ca_cert="auth_serv/ec-ca.pem",
+                   client_cert="auth_serv/ec-user.pem",
+                   private_key="auth_serv/ec-user.key",
+                   pairwise="GCMP", group="GCMP", scan_freq="2412")
+
+def check_suite_b_192_capa(dev):
+    if "GCMP-256" not in dev[0].get_capability("pairwise"):
+        raise HwsimSkip("GCMP-256 not supported")
+    if "BIP-GMAC-256" not in dev[0].get_capability("group_mgmt"):
+        raise HwsimSkip("BIP-GMAC-256 not supported")
+    if "WPA-EAP-SUITE-B-192" not in dev[0].get_capability("key_mgmt"):
+        raise HwsimSkip("WPA-EAP-SUITE-B-192 not supported")
+    check_suite_b_tls_lib(dev)
+
+def test_suite_b_192(dev, apdev):
+    """WPA2/GCMP-256 connection at Suite B 192-bit level"""
+    check_suite_b_192_capa(dev)
+    dev[0].flush_scan_cache()
+    params = { "ssid": "test-suite-b",
+               "wpa": "2",
+               "wpa_key_mgmt": "WPA-EAP-SUITE-B-192",
+               "rsn_pairwise": "GCMP-256",
+               "group_mgmt_cipher": "BIP-GMAC-256",
+               "ieee80211w": "2",
+               "ieee8021x": "1",
+               "openssl_ciphers": "SUITEB192",
+               "eap_server": "1",
+               "eap_user_file": "auth_serv/eap_user.conf",
+               "ca_cert": "auth_serv/ec2-ca.pem",
+               "server_cert": "auth_serv/ec2-server.pem",
+               "private_key": "auth_serv/ec2-server.key" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+                   ieee80211w="2",
+                   openssl_ciphers="SUITEB192",
+                   eap="TLS", identity="tls user",
+                   ca_cert="auth_serv/ec2-ca.pem",
+                   client_cert="auth_serv/ec2-user.pem",
+                   private_key="auth_serv/ec2-user.key",
+                   pairwise="GCMP-256", group="GCMP-256", scan_freq="2412")
+    tls_cipher = dev[0].get_status_field("EAP TLS cipher")
+    if tls_cipher != "ECDHE-ECDSA-AES256-GCM-SHA384":
+        raise Exception("Unexpected TLS cipher: " + tls_cipher)
+
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    if 'flags' not in bss:
+        raise Exception("Could not get BSS flags from BSS table")
+    if "[WPA2-EAP-SUITE-B-192-GCMP-256]" not in bss['flags']:
+        raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=20)
+    dev[0].dump_monitor()
+    dev[0].request("RECONNECT")
+    ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+                            "CTRL-EVENT-CONNECTED"], timeout=20)
+    if ev is None:
+        raise Exception("Roaming with the AP timed out")
+    if "CTRL-EVENT-EAP-STARTED" in ev:
+        raise Exception("Unexpected EAP exchange")
+
+def test_suite_b_192_radius(dev, apdev):
+    """WPA2/GCMP-256 (RADIUS) connection at Suite B 192-bit level"""
+    check_suite_b_192_capa(dev)
+    dev[0].flush_scan_cache()
+    params = suite_b_as_params()
+    params['ca_cert'] = 'auth_serv/ec2-ca.pem'
+    params['server_cert'] = 'auth_serv/ec2-server.pem'
+    params['private_key'] = 'auth_serv/ec2-server.key'
+    params['openssl_ciphers'] = 'SUITEB192'
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    params = { "ssid": "test-suite-b",
+               "wpa": "2",
+               "wpa_key_mgmt": "WPA-EAP-SUITE-B-192",
+               "rsn_pairwise": "GCMP-256",
+               "group_mgmt_cipher": "BIP-GMAC-256",
+               "ieee80211w": "2",
+               "ieee8021x": "1",
+               'auth_server_addr': "127.0.0.1",
+               'auth_server_port': "18129",
+               'auth_server_shared_secret': "radius",
+               'nas_identifier': "nas.w1.fi" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+                   ieee80211w="2",
+                   openssl_ciphers="SUITEB192",
+                   eap="TLS", identity="tls user",
+                   ca_cert="auth_serv/ec2-ca.pem",
+                   client_cert="auth_serv/ec2-user.pem",
+                   private_key="auth_serv/ec2-user.key",
+                   pairwise="GCMP-256", group="GCMP-256", scan_freq="2412")
diff --git a/hostap/tests/hwsim/test_tnc.py b/hostap/tests/hwsim/test_tnc.py
new file mode 100644
index 0000000..ba9e8e9
--- /dev/null
+++ b/hostap/tests/hwsim/test_tnc.py
@@ -0,0 +1,104 @@
+# -*- coding: utf-8 -*-
+# TNC tests
+# Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os.path
+
+import hostapd
+from utils import HwsimSkip
+from test_ap_eap import int_eap_server_params, check_eap_capa
+
+def test_tnc_peap_soh(dev, apdev):
+    """TNC PEAP-SoH"""
+    params = int_eap_server_params()
+    params["tnc"] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="PEAP", identity="user", password="password",
+                   ca_cert="auth_serv/ca.pem",
+                   phase1="peapver=0 tnc=soh cryptobinding=0",
+                   phase2="auth=MSCHAPV2",
+                   wait_connect=False)
+    dev[0].wait_connected(timeout=10)
+
+    dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="PEAP", identity="user", password="password",
+                   ca_cert="auth_serv/ca.pem",
+                   phase1="peapver=0 tnc=soh1 cryptobinding=1",
+                   phase2="auth=MSCHAPV2",
+                   wait_connect=False)
+    dev[1].wait_connected(timeout=10)
+
+    dev[2].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="PEAP", identity="user", password="password",
+                   ca_cert="auth_serv/ca.pem",
+                   phase1="peapver=0 tnc=soh2 cryptobinding=2",
+                   phase2="auth=MSCHAPV2",
+                   wait_connect=False)
+    dev[2].wait_connected(timeout=10)
+
+def test_tnc_ttls(dev, apdev):
+    """TNC TTLS"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = int_eap_server_params()
+    params["tnc"] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    if not os.path.exists("tnc/libhostap_imc.so"):
+        raise HwsimSkip("No IMC installed")
+
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="TTLS", identity="DOMAIN\mschapv2 user",
+                   anonymous_identity="ttls", password="password",
+                   phase2="auth=MSCHAPV2",
+                   ca_cert="auth_serv/ca.pem",
+                   wait_connect=False)
+    dev[0].wait_connected(timeout=10)
+
+def test_tnc_ttls_fragmentation(dev, apdev):
+    """TNC TTLS with fragmentation"""
+    check_eap_capa(dev[0], "MSCHAPV2")
+    params = int_eap_server_params()
+    params["tnc"] = "1"
+    params["fragment_size"] = "150"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    if not os.path.exists("tnc/libhostap_imc.so"):
+        raise HwsimSkip("No IMC installed")
+
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="TTLS", identity="DOMAIN\mschapv2 user",
+                   anonymous_identity="ttls", password="password",
+                   phase2="auth=MSCHAPV2",
+                   ca_cert="auth_serv/ca.pem",
+                   fragment_size="150",
+                   wait_connect=False)
+    dev[0].wait_connected(timeout=10)
+
+def test_tnc_fast(dev, apdev):
+    """TNC FAST"""
+    check_eap_capa(dev[0], "FAST")
+    params = int_eap_server_params()
+    params["tnc"] = "1"
+    params["pac_opaque_encr_key"] ="000102030405060708090a0b0c0d0e00"
+    params["eap_fast_a_id"] = "101112131415161718191a1b1c1d1e00"
+    params["eap_fast_a_id_info"] = "test server2"
+
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    if not os.path.exists("tnc/libhostap_imc.so"):
+        raise HwsimSkip("No IMC installed")
+
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="FAST", identity="user",
+                   anonymous_identity="FAST", password="password",
+                   phase2="auth=GTC",
+                   phase1="fast_provisioning=2",
+                   pac_file="blob://fast_pac_auth_tnc",
+                   ca_cert="auth_serv/ca.pem",
+                   wait_connect=False)
+    dev[0].wait_connected(timeout=10)
diff --git a/hostap/tests/hwsim/test_wep.py b/hostap/tests/hwsim/test_wep.py
new file mode 100644
index 0000000..bd8fec0
--- /dev/null
+++ b/hostap/tests/hwsim/test_wep.py
@@ -0,0 +1,82 @@
+# WEP 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.
+
+import hostapd
+import hwsim_utils
+
+def test_wep_open_auth(dev, apdev):
+    """WEP Open System authentication"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": "wep-open",
+                            "wep_key0": '"hello"' })
+    dev[0].flush_scan_cache()
+    dev[0].connect("wep-open", key_mgmt="NONE", wep_key0='"hello"',
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    if "[WEP]" not in dev[0].request("SCAN_RESULTS"):
+        raise Exception("WEP flag not indicated in scan results")
+
+    bss = dev[0].get_bss(apdev[0]['bssid'])
+    if 'flags' not in bss:
+        raise Exception("Could not get BSS flags from BSS table")
+    if "[WEP]" not in bss['flags']:
+        raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+def test_wep_shared_key_auth(dev, apdev):
+    """WEP Shared Key authentication"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": "wep-shared-key",
+                            "wep_key0": '"hello12345678"',
+                            "auth_algs": "2" })
+    dev[0].connect("wep-shared-key", key_mgmt="NONE", auth_alg="SHARED",
+                   wep_key0='"hello12345678"',
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    dev[1].connect("wep-shared-key", key_mgmt="NONE", auth_alg="OPEN SHARED",
+                   wep_key0='"hello12345678"',
+                   scan_freq="2412")
+
+def test_wep_shared_key_auth_not_allowed(dev, apdev):
+    """WEP Shared Key authentication not allowed"""
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": "wep-shared-key",
+                     "wep_key0": '"hello12345678"',
+                     "auth_algs": "1" })
+    dev[0].connect("wep-shared-key", key_mgmt="NONE", auth_alg="SHARED",
+                   wep_key0='"hello12345678"',
+                   scan_freq="2412", wait_connect=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected association")
+
+def test_wep_shared_key_auth_multi_key(dev, apdev):
+    """WEP Shared Key authentication with multiple keys"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": "wep-shared-key",
+                            "wep_key0": '"hello12345678"',
+                            "wep_key1": '"other12345678"',
+                            "auth_algs": "2" })
+    dev[0].connect("wep-shared-key", key_mgmt="NONE", auth_alg="SHARED",
+                   wep_key0='"hello12345678"',
+                   scan_freq="2412")
+    dev[1].connect("wep-shared-key", key_mgmt="NONE", auth_alg="SHARED",
+                   wep_key0='"hello12345678"',
+                   wep_key1='"other12345678"',
+                   wep_tx_keyidx="1",
+                   scan_freq="2412")
+    id = dev[2].connect("wep-shared-key", key_mgmt="NONE", auth_alg="SHARED",
+                        wep_key0='"hello12345678"',
+                        wep_key1='"other12345678"',
+                        wep_tx_keyidx="0",
+                        scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    hwsim_utils.test_connectivity(dev[1], hapd)
+    hwsim_utils.test_connectivity(dev[2], hapd)
+
+    dev[2].set_network(id, "wep_tx_keyidx", "1")
+    dev[2].request("REASSOCIATE")
+    dev[2].wait_connected(timeout=10, error="Reassociation timed out")
+    hwsim_utils.test_connectivity(dev[2], hapd)
diff --git a/hostap/tests/hwsim/test_wext.py b/hostap/tests/hwsim/test_wext.py
new file mode 100644
index 0000000..83bd8dd
--- /dev/null
+++ b/hostap/tests/hwsim/test_wext.py
@@ -0,0 +1,251 @@
+# Deprecated WEXT driver interface in wpa_supplicant
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import os
+
+import hostapd
+import hwsim_utils
+from wpasupplicant import WpaSupplicant
+from utils import HwsimSkip, skip_with_fips
+from test_rfkill import get_rfkill
+
+def get_wext_interface():
+    if not os.path.exists("/proc/net/wireless"):
+        raise HwsimSkip("WEXT support not included in the kernel")
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    try:
+        wpas.interface_add("wlan5", driver="wext")
+    except Exception, e:
+        wpas.close_ctrl()
+        raise HwsimSkip("WEXT driver support not included in wpa_supplicant")
+    return wpas
+
+def test_wext_open(dev, apdev):
+    """WEXT driver interface with open network"""
+    wpas = get_wext_interface()
+
+    params = { "ssid": "wext-open" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas.connect("wext-open", key_mgmt="NONE")
+    hwsim_utils.test_connectivity(wpas, hapd)
+
+def test_wext_wpa2_psk(dev, apdev):
+    """WEXT driver interface with WPA2-PSK"""
+    wpas = get_wext_interface()
+
+    params = hostapd.wpa2_params(ssid="wext-wpa2-psk", passphrase="12345678")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas.connect("wext-wpa2-psk", psk="12345678")
+    hwsim_utils.test_connectivity(wpas, hapd)
+    if "RSSI=" not in wpas.request("SIGNAL_POLL"):
+        raise Exception("Missing RSSI from SIGNAL_POLL")
+
+    wpas.dump_monitor()
+    hapd.request("DEAUTHENTICATE " + wpas.p2p_interface_addr())
+    wpas.wait_disconnected(timeout=15)
+
+def test_wext_wpa_psk(dev, apdev):
+    """WEXT driver interface with WPA-PSK"""
+    skip_with_fips(dev[0])
+    wpas = get_wext_interface()
+
+    params = hostapd.wpa_params(ssid="wext-wpa-psk", passphrase="12345678")
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    testfile = "/sys/kernel/debug/ieee80211/%s/netdev:%s/tkip_mic_test" % (hapd.get_driver_status_field("phyname"), apdev[0]['ifname'])
+    if not os.path.exists(testfile):
+        wpas.close_ctrl()
+        raise HwsimSkip("tkip_mic_test not supported in mac80211")
+
+    wpas.connect("wext-wpa-psk", psk="12345678")
+    hwsim_utils.test_connectivity(wpas, hapd)
+
+    with open(testfile, "w") as f:
+        f.write(wpas.p2p_interface_addr())
+    ev = wpas.wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected disconnection on first Michael MIC failure")
+
+    with open(testfile, "w") as f:
+        f.write("ff:ff:ff:ff:ff:ff")
+    ev = wpas.wait_disconnected(timeout=10,
+                                error="No disconnection after two Michael MIC failures")
+    if "reason=14 locally_generated=1" not in ev:
+        raise Exception("Unexpected disconnection reason: " + ev)
+
+def test_wext_pmksa_cache(dev, apdev):
+    """PMKSA caching with WEXT"""
+    wpas = get_wext_interface()
+
+    params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    bssid = apdev[0]['bssid']
+    wpas.connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+                   eap="GPSK", identity="gpsk user",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+    pmksa = wpas.get_pmksa(bssid)
+    if pmksa is None:
+        raise Exception("No PMKSA cache entry created")
+    if pmksa['opportunistic'] != '0':
+        raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    bssid2 = apdev[1]['bssid']
+
+    wpas.dump_monitor()
+    logger.info("Roam to AP2")
+    # It can take some time for the second AP to become ready to reply to Probe
+    # Request frames especially under heavy CPU load, so allow couple of rounds
+    # of scanning to avoid reporting errors incorrectly just because of scans
+    # not having seen the target AP.
+    for i in range(3):
+        wpas.scan()
+        if wpas.get_bss(bssid2) is not None:
+            break
+        logger.info("Scan again to find target AP")
+    wpas.request("ROAM " + bssid2)
+    ev = wpas.wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("EAP success timed out")
+    wpas.wait_connected(timeout=10, error="Roaming timed out")
+    pmksa2 = wpas.get_pmksa(bssid2)
+    if pmksa2 is None:
+        raise Exception("No PMKSA cache entry found")
+    if pmksa2['opportunistic'] != '0':
+        raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+    wpas.dump_monitor()
+    logger.info("Roam back to AP1")
+    wpas.scan()
+    wpas.request("ROAM " + bssid)
+    ev = wpas.wait_event(["CTRL-EVENT-EAP-STARTED",
+                          "CTRL-EVENT-CONNECTED"], timeout=15)
+    if ev is None:
+        raise Exception("Roaming with the AP timed out")
+    if "CTRL-EVENT-EAP-STARTED" in ev:
+        raise Exception("Unexpected EAP exchange")
+    pmksa1b = wpas.get_pmksa(bssid)
+    if pmksa1b is None:
+        raise Exception("No PMKSA cache entry found")
+    if pmksa['pmkid'] != pmksa1b['pmkid']:
+        raise Exception("Unexpected PMKID change for AP1")
+
+    wpas.dump_monitor()
+    if "FAIL" in wpas.request("PMKSA_FLUSH"):
+        raise Exception("PMKSA_FLUSH failed")
+    if wpas.get_pmksa(bssid) is not None or wpas.get_pmksa(bssid2) is not None:
+        raise Exception("PMKSA_FLUSH did not remove PMKSA entries")
+    wpas.wait_disconnected(timeout=5)
+    wpas.wait_connected(timeout=15, error="Reconnection timed out")
+
+def test_wext_wep_open_auth(dev, apdev):
+    """WEP Open System authentication"""
+    wpas = get_wext_interface()
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": "wep-open",
+                            "wep_key0": '"hello"' })
+    wpas.connect("wep-open", key_mgmt="NONE", wep_key0='"hello"',
+                 scan_freq="2412")
+    hwsim_utils.test_connectivity(wpas, hapd)
+    if "[WEP]" not in wpas.request("SCAN_RESULTS"):
+        raise Exception("WEP flag not indicated in scan results")
+
+def test_wext_wep_shared_key_auth(dev, apdev):
+    """WEP Shared Key authentication"""
+    wpas = get_wext_interface()
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": "wep-shared-key",
+                            "wep_key0": '"hello12345678"',
+                            "auth_algs": "2" })
+    wpas.connect("wep-shared-key", key_mgmt="NONE", auth_alg="SHARED",
+                 wep_key0='"hello12345678"', scan_freq="2412")
+    hwsim_utils.test_connectivity(wpas, hapd)
+    wpas.request("REMOVE_NETWORK all")
+    wpas.wait_disconnected(timeout=5)
+    wpas.connect("wep-shared-key", key_mgmt="NONE", auth_alg="OPEN SHARED",
+                 wep_key0='"hello12345678"', scan_freq="2412")
+
+def test_wext_pmf(dev, apdev):
+    """WEXT driver interface with WPA2-PSK and PMF"""
+    wpas = get_wext_interface()
+
+    params = hostapd.wpa2_params(ssid="wext-wpa2-psk", passphrase="12345678")
+    params["wpa_key_mgmt"] = "WPA-PSK-SHA256";
+    params["ieee80211w"] = "2";
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    wpas.connect("wext-wpa2-psk", psk="12345678", ieee80211w="1",
+                 key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                 scan_freq="2412")
+    hwsim_utils.test_connectivity(wpas, hapd)
+
+    addr = wpas.p2p_interface_addr()
+    hapd.request("DEAUTHENTICATE " + addr)
+    wpas.wait_disconnected(timeout=5)
+
+def test_wext_scan_hidden(dev, apdev):
+    """WEXT with hidden SSID"""
+    wpas = get_wext_interface()
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-scan",
+                                                "ignore_broadcast_ssid": "1" })
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], { "ssid": "test-scan2",
+                                                 "ignore_broadcast_ssid": "1" })
+
+    id1 = wpas.connect("test-scan", key_mgmt="NONE", scan_ssid="1",
+                       only_add_network=True)
+
+    wpas.request("SCAN scan_id=%d" % id1)
+
+    ev = wpas.wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=15)
+    if ev is None:
+        raise Exception("Scan did not complete")
+
+    if "test-scan" not in wpas.request("SCAN_RESULTS"):
+        raise Exception("Did not find hidden SSID in scan")
+
+    id = wpas.connect("test-scan2", key_mgmt="NONE", scan_ssid="1",
+                      only_add_network=True)
+    wpas.connect_network(id, timeout=30)
+    wpas.request("DISCONNECT")
+    hapd2.disable()
+    hapd.disable()
+    wpas.interface_remove("wlan5")
+    wpas.interface_add("wlan5")
+    wpas.flush_scan_cache(freq=2412)
+    wpas.flush_scan_cache()
+
+def test_wext_rfkill(dev, apdev):
+    """WEXT and rfkill block/unblock"""
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    rfk = get_rfkill(wpas)
+    wpas.interface_remove("wlan5")
+
+    wpas = get_wext_interface()
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
+    try:
+        logger.info("rfkill block")
+        rfk.block()
+        wpas.wait_disconnected(timeout=10,
+                               error="Missing disconnection event on rfkill block")
+
+        logger.info("rfkill unblock")
+        rfk.unblock()
+        wpas.wait_connected(timeout=20,
+                            error="Missing connection event on rfkill unblock")
+        hwsim_utils.test_connectivity(wpas, hapd)
+    finally:
+        rfk.unblock()
diff --git a/hostap/tests/hwsim/test_wnm.py b/hostap/tests/hwsim/test_wnm.py
new file mode 100644
index 0000000..c30436e
--- /dev/null
+++ b/hostap/tests/hwsim/test_wnm.py
@@ -0,0 +1,655 @@
+# WNM tests
+# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import struct
+import time
+import logging
+logger = logging.getLogger()
+import subprocess
+
+import hostapd
+from wlantest import Wlantest
+
+def test_wnm_bss_transition_mgmt(dev, apdev):
+    """WNM BSS Transition Management"""
+    params = { "ssid": "test-wnm",
+               "time_advertisement": "2",
+               "time_zone": "EST5",
+               "wnm_sleep_mode": "1",
+               "bss_transition": "1" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+    dev[0].request("WNM_BSS_QUERY 0")
+
+def test_wnm_disassoc_imminent(dev, apdev):
+    """WNM Disassociation Imminent"""
+    params = { "ssid": "test-wnm",
+               "time_advertisement": "2",
+               "time_zone": "EST5",
+               "wnm_sleep_mode": "1",
+               "bss_transition": "1" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+    addr = dev[0].p2p_interface_addr()
+    hapd.request("DISASSOC_IMMINENT " + addr + " 10")
+    ev = dev[0].wait_event(["WNM: Disassociation Imminent"])
+    if ev is None:
+        raise Exception("Timeout while waiting for disassociation imminent")
+    if "Disassociation Timer 10" not in ev:
+        raise Exception("Unexpected disassociation imminent contents")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+    if ev is None:
+        raise Exception("Timeout while waiting for re-connection scan")
+
+def test_wnm_ess_disassoc_imminent(dev, apdev):
+    """WNM ESS Disassociation Imminent"""
+    params = { "ssid": "test-wnm",
+               "time_advertisement": "2",
+               "time_zone": "EST5",
+               "wnm_sleep_mode": "1",
+               "bss_transition": "1" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+    addr = dev[0].p2p_interface_addr()
+    hapd.request("ESS_DISASSOC " + addr + " 10 http://example.com/session-info")
+    ev = dev[0].wait_event(["ESS-DISASSOC-IMMINENT"])
+    if ev is None:
+        raise Exception("Timeout while waiting for ESS disassociation imminent")
+    if "0 1024 http://example.com/session-info" not in ev:
+        raise Exception("Unexpected ESS disassociation imminent message contents")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+    if ev is None:
+        raise Exception("Timeout while waiting for re-connection scan")
+
+def test_wnm_ess_disassoc_imminent_pmf(dev, apdev):
+    """WNM ESS Disassociation Imminent"""
+    params = hostapd.wpa2_params("test-wnm-rsn", "12345678")
+    params["wpa_key_mgmt"] = "WPA-PSK-SHA256";
+    params["ieee80211w"] = "2";
+    params["bss_transition"] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2",
+                   key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
+    addr = dev[0].p2p_interface_addr()
+    hapd.request("ESS_DISASSOC " + addr + " 10 http://example.com/session-info")
+    ev = dev[0].wait_event(["ESS-DISASSOC-IMMINENT"])
+    if ev is None:
+        raise Exception("Timeout while waiting for ESS disassociation imminent")
+    if "1 1024 http://example.com/session-info" not in ev:
+        raise Exception("Unexpected ESS disassociation imminent message contents")
+    ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+    if ev is None:
+        raise Exception("Timeout while waiting for re-connection scan")
+
+def check_wnm_sleep_mode_enter_exit(hapd, dev, interval=None, tfs_req=None):
+    addr = dev.p2p_interface_addr()
+    sta = hapd.get_sta(addr)
+    if "[WNM_SLEEP_MODE]" in sta['flags']:
+        raise Exception("Station unexpectedly in WNM-Sleep Mode")
+
+    logger.info("Going to WNM Sleep Mode")
+    extra = ""
+    if interval is not None:
+        extra += " interval=" + str(interval)
+    if tfs_req:
+        extra += " tfs_req=" + tfs_req
+    if "OK" not in dev.request("WNM_SLEEP enter" + extra):
+        raise Exception("WNM_SLEEP failed")
+    ok = False
+    for i in range(20):
+        time.sleep(0.1)
+        sta = hapd.get_sta(addr)
+        if "[WNM_SLEEP_MODE]" in sta['flags']:
+            ok = True
+            break
+    if not ok:
+        raise Exception("Station failed to enter WNM-Sleep Mode")
+
+    logger.info("Waking up from WNM Sleep Mode")
+    ok = False
+    dev.request("WNM_SLEEP exit")
+    for i in range(20):
+        time.sleep(0.1)
+        sta = hapd.get_sta(addr)
+        if "[WNM_SLEEP_MODE]" not in sta['flags']:
+            ok = True
+            break
+    if not ok:
+        raise Exception("Station failed to exit WNM-Sleep Mode")
+
+def test_wnm_sleep_mode_open(dev, apdev):
+    """WNM Sleep Mode - open"""
+    params = { "ssid": "test-wnm",
+               "time_advertisement": "2",
+               "time_zone": "EST5",
+               "wnm_sleep_mode": "1",
+               "bss_transition": "1" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+    ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
+    if ev is None:
+        raise Exception("No connection event received from hostapd")
+    check_wnm_sleep_mode_enter_exit(hapd, dev[0])
+    check_wnm_sleep_mode_enter_exit(hapd, dev[0], interval=100)
+    check_wnm_sleep_mode_enter_exit(hapd, dev[0], tfs_req="5b17010001130e110000071122334455661122334455661234")
+
+    cmds = [ "foo",
+             "exit tfs_req=123 interval=10",
+             "enter tfs_req=qq interval=10" ]
+    for cmd in cmds:
+        if "FAIL" not in dev[0].request("WNM_SLEEP " + cmd):
+            raise Exception("Invalid WNM_SLEEP accepted")
+
+def test_wnm_sleep_mode_rsn(dev, apdev):
+    """WNM Sleep Mode - RSN"""
+    params = hostapd.wpa2_params("test-wnm-rsn", "12345678")
+    params["time_advertisement"] = "2"
+    params["time_zone"] = "EST5"
+    params["wnm_sleep_mode"] = "1"
+    params["bss_transition"] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    dev[0].connect("test-wnm-rsn", psk="12345678", scan_freq="2412")
+    ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
+    if ev is None:
+        raise Exception("No connection event received from hostapd")
+    check_wnm_sleep_mode_enter_exit(hapd, dev[0])
+
+def test_wnm_sleep_mode_rsn_pmf(dev, apdev):
+    """WNM Sleep Mode - RSN with PMF"""
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+    params = hostapd.wpa2_params("test-wnm-rsn", "12345678")
+    params["wpa_key_mgmt"] = "WPA-PSK-SHA256";
+    params["ieee80211w"] = "2";
+    params["time_advertisement"] = "2"
+    params["time_zone"] = "EST5"
+    params["wnm_sleep_mode"] = "1"
+    params["bss_transition"] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2",
+                   key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
+    ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
+    if ev is None:
+        raise Exception("No connection event received from hostapd")
+    check_wnm_sleep_mode_enter_exit(hapd, dev[0])
+
+MGMT_SUBTYPE_ACTION = 13
+ACTION_CATEG_WNM = 10
+WNM_ACT_BSS_TM_REQ = 7
+WNM_ACT_BSS_TM_RESP = 8
+
+def bss_tm_req(dst, src, dialog_token=1, req_mode=0, disassoc_timer=0,
+               validity_interval=1):
+    msg = {}
+    msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+    msg['da'] = dst
+    msg['sa'] = src
+    msg['bssid'] = src
+    msg['payload'] = struct.pack("<BBBBHB",
+                                 ACTION_CATEG_WNM, WNM_ACT_BSS_TM_REQ,
+                                 dialog_token, req_mode, disassoc_timer,
+                                 validity_interval)
+    return msg
+
+def rx_bss_tm_resp(hapd, expect_dialog=None, expect_status=None):
+    for i in range(0, 100):
+        resp = hapd.mgmt_rx()
+        if resp is None:
+            raise Exception("No BSS TM Response received")
+        if resp['subtype'] == MGMT_SUBTYPE_ACTION:
+            break
+    if i == 99:
+        raise Exception("Not an Action frame")
+    payload = resp['payload']
+    if len(payload) < 2 + 3:
+        raise Exception("Too short payload")
+    (category, action) = struct.unpack('BB', payload[0:2])
+    if category != ACTION_CATEG_WNM or action != WNM_ACT_BSS_TM_RESP:
+        raise Exception("Not a BSS TM Response")
+    pos = payload[2:]
+    (dialog, status, bss_term_delay) = struct.unpack('BBB', pos[0:3])
+    resp['dialog'] = dialog
+    resp['status'] = status
+    resp['bss_term_delay'] = bss_term_delay
+    pos = pos[3:]
+    if len(pos) >= 6 and status == 0:
+        resp['target_bssid'] = binascii.hexlify(pos[0:6])
+        pos = pos[6:]
+    resp['candidates'] = pos
+    if expect_dialog is not None and dialog != expect_dialog:
+        raise Exception("Unexpected dialog token")
+    if expect_status is not None and status != expect_status:
+        raise Exception("Unexpected status code %d" % status)
+    return resp
+
+def expect_ack(hapd):
+    ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+    if ev is None:
+        raise Exception("Missing TX status")
+    if "ok=1" not in ev:
+        raise Exception("Action frame not acknowledged")
+
+def test_wnm_bss_tm_req(dev, apdev):
+    """BSS Transition Management Request"""
+    params = { "ssid": "test-wnm", "bss_transition": "1" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+    hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    hapd.set("ext_mgmt_frame_handling", "1")
+
+    # truncated BSS TM Request
+    req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
+                     req_mode=0x08)
+    req['payload'] = struct.pack("<BBBBH",
+                                 ACTION_CATEG_WNM, WNM_ACT_BSS_TM_REQ,
+                                 1, 0, 0)
+    hapd.mgmt_tx(req)
+    expect_ack(hapd)
+
+    # no disassociation and no candidate list
+    req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
+                     dialog_token=2)
+    hapd.mgmt_tx(req)
+    resp = rx_bss_tm_resp(hapd, expect_dialog=2, expect_status=1)
+
+    # truncated BSS Termination Duration
+    req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
+                     req_mode=0x08)
+    hapd.mgmt_tx(req)
+    expect_ack(hapd)
+
+    # BSS Termination Duration with TSF=0 and Duration=10
+    req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
+                     req_mode=0x08, dialog_token=3)
+    req['payload'] += struct.pack("<BBQH", 4, 10, 0, 10)
+    hapd.mgmt_tx(req)
+    resp = rx_bss_tm_resp(hapd, expect_dialog=3, expect_status=1)
+
+    # truncated Session Information URL
+    req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
+                     req_mode=0x10)
+    hapd.mgmt_tx(req)
+    expect_ack(hapd)
+    req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
+                     req_mode=0x10)
+    req['payload'] += struct.pack("<BBB", 3, 65, 66)
+    hapd.mgmt_tx(req)
+    expect_ack(hapd)
+
+    # Session Information URL
+    req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
+                     req_mode=0x10, dialog_token=4)
+    req['payload'] += struct.pack("<BBB", 2, 65, 66)
+    hapd.mgmt_tx(req)
+    resp = rx_bss_tm_resp(hapd, expect_dialog=4, expect_status=0)
+
+    # Preferred Candidate List without any entries
+    req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
+                     req_mode=0x01, dialog_token=5)
+    hapd.mgmt_tx(req)
+    resp = rx_bss_tm_resp(hapd, expect_dialog=5, expect_status=7)
+
+    # Preferred Candidate List with a truncated entry
+    req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
+                     req_mode=0x01)
+    req['payload'] += struct.pack("<BB", 52, 1)
+    hapd.mgmt_tx(req)
+    expect_ack(hapd)
+
+    # Preferred Candidate List with a too short entry
+    req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
+                     req_mode=0x01, dialog_token=6)
+    req['payload'] += struct.pack("<BB", 52, 0)
+    hapd.mgmt_tx(req)
+    resp = rx_bss_tm_resp(hapd, expect_dialog=6, expect_status=7)
+
+    # Preferred Candidate List with a non-matching entry
+    req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
+                     req_mode=0x01, dialog_token=6)
+    req['payload'] += struct.pack("<BB6BLBBB", 52, 13,
+                                  1, 2, 3, 4, 5, 6,
+                                  0, 81, 1, 7)
+    hapd.mgmt_tx(req)
+    resp = rx_bss_tm_resp(hapd, expect_dialog=6, expect_status=7)
+
+    # Preferred Candidate List with a truncated subelement
+    req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
+                     req_mode=0x01, dialog_token=7)
+    req['payload'] += struct.pack("<BB6BLBBBBB", 52, 13 + 2,
+                                  1, 2, 3, 4, 5, 6,
+                                  0, 81, 1, 7,
+                                  1, 1)
+    hapd.mgmt_tx(req)
+    resp = rx_bss_tm_resp(hapd, expect_dialog=7, expect_status=7)
+
+    # Preferred Candidate List with lots of invalid optional subelements
+    req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
+                     req_mode=0x01, dialog_token=8)
+    subelems = struct.pack("<BBHB", 1, 3, 0, 100)
+    subelems += struct.pack("<BBB", 2, 1, 65)
+    subelems += struct.pack("<BB", 3, 0)
+    subelems += struct.pack("<BBQB", 4, 9, 0, 10)
+    subelems += struct.pack("<BBHLB", 5, 7, 0, 0, 0)
+    subelems += struct.pack("<BB", 66, 0)
+    subelems += struct.pack("<BBBBBB", 70, 4, 0, 0, 0, 0)
+    subelems += struct.pack("<BB", 71, 0)
+    req['payload'] += struct.pack("<BB6BLBBB", 52, 13 + len(subelems),
+                                  1, 2, 3, 4, 5, 6,
+                                  0, 81, 1, 7) + subelems
+    hapd.mgmt_tx(req)
+    resp = rx_bss_tm_resp(hapd, expect_dialog=8, expect_status=7)
+
+    # Preferred Candidate List with lots of valid optional subelements (twice)
+    req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
+                     req_mode=0x01, dialog_token=8)
+    # TSF Information
+    subelems = struct.pack("<BBHH", 1, 4, 0, 100)
+    # Condensed Country String
+    subelems += struct.pack("<BBBB", 2, 2, 65, 66)
+    # BSS Transition Candidate Preference
+    subelems += struct.pack("<BBB", 3, 1, 100)
+    # BSS Termination Duration
+    subelems += struct.pack("<BBQH", 4, 10, 0, 10)
+    # Bearing
+    subelems += struct.pack("<BBHLH", 5, 8, 0, 0, 0)
+    # Measurement Pilot Transmission
+    subelems += struct.pack("<BBBBB", 66, 3, 0, 0, 0)
+    # RM Enabled Capabilities
+    subelems += struct.pack("<BBBBBBB", 70, 5, 0, 0, 0, 0, 0)
+    # Multiple BSSID
+    subelems += struct.pack("<BBBB", 71, 2, 0, 0)
+    req['payload'] += struct.pack("<BB6BLBBB", 52, 13 + len(subelems) * 2,
+                                  1, 2, 3, 4, 5, 6,
+                                  0, 81, 1, 7) + subelems + subelems
+    hapd.mgmt_tx(req)
+    resp = rx_bss_tm_resp(hapd, expect_dialog=8, expect_status=7)
+
+def test_wnm_bss_keep_alive(dev, apdev):
+    """WNM keep-alive"""
+    params = { "ssid": "test-wnm",
+               "ap_max_inactivity": "1" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    addr = dev[0].p2p_interface_addr()
+    dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+    start = hapd.get_sta(addr)
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=2)
+    if ev is not None:
+        raise Exception("Unexpected disconnection")
+    end = hapd.get_sta(addr)
+    if int(end['rx_packets']) <= int(start['rx_packets']):
+        raise Exception("No keep-alive packets received")
+    try:
+        # Disable client keep-alive so that hostapd will verify connection
+        # with client poll
+        dev[0].request("SET no_keep_alive 1")
+        for i in range(60):
+            sta = hapd.get_sta(addr)
+            logger.info("timeout_next=%s rx_packets=%s tx_packets=%s" % (sta['timeout_next'], sta['rx_packets'], sta['tx_packets']))
+            if i > 1 and sta['timeout_next'] != "NULLFUNC POLL" and int(sta['tx_packets']) > int(end['tx_packets']):
+                break
+            ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+            if ev is not None:
+                raise Exception("Unexpected disconnection (client poll expected)")
+    finally:
+        dev[0].request("SET no_keep_alive 0")
+    if int(sta['tx_packets']) <= int(end['tx_packets']):
+        raise Exception("No client poll packet seen")
+
+def test_wnm_bss_tm(dev, apdev):
+    """WNM BSS Transition Management"""
+    try:
+        hapd = None
+        hapd2 = None
+        params = { "ssid": "test-wnm",
+                   "country_code": "FI",
+                   "ieee80211d": "1",
+                   "hw_mode": "g",
+                   "channel": "1",
+                   "bss_transition": "1" }
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+        id = dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+        dev[0].set_network(id, "scan_freq", "")
+
+        params = { "ssid": "test-wnm",
+                   "country_code": "FI",
+                   "ieee80211d": "1",
+                   "hw_mode": "a",
+                   "channel": "36",
+                   "bss_transition": "1" }
+        hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+        addr = dev[0].p2p_interface_addr()
+        dev[0].dump_monitor()
+
+        logger.info("No neighbor list entries")
+        if "OK" not in hapd.request("BSS_TM_REQ " + addr):
+            raise Exception("BSS_TM_REQ command failed")
+        ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+        if ev is None:
+            raise Exception("No BSS Transition Management Response")
+        if addr not in ev:
+            raise Exception("Unexpected BSS Transition Management Response address")
+        if "status_code=0" in ev:
+            raise Exception("BSS transition accepted unexpectedly")
+        dev[0].dump_monitor()
+
+        logger.info("Neighbor list entry, but not claimed as Preferred Candidate List")
+        if "OK" not in hapd.request("BSS_TM_REQ " + addr + " neighbor=11:22:33:44:55:66,0x0000,81,3,7"):
+            raise Exception("BSS_TM_REQ command failed")
+        ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+        if ev is None:
+            raise Exception("No BSS Transition Management Response")
+        if "status_code=0" in ev:
+            raise Exception("BSS transition accepted unexpectedly")
+        dev[0].dump_monitor()
+
+        logger.info("Preferred Candidate List (no matching neighbor) without Disassociation Imminent")
+        if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 neighbor=11:22:33:44:55:66,0x0000,81,3,7,0301ff neighbor=22:33:44:55:66:77,0x0000,1,36,7 neighbor=00:11:22:33:44:55,0x0000,81,4,7,03010a"):
+            raise Exception("BSS_TM_REQ command failed")
+        ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+        if ev is None:
+            raise Exception("No BSS Transition Management Response")
+        if "status_code=0" in ev:
+            raise Exception("BSS transition accepted unexpectedly")
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+        if ev is None:
+            raise Exception("No scan started")
+        dev[0].dump_monitor()
+
+        logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
+        if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
+            raise Exception("BSS_TM_REQ command failed")
+        ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+        if ev is None:
+            raise Exception("No BSS Transition Management Response")
+        if "status_code=0" not in ev:
+            raise Exception("BSS transition request was not accepted: " + ev)
+        if "target_bssid=" + apdev[1]['bssid'] not in ev:
+            raise Exception("Unexpected target BSS: " + ev)
+        dev[0].wait_connected(timeout=15, error="No reassociation seen")
+        if apdev[1]['bssid'] not in ev:
+            raise Exception("Unexpected reassociation target: " + ev)
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
+        if ev is not None:
+            raise Exception("Unexpected scan started")
+        dev[0].dump_monitor()
+
+        logger.info("Preferred Candidate List with two matches, no roam needed")
+        if "OK" not in hapd2.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[0]['bssid'] + ",0x0000,81,1,7,030101 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
+            raise Exception("BSS_TM_REQ command failed")
+        ev = hapd2.wait_event(['BSS-TM-RESP'], timeout=10)
+        if ev is None:
+            raise Exception("No BSS Transition Management Response")
+        if "status_code=0" not in ev:
+            raise Exception("BSS transition request was not accepted: " + ev)
+        if "target_bssid=" + apdev[1]['bssid'] not in ev:
+            raise Exception("Unexpected target BSS: " + ev)
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
+        if ev is not None:
+            raise Exception("Unexpected scan started")
+        ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.5)
+        if ev is not None:
+            raise Exception("Unexpected reassociation");
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        if hapd2:
+            hapd2.request("DISABLE")
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def start_wnm_tm(ap, country, dev):
+    params = { "ssid": "test-wnm",
+               "country_code": country,
+               "ieee80211d": "1",
+               "hw_mode": "g",
+               "channel": "1",
+               "bss_transition": "1" }
+    hapd = hostapd.add_ap(ap['ifname'], params)
+    id = dev.connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+    dev.dump_monitor()
+    dev.set_network(id, "scan_freq", "")
+    return hapd, id
+
+def stop_wnm_tm(hapd, dev):
+    dev.request("DISCONNECT")
+    try:
+        dev.wait_disconnected()
+    except:
+        pass
+    if hapd:
+        hapd.request("DISABLE")
+    subprocess.call(['iw', 'reg', 'set', '00'])
+    dev.flush_scan_cache()
+
+def wnm_bss_tm_check(hapd, dev, data):
+    addr = dev.p2p_interface_addr()
+    if "OK" not in hapd.request("BSS_TM_REQ " + addr + " " + data):
+        raise Exception("BSS_TM_REQ command failed")
+    ev = dev.wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+    if ev is None:
+        raise Exception("No scan started")
+    ev = dev.wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+    if ev is None:
+        raise Exception("Scan did not complete")
+
+    ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+    if ev is None:
+        raise Exception("No BSS Transition Management Response")
+    if "status_code=7" not in ev:
+        raise Exception("Unexpected response: " + ev)
+
+def test_wnm_bss_tm_country_us(dev, apdev):
+    """WNM BSS Transition Management (US)"""
+    try:
+        hapd = None
+        hapd, id = start_wnm_tm(apdev[0], "US", dev[0])
+
+        logger.info("Preferred Candidate List (no matching neighbor, known channels)")
+        wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,12,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,2,52,7,03010a neighbor=00:11:22:33:44:57,0x0000,4,100,7 neighbor=00:11:22:33:44:59,0x0000,3,149,7 neighbor=00:11:22:33:44:5b,0x0000,34,1,7 neighbor=00:11:22:33:44:5d,0x0000,5,149,7")
+
+        # Make the test take less time by limiting full scans
+        dev[0].set_network(id, "scan_freq", "2412")
+        logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
+        wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,12,0,7,0301ff neighbor=22:33:44:55:66:77,0x0000,12,12,7 neighbor=00:11:22:33:44:55,0x0000,2,35,7,03010a neighbor=00:11:22:33:44:56,0x0000,2,65,7 neighbor=00:11:22:33:44:57,0x0000,4,99,7 neighbor=00:11:22:33:44:58,0x0000,4,145,7")
+
+        logger.info("Preferred Candidate List (no matching neighbor, unknown channels 2)")
+        wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:59,0x0000,3,148,7 neighbor=00:11:22:33:44:5a,0x0000,3,162,7 neighbor=00:11:22:33:44:5b,0x0000,34,0,7 neighbor=00:11:22:33:44:5c,0x0000,34,4,7 neighbor=00:11:22:33:44:5d,0x0000,5,148,7 neighbor=00:11:22:33:44:5e,0x0000,5,166,7 neighbor=00:11:22:33:44:5f,0x0000,0,0,7")
+    finally:
+        stop_wnm_tm(hapd, dev[0])
+
+def test_wnm_bss_tm_country_fi(dev, apdev):
+    """WNM BSS Transition Management (FI)"""
+    addr = dev[0].p2p_interface_addr()
+    try:
+        hapd = None
+        hapd, id = start_wnm_tm(apdev[0], "FI", dev[0])
+
+        logger.info("Preferred Candidate List (no matching neighbor, known channels)")
+        wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,4,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,1,36,7,03010a neighbor=00:11:22:33:44:57,0x0000,3,100,7 neighbor=00:11:22:33:44:59,0x0000,17,149,7 neighbor=00:11:22:33:44:5c,0x0000,18,1,7")
+
+        # Make the test take less time by limiting full scans
+        dev[0].set_network(id, "scan_freq", "2412")
+        logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
+        wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,4,0,7 neighbor=00:11:22:33:44:01,0x0000,4,14,7 neighbor=00:11:22:33:44:02,0x0000,1,35,7 neighbor=00:11:22:33:44:03,0x0000,1,65,7 neighbor=00:11:22:33:44:04,0x0000,3,99,7 neighbor=00:11:22:33:44:05,0x0000,3,141,7 neighbor=00:11:22:33:44:06,0x0000,17,148,7 neighbor=00:11:22:33:44:07,0x0000,17,170,7 neighbor=00:11:22:33:44:08,0x0000,18,0,7 neighbor=00:11:22:33:44:09,0x0000,18,5,7")
+
+        logger.info("Preferred Candidate List (no matching neighbor, unknown channels 2)")
+        wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,0,0,7")
+    finally:
+        stop_wnm_tm(hapd, dev[0])
+
+def test_wnm_bss_tm_country_jp(dev, apdev):
+    """WNM BSS Transition Management (JP)"""
+    addr = dev[0].p2p_interface_addr()
+    try:
+        hapd = None
+        hapd, id = start_wnm_tm(apdev[0], "JP", dev[0])
+
+        logger.info("Preferred Candidate List (no matching neighbor, known channels)")
+        wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,30,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,31,14,7,03010a neighbor=00:11:22:33:44:57,0x0000,1,36,7 neighbor=00:11:22:33:44:59,0x0000,34,100,7 neighbor=00:11:22:33:44:5c,0x0000,59,1,7")
+
+        # Make the test take less time by limiting full scans
+        dev[0].set_network(id, "scan_freq", "2412")
+        logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
+        wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,30,0,7,0301ff neighbor=22:33:44:55:66:77,0x0000,30,14,7 neighbor=00:11:22:33:44:56,0x0000,31,13,7 neighbor=00:11:22:33:44:57,0x0000,1,33,7 neighbor=00:11:22:33:44:58,0x0000,1,65,7 neighbor=00:11:22:33:44:5a,0x0000,34,99,7 neighbor=00:11:22:33:44:5b,0x0000,34,141,7 neighbor=00:11:22:33:44:5d,0x0000,59,0,7 neighbor=00:11:22:33:44:5e,0x0000,59,4,7 neighbor=00:11:22:33:44:5f,0x0000,0,0,7")
+    finally:
+        stop_wnm_tm(hapd, dev[0])
+
+def test_wnm_bss_tm_country_cn(dev, apdev):
+    """WNM BSS Transition Management (CN)"""
+    addr = dev[0].p2p_interface_addr()
+    try:
+        hapd = None
+        hapd, id = start_wnm_tm(apdev[0], "CN", dev[0])
+
+        logger.info("Preferred Candidate List (no matching neighbor, known channels)")
+        wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,7,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,1,36,7,03010a neighbor=00:11:22:33:44:57,0x0000,3,149,7 neighbor=00:11:22:33:44:59,0x0000,6,149,7")
+
+        # Make the test take less time by limiting full scans
+        dev[0].set_network(id, "scan_freq", "2412")
+        logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
+        wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,7,0,7,0301ff neighbor=22:33:44:55:66:77,0x0000,7,14,7 neighbor=00:11:22:33:44:56,0x0000,1,35,7 neighbor=00:11:22:33:44:57,0x0000,1,65,7 neighbor=00:11:22:33:44:58,0x0000,3,148,7 neighbor=00:11:22:33:44:5a,0x0000,3,166,7 neighbor=00:11:22:33:44:5f,0x0000,0,0,7")
+    finally:
+        stop_wnm_tm(hapd, dev[0])
+
+def test_wnm_bss_tm_global(dev, apdev):
+    """WNM BSS Transition Management (global)"""
+    addr = dev[0].p2p_interface_addr()
+    try:
+        hapd = None
+        hapd, id = start_wnm_tm(apdev[0], "XX", dev[0])
+
+        logger.info("Preferred Candidate List (no matching neighbor, known channels)")
+        wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,81,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,82,14,7,03010a neighbor=00:11:22:33:44:57,0x0000,83,1,7 neighbor=00:11:22:33:44:59,0x0000,115,36,7 neighbor=00:11:22:33:44:5a,0x0000,121,100,7 neighbor=00:11:22:33:44:5c,0x0000,124,149,7 neighbor=00:11:22:33:44:5d,0x0000,125,149,7 neighbor=00:11:22:33:44:5e,0x0000,128,42,7 neighbor=00:11:22:33:44:5f,0x0000,129,50,7 neighbor=00:11:22:33:44:60,0x0000,180,1,7")
+
+        # Make the test take less time by limiting full scans
+        dev[0].set_network(id, "scan_freq", "2412")
+        logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
+        wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,81,0,7 neighbor=00:11:22:33:44:01,0x0000,81,14,7 neighbor=00:11:22:33:44:02,0x0000,82,13,7 neighbor=00:11:22:33:44:03,0x0000,83,0,7 neighbor=00:11:22:33:44:04,0x0000,83,14,7 neighbor=00:11:22:33:44:05,0x0000,115,35,7 neighbor=00:11:22:33:44:06,0x0000,115,65,7 neighbor=00:11:22:33:44:07,0x0000,121,99,7 neighbor=00:11:22:33:44:08,0x0000,121,141,7 neighbor=00:11:22:33:44:09,0x0000,124,148,7")
+
+        logger.info("Preferred Candidate List (no matching neighbor, unknown channels 2)")
+        wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,124,162,7 neighbor=00:11:22:33:44:01,0x0000,125,148,7 neighbor=00:11:22:33:44:02,0x0000,125,170,7 neighbor=00:11:22:33:44:03,0x0000,128,35,7 neighbor=00:11:22:33:44:04,0x0000,128,162,7 neighbor=00:11:22:33:44:05,0x0000,129,49,7 neighbor=00:11:22:33:44:06,0x0000,129,115,7 neighbor=00:11:22:33:44:07,0x0000,180,0,7 neighbor=00:11:22:33:44:08,0x0000,180,5,7 neighbor=00:11:22:33:44:09,0x0000,0,0,7")
+    finally:
+        stop_wnm_tm(hapd, dev[0])
diff --git a/hostap/tests/hwsim/test_wpas_ap.py b/hostap/tests/hwsim/test_wpas_ap.py
new file mode 100644
index 0000000..53de399
--- /dev/null
+++ b/hostap/tests/hwsim/test_wpas_ap.py
@@ -0,0 +1,336 @@
+# wpa_supplicant AP mode tests
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+from utils import HwsimSkip
+from test_p2p_channel import set_country
+
+def wait_ap_ready(dev):
+    ev = dev.wait_event(["CTRL-EVENT-CONNECTED"])
+    if ev is None:
+        raise Exception("AP failed to start")
+
+def test_wpas_ap_open(dev):
+    """wpa_supplicant AP mode - open network"""
+    if "FAIL" not in dev[0].request("DEAUTHENTICATE 00:11:22:33:44:55"):
+        raise Exception("Unexpected DEAUTHENTICATE accepted")
+    if "FAIL" not in dev[0].request("DISASSOCIATE 00:11:22:33:44:55"):
+        raise Exception("Unexpected DISASSOCIATE accepted")
+    if "FAIL" not in dev[0].request("CHAN_SWITCH 0 2432"):
+        raise Exception("Unexpected CHAN_SWITCH accepted")
+
+    id = dev[0].add_network()
+    dev[0].set_network(id, "mode", "2")
+    dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+    dev[0].set_network(id, "key_mgmt", "NONE")
+    dev[0].set_network(id, "frequency", "2412")
+    dev[0].set_network(id, "scan_freq", "2412")
+    dev[0].select_network(id)
+    wait_ap_ready(dev[0])
+
+    if "FAIL" not in dev[0].request("DEAUTHENTICATE foo"):
+        raise Exception("Invalid DEAUTHENTICATE accepted")
+    if "FAIL" not in dev[0].request("DISASSOCIATE foo"):
+        raise Exception("Invalid DISASSOCIATE accepted")
+
+    dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+    dev[2].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], dev[1])
+    hwsim_utils.test_connectivity(dev[1], dev[2])
+
+    addr1 = dev[1].p2p_interface_addr()
+    addr2 = dev[2].p2p_interface_addr()
+    addrs = [ addr1, addr2 ]
+    sta = dev[0].get_sta(None)
+    if sta['addr'] not in addrs:
+        raise Exception("Unexpected STA address")
+    sta1 = dev[0].get_sta(sta['addr'])
+    if sta1['addr'] not in addrs:
+        raise Exception("Unexpected STA address")
+    sta2 = dev[0].get_sta(sta['addr'], next=True)
+    if sta2['addr'] not in addrs:
+        raise Exception("Unexpected STA2 address")
+    sta3 = dev[0].get_sta(sta2['addr'], next=True)
+    if len(sta3) != 0:
+        raise Exception("Unexpected STA iteration result (did not stop)")
+
+    status = dev[0].get_status()
+    if status['mode'] != "AP":
+        raise Exception("Unexpected status mode")
+
+    dev[1].dump_monitor()
+    dev[2].dump_monitor()
+    dev[0].request("DEAUTHENTICATE " + addr1)
+    dev[0].request("DISASSOCIATE " + addr2)
+    dev[1].wait_disconnected(timeout=10)
+    dev[2].wait_disconnected(timeout=10)
+    dev[1].wait_connected(timeout=10, error="Reconnection timed out")
+    dev[2].wait_connected(timeout=10, error="Reconnection timed out")
+    dev[1].request("DISCONNECT")
+    dev[2].request("DISCONNECT")
+
+def test_wpas_ap_wep(dev):
+    """wpa_supplicant AP mode - WEP"""
+    id = dev[0].add_network()
+    dev[0].set_network(id, "mode", "2")
+    dev[0].set_network_quoted(id, "ssid", "wpas-ap-wep")
+    dev[0].set_network(id, "key_mgmt", "NONE")
+    dev[0].set_network(id, "frequency", "2412")
+    dev[0].set_network(id, "scan_freq", "2412")
+    dev[0].set_network_quoted(id, "wep_key0", "hello")
+    dev[0].select_network(id)
+    wait_ap_ready(dev[0])
+
+    dev[1].connect("wpas-ap-wep", key_mgmt="NONE", wep_key0='"hello"',
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], dev[1])
+    dev[1].request("DISCONNECT")
+
+def test_wpas_ap_no_ssid(dev):
+    """wpa_supplicant AP mode - invalid network configuration"""
+    id = dev[0].add_network()
+    dev[0].set_network(id, "mode", "2")
+    dev[0].set_network(id, "key_mgmt", "NONE")
+    dev[0].set_network(id, "frequency", "2412")
+    dev[0].set_network(id, "scan_freq", "2412")
+    dev[0].select_network(id)
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected AP start")
+
+def test_wpas_ap_default_frequency(dev):
+    """wpa_supplicant AP mode - default frequency"""
+    id = dev[0].add_network()
+    dev[0].set_network(id, "mode", "2")
+    dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+    dev[0].set_network(id, "key_mgmt", "NONE")
+    dev[0].set_network(id, "scan_freq", "2412")
+    dev[0].select_network(id)
+    wait_ap_ready(dev[0])
+    dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2462")
+    dev[1].request("DISCONNECT")
+
+def test_wpas_ap_invalid_frequency(dev):
+    """wpa_supplicant AP mode - invalid frequency configuration"""
+    id = dev[0].add_network()
+    dev[0].set_network(id, "mode", "2")
+    dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+    dev[0].set_network(id, "key_mgmt", "NONE")
+    dev[0].set_network(id, "frequency", "2413")
+    dev[0].set_network(id, "scan_freq", "2412")
+    dev[0].select_network(id)
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected AP start")
+
+def test_wpas_ap_wps(dev):
+    """wpa_supplicant AP mode - WPS operations"""
+    id = dev[0].add_network()
+    dev[0].set_network(id, "mode", "2")
+    dev[0].set_network_quoted(id, "ssid", "wpas-ap-wps")
+    dev[0].set_network_quoted(id, "psk", "1234567890")
+    dev[0].set_network(id, "frequency", "2412")
+    dev[0].set_network(id, "scan_freq", "2412")
+    dev[0].select_network(id)
+    wait_ap_ready(dev[0])
+    bssid = dev[0].p2p_interface_addr()
+
+    logger.info("Test PBC mode start/stop")
+    if "FAIL" not in dev[0].request("WPS_CANCEL"):
+        raise Exception("Unexpected WPS_CANCEL success")
+    dev[0].request("WPS_PBC")
+    ev = dev[0].wait_event(["WPS-PBC-ACTIVE"])
+    if ev is None:
+        raise Exception("PBC mode start timeout")
+    if "OK" not in dev[0].request("WPS_CANCEL"):
+        raise Exception("Unexpected WPS_CANCEL failure")
+    ev = dev[0].wait_event(["WPS-TIMEOUT"])
+    if ev is None:
+        raise Exception("PBC mode disabling timeout")
+
+    logger.info("Test PBC protocol run")
+    dev[0].request("WPS_PBC")
+    ev = dev[0].wait_event(["WPS-PBC-ACTIVE"])
+    if ev is None:
+        raise Exception("PBC mode start timeout")
+    dev[1].request("WPS_PBC")
+    dev[1].wait_connected(timeout=30, error="WPS PBC operation timed out")
+    hwsim_utils.test_connectivity(dev[0], dev[1])
+
+    logger.info("Test AP PIN to learn configuration")
+    pin = dev[0].request("WPS_AP_PIN random")
+    if "FAIL" in pin:
+        raise Exception("Could not generate random AP PIN")
+    if pin not in dev[0].request("WPS_AP_PIN get"):
+        raise Exception("Could not fetch current AP PIN")
+    dev[2].wps_reg(bssid, pin)
+    hwsim_utils.test_connectivity(dev[1], dev[2])
+
+    dev[1].request("REMOVE_NETWORK all")
+    dev[2].request("REMOVE_NETWORK all")
+
+    logger.info("Test AP PIN operations")
+    dev[0].request("WPS_AP_PIN disable")
+    dev[0].request("WPS_AP_PIN set " + pin + " 1")
+    time.sleep(1.1)
+    if "FAIL" not in dev[0].request("WPS_AP_PIN get"):
+        raise Exception("AP PIN unexpectedly still enabled")
+
+    pin = dev[1].wps_read_pin()
+    dev[0].request("WPS_PIN any " + pin)
+    dev[1].request("WPS_PIN any " + pin)
+    dev[1].wait_connected(timeout=30)
+    dev[1].request("REMOVE_NETWORK all")
+    dev[1].dump_monitor()
+
+    dev[0].request("WPS_PIN any " + pin + " 100")
+    dev[1].request("WPS_PIN any " + pin)
+    dev[1].wait_connected(timeout=30)
+    dev[1].request("REMOVE_NETWORK all")
+    dev[1].dump_monitor()
+
+    dev[0].request("WPS_AP_PIN set 12345670")
+    dev[0].dump_monitor()
+
+    runs = ("88887777", "12340000", "00000000", "12345670")
+    for pin in runs:
+        logger.info("Try AP PIN " + pin)
+        dev[2].dump_monitor()
+        dev[2].request("WPS_REG " + bssid + " " + pin)
+        ev = dev[2].wait_event(["WPS-SUCCESS", "WPS-FAIL msg"], timeout=15)
+        if ev is None:
+            raise Exception("WPS operation timed out")
+        if "WPS-SUCCESS" in ev:
+            raise Exception("WPS operation succeeded unexpectedly")
+        dev[2].wait_disconnected(timeout=10)
+        dev[2].request("WPS_CANCEL")
+        dev[2].request("REMOVE_NETWORK all")
+    ev = dev[0].wait_event(["WPS-AP-SETUP-LOCKED"])
+    if ev is None:
+        raise Exception("WPS AP PIN not locked")
+
+    dev[0].dump_monitor()
+    logger.info("Test random AP PIN timeout")
+    pin = dev[0].request("WPS_AP_PIN random 1")
+    if "FAIL" in pin:
+        raise Exception("Could not generate random AP PIN")
+    res = dev[0].request("WPS_AP_PIN get")
+    if pin not in res:
+        raise Exception("Could not fetch current AP PIN")
+    for i in range(10):
+        time.sleep(0.2)
+        res = dev[0].request("WPS_AP_PIN get")
+        if "FAIL" in res:
+            break
+    if "FAIL" not in res:
+        raise Exception("WPS_AP_PIN random timeout did not work")
+
+    if "FAIL" not in dev[0].request("WPS_AP_PIN foo"):
+        raise Exception("Invalid WPS_AP_PIN command not rejected")
+    if "FAIL" not in dev[0].request("WPS_AP_PIN set"):
+        raise Exception("Invalid WPS_AP_PIN command not rejected")
+
+def test_wpas_ap_wps_pbc_overlap(dev):
+    """wpa_supplicant AP mode - WPS operations with PBC overlap"""
+    id = dev[0].add_network()
+    dev[0].set_network(id, "mode", "2")
+    dev[0].set_network_quoted(id, "ssid", "wpas-ap-wps")
+    dev[0].set_network_quoted(id, "psk", "1234567890")
+    dev[0].set_network(id, "frequency", "2412")
+    dev[0].set_network(id, "scan_freq", "2412")
+    dev[0].select_network(id)
+    wait_ap_ready(dev[0])
+    bssid = dev[0].p2p_interface_addr()
+
+    dev[1].scan_for_bss(bssid, freq="2412")
+    dev[1].dump_monitor()
+    dev[2].scan_for_bss(bssid, freq="2412")
+    dev[2].dump_monitor()
+    dev[0].request("WPS_PBC")
+    dev[1].request("WPS_PBC " + bssid)
+    dev[2].request("WPS_PBC " + bssid)
+
+    ev = dev[1].wait_event(["WPS-M2D"], timeout=15)
+    if ev is None:
+        raise Exception("PBC session overlap not detected (dev1)")
+    if "config_error=12" not in ev:
+        raise Exception("PBC session overlap not correctly reported (dev1)")
+
+    ev = dev[2].wait_event(["WPS-M2D"], timeout=15)
+    if ev is None:
+        raise Exception("PBC session overlap not detected (dev2)")
+    if "config_error=12" not in ev:
+        raise Exception("PBC session overlap not correctly reported (dev2)")
+
+    if "FAIL-PBC-OVERLAP" not in dev[0].request("WPS_PBC"):
+        raise Exception("WPS_PBC(AP) accepted during overlap")
+    if "FAIL-PBC-OVERLAP" not in dev[0].request("WPS_PBC any"):
+        raise Exception("WPS_PBC(AP) accepted during overlap")
+    dev[0].request("WPS_CANCEL")
+    dev[1].request("WPS_CANCEL")
+    dev[2].request("WPS_CANCEL")
+
+def test_wpas_ap_dfs(dev):
+    """wpa_supplicant AP mode - DFS"""
+    try:
+        _test_wpas_ap_dfs(dev)
+    finally:
+        set_country("00")
+        dev[0].request("SET country 00")
+        dev[1].flush_scan_cache()
+
+def _test_wpas_ap_dfs(dev):
+    set_country("US")
+    dev[0].request("SET country US")
+    id = dev[0].add_network()
+    dev[0].set_network(id, "mode", "2")
+    dev[0].set_network_quoted(id, "ssid", "wpas-ap-dfs")
+    dev[0].set_network(id, "key_mgmt", "NONE")
+    dev[0].set_network(id, "frequency", "5260")
+    dev[0].set_network(id, "scan_freq", "5260")
+    dev[0].select_network(id)
+
+    ev = dev[0].wait_event(["DFS-CAC-START"])
+    if ev is None:
+        # For now, assume DFS is not supported by all kernel builds.
+        raise HwsimSkip("CAC did not start - assume not supported")
+
+    ev = dev[0].wait_event(["DFS-CAC-COMPLETED"], timeout=70)
+    if ev is None:
+        raise Exception("CAC did not complete")
+    if "success=1" not in ev:
+        raise Exception("CAC failed")
+    if "freq=5260" not in ev:
+        raise Exception("Unexpected DFS freq result")
+
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"])
+    if ev is None:
+        raise Exception("AP failed to start")
+
+    dev[1].connect("wpas-ap-dfs", key_mgmt="NONE")
+
+def test_wpas_ap_disable(dev):
+    """wpa_supplicant AP mode - DISABLE_NETWORK"""
+    id = dev[0].add_network()
+    dev[0].set_network(id, "mode", "2")
+    dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+    dev[0].set_network(id, "key_mgmt", "NONE")
+    dev[0].set_network(id, "scan_freq", "2412")
+    dev[0].select_network(id)
+
+    ev = dev[0].wait_event(["AP-ENABLED"])
+    if ev is None:
+        raise Exception("AP-ENABLED event not seen")
+    wait_ap_ready(dev[0])
+    dev[0].request("DISABLE_NETWORK %d" % id)
+    ev = dev[0].wait_event(["AP-DISABLED"])
+    if ev is None:
+        raise Exception("AP-DISABLED event not seen")
+    dev[0].wait_disconnected()
diff --git a/hostap/tests/hwsim/test_wpas_config.py b/hostap/tests/hwsim/test_wpas_config.py
new file mode 100644
index 0000000..0af31da
--- /dev/null
+++ b/hostap/tests/hwsim/test_wpas_config.py
@@ -0,0 +1,146 @@
+# wpa_supplicant config file
+# 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.
+
+import logging
+logger = logging.getLogger()
+import os
+
+from wpasupplicant import WpaSupplicant
+
+def check_config(config):
+    with open(config, "r") as f:
+        data = f.read()
+    if "update_config=1\n" not in data:
+        raise Exception("Missing update_config")
+    if "device_name=name\n" not in data:
+        raise Exception("Missing device_name")
+    if "eapol_version=2\n" not in data:
+        raise Exception("Missing eapol_version")
+    if "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=" not in data:
+        raise Exception("Missing ctrl_interface")
+    if "blob-base64-foo={" not in data:
+        raise Exception("Missing blob")
+    if "cred={" not in data:
+        raise Exception("Missing cred")
+    if "network={" not in data:
+        raise Exception("Missing network")
+    if "wps_priority=5\n" not in data:
+        raise Exception("Missing wps_priority")
+    return data
+
+def test_wpas_config_file(dev):
+    """wpa_supplicant config file parsing/writing"""
+    config = "/tmp/test_wpas_config_file.conf"
+    if os.path.exists(config):
+        os.remove(config)
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    try:
+        wpas.interface_add("wlan5", config=config)
+        initialized = True
+    except:
+        initialized = False
+    if initialized:
+        raise Exception("Missing config file did not result in an error")
+
+    try:
+        with open(config, "w") as f:
+            f.write("update_config=1 \t\r\n")
+            f.write("# foo\n")
+            f.write("\n")
+            f.write(" \t\reapol_version=2")
+            for i in range(0, 100):
+                f.write("                    ")
+            f.write("foo\n")
+            f.write("device_name=name#foo\n")
+
+        wpas.interface_add("wlan5", config=config)
+
+        wpas.request("SET wps_priority 5")
+
+        id = wpas.add_network()
+        wpas.set_network_quoted(id, "ssid", "foo")
+        wpas.set_network_quoted(id, "psk", "12345678")
+        wpas.set_network(id, "bssid", "00:11:22:33:44:55")
+        wpas.set_network(id, "proto", "RSN")
+        wpas.set_network(id, "key_mgmt", "WPA-PSK-SHA256")
+        wpas.set_network(id, "pairwise", "CCMP")
+        wpas.set_network(id, "group", "CCMP")
+        wpas.set_network(id, "auth_alg", "OPEN")
+
+        id = wpas.add_cred()
+        wpas.set_cred(id, "priority", "3")
+        wpas.set_cred(id, "sp_priority", "6")
+        wpas.set_cred(id, "update_identifier", "4")
+        wpas.set_cred(id, "ocsp", "1")
+        wpas.set_cred(id, "eap", "TTLS")
+        wpas.set_cred(id, "req_conn_capab", "6:1234")
+        wpas.set_cred_quoted(id, "realm", "example.com")
+        wpas.set_cred_quoted(id, "provisioning_sp", "example.com")
+        wpas.set_cred_quoted(id, "domain", "example.com")
+        wpas.set_cred_quoted(id, "domain_suffix_match", "example.com")
+        wpas.set_cred(id, "roaming_consortium", "112233")
+        wpas.set_cred(id, "required_roaming_consortium", "112233")
+        wpas.set_cred_quoted(id, "roaming_partner",
+                             "roaming.example.net,1,127,*")
+        wpas.set_cred_quoted(id, "ca_cert", "/tmp/ca.pem")
+        wpas.set_cred_quoted(id, "username", "user")
+        wpas.set_cred_quoted(id, "password", "secret")
+        ev = wpas.wait_event(["CRED-MODIFIED 0 password"])
+
+        wpas.request("SET blob foo 12345678")
+
+        if "OK" not in wpas.request("SAVE_CONFIG"):
+            raise Exception("Failed to save configuration file")
+        if "OK" not in wpas.global_request("SAVE_CONFIG"):
+            raise Exception("Failed to save configuration file")
+
+        wpas.interface_remove("wlan5")
+        data1 = check_config(config)
+
+        wpas.interface_add("wlan5", config=config)
+        if len(wpas.list_networks()) != 1:
+            raise Exception("Unexpected number of networks")
+        if len(wpas.request("LIST_CREDS").splitlines()) != 2:
+            raise Exception("Unexpected number of credentials")
+
+        if "OK" not in wpas.request("SAVE_CONFIG"):
+            raise Exception("Failed to save configuration file")
+        data2 = check_config(config)
+
+        if data1 != data2:
+            logger.debug(data1)
+            logger.debug(data2)
+            raise Exception("Unexpected configuration change")
+
+        wpas.request("SET update_config 0")
+        if "OK" in wpas.request("SAVE_CONFIG"):
+            raise Exception("SAVE_CONFIG succeeded unexpectedly")
+        if "OK" in wpas.global_request("SAVE_CONFIG"):
+            raise Exception("SAVE_CONFIG (global) succeeded unexpectedly")
+
+        # replace the config file with a directory to break writing/renaming
+        os.remove(config)
+        os.mkdir(config)
+        wpas.request("SET update_config 1")
+        if "OK" in wpas.request("SAVE_CONFIG"):
+            raise Exception("SAVE_CONFIG succeeded unexpectedly")
+        if "OK" in wpas.global_request("SAVE_CONFIG"):
+            raise Exception("SAVE_CONFIG (global) succeeded unexpectedly")
+
+    finally:
+        try:
+            os.remove(config)
+        except:
+            pass
+        try:
+            os.remove(config + ".tmp")
+        except:
+            pass
+        try:
+            os.rmdir(config)
+        except:
+            pass
diff --git a/hostap/tests/hwsim/test_wpas_ctrl.py b/hostap/tests/hwsim/test_wpas_ctrl.py
new file mode 100644
index 0000000..de7cde4
--- /dev/null
+++ b/hostap/tests/hwsim/test_wpas_ctrl.py
@@ -0,0 +1,1759 @@
+# wpa_supplicant control interface
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import os
+import socket
+import subprocess
+import time
+
+import hostapd
+import hwsim_utils
+from hwsim import HWSimRadio
+from wpasupplicant import WpaSupplicant
+from utils import alloc_fail
+
+def test_wpas_ctrl_network(dev):
+    """wpa_supplicant ctrl_iface network set/get"""
+    id = dev[0].add_network()
+
+    if "FAIL" not in dev[0].request("SET_NETWORK " + str(id)):
+        raise Exception("Unexpected success for invalid SET_NETWORK")
+    if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + " name"):
+        raise Exception("Unexpected success for invalid SET_NETWORK")
+    if "FAIL" not in dev[0].request("SET_NETWORK " + str(id + 1) + " proto OPEN"):
+        raise Exception("Unexpected success for invalid network id")
+    if "FAIL" not in dev[0].request("GET_NETWORK " + str(id)):
+        raise Exception("Unexpected success for invalid GET_NETWORK")
+    if "FAIL" not in dev[0].request("GET_NETWORK " + str(id + 1) + " proto"):
+        raise Exception("Unexpected success for invalid network id")
+
+    tests = (("key_mgmt", "WPA-PSK WPA-EAP IEEE8021X NONE WPA-NONE FT-PSK FT-EAP WPA-PSK-SHA256 WPA-EAP-SHA256"),
+             ("pairwise", "CCMP-256 GCMP-256 CCMP GCMP TKIP"),
+             ("group", "CCMP-256 GCMP-256 CCMP GCMP TKIP"),
+             ("auth_alg", "OPEN SHARED LEAP"),
+             ("scan_freq", "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"),
+             ("freq_list", "2412 2417"),
+             ("scan_ssid", "1"),
+             ("bssid", "00:11:22:33:44:55"),
+             ("proto", "WPA RSN OSEN"),
+             ("eap", "TLS"),
+             ("go_p2p_dev_addr", "22:33:44:55:66:aa"),
+             ("p2p_client_list", "22:33:44:55:66:bb 02:11:22:33:44:55"))
+
+    dev[0].set_network_quoted(id, "ssid", "test")
+    for field, value in tests:
+        dev[0].set_network(id, field, value)
+        res = dev[0].get_network(id, field)
+        if res != value:
+            raise Exception("Unexpected response for '" + field + "': '" + res + "'")
+
+    q_tests = (("identity", "hello"),
+               ("anonymous_identity", "foo@nowhere.com"))
+    for field, value in q_tests:
+        dev[0].set_network_quoted(id, field, value)
+        res = dev[0].get_network(id, field)
+        if res != '"' + value + '"':
+            raise Exception("Unexpected quoted response for '" + field + "': '" + res + "'")
+
+    get_tests = (("foo", None), ("ssid", '"test"'))
+    for field, value in get_tests:
+        res = dev[0].get_network(id, field)
+        if res != value:
+            raise Exception("Unexpected response for '" + field + "': '" + res + "'")
+
+    if dev[0].get_network(id, "password"):
+        raise Exception("Unexpected response for 'password'")
+    dev[0].set_network_quoted(id, "password", "foo")
+    if dev[0].get_network(id, "password") != '*':
+        raise Exception("Unexpected response for 'password' (expected *)")
+    dev[0].set_network(id, "password", "hash:12345678901234567890123456789012")
+    if dev[0].get_network(id, "password") != '*':
+        raise Exception("Unexpected response for 'password' (expected *)")
+    dev[0].set_network(id, "password", "NULL")
+    if dev[0].get_network(id, "password"):
+        raise Exception("Unexpected response for 'password'")
+    if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + " password hash:12"):
+        raise Exception("Unexpected success for invalid password hash")
+    if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + " password hash:123456789012345678x0123456789012"):
+        raise Exception("Unexpected success for invalid password hash")
+
+    dev[0].set_network(id, "identity", "414243")
+    if dev[0].get_network(id, "identity") != '"ABC"':
+        raise Exception("Unexpected identity hex->text response")
+
+    dev[0].set_network(id, "identity", 'P"abc\ndef"')
+    if dev[0].get_network(id, "identity") != "6162630a646566":
+        raise Exception("Unexpected identity printf->hex response")
+
+    if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' identity P"foo'):
+        raise Exception("Unexpected success for invalid identity string")
+
+    if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' identity 12x3'):
+        raise Exception("Unexpected success for invalid identity string")
+
+    for i in range(0, 4):
+        if "FAIL" in dev[0].request("SET_NETWORK " + str(id) + ' wep_key' + str(i) + ' aabbccddee'):
+            raise Exception("Unexpected wep_key set failure")
+        if dev[0].get_network(id, "wep_key" + str(i)) != '*':
+            raise Exception("Unexpected wep_key get failure")
+
+    if "FAIL" in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:22:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'):
+        raise Exception("Unexpected failure for psk_list string")
+
+    if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list 00:11:x2:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'):
+        raise Exception("Unexpected success for invalid psk_list string")
+
+    if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:x2:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'):
+        raise Exception("Unexpected success for invalid psk_list string")
+
+    if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:22:33:44:55+0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'):
+        raise Exception("Unexpected success for invalid psk_list string")
+
+    if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:22:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde'):
+        raise Exception("Unexpected success for invalid psk_list string")
+
+    if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:22:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdex'):
+        raise Exception("Unexpected success for invalid psk_list string")
+
+    if dev[0].get_network(id, "psk_list"):
+        raise Exception("Unexpected psk_list get response")
+
+    if dev[0].list_networks()[0]['ssid'] != "test":
+        raise Exception("Unexpected ssid in LIST_NETWORKS")
+    dev[0].set_network(id, "ssid", "NULL")
+    if dev[0].list_networks()[0]['ssid'] != "":
+        raise Exception("Unexpected ssid in LIST_NETWORKS after clearing it")
+
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' ssid "0123456789abcdef0123456789abcdef0"'):
+        raise Exception("Too long SSID accepted")
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' scan_ssid qwerty'):
+        raise Exception("Invalid integer accepted")
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' scan_ssid 2'):
+        raise Exception("Too large integer accepted")
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' psk 12345678'):
+        raise Exception("Invalid PSK accepted")
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' psk "1234567"'):
+        raise Exception("Too short PSK accepted")
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' psk "1234567890123456789012345678901234567890123456789012345678901234"'):
+        raise Exception("Too long PSK accepted")
+    dev[0].set_network_quoted(id, "psk", "123456768");
+    dev[0].set_network_quoted(id, "psk", "123456789012345678901234567890123456789012345678901234567890123");
+    if dev[0].get_network(id, "psk") != '*':
+        raise Exception("Unexpected psk read result");
+
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' eap UNKNOWN'):
+        raise Exception("Unknown EAP method accepted")
+
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' password "foo'):
+        raise Exception("Invalid password accepted")
+
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' wep_key0 "foo'):
+        raise Exception("Invalid WEP key accepted")
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' wep_key0 "12345678901234567"'):
+        raise Exception("Too long WEP key accepted")
+    # too short WEP key is ignored
+    dev[0].set_network_quoted(id, "wep_key0", "1234")
+    dev[0].set_network_quoted(id, "wep_key1", "12345")
+    dev[0].set_network_quoted(id, "wep_key2", "1234567890123")
+    dev[0].set_network_quoted(id, "wep_key3", "1234567890123456")
+
+    dev[0].set_network(id, "go_p2p_dev_addr", "any")
+    if dev[0].get_network(id, "go_p2p_dev_addr") is not None:
+        raise Exception("Unexpected go_p2p_dev_addr value")
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' go_p2p_dev_addr 00:11:22:33:44'):
+        raise Exception("Invalid go_p2p_dev_addr accepted")
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' p2p_client_list 00:11:22:33:44'):
+        raise Exception("Invalid p2p_client_list accepted")
+    if "FAIL" in dev[0].request('SET_NETWORK ' + str(id) + ' p2p_client_list 00:11:22:33:44:55 00:1'):
+        raise Exception("p2p_client_list truncation workaround failed")
+    if dev[0].get_network(id, "p2p_client_list") != "00:11:22:33:44:55":
+        raise Exception("p2p_client_list truncation workaround did not work")
+
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' auth_alg '):
+        raise Exception("Empty auth_alg accepted")
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' auth_alg FOO'):
+        raise Exception("Invalid auth_alg accepted")
+
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' proto '):
+        raise Exception("Empty proto accepted")
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' proto FOO'):
+        raise Exception("Invalid proto accepted")
+
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' pairwise '):
+        raise Exception("Empty pairwise accepted")
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' pairwise FOO'):
+        raise Exception("Invalid pairwise accepted")
+    if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' pairwise WEP40'):
+        raise Exception("Invalid pairwise accepted")
+
+    if "OK" not in dev[0].request('BSSID ' + str(id) + ' 00:11:22:33:44:55'):
+        raise Exception("Unexpected BSSID failure")
+    if dev[0].request("GET_NETWORK 0 bssid") != '00:11:22:33:44:55':
+        raise Exception("BSSID command did not set network bssid")
+    if "OK" not in dev[0].request('BSSID ' + str(id) + ' 00:00:00:00:00:00'):
+        raise Exception("Unexpected BSSID failure")
+    if "FAIL" not in dev[0].request("GET_NETWORK 0 bssid"):
+        raise Exception("bssid claimed configured after clearing")
+    if "FAIL" not in dev[0].request('BSSID 123 00:11:22:33:44:55'):
+        raise Exception("Unexpected BSSID success")
+    if "FAIL" not in dev[0].request('BSSID ' + str(id) + ' 00:11:22:33:44'):
+        raise Exception("Unexpected BSSID success")
+    if "FAIL" not in dev[0].request('BSSID ' + str(id)):
+        raise Exception("Unexpected BSSID success")
+
+    tests = [ "02:11:22:33:44:55",
+              "02:11:22:33:44:55 02:ae:be:ce:53:77",
+              "02:11:22:33:44:55/ff:00:ff:00:ff:00",
+              "02:11:22:33:44:55/ff:00:ff:00:ff:00 f2:99:88:77:66:55",
+              "f2:99:88:77:66:55 02:11:22:33:44:55/ff:00:ff:00:ff:00",
+              "f2:99:88:77:66:55 02:11:22:33:44:55/ff:00:ff:00:ff:00 12:34:56:78:90:ab",
+              "02:11:22:33:44:55/ff:ff:ff:00:00:00 02:ae:be:ce:53:77/00:00:00:00:00:ff" ]
+    for val in tests:
+        dev[0].set_network(id, "bssid_blacklist", val)
+        res = dev[0].get_network(id, "bssid_blacklist")
+        if res != val:
+            raise Exception("Unexpected bssid_blacklist value: %s != %s" % (res, val))
+        dev[0].set_network(id, "bssid_whitelist", val)
+        res = dev[0].get_network(id, "bssid_whitelist")
+        if res != val:
+            raise Exception("Unexpected bssid_whitelist value: %s != %s" % (res, val))
+
+    tests = [ "foo",
+              "00:11:22:33:44:5",
+              "00:11:22:33:44:55q",
+              "00:11:22:33:44:55/",
+              "00:11:22:33:44:55/66:77:88:99:aa:b" ]
+    for val in tests:
+        if "FAIL" not in dev[0].request("SET_NETWORK %d bssid_blacklist %s" % (id, val)):
+            raise Exception("Invalid bssid_blacklist value accepted")
+
+def test_wpas_ctrl_network_oom(dev):
+    """wpa_supplicant ctrl_iface network OOM in string parsing"""
+    id = dev[0].add_network()
+
+    tests = [ ('"foo"', 1, 'dup_binstr;wpa_config_set'),
+              ('P"foo"', 1, 'dup_binstr;wpa_config_set'),
+              ('P"foo"', 2, 'wpa_config_set'),
+              ('112233', 1, 'wpa_config_set') ]
+    for val,count,func in tests:
+        with alloc_fail(dev[0], count, func):
+            if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' ssid ' + val):
+                raise Exception("Unexpected success for SET_NETWORK during OOM")
+
+def test_wpas_ctrl_many_networks(dev, apdev):
+    """wpa_supplicant ctrl_iface LIST_NETWORKS with huge number of networks"""
+    for i in range(1000):
+        id = dev[0].add_network()
+    res = dev[0].request("LIST_NETWORKS")
+    if str(id) in res:
+        raise Exception("Last added network was unexpectedly included")
+    res = dev[0].request("LIST_NETWORKS LAST_ID=%d" % (id - 2))
+    if str(id) not in res:
+        raise Exception("Last added network was not present when using LAST_ID")
+    # This command can take a very long time under valgrind testing on a low
+    # power CPU, so increase the command timeout significantly to avoid issues
+    # with the test case failing and following reset operation timing out.
+    dev[0].request("REMOVE_NETWORK all", timeout=60)
+
+def test_wpas_ctrl_dup_network(dev, apdev):
+    """wpa_supplicant ctrl_iface DUP_NETWORK"""
+    ssid = "target"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    src = dev[0].connect("another", psk=passphrase, scan_freq="2412",
+                         only_add_network=True)
+    id = dev[0].add_network()
+    dev[0].set_network_quoted(id, "ssid", ssid)
+    for f in [ "key_mgmt", "psk", "scan_freq" ]:
+        res = dev[0].request("DUP_NETWORK {} {} {}".format(src, id, f))
+        if "OK" not in res:
+            raise Exception("DUP_NETWORK failed")
+    dev[0].connect_network(id)
+
+    if "FAIL" not in dev[0].request("DUP_NETWORK "):
+        raise Exception("Unexpected DUP_NETWORK success")
+    if "FAIL" not in dev[0].request("DUP_NETWORK %d " % id):
+        raise Exception("Unexpected DUP_NETWORK success")
+    if "FAIL" not in dev[0].request("DUP_NETWORK %d %d" % (id, id)):
+        raise Exception("Unexpected DUP_NETWORK success")
+    if "FAIL" not in dev[0].request("DUP_NETWORK 123456 1234567 "):
+        raise Exception("Unexpected DUP_NETWORK success")
+    if "FAIL" not in dev[0].request("DUP_NETWORK %d 123456 " % id):
+        raise Exception("Unexpected DUP_NETWORK success")
+    if "FAIL" not in dev[0].request("DUP_NETWORK %d %d foo" % (id, id)):
+        raise Exception("Unexpected DUP_NETWORK success")
+    dev[0].request("DISCONNECT")
+    if "OK" not in dev[0].request("DUP_NETWORK %d %d ssid" % (id, id)):
+        raise Exception("Unexpected DUP_NETWORK failure")
+
+def test_wpas_ctrl_dup_network_global(dev, apdev):
+    """wpa_supplicant ctrl_iface DUP_NETWORK (global)"""
+    ssid = "target"
+    passphrase = 'qwertyuiop'
+    params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    src = dev[0].connect("another", psk=passphrase, scan_freq="2412",
+                         only_add_network=True)
+    id = dev[0].add_network()
+    dev[0].set_network_quoted(id, "ssid", ssid)
+    for f in [ "key_mgmt", "psk", "scan_freq" ]:
+        res = dev[0].global_request("DUP_NETWORK {} {} {} {} {}".format(dev[0].ifname, dev[0].ifname, src, id, f))
+        if "OK" not in res:
+            raise Exception("DUP_NETWORK failed")
+    dev[0].connect_network(id)
+
+    if "FAIL" not in dev[0].global_request("DUP_NETWORK "):
+        raise Exception("Unexpected DUP_NETWORK success")
+    if "FAIL" not in dev[0].global_request("DUP_NETWORK %s" % dev[0].ifname):
+        raise Exception("Unexpected DUP_NETWORK success")
+    if "FAIL" not in dev[0].global_request("DUP_NETWORK %s %s" % (dev[0].ifname, dev[0].ifname)):
+        raise Exception("Unexpected DUP_NETWORK success")
+    if "FAIL" not in dev[0].global_request("DUP_NETWORK %s %s %d" % (dev[0].ifname, dev[0].ifname, id)):
+        raise Exception("Unexpected DUP_NETWORK success")
+    if "FAIL" not in dev[0].global_request("DUP_NETWORK %s %s %d %d" % (dev[0].ifname, dev[0].ifname, id, id)):
+        raise Exception("Unexpected DUP_NETWORK success")
+    dev[0].request("DISCONNECT")
+    if "OK" not in dev[0].global_request("DUP_NETWORK %s %s %d %d ssid" % (dev[0].ifname, dev[0].ifname, id, id)):
+        raise Exception("Unexpected DUP_NETWORK failure")
+
+def add_cred(dev):
+    id = dev.add_cred()
+    ev = dev.wait_event(["CRED-ADDED"])
+    if ev is None:
+        raise Exception("Missing CRED-ADDED event")
+    if " " + str(id) not in ev:
+        raise Exception("CRED-ADDED event without matching id")
+    return id
+
+def set_cred(dev, id, field, value):
+    dev.set_cred(id, field, value)
+    ev = dev.wait_event(["CRED-MODIFIED"])
+    if ev is None:
+        raise Exception("Missing CRED-MODIFIED event")
+    if " " + str(id) + " " not in ev:
+        raise Exception("CRED-MODIFIED event without matching id")
+    if field not in ev:
+        raise Exception("CRED-MODIFIED event without matching field")
+
+def set_cred_quoted(dev, id, field, value):
+    dev.set_cred_quoted(id, field, value)
+    ev = dev.wait_event(["CRED-MODIFIED"])
+    if ev is None:
+        raise Exception("Missing CRED-MODIFIED event")
+    if " " + str(id) + " " not in ev:
+        raise Exception("CRED-MODIFIED event without matching id")
+    if field not in ev:
+        raise Exception("CRED-MODIFIED event without matching field")
+
+def remove_cred(dev, id):
+    dev.remove_cred(id)
+    ev = dev.wait_event(["CRED-REMOVED"])
+    if ev is None:
+        raise Exception("Missing CRED-REMOVED event")
+    if " " + str(id) not in ev:
+        raise Exception("CRED-REMOVED event without matching id")
+
+def test_wpas_ctrl_cred(dev):
+    """wpa_supplicant ctrl_iface cred set"""
+    id1 = add_cred(dev[0])
+    if "FAIL" not in dev[0].request("SET_CRED " + str(id1 + 1) + " temporary 1"):
+        raise Exception("SET_CRED succeeded unexpectedly on unknown cred id")
+    if "FAIL" not in dev[0].request("SET_CRED " + str(id1)):
+        raise Exception("Invalid SET_CRED succeeded unexpectedly")
+    if "FAIL" not in dev[0].request("SET_CRED " + str(id1) + " temporary"):
+        raise Exception("Invalid SET_CRED succeeded unexpectedly")
+    if "FAIL" not in dev[0].request("GET_CRED " + str(id1 + 1) + " temporary"):
+        raise Exception("GET_CRED succeeded unexpectedly on unknown cred id")
+    if "FAIL" not in dev[0].request("GET_CRED " + str(id1)):
+        raise Exception("Invalid GET_CRED succeeded unexpectedly")
+    if "FAIL" not in dev[0].request("GET_CRED " + str(id1) + " foo"):
+        raise Exception("Invalid GET_CRED succeeded unexpectedly")
+    id = add_cred(dev[0])
+    id2 = add_cred(dev[0])
+    set_cred(dev[0], id, "temporary", "1")
+    set_cred(dev[0], id, "priority", "1")
+    set_cred(dev[0], id, "pcsc", "1")
+    set_cred_quoted(dev[0], id, "private_key_passwd", "test")
+    set_cred_quoted(dev[0], id, "domain_suffix_match", "test")
+    set_cred_quoted(dev[0], id, "phase1", "test")
+    set_cred_quoted(dev[0], id, "phase2", "test")
+
+    if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " eap FOO"):
+        raise Exception("Unexpected success on unknown EAP method")
+
+    if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " username 12xa"):
+        raise Exception("Unexpected success on invalid string")
+
+    for i in ("11", "1122", "112233445566778899aabbccddeeff00"):
+        if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " roaming_consortium " + i):
+            raise Exception("Unexpected success on invalid roaming_consortium")
+
+    dev[0].set_cred(id, "excluded_ssid", "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff")
+    if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " excluded_ssid 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00"):
+        raise Exception("Unexpected success on invalid excluded_ssid")
+
+    if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " foo 4142"):
+        raise Exception("Unexpected success on unknown field")
+
+    id3 = add_cred(dev[0])
+    id4 = add_cred(dev[0])
+    if len(dev[0].request("LIST_CREDS").splitlines()) != 6:
+        raise Exception("Unexpected LIST_CREDS result(1)")
+
+    remove_cred(dev[0], id1)
+    remove_cred(dev[0], id3)
+    remove_cred(dev[0], id4)
+    remove_cred(dev[0], id2)
+    remove_cred(dev[0], id)
+    if "FAIL" not in dev[0].request("REMOVE_CRED 1"):
+        raise Exception("Unexpected success on invalid remove cred")
+    if len(dev[0].request("LIST_CREDS").splitlines()) != 1:
+        raise Exception("Unexpected LIST_CREDS result(2)")
+
+    id = add_cred(dev[0])
+    values = [ ("temporary", "1", False),
+               ("temporary", "0", False),
+               ("pcsc", "1", False),
+               ("realm", "example.com", True),
+               ("username", "user@example.com", True),
+               ("password", "foo", True, "*"),
+               ("ca_cert", "ca.pem", True),
+               ("client_cert", "user.pem", True),
+               ("private_key", "key.pem", True),
+               ("private_key_passwd", "foo", True, "*"),
+               ("imsi", "310026-000000000", True),
+               ("milenage", "foo", True, "*"),
+               ("domain_suffix_match", "example.com", True),
+               ("domain", "example.com", True),
+               ("domain", "example.org", True, "example.com\nexample.org"),
+               ("roaming_consortium", "0123456789", False),
+               ("required_roaming_consortium", "456789", False),
+               ("eap", "TTLS", False),
+               ("phase1", "foo=bar1", True),
+               ("phase2", "foo=bar2", True),
+               ("excluded_ssid", "test", True),
+               ("excluded_ssid", "foo", True, "test\nfoo"),
+               ("roaming_partner", "example.com,0,4,*", True),
+               ("roaming_partner", "example.org,1,2,US", True,
+                "example.com,0,4,*\nexample.org,1,2,US"),
+               ("update_identifier", "4", False),
+               ("provisioning_sp", "sp.example.com", True),
+               ("sp_priority", "7", False),
+               ("min_dl_bandwidth_home", "100", False),
+               ("min_ul_bandwidth_home", "101", False),
+               ("min_dl_bandwidth_roaming", "102", False),
+               ("min_ul_bandwidth_roaming", "103", False),
+               ("max_bss_load", "57", False),
+               ("req_conn_capab", "6:22,80,443", False),
+               ("req_conn_capab", "17:500", False, "6:22,80,443\n17:500"),
+               ("req_conn_capab", "50", False, "6:22,80,443\n17:500\n50"),
+               ("ocsp", "1", False) ]
+    for v in values:
+        if v[2]:
+            set_cred_quoted(dev[0], id, v[0], v[1])
+        else:
+            set_cred(dev[0], id, v[0], v[1])
+        val = dev[0].get_cred(id, v[0])
+        if len(v) == 4:
+            expect = v[3]
+        else:
+            expect = v[1]
+        if val != expect:
+            raise Exception("Unexpected GET_CRED value for {}: {} != {}".format(v[0], val, expect))
+    creds = dev[0].request("LIST_CREDS").splitlines()
+    if len(creds) != 2:
+        raise Exception("Unexpected LIST_CREDS result(3)")
+    if creds[1] != "0\texample.com\tuser@example.com\texample.com\t310026-000000000":
+        raise Exception("Unexpected LIST_CREDS value")
+    remove_cred(dev[0], id)
+    if len(dev[0].request("LIST_CREDS").splitlines()) != 1:
+        raise Exception("Unexpected LIST_CREDS result(4)")
+
+    id = add_cred(dev[0])
+    set_cred_quoted(dev[0], id, "domain", "foo.example.com")
+    id = add_cred(dev[0])
+    set_cred_quoted(dev[0], id, "domain", "bar.example.com")
+    id = add_cred(dev[0])
+    set_cred_quoted(dev[0], id, "domain", "foo.example.com")
+    if "OK" not in dev[0].request("REMOVE_CRED sp_fqdn=foo.example.com"):
+        raise Exception("REMOVE_CRED failed")
+    creds = dev[0].request("LIST_CREDS")
+    if "foo.example.com" in creds:
+        raise Exception("REMOVE_CRED sp_fqdn did not remove cred")
+    if "bar.example.com" not in creds:
+        raise Exception("REMOVE_CRED sp_fqdn removed incorrect cred")
+    dev[0].request("REMOVE_CRED all")
+
+    id = add_cred(dev[0])
+    set_cred_quoted(dev[0], id, "domain", "foo.example.com")
+    set_cred_quoted(dev[0], id, "provisioning_sp", "sp.foo.example.com")
+    id = add_cred(dev[0])
+    set_cred_quoted(dev[0], id, "domain", "bar.example.com")
+    set_cred_quoted(dev[0], id, "provisioning_sp", "sp.bar.example.com")
+    id = add_cred(dev[0])
+    set_cred_quoted(dev[0], id, "domain", "foo.example.com")
+    set_cred_quoted(dev[0], id, "provisioning_sp", "sp.foo.example.com")
+    if "OK" not in dev[0].request("REMOVE_CRED provisioning_sp=sp.foo.example.com"):
+        raise Exception("REMOVE_CRED failed")
+    creds = dev[0].request("LIST_CREDS")
+    if "foo.example.com" in creds:
+        raise Exception("REMOVE_CRED provisioning_sp did not remove cred")
+    if "bar.example.com" not in creds:
+        raise Exception("REMOVE_CRED provisioning_sp removed incorrect cred")
+    dev[0].request("REMOVE_CRED all")
+
+    # Test large number of creds and LIST_CREDS truncation
+    dev[0].dump_monitor()
+    for i in range(0, 100):
+        id = add_cred(dev[0])
+        set_cred_quoted(dev[0], id, "realm", "relatively.long.realm.test%d.example.com" % i)
+        dev[0].dump_monitor()
+    creds = dev[0].request("LIST_CREDS")
+    for i in range(0, 100):
+        dev[0].remove_cred(i)
+        dev[0].dump_monitor()
+    if len(creds) < 3900 or len(creds) > 4100:
+        raise Exception("Unexpected LIST_CREDS length: %d" % len(creds))
+    if "test10.example.com" not in creds:
+        raise Exception("Missing credential")
+    if len(creds.splitlines()) > 95:
+        raise Exception("Too many LIST_CREDS entries in the buffer")
+
+def test_wpas_ctrl_pno(dev):
+    """wpa_supplicant ctrl_iface pno"""
+    if "FAIL" not in dev[0].request("SET pno 1"):
+        raise Exception("Unexpected success in enabling PNO without enabled network blocks")
+    id = dev[0].add_network()
+    dev[0].set_network_quoted(id, "ssid", "test")
+    dev[0].set_network(id, "key_mgmt", "NONE")
+    dev[0].request("ENABLE_NETWORK " + str(id) + " no-connect")
+    #mac80211_hwsim does not yet support PNO, so this fails
+    if "FAIL" not in dev[0].request("SET pno 1"):
+        raise Exception("Unexpected success in enabling PNO")
+    if "FAIL" not in dev[0].request("SET pno 1 freq=2000-3000,5180"):
+        raise Exception("Unexpected success in enabling PNO")
+    if "FAIL" not in dev[0].request("SET pno 1 freq=0-6000"):
+        raise Exception("Unexpected success in enabling PNO")
+    if "FAIL" in dev[0].request("SET pno 0"):
+        raise Exception("Unexpected failure in disabling PNO")
+
+def test_wpas_ctrl_get(dev):
+    """wpa_supplicant ctrl_iface get"""
+    if "FAIL" in dev[0].request("GET version"):
+        raise Exception("Unexpected get failure for version")
+    if "FAIL" in dev[0].request("GET wifi_display"):
+        raise Exception("Unexpected get failure for wifi_display")
+    if "FAIL" not in dev[0].request("GET foo"):
+        raise Exception("Unexpected success on get command")
+
+def test_wpas_ctrl_preauth(dev):
+    """wpa_supplicant ctrl_iface preauth"""
+    if "FAIL" not in dev[0].request("PREAUTH "):
+        raise Exception("Unexpected success on invalid PREAUTH")
+    if "FAIL" in dev[0].request("PREAUTH 00:11:22:33:44:55"):
+        raise Exception("Unexpected failure on PREAUTH")
+
+def test_wpas_ctrl_stkstart(dev):
+    """wpa_supplicant ctrl_iface strkstart"""
+    if "FAIL" not in dev[0].request("STKSTART "):
+        raise Exception("Unexpected success on invalid STKSTART")
+    if "FAIL" not in dev[0].request("STKSTART 00:11:22:33:44:55"):
+        raise Exception("Unexpected success on STKSTART")
+
+def test_wpas_ctrl_tdls_discover(dev):
+    """wpa_supplicant ctrl_iface tdls_discover"""
+    if "FAIL" not in dev[0].request("TDLS_DISCOVER "):
+        raise Exception("Unexpected success on invalid TDLS_DISCOVER")
+    if "FAIL" not in dev[0].request("TDLS_DISCOVER 00:11:22:33:44:55"):
+        raise Exception("Unexpected success on TDLS_DISCOVER")
+
+def test_wpas_ctrl_tdls_chan_switch(dev):
+    """wpa_supplicant ctrl_iface tdls_chan_switch error cases"""
+    for args in [ '', '00:11:22:33:44:55' ]:
+        if "FAIL" not in dev[0].request("TDLS_CANCEL_CHAN_SWITCH " + args):
+            raise Exception("Unexpected success on invalid TDLS_CANCEL_CHAN_SWITCH: " + args)
+
+    for args in [ '', 'foo ', '00:11:22:33:44:55 ', '00:11:22:33:44:55 q',
+                  '00:11:22:33:44:55 81', '00:11:22:33:44:55 81 1234',
+                  '00:11:22:33:44:55 81 1234 center_freq1=234 center_freq2=345 bandwidth=456 sec_channel_offset=567 ht vht' ]:
+        if "FAIL" not in dev[0].request("TDLS_CHAN_SWITCH " + args):
+            raise Exception("Unexpected success on invalid TDLS_CHAN_SWITCH: " + args)
+
+def test_wpas_ctrl_addr(dev):
+    """wpa_supplicant ctrl_iface invalid address"""
+    if "FAIL" not in dev[0].request("TDLS_SETUP "):
+        raise Exception("Unexpected success on invalid TDLS_SETUP")
+    if "FAIL" not in dev[0].request("TDLS_TEARDOWN "):
+        raise Exception("Unexpected success on invalid TDLS_TEARDOWN")
+    if "FAIL" not in dev[0].request("FT_DS "):
+        raise Exception("Unexpected success on invalid FT_DS")
+    if "FAIL" not in dev[0].request("WPS_PBC 00:11:22:33:44"):
+        raise Exception("Unexpected success on invalid WPS_PBC")
+    if "FAIL" not in dev[0].request("WPS_PIN 00:11:22:33:44"):
+        raise Exception("Unexpected success on invalid WPS_PIN")
+    if "FAIL" not in dev[0].request("WPS_NFC 00:11:22:33:44"):
+        raise Exception("Unexpected success on invalid WPS_NFC")
+    if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44 12345670"):
+        raise Exception("Unexpected success on invalid WPS_REG")
+    if "FAIL" not in dev[0].request("IBSS_RSN 00:11:22:33:44"):
+        raise Exception("Unexpected success on invalid IBSS_RSN")
+    if "FAIL" not in dev[0].request("BLACKLIST 00:11:22:33:44"):
+        raise Exception("Unexpected success on invalid BLACKLIST")
+
+def test_wpas_ctrl_wps_errors(dev):
+    """wpa_supplicant ctrl_iface WPS error cases"""
+    if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44:55"):
+        raise Exception("Unexpected success on invalid WPS_REG")
+    if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44:55 12345670 2233"):
+        raise Exception("Unexpected success on invalid WPS_REG")
+    if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44:55 12345670 2233 OPEN"):
+        raise Exception("Unexpected success on invalid WPS_REG")
+    if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44:55 12345670 2233 OPEN NONE"):
+        raise Exception("Unexpected success on invalid WPS_REG")
+
+    if "FAIL" not in dev[0].request("WPS_AP_PIN random"):
+        raise Exception("Unexpected success on WPS_AP_PIN in non-AP mode")
+
+    if "FAIL" not in dev[0].request("WPS_ER_PIN any"):
+        raise Exception("Unexpected success on invalid WPS_ER_PIN")
+
+    if "FAIL" not in dev[0].request("WPS_ER_LEARN 00:11:22:33:44:55"):
+        raise Exception("Unexpected success on invalid WPS_ER_LEARN")
+
+    if "FAIL" not in dev[0].request("WPS_ER_SET_CONFIG 00:11:22:33:44:55"):
+        raise Exception("Unexpected success on invalid WPS_ER_SET_CONFIG")
+
+    if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55"):
+        raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
+    if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55 12345670"):
+        raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
+    if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55 12345670 2233"):
+        raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
+    if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55 12345670 2233 OPEN"):
+        raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
+    if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55 12345670 2233 OPEN NONE"):
+        raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
+
+    if "FAIL" not in dev[0].request("WPS_ER_NFC_CONFIG_TOKEN WPS"):
+        raise Exception("Unexpected success on invalid WPS_ER_NFC_CONFIG_TOKEN")
+    if "FAIL" not in dev[0].request("WPS_ER_NFC_CONFIG_TOKEN FOO 00:11:22:33:44:55"):
+        raise Exception("Unexpected success on invalid WPS_ER_NFC_CONFIG_TOKEN")
+    if "FAIL" not in dev[0].request("WPS_ER_NFC_CONFIG_TOKEN NDEF 00:11:22:33:44:55"):
+        raise Exception("Unexpected success on invalid WPS_ER_NFC_CONFIG_TOKEN")
+
+    if "FAIL" not in dev[0].request("WPS_NFC_CONFIG_TOKEN FOO"):
+        raise Exception("Unexpected success on invalid WPS_NFC_CONFIG_TOKEN")
+    if "FAIL" not in dev[0].request("WPS_NFC_CONFIG_TOKEN WPS FOO"):
+        raise Exception("Unexpected success on invalid WPS_NFC_CONFIG_TOKEN")
+    if "FAIL" not in dev[0].request("WPS_NFC_TOKEN FOO"):
+        raise Exception("Unexpected success on invalid WPS_NFC_TOKEN")
+
+def test_wpas_ctrl_config_parser(dev):
+    """wpa_supplicant ctrl_iface SET config parser"""
+    if "FAIL" not in dev[0].request("SET pbc_in_m1 qwerty"):
+        raise Exception("Non-number accepted as integer")
+    if "FAIL" not in dev[0].request("SET eapol_version 0"):
+        raise Exception("Out-of-range value accepted")
+    if "FAIL" not in dev[0].request("SET eapol_version 10"):
+        raise Exception("Out-of-range value accepted")
+
+    if "FAIL" not in dev[0].request("SET serial_number 0123456789abcdef0123456789abcdef0"):
+        raise Exception("Too long string accepted")
+
+def test_wpas_ctrl_mib(dev):
+    """wpa_supplicant ctrl_iface MIB"""
+    mib = dev[0].get_mib()
+    if "dot11RSNAOptionImplemented" not in mib:
+        raise Exception("Missing MIB entry")
+    if mib["dot11RSNAOptionImplemented"] != "TRUE":
+        raise Exception("Unexpected dot11RSNAOptionImplemented value")
+
+def test_wpas_ctrl_set_wps_params(dev):
+    """wpa_supplicant ctrl_iface SET config_methods"""
+    ts = [ "config_methods label virtual_display virtual_push_button keypad",
+           "device_type 1-0050F204-1",
+           "os_version 01020300",
+           "uuid 12345678-9abc-def0-1234-56789abcdef0" ]
+    for t in ts:
+        if "OK" not in dev[2].request("SET " + t):
+            raise Exception("SET failed for: " + t)
+
+    ts = [ "uuid 12345678+9abc-def0-1234-56789abcdef0",
+           "uuid 12345678-qabc-def0-1234-56789abcdef0",
+           "uuid 12345678-9abc+def0-1234-56789abcdef0",
+           "uuid 12345678-9abc-qef0-1234-56789abcdef0",
+           "uuid 12345678-9abc-def0+1234-56789abcdef0",
+           "uuid 12345678-9abc-def0-q234-56789abcdef0",
+           "uuid 12345678-9abc-def0-1234+56789abcdef0",
+           "uuid 12345678-9abc-def0-1234-q6789abcdef0",
+           "uuid qwerty" ]
+    for t in ts:
+        if "FAIL" not in dev[2].request("SET " + t):
+            raise Exception("SET succeeded for: " + t)
+
+def test_wpas_ctrl_level(dev):
+    """wpa_supplicant ctrl_iface LEVEL"""
+    try:
+        if "FAIL" not in dev[2].request("LEVEL 3"):
+            raise Exception("Unexpected LEVEL success")
+        if "OK" not in dev[2].mon.request("LEVEL 2"):
+            raise Exception("Unexpected LEVEL failure")
+        dev[2].request("SCAN freq=2412")
+        ev = dev[2].wait_event(["State:"], timeout=5)
+        if ev is None:
+            raise Exception("No debug message received")
+        dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=5)
+    finally:
+        dev[2].mon.request("LEVEL 3")
+
+def test_wpas_ctrl_bssid_filter(dev, apdev):
+    """wpa_supplicant bssid_filter"""
+    try:
+        if "OK" not in dev[2].request("SET bssid_filter " + apdev[0]['bssid']):
+            raise Exception("Failed to set bssid_filter")
+        params = { "ssid": "test" }
+        hostapd.add_ap(apdev[0]['ifname'], params)
+        hostapd.add_ap(apdev[1]['ifname'], params)
+        dev[2].scan_for_bss(apdev[0]['bssid'], freq="2412")
+        dev[2].scan(freq="2412")
+        bss = dev[2].get_bss(apdev[0]['bssid'])
+        if bss is None or len(bss) == 0:
+            raise Exception("Missing BSS data")
+        bss = dev[2].get_bss(apdev[1]['bssid'])
+        if bss and len(bss) != 0:
+            raise Exception("Unexpected BSS data")
+        dev[2].request("SET bssid_filter ")
+        dev[2].scan(freq="2412")
+        bss = dev[2].get_bss(apdev[0]['bssid'])
+        if bss is None or len(bss) == 0:
+            raise Exception("Missing BSS data")
+        bss = dev[2].get_bss(apdev[1]['bssid'])
+        if bss is None or len(bss) == 0:
+            raise Exception("Missing BSS data(2)")
+        res = dev[2].request("SCAN_RESULTS").splitlines()
+        if "test" not in res[1] or "test" not in res[2]:
+            raise Exception("SSID missing from SCAN_RESULTS")
+        if apdev[0]['bssid'] not in res[1] and apdev[1]['bssid'] not in res[1]:
+            raise Exception("BSS1 missing from SCAN_RESULTS")
+        if apdev[0]['bssid'] not in res[2] and apdev[1]['bssid'] not in res[2]:
+            raise Exception("BSS1 missing from SCAN_RESULTS")
+
+        if "FAIL" not in dev[2].request("SET bssid_filter 00:11:22:33:44:55 00:11:22:33:44"):
+            raise Exception("Unexpected success for invalid SET bssid_filter")
+    finally:
+        dev[2].request("SET bssid_filter ")
+
+def test_wpas_ctrl_disallow_aps(dev, apdev):
+    """wpa_supplicant ctrl_iface disallow_aps"""
+    params = { "ssid": "test" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    if "FAIL" not in dev[0].request("SET disallow_aps bssid "):
+        raise Exception("Unexpected success on invalid disallow_aps")
+    if "FAIL" not in dev[0].request("SET disallow_aps bssid 00:11:22:33:44"):
+        raise Exception("Unexpected success on invalid disallow_aps")
+    if "FAIL" not in dev[0].request("SET disallow_aps ssid 0"):
+        raise Exception("Unexpected success on invalid disallow_aps")
+    if "FAIL" not in dev[0].request("SET disallow_aps ssid 4q"):
+        raise Exception("Unexpected success on invalid disallow_aps")
+    if "FAIL" not in dev[0].request("SET disallow_aps bssid 00:11:22:33:44:55 ssid 112233 ssid 123"):
+        raise Exception("Unexpected success on invalid disallow_aps")
+    if "FAIL" not in dev[0].request("SET disallow_aps ssid 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f00"):
+        raise Exception("Unexpected success on invalid disallow_aps")
+    if "FAIL" not in dev[0].request("SET disallow_aps foo 112233445566"):
+        raise Exception("Unexpected success on invalid disallow_aps")
+
+    dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+    dev[0].dump_monitor()
+    if "OK" not in dev[0].request("SET disallow_aps bssid 00:11:22:33:44:55 bssid 00:22:33:44:55:66"):
+        raise Exception("Failed to set disallow_aps")
+    if "OK" not in dev[0].request("SET disallow_aps bssid " + apdev[0]['bssid']):
+        raise Exception("Failed to set disallow_aps")
+    ev = dev[0].wait_connected(timeout=30, error="Reassociation timed out")
+    if apdev[1]['bssid'] not in ev:
+        raise Exception("Unexpected BSSID")
+
+    dev[0].dump_monitor()
+    if "OK" not in dev[0].request("SET disallow_aps ssid " + "test".encode("hex")):
+        raise Exception("Failed to set disallow_aps")
+    dev[0].wait_disconnected(timeout=5, error="Disconnection not seen")
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected reassociation")
+
+    dev[0].request("DISCONNECT")
+    dev[0].p2p_start_go(freq=2412)
+    if "OK" not in dev[0].request("SET disallow_aps "):
+        raise Exception("Failed to set disallow_aps")
+
+def test_wpas_ctrl_blob(dev):
+    """wpa_supplicant ctrl_iface SET blob"""
+    if "FAIL" not in dev[0].request("SET blob foo"):
+        raise Exception("Unexpected SET success")
+    if "FAIL" not in dev[0].request("SET blob foo 0"):
+        raise Exception("Unexpected SET success")
+    if "FAIL" not in dev[0].request("SET blob foo 0q"):
+        raise Exception("Unexpected SET success")
+    if "OK" not in dev[0].request("SET blob foo 00"):
+        raise Exception("Unexpected SET failure")
+    if "OK" not in dev[0].request("SET blob foo 0011"):
+        raise Exception("Unexpected SET failure")
+
+def test_wpas_ctrl_set_uapsd(dev):
+    """wpa_supplicant ctrl_iface SET uapsd"""
+    if "FAIL" not in dev[0].request("SET uapsd foo"):
+        raise Exception("Unexpected SET success")
+    if "FAIL" not in dev[0].request("SET uapsd 0,0,0"):
+        raise Exception("Unexpected SET success")
+    if "FAIL" not in dev[0].request("SET uapsd 0,0"):
+        raise Exception("Unexpected SET success")
+    if "FAIL" not in dev[0].request("SET uapsd 0"):
+        raise Exception("Unexpected SET success")
+    if "OK" not in dev[0].request("SET uapsd 1,1,1,1;1"):
+        raise Exception("Unexpected SET failure")
+    if "OK" not in dev[0].request("SET uapsd 0,0,0,0;0"):
+        raise Exception("Unexpected SET failure")
+    if "OK" not in dev[0].request("SET uapsd disable"):
+        raise Exception("Unexpected SET failure")
+
+def test_wpas_ctrl_set(dev):
+    """wpa_supplicant ctrl_iface SET"""
+    vals = [ "foo",
+             "ampdu 0",
+             "radio_disable 0",
+             "ps 10",
+             "ps 1",
+             "dot11RSNAConfigPMKLifetime 0",
+             "dot11RSNAConfigPMKReauthThreshold 101",
+             "dot11RSNAConfigSATimeout 0",
+             "wps_version_number -1",
+             "wps_version_number 256" ]
+    for val in vals:
+        if "FAIL" not in dev[0].request("SET " + val):
+            raise Exception("Unexpected SET success for " + val)
+
+    vals = [ "EAPOL::heldPeriod 60",
+             "EAPOL::authPeriod 30",
+             "EAPOL::startPeriod 30",
+             "EAPOL::maxStart 3",
+             "dot11RSNAConfigSATimeout 60",
+             "ps -1",
+             "ps 0",
+             "no_keep_alive 0",
+             "tdls_disabled 1",
+             "tdls_disabled 0" ]
+    for val in vals:
+        if "OK" not in dev[0].request("SET " + val):
+            raise Exception("Unexpected SET failure for " + val)
+
+def test_wpas_ctrl_get_capability(dev):
+    """wpa_supplicant ctrl_iface GET_CAPABILITY"""
+    if "FAIL" not in dev[0].request("GET_CAPABILITY 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"):
+        raise Exception("Unexpected success on invalid GET_CAPABILITY")
+    if "FAIL" not in dev[0].request("GET_CAPABILITY eap foo"):
+        raise Exception("Unexpected success on invalid GET_CAPABILITY")
+    if "AP" not in dev[0].request("GET_CAPABILITY modes strict"):
+        raise Exception("Unexpected GET_CAPABILITY response")
+    res = dev[0].get_capability("eap")
+    if "TTLS" not in res:
+        raise Exception("Unexpected GET_CAPABILITY eap response: " + str(res))
+
+    res = dev[0].get_capability("pairwise")
+    if "CCMP" not in res:
+        raise Exception("Unexpected GET_CAPABILITY pairwise response: " + str(res))
+
+    res = dev[0].get_capability("group")
+    if "CCMP" not in res:
+        raise Exception("Unexpected GET_CAPABILITY group response: " + str(res))
+
+    res = dev[0].get_capability("key_mgmt")
+    if "WPA-PSK" not in res or "WPA-EAP" not in res:
+        raise Exception("Unexpected GET_CAPABILITY key_mgmt response: " + str(res))
+
+    res = dev[0].get_capability("proto")
+    if "WPA" not in res or "RSN" not in res:
+        raise Exception("Unexpected GET_CAPABILITY proto response: " + str(res))
+
+    res = dev[0].get_capability("auth_alg")
+    if "OPEN" not in res or "SHARED" not in res:
+        raise Exception("Unexpected GET_CAPABILITY auth_alg response: " + str(res))
+
+    res = dev[0].get_capability("modes")
+    if "IBSS" not in res or "AP" not in res:
+        raise Exception("Unexpected GET_CAPABILITY modes response: " + str(res))
+
+    res = dev[0].get_capability("channels")
+    if "8" not in res or "36" not in res:
+        raise Exception("Unexpected GET_CAPABILITY channels response: " + str(res))
+
+    res = dev[0].get_capability("freq")
+    if "2457" not in res or "5180" not in res:
+        raise Exception("Unexpected GET_CAPABILITY freq response: " + str(res))
+
+    res = dev[0].get_capability("tdls")
+    if "EXTERNAL" not in res[0]:
+        raise Exception("Unexpected GET_CAPABILITY tdls response: " + str(res))
+
+    res = dev[0].get_capability("erp")
+    if res is None or "ERP" not in res[0]:
+        raise Exception("Unexpected GET_CAPABILITY erp response: " + str(res))
+
+    if dev[0].get_capability("foo") is not None:
+        raise Exception("Unexpected GET_CAPABILITY foo response: " + str(res))
+
+def test_wpas_ctrl_nfc_report_handover(dev):
+    """wpa_supplicant ctrl_iface NFC_REPORT_HANDOVER"""
+    vals = [ "FOO",
+             "ROLE freq=12345",
+             "ROLE TYPE",
+             "ROLE TYPE REQ",
+             "ROLE TYPE REQ SEL",
+             "ROLE TYPE 0Q SEL",
+             "ROLE TYPE 00 SEL",
+             "ROLE TYPE 00 0Q",
+             "ROLE TYPE 00 00" ]
+    for v in vals:
+        if "FAIL" not in dev[0].request("NFC_REPORT_HANDOVER " + v):
+            raise Exception("Unexpected NFC_REPORT_HANDOVER success for " + v)
+
+def test_wpas_ctrl_nfc_tag_read(dev):
+    """wpa_supplicant ctrl_iface WPS_NFC_TAG_READ"""
+    vals = [ "FOO", "0Q", "00", "000000", "10000001", "10000000", "00000000",
+             "100e0000", "100e0001ff", "100e000411110000", "100e0004100e0001" ]
+    for v in vals:
+        if "FAIL" not in dev[0].request("WPS_NFC_TAG_READ " + v):
+            raise Exception("Unexpected WPS_NFC_TAG_READ success for " + v)
+
+def test_wpas_ctrl_nfc_get_handover(dev):
+    """wpa_supplicant ctrl_iface NFC_GET_HANDOVER"""
+    vals = [ "FOO", "FOO BAR", "WPS WPS", "WPS WPS-CR", "WPS FOO", "NDEF P2P" ]
+    for v in vals:
+        if "FAIL" not in dev[0].request("NFC_GET_HANDOVER_REQ " + v):
+            raise Exception("Unexpected NFC_GET_HANDOVER_REQ success for " + v)
+
+    vals = [ "NDEF WPS", "NDEF P2P-CR", "WPS P2P-CR" ]
+    for v in vals:
+        if "FAIL" in dev[0].request("NFC_GET_HANDOVER_REQ " + v):
+            raise Exception("Unexpected NFC_GET_HANDOVER_REQ failure for " + v)
+
+    vals = [ "FOO", "FOO BAR", "WPS WPS", "WPS WPS-CR", "WPS FOO", "NDEF P2P",
+             "NDEF WPS", "NDEF WPS uuid" ]
+    for v in vals:
+        if "FAIL" not in dev[0].request("NFC_GET_HANDOVER_SEL " + v):
+            raise Exception("Unexpected NFC_GET_HANDOVER_SEL success for " + v)
+
+    vals = [ "NDEF P2P-CR", "WPS P2P-CR", "NDEF P2P-CR-TAG",
+             "WPS P2P-CR-TAG" ]
+    for v in vals:
+        if "FAIL" in dev[0].request("NFC_GET_HANDOVER_SEL " + v):
+            raise Exception("Unexpected NFC_GET_HANDOVER_SEL failure for " + v)
+
+def get_blacklist(dev):
+    return dev.request("BLACKLIST").splitlines()
+
+def test_wpas_ctrl_blacklist(dev):
+    """wpa_supplicant ctrl_iface BLACKLIST"""
+    if "OK" not in dev[0].request("BLACKLIST clear"):
+        raise Exception("BLACKLIST clear failed")
+    b = get_blacklist(dev[0])
+    if len(b) != 0:
+        raise Exception("Unexpected blacklist contents: " + str(b))
+    if "OK" not in dev[0].request("BLACKLIST 00:11:22:33:44:55"):
+        raise Exception("BLACKLIST add failed")
+    b = get_blacklist(dev[0])
+    if "00:11:22:33:44:55" not in b:
+        raise Exception("Unexpected blacklist contents: " + str(b))
+    if "OK" not in dev[0].request("BLACKLIST 00:11:22:33:44:56"):
+        raise Exception("BLACKLIST add failed")
+    b = get_blacklist(dev[0])
+    if "00:11:22:33:44:55" not in b or "00:11:22:33:44:56" not in b:
+        raise Exception("Unexpected blacklist contents: " + str(b))
+    if "OK" not in dev[0].request("BLACKLIST 00:11:22:33:44:56"):
+        raise Exception("BLACKLIST add failed")
+    b = get_blacklist(dev[0])
+    if "00:11:22:33:44:55" not in b or "00:11:22:33:44:56" not in b or len(b) != 2:
+        raise Exception("Unexpected blacklist contents: " + str(b))
+
+    if "OK" not in dev[0].request("BLACKLIST clear"):
+        raise Exception("BLACKLIST clear failed")
+    if dev[0].request("BLACKLIST") != "":
+        raise Exception("Unexpected blacklist contents")
+
+def test_wpas_ctrl_blacklist_oom(dev):
+    """wpa_supplicant ctrl_iface BLACKLIST and out-of-memory"""
+    with alloc_fail(dev[0], 1, "wpa_blacklist_add"):
+        if "FAIL" not in dev[0].request("BLACKLIST aa:bb:cc:dd:ee:ff"):
+            raise Exception("Unexpected success with allocation failure")
+
+def test_wpas_ctrl_log_level(dev):
+    """wpa_supplicant ctrl_iface LOG_LEVEL"""
+    level = dev[2].request("LOG_LEVEL")
+    if "Current level: MSGDUMP" not in level:
+        raise Exception("Unexpected debug level(1): " + level)
+    if "Timestamp: 1" not in level:
+        raise Exception("Unexpected timestamp(1): " + level)
+
+    if "OK" not in dev[2].request("LOG_LEVEL  MSGDUMP  0"):
+        raise Exception("LOG_LEVEL failed")
+    level = dev[2].request("LOG_LEVEL")
+    if "Current level: MSGDUMP" not in level:
+        raise Exception("Unexpected debug level(2): " + level)
+    if "Timestamp: 0" not in level:
+        raise Exception("Unexpected timestamp(2): " + level)
+
+    if "OK" not in dev[2].request("LOG_LEVEL  MSGDUMP  1"):
+        raise Exception("LOG_LEVEL failed")
+    level = dev[2].request("LOG_LEVEL")
+    if "Current level: MSGDUMP" not in level:
+        raise Exception("Unexpected debug level(3): " + level)
+    if "Timestamp: 1" not in level:
+        raise Exception("Unexpected timestamp(3): " + level)
+
+    if "FAIL" not in dev[2].request("LOG_LEVEL FOO"):
+        raise Exception("Invalid LOG_LEVEL accepted")
+
+    for lev in [ "EXCESSIVE", "MSGDUMP", "DEBUG", "INFO", "WARNING", "ERROR" ]:
+        if "OK" not in dev[2].request("LOG_LEVEL " + lev):
+            raise Exception("LOG_LEVEL failed for " + lev)
+        level = dev[2].request("LOG_LEVEL")
+        if "Current level: " + lev not in level:
+            raise Exception("Unexpected debug level: " + level)
+
+    if "OK" not in dev[2].request("LOG_LEVEL  MSGDUMP  1"):
+        raise Exception("LOG_LEVEL failed")
+    level = dev[2].request("LOG_LEVEL")
+    if "Current level: MSGDUMP" not in level:
+        raise Exception("Unexpected debug level(3): " + level)
+    if "Timestamp: 1" not in level:
+        raise Exception("Unexpected timestamp(3): " + level)
+
+def test_wpas_ctrl_enable_disable_network(dev, apdev):
+    """wpa_supplicant ctrl_iface ENABLE/DISABLE_NETWORK"""
+    params = { "ssid": "test" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    id = dev[0].connect("test", key_mgmt="NONE", scan_freq="2412",
+                        only_add_network=True)
+    if "OK" not in dev[0].request("DISABLE_NETWORK " + str(id)):
+        raise Exception("Failed to disable network")
+    if "OK" not in dev[0].request("ENABLE_NETWORK " + str(id) + " no-connect"):
+        raise Exception("Failed to enable network")
+    if "OK" not in dev[0].request("DISABLE_NETWORK all"):
+        raise Exception("Failed to disable networks")
+    if "OK" not in dev[0].request("ENABLE_NETWORK " + str(id)):
+        raise Exception("Failed to enable network")
+    dev[0].wait_connected(timeout=10)
+    if "OK" not in dev[0].request("DISABLE_NETWORK " + str(id)):
+        raise Exception("Failed to disable network")
+    dev[0].wait_disconnected(timeout=10)
+    time.sleep(0.1)
+
+    if "OK" not in dev[0].request("ENABLE_NETWORK all"):
+        raise Exception("Failed to enable network")
+    dev[0].wait_connected(timeout=10)
+    if "OK" not in dev[0].request("DISABLE_NETWORK all"):
+        raise Exception("Failed to disable network")
+    dev[0].wait_disconnected(timeout=10)
+
+def test_wpas_ctrl_country(dev, apdev):
+    """wpa_supplicant SET/GET country code"""
+    try:
+        # work around issues with possible pending regdom event from the end of
+        # the previous test case
+        time.sleep(0.2)
+        dev[0].dump_monitor()
+
+        if "OK" not in dev[0].request("SET country FI"):
+            raise Exception("Failed to set country code")
+        if dev[0].request("GET country") != "FI":
+            raise Exception("Country code set failed")
+        ev = dev[0].wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"], 10)
+        if ev is None:
+            raise Exception("regdom change event not seen")
+        if "init=USER type=COUNTRY alpha2=FI" not in ev:
+            raise Exception("Unexpected event contents: " + ev)
+        dev[0].request("SET country 00")
+        if dev[0].request("GET country") != "00":
+            raise Exception("Country code set failed")
+        ev = dev[0].wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"], 10)
+        if ev is None:
+            raise Exception("regdom change event not seen")
+        if "init=CORE type=WORLD" not in ev:
+            raise Exception("Unexpected event contents: " + ev)
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+
+def test_wpas_ctrl_suspend_resume(dev):
+    """wpa_supplicant SUSPEND/RESUME"""
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    if "OK" not in wpas.global_request("SUSPEND"):
+        raise Exception("SUSPEND failed")
+    time.sleep(1)
+    if "OK" not in wpas.global_request("RESUME"):
+        raise Exception("RESUME failed")
+    if "OK" not in wpas.request("SUSPEND"):
+        raise Exception("Per-interface SUSPEND failed")
+    if "OK" not in wpas.request("RESUME"):
+        raise Exception("Per-interface RESUME failed")
+    ev = wpas.wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+    if ev is None:
+        raise Exception("Scan not completed")
+
+def test_wpas_ctrl_global(dev):
+    """wpa_supplicant global control interface"""
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+
+    if "PONG" not in wpas.global_request("PING"):
+        raise Exception("PING failed")
+    if "wlan5" not in wpas.global_request("INTERFACES"):
+        raise Exception("Interface not found")
+    if "UNKNOWN COMMAND" not in wpas.global_request("FOO"):
+        raise Exception("Unexpected response to unknown command")
+    if "PONG" not in wpas.global_request("IFNAME=wlan5 PING"):
+        raise Exception("Per-interface PING failed")
+    if "FAIL-NO-IFNAME-MATCH" not in wpas.global_request("IFNAME=notfound PING"):
+        raise Exception("Unknown interface not reported correctly")
+    if "FAIL" not in wpas.global_request("SAVE_CONFIG"):
+        raise Exception("SAVE_CONFIG succeeded unexpectedly")
+    if "OK" not in wpas.global_request("SET wifi_display 0"):
+        raise Exception("SET failed")
+    if "wifi_display=0" not in wpas.global_request("STATUS"):
+        raise Exception("wifi_display not disabled")
+    if "OK" not in wpas.global_request("SET wifi_display 1"):
+        raise Exception("SET failed")
+    if "wifi_display=1" not in wpas.global_request("STATUS"):
+        raise Exception("wifi_display not enabled")
+    if "FAIL" not in wpas.global_request("SET foo 1"):
+        raise Exception("SET succeeded unexpectedly")
+
+    if "p2p_state=IDLE" not in wpas.global_request("STATUS"):
+        raise Exception("P2P was disabled")
+    wpas.global_request("P2P_SET disabled 1")
+    if "p2p_state=DISABLED" not in wpas.global_request("STATUS"):
+        raise Exception("P2P was not disabled")
+    wpas.global_request("P2P_SET disabled 0")
+    if "p2p_state=IDLE" not in wpas.global_request("STATUS"):
+        raise Exception("P2P was not enabled")
+
+    # driver_nl80211.c does not support interface list, so do not fail because
+    # of that
+    logger.debug(wpas.global_request("INTERFACE_LIST"))
+
+    if "FAIL" not in wpas.global_request("INTERFACE_ADD "):
+        raise Exception("INTERFACE_ADD succeeded unexpectedly")
+    if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO"):
+        raise Exception("INTERFACE_ADD succeeded unexpectedly")
+    if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO	conf"):
+        raise Exception("INTERFACE_ADD succeeded unexpectedly")
+    if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO	conf	driver"):
+        raise Exception("INTERFACE_ADD succeeded unexpectedly")
+    if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO	conf	driver	ctrliface"):
+        raise Exception("INTERFACE_ADD succeeded unexpectedly")
+    if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO	conf	driver	ctrliface	driverparam"):
+        raise Exception("INTERFACE_ADD succeeded unexpectedly")
+    if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO	conf	driver	ctrliface	driverparam	bridge"):
+        raise Exception("INTERFACE_ADD succeeded unexpectedly")
+    if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO	conf	driver	ctrliface	driverparam	bridge	foo"):
+        raise Exception("INTERFACE_ADD succeeded unexpectedly")
+    if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO					"):
+        raise Exception("INTERFACE_ADD succeeded unexpectedly")
+
+def test_wpas_ctrl_roam(dev, apdev):
+    """wpa_supplicant ctrl_iface ROAM error cases"""
+    if "FAIL" not in dev[0].request("ROAM 00:11:22:33:44"):
+        raise Exception("Unexpected success")
+    if "FAIL" not in dev[0].request("ROAM 00:11:22:33:44:55"):
+        raise Exception("Unexpected success")
+    params = { "ssid": "test" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    id = dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+    if "FAIL" not in dev[0].request("ROAM 00:11:22:33:44:55"):
+        raise Exception("Unexpected success")
+
+def test_wpas_ctrl_ipaddr(dev, apdev):
+    """wpa_supplicant IP address in STATUS"""
+    try:
+        subprocess.call(['ip', 'addr', 'add', '10.174.65.207/32', 'dev',
+                         dev[0].ifname])
+        ipaddr = dev[0].get_status_field('ip_address')
+        if ipaddr != '10.174.65.207':
+            raise Exception("IP address not in STATUS output")
+    finally:
+        subprocess.call(['ip', 'addr', 'del', '10.174.65.207/32', 'dev',
+                         dev[0].ifname])
+
+def test_wpas_ctrl_neighbor_rep_req(dev, apdev):
+    """wpa_supplicant ctrl_iface NEIGHBOR_REP_REQUEST"""
+    params = { "ssid": "test" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    params = { "ssid": "test2", "radio_measurements": "1" }
+    hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+    if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+        raise Exception("Request succeeded unexpectedly")
+    if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=abcdef"):
+        raise Exception("Request succeeded unexpectedly")
+    dev[0].request("DISCONNECT")
+
+    rrm = int(dev[0].get_driver_status_field("capa.rrm_flags"), 16)
+    if rrm & 0x5 != 0x5:
+        logger.info("Driver does not support required RRM capabilities - skip rest of the test case")
+        return
+
+    dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
+
+    # These requests are expected to get sent properly, but since hostapd does
+    # not yet support processing of the request, these are expected to fail.
+    
+    if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+        raise Exception("Request failed")
+    ev = dev[0].wait_event([ "RRM-NEIGHBOR-REP-RECEIVED",
+                             "RRM-NEIGHBOR-REP-REQUEST-FAILED" ], timeout=10)
+    if ev is None:
+        raise Exception("RRM report result not indicated")
+    logger.info("RRM result: " + ev)
+
+    if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=abcdef"):
+        raise Exception("Request failed")
+    ev = dev[0].wait_event([ "RRM-NEIGHBOR-REP-RECEIVED",
+                             "RRM-NEIGHBOR-REP-REQUEST-FAILED" ], timeout=10)
+    if ev is None:
+        raise Exception("RRM report result not indicated")
+    logger.info("RRM result: " + ev)
+
+def test_wpas_ctrl_rsp(dev, apdev):
+    """wpa_supplicant ctrl_iface CTRL-RSP-"""
+    if "FAIL" not in dev[0].request("CTRL-RSP-"):
+        raise Exception("Request succeeded unexpectedly")
+    if "FAIL" not in dev[0].request("CTRL-RSP-foo-"):
+        raise Exception("Request succeeded unexpectedly")
+    if "FAIL" not in dev[0].request("CTRL-RSP-foo-1234567"):
+        raise Exception("Request succeeded unexpectedly")
+    if "FAIL" not in dev[0].request("CTRL-RSP-foo-1234567:"):
+        raise Exception("Request succeeded unexpectedly")
+    id = dev[0].add_network()
+    if "FAIL" not in dev[0].request("CTRL-RSP-foo-%d:" % id):
+        raise Exception("Request succeeded unexpectedly")
+    for req in [ "IDENTITY", "PASSWORD", "NEW_PASSWORD", "PIN", "OTP",
+                 "PASSPHRASE", "SIM" ]:
+        if "OK" not in dev[0].request("CTRL-RSP-%s-%d:" % (req, id)):
+            raise Exception("Request failed unexpectedly")
+        if "OK" not in dev[0].request("CTRL-RSP-%s-%d:" % (req, id)):
+            raise Exception("Request failed unexpectedly")
+
+def test_wpas_ctrl_vendor(dev, apdev):
+    """wpa_supplicant ctrl_iface VENDOR"""
+    cmds = [ "foo",
+             "1",
+             "1 foo",
+             "1 2foo",
+             "1 2 qq" ]
+    for cmd in cmds:
+        if "FAIL" not in dev[0].request("VENDOR " + cmd):
+            raise Exception("Invalid VENDOR command accepted: " + cmd)
+
+def test_wpas_ctrl_mgmt_tx(dev, apdev):
+    """wpa_supplicant ctrl_iface MGMT_TX"""
+    cmds = [ "foo",
+             "00:11:22:33:44:55 foo",
+             "00:11:22:33:44:55 11:22:33:44:55:66",
+             "00:11:22:33:44:55 11:22:33:44:55:66 freq=0 no_cck=0 wait_time=0 action=123",
+             "00:11:22:33:44:55 11:22:33:44:55:66 action=12qq" ]
+    for cmd in cmds:
+        if "FAIL" not in dev[0].request("MGMT_TX " + cmd):
+            raise Exception("Invalid MGMT_TX command accepted: " + cmd)
+
+    if "OK" not in dev[0].request("MGMT_TX_DONE"):
+        raise Exception("MGMT_TX_DONE failed")
+
+def test_wpas_ctrl_driver_event(dev, apdev):
+    """wpa_supplicant ctrl_iface DRIVER_EVENT"""
+    if "FAIL" not in dev[0].request("DRIVER_EVENT foo"):
+        raise Exception("Invalid DRIVER_EVENT accepted")
+
+def test_wpas_ctrl_eapol_rx(dev, apdev):
+    """wpa_supplicant ctrl_iface EAPOL_RX"""
+    cmds = [ "foo",
+             "00:11:22:33:44:55 123",
+             "00:11:22:33:44:55 12qq" ]
+    for cmd in cmds:
+        if "FAIL" not in dev[0].request("EAPOL_RX " + cmd):
+            raise Exception("Invalid EAPOL_RX command accepted: " + cmd)
+
+def test_wpas_ctrl_data_test(dev, apdev):
+    """wpa_supplicant ctrl_iface DATA_TEST"""
+    dev[0].request("DATA_TEST_CONFIG 0")
+    if "FAIL" not in dev[0].request("DATA_TEST_TX 00:11:22:33:44:55 00:11:22:33:44:55 0"):
+        raise Exception("DATA_TEST_TX accepted when not in test mode")
+
+    try:
+        if "OK" not in dev[0].request("DATA_TEST_CONFIG 1"):
+            raise Exception("DATA_TEST_CONFIG failed")
+        if "OK" not in dev[0].request("DATA_TEST_CONFIG 1"):
+            raise Exception("DATA_TEST_CONFIG failed")
+        cmds = [ "foo",
+                 "00:11:22:33:44:55 foo",
+                 "00:11:22:33:44:55 00:11:22:33:44:55 -1",
+                 "00:11:22:33:44:55 00:11:22:33:44:55 256" ]
+        for cmd in cmds:
+            if "FAIL" not in dev[0].request("DATA_TEST_TX " + cmd):
+                raise Exception("Invalid DATA_TEST_TX command accepted: " + cmd)
+        if "OK" not in dev[0].request("DATA_TEST_TX 00:11:22:33:44:55 00:11:22:33:44:55 0"):
+            raise Exception("DATA_TEST_TX failed")
+    finally:
+        dev[0].request("DATA_TEST_CONFIG 0")
+
+    cmds = [ "",
+             "00",
+             "00112233445566778899aabbccdde",
+             "00112233445566778899aabbccdq" ]
+    for cmd in cmds:
+        if "FAIL" not in dev[0].request("DATA_TEST_FRAME " + cmd):
+            raise Exception("Invalid DATA_TEST_FRAME command accepted: " + cmd)
+
+    if "OK" not in dev[0].request("DATA_TEST_FRAME 00112233445566778899aabbccddee"):
+        raise Exception("DATA_TEST_FRAME failed")
+
+def test_wpas_ctrl_vendor_elem(dev, apdev):
+    """wpa_supplicant ctrl_iface VENDOR_ELEM"""
+    if "OK" not in dev[0].request("VENDOR_ELEM_ADD 1 "):
+        raise Exception("VENDOR_ELEM_ADD failed")
+    cmds = [ "-1 ",
+             "255 ",
+             "1",
+             "1 123",
+             "1 12qq34" ]
+    for cmd in cmds:
+        if "FAIL" not in dev[0].request("VENDOR_ELEM_ADD " + cmd):
+            raise Exception("Invalid VENDOR_ELEM_ADD command accepted: " + cmd)
+
+    cmds = [ "-1 ",
+             "255 " ]
+    for cmd in cmds:
+        if "FAIL" not in dev[0].request("VENDOR_ELEM_GET " + cmd):
+            raise Exception("Invalid VENDOR_ELEM_GET command accepted: " + cmd)
+
+    dev[0].request("VENDOR_ELEM_REMOVE 1 *")
+    cmds = [ "-1 ",
+             "255 ",
+             "1",
+             "1",
+             "1 123",
+             "1 12qq34",
+             "1 12",
+             "1 0000" ]
+    for cmd in cmds:
+        if "FAIL" not in dev[0].request("VENDOR_ELEM_REMOVE " + cmd):
+            raise Exception("Invalid VENDOR_ELEM_REMOVE command accepted: " + cmd)
+
+    dev[0].request("VENDOR_ELEM_ADD 1 000100")
+    if "OK" not in dev[0].request("VENDOR_ELEM_REMOVE 1 "):
+        raise Exception("VENDOR_ELEM_REMOVE failed")
+    cmds = [ "-1 ",
+             "255 ",
+             "1",
+             "1 123",
+             "1 12qq34",
+             "1 12",
+             "1 0000" ]
+    for cmd in cmds:
+        if "FAIL" not in dev[0].request("VENDOR_ELEM_REMOVE " + cmd):
+            raise Exception("Invalid VENDOR_ELEM_REMOVE command accepted: " + cmd)
+    if "OK" not in dev[0].request("VENDOR_ELEM_REMOVE 1 000100"):
+        raise Exception("VENDOR_ELEM_REMOVE failed")
+
+def test_wpas_ctrl_misc(dev, apdev):
+    """wpa_supplicant ctrl_iface and miscellaneous commands"""
+    if "OK" not in dev[0].request("RELOG"):
+        raise Exception("RELOG failed")
+    if dev[0].request("IFNAME") != dev[0].ifname:
+        raise Exception("IFNAME returned unexpected response")
+    if "FAIL" not in dev[0].request("REATTACH"):
+        raise Exception("REATTACH accepted while disabled")
+    if "OK" not in dev[2].request("RECONFIGURE"):
+        raise Exception("RECONFIGURE failed")
+    if "FAIL" in dev[0].request("INTERFACE_LIST"):
+        raise Exception("INTERFACE_LIST failed")
+    if "UNKNOWN COMMAND" not in dev[0].request("FOO"):
+        raise Exception("Unknown command accepted")
+
+    if "FAIL" not in dev[0].global_request("INTERFACE_REMOVE foo"):
+        raise Exception("Invalid INTERFACE_REMOVE accepted")
+    if "FAIL" not in dev[0].global_request("SET foo"):
+        raise Exception("Invalid global SET accepted")
+
+def test_wpas_ctrl_dump(dev, apdev):
+    """wpa_supplicant ctrl_iface and DUMP/GET global parameters"""
+    vals = dev[0].get_config()
+    logger.info("Config values from DUMP: " + str(vals))
+    for field in vals:
+        res = dev[0].request("GET " + field)
+        if res == 'FAIL\n':
+            res = "null"
+        if res != vals[field]:
+            print "'{}' != '{}'".format(res, vals[field])
+            raise Exception("Mismatch in config field " + field)
+    if "beacon_int" not in vals:
+        raise Exception("Missing config field")
+
+def test_wpas_ctrl_interface_add(dev, apdev):
+    """wpa_supplicant INTERFACE_ADD/REMOVE with vif creation/removal"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+    ifname = "test-" + dev[0].ifname
+    dev[0].interface_add(ifname, create=True)
+    wpas = WpaSupplicant(ifname=ifname)
+    wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity(wpas, hapd)
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    dev[0].global_request("INTERFACE_REMOVE " + ifname)
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_wpas_ctrl_interface_add_many(dev, apdev):
+    """wpa_supplicant INTERFACE_ADD/REMOVE with vif creation/removal (many)"""
+    try:
+        _test_wpas_ctrl_interface_add_many(dev, apdev)
+    finally:
+        for i in range(10):
+            ifname = "test%d-" % i + dev[0].ifname
+            dev[0].global_request("INTERFACE_REMOVE " + ifname)
+
+def _test_wpas_ctrl_interface_add_many(dev, apdev):
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+    l = []
+    for i in range(10):
+        ifname = "test%d-" % i + dev[0].ifname
+        dev[0].interface_add(ifname, create=True)
+        wpas = WpaSupplicant(ifname=ifname)
+        wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
+        l.append(wpas)
+    for wpas in l:
+        hwsim_utils.test_connectivity(wpas, hapd)
+
+def test_wpas_ctrl_interface_add2(dev, apdev):
+    """wpa_supplicant INTERFACE_ADD/REMOVE with vif without creation/removal"""
+    ifname = "test-ext-" + dev[0].ifname
+    try:
+        _test_wpas_ctrl_interface_add2(dev, apdev, ifname)
+    finally:
+        subprocess.call(['iw', 'dev', ifname, 'del'])
+
+def _test_wpas_ctrl_interface_add2(dev, apdev, ifname):
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "open" })
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+    subprocess.call(['iw', 'dev', dev[0].ifname, 'interface', 'add', ifname,
+                     'type', 'station'])
+    dev[0].interface_add(ifname, set_ifname=False, all_params=True)
+    wpas = WpaSupplicant(ifname=ifname)
+    wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
+    hwsim_utils.test_connectivity(wpas, hapd)
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    del wpas
+    dev[0].global_request("INTERFACE_REMOVE " + ifname)
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_wpas_ctrl_wait(dev, apdev, test_params):
+    """wpa_supplicant control interface wait for client"""
+    logfile = os.path.join(test_params['logdir'], 'wpas_ctrl_wait.log-wpas')
+    pidfile = os.path.join(test_params['logdir'], 'wpas_ctrl_wait.pid-wpas')
+    conffile = os.path.join(test_params['logdir'], 'wpas_ctrl_wait.conf')
+    with open(conffile, 'w') as f:
+        f.write("ctrl_interface=DIR=/var/run/wpa_supplicant\n")
+
+    prg = os.path.join(test_params['logdir'],
+                       'alt-wpa_supplicant/wpa_supplicant/wpa_supplicant')
+    if not os.path.exists(prg):
+        prg = '../../wpa_supplicant/wpa_supplicant'
+    arg = [ prg ]
+    cmd = subprocess.Popen(arg, stdout=subprocess.PIPE)
+    out = cmd.communicate()[0]
+    cmd.wait()
+    tracing = "Linux tracing" in out
+
+    with HWSimRadio() as (radio, iface):
+        arg = [ prg, '-BdddW', '-P', pidfile, '-f', logfile,
+                '-Dnl80211', '-c', conffile, '-i', iface ]
+        if tracing:
+            arg += [ '-T' ]
+        logger.info("Start wpa_supplicant: " + str(arg))
+        subprocess.call(arg)
+        wpas = WpaSupplicant(ifname=iface)
+        if "PONG" not in wpas.request("PING"):
+            raise Exception("Could not PING wpa_supplicant")
+        if not os.path.exists(pidfile):
+            raise Exception("PID file not created")
+        if "OK" not in wpas.request("TERMINATE"):
+            raise Exception("Could not TERMINATE")
+        ev = wpas.wait_event([ "CTRL-EVENT-TERMINATING" ], timeout=2)
+        if ev is None:
+            raise Exception("No termination event received")
+        for i in range(20):
+            if not os.path.exists(pidfile):
+                break
+            time.sleep(0.1)
+        if os.path.exists(pidfile):
+            raise Exception("PID file not removed")
+
+def test_wpas_ctrl_oom(dev):
+    """Various wpa_supplicant ctrl_iface OOM cases"""
+    try:
+        _test_wpas_ctrl_oom(dev)
+    finally:
+        dev[0].request("VENDOR_ELEM_REMOVE 1 *")
+        dev[0].request("VENDOR_ELEM_REMOVE 2 *")
+        dev[0].request("SET bssid_filter ")
+
+def _test_wpas_ctrl_oom(dev):
+    dev[0].request('VENDOR_ELEM_ADD 2 000100')
+    tests = [ ('DRIVER_EVENT AVOID_FREQUENCIES 2412', 'FAIL',
+               1, 'freq_range_list_parse'),
+              ('P2P_SET disallow_freq 2412', 'FAIL',
+               1, 'freq_range_list_parse'),
+              ('SCAN freq=2412', 'FAIL',
+               1, 'freq_range_list_parse'),
+              ('INTERWORKING_SELECT freq=2412', 'FAIL',
+               1, 'freq_range_list_parse'),
+              ('SCAN ssid 112233', 'FAIL',
+               1, 'wpas_ctrl_scan'),
+              ('MGMT_TX 00:00:00:00:00:00 00:00:00:00:00:00 action=00', 'FAIL',
+               1, 'wpas_ctrl_iface_mgmt_tx'),
+              ('EAPOL_RX 00:00:00:00:00:00 00', 'FAIL',
+               1, 'wpas_ctrl_iface_eapol_rx'),
+              ('DATA_TEST_FRAME 00112233445566778899aabbccddee', 'FAIL',
+               1, 'wpas_ctrl_iface_data_test_frame'),
+              ('DATA_TEST_FRAME 00112233445566778899aabbccddee', 'FAIL',
+               1, 'l2_packet_init;wpas_ctrl_iface_data_test_frame'),
+              ('VENDOR_ELEM_ADD 1 000100', 'FAIL',
+               1, 'wpas_ctrl_vendor_elem_add'),
+              ('VENDOR_ELEM_ADD 2 000100', 'FAIL',
+               2, 'wpas_ctrl_vendor_elem_add'),
+              ('VENDOR_ELEM_REMOVE 2 000100', 'FAIL',
+               1, 'wpas_ctrl_vendor_elem_remove'),
+              ('SET bssid_filter 00:11:22:33:44:55', 'FAIL',
+               1, 'set_bssid_filter'),
+              ('SET disallow_aps bssid 00:11:22:33:44:55', 'FAIL',
+               1, 'set_disallow_aps'),
+              ('SET disallow_aps ssid 11', 'FAIL',
+               1, 'set_disallow_aps'),
+              ('SET blob foo 0011', 'FAIL',
+               1, 'wpas_ctrl_set_blob'),
+              ('SET blob foo 0011', 'FAIL',
+               2, 'wpas_ctrl_set_blob'),
+              ('SET blob foo 0011', 'FAIL',
+               3, 'wpas_ctrl_set_blob'),
+              ('WPS_NFC_TAG_READ 00', 'FAIL',
+               1, 'wpa_supplicant_ctrl_iface_wps_nfc_tag_read'),
+              ('WPS_NFC_TOKEN NDEF', 'FAIL',
+               1, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
+              ('WPS_NFC_TOKEN NDEF', 'FAIL',
+               2, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
+              ('WPS_NFC_TOKEN NDEF', 'FAIL',
+               3, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
+              ('WPS_NFC_TOKEN NDEF', 'FAIL',
+               4, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
+              ('WPS_NFC_TOKEN NDEF', 'FAIL',
+               5, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
+              ('NFC_REPORT_HANDOVER ROLE TYPE 00 00', 'FAIL',
+               1, 'wpas_ctrl_nfc_report_handover'),
+              ('NFC_REPORT_HANDOVER ROLE TYPE 00 00', 'FAIL',
+               2, 'wpas_ctrl_nfc_report_handover'),
+              ('NFC_GET_HANDOVER_REQ NDEF WPS-CR', 'FAIL',
+               1, 'wps_build_nfc_handover_req'),
+              ('NFC_GET_HANDOVER_REQ NDEF WPS-CR', 'FAIL',
+               1, 'ndef_build_record'),
+              ('NFC_GET_HANDOVER_REQ NDEF P2P-CR', None,
+               1, 'wpas_p2p_nfc_handover'),
+              ('NFC_GET_HANDOVER_REQ NDEF P2P-CR', 'FAIL',
+               2, 'wpas_p2p_nfc_handover'),
+              ('NFC_GET_HANDOVER_REQ NDEF P2P-CR', None,
+               1, 'wps_build_nfc_handover_req_p2p'),
+              ('NFC_GET_HANDOVER_REQ NDEF P2P-CR', 'FAIL',
+               1, 'ndef_build_record'),
+              ('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', None,
+               1, 'wpas_ctrl_nfc_get_handover_sel_p2p'),
+              ('NFC_GET_HANDOVER_SEL NDEF P2P-CR', None,
+               1, 'wpas_ctrl_nfc_get_handover_sel_p2p'),
+              ('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', 'FAIL',
+               2, 'wpas_ctrl_nfc_get_handover_sel_p2p'),
+              ('NFC_GET_HANDOVER_SEL NDEF P2P-CR', 'FAIL',
+               2, 'wpas_ctrl_nfc_get_handover_sel_p2p'),
+              ('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', 'FAIL',
+               3, 'wpas_ctrl_nfc_get_handover_sel_p2p'),
+              ('NFC_GET_HANDOVER_SEL NDEF P2P-CR', 'FAIL',
+               3, 'wpas_ctrl_nfc_get_handover_sel_p2p'),
+              ('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', 'FAIL',
+               4, 'wpas_ctrl_nfc_get_handover_sel_p2p'),
+              ('NFC_GET_HANDOVER_SEL NDEF P2P-CR', 'FAIL',
+               4, 'wpas_ctrl_nfc_get_handover_sel_p2p'),
+              ('P2P_ASP_PROVISION_RESP 00:11:22:33:44:55 id=1', 'FAIL',
+               1, 'p2p_parse_asp_provision_cmd'),
+              ('P2P_SERV_DISC_REQ 00:11:22:33:44:55 02000001', 'FAIL',
+               1, 'p2p_ctrl_serv_disc_req'),
+              ('P2P_SERV_DISC_RESP 2412 00:11:22:33:44:55 1 00', 'FAIL',
+               1, 'p2p_ctrl_serv_disc_resp'),
+              ('P2P_SERVICE_ADD bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027',
+               'FAIL',
+               1, 'p2p_ctrl_service_add_bonjour'),
+              ('P2P_SERVICE_ADD bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027',
+               'FAIL',
+               2, 'p2p_ctrl_service_add_bonjour'),
+              ('P2P_SERVICE_ADD bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027',
+               'FAIL',
+               3, 'p2p_ctrl_service_add_bonjour'),
+              ('P2P_SERVICE_DEL bonjour 0b5f6166706f766572746370c00c000c01',
+               'FAIL',
+               1, 'p2p_ctrl_service_del_bonjour'),
+              ('GAS_REQUEST 00:11:22:33:44:55 00', 'FAIL',
+               1, 'gas_request'),
+              ('GAS_REQUEST 00:11:22:33:44:55 00 11', 'FAIL',
+               2, 'gas_request'),
+              ('HS20_GET_NAI_HOME_REALM_LIST 00:11:22:33:44:55 realm=example.com',
+              'FAIL',
+              1, 'hs20_nai_home_realm_list'),
+              ('HS20_GET_NAI_HOME_REALM_LIST 00:11:22:33:44:55 00',
+              'FAIL',
+              1, 'hs20_get_nai_home_realm_list'),
+              ('WNM_SLEEP enter tfs_req=11', 'FAIL',
+               1, 'wpas_ctrl_iface_wnm_sleep'),
+              ('WNM_SLEEP enter tfs_req=11', 'FAIL',
+               2, 'wpas_ctrl_iface_wnm_sleep'),
+              ('WNM_SLEEP enter tfs_req=11', 'FAIL',
+               3, 'wpas_ctrl_iface_wnm_sleep'),
+              ('WNM_SLEEP enter tfs_req=11', 'FAIL',
+               4, 'wpas_ctrl_iface_wnm_sleep'),
+              ('WNM_SLEEP enter tfs_req=11', 'FAIL',
+               5, 'wpas_ctrl_iface_wnm_sleep'),
+              ('WNM_SLEEP enter', 'FAIL',
+               3, 'wpas_ctrl_iface_wnm_sleep'),
+              ('VENDOR 1 1 00', 'FAIL',
+               1, 'wpa_supplicant_vendor_cmd'),
+              ('VENDOR 1 1 00', 'FAIL',
+               2, 'wpa_supplicant_vendor_cmd'),
+              ('RADIO_WORK add test', 'FAIL',
+               1, 'wpas_ctrl_radio_work_add'),
+              ('RADIO_WORK add test', 'FAIL',
+               2, 'wpas_ctrl_radio_work_add'),
+              ('AUTOSCAN periodic:1', 'FAIL',
+               1, 'wpa_supplicant_ctrl_iface_autoscan'),
+              ('PING', None,
+               1, 'wpa_supplicant_ctrl_iface_process') ]
+    for cmd,exp,count,func in tests:
+        with alloc_fail(dev[0], count, func):
+            res = dev[0].request(cmd)
+            if exp and exp not in res:
+                raise Exception("Unexpected success for '%s' during OOM" % cmd)
+
+    tests = [ ('FOO', None,
+               1, 'wpa_supplicant_global_ctrl_iface_process'),
+              ('IFNAME=notfound PING', 'FAIL\n',
+               1, 'wpas_global_ctrl_iface_ifname') ]
+    for cmd,exp,count,func in tests:
+        with alloc_fail(dev[0], count, func):
+            res = dev[0].global_request(cmd)
+            if exp and exp not in res:
+                raise Exception("Unexpected success for '%s' during OOM" % cmd)
+
+def test_wpas_ctrl_socket_full(dev, apdev, test_params):
+    """wpa_supplicant control socket and full send buffer"""
+    if not dev[0].ping():
+        raise Exception("Could not ping wpa_supplicant at the beginning of the test")
+    dev[0].get_status()
+
+    counter = 0
+
+    s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+    local = "/tmp/wpa_ctrl_test_%d-%d" % (os.getpid(), counter)
+    counter += 1
+    s.bind(local)
+    s.connect("/var/run/wpa_supplicant/wlan0")
+    for i in range(20):
+        logger.debug("Command %d" % i)
+        try:
+            s.send("MIB")
+        except Exception, e:
+            logger.info("Could not send command %d: %s" % (i, str(e)))
+            break
+        # Close without receiving response
+        time.sleep(0.01)
+
+    if not dev[0].ping():
+        raise Exception("Could not ping wpa_supplicant in the middle of the test")
+    dev[0].get_status()
+
+    s2 = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+    local2 = "/tmp/wpa_ctrl_test_%d-%d" % (os.getpid(), counter)
+    counter += 1
+    s2.bind(local2)
+    s2.connect("/var/run/wpa_supplicant/wlan0")
+    for i in range(10):
+        logger.debug("Command %d [2]" % i)
+        try:
+            s2.send("MIB")
+        except Exception, e:
+            logger.info("Could not send command %d [2]: %s" % (i, str(e)))
+            break
+        # Close without receiving response
+        time.sleep(0.01)
+
+    s.close()
+    os.unlink(local)
+
+    for i in range(10):
+        logger.debug("Command %d [3]" % i)
+        try:
+            s2.send("MIB")
+        except Exception, e:
+            logger.info("Could not send command %d [3]: %s" % (i, str(e)))
+            break
+        # Close without receiving response
+        time.sleep(0.01)
+
+    s2.close()
+    os.unlink(local2)
+
+    if not dev[0].ping():
+        raise Exception("Could not ping wpa_supplicant in the middle of the test [2]")
+    dev[0].get_status()
+
+    s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+    local = "/tmp/wpa_ctrl_test_%d-%d" % (os.getpid(), counter)
+    counter += 1
+    s.bind(local)
+    s.connect("/var/run/wpa_supplicant/wlan0")
+    s.send("ATTACH")
+    res = s.recv(100)
+    if "OK" not in res:
+        raise Exception("Could not attach a test socket")
+
+    for i in range(5):
+        dev[0].scan(freq=2412)
+
+    s.close()
+    os.unlink(local)
+
+    for i in range(5):
+        dev[0].scan(freq=2412)
+
+    if not dev[0].ping():
+        raise Exception("Could not ping wpa_supplicant at the end of the test")
+    dev[0].get_status()
diff --git a/hostap/tests/hwsim/test_wpas_mesh.py b/hostap/tests/hwsim/test_wpas_mesh.py
new file mode 100644
index 0000000..b6188d1
--- /dev/null
+++ b/hostap/tests/hwsim/test_wpas_mesh.py
@@ -0,0 +1,612 @@
+#!/usr/bin/python
+#
+# wpa_supplicant mesh mode tests
+# Copyright (c) 2014, cozybit Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import subprocess
+
+import hwsim_utils
+from wpasupplicant import WpaSupplicant
+from utils import HwsimSkip, alloc_fail
+
+def check_mesh_support(dev, secure=False):
+    if "MESH" not in dev.get_capability("modes"):
+        raise HwsimSkip("Driver does not support mesh")
+    if secure and "SAE" not in dev.get_capability("auth_alg"):
+        raise HwsimSkip("SAE not supported")
+
+def check_mesh_scan(dev, params, other_started=False, beacon_int=0):
+    if not other_started:
+        dev.dump_monitor()
+    id = dev.request("SCAN " + params)
+    if "FAIL" in id:
+        raise Exception("Failed to start scan")
+    id = int(id)
+
+    if other_started:
+        ev = dev.wait_event(["CTRL-EVENT-SCAN-STARTED"])
+        if ev is None:
+            raise Exception("Other scan did not start")
+        if "id=" + str(id) in ev:
+            raise Exception("Own scan id unexpectedly included in start event")
+
+        ev = dev.wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+        if ev is None:
+            raise Exception("Other scan did not complete")
+        if "id=" + str(id) in ev:
+            raise Exception(
+                "Own scan id unexpectedly included in completed event")
+
+    ev = dev.wait_event(["CTRL-EVENT-SCAN-STARTED"])
+    if ev is None:
+        raise Exception("Scan did not start")
+    if "id=" + str(id) not in ev:
+        raise Exception("Scan id not included in start event")
+
+    ev = dev.wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+    if ev is None:
+        raise Exception("Scan did not complete")
+    if "id=" + str(id) not in ev:
+        raise Exception("Scan id not included in completed event")
+
+    res = dev.request("SCAN_RESULTS")
+
+    if res.find("[MESH]") < 0:
+        raise Exception("Scan did not contain a MESH network")
+
+    bssid = res.splitlines()[1].split(' ')[0]
+    bss = dev.get_bss(bssid)
+    if bss is None:
+        raise Exception("Could not get BSS entry for mesh")
+    if 'mesh_capability' not in bss:
+        raise Exception("mesh_capability missing from BSS entry")
+    if beacon_int:
+        if 'beacon_int' not in bss:
+            raise Exception("beacon_int missing from BSS entry")
+        if str(beacon_int) != bss['beacon_int']:
+            raise Exception("Unexpected beacon_int in BSS entry: " + bss['beacon_int'])
+
+def check_mesh_group_added(dev):
+    ev = dev.wait_event(["MESH-GROUP-STARTED"])
+    if ev is None:
+        raise Exception("Test exception: Couldn't join mesh")
+
+
+def check_mesh_group_removed(dev):
+    ev = dev.wait_event(["MESH-GROUP-REMOVED"])
+    if ev is None:
+        raise Exception("Test exception: Couldn't leave mesh")
+
+
+def check_mesh_peer_connected(dev, timeout=10):
+    ev = dev.wait_event(["MESH-PEER-CONNECTED"], timeout=timeout)
+    if ev is None:
+        raise Exception("Test exception: Remote peer did not connect.")
+
+
+def check_mesh_peer_disconnected(dev):
+    ev = dev.wait_event(["MESH-PEER-DISCONNECTED"])
+    if ev is None:
+        raise Exception("Test exception: Peer disconnect event not detected.")
+
+
+def test_wpas_add_set_remove_support(dev):
+    """wpa_supplicant MESH add/set/remove network support"""
+    check_mesh_support(dev[0])
+    id = dev[0].add_network()
+    dev[0].set_network(id, "mode", "5")
+    dev[0].remove_network(id)
+
+def add_open_mesh_network(dev, freq="2412", start=True, beacon_int=0):
+    id = dev.add_network()
+    dev.set_network(id, "mode", "5")
+    dev.set_network_quoted(id, "ssid", "wpas-mesh-open")
+    dev.set_network(id, "key_mgmt", "NONE")
+    dev.set_network(id, "frequency", freq)
+    if beacon_int:
+        dev.set_network(id, "beacon_int", str(beacon_int))
+    if start:
+        dev.mesh_group_add(id)
+    return id
+
+def test_wpas_mesh_group_added(dev):
+    """wpa_supplicant MESH group add"""
+    check_mesh_support(dev[0])
+    add_open_mesh_network(dev[0])
+
+    # Check for MESH-GROUP-STARTED event
+    check_mesh_group_added(dev[0])
+
+
+def test_wpas_mesh_group_remove(dev):
+    """wpa_supplicant MESH group remove"""
+    check_mesh_support(dev[0])
+    add_open_mesh_network(dev[0])
+    # Check for MESH-GROUP-STARTED event
+    check_mesh_group_added(dev[0])
+    dev[0].mesh_group_remove()
+    # Check for MESH-GROUP-REMOVED event
+    check_mesh_group_removed(dev[0])
+    dev[0].mesh_group_remove()
+
+def test_wpas_mesh_peer_connected(dev):
+    """wpa_supplicant MESH peer connected"""
+    check_mesh_support(dev[0])
+    add_open_mesh_network(dev[0], beacon_int=160)
+    add_open_mesh_network(dev[1], beacon_int=160)
+
+    # Check for mesh joined
+    check_mesh_group_added(dev[0])
+    check_mesh_group_added(dev[1])
+
+    # Check for peer connected
+    check_mesh_peer_connected(dev[0])
+    check_mesh_peer_connected(dev[1])
+
+
+def test_wpas_mesh_peer_disconnected(dev):
+    """wpa_supplicant MESH peer disconnected"""
+    check_mesh_support(dev[0])
+    add_open_mesh_network(dev[0])
+    add_open_mesh_network(dev[1])
+
+    # Check for mesh joined
+    check_mesh_group_added(dev[0])
+    check_mesh_group_added(dev[1])
+
+    # Check for peer connected
+    check_mesh_peer_connected(dev[0])
+    check_mesh_peer_connected(dev[1])
+
+    # Remove group on dev 1
+    dev[1].mesh_group_remove()
+    # Device 0 should get a disconnection event
+    check_mesh_peer_disconnected(dev[0])
+
+
+def test_wpas_mesh_mode_scan(dev):
+    """wpa_supplicant MESH scan support"""
+    check_mesh_support(dev[0])
+    add_open_mesh_network(dev[0])
+    add_open_mesh_network(dev[1], beacon_int=175)
+
+    # Check for mesh joined
+    check_mesh_group_added(dev[0])
+    check_mesh_group_added(dev[1])
+
+    # Check for Mesh scan
+    check_mesh_scan(dev[0], "use_id=1", beacon_int=175)
+
+def test_wpas_mesh_open(dev, apdev):
+    """wpa_supplicant open MESH network connectivity"""
+    check_mesh_support(dev[0])
+    add_open_mesh_network(dev[0], freq="2462")
+    add_open_mesh_network(dev[1], freq="2462")
+
+    # Check for mesh joined
+    check_mesh_group_added(dev[0])
+    check_mesh_group_added(dev[1])
+
+    # Check for peer connected
+    check_mesh_peer_connected(dev[0])
+    check_mesh_peer_connected(dev[1])
+
+    # Test connectivity 0->1 and 1->0
+    hwsim_utils.test_connectivity(dev[0], dev[1])
+
+def test_wpas_mesh_open_no_auto(dev, apdev):
+    """wpa_supplicant open MESH network connectivity"""
+    check_mesh_support(dev[0])
+    id = add_open_mesh_network(dev[0], start=False)
+    dev[0].set_network(id, "dot11MeshMaxRetries", "16")
+    dev[0].set_network(id, "dot11MeshRetryTimeout", "255")
+    dev[0].mesh_group_add(id)
+
+    id = add_open_mesh_network(dev[1], start=False)
+    dev[1].set_network(id, "no_auto_peer", "1")
+    dev[1].mesh_group_add(id)
+
+    # Check for mesh joined
+    check_mesh_group_added(dev[0])
+    check_mesh_group_added(dev[1])
+
+    # Check for peer connected
+    check_mesh_peer_connected(dev[0], timeout=30)
+    check_mesh_peer_connected(dev[1])
+
+    # Test connectivity 0->1 and 1->0
+    hwsim_utils.test_connectivity(dev[0], dev[1])
+
+def add_mesh_secure_net(dev, psk=True):
+    id = dev.add_network()
+    dev.set_network(id, "mode", "5")
+    dev.set_network_quoted(id, "ssid", "wpas-mesh-sec")
+    dev.set_network(id, "key_mgmt", "SAE")
+    dev.set_network(id, "frequency", "2412")
+    if psk:
+        dev.set_network_quoted(id, "psk", "thisismypassphrase!")
+    return id
+
+def test_wpas_mesh_secure(dev, apdev):
+    """wpa_supplicant secure MESH network connectivity"""
+    check_mesh_support(dev[0], secure=True)
+    dev[0].request("SET sae_groups ")
+    id = add_mesh_secure_net(dev[0])
+    dev[0].mesh_group_add(id)
+
+    dev[1].request("SET sae_groups ")
+    id = add_mesh_secure_net(dev[1])
+    dev[1].mesh_group_add(id)
+
+    # Check for mesh joined
+    check_mesh_group_added(dev[0])
+    check_mesh_group_added(dev[1])
+
+    # Check for peer connected
+    check_mesh_peer_connected(dev[0])
+    check_mesh_peer_connected(dev[1])
+
+    # Test connectivity 0->1 and 1->0
+    hwsim_utils.test_connectivity(dev[0], dev[1])
+
+def test_wpas_mesh_secure_sae_group_mismatch(dev, apdev):
+    """wpa_supplicant secure MESH and SAE group mismatch"""
+    check_mesh_support(dev[0], secure=True)
+    addr0 = dev[0].p2p_interface_addr()
+    addr1 = dev[1].p2p_interface_addr()
+    addr2 = dev[2].p2p_interface_addr()
+
+    dev[0].request("SET sae_groups 19 25")
+    id = add_mesh_secure_net(dev[0])
+    dev[0].mesh_group_add(id)
+
+    dev[1].request("SET sae_groups 19")
+    id = add_mesh_secure_net(dev[1])
+    dev[1].mesh_group_add(id)
+
+    dev[2].request("SET sae_groups 26")
+    id = add_mesh_secure_net(dev[2])
+    dev[2].mesh_group_add(id)
+
+    check_mesh_group_added(dev[0])
+    check_mesh_group_added(dev[1])
+    check_mesh_group_added(dev[2])
+
+    ev = dev[0].wait_event(["MESH-PEER-CONNECTED"])
+    if ev is None:
+        raise Exception("Remote peer did not connect")
+    if addr1 not in ev:
+        raise Exception("Unexpected peer connected: " + ev)
+
+    ev = dev[1].wait_event(["MESH-PEER-CONNECTED"])
+    if ev is None:
+        raise Exception("Remote peer did not connect")
+    if addr0 not in ev:
+        raise Exception("Unexpected peer connected: " + ev)
+
+    ev = dev[2].wait_event(["MESH-PEER-CONNECTED"], timeout=1)
+    if ev is not None:
+        raise Exception("Unexpected peer connection at dev[2]: " + ev)
+
+    ev = dev[0].wait_event(["MESH-PEER-CONNECTED"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected peer connection: " + ev)
+
+    ev = dev[1].wait_event(["MESH-PEER-CONNECTED"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected peer connection: " + ev)
+
+    dev[0].request("SET sae_groups ")
+    dev[1].request("SET sae_groups ")
+    dev[2].request("SET sae_groups ")
+
+def test_wpas_mesh_secure_sae_missing_password(dev, apdev):
+    """wpa_supplicant secure MESH and missing SAE password"""
+    check_mesh_support(dev[0], secure=True)
+    id = add_mesh_secure_net(dev[0], psk=False)
+    dev[0].set_network(id, "psk", "8f20b381f9b84371d61b5080ad85cac3c61ab3ca9525be5b2d0f4da3d979187a")
+    dev[0].mesh_group_add(id)
+    ev = dev[0].wait_event(["MESH-GROUP-STARTED", "Could not join mesh"],
+                           timeout=5)
+    if ev is None:
+        raise Exception("Timeout on mesh start event")
+    if "MESH-GROUP-STARTED" in ev:
+        raise Exception("Unexpected mesh group start")
+    ev = dev[0].wait_event(["MESH-GROUP-STARTED"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected mesh group start")
+
+def test_wpas_mesh_secure_no_auto(dev, apdev):
+    """wpa_supplicant secure MESH network connectivity"""
+    check_mesh_support(dev[0], secure=True)
+    dev[0].request("SET sae_groups 19")
+    id = add_mesh_secure_net(dev[0])
+    dev[0].mesh_group_add(id)
+
+    dev[1].request("SET sae_groups 19")
+    id = add_mesh_secure_net(dev[1])
+    dev[1].set_network(id, "no_auto_peer", "1")
+    dev[1].mesh_group_add(id)
+
+    # Check for mesh joined
+    check_mesh_group_added(dev[0])
+    check_mesh_group_added(dev[1])
+
+    # Check for peer connected
+    check_mesh_peer_connected(dev[0], timeout=30)
+    check_mesh_peer_connected(dev[1])
+
+    # Test connectivity 0->1 and 1->0
+    hwsim_utils.test_connectivity(dev[0], dev[1])
+
+    dev[0].request("SET sae_groups ")
+    dev[1].request("SET sae_groups ")
+
+def test_wpas_mesh_ctrl(dev):
+    """wpa_supplicant ctrl_iface mesh command error cases"""
+    check_mesh_support(dev[0])
+    if "FAIL" not in dev[0].request("MESH_GROUP_ADD 123"):
+        raise Exception("Unexpected MESH_GROUP_ADD success")
+    id = dev[0].add_network()
+    if "FAIL" not in dev[0].request("MESH_GROUP_ADD %d" % id):
+        raise Exception("Unexpected MESH_GROUP_ADD success")
+    dev[0].set_network(id, "mode", "5")
+    dev[0].set_network(id, "key_mgmt", "WPA-PSK")
+    if "FAIL" not in dev[0].request("MESH_GROUP_ADD %d" % id):
+        raise Exception("Unexpected MESH_GROUP_ADD success")
+
+    if "FAIL" not in dev[0].request("MESH_GROUP_REMOVE foo"):
+        raise Exception("Unexpected MESH_GROUP_REMOVE success")
+
+def test_wpas_mesh_dynamic_interface(dev):
+    """wpa_supplicant mesh with dynamic interface"""
+    check_mesh_support(dev[0])
+    mesh0 = None
+    mesh1 = None
+    try:
+        mesh0 = dev[0].request("MESH_INTERFACE_ADD ifname=mesh0")
+        if "FAIL" in mesh0:
+            raise Exception("MESH_INTERFACE_ADD failed")
+        mesh1 = dev[1].request("MESH_INTERFACE_ADD")
+        if "FAIL" in mesh1:
+            raise Exception("MESH_INTERFACE_ADD failed")
+
+        wpas0 = WpaSupplicant(ifname=mesh0)
+        wpas1 = WpaSupplicant(ifname=mesh1)
+        logger.info(mesh0 + " address " + wpas0.get_status_field("address"))
+        logger.info(mesh1 + " address " + wpas1.get_status_field("address"))
+
+        add_open_mesh_network(wpas0)
+        add_open_mesh_network(wpas1)
+        check_mesh_group_added(wpas0)
+        check_mesh_group_added(wpas1)
+        check_mesh_peer_connected(wpas0)
+        check_mesh_peer_connected(wpas1)
+        hwsim_utils.test_connectivity(wpas0, wpas1)
+
+        # Must not allow MESH_GROUP_REMOVE on dynamic interface
+        if "FAIL" not in wpas0.request("MESH_GROUP_REMOVE " + mesh0):
+            raise Exception("Invalid MESH_GROUP_REMOVE accepted")
+        if "FAIL" not in wpas1.request("MESH_GROUP_REMOVE " + mesh1):
+            raise Exception("Invalid MESH_GROUP_REMOVE accepted")
+
+        # Must not allow MESH_GROUP_REMOVE on another radio interface
+        if "FAIL" not in wpas0.request("MESH_GROUP_REMOVE " + mesh1):
+            raise Exception("Invalid MESH_GROUP_REMOVE accepted")
+        if "FAIL" not in wpas1.request("MESH_GROUP_REMOVE " + mesh0):
+            raise Exception("Invalid MESH_GROUP_REMOVE accepted")
+
+        wpas0.remove_ifname()
+        wpas1.remove_ifname()
+
+        if "OK" not in dev[0].request("MESH_GROUP_REMOVE " + mesh0):
+            raise Exception("MESH_GROUP_REMOVE failed")
+        if "OK" not in dev[1].request("MESH_GROUP_REMOVE " + mesh1):
+            raise Exception("MESH_GROUP_REMOVE failed")
+
+        if "FAIL" not in dev[0].request("MESH_GROUP_REMOVE " + mesh0):
+            raise Exception("Invalid MESH_GROUP_REMOVE accepted")
+        if "FAIL" not in dev[1].request("MESH_GROUP_REMOVE " + mesh1):
+            raise Exception("Invalid MESH_GROUP_REMOVE accepted")
+
+        logger.info("Make sure another dynamic group can be added")
+        mesh0 = dev[0].request("MESH_INTERFACE_ADD ifname=mesh0")
+        if "FAIL" in mesh0:
+            raise Exception("MESH_INTERFACE_ADD failed")
+        mesh1 = dev[1].request("MESH_INTERFACE_ADD")
+        if "FAIL" in mesh1:
+            raise Exception("MESH_INTERFACE_ADD failed")
+
+        wpas0 = WpaSupplicant(ifname=mesh0)
+        wpas1 = WpaSupplicant(ifname=mesh1)
+        logger.info(mesh0 + " address " + wpas0.get_status_field("address"))
+        logger.info(mesh1 + " address " + wpas1.get_status_field("address"))
+
+        add_open_mesh_network(wpas0)
+        add_open_mesh_network(wpas1)
+        check_mesh_group_added(wpas0)
+        check_mesh_group_added(wpas1)
+        check_mesh_peer_connected(wpas0)
+        check_mesh_peer_connected(wpas1)
+        hwsim_utils.test_connectivity(wpas0, wpas1)
+    finally:
+        if mesh0:
+            dev[0].request("MESH_GROUP_REMOVE " + mesh0)
+        if mesh1:
+            dev[1].request("MESH_GROUP_REMOVE " + mesh1)
+
+def test_wpas_mesh_max_peering(dev, apdev):
+    """Mesh max peering limit"""
+    check_mesh_support(dev[0])
+    try:
+        dev[0].request("SET max_peer_links 1")
+
+        # first, connect dev[0] and dev[1]
+        add_open_mesh_network(dev[0])
+        add_open_mesh_network(dev[1])
+        for i in range(2):
+            ev = dev[i].wait_event(["MESH-PEER-CONNECTED"])
+            if ev is None:
+                raise Exception("dev%d did not connect with any peer" % i)
+
+        # add dev[2] which will try to connect with both dev[0] and dev[1],
+        # but can complete connection only with dev[1]
+        add_open_mesh_network(dev[2])
+        for i in range(1, 3):
+            ev = dev[i].wait_event(["MESH-PEER-CONNECTED"])
+            if ev is None:
+                raise Exception("dev%d did not connect the second peer" % i)
+
+        ev = dev[0].wait_event(["MESH-PEER-CONNECTED"], timeout=1)
+        if ev is not None:
+            raise Exception("dev0 connection beyond max peering limit")
+
+        ev = dev[2].wait_event(["MESH-PEER-CONNECTED"], timeout=0.1)
+        if ev is not None:
+            raise Exception("dev2 reported unexpected peering: " + ev)
+
+        for i in range(3):
+            dev[i].mesh_group_remove()
+            check_mesh_group_removed(dev[i])
+    finally:
+        dev[0].request("SET max_peer_links 99")
+
+def test_wpas_mesh_open_5ghz(dev, apdev):
+    """wpa_supplicant open MESH network on 5 GHz band"""
+    try:
+        _test_wpas_mesh_open_5ghz(dev, apdev)
+    finally:
+        subprocess.call(['iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+        dev[1].flush_scan_cache()
+
+def _test_wpas_mesh_open_5ghz(dev, apdev):
+    check_mesh_support(dev[0])
+    subprocess.call(['iw', 'reg', 'set', 'US'])
+    for i in range(2):
+        for j in range(5):
+            ev = dev[i].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
+            if ev is None:
+                raise Exception("No regdom change event")
+            if "alpha2=US" in ev:
+                break
+        add_open_mesh_network(dev[i], freq="5180")
+
+    # Check for mesh joined
+    check_mesh_group_added(dev[0])
+    check_mesh_group_added(dev[1])
+
+    # Check for peer connected
+    check_mesh_peer_connected(dev[0])
+    check_mesh_peer_connected(dev[1])
+
+    # Test connectivity 0->1 and 1->0
+    hwsim_utils.test_connectivity(dev[0], dev[1])
+
+def test_wpas_mesh_password_mismatch(dev, apdev):
+    """Mesh network and one device with mismatching password"""
+    check_mesh_support(dev[0], secure=True)
+    dev[0].request("SET sae_groups ")
+    id = add_mesh_secure_net(dev[0])
+    dev[0].mesh_group_add(id)
+
+    dev[1].request("SET sae_groups ")
+    id = add_mesh_secure_net(dev[1])
+    dev[1].mesh_group_add(id)
+
+    dev[2].request("SET sae_groups ")
+    id = add_mesh_secure_net(dev[2])
+    dev[2].set_network_quoted(id, "psk", "wrong password")
+    dev[2].mesh_group_add(id)
+
+    # The two peers with matching password need to be able to connect
+    check_mesh_group_added(dev[0])
+    check_mesh_group_added(dev[1])
+    check_mesh_peer_connected(dev[0])
+    check_mesh_peer_connected(dev[1])
+
+    ev = dev[2].wait_event(["MESH-SAE-AUTH-FAILURE"], timeout=20)
+    if ev is None:
+        raise Exception("dev2 did not report auth failure (1)")
+    ev = dev[2].wait_event(["MESH-SAE-AUTH-FAILURE"], timeout=20)
+    if ev is None:
+        raise Exception("dev2 did not report auth failure (2)")
+
+    count = 0
+    ev = dev[0].wait_event(["MESH-SAE-AUTH-FAILURE"], timeout=1)
+    if ev is None:
+        logger.info("dev0 did not report auth failure")
+    else:
+        if "addr=" + dev[2].own_addr() not in ev:
+            raise Exception("Unexpected peer address in dev0 event: " + ev)
+        count += 1
+
+    ev = dev[1].wait_event(["MESH-SAE-AUTH-FAILURE"], timeout=1)
+    if ev is None:
+        logger.info("dev1 did not report auth failure")
+    else:
+        if "addr=" + dev[2].own_addr() not in ev:
+            raise Exception("Unexpected peer address in dev1 event: " + ev)
+        count += 1
+
+    hwsim_utils.test_connectivity(dev[0], dev[1])
+
+    for i in range(2):
+        try:
+            hwsim_utils.test_connectivity(dev[i], dev[2], timeout=1)
+            raise Exception("Data connectivity test passed unexpectedly")
+        except Exception, e:
+            if "data delivery failed" not in str(e):
+                raise
+
+    if count == 0:
+        raise Exception("Neither dev0 nor dev1 reported auth failure")
+
+def test_wpas_mesh_password_mismatch_retry(dev, apdev, params):
+    """Mesh password mismatch and retry [long]"""
+    if not params['long']:
+        raise HwsimSkip("Skip test case with long duration due to --long not specified")
+    check_mesh_support(dev[0], secure=True)
+    dev[0].request("SET sae_groups ")
+    id = add_mesh_secure_net(dev[0])
+    dev[0].mesh_group_add(id)
+
+    dev[1].request("SET sae_groups ")
+    id = add_mesh_secure_net(dev[1])
+    dev[1].set_network_quoted(id, "psk", "wrong password")
+    dev[1].mesh_group_add(id)
+
+    # Check for mesh joined
+    check_mesh_group_added(dev[0])
+    check_mesh_group_added(dev[1])
+
+    for i in range(4):
+        ev = dev[0].wait_event(["MESH-SAE-AUTH-FAILURE"], timeout=20)
+        if ev is None:
+            raise Exception("dev0 did not report auth failure (%d)" % i)
+        ev = dev[1].wait_event(["MESH-SAE-AUTH-FAILURE"], timeout=20)
+        if ev is None:
+            raise Exception("dev1 did not report auth failure (%d)" % i)
+
+    ev = dev[0].wait_event(["MESH-SAE-AUTH-BLOCKED"], timeout=10)
+    if ev is None:
+        raise Exception("dev0 did not report auth blocked")
+    ev = dev[1].wait_event(["MESH-SAE-AUTH-BLOCKED"], timeout=10)
+    if ev is None:
+        raise Exception("dev1 did not report auth blocked")
+
+def test_mesh_wpa_auth_init_oom(dev, apdev):
+    """Secure mesh network setup failing due to wpa_init() OOM"""
+    check_mesh_support(dev[0], secure=True)
+    dev[0].request("SET sae_groups ")
+    with alloc_fail(dev[0], 1, "wpa_init"):
+        id = add_mesh_secure_net(dev[0])
+        dev[0].mesh_group_add(id)
+        ev = dev[0].wait_event(["MESH-GROUP-STARTED"], timeout=0.2)
+        if ev is not None:
+            raise Exception("Unexpected mesh group start during OOM")
diff --git a/hostap/tests/hwsim/test_wpas_wmm_ac.py b/hostap/tests/hwsim/test_wpas_wmm_ac.py
new file mode 100644
index 0000000..d2d5596
--- /dev/null
+++ b/hostap/tests/hwsim/test_wpas_wmm_ac.py
@@ -0,0 +1,280 @@
+# Test cases for wpa_supplicant WMM-AC operations
+# Copyright (c) 2014, Intel Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import struct
+
+import hwsim_utils
+import hostapd
+
+def add_wmm_ap(apdev, acm_list):
+    params = { "ssid": "wmm_ac",
+               "hw_mode": "g",
+               "channel": "11",
+               "wmm_enabled" : "1"}
+
+    for ac in acm_list:
+        params["wmm_ac_%s_acm" % (ac.lower())] = "1"
+
+    return hostapd.add_ap(apdev['ifname'], params)
+
+def test_tspec(dev, apdev):
+    """Basic addts/delts tests"""
+    # configure ap with VO and VI requiring admission-control
+    hapd = add_wmm_ap(apdev[0], ["VO", "VI"])
+    dev[0].connect("wmm_ac", key_mgmt="NONE", scan_freq="2462")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    status = dev[0].request("WMM_AC_STATUS")
+    if "WMM AC is Enabled" not in status:
+        raise Exception("WMM-AC not enabled")
+    if "TSID" in status:
+        raise Exception("Unexpected TSID info")
+    if "BK: acm=0 uapsd=0" not in status:
+        raise Exception("Unexpected BK info" + status)
+    if "BE: acm=0 uapsd=0" not in status:
+        raise Exception("Unexpected BE info" + status)
+    if "VI: acm=1 uapsd=0" not in status:
+        raise Exception("Unexpected VI info" + status)
+    if "VO: acm=1 uapsd=0" not in status:
+        raise Exception("Unexpected VO info" + status)
+
+    # no tsid --> tsid out of range
+    if "FAIL" not in dev[0].request("WMM_AC_ADDTS downlink"):
+        raise Exception("Invalid WMM_AC_ADDTS accepted")
+    # no direction
+    if "FAIL" not in dev[0].request("WMM_AC_ADDTS tsid=5"):
+        raise Exception("Invalid WMM_AC_ADDTS accepted")
+    # param out of range
+    if "FAIL" not in dev[0].request("WMM_AC_ADDTS tsid=5 downlink"):
+        raise Exception("Invalid WMM_AC_ADDTS accepted")
+
+    tsid = 5
+
+    # make sure we fail when the ac is not configured for acm
+    try:
+        dev[0].add_ts(tsid, 3)
+        raise Exception("ADDTS succeeded although it should have failed")
+    except Exception, e:
+        if not str(e).startswith("ADDTS failed"):
+            raise
+    status = dev[0].request("WMM_AC_STATUS")
+    if "TSID" in status:
+        raise Exception("Unexpected TSID info")
+
+    # add tspec for UP=6
+    dev[0].add_ts(tsid, 6)
+    status = dev[0].request("WMM_AC_STATUS")
+    if "TSID" not in status:
+        raise Exception("Missing TSID info")
+
+    # using the same tsid for a different ac is invalid
+    try:
+        dev[0].add_ts(tsid, 5)
+        raise Exception("ADDTS succeeded although it should have failed")
+    except Exception, e:
+        if not str(e).startswith("ADDTS failed"):
+            raise
+
+    # update the tspec for a different UP of the same ac
+    dev[0].add_ts(tsid, 7, extra="fixed_nominal_msdu")
+    dev[0].del_ts(tsid)
+    status = dev[0].request("WMM_AC_STATUS")
+    if "TSID" in status:
+        raise Exception("Unexpected TSID info")
+
+    # verify failure on uplink/bidi without driver support
+    tsid = 6
+    try:
+        dev[0].add_ts(tsid, 7, direction="uplink")
+        raise Exception("ADDTS succeeded although it should have failed")
+    except Exception, e:
+        if not str(e).startswith("ADDTS failed"):
+            raise
+    try:
+        dev[0].add_ts(tsid, 7, direction="bidi")
+        raise Exception("ADDTS succeeded although it should have failed")
+    except Exception, e:
+        if not str(e).startswith("ADDTS failed"):
+            raise
+
+    # attempt to delete non-existing tsid
+    try:
+        dev[0].del_ts(tsid)
+        raise Exception("DELTS succeeded although it should have failed")
+    except Exception, e:
+        if not str(e).startswith("DELTS failed"):
+            raise
+
+def test_tspec_protocol(dev, apdev):
+    """Protocol tests for addts/delts"""
+    # configure ap with VO and VI requiring admission-control
+    hapd = add_wmm_ap(apdev[0], ["VO", "VI"])
+    dev[0].connect("wmm_ac", key_mgmt="NONE", scan_freq="2462")
+
+    dev[0].dump_monitor()
+    hapd.set("ext_mgmt_frame_handling", "1")
+
+    tsid = 6
+
+    # timeout on ADDTS response
+    dev[0].add_ts(tsid, 7, expect_failure=True)
+
+    hapd.dump_monitor()
+    req = "WMM_AC_ADDTS downlink tsid=6 up=7 nominal_msdu_size=1500 sba=9000 mean_data_rate=1500 min_phy_rate=6000000"
+    if "OK" not in dev[0].request(req):
+        raise Exception("WMM_AC_ADDTS failed")
+    # a new request while previous is still pending
+    if "FAIL" not in dev[0].request(req):
+        raise Exception("WMM_AC_ADDTS accepted while oen was still pending")
+    msg = hapd.mgmt_rx()
+    payload = msg['payload']
+    (categ, action, dialog, status) = struct.unpack('BBBB', payload[0:4])
+    if action != 0:
+        raise Exception("Unexpected Action code: %d" % action)
+
+    msg['da'] = msg['sa']
+    msg['sa'] = apdev[0]['bssid']
+
+    # unexpected dialog token
+    msg['payload'] = struct.pack('BBBB', 17, 1, (dialog + 1) & 0xff, 0) + payload[4:]
+    hapd.mgmt_tx(msg)
+
+    # valid response
+    msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0) + payload[4:]
+    hapd.mgmt_tx(msg)
+    ev = dev[0].wait_event(["TSPEC-ADDED"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on TSPEC-ADDED")
+    if "tsid=%d" % tsid not in ev:
+        raise Exception("Unexpected TSPEC-ADDED contents: " + ev)
+
+    # duplicated response
+    msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0) + payload[4:]
+    hapd.mgmt_tx(msg)
+
+    # too short ADDTS
+    msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0)
+    hapd.mgmt_tx(msg)
+
+    # invalid IE
+    msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0) + payload[4:] + struct.pack('BB', 0xdd, 100)
+    hapd.mgmt_tx(msg)
+
+    # too short WMM element
+    msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0) + payload[4:] + '\xdd\x06\x00\x50\xf2\x02\x02\x01'
+    hapd.mgmt_tx(msg)
+
+    # DELTS
+    dev[0].dump_monitor()
+    msg['payload'] = struct.pack('BBBB', 17, 2, 0, 0) + payload[4:]
+    hapd.mgmt_tx(msg)
+    ev = dev[0].wait_event(['TSPEC-REMOVED'], timeout=6)
+    if ev is None:
+        raise Exception("Timeout on TSPEC-REMOVED event")
+    if "tsid=%d" % tsid not in ev:
+        raise Exception("Unexpected TSPEC-REMOVED contents: " + ev)
+    # DELTS duplicated
+    msg['payload'] = struct.pack('BBBB', 17, 2, 0, 0) + payload[4:]
+    hapd.mgmt_tx(msg)
+
+    # start a new request
+    hapd.dump_monitor()
+    if "OK" not in dev[0].request(req):
+        raise Exception("WMM_AC_ADDTS failed")
+    msg = hapd.mgmt_rx()
+    payload = msg['payload']
+    (categ, action, dialog, status) = struct.unpack('BBBB', payload[0:4])
+    if action != 0:
+        raise Exception("Unexpected Action code: %d" % action)
+
+    msg['da'] = msg['sa']
+    msg['sa'] = apdev[0]['bssid']
+
+    # modified parameters
+    msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 1) + payload[4:12] + struct.pack('B', ord(payload[12]) & ~0x60) + payload[13:]
+    hapd.mgmt_tx(msg)
+
+    # reject request
+    msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 1) + payload[4:]
+    hapd.mgmt_tx(msg)
+    ev = dev[0].wait_event(["TSPEC-REQ-FAILED"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on TSPEC-REQ-FAILED")
+    if "tsid=%d" % tsid not in ev:
+        raise Exception("Unexpected TSPEC-REQ-FAILED contents: " + ev)
+
+    hapd.set("ext_mgmt_frame_handling", "0")
+
+def test_tspec_not_enabled(dev, apdev):
+    """addts failing if AP does not support WMM"""
+    params = { "ssid": "wmm_no_ac",
+               "hw_mode": "g",
+               "channel": "11",
+               "wmm_enabled" : "0" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    dev[0].connect("wmm_no_ac", key_mgmt="NONE", scan_freq="2462")
+    status = dev[0].request("WMM_AC_STATUS")
+    if "Not associated to a WMM AP, WMM AC is Disabled" not in status:
+        raise Exception("Unexpected WMM_AC_STATUS: " + status)
+
+    try:
+        dev[0].add_ts(5, 6)
+        raise Exception("ADDTS succeeded although it should have failed")
+    except Exception, e:
+        if not str(e).startswith("ADDTS failed"):
+            raise
+
+    # attempt to delete non-existing tsid
+    try:
+        dev[0].del_ts(5)
+        raise Exception("DELTS succeeded although it should have failed")
+    except Exception, e:
+        if not str(e).startswith("DELTS failed"):
+            raise
+
+    # unexpected Action frame when WMM is disabled
+    MGMT_SUBTYPE_ACTION = 13
+    msg = {}
+    msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+    msg['da'] = dev[0].p2p_interface_addr()
+    msg['sa'] = apdev[0]['bssid']
+    msg['bssid'] = apdev[0]['bssid']
+    msg['payload'] = struct.pack('BBBB', 17, 2, 0, 0)
+    hapd.mgmt_tx(msg)
+
+def test_tspec_ap_roam_open(dev, apdev):
+    """Roam between two open APs while having tspecs"""
+    hapd0 = add_wmm_ap(apdev[0], ["VO", "VI"])
+    dev[0].connect("wmm_ac", key_mgmt="NONE")
+    hwsim_utils.test_connectivity(dev[0], hapd0)
+    dev[0].add_ts(5, 6)
+
+    hapd1 = add_wmm_ap(apdev[1], ["VO", "VI"])
+    dev[0].scan_for_bss(apdev[1]['bssid'], freq=2462)
+    dev[0].roam(apdev[1]['bssid'])
+    hwsim_utils.test_connectivity(dev[0], hapd1)
+    if dev[0].tspecs():
+        raise Exception("TSPECs weren't deleted on roaming")
+
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2462)
+    dev[0].roam(apdev[0]['bssid'])
+    hwsim_utils.test_connectivity(dev[0], hapd0)
+
+def test_tspec_reassoc(dev, apdev):
+    """Reassociation to same BSS while having tspecs"""
+    hapd0 = add_wmm_ap(apdev[0], ["VO", "VI"])
+    dev[0].connect("wmm_ac", key_mgmt="NONE")
+    hwsim_utils.test_connectivity(dev[0], hapd0)
+    dev[0].add_ts(5, 6)
+    last_tspecs = dev[0].tspecs()
+
+    dev[0].request("REASSOCIATE")
+    dev[0].wait_connected()
+
+    hwsim_utils.test_connectivity(dev[0], hapd0)
+    if dev[0].tspecs() != last_tspecs:
+        raise Exception("TSPECs weren't saved on reassociation")
diff --git a/hostap/tests/hwsim/tnc/Makefile b/hostap/tests/hwsim/tnc/Makefile
new file mode 100644
index 0000000..64ba0ca
--- /dev/null
+++ b/hostap/tests/hwsim/tnc/Makefile
@@ -0,0 +1,23 @@
+CFLAGS += -I$(abspath ../../../src)
+CFLAGS += -I$(abspath ../../../src/utils)
+
+ALL=libhostap_imc.so libhostap_imv.so libhostap2_imc.so libhostap2_imv.so
+all: $(ALL)
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
+
+lib%.so: %.c
+	$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $<
+	@$(E) "  CC " $@
+
+clean:
+	rm -f $(ALL)
diff --git a/hostap/tests/hwsim/tnc/hostap2_imc.c b/hostap/tests/hwsim/tnc/hostap2_imc.c
new file mode 100644
index 0000000..3818c17
--- /dev/null
+++ b/hostap/tests/hwsim/tnc/hostap2_imc.c
@@ -0,0 +1,183 @@
+/*
+ * Example IMC for TNC testing
+ * 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 "includes.h"
+
+#include "common.h"
+#include "common/tnc.h"
+
+static int initialized = 0;
+static TNC_IMCID my_id = -1;
+static TNC_TNCC_SendMessagePointer send_message = NULL;
+static TNC_TNCC_ReportMessageTypesPointer report_message_types = NULL;
+static TNC_TNCC_RequestHandshakeRetryPointer request_retry = NULL;
+
+static TNC_MessageType message_types[] =
+{
+	(TNC_VENDORID_ANY << 8) | TNC_SUBTYPE_ANY
+};
+
+
+TNC_Result TNC_IMC_Initialize(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_Version minVersion,
+	/*in*/ TNC_Version maxVersion,
+	/*out*/ TNC_Version *pOutActualVersion)
+{
+	wpa_printf(MSG_INFO,
+		   "IMC(hostap2) %s(imcID=%u, minVersion=%u, maxVersion=%u)",
+		   __func__, (unsigned) imcID, (unsigned) minVersion,
+		   (unsigned) maxVersion);
+
+	if (initialized)
+		return TNC_RESULT_ALREADY_INITIALIZED;
+
+	if (minVersion < TNC_IFIMC_VERSION_1 ||
+	    maxVersion > TNC_IFIMC_VERSION_1)
+		return TNC_RESULT_NO_COMMON_VERSION;
+
+	if (!pOutActualVersion)
+		return TNC_RESULT_INVALID_PARAMETER;
+	*pOutActualVersion = TNC_IFIMC_VERSION_1;
+	my_id = imcID;
+
+	initialized = 1;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_BeginHandshake(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_ConnectionID connectionID)
+{
+	char *msg = "hello";
+	TNC_Result res;
+
+	wpa_printf(MSG_INFO, "IMC(hostap2) %s(imcID=%u, connectionID=%u)",
+		   __func__, (unsigned) imcID, (unsigned) connectionID);
+
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imcID != my_id)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	if (!send_message)
+		return TNC_RESULT_FATAL;
+
+	res = send_message(imcID, connectionID, msg, os_strlen(msg), 1);
+	if (res != TNC_RESULT_SUCCESS)
+		return res;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_ProvideBindFunction(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_TNCC_BindFunctionPointer bindFunction)
+{
+	TNC_Result res;
+
+	wpa_printf(MSG_INFO, "IMC(hostap2) %s(imcID=%u)",
+		   __func__, (unsigned) imcID);
+
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imcID != my_id || !bindFunction)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	if (bindFunction(imcID, "TNC_TNCC_SendMessage",
+			 (void **) &send_message) != TNC_RESULT_SUCCESS ||
+	    !send_message)
+		return TNC_RESULT_FATAL;
+
+	if (bindFunction(imcID, "TNC_TNCC_ReportMessageTypes",
+			 (void **) &report_message_types) !=
+	    TNC_RESULT_SUCCESS ||
+	    !report_message_types)
+		return TNC_RESULT_FATAL;
+
+	if (bindFunction(imcID, "TNC_TNCC_RequestHandshakeRetry",
+			 (void **) &request_retry) != TNC_RESULT_SUCCESS ||
+	    !request_retry)
+		return TNC_RESULT_FATAL;
+
+	res = report_message_types(imcID, message_types,
+				   ARRAY_SIZE(message_types));
+	if (res != TNC_RESULT_SUCCESS)
+		return res;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_NotifyConnectionChange(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_ConnectionID connectionID,
+	/*in*/ TNC_ConnectionState newState)
+{
+	wpa_printf(MSG_INFO,
+		   "IMC(hostap2) %s(imcID=%u, connectionID=%u, newState=%u)",
+		   __func__, (unsigned) imcID, (unsigned) connectionID,
+		   (unsigned) newState);
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_ReceiveMessage(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_ConnectionID connectionID,
+	/*in*/ TNC_BufferReference message,
+	/*in*/ TNC_UInt32 messageLength,
+	/*in*/ TNC_MessageType messageType)
+{
+	TNC_Result res;
+
+	wpa_printf(MSG_INFO,
+		   "IMC(hostap2) %s(imcID=%u, connectionID=%u, messageType=%u)",
+		   __func__, (unsigned) imcID, (unsigned) connectionID,
+		   (unsigned) messageType);
+	wpa_hexdump_ascii(MSG_INFO, "IMC(hostap2) message",
+			  message, messageLength);
+
+	if (messageType == 1 && messageLength == 5 &&
+	    os_memcmp(message, "hello", 5) == 0) {
+		char *msg = "i'm fine";
+
+		res = send_message(imcID, connectionID, msg, os_strlen(msg), 1);
+		if (res != TNC_RESULT_SUCCESS)
+			return res;
+	}
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_BatchEnding(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_ConnectionID connectionID)
+{
+	wpa_printf(MSG_INFO, "IMC(hostap2) %s(imcID=%u, connectionID=%u)",
+		   __func__, (unsigned) imcID, (unsigned) connectionID);
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_Terminate(
+	/*in*/ TNC_IMCID imcID)
+{
+	wpa_printf(MSG_INFO, "IMC(hostap2) %s(imcID=%u)",
+		   __func__, (unsigned) imcID);
+
+	return TNC_RESULT_SUCCESS;
+}
diff --git a/hostap/tests/hwsim/tnc/hostap2_imv.c b/hostap/tests/hwsim/tnc/hostap2_imv.c
new file mode 100644
index 0000000..652888a
--- /dev/null
+++ b/hostap/tests/hwsim/tnc/hostap2_imv.c
@@ -0,0 +1,203 @@
+/*
+ * Example IMV for TNC testing
+ * 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 "includes.h"
+
+#include "common.h"
+#include "common/tnc.h"
+
+static int initialized = 0;
+static TNC_IMVID my_id = -1;
+static TNC_TNCS_ReportMessageTypesPointer report_message_types = NULL;
+static TNC_TNCS_SendMessagePointer send_message = NULL;
+static TNC_TNCS_RequestHandshakeRetryPointer request_retry = NULL;
+TNC_TNCS_ProvideRecommendationPointer provide_recomm = NULL;
+
+static TNC_MessageType message_types[] =
+{
+	(TNC_VENDORID_ANY << 8) | TNC_SUBTYPE_ANY
+};
+
+
+TNC_Result TNC_IMV_Initialize(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_Version minVersion,
+	/*in*/ TNC_Version maxVersion,
+	/*out*/ TNC_Version *pOutActualVersion)
+{
+	wpa_printf(MSG_INFO,
+		   "IMV(hostap2) %s(imvID=%u, minVersion=%u, maxVersion=%u)",
+		   __func__, (unsigned) imvID, (unsigned) minVersion,
+		   (unsigned) maxVersion);
+
+	if (initialized)
+		return TNC_RESULT_ALREADY_INITIALIZED;
+
+	if (minVersion < TNC_IFIMV_VERSION_1 ||
+	    maxVersion > TNC_IFIMV_VERSION_1)
+		return TNC_RESULT_NO_COMMON_VERSION;
+
+	if (!pOutActualVersion)
+		return TNC_RESULT_INVALID_PARAMETER;
+	*pOutActualVersion = TNC_IFIMV_VERSION_1;
+
+	initialized = 1;
+	my_id = imvID;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_NotifyConnectionChange(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_ConnectionID connectionID,
+	/*in*/ TNC_ConnectionState newState)
+{
+	wpa_printf(MSG_INFO,
+		   "IMV(hostap2) %s(imvID=%u, connectionID=%u, newState=%u)",
+		   __func__, (unsigned) imvID, (unsigned) connectionID,
+		   (unsigned) newState);
+
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imvID != my_id)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	/* TODO: call TNC_TNCS_ProvideRecommendation */
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_ReceiveMessage(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_ConnectionID connectionID,
+	/*in*/ TNC_BufferReference message,
+	/*in*/ TNC_UInt32 messageLength,
+	/*in*/ TNC_MessageType messageType)
+{
+	TNC_Result res;
+
+	wpa_printf(MSG_INFO,
+		   "IMV(hostap2) %s(imvID=%u, connectionID=%u, messageType=%u)",
+		   __func__, (unsigned) imvID, (unsigned) connectionID,
+		   (unsigned) messageType);
+	wpa_hexdump_ascii(MSG_INFO, "IMV(hostap2) message",
+			  message, messageLength);
+
+	if (!send_message)
+		return TNC_RESULT_FATAL;
+
+	if (messageType == 1 && messageLength == 5 &&
+	    os_memcmp(message, "hello", 5) == 0) {
+		char *msg = "hello";
+
+		res = send_message(imvID, connectionID, msg, os_strlen(msg), 1);
+		if (res != TNC_RESULT_SUCCESS)
+			return res;
+	}
+
+	if (messageType == 1 && messageLength == 8 &&
+	    os_memcmp(message, "i'm fine", 8) == 0) {
+		if (!provide_recomm)
+			return TNC_RESULT_FATAL;
+		res = provide_recomm(imvID, connectionID,
+				     TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
+				     TNC_IMV_EVALUATION_RESULT_COMPLIANT);
+		if (res != TNC_RESULT_SUCCESS)
+			return res;
+	}
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_SolicitRecommendation(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_ConnectionID connectionID)
+{
+	wpa_printf(MSG_INFO, "IMV(hostap2) %s(imvID=%u, connectionID=%u)",
+		   __func__, (unsigned) imvID, (unsigned) connectionID);
+
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imvID != my_id)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	/* TODO: call TNC_TNCS_ProvideRecommendation */
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_BatchEnding(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_ConnectionID connectionID)
+{
+	wpa_printf(MSG_INFO, "IMV(hostap2) %s(imvID=%u, connectionID=%u)",
+		   __func__, (unsigned) imvID, (unsigned) connectionID);
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_Terminate(
+	/*in*/ TNC_IMVID imvID)
+{
+	wpa_printf(MSG_INFO, "IMV(hostap2) %s(imvID=%u)",
+		   __func__, (unsigned) imvID);
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_ProvideBindFunction(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_TNCS_BindFunctionPointer bindFunction)
+{
+	TNC_Result res;
+
+	wpa_printf(MSG_INFO, "IMV(hostap2) %s(imvID=%u)",
+		   __func__, (unsigned) imvID);
+
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imvID != my_id || !bindFunction)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	if (bindFunction(imvID, "TNC_TNCS_ReportMessageTypes",
+			 (void **) &report_message_types) !=
+	    TNC_RESULT_SUCCESS ||
+	    !report_message_types)
+		return TNC_RESULT_FATAL;
+
+	if (bindFunction(imvID, "TNC_TNCS_SendMessage",
+			 (void **) &send_message) != TNC_RESULT_SUCCESS ||
+	    !send_message)
+		return TNC_RESULT_FATAL;
+
+	if (bindFunction(imvID, "TNC_TNCS_RequestHandshakeRetry",
+			 (void **) &request_retry) != TNC_RESULT_SUCCESS ||
+	    !request_retry)
+		return TNC_RESULT_FATAL;
+
+	if (bindFunction(imvID, "TNC_TNCS_ProvideRecommendation",
+			 (void **) &provide_recomm) != TNC_RESULT_SUCCESS ||
+	    !provide_recomm)
+		return TNC_RESULT_FATAL;
+
+	res = report_message_types(imvID, message_types,
+				   ARRAY_SIZE(message_types));
+	if (res != TNC_RESULT_SUCCESS)
+		return res;
+
+	return TNC_RESULT_SUCCESS;
+}
diff --git a/hostap/tests/hwsim/tnc/hostap_imc.c b/hostap/tests/hwsim/tnc/hostap_imc.c
new file mode 100644
index 0000000..d28183a
--- /dev/null
+++ b/hostap/tests/hwsim/tnc/hostap_imc.c
@@ -0,0 +1,72 @@
+/*
+ * Minimal example IMC for TNC testing
+ * 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 "includes.h"
+
+#include "common.h"
+#include "common/tnc.h"
+
+static int initialized = 0;
+static TNC_IMCID my_id = -1;
+
+TNC_Result TNC_IMC_Initialize(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_Version minVersion,
+	/*in*/ TNC_Version maxVersion,
+	/*out*/ TNC_Version *pOutActualVersion)
+{
+	wpa_printf(MSG_INFO, "IMC(hostap) %s", __func__);
+
+	if (initialized)
+		return TNC_RESULT_ALREADY_INITIALIZED;
+
+	if (minVersion < TNC_IFIMC_VERSION_1 ||
+	    maxVersion > TNC_IFIMC_VERSION_1)
+		return TNC_RESULT_NO_COMMON_VERSION;
+
+	if (!pOutActualVersion)
+		return TNC_RESULT_INVALID_PARAMETER;
+	*pOutActualVersion = TNC_IFIMC_VERSION_1;
+	my_id = imcID;
+
+	initialized = 1;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_BeginHandshake(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_ConnectionID connectionID)
+{
+	wpa_printf(MSG_INFO, "IMC(hostap) %s", __func__);
+
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imcID != my_id)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_ProvideBindFunction(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_TNCC_BindFunctionPointer bindFunction)
+{
+	wpa_printf(MSG_INFO, "IMC(hostap) %s", __func__);
+
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imcID != my_id)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	return TNC_RESULT_SUCCESS;
+}
diff --git a/hostap/tests/hwsim/tnc/hostap_imv.c b/hostap/tests/hwsim/tnc/hostap_imv.c
new file mode 100644
index 0000000..0f4f9c8
--- /dev/null
+++ b/hostap/tests/hwsim/tnc/hostap_imv.c
@@ -0,0 +1,66 @@
+/*
+ * Minimal example IMV for TNC testing
+ * 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 "includes.h"
+
+#include "common.h"
+#include "common/tnc.h"
+
+static int initialized = 0;
+static TNC_IMVID my_id = -1;
+
+TNC_Result TNC_IMV_Initialize(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_Version minVersion,
+	/*in*/ TNC_Version maxVersion,
+	/*out*/ TNC_Version *pOutActualVersion)
+{
+	if (initialized)
+		return TNC_RESULT_ALREADY_INITIALIZED;
+
+	if (minVersion < TNC_IFIMV_VERSION_1 ||
+	    maxVersion > TNC_IFIMV_VERSION_1)
+		return TNC_RESULT_NO_COMMON_VERSION;
+
+	if (!pOutActualVersion)
+		return TNC_RESULT_INVALID_PARAMETER;
+	*pOutActualVersion = TNC_IFIMV_VERSION_1;
+
+	initialized = 1;
+	my_id = imvID;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_SolicitRecommendation(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_ConnectionID connectionID)
+{
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imvID != my_id)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_ProvideBindFunction(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_TNCS_BindFunctionPointer bindFunction)
+{
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imvID != my_id)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	return TNC_RESULT_SUCCESS;
+}
diff --git a/hostap/tests/hwsim/tnc/tnc_config b/hostap/tests/hwsim/tnc/tnc_config
new file mode 100644
index 0000000..613783a
--- /dev/null
+++ b/hostap/tests/hwsim/tnc/tnc_config
@@ -0,0 +1,4 @@
+IMC "hostap IMC" tnc/libhostap_imc.so
+IMV "hostap IMV" tnc/libhostap_imv.so
+IMC "hostap2 IMC" tnc/libhostap2_imc.so
+IMV "hostap2 IMV" tnc/libhostap2_imv.so
diff --git a/hostap/tests/hwsim/tshark.py b/hostap/tests/hwsim/tshark.py
new file mode 100644
index 0000000..ee70cfd
--- /dev/null
+++ b/hostap/tests/hwsim/tshark.py
@@ -0,0 +1,54 @@
+#
+# tshark module - refactored from test_scan.py
+#
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+# Copyright (c) 2015, Intel Mobile Communications GmbH
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+
+
+_tshark_filter_arg = '-Y'
+
+def run_tshark(filename, filter, display=None, wait=True):
+    global _tshark_filter_arg
+
+    if wait:
+        # wait a bit to make it more likely for wlantest sniffer to have
+        # captured and written the results into a file that we can process here
+        time.sleep(1)
+
+    try:
+        arg = [ "tshark", "-r", filename,
+                _tshark_filter_arg, filter ]
+        if display:
+            arg.append('-Tfields')
+            for d in display:
+                arg.append('-e')
+                arg.append(d)
+        else:
+            arg.append('-V')
+        cmd = subprocess.Popen(arg, stdout=subprocess.PIPE,
+                               stderr=open('/dev/null', 'w'))
+    except Exception, e:
+        logger.info("Could run run tshark check: " + str(e))
+        cmd = None
+        return None
+
+    out = cmd.communicate()[0]
+    res = cmd.wait()
+    if res == 1:
+        # remember this for efficiency
+        _tshark_filter_arg = '-R'
+        arg[3] = '-R'
+        cmd = subprocess.Popen(arg, stdout=subprocess.PIPE,
+                               stderr=open('/dev/null', 'w'))
+        out = cmd.communicate()[0]
+        cmd.wait()
+
+    return out
diff --git a/hostap/tests/hwsim/utils.py b/hostap/tests/hwsim/utils.py
new file mode 100644
index 0000000..daed84f
--- /dev/null
+++ b/hostap/tests/hwsim/utils.py
@@ -0,0 +1,73 @@
+# Testing utilities
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+
+def get_ifnames():
+    ifnames = []
+    with open("/proc/net/dev", "r") as f:
+        lines = f.readlines()
+        for l in lines:
+            val = l.split(':', 1)
+            if len(val) == 2:
+                ifnames.append(val[0].strip(' '))
+    return ifnames
+
+class HwsimSkip(Exception):
+    def __init__(self, reason):
+        self.reason = reason
+    def __str__(self):
+        return self.reason
+
+class alloc_fail(object):
+    def __init__(self, dev, count, funcs):
+        self._dev = dev
+        self._count = count
+        self._funcs = funcs
+    def __enter__(self):
+        cmd = "TEST_ALLOC_FAIL %d:%s" % (self._count, self._funcs)
+        if "OK" not in self._dev.request(cmd):
+            raise HwsimSkip("TEST_ALLOC_FAIL not supported")
+    def __exit__(self, type, value, traceback):
+        if type is None:
+            if self._dev.request("GET_ALLOC_FAIL") != "0:%s" % self._funcs:
+                raise Exception("Allocation failure did not trigger")
+
+class fail_test(object):
+    def __init__(self, dev, count, funcs):
+        self._dev = dev
+        self._count = count
+        self._funcs = funcs
+    def __enter__(self):
+        cmd = "TEST_FAIL %d:%s" % (self._count, self._funcs)
+        if "OK" not in self._dev.request(cmd):
+            raise HwsimSkip("TEST_FAIL not supported")
+    def __exit__(self, type, value, traceback):
+        if type is None:
+            if self._dev.request("GET_FAIL") != "0:%s" % self._funcs:
+                raise Exception("Test failure did not trigger")
+
+def require_under_vm():
+    with open('/proc/1/cmdline', 'r') as f:
+        cmd = f.read()
+        if "inside.sh" not in cmd:
+            raise HwsimSkip("Not running under VM")
+
+def iface_is_in_bridge(bridge, ifname):
+    fname = "/sys/class/net/"+ifname+"/brport/bridge"
+    if not os.path.exists(fname):
+        return False
+    if not os.path.islink(fname):
+        return False
+    truebridge = os.path.basename(os.readlink(fname))
+    if bridge == truebridge:
+        return True
+    return False
+
+def skip_with_fips(dev, reason="Not supported in FIPS mode"):
+    res = dev.get_capability("fips")
+    if res and 'FIPS' in res:
+        raise HwsimSkip(reason)
diff --git a/hostap/tests/hwsim/vm/README b/hostap/tests/hwsim/vm/README
new file mode 100644
index 0000000..8fef72c
--- /dev/null
+++ b/hostap/tests/hwsim/vm/README
@@ -0,0 +1,66 @@
+These scripts allow you to run the hwsim tests inside a KVM virtual machine.
+
+To set it up, first compile a kernel with the kernel-config file as the
+.config. You can adjust it as needed, the configuration is for a 64-bit
+x86 system and should be close to minimal. The architecture must be the
+same as your host since the host's filesystem is used.
+
+Install the required tools: at least 'kvm', if you want tracing trace-cmd,
+valgrind if you want, etc.
+
+Compile the hwsim tests as per the instructions given, you may have to
+install some extra development packages (e.g. binutils-dev for libbfd).
+
+Create a vm-config file and put the KERNELDIR option into it (see the
+vm-run.sh script). If you want valgrind, also increase the memory size.
+
+Now you can run the vm-run.sh script and it will execute the tests using
+your system's root filesystem (read-only) inside the VM. The options you
+give it are passed through to run-all.sh, see there.
+
+To speed up testing, it is possible to run multiple VMs concurrently and
+split the test cases between all the VMs. If the host system has enough
+memory and CPU resources, this can significantly speed up the full test
+cycle. For example, a 4 core system with 4 GB of RAM can easily run 8
+parallel VMs (assuming valgrind is not used with its higher memory
+requirements). This can be run with:
+
+./parallel-vm.sh <number of VMs> [arguments..]
+
+
+--------------------------------------------------------------------------------
+
+Code Coverage Analysis for user space code
+
+Code coverage for wpa_supplicant and hostapd can be generated from the
+test run with following command line:
+
+./vm-run.sh --codecov [other arguments..]
+
+This builds a separate copies of wpa_supplicant and hostapd into a
+directory that is writable from the virtual machine to collect the gcov
+data. lcov is then used to prepare the reports at the end of the test
+run.
+
+
+Code Coverage Analysis for kernel code
+
+In order to do code coverage analysis, reconfigure the kernel to include
+
+CONFIG_GCOV_KERNEL=y
+CONFIG_GCOV_PROFILE_ALL=y
+
+Note that for gcc 4.7, kernel version 3.13-rc1 or higher is required.
+
+The scripts inside the VM will automatically copy the gcov data out of the
+VM into the logs directory. To post-process this data, you'll want to use
+lcov and run
+
+cd /tmp/hwsim-test-logs/<timestamp>
+lcov -b <path to kernel dir> -c -d gcov/ > gcov/data
+genhtml -o html/ gcov/data
+
+Then open html/index.html in your browser.
+
+Note that in this case you need to keep your build and source directories
+across the test run (otherwise, it's safe to only keep the kernel image.)
diff --git a/hostap/tests/hwsim/vm/build-codecov.sh b/hostap/tests/hwsim/vm/build-codecov.sh
new file mode 100755
index 0000000..e67ef2e
--- /dev/null
+++ b/hostap/tests/hwsim/vm/build-codecov.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+LOGDIR=$1
+DIR=$PWD
+TMPDIR=/tmp/logs
+
+if [ -e $TMPDIR ]; then
+	echo "$TMPDIR exists - cannot prepare build trees"
+	exit 1
+fi
+mkdir $TMPDIR
+echo "Preparing separate build trees for hostapd/wpa_supplicant"
+cd ../../..
+git archive --format=tar --prefix=hostap/ HEAD > $TMPDIR/hostap.tar
+cd $DIR
+cat ../../../wpa_supplicant/.config > $TMPDIR/wpa_supplicant.config
+echo "CONFIG_CODE_COVERAGE=y" >> $TMPDIR/wpa_supplicant.config
+cat ../../../hostapd/.config > $TMPDIR/hostapd.config
+echo "CONFIG_CODE_COVERAGE=y" >> $TMPDIR/hostapd.config
+
+cd $TMPDIR
+tar xf hostap.tar
+mv hostap alt-wpa_supplicant
+mv wpa_supplicant.config alt-wpa_supplicant/wpa_supplicant/.config
+tar xf hostap.tar
+mv hostap alt-hostapd
+cp hostapd.config alt-hostapd/hostapd/.config
+tar xf hostap.tar
+mv hostap alt-hostapd-as
+cp hostapd.config alt-hostapd-as/hostapd/.config
+tar xf hostap.tar
+mv hostap alt-hlr_auc_gw
+mv hostapd.config alt-hlr_auc_gw/hostapd/.config
+rm hostap.tar
+
+cd $TMPDIR/alt-wpa_supplicant/wpa_supplicant
+echo "Building wpa_supplicant"
+make -j8 > /dev/null
+
+cd $TMPDIR/alt-hostapd/hostapd
+echo "Building hostapd"
+make -j8 hostapd hostapd_cli > /dev/null
+
+cd $TMPDIR/alt-hostapd-as/hostapd
+echo "Building hostapd (AS)"
+make -j8 hostapd hostapd_cli > /dev/null
+
+cd $TMPDIR/alt-hlr_auc_gw/hostapd
+echo "Building hlr_auc_gw"
+make -j8 hlr_auc_gw > /dev/null
+
+cd $DIR
+
+mv $TMPDIR/alt-wpa_supplicant $LOGDIR
+mv $TMPDIR/alt-hostapd $LOGDIR
+mv $TMPDIR/alt-hostapd-as $LOGDIR
+mv $TMPDIR/alt-hlr_auc_gw $LOGDIR
diff --git a/hostap/tests/hwsim/vm/combine-codecov.sh b/hostap/tests/hwsim/vm/combine-codecov.sh
new file mode 100755
index 0000000..3fe8443
--- /dev/null
+++ b/hostap/tests/hwsim/vm/combine-codecov.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+LOGDIR=$1
+if [ -n "$2" ]; then
+    ODIR=$2
+else
+    ODIR=.
+fi
+TMPDIR=/tmp/logs
+
+mv $LOGDIR/alt-* $TMPDIR
+
+cd $TMPDIR
+args=""
+for i in lcov-*.info-*; do
+    args="$args -a $i"
+done
+
+lcov $args -o $LOGDIR/combined.info > $LOGDIR/combined-lcov.log 2>&1
+cat $LOGDIR/combined.info |
+    sed "/^TN:$/{N;s/TN:\n\(SF:.*\/bits\/byteswap.h$\)/\1/};/^SF:.*\/bits\/byteswap.h$/,/^end_of_record$/d" |
+    sed "/^TN:$/{N;s/TN:\n\(SF:.*\/common\/wpa_ctrl.c$\)/\1/};/^SF:.*\/common\/wpa_ctrl.c$/,/^end_of_record$/d" |
+    sed "/^TN:$/{N;s/TN:\n\(SF:.*\/utils\/edit.c$\)/\1/};/^SF:.*\/utils\/edit.c$/,/^end_of_record$/d" |
+    sed "/^TN:$/{N;s/TN:\n\(SF:.*_module_tests.c$\)/\1/};/^SF:.*_module_tests.c$/,/^end_of_record$/d" |
+    sed "/^TN:$/{N;s/TN:\n\(SF:.*\/hostapd\/hostapd_cli.c$\)/\1/};/^SF:.*\/hostapd\/hostapd_cli.c$/,/^end_of_record$/d" |
+    sed "/^TN:$/{N;s/TN:\n\(SF:.*wpa_supplicant\/wpa_cli.c$\)/\1/};/^SF:.*wpa_supplicant\/wpa_cli.c$/,/^end_of_record$/d" > $LOGDIR/combined.info.filtered
+
+cd $LOGDIR
+genhtml -t "wpa_supplicant/hostapd combined for hwsim test run $(date +%s)" combined.info.filtered --output-directory $ODIR > lcov.log 2>&1
+
+rm -r /tmp/logs/alt-wpa_supplicant
+rm -r /tmp/logs/alt-hostapd
+rm -r /tmp/logs/alt-hostapd-as
+rm -r /tmp/logs/alt-hlr_auc_gw
+rm /tmp/logs/lcov-*info-*
+rmdir /tmp/logs
diff --git a/hostap/tests/hwsim/vm/dbus.conf b/hostap/tests/hwsim/vm/dbus.conf
new file mode 100644
index 0000000..e64e44f
--- /dev/null
+++ b/hostap/tests/hwsim/vm/dbus.conf
@@ -0,0 +1,37 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+  <type>system</type>
+  <user>messagebus</user>
+  <fork/>
+  <standard_system_servicedirs/>
+  <servicehelper>/usr/lib/dbus-1.0/dbus-daemon-launch-helper</servicehelper>
+  <pidfile>/var/run/dbus/pid</pidfile>
+  <auth>EXTERNAL</auth>
+  <listen>unix:path=/var/run/dbus/system_bus_socket</listen>
+  <policy context="default">
+    <allow user="*"/>
+    <deny own="*"/>
+    <deny send_type="method_call"/>
+    <allow send_type="signal"/>
+    <allow send_requested_reply="true" send_type="method_return"/>
+    <allow send_requested_reply="true" send_type="error"/>
+    <allow receive_type="method_call"/>
+    <allow receive_type="method_return"/>
+    <allow receive_type="error"/>
+    <allow receive_type="signal"/>
+    <allow send_destination="org.freedesktop.DBus"/>
+    <deny send_destination="org.freedesktop.DBus"
+          send_interface="org.freedesktop.DBus"
+          send_member="UpdateActivationEnvironment"/>
+  </policy>
+  <policy user="root">
+    <allow own="fi.epitest.hostap.WPASupplicant"/>
+    <allow send_destination="fi.epitest.hostap.WPASupplicant"/>
+    <allow send_interface="fi.epitest.hostap.WPASupplicant"/>
+    <allow own="fi.w1.wpa_supplicant1"/>
+    <allow send_destination="fi.w1.wpa_supplicant1"/>
+    <allow send_interface="fi.w1.wpa_supplicant1"/>
+    <allow receive_sender="fi.w1.wpa_supplicant1" receive_type="signal"/>
+  </policy>
+</busconfig>
diff --git a/hostap/tests/hwsim/vm/inside.sh b/hostap/tests/hwsim/vm/inside.sh
new file mode 100755
index 0000000..ffab4ee
--- /dev/null
+++ b/hostap/tests/hwsim/vm/inside.sh
@@ -0,0 +1,117 @@
+#!/bin/sh
+
+# mount all kinds of things
+mount tmpfs -t tmpfs /etc
+# we need our own /dev/rfkill, and don't want device access
+mount tmpfs -t tmpfs /dev
+mount tmpfs -t tmpfs /tmp
+# some sockets go into /var/run, and / is read-only
+mount tmpfs -t tmpfs /var/run
+mount proc -t proc /proc
+mount sysfs -t sysfs /sys
+# needed for tracing
+mount debugfs -t debugfs /sys/kernel/debug
+
+# reboot on any sort of crash
+sysctl kernel.panic_on_oops=1
+sysctl kernel.panic=1
+
+# get extra command line variables from /proc/cmdline
+TESTDIR=$(sed 's/.*testdir=\([^ ]*\) .*/\1/' /proc/cmdline)
+TIMEWARP=$(sed 's/.*timewarp=\([^ ]*\) .*/\1/' /proc/cmdline)
+EPATH=$(sed 's/.*EPATH=\([^ ]*\) .*/\1/' /proc/cmdline)
+ARGS=$(sed 's/.*ARGS=//' /proc/cmdline)
+
+# create /dev entries we need
+mknod -m 660 /dev/ttyS0 c 4 64
+mknod -m 660 /dev/random c 1 8
+mknod -m 660 /dev/urandom c 1 9
+mknod -m 666 /dev/null c 1 3
+test -f /sys/class/misc/rfkill/dev && \
+	mknod -m 660 /dev/rfkill c $(cat /sys/class/misc/rfkill/dev | tr ':' ' ')
+ln -s /proc/self/fd/0 /dev/stdin
+ln -s /proc/self/fd/1 /dev/stdout
+ln -s /proc/self/fd/2 /dev/stderr
+
+# create dummy sudo - everything runs as uid 0
+mkdir /tmp/bin
+cat > /tmp/bin/sudo << EOF
+#!/bin/bash
+
+exec "\$@"
+EOF
+chmod +x /tmp/bin/sudo
+# and put it into $PATH, as well as our extra-$PATH
+export PATH=/tmp/bin:$EPATH:$PATH
+
+# some tests assume adm/admin group(s) exist(s)
+cat > /etc/group <<EOF
+adm:x:0:
+admin:x:0:
+messagebus:x:106:
+EOF
+# root should exist
+cat > /etc/passwd <<EOF
+root:x:0:0:root:/tmp:/bin/bash
+messagebus:x:102:106::/var/run/dbus:/bin/false
+EOF
+cat > /etc/ethertypes <<EOF
+IPv4	 	0800  	ip ip4
+ARP		0806	ether-arp
+IPv6		86DD	ip6
+EOF
+cat > /etc/protocols <<EOF
+ip      0       IP
+icmp    1       ICMP
+tcp     6       TCP
+udp     17      UDP
+ipv6-icmp 58	IPv6-ICMP
+EOF
+
+# local network is needed for some tests
+ip link set lo up
+
+# create logs mountpoint and mount the logshare
+mkdir /tmp/logs
+mount -t 9p -o trans=virtio,rw logshare /tmp/logs
+
+if [ "$TIMEWARP" = "1" ] ; then
+    (
+        while sleep 1 ; do
+            date --set "@$(($(date +%s) + 19))"
+        done
+    ) &
+fi
+
+# check if we're rebooting due to a kernel panic ...
+if grep -q 'Kernel panic' /tmp/logs/console ; then
+	echo "KERNEL CRASHED!" >/dev/ttyS0
+else
+	# finally run the tests
+	export USER=0
+	export LOGDIR=/tmp/logs
+	export DBFILE=$LOGDIR/results.db
+	export PREFILL_DB=y
+
+	# some tests need CRDA, install a simple uevent helper
+	# and preload the 00 domain it will have asked for already
+	echo $TESTDIR/vm/uevent.sh > /sys/kernel/uevent_helper
+	COUNTRY=00 crda
+
+	mkdir -p /var/run/dbus
+	touch /var/run/dbus/hwsim-test
+	chown messagebus.messagebus /var/run/dbus
+	dbus-daemon --config-file=$TESTDIR/vm/dbus.conf --fork
+
+	cd $TESTDIR
+	./run-all.sh $ARGS </dev/ttyS0 >/dev/ttyS0 2>&1
+	if test -d /sys/kernel/debug/gcov ; then
+		cp -ar /sys/kernel/debug/gcov /tmp/logs/
+		# these are broken as they're updated while being read ...
+		find /tmp/logs/gcov/ -wholename '*kernel/gcov/*' -print0 | xargs -0 rm
+	fi
+	#bash </dev/ttyS0 >/dev/ttyS0 2>&1
+fi
+
+# and shut down the machine again
+halt -f -p
diff --git a/hostap/tests/hwsim/vm/kernel-config b/hostap/tests/hwsim/vm/kernel-config
new file mode 100644
index 0000000..08fc7a9
--- /dev/null
+++ b/hostap/tests/hwsim/vm/kernel-config
@@ -0,0 +1,1713 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Linux/x86 3.12.0-rc1 Kernel Configuration
+#
+CONFIG_64BIT=y
+CONFIG_X86_64=y
+CONFIG_X86=y
+CONFIG_INSTRUCTION_DECODER=y
+CONFIG_OUTPUT_FORMAT="elf64-x86-64"
+CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig"
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_MMU=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NEED_SG_DMA_LENGTH=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_HAS_CPU_RELAX=y
+CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y
+CONFIG_ARCH_HAS_CPU_AUTOPROBE=y
+CONFIG_HAVE_SETUP_PER_CPU_AREA=y
+CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y
+CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y
+CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
+CONFIG_ZONE_DMA32=y
+CONFIG_AUDIT_ARCH=y
+CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y
+CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y
+CONFIG_X86_64_SMP=y
+CONFIG_X86_HT=y
+CONFIG_ARCH_HWEIGHT_CFLAGS="-fcall-saved-rdi -fcall-saved-rsi -fcall-saved-rdx -fcall-saved-rcx -fcall-saved-r8 -fcall-saved-r9 -fcall-saved-r10 -fcall-saved-r11"
+CONFIG_ARCH_SUPPORTS_UPROBES=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_IRQ_WORK=y
+CONFIG_BUILDTIME_EXTABLE_SORT=y
+
+#
+# General setup
+#
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_CROSS_COMPILE=""
+# CONFIG_COMPILE_TEST is not set
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_BZIP2=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_XZ=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_HAVE_KERNEL_LZ4=y
+# CONFIG_KERNEL_GZIP is not set
+CONFIG_KERNEL_BZIP2=y
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_XZ is not set
+# CONFIG_KERNEL_LZO is not set
+# CONFIG_KERNEL_LZ4 is not set
+CONFIG_DEFAULT_HOSTNAME="(none)"
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_FHANDLE is not set
+# CONFIG_AUDIT is not set
+
+#
+# IRQ subsystem
+#
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_PENDING_IRQ=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_CLOCKSOURCE_WATCHDOG=y
+CONFIG_ARCH_CLOCKSOURCE_DATA=y
+CONFIG_GENERIC_TIME_VSYSCALL=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y
+CONFIG_GENERIC_CMOS_UPDATE=y
+
+#
+# Timers subsystem
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ_COMMON=y
+# CONFIG_HZ_PERIODIC is not set
+CONFIG_NO_HZ_IDLE=y
+# CONFIG_NO_HZ_FULL is not set
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+
+#
+# CPU/Task time and stats accounting
+#
+CONFIG_TICK_CPU_ACCOUNTING=y
+# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set
+# CONFIG_IRQ_TIME_ACCOUNTING is not set
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+
+#
+# RCU Subsystem
+#
+CONFIG_TREE_PREEMPT_RCU=y
+CONFIG_PREEMPT_RCU=y
+CONFIG_RCU_STALL_COMMON=y
+# CONFIG_RCU_USER_QS is not set
+CONFIG_RCU_FANOUT=64
+CONFIG_RCU_FANOUT_LEAF=16
+# CONFIG_RCU_FANOUT_EXACT is not set
+# CONFIG_RCU_FAST_NO_HZ is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_RCU_BOOST is not set
+# CONFIG_RCU_NOCB_CPU is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=21
+CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y
+CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y
+CONFIG_ARCH_WANTS_PROT_NUMA_PROT_NONE=y
+# CONFIG_CGROUPS is not set
+# CONFIG_CHECKPOINT_RESTORE is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set
+# CONFIG_SCHED_AUTOGROUP is not set
+# CONFIG_SYSFS_DEPRECATED is not set
+# CONFIG_RELAY is not set
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+CONFIG_SYSCTL_EXCEPTION_TRACE=y
+CONFIG_HAVE_PCSPKR_PLATFORM=y
+CONFIG_EXPERT=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_PCSPKR_PLATFORM=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_PCI_QUIRKS=y
+CONFIG_EMBEDDED=y
+CONFIG_HAVE_PERF_EVENTS=y
+
+#
+# Kernel Performance Events And Counters
+#
+CONFIG_PERF_EVENTS=y
+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+# CONFIG_COMPAT_BRK is not set
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+CONFIG_SLUB_CPU_PARTIAL=y
+# CONFIG_PROFILING is not set
+CONFIG_TRACEPOINTS=y
+CONFIG_HAVE_OPROFILE=y
+CONFIG_OPROFILE_NMI_TIMER=y
+CONFIG_JUMP_LABEL=y
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
+CONFIG_ARCH_USE_BUILTIN_BSWAP=y
+CONFIG_HAVE_IOREMAP_PROT=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_OPTPROBES=y
+CONFIG_HAVE_KPROBES_ON_FTRACE=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_DMA_ATTRS=y
+CONFIG_USE_GENERIC_SMP_HELPERS=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_HW_BREAKPOINT=y
+CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y
+CONFIG_HAVE_USER_RETURN_NOTIFIER=y
+CONFIG_HAVE_PERF_EVENTS_NMI=y
+CONFIG_HAVE_PERF_REGS=y
+CONFIG_HAVE_PERF_USER_STACK_DUMP=y
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y
+CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y
+CONFIG_HAVE_CMPXCHG_LOCAL=y
+CONFIG_HAVE_CMPXCHG_DOUBLE=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y
+CONFIG_HAVE_ARCH_SOFT_DIRTY=y
+CONFIG_MODULES_USE_ELF_RELA=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
+# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+# CONFIG_MODULES is not set
+CONFIG_BLOCK=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_BSGLIB is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+# CONFIG_CMDLINE_PARSER is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_AIX_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+CONFIG_MAC_PARTITION=y
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+CONFIG_EFI_PARTITION=y
+# CONFIG_SYSV68_PARTITION is not set
+# CONFIG_CMDLINE_PARTITION is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_DEFAULT_NOOP=y
+CONFIG_DEFAULT_IOSCHED="noop"
+CONFIG_UNINLINE_SPIN_UNLOCK=y
+# CONFIG_FREEZER is not set
+
+#
+# Processor type and features
+#
+CONFIG_ZONE_DMA=y
+CONFIG_SMP=y
+CONFIG_X86_MPPARSE=y
+# CONFIG_X86_EXTENDED_PLATFORM is not set
+# CONFIG_X86_INTEL_LPSS is not set
+CONFIG_SCHED_OMIT_FRAME_POINTER=y
+CONFIG_HYPERVISOR_GUEST=y
+CONFIG_PARAVIRT=y
+# CONFIG_PARAVIRT_DEBUG is not set
+CONFIG_PARAVIRT_SPINLOCKS=y
+# CONFIG_XEN is not set
+# CONFIG_XEN_PRIVILEGED_GUEST is not set
+CONFIG_KVM_GUEST=y
+# CONFIG_KVM_DEBUG_FS is not set
+# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set
+CONFIG_PARAVIRT_CLOCK=y
+CONFIG_NO_BOOTMEM=y
+# CONFIG_MEMTEST is not set
+# CONFIG_MK8 is not set
+# CONFIG_MPSC is not set
+CONFIG_MCORE2=y
+# CONFIG_MATOM is not set
+# CONFIG_GENERIC_CPU is not set
+CONFIG_X86_INTERNODE_CACHE_SHIFT=6
+CONFIG_X86_L1_CACHE_SHIFT=6
+CONFIG_X86_INTEL_USERCOPY=y
+CONFIG_X86_USE_PPRO_CHECKSUM=y
+CONFIG_X86_P6_NOP=y
+CONFIG_X86_TSC=y
+CONFIG_X86_CMPXCHG64=y
+CONFIG_X86_CMOV=y
+CONFIG_X86_MINIMUM_CPU_FAMILY=64
+CONFIG_X86_DEBUGCTLMSR=y
+# CONFIG_PROCESSOR_SELECT is not set
+CONFIG_CPU_SUP_INTEL=y
+CONFIG_CPU_SUP_AMD=y
+CONFIG_CPU_SUP_CENTAUR=y
+CONFIG_HPET_TIMER=y
+CONFIG_DMI=y
+CONFIG_GART_IOMMU=y
+# CONFIG_CALGARY_IOMMU is not set
+CONFIG_SWIOTLB=y
+CONFIG_IOMMU_HELPER=y
+# CONFIG_MAXSMP is not set
+CONFIG_NR_CPUS=4
+# CONFIG_SCHED_SMT is not set
+CONFIG_SCHED_MC=y
+# CONFIG_PREEMPT_NONE is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+CONFIG_PREEMPT=y
+CONFIG_PREEMPT_COUNT=y
+CONFIG_X86_LOCAL_APIC=y
+CONFIG_X86_IO_APIC=y
+# CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS is not set
+# CONFIG_X86_MCE is not set
+# CONFIG_I8K is not set
+# CONFIG_MICROCODE is not set
+# CONFIG_MICROCODE_INTEL_EARLY is not set
+# CONFIG_MICROCODE_AMD_EARLY is not set
+# CONFIG_X86_MSR is not set
+# CONFIG_X86_CPUID is not set
+CONFIG_ARCH_PHYS_ADDR_T_64BIT=y
+CONFIG_ARCH_DMA_ADDR_T_64BIT=y
+CONFIG_DIRECT_GBPAGES=y
+# CONFIG_NUMA is not set
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_ARCH_PROC_KCORE_TEXT=y
+CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
+CONFIG_HAVE_MEMORY_PRESENT=y
+CONFIG_SPARSEMEM_EXTREME=y
+CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
+CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER=y
+CONFIG_SPARSEMEM_VMEMMAP=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_HAVE_MEMBLOCK_NODE_MAP=y
+CONFIG_ARCH_DISCARD_MEMBLOCK=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+# CONFIG_MEMORY_HOTPLUG is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=999999
+# CONFIG_COMPACTION is not set
+CONFIG_PHYS_ADDR_T_64BIT=y
+CONFIG_ZONE_DMA_FLAG=1
+# CONFIG_BOUNCE is not set
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_KSM is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=65536
+# CONFIG_TRANSPARENT_HUGEPAGE is not set
+# CONFIG_CROSS_MEMORY_ATTACH is not set
+# CONFIG_CLEANCACHE is not set
+# CONFIG_CMA is not set
+# CONFIG_ZBUD is not set
+# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set
+CONFIG_X86_RESERVE_LOW=64
+CONFIG_MTRR=y
+# CONFIG_MTRR_SANITIZER is not set
+CONFIG_X86_PAT=y
+CONFIG_ARCH_USES_PG_UNCACHED=y
+CONFIG_ARCH_RANDOM=y
+CONFIG_X86_SMAP=y
+# CONFIG_EFI is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_CC_STACKPROTECTOR is not set
+CONFIG_HZ_100=y
+# CONFIG_HZ_250 is not set
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=100
+CONFIG_SCHED_HRTICK=y
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+CONFIG_PHYSICAL_START=0x1000000
+# CONFIG_RELOCATABLE is not set
+CONFIG_PHYSICAL_ALIGN=0x1000000
+# CONFIG_HOTPLUG_CPU is not set
+# CONFIG_CMDLINE_BOOL is not set
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+
+#
+# Power management and ACPI options
+#
+# CONFIG_SUSPEND is not set
+# CONFIG_PM_RUNTIME is not set
+CONFIG_ACPI=y
+# CONFIG_ACPI_PROCFS is not set
+# CONFIG_ACPI_PROCFS_POWER is not set
+# CONFIG_ACPI_EC_DEBUGFS is not set
+# CONFIG_ACPI_AC is not set
+# CONFIG_ACPI_BATTERY is not set
+# CONFIG_ACPI_BUTTON is not set
+# CONFIG_ACPI_FAN is not set
+# CONFIG_ACPI_DOCK is not set
+# CONFIG_ACPI_PROCESSOR is not set
+# CONFIG_ACPI_CUSTOM_DSDT is not set
+CONFIG_ACPI_BLACKLIST_YEAR=0
+# CONFIG_ACPI_DEBUG is not set
+# CONFIG_ACPI_PCI_SLOT is not set
+CONFIG_X86_PM_TIMER=y
+# CONFIG_ACPI_CONTAINER is not set
+# CONFIG_ACPI_SBS is not set
+# CONFIG_ACPI_HED is not set
+# CONFIG_ACPI_CUSTOM_METHOD is not set
+# CONFIG_ACPI_APEI is not set
+# CONFIG_SFI is not set
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# CPU Idle
+#
+CONFIG_CPU_IDLE=y
+# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set
+# CONFIG_INTEL_IDLE is not set
+
+#
+# Memory power savings
+#
+# CONFIG_I7300_IDLE is not set
+
+#
+# Bus options (PCI etc.)
+#
+CONFIG_PCI=y
+CONFIG_PCI_DIRECT=y
+# CONFIG_PCI_MMCONFIG is not set
+CONFIG_PCI_DOMAINS=y
+# CONFIG_PCI_CNB20LE_QUIRK is not set
+# CONFIG_PCIEPORTBUS is not set
+# CONFIG_PCI_MSI is not set
+# CONFIG_PCI_DEBUG is not set
+# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set
+# CONFIG_PCI_STUB is not set
+# CONFIG_HT_IRQ is not set
+# CONFIG_PCI_IOV is not set
+# CONFIG_PCI_PRI is not set
+# CONFIG_PCI_PASID is not set
+# CONFIG_PCI_IOAPIC is not set
+CONFIG_PCI_LABEL=y
+
+#
+# PCI host controller drivers
+#
+# CONFIG_ISA_DMA_API is not set
+CONFIG_AMD_NB=y
+# CONFIG_PCCARD is not set
+# CONFIG_HOTPLUG_PCI is not set
+# CONFIG_RAPIDIO is not set
+# CONFIG_X86_SYSFB is not set
+
+#
+# Executable file formats / Emulations
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
+CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
+CONFIG_BINFMT_SCRIPT=y
+# CONFIG_HAVE_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+CONFIG_COREDUMP=y
+# CONFIG_IA32_EMULATION is not set
+CONFIG_X86_DEV_DMA_OPS=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_DIAG is not set
+CONFIG_UNIX=y
+# CONFIG_UNIX_DIAG is not set
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+# CONFIG_IP_FIB_TRIE_STATS is not set
+CONFIG_IP_MULTIPLE_TABLES=y
+# CONFIG_IP_ROUTE_MULTIPATH is not set
+# CONFIG_IP_ROUTE_VERBOSE is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE_DEMUX is not set
+CONFIG_NET_IP_TUNNEL=y
+# CONFIG_IP_MROUTE is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+# CONFIG_INET6_AH is not set
+# CONFIG_INET6_ESP is not set
+# CONFIG_INET6_IPCOMP is not set
+# CONFIG_IPV6_MIP6 is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL is not set
+CONFIG_INET6_XFRM_MODE_TRANSPORT=y
+CONFIG_INET6_XFRM_MODE_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_BEET=y
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=y
+# CONFIG_IPV6_SIT_6RD is not set
+CONFIG_IPV6_NDISC_NODETYPE=y
+# CONFIG_IPV6_TUNNEL is not set
+# CONFIG_IPV6_GRE is not set
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_IPV6_MROUTE is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_RDS is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_L2TP is not set
+CONFIG_STP=y
+CONFIG_BRIDGE=y
+CONFIG_BRIDGE_IGMP_SNOOPING=y
+CONFIG_HAVE_NET_DSA=y
+CONFIG_VLAN_8021Q=y
+# CONFIG_DECNET is not set
+CONFIG_LLC=y
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+# CONFIG_BATMAN_ADV is not set
+# CONFIG_OPENVSWITCH is not set
+# CONFIG_VSOCKETS is not set
+# CONFIG_NETLINK_MMAP is not set
+# CONFIG_NETLINK_DIAG is not set
+# CONFIG_NET_MPLS_GSO is not set
+CONFIG_RPS=y
+CONFIG_RFS_ACCEL=y
+CONFIG_XPS=y
+CONFIG_NET_RX_BUSY_POLL=y
+CONFIG_BQL=y
+CONFIG_NET_FLOW_LIMIT=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_NET_DROP_MONITOR is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+CONFIG_FIB_RULES=y
+CONFIG_WIRELESS=y
+CONFIG_CFG80211=y
+# CONFIG_NL80211_TESTMODE is not set
+CONFIG_CFG80211_DEVELOPER_WARNINGS=y
+# CONFIG_CFG80211_REG_DEBUG is not set
+# CONFIG_CFG80211_CERTIFICATION_ONUS is not set
+CONFIG_CFG80211_DEFAULT_PS=y
+CONFIG_CFG80211_DEBUGFS=y
+# CONFIG_CFG80211_INTERNAL_REGDB is not set
+CONFIG_CFG80211_WEXT=y
+# CONFIG_LIB80211 is not set
+CONFIG_MAC80211=y
+CONFIG_MAC80211_HAS_RC=y
+# CONFIG_MAC80211_RC_PID is not set
+CONFIG_MAC80211_RC_MINSTREL=y
+CONFIG_MAC80211_RC_MINSTREL_HT=y
+CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y
+CONFIG_MAC80211_RC_DEFAULT="minstrel_ht"
+CONFIG_MAC80211_MESH=y
+CONFIG_MAC80211_DEBUGFS=y
+CONFIG_MAC80211_MESSAGE_TRACING=y
+CONFIG_MAC80211_DEBUG_MENU=y
+CONFIG_MAC80211_NOINLINE=y
+CONFIG_MAC80211_VERBOSE_DEBUG=y
+CONFIG_MAC80211_MLME_DEBUG=y
+CONFIG_MAC80211_STA_DEBUG=y
+CONFIG_MAC80211_HT_DEBUG=y
+CONFIG_MAC80211_IBSS_DEBUG=y
+CONFIG_MAC80211_PS_DEBUG=y
+# CONFIG_MAC80211_MPL_DEBUG is not set
+# CONFIG_MAC80211_MPATH_DEBUG is not set
+# CONFIG_MAC80211_MHWMP_DEBUG is not set
+# CONFIG_MAC80211_MESH_SYNC_DEBUG is not set
+# CONFIG_MAC80211_MESH_CSA_DEBUG is not set
+# CONFIG_MAC80211_MESH_PS_DEBUG is not set
+CONFIG_MAC80211_TDLS_DEBUG=y
+# CONFIG_MAC80211_DEBUG_COUNTERS is not set
+# CONFIG_WIMAX is not set
+CONFIG_RFKILL=y
+# CONFIG_RFKILL_INPUT is not set
+CONFIG_NET_9P=y
+CONFIG_NET_9P_VIRTIO=y
+# CONFIG_NET_9P_DEBUG is not set
+# CONFIG_CAIF is not set
+# CONFIG_CEPH_LIB is not set
+# CONFIG_NFC is not set
+CONFIG_HAVE_BPF_JIT=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_DEVTMPFS is not set
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+CONFIG_FW_LOADER_USER_HELPER=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_GENERIC_CPU_DEVICES is not set
+# CONFIG_DMA_SHARED_BUFFER is not set
+
+#
+# Bus devices
+#
+# CONFIG_CONNECTOR is not set
+# CONFIG_MTD is not set
+# CONFIG_PARPORT is not set
+CONFIG_PNP=y
+# CONFIG_PNP_DEBUG_MESSAGES is not set
+
+#
+# Protocols
+#
+CONFIG_PNPACPI=y
+# CONFIG_BLK_DEV is not set
+
+#
+# Misc devices
+#
+# CONFIG_SENSORS_LIS3LV02D is not set
+# CONFIG_DUMMY_IRQ is not set
+# CONFIG_IBM_ASM is not set
+# CONFIG_PHANTOM is not set
+# CONFIG_SGI_IOC4 is not set
+# CONFIG_TIFM_CORE is not set
+# CONFIG_ATMEL_SSC is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_HP_ILO is not set
+# CONFIG_VMWARE_BALLOON is not set
+# CONFIG_PCH_PHUB is not set
+# CONFIG_SRAM is not set
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_CB710_CORE is not set
+
+#
+# Texas Instruments shared transport line discipline
+#
+
+#
+# Altera FPGA firmware download module
+#
+# CONFIG_VMWARE_VMCI is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI_MOD=y
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+# CONFIG_FUSION is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_FIREWIRE is not set
+# CONFIG_FIREWIRE_NOSY is not set
+# CONFIG_I2O is not set
+# CONFIG_MACINTOSH_DRIVERS is not set
+CONFIG_NETDEVICES=y
+CONFIG_NET_CORE=y
+CONFIG_DUMMY=y
+# CONFIG_ARCNET is not set
+
+#
+# CAIF transport drivers
+#
+
+#
+# Distributed Switch Architecture drivers
+#
+# CONFIG_NET_DSA_MV88E6XXX is not set
+# CONFIG_NET_DSA_MV88E6060 is not set
+# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set
+# CONFIG_NET_DSA_MV88E6131 is not set
+# CONFIG_NET_DSA_MV88E6123_61_65 is not set
+# CONFIG_ETHERNET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_NET_SB1000 is not set
+# CONFIG_PHYLIB is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+CONFIG_WLAN=y
+# CONFIG_LIBERTAS_THINFIRM is not set
+# CONFIG_ATMEL is not set
+# CONFIG_PRISM54 is not set
+# CONFIG_RTL8180 is not set
+# CONFIG_ADM8211 is not set
+CONFIG_MAC80211_HWSIM=y
+# CONFIG_MWL8K is not set
+# CONFIG_ATH_CARDS is not set
+# CONFIG_B43 is not set
+# CONFIG_B43LEGACY is not set
+# CONFIG_BRCMFMAC is not set
+# CONFIG_HOSTAP is not set
+# CONFIG_IPW2100 is not set
+# CONFIG_IWLWIFI is not set
+# CONFIG_IWL4965 is not set
+# CONFIG_IWL3945 is not set
+# CONFIG_LIBERTAS is not set
+# CONFIG_P54_COMMON is not set
+# CONFIG_RT2X00 is not set
+CONFIG_RTL_CARDS=y
+# CONFIG_RTL8192CE is not set
+# CONFIG_RTL8192SE is not set
+# CONFIG_RTL8192DE is not set
+# CONFIG_RTL8723AE is not set
+# CONFIG_RTL8188EE is not set
+# CONFIG_WL_TI is not set
+# CONFIG_MWIFIEX is not set
+# CONFIG_CW1200 is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+# CONFIG_WAN is not set
+# CONFIG_VMXNET3 is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+# CONFIG_INPUT_MATRIXKMAP is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_I8042=y
+CONFIG_SERIO_SERPORT=y
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PCIPS2 is not set
+# CONFIG_SERIO_LIBPS2 is not set
+# CONFIG_SERIO_RAW is not set
+# CONFIG_SERIO_ALTERA_PS2 is not set
+# CONFIG_SERIO_PS2MULT is not set
+# CONFIG_SERIO_ARC_PS2 is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_TTY=y
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_NOZOMI is not set
+# CONFIG_N_GSM is not set
+# CONFIG_TRACE_SINK is not set
+# CONFIG_DEVKMEM is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
+# CONFIG_SERIAL_8250_PNP is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_FIX_EARLYCON_MEM=y
+CONFIG_SERIAL_8250_PCI=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+# CONFIG_SERIAL_8250_DW is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_MFD_HSU is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_JSM is not set
+# CONFIG_SERIAL_SCCNXP is not set
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_PCH_UART is not set
+# CONFIG_SERIAL_ARC is not set
+# CONFIG_SERIAL_RP2 is not set
+# CONFIG_SERIAL_FSL_LPUART is not set
+# CONFIG_SERIAL_ST_ASC is not set
+# CONFIG_TTY_PRINTK is not set
+# CONFIG_VIRTIO_CONSOLE is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+# CONFIG_MWAVE is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_HPET is not set
+# CONFIG_HANGCHECK_TIMER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_TELCLOCK is not set
+CONFIG_DEVPORT=y
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+# CONFIG_HSI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
+
+#
+# PPS generators support
+#
+
+#
+# PTP clock support
+#
+# CONFIG_PTP_1588_CLOCK is not set
+
+#
+# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks.
+#
+# CONFIG_PTP_1588_CLOCK_PCH is not set
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+CONFIG_GPIO_DEVRES=y
+# CONFIG_GPIOLIB is not set
+# CONFIG_W1 is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_TEST_POWER is not set
+# CONFIG_BATTERY_DS2780 is not set
+# CONFIG_BATTERY_DS2781 is not set
+# CONFIG_BATTERY_BQ27x00 is not set
+# CONFIG_CHARGER_MAX8903 is not set
+# CONFIG_POWER_RESET is not set
+# CONFIG_POWER_AVS is not set
+# CONFIG_HWMON is not set
+CONFIG_THERMAL=y
+CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
+# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
+# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
+CONFIG_THERMAL_GOV_STEP_WISE=y
+# CONFIG_THERMAL_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_EMULATION is not set
+# CONFIG_INTEL_POWERCLAMP is not set
+
+#
+# Texas Instruments thermal drivers
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+CONFIG_BCMA_POSSIBLE=y
+
+#
+# Broadcom specific AMBA
+#
+# CONFIG_BCMA is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_CS5535 is not set
+# CONFIG_MFD_CROS_EC is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_LPC_ICH is not set
+# CONFIG_LPC_SCH is not set
+# CONFIG_MFD_JANZ_CMODIO is not set
+# CONFIG_MFD_KEMPLD is not set
+# CONFIG_MFD_RDC321X is not set
+# CONFIG_MFD_RTSX_PCI is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_MFD_SYSCON is not set
+# CONFIG_MFD_TI_AM335X_TSCADC is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_VX855 is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+# CONFIG_AGP is not set
+CONFIG_VGA_ARB=y
+CONFIG_VGA_ARB_MAX_GPUS=16
+# CONFIG_VGA_SWITCHEROO is not set
+# CONFIG_DRM is not set
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_BOOT_VESA_SUPPORT=y
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_ARC is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_VGA16 is not set
+CONFIG_FB_VESA=y
+# CONFIG_FB_N411 is not set
+# CONFIG_FB_HGA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_NVIDIA is not set
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_I740 is not set
+# CONFIG_FB_LE80578 is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_S3 is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_VIA is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VT8623 is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_ARK is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_CARMINE is not set
+# CONFIG_FB_GOLDFISH is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_BROADSHEET is not set
+# CONFIG_FB_AUO_K190X is not set
+# CONFIG_FB_SIMPLE is not set
+# CONFIG_EXYNOS_VIDEO is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Console display driver support
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_VGACON_SOFT_SCROLLBACK=y
+CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+# CONFIG_LOGO is not set
+# CONFIG_SOUND is not set
+
+#
+# HID support
+#
+CONFIG_HID=y
+# CONFIG_HID_BATTERY_STRENGTH is not set
+CONFIG_HIDRAW=y
+# CONFIG_UHID is not set
+CONFIG_HID_GENERIC=y
+
+#
+# Special HID drivers
+#
+# CONFIG_HID_A4TECH is not set
+# CONFIG_HID_ACRUX is not set
+# CONFIG_HID_APPLE is not set
+# CONFIG_HID_AUREAL is not set
+# CONFIG_HID_BELKIN is not set
+# CONFIG_HID_CHERRY is not set
+# CONFIG_HID_CHICONY is not set
+# CONFIG_HID_CYPRESS is not set
+# CONFIG_HID_DRAGONRISE is not set
+# CONFIG_HID_EMS_FF is not set
+# CONFIG_HID_ELECOM is not set
+# CONFIG_HID_EZKEY is not set
+# CONFIG_HID_KEYTOUCH is not set
+# CONFIG_HID_KYE is not set
+# CONFIG_HID_UCLOGIC is not set
+# CONFIG_HID_WALTOP is not set
+# CONFIG_HID_GYRATION is not set
+# CONFIG_HID_ICADE is not set
+# CONFIG_HID_TWINHAN is not set
+# CONFIG_HID_KENSINGTON is not set
+# CONFIG_HID_LCPOWER is not set
+# CONFIG_HID_LOGITECH is not set
+# CONFIG_HID_MAGICMOUSE is not set
+# CONFIG_HID_MICROSOFT is not set
+# CONFIG_HID_MONTEREY is not set
+# CONFIG_HID_MULTITOUCH is not set
+# CONFIG_HID_ORTEK is not set
+# CONFIG_HID_PANTHERLORD is not set
+# CONFIG_HID_PETALYNX is not set
+# CONFIG_HID_PICOLCD is not set
+# CONFIG_HID_PRIMAX is not set
+# CONFIG_HID_SAITEK is not set
+# CONFIG_HID_SAMSUNG is not set
+# CONFIG_HID_SPEEDLINK is not set
+# CONFIG_HID_STEELSERIES is not set
+# CONFIG_HID_SUNPLUS is not set
+# CONFIG_HID_GREENASIA is not set
+# CONFIG_HID_SMARTJOYPLUS is not set
+# CONFIG_HID_TIVO is not set
+# CONFIG_HID_TOPSEED is not set
+# CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_XINMO is not set
+# CONFIG_HID_ZEROPLUS is not set
+# CONFIG_HID_ZYDACRON is not set
+# CONFIG_HID_SENSOR_HUB is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_UWB is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_INFINIBAND is not set
+# CONFIG_EDAC is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_UIO is not set
+CONFIG_VIRT_DRIVERS=y
+CONFIG_VIRTIO=y
+
+#
+# Virtio drivers
+#
+CONFIG_VIRTIO_PCI=y
+# CONFIG_VIRTIO_BALLOON is not set
+# CONFIG_VIRTIO_MMIO is not set
+
+#
+# Microsoft Hyper-V guest support
+#
+# CONFIG_HYPERV is not set
+# CONFIG_STAGING is not set
+# CONFIG_X86_PLATFORM_DEVICES is not set
+
+#
+# Hardware Spinlock drivers
+#
+CONFIG_CLKEVT_I8253=y
+CONFIG_I8253_LOCK=y
+CONFIG_CLKBLD_I8253=y
+# CONFIG_MAILBOX is not set
+# CONFIG_IOMMU_SUPPORT is not set
+
+#
+# Remoteproc drivers
+#
+# CONFIG_STE_MODEM_RPROC is not set
+
+#
+# Rpmsg drivers
+#
+# CONFIG_PM_DEVFREQ is not set
+# CONFIG_EXTCON is not set
+# CONFIG_MEMORY is not set
+# CONFIG_IIO is not set
+# CONFIG_NTB is not set
+# CONFIG_VME_BUS is not set
+# CONFIG_PWM is not set
+# CONFIG_IPACK_BUS is not set
+# CONFIG_RESET_CONTROLLER is not set
+# CONFIG_FMC is not set
+
+#
+# Firmware Drivers
+#
+# CONFIG_EDD is not set
+CONFIG_FIRMWARE_MEMMAP=y
+# CONFIG_DELL_RBU is not set
+# CONFIG_DCDBAS is not set
+# CONFIG_DMIID is not set
+# CONFIG_DMI_SYSFS is not set
+# CONFIG_ISCSI_IBFT_FIND is not set
+# CONFIG_GOOGLE_FIRMWARE is not set
+
+#
+# File systems
+#
+CONFIG_DCACHE_WORD_ACCESS=y
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_FILE_LOCKING=y
+# CONFIG_FSNOTIFY is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+# CONFIG_FANOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_QUOTACTL is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+CONFIG_GENERIC_ACL=y
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_TMPFS_XATTR=y
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_CONFIGFS_FS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+# CONFIG_NFS_FS is not set
+# CONFIG_NFSD is not set
+# CONFIG_CEPH_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+CONFIG_9P_FS=y
+CONFIG_9P_FS_POSIX_ACL=y
+# CONFIG_9P_FS_SECURITY is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_MAC_ROMAN is not set
+# CONFIG_NLS_MAC_CELTIC is not set
+# CONFIG_NLS_MAC_CENTEURO is not set
+# CONFIG_NLS_MAC_CROATIAN is not set
+# CONFIG_NLS_MAC_CYRILLIC is not set
+# CONFIG_NLS_MAC_GAELIC is not set
+# CONFIG_NLS_MAC_GREEK is not set
+# CONFIG_NLS_MAC_ICELAND is not set
+# CONFIG_NLS_MAC_INUIT is not set
+# CONFIG_NLS_MAC_ROMANIAN is not set
+# CONFIG_NLS_MAC_TURKISH is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+
+#
+# printk and dmesg options
+#
+CONFIG_PRINTK_TIME=y
+CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_DYNAMIC_DEBUG is not set
+
+#
+# Compile-time checks and compiler options
+#
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_INFO_REDUCED=y
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+# CONFIG_STRIP_ASM_SYMS is not set
+# CONFIG_READABLE_ASM is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_ARCH_WANT_FRAME_POINTERS=y
+CONFIG_FRAME_POINTER=y
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_KERNEL=y
+
+#
+# Memory Debugging
+#
+CONFIG_DEBUG_PAGEALLOC=y
+CONFIG_WANT_PAGE_DEBUG_FLAGS=y
+CONFIG_PAGE_GUARD=y
+CONFIG_DEBUG_OBJECTS=y
+CONFIG_DEBUG_OBJECTS_SELFTEST=y
+CONFIG_DEBUG_OBJECTS_FREE=y
+CONFIG_DEBUG_OBJECTS_TIMERS=y
+CONFIG_DEBUG_OBJECTS_WORK=y
+CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
+CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
+CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=1
+CONFIG_SLUB_DEBUG_ON=y
+# CONFIG_SLUB_STATS is not set
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=400
+# CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF is not set
+CONFIG_DEBUG_STACK_USAGE=y
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_VIRTUAL is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+CONFIG_HAVE_DEBUG_STACKOVERFLOW=y
+# CONFIG_DEBUG_STACKOVERFLOW is not set
+CONFIG_HAVE_ARCH_KMEMCHECK=y
+# CONFIG_DEBUG_SHIRQ is not set
+
+#
+# Debug Lockups and Hangs
+#
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_HARDLOCKUP_DETECTOR=y
+# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=0
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0
+CONFIG_PANIC_ON_OOPS=y
+CONFIG_PANIC_ON_OOPS_VALUE=1
+CONFIG_SCHED_DEBUG=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_PREEMPT=y
+
+#
+# Lock Debugging (spinlocks, mutexes, etc...)
+#
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_PI_LIST=y
+# CONFIG_RT_MUTEX_TESTER is not set
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set
+CONFIG_DEBUG_LOCK_ALLOC=y
+CONFIG_PROVE_LOCKING=y
+CONFIG_LOCKDEP=y
+CONFIG_LOCK_STAT=y
+CONFIG_DEBUG_LOCKDEP=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+CONFIG_TRACE_IRQFLAGS=y
+CONFIG_STACKTRACE=y
+CONFIG_DEBUG_KOBJECT=y
+CONFIG_DEBUG_KOBJECT_RELEASE=y
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_WRITECOUNT is not set
+CONFIG_DEBUG_LIST=y
+# CONFIG_DEBUG_SG is not set
+CONFIG_DEBUG_NOTIFIERS=y
+# CONFIG_DEBUG_CREDENTIALS is not set
+
+#
+# RCU Debugging
+#
+CONFIG_PROVE_RCU=y
+CONFIG_PROVE_RCU_REPEATEDLY=y
+# CONFIG_PROVE_RCU_DELAY is not set
+CONFIG_SPARSE_RCU_POINTER=y
+# CONFIG_RCU_TORTURE_TEST is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+CONFIG_RCU_CPU_STALL_VERBOSE=y
+# CONFIG_RCU_CPU_STALL_INFO is not set
+# CONFIG_RCU_TRACE is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_NOTIFIER_ERROR_INJECTION is not set
+# CONFIG_FAULT_INJECTION is not set
+CONFIG_LATENCYTOP=y
+CONFIG_ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS=y
+CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=y
+CONFIG_USER_STACKTRACE_SUPPORT=y
+CONFIG_NOP_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST=y
+CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_FENTRY=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_TRACE_CLOCK=y
+CONFIG_RING_BUFFER=y
+CONFIG_EVENT_TRACING=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
+CONFIG_TRACING=y
+CONFIG_GENERIC_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+CONFIG_FTRACE=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_FUNCTION_GRAPH_TRACER=y
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_PREEMPT_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_FTRACE_SYSCALLS is not set
+# CONFIG_TRACER_SNAPSHOT is not set
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+# CONFIG_STACK_TRACER is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_UPROBE_EVENT is not set
+# CONFIG_PROBE_EVENTS is not set
+CONFIG_DYNAMIC_FTRACE=y
+CONFIG_DYNAMIC_FTRACE_WITH_REGS=y
+# CONFIG_FUNCTION_PROFILER is not set
+CONFIG_FTRACE_MCOUNT_RECORD=y
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_MMIOTRACE is not set
+# CONFIG_RING_BUFFER_BENCHMARK is not set
+# CONFIG_RING_BUFFER_STARTUP_TEST is not set
+
+#
+# Runtime Testing
+#
+# CONFIG_LKDTM is not set
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_RBTREE_TEST is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_TEST_STRING_HELPERS is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set
+# CONFIG_DMA_API_DEBUG is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_STRICT_DEVMEM is not set
+# CONFIG_X86_VERBOSE_BOOTUP is not set
+CONFIG_EARLY_PRINTK=y
+# CONFIG_EARLY_PRINTK_DBGP is not set
+# CONFIG_X86_PTDUMP is not set
+# CONFIG_DEBUG_RODATA is not set
+CONFIG_DOUBLEFAULT=y
+# CONFIG_DEBUG_TLBFLUSH is not set
+# CONFIG_IOMMU_DEBUG is not set
+# CONFIG_IOMMU_STRESS is not set
+CONFIG_HAVE_MMIOTRACE_SUPPORT=y
+CONFIG_IO_DELAY_TYPE_0X80=0
+CONFIG_IO_DELAY_TYPE_0XED=1
+CONFIG_IO_DELAY_TYPE_UDELAY=2
+CONFIG_IO_DELAY_TYPE_NONE=3
+CONFIG_IO_DELAY_0X80=y
+# CONFIG_IO_DELAY_0XED is not set
+# CONFIG_IO_DELAY_UDELAY is not set
+# CONFIG_IO_DELAY_NONE is not set
+CONFIG_DEFAULT_IO_DELAY_TYPE=0
+# CONFIG_DEBUG_BOOT_PARAMS is not set
+# CONFIG_CPA_DEBUG is not set
+# CONFIG_OPTIMIZE_INLINING is not set
+# CONFIG_DEBUG_NMI_SELFTEST is not set
+# CONFIG_X86_DEBUG_STATIC_CPU_HAS is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY_DMESG_RESTRICT is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEFAULT_SECURITY=""
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_BLKCIPHER2=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_PCOMP2=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+# CONFIG_CRYPTO_USER is not set
+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_PCRYPT is not set
+CONFIG_CRYPTO_WORKQUEUE=y
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+CONFIG_CRYPTO_CCM=y
+# CONFIG_CRYPTO_GCM is not set
+CONFIG_CRYPTO_SEQIV=y
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+CONFIG_CRYPTO_CTR=y
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_CMAC is not set
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_VMAC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_CRC32C_INTEL is not set
+# CONFIG_CRYPTO_CRC32 is not set
+# CONFIG_CRYPTO_CRC32_PCLMUL is not set
+CONFIG_CRYPTO_CRCT10DIF=y
+# CONFIG_CRYPTO_GHASH is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA1_SSSE3 is not set
+# CONFIG_CRYPTO_SHA256_SSSE3 is not set
+# CONFIG_CRYPTO_SHA512_SSSE3 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+# CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL is not set
+
+#
+# Ciphers
+#
+CONFIG_CRYPTO_AES=y
+# CONFIG_CRYPTO_AES_X86_64 is not set
+# CONFIG_CRYPTO_AES_NI_INTEL is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+CONFIG_CRYPTO_ARC4=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_BLOWFISH_X86_64 is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAMELLIA_X86_64 is not set
+# CONFIG_CRYPTO_CAMELLIA_AESNI_AVX_X86_64 is not set
+# CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64 is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST5_AVX_X86_64 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_CAST6_AVX_X86_64 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SALSA20_X86_64 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_SERPENT_SSE2_X86_64 is not set
+# CONFIG_CRYPTO_SERPENT_AVX_X86_64 is not set
+# CONFIG_CRYPTO_SERPENT_AVX2_X86_64 is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_TWOFISH_X86_64 is not set
+# CONFIG_CRYPTO_TWOFISH_X86_64_3WAY is not set
+# CONFIG_CRYPTO_TWOFISH_AVX_X86_64 is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_ZLIB is not set
+# CONFIG_CRYPTO_LZO is not set
+# CONFIG_CRYPTO_LZ4 is not set
+# CONFIG_CRYPTO_LZ4HC is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_USER_API_HASH is not set
+# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
+# CONFIG_CRYPTO_HW is not set
+CONFIG_HAVE_KVM=y
+# CONFIG_VIRTUALIZATION is not set
+CONFIG_BINARY_PRINTF=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GENERIC_NET_UTILS=y
+CONFIG_GENERIC_FIND_FIRST_BIT=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_IOMAP=y
+CONFIG_GENERIC_IO=y
+CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC32_SELFTEST is not set
+CONFIG_CRC32_SLICEBY8=y
+# CONFIG_CRC32_SLICEBY4 is not set
+# CONFIG_CRC32_SARWATE is not set
+# CONFIG_CRC32_BIT is not set
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+# CONFIG_CRC8 is not set
+# CONFIG_XZ_DEC is not set
+# CONFIG_XZ_DEC_BCJ is not set
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_CPU_RMAP=y
+CONFIG_DQL=y
+CONFIG_NLATTR=y
+CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
+CONFIG_AVERAGE=y
+# CONFIG_CORDIC is not set
+# CONFIG_DDR is not set
+CONFIG_FONT_SUPPORT=y
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
diff --git a/hostap/tests/hwsim/vm/parallel-vm.py b/hostap/tests/hwsim/vm/parallel-vm.py
new file mode 100755
index 0000000..40ab5e3
--- /dev/null
+++ b/hostap/tests/hwsim/vm/parallel-vm.py
@@ -0,0 +1,531 @@
+#!/usr/bin/env python2
+#
+# Parallel VM test case executor
+# Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import curses
+import fcntl
+import logging
+import os
+import subprocess
+import sys
+import time
+
+logger = logging.getLogger()
+
+# Test cases that take significantly longer time to execute than average.
+long_tests = [ "ap_roam_open",
+               "wpas_mesh_password_mismatch_retry",
+               "wpas_mesh_password_mismatch",
+               "hostapd_oom_wpa2_psk_connect",
+               "ap_hs20_fetch_osu_stop",
+               "ap_roam_wpa2_psk",
+               "ibss_wpa_none_ccmp",
+               "nfc_wps_er_handover_pk_hash_mismatch_sta",
+               "go_neg_peers_force_diff_freq",
+               "p2p_cli_invite",
+               "sta_ap_scan_2b",
+               "ap_pmf_sta_unprot_deauth_burst",
+               "ap_bss_add_remove_during_ht_scan",
+               "wext_scan_hidden",
+               "autoscan_exponential",
+               "nfc_p2p_client",
+               "wnm_bss_keep_alive",
+               "ap_inactivity_disconnect",
+               "scan_bss_expiration_age",
+               "autoscan_periodic",
+               "discovery_group_client",
+               "concurrent_p2pcli",
+               "ap_bss_add_remove",
+               "wpas_ap_wps",
+               "wext_pmksa_cache",
+               "ibss_wpa_none",
+               "ap_ht_40mhz_intolerant_ap",
+               "ibss_rsn",
+               "discovery_pd_retries",
+               "ap_wps_setup_locked_timeout",
+               "ap_vht160",
+               "dfs_radar",
+               "dfs",
+               "grpform_cred_ready_timeout",
+               "hostapd_oom_wpa2_eap_connect",
+               "wpas_ap_dfs",
+               "autogo_many",
+               "hostapd_oom_wpa2_eap",
+               "ibss_open",
+               "proxyarp_open_ebtables",
+               "radius_failover",
+               "obss_scan_40_intolerant",
+               "dbus_connect_oom",
+               "proxyarp_open",
+               "ap_wps_iteration",
+               "ap_wps_iteration_error",
+               "ap_wps_pbc_timeout",
+               "ap_wps_http_timeout",
+               "p2p_go_move_reg_change",
+               "p2p_go_move_active",
+               "p2p_go_move_scm",
+               "p2p_go_move_scm_peer_supports",
+               "p2p_go_move_scm_peer_does_not_support",
+               "p2p_go_move_scm_multi" ]
+
+def get_failed(vm):
+    failed = []
+    for i in range(num_servers):
+        failed += vm[i]['failed']
+    return failed
+
+def vm_read_stdout(vm, i):
+    global total_started, total_passed, total_failed, total_skipped
+    global rerun_failures
+
+    ready = False
+    try:
+        out = vm['proc'].stdout.read()
+    except:
+        return False
+    logger.debug("VM[%d] stdout.read[%s]" % (i, out))
+    pending = vm['pending'] + out
+    lines = []
+    while True:
+        pos = pending.find('\n')
+        if pos < 0:
+            break
+        line = pending[0:pos].rstrip()
+        pending = pending[(pos + 1):]
+        logger.debug("VM[%d] stdout full line[%s]" % (i, line))
+        if line.startswith("READY"):
+            ready = True
+        elif line.startswith("PASS"):
+            ready = True
+            total_passed += 1
+        elif line.startswith("FAIL"):
+            ready = True
+            total_failed += 1
+            vals = line.split(' ')
+            if len(vals) < 2:
+                logger.info("VM[%d] incomplete FAIL line: %s" % (i, line))
+                name = line
+            else:
+                name = vals[1]
+            logger.debug("VM[%d] test case failed: %s" % (i, name))
+            vm['failed'].append(name)
+        elif line.startswith("NOT-FOUND"):
+            ready = True
+            total_failed += 1
+            logger.info("VM[%d] test case not found" % i)
+        elif line.startswith("SKIP"):
+            ready = True
+            total_skipped += 1
+        elif line.startswith("START"):
+            total_started += 1
+            if len(vm['failed']) == 0:
+                vals = line.split(' ')
+                if len(vals) >= 2:
+                    vm['fail_seq'].append(vals[1])
+        vm['out'] += line + '\n'
+        lines.append(line)
+    vm['pending'] = pending
+    return ready
+
+def show_progress(scr):
+    global num_servers
+    global vm
+    global dir
+    global timestamp
+    global tests
+    global first_run_failures
+    global total_started, total_passed, total_failed, total_skipped
+
+    total_tests = len(tests)
+    logger.info("Total tests: %d" % total_tests)
+
+    scr.leaveok(1)
+    scr.addstr(0, 0, "Parallel test execution status", curses.A_BOLD)
+    for i in range(0, num_servers):
+        scr.addstr(i + 1, 0, "VM %d:" % (i + 1), curses.A_BOLD)
+        scr.addstr(i + 1, 10, "starting VM")
+    scr.addstr(num_servers + 1, 0, "Total:", curses.A_BOLD)
+    scr.addstr(num_servers + 1, 20, "TOTAL={} STARTED=0 PASS=0 FAIL=0 SKIP=0".format(total_tests))
+    scr.refresh()
+
+    completed_first_pass = False
+    rerun_tests = []
+
+    while True:
+        running = False
+        first_running = False
+        updated = False
+
+        for i in range(0, num_servers):
+            if completed_first_pass:
+                continue
+            if vm[i]['first_run_done']:
+                continue
+            if not vm[i]['proc']:
+                continue
+            if vm[i]['proc'].poll() is not None:
+                vm[i]['proc'] = None
+                scr.move(i + 1, 10)
+                scr.clrtoeol()
+                log = '{}/{}.srv.{}/console'.format(dir, timestamp, i + 1)
+                with open(log, 'r') as f:
+                    if "Kernel panic" in f.read():
+                        scr.addstr("kernel panic")
+                        logger.info("VM[%d] kernel panic" % i)
+                    else:
+                        scr.addstr("unexpected exit")
+                        logger.info("VM[%d] unexpected exit" % i)
+                updated = True
+                continue
+
+            running = True
+            first_running = True
+            try:
+                err = vm[i]['proc'].stderr.read()
+                vm[i]['err'] += err
+                logger.debug("VM[%d] stderr.read[%s]" % (i, err))
+            except:
+                pass
+
+            if vm_read_stdout(vm[i], i):
+                scr.move(i + 1, 10)
+                scr.clrtoeol()
+                updated = True
+                if not tests:
+                    vm[i]['first_run_done'] = True
+                    scr.addstr("completed first round")
+                    logger.info("VM[%d] completed first round" % i)
+                    continue
+                else:
+                    name = tests.pop(0)
+                    vm[i]['proc'].stdin.write(name + '\n')
+                    scr.addstr(name)
+                    logger.debug("VM[%d] start test %s" % (i, name))
+
+        if not first_running and not completed_first_pass:
+            logger.info("First round of testing completed")
+            if tests:
+                logger.info("Unexpected test cases remaining from first round: " + str(tests))
+                raise Exception("Unexpected test cases remaining from first round")
+            completed_first_pass = True
+            for name in get_failed(vm):
+                if rerun_failures:
+                    rerun_tests.append(name)
+                first_run_failures.append(name)
+
+        for i in range(num_servers):
+            if not completed_first_pass:
+                continue
+            if not vm[i]['proc']:
+                continue
+            if vm[i]['proc'].poll() is not None:
+                vm[i]['proc'] = None
+                scr.move(i + 1, 10)
+                scr.clrtoeol()
+                log = '{}/{}.srv.{}/console'.format(dir, timestamp, i + 1)
+                with open(log, 'r') as f:
+                    if "Kernel panic" in f.read():
+                        scr.addstr("kernel panic")
+                        logger.info("VM[%d] kernel panic" % i)
+                    else:
+                        scr.addstr("completed run")
+                        logger.info("VM[%d] completed run" % i)
+                updated = True
+                continue
+
+            running = True
+            try:
+                err = vm[i]['proc'].stderr.read()
+                vm[i]['err'] += err
+                logger.debug("VM[%d] stderr.read[%s]" % (i, err))
+            except:
+                pass
+
+            ready = False
+            if vm[i]['first_run_done']:
+                vm[i]['first_run_done'] = False
+                ready = True
+            else:
+                ready = vm_read_stdout(vm[i], i)
+            if ready:
+                scr.move(i + 1, 10)
+                scr.clrtoeol()
+                updated = True
+                if not rerun_tests:
+                    vm[i]['proc'].stdin.write('\n')
+                    scr.addstr("shutting down")
+                    logger.info("VM[%d] shutting down" % i)
+                else:
+                    name = rerun_tests.pop(0)
+                    vm[i]['proc'].stdin.write(name + '\n')
+                    scr.addstr(name + "(*)")
+                    logger.debug("VM[%d] start test %s (*)" % (i, name))
+
+        if not running:
+            break
+
+        if updated:
+            scr.move(num_servers + 1, 10)
+            scr.clrtoeol()
+            scr.addstr("{} %".format(int(100.0 * (total_passed + total_failed + total_skipped) / total_tests)))
+            scr.addstr(num_servers + 1, 20, "TOTAL={} STARTED={} PASS={} FAIL={} SKIP={}".format(total_tests, total_started, total_passed, total_failed, total_skipped))
+            failed = get_failed(vm)
+            if len(failed) > 0:
+                scr.move(num_servers + 2, 0)
+                scr.clrtoeol()
+                scr.addstr("Failed test cases: ")
+                count = 0
+                for f in failed:
+                    count += 1
+                    if count > 30:
+                        scr.addstr('...')
+                        scr.clrtoeol()
+                        break
+                    scr.addstr(f)
+                    scr.addstr(' ')
+
+            scr.move(0, 35)
+            scr.clrtoeol()
+            if rerun_tests:
+                scr.addstr("(RETRY FAILED %d)" % len(rerun_tests))
+            elif rerun_failures:
+                pass
+            elif first_run_failures:
+                scr.addstr("(RETRY FAILED)")
+
+            scr.refresh()
+
+        time.sleep(0.25)
+
+    scr.refresh()
+    time.sleep(0.3)
+
+def main():
+    import argparse
+    import os
+    global num_servers
+    global vm
+    global dir
+    global timestamp
+    global tests
+    global first_run_failures
+    global total_started, total_passed, total_failed, total_skipped
+    global rerun_failures
+
+    total_started = 0
+    total_passed = 0
+    total_failed = 0
+    total_skipped = 0
+
+    debug_level = logging.INFO
+    rerun_failures = True
+    timestamp = int(time.time())
+
+    scriptsdir = os.path.dirname(os.path.realpath(sys.argv[0]))
+
+    p = argparse.ArgumentParser(description='run multiple testing VMs in parallel')
+    p.add_argument('num_servers', metavar='number of VMs', type=int, choices=range(1, 100),
+                   help="number of VMs to start")
+    p.add_argument('-f', dest='testmodules', metavar='<test module>',
+                   help='execute only tests from these test modules',
+                   type=str, nargs='+')
+    p.add_argument('-1', dest='no_retry', action='store_const', const=True, default=False,
+                   help="don't retry failed tests automatically")
+    p.add_argument('--debug', dest='debug', action='store_const', const=True, default=False,
+                   help="enable debug logging")
+    p.add_argument('--codecov', dest='codecov', action='store_const', const=True, default=False,
+                   help="enable code coverage collection")
+    p.add_argument('--shuffle-tests', dest='shuffle', action='store_const', const=True, default=False,
+                   help="shuffle test cases to randomize order")
+    p.add_argument('--short', dest='short', action='store_const', const=True,
+                   default=False,
+                   help="only run short-duration test cases")
+    p.add_argument('--long', dest='long', action='store_const', const=True,
+                   default=False,
+                   help="include long-duration test cases")
+    p.add_argument('--valgrind', dest='valgrind', action='store_const',
+                   const=True, default=False,
+                   help="run tests under valgrind")
+    p.add_argument('params', nargs='*')
+    args = p.parse_args()
+    num_servers = args.num_servers
+    rerun_failures = not args.no_retry
+    if args.debug:
+        debug_level = logging.DEBUG
+    extra_args = []
+    if args.valgrind:
+        extra_args += [ '--valgrind' ]
+    if args.long:
+        extra_args += [ '--long' ]
+    if args.codecov:
+        print "Code coverage - build separate binaries"
+        logdir = "/tmp/hwsim-test-logs/" + str(timestamp)
+        os.makedirs(logdir)
+        subprocess.check_call([os.path.join(scriptsdir, 'build-codecov.sh'),
+                               logdir])
+        codecov_args = ['--codecov_dir', logdir]
+        codecov = True
+    else:
+        codecov_args = []
+        codecov = False
+
+    first_run_failures = []
+    if args.params:
+        tests = args.params
+    else:
+        tests = []
+        cmd = [ os.path.join(os.path.dirname(scriptsdir), 'run-tests.py'),
+                '-L' ]
+        if args.testmodules:
+            cmd += [ "-f" ]
+            cmd += args.testmodules
+        lst = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+        for l in lst.stdout.readlines():
+            name = l.split(' ')[0]
+            tests.append(name)
+    if len(tests) == 0:
+        sys.exit("No test cases selected")
+
+    dir = '/tmp/hwsim-test-logs'
+    try:
+        os.mkdir(dir)
+    except:
+        pass
+
+    if args.shuffle:
+        from random import shuffle
+        shuffle(tests)
+    elif num_servers > 2 and len(tests) > 100:
+        # Move test cases with long duration to the beginning as an
+        # optimization to avoid last part of the test execution running a long
+        # duration test case on a single VM while all other VMs have already
+        # completed their work.
+        for l in long_tests:
+            if l in tests:
+                tests.remove(l)
+                tests.insert(0, l)
+    if args.short:
+        tests = [t for t in tests if t not in long_tests]
+
+    logger.setLevel(debug_level)
+    log_handler = logging.FileHandler('parallel-vm.log')
+    log_handler.setLevel(debug_level)
+    fmt = "%(asctime)s %(levelname)s %(message)s"
+    log_formatter = logging.Formatter(fmt)
+    log_handler.setFormatter(log_formatter)
+    logger.addHandler(log_handler)
+
+    vm = {}
+    for i in range(0, num_servers):
+        print("\rStarting virtual machine {}/{}".format(i + 1, num_servers)),
+        logger.info("Starting virtual machine {}/{}".format(i + 1, num_servers))
+        cmd = [os.path.join(scriptsdir, 'vm-run.sh'), '--delay', str(i),
+               '--timestamp', str(timestamp),
+               '--ext', 'srv.%d' % (i + 1),
+               '-i'] + codecov_args + extra_args
+        vm[i] = {}
+        vm[i]['first_run_done'] = False
+        vm[i]['proc'] = subprocess.Popen(cmd,
+                                         stdin=subprocess.PIPE,
+                                         stdout=subprocess.PIPE,
+                                         stderr=subprocess.PIPE)
+        vm[i]['out'] = ""
+        vm[i]['pending'] = ""
+        vm[i]['err'] = ""
+        vm[i]['failed'] = []
+        vm[i]['fail_seq'] = []
+        for stream in [ vm[i]['proc'].stdout, vm[i]['proc'].stderr ]:
+            fd = stream.fileno()
+            fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+            fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+    print
+
+    curses.wrapper(show_progress)
+
+    with open('{}/{}-parallel.log'.format(dir, timestamp), 'w') as f:
+        for i in range(0, num_servers):
+            f.write('VM {}\n{}\n{}\n'.format(i, vm[i]['out'], vm[i]['err']))
+
+    failed = get_failed(vm)
+
+    if first_run_failures:
+        print "To re-run same failure sequence(s):"
+        for i in range(0, num_servers):
+            if len(vm[i]['failed']) == 0:
+                continue
+            print "./parallel-vm.py -1 1",
+            skip = len(vm[i]['fail_seq'])
+            skip -= min(skip, 30)
+            for t in vm[i]['fail_seq']:
+                if skip > 0:
+                    skip -= 1
+                    continue
+                print t,
+            print
+        print "Failed test cases:"
+        for f in first_run_failures:
+            print f,
+            logger.info("Failed: " + f)
+        print
+    double_failed = []
+    for name in failed:
+        double_failed.append(name)
+    for test in first_run_failures:
+        double_failed.remove(test)
+    if not rerun_failures:
+        pass
+    elif failed and not double_failed:
+        print "All failed cases passed on retry"
+        logger.info("All failed cases passed on retry")
+    elif double_failed:
+        print "Failed even on retry:"
+        for f in double_failed:
+            print f,
+            logger.info("Failed on retry: " + f)
+        print
+    res = "TOTAL={} PASS={} FAIL={} SKIP={}".format(total_started,
+                                                    total_passed,
+                                                    total_failed,
+                                                    total_skipped)
+    print(res)
+    logger.info(res)
+    print "Logs: " + dir + '/' + str(timestamp)
+    logger.info("Logs: " + dir + '/' + str(timestamp))
+
+    for i in range(0, num_servers):
+        if len(vm[i]['pending']) > 0:
+            logger.info("Unprocessed stdout from VM[%d]: '%s'" %
+                        (i, vm[i]['pending']))
+        log = '{}/{}.srv.{}/console'.format(dir, timestamp, i + 1)
+        with open(log, 'r') as f:
+            if "Kernel panic" in f.read():
+                print "Kernel panic in " + log
+                logger.info("Kernel panic in " + log)
+
+    if codecov:
+        print "Code coverage - preparing report"
+        for i in range(num_servers):
+            subprocess.check_call([os.path.join(scriptsdir,
+                                                'process-codecov.sh'),
+                                   logdir + ".srv.%d" % (i + 1),
+                                   str(i)])
+        subprocess.check_call([os.path.join(scriptsdir, 'combine-codecov.sh'),
+                               logdir])
+        print "file://%s/index.html" % logdir
+        logger.info("Code coverage report: file://%s/index.html" % logdir)
+
+    if double_failed or (failed and not rerun_failures):
+        logger.info("Test run complete - failures found")
+        sys.exit(2)
+    if failed:
+        logger.info("Test run complete - failures found on first run; passed on retry")
+        sys.exit(1)
+    logger.info("Test run complete - no failures")
+    sys.exit(0)
+
+if __name__ == "__main__":
+    main()
diff --git a/hostap/tests/hwsim/vm/parallel-vm.sh b/hostap/tests/hwsim/vm/parallel-vm.sh
new file mode 100755
index 0000000..b2fd078
--- /dev/null
+++ b/hostap/tests/hwsim/vm/parallel-vm.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+cd "$(dirname $0)"
+
+NUM=$1
+if [ -z "$NUM" ]; then
+    echo "usage: $0 <num servers> [params..]"
+    exit 1
+fi
+shift
+
+LOGS=/tmp/hwsim-test-logs
+mkdir -p $LOGS
+DATE=$(date +%s)
+
+for i in `seq 1 $NUM`; do
+    printf "\rStarting virtual machine $i/$NUM"
+    ./vm-run.sh --timestamp $DATE --ext srv.$i --split $i/$NUM $* >> $LOGS/parallel-$DATE.srv.$i 2>&1 &
+done
+echo
+
+echo "Waiting for virtual machines to complete testing"
+count=$NUM
+for i in `seq 1 $NUM`; do
+    printf "\r$count VM(s) remaining   "
+    wait -n
+    count=$((count-1))
+done
+printf "\rTesting completed       "
+echo
+
+echo -n "PASS count: "
+grep ^PASS $LOGS/parallel-$DATE.srv.* | wc -l
+cat $LOGS/parallel-$DATE.srv.* | grep FAIL | sort
diff --git a/hostap/tests/hwsim/vm/process-codecov.sh b/hostap/tests/hwsim/vm/process-codecov.sh
new file mode 100755
index 0000000..d932aa2
--- /dev/null
+++ b/hostap/tests/hwsim/vm/process-codecov.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+LOGDIR=$1
+POSTFIX=$2
+RESTORE=$3
+
+DIR=$PWD
+TMPDIR=/tmp/logs
+
+mv $LOGDIR/alt-wpa_supplicant $TMPDIR
+mv $LOGDIR/alt-hostapd $TMPDIR
+mv $LOGDIR/alt-hostapd-as $TMPDIR
+mv $LOGDIR/alt-hlr_auc_gw $TMPDIR
+
+cd $TMPDIR/alt-wpa_supplicant/wpa_supplicant
+lcov -c -d .. 2> lcov.log | sed s%SF:/tmp/logs/alt-[^/]*/%SF:/tmp/logs/alt-wpa_supplicant/% > $TMPDIR/lcov-wpa_supplicant.info-$POSTFIX &
+
+cd $TMPDIR/alt-hostapd/hostapd
+lcov -c -d .. 2> lcov.log | sed s%SF:/tmp/logs/alt-[^/]*/%SF:/tmp/logs/alt-wpa_supplicant/% > $TMPDIR/lcov-hostapd.info-$POSTFIX &
+
+cd $TMPDIR/alt-hostapd-as/hostapd
+lcov -c -d .. 2> lcov.log | sed s%SF:/tmp/logs/alt-[^/]*/%SF:/tmp/logs/alt-wpa_supplicant/% > $TMPDIR/lcov-hostapd-as.info-$POSTFIX &
+
+cd $TMPDIR/alt-hlr_auc_gw/hostapd
+lcov -c -d .. 2> lcov.log | sed s%SF:/tmp/logs/alt-[^/]*/%SF:/tmp/logs/alt-wpa_supplicant/% > $TMPDIR/lcov-hlr_auc_gw.info-$POSTFIX &
+wait
+
+cd $DIR
+if [ "$RESTORE" == "restore" ]; then
+    mv $TMPDIR/alt-* $LOGDIR
+else
+    rm -r $TMPDIR/alt-wpa_supplicant
+    rm -r $TMPDIR/alt-hostapd
+    rm -r $TMPDIR/alt-hostapd-as
+    rm -r $TMPDIR/alt-hlr_auc_gw
+fi
diff --git a/hostap/tests/hwsim/vm/uevent.sh b/hostap/tests/hwsim/vm/uevent.sh
new file mode 100755
index 0000000..d52f7fc
--- /dev/null
+++ b/hostap/tests/hwsim/vm/uevent.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# assume this was a call for CRDA,
+# if not then it won't find a COUNTRY
+# environment variable and exit
+exec crda
diff --git a/hostap/tests/hwsim/vm/vm-run.sh b/hostap/tests/hwsim/vm/vm-run.sh
new file mode 100755
index 0000000..9993043
--- /dev/null
+++ b/hostap/tests/hwsim/vm/vm-run.sh
@@ -0,0 +1,126 @@
+#!/bin/bash
+
+cd "$(dirname $0)"
+
+if [ -z "$TESTDIR" ] ; then
+	TESTDIR=$(pwd)/../
+fi
+
+LOGS=/tmp/hwsim-test-logs
+
+# increase the memory size if you want to run with valgrind, 512 MB works
+MEMORY=192
+
+# Some ubuntu systems (notably 12.04) have issues with this - since the guest
+# mounts as read-only it should be safe to not specify ,readonly. Override in
+# vm-config if needed (see below)
+ROTAG=,readonly
+
+# set this to ttyS0 to see kvm messages (if something doesn't work)
+KVMOUT=ttyS1
+
+# you can set EPATH if you need anything extra in $PATH inside the VM
+#EPATH=/some/dir
+
+# extra KVM arguments, e.g., -s for gdbserver
+#KVMARGS=-s
+
+# number of channels each hwsim device supports
+CHANNELS=1
+
+test -f vm-config && . vm-config
+test -f ~/.wpas-vm-config && . ~/.wpas-vm-config
+
+if [ -z "$KERNEL" ] && [ -z "$KERNELDIR" ] ; then
+	echo "You need to set a KERNEL or KERNELDIR (in the environment or vm-config)"
+	exit 2
+fi
+if [ -z "$KERNEL" ] ; then
+	KERNEL=$KERNELDIR/arch/x86_64/boot/bzImage
+fi
+
+
+CMD=$TESTDIR/vm/inside.sh
+
+unset RUN_TEST_ARGS
+TIMESTAMP=$(date +%s)
+DATE=$TIMESTAMP
+CODECOV=no
+TIMEWARP=0
+DELAY=0
+CODECOV_DIR=
+while [ "$1" != "" ]; do
+	case $1 in
+		--timestamp ) shift
+			TIMESTAMP=$1
+			shift
+			;;
+		--ext ) shift
+			DATE=$TIMESTAMP.$1
+			shift
+			;;
+		--codecov ) shift
+			CODECOV=yes
+			;;
+		--codecov_dir ) shift
+			CODECOV_DIR=$1
+			shift
+			;;
+		--timewrap ) shift
+			TIMEWARP=1
+			;;
+	        --delay ) shift
+			DELAY=$1
+			shift
+			;;
+		* )
+			RUN_TEST_ARGS="$RUN_TEST_ARGS$1 "
+			shift
+			;;
+	esac
+done
+
+LOGDIR=$LOGS/$DATE
+mkdir -p $LOGDIR
+
+if [ -n "$CODECOV_DIR" ]; then
+    cp -a $CODECOV_DIR/alt-wpa_supplicant $LOGDIR
+    cp -a $CODECOV_DIR/alt-hostapd $LOGDIR
+    cp -a $CODECOV_DIR/alt-hostapd-as $LOGDIR
+    cp -a $CODECOV_DIR/alt-hlr_auc_gw $LOGDIR
+elif [ $CODECOV = "yes" ]; then
+    ./build-codecov.sh $LOGDIR || exit 1
+else
+    CODECOV=no
+fi
+
+if [ $DELAY -gt 0 ]; then
+    echo "Wait $DELAY seconds before starting VM"
+    sleep $DELAY
+fi
+
+echo "Starting test run in a virtual machine"
+
+kvm \
+	-kernel $KERNEL -smp 4 \
+	$KVMARGS -m $MEMORY -nographic \
+	-fsdev local,security_model=none,id=fsdev-root,path=/$ROTAG \
+	-device virtio-9p-pci,id=fs-root,fsdev=fsdev-root,mount_tag=/dev/root \
+	-fsdev local,security_model=none,id=fsdev-logs,path="$LOGDIR",writeout=immediate \
+	-device virtio-9p-pci,id=fs-logs,fsdev=fsdev-logs,mount_tag=logshare \
+	-monitor null -serial stdio -serial file:$LOGDIR/console \
+	-append "mac80211_hwsim.support_p2p_device=0 mac80211_hwsim.channels=$CHANNELS mac80211_hwsim.radios=7 init=$CMD testdir=$TESTDIR timewarp=$TIMEWARP console=$KVMOUT root=/dev/root rootflags=trans=virtio,version=9p2000.u ro rootfstype=9p EPATH=$EPATH ARGS=$RUN_TEST_ARGS"
+
+if [ $CODECOV = "yes" ]; then
+    echo "Preparing code coverage reports"
+    ./process-codecov.sh $LOGDIR "" restore
+    ./combine-codecov.sh $LOGDIR lcov
+fi
+
+echo
+echo "Test run completed"
+echo "Logfiles are at $LOGDIR"
+if [ $CODECOV = "yes" ]; then
+    echo "Code coverage report:"
+    echo "file://$LOGDIR/lcov/index.html"
+fi
diff --git a/hostap/tests/hwsim/w1fi_logo.png b/hostap/tests/hwsim/w1fi_logo.png
new file mode 100644
index 0000000..ac7c259
--- /dev/null
+++ b/hostap/tests/hwsim/w1fi_logo.png
Binary files differ
diff --git a/hostap/tests/hwsim/wlantest.py b/hostap/tests/hwsim/wlantest.py
new file mode 100644
index 0000000..5f6b4ac
--- /dev/null
+++ b/hostap/tests/hwsim/wlantest.py
@@ -0,0 +1,161 @@
+# Python class for controlling wlantest
+# Copyright (c) 2013-2014, 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 time
+import subprocess
+import logging
+import wpaspy
+
+logger = logging.getLogger()
+
+class Wlantest:
+    def __init__(self):
+        if os.path.isfile('../../wlantest/wlantest_cli'):
+            self.wlantest_cli = '../../wlantest/wlantest_cli'
+        else:
+            self.wlantest_cli = 'wlantest_cli'
+
+    def flush(self):
+        res = subprocess.check_output([self.wlantest_cli, "flush"])
+        if "FAIL" in res:
+            raise Exception("wlantest_cli flush failed")
+
+    def relog(self):
+        res = subprocess.check_output([self.wlantest_cli, "relog"])
+        if "FAIL" in res:
+            raise Exception("wlantest_cli relog failed")
+
+    def add_passphrase(self, passphrase):
+        res = subprocess.check_output([self.wlantest_cli, "add_passphrase",
+                                       passphrase])
+        if "FAIL" in res:
+            raise Exception("wlantest_cli add_passphrase failed")
+
+    def add_wepkey(self, key):
+        res = subprocess.check_output([self.wlantest_cli, "add_wepkey", key])
+        if "FAIL" in res:
+            raise Exception("wlantest_cli add_key failed")
+
+    def info_bss(self, field, bssid):
+        res = subprocess.check_output([self.wlantest_cli, "info_bss",
+                                       field, bssid])
+        if "FAIL" in res:
+            raise Exception("Could not get BSS info from wlantest for " + bssid)
+        return res
+
+    def get_bss_counter(self, field, bssid):
+        try:
+            res = subprocess.check_output([self.wlantest_cli, "get_bss_counter",
+                                           field, bssid]);
+        except Exception, e:
+            return 0
+        if "FAIL" in res:
+            return 0
+        return int(res)
+
+    def clear_bss_counters(self, bssid):
+        subprocess.call([self.wlantest_cli, "clear_bss_counters", bssid],
+                        stdout=open('/dev/null', 'w'));
+
+    def info_sta(self, field, bssid, addr):
+        res = subprocess.check_output([self.wlantest_cli, "info_sta",
+                                       field, bssid, addr])
+        if "FAIL" in res:
+            raise Exception("Could not get STA info from wlantest for " + addr)
+        return res
+
+    def get_sta_counter(self, field, bssid, addr):
+        res = subprocess.check_output([self.wlantest_cli, "get_sta_counter",
+                                       field, bssid, addr]);
+        if "FAIL" in res:
+            raise Exception("wlantest_cli command failed")
+        return int(res)
+
+    def clear_sta_counters(self, bssid, addr):
+        res = subprocess.check_output([self.wlantest_cli, "clear_sta_counters",
+                                       bssid, addr]);
+        if "FAIL" in res:
+            raise Exception("wlantest_cli command failed")
+
+    def tdls_clear(self, bssid, addr1, addr2):
+        res = subprocess.check_output([self.wlantest_cli, "clear_tdls_counters",
+                                       bssid, addr1, addr2]);
+
+    def get_tdls_counter(self, field, bssid, addr1, addr2):
+        res = subprocess.check_output([self.wlantest_cli, "get_tdls_counter",
+                                       field, bssid, addr1, addr2]);
+        if "FAIL" in res:
+            raise Exception("wlantest_cli command failed")
+        return int(res)
+
+    def require_ap_pmf_mandatory(self, bssid):
+        res = self.info_bss("rsn_capab", bssid)
+        if "MFPR" not in res:
+            raise Exception("AP did not require PMF")
+        if "MFPC" not in res:
+            raise Exception("AP did not enable PMF")
+        res = self.info_bss("key_mgmt", bssid)
+        if "PSK-SHA256" not in res:
+            raise Exception("AP did not enable SHA256-based AKM for PMF")
+
+    def require_ap_pmf_optional(self, bssid):
+        res = self.info_bss("rsn_capab", bssid)
+        if "MFPR" in res:
+            raise Exception("AP required PMF")
+        if "MFPC" not in res:
+            raise Exception("AP did not enable PMF")
+
+    def require_ap_no_pmf(self, bssid):
+        res = self.info_bss("rsn_capab", bssid)
+        if "MFPR" in res:
+            raise Exception("AP required PMF")
+        if "MFPC" in res:
+            raise Exception("AP enabled PMF")
+
+    def require_sta_pmf_mandatory(self, bssid, addr):
+        res = self.info_sta("rsn_capab", bssid, addr)
+        if "MFPR" not in res:
+            raise Exception("STA did not require PMF")
+        if "MFPC" not in res:
+            raise Exception("STA did not enable PMF")
+
+    def require_sta_pmf(self, bssid, addr):
+        res = self.info_sta("rsn_capab", bssid, addr)
+        if "MFPC" not in res:
+            raise Exception("STA did not enable PMF")
+
+    def require_sta_no_pmf(self, bssid, addr):
+        res = self.info_sta("rsn_capab", bssid, addr)
+        if "MFPC" in res:
+            raise Exception("STA enabled PMF")
+
+    def require_sta_key_mgmt(self, bssid, addr, key_mgmt):
+        res = self.info_sta("key_mgmt", bssid, addr)
+        if key_mgmt not in res:
+            raise Exception("Unexpected STA key_mgmt")
+
+    def get_tx_tid(self, bssid, addr, tid):
+        res = subprocess.check_output([self.wlantest_cli, "get_tx_tid",
+                                       bssid, addr, str(tid)]);
+        if "FAIL" in res:
+            raise Exception("wlantest_cli command failed")
+        return int(res)
+
+    def get_rx_tid(self, bssid, addr, tid):
+        res = subprocess.check_output([self.wlantest_cli, "get_rx_tid",
+                                       bssid, addr, str(tid)]);
+        if "FAIL" in res:
+            raise Exception("wlantest_cli command failed")
+        return int(res)
+
+    def get_tid_counters(self, bssid, addr):
+        tx = {}
+        rx = {}
+        for tid in range(0, 17):
+            tx[tid] = self.get_tx_tid(bssid, addr, tid)
+            rx[tid] = self.get_rx_tid(bssid, addr, tid)
+        return [ tx, rx ]
diff --git a/hostap/tests/hwsim/wpasupplicant.py b/hostap/tests/hwsim/wpasupplicant.py
new file mode 100644
index 0000000..8e79c8b
--- /dev/null
+++ b/hostap/tests/hwsim/wpasupplicant.py
@@ -0,0 +1,1122 @@
+# Python class for controlling wpa_supplicant
+# Copyright (c) 2013-2014, 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 time
+import logging
+import binascii
+import re
+import struct
+import subprocess
+import wpaspy
+
+logger = logging.getLogger()
+wpas_ctrl = '/var/run/wpa_supplicant'
+
+class WpaSupplicant:
+    def __init__(self, ifname=None, global_iface=None):
+        self.group_ifname = None
+        self.gctrl_mon = None
+        if ifname:
+            self.set_ifname(ifname)
+        else:
+            self.ifname = None
+
+        self.global_iface = global_iface
+        if global_iface:
+            self.global_ctrl = wpaspy.Ctrl(global_iface)
+            self.global_mon = wpaspy.Ctrl(global_iface)
+            self.global_mon.attach()
+        else:
+            self.global_mon = None
+
+    def close_ctrl(self):
+        if self.global_mon:
+            self.global_mon.detach()
+            self.global_mon = None
+            self.global_ctrl = None
+        self.remove_ifname()
+
+    def set_ifname(self, ifname):
+        self.ifname = ifname
+        self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
+        self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
+        self.mon.attach()
+
+    def remove_ifname(self):
+        if self.ifname:
+            self.mon.detach()
+            self.mon = None
+            self.ctrl = None
+            self.ifname = None
+
+    def interface_add(self, ifname, config="", driver="nl80211",
+                      drv_params=None, br_ifname=None, create=False,
+                      set_ifname=True, all_params=False):
+        try:
+            groups = subprocess.check_output(["id"])
+            group = "admin" if "(admin)" in groups else "adm"
+        except Exception, e:
+            group = "admin"
+        cmd = "INTERFACE_ADD " + ifname + "\t" + config + "\t" + driver + "\tDIR=/var/run/wpa_supplicant GROUP=" + group
+        if drv_params:
+            cmd = cmd + '\t' + drv_params
+        if br_ifname:
+            if not drv_params:
+                cmd += '\t'
+            cmd += '\t' + br_ifname
+        if create:
+            if not br_ifname:
+                cmd += '\t'
+                if not drv_params:
+                    cmd += '\t'
+            cmd += '\tcreate'
+        if all_params and not create:
+            if not br_ifname:
+                cmd += '\t'
+                if not drv_params:
+                    cmd += '\t'
+            cmd += '\t'
+        if "FAIL" in self.global_request(cmd):
+            raise Exception("Failed to add a dynamic wpa_supplicant interface")
+        if not create and set_ifname:
+            self.set_ifname(ifname)
+
+    def interface_remove(self, ifname):
+        self.remove_ifname()
+        self.global_request("INTERFACE_REMOVE " + ifname)
+
+    def request(self, cmd, timeout=10):
+        logger.debug(self.ifname + ": CTRL: " + cmd)
+        return self.ctrl.request(cmd, timeout=timeout)
+
+    def global_request(self, cmd):
+        if self.global_iface is None:
+            self.request(cmd)
+        else:
+            ifname = self.ifname or self.global_iface
+            logger.debug(ifname + ": CTRL(global): " + cmd)
+            return self.global_ctrl.request(cmd)
+
+    def group_request(self, cmd):
+        if self.group_ifname and self.group_ifname != self.ifname:
+            logger.debug(self.group_ifname + ": CTRL: " + cmd)
+            gctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, self.group_ifname))
+            return gctrl.request(cmd)
+        return self.request(cmd)
+
+    def ping(self):
+        return "PONG" in self.request("PING")
+
+    def global_ping(self):
+        return "PONG" in self.global_request("PING")
+
+    def reset(self):
+        self.dump_monitor()
+        res = self.request("FLUSH")
+        if not "OK" in res:
+            logger.info("FLUSH to " + self.ifname + " failed: " + res)
+        self.global_request("REMOVE_NETWORK all")
+        self.global_request("SET p2p_add_cli_chan 0")
+        self.global_request("SET p2p_no_go_freq ")
+        self.global_request("SET p2p_pref_chan ")
+        self.global_request("SET p2p_no_group_iface 1")
+        self.global_request("SET p2p_go_intent 7")
+        self.global_request("P2P_FLUSH")
+        self.request("SET ignore_old_scan_res 0")
+        if self.gctrl_mon:
+            try:
+                self.gctrl_mon.detach()
+            except:
+                pass
+            self.gctrl_mon = None
+        self.group_ifname = None
+        self.dump_monitor()
+
+        iter = 0
+        while iter < 60:
+            state1 = self.get_driver_status_field("scan_state")
+            p2pdev = "p2p-dev-" + self.ifname
+            state2 = self.get_driver_status_field("scan_state", ifname=p2pdev)
+            states = str(state1) + " " + str(state2)
+            if "SCAN_STARTED" in states or "SCAN_REQUESTED" in states:
+                logger.info(self.ifname + ": Waiting for scan operation to complete before continuing")
+                time.sleep(1)
+            else:
+                break
+            iter = iter + 1
+        if iter == 60:
+            logger.error(self.ifname + ": Driver scan state did not clear")
+            print "Trying to clear cfg80211/mac80211 scan state"
+            try:
+                cmd = ["ifconfig", self.ifname, "down"]
+                subprocess.call(cmd)
+            except subprocess.CalledProcessError, e:
+                logger.info("ifconfig failed: " + str(e.returncode))
+                logger.info(e.output)
+            try:
+                cmd = ["ifconfig", self.ifname, "up"]
+                subprocess.call(cmd)
+            except subprocess.CalledProcessError, e:
+                logger.info("ifconfig failed: " + str(e.returncode))
+                logger.info(e.output)
+        if iter > 0:
+            # The ongoing scan could have discovered BSSes or P2P peers
+            logger.info("Run FLUSH again since scan was in progress")
+            self.request("FLUSH")
+            self.dump_monitor()
+
+        if not self.ping():
+            logger.info("No PING response from " + self.ifname + " after reset")
+
+    def add_network(self):
+        id = self.request("ADD_NETWORK")
+        if "FAIL" in id:
+            raise Exception("ADD_NETWORK failed")
+        return int(id)
+
+    def remove_network(self, id):
+        id = self.request("REMOVE_NETWORK " + str(id))
+        if "FAIL" in id:
+            raise Exception("REMOVE_NETWORK failed")
+        return None
+
+    def get_network(self, id, field):
+        res = self.request("GET_NETWORK " + str(id) + " " + field)
+        if res == "FAIL\n":
+            return None
+        return res
+
+    def set_network(self, id, field, value):
+        res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value)
+        if "FAIL" in res:
+            raise Exception("SET_NETWORK failed")
+        return None
+
+    def set_network_quoted(self, id, field, value):
+        res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"')
+        if "FAIL" in res:
+            raise Exception("SET_NETWORK failed")
+        return None
+
+    def list_networks(self, p2p=False):
+        if p2p:
+            res = self.global_request("LIST_NETWORKS")
+        else:
+            res = self.request("LIST_NETWORKS")
+        lines = res.splitlines()
+        networks = []
+        for l in lines:
+            if "network id" in l:
+                continue
+            [id,ssid,bssid,flags] = l.split('\t')
+            network = {}
+            network['id'] = id
+            network['ssid'] = ssid
+            network['bssid'] = bssid
+            network['flags'] = flags
+            networks.append(network)
+        return networks
+
+    def hs20_enable(self, auto_interworking=False):
+        self.request("SET interworking 1")
+        self.request("SET hs20 1")
+        if auto_interworking:
+            self.request("SET auto_interworking 1")
+        else:
+            self.request("SET auto_interworking 0")
+
+    def interworking_add_network(self, bssid):
+        id = self.request("INTERWORKING_ADD_NETWORK " + bssid)
+        if "FAIL" in id or "OK" in id:
+            raise Exception("INTERWORKING_ADD_NETWORK failed")
+        return int(id)
+
+    def add_cred(self):
+        id = self.request("ADD_CRED")
+        if "FAIL" in id:
+            raise Exception("ADD_CRED failed")
+        return int(id)
+
+    def remove_cred(self, id):
+        id = self.request("REMOVE_CRED " + str(id))
+        if "FAIL" in id:
+            raise Exception("REMOVE_CRED failed")
+        return None
+
+    def set_cred(self, id, field, value):
+        res = self.request("SET_CRED " + str(id) + " " + field + " " + value)
+        if "FAIL" in res:
+            raise Exception("SET_CRED failed")
+        return None
+
+    def set_cred_quoted(self, id, field, value):
+        res = self.request("SET_CRED " + str(id) + " " + field + ' "' + value + '"')
+        if "FAIL" in res:
+            raise Exception("SET_CRED failed")
+        return None
+
+    def get_cred(self, id, field):
+        return self.request("GET_CRED " + str(id) + " " + field)
+
+    def add_cred_values(self, params):
+        id = self.add_cred()
+
+        quoted = [ "realm", "username", "password", "domain", "imsi",
+                   "excluded_ssid", "milenage", "ca_cert", "client_cert",
+                   "private_key", "domain_suffix_match", "provisioning_sp",
+                   "roaming_partner", "phase1", "phase2" ]
+        for field in quoted:
+            if field in params:
+                self.set_cred_quoted(id, field, params[field])
+
+        not_quoted = [ "eap", "roaming_consortium", "priority",
+                       "required_roaming_consortium", "sp_priority",
+                       "max_bss_load", "update_identifier", "req_conn_capab",
+                       "min_dl_bandwidth_home", "min_ul_bandwidth_home",
+                       "min_dl_bandwidth_roaming", "min_ul_bandwidth_roaming" ]
+        for field in not_quoted:
+            if field in params:
+                self.set_cred(id, field, params[field])
+
+        return id;
+
+    def select_network(self, id, freq=None):
+        if freq:
+            extra = " freq=" + str(freq)
+        else:
+            extra = ""
+        id = self.request("SELECT_NETWORK " + str(id) + extra)
+        if "FAIL" in id:
+            raise Exception("SELECT_NETWORK failed")
+        return None
+
+    def mesh_group_add(self, id):
+        id = self.request("MESH_GROUP_ADD " + str(id))
+        if "FAIL" in id:
+            raise Exception("MESH_GROUP_ADD failed")
+        return None
+
+    def mesh_group_remove(self):
+        id = self.request("MESH_GROUP_REMOVE " + str(self.ifname))
+        if "FAIL" in id:
+            raise Exception("MESH_GROUP_REMOVE failed")
+        return None
+
+    def connect_network(self, id, timeout=10):
+        self.dump_monitor()
+        self.select_network(id)
+        self.wait_connected(timeout=timeout)
+        self.dump_monitor()
+
+    def get_status(self, extra=None):
+        if extra:
+            extra = "-" + extra
+        else:
+            extra = ""
+        res = self.request("STATUS" + extra)
+        lines = res.splitlines()
+        vals = dict()
+        for l in lines:
+            try:
+                [name,value] = l.split('=', 1)
+                vals[name] = value
+            except ValueError, e:
+                logger.info(self.ifname + ": Ignore unexpected STATUS line: " + l)
+        return vals
+
+    def get_status_field(self, field, extra=None):
+        vals = self.get_status(extra)
+        if field in vals:
+            return vals[field]
+        return None
+
+    def get_group_status(self, extra=None):
+        if extra:
+            extra = "-" + extra
+        else:
+            extra = ""
+        res = self.group_request("STATUS" + extra)
+        lines = res.splitlines()
+        vals = dict()
+        for l in lines:
+            try:
+                [name,value] = l.split('=', 1)
+            except ValueError:
+                logger.info(self.ifname + ": Ignore unexpected status line: " + l)
+                continue
+            vals[name] = value
+        return vals
+
+    def get_group_status_field(self, field, extra=None):
+        vals = self.get_group_status(extra)
+        if field in vals:
+            return vals[field]
+        return None
+
+    def get_driver_status(self, ifname=None):
+        if ifname is None:
+            res = self.request("STATUS-DRIVER")
+        else:
+            res = self.global_request("IFNAME=%s STATUS-DRIVER" % ifname)
+            if res.startswith("FAIL"):
+                return dict()
+        lines = res.splitlines()
+        vals = dict()
+        for l in lines:
+            try:
+                [name,value] = l.split('=', 1)
+            except ValueError:
+                logger.info(self.ifname + ": Ignore unexpected status-driver line: " + l)
+                continue
+            vals[name] = value
+        return vals
+
+    def get_driver_status_field(self, field, ifname=None):
+        vals = self.get_driver_status(ifname)
+        if field in vals:
+            return vals[field]
+        return None
+
+    def get_mcc(self):
+	mcc = int(self.get_driver_status_field('capa.num_multichan_concurrent'))
+	return 1 if mcc < 2 else mcc
+
+    def get_mib(self):
+        res = self.request("MIB")
+        lines = res.splitlines()
+        vals = dict()
+        for l in lines:
+            try:
+                [name,value] = l.split('=', 1)
+                vals[name] = value
+            except ValueError, e:
+                logger.info(self.ifname + ": Ignore unexpected MIB line: " + l)
+        return vals
+
+    def p2p_dev_addr(self):
+        return self.get_status_field("p2p_device_address")
+
+    def p2p_interface_addr(self):
+        return self.get_group_status_field("address")
+
+    def own_addr(self):
+        try:
+            res = self.p2p_interface_addr()
+        except:
+            res = self.p2p_dev_addr()
+        return res
+
+    def p2p_listen(self):
+        return self.global_request("P2P_LISTEN")
+
+    def p2p_find(self, social=False, progressive=False, dev_id=None,
+                 dev_type=None, delay=None, freq=None):
+        cmd = "P2P_FIND"
+        if social:
+            cmd = cmd + " type=social"
+        elif progressive:
+            cmd = cmd + " type=progressive"
+        if dev_id:
+            cmd = cmd + " dev_id=" + dev_id
+        if dev_type:
+            cmd = cmd + " dev_type=" + dev_type
+        if delay:
+            cmd = cmd + " delay=" + str(delay)
+        if freq:
+            cmd = cmd + " freq=" + str(freq)
+        return self.global_request(cmd)
+
+    def p2p_stop_find(self):
+        return self.global_request("P2P_STOP_FIND")
+
+    def wps_read_pin(self):
+        self.pin = self.request("WPS_PIN get").rstrip("\n")
+        if "FAIL" in self.pin:
+            raise Exception("Could not generate PIN")
+        return self.pin
+
+    def peer_known(self, peer, full=True):
+        res = self.global_request("P2P_PEER " + peer)
+        if peer.lower() not in res.lower():
+            return False
+        if not full:
+            return True
+        return "[PROBE_REQ_ONLY]" not in res
+
+    def discover_peer(self, peer, full=True, timeout=15, social=True, force_find=False):
+        logger.info(self.ifname + ": Trying to discover peer " + peer)
+        if not force_find and self.peer_known(peer, full):
+            return True
+        self.p2p_find(social)
+        count = 0
+        while count < timeout * 4:
+            time.sleep(0.25)
+            count = count + 1
+            if self.peer_known(peer, full):
+                return True
+        return False
+
+    def get_peer(self, peer):
+        res = self.global_request("P2P_PEER " + peer)
+        if peer.lower() not in res.lower():
+            raise Exception("Peer information not available")
+        lines = res.splitlines()
+        vals = dict()
+        for l in lines:
+            if '=' in l:
+                [name,value] = l.split('=', 1)
+                vals[name] = value
+        return vals
+
+    def group_form_result(self, ev, expect_failure=False, go_neg_res=None):
+        if expect_failure:
+            if "P2P-GROUP-STARTED" in ev:
+                raise Exception("Group formation succeeded when expecting failure")
+            exp = r'<.>(P2P-GO-NEG-FAILURE) status=([0-9]*)'
+            s = re.split(exp, ev)
+            if len(s) < 3:
+                return None
+            res = {}
+            res['result'] = 'go-neg-failed'
+            res['status'] = int(s[2])
+            return res
+
+        if "P2P-GROUP-STARTED" not in ev:
+            raise Exception("No P2P-GROUP-STARTED event seen")
+
+        exp = r'<.>(P2P-GROUP-STARTED) ([^ ]*) ([^ ]*) ssid="(.*)" freq=([0-9]*) ((?:psk=.*)|(?:passphrase=".*")) go_dev_addr=([0-9a-f:]*) ip_addr=([0-9.]*) ip_mask=([0-9.]*) go_ip_addr=([0-9.]*)'
+        s = re.split(exp, ev)
+        if len(s) < 11:
+            exp = r'<.>(P2P-GROUP-STARTED) ([^ ]*) ([^ ]*) ssid="(.*)" freq=([0-9]*) ((?:psk=.*)|(?:passphrase=".*")) go_dev_addr=([0-9a-f:]*)'
+            s = re.split(exp, ev)
+            if len(s) < 8:
+                raise Exception("Could not parse P2P-GROUP-STARTED")
+        res = {}
+        res['result'] = 'success'
+        res['ifname'] = s[2]
+        self.group_ifname = s[2]
+        try:
+            self.gctrl_mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, self.group_ifname))
+            self.gctrl_mon.attach()
+        except:
+            logger.debug("Could not open monitor socket for group interface")
+            self.gctrl_mon = None
+        res['role'] = s[3]
+        res['ssid'] = s[4]
+        res['freq'] = s[5]
+        if "[PERSISTENT]" in ev:
+            res['persistent'] = True
+        else:
+            res['persistent'] = False
+        p = re.match(r'psk=([0-9a-f]*)', s[6])
+        if p:
+            res['psk'] = p.group(1)
+        p = re.match(r'passphrase="(.*)"', s[6])
+        if p:
+            res['passphrase'] = p.group(1)
+        res['go_dev_addr'] = s[7]
+
+        if len(s) > 8 and len(s[8]) > 0:
+            res['ip_addr'] = s[8]
+        if len(s) > 9:
+            res['ip_mask'] = s[9]
+        if len(s) > 10:
+            res['go_ip_addr'] = s[10]
+
+        if go_neg_res:
+            exp = r'<.>(P2P-GO-NEG-SUCCESS) role=(GO|client) freq=([0-9]*)'
+            s = re.split(exp, go_neg_res)
+            if len(s) < 4:
+                raise Exception("Could not parse P2P-GO-NEG-SUCCESS")
+            res['go_neg_role'] = s[2]
+            res['go_neg_freq'] = s[3]
+
+        return res
+
+    def p2p_go_neg_auth(self, peer, pin, method, go_intent=None, persistent=False, freq=None):
+        if not self.discover_peer(peer):
+            raise Exception("Peer " + peer + " not found")
+        self.dump_monitor()
+        if pin:
+            cmd = "P2P_CONNECT " + peer + " " + pin + " " + method + " auth"
+        else:
+            cmd = "P2P_CONNECT " + peer + " " + method + " auth"
+        if go_intent:
+            cmd = cmd + ' go_intent=' + str(go_intent)
+        if freq:
+            cmd = cmd + ' freq=' + str(freq)
+        if persistent:
+            cmd = cmd + " persistent"
+        if "OK" in self.global_request(cmd):
+            return None
+        raise Exception("P2P_CONNECT (auth) failed")
+
+    def p2p_go_neg_auth_result(self, timeout=1, expect_failure=False):
+        go_neg_res = None
+        ev = self.wait_global_event(["P2P-GO-NEG-SUCCESS",
+                                     "P2P-GO-NEG-FAILURE"], timeout);
+        if ev is None:
+            if expect_failure:
+                return None
+            raise Exception("Group formation timed out")
+        if "P2P-GO-NEG-SUCCESS" in ev:
+            go_neg_res = ev
+            ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout);
+            if ev is None:
+                if expect_failure:
+                    return None
+                raise Exception("Group formation timed out")
+        self.dump_monitor()
+        return self.group_form_result(ev, expect_failure, go_neg_res)
+
+    def p2p_go_neg_init(self, peer, pin, method, timeout=0, go_intent=None, expect_failure=False, persistent=False, persistent_id=None, freq=None, provdisc=False, wait_group=True):
+        if not self.discover_peer(peer):
+            raise Exception("Peer " + peer + " not found")
+        self.dump_monitor()
+        if pin:
+            cmd = "P2P_CONNECT " + peer + " " + pin + " " + method
+        else:
+            cmd = "P2P_CONNECT " + peer + " " + method
+        if go_intent:
+            cmd = cmd + ' go_intent=' + str(go_intent)
+        if freq:
+            cmd = cmd + ' freq=' + str(freq)
+        if persistent:
+            cmd = cmd + " persistent"
+        elif persistent_id:
+            cmd = cmd + " persistent=" + persistent_id
+        if provdisc:
+            cmd = cmd + " provdisc"
+        if "OK" in self.global_request(cmd):
+            if timeout == 0:
+                self.dump_monitor()
+                return None
+            go_neg_res = None
+            ev = self.wait_global_event(["P2P-GO-NEG-SUCCESS",
+                                         "P2P-GO-NEG-FAILURE"], timeout)
+            if ev is None:
+                if expect_failure:
+                    return None
+                raise Exception("Group formation timed out")
+            if "P2P-GO-NEG-SUCCESS" in ev:
+                if not wait_group:
+                    return ev
+                go_neg_res = ev
+                ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout)
+                if ev is None:
+                    if expect_failure:
+                        return None
+                    raise Exception("Group formation timed out")
+            self.dump_monitor()
+            return self.group_form_result(ev, expect_failure, go_neg_res)
+        raise Exception("P2P_CONNECT failed")
+
+    def wait_event(self, events, timeout=10):
+        start = os.times()[4]
+        while True:
+            while self.mon.pending():
+                ev = self.mon.recv()
+                logger.debug(self.ifname + ": " + ev)
+                for event in events:
+                    if event in ev:
+                        return ev
+            now = os.times()[4]
+            remaining = start + timeout - now
+            if remaining <= 0:
+                break
+            if not self.mon.pending(timeout=remaining):
+                break
+        return None
+
+    def wait_global_event(self, events, timeout):
+        if self.global_iface is None:
+            self.wait_event(events, timeout)
+        else:
+            start = os.times()[4]
+            while True:
+                while self.global_mon.pending():
+                    ev = self.global_mon.recv()
+                    logger.debug(self.ifname + "(global): " + ev)
+                    for event in events:
+                        if event in ev:
+                            return ev
+                now = os.times()[4]
+                remaining = start + timeout - now
+                if remaining <= 0:
+                    break
+                if not self.global_mon.pending(timeout=remaining):
+                    break
+        return None
+
+    def wait_group_event(self, events, timeout=10):
+        if self.group_ifname and self.group_ifname != self.ifname:
+            if self.gctrl_mon is None:
+                return None
+            start = os.times()[4]
+            while True:
+                while self.gctrl_mon.pending():
+                    ev = self.gctrl_mon.recv()
+                    logger.debug(self.group_ifname + ": " + ev)
+                    for event in events:
+                        if event in ev:
+                            return ev
+                now = os.times()[4]
+                remaining = start + timeout - now
+                if remaining <= 0:
+                    break
+                if not self.gctrl_mon.pending(timeout=remaining):
+                    break
+            return None
+
+        return self.wait_event(events, timeout)
+
+    def wait_go_ending_session(self):
+        if self.gctrl_mon:
+            try:
+                self.gctrl_mon.detach()
+            except:
+                pass
+            self.gctrl_mon = None
+        ev = self.wait_global_event(["P2P-GROUP-REMOVED"], timeout=3)
+        if ev is None:
+            raise Exception("Group removal event timed out")
+        if "reason=GO_ENDING_SESSION" not in ev:
+            raise Exception("Unexpected group removal reason")
+
+    def dump_monitor(self):
+        while self.mon.pending():
+            ev = self.mon.recv()
+            logger.debug(self.ifname + ": " + ev)
+        while self.global_mon and self.global_mon.pending():
+            ev = self.global_mon.recv()
+            logger.debug(self.ifname + "(global): " + ev)
+
+    def remove_group(self, ifname=None):
+        if self.gctrl_mon:
+            try:
+                self.gctrl_mon.detach()
+            except:
+                pass
+            self.gctrl_mon = None
+        if ifname is None:
+            ifname = self.group_ifname if self.group_ifname else self.ifname
+        if "OK" not in self.global_request("P2P_GROUP_REMOVE " + ifname):
+            raise Exception("Group could not be removed")
+        self.group_ifname = None
+
+    def p2p_start_go(self, persistent=None, freq=None, no_event_clear=False):
+        self.dump_monitor()
+        cmd = "P2P_GROUP_ADD"
+        if persistent is None:
+            pass
+        elif persistent is True:
+            cmd = cmd + " persistent"
+        else:
+            cmd = cmd + " persistent=" + str(persistent)
+        if freq:
+            cmd = cmd + " freq=" + str(freq)
+        if "OK" in self.global_request(cmd):
+            ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
+            if ev is None:
+                raise Exception("GO start up timed out")
+            if not no_event_clear:
+                self.dump_monitor()
+            return self.group_form_result(ev)
+        raise Exception("P2P_GROUP_ADD failed")
+
+    def p2p_go_authorize_client(self, pin):
+        cmd = "WPS_PIN any " + pin
+        if "FAIL" in self.group_request(cmd):
+            raise Exception("Failed to authorize client connection on GO")
+        return None
+
+    def p2p_go_authorize_client_pbc(self):
+        cmd = "WPS_PBC"
+        if "FAIL" in self.group_request(cmd):
+            raise Exception("Failed to authorize client connection on GO")
+        return None
+
+    def p2p_connect_group(self, go_addr, pin, timeout=0, social=False,
+                          freq=None):
+        self.dump_monitor()
+        if not self.discover_peer(go_addr, social=social):
+            if social or not self.discover_peer(go_addr, social=social):
+                raise Exception("GO " + go_addr + " not found")
+        self.dump_monitor()
+        cmd = "P2P_CONNECT " + go_addr + " " + pin + " join"
+        if freq:
+            cmd += " freq=" + str(freq)
+        if "OK" in self.global_request(cmd):
+            if timeout == 0:
+                self.dump_monitor()
+                return None
+            ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout)
+            if ev is None:
+                raise Exception("Joining the group timed out")
+            self.dump_monitor()
+            return self.group_form_result(ev)
+        raise Exception("P2P_CONNECT(join) failed")
+
+    def tdls_setup(self, peer):
+        cmd = "TDLS_SETUP " + peer
+        if "FAIL" in self.group_request(cmd):
+            raise Exception("Failed to request TDLS setup")
+        return None
+
+    def tdls_teardown(self, peer):
+        cmd = "TDLS_TEARDOWN " + peer
+        if "FAIL" in self.group_request(cmd):
+            raise Exception("Failed to request TDLS teardown")
+        return None
+
+    def tdls_link_status(self, peer):
+        cmd = "TDLS_LINK_STATUS " + peer
+        ret = self.group_request(cmd)
+        if "FAIL" in ret:
+            raise Exception("Failed to request TDLS link status")
+        return ret
+
+    def tspecs(self):
+        """Return (tsid, up) tuples representing current tspecs"""
+        res = self.request("WMM_AC_STATUS")
+        tspecs = re.findall(r"TSID=(\d+) UP=(\d+)", res)
+        tspecs = [tuple(map(int, tspec)) for tspec in tspecs]
+
+        logger.debug("tspecs: " + str(tspecs))
+        return tspecs
+
+    def add_ts(self, tsid, up, direction="downlink", expect_failure=False,
+               extra=None):
+        params = {
+            "sba": 9000,
+            "nominal_msdu_size": 1500,
+            "min_phy_rate": 6000000,
+            "mean_data_rate": 1500,
+        }
+        cmd = "WMM_AC_ADDTS %s tsid=%d up=%d" % (direction, tsid, up)
+        for (key, value) in params.iteritems():
+            cmd += " %s=%d" % (key, value)
+        if extra:
+            cmd += " " + extra
+
+        if self.request(cmd).strip() != "OK":
+            raise Exception("ADDTS failed (tsid=%d up=%d)" % (tsid, up))
+
+        if expect_failure:
+            ev = self.wait_event(["TSPEC-REQ-FAILED"], timeout=2)
+            if ev is None:
+                raise Exception("ADDTS failed (time out while waiting failure)")
+            if "tsid=%d" % (tsid) not in ev:
+                raise Exception("ADDTS failed (invalid tsid in TSPEC-REQ-FAILED")
+            return
+
+        ev = self.wait_event(["TSPEC-ADDED"], timeout=1)
+        if ev is None:
+            raise Exception("ADDTS failed (time out)")
+        if "tsid=%d" % (tsid) not in ev:
+            raise Exception("ADDTS failed (invalid tsid in TSPEC-ADDED)")
+
+        if not (tsid, up) in self.tspecs():
+            raise Exception("ADDTS failed (tsid not in tspec list)")
+
+    def del_ts(self, tsid):
+        if self.request("WMM_AC_DELTS %d" % (tsid)).strip() != "OK":
+            raise Exception("DELTS failed")
+
+        ev = self.wait_event(["TSPEC-REMOVED"], timeout=1)
+        if ev is None:
+            raise Exception("DELTS failed (time out)")
+        if "tsid=%d" % (tsid) not in ev:
+            raise Exception("DELTS failed (invalid tsid in TSPEC-REMOVED)")
+
+        tspecs = [(t, u) for (t, u) in self.tspecs() if t == tsid]
+        if tspecs:
+            raise Exception("DELTS failed (still in tspec list)")
+
+    def connect(self, ssid=None, ssid2=None, **kwargs):
+        logger.info("Connect STA " + self.ifname + " to AP")
+        id = self.add_network()
+        if ssid:
+            self.set_network_quoted(id, "ssid", ssid)
+        elif ssid2:
+            self.set_network(id, "ssid", ssid2)
+
+        quoted = [ "psk", "identity", "anonymous_identity", "password",
+                   "ca_cert", "client_cert", "private_key",
+                   "private_key_passwd", "ca_cert2", "client_cert2",
+                   "private_key2", "phase1", "phase2", "domain_suffix_match",
+                   "altsubject_match", "subject_match", "pac_file", "dh_file",
+                   "bgscan", "ht_mcs", "id_str", "openssl_ciphers",
+                   "domain_match" ]
+        for field in quoted:
+            if field in kwargs and kwargs[field]:
+                self.set_network_quoted(id, field, kwargs[field])
+
+        not_quoted = [ "proto", "key_mgmt", "ieee80211w", "pairwise",
+                       "group", "wep_key0", "wep_key1", "wep_key2", "wep_key3",
+                       "wep_tx_keyidx", "scan_freq", "freq_list", "eap",
+                       "eapol_flags", "fragment_size", "scan_ssid", "auth_alg",
+                       "wpa_ptk_rekey", "disable_ht", "disable_vht", "bssid",
+                       "disable_max_amsdu", "ampdu_factor", "ampdu_density",
+                       "disable_ht40", "disable_sgi", "disable_ldpc",
+                       "ht40_intolerant", "update_identifier", "mac_addr",
+                       "erp", "bg_scan_period", "bssid_blacklist",
+                       "bssid_whitelist", "mem_only_psk", "eap_workaround" ]
+        for field in not_quoted:
+            if field in kwargs and kwargs[field]:
+                self.set_network(id, field, kwargs[field])
+
+        if "raw_psk" in kwargs and kwargs['raw_psk']:
+            self.set_network(id, "psk", kwargs['raw_psk'])
+        if "password_hex" in kwargs and kwargs['password_hex']:
+            self.set_network(id, "password", kwargs['password_hex'])
+        if "peerkey" in kwargs and kwargs['peerkey']:
+            self.set_network(id, "peerkey", "1")
+        if "okc" in kwargs and kwargs['okc']:
+            self.set_network(id, "proactive_key_caching", "1")
+        if "ocsp" in kwargs and kwargs['ocsp']:
+            self.set_network(id, "ocsp", str(kwargs['ocsp']))
+        if "only_add_network" in kwargs and kwargs['only_add_network']:
+            return id
+        if "wait_connect" not in kwargs or kwargs['wait_connect']:
+            if "eap" in kwargs:
+                self.connect_network(id, timeout=20)
+            else:
+                self.connect_network(id)
+        else:
+            self.dump_monitor()
+            self.select_network(id)
+        return id
+
+    def scan(self, type=None, freq=None, no_wait=False, only_new=False):
+        if type:
+            cmd = "SCAN TYPE=" + type
+        else:
+            cmd = "SCAN"
+        if freq:
+            cmd = cmd + " freq=" + str(freq)
+        if only_new:
+            cmd += " only_new=1"
+        if not no_wait:
+            self.dump_monitor()
+        if not "OK" in self.request(cmd):
+            raise Exception("Failed to trigger scan")
+        if no_wait:
+            return
+        ev = self.wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+        if ev is None:
+            raise Exception("Scan timed out")
+
+    def scan_for_bss(self, bssid, freq=None, force_scan=False, only_new=False):
+        if not force_scan and self.get_bss(bssid) is not None:
+            return
+        for i in range(0, 10):
+            self.scan(freq=freq, type="ONLY", only_new=only_new)
+            if self.get_bss(bssid) is not None:
+                return
+        raise Exception("Could not find BSS " + bssid + " in scan")
+
+    def flush_scan_cache(self, freq=2417):
+        self.request("BSS_FLUSH 0")
+        self.scan(freq=freq, only_new=True)
+        res = self.request("SCAN_RESULTS")
+        if len(res.splitlines()) > 1:
+            self.request("BSS_FLUSH 0")
+            self.scan(freq=2422, only_new=True)
+            res = self.request("SCAN_RESULTS")
+            if len(res.splitlines()) > 1:
+                logger.info("flush_scan_cache: Could not clear all BSS entries. These remain:\n" + res)
+
+    def roam(self, bssid, fail_test=False):
+        self.dump_monitor()
+        if "OK" not in self.request("ROAM " + bssid):
+            raise Exception("ROAM failed")
+        if fail_test:
+            ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+            if ev is not None:
+                raise Exception("Unexpected connection")
+            self.dump_monitor()
+            return
+        self.wait_connected(timeout=10, error="Roaming with the AP timed out")
+        self.dump_monitor()
+
+    def roam_over_ds(self, bssid, fail_test=False):
+        self.dump_monitor()
+        if "OK" not in self.request("FT_DS " + bssid):
+            raise Exception("FT_DS failed")
+        if fail_test:
+            ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+            if ev is not None:
+                raise Exception("Unexpected connection")
+            self.dump_monitor()
+            return
+        self.wait_connected(timeout=10, error="Roaming with the AP timed out")
+        self.dump_monitor()
+
+    def wps_reg(self, bssid, pin, new_ssid=None, key_mgmt=None, cipher=None,
+                new_passphrase=None, no_wait=False):
+        self.dump_monitor()
+        if new_ssid:
+            self.request("WPS_REG " + bssid + " " + pin + " " +
+                         new_ssid.encode("hex") + " " + key_mgmt + " " +
+                         cipher + " " + new_passphrase.encode("hex"))
+            if no_wait:
+                return
+            ev = self.wait_event(["WPS-SUCCESS"], timeout=15)
+        else:
+            self.request("WPS_REG " + bssid + " " + pin)
+            if no_wait:
+                return
+            ev = self.wait_event(["WPS-CRED-RECEIVED"], timeout=15)
+            if ev is None:
+                raise Exception("WPS cred timed out")
+            ev = self.wait_event(["WPS-FAIL"], timeout=15)
+        if ev is None:
+            raise Exception("WPS timed out")
+        self.wait_connected(timeout=15)
+
+    def relog(self):
+        self.global_request("RELOG")
+
+    def wait_completed(self, timeout=10):
+        for i in range(0, timeout * 2):
+            if self.get_status_field("wpa_state") == "COMPLETED":
+                return
+            time.sleep(0.5)
+        raise Exception("Timeout while waiting for COMPLETED state")
+
+    def get_capability(self, field):
+        res = self.request("GET_CAPABILITY " + field)
+        if "FAIL" in res:
+            return None
+        return res.split(' ')
+
+    def get_bss(self, bssid, ifname=None):
+	if not ifname or ifname == self.ifname:
+            res = self.request("BSS " + bssid)
+        elif ifname == self.group_ifname:
+            res = self.group_request("BSS " + bssid)
+        else:
+            return None
+
+        if "FAIL" in res:
+            return None
+        lines = res.splitlines()
+        vals = dict()
+        for l in lines:
+            [name,value] = l.split('=', 1)
+            vals[name] = value
+        if len(vals) == 0:
+            return None
+        return vals
+
+    def get_pmksa(self, bssid):
+        res = self.request("PMKSA")
+        lines = res.splitlines()
+        for l in lines:
+            if bssid not in l:
+                continue
+            vals = dict()
+            [index,aa,pmkid,expiration,opportunistic] = l.split(' ')
+            vals['index'] = index
+            vals['pmkid'] = pmkid
+            vals['expiration'] = expiration
+            vals['opportunistic'] = opportunistic
+            return vals
+        return None
+
+    def get_sta(self, addr, info=None, next=False):
+        cmd = "STA-NEXT " if next else "STA "
+        if addr is None:
+            res = self.request("STA-FIRST")
+        elif info:
+            res = self.request(cmd + addr + " " + info)
+        else:
+            res = self.request(cmd + addr)
+        lines = res.splitlines()
+        vals = dict()
+        first = True
+        for l in lines:
+            if first:
+                vals['addr'] = l
+                first = False
+            else:
+                [name,value] = l.split('=', 1)
+                vals[name] = value
+        return vals
+
+    def mgmt_rx(self, timeout=5):
+        ev = self.wait_event(["MGMT-RX"], timeout=timeout)
+        if ev is None:
+            return None
+        msg = {}
+        items = ev.split(' ')
+        field,val = items[1].split('=')
+        if field != "freq":
+            raise Exception("Unexpected MGMT-RX event format: " + ev)
+        msg['freq'] = val
+        frame = binascii.unhexlify(items[4])
+        msg['frame'] = frame
+
+        hdr = struct.unpack('<HH6B6B6BH', frame[0:24])
+        msg['fc'] = hdr[0]
+        msg['subtype'] = (hdr[0] >> 4) & 0xf
+        hdr = hdr[1:]
+        msg['duration'] = hdr[0]
+        hdr = hdr[1:]
+        msg['da'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
+        hdr = hdr[6:]
+        msg['sa'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
+        hdr = hdr[6:]
+        msg['bssid'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
+        hdr = hdr[6:]
+        msg['seq_ctrl'] = hdr[0]
+        msg['payload'] = frame[24:]
+
+        return msg
+
+    def wait_connected(self, timeout=10, error="Connection timed out"):
+        ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=timeout)
+        if ev is None:
+            raise Exception(error)
+        return ev
+
+    def wait_disconnected(self, timeout=10, error="Disconnection timed out"):
+        ev = self.wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=timeout)
+        if ev is None:
+            raise Exception(error)
+        return ev
+
+    def get_group_ifname(self):
+        return self.group_ifname if self.group_ifname else self.ifname
+
+    def get_config(self):
+        res = self.request("DUMP")
+        if res.startswith("FAIL"):
+            raise Exception("DUMP failed")
+        lines = res.splitlines()
+        vals = dict()
+        for l in lines:
+            [name,value] = l.split('=', 1)
+            vals[name] = value
+        return vals
+
+    def asp_provision(self, peer, adv_id, adv_mac, session_id, session_mac,
+                      method="1000", info="", status=None, cpt=None):
+        if status is None:
+            cmd = "P2P_ASP_PROVISION"
+            params = "info='%s' method=%s" % (info, method)
+        else:
+            cmd = "P2P_ASP_PROVISION_RESP"
+            params = "status=%d" % status
+
+        if cpt is not None:
+            params += " cpt=" + cpt
+
+        if "OK" not in self.global_request("%s %s adv_id=%s adv_mac=%s session=%d session_mac=%s %s" %
+                                           (cmd, peer, adv_id, adv_mac, session_id, session_mac, params)):
+            raise Exception("%s request failed" % cmd)
diff --git a/hostap/tests/hwsim/wps-mixed-cred b/hostap/tests/hwsim/wps-mixed-cred
new file mode 100644
index 0000000..fca2871
--- /dev/null
+++ b/hostap/tests/hwsim/wps-mixed-cred
Binary files differ
diff --git a/hostap/tests/hwsim/wps-wep-cred b/hostap/tests/hwsim/wps-wep-cred
new file mode 100644
index 0000000..407cf41
--- /dev/null
+++ b/hostap/tests/hwsim/wps-wep-cred
Binary files differ
diff --git a/hostap/tests/p2p-fuzzer/Makefile b/hostap/tests/p2p-fuzzer/Makefile
new file mode 100644
index 0000000..4f81ef1
--- /dev/null
+++ b/hostap/tests/p2p-fuzzer/Makefile
@@ -0,0 +1,51 @@
+all: p2p-fuzzer
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+SRC=../../src
+
+CFLAGS += -I$(SRC)
+
+$(SRC)/utils/libutils.a:
+	$(MAKE) -C $(SRC)/utils
+
+$(SRC)/common/libcommon.a:
+	$(MAKE) -C $(SRC)/common
+
+$(SRC)/crypto/libcrypto.a:
+	$(MAKE) -C $(SRC)/crypto
+
+$(SRC)/tls/libtls.a:
+	$(MAKE) -C $(SRC)/tls
+
+$(SRC)/p2p/libp2p.a:
+	$(MAKE) -C $(SRC)/p2p
+
+$(SRC)/wps/libwps.a:
+	$(MAKE) -C $(SRC)/wps
+
+LIBS += $(SRC)/utils/libutils.a
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/p2p/libp2p.a
+LIBS += $(SRC)/tls/libtls.a
+LIBS += $(SRC)/wps/libwps.a
+
+p2p-fuzzer: p2p-fuzzer.o $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $^ $(LIBS)
+
+clean:
+	$(MAKE) -C $(SRC) clean
+	rm -f p2p-fuzzer *~ *.o *.d
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/tests/p2p-fuzzer/go-neg-req.dat b/hostap/tests/p2p-fuzzer/go-neg-req.dat
new file mode 100644
index 0000000..ed06834
--- /dev/null
+++ b/hostap/tests/p2p-fuzzer/go-neg-req.dat
Binary files differ
diff --git a/hostap/tests/p2p-fuzzer/invitation-req.dat b/hostap/tests/p2p-fuzzer/invitation-req.dat
new file mode 100644
index 0000000..5991f3e
--- /dev/null
+++ b/hostap/tests/p2p-fuzzer/invitation-req.dat
Binary files differ
diff --git a/hostap/tests/p2p-fuzzer/p2p-fuzzer.c b/hostap/tests/p2p-fuzzer/p2p-fuzzer.c
new file mode 100644
index 0000000..dcc1d72
--- /dev/null
+++ b/hostap/tests/p2p-fuzzer/p2p-fuzzer.c
@@ -0,0 +1,213 @@
+/*
+ * wpa_supplicant - P2P fuzzer
+ * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p/p2p.h"
+
+
+static void debug_print(void *ctx, int level, const char *msg)
+{
+	wpa_printf(level, "P2P: %s", msg);
+}
+
+
+static void find_stopped(void *ctx)
+{
+}
+
+
+static int start_listen(void *ctx, unsigned int freq,
+			unsigned int duration,
+			const struct wpabuf *probe_resp_ie)
+{
+	return 0;
+}
+
+
+static void stop_listen(void *ctx)
+{
+}
+
+
+static void dev_found(void *ctx, const u8 *addr,
+		      const struct p2p_peer_info *info,
+		      int new_device)
+{
+}
+
+
+static void dev_lost(void *ctx, const u8 *dev_addr)
+{
+}
+
+
+static int 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)
+{
+	return 0;
+}
+
+
+static void send_action_done(void *ctx)
+{
+}
+
+
+static void go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
+{
+}
+
+
+static struct p2p_data * init_p2p(void)
+{
+	struct p2p_config p2p;
+
+	os_memset(&p2p, 0, sizeof(p2p));
+	p2p.max_peers = 100;
+	p2p.passphrase_len = 8;
+	p2p.channels.reg_classes = 1;
+	p2p.channels.reg_class[0].reg_class = 81;
+	p2p.channels.reg_class[0].channel[0] = 1;
+	p2p.channels.reg_class[0].channel[1] = 2;
+	p2p.channels.reg_class[0].channels = 2;
+	p2p.debug_print = debug_print;
+	p2p.find_stopped = find_stopped;
+	p2p.start_listen = start_listen;
+	p2p.stop_listen = stop_listen;
+	p2p.dev_found = dev_found;
+	p2p.dev_lost = dev_lost;
+	p2p.send_action = send_action;
+	p2p.send_action_done = send_action_done;
+	p2p.go_neg_req_rx = go_neg_req_rx;
+
+	return p2p_init(&p2p);
+}
+
+
+struct arg_ctx {
+	struct p2p_data *p2p;
+	const char *fname;
+};
+
+
+static void test_send_proberesp(void *eloop_data, void *user_ctx)
+{
+	struct arg_ctx *ctx = eloop_data;
+	char *data;
+	size_t len;
+	struct os_reltime rx_time;
+
+	wpa_printf(MSG_INFO, "p2p-fuzzer: Send proberesp '%s'", ctx->fname);
+
+	data = os_readfile(ctx->fname, &len);
+	if (!data) {
+		wpa_printf(MSG_ERROR, "Could not read '%s'", ctx->fname);
+		return;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "fuzzer - IEs", data, len);
+
+	os_memset(&rx_time, 0, sizeof(rx_time));
+	p2p_scan_res_handler(ctx->p2p, (u8 *) "\x02\x00\x00\x00\x01\x00", 2412,
+			     &rx_time, 0, (u8 *) data, len);
+	p2p_scan_res_handled(ctx->p2p);
+
+	os_free(data);
+	eloop_terminate();
+}
+
+
+static void test_send_action(void *eloop_data, void *user_ctx)
+{
+	struct arg_ctx *ctx = eloop_data;
+	char *data;
+	size_t len;
+	struct os_reltime rx_time;
+	struct ieee80211_mgmt *mgmt;
+
+	wpa_printf(MSG_INFO, "p2p-fuzzer: Send action '%s'", ctx->fname);
+
+	data = os_readfile(ctx->fname, &len);
+	if (!data) {
+		wpa_printf(MSG_ERROR, "Could not read '%s'", ctx->fname);
+		return;
+	}
+	if (len < IEEE80211_HDRLEN + 1)
+		goto out;
+
+	wpa_hexdump(MSG_MSGDUMP, "fuzzer - action", data, len);
+
+	mgmt = (struct ieee80211_mgmt *) data;
+	os_memset(&rx_time, 0, sizeof(rx_time));
+	p2p_rx_action(ctx->p2p, mgmt->da, mgmt->sa, mgmt->bssid,
+		      mgmt->u.action.category,
+		      (u8 *) data + IEEE80211_HDRLEN + 1,
+		      len - IEEE80211_HDRLEN - 1, 2412);
+
+out:
+	os_free(data);
+	eloop_terminate();
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct p2p_data *p2p;
+	struct arg_ctx ctx;
+
+	/* TODO: probreq and wpas_p2p_probe_req_rx() */
+
+	if (argc < 3) {
+		printf("usage: %s <proberesp|action> <file>\n", argv[0]);
+		return -1;
+	}
+
+	if (os_program_init())
+		return -1;
+
+	wpa_debug_level = 0;
+	wpa_debug_show_keys = 1;
+
+	if (eloop_init()) {
+		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+		return -1;
+	}
+
+	p2p = init_p2p();
+	if (!p2p) {
+		wpa_printf(MSG_ERROR, "P2P init failed");
+		return -1;
+	}
+
+	ctx.p2p = p2p;
+	ctx.fname = argv[2];
+
+	if (os_strcmp(argv[1], "proberesp") == 0) {
+		eloop_register_timeout(0, 0, test_send_proberesp, &ctx, NULL);
+	} else if (os_strcmp(argv[1], "action") == 0) {
+		eloop_register_timeout(0, 0, test_send_action, &ctx, NULL);
+	} else {
+		wpa_printf(MSG_ERROR, "Unsupported test type '%s'", argv[1]);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "Starting eloop");
+	eloop_run();
+	wpa_printf(MSG_DEBUG, "eloop done");
+
+	p2p_deinit(p2p);
+	eloop_destroy();
+	os_program_deinit();
+
+	return 0;
+}
diff --git a/hostap/tests/p2p-fuzzer/p2ps-pd-req.dat b/hostap/tests/p2p-fuzzer/p2ps-pd-req.dat
new file mode 100644
index 0000000..7e1b6d9
--- /dev/null
+++ b/hostap/tests/p2p-fuzzer/p2ps-pd-req.dat
Binary files differ
diff --git a/hostap/tests/p2p-fuzzer/proberesp-go.dat b/hostap/tests/p2p-fuzzer/proberesp-go.dat
new file mode 100644
index 0000000..8541652
--- /dev/null
+++ b/hostap/tests/p2p-fuzzer/proberesp-go.dat
Binary files differ
diff --git a/hostap/tests/p2p-fuzzer/proberesp.dat b/hostap/tests/p2p-fuzzer/proberesp.dat
new file mode 100644
index 0000000..8d997d1
--- /dev/null
+++ b/hostap/tests/p2p-fuzzer/proberesp.dat
Binary files differ
diff --git a/hostap/tests/test-aes.c b/hostap/tests/test-aes.c
new file mode 100644
index 0000000..9d76c07
--- /dev/null
+++ b/hostap/tests/test-aes.c
@@ -0,0 +1,624 @@
+/*
+ * Test program for AES
+ * Copyright (c) 2003-2012, 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/crypto.h"
+#include "crypto/aes_wrap.h"
+
+#define BLOCK_SIZE 16
+
+static void test_aes_perf(void)
+{
+#if 0 /* this did not seem to work with new compiler?! */
+#ifdef __i386__
+#define rdtscll(val) \
+     __asm__ __volatile__("rdtsc" : "=A" (val))
+	const int num_iters = 10;
+	int i;
+	unsigned int start, end;
+	u8 key[16], pt[16], ct[16];
+	void *ctx;
+
+	printf("keySetupEnc:");
+	for (i = 0; i < num_iters; i++) {
+		rdtscll(start);
+		ctx = aes_encrypt_init(key, 16);
+		rdtscll(end);
+		aes_encrypt_deinit(ctx);
+		printf(" %d", end - start);
+	}
+	printf("\n");
+
+	printf("Encrypt:");
+	ctx = aes_encrypt_init(key, 16);
+	for (i = 0; i < num_iters; i++) {
+		rdtscll(start);
+		aes_encrypt(ctx, pt, ct);
+		rdtscll(end);
+		printf(" %d", end - start);
+	}
+	aes_encrypt_deinit(ctx);
+	printf("\n");
+#endif /* __i386__ */
+#endif
+}
+
+
+/*
+ * GCM test vectors from
+ * http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
+ */
+struct gcm_test_vector {
+	char *k;
+	char *p;
+	char *aad;
+	char *iv;
+	char *c;
+	char *t;
+};
+
+static const struct gcm_test_vector gcm_tests[] = {
+	{
+		/* Test Case 1 */
+		"00000000000000000000000000000000",
+		"",
+		"",
+		"000000000000000000000000",
+		"",
+		"58e2fccefa7e3061367f1d57a4e7455a"
+	},
+	{
+		/* Test Case 2 */
+		"00000000000000000000000000000000",
+		"00000000000000000000000000000000",
+		"",
+		"000000000000000000000000",
+		"0388dace60b6a392f328c2b971b2fe78",
+		"ab6e47d42cec13bdf53a67b21257bddf"
+	},
+	{
+		/* Test Case 3 */
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+		"",
+		"cafebabefacedbaddecaf888",
+		"42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985",
+		"4d5c2af327cd64a62cf35abd2ba6fab4"
+	},
+	{
+		/* Test Case 4 */
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeefabaddad2",
+		"cafebabefacedbaddecaf888",
+		"42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091",
+		"5bc94fbc3221a5db94fae95ae7121a47"
+	},
+	{
+		/* Test Case 5 */
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeefabaddad2",
+		"cafebabefacedbad",
+		"61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598",
+		"3612d2e79e3b0785561be14aaca2fccb"
+	},
+	{
+		/* Test Case 6 */
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeefabaddad2",
+		"9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+		"8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5",
+		"619cc5aefffe0bfa462af43c1699d050"
+	},
+	{
+		/* Test Case 7 */
+		"000000000000000000000000000000000000000000000000",
+		"",
+		"",
+		"000000000000000000000000",
+		"",
+		"cd33b28ac773f74ba00ed1f312572435"
+	},
+	{
+		/* Test Case 8 */
+		"000000000000000000000000000000000000000000000000",
+		"00000000000000000000000000000000",
+		"",
+		"000000000000000000000000",
+		"98e7247c07f0fe411c267e4384b0f600",
+		"2ff58d80033927ab8ef4d4587514f0fb"
+	},
+	{
+		/* Test Case 9 */
+		"feffe9928665731c6d6a8f9467308308feffe9928665731c",
+		"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+		"",
+		"cafebabefacedbaddecaf888",
+		"3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710acade256",
+		"9924a7c8587336bfb118024db8674a14"
+	},
+	{
+		/* Test Case 10 */
+		"feffe9928665731c6d6a8f9467308308feffe9928665731c",
+		"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeefabaddad2",
+		"cafebabefacedbaddecaf888",
+		"3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710",
+		"2519498e80f1478f37ba55bd6d27618c"
+	},
+	{
+		/* Test Case 11 */
+		"feffe9928665731c6d6a8f9467308308feffe9928665731c",
+		"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeefabaddad2",
+		"cafebabefacedbad",
+		"0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7",
+		"65dcc57fcf623a24094fcca40d3533f8"
+	},
+	{
+		/* Test Case 12 */
+		"feffe9928665731c6d6a8f9467308308feffe9928665731c",
+		"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeefabaddad2",
+		"9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+		"d27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e4581e79012af34ddd9e2f037589b292db3e67c036745fa22e7e9b7373b",
+		"dcf566ff291c25bbb8568fc3d376a6d9"
+	},
+	{
+		/* Test Case 13 */
+		"0000000000000000000000000000000000000000000000000000000000000000",
+		"",
+		"",
+		"000000000000000000000000",
+		"",
+		"530f8afbc74536b9a963b4f1c4cb738b"
+	},
+	{
+		/* Test Case 14 */
+		"0000000000000000000000000000000000000000000000000000000000000000",
+		"00000000000000000000000000000000",
+		"",
+		"000000000000000000000000",
+		"cea7403d4d606b6e074ec5d3baf39d18",
+		"d0d1c8a799996bf0265b98b5d48ab919"
+	},
+	{
+		/* Test Case 15 */
+		"feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+		"",
+		"cafebabefacedbaddecaf888",
+		"522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad",
+		"b094dac5d93471bdec1a502270e3cc6c"
+	},
+	{
+		/* Test Case 16 */
+		"feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeefabaddad2",
+		"cafebabefacedbaddecaf888",
+		"522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662",
+		"76fc6ece0f4e1768cddf8853bb2d551b"
+	},
+	{
+		/* Test Case 17 */
+		"feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeefabaddad2",
+		"cafebabefacedbad",
+		"c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f",
+		"3a337dbf46a792c45e454913fe2ea8f2"
+	},
+	{
+		/* Test Case 18 */
+		"feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeefabaddad2",
+		"9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+		"5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f",
+		"a44a8266ee1c8eb0c8b5d4cf5ae9f19a"
+	}
+};
+
+
+static int test_gcm(void)
+{
+	int ret = 0;
+	int i;
+	u8 k[32], aad[32], iv[64], t[16], tag[16];
+	u8 p[64], c[64], tmp[64];
+	size_t k_len, p_len, aad_len, iv_len;
+
+	for (i = 0; i < ARRAY_SIZE(gcm_tests); i++) {
+		const struct gcm_test_vector *tc = &gcm_tests[i];
+
+		k_len = os_strlen(tc->k) / 2;
+		if (hexstr2bin(tc->k, k, k_len)) {
+			printf("Invalid GCM test vector %d (k)\n", i);
+			ret++;
+			continue;
+		}
+
+		p_len = os_strlen(tc->p) / 2;
+		if (hexstr2bin(tc->p, p, p_len)) {
+			printf("Invalid GCM test vector %d (p)\n", i);
+			ret++;
+			continue;
+		}
+
+		aad_len = os_strlen(tc->aad) / 2;
+		if (hexstr2bin(tc->aad, aad, aad_len)) {
+			printf("Invalid GCM test vector %d (aad)\n", i);
+			ret++;
+			continue;
+		}
+
+		iv_len = os_strlen(tc->iv) / 2;
+		if (hexstr2bin(tc->iv, iv, iv_len)) {
+			printf("Invalid GCM test vector %d (iv)\n", i);
+			ret++;
+			continue;
+		}
+
+		if (hexstr2bin(tc->c, c, p_len)) {
+			printf("Invalid GCM test vector %d (c)\n", i);
+			ret++;
+			continue;
+		}
+
+		if (hexstr2bin(tc->t, t, sizeof(t))) {
+			printf("Invalid GCM test vector %d (t)\n", i);
+			ret++;
+			continue;
+		}
+
+		if (aes_gcm_ae(k, k_len, iv, iv_len, p, p_len, aad, aad_len,
+			       tmp, tag) < 0) {
+			printf("GCM-AE failed (test case %d)\n", i);
+			ret++;
+			continue;
+		}
+
+		if (os_memcmp(c, tmp, p_len) != 0) {
+			printf("GCM-AE mismatch (test case %d)\n", i);
+			ret++;
+		}
+
+		if (os_memcmp(tag, t, sizeof(tag)) != 0) {
+			printf("GCM-AE tag mismatch (test case %d)\n", i);
+			ret++;
+		}
+
+		if (p_len == 0) {
+			if (aes_gmac(k, k_len, iv, iv_len, aad, aad_len, tag) <
+			    0) {
+				printf("GMAC failed (test case %d)\n", i);
+				ret++;
+				continue;
+			}
+
+			if (os_memcmp(tag, t, sizeof(tag)) != 0) {
+				printf("GMAC tag mismatch (test case %d)\n", i);
+				ret++;
+			}
+		}
+
+		if (aes_gcm_ad(k, k_len, iv, iv_len, c, p_len, aad, aad_len,
+			       t, tmp) < 0) {
+			printf("GCM-AD failed (test case %d)\n", i);
+			ret++;
+			continue;
+		}
+
+		if (os_memcmp(p, tmp, p_len) != 0) {
+			printf("GCM-AD mismatch (test case %d)\n", i);
+			ret++;
+		}
+	}
+
+	return ret;
+}
+
+
+static int test_nist_key_wrap_ae(const char *fname)
+{
+	FILE *f;
+	int ret = 0;
+	char buf[15000], *pos, *pos2;
+	u8 bin[2000], k[32], p[1024], c[1024 + 8], result[1024 + 8];
+	size_t bin_len, k_len = 0, p_len = 0, c_len = 0;
+	int ok = 0;
+
+	printf("NIST KW AE tests from %s\n", fname);
+
+	f = fopen(fname, "r");
+	if (f == NULL) {
+		printf("%s does not exist - cannot validate test vectors\n",
+		       fname);
+		return 1;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		if (buf[0] == '#')
+			continue;
+		pos = os_strchr(buf, '=');
+		if (pos == NULL)
+			continue;
+		pos2 = pos - 1;
+		while (pos2 >= buf && *pos2 == ' ')
+			*pos2-- = '\0';
+		*pos++ = '\0';
+		while (*pos == ' ')
+			*pos++ = '\0';
+		pos2 = os_strchr(pos, '\r');
+		if (!pos2)
+			pos2 = os_strchr(pos, '\n');
+		if (pos2)
+			*pos2 = '\0';
+		else
+			pos2 = pos + os_strlen(pos);
+
+		if (buf[0] == '[') {
+			printf("%s = %s\n", buf, pos);
+			continue;
+		}
+
+		if (os_strcmp(buf, "COUNT") == 0) {
+			printf("Test %s - ", pos);
+			continue;
+		}
+
+		bin_len = os_strlen(pos);
+		if (bin_len > sizeof(bin) * 2) {
+			printf("Too long binary data (%s)\n", buf);
+			return 1;
+		}
+		if (bin_len & 0x01) {
+			printf("Odd number of hexstring values (%s)\n",
+				buf);
+			return 1;
+		}
+		bin_len /= 2;
+		if (hexstr2bin(pos, bin, bin_len) < 0) {
+			printf("Invalid hex string '%s' (%s)\n", pos, buf);
+			return 1;
+		}
+
+		if (os_strcmp(buf, "K") == 0) {
+			if (bin_len > sizeof(k)) {
+				printf("Too long K (%u)\n", (unsigned) bin_len);
+				return 1;
+			}
+			os_memcpy(k, bin, bin_len);
+			k_len = bin_len;
+			continue;
+		}
+
+		if (os_strcmp(buf, "P") == 0) {
+			if (bin_len > sizeof(p)) {
+				printf("Too long P (%u)\n", (unsigned) bin_len);
+				return 1;
+			}
+			os_memcpy(p, bin, bin_len);
+			p_len = bin_len;
+			continue;
+		}
+
+		if (os_strcmp(buf, "C") != 0) {
+			printf("Unexpected field '%s'\n", buf);
+			continue;
+		}
+
+		if (bin_len > sizeof(c)) {
+			printf("Too long C (%u)\n", (unsigned) bin_len);
+			return 1;
+		}
+		os_memcpy(c, bin, bin_len);
+		c_len = bin_len;
+
+		if (p_len % 8 != 0 || c_len % 8 != 0 || c_len - p_len != 8) {
+			printf("invalid parameter length (p_len=%u c_len=%u)\n",
+			       (unsigned) p_len, (unsigned) c_len);
+			continue;
+		}
+
+		if (aes_wrap(k, k_len, p_len / 8, p, result)) {
+			printf("aes_wrap() failed\n");
+			ret++;
+			continue;
+		}
+
+		if (os_memcmp(c, result, c_len) == 0) {
+			printf("OK\n");
+			ok++;
+		} else {
+			printf("FAIL\n");
+			ret++;
+		}
+	}
+
+	fclose(f);
+
+	if (ret)
+		printf("Test case failed\n");
+	else
+		printf("%d test vectors OK\n", ok);
+
+	return ret;
+}
+
+
+static int test_nist_key_wrap_ad(const char *fname)
+{
+	FILE *f;
+	int ret = 0;
+	char buf[15000], *pos, *pos2;
+	u8 bin[2000], k[32], p[1024], c[1024 + 8], result[1024 + 8];
+	size_t bin_len, k_len = 0, p_len = 0, c_len = 0;
+	int ok = 0;
+	int fail;
+
+	printf("NIST KW AD tests from %s\n", fname);
+
+	f = fopen(fname, "r");
+	if (f == NULL) {
+		printf("%s does not exist - cannot validate test vectors\n",
+		       fname);
+		return 1;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		if (buf[0] == '#')
+			continue;
+		fail = 0;
+		pos = os_strchr(buf, '=');
+		if (pos == NULL) {
+			if (os_strncmp(buf, "FAIL", 4) == 0) {
+				fail = 1;
+				goto skip_val_parse;
+			}
+			continue;
+		}
+		pos2 = pos - 1;
+		while (pos2 >= buf && *pos2 == ' ')
+			*pos2-- = '\0';
+		*pos++ = '\0';
+		while (*pos == ' ')
+			*pos++ = '\0';
+		pos2 = os_strchr(pos, '\r');
+		if (!pos2)
+			pos2 = os_strchr(pos, '\n');
+		if (pos2)
+			*pos2 = '\0';
+		else
+			pos2 = pos + os_strlen(pos);
+
+		if (buf[0] == '[') {
+			printf("%s = %s\n", buf, pos);
+			continue;
+		}
+
+		if (os_strcmp(buf, "COUNT") == 0) {
+			printf("Test %s - ", pos);
+			continue;
+		}
+
+		bin_len = os_strlen(pos);
+		if (bin_len > sizeof(bin) * 2) {
+			printf("Too long binary data (%s)\n", buf);
+			return 1;
+		}
+		if (bin_len & 0x01) {
+			printf("Odd number of hexstring values (%s)\n",
+				buf);
+			return 1;
+		}
+		bin_len /= 2;
+		if (hexstr2bin(pos, bin, bin_len) < 0) {
+			printf("Invalid hex string '%s' (%s)\n", pos, buf);
+			return 1;
+		}
+
+		if (os_strcmp(buf, "K") == 0) {
+			if (bin_len > sizeof(k)) {
+				printf("Too long K (%u)\n", (unsigned) bin_len);
+				return 1;
+			}
+			os_memcpy(k, bin, bin_len);
+			k_len = bin_len;
+			continue;
+		}
+
+		if (os_strcmp(buf, "C") == 0) {
+			if (bin_len > sizeof(c)) {
+				printf("Too long C (%u)\n", (unsigned) bin_len);
+				return 1;
+			}
+			os_memcpy(c, bin, bin_len);
+			c_len = bin_len;
+			continue;
+		}
+
+	skip_val_parse:
+		if (!fail) {
+			if (os_strcmp(buf, "P") != 0) {
+				printf("Unexpected field '%s'\n", buf);
+				continue;
+			}
+
+			if (bin_len > sizeof(p)) {
+				printf("Too long P (%u)\n", (unsigned) bin_len);
+				return 1;
+			}
+			os_memcpy(p, bin, bin_len);
+			p_len = bin_len;
+
+			if (p_len % 8 != 0 || c_len % 8 != 0 ||
+			    c_len - p_len != 8) {
+				printf("invalid parameter length (p_len=%u c_len=%u)\n",
+				       (unsigned) p_len, (unsigned) c_len);
+				continue;
+			}
+		}
+
+		if (aes_unwrap(k, k_len, (c_len / 8) - 1, c, result)) {
+			if (fail) {
+				printf("OK (fail reported)\n");
+				ok++;
+				continue;
+			}
+			printf("aes_unwrap() failed\n");
+			ret++;
+			continue;
+		}
+
+		if (fail) {
+			printf("FAIL (mismatch not reported)\n");
+			ret++;
+		} else if (os_memcmp(p, result, p_len) == 0) {
+			printf("OK\n");
+			ok++;
+		} else {
+			printf("FAIL\n");
+			ret++;
+		}
+	}
+
+	fclose(f);
+
+	if (ret)
+		printf("Test case failed\n");
+	else
+		printf("%d test vectors OK\n", ok);
+
+	return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+	int ret = 0;
+
+	if (argc >= 3 && os_strcmp(argv[1], "NIST-KW-AE") == 0)
+		ret += test_nist_key_wrap_ae(argv[2]);
+	else if (argc >= 3 && os_strcmp(argv[1], "NIST-KW-AD") == 0)
+		ret += test_nist_key_wrap_ad(argv[2]);
+
+	test_aes_perf();
+
+	ret += test_gcm();
+
+	if (ret)
+		printf("FAILED!\n");
+
+	return ret;
+}
diff --git a/hostap/tests/test-asn1.c b/hostap/tests/test-asn1.c
new file mode 100644
index 0000000..c6957fd
--- /dev/null
+++ b/hostap/tests/test-asn1.c
@@ -0,0 +1,195 @@
+/*
+ * Testing tool for ASN.1 routines
+ * Copyright (c) 2006-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 "tls/asn1.h"
+
+
+static const char * asn1_class_str(int class)
+{
+	switch (class) {
+	case ASN1_CLASS_UNIVERSAL:
+		return "Universal";
+	case ASN1_CLASS_APPLICATION:
+		return "Application";
+	case ASN1_CLASS_CONTEXT_SPECIFIC:
+		return "Context-specific";
+	case ASN1_CLASS_PRIVATE:
+		return "Private";
+	default:
+		return "?";
+	}
+}
+
+
+int asn1_parse(const u8 *buf, size_t len, int level)
+{
+	const u8 *pos, *prev, *end;
+	char prefix[10], str[100];
+	int _level;
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+	u8 tmp;
+
+	_level = level;
+	if ((size_t) _level > sizeof(prefix) - 1)
+		_level = sizeof(prefix) - 1;
+	memset(prefix, ' ', _level);
+	prefix[_level] = '\0';
+
+	pos = buf;
+	end = buf + len;
+
+	while (pos < end) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0)
+			return -1;
+
+		prev = pos;
+		pos = hdr.payload;
+
+		wpa_printf(MSG_MSGDUMP, "ASN.1:%s Class %d(%s) P/C %d(%s) "
+			   "Tag %u Length %u",
+			   prefix, hdr.class, asn1_class_str(hdr.class),
+			   hdr.constructed,
+			   hdr.constructed ? "Constructed" : "Primitive",
+			   hdr.tag, hdr.length);
+
+		if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC &&
+		    hdr.constructed) {
+			if (asn1_parse(pos, hdr.length, level + 1) < 0)
+				return -1;
+			pos += hdr.length;
+		}
+
+		if (hdr.class != ASN1_CLASS_UNIVERSAL)
+			continue;
+
+		switch (hdr.tag) {
+		case ASN1_TAG_EOC:
+			if (hdr.length) {
+				wpa_printf(MSG_DEBUG, "ASN.1: Non-zero "
+					   "end-of-contents length (%u)",
+					   hdr.length);
+				return -1;
+			}
+			wpa_printf(MSG_MSGDUMP, "ASN.1:%s EOC", prefix);
+			break;
+		case ASN1_TAG_BOOLEAN:
+			if (hdr.length != 1) {
+				wpa_printf(MSG_DEBUG, "ASN.1: Unexpected "
+					   "Boolean length (%u)", hdr.length);
+				return -1;
+			}
+			tmp = *pos++;
+			wpa_printf(MSG_MSGDUMP, "ASN.1:%s Boolean %s",
+				   prefix, tmp ? "TRUE" : "FALSE");
+			break;
+		case ASN1_TAG_INTEGER:
+			wpa_hexdump(MSG_MSGDUMP, "ASN.1: INTEGER",
+				    pos, hdr.length);
+			pos += hdr.length;
+			break;
+		case ASN1_TAG_BITSTRING:
+			wpa_hexdump(MSG_MSGDUMP, "ASN.1: BitString",
+				    pos, hdr.length);
+			pos += hdr.length;
+			break;
+		case ASN1_TAG_OCTETSTRING:
+			wpa_hexdump(MSG_MSGDUMP, "ASN.1: OctetString",
+				    pos, hdr.length);
+			pos += hdr.length;
+			break;
+		case ASN1_TAG_NULL:
+			if (hdr.length) {
+				wpa_printf(MSG_DEBUG, "ASN.1: Non-zero Null "
+					   "length (%u)", hdr.length);
+				return -1;
+			}
+			wpa_printf(MSG_MSGDUMP, "ASN.1:%s Null", prefix);
+			break;
+		case ASN1_TAG_OID:
+			if (asn1_get_oid(prev, end - prev, &oid, &prev) < 0) {
+				wpa_printf(MSG_DEBUG, "ASN.1: Invalid OID");
+				return -1;
+			}
+			asn1_oid_to_str(&oid, str, sizeof(str));
+			wpa_printf(MSG_DEBUG, "ASN.1:%s OID %s", prefix, str);
+			pos += hdr.length;
+			break;
+		case ANS1_TAG_RELATIVE_OID:
+			wpa_hexdump(MSG_MSGDUMP, "ASN.1: Relative OID",
+				    pos, hdr.length);
+			pos += hdr.length;
+			break;
+		case ASN1_TAG_SEQUENCE:
+			wpa_printf(MSG_MSGDUMP, "ASN.1:%s SEQUENCE", prefix);
+			if (asn1_parse(pos, hdr.length, level + 1) < 0)
+				return -1;
+			pos += hdr.length;
+			break;
+		case ASN1_TAG_SET:
+			wpa_printf(MSG_MSGDUMP, "ASN.1:%s SET", prefix);
+			if (asn1_parse(pos, hdr.length, level + 1) < 0)
+				return -1;
+			pos += hdr.length;
+			break;
+		case ASN1_TAG_PRINTABLESTRING:
+			wpa_hexdump_ascii(MSG_MSGDUMP,
+					  "ASN.1: PrintableString",
+					  pos, hdr.length);
+			pos += hdr.length;
+			break;
+		case ASN1_TAG_IA5STRING:
+			wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: IA5String",
+					  pos, hdr.length);
+			pos += hdr.length;
+			break;
+		case ASN1_TAG_UTCTIME:
+			wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: UTCTIME",
+					  pos, hdr.length);
+			pos += hdr.length;
+			break;
+		case ASN1_TAG_VISIBLESTRING:
+			wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: VisibleString",
+					  pos, hdr.length);
+			pos += hdr.length;
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "ASN.1: Unknown tag %d",
+				   hdr.tag);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+	FILE *f;
+	u8 buf[3000];
+	size_t len;
+
+	wpa_debug_level = 0;
+
+	f = fopen(argv[1], "rb");
+	if (f == NULL)
+		return -1;
+	len = fread(buf, 1, sizeof(buf), f);
+	fclose(f);
+
+	if (asn1_parse(buf, len, 0) < 0)
+		printf("Failed to parse DER ASN.1\n");
+
+	printf("\n\n");
+
+	return 0;
+}
diff --git a/hostap/tests/test-base64.c b/hostap/tests/test-base64.c
new file mode 100644
index 0000000..980febf
--- /dev/null
+++ b/hostap/tests/test-base64.c
@@ -0,0 +1,42 @@
+/*
+ * Base64 encoding/decoding (RFC1341) - test program
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/os.h"
+#include "utils/base64.h"
+
+int main(int argc, char *argv[])
+{
+	FILE *f;
+	size_t len, elen;
+	unsigned char *buf, *e;
+
+	if (argc != 4) {
+		printf("Usage: base64 <encode|decode> <in file> <out file>\n");
+		return -1;
+	}
+
+	buf = (unsigned char *) os_readfile(argv[2], &len);
+	if (buf == NULL)
+		return -1;
+
+	if (strcmp(argv[1], "encode") == 0)
+		e = base64_encode(buf, len, &elen);
+	else
+		e = base64_decode(buf, len, &elen);
+	if (e == NULL)
+		return -2;
+	f = fopen(argv[3], "w");
+	if (f == NULL)
+		return -3;
+	fwrite(e, 1, elen, f);
+	fclose(f);
+	free(e);
+
+	return 0;
+}
diff --git a/hostap/tests/test-https.c b/hostap/tests/test-https.c
new file mode 100644
index 0000000..a72e56f
--- /dev/null
+++ b/hostap/tests/test-https.c
@@ -0,0 +1,225 @@
+/*
+ * Testing tool for TLSv1 client routines using HTTPS
+ * Copyright (c) 2011, 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 <netdb.h>
+
+#include "common.h"
+#include "crypto/tls.h"
+
+
+static void https_tls_event_cb(void *ctx, enum tls_event ev,
+			       union tls_event_data *data)
+{
+	wpa_printf(MSG_DEBUG, "HTTPS: TLS event %d", ev);
+}
+
+
+static struct wpabuf * https_recv(int s)
+{
+	struct wpabuf *in;
+	int len, ret;
+	fd_set rfds;
+	struct timeval tv;
+
+	in = wpabuf_alloc(20000);
+	if (in == NULL)
+		return NULL;
+
+	FD_ZERO(&rfds);
+	FD_SET(s, &rfds);
+	tv.tv_sec = 5;
+	tv.tv_usec = 0;
+
+	wpa_printf(MSG_DEBUG, "Waiting for more data");
+	ret = select(s + 1, &rfds, NULL, NULL, &tv);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "select: %s", strerror(errno));
+		wpabuf_free(in);
+		return NULL;
+	}
+	if (ret == 0) {
+		/* timeout */
+		wpa_printf(MSG_INFO, "Timeout on waiting for data");
+		wpabuf_free(in);
+		return NULL;
+	}
+
+	len = recv(s, wpabuf_put(in, 0), wpabuf_tailroom(in), 0);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+		wpabuf_free(in);
+		return NULL;
+	}
+	if (len == 0) {
+		wpa_printf(MSG_DEBUG, "No more data available");
+		wpabuf_free(in);
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "Received %d bytes", len);
+	wpabuf_put(in, len);
+
+	return in;
+}
+
+
+static int https_client(int s, const char *path)
+{
+	struct tls_config conf;
+	void *tls;
+	struct tls_connection *conn;
+	struct wpabuf *in, *out, *appl;
+	int res = -1;
+	int need_more_data;
+
+	os_memset(&conf, 0, sizeof(conf));
+	conf.event_cb = https_tls_event_cb;
+	tls = tls_init(&conf);
+	if (tls == NULL)
+		return -1;
+
+	conn = tls_connection_init(tls);
+	if (conn == NULL) {
+		tls_deinit(tls);
+		return -1;
+	}
+
+	in = NULL;
+
+	for (;;) {
+		appl = NULL;
+		out = tls_connection_handshake2(tls, conn, in, &appl,
+						&need_more_data);
+		wpabuf_free(in);
+		in = NULL;
+		if (out == NULL) {
+			if (need_more_data)
+				goto read_more;
+			goto done;
+		}
+		if (tls_connection_get_failed(tls, conn)) {
+			wpa_printf(MSG_ERROR, "TLS handshake failed");
+			goto done;
+		}
+		if (tls_connection_established(tls, conn))
+			break;
+		wpa_printf(MSG_DEBUG, "Sending %d bytes",
+			   (int) wpabuf_len(out));
+		if (send(s, wpabuf_head(out), wpabuf_len(out), 0) < 0) {
+			wpa_printf(MSG_ERROR, "send: %s", strerror(errno));
+			goto done;
+		}
+		wpabuf_free(out);
+		out = NULL;
+
+	read_more:
+		in = https_recv(s);
+		if (in == NULL)
+			goto done;
+	}
+	wpabuf_free(out);
+	out = NULL;
+
+	wpa_printf(MSG_INFO, "TLS connection established");
+	if (appl)
+		wpa_hexdump_buf(MSG_DEBUG, "Received application data", appl);
+
+	in = wpabuf_alloc(100 + os_strlen(path));
+	if (in == NULL)
+		goto done;
+	wpabuf_put_str(in, "GET ");
+	wpabuf_put_str(in, path);
+	wpabuf_put_str(in, " HTTP/1.0\r\n\r\n");
+	out = tls_connection_encrypt(tls, conn, in);
+	wpabuf_free(in);
+	in = NULL;
+	if (out == NULL)
+		goto done;
+
+	wpa_printf(MSG_INFO, "Sending HTTP request: %d bytes",
+		   (int) wpabuf_len(out));
+	if (send(s, wpabuf_head(out), wpabuf_len(out), 0) < 0) {
+		wpa_printf(MSG_ERROR, "send: %s", strerror(errno));
+		goto done;
+	}
+	wpabuf_free(out);
+	out = NULL;
+
+	wpa_printf(MSG_INFO, "Reading HTTP response");
+	for (;;) {
+		int need_more_data;
+		in = https_recv(s);
+		if (in == NULL)
+			goto done;
+		out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
+		if (need_more_data)
+			wpa_printf(MSG_DEBUG, "HTTP: Need more data");
+		wpabuf_free(in);
+		in = NULL;
+		if (out == NULL)
+			goto done;
+		wpa_hexdump_ascii(MSG_INFO, "Response", wpabuf_head(out),
+				  wpabuf_len(out));
+		wpabuf_free(out);
+		out = NULL;
+	}
+
+	res = 0;
+done:
+	wpabuf_free(out);
+	wpabuf_free(in);
+	wpabuf_free(appl);
+	tls_connection_deinit(tls, conn);
+	tls_deinit(tls);
+
+	return res;
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct addrinfo hints, *result, *rp;
+	int res, s;
+
+	wpa_debug_level = 0;
+	wpa_debug_show_keys = 1;
+
+	if (argc < 4) {
+		wpa_printf(MSG_INFO, "usage: test-https server port path");
+		return -1;
+	}
+
+	os_memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	res = getaddrinfo(argv[1], argv[2], &hints, &result);
+	if (res) {
+		wpa_printf(MSG_ERROR, "getaddrinfo: %s", gai_strerror(res));
+		return -1;
+	}
+
+	for (rp = result; rp; rp = rp->ai_next) {
+		s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+		if (s < 0)
+			continue;
+		if (connect(s, rp->ai_addr, rp->ai_addrlen) == 0)
+			break;
+		close(s);
+	}
+	freeaddrinfo(result);
+
+	if (rp == NULL) {
+		wpa_printf(MSG_ERROR, "Could not connect");
+		return -1;
+	}
+
+	https_client(s, argv[3]);
+	close(s);
+
+	return 0;
+}
diff --git a/hostap/tests/test-list.c b/hostap/tests/test-list.c
new file mode 100644
index 0000000..01bcbf6
--- /dev/null
+++ b/hostap/tests/test-list.c
@@ -0,0 +1,72 @@
+/*
+ * Doubly-linked list - test program
+ * 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 "utils/includes.h"
+#include "utils/os.h"
+#include "utils/list.h"
+
+struct test {
+	struct dl_list list;
+	int value;
+};
+
+static void dump_list(struct dl_list *head)
+{
+	struct test *t;
+	printf("dump:");
+	dl_list_for_each(t, head, struct test, list)
+		printf(" %d", t->value);
+	printf(" (len=%d%s)\n", dl_list_len(head),
+	       dl_list_empty(head) ? " empty" : "");
+}
+
+int main(int argc, char *argv[])
+{
+	struct dl_list head;
+	struct test *t, *tmp;
+	int i;
+
+	dl_list_init(&head);
+	dump_list(&head);
+
+	for (i = 0; i < 5; i++) {
+		t = os_zalloc(sizeof(*t));
+		if (t == NULL)
+			return -1;
+		t->value = i;
+		dl_list_add(&head, &t->list);
+		dump_list(&head);
+	}
+
+	for (i = 10; i > 5; i--) {
+		t = os_zalloc(sizeof(*t));
+		if (t == NULL)
+			return -1;
+		t->value = i;
+		dl_list_add_tail(&head, &t->list);
+		dump_list(&head);
+	}
+
+	i = 0;
+	dl_list_for_each(t, &head, struct test, list)
+		if (++i == 5)
+			break;
+	printf("move: %d\n", t->value);
+	dl_list_del(&t->list);
+	dl_list_add(&head, &t->list);
+	dump_list(&head);
+
+	dl_list_for_each_safe(t, tmp, &head, struct test, list) {
+		printf("delete: %d\n", t->value);
+		dl_list_del(&t->list);
+		os_free(t);
+		dump_list(&head);
+	}
+
+	return 0;
+}
diff --git a/hostap/tests/test-md4.c b/hostap/tests/test-md4.c
new file mode 100644
index 0000000..e3e63ed
--- /dev/null
+++ b/hostap/tests/test-md4.c
@@ -0,0 +1,93 @@
+/*
+ * Test program for MD4 (test vectors from RFC 1320)
+ * Copyright (c) 2006-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/crypto.h"
+
+int main(int argc, char *argv[])
+{
+	struct {
+		char *data;
+		char *hash;
+	} tests[] = {
+		{
+			"",
+			"\x31\xd6\xcf\xe0\xd1\x6a\xe9\x31"
+			"\xb7\x3c\x59\xd7\xe0\xc0\x89\xc0"
+		},
+		{
+			"a",
+			"\xbd\xe5\x2c\xb3\x1d\xe3\x3e\x46"
+			"\x24\x5e\x05\xfb\xdb\xd6\xfb\x24"
+		},
+		{
+			"abc",
+			"\xa4\x48\x01\x7a\xaf\x21\xd8\x52"
+			"\x5f\xc1\x0a\xe8\x7a\xa6\x72\x9d"
+		},
+		{
+			"message digest",
+			"\xd9\x13\x0a\x81\x64\x54\x9f\xe8"
+			"\x18\x87\x48\x06\xe1\xc7\x01\x4b"
+		},
+		{
+			"abcdefghijklmnopqrstuvwxyz",
+			"\xd7\x9e\x1c\x30\x8a\xa5\xbb\xcd"
+			"\xee\xa8\xed\x63\xdf\x41\x2d\xa9"
+		},
+		{
+			"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+			"0123456789",
+			"\x04\x3f\x85\x82\xf2\x41\xdb\x35"
+			"\x1c\xe6\x27\xe1\x53\xe7\xf0\xe4"
+		},
+		{
+			"12345678901234567890123456789012345678901234567890"
+			"123456789012345678901234567890",
+			"\xe3\x3b\x4d\xdc\x9c\x38\xf2\x19"
+			"\x9c\x3e\x7b\x16\x4f\xcc\x05\x36"
+		}
+	};
+	unsigned int i;
+	u8 hash[16];
+	const u8 *addr[2];
+	size_t len[2];
+	int errors = 0;
+
+	for (i = 0; i < ARRAY_SIZE(tests); i++) {
+		printf("MD4 test case %d:", i);
+
+		addr[0] = (u8 *) tests[i].data;
+		len[0] = strlen(tests[i].data);
+		md4_vector(1, addr, len, hash);
+		if (memcmp(hash, tests[i].hash, 16) != 0) {
+			printf(" FAIL");
+			errors++;
+		} else
+			printf(" OK");
+
+		if (len[0]) {
+			addr[0] = (u8 *) tests[i].data;
+			len[0] = strlen(tests[i].data);
+			addr[1] = (u8 *) tests[i].data + 1;
+			len[1] = strlen(tests[i].data) - 1;
+			md4_vector(1, addr, len, hash);
+			if (memcmp(hash, tests[i].hash, 16) != 0) {
+				printf(" FAIL");
+				errors++;
+			} else
+				printf(" OK");
+		}
+
+		printf("\n");
+	}
+
+	return errors;
+}
diff --git a/hostap/tests/test-milenage.c b/hostap/tests/test-milenage.c
new file mode 100644
index 0000000..7c4be09
--- /dev/null
+++ b/hostap/tests/test-milenage.c
@@ -0,0 +1,814 @@
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/milenage.h"
+
+
+/**
+ * milenage_opc - Determine OPc from OP and K
+ * @op: OP = 128-bit operator variant algorithm configuration field
+ * @k: K = 128-bit subscriber key
+ * @opc: Buffer for OPc = 128-bit value derived from OP and K
+ */
+static int milenage_opc(const u8 *op, const u8 *k, u8 *opc)
+{
+	int i;
+	/* OP_C = OP XOR E_K(OP) */
+	if (aes_128_encrypt_block(k, op, opc) < 0)
+		return -1;
+	for (i = 0; i < 16; i++)
+		opc[i] ^= op[i];
+	return 0;
+}
+
+
+struct gsm_milenage_test_set {
+	u8 ki[16];
+	u8 rand[16];
+	u8 opc[16];
+	u8 sres1[4];
+	u8 sres2[4];
+	u8 kc[8];
+};
+
+static const struct gsm_milenage_test_set gsm_test_sets[] =
+{
+	{
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 1 */
+		{ 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f,
+		  0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc },
+		{ 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d,
+		  0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 },
+		{ 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e,
+		  0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf },
+		{ 0x46, 0xf8, 0x41, 0x6a },
+		{ 0xa5, 0x42, 0x11, 0xd5 },
+		{ 0xea, 0xe4, 0xbe, 0x82, 0x3a, 0xf9, 0xa0, 0x8b }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 2 */
+		{ 0xfe, 0xc8, 0x6b, 0xa6, 0xeb, 0x70, 0x7e, 0xd0,
+		  0x89, 0x05, 0x75, 0x7b, 0x1b, 0xb4, 0x4b, 0x8f },
+		{ 0x9f, 0x7c, 0x8d, 0x02, 0x1a, 0xcc, 0xf4, 0xdb,
+		  0x21, 0x3c, 0xcf, 0xf0, 0xc7, 0xf7, 0x1a, 0x6a },
+		{ 0x10, 0x06, 0x02, 0x0f, 0x0a, 0x47, 0x8b, 0xf6,
+		  0xb6, 0x99, 0xf1, 0x5c, 0x06, 0x2e, 0x42, 0xb3 },
+		{ 0x8c, 0x30, 0x8a, 0x5e },
+		{ 0x80, 0x11, 0xc4, 0x8c },
+		{ 0xaa, 0x01, 0x73, 0x9b, 0x8c, 0xaa, 0x97, 0x6d }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 3 */
+		{ 0x9e, 0x59, 0x44, 0xae, 0xa9, 0x4b, 0x81, 0x16,
+		  0x5c, 0x82, 0xfb, 0xf9, 0xf3, 0x2d, 0xb7, 0x51 },
+		{ 0xce, 0x83, 0xdb, 0xc5, 0x4a, 0xc0, 0x27, 0x4a,
+		  0x15, 0x7c, 0x17, 0xf8, 0x0d, 0x01, 0x7b, 0xd6 },
+		{ 0xa6, 0x4a, 0x50, 0x7a, 0xe1, 0xa2, 0xa9, 0x8b,
+		  0xb8, 0x8e, 0xb4, 0x21, 0x01, 0x35, 0xdc, 0x87 },
+		{ 0xcf, 0xbc, 0xe3, 0xfe },
+		{ 0xf3, 0x65, 0xcd, 0x68 },
+		{ 0x9a, 0x8e, 0xc9, 0x5f, 0x40, 0x8c, 0xc5, 0x07 }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 4 */
+		{ 0x4a, 0xb1, 0xde, 0xb0, 0x5c, 0xa6, 0xce, 0xb0,
+		  0x51, 0xfc, 0x98, 0xe7, 0x7d, 0x02, 0x6a, 0x84 },
+		{ 0x74, 0xb0, 0xcd, 0x60, 0x31, 0xa1, 0xc8, 0x33,
+		  0x9b, 0x2b, 0x6c, 0xe2, 0xb8, 0xc4, 0xa1, 0x86 },
+		{ 0xdc, 0xf0, 0x7c, 0xbd, 0x51, 0x85, 0x52, 0x90,
+		  0xb9, 0x2a, 0x07, 0xa9, 0x89, 0x1e, 0x52, 0x3e },
+		{ 0x96, 0x55, 0xe2, 0x65 },
+		{ 0x58, 0x60, 0xfc, 0x1b },
+		{ 0xcd, 0xc1, 0xdc, 0x08, 0x41, 0xb8, 0x1a, 0x22 }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 5 */
+		{ 0x6c, 0x38, 0xa1, 0x16, 0xac, 0x28, 0x0c, 0x45,
+		  0x4f, 0x59, 0x33, 0x2e, 0xe3, 0x5c, 0x8c, 0x4f },
+		{ 0xee, 0x64, 0x66, 0xbc, 0x96, 0x20, 0x2c, 0x5a,
+		  0x55, 0x7a, 0xbb, 0xef, 0xf8, 0xba, 0xbf, 0x63 },
+		{ 0x38, 0x03, 0xef, 0x53, 0x63, 0xb9, 0x47, 0xc6,
+		  0xaa, 0xa2, 0x25, 0xe5, 0x8f, 0xae, 0x39, 0x34 },
+		{ 0x13, 0x68, 0x8f, 0x17 },
+		{ 0x16, 0xc8, 0x23, 0x3f },
+		{ 0xdf, 0x75, 0xbc, 0x5e, 0xa8, 0x99, 0x87, 0x9f }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 6 */
+		{ 0x2d, 0x60, 0x9d, 0x4d, 0xb0, 0xac, 0x5b, 0xf0,
+		  0xd2, 0xc0, 0xde, 0x26, 0x70, 0x14, 0xde, 0x0d },
+		{ 0x19, 0x4a, 0xa7, 0x56, 0x01, 0x38, 0x96, 0xb7,
+		  0x4b, 0x4a, 0x2a, 0x3b, 0x0a, 0xf4, 0x53, 0x9e },
+		{ 0xc3, 0x5a, 0x0a, 0xb0, 0xbc, 0xbf, 0xc9, 0x25,
+		  0x2c, 0xaf, 0xf1, 0x5f, 0x24, 0xef, 0xbd, 0xe0 },
+		{ 0x55, 0x3d, 0x00, 0xb3 },
+		{ 0x8c, 0x25, 0xa1, 0x6c },
+		{ 0x84, 0xb4, 0x17, 0xae, 0x3a, 0xea, 0xb4, 0xf3 }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 7 */
+		{ 0xa5, 0x30, 0xa7, 0xfe, 0x42, 0x8f, 0xad, 0x10,
+		  0x82, 0xc4, 0x5e, 0xdd, 0xfc, 0xe1, 0x38, 0x84 },
+		{ 0x3a, 0x4c, 0x2b, 0x32, 0x45, 0xc5, 0x0e, 0xb5,
+		  0xc7, 0x1d, 0x08, 0x63, 0x93, 0x95, 0x76, 0x4d },
+		{ 0x27, 0x95, 0x3e, 0x49, 0xbc, 0x8a, 0xf6, 0xdc,
+		  0xc6, 0xe7, 0x30, 0xeb, 0x80, 0x28, 0x6b, 0xe3 },
+		{ 0x59, 0xf1, 0xa4, 0x4a },
+		{ 0xa6, 0x32, 0x41, 0xe1 },
+		{ 0x3b, 0x4e, 0x24, 0x4c, 0xdc, 0x60, 0xce, 0x03 }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 8 */
+		{ 0xd9, 0x15, 0x1c, 0xf0, 0x48, 0x96, 0xe2, 0x58,
+		  0x30, 0xbf, 0x2e, 0x08, 0x26, 0x7b, 0x83, 0x60 },
+		{ 0xf7, 0x61, 0xe5, 0xe9, 0x3d, 0x60, 0x3f, 0xeb,
+		  0x73, 0x0e, 0x27, 0x55, 0x6c, 0xb8, 0xa2, 0xca },
+		{ 0xc4, 0xc9, 0x3e, 0xff, 0xe8, 0xa0, 0x81, 0x38,
+		  0xc2, 0x03, 0xd4, 0xc2, 0x7c, 0xe4, 0xe3, 0xd9 },
+		{ 0x50, 0x58, 0x88, 0x61 },
+		{ 0x4a, 0x90, 0xb2, 0x17 },
+		{ 0x8d, 0x4e, 0xc0, 0x1d, 0xe5, 0x97, 0xac, 0xfe }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 9 */
+		{ 0xa0, 0xe2, 0x97, 0x1b, 0x68, 0x22, 0xe8, 0xd3,
+		  0x54, 0xa1, 0x8c, 0xc2, 0x35, 0x62, 0x4e, 0xcb },
+		{ 0x08, 0xef, 0xf8, 0x28, 0xb1, 0x3f, 0xdb, 0x56,
+		  0x27, 0x22, 0xc6, 0x5c, 0x7f, 0x30, 0xa9, 0xb2 },
+		{ 0x82, 0xa2, 0x6f, 0x22, 0xbb, 0xa9, 0xe9, 0x48,
+		  0x8f, 0x94, 0x9a, 0x10, 0xd9, 0x8e, 0x9c, 0xc4 },
+		{ 0xcd, 0xe6, 0xb0, 0x27 },
+		{ 0x4b, 0xc2, 0x21, 0x2d },
+		{ 0xd8, 0xde, 0xbc, 0x4f, 0xfb, 0xcd, 0x60, 0xaa }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 10 */
+		{ 0x0d, 0xa6, 0xf7, 0xba, 0x86, 0xd5, 0xea, 0xc8,
+		  0xa1, 0x9c, 0xf5, 0x63, 0xac, 0x58, 0x64, 0x2d },
+		{ 0x67, 0x9a, 0xc4, 0xdb, 0xac, 0xd7, 0xd2, 0x33,
+		  0xff, 0x9d, 0x68, 0x06, 0xf4, 0x14, 0x9c, 0xe3 },
+		{ 0x0d, 0xb1, 0x07, 0x1f, 0x87, 0x67, 0x56, 0x2c,
+		  0xa4, 0x3a, 0x0a, 0x64, 0xc4, 0x1e, 0x8d, 0x08 },
+		{ 0x02, 0xd1, 0x3a, 0xcd },
+		{ 0x6f, 0xc3, 0x0f, 0xee },
+		{ 0xf0, 0xea, 0xa5, 0x0a, 0x1e, 0xdc, 0xeb, 0xb7 }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 11 */
+		{ 0x77, 0xb4, 0x58, 0x43, 0xc8, 0x8e, 0x58, 0xc1,
+		  0x0d, 0x20, 0x26, 0x84, 0x51, 0x5e, 0xd4, 0x30 },
+		{ 0x4c, 0x47, 0xeb, 0x30, 0x76, 0xdc, 0x55, 0xfe,
+		  0x51, 0x06, 0xcb, 0x20, 0x34, 0xb8, 0xcd, 0x78 },
+		{ 0xd4, 0x83, 0xaf, 0xae, 0x56, 0x24, 0x09, 0xa3,
+		  0x26, 0xb5, 0xbb, 0x0b, 0x20, 0xc4, 0xd7, 0x62 },
+		{ 0x44, 0x38, 0x9d, 0x01 },
+		{ 0xae, 0xfa, 0x35, 0x7b },
+		{ 0x82, 0xdb, 0xab, 0x7f, 0x83, 0xf0, 0x63, 0xda }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 12 */
+		{ 0x72, 0x9b, 0x17, 0x72, 0x92, 0x70, 0xdd, 0x87,
+		  0xcc, 0xdf, 0x1b, 0xfe, 0x29, 0xb4, 0xe9, 0xbb },
+		{ 0x31, 0x1c, 0x4c, 0x92, 0x97, 0x44, 0xd6, 0x75,
+		  0xb7, 0x20, 0xf3, 0xb7, 0xe9, 0xb1, 0xcb, 0xd0 },
+		{ 0x22, 0x8c, 0x2f, 0x2f, 0x06, 0xac, 0x32, 0x68,
+		  0xa9, 0xe6, 0x16, 0xee, 0x16, 0xdb, 0x4b, 0xa1 },
+		{ 0x03, 0xe0, 0xfd, 0x84 },
+		{ 0x98, 0xdb, 0xbd, 0x09 },
+		{ 0x3c, 0x66, 0xcb, 0x98, 0xca, 0xb2, 0xd3, 0x3d }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 13 */
+		{ 0xd3, 0x2d, 0xd2, 0x3e, 0x89, 0xdc, 0x66, 0x23,
+		  0x54, 0xca, 0x12, 0xeb, 0x79, 0xdd, 0x32, 0xfa },
+		{ 0xcf, 0x7d, 0x0a, 0xb1, 0xd9, 0x43, 0x06, 0x95,
+		  0x0b, 0xf1, 0x20, 0x18, 0xfb, 0xd4, 0x68, 0x87 },
+		{ 0xd2, 0x2a, 0x4b, 0x41, 0x80, 0xa5, 0x32, 0x57,
+		  0x08, 0xa5, 0xff, 0x70, 0xd9, 0xf6, 0x7e, 0xc7 },
+		{ 0xbe, 0x73, 0xb3, 0xdc },
+		{ 0xaf, 0x4a, 0x41, 0x1e },
+		{ 0x96, 0x12, 0xb5, 0xd8, 0x8a, 0x41, 0x30, 0xbb }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 14 */
+		{ 0xaf, 0x7c, 0x65, 0xe1, 0x92, 0x72, 0x21, 0xde,
+		  0x59, 0x11, 0x87, 0xa2, 0xc5, 0x98, 0x7a, 0x53 },
+		{ 0x1f, 0x0f, 0x85, 0x78, 0x46, 0x4f, 0xd5, 0x9b,
+		  0x64, 0xbe, 0xd2, 0xd0, 0x94, 0x36, 0xb5, 0x7a },
+		{ 0xa4, 0xcf, 0x5c, 0x81, 0x55, 0xc0, 0x8a, 0x7e,
+		  0xff, 0x41, 0x8e, 0x54, 0x43, 0xb9, 0x8e, 0x55 },
+		{ 0x8f, 0xe0, 0x19, 0xc7 },
+		{ 0x7b, 0xff, 0xa5, 0xc2 },
+		{ 0x75, 0xa1, 0x50, 0xdf, 0x3c, 0x6a, 0xed, 0x08 }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 15 */
+		{ 0x5b, 0xd7, 0xec, 0xd3, 0xd3, 0x12, 0x7a, 0x41,
+		  0xd1, 0x25, 0x39, 0xbe, 0xd4, 0xe7, 0xcf, 0x71 },
+		{ 0x59, 0xb7, 0x5f, 0x14, 0x25, 0x1c, 0x75, 0x03,
+		  0x1d, 0x0b, 0xcb, 0xac, 0x1c, 0x2c, 0x04, 0xc7 },
+		{ 0x76, 0x08, 0x9d, 0x3c, 0x0f, 0xf3, 0xef, 0xdc,
+		  0x6e, 0x36, 0x72, 0x1d, 0x4f, 0xce, 0xb7, 0x47 },
+		{ 0x27, 0x20, 0x2b, 0x82 },
+		{ 0x7e, 0x3f, 0x44, 0xc7 },
+		{ 0xb7, 0xf9, 0x2e, 0x42, 0x6a, 0x36, 0xfe, 0xc5 }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 16 */
+		{ 0x6c, 0xd1, 0xc6, 0xce, 0xb1, 0xe0, 0x1e, 0x14,
+		  0xf1, 0xb8, 0x23, 0x16, 0xa9, 0x0b, 0x7f, 0x3d },
+		{ 0xf6, 0x9b, 0x78, 0xf3, 0x00, 0xa0, 0x56, 0x8b,
+		  0xce, 0x9f, 0x0c, 0xb9, 0x3c, 0x4b, 0xe4, 0xc9 },
+		{ 0xa2, 0x19, 0xdc, 0x37, 0xf1, 0xdc, 0x7d, 0x66,
+		  0x73, 0x8b, 0x58, 0x43, 0xc7, 0x99, 0xf2, 0x06 },
+		{ 0xdd, 0xd7, 0xef, 0xe6 },
+		{ 0x70, 0xf6, 0xbd, 0xb9 },
+		{ 0x88, 0xd9, 0xde, 0x10, 0xa2, 0x20, 0x04, 0xc5 }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 17 */
+		{ 0xb7, 0x3a, 0x90, 0xcb, 0xcf, 0x3a, 0xfb, 0x62,
+		  0x2d, 0xba, 0x83, 0xc5, 0x8a, 0x84, 0x15, 0xdf },
+		{ 0xb1, 0x20, 0xf1, 0xc1, 0xa0, 0x10, 0x2a, 0x2f,
+		  0x50, 0x7d, 0xd5, 0x43, 0xde, 0x68, 0x28, 0x1f },
+		{ 0xdf, 0x0c, 0x67, 0x86, 0x8f, 0xa2, 0x5f, 0x74,
+		  0x8b, 0x70, 0x44, 0xc6, 0xe7, 0xc2, 0x45, 0xb8 },
+		{ 0x67, 0xe4, 0xff, 0x3f },
+		{ 0x47, 0x9d, 0xd2, 0x5c },
+		{ 0xa8, 0x19, 0xe5, 0x77, 0xa8, 0xd6, 0x17, 0x5b }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 18 */
+		{ 0x51, 0x22, 0x25, 0x02, 0x14, 0xc3, 0x3e, 0x72,
+		  0x3a, 0x5d, 0xd5, 0x23, 0xfc, 0x14, 0x5f, 0xc0 },
+		{ 0x81, 0xe9, 0x2b, 0x6c, 0x0e, 0xe0, 0xe1, 0x2e,
+		  0xbc, 0xeb, 0xa8, 0xd9, 0x2a, 0x99, 0xdf, 0xa5 },
+		{ 0x98, 0x1d, 0x46, 0x4c, 0x7c, 0x52, 0xeb, 0x6e,
+		  0x50, 0x36, 0x23, 0x49, 0x84, 0xad, 0x0b, 0xcf },
+		{ 0x8a, 0x3b, 0x8d, 0x17 },
+		{ 0x28, 0xd7, 0xb0, 0xf2 },
+		{ 0x9a, 0x8d, 0x0e, 0x88, 0x3f, 0xf0, 0x88, 0x7a }
+	}, {
+		/* 3GPP TS 55.205 v6.0.0 - Test Set 19 */
+		{ 0x90, 0xdc, 0xa4, 0xed, 0xa4, 0x5b, 0x53, 0xcf,
+		  0x0f, 0x12, 0xd7, 0xc9, 0xc3, 0xbc, 0x6a, 0x89 },
+		{ 0x9f, 0xdd, 0xc7, 0x20, 0x92, 0xc6, 0xad, 0x03,
+		  0x6b, 0x6e, 0x46, 0x47, 0x89, 0x31, 0x5b, 0x78 },
+		{ 0xcb, 0x9c, 0xcc, 0xc4, 0xb9, 0x25, 0x8e, 0x6d,
+		  0xca, 0x47, 0x60, 0x37, 0x9f, 0xb8, 0x25, 0x81 },
+		{ 0xdf, 0x58, 0x52, 0x2f },
+		{ 0xa9, 0x51, 0x00, 0xe2 },
+		{ 0xed, 0x29, 0xb2, 0xf1, 0xc2, 0x7f, 0x9f, 0x34 }
+	}
+};
+
+#define NUM_GSM_TESTS ARRAY_SIZE(gsm_test_sets)
+
+
+struct milenage_test_set {
+	u8 k[16];
+	u8 rand[16];
+	u8 sqn[6];
+	u8 amf[2];
+	u8 op[16];
+	u8 opc[16];
+	u8 f1[8];
+	u8 f1star[8];
+	u8 f2[8];
+	u8 f3[16];
+	u8 f4[16];
+	u8 f5[6];
+	u8 f5star[6];
+};
+
+static const struct milenage_test_set test_sets[] =
+{
+	{
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.1 Test Set 1 */
+		{ 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f,
+		  0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc },
+		{ 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d,
+		  0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 },
+		{ 0xff, 0x9b, 0xb4, 0xd0, 0xb6, 0x07 },
+		{ 0xb9, 0xb9 },
+		{ 0xcd, 0xc2, 0x02, 0xd5, 0x12, 0x3e, 0x20, 0xf6,
+		  0x2b, 0x6d, 0x67, 0x6a, 0xc7, 0x2c, 0xb3, 0x18 },
+		{ 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e,
+		  0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf },
+		{ 0x4a, 0x9f, 0xfa, 0xc3, 0x54, 0xdf, 0xaf, 0xb3 },
+		{ 0x01, 0xcf, 0xaf, 0x9e, 0xc4, 0xe8, 0x71, 0xe9 },
+		{ 0xa5, 0x42, 0x11, 0xd5, 0xe3, 0xba, 0x50, 0xbf },
+		{ 0xb4, 0x0b, 0xa9, 0xa3, 0xc5, 0x8b, 0x2a, 0x05,
+		  0xbb, 0xf0, 0xd9, 0x87, 0xb2, 0x1b, 0xf8, 0xcb },
+		{ 0xf7, 0x69, 0xbc, 0xd7, 0x51, 0x04, 0x46, 0x04,
+		  0x12, 0x76, 0x72, 0x71, 0x1c, 0x6d, 0x34, 0x41 },
+		{ 0xaa, 0x68, 0x9c, 0x64, 0x83, 0x70 },
+		{ 0x45, 0x1e, 0x8b, 0xec, 0xa4, 0x3b }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.2 Test Set 2 */
+		{ 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f,
+		  0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc },
+		{ 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d,
+		  0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 },
+		{ 0xff, 0x9b, 0xb4, 0xd0, 0xb6, 0x07 },
+		{ 0xb9, 0xb9 },
+		{ 0xcd, 0xc2, 0x02, 0xd5, 0x12, 0x3e, 0x20, 0xf6,
+		  0x2b, 0x6d, 0x67, 0x6a, 0xc7, 0x2c, 0xb3, 0x18 },
+		{ 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e,
+		  0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf },
+		{ 0x4a, 0x9f, 0xfa, 0xc3, 0x54, 0xdf, 0xaf, 0xb3 },
+		{ 0x01, 0xcf, 0xaf, 0x9e, 0xc4, 0xe8, 0x71, 0xe9 },
+		{ 0xa5, 0x42, 0x11, 0xd5, 0xe3, 0xba, 0x50, 0xbf },
+		{ 0xb4, 0x0b, 0xa9, 0xa3, 0xc5, 0x8b, 0x2a, 0x05,
+		  0xbb, 0xf0, 0xd9, 0x87, 0xb2, 0x1b, 0xf8, 0xcb },
+		{ 0xf7, 0x69, 0xbc, 0xd7, 0x51, 0x04, 0x46, 0x04,
+		  0x12, 0x76, 0x72, 0x71, 0x1c, 0x6d, 0x34, 0x41 },
+		{ 0xaa, 0x68, 0x9c, 0x64, 0x83, 0x70 },
+		{ 0x45, 0x1e, 0x8b, 0xec, 0xa4, 0x3b }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.3 Test Set 3 */
+		{ 0xfe, 0xc8, 0x6b, 0xa6, 0xeb, 0x70, 0x7e, 0xd0,
+		  0x89, 0x05, 0x75, 0x7b, 0x1b, 0xb4, 0x4b, 0x8f },
+		{ 0x9f, 0x7c, 0x8d, 0x02, 0x1a, 0xcc, 0xf4, 0xdb,
+		  0x21, 0x3c, 0xcf, 0xf0, 0xc7, 0xf7, 0x1a, 0x6a },
+		{ 0x9d, 0x02, 0x77, 0x59, 0x5f, 0xfc },
+		{ 0x72, 0x5c },
+		{ 0xdb, 0xc5, 0x9a, 0xdc, 0xb6, 0xf9, 0xa0, 0xef,
+		  0x73, 0x54, 0x77, 0xb7, 0xfa, 0xdf, 0x83, 0x74 },
+		{ 0x10, 0x06, 0x02, 0x0f, 0x0a, 0x47, 0x8b, 0xf6,
+		  0xb6, 0x99, 0xf1, 0x5c, 0x06, 0x2e, 0x42, 0xb3 },
+		{ 0x9c, 0xab, 0xc3, 0xe9, 0x9b, 0xaf, 0x72, 0x81 },
+		{ 0x95, 0x81, 0x4b, 0xa2, 0xb3, 0x04, 0x43, 0x24 },
+		{ 0x80, 0x11, 0xc4, 0x8c, 0x0c, 0x21, 0x4e, 0xd2 },
+		{ 0x5d, 0xbd, 0xbb, 0x29, 0x54, 0xe8, 0xf3, 0xcd,
+		  0xe6, 0x65, 0xb0, 0x46, 0x17, 0x9a, 0x50, 0x98 },
+		{ 0x59, 0xa9, 0x2d, 0x3b, 0x47, 0x6a, 0x04, 0x43,
+		  0x48, 0x70, 0x55, 0xcf, 0x88, 0xb2, 0x30, 0x7b },
+		{ 0x33, 0x48, 0x4d, 0xc2, 0x13, 0x6b },
+		{ 0xde, 0xac, 0xdd, 0x84, 0x8c, 0xc6 }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.4 Test Set 4 */
+		{ 0x9e, 0x59, 0x44, 0xae, 0xa9, 0x4b, 0x81, 0x16,
+		  0x5c, 0x82, 0xfb, 0xf9, 0xf3, 0x2d, 0xb7, 0x51 },
+		{ 0xce, 0x83, 0xdb, 0xc5, 0x4a, 0xc0, 0x27, 0x4a,
+		  0x15, 0x7c, 0x17, 0xf8, 0x0d, 0x01, 0x7b, 0xd6 },
+		{ 0x0b, 0x60, 0x4a, 0x81, 0xec, 0xa8 },
+		{ 0x9e, 0x09 },
+		{ 0x22, 0x30, 0x14, 0xc5, 0x80, 0x66, 0x94, 0xc0,
+		  0x07, 0xca, 0x1e, 0xee, 0xf5, 0x7f, 0x00, 0x4f },
+		{ 0xa6, 0x4a, 0x50, 0x7a, 0xe1, 0xa2, 0xa9, 0x8b,
+		  0xb8, 0x8e, 0xb4, 0x21, 0x01, 0x35, 0xdc, 0x87 },
+		{ 0x74, 0xa5, 0x82, 0x20, 0xcb, 0xa8, 0x4c, 0x49 },
+		{ 0xac, 0x2c, 0xc7, 0x4a, 0x96, 0x87, 0x18, 0x37 },
+		{ 0xf3, 0x65, 0xcd, 0x68, 0x3c, 0xd9, 0x2e, 0x96 },
+		{ 0xe2, 0x03, 0xed, 0xb3, 0x97, 0x15, 0x74, 0xf5,
+		  0xa9, 0x4b, 0x0d, 0x61, 0xb8, 0x16, 0x34, 0x5d },
+		{ 0x0c, 0x45, 0x24, 0xad, 0xea, 0xc0, 0x41, 0xc4,
+		  0xdd, 0x83, 0x0d, 0x20, 0x85, 0x4f, 0xc4, 0x6b },
+		{ 0xf0, 0xb9, 0xc0, 0x8a, 0xd0, 0x2e },
+		{ 0x60, 0x85, 0xa8, 0x6c, 0x6f, 0x63 }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.5 Test Set 5 */
+		{ 0x4a, 0xb1, 0xde, 0xb0, 0x5c, 0xa6, 0xce, 0xb0,
+		  0x51, 0xfc, 0x98, 0xe7, 0x7d, 0x02, 0x6a, 0x84 },
+		{ 0x74, 0xb0, 0xcd, 0x60, 0x31, 0xa1, 0xc8, 0x33,
+		  0x9b, 0x2b, 0x6c, 0xe2, 0xb8, 0xc4, 0xa1, 0x86 },
+		{ 0xe8, 0x80, 0xa1, 0xb5, 0x80, 0xb6 },
+		{ 0x9f, 0x07 },
+		{ 0x2d, 0x16, 0xc5, 0xcd, 0x1f, 0xdf, 0x6b, 0x22,
+		  0x38, 0x35, 0x84, 0xe3, 0xbe, 0xf2, 0xa8, 0xd8 },
+		{ 0xdc, 0xf0, 0x7c, 0xbd, 0x51, 0x85, 0x52, 0x90,
+		  0xb9, 0x2a, 0x07, 0xa9, 0x89, 0x1e, 0x52, 0x3e },
+		{ 0x49, 0xe7, 0x85, 0xdd, 0x12, 0x62, 0x6e, 0xf2 },
+		{ 0x9e, 0x85, 0x79, 0x03, 0x36, 0xbb, 0x3f, 0xa2 },
+		{ 0x58, 0x60, 0xfc, 0x1b, 0xce, 0x35, 0x1e, 0x7e },
+		{ 0x76, 0x57, 0x76, 0x6b, 0x37, 0x3d, 0x1c, 0x21,
+		  0x38, 0xf3, 0x07, 0xe3, 0xde, 0x92, 0x42, 0xf9 },
+		{ 0x1c, 0x42, 0xe9, 0x60, 0xd8, 0x9b, 0x8f, 0xa9,
+		  0x9f, 0x27, 0x44, 0xe0, 0x70, 0x8c, 0xcb, 0x53 },
+		{ 0x31, 0xe1, 0x1a, 0x60, 0x91, 0x18 },
+		{ 0xfe, 0x25, 0x55, 0xe5, 0x4a, 0xa9 }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.6 Test Set 6 */
+		{ 0x6c, 0x38, 0xa1, 0x16, 0xac, 0x28, 0x0c, 0x45,
+		  0x4f, 0x59, 0x33, 0x2e, 0xe3, 0x5c, 0x8c, 0x4f },
+		{ 0xee, 0x64, 0x66, 0xbc, 0x96, 0x20, 0x2c, 0x5a,
+		  0x55, 0x7a, 0xbb, 0xef, 0xf8, 0xba, 0xbf, 0x63 },
+		{ 0x41, 0x4b, 0x98, 0x22, 0x21, 0x81 },
+		{ 0x44, 0x64 },
+		{ 0x1b, 0xa0, 0x0a, 0x1a, 0x7c, 0x67, 0x00, 0xac,
+		  0x8c, 0x3f, 0xf3, 0xe9, 0x6a, 0xd0, 0x87, 0x25 },
+		{ 0x38, 0x03, 0xef, 0x53, 0x63, 0xb9, 0x47, 0xc6,
+		  0xaa, 0xa2, 0x25, 0xe5, 0x8f, 0xae, 0x39, 0x34 },
+		{ 0x07, 0x8a, 0xdf, 0xb4, 0x88, 0x24, 0x1a, 0x57 },
+		{ 0x80, 0x24, 0x6b, 0x8d, 0x01, 0x86, 0xbc, 0xf1 },
+		{ 0x16, 0xc8, 0x23, 0x3f, 0x05, 0xa0, 0xac, 0x28 },
+		{ 0x3f, 0x8c, 0x75, 0x87, 0xfe, 0x8e, 0x4b, 0x23,
+		  0x3a, 0xf6, 0x76, 0xae, 0xde, 0x30, 0xba, 0x3b },
+		{ 0xa7, 0x46, 0x6c, 0xc1, 0xe6, 0xb2, 0xa1, 0x33,
+		  0x7d, 0x49, 0xd3, 0xb6, 0x6e, 0x95, 0xd7, 0xb4 },
+		{ 0x45, 0xb0, 0xf6, 0x9a, 0xb0, 0x6c },
+		{ 0x1f, 0x53, 0xcd, 0x2b, 0x11, 0x13 }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.7 Test Set 7 */
+		{ 0x2d, 0x60, 0x9d, 0x4d, 0xb0, 0xac, 0x5b, 0xf0,
+		  0xd2, 0xc0, 0xde, 0x26, 0x70, 0x14, 0xde, 0x0d },
+		{ 0x19, 0x4a, 0xa7, 0x56, 0x01, 0x38, 0x96, 0xb7,
+		  0x4b, 0x4a, 0x2a, 0x3b, 0x0a, 0xf4, 0x53, 0x9e },
+		{ 0x6b, 0xf6, 0x94, 0x38, 0xc2, 0xe4 },
+		{ 0x5f, 0x67 },
+		{ 0x46, 0x0a, 0x48, 0x38, 0x54, 0x27, 0xaa, 0x39,
+		  0x26, 0x4a, 0xac, 0x8e, 0xfc, 0x9e, 0x73, 0xe8 },
+		{ 0xc3, 0x5a, 0x0a, 0xb0, 0xbc, 0xbf, 0xc9, 0x25,
+		  0x2c, 0xaf, 0xf1, 0x5f, 0x24, 0xef, 0xbd, 0xe0 },
+		{ 0xbd, 0x07, 0xd3, 0x00, 0x3b, 0x9e, 0x5c, 0xc3 },
+		{ 0xbc, 0xb6, 0xc2, 0xfc, 0xad, 0x15, 0x22, 0x50 },
+		{ 0x8c, 0x25, 0xa1, 0x6c, 0xd9, 0x18, 0xa1, 0xdf },
+		{ 0x4c, 0xd0, 0x84, 0x60, 0x20, 0xf8, 0xfa, 0x07,
+		  0x31, 0xdd, 0x47, 0xcb, 0xdc, 0x6b, 0xe4, 0x11 },
+		{ 0x88, 0xab, 0x80, 0xa4, 0x15, 0xf1, 0x5c, 0x73,
+		  0x71, 0x12, 0x54, 0xa1, 0xd3, 0x88, 0xf6, 0x96 },
+		{ 0x7e, 0x64, 0x55, 0xf3, 0x4c, 0xf3 },
+		{ 0xdc, 0x6d, 0xd0, 0x1e, 0x8f, 0x15 }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.8 Test Set 8 */
+		{ 0xa5, 0x30, 0xa7, 0xfe, 0x42, 0x8f, 0xad, 0x10,
+		  0x82, 0xc4, 0x5e, 0xdd, 0xfc, 0xe1, 0x38, 0x84 },
+		{ 0x3a, 0x4c, 0x2b, 0x32, 0x45, 0xc5, 0x0e, 0xb5,
+		  0xc7, 0x1d, 0x08, 0x63, 0x93, 0x95, 0x76, 0x4d },
+		{ 0xf6, 0x3f, 0x5d, 0x76, 0x87, 0x84 },
+		{ 0xb9, 0x0e },
+		{ 0x51, 0x1c, 0x6c, 0x4e, 0x83, 0xe3, 0x8c, 0x89,
+		  0xb1, 0xc5, 0xd8, 0xdd, 0xe6, 0x24, 0x26, 0xfa },
+		{ 0x27, 0x95, 0x3e, 0x49, 0xbc, 0x8a, 0xf6, 0xdc,
+		  0xc6, 0xe7, 0x30, 0xeb, 0x80, 0x28, 0x6b, 0xe3 },
+		{ 0x53, 0x76, 0x1f, 0xbd, 0x67, 0x9b, 0x0b, 0xad },
+		{ 0x21, 0xad, 0xfd, 0x33, 0x4a, 0x10, 0xe7, 0xce },
+		{ 0xa6, 0x32, 0x41, 0xe1, 0xff, 0xc3, 0xe5, 0xab },
+		{ 0x10, 0xf0, 0x5b, 0xab, 0x75, 0xa9, 0x9a, 0x5f,
+		  0xbb, 0x98, 0xa9, 0xc2, 0x87, 0x67, 0x9c, 0x3b },
+		{ 0xf9, 0xec, 0x08, 0x65, 0xeb, 0x32, 0xf2, 0x23,
+		  0x69, 0xca, 0xde, 0x40, 0xc5, 0x9c, 0x3a, 0x44 },
+		{ 0x88, 0x19, 0x6c, 0x47, 0x98, 0x6f },
+		{ 0xc9, 0x87, 0xa3, 0xd2, 0x31, 0x15 }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.9 Test Set 9 */
+		{ 0xd9, 0x15, 0x1c, 0xf0, 0x48, 0x96, 0xe2, 0x58,
+		  0x30, 0xbf, 0x2e, 0x08, 0x26, 0x7b, 0x83, 0x60 },
+		{ 0xf7, 0x61, 0xe5, 0xe9, 0x3d, 0x60, 0x3f, 0xeb,
+		  0x73, 0x0e, 0x27, 0x55, 0x6c, 0xb8, 0xa2, 0xca },
+		{ 0x47, 0xee, 0x01, 0x99, 0x82, 0x0a },
+		{ 0x91, 0x13 },
+		{ 0x75, 0xfc, 0x22, 0x33, 0xa4, 0x42, 0x94, 0xee,
+		  0x8e, 0x6d, 0xe2, 0x5c, 0x43, 0x53, 0xd2, 0x6b },
+		{ 0xc4, 0xc9, 0x3e, 0xff, 0xe8, 0xa0, 0x81, 0x38,
+		  0xc2, 0x03, 0xd4, 0xc2, 0x7c, 0xe4, 0xe3, 0xd9 },
+		{ 0x66, 0xcc, 0x4b, 0xe4, 0x48, 0x62, 0xaf, 0x1f },
+		{ 0x7a, 0x4b, 0x8d, 0x7a, 0x87, 0x53, 0xf2, 0x46 },
+		{ 0x4a, 0x90, 0xb2, 0x17, 0x1a, 0xc8, 0x3a, 0x76 },
+		{ 0x71, 0x23, 0x6b, 0x71, 0x29, 0xf9, 0xb2, 0x2a,
+		  0xb7, 0x7e, 0xa7, 0xa5, 0x4c, 0x96, 0xda, 0x22 },
+		{ 0x90, 0x52, 0x7e, 0xba, 0xa5, 0x58, 0x89, 0x68,
+		  0xdb, 0x41, 0x72, 0x73, 0x25, 0xa0, 0x4d, 0x9e },
+		{ 0x82, 0xa0, 0xf5, 0x28, 0x7a, 0x71 },
+		{ 0x52, 0x7d, 0xbf, 0x41, 0xf3, 0x5f }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.10 Test Set 10 */
+		{ 0xa0, 0xe2, 0x97, 0x1b, 0x68, 0x22, 0xe8, 0xd3,
+		  0x54, 0xa1, 0x8c, 0xc2, 0x35, 0x62, 0x4e, 0xcb },
+		{ 0x08, 0xef, 0xf8, 0x28, 0xb1, 0x3f, 0xdb, 0x56,
+		  0x27, 0x22, 0xc6, 0x5c, 0x7f, 0x30, 0xa9, 0xb2 },
+		{ 0xdb, 0x5c, 0x06, 0x64, 0x81, 0xe0 },
+		{ 0x71, 0x6b },
+		{ 0x32, 0x37, 0x92, 0xfa, 0xca, 0x21, 0xfb, 0x4d,
+		  0x5d, 0x6f, 0x13, 0xc1, 0x45, 0xa9, 0xd2, 0xc1 },
+		{ 0x82, 0xa2, 0x6f, 0x22, 0xbb, 0xa9, 0xe9, 0x48,
+		  0x8f, 0x94, 0x9a, 0x10, 0xd9, 0x8e, 0x9c, 0xc4 },
+		{ 0x94, 0x85, 0xfe, 0x24, 0x62, 0x1c, 0xb9, 0xf6 },
+		{ 0xbc, 0xe3, 0x25, 0xce, 0x03, 0xe2, 0xe9, 0xb9 },
+		{ 0x4b, 0xc2, 0x21, 0x2d, 0x86, 0x24, 0x91, 0x0a },
+		{ 0x08, 0xce, 0xf6, 0xd0, 0x04, 0xec, 0x61, 0x47,
+		  0x1a, 0x3c, 0x3c, 0xda, 0x04, 0x81, 0x37, 0xfa },
+		{ 0xed, 0x03, 0x18, 0xca, 0x5d, 0xeb, 0x92, 0x06,
+		  0x27, 0x2f, 0x6e, 0x8f, 0xa6, 0x4b, 0xa4, 0x11 },
+		{ 0xa2, 0xf8, 0x58, 0xaa, 0x9e, 0x5d },
+		{ 0x74, 0xe7, 0x6f, 0xbb, 0xec, 0x38 }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.11 Test Set 11 */
+		{ 0x0d, 0xa6, 0xf7, 0xba, 0x86, 0xd5, 0xea, 0xc8,
+		  0xa1, 0x9c, 0xf5, 0x63, 0xac, 0x58, 0x64, 0x2d },
+		{ 0x67, 0x9a, 0xc4, 0xdb, 0xac, 0xd7, 0xd2, 0x33,
+		  0xff, 0x9d, 0x68, 0x06, 0xf4, 0x14, 0x9c, 0xe3 },
+		{ 0x6e, 0x23, 0x31, 0xd6, 0x92, 0xad },
+		{ 0x22, 0x4a },
+		{ 0x4b, 0x9a, 0x26, 0xfa, 0x45, 0x9e, 0x3a, 0xcb,
+		  0xff, 0x36, 0xf4, 0x01, 0x5d, 0xe3, 0xbd, 0xc1 },
+		{ 0x0d, 0xb1, 0x07, 0x1f, 0x87, 0x67, 0x56, 0x2c,
+		  0xa4, 0x3a, 0x0a, 0x64, 0xc4, 0x1e, 0x8d, 0x08 },
+		{ 0x28, 0x31, 0xd7, 0xae, 0x90, 0x88, 0xe4, 0x92 },
+		{ 0x9b, 0x2e, 0x16, 0x95, 0x11, 0x35, 0xd5, 0x23 },
+		{ 0x6f, 0xc3, 0x0f, 0xee, 0x6d, 0x12, 0x35, 0x23 },
+		{ 0x69, 0xb1, 0xca, 0xe7, 0xc7, 0x42, 0x9d, 0x97,
+		  0x5e, 0x24, 0x5c, 0xac, 0xb0, 0x5a, 0x51, 0x7c },
+		{ 0x74, 0xf2, 0x4e, 0x8c, 0x26, 0xdf, 0x58, 0xe1,
+		  0xb3, 0x8d, 0x7d, 0xcd, 0x4f, 0x1b, 0x7f, 0xbd },
+		{ 0x4c, 0x53, 0x9a, 0x26, 0xe1, 0xfa },
+		{ 0x07, 0x86, 0x1e, 0x12, 0x69, 0x28 }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.12 Test Set 12 */
+		{ 0x77, 0xb4, 0x58, 0x43, 0xc8, 0x8e, 0x58, 0xc1,
+		  0x0d, 0x20, 0x26, 0x84, 0x51, 0x5e, 0xd4, 0x30 },
+		{ 0x4c, 0x47, 0xeb, 0x30, 0x76, 0xdc, 0x55, 0xfe,
+		  0x51, 0x06, 0xcb, 0x20, 0x34, 0xb8, 0xcd, 0x78 },
+		{ 0xfe, 0x1a, 0x87, 0x31, 0x00, 0x5d },
+		{ 0xad, 0x25 },
+		{ 0xbf, 0x32, 0x86, 0xc7, 0xa5, 0x14, 0x09, 0xce,
+		  0x95, 0x72, 0x4d, 0x50, 0x3b, 0xfe, 0x6e, 0x70 },
+		{ 0xd4, 0x83, 0xaf, 0xae, 0x56, 0x24, 0x09, 0xa3,
+		  0x26, 0xb5, 0xbb, 0x0b, 0x20, 0xc4, 0xd7, 0x62 },
+		{ 0x08, 0x33, 0x2d, 0x7e, 0x9f, 0x48, 0x45, 0x70 },
+		{ 0xed, 0x41, 0xb7, 0x34, 0x48, 0x9d, 0x52, 0x07 },
+		{ 0xae, 0xfa, 0x35, 0x7b, 0xea, 0xc2, 0xa8, 0x7a },
+		{ 0x90, 0x8c, 0x43, 0xf0, 0x56, 0x9c, 0xb8, 0xf7,
+		  0x4b, 0xc9, 0x71, 0xe7, 0x06, 0xc3, 0x6c, 0x5f },
+		{ 0xc2, 0x51, 0xdf, 0x0d, 0x88, 0x8d, 0xd9, 0x32,
+		  0x9b, 0xcf, 0x46, 0x65, 0x5b, 0x22, 0x6e, 0x40 },
+		{ 0x30, 0xff, 0x25, 0xcd, 0xad, 0xf6 },
+		{ 0xe8, 0x4e, 0xd0, 0xd4, 0x67, 0x7e }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.13 Test Set 13 */
+		{ 0x72, 0x9b, 0x17, 0x72, 0x92, 0x70, 0xdd, 0x87,
+		  0xcc, 0xdf, 0x1b, 0xfe, 0x29, 0xb4, 0xe9, 0xbb },
+		{ 0x31, 0x1c, 0x4c, 0x92, 0x97, 0x44, 0xd6, 0x75,
+		  0xb7, 0x20, 0xf3, 0xb7, 0xe9, 0xb1, 0xcb, 0xd0 },
+		{ 0xc8, 0x5c, 0x4c, 0xf6, 0x59, 0x16 },
+		{ 0x5b, 0xb2 },
+		{ 0xd0, 0x4c, 0x9c, 0x35, 0xbd, 0x22, 0x62, 0xfa,
+		  0x81, 0x0d, 0x29, 0x24, 0xd0, 0x36, 0xfd, 0x13 },
+		{ 0x22, 0x8c, 0x2f, 0x2f, 0x06, 0xac, 0x32, 0x68,
+		  0xa9, 0xe6, 0x16, 0xee, 0x16, 0xdb, 0x4b, 0xa1 },
+		{ 0xff, 0x79, 0x4f, 0xe2, 0xf8, 0x27, 0xeb, 0xf8 },
+		{ 0x24, 0xfe, 0x4d, 0xc6, 0x1e, 0x87, 0x4b, 0x52 },
+		{ 0x98, 0xdb, 0xbd, 0x09, 0x9b, 0x3b, 0x40, 0x8d },
+		{ 0x44, 0xc0, 0xf2, 0x3c, 0x54, 0x93, 0xcf, 0xd2,
+		  0x41, 0xe4, 0x8f, 0x19, 0x7e, 0x1d, 0x10, 0x12 },
+		{ 0x0c, 0x9f, 0xb8, 0x16, 0x13, 0x88, 0x4c, 0x25,
+		  0x35, 0xdd, 0x0e, 0xab, 0xf3, 0xb4, 0x40, 0xd8 },
+		{ 0x53, 0x80, 0xd1, 0x58, 0xcf, 0xe3 },
+		{ 0x87, 0xac, 0x3b, 0x55, 0x9f, 0xb6 }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.14 Test Set 14 */
+		{ 0xd3, 0x2d, 0xd2, 0x3e, 0x89, 0xdc, 0x66, 0x23,
+		  0x54, 0xca, 0x12, 0xeb, 0x79, 0xdd, 0x32, 0xfa },
+		{ 0xcf, 0x7d, 0x0a, 0xb1, 0xd9, 0x43, 0x06, 0x95,
+		  0x0b, 0xf1, 0x20, 0x18, 0xfb, 0xd4, 0x68, 0x87 },
+		{ 0x48, 0x41, 0x07, 0xe5, 0x6a, 0x43 },
+		{ 0xb5, 0xe6 },
+		{ 0xfe, 0x75, 0x90, 0x5b, 0x9d, 0xa4, 0x7d, 0x35,
+		  0x62, 0x36, 0xd0, 0x31, 0x4e, 0x09, 0xc3, 0x2e },
+		{ 0xd2, 0x2a, 0x4b, 0x41, 0x80, 0xa5, 0x32, 0x57,
+		  0x08, 0xa5, 0xff, 0x70, 0xd9, 0xf6, 0x7e, 0xc7 },
+		{ 0xcf, 0x19, 0xd6, 0x2b, 0x6a, 0x80, 0x98, 0x66 },
+		{ 0x5d, 0x26, 0x95, 0x37, 0xe4, 0x5e, 0x2c, 0xe6 },
+		{ 0xaf, 0x4a, 0x41, 0x1e, 0x11, 0x39, 0xf2, 0xc2 },
+		{ 0x5a, 0xf8, 0x6b, 0x80, 0xed, 0xb7, 0x0d, 0xf5,
+		  0x29, 0x2c, 0xc1, 0x12, 0x1c, 0xba, 0xd5, 0x0c },
+		{ 0x7f, 0x4d, 0x6a, 0xe7, 0x44, 0x0e, 0x18, 0x78,
+		  0x9a, 0x8b, 0x75, 0xad, 0x3f, 0x42, 0xf0, 0x3a },
+		{ 0x21, 0x7a, 0xf4, 0x92, 0x72, 0xad },
+		{ 0x90, 0x0e, 0x10, 0x1c, 0x67, 0x7e }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.15 Test Set 15 */
+		{ 0xaf, 0x7c, 0x65, 0xe1, 0x92, 0x72, 0x21, 0xde,
+		  0x59, 0x11, 0x87, 0xa2, 0xc5, 0x98, 0x7a, 0x53 },
+		{ 0x1f, 0x0f, 0x85, 0x78, 0x46, 0x4f, 0xd5, 0x9b,
+		  0x64, 0xbe, 0xd2, 0xd0, 0x94, 0x36, 0xb5, 0x7a },
+		{ 0x3d, 0x62, 0x7b, 0x01, 0x41, 0x8d },
+		{ 0x84, 0xf6 },
+		{ 0x0c, 0x7a, 0xcb, 0x8d, 0x95, 0xb7, 0xd4, 0xa3,
+		  0x1c, 0x5a, 0xca, 0x6d, 0x26, 0x34, 0x5a, 0x88 },
+		{ 0xa4, 0xcf, 0x5c, 0x81, 0x55, 0xc0, 0x8a, 0x7e,
+		  0xff, 0x41, 0x8e, 0x54, 0x43, 0xb9, 0x8e, 0x55 },
+		{ 0xc3, 0x7c, 0xae, 0x78, 0x05, 0x64, 0x20, 0x32 },
+		{ 0x68, 0xcd, 0x09, 0xa4, 0x52, 0xd8, 0xdb, 0x7c },
+		{ 0x7b, 0xff, 0xa5, 0xc2, 0xf4, 0x1f, 0xbc, 0x05 },
+		{ 0x3f, 0x8c, 0x3f, 0x3c, 0xcf, 0x76, 0x25, 0xbf,
+		  0x77, 0xfc, 0x94, 0xbc, 0xfd, 0x22, 0xfd, 0x26 },
+		{ 0xab, 0xcb, 0xae, 0x8f, 0xd4, 0x61, 0x15, 0xe9,
+		  0x96, 0x1a, 0x55, 0xd0, 0xda, 0x5f, 0x20, 0x78 },
+		{ 0x83, 0x7f, 0xd7, 0xb7, 0x44, 0x19 },
+		{ 0x56, 0xe9, 0x7a, 0x60, 0x90, 0xb1 }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.16 Test Set 16 */
+		{ 0x5b, 0xd7, 0xec, 0xd3, 0xd3, 0x12, 0x7a, 0x41,
+		  0xd1, 0x25, 0x39, 0xbe, 0xd4, 0xe7, 0xcf, 0x71 },
+		{ 0x59, 0xb7, 0x5f, 0x14, 0x25, 0x1c, 0x75, 0x03,
+		  0x1d, 0x0b, 0xcb, 0xac, 0x1c, 0x2c, 0x04, 0xc7 },
+		{ 0xa2, 0x98, 0xae, 0x89, 0x29, 0xdc },
+		{ 0xd0, 0x56 },
+		{ 0xf9, 0x67, 0xf7, 0x60, 0x38, 0xb9, 0x20, 0xa9,
+		  0xcd, 0x25, 0xe1, 0x0c, 0x08, 0xb4, 0x99, 0x24 },
+		{ 0x76, 0x08, 0x9d, 0x3c, 0x0f, 0xf3, 0xef, 0xdc,
+		  0x6e, 0x36, 0x72, 0x1d, 0x4f, 0xce, 0xb7, 0x47 },
+		{ 0xc3, 0xf2, 0x5c, 0xd9, 0x43, 0x09, 0x10, 0x7e },
+		{ 0xb0, 0xc8, 0xba, 0x34, 0x36, 0x65, 0xaf, 0xcc },
+		{ 0x7e, 0x3f, 0x44, 0xc7, 0x59, 0x1f, 0x6f, 0x45 },
+		{ 0xd4, 0x2b, 0x2d, 0x61, 0x5e, 0x49, 0xa0, 0x3a,
+		  0xc2, 0x75, 0xa5, 0xae, 0xf9, 0x7a, 0xf8, 0x92 },
+		{ 0x0b, 0x3f, 0x8d, 0x02, 0x4f, 0xe6, 0xbf, 0xaf,
+		  0xaa, 0x98, 0x2b, 0x8f, 0x82, 0xe3, 0x19, 0xc2 },
+		{ 0x5b, 0xe1, 0x14, 0x95, 0x52, 0x5d },
+		{ 0x4d, 0x6a, 0x34, 0xa1, 0xe4, 0xeb }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.17 Test Set 17 */
+		{ 0x6c, 0xd1, 0xc6, 0xce, 0xb1, 0xe0, 0x1e, 0x14,
+		  0xf1, 0xb8, 0x23, 0x16, 0xa9, 0x0b, 0x7f, 0x3d },
+		{ 0xf6, 0x9b, 0x78, 0xf3, 0x00, 0xa0, 0x56, 0x8b,
+		  0xce, 0x9f, 0x0c, 0xb9, 0x3c, 0x4b, 0xe4, 0xc9 },
+		{ 0xb4, 0xfc, 0xe5, 0xfe, 0xb0, 0x59 },
+		{ 0xe4, 0xbb },
+		{ 0x07, 0x8b, 0xfc, 0xa9, 0x56, 0x46, 0x59, 0xec,
+		  0xd8, 0x85, 0x1e, 0x84, 0xe6, 0xc5, 0x9b, 0x48 },
+		{ 0xa2, 0x19, 0xdc, 0x37, 0xf1, 0xdc, 0x7d, 0x66,
+		  0x73, 0x8b, 0x58, 0x43, 0xc7, 0x99, 0xf2, 0x06 },
+		{ 0x69, 0xa9, 0x08, 0x69, 0xc2, 0x68, 0xcb, 0x7b },
+		{ 0x2e, 0x0f, 0xdc, 0xf9, 0xfd, 0x1c, 0xfa, 0x6a },
+		{ 0x70, 0xf6, 0xbd, 0xb9, 0xad, 0x21, 0x52, 0x5f },
+		{ 0x6e, 0xda, 0xf9, 0x9e, 0x5b, 0xd9, 0xf8, 0x5d,
+		  0x5f, 0x36, 0xd9, 0x1c, 0x12, 0x72, 0xfb, 0x4b },
+		{ 0xd6, 0x1c, 0x85, 0x3c, 0x28, 0x0d, 0xd9, 0xc4,
+		  0x6f, 0x29, 0x7b, 0xae, 0xc3, 0x86, 0xde, 0x17 },
+		{ 0x1c, 0x40, 0x8a, 0x85, 0x8b, 0x3e },
+		{ 0xaa, 0x4a, 0xe5, 0x2d, 0xaa, 0x30 }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.18 Test Set 18 */
+		{ 0xb7, 0x3a, 0x90, 0xcb, 0xcf, 0x3a, 0xfb, 0x62,
+		  0x2d, 0xba, 0x83, 0xc5, 0x8a, 0x84, 0x15, 0xdf },
+		{ 0xb1, 0x20, 0xf1, 0xc1, 0xa0, 0x10, 0x2a, 0x2f,
+		  0x50, 0x7d, 0xd5, 0x43, 0xde, 0x68, 0x28, 0x1f },
+		{ 0xf1, 0xe8, 0xa5, 0x23, 0xa3, 0x6d },
+		{ 0x47, 0x1b },
+		{ 0xb6, 0x72, 0x04, 0x7e, 0x00, 0x3b, 0xb9, 0x52,
+		  0xdc, 0xa6, 0xcb, 0x8a, 0xf0, 0xe5, 0xb7, 0x79 },
+		{ 0xdf, 0x0c, 0x67, 0x86, 0x8f, 0xa2, 0x5f, 0x74,
+		  0x8b, 0x70, 0x44, 0xc6, 0xe7, 0xc2, 0x45, 0xb8 },
+		{ 0xeb, 0xd7, 0x03, 0x41, 0xbc, 0xd4, 0x15, 0xb0 },
+		{ 0x12, 0x35, 0x9f, 0x5d, 0x82, 0x22, 0x0c, 0x14 },
+		{ 0x47, 0x9d, 0xd2, 0x5c, 0x20, 0x79, 0x2d, 0x63 },
+		{ 0x66, 0x19, 0x5d, 0xbe, 0xd0, 0x31, 0x32, 0x74,
+		  0xc5, 0xca, 0x77, 0x66, 0x61, 0x5f, 0xa2, 0x5e },
+		{ 0x66, 0xbe, 0xc7, 0x07, 0xeb, 0x2a, 0xfc, 0x47,
+		  0x6d, 0x74, 0x08, 0xa8, 0xf2, 0x92, 0x7b, 0x36 },
+		{ 0xae, 0xfd, 0xaa, 0x5d, 0xdd, 0x99 },
+		{ 0x12, 0xec, 0x2b, 0x87, 0xfb, 0xb1 }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.19 Test Set 19 */
+		{ 0x51, 0x22, 0x25, 0x02, 0x14, 0xc3, 0x3e, 0x72,
+		  0x3a, 0x5d, 0xd5, 0x23, 0xfc, 0x14, 0x5f, 0xc0 },
+		{ 0x81, 0xe9, 0x2b, 0x6c, 0x0e, 0xe0, 0xe1, 0x2e,
+		  0xbc, 0xeb, 0xa8, 0xd9, 0x2a, 0x99, 0xdf, 0xa5 },
+		{ 0x16, 0xf3, 0xb3, 0xf7, 0x0f, 0xc2 },
+		{ 0xc3, 0xab },
+		{ 0xc9, 0xe8, 0x76, 0x32, 0x86, 0xb5, 0xb9, 0xff,
+		  0xbd, 0xf5, 0x6e, 0x12, 0x97, 0xd0, 0x88, 0x7b },
+		{ 0x98, 0x1d, 0x46, 0x4c, 0x7c, 0x52, 0xeb, 0x6e,
+		  0x50, 0x36, 0x23, 0x49, 0x84, 0xad, 0x0b, 0xcf },
+		{ 0x2a, 0x5c, 0x23, 0xd1, 0x5e, 0xe3, 0x51, 0xd5 },
+		{ 0x62, 0xda, 0xe3, 0x85, 0x3f, 0x3a, 0xf9, 0xd2 },
+		{ 0x28, 0xd7, 0xb0, 0xf2, 0xa2, 0xec, 0x3d, 0xe5 },
+		{ 0x53, 0x49, 0xfb, 0xe0, 0x98, 0x64, 0x9f, 0x94,
+		  0x8f, 0x5d, 0x2e, 0x97, 0x3a, 0x81, 0xc0, 0x0f },
+		{ 0x97, 0x44, 0x87, 0x1a, 0xd3, 0x2b, 0xf9, 0xbb,
+		  0xd1, 0xdd, 0x5c, 0xe5, 0x4e, 0x3e, 0x2e, 0x5a },
+		{ 0xad, 0xa1, 0x5a, 0xeb, 0x7b, 0xb8 },
+		{ 0xd4, 0x61, 0xbc, 0x15, 0x47, 0x5d }
+	}, {
+		/* 3GPP TS 35.208 v6.0.0 - 4.3.20 Test Set 20 */
+		{ 0x90, 0xdc, 0xa4, 0xed, 0xa4, 0x5b, 0x53, 0xcf,
+		  0x0f, 0x12, 0xd7, 0xc9, 0xc3, 0xbc, 0x6a, 0x89 },
+		{ 0x9f, 0xdd, 0xc7, 0x20, 0x92, 0xc6, 0xad, 0x03,
+		  0x6b, 0x6e, 0x46, 0x47, 0x89, 0x31, 0x5b, 0x78 },
+		{ 0x20, 0xf8, 0x13, 0xbd, 0x41, 0x41 },
+		{ 0x61, 0xdf },
+		{ 0x3f, 0xfc, 0xfe, 0x5b, 0x7b, 0x11, 0x11, 0x58,
+		  0x99, 0x20, 0xd3, 0x52, 0x8e, 0x84, 0xe6, 0x55 },
+		{ 0xcb, 0x9c, 0xcc, 0xc4, 0xb9, 0x25, 0x8e, 0x6d,
+		  0xca, 0x47, 0x60, 0x37, 0x9f, 0xb8, 0x25, 0x81 },
+		{ 0x09, 0xdb, 0x94, 0xea, 0xb4, 0xf8, 0x14, 0x9e },
+		{ 0xa2, 0x94, 0x68, 0xaa, 0x97, 0x75, 0xb5, 0x27 },
+		{ 0xa9, 0x51, 0x00, 0xe2, 0x76, 0x09, 0x52, 0xcd },
+		{ 0xb5, 0xf2, 0xda, 0x03, 0x88, 0x3b, 0x69, 0xf9,
+		  0x6b, 0xf5, 0x2e, 0x02, 0x9e, 0xd9, 0xac, 0x45 },
+		{ 0xb4, 0x72, 0x13, 0x68, 0xbc, 0x16, 0xea, 0x67,
+		  0x87, 0x5c, 0x55, 0x98, 0x68, 0x8b, 0xb0, 0xef },
+		{ 0x83, 0xcf, 0xd5, 0x4d, 0xb9, 0x13 },
+		{ 0x4f, 0x20, 0x39, 0x39, 0x2d, 0xdc }
+	}
+};
+
+#define NUM_TESTS ARRAY_SIZE(test_sets)
+
+
+int main(int argc, char *argv[])
+{
+	u8 buf[16], buf2[16], buf3[16], buf4[16], buf5[16], opc[16];
+	u8 auts[14], sqn[6], _rand[16];
+	int ret = 0, res, i;
+	const struct milenage_test_set *t;
+	size_t res_len;
+
+	wpa_debug_level = 0;
+
+	printf("Milenage test sets\n");
+	for (i = 0; i < NUM_TESTS; i++) {
+		t = &test_sets[i];
+		printf("Test Set %d\n", i + 1);
+
+		milenage_opc(t->op, t->k, opc);
+		if (memcmp(opc, t->opc, 16) != 0) {
+			printf("- milenage_opc failed\n");
+			ret++;
+		}
+
+		if (milenage_f1(opc, t->k, t->rand, t->sqn, t->amf, buf, buf2)
+		    ||  memcmp(buf, t->f1, 8) != 0) {
+			printf("- milenage_f1 failed\n");
+			ret++;
+		}
+		if (memcmp(buf2, t->f1star, 8) != 0) {
+			printf("- milenage_f1* failed\n");
+			ret++;
+		}
+
+		if (milenage_f2345(opc, t->k, t->rand, buf, buf2, buf3, buf4,
+				   buf5) ||
+		    memcmp(buf, t->f2, 8) != 0) {
+			printf("- milenage_f2 failed\n");
+			ret++;
+		}
+		if (memcmp(buf2, t->f3, 16) != 0) {
+			printf("- milenage_f3 failed\n");
+			ret++;
+		}
+		if (memcmp(buf3, t->f4, 16) != 0) {
+			printf("- milenage_f4 failed\n");
+			ret++;
+		}
+		if (memcmp(buf4, t->f5, 6) != 0) {
+			printf("- milenage_f5 failed\n");
+			ret++;
+		}
+		if (memcmp(buf5, t->f5star, 6) != 0) {
+			printf("- milenage_f5* failed\n");
+			ret++;
+		}
+	}
+
+	printf("milenage_auts test:\n");
+	os_memcpy(auts, "\x4f\x20\x39\x39\x2d\xdd", 6);
+	os_memcpy(auts + 6, "\x4b\xb4\x31\x6e\xd4\xa1\x46\x88", 8);
+	res = milenage_auts(t->opc, t->k, t->rand, auts, buf);
+	printf("AUTS for test set %d: %d / SQN=%02x%02x%02x%02x%02x%02x\n",
+	       i, res, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+	if (res)
+		ret++;
+
+	os_memset(_rand, 0xaa, sizeof(_rand));
+	os_memcpy(auts,
+		  "\x43\x68\x1a\xd3\xda\xf0\x06\xbc\xde\x40\x5a\x20\x72\x67",
+		  14);
+	res = milenage_auts(t->opc, t->k, _rand, auts, buf);
+	printf("AUTS from a test USIM: %d / SQN=%02x%02x%02x%02x%02x%02x\n",
+	       res, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+	if (res)
+		ret++;
+
+	printf("milenage_generate test:\n");
+	os_memcpy(sqn, "\x00\x00\x00\x00\x40\x44", 6);
+	os_memcpy(_rand, "\x12\x69\xb8\x23\x41\x39\x35\x66\xfb\x99\x41\xe9\x84"
+		  "\x4f\xe6\x2f", 16);
+	res_len = 8;
+	milenage_generate(t->opc, t->amf, t->k, sqn, _rand, buf, buf2, buf3,
+			  buf4, &res_len);
+	wpa_hexdump(MSG_DEBUG, "SQN", sqn, 6);
+	wpa_hexdump(MSG_DEBUG, "RAND", _rand, 16);
+	wpa_hexdump(MSG_DEBUG, "AUTN", buf, 16);
+	wpa_hexdump(MSG_DEBUG, "IK", buf2, 16);
+	wpa_hexdump(MSG_DEBUG, "CK", buf3, 16);
+	wpa_hexdump(MSG_DEBUG, "RES", buf4, res_len);
+
+	printf("GSM-Milenage test sets\n");
+	for (i = 0; i < NUM_GSM_TESTS; i++) {
+		const struct gsm_milenage_test_set *g;
+		u8 sres[4], kc[8];
+		g = &gsm_test_sets[i];
+		printf("Test Set %d\n", i + 1);
+		gsm_milenage(g->opc, g->ki, g->rand, sres, kc);
+		if (memcmp(g->kc, kc, 8) != 0) {
+			printf("- gsm_milenage Kc failed\n");
+			ret++;
+		}
+#ifdef GSM_MILENAGE_ALT_SRES
+		if (memcmp(g->sres2, sres, 4) != 0) {
+			printf("- gsm_milenage SRES#2 failed\n");
+			ret++;
+		}
+#else /* GSM_MILENAGE_ALT_SRES */
+		if (memcmp(g->sres1, sres, 4) != 0) {
+			printf("- gsm_milenage SRES#1 failed\n");
+			ret++;
+		}
+#endif /* GSM_MILENAGE_ALT_SRES */
+	}
+
+	if (ret)
+		printf("Something failed\n");
+	else
+		printf("OK\n");
+
+	return ret;
+}
diff --git a/hostap/tests/test-rc4.c b/hostap/tests/test-rc4.c
new file mode 100644
index 0000000..99f5592
--- /dev/null
+++ b/hostap/tests/test-rc4.c
@@ -0,0 +1,250 @@
+/*
+ * Test program for RC4
+ * Copyright (c) 2011, 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/crypto.h"
+
+
+struct rc4_test_vector {
+	size_t key_len;
+	const u8 *key;
+	const u8 *stream0;
+	const u8 *stream240;
+	const u8 *stream496;
+	const u8 *stream752;
+	const u8 *stream1008;
+	const u8 *stream1520;
+	const u8 *stream2032;
+	const u8 *stream3056;
+	const u8 *stream4080;
+};
+
+/* RFC 6229 test vectors */
+static const struct rc4_test_vector tests[] = {
+	{
+		5, (u8 *) "\x01\x02\x03\x04\x05",
+		(u8 *) "\xb2\x39\x63\x05\xf0\x3d\xc0\x27\xcc\xc3\x52\x4a\x0a\x11\x18\xa8\x69\x82\x94\x4f\x18\xfc\x82\xd5\x89\xc4\x03\xa4\x7a\x0d\x09\x19",
+		(u8 *) "\x28\xcb\x11\x32\xc9\x6c\xe2\x86\x42\x1d\xca\xad\xb8\xb6\x9e\xae\x1c\xfc\xf6\x2b\x03\xed\xdb\x64\x1d\x77\xdf\xcf\x7f\x8d\x8c\x93",
+		(u8 *) "\x42\xb7\xd0\xcd\xd9\x18\xa8\xa3\x3d\xd5\x17\x81\xc8\x1f\x40\x41\x64\x59\x84\x44\x32\xa7\xda\x92\x3c\xfb\x3e\xb4\x98\x06\x61\xf6",
+		(u8 *) "\xec\x10\x32\x7b\xde\x2b\xee\xfd\x18\xf9\x27\x76\x80\x45\x7e\x22\xeb\x62\x63\x8d\x4f\x0b\xa1\xfe\x9f\xca\x20\xe0\x5b\xf8\xff\x2b",
+		(u8 *) "\x45\x12\x90\x48\xe6\xa0\xed\x0b\x56\xb4\x90\x33\x8f\x07\x8d\xa5\x30\xab\xbc\xc7\xc2\x0b\x01\x60\x9f\x23\xee\x2d\x5f\x6b\xb7\xdf",
+		(u8 *) "\x32\x94\xf7\x44\xd8\xf9\x79\x05\x07\xe7\x0f\x62\xe5\xbb\xce\xea\xd8\x72\x9d\xb4\x18\x82\x25\x9b\xee\x4f\x82\x53\x25\xf5\xa1\x30",
+		(u8 *) "\x1e\xb1\x4a\x0c\x13\xb3\xbf\x47\xfa\x2a\x0b\xa9\x3a\xd4\x5b\x8b\xcc\x58\x2f\x8b\xa9\xf2\x65\xe2\xb1\xbe\x91\x12\xe9\x75\xd2\xd7",
+		(u8 *) "\xf2\xe3\x0f\x9b\xd1\x02\xec\xbf\x75\xaa\xad\xe9\xbc\x35\xc4\x3c\xec\x0e\x11\xc4\x79\xdc\x32\x9d\xc8\xda\x79\x68\xfe\x96\x56\x81",
+		(u8 *) "\x06\x83\x26\xa2\x11\x84\x16\xd2\x1f\x9d\x04\xb2\xcd\x1c\xa0\x50\xff\x25\xb5\x89\x95\x99\x67\x07\xe5\x1f\xbd\xf0\x8b\x34\xd8\x75"
+	},
+	{
+		7, (u8 *) "\x01\x02\x03\x04\x05\x06\x07",
+		(u8 *) "\x29\x3f\x02\xd4\x7f\x37\xc9\xb6\x33\xf2\xaf\x52\x85\xfe\xb4\x6b\xe6\x20\xf1\x39\x0d\x19\xbd\x84\xe2\xe0\xfd\x75\x20\x31\xaf\xc1",
+		(u8 *) "\x91\x4f\x02\x53\x1c\x92\x18\x81\x0d\xf6\x0f\x67\xe3\x38\x15\x4c\xd0\xfd\xb5\x83\x07\x3c\xe8\x5a\xb8\x39\x17\x74\x0e\xc0\x11\xd5",
+		(u8 *) "\x75\xf8\x14\x11\xe8\x71\xcf\xfa\x70\xb9\x0c\x74\xc5\x92\xe4\x54\x0b\xb8\x72\x02\x93\x8d\xad\x60\x9e\x87\xa5\xa1\xb0\x79\xe5\xe4",
+		(u8 *) "\xc2\x91\x12\x46\xb6\x12\xe7\xe7\xb9\x03\xdf\xed\xa1\xda\xd8\x66\x32\x82\x8f\x91\x50\x2b\x62\x91\x36\x8d\xe8\x08\x1d\xe3\x6f\xc2",
+		(u8 *) "\xf3\xb9\xa7\xe3\xb2\x97\xbf\x9a\xd8\x04\x51\x2f\x90\x63\xef\xf1\x8e\xcb\x67\xa9\xba\x1f\x55\xa5\xa0\x67\xe2\xb0\x26\xa3\x67\x6f",
+		(u8 *) "\xd2\xaa\x90\x2b\xd4\x2d\x0d\x7c\xfd\x34\x0c\xd4\x58\x10\x52\x9f\x78\xb2\x72\xc9\x6e\x42\xea\xb4\xc6\x0b\xd9\x14\xe3\x9d\x06\xe3",
+		(u8 *) "\xf4\x33\x2f\xd3\x1a\x07\x93\x96\xee\x3c\xee\x3f\x2a\x4f\xf0\x49\x05\x45\x97\x81\xd4\x1f\xda\x7f\x30\xc1\xbe\x7e\x12\x46\xc6\x23",
+		(u8 *) "\xad\xfd\x38\x68\xb8\xe5\x14\x85\xd5\xe6\x10\x01\x7e\x3d\xd6\x09\xad\x26\x58\x1c\x0c\x5b\xe4\x5f\x4c\xea\x01\xdb\x2f\x38\x05\xd5",
+		(u8 *) "\xf3\x17\x2c\xef\xfc\x3b\x3d\x99\x7c\x85\xcc\xd5\xaf\x1a\x95\x0c\xe7\x4b\x0b\x97\x31\x22\x7f\xd3\x7c\x0e\xc0\x8a\x47\xdd\xd8\xb8"
+	},
+	{
+		8, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08",
+		(u8 *) "\x97\xab\x8a\x1b\xf0\xaf\xb9\x61\x32\xf2\xf6\x72\x58\xda\x15\xa8\x82\x63\xef\xdb\x45\xc4\xa1\x86\x84\xef\x87\xe6\xb1\x9e\x5b\x09",
+		(u8 *) "\x96\x36\xeb\xc9\x84\x19\x26\xf4\xf7\xd1\xf3\x62\xbd\xdf\x6e\x18\xd0\xa9\x90\xff\x2c\x05\xfe\xf5\xb9\x03\x73\xc9\xff\x4b\x87\x0a",
+		(u8 *) "\x73\x23\x9f\x1d\xb7\xf4\x1d\x80\xb6\x43\xc0\xc5\x25\x18\xec\x63\x16\x3b\x31\x99\x23\xa6\xbd\xb4\x52\x7c\x62\x61\x26\x70\x3c\x0f",
+		(u8 *) "\x49\xd6\xc8\xaf\x0f\x97\x14\x4a\x87\xdf\x21\xd9\x14\x72\xf9\x66\x44\x17\x3a\x10\x3b\x66\x16\xc5\xd5\xad\x1c\xee\x40\xc8\x63\xd0",
+		(u8 *) "\x27\x3c\x9c\x4b\x27\xf3\x22\xe4\xe7\x16\xef\x53\xa4\x7d\xe7\xa4\xc6\xd0\xe7\xb2\x26\x25\x9f\xa9\x02\x34\x90\xb2\x61\x67\xad\x1d",
+		(u8 *) "\x1f\xe8\x98\x67\x13\xf0\x7c\x3d\x9a\xe1\xc1\x63\xff\x8c\xf9\xd3\x83\x69\xe1\xa9\x65\x61\x0b\xe8\x87\xfb\xd0\xc7\x91\x62\xaa\xfb",
+		(u8 *) "\x0a\x01\x27\xab\xb4\x44\x84\xb9\xfb\xef\x5a\xbc\xae\x1b\x57\x9f\xc2\xcd\xad\xc6\x40\x2e\x8e\xe8\x66\xe1\xf3\x7b\xdb\x47\xe4\x2c",
+		(u8 *) "\x26\xb5\x1e\xa3\x7d\xf8\xe1\xd6\xf7\x6f\xc3\xb6\x6a\x74\x29\xb3\xbc\x76\x83\x20\x5d\x4f\x44\x3d\xc1\xf2\x9d\xda\x33\x15\xc8\x7b",
+		(u8 *) "\xd5\xfa\x5a\x34\x69\xd2\x9a\xaa\xf8\x3d\x23\x58\x9d\xb8\xc8\x5b\x3f\xb4\x6e\x2c\x8f\x0f\x06\x8e\xdc\xe8\xcd\xcd\x7d\xfc\x58\x62"
+	},
+	{
+		10, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a",
+		(u8 *) "\xed\xe3\xb0\x46\x43\xe5\x86\xcc\x90\x7d\xc2\x18\x51\x70\x99\x02\x03\x51\x6b\xa7\x8f\x41\x3b\xeb\x22\x3a\xa5\xd4\xd2\xdf\x67\x11",
+		(u8 *) "\x3c\xfd\x6c\xb5\x8e\xe0\xfd\xde\x64\x01\x76\xad\x00\x00\x04\x4d\x48\x53\x2b\x21\xfb\x60\x79\xc9\x11\x4c\x0f\xfd\x9c\x04\xa1\xad",
+		(u8 *) "\x3e\x8c\xea\x98\x01\x71\x09\x97\x90\x84\xb1\xef\x92\xf9\x9d\x86\xe2\x0f\xb4\x9b\xdb\x33\x7e\xe4\x8b\x8d\x8d\xc0\xf4\xaf\xef\xfe",
+		(u8 *) "\x5c\x25\x21\xea\xcd\x79\x66\xf1\x5e\x05\x65\x44\xbe\xa0\xd3\x15\xe0\x67\xa7\x03\x19\x31\xa2\x46\xa6\xc3\x87\x5d\x2f\x67\x8a\xcb",
+		(u8 *) "\xa6\x4f\x70\xaf\x88\xae\x56\xb6\xf8\x75\x81\xc0\xe2\x3e\x6b\x08\xf4\x49\x03\x1d\xe3\x12\x81\x4e\xc6\xf3\x19\x29\x1f\x4a\x05\x16",
+		(u8 *) "\xbd\xae\x85\x92\x4b\x3c\xb1\xd0\xa2\xe3\x3a\x30\xc6\xd7\x95\x99\x8a\x0f\xed\xdb\xac\x86\x5a\x09\xbc\xd1\x27\xfb\x56\x2e\xd6\x0a",
+		(u8 *) "\xb5\x5a\x0a\x5b\x51\xa1\x2a\x8b\xe3\x48\x99\xc3\xe0\x47\x51\x1a\xd9\xa0\x9c\xea\x3c\xe7\x5f\xe3\x96\x98\x07\x03\x17\xa7\x13\x39",
+		(u8 *) "\x55\x22\x25\xed\x11\x77\xf4\x45\x84\xac\x8c\xfa\x6c\x4e\xb5\xfc\x7e\x82\xcb\xab\xfc\x95\x38\x1b\x08\x09\x98\x44\x21\x29\xc2\xf8",
+		(u8 *) "\x1f\x13\x5e\xd1\x4c\xe6\x0a\x91\x36\x9d\x23\x22\xbe\xf2\x5e\x3c\x08\xb6\xbe\x45\x12\x4a\x43\xe2\xeb\x77\x95\x3f\x84\xdc\x85\x53"
+	},
+	{
+		16, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10",
+		(u8 *) "\x9a\xc7\xcc\x9a\x60\x9d\x1e\xf7\xb2\x93\x28\x99\xcd\xe4\x1b\x97\x52\x48\xc4\x95\x90\x14\x12\x6a\x6e\x8a\x84\xf1\x1d\x1a\x9e\x1c",
+		(u8 *) "\x06\x59\x02\xe4\xb6\x20\xf6\xcc\x36\xc8\x58\x9f\x66\x43\x2f\x2b\xd3\x9d\x56\x6b\xc6\xbc\xe3\x01\x07\x68\x15\x15\x49\xf3\x87\x3f",
+		(u8 *) "\xb6\xd1\xe6\xc4\xa5\xe4\x77\x1c\xad\x79\x53\x8d\xf2\x95\xfb\x11\xc6\x8c\x1d\x5c\x55\x9a\x97\x41\x23\xdf\x1d\xbc\x52\xa4\x3b\x89",
+		(u8 *) "\xc5\xec\xf8\x8d\xe8\x97\xfd\x57\xfe\xd3\x01\x70\x1b\x82\xa2\x59\xec\xcb\xe1\x3d\xe1\xfc\xc9\x1c\x11\xa0\xb2\x6c\x0b\xc8\xfa\x4d",
+		(u8 *) "\xe7\xa7\x25\x74\xf8\x78\x2a\xe2\x6a\xab\xcf\x9e\xbc\xd6\x60\x65\xbd\xf0\x32\x4e\x60\x83\xdc\xc6\xd3\xce\xdd\x3c\xa8\xc5\x3c\x16",
+		(u8 *) "\xb4\x01\x10\xc4\x19\x0b\x56\x22\xa9\x61\x16\xb0\x01\x7e\xd2\x97\xff\xa0\xb5\x14\x64\x7e\xc0\x4f\x63\x06\xb8\x92\xae\x66\x11\x81",
+		(u8 *) "\xd0\x3d\x1b\xc0\x3c\xd3\x3d\x70\xdf\xf9\xfa\x5d\x71\x96\x3e\xbd\x8a\x44\x12\x64\x11\xea\xa7\x8b\xd5\x1e\x8d\x87\xa8\x87\x9b\xf5",
+		(u8 *) "\xfa\xbe\xb7\x60\x28\xad\xe2\xd0\xe4\x87\x22\xe4\x6c\x46\x15\xa3\xc0\x5d\x88\xab\xd5\x03\x57\xf9\x35\xa6\x3c\x59\xee\x53\x76\x23",
+		(u8 *) "\xff\x38\x26\x5c\x16\x42\xc1\xab\xe8\xd3\xc2\xfe\x5e\x57\x2b\xf8\xa3\x6a\x4c\x30\x1a\xe8\xac\x13\x61\x0c\xcb\xc1\x22\x56\xca\xcc"
+	},
+	{
+		24, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18",
+		(u8 *) "\x05\x95\xe5\x7f\xe5\xf0\xbb\x3c\x70\x6e\xda\xc8\xa4\xb2\xdb\x11\xdf\xde\x31\x34\x4a\x1a\xf7\x69\xc7\x4f\x07\x0a\xee\x9e\x23\x26",
+		(u8 *) "\xb0\x6b\x9b\x1e\x19\x5d\x13\xd8\xf4\xa7\x99\x5c\x45\x53\xac\x05\x6b\xd2\x37\x8e\xc3\x41\xc9\xa4\x2f\x37\xba\x79\xf8\x8a\x32\xff",
+		(u8 *) "\xe7\x0b\xce\x1d\xf7\x64\x5a\xdb\x5d\x2c\x41\x30\x21\x5c\x35\x22\x9a\x57\x30\xc7\xfc\xb4\xc9\xaf\x51\xff\xda\x89\xc7\xf1\xad\x22",
+		(u8 *) "\x04\x85\x05\x5f\xd4\xf6\xf0\xd9\x63\xef\x5a\xb9\xa5\x47\x69\x82\x59\x1f\xc6\x6b\xcd\xa1\x0e\x45\x2b\x03\xd4\x55\x1f\x6b\x62\xac",
+		(u8 *) "\x27\x53\xcc\x83\x98\x8a\xfa\x3e\x16\x88\xa1\xd3\xb4\x2c\x9a\x02\x93\x61\x0d\x52\x3d\x1d\x3f\x00\x62\xb3\xc2\xa3\xbb\xc7\xc7\xf0",
+		(u8 *) "\x96\xc2\x48\x61\x0a\xad\xed\xfe\xaf\x89\x78\xc0\x3d\xe8\x20\x5a\x0e\x31\x7b\x3d\x1c\x73\xb9\xe9\xa4\x68\x8f\x29\x6d\x13\x3a\x19",
+		(u8 *) "\xbd\xf0\xe6\xc3\xcc\xa5\xb5\xb9\xd5\x33\xb6\x9c\x56\xad\xa1\x20\x88\xa2\x18\xb6\xe2\xec\xe1\xe6\x24\x6d\x44\xc7\x59\xd1\x9b\x10",
+		(u8 *) "\x68\x66\x39\x7e\x95\xc1\x40\x53\x4f\x94\x26\x34\x21\x00\x6e\x40\x32\xcb\x0a\x1e\x95\x42\xc6\xb3\xb8\xb3\x98\xab\xc3\xb0\xf1\xd5",
+		(u8 *) "\x29\xa0\xb8\xae\xd5\x4a\x13\x23\x24\xc6\x2e\x42\x3f\x54\xb4\xc8\x3c\xb0\xf3\xb5\x02\x0a\x98\xb8\x2a\xf9\xfe\x15\x44\x84\xa1\x68"
+	},
+	{
+		32, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20",
+		(u8 *) "\xea\xa6\xbd\x25\x88\x0b\xf9\x3d\x3f\x5d\x1e\x4c\xa2\x61\x1d\x91\xcf\xa4\x5c\x9f\x7e\x71\x4b\x54\xbd\xfa\x80\x02\x7c\xb1\x43\x80",
+		(u8 *) "\x11\x4a\xe3\x44\xde\xd7\x1b\x35\xf2\xe6\x0f\xeb\xad\x72\x7f\xd8\x02\xe1\xe7\x05\x6b\x0f\x62\x39\x00\x49\x64\x22\x94\x3e\x97\xb6",
+		(u8 *) "\x91\xcb\x93\xc7\x87\x96\x4e\x10\xd9\x52\x7d\x99\x9c\x6f\x93\x6b\x49\xb1\x8b\x42\xf8\xe8\x36\x7c\xbe\xb5\xef\x10\x4b\xa1\xc7\xcd",
+		(u8 *) "\x87\x08\x4b\x3b\xa7\x00\xba\xde\x95\x56\x10\x67\x27\x45\xb3\x74\xe7\xa7\xb9\xe9\xec\x54\x0d\x5f\xf4\x3b\xdb\x12\x79\x2d\x1b\x35",
+		(u8 *) "\xc7\x99\xb5\x96\x73\x8f\x6b\x01\x8c\x76\xc7\x4b\x17\x59\xbd\x90\x7f\xec\x5b\xfd\x9f\x9b\x89\xce\x65\x48\x30\x90\x92\xd7\xe9\x58",
+		(u8 *) "\x40\xf2\x50\xb2\x6d\x1f\x09\x6a\x4a\xfd\x4c\x34\x0a\x58\x88\x15\x3e\x34\x13\x5c\x79\xdb\x01\x02\x00\x76\x76\x51\xcf\x26\x30\x73",
+		(u8 *) "\xf6\x56\xab\xcc\xf8\x8d\xd8\x27\x02\x7b\x2c\xe9\x17\xd4\x64\xec\x18\xb6\x25\x03\xbf\xbc\x07\x7f\xba\xbb\x98\xf2\x0d\x98\xab\x34",
+		(u8 *) "\x8a\xed\x95\xee\x5b\x0d\xcb\xfb\xef\x4e\xb2\x1d\x3a\x3f\x52\xf9\x62\x5a\x1a\xb0\x0e\xe3\x9a\x53\x27\x34\x6b\xdd\xb0\x1a\x9c\x18",
+		(u8 *) "\xa1\x3a\x7c\x79\xc7\xe1\x19\xb5\xab\x02\x96\xab\x28\xc3\x00\xb9\xf3\xe4\xc0\xa2\xe0\x2d\x1d\x01\xf7\xf0\xa7\x46\x18\xaf\x2b\x48"
+	},
+	{
+		5, (u8 *) "\x83\x32\x22\x77\x2a",
+		(u8 *) "\x80\xad\x97\xbd\xc9\x73\xdf\x8a\x2e\x87\x9e\x92\xa4\x97\xef\xda\x20\xf0\x60\xc2\xf2\xe5\x12\x65\x01\xd3\xd4\xfe\xa1\x0d\x5f\xc0",
+		(u8 *) "\xfa\xa1\x48\xe9\x90\x46\x18\x1f\xec\x6b\x20\x85\xf3\xb2\x0e\xd9\xf0\xda\xf5\xba\xb3\xd5\x96\x83\x98\x57\x84\x6f\x73\xfb\xfe\x5a",
+		(u8 *) "\x1c\x7e\x2f\xc4\x63\x92\x32\xfe\x29\x75\x84\xb2\x96\x99\x6b\xc8\x3d\xb9\xb2\x49\x40\x6c\xc8\xed\xff\xac\x55\xcc\xd3\x22\xba\x12",
+		(u8 *) "\xe4\xf9\xf7\xe0\x06\x61\x54\xbb\xd1\x25\xb7\x45\x56\x9b\xc8\x97\x75\xd5\xef\x26\x2b\x44\xc4\x1a\x9c\xf6\x3a\xe1\x45\x68\xe1\xb9",
+		(u8 *) "\x6d\xa4\x53\xdb\xf8\x1e\x82\x33\x4a\x3d\x88\x66\xcb\x50\xa1\xe3\x78\x28\xd0\x74\x11\x9c\xab\x5c\x22\xb2\x94\xd7\xa9\xbf\xa0\xbb",
+		(u8 *) "\xad\xb8\x9c\xea\x9a\x15\xfb\xe6\x17\x29\x5b\xd0\x4b\x8c\xa0\x5c\x62\x51\xd8\x7f\xd4\xaa\xae\x9a\x7e\x4a\xd5\xc2\x17\xd3\xf3\x00",
+		(u8 *) "\xe7\x11\x9b\xd6\xdd\x9b\x22\xaf\xe8\xf8\x95\x85\x43\x28\x81\xe2\x78\x5b\x60\xfd\x7e\xc4\xe9\xfc\xb6\x54\x5f\x35\x0d\x66\x0f\xab",
+		(u8 *) "\xaf\xec\xc0\x37\xfd\xb7\xb0\x83\x8e\xb3\xd7\x0b\xcd\x26\x83\x82\xdb\xc1\xa7\xb4\x9d\x57\x35\x8c\xc9\xfa\x6d\x61\xd7\x3b\x7c\xf0",
+		(u8 *) "\x63\x49\xd1\x26\xa3\x7a\xfc\xba\x89\x79\x4f\x98\x04\x91\x4f\xdc\xbf\x42\xc3\x01\x8c\x2f\x7c\x66\xbf\xde\x52\x49\x75\x76\x81\x15"
+	},
+	{
+		7, (u8 *) "\x19\x10\x83\x32\x22\x77\x2a",
+		(u8 *) "\xbc\x92\x22\xdb\xd3\x27\x4d\x8f\xc6\x6d\x14\xcc\xbd\xa6\x69\x0b\x7a\xe6\x27\x41\x0c\x9a\x2b\xe6\x93\xdf\x5b\xb7\x48\x5a\x63\xe3",
+		(u8 *) "\x3f\x09\x31\xaa\x03\xde\xfb\x30\x0f\x06\x01\x03\x82\x6f\x2a\x64\xbe\xaa\x9e\xc8\xd5\x9b\xb6\x81\x29\xf3\x02\x7c\x96\x36\x11\x81",
+		(u8 *) "\x74\xe0\x4d\xb4\x6d\x28\x64\x8d\x7d\xee\x8a\x00\x64\xb0\x6c\xfe\x9b\x5e\x81\xc6\x2f\xe0\x23\xc5\x5b\xe4\x2f\x87\xbb\xf9\x32\xb8",
+		(u8 *) "\xce\x17\x8f\xc1\x82\x6e\xfe\xcb\xc1\x82\xf5\x79\x99\xa4\x61\x40\x8b\xdf\x55\xcd\x55\x06\x1c\x06\xdb\xa6\xbe\x11\xde\x4a\x57\x8a",
+		(u8 *) "\x62\x6f\x5f\x4d\xce\x65\x25\x01\xf3\x08\x7d\x39\xc9\x2c\xc3\x49\x42\xda\xac\x6a\x8f\x9a\xb9\xa7\xfd\x13\x7c\x60\x37\x82\x56\x82",
+		(u8 *) "\xcc\x03\xfd\xb7\x91\x92\xa2\x07\x31\x2f\x53\xf5\xd4\xdc\x33\xd9\xf7\x0f\x14\x12\x2a\x1c\x98\xa3\x15\x5d\x28\xb8\xa0\xa8\xa4\x1d",
+		(u8 *) "\x2a\x3a\x30\x7a\xb2\x70\x8a\x9c\x00\xfe\x0b\x42\xf9\xc2\xd6\xa1\x86\x26\x17\x62\x7d\x22\x61\xea\xb0\xb1\x24\x65\x97\xca\x0a\xe9",
+		(u8 *) "\x55\xf8\x77\xce\x4f\x2e\x1d\xdb\xbf\x8e\x13\xe2\xcd\xe0\xfd\xc8\x1b\x15\x56\xcb\x93\x5f\x17\x33\x37\x70\x5f\xbb\x5d\x50\x1f\xc1",
+		(u8 *) "\xec\xd0\xe9\x66\x02\xbe\x7f\x8d\x50\x92\x81\x6c\xcc\xf2\xc2\xe9\x02\x78\x81\xfa\xb4\x99\x3a\x1c\x26\x20\x24\xa9\x4f\xff\x3f\x61"
+	},
+	{
+		8, (u8 *) "\x64\x19\x10\x83\x32\x22\x77\x2a",
+		(u8 *) "\xbb\xf6\x09\xde\x94\x13\x17\x2d\x07\x66\x0c\xb6\x80\x71\x69\x26\x46\x10\x1a\x6d\xab\x43\x11\x5d\x6c\x52\x2b\x4f\xe9\x36\x04\xa9",
+		(u8 *) "\xcb\xe1\xff\xf2\x1c\x96\xf3\xee\xf6\x1e\x8f\xe0\x54\x2c\xbd\xf0\x34\x79\x38\xbf\xfa\x40\x09\xc5\x12\xcf\xb4\x03\x4b\x0d\xd1\xa7",
+		(u8 *) "\x78\x67\xa7\x86\xd0\x0a\x71\x47\x90\x4d\x76\xdd\xf1\xe5\x20\xe3\x8d\x3e\x9e\x1c\xae\xfc\xcc\xb3\xfb\xf8\xd1\x8f\x64\x12\x0b\x32",
+		(u8 *) "\x94\x23\x37\xf8\xfd\x76\xf0\xfa\xe8\xc5\x2d\x79\x54\x81\x06\x72\xb8\x54\x8c\x10\xf5\x16\x67\xf6\xe6\x0e\x18\x2f\xa1\x9b\x30\xf7",
+		(u8 *) "\x02\x11\xc7\xc6\x19\x0c\x9e\xfd\x12\x37\xc3\x4c\x8f\x2e\x06\xc4\xbd\xa6\x4f\x65\x27\x6d\x2a\xac\xb8\xf9\x02\x12\x20\x3a\x80\x8e",
+		(u8 *) "\xbd\x38\x20\xf7\x32\xff\xb5\x3e\xc1\x93\xe7\x9d\x33\xe2\x7c\x73\xd0\x16\x86\x16\x86\x19\x07\xd4\x82\xe3\x6c\xda\xc8\xcf\x57\x49",
+		(u8 *) "\x97\xb0\xf0\xf2\x24\xb2\xd2\x31\x71\x14\x80\x8f\xb0\x3a\xf7\xa0\xe5\x96\x16\xe4\x69\x78\x79\x39\xa0\x63\xce\xea\x9a\xf9\x56\xd1",
+		(u8 *) "\xc4\x7e\x0d\xc1\x66\x09\x19\xc1\x11\x01\x20\x8f\x9e\x69\xaa\x1f\x5a\xe4\xf1\x28\x96\xb8\x37\x9a\x2a\xad\x89\xb5\xb5\x53\xd6\xb0",
+		(u8 *) "\x6b\x6b\x09\x8d\x0c\x29\x3b\xc2\x99\x3d\x80\xbf\x05\x18\xb6\xd9\x81\x70\xcc\x3c\xcd\x92\xa6\x98\x62\x1b\x93\x9d\xd3\x8f\xe7\xb9"
+	},
+	{
+		10, (u8 *) "\x8b\x37\x64\x19\x10\x83\x32\x22\x77\x2a",
+		(u8 *) "\xab\x65\xc2\x6e\xdd\xb2\x87\x60\x0d\xb2\xfd\xa1\x0d\x1e\x60\x5c\xbb\x75\x90\x10\xc2\x96\x58\xf2\xc7\x2d\x93\xa2\xd1\x6d\x29\x30",
+		(u8 *) "\xb9\x01\xe8\x03\x6e\xd1\xc3\x83\xcd\x3c\x4c\x4d\xd0\xa6\xab\x05\x3d\x25\xce\x49\x22\x92\x4c\x55\xf0\x64\x94\x33\x53\xd7\x8a\x6c",
+		(u8 *) "\x12\xc1\xaa\x44\xbb\xf8\x7e\x75\xe6\x11\xf6\x9b\x2c\x38\xf4\x9b\x28\xf2\xb3\x43\x4b\x65\xc0\x98\x77\x47\x00\x44\xc6\xea\x17\x0d",
+		(u8 *) "\xbd\x9e\xf8\x22\xde\x52\x88\x19\x61\x34\xcf\x8a\xf7\x83\x93\x04\x67\x55\x9c\x23\xf0\x52\x15\x84\x70\xa2\x96\xf7\x25\x73\x5a\x32",
+		(u8 *) "\x8b\xab\x26\xfb\xc2\xc1\x2b\x0f\x13\xe2\xab\x18\x5e\xab\xf2\x41\x31\x18\x5a\x6d\x69\x6f\x0c\xfa\x9b\x42\x80\x8b\x38\xe1\x32\xa2",
+		(u8 *) "\x56\x4d\x3d\xae\x18\x3c\x52\x34\xc8\xaf\x1e\x51\x06\x1c\x44\xb5\x3c\x07\x78\xa7\xb5\xf7\x2d\x3c\x23\xa3\x13\x5c\x7d\x67\xb9\xf4",
+		(u8 *) "\xf3\x43\x69\x89\x0f\xcf\x16\xfb\x51\x7d\xca\xae\x44\x63\xb2\xdd\x02\xf3\x1c\x81\xe8\x20\x07\x31\xb8\x99\xb0\x28\xe7\x91\xbf\xa7",
+		(u8 *) "\x72\xda\x64\x62\x83\x22\x8c\x14\x30\x08\x53\x70\x17\x95\x61\x6f\x4e\x0a\x8c\x6f\x79\x34\xa7\x88\xe2\x26\x5e\x81\xd6\xd0\xc8\xf4",
+		(u8 *) "\x43\x8d\xd5\xea\xfe\xa0\x11\x1b\x6f\x36\xb4\xb9\x38\xda\x2a\x68\x5f\x6b\xfc\x73\x81\x58\x74\xd9\x71\x00\xf0\x86\x97\x93\x57\xd8"
+	},
+	{
+		16, (u8 *) "\xeb\xb4\x62\x27\xc6\xcc\x8b\x37\x64\x19\x10\x83\x32\x22\x77\x2a",
+		(u8 *) "\x72\x0c\x94\xb6\x3e\xdf\x44\xe1\x31\xd9\x50\xca\x21\x1a\x5a\x30\xc3\x66\xfd\xea\xcf\x9c\xa8\x04\x36\xbe\x7c\x35\x84\x24\xd2\x0b",
+		(u8 *) "\xb3\x39\x4a\x40\xaa\xbf\x75\xcb\xa4\x22\x82\xef\x25\xa0\x05\x9f\x48\x47\xd8\x1d\xa4\x94\x2d\xbc\x24\x9d\xef\xc4\x8c\x92\x2b\x9f",
+		(u8 *) "\x08\x12\x8c\x46\x9f\x27\x53\x42\xad\xda\x20\x2b\x2b\x58\xda\x95\x97\x0d\xac\xef\x40\xad\x98\x72\x3b\xac\x5d\x69\x55\xb8\x17\x61",
+		(u8 *) "\x3c\xb8\x99\x93\xb0\x7b\x0c\xed\x93\xde\x13\xd2\xa1\x10\x13\xac\xef\x2d\x67\x6f\x15\x45\xc2\xc1\x3d\xc6\x80\xa0\x2f\x4a\xdb\xfe",
+		(u8 *) "\xb6\x05\x95\x51\x4f\x24\xbc\x9f\xe5\x22\xa6\xca\xd7\x39\x36\x44\xb5\x15\xa8\xc5\x01\x17\x54\xf5\x90\x03\x05\x8b\xdb\x81\x51\x4e",
+		(u8 *) "\x3c\x70\x04\x7e\x8c\xbc\x03\x8e\x3b\x98\x20\xdb\x60\x1d\xa4\x95\x11\x75\xda\x6e\xe7\x56\xde\x46\xa5\x3e\x2b\x07\x56\x60\xb7\x70",
+		(u8 *) "\x00\xa5\x42\xbb\xa0\x21\x11\xcc\x2c\x65\xb3\x8e\xbd\xba\x58\x7e\x58\x65\xfd\xbb\x5b\x48\x06\x41\x04\xe8\x30\xb3\x80\xf2\xae\xde",
+		(u8 *) "\x34\xb2\x1a\xd2\xad\x44\xe9\x99\xdb\x2d\x7f\x08\x63\xf0\xd9\xb6\x84\xa9\x21\x8f\xc3\x6e\x8a\x5f\x2c\xcf\xbe\xae\x53\xa2\x7d\x25",
+		(u8 *) "\xa2\x22\x1a\x11\xb8\x33\xcc\xb4\x98\xa5\x95\x40\xf0\x54\x5f\x4a\x5b\xbe\xb4\x78\x7d\x59\xe5\x37\x3f\xdb\xea\x6c\x6f\x75\xc2\x9b"
+	},
+	{
+		24, (u8 *) "\xc1\x09\x16\x39\x08\xeb\xe5\x1d\xeb\xb4\x62\x27\xc6\xcc\x8b\x37\x64\x19\x10\x83\x32\x22\x77\x2a",
+		(u8 *) "\x54\xb6\x4e\x6b\x5a\x20\xb5\xe2\xec\x84\x59\x3d\xc7\x98\x9d\xa7\xc1\x35\xee\xe2\x37\xa8\x54\x65\xff\x97\xdc\x03\x92\x4f\x45\xce",
+		(u8 *) "\xcf\xcc\x92\x2f\xb4\xa1\x4a\xb4\x5d\x61\x75\xaa\xbb\xf2\xd2\x01\x83\x7b\x87\xe2\xa4\x46\xad\x0e\xf7\x98\xac\xd0\x2b\x94\x12\x4f",
+		(u8 *) "\x17\xa6\xdb\xd6\x64\x92\x6a\x06\x36\xb3\xf4\xc3\x7a\x4f\x46\x94\x4a\x5f\x9f\x26\xae\xee\xd4\xd4\xa2\x5f\x63\x2d\x30\x52\x33\xd9",
+		(u8 *) "\x80\xa3\xd0\x1e\xf0\x0c\x8e\x9a\x42\x09\xc1\x7f\x4e\xeb\x35\x8c\xd1\x5e\x7d\x5f\xfa\xaa\xbc\x02\x07\xbf\x20\x0a\x11\x77\x93\xa2",
+		(u8 *) "\x34\x96\x82\xbf\x58\x8e\xaa\x52\xd0\xaa\x15\x60\x34\x6a\xea\xfa\xf5\x85\x4c\xdb\x76\xc8\x89\xe3\xad\x63\x35\x4e\x5f\x72\x75\xe3",
+		(u8 *) "\x53\x2c\x7c\xec\xcb\x39\xdf\x32\x36\x31\x84\x05\xa4\xb1\x27\x9c\xba\xef\xe6\xd9\xce\xb6\x51\x84\x22\x60\xe0\xd1\xe0\x5e\x3b\x90",
+		(u8 *) "\xe8\x2d\x8c\x6d\xb5\x4e\x3c\x63\x3f\x58\x1c\x95\x2b\xa0\x42\x07\x4b\x16\xe5\x0a\xbd\x38\x1b\xd7\x09\x00\xa9\xcd\x9a\x62\xcb\x23",
+		(u8 *) "\x36\x82\xee\x33\xbd\x14\x8b\xd9\xf5\x86\x56\xcd\x8f\x30\xd9\xfb\x1e\x5a\x0b\x84\x75\x04\x5d\x9b\x20\xb2\x62\x86\x24\xed\xfd\x9e",
+		(u8 *) "\x63\xed\xd6\x84\xfb\x82\x62\x82\xfe\x52\x8f\x9c\x0e\x92\x37\xbc\xe4\xdd\x2e\x98\xd6\x96\x0f\xae\x0b\x43\x54\x54\x56\x74\x33\x91"
+	},
+	{
+		32, (u8 *) "\x1a\xda\x31\xd5\xcf\x68\x82\x21\xc1\x09\x16\x39\x08\xeb\xe5\x1d\xeb\xb4\x62\x27\xc6\xcc\x8b\x37\x64\x19\x10\x83\x32\x22\x77\x2a",
+		(u8 *) "\xdd\x5b\xcb\x00\x18\xe9\x22\xd4\x94\x75\x9d\x7c\x39\x5d\x02\xd3\xc8\x44\x6f\x8f\x77\xab\xf7\x37\x68\x53\x53\xeb\x89\xa1\xc9\xeb",
+		(u8 *) "\xaf\x3e\x30\xf9\xc0\x95\x04\x59\x38\x15\x15\x75\xc3\xfb\x90\x98\xf8\xcb\x62\x74\xdb\x99\xb8\x0b\x1d\x20\x12\xa9\x8e\xd4\x8f\x0e",
+		(u8 *) "\x25\xc3\x00\x5a\x1c\xb8\x5d\xe0\x76\x25\x98\x39\xab\x71\x98\xab\x9d\xcb\xc1\x83\xe8\xcb\x99\x4b\x72\x7b\x75\xbe\x31\x80\x76\x9c",
+		(u8 *) "\xa1\xd3\x07\x8d\xfa\x91\x69\x50\x3e\xd9\xd4\x49\x1d\xee\x4e\xb2\x85\x14\xa5\x49\x58\x58\x09\x6f\x59\x6e\x4b\xcd\x66\xb1\x06\x65",
+		(u8 *) "\x5f\x40\xd5\x9e\xc1\xb0\x3b\x33\x73\x8e\xfa\x60\xb2\x25\x5d\x31\x34\x77\xc7\xf7\x64\xa4\x1b\xac\xef\xf9\x0b\xf1\x4f\x92\xb7\xcc",
+		(u8 *) "\xac\x4e\x95\x36\x8d\x99\xb9\xeb\x78\xb8\xda\x8f\x81\xff\xa7\x95\x8c\x3c\x13\xf8\xc2\x38\x8b\xb7\x3f\x38\x57\x6e\x65\xb7\xc4\x46",
+		(u8 *) "\x13\xc4\xb9\xc1\xdf\xb6\x65\x79\xed\xdd\x8a\x28\x0b\x9f\x73\x16\xdd\xd2\x78\x20\x55\x01\x26\x69\x8e\xfa\xad\xc6\x4b\x64\xf6\x6e",
+		(u8 *) "\xf0\x8f\x2e\x66\xd2\x8e\xd1\x43\xf3\xa2\x37\xcf\x9d\xe7\x35\x59\x9e\xa3\x6c\x52\x55\x31\xb8\x80\xba\x12\x43\x34\xf5\x7b\x0b\x70",
+		(u8 *) "\xd5\xa3\x9e\x3d\xfc\xc5\x02\x80\xba\xc4\xa6\xb5\xaa\x0d\xca\x7d\x37\x0b\x1c\x1f\xe6\x55\x91\x6d\x97\xfd\x0d\x47\xca\x1d\x72\xb8"
+	}
+};
+
+#define NUM_TESTS ARRAY_SIZE(tests)
+
+
+static int run_test(unsigned int i, const u8 *key, size_t key_len,
+		    const u8 *stream, int offset)
+{
+	u8 res[32];
+	os_memset(res, 0, sizeof(res));
+	if (rc4_skip(key, key_len, offset, res, sizeof(res)) < 0 ||
+	    os_memcmp(res, stream, 32) != 0) {
+		printf("RC4 test case %d (offset %d) - FAILED!\n",
+		       i + 1, offset);
+		return 1;
+	}
+	return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+	int ret = 0;
+	unsigned int i;
+
+	for (i = 0; i < NUM_TESTS; i++) {
+		const struct rc4_test_vector *test = &tests[i];
+		ret += run_test(i, test->key, test->key_len,
+				test->stream0, 0);
+		ret += run_test(i, test->key, test->key_len,
+				test->stream240, 240);
+		ret += run_test(i, test->key, test->key_len,
+				test->stream496, 496);
+		ret += run_test(i, test->key, test->key_len,
+				test->stream752, 752);
+		ret += run_test(i, test->key, test->key_len,
+				test->stream1008, 1008);
+		ret += run_test(i, test->key, test->key_len,
+				test->stream1520, 1520);
+		ret += run_test(i, test->key, test->key_len,
+				test->stream2032, 2032);
+		ret += run_test(i, test->key, test->key_len,
+				test->stream3056, 3056);
+		ret += run_test(i, test->key, test->key_len,
+				test->stream4080, 4080);
+	}
+
+	if (ret == 0)
+		printf("All RC4 test cases passed\n");
+
+	return ret;
+}
diff --git a/hostap/tests/test-rsa-sig-ver.c b/hostap/tests/test-rsa-sig-ver.c
new file mode 100644
index 0000000..6fad5b1
--- /dev/null
+++ b/hostap/tests/test-rsa-sig-ver.c
@@ -0,0 +1,199 @@
+/*
+ * Testing tool for RSA PKCS #1 v1.5 signature verification
+ * 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 "crypto/crypto.h"
+#include "tls/rsa.h"
+#include "tls/asn1.h"
+#include "tls/pkcs1.h"
+
+
+static int cavp_rsa_sig_ver(const char *fname)
+{
+	FILE *f;
+	int ret = 0;
+	char buf[15000], *pos, *pos2;
+	u8 msg[200], n[512], s[512], em[512], e[512];
+	size_t msg_len = 0, n_len = 0, s_len = 0, em_len, e_len = 0;
+	size_t tmp_len;
+	char sha_alg[20];
+	int ok = 0;
+
+	printf("CAVP RSA SigVer test vectors from %s\n", fname);
+
+	f = fopen(fname, "r");
+	if (f == NULL) {
+		printf("%s does not exist - cannot validate CAVP RSA SigVer test vectors\n",
+			fname);
+		return 0;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		pos = os_strchr(buf, '=');
+		if (pos == NULL)
+			continue;
+		pos2 = pos - 1;
+		while (pos2 >= buf && *pos2 == ' ')
+			*pos2-- = '\0';
+		*pos++ = '\0';
+		while (*pos == ' ')
+			*pos++ = '\0';
+		pos2 = os_strchr(pos, '\r');
+		if (!pos2)
+			pos2 = os_strchr(pos, '\n');
+		if (pos2)
+			*pos2 = '\0';
+		else
+			pos2 = pos + os_strlen(pos);
+
+		if (os_strcmp(buf, "SHAAlg") == 0) {
+			os_strlcpy(sha_alg, pos, sizeof(sha_alg));
+		} else if (os_strcmp(buf, "Msg") == 0) {
+			tmp_len = os_strlen(pos);
+			if (tmp_len > sizeof(msg) * 2) {
+				printf("Too long Msg\n");
+				fclose(f);
+				return -1;
+			}
+			msg_len = tmp_len / 2;
+			if (hexstr2bin(pos, msg, msg_len) < 0) {
+				printf("Invalid hex string '%s'\n", pos);
+				ret++;
+				break;
+			}
+		} else if (os_strcmp(buf, "n") == 0) {
+			tmp_len = os_strlen(pos);
+			if (tmp_len > sizeof(n) * 2) {
+				printf("Too long n\n");
+				fclose(f);
+				return -1;
+			}
+			n_len = tmp_len / 2;
+			if (hexstr2bin(pos, n, n_len) < 0) {
+				printf("Invalid hex string '%s'\n", pos);
+				ret++;
+				break;
+			}
+		} else if (os_strcmp(buf, "e") == 0) {
+			tmp_len = os_strlen(pos);
+			if (tmp_len > sizeof(e) * 2) {
+				printf("Too long e\n");
+				fclose(f);
+				return -1;
+			}
+			e_len = tmp_len / 2;
+			if (hexstr2bin(pos, e, e_len) < 0) {
+				printf("Invalid hex string '%s'\n", pos);
+				ret++;
+				break;
+			}
+		} else if (os_strcmp(buf, "S") == 0) {
+			tmp_len = os_strlen(pos);
+			if (tmp_len > sizeof(s) * 2) {
+				printf("Too long S\n");
+				fclose(f);
+				return -1;
+			}
+			s_len = tmp_len / 2;
+			if (hexstr2bin(pos, s, s_len) < 0) {
+				printf("Invalid hex string '%s'\n", pos);
+				ret++;
+				break;
+			}
+		} else if (os_strncmp(buf, "EM", 2) == 0) {
+			tmp_len = os_strlen(pos);
+			if (tmp_len > sizeof(em) * 2) {
+				fclose(f);
+				return -1;
+			}
+			em_len = tmp_len / 2;
+			if (hexstr2bin(pos, em, em_len) < 0) {
+				printf("Invalid hex string '%s'\n", pos);
+				ret++;
+				break;
+			}
+		} else if (os_strcmp(buf, "Result") == 0) {
+			const u8 *addr[1];
+			size_t len[1];
+			struct crypto_public_key *pk;
+			int res;
+			u8 hash[32];
+			size_t hash_len;
+			const struct asn1_oid *alg;
+
+			addr[0] = msg;
+			len[0] = msg_len;
+			if (os_strcmp(sha_alg, "SHA1") == 0) {
+				if (sha1_vector(1, addr, len, hash) < 0) {
+					fclose(f);
+					return -1;
+				}
+				hash_len = 20;
+				alg = &asn1_sha1_oid;
+			} else if (os_strcmp(sha_alg, "SHA256") == 0) {
+				if (sha256_vector(1, addr, len, hash) < 0) {
+					fclose(f);
+					return -1;
+				}
+				hash_len = 32;
+				alg = &asn1_sha256_oid;
+			} else {
+				continue;
+			}
+
+			printf("\nExpected result: %s\n", pos);
+			wpa_hexdump(MSG_INFO, "Hash(Msg)", hash, hash_len);
+
+			pk = crypto_public_key_import_parts(n, n_len,
+							    e, e_len);
+			if (pk == NULL) {
+				printf("Failed to import public key\n");
+				ret++;
+				continue;
+			}
+
+			res = pkcs1_v15_sig_ver(pk, s, s_len, alg,
+						hash, hash_len);
+			crypto_public_key_free(pk);
+			if ((*pos == 'F' && !res) || (*pos != 'F' && res)) {
+				printf("FAIL\n");
+				ret++;
+				continue;
+			}
+
+			printf("PASS\n");
+			ok++;
+		}
+	}
+
+	fclose(f);
+
+	if (ret)
+		printf("Test case failed\n");
+	else
+		printf("%d test vectors OK\n", ok);
+
+	return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+	int ret = 0;
+
+	wpa_debug_level = 0;
+
+	if (cavp_rsa_sig_ver("CAVP/SigVer15_186-3.rsp"))
+		ret++;
+	if (cavp_rsa_sig_ver("CAVP/SigVer15EMTest.txt"))
+		ret++;
+
+	return ret;
+}
diff --git a/hostap/tests/test-sha1.c b/hostap/tests/test-sha1.c
new file mode 100644
index 0000000..3269d4d
--- /dev/null
+++ b/hostap/tests/test-sha1.c
@@ -0,0 +1,119 @@
+/*
+ * Test program for SHA1 and MD5
+ * Copyright (c) 2003-2006, 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/crypto.h"
+
+
+static int cavp_shavs(const char *fname)
+{
+	FILE *f;
+	int ret = 0;
+	char buf[15000], *pos, *pos2;
+	u8 msg[6400];
+	int msg_len = 0, tmp_len;
+	u8 md[20], hash[20];
+	int ok = 0;
+
+	printf("CAVP SHAVS test vectors from %s\n", fname);
+
+	f = fopen(fname, "r");
+	if (f == NULL) {
+		printf("%s does not exist - cannot validate CAVP SHAVS test vectors\n",
+			fname);
+		return 0;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		pos = os_strchr(buf, '=');
+		if (pos == NULL)
+			continue;
+		pos2 = pos - 1;
+		while (pos2 >= buf && *pos2 == ' ')
+			*pos2-- = '\0';
+		*pos++ = '\0';
+		while (*pos == ' ')
+			*pos++ = '\0';
+		pos2 = os_strchr(pos, '\r');
+		if (!pos2)
+			pos2 = os_strchr(pos, '\n');
+		if (pos2)
+			*pos2 = '\0';
+		else
+			pos2 = pos + os_strlen(pos);
+
+		if (os_strcmp(buf, "Len") == 0) {
+			msg_len = atoi(pos);
+		} else if (os_strcmp(buf, "Msg") == 0) {
+			tmp_len = os_strlen(pos);
+			if (msg_len == 0 && tmp_len == 2)
+				tmp_len = 0;
+			if (msg_len != tmp_len * 4) {
+				printf("Unexpected Msg length (msg_len=%u tmp_len=%u, Msg='%s'\n",
+				       msg_len, tmp_len, pos);
+				ret++;
+				break;
+			}
+
+			if (hexstr2bin(pos, msg, msg_len / 8) < 0) {
+				printf("Invalid hex string '%s'\n", pos);
+				ret++;
+				break;
+			}
+		} else if (os_strcmp(buf, "MD") == 0) {
+			const u8 *addr[1];
+			size_t len[1];
+
+			tmp_len = os_strlen(pos);
+			if (tmp_len != 2 * 20) {
+				printf("Unexpected MD length (MD='%s'\n",
+				       pos);
+				ret++;
+				break;
+			}
+
+			if (hexstr2bin(pos, md, 20) < 0) {
+				printf("Invalid hex string '%s'\n", pos);
+				ret++;
+				break;
+			}
+
+			addr[0] = msg;
+			len[0] = msg_len / 8;
+			if (sha1_vector(1, addr, len, hash) < 0 ||
+			    os_memcmp(hash, md, 20) != 0)
+				ret++;
+			else
+				ok++;
+		}
+	}
+
+	fclose(f);
+
+	if (ret)
+		printf("Test case failed\n");
+	else
+		printf("%d test vectors OK\n", ok);
+
+	return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+	int ret = 0;
+
+	if (cavp_shavs("CAVP/SHA1ShortMsg.rsp"))
+		ret++;
+	if (cavp_shavs("CAVP/SHA1LongMsg.rsp"))
+		ret++;
+
+	return ret;
+}
diff --git a/hostap/tests/test-sha256.c b/hostap/tests/test-sha256.c
new file mode 100644
index 0000000..741351a
--- /dev/null
+++ b/hostap/tests/test-sha256.c
@@ -0,0 +1,119 @@
+/*
+ * Test program for SHA256
+ * Copyright (c) 2006, 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/crypto.h"
+
+
+static int cavp_shavs(const char *fname)
+{
+	FILE *f;
+	int ret = 0;
+	char buf[15000], *pos, *pos2;
+	u8 msg[6400];
+	int msg_len = 0, tmp_len;
+	u8 md[32], hash[32];
+	int ok = 0;
+
+	printf("CAVP SHAVS test vectors from %s\n", fname);
+
+	f = fopen(fname, "r");
+	if (f == NULL) {
+		printf("%s does not exist - cannot validate CAVP SHAVS test vectors\n",
+			fname);
+		return 0;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		pos = os_strchr(buf, '=');
+		if (pos == NULL)
+			continue;
+		pos2 = pos - 1;
+		while (pos2 >= buf && *pos2 == ' ')
+			*pos2-- = '\0';
+		*pos++ = '\0';
+		while (*pos == ' ')
+			*pos++ = '\0';
+		pos2 = os_strchr(pos, '\r');
+		if (!pos2)
+			pos2 = os_strchr(pos, '\n');
+		if (pos2)
+			*pos2 = '\0';
+		else
+			pos2 = pos + os_strlen(pos);
+
+		if (os_strcmp(buf, "Len") == 0) {
+			msg_len = atoi(pos);
+		} else if (os_strcmp(buf, "Msg") == 0) {
+			tmp_len = os_strlen(pos);
+			if (msg_len == 0 && tmp_len == 2)
+				tmp_len = 0;
+			if (msg_len != tmp_len * 4) {
+				printf("Unexpected Msg length (msg_len=%u tmp_len=%u, Msg='%s'\n",
+				       msg_len, tmp_len, pos);
+				ret++;
+				break;
+			}
+
+			if (hexstr2bin(pos, msg, msg_len / 8) < 0) {
+				printf("Invalid hex string '%s'\n", pos);
+				ret++;
+				break;
+			}
+		} else if (os_strcmp(buf, "MD") == 0) {
+			const u8 *addr[1];
+			size_t len[1];
+
+			tmp_len = os_strlen(pos);
+			if (tmp_len != 2 * 32) {
+				printf("Unexpected MD length (MD='%s'\n",
+				       pos);
+				ret++;
+				break;
+			}
+
+			if (hexstr2bin(pos, md, 32) < 0) {
+				printf("Invalid hex string '%s'\n", pos);
+				ret++;
+				break;
+			}
+
+			addr[0] = msg;
+			len[0] = msg_len / 8;
+			if (sha256_vector(1, addr, len, hash) < 0 ||
+			    os_memcmp(hash, md, 32) != 0)
+				ret++;
+			else
+				ok++;
+		}
+	}
+
+	fclose(f);
+
+	if (ret)
+		printf("Test case failed\n");
+	else
+		printf("%d test vectors OK\n", ok);
+
+	return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+	int errors = 0;
+
+	if (cavp_shavs("CAVP/SHA256ShortMsg.rsp"))
+		errors++;
+	if (cavp_shavs("CAVP/SHA256LongMsg.rsp"))
+		errors++;
+
+	return errors;
+}
diff --git a/hostap/tests/test-x509.c b/hostap/tests/test-x509.c
new file mode 100644
index 0000000..194f229
--- /dev/null
+++ b/hostap/tests/test-x509.c
@@ -0,0 +1,36 @@
+/*
+ * Testing tool for X.509v3 routines
+ * Copyright (c) 2006-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 "tls/x509v3.h"
+
+
+int main(int argc, char *argv[])
+{
+	FILE *f;
+	u8 buf[3000];
+	size_t len;
+	struct x509_certificate *cert;
+
+	wpa_debug_level = 0;
+
+	f = fopen(argv[1], "rb");
+	if (f == NULL)
+		return -1;
+	len = fread(buf, 1, sizeof(buf), f);
+	fclose(f);
+
+	cert = x509_certificate_parse(buf, len);
+	if (cert == NULL)
+		printf("Failed to parse X.509 certificate\n");
+	x509_certificate_free(cert);
+
+	return 0;
+}
diff --git a/hostap/tests/test-x509v3.c b/hostap/tests/test-x509v3.c
new file mode 100644
index 0000000..bfb0698
--- /dev/null
+++ b/hostap/tests/test-x509v3.c
@@ -0,0 +1,61 @@
+/*
+ * Testing tool for X.509v3 routines
+ * Copyright (c) 2006-2007, 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 "tls/asn1.h"
+#include "tls/x509v3.h"
+
+
+int main(int argc, char *argv[])
+{
+	char *buf;
+	size_t len;
+	struct x509_certificate *certs = NULL, *last = NULL, *cert;
+	int i, reason;
+
+	wpa_debug_level = 0;
+
+	if (argc < 3 || strcmp(argv[1], "-v") != 0) {
+		printf("usage: test_x509v3 -v <cert1.der> <cert2.der> ..\n");
+		return -1;
+	}
+
+	for (i = 2; i < argc; i++) {
+		printf("Reading: %s\n", argv[i]);
+		buf = os_readfile(argv[i], &len);
+		if (buf == NULL) {
+			printf("Failed to read '%s'\n", argv[i]);
+			return -1;
+		}
+
+		cert = x509_certificate_parse((u8 *) buf, len);
+		if (cert == NULL) {
+			printf("Failed to parse X.509 certificate\n");
+			return -1;
+		}
+
+		free(buf);
+
+		if (certs == NULL)
+			certs = cert;
+		else
+			last->next = cert;
+		last = cert;
+	}
+
+	printf("\n\nValidating certificate chain\n");
+	if (x509_certificate_chain_validate(last, certs, &reason, 0) < 0) {
+		printf("\nCertificate chain validation failed: %d\n", reason);
+		return -1;
+	}
+	printf("\nCertificate chain is valid\n");
+
+	return 0;
+}
diff --git a/hostap/tests/test_x509v3_nist.sh b/hostap/tests/test_x509v3_nist.sh
new file mode 100755
index 0000000..d3f94bb
--- /dev/null
+++ b/hostap/tests/test_x509v3_nist.sh
@@ -0,0 +1,144 @@
+#!/bin/bash
+
+# X.509 Path Validation Test Suite, Version 1.07
+# http://csrc.nist.gov/pki/testing/x509paths_old.html
+# http://csrc.nist.gov/pki/testing/x509tests.tgz
+
+if [ -z "$1" ]; then
+    echo "usage: $0 <path to X509tests directory>"
+    exit 1
+fi
+
+TESTS=$1
+
+if [ ! -d $TESTS ]; then
+    echo "Not a directory: $TESTS"
+    exit 1
+fi
+
+X509TEST="./test-x509v3 -v"
+TMPOUT=test_x509v3_nist.out
+
+# TODO: add support for validating CRLs
+
+END="End Certificate "
+ROOT="Trust Anchor "
+ICA="Intermediate Certificate "
+
+SUCCESS=""
+FAILURE=""
+
+function run_test
+{
+    NUM=$1
+    RES=$2
+    shift 2
+    $X509TEST "$@" > $TMPOUT.$NUM
+    VALRES=$?
+    OK=0
+    if [ $RES -eq 0 ]; then
+	# expecting success
+	if [ $VALRES -eq 0 ]; then
+	    OK=1
+	else
+	    echo "test$NUM failed - expected validation success"
+	    OK=0
+	fi
+    else
+	# expecting failure
+	if [ $VALRES -eq 0 ]; then
+	    echo "test$NUM failed - expected validation failure"
+	    OK=0
+	else
+	    REASON=`grep "Certificate chain validation failed: " $TMPOUT.$NUM`
+	    if [ $? -eq 0 ]; then
+		REASONNUM=`echo "$REASON" | colrm 1 37`
+		if [ $REASONNUM -eq $RES ]; then
+		    OK=1
+		else
+		    echo "test$NUM failed - expected validation result $RES; result was $REASONNUM"
+		    OK=0
+		fi
+	    else
+		echo "test$NUM failed - expected validation failure; other type of error detected"
+		OK=0
+	    fi
+	fi
+    fi
+    if [ $OK -eq 1 ]; then
+	rm $TMPOUT.$NUM
+	SUCCESS="$SUCCESS $NUM"
+    else
+	FAILURE="$FAILURE $NUM"
+    fi
+}
+
+P=$TESTS/test
+
+run_test 1 0 "${P}1/${END}CP.01.01.crt" "${P}1/${ROOT}CP.01.01.crt"
+run_test 2 1 "${P}2/${END}CP.01.02.crt" "${P}2/${ICA}CP.01.02.crt" "${P}2/${ROOT}CP.01.01.crt"
+run_test 3 1 "${P}3/${END}CP.01.03.crt" "${P}3/${ICA}CP.01.03.crt" "${P}3/${ROOT}CP.01.01.crt"
+run_test 4 0 "${P}4/${END}CP.02.01.crt" "${P}4/${ICA}2 CP.02.01.crt" "${P}4/${ICA}1 CP.02.01.crt" "${P}4/${ROOT}CP.01.01.crt"
+run_test 5 4 "${P}5/${END}CP.02.02.crt" "${P}5/${ICA}CP.02.02.crt" "${P}5/${ROOT}CP.01.01.crt"
+run_test 6 4 "${P}6/${END}CP.02.03.crt" "${P}6/${ICA}CP.02.03.crt" "${P}6/${ROOT}CP.01.01.crt"
+run_test 7 0 "${P}7/${END}CP.02.04.crt" "${P}7/${ICA}CP.02.04.crt" "${P}7/${ROOT}CP.01.01.crt"
+run_test 8 4 "${P}8/${END}CP.02.05.crt" "${P}8/${ICA}CP.02.05.crt" "${P}8/${ROOT}CP.01.01.crt"
+run_test 9 4 "${P}9/${END}CP.03.01.crt" "${P}9/${ICA}CP.03.01.crt" "${P}9/${ROOT}CP.01.01.crt"
+run_test 10 4 "${P}10/${END}CP.03.02.crt" "${P}10/${ICA}CP.03.02.crt" "${P}10/${ROOT}CP.01.01.crt"
+run_test 11 4 "${P}11/${END}CP.03.03.crt" "${P}11/${ICA}CP.03.03.crt" "${P}11/${ROOT}CP.01.01.crt"
+run_test 12 0 "${P}12/${END}CP.03.04.crt" "${P}12/${ICA}CP.03.04.crt" "${P}12/${ROOT}CP.01.01.crt"
+run_test 13 5 "${P}13/${END}CP.04.01.crt" "${P}13/${ICA}CP.04.01.crt" "${P}13/${ROOT}CP.01.01.crt"
+run_test 14 5 "${P}14/${END}CP.04.02.crt" "${P}14/${ICA}CP.04.02.crt" "${P}14/${ROOT}CP.01.01.crt"
+run_test 15 0 "${P}15/${END}CP.04.03.crt" "${P}15/${ICA}CP.04.03.crt" "${P}15/${ROOT}CP.01.01.crt"
+run_test 16 0 "${P}16/${END}CP.04.04.crt" "${P}16/${ICA}CP.04.04.crt" "${P}16/${ROOT}CP.01.01.crt"
+run_test 17 0 "${P}17/${END}CP.04.05.crt" "${P}17/${ICA}CP.04.05.crt" "${P}17/${ROOT}CP.01.01.crt"
+run_test 18 0 "${P}18/${END}CP.04.06.crt" "${P}18/${ICA}CP.04.06.crt" "${P}18/${ROOT}CP.01.01.crt"
+run_test 19 1 "${P}19/${END}CP.05.01.crt" "${P}19/${ICA}CP.05.01.crt" "${P}19/${ROOT}CP.01.01.crt"
+run_test 20 3 "${P}20/${END}CP.06.01.crt" "${P}20/${ICA}CP.06.01.crt" "${P}20/${ROOT}CP.01.01.crt"
+run_test 21 3 "${P}21/${END}CP.06.02.crt" "${P}21/${ICA}CP.06.02.crt" "${P}21/${ROOT}CP.01.01.crt"
+run_test 22 1 "${P}22/${END}IC.01.01.crt" "${P}22/${ICA}IC.01.01.crt" "${P}22/${ROOT}CP.01.01.crt"
+run_test 23 1 "${P}23/${END}IC.02.01.crt" "${P}23/${ICA}IC.02.01.crt" "${P}23/${ROOT}CP.01.01.crt"
+run_test 24 0 "${P}24/${END}IC.02.02.crt" "${P}24/${ICA}IC.02.02.crt" "${P}24/${ROOT}CP.01.01.crt"
+run_test 25 1 "${P}25/${END}IC.02.03.crt" "${P}25/${ICA}IC.02.03.crt" "${P}25/${ROOT}CP.01.01.crt"
+run_test 26 0 "${P}26/${END}IC.02.04.crt" "${P}26/${ICA}IC.02.04.crt" "${P}26/${ROOT}CP.01.01.crt"
+run_test 27 0 "${P}27/${END}IC.04.01.crt" "${P}27/${ICA}IC.04.01.crt" "${P}27/${ROOT}CP.01.01.crt"
+run_test 28 1 "${P}28/${END}IC.05.01.crt" "${P}28/${ICA}IC.05.01.crt" "${P}28/${ROOT}CP.01.01.crt"
+run_test 29 1 "${P}29/${END}IC.05.02.crt" "${P}29/${ICA}IC.05.02.crt" "${P}29/${ROOT}CP.01.01.crt"
+run_test 30 0 "${P}30/${END}IC.05.03.crt" "${P}30/${ICA}IC.05.03.crt" "${P}30/${ROOT}CP.01.01.crt"
+run_test 31 1 "${P}31/${END}IC.06.01.crt" "${P}31/${ICA}IC.06.01.crt" "${P}31/${ROOT}CP.01.01.crt"
+run_test 32 1 "${P}32/${END}IC.06.02.crt" "${P}32/${ICA}IC.06.02.crt" "${P}32/${ROOT}CP.01.01.crt"
+run_test 33 0 "${P}33/${END}IC.06.03.crt" "${P}33/${ICA}IC.06.03.crt" "${P}33/${ROOT}CP.01.01.crt"
+run_test 34 0 "${P}34/${END}PP.01.01.crt" "${P}34/${ICA}PP.01.01.crt" "${P}34/${ROOT}CP.01.01.crt"
+run_test 35 0 "${P}35/${END}PP.01.02.crt" "${P}35/${ICA}PP.01.02.crt" "${P}35/${ROOT}CP.01.01.crt"
+run_test 36 0 "${P}36/${END}PP.01.03.crt" "${P}36/${ICA}2 PP.01.03.crt" "${P}36/${ICA}1 PP.01.03.crt" "${P}36/${ROOT}CP.01.01.crt"
+run_test 37 0 "${P}37/${END}PP.01.04.crt" "${P}37/${ICA}2 PP.01.04.crt" "${P}37/${ICA}1 PP.01.04.crt" "${P}37/${ROOT}CP.01.01.crt"
+run_test 38 0 "${P}38/${END}PP.01.05.crt" "${P}38/${ICA}2 PP.01.05.crt" "${P}38/${ICA}1 PP.01.05.crt" "${P}38/${ROOT}CP.01.01.crt"
+run_test 39 0 "${P}39/${END}PP.01.06.crt" "${P}39/${ICA}3 PP.01.06.crt" "${P}39/${ICA}2 PP.01.06.crt" "${P}39/${ICA}1 PP.01.06.crt" "${P}39/${ROOT}CP.01.01.crt"
+run_test 40 0 "${P}40/${END}PP.01.07.crt" "${P}40/${ICA}3 PP.01.07.crt" "${P}40/${ICA}2 PP.01.07.crt" "${P}40/${ICA}1 PP.01.07.crt" "${P}40/${ROOT}CP.01.01.crt"
+run_test 41 0 "${P}41/${END}PP.01.08.crt" "${P}41/${ICA}3 PP.01.08.crt" "${P}41/${ICA}2 PP.01.08.crt" "${P}41/${ICA}1 PP.01.08.crt" "${P}41/${ROOT}CP.01.01.crt"
+run_test 42 0 "${P}42/${END}PP.01.09.crt" "${P}42/${ICA}4 PP.01.09.crt" "${P}42/${ICA}3 PP.01.09.crt" "${P}42/${ICA}2 PP.01.09.crt" "${P}42/${ICA}1 PP.01.09.crt" "${P}42/${ROOT}CP.01.01.crt"
+run_test 43 0 "${P}43/${END}PP.06.01.crt" "${P}43/${ICA}4 PP.06.01.crt" "${P}43/${ICA}3 PP.06.01.crt" "${P}43/${ICA}2 PP.06.01.crt" "${P}43/${ICA}1 PP.06.01.crt" "${P}43/${ROOT}CP.01.01.crt"
+run_test 44 0 "${P}44/${END}PP.06.02.crt" "${P}44/${ICA}4 PP.06.02.crt" "${P}44/${ICA}3 PP.06.02.crt" "${P}44/${ICA}2 PP.06.02.crt" "${P}44/${ICA}1 PP.06.02.crt" "${P}44/${ROOT}CP.01.01.crt"
+run_test 45 0 "${P}45/${END}PP.06.03.crt" "${P}45/${ICA}4 PP.06.03.crt" "${P}45/${ICA}3 PP.06.03.crt" "${P}45/${ICA}2 PP.06.03.crt" "${P}45/${ICA}1 PP.06.03.crt" "${P}45/${ROOT}CP.01.01.crt"
+run_test 46 0 "${P}46/${END}PP.06.04.crt" "${P}46/${ICA}4 PP.06.04.crt" "${P}46/${ICA}3 PP.06.04.crt" "${P}46/${ICA}2 PP.06.04.crt" "${P}46/${ICA}1 PP.06.04.crt" "${P}46/${ROOT}CP.01.01.crt"
+run_test 47 0 "${P}47/${END}PP.06.05.crt" "${P}47/${ICA}4 PP.06.05.crt" "${P}47/${ICA}3 PP.06.05.crt" "${P}47/${ICA}2 PP.06.05.crt" "${P}47/${ICA}1 PP.06.05.crt" "${P}47/${ROOT}CP.01.01.crt"
+run_test 48 0 "${P}48/${END}PP.08.01.crt" "${P}48/${ICA}PP.08.01.crt" "${P}48/${ROOT}CP.01.01.crt"
+run_test 49 0 "${P}49/${END}PP.08.02.crt" "${P}49/${ICA}PP.08.02.crt" "${P}49/${ROOT}CP.01.01.crt"
+run_test 50 0 "${P}50/${END}PP.08.03.crt" "${P}50/${ICA}PP.08.03.crt" "${P}50/${ROOT}CP.01.01.crt"
+run_test 51 0 "${P}51/${END}PP.08.04.crt" "${P}51/${ICA}PP.08.04.crt" "${P}51/${ROOT}CP.01.01.crt"
+run_test 52 0 "${P}52/${END}PP.08.05.crt" "${P}52/${ICA}PP.08.05.crt" "${P}52/${ROOT}CP.01.01.crt"
+run_test 53 0 "${P}53/${END}PP.08.06.crt" "${P}53/${ICA}PP.08.06.crt" "${P}53/${ROOT}CP.01.01.crt"
+run_test 54 1 "${P}54/${END}PL.01.01.crt" "${P}54/${ICA}2 PL.01.01.crt" "${P}54/${ICA}1 PL.01.01.crt" "${P}54/${ROOT}CP.01.01.crt"
+run_test 55 1 "${P}55/${END}PL.01.02.crt" "${P}55/${ICA}2 PL.01.02.crt" "${P}55/${ICA}1 PL.01.02.crt" "${P}55/${ROOT}CP.01.01.crt"
+run_test 56 0 "${P}56/${END}PL.01.03.crt" "${P}56/${ICA}PL.01.03.crt" "${P}56/${ROOT}CP.01.01.crt"
+run_test 57 0 "${P}57/${END}PL.01.04.crt" "${P}57/${ICA}PL.01.04.crt" "${P}57/${ROOT}CP.01.01.crt"
+run_test 58 1 "${P}58/${END}PL.01.05.crt" "${P}58/${ICA}3 PL.01.05.crt" "${P}58/${ICA}2 PL.01.05.crt" "${P}58/${ICA}1 PL.01.05.crt" "${P}58/${ROOT}CP.01.01.crt"
+run_test 59 1 "${P}59/${END}PL.01.06.crt" "${P}59/${ICA}3 PL.01.06.crt" "${P}59/${ICA}2 PL.01.06.crt" "${P}59/${ICA}1 PL.01.06.crt" "${P}59/${ROOT}CP.01.01.crt"
+run_test 60 1 "${P}60/${END}PL.01.07.crt" "${P}60/${ICA}4 PL.01.07.crt" "${P}60/${ICA}3 PL.01.07.crt" "${P}60/${ICA}2 PL.01.07.crt" "${P}60/${ICA}1 PL.01.07.crt" "${P}60/${ROOT}CP.01.01.crt"
+run_test 61 1 "${P}61/${END}PL.01.08.crt" "${P}61/${ICA}4 PL.01.08.crt" "${P}61/${ICA}3 PL.01.08.crt" "${P}61/${ICA}2 PL.01.08.crt" "${P}61/${ICA}1 PL.01.08.crt" "${P}61/${ROOT}CP.01.01.crt"
+run_test 62 0 "${P}62/${END}PL.01.09.crt" "${P}62/${ICA}4 PL.01.09.crt" "${P}62/${ICA}3 PL.01.09.crt" "${P}62/${ICA}2 PL.01.09.crt" "${P}62/${ICA}1 PL.01.09.crt" "${P}62/${ROOT}CP.01.01.crt"
+run_test 63 0 "${P}63/${END}PL.01.10.crt" "${P}63/${ICA}4 PL.01.10.crt" "${P}63/${ICA}3 PL.01.10.crt" "${P}63/${ICA}2 PL.01.10.crt" "${P}63/${ICA}1 PL.01.10.crt" "${P}63/${ROOT}CP.01.01.crt"
+
+
+echo "Successful tests:$SUCCESS"
+echo "Failed tests:$FAILURE"
diff --git a/hostap/tests/test_x509v3_nist2.sh b/hostap/tests/test_x509v3_nist2.sh
new file mode 100755
index 0000000..ec34a8b
--- /dev/null
+++ b/hostap/tests/test_x509v3_nist2.sh
@@ -0,0 +1,177 @@
+#!/bin/bash
+
+# Public Key Interoperability Test Suite (PKITS)
+# http://csrc.nist.gov/pki/testing/x509paths.html
+# http://csrc.nist.gov/groups/ST/crypto_apps_infra/documents/PKITS_data.zip
+
+if [ -z "$1" ]; then
+    echo "usage: $0 <path to root test directory>"
+    exit 1
+fi
+
+TESTS=$1
+
+if [ ! -d $TESTS ]; then
+    echo "Not a directory: $TESTS"
+    exit 1
+fi
+
+X509TEST="$PWD/test-x509v3 -v"
+TMPOUT="$PWD/test_x509v3_nist2.out"
+
+# TODO: add support for validating CRLs
+
+SUCCESS=""
+FAILURE=""
+
+function run_test
+{
+    NUM=$1
+    RES=$2
+    shift 2
+    $X509TEST "$@" TrustAnchorRootCertificate.crt > $TMPOUT.$NUM
+    VALRES=$?
+    OK=0
+    if [ $RES -eq 0 ]; then
+	# expecting success
+	if [ $VALRES -eq 0 ]; then
+	    OK=1
+	else
+	    echo "$NUM failed - expected validation success"
+	    OK=0
+	fi
+    else
+	# expecting failure
+	if [ $VALRES -eq 0 ]; then
+	    echo "$NUM failed - expected validation failure"
+	    OK=0
+	else
+	    REASON=`grep "Certificate chain validation failed: " $TMPOUT.$NUM`
+	    if [ $? -eq 0 ]; then
+		REASONNUM=`echo "$REASON" | colrm 1 37`
+		if [ $REASONNUM -eq $RES ]; then
+		    OK=1
+		else
+		    echo "$NUM failed - expected validation result $RES; result was $REASONNUM"
+		    OK=0
+		fi
+	    else
+		if [ $RES -eq -1 ]; then
+		    if grep -q "Failed to parse X.509 certificate" $TMPOUT.$NUM; then
+			OK=1
+		    else
+			echo "$NUM failed - expected parsing failure; other type of error detected"
+			OK=0
+		    fi
+		else
+		    echo "$NUM failed - expected validation failure; other type of error detected"
+		    OK=0
+		fi
+	    fi
+	fi
+    fi
+    if [ $OK -eq 1 ]; then
+	rm $TMPOUT.$NUM
+	SUCCESS="$SUCCESS $NUM"
+    else
+	FAILURE="$FAILURE $NUM"
+    fi
+}
+
+pushd $TESTS/certs
+
+run_test 4.1.1 0 ValidCertificatePathTest1EE.crt GoodCACert.crt
+run_test 4.1.2 1 InvalidCASignatureTest2EE.crt BadSignedCACert.crt
+run_test 4.1.3 1 InvalidEESignatureTest3EE.crt GoodCACert.crt
+
+run_test 4.2.1 4 InvalidCAnotBeforeDateTest1EE.crt BadnotBeforeDateCACert.crt
+run_test 4.2.2 4 InvalidEEnotBeforeDateTest2EE.crt GoodCACert.crt
+run_test 4.2.3 0 Validpre2000UTCnotBeforeDateTest3EE.crt GoodCACert.crt
+run_test 4.2.4 0 ValidGeneralizedTimenotBeforeDateTest4EE.crt GoodCACert.crt
+run_test 4.2.5 4 InvalidCAnotAfterDateTest5EE.crt BadnotAfterDateCACert.crt
+run_test 4.2.6 4 InvalidEEnotAfterDateTest6EE.crt GoodCACert.crt
+run_test 4.2.7 4 Invalidpre2000UTCEEnotAfterDateTest7EE.crt GoodCACert.crt
+run_test 4.2.8 0 ValidGeneralizedTimenotAfterDateTest8EE.crt GoodCACert.crt
+
+run_test 4.3.1 5 InvalidNameChainingTest1EE.crt GoodCACert.crt
+run_test 4.3.2 5 InvalidNameChainingOrderTest2EE.crt NameOrderingCACert.crt
+run_test 4.3.3 0 ValidNameChainingWhitespaceTest3EE.crt GoodCACert.crt
+run_test 4.3.4 0 ValidNameChainingWhitespaceTest4EE.crt GoodCACert.crt
+run_test 4.3.5 0 ValidNameChainingCapitalizationTest5EE.crt GoodCACert.crt
+run_test 4.3.6 0 ValidNameUIDsTest6EE.crt UIDCACert.crt
+run_test 4.3.7 0 ValidRFC3280MandatoryAttributeTypesTest7EE.crt RFC3280MandatoryAttributeTypesCACert.crt
+run_test 4.3.8 0 ValidRFC3280OptionalAttributeTypesTest8EE.crt RFC3280OptionalAttributeTypesCACert.crt
+run_test 4.3.9 0 ValidUTF8StringEncodedNamesTest9EE.crt UTF8StringEncodedNamesCACert.crt
+run_test 4.3.10 0 ValidRolloverfromPrintableStringtoUTF8StringTest10EE.crt RolloverfromPrintableStringtoUTF8StringCACert.crt
+run_test 4.3.11 0 ValidUTF8StringCaseInsensitiveMatchTest11EE.crt UTF8StringCaseInsensitiveMatchCACert.crt
+
+run_test 4.4.1 1 InvalidMissingCRLTest1EE.crt NoCRLCACert.crt
+# skip rest of 4.4.x tests since CRLs are not yet supported
+
+run_test 4.5.1 0 ValidBasicSelfIssuedOldWithNewTest1EE.crt BasicSelfIssuedNewKeyOldWithNewCACert.crt BasicSelfIssuedNewKeyCACert.crt
+run_test 4.5.2 3 InvalidBasicSelfIssuedOldWithNewTest2EE.crt BasicSelfIssuedNewKeyOldWithNewCACert.crt BasicSelfIssuedNewKeyCACert.crt
+run_test 4.5.3 0 ValidBasicSelfIssuedNewWithOldTest3EE.crt BasicSelfIssuedOldKeyNewWithOldCACert.crt BasicSelfIssuedOldKeyCACert.crt
+run_test 4.5.4 0 ValidBasicSelfIssuedNewWithOldTest4EE.crt BasicSelfIssuedOldKeyNewWithOldCACert.crt BasicSelfIssuedOldKeyCACert.crt
+run_test 4.5.5 3 InvalidBasicSelfIssuedNewWithOldTest5EE.crt BasicSelfIssuedOldKeyNewWithOldCACert.crt BasicSelfIssuedOldKeyCACert.crt
+run_test 4.5.6 0 ValidBasicSelfIssuedCRLSigningKeyTest6EE.crt BasicSelfIssuedCRLSigningKeyCRLCert.crt BasicSelfIssuedCRLSigningKeyCACert.crt
+run_test 4.5.7 3 InvalidBasicSelfIssuedCRLSigningKeyTest7EE.crt BasicSelfIssuedCRLSigningKeyCRLCert.crt BasicSelfIssuedCRLSigningKeyCACert.crt
+run_test 4.5.8 1 InvalidBasicSelfIssuedCRLSigningKeyTest8EE.crt BasicSelfIssuedCRLSigningKeyCRLCert.crt BasicSelfIssuedCRLSigningKeyCACert.crt
+
+run_test 4.6.1 1 InvalidMissingbasicConstraintsTest1EE.crt MissingbasicConstraintsCACert.crt
+run_test 4.6.2 1 InvalidcAFalseTest2EE.crt basicConstraintsCriticalcAFalseCACert.crt
+run_test 4.6.3 1 InvalidcAFalseTest3EE.crt basicConstraintsNotCriticalcAFalseCACert.crt
+run_test 4.6.4 0 ValidbasicConstraintsNotCriticalTest4EE.crt basicConstraintsNotCriticalCACert.crt
+run_test 4.6.5 1 InvalidpathLenConstraintTest5EE.crt pathLenConstraint0subCACert.crt pathLenConstraint0CACert.crt
+run_test 4.6.6 1 InvalidpathLenConstraintTest6EE.crt pathLenConstraint0subCACert.crt pathLenConstraint0CACert.crt
+run_test 4.6.7 0 ValidpathLenConstraintTest7EE.crt pathLenConstraint0CACert.crt
+run_test 4.6.8 0 ValidpathLenConstraintTest8EE.crt pathLenConstraint0CACert.crt
+run_test 4.6.9 1 InvalidpathLenConstraintTest9EE.crt pathLenConstraint6subsubCA00Cert.crt pathLenConstraint6subCA0Cert.crt pathLenConstraint6CACert.crt
+run_test 4.6.10 1 InvalidpathLenConstraintTest10EE.crt pathLenConstraint6subsubCA00Cert.crt pathLenConstraint6subCA0Cert.crt pathLenConstraint6CACert.crt
+run_test 4.6.11 1 InvalidpathLenConstraintTest11EE.crt pathLenConstraint6subsubsubCA11XCert.crt pathLenConstraint6subsubCA11Cert.crt pathLenConstraint6subCA1Cert.crt pathLenConstraint6CACert.crt
+run_test 4.6.12 1 InvalidpathLenConstraintTest12EE.crt pathLenConstraint6subsubsubCA11XCert.crt pathLenConstraint6subsubCA11Cert.crt pathLenConstraint6subCA1Cert.crt pathLenConstraint6CACert.crt
+run_test 4.6.13 0 ValidpathLenConstraintTest13EE.crt pathLenConstraint6subsubsubCA41XCert.crt pathLenConstraint6subsubCA41Cert.crt pathLenConstraint6subCA4Cert.crt pathLenConstraint6CACert.crt
+run_test 4.6.14 0 ValidpathLenConstraintTest14EE.crt pathLenConstraint6subsubsubCA41XCert.crt pathLenConstraint6subsubCA41Cert.crt pathLenConstraint6subCA4Cert.crt pathLenConstraint6CACert.crt
+run_test 4.6.15 0 ValidSelfIssuedpathLenConstraintTest15EE.crt pathLenConstraint0SelfIssuedCACert.crt pathLenConstraint0CACert.crt
+run_test 4.6.16 1 InvalidSelfIssuedpathLenConstraintTest16EE.crt pathLenConstraint0subCA2Cert.crt pathLenConstraint0SelfIssuedCACert.crt pathLenConstraint0CACert.crt
+run_test 4.6.17 0 ValidSelfIssuedpathLenConstraintTest17EE.crt pathLenConstraint1SelfIssuedsubCACert.crt pathLenConstraint1subCACert.crt pathLenConstraint1SelfIssuedCACert.crt pathLenConstraint1CACert.crt
+
+run_test 4.7.1 1 InvalidkeyUsageCriticalkeyCertSignFalseTest1EE.crt keyUsageCriticalkeyCertSignFalseCACert.crt
+run_test 4.7.2 1 InvalidkeyUsageNotCriticalkeyCertSignFalseTest2EE.crt keyUsageNotCriticalkeyCertSignFalseCACert.crt
+run_test 4.7.3 0 ValidkeyUsageNotCriticalTest3EE.crt keyUsageNotCriticalCACert.crt
+run_test 4.7.4 1 InvalidkeyUsageCriticalcRLSignFalseTest4EE.crt keyUsageCriticalcRLSignFalseCACert.crt
+run_test 4.7.5 1 InvalidkeyUsageNotCriticalcRLSignFalseTest5EE.crt keyUsageNotCriticalcRLSignFalseCACert.crt
+
+run_test 4.8.1 0 ValidCertificatePathTest1EE.crt GoodCACert.crt
+run_test 4.8.2 0 AllCertificatesNoPoliciesTest2EE.crt NoPoliciesCACert.crt
+run_test 4.8.3 0 DifferentPoliciesTest3EE.crt PoliciesP2subCACert.crt GoodCACert.crt
+run_test 4.8.4 0 DifferentPoliciesTest4EE.crt GoodsubCACert.crt GoodCACert.crt
+run_test 4.8.5 0 DifferentPoliciesTest5EE.crt PoliciesP2subCA2Cert.crt GoodCACert.crt
+run_test 4.8.6 0 OverlappingPoliciesTest6EE.crt PoliciesP1234subsubCAP123P12Cert.crt PoliciesP1234subCAP123Cert.crt PoliciesP1234CACert.crt
+run_test 4.8.7 0 DifferentPoliciesTest7EE.crt PoliciesP123subsubCAP12P1Cert.crt PoliciesP123subCAP12Cert.crt PoliciesP123CACert.crt
+run_test 4.8.8 0 DifferentPoliciesTest8EE.crt PoliciesP12subsubCAP1P2Cert.crt PoliciesP12subCAP1Cert.crt PoliciesP12CACert.crt
+run_test 4.8.9 0 DifferentPoliciesTest9EE.crt PoliciesP123subsubsubCAP12P2P1Cert.crt PoliciesP123subsubCAP12P2Cert.crt PoliciesP123subCAP12Cert.crt PoliciesP123CACert.crt
+run_test 4.8.10 0 AllCertificatesSamePoliciesTest10EE.crt PoliciesP12CACert.crt
+run_test 4.8.11 0 AllCertificatesanyPolicyTest11EE.crt anyPolicyCACert.crt
+run_test 4.8.12 0 DifferentPoliciesTest12EE.crt PoliciesP3CACert.crt
+run_test 4.8.13 0 AllCertificatesSamePoliciesTest13EE.crt PoliciesP123CACert.crt
+run_test 4.8.14 0 AnyPolicyTest14EE.crt anyPolicyCACert.crt
+run_test 4.8.15 0 UserNoticeQualifierTest15EE.crt
+run_test 4.8.16 0 UserNoticeQualifierTest16EE.crt GoodCACert.crt
+run_test 4.8.17 0 UserNoticeQualifierTest17EE.crt GoodCACert.crt
+run_test 4.8.18 0 UserNoticeQualifierTest18EE.crt PoliciesP12CACert.crt
+run_test 4.8.19 0 UserNoticeQualifierTest19EE.crt TrustAnchorRootCertificate.crt
+run_test 4.8.20 0 CPSPointerQualifierTest20EE.crt GoodCACert.crt
+
+run_test 4.16.1 0 ValidUnknownNotCriticalCertificateExtensionTest1EE.crt
+run_test 4.16.2 -1 InvalidUnknownCriticalCertificateExtensionTest2EE.crt
+
+if false; then
+# DSA tests
+run_test 4.1.4 0 ValidDSASignaturesTest4EE.crt DSACACert.crt
+fi
+
+popd
+
+
+echo "Successful tests:$SUCCESS"
+echo "Failed tests:$FAILURE"
diff --git a/hostap/tests/wnm-fuzzer/Makefile b/hostap/tests/wnm-fuzzer/Makefile
new file mode 100644
index 0000000..dede75b
--- /dev/null
+++ b/hostap/tests/wnm-fuzzer/Makefile
@@ -0,0 +1,91 @@
+all: wnm-fuzzer
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+SRC=../../src
+
+CFLAGS += -I$(SRC)
+CFLAGS += -DCONFIG_WNM
+CFLAGS += -DCONFIG_INTERWORKING
+CFLAGS += -DCONFIG_GAS
+CFLAGS += -DCONFIG_HS20
+CFLAGS += -DIEEE8021X_EAPOL
+
+$(SRC)/utils/libutils.a:
+	$(MAKE) -C $(SRC)/utils
+
+$(SRC)/common/libcommon.a:
+	$(MAKE) -C $(SRC)/common
+
+$(SRC)/crypto/libcrypto.a:
+	$(MAKE) -C $(SRC)/crypto
+
+$(SRC)/tls/libtls.a:
+	$(MAKE) -C $(SRC)/tls
+
+$(SRC)/rsn_supp/librsn_supp.a:
+	$(MAKE) -C $(SRC)/rsn_supp
+
+$(SRC)/eapol_supp/libeapol_supp.a:
+	$(MAKE) -C $(SRC)/eapol_supp
+
+$(SRC)/eap_peer/libeap_peer.a:
+	$(MAKE) -C $(SRC)/eap_peer
+
+$(SRC)/eap_common/libeap_common.a:
+	$(MAKE) -C $(SRC)/eap_common
+
+$(SRC)/l2_packet/libl2_packet.a:
+	$(MAKE) -C $(SRC)/l2_packet
+
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/tls/libtls.a
+LIBS += $(SRC)/rsn_supp/librsn_supp.a
+LIBS += $(SRC)/eapol_supp/libeapol_supp.a
+LIBS += $(SRC)/eap_peer/libeap_peer.a
+LIBS += $(SRC)/eap_common/libeap_common.a
+LIBS += $(SRC)/l2_packet/libl2_packet.a
+LIBS += $(SRC)/utils/libutils.a
+
+ELIBS += $(SRC)/crypto/libcrypto.a
+ELIBS += $(SRC)/tls/libtls.a
+
+CFLAGS += -I$(SRC)/utils
+OBJS += ../../wpa_supplicant/wnm_sta.o
+OBJS += ../../wpa_supplicant/bss.o
+OBJS += ../../wpa_supplicant/scan.o
+OBJS += ../../wpa_supplicant/notify.o
+OBJS += ../../wpa_supplicant/wpa_supplicant.o
+OBJS += ../../wpa_supplicant/config.o
+OBJS += ../../wpa_supplicant/config_file.o
+OBJS += ../../wpa_supplicant/blacklist.o
+OBJS += ../../wpa_supplicant/events.o
+OBJS += ../../wpa_supplicant/wpas_glue.o
+OBJS += ../../wpa_supplicant/wmm_ac.o
+OBJS += ../../wpa_supplicant/eap_register.o
+OBJS += ../../wpa_supplicant/gas_query.o
+OBJS += ../../wpa_supplicant/offchannel.o
+OBJS += ../../wpa_supplicant/interworking.o
+OBJS += ../../wpa_supplicant/hs20_supplicant.o
+OBJS += $(SRC)/drivers/drivers.o
+OBJS += $(SRC)/drivers/driver_common.o
+
+wnm-fuzzer: wnm-fuzzer.o $(OBJS) $(LIBS)
+	$(LDO) $(LDFLAGS) -o $@ $^ $(LIBS) $(ELIBS)
+
+clean:
+	$(MAKE) -C $(SRC) clean
+	rm -f wnm-fuzzer *~ *.o *.d
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/tests/wnm-fuzzer/bss-tm-req.dat b/hostap/tests/wnm-fuzzer/bss-tm-req.dat
new file mode 100644
index 0000000..14510bb
--- /dev/null
+++ b/hostap/tests/wnm-fuzzer/bss-tm-req.dat
Binary files differ
diff --git a/hostap/tests/wnm-fuzzer/wnm-fuzzer.c b/hostap/tests/wnm-fuzzer/wnm-fuzzer.c
new file mode 100644
index 0000000..8efa311
--- /dev/null
+++ b/hostap/tests/wnm-fuzzer/wnm-fuzzer.c
@@ -0,0 +1,106 @@
+/*
+ * wpa_supplicant - WNM fuzzer
+ * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/wpa_i.h"
+#include "../../wpa_supplicant/wpa_supplicant_i.h"
+#include "../../wpa_supplicant/bss.h"
+#include "../../wpa_supplicant/wnm_sta.h"
+
+
+struct arg_ctx {
+	const char *fname;
+	struct wpa_supplicant wpa_s;
+	struct wpa_bss bss;
+	struct wpa_driver_ops driver;
+	struct wpa_sm wpa;
+};
+
+
+static void test_send_wnm(void *eloop_data, void *user_ctx)
+{
+	struct arg_ctx *ctx = eloop_data;
+	char *data;
+	size_t len;
+	struct ieee80211_mgmt *mgmt;
+
+	wpa_printf(MSG_INFO, "wnm-fuzzer: Send '%s'", ctx->fname);
+
+	data = os_readfile(ctx->fname, &len);
+	if (!data) {
+		wpa_printf(MSG_ERROR, "Could not read '%s'", ctx->fname);
+		goto out;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "fuzzer - WNM", data, len);
+
+	mgmt = (struct ieee80211_mgmt *) data;
+	ieee802_11_rx_wnm_action(&ctx->wpa_s, mgmt, len);
+
+out:
+	os_free(data);
+	eloop_terminate();
+}
+
+
+static int init_wpa(struct arg_ctx *ctx)
+{
+	ctx->wpa_s.wpa_state = WPA_COMPLETED;
+	os_memcpy(ctx->wpa_s.bssid, "\x02\x00\x00\x00\x03\x00", ETH_ALEN);
+	ctx->wpa_s.current_bss = &ctx->bss;
+	ctx->wpa_s.driver = &ctx->driver;
+	ctx->wpa_s.wpa = &ctx->wpa;
+
+	return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct arg_ctx ctx;
+	int ret = -1;
+
+	if (argc < 2) {
+		printf("usage: %s <file>\n", argv[0]);
+		return -1;
+	}
+
+	if (os_program_init())
+		return -1;
+
+	wpa_debug_level = 0;
+	wpa_debug_show_keys = 1;
+
+	if (eloop_init()) {
+		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+		return -1;
+	}
+
+	os_memset(&ctx, 0, sizeof(ctx));
+	ctx.fname = argv[1];
+	if (init_wpa(&ctx))
+		goto fail;
+
+	eloop_register_timeout(0, 0, test_send_wnm, &ctx, NULL);
+
+	wpa_printf(MSG_DEBUG, "Starting eloop");
+	eloop_run();
+	wpa_printf(MSG_DEBUG, "eloop done");
+
+	ret = 0;
+fail:
+	eloop_destroy();
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/hostap/tests/wnm-fuzzer/wnm-notif.dat b/hostap/tests/wnm-fuzzer/wnm-notif.dat
new file mode 100644
index 0000000..c234d3a
--- /dev/null
+++ b/hostap/tests/wnm-fuzzer/wnm-notif.dat
Binary files differ
diff --git a/hostap/wlantest/Makefile b/hostap/wlantest/Makefile
new file mode 100644
index 0000000..320fdbb
--- /dev/null
+++ b/hostap/wlantest/Makefile
@@ -0,0 +1,132 @@
+ALL=wlantest wlantest_cli test_vectors
+
+all: $(ALL)
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef RANLIB
+RANLIB=ranlib
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+
+CFLAGS += -I.
+CFLAGS += -I../src
+CFLAGS += -I../src/utils
+
+# glibc < 2.17 needs -lrt for clock_gettime()
+LIBS += -lrt
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
+
+%.o: %.c
+	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
+	@$(E) "  CC " $<
+
+
+OBJS_lib += ../src/utils/libutils.a
+OBJS_lib += ../src/crypto/libcrypto.a
+
+CFLAGS += -DCONFIG_PEERKEY
+CFLAGS += -DCONFIG_IEEE80211W
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_HS20
+CFLAGS += -DCONFIG_DEBUG_FILE
+
+OBJS += ../src/common/ieee802_11_common.o
+OBJS += ../src/common/wpa_common.o
+OBJS += ../src/radius/radius.o
+OBJS += ../src/rsn_supp/wpa_ie.o
+
+OBJS += wlantest.o
+OBJS += readpcap.o
+OBJS += writepcap.o
+OBJS += monitor.o
+OBJS += process.o
+OBJS += wired.o
+OBJS += rx_mgmt.o
+OBJS += rx_data.o
+OBJS += rx_eapol.o
+OBJS += rx_ip.o
+OBJS += rx_tdls.o
+OBJS += bss.o
+OBJS += sta.o
+OBJS += crc32.o
+OBJS += ccmp.o
+OBJS += tkip.o
+OBJS += ctrl.o
+OBJS += inject.o
+OBJS += wep.o
+OBJS += bip.o
+OBJS += gcmp.o
+
+LIBS += -lpcap
+
+TOBJS += test_vectors.o
+TOBJS += crc32.o
+TOBJS += ccmp.o
+TOBJS += tkip.o
+TOBJS += wep.o
+TOBJS += bip.o
+TOBJS += gcmp.o
+
+
+../src/utils/libutils.a:
+	$(MAKE) -C ../src/utils
+
+../src/crypto/libcrypto.a:
+	$(MAKE) -C ../src/crypto
+
+
+ifneq ($(CONFIG_SOLIB), yes)
+LIBWLANTEST = libwlantest.a
+libwlantest.a: $(OBJS_lib)
+	$(AR) crT libwlantest.a $(OBJS_lib)
+	$(RANLIB) libwlantest.a
+
+else
+CFLAGS  += -fPIC -DPIC
+LDFLAGS += -shared
+
+LIBWLANTEST  = libwlantest.so
+libwlantest.so: $(OBJS_lib)
+	$(LDO) $(LDFLAGS) $(OBJS_lib) -o $(LIBWLANTEST)
+
+endif
+
+
+OBJS_cli = wlantest_cli.o
+
+
+wlantest: $(OBJS) $(LIBWLANTEST)
+	$(LDO) $(LDFLAGS) -o wlantest $(OBJS) -L. -lwlantest $(LIBS)
+
+wlantest_cli: $(OBJS_cli) $(LIBWLANTEST)
+	$(LDO) $(LDFLAGS) -o wlantest_cli $(OBJS_cli) -L. -lwlantest $(LIBS)
+
+test_vectors: $(TOBJS) $(LIBWLANTEST)
+	$(LDO) $(LDFLAGS) -o test_vectors $(TOBJS) -L. -lwlantest $(LIBS)
+
+clean:
+	$(MAKE) -C ../src clean
+	rm -f core *~ *.o *.d libwlantest.a libwlantest.so $(ALL)
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/wlantest/bip.c b/hostap/wlantest/bip.c
new file mode 100644
index 0000000..bda8036
--- /dev/null
+++ b/hostap/wlantest/bip.c
@@ -0,0 +1,133 @@
+/*
+ * BIP
+ * Copyright (c) 2010-2012, 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 "common/ieee802_11_defs.h"
+#include "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+u8 * bip_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
+		 u8 *ipn, int keyid, size_t *prot_len)
+{
+	u8 *prot, *pos, *buf;
+	u8 mic[16];
+	u16 fc;
+	struct ieee80211_hdr *hdr;
+	size_t plen;
+
+	plen = len + igtk_len == 32 ? 26 : 18;
+	prot = os_malloc(plen);
+	if (prot == NULL)
+		return NULL;
+	os_memcpy(prot, frame, len);
+	pos = prot + len;
+	*pos++ = WLAN_EID_MMIE;
+	*pos++ = igtk_len == 32 ? 24 : 16;
+	WPA_PUT_LE16(pos, keyid);
+	pos += 2;
+	os_memcpy(pos, ipn, 6);
+	pos += 6;
+	os_memset(pos, 0, igtk_len == 32 ? 16 : 8); /* MIC */
+
+	buf = os_malloc(plen + 20 - 24);
+	if (buf == NULL) {
+		os_free(prot);
+		return NULL;
+	}
+
+	/* BIP AAD: FC(masked) A1 A2 A3 */
+	hdr = (struct ieee80211_hdr *) frame;
+	fc = le_to_host16(hdr->frame_control);
+	fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+	WPA_PUT_LE16(buf, fc);
+	os_memcpy(buf + 2, hdr->addr1, 3 * ETH_ALEN);
+	os_memcpy(buf + 20, prot + 24, plen - 24);
+	wpa_hexdump(MSG_MSGDUMP, "BIP: AAD|Body(masked)", buf, plen + 20 - 24);
+	/* MIC = L(AES-128-CMAC(AAD || Frame Body(masked)), 0, 64) */
+	if (omac1_aes_128(igtk, buf, plen + 20 - 24, mic) < 0) {
+		os_free(prot);
+		os_free(buf);
+		return NULL;
+	}
+	os_free(buf);
+
+	os_memcpy(pos, mic, igtk_len == 32 ? 16 : 8);
+	wpa_hexdump(MSG_DEBUG, "BIP MMIE MIC", pos, igtk_len == 32 ? 16 : 8);
+
+	*prot_len = plen;
+	return prot;
+}
+
+
+u8 * bip_gmac_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
+		      u8 *ipn, int keyid, size_t *prot_len)
+{
+	u8 *prot, *pos, *buf;
+	u16 fc;
+	struct ieee80211_hdr *hdr;
+	size_t plen;
+	u8 nonce[12], *npos;
+
+	plen = len + 26;
+	prot = os_malloc(plen);
+	if (prot == NULL)
+		return NULL;
+	os_memcpy(prot, frame, len);
+	pos = prot + len;
+	*pos++ = WLAN_EID_MMIE;
+	*pos++ = 24;
+	WPA_PUT_LE16(pos, keyid);
+	pos += 2;
+	os_memcpy(pos, ipn, 6);
+	pos += 6;
+	os_memset(pos, 0, 16); /* MIC */
+
+	buf = os_malloc(plen + 20 - 24);
+	if (buf == NULL) {
+		os_free(prot);
+		return NULL;
+	}
+
+	/* BIP AAD: FC(masked) A1 A2 A3 */
+	hdr = (struct ieee80211_hdr *) frame;
+	fc = le_to_host16(hdr->frame_control);
+	fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+	WPA_PUT_LE16(buf, fc);
+	os_memcpy(buf + 2, hdr->addr1, 3 * ETH_ALEN);
+	os_memcpy(buf + 20, prot + 24, plen - 24);
+	wpa_hexdump(MSG_MSGDUMP, "BIP-GMAC: AAD|Body(masked)",
+		    buf, plen + 20 - 24);
+
+	/* Nonce: A2 | IPN */
+	os_memcpy(nonce, hdr->addr2, ETH_ALEN);
+	npos = nonce + ETH_ALEN;
+	*npos++ = ipn[5];
+	*npos++ = ipn[4];
+	*npos++ = ipn[3];
+	*npos++ = ipn[2];
+	*npos++ = ipn[1];
+	*npos++ = ipn[0];
+	wpa_hexdump(MSG_EXCESSIVE, "BIP-GMAC: Nonce", nonce, sizeof(nonce));
+
+	/* MIC = AES-GMAC(AAD || Frame Body(masked)) */
+	if (aes_gmac(igtk, igtk_len, nonce, sizeof(nonce),
+		     buf, plen + 20 - 24, pos) < 0) {
+		os_free(prot);
+		os_free(buf);
+		return NULL;
+	}
+	os_free(buf);
+
+	wpa_hexdump(MSG_DEBUG, "BIP-GMAC MMIE MIC", pos, 16);
+
+	*prot_len = plen;
+	return prot;
+}
diff --git a/hostap/wlantest/bss.c b/hostap/wlantest/bss.c
new file mode 100644
index 0000000..f021956
--- /dev/null
+++ b/hostap/wlantest/bss.c
@@ -0,0 +1,344 @@
+/*
+ * BSS list
+ * Copyright (c) 2010, 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 "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/sha1.h"
+#include "wlantest.h"
+
+
+struct wlantest_bss * bss_find(struct wlantest *wt, const u8 *bssid)
+{
+	struct wlantest_bss *bss;
+
+	dl_list_for_each(bss, &wt->bss, struct wlantest_bss, list) {
+		if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+			return bss;
+	}
+
+	return NULL;
+}
+
+
+struct wlantest_bss * bss_get(struct wlantest *wt, const u8 *bssid)
+{
+	struct wlantest_bss *bss;
+
+	if (bssid[0] & 0x01)
+		return NULL; /* Skip group addressed frames */
+
+	bss = bss_find(wt, bssid);
+	if (bss)
+		return bss;
+
+	bss = os_zalloc(sizeof(*bss));
+	if (bss == NULL)
+		return NULL;
+	dl_list_init(&bss->sta);
+	dl_list_init(&bss->pmk);
+	dl_list_init(&bss->tdls);
+	os_memcpy(bss->bssid, bssid, ETH_ALEN);
+	dl_list_add(&wt->bss, &bss->list);
+	wpa_printf(MSG_DEBUG, "Discovered new BSS - " MACSTR,
+		   MAC2STR(bss->bssid));
+	return bss;
+}
+
+
+void pmk_deinit(struct wlantest_pmk *pmk)
+{
+	dl_list_del(&pmk->list);
+	os_free(pmk);
+}
+
+
+void tdls_deinit(struct wlantest_tdls *tdls)
+{
+	dl_list_del(&tdls->list);
+	os_free(tdls);
+}
+
+
+void bss_deinit(struct wlantest_bss *bss)
+{
+	struct wlantest_sta *sta, *n;
+	struct wlantest_pmk *pmk, *np;
+	struct wlantest_tdls *tdls, *nt;
+	dl_list_for_each_safe(sta, n, &bss->sta, struct wlantest_sta, list)
+		sta_deinit(sta);
+	dl_list_for_each_safe(pmk, np, &bss->pmk, struct wlantest_pmk, list)
+		pmk_deinit(pmk);
+	dl_list_for_each_safe(tdls, nt, &bss->tdls, struct wlantest_tdls, list)
+		tdls_deinit(tdls);
+	dl_list_del(&bss->list);
+	os_free(bss);
+}
+
+
+int bss_add_pmk_from_passphrase(struct wlantest_bss *bss,
+				const char *passphrase)
+{
+	struct wlantest_pmk *pmk;
+
+	pmk = os_zalloc(sizeof(*pmk));
+	if (pmk == NULL)
+		return -1;
+	if (pbkdf2_sha1(passphrase, bss->ssid, bss->ssid_len, 4096,
+			pmk->pmk, sizeof(pmk->pmk)) < 0) {
+		os_free(pmk);
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "Add possible PMK for BSSID " MACSTR
+		   " based on passphrase '%s'",
+		   MAC2STR(bss->bssid), passphrase);
+	wpa_hexdump(MSG_DEBUG, "Possible PMK", pmk->pmk, sizeof(pmk->pmk));
+	dl_list_add(&bss->pmk, &pmk->list);
+
+	return 0;
+}
+
+
+static void bss_add_pmk(struct wlantest *wt, struct wlantest_bss *bss)
+{
+	struct wlantest_passphrase *p;
+
+	dl_list_for_each(p, &wt->passphrase, struct wlantest_passphrase, list)
+	{
+		if (!is_zero_ether_addr(p->bssid) &&
+		    os_memcmp(p->bssid, bss->bssid, ETH_ALEN) != 0)
+			continue;
+		if (p->ssid_len &&
+		    (p->ssid_len != bss->ssid_len ||
+		     os_memcmp(p->ssid, bss->ssid, p->ssid_len) != 0))
+			continue;
+
+		if (bss_add_pmk_from_passphrase(bss, p->passphrase) < 0)
+			break;
+	}
+}
+
+
+void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
+		struct ieee802_11_elems *elems)
+{
+	struct wpa_ie_data data;
+	int update = 0;
+
+	if (bss->capab_info != bss->prev_capab_info)
+		update = 1;
+
+	if (elems->ssid == NULL || elems->ssid_len > 32) {
+		wpa_printf(MSG_INFO, "Invalid or missing SSID in a Beacon "
+			   "frame for " MACSTR, MAC2STR(bss->bssid));
+		bss->parse_error_reported = 1;
+		return;
+	}
+
+	if (bss->ssid_len != elems->ssid_len ||
+	    os_memcmp(bss->ssid, elems->ssid, bss->ssid_len) != 0) {
+		wpa_printf(MSG_DEBUG, "Store SSID '%s' for BSSID " MACSTR,
+			   wpa_ssid_txt(elems->ssid, elems->ssid_len),
+			   MAC2STR(bss->bssid));
+		os_memcpy(bss->ssid, elems->ssid, elems->ssid_len);
+		bss->ssid_len = elems->ssid_len;
+		bss_add_pmk(wt, bss);
+	}
+
+	if (elems->osen == NULL) {
+		if (bss->osenie[0]) {
+			add_note(wt, MSG_INFO, "BSS " MACSTR
+				 " - OSEN IE removed", MAC2STR(bss->bssid));
+			bss->rsnie[0] = 0;
+			update = 1;
+		}
+	} else {
+		if (bss->osenie[0] == 0 ||
+		    os_memcmp(bss->osenie, elems->osen - 2,
+			      elems->osen_len + 2) != 0) {
+			wpa_printf(MSG_INFO, "BSS " MACSTR " - OSEN IE "
+				   "stored", MAC2STR(bss->bssid));
+			wpa_hexdump(MSG_DEBUG, "OSEN IE", elems->osen - 2,
+				    elems->osen_len + 2);
+			update = 1;
+		}
+		os_memcpy(bss->osenie, elems->osen - 2,
+			  elems->osen_len + 2);
+	}
+
+	if (elems->rsn_ie == NULL) {
+		if (bss->rsnie[0]) {
+			add_note(wt, MSG_INFO, "BSS " MACSTR
+				 " - RSN IE removed", MAC2STR(bss->bssid));
+			bss->rsnie[0] = 0;
+			update = 1;
+		}
+	} else {
+		if (bss->rsnie[0] == 0 ||
+		    os_memcmp(bss->rsnie, elems->rsn_ie - 2,
+			      elems->rsn_ie_len + 2) != 0) {
+			wpa_printf(MSG_INFO, "BSS " MACSTR " - RSN IE "
+				   "stored", MAC2STR(bss->bssid));
+			wpa_hexdump(MSG_DEBUG, "RSN IE", elems->rsn_ie - 2,
+				    elems->rsn_ie_len + 2);
+			update = 1;
+		}
+		os_memcpy(bss->rsnie, elems->rsn_ie - 2,
+			  elems->rsn_ie_len + 2);
+	}
+
+	if (elems->wpa_ie == NULL) {
+		if (bss->wpaie[0]) {
+			add_note(wt, MSG_INFO, "BSS " MACSTR
+				 " - WPA IE removed", MAC2STR(bss->bssid));
+			bss->wpaie[0] = 0;
+			update = 1;
+		}
+	} else {
+		if (bss->wpaie[0] == 0 ||
+		    os_memcmp(bss->wpaie, elems->wpa_ie - 2,
+			      elems->wpa_ie_len + 2) != 0) {
+			wpa_printf(MSG_INFO, "BSS " MACSTR " - WPA IE "
+				   "stored", MAC2STR(bss->bssid));
+			wpa_hexdump(MSG_DEBUG, "WPA IE", elems->wpa_ie - 2,
+				    elems->wpa_ie_len + 2);
+			update = 1;
+		}
+		os_memcpy(bss->wpaie, elems->wpa_ie - 2,
+			  elems->wpa_ie_len + 2);
+	}
+
+	if (elems->mdie)
+		os_memcpy(bss->mdid, elems->mdie, 2);
+
+	if (!update)
+		return;
+
+	bss->prev_capab_info = bss->capab_info;
+	bss->proto = 0;
+	bss->pairwise_cipher = 0;
+	bss->group_cipher = 0;
+	bss->key_mgmt = 0;
+	bss->rsn_capab = 0;
+	bss->mgmt_group_cipher = 0;
+
+	if (bss->wpaie[0]) {
+		if (wpa_parse_wpa_ie_wpa(bss->wpaie, 2 + bss->wpaie[1], &data)
+		    < 0) {
+			add_note(wt, MSG_INFO, "Failed to parse WPA IE from "
+				 MACSTR, MAC2STR(bss->bssid));
+		} else {
+			bss->proto |= data.proto;
+			bss->pairwise_cipher |= data.pairwise_cipher;
+			bss->group_cipher |= data.group_cipher;
+			bss->key_mgmt |= data.key_mgmt;
+			bss->rsn_capab = data.capabilities;
+			bss->mgmt_group_cipher |= data.mgmt_group_cipher;
+		}
+	}
+
+	if (bss->rsnie[0]) {
+		if (wpa_parse_wpa_ie_rsn(bss->rsnie, 2 + bss->rsnie[1], &data)
+		    < 0) {
+			add_note(wt, MSG_INFO, "Failed to parse RSN IE from "
+				 MACSTR, MAC2STR(bss->bssid));
+		} else {
+			bss->proto |= data.proto;
+			bss->pairwise_cipher |= data.pairwise_cipher;
+			bss->group_cipher |= data.group_cipher;
+			bss->key_mgmt |= data.key_mgmt;
+			bss->rsn_capab = data.capabilities;
+			bss->mgmt_group_cipher |= data.mgmt_group_cipher;
+		}
+	}
+
+	if (bss->osenie[0]) {
+		bss->proto |= WPA_PROTO_OSEN;
+		bss->pairwise_cipher |= WPA_CIPHER_CCMP;
+		bss->group_cipher |= WPA_CIPHER_CCMP;
+		bss->key_mgmt |= WPA_KEY_MGMT_OSEN;
+	}
+
+	if (!(bss->proto & WPA_PROTO_RSN) ||
+	    !(bss->rsn_capab & WPA_CAPABILITY_MFPC))
+		bss->mgmt_group_cipher = 0;
+
+	if (!bss->wpaie[0] && !bss->rsnie[0] && !bss->osenie[0] &&
+	    (bss->capab_info & WLAN_CAPABILITY_PRIVACY))
+		bss->group_cipher = WPA_CIPHER_WEP40;
+
+	wpa_printf(MSG_INFO, "BSS " MACSTR
+		   " proto=%s%s%s%s"
+		   "pairwise=%s%s%s%s%s%s%s"
+		   "group=%s%s%s%s%s%s%s%s%s"
+		   "mgmt_group_cipher=%s%s%s%s%s"
+		   "key_mgmt=%s%s%s%s%s%s%s%s%s"
+		   "rsn_capab=%s%s%s%s%s",
+		   MAC2STR(bss->bssid),
+		   bss->proto == 0 ? "OPEN " : "",
+		   bss->proto & WPA_PROTO_WPA ? "WPA " : "",
+		   bss->proto & WPA_PROTO_RSN ? "WPA2 " : "",
+		   bss->proto & WPA_PROTO_OSEN ? "OSEN " : "",
+		   bss->pairwise_cipher == 0 ? "N/A " : "",
+		   bss->pairwise_cipher & WPA_CIPHER_NONE ? "NONE " : "",
+		   bss->pairwise_cipher & WPA_CIPHER_TKIP ? "TKIP " : "",
+		   bss->pairwise_cipher & WPA_CIPHER_CCMP ? "CCMP " : "",
+		   bss->pairwise_cipher & WPA_CIPHER_CCMP_256 ? "CCMP-256 " :
+		   "",
+		   bss->pairwise_cipher & WPA_CIPHER_GCMP ? "GCMP " : "",
+		   bss->pairwise_cipher & WPA_CIPHER_GCMP_256 ? "GCMP-256 " :
+		   "",
+		   bss->group_cipher == 0 ? "N/A " : "",
+		   bss->group_cipher & WPA_CIPHER_NONE ? "NONE " : "",
+		   bss->group_cipher & WPA_CIPHER_WEP40 ? "WEP40 " : "",
+		   bss->group_cipher & WPA_CIPHER_WEP104 ? "WEP104 " : "",
+		   bss->group_cipher & WPA_CIPHER_TKIP ? "TKIP " : "",
+		   bss->group_cipher & WPA_CIPHER_CCMP ? "CCMP " : "",
+		   bss->group_cipher & WPA_CIPHER_CCMP_256 ? "CCMP-256 " : "",
+		   bss->group_cipher & WPA_CIPHER_GCMP ? "GCMP " : "",
+		   bss->group_cipher & WPA_CIPHER_GCMP_256 ? "GCMP-256 " : "",
+		   bss->mgmt_group_cipher == 0 ? "N/A " : "",
+		   bss->mgmt_group_cipher & WPA_CIPHER_AES_128_CMAC ?
+		   "BIP " : "",
+		   bss->mgmt_group_cipher & WPA_CIPHER_BIP_GMAC_128 ?
+		   "BIP-GMAC-128 " : "",
+		   bss->mgmt_group_cipher & WPA_CIPHER_BIP_GMAC_256 ?
+		   "BIP-GMAC-256 " : "",
+		   bss->mgmt_group_cipher & WPA_CIPHER_BIP_CMAC_256 ?
+		   "BIP-CMAC-256 " : "",
+		   bss->key_mgmt == 0 ? "N/A " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_IEEE8021X ? "EAP " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_PSK ? "PSK " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_WPA_NONE ? "WPA-NONE " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X ? "FT-EAP " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_FT_PSK ? "FT-PSK " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256 ?
+		   "EAP-SHA256 " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_PSK_SHA256 ?
+		   "PSK-SHA256 " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_OSEN ? "OSEN " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_PREAUTH ? "PREAUTH " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_NO_PAIRWISE ?
+		   "NO_PAIRWISE " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_MFPR ? "MFPR " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_MFPC ? "MFPC " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_PEERKEY_ENABLED ?
+		   "PEERKEY " : "");
+}
+
+
+void bss_flush(struct wlantest *wt)
+{
+	struct wlantest_bss *bss, *n;
+	dl_list_for_each_safe(bss, n, &wt->bss, struct wlantest_bss, list)
+		bss_deinit(bss);
+}
diff --git a/hostap/wlantest/ccmp.c b/hostap/wlantest/ccmp.c
new file mode 100644
index 0000000..ee1f1a6
--- /dev/null
+++ b/hostap/wlantest/ccmp.c
@@ -0,0 +1,267 @@
+/*
+ * CTR with CBC-MAC Protocol (CCMP)
+ * Copyright (c) 2010-2012, 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 "common/ieee802_11_defs.h"
+#include "crypto/aes.h"
+#include "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+static void ccmp_aad_nonce(const struct ieee80211_hdr *hdr, const u8 *data,
+			   u8 *aad, size_t *aad_len, u8 *nonce)
+{
+	u16 fc, stype, seq;
+	int qos = 0, addr4 = 0;
+	u8 *pos;
+
+	nonce[0] = 0;
+
+	fc = le_to_host16(hdr->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+	if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+	    (WLAN_FC_TODS | WLAN_FC_FROMDS))
+		addr4 = 1;
+
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
+		fc &= ~0x0070; /* Mask subtype bits */
+		if (stype & 0x08) {
+			const u8 *qc;
+			qos = 1;
+			fc &= ~WLAN_FC_ORDER;
+			qc = (const u8 *) (hdr + 1);
+			if (addr4)
+				qc += ETH_ALEN;
+			nonce[0] = qc[0] & 0x0f;
+		}
+	} else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
+		nonce[0] |= 0x10; /* Management */
+
+	fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+	fc |= WLAN_FC_ISWEP;
+	WPA_PUT_LE16(aad, fc);
+	pos = aad + 2;
+	os_memcpy(pos, hdr->addr1, 3 * ETH_ALEN);
+	pos += 3 * ETH_ALEN;
+	seq = le_to_host16(hdr->seq_ctrl);
+	seq &= ~0xfff0; /* Mask Seq#; do not modify Frag# */
+	WPA_PUT_LE16(pos, seq);
+	pos += 2;
+
+	os_memcpy(pos, hdr + 1, addr4 * ETH_ALEN + qos * 2);
+	pos += addr4 * ETH_ALEN;
+	if (qos) {
+		pos[0] &= ~0x70;
+		if (1 /* FIX: either device has SPP A-MSDU Capab = 0 */)
+			pos[0] &= ~0x80;
+		pos++;
+		*pos++ = 0x00;
+	}
+
+	*aad_len = pos - aad;
+
+	os_memcpy(nonce + 1, hdr->addr2, ETH_ALEN);
+	nonce[7] = data[7]; /* PN5 */
+	nonce[8] = data[6]; /* PN4 */
+	nonce[9] = data[5]; /* PN3 */
+	nonce[10] = data[4]; /* PN2 */
+	nonce[11] = data[1]; /* PN1 */
+	nonce[12] = data[0]; /* PN0 */
+}
+
+
+u8 * ccmp_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+		  const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+	u8 aad[30], nonce[13];
+	size_t aad_len;
+	size_t mlen;
+	u8 *plain;
+
+	if (data_len < 8 + 8)
+		return NULL;
+
+	plain = os_malloc(data_len + AES_BLOCK_SIZE);
+	if (plain == NULL)
+		return NULL;
+
+	mlen = data_len - 8 - 8;
+
+	os_memset(aad, 0, sizeof(aad));
+	ccmp_aad_nonce(hdr, data, aad, &aad_len, nonce);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP AAD", aad, aad_len);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP nonce", nonce, 13);
+
+	if (aes_ccm_ad(tk, 16, nonce, 8, data + 8, mlen, aad, aad_len,
+		       data + 8 + mlen, plain) < 0) {
+		u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+		wpa_printf(MSG_INFO, "Invalid CCMP MIC in frame: A1=" MACSTR
+			   " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
+			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			   MAC2STR(hdr->addr3),
+			   WLAN_GET_SEQ_SEQ(seq_ctrl),
+			   WLAN_GET_SEQ_FRAG(seq_ctrl));
+		os_free(plain);
+		return NULL;
+	}
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP decrypted", plain, mlen);
+
+	*decrypted_len = mlen;
+	return plain;
+}
+
+
+void ccmp_get_pn(u8 *pn, const u8 *data)
+{
+	pn[0] = data[7]; /* PN5 */
+	pn[1] = data[6]; /* PN4 */
+	pn[2] = data[5]; /* PN3 */
+	pn[3] = data[4]; /* PN2 */
+	pn[4] = data[1]; /* PN1 */
+	pn[5] = data[0]; /* PN0 */
+}
+
+
+u8 * ccmp_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
+		  u8 *pn, int keyid, size_t *encrypted_len)
+{
+	u8 aad[30], nonce[13];
+	size_t aad_len, plen;
+	u8 *crypt, *pos;
+	struct ieee80211_hdr *hdr;
+
+	if (len < hdrlen || hdrlen < 24)
+		return NULL;
+	plen = len - hdrlen;
+
+	crypt = os_malloc(hdrlen + 8 + plen + 8 + AES_BLOCK_SIZE);
+	if (crypt == NULL)
+		return NULL;
+
+	os_memcpy(crypt, frame, hdrlen);
+	hdr = (struct ieee80211_hdr *) crypt;
+	hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+	pos = crypt + hdrlen;
+	*pos++ = pn[5]; /* PN0 */
+	*pos++ = pn[4]; /* PN1 */
+	*pos++ = 0x00; /* Rsvd */
+	*pos++ = 0x20 | (keyid << 6);
+	*pos++ = pn[3]; /* PN2 */
+	*pos++ = pn[2]; /* PN3 */
+	*pos++ = pn[1]; /* PN4 */
+	*pos++ = pn[0]; /* PN5 */
+
+	os_memset(aad, 0, sizeof(aad));
+	ccmp_aad_nonce(hdr, crypt + hdrlen, aad, &aad_len, nonce);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP AAD", aad, aad_len);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP nonce", nonce, 13);
+
+	if (aes_ccm_ae(tk, 16, nonce, 8, frame + hdrlen, plen, aad, aad_len,
+		       pos, pos + plen) < 0) {
+		os_free(crypt);
+		return NULL;
+	}
+
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP encrypted", crypt + hdrlen + 8, plen);
+
+	*encrypted_len = hdrlen + 8 + plen + 8;
+
+	return crypt;
+}
+
+
+u8 * ccmp_256_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+		      const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+	u8 aad[30], nonce[13];
+	size_t aad_len;
+	size_t mlen;
+	u8 *plain;
+
+	if (data_len < 8 + 16)
+		return NULL;
+
+	plain = os_malloc(data_len + AES_BLOCK_SIZE);
+	if (plain == NULL)
+		return NULL;
+
+	mlen = data_len - 8 - 16;
+
+	os_memset(aad, 0, sizeof(aad));
+	ccmp_aad_nonce(hdr, data, aad, &aad_len, nonce);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 AAD", aad, aad_len);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 nonce", nonce, 13);
+
+	if (aes_ccm_ad(tk, 32, nonce, 16, data + 8, mlen, aad, aad_len,
+		       data + 8 + mlen, plain) < 0) {
+		u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+		wpa_printf(MSG_INFO, "Invalid CCMP-256 MIC in frame: A1=" MACSTR
+			   " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
+			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			   MAC2STR(hdr->addr3),
+			   WLAN_GET_SEQ_SEQ(seq_ctrl),
+			   WLAN_GET_SEQ_FRAG(seq_ctrl));
+		os_free(plain);
+		return NULL;
+	}
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 decrypted", plain, mlen);
+
+	*decrypted_len = mlen;
+	return plain;
+}
+
+
+u8 * ccmp_256_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen,
+		      u8 *qos, u8 *pn, int keyid, size_t *encrypted_len)
+{
+	u8 aad[30], nonce[13];
+	size_t aad_len, plen;
+	u8 *crypt, *pos;
+	struct ieee80211_hdr *hdr;
+
+	if (len < hdrlen || hdrlen < 24)
+		return NULL;
+	plen = len - hdrlen;
+
+	crypt = os_malloc(hdrlen + 8 + plen + 16 + AES_BLOCK_SIZE);
+	if (crypt == NULL)
+		return NULL;
+
+	os_memcpy(crypt, frame, hdrlen);
+	hdr = (struct ieee80211_hdr *) crypt;
+	hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+	pos = crypt + hdrlen;
+	*pos++ = pn[5]; /* PN0 */
+	*pos++ = pn[4]; /* PN1 */
+	*pos++ = 0x00; /* Rsvd */
+	*pos++ = 0x20 | (keyid << 6);
+	*pos++ = pn[3]; /* PN2 */
+	*pos++ = pn[2]; /* PN3 */
+	*pos++ = pn[1]; /* PN4 */
+	*pos++ = pn[0]; /* PN5 */
+
+	os_memset(aad, 0, sizeof(aad));
+	ccmp_aad_nonce(hdr, crypt + hdrlen, aad, &aad_len, nonce);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 AAD", aad, aad_len);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 nonce", nonce, 13);
+
+	if (aes_ccm_ae(tk, 32, nonce, 16, frame + hdrlen, plen, aad, aad_len,
+		       pos, pos + plen) < 0) {
+		os_free(crypt);
+		return NULL;
+	}
+
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 encrypted", crypt + hdrlen + 8,
+		    plen);
+
+	*encrypted_len = hdrlen + 8 + plen + 16;
+
+	return crypt;
+}
diff --git a/hostap/wlantest/crc32.c b/hostap/wlantest/crc32.c
new file mode 100644
index 0000000..adbbda5
--- /dev/null
+++ b/hostap/wlantest/crc32.c
@@ -0,0 +1,84 @@
+/*
+ * 32-bit CRC for FCS calculation
+ * Copyright (c) 2010, 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"
+
+/*
+ * IEEE 802.11 FCS CRC32
+ * G(x) = x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 +
+ *        x^5 + x^4 + x^2 + x + 1
+ */
+static const u32 crc32_table[256] = {
+	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
+	0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
+	0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
+	0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
+	0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+	0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
+	0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
+	0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
+	0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
+	0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
+	0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
+	0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
+	0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
+	0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+	0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
+	0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+	0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
+	0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
+	0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
+	0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
+	0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
+	0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
+	0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+	0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
+	0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+	0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
+	0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
+	0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
+	0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
+	0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
+	0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
+	0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
+	0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
+	0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+	0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
+	0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+	0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
+	0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
+	0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
+	0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
+	0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
+	0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
+	0x2d02ef8d
+};
+
+
+u32 crc32(const u8 *frame, size_t frame_len)
+{
+	size_t i;
+	u32 crc;
+
+	crc = 0xFFFFFFFF;
+	for (i = 0; i < frame_len; i++)
+		crc = crc32_table[(crc ^ frame[i]) & 0xff] ^ (crc >> 8);
+
+	return ~crc;
+}
diff --git a/hostap/wlantest/ctrl.c b/hostap/wlantest/ctrl.c
new file mode 100644
index 0000000..7de0a8a
--- /dev/null
+++ b/hostap/wlantest/ctrl.c
@@ -0,0 +1,1468 @@
+/*
+ * wlantest control interface
+ * Copyright (c) 2010-2015, 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 <sys/un.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/defs.h"
+#include "common/version.h"
+#include "common/ieee802_11_defs.h"
+#include "wlantest.h"
+#include "wlantest_ctrl.h"
+
+
+static u8 * attr_get(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr,
+		     size_t *len)
+{
+	u8 *pos = buf;
+
+	while (pos + 8 <= buf + buflen) {
+		enum wlantest_ctrl_attr a;
+		size_t alen;
+		a = WPA_GET_BE32(pos);
+		pos += 4;
+		alen = WPA_GET_BE32(pos);
+		pos += 4;
+		if (pos + alen > buf + buflen) {
+			wpa_printf(MSG_DEBUG, "Invalid control message "
+				   "attribute");
+			return NULL;
+		}
+		if (a == attr) {
+			*len = alen;
+			return pos;
+		}
+		pos += alen;
+	}
+
+	return NULL;
+}
+
+
+static u8 * attr_get_macaddr(u8 *buf, size_t buflen,
+			     enum wlantest_ctrl_attr attr)
+{
+	u8 *addr;
+	size_t addr_len;
+	addr = attr_get(buf, buflen, attr, &addr_len);
+	if (addr && addr_len != ETH_ALEN)
+		addr = NULL;
+	return addr;
+}
+
+
+static int attr_get_int(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr)
+{
+	u8 *pos;
+	size_t len;
+	pos = attr_get(buf, buflen, attr, &len);
+	if (pos == NULL || len != 4)
+		return -1;
+	return WPA_GET_BE32(pos);
+}
+
+
+static u8 * attr_add_str(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+			 const char *str)
+{
+	size_t len = os_strlen(str);
+
+	if (pos == NULL || end - pos < 8 + len)
+		return NULL;
+	WPA_PUT_BE32(pos, attr);
+	pos += 4;
+	WPA_PUT_BE32(pos, len);
+	pos += 4;
+	os_memcpy(pos, str, len);
+	pos += len;
+	return pos;
+}
+
+
+static u8 * attr_add_be32(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+			  u32 val)
+{
+	if (pos == NULL || end - pos < 12)
+		return NULL;
+	WPA_PUT_BE32(pos, attr);
+	pos += 4;
+	WPA_PUT_BE32(pos, 4);
+	pos += 4;
+	WPA_PUT_BE32(pos, val);
+	pos += 4;
+	return pos;
+}
+
+
+static void ctrl_disconnect(struct wlantest *wt, int sock)
+{
+	int i;
+	wpa_printf(MSG_DEBUG, "Disconnect control interface connection %d",
+		   sock);
+	for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
+		if (wt->ctrl_socks[i] == sock) {
+			close(wt->ctrl_socks[i]);
+			eloop_unregister_read_sock(wt->ctrl_socks[i]);
+			wt->ctrl_socks[i] = -1;
+			break;
+		}
+	}
+}
+
+
+static void ctrl_send(struct wlantest *wt, int sock, const u8 *buf,
+		      size_t len)
+{
+	if (send(sock, buf, len, 0) < 0) {
+		wpa_printf(MSG_INFO, "send(ctrl): %s", strerror(errno));
+		ctrl_disconnect(wt, sock);
+	}
+}
+
+
+static void ctrl_send_simple(struct wlantest *wt, int sock,
+			     enum wlantest_ctrl_cmd cmd)
+{
+	u8 buf[4];
+	WPA_PUT_BE32(buf, cmd);
+	ctrl_send(wt, sock, buf, sizeof(buf));
+}
+
+
+static struct wlantest_bss * ctrl_get_bss(struct wlantest *wt, int sock,
+					  u8 *cmd, size_t clen)
+{
+	struct wlantest_bss *bss;
+	u8 *pos;
+	size_t len;
+
+	pos = attr_get(cmd, clen, WLANTEST_ATTR_BSSID, &len);
+	if (pos == NULL || len != ETH_ALEN) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return NULL;
+	}
+
+	bss = bss_find(wt, pos);
+	if (bss == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return NULL;
+	}
+
+	return bss;
+}
+
+
+static struct wlantest_sta * ctrl_get_sta(struct wlantest *wt, int sock,
+					  u8 *cmd, size_t clen,
+					  struct wlantest_bss *bss)
+{
+	struct wlantest_sta *sta;
+	u8 *pos;
+	size_t len;
+
+	if (bss == NULL)
+		return NULL;
+
+	pos = attr_get(cmd, clen, WLANTEST_ATTR_STA_ADDR, &len);
+	if (pos == NULL || len != ETH_ALEN) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return NULL;
+	}
+
+	sta = sta_find(bss, pos);
+	if (sta == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return NULL;
+	}
+
+	return sta;
+}
+
+
+static struct wlantest_sta * ctrl_get_sta2(struct wlantest *wt, int sock,
+					   u8 *cmd, size_t clen,
+					   struct wlantest_bss *bss)
+{
+	struct wlantest_sta *sta;
+	u8 *pos;
+	size_t len;
+
+	if (bss == NULL)
+		return NULL;
+
+	pos = attr_get(cmd, clen, WLANTEST_ATTR_STA2_ADDR, &len);
+	if (pos == NULL || len != ETH_ALEN) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return NULL;
+	}
+
+	sta = sta_find(bss, pos);
+	if (sta == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return NULL;
+	}
+
+	return sta;
+}
+
+
+static void ctrl_list_bss(struct wlantest *wt, int sock)
+{
+	u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos, *len;
+	struct wlantest_bss *bss;
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+	pos += 4;
+	len = pos; /* to be filled */
+	pos += 4;
+
+	dl_list_for_each(bss, &wt->bss, struct wlantest_bss, list) {
+		if (pos + ETH_ALEN > buf + WLANTEST_CTRL_MAX_RESP_LEN)
+			break;
+		os_memcpy(pos, bss->bssid, ETH_ALEN);
+		pos += ETH_ALEN;
+	}
+
+	WPA_PUT_BE32(len, pos - len - 4);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_list_sta(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+	u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos, *len;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	if (bss == NULL)
+		return;
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR);
+	pos += 4;
+	len = pos; /* to be filled */
+	pos += 4;
+
+	dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
+		if (pos + ETH_ALEN > buf + WLANTEST_CTRL_MAX_RESP_LEN)
+			break;
+		os_memcpy(pos, sta->addr, ETH_ALEN);
+		pos += ETH_ALEN;
+	}
+
+	WPA_PUT_BE32(len, pos - len - 4);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_flush(struct wlantest *wt, int sock)
+{
+	wpa_printf(MSG_DEBUG, "Drop all collected BSS data");
+	bss_flush(wt);
+	ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_clear_sta_counters(struct wlantest *wt, int sock, u8 *cmd,
+				    size_t clen)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	if (sta == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	os_memset(sta->counters, 0, sizeof(sta->counters));
+	os_memset(sta->tx_tid, 0, sizeof(sta->tx_tid));
+	os_memset(sta->rx_tid, 0, sizeof(sta->rx_tid));
+	ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_clear_bss_counters(struct wlantest *wt, int sock, u8 *cmd,
+				    size_t clen)
+{
+	struct wlantest_bss *bss;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	if (bss == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	os_memset(bss->counters, 0, sizeof(bss->counters));
+	ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_clear_tdls_counters(struct wlantest *wt, int sock, u8 *cmd,
+				     size_t clen)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	struct wlantest_sta *sta2;
+	struct wlantest_tdls *tdls;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	sta2 = ctrl_get_sta2(wt, sock, cmd, clen, bss);
+	if (sta == NULL || sta2 == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if ((tdls->init == sta && tdls->resp == sta2) ||
+		    (tdls->init == sta2 && tdls->resp == sta))
+			os_memset(tdls->counters, 0, sizeof(tdls->counters));
+	}
+	ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_get_sta_counter(struct wlantest *wt, int sock, u8 *cmd,
+				 size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u32 counter;
+	u8 buf[4 + 12], *end, *pos;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	if (sta == NULL)
+		return;
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_STA_COUNTER, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	counter = WPA_GET_BE32(addr);
+	if (counter >= NUM_WLANTEST_STA_COUNTER) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+			    sta->counters[counter]);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_get_bss_counter(struct wlantest *wt, int sock, u8 *cmd,
+				 size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	u32 counter;
+	u8 buf[4 + 12], *end, *pos;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	if (bss == NULL)
+		return;
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_BSS_COUNTER, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	counter = WPA_GET_BE32(addr);
+	if (counter >= NUM_WLANTEST_BSS_COUNTER) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+			    bss->counters[counter]);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_get_tdls_counter(struct wlantest *wt, int sock, u8 *cmd,
+				  size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	struct wlantest_sta *sta2;
+	struct wlantest_tdls *tdls;
+	u32 counter;
+	u8 buf[4 + 12], *end, *pos;
+	int found = 0;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	sta2 = ctrl_get_sta2(wt, sock, cmd, clen, bss);
+	if (sta == NULL || sta2 == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_TDLS_COUNTER, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	counter = WPA_GET_BE32(addr);
+	if (counter >= NUM_WLANTEST_TDLS_COUNTER) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if (tdls->init == sta && tdls->resp == sta2) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+			    tdls->counters[counter]);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void build_mgmt_hdr(struct ieee80211_mgmt *mgmt,
+			   struct wlantest_bss *bss, struct wlantest_sta *sta,
+			   int sender_ap, int stype)
+{
+	os_memset(mgmt, 0, 24);
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
+	if (sender_ap) {
+		if (sta)
+			os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+		else
+			os_memset(mgmt->da, 0xff, ETH_ALEN);
+		os_memcpy(mgmt->sa, bss->bssid, ETH_ALEN);
+	} else {
+		os_memcpy(mgmt->da, bss->bssid, ETH_ALEN);
+		os_memcpy(mgmt->sa, sta->addr, ETH_ALEN);
+	}
+	os_memcpy(mgmt->bssid, bss->bssid, ETH_ALEN);
+}
+
+
+static int ctrl_inject_auth(struct wlantest *wt, struct wlantest_bss *bss,
+			    struct wlantest_sta *sta, int sender_ap,
+			    enum wlantest_inject_protection prot)
+{
+	struct ieee80211_mgmt mgmt;
+
+	if (prot != WLANTEST_INJECT_NORMAL &&
+	    prot != WLANTEST_INJECT_UNPROTECTED)
+		return -1; /* Authentication frame is never protected */
+	if (sta == NULL)
+		return -1; /* No broadcast Authentication frames */
+
+	if (sender_ap)
+		wpa_printf(MSG_INFO, "INJECT: Auth " MACSTR " -> " MACSTR,
+			   MAC2STR(bss->bssid), MAC2STR(sta->addr));
+	else
+		wpa_printf(MSG_INFO, "INJECT: Auth " MACSTR " -> " MACSTR,
+			   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_AUTH);
+
+	mgmt.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN);
+	mgmt.u.auth.auth_transaction = host_to_le16(1);
+	mgmt.u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+
+	return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 6,
+			       WLANTEST_INJECT_UNPROTECTED);
+}
+
+
+static int ctrl_inject_assocreq(struct wlantest *wt, struct wlantest_bss *bss,
+				struct wlantest_sta *sta, int sender_ap,
+				enum wlantest_inject_protection prot)
+{
+	u8 *buf;
+	struct ieee80211_mgmt *mgmt;
+	int ret;
+
+	if (prot != WLANTEST_INJECT_NORMAL &&
+	    prot != WLANTEST_INJECT_UNPROTECTED)
+		return -1; /* Association Request frame is never protected */
+	if (sta == NULL)
+		return -1; /* No broadcast Association Request frames */
+	if (sender_ap)
+		return -1; /* No Association Request frame sent by AP */
+	if (sta->assocreq_ies == NULL) {
+		wpa_printf(MSG_INFO, "INJECT: No previous (Re)Association "
+			   "Request available for " MACSTR,
+			   MAC2STR(sta->addr));
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "INJECT: AssocReq " MACSTR " -> " MACSTR,
+		   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	buf = os_malloc(sizeof(*mgmt) + sta->assocreq_ies_len);
+	if (buf == NULL)
+		return -1;
+	mgmt = (struct ieee80211_mgmt *) buf;
+
+	build_mgmt_hdr(mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_ASSOC_REQ);
+
+	mgmt->u.assoc_req.capab_info = host_to_le16(sta->assocreq_capab_info);
+	mgmt->u.assoc_req.listen_interval =
+		host_to_le16(sta->assocreq_listen_int);
+	os_memcpy(mgmt->u.assoc_req.variable, sta->assocreq_ies,
+		  sta->assocreq_ies_len);
+
+	ret = wlantest_inject(wt, bss, sta, buf,
+			      24 + 4 + sta->assocreq_ies_len,
+			      WLANTEST_INJECT_UNPROTECTED);
+	os_free(buf);
+	return ret;
+}
+
+
+static int ctrl_inject_reassocreq(struct wlantest *wt,
+				  struct wlantest_bss *bss,
+				  struct wlantest_sta *sta, int sender_ap,
+				  enum wlantest_inject_protection prot)
+{
+	u8 *buf;
+	struct ieee80211_mgmt *mgmt;
+	int ret;
+
+	if (prot != WLANTEST_INJECT_NORMAL &&
+	    prot != WLANTEST_INJECT_UNPROTECTED)
+		return -1; /* Reassociation Request frame is never protected */
+	if (sta == NULL)
+		return -1; /* No broadcast Reassociation Request frames */
+	if (sender_ap)
+		return -1; /* No Reassociation Request frame sent by AP */
+	if (sta->assocreq_ies == NULL) {
+		wpa_printf(MSG_INFO, "INJECT: No previous (Re)Association "
+			   "Request available for " MACSTR,
+			   MAC2STR(sta->addr));
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "INJECT: ReassocReq " MACSTR " -> " MACSTR,
+		   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	buf = os_malloc(sizeof(*mgmt) + sta->assocreq_ies_len);
+	if (buf == NULL)
+		return -1;
+	mgmt = (struct ieee80211_mgmt *) buf;
+
+	build_mgmt_hdr(mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_REASSOC_REQ);
+
+	mgmt->u.reassoc_req.capab_info =
+		host_to_le16(sta->assocreq_capab_info);
+	mgmt->u.reassoc_req.listen_interval =
+		host_to_le16(sta->assocreq_listen_int);
+	os_memcpy(mgmt->u.reassoc_req.current_ap, bss->bssid, ETH_ALEN);
+	os_memcpy(mgmt->u.reassoc_req.variable, sta->assocreq_ies,
+		  sta->assocreq_ies_len);
+
+	ret = wlantest_inject(wt, bss, sta, buf,
+			      24 + 10 + sta->assocreq_ies_len,
+			      WLANTEST_INJECT_UNPROTECTED);
+	os_free(buf);
+	return ret;
+}
+
+
+static int ctrl_inject_deauth(struct wlantest *wt, struct wlantest_bss *bss,
+			      struct wlantest_sta *sta, int sender_ap,
+			      enum wlantest_inject_protection prot)
+{
+	struct ieee80211_mgmt mgmt;
+
+	if (sender_ap) {
+		if (sta)
+			wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR " -> "
+				   MACSTR,
+				   MAC2STR(bss->bssid), MAC2STR(sta->addr));
+		else
+			wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR
+				   " -> broadcast", MAC2STR(bss->bssid));
+	} else
+		wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR " -> " MACSTR,
+			   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_DEAUTH);
+
+	mgmt.u.deauth.reason_code = host_to_le16(WLAN_REASON_UNSPECIFIED);
+
+	return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 2, prot);
+}
+
+
+static int ctrl_inject_disassoc(struct wlantest *wt, struct wlantest_bss *bss,
+				struct wlantest_sta *sta, int sender_ap,
+				enum wlantest_inject_protection prot)
+{
+	struct ieee80211_mgmt mgmt;
+
+	if (sender_ap) {
+		if (sta)
+			wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR " -> "
+				   MACSTR,
+				   MAC2STR(bss->bssid), MAC2STR(sta->addr));
+		else
+			wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR
+				   " -> broadcast", MAC2STR(bss->bssid));
+	} else
+		wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR " -> " MACSTR,
+			   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_DISASSOC);
+
+	mgmt.u.disassoc.reason_code = host_to_le16(WLAN_REASON_UNSPECIFIED);
+
+	return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 2, prot);
+}
+
+
+static int ctrl_inject_saqueryreq(struct wlantest *wt,
+				  struct wlantest_bss *bss,
+				  struct wlantest_sta *sta, int sender_ap,
+				  enum wlantest_inject_protection prot)
+{
+	struct ieee80211_mgmt mgmt;
+
+	if (sta == NULL)
+		return -1; /* No broadcast SA Query frames */
+
+	if (sender_ap)
+		wpa_printf(MSG_INFO, "INJECT: SA Query Request " MACSTR " -> "
+			   MACSTR, MAC2STR(bss->bssid), MAC2STR(sta->addr));
+	else
+		wpa_printf(MSG_INFO, "INJECT: SA Query Request " MACSTR " -> "
+			   MACSTR, MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_ACTION);
+
+	mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
+	mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+	mgmt.u.action.u.sa_query_req.trans_id[0] = 0x12;
+	mgmt.u.action.u.sa_query_req.trans_id[1] = 0x34;
+	os_memcpy(sender_ap ? sta->ap_sa_query_tr : sta->sta_sa_query_tr,
+		  mgmt.u.action.u.sa_query_req.trans_id,
+		  WLAN_SA_QUERY_TR_ID_LEN);
+	return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 4, prot);
+}
+
+
+static void ctrl_inject(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+	u8 *bssid, *sta_addr;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	int frame, sender_ap, prot;
+	int ret = 0;
+
+	bssid = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_BSSID);
+	sta_addr = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_STA_ADDR);
+	frame = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_FRAME);
+	sender_ap = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_SENDER_AP);
+	if (sender_ap < 0)
+		sender_ap = 0;
+	prot = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_PROTECTION);
+	if (bssid == NULL || sta_addr == NULL || frame < 0 || prot < 0) {
+		wpa_printf(MSG_INFO, "Invalid inject command parameters");
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	bss = bss_find(wt, bssid);
+	if (bss == NULL) {
+		wpa_printf(MSG_INFO, "BSS not found for inject command");
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	if (is_broadcast_ether_addr(sta_addr)) {
+		if (!sender_ap) {
+			wpa_printf(MSG_INFO, "Invalid broadcast inject "
+				   "command without sender_ap set");
+			ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+			return;
+		} sta = NULL;
+	} else {
+		sta = sta_find(bss, sta_addr);
+		if (sta == NULL) {
+			wpa_printf(MSG_INFO, "Station not found for inject "
+				   "command");
+			ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+			return;
+		}
+	}
+
+	switch (frame) {
+	case WLANTEST_FRAME_AUTH:
+		ret = ctrl_inject_auth(wt, bss, sta, sender_ap, prot);
+		break;
+	case WLANTEST_FRAME_ASSOCREQ:
+		ret = ctrl_inject_assocreq(wt, bss, sta, sender_ap, prot);
+		break;
+	case WLANTEST_FRAME_REASSOCREQ:
+		ret = ctrl_inject_reassocreq(wt, bss, sta, sender_ap, prot);
+		break;
+	case WLANTEST_FRAME_DEAUTH:
+		ret = ctrl_inject_deauth(wt, bss, sta, sender_ap, prot);
+		break;
+	case WLANTEST_FRAME_DISASSOC:
+		ret = ctrl_inject_disassoc(wt, bss, sta, sender_ap, prot);
+		break;
+	case WLANTEST_FRAME_SAQUERYREQ:
+		ret = ctrl_inject_saqueryreq(wt, bss, sta, sender_ap, prot);
+		break;
+	default:
+		wpa_printf(MSG_INFO, "Unsupported inject command frame %d",
+			   frame);
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	if (ret)
+		wpa_printf(MSG_INFO, "Failed to inject frame");
+	else
+		wpa_printf(MSG_INFO, "Frame injected successfully");
+	ctrl_send_simple(wt, sock, ret == 0 ? WLANTEST_CTRL_SUCCESS :
+			 WLANTEST_CTRL_FAILURE);
+}
+
+
+static void ctrl_version(struct wlantest *wt, int sock)
+{
+	u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos;
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_str(pos, buf + sizeof(buf), WLANTEST_ATTR_VERSION,
+			   VERSION_STR);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_add_passphrase(struct wlantest *wt, int sock, u8 *cmd,
+				size_t clen)
+{
+	u8 *passphrase;
+	size_t len;
+	struct wlantest_passphrase *p, *pa;
+	u8 *bssid;
+
+	passphrase = attr_get(cmd, clen, WLANTEST_ATTR_PASSPHRASE, &len);
+	if (passphrase == NULL) {
+		u8 *wepkey;
+		char *key;
+		enum wlantest_ctrl_cmd res;
+
+		wepkey = attr_get(cmd, clen, WLANTEST_ATTR_WEPKEY, &len);
+		if (wepkey == NULL) {
+			ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+			return;
+		}
+		key = os_zalloc(len + 1);
+		if (key == NULL) {
+			ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+			return;
+		}
+		os_memcpy(key, wepkey, len);
+		if (add_wep(wt, key) < 0)
+			res = WLANTEST_CTRL_FAILURE;
+		else
+			res = WLANTEST_CTRL_SUCCESS;
+		os_free(key);
+		ctrl_send_simple(wt, sock, res);
+		return;
+	}
+
+	if (len < 8 || len > 63) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+	os_memcpy(p->passphrase, passphrase, len);
+	wpa_printf(MSG_INFO, "Add passphrase '%s'", p->passphrase);
+
+	bssid = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_BSSID);
+	if (bssid) {
+		os_memcpy(p->bssid, bssid, ETH_ALEN);
+		wpa_printf(MSG_INFO, "Limit passphrase for BSSID " MACSTR,
+			   MAC2STR(p->bssid));
+	}
+
+	dl_list_for_each(pa, &wt->passphrase, struct wlantest_passphrase, list)
+	{
+		if (os_strcmp(p->passphrase, pa->passphrase) == 0 &&
+		    os_memcmp(p->bssid, pa->bssid, ETH_ALEN) == 0) {
+			wpa_printf(MSG_INFO, "Passphrase was already known");
+			os_free(p);
+			p = NULL;
+			break;
+		}
+	}
+
+	if (p) {
+		struct wlantest_bss *bss;
+		dl_list_add(&wt->passphrase, &p->list);
+		dl_list_for_each(bss, &wt->bss, struct wlantest_bss, list) {
+			if (bssid &&
+			    os_memcmp(p->bssid, bss->bssid, ETH_ALEN) != 0)
+				continue;
+			bss_add_pmk_from_passphrase(bss, p->passphrase);
+		}
+	}
+
+	ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void info_print_proto(char *buf, size_t len, int proto)
+{
+	char *pos, *end;
+
+	if (proto == 0) {
+		os_snprintf(buf, len, "OPEN");
+		return;
+	}
+
+	pos = buf;
+	end = buf + len;
+
+	if (proto & WPA_PROTO_WPA)
+		pos += os_snprintf(pos, end - pos, "%sWPA",
+				   pos == buf ? "" : " ");
+	if (proto & WPA_PROTO_RSN)
+		pos += os_snprintf(pos, end - pos, "%sWPA2",
+				   pos == buf ? "" : " ");
+}
+
+
+static void info_print_cipher(char *buf, size_t len, int cipher)
+{
+	char *pos, *end;
+
+	if (cipher == 0) {
+		os_snprintf(buf, len, "N/A");
+		return;
+	}
+
+	pos = buf;
+	end = buf + len;
+
+	if (cipher & WPA_CIPHER_NONE)
+		pos += os_snprintf(pos, end - pos, "%sNONE",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_WEP40)
+		pos += os_snprintf(pos, end - pos, "%sWEP40",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_WEP104)
+		pos += os_snprintf(pos, end - pos, "%sWEP104",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_TKIP)
+		pos += os_snprintf(pos, end - pos, "%sTKIP",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_CCMP)
+		pos += os_snprintf(pos, end - pos, "%sCCMP",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_AES_128_CMAC)
+		pos += os_snprintf(pos, end - pos, "%sBIP",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_BIP_GMAC_128)
+		pos += os_snprintf(pos, end - pos, "%sBIP-GMAC-128",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_BIP_GMAC_256)
+		pos += os_snprintf(pos, end - pos, "%sBIP-GMAC-256",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_BIP_CMAC_256)
+		pos += os_snprintf(pos, end - pos, "%sBIP-CMAC-256",
+				   pos == buf ? "" : " ");
+}
+
+
+static void info_print_key_mgmt(char *buf, size_t len, int key_mgmt)
+{
+	char *pos, *end;
+
+	if (key_mgmt == 0) {
+		os_snprintf(buf, len, "N/A");
+		return;
+	}
+
+	pos = buf;
+	end = buf + len;
+
+	if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+		pos += os_snprintf(pos, end - pos, "%sEAP",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_PSK)
+		pos += os_snprintf(pos, end - pos, "%sPSK",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_WPA_NONE)
+		pos += os_snprintf(pos, end - pos, "%sWPA-NONE",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+		pos += os_snprintf(pos, end - pos, "%sFT-EAP",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+		pos += os_snprintf(pos, end - pos, "%sFT-PSK",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+		pos += os_snprintf(pos, end - pos, "%sEAP-SHA256",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+		pos += os_snprintf(pos, end - pos, "%sPSK-SHA256",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+		pos += os_snprintf(pos, end - pos, "%sEAP-SUITE-B",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		pos += os_snprintf(pos, end - pos, "%sEAP-SUITE-B-192",
+				   pos == buf ? "" : " ");
+}
+
+
+static void info_print_rsn_capab(char *buf, size_t len, int capab)
+{
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + len;
+
+	if (capab & WPA_CAPABILITY_PREAUTH)
+		pos += os_snprintf(pos, end - pos, "%sPREAUTH",
+				   pos == buf ? "" : " ");
+	if (capab & WPA_CAPABILITY_NO_PAIRWISE)
+		pos += os_snprintf(pos, end - pos, "%sNO_PAIRWISE",
+				   pos == buf ? "" : " ");
+	if (capab & WPA_CAPABILITY_MFPR)
+		pos += os_snprintf(pos, end - pos, "%sMFPR",
+				   pos == buf ? "" : " ");
+	if (capab & WPA_CAPABILITY_MFPC)
+		pos += os_snprintf(pos, end - pos, "%sMFPC",
+				   pos == buf ? "" : " ");
+	if (capab & WPA_CAPABILITY_PEERKEY_ENABLED)
+		pos += os_snprintf(pos, end - pos, "%sPEERKEY",
+				   pos == buf ? "" : " ");
+}
+
+
+static void info_print_state(char *buf, size_t len, int state)
+{
+	switch (state) {
+	case STATE1:
+		os_strlcpy(buf, "NOT-AUTH", len);
+		break;
+	case STATE2:
+		os_strlcpy(buf, "AUTH", len);
+		break;
+	case STATE3:
+		os_strlcpy(buf, "AUTH+ASSOC", len);
+		break;
+	}
+}
+
+
+static void info_print_gtk(char *buf, size_t len, struct wlantest_sta *sta)
+{
+	size_t pos;
+
+	pos = os_snprintf(buf, len, "IDX=%d,GTK=", sta->gtk_idx);
+	wpa_snprintf_hex(buf + pos, len - pos, sta->gtk, sta->gtk_len);
+}
+
+
+static void ctrl_info_sta(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	enum wlantest_sta_info info;
+	u8 buf[4 + 108], *end, *pos;
+	char resp[100];
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	if (sta == NULL)
+		return;
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_STA_INFO, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	info = WPA_GET_BE32(addr);
+
+	resp[0] = '\0';
+	switch (info) {
+	case WLANTEST_STA_INFO_PROTO:
+		info_print_proto(resp, sizeof(resp), sta->proto);
+		break;
+	case WLANTEST_STA_INFO_PAIRWISE:
+		info_print_cipher(resp, sizeof(resp), sta->pairwise_cipher);
+		break;
+	case WLANTEST_STA_INFO_KEY_MGMT:
+		info_print_key_mgmt(resp, sizeof(resp), sta->key_mgmt);
+		break;
+	case WLANTEST_STA_INFO_RSN_CAPAB:
+		info_print_rsn_capab(resp, sizeof(resp), sta->rsn_capab);
+		break;
+	case WLANTEST_STA_INFO_STATE:
+		info_print_state(resp, sizeof(resp), sta->state);
+		break;
+	case WLANTEST_STA_INFO_GTK:
+		info_print_gtk(resp, sizeof(resp), sta);
+		break;
+	default:
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_str(pos, end, WLANTEST_ATTR_INFO, resp);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_info_bss(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	enum wlantest_bss_info info;
+	u8 buf[4 + 108], *end, *pos;
+	char resp[100];
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	if (bss == NULL)
+		return;
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_BSS_INFO, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	info = WPA_GET_BE32(addr);
+
+	resp[0] = '\0';
+	switch (info) {
+	case WLANTEST_BSS_INFO_PROTO:
+		info_print_proto(resp, sizeof(resp), bss->proto);
+		break;
+	case WLANTEST_BSS_INFO_PAIRWISE:
+		info_print_cipher(resp, sizeof(resp), bss->pairwise_cipher);
+		break;
+	case WLANTEST_BSS_INFO_GROUP:
+		info_print_cipher(resp, sizeof(resp), bss->group_cipher);
+		break;
+	case WLANTEST_BSS_INFO_GROUP_MGMT:
+		info_print_cipher(resp, sizeof(resp), bss->mgmt_group_cipher);
+		break;
+	case WLANTEST_BSS_INFO_KEY_MGMT:
+		info_print_key_mgmt(resp, sizeof(resp), bss->key_mgmt);
+		break;
+	case WLANTEST_BSS_INFO_RSN_CAPAB:
+		info_print_rsn_capab(resp, sizeof(resp), bss->rsn_capab);
+		break;
+	default:
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_str(pos, end, WLANTEST_ATTR_INFO, resp);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_send_(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u8 *bssid, *sta_addr;
+	int prot;
+	u8 *frame;
+	size_t frame_len;
+	int ret = 0;
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+
+	frame = attr_get(cmd, clen, WLANTEST_ATTR_FRAME, &frame_len);
+	prot = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_PROTECTION);
+	if (frame == NULL || frame_len < 24 || prot < 0) {
+		wpa_printf(MSG_INFO, "Invalid send command parameters");
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	hdr = (struct ieee80211_hdr *) frame;
+	fc = le_to_host16(hdr->frame_control);
+	switch (WLAN_FC_GET_TYPE(fc)) {
+	case WLAN_FC_TYPE_MGMT:
+		bssid = hdr->addr3;
+		if (os_memcmp(hdr->addr2, hdr->addr3, ETH_ALEN) == 0)
+			sta_addr = hdr->addr1;
+		else
+			sta_addr = hdr->addr2;
+		break;
+	case WLAN_FC_TYPE_DATA:
+		switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+		case 0:
+			bssid = hdr->addr3;
+			sta_addr = hdr->addr2;
+			break;
+		case WLAN_FC_TODS:
+			bssid = hdr->addr1;
+			sta_addr = hdr->addr2;
+			break;
+		case WLAN_FC_FROMDS:
+			bssid = hdr->addr2;
+			sta_addr = hdr->addr1;
+			break;
+		default:
+			wpa_printf(MSG_INFO, "Unsupported inject frame");
+			ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+			return;
+		}
+		break;
+	default:
+		wpa_printf(MSG_INFO, "Unsupported inject frame");
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	bss = bss_find(wt, bssid);
+	if (bss == NULL && prot != WLANTEST_INJECT_UNPROTECTED) {
+		wpa_printf(MSG_INFO, "Unknown BSSID");
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	if (bss)
+		sta = sta_find(bss, sta_addr);
+	else
+		sta = NULL;
+	if (sta == NULL && prot != WLANTEST_INJECT_UNPROTECTED) {
+		wpa_printf(MSG_INFO, "Unknown STA address");
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	ret = wlantest_inject(wt, bss, sta, frame, frame_len, prot);
+
+	if (ret)
+		wpa_printf(MSG_INFO, "Failed to inject frame");
+	else
+		wpa_printf(MSG_INFO, "Frame injected successfully");
+	ctrl_send_simple(wt, sock, ret == 0 ? WLANTEST_CTRL_SUCCESS :
+			 WLANTEST_CTRL_FAILURE);
+}
+
+
+static void ctrl_relog(struct wlantest *wt, int sock)
+{
+	int res = wlantest_relog(wt);
+	ctrl_send_simple(wt, sock, res ? WLANTEST_CTRL_FAILURE :
+			 WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_get_tx_tid(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u32 counter;
+	u8 buf[4 + 12], *end, *pos;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	if (sta == NULL)
+		return;
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_TID, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	counter = WPA_GET_BE32(addr);
+	if (counter >= 16 + 1) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+			    sta->tx_tid[counter]);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_get_rx_tid(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u32 counter;
+	u8 buf[4 + 12], *end, *pos;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	if (sta == NULL)
+		return;
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_TID, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	counter = WPA_GET_BE32(addr);
+	if (counter >= 16 + 1) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+			    sta->rx_tid[counter]);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wlantest *wt = eloop_ctx;
+	u8 buf[WLANTEST_CTRL_MAX_CMD_LEN];
+	int len;
+	enum wlantest_ctrl_cmd cmd;
+
+	wpa_printf(MSG_EXCESSIVE, "New control interface message from %d",
+		   sock);
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_INFO, "recv(ctrl): %s", strerror(errno));
+		ctrl_disconnect(wt, sock);
+		return;
+	}
+	if (len == 0) {
+		ctrl_disconnect(wt, sock);
+		return;
+	}
+
+	if (len < 4) {
+		wpa_printf(MSG_INFO, "Too short control interface command "
+			   "from %d", sock);
+		ctrl_disconnect(wt, sock);
+		return;
+	}
+	cmd = WPA_GET_BE32(buf);
+	wpa_printf(MSG_EXCESSIVE, "Control interface command %d from %d",
+		   cmd, sock);
+
+	switch (cmd) {
+	case WLANTEST_CTRL_PING:
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+		break;
+	case WLANTEST_CTRL_TERMINATE:
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+		eloop_terminate();
+		break;
+	case WLANTEST_CTRL_LIST_BSS:
+		ctrl_list_bss(wt, sock);
+		break;
+	case WLANTEST_CTRL_LIST_STA:
+		ctrl_list_sta(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_FLUSH:
+		ctrl_flush(wt, sock);
+		break;
+	case WLANTEST_CTRL_CLEAR_STA_COUNTERS:
+		ctrl_clear_sta_counters(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_CLEAR_BSS_COUNTERS:
+		ctrl_clear_bss_counters(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_CLEAR_TDLS_COUNTERS:
+		ctrl_clear_tdls_counters(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_GET_STA_COUNTER:
+		ctrl_get_sta_counter(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_GET_BSS_COUNTER:
+		ctrl_get_bss_counter(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_GET_TDLS_COUNTER:
+		ctrl_get_tdls_counter(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_INJECT:
+		ctrl_inject(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_VERSION:
+		ctrl_version(wt, sock);
+		break;
+	case WLANTEST_CTRL_ADD_PASSPHRASE:
+		ctrl_add_passphrase(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_INFO_STA:
+		ctrl_info_sta(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_INFO_BSS:
+		ctrl_info_bss(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_SEND:
+		ctrl_send_(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_RELOG:
+		ctrl_relog(wt, sock);
+		break;
+	case WLANTEST_CTRL_GET_TX_TID:
+		ctrl_get_tx_tid(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_GET_RX_TID:
+		ctrl_get_rx_tid(wt, sock, buf + 4, len - 4);
+		break;
+	default:
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_UNKNOWN_CMD);
+		break;
+	}
+}
+
+
+static void ctrl_connect(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wlantest *wt = eloop_ctx;
+	int conn, i;
+
+	conn = accept(sock, NULL, NULL);
+	if (conn < 0) {
+		wpa_printf(MSG_INFO, "accept(ctrl): %s", strerror(errno));
+		return;
+	}
+	wpa_printf(MSG_MSGDUMP, "New control interface connection %d", conn);
+
+	for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
+		if (wt->ctrl_socks[i] < 0)
+			break;
+	}
+
+	if (i == MAX_CTRL_CONNECTIONS) {
+		wpa_printf(MSG_INFO, "No room for new control connection");
+		close(conn);
+		return;
+	}
+
+	wt->ctrl_socks[i] = conn;
+	eloop_register_read_sock(conn, ctrl_read, wt, NULL);
+}
+
+
+int ctrl_init(struct wlantest *wt)
+{
+	struct sockaddr_un addr;
+
+	wt->ctrl_sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (wt->ctrl_sock < 0) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME,
+		   sizeof(addr.sun_path) - 1);
+	if (bind(wt->ctrl_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+		close(wt->ctrl_sock);
+		wt->ctrl_sock = -1;
+		return -1;
+	}
+
+	if (listen(wt->ctrl_sock, 5) < 0) {
+		wpa_printf(MSG_ERROR, "listen: %s", strerror(errno));
+		close(wt->ctrl_sock);
+		wt->ctrl_sock = -1;
+		return -1;
+	}
+
+	if (eloop_register_read_sock(wt->ctrl_sock, ctrl_connect, wt, NULL)) {
+		close(wt->ctrl_sock);
+		wt->ctrl_sock = -1;
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void ctrl_deinit(struct wlantest *wt)
+{
+	int i;
+
+	if (wt->ctrl_sock < 0)
+		return;
+
+	for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
+		if (wt->ctrl_socks[i] >= 0) {
+			close(wt->ctrl_socks[i]);
+			eloop_unregister_read_sock(wt->ctrl_socks[i]);
+			wt->ctrl_socks[i] = -1;
+		}
+	}
+
+	eloop_unregister_read_sock(wt->ctrl_sock);
+	close(wt->ctrl_sock);
+	wt->ctrl_sock = -1;
+}
diff --git a/hostap/wlantest/gcmp.c b/hostap/wlantest/gcmp.c
new file mode 100644
index 0000000..d92f4ed
--- /dev/null
+++ b/hostap/wlantest/gcmp.c
@@ -0,0 +1,160 @@
+/*
+ * GCM with GMAC Protocol (GCMP)
+ * Copyright (c) 2012, 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 "common/ieee802_11_defs.h"
+#include "crypto/aes.h"
+#include "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+static void gcmp_aad_nonce(const struct ieee80211_hdr *hdr, const u8 *data,
+			   u8 *aad, size_t *aad_len, u8 *nonce)
+{
+	u16 fc, stype, seq;
+	int qos = 0, addr4 = 0;
+	u8 *pos;
+
+	fc = le_to_host16(hdr->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+	if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+	    (WLAN_FC_TODS | WLAN_FC_FROMDS))
+		addr4 = 1;
+
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
+		fc &= ~0x0070; /* Mask subtype bits */
+		if (stype & 0x08) {
+			const u8 *qc;
+			qos = 1;
+			fc &= ~WLAN_FC_ORDER;
+			qc = (const u8 *) (hdr + 1);
+			if (addr4)
+				qc += ETH_ALEN;
+		}
+	}
+
+	fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+	WPA_PUT_LE16(aad, fc);
+	pos = aad + 2;
+	os_memcpy(pos, hdr->addr1, 3 * ETH_ALEN);
+	pos += 3 * ETH_ALEN;
+	seq = le_to_host16(hdr->seq_ctrl);
+	seq &= ~0xfff0; /* Mask Seq#; do not modify Frag# */
+	WPA_PUT_LE16(pos, seq);
+	pos += 2;
+
+	os_memcpy(pos, hdr + 1, addr4 * ETH_ALEN + qos * 2);
+	pos += addr4 * ETH_ALEN;
+	if (qos) {
+		pos[0] &= ~0x70;
+		if (1 /* FIX: either device has SPP A-MSDU Capab = 0 */)
+			pos[0] &= ~0x80;
+		pos++;
+		*pos++ = 0x00;
+	}
+
+	*aad_len = pos - aad;
+
+	os_memcpy(nonce, hdr->addr2, ETH_ALEN);
+	nonce[6] = data[7]; /* PN5 */
+	nonce[7] = data[6]; /* PN4 */
+	nonce[8] = data[5]; /* PN3 */
+	nonce[9] = data[4]; /* PN2 */
+	nonce[10] = data[1]; /* PN1 */
+	nonce[11] = data[0]; /* PN0 */
+}
+
+
+u8 * gcmp_decrypt(const u8 *tk, size_t tk_len, const struct ieee80211_hdr *hdr,
+		  const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+	u8 aad[30], nonce[12], *plain;
+	size_t aad_len, mlen;
+	const u8 *m;
+
+	if (data_len < 8 + 16)
+		return NULL;
+
+	plain = os_malloc(data_len + AES_BLOCK_SIZE);
+	if (plain == NULL)
+		return NULL;
+
+	m = data + 8;
+	mlen = data_len - 8 - 16;
+
+	os_memset(aad, 0, sizeof(aad));
+	gcmp_aad_nonce(hdr, data, aad, &aad_len, nonce);
+	wpa_hexdump(MSG_EXCESSIVE, "GCMP AAD", aad, aad_len);
+	wpa_hexdump(MSG_EXCESSIVE, "GCMP nonce", nonce, sizeof(nonce));
+
+	if (aes_gcm_ad(tk, tk_len, nonce, sizeof(nonce), m, mlen, aad, aad_len,
+		       m + mlen, plain) < 0) {
+		u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+		wpa_printf(MSG_INFO, "Invalid GCMP frame: A1=" MACSTR
+			   " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
+			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			   MAC2STR(hdr->addr3),
+			   WLAN_GET_SEQ_SEQ(seq_ctrl),
+			   WLAN_GET_SEQ_FRAG(seq_ctrl));
+		os_free(plain);
+		return NULL;
+	}
+
+	*decrypted_len = mlen;
+	return plain;
+}
+
+
+u8 * gcmp_encrypt(const u8 *tk, size_t tk_len, const u8 *frame, size_t len,
+		  size_t hdrlen, const u8 *qos,
+		  const u8 *pn, int keyid, size_t *encrypted_len)
+{
+	u8 aad[30], nonce[12], *crypt, *pos;
+	size_t aad_len, plen;
+	struct ieee80211_hdr *hdr;
+
+	if (len < hdrlen || hdrlen < 24)
+		return NULL;
+	plen = len - hdrlen;
+
+	crypt = os_malloc(hdrlen + 8 + plen + 16 + AES_BLOCK_SIZE);
+	if (crypt == NULL)
+		return NULL;
+
+	os_memcpy(crypt, frame, hdrlen);
+	hdr = (struct ieee80211_hdr *) crypt;
+	pos = crypt + hdrlen;
+	*pos++ = pn[5]; /* PN0 */
+	*pos++ = pn[4]; /* PN1 */
+	*pos++ = 0x00; /* Rsvd */
+	*pos++ = 0x20 | (keyid << 6);
+	*pos++ = pn[3]; /* PN2 */
+	*pos++ = pn[2]; /* PN3 */
+	*pos++ = pn[1]; /* PN4 */
+	*pos++ = pn[0]; /* PN5 */
+
+	os_memset(aad, 0, sizeof(aad));
+	gcmp_aad_nonce(hdr, crypt + hdrlen, aad, &aad_len, nonce);
+	wpa_hexdump(MSG_EXCESSIVE, "GCMP AAD", aad, aad_len);
+	wpa_hexdump(MSG_EXCESSIVE, "GCMP nonce", nonce, sizeof(nonce));
+
+	if (aes_gcm_ae(tk, tk_len, nonce, sizeof(nonce), frame + hdrlen, plen,
+		       aad, aad_len, pos, pos + plen) < 0) {
+		os_free(crypt);
+		return NULL;
+	}
+
+	wpa_hexdump(MSG_EXCESSIVE, "GCMP MIC", pos + plen, 16);
+	wpa_hexdump(MSG_EXCESSIVE, "GCMP encrypted", pos, plen);
+
+	*encrypted_len = hdrlen + 8 + plen + 16;
+
+	return crypt;
+}
diff --git a/hostap/wlantest/inject.c b/hostap/wlantest/inject.c
new file mode 100644
index 0000000..ed25033
--- /dev/null
+++ b/hostap/wlantest/inject.c
@@ -0,0 +1,340 @@
+/*
+ * wlantest frame injection
+ * Copyright (c) 2010-2015, 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 "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+static int inject_frame(int s, const void *data, size_t len)
+{
+#define	IEEE80211_RADIOTAP_F_FRAG	0x08
+	unsigned char rtap_hdr[] = {
+		0x00, 0x00, /* radiotap version */
+		0x0e, 0x00, /* radiotap length */
+		0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
+		IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
+		0x00,       /* padding */
+		0x00, 0x00, /* RX and TX flags to indicate that */
+		0x00, 0x00, /* this is the injected frame directly */
+	};
+	struct iovec iov[2] = {
+		{
+			.iov_base = &rtap_hdr,
+			.iov_len = sizeof(rtap_hdr),
+		},
+		{
+			.iov_base = (void *) data,
+			.iov_len = len,
+		}
+	};
+	struct msghdr msg = {
+		.msg_name = NULL,
+		.msg_namelen = 0,
+		.msg_iov = iov,
+		.msg_iovlen = 2,
+		.msg_control = NULL,
+		.msg_controllen = 0,
+		.msg_flags = 0,
+	};
+	int ret;
+
+	ret = sendmsg(s, &msg, 0);
+	if (ret < 0)
+		wpa_printf(MSG_ERROR, "sendmsg: %s", strerror(errno));
+	return ret;
+}
+
+
+static int is_robust_mgmt(u8 *frame, size_t len)
+{
+	struct ieee80211_mgmt *mgmt;
+	u16 fc, stype;
+	if (len < 24)
+		return 0;
+	mgmt = (struct ieee80211_mgmt *) frame;
+	fc = le_to_host16(mgmt->frame_control);
+	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
+		return 0;
+	stype = WLAN_FC_GET_STYPE(fc);
+	if (stype == WLAN_FC_STYPE_DEAUTH || stype == WLAN_FC_STYPE_DISASSOC)
+		return 1;
+	if (stype == WLAN_FC_STYPE_ACTION) {
+		if (len < 25)
+			return 0;
+		if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int wlantest_inject_bip(struct wlantest *wt, struct wlantest_bss *bss,
+			       u8 *frame, size_t len, int incorrect_key)
+{
+	u8 *prot;
+	u8 dummy[32];
+	int ret;
+	size_t plen;
+
+	if (!bss->igtk_len[bss->igtk_idx])
+		return -1;
+
+	os_memset(dummy, 0x11, sizeof(dummy));
+	inc_byte_array(bss->ipn[bss->igtk_idx], 6);
+
+	prot = bip_protect(incorrect_key ? dummy : bss->igtk[bss->igtk_idx],
+			   bss->igtk_len[bss->igtk_idx],
+			   frame, len, bss->ipn[bss->igtk_idx],
+			   bss->igtk_idx, &plen);
+	if (prot == NULL)
+		return -1;
+
+
+	ret = inject_frame(wt->monitor_sock, prot, plen);
+	os_free(prot);
+
+	return (ret < 0) ? -1 : 0;
+}
+
+
+static int wlantest_inject_prot_bc(struct wlantest *wt,
+				   struct wlantest_bss *bss,
+				   u8 *frame, size_t len, int incorrect_key)
+{
+	u8 *crypt;
+	size_t crypt_len;
+	int ret;
+	u8 dummy[64];
+	u8 *pn;
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	int hdrlen;
+
+	hdr = (struct ieee80211_hdr *) frame;
+	hdrlen = 24;
+	fc = le_to_host16(hdr->frame_control);
+
+	if (!bss->gtk_len[bss->gtk_idx])
+		return -1;
+
+	if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+	    (WLAN_FC_TODS | WLAN_FC_FROMDS))
+		hdrlen += ETH_ALEN;
+	pn = bss->rsc[bss->gtk_idx];
+	inc_byte_array(pn, 6);
+
+	os_memset(dummy, 0x11, sizeof(dummy));
+	if (bss->group_cipher == WPA_CIPHER_TKIP)
+		crypt = tkip_encrypt(incorrect_key ? dummy :
+				     bss->gtk[bss->gtk_idx],
+				     frame, len, hdrlen, NULL, pn,
+				     bss->gtk_idx, &crypt_len);
+	else
+		crypt = ccmp_encrypt(incorrect_key ? dummy :
+				     bss->gtk[bss->gtk_idx],
+				     frame, len, hdrlen, NULL, pn,
+				     bss->gtk_idx, &crypt_len);
+
+	if (crypt == NULL)
+		return -1;
+
+	ret = inject_frame(wt->monitor_sock, crypt, crypt_len);
+	os_free(crypt);
+
+	return (ret < 0) ? -1 : 0;
+}
+
+
+static int wlantest_inject_prot(struct wlantest *wt, struct wlantest_bss *bss,
+				struct wlantest_sta *sta, u8 *frame,
+				size_t len, int incorrect_key)
+{
+	u8 *crypt;
+	size_t crypt_len;
+	int ret;
+	u8 dummy[64];
+	u8 *pn;
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	int tid = 0;
+	u8 *qos = NULL;
+	int hdrlen;
+	struct wlantest_tdls *tdls = NULL;
+	const u8 *tk = NULL;
+
+	hdr = (struct ieee80211_hdr *) frame;
+	hdrlen = 24;
+	fc = le_to_host16(hdr->frame_control);
+
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+	    (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == 0) {
+		struct wlantest_sta *sta2;
+		bss = bss_get(wt, hdr->addr3);
+		if (bss == NULL) {
+			wpa_printf(MSG_DEBUG, "No BSS found for TDLS "
+				   "injection");
+			return -1;
+		}
+		sta = sta_find(bss, hdr->addr2);
+		sta2 = sta_find(bss, hdr->addr1);
+		if (sta == NULL || sta2 == NULL) {
+			wpa_printf(MSG_DEBUG, "No stations found for TDLS "
+				   "injection");
+			return -1;
+		}
+		dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list)
+		{
+			if ((tdls->init == sta && tdls->resp == sta2) ||
+			    (tdls->init == sta2 && tdls->resp == sta)) {
+				if (!tdls->link_up)
+					wpa_printf(MSG_DEBUG, "TDLS: Link not "
+						   "up, but injecting Data "
+						   "frame on direct link");
+				tk = tdls->tpk.tk;
+				break;
+			}
+		}
+	}
+
+	if (tk == NULL && sta == NULL) {
+		if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
+			return wlantest_inject_bip(wt, bss, frame, len,
+						   incorrect_key);
+		return wlantest_inject_prot_bc(wt, bss, frame, len,
+					       incorrect_key);
+	}
+
+	if (tk == NULL && !sta->ptk_set) {
+		wpa_printf(MSG_DEBUG, "No key known for injection");
+		return -1;
+	}
+
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
+		tid = 16;
+	else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
+		if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+		    (WLAN_FC_TODS | WLAN_FC_FROMDS))
+			hdrlen += ETH_ALEN;
+		if (WLAN_FC_GET_STYPE(fc) & 0x08) {
+			qos = frame + hdrlen;
+			hdrlen += 2;
+			tid = qos[0] & 0x0f;
+		}
+	}
+	if (tk) {
+		if (os_memcmp(hdr->addr2, tdls->init->addr, ETH_ALEN) == 0)
+			pn = tdls->rsc_init[tid];
+		else
+			pn = tdls->rsc_resp[tid];
+	} else if (os_memcmp(hdr->addr2, bss->bssid, ETH_ALEN) == 0)
+		pn = sta->rsc_fromds[tid];
+	else
+		pn = sta->rsc_tods[tid];
+	inc_byte_array(pn, 6);
+
+	os_memset(dummy, 0x11, sizeof(dummy));
+	if (tk) 
+		crypt = ccmp_encrypt(incorrect_key ? dummy : tk,
+				     frame, len, hdrlen, qos, pn, 0,
+				     &crypt_len);
+	else if (sta->pairwise_cipher == WPA_CIPHER_TKIP)
+		crypt = tkip_encrypt(incorrect_key ? dummy : sta->ptk.tk,
+				     frame, len, hdrlen, qos, pn, 0,
+				     &crypt_len);
+	else
+		crypt = ccmp_encrypt(incorrect_key ? dummy : sta->ptk.tk,
+				     frame, len, hdrlen, qos, pn, 0,
+				     &crypt_len);
+
+	if (crypt == NULL) {
+		wpa_printf(MSG_DEBUG, "Frame encryption failed");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "Inject frame (encrypted)", crypt, crypt_len);
+	ret = inject_frame(wt->monitor_sock, crypt, crypt_len);
+	os_free(crypt);
+	wpa_printf(MSG_DEBUG, "inject_frame for protected frame: %d", ret);
+
+	return (ret < 0) ? -1 : 0;
+}
+
+
+int wlantest_inject(struct wlantest *wt, struct wlantest_bss *bss,
+		    struct wlantest_sta *sta, u8 *frame, size_t len,
+		    enum wlantest_inject_protection prot)
+{
+	int ret;
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	int protectable, protect = 0;
+
+	wpa_hexdump(MSG_DEBUG, "Inject frame", frame, len);
+	if (wt->monitor_sock < 0) {
+		wpa_printf(MSG_INFO, "Cannot inject frames when monitor "
+			   "interface is not in use");
+		return -1;
+	}
+
+	if (prot != WLANTEST_INJECT_UNPROTECTED && bss == NULL) {
+		wpa_printf(MSG_INFO, "No BSS information to inject "
+			   "protected frames");
+		return -1;
+	}
+
+	hdr = (struct ieee80211_hdr *) frame;
+	fc = le_to_host16(hdr->frame_control);
+	protectable = WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA ||
+		is_robust_mgmt(frame, len);
+
+	if ((prot == WLANTEST_INJECT_PROTECTED ||
+	     prot == WLANTEST_INJECT_INCORRECT_KEY) && bss) {
+		if (!sta &&
+		    ((WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+		      !bss->igtk_len[bss->igtk_idx]) ||
+		     (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+		      !bss->gtk_len[bss->gtk_idx]))) {
+			wpa_printf(MSG_INFO, "No GTK/IGTK known for "
+				   MACSTR " to protect the injected "
+				   "frame", MAC2STR(bss->bssid));
+			return -1;
+		}
+		if (sta && !sta->ptk_set) {
+			wpa_printf(MSG_INFO, "No PTK known for the STA " MACSTR
+				   " to encrypt the injected frame",
+				   MAC2STR(sta->addr));
+			return -1;
+		}
+		protect = 1;
+	} else if (protectable && prot != WLANTEST_INJECT_UNPROTECTED && bss) {
+		if (sta && sta->ptk_set)
+			protect = 1;
+		else if (!sta) {
+			if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+			    bss->gtk_len[bss->gtk_idx])
+				protect = 1;
+			if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+			    bss->igtk_len[bss->igtk_idx])
+				protect = 1;
+		}
+	}
+
+	if (protect && bss)
+		return wlantest_inject_prot(
+			wt, bss, sta, frame, len,
+			prot == WLANTEST_INJECT_INCORRECT_KEY);
+
+	ret = inject_frame(wt->monitor_sock, frame, len);
+	wpa_printf(MSG_DEBUG, "inject_frame for unprotected frame: %d", ret);
+	return (ret < 0) ? -1 : 0;
+}
diff --git a/hostap/wlantest/monitor.c b/hostap/wlantest/monitor.c
new file mode 100644
index 0000000..afcc380
--- /dev/null
+++ b/hostap/wlantest/monitor.c
@@ -0,0 +1,148 @@
+/*
+ * Linux packet socket monitor
+ * Copyright (c) 2010, 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 <net/if.h>
+#include <netpacket/packet.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "wlantest.h"
+
+
+static void monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wlantest *wt = eloop_ctx;
+	u8 buf[3000];
+	int len;
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_INFO, "recv(PACKET): %s", strerror(errno));
+		return;
+	}
+
+	clear_notes(wt);
+	os_free(wt->decrypted);
+	wt->decrypted = NULL;
+	write_pcap_captured(wt, buf, len);
+	wlantest_process(wt, buf, len);
+	write_pcapng_captured(wt, buf, len);
+}
+
+
+static void monitor_read_wired(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wlantest *wt = eloop_ctx;
+	u8 buf[3000];
+	int len;
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_INFO, "recv(PACKET): %s", strerror(errno));
+		return;
+	}
+
+	wlantest_process_wired(wt, buf, len);
+}
+
+
+int monitor_init(struct wlantest *wt, const char *ifname)
+{
+	struct sockaddr_ll ll;
+
+	os_memset(&ll, 0, sizeof(ll));
+	ll.sll_family = AF_PACKET;
+	ll.sll_ifindex = if_nametoindex(ifname);
+	if (ll.sll_ifindex == 0) {
+		wpa_printf(MSG_ERROR, "Monitor interface '%s' does not exist",
+			   ifname);
+		return -1;
+	}
+
+	wt->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+	if (wt->monitor_sock < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_PACKET,SOCK_RAW): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (bind(wt->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+		wpa_printf(MSG_ERROR, "bind(PACKET): %s", strerror(errno));
+		close(wt->monitor_sock);
+		wt->monitor_sock = -1;
+		return -1;
+	}
+
+	if (eloop_register_read_sock(wt->monitor_sock, monitor_read, wt, NULL))
+	{
+		wpa_printf(MSG_ERROR, "Could not register monitor read "
+			   "socket");
+		close(wt->monitor_sock);
+		wt->monitor_sock = -1;
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int monitor_init_wired(struct wlantest *wt, const char *ifname)
+{
+	struct sockaddr_ll ll;
+
+	os_memset(&ll, 0, sizeof(ll));
+	ll.sll_family = AF_PACKET;
+	ll.sll_ifindex = if_nametoindex(ifname);
+	if (ll.sll_ifindex == 0) {
+		wpa_printf(MSG_ERROR, "Monitor interface '%s' does not exist",
+			   ifname);
+		return -1;
+	}
+
+	wt->monitor_wired = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+	if (wt->monitor_wired < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_PACKET,SOCK_RAW): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (bind(wt->monitor_wired, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+		wpa_printf(MSG_ERROR, "bind(PACKET): %s", strerror(errno));
+		close(wt->monitor_wired);
+		wt->monitor_wired = -1;
+		return -1;
+	}
+
+	if (eloop_register_read_sock(wt->monitor_wired, monitor_read_wired,
+				     wt, NULL)) {
+		wpa_printf(MSG_ERROR, "Could not register monitor read "
+			   "socket");
+		close(wt->monitor_wired);
+		wt->monitor_wired = -1;
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void monitor_deinit(struct wlantest *wt)
+{
+	if (wt->monitor_sock >= 0) {
+		eloop_unregister_read_sock(wt->monitor_sock);
+		close(wt->monitor_sock);
+		wt->monitor_sock = -1;
+	}
+
+	if (wt->monitor_wired >= 0) {
+		eloop_unregister_read_sock(wt->monitor_wired);
+		close(wt->monitor_wired);
+		wt->monitor_wired = -1;
+	}
+}
diff --git a/hostap/wlantest/process.c b/hostap/wlantest/process.c
new file mode 100644
index 0000000..802d0af
--- /dev/null
+++ b/hostap/wlantest/process.c
@@ -0,0 +1,403 @@
+/*
+ * Received frame processing
+ * Copyright (c) 2010, 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/radiotap.h"
+#include "utils/radiotap_iter.h"
+#include "common/ieee802_11_defs.h"
+#include "common/qca-vendor.h"
+#include "wlantest.h"
+
+
+static struct wlantest_sta * rx_get_sta(struct wlantest *wt,
+					const struct ieee80211_hdr *hdr,
+					size_t len, int *to_ap)
+{
+	u16 fc;
+	const u8 *sta_addr, *bssid;
+	struct wlantest_bss *bss;
+
+	*to_ap = 0;
+	if (hdr->addr1[0] & 0x01)
+		return NULL; /* Ignore group addressed frames */
+
+	fc = le_to_host16(hdr->frame_control);
+	switch (WLAN_FC_GET_TYPE(fc)) {
+	case WLAN_FC_TYPE_MGMT:
+		if (len < 24)
+			return NULL;
+		bssid = hdr->addr3;
+		if (os_memcmp(bssid, hdr->addr2, ETH_ALEN) == 0) {
+			sta_addr = hdr->addr1;
+			*to_ap = 0;
+		} else {
+			if (os_memcmp(bssid, hdr->addr1, ETH_ALEN) != 0)
+				return NULL; /* Unsupported STA-to-STA frame */
+			sta_addr = hdr->addr2;
+			*to_ap = 1;
+		}
+		break;
+	case WLAN_FC_TYPE_DATA:
+		if (len < 24)
+			return NULL;
+		switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+		case 0:
+			return NULL; /* IBSS not supported */
+		case WLAN_FC_FROMDS:
+			sta_addr = hdr->addr1;
+			bssid = hdr->addr2;
+			*to_ap = 0;
+			break;
+		case WLAN_FC_TODS:
+			sta_addr = hdr->addr2;
+			bssid = hdr->addr1;
+			*to_ap = 1;
+			break;
+		case WLAN_FC_TODS | WLAN_FC_FROMDS:
+			return NULL; /* WDS not supported */
+		default:
+			return NULL;
+		}
+		break;
+	case WLAN_FC_TYPE_CTRL:
+		if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PSPOLL &&
+		    len >= 16) {
+			sta_addr = hdr->addr2;
+			bssid = hdr->addr1;
+			*to_ap = 1;
+		} else
+			return NULL;
+		break;
+	default:
+		return NULL;
+	}
+
+	bss = bss_find(wt, bssid);
+	if (bss == NULL)
+		return NULL;
+	return sta_find(bss, sta_addr);
+}
+
+
+static void rx_update_ps(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+			 size_t len, struct wlantest_sta *sta, int to_ap)
+{
+	u16 fc, type, stype;
+
+	if (sta == NULL)
+		return;
+
+	fc = le_to_host16(hdr->frame_control);
+	type = WLAN_FC_GET_TYPE(fc);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if (!to_ap) {
+		if (sta->pwrmgt && !sta->pspoll) {
+			u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+			add_note(wt, MSG_DEBUG, "AP " MACSTR " sent a frame "
+				 "(%u:%u) to a sleeping STA " MACSTR
+				 " (seq=%u)",
+				 MAC2STR(sta->bss->bssid),
+				 type, stype, MAC2STR(sta->addr),
+				 WLAN_GET_SEQ_SEQ(seq_ctrl));
+		} else
+			sta->pspoll = 0;
+		return;
+	}
+
+	sta->pspoll = 0;
+
+	if (type == WLAN_FC_TYPE_DATA || type == WLAN_FC_TYPE_MGMT ||
+	    (type == WLAN_FC_TYPE_CTRL && stype == WLAN_FC_STYPE_PSPOLL)) {
+		/*
+		 * In theory, the PS state changes only at the end of the frame
+		 * exchange that is ACKed by the AP. However, most cases are
+		 * handled with this simpler implementation that does not
+		 * maintain state through the frame exchange.
+		 */
+		if (sta->pwrmgt && !(fc & WLAN_FC_PWRMGT)) {
+			add_note(wt, MSG_DEBUG, "STA " MACSTR " woke up from "
+				 "sleep", MAC2STR(sta->addr));
+			sta->pwrmgt = 0;
+		} else if (!sta->pwrmgt && (fc & WLAN_FC_PWRMGT)) {
+			add_note(wt, MSG_DEBUG, "STA " MACSTR " went to sleep",
+				 MAC2STR(sta->addr));
+			sta->pwrmgt = 1;
+		}
+	}
+
+	if (type == WLAN_FC_TYPE_CTRL && stype == WLAN_FC_STYPE_PSPOLL)
+		sta->pspoll = 1;
+}
+
+
+static int rx_duplicate(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+			size_t len, struct wlantest_sta *sta, int to_ap)
+{
+	u16 fc;
+	int tid = 16;
+	le16 *seq_ctrl;
+
+	if (sta == NULL)
+		return 0;
+
+	fc = le_to_host16(hdr->frame_control);
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+	    (WLAN_FC_GET_STYPE(fc) & 0x08) && len >= 26) {
+		const u8 *qos = ((const u8 *) hdr) + 24;
+		tid = qos[0] & 0x0f;
+	}
+
+	if (to_ap)
+		seq_ctrl = &sta->seq_ctrl_to_ap[tid];
+	else
+		seq_ctrl = &sta->seq_ctrl_to_sta[tid];
+
+	if ((fc & WLAN_FC_RETRY) && hdr->seq_ctrl == *seq_ctrl) {
+		u16 s = le_to_host16(hdr->seq_ctrl);
+		add_note(wt, MSG_MSGDUMP, "Ignore duplicated frame (seq=%u "
+			 "frag=%u A1=" MACSTR " A2=" MACSTR ")",
+			 WLAN_GET_SEQ_SEQ(s), WLAN_GET_SEQ_FRAG(s),
+			 MAC2STR(hdr->addr1), MAC2STR(hdr->addr2));
+		return 1;
+	}
+
+	*seq_ctrl = hdr->seq_ctrl;
+
+	return 0;
+}
+
+
+static void rx_ack(struct wlantest *wt, const struct ieee80211_hdr *hdr)
+{
+	struct ieee80211_hdr *last = (struct ieee80211_hdr *) wt->last_hdr;
+	u16 fc;
+
+	if (wt->last_len < 24 || (last->addr1[0] & 0x01) ||
+	    os_memcmp(hdr->addr1, last->addr2, ETH_ALEN) != 0) {
+		add_note(wt, MSG_MSGDUMP, "Unknown Ack frame (previous frame "
+			 "not seen)");
+		return;
+	}
+
+	/* Ack to the previous frame */
+	fc = le_to_host16(last->frame_control);
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
+		rx_mgmt_ack(wt, last);
+}
+
+
+static void rx_frame(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_hdr *hdr;
+	u16 fc;
+	struct wlantest_sta *sta;
+	int to_ap;
+
+	wpa_hexdump(MSG_EXCESSIVE, "RX frame", data, len);
+	if (len < 2)
+		return;
+
+	hdr = (const struct ieee80211_hdr *) data;
+	fc = le_to_host16(hdr->frame_control);
+	if (fc & WLAN_FC_PVER) {
+		wpa_printf(MSG_DEBUG, "Drop RX frame with unexpected pver=%d",
+			   fc & WLAN_FC_PVER);
+		return;
+	}
+
+	sta = rx_get_sta(wt, hdr, len, &to_ap);
+
+	switch (WLAN_FC_GET_TYPE(fc)) {
+	case WLAN_FC_TYPE_MGMT:
+		if (len < 24)
+			break;
+		if (rx_duplicate(wt, hdr, len, sta, to_ap))
+			break;
+		rx_update_ps(wt, hdr, len, sta, to_ap);
+		rx_mgmt(wt, data, len);
+		break;
+	case WLAN_FC_TYPE_CTRL:
+		if (len < 10)
+			break;
+		wt->rx_ctrl++;
+		rx_update_ps(wt, hdr, len, sta, to_ap);
+		if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACK)
+			rx_ack(wt, hdr);
+		break;
+	case WLAN_FC_TYPE_DATA:
+		if (len < 24)
+			break;
+		if (rx_duplicate(wt, hdr, len, sta, to_ap))
+			break;
+		rx_update_ps(wt, hdr, len, sta, to_ap);
+		rx_data(wt, data, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "Drop RX frame with unexpected type %d",
+			   WLAN_FC_GET_TYPE(fc));
+		break;
+	}
+
+	os_memcpy(wt->last_hdr, data, len > sizeof(wt->last_hdr) ?
+		  sizeof(wt->last_hdr) : len);
+	wt->last_len = len;
+}
+
+
+static void tx_status(struct wlantest *wt, const u8 *data, size_t len, int ack)
+{
+	wpa_printf(MSG_DEBUG, "TX status: ack=%d", ack);
+	wpa_hexdump(MSG_EXCESSIVE, "TX status frame", data, len);
+}
+
+
+static int check_fcs(const u8 *frame, size_t frame_len, const u8 *fcs)
+{
+	if (WPA_GET_LE32(fcs) != crc32(frame, frame_len))
+		return -1;
+	return 0;
+}
+
+
+void wlantest_process(struct wlantest *wt, const u8 *data, size_t len)
+{
+	struct ieee80211_radiotap_iterator iter;
+	int ret;
+	int rxflags = 0, txflags = 0, failed = 0, fcs = 0;
+	const u8 *frame, *fcspos;
+	size_t frame_len;
+
+	wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len);
+
+	if (ieee80211_radiotap_iterator_init(&iter, (void *) data, len, NULL)) {
+		add_note(wt, MSG_INFO, "Invalid radiotap frame");
+		return;
+	}
+
+	for (;;) {
+		ret = ieee80211_radiotap_iterator_next(&iter);
+		wpa_printf(MSG_EXCESSIVE, "radiotap iter: %d "
+			   "this_arg_index=%d", ret, iter.this_arg_index);
+		if (ret == -ENOENT)
+			break;
+		if (ret) {
+			add_note(wt, MSG_INFO, "Invalid radiotap header: %d",
+				 ret);
+			return;
+		}
+		switch (iter.this_arg_index) {
+		case IEEE80211_RADIOTAP_FLAGS:
+			if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
+				fcs = 1;
+			break;
+		case IEEE80211_RADIOTAP_RX_FLAGS:
+			rxflags = 1;
+			break;
+		case IEEE80211_RADIOTAP_TX_FLAGS:
+			txflags = 1;
+			failed = le_to_host16((*(u16 *) iter.this_arg)) &
+				IEEE80211_RADIOTAP_F_TX_FAIL;
+			break;
+		case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+			if (WPA_GET_BE24(iter.this_arg) == OUI_QCA &&
+			    iter.this_arg[3] == QCA_RADIOTAP_VID_WLANTEST) {
+				add_note(wt, MSG_DEBUG,
+					 "Skip frame inserted by wlantest");
+				return;
+			}
+		}
+	}
+
+	frame = data + iter._max_length;
+	frame_len = len - iter._max_length;
+
+	if (fcs && frame_len >= 4) {
+		frame_len -= 4;
+		fcspos = frame + frame_len;
+		if (check_fcs(frame, frame_len, fcspos) < 0) {
+			add_note(wt, MSG_EXCESSIVE, "Drop RX frame with "
+				 "invalid FCS");
+			wt->fcs_error++;
+			return;
+		}
+	}
+
+	if (rxflags && txflags)
+		return;
+	if (!txflags)
+		rx_frame(wt, frame, frame_len);
+	else {
+		add_note(wt, MSG_EXCESSIVE, "TX status - process as RX of "
+			 "local frame");
+		tx_status(wt, frame, frame_len, !failed);
+		/* Process as RX frame to support local monitor interface */
+		rx_frame(wt, frame, frame_len);
+	}
+}
+
+
+void wlantest_process_prism(struct wlantest *wt, const u8 *data, size_t len)
+{
+	int fcs = 0;
+	const u8 *frame, *fcspos;
+	size_t frame_len;
+	u32 hdrlen;
+
+	wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len);
+
+	if (len < 8)
+		return;
+	hdrlen = WPA_GET_LE32(data + 4);
+
+	if (len < hdrlen) {
+		wpa_printf(MSG_INFO, "Too short frame to include prism "
+			   "header");
+		return;
+	}
+
+	frame = data + hdrlen;
+	frame_len = len - hdrlen;
+	fcs = 1;
+
+	if (fcs && frame_len >= 4) {
+		frame_len -= 4;
+		fcspos = frame + frame_len;
+		if (check_fcs(frame, frame_len, fcspos) < 0) {
+			add_note(wt, MSG_EXCESSIVE, "Drop RX frame with "
+				 "invalid FCS");
+			wt->fcs_error++;
+			return;
+		}
+	}
+
+	rx_frame(wt, frame, frame_len);
+}
+
+
+void wlantest_process_80211(struct wlantest *wt, const u8 *data, size_t len)
+{
+	wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len);
+
+	if (wt->assume_fcs && len >= 4) {
+		const u8 *fcspos;
+
+		len -= 4;
+		fcspos = data + len;
+		if (check_fcs(data, len, fcspos) < 0) {
+			add_note(wt, MSG_EXCESSIVE, "Drop RX frame with "
+				 "invalid FCS");
+			wt->fcs_error++;
+			return;
+		}
+	}
+
+	rx_frame(wt, data, len);
+}
diff --git a/hostap/wlantest/readpcap.c b/hostap/wlantest/readpcap.c
new file mode 100644
index 0000000..7c3ce18
--- /dev/null
+++ b/hostap/wlantest/readpcap.c
@@ -0,0 +1,187 @@
+/*
+ * PCAP capture file reader
+ * Copyright (c) 2010, 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 <pcap.h>
+
+#include "utils/common.h"
+#include "wlantest.h"
+
+
+static void write_pcap_with_radiotap(struct wlantest *wt,
+				     const u8 *data, size_t data_len)
+{
+	struct pcap_pkthdr h;
+	u8 rtap[] = {
+		0x00 /* rev */,
+		0x00 /* pad */,
+		0x0a, 0x00, /* header len */
+		0x02, 0x00, 0x00, 0x00, /* present flags */
+		0x00, /* flags */
+		0x00 /* pad */
+	};
+	u8 *buf;
+	size_t len;
+
+	if (wt->assume_fcs)
+		rtap[8] |= 0x10;
+
+	os_memset(&h, 0, sizeof(h));
+	h.ts = wt->write_pcap_time;
+	len = sizeof(rtap) + data_len;
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return;
+	os_memcpy(buf, rtap, sizeof(rtap));
+	os_memcpy(buf + sizeof(rtap), data, data_len);
+	h.caplen = len;
+	h.len = len;
+	pcap_dump(wt->write_pcap_dumper, &h, buf);
+	os_free(buf);
+}
+
+
+int read_cap_file(struct wlantest *wt, const char *fname)
+{
+	char errbuf[PCAP_ERRBUF_SIZE];
+	pcap_t *pcap;
+	unsigned int count = 0;
+	struct pcap_pkthdr *hdr;
+	const u_char *data;
+	int res;
+	int dlt;
+
+	pcap = pcap_open_offline(fname, errbuf);
+	if (pcap == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to read pcap file '%s': %s",
+			   fname, errbuf);
+		return -1;
+	}
+	dlt = pcap_datalink(pcap);
+	if (dlt != DLT_IEEE802_11_RADIO && dlt != DLT_PRISM_HEADER &&
+	    dlt != DLT_IEEE802_11) {
+		wpa_printf(MSG_ERROR, "Unsupported pcap datalink type: %d",
+			   dlt);
+		pcap_close(pcap);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "pcap datalink type: %d", dlt);
+
+	for (;;) {
+		clear_notes(wt);
+		os_free(wt->decrypted);
+		wt->decrypted = NULL;
+
+		res = pcap_next_ex(pcap, &hdr, &data);
+		if (res == -2)
+			break; /* No more packets */
+		if (res == -1) {
+			wpa_printf(MSG_INFO, "pcap_next_ex failure: %s",
+				   pcap_geterr(pcap));
+			break;
+		}
+		if (res != 1) {
+			wpa_printf(MSG_INFO, "Unexpected pcap_next_ex return "
+				   "value %d", res);
+			break;
+		}
+
+		/* Packet was read without problems */
+		wpa_printf(MSG_EXCESSIVE, "pcap hdr: ts=%d.%06d "
+			   "len=%u/%u",
+			   (int) hdr->ts.tv_sec, (int) hdr->ts.tv_usec,
+			   hdr->caplen, hdr->len);
+		if (wt->write_pcap_dumper) {
+			wt->write_pcap_time = hdr->ts;
+			if (dlt == DLT_IEEE802_11)
+				write_pcap_with_radiotap(wt, data, hdr->caplen);
+			else
+				pcap_dump(wt->write_pcap_dumper, hdr, data);
+		}
+		if (hdr->caplen < hdr->len) {
+			add_note(wt, MSG_DEBUG, "pcap: Dropped incomplete "
+				 "frame (%u/%u captured)",
+				 hdr->caplen, hdr->len);
+			write_pcapng_write_read(wt, dlt, hdr, data);
+			continue;
+		}
+		count++;
+		switch (dlt) {
+		case DLT_IEEE802_11_RADIO:
+			wlantest_process(wt, data, hdr->caplen);
+			break;
+		case DLT_PRISM_HEADER:
+			wlantest_process_prism(wt, data, hdr->caplen);
+			break;
+		case DLT_IEEE802_11:
+			wlantest_process_80211(wt, data, hdr->caplen);
+			break;
+		}
+		write_pcapng_write_read(wt, dlt, hdr, data);
+	}
+
+	pcap_close(pcap);
+
+	wpa_printf(MSG_DEBUG, "Read %s: %u packets", fname, count);
+
+	return 0;
+}
+
+
+int read_wired_cap_file(struct wlantest *wt, const char *fname)
+{
+	char errbuf[PCAP_ERRBUF_SIZE];
+	pcap_t *pcap;
+	unsigned int count = 0;
+	struct pcap_pkthdr *hdr;
+	const u_char *data;
+	int res;
+
+	pcap = pcap_open_offline(fname, errbuf);
+	if (pcap == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to read pcap file '%s': %s",
+			   fname, errbuf);
+		return -1;
+	}
+
+	for (;;) {
+		res = pcap_next_ex(pcap, &hdr, &data);
+		if (res == -2)
+			break; /* No more packets */
+		if (res == -1) {
+			wpa_printf(MSG_INFO, "pcap_next_ex failure: %s",
+				   pcap_geterr(pcap));
+			break;
+		}
+		if (res != 1) {
+			wpa_printf(MSG_INFO, "Unexpected pcap_next_ex return "
+				   "value %d", res);
+			break;
+		}
+
+		/* Packet was read without problems */
+		wpa_printf(MSG_EXCESSIVE, "pcap hdr: ts=%d.%06d "
+			   "len=%u/%u",
+			   (int) hdr->ts.tv_sec, (int) hdr->ts.tv_usec,
+			   hdr->caplen, hdr->len);
+		if (hdr->caplen < hdr->len) {
+			wpa_printf(MSG_DEBUG, "pcap: Dropped incomplete frame "
+				   "(%u/%u captured)",
+				   hdr->caplen, hdr->len);
+			continue;
+		}
+		count++;
+		wlantest_process_wired(wt, data, hdr->caplen);
+	}
+
+	pcap_close(pcap);
+
+	wpa_printf(MSG_DEBUG, "Read %s: %u packets", fname, count);
+
+	return 0;
+}
diff --git a/hostap/wlantest/rx_data.c b/hostap/wlantest/rx_data.c
new file mode 100644
index 0000000..4c55e7d
--- /dev/null
+++ b/hostap/wlantest/rx_data.c
@@ -0,0 +1,662 @@
+/*
+ * Received Data frame processing
+ * Copyright (c) 2010-2015, 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 <linux/if_ether.h>
+
+#include "utils/common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "wlantest.h"
+
+
+static const char * data_stype(u16 stype)
+{
+	switch (stype) {
+	case WLAN_FC_STYPE_DATA:
+		return "DATA";
+	case WLAN_FC_STYPE_DATA_CFACK:
+		return "DATA-CFACK";
+	case WLAN_FC_STYPE_DATA_CFPOLL:
+		return "DATA-CFPOLL";
+	case WLAN_FC_STYPE_DATA_CFACKPOLL:
+		return "DATA-CFACKPOLL";
+	case WLAN_FC_STYPE_NULLFUNC:
+		return "NULLFUNC";
+	case WLAN_FC_STYPE_CFACK:
+		return "CFACK";
+	case WLAN_FC_STYPE_CFPOLL:
+		return "CFPOLL";
+	case WLAN_FC_STYPE_CFACKPOLL:
+		return "CFACKPOLL";
+	case WLAN_FC_STYPE_QOS_DATA:
+		return "QOSDATA";
+	case WLAN_FC_STYPE_QOS_DATA_CFACK:
+		return "QOSDATA-CFACK";
+	case WLAN_FC_STYPE_QOS_DATA_CFPOLL:
+		return "QOSDATA-CFPOLL";
+	case WLAN_FC_STYPE_QOS_DATA_CFACKPOLL:
+		return "QOSDATA-CFACKPOLL";
+	case WLAN_FC_STYPE_QOS_NULL:
+		return "QOS-NULL";
+	case WLAN_FC_STYPE_QOS_CFPOLL:
+		return "QOS-CFPOLL";
+	case WLAN_FC_STYPE_QOS_CFACKPOLL:
+		return "QOS-CFACKPOLL";
+	}
+	return "??";
+}
+
+
+static void rx_data_eth(struct wlantest *wt, const u8 *bssid,
+			const u8 *sta_addr, const u8 *dst, const u8 *src,
+			u16 ethertype, const u8 *data, size_t len, int prot,
+			const u8 *peer_addr)
+{
+	switch (ethertype) {
+	case ETH_P_PAE:
+		rx_data_eapol(wt, dst, src, data, len, prot);
+		break;
+	case ETH_P_IP:
+		rx_data_ip(wt, bssid, sta_addr, dst, src, data, len,
+			   peer_addr);
+		break;
+	case 0x890d:
+		rx_data_80211_encap(wt, bssid, sta_addr, dst, src, data, len);
+		break;
+	}
+}
+
+
+static void rx_data_process(struct wlantest *wt, const u8 *bssid,
+			    const u8 *sta_addr,
+			    const u8 *dst, const u8 *src,
+			    const u8 *data, size_t len, int prot,
+			    const u8 *peer_addr)
+{
+	if (len == 0)
+		return;
+
+	if (len >= 8 && os_memcmp(data, "\xaa\xaa\x03\x00\x00\x00", 6) == 0) {
+		rx_data_eth(wt, bssid, sta_addr, dst, src,
+			    WPA_GET_BE16(data + 6), data + 8, len - 8, prot,
+			    peer_addr);
+		return;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "Unrecognized LLC", data, len > 8 ? 8 : len);
+}
+
+
+static u8 * try_all_ptk(struct wlantest *wt, int pairwise_cipher,
+			const struct ieee80211_hdr *hdr,
+			const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+	struct wlantest_ptk *ptk;
+	u8 *decrypted;
+	int prev_level = wpa_debug_level;
+
+	wpa_debug_level = MSG_WARNING;
+	dl_list_for_each(ptk, &wt->ptk, struct wlantest_ptk, list) {
+		unsigned int tk_len = ptk->ptk_len - 32;
+		decrypted = NULL;
+		if ((pairwise_cipher == WPA_CIPHER_CCMP ||
+		     pairwise_cipher == 0) && tk_len == 16) {
+			decrypted = ccmp_decrypt(ptk->ptk.tk, hdr, data,
+						 data_len, decrypted_len);
+		} else if ((pairwise_cipher == WPA_CIPHER_CCMP_256 ||
+			    pairwise_cipher == 0) && tk_len == 32) {
+			decrypted = ccmp_256_decrypt(ptk->ptk.tk, hdr, data,
+						     data_len, decrypted_len);
+		} else if ((pairwise_cipher == WPA_CIPHER_GCMP ||
+			    pairwise_cipher == WPA_CIPHER_GCMP_256 ||
+			    pairwise_cipher == 0) &&
+			   (tk_len == 16 || tk_len == 32)) {
+			decrypted = gcmp_decrypt(ptk->ptk.tk, tk_len, hdr,
+						 data, data_len, decrypted_len);
+		} else if ((pairwise_cipher == WPA_CIPHER_TKIP ||
+			    pairwise_cipher == 0) && tk_len == 32) {
+			decrypted = tkip_decrypt(ptk->ptk.tk, hdr, data,
+						 data_len, decrypted_len);
+		}
+		if (decrypted) {
+			wpa_debug_level = prev_level;
+			add_note(wt, MSG_DEBUG, "Found PTK match from list of all known PTKs");
+			return decrypted;
+		}
+	}
+	wpa_debug_level = prev_level;
+
+	return NULL;
+}
+
+
+static void rx_data_bss_prot_group(struct wlantest *wt,
+				   const struct ieee80211_hdr *hdr,
+				   const u8 *qos, const u8 *dst, const u8 *src,
+				   const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss;
+	int keyid;
+	u8 *decrypted = NULL;
+	size_t dlen;
+	u8 pn[6];
+
+	bss = bss_get(wt, hdr->addr2);
+	if (bss == NULL)
+		return;
+	if (len < 4) {
+		add_note(wt, MSG_INFO, "Too short group addressed data frame");
+		return;
+	}
+
+	if (bss->group_cipher & (WPA_CIPHER_TKIP | WPA_CIPHER_CCMP) &&
+	    !(data[3] & 0x20)) {
+		add_note(wt, MSG_INFO, "Expected TKIP/CCMP frame from "
+			 MACSTR " did not have ExtIV bit set to 1",
+			 MAC2STR(bss->bssid));
+		return;
+	}
+
+	if (bss->group_cipher == WPA_CIPHER_TKIP) {
+		if (data[3] & 0x1f) {
+			add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
+				 " used non-zero reserved bit",
+				 MAC2STR(bss->bssid));
+		}
+		if (data[1] != ((data[0] | 0x20) & 0x7f)) {
+			add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
+				 " used incorrect WEPSeed[1] (was 0x%x, "
+				 "expected 0x%x)",
+				 MAC2STR(bss->bssid), data[1],
+				 (data[0] | 0x20) & 0x7f);
+		}
+	} else if (bss->group_cipher == WPA_CIPHER_CCMP) {
+		if (data[2] != 0 || (data[3] & 0x1f) != 0) {
+			add_note(wt, MSG_INFO, "CCMP frame from " MACSTR
+				 " used non-zero reserved bit",
+				 MAC2STR(bss->bssid));
+		}
+	}
+
+	keyid = data[3] >> 6;
+	if (bss->gtk_len[keyid] == 0 && bss->group_cipher != WPA_CIPHER_WEP40)
+	{
+		add_note(wt, MSG_MSGDUMP, "No GTK known to decrypt the frame "
+			 "(A2=" MACSTR " KeyID=%d)",
+			 MAC2STR(hdr->addr2), keyid);
+		return;
+	}
+
+	if (bss->group_cipher == WPA_CIPHER_TKIP)
+		tkip_get_pn(pn, data);
+	else if (bss->group_cipher == WPA_CIPHER_WEP40)
+		goto skip_replay_det;
+	else
+		ccmp_get_pn(pn, data);
+	if (os_memcmp(pn, bss->rsc[keyid], 6) <= 0) {
+		u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+		add_note(wt, MSG_INFO, "CCMP/TKIP replay detected: A1=" MACSTR
+			 " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u%s",
+			 MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			 MAC2STR(hdr->addr3),
+			 WLAN_GET_SEQ_SEQ(seq_ctrl),
+			 WLAN_GET_SEQ_FRAG(seq_ctrl),
+			 (le_to_host16(hdr->frame_control) & WLAN_FC_RETRY) ?
+			 " Retry" : "");
+		wpa_hexdump(MSG_INFO, "RX PN", pn, 6);
+		wpa_hexdump(MSG_INFO, "RSC", bss->rsc[keyid], 6);
+	}
+
+skip_replay_det:
+	if (bss->group_cipher == WPA_CIPHER_TKIP)
+		decrypted = tkip_decrypt(bss->gtk[keyid], hdr, data, len,
+					 &dlen);
+	else if (bss->group_cipher == WPA_CIPHER_WEP40)
+		decrypted = wep_decrypt(wt, hdr, data, len, &dlen);
+	else if (bss->group_cipher == WPA_CIPHER_CCMP)
+		decrypted = ccmp_decrypt(bss->gtk[keyid], hdr, data, len,
+					 &dlen);
+	else if (bss->group_cipher == WPA_CIPHER_CCMP_256)
+		decrypted = ccmp_256_decrypt(bss->gtk[keyid], hdr, data, len,
+					     &dlen);
+	else if (bss->group_cipher == WPA_CIPHER_GCMP ||
+		 bss->group_cipher == WPA_CIPHER_GCMP_256)
+		decrypted = gcmp_decrypt(bss->gtk[keyid], bss->gtk_len[keyid],
+					 hdr, data, len, &dlen);
+
+	if (decrypted) {
+		rx_data_process(wt, bss->bssid, NULL, dst, src, decrypted,
+				dlen, 1, NULL);
+		os_memcpy(bss->rsc[keyid], pn, 6);
+		write_pcap_decrypted(wt, (const u8 *) hdr, 24 + (qos ? 2 : 0),
+				     decrypted, dlen);
+	} else
+		add_note(wt, MSG_DEBUG, "Failed to decrypt frame");
+	os_free(decrypted);
+}
+
+
+static void rx_data_bss_prot(struct wlantest *wt,
+			     const struct ieee80211_hdr *hdr, const u8 *qos,
+			     const u8 *dst, const u8 *src, const u8 *data,
+			     size_t len)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta, *sta2;
+	int keyid;
+	u16 fc = le_to_host16(hdr->frame_control);
+	u8 *decrypted;
+	size_t dlen;
+	int tid;
+	u8 pn[6], *rsc;
+	struct wlantest_tdls *tdls = NULL, *found;
+	const u8 *tk = NULL;
+	int ptk_iter_done = 0;
+	int try_ptk_iter = 0;
+
+	if (hdr->addr1[0] & 0x01) {
+		rx_data_bss_prot_group(wt, hdr, qos, dst, src, data, len);
+		return;
+	}
+
+	if (fc & WLAN_FC_TODS) {
+		bss = bss_get(wt, hdr->addr1);
+		if (bss == NULL)
+			return;
+		sta = sta_get(bss, hdr->addr2);
+		if (sta)
+			sta->counters[WLANTEST_STA_COUNTER_PROT_DATA_TX]++;
+	} else if (fc & WLAN_FC_FROMDS) {
+		bss = bss_get(wt, hdr->addr2);
+		if (bss == NULL)
+			return;
+		sta = sta_get(bss, hdr->addr1);
+	} else {
+		bss = bss_get(wt, hdr->addr3);
+		if (bss == NULL)
+			return;
+		sta = sta_find(bss, hdr->addr2);
+		sta2 = sta_find(bss, hdr->addr1);
+		if (sta == NULL || sta2 == NULL)
+			return;
+		found = NULL;
+		dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list)
+		{
+			if ((tdls->init == sta && tdls->resp == sta2) ||
+			    (tdls->init == sta2 && tdls->resp == sta)) {
+				found = tdls;
+				if (tdls->link_up)
+					break;
+			}
+		}
+		if (found) {
+			if (!found->link_up)
+				add_note(wt, MSG_DEBUG,
+					 "TDLS: Link not up, but Data "
+					 "frame seen");
+			tk = found->tpk.tk;
+			tdls = found;
+		}
+	}
+	if ((sta == NULL ||
+	     (!sta->ptk_set && sta->pairwise_cipher != WPA_CIPHER_WEP40)) &&
+	    tk == NULL) {
+		add_note(wt, MSG_MSGDUMP, "No PTK known to decrypt the frame");
+		if (dl_list_empty(&wt->ptk))
+			return;
+		try_ptk_iter = 1;
+	}
+
+	if (len < 4) {
+		add_note(wt, MSG_INFO, "Too short encrypted data frame");
+		return;
+	}
+
+	if (sta == NULL)
+		return;
+	if (sta->pairwise_cipher & (WPA_CIPHER_TKIP | WPA_CIPHER_CCMP) &&
+	    !(data[3] & 0x20)) {
+		add_note(wt, MSG_INFO, "Expected TKIP/CCMP frame from "
+			 MACSTR " did not have ExtIV bit set to 1",
+			 MAC2STR(src));
+		return;
+	}
+
+	if (tk == NULL && sta->pairwise_cipher == WPA_CIPHER_TKIP) {
+		if (data[3] & 0x1f) {
+			add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
+				 " used non-zero reserved bit",
+				 MAC2STR(hdr->addr2));
+		}
+		if (data[1] != ((data[0] | 0x20) & 0x7f)) {
+			add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
+				 " used incorrect WEPSeed[1] (was 0x%x, "
+				 "expected 0x%x)",
+				 MAC2STR(hdr->addr2), data[1],
+				 (data[0] | 0x20) & 0x7f);
+		}
+	} else if (tk || sta->pairwise_cipher == WPA_CIPHER_CCMP) {
+		if (data[2] != 0 || (data[3] & 0x1f) != 0) {
+			add_note(wt, MSG_INFO, "CCMP frame from " MACSTR
+				 " used non-zero reserved bit",
+				 MAC2STR(hdr->addr2));
+		}
+	}
+
+	keyid = data[3] >> 6;
+	if (keyid != 0) {
+		add_note(wt, MSG_INFO, "Unexpected non-zero KeyID %d in "
+			 "individually addressed Data frame from " MACSTR,
+			 keyid, MAC2STR(hdr->addr2));
+	}
+
+	if (qos) {
+		tid = qos[0] & 0x0f;
+		if (fc & WLAN_FC_TODS)
+			sta->tx_tid[tid]++;
+		else
+			sta->rx_tid[tid]++;
+	} else {
+		tid = 0;
+		if (fc & WLAN_FC_TODS)
+			sta->tx_tid[16]++;
+		else
+			sta->rx_tid[16]++;
+	}
+	if (tk) {
+		if (os_memcmp(hdr->addr2, tdls->init->addr, ETH_ALEN) == 0)
+			rsc = tdls->rsc_init[tid];
+		else
+			rsc = tdls->rsc_resp[tid];
+	} else if (fc & WLAN_FC_TODS)
+		rsc = sta->rsc_tods[tid];
+	else
+		rsc = sta->rsc_fromds[tid];
+
+
+	if (tk == NULL && sta->pairwise_cipher == WPA_CIPHER_TKIP)
+		tkip_get_pn(pn, data);
+	else if (sta->pairwise_cipher == WPA_CIPHER_WEP40)
+		goto skip_replay_det;
+	else
+		ccmp_get_pn(pn, data);
+	if (os_memcmp(pn, rsc, 6) <= 0) {
+		u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+		add_note(wt, MSG_INFO, "CCMP/TKIP replay detected: A1=" MACSTR
+			 " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u%s",
+			 MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			 MAC2STR(hdr->addr3),
+			 WLAN_GET_SEQ_SEQ(seq_ctrl),
+			 WLAN_GET_SEQ_FRAG(seq_ctrl),
+			 (le_to_host16(hdr->frame_control) &  WLAN_FC_RETRY) ?
+			 " Retry" : "");
+		wpa_hexdump(MSG_INFO, "RX PN", pn, 6);
+		wpa_hexdump(MSG_INFO, "RSC", rsc, 6);
+	}
+
+skip_replay_det:
+	if (tk) {
+		if (sta->pairwise_cipher == WPA_CIPHER_CCMP_256)
+			decrypted = ccmp_256_decrypt(tk, hdr, data, len, &dlen);
+		else if (sta->pairwise_cipher == WPA_CIPHER_GCMP ||
+			 sta->pairwise_cipher == WPA_CIPHER_GCMP_256)
+			decrypted = gcmp_decrypt(tk, sta->tk_len, hdr, data,
+						 len, &dlen);
+		else
+			decrypted = ccmp_decrypt(tk, hdr, data, len, &dlen);
+	} else if (sta->pairwise_cipher == WPA_CIPHER_TKIP) {
+		decrypted = tkip_decrypt(sta->ptk.tk, hdr, data, len, &dlen);
+	} else if (sta->pairwise_cipher == WPA_CIPHER_WEP40) {
+		decrypted = wep_decrypt(wt, hdr, data, len, &dlen);
+	} else if (sta->ptk_set) {
+		if (sta->pairwise_cipher == WPA_CIPHER_CCMP_256)
+			decrypted = ccmp_256_decrypt(sta->ptk.tk, hdr, data,
+						     len, &dlen);
+		else if (sta->pairwise_cipher == WPA_CIPHER_GCMP ||
+			 sta->pairwise_cipher == WPA_CIPHER_GCMP_256)
+			decrypted = gcmp_decrypt(sta->ptk.tk, sta->tk_len,
+						 hdr, data, len, &dlen);
+		else
+			decrypted = ccmp_decrypt(sta->ptk.tk, hdr, data, len,
+						 &dlen);
+	} else {
+		decrypted = try_all_ptk(wt, sta->pairwise_cipher, hdr, data,
+					len, &dlen);
+		ptk_iter_done = 1;
+	}
+	if (!decrypted && !ptk_iter_done) {
+		decrypted = try_all_ptk(wt, sta->pairwise_cipher, hdr, data,
+					len, &dlen);
+		if (decrypted) {
+			add_note(wt, MSG_DEBUG, "Current PTK did not work, but found a match from all known PTKs");
+		}
+	}
+	if (decrypted) {
+		u16 fc = le_to_host16(hdr->frame_control);
+		const u8 *peer_addr = NULL;
+		if (!(fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)))
+			peer_addr = hdr->addr1;
+		os_memcpy(rsc, pn, 6);
+		rx_data_process(wt, bss->bssid, sta->addr, dst, src, decrypted,
+				dlen, 1, peer_addr);
+		write_pcap_decrypted(wt, (const u8 *) hdr, 24 + (qos ? 2 : 0),
+				     decrypted, dlen);
+	} else if (!try_ptk_iter)
+		add_note(wt, MSG_DEBUG, "Failed to decrypt frame");
+	os_free(decrypted);
+}
+
+
+static void rx_data_bss(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+			const u8 *qos, const u8 *dst, const u8 *src,
+			const u8 *data, size_t len)
+{
+	u16 fc = le_to_host16(hdr->frame_control);
+	int prot = !!(fc & WLAN_FC_ISWEP);
+
+	if (qos) {
+		u8 ack = (qos[0] & 0x60) >> 5;
+		wpa_printf(MSG_MSGDUMP, "BSS DATA: " MACSTR " -> " MACSTR
+			   " len=%u%s tid=%u%s%s",
+			   MAC2STR(src), MAC2STR(dst), (unsigned int) len,
+			   prot ? " Prot" : "", qos[0] & 0x0f,
+			   (qos[0] & 0x10) ? " EOSP" : "",
+			   ack == 0 ? "" :
+			   (ack == 1 ? " NoAck" :
+			    (ack == 2 ? " NoExpAck" : " BA")));
+	} else {
+		wpa_printf(MSG_MSGDUMP, "BSS DATA: " MACSTR " -> " MACSTR
+			   " len=%u%s",
+			   MAC2STR(src), MAC2STR(dst), (unsigned int) len,
+			   prot ? " Prot" : "");
+	}
+
+	if (prot)
+		rx_data_bss_prot(wt, hdr, qos, dst, src, data, len);
+	else {
+		const u8 *bssid, *sta_addr, *peer_addr;
+		struct wlantest_bss *bss;
+
+		if (fc & WLAN_FC_TODS) {
+			bssid = hdr->addr1;
+			sta_addr = hdr->addr2;
+			peer_addr = NULL;
+		} else if (fc & WLAN_FC_FROMDS) {
+			bssid = hdr->addr2;
+			sta_addr = hdr->addr1;
+			peer_addr = NULL;
+		} else {
+			bssid = hdr->addr3;
+			sta_addr = hdr->addr2;
+			peer_addr = hdr->addr1;
+		}
+
+		bss = bss_get(wt, bssid);
+		if (bss) {
+			struct wlantest_sta *sta = sta_get(bss, sta_addr);
+
+			if (sta) {
+				if (qos) {
+					int tid = qos[0] & 0x0f;
+					if (fc & WLAN_FC_TODS)
+						sta->tx_tid[tid]++;
+					else
+						sta->rx_tid[tid]++;
+				} else {
+					if (fc & WLAN_FC_TODS)
+						sta->tx_tid[16]++;
+					else
+						sta->rx_tid[16]++;
+				}
+			}
+		}
+
+		rx_data_process(wt, bssid, sta_addr, dst, src, data, len, 0,
+				peer_addr);
+	}
+}
+
+
+static struct wlantest_tdls * get_tdls(struct wlantest *wt, const u8 *bssid,
+				       const u8 *sta1_addr,
+				       const u8 *sta2_addr)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta1, *sta2;
+	struct wlantest_tdls *tdls, *found = NULL;
+
+	bss = bss_find(wt, bssid);
+	if (bss == NULL)
+		return NULL;
+	sta1 = sta_find(bss, sta1_addr);
+	if (sta1 == NULL)
+		return NULL;
+	sta2 = sta_find(bss, sta2_addr);
+	if (sta2 == NULL)
+		return NULL;
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if ((tdls->init == sta1 && tdls->resp == sta2) ||
+		    (tdls->init == sta2 && tdls->resp == sta1)) {
+			found = tdls;
+			if (tdls->link_up)
+				break;
+		}
+	}
+
+	return found;
+}
+
+
+static void add_direct_link(struct wlantest *wt, const u8 *bssid,
+			    const u8 *sta1_addr, const u8 *sta2_addr)
+{
+	struct wlantest_tdls *tdls;
+
+	tdls = get_tdls(wt, bssid, sta1_addr, sta2_addr);
+	if (tdls == NULL)
+		return;
+
+	if (tdls->link_up)
+		tdls->counters[WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK]++;
+	else
+		tdls->counters[WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK]++;
+}
+
+
+static void add_ap_path(struct wlantest *wt, const u8 *bssid,
+			const u8 *sta1_addr, const u8 *sta2_addr)
+{
+	struct wlantest_tdls *tdls;
+
+	tdls = get_tdls(wt, bssid, sta1_addr, sta2_addr);
+	if (tdls == NULL)
+		return;
+
+	if (tdls->link_up)
+		tdls->counters[WLANTEST_TDLS_COUNTER_INVALID_AP_PATH]++;
+	else
+		tdls->counters[WLANTEST_TDLS_COUNTER_VALID_AP_PATH]++;
+}
+
+
+void rx_data(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_hdr *hdr;
+	u16 fc, stype;
+	size_t hdrlen;
+	const u8 *qos = NULL;
+
+	if (len < 24)
+		return;
+
+	hdr = (const struct ieee80211_hdr *) data;
+	fc = le_to_host16(hdr->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+	hdrlen = 24;
+	if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+	    (WLAN_FC_TODS | WLAN_FC_FROMDS))
+		hdrlen += ETH_ALEN;
+	if (stype & 0x08) {
+		qos = data + hdrlen;
+		hdrlen += 2;
+	}
+	if (len < hdrlen)
+		return;
+	wt->rx_data++;
+
+	switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+	case 0:
+		wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s IBSS DA=" MACSTR " SA="
+			   MACSTR " BSSID=" MACSTR,
+			   data_stype(WLAN_FC_GET_STYPE(fc)),
+			   fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+			   fc & WLAN_FC_ISWEP ? " Prot" : "",
+			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			   MAC2STR(hdr->addr3));
+		add_direct_link(wt, hdr->addr3, hdr->addr1, hdr->addr2);
+		rx_data_bss(wt, hdr, qos, hdr->addr1, hdr->addr2,
+			    data + hdrlen, len - hdrlen);
+		break;
+	case WLAN_FC_FROMDS:
+		wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s FromDS DA=" MACSTR
+			   " BSSID=" MACSTR " SA=" MACSTR,
+			   data_stype(WLAN_FC_GET_STYPE(fc)),
+			   fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+			   fc & WLAN_FC_ISWEP ? " Prot" : "",
+			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			   MAC2STR(hdr->addr3));
+		add_ap_path(wt, hdr->addr2, hdr->addr1, hdr->addr3);
+		rx_data_bss(wt, hdr, qos, hdr->addr1, hdr->addr3,
+			    data + hdrlen, len - hdrlen);
+		break;
+	case WLAN_FC_TODS:
+		wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s ToDS BSSID=" MACSTR
+			   " SA=" MACSTR " DA=" MACSTR,
+			   data_stype(WLAN_FC_GET_STYPE(fc)),
+			   fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+			   fc & WLAN_FC_ISWEP ? " Prot" : "",
+			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			   MAC2STR(hdr->addr3));
+		add_ap_path(wt, hdr->addr1, hdr->addr3, hdr->addr2);
+		rx_data_bss(wt, hdr, qos, hdr->addr3, hdr->addr2,
+			    data + hdrlen, len - hdrlen);
+		break;
+	case WLAN_FC_TODS | WLAN_FC_FROMDS:
+		wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s WDS RA=" MACSTR " TA="
+			   MACSTR " DA=" MACSTR " SA=" MACSTR,
+			   data_stype(WLAN_FC_GET_STYPE(fc)),
+			   fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+			   fc & WLAN_FC_ISWEP ? " Prot" : "",
+			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			   MAC2STR(hdr->addr3),
+			   MAC2STR((const u8 *) (hdr + 1)));
+		break;
+	}
+}
diff --git a/hostap/wlantest/rx_eapol.c b/hostap/wlantest/rx_eapol.c
new file mode 100644
index 0000000..75bfa7d
--- /dev/null
+++ b/hostap/wlantest/rx_eapol.c
@@ -0,0 +1,1181 @@
+/*
+ * Received Data frame processing for EAPOL messages
+ * Copyright (c) 2010-2015, 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 "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/eapol_common.h"
+#include "common/wpa_common.h"
+#include "rsn_supp/wpa_ie.h"
+#include "wlantest.h"
+
+
+static int is_zero(const u8 *buf, size_t len)
+{
+	size_t i;
+	for (i = 0; i < len; i++) {
+		if (buf[i])
+			return 0;
+	}
+	return 1;
+}
+
+
+static int check_mic(const u8 *kck, size_t kck_len, int akmp, int ver,
+		     const u8 *data, size_t len)
+{
+	u8 *buf;
+	int ret = -1;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	u8 rx_mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	size_t mic_len = 16;
+
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+	os_memcpy(buf, data, len);
+	hdr = (struct ieee802_1x_hdr *) buf;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+
+	os_memcpy(rx_mic, key->key_mic, mic_len);
+	os_memset(key->key_mic, 0, mic_len);
+
+	if (wpa_eapol_key_mic(kck, kck_len, akmp, ver, buf, len,
+			      key->key_mic) == 0 &&
+	    os_memcmp(rx_mic, key->key_mic, mic_len) == 0)
+		ret = 0;
+
+	os_free(buf);
+
+	return ret;
+}
+
+
+static void rx_data_eapol_key_1_of_4(struct wlantest *wt, const u8 *dst,
+				     const u8 *src, const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	const struct ieee802_1x_hdr *eapol;
+	const struct wpa_eapol_key *hdr;
+
+	wpa_printf(MSG_DEBUG, "EAPOL-Key 1/4 " MACSTR " -> " MACSTR,
+		   MAC2STR(src), MAC2STR(dst));
+	bss = bss_get(wt, src);
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, dst);
+	if (sta == NULL)
+		return;
+
+	eapol = (const struct ieee802_1x_hdr *) data;
+	hdr = (const struct wpa_eapol_key *) (eapol + 1);
+	if (is_zero(hdr->key_nonce, WPA_NONCE_LEN)) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 1/4 from " MACSTR
+			 " used zero nonce", MAC2STR(src));
+	}
+	if (!is_zero(hdr->key_rsc, 8)) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 1/4 from " MACSTR
+			 " used non-zero Key RSC", MAC2STR(src));
+	}
+	os_memcpy(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN);
+}
+
+
+static int try_pmk(struct wlantest *wt, struct wlantest_bss *bss,
+		   struct wlantest_sta *sta, u16 ver,
+		   const u8 *data, size_t len,
+		   struct wlantest_pmk *pmk)
+{
+	struct wpa_ptk ptk;
+
+	if (wpa_key_mgmt_ft(sta->key_mgmt)) {
+		u8 pmk_r0[PMK_LEN];
+		u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+		u8 pmk_r1[PMK_LEN];
+		u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+		u8 ptk_name[WPA_PMK_NAME_LEN];
+
+		wpa_derive_pmk_r0(pmk->pmk, sizeof(pmk->pmk),
+				  bss->ssid, bss->ssid_len, bss->mdid,
+				  bss->r0kh_id, bss->r0kh_id_len,
+				  sta->addr, pmk_r0, pmk_r0_name);
+		wpa_hexdump(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name,
+			    WPA_PMK_NAME_LEN);
+		wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, bss->r1kh_id,
+				  sta->addr, pmk_r1, pmk_r1_name);
+		wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name,
+			    WPA_PMK_NAME_LEN);
+		if (wpa_pmk_r1_to_ptk(pmk_r1, sta->snonce, sta->anonce,
+				      sta->addr,
+				      bss->bssid, pmk_r1_name, &ptk, ptk_name,
+				      sta->key_mgmt,
+				      sta->pairwise_cipher) < 0 ||
+		    check_mic(ptk.kck, ptk.kck_len, sta->key_mgmt, ver, data,
+			      len) < 0)
+			return -1;
+	} else if (wpa_pmk_to_ptk(pmk->pmk, sizeof(pmk->pmk),
+				  "Pairwise key expansion",
+				  bss->bssid, sta->addr, sta->anonce,
+				  sta->snonce, &ptk, sta->key_mgmt,
+				  sta->pairwise_cipher) < 0 ||
+		   check_mic(ptk.kck, ptk.kck_len, sta->key_mgmt, ver, data,
+			     len) < 0) {
+		return -1;
+	}
+
+	sta->tk_len = wpa_cipher_key_len(sta->pairwise_cipher);
+	wpa_printf(MSG_INFO, "Derived PTK for STA " MACSTR " BSSID " MACSTR,
+		   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	sta->counters[WLANTEST_STA_COUNTER_PTK_LEARNED]++;
+	if (sta->ptk_set) {
+		/*
+		 * Rekeying - use new PTK for EAPOL-Key frames, but continue
+		 * using the old PTK for frame decryption.
+		 */
+		add_note(wt, MSG_DEBUG, "Derived PTK during rekeying");
+		os_memcpy(&sta->tptk, &ptk, sizeof(ptk));
+		wpa_hexdump(MSG_DEBUG, "TPTK:KCK",
+			    sta->tptk.kck, sta->tptk.kck_len);
+		wpa_hexdump(MSG_DEBUG, "TPTK:KEK",
+			    sta->tptk.kek, sta->tptk.kek_len);
+		wpa_hexdump(MSG_DEBUG, "TPTK:TK",
+			    sta->tptk.tk, sta->tptk.tk_len);
+		sta->tptk_set = 1;
+		return 0;
+	}
+	add_note(wt, MSG_DEBUG, "Derived new PTK");
+	os_memcpy(&sta->ptk, &ptk, sizeof(ptk));
+	wpa_hexdump(MSG_DEBUG, "PTK:KCK", sta->ptk.kck, sta->ptk.kck_len);
+	wpa_hexdump(MSG_DEBUG, "PTK:KEK", sta->ptk.kek, sta->ptk.kek_len);
+	wpa_hexdump(MSG_DEBUG, "PTK:TK", sta->ptk.tk, sta->ptk.tk_len);
+	sta->ptk_set = 1;
+	os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
+	os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
+	return 0;
+}
+
+
+static void derive_ptk(struct wlantest *wt, struct wlantest_bss *bss,
+		       struct wlantest_sta *sta, u16 ver,
+		       const u8 *data, size_t len)
+{
+	struct wlantest_pmk *pmk;
+
+	wpa_printf(MSG_DEBUG, "Trying to derive PTK for " MACSTR " (ver %u)",
+		   MAC2STR(sta->addr), ver);
+	dl_list_for_each(pmk, &bss->pmk, struct wlantest_pmk, list) {
+		wpa_printf(MSG_DEBUG, "Try per-BSS PMK");
+		if (try_pmk(wt, bss, sta, ver, data, len, pmk) == 0)
+			return;
+	}
+
+	dl_list_for_each(pmk, &wt->pmk, struct wlantest_pmk, list) {
+		wpa_printf(MSG_DEBUG, "Try global PMK");
+		if (try_pmk(wt, bss, sta, ver, data, len, pmk) == 0)
+			return;
+	}
+
+	if (!sta->ptk_set) {
+		struct wlantest_ptk *ptk;
+		int prev_level = wpa_debug_level;
+
+		wpa_debug_level = MSG_WARNING;
+		dl_list_for_each(ptk, &wt->ptk, struct wlantest_ptk, list) {
+			if (check_mic(ptk->ptk.kck, ptk->ptk.kck_len,
+				      sta->key_mgmt, ver, data, len) < 0)
+				continue;
+			wpa_printf(MSG_INFO, "Pre-set PTK matches for STA "
+				   MACSTR " BSSID " MACSTR,
+				   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+			add_note(wt, MSG_DEBUG, "Using pre-set PTK");
+			ptk->ptk_len = 32 +
+				wpa_cipher_key_len(sta->pairwise_cipher);
+			os_memcpy(&sta->ptk, &ptk->ptk, sizeof(ptk->ptk));
+			wpa_hexdump(MSG_DEBUG, "PTK:KCK",
+				    sta->ptk.kck, sta->ptk.kck_len);
+			wpa_hexdump(MSG_DEBUG, "PTK:KEK",
+				    sta->ptk.kek, sta->ptk.kek_len);
+			wpa_hexdump(MSG_DEBUG, "PTK:TK",
+				    sta->ptk.tk, sta->ptk.tk_len);
+			sta->ptk_set = 1;
+			os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
+			os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
+		}
+		wpa_debug_level = prev_level;
+	}
+
+	add_note(wt, MSG_DEBUG, "No matching PMK found to derive PTK");
+}
+
+
+static void rx_data_eapol_key_2_of_4(struct wlantest *wt, const u8 *dst,
+				     const u8 *src, const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	const struct ieee802_1x_hdr *eapol;
+	const struct wpa_eapol_key *hdr;
+	const u8 *key_data, *kck;
+	size_t kck_len;
+	u16 key_info, key_data_len;
+	struct wpa_eapol_ie_parse ie;
+
+	wpa_printf(MSG_DEBUG, "EAPOL-Key 2/4 " MACSTR " -> " MACSTR,
+		   MAC2STR(src), MAC2STR(dst));
+	bss = bss_get(wt, dst);
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, src);
+	if (sta == NULL)
+		return;
+
+	eapol = (const struct ieee802_1x_hdr *) data;
+	hdr = (const struct wpa_eapol_key *) (eapol + 1);
+	if (is_zero(hdr->key_nonce, WPA_NONCE_LEN)) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 2/4 from " MACSTR
+			 " used zero nonce", MAC2STR(src));
+	}
+	if (!is_zero(hdr->key_rsc, 8)) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 2/4 from " MACSTR
+			 " used non-zero Key RSC", MAC2STR(src));
+	}
+	os_memcpy(sta->snonce, hdr->key_nonce, WPA_NONCE_LEN);
+	key_info = WPA_GET_BE16(hdr->key_info);
+	key_data_len = WPA_GET_BE16(hdr->key_data_length);
+	derive_ptk(wt, bss, sta, key_info & WPA_KEY_INFO_TYPE_MASK, data, len);
+
+	if (!sta->ptk_set && !sta->tptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "No PTK known to process EAPOL-Key 2/4");
+		return;
+	}
+
+	kck = sta->ptk.kck;
+	kck_len = sta->ptk.kck_len;
+	if (sta->tptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "Use TPTK for validation EAPOL-Key MIC");
+		kck = sta->tptk.kck;
+		kck_len = sta->tptk.kck_len;
+	}
+	if (check_mic(kck, kck_len, sta->key_mgmt,
+		      key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
+		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 2/4 MIC");
+		return;
+	}
+	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 2/4");
+
+	key_data = (const u8 *) (hdr + 1);
+
+	if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) {
+		add_note(wt, MSG_INFO, "Failed to parse EAPOL-Key Key Data");
+		return;
+	}
+
+	if (ie.wpa_ie) {
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - WPA IE",
+			    ie.wpa_ie, ie.wpa_ie_len);
+		if (os_memcmp(ie.wpa_ie, sta->rsnie, ie.wpa_ie_len) != 0) {
+			struct ieee802_11_elems elems;
+			add_note(wt, MSG_INFO,
+				 "Mismatch in WPA IE between EAPOL-Key 2/4 "
+				 "and (Re)Association Request from " MACSTR,
+				 MAC2STR(sta->addr));
+			wpa_hexdump(MSG_INFO, "WPA IE in EAPOL-Key",
+				    ie.wpa_ie, ie.wpa_ie_len);
+			wpa_hexdump(MSG_INFO, "WPA IE in (Re)Association "
+				    "Request",
+				    sta->rsnie,
+				    sta->rsnie[0] ? 2 + sta->rsnie[1] : 0);
+			/*
+			 * The sniffer may have missed (Re)Association
+			 * Request, so try to survive with the information from
+			 * EAPOL-Key.
+			 */
+			os_memset(&elems, 0, sizeof(elems));
+			elems.wpa_ie = ie.wpa_ie + 2;
+			elems.wpa_ie_len = ie.wpa_ie_len - 2;
+			wpa_printf(MSG_DEBUG, "Update STA data based on WPA "
+				   "IE in EAPOL-Key 2/4");
+			sta_update_assoc(sta, &elems);
+		}
+	}
+
+	if (ie.rsn_ie) {
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - RSN IE",
+			    ie.rsn_ie, ie.rsn_ie_len);
+		if (os_memcmp(ie.rsn_ie, sta->rsnie, ie.rsn_ie_len) != 0) {
+			struct ieee802_11_elems elems;
+			add_note(wt, MSG_INFO,
+				 "Mismatch in RSN IE between EAPOL-Key 2/4 "
+				 "and (Re)Association Request from " MACSTR,
+				 MAC2STR(sta->addr));
+			wpa_hexdump(MSG_INFO, "RSN IE in EAPOL-Key",
+				    ie.rsn_ie, ie.rsn_ie_len);
+			wpa_hexdump(MSG_INFO, "RSN IE in (Re)Association "
+				    "Request",
+				    sta->rsnie,
+				    sta->rsnie[0] ? 2 + sta->rsnie[1] : 0);
+			/*
+			 * The sniffer may have missed (Re)Association
+			 * Request, so try to survive with the information from
+			 * EAPOL-Key.
+			 */
+			os_memset(&elems, 0, sizeof(elems));
+			elems.rsn_ie = ie.rsn_ie + 2;
+			elems.rsn_ie_len = ie.rsn_ie_len - 2;
+			wpa_printf(MSG_DEBUG, "Update STA data based on RSN "
+				   "IE in EAPOL-Key 2/4");
+			sta_update_assoc(sta, &elems);
+		}
+	}
+}
+
+
+static u8 * decrypt_eapol_key_data_rc4(struct wlantest *wt, const u8 *kek,
+				       const struct wpa_eapol_key *hdr,
+				       size_t *len)
+{
+	u8 ek[32], *buf;
+	u16 keydatalen = WPA_GET_BE16(hdr->key_data_length);
+
+	buf = os_malloc(keydatalen);
+	if (buf == NULL)
+		return NULL;
+
+	os_memcpy(ek, hdr->key_iv, 16);
+	os_memcpy(ek + 16, kek, 16);
+	os_memcpy(buf, hdr + 1, keydatalen);
+	if (rc4_skip(ek, 32, 256, buf, keydatalen)) {
+		add_note(wt, MSG_INFO, "RC4 failed");
+		os_free(buf);
+		return NULL;
+	}
+
+	*len = keydatalen;
+	return buf;
+}
+
+
+static u8 * decrypt_eapol_key_data_aes(struct wlantest *wt, const u8 *kek,
+				       const struct wpa_eapol_key *hdr,
+				       size_t *len)
+{
+	u8 *buf;
+	u16 keydatalen = WPA_GET_BE16(hdr->key_data_length);
+
+	if (keydatalen % 8) {
+		add_note(wt, MSG_INFO, "Unsupported AES-WRAP len %d",
+			 keydatalen);
+		return NULL;
+	}
+	keydatalen -= 8; /* AES-WRAP adds 8 bytes */
+	buf = os_malloc(keydatalen);
+	if (buf == NULL)
+		return NULL;
+	if (aes_unwrap(kek, 16, keydatalen / 8, (u8 *) (hdr + 1), buf)) {
+		os_free(buf);
+		add_note(wt, MSG_INFO,
+			 "AES unwrap failed - could not decrypt EAPOL-Key "
+			 "key data");
+		return NULL;
+	}
+
+	*len = keydatalen;
+	return buf;
+}
+
+
+static u8 * decrypt_eapol_key_data(struct wlantest *wt, const u8 *kek,
+				   size_t kek_len, u16 ver,
+				   const struct wpa_eapol_key *hdr,
+				   size_t *len)
+{
+	if (kek_len != 16)
+		return NULL;
+	switch (ver) {
+	case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
+		return decrypt_eapol_key_data_rc4(wt, kek, hdr, len);
+	case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
+	case WPA_KEY_INFO_TYPE_AES_128_CMAC:
+		return decrypt_eapol_key_data_aes(wt, kek, hdr, len);
+	case WPA_KEY_INFO_TYPE_AKM_DEFINED:
+		/* For now, assume this is OSEN */
+		return decrypt_eapol_key_data_aes(wt, kek, hdr, len);
+	default:
+		add_note(wt, MSG_INFO,
+			 "Unsupported EAPOL-Key Key Descriptor Version %u",
+			 ver);
+		return NULL;
+	}
+}
+
+
+static void learn_kde_keys(struct wlantest *wt, struct wlantest_bss *bss,
+			   struct wlantest_sta *sta,
+			   const u8 *buf, size_t len, const u8 *rsc)
+{
+	struct wpa_eapol_ie_parse ie;
+
+	if (wpa_supplicant_parse_ies(buf, len, &ie) < 0) {
+		add_note(wt, MSG_INFO, "Failed to parse EAPOL-Key Key Data");
+		return;
+	}
+
+	if (ie.wpa_ie) {
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - WPA IE",
+			    ie.wpa_ie, ie.wpa_ie_len);
+	}
+
+	if (ie.rsn_ie) {
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - RSN IE",
+			    ie.rsn_ie, ie.rsn_ie_len);
+	}
+
+	if (ie.gtk) {
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - GTK KDE",
+			    ie.gtk, ie.gtk_len);
+		if (ie.gtk_len >= 2 && ie.gtk_len <= 2 + 32) {
+			int id;
+			id = ie.gtk[0] & 0x03;
+			add_note(wt, MSG_DEBUG, "GTK KeyID=%u tx=%u",
+				 id, !!(ie.gtk[0] & 0x04));
+			if ((ie.gtk[0] & 0xf8) || ie.gtk[1]) {
+				add_note(wt, MSG_INFO,
+					 "GTK KDE: Reserved field set: "
+					 "%02x %02x", ie.gtk[0], ie.gtk[1]);
+			}
+			wpa_hexdump(MSG_DEBUG, "GTK", ie.gtk + 2,
+				    ie.gtk_len - 2);
+			bss->gtk_len[id] = ie.gtk_len - 2;
+			sta->gtk_len = ie.gtk_len - 2;
+			os_memcpy(bss->gtk[id], ie.gtk + 2, ie.gtk_len - 2);
+			os_memcpy(sta->gtk, ie.gtk + 2, ie.gtk_len - 2);
+			bss->rsc[id][0] = rsc[5];
+			bss->rsc[id][1] = rsc[4];
+			bss->rsc[id][2] = rsc[3];
+			bss->rsc[id][3] = rsc[2];
+			bss->rsc[id][4] = rsc[1];
+			bss->rsc[id][5] = rsc[0];
+			bss->gtk_idx = id;
+			sta->gtk_idx = id;
+			wpa_hexdump(MSG_DEBUG, "RSC", bss->rsc[id], 6);
+		} else {
+			add_note(wt, MSG_INFO, "Invalid GTK KDE length %u",
+				 (unsigned) ie.gtk_len);
+		}
+	}
+
+	if (ie.igtk) {
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - IGTK KDE",
+			    ie.igtk, ie.igtk_len);
+		if (ie.igtk_len == 24) {
+			u16 id;
+			id = WPA_GET_LE16(ie.igtk);
+			if (id > 5) {
+				add_note(wt, MSG_INFO,
+					 "Unexpected IGTK KeyID %u", id);
+			} else {
+				const u8 *ipn;
+				add_note(wt, MSG_DEBUG, "IGTK KeyID %u", id);
+				wpa_hexdump(MSG_DEBUG, "IPN", ie.igtk + 2, 6);
+				wpa_hexdump(MSG_DEBUG, "IGTK", ie.igtk + 8,
+					    16);
+				os_memcpy(bss->igtk[id], ie.igtk + 8, 16);
+				bss->igtk_len[id] = 16;
+				ipn = ie.igtk + 2;
+				bss->ipn[id][0] = ipn[5];
+				bss->ipn[id][1] = ipn[4];
+				bss->ipn[id][2] = ipn[3];
+				bss->ipn[id][3] = ipn[2];
+				bss->ipn[id][4] = ipn[1];
+				bss->ipn[id][5] = ipn[0];
+				bss->igtk_idx = id;
+			}
+		} else if (ie.igtk_len == 40) {
+			u16 id;
+			id = WPA_GET_LE16(ie.igtk);
+			if (id > 5) {
+				add_note(wt, MSG_INFO,
+					 "Unexpected IGTK KeyID %u", id);
+			} else {
+				const u8 *ipn;
+				add_note(wt, MSG_DEBUG, "IGTK KeyID %u", id);
+				wpa_hexdump(MSG_DEBUG, "IPN", ie.igtk + 2, 6);
+				wpa_hexdump(MSG_DEBUG, "IGTK", ie.igtk + 8,
+					    32);
+				os_memcpy(bss->igtk[id], ie.igtk + 8, 32);
+				bss->igtk_len[id] = 32;
+				ipn = ie.igtk + 2;
+				bss->ipn[id][0] = ipn[5];
+				bss->ipn[id][1] = ipn[4];
+				bss->ipn[id][2] = ipn[3];
+				bss->ipn[id][3] = ipn[2];
+				bss->ipn[id][4] = ipn[1];
+				bss->ipn[id][5] = ipn[0];
+				bss->igtk_idx = id;
+			}
+		} else {
+			add_note(wt, MSG_INFO, "Invalid IGTK KDE length %u",
+				 (unsigned) ie.igtk_len);
+		}
+	}
+}
+
+
+static void rx_data_eapol_key_3_of_4(struct wlantest *wt, const u8 *dst,
+				     const u8 *src, const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	const struct ieee802_1x_hdr *eapol;
+	const struct wpa_eapol_key *hdr;
+	const u8 *key_data, *kck, *kek;
+	size_t kck_len, kek_len;
+	int recalc = 0;
+	u16 key_info, ver;
+	u8 *decrypted_buf = NULL;
+	const u8 *decrypted;
+	size_t decrypted_len = 0;
+	struct wpa_eapol_ie_parse ie;
+
+	wpa_printf(MSG_DEBUG, "EAPOL-Key 3/4 " MACSTR " -> " MACSTR,
+		   MAC2STR(src), MAC2STR(dst));
+	bss = bss_get(wt, src);
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, dst);
+	if (sta == NULL)
+		return;
+
+	eapol = (const struct ieee802_1x_hdr *) data;
+	hdr = (const struct wpa_eapol_key *) (eapol + 1);
+	key_info = WPA_GET_BE16(hdr->key_info);
+
+	if (os_memcmp(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN) != 0) {
+		add_note(wt, MSG_INFO,
+			 "EAPOL-Key ANonce mismatch between 1/4 and 3/4");
+		recalc = 1;
+	}
+	os_memcpy(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN);
+	if (recalc) {
+		derive_ptk(wt, bss, sta, key_info & WPA_KEY_INFO_TYPE_MASK,
+			   data, len);
+	}
+
+	if (!sta->ptk_set && !sta->tptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "No PTK known to process EAPOL-Key 3/4");
+		return;
+	}
+
+	kek = sta->ptk.kek;
+	kek_len = sta->ptk.kek_len;
+	kck = sta->ptk.kck;
+	kck_len = sta->ptk.kck_len;
+	if (sta->tptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "Use TPTK for validation EAPOL-Key MIC");
+		kck = sta->tptk.kck;
+		kck_len = sta->tptk.kck_len;
+		kek = sta->tptk.kek;
+		kek_len = sta->tptk.kek_len;
+	}
+	if (check_mic(kck, kck_len, sta->key_mgmt,
+		      key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
+		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 3/4 MIC");
+		return;
+	}
+	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 3/4");
+
+	key_data = (const u8 *) (hdr + 1);
+	if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		if (sta->proto & WPA_PROTO_RSN)
+			add_note(wt, MSG_INFO,
+				 "EAPOL-Key 3/4 without EncrKeyData bit");
+		decrypted = key_data;
+		decrypted_len = WPA_GET_BE16(hdr->key_data_length);
+	} else {
+		ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+		decrypted_buf = decrypt_eapol_key_data(wt, kek, kek_len, ver,
+						       hdr, &decrypted_len);
+		if (decrypted_buf == NULL) {
+			add_note(wt, MSG_INFO,
+				 "Failed to decrypt EAPOL-Key Key Data");
+			return;
+		}
+		decrypted = decrypted_buf;
+		wpa_hexdump(MSG_DEBUG, "Decrypted EAPOL-Key Key Data",
+			    decrypted, decrypted_len);
+	}
+	if (wt->write_pcap_dumper && decrypted != key_data) {
+		/* Fill in a dummy Data frame header */
+		u8 buf[24 + 8 + sizeof(*eapol) + sizeof(*hdr)];
+		struct ieee80211_hdr *h;
+		struct wpa_eapol_key *k;
+		const u8 *p;
+		u8 *pos;
+		size_t plain_len;
+
+		plain_len = decrypted_len;
+		p = decrypted;
+		while (p + 1 < decrypted + decrypted_len) {
+			if (p[0] == 0xdd && p[1] == 0x00) {
+				/* Remove padding */
+				plain_len = p - decrypted;
+				break;
+			}
+			p += 2 + p[1];
+		}
+
+		os_memset(buf, 0, sizeof(buf));
+		h = (struct ieee80211_hdr *) buf;
+		h->frame_control = host_to_le16(0x0208);
+		os_memcpy(h->addr1, dst, ETH_ALEN);
+		os_memcpy(h->addr2, src, ETH_ALEN);
+		os_memcpy(h->addr3, src, ETH_ALEN);
+		pos = (u8 *) (h + 1);
+		os_memcpy(pos, "\xaa\xaa\x03\x00\x00\x00\x88\x8e", 8);
+		pos += 8;
+		os_memcpy(pos, eapol, sizeof(*eapol));
+		pos += sizeof(*eapol);
+		os_memcpy(pos, hdr, sizeof(*hdr));
+		k = (struct wpa_eapol_key *) pos;
+		WPA_PUT_BE16(k->key_info,
+			     key_info & ~WPA_KEY_INFO_ENCR_KEY_DATA);
+		WPA_PUT_BE16(k->key_data_length, plain_len);
+		write_pcap_decrypted(wt, buf, sizeof(buf),
+				     decrypted, plain_len);
+	}
+
+	if (wpa_supplicant_parse_ies(decrypted, decrypted_len, &ie) < 0) {
+		add_note(wt, MSG_INFO, "Failed to parse EAPOL-Key Key Data");
+		os_free(decrypted_buf);
+		return;
+	}
+
+	if ((ie.wpa_ie &&
+	     os_memcmp(ie.wpa_ie, bss->wpaie, ie.wpa_ie_len) != 0) ||
+	    (ie.wpa_ie == NULL && bss->wpaie[0])) {
+		add_note(wt, MSG_INFO,
+			 "Mismatch in WPA IE between EAPOL-Key 3/4 and "
+			 "Beacon/Probe Response from " MACSTR,
+			 MAC2STR(bss->bssid));
+		wpa_hexdump(MSG_INFO, "WPA IE in EAPOL-Key",
+			    ie.wpa_ie, ie.wpa_ie_len);
+		wpa_hexdump(MSG_INFO, "WPA IE in Beacon/Probe "
+			    "Response",
+			    bss->wpaie,
+			    bss->wpaie[0] ? 2 + bss->wpaie[1] : 0);
+	}
+
+	if ((ie.rsn_ie &&
+	     os_memcmp(ie.rsn_ie, bss->rsnie, ie.rsn_ie_len) != 0) ||
+	    (ie.rsn_ie == NULL && bss->rsnie[0])) {
+		add_note(wt, MSG_INFO, "Mismatch in RSN IE between EAPOL-Key "
+			 "3/4 and Beacon/Probe Response from " MACSTR,
+			 MAC2STR(bss->bssid));
+		wpa_hexdump(MSG_INFO, "RSN IE in EAPOL-Key",
+			    ie.rsn_ie, ie.rsn_ie_len);
+		wpa_hexdump(MSG_INFO, "RSN IE in Beacon/Probe Response",
+			    bss->rsnie,
+			    bss->rsnie[0] ? 2 + bss->rsnie[1] : 0);
+	}
+
+	learn_kde_keys(wt, bss, sta, decrypted, decrypted_len, hdr->key_rsc);
+	os_free(decrypted_buf);
+}
+
+
+static void rx_data_eapol_key_4_of_4(struct wlantest *wt, const u8 *dst,
+				     const u8 *src, const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	const struct ieee802_1x_hdr *eapol;
+	const struct wpa_eapol_key *hdr;
+	u16 key_info;
+	const u8 *kck;
+	size_t kck_len;
+
+	wpa_printf(MSG_DEBUG, "EAPOL-Key 4/4 " MACSTR " -> " MACSTR,
+		   MAC2STR(src), MAC2STR(dst));
+	bss = bss_get(wt, dst);
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, src);
+	if (sta == NULL)
+		return;
+
+	eapol = (const struct ieee802_1x_hdr *) data;
+	hdr = (const struct wpa_eapol_key *) (eapol + 1);
+	if (!is_zero(hdr->key_rsc, 8)) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 4/4 from " MACSTR " used "
+			 "non-zero Key RSC", MAC2STR(src));
+	}
+	key_info = WPA_GET_BE16(hdr->key_info);
+
+	if (!sta->ptk_set && !sta->tptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "No PTK known to process EAPOL-Key 4/4");
+		return;
+	}
+
+	kck = sta->ptk.kck;
+	kck_len = sta->ptk.kck_len;
+	if (sta->tptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "Use TPTK for validation EAPOL-Key MIC");
+		kck = sta->tptk.kck;
+		kck_len = sta->tptk.kck_len;
+	}
+	if (check_mic(kck, kck_len, sta->key_mgmt,
+		      key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
+		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 4/4 MIC");
+		return;
+	}
+	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 4/4");
+	if (sta->tptk_set) {
+		add_note(wt, MSG_DEBUG, "Update PTK (rekeying)");
+		os_memcpy(&sta->ptk, &sta->tptk, sizeof(sta->ptk));
+		sta->ptk_set = 1;
+		sta->tptk_set = 0;
+		os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
+		os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
+	}
+}
+
+
+static void rx_data_eapol_key_1_of_2(struct wlantest *wt, const u8 *dst,
+				     const u8 *src, const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	const struct ieee802_1x_hdr *eapol;
+	const struct wpa_eapol_key *hdr;
+	u16 key_info, ver;
+	u8 *decrypted;
+	size_t decrypted_len = 0;
+
+	wpa_printf(MSG_DEBUG, "EAPOL-Key 1/2 " MACSTR " -> " MACSTR,
+		   MAC2STR(src), MAC2STR(dst));
+	bss = bss_get(wt, src);
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, dst);
+	if (sta == NULL)
+		return;
+
+	eapol = (const struct ieee802_1x_hdr *) data;
+	hdr = (const struct wpa_eapol_key *) (eapol + 1);
+	key_info = WPA_GET_BE16(hdr->key_info);
+
+	if (!sta->ptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "No PTK known to process EAPOL-Key 1/2");
+		return;
+	}
+
+	if (sta->ptk_set &&
+	    check_mic(sta->ptk.kck, sta->ptk.kck_len, sta->key_mgmt,
+		      key_info & WPA_KEY_INFO_TYPE_MASK,
+		      data, len) < 0) {
+		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 1/2 MIC");
+		return;
+	}
+	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 1/2");
+
+	if (sta->proto & WPA_PROTO_RSN &&
+	    !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 1/2 without EncrKeyData bit");
+		return;
+	}
+	ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+	decrypted = decrypt_eapol_key_data(wt, sta->ptk.kek, sta->ptk.kek_len,
+					   ver, hdr, &decrypted_len);
+	if (decrypted == NULL) {
+		add_note(wt, MSG_INFO, "Failed to decrypt EAPOL-Key Key Data");
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "Decrypted EAPOL-Key Key Data",
+		    decrypted, decrypted_len);
+	if (wt->write_pcap_dumper) {
+		/* Fill in a dummy Data frame header */
+		u8 buf[24 + 8 + sizeof(*eapol) + sizeof(*hdr)];
+		struct ieee80211_hdr *h;
+		struct wpa_eapol_key *k;
+		u8 *pos;
+		size_t plain_len;
+
+		plain_len = decrypted_len;
+		pos = decrypted;
+		while (pos + 1 < decrypted + decrypted_len) {
+			if (pos[0] == 0xdd && pos[1] == 0x00) {
+				/* Remove padding */
+				plain_len = pos - decrypted;
+				break;
+			}
+			pos += 2 + pos[1];
+		}
+
+		os_memset(buf, 0, sizeof(buf));
+		h = (struct ieee80211_hdr *) buf;
+		h->frame_control = host_to_le16(0x0208);
+		os_memcpy(h->addr1, dst, ETH_ALEN);
+		os_memcpy(h->addr2, src, ETH_ALEN);
+		os_memcpy(h->addr3, src, ETH_ALEN);
+		pos = (u8 *) (h + 1);
+		os_memcpy(pos, "\xaa\xaa\x03\x00\x00\x00\x88\x8e", 8);
+		pos += 8;
+		os_memcpy(pos, eapol, sizeof(*eapol));
+		pos += sizeof(*eapol);
+		os_memcpy(pos, hdr, sizeof(*hdr));
+		k = (struct wpa_eapol_key *) pos;
+		WPA_PUT_BE16(k->key_info,
+			     key_info & ~WPA_KEY_INFO_ENCR_KEY_DATA);
+		WPA_PUT_BE16(k->key_data_length, plain_len);
+		write_pcap_decrypted(wt, buf, sizeof(buf),
+				     decrypted, plain_len);
+	}
+	if (sta->proto & WPA_PROTO_RSN)
+		learn_kde_keys(wt, bss, sta, decrypted, decrypted_len,
+			       hdr->key_rsc);
+	else {
+		int klen = bss->group_cipher == WPA_CIPHER_TKIP ? 32 : 16;
+		if (decrypted_len == klen) {
+			const u8 *rsc = hdr->key_rsc;
+			int id;
+			id = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+				WPA_KEY_INFO_KEY_INDEX_SHIFT;
+			add_note(wt, MSG_DEBUG, "GTK key index %d", id);
+			wpa_hexdump(MSG_DEBUG, "GTK", decrypted,
+				    decrypted_len);
+			bss->gtk_len[id] = decrypted_len;
+			os_memcpy(bss->gtk[id], decrypted, decrypted_len);
+			bss->rsc[id][0] = rsc[5];
+			bss->rsc[id][1] = rsc[4];
+			bss->rsc[id][2] = rsc[3];
+			bss->rsc[id][3] = rsc[2];
+			bss->rsc[id][4] = rsc[1];
+			bss->rsc[id][5] = rsc[0];
+			wpa_hexdump(MSG_DEBUG, "RSC", bss->rsc[id], 6);
+		} else {
+			add_note(wt, MSG_INFO, "Unexpected WPA Key Data length "
+				 "in Group Key msg 1/2 from " MACSTR,
+				 MAC2STR(src));
+		}
+	}
+	os_free(decrypted);
+}
+
+
+static void rx_data_eapol_key_2_of_2(struct wlantest *wt, const u8 *dst,
+				     const u8 *src, const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	const struct ieee802_1x_hdr *eapol;
+	const struct wpa_eapol_key *hdr;
+	u16 key_info;
+
+	wpa_printf(MSG_DEBUG, "EAPOL-Key 2/2 " MACSTR " -> " MACSTR,
+		   MAC2STR(src), MAC2STR(dst));
+	bss = bss_get(wt, dst);
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, src);
+	if (sta == NULL)
+		return;
+
+	eapol = (const struct ieee802_1x_hdr *) data;
+	hdr = (const struct wpa_eapol_key *) (eapol + 1);
+	if (!is_zero(hdr->key_rsc, 8)) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 2/2 from " MACSTR " used "
+			 "non-zero Key RSC", MAC2STR(src));
+	}
+	key_info = WPA_GET_BE16(hdr->key_info);
+
+	if (!sta->ptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "No PTK known to process EAPOL-Key 2/2");
+		return;
+	}
+
+	if (sta->ptk_set &&
+	    check_mic(sta->ptk.kck, sta->ptk.kck_len, sta->key_mgmt,
+		      key_info & WPA_KEY_INFO_TYPE_MASK,
+		      data, len) < 0) {
+		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 2/2 MIC");
+		return;
+	}
+	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 2/2");
+}
+
+
+static void rx_data_eapol_key(struct wlantest *wt, const u8 *dst,
+			      const u8 *src, const u8 *data, size_t len,
+			      int prot)
+{
+	const struct ieee802_1x_hdr *eapol;
+	const struct wpa_eapol_key *hdr;
+	const u8 *key_data;
+	u16 key_info, key_length, ver, key_data_length;
+
+	eapol = (const struct ieee802_1x_hdr *) data;
+	hdr = (const struct wpa_eapol_key *) (eapol + 1);
+
+	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key",
+		    (const u8 *) hdr, len - sizeof(*eapol));
+	if (len < sizeof(*hdr)) {
+		add_note(wt, MSG_INFO, "Too short EAPOL-Key frame from " MACSTR,
+			 MAC2STR(src));
+		return;
+	}
+
+	if (hdr->type == EAPOL_KEY_TYPE_RC4) {
+		/* TODO: EAPOL-Key RC4 for WEP */
+		wpa_printf(MSG_INFO, "EAPOL-Key Descriptor Type RC4 from "
+			   MACSTR, MAC2STR(src));
+		return;
+	}
+
+	if (hdr->type != EAPOL_KEY_TYPE_RSN &&
+	    hdr->type != EAPOL_KEY_TYPE_WPA) {
+		wpa_printf(MSG_INFO, "Unsupported EAPOL-Key Descriptor Type "
+			   "%u from " MACSTR, hdr->type, MAC2STR(src));
+		return;
+	}
+
+	key_info = WPA_GET_BE16(hdr->key_info);
+	key_length = WPA_GET_BE16(hdr->key_length);
+	key_data_length = WPA_GET_BE16(hdr->key_data_length);
+	key_data = (const u8 *) (hdr + 1);
+	if (key_data + key_data_length > data + len) {
+		add_note(wt, MSG_INFO, "Truncated EAPOL-Key from " MACSTR,
+			 MAC2STR(src));
+		return;
+	}
+	if (key_data + key_data_length < data + len) {
+		wpa_hexdump(MSG_DEBUG, "Extra data after EAPOL-Key Key Data "
+			    "field", key_data + key_data_length,
+			data + len - key_data - key_data_length);
+	}
+
+
+	ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+	wpa_printf(MSG_DEBUG, "EAPOL-Key ver=%u %c idx=%u%s%s%s%s%s%s%s%s "
+		   "datalen=%u",
+		   ver, key_info & WPA_KEY_INFO_KEY_TYPE ? 'P' : 'G',
+		   (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+		   WPA_KEY_INFO_KEY_INDEX_SHIFT,
+		   (key_info & WPA_KEY_INFO_INSTALL) ? " Install" : "",
+		   (key_info & WPA_KEY_INFO_ACK) ? " ACK" : "",
+		   (key_info & WPA_KEY_INFO_MIC) ? " MIC" : "",
+		   (key_info & WPA_KEY_INFO_SECURE) ? " Secure" : "",
+		   (key_info & WPA_KEY_INFO_ERROR) ? " Error" : "",
+		   (key_info & WPA_KEY_INFO_REQUEST) ? " Request" : "",
+		   (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) ? " Encr" : "",
+		   (key_info & WPA_KEY_INFO_SMK_MESSAGE) ? " SMK" : "",
+		   key_data_length);
+
+	if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+	    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+	    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+		wpa_printf(MSG_INFO, "Unsupported EAPOL-Key Key Descriptor "
+			   "Version %u from " MACSTR, ver, MAC2STR(src));
+		return;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Replay Counter",
+		    hdr->replay_counter, WPA_REPLAY_COUNTER_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Nonce",
+		    hdr->key_nonce, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key IV",
+		    hdr->key_iv, 16);
+	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key RSC",
+		    hdr->key_rsc, WPA_KEY_RSC_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key MIC",
+		    hdr->key_mic, 16);
+	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data",
+		    key_data, key_data_length);
+
+	if (hdr->type == EAPOL_KEY_TYPE_RSN &&
+	    (key_info & (WPA_KEY_INFO_KEY_INDEX_MASK | BIT(14) | BIT(15))) !=
+	    0) {
+		wpa_printf(MSG_INFO, "RSN EAPOL-Key with non-zero reserved "
+			   "Key Info bits 0x%x from " MACSTR,
+			   key_info, MAC2STR(src));
+	}
+
+	if (hdr->type == EAPOL_KEY_TYPE_WPA &&
+	    (key_info & (WPA_KEY_INFO_ENCR_KEY_DATA |
+			 WPA_KEY_INFO_SMK_MESSAGE |BIT(14) | BIT(15))) != 0) {
+		wpa_printf(MSG_INFO, "WPA EAPOL-Key with non-zero reserved "
+			   "Key Info bits 0x%x from " MACSTR,
+			   key_info, MAC2STR(src));
+	}
+
+	if (key_length > 32) {
+		wpa_printf(MSG_INFO, "EAPOL-Key with invalid Key Length %d "
+			   "from " MACSTR, key_length, MAC2STR(src));
+	}
+
+	if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+	    !is_zero(hdr->key_iv, 16)) {
+		wpa_printf(MSG_INFO, "EAPOL-Key with non-zero Key IV "
+			   "(reserved with ver=%d) field from " MACSTR,
+			   ver, MAC2STR(src));
+		wpa_hexdump(MSG_INFO, "EAPOL-Key Key IV (reserved)",
+			    hdr->key_iv, 16);
+	}
+
+	if (!is_zero(hdr->key_id, 8)) {
+		wpa_printf(MSG_INFO, "EAPOL-Key with non-zero Key ID "
+			   "(reserved) field from " MACSTR, MAC2STR(src));
+		wpa_hexdump(MSG_INFO, "EAPOL-Key Key ID (reserved)",
+			    hdr->key_id, 8);
+	}
+
+	if (hdr->key_rsc[6] || hdr->key_rsc[7]) {
+		wpa_printf(MSG_INFO, "EAPOL-Key with non-zero Key RSC octets "
+			   "(last two are unused)" MACSTR, MAC2STR(src));
+	}
+
+	if (key_info & (WPA_KEY_INFO_ERROR | WPA_KEY_INFO_REQUEST))
+		return;
+
+	if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
+		return;
+
+	if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+		/* 4-Way Handshake */
+		switch (key_info & (WPA_KEY_INFO_SECURE |
+				    WPA_KEY_INFO_MIC |
+				    WPA_KEY_INFO_ACK |
+				    WPA_KEY_INFO_INSTALL)) {
+		case WPA_KEY_INFO_ACK:
+			rx_data_eapol_key_1_of_4(wt, dst, src, data, len);
+			break;
+		case WPA_KEY_INFO_MIC:
+			if (key_data_length == 0)
+				rx_data_eapol_key_4_of_4(wt, dst, src, data,
+							 len);
+			else
+				rx_data_eapol_key_2_of_4(wt, dst, src, data,
+							 len);
+			break;
+		case WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK |
+			WPA_KEY_INFO_INSTALL:
+			/* WPA does not include Secure bit in 3/4 */
+			rx_data_eapol_key_3_of_4(wt, dst, src, data, len);
+			break;
+		case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+			WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL:
+			rx_data_eapol_key_3_of_4(wt, dst, src, data, len);
+			break;
+		case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC:
+			if (key_data_length == 0)
+				rx_data_eapol_key_4_of_4(wt, dst, src, data,
+							 len);
+			else
+				rx_data_eapol_key_2_of_4(wt, dst, src, data,
+							 len);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "Unsupported EAPOL-Key frame");
+			break;
+		}
+	} else {
+		/* Group Key Handshake */
+		switch (key_info & (WPA_KEY_INFO_SECURE |
+				    WPA_KEY_INFO_MIC |
+				    WPA_KEY_INFO_ACK)) {
+		case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+			WPA_KEY_INFO_ACK:
+			rx_data_eapol_key_1_of_2(wt, dst, src, data, len);
+			break;
+		case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC:
+			rx_data_eapol_key_2_of_2(wt, dst, src, data, len);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "Unsupported EAPOL-Key frame");
+			break;
+		}
+	}
+}
+
+
+void rx_data_eapol(struct wlantest *wt, const u8 *dst, const u8 *src,
+		   const u8 *data, size_t len, int prot)
+{
+	const struct ieee802_1x_hdr *hdr;
+	u16 length;
+	const u8 *p;
+
+	wpa_hexdump(MSG_EXCESSIVE, "EAPOL", data, len);
+	if (len < sizeof(*hdr)) {
+		wpa_printf(MSG_INFO, "Too short EAPOL frame from " MACSTR,
+			   MAC2STR(src));
+		return;
+	}
+
+	hdr = (const struct ieee802_1x_hdr *) data;
+	length = be_to_host16(hdr->length);
+	wpa_printf(MSG_DEBUG, "RX EAPOL: " MACSTR " -> " MACSTR "%s ver=%u "
+		   "type=%u len=%u",
+		   MAC2STR(src), MAC2STR(dst), prot ? " Prot" : "",
+		   hdr->version, hdr->type, length);
+	if (hdr->version < 1 || hdr->version > 3) {
+		wpa_printf(MSG_INFO, "Unexpected EAPOL version %u from "
+			   MACSTR, hdr->version, MAC2STR(src));
+	}
+	if (sizeof(*hdr) + length > len) {
+		wpa_printf(MSG_INFO, "Truncated EAPOL frame from " MACSTR,
+			   MAC2STR(src));
+		return;
+	}
+
+	if (sizeof(*hdr) + length < len) {
+		wpa_printf(MSG_INFO, "EAPOL frame with %d extra bytes",
+			   (int) (len - sizeof(*hdr) - length));
+	}
+	p = (const u8 *) (hdr + 1);
+
+	switch (hdr->type) {
+	case IEEE802_1X_TYPE_EAP_PACKET:
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL - EAP packet", p, length);
+		break;
+	case IEEE802_1X_TYPE_EAPOL_START:
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Start", p, length);
+		break;
+	case IEEE802_1X_TYPE_EAPOL_LOGOFF:
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Logoff", p, length);
+		break;
+	case IEEE802_1X_TYPE_EAPOL_KEY:
+		rx_data_eapol_key(wt, dst, src, data, sizeof(*hdr) + length,
+				  prot);
+		break;
+	case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT:
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL - Encapsulated ASF alert",
+			    p, length);
+		break;
+	default:
+		wpa_hexdump(MSG_MSGDUMP, "Unknown EAPOL payload", p, length);
+		break;
+	}
+}
diff --git a/hostap/wlantest/rx_ip.c b/hostap/wlantest/rx_ip.c
new file mode 100644
index 0000000..19b338b
--- /dev/null
+++ b/hostap/wlantest/rx_ip.c
@@ -0,0 +1,177 @@
+/*
+ * Received Data frame processing for IPv4 packets
+ * Copyright (c) 2010, 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 <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#include "utils/common.h"
+#include "wlantest.h"
+
+
+static void ping_update(struct wlantest *wt, struct wlantest_sta *sta, int req,
+			u32 src, u32 dst, u16 id, u16 seq)
+{
+	if (req) {
+		sta->icmp_echo_req_src = src;
+		sta->icmp_echo_req_dst = dst;
+		sta->icmp_echo_req_id = id;
+		sta->icmp_echo_req_seq = seq;
+		return;
+	}
+
+	if (sta->icmp_echo_req_src == dst &&
+	    sta->icmp_echo_req_dst == src &&
+	    sta->icmp_echo_req_id == id &&
+	    sta->icmp_echo_req_seq == seq) {
+		sta->counters[WLANTEST_STA_COUNTER_PING_OK]++;
+		if (sta->counters[WLANTEST_STA_COUNTER_ASSOCREQ_TX] == 0 &&
+		    sta->counters[WLANTEST_STA_COUNTER_REASSOCREQ_TX] == 0)
+			sta->counters[
+				WLANTEST_STA_COUNTER_PING_OK_FIRST_ASSOC]++;
+		add_note(wt, MSG_DEBUG, "ICMP echo (ping) match for STA "
+			 MACSTR, MAC2STR(sta->addr));
+	}
+}
+
+
+static void rx_data_icmp(struct wlantest *wt, const u8 *bssid,
+			 const u8 *sta_addr, u32 dst, u32 src,
+			 const u8 *data, size_t len, const u8 *peer_addr)
+{
+	struct in_addr addr;
+	char buf[20];
+	const struct icmphdr *hdr;
+	u16 id, seq;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	hdr = (const struct icmphdr *) data;
+	if (len < 4)
+		return;
+
+	/* TODO: check hdr->checksum */
+
+	if (hdr->type != ICMP_ECHOREPLY && hdr->type != ICMP_ECHO)
+		return;
+	if (len < 8)
+		return;
+
+	id = ntohs(hdr->un.echo.id);
+	seq = ntohs(hdr->un.echo.sequence);
+
+	addr.s_addr = dst;
+	snprintf(buf, sizeof(buf), "%s", inet_ntoa(addr));
+	addr.s_addr = src;
+	add_note(wt, MSG_DEBUG, "ICMP echo %s %s -> %s id=%04x seq=%u len=%u%s",
+		 hdr->type == ICMP_ECHO ? "request" : "response",
+		 inet_ntoa(addr), buf, id, seq, (unsigned) len - 8,
+		 peer_addr ? " [DL]" : "");
+
+	bss = bss_find(wt, bssid);
+	if (bss == NULL) {
+		add_note(wt, MSG_INFO, "No BSS " MACSTR
+			 " known for ICMP packet", MAC2STR(bssid));
+		return;
+	}
+
+	if (sta_addr == NULL)
+		return; /* FromDS broadcast ping */
+
+	sta = sta_find(bss, sta_addr);
+	if (sta == NULL) {
+		add_note(wt, MSG_INFO, "No STA " MACSTR
+			 " known for ICMP packet", MAC2STR(sta_addr));
+		return;
+	}
+
+	ping_update(wt, sta, hdr->type == ICMP_ECHO, src, dst, id, seq);
+	if (peer_addr && (sta = sta_find(bss, peer_addr)))
+		ping_update(wt, sta, hdr->type == ICMP_ECHO, src, dst, id, seq);
+}
+
+
+static int hwsim_test_packet(const u8 *data, size_t len)
+{
+	size_t i;
+
+	if (len != 1500 - 14)
+		return 0;
+
+	for (i = 0; i < len; i++) {
+		if (data[i] != (i & 0xff))
+			return 0;
+	}
+
+	return 1;
+}
+
+
+void rx_data_ip(struct wlantest *wt, const u8 *bssid, const u8 *sta_addr,
+		const u8 *dst, const u8 *src, const u8 *data, size_t len,
+		const u8 *peer_addr)
+{
+	const struct iphdr *ip;
+	const u8 *payload;
+	size_t plen;
+	u16 frag_off, tot_len;
+
+	ip = (const struct iphdr *) data;
+	if (len < sizeof(*ip))
+		return;
+	if (ip->version != 4) {
+		if (hwsim_test_packet(data, len)) {
+			add_note(wt, MSG_INFO, "hwsim_test package");
+			return;
+		}
+		add_note(wt, MSG_DEBUG, "Unexpected IP protocol version %u in "
+			 "IPv4 packet (bssid=" MACSTR " str=" MACSTR
+			 " dst=" MACSTR ")", ip->version, MAC2STR(bssid),
+			 MAC2STR(src), MAC2STR(dst));
+		return;
+	}
+	if (ip->ihl * 4 < sizeof(*ip)) {
+		add_note(wt, MSG_DEBUG, "Unexpected IP header length %u in "
+			 "IPv4 packet (bssid=" MACSTR " str=" MACSTR
+			 " dst=" MACSTR ")", ip->ihl, MAC2STR(bssid),
+			 MAC2STR(src), MAC2STR(dst));
+		return;
+	}
+	if (ip->ihl * 4 > len) {
+		add_note(wt, MSG_DEBUG, "Truncated IP header (ihl=%u len=%u) "
+			 "in IPv4 packet (bssid=" MACSTR " str=" MACSTR
+			 " dst=" MACSTR ")", ip->ihl, (unsigned) len,
+			 MAC2STR(bssid), MAC2STR(src), MAC2STR(dst));
+		return;
+	}
+
+	/* TODO: check header checksum in ip->check */
+
+	frag_off = be_to_host16(ip->frag_off);
+	if (frag_off & 0x1fff) {
+		wpa_printf(MSG_EXCESSIVE, "IP fragment reassembly not yet "
+			   "supported");
+		return;
+	}
+
+	tot_len = be_to_host16(ip->tot_len);
+	if (tot_len > len)
+		return;
+	if (tot_len < len)
+		len = tot_len;
+
+	payload = data + 4 * ip->ihl;
+	plen = len - 4 * ip->ihl;
+
+	switch (ip->protocol) {
+	case IPPROTO_ICMP:
+		rx_data_icmp(wt, bssid, sta_addr, ip->daddr, ip->saddr,
+			     payload, plen, peer_addr);
+		break;
+	}
+}
diff --git a/hostap/wlantest/rx_mgmt.c b/hostap/wlantest/rx_mgmt.c
new file mode 100644
index 0000000..6242bc6
--- /dev/null
+++ b/hostap/wlantest/rx_mgmt.c
@@ -0,0 +1,1236 @@
+/*
+ * Received Management frame processing
+ * Copyright (c) 2010-2015, 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 "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+static const char * mgmt_stype(u16 stype)
+{
+	switch (stype) {
+	case WLAN_FC_STYPE_ASSOC_REQ:
+		return "ASSOC-REQ";
+	case WLAN_FC_STYPE_ASSOC_RESP:
+		return "ASSOC-RESP";
+	case WLAN_FC_STYPE_REASSOC_REQ:
+		return "REASSOC-REQ";
+	case WLAN_FC_STYPE_REASSOC_RESP:
+		return "REASSOC-RESP";
+	case WLAN_FC_STYPE_PROBE_REQ:
+		return "PROBE-REQ";
+	case WLAN_FC_STYPE_PROBE_RESP:
+		return "PROBE-RESP";
+	case WLAN_FC_STYPE_BEACON:
+		return "BEACON";
+	case WLAN_FC_STYPE_ATIM:
+		return "ATIM";
+	case WLAN_FC_STYPE_DISASSOC:
+		return "DISASSOC";
+	case WLAN_FC_STYPE_AUTH:
+		return "AUTH";
+	case WLAN_FC_STYPE_DEAUTH:
+		return "DEAUTH";
+	case WLAN_FC_STYPE_ACTION:
+		return "ACTION";
+	}
+	return "??";
+}
+
+
+static void rx_mgmt_beacon(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct ieee802_11_elems elems;
+	size_t offset;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	offset = mgmt->u.beacon.variable - data;
+	if (len < offset)
+		return;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	if (bss->proberesp_seen)
+		return; /* do not override with Beacon data */
+	bss->capab_info = le_to_host16(mgmt->u.beacon.capab_info);
+	if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - offset,
+				   &elems, 0) == ParseFailed) {
+		if (bss->parse_error_reported)
+			return;
+		add_note(wt, MSG_INFO, "Invalid IEs in a Beacon frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+		bss->parse_error_reported = 1;
+		return;
+	}
+
+	bss_update(wt, bss, &elems);
+}
+
+
+static void rx_mgmt_probe_resp(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct ieee802_11_elems elems;
+	size_t offset;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	offset = mgmt->u.probe_resp.variable - data;
+	if (len < offset)
+		return;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+
+	bss->counters[WLANTEST_BSS_COUNTER_PROBE_RESPONSE]++;
+	bss->capab_info = le_to_host16(mgmt->u.probe_resp.capab_info);
+	if (ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - offset,
+				   &elems, 0) == ParseFailed) {
+		if (bss->parse_error_reported)
+			return;
+		add_note(wt, MSG_INFO, "Invalid IEs in a Probe Response frame "
+			 "from " MACSTR, MAC2STR(mgmt->sa));
+		bss->parse_error_reported = 1;
+		return;
+	}
+
+	bss_update(wt, bss, &elems);
+}
+
+
+static void rx_mgmt_auth(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u16 alg, trans, status;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		sta = sta_get(bss, mgmt->da);
+	else
+		sta = sta_get(bss, mgmt->sa);
+	if (sta == NULL)
+		return;
+
+	if (len < 24 + 6) {
+		add_note(wt, MSG_INFO, "Too short Authentication frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	alg = le_to_host16(mgmt->u.auth.auth_alg);
+	trans = le_to_host16(mgmt->u.auth.auth_transaction);
+	status = le_to_host16(mgmt->u.auth.status_code);
+
+	wpa_printf(MSG_DEBUG, "AUTH " MACSTR " -> " MACSTR
+		   " (alg=%u trans=%u status=%u)",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da), alg, trans, status);
+
+	if (alg == 0 && trans == 2 && status == 0) {
+		if (sta->state == STATE1) {
+			add_note(wt, MSG_DEBUG, "STA " MACSTR
+				 " moved to State 2 with " MACSTR,
+				 MAC2STR(sta->addr), MAC2STR(bss->bssid));
+			sta->state = STATE2;
+		}
+	}
+
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		sta->counters[WLANTEST_STA_COUNTER_AUTH_RX]++;
+	else
+		sta->counters[WLANTEST_STA_COUNTER_AUTH_TX]++;
+}
+
+
+static void deauth_all_stas(struct wlantest *wt, struct wlantest_bss *bss)
+{
+	struct wlantest_sta *sta;
+	dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
+		if (sta->state == STATE1)
+			continue;
+		add_note(wt, MSG_DEBUG, "STA " MACSTR
+			 " moved to State 1 with " MACSTR,
+			 MAC2STR(sta->addr), MAC2STR(bss->bssid));
+		sta->state = STATE1;
+	}
+}
+
+
+static void tdls_link_down(struct wlantest *wt, struct wlantest_bss *bss,
+			   struct wlantest_sta *sta)
+{
+	struct wlantest_tdls *tdls;
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if ((tdls->init == sta || tdls->resp == sta) && tdls->link_up)
+		{
+			add_note(wt, MSG_DEBUG, "TDLS: Set link down based on "
+				 "STA deauth/disassoc");
+			tdls->link_up = 0;
+		}
+	}
+}
+
+
+static void rx_mgmt_deauth(struct wlantest *wt, const u8 *data, size_t len,
+			   int valid)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u16 fc, reason;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		sta = sta_get(bss, mgmt->da);
+	else
+		sta = sta_get(bss, mgmt->sa);
+
+	if (len < 24 + 2) {
+		add_note(wt, MSG_INFO, "Too short Deauthentication frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	reason = le_to_host16(mgmt->u.deauth.reason_code);
+	wpa_printf(MSG_DEBUG, "DEAUTH " MACSTR " -> " MACSTR
+		   " (reason=%u) (valid=%d)",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
+		   reason, valid);
+	wpa_hexdump(MSG_MSGDUMP, "DEAUTH payload", data + 24, len - 24);
+
+	if (sta == NULL) {
+		if (valid && mgmt->da[0] == 0xff)
+			deauth_all_stas(wt, bss);
+		return;
+	}
+
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
+		sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DEAUTH_RX :
+			      WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX]++;
+		if (sta->pwrmgt && !sta->pspoll)
+			sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_ASLEEP]++;
+		else
+			sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_AWAKE]++;
+
+		fc = le_to_host16(mgmt->frame_control);
+		if (!(fc & WLAN_FC_ISWEP) && reason == 6)
+			sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_RC6]++;
+		else if (!(fc & WLAN_FC_ISWEP) && reason == 7)
+			sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_RC7]++;
+	} else
+		sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DEAUTH_TX :
+			      WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX]++;
+
+	if (!valid) {
+		add_note(wt, MSG_INFO, "Do not change STA " MACSTR " State "
+			 "since Disassociation frame was not protected "
+			 "correctly", MAC2STR(sta->addr));
+		return;
+	}
+
+	if (sta->state != STATE1) {
+		add_note(wt, MSG_DEBUG, "STA " MACSTR
+			 " moved to State 1 with " MACSTR,
+			 MAC2STR(sta->addr), MAC2STR(bss->bssid));
+		sta->state = STATE1;
+	}
+	tdls_link_down(wt, bss, sta);
+}
+
+
+static void rx_mgmt_assoc_req(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	struct ieee802_11_elems elems;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, mgmt->sa);
+	if (sta == NULL)
+		return;
+
+	if (len < 24 + 4) {
+		add_note(wt, MSG_INFO, "Too short Association Request frame "
+			 "from " MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "ASSOCREQ " MACSTR " -> " MACSTR
+		   " (capab=0x%x listen_int=%u)",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
+		   le_to_host16(mgmt->u.assoc_req.capab_info),
+		   le_to_host16(mgmt->u.assoc_req.listen_interval));
+
+	sta->counters[WLANTEST_STA_COUNTER_ASSOCREQ_TX]++;
+
+	if (ieee802_11_parse_elems(mgmt->u.assoc_req.variable,
+				   len - (mgmt->u.assoc_req.variable - data),
+				   &elems, 0) == ParseFailed) {
+		add_note(wt, MSG_INFO, "Invalid IEs in Association Request "
+			 "frame from " MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	sta->assocreq_capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
+	sta->assocreq_listen_int =
+		le_to_host16(mgmt->u.assoc_req.listen_interval);
+	os_free(sta->assocreq_ies);
+	sta->assocreq_ies_len = len - (mgmt->u.assoc_req.variable - data);
+	sta->assocreq_ies = os_malloc(sta->assocreq_ies_len);
+	if (sta->assocreq_ies)
+		os_memcpy(sta->assocreq_ies, mgmt->u.assoc_req.variable,
+			  sta->assocreq_ies_len);
+
+	sta_update_assoc(sta, &elems);
+}
+
+
+static void rx_mgmt_assoc_resp(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u16 capab, status, aid;
+	const u8 *ies;
+	size_t ies_len;
+	struct wpa_ft_ies parse;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, mgmt->da);
+	if (sta == NULL)
+		return;
+
+	if (len < 24 + 6) {
+		add_note(wt, MSG_INFO, "Too short Association Response frame "
+			 "from " MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	ies = mgmt->u.assoc_resp.variable;
+	ies_len = len - (mgmt->u.assoc_resp.variable - data);
+
+	capab = le_to_host16(mgmt->u.assoc_resp.capab_info);
+	status = le_to_host16(mgmt->u.assoc_resp.status_code);
+	aid = le_to_host16(mgmt->u.assoc_resp.aid);
+
+	wpa_printf(MSG_DEBUG, "ASSOCRESP " MACSTR " -> " MACSTR
+		   " (capab=0x%x status=%u aid=%u)",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da), capab, status,
+		   aid & 0x3fff);
+
+	if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+		struct ieee802_11_elems elems;
+		if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
+		    ParseFailed) {
+			add_note(wt, MSG_INFO, "Failed to parse IEs in "
+				 "AssocResp from " MACSTR,
+				 MAC2STR(mgmt->sa));
+		} else if (elems.timeout_int == NULL ||
+			   elems.timeout_int[0] !=
+			   WLAN_TIMEOUT_ASSOC_COMEBACK) {
+			add_note(wt, MSG_INFO, "No valid Timeout Interval IE "
+				 "with Assoc Comeback time in AssocResp "
+				 "(status=30) from " MACSTR,
+				 MAC2STR(mgmt->sa));
+		} else {
+			sta->counters[
+				WLANTEST_STA_COUNTER_ASSOCRESP_COMEBACK]++;
+		}
+	}
+
+	if (status)
+		return;
+
+	if ((aid & 0xc000) != 0xc000) {
+		add_note(wt, MSG_DEBUG, "Two MSBs of the AID were not set to 1 "
+			 "in Association Response from " MACSTR,
+			 MAC2STR(mgmt->sa));
+	}
+	sta->aid = aid & 0xc000;
+
+	if (sta->state < STATE2) {
+		add_note(wt, MSG_DEBUG,
+			 "STA " MACSTR " was not in State 2 when "
+			 "getting associated", MAC2STR(sta->addr));
+	}
+
+	if (sta->state < STATE3) {
+		add_note(wt, MSG_DEBUG, "STA " MACSTR
+			 " moved to State 3 with " MACSTR,
+			 MAC2STR(sta->addr), MAC2STR(bss->bssid));
+		sta->state = STATE3;
+	}
+
+	if (wpa_ft_parse_ies(ies, ies_len, &parse) == 0) {
+		if (parse.r0kh_id) {
+			os_memcpy(bss->r0kh_id, parse.r0kh_id,
+				  parse.r0kh_id_len);
+			bss->r0kh_id_len = parse.r0kh_id_len;
+		}
+		if (parse.r1kh_id)
+			os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+	}
+}
+
+
+static void rx_mgmt_reassoc_req(struct wlantest *wt, const u8 *data,
+				size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	struct ieee802_11_elems elems;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, mgmt->sa);
+	if (sta == NULL)
+		return;
+
+	if (len < 24 + 4 + ETH_ALEN) {
+		add_note(wt, MSG_INFO, "Too short Reassociation Request frame "
+			 "from " MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "REASSOCREQ " MACSTR " -> " MACSTR
+		   " (capab=0x%x listen_int=%u current_ap=" MACSTR ")",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
+		   le_to_host16(mgmt->u.reassoc_req.capab_info),
+		   le_to_host16(mgmt->u.reassoc_req.listen_interval),
+		   MAC2STR(mgmt->u.reassoc_req.current_ap));
+
+	sta->counters[WLANTEST_STA_COUNTER_REASSOCREQ_TX]++;
+
+	if (ieee802_11_parse_elems(mgmt->u.reassoc_req.variable,
+				   len - (mgmt->u.reassoc_req.variable - data),
+				   &elems, 0) == ParseFailed) {
+		add_note(wt, MSG_INFO, "Invalid IEs in Reassociation Request "
+			 "frame from " MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	sta->assocreq_capab_info =
+		le_to_host16(mgmt->u.reassoc_req.capab_info);
+	sta->assocreq_listen_int =
+		le_to_host16(mgmt->u.reassoc_req.listen_interval);
+	os_free(sta->assocreq_ies);
+	sta->assocreq_ies_len = len - (mgmt->u.reassoc_req.variable - data);
+	sta->assocreq_ies = os_malloc(sta->assocreq_ies_len);
+	if (sta->assocreq_ies)
+		os_memcpy(sta->assocreq_ies, mgmt->u.reassoc_req.variable,
+			  sta->assocreq_ies_len);
+
+	sta_update_assoc(sta, &elems);
+}
+
+
+static void rx_mgmt_reassoc_resp(struct wlantest *wt, const u8 *data,
+				 size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u16 capab, status, aid;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, mgmt->da);
+	if (sta == NULL)
+		return;
+
+	if (len < 24 + 6) {
+		add_note(wt, MSG_INFO, "Too short Reassociation Response frame "
+			 "from " MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	capab = le_to_host16(mgmt->u.reassoc_resp.capab_info);
+	status = le_to_host16(mgmt->u.reassoc_resp.status_code);
+	aid = le_to_host16(mgmt->u.reassoc_resp.aid);
+
+	wpa_printf(MSG_DEBUG, "REASSOCRESP " MACSTR " -> " MACSTR
+		   " (capab=0x%x status=%u aid=%u)",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da), capab, status,
+		   aid & 0x3fff);
+
+	if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+		struct ieee802_11_elems elems;
+		const u8 *ies = mgmt->u.reassoc_resp.variable;
+		size_t ies_len = len - (mgmt->u.reassoc_resp.variable - data);
+		if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
+		    ParseFailed) {
+			add_note(wt, MSG_INFO, "Failed to parse IEs in "
+				 "ReassocResp from " MACSTR,
+				 MAC2STR(mgmt->sa));
+		} else if (elems.timeout_int == NULL ||
+			   elems.timeout_int[0] !=
+			   WLAN_TIMEOUT_ASSOC_COMEBACK) {
+			add_note(wt, MSG_INFO, "No valid Timeout Interval IE "
+				 "with Assoc Comeback time in ReassocResp "
+				 "(status=30) from " MACSTR,
+				 MAC2STR(mgmt->sa));
+		} else {
+			sta->counters[
+				WLANTEST_STA_COUNTER_REASSOCRESP_COMEBACK]++;
+		}
+	}
+
+	if (status)
+		return;
+
+	if ((aid & 0xc000) != 0xc000) {
+		add_note(wt, MSG_DEBUG, "Two MSBs of the AID were not set to 1 "
+			 "in Reassociation Response from " MACSTR,
+			 MAC2STR(mgmt->sa));
+	}
+	sta->aid = aid & 0xc000;
+
+	if (sta->state < STATE2) {
+		add_note(wt, MSG_DEBUG,
+			 "STA " MACSTR " was not in State 2 when "
+			 "getting associated", MAC2STR(sta->addr));
+	}
+
+	if (sta->state < STATE3) {
+		add_note(wt, MSG_DEBUG, "STA " MACSTR
+			 " moved to State 3 with " MACSTR,
+			 MAC2STR(sta->addr), MAC2STR(bss->bssid));
+		sta->state = STATE3;
+	}
+}
+
+
+static void disassoc_all_stas(struct wlantest *wt, struct wlantest_bss *bss)
+{
+	struct wlantest_sta *sta;
+	dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
+		if (sta->state <= STATE2)
+			continue;
+		add_note(wt, MSG_DEBUG, "STA " MACSTR
+			 " moved to State 2 with " MACSTR,
+			 MAC2STR(sta->addr), MAC2STR(bss->bssid));
+		sta->state = STATE2;
+	}
+}
+
+
+static void rx_mgmt_disassoc(struct wlantest *wt, const u8 *data, size_t len,
+			     int valid)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u16 fc, reason;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		sta = sta_get(bss, mgmt->da);
+	else
+		sta = sta_get(bss, mgmt->sa);
+
+	if (len < 24 + 2) {
+		add_note(wt, MSG_INFO, "Too short Disassociation frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	reason = le_to_host16(mgmt->u.disassoc.reason_code);
+	wpa_printf(MSG_DEBUG, "DISASSOC " MACSTR " -> " MACSTR
+		   " (reason=%u) (valid=%d)",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
+		   reason, valid);
+	wpa_hexdump(MSG_MSGDUMP, "DISASSOC payload", data + 24, len - 24);
+
+	if (sta == NULL) {
+		if (valid && mgmt->da[0] == 0xff)
+			disassoc_all_stas(wt, bss);
+		return;
+	}
+
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
+		sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DISASSOC_RX :
+			      WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX]++;
+		if (sta->pwrmgt && !sta->pspoll)
+			sta->counters[
+				WLANTEST_STA_COUNTER_DISASSOC_RX_ASLEEP]++;
+		else
+			sta->counters[
+				WLANTEST_STA_COUNTER_DISASSOC_RX_AWAKE]++;
+
+		fc = le_to_host16(mgmt->frame_control);
+		if (!(fc & WLAN_FC_ISWEP) && reason == 6)
+			sta->counters[WLANTEST_STA_COUNTER_DISASSOC_RX_RC6]++;
+		else if (!(fc & WLAN_FC_ISWEP) && reason == 7)
+			sta->counters[WLANTEST_STA_COUNTER_DISASSOC_RX_RC7]++;
+	} else
+		sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DISASSOC_TX :
+			      WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX]++;
+
+	if (!valid) {
+		add_note(wt, MSG_INFO, "Do not change STA " MACSTR " State "
+			 "since Disassociation frame was not protected "
+			 "correctly", MAC2STR(sta->addr));
+		return;
+	}
+
+	if (sta->state < STATE2) {
+		add_note(wt, MSG_DEBUG,
+			 "STA " MACSTR " was not in State 2 or 3 "
+			 "when getting disassociated", MAC2STR(sta->addr));
+	}
+
+	if (sta->state > STATE2) {
+		add_note(wt, MSG_DEBUG, "STA " MACSTR
+			 " moved to State 2 with " MACSTR,
+			 MAC2STR(sta->addr), MAC2STR(bss->bssid));
+		sta->state = STATE2;
+	}
+	tdls_link_down(wt, bss, sta);
+}
+
+
+static void rx_mgmt_action_sa_query_req(struct wlantest *wt,
+					struct wlantest_sta *sta,
+					const struct ieee80211_mgmt *mgmt,
+					size_t len, int valid)
+{
+	const u8 *rx_id;
+	u8 *id;
+
+	rx_id = (const u8 *) mgmt->u.action.u.sa_query_req.trans_id;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		id = sta->ap_sa_query_tr;
+	else
+		id = sta->sta_sa_query_tr;
+	add_note(wt, MSG_INFO, "SA Query Request " MACSTR " -> " MACSTR
+		 " (trans_id=%02x%02x)%s",
+		 MAC2STR(mgmt->sa), MAC2STR(mgmt->da), rx_id[0], rx_id[1],
+		 valid ? "" : " (invalid protection)");
+	os_memcpy(id, mgmt->u.action.u.sa_query_req.trans_id, 2);
+	if (os_memcmp(mgmt->sa, sta->addr, ETH_ALEN) == 0)
+		sta->counters[valid ?
+			      WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX :
+			      WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX]++;
+	else
+		sta->counters[valid ?
+			      WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX :
+			      WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX]++;
+}
+
+
+static void rx_mgmt_action_sa_query_resp(struct wlantest *wt,
+					 struct wlantest_sta *sta,
+					 const struct ieee80211_mgmt *mgmt,
+					 size_t len, int valid)
+{
+	const u8 *rx_id;
+	u8 *id;
+	int match;
+
+	rx_id = (const u8 *) mgmt->u.action.u.sa_query_resp.trans_id;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		id = sta->sta_sa_query_tr;
+	else
+		id = sta->ap_sa_query_tr;
+	match = os_memcmp(rx_id, id, 2) == 0;
+	add_note(wt, MSG_INFO, "SA Query Response " MACSTR " -> " MACSTR
+		 " (trans_id=%02x%02x; %s)%s",
+		 MAC2STR(mgmt->sa), MAC2STR(mgmt->da), rx_id[0], rx_id[1],
+		 match ? "match" : "mismatch",
+		 valid ? "" : " (invalid protection)");
+	if (os_memcmp(mgmt->sa, sta->addr, ETH_ALEN) == 0)
+		sta->counters[(valid && match) ?
+			      WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX :
+			      WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX]++;
+	else
+		sta->counters[(valid && match) ?
+			      WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX :
+			      WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX]++;
+}
+
+
+static void rx_mgmt_action_sa_query(struct wlantest *wt,
+				    struct wlantest_sta *sta,
+				    const struct ieee80211_mgmt *mgmt,
+				    size_t len, int valid)
+{
+	if (len < 24 + 2 + WLAN_SA_QUERY_TR_ID_LEN) {
+		add_note(wt, MSG_INFO, "Too short SA Query frame from " MACSTR,
+			 MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (len > 24 + 2 + WLAN_SA_QUERY_TR_ID_LEN) {
+		size_t elen = len - (24 + 2 + WLAN_SA_QUERY_TR_ID_LEN);
+		add_note(wt, MSG_INFO, "Unexpected %u octets of extra data at "
+			 "the end of SA Query frame from " MACSTR,
+			 (unsigned) elen, MAC2STR(mgmt->sa));
+		wpa_hexdump(MSG_INFO, "SA Query extra data",
+			    ((const u8 *) mgmt) + len - elen, elen);
+	}
+
+	switch (mgmt->u.action.u.sa_query_req.action) {
+	case WLAN_SA_QUERY_REQUEST:
+		rx_mgmt_action_sa_query_req(wt, sta, mgmt, len, valid);
+		break;
+	case WLAN_SA_QUERY_RESPONSE:
+		rx_mgmt_action_sa_query_resp(wt, sta, mgmt, len, valid);
+		break;
+	default:
+		add_note(wt, MSG_INFO, "Unexpected SA Query action value %u "
+			 "from " MACSTR,
+			 mgmt->u.action.u.sa_query_req.action,
+			 MAC2STR(mgmt->sa));
+	}
+}
+
+
+static void rx_mgmt_action(struct wlantest *wt, const u8 *data, size_t len,
+			   int valid)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	if (mgmt->da[0] & 0x01) {
+		add_note(wt, MSG_DEBUG, "Group addressed Action frame: DA="
+			 MACSTR " SA=" MACSTR " BSSID=" MACSTR
+			 " category=%u",
+			 MAC2STR(mgmt->da), MAC2STR(mgmt->sa),
+			 MAC2STR(mgmt->bssid), mgmt->u.action.category);
+		return; /* Ignore group addressed Action frames for now */
+	}
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		sta = sta_get(bss, mgmt->da);
+	else
+		sta = sta_get(bss, mgmt->sa);
+	if (sta == NULL)
+		return;
+
+	if (len < 24 + 1) {
+		add_note(wt, MSG_INFO, "Too short Action frame from " MACSTR,
+			 MAC2STR(mgmt->sa));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "ACTION " MACSTR " -> " MACSTR
+		   " (category=%u) (valid=%d)",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
+		   mgmt->u.action.category, valid);
+	wpa_hexdump(MSG_MSGDUMP, "ACTION payload", data + 24, len - 24);
+
+	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+	    sta->state < STATE3) {
+		add_note(wt, MSG_INFO, "Action frame sent when STA is not in "
+			 "State 3 (SA=" MACSTR " DATA=" MACSTR ")",
+			 MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+	}
+
+	switch (mgmt->u.action.category) {
+	case WLAN_ACTION_SA_QUERY:
+		rx_mgmt_action_sa_query(wt, sta, mgmt, len, valid);
+		break;
+	}
+}
+
+
+static int check_mmie_mic(unsigned int mgmt_group_cipher,
+			  const u8 *igtk, size_t igtk_len,
+			  const u8 *data, size_t len)
+{
+	u8 *buf;
+	u8 mic[16];
+	u16 fc;
+	const struct ieee80211_hdr *hdr;
+	int ret, mic_len;
+
+	if (!mgmt_group_cipher || igtk_len < 16)
+		return -1;
+	mic_len = mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC ? 8 : 16;
+
+	if (len < 24 || len - 24 < mic_len)
+		return -1;
+
+	buf = os_malloc(len + 20 - 24);
+	if (buf == NULL)
+		return -1;
+
+	/* BIP AAD: FC(masked) A1 A2 A3 */
+	hdr = (const struct ieee80211_hdr *) data;
+	fc = le_to_host16(hdr->frame_control);
+	fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+	WPA_PUT_LE16(buf, fc);
+	os_memcpy(buf + 2, hdr->addr1, 3 * ETH_ALEN);
+
+	/* Frame body with MMIE MIC masked to zero */
+	os_memcpy(buf + 20, data + 24, len - 24 - mic_len);
+	os_memset(buf + 20 + len - 24 - mic_len, 0, mic_len);
+
+	wpa_hexdump(MSG_MSGDUMP, "BIP: AAD|Body(masked)", buf, len + 20 - 24);
+	/* MIC = L(AES-128-CMAC(AAD || Frame Body(masked)), 0, 64) */
+	if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+		ret = omac1_aes_128(igtk, buf, len + 20 - 24, mic);
+	} else if (mgmt_group_cipher == WPA_CIPHER_BIP_CMAC_256) {
+		ret = omac1_aes_256(igtk, buf, len + 20 - 24, mic);
+	} else if (mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_128 ||
+		 mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_256) {
+		u8 nonce[12], *npos;
+		const u8 *ipn;
+
+		ipn = data + len - mic_len - 6;
+
+		/* Nonce: A2 | IPN */
+		os_memcpy(nonce, hdr->addr2, ETH_ALEN);
+		npos = nonce + ETH_ALEN;
+		*npos++ = ipn[5];
+		*npos++ = ipn[4];
+		*npos++ = ipn[3];
+		*npos++ = ipn[2];
+		*npos++ = ipn[1];
+		*npos++ = ipn[0];
+
+		ret = aes_gmac(igtk, igtk_len, nonce, sizeof(nonce),
+			       buf, len + 20 - 24, mic);
+	} else {
+		ret = -1;
+	}
+	if (ret < 0) {
+		os_free(buf);
+		return -1;
+	}
+
+	os_free(buf);
+
+	if (os_memcmp(data + len - mic_len, mic, mic_len) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int check_bip(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	u16 fc, stype;
+	const u8 *mmie;
+	u16 keyid;
+	struct wlantest_bss *bss;
+	size_t mic_len;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	fc = le_to_host16(mgmt->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if (stype == WLAN_FC_STYPE_ACTION) {
+		if (len < 24 + 1)
+			return 0;
+		if (mgmt->u.action.category == WLAN_ACTION_PUBLIC)
+			return 0; /* Not a robust management frame */
+	}
+
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return 0; /* No key known yet */
+
+	mic_len = bss->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC ? 8 : 16;
+
+	if (len < 24 + 10 + mic_len ||
+	    data[len - (10 + mic_len)] != WLAN_EID_MMIE ||
+	    data[len - (10 + mic_len - 1)] != 8 + mic_len) {
+		/* No MMIE */
+		if (bss->rsn_capab & WPA_CAPABILITY_MFPC) {
+			add_note(wt, MSG_INFO, "Robust group-addressed "
+				 "management frame sent without BIP by "
+				 MACSTR, MAC2STR(mgmt->sa));
+			bss->counters[WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE]++;
+			return -1;
+		}
+		return 0;
+	}
+
+	mmie = data + len - (8 + mic_len);
+	keyid = WPA_GET_LE16(mmie);
+	if (keyid & 0xf000) {
+		add_note(wt, MSG_INFO, "MMIE KeyID reserved bits not zero "
+			 "(%04x) from " MACSTR, keyid, MAC2STR(mgmt->sa));
+		keyid &= 0x0fff;
+	}
+	if (keyid < 4 || keyid > 5) {
+		add_note(wt, MSG_INFO, "Unexpected MMIE KeyID %u from " MACSTR,
+			 keyid, MAC2STR(mgmt->sa));
+		bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++;
+		return 0;
+	}
+	wpa_printf(MSG_DEBUG, "MMIE KeyID %u", keyid);
+	wpa_hexdump(MSG_MSGDUMP, "MMIE IPN", mmie + 2, 6);
+	wpa_hexdump(MSG_MSGDUMP, "MMIE MIC", mmie + 8, mic_len);
+
+	if (!bss->igtk_len[keyid]) {
+		add_note(wt, MSG_DEBUG, "No IGTK known to validate BIP frame");
+		return 0;
+	}
+
+	if (os_memcmp(mmie + 2, bss->ipn[keyid], 6) <= 0) {
+		add_note(wt, MSG_INFO, "BIP replay detected: SA=" MACSTR,
+			 MAC2STR(mgmt->sa));
+		wpa_hexdump(MSG_INFO, "RX IPN", mmie + 2, 6);
+		wpa_hexdump(MSG_INFO, "Last RX IPN", bss->ipn[keyid], 6);
+	}
+
+	if (check_mmie_mic(bss->mgmt_group_cipher, bss->igtk[keyid],
+			   bss->igtk_len[keyid], data, len) < 0) {
+		add_note(wt, MSG_INFO, "Invalid MMIE MIC in a frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+		bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++;
+		return -1;
+	}
+
+	add_note(wt, MSG_DEBUG, "Valid MMIE MIC");
+	os_memcpy(bss->ipn[keyid], mmie + 2, 6);
+	bss->counters[WLANTEST_BSS_COUNTER_VALID_BIP_MMIE]++;
+
+	if (stype == WLAN_FC_STYPE_DEAUTH)
+		bss->counters[WLANTEST_BSS_COUNTER_BIP_DEAUTH]++;
+	else if (stype == WLAN_FC_STYPE_DISASSOC)
+		bss->counters[WLANTEST_BSS_COUNTER_BIP_DISASSOC]++;
+
+	return 0;
+}
+
+
+static u8 * mgmt_ccmp_decrypt(struct wlantest *wt, const u8 *data, size_t len,
+			      size_t *dlen)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	const struct ieee80211_hdr *hdr;
+	int keyid;
+	u8 *decrypted, *frame = NULL;
+	u8 pn[6], *rsc;
+
+	hdr = (const struct ieee80211_hdr *) data;
+	bss = bss_get(wt, hdr->addr3);
+	if (bss == NULL)
+		return NULL;
+	if (os_memcmp(hdr->addr1, hdr->addr3, ETH_ALEN) == 0)
+		sta = sta_get(bss, hdr->addr2);
+	else
+		sta = sta_get(bss, hdr->addr1);
+	if (sta == NULL || !sta->ptk_set) {
+		add_note(wt, MSG_MSGDUMP, "No PTK known to decrypt the frame");
+		return NULL;
+	}
+
+	if (len < 24 + 4)
+		return NULL;
+
+	if (!(data[24 + 3] & 0x20)) {
+		add_note(wt, MSG_INFO, "Expected CCMP frame from " MACSTR
+			 " did not have ExtIV bit set to 1",
+			 MAC2STR(hdr->addr2));
+		return NULL;
+	}
+
+	if (data[24 + 2] != 0 || (data[24 + 3] & 0x1f) != 0) {
+		add_note(wt, MSG_INFO, "CCMP mgmt frame from " MACSTR " used "
+			 "non-zero reserved bit", MAC2STR(hdr->addr2));
+	}
+
+	keyid = data[24 + 3] >> 6;
+	if (keyid != 0) {
+		add_note(wt, MSG_INFO, "Unexpected non-zero KeyID %d in "
+			 "individually addressed Management frame from "
+			 MACSTR, keyid, MAC2STR(hdr->addr2));
+	}
+
+	if (os_memcmp(hdr->addr1, hdr->addr3, ETH_ALEN) == 0)
+		rsc = sta->rsc_tods[16];
+	else
+		rsc = sta->rsc_fromds[16];
+
+	ccmp_get_pn(pn, data + 24);
+	if (os_memcmp(pn, rsc, 6) <= 0) {
+		u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+		add_note(wt, MSG_INFO, "CCMP/TKIP replay detected: A1=" MACSTR
+			 " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u%s",
+			 MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			 MAC2STR(hdr->addr3),
+			 WLAN_GET_SEQ_SEQ(seq_ctrl),
+			 WLAN_GET_SEQ_FRAG(seq_ctrl),
+			 (le_to_host16(hdr->frame_control) & WLAN_FC_RETRY) ?
+			 " Retry" : "");
+		wpa_hexdump(MSG_INFO, "RX PN", pn, 6);
+		wpa_hexdump(MSG_INFO, "RSC", rsc, 6);
+	}
+
+	decrypted = ccmp_decrypt(sta->ptk.tk, hdr, data + 24, len - 24, dlen);
+	if (decrypted) {
+		os_memcpy(rsc, pn, 6);
+		frame = os_malloc(24 + *dlen);
+		if (frame) {
+			os_memcpy(frame, data, 24);
+			os_memcpy(frame + 24, decrypted, *dlen);
+			*dlen += 24;
+		}
+	}
+
+	os_free(decrypted);
+
+	return frame;
+}
+
+
+static int check_mgmt_ccmp(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	u16 fc;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	fc = le_to_host16(mgmt->frame_control);
+
+	if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
+		if (len > 24 &&
+		    mgmt->u.action.category == WLAN_ACTION_PUBLIC)
+			return 0; /* Not a robust management frame */
+	}
+
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return 0;
+	if (os_memcmp(mgmt->da, mgmt->bssid, ETH_ALEN) == 0)
+		sta = sta_get(bss, mgmt->sa);
+	else
+		sta = sta_get(bss, mgmt->da);
+	if (sta == NULL)
+		return 0;
+
+	if ((sta->rsn_capab & WPA_CAPABILITY_MFPC) &&
+	    (sta->state == STATE3 ||
+	     WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION)) {
+		add_note(wt, MSG_INFO, "Robust individually-addressed "
+			 "management frame sent without CCMP by "
+			 MACSTR, MAC2STR(mgmt->sa));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void rx_mgmt(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_hdr *hdr;
+	u16 fc, stype;
+	int valid = 1;
+	u8 *decrypted = NULL;
+	size_t dlen;
+
+	if (len < 24)
+		return;
+
+	hdr = (const struct ieee80211_hdr *) data;
+	fc = le_to_host16(hdr->frame_control);
+	wt->rx_mgmt++;
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if ((hdr->addr1[0] & 0x01) &&
+	    (stype == WLAN_FC_STYPE_DEAUTH ||
+	     stype == WLAN_FC_STYPE_DISASSOC ||
+	     stype == WLAN_FC_STYPE_ACTION)) {
+		if (check_bip(wt, data, len) < 0)
+			valid = 0;
+	}
+
+	wpa_printf((stype == WLAN_FC_STYPE_BEACON ||
+		    stype == WLAN_FC_STYPE_PROBE_RESP ||
+		    stype == WLAN_FC_STYPE_PROBE_REQ) ?
+		   MSG_EXCESSIVE : MSG_MSGDUMP,
+		   "MGMT %s%s%s DA=" MACSTR " SA=" MACSTR " BSSID=" MACSTR,
+		   mgmt_stype(stype),
+		   fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+		   fc & WLAN_FC_ISWEP ? " Prot" : "",
+		   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+		   MAC2STR(hdr->addr3));
+
+	if ((fc & WLAN_FC_ISWEP) &&
+	    !(hdr->addr1[0] & 0x01) &&
+	    (stype == WLAN_FC_STYPE_DEAUTH ||
+	     stype == WLAN_FC_STYPE_DISASSOC ||
+	     stype == WLAN_FC_STYPE_ACTION)) {
+		decrypted = mgmt_ccmp_decrypt(wt, data, len, &dlen);
+		if (decrypted) {
+			write_pcap_decrypted(wt, decrypted, dlen, NULL, 0);
+			data = decrypted;
+			len = dlen;
+		} else
+			valid = 0;
+	}
+
+	if (!(fc & WLAN_FC_ISWEP) &&
+	    !(hdr->addr1[0] & 0x01) &&
+	    (stype == WLAN_FC_STYPE_DEAUTH ||
+	     stype == WLAN_FC_STYPE_DISASSOC ||
+	     stype == WLAN_FC_STYPE_ACTION)) {
+		if (check_mgmt_ccmp(wt, data, len) < 0)
+			valid = 0;
+	}
+
+	switch (stype) {
+	case WLAN_FC_STYPE_BEACON:
+		rx_mgmt_beacon(wt, data, len);
+		break;
+	case WLAN_FC_STYPE_PROBE_RESP:
+		rx_mgmt_probe_resp(wt, data, len);
+		break;
+	case WLAN_FC_STYPE_AUTH:
+		rx_mgmt_auth(wt, data, len);
+		break;
+	case WLAN_FC_STYPE_DEAUTH:
+		rx_mgmt_deauth(wt, data, len, valid);
+		break;
+	case WLAN_FC_STYPE_ASSOC_REQ:
+		rx_mgmt_assoc_req(wt, data, len);
+		break;
+	case WLAN_FC_STYPE_ASSOC_RESP:
+		rx_mgmt_assoc_resp(wt, data, len);
+		break;
+	case WLAN_FC_STYPE_REASSOC_REQ:
+		rx_mgmt_reassoc_req(wt, data, len);
+		break;
+	case WLAN_FC_STYPE_REASSOC_RESP:
+		rx_mgmt_reassoc_resp(wt, data, len);
+		break;
+	case WLAN_FC_STYPE_DISASSOC:
+		rx_mgmt_disassoc(wt, data, len, valid);
+		break;
+	case WLAN_FC_STYPE_ACTION:
+		rx_mgmt_action(wt, data, len, valid);
+		break;
+	}
+
+	os_free(decrypted);
+
+	wt->last_mgmt_valid = valid;
+}
+
+
+static void rx_mgmt_deauth_ack(struct wlantest *wt,
+			       const struct ieee80211_hdr *hdr)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	mgmt = (const struct ieee80211_mgmt *) hdr;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		sta = sta_get(bss, mgmt->da);
+	else
+		sta = sta_get(bss, mgmt->sa);
+	if (sta == NULL)
+		return;
+
+	add_note(wt, MSG_DEBUG, "DEAUTH from " MACSTR " acknowledged by "
+		 MACSTR, MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
+		int c;
+		c = wt->last_mgmt_valid ?
+			WLANTEST_STA_COUNTER_VALID_DEAUTH_RX_ACK :
+			WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX_ACK;
+		sta->counters[c]++;
+	}
+}
+
+
+static void rx_mgmt_disassoc_ack(struct wlantest *wt,
+				 const struct ieee80211_hdr *hdr)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	mgmt = (const struct ieee80211_mgmt *) hdr;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		sta = sta_get(bss, mgmt->da);
+	else
+		sta = sta_get(bss, mgmt->sa);
+	if (sta == NULL)
+		return;
+
+	add_note(wt, MSG_DEBUG, "DISASSOC from " MACSTR " acknowledged by "
+		 MACSTR, MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
+		int c;
+		c = wt->last_mgmt_valid ?
+			WLANTEST_STA_COUNTER_VALID_DISASSOC_RX_ACK :
+			WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX_ACK;
+		sta->counters[c]++;
+	}
+}
+
+
+void rx_mgmt_ack(struct wlantest *wt, const struct ieee80211_hdr *hdr)
+{
+	u16 fc, stype;
+	fc = le_to_host16(hdr->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	wpa_printf(MSG_MSGDUMP, "MGMT ACK: stype=%u a1=" MACSTR " a2=" MACSTR
+		   " a3=" MACSTR,
+		   stype, MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+		   MAC2STR(hdr->addr3));
+
+	switch (stype) {
+	case WLAN_FC_STYPE_DEAUTH:
+		rx_mgmt_deauth_ack(wt, hdr);
+		break;
+	case WLAN_FC_STYPE_DISASSOC:
+		rx_mgmt_disassoc_ack(wt, hdr);
+		break;
+	}
+}
diff --git a/hostap/wlantest/rx_tdls.c b/hostap/wlantest/rx_tdls.c
new file mode 100644
index 0000000..0c012a9
--- /dev/null
+++ b/hostap/wlantest/rx_tdls.c
@@ -0,0 +1,618 @@
+/*
+ * Received Data frame processing for TDLS packets
+ * Copyright (c) 2010, 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 "crypto/sha256.h"
+#include "crypto/crypto.h"
+#include "crypto/aes_wrap.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wlantest.h"
+
+
+static struct wlantest_tdls * get_tdls(struct wlantest *wt, const u8 *linkid,
+				       int create_new, const u8 *bssid)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *init, *resp;
+	struct wlantest_tdls *tdls;
+
+	bss = bss_find(wt, linkid);
+	if (bss == NULL && bssid) {
+		bss = bss_find(wt, bssid);
+		if (bss)
+			add_note(wt, MSG_INFO, "TDLS: Incorrect BSSID " MACSTR
+				 " in LinkId?! (init=" MACSTR " resp="
+				 MACSTR ")",
+				 MAC2STR(linkid), MAC2STR(linkid + ETH_ALEN),
+				 MAC2STR(linkid + 2 * ETH_ALEN));
+	}
+	if (bss == NULL)
+		return NULL;
+
+	init = sta_find(bss, linkid + ETH_ALEN);
+	if (init == NULL)
+		return NULL;
+
+	resp = sta_find(bss, linkid + 2 * ETH_ALEN);
+	if (resp == NULL)
+		return NULL;
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if (tdls->init == init && tdls->resp == resp)
+			return tdls;
+	}
+
+	if (!create_new)
+		return NULL;
+
+	add_note(wt, MSG_DEBUG, "Add new TDLS link context: initiator " MACSTR
+		 " responder " MACSTR " BSSID " MACSTR,
+		 MAC2STR(linkid + ETH_ALEN),
+		 MAC2STR(linkid + 2 * ETH_ALEN),
+		 MAC2STR(bssid));
+
+	tdls = os_zalloc(sizeof(*tdls));
+	if (tdls == NULL)
+		return NULL;
+	tdls->init = init;
+	tdls->resp = resp;
+	dl_list_add(&bss->tdls, &tdls->list);
+	return tdls;
+}
+
+
+static int tdls_derive_tpk(struct wlantest_tdls *tdls, const u8 *bssid,
+			   const u8 *ftie, u8 ftie_len)
+{
+	const struct rsn_ftie *f;
+	u8 key_input[SHA256_MAC_LEN];
+	const u8 *nonce[2];
+	size_t len[2];
+	u8 data[3 * ETH_ALEN];
+
+	if (ftie == NULL || ftie_len < sizeof(struct rsn_ftie))
+		return 0;
+
+	f = (const struct rsn_ftie *) ftie;
+	wpa_hexdump(MSG_DEBUG, "TDLS ANonce", f->anonce, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "TDLS SNonce", f->snonce, WPA_NONCE_LEN);
+
+	/*
+	 * IEEE Std 802.11z-2010 8.5.9.1:
+	 * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce))
+	 */
+	len[0] = WPA_NONCE_LEN;
+	len[1] = WPA_NONCE_LEN;
+	if (os_memcmp(f->anonce, f->snonce, WPA_NONCE_LEN) < 0) {
+		nonce[0] = f->anonce;
+		nonce[1] = f->snonce;
+	} else {
+		nonce[0] = f->snonce;
+		nonce[1] = f->anonce;
+	}
+	sha256_vector(2, nonce, len, key_input);
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input",
+			key_input, SHA256_MAC_LEN);
+
+	/*
+	 * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK",
+	 *	min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY)
+	 * TODO: is N_KEY really included in KDF Context and if so, in which
+	 * presentation format (little endian 16-bit?) is it used? It gets
+	 * added by the KDF anyway..
+	 */
+
+	if (os_memcmp(tdls->init->addr, tdls->resp->addr, ETH_ALEN) < 0) {
+		os_memcpy(data, tdls->init->addr, ETH_ALEN);
+		os_memcpy(data + ETH_ALEN, tdls->resp->addr, ETH_ALEN);
+	} else {
+		os_memcpy(data, tdls->resp->addr, ETH_ALEN);
+		os_memcpy(data + ETH_ALEN, tdls->init->addr, ETH_ALEN);
+	}
+	os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN);
+	wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data));
+
+	sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data),
+		   (u8 *) &tdls->tpk, sizeof(tdls->tpk));
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK",
+			tdls->tpk.kck, sizeof(tdls->tpk.kck));
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK",
+			tdls->tpk.tk, sizeof(tdls->tpk.tk));
+
+	return 1;
+}
+
+
+static int tdls_verify_mic(struct wlantest *wt, struct wlantest_tdls *tdls,
+			   u8 trans_seq, struct ieee802_11_elems *elems)
+{
+	u8 *buf, *pos;
+	int len;
+	u8 mic[16];
+	int ret;
+	const struct rsn_ftie *rx_ftie;
+	struct rsn_ftie *tmp_ftie;
+
+	if (elems->link_id == NULL || elems->rsn_ie == NULL ||
+	    elems->timeout_int == NULL || elems->ftie == NULL ||
+	    elems->ftie_len < sizeof(struct rsn_ftie))
+		return -1;
+
+	len = 2 * ETH_ALEN + 1 + 2 + 18 + 2 + elems->rsn_ie_len +
+		2 + 5 + 2 + elems->ftie_len;
+
+	buf = os_zalloc(len);
+	if (buf == NULL)
+		return -1;
+
+	pos = buf;
+	/* 1) TDLS initiator STA MAC address */
+	os_memcpy(pos, elems->link_id + ETH_ALEN, ETH_ALEN);
+	pos += ETH_ALEN;
+	/* 2) TDLS responder STA MAC address */
+	os_memcpy(pos, elems->link_id + 2 * ETH_ALEN, ETH_ALEN);
+	pos += ETH_ALEN;
+	/* 3) Transaction Sequence number */
+	*pos++ = trans_seq;
+	/* 4) Link Identifier IE */
+	os_memcpy(pos, elems->link_id - 2, 2 + 18);
+	pos += 2 + 18;
+	/* 5) RSN IE */
+	os_memcpy(pos, elems->rsn_ie - 2, 2 + elems->rsn_ie_len);
+	pos += 2 + elems->rsn_ie_len;
+	/* 6) Timeout Interval IE */
+	os_memcpy(pos, elems->timeout_int - 2, 2 + 5);
+	pos += 2 + 5;
+	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
+	os_memcpy(pos, elems->ftie - 2, 2 + elems->ftie_len);
+	pos += 2;
+	tmp_ftie = (struct rsn_ftie *) pos;
+	os_memset(tmp_ftie->mic, 0, 16);
+	pos += elems->ftie_len;
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", tdls->tpk.kck, 16);
+	ret = omac1_aes_128(tdls->tpk.kck, buf, pos - buf, mic);
+	os_free(buf);
+	if (ret)
+		return -1;
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
+	rx_ftie = (const struct rsn_ftie *) elems->ftie;
+
+	if (os_memcmp(mic, rx_ftie->mic, 16) == 0) {
+		add_note(wt, MSG_DEBUG, "TDLS: Valid MIC");
+		return 0;
+	}
+	add_note(wt, MSG_DEBUG, "TDLS: Invalid MIC");
+	return -1;
+}
+
+
+static void rx_data_tdls_setup_request(struct wlantest *wt, const u8 *bssid,
+				       const u8 *sta_addr, const u8 *dst,
+				       const u8 *src,
+				       const u8 *data, size_t len)
+{
+	struct ieee802_11_elems elems;
+	struct wlantest_tdls *tdls;
+	u8 linkid[3 * ETH_ALEN];
+
+	if (len < 3) {
+		add_note(wt, MSG_INFO, "Too short TDLS Setup Request " MACSTR
+			 " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "TDLS Setup Request " MACSTR " -> "
+		   MACSTR, MAC2STR(src), MAC2STR(dst));
+
+	if (ieee802_11_parse_elems(data + 3, len - 3, &elems, 1) ==
+	    ParseFailed || elems.link_id == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "TDLS Link Identifier: BSSID " MACSTR
+		   " initiator STA " MACSTR " responder STA " MACSTR,
+		   MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
+		   MAC2STR(elems.link_id + 2 * ETH_ALEN));
+	tdls = get_tdls(wt, elems.link_id, 1, bssid);
+	if (tdls) {
+		tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_REQ]++;
+		tdls->dialog_token = data[0];
+		if (elems.ftie && elems.ftie_len >= sizeof(struct rsn_ftie)) {
+			const struct rsn_ftie *f;
+			f = (const struct rsn_ftie *) elems.ftie;
+			os_memcpy(tdls->inonce, f->snonce, WPA_NONCE_LEN);
+		}
+	}
+
+	/* Check whether reverse direction context exists already */
+	os_memcpy(linkid, bssid, ETH_ALEN);
+	os_memcpy(linkid + ETH_ALEN, dst, ETH_ALEN);
+	os_memcpy(linkid + 2 * ETH_ALEN, src, ETH_ALEN);
+	tdls = get_tdls(wt, linkid, 0, bssid);
+	if (tdls)
+		add_note(wt, MSG_INFO, "Reverse direction TDLS context exists");
+}
+
+
+static void rx_data_tdls_setup_response_failure(struct wlantest *wt,
+						const u8 *bssid,
+						const u8 *sta_addr,
+						u8 dialog_token, u16 status)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_tdls *tdls;
+	struct wlantest_sta *sta;
+
+	if (status == WLAN_STATUS_SUCCESS) {
+		add_note(wt, MSG_INFO, "TDLS: Invalid TDLS Setup Response from "
+			 MACSTR, MAC2STR(sta_addr));
+		return;
+	}
+
+	bss = bss_find(wt, bssid);
+	if (!bss)
+		return;
+	sta = sta_find(bss, sta_addr);
+	if (!sta)
+		return;
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if (tdls->resp == sta) {
+			if (dialog_token != tdls->dialog_token) {
+				add_note(wt, MSG_DEBUG, "TDLS: Dialog token "
+					 "mismatch in TDLS Setup Response "
+					 "(failure)");
+				break;
+			}
+			add_note(wt, MSG_DEBUG, "TDLS: Found matching TDLS "
+				 "setup session based on dialog token");
+			tdls->counters[
+				WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL]++;
+			break;
+		}
+	}
+}
+
+
+static void rx_data_tdls_setup_response(struct wlantest *wt, const u8 *bssid,
+					const u8 *sta_addr, const u8 *dst,
+					const u8 *src,
+					const u8 *data, size_t len)
+{
+	u16 status;
+	struct ieee802_11_elems elems;
+	struct wlantest_tdls *tdls;
+
+	if (len < 3) {
+		add_note(wt, MSG_INFO, "Too short TDLS Setup Response " MACSTR
+			 " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
+		return;
+	}
+	status = WPA_GET_LE16(data);
+	wpa_printf(MSG_DEBUG, "TDLS Setup Response " MACSTR " -> "
+		   MACSTR " (status %d)",
+		   MAC2STR(src), MAC2STR(dst), status);
+	if (len < 5 && status == 0) {
+		add_note(wt, MSG_INFO, "Too short TDLS Setup Response " MACSTR
+			 " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
+		return;
+	}
+
+	if (len < 5 ||
+	    ieee802_11_parse_elems(data + 5, len - 5, &elems, 1) ==
+	    ParseFailed || elems.link_id == NULL) {
+		/* Need to match TDLS link based on Dialog Token */
+		rx_data_tdls_setup_response_failure(wt, bssid, sta_addr,
+						    data[2], status);
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "TDLS Link Identifier: BSSID " MACSTR
+		   " initiator STA " MACSTR " responder STA " MACSTR,
+		   MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
+		   MAC2STR(elems.link_id + 2 * ETH_ALEN));
+
+	tdls = get_tdls(wt, elems.link_id, 1, bssid);
+	if (!tdls) {
+		add_note(wt, MSG_INFO, "No match TDLS context found");
+		return;
+	}
+	if (status)
+		tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL]++;
+	else
+		tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_RESP_OK]++;
+
+	if (status != WLAN_STATUS_SUCCESS)
+		return;
+
+	if (elems.ftie && elems.ftie_len >= sizeof(struct rsn_ftie)) {
+		const struct rsn_ftie *f;
+		f = (const struct rsn_ftie *) elems.ftie;
+		if (os_memcmp(tdls->inonce, f->snonce, WPA_NONCE_LEN) != 0) {
+			add_note(wt, MSG_INFO, "Mismatch in TDLS initiator "
+				 "nonce");
+		}
+		os_memcpy(tdls->rnonce, f->anonce, WPA_NONCE_LEN);
+	}
+
+	if (tdls_derive_tpk(tdls, bssid, elems.ftie, elems.ftie_len) < 1)
+		return;
+	if (tdls_verify_mic(wt, tdls, 2, &elems) == 0) {
+		tdls->dialog_token = data[2];
+		add_note(wt, MSG_DEBUG, "TDLS: Dialog Token for the link: %u",
+			 tdls->dialog_token);
+	}
+}
+
+
+static void rx_data_tdls_setup_confirm_failure(struct wlantest *wt,
+					       const u8 *bssid,
+					       const u8 *src,
+					       u8 dialog_token, u16 status)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_tdls *tdls;
+	struct wlantest_sta *sta;
+
+	if (status == WLAN_STATUS_SUCCESS) {
+		add_note(wt, MSG_INFO, "TDLS: Invalid TDLS Setup Confirm from "
+			 MACSTR, MAC2STR(src));
+		return;
+	}
+
+	bss = bss_find(wt, bssid);
+	if (!bss)
+		return;
+	sta = sta_find(bss, src);
+	if (!sta)
+		return;
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if (tdls->init == sta) {
+			if (dialog_token != tdls->dialog_token) {
+				add_note(wt, MSG_DEBUG, "TDLS: Dialog token "
+					 "mismatch in TDLS Setup Confirm "
+					 "(failure)");
+				break;
+			}
+			add_note(wt, MSG_DEBUG, "TDLS: Found matching TDLS "
+				 "setup session based on dialog token");
+			tdls->counters[
+				WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL]++;
+			break;
+		}
+	}
+}
+
+
+static void rx_data_tdls_setup_confirm(struct wlantest *wt, const u8 *bssid,
+				       const u8 *sta_addr, const u8 *dst,
+				       const u8 *src,
+				       const u8 *data, size_t len)
+{
+	u16 status;
+	struct ieee802_11_elems elems;
+	struct wlantest_tdls *tdls;
+	u8 link_id[3 * ETH_ALEN];
+
+	if (len < 3) {
+		add_note(wt, MSG_INFO, "Too short TDLS Setup Confirm " MACSTR
+			 " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
+		return;
+	}
+	status = WPA_GET_LE16(data);
+	wpa_printf(MSG_DEBUG, "TDLS Setup Confirm " MACSTR " -> "
+		   MACSTR " (status %d)",
+		   MAC2STR(src), MAC2STR(dst), status);
+
+	if (ieee802_11_parse_elems(data + 3, len - 3, &elems, 1) ==
+	    ParseFailed || elems.link_id == NULL) {
+		/* Need to match TDLS link based on Dialog Token */
+		rx_data_tdls_setup_confirm_failure(wt, bssid, src,
+						   data[2], status);
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "TDLS Link Identifier: BSSID " MACSTR
+		   " initiator STA " MACSTR " responder STA " MACSTR,
+		   MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
+		   MAC2STR(elems.link_id + 2 * ETH_ALEN));
+
+	tdls = get_tdls(wt, elems.link_id, 1, bssid);
+	if (tdls == NULL)
+		return;
+	if (status)
+		tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL]++;
+	else
+		tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_CONF_OK]++;
+
+	if (status != WLAN_STATUS_SUCCESS)
+		return;
+
+	if (elems.ftie && elems.ftie_len >= sizeof(struct rsn_ftie)) {
+		const struct rsn_ftie *f;
+		f = (const struct rsn_ftie *) elems.ftie;
+		if (os_memcmp(tdls->inonce, f->snonce, WPA_NONCE_LEN) != 0) {
+			add_note(wt, MSG_INFO, "Mismatch in TDLS initiator "
+				 "nonce");
+		}
+		if (os_memcmp(tdls->rnonce, f->anonce, WPA_NONCE_LEN) != 0) {
+			add_note(wt, MSG_INFO, "Mismatch in TDLS responder "
+				 "nonce");
+		}
+	}
+
+	tdls->link_up = 1;
+	if (tdls_derive_tpk(tdls, bssid, elems.ftie, elems.ftie_len) < 1) {
+		if (elems.ftie == NULL)
+			goto remove_reverse;
+		return;
+	}
+	if (tdls_verify_mic(wt, tdls, 3, &elems) == 0) {
+		tdls->dialog_token = data[2];
+		add_note(wt, MSG_DEBUG, "TDLS: Link up - Dialog Token: %u",
+			 tdls->dialog_token);
+	}
+
+remove_reverse:
+	/*
+	 * The TDLS link itself is bidirectional, but there is explicit
+	 * initiator/responder roles. Remove the other direction of the link
+	 * (if it exists) to make sure that the link counters are stored for
+	 * the current TDLS entery.
+	 */
+	os_memcpy(link_id, elems.link_id, ETH_ALEN);
+	os_memcpy(link_id + ETH_ALEN, elems.link_id + 2 * ETH_ALEN, ETH_ALEN);
+	os_memcpy(link_id + 2 * ETH_ALEN, elems.link_id + ETH_ALEN, ETH_ALEN);
+	tdls = get_tdls(wt, link_id, 0, bssid);
+	if (tdls) {
+		add_note(wt, MSG_DEBUG, "TDLS: Remove reverse link entry");
+		tdls_deinit(tdls);
+	}
+}
+
+
+static int tdls_verify_mic_teardown(struct wlantest *wt,
+				    struct wlantest_tdls *tdls, u8 trans_seq,
+				    const u8 *reason_code,
+				    struct ieee802_11_elems *elems)
+{
+	u8 *buf, *pos;
+	int len;
+	u8 mic[16];
+	int ret;
+	const struct rsn_ftie *rx_ftie;
+	struct rsn_ftie *tmp_ftie;
+
+	if (elems->link_id == NULL || elems->ftie == NULL ||
+	    elems->ftie_len < sizeof(struct rsn_ftie))
+		return -1;
+
+	len = 2 + 18 + 2 + 1 + 1 + 2 + elems->ftie_len;
+
+	buf = os_zalloc(len);
+	if (buf == NULL)
+		return -1;
+
+	pos = buf;
+	/* 1) Link Identifier IE */
+	os_memcpy(pos, elems->link_id - 2, 2 + 18);
+	pos += 2 + 18;
+	/* 2) Reason Code */
+	os_memcpy(pos, reason_code, 2);
+	pos += 2;
+	/* 3) Dialog token */
+	*pos++ = tdls->dialog_token;
+	/* 4) Transaction Sequence number */
+	*pos++ = trans_seq;
+	/* 5) FTIE, with the MIC field of the FTIE set to 0 */
+	os_memcpy(pos, elems->ftie - 2, 2 + elems->ftie_len);
+	pos += 2;
+	tmp_ftie = (struct rsn_ftie *) pos;
+	os_memset(tmp_ftie->mic, 0, 16);
+	pos += elems->ftie_len;
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", tdls->tpk.kck, 16);
+	ret = omac1_aes_128(tdls->tpk.kck, buf, pos - buf, mic);
+	os_free(buf);
+	if (ret)
+		return -1;
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
+	rx_ftie = (const struct rsn_ftie *) elems->ftie;
+
+	if (os_memcmp(mic, rx_ftie->mic, 16) == 0) {
+		add_note(wt, MSG_DEBUG, "TDLS: Valid MIC");
+		return 0;
+	}
+	add_note(wt, MSG_DEBUG, "TDLS: Invalid MIC");
+	return -1;
+}
+
+
+static void rx_data_tdls_teardown(struct wlantest *wt, const u8 *bssid,
+				  const u8 *sta_addr, const u8 *dst,
+				  const u8 *src,
+				  const u8 *data, size_t len)
+{
+	u16 reason;
+	struct ieee802_11_elems elems;
+	struct wlantest_tdls *tdls;
+
+	if (len < 2)
+		return;
+	reason = WPA_GET_LE16(data);
+	wpa_printf(MSG_DEBUG, "TDLS Teardown " MACSTR " -> "
+		   MACSTR " (reason %d)",
+		   MAC2STR(src), MAC2STR(dst), reason);
+
+	if (ieee802_11_parse_elems(data + 2, len - 2, &elems, 1) ==
+	    ParseFailed || elems.link_id == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "TDLS Link Identifier: BSSID " MACSTR
+		   " initiator STA " MACSTR " responder STA " MACSTR,
+		   MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
+		   MAC2STR(elems.link_id + 2 * ETH_ALEN));
+
+	tdls = get_tdls(wt, elems.link_id, 1, bssid);
+	if (tdls) {
+		if (tdls->link_up)
+			add_note(wt, MSG_DEBUG, "TDLS: Link down");
+		tdls->link_up = 0;
+		tdls->counters[WLANTEST_TDLS_COUNTER_TEARDOWN]++;
+		tdls_verify_mic_teardown(wt, tdls, 4, data, &elems);
+	}
+}
+
+
+static void rx_data_tdls(struct wlantest *wt, const u8 *bssid,
+			 const u8 *sta_addr, const u8 *dst, const u8 *src,
+			 const u8 *data, size_t len)
+{
+	/* data contains the payload of a TDLS Action frame */
+	if (len < 2 || data[0] != WLAN_ACTION_TDLS) {
+		wpa_hexdump(MSG_DEBUG, "Unrecognized encapsulated TDLS frame",
+			    data, len);
+		return;
+	}
+
+	switch (data[1]) {
+	case WLAN_TDLS_SETUP_REQUEST:
+		rx_data_tdls_setup_request(wt, bssid, sta_addr, dst, src,
+					   data + 2, len - 2);
+		break;
+	case WLAN_TDLS_SETUP_RESPONSE:
+		rx_data_tdls_setup_response(wt, bssid, sta_addr, dst, src,
+					    data + 2, len - 2);
+		break;
+	case WLAN_TDLS_SETUP_CONFIRM:
+		rx_data_tdls_setup_confirm(wt, bssid, sta_addr, dst, src,
+					   data + 2, len - 2);
+		break;
+	case WLAN_TDLS_TEARDOWN:
+		rx_data_tdls_teardown(wt, bssid, sta_addr, dst, src, data + 2,
+				      len - 2);
+		break;
+	case WLAN_TDLS_DISCOVERY_REQUEST:
+		wpa_printf(MSG_DEBUG, "TDLS Discovery Request " MACSTR " -> "
+			   MACSTR, MAC2STR(src), MAC2STR(dst));
+		break;
+	}
+}
+
+
+void rx_data_80211_encap(struct wlantest *wt, const u8 *bssid,
+			 const u8 *sta_addr, const u8 *dst, const u8 *src,
+			 const u8 *data, size_t len)
+{
+	wpa_hexdump(MSG_EXCESSIVE, "802.11 data encap frame", data, len);
+	if (len < 1)
+		return;
+	if (data[0] == 0x02)
+		rx_data_tdls(wt, bssid, sta_addr, dst, src, data + 1, len - 1);
+}
diff --git a/hostap/wlantest/sta.c b/hostap/wlantest/sta.c
new file mode 100644
index 0000000..1268b8a
--- /dev/null
+++ b/hostap/wlantest/sta.c
@@ -0,0 +1,211 @@
+/*
+ * STA list
+ * Copyright (c) 2010-2015, 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 "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wlantest.h"
+
+
+struct wlantest_sta * sta_find(struct wlantest_bss *bss, const u8 *addr)
+{
+	struct wlantest_sta *sta;
+
+	dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
+		if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0)
+			return sta;
+	}
+
+	return NULL;
+}
+
+
+struct wlantest_sta * sta_get(struct wlantest_bss *bss, const u8 *addr)
+{
+	struct wlantest_sta *sta;
+
+	if (addr[0] & 0x01)
+		return NULL; /* Skip group addressed frames */
+
+	sta = sta_find(bss, addr);
+	if (sta)
+		return sta;
+
+	sta = os_zalloc(sizeof(*sta));
+	if (sta == NULL)
+		return NULL;
+	os_memset(sta->seq_ctrl_to_sta, 0xff, sizeof(sta->seq_ctrl_to_sta));
+	os_memset(sta->seq_ctrl_to_ap, 0xff, sizeof(sta->seq_ctrl_to_ap));
+	sta->bss = bss;
+	os_memcpy(sta->addr, addr, ETH_ALEN);
+	dl_list_add(&bss->sta, &sta->list);
+	wpa_printf(MSG_DEBUG, "Discovered new STA " MACSTR " in BSS " MACSTR,
+		   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	return sta;
+}
+
+
+void sta_deinit(struct wlantest_sta *sta)
+{
+	dl_list_del(&sta->list);
+	os_free(sta->assocreq_ies);
+	os_free(sta);
+}
+
+
+void sta_update_assoc(struct wlantest_sta *sta, struct ieee802_11_elems *elems)
+{
+	struct wpa_ie_data data;
+	struct wlantest_bss *bss = sta->bss;
+
+	if (elems->wpa_ie && !bss->wpaie[0]) {
+		wpa_printf(MSG_INFO, "WPA IE included in Association Request "
+			   "frame from " MACSTR " even though BSS does not "
+			   "use WPA - ignore IE",
+			   MAC2STR(sta->addr));
+		elems->wpa_ie = NULL;
+	}
+
+	if (elems->rsn_ie && !bss->rsnie[0]) {
+		wpa_printf(MSG_INFO, "RSN IE included in Association Request "
+			   "frame from " MACSTR " even though BSS does not "
+			   "use RSN - ignore IE",
+			   MAC2STR(sta->addr));
+		elems->rsn_ie = NULL;
+	}
+
+	if (elems->osen && !bss->osenie[0]) {
+		wpa_printf(MSG_INFO, "OSEN IE included in Association Request "
+			   "frame from " MACSTR " even though BSS does not "
+			   "use OSEN - ignore IE",
+			   MAC2STR(sta->addr));
+		elems->osen = NULL;
+	}
+
+	if (elems->wpa_ie && elems->rsn_ie) {
+		wpa_printf(MSG_INFO, "Both WPA IE and RSN IE included in "
+			   "Association Request frame from " MACSTR,
+			   MAC2STR(sta->addr));
+	}
+
+	if (elems->rsn_ie) {
+		wpa_hexdump(MSG_DEBUG, "RSN IE", elems->rsn_ie - 2,
+			    elems->rsn_ie_len + 2);
+		os_memcpy(sta->rsnie, elems->rsn_ie - 2,
+			  elems->rsn_ie_len + 2);
+		if (wpa_parse_wpa_ie_rsn(sta->rsnie, 2 + sta->rsnie[1], &data)
+		    < 0) {
+			wpa_printf(MSG_INFO, "Failed to parse RSN IE from "
+				   MACSTR, MAC2STR(sta->addr));
+		}
+	} else if (elems->wpa_ie) {
+		wpa_hexdump(MSG_DEBUG, "WPA IE", elems->wpa_ie - 2,
+			    elems->wpa_ie_len + 2);
+		os_memcpy(sta->rsnie, elems->wpa_ie - 2,
+			  elems->wpa_ie_len + 2);
+		if (wpa_parse_wpa_ie_wpa(sta->rsnie, 2 + sta->rsnie[1], &data)
+		    < 0) {
+			wpa_printf(MSG_INFO, "Failed to parse WPA IE from "
+				   MACSTR, MAC2STR(sta->addr));
+		}
+	} else if (elems->osen) {
+		wpa_hexdump(MSG_DEBUG, "OSEN IE", elems->osen - 2,
+			    elems->osen_len + 2);
+		os_memcpy(sta->osenie, elems->osen - 2, elems->osen_len + 2);
+		sta->proto = WPA_PROTO_OSEN;
+		sta->pairwise_cipher = WPA_CIPHER_CCMP;
+		sta->key_mgmt = WPA_KEY_MGMT_OSEN;
+		sta->rsn_capab = 0;
+		goto skip_rsn_wpa;
+	} else {
+		sta->rsnie[0] = 0;
+		sta->proto = 0;
+		sta->pairwise_cipher = 0;
+		sta->key_mgmt = 0;
+		sta->rsn_capab = 0;
+		if (sta->assocreq_capab_info & WLAN_CAPABILITY_PRIVACY)
+			sta->pairwise_cipher = WPA_CIPHER_WEP40;
+		goto skip_rsn_wpa;
+	}
+
+	sta->proto = data.proto;
+	sta->pairwise_cipher = data.pairwise_cipher;
+	sta->key_mgmt = data.key_mgmt;
+	sta->rsn_capab = data.capabilities;
+	if (bss->proto && (sta->proto & bss->proto) == 0) {
+		wpa_printf(MSG_INFO, "Mismatch in WPA/WPA2 proto: STA "
+			   MACSTR " 0x%x  BSS " MACSTR " 0x%x",
+			   MAC2STR(sta->addr), sta->proto,
+			   MAC2STR(bss->bssid), bss->proto);
+	}
+	if (bss->pairwise_cipher &&
+	    (sta->pairwise_cipher & bss->pairwise_cipher) == 0) {
+		wpa_printf(MSG_INFO, "Mismatch in pairwise cipher: STA "
+			   MACSTR " 0x%x  BSS " MACSTR " 0x%x",
+			   MAC2STR(sta->addr), sta->pairwise_cipher,
+			   MAC2STR(bss->bssid), bss->pairwise_cipher);
+	}
+	if (sta->proto && data.group_cipher != bss->group_cipher) {
+		wpa_printf(MSG_INFO, "Mismatch in group cipher: STA "
+			   MACSTR " 0x%x != BSS " MACSTR " 0x%x",
+			   MAC2STR(sta->addr), data.group_cipher,
+			   MAC2STR(bss->bssid), bss->group_cipher);
+	}
+	if ((bss->rsn_capab & WPA_CAPABILITY_MFPR) &&
+	    !(sta->rsn_capab & WPA_CAPABILITY_MFPC)) {
+		wpa_printf(MSG_INFO, "STA " MACSTR " tries to associate "
+			   "without MFP to BSS " MACSTR " that advertises "
+			   "MFPR", MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	}
+
+skip_rsn_wpa:
+	wpa_printf(MSG_INFO, "STA " MACSTR
+		   " proto=%s%s%s%s"
+		   "pairwise=%s%s%s%s%s%s%s"
+		   "key_mgmt=%s%s%s%s%s%s%s%s%s%s%s"
+		   "rsn_capab=%s%s%s%s%s",
+		   MAC2STR(sta->addr),
+		   sta->proto == 0 ? "OPEN " : "",
+		   sta->proto & WPA_PROTO_WPA ? "WPA " : "",
+		   sta->proto & WPA_PROTO_RSN ? "WPA2 " : "",
+		   sta->proto & WPA_PROTO_OSEN ? "OSEN " : "",
+		   sta->pairwise_cipher == 0 ? "N/A " : "",
+		   sta->pairwise_cipher & WPA_CIPHER_NONE ? "NONE " : "",
+		   sta->pairwise_cipher & WPA_CIPHER_TKIP ? "TKIP " : "",
+		   sta->pairwise_cipher & WPA_CIPHER_CCMP ? "CCMP " : "",
+		   bss->pairwise_cipher & WPA_CIPHER_CCMP_256 ? "CCMP-256 " :
+		   "",
+		   bss->pairwise_cipher & WPA_CIPHER_GCMP ? "GCMP " : "",
+		   bss->pairwise_cipher & WPA_CIPHER_GCMP_256 ? "GCMP-256 " :
+		   "",
+		   sta->key_mgmt == 0 ? "N/A " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X ? "EAP " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_PSK ? "PSK " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_WPA_NONE ? "WPA-NONE " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X ? "FT-EAP " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_FT_PSK ? "FT-PSK " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256 ?
+		   "EAP-SHA256 " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_PSK_SHA256 ?
+		   "PSK-SHA256 " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_OSEN ? "OSEN " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B ?
+		   "EAP-SUITE-B " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ?
+		   "EAP-SUITE-B-192 " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_PREAUTH ? "PREAUTH " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_NO_PAIRWISE ?
+		   "NO_PAIRWISE " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_MFPR ? "MFPR " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_MFPC ? "MFPC " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_PEERKEY_ENABLED ?
+		   "PEERKEY " : "");
+}
diff --git a/hostap/wlantest/test_vectors.c b/hostap/wlantest/test_vectors.c
new file mode 100644
index 0000000..36f2f5d
--- /dev/null
+++ b/hostap/wlantest/test_vectors.c
@@ -0,0 +1,728 @@
+/*
+ * test_vectors - IEEE 802.11 test vector generator
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "wlantest.h"
+
+
+static void test_vector_tkip(void)
+{
+	u8 tk[] = {
+		0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56,
+		0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+		0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+		0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34
+	};
+	u8 pn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
+	u8 frame[] = {
+		0x08, 0x42, 0x2c, 0x00, 0x02, 0x03, 0x04, 0x05,
+		0x06, 0x08, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xd0, 0x02,
+		/* 0x00, 0x20, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, */
+		0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00,
+		0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
+		0x40, 0x01, 0xa5, 0x55, 0xc0, 0xa8, 0x0a, 0x02,
+		0xc0, 0xa8, 0x0a, 0x01, 0x08, 0x00, 0x3a, 0xb0,
+		0x00, 0x00, 0x00, 0x00, 0xcd, 0x4c, 0x05, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x08, 0x09, 0x0a, 0x0b,
+		0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
+		0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+		0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+		0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+		0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33,
+		0x34, 0x35, 0x36, 0x37,
+		/* 0x68, 0x81, 0xa3, 0xf3, 0xd6, 0x48, 0xd0, 0x3c */
+	};
+	u8 *enc, *plain;
+	size_t enc_len, plain_len;
+
+	wpa_printf(MSG_INFO, "\nIEEE Std 802.11-2012, M.6.3 TKIP test "
+		   "vector\n");
+
+	wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+	wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+	wpa_hexdump(MSG_INFO, "Plaintext MPDU", frame, sizeof(frame));
+
+	enc = tkip_encrypt(tk, frame, sizeof(frame), 24, NULL, pn, 0, &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt TKIP frame");
+		return;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+
+	wpa_debug_level = MSG_INFO;
+	plain = tkip_decrypt(tk, (const struct ieee80211_hdr *) enc,
+			     enc + 24, enc_len - 24, &plain_len);
+	wpa_debug_level = MSG_EXCESSIVE;
+	os_free(enc);
+
+	if (plain == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to decrypt TKIP frame");
+		return;
+	}
+
+	if (plain_len != sizeof(frame) - 24 ||
+	    os_memcmp(plain, frame + 24, plain_len) != 0) {
+		wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+			    plain, plain_len);
+	}
+
+	os_free(plain);
+}
+
+
+static void test_vector_ccmp(void)
+{
+	u8 tk[] = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+		    0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f };
+	u8 pn[] = { 0xB5, 0x03, 0x97, 0x76, 0xE7, 0x0C };
+	u8 frame[] = {
+		0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,
+		0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+		0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33,
+		0xf8, 0xba, 0x1a, 0x55, 0xd0, 0x2f, 0x85, 0xae,
+		0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb,
+		0x7e, 0x78, 0xa0, 0x50
+	};
+	u8 *enc, *plain;
+	size_t enc_len, plain_len;
+	u8 fcs[4];
+
+	wpa_printf(MSG_INFO, "\nIEEE Std 802.11-2012, M.6.4 CCMP test "
+		   "vector\n");
+
+	wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+	wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+	wpa_hexdump(MSG_INFO, "802.11 Header", frame, 24);
+	wpa_hexdump(MSG_INFO, "Plaintext Data", frame + 24, sizeof(frame) - 24);
+
+	enc = ccmp_encrypt(tk, frame, sizeof(frame), 24, NULL, pn, 0, &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+		return;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+	WPA_PUT_LE32(fcs, crc32(enc, enc_len));
+	wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
+
+	wpa_debug_level = MSG_INFO;
+	plain = ccmp_decrypt(tk, (const struct ieee80211_hdr *) enc,
+			     enc + 24, enc_len - 24, &plain_len);
+	wpa_debug_level = MSG_EXCESSIVE;
+	os_free(enc);
+
+	if (plain == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to decrypt CCMP frame");
+		return;
+	}
+
+	if (plain_len != sizeof(frame) - 24 ||
+	    os_memcmp(plain, frame + 24, plain_len) != 0) {
+		wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+			    plain, plain_len);
+	}
+
+	os_free(plain);
+}
+
+
+static void test_vector_bip(void)
+{
+	u8 igtk[] = {
+		0x4e, 0xa9, 0x54, 0x3e, 0x09, 0xcf, 0x2b, 0x1e,
+		0xca, 0x66, 0xff, 0xc5, 0x8b, 0xde, 0xcb, 0xcf
+	};
+	u8 ipn[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	u8 frame[] = {
+		0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+		0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+		0x02, 0x00
+	};
+	u8 *prot;
+	size_t prot_len;
+
+	wpa_printf(MSG_INFO, "\nIEEE Std 802.11-2012, M.9.1 BIP with broadcast "
+		   "Deauthentication frame\n");
+
+	wpa_hexdump(MSG_INFO, "IGTK", igtk, sizeof(igtk));
+	wpa_hexdump(MSG_INFO, "IPN", ipn, sizeof(ipn));
+	wpa_hexdump(MSG_INFO, "Plaintext frame", frame, sizeof(frame));
+
+	prot = bip_protect(igtk, sizeof(igtk), frame, sizeof(frame),
+			   ipn, 4, &prot_len);
+	if (prot == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to protect BIP frame");
+		return;
+	}
+
+	wpa_hexdump(MSG_INFO, "Protected MPDU (without FCS)", prot, prot_len);
+	os_free(prot);
+}
+
+
+static void test_vector_ccmp_mgmt(void)
+{
+	u8 tk[] = { 0x66, 0xed, 0x21, 0x04, 0x2f, 0x9f, 0x26, 0xd7,
+		    0x11, 0x57, 0x06, 0xe4, 0x04, 0x14, 0xcf, 0x2e };
+	u8 pn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
+	u8 frame[] = {
+		0xc0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+		0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00,
+		0x02, 0x00
+	};
+	u8 *enc, *plain;
+	size_t enc_len, plain_len;
+
+	wpa_printf(MSG_INFO, "\nIEEE Std 802.11-2012, M.9.2 CCMP with unicast "
+		   "Deauthentication frame\n");
+
+	wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+	wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+	wpa_hexdump(MSG_INFO, "802.11 Header", frame, 24);
+	wpa_hexdump(MSG_INFO, "Plaintext Data", frame + 24, sizeof(frame) - 24);
+
+	enc = ccmp_encrypt(tk, frame, sizeof(frame), 24, NULL, pn, 0, &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+		return;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+
+	wpa_debug_level = MSG_INFO;
+	plain = ccmp_decrypt(tk, (const struct ieee80211_hdr *) enc,
+			     enc + 24, enc_len - 24, &plain_len);
+	wpa_debug_level = MSG_EXCESSIVE;
+	os_free(enc);
+
+	if (plain == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to decrypt CCMP frame");
+		return;
+	}
+
+	if (plain_len != sizeof(frame) - 24 ||
+	    os_memcmp(plain, frame + 24, plain_len) != 0) {
+		wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+			    plain, plain_len);
+	}
+
+	os_free(plain);
+}
+
+
+struct gcmp_test {
+	u8 tk[16];
+	u8 pn[6];
+	u8 frame[300];
+	size_t hdr_len;
+	size_t payload_len;
+	u8 mic[16];
+	u8 encr[300];
+};
+
+static const struct gcmp_test gcmp_vectors[] =
+{
+	{
+		.tk = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
+		.pn = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
+		.frame = {
+			0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 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, 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, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		},
+		.hdr_len = 24,
+		.payload_len = 256,
+		.mic = {
+			0x80, 0xCB, 0x06, 0x62, 0xEA, 0x71, 0xAB, 0xFD,
+			0x9F, 0x04, 0xC7, 0xF8, 0x72, 0xF5, 0x80, 0x90 },
+		.encr = {
+			0x5F, 0x55, 0x78, 0xC1, 0x8F, 0x13, 0x7A, 0xD2,
+			0x79, 0xBF, 0x3F, 0x2B, 0x24, 0xC7, 0xBD, 0x8F,
+			0x27, 0x7A, 0x1B, 0xE6, 0x77, 0x0D, 0xA1, 0xD9,
+			0x8B, 0x70, 0xC6, 0xD2, 0x8A, 0xE0, 0x1C, 0x55,
+			0x9E, 0xCB, 0xA6, 0xA0, 0x1D, 0xB0, 0x67, 0xC5,
+			0xA2, 0x7E, 0x4D, 0xB0, 0x8C, 0xDA, 0xDC, 0x77,
+			0x52, 0xAD, 0x63, 0x7E, 0xAF, 0x0A, 0x18, 0xED,
+			0x13, 0xFB, 0xAA, 0x14, 0x3B, 0xAF, 0xEF, 0x18,
+			0xF8, 0xFB, 0xCE, 0x4C, 0x65, 0xE8, 0x6B, 0xD0,
+			0x2A, 0x87, 0xB6, 0x01, 0xB7, 0xEA, 0xB9, 0x3F,
+			0x2B, 0xBC, 0x87, 0x4C, 0x8A, 0x71, 0x05, 0x80,
+			0xF5, 0x02, 0x34, 0x1A, 0x6A, 0x53, 0x39, 0x31,
+			0x43, 0xDE, 0x4C, 0x9E, 0xC6, 0xA2, 0x86, 0xF1,
+			0x25, 0x71, 0x83, 0x78, 0xAE, 0xDC, 0x84, 0xEB,
+			0xA2, 0xB3, 0x0F, 0x5C, 0x28, 0xBB, 0x5D, 0x75,
+			0xC6, 0xB0, 0x25, 0x46, 0x6D, 0x06, 0x51, 0xC7,
+			0x22, 0xDC, 0x71, 0x15, 0x1F, 0x21, 0x2D, 0x68,
+			0x87, 0x82, 0x8A, 0x03, 0x82, 0xE9, 0x28, 0x8A,
+			0x7F, 0x43, 0xD5, 0x2B, 0x7D, 0x25, 0x08, 0x61,
+			0x57, 0x64, 0x69, 0x54, 0xBB, 0x43, 0xB5, 0x7E,
+			0xA5, 0x87, 0xA0, 0x25, 0xF4, 0x0C, 0xE7, 0x45,
+			0x11, 0xE4, 0xDD, 0x22, 0x85, 0xB4, 0x0B, 0xA3,
+			0xF3, 0xB9, 0x62, 0x62, 0xCB, 0xC2, 0x8C, 0x6A,
+			0xA7, 0xBE, 0x44, 0x3E, 0x7B, 0x41, 0xE1, 0xEB,
+			0xFF, 0x52, 0x48, 0x57, 0xA6, 0x81, 0x68, 0x97,
+			0x75, 0x01, 0x15, 0xB0, 0x23, 0x1A, 0xB7, 0xC2,
+			0x84, 0x72, 0xC0, 0x6D, 0xD0, 0xB4, 0x9B, 0xE9,
+			0xF3, 0x69, 0xA8, 0xC3, 0x9C, 0xCD, 0x0D, 0xB7,
+			0x98, 0x35, 0x10, 0xE1, 0xAE, 0x8F, 0x05, 0xD7,
+			0x75, 0x45, 0xE0, 0x23, 0x5C, 0xDB, 0xD6, 0x12,
+			0xF3, 0x15, 0x07, 0x54, 0xCE, 0xE5, 0xCE, 0x6A,
+			0x12, 0x25, 0xD9, 0x95, 0x25, 0x02, 0x6F, 0x74
+		}
+	},
+	{
+		.tk = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+			0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f },
+		.pn = { 0x00, 0x89, 0x5F, 0x5F, 0x2B, 0x08 },
+		.frame = {
+			0x88, 0x48, 0x0b, 0x00, 0x0f, 0xd2, 0xe1, 0x28,
+			0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+			0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, 0x80, 0x33,
+			0x03, 0x00,
+
+			0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+			0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+			0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+			0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
+		},
+		.hdr_len = 26,
+		.payload_len = 40,
+		.mic = {
+			0xde, 0xf6, 0x19, 0xc2, 0xa3, 0x74, 0xb6, 0xdf,
+			0x66, 0xff, 0xa5, 0x3b, 0x6c, 0x69, 0xd7, 0x9e },
+		.encr = {
+			0x60, 0xe9, 0x70, 0x0c, 0xc4, 0xd4, 0x0a, 0xc6,
+			0xd2, 0x88, 0xb2, 0x01, 0xc3, 0x8f, 0x5b, 0xf0,
+			0x8b, 0x80, 0x74, 0x42, 0x64, 0x0a, 0x15, 0x96,
+			0xe5, 0xdb, 0xda, 0xd4, 0x1d, 0x1f, 0x36, 0x23,
+			0xf4, 0x5d, 0x7a, 0x12, 0xdb, 0x7a, 0xfb, 0x23
+		}
+	}
+};
+
+
+static int run_gcmp(int idx, const struct gcmp_test *vector)
+{
+	u8 *enc, *plain;
+	size_t enc_len, plain_len;
+	u8 fcs[4];
+	int err = 0;
+
+	wpa_printf(MSG_INFO,
+		   "\nIEEE Std 802.11ad-2012, M.11.1 GCMP test mpdu #%d\n",
+		   idx);
+
+	wpa_hexdump(MSG_INFO, "TK", vector->tk, sizeof(vector->tk));
+	wpa_hexdump(MSG_INFO, "PN", vector->pn, sizeof(vector->pn));
+	wpa_hexdump(MSG_INFO, "802.11 Header", vector->frame, vector->hdr_len);
+	wpa_hexdump(MSG_INFO, "Plaintext Data",
+		    vector->frame + vector->hdr_len,
+		    vector->payload_len);
+
+	enc = gcmp_encrypt(vector->tk, sizeof(vector->tk),
+			   vector->frame,
+			   vector->hdr_len + vector->payload_len,
+			   vector->hdr_len,
+			   vector->hdr_len == 26 ?
+			   vector->frame + vector->hdr_len - 2 : NULL,
+			   vector->pn, 0, &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt GCMP frame");
+		return 1;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+	if (os_memcmp(vector->encr, enc + vector->hdr_len + 8,
+		      vector->payload_len) != 0) {
+		wpa_printf(MSG_ERROR, "GCMP test mpdu #%d enctypted data mismatch",
+			   idx);
+		err++;
+	}
+	if (os_memcmp(vector->mic, enc + enc_len - sizeof(vector->mic),
+		      sizeof(vector->mic)) != 0) {
+		wpa_printf(MSG_ERROR, "GCMP test mpdu #%d MIC mismatch", idx);
+		err++;
+	}
+	WPA_PUT_LE32(fcs, crc32(enc, enc_len));
+	wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
+
+	wpa_debug_level = MSG_INFO;
+	plain = gcmp_decrypt(vector->tk, sizeof(vector->tk),
+			     (const struct ieee80211_hdr *) enc,
+			     enc + vector->hdr_len,
+			     enc_len - vector->hdr_len, &plain_len);
+	wpa_debug_level = MSG_EXCESSIVE;
+	os_free(enc);
+
+	if (plain == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to decrypt GCMP frame");
+		return 1;
+	}
+
+	if (plain_len != vector->payload_len ||
+	    os_memcmp(plain, vector->frame + vector->hdr_len, plain_len) != 0) {
+		wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+			    plain, plain_len);
+		err++;
+	}
+
+	os_free(plain);
+
+	return err;
+}
+
+
+static int test_vector_gcmp(void)
+{
+	int err = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(gcmp_vectors); i++) {
+		if (run_gcmp(i + 1, &gcmp_vectors[i]))
+			err++;
+
+	}
+
+	return err;
+}
+
+
+static int test_vector_gcmp_256(void)
+{
+	u8 tk[] = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+		    0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f,
+		    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
+	u8 pn[] = {
+		0x00, 0x89, 0x5F, 0x5F, 0x2B, 0x08
+	};
+	u8 frame[] = {
+		0x88, 0x48, 0x0b, 0x00, 0x0f, 0xd2, 0xe1, 0x28,
+		0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+		0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, 0x80, 0x33,
+		0x03, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+		0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+		0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+		0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+		0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+		0x26, 0x27
+	};
+	u8 encr[] = {
+		0x88, 0x48, 0x0b, 0x00, 0x0f, 0xd2, 0xe1, 0x28,
+		0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+		0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, 0x80, 0x33,
+		0x03, 0x00, 0x08, 0x2b, 0x00, 0x20, 0x5f, 0x5f,
+		0x89, 0x00, 0x65, 0x83, 0x43, 0xc8, 0xb1, 0x44,
+		0x47, 0xd9, 0x21, 0x1d, 0xef, 0xd4, 0x6a, 0xd8,
+		0x9c, 0x71, 0x0c, 0x6f, 0xc3, 0x33, 0x33, 0x23,
+		0x6e, 0x39, 0x97, 0xb9, 0x17, 0x6a, 0x5a, 0x8b,
+		0xe7, 0x79, 0xb2, 0x12, 0x66, 0x55, 0x5e, 0x70,
+		0xad, 0x79, 0x11, 0x43, 0x16, 0x85, 0x90, 0x95,
+		0x47, 0x3d, 0x5b, 0x1b, 0xd5, 0x96, 0xb3, 0xde,
+		0xa3, 0xbf
+	};
+	u8 *enc, *plain;
+	size_t enc_len, plain_len;
+	u8 fcs[4];
+	int err = 0;
+
+	wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.11.1 GCMP-256 test vector\n");
+
+	wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+	wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+	wpa_hexdump(MSG_INFO, "802.11 Header", frame, 26);
+	wpa_hexdump(MSG_INFO, "Plaintext Data", frame + 26, sizeof(frame) - 26);
+
+	enc = gcmp_encrypt(tk, sizeof(tk), frame, sizeof(frame), 26, frame + 24,
+			   pn, 0, &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt GCMP frame");
+		return 1;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+	if (enc_len != sizeof(encr) || os_memcmp(enc, encr, enc_len) != 0) {
+		wpa_printf(MSG_ERROR, "GCMP-256 test vector mismatch");
+		err++;
+	}
+	WPA_PUT_LE32(fcs, crc32(enc, enc_len));
+	wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
+
+	wpa_debug_level = MSG_INFO;
+	plain = gcmp_decrypt(tk, sizeof(tk), (const struct ieee80211_hdr *) enc,
+			     enc + 26, enc_len - 26, &plain_len);
+	wpa_debug_level = MSG_EXCESSIVE;
+	os_free(enc);
+
+	if (plain == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to decrypt GCMP frame");
+		return 1;
+	}
+
+	if (plain_len != sizeof(frame) - 26 ||
+	    os_memcmp(plain, frame + 26, plain_len) != 0) {
+		wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+			    plain, plain_len);
+		err++;
+	}
+
+	os_free(plain);
+
+	return err;
+}
+
+
+static int test_vector_ccmp_256(void)
+{
+	u8 tk[] = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+		    0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f,
+		    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
+	u8 pn[] = { 0xB5, 0x03, 0x97, 0x76, 0xE7, 0x0C };
+	u8 frame[] = {
+		0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,
+		0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+		0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33,
+		0xf8, 0xba, 0x1a, 0x55, 0xd0, 0x2f, 0x85, 0xae,
+		0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb,
+		0x7e, 0x78, 0xa0, 0x50
+	};
+	u8 encr[] = {
+		0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,
+		0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+		0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33,
+		0x0c, 0xe7, 0x00, 0x20, 0x76, 0x97, 0x03, 0xb5,
+		0x6d, 0x15, 0x5d, 0x88, 0x32, 0x66, 0x82, 0x56,
+		0xd6, 0xa9, 0x2b, 0x78, 0xe1, 0x1d, 0x8e, 0x54,
+		0x49, 0x5d, 0xd1, 0x74, 0x80, 0xaa, 0x56, 0xc9,
+		0x49, 0x2e, 0x88, 0x2b, 0x97, 0x64, 0x2f, 0x80,
+		0xd5, 0x0f, 0xe9, 0x7b
+
+	};
+	u8 *enc, *plain;
+	size_t enc_len, plain_len;
+	u8 fcs[4];
+	int err = 0;
+
+	wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.6.4 CCMP-256 test vector\n");
+
+	wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+	wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+	wpa_hexdump(MSG_INFO, "802.11 Header", frame, 24);
+	wpa_hexdump(MSG_INFO, "Plaintext Data", frame + 24, sizeof(frame) - 24);
+
+	enc = ccmp_256_encrypt(tk, frame, sizeof(frame), 24, NULL, pn, 0,
+			       &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+		return 1;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+	if (enc_len != sizeof(encr) || os_memcmp(enc, encr, enc_len) != 0) {
+		wpa_printf(MSG_ERROR, "CCMP-256 test vector mismatch");
+		err++;
+	}
+	WPA_PUT_LE32(fcs, crc32(enc, enc_len));
+	wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
+
+	wpa_debug_level = MSG_INFO;
+	plain = ccmp_256_decrypt(tk, (const struct ieee80211_hdr *) enc,
+				 enc + 24, enc_len - 24, &plain_len);
+	wpa_debug_level = MSG_EXCESSIVE;
+	os_free(enc);
+
+	if (plain == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to decrypt CCMP-256 frame");
+		return 1;
+	}
+
+	if (plain_len != sizeof(frame) - 24 ||
+	    os_memcmp(plain, frame + 24, plain_len) != 0) {
+		wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+			    plain, plain_len);
+		err++;
+	}
+
+	os_free(plain);
+
+	return err;
+}
+
+
+static int test_vector_bip_gmac_128(void)
+{
+	u8 igtk[] = {
+		0x4e, 0xa9, 0x54, 0x3e, 0x09, 0xcf, 0x2b, 0x1e,
+		0xca, 0x66, 0xff, 0xc5, 0x8b, 0xde, 0xcb, 0xcf
+	};
+	u8 ipn[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	u8 frame[] = {
+		0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+		0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+		0x02, 0x00
+	};
+	u8 res[] = {
+		0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+		0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+		0x02, 0x00, 0x4c, 0x18, 0x04, 0x00, 0x04, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x3e, 0xd8, 0x62, 0xfb,
+		0x0f, 0x33, 0x38, 0xdd, 0x33, 0x86, 0xc8, 0x97,
+		0xe2, 0xed, 0x05, 0x3d
+	};
+	u8 *prot;
+	size_t prot_len;
+	int err = 0;
+
+	wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.9.1 BIP-GMAC-128 with broadcast "
+		   "Deauthentication frame\n");
+
+	wpa_hexdump(MSG_INFO, "IGTK", igtk, sizeof(igtk));
+	wpa_hexdump(MSG_INFO, "IPN", ipn, sizeof(ipn));
+	wpa_hexdump(MSG_INFO, "Plaintext frame", frame, sizeof(frame));
+
+	prot = bip_gmac_protect(igtk, sizeof(igtk), frame, sizeof(frame),
+				ipn, 4, &prot_len);
+	if (prot == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to protect BIP-GMAC-128 frame");
+		return 1;
+	}
+
+	wpa_hexdump(MSG_INFO, "Protected MPDU (without FCS)", prot, prot_len);
+	if (prot_len != sizeof(res) || os_memcmp(res, prot, prot_len) != 0) {
+		wpa_printf(MSG_ERROR, "BIP-GMAC-128 test vector mismatch");
+		err++;
+	}
+	os_free(prot);
+
+	return err;
+}
+
+
+static int test_vector_bip_gmac_256(void)
+{
+	u8 igtk[] = {
+		0x4e, 0xa9, 0x54, 0x3e, 0x09, 0xcf, 0x2b, 0x1e,
+		0xca, 0x66, 0xff, 0xc5, 0x8b, 0xde, 0xcb, 0xcf,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+	};
+	u8 ipn[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	u8 frame[] = {
+		0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+		0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+		0x02, 0x00
+	};
+	u8 res[] = {
+		0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+		0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+		0x02, 0x00, 0x4c, 0x18, 0x04, 0x00, 0x04, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x23, 0xbe, 0x59, 0xdc,
+		0xc7, 0x02, 0x2e, 0xe3, 0x83, 0x62, 0x7e, 0xbb,
+		0x10, 0x17, 0xdd, 0xfc
+	};
+	u8 *prot;
+	size_t prot_len;
+	int err = 0;
+
+	wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.9.1 BIP-GMAC-256 with broadcast Deauthentication frame\n");
+
+	wpa_hexdump(MSG_INFO, "IGTK", igtk, sizeof(igtk));
+	wpa_hexdump(MSG_INFO, "IPN", ipn, sizeof(ipn));
+	wpa_hexdump(MSG_INFO, "Plaintext frame", frame, sizeof(frame));
+
+	prot = bip_gmac_protect(igtk, sizeof(igtk), frame, sizeof(frame),
+				ipn, 4, &prot_len);
+	if (prot == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to protect BIP-GMAC-256 frame");
+		return 1;
+	}
+
+	wpa_hexdump(MSG_INFO, "Protected MPDU (without FCS)", prot, prot_len);
+	if (prot_len != sizeof(res) || os_memcmp(res, prot, prot_len) != 0) {
+		wpa_printf(MSG_ERROR, "BIP-GMAC-128 test vector mismatch");
+		err++;
+	}
+	os_free(prot);
+
+	return err;
+}
+
+
+int main(int argc, char *argv[])
+{
+	int errors = 0;
+
+	wpa_debug_level = MSG_EXCESSIVE;
+	wpa_debug_show_keys = 1;
+
+	if (os_program_init())
+		return -1;
+
+	test_vector_tkip();
+	test_vector_ccmp();
+	test_vector_bip();
+	test_vector_ccmp_mgmt();
+	errors += test_vector_gcmp();
+	errors += test_vector_gcmp_256();
+	errors += test_vector_ccmp_256();
+	errors += test_vector_bip_gmac_128();
+	errors += test_vector_bip_gmac_256();
+
+	if (errors)
+		wpa_printf(MSG_INFO, "One or more test vectors failed");
+	os_program_deinit();
+
+	return errors ? -1 : 0;
+}
diff --git a/hostap/wlantest/tkip.c b/hostap/wlantest/tkip.c
new file mode 100644
index 0000000..ed3d601
--- /dev/null
+++ b/hostap/wlantest/tkip.c
@@ -0,0 +1,428 @@
+/*
+ * Temporal Key Integrity Protocol (CCMP)
+ * Copyright (c) 2010, 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 "common/ieee802_11_defs.h"
+#include "wlantest.h"
+
+
+void wep_crypt(u8 *key, u8 *buf, size_t plen);
+
+
+static inline u16 RotR1(u16 val)
+{
+	return (val >> 1) | (val << 15);
+}
+
+
+static inline u8 Lo8(u16 val)
+{
+	return val & 0xff;
+}
+
+
+static inline u8 Hi8(u16 val)
+{
+	return val >> 8;
+}
+
+
+static inline u16 Lo16(u32 val)
+{
+	return val & 0xffff;
+}
+
+
+static inline u16 Hi16(u32 val)
+{
+	return val >> 16;
+}
+
+
+static inline u16 Mk16(u8 hi, u8 lo)
+{
+	return lo | (((u16) hi) << 8);
+}
+
+
+static inline u16 Mk16_le(u16 *v)
+{
+	return le_to_host16(*v);
+}
+
+
+static const u16 Sbox[256] =
+{
+	0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+	0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+	0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+	0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+	0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+	0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+	0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+	0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+	0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+	0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+	0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+	0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+	0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+	0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+	0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+	0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+	0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+	0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+	0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+	0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+	0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+	0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+	0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+	0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+	0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+	0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+	0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+	0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+	0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+	0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+	0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+	0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+};
+
+
+static inline u16 _S_(u16 v)
+{
+	u16 t = Sbox[Hi8(v)];
+	return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
+}
+
+
+#define PHASE1_LOOP_COUNT 8
+
+static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32)
+{
+	int i, j;
+
+	/* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
+	TTAK[0] = Lo16(IV32);
+	TTAK[1] = Hi16(IV32);
+	TTAK[2] = Mk16(TA[1], TA[0]);
+	TTAK[3] = Mk16(TA[3], TA[2]);
+	TTAK[4] = Mk16(TA[5], TA[4]);
+
+	for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
+		j = 2 * (i & 1);
+		TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
+		TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
+		TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
+		TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
+		TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
+	}
+}
+
+
+static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK,
+			       u16 IV16)
+{
+	u16 PPK[6];
+
+	/* Step 1 - make copy of TTAK and bring in TSC */
+	PPK[0] = TTAK[0];
+	PPK[1] = TTAK[1];
+	PPK[2] = TTAK[2];
+	PPK[3] = TTAK[3];
+	PPK[4] = TTAK[4];
+	PPK[5] = TTAK[4] + IV16;
+
+	/* Step 2 - 96-bit bijective mixing using S-box */
+	PPK[0] += _S_(PPK[5] ^ Mk16_le((u16 *) &TK[0]));
+	PPK[1] += _S_(PPK[0] ^ Mk16_le((u16 *) &TK[2]));
+	PPK[2] += _S_(PPK[1] ^ Mk16_le((u16 *) &TK[4]));
+	PPK[3] += _S_(PPK[2] ^ Mk16_le((u16 *) &TK[6]));
+	PPK[4] += _S_(PPK[3] ^ Mk16_le((u16 *) &TK[8]));
+	PPK[5] += _S_(PPK[4] ^ Mk16_le((u16 *) &TK[10]));
+
+	PPK[0] += RotR1(PPK[5] ^ Mk16_le((u16 *) &TK[12]));
+	PPK[1] += RotR1(PPK[0] ^ Mk16_le((u16 *) &TK[14]));
+	PPK[2] += RotR1(PPK[1]);
+	PPK[3] += RotR1(PPK[2]);
+	PPK[4] += RotR1(PPK[3]);
+	PPK[5] += RotR1(PPK[4]);
+
+	/* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
+	 * WEPSeed[0..2] is transmitted as WEP IV */
+	WEPSeed[0] = Hi8(IV16);
+	WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
+	WEPSeed[2] = Lo8(IV16);
+	WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((u16 *) &TK[0])) >> 1);
+	WPA_PUT_LE16(&WEPSeed[4], PPK[0]);
+	WPA_PUT_LE16(&WEPSeed[6], PPK[1]);
+	WPA_PUT_LE16(&WEPSeed[8], PPK[2]);
+	WPA_PUT_LE16(&WEPSeed[10], PPK[3]);
+	WPA_PUT_LE16(&WEPSeed[12], PPK[4]);
+	WPA_PUT_LE16(&WEPSeed[14], PPK[5]);
+}
+
+
+static inline u32 rotl(u32 val, int bits)
+{
+	return (val << bits) | (val >> (32 - bits));
+}
+
+
+static inline u32 rotr(u32 val, int bits)
+{
+	return (val >> bits) | (val << (32 - bits));
+}
+
+
+static inline u32 xswap(u32 val)
+{
+	return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8);
+}
+
+
+#define michael_block(l, r)	\
+do {				\
+	r ^= rotl(l, 17);	\
+	l += r;			\
+	r ^= xswap(l);		\
+	l += r;			\
+	r ^= rotl(l, 3);	\
+	l += r;			\
+	r ^= rotr(l, 2);	\
+	l += r;			\
+} while (0)
+
+
+static void michael_mic(const u8 *key, const u8 *hdr, const u8 *data,
+			size_t data_len, u8 *mic)
+{
+	u32 l, r;
+	int i, blocks, last;
+
+	l = WPA_GET_LE32(key);
+	r = WPA_GET_LE32(key + 4);
+
+	/* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */
+	l ^= WPA_GET_LE32(hdr);
+	michael_block(l, r);
+	l ^= WPA_GET_LE32(&hdr[4]);
+	michael_block(l, r);
+	l ^= WPA_GET_LE32(&hdr[8]);
+	michael_block(l, r);
+	l ^= WPA_GET_LE32(&hdr[12]);
+	michael_block(l, r);
+
+	/* 32-bit blocks of data */
+	blocks = data_len / 4;
+	last = data_len % 4;
+	for (i = 0; i < blocks; i++) {
+		l ^= WPA_GET_LE32(&data[4 * i]);
+		michael_block(l, r);
+	}
+
+	/* Last block and padding (0x5a, 4..7 x 0) */
+	switch (last) {
+	case 0:
+		l ^= 0x5a;
+		break;
+	case 1:
+		l ^= data[4 * i] | 0x5a00;
+		break;
+	case 2:
+		l ^= data[4 * i] | (data[4 * i + 1] << 8) | 0x5a0000;
+		break;
+	case 3:
+		l ^= data[4 * i] | (data[4 * i + 1] << 8) |
+			(data[4 * i + 2] << 16) | 0x5a000000;
+		break;
+	}
+	michael_block(l, r);
+	/* l ^= 0; */
+	michael_block(l, r);
+
+	WPA_PUT_LE32(mic, l);
+	WPA_PUT_LE32(mic + 4, r);
+}
+
+
+static void michael_mic_hdr(const struct ieee80211_hdr *hdr11, u8 *hdr)
+{
+	int hdrlen = 24;
+	u16 fc = le_to_host16(hdr11->frame_control);
+
+	switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
+	case WLAN_FC_TODS:
+		os_memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+		os_memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+		break;
+	case WLAN_FC_FROMDS:
+		os_memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+		os_memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
+		break;
+	case WLAN_FC_FROMDS | WLAN_FC_TODS:
+		os_memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+		os_memcpy(hdr + ETH_ALEN, hdr11 + 1, ETH_ALEN); /* SA */
+		hdrlen += ETH_ALEN;
+		break;
+	case 0:
+		os_memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+		os_memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+		break;
+	}
+
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+	    (WLAN_FC_GET_STYPE(fc) & 0x08)) {
+		const u8 *qos = ((const u8 *) hdr11) + hdrlen;
+		hdr[12] = qos[0] & 0x0f; /* priority */
+	} else
+		hdr[12] = 0; /* priority */
+
+	hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
+}
+
+
+u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+		  const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+	u16 iv16;
+	u32 iv32;
+	u16 ttak[5];
+	u8 rc4key[16];
+	u8 *plain;
+	size_t plain_len;
+	u32 icv, rx_icv;
+	const u8 *mic_key;
+	u8 michael_hdr[16];
+	u8 mic[8];
+	u16 fc = le_to_host16(hdr->frame_control);
+
+	if (data_len < 8 + 4)
+		return NULL;
+
+	iv16 = (data[0] << 8) | data[2];
+	iv32 = WPA_GET_LE32(&data[4]);
+	wpa_printf(MSG_EXCESSIVE, "TKIP decrypt: iv32=%08x iv16=%04x",
+		   iv32, iv16);
+
+	tkip_mixing_phase1(ttak, tk, hdr->addr2, iv32);
+	wpa_hexdump(MSG_EXCESSIVE, "TKIP TTAK", (u8 *) ttak, sizeof(ttak));
+	tkip_mixing_phase2(rc4key, tk, ttak, iv16);
+	wpa_hexdump(MSG_EXCESSIVE, "TKIP RC4KEY", rc4key, sizeof(rc4key));
+
+	plain_len = data_len - 8;
+	plain = os_malloc(plain_len);
+	if (plain == NULL)
+		return NULL;
+	os_memcpy(plain, data + 8, plain_len);
+	wep_crypt(rc4key, plain, plain_len);
+
+	icv = crc32(plain, plain_len - 4);
+	rx_icv = WPA_GET_LE32(plain + plain_len - 4);
+	if (icv != rx_icv) {
+		wpa_printf(MSG_INFO, "TKIP ICV mismatch in frame from " MACSTR,
+			   MAC2STR(hdr->addr2));
+		wpa_printf(MSG_DEBUG, "TKIP calculated ICV %08x  received ICV "
+			   "%08x", icv, rx_icv);
+		os_free(plain);
+		return NULL;
+	}
+	plain_len -= 4;
+
+	/* TODO: MSDU reassembly */
+
+	if (plain_len < 8) {
+		wpa_printf(MSG_INFO, "TKIP: Not enough room for Michael MIC "
+			   "in a frame from " MACSTR, MAC2STR(hdr->addr2));
+		os_free(plain);
+		return NULL;
+	}
+
+	michael_mic_hdr(hdr, michael_hdr);
+	mic_key = tk + ((fc & WLAN_FC_FROMDS) ? 16 : 24);
+	michael_mic(mic_key, michael_hdr, plain, plain_len - 8, mic);
+	if (os_memcmp(mic, plain + plain_len - 8, 8) != 0) {
+		wpa_printf(MSG_INFO, "TKIP: Michael MIC mismatch in a frame "
+			   "from " MACSTR, MAC2STR(hdr->addr2));
+		wpa_hexdump(MSG_DEBUG, "TKIP: Calculated MIC", mic, 8);
+		wpa_hexdump(MSG_DEBUG, "TKIP: Received MIC",
+			    plain + plain_len - 8, 8);
+		os_free(plain);
+		return NULL;
+	}
+
+	*decrypted_len = plain_len - 8;
+	return plain;
+}
+
+
+void tkip_get_pn(u8 *pn, const u8 *data)
+{
+	pn[0] = data[7]; /* PN5 */
+	pn[1] = data[6]; /* PN4 */
+	pn[2] = data[5]; /* PN3 */
+	pn[3] = data[4]; /* PN2 */
+	pn[4] = data[0]; /* PN1 */
+	pn[5] = data[2]; /* PN0 */
+}
+
+
+u8 * tkip_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
+		  u8 *pn, int keyid, size_t *encrypted_len)
+{
+	u8 michael_hdr[16];
+	u8 mic[8];
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	const u8 *mic_key;
+	u8 *crypt, *pos;
+	u16 iv16;
+	u32 iv32;
+	u16 ttak[5];
+	u8 rc4key[16];
+
+	if (len < sizeof(*hdr) || len < hdrlen)
+		return NULL;
+	hdr = (struct ieee80211_hdr *) frame;
+	fc = le_to_host16(hdr->frame_control);
+
+	michael_mic_hdr(hdr, michael_hdr);
+	mic_key = tk + ((fc & WLAN_FC_FROMDS) ? 16 : 24);
+	michael_mic(mic_key, michael_hdr, frame + hdrlen, len - hdrlen, mic);
+	wpa_hexdump(MSG_EXCESSIVE, "TKIP: MIC", mic, sizeof(mic));
+
+	iv32 = WPA_GET_BE32(pn);
+	iv16 = WPA_GET_BE16(pn + 4);
+	tkip_mixing_phase1(ttak, tk, hdr->addr2, iv32);
+	wpa_hexdump(MSG_EXCESSIVE, "TKIP TTAK", (u8 *) ttak, sizeof(ttak));
+	tkip_mixing_phase2(rc4key, tk, ttak, iv16);
+	wpa_hexdump(MSG_EXCESSIVE, "TKIP RC4KEY", rc4key, sizeof(rc4key));
+
+	crypt = os_malloc(len + 8 + sizeof(mic) + 4);
+	if (crypt == NULL)
+		return NULL;
+	os_memcpy(crypt, frame, hdrlen);
+	pos = crypt + hdrlen;
+	os_memcpy(pos, rc4key, 3);
+	pos += 3;
+	*pos++ = keyid << 6 | BIT(5);
+	*pos++ = pn[3];
+	*pos++ = pn[2];
+	*pos++ = pn[1];
+	*pos++ = pn[0];
+
+	os_memcpy(pos, frame + hdrlen, len - hdrlen);
+	os_memcpy(pos + len - hdrlen, mic, sizeof(mic));
+	WPA_PUT_LE32(pos + len - hdrlen + sizeof(mic),
+		     crc32(pos, len - hdrlen + sizeof(mic)));
+	wep_crypt(rc4key, pos, len - hdrlen + sizeof(mic) + 4);
+
+	*encrypted_len = len + 8 + sizeof(mic) + 4;
+	return crypt;
+}
diff --git a/hostap/wlantest/wep.c b/hostap/wlantest/wep.c
new file mode 100644
index 0000000..c4137f3
--- /dev/null
+++ b/hostap/wlantest/wep.c
@@ -0,0 +1,103 @@
+/*
+ * Wired Equivalent Privacy (WEP)
+ * Copyright (c) 2010, 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 "common/ieee802_11_defs.h"
+#include "wlantest.h"
+
+
+void wep_crypt(u8 *key, u8 *buf, size_t plen)
+{
+	u32 i, j, k;
+	u8 S[256];
+#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+	u8 *pos;
+
+	/* Setup RC4 state */
+	for (i = 0; i < 256; i++)
+		S[i] = i;
+	j = 0;
+	for (i = 0; i < 256; i++) {
+		j = (j + S[i] + key[i & 0x0f]) & 0xff;
+		S_SWAP(i, j);
+	}
+
+	/* Apply RC4 to data */
+	pos = buf;
+	i = j = 0;
+	for (k = 0; k < plen; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		S_SWAP(i, j);
+		*pos ^= S[(S[i] + S[j]) & 0xff];
+		pos++;
+	}
+}
+
+
+static int try_wep(const u8 *key, size_t key_len, const u8 *data,
+		   size_t data_len, u8 *plain)
+{
+	u32 icv, rx_icv;
+	u8 k[16];
+	int i, j;
+
+	for (i = 0, j = 0; i < sizeof(k); i++) {
+		k[i] = key[j];
+		j++;
+		if (j >= key_len)
+			j = 0;
+	}
+
+	os_memcpy(plain, data, data_len);
+	wep_crypt(k, plain, data_len);
+	icv = crc32(plain, data_len - 4);
+	rx_icv = WPA_GET_LE32(plain + data_len - 4);
+	if (icv != rx_icv)
+		return -1;
+
+	return 0;
+}
+
+
+u8 * wep_decrypt(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+		 const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+	u8 *plain;
+	struct wlantest_wep *w;
+	int found = 0;
+	u8 key[16];
+
+	if (dl_list_empty(&wt->wep))
+		return NULL;
+
+	if (data_len < 4 + 4)
+		return NULL;
+	plain = os_malloc(data_len - 4);
+	if (plain == NULL)
+		return NULL;
+
+	dl_list_for_each(w, &wt->wep, struct wlantest_wep, list) {
+		os_memcpy(key, data, 3);
+		os_memcpy(key + 3, w->key, w->key_len);
+		if (try_wep(key, 3 + w->key_len, data + 4, data_len - 4, plain)
+		    == 0) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found) {
+		os_free(plain);
+		return NULL;
+	}
+
+	*decrypted_len = data_len - 4 - 4;
+	return plain;
+}
diff --git a/hostap/wlantest/wired.c b/hostap/wlantest/wired.c
new file mode 100644
index 0000000..77a395f
--- /dev/null
+++ b/hostap/wlantest/wired.c
@@ -0,0 +1,288 @@
+/*
+ * Received frame processing for wired interface
+ * Copyright (c) 2010, 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 <net/ethernet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include "utils/common.h"
+#include "radius/radius.h"
+#include "wlantest.h"
+
+
+static struct wlantest_radius * radius_get(struct wlantest *wt, u32 srv,
+					   u32 cli)
+{
+	struct wlantest_radius *r;
+
+	dl_list_for_each(r, &wt->radius, struct wlantest_radius, list) {
+		if (r->srv == srv && r->cli == cli)
+			return r;
+	}
+
+	r = os_zalloc(sizeof(*r));
+	if (r == NULL)
+		return NULL;
+
+	r->srv = srv;
+	r->cli = cli;
+	dl_list_add(&wt->radius, &r->list);
+
+	return r;
+}
+
+
+static const char * radius_code_string(u8 code)
+{
+	switch (code) {
+	case RADIUS_CODE_ACCESS_REQUEST:
+		return "Access-Request";
+	case RADIUS_CODE_ACCESS_ACCEPT:
+		return "Access-Accept";
+	case RADIUS_CODE_ACCESS_REJECT:
+		return "Access-Reject";
+	case RADIUS_CODE_ACCOUNTING_REQUEST:
+		return "Accounting-Request";
+	case RADIUS_CODE_ACCOUNTING_RESPONSE:
+		return "Accounting-Response";
+	case RADIUS_CODE_ACCESS_CHALLENGE:
+		return "Access-Challenge";
+	case RADIUS_CODE_STATUS_SERVER:
+		return "Status-Server";
+	case RADIUS_CODE_STATUS_CLIENT:
+		return "Status-Client";
+	case RADIUS_CODE_RESERVED:
+		return "Reserved";
+	default:
+		return "?Unknown?";
+	}
+}
+
+
+static void process_radius_access_request(struct wlantest *wt, u32 dst,
+					  u32 src, const u8 *data, size_t len)
+{
+	struct radius_msg *msg;
+	struct wlantest_radius *r;
+
+	msg = radius_msg_parse(data, len);
+	if (msg == NULL) {
+		wpa_printf(MSG_DEBUG, "Failed to parse RADIUS Access-Request");
+		return;
+	}
+
+	r = radius_get(wt, dst, src);
+	if (r) {
+		radius_msg_free(r->last_req);
+		r->last_req = msg;
+		return;
+	}
+	radius_msg_free(msg);
+}
+
+
+static void wlantest_add_pmk(struct wlantest *wt, const u8 *pmk)
+{
+	struct wlantest_pmk *p;
+
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL)
+		return;
+	os_memcpy(p->pmk, pmk, 32);
+	dl_list_add(&wt->pmk, &p->list);
+	wpa_hexdump(MSG_INFO, "Add PMK", pmk, 32);
+}
+
+
+static void process_radius_access_accept(struct wlantest *wt, u32 dst, u32 src,
+					 const u8 *data, size_t len)
+{
+	struct radius_msg *msg;
+	struct wlantest_radius *r;
+	struct radius_ms_mppe_keys *keys;
+	struct wlantest_radius_secret *s;
+
+	r = radius_get(wt, src, dst);
+	if (r == NULL || r->last_req == NULL) {
+		wpa_printf(MSG_DEBUG, "No RADIUS Access-Challenge found for "
+			   "decrypting Access-Accept keys");
+		return;
+	}
+
+	msg = radius_msg_parse(data, len);
+	if (msg == NULL) {
+		wpa_printf(MSG_DEBUG, "Failed to parse RADIUS Access-Accept");
+		return;
+	}
+
+	dl_list_for_each(s, &wt->secret, struct wlantest_radius_secret, list) {
+		int found = 0;
+		keys = radius_msg_get_ms_keys(msg, r->last_req,
+					      (u8 *) s->secret,
+					      os_strlen(s->secret));
+		if (keys && keys->send && keys->recv) {
+			u8 pmk[32];
+			wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key",
+					keys->send, keys->send_len);
+			wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key",
+					keys->recv, keys->recv_len);
+			os_memcpy(pmk, keys->recv,
+				  keys->recv_len > 32 ? 32 : keys->recv_len);
+			if (keys->recv_len < 32) {
+				os_memcpy(pmk + keys->recv_len,
+					  keys->send,
+					  keys->recv_len + keys->send_len > 32
+					  ? 32 : 32 - keys->recv_len);
+			}
+			wlantest_add_pmk(wt, pmk);
+			found = 1;
+		}
+
+		if (keys) {
+			os_free(keys->send);
+			os_free(keys->recv);
+			os_free(keys);
+		}
+
+		if (found)
+			break;
+	}
+
+	radius_msg_free(msg);
+}
+
+
+static void process_radius(struct wlantest *wt, u32 dst, u16 dport, u32 src,
+			   u16 sport, const u8 *data, size_t len)
+{
+	struct in_addr addr;
+	char buf[20];
+	const struct radius_hdr *hdr;
+	u16 rlen;
+
+	if (len < sizeof(*hdr))
+		return;
+	hdr = (const struct radius_hdr *) data;
+	rlen = be_to_host16(hdr->length);
+	if (len < rlen)
+		return;
+	if (len > rlen)
+		len = rlen;
+
+	addr.s_addr = dst;
+	snprintf(buf, sizeof(buf), "%s", inet_ntoa(addr));
+
+	addr.s_addr = src;
+	wpa_printf(MSG_DEBUG, "RADIUS %s:%u -> %s:%u id=%u %s",
+		   inet_ntoa(addr), sport, buf, dport, hdr->identifier,
+		   radius_code_string(hdr->code));
+
+	switch (hdr->code) {
+	case RADIUS_CODE_ACCESS_REQUEST:
+		process_radius_access_request(wt, dst, src, data, len);
+		break;
+	case RADIUS_CODE_ACCESS_ACCEPT:
+		process_radius_access_accept(wt, dst, src, data, len);
+		break;
+	}
+}
+
+
+static void process_udp(struct wlantest *wt, u32 dst, u32 src,
+			const u8 *data, size_t len)
+{
+	const struct udphdr *udp;
+	u16 sport, dport, ulen;
+	const u8 *payload;
+	size_t plen;
+
+	if (len < sizeof(*udp))
+		return;
+	udp = (const struct udphdr *) data;
+	/* TODO: check UDP checksum */
+	sport = be_to_host16(udp->source);
+	dport = be_to_host16(udp->dest);
+	ulen = be_to_host16(udp->len);
+
+	if (ulen > len)
+		return;
+	if (len < ulen)
+		len = ulen;
+
+	payload = (const u8 *) (udp + 1);
+	plen = len - sizeof(*udp);
+
+	if (sport == 1812 || dport == 1812)
+		process_radius(wt, dst, dport, src, sport, payload, plen);
+}
+
+
+static void process_ipv4(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct iphdr *ip;
+	const u8 *payload;
+	size_t plen;
+	u16 frag_off, tot_len;
+
+	if (len < sizeof(*ip))
+		return;
+
+	ip = (const struct iphdr *) data;
+	if (ip->version != 4)
+		return;
+	if (ip->ihl < 5)
+		return;
+
+	/* TODO: check header checksum in ip->check */
+
+	frag_off = be_to_host16(ip->frag_off);
+	if (frag_off & 0x1fff) {
+		wpa_printf(MSG_EXCESSIVE, "IP fragment reassembly not yet "
+			   "supported");
+		return;
+	}
+
+	tot_len = be_to_host16(ip->tot_len);
+	if (tot_len > len)
+		return;
+	if (tot_len < len)
+		len = tot_len;
+
+	payload = data + 4 * ip->ihl;
+	plen = len - 4 * ip->ihl;
+	if (payload + plen > data + len)
+		return;
+
+	switch (ip->protocol) {
+	case IPPROTO_UDP:
+		process_udp(wt, ip->daddr, ip->saddr, payload, plen);
+		break;
+	}
+}
+
+
+void wlantest_process_wired(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ether_header *eth;
+	u16 ethertype;
+
+	wpa_hexdump(MSG_EXCESSIVE, "Process wired frame", data, len);
+
+	if (len < sizeof(*eth))
+		return;
+
+	eth = (const struct ether_header *) data;
+	ethertype = be_to_host16(eth->ether_type);
+
+	switch (ethertype) {
+	case ETHERTYPE_IP:
+		process_ipv4(wt, data + sizeof(*eth), len - sizeof(*eth));
+		break;
+	}
+}
diff --git a/hostap/wlantest/wlantest.c b/hostap/wlantest/wlantest.c
new file mode 100644
index 0000000..ab3b2fc
--- /dev/null
+++ b/hostap/wlantest/wlantest.c
@@ -0,0 +1,469 @@
+/*
+ * wlantest - IEEE 802.11 protocol monitoring and testing tool
+ * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "wlantest.h"
+
+
+static void wlantest_terminate(int sig, void *signal_ctx)
+{
+	eloop_terminate();
+}
+
+
+static void usage(void)
+{
+	printf("wlantest [-cddhqqFt] [-i<ifname>] [-r<pcap file>] "
+	       "[-p<passphrase>]\n"
+	       "         [-I<wired ifname>] [-R<wired pcap file>] "
+	       "[-P<RADIUS shared secret>]\n"
+	       "         [-n<write pcapng file>]\n"
+	       "         [-w<write pcap file>] [-f<MSK/PMK file>]\n"
+	       "         [-L<log file>] [-T<PTK file>]\n");
+}
+
+
+static void passphrase_deinit(struct wlantest_passphrase *p)
+{
+	dl_list_del(&p->list);
+	os_free(p);
+}
+
+
+static void secret_deinit(struct wlantest_radius_secret *r)
+{
+	dl_list_del(&r->list);
+	os_free(r);
+}
+
+
+static void wlantest_init(struct wlantest *wt)
+{
+	int i;
+	os_memset(wt, 0, sizeof(*wt));
+	wt->monitor_sock = -1;
+	wt->ctrl_sock = -1;
+	for (i = 0; i < MAX_CTRL_CONNECTIONS; i++)
+		wt->ctrl_socks[i] = -1;
+	dl_list_init(&wt->passphrase);
+	dl_list_init(&wt->bss);
+	dl_list_init(&wt->secret);
+	dl_list_init(&wt->radius);
+	dl_list_init(&wt->pmk);
+	dl_list_init(&wt->ptk);
+	dl_list_init(&wt->wep);
+}
+
+
+void radius_deinit(struct wlantest_radius *r)
+{
+	dl_list_del(&r->list);
+	os_free(r);
+}
+
+
+static void ptk_deinit(struct wlantest_ptk *ptk)
+{
+	dl_list_del(&ptk->list);
+	os_free(ptk);
+}
+
+
+static void wlantest_deinit(struct wlantest *wt)
+{
+	struct wlantest_passphrase *p, *pn;
+	struct wlantest_radius_secret *s, *sn;
+	struct wlantest_radius *r, *rn;
+	struct wlantest_pmk *pmk, *np;
+	struct wlantest_ptk *ptk, *npt;
+	struct wlantest_wep *wep, *nw;
+
+	if (wt->ctrl_sock >= 0)
+		ctrl_deinit(wt);
+	if (wt->monitor_sock >= 0)
+		monitor_deinit(wt);
+	bss_flush(wt);
+	dl_list_for_each_safe(p, pn, &wt->passphrase,
+			      struct wlantest_passphrase, list)
+		passphrase_deinit(p);
+	dl_list_for_each_safe(s, sn, &wt->secret,
+			      struct wlantest_radius_secret, list)
+		secret_deinit(s);
+	dl_list_for_each_safe(r, rn, &wt->radius, struct wlantest_radius, list)
+		radius_deinit(r);
+	dl_list_for_each_safe(pmk, np, &wt->pmk, struct wlantest_pmk, list)
+		pmk_deinit(pmk);
+	dl_list_for_each_safe(ptk, npt, &wt->ptk, struct wlantest_ptk, list)
+		ptk_deinit(ptk);
+	dl_list_for_each_safe(wep, nw, &wt->wep, struct wlantest_wep, list)
+		os_free(wep);
+	write_pcap_deinit(wt);
+	write_pcapng_deinit(wt);
+	clear_notes(wt);
+	os_free(wt->decrypted);
+	wt->decrypted = NULL;
+}
+
+
+static void add_passphrase(struct wlantest *wt, const char *passphrase)
+{
+	struct wlantest_passphrase *p;
+	size_t len = os_strlen(passphrase);
+
+	if (len < 8 || len > 63)
+		return;
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL)
+		return;
+	os_memcpy(p->passphrase, passphrase, len);
+	dl_list_add(&wt->passphrase, &p->list);
+}
+
+
+static void add_secret(struct wlantest *wt, const char *secret)
+{
+	struct wlantest_radius_secret *s;
+	size_t len = os_strlen(secret);
+
+	if (len >= MAX_RADIUS_SECRET_LEN)
+		return;
+	s = os_zalloc(sizeof(*s));
+	if (s == NULL)
+		return;
+	os_memcpy(s->secret, secret, len);
+	dl_list_add(&wt->secret, &s->list);
+}
+
+
+static int add_pmk_file(struct wlantest *wt, const char *pmk_file)
+{
+	FILE *f;
+	u8 pmk[32];
+	char buf[300], *pos;
+	struct wlantest_pmk *p;
+
+	f = fopen(pmk_file, "r");
+	if (f == NULL) {
+		wpa_printf(MSG_ERROR, "Could not open '%s'", pmk_file);
+		return -1;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		pos = buf;
+		while (*pos && *pos != '\r' && *pos != '\n')
+			pos++;
+		*pos = '\0';
+		if (pos - buf < 2 * 32)
+			continue;
+		if (hexstr2bin(buf, pmk, 32) < 0)
+			continue;
+		p = os_zalloc(sizeof(*p));
+		if (p == NULL)
+			break;
+		os_memcpy(p->pmk, pmk, 32);
+		dl_list_add(&wt->pmk, &p->list);
+		wpa_hexdump(MSG_DEBUG, "Added PMK from file", pmk, 32);
+	}
+
+	fclose(f);
+	return 0;
+}
+
+
+static int add_ptk_file(struct wlantest *wt, const char *ptk_file)
+{
+	FILE *f;
+	u8 ptk[64];
+	size_t ptk_len;
+	char buf[300], *pos;
+	struct wlantest_ptk *p;
+
+	f = fopen(ptk_file, "r");
+	if (f == NULL) {
+		wpa_printf(MSG_ERROR, "Could not open '%s'", ptk_file);
+		return -1;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		pos = buf;
+		while (*pos && *pos != '\r' && *pos != '\n')
+			pos++;
+		*pos = '\0';
+		ptk_len = pos - buf;
+		if (ptk_len & 1)
+			continue;
+		ptk_len /= 2;
+		if (ptk_len != 16 && ptk_len != 32 &&
+		    ptk_len != 48 && ptk_len != 64)
+			continue;
+		if (hexstr2bin(buf, ptk, ptk_len) < 0)
+			continue;
+		p = os_zalloc(sizeof(*p));
+		if (p == NULL)
+			break;
+		if (ptk_len < 48) {
+			os_memcpy(p->ptk.tk, ptk, ptk_len);
+			p->ptk.tk_len = ptk_len;
+			p->ptk_len = 32 + ptk_len;
+		} else {
+			os_memcpy(p->ptk.kck, ptk, 16);
+			p->ptk.kck_len = 16;
+			os_memcpy(p->ptk.kek, ptk + 16, 16);
+			p->ptk.kek_len = 16;
+			os_memcpy(p->ptk.tk, ptk + 32, ptk_len - 32);
+			p->ptk.tk_len = ptk_len - 32;
+			p->ptk_len = ptk_len;
+		}
+		dl_list_add(&wt->ptk, &p->list);
+		wpa_hexdump(MSG_DEBUG, "Added PTK from file", ptk, ptk_len);
+	}
+
+	fclose(f);
+	return 0;
+}
+
+
+int add_wep(struct wlantest *wt, const char *key)
+{
+	struct wlantest_wep *w;
+	size_t len = os_strlen(key);
+
+	if (len != 2 * 5 && len != 2 * 13) {
+		wpa_printf(MSG_INFO, "Invalid WEP key '%s'", key);
+		return -1;
+	}
+	w = os_zalloc(sizeof(*w));
+	if (w == NULL)
+		return -1;
+	if (hexstr2bin(key, w->key, len / 2) < 0) {
+		os_free(w);
+		wpa_printf(MSG_INFO, "Invalid WEP key '%s'", key);
+		return -1;
+	}
+	w->key_len = len / 2;
+	dl_list_add(&wt->wep, &w->list);
+	return 0;
+}
+
+
+void add_note(struct wlantest *wt, int level, const char *fmt, ...)
+{
+	va_list ap;
+	size_t len = 1000;
+	int wlen;
+
+	if (wt->num_notes == MAX_NOTES)
+		return;
+
+	wt->notes[wt->num_notes] = os_malloc(len);
+	if (wt->notes[wt->num_notes] == NULL)
+		return;
+	va_start(ap, fmt);
+	wlen = vsnprintf(wt->notes[wt->num_notes], len, fmt, ap);
+	va_end(ap);
+	if (wlen < 0) {
+		os_free(wt->notes[wt->num_notes]);
+		wt->notes[wt->num_notes] = NULL;
+		return;
+	}
+	if (wlen >= len)
+		wt->notes[wt->num_notes][len - 1] = '\0';
+	wpa_printf(level, "%s", wt->notes[wt->num_notes]);
+	wt->num_notes++;
+}
+
+
+void clear_notes(struct wlantest *wt)
+{
+	size_t i;
+
+	for (i = 0; i < wt->num_notes; i++) {
+		os_free(wt->notes[i]);
+		wt->notes[i] = NULL;
+	}
+
+	wt->num_notes = 0;
+}
+
+
+size_t notes_len(struct wlantest *wt, size_t hdrlen)
+{
+	size_t i;
+	size_t len = wt->num_notes * hdrlen;
+
+	for (i = 0; i < wt->num_notes; i++)
+		len += os_strlen(wt->notes[i]);
+
+	return len;
+}
+
+
+int wlantest_relog(struct wlantest *wt)
+{
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "Re-open log/capture files");
+	if (wpa_debug_reopen_file())
+		ret = -1;
+
+	if (wt->write_file) {
+		write_pcap_deinit(wt);
+		if (write_pcap_init(wt, wt->write_file) < 0)
+			ret = -1;
+	}
+
+	if (wt->pcapng_file) {
+		write_pcapng_deinit(wt);
+		if (write_pcapng_init(wt, wt->pcapng_file) < 0)
+			ret = -1;
+	}
+
+	return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+	int c;
+	const char *read_file = NULL;
+	const char *read_wired_file = NULL;
+	const char *ifname = NULL;
+	const char *ifname_wired = NULL;
+	const char *logfile = NULL;
+	struct wlantest wt;
+	int ctrl_iface = 0;
+
+	wpa_debug_level = MSG_INFO;
+	wpa_debug_show_keys = 1;
+
+	if (os_program_init())
+		return -1;
+
+	wlantest_init(&wt);
+
+	for (;;) {
+		c = getopt(argc, argv, "cdf:Fhi:I:L:n:p:P:qr:R:tT:w:W:");
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'c':
+			ctrl_iface = 1;
+			break;
+		case 'd':
+			if (wpa_debug_level > 0)
+				wpa_debug_level--;
+			break;
+		case 'f':
+			if (add_pmk_file(&wt, optarg) < 0)
+				return -1;
+			break;
+		case 'F':
+			wt.assume_fcs = 1;
+			break;
+		case 'h':
+			usage();
+			return 0;
+		case 'i':
+			ifname = optarg;
+			break;
+		case 'I':
+			ifname_wired = optarg;
+			break;
+		case 'L':
+			logfile = optarg;
+			break;
+		case 'n':
+			wt.pcapng_file = optarg;
+			break;
+		case 'p':
+			add_passphrase(&wt, optarg);
+			break;
+		case 'P':
+			add_secret(&wt, optarg);
+			break;
+		case 'q':
+			wpa_debug_level++;
+			break;
+		case 'r':
+			read_file = optarg;
+			break;
+		case 'R':
+			read_wired_file = optarg;
+			break;
+		case 't':
+			wpa_debug_timestamp = 1;
+			break;
+		case 'T':
+			if (add_ptk_file(&wt, optarg) < 0)
+				return -1;
+			break;
+		case 'w':
+			wt.write_file = optarg;
+			break;
+		case 'W':
+			if (add_wep(&wt, optarg) < 0)
+				return -1;
+			break;
+		default:
+			usage();
+			return -1;
+		}
+	}
+
+	if (ifname == NULL && ifname_wired == NULL &&
+	    read_file == NULL && read_wired_file == NULL) {
+		usage();
+		return 0;
+	}
+
+	if (eloop_init())
+		return -1;
+
+	if (logfile)
+		wpa_debug_open_file(logfile);
+
+	if (wt.write_file && write_pcap_init(&wt, wt.write_file) < 0)
+		return -1;
+
+	if (wt.pcapng_file && write_pcapng_init(&wt, wt.pcapng_file) < 0)
+		return -1;
+
+	if (read_wired_file && read_wired_cap_file(&wt, read_wired_file) < 0)
+		return -1;
+
+	if (read_file && read_cap_file(&wt, read_file) < 0)
+		return -1;
+
+	if (ifname && monitor_init(&wt, ifname) < 0)
+		return -1;
+
+	if (ifname_wired && monitor_init_wired(&wt, ifname_wired) < 0)
+		return -1;
+
+	if (ctrl_iface && ctrl_init(&wt) < 0)
+		return -1;
+
+	eloop_register_signal_terminate(wlantest_terminate, &wt);
+
+	eloop_run();
+
+	wpa_printf(MSG_INFO, "Processed: rx_mgmt=%u rx_ctrl=%u rx_data=%u "
+		   "fcs_error=%u",
+		   wt.rx_mgmt, wt.rx_ctrl, wt.rx_data, wt.fcs_error);
+
+	wlantest_deinit(&wt);
+
+	wpa_debug_close_file();
+	eloop_destroy();
+	os_program_deinit();
+
+	return 0;
+}
diff --git a/hostap/wlantest/wlantest.h b/hostap/wlantest/wlantest.h
new file mode 100644
index 0000000..ced9baa
--- /dev/null
+++ b/hostap/wlantest/wlantest.h
@@ -0,0 +1,311 @@
+/*
+ * wlantest - IEEE 802.11 protocol monitoring and testing tool
+ * Copyright (c) 2010-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WLANTEST_H
+#define WLANTEST_H
+
+#include "utils/list.h"
+#include "common/wpa_common.h"
+#include "wlantest_ctrl.h"
+
+struct ieee802_11_elems;
+struct radius_msg;
+struct ieee80211_hdr;
+struct wlantest_bss;
+
+#define MAX_RADIUS_SECRET_LEN 128
+
+struct wlantest_radius_secret {
+	struct dl_list list;
+	char secret[MAX_RADIUS_SECRET_LEN];
+};
+
+struct wlantest_passphrase {
+	struct dl_list list;
+	char passphrase[64];
+	u8 ssid[32];
+	size_t ssid_len;
+	u8 bssid[ETH_ALEN];
+};
+
+struct wlantest_pmk {
+	struct dl_list list;
+	u8 pmk[32];
+};
+
+struct wlantest_ptk {
+	struct dl_list list;
+	struct wpa_ptk ptk;
+	size_t ptk_len;
+};
+
+struct wlantest_wep {
+	struct dl_list list;
+	size_t key_len;
+	u8 key[13];
+};
+
+struct wlantest_sta {
+	struct dl_list list;
+	struct wlantest_bss *bss;
+	u8 addr[ETH_ALEN];
+	enum {
+		STATE1 /* not authenticated */,
+		STATE2 /* authenticated */,
+		STATE3 /* associated */
+	} state;
+	u16 aid;
+	u8 rsnie[257]; /* WPA/RSN IE */
+	u8 osenie[257]; /* OSEN IE */
+	int proto;
+	int pairwise_cipher;
+	int group_cipher;
+	int key_mgmt;
+	int rsn_capab;
+	u8 anonce[32]; /* ANonce from the previous EAPOL-Key msg 1/4 or 3/4 */
+	u8 snonce[32]; /* SNonce from the previous EAPOL-Key msg 2/4 */
+	struct wpa_ptk ptk; /* Derived PTK */
+	size_t tk_len;
+	int ptk_set;
+	struct wpa_ptk tptk; /* Derived PTK during rekeying */
+	int tptk_set;
+	u8 rsc_tods[16 + 1][6];
+	u8 rsc_fromds[16 + 1][6];
+	u8 ap_sa_query_tr[2];
+	u8 sta_sa_query_tr[2];
+	u32 counters[NUM_WLANTEST_STA_COUNTER];
+	u16 assocreq_capab_info;
+	u16 assocreq_listen_int;
+	u8 *assocreq_ies;
+	size_t assocreq_ies_len;
+
+	/* Last ICMP Echo request information */
+	u32 icmp_echo_req_src;
+	u32 icmp_echo_req_dst;
+	u16 icmp_echo_req_id;
+	u16 icmp_echo_req_seq;
+
+	le16 seq_ctrl_to_sta[17];
+	le16 seq_ctrl_to_ap[17];
+
+	int pwrmgt;
+	int pspoll;
+
+	u8 gtk[32];
+	size_t gtk_len;
+	int gtk_idx;
+
+	u32 tx_tid[16 + 1];
+	u32 rx_tid[16 + 1];
+};
+
+struct wlantest_tdls {
+	struct dl_list list;
+	struct wlantest_sta *init;
+	struct wlantest_sta *resp;
+	struct tpk {
+		u8 kck[16];
+		u8 tk[16];
+	} tpk;
+	int link_up;
+	u8 dialog_token;
+	u8 rsc_init[16 + 1][6];
+	u8 rsc_resp[16 + 1][6];
+	u32 counters[NUM_WLANTEST_TDLS_COUNTER];
+	u8 inonce[32];
+	u8 rnonce[32];
+};
+
+struct wlantest_bss {
+	struct dl_list list;
+	u8 bssid[ETH_ALEN];
+	u16 capab_info;
+	u16 prev_capab_info;
+	u8 ssid[32];
+	size_t ssid_len;
+	int proberesp_seen;
+	int parse_error_reported;
+	u8 wpaie[257];
+	u8 rsnie[257];
+	u8 osenie[257];
+	int proto;
+	int pairwise_cipher;
+	int group_cipher;
+	int mgmt_group_cipher;
+	int key_mgmt;
+	int rsn_capab;
+	struct dl_list sta; /* struct wlantest_sta */
+	struct dl_list pmk; /* struct wlantest_pmk */
+	u8 gtk[4][32];
+	size_t gtk_len[4];
+	int gtk_idx;
+	u8 rsc[4][6];
+	u8 igtk[6][32];
+	size_t igtk_len[6];
+	int igtk_idx;
+	u8 ipn[6][6];
+	u32 counters[NUM_WLANTEST_BSS_COUNTER];
+	struct dl_list tdls; /* struct wlantest_tdls */
+	u8 mdid[2];
+	u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
+	size_t r0kh_id_len;
+	u8 r1kh_id[FT_R1KH_ID_LEN];
+};
+
+struct wlantest_radius {
+	struct dl_list list;
+	u32 srv;
+	u32 cli;
+	struct radius_msg *last_req;
+};
+
+
+#define MAX_CTRL_CONNECTIONS 10
+#define MAX_NOTES 10
+
+struct wlantest {
+	int monitor_sock;
+	int monitor_wired;
+
+	int ctrl_sock;
+	int ctrl_socks[MAX_CTRL_CONNECTIONS];
+
+	struct dl_list passphrase; /* struct wlantest_passphrase */
+	struct dl_list bss; /* struct wlantest_bss */
+	struct dl_list secret; /* struct wlantest_radius_secret */
+	struct dl_list radius; /* struct wlantest_radius */
+	struct dl_list pmk; /* struct wlantest_pmk */
+	struct dl_list ptk; /* struct wlantest_ptk */
+	struct dl_list wep; /* struct wlantest_wep */
+
+	unsigned int rx_mgmt;
+	unsigned int rx_ctrl;
+	unsigned int rx_data;
+	unsigned int fcs_error;
+
+	void *write_pcap; /* pcap_t* */
+	void *write_pcap_dumper; /* pcpa_dumper_t */
+	struct timeval write_pcap_time;
+	u8 *decrypted;
+	size_t decrypted_len;
+	FILE *pcapng;
+	u32 write_pcapng_time_high;
+	u32 write_pcapng_time_low;
+
+	u8 last_hdr[30];
+	size_t last_len;
+	int last_mgmt_valid;
+
+	unsigned int assume_fcs:1;
+
+	char *notes[MAX_NOTES];
+	size_t num_notes;
+
+	const char *write_file;
+	const char *pcapng_file;
+};
+
+void add_note(struct wlantest *wt, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+void clear_notes(struct wlantest *wt);
+size_t notes_len(struct wlantest *wt, size_t hdrlen);
+
+int add_wep(struct wlantest *wt, const char *key);
+int read_cap_file(struct wlantest *wt, const char *fname);
+int read_wired_cap_file(struct wlantest *wt, const char *fname);
+
+int write_pcap_init(struct wlantest *wt, const char *fname);
+void write_pcap_deinit(struct wlantest *wt);
+void write_pcap_captured(struct wlantest *wt, const u8 *buf, size_t len);
+void write_pcap_decrypted(struct wlantest *wt, const u8 *buf1, size_t len1,
+			  const u8 *buf2, size_t len2);
+
+int write_pcapng_init(struct wlantest *wt, const char *fname);
+void write_pcapng_deinit(struct wlantest *wt);
+struct pcap_pkthdr;
+void write_pcapng_write_read(struct wlantest *wt, int dlt,
+			     struct pcap_pkthdr *hdr, const u8 *data);
+void write_pcapng_captured(struct wlantest *wt, const u8 *buf, size_t len);
+
+void wlantest_process(struct wlantest *wt, const u8 *data, size_t len);
+void wlantest_process_prism(struct wlantest *wt, const u8 *data, size_t len);
+void wlantest_process_80211(struct wlantest *wt, const u8 *data, size_t len);
+void wlantest_process_wired(struct wlantest *wt, const u8 *data, size_t len);
+u32 crc32(const u8 *frame, size_t frame_len);
+int monitor_init(struct wlantest *wt, const char *ifname);
+int monitor_init_wired(struct wlantest *wt, const char *ifname);
+void monitor_deinit(struct wlantest *wt);
+void rx_mgmt(struct wlantest *wt, const u8 *data, size_t len);
+void rx_mgmt_ack(struct wlantest *wt, const struct ieee80211_hdr *hdr);
+void rx_data(struct wlantest *wt, const u8 *data, size_t len);
+void rx_data_eapol(struct wlantest *wt, const u8 *dst, const u8 *src,
+		   const u8 *data, size_t len, int prot);
+void rx_data_ip(struct wlantest *wt, const u8 *bssid, const u8 *sta_addr,
+		const u8 *dst, const u8 *src, const u8 *data, size_t len,
+		const u8 *peer_addr);
+void rx_data_80211_encap(struct wlantest *wt, const u8 *bssid,
+			 const u8 *sta_addr, const u8 *dst, const u8 *src,
+			 const u8 *data, size_t len);
+
+struct wlantest_bss * bss_find(struct wlantest *wt, const u8 *bssid);
+struct wlantest_bss * bss_get(struct wlantest *wt, const u8 *bssid);
+void bss_deinit(struct wlantest_bss *bss);
+void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
+		struct ieee802_11_elems *elems);
+void bss_flush(struct wlantest *wt);
+int bss_add_pmk_from_passphrase(struct wlantest_bss *bss,
+				const char *passphrase);
+void pmk_deinit(struct wlantest_pmk *pmk);
+void tdls_deinit(struct wlantest_tdls *tdls);
+
+struct wlantest_sta * sta_find(struct wlantest_bss *bss, const u8 *addr);
+struct wlantest_sta * sta_get(struct wlantest_bss *bss, const u8 *addr);
+void sta_deinit(struct wlantest_sta *sta);
+void sta_update_assoc(struct wlantest_sta *sta,
+		      struct ieee802_11_elems *elems);
+
+u8 * ccmp_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+		  const u8 *data, size_t data_len, size_t *decrypted_len);
+u8 * ccmp_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
+		  u8 *pn, int keyid, size_t *encrypted_len);
+void ccmp_get_pn(u8 *pn, const u8 *data);
+u8 * ccmp_256_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+		      const u8 *data, size_t data_len, size_t *decrypted_len);
+u8 * ccmp_256_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen,
+		      u8 *qos, u8 *pn, int keyid, size_t *encrypted_len);
+
+u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+		  const u8 *data, size_t data_len, size_t *decrypted_len);
+u8 * tkip_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
+		  u8 *pn, int keyid, size_t *encrypted_len);
+void tkip_get_pn(u8 *pn, const u8 *data);
+
+u8 * wep_decrypt(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+		 const u8 *data, size_t data_len, size_t *decrypted_len);
+
+u8 * bip_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
+		 u8 *ipn, int keyid, size_t *prot_len);
+u8 * bip_gmac_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
+		      u8 *ipn, int keyid, size_t *prot_len);
+
+u8 * gcmp_decrypt(const u8 *tk, size_t tk_len, const struct ieee80211_hdr *hdr,
+		  const u8 *data, size_t data_len, size_t *decrypted_len);
+u8 * gcmp_encrypt(const u8 *tk, size_t tk_len, const u8 *frame, size_t len,
+		  size_t hdrlen, const u8 *qos,
+		  const u8 *pn, int keyid, size_t *encrypted_len);
+
+int ctrl_init(struct wlantest *wt);
+void ctrl_deinit(struct wlantest *wt);
+
+int wlantest_inject(struct wlantest *wt, struct wlantest_bss *bss,
+		    struct wlantest_sta *sta, u8 *frame, size_t len,
+		    enum wlantest_inject_protection prot);
+
+int wlantest_relog(struct wlantest *wt);
+
+#endif /* WLANTEST_H */
diff --git a/hostap/wlantest/wlantest_cli.c b/hostap/wlantest/wlantest_cli.c
new file mode 100644
index 0000000..ad5a48d
--- /dev/null
+++ b/hostap/wlantest/wlantest_cli.c
@@ -0,0 +1,1865 @@
+/*
+ * wlantest controller
+ * Copyright (c) 2010-2013, 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 <sys/un.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/edit.h"
+#include "wlantest_ctrl.h"
+
+
+static int get_cmd_arg_num(const char *str, int pos)
+{
+	int arg = 0, i;
+
+	for (i = 0; i <= pos; i++) {
+		if (str[i] != ' ') {
+			arg++;
+			while (i <= pos && str[i] != ' ')
+				i++;
+		}
+	}
+
+	if (arg > 0)
+		arg--;
+	return arg;
+}
+
+
+static int get_prev_arg_pos(const char *str, int pos)
+{
+	while (pos > 0 && str[pos - 1] != ' ')
+		pos--;
+	while (pos > 0 && str[pos - 1] == ' ')
+		pos--;
+	while (pos > 0 && str[pos - 1] != ' ')
+		pos--;
+	return pos;
+}
+
+
+static u8 * attr_get(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr,
+		     size_t *len)
+{
+	u8 *pos = buf;
+
+	while (pos + 8 <= buf + buflen) {
+		enum wlantest_ctrl_attr a;
+		size_t alen;
+		a = WPA_GET_BE32(pos);
+		pos += 4;
+		alen = WPA_GET_BE32(pos);
+		pos += 4;
+		if (pos + alen > buf + buflen) {
+			printf("Invalid control message attribute\n");
+			return NULL;
+		}
+		if (a == attr) {
+			*len = alen;
+			return pos;
+		}
+		pos += alen;
+	}
+
+	return NULL;
+}
+
+
+static u8 * attr_hdr_add(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+			 size_t len)
+{
+	if (pos == NULL || end - pos < 8 + len)
+		return NULL;
+	WPA_PUT_BE32(pos, attr);
+	pos += 4;
+	WPA_PUT_BE32(pos, len);
+	pos += 4;
+	return pos;
+}
+
+
+static u8 * attr_add_str(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+			 const char *str)
+{
+	size_t len = os_strlen(str);
+
+	if (pos == NULL || end - pos < 8 + len)
+		return NULL;
+	WPA_PUT_BE32(pos, attr);
+	pos += 4;
+	WPA_PUT_BE32(pos, len);
+	pos += 4;
+	os_memcpy(pos, str, len);
+	pos += len;
+	return pos;
+}
+
+
+static u8 * attr_add_be32(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+			  u32 val)
+{
+	if (pos == NULL || end - pos < 12)
+		return NULL;
+	WPA_PUT_BE32(pos, attr);
+	pos += 4;
+	WPA_PUT_BE32(pos, 4);
+	pos += 4;
+	WPA_PUT_BE32(pos, val);
+	pos += 4;
+	return pos;
+}
+
+
+static int cmd_send_and_recv(int s, const u8 *cmd, size_t cmd_len,
+			     u8 *resp, size_t max_resp_len)
+{
+	int res;
+	enum wlantest_ctrl_cmd cmd_resp;
+
+	if (send(s, cmd, cmd_len, 0) < 0)
+		return -1;
+	res = recv(s, resp, max_resp_len, 0);
+	if (res < 4)
+		return -1;
+
+	cmd_resp = WPA_GET_BE32(resp);
+	if (cmd_resp == WLANTEST_CTRL_SUCCESS)
+		return res;
+
+	if (cmd_resp == WLANTEST_CTRL_UNKNOWN_CMD)
+		printf("Unknown command\n");
+	else if (cmd_resp == WLANTEST_CTRL_INVALID_CMD)
+		printf("Invalid command\n");
+
+	return -1;
+}
+
+
+static int cmd_simple(int s, enum wlantest_ctrl_cmd cmd)
+{
+	u8 buf[4];
+	int res;
+	WPA_PUT_BE32(buf, cmd);
+	res = cmd_send_and_recv(s, buf, sizeof(buf), buf, sizeof(buf));
+	return res < 0 ? -1 : 0;
+}
+
+
+static char ** get_bssid_list(int s)
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[4];
+	u8 *bssid;
+	size_t len;
+	int rlen, i;
+	char **res;
+
+	WPA_PUT_BE32(buf, WLANTEST_CTRL_LIST_BSS);
+	rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp));
+	if (rlen < 0)
+		return NULL;
+
+	bssid = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_BSSID, &len);
+	if (bssid == NULL)
+		return NULL;
+
+	res = os_calloc(len / ETH_ALEN + 1, sizeof(char *));
+	if (res == NULL)
+		return NULL;
+	for (i = 0; i < len / ETH_ALEN; i++) {
+		res[i] = os_zalloc(18);
+		if (res[i] == NULL)
+			break;
+		os_snprintf(res[i], 18, MACSTR, MAC2STR(bssid + ETH_ALEN * i));
+	}
+
+	return res;
+}
+
+
+static char ** get_sta_list(int s, const u8 *bssid, int add_bcast)
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos, *end;
+	u8 *addr;
+	size_t len;
+	int rlen, i;
+	char **res;
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_LIST_STA);
+	pos += 4;
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	os_memcpy(pos, bssid, ETH_ALEN);
+	pos += ETH_ALEN;
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return NULL;
+
+	addr = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_STA_ADDR, &len);
+	if (addr == NULL)
+		return NULL;
+
+	res = os_calloc(len / ETH_ALEN + 1 + add_bcast, sizeof(char *));
+	if (res == NULL)
+		return NULL;
+	for (i = 0; i < len / ETH_ALEN; i++) {
+		res[i] = os_zalloc(18);
+		if (res[i] == NULL)
+			break;
+		os_snprintf(res[i], 18, MACSTR, MAC2STR(addr + ETH_ALEN * i));
+	}
+	if (add_bcast)
+		res[i] = os_strdup("ff:ff:ff:ff:ff:ff");
+
+	return res;
+}
+
+
+static int cmd_ping(int s, int argc, char *argv[])
+{
+	int res = cmd_simple(s, WLANTEST_CTRL_PING);
+	if (res == 0)
+		printf("PONG\n");
+	return res == 0;
+}
+
+
+static int cmd_terminate(int s, int argc, char *argv[])
+{
+	return cmd_simple(s, WLANTEST_CTRL_TERMINATE);
+}
+
+
+static int cmd_list_bss(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[4];
+	u8 *bssid;
+	size_t len;
+	int rlen, i;
+
+	WPA_PUT_BE32(buf, WLANTEST_CTRL_LIST_BSS);
+	rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	bssid = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_BSSID, &len);
+	if (bssid == NULL)
+		return -1;
+
+	for (i = 0; i < len / ETH_ALEN; i++)
+		printf(MACSTR " ", MAC2STR(bssid + ETH_ALEN * i));
+	printf("\n");
+
+	return 0;
+}
+
+
+static int cmd_list_sta(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos;
+	u8 *addr;
+	size_t len;
+	int rlen, i;
+
+	if (argc < 1) {
+		printf("list_sta needs one argument: BSSID\n");
+		return -1;
+	}
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_LIST_STA);
+	pos += 4;
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[0], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[0]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	addr = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_STA_ADDR, &len);
+	if (addr == NULL)
+		return -1;
+
+	for (i = 0; i < len / ETH_ALEN; i++)
+		printf(MACSTR " ", MAC2STR(addr + ETH_ALEN * i));
+	printf("\n");
+
+	return 0;
+}
+
+
+static char ** complete_list_sta(int s, const char *str, int pos)
+{
+	if (get_cmd_arg_num(str, pos) == 1)
+		return get_bssid_list(s);
+	return NULL;
+}
+
+
+static int cmd_flush(int s, int argc, char *argv[])
+{
+	return cmd_simple(s, WLANTEST_CTRL_FLUSH);
+}
+
+
+static int cmd_clear_sta_counters(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos;
+	int rlen;
+
+	if (argc < 2) {
+		printf("clear_sta_counters needs two arguments: BSSID and "
+		       "STA address\n");
+		return -1;
+	}
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_STA_COUNTERS);
+	pos += 4;
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[0], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[0]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid STA address '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	printf("OK\n");
+	return 0;
+}
+
+
+static char ** complete_clear_sta_counters(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		res = get_bssid_list(s);
+		break;
+	case 2:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 0);
+		break;
+	}
+
+	return res;
+}
+
+
+static int cmd_clear_bss_counters(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos;
+	int rlen;
+
+	if (argc < 1) {
+		printf("clear_bss_counters needs one argument: BSSID\n");
+		return -1;
+	}
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_BSS_COUNTERS);
+	pos += 4;
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[0], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[0]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	printf("OK\n");
+	return 0;
+}
+
+
+static char ** complete_clear_bss_counters(int s, const char *str, int pos)
+{
+	if (get_cmd_arg_num(str, pos) == 1)
+		return get_bssid_list(s);
+	return NULL;
+}
+
+
+static int cmd_clear_tdls_counters(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos;
+	int rlen;
+
+	if (argc < 3) {
+		printf("clear_tdls_counters needs three arguments: BSSID, "
+		       "STA1 address, STA2 address\n");
+		return -1;
+	}
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_TDLS_COUNTERS);
+	pos += 4;
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[0], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[0]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid STA1 address '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_STA2_ADDR);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[2], pos) < 0) {
+		printf("Invalid STA2 address '%s'\n", argv[2]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	printf("OK\n");
+	return 0;
+}
+
+
+static char ** complete_clear_tdls_counters(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		res = get_bssid_list(s);
+		break;
+	case 2:
+	case 3:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 0);
+		break;
+	}
+
+	return res;
+}
+
+
+struct sta_counters {
+	const char *name;
+	enum wlantest_sta_counter num;
+};
+
+static const struct sta_counters sta_counters[] = {
+	{ "auth_tx", WLANTEST_STA_COUNTER_AUTH_TX },
+	{ "auth_rx", WLANTEST_STA_COUNTER_AUTH_RX },
+	{ "assocreq_tx", WLANTEST_STA_COUNTER_ASSOCREQ_TX },
+	{ "reassocreq_tx", WLANTEST_STA_COUNTER_REASSOCREQ_TX },
+	{ "ptk_learned", WLANTEST_STA_COUNTER_PTK_LEARNED },
+	{ "valid_deauth_tx", WLANTEST_STA_COUNTER_VALID_DEAUTH_TX },
+	{ "valid_deauth_rx", WLANTEST_STA_COUNTER_VALID_DEAUTH_RX },
+	{ "invalid_deauth_tx", WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX },
+	{ "invalid_deauth_rx", WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX },
+	{ "valid_disassoc_tx", WLANTEST_STA_COUNTER_VALID_DISASSOC_TX },
+	{ "valid_disassoc_rx", WLANTEST_STA_COUNTER_VALID_DISASSOC_RX },
+	{ "invalid_disassoc_tx", WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX },
+	{ "invalid_disassoc_rx", WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX },
+	{ "valid_saqueryreq_tx", WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX },
+	{ "valid_saqueryreq_rx", WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX },
+	{ "invalid_saqueryreq_tx",
+	  WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX },
+	{ "invalid_saqueryreq_rx",
+	  WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX },
+	{ "valid_saqueryresp_tx", WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX },
+	{ "valid_saqueryresp_rx", WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX },
+	{ "invalid_saqueryresp_tx",
+	  WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX },
+	{ "invalid_saqueryresp_rx",
+	  WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX },
+	{ "ping_ok", WLANTEST_STA_COUNTER_PING_OK },
+	{ "assocresp_comeback", WLANTEST_STA_COUNTER_ASSOCRESP_COMEBACK },
+	{ "reassocresp_comeback", WLANTEST_STA_COUNTER_REASSOCRESP_COMEBACK },
+	{ "ping_ok_first_assoc", WLANTEST_STA_COUNTER_PING_OK_FIRST_ASSOC },
+	{ "valid_deauth_rx_ack", WLANTEST_STA_COUNTER_VALID_DEAUTH_RX_ACK },
+	{ "valid_disassoc_rx_ack",
+	  WLANTEST_STA_COUNTER_VALID_DISASSOC_RX_ACK },
+	{ "invalid_deauth_rx_ack",
+	  WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX_ACK },
+	{ "invalid_disassoc_rx_ack",
+	  WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX_ACK },
+	{ "deauth_rx_asleep", WLANTEST_STA_COUNTER_DEAUTH_RX_ASLEEP },
+	{ "deauth_rx_awake", WLANTEST_STA_COUNTER_DEAUTH_RX_AWAKE },
+	{ "disassoc_rx_asleep", WLANTEST_STA_COUNTER_DISASSOC_RX_ASLEEP },
+	{ "disassoc_rx_awake", WLANTEST_STA_COUNTER_DISASSOC_RX_AWAKE },
+	{ "prot_data_tx", WLANTEST_STA_COUNTER_PROT_DATA_TX },
+	{ "deauth_rx_rc6", WLANTEST_STA_COUNTER_DEAUTH_RX_RC6 },
+	{ "deauth_rx_rc7", WLANTEST_STA_COUNTER_DEAUTH_RX_RC7 },
+	{ "disassoc_rx_rc6", WLANTEST_STA_COUNTER_DISASSOC_RX_RC6 },
+	{ "disassoc_rx_rc7", WLANTEST_STA_COUNTER_DISASSOC_RX_RC7 },
+	{ NULL, 0 }
+};
+
+static int cmd_get_sta_counter(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen, i;
+	size_t len;
+
+	if (argc != 3) {
+		printf("get_sta_counter needs at three arguments: "
+		       "counter name, BSSID, and STA address\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_STA_COUNTER);
+	pos += 4;
+
+	for (i = 0; sta_counters[i].name; i++) {
+		if (os_strcasecmp(sta_counters[i].name, argv[0]) == 0)
+			break;
+	}
+	if (sta_counters[i].name == NULL) {
+		printf("Unknown STA counter '%s'\n", argv[0]);
+		printf("Counters:");
+		for (i = 0; sta_counters[i].name; i++)
+			printf(" %s", sta_counters[i].name);
+		printf("\n");
+		return -1;
+	}
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_STA_COUNTER,
+			    sta_counters[i].num);
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[2], pos) < 0) {
+		printf("Invalid STA address '%s'\n", argv[2]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+	if (pos == NULL || len != 4)
+		return -1;
+	printf("%u\n", WPA_GET_BE32(pos));
+	return 0;
+}
+
+
+static char ** complete_get_sta_counter(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	int i, count;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		/* counter list */
+		count = ARRAY_SIZE(sta_counters);
+		res = os_calloc(count, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; sta_counters[i].name; i++) {
+			res[i] = os_strdup(sta_counters[i].name);
+			if (res[i] == NULL)
+				break;
+		}
+		break;
+	case 2:
+		res = get_bssid_list(s);
+		break;
+	case 3:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 0);
+		break;
+	}
+
+	return res;
+}
+
+
+struct bss_counters {
+	const char *name;
+	enum wlantest_bss_counter num;
+};
+
+static const struct bss_counters bss_counters[] = {
+	{ "valid_bip_mmie", WLANTEST_BSS_COUNTER_VALID_BIP_MMIE },
+	{ "invalid_bip_mmie", WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE },
+	{ "missing_bip_mmie", WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE },
+	{ "bip_deauth", WLANTEST_BSS_COUNTER_BIP_DEAUTH },
+	{ "bip_disassoc", WLANTEST_BSS_COUNTER_BIP_DISASSOC },
+	{ "probe_response", WLANTEST_BSS_COUNTER_PROBE_RESPONSE },
+	{ NULL, 0 }
+};
+
+static int cmd_get_bss_counter(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen, i;
+	size_t len;
+
+	if (argc != 2) {
+		printf("get_bss_counter needs at two arguments: "
+		       "counter name and BSSID\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_BSS_COUNTER);
+	pos += 4;
+
+	for (i = 0; bss_counters[i].name; i++) {
+		if (os_strcasecmp(bss_counters[i].name, argv[0]) == 0)
+			break;
+	}
+	if (bss_counters[i].name == NULL) {
+		printf("Unknown BSS counter '%s'\n", argv[0]);
+		printf("Counters:");
+		for (i = 0; bss_counters[i].name; i++)
+			printf(" %s", bss_counters[i].name);
+		printf("\n");
+		return -1;
+	}
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_BSS_COUNTER,
+			    bss_counters[i].num);
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+	if (pos == NULL || len != 4)
+		return -1;
+	printf("%u\n", WPA_GET_BE32(pos));
+	return 0;
+}
+
+
+static char ** complete_get_bss_counter(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	int i, count;
+
+	switch (arg) {
+	case 1:
+		/* counter list */
+		count = ARRAY_SIZE(bss_counters);
+		res = os_calloc(count, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; bss_counters[i].name; i++) {
+			res[i] = os_strdup(bss_counters[i].name);
+			if (res[i] == NULL)
+				break;
+		}
+		break;
+	case 2:
+		res = get_bssid_list(s);
+		break;
+	}
+
+	return res;
+}
+
+
+static int cmd_relog(int s, int argc, char *argv[])
+{
+	return cmd_simple(s, WLANTEST_CTRL_RELOG);
+}
+
+
+struct tdls_counters {
+	const char *name;
+	enum wlantest_tdls_counter num;
+};
+
+static const struct tdls_counters tdls_counters[] = {
+	{ "valid_direct_link", WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK },
+	{ "invalid_direct_link", WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK },
+	{ "valid_ap_path", WLANTEST_TDLS_COUNTER_VALID_AP_PATH },
+	{ "invalid_ap_path", WLANTEST_TDLS_COUNTER_INVALID_AP_PATH },
+	{ "setup_req", WLANTEST_TDLS_COUNTER_SETUP_REQ },
+	{ "setup_resp_ok", WLANTEST_TDLS_COUNTER_SETUP_RESP_OK },
+	{ "setup_resp_fail", WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL },
+	{ "setup_conf_ok", WLANTEST_TDLS_COUNTER_SETUP_CONF_OK },
+	{ "setup_conf_fail", WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL },
+	{ "teardown", WLANTEST_TDLS_COUNTER_TEARDOWN },
+	{ NULL, 0 }
+};
+
+static int cmd_get_tdls_counter(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen, i;
+	size_t len;
+
+	if (argc != 4) {
+		printf("get_tdls_counter needs four arguments: "
+		       "counter name, BSSID, STA1 address, STA2 address\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_TDLS_COUNTER);
+	pos += 4;
+
+	for (i = 0; tdls_counters[i].name; i++) {
+		if (os_strcasecmp(tdls_counters[i].name, argv[0]) == 0)
+			break;
+	}
+	if (tdls_counters[i].name == NULL) {
+		printf("Unknown TDLS counter '%s'\n", argv[0]);
+		printf("Counters:");
+		for (i = 0; tdls_counters[i].name; i++)
+			printf(" %s", tdls_counters[i].name);
+		printf("\n");
+		return -1;
+	}
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_TDLS_COUNTER,
+			    tdls_counters[i].num);
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[2], pos) < 0) {
+		printf("Invalid STA1 address '%s'\n", argv[2]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA2_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[3], pos) < 0) {
+		printf("Invalid STA2 address '%s'\n", argv[3]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+	if (pos == NULL || len != 4)
+		return -1;
+	printf("%u\n", WPA_GET_BE32(pos));
+	return 0;
+}
+
+
+static char ** complete_get_tdls_counter(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	int i, count;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		/* counter list */
+		count = ARRAY_SIZE(tdls_counters);
+		res = os_calloc(count, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; tdls_counters[i].name; i++) {
+			res[i] = os_strdup(tdls_counters[i].name);
+			if (res[i] == NULL)
+				break;
+		}
+		break;
+	case 2:
+		res = get_bssid_list(s);
+		break;
+	case 3:
+	case 4:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 0);
+		break;
+	}
+
+	return res;
+}
+
+
+struct inject_frames {
+	const char *name;
+	enum wlantest_inject_frame frame;
+};
+
+static const struct inject_frames inject_frames[] = {
+	{ "auth", WLANTEST_FRAME_AUTH },
+	{ "assocreq", WLANTEST_FRAME_ASSOCREQ },
+	{ "reassocreq", WLANTEST_FRAME_REASSOCREQ },
+	{ "deauth", WLANTEST_FRAME_DEAUTH },
+	{ "disassoc", WLANTEST_FRAME_DISASSOC },
+	{ "saqueryreq", WLANTEST_FRAME_SAQUERYREQ },
+	{ NULL, 0 }
+};
+
+static int cmd_inject(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen, i;
+	enum wlantest_inject_protection prot;
+
+	/* <frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff> */
+
+	if (argc < 5) {
+		printf("inject needs five arguments: frame, protection, "
+		       "sender, BSSID, STA/ff:ff:ff:ff:ff:ff\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_INJECT);
+	pos += 4;
+
+	for (i = 0; inject_frames[i].name; i++) {
+		if (os_strcasecmp(inject_frames[i].name, argv[0]) == 0)
+			break;
+	}
+	if (inject_frames[i].name == NULL) {
+		printf("Unknown inject frame '%s'\n", argv[0]);
+		printf("Frames:");
+		for (i = 0; inject_frames[i].name; i++)
+			printf(" %s", inject_frames[i].name);
+		printf("\n");
+		return -1;
+	}
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_FRAME,
+			    inject_frames[i].frame);
+
+	if (os_strcasecmp(argv[1], "normal") == 0)
+		prot = WLANTEST_INJECT_NORMAL;
+	else if (os_strcasecmp(argv[1], "protected") == 0)
+		prot = WLANTEST_INJECT_PROTECTED;
+	else if (os_strcasecmp(argv[1], "unprotected") == 0)
+		prot = WLANTEST_INJECT_UNPROTECTED;
+	else if (os_strcasecmp(argv[1], "incorrect") == 0)
+		prot = WLANTEST_INJECT_INCORRECT_KEY;
+	else {
+		printf("Unknown protection type '%s'\n", argv[1]);
+		printf("Protection types: normal protected unprotected "
+		       "incorrect\n");
+		return -1;
+	}
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_PROTECTION, prot);
+
+	if (os_strcasecmp(argv[2], "ap") == 0) {
+		pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP,
+				    1);
+	} else if (os_strcasecmp(argv[2], "sta") == 0) {
+		pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP,
+				    0);
+	} else {
+		printf("Unknown sender '%s'\n", argv[2]);
+		printf("Sender types: ap sta\n");
+		return -1;
+	}
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[3], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[3]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[4], pos) < 0) {
+		printf("Invalid STA '%s'\n", argv[4]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	printf("OK\n");
+	return 0;
+}
+
+
+static char ** complete_inject(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	int i, count;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		/* frame list */
+		count = ARRAY_SIZE(inject_frames);
+		res = os_calloc(count, sizeof(char *));
+		if (res == NULL)
+			break;
+		for (i = 0; inject_frames[i].name; i++) {
+			res[i] = os_strdup(inject_frames[i].name);
+			if (res[i] == NULL)
+				break;
+		}
+		break;
+	case 2:
+		res = os_calloc(5, sizeof(char *));
+		if (res == NULL)
+			break;
+		res[0] = os_strdup("normal");
+		if (res[0] == NULL)
+			break;
+		res[1] = os_strdup("protected");
+		if (res[1] == NULL)
+			break;
+		res[2] = os_strdup("unprotected");
+		if (res[2] == NULL)
+			break;
+		res[3] = os_strdup("incorrect");
+		if (res[3] == NULL)
+			break;
+		break;
+	case 3:
+		res = os_calloc(3, sizeof(char *));
+		if (res == NULL)
+			break;
+		res[0] = os_strdup("ap");
+		if (res[0] == NULL)
+			break;
+		res[1] = os_strdup("sta");
+		if (res[1] == NULL)
+			break;
+		break;
+	case 4:
+		res = get_bssid_list(s);
+		break;
+	case 5:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 1);
+		break;
+	}
+
+	return res;
+}
+
+
+static u8 * add_hex(u8 *pos, u8 *end, const char *str)
+{
+	const char *s;
+	int val;
+
+	s = str;
+	while (*s) {
+		while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n' ||
+		       *s == ':')
+			s++;
+		if (*s == '\0')
+			break;
+		if (*s == '#') {
+			while (*s != '\0' && *s != '\r' && *s != '\n')
+				s++;
+			continue;
+		}
+
+		val = hex2byte(s);
+		if (val < 0) {
+			printf("Invalid hex encoding '%s'\n", s);
+			return NULL;
+		}
+		if (pos == end) {
+			printf("Too long frame\n");
+			return NULL;
+		}
+		*pos++ = val;
+		s += 2;
+	}
+
+	return pos;
+}
+
+
+static int cmd_send(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[WLANTEST_CTRL_MAX_CMD_LEN], *end, *pos, *len_pos;
+	int rlen;
+	enum wlantest_inject_protection prot;
+	int arg;
+
+	/* <prot> <raw frame as hex dump> */
+
+	if (argc < 2) {
+		printf("send needs two arguments: protected/unprotected, "
+		       "raw frame as hex dump\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SEND);
+	pos += 4;
+
+	if (os_strcasecmp(argv[0], "normal") == 0)
+		prot = WLANTEST_INJECT_NORMAL;
+	else if (os_strcasecmp(argv[0], "protected") == 0)
+		prot = WLANTEST_INJECT_PROTECTED;
+	else if (os_strcasecmp(argv[0], "unprotected") == 0)
+		prot = WLANTEST_INJECT_UNPROTECTED;
+	else if (os_strcasecmp(argv[0], "incorrect") == 0)
+		prot = WLANTEST_INJECT_INCORRECT_KEY;
+	else {
+		printf("Unknown protection type '%s'\n", argv[1]);
+		printf("Protection types: normal protected unprotected "
+		       "incorrect\n");
+		return -1;
+	}
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_PROTECTION, prot);
+
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_FRAME);
+	pos += 4;
+	len_pos = pos;
+	pos += 4;
+
+	for (arg = 1; pos && arg < argc; arg++)
+		pos = add_hex(pos, end, argv[arg]);
+	if (pos == NULL)
+		return -1;
+
+	WPA_PUT_BE32(len_pos, pos - len_pos - 4);
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	printf("OK\n");
+	return 0;
+}
+
+
+static char ** complete_send(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = os_calloc(5, sizeof(char *));
+		if (res == NULL)
+			break;
+		res[0] = os_strdup("normal");
+		if (res[0] == NULL)
+			break;
+		res[1] = os_strdup("protected");
+		if (res[1] == NULL)
+			break;
+		res[2] = os_strdup("unprotected");
+		if (res[2] == NULL)
+			break;
+		res[3] = os_strdup("incorrect");
+		if (res[3] == NULL)
+			break;
+		break;
+	}
+
+	return res;
+}
+
+
+static int cmd_version(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[4];
+	char *version;
+	size_t len;
+	int rlen, i;
+
+	WPA_PUT_BE32(buf, WLANTEST_CTRL_VERSION);
+	rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	version = (char *) attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_VERSION,
+				    &len);
+	if (version == NULL)
+		return -1;
+
+	for (i = 0; i < len; i++)
+		putchar(version[i]);
+	printf("\n");
+
+	return 0;
+}
+
+
+static int cmd_add_passphrase(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos, *end;
+	size_t len;
+	int rlen;
+
+	if (argc < 1) {
+		printf("add_passphrase needs one argument: passphrase\n");
+		return -1;
+	}
+
+	len = os_strlen(argv[0]);
+	if (len < 8 || len > 63) {
+		printf("Invalid passphrase '%s'\n", argv[0]);
+		return -1;
+	}
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_ADD_PASSPHRASE);
+	pos += 4;
+	pos = attr_add_str(pos, end, WLANTEST_ATTR_PASSPHRASE,
+			   argv[0]);
+	if (argc > 1) {
+		pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+		if (hwaddr_aton(argv[1], pos) < 0) {
+			printf("Invalid BSSID '%s'\n", argv[3]);
+			return -1;
+		}
+		pos += ETH_ALEN;
+	}
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	return 0;
+}
+
+
+static int cmd_add_wepkey(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos, *end;
+	int rlen;
+
+	if (argc < 1) {
+		printf("add_wepkey needs one argument: WEP key\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_ADD_PASSPHRASE);
+	pos += 4;
+	pos = attr_add_str(pos, end, WLANTEST_ATTR_WEPKEY, argv[0]);
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	return 0;
+}
+
+
+struct sta_infos {
+	const char *name;
+	enum wlantest_sta_info num;
+};
+
+static const struct sta_infos sta_infos[] = {
+	{ "proto", WLANTEST_STA_INFO_PROTO },
+	{ "pairwise", WLANTEST_STA_INFO_PAIRWISE },
+	{ "key_mgmt", WLANTEST_STA_INFO_KEY_MGMT },
+	{ "rsn_capab", WLANTEST_STA_INFO_RSN_CAPAB },
+	{ "state", WLANTEST_STA_INFO_STATE },
+	{ "gtk", WLANTEST_STA_INFO_GTK },
+	{ NULL, 0 }
+};
+
+static int cmd_info_sta(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen, i;
+	size_t len;
+	char info[100];
+
+	if (argc != 3) {
+		printf("sta_info needs at three arguments: "
+		       "counter name, BSSID, and STA address\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_INFO_STA);
+	pos += 4;
+
+	for (i = 0; sta_infos[i].name; i++) {
+		if (os_strcasecmp(sta_infos[i].name, argv[0]) == 0)
+			break;
+	}
+	if (sta_infos[i].name == NULL) {
+		printf("Unknown STA info '%s'\n", argv[0]);
+		printf("Info fields:");
+		for (i = 0; sta_infos[i].name; i++)
+			printf(" %s", sta_infos[i].name);
+		printf("\n");
+		return -1;
+	}
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_STA_INFO,
+			    sta_infos[i].num);
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[2], pos) < 0) {
+		printf("Invalid STA address '%s'\n", argv[2]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_INFO, &len);
+	if (pos == NULL)
+		return -1;
+	if (len >= sizeof(info))
+		len = sizeof(info) - 1;
+	os_memcpy(info, pos, len);
+	info[len] = '\0';
+	printf("%s\n", info);
+	return 0;
+}
+
+
+static char ** complete_info_sta(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	int i, count;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		/* counter list */
+		count = ARRAY_SIZE(sta_infos);
+		res = os_calloc(count, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; sta_infos[i].name; i++) {
+			res[i] = os_strdup(sta_infos[i].name);
+			if (res[i] == NULL)
+				break;
+		}
+		break;
+	case 2:
+		res = get_bssid_list(s);
+		break;
+	case 3:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 0);
+		break;
+	}
+
+	return res;
+}
+
+
+struct bss_infos {
+	const char *name;
+	enum wlantest_bss_info num;
+};
+
+static const struct bss_infos bss_infos[] = {
+	{ "proto", WLANTEST_BSS_INFO_PROTO },
+	{ "pairwise", WLANTEST_BSS_INFO_PAIRWISE },
+	{ "group", WLANTEST_BSS_INFO_GROUP },
+	{ "group_mgmt", WLANTEST_BSS_INFO_GROUP_MGMT },
+	{ "key_mgmt", WLANTEST_BSS_INFO_KEY_MGMT },
+	{ "rsn_capab", WLANTEST_BSS_INFO_RSN_CAPAB },
+	{ NULL, 0 }
+};
+
+static int cmd_info_bss(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen, i;
+	size_t len;
+	char info[100];
+
+	if (argc != 2) {
+		printf("bss_info needs at two arguments: "
+		       "field name and BSSID\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_INFO_BSS);
+	pos += 4;
+
+	for (i = 0; bss_infos[i].name; i++) {
+		if (os_strcasecmp(bss_infos[i].name, argv[0]) == 0)
+			break;
+	}
+	if (bss_infos[i].name == NULL) {
+		printf("Unknown BSS info '%s'\n", argv[0]);
+		printf("Info fields:");
+		for (i = 0; bss_infos[i].name; i++)
+			printf(" %s", bss_infos[i].name);
+		printf("\n");
+		return -1;
+	}
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_BSS_INFO,
+			    bss_infos[i].num);
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_INFO, &len);
+	if (pos == NULL)
+		return -1;
+	if (len >= sizeof(info))
+		len = sizeof(info) - 1;
+	os_memcpy(info, pos, len);
+	info[len] = '\0';
+	printf("%s\n", info);
+	return 0;
+}
+
+
+static char ** complete_info_bss(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	int i, count;
+
+	switch (arg) {
+	case 1:
+		/* counter list */
+		count = ARRAY_SIZE(bss_infos);
+		res = os_calloc(count, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; bss_infos[i].name; i++) {
+			res[i] = os_strdup(bss_infos[i].name);
+			if (res[i] == NULL)
+				break;
+		}
+		break;
+	case 2:
+		res = get_bssid_list(s);
+		break;
+	}
+
+	return res;
+}
+
+
+static int cmd_get_tx_tid(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen;
+	size_t len;
+
+	if (argc != 3) {
+		printf("get_tx_tid needs three arguments: "
+		       "BSSID, STA address, and TID\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_TX_TID);
+	pos += 4;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[0], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[0]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid STA address '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_TID, atoi(argv[2]));
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+	if (pos == NULL || len != 4)
+		return -1;
+	printf("%u\n", WPA_GET_BE32(pos));
+	return 0;
+}
+
+
+static int cmd_get_rx_tid(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen;
+	size_t len;
+
+	if (argc != 3) {
+		printf("get_tx_tid needs three arguments: "
+		       "BSSID, STA address, and TID\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_RX_TID);
+	pos += 4;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[0], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[0]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid STA address '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_TID, atoi(argv[2]));
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+	if (pos == NULL || len != 4)
+		return -1;
+	printf("%u\n", WPA_GET_BE32(pos));
+	return 0;
+}
+
+
+static char ** complete_get_tid(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		res = get_bssid_list(s);
+		break;
+	case 2:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 0);
+		break;
+	}
+
+	return res;
+}
+
+
+struct wlantest_cli_cmd {
+	const char *cmd;
+	int (*handler)(int s, int argc, char *argv[]);
+	const char *usage;
+	char ** (*complete)(int s, const char *str, int pos);
+};
+
+static const struct wlantest_cli_cmd wlantest_cli_commands[] = {
+	{ "ping", cmd_ping, "= test connection to wlantest", NULL },
+	{ "terminate", cmd_terminate, "= terminate wlantest", NULL },
+	{ "list_bss", cmd_list_bss, "= get BSS list", NULL },
+	{ "list_sta", cmd_list_sta, "<BSSID> = get STA list",
+	  complete_list_sta },
+	{ "flush", cmd_flush, "= drop all collected BSS data", NULL },
+	{ "clear_sta_counters", cmd_clear_sta_counters,
+	  "<BSSID> <STA> = clear STA counters", complete_clear_sta_counters },
+	{ "clear_bss_counters", cmd_clear_bss_counters,
+	  "<BSSID> = clear BSS counters", complete_clear_bss_counters },
+	{ "get_sta_counter", cmd_get_sta_counter,
+	  "<counter> <BSSID> <STA> = get STA counter value",
+	  complete_get_sta_counter },
+	{ "get_bss_counter", cmd_get_bss_counter,
+	  "<counter> <BSSID> = get BSS counter value",
+	  complete_get_bss_counter },
+	{ "inject", cmd_inject,
+	  "<frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff>",
+	  complete_inject },
+	{ "send", cmd_send,
+	  "<prot> <raw frame as hex dump>",
+	  complete_send },
+	{ "version", cmd_version, "= get wlantest version", NULL },
+	{ "add_passphrase", cmd_add_passphrase,
+	  "<passphrase> = add a known passphrase", NULL },
+	{ "add_wepkey", cmd_add_wepkey,
+	  "<WEP key> = add a known WEP key", NULL },
+	{ "info_sta", cmd_info_sta,
+	  "<field> <BSSID> <STA> = get STA information",
+	  complete_info_sta },
+	{ "info_bss", cmd_info_bss,
+	  "<field> <BSSID> = get BSS information",
+	  complete_info_bss },
+	{ "clear_tdls_counters", cmd_clear_tdls_counters,
+	  "<BSSID> <STA1> <STA2> = clear TDLS counters",
+	  complete_clear_tdls_counters },
+	{ "get_tdls_counter", cmd_get_tdls_counter,
+	  "<counter> <BSSID> <STA1> <STA2> = get TDLS counter value",
+	  complete_get_tdls_counter },
+	{ "get_bss_counter", cmd_get_bss_counter,
+	  "<counter> <BSSID> = get BSS counter value",
+	  complete_get_bss_counter },
+	{ "relog", cmd_relog, "= re-open log-file (allow rolling logs)", NULL },
+	{ "get_tx_tid", cmd_get_tx_tid,
+	  "<BSSID> <STA> <TID> = get STA TX TID counter value",
+	  complete_get_tid },
+	{ "get_rx_tid", cmd_get_rx_tid,
+	  "<BSSID> <STA> <TID> = get STA RX TID counter value",
+	  complete_get_tid },
+	{ NULL, NULL, NULL, NULL }
+};
+
+
+static int ctrl_command(int s, int argc, char *argv[])
+{
+	const struct wlantest_cli_cmd *cmd, *match = NULL;
+	int count = 0;
+	int ret = 0;
+
+	for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) {
+		if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0)
+		{
+			match = cmd;
+			if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
+				/* exact match */
+				count = 1;
+				break;
+			}
+			count++;
+		}
+	}
+
+	if (count > 1) {
+		printf("Ambiguous command '%s'; possible commands:", argv[0]);
+		for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) {
+			if (os_strncasecmp(cmd->cmd, argv[0],
+					   os_strlen(argv[0])) == 0) {
+				printf(" %s", cmd->cmd);
+			}
+		}
+		printf("\n");
+		ret = 1;
+	} else if (count == 0) {
+		printf("Unknown command '%s'\n", argv[0]);
+		ret = 1;
+	} else {
+		ret = match->handler(s, argc - 1, &argv[1]);
+	}
+
+	return ret;
+}
+
+
+struct wlantest_cli {
+	int s;
+};
+
+
+#define max_args 10
+
+static int tokenize_cmd(char *cmd, char *argv[])
+{
+	char *pos;
+	int argc = 0;
+
+	pos = cmd;
+	for (;;) {
+		while (*pos == ' ')
+			pos++;
+		if (*pos == '\0')
+			break;
+		argv[argc] = pos;
+		argc++;
+		if (argc == max_args)
+			break;
+		if (*pos == '"') {
+			char *pos2 = os_strrchr(pos, '"');
+			if (pos2)
+				pos = pos2 + 1;
+		}
+		while (*pos != '\0' && *pos != ' ')
+			pos++;
+		if (*pos == ' ')
+			*pos++ = '\0';
+	}
+
+	return argc;
+}
+
+
+static void wlantest_cli_edit_cmd_cb(void *ctx, char *cmd)
+{
+	struct wlantest_cli *cli = ctx;
+	char *argv[max_args];
+	int argc;
+	argc = tokenize_cmd(cmd, argv);
+	if (argc) {
+		int ret = ctrl_command(cli->s, argc, argv);
+		if (ret < 0)
+			printf("FAIL\n");
+	}
+}
+
+
+static void wlantest_cli_eloop_terminate(int sig, void *signal_ctx)
+{
+	eloop_terminate();
+}
+
+
+static void wlantest_cli_edit_eof_cb(void *ctx)
+{
+	eloop_terminate();
+}
+
+
+static char ** wlantest_cli_cmd_list(void)
+{
+	char **res;
+	int i;
+
+	res = os_calloc(ARRAY_SIZE(wlantest_cli_commands), sizeof(char *));
+	if (res == NULL)
+		return NULL;
+
+	for (i = 0; wlantest_cli_commands[i].cmd; i++) {
+		res[i] = os_strdup(wlantest_cli_commands[i].cmd);
+		if (res[i] == NULL)
+			break;
+	}
+
+	return res;
+}
+
+
+static char ** wlantest_cli_cmd_completion(struct wlantest_cli *cli,
+					   const char *cmd, const char *str,
+					   int pos)
+{
+	int i;
+
+	for (i = 0; wlantest_cli_commands[i].cmd; i++) {
+		const struct wlantest_cli_cmd *c = &wlantest_cli_commands[i];
+		if (os_strcasecmp(c->cmd, cmd) == 0) {
+			edit_clear_line();
+			printf("\r%s\n", c->usage);
+			edit_redraw();
+			if (c->complete)
+				return c->complete(cli->s, str, pos);
+			break;
+		}
+	}
+
+	return NULL;
+}
+
+
+static char ** wlantest_cli_edit_completion_cb(void *ctx, const char *str,
+					       int pos)
+{
+	struct wlantest_cli *cli = ctx;
+	char **res;
+	const char *end;
+	char *cmd;
+
+	end = os_strchr(str, ' ');
+	if (end == NULL || str + pos < end)
+		return wlantest_cli_cmd_list();
+
+	cmd = os_malloc(pos + 1);
+	if (cmd == NULL)
+		return NULL;
+	os_memcpy(cmd, str, pos);
+	cmd[end - str] = '\0';
+	res = wlantest_cli_cmd_completion(cli, cmd, str, pos);
+	os_free(cmd);
+	return res;
+}
+
+
+static void wlantest_cli_interactive(int s)
+{
+	struct wlantest_cli cli;
+	char *home, *hfile = NULL;
+
+	if (eloop_init())
+		return;
+
+	home = getenv("HOME");
+	if (home) {
+		const char *fname = ".wlantest_cli_history";
+		int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
+		hfile = os_malloc(hfile_len);
+		if (hfile)
+			os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
+	}
+
+	cli.s = s;
+	eloop_register_signal_terminate(wlantest_cli_eloop_terminate, &cli);
+	edit_init(wlantest_cli_edit_cmd_cb, wlantest_cli_edit_eof_cb,
+		  wlantest_cli_edit_completion_cb, &cli, hfile, NULL);
+
+	eloop_run();
+
+	edit_deinit(hfile, NULL);
+	os_free(hfile);
+	eloop_destroy();
+}
+
+
+int main(int argc, char *argv[])
+{
+	int s;
+	struct sockaddr_un addr;
+	int ret = 0;
+
+	if (os_program_init())
+		return -1;
+
+	s = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (s < 0) {
+		perror("socket");
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME,
+		   sizeof(addr.sun_path) - 1);
+	if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("connect");
+		close(s);
+		return -1;
+	}
+
+	if (argc > 1) {
+		ret = ctrl_command(s, argc - 1, &argv[1]);
+		if (ret < 0)
+			printf("FAIL\n");
+	} else {
+		wlantest_cli_interactive(s);
+	}
+
+	close(s);
+
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/hostap/wlantest/wlantest_ctrl.h b/hostap/wlantest/wlantest_ctrl.h
new file mode 100644
index 0000000..1af6838
--- /dev/null
+++ b/hostap/wlantest/wlantest_ctrl.h
@@ -0,0 +1,171 @@
+/*
+ * wlantest control interface
+ * Copyright (c) 2010-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WLANTEST_CTRL_H
+#define WLANTEST_CTRL_H
+
+#define WLANTEST_SOCK_NAME "w1.fi.wlantest"
+#define WLANTEST_CTRL_MAX_CMD_LEN 1000
+#define WLANTEST_CTRL_MAX_RESP_LEN 1000
+
+enum wlantest_ctrl_cmd {
+	WLANTEST_CTRL_SUCCESS,
+	WLANTEST_CTRL_FAILURE,
+	WLANTEST_CTRL_INVALID_CMD,
+	WLANTEST_CTRL_UNKNOWN_CMD,
+	WLANTEST_CTRL_PING,
+	WLANTEST_CTRL_TERMINATE,
+	WLANTEST_CTRL_LIST_BSS,
+	WLANTEST_CTRL_LIST_STA,
+	WLANTEST_CTRL_FLUSH,
+	WLANTEST_CTRL_CLEAR_STA_COUNTERS,
+	WLANTEST_CTRL_CLEAR_BSS_COUNTERS,
+	WLANTEST_CTRL_GET_STA_COUNTER,
+	WLANTEST_CTRL_GET_BSS_COUNTER,
+	WLANTEST_CTRL_INJECT,
+	WLANTEST_CTRL_VERSION,
+	WLANTEST_CTRL_ADD_PASSPHRASE,
+	WLANTEST_CTRL_INFO_STA,
+	WLANTEST_CTRL_INFO_BSS,
+	WLANTEST_CTRL_SEND,
+	WLANTEST_CTRL_CLEAR_TDLS_COUNTERS,
+	WLANTEST_CTRL_GET_TDLS_COUNTER,
+	WLANTEST_CTRL_RELOG,
+	WLANTEST_CTRL_GET_TX_TID,
+	WLANTEST_CTRL_GET_RX_TID,
+};
+
+enum wlantest_ctrl_attr {
+	WLANTEST_ATTR_BSSID,
+	WLANTEST_ATTR_STA_ADDR,
+	WLANTEST_ATTR_STA_COUNTER,
+	WLANTEST_ATTR_BSS_COUNTER,
+	WLANTEST_ATTR_COUNTER,
+	WLANTEST_ATTR_INJECT_FRAME,
+	WLANTEST_ATTR_INJECT_SENDER_AP,
+	WLANTEST_ATTR_INJECT_PROTECTION,
+	WLANTEST_ATTR_VERSION,
+	WLANTEST_ATTR_PASSPHRASE,
+	WLANTEST_ATTR_STA_INFO,
+	WLANTEST_ATTR_BSS_INFO,
+	WLANTEST_ATTR_INFO,
+	WLANTEST_ATTR_FRAME,
+	WLANTEST_ATTR_TDLS_COUNTER,
+	WLANTEST_ATTR_STA2_ADDR,
+	WLANTEST_ATTR_WEPKEY,
+	WLANTEST_ATTR_TID,
+};
+
+enum wlantest_bss_counter {
+	WLANTEST_BSS_COUNTER_VALID_BIP_MMIE,
+	WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE,
+	WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE,
+	WLANTEST_BSS_COUNTER_BIP_DEAUTH,
+	WLANTEST_BSS_COUNTER_BIP_DISASSOC,
+	WLANTEST_BSS_COUNTER_PROBE_RESPONSE,
+	NUM_WLANTEST_BSS_COUNTER
+};
+
+enum wlantest_sta_counter {
+	WLANTEST_STA_COUNTER_AUTH_TX,
+	WLANTEST_STA_COUNTER_AUTH_RX,
+	WLANTEST_STA_COUNTER_ASSOCREQ_TX,
+	WLANTEST_STA_COUNTER_REASSOCREQ_TX,
+	WLANTEST_STA_COUNTER_PTK_LEARNED,
+	WLANTEST_STA_COUNTER_VALID_DEAUTH_TX,
+	WLANTEST_STA_COUNTER_VALID_DEAUTH_RX,
+	WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX,
+	WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX,
+	WLANTEST_STA_COUNTER_VALID_DISASSOC_TX,
+	WLANTEST_STA_COUNTER_VALID_DISASSOC_RX,
+	WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX,
+	WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX,
+	WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX,
+	WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX,
+	WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX,
+	WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX,
+	WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX,
+	WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX,
+	WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX,
+	WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX,
+	WLANTEST_STA_COUNTER_PING_OK,
+	WLANTEST_STA_COUNTER_ASSOCRESP_COMEBACK,
+	WLANTEST_STA_COUNTER_REASSOCRESP_COMEBACK,
+	WLANTEST_STA_COUNTER_PING_OK_FIRST_ASSOC,
+	WLANTEST_STA_COUNTER_VALID_DEAUTH_RX_ACK,
+	WLANTEST_STA_COUNTER_VALID_DISASSOC_RX_ACK,
+	WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX_ACK,
+	WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX_ACK,
+	WLANTEST_STA_COUNTER_DEAUTH_RX_ASLEEP,
+	WLANTEST_STA_COUNTER_DEAUTH_RX_AWAKE,
+	WLANTEST_STA_COUNTER_DISASSOC_RX_ASLEEP,
+	WLANTEST_STA_COUNTER_DISASSOC_RX_AWAKE,
+	WLANTEST_STA_COUNTER_PROT_DATA_TX,
+	WLANTEST_STA_COUNTER_DEAUTH_RX_RC6,
+	WLANTEST_STA_COUNTER_DEAUTH_RX_RC7,
+	WLANTEST_STA_COUNTER_DISASSOC_RX_RC6,
+	WLANTEST_STA_COUNTER_DISASSOC_RX_RC7,
+	NUM_WLANTEST_STA_COUNTER
+};
+
+enum wlantest_tdls_counter {
+	WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK,
+	WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK,
+	WLANTEST_TDLS_COUNTER_VALID_AP_PATH,
+	WLANTEST_TDLS_COUNTER_INVALID_AP_PATH,
+	WLANTEST_TDLS_COUNTER_SETUP_REQ,
+	WLANTEST_TDLS_COUNTER_SETUP_RESP_OK,
+	WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL,
+	WLANTEST_TDLS_COUNTER_SETUP_CONF_OK,
+	WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL,
+	WLANTEST_TDLS_COUNTER_TEARDOWN,
+	NUM_WLANTEST_TDLS_COUNTER
+};
+
+enum wlantest_inject_frame {
+	WLANTEST_FRAME_AUTH,
+	WLANTEST_FRAME_ASSOCREQ,
+	WLANTEST_FRAME_REASSOCREQ,
+	WLANTEST_FRAME_DEAUTH,
+	WLANTEST_FRAME_DISASSOC,
+	WLANTEST_FRAME_SAQUERYREQ,
+};
+
+/**
+ * enum wlantest_inject_protection - WLANTEST_CTRL_INJECT protection
+ * @WLANTEST_INJECT_NORMAL: Use normal rules (protect if key is set)
+ * @WLANTEST_INJECT_PROTECTED: Force protection (fail if not possible)
+ * @WLANTEST_INJECT_UNPROTECTED: Force unprotected
+ * @WLANTEST_INJECT_INCORRECT_KEY: Force protection with incorrect key
+ */
+enum wlantest_inject_protection {
+	WLANTEST_INJECT_NORMAL,
+	WLANTEST_INJECT_PROTECTED,
+	WLANTEST_INJECT_UNPROTECTED,
+	WLANTEST_INJECT_INCORRECT_KEY,
+};
+
+enum wlantest_sta_info {
+	WLANTEST_STA_INFO_PROTO,
+	WLANTEST_STA_INFO_PAIRWISE,
+	WLANTEST_STA_INFO_KEY_MGMT,
+	WLANTEST_STA_INFO_RSN_CAPAB,
+	WLANTEST_STA_INFO_STATE,
+	WLANTEST_STA_INFO_GTK,
+};
+
+enum wlantest_bss_info {
+	WLANTEST_BSS_INFO_PROTO,
+	WLANTEST_BSS_INFO_PAIRWISE,
+	WLANTEST_BSS_INFO_GROUP,
+	WLANTEST_BSS_INFO_GROUP_MGMT,
+	WLANTEST_BSS_INFO_KEY_MGMT,
+	WLANTEST_BSS_INFO_RSN_CAPAB,
+};
+
+#endif /* WLANTEST_CTRL_H */
diff --git a/hostap/wlantest/writepcap.c b/hostap/wlantest/writepcap.c
new file mode 100644
index 0000000..28b306b
--- /dev/null
+++ b/hostap/wlantest/writepcap.c
@@ -0,0 +1,359 @@
+/*
+ * PCAP capture file writer
+ * Copyright (c) 2010, 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 <pcap.h>
+#include <pcap-bpf.h>
+
+#include "utils/common.h"
+#include "wlantest.h"
+#include "common/qca-vendor.h"
+
+
+int write_pcap_init(struct wlantest *wt, const char *fname)
+{
+	wt->write_pcap = pcap_open_dead(DLT_IEEE802_11_RADIO, 4000);
+	if (wt->write_pcap == NULL)
+		return -1;
+	wt->write_pcap_dumper = pcap_dump_open(wt->write_pcap, fname);
+	if (wt->write_pcap_dumper == NULL) {
+		pcap_close(wt->write_pcap);
+		wt->write_pcap = NULL;
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "Writing PCAP dump to '%s'", fname);
+
+	return 0;
+}
+
+
+void write_pcap_deinit(struct wlantest *wt)
+{
+	if (wt->write_pcap_dumper) {
+		pcap_dump_close(wt->write_pcap_dumper);
+		wt->write_pcap_dumper = NULL;
+	}
+	if (wt->write_pcap) {
+		pcap_close(wt->write_pcap);
+		wt->write_pcap = NULL;
+	}
+}
+
+
+void write_pcap_captured(struct wlantest *wt, const u8 *buf, size_t len)
+{
+	struct pcap_pkthdr h;
+
+	if (!wt->write_pcap_dumper)
+		return;
+
+	os_memset(&h, 0, sizeof(h));
+	gettimeofday(&wt->write_pcap_time, NULL);
+	h.ts = wt->write_pcap_time;
+	h.caplen = len;
+	h.len = len;
+	pcap_dump(wt->write_pcap_dumper, &h, buf);
+}
+
+
+void write_pcap_decrypted(struct wlantest *wt, const u8 *buf1, size_t len1,
+			  const u8 *buf2, size_t len2)
+{
+	struct pcap_pkthdr h;
+	u8 rtap[] = {
+		0x00 /* rev */,
+		0x00 /* pad */,
+		0x0e, 0x00, /* header len */
+		0x00, 0x00, 0x00, 0x40, /* present flags */
+		0x00, 0x13, 0x74, QCA_RADIOTAP_VID_WLANTEST,
+		0x00, 0x00
+	};
+	u8 *buf;
+	size_t len;
+
+	if (!wt->write_pcap_dumper && !wt->pcapng)
+		return;
+
+	os_free(wt->decrypted);
+	len = sizeof(rtap) + len1 + len2;
+	wt->decrypted = buf = os_malloc(len);
+	if (buf == NULL)
+		return;
+	wt->decrypted_len = len;
+	os_memcpy(buf, rtap, sizeof(rtap));
+	if (buf1) {
+		os_memcpy(buf + sizeof(rtap), buf1, len1);
+		buf[sizeof(rtap) + 1] &= ~0x40; /* Clear Protected flag */
+	}
+	if (buf2)
+		os_memcpy(buf + sizeof(rtap) + len1, buf2, len2);
+
+	if (!wt->write_pcap_dumper)
+		return;
+
+	os_memset(&h, 0, sizeof(h));
+	h.ts = wt->write_pcap_time;
+	h.caplen = len;
+	h.len = len;
+	pcap_dump(wt->write_pcap_dumper, &h, buf);
+}
+
+
+struct pcapng_section_header {
+	u32 block_type; /* 0x0a0d0d0a */
+	u32 block_total_len;
+	u32 byte_order_magic;
+	u16 major_version;
+	u16 minor_version;
+	u64 section_len;
+	u32 block_total_len2;
+} STRUCT_PACKED;
+
+struct pcapng_interface_description {
+	u32 block_type; /* 0x00000001 */
+	u32 block_total_len;
+	u16 link_type;
+	u16 reserved;
+	u32 snap_len;
+	u32 block_total_len2;
+} STRUCT_PACKED;
+
+struct pcapng_enhanced_packet {
+	u32 block_type; /* 0x00000006 */
+	u32 block_total_len;
+	u32 interface_id;
+	u32 timestamp_high;
+	u32 timestamp_low;
+	u32 captured_len;
+	u32 packet_len;
+	/* Packet data - aligned to 32 bits */
+	/* Options (variable) */
+	/* Block Total Length copy */
+} STRUCT_PACKED;
+
+#define PCAPNG_BYTE_ORDER_MAGIC 0x1a2b3c4d
+#define PCAPNG_BLOCK_IFACE_DESC 0x00000001
+#define PCAPNG_BLOCK_PACKET 0x00000002
+#define PCAPNG_BLOCK_SIMPLE_PACKET 0x00000003
+#define PCAPNG_BLOCK_NAME_RESOLUTION 0x00000004
+#define PCAPNG_BLOCK_INTERFACE_STATISTICS 0x00000005
+#define PCAPNG_BLOCK_ENHANCED_PACKET 0x00000006
+#define PCAPNG_BLOCK_SECTION_HEADER 0x0a0d0d0a
+
+#define LINKTYPE_IEEE802_11 105
+#define LINKTYPE_IEEE802_11_RADIO 127
+
+#define PAD32(a) ((4 - ((a) & 3)) & 3)
+#define ALIGN32(a) ((a) + PAD32((a)))
+
+
+int write_pcapng_init(struct wlantest *wt, const char *fname)
+{
+	struct pcapng_section_header hdr;
+	struct pcapng_interface_description desc;
+
+	wt->pcapng = fopen(fname, "wb");
+	if (wt->pcapng == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "Writing PCAPNG dump to '%s'", fname);
+
+	os_memset(&hdr, 0, sizeof(hdr));
+	hdr.block_type = PCAPNG_BLOCK_SECTION_HEADER;
+	hdr.block_total_len = sizeof(hdr);
+	hdr.byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC;
+	hdr.major_version = 1;
+	hdr.minor_version = 0;
+	hdr.section_len = -1;
+	hdr.block_total_len2 = hdr.block_total_len;
+	fwrite(&hdr, sizeof(hdr), 1, wt->pcapng);
+
+	os_memset(&desc, 0, sizeof(desc));
+	desc.block_type = PCAPNG_BLOCK_IFACE_DESC;
+	desc.block_total_len = sizeof(desc);
+	desc.block_total_len2 = desc.block_total_len;
+	desc.link_type = LINKTYPE_IEEE802_11_RADIO;
+	desc.snap_len = 65535;
+	fwrite(&desc, sizeof(desc), 1, wt->pcapng);
+
+	return 0;
+}
+
+
+void write_pcapng_deinit(struct wlantest *wt)
+{
+	if (wt->pcapng) {
+		fclose(wt->pcapng);
+		wt->pcapng = NULL;
+	}
+}
+
+
+static u8 * pcapng_add_comments(struct wlantest *wt, u8 *pos)
+{
+	size_t i;
+	u16 *len;
+
+	if (!wt->num_notes)
+		return pos;
+
+	*((u16 *) pos) = 1 /* opt_comment */;
+	pos += 2;
+	len = (u16 *) pos /* length to be filled in */;
+	pos += 2;
+
+	for (i = 0; i < wt->num_notes; i++) {
+		size_t nlen = os_strlen(wt->notes[i]);
+		if (i > 0)
+			*pos++ = '\n';
+		os_memcpy(pos, wt->notes[i], nlen);
+		pos += nlen;
+	}
+	*len = pos - (u8 *) len - 2;
+	pos += PAD32(*len);
+
+	*((u16 *) pos) = 0 /* opt_endofopt */;
+	pos += 2;
+	*((u16 *) pos) = 0;
+	pos += 2;
+
+	return pos;
+}
+
+
+static void write_pcapng_decrypted(struct wlantest *wt)
+{
+	size_t len;
+	struct pcapng_enhanced_packet *pkt;
+	u8 *pos;
+	u32 *block_len;
+
+	if (!wt->pcapng || wt->decrypted == NULL)
+		return;
+
+	add_note(wt, MSG_EXCESSIVE, "decrypted version of the previous frame");
+
+	len = sizeof(*pkt) + wt->decrypted_len + 100 + notes_len(wt, 32);
+	pkt = os_zalloc(len);
+	if (pkt == NULL)
+		return;
+
+	pkt->block_type = PCAPNG_BLOCK_ENHANCED_PACKET;
+	pkt->interface_id = 0;
+	pkt->timestamp_high = wt->write_pcapng_time_high;
+	pkt->timestamp_low = wt->write_pcapng_time_low;
+	pkt->captured_len = wt->decrypted_len;
+	pkt->packet_len = wt->decrypted_len;
+
+	pos = (u8 *) (pkt + 1);
+
+	os_memcpy(pos, wt->decrypted, wt->decrypted_len);
+	pos += ALIGN32(wt->decrypted_len);
+
+	pos = pcapng_add_comments(wt, pos);
+
+	block_len = (u32 *) pos;
+	pos += 4;
+	*block_len = pkt->block_total_len = pos - (u8 *) pkt;
+
+	fwrite(pkt, pos - (u8 *) pkt, 1, wt->pcapng);
+
+	os_free(pkt);
+}
+
+
+void write_pcapng_write_read(struct wlantest *wt, int dlt,
+			     struct pcap_pkthdr *hdr, const u8 *data)
+{
+	struct pcapng_enhanced_packet *pkt;
+	u8 *pos;
+	u32 *block_len;
+	u64 timestamp;
+	size_t len, datalen = hdr->caplen;
+	u8 rtap[] = {
+		0x00 /* rev */,
+		0x00 /* pad */,
+		0x0a, 0x00, /* header len */
+		0x02, 0x00, 0x00, 0x00, /* present flags */
+		0x00, /* flags */
+		0x00 /* pad */
+	};
+
+	if (wt->assume_fcs)
+		rtap[8] |= 0x10;
+
+	if (!wt->pcapng)
+		return;
+
+	len = sizeof(*pkt) + hdr->len + 100 + notes_len(wt, 32) + sizeof(rtap);
+	pkt = os_zalloc(len);
+	if (pkt == NULL)
+		return;
+
+	pkt->block_type = PCAPNG_BLOCK_ENHANCED_PACKET;
+	pkt->interface_id = 0;
+	timestamp = 1000000 * hdr->ts.tv_sec + hdr->ts.tv_usec;
+	pkt->timestamp_high = timestamp >> 32;
+	pkt->timestamp_low = timestamp & 0xffffffff;
+	wt->write_pcapng_time_high = pkt->timestamp_high;
+	wt->write_pcapng_time_low = pkt->timestamp_low;
+	pkt->captured_len = hdr->caplen;
+	pkt->packet_len = hdr->len;
+
+	pos = (u8 *) (pkt + 1);
+
+	switch (dlt) {
+	case DLT_IEEE802_11_RADIO:
+		break;
+	case DLT_PRISM_HEADER:
+		/* remove prism header (could be kept ... lazy) */
+		pkt->captured_len -= WPA_GET_LE32(data + 4);
+		pkt->packet_len -= WPA_GET_LE32(data + 4);
+		datalen -= WPA_GET_LE32(data + 4);
+		data += WPA_GET_LE32(data + 4);
+		/* fall through */
+	case DLT_IEEE802_11:
+		pkt->captured_len += sizeof(rtap);
+		pkt->packet_len += sizeof(rtap);
+		os_memcpy(pos, &rtap, sizeof(rtap));
+		pos += sizeof(rtap);
+		break;
+	default:
+		return;
+	}
+
+	os_memcpy(pos, data, datalen);
+	pos += datalen + PAD32(pkt->captured_len);
+	pos = pcapng_add_comments(wt, pos);
+
+	block_len = (u32 *) pos;
+	pos += 4;
+	*block_len = pkt->block_total_len = pos - (u8 *) pkt;
+
+	fwrite(pkt, pos - (u8 *) pkt, 1, wt->pcapng);
+
+	os_free(pkt);
+
+	write_pcapng_decrypted(wt);
+}
+
+
+void write_pcapng_captured(struct wlantest *wt, const u8 *buf, size_t len)
+{
+	struct pcap_pkthdr h;
+
+	if (!wt->pcapng)
+		return;
+
+	os_memset(&h, 0, sizeof(h));
+	gettimeofday(&h.ts, NULL);
+	h.caplen = len;
+	h.len = len;
+	write_pcapng_write_read(wt, DLT_IEEE802_11_RADIO, &h, buf);
+}
diff --git a/hostap/wpa_supplicant/Android.mk b/hostap/wpa_supplicant/Android.mk
new file mode 100644
index 0000000..f05e3ed
--- /dev/null
+++ b/hostap/wpa_supplicant/Android.mk
@@ -0,0 +1,1659 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+#
+
+LOCAL_PATH := $(call my-dir)
+PKG_CONFIG ?= pkg-config
+
+ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),)
+  CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y
+endif
+
+include $(LOCAL_PATH)/android.config
+
+# To ignore possible wrong network configurations
+L_CFLAGS = -DWPA_IGNORE_CONFIG_ERRORS
+
+L_CFLAGS += -DVERSION_STR_POSTFIX=\"-$(PLATFORM_VERSION)\"
+
+# 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_LIB_STUB
+endif
+
+# Disable roaming in wpa_supplicant
+ifdef CONFIG_NO_ROAMING
+L_CFLAGS += -DCONFIG_NO_ROAMING
+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/misc/wifi/sockets\"
+
+# Use Android specific directory for wpa_cli command completion history
+L_CFLAGS += -DCONFIG_WPA_CLI_HISTORY_DIR=\"/data/misc/wifi\"
+
+# To force sizeof(enum) = 4
+ifeq ($(TARGET_ARCH),arm)
+L_CFLAGS += -mabi=aapcs-linux
+endif
+
+INCLUDES = $(LOCAL_PATH)
+INCLUDES += $(LOCAL_PATH)/src
+INCLUDES += $(LOCAL_PATH)/src/common
+# INCLUDES += $(LOCAL_PATH)/src/crypto # To force proper includes
+INCLUDES += $(LOCAL_PATH)/src/drivers
+INCLUDES += $(LOCAL_PATH)/src/eap_common
+INCLUDES += $(LOCAL_PATH)/src/eapol_supp
+INCLUDES += $(LOCAL_PATH)/src/eap_peer
+INCLUDES += $(LOCAL_PATH)/src/eap_server
+INCLUDES += $(LOCAL_PATH)/src/hlr_auc_gw
+INCLUDES += $(LOCAL_PATH)/src/l2_packet
+INCLUDES += $(LOCAL_PATH)/src/radius
+INCLUDES += $(LOCAL_PATH)/src/rsn_supp
+INCLUDES += $(LOCAL_PATH)/src/tls
+INCLUDES += $(LOCAL_PATH)/src/utils
+INCLUDES += $(LOCAL_PATH)/src/wps
+INCLUDES += external/dbus
+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
+
+ifdef CONFIG_FIPS
+CONFIG_NO_RANDOM_POOL=
+CONFIG_OPENSSL_CMAC=y
+endif
+
+OBJS = config.c
+OBJS += notify.c
+OBJS += bss.c
+OBJS += eap_register.c
+OBJS += src/utils/common.c
+OBJS += src/utils/wpa_debug.c
+OBJS += src/utils/wpabuf.c
+OBJS += wmm_ac.c
+OBJS_p = wpa_passphrase.c
+OBJS_p += src/utils/common.c
+OBJS_p += src/utils/wpa_debug.c
+OBJS_p += src/utils/wpabuf.c
+OBJS_c = wpa_cli.c src/common/wpa_ctrl.c
+OBJS_c += src/utils/wpa_debug.c
+OBJS_c += src/utils/common.c
+OBJS_d =
+OBJS_priv =
+
+ifndef CONFIG_OS
+ifdef CONFIG_NATIVE_WINDOWS
+CONFIG_OS=win32
+else
+CONFIG_OS=unix
+endif
+endif
+
+ifeq ($(CONFIG_OS), internal)
+L_CFLAGS += -DOS_NO_C_LIB_DEFINES
+endif
+
+OBJS += src/utils/os_$(CONFIG_OS).c
+OBJS_p += src/utils/os_$(CONFIG_OS).c
+OBJS_c += src/utils/os_$(CONFIG_OS).c
+
+ifdef CONFIG_WPA_TRACE
+L_CFLAGS += -DWPA_TRACE
+OBJS += src/utils/trace.c
+OBJS_p += src/utils/trace.c
+OBJS_c += src/utils/trace.c
+LDFLAGS += -rdynamic
+L_CFLAGS += -funwind-tables
+ifdef CONFIG_WPA_TRACE_BFD
+L_CFLAGS += -DWPA_TRACE_BFD
+LIBS += -lbfd
+LIBS_p += -lbfd
+LIBS_c += -lbfd
+endif
+endif
+
+ifndef CONFIG_ELOOP
+CONFIG_ELOOP=eloop
+endif
+OBJS += src/utils/$(CONFIG_ELOOP).c
+OBJS_c += src/utils/$(CONFIG_ELOOP).c
+
+ifdef CONFIG_ELOOP_POLL
+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
+
+ifdef CONFIG_HT_OVERRIDES
+L_CFLAGS += -DCONFIG_HT_OVERRIDES
+endif
+
+ifdef CONFIG_VHT_OVERRIDES
+L_CFLAGS += -DCONFIG_VHT_OVERRIDES
+endif
+
+ifndef CONFIG_BACKEND
+CONFIG_BACKEND=file
+endif
+
+ifeq ($(CONFIG_BACKEND), file)
+OBJS += config_file.c
+ifndef CONFIG_NO_CONFIG_BLOBS
+NEED_BASE64=y
+endif
+L_CFLAGS += -DCONFIG_BACKEND_FILE
+endif
+
+ifeq ($(CONFIG_BACKEND), winreg)
+OBJS += config_winreg.c
+endif
+
+ifeq ($(CONFIG_BACKEND), none)
+OBJS += config_none.c
+endif
+
+ifdef CONFIG_NO_CONFIG_WRITE
+L_CFLAGS += -DCONFIG_NO_CONFIG_WRITE
+endif
+
+ifdef CONFIG_NO_CONFIG_BLOBS
+L_CFLAGS += -DCONFIG_NO_CONFIG_BLOBS
+endif
+
+ifdef CONFIG_NO_SCAN_PROCESSING
+L_CFLAGS += -DCONFIG_NO_SCAN_PROCESSING
+endif
+
+ifdef CONFIG_SUITEB
+L_CFLAGS += -DCONFIG_SUITEB
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_SUITEB192
+L_CFLAGS += -DCONFIG_SUITEB192
+NEED_SHA384=y
+endif
+
+ifdef CONFIG_IEEE80211W
+L_CFLAGS += -DCONFIG_IEEE80211W
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_IEEE80211R
+L_CFLAGS += -DCONFIG_IEEE80211R
+OBJS += src/rsn_supp/wpa_ft.c
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_MESH
+NEED_80211_COMMON=y
+NEED_SHA256=y
+NEED_AES_SIV=y
+NEED_AES_OMAC1=y
+NEED_AES_CTR=y
+CONFIG_SAE=y
+CONFIG_AP=y
+L_CFLAGS += -DCONFIG_MESH
+OBJS += mesh.c
+OBJS += mesh_mpm.c
+OBJS += mesh_rsn.c
+endif
+
+ifdef CONFIG_SAE
+L_CFLAGS += -DCONFIG_SAE
+OBJS += src/common/sae.c
+NEED_ECC=y
+NEED_DH_GROUPS=y
+endif
+
+ifdef CONFIG_WNM
+L_CFLAGS += -DCONFIG_WNM
+OBJS += wnm_sta.c
+endif
+
+ifdef CONFIG_TDLS
+L_CFLAGS += -DCONFIG_TDLS
+OBJS += src/rsn_supp/tdls.c
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_TDLS_TESTING
+L_CFLAGS += -DCONFIG_TDLS_TESTING
+endif
+
+ifdef CONFIG_PEERKEY
+L_CFLAGS += -DCONFIG_PEERKEY
+endif
+
+ifndef CONFIG_NO_WPA
+OBJS += src/rsn_supp/wpa.c
+OBJS += src/rsn_supp/preauth.c
+OBJS += src/rsn_supp/pmksa_cache.c
+OBJS += src/rsn_supp/peerkey.c
+OBJS += src/rsn_supp/wpa_ie.c
+OBJS += src/common/wpa_common.c
+NEED_AES=y
+NEED_SHA1=y
+NEED_MD5=y
+NEED_RC4=y
+else
+L_CFLAGS += -DCONFIG_NO_WPA
+endif
+
+ifdef CONFIG_IBSS_RSN
+NEED_RSN_AUTHENTICATOR=y
+L_CFLAGS += -DCONFIG_IBSS_RSN
+OBJS += ibss_rsn.c
+endif
+
+ifdef CONFIG_P2P
+OBJS += p2p_supplicant.c
+OBJS += p2p_supplicant_sd.c
+OBJS += src/p2p/p2p.c
+OBJS += src/p2p/p2p_utils.c
+OBJS += src/p2p/p2p_parse.c
+OBJS += src/p2p/p2p_build.c
+OBJS += src/p2p/p2p_go_neg.c
+OBJS += src/p2p/p2p_sd.c
+OBJS += src/p2p/p2p_pd.c
+OBJS += src/p2p/p2p_invitation.c
+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
+CONFIG_WPS=y
+CONFIG_AP=y
+ifdef CONFIG_P2P_STRICT
+L_CFLAGS += -DCONFIG_P2P_STRICT
+endif
+endif
+
+ifdef CONFIG_WIFI_DISPLAY
+L_CFLAGS += -DCONFIG_WIFI_DISPLAY
+OBJS += wifi_display.c
+endif
+
+ifdef CONFIG_HS20
+OBJS += hs20_supplicant.c
+L_CFLAGS += -DCONFIG_HS20
+CONFIG_INTERWORKING=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_INTERWORKING
+OBJS += interworking.c
+L_CFLAGS += -DCONFIG_INTERWORKING
+NEED_GAS=y
+endif
+
+ifdef CONFIG_FST
+L_CFLAGS += -DCONFIG_FST
+OBJS += src/fst/fst.c
+OBJS += src/fst/fst_session.c
+OBJS += src/fst/fst_iface.c
+OBJS += src/fst/fst_group.c
+OBJS += src/fst/fst_ctrl_aux.c
+ifdef CONFIG_FST_TEST
+L_CFLAGS += -DCONFIG_FST_TEST
+endif
+ifdef CONFIG_CTRL_IFACE
+OBJS += src/fst/fst_ctrl_iface.c
+endif
+endif
+
+
+include $(LOCAL_PATH)/src/drivers/drivers.mk
+
+ifdef CONFIG_AP
+OBJS_d += $(DRV_BOTH_OBJS)
+L_CFLAGS += $(DRV_BOTH_CFLAGS)
+LDFLAGS += $(DRV_BOTH_LDFLAGS)
+LIBS += $(DRV_BOTH_LIBS)
+else
+NEED_AP_MLME=
+OBJS_d += $(DRV_WPA_OBJS)
+L_CFLAGS += $(DRV_WPA_CFLAGS)
+LDFLAGS += $(DRV_WPA_LDFLAGS)
+LIBS += $(DRV_WPA_LIBS)
+endif
+
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=linux
+endif
+
+OBJS_l2 += src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).c
+
+ifeq ($(CONFIG_L2_PACKET), pcap)
+ifdef CONFIG_WINPCAP
+L_CFLAGS += -DCONFIG_WINPCAP
+LIBS += -lwpcap -lpacket
+LIBS_w += -lwpcap
+else
+LIBS += -ldnet -lpcap
+endif
+endif
+
+ifeq ($(CONFIG_L2_PACKET), winpcap)
+LIBS += -lwpcap -lpacket
+LIBS_w += -lwpcap
+endif
+
+ifeq ($(CONFIG_L2_PACKET), freebsd)
+LIBS += -lpcap
+endif
+
+ifdef CONFIG_ERP
+L_CFLAGS += -DCONFIG_ERP
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
+ifdef CONFIG_EAP_TLS
+# EAP-TLS
+ifeq ($(CONFIG_EAP_TLS), dyn)
+L_CFLAGS += -DEAP_TLS_DYNAMIC
+EAPDYN += src/eap_peer/eap_tls.so
+else
+L_CFLAGS += -DEAP_TLS
+OBJS += src/eap_peer/eap_tls.c
+OBJS_h += src/eap_server/eap_server_tls.c
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_UNAUTH_TLS
+# EAP-UNAUTH-TLS
+L_CFLAGS += -DEAP_UNAUTH_TLS
+ifndef CONFIG_EAP_TLS
+OBJS += src/eap_peer/eap_tls.c
+OBJS_h += src/eap_server/eap_server_tls.c
+TLS_FUNCS=y
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_PEAP
+# EAP-PEAP
+ifeq ($(CONFIG_EAP_PEAP), dyn)
+L_CFLAGS += -DEAP_PEAP_DYNAMIC
+EAPDYN += src/eap_peer/eap_peap.so
+else
+L_CFLAGS += -DEAP_PEAP
+OBJS += src/eap_peer/eap_peap.c
+OBJS += src/eap_common/eap_peap_common.c
+OBJS_h += src/eap_server/eap_server_peap.c
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_TTLS
+# EAP-TTLS
+ifeq ($(CONFIG_EAP_TTLS), dyn)
+L_CFLAGS += -DEAP_TTLS_DYNAMIC
+EAPDYN += src/eap_peer/eap_ttls.so
+else
+L_CFLAGS += -DEAP_TTLS
+OBJS += src/eap_peer/eap_ttls.c
+OBJS_h += src/eap_server/eap_server_ttls.c
+endif
+TLS_FUNCS=y
+ifndef CONFIG_FIPS
+MS_FUNCS=y
+CHAP=y
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_MD5
+# EAP-MD5
+ifeq ($(CONFIG_EAP_MD5), dyn)
+L_CFLAGS += -DEAP_MD5_DYNAMIC
+EAPDYN += src/eap_peer/eap_md5.so
+else
+L_CFLAGS += -DEAP_MD5
+OBJS += src/eap_peer/eap_md5.c
+OBJS_h += src/eap_server/eap_server_md5.c
+endif
+CHAP=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+# backwards compatibility for old spelling
+ifdef CONFIG_MSCHAPV2
+ifndef CONFIG_EAP_MSCHAPV2
+CONFIG_EAP_MSCHAPV2=y
+endif
+endif
+
+ifdef CONFIG_EAP_MSCHAPV2
+# EAP-MSCHAPv2
+ifeq ($(CONFIG_EAP_MSCHAPV2), dyn)
+L_CFLAGS += -DEAP_MSCHAPv2_DYNAMIC
+EAPDYN += src/eap_peer/eap_mschapv2.so
+EAPDYN += src/eap_peer/mschapv2.so
+else
+L_CFLAGS += -DEAP_MSCHAPv2
+OBJS += src/eap_peer/eap_mschapv2.c
+OBJS += src/eap_peer/mschapv2.c
+OBJS_h += src/eap_server/eap_server_mschapv2.c
+endif
+MS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_GTC
+# EAP-GTC
+ifeq ($(CONFIG_EAP_GTC), dyn)
+L_CFLAGS += -DEAP_GTC_DYNAMIC
+EAPDYN += src/eap_peer/eap_gtc.so
+else
+L_CFLAGS += -DEAP_GTC
+OBJS += src/eap_peer/eap_gtc.c
+OBJS_h += src/eap_server/eap_server_gtc.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_OTP
+# EAP-OTP
+ifeq ($(CONFIG_EAP_OTP), dyn)
+L_CFLAGS += -DEAP_OTP_DYNAMIC
+EAPDYN += src/eap_peer/eap_otp.so
+else
+L_CFLAGS += -DEAP_OTP
+OBJS += src/eap_peer/eap_otp.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_SIM
+# EAP-SIM
+ifeq ($(CONFIG_EAP_SIM), dyn)
+L_CFLAGS += -DEAP_SIM_DYNAMIC
+EAPDYN += src/eap_peer/eap_sim.so
+else
+L_CFLAGS += -DEAP_SIM
+OBJS += src/eap_peer/eap_sim.c
+OBJS_h += src/eap_server/eap_server_sim.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_LEAP
+# EAP-LEAP
+ifeq ($(CONFIG_EAP_LEAP), dyn)
+L_CFLAGS += -DEAP_LEAP_DYNAMIC
+EAPDYN += src/eap_peer/eap_leap.so
+else
+L_CFLAGS += -DEAP_LEAP
+OBJS += src/eap_peer/eap_leap.c
+endif
+MS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_PSK
+# EAP-PSK
+ifeq ($(CONFIG_EAP_PSK), dyn)
+L_CFLAGS += -DEAP_PSK_DYNAMIC
+EAPDYN += src/eap_peer/eap_psk.so
+else
+L_CFLAGS += -DEAP_PSK
+OBJS += src/eap_peer/eap_psk.c src/eap_common/eap_psk_common.c
+OBJS_h += src/eap_server/eap_server_psk.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_AES=y
+NEED_AES_OMAC1=y
+NEED_AES_ENCBLOCK=y
+NEED_AES_EAX=y
+endif
+
+ifdef CONFIG_EAP_AKA
+# EAP-AKA
+ifeq ($(CONFIG_EAP_AKA), dyn)
+L_CFLAGS += -DEAP_AKA_DYNAMIC
+EAPDYN += src/eap_peer/eap_aka.so
+else
+L_CFLAGS += -DEAP_AKA
+OBJS += src/eap_peer/eap_aka.c
+OBJS_h += src/eap_server/eap_server_aka.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_PROXY
+L_CFLAGS += -DCONFIG_EAP_PROXY
+OBJS += src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).c
+include $(LOCAL_PATH)/eap_proxy_$(CONFIG_EAP_PROXY).mk
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_AKA_PRIME
+# EAP-AKA'
+ifeq ($(CONFIG_EAP_AKA_PRIME), dyn)
+L_CFLAGS += -DEAP_AKA_PRIME_DYNAMIC
+else
+L_CFLAGS += -DEAP_AKA_PRIME
+endif
+NEED_SHA256=y
+endif
+
+ifdef CONFIG_EAP_SIM_COMMON
+OBJS += src/eap_common/eap_sim_common.c
+OBJS_h += src/eap_server/eap_sim_db.c
+NEED_AES=y
+NEED_FIPS186_2_PRF=y
+endif
+
+ifdef CONFIG_EAP_FAST
+# EAP-FAST
+ifeq ($(CONFIG_EAP_FAST), dyn)
+L_CFLAGS += -DEAP_FAST_DYNAMIC
+EAPDYN += src/eap_peer/eap_fast.so
+EAPDYN += src/eap_common/eap_fast_common.c
+else
+L_CFLAGS += -DEAP_FAST
+OBJS += src/eap_peer/eap_fast.c src/eap_peer/eap_fast_pac.c
+OBJS += src/eap_common/eap_fast_common.c
+OBJS_h += src/eap_server/eap_server_fast.c
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+NEED_T_PRF=y
+endif
+
+ifdef CONFIG_EAP_PAX
+# EAP-PAX
+ifeq ($(CONFIG_EAP_PAX), dyn)
+L_CFLAGS += -DEAP_PAX_DYNAMIC
+EAPDYN += src/eap_peer/eap_pax.so
+else
+L_CFLAGS += -DEAP_PAX
+OBJS += src/eap_peer/eap_pax.c src/eap_common/eap_pax_common.c
+OBJS_h += src/eap_server/eap_server_pax.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_SAKE
+# EAP-SAKE
+ifeq ($(CONFIG_EAP_SAKE), dyn)
+L_CFLAGS += -DEAP_SAKE_DYNAMIC
+EAPDYN += src/eap_peer/eap_sake.so
+else
+L_CFLAGS += -DEAP_SAKE
+OBJS += src/eap_peer/eap_sake.c src/eap_common/eap_sake_common.c
+OBJS_h += src/eap_server/eap_server_sake.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_GPSK
+# EAP-GPSK
+ifeq ($(CONFIG_EAP_GPSK), dyn)
+L_CFLAGS += -DEAP_GPSK_DYNAMIC
+EAPDYN += src/eap_peer/eap_gpsk.so
+else
+L_CFLAGS += -DEAP_GPSK
+OBJS += src/eap_peer/eap_gpsk.c src/eap_common/eap_gpsk_common.c
+OBJS_h += src/eap_server/eap_server_gpsk.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+ifdef CONFIG_EAP_GPSK_SHA256
+L_CFLAGS += -DEAP_GPSK_SHA256
+endif
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_EAP_PWD
+L_CFLAGS += -DEAP_PWD
+OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c
+OBJS_h += src/eap_server/eap_server_pwd.c
+CONFIG_IEEE8021X_EAPOL=y
+NEED_SHA256=y
+endif
+
+ifdef CONFIG_EAP_EKE
+# EAP-EKE
+ifeq ($(CONFIG_EAP_EKE), dyn)
+L_CFLAGS += -DEAP_EKE_DYNAMIC
+EAPDYN += src/eap_peer/eap_eke.so
+else
+L_CFLAGS += -DEAP_EKE
+OBJS += src/eap_peer/eap_eke.c src/eap_common/eap_eke_common.c
+OBJS_h += src/eap_server/eap_server_eke.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+NEED_SHA256=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_WPS
+# EAP-WSC
+L_CFLAGS += -DCONFIG_WPS -DEAP_WSC
+OBJS += wps_supplicant.c
+OBJS += src/utils/uuid.c
+OBJS += src/eap_peer/eap_wsc.c src/eap_common/eap_wsc_common.c
+OBJS += src/wps/wps.c
+OBJS += src/wps/wps_common.c
+OBJS += src/wps/wps_attr_parse.c
+OBJS += src/wps/wps_attr_build.c
+OBJS += src/wps/wps_attr_process.c
+OBJS += src/wps/wps_dev_attr.c
+OBJS += src/wps/wps_enrollee.c
+OBJS += src/wps/wps_registrar.c
+OBJS_h += src/eap_server/eap_server_wsc.c
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_AES_CBC=y
+NEED_MODEXP=y
+
+ifdef CONFIG_WPS_NFC
+L_CFLAGS += -DCONFIG_WPS_NFC
+OBJS += src/wps/ndef.c
+NEED_WPS_OOB=y
+endif
+
+ifdef NEED_WPS_OOB
+L_CFLAGS += -DCONFIG_WPS_OOB
+endif
+
+ifdef CONFIG_WPS_ER
+CONFIG_WPS_UPNP=y
+L_CFLAGS += -DCONFIG_WPS_ER
+OBJS += src/wps/wps_er.c
+OBJS += src/wps/wps_er_ssdp.c
+endif
+
+ifdef CONFIG_WPS_UPNP
+L_CFLAGS += -DCONFIG_WPS_UPNP
+OBJS += src/wps/wps_upnp.c
+OBJS += src/wps/wps_upnp_ssdp.c
+OBJS += src/wps/wps_upnp_web.c
+OBJS += src/wps/wps_upnp_event.c
+OBJS += src/wps/wps_upnp_ap.c
+OBJS += src/wps/upnp_xml.c
+OBJS += src/wps/httpread.c
+OBJS += src/wps/http_client.c
+OBJS += src/wps/http_server.c
+endif
+
+ifdef CONFIG_WPS_STRICT
+L_CFLAGS += -DCONFIG_WPS_STRICT
+OBJS += src/wps/wps_validate.c
+endif
+
+ifdef CONFIG_WPS_TESTING
+L_CFLAGS += -DCONFIG_WPS_TESTING
+endif
+
+ifdef CONFIG_WPS_REG_DISABLE_OPEN
+L_CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN
+endif
+
+endif
+
+ifdef CONFIG_EAP_IKEV2
+# EAP-IKEv2
+ifeq ($(CONFIG_EAP_IKEV2), dyn)
+L_CFLAGS += -DEAP_IKEV2_DYNAMIC
+EAPDYN += src/eap_peer/eap_ikev2.so src/eap_peer/ikev2.c
+EAPDYN += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c
+else
+L_CFLAGS += -DEAP_IKEV2
+OBJS += src/eap_peer/eap_ikev2.c src/eap_peer/ikev2.c
+OBJS += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c
+OBJS_h += src/eap_server/eap_server_ikev2.c
+OBJS_h += src/eap_server/ikev2.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+NEED_MODEXP=y
+NEED_CIPHER=y
+endif
+
+ifdef CONFIG_EAP_VENDOR_TEST
+ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn)
+L_CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC
+EAPDYN += src/eap_peer/eap_vendor_test.so
+else
+L_CFLAGS += -DEAP_VENDOR_TEST
+OBJS += src/eap_peer/eap_vendor_test.c
+OBJS_h += src/eap_server/eap_server_vendor_test.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_TNC
+# EAP-TNC
+L_CFLAGS += -DEAP_TNC
+OBJS += src/eap_peer/eap_tnc.c
+OBJS += src/eap_peer/tncc.c
+OBJS_h += src/eap_server/eap_server_tnc.c
+OBJS_h += src/eap_server/tncs.c
+NEED_BASE64=y
+ifndef CONFIG_NATIVE_WINDOWS
+ifndef CONFIG_DRIVER_BSD
+LIBS += -ldl
+endif
+endif
+endif
+
+ifdef CONFIG_IEEE8021X_EAPOL
+# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication)
+L_CFLAGS += -DIEEE8021X_EAPOL
+OBJS += src/eapol_supp/eapol_supp_sm.c
+OBJS += src/eap_peer/eap.c src/eap_peer/eap_methods.c
+NEED_EAP_COMMON=y
+ifdef CONFIG_DYNAMIC_EAP_METHODS
+L_CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
+LIBS += -ldl -rdynamic
+endif
+endif
+
+ifdef CONFIG_AP
+NEED_EAP_COMMON=y
+NEED_RSN_AUTHENTICATOR=y
+L_CFLAGS += -DCONFIG_AP
+OBJS += ap.c
+L_CFLAGS += -DCONFIG_NO_RADIUS
+L_CFLAGS += -DCONFIG_NO_ACCOUNTING
+L_CFLAGS += -DCONFIG_NO_VLAN
+OBJS += src/ap/hostapd.c
+OBJS += src/ap/wpa_auth_glue.c
+OBJS += src/ap/utils.c
+OBJS += src/ap/authsrv.c
+OBJS += src/ap/ap_config.c
+OBJS += src/utils/ip_addr.c
+OBJS += src/ap/sta_info.c
+OBJS += src/ap/tkip_countermeasures.c
+OBJS += src/ap/ap_mlme.c
+OBJS += src/ap/ieee802_1x.c
+OBJS += src/eapol_auth/eapol_auth_sm.c
+OBJS += src/ap/ieee802_11_auth.c
+OBJS += src/ap/ieee802_11_shared.c
+OBJS += src/ap/drv_callbacks.c
+OBJS += src/ap/ap_drv_ops.c
+OBJS += src/ap/beacon.c
+OBJS += src/ap/bss_load.c
+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
+endif
+ifdef CONFIG_CTRL_IFACE
+OBJS += src/ap/ctrl_iface_ap.c
+endif
+
+L_CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
+OBJS += src/eap_server/eap_server.c
+OBJS += src/eap_server/eap_server_identity.c
+OBJS += src/eap_server/eap_server_methods.c
+
+ifdef CONFIG_IEEE80211N
+L_CFLAGS += -DCONFIG_IEEE80211N
+ifdef CONFIG_IEEE80211AC
+L_CFLAGS += -DCONFIG_IEEE80211AC
+endif
+endif
+
+ifdef NEED_AP_MLME
+OBJS += src/ap/wmm.c
+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
+L_CFLAGS += -DEAP_SERVER_WSC
+OBJS += src/ap/wps_hostapd.c
+OBJS += src/eap_server/eap_server_wsc.c
+endif
+ifdef CONFIG_INTERWORKING
+OBJS += src/ap/gas_serv.c
+endif
+ifdef CONFIG_HS20
+OBJS += src/ap/hs20.c
+endif
+endif
+
+ifdef NEED_RSN_AUTHENTICATOR
+L_CFLAGS += -DCONFIG_NO_RADIUS
+NEED_AES_WRAP=y
+OBJS += src/ap/wpa_auth.c
+OBJS += src/ap/wpa_auth_ie.c
+OBJS += src/ap/pmksa_cache_auth.c
+ifdef CONFIG_IEEE80211R
+OBJS += src/ap/wpa_auth_ft.c
+endif
+ifdef CONFIG_PEERKEY
+OBJS += src/ap/peerkey_auth.c
+endif
+endif
+
+ifdef CONFIG_EAP_SERVER
+L_CFLAGS += -DEAP_SERVER
+OBJS_h += src/eap_server/eap_server.c
+OBJS_h += src/eap_server/eap_server_identity.c
+OBJS_h += src/eap_server/eap_server_methods.c
+endif
+
+ifdef CONFIG_RADIUS_CLIENT
+OBJS_h += src/utils/ip_addr.c
+OBJS_h += src/radius/radius.c
+OBJS_h += src/radius/radius_client.c
+endif
+
+ifdef CONFIG_AUTHENTICATOR
+OBJS_h += src/eapol_auth/eapol_auth_sm.c
+OBJS_h += src/ap/ieee802_1x.c
+endif
+
+ifdef CONFIG_WPA_AUTHENTICATOR
+OBJS_h += src/ap/wpa_auth.c
+OBJS_h += src/ap/wpa_auth_ie.c
+OBJS_h += src/ap/pmksa_cache_auth.c
+ifdef CONFIG_IEEE80211R
+OBJS_h += src/ap/wpa_auth_ft.c
+endif
+ifdef CONFIG_PEERKEY
+OBJS_h += src/ap/peerkey_auth.c
+endif
+endif
+
+ifdef CONFIG_PCSC
+# PC/SC interface for smartcards (USIM, GSM SIM)
+L_CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC
+OBJS += src/utils/pcsc_funcs.c
+# -lpthread may not be needed depending on how pcsc-lite was configured
+ifdef CONFIG_NATIVE_WINDOWS
+#Once MinGW gets support for WinScard, -lwinscard could be used instead of the
+#dynamic symbol loading that is now used in pcsc_funcs.c
+#LIBS += -lwinscard
+else
+LIBS += -lpcsclite -lpthread
+endif
+endif
+
+ifdef CONFIG_SIM_SIMULATOR
+L_CFLAGS += -DCONFIG_SIM_SIMULATOR
+NEED_MILENAGE=y
+endif
+
+ifdef CONFIG_USIM_SIMULATOR
+L_CFLAGS += -DCONFIG_USIM_SIMULATOR
+NEED_MILENAGE=y
+endif
+
+ifdef NEED_MILENAGE
+OBJS += src/crypto/milenage.c
+NEED_AES_ENCBLOCK=y
+endif
+
+ifdef CONFIG_PKCS12
+L_CFLAGS += -DPKCS12_FUNCS
+endif
+
+ifdef CONFIG_SMARTCARD
+L_CFLAGS += -DCONFIG_SMARTCARD
+endif
+
+ifdef MS_FUNCS
+OBJS += src/crypto/ms_funcs.c
+NEED_DES=y
+NEED_MD4=y
+endif
+
+ifdef CHAP
+OBJS += src/eap_common/chap.c
+endif
+
+ifdef TLS_FUNCS
+NEED_DES=y
+# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
+OBJS += src/eap_peer/eap_tls_common.c
+OBJS_h += src/eap_server/eap_server_tls_common.c
+ifndef CONFIG_FIPS
+NEED_TLS_PRF=y
+NEED_SHA1=y
+NEED_MD5=y
+endif
+endif
+
+ifndef CONFIG_TLS
+CONFIG_TLS=openssl
+endif
+
+ifdef CONFIG_TLSV11
+L_CFLAGS += -DCONFIG_TLSV11
+endif
+
+ifdef CONFIG_TLSV12
+L_CFLAGS += -DCONFIG_TLSV12
+NEED_SHA256=y
+endif
+
+ifeq ($(CONFIG_TLS), openssl)
+ifdef TLS_FUNCS
+L_CFLAGS += -DEAP_TLS_OPENSSL
+OBJS += src/crypto/tls_openssl.c
+LIBS += -lssl
+endif
+OBJS += src/crypto/crypto_openssl.c
+OBJS_p += src/crypto/crypto_openssl.c
+ifdef NEED_FIPS186_2_PRF
+OBJS += src/crypto/fips_prf_openssl.c
+endif
+NEED_SHA256=y
+NEED_TLS_PRF_SHA256=y
+LIBS += -lcrypto
+LIBS_p += -lcrypto
+ifdef CONFIG_TLS_ADD_DL
+LIBS += -ldl
+LIBS_p += -ldl
+endif
+endif
+
+ifeq ($(CONFIG_TLS), gnutls)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_gnutls.c
+LIBS += -lgnutls -lgpg-error
+endif
+OBJS += src/crypto/crypto_gnutls.c
+OBJS_p += src/crypto/crypto_gnutls.c
+ifdef NEED_FIPS186_2_PRF
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
+endif
+LIBS += -lgcrypt
+LIBS_p += -lgcrypt
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+
+ifeq ($(CONFIG_TLS), internal)
+ifndef CONFIG_CRYPTO
+CONFIG_CRYPTO=internal
+endif
+ifdef TLS_FUNCS
+OBJS += src/crypto/crypto_internal-rsa.c
+OBJS += src/crypto/tls_internal.c
+OBJS += src/tls/tlsv1_common.c
+OBJS += src/tls/tlsv1_record.c
+OBJS += src/tls/tlsv1_cred.c
+OBJS += src/tls/tlsv1_client.c
+OBJS += src/tls/tlsv1_client_write.c
+OBJS += src/tls/tlsv1_client_read.c
+OBJS += src/tls/asn1.c
+OBJS += src/tls/rsa.c
+OBJS += src/tls/x509v3.c
+OBJS += src/tls/pkcs1.c
+OBJS += src/tls/pkcs5.c
+OBJS += src/tls/pkcs8.c
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+ifdef CONFIG_TLSV12
+NEED_TLS_PRF_SHA256=y
+endif
+NEED_MODEXP=y
+NEED_CIPHER=y
+L_CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+endif
+ifdef NEED_CIPHER
+NEED_DES=y
+OBJS += src/crypto/crypto_internal-cipher.c
+endif
+ifdef NEED_MODEXP
+OBJS += src/crypto/crypto_internal-modexp.c
+OBJS += src/tls/bignum.c
+endif
+ifeq ($(CONFIG_CRYPTO), libtomcrypt)
+OBJS += src/crypto/crypto_libtomcrypt.c
+OBJS_p += src/crypto/crypto_libtomcrypt.c
+LIBS += -ltomcrypt -ltfm
+LIBS_p += -ltomcrypt -ltfm
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), internal)
+OBJS += src/crypto/crypto_internal.c
+OBJS_p += src/crypto/crypto_internal.c
+NEED_AES_ENC=y
+L_CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+ifdef CONFIG_INTERNAL_LIBTOMMATH
+L_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST
+L_CFLAGS += -DLTM_FAST
+endif
+else
+LIBS += -ltommath
+LIBS_p += -ltommath
+endif
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_DES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD4=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), cryptoapi)
+OBJS += src/crypto/crypto_cryptoapi.c
+OBJS_p += src/crypto/crypto_cryptoapi.c
+L_CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+ifeq ($(CONFIG_TLS), none)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_none.c
+L_CFLAGS += -DEAP_TLS_NONE
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+endif
+OBJS += src/crypto/crypto_none.c
+OBJS_p += src/crypto/crypto_none.c
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+
+ifdef TLS_FUNCS
+ifdef CONFIG_SMARTCARD
+ifndef CONFIG_NATIVE_WINDOWS
+ifneq ($(CONFIG_L2_PACKET), freebsd)
+LIBS += -ldl
+endif
+endif
+endif
+endif
+
+ifndef TLS_FUNCS
+OBJS += src/crypto/tls_none.c
+ifeq ($(CONFIG_TLS), internal)
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+AESOBJS = # none so far (see below)
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-dec.c
+endif
+
+ifneq ($(CONFIG_TLS), openssl)
+NEED_INTERNAL_AES_WRAP=y
+endif
+ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+# Seems to be needed at least with BoringSSL
+NEED_INTERNAL_AES_WRAP=y
+L_CFLAGS += -DCONFIG_OPENSSL_INTERNAL_AES_WRAP
+endif
+ifdef CONFIG_FIPS
+# Have to use internal AES key wrap routines to use OpenSSL EVP since the
+# OpenSSL AES_wrap_key()/AES_unwrap_key() API is not available in FIPS mode.
+NEED_INTERNAL_AES_WRAP=y
+endif
+
+ifdef NEED_INTERNAL_AES_WRAP
+AESOBJS += src/crypto/aes-unwrap.c
+endif
+ifdef NEED_AES_EAX
+AESOBJS += src/crypto/aes-eax.c
+NEED_AES_CTR=y
+endif
+ifdef NEED_AES_CTR
+AESOBJS += src/crypto/aes-ctr.c
+endif
+ifdef NEED_AES_ENCBLOCK
+AESOBJS += src/crypto/aes-encblock.c
+endif
+ifdef NEED_AES_OMAC1
+NEED_AES_ENC=y
+ifdef CONFIG_OPENSSL_CMAC
+L_CFLAGS += -DCONFIG_OPENSSL_CMAC
+else
+AESOBJS += src/crypto/aes-omac1.c
+endif
+endif
+ifdef NEED_AES_WRAP
+NEED_AES_ENC=y
+ifdef NEED_INTERNAL_AES_WRAP
+AESOBJS += src/crypto/aes-wrap.c
+endif
+endif
+ifdef NEED_AES_CBC
+NEED_AES_ENC=y
+ifneq ($(CONFIG_TLS), openssl)
+AESOBJS += src/crypto/aes-cbc.c
+endif
+endif
+ifdef NEED_AES_ENC
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += src/crypto/aes-internal-enc.c
+endif
+endif
+ifdef NEED_AES_SIV
+AESOBJS += src/crypto/aes-siv.c
+endif
+ifdef NEED_AES
+OBJS += $(AESOBJS)
+endif
+
+SHA1OBJS =
+ifdef NEED_SHA1
+ifneq ($(CONFIG_TLS), openssl)
+SHA1OBJS += src/crypto/sha1.c
+endif
+SHA1OBJS += src/crypto/sha1-prf.c
+ifdef CONFIG_INTERNAL_SHA1
+SHA1OBJS += src/crypto/sha1-internal.c
+ifdef NEED_FIPS186_2_PRF
+SHA1OBJS += src/crypto/fips_prf_internal.c
+endif
+endif
+ifdef CONFIG_NO_WPA_PASSPHRASE
+L_CFLAGS += -DCONFIG_NO_PBKDF2
+else
+ifneq ($(CONFIG_TLS), openssl)
+SHA1OBJS += src/crypto/sha1-pbkdf2.c
+endif
+endif
+ifdef NEED_T_PRF
+SHA1OBJS += src/crypto/sha1-tprf.c
+endif
+ifdef NEED_TLS_PRF
+SHA1OBJS += src/crypto/sha1-tlsprf.c
+endif
+endif
+
+MD5OBJS =
+ifndef CONFIG_FIPS
+ifneq ($(CONFIG_TLS), openssl)
+MD5OBJS += src/crypto/md5.c
+endif
+endif
+ifdef NEED_MD5
+ifdef CONFIG_INTERNAL_MD5
+MD5OBJS += src/crypto/md5-internal.c
+endif
+OBJS += $(MD5OBJS)
+OBJS_p += $(MD5OBJS)
+endif
+
+ifdef NEED_MD4
+ifdef CONFIG_INTERNAL_MD4
+OBJS += src/crypto/md4-internal.c
+endif
+endif
+
+DESOBJS = # none needed when not internal
+ifdef NEED_DES
+ifdef CONFIG_INTERNAL_DES
+DESOBJS += src/crypto/des-internal.c
+endif
+endif
+
+ifdef CONFIG_NO_RC4
+L_CFLAGS += -DCONFIG_NO_RC4
+endif
+
+ifdef NEED_RC4
+ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
+OBJS += src/crypto/rc4.c
+endif
+endif
+endif
+
+SHA256OBJS = # none by default
+ifdef NEED_SHA256
+L_CFLAGS += -DCONFIG_SHA256
+ifneq ($(CONFIG_TLS), openssl)
+SHA256OBJS += src/crypto/sha256.c
+endif
+SHA256OBJS += src/crypto/sha256-prf.c
+ifdef CONFIG_INTERNAL_SHA256
+SHA256OBJS += src/crypto/sha256-internal.c
+endif
+ifdef NEED_TLS_PRF_SHA256
+SHA256OBJS += src/crypto/sha256-tlsprf.c
+endif
+ifdef NEED_HMAC_SHA256_KDF
+SHA256OBJS += src/crypto/sha256-kdf.c
+endif
+OBJS += $(SHA256OBJS)
+endif
+ifdef NEED_SHA384
+L_CFLAGS += -DCONFIG_SHA384
+OBJS += src/crypto/sha384-prf.c
+endif
+
+ifdef NEED_DH_GROUPS
+OBJS += src/crypto/dh_groups.c
+endif
+ifdef NEED_DH_GROUPS_ALL
+L_CFLAGS += -DALL_DH_GROUPS
+endif
+ifdef CONFIG_INTERNAL_DH_GROUP5
+ifdef NEED_DH_GROUPS
+OBJS += src/crypto/dh_group5.c
+endif
+endif
+
+ifdef NEED_ECC
+L_CFLAGS += -DCONFIG_ECC
+endif
+
+ifdef CONFIG_NO_RANDOM_POOL
+L_CFLAGS += -DCONFIG_NO_RANDOM_POOL
+else
+OBJS += src/crypto/random.c
+endif
+
+ifdef CONFIG_CTRL_IFACE
+ifeq ($(CONFIG_CTRL_IFACE), y)
+ifdef CONFIG_NATIVE_WINDOWS
+CONFIG_CTRL_IFACE=named_pipe
+else
+CONFIG_CTRL_IFACE=unix
+endif
+endif
+L_CFLAGS += -DCONFIG_CTRL_IFACE
+ifeq ($(CONFIG_CTRL_IFACE), unix)
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+endif
+ifeq ($(CONFIG_CTRL_IFACE), udp)
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+endif
+ifeq ($(CONFIG_CTRL_IFACE), named_pipe)
+L_CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE
+endif
+ifeq ($(CONFIG_CTRL_IFACE), udp-remote)
+CONFIG_CTRL_IFACE=udp
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
+endif
+OBJS += ctrl_iface.c ctrl_iface_$(CONFIG_CTRL_IFACE).c
+endif
+
+ifdef CONFIG_CTRL_IFACE_DBUS
+DBUS=y
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE
+DBUS_OBJS += dbus/dbus_old.c dbus/dbus_old_handlers.c
+ifdef CONFIG_WPS
+DBUS_OBJS += dbus/dbus_old_handlers_wps.c
+endif
+DBUS_OBJS += dbus/dbus_dict_helpers.c
+ifndef DBUS_LIBS
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
+endif
+ifndef DBUS_INCLUDE
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
+endif
+DBUS_CFLAGS += $(DBUS_INCLUDE)
+endif
+
+ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+DBUS=y
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW
+DBUS_OBJS ?= dbus/dbus_dict_helpers.c
+DBUS_OBJS += dbus/dbus_new_helpers.c
+DBUS_OBJS += dbus/dbus_new.c dbus/dbus_new_handlers.c
+ifdef CONFIG_WPS
+DBUS_OBJS += dbus/dbus_new_handlers_wps.c
+endif
+ifdef CONFIG_P2P
+DBUS_OBJS += dbus/dbus_new_handlers_p2p.c
+endif
+ifndef DBUS_LIBS
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
+endif
+ifndef DBUS_INCLUDE
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
+endif
+ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
+DBUS_OBJS += dbus/dbus_new_introspect.c
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
+endif
+DBUS_CFLAGS += $(DBUS_INCLUDE)
+endif
+
+ifdef DBUS
+DBUS_CFLAGS += -DCONFIG_DBUS
+DBUS_OBJS += dbus/dbus_common.c
+endif
+
+OBJS += $(DBUS_OBJS)
+L_CFLAGS += $(DBUS_CFLAGS)
+LIBS += $(DBUS_LIBS)
+
+ifdef CONFIG_READLINE
+OBJS_c += src/utils/edit_readline.c
+LIBS_c += -lncurses -lreadline
+else
+ifdef CONFIG_WPA_CLI_EDIT
+OBJS_c += src/utils/edit.c
+else
+OBJS_c += src/utils/edit_simple.c
+endif
+endif
+
+ifdef CONFIG_NATIVE_WINDOWS
+L_CFLAGS += -DCONFIG_NATIVE_WINDOWS
+LIBS += -lws2_32 -lgdi32 -lcrypt32
+LIBS_c += -lws2_32
+LIBS_p += -lws2_32 -lgdi32
+ifeq ($(CONFIG_CRYPTO), cryptoapi)
+LIBS_p += -lcrypt32
+endif
+endif
+
+ifdef CONFIG_NO_STDOUT_DEBUG
+L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
+ifndef CONFIG_CTRL_IFACE
+L_CFLAGS += -DCONFIG_NO_WPA_MSG
+endif
+endif
+
+ifdef CONFIG_ANDROID_LOG
+L_CFLAGS += -DCONFIG_ANDROID_LOG
+endif
+
+ifdef CONFIG_IPV6
+# for eapol_test only
+L_CFLAGS += -DCONFIG_IPV6
+endif
+
+ifdef NEED_BASE64
+OBJS += src/utils/base64.c
+endif
+
+ifdef NEED_SME
+OBJS += sme.c
+L_CFLAGS += -DCONFIG_SME
+endif
+
+OBJS += src/common/ieee802_11_common.c
+OBJS += src/common/hw_features_common.c
+
+ifdef NEED_EAP_COMMON
+OBJS += src/eap_common/eap_common.c
+endif
+
+ifndef CONFIG_MAIN
+CONFIG_MAIN=main
+endif
+
+ifdef CONFIG_DEBUG_SYSLOG
+L_CFLAGS += -DCONFIG_DEBUG_SYSLOG
+ifdef CONFIG_DEBUG_SYSLOG_FACILITY
+L_CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)"
+endif
+endif
+
+ifdef CONFIG_DEBUG_LINUX_TRACING
+L_CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING
+endif
+
+ifdef CONFIG_DEBUG_FILE
+L_CFLAGS += -DCONFIG_DEBUG_FILE
+endif
+
+ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+L_CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT
+endif
+
+ifdef CONFIG_FIPS
+L_CFLAGS += -DCONFIG_FIPS
+endif
+
+OBJS += $(SHA1OBJS) $(DESOBJS)
+
+OBJS_p += $(SHA1OBJS)
+OBJS_p += $(SHA256OBJS)
+
+ifdef CONFIG_BGSCAN_SIMPLE
+L_CFLAGS += -DCONFIG_BGSCAN_SIMPLE
+OBJS += bgscan_simple.c
+NEED_BGSCAN=y
+endif
+
+ifdef CONFIG_BGSCAN_LEARN
+L_CFLAGS += -DCONFIG_BGSCAN_LEARN
+OBJS += bgscan_learn.c
+NEED_BGSCAN=y
+endif
+
+ifdef NEED_BGSCAN
+L_CFLAGS += -DCONFIG_BGSCAN
+OBJS += bgscan.c
+endif
+
+ifdef CONFIG_AUTOSCAN_EXPONENTIAL
+L_CFLAGS += -DCONFIG_AUTOSCAN_EXPONENTIAL
+OBJS += autoscan_exponential.c
+NEED_AUTOSCAN=y
+endif
+
+ifdef CONFIG_AUTOSCAN_PERIODIC
+L_CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC
+OBJS += autoscan_periodic.c
+NEED_AUTOSCAN=y
+endif
+
+ifdef NEED_AUTOSCAN
+L_CFLAGS += -DCONFIG_AUTOSCAN
+OBJS += autoscan.c
+endif
+
+ifdef CONFIG_EXT_PASSWORD_TEST
+OBJS += src/utils/ext_password_test.c
+L_CFLAGS += -DCONFIG_EXT_PASSWORD_TEST
+NEED_EXT_PASSWORD=y
+endif
+
+ifdef NEED_EXT_PASSWORD
+OBJS += src/utils/ext_password.c
+L_CFLAGS += -DCONFIG_EXT_PASSWORD
+endif
+
+ifdef NEED_GAS
+OBJS += src/common/gas.c
+OBJS += gas_query.c
+L_CFLAGS += -DCONFIG_GAS
+NEED_OFFCHANNEL=y
+endif
+
+ifdef NEED_OFFCHANNEL
+OBJS += offchannel.c
+L_CFLAGS += -DCONFIG_OFFCHANNEL
+endif
+
+OBJS += src/drivers/driver_common.c
+
+OBJS_wpa_rm := ctrl_iface.c ctrl_iface_unix.c
+OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.c
+ifdef CONFIG_AUTHENTICATOR
+OBJS_wpa += tests/link_test.c
+endif
+OBJS_wpa += $(OBJS_l2)
+OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c
+OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.c
+OBJS_t += src/radius/radius_client.c
+OBJS_t += src/radius/radius.c
+ifndef CONFIG_AP
+OBJS_t += src/utils/ip_addr.c
+endif
+OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.c
+OBJS += $(CONFIG_MAIN).c
+
+ifdef CONFIG_PRIVSEP
+OBJS_priv += $(OBJS_d) src/drivers/drivers.c
+OBJS_priv += $(OBJS_l2)
+OBJS_priv += src/utils/os_$(CONFIG_OS).c
+OBJS_priv += src/utils/$(CONFIG_ELOOP).c
+OBJS_priv += src/utils/common.c
+OBJS_priv += src/utils/wpa_debug.c
+OBJS_priv += src/utils/wpabuf.c
+OBJS_priv += wpa_priv.c
+ifdef CONFIG_DRIVER_NL80211
+OBJS_priv += src/common/ieee802_11_common.c
+endif
+OBJS += src/l2_packet/l2_packet_privsep.c
+OBJS += src/drivers/driver_privsep.c
+EXTRA_progs += wpa_priv
+else
+OBJS += $(OBJS_d) src/drivers/drivers.c
+OBJS += $(OBJS_l2)
+endif
+
+ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+L_CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED
+OBJS += src/drivers/ndis_events.c
+EXTRALIBS += -loleaut32 -lole32 -luuid
+ifdef PLATFORMSDKLIB
+EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib
+else
+EXTRALIBS += WbemUuid.Lib
+endif
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+########################
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := wpa_cli
+LOCAL_MODULE_TAGS := debug
+LOCAL_SHARED_LIBRARIES := libc libcutils liblog
+LOCAL_CFLAGS := $(L_CFLAGS)
+LOCAL_SRC_FILES := $(OBJS_c)
+LOCAL_C_INCLUDES := $(INCLUDES)
+include $(BUILD_EXECUTABLE)
+
+########################
+include $(CLEAR_VARS)
+LOCAL_MODULE := wpa_supplicant
+ifdef CONFIG_DRIVER_CUSTOM
+LOCAL_STATIC_LIBRARIES := libCustomWifi
+endif
+ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)
+LOCAL_STATIC_LIBRARIES += $(BOARD_WPA_SUPPLICANT_PRIVATE_LIB)
+endif
+LOCAL_SHARED_LIBRARIES := libc libcutils liblog libdbus
+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)
+include $(BUILD_EXECUTABLE)
+
+########################
+#
+#include $(CLEAR_VARS)
+#LOCAL_MODULE := eapol_test
+#ifdef CONFIG_DRIVER_CUSTOM
+#LOCAL_STATIC_LIBRARIES := libCustomWifi
+#endif
+#LOCAL_SHARED_LIBRARIES := libc libcrypto libssl
+#LOCAL_CFLAGS := $(L_CFLAGS)
+#LOCAL_SRC_FILES := $(OBJS_t)
+#LOCAL_C_INCLUDES := $(INCLUDES)
+#include $(BUILD_EXECUTABLE)
+#
+########################
+#
+#local_target_dir := $(TARGET_OUT)/etc/wifi
+#
+#include $(CLEAR_VARS)
+#LOCAL_MODULE := wpa_supplicant.conf
+#LOCAL_MODULE_CLASS := ETC
+#LOCAL_MODULE_PATH := $(local_target_dir)
+#LOCAL_SRC_FILES := $(LOCAL_MODULE)
+#include $(BUILD_PREBUILT)
+#
+########################
+
+include $(CLEAR_VARS)
+LOCAL_MODULE = libwpa_client
+LOCAL_CFLAGS = $(L_CFLAGS)
+LOCAL_SRC_FILES = src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c
+LOCAL_C_INCLUDES = $(INCLUDES)
+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/hostap/wpa_supplicant/ChangeLog b/hostap/wpa_supplicant/ChangeLog
new file mode 100644
index 0000000..facd90e
--- /dev/null
+++ b/hostap/wpa_supplicant/ChangeLog
@@ -0,0 +1,2134 @@
+ChangeLog for wpa_supplicant
+
+2015-09-27 - v2.5
+	* fixed P2P validation of SSID element length before copying it
+	  [http://w1.fi/security/2015-1/] (CVE-2015-1863)
+	* fixed WPS UPnP vulnerability with HTTP chunked transfer encoding
+	  [http://w1.fi/security/2015-2/] (CVE-2015-4141)
+	* fixed WMM Action frame parser (AP mode)
+	  [http://w1.fi/security/2015-3/] (CVE-2015-4142)
+	* fixed EAP-pwd peer missing payload length validation
+	  [http://w1.fi/security/2015-4/]
+	  (CVE-2015-4143, CVE-2015-4144, CVE-2015-4145, CVE-2015-4146)
+	* fixed validation of WPS and P2P NFC NDEF record payload length
+	  [http://w1.fi/security/2015-5/]
+	* nl80211:
+	  - added VHT configuration for IBSS
+	  - fixed vendor command handling to check OUI properly
+	  - allow driver-based roaming to change ESS
+	* added AVG_BEACON_RSSI to SIGNAL_POLL output
+	* wpa_cli: added tab completion for number of commands
+	* removed unmaintained and not yet completed SChannel/CryptoAPI support
+	* modified Extended Capabilities element use in Probe Request frames to
+	  include all cases if any of the values are non-zero
+	* added support for dynamically creating/removing a virtual interface
+	  with interface_add/interface_remove
+	* added support for hashed password (NtHash) in EAP-pwd peer
+	* added support for memory-only PSK/passphrase (mem_only_psk=1 and
+	  CTRL-REQ/RSP-PSK_PASSPHRASE)
+	* P2P
+	  - optimize scan frequencies list when re-joining a persistent group
+	  - fixed number of sequences with nl80211 P2P Device interface
+	  - added operating class 125 for P2P use cases (this allows 5 GHz
+	    channels 161 and 169 to be used if they are enabled in the current
+	    regulatory domain)
+	  - number of fixes to P2PS functionality
+	  - do not allow 40 MHz co-ex PRI/SEC switch to force MCC
+	  - extended support for preferred channel listing
+	* D-Bus:
+	  - fixed WPS property of fi.w1.wpa_supplicant1.BSS interface
+	  - fixed PresenceRequest to use group interface
+	  - added new signals: FindStopped, WPS pbc-overlap,
+	    GroupFormationFailure, WPS timeout, InvitationReceived
+	  - added new methods: WPS Cancel, P2P Cancel, Reconnect, RemoveClient
+	  - added manufacturer info
+	* added EAP-EKE peer support for deriving Session-Id
+	* added wps_priority configuration parameter to set the default priority
+	  for all network profiles added by WPS
+	* added support to request a scan with specific SSIDs with the SCAN
+	  command (optional "ssid <hexdump>" arguments)
+	* removed support for WEP40/WEP104 as a group cipher with WPA/WPA2
+	* fixed SAE group selection in an error case
+	* modified SAE routines to be more robust and PWE generation to be
+	  stronger against timing attacks
+	* added support for Brainpool Elliptic Curves with SAE
+	* added support for CCMP-256 and GCMP-256 as group ciphers with FT
+	* fixed BSS selection based on estimated throughput
+	* added option to disable TLSv1.0 with OpenSSL
+	  (phase1="tls_disable_tlsv1_0=1")
+	* added Fast Session Transfer (FST) module
+	* fixed OpenSSL PKCS#12 extra certificate handling
+	* fixed key derivation for Suite B 192-bit AKM (this breaks
+	  compatibility with the earlier version)
+	* added RSN IE to Mesh Peering Open/Confirm frames
+	* number of small fixes
+
+2015-03-15 - v2.4
+	* allow OpenSSL cipher configuration to be set for internal EAP server
+	  (openssl_ciphers parameter)
+	* fixed number of small issues based on hwsim test case failures and
+	  static analyzer reports
+	* P2P:
+	  - add new=<0/1> flag to P2P-DEVICE-FOUND events
+	  - add passive channels in invitation response from P2P Client
+	  - enable nl80211 P2P_DEVICE support by default
+	  - fix regresssion in disallow_freq preventing search on social
+	    channels
+	  - fix regressions in P2P SD query processing
+	  - try to re-invite with social operating channel if no common channels
+	    in invitation
+	  - allow cross connection on parent interface (this fixes number of
+	    use cases with nl80211)
+	  - add support for P2P services (P2PS)
+	  - add p2p_go_ctwindow configuration parameter to allow GO CTWindow to
+	    be configured
+	* increase postponing of EAPOL-Start by one second with AP/GO that
+	  supports WPS 2.0 (this makes it less likely to trigger extra roundtrip
+	  of identity frames)
+	* add support for PMKSA caching with SAE
+	* add support for control mesh BSS (IEEE 802.11s) operations
+	* fixed number of issues with D-Bus P2P commands
+	* fixed regression in ap_scan=2 special case for WPS
+	* fixed macsec_validate configuration
+	* add a workaround for incorrectly behaving APs that try to use
+	  EAPOL-Key descriptor version 3 when the station supports PMF even if
+	  PMF is not enabled on the AP
+	* allow TLS v1.1 and v1.2 to be negotiated by default; previous behavior
+	  of disabling these can be configured to work around issues with broken
+	  servers with phase1="tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1"
+	* add support for Suite B (128-bit and 192-bit level) key management and
+	  cipher suites
+	* add WMM-AC support (WMM_AC_ADDTS/WMM_AC_DELTS)
+	* improved BSS Transition Management processing
+	* add support for neighbor report
+	* add support for link measurement
+	* fixed expiration of BSS entry with all-zeros BSSID
+	* add optional LAST_ID=x argument to LIST_NETWORK to allow all
+	  configured networks to be listed even with huge number of network
+	  profiles
+	* add support for EAP Re-Authentication Protocol (ERP)
+	* fixed EAP-IKEv2 fragmentation reassembly
+	* improved PKCS#11 configuration for OpenSSL
+	* set stdout to be line-buffered
+	* add TDLS channel switch configuration
+	* add support for MAC address randomization in scans with nl80211
+	* enable HT for IBSS if supported by the driver
+	* add BSSID black and white lists (bssid_blacklist, bssid_whitelist)
+	* add support for domain_suffix_match with GnuTLS
+	* add OCSP stapling client support with GnuTLS
+	* include peer certificate in EAP events even without a separate probe
+	  operation; old behavior can be restored with cert_in_cb=0
+	* add peer ceritficate alt subject name to EAP events
+	  (CTRL-EVENT-EAP-PEER-ALT)
+	* add domain_match network profile parameter (similar to
+	  domain_suffix_match, but full match is required)
+	* enable AP/GO mode HT Tx STBC automatically based on driver support
+	* add ANQP-QUERY-DONE event to provide information on ANQP parsing
+	  status
+	* allow passive scanning to be forced with passive_scan=1
+	* add a workaround for Linux packet socket behavior when interface is in
+	  bridge
+	* increase 5 GHz band preference in BSS selection (estimate SNR, if info
+	  not available from driver; estimate maximum throughput based on common
+	  HT/VHT/specific TX rate support)
+	* add INTERWORKING_ADD_NETWORK ctrl_iface command; this can be used to
+	  implement Interworking network selection behavior in upper layers
+	  software components
+	* add optional reassoc_same_bss_optim=1 (disabled by default)
+	  optimization to avoid unnecessary Authentication frame exchange
+	* extend TDLS frame padding workaround to cover all packets
+	* allow wpa_supplicant to recover nl80211 functionality if the cfg80211
+	  module gets removed and reloaded without restarting wpa_supplicant
+	* allow hostapd DFS implementation to be used in wpa_supplicant AP mode
+
+2014-10-09 - v2.3
+	* fixed number of minor issues identified in static analyzer warnings
+	* fixed wfd_dev_info to be more careful and not read beyond the buffer
+	  when parsing invalid information for P2P-DEVICE-FOUND
+	* extended P2P and GAS query operations to support drivers that have
+	  maximum remain-on-channel time below 1000 ms (500 ms is the current
+	  minimum supported value)
+	* added p2p_search_delay parameter to make the default p2p_find delay
+	  configurable
+	* improved P2P operating channel selection for various multi-channel
+	  concurrency cases
+	* fixed some TDLS failure cases to clean up driver state
+	* fixed dynamic interface addition cases with nl80211 to avoid adding
+	  ifindex values to incorrect interface to skip foreign interface events
+	  properly
+	* added TDLS workaround for some APs that may add extra data to the
+	  end of a short frame
+	* fixed EAP-AKA' message parser with multiple AT_KDF attributes
+	* added configuration option (p2p_passphrase_len) to allow longer
+	  passphrases to be generated for P2P groups
+	* fixed IBSS channel configuration in some corner cases
+	* improved HT/VHT/QoS parameter setup for TDLS
+	* modified D-Bus interface for P2P peers/groups
+	* started to use constant time comparison for various password and hash
+	  values to reduce possibility of any externally measurable timing
+	  differences
+	* extended explicit clearing of freed memory and expired keys to avoid
+	  keeping private data in memory longer than necessary
+	* added optional scan_id parameter to the SCAN command to allow manual
+	  scan requests for active scans for specific configured SSIDs
+	* fixed CTRL-EVENT-REGDOM-CHANGE event init parameter value
+	* added option to set Hotspot 2.0 Rel 2 update_identifier in network
+	  configuration to support external configuration
+	* modified Android PNO functionality to send Probe Request frames only
+	  for hidden SSIDs (based on scan_ssid=1)
+	* added generic mechanism for adding vendor elements into frames at
+	  runtime (VENDOR_ELEM_ADD, VENDOR_ELEM_GET, VENDOR_ELEM_REMOVE)
+	* added fields to show unrecognized vendor elements in P2P_PEER
+	* removed EAP-TTLS/MSCHAPv2 interoperability workaround so that
+	  MS-CHAP2-Success is required to be present regardless of
+	  eap_workaround configuration
+	* modified EAP fast session resumption to allow results to be used only
+	  with the same network block that generated them
+	* extended freq_list configuration to apply for sched_scan as well as
+	  normal scan
+	* modified WPS to merge mixed-WPA/WPA2 credentials from a single session
+	* fixed nl80211/RTM_DELLINK processing when a P2P GO interface is
+	  removed from a bridge
+	* fixed number of small P2P issues to make negotiations more robust in
+	  corner cases
+	* added experimental support for using temporary, random local MAC
+	  address (mac_addr and preassoc_mac_addr parameters); this is disabled
+	  by default (i.e., previous behavior of using permanent address is
+	  maintained if configuration is not changed)
+	* added D-Bus interface for setting/clearing WFD IEs
+	* fixed TDLS AID configuration for VHT
+	* modified -m<conf> configuration file to be used only for the P2P
+	  non-netdev management device and do not load this for the default
+	  station interface or load the station interface configuration for
+	  the P2P management interface
+	* fixed external MAC address changes while wpa_supplicant is running
+	* started to enable HT (if supported by the driver) for IBSS
+	* fixed wpa_cli action script execution to use more robust mechanism
+	  (CVE-2014-3686)
+
+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)
+	* removed unmaintained driver wrappers broadcom, iphone, osx, ralink,
+	  hostap, madwifi (hostap and madwifi remain available for hostapd;
+	  their wpa_supplicant functionality is obsoleted by wext)
+	* improved debug logging (human readable event names, interface name
+	  included in more entries)
+	* changed AP mode behavior to enable WPS only for open and
+	  WPA/WPA2-Personal configuration
+	* improved P2P concurrency operations
+	  - better coordination of concurrent scan and P2P search operations
+	  - avoid concurrent remain-on-channel operation requests by canceling
+	    previous operations prior to starting a new one
+	  - reject operations that would require multi-channel concurrency if
+	    the driver does not support it
+	  - add parameter to select whether STA or P2P connection is preferred
+	    if the driver cannot support both at the same time
+	  - allow driver to indicate channel changes
+	  - added optional delay=<search delay in milliseconds> parameter for
+	    p2p_find to avoid taking all radio resources
+	  - use 500 ms p2p_find search delay by default during concurrent
+	    operations
+	  - allow all channels in GO Negotiation if the driver supports
+	    multi-channel concurrency
+	* added number of small changes to make it easier for static analyzers
+	  to understand the implementation
+	* fixed number of small bugs (see git logs for more details)
+	* nl80211: number of updates to use new cfg80211/nl80211 functionality
+	  - replace monitor interface with nl80211 commands for AP mode
+	  - additional information for driver-based AP SME
+	  - STA entry authorization in RSN IBSS
+	* EAP-pwd:
+	  - fixed KDF for group 21 and zero-padding
+	  - added support for fragmentation
+	  - increased maximum number of hunting-and-pecking iterations
+	* avoid excessive Probe Response retries for broadcast Probe Request
+	  frames (only with drivers using wpa_supplicant AP mode SME/MLME)
+	* added "GET country" ctrl_iface command
+	* do not save an invalid network block in wpa_supplicant.conf to avoid
+	  problems reading the file on next start
+	* send STA connected/disconnected ctrl_iface events to both the P2P
+	  group and parent interfaces
+	* added preliminary support for using TLS v1.2 (CONFIG_TLSV12=y)
+	* added "SET pno <1/0>" ctrl_iface command to start/stop preferred
+	  network offload with sched_scan driver command
+	* merged in number of changes from Android repository for P2P, nl80211,
+	  and build parameters
+	* changed P2P GO mode configuration to use driver capabilities to
+	  automatically enable HT operations when supported
+	* added "wpa_cli status wps" command to fetch WPA2-Personal passhrase
+	  for WPS use cases in AP mode
+	* EAP-AKA: keep pseudonym identity across EAP exchanges to match EAP-SIM
+	  behavior
+	* improved reassociation behavior in cases where association is rejected
+	  or when an AP disconnects us to handle common load balancing
+	  mechanisms
+	  - try to avoid extra scans when the needed information is available
+	* added optional "join" argument for p2p_prov_disc ctrl_iface command
+	* added group ifname to P2P-PROV-DISC-* events
+	* added P2P Device Address to AP-STA-DISCONNECTED event and use
+	  p2p_dev_addr parameter name with AP-STA-CONNECTED
+	* added workarounds for WPS PBC overlap detection for some P2P use cases
+	  where deployed stations work incorrectly
+	* optimize WPS connection speed by disconnecting prior to WPS scan and
+	  by using single channel scans when AP channel is known
+	* PCSC and SIM/USIM improvements:
+	  - accept 0x67 (Wrong length) as a response to READ RECORD to fix
+	    issues with some USIM cards
+	  - try to read MNC length from SIM/USIM
+	  - build realm according to 3GPP TS 23.003 with identity from the SIM
+	  - allow T1 protocol to be enabled
+	* added more WPS and P2P information available through D-Bus
+	* improve P2P negotiation robustness
+	  - extra waits to get ACK frames through
+	  - longer timeouts for cases where deployed devices have been
+	    identified have issues meeting the specification requirements
+	  - more retries for some P2P frames
+	  - handle race conditions in GO Negotiation start by both devices
+	  - ignore unexpected GO Negotiation Response frame
+	* added support for libnl 3.2 and newer
+	* added P2P persistent group info to P2P_PEER data
+	* maintain a list of P2P Clients for persistent group on GO
+	* AP: increased initial group key handshake retransmit timeout to 500 ms
+	* added optional dev_id parameter for p2p_find
+	* added P2P-FIND-STOPPED ctrl_iface event
+	* fixed issues in WPA/RSN element validation when roaming with ap_scan=1
+	  and driver-based BSS selection
+	* do not expire P2P peer entries while connected with the peer in a
+	  group
+	* fixed WSC element inclusion in cases where P2P is disabled
+	* AP: added a WPS workaround for mixed mode AP Settings with Windows 7
+	* EAP-SIM: fixed AT_COUNTER_TOO_SMALL use
+	* EAP-SIM/AKA: append realm to pseudonym identity
+	* EAP-SIM/AKA: store pseudonym identity in network configuration to
+	  allow it to persist over multiple EAP sessions and wpa_supplicant
+	  restarts
+	* EAP-AKA': updated to RFC 5448 (username prefixes changed); note: this
+	  breaks interoperability with older versions
+	* added support for WFA Hotspot 2.0
+	  - GAS/ANQP to fetch network information
+	  - credential configuration and automatic network selections based on
+	    credential match with ANQP information
+	* limited PMKSA cache entries to be used only with the network context
+	  that was used to create them
+	* improved PMKSA cache expiration to avoid unnecessary disconnections
+	* adjusted bgscan_simple fast-scan backoff to avoid too frequent
+	  background scans
+	* removed ctrl_iface event on P2P PD Response in join-group case
+	* added option to fetch BSS table entry based on P2P Device Address
+	  ("BSS p2p_dev_addr=<P2P Device Address>")
+	* added BSS entry age to ctrl_iface BSS command output
+	* added optional MASK=0xH option for ctrl_iface BSS command to select
+	  which fields are included in the response
+	* added optional RANGE=ALL|N1-N2 option for ctrl_iface BSS command to
+	  fetch information about several BSSes in one call
+	* simplified licensing terms by selecting the BSD license as the only
+	  alternative
+	* added "P2P_SET disallow_freq <freq list>" ctrl_iface command to
+	  disable channels from P2P use
+	* added p2p_pref_chan configuration parameter to allow preferred P2P
+	  channels to be specified
+	* added support for advertising immediate availability of a WPS
+	  credential for P2P use cases
+	* optimized scan operations for P2P use cases (use single channel scan
+	  for a specific SSID when possible)
+	* EAP-TTLS: fixed peer challenge generation for MSCHAPv2
+	* SME: do not use reassociation after explicit disconnection request
+	  (local or a notification from an AP)
+	* added support for sending debug info to Linux tracing (-T on command
+	  line)
+	* added support for using Deauthentication reason code 3 as an
+	  indication of P2P group termination
+	* added wps_vendor_ext_m1 configuration parameter to allow vendor
+	  specific attributes to be added to WPS M1
+	* started using separate TLS library context for tunneled TLS
+	  (EAP-PEAP/TLS, EAP-TTLS/TLS, EAP-FAST/TLS) to support different CA
+	  certificate configuration between Phase 1 and Phase 2
+	* added optional "auto" parameter for p2p_connect to request automatic
+	  GO Negotiation vs. join-a-group selection
+	* added disabled_scan_offload parameter to disable automatic scan
+	  offloading (sched_scan)
+	* added optional persistent=<network id> parameter for p2p_connect to
+	  allow forcing of a specific SSID/passphrase for GO Negotiation
+	* added support for OBSS scan requests and 20/40 BSS coexistence reports
+	* reject PD Request for unknown group
+	* removed scripts and notes related to Windows binary releases (which
+	  have not been used starting from 1.x)
+	* added initial support for WNM operations
+	  - Keep-alive based on BSS max idle period
+	  - WNM-Sleep Mode
+	  - minimal BSS Transition Management processing
+	* added autoscan module to control scanning behavior while not connected
+	  - autoscan_periodic and autoscan_exponential modules
+	* added new WPS NFC ctrl_iface mechanism
+	  - added initial support NFC connection handover
+	  - removed obsoleted WPS_OOB command (including support for deprecated
+	    UFD config_method)
+	* added optional framework for external password storage ("ext:<name>")
+	* wpa_cli: added optional support for controlling wpa_supplicant
+	  remotely over UDP (CONFIG_CTRL_IFACE=udp-remote) for testing purposes
+	* wpa_cli: extended tab completion to more commands
+	* changed SSID output to use printf-escaped strings instead of masking
+	  of non-ASCII characters
+	  - SSID can now be configured in the same format: ssid=P"abc\x00test"
+	* removed default ACM=1 from AC_VO and AC_VI
+	* added optional "ht40" argument for P2P ctrl_iface commands to allow
+	  40 MHz channels to be requested on the 5 GHz band
+	* added optional parameters for p2p_invite command to specify channel
+	  when reinvoking a persistent group as the GO
+	* improved FIPS mode builds with OpenSSL
+	  - "make fips" with CONFIG_FIPS=y to build wpa_supplicant with the
+	    OpenSSL FIPS object module
+	  - replace low level OpenSSL AES API calls to use EVP
+	  - use OpenSSL keying material exporter when possible
+	  - do not export TLS keys in FIPS mode
+	  - remove MD5 from CONFIG_FIPS=y builds
+	  - use OpenSSL function for PKBDF2 passphrase-to-PSK
+	  - use OpenSSL HMAC implementation
+	  - mix RAND_bytes() output into random_get_bytes() to force OpenSSL
+	    DRBG to be used in FIPS mode
+	  - use OpenSSL CMAC implementation
+	* added mechanism to disable TLS Session Ticket extension
+	  - a workaround for servers that do not support TLS extensions that
+	    was enabled by default in recent OpenSSL versions
+	  - tls_disable_session_ticket=1
+	  - automatically disable TLS Session Ticket extension by default when
+	    using EAP-TLS/PEAP/TTLS (i.e., only use it with EAP-FAST)
+	* changed VENDOR-TEST EAP method to use proper private enterprise number
+	  (this will not interoperate with older versions)
+	* disable network block temporarily on authentication failures
+	* improved WPS AP selection during WPS PIN iteration
+	* added support for configuring GCMP cipher for IEEE 802.11ad
+	* added support for Wi-Fi Display extensions
+	  - WFD_SUBELEMENT_SET ctrl_iface command to configure WFD subelements
+	  - SET wifi_display <0/1> to disable/enable WFD support
+	  - WFD service discovery
+	  - an external program is needed to manage the audio/video streaming
+	    and codecs
+	* optimized scan result use for network selection
+	  - use the internal BSS table instead of raw scan results
+	  - allow unnecessary scans to be skipped if fresh information is
+	    available (e.g., after GAS/ANQP round for Interworking)
+	* added support for 256-bit AES with internal TLS implementation
+	* allow peer to propose channel in P2P invitation process for a
+	  persistent group
+	* added disallow_aps parameter to allow BSSIDs/SSIDs to be disallowed
+	  from network selection
+	* re-enable the networks disabled during WPS operations
+	* allow P2P functionality to be disabled per interface (p2p_disabled=1)
+	* added secondary device types into P2P_PEER output
+	* added an option to disable use of a separate P2P group interface
+	  (p2p_no_group_iface=1)
+	* fixed P2P Bonjour SD to match entries with both compressed and not
+	  compressed domain name format and support multiple Bonjour PTR matches
+	  for the same key
+	* use deauthentication instead of disassociation for all disconnection
+	  operations; this removes the now unused disassociate() wpa_driver_ops
+	  callback
+	* optimized PSK generation on P2P GO by caching results to avoid
+	  multiple PBKDF2 operations
+	* added okc=1 global configuration parameter to allow OKC to be enabled
+	  by default for all network blocks
+	* added a workaround for WPS PBC session overlap detection to avoid
+	  interop issues with deployed station implementations that do not
+	  remove active PBC indication from Probe Request frames properly
+	* added basic support for 60 GHz band
+	* extend EAPOL frames processing workaround for roaming cases
+	  (postpone processing of unexpected EAPOL frame until association
+	  event to handle reordered events)
+
+2012-05-10 - v1.0
+	* bsd: Add support for setting HT values in IFM_MMASK.
+	* Delay STA entry removal until Deauth/Disassoc TX status in AP mode.
+	  This allows the driver to use PS buffering of Deauthentication and
+	  Disassociation frames when the STA is in power save sleep. Only
+	  available with drivers that provide TX status events for Deauth/
+	  Disassoc frames (nl80211).
+	* Drop oldest unknown BSS table entries first. This makes it less
+	  likely to hit connection issues in environments with huge number
+	  of visible APs.
+	* Add systemd support.
+	* Add support for setting the syslog facility from the config file
+	  at build time.
+	* atheros: Add support for IEEE 802.11w configuration.
+	* AP mode: Allow enable HT20 if driver supports it, by setting the
+	  config parameter ieee80211n.
+	* Allow AP mode to disconnect STAs based on low ACK condition (when
+	  the data connection is not working properly, e.g., due to the STA
+	  going outside the range of the AP). Disabled by default, enable by
+	  config option disassoc_low_ack.
+	* nl80211:
+	  - Support GTK rekey offload.
+	  - Support PMKSA candidate events. This adds support for RSN
+	    pre-authentication with nl80211 interface and drivers that handle
+	    roaming internally.
+	* dbus:
+	  - Add a DBus signal for EAP SM requests, emitted on the Interface
+	    object.
+	  - Export max scan ssids supported by the driver as MaxScanSSID.
+	  - Add signal Certification for information about server certification.
+	  - Add BSSExpireAge and BSSExpireCount interface properties and
+	    support set/get, which allows for setting BSS cache expiration age
+	    and expiration scan count.
+	  - Add ConfigFile to AddInterface properties.
+	  - Add Interface.Country property and support to get/set the value.
+	  - Add DBus property CurrentAuthMode.
+	  - P2P DBus API added.
+	  - Emit property changed events (for property BSSs) when adding/
+	    removing BSSs.
+	  - Treat '' in SSIDs of Interface.Scan as a request for broadcast
+	    scan, instead of ignoring it.
+	  - Add DBus getter/setter for FastReauth.
+	  - Raise PropertiesChanged on org.freedesktop.DBus.Properties.
+	* wpa_cli:
+	  - Send AP-STA-DISCONNECTED event when an AP disconnects a station
+	    due to inactivity.
+	  - Make second argument to set command optional. This can be used to
+	    indicate a zero length value.
+	  - Add signal_poll command.
+	  - Add bss_expire_age and bss_expire_count commands to set/get BSS
+	    cache expiration age and expiration scan count.
+	  - Add ability to set scan interval (the time in seconds wpa_s waits
+	    before requesting a new scan after failing to find a suitable
+	    network in scan results) using scan_interval command.
+	  - Add event CTRL-EVENT-ASSOC-REJECT for association rejected.
+	  - Add command get version, that returns wpa_supplicant version string.
+	  - Add command sta_autoconnect for disabling automatic reconnection
+	    on receiving disconnection event.
+	  - Setting bssid parameter to an empty string "" or any can now be
+	    used to clear the bssid_set flag in a network block, i.e., to remove
+	    bssid filtering.
+	  - Add tdls_testing command to add a special testing feature for
+	    changing TDLS behavior. Build param CONFIG_TDLS_TESTING must be
+	    enabled as well.
+	  - For interworking, add wpa_cli commands interworking_select,
+	    interworking_connect, anqp_get, fetch_anqp, and stop_fetch_anqp.
+	  - Many P2P commands were added. See README-P2P.
+	  - Many WPS/WPS ER commands - see WPS/WPS ER sections for details.
+	  - Allow set command to change global config parameters.
+	  - Add log_level command, which can be used to display the current
+	    debugging level and to change the log level during run time.
+	  - Add note command, which can be used to insert notes to the debug
+	    log.
+	  - Add internal line edit implementation. CONFIG_WPA_CLI_EDIT=y
+	    can now be used to build wpa_cli with internal implementation of
+	    line editing and history support. This can be used as a replacement
+	    for CONFIG_READLINE=y.
+	* AP mode: Add max_num_sta config option, which can be used to limit
+	  the number of stations allowed to connect to the AP.
+	* Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad
+	  config file.
+	* wext: Increase scan timeout from 5 to 10 seconds.
+	* Add blacklist command, allowing an external program to
+	  manage the BSS blacklist and display its current contents.
+	* WPS:
+	  - Add wpa_cli wps_pin get command for generating random PINs. This can
+	    be used in a UI to generate a PIN without starting WPS (or P2P)
+	    operation.
+	  - Set RF bands based on driver capabilities, instead of hardcoding
+	    them.
+	  - Add mechanism for indicating non-standard WPS errors.
+	  - Add CONFIG_WPS_REG_DISABLE_OPEN=y option to disable open networks
+	    by default.
+	  - Add wps_ap_pin cli command for wpa_supplicant AP mode.
+	  - Add wps_check_pin cli command for processing PIN from user input.
+	    UIs can use this command to process a PIN entered by a user and to
+	    validate the checksum digit (if present).
+	  - Cancel WPS operation on PBC session overlap detection.
+	  - New wps_cancel command in wpa_cli will cancel a pending WPS
+	    operation.
+	  - wpa_cli action: Add WPS_EVENT_SUCCESS and WPS_EVENT_FAIL handlers.
+	  - Trigger WPS config update on Manufacturer, Model Name, Model
+	    Number, and Serial Number changes.
+	  - Fragment size is now configurable for EAP-WSC peer. Use
+	    wpa_cli set wps_fragment_size <val>.
+	  - Disable AP PIN after 10 consecutive failures. Slow down attacks on
+	    failures up to 10.
+	  - Allow AP to start in Enrollee mode without AP PIN for probing, to
+	    be compatible with Windows 7.
+	  - Add Config Error into WPS-FAIL events to provide more info to the
+	    user on how to resolve the issue.
+	  - Label and Display config methods are not allowed to be enabled
+	    at the same time, since it is unclear which PIN to use if both
+	    methods are advertised.
+	  - When controlling multiple interfaces:
+	     - apply WPS commands to all interfaces configured to use WPS
+	     - apply WPS config changes to all interfaces that use WPS
+	     - when an attack is detected on any interface, disable AP PIN on
+	       all interfaces
+	* WPS ER:
+	  - Add special AP Setup Locked mode to allow read only ER.
+	    ap_setup_locked=2 can now be used to enable a special mode where
+	    WPS ER can learn the current AP settings, but cannot change them.
+	  - Show SetSelectedRegistrar events as ctrl_iface events
+	  - Add wps_er_set_config to enroll a network based on a local
+	    network configuration block instead of having to (re-)learn the
+	    current AP settings with wps_er_learn.
+	  - Allow AP filtering based on IP address, add ctrl_iface event for
+	    learned AP settings, add wps_er_config command to configure an AP.
+	* WPS 2.0: Add support for WPS 2.0 (CONFIG_WPS2)
+	  - Add build option CONFIG_WPS_EXTENSIBILITY_TESTING to enable tool
+	    for testing protocol extensibility.
+	  - Add build option CONFIG_WPS_STRICT to allow disabling of WPS
+	    workarounds.
+	  - Add support for AuthorizedMACs attribute.
+	* TDLS:
+	  - Propogate TDLS related nl80211 capability flags from kernel and
+	    add them as driver capability flags. If the driver doesn't support
+	    capabilities, assume TDLS is supported internally. When TDLS is
+	    explicitly not supported, disable all user facing TDLS operations.
+	  - Allow TDLS to be disabled at runtime (mostly for testing).
+	    Use set tdls_disabled.
+	  - Honor AP TDLS settings that prohibit/allow TDLS.
+	  - Add a special testing feature for changing TDLS behavior. Use
+	    CONFIG_TDLS_TESTING build param to enable. Configure at runtime
+	    with tdls_testing cli command.
+	  - Add support for TDLS 802.11z.
+	* wlantest: Add a tool wlantest for IEEE802.11 protocol testing.
+	  wlantest can be used to capture frames from a monitor interface
+	  for realtime capturing or from pcap files for offline analysis.
+	* Interworking: Support added for 802.11u. Enable in .config with
+	  CONFIG_INTERWORKING. See wpa_supplicant.conf for config parameters
+	  for interworking. wpa_cli commands added to support this are
+	  interworking_select, interworking_connect, anqp_get, fetch_anqp,
+	  and stop_fetch_anqp.
+	* Android: Add build and runtime support for Android wpa_supplicant.
+	* bgscan learn: Add new bgscan that learns BSS information based on
+	  previous scans, and uses that information to dynamically generate
+	  the list of channels for background scans.
+	* Add a new debug message level for excessive information. Use
+	  -ddd to enable.
+	* TLS: Add support for tls_disable_time_checks=1 in client mode.
+	* Internal TLS:
+	  - Add support for TLS v1.1 (RFC 4346). Enable with build parameter
+	    CONFIG_TLSV11.
+	  - Add domainComponent parser for X.509 names.
+	* Linux: Add RFKill support by adding an interface state "disabled".
+	* Reorder some IEs to get closer to IEEE 802.11 standard. Move
+	  WMM into end of Beacon, Probe Resp and (Re)Assoc Resp frames.
+	  Move HT IEs to be later in (Re)Assoc Resp.
+	* Solaris: Add support for wired 802.1X client.
+	* Wi-Fi Direct support. See README-P2P for more information.
+	* Many bugfixes.
+
+2010-04-18 - v0.7.2
+	* nl80211: fixed number of issues with roaming
+	* avoid unnecessary roaming if multiple APs with similar signal
+	  strength are present in scan results
+	* add TLS client events and server probing to ease design of
+	  automatic detection of EAP parameters
+	* add option for server certificate matching (SHA256 hash of the
+	  certificate) instead of trusted CA certificate configuration
+	* bsd: Cleaned up driver wrapper and added various low-level
+	  configuration options
+	* wpa_gui-qt4: do not show too frequent WPS AP available events as
+	  tray messages
+	* TNC: fixed issues with fragmentation
+	* EAP-TNC: add Flags field into fragment acknowledgement (needed to
+	  interoperate with other implementations; may potentially breaks
+	  compatibility with older wpa_supplicant/hostapd versions)
+	* wpa_cli: added option for using a separate process to receive event
+	  messages to reduce latency in showing these
+	  (CFLAGS += -DCONFIG_WPA_CLI_FORK=y in .config to enable this)
+	* maximum BSS table size can now be configured (bss_max_count)
+	* BSSes to be included in the BSS table can be filtered based on
+	  configured SSIDs to save memory (filter_ssids)
+	* fix number of issues with IEEE 802.11r/FT; this version is not
+	  backwards compatible with old versions
+	* nl80211: add support for IEEE 802.11r/FT protocol (both over-the-air
+	  and over-the-DS)
+	* add freq_list network configuration parameter to allow the AP
+	  selection to filter out entries based on the operating channel
+	* add signal strength change events for bgscan; this allows more
+	  dynamic changes to background scanning interval based on changes in
+	  the signal strength with the current AP; this improves roaming within
+	  ESS quite a bit, e.g., with bgscan="simple:30:-45:300" in the network
+	  configuration block to request background scans less frequently when
+	  signal strength remains good and to automatically trigger background
+	  scans whenever signal strength drops noticeably
+	  (this is currently only available with nl80211)
+	* add BSSID and reason code (if available) to disconnect event messages
+	* wpa_gui-qt4: more complete support for translating the GUI with
+	  linguist and add German translation
+	* fix DH padding with internal crypto code (mainly, for WPS)
+	* do not trigger initial scan automatically anymore if there are no
+	  enabled networks
+
+2010-01-16 - v0.7.1
+	* cleaned up driver wrapper API (struct wpa_driver_ops); the new API
+	  is not fully backwards compatible, so out-of-tree driver wrappers
+	  will need modifications
+	* cleaned up various module interfaces
+	* merge hostapd and wpa_supplicant developers' documentation into a
+	  single document
+	* nl80211: use explicit deauthentication to clear cfg80211 state to
+	  avoid issues when roaming between APs
+	* dbus: major design changes in the new D-Bus API
+	  (fi.w1.wpa_supplicant1)
+	* nl80211: added support for IBSS networks
+	* added internal debugging mechanism with backtrace support and memory
+	  allocation/freeing validation, etc. tests (CONFIG_WPA_TRACE=y)
+	* added WPS ER unsubscription command to more cleanly unregister from
+	  receiving UPnP events when ER is terminated
+	* cleaned up AP mode operations to avoid need for virtual driver_ops
+	  wrapper
+	* added BSS table to maintain more complete scan result information
+	  over multiple scans (that may include only partial results)
+	* wpa_gui-qt4: update Peers dialog information more dynamically while
+	  the dialog is kept open
+	* fixed PKCS#12 use with OpenSSL 1.0.0
+	* driver_wext: Added cfg80211-specific optimization to avoid some
+	  unnecessary scans and to speed up association
+
+2009-11-21 - v0.7.0
+	* increased wpa_cli ping interval to 5 seconds and made this
+	  configurable with a new command line options (-G<seconds>)
+	* fixed scan buffer processing with WEXT to handle up to 65535
+	  byte result buffer (previously, limited to 32768 bytes)
+	* allow multiple driver wrappers to be specified on command line
+	  (e.g., -Dnl80211,wext); the first one that is able to initialize the
+	  interface will be used
+	* added support for multiple SSIDs per scan request to optimize
+	  scan_ssid=1 operations in ap_scan=1 mode (i.e., search for hidden
+	  SSIDs); this requires driver support and can currently be used only
+	  with nl80211
+	* added support for WPS USBA out-of-band mechanism with USB Flash
+	  Drives (UFD) (CONFIG_WPS_UFD=y)
+	* driver_ndis: add PAE group address to the multicast address list to
+	  fix wired IEEE 802.1X authentication
+	* fixed IEEE 802.11r key derivation function to match with the standard
+	  (note: this breaks interoperability with previous version) [Bug 303]
+	* added better support for drivers that allow separate authentication
+	  and association commands (e.g., mac80211-based Linux drivers with
+	  nl80211; SME in wpa_supplicant); this allows over-the-air FT protocol
+	  to be used (IEEE 802.11r)
+	* fixed SHA-256 based key derivation function to match with the
+	  standard when using CCMP (for IEEE 802.11r and IEEE 802.11w)
+	  (note: this breaks interoperability with previous version) [Bug 307]
+	* use shared driver wrapper files with hostapd
+	* added AP mode functionality (CONFIG_AP=y) with mode=2 in the network
+	  block; this can be used for open and WPA2-Personal networks
+	  (optionally, with WPS); this links in parts of hostapd functionality
+	  into wpa_supplicant
+	* wpa_gui-qt4: added new Peers dialog to show information about peers
+	  (other devices, including APs and stations, etc. in the neighborhood)
+	* added support for WPS External Registrar functionality (configure APs
+	  and enroll new devices); can be used with wpa_gui-qt4 Peers dialog
+	  and wpa_cli commands wps_er_start, wps_er_stop, wps_er_pin,
+	  wps_er_pbc, wps_er_learn
+	  (this can also be used with a new 'none' driver wrapper if no
+	  wireless device or IEEE 802.1X on wired is needed)
+	* driver_nl80211: multiple updates to provide support for new Linux
+	  nl80211/mac80211 functionality
+	* updated management frame protection to use IEEE Std 802.11w-2009
+	* fixed number of small WPS issues and added workarounds to
+	  interoperate with common deployed broken implementations
+	* added support for NFC out-of-band mechanism with WPS
+	* driver_ndis: fixed wired IEEE 802.1X authentication with PAE group
+	  address frames
+	* added preliminary support for IEEE 802.11r RIC processing
+	* added support for specifying subset of enabled frequencies to scan
+	  (scan_freq option in the network configuration block); this can speed
+	  up scanning process considerably if it is known that only a small
+	  subset of channels is actually used in the network (this is currently
+	  supported only with -Dnl80211)
+	* added a workaround for race condition between receiving the
+	  association event and the following EAPOL-Key
+	* added background scan and roaming infrastructure to allow
+	  network-specific optimizations to be used to improve roaming within
+	  an ESS (same SSID)
+	* added new DBus interface (fi.w1.wpa_supplicant1)
+
+2009-01-06 - v0.6.7
+	* added support for Wi-Fi Protected Setup (WPS)
+	  (wpa_supplicant can now be configured to act as a WPS Enrollee to
+	  enroll credentials for a network using PIN and PBC methods; in
+	  addition, wpa_supplicant can act as a wireless WPS Registrar to
+	  configure an AP); WPS support can be enabled by adding CONFIG_WPS=y
+	  into .config and setting the runtime configuration variables in
+	  wpa_supplicant.conf (see WPS section in the example configuration
+	  file); new wpa_cli commands wps_pin, wps_pbc, and wps_reg are used to
+	  manage WPS negotiation; see README-WPS for more details
+	* added support for EAP-AKA' (draft-arkko-eap-aka-kdf)
+	* added support for using driver_test over UDP socket
+	* fixed PEAPv0 Cryptobinding interoperability issue with Windows Server
+	  2008 NPS; optional cryptobinding is now enabled (again) by default
+	* fixed PSK editing in wpa_gui
+	* changed EAP-GPSK to use the IANA assigned EAP method type 51
+	* added a Windows installer that includes WinPcap and all the needed
+	  DLLs; in addition, it set up the registry automatically so that user
+	  will only need start wpa_gui to get prompted to start the wpasvc
+	  servide and add a new interface if needed through wpa_gui dialog
+	* updated management frame protection to use IEEE 802.11w/D7.0
+
+2008-11-23 - v0.6.6
+	* added Milenage SIM/USIM emulator for EAP-SIM/EAP-AKA
+	  (can be used to simulate test SIM/USIM card with a known private key;
+	  enable with CONFIG_SIM_SIMULATOR=y/CONFIG_USIM_SIMULATOR=y in .config
+	  and password="Ki:OPc"/password="Ki:OPc:SQN" in network configuration)
+	* added a new network configuration option, wpa_ptk_rekey, that can be
+	  used to enforce frequent PTK rekeying, e.g., to mitigate some attacks
+	  against TKIP deficiencies
+	* added an optional mitigation mechanism for certain attacks against
+	  TKIP by delaying Michael MIC error reports by a random amount of time
+	  between 0 and 60 seconds; this can be enabled with a build option
+	  CONFIG_DELAYED_MIC_ERROR_REPORT=y in .config
+	* fixed EAP-AKA to use RES Length field in AT_RES as length in bits,
+	  not bytes
+	* updated OpenSSL code for EAP-FAST to use an updated version of the
+	  session ticket overriding API that was included into the upstream
+	  OpenSSL 0.9.9 tree on 2008-11-15 (no additional OpenSSL patch is
+	  needed with that version anymore)
+	* updated userspace MLME instructions to match with the current Linux
+	  mac80211 implementation; please also note that this can only be used
+	  with driver_nl80211.c (the old code from driver_wext.c was removed)
+	* added support (Linux only) for RoboSwitch chipsets (often found in
+	  consumer grade routers); driver interface 'roboswitch'
+	* fixed canceling of PMKSA caching when using drivers that generate
+	  RSN IE and refuse to drop PMKIDs that wpa_supplicant does not know
+	  about
+
+2008-11-01 - v0.6.5
+	* added support for SHA-256 as X.509 certificate digest when using the
+	  internal X.509/TLSv1 implementation
+	* updated management frame protection to use IEEE 802.11w/D6.0
+	* added support for using SHA256-based stronger key derivation for WPA2
+	  (IEEE 802.11w)
+	* fixed FT (IEEE 802.11r) authentication after a failed association to
+	  use correct FTIE
+	* added support for configuring Phase 2 (inner/tunneled) authentication
+	  method with wpa_gui-qt4
+
+2008-08-10 - v0.6.4
+	* added support for EAP Sequences in EAP-FAST Phase 2
+	* added support for using TNC with EAP-FAST
+	* added driver_ps3 for the PS3 Linux wireless driver
+	* added support for optional cryptobinding with PEAPv0
+	* fixed the OpenSSL patches (0.9.8g and 0.9.9) for EAP-FAST to
+	  allow fallback to full handshake if server rejects PAC-Opaque
+	* added fragmentation support for EAP-TNC
+	* added support for parsing PKCS #8 formatted private keys into the
+	  internal TLS implementation (both PKCS #1 RSA key and PKCS #8
+	  encapsulated RSA key can now be used)
+	* added option of using faster, but larger, routines in the internal
+	  LibTomMath (for internal TLS implementation) to speed up DH and RSA
+	  calculations (CONFIG_INTERNAL_LIBTOMMATH_FAST=y)
+	* fixed race condition between disassociation event and group key
+	  handshake to avoid getting stuck in incorrect state [Bug 261]
+	* fixed opportunistic key caching (proactive_key_caching)
+
+2008-02-22 - v0.6.3
+	* removed 'nai' and 'eappsk' network configuration variables that were
+	  previously used for configuring user identity and key for EAP-PSK,
+	  EAP-PAX, EAP-SAKE, and EAP-GPSK. 'identity' field is now used as the
+	  replacement for 'nai' (if old configuration used a separate
+	  'identity' value, that would now be configured as
+	  'anonymous_identity'). 'password' field is now used as the
+	  replacement for 'eappsk' (it can also be set using hexstring to
+	  present random binary data)
+	* removed '-w' command line parameter (wait for interface to be added,
+	  if needed); cleaner way of handling this functionality is to use an
+	  external mechanism (e.g., hotplug scripts) that start wpa_supplicant
+	  when an interface is added
+	* updated FT support to use the latest draft, IEEE 802.11r/D9.0
+	* added ctrl_iface monitor event (CTRL-EVENT-SCAN-RESULTS) for
+	  indicating when new scan results become available
+	* added new ctrl_iface command, BSS, to allow scan results to be
+	  fetched without hitting the message size limits (this command
+	  can be used to iterate through the scan results one BSS at the time)
+	* fixed EAP-SIM not to include AT_NONCE_MT and AT_SELECTED_VERSION
+	  attributes in EAP-SIM Start/Response when using fast reauthentication
+	* fixed EAPOL not to end up in infinite loop when processing dynamic
+	  WEP keys with IEEE 802.1X
+	* fixed problems in getting NDIS events from WMI on Windows 2000
+
+2008-01-01 - v0.6.2
+	* added support for Makefile builds to include debug-log-to-a-file
+	  functionality (CONFIG_DEBUG_FILE=y and -f<path> on command line)
+	* fixed EAP-SIM and EAP-AKA message parser to validate attribute
+	  lengths properly to avoid potential crash caused by invalid messages
+	* added data structure for storing allocated buffers (struct wpabuf);
+	  this does not affect wpa_supplicant usage, but many of the APIs
+	  changed and various interfaces (e.g., EAP) is not compatible with old
+	  versions
+	* added support for protecting EAP-AKA/Identity messages with
+	  AT_CHECKCODE (optional feature in RFC 4187)
+	* added support for protected result indication with AT_RESULT_IND for
+	  EAP-SIM and EAP-AKA (phase1="result_ind=1")
+	* added driver_wext workaround for race condition between scanning and
+	  association with drivers that take very long time to scan all
+	  channels (e.g., madwifi with dual-band cards); wpa_supplicant is now
+	  using a longer hardcoded timeout for the scan if the driver supports
+	  notifications for scan completion (SIOCGIWSCAN event); this helps,
+	  e.g., in cases where wpa_supplicant and madwifi driver ended up in
+	  loop where the driver did not even try to associate
+	* stop EAPOL timer tick when no timers are in use in order to reduce
+	  power consumption (no need to wake up the process once per second)
+	  [Bug 237]
+	* added support for privilege separation (run only minimal part of
+	  wpa_supplicant functionality as root and rest as unprivileged,
+	  non-root process); see 'Privilege separation' in README for details;
+	  this is disabled by default and can be enabled with CONFIG_PRIVSEP=y
+	  in .config
+	* changed scan results data structure to include all information
+	  elements to make it easier to support new IEs; old get_scan_result()
+	  driver_ops is still supported for backwards compatibility (results
+	  are converted internally to the new format), but all drivers should
+	  start using the new get_scan_results2() to make them more likely to
+	  work with new features
+	* Qt4 version of wpa_gui (wpa_gui-qt4 subdirectory) is now native Qt4
+	  application, i.e., it does not require Qt3Support anymore; Windows
+	  binary of wpa_gui.exe is now from this directory and only requires
+	  QtCore4.dll and QtGui4.dll libraries
+	* updated Windows binary build to use Qt 4.3.3 and made Qt DLLs
+	  available as a separate package to make wpa_gui installation easier:
+	  http://w1.fi/wpa_supplicant/qt4/wpa_gui-qt433-windows-dll.zip
+	* added support for EAP-IKEv2 (draft-tschofenig-eap-ikev2-15.txt);
+	  only shared key/password authentication is supported in this version
+
+2007-11-24 - v0.6.1
+	* added support for configuring password as NtPasswordHash
+	  (16-byte MD4 hash of password) in hash:<32 hex digits> format
+	* added support for fallback from abbreviated TLS handshake to
+	  full handshake when using EAP-FAST (e.g., due to an expired
+	  PAC-Opaque)
+	* updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
+	  draft (draft-ietf-emu-eap-gpsk-07.txt)
+	* added support for drivers that take care of RSN 4-way handshake
+	  internally (WPA_DRIVER_FLAGS_4WAY_HANDSHAKE in get_capa flags and
+	  WPA_ALG_PMK in set_key)
+	* added an experimental port for Mac OS X (CONFIG_DRIVER_OSX=y in
+	  .config); this version supports only ap_scan=2 mode and allow the
+	  driver to take care of the 4-way handshake
+	* fixed a buffer overflow in parsing TSF from scan results when using
+	  driver_wext.c with a driver that includes the TSF (e.g., iwl4965)
+	  [Bug 232]
+	* updated FT support to use the latest draft, IEEE 802.11r/D8.0
+	* fixed an integer overflow issue in the ASN.1 parser used by the
+	  (experimental) internal TLS implementation to avoid a potential
+	  buffer read overflow
+	* fixed a race condition with -W option (wait for a control interface
+	  monitor before starting) that could have caused the first messages to
+	  be lost
+	* added support for processing TNCC-TNCS-Messages to report
+	  recommendation (allow/none/isolate) when using TNC [Bug 243]
+
+2007-05-28 - v0.6.0
+	* added network configuration parameter 'frequency' for setting
+	  initial channel for IBSS (adhoc) networks
+	* added experimental IEEE 802.11r/D6.0 support
+	* updated EAP-SAKE to RFC 4763 and the IANA-allocated EAP type 48
+	* updated EAP-PSK to use the IANA-allocated EAP type 47
+	* fixed EAP-PAX key derivation
+	* fixed EAP-PSK bit ordering of the Flags field
+	* fixed EAP-PEAP/TTLS/FAST to use the correct EAP identifier in
+	  tunnelled identity request (previously, the identifier from the outer
+	  method was used, not the tunnelled identifier which could be
+	  different)
+	* added support for fragmentation of outer TLS packets during Phase 2
+	  of EAP-PEAP/TTLS/FAST
+	* fixed EAP-TTLS AVP parser processing for too short AVP lengths
+	* added support for EAP-FAST authentication with inner methods that
+	  generate MSK (e.g., EAP-MSCHAPv2 that was previously only supported
+	  for PAC provisioning)
+	* added support for authenticated EAP-FAST provisioning
+	* added support for configuring maximum number of EAP-FAST PACs to
+	  store in a PAC list (fast_max_pac_list_len=<max> in phase1 string)
+	* added support for storing EAP-FAST PACs in binary format
+	  (fast_pac_format=binary in phase1 string)
+	* fixed dbus ctrl_iface to validate message interface before
+	  dispatching to avoid a possible segfault [Bug 190]
+	* fixed PeerKey key derivation to use the correct PRF label
+	* updated Windows binary build to link against OpenSSL 0.9.8d and
+	  added support for EAP-FAST
+	* updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
+	  draft (draft-ietf-emu-eap-gpsk-04.txt)
+	* fixed EAP-AKA Notification processing to allow Notification to be
+	  processed after AKA Challenge response has been sent
+	* updated to use IEEE 802.11w/D2.0 for management frame protection
+	  (still experimental)
+	* fixed EAP-TTLS implementation not to crash on use of freed memory
+	  if TLS library initialization fails
+	* added support for EAP-TNC (Trusted Network Connect)
+	  (this version implements the EAP-TNC method and EAP-TTLS changes
+	  needed to run two methods in sequence (IF-T) and the IF-IMC and
+	  IF-TNCCS interfaces from TNCC)
+
+2006-11-24 - v0.5.6
+	* added experimental, integrated TLSv1 client implementation with the
+	  needed X.509/ASN.1/RSA/bignum processing (this can be enabled by
+	  setting CONFIG_TLS=internal and CONFIG_INTERNAL_LIBTOMMATH=y in
+	  .config); this can be useful, e.g., if the target system does not
+	  have a suitable TLS library and a minimal code size is required
+	  (total size of this internal TLS/crypto code is bit under 50 kB on
+	  x86 and the crypto code is shared by rest of the supplicant so some
+	  of it was already required; TLSv1/X.509/ASN.1/RSA added about 25 kB)
+	* removed STAKey handshake since PeerKey handshake has replaced it in
+	  IEEE 802.11ma and there are no known deployments of STAKey
+	* updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
+	  draft (draft-ietf-emu-eap-gpsk-01.txt)
+	* added preliminary implementation of IEEE 802.11w/D1.0 (management
+	  frame protection)
+	  (Note: this requires driver support to work properly.)
+	  (Note2: IEEE 802.11w is an unapproved draft and subject to change.)
+	* fixed Windows named pipes ctrl_iface to not stop listening for
+	  commands if client program opens a named pipe and closes it
+	  immediately without sending a command
+	* fixed USIM PIN status determination for the case that PIN is not
+	  needed (this allows EAP-AKA to be used with USIM cards that do not
+	  use PIN)
+	* added support for reading 3G USIM AID from EF_DIR to allow EAP-AKA to
+	  be used with cards that do not support file selection based on
+	  partial AID
+	* added support for matching the subjectAltName of the authentication
+	  server certificate against multiple name components (e.g.,
+	  altsubject_match="DNS:server.example.com;DNS:server2.example.com")
+	* fixed EAP-SIM/AKA key derivation for re-authentication case (only
+	  affects IEEE 802.1X with dynamic WEP keys)
+	* changed ctrl_iface network configuration 'get' operations to not
+	  return password/key material; if these fields are requested, "*"
+	  will be returned if the password/key is set, but the value of the
+	  parameter is not exposed
+
+2006-08-27 - v0.5.5
+	* added support for building Windows version with UNICODE defined
+	  (wide-char functions)
+	* driver_ndis: fixed static WEP configuration to avoid race condition
+	  issues with some NDIS drivers between association and setting WEP
+	  keys
+	* driver_ndis: added validation for IELength value in scan results to
+	  avoid crashes when using buggy NDIS drivers [Bug 165]
+	* fixed Release|Win32 target in the Visual Studio project files
+	  (previously, only Debug|Win32 target was set properly)
+	* changed control interface API call wpa_ctrl_pending() to allow it to
+	  return -1 on error (e.g., connection lost); control interface clients
+	  will need to make sure that they verify that the value is indeed >0
+	  when determining whether there are pending messages
+	* added an alternative control interface backend for Windows targets:
+	  Named Pipe (CONFIG_CTRL_IFACE=named_pipe); this is now the default
+	  control interface mechanism for Windows builds (previously, UDP to
+	  localhost was used)
+	* changed ctrl_interface configuration for UNIX domain sockets:
+	  - deprecated ctrl_interface_group variable (it may be removed in
+	    future versions)
+	  - allow both directory and group be configured with ctrl_interface
+	    in following format: DIR=/var/run/wpa_supplicant GROUP=wheel
+	  - ctrl_interface=/var/run/wpa_supplicant is still supported for the
+	    case when group is not changed
+	* added support for controlling more than one interface per process in
+	  Windows version
+	* added a workaround for a case where the AP is using unknown address
+	  (e.g., MAC address of the wired interface) as the source address for
+	  EAPOL-Key frames; previously, that source address was used as the
+	  destination for EAPOL-Key frames and in key derivation; now, BSSID is
+	  used even if the source address does not match with it
+	  (this resolves an interoperability issue with Thomson SpeedTouch 580)
+	* added a workaround for UDP-based control interface (which was used in
+	  Windows builds before this release) to prevent packets with forged
+	  addresses from being accepted as local control requests
+	* removed ndis_events.cpp and possibility of using external
+	  ndis_events.exe; C version (ndis_events.c) is fully functional and
+	  there is no desire to maintain two separate versions of this
+	  implementation
+	* ndis_events: Changed NDIS event notification design to use WMI to
+	  learn the adapter description through Win32_PnPEntity class; this
+	  should fix some cases where the adapter name was not recognized
+	  correctly (e.g., with some USB WLAN adapters, e.g., Ralink RT2500
+	  USB) [Bug 113]
+	* fixed selection of the first network in ap_scan=2 mode; previously,
+	  wpa_supplicant could get stuck in SCANNING state when only the first
+	  network for enabled (e.g., after 'wpa_cli select_network 0')
+	* winsvc: added support for configuring ctrl_interface parameters in
+	  registry (ctrl_interface string value in
+	  HKLM\SOFTWARE\wpa_supplicant\interfaces\0000 key); this new value is
+	  required to enable control interface (previously, this was hardcoded
+	  to be enabled)
+	* allow wpa_gui subdirectory to be built with both Qt3 and Qt4
+	* converted wpa_gui-qt4 subdirectory to use Qt4 specific project format
+
+2006-06-20 - v0.5.4
+	* fixed build with CONFIG_STAKEY=y [Bug 143]
+	* added support for doing MLME (IEEE 802.11 management frame
+	  processing) in wpa_supplicant when using Devicescape IEEE 802.11
+	  stack (wireless-dev.git tree)
+	* added a new network block configuration option, fragment_size, to
+	  configure the maximum EAP fragment size
+	* driver_ndis: Disable WZC automatically for the selected interface to
+	  avoid conflicts with two programs trying to control the radio; WZC
+	  will be re-enabled (if it was enabled originally) when wpa_supplicant
+	  is terminated
+	* added an experimental TLSv1 client implementation
+	  (CONFIG_TLS=internal) that can be used instead of an external TLS
+	  library, e.g., to reduce total size requirement on systems that do
+	  not include any TLS library by default (this is not yet complete;
+	  basic functionality is there, but certificate validation is not yet
+	  included)
+	* added PeerKey handshake implementation for IEEE 802.11e
+	  direct link setup (DLS) to replace STAKey handshake
+	* fixed WPA PSK update through ctrl_iface for the case where the old
+	  PSK was derived from an ASCII passphrase and the new PSK is set as
+	  a raw PSK (hex string)
+	* added new configuration option for identifying which network block
+	  was used (id_str in wpa_supplicant.conf; included on
+	  WPA_EVENT_CONNECT monitor event and as WPA_ID_STR environmental
+	  variable in wpa_cli action scripts; in addition WPA_ID variable is
+	  set to the current unique identifier that wpa_supplicant assigned
+	  automatically for the network and that can be used with
+	  GET_NETWORK/SET_NETWORK ctrl_iface commands)
+	* wpa_cli action script is now called only when the connect/disconnect
+	  status changes or when associating with a different network
+	* fixed configuration parser not to remove CCMP from group cipher list
+	  if WPA-None (adhoc) is used (pairwise=NONE in that case)
+	* fixed integrated NDIS events processing not to hang the process due
+	  to a missed change in eloop_win.c API in v0.5.3 [Bug 155]
+	* added support for EAP Generalized Pre-Shared Key (EAP-GPSK,
+	  draft-clancy-emu-eap-shared-secret-00.txt)
+	* added Microsoft Visual Studio 2005 solution and project files for
+	  build wpa_supplicant for Windows (see vs2005 subdirectory)
+	* eloop_win: fixed unregistration of Windows events
+	* l2_packet_winpcap: fixed a deadlock in deinitializing l2_packet
+	  at the end of RSN pre-authentication and added unregistration of
+	  a Windows event to avoid getting eloop_win stuck with an invalid
+	  handle
+	* driver_ndis: added support for selecting AP based on BSSID
+	* added new environmental variable for wpa_cli action scripts:
+	  WPA_CTRL_DIR is the current control interface directory
+	* driver_ndis: added support for using NDISUIO instead of WinPcap for
+	  OID set/query operations (CONFIG_USE_NDISUIO=y in .config); with new
+	  l2_packet_ndis (CONFIG_L2_PACKET=ndis), this can be used to build
+	  wpa_supplicant without requiring WinPcap; note that using NDISUIO
+	  requires that WZC is disabled (net stop wzcsvc) since NDISUIO allows
+	  only one application to open the device
+	* changed NDIS driver naming to only include device GUID, e.g.,
+	  {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D}, instead of including WinPcap
+	  specific \Device\NPF_ prefix before the GUID; the prefix is still
+	  allowed for backwards compatibility, but it is not required anymore
+	  when specifying the interface
+	* driver_ndis: re-initialize driver interface is the adapter is removed
+	  and re-inserted [Bug 159]
+	* driver_madwifi: fixed TKIP and CCMP sequence number configuration on
+	  big endian hosts [Bug 146]
+
+2006-04-27 - v0.5.3
+	* fixed EAP-GTC response to include correct user identity when run as
+	  phase 2 method of EAP-FAST (i.e., EAP-FAST did not work in v0.5.2)
+	* driver_ndis: Fixed encryption mode configuration for unencrypted
+	  networks (some NDIS drivers ignored this, but others, e.g., Broadcom,
+	  refused to associate with open networks) [Bug 106]
+	* driver_ndis: use BSSID OID polling to detect when IBSS network is
+	  formed even when ndis_events code is included since some NDIS drivers
+	  do not generate media connect events in IBSS mode
+	* config_winreg: allow global ctrl_interface parameter to be configured
+	  in Windows registry
+	* config_winreg: added support for saving configuration data into
+	  Windows registry
+	* added support for controlling network device operational state
+	  (dormant/up) for Linux 2.6.17 to improve DHCP processing (see
+	  http://www.flamewarmaster.de/software/dhcpclient/ for a DHCP client
+	  that can use this information)
+	* driver_wext: added support for WE-21 change to SSID configuration
+	* driver_wext: fixed privacy configuration for static WEP keys mode
+	  [Bug 140]
+	* added an optional driver_ops callback for MLME-SETPROTECTION.request
+	  primitive
+	* added support for EAP-SAKE (no EAP method number allocated yet, so
+	  this is using the same experimental type 255 as EAP-PSK)
+	* added support for dynamically loading EAP methods (.so files) instead
+	  of requiring them to be statically linked in; this is disabled by
+	  default (see CONFIG_DYNAMIC_EAP_METHODS in defconfig for information
+	  on how to use this)
+
+2006-03-19 - v0.5.2
+	* do not try to use USIM APDUs when initializing PC/SC for SIM card
+	  access for a network that has not enabled EAP-AKA
+	* fixed EAP phase 2 Nak for EAP-{PEAP,TTLS,FAST} (this was broken in
+	  v0.5.1 due to the new support for expanded EAP types)
+	* added support for generating EAP Expanded Nak
+	* try to fetch scan results once before requesting new scan when
+	  starting up in ap_scan=1 mode (this can speed up initial association
+	  a lot with, e.g., madwifi-ng driver)
+	* added support for receiving EAPOL frames from a Linux bridge
+	  interface (-bbr0 on command line)
+	* fixed EAPOL re-authentication for sessions that used PMKSA caching
+	* changed EAP method registration to use a dynamic list of methods
+	  instead of a static list generated at build time
+	* fixed PMKSA cache deinitialization not to use freed memory when
+	  removing PMKSA entries
+	* fixed a memory leak in EAP-TTLS re-authentication
+	* reject WPA/WPA2 message 3/4 if it does not include any valid
+	  WPA/RSN IE
+	* driver_wext: added fallback to use SIOCSIWENCODE for setting auth_alg
+	  if the driver does not support SIOCSIWAUTH
+
+2006-01-29 - v0.5.1
+	* driver_test: added better support for multiple APs and STAs by using
+	  a directory with sockets that include MAC address for each device in
+	  the name (driver_param=test_dir=/tmp/test)
+	* added support for EAP expanded type (vendor specific EAP methods)
+	* added AP_SCAN command into ctrl_iface so that ap_scan configuration
+	  option can be changed if needed
+	* wpa_cli/wpa_gui: skip non-socket files in control directory when
+	  using UNIX domain sockets; this avoids selecting an incorrect
+	  interface (e.g., a PID file could be in this directory, even though
+	  use of this directory for something else than socket files is not
+	  recommended)
+	* fixed TLS library deinitialization after RSN pre-authentication not
+	  to disable TLS library for normal authentication
+	* driver_wext: Remove null-termination from SSID length if the driver
+	  used it; some Linux drivers do this and they were causing problems in
+	  wpa_supplicant not finding matching configuration block. This change
+	  would break a case where the SSID actually ends in '\0', but that is
+	  not likely to happen in real use.
+	* fixed PMKSA cache processing not to trigger deauthentication if the
+	  current PMKSA cache entry is replaced with a valid new entry
+	* fixed PC/SC initialization for ap_scan != 1 modes (this fixes
+	  EAP-SIM and EAP-AKA with real SIM/USIM card when using ap_scan=0 or
+	  ap_scan=2)
+
+2005-12-18 - v0.5.0 (beginning of 0.5.x development releases)
+	* added experimental STAKey handshake implementation for IEEE 802.11e
+	  direct link setup (DLS); note: this is disabled by default in both
+	  build and runtime configuration (can be enabled with CONFIG_STAKEY=y
+	  and stakey=1)
+	* fixed EAP-SIM and EAP-AKA pseudonym and fast re-authentication to
+	  decrypt AT_ENCR_DATA attributes correctly
+	* fixed EAP-AKA to allow resynchronization within the same session
+	* made code closer to ANSI C89 standard to make it easier to port to
+	  other C libraries and compilers
+	* started moving operating system or C library specific functions into
+	  wrapper functions defined in os.h and implemented in os_*.c to make
+	  code more portable
+	* wpa_supplicant can now be built with Microsoft Visual C++
+	  (e.g., with the freely available Toolkit 2003 version or Visual
+	  C++ 2005 Express Edition and Platform SDK); see nmake.mak for an
+	  example makefile for nmake
+	* added support for using Windows registry for command line parameters
+	  (CONFIG_MAIN=main_winsvc) and configuration data
+	  (CONFIG_BACKEND=winreg); see win_example.reg for an example registry
+	  contents; this version can be run both as a Windows service and as a
+	  normal application; 'wpasvc.exe app' to start as applicant,
+	  'wpasvc.exe reg <full path to wpasvc.exe>' to register a service,
+	  'net start wpasvc' to start the service, 'wpasvc.exe unreg' to
+	  unregister a service
+	* made it possible to link ndis_events.exe functionality into
+	  wpa_supplicant.exe by defining CONFIG_NDIS_EVENTS_INTEGRATED
+	* added better support for multiple control interface backends
+	  (CONFIG_CTRL_IFACE option); currently, 'unix' and 'udp' are supported
+	* fixed PC/SC code to use correct length for GSM AUTH command buffer
+	  and to not use pioRecvPci with SCardTransmit() calls; these were not
+	  causing visible problems with pcsc-lite, but Windows Winscard.dll
+	  refused the previously used parameters; this fixes EAP-SIM and
+	  EAP-AKA authentication using SIM/USIM card under Windows
+	* added new event loop implementation for Windows using
+	  WaitForMultipleObject() instead of select() in order to allow waiting
+	  for non-socket objects; this can be selected with
+	  CONFIG_ELOOP=eloop_win in .config
+	* added support for selecting l2_packet implementation in .config
+	  (CONFIG_L2_PACKET; following options are available now: linux, pcap,
+	  winpcap, freebsd, none)
+	* added new l2_packet implementation for WinPcap
+	  (CONFIG_L2_PACKET=winpcap) that uses a separate receive thread to
+	  reduce latency in EAPOL receive processing from about 100 ms to about
+	  3 ms
+	* added support for EAP-FAST key derivation using other ciphers than
+	  RC4-128-SHA for authentication and AES128-SHA for provisioning
+	* added support for configuring CA certificate as DER file and as a
+	  configuration blob
+	* fixed private key configuration as configuration blob and added
+	  support for using PKCS#12 as a blob
+	* tls_gnutls: added support for using PKCS#12 files; added support for
+	  session resumption
+	* added support for loading trusted CA certificates from Windows
+	  certificate store: ca_cert="cert_store://<name>", where <name> is
+	  likely CA (Intermediate CA certificates) or ROOT (root certificates)
+	* added C version of ndis_events.cpp and made it possible to build this
+	  with MinGW so that CONFIG_NDIS_EVENTS_INTEGRATED can be used more
+	  easily on cross-compilation builds
+	* added wpasvc.exe into Windows binary release; this is an alternative
+	  version of wpa_supplicant.exe with configuration backend using
+	  Windows registry and with the entry point designed to run as a
+	  Windows service
+	* integrated ndis_events.exe functionality into wpa_supplicant.exe and
+	  wpasvc.exe and removed this additional tool from the Windows binary
+	  release since it is not needed anymore
+	* load winscard.dll functions dynamically when building with MinGW
+	  since MinGW does not yet include winscard library
+
+2005-11-20 - v0.4.7 (beginning of 0.4.x stable releases)
+	* l2_packet_pcap: fixed wired IEEE 802.1X authentication with libpcap
+	  and WinPcap to receive frames sent to PAE group address
+	* disable EAP state machine when IEEE 802.1X authentication is not used
+	  in order to get rid of bogus "EAP failed" messages
+	* fixed OpenSSL error reporting to go through all pending errors to
+	  avoid confusing reports of old errors being reported at later point
+	  during handshake
+	* fixed configuration file updating to not write empty variables
+	  (e.g., proto or key_mgmt) that the file parser would not accept
+	* fixed ADD_NETWORK ctrl_iface command to use the same default values
+	  for variables as empty network definitions read from config file
+	  would get
+	* fixed EAP state machine to not discard EAP-Failure messages in many
+	  cases (e.g., during TLS handshake)
+	* fixed a infinite loop in private key reading if the configured file
+	  cannot be parsed successfully
+	* driver_madwifi: added support for madwifi-ng
+	* wpa_gui: do not display password/PSK field contents
+	* wpa_gui: added CA certificate configuration
+	* driver_ndis: fixed scan request in ap_scan=2 mode not to change SSID
+	* driver_ndis: include Beacon IEs in AssocInfo in order to notice if
+	  the new AP is using different WPA/RSN IE
+	* use longer timeout for IEEE 802.11 association to avoid problems with
+	  drivers that may take more than five second to associate
+
+2005-10-27 - v0.4.6
+	* allow fallback to WPA, if mixed WPA+WPA2 networks have mismatch in
+	  RSN IE, but WPA IE would match with wpa_supplicant configuration
+	* added support for named configuration blobs in order to avoid having
+	  to use file system for external files (e.g., certificates);
+	  variables can be set to "blob://<blob name>" instead of file path to
+	  use a named blob; supported fields: pac_file, client_cert,
+	  private_key
+	* fixed RSN pre-authentication (it was broken in the clean up of WPA
+	  state machine interface in v0.4.5)
+	* driver_madwifi: set IEEE80211_KEY_GROUP flag for group keys to make
+	  sure the driver configures broadcast decryption correctly
+	* added ca_path (and ca_path2) configuration variables that can be used
+	  to configure OpenSSL CA path, e.g., /etc/ssl/certs, for using the
+	  system-wide trusted CA list
+	* added support for starting wpa_supplicant without a configuration
+	  file (-C argument must be used to set ctrl_interface parameter for
+	  this case; in addition, -p argument can be used to provide
+	  driver_param; these new arguments can also be used with a
+	  configuration to override the values from the configuration)
+	* added global control interface that can be optionally used for adding
+	  and removing network interfaces dynamically (-g command line argument
+	  for both wpa_supplicant and wpa_cli) without having to restart
+	  wpa_supplicant process
+	* wpa_gui:
+	  - try to save configuration whenever something is modified
+	  - added WEP key configuration
+	  - added possibility to edit the current network configuration
+	* driver_ndis: fixed driver polling not to increase frequency on each
+	  received EAPOL frame due to incorrectly cancelled timeout
+	* added simple configuration file examples (in examples subdirectory)
+	* fixed driver_wext.c to filter wireless events based on ifindex to
+	  avoid interfaces receiving events from other interfaces
+	* delay sending initial EAPOL-Start couple of seconds to speed up
+	  authentication for the most common case of Authenticator starting
+	  EAP authentication immediately after association
+
+2005-09-25 - v0.4.5
+	* added a workaround for clearing keys with ndiswrapper to allow
+	  roaming from WPA enabled AP to plaintext one
+	* added docbook documentation (doc/docbook) that can be used to
+	  generate, e.g., man pages
+	* l2_packet_linux: use socket type SOCK_DGRAM instead of SOCK_RAW for
+	  PF_PACKET in order to prepare for network devices that do not use
+	  Ethernet headers (e.g., network stack with native IEEE 802.11 frames)
+	* use receipt of EAPOL-Key frame as a lower layer success indication
+	  for EAP state machine to allow recovery from dropped EAP-Success
+	  frame
+	* cleaned up internal EAPOL frame processing by not including link
+	  layer (Ethernet) header during WPA and EAPOL/EAP processing; this
+	  header is added only when transmitted the frame; this makes it easier
+	  to use wpa_supplicant on link layers that use different header than
+	  Ethernet
+	* updated EAP-PSK to use draft 9 by default since this can now be
+	  tested with hostapd; removed support for draft 3, including
+	  server_nai configuration option from network blocks
+	* driver_wired: add PAE address to the multicast address list in order
+	  to be able to receive EAPOL frames with drivers that do not include
+	  these multicast addresses by default
+	* driver_wext: add support for WE-19
+	* added support for multiple configuration backends (CONFIG_BACKEND
+	  option); currently, only 'file' is supported (i.e., the format used
+	  in wpa_supplicant.conf)
+	* added support for updating configuration ('wpa_cli save_config');
+	  this is disabled by default and can be enabled with global
+	  update_config=1 variable in wpa_supplicant.conf; this allows wpa_cli
+	  and wpa_gui to store the configuration changes in a permanent store
+	* added GET_NETWORK ctrl_iface command
+	  (e.g., 'wpa_cli get_network 0 ssid')
+
+2005-08-21 - v0.4.4
+	* replaced OpenSSL patch for EAP-FAST support
+	  (openssl-tls-extensions.patch) with a more generic and correct
+	  patch (the new patch is not compatible with the previous one, so the
+	  OpenSSL library will need to be patched with the new patch in order
+	  to be able to build wpa_supplicant with EAP-FAST support)
+	* added support for using Windows certificate store (through CryptoAPI)
+	  for client certificate and private key operations (EAP-TLS)
+	  (see wpa_supplicant.conf for more information on how to configure
+	  this with private_key)
+	* ported wpa_gui to Windows
+	* added Qt4 version of wpa_gui (wpa_gui-qt4 directory); this can be
+	  built with the open source version of the Qt4 for Windows
+	* allow non-WPA modes (e.g., IEEE 802.1X with dynamic WEP) to be used
+	  with drivers that do not support WPA
+	* ndis_events: fixed Windows 2000 support
+	* added support for enabling/disabling networks from the list of all
+	  configured networks ('wpa_cli enable_network <network id>' and
+	  'wpa_cli disable_network <network id>')
+	* added support for adding and removing network from the current
+	  configuration ('wpa_cli add_network' and 'wpa_cli remove_network
+	  <network id>'); added networks are disabled by default and they can
+	  be enabled with enable_network command once the configuration is done
+	  for the new network; note: configuration file is not yet updated, so
+	  these new networks are lost when wpa_supplicant is restarted
+	* added support for setting network configuration parameters through
+	  the control interface, for example:
+	  wpa_cli set_network 0 ssid "\"my network\""
+	* fixed parsing of strings that include both " and # within double
+	  quoted area (e.g., "start"#end")
+	* added EAP workaround for PEAP session resumption: allow outer,
+	  i.e., not tunneled, EAP-Success to terminate session since; this can
+	  be disabled with eap_workaround=0
+	  (this was allowed for PEAPv1 before, but now it is also allowed for
+	  PEAPv0 since at least one RADIUS authentication server seems to be
+	  doing this for PEAPv0, too)
+	* wpa_gui: added preliminary support for adding new networks to the
+	  wpa_supplicant configuration (double click on the scan results to
+	  open network configuration)
+
+2005-06-26 - v0.4.3
+	* removed interface for external EAPOL/EAP supplicant (e.g.,
+	  Xsupplicant), (CONFIG_XSUPPLICANT_IFACE) since it is not required
+	  anymore and is unlikely to be used by anyone
+	* driver_ndis: fixed WinPcap 3.0 support
+	* fixed build with CONFIG_DNET_PCAP=y on Linux
+	* l2_packet: moved different implementations into separate files
+	  (l2_packet_*.c)
+
+2005-06-12 - v0.4.2
+	* driver_ipw: updated driver structures to match with ipw2200-1.0.4
+	  (note: ipw2100-1.1.0 is likely to require an update to work with
+	  this)
+	* added support for using ap_scan=2 mode with multiple network blocks;
+	  wpa_supplicant will go through the networks one by one until the
+	  driver reports a successful association; this uses the same order for
+	  networks as scan_ssid=1 scans, i.e., the priority field is ignored
+	  and the network block order in the file is used instead
+	* fixed a potential issue in RSN pre-authentication ending up using
+	  freed memory if pre-authentication times out
+	* added support for matching alternative subject name extensions of the
+	  authentication server certificate; new configuration variables
+	  altsubject_match and altsubject_match2
+	* driver_ndis: added support for IEEE 802.1X authentication with wired
+	  NDIS drivers
+	* added support for querying private key password (EAP-TLS) through the
+	  control interface (wpa_cli/wpa_gui) if one is not included in the
+	  configuration file
+	* driver_broadcom: fixed couple of memory leaks in scan result
+	  processing
+	* EAP-PAX is now registered as EAP type 46
+	* fixed EAP-PAX MAC calculation
+	* fixed EAP-PAX CK and ICK key derivation
+	* added support for using password with EAP-PAX (as an alternative to
+	  entering key with eappsk); SHA-1 hash of the password will be used as
+	  the key in this case
+	* added support for arbitrary driver interface parameters through the
+	  configuration file with a new driver_param field; this adds a new
+	  driver_ops function set_param()
+	* added possibility to override l2_packet module with driver interface
+	  API (new send_eapol handler); this can be used to implement driver
+	  specific TX/RX functions for EAPOL frames
+	* fixed ctrl_interface_group processing for the case where gid is
+	  entered as a number, not group name
+	* driver_test: added support for testing hostapd with wpa_supplicant
+	  by using test driver interface without any kernel drivers or network
+	  cards
+
+2005-05-22 - v0.4.1
+	* driver_madwifi: fixed WPA/WPA2 mode configuration to allow EAPOL
+	  packets to be encrypted; this was apparently broken by the changed
+	  ioctl order in v0.4.0
+	* driver_madwifi: added preliminary support for compiling against 'BSD'
+	  branch of madwifi CVS tree
+	* added support for EAP-MSCHAPv2 password retries within the same EAP
+	  authentication session
+	* added support for password changes with EAP-MSCHAPv2 (used when the
+	  password has expired)
+	* added support for reading additional certificates from PKCS#12 files
+	  and adding them to the certificate chain
+	* fixed association with IEEE 802.1X (no WPA) when dynamic WEP keys
+	  were used
+	* fixed a possible double free in EAP-TTLS fast-reauthentication when
+	  identity or password is entered through control interface
+	* display EAP Notification messages to user through control interface
+	  with "CTRL-EVENT-EAP-NOTIFICATION" prefix
+	* added GUI version of wpa_cli, wpa_gui; this is not build
+	  automatically with 'make'; use 'make wpa_gui' to build (this requires
+	  Qt development tools)
+	* added 'disconnect' command to control interface for setting
+	  wpa_supplicant in state where it will not associate before
+	  'reassociate' command has been used
+	* added support for selecting a network from the list of all configured
+	  networks ('wpa_cli select_network <network id>'; this disabled all
+	  other networks; to re-enable, 'wpa_cli select_network any')
+	* added support for getting scan results through control interface
+	* added EAP workaround for PEAPv1 session resumption: allow outer,
+	  i.e., not tunneled, EAP-Success to terminate session since; this can
+	  be disabled with eap_workaround=0
+
+2005-04-25 - v0.4.0 (beginning of 0.4.x development releases)
+	* added a new build time option, CONFIG_NO_STDOUT_DEBUG, that can be
+	  used to reduce the size of the wpa_supplicant considerably if
+	  debugging code is not needed
+	* fixed EAPOL-Key validation to drop packets with invalid Key Data
+	  Length; such frames could have crashed wpa_supplicant due to buffer
+	  overflow
+	* added support for wired authentication (IEEE 802.1X on wired
+	  Ethernet); driver interface 'wired'
+	* obsoleted set_wpa() handler in the driver interface API (it can be
+	  replaced by moving enable/disable functionality into init()/deinit())
+	  (calls to set_wpa() are still present for backwards compatibility,
+	  but they may be removed in the future)
+	* driver_madwifi: fixed association in plaintext mode
+	* modified the EAP workaround that accepts EAP-Success with incorrect
+	  Identifier to be even less strict about verification in order to
+	  interoperate with some authentication servers
+	* added support for sending TLS alerts
+	* added support for 'any' SSID wildcard; if ssid is not configured or
+	  is set to an empty string, any SSID will be accepted for non-WPA AP
+	* added support for asking PIN (for SIM) from frontends (e.g.,
+	  wpa_cli); if a PIN is needed, but not included in the configuration
+	  file, a control interface request is sent and EAP processing is
+	  delayed until the PIN is available
+	* added support for using external devices (e.g., a smartcard) for
+	  private key operations in EAP-TLS (CONFIG_SMARTCARD=y in .config);
+	  new wpa_supplicant.conf variables:
+	  - global: opensc_engine_path, pkcs11_engine_path, pkcs11_module_path
+	  - network: engine, engine_id, key_id
+	* added experimental support for EAP-PAX
+	* added monitor mode for wpa_cli (-a<path to a program to run>) that
+	  allows external commands (e.g., shell scripts) to be run based on
+	  wpa_supplicant events, e.g., when authentication has been completed
+	  and data connection is ready; other related wpa_cli arguments:
+	  -B (run in background), -P (write PID file); wpa_supplicant has a new
+	  command line argument (-W) that can be used to make it wait until a
+	  control interface command is received in order to avoid missing
+	  events
+	* added support for opportunistic WPA2 PMKSA key caching (disabled by
+	  default, can be enabled with proactive_key_caching=1)
+	* fixed RSN IE in 4-Way Handshake message 2/4 for the case where
+	  Authenticator rejects PMKSA caching attempt and the driver is not
+	  using assoc_info events
+	* added -P<pid file> argument for wpa_supplicant to write the current
+	  process id into a file
+
+2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases)
+	* added new phase1 option parameter, include_tls_length=1, to force
+	  wpa_supplicant to add TLS Message Length field to all TLS messages
+	  even if the packet is not fragmented; this may be needed with some
+	  authentication servers
+	* fixed WPA/RSN IE verification in message 3 of 4-Way Handshake when
+	  using drivers that take care of AP selection (e.g., when using
+	  ap_scan=2)
+	* fixed reprocessing of pending request after ctrl_iface requests for
+	  identity/password/otp
+	* fixed ctrl_iface requests for identity/password/otp in Phase 2 of
+	  EAP-PEAP and EAP-TTLS
+	* all drivers using driver_wext: set interface up and select Managed
+	  mode when starting wpa_supplicant; set interface down when exiting
+	* renamed driver_ipw2100.c to driver_ipw.c since it now supports both
+	  ipw2100 and ipw2200; please note that this also changed the
+	  configuration variable in .config to CONFIG_DRIVER_IPW
+
+2005-01-24 - v0.3.6
+	* fixed a busy loop introduced in v0.3.5 for scan result processing
+	  when no matching AP is found
+
+2005-01-23 - v0.3.5
+	* added a workaround for an interoperability issue with a Cisco AP
+	  when using WPA2-PSK
+	* fixed non-WPA IEEE 802.1X to use the same authentication timeout as
+	  WPA with IEEE 802.1X (i.e., timeout 10 -> 70 sec to allow
+	  retransmission of dropped frames)
+	* fixed issues with 64-bit CPUs and SHA1 cleanup in previous version
+	  (e.g., segfault when processing EAPOL-Key frames)
+	* fixed EAP workaround and fast reauthentication configuration for
+	  RSN pre-authentication; previously these were disabled and
+	  pre-authentication would fail if the used authentication server
+	  requires EAP workarounds
+	* added support for blacklisting APs that fail or timeout
+	  authentication in ap_scan=1 mode so that all APs are tried in cases
+	  where the ones with strongest signal level are failing authentication
+	* fixed CA certificate loading after a failed EAP-TLS/PEAP/TTLS
+	  authentication attempt
+	* allow EAP-PEAP/TTLS fast reauthentication only if Phase 2 succeeded
+	  in the previous authentication (previously, only Phase 1 success was
+	  verified)
+
+2005-01-09 - v0.3.4
+	* added preliminary support for IBSS (ad-hoc) mode configuration
+	  (mode=1 in network block); this included a new key_mgmt mode
+	  WPA-NONE, i.e., TKIP or CCMP with a fixed key (based on psk) and no
+	  key management; see wpa_supplicant.conf for more details and an
+	  example on how to configure this (note: this is currently implemented
+	  only for driver_hostapd.c, but the changes should be trivial to add
+	  in associate() handler for other drivers, too (assuming the driver
+	  supports WPA-None)
+	* added preliminary port for native Windows (i.e., no cygwin) using
+	  mingw
+
+2005-01-02 - v0.3.3
+	* added optional support for GNU Readline and History Libraries for
+	  wpa_cli (CONFIG_READLINE)
+	* cleaned up EAP state machine <-> method interface and number of
+	  small problems with error case processing not terminating on
+	  EAP-Failure but waiting for timeout
+	* added couple of workarounds for interoperability issues with a
+	  Cisco AP when using WPA2
+	* added support for EAP-FAST (draft-cam-winget-eap-fast-00.txt);
+	  Note: This requires a patch for openssl to add support for TLS
+	  extensions and number of workarounds for operations without
+	  certificates. Proof of concept type of experimental patch is
+	  included in openssl-tls-extensions.patch.
+
+2004-12-19 - v0.3.2
+	* fixed private key loading for cases where passphrase is not set
+	* fixed Windows/cygwin L2 packet handler freeing; previous version
+	  could cause a segfault when RSN pre-authentication was completed
+	* added support for PMKSA caching with drivers that generate RSN IEs
+	  (e.g., NDIS); currently, this is only implemented in driver_ndis.c,
+	  but similar code can be easily added to driver_ndiswrapper.c once
+	  ndiswrapper gets full support for RSN PMKSA caching
+	* improved recovery from PMKID mismatches by requesting full EAP
+	  authentication in case of failed PMKSA caching attempt
+	* driver_ndis: added support for NDIS NdisMIncidateStatus() events
+	  (this requires that ndis_events is ran while wpa_supplicant is
+	  running)
+	* driver_ndis: use ADD_WEP/REMOVE_WEP when configuring WEP keys
+	* added support for driver interfaces to replace the interface name
+	  based on driver/OS specific mapping, e.g., in case of driver_ndis,
+	  this allows the beginning of the adapter description to be used as
+	  the interface name
+	* added support for CR+LF (Windows-style) line ends in configuration
+	  file
+	* driver_ndis: enable radio before starting scanning, disable radio
+	  when exiting
+	* modified association event handler to set portEnabled = FALSE before
+	  clearing port Valid in order to reset EAP state machine and avoid
+	  problems with new authentication getting ignored because of state
+	  machines ending up in AUTHENTICATED/SUCCESS state based on old
+	  information
+	* added support for driver events to add PMKID candidates in order to
+	  allow drivers to give priority to most likely roaming candidates
+	* driver_hostap: moved PrivacyInvoked configuration to associate()
+	  function so that this will not be set for plaintext connections
+	* added KEY_MGMT_802_1X_NO_WPA as a new key_mgmt type so that driver
+	  interface can distinguish plaintext and IEEE 802.1X (no WPA)
+	  authentication
+	* fixed static WEP key configuration to use broadcast/default type for
+	  all keys (previously, the default TX key was configured as pairwise/
+	  unicast key)
+	* driver_ndis: added legacy WPA capability detection for non-WPA2
+	  drivers
+	* added support for setting static WEP keys for IEEE 802.1X without
+	  dynamic WEP keying (eapol_flags=0)
+
+2004-12-12 - v0.3.1
+	* added support for reading PKCS#12 (PFX) files (as a replacement for
+	  PEM/DER) to get certificate and private key (CONFIG_PKCS12)
+	* fixed compilation with CONFIG_PCSC=y
+	* added new ap_scan mode, ap_scan=2, for drivers that take care of
+	  association, but need to be configured with security policy and SSID,
+	  e.g., ndiswrapper and NDIS driver; this mode should allow such
+	  drivers to work with hidden SSIDs and optimized roaming; when
+	  ap_scan=2 is used, only the first network block in the configuration
+	  file is used and this configuration should have explicit security
+	  policy (i.e., only one option in the lists) for key_mgmt, pairwise,
+	  group, proto variables
+	* added experimental port of wpa_supplicant for Windows
+	  - driver_ndis.c driver interface (NDIS OIDs)
+	  - currently, this requires cygwin and WinPcap
+	  - small utility, win_if_list, can be used to get interface name
+	* control interface can now be removed at build time; add
+	  CONFIG_CTRL_IFACE=y to .config to maintain old functionality
+	* optional Xsupplicant interface can now be removed at build time;
+	  (CONFIG_XSUPPLICANT_IFACE=y in .config to bring it back)
+	* added auth_alg to driver interface associate() parameters to make it
+	  easier for drivers to configure authentication algorithm as part of
+	  the association
+
+2004-12-05 - v0.3.0 (beginning of 0.3.x development releases)
+	* driver_broadcom: added new driver interface for Broadcom wl.o driver
+	  (a generic driver for Broadcom IEEE 802.11a/g cards)
+	* wpa_cli: fixed parsing of -p <path> command line argument
+	* PEAPv1: fixed tunneled EAP-Success reply handling to reply with TLS
+	  ACK, not tunneled EAP-Success (of which only the first byte was
+	  actually send due to a bug in previous code); this seems to
+	  interoperate with most RADIUS servers that implements PEAPv1
+	* PEAPv1: added support for terminating PEAP authentication on tunneled
+	  EAP-Success message; this can be configured by adding
+	  peap_outer_success=0 on phase1 parameters in wpa_supplicant.conf
+	  (some RADIUS servers require this whereas others require a tunneled
+	  reply
+	* PEAPv1: changed phase1 option peaplabel to use default to 0, i.e., to
+	  the old label for key derivation; previously, the default was 1,
+	  but it looks like most existing PEAPv1 implementations use the old
+	  label which is thus more suitable default option
+	* added support for EAP-PSK (draft-bersani-eap-psk-03.txt)
+	* fixed parsing of wep_tx_keyidx
+	* added support for configuring list of allowed Phase 2 EAP types
+	  (for both EAP-PEAP and EAP-TTLS) instead of only one type
+	* added support for configuring IEEE 802.11 authentication algorithm
+	  (auth_alg; mainly for using Shared Key authentication with static
+	  WEP keys)
+	* added support for EAP-AKA (with UMTS SIM)
+	* fixed couple of errors in PCSC handling that could have caused
+	  random-looking errors for EAP-SIM
+	* added support for EAP-SIM pseudonyms and fast re-authentication
+	* added support for EAP-TLS/PEAP/TTLS fast re-authentication (TLS
+	  session resumption)
+	* added support for EAP-SIM with two challanges
+	  (phase1="sim_min_num_chal=3" can be used to require three challenges)
+	* added support for configuring DH/DSA parameters for an ephemeral DH
+	  key exchange (EAP-TLS/PEAP/TTLS) using new configuration parameters
+	  dh_file and dh_file2 (phase 2); this adds support for using DSA keys
+	  and optional DH key exchange to achieve forward secracy with RSA keys
+	* added support for matching subject of the authentication server
+	  certificate with a substring when using EAP-TLS/PEAP/TTLS; new
+	  configuration variables subject_match and subject_match2
+	* changed SSID configuration in driver_wext.c (used by many driver
+	  interfaces) to use ssid_len+1 as the length for SSID since some Linux
+	  drivers expect this
+	* fixed couple of unaligned reads in scan result parsing to fix WPA
+	  connection on some platforms (e.g., ARM)
+	* added driver interface for Intel ipw2100 driver
+	* added support for LEAP with WPA
+	* added support for larger scan results report (old limit was 4 kB of
+	  data, i.e., about 35 or so APs) when using Linux wireless extensions
+	  v17 or newer
+	* fixed a bug in PMKSA cache processing: skip sending of EAPOL-Start
+	  only if there is a PMKSA cache entry for the current AP
+	* fixed error handling for case where reading of scan results fails:
+	  must schedule a new scan or wpa_supplicant will remain waiting
+	  forever
+	* changed debug output to remove shared password/key material by
+	  default; all key information can be included with -K command line
+	  argument to match the previous behavior
+	* added support for timestamping debug log messages (disabled by
+	  default, can be enabled with -t command line argument)
+	* set pairwise/group cipher suite for non-WPA IEEE 802.1X to WEP-104
+	  if keys are not configured to be used; this fixes IEEE 802.1X mode
+	  with drivers that use this information to configure whether Privacy
+	  bit can be in Beacon frames (e.g., ndiswrapper)
+	* avoid clearing driver keys if no keys have been configured since last
+	  key clear request; this seems to improve reliability of group key
+	  handshake for ndiswrapper & NDIS driver which seems to be suffering
+	  of some kind of timing issue when the keys are cleared again after
+	  association
+	* changed driver interface API:
+	  - WPA_SUPPLICANT_DRIVER_VERSION define can be used to determine which
+	    version is being used (now, this is set to 2; previously, it was
+	    not defined)
+	  - pass pointer to private data structure to all calls
+	  - the new API is not backwards compatible; all in-tree driver
+	    interfaces has been converted to the new API
+	* added support for controlling multiple interfaces (radios) per
+	  wpa_supplicant process; each interface needs to be listed on the
+	  command line (-c, -i, -D arguments) with -N as a separator
+	  (-cwpa1.conf -iwlan0 -Dhostap -N -cwpa2.conf -iath0 -Dmadwifi)
+	* added a workaround for EAP servers that incorrectly use same Id for
+	  sequential EAP packets
+	* changed libpcap/libdnet configuration to use .config variable,
+	  CONFIG_DNET_PCAP, instead of requiring Makefile modification
+	* improved downgrade attack detection in IE verification of msg 3/4:
+	  verify both WPA and RSN IEs, if present, not only the selected one;
+	  reject the AP if an RSN IE is found in msg 3/4, but not in Beacon or
+	  Probe Response frame, and RSN is enabled in wpa_supplicant
+	  configuration
+	* fixed WPA msg 3/4 processing to allow Key Data field contain other
+	  IEs than just one WPA IE
+	* added support for FreeBSD and driver interface for the BSD net80211
+	  layer (CONFIG_DRIVER_BSD=y in .config); please note that some of the
+	  required kernel mods have not yet been committed
+	* made EAP workarounds configurable; enabled by default, can be
+	  disabled with network block option eap_workaround=0
+
+2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases)
+	* resolved couple of interoperability issues with EAP-PEAPv1 and
+	  Phase 2 (inner EAP) fragment reassembly
+	* driver_madwifi: fixed WEP key configuration for IEEE 802.1X when the
+	  AP is using non-zero key index for the unicast key and key index zero
+	  for the broadcast key
+	* driver_hostap: fixed IEEE 802.1X WEP key updates and
+	  re-authentication by allowing unencrypted EAPOL frames when not using
+	  WPA
+	* added a new driver interface, 'wext', which uses only standard,
+	  driver independent functionality in Linux wireless extensions;
+	  currently, this can be used only for non-WPA IEEE 802.1X mode, but
+	  eventually, this is to be extended to support full WPA/WPA2 once
+	  Linux wireless extensions get support for this
+	* added support for mode in which the driver is responsible for AP
+	  scanning and selection; this is disabled by default and can be
+	  enabled with global ap_scan=0 variable in wpa_supplicant.conf;
+	  this mode can be used, e.g., with generic 'wext' driver interface to
+	  use wpa_supplicant as IEEE 802.1X Supplicant with any Linux driver
+	  supporting wireless extensions.
+	* driver_madwifi: fixed WPA2 configuration and scan_ssid=1 (e.g.,
+	  operation with an AP that does not include SSID in the Beacon frames)
+	* added support for new EAP authentication methods:
+	  EAP-TTLS/EAP-OTP, EAP-PEAPv0/OTP, EAP-PEAPv1/OTP, EAP-OTP
+	* added support for asking one-time-passwords from frontends (e.g.,
+	  wpa_cli); this 'otp' command works otherwise like 'password' command,
+	  but the password is used only once and the frontend will be asked for
+	  a new password whenever a request from authenticator requires a
+	  password; this can be used with both EAP-OTP and EAP-GTC
+	* changed wpa_cli to automatically re-establish connection so that it
+	  does not need to be re-started when wpa_supplicant is terminated and
+	  started again
+	* improved user data (identity/password/otp) requests through
+	  frontends: process pending EAPOL packets after getting new
+	  information so that full authentication does not need to be
+	  restarted; in addition, send pending requests again whenever a new
+	  frontend is attached
+	* changed control frontends to use a new directory for socket files to
+	  make it easier for wpa_cli to automatically select between interfaces
+	  and to provide access control for the control interface;
+	  wpa_supplicant.conf: ctrl_interface is now a path
+	  (/var/run/wpa_supplicant is the recommended path) and
+	  ctrl_interface_group can be used to select which group gets access to
+	  the control interface;
+	  wpa_cli: by default, try to connect to the first interface available
+	  in /var/run/wpa_supplicant; this path can be overriden with -p option
+	  and an interface can be selected with -i option (i.e., in most common
+	  cases, wpa_cli does not need to get any arguments)
+	* added support for LEAP
+	* added driver interface for Linux ndiswrapper
+	* added priority option for network blocks in the configuration file;
+	  this allows networks to be grouped based on priority (the scan
+	  results are searched for matches with network blocks in this order)
+
+2004-06-20 - v0.2.3
+	* sort scan results to improve AP selection
+	* fixed control interface socket removal for some error cases
+	* improved scan requesting and authentication timeout
+	* small improvements/bug fixes for EAP-MSCHAPv2, EAP-PEAP, and
+	  TLS processing
+	* PEAP version can now be forced with phase1="peapver=<ver>"
+	  (mostly for testing; by default, the highest version supported by
+	  both the Supplicant and Authentication Server is selected
+	  automatically)
+	* added support for madwifi driver (Atheros ar521x)
+	* added a workaround for cases where AP sets Install Tx/Rx bit for
+	  WPA Group Key messages when pairwise keys are used (without this,
+	  the Group Key would be used for Tx and the AP would drop frames
+	  from the station)
+	* added GSM SIM/USIM interface for GSM authentication algorithm for
+	  EAP-SIM; this requires pcsc-lite
+	* added support for ATMEL AT76C5XXx driver
+	* fixed IEEE 802.1X WEP key derivation in the case where Authenticator
+	  does not include key data in the EAPOL-Key frame (i.e., part of
+	  EAP keying material is used as data encryption key)
+	* added support for using plaintext and static WEP networks
+	  (key_mgmt=NONE)
+
+2004-05-31 - v0.2.2
+	* added support for new EAP authentication methods:
+	  EAP-TTLS/EAP-MD5-Challenge
+	  EAP-TTLS/EAP-GTC
+	  EAP-TTLS/EAP-MSCHAPv2
+	  EAP-TTLS/EAP-TLS
+	  EAP-TTLS/MSCHAPv2
+	  EAP-TTLS/MSCHAP
+	  EAP-TTLS/PAP
+	  EAP-TTLS/CHAP
+	  EAP-PEAP/TLS
+	  EAP-PEAP/GTC
+	  EAP-PEAP/MD5-Challenge
+	  EAP-GTC
+	  EAP-SIM (not yet complete; needs GSM/SIM authentication interface)
+	* added support for anonymous identity (to be used when identity is
+	  sent in plaintext; real identity will be used within TLS protected
+	  tunnel (e.g., with EAP-TTLS)
+	* added event messages from wpa_supplicant to frontends, e.g., wpa_cli
+	* added support for requesting identity and password information using
+	  control interface; in other words, the password for EAP-PEAP or
+	  EAP-TTLS does not need to be included in the configuration file since
+	  a frontand (e.g., wpa_cli) can ask it from the user
+	* improved RSN pre-authentication to use a candidate list and process
+	  all candidates from each scan; not only one per scan
+	* fixed RSN IE and WPA IE capabilities field parsing
+	* ignore Tx bit in GTK IE when Pairwise keys are used
+	* avoid making new scan requests during IEEE 802.1X negotiation
+	* use openssl/libcrypto for MD5 and SHA-1 when compiling wpa_supplicant
+	  with TLS support (this replaces the included implementation with
+	  library code to save about 8 kB since the library code is needed
+	  anyway for TLS)
+	* fixed WPA-PSK only mode when compiled without IEEE 802.1X support
+	  (i.e., without CONFIG_IEEE8021X_EAPOL=y in .config)
+
+2004-05-06 - v0.2.1
+	* added support for internal IEEE 802.1X (actually, IEEE 802.1aa/D6.1)
+	  Supplicant
+	  - EAPOL state machines for Supplicant [IEEE 802.1aa/D6.1]
+	  - EAP peer state machine [draft-ietf-eap-statemachine-02.pdf]
+	  - EAP-MD5 (cannot be used with WPA-RADIUS)
+	    [draft-ietf-eap-rfc2284bis-09.txt]
+	  - EAP-TLS [RFC 2716]
+	  - EAP-MSCHAPv2 (currently used only with EAP-PEAP)
+	  - EAP-PEAP/MSCHAPv2 [draft-josefsson-pppext-eap-tls-eap-07.txt]
+	    [draft-kamath-pppext-eap-mschapv2-00.txt]
+	    (PEAP version 0, 1, and parts of 2; only 0 and 1 are enabled by
+	    default; tested with FreeRADIUS, Microsoft IAS, and Funk Odyssey)
+	  - new configuration file options: eap, identity, password, ca_cert,
+	    client_cert, privatekey, private_key_passwd
+	  - Xsupplicant is not required anymore, but it can be used by
+	    disabling the internal IEEE 802.1X Supplicant with -e command line
+	    option
+	  - this code is not included in the default build; Makefile need to
+	    be edited for this (uncomment lines for selected functionality)
+	  - EAP-TLS and EAP-PEAP require openssl libraries
+	* use module prefix in debug messages (WPA, EAP, EAP-TLS, ..)
+	* added support for non-WPA IEEE 802.1X mode with dynamic WEP keys
+	  (i.e., complete IEEE 802.1X/EAP authentication and use IEEE 802.1X
+	   EAPOL-Key frames instead of WPA key handshakes)
+	* added support for IEEE 802.11i/RSN (WPA2)
+	  - improved PTK Key Handshake
+	  - PMKSA caching, pre-authentication
+	* fixed wpa_supplicant to ignore possible extra data after WPA
+	  EAPOL-Key packets (this fixes 'Invalid EAPOL-Key MIC when using
+	  TPTK' error from message 3 of 4-Way Handshake in case the AP
+	  includes extra data after the EAPOL-Key)
+	* added interface for external programs (frontends) to control
+	  wpa_supplicant
+	  - CLI example (wpa_cli) with interactive mode and command line
+	    mode
+	  - replaced SIGUSR1 status/statistics with the new control interface
+	* made some feature compile time configurable
+	  - .config file for make
+	  - driver interfaces (hostap, hermes, ..)
+	  - EAPOL/EAP functions
+
+2004-02-15 - v0.2.0
+	* Initial version of wpa_supplicant
diff --git a/hostap/wpa_supplicant/Makefile b/hostap/wpa_supplicant/Makefile
new file mode 100644
index 0000000..6568084
--- /dev/null
+++ b/hostap/wpa_supplicant/Makefile
@@ -0,0 +1,1840 @@
+ifndef CC
+CC=gcc
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+export LIBDIR ?= /usr/local/lib/
+export INCDIR ?= /usr/local/include/
+export BINDIR ?= /usr/local/sbin/
+PKG_CONFIG ?= pkg-config
+
+CFLAGS += $(EXTRA_CFLAGS)
+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
+BINALL += wpa_passphrase
+endif
+
+ALL = $(BINALL)
+ALL += systemd/wpa_supplicant.service
+ALL += systemd/wpa_supplicant@.service
+ALL += systemd/wpa_supplicant-nl80211@.service
+ALL += systemd/wpa_supplicant-wired@.service
+ALL += dbus/fi.epitest.hostap.WPASupplicant.service
+ALL += dbus/fi.w1.wpa_supplicant1.service
+ifdef CONFIG_BUILD_WPA_CLIENT_SO
+ALL += libwpa_client.so
+endif
+
+
+all: verify_config $(ALL) dynamic_eap_methods
+
+verify_config:
+	@if [ ! -r .config ]; then \
+		echo 'Building wpa_supplicant requires a configuration file'; \
+		echo '(.config). See README for more instructions. You can'; \
+		echo 'run "cp defconfig .config" to create an example'; \
+		echo 'configuration.'; \
+		exit 1; \
+	fi
+
+mkconfig:
+	@if [ -f .config ]; then \
+		echo '.config exists - did not replace it'; \
+		exit 1; \
+	fi
+	echo CONFIG_DRIVER_HOSTAP=y >> .config
+	echo CONFIG_DRIVER_WEXT=y >> .config
+
+$(DESTDIR)$(BINDIR)/%: %
+	install -D $(<) $(@)
+
+install: $(addprefix $(DESTDIR)$(BINDIR)/,$(BINALL))
+	$(MAKE) -C ../src install
+ifdef CONFIG_BUILD_WPA_CLIENT_SO
+	install -m 0644 -D libwpa_client.so $(DESTDIR)/$(LIBDIR)/libwpa_client.so
+	install -m 0644 -D ../src/common/wpa_ctrl.h $(DESTDIR)/$(INCDIR)/wpa_ctrl.h
+endif
+
+ifdef CONFIG_FIPS
+CONFIG_NO_RANDOM_POOL=
+CONFIG_OPENSSL_CMAC=y
+endif
+
+OBJS = config.o
+OBJS += notify.o
+OBJS += bss.o
+OBJS += eap_register.o
+OBJS += ../src/utils/common.o
+OBJS += ../src/utils/wpa_debug.o
+OBJS += ../src/utils/wpabuf.o
+OBJS_p = wpa_passphrase.o
+OBJS_p += ../src/utils/common.o
+OBJS_p += ../src/utils/wpa_debug.o
+OBJS_p += ../src/utils/wpabuf.o
+OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o
+OBJS_c += ../src/utils/wpa_debug.o
+OBJS_c += ../src/utils/common.o
+OBJS += wmm_ac.o
+
+ifndef CONFIG_OS
+ifdef CONFIG_NATIVE_WINDOWS
+CONFIG_OS=win32
+else
+CONFIG_OS=unix
+endif
+endif
+
+ifeq ($(CONFIG_OS), internal)
+CFLAGS += -DOS_NO_C_LIB_DEFINES
+endif
+
+OBJS += ../src/utils/os_$(CONFIG_OS).o
+OBJS_p += ../src/utils/os_$(CONFIG_OS).o
+OBJS_c += ../src/utils/os_$(CONFIG_OS).o
+
+ifdef CONFIG_WPA_TRACE
+CFLAGS += -DWPA_TRACE
+LDFLAGS += -rdynamic
+CFLAGS += -funwind-tables
+CONFIG_COMPILE_TRACE=1
+endif
+ifdef CONFIG_WPA_TRACE_SIGNALS
+CFLAGS += -DWPA_TRACE_SIGNALS
+CONFIG_COMPILE_TRACE=1
+endif
+ifdef CONFIG_COMPILE_TRACE
+OBJS += ../src/utils/trace.o
+OBJS_p += ../src/utils/trace.o
+OBJS_c += ../src/utils/trace.o
+OBJS_priv += ../src/utils/trace.o
+#LIBCTRL += ../src/utils/trace.o
+#LIBCTRLSO += ../src/utils/trace.c
+endif
+
+ifdef CONFIG_WPA_TRACE
+ifdef CONFIG_WPA_TRACE_BFD
+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
+
+ifndef CONFIG_ELOOP
+CONFIG_ELOOP=eloop
+endif
+OBJS += ../src/utils/$(CONFIG_ELOOP).o
+OBJS_c += ../src/utils/$(CONFIG_ELOOP).o
+
+ifndef CONFIG_OSX
+ifeq ($(CONFIG_ELOOP), eloop)
+# Using glibc < 2.17 requires -lrt for clock_gettime()
+# OS X has an alternate implementation
+LIBS += -lrt
+LIBS_c += -lrt
+LIBS_p += -lrt
+endif
+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
+
+ifdef CONFIG_VHT_OVERRIDES
+CFLAGS += -DCONFIG_VHT_OVERRIDES
+endif
+
+ifndef CONFIG_BACKEND
+CONFIG_BACKEND=file
+endif
+
+ifeq ($(CONFIG_BACKEND), file)
+OBJS += config_file.o
+ifndef CONFIG_NO_CONFIG_BLOBS
+NEED_BASE64=y
+endif
+CFLAGS += -DCONFIG_BACKEND_FILE
+endif
+
+ifeq ($(CONFIG_BACKEND), winreg)
+OBJS += config_winreg.o
+endif
+
+ifeq ($(CONFIG_BACKEND), none)
+OBJS += config_none.o
+endif
+
+ifdef CONFIG_NO_CONFIG_WRITE
+CFLAGS += -DCONFIG_NO_CONFIG_WRITE
+endif
+
+ifdef CONFIG_NO_CONFIG_BLOBS
+CFLAGS += -DCONFIG_NO_CONFIG_BLOBS
+endif
+
+ifdef CONFIG_NO_SCAN_PROCESSING
+CFLAGS += -DCONFIG_NO_SCAN_PROCESSING
+endif
+
+ifdef CONFIG_SUITEB
+CFLAGS += -DCONFIG_SUITEB
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_SUITEB192
+CFLAGS += -DCONFIG_SUITEB192
+NEED_SHA384=y
+endif
+
+ifdef CONFIG_IEEE80211W
+CFLAGS += -DCONFIG_IEEE80211W
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211R
+OBJS += ../src/rsn_supp/wpa_ft.o
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_MESH
+NEED_80211_COMMON=y
+NEED_SHA256=y
+NEED_AES_SIV=y
+NEED_AES_OMAC1=y
+NEED_AES_CTR=y
+CONFIG_SAE=y
+CONFIG_AP=y
+CFLAGS += -DCONFIG_MESH
+OBJS += mesh.o
+OBJS += mesh_mpm.o
+OBJS += mesh_rsn.o
+endif
+
+ifdef CONFIG_SAE
+CFLAGS += -DCONFIG_SAE
+OBJS += ../src/common/sae.o
+NEED_ECC=y
+NEED_DH_GROUPS=y
+endif
+
+ifdef CONFIG_WNM
+CFLAGS += -DCONFIG_WNM
+OBJS += wnm_sta.o
+endif
+
+ifdef CONFIG_TDLS
+CFLAGS += -DCONFIG_TDLS
+OBJS += ../src/rsn_supp/tdls.o
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_TDLS_TESTING
+CFLAGS += -DCONFIG_TDLS_TESTING
+endif
+
+ifdef CONFIG_PEERKEY
+CFLAGS += -DCONFIG_PEERKEY
+endif
+
+ifndef CONFIG_NO_WPA
+OBJS += ../src/rsn_supp/wpa.o
+OBJS += ../src/rsn_supp/preauth.o
+OBJS += ../src/rsn_supp/pmksa_cache.o
+OBJS += ../src/rsn_supp/peerkey.o
+OBJS += ../src/rsn_supp/wpa_ie.o
+OBJS += ../src/common/wpa_common.o
+NEED_AES=y
+NEED_SHA1=y
+NEED_MD5=y
+NEED_RC4=y
+else
+CFLAGS += -DCONFIG_NO_WPA
+endif
+
+ifdef CONFIG_IBSS_RSN
+NEED_RSN_AUTHENTICATOR=y
+CFLAGS += -DCONFIG_IBSS_RSN
+OBJS += ibss_rsn.o
+endif
+
+ifdef CONFIG_P2P
+OBJS += p2p_supplicant.o
+OBJS += p2p_supplicant_sd.o
+OBJS += ../src/p2p/p2p.o
+OBJS += ../src/p2p/p2p_utils.o
+OBJS += ../src/p2p/p2p_parse.o
+OBJS += ../src/p2p/p2p_build.o
+OBJS += ../src/p2p/p2p_go_neg.o
+OBJS += ../src/p2p/p2p_sd.o
+OBJS += ../src/p2p/p2p_pd.o
+OBJS += ../src/p2p/p2p_invitation.o
+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
+CONFIG_WPS=y
+CONFIG_AP=y
+ifdef CONFIG_P2P_STRICT
+CFLAGS += -DCONFIG_P2P_STRICT
+endif
+endif
+
+ifdef CONFIG_WIFI_DISPLAY
+CFLAGS += -DCONFIG_WIFI_DISPLAY
+OBJS += wifi_display.o
+endif
+
+ifdef CONFIG_HS20
+OBJS += hs20_supplicant.o
+CFLAGS += -DCONFIG_HS20
+CONFIG_INTERWORKING=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_INTERWORKING
+OBJS += interworking.o
+CFLAGS += -DCONFIG_INTERWORKING
+NEED_GAS=y
+endif
+
+ifdef CONFIG_NO_ROAMING
+CFLAGS += -DCONFIG_NO_ROAMING
+endif
+
+include ../src/drivers/drivers.mak
+ifdef CONFIG_AP
+OBJS_d += $(DRV_BOTH_OBJS)
+CFLAGS += $(DRV_BOTH_CFLAGS)
+LDFLAGS += $(DRV_BOTH_LDFLAGS)
+LIBS += $(DRV_BOTH_LIBS)
+else
+NEED_AP_MLME=
+OBJS_d += $(DRV_WPA_OBJS)
+CFLAGS += $(DRV_WPA_CFLAGS)
+LDFLAGS += $(DRV_WPA_LDFLAGS)
+LIBS += $(DRV_WPA_LIBS)
+endif
+
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=linux
+endif
+
+OBJS_l2 += ../src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).o
+
+ifeq ($(CONFIG_L2_PACKET), pcap)
+ifdef CONFIG_WINPCAP
+CFLAGS += -DCONFIG_WINPCAP
+LIBS += -lwpcap -lpacket
+LIBS_w += -lwpcap
+else
+LIBS += -ldnet -lpcap
+endif
+endif
+
+ifeq ($(CONFIG_L2_PACKET), winpcap)
+LIBS += -lwpcap -lpacket
+LIBS_w += -lwpcap
+endif
+
+ifeq ($(CONFIG_L2_PACKET), freebsd)
+LIBS += -lpcap
+endif
+
+ifdef CONFIG_ERP
+CFLAGS += -DCONFIG_ERP
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
+ifdef CONFIG_EAP_TLS
+# EAP-TLS
+ifeq ($(CONFIG_EAP_TLS), dyn)
+CFLAGS += -DEAP_TLS_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_tls.so
+else
+CFLAGS += -DEAP_TLS
+OBJS += ../src/eap_peer/eap_tls.o
+OBJS_h += ../src/eap_server/eap_server_tls.o
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_UNAUTH_TLS
+# EAP-UNAUTH-TLS
+CFLAGS += -DEAP_UNAUTH_TLS
+ifndef CONFIG_EAP_TLS
+OBJS += ../src/eap_peer/eap_tls.o
+OBJS_h += ../src/eap_server/eap_server_tls.o
+TLS_FUNCS=y
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_PEAP
+# EAP-PEAP
+ifeq ($(CONFIG_EAP_PEAP), dyn)
+CFLAGS += -DEAP_PEAP_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_peap.so
+else
+CFLAGS += -DEAP_PEAP
+OBJS += ../src/eap_peer/eap_peap.o
+OBJS += ../src/eap_common/eap_peap_common.o
+OBJS_h += ../src/eap_server/eap_server_peap.o
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_TTLS
+# EAP-TTLS
+ifeq ($(CONFIG_EAP_TTLS), dyn)
+CFLAGS += -DEAP_TTLS_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_ttls.so
+else
+CFLAGS += -DEAP_TTLS
+OBJS += ../src/eap_peer/eap_ttls.o
+OBJS_h += ../src/eap_server/eap_server_ttls.o
+endif
+TLS_FUNCS=y
+ifndef CONFIG_FIPS
+MS_FUNCS=y
+CHAP=y
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_MD5
+# EAP-MD5
+ifeq ($(CONFIG_EAP_MD5), dyn)
+CFLAGS += -DEAP_MD5_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_md5.so
+else
+CFLAGS += -DEAP_MD5
+OBJS += ../src/eap_peer/eap_md5.o
+OBJS_h += ../src/eap_server/eap_server_md5.o
+endif
+CHAP=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+# backwards compatibility for old spelling
+ifdef CONFIG_MSCHAPV2
+ifndef CONFIG_EAP_MSCHAPV2
+CONFIG_EAP_MSCHAPV2=y
+endif
+endif
+
+ifdef CONFIG_EAP_MSCHAPV2
+# EAP-MSCHAPv2
+ifeq ($(CONFIG_EAP_MSCHAPV2), dyn)
+CFLAGS += -DEAP_MSCHAPv2_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_mschapv2.so
+EAPDYN += ../src/eap_peer/mschapv2.so
+else
+CFLAGS += -DEAP_MSCHAPv2
+OBJS += ../src/eap_peer/eap_mschapv2.o
+OBJS += ../src/eap_peer/mschapv2.o
+OBJS_h += ../src/eap_server/eap_server_mschapv2.o
+endif
+MS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_GTC
+# EAP-GTC
+ifeq ($(CONFIG_EAP_GTC), dyn)
+CFLAGS += -DEAP_GTC_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_gtc.so
+else
+CFLAGS += -DEAP_GTC
+OBJS += ../src/eap_peer/eap_gtc.o
+OBJS_h += ../src/eap_server/eap_server_gtc.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_OTP
+# EAP-OTP
+ifeq ($(CONFIG_EAP_OTP), dyn)
+CFLAGS += -DEAP_OTP_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_otp.so
+else
+CFLAGS += -DEAP_OTP
+OBJS += ../src/eap_peer/eap_otp.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_SIM
+# EAP-SIM
+ifeq ($(CONFIG_EAP_SIM), dyn)
+CFLAGS += -DEAP_SIM_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_sim.so
+else
+CFLAGS += -DEAP_SIM
+OBJS += ../src/eap_peer/eap_sim.o
+OBJS_h += ../src/eap_server/eap_server_sim.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_LEAP
+# EAP-LEAP
+ifeq ($(CONFIG_EAP_LEAP), dyn)
+CFLAGS += -DEAP_LEAP_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_leap.so
+else
+CFLAGS += -DEAP_LEAP
+OBJS += ../src/eap_peer/eap_leap.o
+endif
+MS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_PSK
+# EAP-PSK
+ifeq ($(CONFIG_EAP_PSK), dyn)
+CFLAGS += -DEAP_PSK_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_psk.so
+else
+CFLAGS += -DEAP_PSK
+OBJS += ../src/eap_peer/eap_psk.o ../src/eap_common/eap_psk_common.o
+OBJS_h += ../src/eap_server/eap_server_psk.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_AES=y
+NEED_AES_OMAC1=y
+NEED_AES_ENCBLOCK=y
+NEED_AES_EAX=y
+endif
+
+ifdef CONFIG_EAP_AKA
+# EAP-AKA
+ifeq ($(CONFIG_EAP_AKA), dyn)
+CFLAGS += -DEAP_AKA_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_aka.so
+else
+CFLAGS += -DEAP_AKA
+OBJS += ../src/eap_peer/eap_aka.o
+OBJS_h += ../src/eap_server/eap_server_aka.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_PROXY
+CFLAGS += -DCONFIG_EAP_PROXY
+OBJS += ../src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).o
+include eap_proxy_$(CONFIG_EAP_PROXY).mak
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_AKA_PRIME
+# EAP-AKA'
+ifeq ($(CONFIG_EAP_AKA_PRIME), dyn)
+CFLAGS += -DEAP_AKA_PRIME_DYNAMIC
+else
+CFLAGS += -DEAP_AKA_PRIME
+endif
+NEED_SHA256=y
+endif
+
+ifdef CONFIG_EAP_SIM_COMMON
+OBJS += ../src/eap_common/eap_sim_common.o
+OBJS_h += ../src/eap_server/eap_sim_db.o
+NEED_AES=y
+NEED_FIPS186_2_PRF=y
+endif
+
+ifdef CONFIG_EAP_FAST
+# EAP-FAST
+ifeq ($(CONFIG_EAP_FAST), dyn)
+CFLAGS += -DEAP_FAST_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_fast.so
+EAPDYN += ../src/eap_common/eap_fast_common.o
+else
+CFLAGS += -DEAP_FAST
+OBJS += ../src/eap_peer/eap_fast.o ../src/eap_peer/eap_fast_pac.o
+OBJS += ../src/eap_common/eap_fast_common.o
+OBJS_h += ../src/eap_server/eap_server_fast.o
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+NEED_T_PRF=y
+endif
+
+ifdef CONFIG_EAP_PAX
+# EAP-PAX
+ifeq ($(CONFIG_EAP_PAX), dyn)
+CFLAGS += -DEAP_PAX_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_pax.so
+else
+CFLAGS += -DEAP_PAX
+OBJS += ../src/eap_peer/eap_pax.o ../src/eap_common/eap_pax_common.o
+OBJS_h += ../src/eap_server/eap_server_pax.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_SAKE
+# EAP-SAKE
+ifeq ($(CONFIG_EAP_SAKE), dyn)
+CFLAGS += -DEAP_SAKE_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_sake.so
+else
+CFLAGS += -DEAP_SAKE
+OBJS += ../src/eap_peer/eap_sake.o ../src/eap_common/eap_sake_common.o
+OBJS_h += ../src/eap_server/eap_server_sake.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_GPSK
+# EAP-GPSK
+ifeq ($(CONFIG_EAP_GPSK), dyn)
+CFLAGS += -DEAP_GPSK_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_gpsk.so
+else
+CFLAGS += -DEAP_GPSK
+OBJS += ../src/eap_peer/eap_gpsk.o ../src/eap_common/eap_gpsk_common.o
+OBJS_h += ../src/eap_server/eap_server_gpsk.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+ifdef CONFIG_EAP_GPSK_SHA256
+CFLAGS += -DEAP_GPSK_SHA256
+endif
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_EAP_PWD
+CFLAGS += -DEAP_PWD
+OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o
+OBJS_h += ../src/eap_server/eap_server_pwd.o
+CONFIG_IEEE8021X_EAPOL=y
+NEED_SHA256=y
+endif
+
+ifdef CONFIG_EAP_EKE
+# EAP-EKE
+ifeq ($(CONFIG_EAP_EKE), dyn)
+CFLAGS += -DEAP_EKE_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_eke.so
+else
+CFLAGS += -DEAP_EKE
+OBJS += ../src/eap_peer/eap_eke.o ../src/eap_common/eap_eke_common.o
+OBJS_h += ../src/eap_server/eap_server_eke.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+NEED_SHA256=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_WPS
+# EAP-WSC
+CFLAGS += -DCONFIG_WPS -DEAP_WSC
+OBJS += wps_supplicant.o
+OBJS += ../src/utils/uuid.o
+OBJS += ../src/eap_peer/eap_wsc.o ../src/eap_common/eap_wsc_common.o
+OBJS += ../src/wps/wps.o
+OBJS += ../src/wps/wps_common.o
+OBJS += ../src/wps/wps_attr_parse.o
+OBJS += ../src/wps/wps_attr_build.o
+OBJS += ../src/wps/wps_attr_process.o
+OBJS += ../src/wps/wps_dev_attr.o
+OBJS += ../src/wps/wps_enrollee.o
+OBJS += ../src/wps/wps_registrar.o
+OBJS_h += ../src/eap_server/eap_server_wsc.o
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_AES_CBC=y
+NEED_MODEXP=y
+
+ifdef CONFIG_WPS_NFC
+CFLAGS += -DCONFIG_WPS_NFC
+OBJS += ../src/wps/ndef.o
+NEED_WPS_OOB=y
+endif
+
+ifdef NEED_WPS_OOB
+CFLAGS += -DCONFIG_WPS_OOB
+endif
+
+ifdef CONFIG_WPS_ER
+CONFIG_WPS_UPNP=y
+CFLAGS += -DCONFIG_WPS_ER
+OBJS += ../src/wps/wps_er.o
+OBJS += ../src/wps/wps_er_ssdp.o
+endif
+
+ifdef CONFIG_WPS_UPNP
+CFLAGS += -DCONFIG_WPS_UPNP
+OBJS += ../src/wps/wps_upnp.o
+OBJS += ../src/wps/wps_upnp_ssdp.o
+OBJS += ../src/wps/wps_upnp_web.o
+OBJS += ../src/wps/wps_upnp_event.o
+OBJS += ../src/wps/wps_upnp_ap.o
+OBJS += ../src/wps/upnp_xml.o
+OBJS += ../src/wps/httpread.o
+OBJS += ../src/wps/http_client.o
+OBJS += ../src/wps/http_server.o
+endif
+
+ifdef CONFIG_WPS_STRICT
+CFLAGS += -DCONFIG_WPS_STRICT
+OBJS += ../src/wps/wps_validate.o
+endif
+
+ifdef CONFIG_WPS_TESTING
+CFLAGS += -DCONFIG_WPS_TESTING
+endif
+
+ifdef CONFIG_WPS_REG_DISABLE_OPEN
+CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN
+endif
+
+endif
+
+ifdef CONFIG_EAP_IKEV2
+# EAP-IKEv2
+ifeq ($(CONFIG_EAP_IKEV2), dyn)
+CFLAGS += -DEAP_IKEV2_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_ikev2.so ../src/eap_peer/ikev2.o
+EAPDYN += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o
+else
+CFLAGS += -DEAP_IKEV2
+OBJS += ../src/eap_peer/eap_ikev2.o ../src/eap_peer/ikev2.o
+OBJS += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o
+OBJS_h += ../src/eap_server/eap_server_ikev2.o
+OBJS_h += ../src/eap_server/ikev2.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+NEED_MODEXP=y
+NEED_CIPHER=y
+endif
+
+ifdef CONFIG_EAP_VENDOR_TEST
+ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn)
+CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_vendor_test.so
+else
+CFLAGS += -DEAP_VENDOR_TEST
+OBJS += ../src/eap_peer/eap_vendor_test.o
+OBJS_h += ../src/eap_server/eap_server_vendor_test.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_TNC
+# EAP-TNC
+CFLAGS += -DEAP_TNC
+OBJS += ../src/eap_peer/eap_tnc.o
+OBJS += ../src/eap_peer/tncc.o
+OBJS_h += ../src/eap_server/eap_server_tnc.o
+OBJS_h += ../src/eap_server/tncs.o
+NEED_BASE64=y
+ifndef CONFIG_NATIVE_WINDOWS
+ifndef CONFIG_DRIVER_BSD
+LIBS += -ldl
+endif
+endif
+endif
+
+ifdef CONFIG_IEEE8021X_EAPOL
+# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication)
+CFLAGS += -DIEEE8021X_EAPOL
+OBJS += ../src/eapol_supp/eapol_supp_sm.o
+OBJS += ../src/eap_peer/eap.o ../src/eap_peer/eap_methods.o
+NEED_EAP_COMMON=y
+ifdef CONFIG_DYNAMIC_EAP_METHODS
+CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
+LIBS += -ldl -rdynamic
+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_EAP_COMMON=y
+NEED_RSN_AUTHENTICATOR=y
+CFLAGS += -DCONFIG_AP
+OBJS += ap.o
+CFLAGS += -DCONFIG_NO_RADIUS
+CFLAGS += -DCONFIG_NO_ACCOUNTING
+CFLAGS += -DCONFIG_NO_VLAN
+OBJS += ../src/ap/hostapd.o
+OBJS += ../src/ap/wpa_auth_glue.o
+OBJS += ../src/ap/utils.o
+OBJS += ../src/ap/authsrv.o
+OBJS += ../src/ap/ap_config.o
+OBJS += ../src/utils/ip_addr.o
+OBJS += ../src/ap/sta_info.o
+OBJS += ../src/ap/tkip_countermeasures.o
+OBJS += ../src/ap/ap_mlme.o
+OBJS += ../src/ap/ieee802_1x.o
+OBJS += ../src/eapol_auth/eapol_auth_sm.o
+OBJS += ../src/ap/ieee802_11_auth.o
+OBJS += ../src/ap/ieee802_11_shared.o
+OBJS += ../src/ap/drv_callbacks.o
+OBJS += ../src/ap/ap_drv_ops.o
+OBJS += ../src/ap/beacon.o
+OBJS += ../src/ap/bss_load.o
+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
+endif
+ifdef CONFIG_CTRL_IFACE
+OBJS += ../src/ap/ctrl_iface_ap.o
+endif
+
+CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
+OBJS += ../src/eap_server/eap_server.o
+OBJS += ../src/eap_server/eap_server_identity.o
+OBJS += ../src/eap_server/eap_server_methods.o
+
+ifdef CONFIG_IEEE80211N
+CFLAGS += -DCONFIG_IEEE80211N
+ifdef CONFIG_IEEE80211AC
+CFLAGS += -DCONFIG_IEEE80211AC
+endif
+endif
+
+ifdef NEED_AP_MLME
+OBJS += ../src/ap/wmm.o
+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
+CFLAGS += -DEAP_SERVER_WSC
+OBJS += ../src/ap/wps_hostapd.o
+OBJS += ../src/eap_server/eap_server_wsc.o
+endif
+ifdef CONFIG_INTERWORKING
+OBJS += ../src/ap/gas_serv.o
+endif
+ifdef CONFIG_HS20
+OBJS += ../src/ap/hs20.o
+endif
+endif
+
+ifdef NEED_RSN_AUTHENTICATOR
+CFLAGS += -DCONFIG_NO_RADIUS
+NEED_AES_WRAP=y
+OBJS += ../src/ap/wpa_auth.o
+OBJS += ../src/ap/wpa_auth_ie.o
+OBJS += ../src/ap/pmksa_cache_auth.o
+ifdef CONFIG_IEEE80211R
+OBJS += ../src/ap/wpa_auth_ft.o
+endif
+ifdef CONFIG_PEERKEY
+OBJS += ../src/ap/peerkey_auth.o
+endif
+endif
+
+ifdef CONFIG_EAP_SERVER
+CFLAGS += -DEAP_SERVER
+OBJS_h += ../src/eap_server/eap_server.o
+OBJS_h += ../src/eap_server/eap_server_identity.o
+OBJS_h += ../src/eap_server/eap_server_methods.o
+endif
+
+ifdef CONFIG_RADIUS_CLIENT
+OBJS_h += ../src/utils/ip_addr.o
+OBJS_h += ../src/radius/radius.o
+OBJS_h += ../src/radius/radius_client.o
+endif
+
+ifdef CONFIG_AUTHENTICATOR
+OBJS_h += ../src/eapol_auth/eapol_auth_sm.o
+OBJS_h += ../src/ap/ieee802_1x.o
+endif
+
+ifdef CONFIG_WPA_AUTHENTICATOR
+OBJS_h += ../src/ap/wpa_auth.o
+OBJS_h += ../src/ap/wpa_auth_ie.o
+OBJS_h += ../src/ap/pmksa_cache_auth.o
+ifdef CONFIG_IEEE80211R
+OBJS_h += ../src/ap/wpa_auth_ft.o
+endif
+ifdef CONFIG_PEERKEY
+OBJS_h += ../src/ap/peerkey_auth.o
+endif
+endif
+
+ifdef CONFIG_PCSC
+# PC/SC interface for smartcards (USIM, GSM SIM)
+CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC
+OBJS += ../src/utils/pcsc_funcs.o
+# -lpthread may not be needed depending on how pcsc-lite was configured
+ifdef CONFIG_NATIVE_WINDOWS
+#Once MinGW gets support for WinScard, -lwinscard could be used instead of the
+#dynamic symbol loading that is now used in pcsc_funcs.c
+#LIBS += -lwinscard
+else
+LIBS += -lpcsclite -lpthread
+endif
+endif
+
+ifdef CONFIG_SIM_SIMULATOR
+CFLAGS += -DCONFIG_SIM_SIMULATOR
+NEED_MILENAGE=y
+endif
+
+ifdef CONFIG_USIM_SIMULATOR
+CFLAGS += -DCONFIG_USIM_SIMULATOR
+NEED_MILENAGE=y
+endif
+
+ifdef NEED_MILENAGE
+OBJS += ../src/crypto/milenage.o
+NEED_AES_ENCBLOCK=y
+endif
+
+ifdef CONFIG_PKCS12
+CFLAGS += -DPKCS12_FUNCS
+endif
+
+ifdef CONFIG_SMARTCARD
+CFLAGS += -DCONFIG_SMARTCARD
+endif
+
+ifdef MS_FUNCS
+OBJS += ../src/crypto/ms_funcs.o
+NEED_DES=y
+NEED_MD4=y
+endif
+
+ifdef CHAP
+OBJS += ../src/eap_common/chap.o
+endif
+
+ifdef TLS_FUNCS
+NEED_DES=y
+# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
+OBJS += ../src/eap_peer/eap_tls_common.o
+OBJS_h += ../src/eap_server/eap_server_tls_common.o
+ifndef CONFIG_FIPS
+NEED_TLS_PRF=y
+NEED_SHA1=y
+NEED_MD5=y
+endif
+endif
+
+ifndef CONFIG_TLS
+CONFIG_TLS=openssl
+endif
+
+ifdef CONFIG_TLSV11
+CFLAGS += -DCONFIG_TLSV11
+endif
+
+ifdef CONFIG_TLSV12
+CFLAGS += -DCONFIG_TLSV12
+NEED_SHA256=y
+endif
+
+ifeq ($(CONFIG_TLS), openssl)
+ifdef TLS_FUNCS
+CFLAGS += -DEAP_TLS_OPENSSL
+OBJS += ../src/crypto/tls_openssl.o
+LIBS += -lssl
+endif
+OBJS += ../src/crypto/crypto_openssl.o
+OBJS_p += ../src/crypto/crypto_openssl.o
+OBJS_priv += ../src/crypto/crypto_openssl.o
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_openssl.o
+endif
+NEED_SHA256=y
+NEED_TLS_PRF_SHA256=y
+LIBS += -lcrypto
+LIBS_p += -lcrypto
+ifdef CONFIG_TLS_ADD_DL
+LIBS += -ldl
+LIBS_p += -ldl
+endif
+endif
+
+ifeq ($(CONFIG_TLS), gnutls)
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/tls_gnutls.o
+LIBS += -lgnutls -lgpg-error
+endif
+OBJS += ../src/crypto/crypto_gnutls.o
+OBJS_p += ../src/crypto/crypto_gnutls.o
+OBJS_priv += ../src/crypto/crypto_gnutls.o
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
+endif
+LIBS += -lgcrypt
+LIBS_p += -lgcrypt
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+
+ifeq ($(CONFIG_TLS), internal)
+ifndef CONFIG_CRYPTO
+CONFIG_CRYPTO=internal
+endif
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/crypto_internal-rsa.o
+OBJS += ../src/crypto/tls_internal.o
+OBJS += ../src/tls/tlsv1_common.o
+OBJS += ../src/tls/tlsv1_record.o
+OBJS += ../src/tls/tlsv1_cred.o
+OBJS += ../src/tls/tlsv1_client.o
+OBJS += ../src/tls/tlsv1_client_write.o
+OBJS += ../src/tls/tlsv1_client_read.o
+OBJS += ../src/tls/asn1.o
+OBJS += ../src/tls/rsa.o
+OBJS += ../src/tls/x509v3.o
+OBJS += ../src/tls/pkcs1.o
+OBJS += ../src/tls/pkcs5.o
+OBJS += ../src/tls/pkcs8.o
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+ifdef CONFIG_TLSV12
+NEED_TLS_PRF_SHA256=y
+endif
+NEED_MODEXP=y
+NEED_CIPHER=y
+CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+endif
+ifdef NEED_CIPHER
+NEED_DES=y
+OBJS += ../src/crypto/crypto_internal-cipher.o
+endif
+ifdef NEED_MODEXP
+OBJS += ../src/crypto/crypto_internal-modexp.o
+OBJS += ../src/tls/bignum.o
+endif
+ifeq ($(CONFIG_CRYPTO), libtomcrypt)
+OBJS += ../src/crypto/crypto_libtomcrypt.o
+OBJS_p += ../src/crypto/crypto_libtomcrypt.o
+LIBS += -ltomcrypt -ltfm
+LIBS_p += -ltomcrypt -ltfm
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), internal)
+OBJS += ../src/crypto/crypto_internal.o
+OBJS_p += ../src/crypto/crypto_internal.o
+NEED_AES_ENC=y
+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+ifdef CONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST
+CFLAGS += -DLTM_FAST
+endif
+else
+LIBS += -ltommath
+LIBS_p += -ltommath
+endif
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_DES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD4=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), cryptoapi)
+OBJS += ../src/crypto/crypto_cryptoapi.o
+OBJS_p += ../src/crypto/crypto_cryptoapi.o
+CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+ifeq ($(CONFIG_TLS), none)
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/tls_none.o
+CFLAGS += -DEAP_TLS_NONE
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+endif
+OBJS += ../src/crypto/crypto_none.o
+OBJS_p += ../src/crypto/crypto_none.o
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+
+ifdef TLS_FUNCS
+ifdef CONFIG_SMARTCARD
+ifndef CONFIG_NATIVE_WINDOWS
+ifneq ($(CONFIG_L2_PACKET), freebsd)
+LIBS += -ldl
+endif
+endif
+endif
+endif
+
+ifndef TLS_FUNCS
+OBJS += ../src/crypto/tls_none.o
+ifeq ($(CONFIG_TLS), internal)
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+AESOBJS = # none so far (see below)
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-dec.o
+endif
+
+ifneq ($(CONFIG_TLS), openssl)
+NEED_INTERNAL_AES_WRAP=y
+endif
+ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+# Seems to be needed at least with BoringSSL
+NEED_INTERNAL_AES_WRAP=y
+CFLAGS += -DCONFIG_OPENSSL_INTERNAL_AES_WRAP
+endif
+ifdef CONFIG_FIPS
+# Have to use internal AES key wrap routines to use OpenSSL EVP since the
+# OpenSSL AES_wrap_key()/AES_unwrap_key() API is not available in FIPS mode.
+NEED_INTERNAL_AES_WRAP=y
+endif
+
+ifdef NEED_INTERNAL_AES_WRAP
+AESOBJS += ../src/crypto/aes-unwrap.o
+endif
+ifdef NEED_AES_EAX
+AESOBJS += ../src/crypto/aes-eax.o
+NEED_AES_CTR=y
+endif
+ifdef NEED_AES_CTR
+AESOBJS += ../src/crypto/aes-ctr.o
+endif
+ifdef NEED_AES_ENCBLOCK
+AESOBJS += ../src/crypto/aes-encblock.o
+endif
+ifdef NEED_AES_OMAC1
+NEED_AES_ENC=y
+ifdef CONFIG_OPENSSL_CMAC
+CFLAGS += -DCONFIG_OPENSSL_CMAC
+else
+AESOBJS += ../src/crypto/aes-omac1.o
+endif
+endif
+ifdef NEED_AES_SIV
+AESOBJS += ../src/crypto/aes-siv.o
+endif
+ifdef NEED_AES_WRAP
+NEED_AES_ENC=y
+ifdef NEED_INTERNAL_AES_WRAP
+AESOBJS += ../src/crypto/aes-wrap.o
+endif
+endif
+ifdef NEED_AES_CBC
+NEED_AES_ENC=y
+ifneq ($(CONFIG_TLS), openssl)
+AESOBJS += ../src/crypto/aes-cbc.o
+endif
+endif
+ifdef NEED_AES_ENC
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += ../src/crypto/aes-internal-enc.o
+endif
+endif
+ifdef NEED_AES
+OBJS += $(AESOBJS)
+endif
+
+ifdef NEED_SHA1
+ifneq ($(CONFIG_TLS), openssl)
+SHA1OBJS += ../src/crypto/sha1.o
+endif
+SHA1OBJS += ../src/crypto/sha1-prf.o
+ifdef CONFIG_INTERNAL_SHA1
+SHA1OBJS += ../src/crypto/sha1-internal.o
+ifdef NEED_FIPS186_2_PRF
+SHA1OBJS += ../src/crypto/fips_prf_internal.o
+endif
+endif
+ifdef CONFIG_NO_WPA_PASSPHRASE
+CFLAGS += -DCONFIG_NO_PBKDF2
+else
+ifneq ($(CONFIG_TLS), openssl)
+SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
+endif
+endif
+ifdef NEED_T_PRF
+SHA1OBJS += ../src/crypto/sha1-tprf.o
+endif
+ifdef NEED_TLS_PRF
+SHA1OBJS += ../src/crypto/sha1-tlsprf.o
+endif
+endif
+
+ifndef CONFIG_FIPS
+ifneq ($(CONFIG_TLS), openssl)
+MD5OBJS += ../src/crypto/md5.o
+endif
+endif
+ifdef NEED_MD5
+ifdef CONFIG_INTERNAL_MD5
+MD5OBJS += ../src/crypto/md5-internal.o
+endif
+OBJS += $(MD5OBJS)
+OBJS_p += $(MD5OBJS)
+OBJS_priv += $(MD5OBJS)
+endif
+
+ifdef NEED_MD4
+ifdef CONFIG_INTERNAL_MD4
+OBJS += ../src/crypto/md4-internal.o
+endif
+endif
+
+DESOBJS = # none needed when not internal
+ifdef NEED_DES
+ifdef CONFIG_INTERNAL_DES
+DESOBJS += ../src/crypto/des-internal.o
+endif
+endif
+
+ifdef CONFIG_NO_RC4
+CFLAGS += -DCONFIG_NO_RC4
+endif
+
+ifdef NEED_RC4
+ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
+OBJS += ../src/crypto/rc4.o
+endif
+endif
+endif
+
+SHA256OBJS = # none by default
+ifdef NEED_SHA256
+CFLAGS += -DCONFIG_SHA256
+ifneq ($(CONFIG_TLS), openssl)
+SHA256OBJS += ../src/crypto/sha256.o
+endif
+SHA256OBJS += ../src/crypto/sha256-prf.o
+ifdef CONFIG_INTERNAL_SHA256
+SHA256OBJS += ../src/crypto/sha256-internal.o
+endif
+ifdef NEED_TLS_PRF_SHA256
+SHA256OBJS += ../src/crypto/sha256-tlsprf.o
+endif
+ifdef NEED_HMAC_SHA256_KDF
+OBJS += ../src/crypto/sha256-kdf.o
+endif
+OBJS += $(SHA256OBJS)
+endif
+ifdef NEED_SHA384
+CFLAGS += -DCONFIG_SHA384
+OBJS += ../src/crypto/sha384-prf.o
+endif
+
+ifdef NEED_DH_GROUPS
+OBJS += ../src/crypto/dh_groups.o
+endif
+ifdef NEED_DH_GROUPS_ALL
+CFLAGS += -DALL_DH_GROUPS
+endif
+ifdef CONFIG_INTERNAL_DH_GROUP5
+ifdef NEED_DH_GROUPS
+OBJS += ../src/crypto/dh_group5.o
+endif
+endif
+
+ifdef NEED_ECC
+CFLAGS += -DCONFIG_ECC
+endif
+
+ifdef CONFIG_NO_RANDOM_POOL
+CFLAGS += -DCONFIG_NO_RANDOM_POOL
+else
+OBJS += ../src/crypto/random.o
+endif
+
+ifdef CONFIG_CTRL_IFACE
+ifeq ($(CONFIG_CTRL_IFACE), y)
+ifdef CONFIG_NATIVE_WINDOWS
+CONFIG_CTRL_IFACE=named_pipe
+else
+CONFIG_CTRL_IFACE=unix
+endif
+endif
+CFLAGS += -DCONFIG_CTRL_IFACE
+ifeq ($(CONFIG_CTRL_IFACE), unix)
+CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+endif
+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
+ifeq ($(CONFIG_CTRL_IFACE), udp-remote)
+CONFIG_CTRL_IFACE=udp
+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
+
+ifdef CONFIG_CTRL_IFACE_DBUS
+DBUS=y
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE
+DBUS_OBJS += dbus/dbus_old.o dbus/dbus_old_handlers.o
+ifdef CONFIG_WPS
+DBUS_OBJS += dbus/dbus_old_handlers_wps.o
+endif
+DBUS_OBJS += dbus/dbus_dict_helpers.o
+ifndef DBUS_LIBS
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
+endif
+ifndef DBUS_INCLUDE
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
+endif
+DBUS_CFLAGS += $(DBUS_INCLUDE)
+endif
+
+ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+DBUS=y
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW
+DBUS_OBJS ?= dbus/dbus_dict_helpers.o
+DBUS_OBJS += dbus/dbus_new_helpers.o
+DBUS_OBJS += dbus/dbus_new.o dbus/dbus_new_handlers.o
+ifdef CONFIG_WPS
+DBUS_OBJS += dbus/dbus_new_handlers_wps.o
+endif
+ifdef CONFIG_P2P
+DBUS_OBJS += dbus/dbus_new_handlers_p2p.o
+endif
+ifndef DBUS_LIBS
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
+endif
+ifndef DBUS_INCLUDE
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
+endif
+ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
+DBUS_OBJS += dbus/dbus_new_introspect.o
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
+endif
+DBUS_CFLAGS += $(DBUS_INCLUDE)
+endif
+
+ifdef DBUS
+DBUS_CFLAGS += -DCONFIG_DBUS
+DBUS_OBJS += dbus/dbus_common.o
+endif
+
+OBJS += $(DBUS_OBJS)
+CFLAGS += $(DBUS_CFLAGS)
+LIBS += $(DBUS_LIBS)
+
+ifdef CONFIG_READLINE
+OBJS_c += ../src/utils/edit_readline.o
+LIBS_c += -lncurses -lreadline
+else
+ifdef CONFIG_WPA_CLI_EDIT
+OBJS_c += ../src/utils/edit.o
+else
+OBJS_c += ../src/utils/edit_simple.o
+endif
+endif
+
+ifdef CONFIG_NATIVE_WINDOWS
+CFLAGS += -DCONFIG_NATIVE_WINDOWS
+LIBS += -lws2_32 -lgdi32 -lcrypt32
+LIBS_c += -lws2_32
+LIBS_p += -lws2_32 -lgdi32
+ifeq ($(CONFIG_CRYPTO), cryptoapi)
+LIBS_p += -lcrypt32
+endif
+endif
+
+ifdef CONFIG_NO_STDOUT_DEBUG
+CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
+ifndef CONFIG_CTRL_IFACE
+CFLAGS += -DCONFIG_NO_WPA_MSG
+endif
+endif
+
+ifdef CONFIG_IPV6
+# for eapol_test only
+CFLAGS += -DCONFIG_IPV6
+endif
+
+ifdef NEED_BASE64
+OBJS += ../src/utils/base64.o
+endif
+
+ifdef NEED_SME
+OBJS += sme.o
+CFLAGS += -DCONFIG_SME
+endif
+
+OBJS += ../src/common/ieee802_11_common.o
+OBJS += ../src/common/hw_features_common.o
+
+ifdef NEED_EAP_COMMON
+OBJS += ../src/eap_common/eap_common.o
+endif
+
+ifndef CONFIG_MAIN
+CONFIG_MAIN=main
+endif
+
+ifdef CONFIG_DEBUG_SYSLOG
+CFLAGS += -DCONFIG_DEBUG_SYSLOG
+ifdef CONFIG_DEBUG_SYSLOG_FACILITY
+CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)"
+endif
+endif
+
+ifdef CONFIG_DEBUG_LINUX_TRACING
+CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING
+endif
+
+ifdef CONFIG_DEBUG_FILE
+CFLAGS += -DCONFIG_DEBUG_FILE
+endif
+
+ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT
+endif
+
+ifdef CONFIG_FIPS
+CFLAGS += -DCONFIG_FIPS
+ifneq ($(CONFIG_TLS), openssl)
+$(error CONFIG_FIPS=y requires CONFIG_TLS=openssl)
+endif
+endif
+
+OBJS += $(SHA1OBJS) $(DESOBJS)
+
+OBJS_p += $(SHA1OBJS)
+OBJS_p += $(SHA256OBJS)
+OBJS_priv += $(SHA1OBJS)
+
+ifdef CONFIG_BGSCAN_SIMPLE
+CFLAGS += -DCONFIG_BGSCAN_SIMPLE
+OBJS += bgscan_simple.o
+NEED_BGSCAN=y
+endif
+
+ifdef CONFIG_BGSCAN_LEARN
+CFLAGS += -DCONFIG_BGSCAN_LEARN
+OBJS += bgscan_learn.o
+NEED_BGSCAN=y
+endif
+
+ifdef NEED_BGSCAN
+CFLAGS += -DCONFIG_BGSCAN
+OBJS += bgscan.o
+endif
+
+ifdef CONFIG_AUTOSCAN_EXPONENTIAL
+CFLAGS += -DCONFIG_AUTOSCAN_EXPONENTIAL
+OBJS += autoscan_exponential.o
+NEED_AUTOSCAN=y
+endif
+
+ifdef CONFIG_AUTOSCAN_PERIODIC
+CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC
+OBJS += autoscan_periodic.o
+NEED_AUTOSCAN=y
+endif
+
+ifdef NEED_AUTOSCAN
+CFLAGS += -DCONFIG_AUTOSCAN
+OBJS += autoscan.o
+endif
+
+ifdef CONFIG_EXT_PASSWORD_TEST
+OBJS += ../src/utils/ext_password_test.o
+CFLAGS += -DCONFIG_EXT_PASSWORD_TEST
+NEED_EXT_PASSWORD=y
+endif
+
+ifdef NEED_EXT_PASSWORD
+OBJS += ../src/utils/ext_password.o
+CFLAGS += -DCONFIG_EXT_PASSWORD
+endif
+
+ifdef NEED_GAS
+OBJS += ../src/common/gas.o
+OBJS += gas_query.o
+CFLAGS += -DCONFIG_GAS
+NEED_OFFCHANNEL=y
+endif
+
+ifdef NEED_OFFCHANNEL
+OBJS += offchannel.o
+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
+OBJS += ../src/crypto/crypto_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
+
+OBJS_wpa_rm := ctrl_iface.o ctrl_iface_unix.o
+OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o
+ifdef CONFIG_AUTHENTICATOR
+OBJS_wpa += tests/link_test.o
+endif
+OBJS_wpa += $(OBJS_l2)
+OBJS += wpa_supplicant.o events.o blacklist.o wpas_glue.o scan.o
+OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o
+OBJS_t += ../src/radius/radius_client.o
+OBJS_t += ../src/radius/radius.o
+ifndef CONFIG_AP
+OBJS_t += ../src/utils/ip_addr.o
+endif
+OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o
+
+OBJS_nfc := $(OBJS) $(OBJS_l2) nfc_pw_token.o
+OBJS_nfc += $(OBJS_d) ../src/drivers/drivers.o
+
+OBJS += $(CONFIG_MAIN).o
+
+ifdef CONFIG_PRIVSEP
+OBJS_priv += $(OBJS_d) ../src/drivers/drivers.o
+OBJS_priv += $(OBJS_l2)
+OBJS_priv += ../src/utils/os_$(CONFIG_OS).o
+OBJS_priv += ../src/utils/$(CONFIG_ELOOP).o
+OBJS_priv += ../src/utils/common.o
+OBJS_priv += ../src/utils/wpa_debug.o
+OBJS_priv += ../src/utils/wpabuf.o
+OBJS_priv += wpa_priv.o
+ifdef CONFIG_DRIVER_NL80211
+OBJS_priv += ../src/common/ieee802_11_common.o
+endif
+OBJS += ../src/l2_packet/l2_packet_privsep.o
+OBJS += ../src/drivers/driver_privsep.o
+EXTRA_progs += wpa_priv
+else
+OBJS += $(OBJS_d) ../src/drivers/drivers.o
+OBJS += $(OBJS_l2)
+endif
+
+ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED
+OBJS += ../src/drivers/ndis_events.o
+EXTRALIBS += -loleaut32 -lole32 -luuid
+ifdef PLATFORMSDKLIB
+EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib
+else
+EXTRALIBS += WbemUuid.Lib
+endif
+endif
+
+ifdef CONFIG_FST
+CFLAGS += -DCONFIG_FST
+ifdef CONFIG_FST_TEST
+CFLAGS += -DCONFIG_FST_TEST
+endif
+FST_OBJS += ../src/fst/fst.o
+FST_OBJS += ../src/fst/fst_session.o
+FST_OBJS += ../src/fst/fst_iface.o
+FST_OBJS += ../src/fst/fst_group.o
+FST_OBJS += ../src/fst/fst_ctrl_aux.o
+ifdef CONFIG_CTRL_IFACE
+FST_OBJS += ../src/fst/fst_ctrl_iface.o
+endif
+OBJS += $(FST_OBJS)
+OBJS_t += $(FST_OBJS)
+OBJS_t2 += $(FST_OBJS)
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
+
+dynamic_eap_methods: $(EAPDYN)
+
+../src/drivers/build.wpa_supplicant:
+	@if [ -f ../src/drivers/build.hostapd ]; then \
+		$(MAKE) -C ../src/drivers clean; \
+	fi
+	@touch ../src/drivers/build.wpa_supplicant
+
+BCHECK=../src/drivers/build.wpa_supplicant
+
+wpa_priv: $(BCHECK) $(OBJS_priv)
+	$(Q)$(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS)
+	@$(E) "  LD " $@
+
+$(OBJS_c) $(OBJS_t) $(OBJS_t2) $(OBJS) $(BCHECK) $(EXTRA_progs): .config
+
+wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
+	$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
+	@$(E) "  LD " $@
+
+eapol_test: $(OBJS_t)
+	$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
+	@$(E) "  LD " $@
+
+preauth_test: $(OBJS_t2)
+	$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
+	@$(E) "  LD " $@
+
+wpa_passphrase: $(OBJS_p)
+	$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p)
+	@$(E) "  LD " $@
+
+wpa_cli: $(OBJS_c)
+	$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
+	@$(E) "  LD " $@
+
+LIBCTRL += ../src/common/wpa_ctrl.o
+LIBCTRL += ../src/utils/os_$(CONFIG_OS).o
+LIBCTRL += ../src/utils/wpa_debug.o
+LIBCTRLSO += ../src/common/wpa_ctrl.c
+LIBCTRLSO += ../src/utils/os_$(CONFIG_OS).c
+LIBCTRLSO += ../src/utils/wpa_debug.c
+
+libwpa_client.a: $(LIBCTRL)
+	$(Q)rm -f $@
+	$(Q)$(AR) crs $@ $?
+	@$(E) "  AR " $@
+
+libwpa_client.so: $(LIBCTRLSO)
+	@$(E) "  CC  $@ ($^)"
+	$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -fPIC $^
+
+link_test: $(OBJS) $(OBJS_h) tests/link_test.o
+	$(Q)$(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS)
+	@$(E) "  LD " $@
+
+test_wpa: $(OBJS_wpa) $(OBJS_h)
+	$(Q)$(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS)
+	@$(E) "  LD " $@
+
+nfc_pw_token: $(OBJS_nfc)
+	$(Q)$(LDO) $(LDFLAGS) -o nfc_pw_token $(OBJS_nfc) $(LIBS)
+	@$(E) "  LD " $@
+
+win_if_list: win_if_list.c
+	$(Q)$(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w)
+	@$(E) "  LD " $@
+
+eap_psk.so: ../src/eap_peer/eap_psk.c ../src/eap_common/eap_psk_common.c
+	$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
+		-Deap_peer_psk_register=eap_peer_method_dynamic_init
+
+eap_pax.so: ../src/eap_peer/eap_pax.c ../src/eap_common/eap_pax_common.c
+	$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
+		-Deap_peer_pax_register=eap_peer_method_dynamic_init
+
+eap_sake.so: ../src/eap_peer/eap_sake.c ../src/eap_common/eap_sake_common.c
+	$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
+		-Deap_peer_sake_register=eap_peer_method_dynamic_init
+
+eap_wsc.so: ../src/eap_peer/eap_wsc.c ../src/eap_common/eap_wsc_common.c ../src/wps/wps.c
+	$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
+		-Deap_peer_wsc_register=eap_peer_method_dynamic_init
+
+eap_ikev2.so: ../src/eap_peer/eap_ikev2.c ../src/eap_peer/ikev2.c ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.c
+	$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
+		-Deap_peer_ikev2_register=eap_peer_method_dynamic_init
+
+eap_eke.so: ../src/eap_peer/eap_eke.c ../src/eap_common/eap_eke_common.c
+	$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
+		-Deap_peer_eke_register=eap_peer_method_dynamic_init
+
+%.so: %.c
+	$(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
+	$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+	@$(E) "  sed" $<
+
+%@.service: %.service.arg.in
+	$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+	@$(E) "  sed" $<
+
+wpa_supplicant.exe: wpa_supplicant
+	mv -f $< $@
+wpa_cli.exe: wpa_cli
+	mv -f $< $@
+wpa_passphrase.exe: wpa_passphrase
+	mv -f $< $@
+win_if_list.exe: win_if_list
+	mv -f $< $@
+eapol_test.exe: eapol_test
+	mv -f $< $@
+
+WINALL=wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe win_if_list.exe
+
+windows-bin: $(WINALL)
+	$(STRIP) $(WINALL)
+
+wpa_gui:
+	@echo "wpa_gui has been removed - see wpa_gui-qt4 for replacement"
+
+wpa_gui-qt4/Makefile:
+	qmake -o wpa_gui-qt4/Makefile wpa_gui-qt4/wpa_gui.pro
+
+wpa_gui-qt4/lang/wpa_gui_de.qm: wpa_gui-qt4/lang/wpa_gui_de.ts
+	lrelease wpa_gui-qt4/wpa_gui.pro
+
+wpa_gui-qt4: wpa_gui-qt4/Makefile wpa_gui-qt4/lang/wpa_gui_de.qm
+	$(MAKE) -C wpa_gui-qt4
+
+TEST_EAP_SIM_COMMON_OBJS = $(SHA1OBJS) $(MD5OBJS) \
+	../src/utils/common.o ../src/utils/os_unix.o \
+	../src/utils/wpa_debug.o $(AESOBJS) \
+	tests/test_eap_sim_common.o
+test-eap_sim_common: $(TEST_EAP_SIM_COMMON_OBJS)
+	$(LDO) $(LDFLAGS) -o $@ $(TEST_EAP_SIM_COMMON_OBJS) $(LIBS)
+	./test-eap_sim_common
+	rm test-eap_sim_common
+
+tests: test-eap_sim_common
+
+FIPSDIR=/usr/local/ssl/fips-2.0
+FIPSLD=$(FIPSDIR)/bin/fipsld
+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 *.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
+	rm -f libwpa_client.a
+	rm -f libwpa_client.so
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/wpa_supplicant/README b/hostap/wpa_supplicant/README
new file mode 100644
index 0000000..f9c65d2
--- /dev/null
+++ b/hostap/wpa_supplicant/README
@@ -0,0 +1,1054 @@
+WPA Supplicant
+==============
+
+Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors
+All Rights Reserved.
+
+This program is 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.
+
+
+
+License
+-------
+
+This software may be distributed, used, and modified under the terms of
+BSD license:
+
+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.
+
+
+
+Features
+--------
+
+Supported WPA/IEEE 802.11i features:
+- WPA-PSK ("WPA-Personal")
+- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise")
+  Following authentication methods are supported with an integrate IEEE 802.1X
+  Supplicant:
+  * EAP-TLS
+  * EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1)
+  * EAP-PEAP/TLS (both PEAPv0 and PEAPv1)
+  * EAP-PEAP/GTC (both PEAPv0 and PEAPv1)
+  * EAP-PEAP/OTP (both PEAPv0 and PEAPv1)
+  * EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1)
+  * EAP-TTLS/EAP-MD5-Challenge
+  * EAP-TTLS/EAP-GTC
+  * EAP-TTLS/EAP-OTP
+  * EAP-TTLS/EAP-MSCHAPv2
+  * EAP-TTLS/EAP-TLS
+  * EAP-TTLS/MSCHAPv2
+  * EAP-TTLS/MSCHAP
+  * EAP-TTLS/PAP
+  * EAP-TTLS/CHAP
+  * EAP-SIM
+  * EAP-AKA
+  * EAP-PSK
+  * EAP-PAX
+  * EAP-SAKE
+  * EAP-IKEv2
+  * EAP-GPSK
+  * LEAP (note: requires special support from the driver for IEEE 802.11
+	  authentication)
+  (following methods are supported, but since they do not generate keying
+   material, they cannot be used with WPA or IEEE 802.1X WEP keying)
+  * EAP-MD5-Challenge 
+  * EAP-MSCHAPv2
+  * EAP-GTC
+  * EAP-OTP
+- key management for CCMP, TKIP, WEP104, WEP40
+- RSN/WPA2 (IEEE 802.11i)
+  * pre-authentication
+  * PMKSA caching
+
+Supported TLS/crypto libraries:
+- OpenSSL (default)
+- GnuTLS
+
+Internal TLS/crypto implementation (optional):
+- can be used in place of an external TLS/crypto library
+- TLSv1
+- X.509 certificate processing
+- PKCS #1
+- ASN.1
+- RSA
+- bignum
+- minimal size (ca. 50 kB binary, parts of which are already needed for WPA;
+  TLSv1/X.509/ASN.1/RSA/bignum parts are about 25 kB on x86)
+
+
+Requirements
+------------
+
+Current hardware/software requirements:
+- Linux kernel 2.4.x or 2.6.x with Linux Wireless Extensions v15 or newer
+- FreeBSD 6-CURRENT
+- NetBSD-current
+- Microsoft Windows with WinPcap (at least WinXP, may work with other versions)
+- drivers:
+	Linux drivers that support cfg80211/nl80211. Even though there are
+	number of driver specific interface included in wpa_supplicant, please
+	note that Linux drivers are moving to use generic wireless configuration
+	interface driver_nl80211 (-Dnl80211 on wpa_supplicant command line)
+	should be the default option to start with before falling back to driver
+	specific interface.
+
+	Linux drivers that support WPA/WPA2 configuration with the generic
+	Linux wireless extensions (WE-18 or newer). Obsoleted by nl80211.
+
+	In theory, any driver that supports Linux wireless extensions can be
+	used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in
+	configuration file.
+
+	Wired Ethernet drivers (with ap_scan=0)
+
+	BSD net80211 layer (e.g., Atheros driver)
+	At the moment, this is for FreeBSD 6-CURRENT branch and NetBSD-current.
+
+	Windows NDIS
+	The current Windows port requires WinPcap (http://winpcap.polito.it/).
+	See README-Windows.txt for more information.
+
+wpa_supplicant was designed to be portable for different drivers and
+operating systems. Hopefully, support for more wlan cards and OSes will be
+added in the future. See developer's documentation
+(http://hostap.epitest.fi/wpa_supplicant/devel/) for more information about the
+design of wpa_supplicant and porting to other drivers. One main goal
+is to add full WPA/WPA2 support to Linux wireless extensions to allow
+new drivers to be supported without having to implement new
+driver-specific interface code in wpa_supplicant.
+
+Optional libraries for layer2 packet processing:
+- libpcap (tested with 0.7.2, most relatively recent versions assumed to work,
+	this is likely to be available with most distributions,
+	http://tcpdump.org/)
+- libdnet (tested with v1.4, most versions assumed to work,
+	http://libdnet.sourceforge.net/)
+
+These libraries are _not_ used in the default Linux build. Instead,
+internal Linux specific implementation is used. libpcap/libdnet are
+more portable and they can be used by adding CONFIG_L2_PACKET=pcap into
+.config. They may also be selected automatically for other operating
+systems. In case of Windows builds, WinPcap is used by default
+(CONFIG_L2_PACKET=winpcap).
+
+
+Optional libraries for EAP-TLS, EAP-PEAP, and EAP-TTLS:
+- OpenSSL (tested with 0.9.7c and 0.9.7d, and 0.9.8 versions; assumed to
+  work with most relatively recent versions; this is likely to be
+  available with most distributions, http://www.openssl.org/)
+- GnuTLS
+- internal TLSv1 implementation
+
+TLS options for EAP-FAST:
+- OpenSSL 0.9.8d _with_ openssl-0.9.8d-tls-extensions.patch applied
+  (i.e., the default OpenSSL package does not include support for
+  extensions needed for EAP-FAST)
+- internal TLSv1 implementation
+
+One of these libraries is needed when EAP-TLS, EAP-PEAP, EAP-TTLS, or
+EAP-FAST support is enabled. WPA-PSK mode does not require this or EAPOL/EAP
+implementation. A configuration file, .config, for compilation is
+needed to enable IEEE 802.1X/EAPOL and EAP methods. Note that EAP-MD5,
+EAP-GTC, EAP-OTP, and EAP-MSCHAPV2 cannot be used alone with WPA, so
+they should only be enabled if testing the EAPOL/EAP state
+machines. However, there can be used as inner authentication
+algorithms with EAP-PEAP and EAP-TTLS.
+
+See Building and installing section below for more detailed
+information about the wpa_supplicant build time configuration.
+
+
+
+WPA
+---
+
+The original security mechanism of IEEE 802.11 standard was not
+designed to be strong and has proven to be insufficient for most
+networks that require some kind of security. Task group I (Security)
+of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked
+to address the flaws of the base standard and has in practice
+completed its work in May 2004. The IEEE 802.11i amendment to the IEEE
+802.11 standard was approved in June 2004 and published in July 2004.
+
+Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the
+IEEE 802.11i work (draft 3.0) to define a subset of the security
+enhancements that can be implemented with existing wlan hardware. This
+is called Wi-Fi Protected Access<TM> (WPA). This has now become a
+mandatory component of interoperability testing and certification done
+by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web
+site (http://www.wi-fi.org/OpenSection/protected_access.asp).
+
+IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm
+for protecting wireless networks. WEP uses RC4 with 40-bit keys,
+24-bit initialization vector (IV), and CRC32 to protect against packet
+forgery. All these choices have proven to be insufficient: key space is
+too small against current attacks, RC4 key scheduling is insufficient
+(beginning of the pseudorandom stream should be skipped), IV space is
+too small and IV reuse makes attacks easier, there is no replay
+protection, and non-keyed authentication does not protect against bit
+flipping packet data.
+
+WPA is an intermediate solution for the security issues. It uses
+Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a
+compromise on strong security and possibility to use existing
+hardware. It still uses RC4 for the encryption like WEP, but with
+per-packet RC4 keys. In addition, it implements replay protection,
+keyed packet authentication mechanism (Michael MIC).
+
+Keys can be managed using two different mechanisms. WPA can either use
+an external authentication server (e.g., RADIUS) and EAP just like
+IEEE 802.1X is using or pre-shared keys without need for additional
+servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal",
+respectively. Both mechanisms will generate a master session key for
+the Authenticator (AP) and Supplicant (client station).
+
+WPA implements a new key handshake (4-Way Handshake and Group Key
+Handshake) for generating and exchanging data encryption keys between
+the Authenticator and Supplicant. This handshake is also used to
+verify that both Authenticator and Supplicant know the master session
+key. These handshakes are identical regardless of the selected key
+management mechanism (only the method for generating master session
+key changes).
+
+
+
+IEEE 802.11i / WPA2
+-------------------
+
+The design for parts of IEEE 802.11i that were not included in WPA has
+finished (May 2004) and this amendment to IEEE 802.11 was approved in
+June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new
+version of WPA called WPA2. This includes, e.g., support for more
+robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC)
+to replace TKIP and optimizations for handoff (reduced number of
+messages in initial key handshake, pre-authentication, and PMKSA caching).
+
+
+
+wpa_supplicant
+--------------
+
+wpa_supplicant is an implementation of the WPA Supplicant component,
+i.e., the part that runs in the client stations. It implements WPA key
+negotiation with a WPA Authenticator and EAP authentication with
+Authentication Server. In addition, it controls the roaming and IEEE
+802.11 authentication/association of the wlan driver.
+
+wpa_supplicant is designed to be a "daemon" program that runs in the
+background and acts as the backend component controlling the wireless
+connection. wpa_supplicant supports separate frontend programs and an
+example text-based frontend, wpa_cli, is included with wpa_supplicant.
+
+Following steps are used when associating with an AP using WPA:
+
+- wpa_supplicant requests the kernel driver to scan neighboring BSSes
+- wpa_supplicant selects a BSS based on its configuration
+- wpa_supplicant requests the kernel driver to associate with the chosen
+  BSS
+- If WPA-EAP: integrated IEEE 802.1X Supplicant completes EAP
+  authentication with the authentication server (proxied by the
+  Authenticator in the AP)
+- If WPA-EAP: master key is received from the IEEE 802.1X Supplicant
+- If WPA-PSK: wpa_supplicant uses PSK as the master session key
+- wpa_supplicant completes WPA 4-Way Handshake and Group Key Handshake
+  with the Authenticator (AP)
+- wpa_supplicant configures encryption keys for unicast and broadcast
+- normal data packets can be transmitted and received
+
+
+
+Building and installing
+-----------------------
+
+In order to be able to build wpa_supplicant, you will first need to
+select which parts of it will be included. This is done by creating a
+build time configuration file, .config, in the wpa_supplicant root
+directory. Configuration options are text lines using following
+format: CONFIG_<option>=y. Lines starting with # are considered
+comments and are ignored. See defconfig file for an example configuration
+and a list of available options and additional notes.
+
+The build time configuration can be used to select only the needed
+features and limit the binary size and requirements for external
+libraries. The main configuration parts are the selection of which
+driver interfaces (e.g., nl80211, wext, ..) and which authentication
+methods (e.g., EAP-TLS, EAP-PEAP, ..) are included.
+
+Following build time configuration options are used to control IEEE
+802.1X/EAPOL and EAP state machines and all EAP methods. Including
+TLS, PEAP, or TTLS will require linking wpa_supplicant with OpenSSL
+library for TLS implementation. Alternatively, GnuTLS or the internal
+TLSv1 implementation can be used for TLS functionaly.
+
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_MD5=y
+CONFIG_EAP_MSCHAPV2=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_OTP=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_AKA=y
+CONFIG_EAP_PSK=y
+CONFIG_EAP_SAKE=y
+CONFIG_EAP_GPSK=y
+CONFIG_EAP_PAX=y
+CONFIG_EAP_LEAP=y
+CONFIG_EAP_IKEV2=y
+
+Following option can be used to include GSM SIM/USIM interface for GSM/UMTS
+authentication algorithm (for EAP-SIM/EAP-AKA). This requires pcsc-lite
+(http://www.linuxnet.com/) for smart card access.
+
+CONFIG_PCSC=y
+
+Following options can be added to .config to select which driver
+interfaces are included.
+
+CONFIG_DRIVER_NL80211=y
+CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_BSD=y
+CONFIG_DRIVER_NDIS=y
+
+Following example includes some more features and driver interfaces that
+are included in the wpa_supplicant package:
+
+CONFIG_DRIVER_NL80211=y
+CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_BSD=y
+CONFIG_DRIVER_NDIS=y
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_MD5=y
+CONFIG_EAP_MSCHAPV2=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_OTP=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_AKA=y
+CONFIG_EAP_PSK=y
+CONFIG_EAP_SAKE=y
+CONFIG_EAP_GPSK=y
+CONFIG_EAP_PAX=y
+CONFIG_EAP_LEAP=y
+CONFIG_EAP_IKEV2=y
+CONFIG_PCSC=y
+
+EAP-PEAP and EAP-TTLS will automatically include configured EAP
+methods (MD5, OTP, GTC, MSCHAPV2) for inner authentication selection.
+
+
+After you have created a configuration file, you can build
+wpa_supplicant and wpa_cli with 'make' command. You may then install
+the binaries to a suitable system directory, e.g., /usr/local/bin.
+
+Example commands:
+
+# build wpa_supplicant and wpa_cli
+make
+# install binaries (this may need root privileges)
+cp wpa_cli wpa_supplicant /usr/local/bin
+
+
+You will need to make a configuration file, e.g.,
+/etc/wpa_supplicant.conf, with network configuration for the networks
+you are going to use. Configuration file section below includes
+explanation fo the configuration file format and includes various
+examples. Once the configuration is ready, you can test whether the
+configuration work by first running wpa_supplicant with following
+command to start it on foreground with debugging enabled:
+
+wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d
+
+Assuming everything goes fine, you can start using following command
+to start wpa_supplicant on background without debugging:
+
+wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B
+
+Please note that if you included more than one driver interface in the
+build time configuration (.config), you may need to specify which
+interface to use by including -D<driver name> option on the command
+line. See following section for more details on command line options
+for wpa_supplicant.
+
+
+
+Command line options
+--------------------
+
+usage:
+  wpa_supplicant [-BddfhKLqqtuvwW] [-P<pid file>] [-g<global ctrl>] \
+        [-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>] [-m<P2P Device config file>] ...
+
+options:
+  -b = optional bridge interface name
+  -B = run daemon in the background
+  -c = Configuration file
+  -C = ctrl_interface parameter (only used if -c is not)
+  -i = interface name
+  -d = increase debugging verbosity (-dd even more)
+  -D = driver name (can be multiple drivers: nl80211,wext)
+  -f = Log output to default log location (normally /tmp)
+  -g = global ctrl_interface
+  -G = global ctrl_interface group
+  -K = include keys (passwords, etc.) in debug output
+  -t = include timestamp in debug messages
+  -h = show this help text
+  -L = show license (BSD)
+  -p = driver parameters
+  -P = PID file
+  -q = decrease debugging verbosity (-qq even less)
+  -u = enable DBus control interface
+  -v = show version
+  -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
+  wext = Linux wireless extensions (generic)
+  wired = wpa_supplicant wired Ethernet driver
+  roboswitch = wpa_supplicant Broadcom switch driver
+  bsd = BSD 802.11 support (Atheros, etc.)
+  ndis = Windows NDIS driver
+
+In most common cases, wpa_supplicant is started with
+
+wpa_supplicant -B -c/etc/wpa_supplicant.conf -iwlan0
+
+This makes the process fork into background.
+
+The easiest way to debug problems, and to get debug log for bug
+reports, is to start wpa_supplicant on foreground with debugging
+enabled:
+
+wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d
+
+If the specific driver wrapper is not known beforehand, it is possible
+to specify multiple comma separated driver wrappers on the command
+line. wpa_supplicant will use the first driver wrapper that is able to
+initialize the interface.
+
+wpa_supplicant -Dnl80211,wext -c/etc/wpa_supplicant.conf -iwlan0
+
+
+wpa_supplicant can control multiple interfaces (radios) either by
+running one process for each interface separately or by running just
+one process and list of options at command line. Each interface is
+separated with -N argument. As an example, following command would
+start wpa_supplicant for two interfaces:
+
+wpa_supplicant \
+	-c wpa1.conf -i wlan0 -D nl80211 -N \
+	-c wpa2.conf -i wlan1 -D wext
+
+
+If the interface is added in a Linux bridge (e.g., br0), the bridge
+interface needs to be configured to wpa_supplicant in addition to the
+main interface:
+
+wpa_supplicant -cw.conf -Dnl80211 -iwlan0 -bbr0
+
+
+Configuration file
+------------------
+
+wpa_supplicant is configured using a text file that lists all accepted
+networks and security policies, including pre-shared keys. See
+example configuration file, wpa_supplicant.conf, for detailed
+information about the configuration format and supported fields.
+
+Changes to configuration file can be reloaded be sending SIGHUP signal
+to wpa_supplicant ('killall -HUP wpa_supplicant'). Similarly,
+reloading can be triggered with 'wpa_cli reconfigure' command.
+
+Configuration file can include one or more network blocks, e.g., one
+for each used SSID. wpa_supplicant will automatically select the best
+betwork based on the order of network blocks in the configuration
+file, network security level (WPA/WPA2 is preferred), and signal
+strength.
+
+Example configuration files for some common configurations:
+
+1) WPA-Personal (PSK) as home network and WPA-Enterprise with EAP-TLS as work
+   network
+
+# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+#
+# home network; allow all valid ciphers
+network={
+	ssid="home"
+	scan_ssid=1
+	key_mgmt=WPA-PSK
+	psk="very secret passphrase"
+}
+#
+# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers
+network={
+	ssid="work"
+	scan_ssid=1
+	key_mgmt=WPA-EAP
+	pairwise=CCMP TKIP
+	group=CCMP TKIP
+	eap=TLS
+	identity="user@example.com"
+	ca_cert="/etc/cert/ca.pem"
+	client_cert="/etc/cert/user.pem"
+	private_key="/etc/cert/user.prv"
+	private_key_passwd="password"
+}
+
+
+2) WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that use old peaplabel
+   (e.g., Funk Odyssey and SBR, Meetinghouse Aegis, Interlink RAD-Series)
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+	ssid="example"
+	scan_ssid=1
+	key_mgmt=WPA-EAP
+	eap=PEAP
+	identity="user@example.com"
+	password="foobar"
+	ca_cert="/etc/cert/ca.pem"
+	phase1="peaplabel=0"
+	phase2="auth=MSCHAPV2"
+}
+
+
+3) EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the
+   unencrypted use. Real identity is sent only within an encrypted TLS tunnel.
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+	ssid="example"
+	scan_ssid=1
+	key_mgmt=WPA-EAP
+	eap=TTLS
+	identity="user@example.com"
+	anonymous_identity="anonymous@example.com"
+	password="foobar"
+	ca_cert="/etc/cert/ca.pem"
+	phase2="auth=MD5"
+}
+
+
+4) IEEE 802.1X (i.e., no WPA) with dynamic WEP keys (require both unicast and
+   broadcast); use EAP-TLS for authentication
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+	ssid="1x-test"
+	scan_ssid=1
+	key_mgmt=IEEE8021X
+	eap=TLS
+	identity="user@example.com"
+	ca_cert="/etc/cert/ca.pem"
+	client_cert="/etc/cert/user.pem"
+	private_key="/etc/cert/user.prv"
+	private_key_passwd="password"
+	eapol_flags=3
+}
+
+
+5) Catch all example that allows more or less all configuration modes. The
+   configuration options are used based on what security policy is used in the
+   selected SSID. This is mostly for testing and is not recommended for normal
+   use.
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+	ssid="example"
+	scan_ssid=1
+	key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
+	pairwise=CCMP TKIP
+	group=CCMP TKIP WEP104 WEP40
+	psk="very secret passphrase"
+	eap=TTLS PEAP TLS
+	identity="user@example.com"
+	password="foobar"
+	ca_cert="/etc/cert/ca.pem"
+	client_cert="/etc/cert/user.pem"
+	private_key="/etc/cert/user.prv"
+	private_key_passwd="password"
+	phase1="peaplabel=0"
+	ca_cert2="/etc/cert/ca2.pem"
+	client_cert2="/etc/cer/user.pem"
+	private_key2="/etc/cer/user.prv"
+	private_key2_passwd="password"
+}
+
+
+6) Authentication for wired Ethernet. This can be used with 'wired' or
+   'roboswitch' interface (-Dwired or -Droboswitch on command line).
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+ap_scan=0
+network={
+	key_mgmt=IEEE8021X
+	eap=MD5
+	identity="user"
+	password="password"
+	eapol_flags=0
+}
+
+
+
+Certificates
+------------
+
+Some EAP authentication methods require use of certificates. EAP-TLS
+uses both server side and client certificates whereas EAP-PEAP and
+EAP-TTLS only require the server side certificate. When client
+certificate is used, a matching private key file has to also be
+included in configuration. If the private key uses a passphrase, this
+has to be configured in wpa_supplicant.conf ("private_key_passwd").
+
+wpa_supplicant supports X.509 certificates in PEM and DER
+formats. User certificate and private key can be included in the same
+file.
+
+If the user certificate and private key is received in PKCS#12/PFX
+format, they need to be converted to suitable PEM/DER format for
+wpa_supplicant. This can be done, e.g., with following commands:
+
+# convert client certificate and private key to PEM format
+openssl pkcs12 -in example.pfx -out user.pem -clcerts
+# convert CA certificate (if included in PFX file) to PEM format
+openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys
+
+
+
+wpa_cli
+-------
+
+wpa_cli is a text-based frontend program for interacting with
+wpa_supplicant. It is used to query current status, change
+configuration, trigger events, and request interactive user input.
+
+wpa_cli can show the current authentication status, selected security
+mode, dot11 and dot1x MIBs, etc. In addition, it can configure some
+variables like EAPOL state machine parameters and trigger events like
+reassociation and IEEE 802.1X logoff/logon. wpa_cli provides a user
+interface to request authentication information, like username and
+password, if these are not included in the configuration. This can be
+used to implement, e.g., one-time-passwords or generic token card
+authentication where the authentication is based on a
+challenge-response that uses an external device for generating the
+response.
+
+The control interface of wpa_supplicant can be configured to allow
+non-root user access (ctrl_interface_group in the configuration
+file). This makes it possible to run wpa_cli with a normal user
+account.
+
+wpa_cli supports two modes: interactive and command line. Both modes
+share the same command set and the main difference is in interactive
+mode providing access to unsolicited messages (event messages,
+username/password requests).
+
+Interactive mode is started when wpa_cli is executed without including
+the command as a command line parameter. Commands are then entered on
+the wpa_cli prompt. In command line mode, the same commands are
+entered as command line arguments for wpa_cli.
+
+
+Interactive authentication parameters request
+
+When wpa_supplicant need authentication parameters, like username and
+password, which are not present in the configuration file, it sends a
+request message to all attached frontend programs, e.g., wpa_cli in
+interactive mode. wpa_cli shows these requests with
+"CTRL-REQ-<type>-<id>:<text>" prefix. <type> is IDENTITY, PASSWORD, or
+OTP (one-time-password). <id> is a unique identifier for the current
+network. <text> is description of the request. In case of OTP request,
+it includes the challenge from the authentication server.
+
+The reply to these requests can be given with 'identity', 'password',
+and 'otp' commands. <id> needs to be copied from the the matching
+request. 'password' and 'otp' commands can be used regardless of
+whether the request was for PASSWORD or OTP. The main difference
+between these two commands is that values given with 'password' are
+remembered as long as wpa_supplicant is running whereas values given
+with 'otp' are used only once and then forgotten, i.e., wpa_supplicant
+will ask frontend for a new value for every use. This can be used to
+implement one-time-password lists and generic token card -based
+authentication.
+
+Example request for password and a matching reply:
+
+CTRL-REQ-PASSWORD-1:Password needed for SSID foobar
+> password 1 mysecretpassword
+
+Example request for generic token card challenge-response:
+
+CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
+> otp 2 9876
+
+
+wpa_cli commands
+
+  status = get current WPA/EAPOL/EAP status
+  mib = get MIB variables (dot1x, dot11)
+  help = show this usage help
+  interface [ifname] = show interfaces/select interface
+  level <debug level> = change debug level
+  license = show full wpa_cli license
+  logoff = IEEE 802.1X EAPOL state machine logoff
+  logon = IEEE 802.1X EAPOL state machine logon
+  set = set variables (shows list of variables when run without arguments)
+  pmksa = show PMKSA cache
+  reassociate = force reassociation
+  reconfigure = force wpa_supplicant to re-read its configuration file
+  preauthenticate <BSSID> = force preauthentication
+  identity <network id> <identity> = configure identity for an SSID
+  password <network id> <password> = configure password for an SSID
+  pin <network id> <pin> = configure pin for an SSID
+  otp <network id> <password> = configure one-time-password for an SSID
+  passphrase <network id> <passphrase> = configure private key passphrase
+    for an SSID
+  bssid <network id> <BSSID> = set preferred BSSID for an SSID
+  list_networks = list configured networks
+  select_network <network id> = select a network (disable others)
+  enable_network <network id> = enable a network
+  disable_network <network id> = disable a network
+  add_network = add a network
+  remove_network <network id> = remove a network
+  set_network <network id> <variable> <value> = set network variables (shows
+    list of variables when run without arguments)
+  get_network <network id> <variable> = get network variables
+  save_config = save the current configuration
+  disconnect = disconnect and wait for reassociate command before connecting
+  scan = request new BSS scan
+  scan_results = get latest scan results
+  get_capability <eap/pairwise/group/key_mgmt/proto/auth_alg> = get capabilies
+  terminate = terminate wpa_supplicant
+  quit = exit wpa_cli
+
+
+wpa_cli command line options
+
+wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] [-a<action file>] \
+        [-P<pid file>] [-g<global ctrl>]  [command..]
+  -h = help (show this usage text)
+  -v = shown version information
+  -a = run in daemon mode executing the action file based on events from
+       wpa_supplicant
+  -B = run a daemon in the background
+  default path: /var/run/wpa_supplicant
+  default interface: first interface found in socket path
+
+
+Using wpa_cli to run external program on connect/disconnect
+-----------------------------------------------------------
+
+wpa_cli can used to run external programs whenever wpa_supplicant
+connects or disconnects from a network. This can be used, e.g., to
+update network configuration and/or trigget DHCP client to update IP
+addresses, etc.
+
+One wpa_cli process in "action" mode needs to be started for each
+interface. For example, the following command starts wpa_cli for the
+default ingterface (-i can be used to select the interface in case of
+more than one interface being used at the same time):
+
+wpa_cli -a/sbin/wpa_action.sh -B
+
+The action file (-a option, /sbin/wpa_action.sh in this example) will
+be executed whenever wpa_supplicant completes authentication (connect
+event) or detects disconnection). The action script will be called
+with two command line arguments: interface name and event (CONNECTED
+or DISCONNECTED). If the action script needs to get more information
+about the current network, it can use 'wpa_cli status' to query
+wpa_supplicant for more information.
+
+Following example can be used as a simple template for an action
+script:
+
+#!/bin/sh
+
+IFNAME=$1
+CMD=$2
+
+if [ "$CMD" = "CONNECTED" ]; then
+    SSID=`wpa_cli -i$IFNAME status | grep ^ssid= | cut -f2- -d=`
+    # configure network, signal DHCP client, etc.
+fi
+
+if [ "$CMD" = "DISCONNECTED" ]; then
+    # remove network configuration, if needed
+    SSID=
+fi
+
+
+
+Integrating with pcmcia-cs/cardmgr scripts
+------------------------------------------
+
+wpa_supplicant needs to be running when using a wireless network with
+WPA. It can be started either from system startup scripts or from
+pcmcia-cs/cardmgr scripts (when using PC Cards). WPA handshake must be
+completed before data frames can be exchanged, so wpa_supplicant
+should be started before DHCP client.
+
+For example, following small changes to pcmcia-cs scripts can be used
+to enable WPA support:
+
+Add MODE="Managed" and WPA="y" to the network scheme in
+/etc/pcmcia/wireless.opts.
+
+Add the following block to the end of 'start' action handler in
+/etc/pcmcia/wireless:
+
+    if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
+	/usr/local/bin/wpa_supplicant -B -c/etc/wpa_supplicant.conf \
+		-i$DEVICE
+    fi
+
+Add the following block to the end of 'stop' action handler (may need
+to be separated from other actions) in /etc/pcmcia/wireless:
+
+    if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
+	killall wpa_supplicant
+    fi
+
+This will make cardmgr start wpa_supplicant when the card is plugged
+in.
+
+
+
+Dynamic interface add and operation without configuration files
+---------------------------------------------------------------
+
+wpa_supplicant can be started without any configuration files or
+network interfaces. When used in this way, a global (i.e., per
+wpa_supplicant process) control interface is used to add and remove
+network interfaces. Each network interface can then be configured
+through a per-network interface control interface. For example,
+following commands show how to start wpa_supplicant without any
+network interfaces and then add a network interface and configure a
+network (SSID):
+
+# Start wpa_supplicant in the background
+wpa_supplicant -g/var/run/wpa_supplicant-global -B
+
+# Add a new interface (wlan0, no configuration file, driver=nl80211, and
+# enable control interface)
+wpa_cli -g/var/run/wpa_supplicant-global interface_add wlan0 \
+	"" nl80211 /var/run/wpa_supplicant
+
+# Configure a network using the newly added network interface:
+wpa_cli -iwlan0 add_network
+wpa_cli -iwlan0 set_network 0 ssid '"test"'
+wpa_cli -iwlan0 set_network 0 key_mgmt WPA-PSK
+wpa_cli -iwlan0 set_network 0 psk '"12345678"'
+wpa_cli -iwlan0 set_network 0 pairwise TKIP
+wpa_cli -iwlan0 set_network 0 group TKIP
+wpa_cli -iwlan0 set_network 0 proto WPA
+wpa_cli -iwlan0 enable_network 0
+
+# At this point, the new network interface should start trying to associate
+# with the WPA-PSK network using SSID test.
+
+# Remove network interface
+wpa_cli -g/var/run/wpa_supplicant-global interface_remove wlan0
+
+
+Privilege separation
+--------------------
+
+To minimize the size of code that needs to be run with root privileges
+(e.g., to control wireless interface operation), wpa_supplicant
+supports optional privilege separation. If enabled, this separates the
+privileged operations into a separate process (wpa_priv) while leaving
+rest of the code (e.g., EAP authentication and WPA handshakes) into an
+unprivileged process (wpa_supplicant) that can be run as non-root
+user. Privilege separation restricts the effects of potential software
+errors by containing the majority of the code in an unprivileged
+process to avoid full system compromise.
+
+Privilege separation is not enabled by default and it can be enabled
+by adding CONFIG_PRIVSEP=y to the build configuration (.config). When
+enabled, the privileged operations (driver wrapper and l2_packet) are
+linked into a separate daemon program, wpa_priv. The unprivileged
+program, wpa_supplicant, will be built with a special driver/l2_packet
+wrappers that communicate with the privileged wpa_priv process to
+perform the needed operations. wpa_priv can control what privileged
+are allowed.
+
+wpa_priv needs to be run with network admin privileges (usually, root
+user). It opens a UNIX domain socket for each interface that is
+included on the command line; any other interface will be off limits
+for wpa_supplicant in this kind of configuration. After this,
+wpa_supplicant can be run as a non-root user (e.g., all standard users
+on a laptop or as a special non-privileged user account created just
+for this purpose to limit access to user files even further).
+
+
+Example configuration:
+- create user group for users that are allowed to use wpa_supplicant
+  ('wpapriv' in this example) and assign users that should be able to
+  use wpa_supplicant into that group
+- create /var/run/wpa_priv directory for UNIX domain sockets and control
+  user access by setting it accessible only for the wpapriv group:
+  mkdir /var/run/wpa_priv
+  chown root:wpapriv /var/run/wpa_priv
+  chmod 0750 /var/run/wpa_priv
+- start wpa_priv as root (e.g., from system startup scripts) with the
+  enabled interfaces configured on the command line:
+  wpa_priv -B -P /var/run/wpa_priv.pid nl80211:wlan0
+- run wpa_supplicant as non-root with a user that is in wpapriv group:
+  wpa_supplicant -i ath0 -c wpa_supplicant.conf
+
+wpa_priv does not use the network interface before wpa_supplicant is
+started, so it is fine to include network interfaces that are not
+available at the time wpa_priv is started. As an alternative, wpa_priv
+can be started when an interface is added (hotplug/udev/etc. scripts).
+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/hostap/wpa_supplicant/README-HS20 b/hostap/wpa_supplicant/README-HS20
new file mode 100644
index 0000000..161dc06
--- /dev/null
+++ b/hostap/wpa_supplicant/README-HS20
@@ -0,0 +1,566 @@
+wpa_supplicant and Hotspot 2.0
+==============================
+
+This document describe how the IEEE 802.11u Interworking and Wi-Fi
+Hotspot 2.0 (Release 1) implementation in wpa_supplicant can be
+configured and how an external component on the client e.g., management
+GUI or Wi-Fi framework) is used to manage this functionality.
+
+
+Introduction to Wi-Fi Hotspot 2.0
+---------------------------------
+
+Hotspot 2.0 is the name of the Wi-Fi Alliance specification that is used
+in the Wi-Fi CERTIFIED Passpoint<TM> program. More information about
+this is available in this white paper:
+
+http://www.wi-fi.org/knowledge-center/white-papers/wi-fi-certified-passpoint%E2%84%A2-new-program-wi-fi-alliance%C2%AE-enable-seamless
+
+The Hotspot 2.0 specification is also available from WFA:
+https://www.wi-fi.org/knowledge-center/published-specifications
+
+The core Interworking functionality (network selection, GAS/ANQP) were
+standardized in IEEE Std 802.11u-2011 which is now part of the IEEE Std
+802.11-2012.
+
+
+wpa_supplicant network selection
+--------------------------------
+
+Interworking support added option for configuring credentials that can
+work with multiple networks as an alternative to configuration of
+network blocks (e.g., per-SSID parameters). When requested to perform
+network selection, wpa_supplicant picks the highest priority enabled
+network block or credential. If a credential is picked (based on ANQP
+information from APs), a temporary network block is created
+automatically for the matching network. This temporary network block is
+used similarly to the network blocks that can be configured by the user,
+but it is not stored into the configuration file and is meant to be used
+only for temporary period of time since a new one can be created
+whenever needed based on ANQP information and the credential.
+
+By default, wpa_supplicant is not using automatic network selection
+unless requested explicitly with the interworking_select command. This
+can be changed with the auto_interworking=1 parameter to perform network
+selection automatically whenever trying to find a network for connection
+and none of the enabled network blocks match with the scan results. This
+case works similarly to "interworking_select auto", i.e., wpa_supplicant
+will internally determine which network or credential is going to be
+used based on configured priorities, scan results, and ANQP information.
+
+
+wpa_supplicant configuration
+----------------------------
+
+Interworking and Hotspot 2.0 functionality are optional components that
+need to be enabled in the wpa_supplicant build configuration
+(.config). This is done by adding following parameters into that file:
+
+CONFIG_INTERWORKING=y
+CONFIG_HS20=y
+
+It should be noted that this functionality requires a driver that
+supports GAS/ANQP operations. This uses the same design as P2P, i.e.,
+Action frame processing and building in user space within
+wpa_supplicant. The Linux nl80211 driver interface provides the needed
+functionality for this.
+
+
+There are number of run-time configuration parameters (e.g., in
+wpa_supplicant.conf when using the configuration file) that can be used
+to control Hotspot 2.0 operations.
+
+# Enable Interworking
+interworking=1
+
+# Enable Hotspot 2.0
+hs20=1
+
+# Parameters for controlling scanning
+
+# Homogenous ESS identifier
+# If this is set, scans will be used to request response only from BSSes
+# belonging to the specified Homogeneous ESS. This is used only if interworking
+# is enabled.
+#hessid=00:11:22:33:44:55
+
+# Access Network Type
+# When Interworking is enabled, scans can be limited to APs that advertise the
+# specified Access Network Type (0..15; with 15 indicating wildcard match).
+# This value controls the Access Network Type value in Probe Request frames.
+#access_network_type=15
+
+# Automatic network selection behavior
+# 0 = do not automatically go through Interworking network selection
+#     (i.e., require explicit interworking_select command for this; default)
+# 1 = perform Interworking network selection if one or more
+#     credentials have been configured and scan did not find a
+#     matching network block
+#auto_interworking=0
+
+
+Credentials can be pre-configured for automatic network selection:
+
+# credential block
+#
+# Each credential used for automatic network selection is configured as a set
+# of parameters that are compared to the information advertised by the APs when
+# interworking_select and interworking_connect commands are used.
+#
+# 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
+#	(and similarly in struct wpa_ssid for network blocks) to change the
+#	Interworking automatic networking selection behavior. The matching
+#	network (based on either an enabled network block or a credential)
+#	with the highest priority value will be selected.
+#
+# pcsc: Use PC/SC and SIM/USIM card
+#
+# realm: Home Realm for Interworking
+#
+# username: Username for Interworking network selection
+#
+# password: Password for Interworking network selection
+#
+# ca_cert: CA certificate for Interworking network selection
+#
+# client_cert: File path to client certificate file (PEM/DER)
+#	This field is used with Interworking networking selection for a case
+#	where client certificate/private key is used for authentication
+#	(EAP-TLS). Full path to the file should be used since working
+#	directory may change when wpa_supplicant is run in the background.
+#
+#	Alternatively, a named configuration blob can be used by setting
+#	this to blob://blob_name.
+#
+# private_key: File path to client private key file (PEM/DER/PFX)
+#	When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+#	commented out. Both the private key and certificate will be read
+#	from the PKCS#12 file in this case. Full path to the file should be
+#	used since working directory may change when wpa_supplicant is run
+#	in the background.
+#
+#	Windows certificate store can be used by leaving client_cert out and
+#	configuring private_key in one of the following formats:
+#
+#	cert://substring_to_match
+#
+#	hash://certificate_thumbprint_in_hex
+#
+#	For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+#
+#	Note that when running wpa_supplicant as an application, the user
+#	certificate store (My user account) is used, whereas computer store
+#	(Computer account) is used when running wpasvc as a service.
+#
+#	Alternatively, a named configuration blob can be used by setting
+#	this to blob://blob_name.
+#
+# private_key_passwd: Password for private key file
+#
+# imsi: IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+#
+# milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
+#	format
+#
+# 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 SubjectName 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. 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
+#	Roaming Consortium OI that can be used to determine which access
+#	points support authentication with this credential. This is an
+#	alternative to the use of the realm parameter. When using Roaming
+#	Consortium to match the network, the EAP parameters need to be
+#	pre-configured with the credential since the NAI Realm information
+#	may not be available or fetched.
+#
+# eap: Pre-configured EAP method
+#	This optional field can be used to specify which EAP method will be
+#	used with this credential. If not set, the EAP method is selected
+#	automatically based on ANQP information (e.g., NAI Realm).
+#
+# phase1: Pre-configure Phase 1 (outer authentication) parameters
+#	This optional field is used with like the 'eap' parameter.
+#
+# phase2: Pre-configure Phase 2 (inner authentication) parameters
+#	This optional field is used with like the 'eap' parameter.
+#
+# excluded_ssid: Excluded SSID
+#	This optional field can be used to excluded specific SSID(s) from
+#	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={
+#	realm="example.com"
+#	username="user@example.com"
+#	password="password"
+#	ca_cert="/etc/wpa_supplicant/ca.pem"
+#	domain="example.com"
+#	domain_suffix_match="example.com"
+#}
+#
+#cred={
+#	imsi="310026-000000000"
+#	milenage="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82"
+#}
+#
+#cred={
+#	realm="example.com"
+#	username="user"
+#	password="password"
+#	ca_cert="/etc/wpa_supplicant/ca.pem"
+#	domain="example.com"
+#	roaming_consortium=223344
+#	eap=TTLS
+#	phase2="auth=MSCHAPV2"
+#}
+
+
+Control interface
+-----------------
+
+wpa_supplicant provides a control interface that can be used from
+external programs to manage various operations. The included command
+line tool, wpa_cli, can be used for manual testing with this interface.
+
+Following wpa_cli interactive mode commands show some examples of manual
+operations related to Hotspot 2.0:
+
+Remove configured networks and credentials:
+
+> remove_network all
+OK
+> remove_cred all
+OK
+
+
+Add a username/password credential:
+
+> add_cred
+0
+> set_cred 0 realm "mail.example.com"
+OK
+> set_cred 0 username "username"
+OK
+> set_cred 0 password "password"
+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:
+
+> add_cred
+1
+> set_cred 1 imsi "23456-0000000000"
+OK
+> set_cred 1 milenage "90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123"
+OK
+> set_cred 1 priority 1
+OK
+
+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:
+
+> add_network
+0
+> set_network 0 key_mgmt WPA-EAP
+OK
+> set_network 0 ssid "enterprise"
+OK
+> set_network 0 eap TTLS
+OK
+> set_network 0 anonymous_identity "anonymous"
+OK
+> set_network 0 identity "user"
+OK
+> set_network 0 password "password"
+OK
+> set_network 0 priority 0
+OK
+> enable_network 0 no-connect
+OK
+
+
+Add an open network:
+
+> add_network
+3
+> set_network 3 key_mgmt NONE
+OK
+> set_network 3 ssid "coffee-shop"
+OK
+> select_network 3
+OK
+
+Note: the return value of add_network is used as the first argument to
+the following set_network commands.
+
+The preferred credentials/networks can be indicated with the priority
+parameter (1 is higher priority than 0).
+
+
+Interworking network selection can be started with interworking_select
+command. This instructs wpa_supplicant to run a network scan and iterate
+through the discovered APs to request ANQP information from the APs that
+advertise support for Interworking/Hotspot 2.0:
+
+> interworking_select
+OK
+<3>Starting ANQP fetch for 02:00:00:00:01:00
+<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list
+<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list
+<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List
+<3>ANQP fetch completed
+<3>INTERWORKING-AP 02:00:00:00:01:00 type=unknown
+
+
+INTERWORKING-AP event messages indicate the APs that support network
+selection and for which there is a matching
+credential. interworking_connect command can be used to select a network
+to connect with:
+
+
+> interworking_connect 02:00:00:00:01:00
+OK
+<3>CTRL-EVENT-SCAN-RESULTS
+<3>SME: Trying to authenticate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz)
+<3>Trying to associate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz)
+<3>Associated with 02:00:00:00:01:00
+<3>CTRL-EVENT-EAP-STARTED EAP authentication started
+<3>CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21
+<3>CTRL-EVENT-EAP-METHOD EAP vendor 0 method 21 (TTLS) selected
+<3>CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully
+<3>WPA: Key negotiation completed with 02:00:00:00:01:00 [PTK=CCMP GTK=CCMP]
+<3>CTRL-EVENT-CONNECTED - Connection to 02:00:00:00:01:00 completed (auth) [id=0 id_str=]
+
+
+wpa_supplicant creates a temporary network block for the selected
+network based on the configured credential and ANQP information from the
+AP:
+
+> list_networks
+network id / ssid / bssid / flags
+0	Example Network	any	[CURRENT]
+> get_network 0 key_mgmt
+WPA-EAP
+> get_network 0 eap
+TTLS
+
+
+Alternatively to using an external program to select the network,
+"interworking_select auto" command can be used to request wpa_supplicant
+to select which network to use based on configured priorities:
+
+
+> remove_network all
+OK
+<3>CTRL-EVENT-DISCONNECTED bssid=02:00:00:00:01:00 reason=1 locally_generated=1
+> interworking_select auto
+OK
+<3>Starting ANQP fetch for 02:00:00:00:01:00
+<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list
+<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list
+<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List
+<3>ANQP fetch completed
+<3>INTERWORKING-AP 02:00:00:00:01:00 type=unknown
+<3>CTRL-EVENT-SCAN-RESULTS
+<3>SME: Trying to authenticate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz)
+<3>Trying to associate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz)
+<3>Associated with 02:00:00:00:01:00
+<3>CTRL-EVENT-EAP-STARTED EAP authentication started
+<3>CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21
+<3>CTRL-EVENT-EAP-METHOD EAP vendor 0 method 21 (TTLS) selected
+<3>CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully
+<3>WPA: Key negotiation completed with 02:00:00:00:01:00 [PTK=CCMP GTK=CCMP]
+<3>CTRL-EVENT-CONNECTED - Connection to 02:00:00:00:01:00 completed (reauth) [id=0 id_str=]
+
+
+The connection status can be shown with the status command:
+
+> status
+bssid=02:00:00:00:01:00
+ssid=Example Network
+id=0
+mode=station
+pairwise_cipher=CCMP       <--- link layer security indication
+group_cipher=CCMP
+key_mgmt=WPA2/IEEE 802.1X/EAP
+wpa_state=COMPLETED
+p2p_device_address=02:00:00:00:00:00
+address=02:00:00:00:00:00
+hs20=1      <--- HS 2.0 indication
+Supplicant PAE state=AUTHENTICATED
+suppPortStatus=Authorized
+EAP state=SUCCESS
+selectedMethod=21 (EAP-TTLS)
+EAP TLS cipher=AES-128-SHA
+EAP-TTLSv0 Phase2 method=PAP
+
+
+> status
+bssid=02:00:00:00:02:00
+ssid=coffee-shop
+id=3
+mode=station
+pairwise_cipher=NONE
+group_cipher=NONE
+key_mgmt=NONE
+wpa_state=COMPLETED
+p2p_device_address=02:00:00:00:00:00
+address=02:00:00:00:00:00
+
+
+Note: The Hotspot 2.0 indication is shown as "hs20=1" in the status
+command output. Link layer security is indicated with the
+pairwise_cipher (CCMP = secure, NONE = no encryption used).
+
+
+Also the scan results include the Hotspot 2.0 indication:
+
+> scan_results
+bssid / frequency / signal level / flags / ssid
+02:00:00:00:01:00	2412	-30	[WPA2-EAP-CCMP][ESS][HS20]	Example Network
+
+
+ANQP information for the BSS can be fetched using the BSS command:
+
+> bss 02:00:00:00:01:00
+id=1
+bssid=02:00:00:00:01:00
+freq=2412
+beacon_int=100
+capabilities=0x0411
+qual=0
+noise=-92
+level=-30
+tsf=1345573286517276
+age=105
+ie=000f4578616d706c65204e6574776f726b010882848b960c1218240301012a010432043048606c30140100000fac040100000fac040100000fac0100007f04000000806b091e07010203040506076c027f006f1001531122331020304050010203040506dd05506f9a1000
+flags=[WPA2-EAP-CCMP][ESS][HS20]
+ssid=Example Network
+anqp_roaming_consortium=031122330510203040500601020304050603fedcba
+
+
+ANQP queries can also be requested with the anqp_get and hs20_anqp_get
+commands:
+
+> anqp_get 02:00:00:00:01:00 261
+OK
+<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list
+> hs20_anqp_get 02:00:00:00:01:00 2
+OK
+<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List
+
+In addition, fetch_anqp command can be used to request similar set of
+ANQP queries to be done as is run as part of interworking_select:
+
+> scan
+OK
+<3>CTRL-EVENT-SCAN-RESULTS
+> fetch_anqp
+OK
+<3>Starting ANQP fetch for 02:00:00:00:01:00
+<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list
+<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list
+<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List
+<3>ANQP fetch completed
diff --git a/hostap/wpa_supplicant/README-P2P b/hostap/wpa_supplicant/README-P2P
new file mode 100644
index 0000000..6a5b032
--- /dev/null
+++ b/hostap/wpa_supplicant/README-P2P
@@ -0,0 +1,844 @@
+wpa_supplicant and Wi-Fi P2P
+============================
+
+This document describes how the Wi-Fi P2P implementation in
+wpa_supplicant can be configured and how an external component on the
+client (e.g., management GUI) is used to enable WPS enrollment and
+registrar registration.
+
+
+Introduction to Wi-Fi P2P
+-------------------------
+
+TODO
+
+More information about Wi-Fi P2P is available from Wi-Fi Alliance:
+http://www.wi-fi.org/Wi-Fi_Direct.php
+
+
+wpa_supplicant implementation
+-----------------------------
+
+TODO
+
+
+wpa_supplicant configuration
+----------------------------
+
+Wi-Fi P2P is an optional component that needs to be enabled in the
+wpa_supplicant build configuration (.config). Here is an example
+configuration that includes Wi-Fi P2P support and Linux nl80211
+-based driver interface:
+
+CONFIG_DRIVER_NL80211=y
+CONFIG_CTRL_IFACE=y
+CONFIG_P2P=y
+CONFIG_AP=y
+CONFIG_WPS=y
+
+
+In run-time configuration file (wpa_supplicant.conf), some parameters
+for P2P may be set. In order to make the devices easier to recognize,
+device_name and device_type should be specified. For example,
+something like this should be included:
+
+ctrl_interface=/var/run/wpa_supplicant
+device_name=My P2P Device
+device_type=1-0050F204-1
+
+
+wpa_cli
+-------
+
+Actual Wi-Fi P2P operations are requested during runtime. These can be
+done for example using wpa_cli (which is described below) or a GUI
+like wpa_gui-qt4.
+
+
+wpa_cli starts in interactive mode if no command string is included on
+the command line. By default, it will select the first network interface
+that it can find (and that wpa_supplicant controls). If more than one
+interface is in use, it may be necessary to select one of the explicitly
+by adding -i argument on the command line (e.g., 'wpa_cli -i wlan1').
+
+Most of the P2P operations are done on the main interface (e.g., the
+interface that is automatically added when the driver is loaded, e.g.,
+wlan0). When using a separate virtual interface for group operations
+(e.g., wlan1), the control interface for that group interface may need
+to be used for some operations (mainly WPS activation in GO). This may
+change in the future so that all the needed operations could be done
+over the main control interface.
+
+Device Discovery
+
+p2p_find [timeout in seconds] [type=<social|progressive>] \
+	[dev_id=<addr>] [dev_type=<device type>] \
+	[delay=<search delay in ms>] [seek=<service name>] [freq=<MHz>]
+
+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
+channels, i.e., it skips the initial full scan. type=progressive is
+like the default behavior, but it will scan through all the channels
+progressively one channel at the time in the Search state rounds. This
+will help in finding new groups or groups missed during the initial
+full scan. When the type parameter is not included (i.e., full scan), the
+optional freq parameter can be used to override the first scan to use only
+the specified channel after which only social channels are scanned.
+
+The optional dev_id option can be used to specify a single P2P peer to
+search for. The optional delay parameter can be used to request an extra
+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".
+
+
+With one or more seek arguments, the command sends Probe Request frames
+for a P2PS service. For example,
+p2p_find 5 dev_id=11:22:33:44:55:66 seek=alt.example.chat seek=alt.example.video
+
+Parameters description:
+    Timeout - Optional ASCII base-10-encoded u16. If missing, request will not
+	time out and must be canceled manually
+    dev_id - Optional to request responses from a single known remote device
+    Service Name - Mandatory UTF-8 string for ASP seeks
+	Service name must match the remote service being advertised exactly
+	(no prefix matching).
+	Service name may be empty, in which case all ASP services will be
+	returned, and may be filtered with p2p_serv_disc_req settings, and
+	p2p_serv_asp_resp results.
+	Multiple service names may be requested, but if it exceeds internal
+	limit, it will automatically revert to requesting all ASP services.
+
+p2p_listen [timeout in seconds]
+
+Start Listen-only state (become discoverable without searching for
+other devices). Optional parameter can be used to specify the duration
+for the Listen operation in seconds. This command may not be of that
+much use during normal operations and is mainly designed for
+testing. It can also be used to keep the device discoverable without
+having to maintain a group.
+
+p2p_stop_find
+
+Stop ongoing P2P device discovery or other operation (connect, listen
+mode).
+
+p2p_flush
+
+Flush P2P peer table and state.
+
+Group Formation
+
+p2p_prov_disc <peer device address> <display|keypad|pbc> [join|auto]
+
+Send P2P provision discovery request to the specified peer. The
+parameters for this command are the P2P device address of the peer and
+the desired configuration method. For example, "p2p_prov_disc
+02:01:02:03:04:05 display" would request the peer to display a PIN for
+us and "p2p_prov_disc 02:01:02:03:04:05 keypad" would request the peer
+to enter a PIN that we display.
+
+The optional "join" parameter can be used to indicate that this command
+is requesting an already running GO to prepare for a new client. This is
+mainly used with "display" to request it to display a PIN. The "auto"
+parameter can be used to request wpa_supplicant to automatically figure
+out whether the peer device is operating as a GO and if so, use
+join-a-group style PD instead of GO Negotiation style PD.
+
+p2p_connect <peer device address> <pbc|pin|PIN#|p2ps> [display|keypad|p2ps]
+	[persistent|persistent=<network id>] [join|auth]
+	[go_intent=<0..15>] [freq=<in MHz>] [ht40] [vht] [provdisc] [auto]
+
+Start P2P group formation with a discovered P2P peer. This includes
+optional group owner negotiation, group interface setup, provisioning,
+and establishing data connection.
+
+The <pbc|pin|PIN#> parameter specifies the WPS provisioning
+method. "pbc" string starts pushbutton method, "pin" string start PIN
+method using an automatically generated PIN (which will be returned as
+the command return code), PIN# means that a pre-selected PIN can be
+used (e.g., 12345670). [display|keypad] is used with PIN method
+to specify which PIN is used (display=dynamically generated random PIN
+from local display, keypad=PIN entered from peer display). "persistent"
+parameter can be used to request a persistent group to be formed. The
+"persistent=<network id>" alternative can be used to pre-populate
+SSID/passphrase configuration based on a previously used persistent
+group where this device was the GO. The previously used parameters will
+then be used if the local end becomes the GO in GO Negotiation (which
+can be forced with go_intent=15).
+
+"join" indicates that this is a command to join an existing group as a
+client. It skips the GO Negotiation part. This will send a Provision
+Discovery Request message to the target GO before associating for WPS
+provisioning.
+
+"auth" indicates that the WPS parameters are authorized for the peer
+device without actually starting GO Negotiation (i.e., the peer is
+expected to initiate GO Negotiation). This is mainly for testing
+purposes.
+
+"go_intent" can be used to override the default GO Intent for this GO
+Negotiation.
+
+"freq" can be used to set a forced operating channel (e.g., freq=2412
+to select 2.4 GHz channel 1).
+
+"provdisc" can be used to request a Provision Discovery exchange to be
+used prior to starting GO Negotiation as a workaround with some deployed
+P2P implementations that require this to allow the user to accept the
+connection.
+
+"auto" can be used to request wpa_supplicant to automatically figure
+out whether the peer device is operating as a GO and if so, use
+join-a-group operation rather than GO Negotiation.
+
+P2PS attribute changes to p2p_connect command:
+
+P2PS supports two WPS provisioning methods namely PIN method and P2PS default.
+The remaining paramters hold same role as in legacy P2P. In case of P2PS default
+config method "p2ps" keyword is added in p2p_connect command.
+
+For example:
+p2p_connect 02:0a:f5:85:11:00 12345670 p2ps persistent join
+	(WPS Method = P2PS default)
+
+p2p_connect 02:0a:f5:85:11:00 45629034 keypad persistent
+	(WPS Method = PIN)
+
+p2p_asp_provision <peer MAC address> <adv_id=peer adv id>
+	<adv_mac=peer MAC address> [role=2|4|1] <session=session id>
+	<session_mac=initiator mac address>
+	[info='service info'] <method=Default|keypad|Display>
+
+This command starts provision discovery with the P2PS enabled peer device.
+
+For example,
+p2p_asp_provision 00:11:22:33:44:55 adv_id=4d6fc7 adv_mac=00:55:44:33:22:11 role=1 session=12ab34 session_mac=00:11:22:33:44:55 info='name=john' method=1000
+
+Parameter description:
+    MAC address - Mandatory
+    adv_id - Mandatory remote Advertising ID of service connection is being
+	established for
+    adv_mac - Mandatory MAC address that owns/registered the service
+    role - Optional
+	2 (group client only) or 4 (group owner only)
+	if not present (or 1) role is negotiated by the two peers.
+    session - Mandatory Session ID of the first session to be established
+    session_mac - Mandatory MAC address that owns/initiated the session
+    method - Optional method to request for provisioning (1000 - P2PS Default,
+	100 - Keypad(PIN), 8 - Display(PIN))
+    info - Optional UTF-8 string. Hint for service to indicate possible usage
+	parameters - Escape single quote & backslash:
+	with a backslash 0x27 == ' == \', and 0x5c == \ == \\
+
+p2p_asp_provision_resp <peer mac address> <adv_id= local adv id>
+	<adv_mac=local MAC address> <role=1|2|4> <status=0>
+	<session=session id> <session_mac=peer MAC address>
+
+This command sends a provision discovery response from responder side.
+
+For example,
+p2p_asp_provision_resp 00:55:44:33:22:11 adv_id=4d6fc7 adv_mac=00:55:44:33:22:11 role=1 status=0 session=12ab34 session_mac=00:11:22:33:44:55
+
+Parameters definition:
+    MAC address - Mandatory
+    adv_id - Mandatory local Advertising ID of service connection is being
+	established for
+    adv_mac - Mandatory MAC address that owns/registered the service
+    role -  Optional 2 (group client only) or 4 (group owner only)
+	if not present (or 1) role is negotiated by the two peers.
+    status - Mandatory Acceptance/Rejection code of Provisioning
+    session - Mandatory Session ID of the first session to be established
+    session_mac - Mandatory MAC address that owns/initiated the session
+
+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
+GO. Optional persistent=<network id> can be used to specify restart of
+a persistent group. Optional freq=<freq in MHz> can be used to force
+the GO to be started on a specific frequency. Special freq=2 or freq=5
+options can be used to request the best 2.4 GHz or 5 GHz band channel
+to be selected automatically.
+
+p2p_reject <peer device address>
+
+Reject connection attempt from a peer (specified with a device
+address). This is a mechanism to reject a pending GO Negotiation with
+a peer and request to automatically block any further connection or
+discovery of the peer.
+
+p2p_group_remove <group interface>
+
+Terminate a P2P group. If a new virtual network interface was used for
+the group, it will also be removed. The network interface name of the
+group interface is used as a parameter for this command.
+
+p2p_cancel
+
+Cancel an ongoing P2P group formation and joining-a-group related
+operation. This operations unauthorizes the specific peer device (if any
+had been authorized to start group formation), stops P2P find (if in
+progress), stops pending operations for join-a-group, and removes the
+P2P group interface (if one was used) that is in the WPS provisioning
+step. If the WPS provisioning step has been completed, the group is not
+terminated.
+
+p2p_remove_client <peer's P2P Device Address|iface=<interface address>>
+
+This command can be used to remove the specified client from all groups
+(operating and persistent) from the local GO. Note that the peer device
+can rejoin the group if it is in possession of a valid key. See p2p_set
+per_sta_psk command below for more details on how the peer can be
+removed securely.
+
+Service Discovery
+
+p2p_service_add asp <auto accept> <adv id> <status 0/1> <Config Methods>
+	<Service name> [Service Information] [Response Info]
+
+This command can be used to search for a P2PS service which includes
+Play, Send, Display, and Print service. The parameters for this command
+are "asp" to identify the command as P2PS one, auto accept value,
+advertisement id which uniquely identifies the service requests, state
+of the service whether the service is available or not, config methods
+which can be either P2PS method or PIN method, service name followed by
+two optional parameters service information, and response info.
+
+For example,
+p2p_service_add asp 1 4d6fc7 0 1108 alt.example.chat svc_info='name=john' rsp_info='enter PIN 1234'
+
+Parameters definition:
+    asp - Mandatory for ASP service registration
+    auto accept - Mandatory ASCII hex-encoded boolean (0 == no auto-accept,
+	1 == auto-accept ANY role, 2 == auto-accept CLIENT role,
+	4 == auto-accept GO role)
+    Advertisement ID - Mandatory non-zero ASCII hex-encoded u32
+	(Must be unique/not yet exist in svc db)
+    State - Mandatory ASCII hex-encoded u8 (0 -- Svc not available,
+	1 -- Svc available, 2-0xff  Application defined)
+    Config Methods - Mandatory ASCII hex-encoded u16 (bitmask of WSC config
+	methods)
+    Service Name - Mandatory UTF-8 string
+    Service Information - Optional UTF-8 string
+	Escape single quote & backslash with a backslash:
+	0x27 == ' == \', and 0x5c == \ == \\
+    Session response information -  Optional (used only if auto accept is TRUE)
+	UTF-8 string
+	Escape single quote & backslash with a backslash:
+	0x27 == ' == \', and 0x5c == \ == \\
+
+p2p_service_rep asp <auto accept> <adv id> <status 0/1> <Config Methods>
+	<Service name> [Service Information] [Response Info]
+
+This command can be used to replace the existing service request
+attributes from the initiator side. The replacement is only allowed if
+the advertisement id issued in the command matches with any one entry in
+the list of existing SD queries. If advertisement id doesn't match the
+command returns a failure.
+
+For example,
+p2p_service_rep asp 1 4d6fc7 1 1108 alt.example.chat svc_info='name=john' rsp_info='enter PIN 1234'
+
+Parameters definition:
+    asp - Mandatory for ASP service registration
+    auto accept - Mandatory ASCII hex-encoded boolean (1 == true, 0 == false)
+    Advertisement ID - Mandatory non-zero ASCII hex-encoded u32
+	(Must already exist in svc db)
+    State - Mandatory ASCII hex-encoded u8 (can be used to indicate svc
+	available or not available for instance)
+    Config Methods - Mandatory ASCII hex-encoded u16 (bitmask of WSC config
+	methods)
+    Service Name - Mandatory UTF-8 string (Must match existing string in svc db)
+    Service Information - Optional UTF-8 string
+	Escape single quote & backslash with a backslash:
+	0x27 == ' == \', and 0x5c == \ == \\
+    Session response information -  Optional (used only if auto accept is TRUE)
+	UTF-8 string
+	Escape single quote & backslash with a backslash:
+	0x27 == ' == \', and 0x5c == \ == \\
+
+p2p_serv_disc_req
+
+Schedule a P2P service discovery request. The parameters for this
+command are the device address of the peer device (or 00:00:00:00:00:00
+for wildcard query that is sent to every discovered P2P peer that
+supports service discovery) and P2P Service Query TLV(s) as hexdump. For
+example,
+
+p2p_serv_disc_req 00:00:00:00:00:00 02000001
+
+schedules a request for listing all available services of all service
+discovery protocols and requests this to be sent to all discovered
+peers (note: this can result in long response frames). The pending
+requests are sent during device discovery (see p2p_find).
+
+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
+will be automatically removed when the specified peer has replied to
+it.
+
+Service Query TLV has following format:
+Length (2 octets, little endian) - length of following data
+Service Protocol Type (1 octet) - see the table below
+Service Transaction ID (1 octet) - nonzero identifier for the TLV
+Query Data (Length - 2 octets of data) - service protocol specific data
+
+Service Protocol Types:
+0 = All service protocols
+1 = Bonjour
+2 = UPnP
+3 = WS-Discovery
+4 = Wi-Fi Display
+
+For UPnP, an alternative command format can be used to specify a
+single query TLV (i.e., a service discovery for a specific UPnP
+service):
+
+p2p_serv_disc_req 00:00:00:00:00:00 upnp <version hex> <ST: from M-SEARCH>
+
+For example:
+
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1
+
+Additional examples for queries:
+
+# list of all Bonjour services
+p2p_serv_disc_req 00:00:00:00:00:00 02000101
+
+# list of all UPnP services
+p2p_serv_disc_req 00:00:00:00:00:00 02000201
+
+# list of all WS-Discovery services
+p2p_serv_disc_req 00:00:00:00:00:00 02000301
+
+# list of all Bonjour and UPnP services
+p2p_serv_disc_req 00:00:00:00:00:00 0200010102000202
+
+# Apple File Sharing over TCP
+p2p_serv_disc_req 00:00:00:00:00:00 130001010b5f6166706f766572746370c00c000c01
+
+# Bonjour SSTH (supported service type hash)
+p2p_serv_disc_req 00:00:00:00:00:00 05000101000000
+
+# UPnP examples
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 ssdp:all
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 upnp:rootdevice
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:service:ContentDirectory:2
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1
+
+# Wi-Fi Display examples
+# format: wifi-display <list of roles> <list of subelements>
+p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source] 2,3,4,5
+p2p_serv_disc_req 02:01:02:03:04:05 wifi-display [pri-sink] 3
+p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [sec-source] 2
+p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source+sink] 2,3,4,5
+p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source][pri-sink] 2,3,4,5
+
+p2p_serv_disc_req <Unicast|Broadcast mac address> asp <Transaction ID>
+	<Service Name> [Service Information]
+
+The command can be used for service discovery for P2PS enabled devices.
+
+For example: p2p_serv_disc_req 00:00:00:00:00:00 asp a1 alt.example 'john'
+
+Parameters definition:
+    MAC address - Mandatory Existing
+    asp - Mandatory for ASP queries
+    Transaction ID - Mandatory non-zero ASCII hex-encoded u8 for GAS
+    Service Name Prefix - Mandatory UTF-8 string.
+	Will match from beginning of remote Service Name
+    Service Information Substring - Optional UTF-8 string
+	If Service Information Substring is not included, all services matching
+	Service Name Prefix will be returned.
+	If Service Information Substring is included, both the Substring and the
+	Service Name Prefix must match for service to be returned.
+	If remote service has no Service Information, all Substring searches
+	will fail.
+
+p2p_serv_disc_cancel_req <query identifier>
+
+Cancel a pending P2P service discovery request. This command takes a
+single parameter: identifier for the pending query (the value returned
+by p2p_serv_disc_req, e.g., "p2p_serv_disc_cancel_req 1f77628".
+
+p2p_serv_disc_resp
+
+Reply to a service discovery query. This command takes following
+parameters: frequency in MHz, destination address, dialog token,
+response TLV(s). The first three parameters are copied from the
+request event. For example, "p2p_serv_disc_resp 2437 02:40:61:c2:f3:b7
+1 0300000101". This command is used only if external program is used
+to process the request (see p2p_serv_disc_external).
+
+p2p_service_update
+
+Indicate that local services have changed. This is used to increment
+the P2P service indicator value so that peers know when previously
+cached information may have changed. This is only needed when external
+service discovery processing is enabled since the commands to
+pre-configure services for internal processing will increment the
+indicator automatically.
+
+p2p_serv_disc_external <0|1>
+
+Configure external processing of P2P service requests: 0 (default) =
+no external processing of requests (i.e., internal code will process
+each request based on pre-configured services), 1 = external
+processing of requests (external program is responsible for replying
+to service discovery requests with p2p_serv_disc_resp). Please note
+that there is quite strict limit on how quickly the response needs to
+be transmitted, so use of the internal processing is strongly
+recommended.
+
+p2p_service_add bonjour <query hexdump> <RDATA hexdump>
+
+Add a local Bonjour service for internal SD query processing.
+
+Examples:
+
+# AFP Over TCP (PTR)
+p2p_service_add bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027
+# AFP Over TCP (TXT) (RDATA=null)
+p2p_service_add bonjour 076578616d706c650b5f6166706f766572746370c00c001001 00
+
+# IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
+p2p_service_add bonjour 045f697070c00c000c01 094d795072696e746572c027
+# IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
+p2p_service_add bonjour 096d797072696e746572045f697070c00c001001 09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
+
+# Supported Service Type Hash (SSTH)
+p2p_service_add bonjour 000000 <32-byte bitfield as hexdump>
+(note: see P2P spec Annex E.4 for information on how to construct the bitfield)
+
+p2p_service_del bonjour <query hexdump>
+
+Remove a local Bonjour service from internal SD query processing.
+
+p2p_service_add upnp <version hex> <service>
+
+Add a local UPnP service for internal SD query processing.
+
+Examples:
+
+p2p_service_add upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
+p2p_service_add upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::upnp:rootdevice
+p2p_service_add upnp 10 uuid:1122de4e-8574-59ab-9322-333456789044::urn:schemas-upnp-org:service:ContentDirectory:2
+p2p_service_add upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::urn:schemas-upnp-org:service:ContentDirectory:2
+p2p_service_add upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp-org:device:InternetGatewayDevice:1
+
+p2p_service_del upnp <version hex> <service>
+
+Remove a local UPnP service from internal SD query processing.
+
+p2p_service_del asp <adv id>
+
+Removes the local asp service from internal SD query list.
+For example: p2p_service_del asp 4d6fc7
+
+p2p_service_flush
+
+Remove all local services from internal SD query processing.
+
+Invitation
+
+p2p_invite [persistent=<network id>|group=<group ifname>] [peer=address]
+	[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
+the persistent group, the peer parameter is not needed. Otherwise it is
+used to specify which device to invite. go_dev_addr parameter can be
+used to override the GO device address for Invitation Request should
+it be not known for some reason (this should not be needed in most
+cases). When reinvoking a persistent group, the GO device can specify
+the frequency for the group with the freq parameter. When reinvoking a
+persistent group, the P2P client device can use freq parameter to force
+a specific operating channel (or invitation failure if GO rejects that)
+or pref parameter to request a specific channel (while allowing GO to
+select to use another channel, if needed).
+
+Group Operations
+
+(These are used on the group interface.)
+
+wps_pin <any|address> <PIN>
+
+Start WPS PIN method. This allows a single WPS Enrollee to connect to
+the AP/GO. This is used on the GO when a P2P client joins an existing
+group. The second parameter is the address of the Enrollee or a string
+"any" to allow any station to use the entered PIN (which will restrict
+the PIN for one-time-use). PIN is the Enrollee PIN read either from a
+label or display on the P2P Client/WPS Enrollee.
+
+wps_pbc
+
+Start WPS PBC method (i.e., push the button). This allows a single WPS
+Enrollee to connect to the AP/GO. This is used on the GO when a P2P
+client joins an existing group.
+
+p2p_get_passphrase
+
+Get the passphrase for a group (only available when acting as a GO).
+
+p2p_presence_req [<duration> <interval>] [<duration> <interval>]
+
+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
+interval values in microseconds. If the second pair is included, that
+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
+
+p2p_ext_listen [<period> <interval>]
+
+Configure Extended Listen Timing. If the parameters are omitted, this
+feature is disabled. If the parameters are included, Listen State will
+be entered every interval msec for at least period msec. Both values
+have acceptable range of 1-65535 (with interval obviously having to be
+larger than or equal to duration). If the P2P module is not idle at
+the time the Extended Listen Timing timeout occurs, the Listen State
+operation will be skipped.
+
+The configured values will also be advertised to other P2P Devices. The
+received values are available in the p2p_peer command output:
+
+ext_listen_period=100 ext_listen_interval=5000
+
+p2p_set <field> <value>
+
+Change dynamic P2P parameters
+
+p2p_set discoverability <0/1>
+
+Disable/enable advertisement of client discoverability. This is
+enabled by default and this parameter is mainly used to allow testing
+of device discoverability.
+
+p2p_set managed <0/1>
+
+Disable/enable managed P2P Device operations. This is disabled by
+default.
+
+p2p_set listen_channel <1/6/11>
+
+Set P2P Listen channel. This is mainly meant for testing purposes and
+changing the Listen channel during normal operations can result in
+protocol failures.
+
+p2p_set ssid_postfix <postfix>
+
+Set postfix string to be added to the automatically generated P2P SSID
+(DIRECT-<two random characters>). For example, postfix of "-testing"
+could result in the SSID becoming DIRECT-ab-testing.
+
+p2p_set per_sta_psk <0/1>
+
+Disabled(default)/enables use of per-client PSK in the P2P groups. This
+can be used to request GO to assign a unique PSK for each client during
+WPS provisioning. When enabled, this allow clients to be removed from
+the group securily with p2p_remove_client command since that client's
+PSK is removed at the same time to prevent it from connecting back using
+the old PSK. When per-client PSK is not used, the client can still be
+disconnected, but it will be able to re-join the group since the PSK it
+learned previously is still valid. It should be noted that the default
+passphrase on the GO that is normally used to allow legacy stations to
+connect through manual configuration does not change here, so if that is
+shared, devices with knowledge of that passphrase can still connect.
+
+set <field> <value>
+
+Set global configuration parameters which may also affect P2P
+operations. The format on these parameters is same as is used in
+wpa_supplicant.conf. Only the parameters listen here should be
+changed. Modifying other parameters may result in incorrect behavior
+since not all existing users of the parameters are updated.
+
+set uuid <UUID>
+
+Set WPS UUID (by default, this is generated based on the MAC address).
+
+set device_name <device name>
+
+Set WPS Device Name (also included in some P2P messages).
+
+set manufacturer <manufacturer>
+
+Set WPS Manufacturer.
+
+set model_name <model name>
+
+Set WPS Model Name.
+
+set model_number <model number>
+
+Set WPS Model Number.
+
+set serial_number <serial number>
+
+Set WPS Serial Number.
+
+set device_type <device type>
+
+Set WPS Device Type.
+
+set os_version <OS version>
+
+Set WPS OS Version.
+
+set config_methods <config methods>
+
+Set WPS Configuration Methods.
+
+set sec_device_type <device type>
+
+Add a new Secondary Device Type.
+
+set p2p_go_intent <GO intent>
+
+Set the default P2P GO Intent. Note: This value can be overridden in
+p2p_connect command and as such, there should be no need to change the
+default value here during normal operations.
+
+set p2p_ssid_postfix <P2P SSID postfix>
+
+Set P2P SSID postfix.
+
+set persistent_reconnect <0/1>
+
+Disable/enabled persistent reconnect for reinvocation of persistent
+groups. If enabled, invitations to reinvoke a persistent group will be
+accepted without separate authorization (e.g., user interaction).
+
+set country <two character country code>
+
+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]
+
+List P2P Device Addresses of all the P2P peers we know. The optional
+"discovered" parameter filters out the peers that we have not fully
+discovered, i.e., which we have only seen in a received Probe Request
+frame.
+
+p2p_peer <P2P Device Address>
+
+Fetch information about a known P2P peer.
+
+Group Status
+
+(These are used on the group interface.)
+
+status
+
+Show status information (connection state, role, use encryption
+parameters, IP address, etc.).
+
+sta
+
+Show information about an associated station (when acting in AP/GO role).
+
+all_sta
+
+Lists the currently associated stations.
+
+Configuration data
+
+list_networks
+
+Lists the configured networks, including stored information for
+persistent groups. The identifier in this list is used with
+p2p_group_add and p2p_invite to indicate which persistent group is to
+be reinvoked.
+
+remove_network <network id>
+
+Remove a network entry from configuration. 
+
+
+P2PS Events/Responses:
+
+P2PS-PROV-START: This events gets triggered when provisioning is issued for
+either seeker or advertiser.
+
+For example,
+P2PS-PROV-START 00:55:44:33:22:11 adv_id=111 adv_mac=00:55:44:33:22:11 conncap=1 session=1234567 session_mac=00:11:22:33:44:55 info='xxxx'
+
+Parameters definition:
+    MAC address - always
+    adv_id - always ASCII hex-encoded u32
+    adv_mac - always MAC address that owns/registered the service
+    conncap - always mask of 0x01 (new), 0x02 (group client), 0x04 (group owner)
+	bits
+    session - always Session ID of the first session to be established
+    session_mac - always MAC address that owns/initiated the session
+    info - if available, UTF-8 string
+	Escaped single quote & backslash with a backslash:
+	\' == 0x27 == ', and \\ == 0x5c == \
+
+P2PS-PROV-DONE: When provisioning is completed then this event gets triggered.
+
+For example,
+P2PS-PROV-DONE 00:11:22:33:44:55 status=0 adv_id=111 adv_mac=00:55:44:33:22:11 conncap=1 session=1234567 session_mac=00:11:22:33:44:55 [dev_passwd_id=8 | go=p2p-wlan0-0 | join=11:22:33:44:55:66 | persist=0]
+
+Parameters definition:
+    MAC address - always main device address of peer. May be different from MAC
+	ultimately connected to.
+    status - always ascii hex-encoded u8 (0 == success, 12 == deferred success)
+    adv_id - always ascii hex-encoded u32
+    adv_mac - always MAC address that owns/registered the service
+    conncap - always One of: 1 (new), 2 (group client), 4 (group owner) bits
+    session - always Session ID of the first session to be established
+    session_mac - always MAC address that owns/initiated the session
+    dev_passwd_id - only if conncap value == 1 (New GO negotiation)
+	8 - "p2ps" password must be passed in p2p_connect command
+	1 - "display" password must be passed in p2p_connect command
+	5 - "keypad" password must be passed in p2p_connect command
+    join only - if conncap value == 2 (Client Only). Display password and "join"
+	must be passed in p2p_connect and address must be the MAC specified
+    go only - if conncap value == 4 (GO Only). Interface name must be set with a
+	password
+    persist - only if previous persistent group existed between peers and shall
+	be re-used. Group is restarted by sending "p2p_group_add persistent=0"
+	where value is taken from P2P-PROV-DONE
+
+Extended Events/Response
+
+P2P-DEVICE-FOUND 00:11:22:33:44:55 p2p_dev_addr=00:11:22:33:44:55 pri_dev_type=0-00000000-0 name='' config_methods=0x108 dev_capab=0x21 group_capab=0x0 adv_id=111 asp_svc=alt.example.chat
+
+Parameters definition:
+    adv_id - if ASP ASCII hex-encoded u32. If it is reporting the
+	"wildcard service", this value will be 0
+    asp_svc - if ASP this is the service string. If it is reporting the
+	"wildcard service", this value will be org.wi-fi.wfds
+
+
+wpa_cli action script
+---------------------
+
+See examples/p2p-action.sh
+
+TODO: describe DHCP/DNS setup
+TODO: cross-connection
diff --git a/hostap/wpa_supplicant/README-WPS b/hostap/wpa_supplicant/README-WPS
new file mode 100644
index 0000000..b884f67
--- /dev/null
+++ b/hostap/wpa_supplicant/README-WPS
@@ -0,0 +1,399 @@
+wpa_supplicant and Wi-Fi Protected Setup (WPS)
+==============================================
+
+This document describes how the WPS implementation in wpa_supplicant
+can be configured and how an external component on the client (e.g.,
+management GUI) is used to enable WPS enrollment and registrar
+registration.
+
+
+Introduction to WPS
+-------------------
+
+Wi-Fi Protected Setup (WPS) is a mechanism for easy configuration of a
+wireless network. It allows automated generation of random keys (WPA
+passphrase/PSK) and configuration of an access point and client
+devices. WPS includes number of methods for setting up connections
+with PIN method and push-button configuration (PBC) being the most
+commonly deployed options.
+
+While WPS can enable more home networks to use encryption in the
+wireless network, it should be noted that the use of the PIN and
+especially PBC mechanisms for authenticating the initial key setup is
+not very secure. As such, use of WPS may not be suitable for
+environments that require secure network access without chance for
+allowing outsiders to gain access during the setup phase.
+
+WPS uses following terms to describe the entities participating in the
+network setup:
+- access point: the WLAN access point
+- Registrar: a device that control a network and can authorize
+  addition of new devices); this may be either in the AP ("internal
+  Registrar") or in an external device, e.g., a laptop, ("external
+  Registrar")
+- Enrollee: a device that is being authorized to use the network
+
+It should also be noted that the AP and a client device may change
+roles (i.e., AP acts as an Enrollee and client device as a Registrar)
+when WPS is used to configure the access point.
+
+
+More information about WPS is available from Wi-Fi Alliance:
+http://www.wi-fi.org/wifi-protected-setup
+
+
+wpa_supplicant implementation
+-----------------------------
+
+wpa_supplicant includes an optional WPS component that can be used as
+an Enrollee to enroll new network credential or as a Registrar to
+configure an AP.
+
+
+wpa_supplicant configuration
+----------------------------
+
+WPS is an optional component that needs to be enabled in
+wpa_supplicant build configuration (.config). Here is an example
+configuration that includes WPS support and Linux nl80211 -based
+driver interface:
+
+CONFIG_DRIVER_NL80211=y
+CONFIG_WPS=y
+
+If you want to enable WPS external registrar (ER) functionality, you
+will also need to add following line:
+
+CONFIG_WPS_ER=y
+
+Following parameter can be used to enable support for NFC config method:
+
+CONFIG_WPS_NFC=y
+
+
+WPS needs the Universally Unique IDentifier (UUID; see RFC 4122) for
+the device. This is configured in the runtime configuration for
+wpa_supplicant (if not set, UUID will be generated based on local MAC
+address):
+
+# example UUID for WPS
+uuid=12345678-9abc-def0-1234-56789abcdef0
+
+The network configuration blocks needed for WPS are added
+automatically based on control interface commands, so they do not need
+to be added explicitly in the configuration file.
+
+WPS registration will generate new network blocks for the acquired
+credentials. If these are to be stored for future use (after
+restarting wpa_supplicant), wpa_supplicant will need to be configured
+to allow configuration file updates:
+
+update_config=1
+
+
+
+External operations
+-------------------
+
+WPS requires either a device PIN code (usually, 8-digit number) or a
+pushbutton event (for PBC) to allow a new WPS Enrollee to join the
+network. wpa_supplicant uses the control interface as an input channel
+for these events.
+
+The PIN value used in the commands must be processed by an UI to
+remove non-digit characters and potentially, to verify the checksum
+digit. "wpa_cli wps_check_pin <PIN>" can be used to do such processing.
+It returns FAIL if the PIN is invalid, or FAIL-CHECKSUM if the checksum
+digit is incorrect, or the processed PIN (non-digit characters removed)
+if the PIN is valid.
+
+If the client device has a display, a random PIN has to be generated
+for each WPS registration session. wpa_supplicant can do this with a
+control interface request, e.g., by calling wpa_cli:
+
+wpa_cli wps_pin any
+
+This will return the generated 8-digit PIN which will then need to be
+entered at the Registrar to complete WPS registration. At that point,
+the client will be enrolled with credentials needed to connect to the
+AP to access the network.
+
+
+If the client device does not have a display that could show the
+random PIN, a hardcoded PIN that is printed on a label can be
+used. wpa_supplicant is notified this with a control interface
+request, e.g., by calling wpa_cli:
+
+wpa_cli wps_pin any 12345670
+
+This starts the WPS negotiation in the same way as above with the
+generated PIN.
+
+When the wps_pin command is issued for an AP (including P2P GO) mode
+interface, an optional timeout parameter can be used to specify
+expiration timeout for the PIN in seconds. For example:
+
+wpa_cli wps_pin any 12345670 300
+
+
+If a random PIN is needed for a user interface, "wpa_cli wps_pin get"
+can be used to generate a new PIN without starting WPS negotiation.
+This random PIN can then be passed as an argument to another wps_pin
+call when the actual operation should be started.
+
+If the client design wants to support optional WPS PBC mode, this can
+be enabled by either a physical button in the client device or a
+virtual button in the user interface. The PBC operation requires that
+a button is also pressed at the AP/Registrar at about the same time (2
+minute window). wpa_supplicant is notified of the local button event
+over the control interface, e.g., by calling wpa_cli:
+
+wpa_cli wps_pbc
+
+At this point, the AP/Registrar has two minutes to complete WPS
+negotiation which will generate a new WPA PSK in the same way as the
+PIN method described above.
+
+
+If the client wants to operate in the Registrar role to learn the
+current AP configuration and optionally, to configure an AP,
+wpa_supplicant is notified over the control interface, e.g., with
+wpa_cli:
+
+wpa_cli wps_reg <AP BSSID> <AP PIN>
+(example: wpa_cli wps_reg 02:34:56:78:9a:bc 12345670)
+
+This is used to fetch the current AP settings instead of actually
+changing them. The main difference with the wps_pin command is that
+wps_reg uses the AP PIN (e.g., from a label on the AP) instead of a
+PIN generated at the client.
+
+In order to change the AP configuration, the new configuration
+parameters are given to the wps_reg command:
+
+wpa_cli wps_reg <AP BSSID> <AP PIN> <new SSID> <auth> <encr> <new key>
+examples:
+  wpa_cli wps_reg 02:34:56:78:9a:bc 12345670 testing WPA2PSK CCMP 12345678
+  wpa_cli wps_reg 02:34:56:78:9a:bc 12345670 clear OPEN NONE ""
+
+<auth> must be one of the following: OPEN WPAPSK WPA2PSK
+<encr> must be one of the following: NONE WEP TKIP CCMP
+
+
+Scanning
+--------
+
+Scan results ('wpa_cli scan_results' or 'wpa_cli bss <idx>') include a
+flags field that is used to indicate whether the BSS support WPS. If
+the AP support WPS, but has not recently activated a Registrar, [WPS]
+flag will be included. If PIN method has been recently selected,
+[WPS-PIN] is shown instead. Similarly, [WPS-PBC] is shown if PBC mode
+is in progress. GUI programs can use these as triggers for suggesting
+a guided WPS configuration to the user. In addition, control interface
+monitor events WPS-AP-AVAILABLE{,-PBC,-PIN} can be used to find out if
+there are WPS enabled APs in scan results without having to go through
+all the details in the GUI. These notification could be used, e.g., to
+suggest possible WPS connection to the user.
+
+
+wpa_gui
+-------
+
+wpa_gui-qt4 directory contains a sample GUI that shows an example of
+how WPS support can be integrated into the GUI. Its main window has a
+WPS tab that guides user through WPS registration with automatic AP
+selection. In addition, it shows how WPS can be started manually by
+selecting an AP from scan results.
+
+
+Credential processing
+---------------------
+
+By default, wpa_supplicant processes received credentials and updates
+its configuration internally. However, it is possible to
+control these operations from external programs, if desired.
+
+This internal processing can be disabled with wps_cred_processing=1
+option. When this is used, an external program is responsible for
+processing the credential attributes and updating wpa_supplicant
+configuration based on them.
+
+Following control interface messages are sent out for external programs:
+
+WPS-CRED-RECEIVED  <hexdump of Credential attribute(s)>
+For example:
+<2>WPS-CRED-RECEIVED 100e006f10260001011045000c6a6b6d2d7770732d74657374100300020020100f000200081027004030653462303435366332363666653064333961643135353461316634626637313234333761636664623766333939653534663166316230323061643434386235102000060266a0ee1727
+
+
+wpa_supplicant as WPS External Registrar (ER)
+---------------------------------------------
+
+wpa_supplicant can be used as a WPS ER to configure an AP or enroll
+new Enrollee to join the network. This functionality uses UPnP and
+requires that a working IP connectivity is available with the AP (this
+can be either over a wired or wireless connection).
+
+Separate wpa_supplicant process can be started for WPS ER
+operations. A special "none" driver can be used in such a case to
+indicate that no local network interface is actually controlled. For
+example, following command could be used to start the ER:
+
+wpa_supplicant -Dnone -c er.conf -ieth0
+
+Sample er.conf:
+
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=admin
+device_name=WPS External Registrar
+
+
+wpa_cli commands for ER functionality:
+
+wps_er_start [IP address]
+- start WPS ER functionality
+- the optional IP address parameter can be used to filter operations only
+  to include a single AP
+- if run again while ER is active, the stored information (discovered APs
+  and Enrollees) are shown again
+
+wps_er_stop
+- stop WPS ER functionality
+
+wps_er_learn <UUID|BSSID> <AP PIN>
+- learn AP configuration
+
+wps_er_set_config <UUID|BSSID> <network id>
+- use AP configuration from a locally configured network (e.g., from
+  wps_reg command); this does not change the AP's configuration, but
+  only prepares a configuration to be used when enrolling a new device
+  to the AP
+
+wps_er_config <UUID|BSSID> <AP PIN> <new SSID> <auth> <encr> <new key>
+- examples:
+  wps_er_config 87654321-9abc-def0-1234-56789abc0002 12345670 testing WPA2PSK CCMP 12345678
+  wpa_er_config 87654321-9abc-def0-1234-56789abc0002 12345670 clear OPEN NONE ""
+
+<auth> must be one of the following: OPEN WPAPSK WPA2PSK
+<encr> must be one of the following: NONE WEP TKIP CCMP
+
+
+wps_er_pbc <Enrollee UUID|MAC address>
+- accept an Enrollee PBC using External Registrar
+
+wps_er_pin <Enrollee UUID|"any"|MAC address> <PIN> [Enrollee MAC address]
+- add an Enrollee PIN to External Registrar
+- if Enrollee UUID is not known, "any" can be used to add a wildcard PIN
+- if the MAC address of the enrollee is known, it should be configured
+  to allow the AP to advertise list of authorized enrollees
+
+
+WPS ER events:
+
+WPS_EVENT_ER_AP_ADD
+- WPS ER discovered an AP
+
+WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1 |Very friendly name|Company|Long description of the model|WAP|http://w1.fi/|http://w1.fi/hostapd/
+
+WPS_EVENT_ER_AP_REMOVE
+- WPS ER removed an AP entry
+
+WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002
+
+WPS_EVENT_ER_ENROLLEE_ADD
+- WPS ER discovered a new Enrollee
+
+WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0 pri_dev_type=1-0050F204-1 |Wireless Client|Company|cmodel|123|12345|
+
+WPS_EVENT_ER_ENROLLEE_REMOVE
+- WPS ER removed an Enrollee entry
+
+WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333 02:66:a0:ee:17:27
+
+WPS-ER-AP-SETTINGS
+- WPS ER learned AP settings
+
+WPS-ER-AP-SETTINGS uuid=fd91b4ec-e3fa-5891-a57d-8c59efeed1d2 ssid=test-wps auth_type=0x0020 encr_type=0x0008 key=12345678
+
+
+WPS with NFC
+------------
+
+WPS can be used with NFC-based configuration method. An NFC tag
+containing a password token from the Enrollee can be used to
+authenticate the connection instead of the PIN. In addition, an NFC tag
+with a configuration token can be used to transfer AP settings without
+going through the WPS protocol.
+
+When the station acts as an Enrollee, a local NFC tag with a password
+token can be used by touching the NFC interface of a Registrar.
+
+"wps_nfc [BSSID]" command starts WPS protocol run with the local end as
+the Enrollee using the NFC password token that is either pre-configured
+in the configuration file (wps_nfc_dev_pw_id, wps_nfc_dh_pubkey,
+wps_nfc_dh_privkey, wps_nfc_dev_pw) or generated dynamically with
+"wps_nfc_token <WPS|NDEF>" command. The included nfc_pw_token tool
+(build with "make nfc_pw_token") can be used to generate NFC password
+tokens during manufacturing (each station needs to have its own random
+keys).
+
+The "wps_nfc_config_token <WPS/NDEF>" command can be used to build an
+NFC configuration token when wpa_supplicant is controlling an AP
+interface (AP or P2P GO). The output value from this command is a
+hexdump of the current AP configuration (WPS parameter requests this to
+include only the WPS attributes; NDEF parameter requests additional NDEF
+encapsulation to be included). This data needs to be written to an NFC
+tag with an external program. Once written, the NFC configuration token
+can be used to touch an NFC interface on a station to provision the
+credentials needed to access the network.
+
+The "wps_nfc_config_token <WPS/NDEF> <network id>" command can be used
+to build an NFC configuration token based on a locally configured
+network.
+
+If the station includes NFC interface and reads an NFC tag with a MIME
+media type "application/vnd.wfa.wsc", the NDEF message payload (with or
+without NDEF encapsulation) can be delivered to wpa_supplicant using the
+following wpa_cli command:
+
+wps_nfc_tag_read <hexdump of payload>
+
+If the NFC tag contains a configuration token, the network is added to
+wpa_supplicant configuration. If the NFC tag contains a password token,
+the token is added to the WPS Registrar component. This information can
+then be used with wps_reg command (when the NFC password token was from
+an AP) using a special value "nfc-pw" in place of the PIN parameter. If
+the ER functionality has been started (wps_er_start), the NFC password
+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-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
+when this does not depend on the contents of the Handover Request
+Message. 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). If the options
+UUID|BSSID argument is included, this is a request to build the handover
+message for the specified AP when wpa_supplicant is operating as a WPS
+ER.
+
+"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
+indicates whether the local device initiated or responded to the
+connection handover and the carrier records are the selected carrier
+from the handover request and select messages as a hexdump.
+
+The "wps_er_nfc_config_token <WPS/NDEF> <UUID|BSSID>" command can be
+used to build an NFC configuration token for the specified AP when
+wpa_supplicant is operating as a WPS ER. The output value from this
+command is a hexdump of the selected AP configuration (WPS parameter
+requests this to include only the WPS attributes; NDEF parameter
+requests additional NDEF encapsulation to be included). This data needs
+to be written to an NFC tag with an external program. Once written, the
+NFC configuration token can be used to touch an NFC interface on a
+station to provision the credentials needed to access the network.
diff --git a/hostap/wpa_supplicant/README-Windows.txt b/hostap/wpa_supplicant/README-Windows.txt
new file mode 100644
index 0000000..7288abd
--- /dev/null
+++ b/hostap/wpa_supplicant/README-Windows.txt
@@ -0,0 +1,299 @@
+wpa_supplicant for Windows
+==========================
+
+Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> and contributors
+All Rights Reserved.
+
+This program is licensed under the BSD license (the one with
+advertisement clause removed).
+
+
+wpa_supplicant has support for being used as a WPA/WPA2/IEEE 802.1X
+Supplicant on Windows. The current port requires that WinPcap
+(http://winpcap.polito.it/) is installed for accessing packets and the
+driver interface. Both release versions 3.0 and 3.1 are supported.
+
+The current port is still somewhat experimental. It has been tested
+mainly on Windows XP (SP2) with limited set of NDIS drivers. In
+addition, the current version has been reported to work with Windows
+2000.
+
+All security modes have been verified to work (at least complete
+authentication and successfully ping a wired host):
+- plaintext
+- static WEP / open system authentication
+- static WEP / shared key authentication
+- IEEE 802.1X with dynamic WEP keys
+- WPA-PSK, TKIP, CCMP, TKIP+CCMP
+- WPA-EAP, TKIP, CCMP, TKIP+CCMP
+- WPA2-PSK, TKIP, CCMP, TKIP+CCMP
+- WPA2-EAP, TKIP, CCMP, TKIP+CCMP
+
+
+Building wpa_supplicant with mingw
+----------------------------------
+
+The default build setup for wpa_supplicant is to use MinGW and
+cross-compiling from Linux to MinGW/Windows. It should also be
+possible to build this under Windows using the MinGW tools, but that
+is not tested nor supported and is likely to require some changes to
+the Makefile unless cygwin is used.
+
+
+Building wpa_supplicant with MSVC
+---------------------------------
+
+wpa_supplicant can be built with Microsoft Visual C++ compiler. This
+has been tested with Microsoft Visual C++ Toolkit 2003 and Visual
+Studio 2005 using the included nmake.mak as a Makefile for nmake. IDE
+can also be used by creating a project that includes the files and
+defines mentioned in nmake.mak. Example VS2005 solution and project
+files are included in vs2005 subdirectory. This can be used as a
+starting point for building the programs with VS2005 IDE. Visual Studio
+2008 Express Edition is also able to use these project files.
+
+WinPcap development package is needed for the build and this can be
+downloaded from http://www.winpcap.org/install/bin/WpdPack_4_0_2.zip. The
+default nmake.mak expects this to be unpacked into C:\dev\WpdPack so
+that Include and Lib directories are in this directory. The files can be
+stored elsewhere as long as the WINPCAPDIR in nmake.mak is updated to
+match with the selected directory. In case a project file in the IDE is
+used, these Include and Lib directories need to be added to project
+properties as additional include/library directories.
+
+OpenSSL source package can be downloaded from
+http://www.openssl.org/source/openssl-0.9.8i.tar.gz and built and
+installed following instructions in INSTALL.W32. Note that if EAP-FAST
+support will be included in the wpa_supplicant, OpenSSL needs to be
+patched to# support it openssl-0.9.8i-tls-extensions.patch. The example
+nmake.mak file expects OpenSSL to be installed into C:\dev\openssl, but
+this directory can be modified by changing OPENSSLDIR variable in
+nmake.mak.
+
+If you do not need EAP-FAST support, you may also be able to use Win32
+binary installation package of OpenSSL from
+http://www.slproweb.com/products/Win32OpenSSL.html instead of building
+the library yourself. In this case, you will need to copy Include and
+Lib directories in suitable directory, e.g., C:\dev\openssl for the
+default nmake.mak. Copy {Win32OpenSSLRoot}\include into
+C:\dev\openssl\include and make C:\dev\openssl\lib subdirectory with
+files from {Win32OpenSSLRoot}\VC (i.e., libeay*.lib and ssleay*.lib).
+This will end up using dynamically linked OpenSSL (i.e., .dll files are
+needed) for it. Alternative, you can copy files from
+{Win32OpenSSLRoot}\VC\static to create a static build (no OpenSSL .dll
+files needed).
+
+
+Building wpa_supplicant for cygwin
+----------------------------------
+
+wpa_supplicant can be built for cygwin by installing the needed
+development packages for cygwin. This includes things like compiler,
+make, openssl development package, etc. In addition, developer's pack
+for WinPcap (WPdpack.zip) from
+http://winpcap.polito.it/install/default.htm is needed.
+
+.config file should enable only one driver interface,
+CONFIG_DRIVER_NDIS. In addition, include directories may need to be
+added to match the system. An example configuration is available in
+defconfig. The library and include files for WinPcap will either need
+to be installed in compiler/linker default directories or their
+location will need to be adding to .config when building
+wpa_supplicant.
+
+Othen than this, the build should be more or less identical to Linux
+version, i.e., just run make after having created .config file. An
+additional tool, win_if_list.exe, can be built by running "make
+win_if_list".
+
+
+Building wpa_gui
+----------------
+
+wpa_gui uses Qt application framework from Trolltech. It can be built
+with the open source version of Qt4 and MinGW. Following commands can
+be used to build the binary in the Qt 4 Command Prompt:
+
+# go to the root directory of wpa_supplicant source code
+cd wpa_gui-qt4
+qmake -o Makefile wpa_gui.pro
+make
+# the wpa_gui.exe binary is created into 'release' subdirectory
+
+
+Using wpa_supplicant for Windows
+--------------------------------
+
+wpa_supplicant, wpa_cli, and wpa_gui behave more or less identically to
+Linux version, so instructions in README and example wpa_supplicant.conf
+should be applicable for most parts. In addition, there is another
+version of wpa_supplicant, wpasvc.exe, which can be used as a Windows
+service and which reads its configuration from registry instead of
+text file.
+
+When using access points in "hidden SSID" mode, ap_scan=2 mode need to
+be used (see wpa_supplicant.conf for more information).
+
+Windows NDIS/WinPcap uses quite long interface names, so some care
+will be needed when starting wpa_supplicant. Alternatively, the
+adapter description can be used as the interface name which may be
+easier since it is usually in more human-readable
+format. win_if_list.exe can be used to find out the proper interface
+name.
+
+Example steps in starting up wpa_supplicant:
+
+# win_if_list.exe
+ifname: \Device\NPF_GenericNdisWanAdapter
+description: Generic NdisWan adapter
+
+ifname: \Device\NPF_{769E012B-FD17-4935-A5E3-8090C38E25D2}
+description: Atheros Wireless Network Adapter (Microsoft's Packet Scheduler)
+
+ifname: \Device\NPF_{732546E7-E26C-48E3-9871-7537B020A211}
+description: Intel 8255x-based Integrated Fast Ethernet (Microsoft's Packet Scheduler)
+
+
+Since the example configuration used Atheros WLAN card, the middle one
+is the correct interface in this case. The interface name for -i
+command line option is the full string following "ifname:" (the
+"\Device\NPF_" prefix can be removed). In other words, wpa_supplicant
+would be started with the following command:
+
+# wpa_supplicant.exe -i'{769E012B-FD17-4935-A5E3-8090C38E25D2}' -c wpa_supplicant.conf -d
+
+-d optional enables some more debugging (use -dd for even more, if
+needed). It can be left out if debugging information is not needed.
+
+With the alternative mechanism for selecting the interface, this
+command has identical results in this case:
+
+# wpa_supplicant.exe -iAtheros -c wpa_supplicant.conf -d
+
+
+Simple configuration example for WPA-PSK:
+
+#ap_scan=2
+ctrl_interface=
+network={
+	ssid="test"
+	key_mgmt=WPA-PSK
+	proto=WPA
+	pairwise=TKIP
+	psk="secret passphrase"
+}
+
+(remove '#' from the comment out ap_scan line to enable mode in which
+wpa_supplicant tries to associate with the SSID without doing
+scanning; this allows APs with hidden SSIDs to be used)
+
+
+wpa_cli.exe and wpa_gui.exe can be used to interact with the
+wpa_supplicant.exe program in the same way as with Linux. Note that
+ctrl_interface is using UNIX domain sockets when built for cygwin, but
+the native build for Windows uses named pipes and the contents of the
+ctrl_interface configuration item is used to control access to the
+interface. Anyway, this variable has to be included in the configuration
+to enable the control interface.
+
+
+Example SDDL string formats:
+
+(local admins group has permission, but nobody else):
+
+ctrl_interface=SDDL=D:(A;;GA;;;BA)
+
+("A" == "access allowed", "GA" == GENERIC_ALL == all permissions, and
+"BA" == "builtin administrators" == the local admins.  The empty fields
+are for flags and object GUIDs, none of which should be required in this
+case.)
+
+(local admins and the local "power users" group have permissions,
+but nobody else):
+
+ctrl_interface=SDDL=D:(A;;GA;;;BA)(A;;GA;;;PU)
+
+(One ACCESS_ALLOWED ACE for GENERIC_ALL for builtin administrators, and
+one ACCESS_ALLOWED ACE for GENERIC_ALL for power users.)
+
+(close to wide open, but you have to be a valid user on
+the machine):
+
+ctrl_interface=SDDL=D:(A;;GA;;;AU)
+
+(One ACCESS_ALLOWED ACE for GENERIC_ALL for the "authenticated users"
+group.)
+
+This one would allow absolutely everyone (including anonymous
+users) -- this is *not* recommended, since named pipes can be attached
+to from anywhere on the network (i.e. there's no "this machine only"
+like there is with 127.0.0.1 sockets):
+
+ctrl_interface=SDDL=D:(A;;GA;;;BU)(A;;GA;;;AN)
+
+(BU == "builtin users", "AN" == "anonymous")
+
+See also [1] for the format of ACEs, and [2] for the possible strings
+that can be used for principal names.
+
+[1]
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/ace_strings.asp
+[2]
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/sid_strings.asp
+
+
+Starting wpa_supplicant as a Windows service (wpasvc.exe)
+---------------------------------------------------------
+
+wpa_supplicant can be started as a Windows service by using wpasvc.exe
+program that is alternative build of wpa_supplicant.exe. Most of the
+core functionality of wpasvc.exe is identical to wpa_supplicant.exe,
+but it is using Windows registry for configuration information instead
+of a text file and command line parameters. In addition, it can be
+registered as a service that can be started automatically or manually
+like any other Windows service.
+
+The root of wpa_supplicant configuration in registry is
+HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant. This level includes global
+parameters and a 'interfaces' subkey with all the interface configuration
+(adapter to confname mapping). Each such mapping is a subkey that has
+'adapter', 'config', and 'ctrl_interface' values.
+
+This program can be run either as a normal command line application,
+e.g., for debugging, with 'wpasvc.exe app' or as a Windows service.
+Service need to be registered with 'wpasvc.exe reg <full path to
+wpasvc.exe>'. Alternatively, 'wpasvc.exe reg' can be used to register
+the service with the current location of wpasvc.exe. After this, wpasvc
+can be started like any other Windows service (e.g., 'net start wpasvc')
+or it can be configured to start automatically through the Services tool
+in administrative tasks. The service can be unregistered with
+'wpasvc.exe unreg'.
+
+If the service is set to start during system bootup to make the
+network connection available before any user has logged in, there may
+be a long (half a minute or so) delay in starting up wpa_supplicant
+due to WinPcap needing a driver called "Network Monitor Driver" which
+is started by default on demand.
+
+To speed up wpa_supplicant start during system bootup, "Network
+Monitor Driver" can be configured to be started sooner by setting its
+startup type to System instead of the default Demand. To do this, open
+up Device Manager, select Show Hidden Devices, expand the "Non
+Plug-and-Play devices" branch, double click "Network Monitor Driver",
+go to the Driver tab, and change the Demand setting to System instead.
+
+Configuration data is in HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs
+key. Each configuration profile has its own key under this. In terms of text
+files, each profile would map to a separate text file with possibly multiple
+networks. Under each profile, there is a networks key that lists all
+networks as a subkey. Each network has set of values in the same way as
+network block in the configuration file. In addition, blobs subkey has
+possible blobs as values.
+
+HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000
+   ssid="example"
+   key_mgmt=WPA-PSK
+
+See win_example.reg for an example on how to setup wpasvc.exe
+parameters in registry. It can also be imported to registry as a
+starting point for the configuration.
diff --git a/hostap/wpa_supplicant/android.config b/hostap/wpa_supplicant/android.config
new file mode 100644
index 0000000..6459ae2
--- /dev/null
+++ b/hostap/wpa_supplicant/android.config
@@ -0,0 +1,482 @@
+# Example wpa_supplicant build time configuration
+#
+# This file lists the configuration options that are used when building the
+# hostapd binary. All lines starting with # are ignored. Configuration option
+# lines must be commented out complete, if they are not to be included, i.e.,
+# just setting VARIABLE=n is not disabling that variable.
+#
+# This file is included in Makefile, so variables like CFLAGS and LIBS can also
+# be modified from here. In most cases, these lines should use += in order not
+# to override previous values of the variables.
+
+
+# Uncomment following two lines and fix the paths if you have installed OpenSSL
+# or GnuTLS in non-default location
+#CFLAGS += -I/usr/local/openssl/include
+#LIBS += -L/usr/local/openssl/lib
+
+# Some Red Hat versions seem to include kerberos header files from OpenSSL, but
+# the kerberos files are not in the default include path. Following line can be
+# used to fix build issues on such systems (krb5.h not found).
+#CFLAGS += -I/usr/include/kerberos
+
+# 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
+# replacement for WEXT and its use allows wpa_supplicant to properly control
+# the driver to improve existing functionality like roaming and to support new
+# functionality.
+#CONFIG_DRIVER_WEXT=y
+
+# Driver interface for Linux drivers using the nl80211 kernel interface
+#CONFIG_DRIVER_NL80211=y
+CONFIG_LIBNL20=y
+
+# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
+#CONFIG_DRIVER_BSD=y
+#CFLAGS += -I/usr/local/include
+#LIBS += -L/usr/local/lib
+#LIBS_p += -L/usr/local/lib
+#LIBS_c += -L/usr/local/lib
+
+# Driver interface for Windows NDIS
+#CONFIG_DRIVER_NDIS=y
+#CFLAGS += -I/usr/include/w32api/ddk
+#LIBS += -L/usr/local/lib
+# For native build using mingw
+#CONFIG_NATIVE_WINDOWS=y
+# Additional directories for cross-compilation on Linux host for mingw target
+#CFLAGS += -I/opt/mingw/mingw32/include/ddk
+#LIBS += -L/opt/mingw/mingw32/lib
+#CC=mingw32-gcc
+# By default, driver_ndis uses WinPcap for low-level operations. This can be
+# replaced with the following option which replaces WinPcap calls with NDISUIO.
+# However, this requires that WZC is disabled (net stop wzcsvc) before starting
+# wpa_supplicant.
+# CONFIG_USE_NDISUIO=y
+
+# Driver interface for wired Ethernet drivers
+#CONFIG_DRIVER_WIRED=y
+
+# Driver interface for the Broadcom RoboSwitch family
+#CONFIG_DRIVER_ROBOSWITCH=y
+
+# Driver interface for no driver (e.g., WPS ER only)
+#CONFIG_DRIVER_NONE=y
+
+# Solaris libraries
+#LIBS += -lsocket -ldlpi -lnsl
+#LIBS_c += -lsocket
+
+# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is
+# included)
+CONFIG_IEEE8021X_EAPOL=y
+
+# EAP-MD5
+CONFIG_EAP_MD5=
+
+# EAP-MSCHAPv2
+CONFIG_EAP_MSCHAPV2=
+
+# EAP-TLS
+CONFIG_EAP_TLS=
+
+# EAL-PEAP
+CONFIG_EAP_PEAP=
+
+# EAP-TTLS
+CONFIG_EAP_TTLS=
+
+# 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.
+CONFIG_EAP_FAST=
+
+# EAP-GTC
+CONFIG_EAP_GTC=
+
+# EAP-OTP
+CONFIG_EAP_OTP=
+
+# EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used)
+CONFIG_EAP_SIM=
+
+# EAP-PSK (experimental; this is _not_ needed for WPA-PSK)
+CONFIG_EAP_PSK=
+
+# EAP-pwd (secure authentication using only a password)
+CONFIG_EAP_PWD=
+
+# EAP-PAX
+CONFIG_EAP_PAX=
+
+# LEAP
+CONFIG_EAP_LEAP=
+
+# EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used)
+CONFIG_EAP_AKA=
+
+# EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used).
+# This requires CONFIG_EAP_AKA to be enabled, too.
+CONFIG_EAP_AKA_PRIME=
+
+# Enable USIM simulator (Milenage) for EAP-AKA
+#CONFIG_USIM_SIMULATOR=y
+
+# EAP-SAKE
+CONFIG_EAP_SAKE=
+
+# EAP-GPSK
+CONFIG_EAP_GPSK=
+# Include support for optional SHA256 cipher suite in EAP-GPSK
+CONFIG_EAP_GPSK_SHA256=
+
+# EAP-TNC and related Trusted Network Connect support (experimental)
+CONFIG_EAP_TNC=
+
+# Wi-Fi Protected Setup (WPS)
+CONFIG_WPS=
+# Enable WPS external registrar functionality
+#CONFIG_WPS_ER=y
+# Disable credentials for an open network by default when acting as a WPS
+# registrar.
+#CONFIG_WPS_REG_DISABLE_OPEN=y
+# Enable WPS support with NFC config method
+#CONFIG_WPS_NFC=y
+
+# EAP-IKEv2
+CONFIG_EAP_IKEV2=
+
+# PKCS#12 (PFX) support (used to read private key and certificate file from
+# a file that usually has extension .p12 or .pfx)
+#CONFIG_PKCS12=y
+
+# Smartcard support (i.e., private key on a smartcard), e.g., with openssl
+# engine.
+CONFIG_SMARTCARD=
+
+# PC/SC interface for smartcards (USIM, GSM SIM)
+# Enable this if EAP-SIM or EAP-AKA is included
+#CONFIG_PCSC=y
+
+# Support HT overrides (disable HT/HT40, mask MCS rates, etc.)
+#CONFIG_HT_OVERRIDES=y
+
+# Support VHT overrides (disable VHT, mask MCS rates, etc.)
+#CONFIG_VHT_OVERRIDES=y
+
+# Development testing
+#CONFIG_EAPOL_TEST=y
+
+# 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)
+# named_pipe = Windows Named Pipe (default for Windows)
+# udp-remote = UDP sockets with remote access (only for tests systems/purpose)
+# y = use default (backwards compatibility)
+# If this option is commented out, control interface is not included in the
+# build.
+CONFIG_CTRL_IFACE=y
+
+# Include support for GNU Readline and History Libraries in wpa_cli.
+# When building a wpa_cli binary for distribution, please note that these
+# libraries are licensed under GPL and as such, BSD license may not apply for
+# the resulting binary.
+#CONFIG_READLINE=y
+
+# Include internal line edit mode in wpa_cli. This can be used as a replacement
+# for GNU Readline to provide limited command line editing and history support.
+CONFIG_WPA_CLI_EDIT=y
+
+# Remove debugging code that is printing out debug message to stdout.
+# This can be used to reduce the size of the wpa_supplicant considerably
+# if debugging code is not needed. The size reduction can be around 35%
+# (e.g., 90 kB).
+#CONFIG_NO_STDOUT_DEBUG=y
+
+# Remove WPA support, e.g., for wired-only IEEE 802.1X supplicant, to save
+# 35-50 kB in code size.
+#CONFIG_NO_WPA=y
+
+# Remove IEEE 802.11i/WPA-Personal ASCII passphrase support
+# This option can be used to reduce code size by removing support for
+# converting ASCII passphrases into PSK. If this functionality is removed, the
+# PSK can only be configured as the 64-octet hexstring (e.g., from
+# wpa_passphrase). This saves about 0.5 kB in code size.
+#CONFIG_NO_WPA_PASSPHRASE=y
+
+# Disable scan result processing (ap_mode=1) to save code size by about 1 kB.
+# This can be used if ap_scan=1 mode is never enabled.
+#CONFIG_NO_SCAN_PROCESSING=y
+
+# Select configuration backend:
+# file = text file (e.g., wpa_supplicant.conf; note: the configuration file
+#	path is given on command line, not here; this option is just used to
+#	select the backend that allows configuration files to be used)
+# winreg = Windows registry (see win_example.reg for an example)
+CONFIG_BACKEND=file
+
+# Remove configuration write functionality (i.e., to allow the configuration
+# file to be updated based on runtime configuration changes). The runtime
+# configuration can still be changed, the changes are just not going to be
+# persistent over restarts. This option can be used to reduce code size by
+# about 3.5 kB.
+#CONFIG_NO_CONFIG_WRITE=y
+
+# Remove support for configuration blobs to reduce code size by about 1.5 kB.
+#CONFIG_NO_CONFIG_BLOBS=y
+
+# Select program entry point implementation:
+# main = UNIX/POSIX like main() function (default)
+# main_winsvc = Windows service (read parameters from registry)
+# main_none = Very basic example (development use only)
+#CONFIG_MAIN=main
+
+# Select wrapper for operating system and C library specific functions
+# unix = UNIX/POSIX like systems (default)
+# win32 = Windows systems
+# none = Empty template
+CONFIG_OS=unix
+
+# Select event loop implementation
+# eloop = select() loop (default)
+# eloop_win = Windows events and WaitForMultipleObject() loop
+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
+# freebsd = FreeBSD libpcap
+# winpcap = WinPcap with receive thread
+# ndis = Windows NDISUIO (note: requires CONFIG_USE_NDISUIO=y)
+# none = Empty template
+CONFIG_L2_PACKET=linux
+
+# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
+CONFIG_PEERKEY=
+
+# IEEE 802.11w (management frame protection), also known as PMF
+# Driver support is also needed for IEEE 802.11w.
+CONFIG_IEEE80211W=
+
+# Select TLS implementation
+# openssl = OpenSSL (default)
+# gnutls = GnuTLS
+# internal = Internal TLSv1 implementation (experimental)
+# none = Empty template
+#CONFIG_TLS=openssl
+
+# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1)
+# can be enabled to get a stronger construction of messages when block ciphers
+# are used. It should be noted that some existing TLS v1.0 -based
+# implementation may not be compatible with TLS v1.1 message (ClientHello is
+# sent prior to negotiating which version will be used)
+#CONFIG_TLSV11=y
+
+# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2)
+# can be enabled to enable use of stronger crypto algorithms. It should be
+# noted that some existing TLS v1.0 -based implementation may not be compatible
+# with TLS v1.2 message (ClientHello is sent prior to negotiating which version
+# will be used)
+#CONFIG_TLSV12=y
+
+# If CONFIG_TLS=internal is used, additional library and include paths are
+# needed for LibTomMath. Alternatively, an integrated, minimal version of
+# LibTomMath can be used. See beginning of libtommath.c for details on benefits
+# and drawbacks of this option.
+#CONFIG_INTERNAL_LIBTOMMATH=y
+#ifndef CONFIG_INTERNAL_LIBTOMMATH
+#LTM_PATH=/usr/src/libtommath-0.39
+#CFLAGS += -I$(LTM_PATH)
+#LIBS += -L$(LTM_PATH)
+#LIBS_p += -L$(LTM_PATH)
+#endif
+# At the cost of about 4 kB of additional binary size, the internal LibTomMath
+# can be configured to include faster routines for exptmod, sqr, and div to
+# speed up DH and RSA calculation considerably
+#CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+
+# Include NDIS event processing through WMI into wpa_supplicant/wpasvc.
+# This is only for Windows builds and requires WMI-related header files and
+# WbemUuid.Lib from Platform SDK even when building with MinGW.
+#CONFIG_NDIS_EVENTS_INTEGRATED=y
+#PLATFORMSDKLIB="/opt/Program Files/Microsoft Platform SDK/Lib"
+
+# Add support for old DBus control interface
+# (fi.epitest.hostap.WPASupplicant)
+#CONFIG_CTRL_IFACE_DBUS=y
+
+# Add support for new DBus control interface
+# (fi.w1.hostap.wpa_supplicant1)
+#CONFIG_CTRL_IFACE_DBUS_NEW=y
+
+# Add introspection support for new DBus control interface
+#CONFIG_CTRL_IFACE_DBUS_INTRO=y
+
+# Add support for loading EAP methods dynamically as shared libraries.
+# When this option is enabled, each EAP method can be either included
+# statically (CONFIG_EAP_<method>=y) or dynamically (CONFIG_EAP_<method>=dyn).
+# Dynamic EAP methods are build as shared objects (eap_*.so) and they need to
+# be loaded in the beginning of the wpa_supplicant configuration file
+# (see load_dynamic_eap parameter in the example file) before being used in
+# the network blocks.
+#
+# Note that some shared parts of EAP methods are included in the main program
+# and in order to be able to use dynamic EAP methods using these parts, the
+# main program must have been build with the EAP method enabled (=y or =dyn).
+# This means that EAP-TLS/PEAP/TTLS/FAST cannot be added as dynamic libraries
+# unless at least one of them was included in the main build to force inclusion
+# of the shared code. Similarly, at least one of EAP-SIM/AKA must be included
+# in the main build to be able to load these methods dynamically.
+#
+# Please also note that using dynamic libraries will increase the total binary
+# size. Thus, it may not be the best option for targets that have limited
+# amount of memory/flash.
+#CONFIG_DYNAMIC_EAP_METHODS=y
+
+# IEEE Std 802.11r-2008 (Fast BSS Transition)
+CONFIG_IEEE80211R=
+
+# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt)
+#CONFIG_DEBUG_FILE=y
+
+# Send debug messages to syslog instead of stdout
+#CONFIG_DEBUG_SYSLOG=y
+# Set syslog facility for debug messages
+#CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON
+
+# 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
+
+# Add support for writing debug log to Android logcat instead of standard
+# output
+CONFIG_ANDROID_LOG=y
+
+# Enable privilege separation (see README 'Privilege separation' for details)
+#CONFIG_PRIVSEP=y
+
+# Enable mitigation against certain attacks against TKIP by delaying Michael
+# MIC error reports by a random amount of time between 0 and 60 seconds
+#CONFIG_DELAYED_MIC_ERROR_REPORT=y
+
+# Enable tracing code for developer debugging
+# This tracks use of memory allocations and other registrations and reports
+# incorrect use with a backtrace of call (or allocation) location.
+#CONFIG_WPA_TRACE=y
+# For BSD, uncomment these.
+#LIBS += -lexecinfo
+#LIBS_p += -lexecinfo
+#LIBS_c += -lexecinfo
+
+# Use libbfd to get more details for developer debugging
+# This enables use of libbfd to get more detailed symbols for the backtraces
+# generated by CONFIG_WPA_TRACE=y.
+#CONFIG_WPA_TRACE_BFD=y
+# For BSD, uncomment these.
+#LIBS += -lbfd -liberty -lz
+#LIBS_p += -lbfd -liberty -lz
+#LIBS_c += -lbfd -liberty -lz
+
+# wpa_supplicant depends on strong random number generation being available
+# from the operating system. os_get_random() function is used to fetch random
+# data when needed, e.g., for key generation. On Linux and BSD systems, this
+# works by reading /dev/urandom. It should be noted that the OS entropy pool
+# needs to be properly initialized before wpa_supplicant is started. This is
+# important especially on embedded devices that do not have a hardware random
+# number generator and may by default start up with minimal entropy available
+# for random number generation.
+#
+# As a safety net, wpa_supplicant is by default trying to internally collect
+# additional entropy for generating random data to mix in with the data fetched
+# from the OS. This by itself is not considered to be very strong, but it may
+# help in cases where the system pool is not initialized properly. However, it
+# is very strongly recommended that the system pool is initialized with enough
+# entropy either by using hardware assisted random number generator or by
+# storing state over device reboots.
+#
+# wpa_supplicant can be configured to maintain its own entropy store over
+# restarts to enhance random number generation. This is not perfect, but it is
+# much more secure than using the same sequence of random numbers after every
+# reboot. This can be enabled with -e<entropy file> command line option. The
+# specified file needs to be readable and writable by wpa_supplicant.
+#
+# If the os_get_random() is known to provide strong random data (e.g., on
+# Linux/BSD, the board in question is known to have reliable source of random
+# data from /dev/urandom), the internal wpa_supplicant random pool can be
+# disabled. This will save some in binary size and CPU use. However, this
+# should only be considered for builds that are known to be used on devices
+# that meet the requirements described above.
+#CONFIG_NO_RANDOM_POOL=y
+
+# IEEE 802.11n (High Throughput) support (mainly for AP mode)
+CONFIG_IEEE80211N=
+
+# Wireless Network Management (IEEE Std 802.11v-2011)
+# Note: This is experimental and not complete implementation.
+CONFIG_WNM=
+
+# 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=
+
+# Hotspot 2.0
+CONFIG_HS20=
+
+# Disable roaming in wpa_supplicant
+CONFIG_NO_ROAMING=y
+
+# AP mode operations with wpa_supplicant
+# This can be used for controlling AP mode operations with wpa_supplicant. It
+# should be noted that this is mainly aimed at simple cases like
+# WPA2-Personal while more complex configurations like WPA2-Enterprise with an
+# external RADIUS server can be supported with hostapd.
+CONFIG_AP=
+
+# P2P (Wi-Fi Direct)
+# This can be used to enable P2P support in wpa_supplicant. See README-P2P for
+# more information on P2P operations.
+CONFIG_P2P=
+
+# 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=
+
+# Autoscan
+# This can be used to enable automatic scan support in wpa_supplicant.
+# See wpa_supplicant.conf for more information on autoscan usage.
+#
+# Enabling directly a module will enable autoscan support.
+# For exponential module:
+#CONFIG_AUTOSCAN_EXPONENTIAL=y
+# For periodic module:
+#CONFIG_AUTOSCAN_PERIODIC=y
+
+# Password (and passphrase, etc.) backend for external storage
+# These optional mechanisms can be used to add support for storing passwords
+# and other secrets in external (to wpa_supplicant) location. This allows, for
+# example, operating system specific key storage to be used
+#
+# External password backend for testing purposes (developer use)
+#CONFIG_EXT_PASSWORD_TEST=y
+
+# Enable Fast Session Transfer (FST)
+#CONFIG_FST=y
+
+include $(wildcard $(LOCAL_PATH)/android_config_*.inc)
diff --git a/hostap/wpa_supplicant/ap.c b/hostap/wpa_supplicant/ap.c
new file mode 100644
index 0000000..7a4f4cf
--- /dev/null
+++ b/hostap/wpa_supplicant/ap.c
@@ -0,0 +1,1381 @@
+/*
+ * WPA Supplicant - Basic AP mode support routines
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * 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 "utils/uuid.h"
+#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"
+#ifdef NEED_AP_MLME
+#include "ap/ieee802_11.h"
+#endif /* NEED_AP_MLME */
+#include "ap/beacon.h"
+#include "ap/ieee802_1x.h"
+#include "ap/wps_hostapd.h"
+#include "ap/ctrl_iface_ap.h"
+#include "ap/dfs.h"
+#include "wps/wps.h"
+#include "common/ieee802_11_defs.h"
+#include "config_ssid.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "p2p_supplicant.h"
+#include "ap.h"
+#include "ap/sta_info.h"
+#include "notify.h"
+
+
+#ifdef CONFIG_WPS
+static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
+#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 */
+
+
+void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
+			       struct wpa_ssid *ssid,
+			       struct hostapd_config *conf)
+{
+	/* TODO: enable HT40 if driver supports it;
+	 * drop to 11b if driver does not support 11g */
+
+#ifdef CONFIG_IEEE80211N
+	/*
+	 * Enable HT20 if the driver supports it, by setting conf->ieee80211n
+	 * and a mask of allowed capabilities within conf->ht_capab.
+	 * Using default config settings for: conf->ht_op_mode_fixed,
+	 * conf->secondary_channel, conf->require_ht
+	 */
+	if (wpa_s->hw.modes) {
+		struct hostapd_hw_modes *mode = NULL;
+		int i, no_ht = 0;
+		for (i = 0; i < wpa_s->hw.num_modes; i++) {
+			if (wpa_s->hw.modes[i].mode == conf->hw_mode) {
+				mode = &wpa_s->hw.modes[i];
+				break;
+			}
+		}
+
+#ifdef CONFIG_HT_OVERRIDES
+		if (ssid->disable_ht) {
+			conf->ieee80211n = 0;
+			conf->ht_capab = 0;
+			no_ht = 1;
+		}
+#endif /* CONFIG_HT_OVERRIDES */
+
+		if (!no_ht && mode && mode->ht_capab) {
+			conf->ieee80211n = 1;
+#ifdef CONFIG_P2P
+			if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A &&
+			    (mode->ht_capab &
+			     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+			    ssid->ht40)
+				conf->secondary_channel =
+					wpas_p2p_get_ht40_mode(wpa_s, mode,
+							       conf->channel);
+			if (conf->secondary_channel)
+				conf->ht_capab |=
+					HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+#endif /* CONFIG_P2P */
+
+			/*
+			 * white-list capabilities that won't cause issues
+			 * to connecting stations, while leaving the current
+			 * capabilities intact (currently disabled SMPS).
+			 */
+			conf->ht_capab |= mode->ht_capab &
+				(HT_CAP_INFO_GREEN_FIELD |
+				 HT_CAP_INFO_SHORT_GI20MHZ |
+				 HT_CAP_INFO_SHORT_GI40MHZ |
+				 HT_CAP_INFO_RX_STBC_MASK |
+				 HT_CAP_INFO_TX_STBC |
+				 HT_CAP_INFO_MAX_AMSDU_SIZE);
+
+			if (mode->vht_capab && ssid->vht) {
+				conf->ieee80211ac = 1;
+				wpas_conf_ap_vht(wpa_s, conf, mode);
+			}
+		}
+	}
+
+	if (conf->secondary_channel) {
+		struct wpa_supplicant *iface;
+
+		for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
+		{
+			if (iface == wpa_s ||
+			    iface->wpa_state < WPA_AUTHENTICATING ||
+			    (int) iface->assoc_freq != ssid->frequency)
+				continue;
+
+			/*
+			 * Do not allow 40 MHz co-ex PRI/SEC switch to force us
+			 * to change our PRI channel since we have an existing,
+			 * concurrent connection on that channel and doing
+			 * multi-channel concurrency is likely to cause more
+			 * harm than using different PRI/SEC selection in
+			 * environment with multiple BSSes on these two channels
+			 * with mixed 20 MHz or PRI channel selection.
+			 */
+			conf->no_pri_sec_switch = 1;
+		}
+	}
+#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];
+
+	conf->driver = wpa_s->driver;
+
+	os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface));
+
+	conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
+					       &conf->channel);
+	if (conf->hw_mode == NUM_HOSTAPD_MODES) {
+		wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz",
+			   ssid->frequency);
+		return -1;
+	}
+
+	wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf);
+
+	if (ieee80211_is_dfs(ssid->frequency) && wpa_s->conf->country[0]) {
+		conf->ieee80211h = 1;
+		conf->ieee80211d = 1;
+		conf->country[0] = wpa_s->conf->country[0];
+		conf->country[1] = wpa_s->conf->country[1];
+	}
+
+#ifdef CONFIG_P2P
+	if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G &&
+	    (ssid->mode == WPAS_MODE_P2P_GO ||
+	     ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)) {
+		/* Remove 802.11b rates from supported and basic rate sets */
+		int *list = os_malloc(4 * sizeof(int));
+		if (list) {
+			list[0] = 60;
+			list[1] = 120;
+			list[2] = 240;
+			list[3] = -1;
+		}
+		conf->basic_rates = list;
+
+		list = os_malloc(9 * sizeof(int));
+		if (list) {
+			list[0] = 60;
+			list[1] = 90;
+			list[2] = 120;
+			list[3] = 180;
+			list[4] = 240;
+			list[5] = 360;
+			list[6] = 480;
+			list[7] = 540;
+			list[8] = -1;
+		}
+		conf->supported_rates = list;
+	}
+
+	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) {
+		wpa_printf(MSG_ERROR, "No SSID configured for AP mode");
+		return -1;
+	}
+	os_memcpy(bss->ssid.ssid, ssid->ssid, ssid->ssid_len);
+	bss->ssid.ssid_len = ssid->ssid_len;
+	bss->ssid.ssid_set = 1;
+
+	bss->ignore_broadcast_ssid = ssid->ignore_broadcast_ssid;
+
+	if (ssid->auth_alg)
+		bss->auth_algs = ssid->auth_alg;
+
+	if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt))
+		bss->wpa = ssid->proto;
+	bss->wpa_key_mgmt = ssid->key_mgmt;
+	bss->wpa_pairwise = ssid->pairwise_cipher;
+	if (ssid->psk_set) {
+		bin_clear_free(bss->ssid.wpa_psk, sizeof(*bss->ssid.wpa_psk));
+		bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
+		if (bss->ssid.wpa_psk == NULL)
+			return -1;
+		os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN);
+		bss->ssid.wpa_psk->group = 1;
+	} else if (ssid->passphrase) {
+		bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase);
+	} else if (ssid->wep_key_len[0] || ssid->wep_key_len[1] ||
+		   ssid->wep_key_len[2] || ssid->wep_key_len[3]) {
+		struct hostapd_wep_keys *wep = &bss->ssid.wep;
+		int i;
+		for (i = 0; i < NUM_WEP_KEYS; i++) {
+			if (ssid->wep_key_len[i] == 0)
+				continue;
+			wep->key[i] = os_malloc(ssid->wep_key_len[i]);
+			if (wep->key[i] == NULL)
+				return -1;
+			os_memcpy(wep->key[i], ssid->wep_key[i],
+				  ssid->wep_key_len[i]);
+			wep->len[i] = ssid->wep_key_len[i];
+		}
+		wep->idx = ssid->wep_tx_keyidx;
+		wep->keys_set = 1;
+	}
+
+	if (ssid->ap_max_inactivity)
+		bss->ap_max_inactivity = ssid->ap_max_inactivity;
+
+	if (ssid->dtim_period)
+		bss->dtim_period = ssid->dtim_period;
+	else if (wpa_s->conf->dtim_period)
+		bss->dtim_period = wpa_s->conf->dtim_period;
+
+	if (ssid->beacon_int)
+		conf->beacon_int = ssid->beacon_int;
+	else if (wpa_s->conf->beacon_int)
+		conf->beacon_int = wpa_s->conf->beacon_int;
+
+#ifdef CONFIG_P2P
+	if (wpa_s->conf->p2p_go_ctwindow > conf->beacon_int) {
+		wpa_printf(MSG_INFO,
+			   "CTWindow (%d) is bigger than beacon interval (%d) - avoid configuring it",
+			   wpa_s->conf->p2p_go_ctwindow, conf->beacon_int);
+		conf->p2p_go_ctwindow = 0;
+	} else {
+		conf->p2p_go_ctwindow = wpa_s->conf->p2p_go_ctwindow;
+	}
+#endif /* CONFIG_P2P */
+
+	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 (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 {
+		bss->ssid.security_policy = SECURITY_PLAINTEXT;
+		bss->wpa_group = WPA_CIPHER_NONE;
+		bss->wpa_pairwise = WPA_CIPHER_NONE;
+		bss->rsn_pairwise = WPA_CIPHER_NONE;
+	}
+
+	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_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.
+		 */
+		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
+	 * require user interaction to actually use it. Only the internal
+	 * Registrar is supported.
+	 */
+	if (bss->ssid.security_policy != SECURITY_WPA_PSK &&
+	    bss->ssid.security_policy != SECURITY_PLAINTEXT)
+		goto no_wps;
+	if (bss->ssid.security_policy == SECURITY_WPA_PSK &&
+	    (!(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) ||
+	     !(bss->wpa & 2)))
+		goto no_wps; /* WPS2 does not allow WPA/TKIP-only
+			      * configuration */
+	bss->eap_server = 1;
+
+	if (!ssid->ignore_broadcast_ssid)
+		bss->wps_state = 2;
+
+	bss->ap_setup_locked = 2;
+	if (wpa_s->conf->config_methods)
+		bss->config_methods = os_strdup(wpa_s->conf->config_methods);
+	os_memcpy(bss->device_type, wpa_s->conf->device_type,
+		  WPS_DEV_TYPE_LEN);
+	if (wpa_s->conf->device_name) {
+		bss->device_name = os_strdup(wpa_s->conf->device_name);
+		bss->friendly_name = os_strdup(wpa_s->conf->device_name);
+	}
+	if (wpa_s->conf->manufacturer)
+		bss->manufacturer = os_strdup(wpa_s->conf->manufacturer);
+	if (wpa_s->conf->model_name)
+		bss->model_name = os_strdup(wpa_s->conf->model_name);
+	if (wpa_s->conf->model_number)
+		bss->model_number = os_strdup(wpa_s->conf->model_number);
+	if (wpa_s->conf->serial_number)
+		bss->serial_number = os_strdup(wpa_s->conf->serial_number);
+	if (is_nil_uuid(wpa_s->conf->uuid))
+		os_memcpy(bss->uuid, wpa_s->wps->uuid, WPS_UUID_LEN);
+	else
+		os_memcpy(bss->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
+	os_memcpy(bss->os_version, wpa_s->conf->os_version, 4);
+	bss->pbc_in_m1 = wpa_s->conf->pbc_in_m1;
+no_wps:
+#endif /* CONFIG_WPS */
+
+	if (wpa_s->max_stations &&
+	    wpa_s->max_stations < wpa_s->conf->max_num_sta)
+		bss->max_num_sta = wpa_s->max_stations;
+	else
+		bss->max_num_sta = wpa_s->conf->max_num_sta;
+
+	bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack;
+
+	if (wpa_s->conf->ap_vendor_elements) {
+		bss->vendor_elements =
+			wpabuf_dup(wpa_s->conf->ap_vendor_elements);
+	}
+
+	return 0;
+}
+
+
+static void ap_public_action_rx(void *ctx, const u8 *buf, size_t len, int freq)
+{
+#ifdef CONFIG_P2P
+	struct wpa_supplicant *wpa_s = ctx;
+	const struct ieee80211_mgmt *mgmt;
+
+	mgmt = (const struct ieee80211_mgmt *) buf;
+	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,
+			   buf + IEEE80211_HDRLEN + 1,
+			   len - IEEE80211_HDRLEN - 1, freq);
+#endif /* CONFIG_P2P */
+}
+
+
+static void ap_wps_event_cb(void *ctx, enum wps_event event,
+			    union wps_event_data *data)
+{
+#ifdef CONFIG_P2P
+	struct wpa_supplicant *wpa_s = ctx;
+
+	if (event == WPS_EV_FAIL) {
+		struct wps_event_fail *fail = &data->fail;
+
+		if (wpa_s->parent && wpa_s->parent != wpa_s &&
+		    wpa_s == wpa_s->global->p2p_group_formation) {
+			/*
+			 * src/ap/wps_hostapd.c has already sent this on the
+			 * main interface, so only send on the parent interface
+			 * here if needed.
+			 */
+			wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
+				"msg=%d config_error=%d",
+				fail->msg, fail->config_error);
+		}
+		wpas_p2p_wps_failed(wpa_s, fail);
+	}
+#endif /* CONFIG_P2P */
+}
+
+
+static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr,
+				 int authorized, const u8 *p2p_dev_addr)
+{
+	wpas_notify_sta_authorized(ctx, mac_addr, authorized, p2p_dev_addr);
+}
+
+
+#ifdef CONFIG_P2P
+static void ap_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr,
+			  const u8 *psk, size_t psk_len)
+{
+
+	struct wpa_supplicant *wpa_s = ctx;
+	if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL)
+		return;
+	wpas_p2p_new_psk_cb(wpa_s, mac_addr, p2p_dev_addr, psk, psk_len);
+}
+#endif /* CONFIG_P2P */
+
+
+static int ap_vendor_action_rx(void *ctx, const u8 *buf, size_t len, int freq)
+{
+#ifdef CONFIG_P2P
+	struct wpa_supplicant *wpa_s = ctx;
+	const struct ieee80211_mgmt *mgmt;
+
+	mgmt = (const struct ieee80211_mgmt *) buf;
+	if (len < IEEE80211_HDRLEN + 1)
+		return -1;
+	wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
+			   mgmt->u.action.category,
+			   buf + IEEE80211_HDRLEN + 1,
+			   len - IEEE80211_HDRLEN - 1, freq);
+#endif /* CONFIG_P2P */
+	return 0;
+}
+
+
+static int ap_probe_req_rx(void *ctx, const u8 *sa, const u8 *da,
+			   const u8 *bssid, const u8 *ie, size_t ie_len,
+			   int ssi_signal)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	unsigned int freq = 0;
+
+	if (wpa_s->ap_iface)
+		freq = wpa_s->ap_iface->freq;
+
+	return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len,
+				     freq, ssi_signal);
+}
+
+
+static void ap_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
+				  const u8 *uuid_e)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	wpas_p2p_wps_success(wpa_s, mac_addr, 1);
+}
+
+
+static void wpas_ap_configured_cb(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+
+	if (wpa_s->ap_configured_cb)
+		wpa_s->ap_configured_cb(wpa_s->ap_configured_cb_ctx,
+					wpa_s->ap_configured_cb_data);
+}
+
+
+int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
+			     struct wpa_ssid *ssid)
+{
+	struct wpa_driver_associate_params params;
+	struct hostapd_iface *hapd_iface;
+	struct hostapd_config *conf;
+	size_t i;
+
+	if (ssid->ssid == NULL || ssid->ssid_len == 0) {
+		wpa_printf(MSG_ERROR, "No SSID configured for AP mode");
+		return -1;
+	}
+
+	wpa_supplicant_ap_deinit(wpa_s);
+
+	wpa_printf(MSG_DEBUG, "Setting up AP (SSID='%s')",
+		   wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+
+	os_memset(&params, 0, sizeof(params));
+	params.ssid = ssid->ssid;
+	params.ssid_len = ssid->ssid_len;
+	switch (ssid->mode) {
+	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 */
+	params.freq.freq = ssid->frequency;
+
+	params.wpa_proto = ssid->proto;
+	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK)
+		wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
+	else
+		wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
+	params.key_mgmt_suite = wpa_s->key_mgmt;
+
+	wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher,
+							  1);
+	if (wpa_s->pairwise_cipher < 0) {
+		wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise "
+			   "cipher.");
+		return -1;
+	}
+	params.pairwise_suite = wpa_s->pairwise_cipher;
+	params.group_suite = params.pairwise_suite;
+
+#ifdef CONFIG_P2P
+	if (ssid->mode == WPAS_MODE_P2P_GO ||
+	    ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
+		params.p2p = 1;
+#endif /* CONFIG_P2P */
+
+	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;
+
+	if (ieee80211_is_dfs(params.freq.freq))
+		params.freq.freq = 0; /* set channel after CAC */
+
+	if (wpa_drv_associate(wpa_s, &params) < 0) {
+		wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality");
+		return -1;
+	}
+
+	wpa_s->ap_iface = hapd_iface = os_zalloc(sizeof(*wpa_s->ap_iface));
+	if (hapd_iface == NULL)
+		return -1;
+	hapd_iface->owner = wpa_s;
+	hapd_iface->drv_flags = wpa_s->drv_flags;
+	hapd_iface->smps_modes = wpa_s->drv_smps_modes;
+	hapd_iface->probe_resp_offloads = wpa_s->probe_resp_offloads;
+	hapd_iface->extended_capa = wpa_s->extended_capa;
+	hapd_iface->extended_capa_mask = wpa_s->extended_capa_mask;
+	hapd_iface->extended_capa_len = wpa_s->extended_capa_len;
+
+	wpa_s->ap_iface->conf = conf = hostapd_config_defaults();
+	if (conf == NULL) {
+		wpa_supplicant_ap_deinit(wpa_s);
+		return -1;
+	}
+
+	os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params,
+		  wpa_s->conf->wmm_ac_params,
+		  sizeof(wpa_s->conf->wmm_ac_params));
+
+	if (params.uapsd > 0) {
+		conf->bss[0]->wmm_enabled = 1;
+		conf->bss[0]->wmm_uapsd = 1;
+	}
+
+	if (wpa_supplicant_conf_ap(wpa_s, ssid, conf)) {
+		wpa_printf(MSG_ERROR, "Failed to create AP configuration");
+		wpa_supplicant_ap_deinit(wpa_s);
+		return -1;
+	}
+
+#ifdef CONFIG_P2P
+	if (ssid->mode == WPAS_MODE_P2P_GO)
+		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 |
+			P2P_GROUP_FORMATION;
+#endif /* CONFIG_P2P */
+
+	hapd_iface->num_bss = conf->num_bss;
+	hapd_iface->bss = os_calloc(conf->num_bss,
+				    sizeof(struct hostapd_data *));
+	if (hapd_iface->bss == NULL) {
+		wpa_supplicant_ap_deinit(wpa_s);
+		return -1;
+	}
+
+	for (i = 0; i < conf->num_bss; i++) {
+		hapd_iface->bss[i] =
+			hostapd_alloc_bss_data(hapd_iface, conf,
+					       conf->bss[i]);
+		if (hapd_iface->bss[i] == NULL) {
+			wpa_supplicant_ap_deinit(wpa_s);
+			return -1;
+		}
+
+		hapd_iface->bss[i]->msg_ctx = wpa_s;
+		hapd_iface->bss[i]->msg_ctx_parent = wpa_s->parent;
+		hapd_iface->bss[i]->public_action_cb = ap_public_action_rx;
+		hapd_iface->bss[i]->public_action_cb_ctx = wpa_s;
+		hapd_iface->bss[i]->vendor_action_cb = ap_vendor_action_rx;
+		hapd_iface->bss[i]->vendor_action_cb_ctx = wpa_s;
+		hostapd_register_probereq_cb(hapd_iface->bss[i],
+					     ap_probe_req_rx, wpa_s);
+		hapd_iface->bss[i]->wps_reg_success_cb = ap_wps_reg_success_cb;
+		hapd_iface->bss[i]->wps_reg_success_cb_ctx = wpa_s;
+		hapd_iface->bss[i]->wps_event_cb = ap_wps_event_cb;
+		hapd_iface->bss[i]->wps_event_cb_ctx = wpa_s;
+		hapd_iface->bss[i]->sta_authorized_cb = ap_sta_authorized_cb;
+		hapd_iface->bss[i]->sta_authorized_cb_ctx = wpa_s;
+#ifdef CONFIG_P2P
+		hapd_iface->bss[i]->new_psk_cb = ap_new_psk_cb;
+		hapd_iface->bss[i]->new_psk_cb_ctx = wpa_s;
+		hapd_iface->bss[i]->p2p = wpa_s->global->p2p;
+		hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init(wpa_s,
+								    ssid);
+#endif /* CONFIG_P2P */
+		hapd_iface->bss[i]->setup_complete_cb = wpas_ap_configured_cb;
+		hapd_iface->bss[i]->setup_complete_cb_ctx = wpa_s;
+#ifdef CONFIG_TESTING_OPTIONS
+		hapd_iface->bss[i]->ext_eapol_frame_io =
+			wpa_s->ext_eapol_frame_io;
+#endif /* CONFIG_TESTING_OPTIONS */
+	}
+
+	os_memcpy(hapd_iface->bss[0]->own_addr, wpa_s->own_addr, ETH_ALEN);
+	hapd_iface->bss[0]->driver = wpa_s->driver;
+	hapd_iface->bss[0]->drv_priv = wpa_s->drv_priv;
+
+	wpa_s->current_ssid = ssid;
+	eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+	os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN);
+	wpa_s->assoc_freq = ssid->frequency;
+
+	if (hostapd_setup_interface(wpa_s->ap_iface)) {
+		wpa_printf(MSG_ERROR, "Failed to initialize AP interface");
+		wpa_supplicant_ap_deinit(wpa_s);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WPS
+	eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
+#endif /* CONFIG_WPS */
+
+	if (wpa_s->ap_iface == NULL)
+		return;
+
+	wpa_s->current_ssid = NULL;
+	eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+	wpa_s->assoc_freq = 0;
+	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;
+	wpa_drv_deinit_ap(wpa_s);
+	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
+		" reason=%d locally_generated=1",
+		MAC2STR(wpa_s->own_addr), WLAN_REASON_DEAUTH_LEAVING);
+}
+
+
+void ap_tx_status(void *ctx, const u8 *addr,
+		  const u8 *buf, size_t len, int ack)
+{
+#ifdef NEED_AP_MLME
+	struct wpa_supplicant *wpa_s = ctx;
+	hostapd_tx_status(wpa_s->ap_iface->bss[0], addr, buf, len, ack);
+#endif /* NEED_AP_MLME */
+}
+
+
+void ap_eapol_tx_status(void *ctx, const u8 *dst,
+			const u8 *data, size_t len, int ack)
+{
+#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 */
+}
+
+
+void ap_client_poll_ok(void *ctx, const u8 *addr)
+{
+#ifdef NEED_AP_MLME
+	struct wpa_supplicant *wpa_s = ctx;
+	if (wpa_s->ap_iface)
+		hostapd_client_poll_ok(wpa_s->ap_iface->bss[0], addr);
+#endif /* NEED_AP_MLME */
+}
+
+
+void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds)
+{
+#ifdef NEED_AP_MLME
+	struct wpa_supplicant *wpa_s = ctx;
+	ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], addr, wds);
+#endif /* NEED_AP_MLME */
+}
+
+
+void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt)
+{
+#ifdef NEED_AP_MLME
+	struct wpa_supplicant *wpa_s = ctx;
+	struct hostapd_frame_info fi;
+	os_memset(&fi, 0, sizeof(fi));
+	fi.datarate = rx_mgmt->datarate;
+	fi.ssi_signal = rx_mgmt->ssi_signal;
+	ieee802_11_mgmt(wpa_s->ap_iface->bss[0], rx_mgmt->frame,
+			rx_mgmt->frame_len, &fi);
+#endif /* NEED_AP_MLME */
+}
+
+
+void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok)
+{
+#ifdef NEED_AP_MLME
+	struct wpa_supplicant *wpa_s = ctx;
+	ieee802_11_mgmt_cb(wpa_s->ap_iface->bss[0], buf, len, stype, ok);
+#endif /* NEED_AP_MLME */
+}
+
+
+void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
+				const u8 *src_addr, const u8 *buf, size_t len)
+{
+	ieee802_1x_receive(wpa_s->ap_iface->bss[0], src_addr, buf, len);
+}
+
+
+#ifdef CONFIG_WPS
+
+int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			      const u8 *p2p_dev_addr)
+{
+	if (!wpa_s->ap_iface)
+		return -1;
+	return hostapd_wps_button_pushed(wpa_s->ap_iface->bss[0],
+					 p2p_dev_addr);
+}
+
+
+int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s)
+{
+	struct wps_registrar *reg;
+	int reg_sel = 0, wps_sta = 0;
+
+	if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]->wps)
+		return -1;
+
+	reg = wpa_s->ap_iface->bss[0]->wps->registrar;
+	reg_sel = wps_registrar_wps_cancel(reg);
+	wps_sta = ap_for_each_sta(wpa_s->ap_iface->bss[0],
+				  ap_sta_wps_cancel, NULL);
+
+	if (!reg_sel && !wps_sta) {
+		wpa_printf(MSG_DEBUG, "No WPS operation in progress at this "
+			   "time");
+		return -1;
+	}
+
+	/*
+	 * There are 2 cases to return wps cancel as success:
+	 * 1. When wps cancel was initiated but no connection has been
+	 *    established with client yet.
+	 * 2. Client is in the middle of exchanging WPS messages.
+	 */
+
+	return 0;
+}
+
+
+int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			      const char *pin, char *buf, size_t buflen,
+			      int timeout)
+{
+	int ret, ret_len = 0;
+
+	if (!wpa_s->ap_iface)
+		return -1;
+
+	if (pin == NULL) {
+		unsigned int rpin = wps_generate_pin();
+		ret_len = os_snprintf(buf, buflen, "%08d", rpin);
+		if (os_snprintf_error(buflen, ret_len))
+			return -1;
+		pin = buf;
+	} else if (buf) {
+		ret_len = os_snprintf(buf, buflen, "%s", pin);
+		if (os_snprintf_error(buflen, ret_len))
+			return -1;
+	}
+
+	ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], bssid, "any", pin,
+				  timeout);
+	if (ret)
+		return -1;
+	return ret_len;
+}
+
+
+static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_data;
+	wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
+	wpas_wps_ap_pin_disable(wpa_s);
+}
+
+
+static void wpas_wps_ap_pin_enable(struct wpa_supplicant *wpa_s, int timeout)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface == NULL)
+		return;
+	hapd = wpa_s->ap_iface->bss[0];
+	wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
+	hapd->ap_pin_failures = 0;
+	eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0,
+				       wpas_wps_ap_pin_timeout, wpa_s, NULL);
+}
+
+
+void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
+	hapd = wpa_s->ap_iface->bss[0];
+	os_free(hapd->conf->ap_pin);
+	hapd->conf->ap_pin = NULL;
+	eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
+}
+
+
+const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout)
+{
+	struct hostapd_data *hapd;
+	unsigned int pin;
+	char pin_txt[9];
+
+	if (wpa_s->ap_iface == NULL)
+		return NULL;
+	hapd = wpa_s->ap_iface->bss[0];
+	pin = wps_generate_pin();
+	os_snprintf(pin_txt, sizeof(pin_txt), "%08u", pin);
+	os_free(hapd->conf->ap_pin);
+	hapd->conf->ap_pin = os_strdup(pin_txt);
+	if (hapd->conf->ap_pin == NULL)
+		return NULL;
+	wpas_wps_ap_pin_enable(wpa_s, timeout);
+
+	return hapd->conf->ap_pin;
+}
+
+
+const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s)
+{
+	struct hostapd_data *hapd;
+	if (wpa_s->ap_iface == NULL)
+		return NULL;
+	hapd = wpa_s->ap_iface->bss[0];
+	return hapd->conf->ap_pin;
+}
+
+
+int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin,
+			int timeout)
+{
+	struct hostapd_data *hapd;
+	char pin_txt[9];
+	int ret;
+
+	if (wpa_s->ap_iface == NULL)
+		return -1;
+	hapd = wpa_s->ap_iface->bss[0];
+	ret = os_snprintf(pin_txt, sizeof(pin_txt), "%s", pin);
+	if (os_snprintf_error(sizeof(pin_txt), ret))
+		return -1;
+	os_free(hapd->conf->ap_pin);
+	hapd->conf->ap_pin = os_strdup(pin_txt);
+	if (hapd->conf->ap_pin == NULL)
+		return -1;
+	wpas_wps_ap_pin_enable(wpa_s, timeout);
+
+	return 0;
+}
+
+
+void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface == NULL)
+		return;
+	hapd = wpa_s->ap_iface->bss[0];
+
+	/*
+	 * Registrar failed to prove its knowledge of the AP PIN. Disable AP
+	 * PIN if this happens multiple times to slow down brute force attacks.
+	 */
+	hapd->ap_pin_failures++;
+	wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u",
+		   hapd->ap_pin_failures);
+	if (hapd->ap_pin_failures < 3)
+		return;
+
+	wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN");
+	hapd->ap_pin_failures = 0;
+	os_free(hapd->conf->ap_pin);
+	hapd->conf->ap_pin = NULL;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
+					     int ndef)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface == NULL)
+		return NULL;
+	hapd = wpa_s->ap_iface->bss[0];
+	return hostapd_wps_nfc_config_token(hapd, ndef);
+}
+
+
+struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+					     int ndef)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface == NULL)
+		return NULL;
+	hapd = wpa_s->ap_iface->bss[0];
+	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 */
+
+
+#ifdef CONFIG_CTRL_IFACE
+
+int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
+			    char *buf, size_t buflen)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else if (wpa_s->ifmsh)
+		hapd = wpa_s->ifmsh->bss[0];
+	else
+		return -1;
+	return hostapd_ctrl_iface_sta_first(hapd, buf, buflen);
+}
+
+
+int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
+		      char *buf, size_t buflen)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else if (wpa_s->ifmsh)
+		hapd = wpa_s->ifmsh->bss[0];
+	else
+		return -1;
+	return hostapd_ctrl_iface_sta(hapd, txtaddr, buf, buflen);
+}
+
+
+int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
+			   char *buf, size_t buflen)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else if (wpa_s->ifmsh)
+		hapd = wpa_s->ifmsh->bss[0];
+	else
+		return -1;
+	return hostapd_ctrl_iface_sta_next(hapd, txtaddr, buf, buflen);
+}
+
+
+int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s,
+				   const char *txtaddr)
+{
+	if (wpa_s->ap_iface == NULL)
+		return -1;
+	return hostapd_ctrl_iface_disassociate(wpa_s->ap_iface->bss[0],
+					       txtaddr);
+}
+
+
+int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s,
+				     const char *txtaddr)
+{
+	if (wpa_s->ap_iface == NULL)
+		return -1;
+	return hostapd_ctrl_iface_deauthenticate(wpa_s->ap_iface->bss[0],
+						 txtaddr);
+}
+
+
+int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
+				 size_t buflen, int verbose)
+{
+	char *pos = buf, *end = buf + buflen;
+	int ret;
+	struct hostapd_bss_config *conf;
+
+	if (wpa_s->ap_iface == NULL)
+		return -1;
+
+	conf = wpa_s->ap_iface->bss[0]->conf;
+	if (conf->wpa == 0)
+		return 0;
+
+	ret = os_snprintf(pos, end - pos,
+			  "pairwise_cipher=%s\n"
+			  "group_cipher=%s\n"
+			  "key_mgmt=%s\n",
+			  wpa_cipher_txt(conf->rsn_pairwise),
+			  wpa_cipher_txt(conf->wpa_group),
+			  wpa_key_mgmt_txt(conf->wpa_key_mgmt,
+					   conf->wpa));
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+	return pos - buf;
+}
+
+#endif /* CONFIG_CTRL_IFACE */
+
+
+int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s)
+{
+	struct hostapd_iface *iface = wpa_s->ap_iface;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	struct hostapd_data *hapd;
+
+	if (ssid == NULL || wpa_s->ap_iface == NULL ||
+	    ssid->mode == WPAS_MODE_INFRA ||
+	    ssid->mode == WPAS_MODE_IBSS)
+		return -1;
+
+#ifdef CONFIG_P2P
+	if (ssid->mode == WPAS_MODE_P2P_GO)
+		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 |
+			P2P_GROUP_FORMATION;
+#endif /* CONFIG_P2P */
+
+	hapd = iface->bss[0];
+	if (hapd->drv_priv == NULL)
+		return -1;
+	ieee802_11_set_beacons(iface);
+	hostapd_set_ap_wps_ie(hapd);
+
+	return 0;
+}
+
+
+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 */
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+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);
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
+		       int offset, int width, int cf1, int cf2)
+{
+	if (!wpa_s->ap_iface)
+		return;
+
+	wpa_s->assoc_freq = freq;
+	if (wpa_s->current_ssid)
+		wpa_s->current_ssid->frequency = freq;
+	hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht,
+				offset, width, cf1, cf2);
+}
+
+
+int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
+				      const u8 *addr)
+{
+	struct hostapd_data *hapd;
+	struct hostapd_bss_config *conf;
+
+	if (!wpa_s->ap_iface)
+		return -1;
+
+	if (addr)
+		wpa_printf(MSG_DEBUG, "AP: Set MAC address filter: " MACSTR,
+			   MAC2STR(addr));
+	else
+		wpa_printf(MSG_DEBUG, "AP: Clear MAC address filter");
+
+	hapd = wpa_s->ap_iface->bss[0];
+	conf = hapd->conf;
+
+	os_free(conf->accept_mac);
+	conf->accept_mac = NULL;
+	conf->num_accept_mac = 0;
+	os_free(conf->deny_mac);
+	conf->deny_mac = NULL;
+	conf->num_deny_mac = 0;
+
+	if (addr == NULL) {
+		conf->macaddr_acl = ACCEPT_UNLESS_DENIED;
+		return 0;
+	}
+
+	conf->macaddr_acl = DENY_UNLESS_ACCEPTED;
+	conf->accept_mac = os_zalloc(sizeof(struct mac_acl_entry));
+	if (conf->accept_mac == NULL)
+		return -1;
+	os_memcpy(conf->accept_mac[0].addr, addr, ETH_ALEN);
+	conf->num_accept_mac = 1;
+
+	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 */
+
+
+#ifdef CONFIG_CTRL_IFACE
+int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s)
+{
+	struct hostapd_data *hapd;
+
+	if (!wpa_s->ap_iface)
+		return -1;
+	hapd = wpa_s->ap_iface->bss[0];
+	return hostapd_ctrl_iface_stop_ap(hapd);
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#ifdef NEED_AP_MLME
+void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
+				   struct dfs_event *radar)
+{
+	if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+		return;
+	wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
+	hostapd_dfs_radar_detected(wpa_s->ap_iface, radar->freq,
+				   radar->ht_enabled, radar->chan_offset,
+				   radar->chan_width,
+				   radar->cf1, radar->cf2);
+}
+
+
+void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
+				struct dfs_event *radar)
+{
+	if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+		return;
+	wpa_printf(MSG_DEBUG, "DFS CAC started on %d MHz", radar->freq);
+	hostapd_dfs_start_cac(wpa_s->ap_iface, radar->freq,
+			      radar->ht_enabled, radar->chan_offset,
+			      radar->chan_width, radar->cf1, radar->cf2);
+}
+
+
+void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
+				 struct dfs_event *radar)
+{
+	if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+		return;
+	wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq);
+	hostapd_dfs_complete_cac(wpa_s->ap_iface, 1, radar->freq,
+				 radar->ht_enabled, radar->chan_offset,
+				 radar->chan_width, radar->cf1, radar->cf2);
+}
+
+
+void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
+				struct dfs_event *radar)
+{
+	if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+		return;
+	wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq);
+	hostapd_dfs_complete_cac(wpa_s->ap_iface, 0, radar->freq,
+				 radar->ht_enabled, radar->chan_offset,
+				 radar->chan_width, radar->cf1, radar->cf2);
+}
+
+
+void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
+				     struct dfs_event *radar)
+{
+	if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+		return;
+	wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq);
+	hostapd_dfs_nop_finished(wpa_s->ap_iface, radar->freq,
+				 radar->ht_enabled, radar->chan_offset,
+				 radar->chan_width, radar->cf1, radar->cf2);
+}
+#endif /* NEED_AP_MLME */
+
+
+void ap_periodic(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->ap_iface)
+		hostapd_periodic_iface(wpa_s->ap_iface);
+}
diff --git a/hostap/wpa_supplicant/ap.h b/hostap/wpa_supplicant/ap.h
new file mode 100644
index 0000000..594168c
--- /dev/null
+++ b/hostap/wpa_supplicant/ap.h
@@ -0,0 +1,98 @@
+/*
+ * WPA Supplicant - Basic AP mode support routines
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AP_H
+#define AP_H
+
+int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
+			     struct wpa_ssid *ssid);
+void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
+				const u8 *src_addr, const u8 *buf, size_t len);
+int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			      const u8 *p2p_dev_addr);
+int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			      const char *pin, char *buf, size_t buflen,
+			      int timeout);
+int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s);
+void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s);
+const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout);
+const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s);
+int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin,
+			int timeout);
+int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
+			    char *buf, size_t buflen);
+int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
+		      char *buf, size_t buflen);
+int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
+			   char *buf, size_t buflen);
+int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s,
+				     const char *txtaddr);
+int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s,
+				   const char *txtaddr);
+int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
+				 size_t buflen, int verbose);
+void ap_tx_status(void *ctx, const u8 *addr,
+		  const u8 *buf, size_t len, int ack);
+void ap_eapol_tx_status(void *ctx, const u8 *dst,
+			const u8 *data, size_t len, int ack);
+void ap_client_poll_ok(void *ctx, const u8 *addr);
+void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds);
+void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt);
+void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok);
+int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s);
+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 width, int cf1, int cf2);
+struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
+					     int ndef);
+#ifdef CONFIG_AP
+struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+					     int ndef);
+#else /* CONFIG_AP */
+static inline struct wpabuf *
+wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+			     int ndef)
+{
+	return NULL;
+}
+#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);
+
+struct hostapd_config;
+void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
+			       struct wpa_ssid *ssid,
+			       struct hostapd_config *conf);
+
+int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s);
+
+void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
+				   struct dfs_event *radar);
+void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
+				struct dfs_event *radar);
+void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
+				 struct dfs_event *radar);
+void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
+				struct dfs_event *radar);
+void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
+				     struct dfs_event *radar);
+
+void ap_periodic(struct wpa_supplicant *wpa_s);
+
+#endif /* AP_H */
diff --git a/hostap/wpa_supplicant/autoscan.c b/hostap/wpa_supplicant/autoscan.c
new file mode 100644
index 0000000..a2cf7a5
--- /dev/null
+++ b/hostap/wpa_supplicant/autoscan.c
@@ -0,0 +1,143 @@
+/*
+ * WPA Supplicant - auto scan
+ * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "bss.h"
+#include "scan.h"
+#include "autoscan.h"
+
+#ifdef CONFIG_AUTOSCAN_EXPONENTIAL
+extern const struct autoscan_ops autoscan_exponential_ops;
+#endif /* CONFIG_AUTOSCAN_EXPONENTIAL */
+
+#ifdef CONFIG_AUTOSCAN_PERIODIC
+extern const struct autoscan_ops autoscan_periodic_ops;
+#endif /* CONFIG_AUTOSCAN_PERIODIC */
+
+static const struct autoscan_ops * autoscan_modules[] = {
+#ifdef CONFIG_AUTOSCAN_EXPONENTIAL
+	&autoscan_exponential_ops,
+#endif /* CONFIG_AUTOSCAN_EXPONENTIAL */
+#ifdef CONFIG_AUTOSCAN_PERIODIC
+	&autoscan_periodic_ops,
+#endif /* CONFIG_AUTOSCAN_PERIODIC */
+	NULL
+};
+
+
+static void request_scan(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->scan_req = MANUAL_SCAN_REQ;
+
+	if (wpa_supplicant_req_sched_scan(wpa_s))
+		wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval, 0);
+}
+
+
+int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan)
+{
+	const char *name = wpa_s->conf->autoscan;
+	const char *params;
+	size_t nlen;
+	int i;
+	const struct autoscan_ops *ops = NULL;
+
+	if (wpa_s->autoscan && wpa_s->autoscan_priv)
+		return 0;
+
+	if (name == NULL)
+		return 0;
+
+	params = os_strchr(name, ':');
+	if (params == NULL) {
+		params = "";
+		nlen = os_strlen(name);
+	} else {
+		nlen = params - name;
+		params++;
+	}
+
+	for (i = 0; autoscan_modules[i]; i++) {
+		if (os_strncmp(name, autoscan_modules[i]->name, nlen) == 0) {
+			ops = autoscan_modules[i];
+			break;
+		}
+	}
+
+	if (ops == NULL) {
+		wpa_printf(MSG_ERROR, "autoscan: Could not find module "
+			   "matching the parameter '%s'", name);
+		return -1;
+	}
+
+	wpa_s->autoscan_params = NULL;
+
+	wpa_s->autoscan_priv = ops->init(wpa_s, params);
+	if (wpa_s->autoscan_priv == NULL)
+		return -1;
+	wpa_s->autoscan = ops;
+
+	wpa_printf(MSG_DEBUG, "autoscan: Initialized module '%s' with "
+		   "parameters '%s'", ops->name, params);
+	if (!req_scan)
+		return 0;
+
+	/*
+	 * Cancelling existing scan requests, if any.
+	 */
+	wpa_supplicant_cancel_sched_scan(wpa_s);
+	wpa_supplicant_cancel_scan(wpa_s);
+
+	/*
+	 * Firing first scan, which will lead to call autoscan_notify_scan.
+	 */
+	request_scan(wpa_s);
+
+	return 0;
+}
+
+
+void autoscan_deinit(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->autoscan && wpa_s->autoscan_priv) {
+		wpa_printf(MSG_DEBUG, "autoscan: Deinitializing module '%s'",
+			   wpa_s->autoscan->name);
+		wpa_s->autoscan->deinit(wpa_s->autoscan_priv);
+		wpa_s->autoscan = NULL;
+		wpa_s->autoscan_priv = NULL;
+
+		wpa_s->scan_interval = 5;
+		wpa_s->sched_scan_interval = 0;
+	}
+}
+
+
+int autoscan_notify_scan(struct wpa_supplicant *wpa_s,
+			 struct wpa_scan_results *scan_res)
+{
+	int interval;
+
+	if (wpa_s->autoscan && wpa_s->autoscan_priv) {
+		interval = wpa_s->autoscan->notify_scan(wpa_s->autoscan_priv,
+							scan_res);
+
+		if (interval <= 0)
+			return -1;
+
+		wpa_s->scan_interval = interval;
+		wpa_s->sched_scan_interval = interval;
+
+		request_scan(wpa_s);
+	}
+
+	return 0;
+}
diff --git a/hostap/wpa_supplicant/autoscan.h b/hostap/wpa_supplicant/autoscan.h
new file mode 100644
index 0000000..e2a7652
--- /dev/null
+++ b/hostap/wpa_supplicant/autoscan.h
@@ -0,0 +1,49 @@
+/*
+ * WPA Supplicant - auto scan
+ * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AUTOSCAN_H
+#define AUTOSCAN_H
+
+struct wpa_supplicant;
+
+struct autoscan_ops {
+	const char *name;
+
+	void * (*init)(struct wpa_supplicant *wpa_s, const char *params);
+	void (*deinit)(void *priv);
+
+	int (*notify_scan)(void *priv, struct wpa_scan_results *scan_res);
+};
+
+#ifdef CONFIG_AUTOSCAN
+
+int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan);
+void autoscan_deinit(struct wpa_supplicant *wpa_s);
+int autoscan_notify_scan(struct wpa_supplicant *wpa_s,
+			 struct wpa_scan_results *scan_res);
+
+#else /* CONFIG_AUTOSCAN */
+
+static inline int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan)
+{
+	return 0;
+}
+
+static inline void autoscan_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int autoscan_notify_scan(struct wpa_supplicant *wpa_s,
+				       struct wpa_scan_results *scan_res)
+{
+	return 0;
+}
+
+#endif /* CONFIG_AUTOSCAN */
+
+#endif /* AUTOSCAN_H */
diff --git a/hostap/wpa_supplicant/autoscan_exponential.c b/hostap/wpa_supplicant/autoscan_exponential.c
new file mode 100644
index 0000000..424477b
--- /dev/null
+++ b/hostap/wpa_supplicant/autoscan_exponential.c
@@ -0,0 +1,104 @@
+/*
+ * WPA Supplicant - auto scan exponential module
+ * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ *
+ * 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_supplicant_i.h"
+#include "autoscan.h"
+
+struct autoscan_exponential_data {
+	struct wpa_supplicant *wpa_s;
+	int base;
+	int limit;
+	int interval;
+};
+
+
+static int
+autoscan_exponential_get_params(struct autoscan_exponential_data *data,
+				const char *params)
+{
+	const char *pos;
+
+	if (params == NULL)
+		return -1;
+
+	data->base = atoi(params);
+
+	pos = os_strchr(params, ':');
+	if (pos == NULL)
+		return -1;
+
+	pos++;
+	data->limit = atoi(pos);
+
+	return 0;
+}
+
+
+static void * autoscan_exponential_init(struct wpa_supplicant *wpa_s,
+					const char *params)
+{
+	struct autoscan_exponential_data *data;
+
+	data = os_zalloc(sizeof(struct autoscan_exponential_data));
+	if (data == NULL)
+		return NULL;
+
+	if (autoscan_exponential_get_params(data, params) < 0) {
+		os_free(data);
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "autoscan exponential: base exponential is %d "
+		   "and limit is %d", data->base, data->limit);
+
+	data->wpa_s = wpa_s;
+
+	return data;
+}
+
+
+static void autoscan_exponential_deinit(void *priv)
+{
+	struct autoscan_exponential_data *data = priv;
+
+	os_free(data);
+}
+
+
+static int autoscan_exponential_notify_scan(void *priv,
+					    struct wpa_scan_results *scan_res)
+{
+	struct autoscan_exponential_data *data = priv;
+
+	wpa_printf(MSG_DEBUG, "autoscan exponential: scan result "
+		   "notification");
+
+	if (data->interval >= data->limit)
+		return data->limit;
+
+	if (data->interval <= 0)
+		data->interval = data->base;
+	else {
+		data->interval = data->interval * data->base;
+		if (data->interval > data->limit)
+			return data->limit;
+	}
+
+	return data->interval;
+}
+
+
+const struct autoscan_ops autoscan_exponential_ops = {
+	.name = "exponential",
+	.init = autoscan_exponential_init,
+	.deinit = autoscan_exponential_deinit,
+	.notify_scan = autoscan_exponential_notify_scan,
+};
diff --git a/hostap/wpa_supplicant/autoscan_periodic.c b/hostap/wpa_supplicant/autoscan_periodic.c
new file mode 100644
index 0000000..102d723
--- /dev/null
+++ b/hostap/wpa_supplicant/autoscan_periodic.c
@@ -0,0 +1,85 @@
+/*
+ * WPA Supplicant - auto scan periodic module
+ * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ *
+ * 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_supplicant_i.h"
+#include "autoscan.h"
+
+
+struct autoscan_periodic_data {
+	int periodic_interval;
+};
+
+
+static int autoscan_periodic_get_params(struct autoscan_periodic_data *data,
+					const char *params)
+{
+	int interval;
+
+	if (params == NULL)
+		return -1;
+
+	interval = atoi(params);
+
+	if (interval < 0)
+		return -1;
+
+	data->periodic_interval = interval;
+
+	return 0;
+}
+
+
+static void * autoscan_periodic_init(struct wpa_supplicant *wpa_s,
+				     const char *params)
+{
+	struct autoscan_periodic_data *data;
+
+	data = os_zalloc(sizeof(struct autoscan_periodic_data));
+	if (data == NULL)
+		return NULL;
+
+	if (autoscan_periodic_get_params(data, params) < 0) {
+		os_free(data);
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "autoscan periodic: interval is %d",
+		   data->periodic_interval);
+
+	return data;
+}
+
+
+static void autoscan_periodic_deinit(void *priv)
+{
+	struct autoscan_periodic_data *data = priv;
+
+	os_free(data);
+}
+
+
+static int autoscan_periodic_notify_scan(void *priv,
+					 struct wpa_scan_results *scan_res)
+{
+	struct autoscan_periodic_data *data = priv;
+
+	wpa_printf(MSG_DEBUG, "autoscan periodic: scan result notification");
+
+	return data->periodic_interval;
+}
+
+
+const struct autoscan_ops autoscan_periodic_ops = {
+	.name = "periodic",
+	.init = autoscan_periodic_init,
+	.deinit = autoscan_periodic_deinit,
+	.notify_scan = autoscan_periodic_notify_scan,
+};
diff --git a/hostap/wpa_supplicant/bgscan.c b/hostap/wpa_supplicant/bgscan.c
new file mode 100644
index 0000000..f74cdbf
--- /dev/null
+++ b/hostap/wpa_supplicant/bgscan.c
@@ -0,0 +1,117 @@
+/*
+ * WPA Supplicant - background scan and roaming interface
+ * Copyright (c) 2009-2010, 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 "wpa_supplicant_i.h"
+#include "config_ssid.h"
+#include "bgscan.h"
+
+#ifdef CONFIG_BGSCAN_SIMPLE
+extern const struct bgscan_ops bgscan_simple_ops;
+#endif /* CONFIG_BGSCAN_SIMPLE */
+#ifdef CONFIG_BGSCAN_LEARN
+extern const struct bgscan_ops bgscan_learn_ops;
+#endif /* CONFIG_BGSCAN_LEARN */
+
+static const struct bgscan_ops * bgscan_modules[] = {
+#ifdef CONFIG_BGSCAN_SIMPLE
+	&bgscan_simple_ops,
+#endif /* CONFIG_BGSCAN_SIMPLE */
+#ifdef CONFIG_BGSCAN_LEARN
+	&bgscan_learn_ops,
+#endif /* CONFIG_BGSCAN_LEARN */
+	NULL
+};
+
+
+int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+		const char *name)
+{
+	const char *params;
+	size_t nlen;
+	int i;
+	const struct bgscan_ops *ops = NULL;
+
+	bgscan_deinit(wpa_s);
+	if (name == NULL)
+		return -1;
+
+	params = os_strchr(name, ':');
+	if (params == NULL) {
+		params = "";
+		nlen = os_strlen(name);
+	} else {
+		nlen = params - name;
+		params++;
+	}
+
+	for (i = 0; bgscan_modules[i]; i++) {
+		if (os_strncmp(name, bgscan_modules[i]->name, nlen) == 0) {
+			ops = bgscan_modules[i];
+			break;
+		}
+	}
+
+	if (ops == NULL) {
+		wpa_printf(MSG_ERROR, "bgscan: Could not find module "
+			   "matching the parameter '%s'", name);
+		return -1;
+	}
+
+	wpa_s->bgscan_priv = ops->init(wpa_s, params, ssid);
+	if (wpa_s->bgscan_priv == NULL)
+		return -1;
+	wpa_s->bgscan = ops;
+	wpa_printf(MSG_DEBUG, "bgscan: Initialized module '%s' with "
+		   "parameters '%s'", ops->name, params);
+
+	return 0;
+}
+
+
+void bgscan_deinit(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->bgscan && wpa_s->bgscan_priv) {
+		wpa_printf(MSG_DEBUG, "bgscan: Deinitializing module '%s'",
+			   wpa_s->bgscan->name);
+		wpa_s->bgscan->deinit(wpa_s->bgscan_priv);
+		wpa_s->bgscan = NULL;
+		wpa_s->bgscan_priv = NULL;
+	}
+}
+
+
+int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
+		       struct wpa_scan_results *scan_res)
+{
+	if (wpa_s->bgscan && wpa_s->bgscan_priv)
+		return wpa_s->bgscan->notify_scan(wpa_s->bgscan_priv,
+						  scan_res);
+	return 0;
+}
+
+
+void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->bgscan && wpa_s->bgscan_priv)
+		wpa_s->bgscan->notify_beacon_loss(wpa_s->bgscan_priv);
+}
+
+
+void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above,
+				 int current_signal, int current_noise,
+				 int current_txrate)
+{
+	if (wpa_s->bgscan && wpa_s->bgscan_priv)
+		wpa_s->bgscan->notify_signal_change(wpa_s->bgscan_priv, above,
+						    current_signal,
+						    current_noise,
+						    current_txrate);
+}
diff --git a/hostap/wpa_supplicant/bgscan.h b/hostap/wpa_supplicant/bgscan.h
new file mode 100644
index 0000000..9131e4e
--- /dev/null
+++ b/hostap/wpa_supplicant/bgscan.h
@@ -0,0 +1,73 @@
+/*
+ * WPA Supplicant - background scan and roaming interface
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BGSCAN_H
+#define BGSCAN_H
+
+struct wpa_supplicant;
+struct wpa_ssid;
+
+struct bgscan_ops {
+	const char *name;
+
+	void * (*init)(struct wpa_supplicant *wpa_s, const char *params,
+		       const struct wpa_ssid *ssid);
+	void (*deinit)(void *priv);
+
+	int (*notify_scan)(void *priv, struct wpa_scan_results *scan_res);
+	void (*notify_beacon_loss)(void *priv);
+	void (*notify_signal_change)(void *priv, int above,
+				     int current_signal,
+				     int current_noise,
+				     int current_txrate);
+};
+
+#ifdef CONFIG_BGSCAN
+
+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);
+void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s);
+void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above,
+				 int current_signal, int current_noise,
+				 int current_txrate);
+
+#else /* CONFIG_BGSCAN */
+
+static inline int bgscan_init(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid, const char name)
+{
+	return 0;
+}
+
+static inline void bgscan_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
+				     struct wpa_scan_results *scan_res)
+{
+	return 0;
+}
+
+static inline void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s,
+					       int above, int current_signal,
+					       int current_noise,
+					       int current_txrate)
+{
+}
+
+#endif /* CONFIG_BGSCAN */
+
+#endif /* BGSCAN_H */
diff --git a/hostap/wpa_supplicant/bgscan_learn.c b/hostap/wpa_supplicant/bgscan_learn.c
new file mode 100644
index 0000000..a320cc4
--- /dev/null
+++ b/hostap/wpa_supplicant/bgscan_learn.c
@@ -0,0 +1,617 @@
+/*
+ * WPA Supplicant - background scan and roaming module: learn
+ * Copyright (c) 2009-2010, 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 "eloop.h"
+#include "list.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "config_ssid.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "scan.h"
+#include "bgscan.h"
+
+struct bgscan_learn_bss {
+	struct dl_list list;
+	u8 bssid[ETH_ALEN];
+	int freq;
+	u8 *neigh; /* num_neigh * ETH_ALEN buffer */
+	size_t num_neigh;
+};
+
+struct bgscan_learn_data {
+	struct wpa_supplicant *wpa_s;
+	const struct wpa_ssid *ssid;
+	int scan_interval;
+	int signal_threshold;
+	int short_interval; /* use if signal < threshold */
+	int long_interval; /* use if signal > threshold */
+	struct os_reltime last_bgscan;
+	char *fname;
+	struct dl_list bss;
+	int *supp_freqs;
+	int probe_idx;
+};
+
+
+static void bss_free(struct bgscan_learn_bss *bss)
+{
+	os_free(bss->neigh);
+	os_free(bss);
+}
+
+
+static int bssid_in_array(u8 *array, size_t array_len, const u8 *bssid)
+{
+	size_t i;
+
+	if (array == NULL || array_len == 0)
+		return 0;
+
+	for (i = 0; i < array_len; i++) {
+		if (os_memcmp(array + i * ETH_ALEN, bssid, ETH_ALEN) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void bgscan_learn_add_neighbor(struct bgscan_learn_bss *bss,
+				      const u8 *bssid)
+{
+	u8 *n;
+
+	if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+		return;
+	if (bssid_in_array(bss->neigh, bss->num_neigh, bssid))
+		return;
+
+	n = os_realloc_array(bss->neigh, bss->num_neigh + 1, ETH_ALEN);
+	if (n == NULL)
+		return;
+
+	os_memcpy(n + bss->num_neigh * ETH_ALEN, bssid, ETH_ALEN);
+	bss->neigh = n;
+	bss->num_neigh++;
+}
+
+
+static struct bgscan_learn_bss * bgscan_learn_get_bss(
+	struct bgscan_learn_data *data, const u8 *bssid)
+{
+	struct bgscan_learn_bss *bss;
+
+	dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+		if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+			return bss;
+	}
+	return NULL;
+}
+
+
+static int bgscan_learn_load(struct bgscan_learn_data *data)
+{
+	FILE *f;
+	char buf[128];
+	struct bgscan_learn_bss *bss;
+
+	if (data->fname == NULL)
+		return 0;
+
+	f = fopen(data->fname, "r");
+	if (f == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "bgscan learn: Loading data from %s",
+		   data->fname);
+
+	if (fgets(buf, sizeof(buf), f) == NULL ||
+	    os_strncmp(buf, "wpa_supplicant-bgscan-learn\n", 28) != 0) {
+		wpa_printf(MSG_INFO, "bgscan learn: Invalid data file %s",
+			   data->fname);
+		fclose(f);
+		return -1;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		if (os_strncmp(buf, "BSS ", 4) == 0) {
+			bss = os_zalloc(sizeof(*bss));
+			if (!bss)
+				continue;
+			if (hwaddr_aton(buf + 4, bss->bssid) < 0) {
+				bss_free(bss);
+				continue;
+			}
+			bss->freq = atoi(buf + 4 + 18);
+			dl_list_add(&data->bss, &bss->list);
+			wpa_printf(MSG_DEBUG, "bgscan learn: Loaded BSS "
+				   "entry: " MACSTR " freq=%d",
+				   MAC2STR(bss->bssid), bss->freq);
+		}
+
+		if (os_strncmp(buf, "NEIGHBOR ", 9) == 0) {
+			u8 addr[ETH_ALEN];
+
+			if (hwaddr_aton(buf + 9, addr) < 0)
+				continue;
+			bss = bgscan_learn_get_bss(data, addr);
+			if (bss == NULL)
+				continue;
+			if (hwaddr_aton(buf + 9 + 18, addr) < 0)
+				continue;
+
+			bgscan_learn_add_neighbor(bss, addr);
+		}
+	}
+
+	fclose(f);
+	return 0;
+}
+
+
+static void bgscan_learn_save(struct bgscan_learn_data *data)
+{
+	FILE *f;
+	struct bgscan_learn_bss *bss;
+
+	if (data->fname == NULL)
+		return;
+
+	wpa_printf(MSG_DEBUG, "bgscan learn: Saving data to %s",
+		   data->fname);
+
+	f = fopen(data->fname, "w");
+	if (f == NULL)
+		return;
+	fprintf(f, "wpa_supplicant-bgscan-learn\n");
+
+	dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+		fprintf(f, "BSS " MACSTR " %d\n",
+			MAC2STR(bss->bssid), bss->freq);
+	}
+
+	dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+		size_t i;
+		for (i = 0; i < bss->num_neigh; i++) {
+			fprintf(f, "NEIGHBOR " MACSTR " " MACSTR "\n",
+				MAC2STR(bss->bssid),
+				MAC2STR(bss->neigh + i * ETH_ALEN));
+		}
+	}
+
+	fclose(f);
+}
+
+
+static int in_array(int *array, int val)
+{
+	int i;
+
+	if (array == NULL)
+		return 0;
+
+	for (i = 0; array[i]; i++) {
+		if (array[i] == val)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int * bgscan_learn_get_freqs(struct bgscan_learn_data *data,
+				    size_t *count)
+{
+	struct bgscan_learn_bss *bss;
+	int *freqs = NULL, *n;
+
+	*count = 0;
+
+	dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+		if (in_array(freqs, bss->freq))
+			continue;
+		n = os_realloc_array(freqs, *count + 2, sizeof(int));
+		if (n == NULL)
+			return freqs;
+		freqs = n;
+		freqs[*count] = bss->freq;
+		(*count)++;
+		freqs[*count] = 0;
+	}
+
+	return freqs;
+}
+
+
+static int * bgscan_learn_get_probe_freq(struct bgscan_learn_data *data,
+					 int *freqs, size_t count)
+{
+	int idx, *n;
+
+	if (data->supp_freqs == NULL)
+		return freqs;
+
+	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 + 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;
+			freqs = n;
+			freqs[count] = data->supp_freqs[idx];
+			count++;
+			freqs[count] = 0;
+			break;
+		}
+
+		idx++;
+		if (data->supp_freqs[idx] == 0)
+			idx = 0;
+	} while (idx != data->probe_idx);
+
+	return freqs;
+}
+
+
+static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct bgscan_learn_data *data = eloop_ctx;
+	struct wpa_supplicant *wpa_s = data->wpa_s;
+	struct wpa_driver_scan_params params;
+	int *freqs = NULL;
+	size_t count, i;
+	char msg[100], *pos;
+
+	os_memset(&params, 0, sizeof(params));
+	params.num_ssids = 1;
+	params.ssids[0].ssid = data->ssid->ssid;
+	params.ssids[0].ssid_len = data->ssid->ssid_len;
+	if (data->ssid->scan_freq)
+		params.freqs = data->ssid->scan_freq;
+	else {
+		freqs = bgscan_learn_get_freqs(data, &count);
+		wpa_printf(MSG_DEBUG, "bgscan learn: BSSes in this ESS have "
+			   "been seen on %u channels", (unsigned int) count);
+		freqs = bgscan_learn_get_probe_freq(data, freqs, count);
+
+		msg[0] = '\0';
+		pos = msg;
+		for (i = 0; freqs && freqs[i]; i++) {
+			int ret;
+			ret = os_snprintf(pos, msg + sizeof(msg) - pos, " %d",
+					  freqs[i]);
+			if (os_snprintf_error(msg + sizeof(msg) - pos, ret))
+				break;
+			pos += ret;
+		}
+		pos[0] = '\0';
+		wpa_printf(MSG_DEBUG, "bgscan learn: Scanning frequencies:%s",
+			   msg);
+		params.freqs = freqs;
+	}
+
+	wpa_printf(MSG_DEBUG, "bgscan learn: Request a background scan");
+	if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
+		wpa_printf(MSG_DEBUG, "bgscan learn: Failed to trigger scan");
+		eloop_register_timeout(data->scan_interval, 0,
+				       bgscan_learn_timeout, data, NULL);
+	} else
+		os_get_reltime(&data->last_bgscan);
+	os_free(freqs);
+}
+
+
+static int bgscan_learn_get_params(struct bgscan_learn_data *data,
+				   const char *params)
+{
+	const char *pos;
+
+	if (params == NULL)
+		return 0;
+
+	data->short_interval = atoi(params);
+
+	pos = os_strchr(params, ':');
+	if (pos == NULL)
+		return 0;
+	pos++;
+	data->signal_threshold = atoi(pos);
+	pos = os_strchr(pos, ':');
+	if (pos == NULL) {
+		wpa_printf(MSG_ERROR, "bgscan learn: Missing scan interval "
+			   "for high signal");
+		return -1;
+	}
+	pos++;
+	data->long_interval = atoi(pos);
+	pos = os_strchr(pos, ':');
+	if (pos) {
+		pos++;
+		data->fname = os_strdup(pos);
+	}
+
+	return 0;
+}
+
+
+static int * bgscan_learn_get_supp_freqs(struct wpa_supplicant *wpa_s)
+{
+	struct hostapd_hw_modes *modes;
+	int i, j, *freqs = NULL, *n;
+	size_t count = 0;
+
+	modes = wpa_s->hw.modes;
+	if (modes == NULL)
+		return NULL;
+
+	for (i = 0; i < wpa_s->hw.num_modes; i++) {
+		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;
+
+			freqs = n;
+			freqs[count] = modes[i].channels[j].freq;
+			count++;
+			freqs[count] = 0;
+		}
+	}
+
+	return freqs;
+}
+
+
+static void * bgscan_learn_init(struct wpa_supplicant *wpa_s,
+				const char *params,
+				const struct wpa_ssid *ssid)
+{
+	struct bgscan_learn_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	dl_list_init(&data->bss);
+	data->wpa_s = wpa_s;
+	data->ssid = ssid;
+	if (bgscan_learn_get_params(data, params) < 0) {
+		os_free(data->fname);
+		os_free(data);
+		return NULL;
+	}
+	if (data->short_interval <= 0)
+		data->short_interval = 30;
+	if (data->long_interval <= 0)
+		data->long_interval = 30;
+
+	if (bgscan_learn_load(data) < 0) {
+		os_free(data->fname);
+		os_free(data);
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "bgscan learn: Signal strength threshold %d  "
+		   "Short bgscan interval %d  Long bgscan interval %d",
+		   data->signal_threshold, data->short_interval,
+		   data->long_interval);
+
+	if (data->signal_threshold &&
+	    wpa_drv_signal_monitor(wpa_s, data->signal_threshold, 4) < 0) {
+		wpa_printf(MSG_ERROR, "bgscan learn: Failed to enable "
+			   "signal strength monitoring");
+	}
+
+	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);
+
+	/*
+	 * This function is called immediately after an association, so it is
+	 * reasonable to assume that a scan was completed recently. This makes
+	 * us skip an immediate new scan in cases where the current signal
+	 * level is below the bgscan threshold.
+	 */
+	os_get_reltime(&data->last_bgscan);
+
+	return data;
+}
+
+
+static void bgscan_learn_deinit(void *priv)
+{
+	struct bgscan_learn_data *data = priv;
+	struct bgscan_learn_bss *bss, *n;
+
+	bgscan_learn_save(data);
+	eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+	if (data->signal_threshold)
+		wpa_drv_signal_monitor(data->wpa_s, 0, 0);
+	os_free(data->fname);
+	dl_list_for_each_safe(bss, n, &data->bss, struct bgscan_learn_bss,
+			      list) {
+		dl_list_del(&bss->list);
+		bss_free(bss);
+	}
+	os_free(data->supp_freqs);
+	os_free(data);
+}
+
+
+static int bgscan_learn_bss_match(struct bgscan_learn_data *data,
+				  struct wpa_scan_res *bss)
+{
+	const u8 *ie;
+
+	ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
+	if (ie == NULL)
+		return 0;
+
+	if (data->ssid->ssid_len != ie[1] ||
+	    os_memcmp(data->ssid->ssid, ie + 2, ie[1]) != 0)
+		return 0; /* SSID mismatch */
+
+	return 1;
+}
+
+
+static int bgscan_learn_notify_scan(void *priv,
+				    struct wpa_scan_results *scan_res)
+{
+	struct bgscan_learn_data *data = priv;
+	size_t i, j;
+#define MAX_BSS 50
+	u8 bssid[MAX_BSS * ETH_ALEN];
+	size_t num_bssid = 0;
+
+	wpa_printf(MSG_DEBUG, "bgscan learn: scan result notification");
+
+	eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+	eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout,
+			       data, NULL);
+
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *res = scan_res->res[i];
+		if (!bgscan_learn_bss_match(data, res))
+			continue;
+
+		if (num_bssid < MAX_BSS) {
+			os_memcpy(bssid + num_bssid * ETH_ALEN, res->bssid,
+				  ETH_ALEN);
+			num_bssid++;
+		}
+	}
+	wpa_printf(MSG_DEBUG, "bgscan learn: %u matching BSSes in scan "
+		   "results", (unsigned int) num_bssid);
+
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *res = scan_res->res[i];
+		struct bgscan_learn_bss *bss;
+
+		if (!bgscan_learn_bss_match(data, res))
+			continue;
+
+		bss = bgscan_learn_get_bss(data, res->bssid);
+		if (bss && bss->freq != res->freq) {
+			wpa_printf(MSG_DEBUG, "bgscan learn: Update BSS "
+			   MACSTR " freq %d -> %d",
+				   MAC2STR(res->bssid), bss->freq, res->freq);
+			bss->freq = res->freq;
+		} else if (!bss) {
+			wpa_printf(MSG_DEBUG, "bgscan learn: Add BSS " MACSTR
+				   " freq=%d", MAC2STR(res->bssid), res->freq);
+			bss = os_zalloc(sizeof(*bss));
+			if (!bss)
+				continue;
+			os_memcpy(bss->bssid, res->bssid, ETH_ALEN);
+			bss->freq = res->freq;
+			dl_list_add(&data->bss, &bss->list);
+		}
+
+		for (j = 0; j < num_bssid; j++) {
+			u8 *addr = bssid + j * ETH_ALEN;
+			bgscan_learn_add_neighbor(bss, addr);
+		}
+	}
+
+	/*
+	 * A more advanced bgscan could process scan results internally, select
+	 * the BSS and request roam if needed. This sample uses the existing
+	 * BSS/ESS selection routine. Change this to return 1 if selection is
+	 * done inside the bgscan module.
+	 */
+
+	return 0;
+}
+
+
+static void bgscan_learn_notify_beacon_loss(void *priv)
+{
+	wpa_printf(MSG_DEBUG, "bgscan learn: beacon loss");
+	/* TODO: speed up background scanning */
+}
+
+
+static void bgscan_learn_notify_signal_change(void *priv, int above,
+					      int current_signal,
+					      int current_noise,
+					      int current_txrate)
+{
+	struct bgscan_learn_data *data = priv;
+	int scan = 0;
+	struct os_reltime now;
+
+	if (data->short_interval == data->long_interval ||
+	    data->signal_threshold == 0)
+		return;
+
+	wpa_printf(MSG_DEBUG, "bgscan learn: signal level changed "
+		   "(above=%d current_signal=%d current_noise=%d "
+		   "current_txrate=%d)", above, current_signal,
+		   current_noise, current_txrate);
+	if (data->scan_interval == data->long_interval && !above) {
+		wpa_printf(MSG_DEBUG, "bgscan learn: Start using short bgscan "
+			   "interval");
+		data->scan_interval = data->short_interval;
+		os_get_reltime(&now);
+		if (now.sec > data->last_bgscan.sec + 1)
+			scan = 1;
+	} else if (data->scan_interval == data->short_interval && above) {
+		wpa_printf(MSG_DEBUG, "bgscan learn: Start using long bgscan "
+			   "interval");
+		data->scan_interval = data->long_interval;
+		eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+		eloop_register_timeout(data->scan_interval, 0,
+				       bgscan_learn_timeout, data, NULL);
+	} else if (!above) {
+		/*
+		 * Signal dropped further 4 dB. Request a new scan if we have
+		 * not yet scanned in a while.
+		 */
+		os_get_reltime(&now);
+		if (now.sec > data->last_bgscan.sec + 10)
+			scan = 1;
+	}
+
+	if (scan) {
+		wpa_printf(MSG_DEBUG, "bgscan learn: Trigger immediate scan");
+		eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+		eloop_register_timeout(0, 0, bgscan_learn_timeout, data, NULL);
+	}
+}
+
+
+const struct bgscan_ops bgscan_learn_ops = {
+	.name = "learn",
+	.init = bgscan_learn_init,
+	.deinit = bgscan_learn_deinit,
+	.notify_scan = bgscan_learn_notify_scan,
+	.notify_beacon_loss = bgscan_learn_notify_beacon_loss,
+	.notify_signal_change = bgscan_learn_notify_signal_change,
+};
diff --git a/hostap/wpa_supplicant/bgscan_simple.c b/hostap/wpa_supplicant/bgscan_simple.c
new file mode 100644
index 0000000..a467cc5
--- /dev/null
+++ b/hostap/wpa_supplicant/bgscan_simple.c
@@ -0,0 +1,283 @@
+/*
+ * WPA Supplicant - background scan and roaming module: simple
+ * Copyright (c) 2009-2010, 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 "eloop.h"
+#include "drivers/driver.h"
+#include "config_ssid.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "scan.h"
+#include "bgscan.h"
+
+struct bgscan_simple_data {
+	struct wpa_supplicant *wpa_s;
+	const struct wpa_ssid *ssid;
+	int scan_interval;
+	int signal_threshold;
+	int short_scan_count; /* counter for scans using short scan interval */
+	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_reltime last_bgscan;
+};
+
+
+static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct bgscan_simple_data *data = eloop_ctx;
+	struct wpa_supplicant *wpa_s = data->wpa_s;
+	struct wpa_driver_scan_params params;
+
+	os_memset(&params, 0, sizeof(params));
+	params.num_ssids = 1;
+	params.ssids[0].ssid = data->ssid->ssid;
+	params.ssids[0].ssid_len = data->ssid->ssid_len;
+	params.freqs = data->ssid->scan_freq;
+
+	/*
+	 * A more advanced bgscan module would learn about most like channels
+	 * over time and request scans only for some channels (probing others
+	 * every now and then) to reduce effect on the data connection.
+	 */
+
+	wpa_printf(MSG_DEBUG, "bgscan simple: Request a background scan");
+	if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
+		wpa_printf(MSG_DEBUG, "bgscan simple: Failed to trigger scan");
+		eloop_register_timeout(data->scan_interval, 0,
+				       bgscan_simple_timeout, data, NULL);
+	} else {
+		if (data->scan_interval == data->short_interval) {
+			data->short_scan_count++;
+			/*
+			 * Spend at most the duration of a long scan interval
+			 * scanning at the short scan interval. After that,
+			 * revert to the long scan interval.
+			 */
+			if (data->short_scan_count > data->max_short_scans) {
+				data->scan_interval = data->long_interval;
+				wpa_printf(MSG_DEBUG, "bgscan simple: Backing "
+					   "off to long scan interval");
+			}
+		} else if (data->short_scan_count > 0) {
+			/*
+			 * If we lasted a long scan interval without any
+			 * CQM triggers, decrease the short-scan count,
+			 * which allows 1 more short-scan interval to
+			 * occur in the future when CQM triggers.
+			 */
+			data->short_scan_count--;
+		}
+		os_get_reltime(&data->last_bgscan);
+	}
+}
+
+
+static int bgscan_simple_get_params(struct bgscan_simple_data *data,
+				    const char *params)
+{
+	const char *pos;
+
+	if (params == NULL)
+		return 0;
+
+	data->short_interval = atoi(params);
+
+	pos = os_strchr(params, ':');
+	if (pos == NULL)
+		return 0;
+	pos++;
+	data->signal_threshold = atoi(pos);
+	pos = os_strchr(pos, ':');
+	if (pos == NULL) {
+		wpa_printf(MSG_ERROR, "bgscan simple: Missing scan interval "
+			   "for high signal");
+		return -1;
+	}
+	pos++;
+	data->long_interval = atoi(pos);
+
+	return 0;
+}
+
+
+static void * bgscan_simple_init(struct wpa_supplicant *wpa_s,
+				 const char *params,
+				 const struct wpa_ssid *ssid)
+{
+	struct bgscan_simple_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->wpa_s = wpa_s;
+	data->ssid = ssid;
+	if (bgscan_simple_get_params(data, params) < 0) {
+		os_free(data);
+		return NULL;
+	}
+	if (data->short_interval <= 0)
+		data->short_interval = 30;
+	if (data->long_interval <= 0)
+		data->long_interval = 30;
+
+	wpa_printf(MSG_DEBUG, "bgscan simple: Signal strength threshold %d  "
+		   "Short bgscan interval %d  Long bgscan interval %d",
+		   data->signal_threshold, data->short_interval,
+		   data->long_interval);
+
+	if (data->signal_threshold &&
+	    wpa_drv_signal_monitor(wpa_s, data->signal_threshold, 4) < 0) {
+		wpa_printf(MSG_ERROR, "bgscan simple: Failed to enable "
+			   "signal strength monitoring");
+	}
+
+	data->scan_interval = data->short_interval;
+	data->max_short_scans = data->long_interval / data->short_interval + 1;
+	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;
+	}
+	wpa_printf(MSG_DEBUG, "bgscan simple: Init scan interval: %d",
+		   data->scan_interval);
+	eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout,
+			       data, NULL);
+
+	/*
+	 * This function is called immediately after an association, so it is
+	 * reasonable to assume that a scan was completed recently. This makes
+	 * us skip an immediate new scan in cases where the current signal
+	 * level is below the bgscan threshold.
+	 */
+	os_get_reltime(&data->last_bgscan);
+
+	return data;
+}
+
+
+static void bgscan_simple_deinit(void *priv)
+{
+	struct bgscan_simple_data *data = priv;
+	eloop_cancel_timeout(bgscan_simple_timeout, data, NULL);
+	if (data->signal_threshold)
+		wpa_drv_signal_monitor(data->wpa_s, 0, 0);
+	os_free(data);
+}
+
+
+static int bgscan_simple_notify_scan(void *priv,
+				     struct wpa_scan_results *scan_res)
+{
+	struct bgscan_simple_data *data = priv;
+
+	wpa_printf(MSG_DEBUG, "bgscan simple: scan result notification");
+
+	eloop_cancel_timeout(bgscan_simple_timeout, data, NULL);
+	eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout,
+			       data, NULL);
+
+	/*
+	 * A more advanced bgscan could process scan results internally, select
+	 * the BSS and request roam if needed. This sample uses the existing
+	 * BSS/ESS selection routine. Change this to return 1 if selection is
+	 * done inside the bgscan module.
+	 */
+
+	return 0;
+}
+
+
+static void bgscan_simple_notify_beacon_loss(void *priv)
+{
+	wpa_printf(MSG_DEBUG, "bgscan simple: beacon loss");
+	/* TODO: speed up background scanning */
+}
+
+
+static void bgscan_simple_notify_signal_change(void *priv, int above,
+					       int current_signal,
+					       int current_noise,
+					       int current_txrate)
+{
+	struct bgscan_simple_data *data = priv;
+	int scan = 0;
+	struct os_reltime now;
+
+	if (data->short_interval == data->long_interval ||
+	    data->signal_threshold == 0)
+		return;
+
+	wpa_printf(MSG_DEBUG, "bgscan simple: signal level changed "
+		   "(above=%d current_signal=%d current_noise=%d "
+		   "current_txrate=%d))", above, current_signal,
+		   current_noise, current_txrate);
+	if (data->scan_interval == data->long_interval && !above) {
+		wpa_printf(MSG_DEBUG, "bgscan simple: Start using short "
+			   "bgscan interval");
+		data->scan_interval = data->short_interval;
+		os_get_reltime(&now);
+		if (now.sec > data->last_bgscan.sec + 1 &&
+		    data->short_scan_count <= data->max_short_scans)
+			/*
+			 * If we haven't just previously (<1 second ago)
+			 * performed a scan, and we haven't depleted our
+			 * budget for short-scans, perform a scan
+			 * immediately.
+			 */
+			scan = 1;
+		else if (data->last_bgscan.sec + data->long_interval >
+			 now.sec + data->scan_interval) {
+			/*
+			 * Restart scan interval timer if currently scheduled
+			 * scan is too far in the future.
+			 */
+			eloop_cancel_timeout(bgscan_simple_timeout, data,
+					     NULL);
+			eloop_register_timeout(data->scan_interval, 0,
+					       bgscan_simple_timeout, data,
+					       NULL);
+		}
+	} else if (data->scan_interval == data->short_interval && above) {
+		wpa_printf(MSG_DEBUG, "bgscan simple: Start using long bgscan "
+			   "interval");
+		data->scan_interval = data->long_interval;
+		eloop_cancel_timeout(bgscan_simple_timeout, data, NULL);
+		eloop_register_timeout(data->scan_interval, 0,
+				       bgscan_simple_timeout, data, NULL);
+	} else if (!above) {
+		/*
+		 * Signal dropped further 4 dB. Request a new scan if we have
+		 * not yet scanned in a while.
+		 */
+		os_get_reltime(&now);
+		if (now.sec > data->last_bgscan.sec + 10)
+			scan = 1;
+	}
+
+	if (scan) {
+		wpa_printf(MSG_DEBUG, "bgscan simple: Trigger immediate scan");
+		eloop_cancel_timeout(bgscan_simple_timeout, data, NULL);
+		eloop_register_timeout(0, 0, bgscan_simple_timeout, data,
+				       NULL);
+	}
+}
+
+
+const struct bgscan_ops bgscan_simple_ops = {
+	.name = "simple",
+	.init = bgscan_simple_init,
+	.deinit = bgscan_simple_deinit,
+	.notify_scan = bgscan_simple_notify_scan,
+	.notify_beacon_loss = bgscan_simple_notify_beacon_loss,
+	.notify_signal_change = bgscan_simple_notify_signal_change,
+};
diff --git a/hostap/wpa_supplicant/blacklist.c b/hostap/wpa_supplicant/blacklist.c
new file mode 100644
index 0000000..e53dc38
--- /dev/null
+++ b/hostap/wpa_supplicant/blacklist.c
@@ -0,0 +1,141 @@
+/*
+ * wpa_supplicant - Temporary BSSID blacklist
+ * Copyright (c) 2003-2007, 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 "wpa_supplicant_i.h"
+#include "blacklist.h"
+
+/**
+ * wpa_blacklist_get - Get the blacklist entry for a BSSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * Returns: Matching blacklist entry for the BSSID or %NULL if not found
+ */
+struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s,
+					 const u8 *bssid)
+{
+	struct wpa_blacklist *e;
+
+	if (wpa_s == NULL || bssid == NULL)
+		return NULL;
+
+	e = wpa_s->blacklist;
+	while (e) {
+		if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0)
+			return e;
+		e = e->next;
+	}
+
+	return NULL;
+}
+
+
+/**
+ * wpa_blacklist_add - Add an BSSID to the blacklist
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to be added to the blacklist
+ * Returns: Current blacklist count on success, -1 on failure
+ *
+ * This function adds the specified BSSID to the blacklist or increases the
+ * blacklist count if the BSSID was already listed. It should be called when
+ * an association attempt fails either due to the selected BSS rejecting
+ * association or due to timeout.
+ *
+ * This blacklist is used to force %wpa_supplicant to go through all available
+ * BSSes before retrying to associate with an BSS that rejected or timed out
+ * association. It does not prevent the listed BSS from being used; it only
+ * changes the order in which they are tried.
+ */
+int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+	struct wpa_blacklist *e;
+
+	if (wpa_s == NULL || bssid == NULL)
+		return -1;
+
+	e = wpa_blacklist_get(wpa_s, bssid);
+	if (e) {
+		e->count++;
+		wpa_printf(MSG_DEBUG, "BSSID " MACSTR " blacklist count "
+			   "incremented to %d",
+			   MAC2STR(bssid), e->count);
+		return e->count;
+	}
+
+	e = os_zalloc(sizeof(*e));
+	if (e == NULL)
+		return -1;
+	os_memcpy(e->bssid, bssid, ETH_ALEN);
+	e->count = 1;
+	e->next = wpa_s->blacklist;
+	wpa_s->blacklist = e;
+	wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist",
+		   MAC2STR(bssid));
+
+	return e->count;
+}
+
+
+/**
+ * wpa_blacklist_del - Remove an BSSID from the blacklist
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to be removed from the blacklist
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+	struct wpa_blacklist *e, *prev = NULL;
+
+	if (wpa_s == NULL || bssid == NULL)
+		return -1;
+
+	e = wpa_s->blacklist;
+	while (e) {
+		if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) {
+			if (prev == NULL) {
+				wpa_s->blacklist = e->next;
+			} else {
+				prev->next = e->next;
+			}
+			wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from "
+				   "blacklist", MAC2STR(bssid));
+			os_free(e);
+			return 0;
+		}
+		prev = e;
+		e = e->next;
+	}
+	return -1;
+}
+
+
+/**
+ * wpa_blacklist_clear - Clear the blacklist of all entries
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void wpa_blacklist_clear(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_blacklist *e, *prev;
+	int max_count = 0;
+
+	e = wpa_s->blacklist;
+	wpa_s->blacklist = NULL;
+	while (e) {
+		if (e->count > max_count)
+			max_count = e->count;
+		prev = e;
+		e = e->next;
+		wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from "
+			   "blacklist (clear)", MAC2STR(prev->bssid));
+		os_free(prev);
+	}
+
+	wpa_s->extra_blacklist_count += max_count;
+}
diff --git a/hostap/wpa_supplicant/blacklist.h b/hostap/wpa_supplicant/blacklist.h
new file mode 100644
index 0000000..ae06986
--- /dev/null
+++ b/hostap/wpa_supplicant/blacklist.h
@@ -0,0 +1,24 @@
+/*
+ * wpa_supplicant - Temporary BSSID blacklist
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BLACKLIST_H
+#define BLACKLIST_H
+
+struct wpa_blacklist {
+	struct wpa_blacklist *next;
+	u8 bssid[ETH_ALEN];
+	int count;
+};
+
+struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s,
+					 const u8 *bssid);
+int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid);
+void wpa_blacklist_clear(struct wpa_supplicant *wpa_s);
+
+#endif /* BLACKLIST_H */
diff --git a/hostap/wpa_supplicant/bss.c b/hostap/wpa_supplicant/bss.c
new file mode 100644
index 0000000..1051ee3
--- /dev/null
+++ b/hostap/wpa_supplicant/bss.c
@@ -0,0 +1,1236 @@
+/*
+ * BSS table
+ * Copyright (c) 2009-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "notify.h"
+#include "scan.h"
+#include "bss.h"
+
+
+#define WPA_BSS_FREQ_CHANGED_FLAG	BIT(0)
+#define WPA_BSS_SIGNAL_CHANGED_FLAG	BIT(1)
+#define WPA_BSS_PRIVACY_CHANGED_FLAG	BIT(2)
+#define WPA_BSS_MODE_CHANGED_FLAG	BIT(3)
+#define WPA_BSS_WPAIE_CHANGED_FLAG	BIT(4)
+#define WPA_BSS_RSNIE_CHANGED_FLAG	BIT(5)
+#define WPA_BSS_WPS_CHANGED_FLAG	BIT(6)
+#define WPA_BSS_RATES_CHANGED_FLAG	BIT(7)
+#define WPA_BSS_IES_CHANGED_FLAG	BIT(8)
+
+
+static void wpa_bss_set_hessid(struct wpa_bss *bss)
+{
+#ifdef CONFIG_INTERWORKING
+	const u8 *ie = wpa_bss_get_ie(bss, WLAN_EID_INTERWORKING);
+	if (ie == NULL || (ie[1] != 7 && ie[1] != 9)) {
+		os_memset(bss->hessid, 0, ETH_ALEN);
+		return;
+	}
+	if (ie[1] == 7)
+		os_memcpy(bss->hessid, ie + 3, ETH_ALEN);
+	else
+		os_memcpy(bss->hessid, ie + 5, ETH_ALEN);
+#endif /* CONFIG_INTERWORKING */
+}
+
+
+/**
+ * wpa_bss_anqp_alloc - Allocate ANQP data structure for a BSS entry
+ * Returns: Allocated ANQP data structure or %NULL on failure
+ *
+ * The allocated ANQP data structure has its users count set to 1. It may be
+ * shared by multiple BSS entries and each shared entry is freed with
+ * wpa_bss_anqp_free().
+ */
+struct wpa_bss_anqp * wpa_bss_anqp_alloc(void)
+{
+	struct wpa_bss_anqp *anqp;
+	anqp = os_zalloc(sizeof(*anqp));
+	if (anqp == NULL)
+		return NULL;
+	anqp->users = 1;
+	return anqp;
+}
+
+
+/**
+ * wpa_bss_anqp_clone - Clone an ANQP data structure
+ * @anqp: ANQP data structure from wpa_bss_anqp_alloc()
+ * Returns: Cloned ANQP data structure or %NULL on failure
+ */
+static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp)
+{
+	struct wpa_bss_anqp *n;
+
+	n = os_zalloc(sizeof(*n));
+	if (n == NULL)
+		return NULL;
+
+#define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f)
+#ifdef CONFIG_INTERWORKING
+	ANQP_DUP(capability_list);
+	ANQP_DUP(venue_name);
+	ANQP_DUP(network_auth_type);
+	ANQP_DUP(roaming_consortium);
+	ANQP_DUP(ip_addr_type_availability);
+	ANQP_DUP(nai_realm);
+	ANQP_DUP(anqp_3gpp);
+	ANQP_DUP(domain_name);
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+	ANQP_DUP(hs20_capability_list);
+	ANQP_DUP(hs20_operator_friendly_name);
+	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
+
+	return n;
+}
+
+
+/**
+ * wpa_bss_anqp_unshare_alloc - Unshare ANQP data (if shared) in a BSS entry
+ * @bss: BSS entry
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function ensures the specific BSS entry has an ANQP data structure that
+ * is not shared with any other BSS entry.
+ */
+int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss)
+{
+	struct wpa_bss_anqp *anqp;
+
+	if (bss->anqp && bss->anqp->users > 1) {
+		/* allocated, but shared - clone an unshared copy */
+		anqp = wpa_bss_anqp_clone(bss->anqp);
+		if (anqp == NULL)
+			return -1;
+		anqp->users = 1;
+		bss->anqp->users--;
+		bss->anqp = anqp;
+		return 0;
+	}
+
+	if (bss->anqp)
+		return 0; /* already allocated and not shared */
+
+	/* not allocated - allocate a new storage area */
+	bss->anqp = wpa_bss_anqp_alloc();
+	return bss->anqp ? 0 : -1;
+}
+
+
+/**
+ * wpa_bss_anqp_free - Free an ANQP data structure
+ * @anqp: ANQP data structure from wpa_bss_anqp_alloc() or wpa_bss_anqp_clone()
+ */
+static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
+{
+	if (anqp == NULL)
+		return;
+
+	anqp->users--;
+	if (anqp->users > 0) {
+		/* Another BSS entry holds a pointer to this ANQP info */
+		return;
+	}
+
+#ifdef CONFIG_INTERWORKING
+	wpabuf_free(anqp->capability_list);
+	wpabuf_free(anqp->venue_name);
+	wpabuf_free(anqp->network_auth_type);
+	wpabuf_free(anqp->roaming_consortium);
+	wpabuf_free(anqp->ip_addr_type_availability);
+	wpabuf_free(anqp->nai_realm);
+	wpabuf_free(anqp->anqp_3gpp);
+	wpabuf_free(anqp->domain_name);
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+	wpabuf_free(anqp->hs20_capability_list);
+	wpabuf_free(anqp->hs20_operator_friendly_name);
+	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);
+}
+
+
+static void wpa_bss_update_pending_connect(struct wpa_supplicant *wpa_s,
+					   struct wpa_bss *old_bss,
+					   struct wpa_bss *new_bss)
+{
+	struct wpa_radio_work *work;
+	struct wpa_connect_work *cwork;
+
+	work = radio_work_pending(wpa_s, "sme-connect");
+	if (!work)
+		work = radio_work_pending(wpa_s, "connect");
+	if (!work)
+		return;
+
+	cwork = work->ctx;
+	if (cwork->bss != old_bss)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "Update BSS pointer for the pending connect radio work");
+	cwork->bss = new_bss;
+	if (!new_bss)
+		cwork->bss_removed = 1;
+}
+
+
+static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			   const char *reason)
+{
+	if (wpa_s->last_scan_res) {
+		unsigned int i;
+		for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+			if (wpa_s->last_scan_res[i] == bss) {
+				os_memmove(&wpa_s->last_scan_res[i],
+					   &wpa_s->last_scan_res[i + 1],
+					   (wpa_s->last_scan_res_used - i - 1)
+					   * sizeof(struct wpa_bss *));
+				wpa_s->last_scan_res_used--;
+				break;
+			}
+		}
+	}
+	wpa_bss_update_pending_connect(wpa_s, bss, NULL);
+	dl_list_del(&bss->list);
+	dl_list_del(&bss->list_id);
+	wpa_s->num_bss--;
+	wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR
+		" SSID '%s' due to %s", bss->id, MAC2STR(bss->bssid),
+		wpa_ssid_txt(bss->ssid, bss->ssid_len), reason);
+	wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
+	wpa_bss_anqp_free(bss->anqp);
+	os_free(bss);
+}
+
+
+/**
+ * wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * @ssid: SSID
+ * @ssid_len: Length of @ssid
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
+struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			     const u8 *ssid, size_t ssid_len)
+{
+	struct wpa_bss *bss;
+	if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
+		return NULL;
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
+		    bss->ssid_len == ssid_len &&
+		    os_memcmp(bss->ssid, ssid, ssid_len) == 0)
+			return bss;
+	}
+	return NULL;
+}
+
+
+static void calculate_update_time(const struct os_reltime *fetch_time,
+				  unsigned int age_ms,
+				  struct os_reltime *update_time)
+{
+	os_time_t usec;
+
+	update_time->sec = fetch_time->sec;
+	update_time->usec = fetch_time->usec;
+	update_time->sec -= age_ms / 1000;
+	usec = (age_ms % 1000) * 1000;
+	if (update_time->usec < usec) {
+		update_time->sec--;
+		update_time->usec += 1000000;
+	}
+	update_time->usec -= usec;
+}
+
+
+static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
+			     struct os_reltime *fetch_time)
+{
+	dst->flags = src->flags;
+	os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
+	dst->freq = src->freq;
+	dst->beacon_int = src->beacon_int;
+	dst->caps = src->caps;
+	dst->qual = src->qual;
+	dst->noise = src->noise;
+	dst->level = src->level;
+	dst->tsf = src->tsf;
+	dst->est_throughput = src->est_throughput;
+	dst->snr = src->snr;
+
+	calculate_update_time(fetch_time, src->age, &dst->last_update);
+}
+
+
+static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+	struct wpa_ssid *ssid;
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (ssid->ssid == NULL || ssid->ssid_len == 0)
+			continue;
+		if (ssid->ssid_len == bss->ssid_len &&
+		    os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+	if (bss == wpa_s->current_bss)
+		return 1;
+
+	if (wpa_s->current_bss &&
+	    (bss->ssid_len != wpa_s->current_bss->ssid_len ||
+	     os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
+		       bss->ssid_len) != 0))
+		return 0; /* SSID has changed */
+
+	return !is_zero_ether_addr(bss->bssid) &&
+		(os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
+		 os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0);
+}
+
+
+static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss;
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		if (!wpa_bss_known(wpa_s, bss)) {
+			wpa_bss_remove(wpa_s, bss, __func__);
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss;
+
+	/*
+	 * Remove the oldest entry that does not match with any configured
+	 * network.
+	 */
+	if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
+		return 0;
+
+	/*
+	 * Remove the oldest entry that isn't currently in use.
+	 */
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		if (!wpa_bss_in_use(wpa_s, bss)) {
+			wpa_bss_remove(wpa_s, bss, __func__);
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+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_reltime *fetch_time)
+{
+	struct wpa_bss *bss;
+
+	bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
+	if (bss == NULL)
+		return NULL;
+	bss->id = wpa_s->bss_next_id++;
+	bss->last_update_idx = wpa_s->bss_update_idx;
+	wpa_bss_copy_res(bss, res, fetch_time);
+	os_memcpy(bss->ssid, ssid, ssid_len);
+	bss->ssid_len = ssid_len;
+	bss->ie_len = res->ie_len;
+	bss->beacon_ie_len = res->beacon_ie_len;
+	os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
+	wpa_bss_set_hessid(bss);
+
+	if (wpa_s->num_bss + 1 > wpa_s->conf->bss_max_count &&
+	    wpa_bss_remove_oldest(wpa_s) != 0) {
+		wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d "
+			   "because all BSSes are in use. We should normally "
+			   "not get here!", (int) wpa_s->num_bss + 1);
+		wpa_s->conf->bss_max_count = wpa_s->num_bss + 1;
+	}
+
+	dl_list_add_tail(&wpa_s->bss, &bss->list);
+	dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
+	wpa_s->num_bss++;
+	wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
+		" SSID '%s' freq %d",
+		bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
+		bss->freq);
+	wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
+	return bss;
+}
+
+
+static int are_ies_equal(const struct wpa_bss *old,
+			 const struct wpa_scan_res *new_res, u32 ie)
+{
+	const u8 *old_ie, *new_ie;
+	struct wpabuf *old_ie_buff = NULL;
+	struct wpabuf *new_ie_buff = NULL;
+	int new_ie_len, old_ie_len, ret, is_multi;
+
+	switch (ie) {
+	case WPA_IE_VENDOR_TYPE:
+		old_ie = wpa_bss_get_vendor_ie(old, ie);
+		new_ie = wpa_scan_get_vendor_ie(new_res, ie);
+		is_multi = 0;
+		break;
+	case WPS_IE_VENDOR_TYPE:
+		old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie);
+		new_ie_buff = wpa_scan_get_vendor_ie_multi(new_res, ie);
+		is_multi = 1;
+		break;
+	case WLAN_EID_RSN:
+	case WLAN_EID_SUPP_RATES:
+	case WLAN_EID_EXT_SUPP_RATES:
+		old_ie = wpa_bss_get_ie(old, ie);
+		new_ie = wpa_scan_get_ie(new_res, ie);
+		is_multi = 0;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "bss: %s: cannot compare IEs", __func__);
+		return 0;
+	}
+
+	if (is_multi) {
+		/* in case of multiple IEs stored in buffer */
+		old_ie = old_ie_buff ? wpabuf_head_u8(old_ie_buff) : NULL;
+		new_ie = new_ie_buff ? wpabuf_head_u8(new_ie_buff) : NULL;
+		old_ie_len = old_ie_buff ? wpabuf_len(old_ie_buff) : 0;
+		new_ie_len = new_ie_buff ? wpabuf_len(new_ie_buff) : 0;
+	} else {
+		/* in case of single IE */
+		old_ie_len = old_ie ? old_ie[1] + 2 : 0;
+		new_ie_len = new_ie ? new_ie[1] + 2 : 0;
+	}
+
+	if (!old_ie || !new_ie)
+		ret = !old_ie && !new_ie;
+	else
+		ret = (old_ie_len == new_ie_len &&
+		       os_memcmp(old_ie, new_ie, old_ie_len) == 0);
+
+	wpabuf_free(old_ie_buff);
+	wpabuf_free(new_ie_buff);
+
+	return ret;
+}
+
+
+static u32 wpa_bss_compare_res(const struct wpa_bss *old,
+			       const struct wpa_scan_res *new_res)
+{
+	u32 changes = 0;
+	int caps_diff = old->caps ^ new_res->caps;
+
+	if (old->freq != new_res->freq)
+		changes |= WPA_BSS_FREQ_CHANGED_FLAG;
+
+	if (old->level != new_res->level)
+		changes |= WPA_BSS_SIGNAL_CHANGED_FLAG;
+
+	if (caps_diff & IEEE80211_CAP_PRIVACY)
+		changes |= WPA_BSS_PRIVACY_CHANGED_FLAG;
+
+	if (caps_diff & IEEE80211_CAP_IBSS)
+		changes |= WPA_BSS_MODE_CHANGED_FLAG;
+
+	if (old->ie_len == new_res->ie_len &&
+	    os_memcmp(old + 1, new_res + 1, old->ie_len) == 0)
+		return changes;
+	changes |= WPA_BSS_IES_CHANGED_FLAG;
+
+	if (!are_ies_equal(old, new_res, WPA_IE_VENDOR_TYPE))
+		changes |= WPA_BSS_WPAIE_CHANGED_FLAG;
+
+	if (!are_ies_equal(old, new_res, WLAN_EID_RSN))
+		changes |= WPA_BSS_RSNIE_CHANGED_FLAG;
+
+	if (!are_ies_equal(old, new_res, WPS_IE_VENDOR_TYPE))
+		changes |= WPA_BSS_WPS_CHANGED_FLAG;
+
+	if (!are_ies_equal(old, new_res, WLAN_EID_SUPP_RATES) ||
+	    !are_ies_equal(old, new_res, WLAN_EID_EXT_SUPP_RATES))
+		changes |= WPA_BSS_RATES_CHANGED_FLAG;
+
+	return changes;
+}
+
+
+static void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes,
+			       const struct wpa_bss *bss)
+{
+	if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
+		wpas_notify_bss_freq_changed(wpa_s, bss->id);
+
+	if (changes & WPA_BSS_SIGNAL_CHANGED_FLAG)
+		wpas_notify_bss_signal_changed(wpa_s, bss->id);
+
+	if (changes & WPA_BSS_PRIVACY_CHANGED_FLAG)
+		wpas_notify_bss_privacy_changed(wpa_s, bss->id);
+
+	if (changes & WPA_BSS_MODE_CHANGED_FLAG)
+		wpas_notify_bss_mode_changed(wpa_s, bss->id);
+
+	if (changes & WPA_BSS_WPAIE_CHANGED_FLAG)
+		wpas_notify_bss_wpaie_changed(wpa_s, bss->id);
+
+	if (changes & WPA_BSS_RSNIE_CHANGED_FLAG)
+		wpas_notify_bss_rsnie_changed(wpa_s, bss->id);
+
+	if (changes & WPA_BSS_WPS_CHANGED_FLAG)
+		wpas_notify_bss_wps_changed(wpa_s, bss->id);
+
+	if (changes & WPA_BSS_IES_CHANGED_FLAG)
+		wpas_notify_bss_ies_changed(wpa_s, bss->id);
+
+	if (changes & WPA_BSS_RATES_CHANGED_FLAG)
+		wpas_notify_bss_rates_changed(wpa_s, bss->id);
+
+	wpas_notify_bss_seen(wpa_s, bss->id);
+}
+
+
+static struct wpa_bss *
+wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+	       struct wpa_scan_res *res, struct os_reltime *fetch_time)
+{
+	u32 changes;
+
+	changes = wpa_bss_compare_res(bss, res);
+	if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
+		wpa_printf(MSG_DEBUG, "BSS: " MACSTR " changed freq %d --> %d",
+			   MAC2STR(bss->bssid), bss->freq, res->freq);
+	bss->scan_miss_count = 0;
+	bss->last_update_idx = wpa_s->bss_update_idx;
+	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);
+		bss->ie_len = res->ie_len;
+		bss->beacon_ie_len = res->beacon_ie_len;
+	} else {
+		struct wpa_bss *nbss;
+		struct dl_list *prev = bss->list_id.prev;
+		dl_list_del(&bss->list_id);
+		nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
+				  res->beacon_ie_len);
+		if (nbss) {
+			unsigned int i;
+			for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+				if (wpa_s->last_scan_res[i] == bss) {
+					wpa_s->last_scan_res[i] = nbss;
+					break;
+				}
+			}
+			if (wpa_s->current_bss == bss)
+				wpa_s->current_bss = nbss;
+			wpa_bss_update_pending_connect(wpa_s, bss, nbss);
+			bss = nbss;
+			os_memcpy(bss + 1, res + 1,
+				  res->ie_len + res->beacon_ie_len);
+			bss->ie_len = res->ie_len;
+			bss->beacon_ie_len = res->beacon_ie_len;
+		}
+		dl_list_add(prev, &bss->list_id);
+	}
+	if (changes & WPA_BSS_IES_CHANGED_FLAG)
+		wpa_bss_set_hessid(bss);
+	dl_list_add_tail(&wpa_s->bss, &bss->list);
+
+	notify_bss_changes(wpa_s, changes, bss);
+
+	return bss;
+}
+
+
+/**
+ * wpa_bss_update_start - Start a BSS table update from scan results
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is called at the start of each BSS table update round for new
+ * scan results. The actual scan result entries are indicated with calls to
+ * wpa_bss_update_scan_res() and the update round is finished with a call to
+ * wpa_bss_update_end().
+ */
+void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->bss_update_idx++;
+	wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
+		wpa_s->bss_update_idx);
+	wpa_s->last_scan_res_used = 0;
+}
+
+
+/**
+ * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @res: Scan result
+ * @fetch_time: Time when the result was fetched from the driver
+ *
+ * This function updates a BSS table entry (or adds one) based on a scan result.
+ * This is called separately for each scan result between the calls to
+ * wpa_bss_update_start() and wpa_bss_update_end().
+ */
+void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
+			     struct wpa_scan_res *res,
+			     struct os_reltime *fetch_time)
+{
+	const u8 *ssid, *p2p, *mesh;
+	struct wpa_bss *bss;
+
+	if (wpa_s->conf->ignore_old_scan_res) {
+		struct os_reltime update;
+		calculate_update_time(fetch_time, res->age, &update);
+		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",
+				(unsigned int) age.sec,
+				(unsigned int) age.usec);
+			return;
+		}
+	}
+
+	ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
+	if (ssid == NULL) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
+			MACSTR, MAC2STR(res->bssid));
+		return;
+	}
+	if (ssid[1] > SSID_MAX_LEN) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for "
+			MACSTR, MAC2STR(res->bssid));
+		return;
+	}
+
+	p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE);
+#ifdef CONFIG_P2P
+	if (p2p == NULL &&
+	    wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
+		/*
+		 * If it's a P2P specific interface, then don't update
+		 * the scan result without a P2P IE.
+		 */
+		wpa_printf(MSG_DEBUG, "BSS: No P2P IE - skipping BSS " MACSTR
+			   " update for P2P interface", MAC2STR(res->bssid));
+		return;
+	}
+#endif /* CONFIG_P2P */
+	if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN &&
+	    os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0)
+		return; /* Skip P2P listen discovery results here */
+
+	/* TODO: add option for ignoring BSSes we are not interested in
+	 * (to save memory) */
+
+	mesh = wpa_scan_get_ie(res, WLAN_EID_MESH_ID);
+	if (mesh && mesh[1] <= SSID_MAX_LEN)
+		ssid = mesh;
+
+	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 {
+		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;
+	if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) {
+		struct wpa_bss **n;
+		unsigned int siz;
+		if (wpa_s->last_scan_res_size == 0)
+			siz = 32;
+		else
+			siz = wpa_s->last_scan_res_size * 2;
+		n = os_realloc_array(wpa_s->last_scan_res, siz,
+				     sizeof(struct wpa_bss *));
+		if (n == NULL)
+			return;
+		wpa_s->last_scan_res = n;
+		wpa_s->last_scan_res_size = siz;
+	}
+
+	if (wpa_s->last_scan_res)
+		wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
+}
+
+
+static int wpa_bss_included_in_scan(const struct wpa_bss *bss,
+				    const struct scan_info *info)
+{
+	int found;
+	size_t i;
+
+	if (info == NULL)
+		return 1;
+
+	if (info->num_freqs) {
+		found = 0;
+		for (i = 0; i < info->num_freqs; i++) {
+			if (bss->freq == info->freqs[i]) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			return 0;
+	}
+
+	if (info->num_ssids) {
+		found = 0;
+		for (i = 0; i < info->num_ssids; i++) {
+			const struct wpa_driver_scan_ssid *s = &info->ssids[i];
+			if ((s->ssid == NULL || s->ssid_len == 0) ||
+			    (s->ssid_len == bss->ssid_len &&
+			     os_memcmp(s->ssid, bss->ssid, bss->ssid_len) ==
+			     0)) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+/**
+ * wpa_bss_update_end - End a BSS table update from scan results
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @info: Information about scan parameters
+ * @new_scan: Whether this update round was based on a new scan
+ *
+ * This function is called at the end of each BSS table update round for new
+ * scan results. The start of the update was indicated with a call to
+ * wpa_bss_update_start().
+ */
+void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
+			int new_scan)
+{
+	struct wpa_bss *bss, *n;
+
+	os_get_reltime(&wpa_s->last_scan);
+	if (!new_scan)
+		return; /* do not expire entries without new scan */
+
+	dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
+		if (wpa_bss_in_use(wpa_s, bss))
+			continue;
+		if (!wpa_bss_included_in_scan(bss, info))
+			continue; /* expire only BSSes that were scanned */
+		if (bss->last_update_idx < wpa_s->bss_update_idx)
+			bss->scan_miss_count++;
+		if (bss->scan_miss_count >=
+		    wpa_s->conf->bss_expiration_scan_count) {
+			wpa_bss_remove(wpa_s, bss, "no match in scan");
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u",
+		   wpa_s->last_scan_res_used, wpa_s->last_scan_res_size);
+}
+
+
+/**
+ * wpa_bss_flush_by_age - Flush old BSS entries
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @age: Maximum entry age in seconds
+ *
+ * Remove BSS entries that have not been updated during the last @age seconds.
+ */
+void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
+{
+	struct wpa_bss *bss, *n;
+	struct os_reltime t;
+
+	if (dl_list_empty(&wpa_s->bss))
+		return;
+
+	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_reltime_before(&bss->last_update, &t)) {
+			wpa_bss_remove(wpa_s, bss, __func__);
+		} else
+			break;
+	}
+}
+
+
+/**
+ * wpa_bss_init - Initialize BSS table
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This prepares BSS table lists and timer for periodic updates. The BSS table
+ * is deinitialized with wpa_bss_deinit() once not needed anymore.
+ */
+int wpa_bss_init(struct wpa_supplicant *wpa_s)
+{
+	dl_list_init(&wpa_s->bss);
+	dl_list_init(&wpa_s->bss_id);
+	return 0;
+}
+
+
+/**
+ * wpa_bss_flush - Flush all unused BSS entries
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void wpa_bss_flush(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss, *n;
+
+	wpa_s->clear_driver_scan_cache = 1;
+
+	if (wpa_s->bss.next == NULL)
+		return; /* BSS table not yet initialized */
+
+	dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
+		if (wpa_bss_in_use(wpa_s, bss))
+			continue;
+		wpa_bss_remove(wpa_s, bss, __func__);
+	}
+}
+
+
+/**
+ * wpa_bss_deinit - Deinitialize BSS table
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
+{
+	wpa_bss_flush(wpa_s);
+}
+
+
+/**
+ * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
+struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
+				   const u8 *bssid)
+{
+	struct wpa_bss *bss;
+	if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
+		return NULL;
+	dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
+		if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+			return bss;
+	}
+	return NULL;
+}
+
+
+/**
+ * wpa_bss_get_bssid_latest - Fetch the latest BSS table entry based on BSSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ *
+ * This function is like wpa_bss_get_bssid(), but full BSS table is iterated to
+ * find the entry that has the most recent update. This can help in finding the
+ * correct entry in cases where the SSID of the AP may have changed recently
+ * (e.g., in WPS reconfiguration cases).
+ */
+struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s,
+					  const u8 *bssid)
+{
+	struct wpa_bss *bss, *found = NULL;
+	if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
+		return NULL;
+	dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
+		if (os_memcmp(bss->bssid, bssid, ETH_ALEN) != 0)
+			continue;
+		if (found == NULL ||
+		    os_reltime_before(&found->last_update, &bss->last_update))
+			found = bss;
+	}
+	return found;
+}
+
+
+#ifdef CONFIG_P2P
+/**
+ * wpa_bss_get_p2p_dev_addr - Fetch a BSS table entry based on P2P Device Addr
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @dev_addr: P2P Device Address of the GO
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
+struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
+					  const u8 *dev_addr)
+{
+	struct wpa_bss *bss;
+	dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
+		u8 addr[ETH_ALEN];
+		if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len,
+				       addr) == 0 &&
+		    os_memcmp(addr, dev_addr, ETH_ALEN) == 0)
+			return bss;
+	}
+	return NULL;
+}
+#endif /* CONFIG_P2P */
+
+
+/**
+ * wpa_bss_get_id - Fetch a BSS table entry based on identifier
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @id: Unique identifier (struct wpa_bss::id) assigned for the entry
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
+struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+	struct wpa_bss *bss;
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		if (bss->id == id)
+			return bss;
+	}
+	return NULL;
+}
+
+
+/**
+ * wpa_bss_get_id_range - Fetch a BSS table entry based on identifier range
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @idf: Smallest allowed identifier assigned for the entry
+ * @idf: Largest allowed identifier assigned for the entry
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ *
+ * This function is similar to wpa_bss_get_id() but allows a BSS entry with the
+ * smallest id value to be fetched within the specified range without the
+ * caller having to know the exact id.
+ */
+struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
+				      unsigned int idf, unsigned int idl)
+{
+	struct wpa_bss *bss;
+	dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
+		if (bss->id >= idf && bss->id <= idl)
+			return bss;
+	}
+	return NULL;
+}
+
+
+/**
+ * wpa_bss_get_ie - Fetch a specified information element from a BSS entry
+ * @bss: BSS table entry
+ * @ie: Information element identitifier (WLAN_EID_*)
+ * 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.
+ */
+const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
+{
+	const u8 *end, *pos;
+
+	pos = (const u8 *) (bss + 1);
+	end = pos + bss->ie_len;
+
+	while (pos + 1 < end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		if (pos[0] == ie)
+			return pos;
+		pos += 2 + pos[1];
+	}
+
+	return NULL;
+}
+
+
+/**
+ * wpa_bss_get_vendor_ie - Fetch a vendor information element 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.
+ */
+const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
+{
+	const u8 *end, *pos;
+
+	pos = (const u8 *) (bss + 1);
+	end = pos + bss->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_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)
+ * Returns: Pointer to the information element payload or %NULL if not found
+ *
+ * This function returns concatenated payload of possibly fragmented vendor
+ * specific information elements in the BSS entry. The caller is responsible for
+ * freeing the returned buffer.
+ */
+struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
+					    u32 vendor_type)
+{
+	struct wpabuf *buf;
+	const u8 *end, *pos;
+
+	buf = wpabuf_alloc(bss->ie_len);
+	if (buf == NULL)
+		return NULL;
+
+	pos = (const u8 *) (bss + 1);
+	end = pos + bss->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]))
+			wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
+		pos += 2 + pos[1];
+	}
+
+	if (wpabuf_len(buf) == 0) {
+		wpabuf_free(buf);
+		buf = NULL;
+	}
+
+	return buf;
+}
+
+
+/**
+ * wpa_bss_get_vendor_ie_multi_beacon - Fetch vendor IE data from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element payload or %NULL if not found
+ *
+ * This function returns concatenated payload of possibly fragmented vendor
+ * specific information elements in the BSS entry. The caller is responsible for
+ * freeing the returned buffer.
+ *
+ * This function is like wpa_bss_get_vendor_ie_multi(), but uses IE buffer only
+ * from Beacon frames instead of either Beacon or Probe Response frames.
+ */
+struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
+						   u32 vendor_type)
+{
+	struct wpabuf *buf;
+	const u8 *end, *pos;
+
+	buf = wpabuf_alloc(bss->beacon_ie_len);
+	if (buf == NULL)
+		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]))
+			wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
+		pos += 2 + pos[1];
+	}
+
+	if (wpabuf_len(buf) == 0) {
+		wpabuf_free(buf);
+		buf = NULL;
+	}
+
+	return buf;
+}
+
+
+/**
+ * wpa_bss_get_max_rate - Get maximum legacy TX rate supported in a BSS
+ * @bss: BSS table entry
+ * Returns: Maximum legacy rate in units of 500 kbps
+ */
+int wpa_bss_get_max_rate(const struct wpa_bss *bss)
+{
+	int rate = 0;
+	const u8 *ie;
+	int i;
+
+	ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
+	for (i = 0; ie && i < ie[1]; i++) {
+		if ((ie[i + 2] & 0x7f) > rate)
+			rate = ie[i + 2] & 0x7f;
+	}
+
+	ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
+	for (i = 0; ie && i < ie[1]; i++) {
+		if ((ie[i + 2] & 0x7f) > rate)
+			rate = ie[i + 2] & 0x7f;
+	}
+
+	return rate;
+}
+
+
+/**
+ * wpa_bss_get_bit_rates - Get legacy TX rates supported in a BSS
+ * @bss: BSS table entry
+ * @rates: Buffer for returning a pointer to the rates list (units of 500 kbps)
+ * Returns: number of legacy TX rates or -1 on failure
+ *
+ * The caller is responsible for freeing the returned buffer with os_free() in
+ * case of success.
+ */
+int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates)
+{
+	const u8 *ie, *ie2;
+	int i, j;
+	unsigned int len;
+	u8 *r;
+
+	ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
+	ie2 = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
+
+	len = (ie ? ie[1] : 0) + (ie2 ? ie2[1] : 0);
+
+	r = os_malloc(len);
+	if (!r)
+		return -1;
+
+	for (i = 0; ie && i < ie[1]; i++)
+		r[i] = ie[i + 2] & 0x7f;
+
+	for (j = 0; ie2 && j < ie2[1]; j++)
+		r[i + j] = ie2[j + 2] & 0x7f;
+
+	*rates = r;
+	return len;
+}
diff --git a/hostap/wpa_supplicant/bss.h b/hostap/wpa_supplicant/bss.h
new file mode 100644
index 0000000..b215380
--- /dev/null
+++ b/hostap/wpa_supplicant/bss.h
@@ -0,0 +1,150 @@
+/*
+ * BSS table
+ * Copyright (c) 2009-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BSS_H
+#define BSS_H
+
+struct wpa_scan_res;
+
+#define WPA_BSS_QUAL_INVALID		BIT(0)
+#define WPA_BSS_NOISE_INVALID		BIT(1)
+#define WPA_BSS_LEVEL_INVALID		BIT(2)
+#define WPA_BSS_LEVEL_DBM		BIT(3)
+#define WPA_BSS_AUTHENTICATED		BIT(4)
+#define WPA_BSS_ASSOCIATED		BIT(5)
+#define WPA_BSS_ANQP_FETCH_TRIED	BIT(6)
+
+/**
+ * struct wpa_bss_anqp - ANQP data for a BSS entry (struct wpa_bss)
+ */
+struct wpa_bss_anqp {
+	/** Number of BSS entries referring to this ANQP data instance */
+	unsigned int users;
+#ifdef CONFIG_INTERWORKING
+	struct wpabuf *capability_list;
+	struct wpabuf *venue_name;
+	struct wpabuf *network_auth_type;
+	struct wpabuf *roaming_consortium;
+	struct wpabuf *ip_addr_type_availability;
+	struct wpabuf *nai_realm;
+	struct wpabuf *anqp_3gpp;
+	struct wpabuf *domain_name;
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+	struct wpabuf *hs20_capability_list;
+	struct wpabuf *hs20_operator_friendly_name;
+	struct wpabuf *hs20_wan_metrics;
+	struct wpabuf *hs20_connection_capability;
+	struct wpabuf *hs20_operating_class;
+	struct wpabuf *hs20_osu_providers_list;
+#endif /* CONFIG_HS20 */
+};
+
+/**
+ * struct wpa_bss - BSS table
+ *
+ * This structure is used to store information about neighboring BSSes in
+ * generic format. It is mainly updated based on scan results from the driver.
+ */
+struct wpa_bss {
+	/** List entry for struct wpa_supplicant::bss */
+	struct dl_list list;
+	/** List entry for struct wpa_supplicant::bss_id */
+	struct dl_list list_id;
+	/** Unique identifier for this BSS entry */
+	unsigned int id;
+	/** Number of counts without seeing this BSS */
+	unsigned int scan_miss_count;
+	/** Index of the last scan update */
+	unsigned int last_update_idx;
+	/** Information flags about the BSS/IBSS (WPA_BSS_*) */
+	unsigned int flags;
+	/** BSSID */
+	u8 bssid[ETH_ALEN];
+	/** HESSID */
+	u8 hessid[ETH_ALEN];
+	/** SSID */
+	u8 ssid[SSID_MAX_LEN];
+	/** Length of SSID */
+	size_t ssid_len;
+	/** Frequency of the channel in MHz (e.g., 2412 = channel 1) */
+	int freq;
+	/** Beacon interval in TUs (host byte order) */
+	u16 beacon_int;
+	/** Capability information field in host byte order */
+	u16 caps;
+	/** Signal quality */
+	int qual;
+	/** Noise level */
+	int noise;
+	/** Signal level */
+	int level;
+	/** Timestamp of last Beacon/Probe Response frame */
+	u64 tsf;
+	/** Time of the last update (i.e., Beacon or Probe Response RX) */
+	struct os_reltime last_update;
+	/** Estimated throughput in kbps */
+	unsigned int est_throughput;
+	/** Signal-to-noise ratio in dB */
+	int snr;
+	/** ANQP data */
+	struct wpa_bss_anqp *anqp;
+	/** Length of the following IE field in octets (from Probe Response) */
+	size_t ie_len;
+	/** Length of the following Beacon IE field in octets */
+	size_t beacon_ie_len;
+	/* followed by ie_len octets of IEs */
+	/* followed by beacon_ie_len octets of IEs */
+};
+
+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_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);
+void wpa_bss_deinit(struct wpa_supplicant *wpa_s);
+void wpa_bss_flush(struct wpa_supplicant *wpa_s);
+void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age);
+struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			     const u8 *ssid, size_t ssid_len);
+struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
+				   const u8 *bssid);
+struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s,
+					  const u8 *bssid);
+struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
+					  const u8 *dev_addr);
+struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id);
+struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
+				      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,
+						   u32 vendor_type);
+int wpa_bss_get_max_rate(const struct wpa_bss *bss);
+int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates);
+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;
+}
+
+static inline void wpa_bss_update_level(struct wpa_bss *bss, int new_level)
+{
+	if (bss != NULL && new_level < 0)
+		bss->level = new_level;
+}
+
+#endif /* BSS_H */
diff --git a/hostap/wpa_supplicant/config.c b/hostap/wpa_supplicant/config.c
new file mode 100644
index 0000000..b1adab7
--- /dev/null
+++ b/hostap/wpa_supplicant/config.c
@@ -0,0 +1,4357 @@
+/*
+ * WPA Supplicant / Configuration parser and common functions
+ * Copyright (c) 2003-2015, 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 "utils/uuid.h"
+#include "utils/ip_addr.h"
+#include "crypto/sha1.h"
+#include "rsn_supp/wpa.h"
+#include "eap_peer/eap.h"
+#include "p2p/p2p.h"
+#include "fst/fst.h"
+#include "config.h"
+
+
+#if !defined(CONFIG_CTRL_IFACE) && defined(CONFIG_NO_CONFIG_WRITE)
+#define NO_CONFIG_WRITE
+#endif
+
+/*
+ * Structure for network configuration parsing. This data is used to implement
+ * a generic parser for each network block variable. The table of configuration
+ * variables is defined below in this file (ssid_fields[]).
+ */
+struct parse_data {
+	/* Configuration variable name */
+	char *name;
+
+	/* Parser function for this variable */
+	int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid,
+		      int line, const char *value);
+
+#ifndef NO_CONFIG_WRITE
+	/* Writer function (i.e., to get the variable in text format from
+	 * internal presentation). */
+	char * (*writer)(const struct parse_data *data, struct wpa_ssid *ssid);
+#endif /* NO_CONFIG_WRITE */
+
+	/* Variable specific parameters for the parser. */
+	void *param1, *param2, *param3, *param4;
+
+	/* 0 = this variable can be included in debug output and ctrl_iface
+	 * 1 = this variable contains key/private data and it must not be
+	 *     included in debug output unless explicitly requested. In
+	 *     addition, this variable will not be readable through the
+	 *     ctrl_iface.
+	 */
+	int key_data;
+};
+
+
+static int wpa_config_parse_str(const struct parse_data *data,
+				struct wpa_ssid *ssid,
+				int line, const char *value)
+{
+	size_t res_len, *dst_len;
+	char **dst, *tmp;
+
+	if (os_strcmp(value, "NULL") == 0) {
+		wpa_printf(MSG_DEBUG, "Unset configuration string '%s'",
+			   data->name);
+		tmp = NULL;
+		res_len = 0;
+		goto set;
+	}
+
+	tmp = wpa_config_parse_string(value, &res_len);
+	if (tmp == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: failed to parse %s '%s'.",
+			   line, data->name,
+			   data->key_data ? "[KEY DATA REMOVED]" : value);
+		return -1;
+	}
+
+	if (data->key_data) {
+		wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
+				      (u8 *) tmp, res_len);
+	} else {
+		wpa_hexdump_ascii(MSG_MSGDUMP, data->name,
+				  (u8 *) tmp, res_len);
+	}
+
+	if (data->param3 && res_len < (size_t) data->param3) {
+		wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
+			   "min_len=%ld)", line, data->name,
+			   (unsigned long) res_len, (long) data->param3);
+		os_free(tmp);
+		return -1;
+	}
+
+	if (data->param4 && res_len > (size_t) data->param4) {
+		wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
+			   "max_len=%ld)", line, data->name,
+			   (unsigned long) res_len, (long) data->param4);
+		os_free(tmp);
+		return -1;
+	}
+
+set:
+	dst = (char **) (((u8 *) ssid) + (long) data->param1);
+	dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2);
+	os_free(*dst);
+	*dst = tmp;
+	if (data->param2)
+		*dst_len = res_len;
+
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_string_ascii(const u8 *value, size_t len)
+{
+	char *buf;
+
+	buf = os_malloc(len + 3);
+	if (buf == NULL)
+		return NULL;
+	buf[0] = '"';
+	os_memcpy(buf + 1, value, len);
+	buf[len + 1] = '"';
+	buf[len + 2] = '\0';
+
+	return buf;
+}
+
+
+static char * wpa_config_write_string_hex(const u8 *value, size_t len)
+{
+	char *buf;
+
+	buf = os_zalloc(2 * len + 1);
+	if (buf == NULL)
+		return NULL;
+	wpa_snprintf_hex(buf, 2 * len + 1, value, len);
+
+	return buf;
+}
+
+
+static char * wpa_config_write_string(const u8 *value, size_t len)
+{
+	if (value == NULL)
+		return NULL;
+
+	if (is_hex(value, len))
+		return wpa_config_write_string_hex(value, len);
+	else
+		return wpa_config_write_string_ascii(value, len);
+}
+
+
+static char * wpa_config_write_str(const struct parse_data *data,
+				   struct wpa_ssid *ssid)
+{
+	size_t len;
+	char **src;
+
+	src = (char **) (((u8 *) ssid) + (long) data->param1);
+	if (*src == NULL)
+		return NULL;
+
+	if (data->param2)
+		len = *((size_t *) (((u8 *) ssid) + (long) data->param2));
+	else
+		len = os_strlen(*src);
+
+	return wpa_config_write_string((const u8 *) *src, len);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_int(const struct parse_data *data,
+				struct wpa_ssid *ssid,
+				int line, const char *value)
+{
+	int val, *dst;
+	char *end;
+
+	dst = (int *) (((u8 *) ssid) + (long) data->param1);
+	val = strtol(value, &end, 0);
+	if (*end) {
+		wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"",
+			   line, value);
+		return -1;
+	}
+	*dst = val;
+	wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
+
+	if (data->param3 && *dst < (long) data->param3) {
+		wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+			   "min_value=%ld)", line, data->name, *dst,
+			   (long) data->param3);
+		*dst = (long) data->param3;
+		return -1;
+	}
+
+	if (data->param4 && *dst > (long) data->param4) {
+		wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+			   "max_value=%ld)", line, data->name, *dst,
+			   (long) data->param4);
+		*dst = (long) data->param4;
+		return -1;
+	}
+
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_int(const struct parse_data *data,
+				   struct wpa_ssid *ssid)
+{
+	int *src, res;
+	char *value;
+
+	src = (int *) (((u8 *) ssid) + (long) data->param1);
+
+	value = os_malloc(20);
+	if (value == NULL)
+		return NULL;
+	res = os_snprintf(value, 20, "%d", *src);
+	if (os_snprintf_error(20, res)) {
+		os_free(value);
+		return NULL;
+	}
+	value[20 - 1] = '\0';
+	return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_addr_list(const struct parse_data *data,
+				      int line, const char *value,
+				      u8 **list, size_t *num, char *name,
+				      u8 abort_on_error, u8 masked)
+{
+	const char *pos;
+	u8 *buf, *n, addr[2 * ETH_ALEN];
+	size_t count;
+
+	buf = NULL;
+	count = 0;
+
+	pos = value;
+	while (pos && *pos) {
+		while (*pos == ' ')
+			pos++;
+
+		if (hwaddr_masked_aton(pos, addr, &addr[ETH_ALEN], masked)) {
+			if (abort_on_error || count == 0) {
+				wpa_printf(MSG_ERROR,
+					   "Line %d: Invalid %s address '%s'",
+					   line, name, value);
+				os_free(buf);
+				return -1;
+			}
+			/* continue anyway since this could have been from a
+			 * truncated configuration file line */
+			wpa_printf(MSG_INFO,
+				   "Line %d: Ignore likely truncated %s address '%s'",
+				   line, name, pos);
+		} else {
+			n = os_realloc_array(buf, count + 1, 2 * ETH_ALEN);
+			if (n == NULL) {
+				os_free(buf);
+				return -1;
+			}
+			buf = n;
+			os_memmove(buf + 2 * ETH_ALEN, buf,
+				   count * 2 * ETH_ALEN);
+			os_memcpy(buf, addr, 2 * ETH_ALEN);
+			count++;
+			wpa_printf(MSG_MSGDUMP,
+				   "%s: addr=" MACSTR " mask=" MACSTR,
+				   name, MAC2STR(addr),
+				   MAC2STR(&addr[ETH_ALEN]));
+		}
+
+		pos = os_strchr(pos, ' ');
+	}
+
+	os_free(*list);
+	*list = buf;
+	*num = count;
+
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_addr_list(const struct parse_data *data,
+					 const u8 *list, size_t num, char *name)
+{
+	char *value, *end, *pos;
+	int res;
+	size_t i;
+
+	if (list == NULL || num == 0)
+		return NULL;
+
+	value = os_malloc(2 * 20 * num);
+	if (value == NULL)
+		return NULL;
+	pos = value;
+	end = value + 2 * 20 * num;
+
+	for (i = num; i > 0; i--) {
+		const u8 *a = list + (i - 1) * 2 * ETH_ALEN;
+		const u8 *m = a + ETH_ALEN;
+
+		if (i < num)
+			*pos++ = ' ';
+		res = hwaddr_mask_txt(pos, end - pos, a, m);
+		if (res < 0) {
+			os_free(value);
+			return NULL;
+		}
+		pos += res;
+	}
+
+	return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+static int wpa_config_parse_bssid(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) {
+		ssid->bssid_set = 0;
+		wpa_printf(MSG_MSGDUMP, "BSSID any");
+		return 0;
+	}
+	if (hwaddr_aton(value, ssid->bssid)) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.",
+			   line, value);
+		return -1;
+	}
+	ssid->bssid_set = 1;
+	wpa_hexdump(MSG_MSGDUMP, "BSSID", ssid->bssid, ETH_ALEN);
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid(const struct parse_data *data,
+				     struct wpa_ssid *ssid)
+{
+	char *value;
+	int res;
+
+	if (!ssid->bssid_set)
+		return NULL;
+
+	value = os_malloc(20);
+	if (value == NULL)
+		return NULL;
+	res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid));
+	if (os_snprintf_error(20, res)) {
+		os_free(value);
+		return NULL;
+	}
+	value[20 - 1] = '\0';
+	return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_bssid_blacklist(const struct parse_data *data,
+					    struct wpa_ssid *ssid, int line,
+					    const char *value)
+{
+	return wpa_config_parse_addr_list(data, line, value,
+					  &ssid->bssid_blacklist,
+					  &ssid->num_bssid_blacklist,
+					  "bssid_blacklist", 1, 1);
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid_blacklist(const struct parse_data *data,
+					       struct wpa_ssid *ssid)
+{
+	return wpa_config_write_addr_list(data, ssid->bssid_blacklist,
+					  ssid->num_bssid_blacklist,
+					  "bssid_blacklist");
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_bssid_whitelist(const struct parse_data *data,
+					    struct wpa_ssid *ssid, int line,
+					    const char *value)
+{
+	return wpa_config_parse_addr_list(data, line, value,
+					  &ssid->bssid_whitelist,
+					  &ssid->num_bssid_whitelist,
+					  "bssid_whitelist", 1, 1);
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid_whitelist(const struct parse_data *data,
+					       struct wpa_ssid *ssid)
+{
+	return wpa_config_write_addr_list(data, ssid->bssid_whitelist,
+					  ssid->num_bssid_whitelist,
+					  "bssid_whitelist");
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_psk(const struct parse_data *data,
+				struct wpa_ssid *ssid, int line,
+				const char *value)
+{
+#ifdef CONFIG_EXT_PASSWORD
+	if (os_strncmp(value, "ext:", 4) == 0) {
+		str_clear_free(ssid->passphrase);
+		ssid->passphrase = NULL;
+		ssid->psk_set = 0;
+		os_free(ssid->ext_psk);
+		ssid->ext_psk = os_strdup(value + 4);
+		if (ssid->ext_psk == NULL)
+			return -1;
+		wpa_printf(MSG_DEBUG, "PSK: External password '%s'",
+			   ssid->ext_psk);
+		return 0;
+	}
+#endif /* CONFIG_EXT_PASSWORD */
+
+	if (*value == '"') {
+#ifndef CONFIG_NO_PBKDF2
+		const char *pos;
+		size_t len;
+
+		value++;
+		pos = os_strrchr(value, '"');
+		if (pos)
+			len = pos - value;
+		else
+			len = os_strlen(value);
+		if (len < 8 || len > 63) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid passphrase "
+				   "length %lu (expected: 8..63) '%s'.",
+				   line, (unsigned long) len, value);
+			return -1;
+		}
+		wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)",
+				      (u8 *) value, len);
+		if (ssid->passphrase && os_strlen(ssid->passphrase) == len &&
+		    os_memcmp(ssid->passphrase, value, len) == 0)
+			return 0;
+		ssid->psk_set = 0;
+		str_clear_free(ssid->passphrase);
+		ssid->passphrase = dup_binstr(value, len);
+		if (ssid->passphrase == NULL)
+			return -1;
+		return 0;
+#else /* CONFIG_NO_PBKDF2 */
+		wpa_printf(MSG_ERROR, "Line %d: ASCII passphrase not "
+			   "supported.", line);
+		return -1;
+#endif /* CONFIG_NO_PBKDF2 */
+	}
+
+	if (hexstr2bin(value, ssid->psk, PMK_LEN) ||
+	    value[PMK_LEN * 2] != '\0') {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
+			   line, value);
+		return -1;
+	}
+
+	str_clear_free(ssid->passphrase);
+	ssid->passphrase = NULL;
+
+	ssid->psk_set = 1;
+	wpa_hexdump_key(MSG_MSGDUMP, "PSK", ssid->psk, PMK_LEN);
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_psk(const struct parse_data *data,
+				   struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_EXT_PASSWORD
+	if (ssid->ext_psk) {
+		size_t len = 4 + os_strlen(ssid->ext_psk) + 1;
+		char *buf = os_malloc(len);
+		int res;
+
+		if (buf == NULL)
+			return NULL;
+		res = os_snprintf(buf, len, "ext:%s", ssid->ext_psk);
+		if (os_snprintf_error(len, res)) {
+			os_free(buf);
+			buf = NULL;
+		}
+		return buf;
+	}
+#endif /* CONFIG_EXT_PASSWORD */
+
+	if (ssid->passphrase)
+		return wpa_config_write_string_ascii(
+			(const u8 *) ssid->passphrase,
+			os_strlen(ssid->passphrase));
+
+	if (ssid->psk_set)
+		return wpa_config_write_string_hex(ssid->psk, PMK_LEN);
+
+	return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_proto(const struct parse_data *data,
+				  struct wpa_ssid *ssid, int line,
+				  const char *value)
+{
+	int val = 0, last, errors = 0;
+	char *start, *end, *buf;
+
+	buf = os_strdup(value);
+	if (buf == NULL)
+		return -1;
+	start = buf;
+
+	while (*start != '\0') {
+		while (*start == ' ' || *start == '\t')
+			start++;
+		if (*start == '\0')
+			break;
+		end = start;
+		while (*end != ' ' && *end != '\t' && *end != '\0')
+			end++;
+		last = *end == '\0';
+		*end = '\0';
+		if (os_strcmp(start, "WPA") == 0)
+			val |= WPA_PROTO_WPA;
+		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);
+			errors++;
+		}
+
+		if (last)
+			break;
+		start = end + 1;
+	}
+	os_free(buf);
+
+	if (val == 0) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: no proto values configured.", line);
+		errors++;
+	}
+
+	wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val);
+	ssid->proto = val;
+	return errors ? -1 : 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_proto(const struct parse_data *data,
+				     struct wpa_ssid *ssid)
+{
+	int ret;
+	char *buf, *pos, *end;
+
+	pos = buf = os_zalloc(20);
+	if (buf == NULL)
+		return NULL;
+	end = buf + 20;
+
+	if (ssid->proto & WPA_PROTO_WPA) {
+		ret = os_snprintf(pos, end - pos, "%sWPA",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return buf;
+		pos += ret;
+	}
+
+	if (ssid->proto & WPA_PROTO_RSN) {
+		ret = os_snprintf(pos, end - pos, "%sRSN",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return buf;
+		pos += ret;
+	}
+
+	if (ssid->proto & WPA_PROTO_OSEN) {
+		ret = os_snprintf(pos, end - pos, "%sOSEN",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return buf;
+		pos += ret;
+	}
+
+	if (pos == buf) {
+		os_free(buf);
+		buf = NULL;
+	}
+
+	return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_key_mgmt(const struct parse_data *data,
+				     struct wpa_ssid *ssid, int line,
+				     const char *value)
+{
+	int val = 0, last, errors = 0;
+	char *start, *end, *buf;
+
+	buf = os_strdup(value);
+	if (buf == NULL)
+		return -1;
+	start = buf;
+
+	while (*start != '\0') {
+		while (*start == ' ' || *start == '\t')
+			start++;
+		if (*start == '\0')
+			break;
+		end = start;
+		while (*end != ' ' && *end != '\t' && *end != '\0')
+			end++;
+		last = *end == '\0';
+		*end = '\0';
+		if (os_strcmp(start, "WPA-PSK") == 0)
+			val |= WPA_KEY_MGMT_PSK;
+		else if (os_strcmp(start, "WPA-EAP") == 0)
+			val |= WPA_KEY_MGMT_IEEE8021X;
+		else if (os_strcmp(start, "IEEE8021X") == 0)
+			val |= WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+		else if (os_strcmp(start, "NONE") == 0)
+			val |= WPA_KEY_MGMT_NONE;
+		else if (os_strcmp(start, "WPA-NONE") == 0)
+			val |= WPA_KEY_MGMT_WPA_NONE;
+#ifdef CONFIG_IEEE80211R
+		else if (os_strcmp(start, "FT-PSK") == 0)
+			val |= WPA_KEY_MGMT_FT_PSK;
+		else if (os_strcmp(start, "FT-EAP") == 0)
+			val |= WPA_KEY_MGMT_FT_IEEE8021X;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+		else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
+			val |= WPA_KEY_MGMT_PSK_SHA256;
+		else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
+			val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_WPS
+		else if (os_strcmp(start, "WPS") == 0)
+			val |= WPA_KEY_MGMT_WPS;
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_SAE
+		else if (os_strcmp(start, "SAE") == 0)
+			val |= WPA_KEY_MGMT_SAE;
+		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 */
+#ifdef CONFIG_SUITEB
+		else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0)
+			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+		else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
+			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+#endif /* CONFIG_SUITEB192 */
+		else {
+			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
+				   line, start);
+			errors++;
+		}
+
+		if (last)
+			break;
+		start = end + 1;
+	}
+	os_free(buf);
+
+	if (val == 0) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: no key_mgmt values configured.", line);
+		errors++;
+	}
+
+	wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val);
+	ssid->key_mgmt = val;
+	return errors ? -1 : 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_key_mgmt(const struct parse_data *data,
+					struct wpa_ssid *ssid)
+{
+	char *buf, *pos, *end;
+	int ret;
+
+	pos = buf = os_zalloc(100);
+	if (buf == NULL)
+		return NULL;
+	end = buf + 100;
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
+		ret = os_snprintf(pos, end - pos, "%sWPA-PSK",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+		ret = os_snprintf(pos, end - pos, "%sWPA-EAP",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+		ret = os_snprintf(pos, end - pos, "%sIEEE8021X",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_NONE) {
+		ret = os_snprintf(pos, end - pos, "%sNONE",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
+		ret = os_snprintf(pos, end - pos, "%sWPA-NONE",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
+#ifdef CONFIG_IEEE80211R
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+		ret = os_snprintf(pos, end - pos, "%sFT-PSK",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+		ret = os_snprintf(pos, end - pos, "%sFT-EAP",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211W
+	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+		ret = os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+		ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_WPS
+	if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+		ret = os_snprintf(pos, end - pos, "%sWPS",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_SAE
+	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+		ret = os_snprintf(pos, end - pos, "%sSAE",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+		ret = os_snprintf(pos, end - pos, "%sFT-SAE",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_HS20
+	if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN) {
+		ret = os_snprintf(pos, end - pos, "%sOSEN",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_SUITEB
+	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+		ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#endif /* CONFIG_SUITEB */
+
+#ifdef CONFIG_SUITEB192
+	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B-192",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#endif /* CONFIG_SUITEB192 */
+
+	if (pos == buf) {
+		os_free(buf);
+		buf = NULL;
+	}
+
+	return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_cipher(int line, const char *value)
+{
+	int val = wpa_parse_cipher(value);
+	if (val < 0) {
+		wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
+			   line, value);
+		return -1;
+	}
+	if (val == 0) {
+		wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.",
+			   line);
+		return -1;
+	}
+	return val;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_cipher(int cipher)
+{
+	char *buf = os_zalloc(50);
+	if (buf == NULL)
+		return NULL;
+
+	if (wpa_write_ciphers(buf, buf + 50, cipher, " ") < 0) {
+		os_free(buf);
+		return NULL;
+	}
+
+	return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_pairwise(const struct parse_data *data,
+				     struct wpa_ssid *ssid, int line,
+				     const char *value)
+{
+	int val;
+	val = wpa_config_parse_cipher(line, value);
+	if (val == -1)
+		return -1;
+	if (val & ~WPA_ALLOWED_PAIRWISE_CIPHERS) {
+		wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher "
+			   "(0x%x).", line, val);
+		return -1;
+	}
+
+	wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val);
+	ssid->pairwise_cipher = val;
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_pairwise(const struct parse_data *data,
+					struct wpa_ssid *ssid)
+{
+	return wpa_config_write_cipher(ssid->pairwise_cipher);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_group(const struct parse_data *data,
+				  struct wpa_ssid *ssid, int line,
+				  const char *value)
+{
+	int val;
+	val = wpa_config_parse_cipher(line, value);
+	if (val == -1)
+		return -1;
+
+	/*
+	 * Backwards compatibility - filter out WEP ciphers that were previously
+	 * allowed.
+	 */
+	val &= ~(WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40);
+
+	if (val & ~WPA_ALLOWED_GROUP_CIPHERS) {
+		wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher "
+			   "(0x%x).", line, val);
+		return -1;
+	}
+
+	wpa_printf(MSG_MSGDUMP, "group: 0x%x", val);
+	ssid->group_cipher = val;
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_group(const struct parse_data *data,
+				     struct wpa_ssid *ssid)
+{
+	return wpa_config_write_cipher(ssid->group_cipher);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_auth_alg(const struct parse_data *data,
+				     struct wpa_ssid *ssid, int line,
+				     const char *value)
+{
+	int val = 0, last, errors = 0;
+	char *start, *end, *buf;
+
+	buf = os_strdup(value);
+	if (buf == NULL)
+		return -1;
+	start = buf;
+
+	while (*start != '\0') {
+		while (*start == ' ' || *start == '\t')
+			start++;
+		if (*start == '\0')
+			break;
+		end = start;
+		while (*end != ' ' && *end != '\t' && *end != '\0')
+			end++;
+		last = *end == '\0';
+		*end = '\0';
+		if (os_strcmp(start, "OPEN") == 0)
+			val |= WPA_AUTH_ALG_OPEN;
+		else if (os_strcmp(start, "SHARED") == 0)
+			val |= WPA_AUTH_ALG_SHARED;
+		else if (os_strcmp(start, "LEAP") == 0)
+			val |= WPA_AUTH_ALG_LEAP;
+		else {
+			wpa_printf(MSG_ERROR, "Line %d: invalid auth_alg '%s'",
+				   line, start);
+			errors++;
+		}
+
+		if (last)
+			break;
+		start = end + 1;
+	}
+	os_free(buf);
+
+	if (val == 0) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: no auth_alg values configured.", line);
+		errors++;
+	}
+
+	wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val);
+	ssid->auth_alg = val;
+	return errors ? -1 : 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_auth_alg(const struct parse_data *data,
+					struct wpa_ssid *ssid)
+{
+	char *buf, *pos, *end;
+	int ret;
+
+	pos = buf = os_zalloc(30);
+	if (buf == NULL)
+		return NULL;
+	end = buf + 30;
+
+	if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) {
+		ret = os_snprintf(pos, end - pos, "%sOPEN",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
+	if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) {
+		ret = os_snprintf(pos, end - pos, "%sSHARED",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
+	if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) {
+		ret = os_snprintf(pos, end - pos, "%sLEAP",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
+	if (pos == buf) {
+		os_free(buf);
+		buf = NULL;
+	}
+
+	return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int * wpa_config_parse_int_array(const char *value)
+{
+	int *freqs;
+	size_t used, len;
+	const char *pos;
+
+	used = 0;
+	len = 10;
+	freqs = os_calloc(len + 1, sizeof(int));
+	if (freqs == NULL)
+		return NULL;
+
+	pos = value;
+	while (pos) {
+		while (*pos == ' ')
+			pos++;
+		if (used == len) {
+			int *n;
+			size_t i;
+			n = os_realloc_array(freqs, len * 2 + 1, sizeof(int));
+			if (n == NULL) {
+				os_free(freqs);
+				return NULL;
+			}
+			for (i = len; i <= len * 2; i++)
+				n[i] = 0;
+			freqs = n;
+			len *= 2;
+		}
+
+		freqs[used] = atoi(pos);
+		if (freqs[used] == 0)
+			break;
+		used++;
+		pos = os_strchr(pos + 1, ' ');
+	}
+
+	return freqs;
+}
+
+
+static int wpa_config_parse_scan_freq(const struct parse_data *data,
+				      struct wpa_ssid *ssid, int line,
+				      const char *value)
+{
+	int *freqs;
+
+	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;
+
+	return 0;
+}
+
+
+static int wpa_config_parse_freq_list(const struct parse_data *data,
+				      struct wpa_ssid *ssid, int line,
+				      const char *value)
+{
+	int *freqs;
+
+	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;
+
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_freqs(const struct parse_data *data,
+				     const int *freqs)
+{
+	char *buf, *pos, *end;
+	int i, ret;
+	size_t count;
+
+	if (freqs == NULL)
+		return NULL;
+
+	count = 0;
+	for (i = 0; freqs[i]; i++)
+		count++;
+
+	pos = buf = os_zalloc(10 * count + 1);
+	if (buf == NULL)
+		return NULL;
+	end = buf + 10 * count + 1;
+
+	for (i = 0; freqs[i]; i++) {
+		ret = os_snprintf(pos, end - pos, "%s%u",
+				  i == 0 ? "" : " ", freqs[i]);
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
+	return buf;
+}
+
+
+static char * wpa_config_write_scan_freq(const struct parse_data *data,
+					 struct wpa_ssid *ssid)
+{
+	return wpa_config_write_freqs(data, ssid->scan_freq);
+}
+
+
+static char * wpa_config_write_freq_list(const struct parse_data *data,
+					 struct wpa_ssid *ssid)
+{
+	return wpa_config_write_freqs(data, ssid->freq_list);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+#ifdef IEEE8021X_EAPOL
+static int wpa_config_parse_eap(const struct parse_data *data,
+				struct wpa_ssid *ssid, int line,
+				const char *value)
+{
+	int last, errors = 0;
+	char *start, *end, *buf;
+	struct eap_method_type *methods = NULL, *tmp;
+	size_t num_methods = 0;
+
+	buf = os_strdup(value);
+	if (buf == NULL)
+		return -1;
+	start = buf;
+
+	while (*start != '\0') {
+		while (*start == ' ' || *start == '\t')
+			start++;
+		if (*start == '\0')
+			break;
+		end = start;
+		while (*end != ' ' && *end != '\t' && *end != '\0')
+			end++;
+		last = *end == '\0';
+		*end = '\0';
+		tmp = methods;
+		methods = os_realloc_array(methods, num_methods + 1,
+					   sizeof(*methods));
+		if (methods == NULL) {
+			os_free(tmp);
+			os_free(buf);
+			return -1;
+		}
+		methods[num_methods].method = eap_peer_get_type(
+			start, &methods[num_methods].vendor);
+		if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
+		    methods[num_methods].method == EAP_TYPE_NONE) {
+			wpa_printf(MSG_ERROR, "Line %d: unknown EAP method "
+				   "'%s'", line, start);
+			wpa_printf(MSG_ERROR, "You may need to add support for"
+				   " this EAP method during wpa_supplicant\n"
+				   "build time configuration.\n"
+				   "See README for more information.");
+			errors++;
+		} else if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
+			   methods[num_methods].method == EAP_TYPE_LEAP)
+			ssid->leap++;
+		else
+			ssid->non_leap++;
+		num_methods++;
+		if (last)
+			break;
+		start = end + 1;
+	}
+	os_free(buf);
+
+	tmp = methods;
+	methods = os_realloc_array(methods, num_methods + 1, sizeof(*methods));
+	if (methods == NULL) {
+		os_free(tmp);
+		return -1;
+	}
+	methods[num_methods].vendor = EAP_VENDOR_IETF;
+	methods[num_methods].method = EAP_TYPE_NONE;
+	num_methods++;
+
+	wpa_hexdump(MSG_MSGDUMP, "eap methods",
+		    (u8 *) methods, num_methods * sizeof(*methods));
+	os_free(ssid->eap.eap_methods);
+	ssid->eap.eap_methods = methods;
+	return errors ? -1 : 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_eap(const struct parse_data *data,
+				   struct wpa_ssid *ssid)
+{
+	int i, ret;
+	char *buf, *pos, *end;
+	const struct eap_method_type *eap_methods = ssid->eap.eap_methods;
+	const char *name;
+
+	if (eap_methods == NULL)
+		return NULL;
+
+	pos = buf = os_zalloc(100);
+	if (buf == NULL)
+		return NULL;
+	end = buf + 100;
+
+	for (i = 0; eap_methods[i].vendor != EAP_VENDOR_IETF ||
+		     eap_methods[i].method != EAP_TYPE_NONE; i++) {
+		name = eap_get_name(eap_methods[i].vendor,
+				    eap_methods[i].method);
+		if (name) {
+			ret = os_snprintf(pos, end - pos, "%s%s",
+					  pos == buf ? "" : " ", name);
+			if (os_snprintf_error(end - pos, ret))
+				break;
+			pos += ret;
+		}
+	}
+
+	end[-1] = '\0';
+
+	return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_password(const struct parse_data *data,
+				     struct wpa_ssid *ssid, int line,
+				     const char *value)
+{
+	u8 *hash;
+
+	if (os_strcmp(value, "NULL") == 0) {
+		wpa_printf(MSG_DEBUG, "Unset configuration string 'password'");
+		bin_clear_free(ssid->eap.password, ssid->eap.password_len);
+		ssid->eap.password = NULL;
+		ssid->eap.password_len = 0;
+		return 0;
+	}
+
+#ifdef CONFIG_EXT_PASSWORD
+	if (os_strncmp(value, "ext:", 4) == 0) {
+		char *name = os_strdup(value + 4);
+		if (name == NULL)
+			return -1;
+		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;
+		ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_PASSWORD;
+		return 0;
+	}
+#endif /* CONFIG_EXT_PASSWORD */
+
+	if (os_strncmp(value, "hash:", 5) != 0) {
+		char *tmp;
+		size_t res_len;
+
+		tmp = wpa_config_parse_string(value, &res_len);
+		if (tmp == NULL) {
+			wpa_printf(MSG_ERROR, "Line %d: failed to parse "
+				   "password.", line);
+			return -1;
+		}
+		wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
+				      (u8 *) tmp, res_len);
+
+		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;
+		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
+
+		return 0;
+	}
+
+
+	/* NtPasswordHash: hash:<32 hex digits> */
+	if (os_strlen(value + 5) != 2 * 16) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid password hash length "
+			   "(expected 32 hex digits)", line);
+		return -1;
+	}
+
+	hash = os_malloc(16);
+	if (hash == NULL)
+		return -1;
+
+	if (hexstr2bin(value + 5, hash, 16)) {
+		os_free(hash);
+		wpa_printf(MSG_ERROR, "Line %d: Invalid password hash", line);
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
+
+	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;
+	ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
+
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_password(const struct parse_data *data,
+					struct wpa_ssid *ssid)
+{
+	char *buf;
+
+	if (ssid->eap.password == NULL)
+		return NULL;
+
+#ifdef CONFIG_EXT_PASSWORD
+	if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+		buf = os_zalloc(4 + ssid->eap.password_len + 1);
+		if (buf == NULL)
+			return NULL;
+		os_memcpy(buf, "ext:", 4);
+		os_memcpy(buf + 4, ssid->eap.password, ssid->eap.password_len);
+		return buf;
+	}
+#endif /* CONFIG_EXT_PASSWORD */
+
+	if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) {
+		return wpa_config_write_string(
+			ssid->eap.password, ssid->eap.password_len);
+	}
+
+	buf = os_malloc(5 + 32 + 1);
+	if (buf == NULL)
+		return NULL;
+
+	os_memcpy(buf, "hash:", 5);
+	wpa_snprintf_hex(buf + 5, 32 + 1, ssid->eap.password, 16);
+
+	return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+#endif /* IEEE8021X_EAPOL */
+
+
+static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line,
+				    const char *value, int idx)
+{
+	char *buf, title[20];
+	int res;
+
+	buf = wpa_config_parse_string(value, len);
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key %d '%s'.",
+			   line, idx, value);
+		return -1;
+	}
+	if (*len > MAX_WEP_KEY_LEN) {
+		wpa_printf(MSG_ERROR, "Line %d: Too long WEP key %d '%s'.",
+			   line, idx, value);
+		os_free(buf);
+		return -1;
+	}
+	if (*len && *len != 5 && *len != 13 && *len != 16) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key length %u - "
+			   "this network block will be ignored",
+			   line, (unsigned int) *len);
+	}
+	os_memcpy(key, buf, *len);
+	str_clear_free(buf);
+	res = os_snprintf(title, sizeof(title), "wep_key%d", idx);
+	if (!os_snprintf_error(sizeof(title), res))
+		wpa_hexdump_key(MSG_MSGDUMP, title, key, *len);
+	return 0;
+}
+
+
+static int wpa_config_parse_wep_key0(const struct parse_data *data,
+				     struct wpa_ssid *ssid, int line,
+				     const char *value)
+{
+	return wpa_config_parse_wep_key(ssid->wep_key[0],
+					&ssid->wep_key_len[0], line,
+					value, 0);
+}
+
+
+static int wpa_config_parse_wep_key1(const struct parse_data *data,
+				     struct wpa_ssid *ssid, int line,
+				     const char *value)
+{
+	return wpa_config_parse_wep_key(ssid->wep_key[1],
+					&ssid->wep_key_len[1], line,
+					value, 1);
+}
+
+
+static int wpa_config_parse_wep_key2(const struct parse_data *data,
+				     struct wpa_ssid *ssid, int line,
+				     const char *value)
+{
+	return wpa_config_parse_wep_key(ssid->wep_key[2],
+					&ssid->wep_key_len[2], line,
+					value, 2);
+}
+
+
+static int wpa_config_parse_wep_key3(const struct parse_data *data,
+				     struct wpa_ssid *ssid, int line,
+				     const char *value)
+{
+	return wpa_config_parse_wep_key(ssid->wep_key[3],
+					&ssid->wep_key_len[3], line,
+					value, 3);
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_wep_key(struct wpa_ssid *ssid, int idx)
+{
+	if (ssid->wep_key_len[idx] == 0)
+		return NULL;
+	return wpa_config_write_string(ssid->wep_key[idx],
+				       ssid->wep_key_len[idx]);
+}
+
+
+static char * wpa_config_write_wep_key0(const struct parse_data *data,
+					struct wpa_ssid *ssid)
+{
+	return wpa_config_write_wep_key(ssid, 0);
+}
+
+
+static char * wpa_config_write_wep_key1(const struct parse_data *data,
+					struct wpa_ssid *ssid)
+{
+	return wpa_config_write_wep_key(ssid, 1);
+}
+
+
+static char * wpa_config_write_wep_key2(const struct parse_data *data,
+					struct wpa_ssid *ssid)
+{
+	return wpa_config_write_wep_key(ssid, 2);
+}
+
+
+static char * wpa_config_write_wep_key3(const struct parse_data *data,
+					struct wpa_ssid *ssid)
+{
+	return wpa_config_write_wep_key(ssid, 3);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+#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 (os_snprintf_error(20, res)) {
+		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)
+{
+	return wpa_config_parse_addr_list(data, line, value,
+					  &ssid->p2p_client_list,
+					  &ssid->num_p2p_clients,
+					  "p2p_client_list", 0, 0);
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_p2p_client_list(const struct parse_data *data,
+					       struct wpa_ssid *ssid)
+{
+	return wpa_config_write_addr_list(data, ssid->p2p_client_list,
+					  ssid->num_p2p_clients,
+					  "p2p_client_list");
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_psk_list(const struct parse_data *data,
+				     struct wpa_ssid *ssid, int line,
+				     const char *value)
+{
+	struct psk_list_entry *p;
+	const char *pos;
+
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL)
+		return -1;
+
+	pos = value;
+	if (os_strncmp(pos, "P2P-", 4) == 0) {
+		p->p2p = 1;
+		pos += 4;
+	}
+
+	if (hwaddr_aton(pos, p->addr)) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list address '%s'",
+			   line, pos);
+		os_free(p);
+		return -1;
+	}
+	pos += 17;
+	if (*pos != '-') {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list '%s'",
+			   line, pos);
+		os_free(p);
+		return -1;
+	}
+	pos++;
+
+	if (hexstr2bin(pos, p->psk, PMK_LEN) || pos[PMK_LEN * 2] != '\0') {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list PSK '%s'",
+			   line, pos);
+		os_free(p);
+		return -1;
+	}
+
+	dl_list_add(&ssid->psk_list, &p->list);
+
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_psk_list(const struct parse_data *data,
+					struct wpa_ssid *ssid)
+{
+	return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_MESH
+
+static int wpa_config_parse_mesh_basic_rates(const struct parse_data *data,
+					     struct wpa_ssid *ssid, int line,
+					     const char *value)
+{
+	int *rates = wpa_config_parse_int_array(value);
+
+	if (rates == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid mesh_basic_rates '%s'",
+			   line, value);
+		return -1;
+	}
+	if (rates[0] == 0) {
+		os_free(rates);
+		rates = NULL;
+	}
+
+	os_free(ssid->mesh_basic_rates);
+	ssid->mesh_basic_rates = rates;
+
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+
+static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data,
+						struct wpa_ssid *ssid)
+{
+	return wpa_config_write_freqs(data, ssid->mesh_basic_rates);
+}
+
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_MESH */
+
+
+/* Helper macros for network block parser */
+
+#ifdef OFFSET
+#undef OFFSET
+#endif /* OFFSET */
+/* OFFSET: Get offset of a variable within the wpa_ssid structure */
+#define OFFSET(v) ((void *) &((struct wpa_ssid *) 0)->v)
+
+/* STR: Define a string variable for an ASCII string; f = field name */
+#ifdef NO_CONFIG_WRITE
+#define _STR(f) #f, wpa_config_parse_str, OFFSET(f)
+#define _STRe(f) #f, wpa_config_parse_str, OFFSET(eap.f)
+#else /* NO_CONFIG_WRITE */
+#define _STR(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(f)
+#define _STRe(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(eap.f)
+#endif /* NO_CONFIG_WRITE */
+#define STR(f) _STR(f), NULL, NULL, NULL, 0
+#define STRe(f) _STRe(f), NULL, NULL, NULL, 0
+#define STR_KEY(f) _STR(f), NULL, NULL, NULL, 1
+#define STR_KEYe(f) _STRe(f), NULL, NULL, NULL, 1
+
+/* STR_LEN: Define a string variable with a separate variable for storing the
+ * data length. Unlike STR(), this can be used to store arbitrary binary data
+ * (i.e., even nul termination character). */
+#define _STR_LEN(f) _STR(f), OFFSET(f ## _len)
+#define _STR_LENe(f) _STRe(f), OFFSET(eap.f ## _len)
+#define STR_LEN(f) _STR_LEN(f), NULL, NULL, 0
+#define STR_LENe(f) _STR_LENe(f), NULL, NULL, 0
+#define STR_LEN_KEY(f) _STR_LEN(f), NULL, NULL, 1
+
+/* STR_RANGE: Like STR_LEN(), but with minimum and maximum allowed length
+ * explicitly specified. */
+#define _STR_RANGE(f, min, max) _STR_LEN(f), (void *) (min), (void *) (max)
+#define STR_RANGE(f, min, max) _STR_RANGE(f, min, max), 0
+#define STR_RANGE_KEY(f, min, max) _STR_RANGE(f, min, max), 1
+
+#ifdef NO_CONFIG_WRITE
+#define _INT(f) #f, wpa_config_parse_int, OFFSET(f), (void *) 0
+#define _INTe(f) #f, wpa_config_parse_int, OFFSET(eap.f), (void *) 0
+#else /* NO_CONFIG_WRITE */
+#define _INT(f) #f, wpa_config_parse_int, wpa_config_write_int, \
+	OFFSET(f), (void *) 0
+#define _INTe(f) #f, wpa_config_parse_int, wpa_config_write_int, \
+	OFFSET(eap.f), (void *) 0
+#endif /* NO_CONFIG_WRITE */
+
+/* INT: Define an integer variable */
+#define INT(f) _INT(f), NULL, NULL, 0
+#define INTe(f) _INTe(f), NULL, NULL, 0
+
+/* INT_RANGE: Define an integer variable with allowed value range */
+#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0
+
+/* FUNC: Define a configuration variable that uses a custom function for
+ * parsing and writing the value. */
+#ifdef NO_CONFIG_WRITE
+#define _FUNC(f) #f, wpa_config_parse_ ## f, NULL, NULL, NULL, NULL
+#else /* NO_CONFIG_WRITE */
+#define _FUNC(f) #f, wpa_config_parse_ ## f, wpa_config_write_ ## f, \
+	NULL, NULL, NULL, NULL
+#endif /* NO_CONFIG_WRITE */
+#define FUNC(f) _FUNC(f), 0
+#define FUNC_KEY(f) _FUNC(f), 1
+
+/*
+ * Table of network configuration variables. This table is used to parse each
+ * network configuration variable, e.g., each line in wpa_supplicant.conf file
+ * that is inside a network block.
+ *
+ * This table is generated using the helper macros defined above and with
+ * generous help from the C pre-processor. The field name is stored as a string
+ * into .name and for STR and INT types, the offset of the target buffer within
+ * struct wpa_ssid is stored in .param1. .param2 (if not NULL) is similar
+ * offset to the field containing the length of the configuration variable.
+ * .param3 and .param4 can be used to mark the allowed range (length for STR
+ * and value for INT).
+ *
+ * For each configuration line in wpa_supplicant.conf, the parser goes through
+ * this table and select the entry that matches with the field name. The parser
+ * function (.parser) is then called to parse the actual value of the field.
+ *
+ * This kind of mechanism makes it easy to add new configuration parameters,
+ * since only one line needs to be added into this table and into the
+ * struct wpa_ssid definition if the new variable is either a string or
+ * integer. More complex types will need to use their own parser and writer
+ * functions.
+ */
+static const struct parse_data ssid_fields[] = {
+	{ STR_RANGE(ssid, 0, SSID_MAX_LEN) },
+	{ INT_RANGE(scan_ssid, 0, 1) },
+	{ FUNC(bssid) },
+	{ FUNC(bssid_blacklist) },
+	{ FUNC(bssid_whitelist) },
+	{ FUNC_KEY(psk) },
+	{ INT(mem_only_psk) },
+	{ FUNC(proto) },
+	{ FUNC(key_mgmt) },
+	{ INT(bg_scan_period) },
+	{ FUNC(pairwise) },
+	{ FUNC(group) },
+	{ FUNC(auth_alg) },
+	{ FUNC(scan_freq) },
+	{ FUNC(freq_list) },
+#ifdef IEEE8021X_EAPOL
+	{ FUNC(eap) },
+	{ STR_LENe(identity) },
+	{ STR_LENe(anonymous_identity) },
+	{ FUNC_KEY(password) },
+	{ STRe(ca_cert) },
+	{ STRe(ca_path) },
+	{ STRe(client_cert) },
+	{ STRe(private_key) },
+	{ STR_KEYe(private_key_passwd) },
+	{ STRe(dh_file) },
+	{ STRe(subject_match) },
+	{ STRe(altsubject_match) },
+	{ STRe(domain_suffix_match) },
+	{ STRe(domain_match) },
+	{ STRe(ca_cert2) },
+	{ STRe(ca_path2) },
+	{ STRe(client_cert2) },
+	{ STRe(private_key2) },
+	{ STR_KEYe(private_key2_passwd) },
+	{ STRe(dh_file2) },
+	{ STRe(subject_match2) },
+	{ STRe(altsubject_match2) },
+	{ STRe(domain_suffix_match2) },
+	{ STRe(domain_match2) },
+	{ STRe(phase1) },
+	{ STRe(phase2) },
+	{ STRe(pcsc) },
+	{ STR_KEYe(pin) },
+	{ STRe(engine_id) },
+	{ STRe(key_id) },
+	{ STRe(cert_id) },
+	{ STRe(ca_cert_id) },
+	{ STR_KEYe(pin2) },
+	{ STRe(engine2_id) },
+	{ STRe(key2_id) },
+	{ STRe(cert2_id) },
+	{ STRe(ca_cert2_id) },
+	{ INTe(engine) },
+	{ INTe(engine2) },
+	{ INT(eapol_flags) },
+	{ INTe(sim_num) },
+	{ STRe(openssl_ciphers) },
+	{ INTe(erp) },
+#endif /* IEEE8021X_EAPOL */
+	{ FUNC_KEY(wep_key0) },
+	{ FUNC_KEY(wep_key1) },
+	{ FUNC_KEY(wep_key2) },
+	{ FUNC_KEY(wep_key3) },
+	{ INT(wep_tx_keyidx) },
+	{ INT(priority) },
+#ifdef IEEE8021X_EAPOL
+	{ INT(eap_workaround) },
+	{ STRe(pac_file) },
+	{ INTe(fragment_size) },
+	{ INTe(ocsp) },
+#endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_MESH
+	{ INT_RANGE(mode, 0, 5) },
+	{ INT_RANGE(no_auto_peer, 0, 1) },
+#else /* CONFIG_MESH */
+	{ INT_RANGE(mode, 0, 4) },
+#endif /* CONFIG_MESH */
+	{ INT_RANGE(proactive_key_caching, 0, 1) },
+	{ INT_RANGE(disabled, 0, 2) },
+	{ STR(id_str) },
+#ifdef CONFIG_IEEE80211W
+	{ INT_RANGE(ieee80211w, 0, 2) },
+#endif /* CONFIG_IEEE80211W */
+	{ INT_RANGE(peerkey, 0, 1) },
+	{ INT_RANGE(mixed_cell, 0, 1) },
+	{ INT_RANGE(frequency, 0, 65000) },
+	{ INT_RANGE(fixed_freq, 0, 1) },
+#ifdef CONFIG_MESH
+	{ FUNC(mesh_basic_rates) },
+	{ INT(dot11MeshMaxRetries) },
+	{ INT(dot11MeshRetryTimeout) },
+	{ INT(dot11MeshConfirmTimeout) },
+	{ INT(dot11MeshHoldingTimeout) },
+#endif /* CONFIG_MESH */
+	{ INT(wpa_ptk_rekey) },
+	{ 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 */
+#ifdef CONFIG_HT_OVERRIDES
+	{ 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) },
+	{ STR(ht_mcs) },
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+	{ INT_RANGE(disable_vht, 0, 1) },
+	{ INT(vht_capa) },
+	{ INT(vht_capa_mask) },
+	{ INT_RANGE(vht_rx_mcs_nss_1, -1, 3) },
+	{ INT_RANGE(vht_rx_mcs_nss_2, -1, 3) },
+	{ INT_RANGE(vht_rx_mcs_nss_3, -1, 3) },
+	{ INT_RANGE(vht_rx_mcs_nss_4, -1, 3) },
+	{ INT_RANGE(vht_rx_mcs_nss_5, -1, 3) },
+	{ INT_RANGE(vht_rx_mcs_nss_6, -1, 3) },
+	{ INT_RANGE(vht_rx_mcs_nss_7, -1, 3) },
+	{ INT_RANGE(vht_rx_mcs_nss_8, -1, 3) },
+	{ INT_RANGE(vht_tx_mcs_nss_1, -1, 3) },
+	{ INT_RANGE(vht_tx_mcs_nss_2, -1, 3) },
+	{ INT_RANGE(vht_tx_mcs_nss_3, -1, 3) },
+	{ INT_RANGE(vht_tx_mcs_nss_4, -1, 3) },
+	{ INT_RANGE(vht_tx_mcs_nss_5, -1, 3) },
+	{ INT_RANGE(vht_tx_mcs_nss_6, -1, 3) },
+	{ INT_RANGE(vht_tx_mcs_nss_7, -1, 3) },
+	{ INT_RANGE(vht_tx_mcs_nss_8, -1, 3) },
+#endif /* CONFIG_VHT_OVERRIDES */
+	{ 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 */
+	{ INT_RANGE(mac_addr, 0, 2) },
+};
+
+#undef OFFSET
+#undef _STR
+#undef STR
+#undef STR_KEY
+#undef _STR_LEN
+#undef STR_LEN
+#undef STR_LEN_KEY
+#undef _STR_RANGE
+#undef STR_RANGE
+#undef STR_RANGE_KEY
+#undef _INT
+#undef INT
+#undef INT_RANGE
+#undef _FUNC
+#undef FUNC
+#undef FUNC_KEY
+#define NUM_SSID_FIELDS ARRAY_SIZE(ssid_fields)
+
+
+/**
+ * wpa_config_add_prio_network - Add a network to priority lists
+ * @config: Configuration data from wpa_config_read()
+ * @ssid: Pointer to the network configuration to be added to the list
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to add a network block to the priority list of
+ * networks. This must be called for each network when reading in the full
+ * configuration. In addition, this can be used indirectly when updating
+ * priorities by calling wpa_config_update_prio_list().
+ */
+int wpa_config_add_prio_network(struct wpa_config *config,
+				struct wpa_ssid *ssid)
+{
+	int prio;
+	struct wpa_ssid *prev, **nlist;
+
+	/*
+	 * Add to an existing priority list if one is available for the
+	 * configured priority level for this network.
+	 */
+	for (prio = 0; prio < config->num_prio; prio++) {
+		prev = config->pssid[prio];
+		if (prev->priority == ssid->priority) {
+			while (prev->pnext)
+				prev = prev->pnext;
+			prev->pnext = ssid;
+			return 0;
+		}
+	}
+
+	/* First network for this priority - add a new priority list */
+	nlist = os_realloc_array(config->pssid, config->num_prio + 1,
+				 sizeof(struct wpa_ssid *));
+	if (nlist == NULL)
+		return -1;
+
+	for (prio = 0; prio < config->num_prio; prio++) {
+		if (nlist[prio]->priority < ssid->priority) {
+			os_memmove(&nlist[prio + 1], &nlist[prio],
+				   (config->num_prio - prio) *
+				   sizeof(struct wpa_ssid *));
+			break;
+		}
+	}
+
+	nlist[prio] = ssid;
+	config->num_prio++;
+	config->pssid = nlist;
+
+	return 0;
+}
+
+
+/**
+ * wpa_config_update_prio_list - Update network priority list
+ * @config: Configuration data from wpa_config_read()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called to update the priority list of networks in the
+ * configuration when a network is being added or removed. This is also called
+ * if a priority for a network is changed.
+ */
+int wpa_config_update_prio_list(struct wpa_config *config)
+{
+	struct wpa_ssid *ssid;
+	int ret = 0;
+
+	os_free(config->pssid);
+	config->pssid = NULL;
+	config->num_prio = 0;
+
+	ssid = config->ssid;
+	while (ssid) {
+		ssid->pnext = NULL;
+		if (wpa_config_add_prio_network(config, ssid) < 0)
+			ret = -1;
+		ssid = ssid->next;
+	}
+
+	return ret;
+}
+
+
+#ifdef IEEE8021X_EAPOL
+static void eap_peer_config_free(struct eap_peer_config *eap)
+{
+	os_free(eap->eap_methods);
+	bin_clear_free(eap->identity, eap->identity_len);
+	os_free(eap->anonymous_identity);
+	bin_clear_free(eap->password, eap->password_len);
+	os_free(eap->ca_cert);
+	os_free(eap->ca_path);
+	os_free(eap->client_cert);
+	os_free(eap->private_key);
+	str_clear_free(eap->private_key_passwd);
+	os_free(eap->dh_file);
+	os_free(eap->subject_match);
+	os_free(eap->altsubject_match);
+	os_free(eap->domain_suffix_match);
+	os_free(eap->domain_match);
+	os_free(eap->ca_cert2);
+	os_free(eap->ca_path2);
+	os_free(eap->client_cert2);
+	os_free(eap->private_key2);
+	str_clear_free(eap->private_key2_passwd);
+	os_free(eap->dh_file2);
+	os_free(eap->subject_match2);
+	os_free(eap->altsubject_match2);
+	os_free(eap->domain_suffix_match2);
+	os_free(eap->domain_match2);
+	os_free(eap->phase1);
+	os_free(eap->phase2);
+	os_free(eap->pcsc);
+	str_clear_free(eap->pin);
+	os_free(eap->engine_id);
+	os_free(eap->key_id);
+	os_free(eap->cert_id);
+	os_free(eap->ca_cert_id);
+	os_free(eap->key2_id);
+	os_free(eap->cert2_id);
+	os_free(eap->ca_cert2_id);
+	str_clear_free(eap->pin2);
+	os_free(eap->engine2_id);
+	os_free(eap->otp);
+	os_free(eap->pending_req_otp);
+	os_free(eap->pac_file);
+	bin_clear_free(eap->new_password, eap->new_password_len);
+	str_clear_free(eap->external_sim_resp);
+	os_free(eap->openssl_ciphers);
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+/**
+ * wpa_config_free_ssid - Free network/ssid configuration data
+ * @ssid: Configuration data for the network
+ *
+ * This function frees all resources allocated for the network configuration
+ * data.
+ */
+void wpa_config_free_ssid(struct wpa_ssid *ssid)
+{
+	struct psk_list_entry *psk;
+
+	os_free(ssid->ssid);
+	str_clear_free(ssid->passphrase);
+	os_free(ssid->ext_psk);
+#ifdef IEEE8021X_EAPOL
+	eap_peer_config_free(&ssid->eap);
+#endif /* IEEE8021X_EAPOL */
+	os_free(ssid->id_str);
+	os_free(ssid->scan_freq);
+	os_free(ssid->freq_list);
+	os_free(ssid->bgscan);
+	os_free(ssid->p2p_client_list);
+	os_free(ssid->bssid_blacklist);
+	os_free(ssid->bssid_whitelist);
+#ifdef CONFIG_HT_OVERRIDES
+	os_free(ssid->ht_mcs);
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_MESH
+	os_free(ssid->mesh_basic_rates);
+#endif /* CONFIG_MESH */
+	while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry,
+				    list))) {
+		dl_list_del(&psk->list);
+		bin_clear_free(psk, sizeof(*psk));
+	}
+	bin_clear_free(ssid, sizeof(*ssid));
+}
+
+
+void wpa_config_free_cred(struct wpa_cred *cred)
+{
+	size_t i;
+
+	os_free(cred->realm);
+	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);
+	str_clear_free(cred->private_key_passwd);
+	os_free(cred->imsi);
+	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()
+ *
+ * This function frees all resources allocated for the configuration data by
+ * wpa_config_read().
+ */
+void wpa_config_free(struct wpa_config *config)
+{
+	struct wpa_ssid *ssid, *prev = NULL;
+	struct wpa_cred *cred, *cprev;
+	int i;
+
+	ssid = config->ssid;
+	while (ssid) {
+		prev = ssid;
+		ssid = ssid->next;
+		wpa_config_free_ssid(prev);
+	}
+
+	cred = config->cred;
+	while (cred) {
+		cprev = cred;
+		cred = cred->next;
+		wpa_config_free_cred(cprev);
+	}
+
+	wpa_config_flush_blobs(config);
+
+	wpabuf_free(config->wps_vendor_ext_m1);
+	for (i = 0; i < MAX_WPS_VENDOR_EXT; i++)
+		wpabuf_free(config->wps_vendor_ext[i]);
+	os_free(config->ctrl_interface);
+	os_free(config->ctrl_interface_group);
+	os_free(config->opensc_engine_path);
+	os_free(config->pkcs11_engine_path);
+	os_free(config->pkcs11_module_path);
+	os_free(config->openssl_ciphers);
+	os_free(config->pcsc_reader);
+	str_clear_free(config->pcsc_pin);
+	os_free(config->driver_param);
+	os_free(config->device_name);
+	os_free(config->manufacturer);
+	os_free(config->model_name);
+	os_free(config->model_number);
+	os_free(config->serial_number);
+	os_free(config->config_methods);
+	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);
+	wpabuf_free(config->wps_nfc_dh_privkey);
+	wpabuf_free(config->wps_nfc_dev_pw);
+	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->bgscan);
+	os_free(config->wowlan_triggers);
+	os_free(config->fst_group_id);
+	os_free(config);
+}
+
+
+/**
+ * wpa_config_foreach_network - Iterate over each configured network
+ * @config: Configuration data from wpa_config_read()
+ * @func: Callback function to process each network
+ * @arg: Opaque argument to pass to callback function
+ *
+ * Iterate over the set of configured networks calling the specified
+ * function for each item. We guard against callbacks removing the
+ * supplied network.
+ */
+void wpa_config_foreach_network(struct wpa_config *config,
+				void (*func)(void *, struct wpa_ssid *),
+				void *arg)
+{
+	struct wpa_ssid *ssid, *next;
+
+	ssid = config->ssid;
+	while (ssid) {
+		next = ssid->next;
+		func(arg, ssid);
+		ssid = next;
+	}
+}
+
+
+/**
+ * wpa_config_get_network - Get configured network based on id
+ * @config: Configuration data from wpa_config_read()
+ * @id: Unique network id to search for
+ * Returns: Network configuration or %NULL if not found
+ */
+struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id)
+{
+	struct wpa_ssid *ssid;
+
+	ssid = config->ssid;
+	while (ssid) {
+		if (id == ssid->id)
+			break;
+		ssid = ssid->next;
+	}
+
+	return ssid;
+}
+
+
+/**
+ * wpa_config_add_network - Add a new network with empty configuration
+ * @config: Configuration data from wpa_config_read()
+ * Returns: The new network configuration or %NULL if operation failed
+ */
+struct wpa_ssid * wpa_config_add_network(struct wpa_config *config)
+{
+	int id;
+	struct wpa_ssid *ssid, *last = NULL;
+
+	id = -1;
+	ssid = config->ssid;
+	while (ssid) {
+		if (ssid->id > id)
+			id = ssid->id;
+		last = ssid;
+		ssid = ssid->next;
+	}
+	id++;
+
+	ssid = os_zalloc(sizeof(*ssid));
+	if (ssid == NULL)
+		return NULL;
+	ssid->id = id;
+	dl_list_init(&ssid->psk_list);
+	if (last)
+		last->next = ssid;
+	else
+		config->ssid = ssid;
+
+	wpa_config_update_prio_list(config);
+
+	return ssid;
+}
+
+
+/**
+ * wpa_config_remove_network - Remove a configured network based on id
+ * @config: Configuration data from wpa_config_read()
+ * @id: Unique network id to search for
+ * Returns: 0 on success, or -1 if the network was not found
+ */
+int wpa_config_remove_network(struct wpa_config *config, int id)
+{
+	struct wpa_ssid *ssid, *prev = NULL;
+
+	ssid = config->ssid;
+	while (ssid) {
+		if (id == ssid->id)
+			break;
+		prev = ssid;
+		ssid = ssid->next;
+	}
+
+	if (ssid == NULL)
+		return -1;
+
+	if (prev)
+		prev->next = ssid->next;
+	else
+		config->ssid = ssid->next;
+
+	wpa_config_update_prio_list(config);
+	wpa_config_free_ssid(ssid);
+	return 0;
+}
+
+
+/**
+ * wpa_config_set_network_defaults - Set network default values
+ * @ssid: Pointer to network configuration data
+ */
+void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
+{
+	ssid->proto = DEFAULT_PROTO;
+	ssid->pairwise_cipher = DEFAULT_PAIRWISE;
+	ssid->group_cipher = DEFAULT_GROUP;
+	ssid->key_mgmt = DEFAULT_KEY_MGMT;
+	ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
+#ifdef IEEE8021X_EAPOL
+	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_MESH
+	ssid->dot11MeshMaxRetries = DEFAULT_MESH_MAX_RETRIES;
+	ssid->dot11MeshRetryTimeout = DEFAULT_MESH_RETRY_TIMEOUT;
+	ssid->dot11MeshConfirmTimeout = DEFAULT_MESH_CONFIRM_TIMEOUT;
+	ssid->dot11MeshHoldingTimeout = DEFAULT_MESH_HOLDING_TIMEOUT;
+#endif /* CONFIG_MESH */
+#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;
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+	ssid->vht_rx_mcs_nss_1 = -1;
+	ssid->vht_rx_mcs_nss_2 = -1;
+	ssid->vht_rx_mcs_nss_3 = -1;
+	ssid->vht_rx_mcs_nss_4 = -1;
+	ssid->vht_rx_mcs_nss_5 = -1;
+	ssid->vht_rx_mcs_nss_6 = -1;
+	ssid->vht_rx_mcs_nss_7 = -1;
+	ssid->vht_rx_mcs_nss_8 = -1;
+	ssid->vht_tx_mcs_nss_1 = -1;
+	ssid->vht_tx_mcs_nss_2 = -1;
+	ssid->vht_tx_mcs_nss_3 = -1;
+	ssid->vht_tx_mcs_nss_4 = -1;
+	ssid->vht_tx_mcs_nss_5 = -1;
+	ssid->vht_tx_mcs_nss_6 = -1;
+	ssid->vht_tx_mcs_nss_7 = -1;
+	ssid->vht_tx_mcs_nss_8 = -1;
+#endif /* CONFIG_VHT_OVERRIDES */
+	ssid->proactive_key_caching = -1;
+#ifdef CONFIG_IEEE80211W
+	ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT;
+#endif /* CONFIG_IEEE80211W */
+	ssid->mac_addr = -1;
+}
+
+
+/**
+ * wpa_config_set - Set a variable in network configuration
+ * @ssid: Pointer to network configuration data
+ * @var: Variable name, e.g., "ssid"
+ * @value: Variable value
+ * @line: Line number in configuration file or 0 if not used
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to set network configuration variables based on
+ * both the configuration file and management interface input. The value
+ * parameter must be in the same format as the text-based configuration file is
+ * using. For example, strings are using double quotation marks.
+ */
+int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
+		   int line)
+{
+	size_t i;
+	int ret = 0;
+
+	if (ssid == NULL || var == NULL || value == NULL)
+		return -1;
+
+	for (i = 0; i < NUM_SSID_FIELDS; i++) {
+		const struct parse_data *field = &ssid_fields[i];
+		if (os_strcmp(var, field->name) != 0)
+			continue;
+
+		if (field->parser(field, ssid, line, value)) {
+			if (line) {
+				wpa_printf(MSG_ERROR, "Line %d: failed to "
+					   "parse %s '%s'.", line, var, value);
+			}
+			ret = -1;
+		}
+		break;
+	}
+	if (i == NUM_SSID_FIELDS) {
+		if (line) {
+			wpa_printf(MSG_ERROR, "Line %d: unknown network field "
+				   "'%s'.", line, var);
+		}
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
+			  const char *value)
+{
+	size_t len;
+	char *buf;
+	int ret;
+
+	len = os_strlen(value);
+	buf = os_malloc(len + 3);
+	if (buf == NULL)
+		return -1;
+	buf[0] = '"';
+	os_memcpy(buf + 1, value, len);
+	buf[len + 1] = '"';
+	buf[len + 2] = '\0';
+	ret = wpa_config_set(ssid, var, buf, 0);
+	os_free(buf);
+	return ret;
+}
+
+
+/**
+ * wpa_config_get_all - Get all options from network configuration
+ * @ssid: Pointer to network configuration data
+ * @get_keys: Determines if keys/passwords will be included in returned list
+ *	(if they may be exported)
+ * Returns: %NULL terminated list of all set keys and their values in the form
+ * of [key1, val1, key2, val2, ... , NULL]
+ *
+ * This function can be used to get list of all configured network properties.
+ * The caller is responsible for freeing the returned list and all its
+ * elements.
+ */
+char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys)
+{
+#ifdef NO_CONFIG_WRITE
+	return NULL;
+#else /* NO_CONFIG_WRITE */
+	const struct parse_data *field;
+	char *key, *value;
+	size_t i;
+	char **props;
+	int fields_num;
+
+	get_keys = get_keys && ssid->export_keys;
+
+	props = os_calloc(2 * NUM_SSID_FIELDS + 1, sizeof(char *));
+	if (!props)
+		return NULL;
+
+	fields_num = 0;
+	for (i = 0; i < NUM_SSID_FIELDS; i++) {
+		field = &ssid_fields[i];
+		if (field->key_data && !get_keys)
+			continue;
+		value = field->writer(field, ssid);
+		if (value == NULL)
+			continue;
+		if (os_strlen(value) == 0) {
+			os_free(value);
+			continue;
+		}
+
+		key = os_strdup(field->name);
+		if (key == NULL) {
+			os_free(value);
+			goto err;
+		}
+
+		props[fields_num * 2] = key;
+		props[fields_num * 2 + 1] = value;
+
+		fields_num++;
+	}
+
+	return props;
+
+err:
+	value = *props;
+	while (value)
+		os_free(value++);
+	os_free(props);
+	return NULL;
+#endif /* NO_CONFIG_WRITE */
+}
+
+
+#ifndef NO_CONFIG_WRITE
+/**
+ * wpa_config_get - Get a variable in network configuration
+ * @ssid: Pointer to network configuration data
+ * @var: Variable name, e.g., "ssid"
+ * Returns: Value of the variable or %NULL on failure
+ *
+ * This function can be used to get network configuration variables. The
+ * returned value is a copy of the configuration variable in text format, i.e,.
+ * the same format that the text-based configuration file and wpa_config_set()
+ * are using for the value. The caller is responsible for freeing the returned
+ * value.
+ */
+char * wpa_config_get(struct wpa_ssid *ssid, const char *var)
+{
+	size_t i;
+
+	if (ssid == NULL || var == NULL)
+		return NULL;
+
+	for (i = 0; i < NUM_SSID_FIELDS; i++) {
+		const struct parse_data *field = &ssid_fields[i];
+		if (os_strcmp(var, field->name) == 0)
+			return field->writer(field, ssid);
+	}
+
+	return NULL;
+}
+
+
+/**
+ * wpa_config_get_no_key - Get a variable in network configuration (no keys)
+ * @ssid: Pointer to network configuration data
+ * @var: Variable name, e.g., "ssid"
+ * Returns: Value of the variable or %NULL on failure
+ *
+ * This function can be used to get network configuration variable like
+ * wpa_config_get(). The only difference is that this functions does not expose
+ * key/password material from the configuration. In case a key/password field
+ * is requested, the returned value is an empty string or %NULL if the variable
+ * is not set or "*" if the variable is set (regardless of its value). The
+ * returned value is a copy of the configuration variable in text format, i.e,.
+ * the same format that the text-based configuration file and wpa_config_set()
+ * are using for the value. The caller is responsible for freeing the returned
+ * value.
+ */
+char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var)
+{
+	size_t i;
+
+	if (ssid == NULL || var == NULL)
+		return NULL;
+
+	for (i = 0; i < NUM_SSID_FIELDS; i++) {
+		const struct parse_data *field = &ssid_fields[i];
+		if (os_strcmp(var, field->name) == 0) {
+			char *res = field->writer(field, ssid);
+			if (field->key_data) {
+				if (res && res[0]) {
+					wpa_printf(MSG_DEBUG, "Do not allow "
+						   "key_data field to be "
+						   "exposed");
+					str_clear_free(res);
+					return os_strdup("*");
+				}
+
+				os_free(res);
+				return NULL;
+			}
+			return res;
+		}
+	}
+
+	return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+/**
+ * wpa_config_update_psk - Update WPA PSK based on passphrase and SSID
+ * @ssid: Pointer to network configuration data
+ *
+ * This function must be called to update WPA PSK when either SSID or the
+ * passphrase has changed for the network configuration.
+ */
+void wpa_config_update_psk(struct wpa_ssid *ssid)
+{
+#ifndef CONFIG_NO_PBKDF2
+	pbkdf2_sha1(ssid->passphrase, ssid->ssid, ssid->ssid_len, 4096,
+		    ssid->psk, PMK_LEN);
+	wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
+			ssid->psk, PMK_LEN);
+	ssid->psk_set = 1;
+#endif /* CONFIG_NO_PBKDF2 */
+}
+
+
+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;
+	}
+
+	if (os_strcmp(var, "eap") == 0) {
+		struct eap_method_type method;
+		method.method = eap_peer_get_type(value, &method.vendor);
+		if (method.vendor == EAP_VENDOR_IETF &&
+		    method.method == EAP_TYPE_NONE) {
+			wpa_printf(MSG_ERROR, "Line %d: unknown EAP type '%s' "
+				   "for a credential", line, value);
+			return -1;
+		}
+		os_free(cred->eap_method);
+		cred->eap_method = os_malloc(sizeof(*cred->eap_method));
+		if (cred->eap_method == NULL)
+			return -1;
+		os_memcpy(cred->eap_method, &method, sizeof(method));
+		return 0;
+	}
+
+	if (os_strcmp(var, "password") == 0 &&
+	    os_strncmp(value, "ext:", 4) == 0) {
+		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 "
+			   "value '%s'.", line, var, value);
+		return -1;
+	}
+
+	if (os_strcmp(var, "realm") == 0) {
+		os_free(cred->realm);
+		cred->realm = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "username") == 0) {
+		str_clear_free(cred->username);
+		cred->username = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "password") == 0) {
+		str_clear_free(cred->password);
+		cred->password = val;
+		cred->ext_password = 0;
+		return 0;
+	}
+
+	if (os_strcmp(var, "ca_cert") == 0) {
+		os_free(cred->ca_cert);
+		cred->ca_cert = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "client_cert") == 0) {
+		os_free(cred->client_cert);
+		cred->client_cert = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "private_key") == 0) {
+		os_free(cred->private_key);
+		cred->private_key = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "private_key_passwd") == 0) {
+		str_clear_free(cred->private_key_passwd);
+		cred->private_key_passwd = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "imsi") == 0) {
+		os_free(cred->imsi);
+		cred->imsi = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "milenage") == 0) {
+		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) {
+		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;
+	}
+
+	if (os_strcmp(var, "phase1") == 0) {
+		os_free(cred->phase1);
+		cred->phase1 = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "phase2") == 0) {
+		os_free(cred->phase2);
+		cred->phase2 = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "roaming_consortium") == 0) {
+		if (len < 3 || len > sizeof(cred->roaming_consortium)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid "
+				   "roaming_consortium length %d (3..15 "
+				   "expected)", line, (int) len);
+			os_free(val);
+			return -1;
+		}
+		os_memcpy(cred->roaming_consortium, val, len);
+		cred->roaming_consortium_len = len;
+		os_free(val);
+		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;
+
+		if (len > SSID_MAX_LEN) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid "
+				   "excluded_ssid length %d", line, (int) len);
+			os_free(val);
+			return -1;
+		}
+
+		e = os_realloc_array(cred->excluded_ssid,
+				     cred->num_excluded_ssid + 1,
+				     sizeof(struct excluded_ssid));
+		if (e == NULL) {
+			os_free(val);
+			return -1;
+		}
+		cred->excluded_ssid = e;
+
+		e = &cred->excluded_ssid[cred->num_excluded_ssid++];
+		os_memcpy(e->ssid, val, len);
+		e->ssid_len = len;
+
+		os_free(val);
+
+		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);
+	}
+
+	os_free(val);
+
+	return -1;
+}
+
+
+static char * alloc_int_str(int val)
+{
+	const unsigned int bufsize = 20;
+	char *buf;
+	int res;
+
+	buf = os_malloc(bufsize);
+	if (buf == NULL)
+		return NULL;
+	res = os_snprintf(buf, bufsize, "%d", val);
+	if (os_snprintf_error(bufsize, res)) {
+		os_free(buf);
+		buf = NULL;
+	}
+	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 (os_snprintf_error(end - pos, ret))
+				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 (os_snprintf_error(end - pos, ret))
+						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 (os_snprintf_error(end - pos, ret))
+				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 (os_snprintf_error(end - pos, ret))
+				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 (os_snprintf_error(end - pos, ret))
+				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;
+
+	cred = config->cred;
+	while (cred) {
+		if (id == cred->id)
+			break;
+		cred = cred->next;
+	}
+
+	return cred;
+}
+
+
+struct wpa_cred * wpa_config_add_cred(struct wpa_config *config)
+{
+	int id;
+	struct wpa_cred *cred, *last = NULL;
+
+	id = -1;
+	cred = config->cred;
+	while (cred) {
+		if (cred->id > id)
+			id = cred->id;
+		last = cred;
+		cred = cred->next;
+	}
+	id++;
+
+	cred = os_zalloc(sizeof(*cred));
+	if (cred == NULL)
+		return NULL;
+	cred->id = id;
+	cred->sim_num = DEFAULT_USER_SELECTED_SIM;
+	if (last)
+		last->next = cred;
+	else
+		config->cred = cred;
+
+	return cred;
+}
+
+
+int wpa_config_remove_cred(struct wpa_config *config, int id)
+{
+	struct wpa_cred *cred, *prev = NULL;
+
+	cred = config->cred;
+	while (cred) {
+		if (id == cred->id)
+			break;
+		prev = cred;
+		cred = cred->next;
+	}
+
+	if (cred == NULL)
+		return -1;
+
+	if (prev)
+		prev->next = cred->next;
+	else
+		config->cred = cred->next;
+
+	wpa_config_free_cred(cred);
+	return 0;
+}
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+/**
+ * wpa_config_get_blob - Get a named configuration blob
+ * @config: Configuration data from wpa_config_read()
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+const struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config,
+						   const char *name)
+{
+	struct wpa_config_blob *blob = config->blobs;
+
+	while (blob) {
+		if (os_strcmp(blob->name, name) == 0)
+			return blob;
+		blob = blob->next;
+	}
+	return NULL;
+}
+
+
+/**
+ * wpa_config_set_blob - Set or add a named configuration blob
+ * @config: Configuration data from wpa_config_read()
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an existing
+ * blob.
+ */
+void wpa_config_set_blob(struct wpa_config *config,
+			 struct wpa_config_blob *blob)
+{
+	wpa_config_remove_blob(config, blob->name);
+	blob->next = config->blobs;
+	config->blobs = blob;
+}
+
+
+/**
+ * wpa_config_free_blob - Free blob data
+ * @blob: Pointer to blob to be freed
+ */
+void wpa_config_free_blob(struct wpa_config_blob *blob)
+{
+	if (blob) {
+		os_free(blob->name);
+		bin_clear_free(blob->data, blob->len);
+		os_free(blob);
+	}
+}
+
+
+/**
+ * wpa_config_remove_blob - Remove a named configuration blob
+ * @config: Configuration data from wpa_config_read()
+ * @name: Name of the blob to remove
+ * Returns: 0 if blob was removed or -1 if blob was not found
+ */
+int wpa_config_remove_blob(struct wpa_config *config, const char *name)
+{
+	struct wpa_config_blob *pos = config->blobs, *prev = NULL;
+
+	while (pos) {
+		if (os_strcmp(pos->name, name) == 0) {
+			if (prev)
+				prev->next = pos->next;
+			else
+				config->blobs = pos->next;
+			wpa_config_free_blob(pos);
+			return 0;
+		}
+		prev = pos;
+		pos = pos->next;
+	}
+
+	return -1;
+}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+/**
+ * wpa_config_alloc_empty - Allocate an empty configuration
+ * @ctrl_interface: Control interface parameters, e.g., path to UNIX domain
+ * socket
+ * @driver_param: Driver parameters
+ * Returns: Pointer to allocated configuration data or %NULL on failure
+ */
+struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
+					   const char *driver_param)
+{
+	struct wpa_config *config;
+	const int aCWmin = 4, aCWmax = 10;
+	const struct hostapd_wmm_ac_params ac_bk =
+		{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
+	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 };
+	const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
+		{ aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
+
+	config = os_zalloc(sizeof(*config));
+	if (config == NULL)
+		return NULL;
+	config->eapol_version = DEFAULT_EAPOL_VERSION;
+	config->ap_scan = DEFAULT_AP_SCAN;
+	config->user_mpm = DEFAULT_USER_MPM;
+	config->max_peer_links = DEFAULT_MAX_PEER_LINKS;
+	config->mesh_max_inactivity = DEFAULT_MESH_MAX_INACTIVITY;
+	config->dot11RSNASAERetransPeriod =
+		DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD;
+	config->fast_reauth = DEFAULT_FAST_REAUTH;
+	config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
+	config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
+	config->p2p_go_freq_change_policy = DEFAULT_P2P_GO_FREQ_MOVE;
+	config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
+	config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN;
+	config->p2p_go_ctwindow = DEFAULT_P2P_GO_CTWINDOW;
+	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;
+	config->max_num_sta = DEFAULT_MAX_NUM_STA;
+	config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE;
+	config->scan_cur_freq = DEFAULT_SCAN_CUR_FREQ;
+	config->wmm_ac_params[0] = ac_be;
+	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;
+	config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
+	config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD;
+	config->cert_in_cb = DEFAULT_CERT_IN_CB;
+
+	if (ctrl_interface)
+		config->ctrl_interface = os_strdup(ctrl_interface);
+	if (driver_param)
+		config->driver_param = os_strdup(driver_param);
+
+	return config;
+}
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+/**
+ * wpa_config_debug_dump_networks - Debug dump of configured networks
+ * @config: Configuration data from wpa_config_read()
+ */
+void wpa_config_debug_dump_networks(struct wpa_config *config)
+{
+	int prio;
+	struct wpa_ssid *ssid;
+
+	for (prio = 0; prio < config->num_prio; prio++) {
+		ssid = config->pssid[prio];
+		wpa_printf(MSG_DEBUG, "Priority group %d",
+			   ssid->priority);
+		while (ssid) {
+			wpa_printf(MSG_DEBUG, "   id=%d ssid='%s'",
+				   ssid->id,
+				   wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+			ssid = ssid->pnext;
+		}
+	}
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+struct global_parse_data {
+	char *name;
+	int (*parser)(const struct global_parse_data *data,
+		      struct wpa_config *config, int line, const char *value);
+	int (*get)(const char *name, struct wpa_config *config, long offset,
+		   char *buf, size_t buflen, int pretty_print);
+	void *param1, *param2, *param3;
+	unsigned int changed_flag;
+};
+
+
+static int wpa_global_config_parse_int(const struct global_parse_data *data,
+				       struct wpa_config *config, int line,
+				       const char *pos)
+{
+	int val, *dst;
+	char *end;
+
+	dst = (int *) (((u8 *) config) + (long) data->param1);
+	val = strtol(pos, &end, 0);
+	if (*end) {
+		wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"",
+			   line, pos);
+		return -1;
+	}
+	*dst = val;
+
+	wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
+
+	if (data->param2 && *dst < (long) data->param2) {
+		wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+			   "min_value=%ld)", line, data->name, *dst,
+			   (long) data->param2);
+		*dst = (long) data->param2;
+		return -1;
+	}
+
+	if (data->param3 && *dst > (long) data->param3) {
+		wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+			   "max_value=%ld)", line, data->name, *dst,
+			   (long) data->param3);
+		*dst = (long) data->param3;
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_global_config_parse_str(const struct global_parse_data *data,
+				       struct wpa_config *config, int line,
+				       const char *pos)
+{
+	size_t len;
+	char **dst, *tmp;
+
+	len = os_strlen(pos);
+	if (data->param2 && len < (size_t) data->param2) {
+		wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
+			   "min_len=%ld)", line, data->name,
+			   (unsigned long) len, (long) data->param2);
+		return -1;
+	}
+
+	if (data->param3 && len > (size_t) data->param3) {
+		wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
+			   "max_len=%ld)", line, data->name,
+			   (unsigned long) len, (long) data->param3);
+		return -1;
+	}
+
+	tmp = os_strdup(pos);
+	if (tmp == NULL)
+		return -1;
+
+	dst = (char **) (((u8 *) config) + (long) data->param1);
+	os_free(*dst);
+	*dst = tmp;
+	wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst);
+
+	return 0;
+}
+
+
+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)
+{
+	size_t len;
+	struct wpabuf **dst, *tmp;
+
+	len = os_strlen(pos);
+	if (len & 0x01)
+		return -1;
+
+	tmp = wpabuf_alloc(len / 2);
+	if (tmp == NULL)
+		return -1;
+
+	if (hexstr2bin(pos, wpabuf_put(tmp, len / 2), len / 2)) {
+		wpabuf_free(tmp);
+		return -1;
+	}
+
+	dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1);
+	wpabuf_free(*dst);
+	*dst = tmp;
+	wpa_printf(MSG_DEBUG, "%s", data->name);
+
+	return 0;
+}
+
+
+static int wpa_config_process_freq_list(const struct global_parse_data *data,
+					struct wpa_config *config, int line,
+					const char *value)
+{
+	int *freqs;
+
+	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)
+{
+	if (!pos[0] || !pos[1]) {
+		wpa_printf(MSG_DEBUG, "Invalid country set");
+		return -1;
+	}
+	config->country[0] = pos[0];
+	config->country[1] = pos[1];
+	wpa_printf(MSG_DEBUG, "country='%c%c'",
+		   config->country[0], config->country[1]);
+	return 0;
+}
+
+
+static int wpa_config_process_load_dynamic_eap(
+	const struct global_parse_data *data, struct wpa_config *config,
+	int line, const char *so)
+{
+	int ret;
+	wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so);
+	ret = eap_peer_method_load(so);
+	if (ret == -2) {
+		wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not "
+			   "reloading.");
+	} else if (ret) {
+		wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP "
+			   "method '%s'.", line, so);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+#ifdef CONFIG_WPS
+
+static int wpa_config_process_uuid(const struct global_parse_data *data,
+				   struct wpa_config *config, int line,
+				   const char *pos)
+{
+	char buf[40];
+	if (uuid_str2bin(pos, config->uuid)) {
+		wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
+		return -1;
+	}
+	uuid_bin2str(config->uuid, buf, sizeof(buf));
+	wpa_printf(MSG_DEBUG, "uuid=%s", buf);
+	return 0;
+}
+
+
+static int wpa_config_process_device_type(
+	const struct global_parse_data *data,
+	struct wpa_config *config, int line, const char *pos)
+{
+	return wps_dev_type_str2bin(pos, config->device_type);
+}
+
+
+static int wpa_config_process_os_version(const struct global_parse_data *data,
+					 struct wpa_config *config, int line,
+					 const char *pos)
+{
+	if (hexstr2bin(pos, config->os_version, 4)) {
+		wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "os_version=%08x",
+		   WPA_GET_BE32(config->os_version));
+	return 0;
+}
+
+
+static int wpa_config_process_wps_vendor_ext_m1(
+	const struct global_parse_data *data,
+	struct wpa_config *config, int line, const char *pos)
+{
+	struct wpabuf *tmp;
+	int len = os_strlen(pos) / 2;
+	u8 *p;
+
+	if (!len) {
+		wpa_printf(MSG_ERROR, "Line %d: "
+			   "invalid wps_vendor_ext_m1", line);
+		return -1;
+	}
+
+	tmp = wpabuf_alloc(len);
+	if (tmp) {
+		p = wpabuf_put(tmp, len);
+
+		if (hexstr2bin(pos, p, len)) {
+			wpa_printf(MSG_ERROR, "Line %d: "
+				   "invalid wps_vendor_ext_m1", line);
+			wpabuf_free(tmp);
+			return -1;
+		}
+
+		wpabuf_free(config->wps_vendor_ext_m1);
+		config->wps_vendor_ext_m1 = tmp;
+	} else {
+		wpa_printf(MSG_ERROR, "Can not allocate "
+			   "memory for wps_vendor_ext_m1");
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+static int wpa_config_process_sec_device_type(
+	const struct global_parse_data *data,
+	struct wpa_config *config, int line, const char *pos)
+{
+	int idx;
+
+	if (config->num_sec_device_types >= MAX_SEC_DEVICE_TYPES) {
+		wpa_printf(MSG_ERROR, "Line %d: too many sec_device_type "
+			   "items", line);
+		return -1;
+	}
+
+	idx = config->num_sec_device_types;
+
+	if (wps_dev_type_str2bin(pos, config->sec_device_type[idx]))
+		return -1;
+
+	config->num_sec_device_types++;
+	return 0;
+}
+
+
+static int wpa_config_process_p2p_pref_chan(
+	const struct global_parse_data *data,
+	struct wpa_config *config, int line, const char *pos)
+{
+	struct p2p_channel *pref = NULL, *n;
+	unsigned int num = 0;
+	const char *pos2;
+	u8 op_class, chan;
+
+	/* format: class:chan,class:chan,... */
+
+	while (*pos) {
+		op_class = atoi(pos);
+		pos2 = os_strchr(pos, ':');
+		if (pos2 == NULL)
+			goto fail;
+		pos2++;
+		chan = atoi(pos2);
+
+		n = os_realloc_array(pref, num + 1,
+				     sizeof(struct p2p_channel));
+		if (n == NULL)
+			goto fail;
+		pref = n;
+		pref[num].op_class = op_class;
+		pref[num].chan = chan;
+		num++;
+
+		pos = os_strchr(pos2, ',');
+		if (pos == NULL)
+			break;
+		pos++;
+	}
+
+	os_free(config->p2p_pref_chan);
+	config->p2p_pref_chan = pref;
+	config->num_p2p_pref_chan = num;
+	wpa_hexdump(MSG_DEBUG, "P2P: Preferred class/channel pairs",
+		    (u8 *) config->p2p_pref_chan,
+		    config->num_p2p_pref_chan * sizeof(struct p2p_channel));
+
+	return 0;
+
+fail:
+	os_free(pref);
+	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 */
+
+
+static int wpa_config_process_hessid(
+	const struct global_parse_data *data,
+	struct wpa_config *config, int line, const char *pos)
+{
+	if (hwaddr_aton2(pos, config->hessid) < 0) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid hessid '%s'",
+			   line, pos);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_config_process_sae_groups(
+	const struct global_parse_data *data,
+	struct wpa_config *config, int line, const char *pos)
+{
+	int *groups = wpa_config_parse_int_array(pos);
+	if (groups == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid sae_groups '%s'",
+			   line, pos);
+		return -1;
+	}
+
+	os_free(config->sae_groups);
+	config->sae_groups = groups;
+
+	return 0;
+}
+
+
+static int wpa_config_process_ap_vendor_elements(
+	const struct global_parse_data *data,
+	struct wpa_config *config, int line, const char *pos)
+{
+	struct wpabuf *tmp;
+	int len = os_strlen(pos) / 2;
+	u8 *p;
+
+	if (!len) {
+		wpa_printf(MSG_ERROR, "Line %d: invalid ap_vendor_elements",
+			   line);
+		return -1;
+	}
+
+	tmp = wpabuf_alloc(len);
+	if (tmp) {
+		p = wpabuf_put(tmp, len);
+
+		if (hexstr2bin(pos, p, len)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid "
+				   "ap_vendor_elements", line);
+			wpabuf_free(tmp);
+			return -1;
+		}
+
+		wpabuf_free(config->ap_vendor_elements);
+		config->ap_vendor_elements = tmp;
+	} else {
+		wpa_printf(MSG_ERROR, "Cannot allocate memory for "
+			   "ap_vendor_elements");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+#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 */
+
+
+static int wpa_config_get_int(const char *name, struct wpa_config *config,
+			      long offset, char *buf, size_t buflen,
+			      int pretty_print)
+{
+	int *val = (int *) (((u8 *) config) + (long) offset);
+
+	if (pretty_print)
+		return os_snprintf(buf, buflen, "%s=%d\n", name, *val);
+	return os_snprintf(buf, buflen, "%d", *val);
+}
+
+
+static int wpa_config_get_str(const char *name, struct wpa_config *config,
+			      long offset, char *buf, size_t buflen,
+			      int pretty_print)
+{
+	char **val = (char **) (((u8 *) config) + (long) offset);
+	int res;
+
+	if (pretty_print)
+		res = os_snprintf(buf, buflen, "%s=%s\n", name,
+				  *val ? *val : "null");
+	else if (!*val)
+		return -1;
+	else
+		res = os_snprintf(buf, buflen, "%s", *val);
+	if (os_snprintf_error(buflen, res))
+		res = -1;
+
+	return res;
+}
+
+
+#ifdef CONFIG_P2P
+static int wpa_config_get_ipv4(const char *name, struct wpa_config *config,
+			       long offset, char *buf, size_t buflen,
+			       int pretty_print)
+{
+	void *val = ((u8 *) config) + (long) offset;
+	int res;
+	char addr[INET_ADDRSTRLEN];
+
+	if (!val || !inet_ntop(AF_INET, val, addr, sizeof(addr)))
+		return -1;
+
+	if (pretty_print)
+		res = os_snprintf(buf, buflen, "%s=%s\n", name, addr);
+	else
+		res = os_snprintf(buf, buflen, "%s", addr);
+
+	if (os_snprintf_error(buflen, res))
+		res = -1;
+
+	return res;
+}
+#endif /* CONFIG_P2P */
+
+
+#ifdef OFFSET
+#undef OFFSET
+#endif /* OFFSET */
+/* OFFSET: Get offset of a variable within the wpa_config structure */
+#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v)
+
+#define FUNC(f) #f, wpa_config_process_ ## f, NULL, OFFSET(f), NULL, NULL
+#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL, NULL
+#define _INT(f) #f, wpa_global_config_parse_int, wpa_config_get_int, OFFSET(f)
+#define INT(f) _INT(f), NULL, NULL
+#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
+#define _STR(f) #f, wpa_global_config_parse_str, wpa_config_get_str, OFFSET(f)
+#define STR(f) _STR(f), NULL, NULL
+#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
+#define BIN(f) #f, wpa_global_config_parse_bin, NULL, OFFSET(f), NULL, NULL
+#define IPV4(f) #f, wpa_global_config_parse_ipv4, wpa_config_get_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 },
+#ifdef CONFIG_MESH
+	{ INT(user_mpm), 0 },
+	{ INT_RANGE(max_peer_links, 0, 255), 0 },
+	{ INT(mesh_max_inactivity), 0 },
+	{ INT(dot11RSNASAERetransPeriod), 0 },
+#endif /* CONFIG_MESH */
+	{ INT(disable_scan_offload), 0 },
+	{ INT(fast_reauth), 0 },
+	{ STR(opensc_engine_path), 0 },
+	{ STR(pkcs11_engine_path), 0 },
+	{ STR(pkcs11_module_path), 0 },
+	{ STR(openssl_ciphers), 0 },
+	{ STR(pcsc_reader), 0 },
+	{ STR(pcsc_pin), 0 },
+	{ INT(external_sim), 0 },
+	{ STR(driver_param), 0 },
+	{ INT(dot11RSNAConfigPMKLifetime), 0 },
+	{ INT(dot11RSNAConfigPMKReauthThreshold), 0 },
+	{ INT(dot11RSNAConfigSATimeout), 0 },
+#ifndef CONFIG_NO_CONFIG_WRITE
+	{ INT(update_config), 0 },
+#endif /* CONFIG_NO_CONFIG_WRITE */
+	{ FUNC_NO_VAR(load_dynamic_eap), 0 },
+#ifdef CONFIG_WPS
+	{ FUNC(uuid), CFG_CHANGED_UUID },
+	{ STR_RANGE(device_name, 0, WPS_DEV_NAME_MAX_LEN),
+	  CFG_CHANGED_DEVICE_NAME },
+	{ STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING },
+	{ STR_RANGE(model_name, 0, 32), CFG_CHANGED_WPS_STRING },
+	{ STR_RANGE(model_number, 0, 32), CFG_CHANGED_WPS_STRING },
+	{ STR_RANGE(serial_number, 0, 32), CFG_CHANGED_WPS_STRING },
+	{ FUNC(device_type), CFG_CHANGED_DEVICE_TYPE },
+	{ FUNC(os_version), CFG_CHANGED_OS_VERSION },
+	{ STR(config_methods), CFG_CHANGED_CONFIG_METHODS },
+	{ INT_RANGE(wps_cred_processing, 0, 2), 0 },
+	{ FUNC(wps_vendor_ext_m1), CFG_CHANGED_VENDOR_EXTENSION },
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+	{ FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
+	{ INT(p2p_listen_reg_class), CFG_CHANGED_P2P_LISTEN_CHANNEL },
+	{ INT(p2p_listen_channel), CFG_CHANGED_P2P_LISTEN_CHANNEL },
+	{ 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_go_freq_change_policy, 0, P2P_GO_FREQ_MOVE_MAX), 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_RANGE(p2p_go_ctwindow, 0, 127), 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 },
+	{ INT_RANGE(p2p_cli_probe, 0, 1), 0 },
+#endif /* CONFIG_P2P */
+	{ FUNC(country), CFG_CHANGED_COUNTRY },
+	{ INT(bss_max_count), 0 },
+	{ INT(bss_expiration_age), 0 },
+	{ INT(bss_expiration_scan_count), 0 },
+	{ INT_RANGE(filter_ssids, 0, 1), 0 },
+	{ INT_RANGE(filter_rssi, -100, 0), 0 },
+	{ INT(max_num_sta), 0 },
+	{ INT_RANGE(disassoc_low_ack, 0, 1), 0 },
+#ifdef CONFIG_HS20
+	{ INT_RANGE(hs20, 0, 1), 0 },
+#endif /* CONFIG_HS20 */
+	{ INT_RANGE(interworking, 0, 1), 0 },
+	{ FUNC(hessid), 0 },
+	{ INT_RANGE(access_network_type, 0, 15), 0 },
+	{ INT_RANGE(pbc_in_m1, 0, 1), 0 },
+	{ STR(autoscan), 0 },
+	{ INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff),
+	  CFG_CHANGED_NFC_PASSWORD_TOKEN },
+	{ BIN(wps_nfc_dh_pubkey), CFG_CHANGED_NFC_PASSWORD_TOKEN },
+	{ BIN(wps_nfc_dh_privkey), CFG_CHANGED_NFC_PASSWORD_TOKEN },
+	{ BIN(wps_nfc_dev_pw), CFG_CHANGED_NFC_PASSWORD_TOKEN },
+	{ STR(ext_password_backend), CFG_CHANGED_EXT_PW_BACKEND },
+	{ INT(p2p_go_max_inactivity), 0 },
+	{ INT_RANGE(auto_interworking, 0, 1), 0 },
+	{ INT(okc), 0 },
+	{ INT(pmf), 0 },
+	{ FUNC(sae_groups), 0 },
+	{ INT(dtim_period), 0 },
+	{ INT(beacon_int), 0 },
+	{ FUNC(ap_vendor_elements), 0 },
+	{ INT_RANGE(ignore_old_scan_res, 0, 1), 0 },
+	{ FUNC(freq_list), 0 },
+	{ 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},
+	{ INT(mac_addr), 0 },
+	{ INT(rand_addr_lifetime), 0 },
+	{ INT(preassoc_mac_addr), 0 },
+	{ INT(key_mgmt_offload), 0},
+	{ INT(passive_scan), 0 },
+	{ INT(reassoc_same_bss_optim), 0 },
+	{ INT(wps_priority), 0},
+#ifdef CONFIG_FST
+	{ STR_RANGE(fst_group_id, 1, FST_MAX_GROUP_ID_LEN), 0 },
+	{ INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 },
+	{ INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 },
+#endif /* CONFIG_FST */
+};
+
+#undef FUNC
+#undef _INT
+#undef INT
+#undef INT_RANGE
+#undef _STR
+#undef STR
+#undef STR_RANGE
+#undef BIN
+#undef IPV4
+#define NUM_GLOBAL_FIELDS ARRAY_SIZE(global_fields)
+
+
+int wpa_config_dump_values(struct wpa_config *config, char *buf, size_t buflen)
+{
+	int result = 0;
+	size_t i;
+
+	for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
+		const struct global_parse_data *field = &global_fields[i];
+		int tmp;
+
+		if (!field->get)
+			continue;
+
+		tmp = field->get(field->name, config, (long) field->param1,
+				 buf, buflen, 1);
+		if (tmp < 0)
+			return -1;
+		buf += tmp;
+		buflen -= tmp;
+		result += tmp;
+	}
+	return result;
+}
+
+
+int wpa_config_get_value(const char *name, struct wpa_config *config,
+			 char *buf, size_t buflen)
+{
+	size_t i;
+
+	for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
+		const struct global_parse_data *field = &global_fields[i];
+
+		if (os_strcmp(name, field->name) != 0)
+			continue;
+		if (!field->get)
+			break;
+		return field->get(name, config, (long) field->param1,
+				  buf, buflen, 0);
+	}
+
+	return -1;
+}
+
+
+int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
+{
+	size_t i;
+	int ret = 0;
+
+	for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
+		const struct global_parse_data *field = &global_fields[i];
+		size_t flen = os_strlen(field->name);
+		if (os_strncmp(pos, field->name, flen) != 0 ||
+		    pos[flen] != '=')
+			continue;
+
+		if (field->parser(field, config, line, pos + flen + 1)) {
+			wpa_printf(MSG_ERROR, "Line %d: failed to "
+				   "parse '%s'.", line, pos);
+			ret = -1;
+		}
+		if (field->changed_flag == CFG_CHANGED_NFC_PASSWORD_TOKEN)
+			config->wps_nfc_pw_from_config = 1;
+		config->changed_parameters |= field->changed_flag;
+		break;
+	}
+	if (i == NUM_GLOBAL_FIELDS) {
+#ifdef CONFIG_AP
+		if (os_strncmp(pos, "wmm_ac_", 7) == 0) {
+			char *tmp = os_strchr(pos, '=');
+			if (tmp == NULL) {
+				if (line < 0)
+					return -1;
+				wpa_printf(MSG_ERROR, "Line %d: invalid line "
+					   "'%s'", line, pos);
+				return -1;
+			}
+			*tmp++ = '\0';
+			if (hostapd_config_wmm_ac(config->wmm_ac_params, pos,
+						  tmp)) {
+				wpa_printf(MSG_ERROR, "Line %d: invalid WMM "
+					   "AC item", line);
+				return -1;
+			}
+		}
+#endif /* CONFIG_AP */
+		if (line < 0)
+			return -1;
+		wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.",
+			   line, pos);
+		ret = -1;
+	}
+
+	return ret;
+}
diff --git a/hostap/wpa_supplicant/config.h b/hostap/wpa_supplicant/config.h
new file mode 100644
index 0000000..627f38b
--- /dev/null
+++ b/hostap/wpa_supplicant/config.h
@@ -0,0 +1,1342 @@
+/*
+ * WPA Supplicant / Configuration file structures
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define DEFAULT_EAPOL_VERSION 1
+#ifdef CONFIG_NO_SCAN_PROCESSING
+#define DEFAULT_AP_SCAN 2
+#else /* CONFIG_NO_SCAN_PROCESSING */
+#define DEFAULT_AP_SCAN 1
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+#define DEFAULT_USER_MPM 1
+#define DEFAULT_MAX_PEER_LINKS 99
+#define DEFAULT_MESH_MAX_INACTIVITY 300
+/*
+ * The default dot11RSNASAERetransPeriod is defined as 40 ms in the standard,
+ * but use 1000 ms in practice to avoid issues on low power CPUs.
+ */
+#define DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD 1000
+#define DEFAULT_FAST_REAUTH 1
+#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
+#define DEFAULT_RAND_ADDR_LIFETIME 60
+#define DEFAULT_KEY_MGMT_OFFLOAD 1
+#define DEFAULT_CERT_IN_CB 1
+#define DEFAULT_P2P_GO_CTWINDOW 0
+
+#include "config_ssid.h"
+#include "wps/wps.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+
+
+struct wpa_cred {
+	/**
+	 * next - Next credential in the list
+	 *
+	 * This pointer can be used to iterate over all credentials. The head
+	 * of this list is stored in the cred field of struct wpa_config.
+	 */
+	struct wpa_cred *next;
+
+	/**
+	 * id - Unique id for the credential
+	 *
+	 * This identifier is used as a unique identifier for each credential
+	 * block when using the control interface. Each credential is allocated
+	 * an id when it is being created, either when reading the
+	 * configuration file or when a new credential is added through the
+	 * control interface.
+	 */
+	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
+	 * (0). This field can be used to give higher priority for credentials
+	 * (and similarly in struct wpa_ssid for network blocks) to change the
+	 * Interworking automatic networking selection behavior. The matching
+	 * network (based on either an enabled network block or a credential)
+	 * with the highest priority value will be selected.
+	 */
+	int priority;
+
+	/**
+	 * pcsc - Use PC/SC and SIM/USIM card
+	 */
+	int pcsc;
+
+	/**
+	 * realm - Home Realm for Interworking
+	 */
+	char *realm;
+
+	/**
+	 * username - Username for Interworking network selection
+	 */
+	char *username;
+
+	/**
+	 * password - Password for Interworking network selection
+	 */
+	char *password;
+
+	/**
+	 * ext_password - Whether password is a name for external storage
+	 */
+	int ext_password;
+
+	/**
+	 * ca_cert - CA certificate for Interworking network selection
+	 */
+	char *ca_cert;
+
+	/**
+	 * client_cert - File path to client certificate file (PEM/DER)
+	 *
+	 * This field is used with Interworking networking selection for a case
+	 * where client certificate/private key is used for authentication
+	 * (EAP-TLS). Full path to the file should be used since working
+	 * directory may change when wpa_supplicant is run in the background.
+	 *
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
+	char *client_cert;
+
+	/**
+	 * private_key - File path to client private key file (PEM/DER/PFX)
+	 *
+	 * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+	 * commented out. Both the private key and certificate will be read
+	 * from the PKCS#12 file in this case. Full path to the file should be
+	 * used since working directory may change when wpa_supplicant is run
+	 * in the background.
+	 *
+	 * Windows certificate store can be used by leaving client_cert out and
+	 * configuring private_key in one of the following formats:
+	 *
+	 * cert://substring_to_match
+	 *
+	 * hash://certificate_thumbprint_in_hex
+	 *
+	 * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+	 *
+	 * Note that when running wpa_supplicant as an application, the user
+	 * certificate store (My user account) is used, whereas computer store
+	 * (Computer account) is used when running wpasvc as a service.
+	 *
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
+	char *private_key;
+
+	/**
+	 * private_key_passwd - Password for private key file
+	 */
+	char *private_key_passwd;
+
+	/**
+	 * imsi - IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+	 */
+	char *imsi;
+
+	/**
+	 * milenage - Milenage parameters for SIM/USIM simulator in
+	 *	<Ki>:<OPc>:<SQN> format
+	 */
+	char *milenage;
+
+	/**
+	 * 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 SubjectName 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. Multiple domain entries
+	 * can be used to configure alternative FQDNs that will be considered
+	 * home networks.
+	 */
+	char **domain;
+
+	/**
+	 * num_domain - Number of FQDNs in the domain array
+	 */
+	size_t num_domain;
+
+	/**
+	 * roaming_consortium - Roaming Consortium OI
+	 *
+	 * If roaming_consortium_len is non-zero, this field contains the
+	 * Roaming Consortium OI that can be used to determine which access
+	 * points support authentication with this credential. This is an
+	 * alternative to the use of the realm parameter. When using Roaming
+	 * Consortium to match the network, the EAP parameters need to be
+	 * pre-configured with the credential since the NAI Realm information
+	 * may not be available or fetched.
+	 */
+	u8 roaming_consortium[15];
+
+	/**
+	 * roaming_consortium_len - Length of roaming_consortium
+	 */
+	size_t roaming_consortium_len;
+
+	u8 required_roaming_consortium[15];
+	size_t required_roaming_consortium_len;
+
+	/**
+	 * eap_method - EAP method to use
+	 *
+	 * Pre-configured EAP method to use with this credential or %NULL to
+	 * indicate no EAP method is selected, i.e., the method will be
+	 * selected automatically based on ANQP information.
+	 */
+	struct eap_method_type *eap_method;
+
+	/**
+	 * phase1 - Phase 1 (outer authentication) parameters
+	 *
+	 * Pre-configured EAP parameters or %NULL.
+	 */
+	char *phase1;
+
+	/**
+	 * phase2 - Phase 2 (inner authentication) parameters
+	 *
+	 * Pre-configured EAP parameters or %NULL.
+	 */
+	char *phase2;
+
+	struct excluded_ssid {
+		u8 ssid[SSID_MAX_LEN];
+		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;
+};
+
+
+#define CFG_CHANGED_DEVICE_NAME BIT(0)
+#define CFG_CHANGED_CONFIG_METHODS BIT(1)
+#define CFG_CHANGED_DEVICE_TYPE BIT(2)
+#define CFG_CHANGED_OS_VERSION BIT(3)
+#define CFG_CHANGED_UUID BIT(4)
+#define CFG_CHANGED_COUNTRY BIT(5)
+#define CFG_CHANGED_SEC_DEVICE_TYPE BIT(6)
+#define CFG_CHANGED_P2P_SSID_POSTFIX BIT(7)
+#define CFG_CHANGED_WPS_STRING BIT(8)
+#define CFG_CHANGED_P2P_INTRA_BSS BIT(9)
+#define CFG_CHANGED_VENDOR_EXTENSION BIT(10)
+#define CFG_CHANGED_P2P_LISTEN_CHANNEL BIT(11)
+#define CFG_CHANGED_P2P_OPER_CHANNEL BIT(12)
+#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
+ *
+ * This data structure is presents the per-interface (radio) configuration
+ * data. In many cases, there is only one struct wpa_config instance, but if
+ * more than one network interface is being controlled, one instance is used
+ * for each.
+ */
+struct wpa_config {
+	/**
+	 * ssid - Head of the global network list
+	 *
+	 * This is the head for the list of all the configured networks.
+	 */
+	struct wpa_ssid *ssid;
+
+	/**
+	 * pssid - Per-priority network lists (in priority order)
+	 */
+	struct wpa_ssid **pssid;
+
+	/**
+	 * num_prio - Number of different priorities used in the pssid lists
+	 *
+	 * This indicates how many per-priority network lists are included in
+	 * pssid.
+	 */
+	int num_prio;
+
+	/**
+	 * cred - Head of the credential list
+	 *
+	 * This is the head for the list of all the configured credentials.
+	 */
+	struct wpa_cred *cred;
+
+	/**
+	 * eapol_version - IEEE 802.1X/EAPOL version number
+	 *
+	 * wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which
+	 * defines EAPOL version 2. However, there are many APs that do not
+	 * handle the new version number correctly (they seem to drop the
+	 * frames completely). In order 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).
+	 */
+	int eapol_version;
+
+	/**
+	 * ap_scan - AP scanning/selection
+	 *
+	 * By default, wpa_supplicant requests driver to perform AP
+	 * scanning and then uses the scan results to select a
+	 * suitable AP. Another alternative is to allow the driver to
+	 * take care of AP scanning and selection and use
+	 * wpa_supplicant just to process EAPOL frames based on IEEE
+	 * 802.11 association information from the driver.
+	 *
+	 * 1: wpa_supplicant initiates scanning and AP selection (default).
+	 *
+	 * 0: Driver takes care of scanning, AP selection, and IEEE 802.11
+	 * association parameters (e.g., WPA IE generation); this mode can
+	 * also be used with 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.
+	 *
+	 * 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, the network blocks in the configuration are tried
+	 * one by one until the driver reports successful association; each
+	 * network block should have explicit security policy (i.e., only one
+	 * option in the lists) for key_mgmt, pairwise, group, proto variables.
+	 *
+	 * Note: ap_scan=2 should not be used with the nl80211 driver interface
+	 * (the current Linux interface). ap_scan=1 is optimized work working
+	 * with nl80211. For finding networks using hidden SSID, scan_ssid=1 in
+	 * the network block can be used with nl80211.
+	 */
+	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
+	 * indicates support for this (sched_scan). This configuration
+	 * parameter can be used to disable this offloading mechanism.
+	 */
+	int disable_scan_offload;
+
+	/**
+	 * ctrl_interface - Parameters for the control interface
+	 *
+	 * If this is specified, %wpa_supplicant will open a control interface
+	 * that is available for external programs to manage %wpa_supplicant.
+	 * The meaning of this string depends on which control interface
+	 * mechanism is used. For all cases, the existence of this parameter
+	 * in configuration is used to determine whether the control interface
+	 * is enabled.
+	 *
+	 * For UNIX domain sockets (default on Linux and BSD): This is a
+	 * directory that will be created for UNIX domain sockets for listening
+	 * to requests from external programs (CLI/GUI, etc.) for status
+	 * information and configuration. The socket file will be named based
+	 * on the interface name, so multiple %wpa_supplicant processes can be
+	 * run at the same time if more than one interface is used.
+	 * /var/run/wpa_supplicant is the recommended directory for sockets and
+	 * by default, wpa_cli will use it when trying to connect with
+	 * %wpa_supplicant.
+	 *
+	 * Access control for the control interface can be configured
+	 * by setting the directory to allow only members of a group
+	 * to use sockets. This way, it is possible to run
+	 * %wpa_supplicant as root (since it needs to change network
+	 * configuration and open raw sockets) and still allow GUI/CLI
+	 * components to be run as non-root users. However, since the
+	 * control interface can be used to change the network
+	 * configuration, this access needs to be protected in many
+	 * cases. By default, %wpa_supplicant is configured to use gid
+	 * 0 (root). If you want to allow non-root users to use the
+	 * control interface, add a new group and change this value to
+	 * match with that group. Add users that should have control
+	 * interface access to this group.
+	 *
+	 * When configuring both the directory and group, use following format:
+	 * DIR=/var/run/wpa_supplicant GROUP=wheel
+	 * DIR=/var/run/wpa_supplicant GROUP=0
+	 * (group can be either group name or gid)
+	 *
+	 * For UDP connections (default on Windows): The value will be ignored.
+	 * This variable is just used to select that the control interface is
+	 * to be created. The value can be set to, e.g., udp
+	 * (ctrl_interface=udp).
+	 *
+	 * For Windows Named Pipe: This value can be used to set the security
+	 * descriptor for controlling access to the control interface. Security
+	 * descriptor can be set using Security Descriptor String Format (see
+	 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/security_descriptor_string_format.asp).
+	 * The descriptor string needs to be prefixed with SDDL=. For example,
+	 * ctrl_interface=SDDL=D: would set an empty DACL (which will reject
+	 * all connections).
+	 */
+	char *ctrl_interface;
+
+	/**
+	 * ctrl_interface_group - Control interface group (DEPRECATED)
+	 *
+	 * This variable is only used for backwards compatibility. Group for
+	 * UNIX domain sockets should now be specified using GROUP=group in
+	 * ctrl_interface variable.
+	 */
+	char *ctrl_interface_group;
+
+	/**
+	 * fast_reauth - EAP fast re-authentication (session resumption)
+	 *
+	 * By default, fast re-authentication is enabled for all EAP methods
+	 * that support it. This variable can be used to disable fast
+	 * re-authentication (by setting fast_reauth=0). Normally, there is no
+	 * need to disable fast re-authentication.
+	 */
+	int fast_reauth;
+
+	/**
+	 * opensc_engine_path - Path to the OpenSSL engine for opensc
+	 *
+	 * This is an OpenSSL specific configuration option for loading OpenSC
+	 * engine (engine_opensc.so); if %NULL, this engine is not loaded.
+	 */
+	char *opensc_engine_path;
+
+	/**
+	 * pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11
+	 *
+	 * This is an OpenSSL specific configuration option for loading PKCS#11
+	 * engine (engine_pkcs11.so); if %NULL, this engine is not loaded.
+	 */
+	char *pkcs11_engine_path;
+
+	/**
+	 * pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module
+	 *
+	 * This is an OpenSSL specific configuration option for configuring
+	 * path to OpenSC/PKCS#11 engine (opensc-pkcs11.so); if %NULL, this
+	 * module is not loaded.
+	 */
+	char *pkcs11_module_path;
+
+	/**
+	 * openssl_ciphers - OpenSSL cipher string
+	 *
+	 * This is an OpenSSL specific configuration option for configuring the
+	 * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the
+	 * default.
+	 */
+	char *openssl_ciphers;
+
+	/**
+	 * pcsc_reader - PC/SC reader name prefix
+	 *
+	 * If not %NULL, PC/SC reader with a name that matches this prefix is
+	 * initialized for SIM/USIM access. Empty string can be used to match
+	 * the first available reader.
+	 */
+	char *pcsc_reader;
+
+	/**
+	 * pcsc_pin - PIN for USIM, GSM SIM, and smartcards
+	 *
+	 * This field is used to configure PIN for SIM/USIM for EAP-SIM and
+	 * EAP-AKA. If left out, this will be asked through control interface.
+	 */
+	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
+	 * optional struct wpa_driver_ops::set_param() handler. This can be
+	 * used to configure driver specific options without having to add new
+	 * driver interface functionality.
+	 */
+	char *driver_param;
+
+	/**
+	 * dot11RSNAConfigPMKLifetime - Maximum lifetime of a PMK
+	 *
+	 * dot11 MIB variable for the maximum lifetime of a PMK in the PMK
+	 * cache (unit: seconds).
+	 */
+	unsigned int dot11RSNAConfigPMKLifetime;
+
+	/**
+	 * dot11RSNAConfigPMKReauthThreshold - PMK re-authentication threshold
+	 *
+	 * dot11 MIB variable for the percentage of the PMK lifetime
+	 * that should expire before an IEEE 802.1X reauthentication occurs.
+	 */
+	unsigned int dot11RSNAConfigPMKReauthThreshold;
+
+	/**
+	 * dot11RSNAConfigSATimeout - Security association timeout
+	 *
+	 * dot11 MIB variable for the maximum time a security association
+	 * shall take to set up (unit: seconds).
+	 */
+	unsigned int dot11RSNAConfigSATimeout;
+
+	/**
+	 * update_config - Is wpa_supplicant allowed to update configuration
+	 *
+	 * This variable control whether wpa_supplicant is allow to re-write
+	 * its configuration with wpa_config_write(). If this is zero,
+	 * configuration data is only changed in memory and the external data
+	 * is not overriden. If this is non-zero, wpa_supplicant will update
+	 * the configuration data (e.g., a file) whenever configuration is
+	 * changed. This update may replace the old configuration which can
+	 * remove comments from it in case of a text file configuration.
+	 */
+	int update_config;
+
+	/**
+	 * blobs - Configuration blobs
+	 */
+	struct wpa_config_blob *blobs;
+
+	/**
+	 * uuid - Universally Unique IDentifier (UUID; see RFC 4122) for WPS
+	 */
+	u8 uuid[16];
+
+	/**
+	 * device_name - Device Name (WPS)
+	 * User-friendly description of device; up to 32 octets encoded in
+	 * UTF-8
+	 */
+	char *device_name;
+
+	/**
+	 * manufacturer - Manufacturer (WPS)
+	 * The manufacturer of the device (up to 64 ASCII characters)
+	 */
+	char *manufacturer;
+
+	/**
+	 * model_name - Model Name (WPS)
+	 * Model of the device (up to 32 ASCII characters)
+	 */
+	char *model_name;
+
+	/**
+	 * model_number - Model Number (WPS)
+	 * Additional device description (up to 32 ASCII characters)
+	 */
+	char *model_number;
+
+	/**
+	 * serial_number - Serial Number (WPS)
+	 * Serial number of the device (up to 32 characters)
+	 */
+	char *serial_number;
+
+	/**
+	 * device_type - Primary Device Type (WPS)
+	 */
+	u8 device_type[WPS_DEV_TYPE_LEN];
+
+	/**
+	 * config_methods - Config Methods
+	 *
+	 * This is a space-separated list of supported WPS configuration
+	 * methods. For example, "label virtual_display virtual_push_button
+	 * keypad".
+	 * Available methods: usba ethernet label display ext_nfc_token
+	 * int_nfc_token nfc_interface push_button keypad
+	 * virtual_display physical_display
+	 * virtual_push_button physical_push_button.
+	 */
+	char *config_methods;
+
+	/**
+	 * os_version - OS Version (WPS)
+	 * 4-octet operating system version number
+	 */
+	u8 os_version[4];
+
+	/**
+	 * country - Country code
+	 *
+	 * This is the ISO/IEC alpha2 country code for which we are operating
+	 * in
+	 */
+	char country[2];
+
+	/**
+	 * wps_cred_processing - Credential processing
+	 *
+	 *   0 = process received credentials internally
+	 *   1 = do not process received credentials; just pass them over
+	 *	ctrl_iface to external program(s)
+	 *   2 = process received credentials internally and pass them over
+	 *	ctrl_iface to external program(s)
+	 */
+	int wps_cred_processing;
+
+#define MAX_SEC_DEVICE_TYPES 5
+	/**
+	 * sec_device_types - Secondary Device Types (P2P)
+	 */
+	u8 sec_device_type[MAX_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN];
+	int num_sec_device_types;
+
+	int p2p_listen_reg_class;
+	int p2p_listen_channel;
+	int p2p_oper_reg_class;
+	int p2p_oper_channel;
+	int p2p_go_intent;
+	char *p2p_ssid_postfix;
+	int persistent_reconnect;
+	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;
+
+#define MAX_WPS_VENDOR_EXT 10
+	/**
+	 * wps_vendor_ext - Vendor extension attributes in WPS
+	 */
+	struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXT];
+
+	/**
+	 * p2p_group_idle - Maximum idle time in seconds for P2P group
+	 *
+	 * This value controls how long a P2P group is maintained after there
+	 * is no other members in the group. As a GO, this means no associated
+	 * stations in the group. As a P2P client, this means no GO seen in
+	 * scan results. The maximum idle time is specified in seconds with 0
+	 * indicating no time limit, i.e., the P2P group remains in active
+	 * state indefinitely until explicitly removed. As a P2P client, the
+	 * maximum idle time of P2P_MAX_CLIENT_IDLE seconds is enforced, i.e.,
+	 * this parameter is mainly meant for GO use and for P2P client, it can
+	 * only be used to reduce the default timeout to smaller value. A
+	 * special value -1 can be used to configure immediate removal of the
+	 * group for P2P client role on any disconnection after the data
+	 * connection has been established.
+	 */
+	int p2p_group_idle;
+
+	/**
+	 * p2p_go_freq_change_policy - The GO frequency change policy
+	 *
+	 * This controls the behavior of the GO when there is a change in the
+	 * map of the currently used frequencies in case more than one channel
+	 * is supported.
+	 *
+	 * @P2P_GO_FREQ_MOVE_SCM: Prefer working in a single channel mode if
+	 * possible. In case the GO is the only interface using its frequency
+	 * and there are other station interfaces on other frequencies, the GO
+	 * will migrate to one of these frequencies.
+	 *
+	 * @P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS: Same as P2P_GO_FREQ_MOVE_SCM,
+	 * but a transition is possible only in case one of the other used
+	 * frequencies is one of the frequencies in the intersection of the
+	 * frequency list of the local device and the peer device.
+	 *
+	 * @P2P_GO_FREQ_MOVE_STAY: Prefer to stay on the current frequency.
+	 */
+	enum {
+		P2P_GO_FREQ_MOVE_SCM = 0,
+		P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS = 1,
+		P2P_GO_FREQ_MOVE_STAY = 2,
+		P2P_GO_FREQ_MOVE_MAX = P2P_GO_FREQ_MOVE_STAY,
+	} p2p_go_freq_change_policy;
+
+#define DEFAULT_P2P_GO_FREQ_MOVE P2P_GO_FREQ_MOVE_STAY
+
+	/**
+	 * 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;
+
+	/**
+	 * bss_expiration_age - BSS entry age after which it can be expired
+	 *
+	 * This value controls the time in seconds after which a BSS entry
+	 * gets removed if it has not been updated or is not in use.
+	 */
+	unsigned int bss_expiration_age;
+
+	/**
+	 * bss_expiration_scan_count - Expire BSS after number of scans
+	 *
+	 * If the BSS entry has not been seen in this many scans, it will be
+	 * removed. A value of 1 means that entry is removed after the first
+	 * scan in which the BSSID is not seen. Larger values can be used
+	 * to avoid BSS entries disappearing if they are not visible in
+	 * every scan (e.g., low signal quality or interference).
+	 */
+	unsigned int bss_expiration_scan_count;
+
+	/**
+	 * filter_ssids - SSID-based scan result filtering
+	 *
+	 *   0 = do not filter scan results
+	 *   1 = only include configured SSIDs in scan results/BSS table
+	 */
+	int filter_ssids;
+
+	/**
+	 * filter_rssi - RSSI-based scan result filtering
+	 *
+	 * 0 = do not filter scan results
+	 * -n = filter scan results below -n dBm
+	 */
+	int filter_rssi;
+
+	/**
+	 * max_num_sta - Maximum number of STAs in an AP/P2P GO
+	 */
+	unsigned int max_num_sta;
+
+	/**
+	 * freq_list - Array of allowed scan frequencies or %NULL for all
+	 *
+	 * This is an optional zero-terminated array of frequencies in
+	 * megahertz (MHz) to allow for narrowing scanning range.
+	 */
+	int *freq_list;
+
+	/**
+	 * scan_cur_freq - Whether to scan only the current channel
+	 *
+	 * If true, attempt to scan only the current channel if any other
+	 * VIFs on this radio are already associated on a particular channel.
+	 */
+	int scan_cur_freq;
+
+	/**
+	 * changed_parameters - Bitmap of changed parameters since last update
+	 */
+	unsigned int changed_parameters;
+
+	/**
+	 * disassoc_low_ack - Disassocicate stations with massive packet loss
+	 */
+	int disassoc_low_ack;
+
+	/**
+	 * interworking - Whether Interworking (IEEE 802.11u) is enabled
+	 */
+	int interworking;
+
+	/**
+	 * access_network_type - Access Network Type
+	 *
+	 * When Interworking is enabled, scans will be limited to APs that
+	 * advertise the specified Access Network Type (0..15; with 15
+	 * indicating wildcard match).
+	 */
+	int access_network_type;
+
+	/**
+	 * hessid - Homogenous ESS identifier
+	 *
+	 * If this is set (any octet is non-zero), scans will be used to
+	 * request response only from BSSes belonging to the specified
+	 * Homogeneous ESS. This is used only if interworking is enabled.
+	 */
+	u8 hessid[ETH_ALEN];
+
+	/**
+	 * hs20 - Hotspot 2.0
+	 */
+	int hs20;
+
+	/**
+	 * pbc_in_m1 - AP mode WPS probing workaround for PBC with Windows 7
+	 *
+	 * Windows 7 uses incorrect way of figuring out AP's WPS capabilities
+	 * by acting as a Registrar and using M1 from the AP. The config
+	 * methods attribute in that message is supposed to indicate only the
+	 * configuration method supported by the AP in Enrollee role, i.e., to
+	 * add an external Registrar. For that case, PBC shall not be used and
+	 * as such, the PushButton config method is removed from M1 by default.
+	 * If pbc_in_m1=1 is included in the configuration file, the PushButton
+	 * config method is left in M1 (if included in config_methods
+	 * parameter) to allow Windows 7 to use PBC instead of PIN (e.g., from
+	 * a label in the AP).
+	 */
+	int pbc_in_m1;
+
+	/**
+	 * autoscan - Automatic scan parameters or %NULL if none
+	 *
+	 * This is an optional set of parameters for automatic scanning
+	 * within an interface in following format:
+	 * <autoscan module name>:<module parameters>
+	 */
+	char *autoscan;
+
+	/**
+	 * wps_nfc_pw_from_config - NFC Device Password was read from config
+	 *
+	 * This parameter can be determined whether the NFC Device Password was
+	 * included in the configuration (1) or generated dynamically (0). Only
+	 * the former case is re-written back to the configuration file.
+	 */
+	int wps_nfc_pw_from_config;
+
+	/**
+	 * wps_nfc_dev_pw_id - NFC Device Password ID for password token
+	 */
+	int wps_nfc_dev_pw_id;
+
+	/**
+	 * wps_nfc_dh_pubkey - NFC DH Public Key for password token
+	 */
+	struct wpabuf *wps_nfc_dh_pubkey;
+
+	/**
+	 * wps_nfc_dh_privkey - NFC DH Private Key for password token
+	 */
+	struct wpabuf *wps_nfc_dh_privkey;
+
+	/**
+	 * wps_nfc_dev_pw - NFC Device Password for password token
+	 */
+	struct wpabuf *wps_nfc_dev_pw;
+
+	/**
+	 * ext_password_backend - External password backend or %NULL if none
+	 *
+	 * format: <backend name>[:<optional backend parameters>]
+	 */
+	char *ext_password_backend;
+
+	/*
+	 * p2p_go_max_inactivity - Timeout in seconds to detect STA inactivity
+	 *
+	 * This timeout value is used in P2P GO mode to clean up
+	 * inactive stations.
+	 * By default: 300 seconds.
+	 */
+	int p2p_go_max_inactivity;
+
+	struct hostapd_wmm_ac_params wmm_ac_params[4];
+
+	/**
+	 * auto_interworking - Whether to use network selection automatically
+	 *
+	 * 0 = do not automatically go through Interworking network selection
+	 *     (i.e., require explicit interworking_select command for this)
+	 * 1 = perform Interworking network selection if one or more
+	 *     credentials have been configured and scan did not find a
+	 *     matching network block
+	 */
+	int auto_interworking;
+
+	/**
+	 * p2p_go_ht40 - Default mode for HT40 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_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_go_ctwindow - CTWindow to use when operating as GO
+	 *
+	 * By default: 0 (no CTWindow). Values 0-127 can be used to indicate
+	 * the length of the CTWindow in TUs.
+	 */
+	int p2p_go_ctwindow;
+
+	/**
+	 * p2p_disabled - Whether P2P operations are disabled for this interface
+	 */
+	int p2p_disabled;
+
+	/**
+	 * p2p_no_group_iface - Whether group interfaces can be used
+	 *
+	 * By default, wpa_supplicant will create a separate interface for P2P
+	 * group operations if the driver supports this. This functionality can
+	 * be disabled by setting this parameter to 1. In that case, the same
+	 * interface that was used for the P2P management operations is used
+	 * also for the group operation.
+	 */
+	int p2p_no_group_iface;
+
+	/**
+	 * p2p_cli_probe - Enable/disable P2P CLI probe request handling
+	 *
+	 * If this parameter is set to 1, a connected P2P Client will receive
+	 * and handle Probe Request frames. Setting this parameter to 0
+	 * disables this option. Default value: 0.
+	 *
+	 * Note: Setting this property at run time takes effect on the following
+	 * interface state transition to/from the WPA_COMPLETED state.
+	 */
+	int p2p_cli_probe;
+
+	/**
+	 * okc - Whether to enable opportunistic key caching by default
+	 *
+	 * By default, OKC is disabled unless enabled by the per-network
+	 * proactive_key_caching=1 parameter. okc=1 can be used to change this
+	 * default behavior.
+	 */
+	int okc;
+
+	/**
+	 * pmf - Whether to enable/require PMF by default
+	 *
+	 * By default, PMF is disabled unless enabled by the per-network
+	 * ieee80211w=1 or ieee80211w=2 parameter. pmf=1/2 can be used to change
+	 * this default behavior.
+	 */
+	enum mfp_options pmf;
+
+	/**
+	 * sae_groups - Preference list of enabled groups for SAE
+	 *
+	 * By default (if this parameter is not set), the mandatory group 19
+	 * (ECC group defined over a 256-bit prime order field) is preferred,
+	 * but other groups are also enabled. If this parameter is set, the
+	 * groups will be tried in the indicated order.
+	 */
+	int *sae_groups;
+
+	/**
+	 * dtim_period - Default DTIM period in Beacon intervals
+	 *
+	 * This parameter can be used to set the default value for network
+	 * blocks that do not specify dtim_period.
+	 */
+	int dtim_period;
+
+	/**
+	 * beacon_int - Default Beacon interval in TU
+	 *
+	 * This parameter can be used to set the default value for network
+	 * blocks that do not specify beacon_int.
+	 */
+	int beacon_int;
+
+	/**
+	 * ap_vendor_elements: Vendor specific elements for Beacon/ProbeResp
+	 *
+	 * This parameter can be used to define additional vendor specific
+	 * elements for Beacon and Probe Response frames in AP/P2P GO mode. The
+	 * format for these element(s) is a hexdump of the raw information
+	 * elements (id+len+payload for one or more elements).
+	 */
+	struct wpabuf *ap_vendor_elements;
+
+	/**
+	 * ignore_old_scan_res - Ignore scan results older than request
+	 *
+	 * The driver may have a cache of scan results that makes it return
+	 * information that is older than our scan trigger. This parameter can
+	 * be used to configure such old information to be ignored instead of
+	 * allowing it to update the internal BSS table.
+	 */
+	int ignore_old_scan_res;
+
+	/**
+	 * sched_scan_interval -  schedule scan interval
+	 */
+	unsigned int sched_scan_interval;
+
+	/**
+	 * tdls_external_control - External control for TDLS setup requests
+	 *
+	 * Enable TDLS mode where external programs are given the control
+	 * 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;
+
+	/**
+	 * mac_addr - MAC address policy default
+	 *
+	 * 0 = use permanent MAC address
+	 * 1 = use random MAC address for each ESS connection
+	 * 2 = like 1, but maintain OUI (with local admin bit set)
+	 *
+	 * By default, permanent MAC address is used unless policy is changed by
+	 * the per-network mac_addr parameter. Global mac_addr=1 can be used to
+	 * change this default behavior.
+	 */
+	int mac_addr;
+
+	/**
+	 * rand_addr_lifetime - Lifetime of random MAC address in seconds
+	 */
+	unsigned int rand_addr_lifetime;
+
+	/**
+	 * preassoc_mac_addr - Pre-association MAC address policy
+	 *
+	 * 0 = use permanent MAC address
+	 * 1 = use random MAC address
+	 * 2 = like 1, but maintain OUI (with local admin bit set)
+	 */
+	int preassoc_mac_addr;
+
+	/**
+	 * key_mgmt_offload - Use key management offload
+	 *
+	 * Key management offload should be used if the device supports it.
+	 * Key management offload is the capability of a device operating as
+	 * a station to do the exchange necessary to establish temporal keys
+	 * during initial RSN connection, after roaming, or during a PTK
+	 * rekeying operation.
+	 */
+	int key_mgmt_offload;
+
+	/**
+	 * user_mpm - MPM residency
+	 *
+	 * 0: MPM lives in driver.
+	 * 1: wpa_supplicant handles peering and station allocation.
+	 *
+	 * If AMPE or SAE is enabled, the MPM is always in userspace.
+	 */
+	int user_mpm;
+
+	/**
+	 * max_peer_links - Maximum number of peer links
+	 *
+	 * Maximum number of mesh peering currently maintained by the STA.
+	 */
+	int max_peer_links;
+
+	/**
+	 * cert_in_cb - Whether to include a peer certificate dump in events
+	 *
+	 * This controls whether peer certificates for authentication server and
+	 * its certificate chain are included in EAP peer certificate events.
+	 */
+	int cert_in_cb;
+
+	/**
+	 * mesh_max_inactivity - Timeout in seconds to detect STA inactivity
+	 *
+	 * This timeout value is used in mesh STA to clean up inactive stations.
+	 * By default: 300 seconds.
+	 */
+	int mesh_max_inactivity;
+
+	/**
+	 * dot11RSNASAERetransPeriod - Timeout to retransmit SAE Auth frame
+	 *
+	 * This timeout value is used in mesh STA to retransmit
+	 * SAE Authentication frame.
+	 * By default: 1000 milliseconds.
+	 */
+	int dot11RSNASAERetransPeriod;
+
+	/**
+	 * passive_scan - Whether to force passive scan for network connection
+	 *
+	 * This parameter can be used to force only passive scanning to be used
+	 * for network connection cases. It should be noted that this will slow
+	 * down scan operations and reduce likelihood of finding the AP. In
+	 * addition, some use cases will override this due to functional
+	 * requirements, e.g., for finding an AP that uses hidden SSID
+	 * (scan_ssid=1) or P2P device discovery.
+	 */
+	int passive_scan;
+
+	/**
+	 * reassoc_same_bss_optim - Whether to optimize reassoc-to-same-BSS
+	 */
+	int reassoc_same_bss_optim;
+
+	/**
+	 * wps_priority - Priority for the networks added through WPS
+	 *
+	 * This priority value will be set to each network profile that is added
+	 * by executing the WPS protocol.
+	 */
+	int wps_priority;
+
+	/**
+	 * fst_group_id - FST group ID
+	 */
+	char *fst_group_id;
+
+	/**
+	 * fst_priority - priority of the interface within the FST group
+	 */
+	int fst_priority;
+
+	/**
+	 * fst_llt - default FST LLT (Link-Lost Timeout) to be used for the
+	 * interface.
+	 */
+	int fst_llt;
+};
+
+
+/* Prototypes for common functions from config.c */
+
+void wpa_config_free(struct wpa_config *ssid);
+void wpa_config_free_ssid(struct wpa_ssid *ssid);
+void wpa_config_foreach_network(struct wpa_config *config,
+				void (*func)(void *, struct wpa_ssid *),
+				void *arg);
+struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id);
+struct wpa_ssid * wpa_config_add_network(struct wpa_config *config);
+int wpa_config_remove_network(struct wpa_config *config, int id);
+void wpa_config_set_network_defaults(struct wpa_ssid *ssid);
+int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
+		   int line);
+int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
+			  const char *value);
+int wpa_config_dump_values(struct wpa_config *config, char *buf,
+			   size_t buflen);
+int wpa_config_get_value(const char *name, struct wpa_config *config,
+			 char *buf, size_t buflen);
+
+char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys);
+char * wpa_config_get(struct wpa_ssid *ssid, const char *var);
+char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var);
+void wpa_config_update_psk(struct wpa_ssid *ssid);
+int wpa_config_add_prio_network(struct wpa_config *config,
+				struct wpa_ssid *ssid);
+int wpa_config_update_prio_list(struct wpa_config *config);
+const struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config,
+						   const char *name);
+void wpa_config_set_blob(struct wpa_config *config,
+			 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);
+int wpa_config_remove_cred(struct wpa_config *config, int id);
+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);
+#ifndef CONFIG_NO_STDOUT_DEBUG
+void wpa_config_debug_dump_networks(struct wpa_config *config);
+#else /* CONFIG_NO_STDOUT_DEBUG */
+#define wpa_config_debug_dump_networks(c) do { } while (0)
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+/* Prototypes for common functions from config.c */
+int wpa_config_process_global(struct wpa_config *config, char *pos, int line);
+
+
+/* Prototypes for backend specific functions from the selected config_*.c */
+
+/**
+ * wpa_config_read - Read and parse configuration database
+ * @name: Name of the configuration (e.g., path and file name for the
+ * configuration file)
+ * @cfgp: Pointer to previously allocated configuration data or %NULL if none
+ * Returns: Pointer to allocated configuration data or %NULL on failure
+ *
+ * This function reads configuration data, parses its contents, and allocates
+ * data structures needed for storing configuration information. The allocated
+ * data can be freed with wpa_config_free().
+ *
+ * Each configuration backend needs to implement this function.
+ */
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp);
+
+/**
+ * wpa_config_write - Write or update configuration data
+ * @name: Name of the configuration (e.g., path and file name for the
+ * configuration file)
+ * @config: Configuration data from wpa_config_read()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function write all configuration data into an external database (e.g.,
+ * a text file) in a format that can be read with wpa_config_read(). This can
+ * be used to allow wpa_supplicant to update its configuration, e.g., when a
+ * new network is added or a password is changed.
+ *
+ * Each configuration backend needs to implement this function.
+ */
+int wpa_config_write(const char *name, struct wpa_config *config);
+
+#endif /* CONFIG_H */
diff --git a/hostap/wpa_supplicant/config_file.c b/hostap/wpa_supplicant/config_file.c
new file mode 100644
index 0000000..fb438ea
--- /dev/null
+++ b/hostap/wpa_supplicant/config_file.c
@@ -0,0 +1,1387 @@
+/*
+ * WPA Supplicant / Configuration backend: text file
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements a configuration backend for text files. All the
+ * configuration information is stored in a text file that uses a format
+ * described in the sample configuration file, wpa_supplicant.conf.
+ */
+
+#include "includes.h"
+#ifdef ANDROID
+#include <sys/stat.h>
+#endif /* ANDROID */
+
+#include "common.h"
+#include "config.h"
+#include "base64.h"
+#include "uuid.h"
+#include "p2p/p2p.h"
+#include "eap_peer/eap_methods.h"
+#include "eap_peer/eap.h"
+
+
+static int newline_terminated(const char *buf, size_t buflen)
+{
+	size_t len = os_strlen(buf);
+	if (len == 0)
+		return 0;
+	if (len == buflen - 1 && buf[buflen - 1] != '\r' &&
+	    buf[len - 1] != '\n')
+		return 0;
+	return 1;
+}
+
+
+static void skip_line_end(FILE *stream)
+{
+	char buf[100];
+	while (fgets(buf, sizeof(buf), stream)) {
+		buf[sizeof(buf) - 1] = '\0';
+		if (newline_terminated(buf, sizeof(buf)))
+			return;
+	}
+}
+
+
+/**
+ * wpa_config_get_line - Read the next configuration file line
+ * @s: Buffer for the line
+ * @size: The buffer length
+ * @stream: File stream to read from
+ * @line: Pointer to a variable storing the file line number
+ * @_pos: Buffer for the pointer to the beginning of data on the text line or
+ * %NULL if not needed (returned value used instead)
+ * Returns: Pointer to the beginning of data on the text line or %NULL if no
+ * more text lines are available.
+ *
+ * This function reads the next non-empty line from the configuration file and
+ * removes comments. The returned string is guaranteed to be null-terminated.
+ */
+static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line,
+				  char **_pos)
+{
+	char *pos, *end, *sstart;
+
+	while (fgets(s, size, stream)) {
+		(*line)++;
+		s[size - 1] = '\0';
+		if (!newline_terminated(s, size)) {
+			/*
+			 * The line was truncated - skip rest of it to avoid
+			 * confusing error messages.
+			 */
+			wpa_printf(MSG_INFO, "Long line in configuration file "
+				   "truncated");
+			skip_line_end(stream);
+		}
+		pos = s;
+
+		/* Skip white space from the beginning of line. */
+		while (*pos == ' ' || *pos == '\t' || *pos == '\r')
+			pos++;
+
+		/* Skip comment lines and empty lines */
+		if (*pos == '#' || *pos == '\n' || *pos == '\0')
+			continue;
+
+		/*
+		 * Remove # comments unless they are within a double quoted
+		 * string.
+		 */
+		sstart = os_strchr(pos, '"');
+		if (sstart)
+			sstart = os_strrchr(sstart + 1, '"');
+		if (!sstart)
+			sstart = pos;
+		end = os_strchr(sstart, '#');
+		if (end)
+			*end-- = '\0';
+		else
+			end = pos + os_strlen(pos) - 1;
+
+		/* Remove trailing white space. */
+		while (end > pos &&
+		       (*end == '\n' || *end == ' ' || *end == '\t' ||
+			*end == '\r'))
+			*end-- = '\0';
+
+		if (*pos == '\0')
+			continue;
+
+		if (_pos)
+			*_pos = pos;
+		return pos;
+	}
+
+	if (_pos)
+		*_pos = NULL;
+	return NULL;
+}
+
+
+static int wpa_config_validate_network(struct wpa_ssid *ssid, int line)
+{
+	int errors = 0;
+
+	if (ssid->passphrase) {
+		if (ssid->psk_set) {
+			wpa_printf(MSG_ERROR, "Line %d: both PSK and "
+				   "passphrase configured.", line);
+			errors++;
+		}
+		wpa_config_update_psk(ssid);
+	}
+
+	if ((ssid->group_cipher & WPA_CIPHER_CCMP) &&
+	    !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
+	    !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) {
+		/* Group cipher cannot be stronger than the pairwise cipher. */
+		wpa_printf(MSG_DEBUG, "Line %d: removed CCMP from group cipher"
+			   " list since it was not allowed for pairwise "
+			   "cipher", line);
+		ssid->group_cipher &= ~WPA_CIPHER_CCMP;
+	}
+
+	if (ssid->mode == WPAS_MODE_MESH &&
+	    (ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
+	    ssid->key_mgmt != WPA_KEY_MGMT_SAE)) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: key_mgmt for mesh network should be open or SAE",
+			   line);
+		errors++;
+	}
+
+	return errors;
+}
+
+
+static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id)
+{
+	struct wpa_ssid *ssid;
+	int errors = 0, end = 0;
+	char buf[2000], *pos, *pos2;
+
+	wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block",
+		   *line);
+	ssid = os_zalloc(sizeof(*ssid));
+	if (ssid == NULL)
+		return NULL;
+	dl_list_init(&ssid->psk_list);
+	ssid->id = id;
+
+	wpa_config_set_network_defaults(ssid);
+
+	while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
+		if (os_strcmp(pos, "}") == 0) {
+			end = 1;
+			break;
+		}
+
+		pos2 = os_strchr(pos, '=');
+		if (pos2 == NULL) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid SSID line "
+				   "'%s'.", *line, pos);
+			errors++;
+			continue;
+		}
+
+		*pos2++ = '\0';
+		if (*pos2 == '"') {
+			if (os_strchr(pos2 + 1, '"') == NULL) {
+				wpa_printf(MSG_ERROR, "Line %d: invalid "
+					   "quotation '%s'.", *line, pos2);
+				errors++;
+				continue;
+			}
+		}
+
+		if (wpa_config_set(ssid, pos, pos2, *line) < 0)
+			errors++;
+	}
+
+	if (!end) {
+		wpa_printf(MSG_ERROR, "Line %d: network block was not "
+			   "terminated properly.", *line);
+		errors++;
+	}
+
+	errors += wpa_config_validate_network(ssid, *line);
+
+	if (errors) {
+		wpa_config_free_ssid(ssid);
+		ssid = NULL;
+	}
+
+	return ssid;
+}
+
+
+static struct wpa_cred * wpa_config_read_cred(FILE *f, int *line, int id)
+{
+	struct wpa_cred *cred;
+	int errors = 0, end = 0;
+	char buf[256], *pos, *pos2;
+
+	wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new cred block", *line);
+	cred = os_zalloc(sizeof(*cred));
+	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) {
+			end = 1;
+			break;
+		}
+
+		pos2 = os_strchr(pos, '=');
+		if (pos2 == NULL) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid cred line "
+				   "'%s'.", *line, pos);
+			errors++;
+			continue;
+		}
+
+		*pos2++ = '\0';
+		if (*pos2 == '"') {
+			if (os_strchr(pos2 + 1, '"') == NULL) {
+				wpa_printf(MSG_ERROR, "Line %d: invalid "
+					   "quotation '%s'.", *line, pos2);
+				errors++;
+				continue;
+			}
+		}
+
+		if (wpa_config_set_cred(cred, pos, pos2, *line) < 0)
+			errors++;
+	}
+
+	if (!end) {
+		wpa_printf(MSG_ERROR, "Line %d: cred block was not "
+			   "terminated properly.", *line);
+		errors++;
+	}
+
+	if (errors) {
+		wpa_config_free_cred(cred);
+		cred = NULL;
+	}
+
+	return cred;
+}
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+static struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line,
+						     const char *name)
+{
+	struct wpa_config_blob *blob;
+	char buf[256], *pos;
+	unsigned char *encoded = NULL, *nencoded;
+	int end = 0;
+	size_t encoded_len = 0, len;
+
+	wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new named blob '%s'",
+		   *line, name);
+
+	while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
+		if (os_strcmp(pos, "}") == 0) {
+			end = 1;
+			break;
+		}
+
+		len = os_strlen(pos);
+		nencoded = os_realloc(encoded, encoded_len + len);
+		if (nencoded == NULL) {
+			wpa_printf(MSG_ERROR, "Line %d: not enough memory for "
+				   "blob", *line);
+			os_free(encoded);
+			return NULL;
+		}
+		encoded = nencoded;
+		os_memcpy(encoded + encoded_len, pos, len);
+		encoded_len += len;
+	}
+
+	if (!end) {
+		wpa_printf(MSG_ERROR, "Line %d: blob was not terminated "
+			   "properly", *line);
+		os_free(encoded);
+		return NULL;
+	}
+
+	blob = os_zalloc(sizeof(*blob));
+	if (blob == NULL) {
+		os_free(encoded);
+		return NULL;
+	}
+	blob->name = os_strdup(name);
+	blob->data = base64_decode(encoded, encoded_len, &blob->len);
+	os_free(encoded);
+
+	if (blob->name == NULL || blob->data == NULL) {
+		wpa_config_free_blob(blob);
+		return NULL;
+	}
+
+	return blob;
+}
+
+
+static int wpa_config_process_blob(struct wpa_config *config, FILE *f,
+				   int *line, char *bname)
+{
+	char *name_end;
+	struct wpa_config_blob *blob;
+
+	name_end = os_strchr(bname, '=');
+	if (name_end == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: no blob name terminator",
+			   *line);
+		return -1;
+	}
+	*name_end = '\0';
+
+	blob = wpa_config_read_blob(f, line, bname);
+	if (blob == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: failed to read blob %s",
+			   *line, bname);
+		return -1;
+	}
+	wpa_config_set_blob(config, blob);
+	return 0;
+}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
+{
+	FILE *f;
+	char buf[512], *pos;
+	int errors = 0, line = 0;
+	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;
+
+	if (name == NULL)
+		return NULL;
+	if (cfgp)
+		config = cfgp;
+	else
+		config = wpa_config_alloc_empty(NULL, NULL);
+	if (config == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to allocate config file "
+			   "structure");
+		return NULL;
+	}
+	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");
+	if (f == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to open config file '%s', "
+			   "error: %s", name, strerror(errno));
+		os_free(config);
+		return NULL;
+	}
+
+	while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) {
+		if (os_strcmp(pos, "network={") == 0) {
+			ssid = wpa_config_read_network(f, &line, id++);
+			if (ssid == NULL) {
+				wpa_printf(MSG_ERROR, "Line %d: failed to "
+					   "parse network block.", line);
+				errors++;
+				continue;
+			}
+			if (head == NULL) {
+				head = tail = ssid;
+			} else {
+				tail->next = ssid;
+				tail = ssid;
+			}
+			if (wpa_config_add_prio_network(config, ssid)) {
+				wpa_printf(MSG_ERROR, "Line %d: failed to add "
+					   "network block to priority list.",
+					   line);
+				errors++;
+				continue;
+			}
+		} else if (os_strcmp(pos, "cred={") == 0) {
+			cred = wpa_config_read_cred(f, &line, cred_id++);
+			if (cred == NULL) {
+				wpa_printf(MSG_ERROR, "Line %d: failed to "
+					   "parse cred block.", line);
+				errors++;
+				continue;
+			}
+			if (cred_head == NULL) {
+				cred_head = cred_tail = cred;
+			} else {
+				cred_tail->next = cred;
+				cred_tail = cred;
+			}
+#ifndef CONFIG_NO_CONFIG_BLOBS
+		} else if (os_strncmp(pos, "blob-base64-", 12) == 0) {
+			if (wpa_config_process_blob(config, f, &line, pos + 12)
+			    < 0) {
+				wpa_printf(MSG_ERROR, "Line %d: failed to "
+					   "process blob.", line);
+				errors++;
+				continue;
+			}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+		} else if (wpa_config_process_global(config, pos, line) < 0) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid configuration "
+				   "line '%s'.", line, pos);
+			errors++;
+			continue;
+		}
+	}
+
+	fclose(f);
+
+	config->ssid = head;
+	wpa_config_debug_dump_networks(config);
+	config->cred = cred_head;
+
+#ifndef WPA_IGNORE_CONFIG_ERRORS
+	if (errors) {
+		wpa_config_free(config);
+		config = NULL;
+		head = NULL;
+	}
+#endif /* WPA_IGNORE_CONFIG_ERRORS */
+
+	return config;
+}
+
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+
+static void write_str(FILE *f, const char *field, struct wpa_ssid *ssid)
+{
+	char *value = wpa_config_get(ssid, field);
+	if (value == NULL)
+		return;
+	fprintf(f, "\t%s=%s\n", field, value);
+	os_free(value);
+}
+
+
+static void write_int(FILE *f, const char *field, int value, int def)
+{
+	if (value == def)
+		return;
+	fprintf(f, "\t%s=%d\n", field, value);
+}
+
+
+static void write_bssid(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value = wpa_config_get(ssid, "bssid");
+	if (value == NULL)
+		return;
+	fprintf(f, "\tbssid=%s\n", value);
+	os_free(value);
+}
+
+
+static void write_psk(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (ssid->mem_only_psk)
+		return;
+
+	value = wpa_config_get(ssid, "psk");
+	if (value == NULL)
+		return;
+	fprintf(f, "\tpsk=%s\n", value);
+	os_free(value);
+}
+
+
+static void write_proto(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (ssid->proto == DEFAULT_PROTO)
+		return;
+
+	value = wpa_config_get(ssid, "proto");
+	if (value == NULL)
+		return;
+	if (value[0])
+		fprintf(f, "\tproto=%s\n", value);
+	os_free(value);
+}
+
+
+static void write_key_mgmt(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (ssid->key_mgmt == DEFAULT_KEY_MGMT)
+		return;
+
+	value = wpa_config_get(ssid, "key_mgmt");
+	if (value == NULL)
+		return;
+	if (value[0])
+		fprintf(f, "\tkey_mgmt=%s\n", value);
+	os_free(value);
+}
+
+
+static void write_pairwise(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (ssid->pairwise_cipher == DEFAULT_PAIRWISE)
+		return;
+
+	value = wpa_config_get(ssid, "pairwise");
+	if (value == NULL)
+		return;
+	if (value[0])
+		fprintf(f, "\tpairwise=%s\n", value);
+	os_free(value);
+}
+
+
+static void write_group(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (ssid->group_cipher == DEFAULT_GROUP)
+		return;
+
+	value = wpa_config_get(ssid, "group");
+	if (value == NULL)
+		return;
+	if (value[0])
+		fprintf(f, "\tgroup=%s\n", value);
+	os_free(value);
+}
+
+
+static void write_auth_alg(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (ssid->auth_alg == 0)
+		return;
+
+	value = wpa_config_get(ssid, "auth_alg");
+	if (value == NULL)
+		return;
+	if (value[0])
+		fprintf(f, "\tauth_alg=%s\n", value);
+	os_free(value);
+}
+
+
+#ifdef IEEE8021X_EAPOL
+static void write_eap(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	value = wpa_config_get(ssid, "eap");
+	if (value == NULL)
+		return;
+
+	if (value[0])
+		fprintf(f, "\teap=%s\n", value);
+	os_free(value);
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid)
+{
+	char field[20], *value;
+	int res;
+
+	res = os_snprintf(field, sizeof(field), "wep_key%d", idx);
+	if (os_snprintf_error(sizeof(field), res))
+		return;
+	value = wpa_config_get(ssid, field);
+	if (value) {
+		fprintf(f, "\t%s=%s\n", field, value);
+		os_free(value);
+	}
+}
+
+
+#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");
+	if (value == NULL)
+		return;
+	fprintf(f, "\tp2p_client_list=%s\n", value);
+	os_free(value);
+}
+
+
+static void write_psk_list(FILE *f, struct wpa_ssid *ssid)
+{
+	struct psk_list_entry *psk;
+	char hex[32 * 2 + 1];
+
+	dl_list_for_each(psk, &ssid->psk_list, struct psk_list_entry, list) {
+		wpa_snprintf_hex(hex, sizeof(hex), psk->psk, sizeof(psk->psk));
+		fprintf(f, "\tpsk_list=%s" MACSTR "-%s\n",
+			psk->p2p ? "P2P-" : "", MAC2STR(psk->addr), hex);
+	}
+}
+
+#endif /* CONFIG_P2P */
+
+
+static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
+{
+	int i;
+
+#define STR(t) write_str(f, #t, ssid)
+#define INT(t) write_int(f, #t, ssid->t, 0)
+#define INTe(t) write_int(f, #t, ssid->eap.t, 0)
+#define INT_DEF(t, def) write_int(f, #t, ssid->t, def)
+#define INT_DEFe(t, def) write_int(f, #t, ssid->eap.t, def)
+
+	STR(ssid);
+	INT(scan_ssid);
+	write_bssid(f, ssid);
+	write_str(f, "bssid_blacklist", ssid);
+	write_str(f, "bssid_whitelist", ssid);
+	write_psk(f, ssid);
+	INT(mem_only_psk);
+	write_proto(f, ssid);
+	write_key_mgmt(f, ssid);
+	INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD);
+	write_pairwise(f, ssid);
+	write_group(f, ssid);
+	write_auth_alg(f, ssid);
+	STR(bgscan);
+	STR(autoscan);
+	STR(scan_freq);
+#ifdef IEEE8021X_EAPOL
+	write_eap(f, ssid);
+	STR(identity);
+	STR(anonymous_identity);
+	STR(password);
+	STR(ca_cert);
+	STR(ca_path);
+	STR(client_cert);
+	STR(private_key);
+	STR(private_key_passwd);
+	STR(dh_file);
+	STR(subject_match);
+	STR(altsubject_match);
+	STR(domain_suffix_match);
+	STR(domain_match);
+	STR(ca_cert2);
+	STR(ca_path2);
+	STR(client_cert2);
+	STR(private_key2);
+	STR(private_key2_passwd);
+	STR(dh_file2);
+	STR(subject_match2);
+	STR(altsubject_match2);
+	STR(domain_suffix_match2);
+	STR(domain_match2);
+	STR(phase1);
+	STR(phase2);
+	STR(pcsc);
+	STR(pin);
+	STR(engine_id);
+	STR(key_id);
+	STR(cert_id);
+	STR(ca_cert_id);
+	STR(key2_id);
+	STR(pin2);
+	STR(engine2_id);
+	STR(cert2_id);
+	STR(ca_cert2_id);
+	INTe(engine);
+	INTe(engine2);
+	INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS);
+	STR(openssl_ciphers);
+	INTe(erp);
+#endif /* IEEE8021X_EAPOL */
+	for (i = 0; i < 4; i++)
+		write_wep_key(f, i, ssid);
+	INT(wep_tx_keyidx);
+	INT(priority);
+#ifdef IEEE8021X_EAPOL
+	INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND);
+	STR(pac_file);
+	INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE);
+	INTe(ocsp);
+	INT_DEFe(sim_num, DEFAULT_USER_SELECTED_SIM);
+#endif /* IEEE8021X_EAPOL */
+	INT(mode);
+	INT(no_auto_peer);
+	INT(frequency);
+	INT(fixed_freq);
+	write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1);
+	INT(disabled);
+	INT(peerkey);
+	INT(mixed_cell);
+#ifdef CONFIG_IEEE80211W
+	write_int(f, "ieee80211w", ssid->ieee80211w,
+		  MGMT_FRAME_PROTECTION_DEFAULT);
+#endif /* CONFIG_IEEE80211W */
+	STR(id_str);
+#ifdef CONFIG_P2P
+	write_go_p2p_dev_addr(f, ssid);
+	write_p2p_client_list(f, ssid);
+	write_psk_list(f, ssid);
+#endif /* CONFIG_P2P */
+	INT(ap_max_inactivity);
+	INT(dtim_period);
+	INT(beacon_int);
+#ifdef CONFIG_MACSEC
+	INT(macsec_policy);
+#endif /* CONFIG_MACSEC */
+#ifdef CONFIG_HS20
+	INT(update_identifier);
+#endif /* CONFIG_HS20 */
+	write_int(f, "mac_addr", ssid->mac_addr, -1);
+#ifdef CONFIG_MESH
+	STR(mesh_basic_rates);
+	INT_DEF(dot11MeshMaxRetries, DEFAULT_MESH_MAX_RETRIES);
+	INT_DEF(dot11MeshRetryTimeout, DEFAULT_MESH_RETRY_TIMEOUT);
+	INT_DEF(dot11MeshConfirmTimeout, DEFAULT_MESH_CONFIRM_TIMEOUT);
+	INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT);
+#endif /* CONFIG_MESH */
+	INT(wpa_ptk_rekey);
+	INT(ignore_broadcast_ssid);
+#ifdef CONFIG_HT_OVERRIDES
+	INT_DEF(disable_ht, DEFAULT_DISABLE_HT);
+	INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40);
+	INT_DEF(disable_sgi, DEFAULT_DISABLE_SGI);
+	INT_DEF(disable_ldpc, DEFAULT_DISABLE_LDPC);
+	INT(ht40_intolerant);
+	INT_DEF(disable_max_amsdu, DEFAULT_DISABLE_MAX_AMSDU);
+	INT_DEF(ampdu_factor, DEFAULT_AMPDU_FACTOR);
+	INT_DEF(ampdu_density, DEFAULT_AMPDU_DENSITY);
+	STR(ht_mcs);
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+	INT(disable_vht);
+	INT(vht_capa);
+	INT(vht_capa_mask);
+	INT_DEF(vht_rx_mcs_nss_1, -1);
+	INT_DEF(vht_rx_mcs_nss_2, -1);
+	INT_DEF(vht_rx_mcs_nss_3, -1);
+	INT_DEF(vht_rx_mcs_nss_4, -1);
+	INT_DEF(vht_rx_mcs_nss_5, -1);
+	INT_DEF(vht_rx_mcs_nss_6, -1);
+	INT_DEF(vht_rx_mcs_nss_7, -1);
+	INT_DEF(vht_rx_mcs_nss_8, -1);
+	INT_DEF(vht_tx_mcs_nss_1, -1);
+	INT_DEF(vht_tx_mcs_nss_2, -1);
+	INT_DEF(vht_tx_mcs_nss_3, -1);
+	INT_DEF(vht_tx_mcs_nss_4, -1);
+	INT_DEF(vht_tx_mcs_nss_5, -1);
+	INT_DEF(vht_tx_mcs_nss_6, -1);
+	INT_DEF(vht_tx_mcs_nss_7, -1);
+	INT_DEF(vht_tx_mcs_nss_8, -1);
+#endif /* CONFIG_VHT_OVERRIDES */
+
+#undef STR
+#undef INT
+#undef INT_DEF
+}
+
+
+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)
+		fprintf(f, "\tpcsc=%d\n", cred->pcsc);
+	if (cred->realm)
+		fprintf(f, "\trealm=\"%s\"\n", cred->realm);
+	if (cred->username)
+		fprintf(f, "\tusername=\"%s\"\n", cred->username);
+	if (cred->password && cred->ext_password)
+		fprintf(f, "\tpassword=ext:%s\n", cred->password);
+	else if (cred->password)
+		fprintf(f, "\tpassword=\"%s\"\n", cred->password);
+	if (cred->ca_cert)
+		fprintf(f, "\tca_cert=\"%s\"\n", cred->ca_cert);
+	if (cred->client_cert)
+		fprintf(f, "\tclient_cert=\"%s\"\n", cred->client_cert);
+	if (cred->private_key)
+		fprintf(f, "\tprivate_key=\"%s\"\n", cred->private_key);
+	if (cred->private_key_passwd)
+		fprintf(f, "\tprivate_key_passwd=\"%s\"\n",
+			cred->private_key_passwd);
+	if (cred->imsi)
+		fprintf(f, "\timsi=\"%s\"\n", cred->imsi);
+	if (cred->milenage)
+		fprintf(f, "\tmilenage=\"%s\"\n", cred->milenage);
+	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) {
+		fprintf(f, "\troaming_consortium=");
+		for (i = 0; i < cred->roaming_consortium_len; i++)
+			fprintf(f, "%02x", cred->roaming_consortium[i]);
+		fprintf(f, "\n");
+	}
+	if (cred->eap_method) {
+		const char *name;
+		name = eap_get_name(cred->eap_method[0].vendor,
+				    cred->eap_method[0].method);
+		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 j;
+		for (i = 0; i < cred->num_excluded_ssid; i++) {
+			struct excluded_ssid *e = &cred->excluded_ssid[i];
+			fprintf(f, "\texcluded_ssid=");
+			for (j = 0; j < e->ssid_len; j++)
+				fprintf(f, "%02x", e->ssid[j]);
+			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);
+}
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob)
+{
+	unsigned char *encoded;
+
+	encoded = base64_encode(blob->data, blob->len, NULL);
+	if (encoded == NULL)
+		return -1;
+
+	fprintf(f, "\nblob-base64-%s={\n%s}\n", blob->name, encoded);
+	os_free(encoded);
+	return 0;
+}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+static void write_global_bin(FILE *f, const char *field,
+			     const struct wpabuf *val)
+{
+	size_t i;
+	const u8 *pos;
+
+	if (val == NULL)
+		return;
+
+	fprintf(f, "%s=", field);
+	pos = wpabuf_head(val);
+	for (i = 0; i < wpabuf_len(val); i++)
+		fprintf(f, "%02X", *pos++);
+	fprintf(f, "\n");
+}
+
+
+static void wpa_config_write_global(FILE *f, struct wpa_config *config)
+{
+#ifdef CONFIG_CTRL_IFACE
+	if (config->ctrl_interface)
+		fprintf(f, "ctrl_interface=%s\n", config->ctrl_interface);
+	if (config->ctrl_interface_group)
+		fprintf(f, "ctrl_interface_group=%s\n",
+			config->ctrl_interface_group);
+#endif /* CONFIG_CTRL_IFACE */
+	if (config->eapol_version != DEFAULT_EAPOL_VERSION)
+		fprintf(f, "eapol_version=%d\n", config->eapol_version);
+	if (config->ap_scan != DEFAULT_AP_SCAN)
+		fprintf(f, "ap_scan=%d\n", config->ap_scan);
+	if (config->disable_scan_offload)
+		fprintf(f, "disable_scan_offload=%d\n",
+			config->disable_scan_offload);
+	if (config->fast_reauth != DEFAULT_FAST_REAUTH)
+		fprintf(f, "fast_reauth=%d\n", config->fast_reauth);
+	if (config->opensc_engine_path)
+		fprintf(f, "opensc_engine_path=%s\n",
+			config->opensc_engine_path);
+	if (config->pkcs11_engine_path)
+		fprintf(f, "pkcs11_engine_path=%s\n",
+			config->pkcs11_engine_path);
+	if (config->pkcs11_module_path)
+		fprintf(f, "pkcs11_module_path=%s\n",
+			config->pkcs11_module_path);
+	if (config->openssl_ciphers)
+		fprintf(f, "openssl_ciphers=%s\n", config->openssl_ciphers);
+	if (config->pcsc_reader)
+		fprintf(f, "pcsc_reader=%s\n", config->pcsc_reader);
+	if (config->pcsc_pin)
+		fprintf(f, "pcsc_pin=%s\n", config->pcsc_pin);
+	if (config->driver_param)
+		fprintf(f, "driver_param=%s\n", config->driver_param);
+	if (config->dot11RSNAConfigPMKLifetime)
+		fprintf(f, "dot11RSNAConfigPMKLifetime=%u\n",
+			config->dot11RSNAConfigPMKLifetime);
+	if (config->dot11RSNAConfigPMKReauthThreshold)
+		fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%u\n",
+			config->dot11RSNAConfigPMKReauthThreshold);
+	if (config->dot11RSNAConfigSATimeout)
+		fprintf(f, "dot11RSNAConfigSATimeout=%u\n",
+			config->dot11RSNAConfigSATimeout);
+	if (config->update_config)
+		fprintf(f, "update_config=%d\n", config->update_config);
+#ifdef CONFIG_WPS
+	if (!is_nil_uuid(config->uuid)) {
+		char buf[40];
+		uuid_bin2str(config->uuid, buf, sizeof(buf));
+		fprintf(f, "uuid=%s\n", buf);
+	}
+	if (config->device_name)
+		fprintf(f, "device_name=%s\n", config->device_name);
+	if (config->manufacturer)
+		fprintf(f, "manufacturer=%s\n", config->manufacturer);
+	if (config->model_name)
+		fprintf(f, "model_name=%s\n", config->model_name);
+	if (config->model_number)
+		fprintf(f, "model_number=%s\n", config->model_number);
+	if (config->serial_number)
+		fprintf(f, "serial_number=%s\n", config->serial_number);
+	{
+		char _buf[WPS_DEV_TYPE_BUFSIZE], *buf;
+		buf = wps_dev_type_bin2str(config->device_type,
+					   _buf, sizeof(_buf));
+		if (os_strcmp(buf, "0-00000000-0") != 0)
+			fprintf(f, "device_type=%s\n", buf);
+	}
+	if (WPA_GET_BE32(config->os_version))
+		fprintf(f, "os_version=%08x\n",
+			WPA_GET_BE32(config->os_version));
+	if (config->config_methods)
+		fprintf(f, "config_methods=%s\n", config->config_methods);
+	if (config->wps_cred_processing)
+		fprintf(f, "wps_cred_processing=%d\n",
+			config->wps_cred_processing);
+	if (config->wps_vendor_ext_m1) {
+		int i, len = wpabuf_len(config->wps_vendor_ext_m1);
+		const u8 *p = wpabuf_head_u8(config->wps_vendor_ext_m1);
+		if (len > 0) {
+			fprintf(f, "wps_vendor_ext_m1=");
+			for (i = 0; i < len; i++)
+				fprintf(f, "%02x", *p++);
+			fprintf(f, "\n");
+		}
+	}
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+	if (config->p2p_listen_reg_class)
+		fprintf(f, "p2p_listen_reg_class=%d\n",
+			config->p2p_listen_reg_class);
+	if (config->p2p_listen_channel)
+		fprintf(f, "p2p_listen_channel=%d\n",
+			config->p2p_listen_channel);
+	if (config->p2p_oper_reg_class)
+		fprintf(f, "p2p_oper_reg_class=%d\n",
+			config->p2p_oper_reg_class);
+	if (config->p2p_oper_channel)
+		fprintf(f, "p2p_oper_channel=%d\n", config->p2p_oper_channel);
+	if (config->p2p_go_intent != DEFAULT_P2P_GO_INTENT)
+		fprintf(f, "p2p_go_intent=%d\n", config->p2p_go_intent);
+	if (config->p2p_ssid_postfix)
+		fprintf(f, "p2p_ssid_postfix=%s\n", config->p2p_ssid_postfix);
+	if (config->persistent_reconnect)
+		fprintf(f, "persistent_reconnect=%d\n",
+			config->persistent_reconnect);
+	if (config->p2p_intra_bss != DEFAULT_P2P_INTRA_BSS)
+		fprintf(f, "p2p_intra_bss=%d\n", config->p2p_intra_bss);
+	if (config->p2p_group_idle)
+		fprintf(f, "p2p_group_idle=%d\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=");
+		for (i = 0; i < config->num_p2p_pref_chan; i++) {
+			fprintf(f, "%s%u:%u", i > 0 ? "," : "",
+				config->p2p_pref_chan[i].op_class,
+				config->p2p_pref_chan[i].chan);
+		}
+		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=%d\n", config->p2p_go_ht40);
+	if (config->p2p_go_vht)
+		fprintf(f, "p2p_go_vht=%d\n", config->p2p_go_vht);
+	if (config->p2p_go_ctwindow != DEFAULT_P2P_GO_CTWINDOW)
+		fprintf(f, "p2p_go_ctwindow=%d\n", config->p2p_go_ctwindow);
+	if (config->p2p_disabled)
+		fprintf(f, "p2p_disabled=%d\n", config->p2p_disabled);
+	if (config->p2p_no_group_iface)
+		fprintf(f, "p2p_no_group_iface=%d\n",
+			config->p2p_no_group_iface);
+	if (config->p2p_ignore_shared_freq)
+		fprintf(f, "p2p_ignore_shared_freq=%d\n",
+			config->p2p_ignore_shared_freq);
+	if (config->p2p_cli_probe)
+		fprintf(f, "p2p_cli_probe=%d\n", config->p2p_cli_probe);
+	if (config->p2p_go_freq_change_policy != DEFAULT_P2P_GO_FREQ_MOVE)
+		fprintf(f, "p2p_go_freq_change_policy=%u\n",
+			config->p2p_go_freq_change_policy);
+#endif /* CONFIG_P2P */
+	if (config->country[0] && config->country[1]) {
+		fprintf(f, "country=%c%c\n",
+			config->country[0], config->country[1]);
+	}
+	if (config->bss_max_count != DEFAULT_BSS_MAX_COUNT)
+		fprintf(f, "bss_max_count=%u\n", config->bss_max_count);
+	if (config->bss_expiration_age != DEFAULT_BSS_EXPIRATION_AGE)
+		fprintf(f, "bss_expiration_age=%u\n",
+			config->bss_expiration_age);
+	if (config->bss_expiration_scan_count !=
+	    DEFAULT_BSS_EXPIRATION_SCAN_COUNT)
+		fprintf(f, "bss_expiration_scan_count=%u\n",
+			config->bss_expiration_scan_count);
+	if (config->filter_ssids)
+		fprintf(f, "filter_ssids=%d\n", config->filter_ssids);
+	if (config->max_num_sta != DEFAULT_MAX_NUM_STA)
+		fprintf(f, "max_num_sta=%u\n", config->max_num_sta);
+	if (config->disassoc_low_ack)
+		fprintf(f, "disassoc_low_ack=%d\n", config->disassoc_low_ack);
+#ifdef CONFIG_HS20
+	if (config->hs20)
+		fprintf(f, "hs20=1\n");
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_INTERWORKING
+	if (config->interworking)
+		fprintf(f, "interworking=%d\n", config->interworking);
+	if (!is_zero_ether_addr(config->hessid))
+		fprintf(f, "hessid=" MACSTR "\n", MAC2STR(config->hessid));
+	if (config->access_network_type != DEFAULT_ACCESS_NETWORK_TYPE)
+		fprintf(f, "access_network_type=%d\n",
+			config->access_network_type);
+#endif /* CONFIG_INTERWORKING */
+	if (config->pbc_in_m1)
+		fprintf(f, "pbc_in_m1=%d\n", config->pbc_in_m1);
+	if (config->wps_nfc_pw_from_config) {
+		if (config->wps_nfc_dev_pw_id)
+			fprintf(f, "wps_nfc_dev_pw_id=%d\n",
+				config->wps_nfc_dev_pw_id);
+		write_global_bin(f, "wps_nfc_dh_pubkey",
+				 config->wps_nfc_dh_pubkey);
+		write_global_bin(f, "wps_nfc_dh_privkey",
+				 config->wps_nfc_dh_privkey);
+		write_global_bin(f, "wps_nfc_dev_pw", config->wps_nfc_dev_pw);
+	}
+
+	if (config->ext_password_backend)
+		fprintf(f, "ext_password_backend=%s\n",
+			config->ext_password_backend);
+	if (config->p2p_go_max_inactivity != DEFAULT_P2P_GO_MAX_INACTIVITY)
+		fprintf(f, "p2p_go_max_inactivity=%d\n",
+			config->p2p_go_max_inactivity);
+	if (config->auto_interworking)
+		fprintf(f, "auto_interworking=%d\n",
+			config->auto_interworking);
+	if (config->okc)
+		fprintf(f, "okc=%d\n", config->okc);
+	if (config->pmf)
+		fprintf(f, "pmf=%d\n", config->pmf);
+	if (config->dtim_period)
+		fprintf(f, "dtim_period=%d\n", config->dtim_period);
+	if (config->beacon_int)
+		fprintf(f, "beacon_int=%d\n", config->beacon_int);
+
+	if (config->sae_groups) {
+		int i;
+		fprintf(f, "sae_groups=");
+		for (i = 0; config->sae_groups[i] >= 0; i++) {
+			fprintf(f, "%s%d", i > 0 ? " " : "",
+				config->sae_groups[i]);
+		}
+		fprintf(f, "\n");
+	}
+
+	if (config->ap_vendor_elements) {
+		int i, len = wpabuf_len(config->ap_vendor_elements);
+		const u8 *p = wpabuf_head_u8(config->ap_vendor_elements);
+		if (len > 0) {
+			fprintf(f, "ap_vendor_elements=");
+			for (i = 0; i < len; i++)
+				fprintf(f, "%02x", *p++);
+			fprintf(f, "\n");
+		}
+	}
+
+	if (config->ignore_old_scan_res)
+		fprintf(f, "ignore_old_scan_res=%d\n",
+			config->ignore_old_scan_res);
+
+	if (config->freq_list && config->freq_list[0]) {
+		int i;
+		fprintf(f, "freq_list=");
+		for (i = 0; config->freq_list[i]; i++) {
+			fprintf(f, "%s%d", i > 0 ? " " : "",
+				config->freq_list[i]);
+		}
+		fprintf(f, "\n");
+	}
+	if (config->scan_cur_freq != DEFAULT_SCAN_CUR_FREQ)
+		fprintf(f, "scan_cur_freq=%d\n", config->scan_cur_freq);
+
+	if (config->sched_scan_interval)
+		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=%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);
+
+	if (config->mac_addr)
+		fprintf(f, "mac_addr=%d\n", config->mac_addr);
+
+	if (config->rand_addr_lifetime != DEFAULT_RAND_ADDR_LIFETIME)
+		fprintf(f, "rand_addr_lifetime=%u\n",
+			config->rand_addr_lifetime);
+
+	if (config->preassoc_mac_addr)
+		fprintf(f, "preassoc_mac_addr=%d\n", config->preassoc_mac_addr);
+
+	if (config->key_mgmt_offload != DEFAULT_KEY_MGMT_OFFLOAD)
+		fprintf(f, "key_mgmt_offload=%d\n", config->key_mgmt_offload);
+
+	if (config->user_mpm != DEFAULT_USER_MPM)
+		fprintf(f, "user_mpm=%d\n", config->user_mpm);
+
+	if (config->max_peer_links != DEFAULT_MAX_PEER_LINKS)
+		fprintf(f, "max_peer_links=%d\n", config->max_peer_links);
+
+	if (config->cert_in_cb != DEFAULT_CERT_IN_CB)
+		fprintf(f, "cert_in_cb=%d\n", config->cert_in_cb);
+
+	if (config->mesh_max_inactivity != DEFAULT_MESH_MAX_INACTIVITY)
+		fprintf(f, "mesh_max_inactivity=%d\n",
+			config->mesh_max_inactivity);
+
+	if (config->dot11RSNASAERetransPeriod !=
+	    DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD)
+		fprintf(f, "dot11RSNASAERetransPeriod=%d\n",
+			config->dot11RSNASAERetransPeriod);
+
+	if (config->passive_scan)
+		fprintf(f, "passive_scan=%d\n", config->passive_scan);
+
+	if (config->reassoc_same_bss_optim)
+		fprintf(f, "reassoc_same_bss_optim=%d\n",
+			config->reassoc_same_bss_optim);
+
+	if (config->wps_priority)
+		fprintf(f, "wps_priority=%d\n", config->wps_priority);
+}
+
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+
+int wpa_config_write(const char *name, struct wpa_config *config)
+{
+#ifndef CONFIG_NO_CONFIG_WRITE
+	FILE *f;
+	struct wpa_ssid *ssid;
+	struct wpa_cred *cred;
+#ifndef CONFIG_NO_CONFIG_BLOBS
+	struct wpa_config_blob *blob;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+	int ret = 0;
+	const char *orig_name = name;
+	int tmp_len = os_strlen(name) + 5; /* allow space for .tmp suffix */
+	char *tmp_name = os_malloc(tmp_len);
+
+	if (tmp_name) {
+		os_snprintf(tmp_name, tmp_len, "%s.tmp", name);
+		name = tmp_name;
+	}
+
+	wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name);
+
+	f = fopen(name, "w");
+	if (f == NULL) {
+		wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", name);
+		os_free(tmp_name);
+		return -1;
+	}
+
+	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");
+	}
+
+	for (ssid = config->ssid; ssid; ssid = ssid->next) {
+		if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary)
+			continue; /* do not save temporary networks */
+		if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
+		    !ssid->passphrase)
+			continue; /* do not save invalid network */
+		fprintf(f, "\nnetwork={\n");
+		wpa_config_write_network(f, ssid);
+		fprintf(f, "}\n");
+	}
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+	for (blob = config->blobs; blob; blob = blob->next) {
+		ret = wpa_config_write_blob(f, blob);
+		if (ret)
+			break;
+	}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+	os_fdatasync(f);
+
+	fclose(f);
+
+	if (tmp_name) {
+		int chmod_ret = 0;
+
+#ifdef ANDROID
+		chmod_ret = chmod(tmp_name,
+				  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+#endif /* ANDROID */
+		if (chmod_ret != 0 || rename(tmp_name, orig_name) != 0)
+			ret = -1;
+
+		os_free(tmp_name);
+	}
+
+	wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully",
+		   orig_name, ret ? "un" : "");
+	return ret;
+#else /* CONFIG_NO_CONFIG_WRITE */
+	return -1;
+#endif /* CONFIG_NO_CONFIG_WRITE */
+}
diff --git a/hostap/wpa_supplicant/config_none.c b/hostap/wpa_supplicant/config_none.c
new file mode 100644
index 0000000..2aac28f
--- /dev/null
+++ b/hostap/wpa_supplicant/config_none.c
@@ -0,0 +1,56 @@
+/*
+ * WPA Supplicant / Configuration backend: empty starting point
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements dummy example of a configuration backend. None of the
+ * functions are actually implemented so this can be used as a simple
+ * compilation test or a starting point for a new configuration backend.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "config.h"
+#include "base64.h"
+
+
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
+{
+	struct wpa_config *config;
+
+	if (name == NULL)
+		return NULL;
+	if (cfgp)
+		config = cfgp;
+	else
+		config = wpa_config_alloc_empty(NULL, NULL);
+	if (config == NULL)
+		return NULL;
+	/* TODO: fill in configuration data */
+	return config;
+}
+
+
+int wpa_config_write(const char *name, struct wpa_config *config)
+{
+	struct wpa_ssid *ssid;
+	struct wpa_config_blob *blob;
+
+	wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name);
+
+	/* TODO: write global config parameters */
+
+
+	for (ssid = config->ssid; ssid; ssid = ssid->next) {
+		/* TODO: write networks */
+	}
+
+	for (blob = config->blobs; blob; blob = blob->next) {
+		/* TODO: write blobs */
+	}
+
+	return 0;
+}
diff --git a/hostap/wpa_supplicant/config_ssid.h b/hostap/wpa_supplicant/config_ssid.h
new file mode 100644
index 0000000..7ef326c
--- /dev/null
+++ b/hostap/wpa_supplicant/config_ssid.h
@@ -0,0 +1,724 @@
+/*
+ * WPA Supplicant / Network configuration structures
+ * 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.
+ */
+
+#ifndef CONFIG_SSID_H
+#define CONFIG_SSID_H
+
+#include "common/defs.h"
+#include "utils/list.h"
+#include "eap_peer/eap_config.h"
+
+
+#define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
+#define DEFAULT_EAPOL_FLAGS (EAPOL_FLAG_REQUIRE_KEY_UNICAST | \
+			     EAPOL_FLAG_REQUIRE_KEY_BROADCAST)
+#define DEFAULT_PROTO (WPA_PROTO_WPA | WPA_PROTO_RSN)
+#define DEFAULT_KEY_MGMT (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X)
+#define DEFAULT_PAIRWISE (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP)
+#define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP)
+#define DEFAULT_FRAGMENT_SIZE 1398
+
+#define DEFAULT_BG_SCAN_PERIOD -1
+#define DEFAULT_MESH_MAX_RETRIES 2
+#define DEFAULT_MESH_RETRY_TIMEOUT 40
+#define DEFAULT_MESH_CONFIRM_TIMEOUT 40
+#define DEFAULT_MESH_HOLDING_TIMEOUT 40
+#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;
+	u8 addr[ETH_ALEN];
+	u8 psk[32];
+	u8 p2p;
+};
+
+/**
+ * struct wpa_ssid - Network configuration data
+ *
+ * This structure includes all the configuration variables for a network. This
+ * data is included in the per-interface configuration data as an element of
+ * the network list, struct wpa_config::ssid. Each network block in the
+ * configuration is mapped to a struct wpa_ssid instance.
+ */
+struct wpa_ssid {
+	/**
+	 * next - Next network in global list
+	 *
+	 * This pointer can be used to iterate over all networks. The head of
+	 * this list is stored in the ssid field of struct wpa_config.
+	 */
+	struct wpa_ssid *next;
+
+	/**
+	 * pnext - Next network in per-priority list
+	 *
+	 * This pointer can be used to iterate over all networks in the same
+	 * priority class. The heads of these list are stored in the pssid
+	 * fields of struct wpa_config.
+	 */
+	struct wpa_ssid *pnext;
+
+	/**
+	 * id - Unique id for the network
+	 *
+	 * This identifier is used as a unique identifier for each network
+	 * block when using the control interface. Each network is allocated an
+	 * id when it is being created, either when reading the configuration
+	 * file or when a new network is added through the control interface.
+	 */
+	int id;
+
+	/**
+	 * priority - Priority group
+	 *
+	 * By default, all networks will get same priority group (0). If some
+	 * of the networks are more desirable, this field can be used to change
+	 * the order in which wpa_supplicant goes through the networks when
+	 * selecting a BSS. The priority groups will be iterated in decreasing
+	 * priority (i.e., the larger the priority value, the sooner the
+	 * network is matched against the scan results). Within each priority
+	 * group, networks will be selected based on security policy, signal
+	 * strength, etc.
+	 *
+	 * Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are
+	 * not using this priority to select the order for scanning. Instead,
+	 * they try the networks in the order that used in the configuration
+	 * file.
+	 */
+	int priority;
+
+	/**
+	 * ssid - Service set identifier (network name)
+	 *
+	 * This is the SSID for the network. For wireless interfaces, this is
+	 * used to select which network will be used. If set to %NULL (or
+	 * ssid_len=0), any SSID can be used. For wired interfaces, this must
+	 * be set to %NULL. Note: SSID may contain any characters, even nul
+	 * (ASCII 0) and as such, this should not be assumed to be a nul
+	 * terminated string. ssid_len defines how many characters are valid
+	 * and the ssid field is not guaranteed to be nul terminated.
+	 */
+	u8 *ssid;
+
+	/**
+	 * ssid_len - Length of the SSID
+	 */
+	size_t ssid_len;
+
+	/**
+	 * bssid - BSSID
+	 *
+	 * If set, this network block is used only when associating with the AP
+	 * using the configured BSSID
+	 *
+	 * If this is a persistent P2P group (disabled == 2), this is the GO
+	 * Device Address.
+	 */
+	u8 bssid[ETH_ALEN];
+
+	/**
+	 * bssid_blacklist - List of inacceptable BSSIDs
+	 */
+	u8 *bssid_blacklist;
+	size_t num_bssid_blacklist;
+
+	/**
+	 * bssid_blacklist - List of acceptable BSSIDs
+	 */
+	u8 *bssid_whitelist;
+	size_t num_bssid_whitelist;
+
+	/**
+	 * bssid_set - Whether BSSID is configured for this network
+	 */
+	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];
+
+	/**
+	 * psk_set - Whether PSK field is configured
+	 */
+	int psk_set;
+
+	/**
+	 * passphrase - WPA ASCII passphrase
+	 *
+	 * If this is set, psk will be generated using the SSID and passphrase
+	 * configured for the network. ASCII passphrase must be between 8 and
+	 * 63 characters (inclusive).
+	 */
+	char *passphrase;
+
+	/**
+	 * ext_psk - PSK/passphrase name in external storage
+	 *
+	 * If this is set, PSK/passphrase will be fetched from external storage
+	 * when requesting association with the network.
+	 */
+	char *ext_psk;
+
+	/**
+	 * mem_only_psk - Whether to keep PSK/passphrase only in memory
+	 *
+	 * 0 = allow psk/passphrase to be stored to the configuration file
+	 * 1 = do not store psk/passphrase to the configuration file
+	 */
+	int mem_only_psk;
+
+	/**
+	 * pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_*
+	 */
+	int pairwise_cipher;
+
+	/**
+	 * group_cipher - Bitfield of allowed group ciphers, WPA_CIPHER_*
+	 */
+	int group_cipher;
+
+	/**
+	 * key_mgmt - Bitfield of allowed key management protocols
+	 *
+	 * WPA_KEY_MGMT_*
+	 */
+	int key_mgmt;
+
+	/**
+	 * bg_scan_period - Background scan period in seconds, 0 to disable, or
+	 * -1 to indicate no change to default driver configuration
+	 */
+	int bg_scan_period;
+
+	/**
+	 * proto - Bitfield of allowed protocols, WPA_PROTO_*
+	 */
+	int proto;
+
+	/**
+	 * auth_alg -  Bitfield of allowed authentication algorithms
+	 *
+	 * WPA_AUTH_ALG_*
+	 */
+	int auth_alg;
+
+	/**
+	 * scan_ssid - Scan this SSID with Probe Requests
+	 *
+	 * scan_ssid can be used to scan for APs using hidden SSIDs.
+	 * Note: Many drivers do not support this. ap_mode=2 can be used with
+	 * such drivers to use hidden SSIDs. Note2: Most nl80211-based drivers
+	 * do support scan_ssid=1 and that should be used with them instead of
+	 * ap_scan=2.
+	 */
+	int scan_ssid;
+
+#ifdef IEEE8021X_EAPOL
+#define EAPOL_FLAG_REQUIRE_KEY_UNICAST BIT(0)
+#define EAPOL_FLAG_REQUIRE_KEY_BROADCAST BIT(1)
+	/**
+	 * eapol_flags - Bit field of IEEE 802.1X/EAPOL options (EAPOL_FLAG_*)
+	 */
+	int eapol_flags;
+
+	/**
+	 * eap - EAP peer configuration for this network
+	 */
+	struct eap_peer_config eap;
+#endif /* IEEE8021X_EAPOL */
+
+#define NUM_WEP_KEYS 4
+#define MAX_WEP_KEY_LEN 16
+	/**
+	 * wep_key - WEP keys
+	 */
+	u8 wep_key[NUM_WEP_KEYS][MAX_WEP_KEY_LEN];
+
+	/**
+	 * wep_key_len - WEP key lengths
+	 */
+	size_t wep_key_len[NUM_WEP_KEYS];
+
+	/**
+	 * wep_tx_keyidx - Default key index for TX frames using WEP
+	 */
+	int wep_tx_keyidx;
+
+	/**
+	 * proactive_key_caching - Enable proactive key caching
+	 *
+	 * This field can be used to enable proactive key caching which is also
+	 * known as opportunistic PMKSA caching for WPA2. This is disabled (0)
+	 * by default unless default value is changed with the global okc=1
+	 * parameter. Enable by setting this to 1.
+	 *
+	 * Proactive key caching is used to make supplicant assume that the APs
+	 * are using the same PMK and generate PMKSA cache entries without
+	 * doing RSN pre-authentication. This requires support from the AP side
+	 * and is normally used with wireless switches that co-locate the
+	 * authenticator.
+	 *
+	 * Internally, special value -1 is used to indicate that the parameter
+	 * was not specified in the configuration (i.e., default behavior is
+	 * followed).
+	 */
+	int proactive_key_caching;
+
+	/**
+	 * mixed_cell - Whether mixed cells are allowed
+	 *
+	 * This option can be used to configure whether so called mixed cells,
+	 * i.e., networks that use both plaintext and encryption in the same
+	 * SSID, are allowed. This is disabled (0) by default. Enable by
+	 * setting this to 1.
+	 */
+	int mixed_cell;
+
+#ifdef IEEE8021X_EAPOL
+
+	/**
+	 * leap - Number of EAP methods using LEAP
+	 *
+	 * This field should be set to 1 if LEAP is enabled. This is used to
+	 * select IEEE 802.11 authentication algorithm.
+	 */
+	int leap;
+
+	/**
+	 * non_leap - Number of EAP methods not using LEAP
+	 *
+	 * This field should be set to >0 if any EAP method other than LEAP is
+	 * enabled. This is used to select IEEE 802.11 authentication
+	 * algorithm.
+	 */
+	int non_leap;
+
+	/**
+	 * eap_workaround - EAP workarounds enabled
+	 *
+	 * wpa_supplicant supports number of "EAP workarounds" to work around
+	 * interoperability issues with incorrectly behaving authentication
+	 * servers. This is recommended to be enabled by default because some
+	 * of the issues are present in large number of authentication servers.
+	 *
+	 * Strict EAP conformance mode can be configured by disabling
+	 * workarounds with eap_workaround = 0.
+	 */
+	unsigned int eap_workaround;
+
+#endif /* IEEE8021X_EAPOL */
+
+	/**
+	 * mode - IEEE 802.11 operation mode (Infrastucture/IBSS)
+	 *
+	 * 0 = infrastructure (Managed) mode, i.e., associate with an AP.
+	 *
+	 * 1 = IBSS (ad-hoc, peer-to-peer)
+	 *
+	 * 2 = AP (access point)
+	 *
+	 * 3 = P2P Group Owner (can be set in the configuration file)
+	 *
+	 * 4 = P2P Group Formation (used internally; not in configuration
+	 * files)
+	 *
+	 * 5 = Mesh
+	 *
+	 * 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,
+		WPAS_MODE_IBSS = 1,
+		WPAS_MODE_AP = 2,
+		WPAS_MODE_P2P_GO = 3,
+		WPAS_MODE_P2P_GROUP_FORMATION = 4,
+		WPAS_MODE_MESH = 5,
+	} mode;
+
+	/**
+	 * disabled - Whether this network is currently disabled
+	 *
+	 * 0 = this network can be used (default).
+	 * 1 = this network block is disabled (can be enabled through
+	 * ctrl_iface, e.g., with wpa_cli or wpa_gui).
+	 * 2 = this network block includes parameters for a persistent P2P
+	 * group (can be used with P2P ctrl_iface commands)
+	 */
+	int disabled;
+
+	/**
+	 * disabled_for_connect - Whether this network was temporarily disabled
+	 *
+	 * This flag is used to reenable all the temporarily disabled networks
+	 * after either the success or failure of a WPS connection.
+	 */
+	int disabled_for_connect;
+
+	/**
+	 * peerkey -  Whether PeerKey handshake for direct links is allowed
+	 *
+	 * This is only used when both RSN/WPA2 and IEEE 802.11e (QoS) are
+	 * enabled.
+	 *
+	 * 0 = disabled (default)
+	 * 1 = enabled
+	 */
+	int peerkey;
+
+	/**
+	 * id_str - Network identifier string for external scripts
+	 *
+	 * This value is passed to external ctrl_iface monitors in
+	 * WPA_EVENT_CONNECTED event and wpa_cli sets this as WPA_ID_STR
+	 * environment variable for action scripts.
+	 */
+	char *id_str;
+
+#ifdef CONFIG_IEEE80211W
+	/**
+	 * ieee80211w - Whether management frame protection is enabled
+	 *
+	 * This value is used to configure policy for management frame
+	 * protection (IEEE 802.11w). 0 = disabled, 1 = optional, 2 = required.
+	 * This is disabled by default unless the default value has been changed
+	 * with the global pmf=1/2 parameter.
+	 *
+	 * Internally, special value 3 is used to indicate that the parameter
+	 * was not specified in the configuration (i.e., default behavior is
+	 * followed).
+	 */
+	enum mfp_options ieee80211w;
+#endif /* CONFIG_IEEE80211W */
+
+	/**
+	 * frequency - Channel frequency in megahertz (MHz) for IBSS
+	 *
+	 * This value is used to configure the initial channel for IBSS (adhoc)
+	 * networks, e.g., 2412 = IEEE 802.11b/g channel 1. It is ignored in
+	 * the infrastructure mode. In addition, this value is only used by the
+	 * station that creates the IBSS. If an IBSS network with the
+	 * configured SSID is already present, the frequency of the network
+	 * will be used instead of this configured value.
+	 */
+	int frequency;
+
+	/**
+	 * fixed_freq - Use fixed frequency for IBSS
+	 */
+	int fixed_freq;
+
+	/**
+	 * mesh_basic_rates - BSS Basic rate set for mesh network
+	 *
+	 */
+	int *mesh_basic_rates;
+
+	/**
+	 * Mesh network plink parameters
+	 */
+	int dot11MeshMaxRetries;
+	int dot11MeshRetryTimeout; /* msec */
+	int dot11MeshConfirmTimeout; /* msec */
+	int dot11MeshHoldingTimeout; /* msec */
+
+	int ht40;
+
+	int vht;
+
+	/**
+	 * wpa_ptk_rekey - Maximum lifetime for PTK in seconds
+	 *
+	 * This value can be used to enforce rekeying of PTK to mitigate some
+	 * attacks against TKIP deficiencies.
+	 */
+	int wpa_ptk_rekey;
+
+	/**
+	 * scan_freq - Array of frequencies to scan or %NULL for all
+	 *
+	 * This is an optional zero-terminated array of frequencies in
+	 * megahertz (MHz) to include in scan requests when searching for this
+	 * network. This can be used to speed up scanning when the network is
+	 * known to not use all possible channels.
+	 */
+	int *scan_freq;
+
+	/**
+	 * 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) in following format:
+	 * <bgscan module name>:<module parameters>
+	 */
+	char *bgscan;
+
+	/**
+	 * ignore_broadcast_ssid - Hide SSID in AP mode
+	 *
+	 * Send empty SSID in beacons and ignore probe request frames that do
+	 * not specify full SSID, i.e., require stations to know SSID.
+	 * default: disabled (0)
+	 * 1 = send empty (length=0) SSID in beacon and ignore probe request
+	 * for broadcast SSID
+	 * 2 = clear SSID (ASCII 0), but keep the original length (this may be
+	 * required with some clients that do not support empty SSID) and
+	 * ignore probe requests for broadcast SSID
+	 */
+	int ignore_broadcast_ssid;
+
+	/**
+	 * freq_list - Array of allowed frequencies or %NULL for all
+	 *
+	 * This is an optional zero-terminated array of frequencies in
+	 * megahertz (MHz) to allow for selecting the BSS. If set, scan results
+	 * that do not match any of the specified frequencies are not
+	 * considered when selecting a BSS.
+	 */
+	int *freq_list;
+
+	/**
+	 * p2p_client_list - List of P2P Clients in a persistent group (GO)
+	 *
+	 * This is a list of P2P Clients (P2P Device Address) that have joined
+	 * the persistent group. This is maintained on the GO for persistent
+	 * group entries (disabled == 2).
+	 */
+	u8 *p2p_client_list;
+
+	/**
+	 * num_p2p_clients - Number of entries in p2p_client_list
+	 */
+	size_t num_p2p_clients;
+
+#ifndef P2P_MAX_STORED_CLIENTS
+#define P2P_MAX_STORED_CLIENTS 100
+#endif /* P2P_MAX_STORED_CLIENTS */
+
+	/**
+	 * psk_list - Per-client PSKs (struct psk_list_entry)
+	 */
+	struct dl_list psk_list;
+
+	/**
+	 * p2p_group - Network generated as a P2P group (used internally)
+	 */
+	int p2p_group;
+
+	/**
+	 * p2p_persistent_group - Whether this is a persistent group
+	 */
+	int p2p_persistent_group;
+
+	/**
+	 * temporary - Whether this network is temporary and not to be saved
+	 */
+	int temporary;
+
+	/**
+	 * export_keys - Whether keys may be exported
+	 *
+	 * This attribute will be set when keys are determined through
+	 * WPS or similar so that they may be exported.
+	 */
+	int export_keys;
+
+#ifdef CONFIG_HT_OVERRIDES
+	/**
+	 * disable_ht - Disable HT (IEEE 802.11n) for this network
+	 *
+	 * By default, use it if it is available, but this can be configured
+	 * to 1 to have it disabled.
+	 */
+	int disable_ht;
+
+	/**
+	 * disable_ht40 - Disable HT40 for this network
+	 *
+	 * By default, use it if it is available, but this can be configured
+	 * to 1 to have it disabled.
+	 */
+	int disable_ht40;
+
+	/**
+	 * disable_sgi - Disable SGI (Short Guard Interval) for this network
+	 *
+	 * By default, use it if it is available, but this can be configured
+	 * to 1 to have it disabled.
+	 */
+	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
+	 * when enabled (assuming it is otherwise supported)
+	 * -1 (default) means do not apply any settings to the kernel.
+	 */
+	int disable_max_amsdu;
+
+	/**
+	 * ampdu_factor - Maximum A-MPDU Length Exponent
+	 *
+	 * Value: 0-3, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
+	 */
+	int ampdu_factor;
+
+	/**
+	 * ampdu_density - Minimum A-MPDU Start Spacing
+	 *
+	 * Value: 0-7, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
+	 */
+	int ampdu_density;
+
+	/**
+	 * ht_mcs - Allowed HT-MCS rates, in ASCII hex: ffff0000...
+	 *
+	 * By default (empty string): Use whatever the OS has configured.
+	 */
+	char *ht_mcs;
+#endif /* CONFIG_HT_OVERRIDES */
+
+#ifdef CONFIG_VHT_OVERRIDES
+	/**
+	 * disable_vht - Disable VHT (IEEE 802.11ac) for this network
+	 *
+	 * By default, use it if it is available, but this can be configured
+	 * to 1 to have it disabled.
+	 */
+	int disable_vht;
+
+	/**
+	 * vht_capa - VHT capabilities to use
+	 */
+	unsigned int vht_capa;
+
+	/**
+	 * vht_capa_mask - mask for VHT capabilities
+	 */
+	unsigned int vht_capa_mask;
+
+	int vht_rx_mcs_nss_1, vht_rx_mcs_nss_2,
+	    vht_rx_mcs_nss_3, vht_rx_mcs_nss_4,
+	    vht_rx_mcs_nss_5, vht_rx_mcs_nss_6,
+	    vht_rx_mcs_nss_7, vht_rx_mcs_nss_8;
+	int vht_tx_mcs_nss_1, vht_tx_mcs_nss_2,
+	    vht_tx_mcs_nss_3, vht_tx_mcs_nss_4,
+	    vht_tx_mcs_nss_5, vht_tx_mcs_nss_6,
+	    vht_tx_mcs_nss_7, vht_tx_mcs_nss_8;
+#endif /* CONFIG_VHT_OVERRIDES */
+
+	/**
+	 * ap_max_inactivity - Timeout in seconds to detect STA's inactivity
+	 *
+	 * This timeout value is used in AP mode to clean up inactive stations.
+	 * By default: 300 seconds.
+	 */
+	int ap_max_inactivity;
+
+	/**
+	 * dtim_period - DTIM period in Beacon intervals
+	 * By default: 2
+	 */
+	int dtim_period;
+
+	/**
+	 * beacon_int - Beacon interval (default: 100 TU)
+	 */
+	int beacon_int;
+
+	/**
+	 * auth_failures - Number of consecutive authentication failures
+	 */
+	unsigned int auth_failures;
+
+	/**
+	 * disabled_until - Network block disabled until this time if non-zero
+	 */
+	struct os_reltime disabled_until;
+
+	/**
+	 * parent_cred - Pointer to parent wpa_cred entry
+	 *
+	 * This pointer can be used to delete temporary networks when a wpa_cred
+	 * that was used to create them is removed. This pointer should not be
+	 * 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 */
+
+	unsigned int wps_run;
+
+	/**
+	 * mac_addr - MAC address policy
+	 *
+	 * 0 = use permanent MAC address
+	 * 1 = use random MAC address for each ESS connection
+	 * 2 = like 1, but maintain OUI (with local admin bit set)
+	 *
+	 * Internally, special value -1 is used to indicate that the parameter
+	 * was not specified in the configuration (i.e., default behavior is
+	 * followed).
+	 */
+	int mac_addr;
+
+	/**
+	 * no_auto_peer - Do not automatically peer with compatible mesh peers
+	 *
+	 * When unset, the reception of a beacon from a another mesh peer in
+	 * this MBSS will trigger a peering attempt.
+	 */
+	int no_auto_peer;
+};
+
+#endif /* CONFIG_SSID_H */
diff --git a/hostap/wpa_supplicant/config_winreg.c b/hostap/wpa_supplicant/config_winreg.c
new file mode 100644
index 0000000..199f04f
--- /dev/null
+++ b/hostap/wpa_supplicant/config_winreg.c
@@ -0,0 +1,1032 @@
+/*
+ * WPA Supplicant / Configuration backend: Windows registry
+ * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements a configuration backend for Windows registry. All the
+ * configuration information is stored in the registry and the format for
+ * network configuration fields is same as described in the sample
+ * configuration file, wpa_supplicant.conf.
+ *
+ * Configuration data is in
+ * \a HKEY_LOCAL_MACHINE\\SOFTWARE\\%wpa_supplicant\\configs
+ * key. Each configuration profile has its own key under this. In terms of text
+ * files, each profile would map to a separate text file with possibly multiple
+ * networks. Under each profile, there is a networks key that lists all
+ * networks as a subkey. Each network has set of values in the same way as
+ * network block in the configuration file. In addition, blobs subkey has
+ * possible blobs as values.
+ *
+ * Example network configuration block:
+ * \verbatim
+HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000
+   ssid="example"
+   key_mgmt=WPA-PSK
+\endverbatim
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "uuid.h"
+#include "config.h"
+
+#ifndef WPA_KEY_ROOT
+#define WPA_KEY_ROOT HKEY_LOCAL_MACHINE
+#endif
+#ifndef WPA_KEY_PREFIX
+#define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant")
+#endif
+
+#ifdef UNICODE
+#define TSTR "%S"
+#else /* UNICODE */
+#define TSTR "%s"
+#endif /* UNICODE */
+
+
+static int wpa_config_read_blobs(struct wpa_config *config, HKEY hk)
+{
+	struct wpa_config_blob *blob;
+	int errors = 0;
+	HKEY bhk;
+	LONG ret;
+	DWORD i;
+
+	ret = RegOpenKeyEx(hk, TEXT("blobs"), 0, KEY_QUERY_VALUE, &bhk);
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "Could not open wpa_supplicant config "
+			   "blobs key");
+		return 0; /* assume no blobs */
+	}
+
+	for (i = 0; ; i++) {
+#define TNAMELEN 255
+		TCHAR name[TNAMELEN];
+		char data[4096];
+		DWORD namelen, datalen, type;
+
+		namelen = TNAMELEN;
+		datalen = sizeof(data);
+		ret = RegEnumValue(bhk, i, name, &namelen, NULL, &type,
+				   (LPBYTE) data, &datalen);
+
+		if (ret == ERROR_NO_MORE_ITEMS)
+			break;
+
+		if (ret != ERROR_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "RegEnumValue failed: 0x%x",
+				   (unsigned int) ret);
+			break;
+		}
+
+		if (namelen >= TNAMELEN)
+			namelen = TNAMELEN - 1;
+		name[namelen] = TEXT('\0');
+		wpa_unicode2ascii_inplace(name);
+
+		if (datalen >= sizeof(data))
+			datalen = sizeof(data) - 1;
+
+		wpa_printf(MSG_MSGDUMP, "blob %d: field='%s' len %d",
+			   (int) i, name, (int) datalen);
+
+		blob = os_zalloc(sizeof(*blob));
+		if (blob == NULL) {
+			errors++;
+			break;
+		}
+		blob->name = os_strdup((char *) name);
+		blob->data = os_malloc(datalen);
+		if (blob->name == NULL || blob->data == NULL) {
+			wpa_config_free_blob(blob);
+			errors++;
+			break;
+		}
+		os_memcpy(blob->data, data, datalen);
+		blob->len = datalen;
+
+		wpa_config_set_blob(config, blob);
+	}
+
+	RegCloseKey(bhk);
+
+	return errors ? -1 : 0;
+}
+
+
+static int wpa_config_read_reg_dword(HKEY hk, const TCHAR *name, int *_val)
+{
+	DWORD val, buflen;
+	LONG ret;
+
+	buflen = sizeof(val);
+	ret = RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE) &val, &buflen);
+	if (ret == ERROR_SUCCESS && buflen == sizeof(val)) {
+		wpa_printf(MSG_DEBUG, TSTR "=%d", name, (int) val);
+		*_val = val;
+		return 0;
+	}
+
+	return -1;
+}
+
+
+static char * wpa_config_read_reg_string(HKEY hk, const TCHAR *name)
+{
+	DWORD buflen;
+	LONG ret;
+	TCHAR *val;
+
+	buflen = 0;
+	ret = RegQueryValueEx(hk, name, NULL, NULL, NULL, &buflen);
+	if (ret != ERROR_SUCCESS)
+		return NULL;
+	val = os_malloc(buflen);
+	if (val == NULL)
+		return NULL;
+
+	ret = RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE) val, &buflen);
+	if (ret != ERROR_SUCCESS) {
+		os_free(val);
+		return NULL;
+	}
+
+	wpa_unicode2ascii_inplace(val);
+	wpa_printf(MSG_DEBUG, TSTR "=%s", name, (char *) val);
+	return (char *) val;
+}
+
+
+#ifdef CONFIG_WPS
+static int wpa_config_read_global_uuid(struct wpa_config *config, HKEY hk)
+{
+	char *str;
+	int ret = 0;
+
+	str = wpa_config_read_reg_string(hk, TEXT("uuid"));
+	if (str == NULL)
+		return 0;
+
+	if (uuid_str2bin(str, config->uuid))
+		ret = -1;
+
+	os_free(str);
+
+	return ret;
+}
+
+
+static int wpa_config_read_global_os_version(struct wpa_config *config,
+					     HKEY hk)
+{
+	char *str;
+	int ret = 0;
+
+	str = wpa_config_read_reg_string(hk, TEXT("os_version"));
+	if (str == NULL)
+		return 0;
+
+	if (hexstr2bin(str, config->os_version, 4))
+		ret = -1;
+
+	os_free(str);
+
+	return ret;
+}
+#endif /* CONFIG_WPS */
+
+
+static int wpa_config_read_global(struct wpa_config *config, HKEY hk)
+{
+	int errors = 0;
+	int val;
+
+	wpa_config_read_reg_dword(hk, TEXT("ap_scan"), &config->ap_scan);
+	wpa_config_read_reg_dword(hk, TEXT("fast_reauth"),
+				  &config->fast_reauth);
+	wpa_config_read_reg_dword(hk, TEXT("dot11RSNAConfigPMKLifetime"),
+				  (int *) &config->dot11RSNAConfigPMKLifetime);
+	wpa_config_read_reg_dword(hk,
+				  TEXT("dot11RSNAConfigPMKReauthThreshold"),
+				  (int *)
+				  &config->dot11RSNAConfigPMKReauthThreshold);
+	wpa_config_read_reg_dword(hk, TEXT("dot11RSNAConfigSATimeout"),
+				  (int *) &config->dot11RSNAConfigSATimeout);
+	wpa_config_read_reg_dword(hk, TEXT("update_config"),
+				  &config->update_config);
+
+	if (wpa_config_read_reg_dword(hk, TEXT("eapol_version"),
+				      &config->eapol_version) == 0) {
+		if (config->eapol_version < 1 ||
+		    config->eapol_version > 2) {
+			wpa_printf(MSG_ERROR, "Invalid EAPOL version (%d)",
+				   config->eapol_version);
+			errors++;
+		}
+	}
+
+	config->ctrl_interface = wpa_config_read_reg_string(
+		hk, TEXT("ctrl_interface"));
+
+#ifdef CONFIG_WPS
+	if (wpa_config_read_global_uuid(config, hk))
+		errors++;
+	config->device_name = wpa_config_read_reg_string(
+		hk, TEXT("device_name"));
+	config->manufacturer = wpa_config_read_reg_string(
+		hk, TEXT("manufacturer"));
+	config->model_name = wpa_config_read_reg_string(
+		hk, TEXT("model_name"));
+	config->serial_number = wpa_config_read_reg_string(
+		hk, TEXT("serial_number"));
+	{
+		char *t = wpa_config_read_reg_string(
+			hk, TEXT("device_type"));
+		if (t && wps_dev_type_str2bin(t, config->device_type))
+			errors++;
+		os_free(t);
+	}
+	config->config_methods = wpa_config_read_reg_string(
+		hk, TEXT("config_methods"));
+	if (wpa_config_read_global_os_version(config, hk))
+		errors++;
+	wpa_config_read_reg_dword(hk, TEXT("wps_cred_processing"),
+				  &config->wps_cred_processing);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+	config->p2p_ssid_postfix = wpa_config_read_reg_string(
+		hk, TEXT("p2p_ssid_postfix"));
+	wpa_config_read_reg_dword(hk, TEXT("p2p_group_idle"),
+				  (int *) &config->p2p_group_idle);
+#endif /* CONFIG_P2P */
+
+	wpa_config_read_reg_dword(hk, TEXT("bss_max_count"),
+				  (int *) &config->bss_max_count);
+	wpa_config_read_reg_dword(hk, TEXT("filter_ssids"),
+				  &config->filter_ssids);
+	wpa_config_read_reg_dword(hk, TEXT("max_num_sta"),
+				  (int *) &config->max_num_sta);
+	wpa_config_read_reg_dword(hk, TEXT("disassoc_low_ack"),
+				  (int *) &config->disassoc_low_ack);
+
+	wpa_config_read_reg_dword(hk, TEXT("okc"), &config->okc);
+	wpa_config_read_reg_dword(hk, TEXT("pmf"), &val);
+	config->pmf = val;
+
+	return errors ? -1 : 0;
+}
+
+
+static struct wpa_ssid * wpa_config_read_network(HKEY hk, const TCHAR *netw,
+						 int id)
+{
+	HKEY nhk;
+	LONG ret;
+	DWORD i;
+	struct wpa_ssid *ssid;
+	int errors = 0;
+
+	ret = RegOpenKeyEx(hk, netw, 0, KEY_QUERY_VALUE, &nhk);
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "Could not open wpa_supplicant config "
+			   "network '" TSTR "'", netw);
+		return NULL;
+	}
+
+	wpa_printf(MSG_MSGDUMP, "Start of a new network '" TSTR "'", netw);
+	ssid = os_zalloc(sizeof(*ssid));
+	if (ssid == NULL) {
+		RegCloseKey(nhk);
+		return NULL;
+	}
+	dl_list_init(&ssid->psk_list);
+	ssid->id = id;
+
+	wpa_config_set_network_defaults(ssid);
+
+	for (i = 0; ; i++) {
+		TCHAR name[255], data[1024];
+		DWORD namelen, datalen, type;
+
+		namelen = 255;
+		datalen = sizeof(data);
+		ret = RegEnumValue(nhk, i, name, &namelen, NULL, &type,
+				   (LPBYTE) data, &datalen);
+
+		if (ret == ERROR_NO_MORE_ITEMS)
+			break;
+
+		if (ret != ERROR_SUCCESS) {
+			wpa_printf(MSG_ERROR, "RegEnumValue failed: 0x%x",
+				   (unsigned int) ret);
+			break;
+		}
+
+		if (namelen >= 255)
+			namelen = 255 - 1;
+		name[namelen] = TEXT('\0');
+
+		if (datalen >= 1024)
+			datalen = 1024 - 1;
+		data[datalen] = TEXT('\0');
+
+		wpa_unicode2ascii_inplace(name);
+		wpa_unicode2ascii_inplace(data);
+		if (wpa_config_set(ssid, (char *) name, (char *) data, 0) < 0)
+			errors++;
+	}
+
+	RegCloseKey(nhk);
+
+	if (ssid->passphrase) {
+		if (ssid->psk_set) {
+			wpa_printf(MSG_ERROR, "Both PSK and passphrase "
+				   "configured for network '" TSTR "'.", netw);
+			errors++;
+		}
+		wpa_config_update_psk(ssid);
+	}
+
+	if ((ssid->group_cipher & WPA_CIPHER_CCMP) &&
+	    !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
+	    !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) {
+		/* Group cipher cannot be stronger than the pairwise cipher. */
+		wpa_printf(MSG_DEBUG, "Removed CCMP from group cipher "
+			   "list since it was not allowed for pairwise "
+			   "cipher for network '" TSTR "'.", netw);
+		ssid->group_cipher &= ~WPA_CIPHER_CCMP;
+	}
+
+	if (errors) {
+		wpa_config_free_ssid(ssid);
+		ssid = NULL;
+	}
+
+	return ssid;
+}
+
+
+static int wpa_config_read_networks(struct wpa_config *config, HKEY hk)
+{
+	HKEY nhk;
+	struct wpa_ssid *ssid, *tail = NULL, *head = NULL;
+	int errors = 0;
+	LONG ret;
+	DWORD i;
+
+	ret = RegOpenKeyEx(hk, TEXT("networks"), 0, KEY_ENUMERATE_SUB_KEYS,
+			   &nhk);
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_ERROR, "Could not open wpa_supplicant networks "
+			   "registry key");
+		return -1;
+	}
+
+	for (i = 0; ; i++) {
+		TCHAR name[255];
+		DWORD namelen;
+
+		namelen = 255;
+		ret = RegEnumKeyEx(nhk, i, name, &namelen, NULL, NULL, NULL,
+				   NULL);
+
+		if (ret == ERROR_NO_MORE_ITEMS)
+			break;
+
+		if (ret != ERROR_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "RegEnumKeyEx failed: 0x%x",
+				   (unsigned int) ret);
+			break;
+		}
+
+		if (namelen >= 255)
+			namelen = 255 - 1;
+		name[namelen] = '\0';
+
+		ssid = wpa_config_read_network(nhk, name, i);
+		if (ssid == NULL) {
+			wpa_printf(MSG_ERROR, "Failed to parse network "
+				   "profile '%s'.", name);
+			errors++;
+			continue;
+		}
+		if (head == NULL) {
+			head = tail = ssid;
+		} else {
+			tail->next = ssid;
+			tail = ssid;
+		}
+		if (wpa_config_add_prio_network(config, ssid)) {
+			wpa_printf(MSG_ERROR, "Failed to add network profile "
+				   "'%s' to priority list.", name);
+			errors++;
+			continue;
+		}
+	}
+
+	RegCloseKey(nhk);
+
+	config->ssid = head;
+
+	return errors ? -1 : 0;
+}
+
+
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
+{
+	TCHAR buf[256];
+	int errors = 0;
+	struct wpa_config *config;
+	HKEY hk;
+	LONG ret;
+
+	if (name == NULL)
+		return NULL;
+	if (cfgp)
+		config = cfgp;
+	else
+		config = wpa_config_alloc_empty(NULL, NULL);
+	if (config == NULL)
+		return NULL;
+	wpa_printf(MSG_DEBUG, "Reading configuration profile '%s'", name);
+
+#ifdef UNICODE
+	_snwprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%S"), name);
+#else /* UNICODE */
+	os_snprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%s"), name);
+#endif /* UNICODE */
+
+	ret = RegOpenKeyEx(WPA_KEY_ROOT, buf, 0, KEY_QUERY_VALUE, &hk);
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_ERROR, "Could not open wpa_supplicant "
+			   "configuration registry HKLM\\" TSTR, buf);
+		os_free(config);
+		return NULL;
+	}
+
+	if (wpa_config_read_global(config, hk))
+		errors++;
+
+	if (wpa_config_read_networks(config, hk))
+		errors++;
+
+	if (wpa_config_read_blobs(config, hk))
+		errors++;
+
+	wpa_config_debug_dump_networks(config);
+
+	RegCloseKey(hk);
+
+	if (errors) {
+		wpa_config_free(config);
+		config = NULL;
+	}
+
+	return config;
+}
+
+
+static int wpa_config_write_reg_dword(HKEY hk, const TCHAR *name, int val,
+				      int def)
+{
+	LONG ret;
+	DWORD _val = val;
+
+	if (val == def) {
+		RegDeleteValue(hk, name);
+		return 0;
+	}
+
+	ret = RegSetValueEx(hk, name, 0, REG_DWORD, (LPBYTE) &_val,
+			    sizeof(_val));
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_ERROR, "WINREG: Failed to set %s=%d: error %d",
+			   name, val, (int) GetLastError());
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_config_write_reg_string(HKEY hk, const char *name,
+				       const char *val)
+{
+	LONG ret;
+	TCHAR *_name, *_val;
+
+	_name = wpa_strdup_tchar(name);
+	if (_name == NULL)
+		return -1;
+
+	if (val == NULL) {
+		RegDeleteValue(hk, _name);
+		os_free(_name);
+		return 0;
+	}
+
+	_val = wpa_strdup_tchar(val);
+	if (_val == NULL) {
+		os_free(_name);
+		return -1;
+	}
+	ret = RegSetValueEx(hk, _name, 0, REG_SZ, (BYTE *) _val,
+			    (os_strlen(val) + 1) * sizeof(TCHAR));
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_ERROR, "WINREG: Failed to set %s='%s': "
+			   "error %d", name, val, (int) GetLastError());
+		os_free(_name);
+		os_free(_val);
+		return -1;
+	}
+
+	os_free(_name);
+	os_free(_val);
+	return 0;
+}
+
+
+static int wpa_config_write_global(struct wpa_config *config, HKEY hk)
+{
+#ifdef CONFIG_CTRL_IFACE
+	wpa_config_write_reg_string(hk, "ctrl_interface",
+				    config->ctrl_interface);
+#endif /* CONFIG_CTRL_IFACE */
+
+	wpa_config_write_reg_dword(hk, TEXT("eapol_version"),
+				   config->eapol_version,
+				   DEFAULT_EAPOL_VERSION);
+	wpa_config_write_reg_dword(hk, TEXT("ap_scan"), config->ap_scan,
+				   DEFAULT_AP_SCAN);
+	wpa_config_write_reg_dword(hk, TEXT("fast_reauth"),
+				   config->fast_reauth, DEFAULT_FAST_REAUTH);
+	wpa_config_write_reg_dword(hk, TEXT("dot11RSNAConfigPMKLifetime"),
+				   config->dot11RSNAConfigPMKLifetime, 0);
+	wpa_config_write_reg_dword(hk,
+				   TEXT("dot11RSNAConfigPMKReauthThreshold"),
+				   config->dot11RSNAConfigPMKReauthThreshold,
+				   0);
+	wpa_config_write_reg_dword(hk, TEXT("dot11RSNAConfigSATimeout"),
+				   config->dot11RSNAConfigSATimeout, 0);
+	wpa_config_write_reg_dword(hk, TEXT("update_config"),
+				   config->update_config,
+				   0);
+#ifdef CONFIG_WPS
+	if (!is_nil_uuid(config->uuid)) {
+		char buf[40];
+		uuid_bin2str(config->uuid, buf, sizeof(buf));
+		wpa_config_write_reg_string(hk, "uuid", buf);
+	}
+	wpa_config_write_reg_string(hk, "device_name", config->device_name);
+	wpa_config_write_reg_string(hk, "manufacturer", config->manufacturer);
+	wpa_config_write_reg_string(hk, "model_name", config->model_name);
+	wpa_config_write_reg_string(hk, "model_number", config->model_number);
+	wpa_config_write_reg_string(hk, "serial_number",
+				    config->serial_number);
+	{
+		char _buf[WPS_DEV_TYPE_BUFSIZE], *buf;
+		buf = wps_dev_type_bin2str(config->device_type,
+					   _buf, sizeof(_buf));
+		wpa_config_write_reg_string(hk, "device_type", buf);
+	}
+	wpa_config_write_reg_string(hk, "config_methods",
+				    config->config_methods);
+	if (WPA_GET_BE32(config->os_version)) {
+		char vbuf[10];
+		os_snprintf(vbuf, sizeof(vbuf), "%08x",
+			    WPA_GET_BE32(config->os_version));
+		wpa_config_write_reg_string(hk, "os_version", vbuf);
+	}
+	wpa_config_write_reg_dword(hk, TEXT("wps_cred_processing"),
+				   config->wps_cred_processing, 0);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+	wpa_config_write_reg_string(hk, "p2p_ssid_postfix",
+				    config->p2p_ssid_postfix);
+	wpa_config_write_reg_dword(hk, TEXT("p2p_group_idle"),
+				   config->p2p_group_idle, 0);
+#endif /* CONFIG_P2P */
+
+	wpa_config_write_reg_dword(hk, TEXT("bss_max_count"),
+				   config->bss_max_count,
+				   DEFAULT_BSS_MAX_COUNT);
+	wpa_config_write_reg_dword(hk, TEXT("filter_ssids"),
+				   config->filter_ssids, 0);
+	wpa_config_write_reg_dword(hk, TEXT("max_num_sta"),
+				   config->max_num_sta, DEFAULT_MAX_NUM_STA);
+	wpa_config_write_reg_dword(hk, TEXT("disassoc_low_ack"),
+				   config->disassoc_low_ack, 0);
+
+	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;
+}
+
+
+static int wpa_config_delete_subkeys(HKEY hk, const TCHAR *key)
+{
+	HKEY nhk;
+	int i, errors = 0;
+	LONG ret;
+
+	ret = RegOpenKeyEx(hk, key, 0, KEY_ENUMERATE_SUB_KEYS | DELETE, &nhk);
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "WINREG: Could not open key '" TSTR
+			   "' for subkey deletion: error 0x%x (%d)", key,
+			   (unsigned int) ret, (int) GetLastError());
+		return 0;
+	}
+
+	for (i = 0; ; i++) {
+		TCHAR name[255];
+		DWORD namelen;
+
+		namelen = 255;
+		ret = RegEnumKeyEx(nhk, i, name, &namelen, NULL, NULL, NULL,
+				   NULL);
+
+		if (ret == ERROR_NO_MORE_ITEMS)
+			break;
+
+		if (ret != ERROR_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "RegEnumKeyEx failed: 0x%x (%d)",
+				   (unsigned int) ret, (int) GetLastError());
+			break;
+		}
+
+		if (namelen >= 255)
+			namelen = 255 - 1;
+		name[namelen] = TEXT('\0');
+
+		ret = RegDeleteKey(nhk, name);
+		if (ret != ERROR_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "RegDeleteKey failed: 0x%x (%d)",
+				   (unsigned int) ret, (int) GetLastError());
+			errors++;
+		}
+	}
+
+	RegCloseKey(nhk);
+
+	return errors ? -1 : 0;
+}
+
+
+static void write_str(HKEY hk, const char *field, struct wpa_ssid *ssid)
+{
+	char *value = wpa_config_get(ssid, field);
+	if (value == NULL)
+		return;
+	wpa_config_write_reg_string(hk, field, value);
+	os_free(value);
+}
+
+
+static void write_int(HKEY hk, const char *field, int value, int def)
+{
+	char val[20];
+	if (value == def)
+		return;
+	os_snprintf(val, sizeof(val), "%d", value);
+	wpa_config_write_reg_string(hk, field, val);
+}
+
+
+static void write_bssid(HKEY hk, struct wpa_ssid *ssid)
+{
+	char *value = wpa_config_get(ssid, "bssid");
+	if (value == NULL)
+		return;
+	wpa_config_write_reg_string(hk, "bssid", value);
+	os_free(value);
+}
+
+
+static void write_psk(HKEY hk, struct wpa_ssid *ssid)
+{
+	char *value = wpa_config_get(ssid, "psk");
+	if (value == NULL)
+		return;
+	wpa_config_write_reg_string(hk, "psk", value);
+	os_free(value);
+}
+
+
+static void write_proto(HKEY hk, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (ssid->proto == DEFAULT_PROTO)
+		return;
+
+	value = wpa_config_get(ssid, "proto");
+	if (value == NULL)
+		return;
+	if (value[0])
+		wpa_config_write_reg_string(hk, "proto", value);
+	os_free(value);
+}
+
+
+static void write_key_mgmt(HKEY hk, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (ssid->key_mgmt == DEFAULT_KEY_MGMT)
+		return;
+
+	value = wpa_config_get(ssid, "key_mgmt");
+	if (value == NULL)
+		return;
+	if (value[0])
+		wpa_config_write_reg_string(hk, "key_mgmt", value);
+	os_free(value);
+}
+
+
+static void write_pairwise(HKEY hk, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (ssid->pairwise_cipher == DEFAULT_PAIRWISE)
+		return;
+
+	value = wpa_config_get(ssid, "pairwise");
+	if (value == NULL)
+		return;
+	if (value[0])
+		wpa_config_write_reg_string(hk, "pairwise", value);
+	os_free(value);
+}
+
+
+static void write_group(HKEY hk, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (ssid->group_cipher == DEFAULT_GROUP)
+		return;
+
+	value = wpa_config_get(ssid, "group");
+	if (value == NULL)
+		return;
+	if (value[0])
+		wpa_config_write_reg_string(hk, "group", value);
+	os_free(value);
+}
+
+
+static void write_auth_alg(HKEY hk, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (ssid->auth_alg == 0)
+		return;
+
+	value = wpa_config_get(ssid, "auth_alg");
+	if (value == NULL)
+		return;
+	if (value[0])
+		wpa_config_write_reg_string(hk, "auth_alg", value);
+	os_free(value);
+}
+
+
+#ifdef IEEE8021X_EAPOL
+static void write_eap(HKEY hk, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	value = wpa_config_get(ssid, "eap");
+	if (value == NULL)
+		return;
+
+	if (value[0])
+		wpa_config_write_reg_string(hk, "eap", value);
+	os_free(value);
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+static void write_wep_key(HKEY hk, int idx, struct wpa_ssid *ssid)
+{
+	char field[20], *value;
+
+	os_snprintf(field, sizeof(field), "wep_key%d", idx);
+	value = wpa_config_get(ssid, field);
+	if (value) {
+		wpa_config_write_reg_string(hk, field, value);
+		os_free(value);
+	}
+}
+
+
+static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id)
+{
+	int i, errors = 0;
+	HKEY nhk, netw;
+	LONG ret;
+	TCHAR name[5];
+
+	ret = RegOpenKeyEx(hk, TEXT("networks"), 0, KEY_CREATE_SUB_KEY, &nhk);
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "WINREG: Could not open networks key "
+			   "for subkey addition: error 0x%x (%d)",
+			   (unsigned int) ret, (int) GetLastError());
+		return 0;
+	}
+
+#ifdef UNICODE
+	wsprintf(name, L"%04d", id);
+#else /* UNICODE */
+	os_snprintf(name, sizeof(name), "%04d", id);
+#endif /* UNICODE */
+	ret = RegCreateKeyEx(nhk, name, 0, NULL, 0, KEY_WRITE, NULL, &netw,
+			     NULL);
+	RegCloseKey(nhk);
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "WINREG: Could not add network key '%s':"
+			   " error 0x%x (%d)",
+			   name, (unsigned int) ret, (int) GetLastError());
+		return -1;
+	}
+
+#define STR(t) write_str(netw, #t, ssid)
+#define INT(t) write_int(netw, #t, ssid->t, 0)
+#define INTe(t) write_int(netw, #t, ssid->eap.t, 0)
+#define INT_DEF(t, def) write_int(netw, #t, ssid->t, def)
+#define INT_DEFe(t, def) write_int(netw, #t, ssid->eap.t, def)
+
+	STR(ssid);
+	INT(scan_ssid);
+	write_bssid(netw, ssid);
+	write_psk(netw, ssid);
+	write_proto(netw, ssid);
+	write_key_mgmt(netw, ssid);
+	write_pairwise(netw, ssid);
+	write_group(netw, ssid);
+	write_auth_alg(netw, ssid);
+#ifdef IEEE8021X_EAPOL
+	write_eap(netw, ssid);
+	STR(identity);
+	STR(anonymous_identity);
+	STR(password);
+	STR(ca_cert);
+	STR(ca_path);
+	STR(client_cert);
+	STR(private_key);
+	STR(private_key_passwd);
+	STR(dh_file);
+	STR(subject_match);
+	STR(altsubject_match);
+	STR(ca_cert2);
+	STR(ca_path2);
+	STR(client_cert2);
+	STR(private_key2);
+	STR(private_key2_passwd);
+	STR(dh_file2);
+	STR(subject_match2);
+	STR(altsubject_match2);
+	STR(phase1);
+	STR(phase2);
+	STR(pcsc);
+	STR(pin);
+	STR(engine_id);
+	STR(key_id);
+	STR(cert_id);
+	STR(ca_cert_id);
+	STR(key2_id);
+	STR(pin2);
+	STR(engine2_id);
+	STR(cert2_id);
+	STR(ca_cert2_id);
+	INTe(engine);
+	INTe(engine2);
+	INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS);
+#endif /* IEEE8021X_EAPOL */
+	for (i = 0; i < 4; i++)
+		write_wep_key(netw, i, ssid);
+	INT(wep_tx_keyidx);
+	INT(priority);
+#ifdef IEEE8021X_EAPOL
+	INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND);
+	STR(pac_file);
+	INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE);
+#endif /* IEEE8021X_EAPOL */
+	INT(mode);
+	write_int(netw, "proactive_key_caching", ssid->proactive_key_caching,
+		  -1);
+	INT(disabled);
+	INT(peerkey);
+#ifdef CONFIG_IEEE80211W
+	write_int(netw, "ieee80211w", ssid->ieee80211w,
+		  MGMT_FRAME_PROTECTION_DEFAULT);
+#endif /* CONFIG_IEEE80211W */
+	STR(id_str);
+#ifdef CONFIG_HS20
+	INT(update_identifier);
+#endif /* CONFIG_HS20 */
+
+#undef STR
+#undef INT
+#undef INT_DEF
+
+	RegCloseKey(netw);
+
+	return errors ? -1 : 0;
+}
+
+
+static int wpa_config_write_blob(HKEY hk, struct wpa_config_blob *blob)
+{
+	HKEY bhk;
+	LONG ret;
+	TCHAR *name;
+
+	ret = RegCreateKeyEx(hk, TEXT("blobs"), 0, NULL, 0, KEY_WRITE, NULL,
+			     &bhk, NULL);
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "WINREG: Could not add blobs key: "
+			   "error 0x%x (%d)",
+			   (unsigned int) ret, (int) GetLastError());
+		return -1;
+	}
+
+	name = wpa_strdup_tchar(blob->name);
+	ret = RegSetValueEx(bhk, name, 0, REG_BINARY, blob->data,
+			    blob->len);
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_ERROR, "WINREG: Failed to set blob %s': "
+			   "error 0x%x (%d)", blob->name, (unsigned int) ret,
+			   (int) GetLastError());
+		RegCloseKey(bhk);
+		os_free(name);
+		return -1;
+	}
+	os_free(name);
+
+	RegCloseKey(bhk);
+
+	return 0;
+}
+
+
+int wpa_config_write(const char *name, struct wpa_config *config)
+{
+	TCHAR buf[256];
+	HKEY hk;
+	LONG ret;
+	int errors = 0;
+	struct wpa_ssid *ssid;
+	struct wpa_config_blob *blob;
+	int id;
+
+	wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name);
+
+#ifdef UNICODE
+	_snwprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%S"), name);
+#else /* UNICODE */
+	os_snprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%s"), name);
+#endif /* UNICODE */
+
+	ret = RegOpenKeyEx(WPA_KEY_ROOT, buf, 0, KEY_SET_VALUE | DELETE, &hk);
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_ERROR, "Could not open wpa_supplicant "
+			   "configuration registry %s: error %d", buf,
+			   (int) GetLastError());
+		return -1;
+	}
+
+	if (wpa_config_write_global(config, hk)) {
+		wpa_printf(MSG_ERROR, "Failed to write global configuration "
+			   "data");
+		errors++;
+	}
+
+	wpa_config_delete_subkeys(hk, TEXT("networks"));
+	for (ssid = config->ssid, id = 0; ssid; ssid = ssid->next, id++) {
+		if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
+			continue; /* do not save temporary WPS networks */
+		if (wpa_config_write_network(hk, ssid, id))
+			errors++;
+	}
+
+	RegDeleteKey(hk, TEXT("blobs"));
+	for (blob = config->blobs; blob; blob = blob->next) {
+		if (wpa_config_write_blob(hk, blob))
+			errors++;
+	}
+
+	RegCloseKey(hk);
+
+	wpa_printf(MSG_DEBUG, "Configuration '%s' written %ssuccessfully",
+		   name, errors ? "un" : "");
+	return errors ? -1 : 0;
+}
diff --git a/hostap/wpa_supplicant/ctrl_iface.c b/hostap/wpa_supplicant/ctrl_iface.c
new file mode 100644
index 0000000..76d9d3e
--- /dev/null
+++ b/hostap/wpa_supplicant/ctrl_iface.c
@@ -0,0 +1,9480 @@
+/*
+ * WPA Supplicant / Control interface (shared code for all backends)
+ * Copyright (c) 2004-2015, 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"
+#ifdef CONFIG_TESTING_OPTIONS
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#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"
+#include "common/wpa_ctrl.h"
+#include "crypto/tls.h"
+#include "ap/hostapd.h"
+#include "eap_peer/eap.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/preauth.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "l2_packet/l2_packet.h"
+#include "wps/wps.h"
+#include "fst/fst.h"
+#include "fst/fst_ctrl_iface.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "wps_supplicant.h"
+#include "ibss_rsn.h"
+#include "ap.h"
+#include "p2p_supplicant.h"
+#include "p2p/p2p.h"
+#include "hs20_supplicant.h"
+#include "wifi_display.h"
+#include "notify.h"
+#include "bss.h"
+#include "scan.h"
+#include "ctrl_iface.h"
+#include "interworking.h"
+#include "blacklist.h"
+#include "autoscan.h"
+#include "wnm_sta.h"
+#include "offchannel.h"
+#include "drivers/driver.h"
+#include "mesh.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 * freq_range_to_channel_list(struct wpa_supplicant *wpa_s,
+					char *val);
+
+static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val)
+{
+	char *pos;
+	u8 addr[ETH_ALEN], *filter = NULL, *n;
+	size_t count = 0;
+
+	pos = val;
+	while (pos) {
+		if (*pos == '\0')
+			break;
+		if (hwaddr_aton(pos, addr)) {
+			os_free(filter);
+			return -1;
+		}
+		n = os_realloc_array(filter, count + 1, ETH_ALEN);
+		if (n == NULL) {
+			os_free(filter);
+			return -1;
+		}
+		filter = n;
+		os_memcpy(filter + count * ETH_ALEN, addr, ETH_ALEN);
+		count++;
+
+		pos = os_strchr(pos, ' ');
+		if (pos)
+			pos++;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "bssid_filter", filter, count * ETH_ALEN);
+	os_free(wpa_s->bssid_filter);
+	wpa_s->bssid_filter = filter;
+	wpa_s->bssid_filter_count = count;
+
+	return 0;
+}
+
+
+static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val)
+{
+	char *pos;
+	u8 addr[ETH_ALEN], *bssid = NULL, *n;
+	struct wpa_ssid_value *ssid = NULL, *ns;
+	size_t count = 0, ssid_count = 0;
+	struct wpa_ssid *c;
+
+	/*
+	 * disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | ""
+	 * SSID_SPEC ::= ssid <SSID_HEX>
+	 * BSSID_SPEC ::= bssid <BSSID_HEX>
+	 */
+
+	pos = val;
+	while (pos) {
+		if (*pos == '\0')
+			break;
+		if (os_strncmp(pos, "bssid ", 6) == 0) {
+			int res;
+			pos += 6;
+			res = hwaddr_aton2(pos, addr);
+			if (res < 0) {
+				os_free(ssid);
+				os_free(bssid);
+				wpa_printf(MSG_DEBUG, "Invalid disallow_aps "
+					   "BSSID value '%s'", pos);
+				return -1;
+			}
+			pos += res;
+			n = os_realloc_array(bssid, count + 1, ETH_ALEN);
+			if (n == NULL) {
+				os_free(ssid);
+				os_free(bssid);
+				return -1;
+			}
+			bssid = n;
+			os_memcpy(bssid + count * ETH_ALEN, addr, ETH_ALEN);
+			count++;
+		} else if (os_strncmp(pos, "ssid ", 5) == 0) {
+			char *end;
+			pos += 5;
+
+			end = pos;
+			while (*end) {
+				if (*end == '\0' || *end == ' ')
+					break;
+				end++;
+			}
+
+			ns = os_realloc_array(ssid, ssid_count + 1,
+					      sizeof(struct wpa_ssid_value));
+			if (ns == NULL) {
+				os_free(ssid);
+				os_free(bssid);
+				return -1;
+			}
+			ssid = ns;
+
+			if ((end - pos) & 0x01 ||
+			    end - pos > 2 * SSID_MAX_LEN ||
+			    hexstr2bin(pos, ssid[ssid_count].ssid,
+				       (end - pos) / 2) < 0) {
+				os_free(ssid);
+				os_free(bssid);
+				wpa_printf(MSG_DEBUG, "Invalid disallow_aps "
+					   "SSID value '%s'", pos);
+				return -1;
+			}
+			ssid[ssid_count].ssid_len = (end - pos) / 2;
+			wpa_hexdump_ascii(MSG_DEBUG, "disallow_aps SSID",
+					  ssid[ssid_count].ssid,
+					  ssid[ssid_count].ssid_len);
+			ssid_count++;
+			pos = end;
+		} else {
+			wpa_printf(MSG_DEBUG, "Unexpected disallow_aps value "
+				   "'%s'", pos);
+			os_free(ssid);
+			os_free(bssid);
+			return -1;
+		}
+
+		pos = os_strchr(pos, ' ');
+		if (pos)
+			pos++;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "disallow_aps_bssid", bssid, count * ETH_ALEN);
+	os_free(wpa_s->disallow_aps_bssid);
+	wpa_s->disallow_aps_bssid = bssid;
+	wpa_s->disallow_aps_bssid_count = count;
+
+	wpa_printf(MSG_DEBUG, "disallow_aps_ssid_count %d", (int) ssid_count);
+	os_free(wpa_s->disallow_aps_ssid);
+	wpa_s->disallow_aps_ssid = ssid;
+	wpa_s->disallow_aps_ssid_count = ssid_count;
+
+	if (!wpa_s->current_ssid || wpa_s->wpa_state < WPA_AUTHENTICATING)
+		return 0;
+
+	c = wpa_s->current_ssid;
+	if (c->mode != WPAS_MODE_INFRA && c->mode != WPAS_MODE_IBSS)
+		return 0;
+
+	if (!disallowed_bssid(wpa_s, wpa_s->bssid) &&
+	    !disallowed_ssid(wpa_s, c->ssid, c->ssid_len))
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "Disconnect and try to find another network "
+		   "because current AP was marked disallowed");
+
+#ifdef CONFIG_SME
+	wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+	wpa_s->reassociate = 1;
+	wpa_s->own_disconnect_req = 1;
+	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+	return 0;
+}
+
+
+#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 wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *band)
+{
+	union wpa_event_data event;
+
+	if (os_strcmp(band, "AUTO") == 0)
+		wpa_s->setband = WPA_SETBAND_AUTO;
+	else if (os_strcmp(band, "5G") == 0)
+		wpa_s->setband = WPA_SETBAND_5G;
+	else if (os_strcmp(band, "2G") == 0)
+		wpa_s->setband = WPA_SETBAND_2G;
+	else
+		return -1;
+
+	if (wpa_drv_setband(wpa_s, wpa_s->setband) == 0) {
+		os_memset(&event, 0, sizeof(event));
+		event.channel_list_changed.initiator = REGDOM_SET_BY_USER;
+		event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN;
+		wpa_supplicant_event(wpa_s, EVENT_CHANNEL_LIST_CHANGED, &event);
+	}
+
+	return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
+					 char *cmd)
+{
+	char *value;
+	int ret = 0;
+
+	value = os_strchr(cmd, ' ');
+	if (value == NULL)
+		return -1;
+	*value++ = '\0';
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value);
+	if (os_strcasecmp(cmd, "EAPOL::heldPeriod") == 0) {
+		eapol_sm_configure(wpa_s->eapol,
+				   atoi(value), -1, -1, -1);
+	} else if (os_strcasecmp(cmd, "EAPOL::authPeriod") == 0) {
+		eapol_sm_configure(wpa_s->eapol,
+				   -1, atoi(value), -1, -1);
+	} else if (os_strcasecmp(cmd, "EAPOL::startPeriod") == 0) {
+		eapol_sm_configure(wpa_s->eapol,
+				   -1, -1, atoi(value), -1);
+	} else if (os_strcasecmp(cmd, "EAPOL::maxStart") == 0) {
+		eapol_sm_configure(wpa_s->eapol,
+				   -1, -1, -1, atoi(value));
+	} else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) {
+		if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
+				     atoi(value)))
+			ret = -1;
+	} else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") ==
+		   0) {
+		if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
+				     atoi(value)))
+			ret = -1;
+	} else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) {
+		if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value)))
+			ret = -1;
+	} else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) {
+		wpa_s->wps_fragment_size = atoi(value);
+#ifdef CONFIG_WPS_TESTING
+	} else if (os_strcasecmp(cmd, "wps_version_number") == 0) {
+		long int val;
+		val = strtol(value, NULL, 0);
+		if (val < 0 || val > 0xff) {
+			ret = -1;
+			wpa_printf(MSG_DEBUG, "WPS: Invalid "
+				   "wps_version_number %ld", val);
+		} else {
+			wps_version_number = val;
+			wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS "
+				   "version %u.%u",
+				   (wps_version_number & 0xf0) >> 4,
+				   wps_version_number & 0x0f);
+		}
+	} else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) {
+		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 */
+	} else if (os_strcasecmp(cmd, "tdls_disabled") == 0) {
+		int disabled = atoi(value);
+		wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled);
+		if (disabled) {
+			if (wpa_drv_tdls_oper(wpa_s, TDLS_DISABLE, NULL) < 0)
+				ret = -1;
+		} else if (wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL) < 0)
+			ret = -1;
+		wpa_tdls_enable(wpa_s->wpa, !disabled);
+#endif /* CONFIG_TDLS */
+	} else if (os_strcasecmp(cmd, "pno") == 0) {
+		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)
+			ret = -1;
+		else if (disabled)
+			wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+	} else if (os_strcasecmp(cmd, "uapsd") == 0) {
+		if (os_strcmp(value, "disable") == 0)
+			wpa_s->set_sta_uapsd = 0;
+		else {
+			int be, bk, vi, vo;
+			char *pos;
+			/* format: BE,BK,VI,VO;max SP Length */
+			be = atoi(value);
+			pos = os_strchr(value, ',');
+			if (pos == NULL)
+				return -1;
+			pos++;
+			bk = atoi(pos);
+			pos = os_strchr(pos, ',');
+			if (pos == NULL)
+				return -1;
+			pos++;
+			vi = atoi(pos);
+			pos = os_strchr(pos, ',');
+			if (pos == NULL)
+				return -1;
+			pos++;
+			vo = atoi(pos);
+			/* ignore max SP Length for now */
+
+			wpa_s->set_sta_uapsd = 1;
+			wpa_s->sta_uapsd = 0;
+			if (be)
+				wpa_s->sta_uapsd |= BIT(0);
+			if (bk)
+				wpa_s->sta_uapsd |= BIT(1);
+			if (vi)
+				wpa_s->sta_uapsd |= BIT(2);
+			if (vo)
+				wpa_s->sta_uapsd |= BIT(3);
+		}
+	} else if (os_strcasecmp(cmd, "ps") == 0) {
+		ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1);
+#ifdef CONFIG_WIFI_DISPLAY
+	} else if (os_strcasecmp(cmd, "wifi_display") == 0) {
+		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);
+	} else if (os_strcasecmp(cmd, "disallow_aps") == 0) {
+		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);
+	} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
+		wpa_s->ext_eapol_frame_io = !!atoi(value);
+#ifdef CONFIG_AP
+		if (wpa_s->ap_iface) {
+			wpa_s->ap_iface->bss[0]->ext_eapol_frame_io =
+				wpa_s->ext_eapol_frame_io;
+		}
+#endif /* CONFIG_AP */
+	} else if (os_strcasecmp(cmd, "extra_roc_dur") == 0) {
+		wpa_s->extra_roc_dur = atoi(value);
+	} else if (os_strcasecmp(cmd, "test_failure") == 0) {
+		wpa_s->test_failure = 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 if (os_strcasecmp(cmd, "setband") == 0) {
+		ret = wpas_ctrl_set_band(wpa_s, value);
+	} else {
+		value[-1] = '=';
+		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
+		if (ret == 0)
+			wpa_supplicant_update_config(wpa_s);
+	}
+
+	return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s,
+					 char *cmd, char *buf, size_t buflen)
+{
+	int res = -1;
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
+
+	if (os_strcmp(cmd, "version") == 0) {
+		res = os_snprintf(buf, buflen, "%s", VERSION_STR);
+	} else if (os_strcasecmp(cmd, "country") == 0) {
+		if (wpa_s->conf->country[0] && wpa_s->conf->country[1])
+			res = os_snprintf(buf, buflen, "%c%c",
+					  wpa_s->conf->country[0],
+					  wpa_s->conf->country[1]);
+#ifdef CONFIG_WIFI_DISPLAY
+	} else if (os_strcasecmp(cmd, "wifi_display") == 0) {
+		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);
+#endif /* CONFIG_WIFI_DISPLAY */
+#ifdef CONFIG_TESTING_GET_GTK
+	} else if (os_strcmp(cmd, "gtk") == 0) {
+		if (wpa_s->last_gtk_len == 0)
+			return -1;
+		res = wpa_snprintf_hex(buf, buflen, wpa_s->last_gtk,
+				       wpa_s->last_gtk_len);
+		return res;
+#endif /* CONFIG_TESTING_GET_GTK */
+	} else if (os_strcmp(cmd, "tls_library") == 0) {
+		res = tls_get_library_version(buf, buflen);
+	} else {
+		res = wpa_config_get_value(cmd, wpa_s->conf, buf, buflen);
+	}
+
+	if (os_snprintf_error(buflen, res))
+		return -1;
+	return res;
+}
+
+
+#ifdef IEEE8021X_EAPOL
+static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s,
+					     char *addr)
+{
+	u8 bssid[ETH_ALEN];
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (hwaddr_aton(addr, bssid)) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH: invalid address "
+			   "'%s'", addr);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid));
+	rsn_preauth_deinit(wpa_s->wpa);
+	if (rsn_preauth_init(wpa_s->wpa, bssid, ssid ? &ssid->eap : NULL))
+		return -1;
+
+	return 0;
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+#ifdef CONFIG_PEERKEY
+/* MLME-STKSTART.request(peer) */
+static int wpa_supplicant_ctrl_iface_stkstart(
+	struct wpa_supplicant *wpa_s, char *addr)
+{
+	u8 peer[ETH_ALEN];
+
+	if (hwaddr_aton(addr, peer)) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART: invalid "
+			   "address '%s'", addr);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART " MACSTR,
+		   MAC2STR(peer));
+
+	return wpa_sm_stkstart(wpa_s->wpa, peer);
+}
+#endif /* CONFIG_PEERKEY */
+
+
+#ifdef CONFIG_TDLS
+
+static int wpa_supplicant_ctrl_iface_tdls_discover(
+	struct wpa_supplicant *wpa_s, char *addr)
+{
+	u8 peer[ETH_ALEN];
+	int ret;
+
+	if (hwaddr_aton(addr, peer)) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER: invalid "
+			   "address '%s'", addr);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE 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);
+
+	return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_setup(
+	struct wpa_supplicant *wpa_s, char *addr)
+{
+	u8 peer[ETH_ALEN];
+	int ret;
+
+	if (hwaddr_aton(addr, peer)) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP: invalid "
+			   "address '%s'", addr);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP " MACSTR,
+		   MAC2STR(peer));
+
+	if ((wpa_s->conf->tdls_external_control) &&
+	    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);
+
+	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);
+
+	return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_teardown(
+	struct wpa_supplicant *wpa_s, char *addr)
+{
+	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);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN " MACSTR,
+		   MAC2STR(peer));
+
+	if ((wpa_s->conf->tdls_external_control) &&
+	    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(
+			wpa_s->wpa, peer,
+			WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+	else
+		ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
+
+	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 (os_snprintf_error(buflen, ret))
+		return -1;
+	return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_chan_switch(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 peer[ETH_ALEN];
+	struct hostapd_freq_params freq_params;
+	u8 oper_class;
+	char *pos, *end;
+
+	if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
+		wpa_printf(MSG_INFO,
+			   "tdls_chanswitch: Only supported with external setup");
+		return -1;
+	}
+
+	os_memset(&freq_params, 0, sizeof(freq_params));
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	oper_class = strtol(pos, &end, 10);
+	if (pos == end) {
+		wpa_printf(MSG_INFO,
+			   "tdls_chanswitch: Invalid op class provided");
+		return -1;
+	}
+
+	pos = end;
+	freq_params.freq = atoi(pos);
+	if (freq_params.freq == 0) {
+		wpa_printf(MSG_INFO, "tdls_chanswitch: Invalid freq provided");
+		return -1;
+	}
+
+#define SET_FREQ_SETTING(str) \
+	do { \
+		const char *pos2 = os_strstr(pos, " " #str "="); \
+		if (pos2) { \
+			pos2 += sizeof(" " #str "=") - 1; \
+			freq_params.str = atoi(pos2); \
+		} \
+	} while (0)
+
+	SET_FREQ_SETTING(center_freq1);
+	SET_FREQ_SETTING(center_freq2);
+	SET_FREQ_SETTING(bandwidth);
+	SET_FREQ_SETTING(sec_channel_offset);
+#undef SET_FREQ_SETTING
+
+	freq_params.ht_enabled = !!os_strstr(pos, " ht");
+	freq_params.vht_enabled = !!os_strstr(pos, " vht");
+
+	if (hwaddr_aton(cmd, peer)) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE TDLS_CHAN_SWITCH: Invalid address '%s'",
+			   cmd);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CHAN_SWITCH " MACSTR
+		   " OP CLASS %d FREQ %d CENTER1 %d CENTER2 %d BW %d SEC_OFFSET %d%s%s",
+		   MAC2STR(peer), oper_class, freq_params.freq,
+		   freq_params.center_freq1, freq_params.center_freq2,
+		   freq_params.bandwidth, freq_params.sec_channel_offset,
+		   freq_params.ht_enabled ? " HT" : "",
+		   freq_params.vht_enabled ? " VHT" : "");
+
+	return wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class,
+					   &freq_params);
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 peer[ETH_ALEN];
+
+	if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
+		wpa_printf(MSG_INFO,
+			   "tdls_chanswitch: Only supported with external setup");
+		return -1;
+	}
+
+	if (hwaddr_aton(cmd, peer)) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH: Invalid address '%s'",
+			   cmd);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH " MACSTR,
+		   MAC2STR(peer));
+
+	return wpa_tdls_disable_chan_switch(wpa_s->wpa, peer);
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_link_status(
+	struct wpa_supplicant *wpa_s, const char *addr,
+	char *buf, size_t buflen)
+{
+	u8 peer[ETH_ALEN];
+	const char *tdls_status;
+	int ret;
+
+	if (hwaddr_aton(addr, peer)) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE TDLS_LINK_STATUS: Invalid address '%s'",
+			   addr);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS " MACSTR,
+		   MAC2STR(peer));
+
+	tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS: %s", tdls_status);
+	ret = os_snprintf(buf, buflen, "TDLS link status: %s\n", tdls_status);
+	if (os_snprintf_error(buflen, ret))
+		return -1;
+
+	return ret;
+}
+
+#endif /* CONFIG_TDLS */
+
+
+static int wmm_ac_ctrl_addts(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *token, *context = NULL;
+	struct wmm_ac_ts_setup_params params = {
+		.tsid = 0xff,
+		.direction = 0xff,
+	};
+
+	while ((token = str_token(cmd, " ", &context))) {
+		if (sscanf(token, "tsid=%i", &params.tsid) == 1 ||
+		    sscanf(token, "up=%i", &params.user_priority) == 1 ||
+		    sscanf(token, "nominal_msdu_size=%i",
+			   &params.nominal_msdu_size) == 1 ||
+		    sscanf(token, "mean_data_rate=%i",
+			   &params.mean_data_rate) == 1 ||
+		    sscanf(token, "min_phy_rate=%i",
+			   &params.minimum_phy_rate) == 1 ||
+		    sscanf(token, "sba=%i",
+			   &params.surplus_bandwidth_allowance) == 1)
+			continue;
+
+		if (os_strcasecmp(token, "downlink") == 0) {
+			params.direction = WMM_TSPEC_DIRECTION_DOWNLINK;
+		} else if (os_strcasecmp(token, "uplink") == 0) {
+			params.direction = WMM_TSPEC_DIRECTION_UPLINK;
+		} else if (os_strcasecmp(token, "bidi") == 0) {
+			params.direction = WMM_TSPEC_DIRECTION_BI_DIRECTIONAL;
+		} else if (os_strcasecmp(token, "fixed_nominal_msdu") == 0) {
+			params.fixed_nominal_msdu = 1;
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "CTRL: Invalid WMM_AC_ADDTS parameter: '%s'",
+				   token);
+			return -1;
+		}
+
+	}
+
+	return wpas_wmm_ac_addts(wpa_s, &params);
+}
+
+
+static int wmm_ac_ctrl_delts(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 tsid = atoi(cmd);
+
+	return wpas_wmm_ac_delts(wpa_s, tsid);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static int wpa_supplicant_ctrl_iface_ft_ds(
+	struct wpa_supplicant *wpa_s, char *addr)
+{
+	u8 target_ap[ETH_ALEN];
+	struct wpa_bss *bss;
+	const u8 *mdie;
+
+	if (hwaddr_aton(addr, target_ap)) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid "
+			   "address '%s'", addr);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS " MACSTR, MAC2STR(target_ap));
+
+	bss = wpa_bss_get_bssid(wpa_s, target_ap);
+	if (bss)
+		mdie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
+	else
+		mdie = NULL;
+
+	return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie);
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+#ifdef CONFIG_WPS
+static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s,
+					     char *cmd)
+{
+	u8 bssid[ETH_ALEN], *_bssid = bssid;
+#ifdef CONFIG_P2P
+	u8 p2p_dev_addr[ETH_ALEN];
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_AP
+	u8 *_p2p_dev_addr = NULL;
+#endif /* CONFIG_AP */
+
+	if (cmd == NULL || os_strcmp(cmd, "any") == 0) {
+		_bssid = NULL;
+#ifdef CONFIG_P2P
+	} else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
+		if (hwaddr_aton(cmd + 13, p2p_dev_addr)) {
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid "
+				   "P2P Device Address '%s'",
+				   cmd + 13);
+			return -1;
+		}
+		_p2p_dev_addr = p2p_dev_addr;
+#endif /* CONFIG_P2P */
+	} else if (hwaddr_aton(cmd, bssid)) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'",
+			   cmd);
+		return -1;
+	}
+
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface)
+		return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr);
+#endif /* CONFIG_AP */
+
+	return wpas_wps_start_pbc(wpa_s, _bssid, 0);
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
+					     char *cmd, char *buf,
+					     size_t buflen)
+{
+	u8 bssid[ETH_ALEN], *_bssid = bssid;
+	char *pin;
+	int ret;
+
+	pin = os_strchr(cmd, ' ');
+	if (pin)
+		*pin++ = '\0';
+
+	if (os_strcmp(cmd, "any") == 0)
+		_bssid = NULL;
+	else if (os_strcmp(cmd, "get") == 0) {
+		ret = wps_generate_pin();
+		goto done;
+	} else if (hwaddr_aton(cmd, bssid)) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'",
+			   cmd);
+		return -1;
+	}
+
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface) {
+		int timeout = 0;
+		char *pos;
+
+		if (pin) {
+			pos = os_strchr(pin, ' ');
+			if (pos) {
+				*pos++ = '\0';
+				timeout = atoi(pos);
+			}
+		}
+
+		return wpa_supplicant_ap_wps_pin(wpa_s, _bssid, pin,
+						 buf, buflen, timeout);
+	}
+#endif /* CONFIG_AP */
+
+	if (pin) {
+		ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0,
+					 DEV_PW_DEFAULT);
+		if (ret < 0)
+			return -1;
+		ret = os_snprintf(buf, buflen, "%s", pin);
+		if (os_snprintf_error(buflen, ret))
+			return -1;
+		return ret;
+	}
+
+	ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, DEV_PW_DEFAULT);
+	if (ret < 0)
+		return -1;
+
+done:
+	/* Return the generated PIN */
+	ret = os_snprintf(buf, buflen, "%08d", ret);
+	if (os_snprintf_error(buflen, ret))
+		return -1;
+	return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_check_pin(
+	struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+	char pin[9];
+	size_t len;
+	char *pos;
+	int ret;
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
+			      (u8 *) cmd, os_strlen(cmd));
+	for (pos = cmd, len = 0; *pos != '\0'; pos++) {
+		if (*pos < '0' || *pos > '9')
+			continue;
+		pin[len++] = *pos;
+		if (len == 9) {
+			wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
+			return -1;
+		}
+	}
+	if (len != 4 && len != 8) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
+		return -1;
+	}
+	pin[len] = '\0';
+
+	if (len == 8) {
+		unsigned int pin_val;
+		pin_val = atoi(pin);
+		if (!wps_pin_valid(pin_val)) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
+			ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
+			if (os_snprintf_error(buflen, ret))
+				return -1;
+			return ret;
+		}
+	}
+
+	ret = os_snprintf(buf, buflen, "%s", pin);
+	if (os_snprintf_error(buflen, ret))
+		return -1;
+
+	return ret;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s,
+					     char *cmd)
+{
+	u8 bssid[ETH_ALEN], *_bssid = bssid;
+
+	if (cmd == NULL || cmd[0] == '\0')
+		_bssid = NULL;
+	else if (hwaddr_aton(cmd, bssid))
+		return -1;
+
+	return wpas_wps_start_nfc(wpa_s, NULL, _bssid, NULL, 0, 0, NULL, NULL,
+				  0, 0);
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_nfc_config_token(
+	struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+	int ndef;
+	struct wpabuf *buf;
+	int res;
+	char *pos;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos)
+		*pos++ = '\0';
+	if (os_strcmp(cmd, "WPS") == 0)
+		ndef = 0;
+	else if (os_strcmp(cmd, "NDEF") == 0)
+		ndef = 1;
+	else
+		return -1;
+
+	buf = wpas_wps_nfc_config_token(wpa_s, ndef, pos);
+	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;
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_nfc_token(
+	struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+	int ndef;
+	struct wpabuf *buf;
+	int res;
+
+	if (os_strcmp(cmd, "WPS") == 0)
+		ndef = 0;
+	else if (os_strcmp(cmd, "NDEF") == 0)
+		ndef = 1;
+	else
+		return -1;
+
+	buf = wpas_wps_nfc_token(wpa_s, ndef);
+	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;
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read(
+	struct wpa_supplicant *wpa_s, char *pos)
+{
+	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)
+		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;
+	}
+
+	ret = wpas_wps_nfc_tag_read(wpa_s, buf, forced_freq);
+	wpabuf_free(buf);
+
+	return ret;
+}
+
+
+static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
+					      char *reply, size_t max_len,
+					      int ndef)
+{
+	struct wpabuf *buf;
+	int res;
+
+	buf = wpas_wps_nfc_handover_req(wpa_s, ndef);
+	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;
+}
+
+
+#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, "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, 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;
+}
+
+
+static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s,
+					      char *reply, size_t max_len,
+					      int ndef, int cr, char *uuid)
+{
+	struct wpabuf *buf;
+	int res;
+
+	buf = wpas_wps_nfc_handover_sel(wpa_s, ndef, cr, uuid);
+	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;
+}
+
+
+#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)
+{
+	char *pos, *pos2;
+	int ndef;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	if (os_strcmp(cmd, "WPS") == 0)
+		ndef = 0;
+	else if (os_strcmp(cmd, "NDEF") == 0)
+		ndef = 1;
+	else
+		return -1;
+
+	pos2 = os_strchr(pos, ' ');
+	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_report_handover(struct wpa_supplicant *wpa_s,
+					 char *cmd)
+{
+	size_t len;
+	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) {
+		wpa_printf(MSG_DEBUG, "NFC: Missing type in handover report");
+		return -1;
+	}
+	*pos++ = '\0';
+
+	type = pos;
+	pos = os_strchr(type, ' ');
+	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) {
+		wpa_printf(MSG_DEBUG, "NFC: Missing select message in handover report");
+		return -1;
+	}
+	*pos2++ = '\0';
+
+	len = os_strlen(pos);
+	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) {
+		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;
+	}
+	len /= 2;
+
+	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);
+		ret = -1;
+	}
+	wpabuf_free(req);
+	wpabuf_free(sel);
+
+	if (ret)
+		wpa_printf(MSG_DEBUG, "NFC: Failed to process reported handover messages");
+
+	return ret;
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
+					     char *cmd)
+{
+	u8 bssid[ETH_ALEN];
+	char *pin;
+	char *new_ssid;
+	char *new_auth;
+	char *new_encr;
+	char *new_key;
+	struct wps_new_ap_settings ap;
+
+	pin = os_strchr(cmd, ' ');
+	if (pin == NULL)
+		return -1;
+	*pin++ = '\0';
+
+	if (hwaddr_aton(cmd, bssid)) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'",
+			   cmd);
+		return -1;
+	}
+
+	new_ssid = os_strchr(pin, ' ');
+	if (new_ssid == NULL)
+		return wpas_wps_start_reg(wpa_s, bssid, pin, NULL);
+	*new_ssid++ = '\0';
+
+	new_auth = os_strchr(new_ssid, ' ');
+	if (new_auth == NULL)
+		return -1;
+	*new_auth++ = '\0';
+
+	new_encr = os_strchr(new_auth, ' ');
+	if (new_encr == NULL)
+		return -1;
+	*new_encr++ = '\0';
+
+	new_key = os_strchr(new_encr, ' ');
+	if (new_key == NULL)
+		return -1;
+	*new_key++ = '\0';
+
+	os_memset(&ap, 0, sizeof(ap));
+	ap.ssid_hex = new_ssid;
+	ap.auth = new_auth;
+	ap.encr = new_encr;
+	ap.key_hex = new_key;
+	return wpas_wps_start_reg(wpa_s, bssid, pin, &ap);
+}
+
+
+#ifdef CONFIG_AP
+static int wpa_supplicant_ctrl_iface_wps_ap_pin(struct wpa_supplicant *wpa_s,
+						char *cmd, char *buf,
+						size_t buflen)
+{
+	int timeout = 300;
+	char *pos;
+	const char *pin_txt;
+
+	if (!wpa_s->ap_iface)
+		return -1;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos)
+		*pos++ = '\0';
+
+	if (os_strcmp(cmd, "disable") == 0) {
+		wpas_wps_ap_pin_disable(wpa_s);
+		return os_snprintf(buf, buflen, "OK\n");
+	}
+
+	if (os_strcmp(cmd, "random") == 0) {
+		if (pos)
+			timeout = atoi(pos);
+		pin_txt = wpas_wps_ap_pin_random(wpa_s, timeout);
+		if (pin_txt == NULL)
+			return -1;
+		return os_snprintf(buf, buflen, "%s", pin_txt);
+	}
+
+	if (os_strcmp(cmd, "get") == 0) {
+		pin_txt = wpas_wps_ap_pin_get(wpa_s);
+		if (pin_txt == NULL)
+			return -1;
+		return os_snprintf(buf, buflen, "%s", pin_txt);
+	}
+
+	if (os_strcmp(cmd, "set") == 0) {
+		char *pin;
+		if (pos == NULL)
+			return -1;
+		pin = pos;
+		pos = os_strchr(pos, ' ');
+		if (pos) {
+			*pos++ = '\0';
+			timeout = atoi(pos);
+		}
+		if (os_strlen(pin) > buflen)
+			return -1;
+		if (wpas_wps_ap_pin_set(wpa_s, pin, timeout) < 0)
+			return -1;
+		return os_snprintf(buf, buflen, "%s", pin);
+	}
+
+	return -1;
+}
+#endif /* CONFIG_AP */
+
+
+#ifdef CONFIG_WPS_ER
+static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s,
+						char *cmd)
+{
+	char *uuid = cmd, *pin, *pos;
+	u8 addr_buf[ETH_ALEN], *addr = NULL;
+	pin = os_strchr(uuid, ' ');
+	if (pin == NULL)
+		return -1;
+	*pin++ = '\0';
+	pos = os_strchr(pin, ' ');
+	if (pos) {
+		*pos++ = '\0';
+		if (hwaddr_aton(pos, addr_buf) == 0)
+			addr = addr_buf;
+	}
+	return wpas_wps_er_add_pin(wpa_s, addr, uuid, pin);
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_er_learn(struct wpa_supplicant *wpa_s,
+						  char *cmd)
+{
+	char *uuid = cmd, *pin;
+	pin = os_strchr(uuid, ' ');
+	if (pin == NULL)
+		return -1;
+	*pin++ = '\0';
+	return wpas_wps_er_learn(wpa_s, uuid, pin);
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_er_set_config(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *uuid = cmd, *id;
+	id = os_strchr(uuid, ' ');
+	if (id == NULL)
+		return -1;
+	*id++ = '\0';
+	return wpas_wps_er_set_config(wpa_s, uuid, atoi(id));
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_er_config(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pin;
+	char *new_ssid;
+	char *new_auth;
+	char *new_encr;
+	char *new_key;
+	struct wps_new_ap_settings ap;
+
+	pin = os_strchr(cmd, ' ');
+	if (pin == NULL)
+		return -1;
+	*pin++ = '\0';
+
+	new_ssid = os_strchr(pin, ' ');
+	if (new_ssid == NULL)
+		return -1;
+	*new_ssid++ = '\0';
+
+	new_auth = os_strchr(new_ssid, ' ');
+	if (new_auth == NULL)
+		return -1;
+	*new_auth++ = '\0';
+
+	new_encr = os_strchr(new_auth, ' ');
+	if (new_encr == NULL)
+		return -1;
+	*new_encr++ = '\0';
+
+	new_key = os_strchr(new_encr, ' ');
+	if (new_key == NULL)
+		return -1;
+	*new_key++ = '\0';
+
+	os_memset(&ap, 0, sizeof(ap));
+	ap.ssid_hex = new_ssid;
+	ap.auth = new_auth;
+	ap.encr = new_encr;
+	ap.key_hex = new_key;
+	return wpas_wps_er_config(wpa_s, cmd, pin, &ap);
+}
+
+
+#ifdef CONFIG_WPS_NFC
+static int wpa_supplicant_ctrl_iface_wps_er_nfc_config_token(
+	struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+	int ndef;
+	struct wpabuf *buf;
+	int res;
+	char *uuid;
+
+	uuid = os_strchr(cmd, ' ');
+	if (uuid == NULL)
+		return -1;
+	*uuid++ = '\0';
+
+	if (os_strcmp(cmd, "WPS") == 0)
+		ndef = 0;
+	else if (os_strcmp(cmd, "NDEF") == 0)
+		ndef = 1;
+	else
+		return -1;
+
+	buf = wpas_wps_er_nfc_config_token(wpa_s, ndef, uuid);
+	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_WPS_NFC */
+#endif /* CONFIG_WPS_ER */
+
+#endif /* CONFIG_WPS */
+
+
+#ifdef CONFIG_IBSS_RSN
+static int wpa_supplicant_ctrl_iface_ibss_rsn(
+	struct wpa_supplicant *wpa_s, char *addr)
+{
+	u8 peer[ETH_ALEN];
+
+	if (hwaddr_aton(addr, peer)) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN: invalid "
+			   "address '%s'", addr);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN " MACSTR,
+		   MAC2STR(peer));
+
+	return ibss_rsn_start(wpa_s->ibss_rsn, peer);
+}
+#endif /* CONFIG_IBSS_RSN */
+
+
+static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s,
+					      char *rsp)
+{
+#ifdef IEEE8021X_EAPOL
+	char *pos, *id_pos;
+	int id;
+	struct wpa_ssid *ssid;
+
+	pos = os_strchr(rsp, '-');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+	id_pos = pos;
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+	id = atoi(id_pos);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
+			      (u8 *) pos, os_strlen(pos));
+
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
+			   "to update", id);
+		return -1;
+	}
+
+	return wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid, rsp,
+							 pos);
+#else /* IEEE8021X_EAPOL */
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
+	return -1;
+#endif /* IEEE8021X_EAPOL */
+}
+
+
+static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
+					    const char *params,
+					    char *buf, size_t buflen)
+{
+	char *pos, *end, tmp[30];
+	int res, verbose, wps, ret;
+#ifdef CONFIG_HS20
+	const u8 *hs20;
+#endif /* CONFIG_HS20 */
+	const u8 *sess_id;
+	size_t sess_id_len;
+
+	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;
+	end = buf + buflen;
+	if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
+		struct wpa_ssid *ssid = wpa_s->current_ssid;
+		ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
+				  MAC2STR(wpa_s->bssid));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+		ret = os_snprintf(pos, end - pos, "freq=%u\n",
+				  wpa_s->assoc_freq);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+		if (ssid) {
+			u8 *_ssid = ssid->ssid;
+			size_t ssid_len = ssid->ssid_len;
+			u8 ssid_buf[SSID_MAX_LEN];
+			if (ssid_len == 0) {
+				int _res = wpa_drv_get_ssid(wpa_s, ssid_buf);
+				if (_res < 0)
+					ssid_len = 0;
+				else
+					ssid_len = _res;
+				_ssid = ssid_buf;
+			}
+			ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n",
+					  wpa_ssid_txt(_ssid, ssid_len),
+					  ssid->id);
+			if (os_snprintf_error(end - pos, ret))
+				return pos - buf;
+			pos += ret;
+
+			if (wps && ssid->passphrase &&
+			    wpa_key_mgmt_wpa_psk(ssid->key_mgmt) &&
+			    (ssid->mode == WPAS_MODE_AP ||
+			     ssid->mode == WPAS_MODE_P2P_GO)) {
+				ret = os_snprintf(pos, end - pos,
+						  "passphrase=%s\n",
+						  ssid->passphrase);
+				if (os_snprintf_error(end - pos, ret))
+					return pos - buf;
+				pos += ret;
+			}
+			if (ssid->id_str) {
+				ret = os_snprintf(pos, end - pos,
+						  "id_str=%s\n",
+						  ssid->id_str);
+				if (os_snprintf_error(end - pos, ret))
+					return pos - buf;
+				pos += ret;
+			}
+
+			switch (ssid->mode) {
+			case WPAS_MODE_INFRA:
+				ret = os_snprintf(pos, end - pos,
+						  "mode=station\n");
+				break;
+			case WPAS_MODE_IBSS:
+				ret = os_snprintf(pos, end - pos,
+						  "mode=IBSS\n");
+				break;
+			case WPAS_MODE_AP:
+				ret = os_snprintf(pos, end - pos,
+						  "mode=AP\n");
+				break;
+			case WPAS_MODE_P2P_GO:
+				ret = os_snprintf(pos, end - pos,
+						  "mode=P2P GO\n");
+				break;
+			case WPAS_MODE_P2P_GROUP_FORMATION:
+				ret = os_snprintf(pos, end - pos,
+						  "mode=P2P GO - group "
+						  "formation\n");
+				break;
+			default:
+				ret = 0;
+				break;
+			}
+			if (os_snprintf_error(end - pos, ret))
+				return pos - buf;
+			pos += ret;
+		}
+
+#ifdef CONFIG_AP
+		if (wpa_s->ap_iface) {
+			pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos,
+							    end - pos,
+							    verbose);
+		} else
+#endif /* CONFIG_AP */
+		pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose);
+	}
+#ifdef CONFIG_SAE
+	if (wpa_s->wpa_state >= WPA_ASSOCIATED &&
+#ifdef CONFIG_AP
+	    !wpa_s->ap_iface &&
+#endif /* CONFIG_AP */
+	    wpa_s->sme.sae.state == SAE_ACCEPTED) {
+		ret = os_snprintf(pos, end - pos, "sae_group=%d\n",
+				  wpa_s->sme.sae.group);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_SAE */
+	ret = os_snprintf(pos, end - pos, "wpa_state=%s\n",
+			  wpa_supplicant_state_txt(wpa_s->wpa_state));
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	if (wpa_s->l2 &&
+	    l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) {
+		ret = os_snprintf(pos, end - pos, "ip_address=%s\n", tmp);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+#ifdef CONFIG_P2P
+	if (wpa_s->global->p2p) {
+		ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR
+				  "\n", MAC2STR(wpa_s->global->p2p_dev_addr));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_P2P */
+
+	ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n",
+			  MAC2STR(wpa_s->own_addr));
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+#ifdef CONFIG_HS20
+	if (wpa_s->current_bss &&
+	    (hs20 = wpa_bss_get_vendor_ie(wpa_s->current_bss,
+					  HS20_IE_VENDOR_TYPE)) &&
+	    wpa_s->wpa_proto == WPA_PROTO_RSN &&
+	    wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+		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 (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (wpa_s->current_ssid) {
+		struct wpa_cred *cred;
+		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->provisioning_sp) {
+				ret = os_snprintf(pos, end - pos,
+						  "provisioning_sp=%s\n",
+						  cred->provisioning_sp);
+				if (os_snprintf_error(end - pos, ret))
+					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[i]);
+			if (os_snprintf_error(end - pos, ret))
+				return pos - buf;
+			pos += ret;
+
+		no_domain:
+			if (wpa_s->current_bss == NULL ||
+			    wpa_s->current_bss->anqp == NULL)
+				res = -1;
+			else
+				res = interworking_home_sp_cred(
+					wpa_s, cred,
+					wpa_s->current_bss->anqp->domain_name);
+			if (res > 0)
+				type = "home";
+			else if (res == 0)
+				type = "roaming";
+			else
+				type = "unknown";
+
+			ret = os_snprintf(pos, end - pos, "sp_type=%s\n", type);
+			if (os_snprintf_error(end - pos, ret))
+				return pos - buf;
+			pos += ret;
+
+			break;
+		}
+	}
+#endif /* CONFIG_HS20 */
+
+	if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+		res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos,
+					  verbose);
+		if (res >= 0)
+			pos += res;
+	}
+
+	sess_id = eapol_sm_get_session_id(wpa_s->eapol, &sess_id_len);
+	if (sess_id) {
+		char *start = pos;
+
+		ret = os_snprintf(pos, end - pos, "eap_session_id=");
+		if (os_snprintf_error(end - pos, ret))
+			return start - buf;
+		pos += ret;
+		ret = wpa_snprintf_hex(pos, end - pos, sess_id, sess_id_len);
+		if (ret <= 0)
+			return start - buf;
+		pos += ret;
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			return start - buf;
+		pos += ret;
+	}
+
+	res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose);
+	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 (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef ANDROID
+	/*
+	 * 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 */
+
+	return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s,
+					   char *cmd)
+{
+	char *pos;
+	int id;
+	struct wpa_ssid *ssid;
+	u8 bssid[ETH_ALEN];
+
+	/* cmd: "<network id> <BSSID>" */
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+	id = atoi(cmd);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: id=%d bssid='%s'", id, pos);
+	if (hwaddr_aton(pos, bssid)) {
+		wpa_printf(MSG_DEBUG ,"CTRL_IFACE: invalid BSSID '%s'", pos);
+		return -1;
+	}
+
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
+			   "to update", id);
+		return -1;
+	}
+
+	os_memcpy(ssid->bssid, bssid, ETH_ALEN);
+	ssid->bssid_set = !is_zero_ether_addr(bssid);
+
+	return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s,
+					       char *cmd, char *buf,
+					       size_t buflen)
+{
+	u8 bssid[ETH_ALEN];
+	struct wpa_blacklist *e;
+	char *pos, *end;
+	int ret;
+
+	/* cmd: "BLACKLIST [<BSSID>]" */
+	if (*cmd == '\0') {
+		pos = buf;
+		end = buf + buflen;
+		e = wpa_s->blacklist;
+		while (e) {
+			ret = os_snprintf(pos, end - pos, MACSTR "\n",
+					  MAC2STR(e->bssid));
+			if (os_snprintf_error(end - pos, ret))
+				return pos - buf;
+			pos += ret;
+			e = e->next;
+		}
+		return pos - buf;
+	}
+
+	cmd++;
+	if (os_strncmp(cmd, "clear", 5) == 0) {
+		wpa_blacklist_clear(wpa_s);
+		os_memcpy(buf, "OK\n", 3);
+		return 3;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: BLACKLIST bssid='%s'", cmd);
+	if (hwaddr_aton(cmd, bssid)) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: invalid BSSID '%s'", cmd);
+		return -1;
+	}
+
+	/*
+	 * Add the BSSID twice, so its count will be 2, causing it to be
+	 * skipped when processing scan results.
+	 */
+	ret = wpa_blacklist_add(wpa_s, bssid);
+	if (ret < 0)
+		return -1;
+	ret = wpa_blacklist_add(wpa_s, bssid);
+	if (ret < 0)
+		return -1;
+	os_memcpy(buf, "OK\n", 3);
+	return 3;
+}
+
+
+static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s,
+					       char *cmd, char *buf,
+					       size_t buflen)
+{
+	char *pos, *end, *stamp;
+	int ret;
+
+	/* cmd: "LOG_LEVEL [<level>]" */
+	if (*cmd == '\0') {
+		pos = buf;
+		end = buf + buflen;
+		ret = os_snprintf(pos, end - pos, "Current level: %s\n"
+				  "Timestamp: %d\n",
+				  debug_level_str(wpa_debug_level),
+				  wpa_debug_timestamp);
+		if (os_snprintf_error(end - pos, ret))
+			ret = 0;
+
+		return ret;
+	}
+
+	while (*cmd == ' ')
+		cmd++;
+
+	stamp = os_strchr(cmd, ' ');
+	if (stamp) {
+		*stamp++ = '\0';
+		while (*stamp == ' ') {
+			stamp++;
+		}
+	}
+
+	if (os_strlen(cmd)) {
+		int level = str_to_debug_level(cmd);
+		if (level < 0)
+			return -1;
+		wpa_debug_level = level;
+	}
+
+	if (stamp && os_strlen(stamp))
+		wpa_debug_timestamp = atoi(stamp);
+
+	os_memcpy(buf, "OK\n", 3);
+	return 3;
+}
+
+
+static int wpa_supplicant_ctrl_iface_list_networks(
+	struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+	char *pos, *end, *prev;
+	struct wpa_ssid *ssid;
+	int ret;
+
+	pos = buf;
+	end = buf + buflen;
+	ret = os_snprintf(pos, end - pos,
+			  "network id / ssid / bssid / flags\n");
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	ssid = wpa_s->conf->ssid;
+
+	/* skip over ssids until we find next one */
+	if (cmd != NULL && os_strncmp(cmd, "LAST_ID=", 8) == 0) {
+		int last_id = atoi(cmd + 8);
+		if (last_id != -1) {
+			while (ssid != NULL && ssid->id <= last_id) {
+				ssid = ssid->next;
+			}
+		}
+	}
+
+	while (ssid) {
+		prev = pos;
+		ret = os_snprintf(pos, end - pos, "%d\t%s",
+				  ssid->id,
+				  wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+		if (os_snprintf_error(end - pos, ret))
+			return prev - buf;
+		pos += ret;
+		if (ssid->bssid_set) {
+			ret = os_snprintf(pos, end - pos, "\t" MACSTR,
+					  MAC2STR(ssid->bssid));
+		} else {
+			ret = os_snprintf(pos, end - pos, "\tany");
+		}
+		if (os_snprintf_error(end - pos, ret))
+			return prev - buf;
+		pos += ret;
+		ret = os_snprintf(pos, end - pos, "\t%s%s%s%s",
+				  ssid == wpa_s->current_ssid ?
+				  "[CURRENT]" : "",
+				  ssid->disabled ? "[DISABLED]" : "",
+				  ssid->disabled_until.sec ?
+				  "[TEMP-DISABLED]" : "",
+				  ssid->disabled == 2 ? "[P2P-PERSISTENT]" :
+				  "");
+		if (os_snprintf_error(end - pos, ret))
+			return prev - buf;
+		pos += ret;
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			return prev - buf;
+		pos += ret;
+
+		ssid = ssid->next;
+	}
+
+	return pos - buf;
+}
+
+
+static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher)
+{
+	int ret;
+	ret = os_snprintf(pos, end - pos, "-");
+	if (os_snprintf_error(end - pos, ret))
+		return pos;
+	pos += ret;
+	ret = wpa_write_ciphers(pos, end, cipher, "+");
+	if (ret < 0)
+		return pos;
+	pos += ret;
+	return pos;
+}
+
+
+static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto,
+				    const u8 *ie, size_t ie_len)
+{
+	struct wpa_ie_data data;
+	char *start;
+	int ret;
+
+	ret = os_snprintf(pos, end - pos, "[%s-", proto);
+	if (os_snprintf_error(end - pos, ret))
+		return pos;
+	pos += ret;
+
+	if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) {
+		ret = os_snprintf(pos, end - pos, "?]");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+		return pos;
+	}
+
+	start = pos;
+	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+		ret = os_snprintf(pos, end - pos, "%sEAP",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+	if (data.key_mgmt & WPA_KEY_MGMT_PSK) {
+		ret = os_snprintf(pos, end - pos, "%sPSK",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+	if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
+		ret = os_snprintf(pos, end - pos, "%sNone",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+	if (data.key_mgmt & WPA_KEY_MGMT_SAE) {
+		ret = os_snprintf(pos, end - pos, "%sSAE",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#ifdef CONFIG_IEEE80211R
+	if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+		ret = os_snprintf(pos, end - pos, "%sFT/EAP",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+	if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+		ret = os_snprintf(pos, end - pos, "%sFT/PSK",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+	if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+		ret = os_snprintf(pos, end - pos, "%sFT/SAE",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+		ret = os_snprintf(pos, end - pos, "%sEAP-SHA256",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+	if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+		ret = os_snprintf(pos, end - pos, "%sPSK-SHA256",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_SUITEB
+	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+		ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#endif /* CONFIG_SUITEB */
+
+#ifdef CONFIG_SUITEB192
+	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B-192",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#endif /* CONFIG_SUITEB192 */
+
+	if (data.key_mgmt & WPA_KEY_MGMT_OSEN) {
+		ret = os_snprintf(pos, end - pos, "%sOSEN",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+
+	pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher);
+
+	if (data.capabilities & WPA_CAPABILITY_PREAUTH) {
+		ret = os_snprintf(pos, end - pos, "-preauth");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+
+	ret = os_snprintf(pos, end - pos, "]");
+	if (os_snprintf_error(end - pos, ret))
+		return pos;
+	pos += ret;
+
+	return pos;
+}
+
+
+#ifdef CONFIG_WPS
+static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s,
+					    char *pos, char *end,
+					    struct wpabuf *wps_ie)
+{
+	int ret;
+	const char *txt;
+
+	if (wps_ie == NULL)
+		return pos;
+	if (wps_is_selected_pbc_registrar(wps_ie))
+		txt = "[WPS-PBC]";
+	else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0))
+		txt = "[WPS-AUTH]";
+	else if (wps_is_selected_pin_registrar(wps_ie))
+		txt = "[WPS-PIN]";
+	else
+		txt = "[WPS]";
+
+	ret = os_snprintf(pos, end - pos, "%s", txt);
+	if (!os_snprintf_error(end - pos, ret))
+		pos += ret;
+	wpabuf_free(wps_ie);
+	return pos;
+}
+#endif /* CONFIG_WPS */
+
+
+static char * wpa_supplicant_wps_ie_txt(struct wpa_supplicant *wpa_s,
+					char *pos, char *end,
+					const struct wpa_bss *bss)
+{
+#ifdef CONFIG_WPS
+	struct wpabuf *wps_ie;
+	wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+	return wpa_supplicant_wps_ie_txt_buf(wpa_s, pos, end, wps_ie);
+#else /* CONFIG_WPS */
+	return pos;
+#endif /* CONFIG_WPS */
+}
+
+
+/* Format one result on one text line into a buffer. */
+static int wpa_supplicant_ctrl_iface_scan_result(
+	struct wpa_supplicant *wpa_s,
+	const struct wpa_bss *bss, char *buf, size_t buflen)
+{
+	char *pos, *end;
+	int ret;
+	const u8 *ie, *ie2, *osen_ie, *p2p, *mesh;
+
+	mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
+	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)
+		return 0; /* Do not show P2P listen discovery results here */
+
+	pos = buf;
+	end = buf + buflen;
+
+	ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t",
+			  MAC2STR(bss->bssid), bss->freq, bss->level);
+	if (os_snprintf_error(end - pos, ret))
+		return -1;
+	pos += ret;
+	ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+	if (ie)
+		pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]);
+	ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	if (ie2) {
+		pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2",
+					    ie2, 2 + ie2[1]);
+	}
+	osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
+	if (osen_ie)
+		pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
+					    osen_ie, 2 + osen_ie[1]);
+	pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
+	if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) {
+		ret = os_snprintf(pos, end - pos, "[WEP]");
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+	if (mesh) {
+		ret = os_snprintf(pos, end - pos, "[MESH]");
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+	if (bss_is_dmg(bss)) {
+		const char *s;
+		ret = os_snprintf(pos, end - pos, "[DMG]");
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+		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 (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	} else {
+		if (bss->caps & IEEE80211_CAP_IBSS) {
+			ret = os_snprintf(pos, end - pos, "[IBSS]");
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+		if (bss->caps & IEEE80211_CAP_ESS) {
+			ret = os_snprintf(pos, end - pos, "[ESS]");
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+	}
+	if (p2p) {
+		ret = os_snprintf(pos, end - pos, "[P2P]");
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+#ifdef CONFIG_HS20
+	if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) {
+		ret = os_snprintf(pos, end - pos, "[HS20]");
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_FST
+	if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) {
+		ret = os_snprintf(pos, end - pos, "[FST]");
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+#endif /* CONFIG_FST */
+
+	ret = os_snprintf(pos, end - pos, "\t%s",
+			  wpa_ssid_txt(bss->ssid, bss->ssid_len));
+	if (os_snprintf_error(end - pos, ret))
+		return -1;
+	pos += ret;
+
+	ret = os_snprintf(pos, end - pos, "\n");
+	if (os_snprintf_error(end - pos, ret))
+		return -1;
+	pos += ret;
+
+	return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_scan_results(
+	struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+	char *pos, *end;
+	struct wpa_bss *bss;
+	int ret;
+
+	pos = buf;
+	end = buf + buflen;
+	ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / "
+			  "flags / ssid\n");
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
+		ret = wpa_supplicant_ctrl_iface_scan_result(wpa_s, bss, pos,
+							    end - pos);
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+#ifdef CONFIG_MESH
+
+static int wpa_supplicant_ctrl_iface_mesh_interface_add(
+	struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+	char *pos, ifname[IFNAMSIZ + 1];
+
+	ifname[0] = '\0';
+
+	pos = os_strstr(cmd, "ifname=");
+	if (pos) {
+		pos += 7;
+		os_strlcpy(ifname, pos, sizeof(ifname));
+	}
+
+	if (wpas_mesh_add_interface(wpa_s, ifname, sizeof(ifname)) < 0)
+		return -1;
+
+	os_strlcpy(reply, ifname, max_len);
+	return os_strlen(ifname);
+}
+
+
+static int wpa_supplicant_ctrl_iface_mesh_group_add(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int id;
+	struct wpa_ssid *ssid;
+
+	id = atoi(cmd);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_ADD id=%d", id);
+
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE: Could not find network id=%d", id);
+		return -1;
+	}
+	if (ssid->mode != WPAS_MODE_MESH) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE: Cannot use MESH_GROUP_ADD on a non mesh network");
+		return -1;
+	}
+	if (ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
+	    ssid->key_mgmt != WPA_KEY_MGMT_SAE) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL_IFACE: key_mgmt for mesh network should be open or SAE");
+		return -1;
+	}
+
+	/*
+	 * TODO: If necessary write our own group_add function,
+	 * for now we can reuse select_network
+	 */
+	wpa_supplicant_select_network(wpa_s, ssid);
+
+	return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_mesh_group_remove(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	struct wpa_supplicant *orig;
+	struct wpa_global *global;
+	int found = 0;
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s", cmd);
+
+	global = wpa_s->global;
+	orig = wpa_s;
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (os_strcmp(wpa_s->ifname, cmd) == 0) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s not found",
+			   cmd);
+		return -1;
+	}
+	if (wpa_s->mesh_if_created && wpa_s == orig) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL_IFACE: MESH_GROUP_REMOVE can't remove itself");
+		return -1;
+	}
+
+	wpa_s->reassociate = 0;
+	wpa_s->disconnected = 1;
+	wpa_supplicant_cancel_sched_scan(wpa_s);
+	wpa_supplicant_cancel_scan(wpa_s);
+
+	/*
+	 * TODO: If necessary write our own group_remove function,
+	 * for now we can reuse deauthenticate
+	 */
+	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+
+	if (wpa_s->mesh_if_created)
+		wpa_supplicant_remove_iface(global, wpa_s, 0);
+
+	return 0;
+}
+
+#endif /* CONFIG_MESH */
+
+
+static int wpa_supplicant_ctrl_iface_select_network(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int id;
+	struct wpa_ssid *ssid;
+	char *pos;
+
+	/* cmd: "<network id>" or "any" */
+	if (os_strncmp(cmd, "any", 3) == 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any");
+		ssid = NULL;
+	} else {
+		id = atoi(cmd);
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK id=%d", id);
+
+		ssid = wpa_config_get_network(wpa_s->conf, id);
+		if (ssid == NULL) {
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+				   "network id=%d", id);
+			return -1;
+		}
+		if (ssid->disabled == 2) {
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
+				   "SELECT_NETWORK with persistent P2P group");
+			return -1;
+		}
+	}
+
+	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_s->scan_min_time.sec = 0;
+	wpa_s->scan_min_time.usec = 0;
+	wpa_supplicant_select_network(wpa_s, ssid);
+
+	return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_enable_network(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int id;
+	struct wpa_ssid *ssid;
+
+	/* cmd: "<network id>" or "all" */
+	if (os_strcmp(cmd, "all") == 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK all");
+		ssid = NULL;
+	} else {
+		id = atoi(cmd);
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK id=%d", id);
+
+		ssid = wpa_config_get_network(wpa_s->conf, id);
+		if (ssid == NULL) {
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+				   "network id=%d", id);
+			return -1;
+		}
+		if (ssid->disabled == 2) {
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
+				   "ENABLE_NETWORK with persistent P2P group");
+			return -1;
+		}
+
+		if (os_strstr(cmd, " no-connect")) {
+			ssid->disabled = 0;
+			return 0;
+		}
+	}
+	wpa_s->scan_min_time.sec = 0;
+	wpa_s->scan_min_time.usec = 0;
+	wpa_supplicant_enable_network(wpa_s, ssid);
+
+	return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_disable_network(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int id;
+	struct wpa_ssid *ssid;
+
+	/* cmd: "<network id>" or "all" */
+	if (os_strcmp(cmd, "all") == 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK all");
+		ssid = NULL;
+	} else {
+		id = atoi(cmd);
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK id=%d", id);
+
+		ssid = wpa_config_get_network(wpa_s->conf, id);
+		if (ssid == NULL) {
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+				   "network id=%d", id);
+			return -1;
+		}
+		if (ssid->disabled == 2) {
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
+				   "DISABLE_NETWORK with persistent P2P "
+				   "group");
+			return -1;
+		}
+	}
+	wpa_supplicant_disable_network(wpa_s, ssid);
+
+	return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_add_network(
+	struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+	struct wpa_ssid *ssid;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_NETWORK");
+
+	ssid = wpa_config_add_network(wpa_s->conf);
+	if (ssid == NULL)
+		return -1;
+
+	wpas_notify_network_added(wpa_s, ssid);
+
+	ssid->disabled = 1;
+	wpa_config_set_network_defaults(ssid);
+
+	ret = os_snprintf(buf, buflen, "%d\n", ssid->id);
+	if (os_snprintf_error(buflen, ret))
+		return -1;
+	return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_remove_network(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int id;
+	struct wpa_ssid *ssid;
+	int was_disabled;
+
+	/* cmd: "<network id>" or "all" */
+	if (os_strcmp(cmd, "all") == 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all");
+		if (wpa_s->sched_scanning)
+			wpa_supplicant_cancel_sched_scan(wpa_s);
+
+		eapol_sm_invalidate_cached_session(wpa_s->eapol);
+		if (wpa_s->current_ssid) {
+#ifdef CONFIG_SME
+			wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+			wpa_sm_set_config(wpa_s->wpa, NULL);
+			eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+			if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+				wpa_s->own_disconnect_req = 1;
+			wpa_supplicant_deauthenticate(
+				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+		}
+		ssid = wpa_s->conf->ssid;
+		while (ssid) {
+			struct wpa_ssid *remove_ssid = ssid;
+			id = ssid->id;
+			ssid = ssid->next;
+			if (wpa_s->last_ssid == remove_ssid)
+				wpa_s->last_ssid = NULL;
+			wpas_notify_network_removed(wpa_s, remove_ssid);
+			wpa_config_remove_network(wpa_s->conf, id);
+		}
+		return 0;
+	}
+
+	id = atoi(cmd);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id);
+
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid)
+		wpas_notify_network_removed(wpa_s, ssid);
+	if (ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
+			   "id=%d", id);
+		return -1;
+	}
+
+	if (wpa_s->last_ssid == ssid)
+		wpa_s->last_ssid = NULL;
+
+	if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) {
+#ifdef CONFIG_SME
+		wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+		/*
+		 * Invalidate the EAP session cache if the current or
+		 * previously used network is removed.
+		 */
+		eapol_sm_invalidate_cached_session(wpa_s->eapol);
+	}
+
+	if (ssid == wpa_s->current_ssid) {
+		wpa_sm_set_config(wpa_s->wpa, NULL);
+		eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+
+		if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+			wpa_s->own_disconnect_req = 1;
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+	}
+
+	was_disabled = ssid->disabled;
+
+	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the "
+			   "network id=%d", id);
+		return -1;
+	}
+
+	if (!was_disabled && wpa_s->sched_scanning) {
+		wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove "
+			   "network from filters");
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	}
+
+	return 0;
+}
+
+
+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)
+{
+	int id, ret, prev_bssid_set, prev_disabled;
+	struct wpa_ssid *ssid;
+	char *name, *value;
+	u8 prev_bssid[ETH_ALEN];
+
+	/* cmd: "<network id> <variable name> <value>" */
+	name = os_strchr(cmd, ' ');
+	if (name == NULL)
+		return -1;
+	*name++ = '\0';
+
+	value = os_strchr(name, ' ');
+	if (value == NULL)
+		return -1;
+	*value++ = '\0';
+
+	id = atoi(cmd);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_NETWORK id=%d name='%s'",
+		   id, name);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
+			      (u8 *) value, os_strlen(value));
+
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
+			   "id=%d", id);
+		return -1;
+	}
+
+	prev_bssid_set = ssid->bssid_set;
+	prev_disabled = ssid->disabled;
+	os_memcpy(prev_bssid, ssid->bssid, ETH_ALEN);
+	ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid, name,
+						       value);
+	if (ret == 0 &&
+	    (ssid->bssid_set != prev_bssid_set ||
+	     os_memcmp(ssid->bssid, prev_bssid, ETH_ALEN) != 0))
+		wpas_notify_network_bssid_set_changed(wpa_s, ssid);
+
+	if (prev_disabled != ssid->disabled &&
+	    (prev_disabled == 2 || ssid->disabled == 2))
+		wpas_notify_network_type_changed(wpa_s, ssid);
+
+	return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_get_network(
+	struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+	int id;
+	size_t res;
+	struct wpa_ssid *ssid;
+	char *name, *value;
+
+	/* cmd: "<network id> <variable name>" */
+	name = os_strchr(cmd, ' ');
+	if (name == NULL || buflen == 0)
+		return -1;
+	*name++ = '\0';
+
+	id = atoi(cmd);
+	wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: GET_NETWORK id=%d name='%s'",
+		   id, name);
+
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid == NULL) {
+		wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Could not find network "
+			   "id=%d", id);
+		return -1;
+	}
+
+	value = wpa_config_get_no_key(ssid, name);
+	if (value == NULL) {
+		wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Failed to get network "
+			   "variable '%s'", name);
+		return -1;
+	}
+
+	res = os_strlcpy(buf, value, buflen);
+	if (res >= buflen) {
+		os_free(value);
+		return -1;
+	}
+
+	os_free(value);
+
+	return res;
+}
+
+
+static int wpa_supplicant_ctrl_iface_dup_network(
+	struct wpa_supplicant *wpa_s, char *cmd,
+	struct wpa_supplicant *dst_wpa_s)
+{
+	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 ifname=%s->%s id=%d->%d name='%s'",
+		   wpa_s->ifname, dst_wpa_s->ifname, 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(dst_wpa_s->conf, id_d);
+	if (ssid_d == NULL) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+			   "network id=%d", id_d);
+		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(dst_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)
+{
+	char *pos, *end;
+	struct wpa_cred *cred;
+	int ret;
+
+	pos = buf;
+	end = buf + buflen;
+	ret = os_snprintf(pos, end - pos,
+			  "cred id / realm / username / domain / imsi\n");
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	cred = wpa_s->conf->cred;
+	while (cred) {
+		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[0] : "",
+				  cred->imsi ? cred->imsi : "");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+
+		cred = cred->next;
+	}
+
+	return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_add_cred(struct wpa_supplicant *wpa_s,
+					      char *buf, size_t buflen)
+{
+	struct wpa_cred *cred;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_CRED");
+
+	cred = wpa_config_add_cred(wpa_s->conf);
+	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 (os_snprintf_error(buflen, ret))
+		return -1;
+	return ret;
+}
+
+
+static int wpas_ctrl_remove_cred(struct wpa_supplicant *wpa_s,
+				 struct wpa_cred *cred)
+{
+	struct wpa_ssid *ssid;
+	char str[20];
+	int id;
+
+	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) {
+		if (ssid->parent_cred == cred) {
+			int res;
+
+			wpa_printf(MSG_DEBUG, "Remove network id %d since it "
+				   "used the removed credential", ssid->id);
+			res = os_snprintf(str, sizeof(str), "%d", ssid->id);
+			if (os_snprintf_error(sizeof(str), res))
+				str[sizeof(str) - 1] = '\0';
+			ssid = ssid->next;
+			wpa_supplicant_ctrl_iface_remove_network(wpa_s, str);
+		} else
+			ssid = ssid->next;
+	}
+
+	return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s,
+						 char *cmd)
+{
+	int id;
+	struct wpa_cred *cred, *prev;
+
+	/* 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;
+		while (cred) {
+			prev = cred;
+			cred = cred->next;
+			wpas_ctrl_remove_cred(wpa_s, prev);
+		}
+		return 0;
+	}
+
+	if (os_strncmp(cmd, "sp_fqdn=", 8) == 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED SP FQDN '%s'",
+			   cmd + 8);
+		cred = wpa_s->conf->cred;
+		while (cred) {
+			prev = cred;
+			cred = cred->next;
+			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;
+	}
+
+	id = atoi(cmd);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED id=%d", id);
+
+	cred = wpa_config_get_cred(wpa_s->conf, id);
+	return wpas_ctrl_remove_cred(wpa_s, cred);
+}
+
+
+static int wpa_supplicant_ctrl_iface_set_cred(struct wpa_supplicant *wpa_s,
+					      char *cmd)
+{
+	int id;
+	struct wpa_cred *cred;
+	char *name, *value;
+
+	/* cmd: "<cred id> <variable name> <value>" */
+	name = os_strchr(cmd, ' ');
+	if (name == NULL)
+		return -1;
+	*name++ = '\0';
+
+	value = os_strchr(name, ' ');
+	if (value == NULL)
+		return -1;
+	*value++ = '\0';
+
+	id = atoi(cmd);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_CRED id=%d name='%s'",
+		   id, name);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
+			      (u8 *) value, os_strlen(value));
+
+	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;
+	}
+
+	if (wpa_config_set_cred(cred, name, value, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set cred "
+			   "variable '%s'", name);
+		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)
+{
+	int ret;
+
+	if (!wpa_s->conf->update_config) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed "
+			   "to update configuration (update_config=0)");
+		return -1;
+	}
+
+	ret = wpa_config_write(wpa_s->confname, wpa_s->conf);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to "
+			   "update configuration");
+	} else {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration"
+			   " updated");
+	}
+
+	return ret;
+}
+#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 const struct cipher_info ciphers_group_mgmt[] = {
+	{ WPA_DRIVER_CAPA_ENC_BIP, "AES-128-CMAC", 1 },
+	{ WPA_DRIVER_CAPA_ENC_BIP_GMAC_128, "BIP-GMAC-128", 1 },
+	{ WPA_DRIVER_CAPA_ENC_BIP_GMAC_256, "BIP-GMAC-256", 1 },
+	{ WPA_DRIVER_CAPA_ENC_BIP_CMAC_256, "BIP-CMAC-256", 1 },
+};
+
+
+static int ctrl_iface_get_capability_pairwise(int res, char *strict,
+					      struct wpa_driver_capa *capa,
+					      char *buf, size_t buflen)
+{
+	int ret;
+	char *pos, *end;
+	size_t len;
+	unsigned int i;
+
+	pos = buf;
+	end = pos + buflen;
+
+	if (res < 0) {
+		if (strict)
+			return 0;
+		len = os_strlcpy(buf, "CCMP TKIP NONE", buflen);
+		if (len >= buflen)
+			return -1;
+		return len;
+	}
+
+	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 (os_snprintf_error(end - pos, ret))
+				return pos - buf;
+			pos += ret;
+		}
+	}
+
+	return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_group(int res, char *strict,
+					   struct wpa_driver_capa *capa,
+					   char *buf, size_t buflen)
+{
+	int ret;
+	char *pos, *end;
+	size_t len;
+	unsigned int i;
+
+	pos = buf;
+	end = pos + buflen;
+
+	if (res < 0) {
+		if (strict)
+			return 0;
+		len = os_strlcpy(buf, "CCMP TKIP WEP104 WEP40", buflen);
+		if (len >= buflen)
+			return -1;
+		return len;
+	}
+
+	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 (os_snprintf_error(end - pos, ret))
+				return pos - buf;
+			pos += ret;
+		}
+	}
+
+	return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_group_mgmt(int res, char *strict,
+						struct wpa_driver_capa *capa,
+						char *buf, size_t buflen)
+{
+	int ret;
+	char *pos, *end;
+	unsigned int i;
+
+	pos = buf;
+	end = pos + buflen;
+
+	if (res < 0)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(ciphers_group_mgmt); i++) {
+		if (capa->enc & ciphers_group_mgmt[i].capa) {
+			ret = os_snprintf(pos, end - pos, "%s%s",
+					  pos == buf ? "" : " ",
+					  ciphers_group_mgmt[i].name);
+			if (os_snprintf_error(end - pos, ret))
+				return pos - buf;
+			pos += ret;
+		}
+	}
+
+	return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_key_mgmt(int res, char *strict,
+					      struct wpa_driver_capa *capa,
+					      char *buf, size_t buflen)
+{
+	int ret;
+	char *pos, *end;
+	size_t len;
+
+	pos = buf;
+	end = pos + buflen;
+
+	if (res < 0) {
+		if (strict)
+			return 0;
+		len = os_strlcpy(buf, "WPA-PSK WPA-EAP IEEE8021X WPA-NONE "
+				 "NONE", buflen);
+		if (len >= buflen)
+			return -1;
+		return len;
+	}
+
+	ret = os_snprintf(pos, end - pos, "NONE IEEE8021X");
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+			      WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
+		ret = os_snprintf(pos, end - pos, " WPA-EAP");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+			      WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+		ret = os_snprintf(pos, end - pos, " WPA-PSK");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
+		ret = os_snprintf(pos, end - pos, " WPA-NONE");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+#ifdef CONFIG_SUITEB
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B) {
+		ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) {
+		ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B-192");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_SUITEB192 */
+
+	return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_proto(int res, char *strict,
+					   struct wpa_driver_capa *capa,
+					   char *buf, size_t buflen)
+{
+	int ret;
+	char *pos, *end;
+	size_t len;
+
+	pos = buf;
+	end = pos + buflen;
+
+	if (res < 0) {
+		if (strict)
+			return 0;
+		len = os_strlcpy(buf, "RSN WPA", buflen);
+		if (len >= buflen)
+			return -1;
+		return len;
+	}
+
+	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+			      WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+		ret = os_snprintf(pos, end - pos, "%sRSN",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+			      WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
+		ret = os_snprintf(pos, end - pos, "%sWPA",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s,
+					      int res, char *strict,
+					      struct wpa_driver_capa *capa,
+					      char *buf, size_t buflen)
+{
+	int ret;
+	char *pos, *end;
+	size_t len;
+
+	pos = buf;
+	end = pos + buflen;
+
+	if (res < 0) {
+		if (strict)
+			return 0;
+		len = os_strlcpy(buf, "OPEN SHARED LEAP", buflen);
+		if (len >= buflen)
+			return -1;
+		return len;
+	}
+
+	if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) {
+		ret = os_snprintf(pos, end - pos, "%sOPEN",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) {
+		ret = os_snprintf(pos, end - pos, "%sSHARED",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) {
+		ret = os_snprintf(pos, end - pos, "%sLEAP",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+#ifdef CONFIG_SAE
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) {
+		ret = os_snprintf(pos, end - pos, "%sSAE",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_SAE */
+
+	return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_modes(int res, char *strict,
+					   struct wpa_driver_capa *capa,
+					   char *buf, size_t buflen)
+{
+	int ret;
+	char *pos, *end;
+	size_t len;
+
+	pos = buf;
+	end = pos + buflen;
+
+	if (res < 0) {
+		if (strict)
+			return 0;
+		len = os_strlcpy(buf, "IBSS AP", buflen);
+		if (len >= buflen)
+			return -1;
+		return len;
+	}
+
+	if (capa->flags & WPA_DRIVER_FLAGS_IBSS) {
+		ret = os_snprintf(pos, end - pos, "%sIBSS",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (capa->flags & WPA_DRIVER_FLAGS_AP) {
+		ret = os_snprintf(pos, end - pos, "%sAP",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+#ifdef CONFIG_MESH
+	if (capa->flags & WPA_DRIVER_FLAGS_MESH) {
+		ret = os_snprintf(pos, end - pos, "%sMESH",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_MESH */
+
+	return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s,
+					      char *buf, size_t buflen)
+{
+	struct hostapd_channel_data *chnl;
+	int ret, i, j;
+	char *pos, *end, *hmode;
+
+	pos = buf;
+	end = pos + buflen;
+
+	for (j = 0; j < wpa_s->hw.num_modes; j++) {
+		switch (wpa_s->hw.modes[j].mode) {
+		case HOSTAPD_MODE_IEEE80211B:
+			hmode = "B";
+			break;
+		case HOSTAPD_MODE_IEEE80211G:
+			hmode = "G";
+			break;
+		case HOSTAPD_MODE_IEEE80211A:
+			hmode = "A";
+			break;
+		case HOSTAPD_MODE_IEEE80211AD:
+			hmode = "AD";
+			break;
+		default:
+			continue;
+		}
+		ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+		chnl = wpa_s->hw.modes[j].channels;
+		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", chnl[i].chan);
+			if (os_snprintf_error(end - pos, ret))
+				return pos - buf;
+			pos += ret;
+		}
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_freq(struct wpa_supplicant *wpa_s,
+					  char *buf, size_t buflen)
+{
+	struct hostapd_channel_data *chnl;
+	int ret, i, j;
+	char *pos, *end, *hmode;
+
+	pos = buf;
+	end = pos + buflen;
+
+	for (j = 0; j < wpa_s->hw.num_modes; j++) {
+		switch (wpa_s->hw.modes[j].mode) {
+		case HOSTAPD_MODE_IEEE80211B:
+			hmode = "B";
+			break;
+		case HOSTAPD_MODE_IEEE80211G:
+			hmode = "G";
+			break;
+		case HOSTAPD_MODE_IEEE80211A:
+			hmode = "A";
+			break;
+		case HOSTAPD_MODE_IEEE80211AD:
+			hmode = "AD";
+			break;
+		default:
+			continue;
+		}
+		ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:\n",
+				  hmode);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+		chnl = wpa_s->hw.modes[j].channels;
+		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%s\n",
+					  chnl[i].chan, chnl[i].freq,
+					  chnl[i].flag & HOSTAPD_CHAN_NO_IR ?
+					  " (NO_IR)" : "",
+					  chnl[i].flag & HOSTAPD_CHAN_RADAR ?
+					  " (DFS)" : "");
+
+			if (os_snprintf_error(end - pos, ret))
+				return pos - buf;
+			pos += ret;
+		}
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_get_capability(
+	struct wpa_supplicant *wpa_s, const char *_field, char *buf,
+	size_t buflen)
+{
+	struct wpa_driver_capa capa;
+	int res;
+	char *strict;
+	char field[30];
+	size_t len;
+
+	/* Determine whether or not strict checking was requested */
+	len = os_strlcpy(field, _field, sizeof(field));
+	if (len >= sizeof(field))
+		return -1;
+	strict = os_strchr(field, ' ');
+	if (strict != NULL) {
+		*strict++ = '\0';
+		if (os_strcmp(strict, "strict") != 0)
+			return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s' %s",
+		field, strict ? strict : "");
+
+	if (os_strcmp(field, "eap") == 0) {
+		return eap_get_names(buf, buflen);
+	}
+
+	res = wpa_drv_get_capa(wpa_s, &capa);
+
+	if (os_strcmp(field, "pairwise") == 0)
+		return ctrl_iface_get_capability_pairwise(res, strict, &capa,
+							  buf, buflen);
+
+	if (os_strcmp(field, "group") == 0)
+		return ctrl_iface_get_capability_group(res, strict, &capa,
+						       buf, buflen);
+
+	if (os_strcmp(field, "group_mgmt") == 0)
+		return ctrl_iface_get_capability_group_mgmt(res, strict, &capa,
+							    buf, buflen);
+
+	if (os_strcmp(field, "key_mgmt") == 0)
+		return ctrl_iface_get_capability_key_mgmt(res, strict, &capa,
+							  buf, buflen);
+
+	if (os_strcmp(field, "proto") == 0)
+		return ctrl_iface_get_capability_proto(res, strict, &capa,
+						       buf, buflen);
+
+	if (os_strcmp(field, "auth_alg") == 0)
+		return ctrl_iface_get_capability_auth_alg(wpa_s, res, strict,
+							  &capa, buf, buflen);
+
+	if (os_strcmp(field, "modes") == 0)
+		return ctrl_iface_get_capability_modes(res, strict, &capa,
+						       buf, buflen);
+
+	if (os_strcmp(field, "channels") == 0)
+		return ctrl_iface_get_capability_channels(wpa_s, buf, buflen);
+
+	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 */
+
+#ifdef CONFIG_ERP
+	if (os_strcmp(field, "erp") == 0) {
+		res = os_snprintf(buf, buflen, "ERP");
+		if (os_snprintf_error(buflen, res))
+			return -1;
+		return res;
+	}
+#endif /* CONFIG_EPR */
+
+#ifdef CONFIG_FIPS
+	if (os_strcmp(field, "fips") == 0) {
+		res = os_snprintf(buf, buflen, "FIPS");
+		if (os_snprintf_error(buflen, res))
+			return -1;
+		return res;
+	}
+#endif /* CONFIG_FIPS */
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
+		   field);
+
+	return -1;
+}
+
+
+#ifdef CONFIG_INTERWORKING
+static char * anqp_add_hex(char *pos, char *end, const char *title,
+			   struct wpabuf *data)
+{
+	char *start = pos;
+	size_t i;
+	int ret;
+	const u8 *d;
+
+	if (data == NULL)
+		return start;
+
+	ret = os_snprintf(pos, end - pos, "%s=", title);
+	if (os_snprintf_error(end - pos, ret))
+		return start;
+	pos += ret;
+
+	d = wpabuf_head_u8(data);
+	for (i = 0; i < wpabuf_len(data); i++) {
+		ret = os_snprintf(pos, end - pos, "%02x", *d++);
+		if (os_snprintf_error(end - pos, ret))
+			return start;
+		pos += ret;
+	}
+
+	ret = os_snprintf(pos, end - pos, "\n");
+	if (os_snprintf_error(end - pos, ret))
+		return start;
+	pos += ret;
+
+	return pos;
+}
+#endif /* CONFIG_INTERWORKING */
+
+
+static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			  unsigned long mask, char *buf, size_t buflen)
+{
+	size_t i;
+	int ret;
+	char *pos, *end;
+	const u8 *ie, *ie2, *osen_ie;
+
+	pos = buf;
+	end = buf + buflen;
+
+	if (mask & WPA_BSS_MASK_ID) {
+		ret = os_snprintf(pos, end - pos, "id=%u\n", bss->id);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_BSSID) {
+		ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
+				  MAC2STR(bss->bssid));
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_FREQ) {
+		ret = os_snprintf(pos, end - pos, "freq=%d\n", bss->freq);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_BEACON_INT) {
+		ret = os_snprintf(pos, end - pos, "beacon_int=%d\n",
+				  bss->beacon_int);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_CAPABILITIES) {
+		ret = os_snprintf(pos, end - pos, "capabilities=0x%04x\n",
+				  bss->caps);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_QUAL) {
+		ret = os_snprintf(pos, end - pos, "qual=%d\n", bss->qual);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_NOISE) {
+		ret = os_snprintf(pos, end - pos, "noise=%d\n", bss->noise);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_LEVEL) {
+		ret = os_snprintf(pos, end - pos, "level=%d\n", bss->level);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_TSF) {
+		ret = os_snprintf(pos, end - pos, "tsf=%016llu\n",
+				  (unsigned long long) bss->tsf);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_AGE) {
+		struct os_reltime now;
+
+		os_get_reltime(&now);
+		ret = os_snprintf(pos, end - pos, "age=%d\n",
+				  (int) (now.sec - bss->last_update.sec));
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_IE) {
+		ret = os_snprintf(pos, end - pos, "ie=");
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+
+		ie = (const u8 *) (bss + 1);
+		for (i = 0; i < bss->ie_len; i++) {
+			ret = os_snprintf(pos, end - pos, "%02x", *ie++);
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+			pos += ret;
+		}
+
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_FLAGS) {
+		ret = os_snprintf(pos, end - pos, "flags=");
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+
+		ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+		if (ie)
+			pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie,
+						    2 + ie[1]);
+		ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+		if (ie2)
+			pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2,
+						    2 + ie2[1]);
+		osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
+		if (osen_ie)
+			pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
+						    osen_ie, 2 + osen_ie[1]);
+		pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
+		if (!ie && !ie2 && !osen_ie &&
+		    (bss->caps & IEEE80211_CAP_PRIVACY)) {
+			ret = os_snprintf(pos, end - pos, "[WEP]");
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+			pos += ret;
+		}
+		if (bss_is_dmg(bss)) {
+			const char *s;
+			ret = os_snprintf(pos, end - pos, "[DMG]");
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+			pos += ret;
+			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 (os_snprintf_error(end - pos, ret))
+				return 0;
+			pos += ret;
+		} else {
+			if (bss->caps & IEEE80211_CAP_IBSS) {
+				ret = os_snprintf(pos, end - pos, "[IBSS]");
+				if (os_snprintf_error(end - pos, ret))
+					return 0;
+				pos += ret;
+			}
+			if (bss->caps & IEEE80211_CAP_ESS) {
+				ret = os_snprintf(pos, end - pos, "[ESS]");
+				if (os_snprintf_error(end - pos, ret))
+					return 0;
+				pos += ret;
+			}
+		}
+		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 (os_snprintf_error(end - pos, ret))
+				return 0;
+			pos += ret;
+		}
+#ifdef CONFIG_HS20
+		if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
+			ret = os_snprintf(pos, end - pos, "[HS20]");
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+			pos += ret;
+		}
+#endif /* CONFIG_HS20 */
+
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_SSID) {
+		ret = os_snprintf(pos, end - pos, "ssid=%s\n",
+				  wpa_ssid_txt(bss->ssid, bss->ssid_len));
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+#ifdef CONFIG_WPS
+	if (mask & WPA_BSS_MASK_WPS_SCAN) {
+		ie = (const u8 *) (bss + 1);
+		ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end);
+		if (ret >= end - pos)
+			return 0;
+		if (ret > 0)
+			pos += ret;
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+	if (mask & WPA_BSS_MASK_P2P_SCAN) {
+		ie = (const u8 *) (bss + 1);
+		ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end);
+		if (ret < 0 || ret >= end - pos)
+			return 0;
+		pos += ret;
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (mask & WPA_BSS_MASK_WIFI_DISPLAY) {
+		struct wpabuf *wfd;
+		ie = (const u8 *) (bss + 1);
+		wfd = ieee802_11_vendor_ie_concat(ie, bss->ie_len,
+						  WFD_IE_VENDOR_TYPE);
+		if (wfd) {
+			ret = os_snprintf(pos, end - pos, "wfd_subelems=");
+			if (os_snprintf_error(end - pos, ret)) {
+				wpabuf_free(wfd);
+				return 0;
+			}
+			pos += ret;
+
+			pos += wpa_snprintf_hex(pos, end - pos,
+						wpabuf_head(wfd),
+						wpabuf_len(wfd));
+			wpabuf_free(wfd);
+
+			ret = os_snprintf(pos, end - pos, "\n");
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+			pos += ret;
+		}
+	}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+#ifdef CONFIG_INTERWORKING
+	if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) {
+		struct wpa_bss_anqp *anqp = bss->anqp;
+		pos = anqp_add_hex(pos, end, "anqp_capability_list",
+				   anqp->capability_list);
+		pos = anqp_add_hex(pos, end, "anqp_venue_name",
+				   anqp->venue_name);
+		pos = anqp_add_hex(pos, end, "anqp_network_auth_type",
+				   anqp->network_auth_type);
+		pos = anqp_add_hex(pos, end, "anqp_roaming_consortium",
+				   anqp->roaming_consortium);
+		pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability",
+				   anqp->ip_addr_type_availability);
+		pos = anqp_add_hex(pos, end, "anqp_nai_realm",
+				   anqp->nai_realm);
+		pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp);
+		pos = anqp_add_hex(pos, end, "anqp_domain_name",
+				   anqp->domain_name);
+#ifdef CONFIG_HS20
+		pos = anqp_add_hex(pos, end, "hs20_capability_list",
+				   anqp->hs20_capability_list);
+		pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name",
+				   anqp->hs20_operator_friendly_name);
+		pos = anqp_add_hex(pos, end, "hs20_wan_metrics",
+				   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 */
+
+#ifdef CONFIG_MESH
+	if (mask & WPA_BSS_MASK_MESH_SCAN) {
+		ie = (const u8 *) (bss + 1);
+		ret = wpas_mesh_scan_result_text(ie, bss->ie_len, pos, end);
+		if (ret < 0 || ret >= end - pos)
+			return 0;
+		pos += ret;
+	}
+#endif /* CONFIG_MESH */
+
+	if (mask & WPA_BSS_MASK_SNR) {
+		ret = os_snprintf(pos, end - pos, "snr=%d\n", bss->snr);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_EST_THROUGHPUT) {
+		ret = os_snprintf(pos, end - pos, "est_throughput=%d\n",
+				  bss->est_throughput);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+#ifdef CONFIG_FST
+	if (mask & WPA_BSS_MASK_FST) {
+		ret = fst_ctrl_iface_mb_info(bss->bssid, pos, end - pos);
+		if (ret < 0 || ret >= end - pos)
+			return 0;
+		pos += ret;
+	}
+#endif /* CONFIG_FST */
+
+	if (mask & WPA_BSS_MASK_DELIM) {
+		ret = os_snprintf(pos, end - pos, "====\n");
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
+					 const char *cmd, char *buf,
+					 size_t buflen)
+{
+	u8 bssid[ETH_ALEN];
+	size_t i;
+	struct wpa_bss *bss;
+	struct wpa_bss *bsslast = NULL;
+	struct dl_list *next;
+	int ret = 0;
+	int len;
+	char *ctmp, *end = buf + buflen;
+	unsigned long mask = WPA_BSS_MASK_ALL;
+
+	if (os_strncmp(cmd, "RANGE=", 6) == 0) {
+		if (os_strncmp(cmd + 6, "ALL", 3) == 0) {
+			bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss,
+					    list_id);
+			bsslast = dl_list_last(&wpa_s->bss_id, struct wpa_bss,
+					       list_id);
+		} else { /* N1-N2 */
+			unsigned int id1, id2;
+
+			if ((ctmp = os_strchr(cmd + 6, '-')) == NULL) {
+				wpa_printf(MSG_INFO, "Wrong BSS range "
+					   "format");
+				return 0;
+			}
+
+			if (*(cmd + 6) == '-')
+				id1 = 0;
+			else
+				id1 = atoi(cmd + 6);
+			ctmp++;
+			if (*ctmp >= '0' && *ctmp <= '9')
+				id2 = atoi(ctmp);
+			else
+				id2 = (unsigned int) -1;
+			bss = wpa_bss_get_id_range(wpa_s, id1, id2);
+			if (id2 == (unsigned int) -1)
+				bsslast = dl_list_last(&wpa_s->bss_id,
+						       struct wpa_bss,
+						       list_id);
+			else {
+				bsslast = wpa_bss_get_id(wpa_s, id2);
+				if (bsslast == NULL && bss && id2 > id1) {
+					struct wpa_bss *tmp = bss;
+					for (;;) {
+						next = tmp->list_id.next;
+						if (next == &wpa_s->bss_id)
+							break;
+						tmp = dl_list_entry(
+							next, struct wpa_bss,
+							list_id);
+						if (tmp->id > id2)
+							break;
+						bsslast = tmp;
+					}
+				}
+			}
+		}
+	} else if (os_strncmp(cmd, "FIRST", 5) == 0)
+		bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, list_id);
+	else if (os_strncmp(cmd, "LAST", 4) == 0)
+		bss = dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id);
+	else if (os_strncmp(cmd, "ID-", 3) == 0) {
+		i = atoi(cmd + 3);
+		bss = wpa_bss_get_id(wpa_s, i);
+	} else if (os_strncmp(cmd, "NEXT-", 5) == 0) {
+		i = atoi(cmd + 5);
+		bss = wpa_bss_get_id(wpa_s, i);
+		if (bss) {
+			next = bss->list_id.next;
+			if (next == &wpa_s->bss_id)
+				bss = NULL;
+			else
+				bss = dl_list_entry(next, struct wpa_bss,
+						    list_id);
+		}
+#ifdef CONFIG_P2P
+	} else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
+		if (hwaddr_aton(cmd + 13, bssid) == 0)
+			bss = wpa_bss_get_p2p_dev_addr(wpa_s, bssid);
+		else
+			bss = NULL;
+#endif /* CONFIG_P2P */
+	} else if (hwaddr_aton(cmd, bssid) == 0)
+		bss = wpa_bss_get_bssid(wpa_s, bssid);
+	else {
+		struct wpa_bss *tmp;
+		i = atoi(cmd);
+		bss = NULL;
+		dl_list_for_each(tmp, &wpa_s->bss_id, struct wpa_bss, list_id)
+		{
+			if (i-- == 0) {
+				bss = tmp;
+				break;
+			}
+		}
+	}
+
+	if ((ctmp = os_strstr(cmd, "MASK=")) != NULL) {
+		mask = strtoul(ctmp + 5, NULL, 0x10);
+		if (mask == 0)
+			mask = WPA_BSS_MASK_ALL;
+	}
+
+	if (bss == NULL)
+		return 0;
+
+	if (bsslast == NULL)
+		bsslast = bss;
+	do {
+		len = print_bss_info(wpa_s, bss, mask, buf, buflen);
+		ret += len;
+		buf += len;
+		buflen -= len;
+		if (bss == bsslast) {
+			if ((mask & WPA_BSS_MASK_DELIM) && len &&
+			    (bss == dl_list_last(&wpa_s->bss_id,
+						 struct wpa_bss, list_id))) {
+				int res;
+
+				res = os_snprintf(buf - 5, end - buf + 5,
+						  "####\n");
+				if (os_snprintf_error(end - buf + 5, res)) {
+					wpa_printf(MSG_DEBUG,
+						   "Could not add end delim");
+				}
+			}
+			break;
+		}
+		next = bss->list_id.next;
+		if (next == &wpa_s->bss_id)
+			break;
+		bss = dl_list_entry(next, struct wpa_bss, list_id);
+	} while (bss && len);
+
+	return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_ap_scan(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int ap_scan = atoi(cmd);
+	return wpa_supplicant_set_ap_scan(wpa_s, ap_scan);
+}
+
+
+static int wpa_supplicant_ctrl_iface_scan_interval(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int scan_int = atoi(cmd);
+	return wpa_supplicant_set_scan_interval(wpa_s, scan_int);
+}
+
+
+static int wpa_supplicant_ctrl_iface_bss_expire_age(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int expire_age = atoi(cmd);
+	return wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age);
+}
+
+
+static int wpa_supplicant_ctrl_iface_bss_expire_count(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int expire_count = atoi(cmd);
+	return wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count);
+}
+
+
+static void wpa_supplicant_ctrl_iface_bss_flush(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int flush_age = atoi(cmd);
+
+	if (flush_age == 0)
+		wpa_bss_flush(wpa_s);
+	else
+		wpa_bss_flush_by_age(wpa_s, flush_age);
+}
+
+
+#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");
+	/* 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 */
+
+	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL,
+			0);
+	/* MLME-SETPROTECTION.request(None) */
+	wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid,
+				   MLME_SETPROTECTION_PROTECT_TYPE_NONE,
+				   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,
+					  char *addr)
+{
+#ifdef CONFIG_NO_SCAN_PROCESSING
+	return -1;
+#else /* CONFIG_NO_SCAN_PROCESSING */
+	u8 bssid[ETH_ALEN];
+	struct wpa_bss *bss;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (hwaddr_aton(addr, bssid)) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: invalid "
+			   "address '%s'", addr);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM " MACSTR, MAC2STR(bssid));
+
+	if (!ssid) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network "
+			   "configuration known for the target AP");
+		return -1;
+	}
+
+	bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
+	if (!bss) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found "
+			   "from BSS table");
+		return -1;
+	}
+
+	/*
+	 * TODO: Find best network configuration block from configuration to
+	 * allow roaming to other networks
+	 */
+
+	wpa_s->reassociate = 1;
+	wpa_supplicant_connect(wpa_s, bss, ssid);
+
+	return 0;
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+}
+
+
+#ifdef CONFIG_P2P
+static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	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;
+	const char *_seek[P2P_MAX_QUERY_HASH + 1], **seek = NULL;
+	u8 seek_count = 0;
+	int freq = 0;
+
+	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"))
+		type = P2P_FIND_PROGRESSIVE;
+
+	pos = os_strstr(cmd, "dev_id=");
+	if (pos) {
+		pos += 7;
+		if (hwaddr_aton(pos, dev_id))
+			return -1;
+		_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;
+		search_delay = atoi(pos);
+	} else
+		search_delay = wpas_p2p_search_delay(wpa_s);
+
+	pos = os_strstr(cmd, "freq=");
+	if (pos) {
+		pos += 5;
+		freq = atoi(pos);
+		if (freq <= 0)
+			return -1;
+	}
+
+	/* Must be searched for last, because it adds nul termination */
+	pos = os_strstr(cmd, " seek=");
+	if (pos)
+		pos += 6;
+	while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) {
+		char *term;
+
+		_seek[seek_count++] = pos;
+		seek = _seek;
+		term = os_strchr(pos, ' ');
+		if (!term)
+			break;
+		*term = '\0';
+		pos = os_strstr(term + 1, "seek=");
+		if (pos)
+			pos += 5;
+	}
+	if (seek_count > P2P_MAX_QUERY_HASH) {
+		seek[0] = NULL;
+		seek_count = 1;
+	}
+
+	return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type,
+			     _dev_id, search_delay, seek_count, seek, freq);
+}
+
+
+static int p2ps_ctrl_parse_cpt_priority(const char *pos, u8 *cpt)
+{
+	const char *last = NULL;
+	const char *token;
+	long int token_len;
+	unsigned int i;
+
+	/* Expected predefined CPT names delimited by ':' */
+	for (i = 0; (token = cstr_token(pos, ": \t", &last)); i++) {
+		if (i >= P2PS_FEATURE_CAPAB_CPT_MAX) {
+			wpa_printf(MSG_ERROR,
+				   "P2PS: CPT name list is too long, expected up to %d names",
+				   P2PS_FEATURE_CAPAB_CPT_MAX);
+			cpt[0] = 0;
+			return -1;
+		}
+
+		token_len = last - token;
+
+		if (token_len  == 3 &&
+		    os_memcmp(token, "UDP", token_len) == 0) {
+			cpt[i] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+		} else if (token_len == 3 &&
+			   os_memcmp(token, "MAC", token_len) == 0) {
+			cpt[i] = P2PS_FEATURE_CAPAB_MAC_TRANSPORT;
+		} else {
+			wpa_printf(MSG_ERROR,
+				   "P2PS: Unsupported CPT name '%s'", token);
+			cpt[0] = 0;
+			return -1;
+		}
+
+		if (isblank(*last)) {
+			i++;
+			break;
+		}
+	}
+	cpt[i] = 0;
+	return 0;
+}
+
+
+static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd)
+{
+	struct p2ps_provision *p2ps_prov;
+	char *pos;
+	size_t info_len = 0;
+	char *info = NULL;
+	u8 role = P2PS_SETUP_NONE;
+	long long unsigned val;
+	int i;
+
+	pos = os_strstr(cmd, "info=");
+	if (pos) {
+		pos += 5;
+		info_len = os_strlen(pos);
+
+		if (info_len) {
+			info = os_malloc(info_len + 1);
+			if (info) {
+				info_len = utf8_unescape(pos, info_len,
+							 info, info_len + 1);
+			} else
+				info_len = 0;
+		}
+	}
+
+	p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + info_len + 1);
+	if (p2ps_prov == NULL) {
+		os_free(info);
+		return NULL;
+	}
+
+	if (info) {
+		os_memcpy(p2ps_prov->info, info, info_len);
+		p2ps_prov->info[info_len] = '\0';
+		os_free(info);
+	}
+
+	pos = os_strstr(cmd, "status=");
+	if (pos)
+		p2ps_prov->status = atoi(pos + 7);
+	else
+		p2ps_prov->status = -1;
+
+	pos = os_strstr(cmd, "adv_id=");
+	if (!pos || sscanf(pos + 7, "%llx", &val) != 1 || val > 0xffffffffULL)
+		goto invalid_args;
+	p2ps_prov->adv_id = val;
+
+	pos = os_strstr(cmd, "method=");
+	if (pos)
+		p2ps_prov->method = strtol(pos + 7, NULL, 16);
+	else
+		p2ps_prov->method = 0;
+
+	pos = os_strstr(cmd, "session=");
+	if (!pos || sscanf(pos + 8, "%llx", &val) != 1 || val > 0xffffffffULL)
+		goto invalid_args;
+	p2ps_prov->session_id = val;
+
+	pos = os_strstr(cmd, "adv_mac=");
+	if (!pos || hwaddr_aton(pos + 8, p2ps_prov->adv_mac))
+		goto invalid_args;
+
+	pos = os_strstr(cmd, "session_mac=");
+	if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac))
+		goto invalid_args;
+
+	pos = os_strstr(cmd, "cpt=");
+	if (pos) {
+		if (p2ps_ctrl_parse_cpt_priority(pos + 4,
+						 p2ps_prov->cpt_priority))
+			goto invalid_args;
+	} else {
+		p2ps_prov->cpt_priority[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+	}
+
+	for (i = 0; p2ps_prov->cpt_priority[i]; i++)
+		p2ps_prov->cpt_mask |= p2ps_prov->cpt_priority[i];
+
+	/* force conncap with tstCap (no sanity checks) */
+	pos = os_strstr(cmd, "tstCap=");
+	if (pos) {
+		role = strtol(pos + 7, NULL, 16);
+	} else {
+		pos = os_strstr(cmd, "role=");
+		if (pos) {
+			role = strtol(pos + 5, NULL, 16);
+			if (role != P2PS_SETUP_CLIENT &&
+			    role != P2PS_SETUP_GROUP_OWNER)
+				role = P2PS_SETUP_NONE;
+		}
+	}
+	p2ps_prov->role = role;
+
+	return p2ps_prov;
+
+invalid_args:
+	os_free(p2ps_prov);
+	return NULL;
+}
+
+
+static int p2p_ctrl_asp_provision_resp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	struct p2ps_provision *p2ps_prov;
+	char *pos;
+
+	/* <addr> id=<adv_id> [role=<conncap>] [info=<infodata>] */
+
+	wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	pos = cmd + 17;
+	if (*pos != ' ')
+		return -1;
+
+	p2ps_prov = p2p_parse_asp_provision_cmd(pos);
+	if (!p2ps_prov)
+		return -1;
+
+	if (p2ps_prov->status < 0) {
+		os_free(p2ps_prov);
+		return -1;
+	}
+
+	return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
+				  p2ps_prov);
+}
+
+
+static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	struct p2ps_provision *p2ps_prov;
+	char *pos;
+
+	/* <addr> id=<adv_id> adv_mac=<adv_mac> conncap=<conncap>
+	 *        session=<ses_id> mac=<ses_mac> [info=<infodata>]
+	 */
+
+	wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	pos = cmd + 17;
+	if (*pos != ' ')
+		return -1;
+
+	p2ps_prov = p2p_parse_asp_provision_cmd(pos);
+	if (!p2ps_prov)
+		return -1;
+
+	p2ps_prov->pd_seeker = 1;
+
+	return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
+				  p2ps_prov);
+}
+
+
+static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
+			    char *buf, size_t buflen)
+{
+	u8 addr[ETH_ALEN];
+	char *pos, *pos2;
+	char *pin = NULL;
+	enum p2p_wps_method wps_method;
+	int new_pin;
+	int ret;
+	int persistent_group, persistent_id = -1;
+	int join;
+	int auth;
+	int automatic;
+	int go_intent = -1;
+	int freq = 0;
+	int pd;
+	int ht40, vht;
+
+	if (!wpa_s->global->p2p_init_wpa_s)
+		return -1;
+	if (wpa_s->global->p2p_init_wpa_s != wpa_s) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Direct P2P_CONNECT command to %s",
+			wpa_s->global->p2p_init_wpa_s->ifname);
+		wpa_s = wpa_s->global->p2p_init_wpa_s;
+	}
+
+	/* <addr> <"pbc" | "pin" | PIN> [label|display|keypad|p2ps]
+	 * [persistent|persistent=<network id>]
+	 * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
+	 * [ht40] [vht] [auto] */
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	pos = cmd + 17;
+	if (*pos != ' ')
+		return -1;
+	pos++;
+
+	persistent_group = os_strstr(pos, " persistent") != NULL;
+	pos2 = os_strstr(pos, " persistent=");
+	if (pos2) {
+		struct wpa_ssid *ssid;
+		persistent_id = atoi(pos2 + 12);
+		ssid = wpa_config_get_network(wpa_s->conf, persistent_id);
+		if (ssid == NULL || ssid->disabled != 2 ||
+		    ssid->mode != WPAS_MODE_P2P_GO) {
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+				   "SSID id=%d for persistent P2P group (GO)",
+				   persistent_id);
+			return -1;
+		}
+	}
+	join = os_strstr(pos, " join") != NULL;
+	auth = os_strstr(pos, " auth") != NULL;
+	automatic = os_strstr(pos, " auto") != NULL;
+	pd = os_strstr(pos, " provdisc") != NULL;
+	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) {
+		pos2 += 11;
+		go_intent = atoi(pos2);
+		if (go_intent < 0 || go_intent > 15)
+			return -1;
+	}
+
+	pos2 = os_strstr(pos, " freq=");
+	if (pos2) {
+		pos2 += 6;
+		freq = atoi(pos2);
+		if (freq <= 0)
+			return -1;
+	}
+
+	if (os_strncmp(pos, "pin", 3) == 0) {
+		/* Request random PIN (to be displayed) and enable the PIN */
+		wps_method = WPS_PIN_DISPLAY;
+	} else if (os_strncmp(pos, "pbc", 3) == 0) {
+		wps_method = WPS_PBC;
+	} else {
+		pin = pos;
+		pos = os_strchr(pin, ' ');
+		wps_method = WPS_PIN_KEYPAD;
+		if (pos) {
+			*pos++ = '\0';
+			if (os_strncmp(pos, "display", 7) == 0)
+				wps_method = WPS_PIN_DISPLAY;
+			else if (os_strncmp(pos, "p2ps", 4) == 0)
+				wps_method = WPS_P2PS;
+		}
+		if (!wps_pin_str_valid(pin)) {
+			os_memcpy(buf, "FAIL-INVALID-PIN\n", 17);
+			return 17;
+		}
+	}
+
+	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
+				   persistent_group, automatic, join,
+				   auth, go_intent, freq, persistent_id, pd,
+				   ht40, vht);
+	if (new_pin == -2) {
+		os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
+		return 25;
+	}
+	if (new_pin == -3) {
+		os_memcpy(buf, "FAIL-CHANNEL-UNSUPPORTED\n", 25);
+		return 25;
+	}
+	if (new_pin < 0)
+		return -1;
+	if (wps_method == WPS_PIN_DISPLAY && pin == NULL) {
+		ret = os_snprintf(buf, buflen, "%08d", new_pin);
+		if (os_snprintf_error(buflen, ret))
+			return -1;
+		return ret;
+	}
+
+	os_memcpy(buf, "OK\n", 3);
+	return 3;
+}
+
+
+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);
+}
+
+
+static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	char *pos;
+	enum wpas_p2p_prov_disc_use use = WPAS_P2P_PD_FOR_GO_NEG;
+
+	/* <addr> <config method> [join|auto] */
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	pos = cmd + 17;
+	if (*pos != ' ')
+		return -1;
+	pos++;
+
+	if (os_strstr(pos, " join") != NULL)
+		use = WPAS_P2P_PD_FOR_JOIN;
+	else if (os_strstr(pos, " auto") != NULL)
+		use = WPAS_P2P_PD_AUTO;
+
+	return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL);
+}
+
+
+static int p2p_get_passphrase(struct wpa_supplicant *wpa_s, char *buf,
+			      size_t buflen)
+{
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
+	    ssid->passphrase == NULL)
+		return -1;
+
+	os_strlcpy(buf, ssid->passphrase, buflen);
+	return os_strlen(buf);
+}
+
+
+static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd,
+				  char *buf, size_t buflen)
+{
+	u64 ref;
+	int res;
+	u8 dst_buf[ETH_ALEN], *dst;
+	struct wpabuf *tlvs;
+	char *pos;
+	size_t len;
+
+	if (hwaddr_aton(cmd, dst_buf))
+		return -1;
+	dst = dst_buf;
+	if (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 &&
+	    dst[3] == 0 && dst[4] == 0 && dst[5] == 0)
+		dst = NULL;
+	pos = cmd + 17;
+	if (*pos != ' ')
+		return -1;
+	pos++;
+
+	if (os_strncmp(pos, "upnp ", 5) == 0) {
+		u8 version;
+		pos += 5;
+		if (hexstr2bin(pos, &version, 1) < 0)
+			return -1;
+		pos += 2;
+		if (*pos != ' ')
+			return -1;
+		pos++;
+		ref = wpas_p2p_sd_request_upnp(wpa_s, dst, version, pos);
+#ifdef CONFIG_WIFI_DISPLAY
+	} else if (os_strncmp(pos, "wifi-display ", 13) == 0) {
+		ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13);
+#endif /* CONFIG_WIFI_DISPLAY */
+	} else if (os_strncmp(pos, "asp ", 4) == 0) {
+		char *svc_str;
+		char *svc_info = NULL;
+		u32 id;
+
+		pos += 4;
+		if (sscanf(pos, "%x", &id) != 1 || id > 0xff)
+			return -1;
+
+		pos = os_strchr(pos, ' ');
+		if (pos == NULL || pos[1] == '\0' || pos[1] == ' ')
+			return -1;
+
+		svc_str = pos + 1;
+
+		pos = os_strchr(svc_str, ' ');
+
+		if (pos)
+			*pos++ = '\0';
+
+		/* All remaining data is the svc_info string */
+		if (pos && pos[0] && pos[0] != ' ') {
+			len = os_strlen(pos);
+
+			/* Unescape in place */
+			len = utf8_unescape(pos, len, pos, len);
+			if (len > 0xff)
+				return -1;
+
+			svc_info = pos;
+		}
+
+		ref = wpas_p2p_sd_request_asp(wpa_s, dst, (u8) id,
+					      svc_str, svc_info);
+	} else {
+		len = os_strlen(pos);
+		if (len & 1)
+			return -1;
+		len /= 2;
+		tlvs = wpabuf_alloc(len);
+		if (tlvs == NULL)
+			return -1;
+		if (hexstr2bin(pos, wpabuf_put(tlvs, len), len) < 0) {
+			wpabuf_free(tlvs);
+			return -1;
+		}
+
+		ref = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+		wpabuf_free(tlvs);
+	}
+	if (ref == 0)
+		return -1;
+	res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref);
+	if (os_snprintf_error(buflen, res))
+		return -1;
+	return res;
+}
+
+
+static int p2p_ctrl_serv_disc_cancel_req(struct wpa_supplicant *wpa_s,
+					 char *cmd)
+{
+	long long unsigned val;
+	u64 req;
+	if (sscanf(cmd, "%llx", &val) != 1)
+		return -1;
+	req = val;
+	return wpas_p2p_sd_cancel_request(wpa_s, req);
+}
+
+
+static int p2p_ctrl_serv_disc_resp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int freq;
+	u8 dst[ETH_ALEN];
+	u8 dialog_token;
+	struct wpabuf *resp_tlvs;
+	char *pos, *pos2;
+	size_t len;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+	freq = atoi(cmd);
+	if (freq == 0)
+		return -1;
+
+	if (hwaddr_aton(pos, dst))
+		return -1;
+	pos += 17;
+	if (*pos != ' ')
+		return -1;
+	pos++;
+
+	pos2 = os_strchr(pos, ' ');
+	if (pos2 == NULL)
+		return -1;
+	*pos2++ = '\0';
+	dialog_token = atoi(pos);
+
+	len = os_strlen(pos2);
+	if (len & 1)
+		return -1;
+	len /= 2;
+	resp_tlvs = wpabuf_alloc(len);
+	if (resp_tlvs == NULL)
+		return -1;
+	if (hexstr2bin(pos2, wpabuf_put(resp_tlvs, len), len) < 0) {
+		wpabuf_free(resp_tlvs);
+		return -1;
+	}
+
+	wpas_p2p_sd_response(wpa_s, freq, dst, dialog_token, resp_tlvs);
+	wpabuf_free(resp_tlvs);
+	return 0;
+}
+
+
+static int p2p_ctrl_serv_disc_external(struct wpa_supplicant *wpa_s,
+				       char *cmd)
+{
+	if (os_strcmp(cmd, "0") && os_strcmp(cmd, "1"))
+		return -1;
+	wpa_s->p2p_sd_over_ctrl_iface = atoi(cmd);
+	return 0;
+}
+
+
+static int p2p_ctrl_service_add_bonjour(struct wpa_supplicant *wpa_s,
+					char *cmd)
+{
+	char *pos;
+	size_t len;
+	struct wpabuf *query, *resp;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	len = os_strlen(cmd);
+	if (len & 1)
+		return -1;
+	len /= 2;
+	query = wpabuf_alloc(len);
+	if (query == NULL)
+		return -1;
+	if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
+		wpabuf_free(query);
+		return -1;
+	}
+
+	len = os_strlen(pos);
+	if (len & 1) {
+		wpabuf_free(query);
+		return -1;
+	}
+	len /= 2;
+	resp = wpabuf_alloc(len);
+	if (resp == NULL) {
+		wpabuf_free(query);
+		return -1;
+	}
+	if (hexstr2bin(pos, wpabuf_put(resp, len), len) < 0) {
+		wpabuf_free(query);
+		wpabuf_free(resp);
+		return -1;
+	}
+
+	if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) {
+		wpabuf_free(query);
+		wpabuf_free(resp);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int p2p_ctrl_service_add_upnp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos;
+	u8 version;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	if (hexstr2bin(cmd, &version, 1) < 0)
+		return -1;
+
+	return wpas_p2p_service_add_upnp(wpa_s, version, pos);
+}
+
+
+static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s,
+				    u8 replace, char *cmd)
+{
+	char *pos;
+	char *adv_str;
+	u32 auto_accept, adv_id, svc_state, config_methods;
+	char *svc_info = NULL;
+	char *cpt_prio_str;
+	u8 cpt_prio[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	/* Auto-Accept value is mandatory, and must be one of the
+	 * single values (0, 1, 2, 4) */
+	auto_accept = atoi(cmd);
+	switch (auto_accept) {
+	case P2PS_SETUP_NONE: /* No auto-accept */
+	case P2PS_SETUP_NEW:
+	case P2PS_SETUP_CLIENT:
+	case P2PS_SETUP_GROUP_OWNER:
+		break;
+	default:
+		return -1;
+	}
+
+	/* Advertisement ID is mandatory */
+	cmd = pos;
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	/* Handle Adv_ID == 0 (wildcard "org.wi-fi.wfds") internally. */
+	if (sscanf(cmd, "%x", &adv_id) != 1 || adv_id == 0)
+		return -1;
+
+	/* Only allow replacements if exist, and adds if not */
+	if (wpas_p2p_service_p2ps_id_exists(wpa_s, adv_id)) {
+		if (!replace)
+			return -1;
+	} else {
+		if (replace)
+			return -1;
+	}
+
+	/* svc_state between 0 - 0xff is mandatory */
+	if (sscanf(pos, "%x", &svc_state) != 1 || svc_state > 0xff)
+		return -1;
+
+	pos = os_strchr(pos, ' ');
+	if (pos == NULL)
+		return -1;
+
+	/* config_methods is mandatory */
+	pos++;
+	if (sscanf(pos, "%x", &config_methods) != 1)
+		return -1;
+
+	if (!(config_methods &
+	      (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS)))
+		return -1;
+
+	pos = os_strchr(pos, ' ');
+	if (pos == NULL)
+		return -1;
+
+	pos++;
+	adv_str = pos;
+
+	/* Advertisement string is mandatory */
+	if (!pos[0] || pos[0] == ' ')
+		return -1;
+
+	/* Terminate svc string */
+	pos = os_strchr(pos, ' ');
+	if (pos != NULL)
+		*pos++ = '\0';
+
+	cpt_prio_str = (pos && pos[0]) ? os_strstr(pos, "cpt=") : NULL;
+	if (cpt_prio_str) {
+		pos = os_strchr(pos, ' ');
+		if (pos != NULL)
+			*pos++ = '\0';
+
+		if (p2ps_ctrl_parse_cpt_priority(cpt_prio_str + 4, cpt_prio))
+			return -1;
+	} else {
+		cpt_prio[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+		cpt_prio[1] = 0;
+	}
+
+	/* Service and Response Information are optional */
+	if (pos && pos[0]) {
+		size_t len;
+
+		/* Note the bare ' included, which cannot exist legally
+		 * in unescaped string. */
+		svc_info = os_strstr(pos, "svc_info='");
+
+		if (svc_info) {
+			svc_info += 9;
+			len = os_strlen(svc_info);
+			utf8_unescape(svc_info, len, svc_info, len);
+		}
+	}
+
+	return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str,
+					(u8) svc_state, (u16) config_methods,
+					svc_info, cpt_prio);
+}
+
+
+static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	if (os_strcmp(cmd, "bonjour") == 0)
+		return p2p_ctrl_service_add_bonjour(wpa_s, pos);
+	if (os_strcmp(cmd, "upnp") == 0)
+		return p2p_ctrl_service_add_upnp(wpa_s, pos);
+	if (os_strcmp(cmd, "asp") == 0)
+		return p2p_ctrl_service_add_asp(wpa_s, 0, pos);
+	wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
+	return -1;
+}
+
+
+static int p2p_ctrl_service_del_bonjour(struct wpa_supplicant *wpa_s,
+					char *cmd)
+{
+	size_t len;
+	struct wpabuf *query;
+	int ret;
+
+	len = os_strlen(cmd);
+	if (len & 1)
+		return -1;
+	len /= 2;
+	query = wpabuf_alloc(len);
+	if (query == NULL)
+		return -1;
+	if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
+		wpabuf_free(query);
+		return -1;
+	}
+
+	ret = wpas_p2p_service_del_bonjour(wpa_s, query);
+	wpabuf_free(query);
+	return ret;
+}
+
+
+static int p2p_ctrl_service_del_upnp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos;
+	u8 version;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	if (hexstr2bin(cmd, &version, 1) < 0)
+		return -1;
+
+	return wpas_p2p_service_del_upnp(wpa_s, version, pos);
+}
+
+
+static int p2p_ctrl_service_del_asp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u32 adv_id;
+
+	if (os_strcmp(cmd, "all") == 0) {
+		wpas_p2p_service_flush_asp(wpa_s);
+		return 0;
+	}
+
+	if (sscanf(cmd, "%x", &adv_id) != 1)
+		return -1;
+
+	return wpas_p2p_service_del_asp(wpa_s, adv_id);
+}
+
+
+static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	if (os_strcmp(cmd, "bonjour") == 0)
+		return p2p_ctrl_service_del_bonjour(wpa_s, pos);
+	if (os_strcmp(cmd, "upnp") == 0)
+		return p2p_ctrl_service_del_upnp(wpa_s, pos);
+	if (os_strcmp(cmd, "asp") == 0)
+		return p2p_ctrl_service_del_asp(wpa_s, pos);
+	wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
+	return -1;
+}
+
+
+static int p2p_ctrl_service_replace(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	if (os_strcmp(cmd, "asp") == 0)
+		return p2p_ctrl_service_add_asp(wpa_s, 1, pos);
+
+	wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
+	return -1;
+}
+
+
+static int p2p_ctrl_reject(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 addr[ETH_ALEN];
+
+	/* <addr> */
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	return wpas_p2p_reject(wpa_s, addr);
+}
+
+
+static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos;
+	int id;
+	struct wpa_ssid *ssid;
+	u8 *_peer = NULL, peer[ETH_ALEN];
+	int freq = 0, pref_freq = 0;
+	int ht40, vht;
+
+	id = atoi(cmd);
+	pos = os_strstr(cmd, " peer=");
+	if (pos) {
+		pos += 6;
+		if (hwaddr_aton(pos, peer))
+			return -1;
+		_peer = peer;
+	}
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid == NULL || ssid->disabled != 2) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
+			   "for persistent P2P group",
+			   id);
+		return -1;
+	}
+
+	pos = os_strstr(cmd, " freq=");
+	if (pos) {
+		pos += 6;
+		freq = atoi(pos);
+		if (freq <= 0)
+			return -1;
+	}
+
+	pos = os_strstr(cmd, " pref=");
+	if (pos) {
+		pos += 6;
+		pref_freq = atoi(pos);
+		if (pref_freq <= 0)
+			return -1;
+	}
+
+	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, vht,
+			       pref_freq);
+}
+
+
+static int p2p_ctrl_invite_group(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos;
+	u8 peer[ETH_ALEN], go_dev_addr[ETH_ALEN], *go_dev = NULL;
+
+	pos = os_strstr(cmd, " peer=");
+	if (!pos)
+		return -1;
+
+	*pos = '\0';
+	pos += 6;
+	if (hwaddr_aton(pos, peer)) {
+		wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", pos);
+		return -1;
+	}
+
+	pos = os_strstr(pos, " go_dev_addr=");
+	if (pos) {
+		pos += 13;
+		if (hwaddr_aton(pos, go_dev_addr)) {
+			wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'",
+				   pos);
+			return -1;
+		}
+		go_dev = go_dev_addr;
+	}
+
+	return wpas_p2p_invite_group(wpa_s, cmd, peer, go_dev);
+}
+
+
+static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	if (os_strncmp(cmd, "persistent=", 11) == 0)
+		return p2p_ctrl_invite_persistent(wpa_s, cmd + 11);
+	if (os_strncmp(cmd, "group=", 6) == 0)
+		return p2p_ctrl_invite_group(wpa_s, cmd + 6);
+
+	return -1;
+}
+
+
+static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
+					 int id, int freq, int ht40, int vht)
+{
+	struct wpa_ssid *ssid;
+
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid == NULL || ssid->disabled != 2) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
+			   "for persistent P2P group",
+			   id);
+		return -1;
+	}
+
+	return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht,
+					     NULL, 0, 0);
+}
+
+
+static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int freq = 0, persistent = 0, group_id = -1;
+	int vht = wpa_s->conf->p2p_go_vht;
+	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
+	char *token, *context = NULL;
+
+	while ((token = str_token(cmd, " ", &context))) {
+		if (sscanf(token, "freq=%d", &freq) == 1 ||
+		    sscanf(token, "persistent=%d", &group_id) == 1) {
+			continue;
+		} else if (os_strcmp(token, "ht40") == 0) {
+			ht40 = 1;
+		} else if (os_strcmp(token, "vht") == 0) {
+			vht = 1;
+			ht40 = 1;
+		} else if (os_strcmp(token, "persistent") == 0) {
+			persistent = 1;
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "CTRL: Invalid P2P_GROUP_ADD parameter: '%s'",
+				   token);
+			return -1;
+		}
+	}
+
+	if (group_id >= 0)
+		return p2p_ctrl_group_add_persistent(wpa_s, group_id,
+						     freq, ht40, vht);
+
+	return wpas_p2p_group_add(wpa_s, persistent, freq, ht40, vht);
+}
+
+
+static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
+			 char *buf, size_t buflen)
+{
+	u8 addr[ETH_ALEN], *addr_ptr;
+	int next, res;
+	const struct p2p_peer_info *info;
+	char *pos, *end;
+	char devtype[WPS_DEV_TYPE_BUFSIZE];
+	struct wpa_ssid *ssid;
+	size_t i;
+
+	if (!wpa_s->global->p2p)
+		return -1;
+
+	if (os_strcmp(cmd, "FIRST") == 0) {
+		addr_ptr = NULL;
+		next = 0;
+	} else if (os_strncmp(cmd, "NEXT-", 5) == 0) {
+		if (hwaddr_aton(cmd + 5, addr) < 0)
+			return -1;
+		addr_ptr = addr;
+		next = 1;
+	} else {
+		if (hwaddr_aton(cmd, addr) < 0)
+			return -1;
+		addr_ptr = addr;
+		next = 0;
+	}
+
+	info = p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next);
+	if (info == NULL)
+		return -1;
+
+	pos = buf;
+	end = buf + buflen;
+
+	res = os_snprintf(pos, end - pos, MACSTR "\n"
+			  "pri_dev_type=%s\n"
+			  "device_name=%s\n"
+			  "manufacturer=%s\n"
+			  "model_name=%s\n"
+			  "model_number=%s\n"
+			  "serial_number=%s\n"
+			  "config_methods=0x%x\n"
+			  "dev_capab=0x%x\n"
+			  "group_capab=0x%x\n"
+			  "level=%d\n",
+			  MAC2STR(info->p2p_device_addr),
+			  wps_dev_type_bin2str(info->pri_dev_type,
+					       devtype, sizeof(devtype)),
+			  info->device_name,
+			  info->manufacturer,
+			  info->model_name,
+			  info->model_number,
+			  info->serial_number,
+			  info->config_methods,
+			  info->dev_capab,
+			  info->group_capab,
+			  info->level);
+	if (os_snprintf_error(end - pos, res))
+		return pos - buf;
+	pos += res;
+
+	for (i = 0; i < info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN; i++)
+	{
+		const u8 *t;
+		t = &info->wps_sec_dev_type_list[i * WPS_DEV_TYPE_LEN];
+		res = os_snprintf(pos, end - pos, "sec_dev_type=%s\n",
+				  wps_dev_type_bin2str(t, devtype,
+						       sizeof(devtype)));
+		if (os_snprintf_error(end - pos, res))
+			return pos - buf;
+		pos += res;
+	}
+
+	ssid = wpas_p2p_get_persistent(wpa_s, info->p2p_device_addr, NULL, 0);
+	if (ssid) {
+		res = os_snprintf(pos, end - pos, "persistent=%d\n", ssid->id);
+		if (os_snprintf_error(end - pos, res))
+			return pos - buf;
+		pos += res;
+	}
+
+	res = p2p_get_peer_info_txt(info, pos, end - pos);
+	if (res < 0)
+		return pos - buf;
+	pos += res;
+
+	if (info->vendor_elems) {
+		res = os_snprintf(pos, end - pos, "vendor_elems=");
+		if (os_snprintf_error(end - pos, res))
+			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 (os_snprintf_error(end - pos, res))
+			return pos - buf;
+		pos += res;
+	}
+
+	return pos - buf;
+}
+
+
+static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s,
+				  const char *param)
+{
+	unsigned int i;
+
+	if (wpa_s->global->p2p == NULL)
+		return -1;
+
+	if (freq_range_list_parse(&wpa_s->global->p2p_disallow_freq, param) < 0)
+		return -1;
+
+	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->min, freq->max);
+	}
+
+	wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DISALLOW);
+	return 0;
+}
+
+
+static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *param;
+
+	if (wpa_s->global->p2p == NULL)
+		return -1;
+
+	param = os_strchr(cmd, ' ');
+	if (param == NULL)
+		return -1;
+	*param++ = '\0';
+
+	if (os_strcmp(cmd, "discoverability") == 0) {
+		p2p_set_client_discoverability(wpa_s->global->p2p,
+					       atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "managed") == 0) {
+		p2p_set_managed_oper(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "listen_channel") == 0) {
+		return p2p_set_listen_channel(wpa_s->global->p2p, 81,
+					      atoi(param), 1);
+	}
+
+	if (os_strcmp(cmd, "ssid_postfix") == 0) {
+		return p2p_set_ssid_postfix(wpa_s->global->p2p, (u8 *) param,
+					    os_strlen(param));
+	}
+
+	if (os_strcmp(cmd, "noa") == 0) {
+		char *pos;
+		int count, start, duration;
+		/* GO NoA parameters: count,start_offset(ms),duration(ms) */
+		count = atoi(param);
+		pos = os_strchr(param, ',');
+		if (pos == NULL)
+			return -1;
+		pos++;
+		start = atoi(pos);
+		pos = os_strchr(pos, ',');
+		if (pos == NULL)
+			return -1;
+		pos++;
+		duration = atoi(pos);
+		if (count < 0 || count > 255 || start < 0 || duration < 0)
+			return -1;
+		if (count == 0 && duration > 0)
+			return -1;
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: P2P_SET GO NoA: count=%d "
+			   "start=%d duration=%d", count, start, duration);
+		return wpas_p2p_set_noa(wpa_s, count, start, duration);
+	}
+
+	if (os_strcmp(cmd, "ps") == 0)
+		return wpa_drv_set_p2p_powersave(wpa_s, atoi(param), -1, -1);
+
+	if (os_strcmp(cmd, "oppps") == 0)
+		return wpa_drv_set_p2p_powersave(wpa_s, -1, atoi(param), -1);
+
+	if (os_strcmp(cmd, "ctwindow") == 0)
+		return wpa_drv_set_p2p_powersave(wpa_s, -1, -1, atoi(param));
+
+	if (os_strcmp(cmd, "disabled") == 0) {
+		wpa_s->global->p2p_disabled = atoi(param);
+		wpa_printf(MSG_DEBUG, "P2P functionality %s",
+			   wpa_s->global->p2p_disabled ?
+			   "disabled" : "enabled");
+		if (wpa_s->global->p2p_disabled) {
+			wpas_p2p_stop_find(wpa_s);
+			os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+			p2p_flush(wpa_s->global->p2p);
+		}
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "conc_pref") == 0) {
+		if (os_strcmp(param, "sta") == 0)
+			wpa_s->global->conc_pref = WPA_CONC_PREF_STA;
+		else if (os_strcmp(param, "p2p") == 0)
+			wpa_s->global->conc_pref = WPA_CONC_PREF_P2P;
+		else {
+			wpa_printf(MSG_INFO, "Invalid conc_pref value");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "Single channel concurrency preference: "
+			   "%s", param);
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "force_long_sd") == 0) {
+		wpa_s->force_long_sd = atoi(param);
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "peer_filter") == 0) {
+		u8 addr[ETH_ALEN];
+		if (hwaddr_aton(param, addr))
+			return -1;
+		p2p_set_peer_filter(wpa_s->global->p2p, addr);
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "cross_connect") == 0)
+		return wpas_p2p_set_cross_connect(wpa_s, atoi(param));
+
+	if (os_strcmp(cmd, "go_apsd") == 0) {
+		if (os_strcmp(param, "disable") == 0)
+			wpa_s->set_ap_uapsd = 0;
+		else {
+			wpa_s->set_ap_uapsd = 1;
+			wpa_s->ap_uapsd = atoi(param);
+		}
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "client_apsd") == 0) {
+		if (os_strcmp(param, "disable") == 0)
+			wpa_s->set_sta_uapsd = 0;
+		else {
+			int be, bk, vi, vo;
+			char *pos;
+			/* format: BE,BK,VI,VO;max SP Length */
+			be = atoi(param);
+			pos = os_strchr(param, ',');
+			if (pos == NULL)
+				return -1;
+			pos++;
+			bk = atoi(pos);
+			pos = os_strchr(pos, ',');
+			if (pos == NULL)
+				return -1;
+			pos++;
+			vi = atoi(pos);
+			pos = os_strchr(pos, ',');
+			if (pos == NULL)
+				return -1;
+			pos++;
+			vo = atoi(pos);
+			/* ignore max SP Length for now */
+
+			wpa_s->set_sta_uapsd = 1;
+			wpa_s->sta_uapsd = 0;
+			if (be)
+				wpa_s->sta_uapsd |= BIT(0);
+			if (bk)
+				wpa_s->sta_uapsd |= BIT(1);
+			if (vi)
+				wpa_s->sta_uapsd |= BIT(2);
+			if (vo)
+				wpa_s->sta_uapsd |= BIT(3);
+		}
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "disallow_freq") == 0)
+		return p2p_ctrl_disallow_freq(wpa_s, param);
+
+	if (os_strcmp(cmd, "disc_int") == 0) {
+		int min_disc_int, max_disc_int, max_disc_tu;
+		char *pos;
+
+		pos = param;
+
+		min_disc_int = atoi(pos);
+		pos = os_strchr(pos, ' ');
+		if (pos == NULL)
+			return -1;
+		*pos++ = '\0';
+
+		max_disc_int = atoi(pos);
+		pos = os_strchr(pos, ' ');
+		if (pos == NULL)
+			return -1;
+		*pos++ = '\0';
+
+		max_disc_tu = atoi(pos);
+
+		return p2p_set_disc_int(wpa_s->global->p2p, min_disc_int,
+					max_disc_int, max_disc_tu);
+	}
+
+	if (os_strcmp(cmd, "per_sta_psk") == 0) {
+		wpa_s->global->p2p_per_sta_psk = !!atoi(param);
+		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);
+
+	return -1;
+}
+
+
+static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s)
+{
+	os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+	wpa_s->force_long_sd = 0;
+	wpas_p2p_stop_find(wpa_s);
+	wpa_s->parent->p2ps_method_config_any = 0;
+	if (wpa_s->global->p2p)
+		p2p_flush(wpa_s->global->p2p);
+}
+
+
+static int p2p_ctrl_presence_req(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos, *pos2;
+	unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0;
+
+	if (cmd[0]) {
+		pos = os_strchr(cmd, ' ');
+		if (pos == NULL)
+			return -1;
+		*pos++ = '\0';
+		dur1 = atoi(cmd);
+
+		pos2 = os_strchr(pos, ' ');
+		if (pos2)
+			*pos2++ = '\0';
+		int1 = atoi(pos);
+	} else
+		pos2 = NULL;
+
+	if (pos2) {
+		pos = os_strchr(pos2, ' ');
+		if (pos == NULL)
+			return -1;
+		*pos++ = '\0';
+		dur2 = atoi(pos2);
+		int2 = atoi(pos);
+	}
+
+	return wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2);
+}
+
+
+static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos;
+	unsigned int period = 0, interval = 0;
+
+	if (cmd[0]) {
+		pos = os_strchr(cmd, ' ');
+		if (pos == NULL)
+			return -1;
+		*pos++ = '\0';
+		period = atoi(cmd);
+		interval = atoi(pos);
+	}
+
+	return wpas_p2p_ext_listen(wpa_s, period, interval);
+}
+
+
+static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	const char *pos;
+	u8 peer[ETH_ALEN];
+	int iface_addr = 0;
+
+	pos = cmd;
+	if (os_strncmp(pos, "iface=", 6) == 0) {
+		iface_addr = 1;
+		pos += 6;
+	}
+	if (hwaddr_aton(pos, peer))
+		return -1;
+
+	wpas_p2p_remove_client(wpa_s, peer, iface_addr);
+	return 0;
+}
+
+#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,
+				     int only_add)
+{
+	u8 bssid[ETH_ALEN];
+	struct wpa_bss *bss;
+
+	if (hwaddr_aton(dst, bssid)) {
+		wpa_printf(MSG_DEBUG, "Invalid BSSID '%s'", dst);
+		return -1;
+	}
+
+	bss = wpa_bss_get_bssid(wpa_s, bssid);
+	if (bss == NULL) {
+		wpa_printf(MSG_DEBUG, "Could not find BSS " MACSTR,
+			   MAC2STR(bssid));
+		return -1;
+	}
+
+	if (bss->ssid_len == 0) {
+		int found = 0;
+
+		wpa_printf(MSG_DEBUG, "Selected BSS entry for " MACSTR
+			   " does not have SSID information", MAC2STR(bssid));
+
+		dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss,
+					 list) {
+			if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
+			    bss->ssid_len > 0) {
+				found = 1;
+				break;
+			}
+		}
+
+		if (!found)
+			return -1;
+		wpa_printf(MSG_DEBUG,
+			   "Found another matching BSS entry with SSID");
+	}
+
+	return interworking_connect(wpa_s, bss, only_add);
+}
+
+
+static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
+{
+	u8 dst_addr[ETH_ALEN];
+	int used;
+	char *pos;
+#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;
+	if (*pos == ' ')
+		pos++;
+	while (num_id < MAX_ANQP_INFO_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;
+		pos++;
+	}
+
+	if (num_id == 0)
+		return -1;
+
+	return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes);
+}
+
+
+static int gas_request(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 dst_addr[ETH_ALEN];
+	struct wpabuf *advproto, *query = NULL;
+	int used, ret = -1;
+	char *pos, *end;
+	size_t len;
+
+	used = hwaddr_aton2(cmd, dst_addr);
+	if (used < 0)
+		return -1;
+
+	pos = cmd + used;
+	while (*pos == ' ')
+		pos++;
+
+	/* Advertisement Protocol ID */
+	end = os_strchr(pos, ' ');
+	if (end)
+		len = end - pos;
+	else
+		len = os_strlen(pos);
+	if (len & 0x01)
+		return -1;
+	len /= 2;
+	if (len == 0)
+		return -1;
+	advproto = wpabuf_alloc(len);
+	if (advproto == NULL)
+		return -1;
+	if (hexstr2bin(pos, wpabuf_put(advproto, len), len) < 0)
+		goto fail;
+
+	if (end) {
+		/* Optional Query Request */
+		pos = end + 1;
+		while (*pos == ' ')
+			pos++;
+
+		len = os_strlen(pos);
+		if (len) {
+			if (len & 0x01)
+				goto fail;
+			len /= 2;
+			if (len == 0)
+				goto fail;
+			query = wpabuf_alloc(len);
+			if (query == NULL)
+				goto fail;
+			if (hexstr2bin(pos, wpabuf_put(query, len), len) < 0)
+				goto fail;
+		}
+	}
+
+	ret = gas_send_request(wpa_s, dst_addr, advproto, query);
+
+fail:
+	wpabuf_free(advproto);
+	wpabuf_free(query);
+
+	return ret;
+}
+
+
+static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
+			    size_t buflen)
+{
+	u8 addr[ETH_ALEN];
+	int dialog_token;
+	int used;
+	char *pos;
+	size_t resp_len, start, requested_len;
+	struct wpabuf *resp;
+	int ret;
+
+	used = hwaddr_aton2(cmd, addr);
+	if (used < 0)
+		return -1;
+
+	pos = cmd + used;
+	while (*pos == ' ')
+		pos++;
+	dialog_token = atoi(pos);
+
+	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(resp);
+	start = 0;
+	requested_len = resp_len;
+
+	pos = os_strchr(pos, ' ');
+	if (pos) {
+		start = atoi(pos);
+		if (start > resp_len)
+			return os_snprintf(buf, buflen, "FAIL-Invalid range");
+		pos = os_strchr(pos, ',');
+		if (pos == NULL)
+			return -1;
+		pos++;
+		requested_len = atoi(pos);
+		if (start + requested_len > resp_len)
+			return os_snprintf(buf, buflen, "FAIL-Invalid range");
+	}
+
+	if (requested_len * 2 + 1 > buflen)
+		return os_snprintf(buf, buflen, "FAIL-Too long response");
+
+	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 */
+
+
+#ifdef CONFIG_HS20
+
+static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst)
+{
+	u8 dst_addr[ETH_ALEN];
+	int used;
+	char *pos;
+	u32 subtypes = 0;
+
+	used = hwaddr_aton2(dst, dst_addr);
+	if (used < 0)
+		return -1;
+	pos = dst + used;
+	if (*pos == ' ')
+		pos++;
+	for (;;) {
+		int num = atoi(pos);
+		if (num <= 0 || num > 31)
+			return -1;
+		subtypes |= BIT(num);
+		pos = os_strchr(pos + 1, ',');
+		if (pos == NULL)
+			break;
+		pos++;
+	}
+
+	if (subtypes == 0)
+		return -1;
+
+	return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0);
+}
+
+
+static int hs20_nai_home_realm_list(struct wpa_supplicant *wpa_s,
+				    const u8 *addr, const char *realm)
+{
+	u8 *buf;
+	size_t rlen, len;
+	int ret;
+
+	rlen = os_strlen(realm);
+	len = 3 + rlen;
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+	buf[0] = 1; /* NAI Home Realm Count */
+	buf[1] = 0; /* Formatted in accordance with RFC 4282 */
+	buf[2] = rlen;
+	os_memcpy(buf + 3, realm, rlen);
+
+	ret = hs20_anqp_send_req(wpa_s, addr,
+				 BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
+				 buf, len);
+
+	os_free(buf);
+
+	return ret;
+}
+
+
+static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s,
+					char *dst)
+{
+	struct wpa_cred *cred = wpa_s->conf->cred;
+	u8 dst_addr[ETH_ALEN];
+	int used;
+	u8 *buf;
+	size_t len;
+	int ret;
+
+	used = hwaddr_aton2(dst, dst_addr);
+	if (used < 0)
+		return -1;
+
+	while (dst[used] == ' ')
+		used++;
+	if (os_strncmp(dst + used, "realm=", 6) == 0)
+		return hs20_nai_home_realm_list(wpa_s, dst_addr,
+						dst + used + 6);
+
+	len = os_strlen(dst + used);
+
+	if (len == 0 && cred && cred->realm)
+		return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm);
+
+	if (len & 1)
+		return -1;
+	len /= 2;
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+	if (hexstr2bin(dst + used, buf, len) < 0) {
+		os_free(buf);
+		return -1;
+	}
+
+	ret = hs20_anqp_send_req(wpa_s, dst_addr,
+				 BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
+				 buf, len);
+	os_free(buf);
+
+	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 */
+
+
+#ifdef CONFIG_AUTOSCAN
+
+static int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s,
+					      char *cmd)
+{
+	enum wpa_states state = wpa_s->wpa_state;
+	char *new_params = NULL;
+
+	if (os_strlen(cmd) > 0) {
+		new_params = os_strdup(cmd);
+		if (new_params == NULL)
+			return -1;
+	}
+
+	os_free(wpa_s->conf->autoscan);
+	wpa_s->conf->autoscan = new_params;
+
+	if (wpa_s->conf->autoscan == NULL)
+		autoscan_deinit(wpa_s);
+	else if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
+		autoscan_init(wpa_s, 1);
+	else if (state == WPA_SCANNING)
+		wpa_supplicant_reinit_autoscan(wpa_s);
+
+	return 0;
+}
+
+#endif /* CONFIG_AUTOSCAN */
+
+
+#ifdef CONFIG_WNM
+
+static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int enter;
+	int intval = 0;
+	char *pos;
+	int ret;
+	struct wpabuf *tfs_req = NULL;
+
+	if (os_strncmp(cmd, "enter", 5) == 0)
+		enter = 1;
+	else if (os_strncmp(cmd, "exit", 4) == 0)
+		enter = 0;
+	else
+		return -1;
+
+	pos = os_strstr(cmd, " interval=");
+	if (pos)
+		intval = atoi(pos + 10);
+
+	pos = os_strstr(cmd, " tfs_req=");
+	if (pos) {
+		char *end;
+		size_t len;
+		pos += 9;
+		end = os_strchr(pos, ' ');
+		if (end)
+			len = end - pos;
+		else
+			len = os_strlen(pos);
+		if (len & 1)
+			return -1;
+		len /= 2;
+		tfs_req = wpabuf_alloc(len);
+		if (tfs_req == NULL)
+			return -1;
+		if (hexstr2bin(pos, wpabuf_put(tfs_req, len), len) < 0) {
+			wpabuf_free(tfs_req);
+			return -1;
+		}
+	}
+
+	ret = ieee802_11_send_wnmsleep_req(wpa_s, enter ? WNM_SLEEP_MODE_ENTER :
+					   WNM_SLEEP_MODE_EXIT, intval,
+					   tfs_req);
+	wpabuf_free(tfs_req);
+
+	return ret;
+}
+
+
+static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int query_reason;
+
+	query_reason = atoi(cmd);
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d",
+		   query_reason);
+
+	return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason);
+}
+
+#endif /* CONFIG_WNM */
+
+
+static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
+				      size_t buflen)
+{
+	struct wpa_signal_info si;
+	int ret;
+	char *pos, *end;
+
+	ret = wpa_drv_signal_poll(wpa_s, &si);
+	if (ret)
+		return -1;
+
+	pos = buf;
+	end = buf + buflen;
+
+	ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%d\n"
+			  "NOISE=%d\nFREQUENCY=%u\n",
+			  si.current_signal, si.current_txrate / 1000,
+			  si.current_noise, si.frequency);
+	if (os_snprintf_error(end - pos, ret))
+		return -1;
+	pos += ret;
+
+	if (si.chanwidth != CHAN_WIDTH_UNKNOWN) {
+		ret = os_snprintf(pos, end - pos, "WIDTH=%s\n",
+				  channel_width_to_string(si.chanwidth));
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+
+	if (si.center_frq1 > 0 && si.center_frq2 > 0) {
+		ret = os_snprintf(pos, end - pos,
+				  "CENTER_FRQ1=%d\nCENTER_FRQ2=%d\n",
+				  si.center_frq1, si.center_frq2);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+
+	if (si.avg_signal) {
+		ret = os_snprintf(pos, end - pos,
+				  "AVG_RSSI=%d\n", si.avg_signal);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+
+	if (si.avg_beacon_signal) {
+		ret = os_snprintf(pos, end - pos,
+				  "AVG_BEACON_RSSI=%d\n", si.avg_beacon_signal);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+static int wpas_ctrl_iface_get_pref_freq_list(
+	struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+	unsigned int freq_list[100], num = 100, i;
+	int ret;
+	enum wpa_driver_if_type iface_type;
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + buflen;
+
+	/* buf: "<interface_type>" */
+	if (os_strcmp(cmd, "STATION") == 0)
+		iface_type = WPA_IF_STATION;
+	else if (os_strcmp(cmd, "AP") == 0)
+		iface_type = WPA_IF_AP_BSS;
+	else if (os_strcmp(cmd, "P2P_GO") == 0)
+		iface_type = WPA_IF_P2P_GO;
+	else if (os_strcmp(cmd, "P2P_CLIENT") == 0)
+		iface_type = WPA_IF_P2P_CLIENT;
+	else if (os_strcmp(cmd, "IBSS") == 0)
+		iface_type = WPA_IF_IBSS;
+	else if (os_strcmp(cmd, "TDLS") == 0)
+		iface_type = WPA_IF_TDLS;
+	else
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)",
+		   iface_type, buf);
+
+	ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list);
+	if (ret)
+		return -1;
+
+	for (i = 0; i < num; i++) {
+		ret = os_snprintf(pos, end - pos, "%s%u",
+				  i > 0 ? "," : "", freq_list[i]);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
+				      size_t buflen)
+{
+	struct hostap_sta_driver_data sta;
+	int ret;
+
+	ret = wpa_drv_pktcnt_poll(wpa_s, &sta);
+	if (ret)
+		return -1;
+
+	ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n",
+			  sta.tx_packets, sta.tx_retry_failed, sta.rx_packets);
+	if (os_snprintf_error(buflen, ret))
+		return -1;
+	return ret;
+}
+
+
+#ifdef ANDROID
+static int wpa_supplicant_driver_cmd(struct wpa_supplicant *wpa_s, char *cmd,
+				     char *buf, size_t buflen)
+{
+	int ret;
+
+	ret = wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen);
+	if (ret == 0) {
+#ifdef P2P_CONFIG
+		if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) {
+			struct p2p_data *p2p = wpa_s->global->p2p;
+			if (p2p) {
+				char country[3];
+				country[0] = cmd[8];
+				country[1] = cmd[9];
+				country[2] = 0x04;
+				p2p_set_country(p2p, country);
+			}
+		}
+#endif /* P2P_CONFIG */
+		ret = os_snprintf(buf, buflen, "%s\n", "OK");
+		if (os_snprintf_error(buflen, ret))
+			ret = -1;
+	}
+	return ret;
+}
+#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)
+{
+#ifdef CONFIG_P2P
+	struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s ?
+		wpa_s->global->p2p_init_wpa_s : wpa_s;
+#endif /* CONFIG_P2P */
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
+
+#ifdef CONFIG_P2P
+	wpas_p2p_cancel(p2p_wpa_s);
+	p2p_ctrl_flush(p2p_wpa_s);
+	wpas_p2p_group_remove(p2p_wpa_s, "*");
+	wpas_p2p_service_flush(p2p_wpa_s);
+	p2p_wpa_s->global->p2p_disabled = 0;
+	p2p_wpa_s->global->p2p_per_sta_psk = 0;
+	p2p_wpa_s->conf->num_sec_device_types = 0;
+	p2p_wpa_s->p2p_disable_ip_addr_req = 0;
+	os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range);
+	p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL;
+	p2p_wpa_s->global->p2p_go_avoid_freq.num = 0;
+	p2p_wpa_s->global->pending_p2ps_group = 0;
+#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);
+	wps_registrar_flush(wpa_s->wps->registrar);
+#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 */
+	wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL);
+	wpa_tdls_enable(wpa_s->wpa, 1);
+#endif /* CONFIG_TDLS */
+
+	eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL);
+	wpa_supplicant_stop_countermeasures(wpa_s, NULL);
+
+	wpa_s->no_keep_alive = 0;
+	wpa_s->own_disconnect_req = 0;
+
+	os_free(wpa_s->disallow_aps_bssid);
+	wpa_s->disallow_aps_bssid = NULL;
+	wpa_s->disallow_aps_bssid_count = 0;
+	os_free(wpa_s->disallow_aps_ssid);
+	wpa_s->disallow_aps_ssid = NULL;
+	wpa_s->disallow_aps_ssid_count = 0;
+
+	wpa_s->set_sta_uapsd = 0;
+	wpa_s->sta_uapsd = 0;
+
+	wpa_drv_radio_disable(wpa_s, 0);
+	wpa_blacklist_clear(wpa_s);
+	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_pmksa_cache_flush(wpa_s->wpa, NULL);
+	rsn_preauth_deinit(wpa_s->wpa);
+
+	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->ext_work_in_progress = 0;
+
+	wpa_s->next_ssid = NULL;
+
+#ifdef CONFIG_INTERWORKING
+	hs20_cancel_fetch_osu(wpa_s);
+#endif /* CONFIG_INTERWORKING */
+
+	wpa_s->ext_mgmt_frame_handling = 0;
+	wpa_s->ext_eapol_frame_io = 0;
+#ifdef CONFIG_TESTING_OPTIONS
+	wpa_s->extra_roc_dur = 0;
+	wpa_s->test_failure = WPAS_TEST_FAILURE_NONE;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	wpa_s->disconnected = 0;
+	os_free(wpa_s->next_scan_freqs);
+	wpa_s->next_scan_freqs = NULL;
+
+	wpa_bss_flush(wpa_s);
+	if (!dl_list_empty(&wpa_s->bss)) {
+		wpa_printf(MSG_DEBUG,
+			   "BSS table not empty after flush: %u entries, current_bss=%p bssid="
+			   MACSTR " pending_bssid=" MACSTR,
+			   dl_list_len(&wpa_s->bss), wpa_s->current_bss,
+			   MAC2STR(wpa_s->bssid),
+			   MAC2STR(wpa_s->pending_bssid));
+	}
+
+	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+}
+
+
+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 (os_snprintf_error(end - pos, ret))
+			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);
+	work->wpa_s->ext_work_in_progress = 0;
+	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);
+	work->wpa_s->ext_work_in_progress = 1;
+	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 (os_snprintf_error(buflen, ret))
+		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);
+		wpa_s->ext_work_in_progress = 0;
+		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 scan_id_list_parse(struct wpa_supplicant *wpa_s, const char *value,
+			      unsigned int *scan_id_count, int scan_id[])
+{
+	const char *pos = value;
+
+	while (pos) {
+		if (*pos == ' ' || *pos == '\0')
+			break;
+		if (*scan_id_count == MAX_SCAN_ID)
+			return -1;
+		scan_id[(*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;
+	unsigned int manual_scan_passive = 0;
+	unsigned int manual_scan_use_id = 0;
+	unsigned int manual_scan_only_new = 0;
+	unsigned int scan_only = 0;
+	unsigned int scan_id_count = 0;
+	int scan_id[MAX_SCAN_ID];
+	void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
+				 struct wpa_scan_results *scan_res);
+	int *manual_scan_freqs = NULL;
+	struct wpa_ssid_value *ssid = NULL, *ns;
+	unsigned int ssid_count = 0;
+
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+		*reply_len = -1;
+		return;
+	}
+
+	if (radio_work_pending(wpa_s, "scan")) {
+		wpa_printf(MSG_DEBUG,
+			   "Pending scan scheduled - reject new request");
+		*reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
+		return;
+	}
+
+#ifdef CONFIG_INTERWORKING
+	if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) {
+		wpa_printf(MSG_DEBUG,
+			   "Interworking select in progress - reject new scan");
+		*reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
+		return;
+	}
+#endif /* CONFIG_INTERWORKING */
+
+	if (params) {
+		if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0)
+			scan_only = 1;
+
+		pos = os_strstr(params, "freq=");
+		if (pos) {
+			manual_scan_freqs = freq_range_to_channel_list(wpa_s,
+								       pos + 5);
+			if (manual_scan_freqs == NULL) {
+				*reply_len = -1;
+				goto done;
+			}
+		}
+
+		pos = os_strstr(params, "passive=");
+		if (pos)
+			manual_scan_passive = !!atoi(pos + 8);
+
+		pos = os_strstr(params, "use_id=");
+		if (pos)
+			manual_scan_use_id = atoi(pos + 7);
+
+		pos = os_strstr(params, "only_new=1");
+		if (pos)
+			manual_scan_only_new = 1;
+
+		pos = os_strstr(params, "scan_id=");
+		if (pos && scan_id_list_parse(wpa_s, pos + 8, &scan_id_count,
+					      scan_id) < 0) {
+			*reply_len = -1;
+			goto done;
+		}
+
+		pos = params;
+		while (pos && *pos != '\0') {
+			if (os_strncmp(pos, "ssid ", 5) == 0) {
+				char *end;
+
+				pos += 5;
+				end = pos;
+				while (*end) {
+					if (*end == '\0' || *end == ' ')
+						break;
+					end++;
+				}
+
+				ns = os_realloc_array(
+					ssid, ssid_count + 1,
+					sizeof(struct wpa_ssid_value));
+				if (ns == NULL) {
+					*reply_len = -1;
+					goto done;
+				}
+				ssid = ns;
+
+				if ((end - pos) & 0x01 ||
+				    end - pos > 2 * SSID_MAX_LEN ||
+				    hexstr2bin(pos, ssid[ssid_count].ssid,
+					       (end - pos) / 2) < 0) {
+					wpa_printf(MSG_DEBUG,
+						   "Invalid SSID value '%s'",
+						   pos);
+					*reply_len = -1;
+					goto done;
+				}
+				ssid[ssid_count].ssid_len = (end - pos) / 2;
+				wpa_hexdump_ascii(MSG_DEBUG, "scan SSID",
+						  ssid[ssid_count].ssid,
+						  ssid[ssid_count].ssid_len);
+				ssid_count++;
+				pos = end;
+			}
+
+			pos = os_strchr(pos, ' ');
+			if (pos)
+				pos++;
+		}
+	}
+
+	wpa_s->num_ssids_from_scan_req = ssid_count;
+	os_free(wpa_s->ssids_from_scan_req);
+	if (ssid_count) {
+		wpa_s->ssids_from_scan_req = ssid;
+		ssid = NULL;
+	} else {
+		wpa_s->ssids_from_scan_req = NULL;
+	}
+
+	if (scan_only)
+		scan_res_handler = scan_only_handler;
+	else if (wpa_s->scan_res_handler == scan_only_handler)
+		scan_res_handler = NULL;
+	else
+		scan_res_handler = wpa_s->scan_res_handler;
+
+	if (!wpa_s->sched_scanning && !wpa_s->scanning &&
+	    ((wpa_s->wpa_state <= WPA_SCANNING) ||
+	     (wpa_s->wpa_state == WPA_COMPLETED))) {
+		wpa_s->manual_scan_passive = manual_scan_passive;
+		wpa_s->manual_scan_use_id = manual_scan_use_id;
+		wpa_s->manual_scan_only_new = manual_scan_only_new;
+		wpa_s->scan_id_count = scan_id_count;
+		os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int));
+		wpa_s->scan_res_handler = scan_res_handler;
+		os_free(wpa_s->manual_scan_freqs);
+		wpa_s->manual_scan_freqs = manual_scan_freqs;
+		manual_scan_freqs = NULL;
+
+		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_s->manual_scan_passive = manual_scan_passive;
+		wpa_s->manual_scan_use_id = manual_scan_use_id;
+		wpa_s->manual_scan_only_new = manual_scan_only_new;
+		wpa_s->scan_id_count = scan_id_count;
+		os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int));
+		wpa_s->scan_res_handler = scan_res_handler;
+		os_free(wpa_s->manual_scan_freqs);
+		wpa_s->manual_scan_freqs = manual_scan_freqs;
+		manual_scan_freqs = NULL;
+
+		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");
+	}
+
+done:
+	os_free(manual_scan_freqs);
+	os_free(ssid);
+}
+
+
+#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;
+}
+
+
+static int wpas_ctrl_iface_eapol_rx(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos;
+	u8 src[ETH_ALEN], *buf;
+	int used;
+	size_t len;
+
+	wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd);
+
+	pos = cmd;
+	used = hwaddr_aton2(pos, src);
+	if (used < 0)
+		return -1;
+	pos += used;
+	while (*pos == ' ')
+		pos++;
+
+	len = os_strlen(pos);
+	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;
+	}
+
+	wpa_supplicant_rx_eapol(wpa_s, src, buf, len);
+	os_free(buf);
+
+	return 0;
+}
+
+
+static u16 ipv4_hdr_checksum(const void *buf, size_t len)
+{
+	size_t i;
+	u32 sum = 0;
+	const u16 *pos = buf;
+
+	for (i = 0; i < len / 2; i++)
+		sum += *pos++;
+
+	while (sum >> 16)
+		sum = (sum & 0xffff) + (sum >> 16);
+
+	return sum ^ 0xffff;
+}
+
+
+#define HWSIM_PACKETLEN 1500
+#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
+
+void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	const struct ether_header *eth;
+	struct iphdr ip;
+	const u8 *pos;
+	unsigned int i;
+
+	if (len != HWSIM_PACKETLEN)
+		return;
+
+	eth = (const struct ether_header *) buf;
+	os_memcpy(&ip, eth + 1, sizeof(ip));
+	pos = &buf[sizeof(*eth) + sizeof(ip)];
+
+	if (ip.ihl != 5 || ip.version != 4 ||
+	    ntohs(ip.tot_len) != HWSIM_IP_LEN)
+		return;
+
+	for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) {
+		if (*pos != (u8) i)
+			return;
+		pos++;
+	}
+
+	wpa_msg(wpa_s, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR,
+		MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost));
+}
+
+
+static int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s,
+					    char *cmd)
+{
+	int enabled = atoi(cmd);
+
+	if (!enabled) {
+		if (wpa_s->l2_test) {
+			l2_packet_deinit(wpa_s->l2_test);
+			wpa_s->l2_test = NULL;
+			wpa_dbg(wpa_s, MSG_DEBUG, "test data: Disabled");
+		}
+		return 0;
+	}
+
+	if (wpa_s->l2_test)
+		return 0;
+
+	wpa_s->l2_test = l2_packet_init(wpa_s->ifname, wpa_s->own_addr,
+					ETHERTYPE_IP, wpas_data_test_rx,
+					wpa_s, 1);
+	if (wpa_s->l2_test == NULL)
+		return -1;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "test data: Enabled");
+
+	return 0;
+}
+
+
+static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 dst[ETH_ALEN], src[ETH_ALEN];
+	char *pos;
+	int used;
+	long int val;
+	u8 tos;
+	u8 buf[2 + HWSIM_PACKETLEN];
+	struct ether_header *eth;
+	struct iphdr *ip;
+	u8 *dpos;
+	unsigned int i;
+
+	if (wpa_s->l2_test == NULL)
+		return -1;
+
+	/* format: <dst> <src> <tos> */
+
+	pos = cmd;
+	used = hwaddr_aton2(pos, dst);
+	if (used < 0)
+		return -1;
+	pos += used;
+	while (*pos == ' ')
+		pos++;
+	used = hwaddr_aton2(pos, src);
+	if (used < 0)
+		return -1;
+	pos += used;
+
+	val = strtol(pos, NULL, 0);
+	if (val < 0 || val > 0xff)
+		return -1;
+	tos = val;
+
+	eth = (struct ether_header *) &buf[2];
+	os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
+	os_memcpy(eth->ether_shost, src, ETH_ALEN);
+	eth->ether_type = htons(ETHERTYPE_IP);
+	ip = (struct iphdr *) (eth + 1);
+	os_memset(ip, 0, sizeof(*ip));
+	ip->ihl = 5;
+	ip->version = 4;
+	ip->ttl = 64;
+	ip->tos = tos;
+	ip->tot_len = htons(HWSIM_IP_LEN);
+	ip->protocol = 1;
+	ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
+	ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2);
+	ip->check = ipv4_hdr_checksum(ip, sizeof(*ip));
+	dpos = (u8 *) (ip + 1);
+	for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++)
+		*dpos++ = i;
+
+	if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, &buf[2],
+			   HWSIM_PACKETLEN) < 0)
+		return -1;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX dst=" MACSTR " src=" MACSTR
+		" tos=0x%x", MAC2STR(dst), MAC2STR(src), tos);
+
+	return 0;
+}
+
+
+static int wpas_ctrl_iface_data_test_frame(struct wpa_supplicant *wpa_s,
+					   char *cmd)
+{
+	u8 *buf;
+	struct ether_header *eth;
+	struct l2_packet_data *l2 = NULL;
+	size_t len;
+	u16 ethertype;
+	int res = -1;
+
+	len = os_strlen(cmd);
+	if (len & 1 || len < ETH_HLEN * 2)
+		return -1;
+	len /= 2;
+
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+
+	if (hexstr2bin(cmd, buf, len) < 0)
+		goto done;
+
+	eth = (struct ether_header *) buf;
+	ethertype = ntohs(eth->ether_type);
+
+	l2 = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, ethertype,
+			    wpas_data_test_rx, wpa_s, 1);
+	if (l2 == NULL)
+		goto done;
+
+	res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len);
+	wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX frame res=%d", res);
+done:
+	if (l2)
+		l2_packet_deinit(l2);
+	os_free(buf);
+
+	return res < 0 ? -1 : 0;
+}
+
+
+static int wpas_ctrl_test_alloc_fail(struct wpa_supplicant *wpa_s, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_fail_func[256];
+	extern unsigned int wpa_trace_fail_after;
+	char *pos;
+
+	wpa_trace_fail_after = atoi(cmd);
+	pos = os_strchr(cmd, ':');
+	if (pos) {
+		pos++;
+		os_strlcpy(wpa_trace_fail_func, pos,
+			   sizeof(wpa_trace_fail_func));
+	} else {
+		wpa_trace_fail_after = 0;
+	}
+	return 0;
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s,
+				    char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_fail_func[256];
+	extern unsigned int wpa_trace_fail_after;
+
+	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
+			   wpa_trace_fail_func);
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_test_fail_func[256];
+	extern unsigned int wpa_trace_test_fail_after;
+	char *pos;
+
+	wpa_trace_test_fail_after = atoi(cmd);
+	pos = os_strchr(cmd, ':');
+	if (pos) {
+		pos++;
+		os_strlcpy(wpa_trace_test_fail_func, pos,
+			   sizeof(wpa_trace_test_fail_func));
+	} else {
+		wpa_trace_test_fail_after = 0;
+	}
+	return 0;
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s,
+				    char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_test_fail_func[256];
+	extern unsigned int wpa_trace_test_fail_after;
+
+	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
+			   wpa_trace_test_fail_func);
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+#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]) {
+			int res;
+
+			res = os_snprintf(buf, sizeof(buf), "frame[%u]", i);
+			if (!os_snprintf_error(sizeof(buf), res)) {
+				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,
+				   end - (ie + 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;
+}
+
+
+static void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	if (neighbor_rep) {
+		wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED
+			     "length=%u",
+			     (unsigned int) wpabuf_len(neighbor_rep));
+		wpabuf_free(neighbor_rep);
+	} else {
+		wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_FAILED);
+	}
+}
+
+
+static int wpas_ctrl_iface_send_neigbor_rep(struct wpa_supplicant *wpa_s,
+					    char *cmd)
+{
+	struct wpa_ssid ssid;
+	struct wpa_ssid *ssid_p = NULL;
+	int ret = 0;
+
+	if (os_strncmp(cmd, " ssid=", 6) == 0) {
+		ssid.ssid_len = os_strlen(cmd + 6);
+		if (ssid.ssid_len > SSID_MAX_LEN)
+			return -1;
+		ssid.ssid = (u8 *) (cmd + 6);
+		ssid_p = &ssid;
+	}
+
+	ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p,
+						 wpas_ctrl_neighbor_rep_cb,
+						 wpa_s);
+
+	return ret;
+}
+
+
+static int wpas_ctrl_iface_erp_flush(struct wpa_supplicant *wpa_s)
+{
+	eapol_sm_erp_flush(wpa_s->eapol);
+	return 0;
+}
+
+
+static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s,
+					 char *cmd)
+{
+	char *token, *context = NULL;
+	unsigned int enable = ~0, type = 0;
+	u8 _addr[ETH_ALEN], _mask[ETH_ALEN];
+	u8 *addr = NULL, *mask = NULL;
+
+	while ((token = str_token(cmd, " ", &context))) {
+		if (os_strcasecmp(token, "scan") == 0) {
+			type |= MAC_ADDR_RAND_SCAN;
+		} else if (os_strcasecmp(token, "sched") == 0) {
+			type |= MAC_ADDR_RAND_SCHED_SCAN;
+		} else if (os_strcasecmp(token, "pno") == 0) {
+			type |= MAC_ADDR_RAND_PNO;
+		} else if (os_strcasecmp(token, "all") == 0) {
+			type = wpa_s->mac_addr_rand_supported;
+		} else if (os_strncasecmp(token, "enable=", 7) == 0) {
+			enable = atoi(token + 7);
+		} else if (os_strncasecmp(token, "addr=", 5) == 0) {
+			addr = _addr;
+			if (hwaddr_aton(token + 5, addr)) {
+				wpa_printf(MSG_INFO,
+					   "CTRL: Invalid MAC address: %s",
+					   token);
+				return -1;
+			}
+		} else if (os_strncasecmp(token, "mask=", 5) == 0) {
+			mask = _mask;
+			if (hwaddr_aton(token + 5, mask)) {
+				wpa_printf(MSG_INFO,
+					   "CTRL: Invalid MAC address mask: %s",
+					   token);
+				return -1;
+			}
+		} else {
+			wpa_printf(MSG_INFO,
+				   "CTRL: Invalid MAC_RAND_SCAN parameter: %s",
+				   token);
+			return -1;
+		}
+	}
+
+	if (!type) {
+		wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN no type specified");
+		return -1;
+	}
+
+	if ((wpa_s->mac_addr_rand_supported & type) != type) {
+		wpa_printf(MSG_INFO,
+			   "CTRL: MAC_RAND_SCAN types=%u != supported=%u",
+			   type, wpa_s->mac_addr_rand_supported);
+		return -1;
+	}
+
+	if (enable > 1) {
+		wpa_printf(MSG_INFO,
+			   "CTRL: MAC_RAND_SCAN enable=<0/1> not specified");
+		return -1;
+	}
+
+	if (!enable) {
+		wpas_mac_addr_rand_scan_clear(wpa_s, type);
+		if (wpa_s->pno) {
+			if (type & MAC_ADDR_RAND_PNO) {
+				wpas_stop_pno(wpa_s);
+				wpas_start_pno(wpa_s);
+			}
+		} else if (wpa_s->sched_scanning &&
+			   (type & MAC_ADDR_RAND_SCHED_SCAN)) {
+			/* simulate timeout to restart the sched scan */
+			wpa_s->sched_scan_timed_out = 1;
+			wpa_s->prev_sched_ssid = NULL;
+			wpa_supplicant_cancel_sched_scan(wpa_s);
+		}
+		return 0;
+	}
+
+	if ((addr && !mask) || (!addr && mask)) {
+		wpa_printf(MSG_INFO,
+			   "CTRL: MAC_RAND_SCAN invalid addr/mask combination");
+		return -1;
+	}
+
+	if (addr && mask && (!(mask[0] & 0x01) || (addr[0] & 0x01))) {
+		wpa_printf(MSG_INFO,
+			   "CTRL: MAC_RAND_SCAN cannot allow multicast address");
+		return -1;
+	}
+
+	if (type & MAC_ADDR_RAND_SCAN) {
+		wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN,
+					    addr, mask);
+	}
+
+	if (type & MAC_ADDR_RAND_SCHED_SCAN) {
+		wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN,
+					    addr, mask);
+
+		if (wpa_s->sched_scanning && !wpa_s->pno) {
+			/* simulate timeout to restart the sched scan */
+			wpa_s->sched_scan_timed_out = 1;
+			wpa_s->prev_sched_ssid = NULL;
+			wpa_supplicant_cancel_sched_scan(wpa_s);
+		}
+	}
+
+	if (type & MAC_ADDR_RAND_PNO) {
+		wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO,
+					    addr, mask);
+		if (wpa_s->pno) {
+			wpas_stop_pno(wpa_s);
+			wpas_start_pno(wpa_s);
+		}
+	}
+
+	return 0;
+}
+
+
+static int wpas_ctrl_cmd_debug_level(const char *cmd)
+{
+	if (os_strcmp(cmd, "PING") == 0 ||
+	    os_strncmp(cmd, "BSS ", 4) == 0 ||
+	    os_strncmp(cmd, "GET_NETWORK ", 12) == 0 ||
+	    os_strncmp(cmd, "STATUS", 6) == 0 ||
+	    os_strncmp(cmd, "STA ", 4) == 0 ||
+	    os_strncmp(cmd, "STA-", 4) == 0)
+		return MSG_EXCESSIVE;
+	return MSG_DEBUG;
+}
+
+
+char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+					 char *buf, size_t *resp_len)
+{
+	char *reply;
+	const int reply_size = 4096;
+	int reply_len;
+
+	if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 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 = wpas_ctrl_cmd_debug_level(buf);
+		wpa_dbg(wpa_s, level, "Control interface command '%s'", buf);
+	}
+
+	reply = os_malloc(reply_size);
+	if (reply == NULL) {
+		*resp_len = 1;
+		return NULL;
+	}
+
+	os_memcpy(reply, "OK\n", 3);
+	reply_len = 3;
+
+	if (os_strcmp(buf, "PING") == 0) {
+		os_memcpy(reply, "PONG\n", 5);
+		reply_len = 5;
+	} else if (os_strcmp(buf, "IFNAME") == 0) {
+		reply_len = os_strlen(wpa_s->ifname);
+		os_memcpy(reply, wpa_s->ifname, reply_len);
+	} else if (os_strncmp(buf, "RELOG", 5) == 0) {
+		if (wpa_debug_reopen_file() < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "NOTE ", 5) == 0) {
+		wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
+	} else if (os_strcmp(buf, "MIB") == 0) {
+		reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
+		if (reply_len >= 0) {
+			reply_len += eapol_sm_get_mib(wpa_s->eapol,
+						      reply + reply_len,
+						      reply_size - reply_len);
+		}
+	} else if (os_strncmp(buf, "STATUS", 6) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_status(
+			wpa_s, buf + 6, reply, reply_size);
+	} else if (os_strcmp(buf, "PMKSA") == 0) {
+		reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, reply,
+						    reply_size);
+	} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
+		wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
+	} else if (os_strncmp(buf, "SET ", 4) == 0) {
+		if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DUMP", 4) == 0) {
+		reply_len = wpa_config_dump_values(wpa_s->conf,
+						   reply, reply_size);
+	} else if (os_strncmp(buf, "GET ", 4) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_get(wpa_s, buf + 4,
+							  reply, reply_size);
+	} else if (os_strcmp(buf, "LOGON") == 0) {
+		eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
+	} else if (os_strcmp(buf, "LOGOFF") == 0) {
+		eapol_sm_notify_logoff(wpa_s->eapol, TRUE);
+	} else if (os_strcmp(buf, "REASSOCIATE") == 0) {
+		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+			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;
+		else if (wpa_s->disconnected)
+			wpas_request_connection(wpa_s);
+#ifdef IEEE8021X_EAPOL
+	} else if (os_strncmp(buf, "PREAUTH ", 8) == 0) {
+		if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8))
+			reply_len = -1;
+#endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_PEERKEY
+	} else if (os_strncmp(buf, "STKSTART ", 9) == 0) {
+		if (wpa_supplicant_ctrl_iface_stkstart(wpa_s, buf + 9))
+			reply_len = -1;
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_IEEE80211R
+	} else if (os_strncmp(buf, "FT_DS ", 6) == 0) {
+		if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6))
+			reply_len = -1;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_WPS
+	} else if (os_strcmp(buf, "WPS_PBC") == 0) {
+		int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL);
+		if (res == -2) {
+			os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
+			reply_len = 17;
+		} else if (res)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) {
+		int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8);
+		if (res == -2) {
+			os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
+			reply_len = 17;
+		} else if (res)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8,
+							      reply,
+							      reply_size);
+	} else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_wps_check_pin(
+			wpa_s, buf + 14, reply, reply_size);
+	} else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
+		if (wpas_wps_cancel(wpa_s))
+			reply_len = -1;
+#ifdef CONFIG_WPS_NFC
+	} else if (os_strcmp(buf, "WPS_NFC") == 0) {
+		if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, NULL))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_NFC ", 8) == 0) {
+		if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, buf + 8))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_wps_nfc_config_token(
+			wpa_s, buf + 21, reply, reply_size);
+	} else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_wps_nfc_token(
+			wpa_s, buf + 14, reply, reply_size);
+	} else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
+		if (wpa_supplicant_ctrl_iface_wps_nfc_tag_read(wpa_s,
+							       buf + 17))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "NFC_GET_HANDOVER_REQ ", 21) == 0) {
+		reply_len = wpas_ctrl_nfc_get_handover_req(
+			wpa_s, buf + 21, reply, reply_size);
+	} 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_REPORT_HANDOVER ", 20) == 0) {
+		if (wpas_ctrl_nfc_report_handover(wpa_s, buf + 20))
+			reply_len = -1;
+#endif /* CONFIG_WPS_NFC */
+	} else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
+		if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
+			reply_len = -1;
+#ifdef CONFIG_AP
+	} else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_wps_ap_pin(
+			wpa_s, buf + 11, reply, reply_size);
+#endif /* CONFIG_AP */
+#ifdef CONFIG_WPS_ER
+	} else if (os_strcmp(buf, "WPS_ER_START") == 0) {
+		if (wpas_wps_er_start(wpa_s, NULL))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_ER_START ", 13) == 0) {
+		if (wpas_wps_er_start(wpa_s, buf + 13))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "WPS_ER_STOP") == 0) {
+		wpas_wps_er_stop(wpa_s);
+	} else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) {
+		if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_ER_PBC ", 11) == 0) {
+		int ret = wpas_wps_er_pbc(wpa_s, buf + 11);
+		if (ret == -2) {
+			os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
+			reply_len = 17;
+		} else if (ret == -3) {
+			os_memcpy(reply, "FAIL-UNKNOWN-UUID\n", 18);
+			reply_len = 18;
+		} else if (ret == -4) {
+			os_memcpy(reply, "FAIL-NO-AP-SETTINGS\n", 20);
+			reply_len = 20;
+		} else if (ret)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_ER_LEARN ", 13) == 0) {
+		if (wpa_supplicant_ctrl_iface_wps_er_learn(wpa_s, buf + 13))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_ER_SET_CONFIG ", 18) == 0) {
+		if (wpa_supplicant_ctrl_iface_wps_er_set_config(wpa_s,
+								buf + 18))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_ER_CONFIG ", 14) == 0) {
+		if (wpa_supplicant_ctrl_iface_wps_er_config(wpa_s, buf + 14))
+			reply_len = -1;
+#ifdef CONFIG_WPS_NFC
+	} else if (os_strncmp(buf, "WPS_ER_NFC_CONFIG_TOKEN ", 24) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_wps_er_nfc_config_token(
+			wpa_s, buf + 24, reply, reply_size);
+#endif /* CONFIG_WPS_NFC */
+#endif /* CONFIG_WPS_ER */
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_IBSS_RSN
+	} else if (os_strncmp(buf, "IBSS_RSN ", 9) == 0) {
+		if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9))
+			reply_len = -1;
+#endif /* CONFIG_IBSS_RSN */
+#ifdef CONFIG_MESH
+	} else if (os_strncmp(buf, "MESH_INTERFACE_ADD ", 19) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add(
+			wpa_s, buf + 19, reply, reply_size);
+	} else if (os_strcmp(buf, "MESH_INTERFACE_ADD") == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add(
+			wpa_s, "", reply, reply_size);
+	} else if (os_strncmp(buf, "MESH_GROUP_ADD ", 15) == 0) {
+		if (wpa_supplicant_ctrl_iface_mesh_group_add(wpa_s, buf + 15))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "MESH_GROUP_REMOVE ", 18) == 0) {
+		if (wpa_supplicant_ctrl_iface_mesh_group_remove(wpa_s,
+								buf + 18))
+			reply_len = -1;
+#endif /* CONFIG_MESH */
+#ifdef CONFIG_P2P
+	} else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) {
+		if (p2p_ctrl_find(wpa_s, buf + 8))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "P2P_FIND") == 0) {
+		if (p2p_ctrl_find(wpa_s, ""))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "P2P_STOP_FIND") == 0) {
+		wpas_p2p_stop_find(wpa_s);
+	} else if (os_strncmp(buf, "P2P_ASP_PROVISION ", 18) == 0) {
+		if (p2p_ctrl_asp_provision(wpa_s, buf + 18))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_ASP_PROVISION_RESP ", 23) == 0) {
+		if (p2p_ctrl_asp_provision_resp(wpa_s, buf + 23))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) {
+		reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply,
+					     reply_size);
+	} else if (os_strncmp(buf, "P2P_LISTEN ", 11) == 0) {
+		if (p2p_ctrl_listen(wpa_s, buf + 11))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "P2P_LISTEN") == 0) {
+		if (p2p_ctrl_listen(wpa_s, ""))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_GROUP_REMOVE ", 17) == 0) {
+		if (wpas_p2p_group_remove(wpa_s, buf + 17))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) {
+		if (p2p_ctrl_group_add(wpa_s, ""))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) {
+		if (p2p_ctrl_group_add(wpa_s, buf + 14))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_PROV_DISC ", 14) == 0) {
+		if (p2p_ctrl_prov_disc(wpa_s, buf + 14))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "P2P_GET_PASSPHRASE") == 0) {
+		reply_len = p2p_get_passphrase(wpa_s, reply, reply_size);
+	} else if (os_strncmp(buf, "P2P_SERV_DISC_REQ ", 18) == 0) {
+		reply_len = p2p_ctrl_serv_disc_req(wpa_s, buf + 18, reply,
+						   reply_size);
+	} else if (os_strncmp(buf, "P2P_SERV_DISC_CANCEL_REQ ", 25) == 0) {
+		if (p2p_ctrl_serv_disc_cancel_req(wpa_s, buf + 25) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_SERV_DISC_RESP ", 19) == 0) {
+		if (p2p_ctrl_serv_disc_resp(wpa_s, buf + 19) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "P2P_SERVICE_UPDATE") == 0) {
+		wpas_p2p_sd_service_update(wpa_s);
+	} else if (os_strncmp(buf, "P2P_SERV_DISC_EXTERNAL ", 23) == 0) {
+		if (p2p_ctrl_serv_disc_external(wpa_s, buf + 23) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "P2P_SERVICE_FLUSH") == 0) {
+		wpas_p2p_service_flush(wpa_s);
+	} else if (os_strncmp(buf, "P2P_SERVICE_ADD ", 16) == 0) {
+		if (p2p_ctrl_service_add(wpa_s, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_SERVICE_DEL ", 16) == 0) {
+		if (p2p_ctrl_service_del(wpa_s, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_SERVICE_REP ", 16) == 0) {
+		if (p2p_ctrl_service_replace(wpa_s, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_REJECT ", 11) == 0) {
+		if (p2p_ctrl_reject(wpa_s, buf + 11) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_INVITE ", 11) == 0) {
+		if (p2p_ctrl_invite(wpa_s, buf + 11) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_PEER ", 9) == 0) {
+		reply_len = p2p_ctrl_peer(wpa_s, buf + 9, reply,
+					      reply_size);
+	} else if (os_strncmp(buf, "P2P_SET ", 8) == 0) {
+		if (p2p_ctrl_set(wpa_s, buf + 8) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "P2P_FLUSH") == 0) {
+		p2p_ctrl_flush(wpa_s);
+	} else if (os_strncmp(buf, "P2P_UNAUTHORIZE ", 16) == 0) {
+		if (wpas_p2p_unauthorize(wpa_s, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "P2P_CANCEL") == 0) {
+		if (wpas_p2p_cancel(wpa_s))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_PRESENCE_REQ ", 17) == 0) {
+		if (p2p_ctrl_presence_req(wpa_s, buf + 17) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "P2P_PRESENCE_REQ") == 0) {
+		if (p2p_ctrl_presence_req(wpa_s, "") < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_EXT_LISTEN ", 15) == 0) {
+		if (p2p_ctrl_ext_listen(wpa_s, buf + 15) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) {
+		if (p2p_ctrl_ext_listen(wpa_s, "") < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) {
+		if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0)
+			reply_len = -1;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_WIFI_DISPLAY
+	} else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) {
+		if (wifi_display_subelem_set(wpa_s->global, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) {
+		reply_len = wifi_display_subelem_get(wpa_s->global, buf + 16,
+						     reply, reply_size);
+#endif /* CONFIG_WIFI_DISPLAY */
+#ifdef CONFIG_INTERWORKING
+	} else if (os_strcmp(buf, "FETCH_ANQP") == 0) {
+		if (interworking_fetch_anqp(wpa_s) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) {
+		interworking_stop_fetch_anqp(wpa_s);
+	} 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) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "INTERWORKING_ADD_NETWORK ", 25) == 0) {
+		int id;
+
+		id = ctrl_interworking_connect(wpa_s, buf + 25, 1);
+		if (id < 0)
+			reply_len = -1;
+		else {
+			reply_len = os_snprintf(reply, reply_size, "%d\n", id);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) {
+		if (get_anqp(wpa_s, buf + 9) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "GAS_REQUEST ", 12) == 0) {
+		if (gas_request(wpa_s, buf + 12) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "GAS_RESPONSE_GET ", 17) == 0) {
+		reply_len = gas_response_get(wpa_s, buf + 17, reply,
+					     reply_size);
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+	} else if (os_strncmp(buf, "HS20_ANQP_GET ", 14) == 0) {
+		if (get_hs20_anqp(wpa_s, buf + 14) < 0)
+			reply_len = -1;
+	} 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 {
+			/*
+			 * 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;
+	} else if (os_strcmp(buf, "TERMINATE") == 0) {
+		wpa_supplicant_terminate_proc(wpa_s->global);
+	} else if (os_strncmp(buf, "BSSID ", 6) == 0) {
+		if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "BLACKLIST", 9) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_blacklist(
+			wpa_s, buf + 9, reply, reply_size);
+	} else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_log_level(
+			wpa_s, buf + 9, reply, reply_size);
+	} else if (os_strncmp(buf, "LIST_NETWORKS ", 14) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_list_networks(
+			wpa_s, buf + 14, reply, reply_size);
+	} else if (os_strcmp(buf, "LIST_NETWORKS") == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_list_networks(
+			wpa_s, NULL, reply, reply_size);
+	} else if (os_strcmp(buf, "DISCONNECT") == 0) {
+#ifdef CONFIG_SME
+		wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+		wpa_s->reassociate = 0;
+		wpa_s->disconnected = 1;
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_cancel_scan(wpa_s);
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+		eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+	} 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);
+	} else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) {
+		if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "ENABLE_NETWORK ", 15) == 0) {
+		if (wpa_supplicant_ctrl_iface_enable_network(wpa_s, buf + 15))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DISABLE_NETWORK ", 16) == 0) {
+		if (wpa_supplicant_ctrl_iface_disable_network(wpa_s, buf + 16))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "ADD_NETWORK") == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_add_network(
+			wpa_s, reply, reply_size);
+	} else if (os_strncmp(buf, "REMOVE_NETWORK ", 15) == 0) {
+		if (wpa_supplicant_ctrl_iface_remove_network(wpa_s, buf + 15))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+		if (wpa_supplicant_ctrl_iface_set_network(wpa_s, buf + 12))
+			reply_len = -1;
+	} 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,
+							  wpa_s))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "LIST_CREDS") == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_list_creds(
+			wpa_s, reply, reply_size);
+	} else if (os_strcmp(buf, "ADD_CRED") == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_add_cred(
+			wpa_s, reply, reply_size);
+	} else if (os_strncmp(buf, "REMOVE_CRED ", 12) == 0) {
+		if (wpa_supplicant_ctrl_iface_remove_cred(wpa_s, buf + 12))
+			reply_len = -1;
+	} 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))
+			reply_len = -1;
+#endif /* CONFIG_NO_CONFIG_WRITE */
+	} else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_get_capability(
+			wpa_s, buf + 15, reply, reply_size);
+	} else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) {
+		if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "SCAN_INTERVAL ", 14) == 0) {
+		if (wpa_supplicant_ctrl_iface_scan_interval(wpa_s, buf + 14))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "INTERFACE_LIST") == 0) {
+		reply_len = wpa_supplicant_global_iface_list(
+			wpa_s->global, reply, reply_size);
+	} else if (os_strcmp(buf, "INTERFACES") == 0) {
+		reply_len = wpa_supplicant_global_iface_interfaces(
+			wpa_s->global, reply, reply_size);
+	} else if (os_strncmp(buf, "BSS ", 4) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_bss(
+			wpa_s, buf + 4, reply, reply_size);
+#ifdef CONFIG_AP
+	} else if (os_strcmp(buf, "STA-FIRST") == 0) {
+		reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size);
+	} else if (os_strncmp(buf, "STA ", 4) == 0) {
+		reply_len = ap_ctrl_iface_sta(wpa_s, buf + 4, reply,
+					      reply_size);
+	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
+		reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
+						   reply_size);
+	} else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
+		if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
+			reply_len = -1;
+	} 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;
+	} else if (os_strcmp(buf, "STOP_AP") == 0) {
+		if (wpas_ap_stop_ap(wpa_s))
+			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;
+	} else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) {
+		wpa_s->auto_reconnect_disabled = atoi(buf + 16) == 0;
+	} else if (os_strncmp(buf, "BSS_EXPIRE_AGE ", 15) == 0) {
+		if (wpa_supplicant_ctrl_iface_bss_expire_age(wpa_s, buf + 15))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "BSS_EXPIRE_COUNT ", 17) == 0) {
+		if (wpa_supplicant_ctrl_iface_bss_expire_count(wpa_s,
+							       buf + 17))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) {
+		wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10);
+#ifdef CONFIG_TDLS
+	} else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) {
+		if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "TDLS_SETUP ", 11) == 0) {
+		if (wpa_supplicant_ctrl_iface_tdls_setup(wpa_s, buf + 11))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) {
+		if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "TDLS_CHAN_SWITCH ", 17) == 0) {
+		if (wpa_supplicant_ctrl_iface_tdls_chan_switch(wpa_s,
+							       buf + 17))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "TDLS_CANCEL_CHAN_SWITCH ", 24) == 0) {
+		if (wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(wpa_s,
+								      buf + 24))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "TDLS_LINK_STATUS ", 17) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_tdls_link_status(
+			wpa_s, buf + 17, reply, reply_size);
+#endif /* CONFIG_TDLS */
+	} else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) {
+		reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size);
+	} else if (os_strncmp(buf, "WMM_AC_ADDTS ", 13) == 0) {
+		if (wmm_ac_ctrl_addts(wpa_s, buf + 13))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WMM_AC_DELTS ", 13) == 0) {
+		if (wmm_ac_ctrl_delts(wpa_s, buf + 13))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) {
+		reply_len = wpa_supplicant_signal_poll(wpa_s, reply,
+						       reply_size);
+	} else if (os_strncmp(buf, "PKTCNT_POLL", 11) == 0) {
+		reply_len = wpa_supplicant_pktcnt_poll(wpa_s, reply,
+						       reply_size);
+#ifdef CONFIG_AUTOSCAN
+	} else if (os_strncmp(buf, "AUTOSCAN ", 9) == 0) {
+		if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9))
+			reply_len = -1;
+#endif /* CONFIG_AUTOSCAN */
+#ifdef ANDROID
+	} else if (os_strncmp(buf, "DRIVER ", 7) == 0) {
+		reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply,
+						      reply_size);
+#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);
+#ifdef CONFIG_WNM
+	} else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) {
+		if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WNM_BSS_QUERY ", 14) == 0) {
+		if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 14))
+				reply_len = -1;
+#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;
+	} else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
+		if (wpas_ctrl_iface_eapol_rx(wpa_s, buf + 9) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) {
+		if (wpas_ctrl_iface_data_test_config(wpa_s, buf + 17) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) {
+		if (wpas_ctrl_iface_data_test_tx(wpa_s, buf + 13) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
+		if (wpas_ctrl_iface_data_test_frame(wpa_s, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
+		if (wpas_ctrl_test_alloc_fail(wpa_s, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
+		reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size);
+	} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
+		if (wpas_ctrl_test_fail(wpa_s, buf + 10) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "GET_FAIL") == 0) {
+		reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size);
+#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 if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) {
+		if (wpas_ctrl_iface_send_neigbor_rep(wpa_s, buf + 20))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
+		wpas_ctrl_iface_erp_flush(wpa_s);
+	} else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) {
+		if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) {
+		reply_len = wpas_ctrl_iface_get_pref_freq_list(
+			wpa_s, buf + 19, reply, reply_size);
+	} else {
+		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+		reply_len = 16;
+	}
+
+	if (reply_len < 0) {
+		os_memcpy(reply, "FAIL\n", 5);
+		reply_len = 5;
+	}
+
+	*resp_len = reply_len;
+	return reply;
+}
+
+
+static int wpa_supplicant_global_iface_add(struct wpa_global *global,
+					   char *cmd)
+{
+	struct wpa_interface iface;
+	char *pos, *extra;
+	struct wpa_supplicant *wpa_s;
+	unsigned int create_iface = 0;
+	u8 mac_addr[ETH_ALEN];
+
+	/*
+	 * <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param>
+	 * TAB<bridge_ifname>[TAB<create>]
+	 */
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd);
+
+	os_memset(&iface, 0, sizeof(iface));
+
+	do {
+		iface.ifname = pos = cmd;
+		pos = os_strchr(pos, '\t');
+		if (pos)
+			*pos++ = '\0';
+		if (iface.ifname[0] == '\0')
+			return -1;
+		if (pos == NULL)
+			break;
+
+		iface.confname = pos;
+		pos = os_strchr(pos, '\t');
+		if (pos)
+			*pos++ = '\0';
+		if (iface.confname[0] == '\0')
+			iface.confname = NULL;
+		if (pos == NULL)
+			break;
+
+		iface.driver = pos;
+		pos = os_strchr(pos, '\t');
+		if (pos)
+			*pos++ = '\0';
+		if (iface.driver[0] == '\0')
+			iface.driver = NULL;
+		if (pos == NULL)
+			break;
+
+		iface.ctrl_interface = pos;
+		pos = os_strchr(pos, '\t');
+		if (pos)
+			*pos++ = '\0';
+		if (iface.ctrl_interface[0] == '\0')
+			iface.ctrl_interface = NULL;
+		if (pos == NULL)
+			break;
+
+		iface.driver_param = pos;
+		pos = os_strchr(pos, '\t');
+		if (pos)
+			*pos++ = '\0';
+		if (iface.driver_param[0] == '\0')
+			iface.driver_param = NULL;
+		if (pos == NULL)
+			break;
+
+		iface.bridge_ifname = pos;
+		pos = os_strchr(pos, '\t');
+		if (pos)
+			*pos++ = '\0';
+		if (iface.bridge_ifname[0] == '\0')
+			iface.bridge_ifname = NULL;
+		if (pos == NULL)
+			break;
+
+		extra = pos;
+		pos = os_strchr(pos, '\t');
+		if (pos)
+			*pos++ = '\0';
+		if (!extra[0])
+			break;
+
+		if (os_strcmp(extra, "create") == 0)
+			create_iface = 1;
+		else {
+			wpa_printf(MSG_DEBUG,
+				   "INTERFACE_ADD unsupported extra parameter: '%s'",
+				   extra);
+			return -1;
+		}
+	} while (0);
+
+	if (create_iface) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE creating interface '%s'",
+			   iface.ifname);
+		if (!global->ifaces)
+			return -1;
+		if (wpa_drv_if_add(global->ifaces, WPA_IF_STATION, iface.ifname,
+				   NULL, NULL, NULL, mac_addr, NULL) < 0) {
+			wpa_printf(MSG_ERROR,
+				   "CTRL_IFACE interface creation failed");
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE interface '%s' created with MAC addr: "
+			   MACSTR, iface.ifname, MAC2STR(mac_addr));
+	}
+
+	if (wpa_supplicant_get_iface(global, iface.ifname))
+		goto fail;
+
+	wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
+	if (!wpa_s)
+		goto fail;
+	wpa_s->added_vif = create_iface;
+	return 0;
+
+fail:
+	if (create_iface)
+		wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, iface.ifname);
+	return -1;
+}
+
+
+static int wpa_supplicant_global_iface_remove(struct wpa_global *global,
+					      char *cmd)
+{
+	struct wpa_supplicant *wpa_s;
+	int ret;
+	unsigned int delete_iface;
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd);
+
+	wpa_s = wpa_supplicant_get_iface(global, cmd);
+	if (wpa_s == NULL)
+		return -1;
+	delete_iface = wpa_s->added_vif;
+	ret = wpa_supplicant_remove_iface(global, wpa_s, 0);
+	if (!ret && delete_iface) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE deleting the interface '%s'",
+			   cmd);
+		ret = wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, cmd);
+	}
+	return ret;
+}
+
+
+static void wpa_free_iface_info(struct wpa_interface_info *iface)
+{
+	struct wpa_interface_info *prev;
+
+	while (iface) {
+		prev = iface;
+		iface = iface->next;
+
+		os_free(prev->ifname);
+		os_free(prev->desc);
+		os_free(prev);
+	}
+}
+
+
+static int wpa_supplicant_global_iface_list(struct wpa_global *global,
+					    char *buf, int len)
+{
+	int i, res;
+	struct wpa_interface_info *iface = NULL, *last = NULL, *tmp;
+	char *pos, *end;
+
+	for (i = 0; wpa_drivers[i]; i++) {
+		const struct wpa_driver_ops *drv = wpa_drivers[i];
+		if (drv->get_interfaces == NULL)
+			continue;
+		tmp = drv->get_interfaces(global->drv_priv[i]);
+		if (tmp == NULL)
+			continue;
+
+		if (last == NULL)
+			iface = last = tmp;
+		else
+			last->next = tmp;
+		while (last->next)
+			last = last->next;
+	}
+
+	pos = buf;
+	end = buf + len;
+	for (tmp = iface; tmp; tmp = tmp->next) {
+		res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n",
+				  tmp->drv_name, tmp->ifname,
+				  tmp->desc ? tmp->desc : "");
+		if (os_snprintf_error(end - pos, res)) {
+			*pos = '\0';
+			break;
+		}
+		pos += res;
+	}
+
+	wpa_free_iface_info(iface);
+
+	return pos - buf;
+}
+
+
+static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
+						  char *buf, int len)
+{
+	int res;
+	char *pos, *end;
+	struct wpa_supplicant *wpa_s;
+
+	wpa_s = global->ifaces;
+	pos = buf;
+	end = buf + len;
+
+	while (wpa_s) {
+		res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname);
+		if (os_snprintf_error(end - pos, res)) {
+			*pos = '\0';
+			break;
+		}
+		pos += res;
+		wpa_s = wpa_s->next;
+	}
+	return pos - buf;
+}
+
+
+static char * wpas_global_ctrl_iface_ifname(struct wpa_global *global,
+					    const char *ifname,
+					    char *cmd, size_t *resp_len)
+{
+	struct wpa_supplicant *wpa_s;
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (os_strcmp(ifname, wpa_s->ifname) == 0)
+			break;
+	}
+
+	if (wpa_s == NULL) {
+		char *resp = os_strdup("FAIL-NO-IFNAME-MATCH\n");
+		if (resp)
+			*resp_len = os_strlen(resp);
+		else
+			*resp_len = 1;
+		return resp;
+	}
+
+	return wpa_supplicant_ctrl_iface_process(wpa_s, cmd, resp_len);
+}
+
+
+static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
+					       char *buf, size_t *resp_len)
+{
+#ifdef CONFIG_P2P
+	static const char * cmd[] = {
+		"LIST_NETWORKS",
+		"P2P_FIND",
+		"P2P_STOP_FIND",
+		"P2P_LISTEN",
+		"P2P_GROUP_ADD",
+		"P2P_GET_PASSPHRASE",
+		"P2P_SERVICE_UPDATE",
+		"P2P_SERVICE_FLUSH",
+		"P2P_FLUSH",
+		"P2P_CANCEL",
+		"P2P_PRESENCE_REQ",
+		"P2P_EXT_LISTEN",
+		NULL
+	};
+	static const char * prefix[] = {
+#ifdef ANDROID
+		"DRIVER ",
+#endif /* ANDROID */
+		"GET_NETWORK ",
+		"REMOVE_NETWORK ",
+		"P2P_FIND ",
+		"P2P_CONNECT ",
+		"P2P_LISTEN ",
+		"P2P_GROUP_REMOVE ",
+		"P2P_GROUP_ADD ",
+		"P2P_PROV_DISC ",
+		"P2P_SERV_DISC_REQ ",
+		"P2P_SERV_DISC_CANCEL_REQ ",
+		"P2P_SERV_DISC_RESP ",
+		"P2P_SERV_DISC_EXTERNAL ",
+		"P2P_SERVICE_ADD ",
+		"P2P_SERVICE_DEL ",
+		"P2P_SERVICE_REP ",
+		"P2P_REJECT ",
+		"P2P_INVITE ",
+		"P2P_PEER ",
+		"P2P_SET ",
+		"P2P_UNAUTHORIZE ",
+		"P2P_PRESENCE_REQ ",
+		"P2P_EXT_LISTEN ",
+		"P2P_REMOVE_CLIENT ",
+		"WPS_NFC_TOKEN ",
+		"WPS_NFC_TAG_READ ",
+		"NFC_GET_HANDOVER_SEL ",
+		"NFC_GET_HANDOVER_REQ ",
+		"NFC_REPORT_HANDOVER ",
+		"P2P_ASP_PROVISION ",
+		"P2P_ASP_PROVISION_RESP ",
+		NULL
+	};
+	int found = 0;
+	int i;
+
+	if (global->p2p_init_wpa_s == NULL)
+		return NULL;
+
+	for (i = 0; !found && cmd[i]; i++) {
+		if (os_strcmp(buf, cmd[i]) == 0)
+			found = 1;
+	}
+
+	for (i = 0; !found && prefix[i]; i++) {
+		if (os_strncmp(buf, prefix[i], os_strlen(prefix[i])) == 0)
+			found = 1;
+	}
+
+	if (found)
+		return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s,
+							 buf, resp_len);
+#endif /* CONFIG_P2P */
+	return NULL;
+}
+
+
+static char * wpas_global_ctrl_iface_redir_wfd(struct wpa_global *global,
+					       char *buf, size_t *resp_len)
+{
+#ifdef CONFIG_WIFI_DISPLAY
+	if (global->p2p_init_wpa_s == NULL)
+		return NULL;
+	if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0 ||
+	    os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0)
+		return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s,
+							 buf, resp_len);
+#endif /* CONFIG_WIFI_DISPLAY */
+	return NULL;
+}
+
+
+static char * wpas_global_ctrl_iface_redir(struct wpa_global *global,
+					   char *buf, size_t *resp_len)
+{
+	char *ret;
+
+	ret = wpas_global_ctrl_iface_redir_p2p(global, buf, resp_len);
+	if (ret)
+		return ret;
+
+	ret = wpas_global_ctrl_iface_redir_wfd(global, buf, resp_len);
+	if (ret)
+		return ret;
+
+	return NULL;
+}
+
+
+static int wpas_global_ctrl_iface_set(struct wpa_global *global, char *cmd)
+{
+	char *value;
+
+	value = os_strchr(cmd, ' ');
+	if (value == NULL)
+		return -1;
+	*value++ = '\0';
+
+	wpa_printf(MSG_DEBUG, "GLOBAL_CTRL_IFACE SET '%s'='%s'", cmd, value);
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (os_strcasecmp(cmd, "wifi_display") == 0) {
+		wifi_display_enable(global, !!atoi(value));
+		return 0;
+	}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	/* Restore cmd to its original value to allow redirection */
+	value[-1] = ' ';
+
+	return -1;
+}
+
+
+static int wpas_global_ctrl_iface_dup_network(struct wpa_global *global,
+					      char *cmd)
+{
+	struct wpa_supplicant *wpa_s[2]; /* src, dst */
+	char *p;
+	unsigned int i;
+
+	/* cmd: "<src ifname> <dst ifname> <src network id> <dst network id>
+	 * <variable name> */
+
+	for (i = 0; i < ARRAY_SIZE(wpa_s) ; i++) {
+		p = os_strchr(cmd, ' ');
+		if (p == NULL)
+			return -1;
+		*p = '\0';
+
+		wpa_s[i] = global->ifaces;
+		for (; wpa_s[i]; wpa_s[i] = wpa_s[i]->next) {
+			if (os_strcmp(cmd, wpa_s[i]->ifname) == 0)
+				break;
+		}
+
+		if (!wpa_s[i]) {
+			wpa_printf(MSG_DEBUG,
+				   "CTRL_IFACE: Could not find iface=%s", cmd);
+			return -1;
+		}
+
+		cmd = p + 1;
+	}
+
+	return wpa_supplicant_ctrl_iface_dup_network(wpa_s[0], cmd, wpa_s[1]);
+}
+
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+static int wpas_global_ctrl_iface_save_config(struct wpa_global *global)
+{
+	int ret = 0, saved = 0;
+	struct wpa_supplicant *wpa_s;
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (!wpa_s->conf->update_config) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed to update configuration (update_config=0)");
+			continue;
+		}
+
+		if (wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to update configuration");
+			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 */
+
+
+static int wpas_global_ctrl_iface_status(struct wpa_global *global,
+					 char *buf, size_t buflen)
+{
+	char *pos, *end;
+	int ret;
+	struct wpa_supplicant *wpa_s;
+
+	pos = buf;
+	end = buf + buflen;
+
+#ifdef CONFIG_P2P
+	if (global->p2p && !global->p2p_disabled) {
+		ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR
+				  "\n"
+				  "p2p_state=%s\n",
+				  MAC2STR(global->p2p_dev_addr),
+				  p2p_get_state_txt(global->p2p));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	} else if (global->p2p) {
+		ret = os_snprintf(pos, end - pos, "p2p_state=DISABLED\n");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WIFI_DISPLAY
+	ret = os_snprintf(pos, end - pos, "wifi_display=%d\n",
+			  !!global->wifi_display);
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		ret = os_snprintf(pos, end - pos, "ifname=%s\n"
+				  "address=" MACSTR "\n",
+				  wpa_s->ifname, MAC2STR(wpa_s->own_addr));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+#ifdef CONFIG_FST
+
+static int wpas_global_ctrl_iface_fst_attach(struct wpa_global *global,
+					     char *cmd, char *buf,
+					     size_t reply_size)
+{
+	char ifname[IFNAMSIZ + 1];
+	struct fst_iface_cfg cfg;
+	struct wpa_supplicant *wpa_s;
+	struct fst_wpa_obj iface_obj;
+
+	if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
+		wpa_s = wpa_supplicant_get_iface(global, ifname);
+		if (wpa_s) {
+			if (wpa_s->fst) {
+				wpa_printf(MSG_INFO, "FST: Already attached");
+				return -1;
+			}
+			fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
+			wpa_s->fst = fst_attach(ifname, wpa_s->own_addr,
+						&iface_obj, &cfg);
+			if (wpa_s->fst)
+				return os_snprintf(buf, reply_size, "OK\n");
+		}
+	}
+
+	return -1;
+}
+
+
+static int wpas_global_ctrl_iface_fst_detach(struct wpa_global *global,
+					     char *cmd, char *buf,
+					     size_t reply_size)
+{
+	char ifname[IFNAMSIZ + 1];
+	struct wpa_supplicant *wpa_s;
+
+	if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
+		wpa_s = wpa_supplicant_get_iface(global, ifname);
+		if (wpa_s) {
+			if (!fst_iface_detach(ifname)) {
+				wpa_s->fst = NULL;
+				return os_snprintf(buf, reply_size, "OK\n");
+			}
+		}
+	}
+
+	return -1;
+}
+
+#endif /* CONFIG_FST */
+
+
+char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
+						char *buf, size_t *resp_len)
+{
+	char *reply;
+	const int reply_size = 2048;
+	int reply_len;
+	int level = MSG_DEBUG;
+
+	if (os_strncmp(buf, "IFNAME=", 7) == 0) {
+		char *pos = os_strchr(buf + 7, ' ');
+		if (pos) {
+			*pos++ = '\0';
+			return wpas_global_ctrl_iface_ifname(global,
+							     buf + 7, pos,
+							     resp_len);
+		}
+	}
+
+	reply = wpas_global_ctrl_iface_redir(global, buf, resp_len);
+	if (reply)
+		return reply;
+
+	if (os_strcmp(buf, "PING") == 0)
+		level = MSG_EXCESSIVE;
+	wpa_hexdump_ascii(level, "RX global ctrl_iface",
+			  (const u8 *) buf, os_strlen(buf));
+
+	reply = os_malloc(reply_size);
+	if (reply == NULL) {
+		*resp_len = 1;
+		return NULL;
+	}
+
+	os_memcpy(reply, "OK\n", 3);
+	reply_len = 3;
+
+	if (os_strcmp(buf, "PING") == 0) {
+		os_memcpy(reply, "PONG\n", 5);
+		reply_len = 5;
+	} else if (os_strncmp(buf, "INTERFACE_ADD ", 14) == 0) {
+		if (wpa_supplicant_global_iface_add(global, buf + 14))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "INTERFACE_REMOVE ", 17) == 0) {
+		if (wpa_supplicant_global_iface_remove(global, buf + 17))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "INTERFACE_LIST") == 0) {
+		reply_len = wpa_supplicant_global_iface_list(
+			global, reply, reply_size);
+	} else if (os_strcmp(buf, "INTERFACES") == 0) {
+		reply_len = wpa_supplicant_global_iface_interfaces(
+			global, reply, reply_size);
+#ifdef CONFIG_FST
+	} else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
+		reply_len = wpas_global_ctrl_iface_fst_attach(global, buf + 11,
+							      reply,
+							      reply_size);
+	} else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
+		reply_len = wpas_global_ctrl_iface_fst_detach(global, buf + 11,
+							      reply,
+							      reply_size);
+	} else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
+		reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
+#endif /* CONFIG_FST */
+	} else if (os_strcmp(buf, "TERMINATE") == 0) {
+		wpa_supplicant_terminate_proc(global);
+	} else if (os_strcmp(buf, "SUSPEND") == 0) {
+		wpas_notify_suspend(global);
+	} 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)) {
+#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;
+		}
+	} else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
+		if (wpas_global_ctrl_iface_dup_network(global, buf + 12))
+			reply_len = -1;
+#ifndef CONFIG_NO_CONFIG_WRITE
+	} else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
+		if (wpas_global_ctrl_iface_save_config(global))
+			reply_len = -1;
+#endif /* CONFIG_NO_CONFIG_WRITE */
+	} 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 if (os_strncmp(buf, "RELOG", 5) == 0) {
+		if (wpa_debug_reopen_file() < 0)
+			reply_len = -1;
+	} else {
+		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+		reply_len = 16;
+	}
+
+	if (reply_len < 0) {
+		os_memcpy(reply, "FAIL\n", 5);
+		reply_len = 5;
+	}
+
+	*resp_len = reply_len;
+	return reply;
+}
diff --git a/hostap/wpa_supplicant/ctrl_iface.h b/hostap/wpa_supplicant/ctrl_iface.h
new file mode 100644
index 0000000..d54cc07
--- /dev/null
+++ b/hostap/wpa_supplicant/ctrl_iface.h
@@ -0,0 +1,159 @@
+/*
+ * WPA Supplicant / UNIX domain socket -based control interface
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CTRL_IFACE_H
+#define CTRL_IFACE_H
+
+#ifdef CONFIG_CTRL_IFACE
+
+/* Shared functions from ctrl_iface.c; to be called by ctrl_iface backends */
+
+/**
+ * wpa_supplicant_ctrl_iface_process - Process ctrl_iface command
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @buf: Received command buffer (nul terminated string)
+ * @resp_len: Variable to be set to the response length
+ * Returns: Response (*resp_len bytes) or %NULL on failure
+ *
+ * Control interface backends call this function when receiving a message that
+ * they do not process internally, i.e., anything else than ATTACH, DETACH,
+ * and LEVEL. The return response value is then sent to the external program
+ * that sent the command. Caller is responsible for freeing the buffer after
+ * this. If %NULL is returned, *resp_len can be set to two special values:
+ * 1 = send "FAIL\n" response, 2 = send "OK\n" response. If *resp_len has any
+ * other value, no response is sent.
+ */
+char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+					 char *buf, size_t *resp_len);
+
+/**
+ * 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
+ * Returns: Response (*resp_len bytes) or %NULL on failure
+ *
+ * Control interface backends call this function when receiving a message from
+ * the global ctrl_iface connection. The return response value is then sent to
+ * the external program that sent the command. Caller is responsible for
+ * freeing the buffer after this. If %NULL is returned, *resp_len can be set to
+ * two special values: 1 = send "FAIL\n" response, 2 = send "OK\n" response. If
+ * *resp_len has any other value, no response is sent.
+ */
+char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
+						char *buf, size_t *resp_len);
+
+
+/* Functions that each ctrl_iface backend must implement */
+
+/**
+ * wpa_supplicant_ctrl_iface_init - Initialize control interface
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: Pointer to private data on success, %NULL on failure
+ *
+ * Initialize the control interface and start receiving commands from external
+ * programs.
+ *
+ * Required to be implemented in each control interface backend.
+ */
+struct ctrl_iface_priv *
+wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s);
+
+/**
+ * wpa_supplicant_ctrl_iface_deinit - Deinitialize control interface
+ * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init()
+ *
+ * Deinitialize the control interface that was initialized with
+ * wpa_supplicant_ctrl_iface_init().
+ *
+ * Required to be implemented in each control interface backend.
+ */
+void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv);
+
+/**
+ * wpa_supplicant_ctrl_iface_wait - Wait for ctrl_iface monitor
+ * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init()
+ *
+ * Wait until the first message from an external program using the control
+ * interface is received. This function can be used to delay normal startup
+ * processing to allow control interface programs to attach with
+ * %wpa_supplicant before normal operations are started.
+ *
+ * Required to be implemented in each control interface backend.
+ */
+void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv);
+
+/**
+ * wpa_supplicant_global_ctrl_iface_init - Initialize global control interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: Pointer to private data on success, %NULL on failure
+ *
+ * Initialize the global control interface and start receiving commands from
+ * external programs.
+ *
+ * Required to be implemented in each control interface backend.
+ */
+struct ctrl_iface_global_priv *
+wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global);
+
+/**
+ * wpa_supplicant_global_ctrl_iface_deinit - Deinitialize global ctrl interface
+ * @priv: Pointer to private data from wpa_supplicant_global_ctrl_iface_init()
+ *
+ * Deinitialize the global control interface that was initialized with
+ * wpa_supplicant_global_ctrl_iface_init().
+ *
+ * Required to be implemented in each control interface backend.
+ */
+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 *
+wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+{
+	return (void *) -1;
+}
+
+static inline void
+wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+{
+}
+
+static inline void
+wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, int level,
+			       char *buf, size_t len)
+{
+}
+
+static inline void
+wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
+{
+}
+
+static inline struct ctrl_iface_global_priv *
+wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
+{
+	return (void *) 1;
+}
+
+static inline void
+wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
+{
+}
+
+static inline void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
+{
+}
+
+#endif /* CONFIG_CTRL_IFACE */
+
+#endif /* CTRL_IFACE_H */
diff --git a/hostap/wpa_supplicant/ctrl_iface_named_pipe.c b/hostap/wpa_supplicant/ctrl_iface_named_pipe.c
new file mode 100644
index 0000000..54e0e2f
--- /dev/null
+++ b/hostap/wpa_supplicant/ctrl_iface_named_pipe.c
@@ -0,0 +1,830 @@
+/*
+ * WPA Supplicant / Windows Named Pipe -based control interface
+ * Copyright (c) 2004-2006, 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 "eloop.h"
+#include "config.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "wpa_supplicant_i.h"
+#include "ctrl_iface.h"
+#include "common/wpa_ctrl.h"
+
+#ifdef __MINGW32_VERSION
+/* mingw-w32api v3.1 does not yet include sddl.h, so define needed parts here
+ */
+#define SDDL_REVISION_1 1
+BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorA(
+	LPCSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG);
+BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW(
+	LPCWSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG);
+#ifdef UNICODE
+#define ConvertStringSecurityDescriptorToSecurityDescriptor \
+ConvertStringSecurityDescriptorToSecurityDescriptorW
+#else
+#define ConvertStringSecurityDescriptorToSecurityDescriptor \
+ConvertStringSecurityDescriptorToSecurityDescriptorA
+#endif
+#else /* __MINGW32_VERSION */
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+#include <sddl.h>
+#endif /* __MINGW32_VERSION */
+
+#ifndef WPA_SUPPLICANT_NAMED_PIPE
+#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant"
+#endif
+#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE)
+
+/* Per-interface ctrl_iface */
+
+#define REQUEST_BUFSIZE 256
+#define REPLY_BUFSIZE 4096
+
+struct ctrl_iface_priv;
+
+/**
+ * struct wpa_ctrl_dst - Internal data structure of control interface clients
+ *
+ * This structure is used to store information about registered control
+ * interface monitors into struct wpa_supplicant. This data is private to
+ * ctrl_iface_named_pipe.c and should not be touched directly from other files.
+ */
+struct wpa_ctrl_dst {
+	/* Note: OVERLAPPED must be the first member of struct wpa_ctrl_dst */
+	OVERLAPPED overlap;
+	struct wpa_ctrl_dst *next, *prev;
+	struct ctrl_iface_priv *priv;
+	HANDLE pipe;
+	int attached;
+	int debug_level;
+	int errors;
+	char req_buf[REQUEST_BUFSIZE];
+	char *rsp_buf;
+	int used;
+};
+
+
+struct ctrl_iface_priv {
+	struct wpa_supplicant *wpa_s;
+	struct wpa_ctrl_dst *ctrl_dst;
+	SECURITY_ATTRIBUTES attr;
+	int sec_attr_set;
+};
+
+
+static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
+					   int level, const char *buf,
+					   size_t len);
+
+static void ctrl_close_pipe(struct wpa_ctrl_dst *dst);
+static void wpa_supplicant_ctrl_iface_receive(void *, void *);
+static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes,
+					     LPOVERLAPPED overlap);
+
+struct wpa_global_dst;
+static void global_close_pipe(struct wpa_global_dst *dst);
+static void wpa_supplicant_global_iface_receive(void *eloop_data,
+						void *user_ctx);
+static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes,
+					       LPOVERLAPPED overlap);
+
+
+static int ctrl_broken_pipe(HANDLE pipe, int used)
+{
+	DWORD err;
+
+	if (PeekNamedPipe(pipe, NULL, 0, NULL, NULL, NULL))
+		return 0;
+
+	err = GetLastError();
+	if (err == ERROR_BROKEN_PIPE || (err == ERROR_BAD_PIPE && used))
+		return 1;
+	return 0;
+}
+
+
+static void ctrl_flush_broken_pipes(struct ctrl_iface_priv *priv)
+{
+	struct wpa_ctrl_dst *dst, *next;
+
+	dst = priv->ctrl_dst;
+
+	while (dst) {
+		next = dst->next;
+		if (ctrl_broken_pipe(dst->pipe, dst->used)) {
+			wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p",
+				   dst);
+			ctrl_close_pipe(dst);
+		}
+		dst = next;
+	}
+}
+
+
+static int ctrl_open_pipe(struct ctrl_iface_priv *priv)
+{
+	struct wpa_ctrl_dst *dst;
+	DWORD err;
+	TCHAR name[256];
+
+	dst = os_zalloc(sizeof(*dst));
+	if (dst == NULL)
+		return -1;
+	wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst);
+
+	dst->priv = priv;
+	dst->debug_level = MSG_INFO;
+	dst->pipe = INVALID_HANDLE_VALUE;
+
+	dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+	if (dst->overlap.hEvent == NULL) {
+		wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d",
+			   (int) GetLastError());
+		goto fail;
+	}
+
+	eloop_register_event(dst->overlap.hEvent,
+			     sizeof(dst->overlap.hEvent),
+			     wpa_supplicant_ctrl_iface_receive, dst, NULL);
+
+#ifdef UNICODE
+	_snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"),
+		   priv->wpa_s->ifname);
+#else /* UNICODE */
+	os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s",
+		    priv->wpa_s->ifname);
+#endif /* UNICODE */
+
+	/* TODO: add support for configuring access list for the pipe */
+	dst->pipe = CreateNamedPipe(name,
+				    PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+				    PIPE_TYPE_MESSAGE |
+				    PIPE_READMODE_MESSAGE |
+				    PIPE_WAIT,
+				    15, REPLY_BUFSIZE, REQUEST_BUFSIZE,
+				    1000,
+				    priv->sec_attr_set ? &priv->attr : NULL);
+	if (dst->pipe == INVALID_HANDLE_VALUE) {
+		wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d",
+			   (int) GetLastError());
+		goto fail;
+	}
+
+	if (ConnectNamedPipe(dst->pipe, &dst->overlap)) {
+		wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d",
+			   (int) GetLastError());
+		CloseHandle(dst->pipe);
+		os_free(dst);
+		return -1;
+	}
+
+	err = GetLastError();
+	switch (err) {
+	case ERROR_IO_PENDING:
+		wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in "
+			   "progress");
+		break;
+	case ERROR_PIPE_CONNECTED:
+		wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already "
+			   "connected");
+		if (SetEvent(dst->overlap.hEvent))
+			break;
+		/* fall through */
+	default:
+		wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d",
+			   (int) err);
+		CloseHandle(dst->pipe);
+		os_free(dst);
+		return -1;
+	}
+
+	dst->next = priv->ctrl_dst;
+	if (dst->next)
+		dst->next->prev = dst;
+	priv->ctrl_dst = dst;
+
+	return 0;
+
+fail:
+	ctrl_close_pipe(dst);
+	return -1;
+}
+
+
+static void ctrl_close_pipe(struct wpa_ctrl_dst *dst)
+{
+	wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst);
+
+	if (dst->overlap.hEvent) {
+		eloop_unregister_event(dst->overlap.hEvent,
+				       sizeof(dst->overlap.hEvent));
+		CloseHandle(dst->overlap.hEvent);
+	}
+
+	if (dst->pipe != INVALID_HANDLE_VALUE) {
+		/*
+		 * Could use FlushFileBuffers() here to guarantee that all data
+		 * gets delivered to the client, but that can block, so let's
+		 * not do this for now.
+		 * FlushFileBuffers(dst->pipe);
+		 */
+		CloseHandle(dst->pipe);
+	}
+
+	if (dst->prev)
+		dst->prev->next = dst->next;
+	else
+		dst->priv->ctrl_dst = dst->next;
+	if (dst->next)
+		dst->next->prev = dst->prev;
+
+	os_free(dst->rsp_buf);
+	os_free(dst);
+}
+
+
+static VOID WINAPI ctrl_iface_write_completed(DWORD err, DWORD bytes,
+					      LPOVERLAPPED overlap)
+{
+	struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap;
+	wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p "
+		   "err=%d bytes=%d", dst, (int) err, (int) bytes);
+	if (err) {
+		ctrl_close_pipe(dst);
+		return;
+	}
+
+	os_free(dst->rsp_buf);
+	dst->rsp_buf = NULL;
+
+	if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf),
+			&dst->overlap, ctrl_iface_read_completed)) {
+		wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d",
+			   (int) GetLastError());
+		ctrl_close_pipe(dst);
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst);
+}
+
+
+static void wpa_supplicant_ctrl_iface_rx(struct wpa_ctrl_dst *dst, size_t len)
+{
+	struct wpa_supplicant *wpa_s = dst->priv->wpa_s;
+	char *reply = NULL, *send_buf;
+	size_t reply_len = 0, send_len;
+	int new_attached = 0;
+	char *buf = dst->req_buf;
+
+	dst->used = 1;
+	if (len >= REQUEST_BUFSIZE)
+		len = REQUEST_BUFSIZE - 1;
+	buf[len] = '\0';
+
+	if (os_strcmp(buf, "ATTACH") == 0) {
+		dst->attached = 1;
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached");
+		new_attached = 1;
+		reply_len = 2;
+	} else if (os_strcmp(buf, "DETACH") == 0) {
+		dst->attached = 0;
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached");
+		reply_len = 2;
+	} else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", buf + 6);
+		dst->debug_level = atoi(buf + 6);
+		reply_len = 2;
+	} else {
+		reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
+							  &reply_len);
+	}
+
+	if (reply) {
+		send_buf = reply;
+		send_len = reply_len;
+	} else if (reply_len == 2) {
+		send_buf = "OK\n";
+		send_len = 3;
+	} else {
+		send_buf = "FAIL\n";
+		send_len = 5;
+	}
+
+	os_free(dst->rsp_buf);
+	dst->rsp_buf = os_malloc(send_len);
+	if (dst->rsp_buf == NULL) {
+		ctrl_close_pipe(dst);
+		os_free(reply);
+		return;
+	}
+	os_memcpy(dst->rsp_buf, send_buf, send_len);
+	os_free(reply);
+
+	if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
+			 ctrl_iface_write_completed)) {
+		wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d",
+			   (int) GetLastError());
+		ctrl_close_pipe(dst);
+	} else {
+		wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p",
+			   dst);
+	}
+
+	if (new_attached)
+		eapol_sm_notify_ctrl_attached(wpa_s->eapol);
+}
+
+
+static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes,
+					     LPOVERLAPPED overlap)
+{
+	struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap;
+	wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d "
+		   "bytes=%d", dst, (int) err, (int) bytes);
+	if (err == 0 && bytes > 0)
+		wpa_supplicant_ctrl_iface_rx(dst, bytes);
+}
+
+
+static void wpa_supplicant_ctrl_iface_receive(void *eloop_data, void *user_ctx)
+{
+	struct wpa_ctrl_dst *dst = eloop_data;
+	struct ctrl_iface_priv *priv = dst->priv;
+	DWORD bytes;
+
+	wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_ctrl_iface_receive");
+	ResetEvent(dst->overlap.hEvent);
+
+	if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) {
+		wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d",
+			   (int) GetLastError());
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client "
+		   "connected");
+
+	/* Open a new named pipe for the next client. */
+	ctrl_open_pipe(priv);
+
+	/* Use write completion function to start reading a command */
+	ctrl_iface_write_completed(0, 0, &dst->overlap);
+
+	ctrl_flush_broken_pipes(priv);
+}
+
+
+static int ctrl_iface_parse(struct ctrl_iface_priv *priv, const char *params)
+{
+	const char *sddl = NULL;
+	TCHAR *t_sddl;
+
+	if (os_strncmp(params, "SDDL=", 5) == 0)
+		sddl = params + 5;
+	if (!sddl) {
+		sddl = os_strstr(params, " SDDL=");
+		if (sddl)
+			sddl += 6;
+	}
+
+	if (!sddl)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "CTRL: SDDL='%s'", sddl);
+	os_memset(&priv->attr, 0, sizeof(priv->attr));
+	priv->attr.nLength = sizeof(priv->attr);
+	priv->attr.bInheritHandle = FALSE;
+	t_sddl = wpa_strdup_tchar(sddl);
+	if (t_sddl == NULL)
+		return -1;
+	if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
+		    t_sddl, SDDL_REVISION_1,
+		    (PSECURITY_DESCRIPTOR *) (void *)
+		    &priv->attr.lpSecurityDescriptor,
+		    NULL)) {
+		os_free(t_sddl);
+		wpa_printf(MSG_ERROR, "CTRL: SDDL='%s' - could not convert to "
+			   "security descriptor: %d",
+			   sddl, (int) GetLastError());
+		return -1;
+	}
+	os_free(t_sddl);
+
+	priv->sec_attr_set = 1;
+
+	return 0;
+}
+
+
+static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
+					     enum wpa_msg_type type,
+					     const char *txt, size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	if (wpa_s == NULL || wpa_s->ctrl_iface == NULL)
+		return;
+	wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len);
+}
+
+
+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;
+	priv->wpa_s = wpa_s;
+
+	if (wpa_s->conf->ctrl_interface == NULL)
+		return priv;
+
+	if (ctrl_iface_parse(priv, wpa_s->conf->ctrl_interface) < 0) {
+		os_free(priv);
+		return NULL;
+	}
+
+	if (ctrl_open_pipe(priv) < 0) {
+		os_free(priv);
+		return NULL;
+	}
+
+	wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
+
+	return priv;
+}
+
+
+void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+{
+	while (priv->ctrl_dst)
+		ctrl_close_pipe(priv->ctrl_dst);
+	if (priv->sec_attr_set)
+		LocalFree(priv->attr.lpSecurityDescriptor);
+	os_free(priv);
+}
+
+
+static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
+					   int level, const char *buf,
+					   size_t len)
+{
+	struct wpa_ctrl_dst *dst, *next;
+	char levelstr[10];
+	int idx;
+	char *sbuf;
+	int llen;
+	DWORD written;
+
+	dst = priv->ctrl_dst;
+	if (dst == NULL)
+		return;
+
+	os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
+
+	llen = os_strlen(levelstr);
+	sbuf = os_malloc(llen + len);
+	if (sbuf == NULL)
+		return;
+
+	os_memcpy(sbuf, levelstr, llen);
+	os_memcpy(sbuf + llen, buf, len);
+
+	idx = 0;
+	while (dst) {
+		next = dst->next;
+		if (dst->attached && level >= dst->debug_level) {
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %p",
+				   dst);
+			if (!WriteFile(dst->pipe, sbuf, llen + len, &written,
+				       NULL)) {
+				wpa_printf(MSG_DEBUG, "CTRL: WriteFile to dst "
+					   "%p failed: %d",
+					   dst, (int) GetLastError());
+				dst->errors++;
+				if (dst->errors > 10)
+					ctrl_close_pipe(dst);
+			} else
+				dst->errors = 0;
+		}
+		idx++;
+		dst = next;
+	}
+	os_free(sbuf);
+}
+
+
+void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
+{
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor",
+		   priv->wpa_s->ifname);
+	if (priv->ctrl_dst == NULL)
+		return;
+	WaitForSingleObject(priv->ctrl_dst->pipe, INFINITE);
+}
+
+
+/* Global ctrl_iface */
+
+struct ctrl_iface_global_priv;
+
+struct wpa_global_dst {
+	/* Note: OVERLAPPED must be the first member of struct wpa_global_dst
+	 */
+	OVERLAPPED overlap;
+	struct wpa_global_dst *next, *prev;
+	struct ctrl_iface_global_priv *priv;
+	HANDLE pipe;
+	char req_buf[REQUEST_BUFSIZE];
+	char *rsp_buf;
+	int used;
+};
+
+struct ctrl_iface_global_priv {
+	struct wpa_global *global;
+	struct wpa_global_dst *ctrl_dst;
+};
+
+
+static void global_flush_broken_pipes(struct ctrl_iface_global_priv *priv)
+{
+	struct wpa_global_dst *dst, *next;
+
+	dst = priv->ctrl_dst;
+
+	while (dst) {
+		next = dst->next;
+		if (ctrl_broken_pipe(dst->pipe, dst->used)) {
+			wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p",
+				   dst);
+			global_close_pipe(dst);
+		}
+		dst = next;
+	}
+}
+
+
+static int global_open_pipe(struct ctrl_iface_global_priv *priv)
+{
+	struct wpa_global_dst *dst;
+	DWORD err;
+
+	dst = os_zalloc(sizeof(*dst));
+	if (dst == NULL)
+		return -1;
+	wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst);
+
+	dst->priv = priv;
+	dst->pipe = INVALID_HANDLE_VALUE;
+
+	dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+	if (dst->overlap.hEvent == NULL) {
+		wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d",
+			   (int) GetLastError());
+		goto fail;
+	}
+
+	eloop_register_event(dst->overlap.hEvent,
+			     sizeof(dst->overlap.hEvent),
+			     wpa_supplicant_global_iface_receive, dst, NULL);
+
+	/* TODO: add support for configuring access list for the pipe */
+	dst->pipe = CreateNamedPipe(NAMED_PIPE_PREFIX,
+				    PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+				    PIPE_TYPE_MESSAGE |
+				    PIPE_READMODE_MESSAGE |
+				    PIPE_WAIT,
+				    10, REPLY_BUFSIZE, REQUEST_BUFSIZE,
+				    1000, NULL);
+	if (dst->pipe == INVALID_HANDLE_VALUE) {
+		wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d",
+			   (int) GetLastError());
+		goto fail;
+	}
+
+	if (ConnectNamedPipe(dst->pipe, &dst->overlap)) {
+		wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d",
+			   (int) GetLastError());
+		CloseHandle(dst->pipe);
+		os_free(dst);
+		return -1;
+	}
+
+	err = GetLastError();
+	switch (err) {
+	case ERROR_IO_PENDING:
+		wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in "
+			   "progress");
+		break;
+	case ERROR_PIPE_CONNECTED:
+		wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already "
+			   "connected");
+		if (SetEvent(dst->overlap.hEvent))
+			break;
+		/* fall through */
+	default:
+		wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d",
+			   (int) err);
+		CloseHandle(dst->pipe);
+		os_free(dst);
+		return -1;
+	}
+
+	dst->next = priv->ctrl_dst;
+	if (dst->next)
+		dst->next->prev = dst;
+	priv->ctrl_dst = dst;
+
+	return 0;
+
+fail:
+	global_close_pipe(dst);
+	return -1;
+}
+
+
+static void global_close_pipe(struct wpa_global_dst *dst)
+{
+	wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst);
+
+	if (dst->overlap.hEvent) {
+		eloop_unregister_event(dst->overlap.hEvent,
+				       sizeof(dst->overlap.hEvent));
+		CloseHandle(dst->overlap.hEvent);
+	}
+
+	if (dst->pipe != INVALID_HANDLE_VALUE) {
+		/*
+		 * Could use FlushFileBuffers() here to guarantee that all data
+		 * gets delivered to the client, but that can block, so let's
+		 * not do this for now.
+		 * FlushFileBuffers(dst->pipe);
+		 */
+		CloseHandle(dst->pipe);
+	}
+
+	if (dst->prev)
+		dst->prev->next = dst->next;
+	else
+		dst->priv->ctrl_dst = dst->next;
+	if (dst->next)
+		dst->next->prev = dst->prev;
+
+	os_free(dst->rsp_buf);
+	os_free(dst);
+}
+
+
+static VOID WINAPI global_iface_write_completed(DWORD err, DWORD bytes,
+						LPOVERLAPPED overlap)
+{
+	struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap;
+	wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p "
+		   "err=%d bytes=%d", dst, (int) err, (int) bytes);
+	if (err) {
+		global_close_pipe(dst);
+		return;
+	}
+
+	os_free(dst->rsp_buf);
+	dst->rsp_buf = NULL;
+
+	if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf),
+			&dst->overlap, global_iface_read_completed)) {
+		wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d",
+			   (int) GetLastError());
+		global_close_pipe(dst);
+		/* FIX: if this was the pipe waiting for new global
+		 * connections, at this point there are no open global pipes..
+		 * Should try to open a new pipe.. */
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst);
+}
+
+
+static void wpa_supplicant_global_iface_rx(struct wpa_global_dst *dst,
+					   size_t len)
+{
+	struct wpa_global *global = dst->priv->global;
+	char *reply = NULL, *send_buf;
+	size_t reply_len = 0, send_len;
+	char *buf = dst->req_buf;
+
+	dst->used = 1;
+	if (len >= REQUEST_BUFSIZE)
+		len = REQUEST_BUFSIZE - 1;
+	buf[len] = '\0';
+
+	reply = wpa_supplicant_global_ctrl_iface_process(global, buf,
+							 &reply_len);
+	if (reply) {
+		send_buf = reply;
+		send_len = reply_len;
+	} else if (reply_len) {
+		send_buf = "FAIL\n";
+		send_len = 5;
+	} else {
+		os_free(dst->rsp_buf);
+		dst->rsp_buf = NULL;
+		return;
+	}
+
+	os_free(dst->rsp_buf);
+	dst->rsp_buf = os_malloc(send_len);
+	if (dst->rsp_buf == NULL) {
+		global_close_pipe(dst);
+		os_free(reply);
+		return;
+	}
+	os_memcpy(dst->rsp_buf, send_buf, send_len);
+	os_free(reply);
+
+	if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
+			 global_iface_write_completed)) {
+		wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d",
+			   (int) GetLastError());
+		global_close_pipe(dst);
+	} else {
+		wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p",
+			   dst);
+	}
+}
+
+
+static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes,
+					       LPOVERLAPPED overlap)
+{
+	struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap;
+	wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d "
+		   "bytes=%d", dst, (int) err, (int) bytes);
+	if (err == 0 && bytes > 0)
+		wpa_supplicant_global_iface_rx(dst, bytes);
+}
+
+
+static void wpa_supplicant_global_iface_receive(void *eloop_data,
+						void *user_ctx)
+{
+	struct wpa_global_dst *dst = eloop_data;
+	struct ctrl_iface_global_priv *priv = dst->priv;
+	DWORD bytes;
+
+	wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_global_iface_receive");
+	ResetEvent(dst->overlap.hEvent);
+
+	if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) {
+		wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d",
+			   (int) GetLastError());
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client "
+		   "connected");
+
+	/* Open a new named pipe for the next client. */
+	if (global_open_pipe(priv) < 0) {
+		wpa_printf(MSG_DEBUG, "CTRL: global_open_pipe failed");
+		return;
+	}
+
+	/* Use write completion function to start reading a command */
+	global_iface_write_completed(0, 0, &dst->overlap);
+
+	global_flush_broken_pipes(priv);
+}
+
+
+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;
+	priv->global = global;
+
+	if (global_open_pipe(priv) < 0) {
+		os_free(priv);
+		return NULL;
+	}
+
+	return priv;
+}
+
+
+void
+wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
+{
+	while (priv->ctrl_dst)
+		global_close_pipe(priv->ctrl_dst);
+	os_free(priv);
+}
diff --git a/hostap/wpa_supplicant/ctrl_iface_udp.c b/hostap/wpa_supplicant/ctrl_iface_udp.c
new file mode 100644
index 0000000..76f69f2
--- /dev/null
+++ b/hostap/wpa_supplicant/ctrl_iface_udp.c
@@ -0,0 +1,690 @@
+/*
+ * WPA Supplicant / UDP socket -based control interface
+ * Copyright (c) 2004-2005, 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 "eloop.h"
+#include "config.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "wpa_supplicant_i.h"
+#include "ctrl_iface.h"
+#include "common/wpa_ctrl.h"
+
+
+#define COOKIE_LEN 8
+
+/* Per-interface ctrl_iface */
+
+/**
+ * struct wpa_ctrl_dst - Internal data structure of control interface monitors
+ *
+ * This structure is used to store information about registered control
+ * interface monitors into struct wpa_supplicant. This data is private to
+ * ctrl_iface_udp.c and should not be touched directly from other files.
+ */
+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;
+};
+
+
+struct ctrl_iface_priv {
+	struct wpa_supplicant *wpa_s;
+	int sock;
+	struct wpa_ctrl_dst *ctrl_dst;
+	u8 cookie[COOKIE_LEN];
+};
+
+
+static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
+					   int level, const char *buf,
+					   size_t len);
+
+
+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(*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
+				prev->next = dst->next;
+			os_free(dst);
+			return 0;
+		}
+		prev = dst;
+		dst = dst->next;
+	}
+	return -1;
+}
+
+
+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;
+		}
+		dst = dst->next;
+	}
+
+	return -1;
+}
+
+
+static char *
+wpa_supplicant_ctrl_iface_get_cookie(struct ctrl_iface_priv *priv,
+				     size_t *reply_len)
+{
+	char *reply;
+	reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
+	if (reply == NULL) {
+		*reply_len = 1;
+		return NULL;
+	}
+
+	os_memcpy(reply, "COOKIE=", 7);
+	wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
+			 priv->cookie, COOKIE_LEN);
+
+	*reply_len = 7 + 2 * COOKIE_LEN;
+	return reply;
+}
+
+
+static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
+					      void *sock_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	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;
+	int new_attached = 0;
+	u8 cookie[COOKIE_LEN];
+
+	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+		       (struct sockaddr *) &from, &fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+			   strerror(errno));
+		return;
+	}
+
+#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
+		 * frames since the socket is bound to only localhost address.
+		 * Just in case, drop the frame if it is coming from any other
+		 * address.
+		 */
+		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
+			   "source %s", inet_ntoa(from.sin_addr));
+		return;
+	}
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
+	buf[res] = '\0';
+
+	if (os_strcmp(buf, "GET_COOKIE") == 0) {
+		reply = wpa_supplicant_ctrl_iface_get_cookie(priv, &reply_len);
+		goto done;
+	}
+
+	/*
+	 * Require that the client includes a prefix with the 'cookie' value
+	 * fetched with GET_COOKIE command. This is used to verify that the
+	 * client has access to a bidirectional link over UDP in order to
+	 * avoid attacks using forged localhost IP address even if the OS does
+	 * not block such frames from remote destinations.
+	 */
+	if (os_strncmp(buf, "COOKIE=", 7) != 0) {
+		wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
+			   "drop request");
+		return;
+	}
+
+	if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
+		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
+			   "request - drop request");
+		return;
+	}
+
+	if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
+			   "drop request");
+		return;
+	}
+
+	pos = buf + 7 + 2 * COOKIE_LEN;
+	while (*pos == ' ')
+		pos++;
+
+	if (os_strcmp(pos, "ATTACH") == 0) {
+		if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen))
+			reply_len = 1;
+		else {
+			new_attached = 1;
+			reply_len = 2;
+		}
+	} else if (os_strcmp(pos, "DETACH") == 0) {
+		if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen))
+			reply_len = 1;
+		else
+			reply_len = 2;
+	} else if (os_strncmp(pos, "LEVEL ", 6) == 0) {
+		if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen,
+						    pos + 6))
+			reply_len = 1;
+		else
+			reply_len = 2;
+	} else {
+		reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos,
+							  &reply_len);
+	}
+
+ done:
+	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 (new_attached)
+		eapol_sm_notify_ctrl_attached(wpa_s->eapol);
+}
+
+
+static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
+					     enum wpa_msg_type type,
+					     const char *txt, size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	if (wpa_s == NULL || wpa_s->ctrl_iface == NULL)
+		return;
+	wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len);
+}
+
+
+struct ctrl_iface_priv *
+wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+{
+	struct ctrl_iface_priv *priv;
+	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)
+		return NULL;
+	priv->wpa_s = wpa_s;
+	priv->sock = -1;
+	os_get_random(priv->cookie, COOKIE_LEN);
+
+	if (wpa_s->conf->ctrl_interface == NULL)
+		return priv;
+
+	priv->sock = socket(domain, SOCK_DGRAM, 0);
+	if (priv->sock < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
+		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)
+			goto try_again;
+		wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
+		goto fail;
+	}
+
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	wpa_msg(wpa_s, MSG_DEBUG, "ctrl_iface_init UDP port: %d", port);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
+	eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
+				 wpa_s, priv);
+	wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
+
+	return priv;
+
+fail:
+	if (priv->sock >= 0)
+		close(priv->sock);
+	os_free(priv);
+	return NULL;
+}
+
+
+void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+{
+	struct wpa_ctrl_dst *dst, *prev;
+
+	if (priv->sock > -1) {
+		eloop_unregister_read_sock(priv->sock);
+		if (priv->ctrl_dst) {
+			/*
+			 * Wait before closing the control socket if
+			 * there are any attached monitors in order to allow
+			 * them to receive any pending messages.
+			 */
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached "
+				   "monitors to receive messages");
+			os_sleep(0, 100000);
+		}
+		close(priv->sock);
+		priv->sock = -1;
+	}
+
+	dst = priv->ctrl_dst;
+	while (dst) {
+		prev = dst;
+		dst = dst->next;
+		os_free(prev);
+	}
+	os_free(priv);
+}
+
+
+static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
+					   int level, const char *buf,
+					   size_t len)
+{
+	struct wpa_ctrl_dst *dst, *next;
+	char levelstr[10];
+	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)
+		return;
+
+	os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
+
+	llen = os_strlen(levelstr);
+	sbuf = os_malloc(llen + len);
+	if (sbuf == NULL)
+		return;
+
+	os_memcpy(sbuf, levelstr, llen);
+	os_memcpy(sbuf + llen, buf, len);
+
+	idx = 0;
+	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) {
+				wpa_printf(MSG_ERROR,
+					   "sendto(CTRL_IFACE monitor): %s",
+					   strerror(errno));
+				dst->errors++;
+				if (dst->errors > 10) {
+					wpa_supplicant_ctrl_iface_detach(
+						priv, &dst->addr,
+						dst->addrlen);
+				}
+			} else
+				dst->errors = 0;
+		}
+		idx++;
+		dst = next;
+	}
+	os_free(sbuf);
+}
+
+
+void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
+{
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor",
+		   priv->wpa_s->ifname);
+	eloop_wait_for_read_sock(priv->sock);
+}
+
+
+/* Global ctrl_iface */
+
+struct ctrl_iface_global_priv {
+	int sock;
+	u8 cookie[COOKIE_LEN];
+};
+
+
+static char *
+wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv,
+				 size_t *reply_len)
+{
+	char *reply;
+	reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
+	if (reply == NULL) {
+		*reply_len = 1;
+		return NULL;
+	}
+
+	os_memcpy(reply, "COOKIE=", 7);
+	wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
+			 priv->cookie, COOKIE_LEN);
+
+	*reply_len = 7 + 2 * COOKIE_LEN;
+	return reply;
+}
+
+
+static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
+						     void *sock_ctx)
+{
+	struct wpa_global *global = eloop_ctx;
+	struct ctrl_iface_global_priv *priv = sock_ctx;
+	char buf[256], *pos;
+	int res;
+	struct sockaddr_in from;
+	socklen_t fromlen = sizeof(from);
+	char *reply;
+	size_t reply_len;
+	u8 cookie[COOKIE_LEN];
+
+	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+		       (struct sockaddr *) &from, &fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+			   strerror(errno));
+		return;
+	}
+
+#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
+	if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
+		/*
+		 * The OS networking stack is expected to drop this kind of
+		 * frames since the socket is bound to only localhost address.
+		 * Just in case, drop the frame if it is coming from any other
+		 * address.
+		 */
+		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
+			   "source %s", inet_ntoa(from.sin_addr));
+		return;
+	}
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
+	buf[res] = '\0';
+
+	if (os_strcmp(buf, "GET_COOKIE") == 0) {
+		reply = wpa_supplicant_global_get_cookie(priv, &reply_len);
+		goto done;
+	}
+
+	if (os_strncmp(buf, "COOKIE=", 7) != 0) {
+		wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
+			   "drop request");
+		return;
+	}
+
+	if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
+		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
+			   "request - drop request");
+		return;
+	}
+
+	if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
+			   "drop request");
+		return;
+	}
+
+	pos = buf + 7 + 2 * COOKIE_LEN;
+	while (*pos == ' ')
+		pos++;
+
+	reply = wpa_supplicant_global_ctrl_iface_process(global, pos,
+							 &reply_len);
+
+ done:
+	if (reply) {
+		sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+		       fromlen);
+		os_free(reply);
+	} else if (reply_len) {
+		sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
+		       fromlen);
+	}
+}
+
+
+struct ctrl_iface_global_priv *
+wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
+{
+	struct ctrl_iface_global_priv *priv;
+	struct sockaddr_in addr;
+	int port = WPA_GLOBAL_CTRL_IFACE_PORT;
+
+	priv = os_zalloc(sizeof(*priv));
+	if (priv == NULL)
+		return NULL;
+	priv->sock = -1;
+	os_get_random(priv->cookie, COOKIE_LEN);
+
+	if (global->params.ctrl_interface == NULL)
+		return priv;
+
+	wpa_printf(MSG_DEBUG, "Global control interface '%s'",
+		   global->params.ctrl_interface);
+
+	priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (priv->sock < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
+		goto fail;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	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 */
+try_again:
+	addr.sin_port = htons(port);
+	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		port++;
+		if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) <
+		    WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT)
+			goto try_again;
+		wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
+		goto fail;
+	}
+
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	wpa_printf(MSG_DEBUG, "global_ctrl_iface_init UDP port: %d", port);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
+	eloop_register_read_sock(priv->sock,
+				 wpa_supplicant_global_ctrl_iface_receive,
+				 global, priv);
+
+	return priv;
+
+fail:
+	if (priv->sock >= 0)
+		close(priv->sock);
+	os_free(priv);
+	return NULL;
+}
+
+
+void
+wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
+{
+	if (priv->sock >= 0) {
+		eloop_unregister_read_sock(priv->sock);
+		close(priv->sock);
+	}
+	os_free(priv);
+}
diff --git a/hostap/wpa_supplicant/ctrl_iface_unix.c b/hostap/wpa_supplicant/ctrl_iface_unix.c
new file mode 100644
index 0000000..11f2814
--- /dev/null
+++ b/hostap/wpa_supplicant/ctrl_iface_unix.c
@@ -0,0 +1,1218 @@
+/*
+ * WPA Supplicant / UNIX domain socket -based control interface
+ * 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.
+ */
+
+#include "includes.h"
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <grp.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#ifdef __linux__
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#endif /* __linux__ */
+#ifdef ANDROID
+#include <cutils/sockets.h>
+#endif /* ANDROID */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "ctrl_iface.h"
+
+/* Per-interface ctrl_iface */
+
+/**
+ * struct wpa_ctrl_dst - Internal data structure of control interface monitors
+ *
+ * This structure is used to store information about registered control
+ * interface monitors into struct wpa_supplicant. This data is private to
+ * ctrl_iface_unix.c and should not be touched directly from other files.
+ */
+struct wpa_ctrl_dst {
+	struct dl_list list;
+	struct sockaddr_un addr;
+	socklen_t addrlen;
+	int debug_level;
+	int errors;
+};
+
+
+struct ctrl_iface_priv {
+	struct wpa_supplicant *wpa_s;
+	int sock;
+	struct dl_list ctrl_dst;
+	int android_control_socket;
+};
+
+
+struct ctrl_iface_global_priv {
+	struct wpa_global *global;
+	int sock;
+	struct dl_list ctrl_dst;
+	int android_control_socket;
+};
+
+
+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,
+					   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 void wpas_ctrl_sock_debug(const char *title, int sock, const char *buf,
+				 size_t len)
+{
+#ifdef __linux__
+	socklen_t optlen;
+	int sndbuf, outq;
+	int level = MSG_MSGDUMP;
+
+	if (len >= 5 && os_strncmp(buf, "PONG\n", 5) == 0)
+		level = MSG_EXCESSIVE;
+
+	optlen = sizeof(sndbuf);
+	sndbuf = 0;
+	if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0)
+		sndbuf = -1;
+
+	if (ioctl(sock, SIOCOUTQ, &outq) < 0)
+		outq = -1;
+
+	wpa_printf(level,
+		   "CTRL-DEBUG: %s: sock=%d sndbuf=%d outq=%d send_len=%d",
+		   title, sock, sndbuf, outq, (int) len);
+#endif /* __linux__ */
+}
+
+
+static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst,
+					    struct sockaddr_un *from,
+					    socklen_t fromlen, int global)
+{
+	struct wpa_ctrl_dst *dst;
+	char addr_txt[200];
+
+	dst = os_zalloc(sizeof(*dst));
+	if (dst == NULL)
+		return -1;
+	os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
+	dst->addrlen = fromlen;
+	dst->debug_level = MSG_INFO;
+	dl_list_add(ctrl_dst, &dst->list);
+	printf_encode(addr_txt, sizeof(addr_txt),
+		      (u8 *) from->sun_path,
+		      fromlen - offsetof(struct sockaddr_un, sun_path));
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE %smonitor attached %s",
+		   global ? "global " : "", addr_txt);
+	return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_detach(struct dl_list *ctrl_dst,
+					    struct sockaddr_un *from,
+					    socklen_t fromlen)
+{
+	struct wpa_ctrl_dst *dst;
+
+	dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
+		if (fromlen == dst->addrlen &&
+		    os_memcmp(from->sun_path, dst->addr.sun_path,
+			      fromlen - offsetof(struct sockaddr_un, sun_path))
+		    == 0) {
+			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;
+		}
+	}
+	return -1;
+}
+
+
+static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
+					   struct sockaddr_un *from,
+					   socklen_t fromlen,
+					   char *level)
+{
+	struct wpa_ctrl_dst *dst;
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
+
+	dl_list_for_each(dst, &priv->ctrl_dst, struct wpa_ctrl_dst, list) {
+		if (fromlen == dst->addrlen &&
+		    os_memcmp(from->sun_path, dst->addr.sun_path,
+			      fromlen - offsetof(struct sockaddr_un, sun_path))
+		    == 0) {
+			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;
+		}
+	}
+
+	return -1;
+}
+
+
+static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
+					      void *sock_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct ctrl_iface_priv *priv = sock_ctx;
+	char buf[4096];
+	int res;
+	struct sockaddr_un from;
+	socklen_t fromlen = sizeof(from);
+	char *reply = NULL, *reply_buf = NULL;
+	size_t reply_len = 0;
+	int new_attached = 0;
+
+	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+		       (struct sockaddr *) &from, &fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+			   strerror(errno));
+		return;
+	}
+	buf[res] = '\0';
+
+	if (os_strcmp(buf, "ATTACH") == 0) {
+		if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from,
+						     fromlen, 0))
+			reply_len = 1;
+		else {
+			new_attached = 1;
+			reply_len = 2;
+		}
+	} else if (os_strcmp(buf, "DETACH") == 0) {
+		if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, &from,
+						     fromlen))
+			reply_len = 1;
+		else
+			reply_len = 2;
+	} else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
+		if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen,
+						    buf + 6))
+			reply_len = 1;
+		else
+			reply_len = 2;
+	} else {
+		reply_buf = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
+							      &reply_len);
+		reply = reply_buf;
+
+		/*
+		 * There could be some password/key material in the command, so
+		 * clear the buffer explicitly now that it is not needed
+		 * anymore.
+		 */
+		os_memset(buf, 0, res);
+	}
+
+	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) {
+		wpas_ctrl_sock_debug("ctrl_sock-sendto", sock, reply,
+				     reply_len);
+		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);
+}
+
+
+static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s)
+{
+	char *buf;
+	size_t len;
+	char *pbuf, *dir = NULL;
+	int res;
+
+	if (wpa_s->conf->ctrl_interface == NULL)
+		return NULL;
+
+	pbuf = os_strdup(wpa_s->conf->ctrl_interface);
+	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)
+			*gid_str = '\0';
+	} else
+		dir = pbuf;
+
+	len = os_strlen(dir) + os_strlen(wpa_s->ifname) + 2;
+	buf = os_malloc(len);
+	if (buf == NULL) {
+		os_free(pbuf);
+		return NULL;
+	}
+
+	res = os_snprintf(buf, len, "%s/%s", dir, wpa_s->ifname);
+	if (os_snprintf_error(len, res)) {
+		os_free(pbuf);
+		os_free(buf);
+		return NULL;
+	}
+#ifdef __CYGWIN__
+	{
+		/* Windows/WinPcap uses interface names that are not suitable
+		 * as a file name - convert invalid chars to underscores */
+		char *pos = buf;
+		while (*pos) {
+			if (*pos == '\\')
+				*pos = '_';
+			pos++;
+		}
+	}
+#endif /* __CYGWIN__ */
+	os_free(pbuf);
+	return buf;
+}
+
+
+static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
+					     enum wpa_msg_type type,
+					     const char *txt, size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	if (wpa_s == NULL)
+		return;
+
+	if (type != WPA_MSG_NO_GLOBAL && 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(
+				wpa_s,
+				type != WPA_MSG_PER_INTERFACE ?
+				NULL : wpa_s->ifname,
+				priv->sock, &priv->ctrl_dst, level, txt, len,
+				NULL, priv);
+		}
+	}
+
+	if (type == WPA_MSG_ONLY_GLOBAL || wpa_s->ctrl_iface == NULL)
+		return;
+	wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock,
+				       &wpa_s->ctrl_iface->ctrl_dst,
+				       level, txt, len, wpa_s->ctrl_iface,
+				       NULL);
+}
+
+
+static int wpas_ctrl_iface_open_sock(struct wpa_supplicant *wpa_s,
+				     struct ctrl_iface_priv *priv)
+{
+	struct sockaddr_un addr;
+	char *fname = NULL;
+	gid_t gid = 0;
+	int gid_set = 0;
+	char *buf, *dir = NULL, *gid_str = NULL;
+	struct group *grp;
+	char *endp;
+	int flags;
+
+	buf = os_strdup(wpa_s->conf->ctrl_interface);
+	if (buf == NULL)
+		goto fail;
+#ifdef ANDROID
+	os_snprintf(addr.sun_path, sizeof(addr.sun_path), "wpa_%s",
+		    wpa_s->conf->ctrl_interface);
+	priv->sock = android_get_control_socket(addr.sun_path);
+	if (priv->sock >= 0) {
+		priv->android_control_socket = 1;
+		goto havesock;
+	}
+#endif /* ANDROID */
+	if (os_strncmp(buf, "DIR=", 4) == 0) {
+		dir = buf + 4;
+		gid_str = os_strstr(dir, " GROUP=");
+		if (gid_str) {
+			*gid_str = '\0';
+			gid_str += 7;
+		}
+	} else {
+		dir = buf;
+		gid_str = wpa_s->conf->ctrl_interface_group;
+	}
+
+	if (mkdir(dir, S_IRWXU | S_IRWXG) < 0) {
+		if (errno == EEXIST) {
+			wpa_printf(MSG_DEBUG, "Using existing control "
+				   "interface directory.");
+		} else {
+			wpa_printf(MSG_ERROR, "mkdir[ctrl_interface=%s]: %s",
+				   dir, strerror(errno));
+			goto fail;
+		}
+	}
+
+#ifdef ANDROID
+	/*
+	 * wpa_supplicant is started from /init.*.rc on Android and that seems
+	 * to be using umask 0077 which would leave the control interface
+	 * directory without group access. This breaks things since Wi-Fi
+	 * framework assumes that this directory can be accessed by other
+	 * applications in the wifi group. Fix this by adding group access even
+	 * if umask value would prevent this.
+	 */
+	if (chmod(dir, S_IRWXU | S_IRWXG) < 0) {
+		wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s",
+			   strerror(errno));
+		/* Try to continue anyway */
+	}
+#endif /* ANDROID */
+
+	if (gid_str) {
+		grp = getgrnam(gid_str);
+		if (grp) {
+			gid = grp->gr_gid;
+			gid_set = 1;
+			wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
+				   " (from group name '%s')",
+				   (int) gid, gid_str);
+		} else {
+			/* Group name not found - try to parse this as gid */
+			gid = strtol(gid_str, &endp, 10);
+			if (*gid_str == '\0' || *endp != '\0') {
+				wpa_printf(MSG_ERROR, "CTRL: Invalid group "
+					   "'%s'", gid_str);
+				goto fail;
+			}
+			gid_set = 1;
+			wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
+				   (int) gid);
+		}
+	}
+
+	if (gid_set && chown(dir, -1, gid) < 0) {
+		wpa_printf(MSG_ERROR, "chown[ctrl_interface=%s,gid=%d]: %s",
+			   dir, (int) gid, strerror(errno));
+		goto fail;
+	}
+
+	/* Make sure the group can enter and read the directory */
+	if (gid_set &&
+	    chmod(dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP) < 0) {
+		wpa_printf(MSG_ERROR, "CTRL: chmod[ctrl_interface]: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	if (os_strlen(dir) + 1 + os_strlen(wpa_s->ifname) >=
+	    sizeof(addr.sun_path)) {
+		wpa_printf(MSG_ERROR, "ctrl_iface path limit exceeded");
+		goto fail;
+	}
+
+	priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (priv->sock < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
+		goto fail;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+	addr.sun_len = sizeof(addr);
+#endif /* __FreeBSD__ */
+	addr.sun_family = AF_UNIX;
+	fname = wpa_supplicant_ctrl_iface_path(wpa_s);
+	if (fname == NULL)
+		goto fail;
+	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
+	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
+			   strerror(errno));
+		if (connect(priv->sock, (struct sockaddr *) &addr,
+			    sizeof(addr)) < 0) {
+			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
+				   " allow connections - assuming it was left"
+				   "over from forced program termination");
+			if (unlink(fname) < 0) {
+				wpa_printf(MSG_ERROR,
+					   "Could not unlink existing ctrl_iface socket '%s': %s",
+					   fname, strerror(errno));
+				goto fail;
+			}
+			if (bind(priv->sock, (struct sockaddr *) &addr,
+				 sizeof(addr)) < 0) {
+				wpa_printf(MSG_ERROR, "supp-ctrl-iface-init: bind(PF_UNIX): %s",
+					   strerror(errno));
+				goto fail;
+			}
+			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
+				   "ctrl_iface socket '%s'", fname);
+		} else {
+			wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
+				   "be in use - cannot override it");
+			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
+				   "not used anymore", fname);
+			os_free(fname);
+			fname = NULL;
+			goto fail;
+		}
+	}
+
+	if (gid_set && chown(fname, -1, gid) < 0) {
+		wpa_printf(MSG_ERROR, "chown[ctrl_interface=%s,gid=%d]: %s",
+			   fname, (int) gid, strerror(errno));
+		goto fail;
+	}
+
+	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
+		wpa_printf(MSG_ERROR, "chmod[ctrl_interface=%s]: %s",
+			   fname, strerror(errno));
+		goto fail;
+	}
+	os_free(fname);
+
+#ifdef ANDROID
+havesock:
+#endif /* ANDROID */
+
+	/*
+	 * Make socket non-blocking so that we don't hang forever if
+	 * target dies unexpectedly.
+	 */
+	flags = fcntl(priv->sock, F_GETFL);
+	if (flags >= 0) {
+		flags |= O_NONBLOCK;
+		if (fcntl(priv->sock, F_SETFL, flags) < 0) {
+			wpa_printf(MSG_INFO, "fcntl(ctrl, O_NONBLOCK): %s",
+				   strerror(errno));
+			/* Not fatal, continue on.*/
+		}
+	}
+
+	eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
+				 wpa_s, priv);
+	wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
+
+	os_free(buf);
+	return 0;
+
+fail:
+	if (priv->sock >= 0) {
+		close(priv->sock);
+		priv->sock = -1;
+	}
+	if (fname) {
+		unlink(fname);
+		os_free(fname);
+	}
+	os_free(buf);
+	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;
+
+#ifdef ANDROID
+	if (wpa_s->global->params.ctrl_interface) {
+		int same = 0;
+
+		if (wpa_s->global->params.ctrl_interface[0] == '/') {
+			if (os_strcmp(wpa_s->global->params.ctrl_interface,
+				      wpa_s->conf->ctrl_interface) == 0)
+				same = 1;
+		} else if (os_strncmp(wpa_s->global->params.ctrl_interface,
+				      "@android:", 9) == 0 ||
+			   os_strncmp(wpa_s->global->params.ctrl_interface,
+				      "@abstract:", 10) == 0) {
+			char *pos;
+
+			/*
+			 * Currently, Android uses @android:wpa_* as the naming
+			 * convention for the global ctrl interface. This logic
+			 * needs to be revisited if the above naming convention
+			 * is modified.
+			 */
+			pos = os_strchr(wpa_s->global->params.ctrl_interface,
+					'_');
+			if (pos &&
+			    os_strcmp(pos + 1,
+				      wpa_s->conf->ctrl_interface) == 0)
+				same = 1;
+		}
+
+		if (same) {
+			/*
+			 * The invalid configuration combination might be
+			 * possible to hit in an Android OTA upgrade case, so
+			 * instead of refusing to start the wpa_supplicant
+			 * process, do not open the per-interface ctrl_iface
+			 * and continue with the global control interface that
+			 * was set from the command line since the Wi-Fi
+			 * framework will use it for operations.
+			 */
+			wpa_printf(MSG_ERROR,
+				   "global ctrl interface %s matches ctrl interface %s - do not open per-interface ctrl interface",
+				   wpa_s->global->params.ctrl_interface,
+				   wpa_s->conf->ctrl_interface);
+			return priv;
+		}
+	}
+#endif /* ANDROID */
+
+	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;
+
+	/*
+	 * On Android, the control socket being used may be the socket
+	 * that is created when wpa_supplicant is started as a /init.*.rc
+	 * service. Such a socket is maintained as a key-value pair in
+	 * Android's environment. Closing this control socket would leave us
+	 * in a bad state with an invalid socket descriptor.
+	 */
+	if (priv->android_control_socket)
+		return priv->sock;
+
+	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;
+}
+
+
+void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+{
+	struct wpa_ctrl_dst *dst, *prev;
+
+	if (priv->sock > -1) {
+		char *fname;
+		char *buf, *dir = NULL;
+		eloop_unregister_read_sock(priv->sock);
+		if (!dl_list_empty(&priv->ctrl_dst)) {
+			/*
+			 * Wait before closing the control socket if
+			 * there are any attached monitors in order to allow
+			 * them to receive any pending messages.
+			 */
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached "
+				   "monitors to receive messages");
+			os_sleep(0, 100000);
+		}
+		close(priv->sock);
+		priv->sock = -1;
+		fname = wpa_supplicant_ctrl_iface_path(priv->wpa_s);
+		if (fname) {
+			unlink(fname);
+			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)
+				*gid_str = '\0';
+		} else
+			dir = buf;
+
+		if (rmdir(dir) < 0) {
+			if (errno == ENOTEMPTY) {
+				wpa_printf(MSG_DEBUG, "Control interface "
+					   "directory not empty - leaving it "
+					   "behind");
+			} else {
+				wpa_printf(MSG_ERROR,
+					   "rmdir[ctrl_interface=%s]: %s",
+					   dir, strerror(errno));
+			}
+		}
+		os_free(buf);
+	}
+
+free_dst:
+	dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
+			      list)
+		os_free(dst);
+	os_free(priv);
+}
+
+
+/**
+ * wpa_supplicant_ctrl_iface_send - Send a control interface packet to monitors
+ * @ifname: Interface name for global control socket or %NULL
+ * @sock: Local socket fd
+ * @ctrl_dst: List of attached listeners
+ * @level: Priority level of the message
+ * @buf: Message data
+ * @len: Message length
+ *
+ * Send a packet to all monitor programs attached to the control interface.
+ */
+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,
+					   struct ctrl_iface_priv *priv,
+					   struct ctrl_iface_global_priv *gp)
+{
+	struct wpa_ctrl_dst *dst, *next;
+	char levelstr[10];
+	int idx, res;
+	struct msghdr msg;
+	struct iovec io[5];
+
+	if (sock < 0 || dl_list_empty(ctrl_dst))
+		return;
+
+	res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
+	if (os_snprintf_error(sizeof(levelstr), res))
+		return;
+	idx = 0;
+	if (ifname) {
+		io[idx].iov_base = "IFNAME=";
+		io[idx].iov_len = 7;
+		idx++;
+		io[idx].iov_base = (char *) ifname;
+		io[idx].iov_len = os_strlen(ifname);
+		idx++;
+		io[idx].iov_base = " ";
+		io[idx].iov_len = 1;
+		idx++;
+	}
+	io[idx].iov_base = levelstr;
+	io[idx].iov_len = os_strlen(levelstr);
+	idx++;
+	io[idx].iov_base = (char *) buf;
+	io[idx].iov_len = len;
+	idx++;
+	os_memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = io;
+	msg.msg_iovlen = idx;
+
+	dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
+		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;
+		wpas_ctrl_sock_debug("ctrl_sock-sendmsg", sock, buf, len);
+		if (sendmsg(sock, &msg, MSG_DONTWAIT) >= 0) {
+			wpa_printf(MSG_MSGDUMP,
+				   "CTRL_IFACE monitor sent successfully to %s",
+				   addr_txt);
+			dst->errors = 0;
+			continue;
+		}
+
+		_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;
+			}
+		}
+	}
+}
+
+
+void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
+{
+	char buf[256];
+	int res;
+	struct sockaddr_un from;
+	socklen_t fromlen = sizeof(from);
+
+	for (;;) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor to "
+			   "attach", priv->wpa_s->ifname);
+		eloop_wait_for_read_sock(priv->sock);
+
+		res = recvfrom(priv->sock, buf, sizeof(buf) - 1, 0,
+			       (struct sockaddr *) &from, &fromlen);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+				   strerror(errno));
+			continue;
+		}
+		buf[res] = '\0';
+
+		if (os_strcmp(buf, "ATTACH") == 0) {
+			/* handle ATTACH signal of first monitor interface */
+			if (!wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst,
+							      &from, fromlen,
+							      0)) {
+				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 {
+				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 */
+			if (sendto(priv->sock, "FAIL\n", 5, 0,
+				   (struct sockaddr *) &from, fromlen) < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "ctrl_iface sendto failed: %s",
+					   strerror(errno));
+			}
+		}
+	}
+}
+
+
+/* Global ctrl_iface */
+
+static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
+						     void *sock_ctx)
+{
+	struct wpa_global *global = eloop_ctx;
+	struct ctrl_iface_global_priv *priv = sock_ctx;
+	char buf[4096];
+	int res;
+	struct sockaddr_un from;
+	socklen_t fromlen = sizeof(from);
+	char *reply = NULL, *reply_buf = NULL;
+	size_t reply_len;
+
+	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+		       (struct sockaddr *) &from, &fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+			   strerror(errno));
+		return;
+	}
+	buf[res] = '\0';
+
+	if (os_strcmp(buf, "ATTACH") == 0) {
+		if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from,
+						     fromlen, 1))
+			reply_len = 1;
+		else
+			reply_len = 2;
+	} else if (os_strcmp(buf, "DETACH") == 0) {
+		if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, &from,
+						     fromlen))
+			reply_len = 1;
+		else
+			reply_len = 2;
+	} else {
+		reply_buf = wpa_supplicant_global_ctrl_iface_process(
+			global, buf, &reply_len);
+		reply = reply_buf;
+
+		/*
+		 * There could be some password/key material in the command, so
+		 * clear the buffer explicitly now that it is not needed
+		 * anymore.
+		 */
+		os_memset(buf, 0, res);
+	}
+
+	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) {
+		wpas_ctrl_sock_debug("global_ctrl_sock-sendto",
+				     sock, reply, reply_len);
+		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);
+}
+
+
+static int wpas_global_ctrl_iface_open_sock(struct wpa_global *global,
+					    struct ctrl_iface_global_priv *priv)
+{
+	struct sockaddr_un addr;
+	const char *ctrl = global->params.ctrl_interface;
+	int flags;
+
+	wpa_printf(MSG_DEBUG, "Global control interface '%s'", ctrl);
+
+#ifdef ANDROID
+	if (os_strncmp(ctrl, "@android:", 9) == 0) {
+		priv->sock = android_get_control_socket(ctrl + 9);
+		if (priv->sock < 0) {
+			wpa_printf(MSG_ERROR, "Failed to open Android control "
+				   "socket '%s'", ctrl + 9);
+			goto fail;
+		}
+		wpa_printf(MSG_DEBUG, "Using Android control socket '%s'",
+			   ctrl + 9);
+		priv->android_control_socket = 1;
+		goto havesock;
+	}
+
+	if (os_strncmp(ctrl, "@abstract:", 10) != 0) {
+		/*
+		 * Backwards compatibility - try to open an Android control
+		 * socket and if that fails, assume this was a UNIX domain
+		 * socket instead.
+		 */
+		priv->sock = android_get_control_socket(ctrl);
+		if (priv->sock >= 0) {
+			wpa_printf(MSG_DEBUG,
+				   "Using Android control socket '%s'",
+				   ctrl);
+			priv->android_control_socket = 1;
+			goto havesock;
+		}
+	}
+#endif /* ANDROID */
+
+	priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (priv->sock < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
+		goto fail;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+	addr.sun_len = sizeof(addr);
+#endif /* __FreeBSD__ */
+	addr.sun_family = AF_UNIX;
+
+	if (os_strncmp(ctrl, "@abstract:", 10) == 0) {
+		addr.sun_path[0] = '\0';
+		os_strlcpy(addr.sun_path + 1, ctrl + 10,
+			   sizeof(addr.sun_path) - 1);
+		if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) <
+		    0) {
+			wpa_printf(MSG_ERROR, "supp-global-ctrl-iface-init: "
+				   "bind(PF_UNIX;%s) failed: %s",
+				   ctrl, strerror(errno));
+			goto fail;
+		}
+		wpa_printf(MSG_DEBUG, "Using Abstract control socket '%s'",
+			   ctrl + 10);
+		goto havesock;
+	}
+
+	os_strlcpy(addr.sun_path, ctrl, sizeof(addr.sun_path));
+	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_INFO, "supp-global-ctrl-iface-init(%s) (will try fixup): bind(PF_UNIX): %s",
+			   ctrl, strerror(errno));
+		if (connect(priv->sock, (struct sockaddr *) &addr,
+			    sizeof(addr)) < 0) {
+			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
+				   " allow connections - assuming it was left"
+				   "over from forced program termination");
+			if (unlink(ctrl) < 0) {
+				wpa_printf(MSG_ERROR,
+					   "Could not unlink existing ctrl_iface socket '%s': %s",
+					   ctrl, strerror(errno));
+				goto fail;
+			}
+			if (bind(priv->sock, (struct sockaddr *) &addr,
+				 sizeof(addr)) < 0) {
+				wpa_printf(MSG_ERROR, "supp-glb-iface-init: bind(PF_UNIX;%s): %s",
+					   ctrl, strerror(errno));
+				goto fail;
+			}
+			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
+				   "ctrl_iface socket '%s'",
+				   ctrl);
+		} else {
+			wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
+				   "be in use - cannot override it");
+			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
+				   "not used anymore",
+				   ctrl);
+			goto fail;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "Using UNIX control socket '%s'", ctrl);
+
+	if (global->params.ctrl_interface_group) {
+		char *gid_str = global->params.ctrl_interface_group;
+		gid_t gid = 0;
+		struct group *grp;
+		char *endp;
+
+		grp = getgrnam(gid_str);
+		if (grp) {
+			gid = grp->gr_gid;
+			wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
+				   " (from group name '%s')",
+				   (int) gid, gid_str);
+		} else {
+			/* Group name not found - try to parse this as gid */
+			gid = strtol(gid_str, &endp, 10);
+			if (*gid_str == '\0' || *endp != '\0') {
+				wpa_printf(MSG_ERROR, "CTRL: Invalid group "
+					   "'%s'", gid_str);
+				goto fail;
+			}
+			wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
+				   (int) gid);
+		}
+		if (chown(ctrl, -1, gid) < 0) {
+			wpa_printf(MSG_ERROR,
+				   "chown[global_ctrl_interface=%s,gid=%d]: %s",
+				   ctrl, (int) gid, strerror(errno));
+			goto fail;
+		}
+
+		if (chmod(ctrl, S_IRWXU | S_IRWXG) < 0) {
+			wpa_printf(MSG_ERROR,
+				   "chmod[global_ctrl_interface=%s]: %s",
+				   ctrl, strerror(errno));
+			goto fail;
+		}
+	} else {
+		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:
+
+	/*
+	 * Make socket non-blocking so that we don't hang forever if
+	 * target dies unexpectedly.
+	 */
+	flags = fcntl(priv->sock, F_GETFL);
+	if (flags >= 0) {
+		flags |= O_NONBLOCK;
+		if (fcntl(priv->sock, F_SETFL, flags) < 0) {
+			wpa_printf(MSG_INFO, "fcntl(ctrl, O_NONBLOCK): %s",
+				   strerror(errno));
+			/* Not fatal, continue on.*/
+		}
+	}
+
+	eloop_register_read_sock(priv->sock,
+				 wpa_supplicant_global_ctrl_iface_receive,
+				 global, priv);
+
+	return 0;
+
+fail:
+	if (priv->sock >= 0) {
+		close(priv->sock);
+		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;
+
+	/*
+	 * On Android, the control socket being used may be the socket
+	 * that is created when wpa_supplicant is started as a /init.*.rc
+	 * service. Such a socket is maintained as a key-value pair in
+	 * Android's environment. Closing this control socket would leave us
+	 * in a bad state with an invalid socket descriptor.
+	 */
+	if (priv->android_control_socket)
+		return priv->sock;
+
+	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;
+}
+
+
+void
+wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
+{
+	struct wpa_ctrl_dst *dst, *prev;
+
+	if (priv->sock >= 0) {
+		eloop_unregister_read_sock(priv->sock);
+		close(priv->sock);
+	}
+	if (priv->global->params.ctrl_interface)
+		unlink(priv->global->params.ctrl_interface);
+	dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
+			      list)
+		os_free(dst);
+	os_free(priv);
+}
diff --git a/hostap/wpa_supplicant/dbus/Makefile b/hostap/wpa_supplicant/dbus/Makefile
new file mode 100644
index 0000000..f355ebe
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/Makefile
@@ -0,0 +1,73 @@
+all: libwpadbus.a
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
+	rm -f libwpadbus.a
+
+install:
+	@echo Nothing to be made.
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+PKG_CONFIG ?= pkg-config
+CFLAGS += -I../../src -I../../src/utils
+
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+
+%.o: %.c
+	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
+	@$(E) "  CC " $<
+
+
+ifdef CONFIG_WPS
+CFLAGS += -DCONFIG_WPS
+endif
+
+CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW
+CFLAGS += -DCONFIG_CTRL_IFACE_DBUS
+
+ifndef DBUS_LIBS
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
+endif
+ifndef DBUS_INCLUDE
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
+endif
+ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
+CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
+DBUS_INCLUDE += $(shell xml2-config --cflags)
+DBUS_LIBS += $(shell xml2-config --libs)
+endif
+
+CFLAGS += $(DBUS_INCLUDE)
+
+LIB_OBJS= \
+	dbus_common.o \
+	dbus_old.o \
+	dbus_old_handlers.o \
+	dbus_new.o \
+	dbus_new_handlers.o \
+	dbus_new_helpers.o \
+	dbus_new_introspect.o \
+	dbus_dict_helpers.o
+
+ifdef CONFIG_WPS
+LIB_OBJS += dbus_old_handlers_wps.o
+LIB_OBJS += dbus_new_handlers_wps.o
+endif
+
+libwpadbus.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/hostap/wpa_supplicant/dbus/dbus-wpa_supplicant.conf b/hostap/wpa_supplicant/dbus/dbus-wpa_supplicant.conf
new file mode 100644
index 0000000..c091234
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus-wpa_supplicant.conf
@@ -0,0 +1,27 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+        <policy user="root">
+                <allow own="fi.epitest.hostap.WPASupplicant"/>
+
+                <allow send_destination="fi.epitest.hostap.WPASupplicant"/>
+                <allow send_interface="fi.epitest.hostap.WPASupplicant"/>
+
+                <allow own="fi.w1.wpa_supplicant1"/>
+
+                <allow send_destination="fi.w1.wpa_supplicant1"/>
+                <allow send_interface="fi.w1.wpa_supplicant1"/>
+                <allow receive_sender="fi.w1.wpa_supplicant1" receive_type="signal"/>
+        </policy>
+        <policy context="default">
+                <deny own="fi.epitest.hostap.WPASupplicant"/>
+                <deny send_destination="fi.epitest.hostap.WPASupplicant"/>
+                <deny send_interface="fi.epitest.hostap.WPASupplicant"/>
+
+                <deny own="fi.w1.wpa_supplicant1"/>
+                <deny send_destination="fi.w1.wpa_supplicant1"/>
+                <deny send_interface="fi.w1.wpa_supplicant1"/>
+                <deny receive_sender="fi.w1.wpa_supplicant1" receive_type="signal"/>
+        </policy>
+</busconfig>
diff --git a/hostap/wpa_supplicant/dbus/dbus_common.c b/hostap/wpa_supplicant/dbus/dbus_common.c
new file mode 100644
index 0000000..7ef6cad
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_common.c
@@ -0,0 +1,380 @@
+/*
+ * wpa_supplicant D-Bus control interface - common functionality
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ * 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 "utils/includes.h"
+#include <dbus/dbus.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "dbus_common.h"
+#include "dbus_common_i.h"
+#include "dbus_new.h"
+#include "dbus_old.h"
+#include "../wpa_supplicant_i.h"
+
+
+#ifndef SIGPOLL
+#ifdef SIGIO
+/*
+ * If we do not have SIGPOLL, try to use SIGIO instead. This is needed for
+ * FreeBSD.
+ */
+#define SIGPOLL SIGIO
+#endif
+#endif
+
+
+static void dispatch_data(DBusConnection *con)
+{
+	while (dbus_connection_get_dispatch_status(con) ==
+	       DBUS_DISPATCH_DATA_REMAINS)
+		dbus_connection_dispatch(con);
+}
+
+
+/**
+ * dispatch_initial_dbus_messages - Dispatch initial dbus messages after
+ *     claiming bus name
+ * @eloop_ctx: the DBusConnection to dispatch on
+ * @timeout_ctx: unused
+ *
+ * If clients are quick to notice that service claimed its bus name,
+ * there may have been messages that came in before initialization was
+ * all finished.  Dispatch those here.
+ */
+static void dispatch_initial_dbus_messages(void *eloop_ctx, void *timeout_ctx)
+{
+	DBusConnection *con = eloop_ctx;
+	dispatch_data(con);
+}
+
+
+static void process_watch(struct wpas_dbus_priv *priv,
+			  DBusWatch *watch, eloop_event_type type)
+{
+	dbus_connection_ref(priv->con);
+
+	priv->should_dispatch = 0;
+
+	if (type == EVENT_TYPE_READ)
+		dbus_watch_handle(watch, DBUS_WATCH_READABLE);
+	else if (type == EVENT_TYPE_WRITE)
+		dbus_watch_handle(watch, DBUS_WATCH_WRITABLE);
+	else if (type == EVENT_TYPE_EXCEPTION)
+		dbus_watch_handle(watch, DBUS_WATCH_ERROR);
+
+	if (priv->should_dispatch) {
+		dispatch_data(priv->con);
+		priv->should_dispatch = 0;
+	}
+
+	dbus_connection_unref(priv->con);
+}
+
+
+static void process_watch_exception(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_EXCEPTION);
+}
+
+
+static void process_watch_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_READ);
+}
+
+
+static void process_watch_write(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_WRITE);
+}
+
+
+static dbus_bool_t add_watch(DBusWatch *watch, void *data)
+{
+	struct wpas_dbus_priv *priv = data;
+	unsigned int flags;
+	int fd;
+
+	if (!dbus_watch_get_enabled(watch))
+		return TRUE;
+
+	flags = dbus_watch_get_flags(watch);
+	fd = dbus_watch_get_unix_fd(watch);
+
+	eloop_register_sock(fd, EVENT_TYPE_EXCEPTION, process_watch_exception,
+			    priv, watch);
+
+	if (flags & DBUS_WATCH_READABLE) {
+		eloop_register_sock(fd, EVENT_TYPE_READ, process_watch_read,
+				    priv, watch);
+	}
+	if (flags & DBUS_WATCH_WRITABLE) {
+		eloop_register_sock(fd, EVENT_TYPE_WRITE, process_watch_write,
+				    priv, watch);
+	}
+
+	dbus_watch_set_data(watch, priv, NULL);
+
+	return TRUE;
+}
+
+
+static void remove_watch(DBusWatch *watch, void *data)
+{
+	unsigned int flags;
+	int fd;
+
+	flags = dbus_watch_get_flags(watch);
+	fd = dbus_watch_get_unix_fd(watch);
+
+	eloop_unregister_sock(fd, EVENT_TYPE_EXCEPTION);
+
+	if (flags & DBUS_WATCH_READABLE)
+		eloop_unregister_sock(fd, EVENT_TYPE_READ);
+	if (flags & DBUS_WATCH_WRITABLE)
+		eloop_unregister_sock(fd, EVENT_TYPE_WRITE);
+
+	dbus_watch_set_data(watch, NULL, NULL);
+}
+
+
+static void watch_toggled(DBusWatch *watch, void *data)
+{
+	if (dbus_watch_get_enabled(watch))
+		add_watch(watch, data);
+	else
+		remove_watch(watch, data);
+}
+
+
+static void process_timeout(void *eloop_ctx, void *sock_ctx)
+{
+	DBusTimeout *timeout = sock_ctx;
+	dbus_timeout_handle(timeout);
+}
+
+
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
+{
+	struct wpas_dbus_priv *priv = data;
+
+	if (!dbus_timeout_get_enabled(timeout))
+		return TRUE;
+
+	eloop_register_timeout(0, dbus_timeout_get_interval(timeout) * 1000,
+			       process_timeout, priv, timeout);
+
+	dbus_timeout_set_data(timeout, priv, NULL);
+
+	return TRUE;
+}
+
+
+static void remove_timeout(DBusTimeout *timeout, void *data)
+{
+	struct wpas_dbus_priv *priv = data;
+
+	eloop_cancel_timeout(process_timeout, priv, timeout);
+	dbus_timeout_set_data(timeout, NULL, NULL);
+}
+
+
+static void timeout_toggled(DBusTimeout *timeout, void *data)
+{
+	if (dbus_timeout_get_enabled(timeout))
+		add_timeout(timeout, data);
+	else
+		remove_timeout(timeout, data);
+}
+
+
+static void process_wakeup_main(int sig, void *signal_ctx)
+{
+	struct wpas_dbus_priv *priv = signal_ctx;
+
+	if (sig != SIGPOLL || !priv->con)
+		return;
+
+	if (dbus_connection_get_dispatch_status(priv->con) !=
+	    DBUS_DISPATCH_DATA_REMAINS)
+		return;
+
+	/* Only dispatch once - we do not want to starve other events */
+	dbus_connection_ref(priv->con);
+	dbus_connection_dispatch(priv->con);
+	dbus_connection_unref(priv->con);
+}
+
+
+/**
+ * wakeup_main - Attempt to wake our mainloop up
+ * @data: dbus control interface private data
+ *
+ * Try to wake up the main eloop so it will process
+ * dbus events that may have happened.
+ */
+static void wakeup_main(void *data)
+{
+	struct wpas_dbus_priv *priv = data;
+
+	/* Use SIGPOLL to break out of the eloop select() */
+	raise(SIGPOLL);
+	priv->should_dispatch = 1;
+}
+
+
+/**
+ * integrate_with_eloop - Register our mainloop integration with dbus
+ * @connection: connection to the system message bus
+ * @priv: a dbus control interface data structure
+ * Returns: 0 on success, -1 on failure
+ */
+static int integrate_with_eloop(struct wpas_dbus_priv *priv)
+{
+	if (!dbus_connection_set_watch_functions(priv->con, add_watch,
+						 remove_watch, watch_toggled,
+						 priv, NULL) ||
+	    !dbus_connection_set_timeout_functions(priv->con, add_timeout,
+						   remove_timeout,
+						   timeout_toggled, priv,
+						   NULL)) {
+		wpa_printf(MSG_ERROR, "dbus: Failed to set callback functions");
+		return -1;
+	}
+
+	if (eloop_register_signal(SIGPOLL, process_wakeup_main, priv))
+		return -1;
+	dbus_connection_set_wakeup_main_function(priv->con, wakeup_main,
+						 priv, NULL);
+
+	return 0;
+}
+
+
+static DBusHandlerResult disconnect_filter(DBusConnection *conn,
+					   DBusMessage *message, void *data)
+{
+	struct wpas_dbus_priv *priv = data;
+
+	if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL,
+				   "Disconnected")) {
+		wpa_printf(MSG_DEBUG, "dbus: bus disconnected, terminating");
+		dbus_connection_set_exit_on_disconnect(conn, FALSE);
+		wpa_supplicant_terminate_proc(priv->global);
+		return DBUS_HANDLER_RESULT_HANDLED;
+	} else
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static int wpas_dbus_init_common(struct wpas_dbus_priv *priv)
+{
+	DBusError error;
+	int ret = 0;
+
+	/* Get a reference to the system bus */
+	dbus_error_init(&error);
+	priv->con = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	if (priv->con) {
+		dbus_connection_add_filter(priv->con, disconnect_filter, priv,
+					   NULL);
+	} else {
+		wpa_printf(MSG_ERROR,
+			   "dbus: Could not acquire the system bus: %s - %s",
+			   error.name, error.message);
+		ret = -1;
+	}
+	dbus_error_free(&error);
+
+	return ret;
+}
+
+
+static int wpas_dbus_init_common_finish(struct wpas_dbus_priv *priv)
+{
+	/* Tell dbus about our mainloop integration functions */
+	integrate_with_eloop(priv);
+
+	/*
+	 * Dispatch initial DBus messages that may have come in since the bus
+	 * name was claimed above. Happens when clients are quick to notice the
+	 * service.
+	 *
+	 * FIXME: is there a better solution to this problem?
+	 */
+	eloop_register_timeout(0, 50, dispatch_initial_dbus_messages,
+			       priv->con, NULL);
+
+	return 0;
+}
+
+
+static void wpas_dbus_deinit_common(struct wpas_dbus_priv *priv)
+{
+	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,
+						      NULL, NULL, NULL);
+		dbus_connection_remove_filter(priv->con, disconnect_filter,
+					      priv);
+
+		dbus_connection_unref(priv->con);
+	}
+
+	os_free(priv);
+}
+
+
+struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global)
+{
+	struct wpas_dbus_priv *priv;
+
+	priv = os_zalloc(sizeof(*priv));
+	if (priv == NULL)
+		return NULL;
+	priv->global = global;
+
+	if (wpas_dbus_init_common(priv) < 0 ||
+#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+	    wpas_dbus_ctrl_iface_init(priv) < 0 ||
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+#ifdef CONFIG_CTRL_IFACE_DBUS
+	    wpa_supplicant_dbus_ctrl_iface_init(priv) < 0 ||
+#endif /* CONFIG_CTRL_IFACE_DBUS */
+	    wpas_dbus_init_common_finish(priv) < 0) {
+		wpas_dbus_deinit(priv);
+		return NULL;
+	}
+
+	return priv;
+}
+
+
+void wpas_dbus_deinit(struct wpas_dbus_priv *priv)
+{
+	if (priv == NULL)
+		return;
+
+#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+	wpas_dbus_ctrl_iface_deinit(priv);
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+
+#ifdef CONFIG_CTRL_IFACE_DBUS
+	/* TODO: is any deinit needed? */
+#endif /* CONFIG_CTRL_IFACE_DBUS */
+
+	wpas_dbus_deinit_common(priv);
+}
diff --git a/hostap/wpa_supplicant/dbus/dbus_common.h b/hostap/wpa_supplicant/dbus/dbus_common.h
new file mode 100644
index 0000000..aea7db7
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_common.h
@@ -0,0 +1,20 @@
+/*
+ * wpa_supplicant D-Bus control interface - common definitions
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ * 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.
+ */
+
+#ifndef DBUS_COMMON_H
+#define DBUS_COMMON_H
+
+struct wpas_dbus_priv;
+struct wpa_global;
+
+struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global);
+void wpas_dbus_deinit(struct wpas_dbus_priv *priv);
+
+#endif /* DBUS_COMMON_H */
diff --git a/hostap/wpa_supplicant/dbus/dbus_common_i.h b/hostap/wpa_supplicant/dbus/dbus_common_i.h
new file mode 100644
index 0000000..a551ccd
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_common_i.h
@@ -0,0 +1,28 @@
+/*
+ * wpa_supplicant D-Bus control interface - internal definitions
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ * 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.
+ */
+
+#ifndef DBUS_COMMON_I_H
+#define DBUS_COMMON_I_H
+
+#include <dbus/dbus.h>
+
+struct wpas_dbus_priv {
+	DBusConnection *con;
+	int should_dispatch;
+	struct wpa_global *global;
+	u32 next_objid;
+	int dbus_new_initialized;
+
+#if defined(CONFIG_CTRL_IFACE_DBUS_NEW) && defined(CONFIG_AP)
+	int dbus_noc_refcnt;
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW && CONFIG_AP */
+};
+
+#endif /* DBUS_COMMON_I_H */
diff --git a/hostap/wpa_supplicant/dbus/dbus_dict_helpers.c b/hostap/wpa_supplicant/dbus/dbus_dict_helpers.c
new file mode 100644
index 0000000..a0c44eb
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -0,0 +1,1135 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <dbus/dbus.h>
+
+#include "common.h"
+#include "wpabuf.h"
+#include "dbus_dict_helpers.h"
+
+
+/**
+ * Start a dict in a dbus message.  Should be paired with a call to
+ * wpa_dbus_dict_close_write().
+ *
+ * @param iter A valid dbus message iterator
+ * @param iter_dict (out) A dict iterator to pass to further dict functions
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_open_write(DBusMessageIter *iter,
+				     DBusMessageIter *iter_dict)
+{
+	dbus_bool_t result;
+
+	if (!iter || !iter_dict)
+		return FALSE;
+
+	result = dbus_message_iter_open_container(
+		iter,
+		DBUS_TYPE_ARRAY,
+		DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+		DBUS_TYPE_STRING_AS_STRING
+		DBUS_TYPE_VARIANT_AS_STRING
+		DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+		iter_dict);
+	return result;
+}
+
+
+/**
+ * End a dict element in a dbus message.  Should be paired with
+ * a call to wpa_dbus_dict_open_write().
+ *
+ * @param iter valid dbus message iterator, same as passed to
+ *    wpa_dbus_dict_open_write()
+ * @param iter_dict a dbus dict iterator returned from
+ *    wpa_dbus_dict_open_write()
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_close_write(DBusMessageIter *iter,
+				      DBusMessageIter *iter_dict)
+{
+	if (!iter || !iter_dict)
+		return FALSE;
+
+	return dbus_message_iter_close_container(iter, iter_dict);
+}
+
+
+const char * wpa_dbus_type_as_string(const int type)
+{
+	switch (type) {
+	case DBUS_TYPE_BYTE:
+		return DBUS_TYPE_BYTE_AS_STRING;
+	case DBUS_TYPE_BOOLEAN:
+		return DBUS_TYPE_BOOLEAN_AS_STRING;
+	case DBUS_TYPE_INT16:
+		return DBUS_TYPE_INT16_AS_STRING;
+	case DBUS_TYPE_UINT16:
+		return DBUS_TYPE_UINT16_AS_STRING;
+	case DBUS_TYPE_INT32:
+		return DBUS_TYPE_INT32_AS_STRING;
+	case DBUS_TYPE_UINT32:
+		return DBUS_TYPE_UINT32_AS_STRING;
+	case DBUS_TYPE_INT64:
+		return DBUS_TYPE_INT64_AS_STRING;
+	case DBUS_TYPE_UINT64:
+		return DBUS_TYPE_UINT64_AS_STRING;
+	case DBUS_TYPE_DOUBLE:
+		return DBUS_TYPE_DOUBLE_AS_STRING;
+	case DBUS_TYPE_STRING:
+		return DBUS_TYPE_STRING_AS_STRING;
+	case DBUS_TYPE_OBJECT_PATH:
+		return DBUS_TYPE_OBJECT_PATH_AS_STRING;
+	case DBUS_TYPE_ARRAY:
+		return DBUS_TYPE_ARRAY_AS_STRING;
+	default:
+		return NULL;
+	}
+}
+
+
+static dbus_bool_t _wpa_dbus_add_dict_entry_start(
+	DBusMessageIter *iter_dict, DBusMessageIter *iter_dict_entry,
+	const char *key, const int value_type)
+{
+	if (!dbus_message_iter_open_container(iter_dict,
+					      DBUS_TYPE_DICT_ENTRY, NULL,
+					      iter_dict_entry))
+		return FALSE;
+
+	return dbus_message_iter_append_basic(iter_dict_entry, DBUS_TYPE_STRING,
+					      &key);
+}
+
+
+static dbus_bool_t _wpa_dbus_add_dict_entry_end(
+	DBusMessageIter *iter_dict, DBusMessageIter *iter_dict_entry,
+	DBusMessageIter *iter_dict_val)
+{
+	if (!dbus_message_iter_close_container(iter_dict_entry, iter_dict_val))
+		return FALSE;
+
+	return dbus_message_iter_close_container(iter_dict, iter_dict_entry);
+}
+
+
+static dbus_bool_t _wpa_dbus_add_dict_entry_basic(DBusMessageIter *iter_dict,
+						  const char *key,
+						  const int value_type,
+						  const void *value)
+{
+	DBusMessageIter iter_dict_entry, iter_dict_val;
+	const char *type_as_string = NULL;
+
+	if (key == NULL)
+		return FALSE;
+
+	type_as_string = wpa_dbus_type_as_string(value_type);
+	if (!type_as_string)
+		return FALSE;
+
+	if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry,
+					    key, value_type) ||
+	    !dbus_message_iter_open_container(&iter_dict_entry,
+					      DBUS_TYPE_VARIANT,
+					      type_as_string, &iter_dict_val) ||
+	    !dbus_message_iter_append_basic(&iter_dict_val, value_type, value))
+		return FALSE;
+
+	return _wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry,
+					    &iter_dict_val);
+}
+
+
+static dbus_bool_t _wpa_dbus_add_dict_entry_byte_array(
+	DBusMessageIter *iter_dict, const char *key,
+	const char *value, const dbus_uint32_t value_len)
+{
+	DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
+	dbus_uint32_t i;
+
+	if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry,
+					    key, DBUS_TYPE_ARRAY) ||
+	    !dbus_message_iter_open_container(&iter_dict_entry,
+					      DBUS_TYPE_VARIANT,
+					      DBUS_TYPE_ARRAY_AS_STRING
+					      DBUS_TYPE_BYTE_AS_STRING,
+					      &iter_dict_val) ||
+	    !dbus_message_iter_open_container(&iter_dict_val, DBUS_TYPE_ARRAY,
+					      DBUS_TYPE_BYTE_AS_STRING,
+					      &iter_array))
+		return FALSE;
+
+	for (i = 0; i < value_len; i++) {
+		if (!dbus_message_iter_append_basic(&iter_array,
+						    DBUS_TYPE_BYTE,
+						    &(value[i])))
+			return FALSE;
+	}
+
+	if (!dbus_message_iter_close_container(&iter_dict_val, &iter_array))
+		return FALSE;
+
+	return _wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry,
+					    &iter_dict_val);
+}
+
+
+/**
+ * Add a string entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The string value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_string(DBusMessageIter *iter_dict,
+					const char *key, const char *value)
+{
+	if (!value)
+		return FALSE;
+	return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_STRING,
+					      &value);
+}
+
+
+/**
+ * Add a byte entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The byte value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict,
+				      const char *key, const char value)
+{
+	return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_BYTE,
+					      &value);
+}
+
+
+/**
+ * Add a boolean entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The boolean value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_bool(DBusMessageIter *iter_dict,
+				      const char *key, const dbus_bool_t value)
+{
+	return _wpa_dbus_add_dict_entry_basic(iter_dict, key,
+					      DBUS_TYPE_BOOLEAN, &value);
+}
+
+
+/**
+ * Add a 16-bit signed integer entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 16-bit signed integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_int16(DBusMessageIter *iter_dict,
+				       const char *key,
+				       const dbus_int16_t value)
+{
+	return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT16,
+					      &value);
+}
+
+
+/**
+ * Add a 16-bit unsigned integer entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 16-bit unsigned integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_uint16(DBusMessageIter *iter_dict,
+					const char *key,
+					const dbus_uint16_t value)
+{
+	return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT16,
+					      &value);
+}
+
+
+/**
+ * Add a 32-bit signed integer to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 32-bit signed integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_int32(DBusMessageIter *iter_dict,
+				       const char *key,
+				       const dbus_int32_t value)
+{
+	return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT32,
+					      &value);
+}
+
+
+/**
+ * Add a 32-bit unsigned integer entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 32-bit unsigned integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_uint32(DBusMessageIter *iter_dict,
+					const char *key,
+					const dbus_uint32_t value)
+{
+	return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT32,
+					      &value);
+}
+
+
+/**
+ * Add a 64-bit integer entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 64-bit integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict,
+				       const char *key,
+				       const dbus_int64_t value)
+{
+	return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT64,
+					      &value);
+}
+
+
+/**
+ * Add a 64-bit unsigned integer entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 64-bit unsigned integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict,
+					const char *key,
+					const dbus_uint64_t value)
+{
+	return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT64,
+					      &value);
+}
+
+
+/**
+ * Add a double-precision floating point entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The double-precision floating point value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict,
+					const char *key, const double value)
+{
+	return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_DOUBLE,
+					      &value);
+}
+
+
+/**
+ * Add a DBus object path entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The DBus object path value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict,
+					     const char *key,
+					     const char *value)
+{
+	if (!value)
+		return FALSE;
+	return _wpa_dbus_add_dict_entry_basic(iter_dict, key,
+					      DBUS_TYPE_OBJECT_PATH, &value);
+}
+
+
+/**
+ * Add a byte array entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The byte array
+ * @param value_len The length of the byte array, in bytes
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict,
+					    const char *key,
+					    const char *value,
+					    const dbus_uint32_t value_len)
+{
+	if (!key || (!value && value_len != 0))
+		return FALSE;
+	return _wpa_dbus_add_dict_entry_byte_array(iter_dict, key, value,
+						   value_len);
+}
+
+
+/**
+ * Begin an array entry in the dict
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *                  wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param type The type of the contained data
+ * @param iter_dict_entry A private DBusMessageIter provided by the caller to
+ *                        be passed to wpa_dbus_dict_end_string_array()
+ * @param iter_dict_val A private DBusMessageIter provided by the caller to
+ *                      be passed to wpa_dbus_dict_end_string_array()
+ * @param iter_array On return, the DBusMessageIter to be passed to
+ *                   wpa_dbus_dict_string_array_add_element()
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict,
+				      const char *key, const char *type,
+				      DBusMessageIter *iter_dict_entry,
+				      DBusMessageIter *iter_dict_val,
+				      DBusMessageIter *iter_array)
+{
+	char array_type[10];
+	int err;
+
+	err = os_snprintf(array_type, sizeof(array_type),
+			  DBUS_TYPE_ARRAY_AS_STRING "%s",
+			  type);
+	if (os_snprintf_error(sizeof(array_type), err))
+		return FALSE;
+
+	if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array ||
+	    !_wpa_dbus_add_dict_entry_start(iter_dict, iter_dict_entry,
+					    key, DBUS_TYPE_ARRAY) ||
+	    !dbus_message_iter_open_container(iter_dict_entry,
+					      DBUS_TYPE_VARIANT,
+					      array_type,
+					      iter_dict_val))
+		return FALSE;
+
+	return dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY,
+						type, iter_array);
+}
+
+
+dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
+					     const char *key,
+					     DBusMessageIter *iter_dict_entry,
+					     DBusMessageIter *iter_dict_val,
+					     DBusMessageIter *iter_array)
+{
+	return wpa_dbus_dict_begin_array(
+		iter_dict, key,
+		DBUS_TYPE_STRING_AS_STRING,
+		iter_dict_entry, iter_dict_val, iter_array);
+}
+
+
+/**
+ * Add a single string element to a string array dict entry
+ *
+ * @param iter_array A valid DBusMessageIter returned from
+ *                   wpa_dbus_dict_begin_string_array()'s
+ *                   iter_array parameter
+ * @param elem The string element to be added to the dict entry's string array
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array,
+						   const char *elem)
+{
+	if (!iter_array || !elem)
+		return FALSE;
+
+	return dbus_message_iter_append_basic(iter_array, DBUS_TYPE_STRING,
+					      &elem);
+}
+
+
+/**
+ * Add a single byte array element to a string array dict entry
+ *
+ * @param iter_array A valid DBusMessageIter returned from
+ *                   wpa_dbus_dict_begin_array()'s iter_array
+ *                   parameter -- note that wpa_dbus_dict_begin_array()
+ *                   must have been called with "ay" as the type
+ * @param value The data to be added to the dict entry's array
+ * @param value_len The length of the data
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array,
+						const u8 *value,
+						size_t value_len)
+{
+	DBusMessageIter iter_bytes;
+	size_t i;
+
+	if (!iter_array || !value ||
+	    !dbus_message_iter_open_container(iter_array, DBUS_TYPE_ARRAY,
+					      DBUS_TYPE_BYTE_AS_STRING,
+					      &iter_bytes))
+		return FALSE;
+
+	for (i = 0; i < value_len; i++) {
+		if (!dbus_message_iter_append_basic(&iter_bytes,
+						    DBUS_TYPE_BYTE,
+						    &(value[i])))
+			return FALSE;
+	}
+
+	return dbus_message_iter_close_container(iter_array, &iter_bytes);
+}
+
+
+/**
+ * End an array dict entry
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *                  wpa_dbus_dict_open_write()
+ * @param iter_dict_entry A private DBusMessageIter returned from
+ *                        wpa_dbus_dict_begin_string_array() or
+ *			  wpa_dbus_dict_begin_array()
+ * @param iter_dict_val A private DBusMessageIter returned from
+ *                      wpa_dbus_dict_begin_string_array() or
+ *			wpa_dbus_dict_begin_array()
+ * @param iter_array A DBusMessageIter returned from
+ *                   wpa_dbus_dict_begin_string_array() or
+ *		     wpa_dbus_dict_begin_array()
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict,
+				    DBusMessageIter *iter_dict_entry,
+				    DBusMessageIter *iter_dict_val,
+				    DBusMessageIter *iter_array)
+{
+	if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array ||
+	    !dbus_message_iter_close_container(iter_dict_val, iter_array))
+		return FALSE;
+
+	return _wpa_dbus_add_dict_entry_end(iter_dict, iter_dict_entry,
+					    iter_dict_val);
+}
+
+
+/**
+ * Convenience function to add an entire string array to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *                  wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param items The array of strings
+ * @param num_items The number of strings in the array
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
+					      const char *key,
+					      const char **items,
+					      const dbus_uint32_t num_items)
+{
+	DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
+	dbus_uint32_t i;
+
+	if (!key || (!items && num_items != 0) ||
+	    !wpa_dbus_dict_begin_string_array(iter_dict, key,
+					      &iter_dict_entry, &iter_dict_val,
+					      &iter_array))
+		return FALSE;
+
+	for (i = 0; i < num_items; i++) {
+		if (!wpa_dbus_dict_string_array_add_element(&iter_array,
+							    items[i]))
+			return FALSE;
+	}
+
+	return wpa_dbus_dict_end_string_array(iter_dict, &iter_dict_entry,
+					      &iter_dict_val, &iter_array);
+}
+
+
+/**
+ * Convenience function to add an wpabuf binary array to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *                  wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param items The array of wpabuf structures
+ * @param num_items The number of strings in the array
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict,
+					      const char *key,
+					      const struct wpabuf **items,
+					      const dbus_uint32_t num_items)
+{
+	DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
+	dbus_uint32_t i;
+
+	if (!key ||
+	    (!items && num_items != 0) ||
+	    !wpa_dbus_dict_begin_array(iter_dict, key,
+				       DBUS_TYPE_ARRAY_AS_STRING
+				       DBUS_TYPE_BYTE_AS_STRING,
+				       &iter_dict_entry, &iter_dict_val,
+				       &iter_array))
+		return FALSE;
+
+	for (i = 0; i < num_items; i++) {
+		if (!wpa_dbus_dict_bin_array_add_element(&iter_array,
+							 wpabuf_head(items[i]),
+							 wpabuf_len(items[i])))
+			return FALSE;
+	}
+
+	return wpa_dbus_dict_end_array(iter_dict, &iter_dict_entry,
+				       &iter_dict_val, &iter_array);
+}
+
+
+/*****************************************************/
+/* Stuff for reading dicts                           */
+/*****************************************************/
+
+/**
+ * Start reading from a dbus dict.
+ *
+ * @param iter A valid DBusMessageIter pointing to the start of the dict
+ * @param iter_dict (out) A DBusMessageIter to be passed to
+ *    wpa_dbus_dict_read_next_entry()
+ * @error on failure a descriptive error
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter,
+				    DBusMessageIter *iter_dict,
+				    DBusError *error)
+{
+	int type;
+
+	wpa_printf(MSG_MSGDUMP, "%s: start reading a dict entry", __func__);
+	if (!iter || !iter_dict) {
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "[internal] missing message iterators");
+		return FALSE;
+	}
+
+	type = dbus_message_iter_get_arg_type(iter);
+	if (type != DBUS_TYPE_ARRAY ||
+	    dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: unexpected message argument types (arg=%c element=%c)",
+			   __func__, type,
+			   type != DBUS_TYPE_ARRAY ? '?' :
+			   dbus_message_iter_get_element_type(iter));
+		dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+				     "unexpected message argument types");
+		return FALSE;
+	}
+
+	dbus_message_iter_recurse(iter, iter_dict);
+	return TRUE;
+}
+
+
+#define BYTE_ARRAY_CHUNK_SIZE 34
+#define BYTE_ARRAY_ITEM_SIZE (sizeof(char))
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array(
+	DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry)
+{
+	dbus_uint32_t count = 0;
+	dbus_bool_t success = FALSE;
+	char *buffer, *nbuffer;
+
+	entry->bytearray_value = NULL;
+	entry->array_type = DBUS_TYPE_BYTE;
+
+	buffer = os_calloc(BYTE_ARRAY_CHUNK_SIZE, BYTE_ARRAY_ITEM_SIZE);
+	if (!buffer)
+		return FALSE;
+
+	entry->array_len = 0;
+	while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_BYTE) {
+		char byte;
+
+		if ((count % BYTE_ARRAY_CHUNK_SIZE) == 0 && count != 0) {
+			nbuffer = os_realloc_array(
+				buffer, count + BYTE_ARRAY_CHUNK_SIZE,
+				BYTE_ARRAY_ITEM_SIZE);
+			if (nbuffer == NULL) {
+				os_free(buffer);
+				wpa_printf(MSG_ERROR,
+					   "dbus: %s out of memory trying to retrieve the string array",
+					   __func__);
+				goto done;
+			}
+			buffer = nbuffer;
+		}
+
+		dbus_message_iter_get_basic(iter, &byte);
+		buffer[count] = byte;
+		entry->array_len = ++count;
+		dbus_message_iter_next(iter);
+	}
+	entry->bytearray_value = buffer;
+	wpa_hexdump_key(MSG_MSGDUMP, "dbus: byte array contents",
+			entry->bytearray_value, entry->array_len);
+
+	/* Zero-length arrays are valid. */
+	if (entry->array_len == 0) {
+		os_free(entry->bytearray_value);
+		entry->bytearray_value = NULL;
+	}
+
+	success = TRUE;
+
+done:
+	return success;
+}
+
+
+#define STR_ARRAY_CHUNK_SIZE 8
+#define STR_ARRAY_ITEM_SIZE (sizeof(char *))
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_string_array(
+	DBusMessageIter *iter, int array_type,
+	struct wpa_dbus_dict_entry *entry)
+{
+	dbus_uint32_t count = 0;
+	char **buffer, **nbuffer;
+
+	entry->strarray_value = NULL;
+	entry->array_len = 0;
+	entry->array_type = DBUS_TYPE_STRING;
+
+	buffer = os_calloc(STR_ARRAY_CHUNK_SIZE, STR_ARRAY_ITEM_SIZE);
+	if (buffer == NULL)
+		return FALSE;
+
+	while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
+		const char *value;
+		char *str;
+
+		if ((count % STR_ARRAY_CHUNK_SIZE) == 0 && count != 0) {
+			nbuffer = os_realloc_array(
+				buffer, count + STR_ARRAY_CHUNK_SIZE,
+				STR_ARRAY_ITEM_SIZE);
+			if (nbuffer == NULL) {
+				wpa_printf(MSG_ERROR,
+					   "dbus: %s out of memory trying to retrieve the string array",
+					   __func__);
+				goto fail;
+			}
+			buffer = nbuffer;
+		}
+
+		dbus_message_iter_get_basic(iter, &value);
+		wpa_printf(MSG_MSGDUMP, "%s: string_array value: %s",
+			   __func__, wpa_debug_show_keys ? value : "[omitted]");
+		str = os_strdup(value);
+		if (str == NULL) {
+			wpa_printf(MSG_ERROR,
+				   "dbus: %s out of memory trying to duplicate the string array",
+				   __func__);
+			goto fail;
+		}
+		buffer[count++] = str;
+		dbus_message_iter_next(iter);
+	}
+	entry->strarray_value = buffer;
+	entry->array_len = count;
+	wpa_printf(MSG_MSGDUMP, "%s: string_array length %u",
+		   __func__, entry->array_len);
+
+	/* Zero-length arrays are valid. */
+	if (entry->array_len == 0) {
+		os_free(entry->strarray_value);
+		entry->strarray_value = NULL;
+	}
+
+	return TRUE;
+
+fail:
+	while (count > 0) {
+		count--;
+		os_free(buffer[count]);
+	}
+	os_free(buffer);
+	return FALSE;
+}
+
+
+#define BIN_ARRAY_CHUNK_SIZE 10
+#define BIN_ARRAY_ITEM_SIZE (sizeof(struct wpabuf *))
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_binarray(
+	DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry)
+{
+	struct wpa_dbus_dict_entry tmpentry;
+	size_t buflen = 0;
+	int i, type;
+
+	entry->array_type = WPAS_DBUS_TYPE_BINARRAY;
+	entry->array_len = 0;
+	entry->binarray_value = NULL;
+
+	type = dbus_message_iter_get_arg_type(iter);
+	wpa_printf(MSG_MSGDUMP, "%s: parsing binarray type %c", __func__, type);
+	if (type == DBUS_TYPE_INVALID) {
+		/* Likely an empty array of arrays */
+		return TRUE;
+	}
+	if (type != DBUS_TYPE_ARRAY) {
+		wpa_printf(MSG_DEBUG, "%s: not an array type: %c",
+			   __func__, type);
+		return FALSE;
+	}
+
+	type = dbus_message_iter_get_element_type(iter);
+	if (type != DBUS_TYPE_BYTE) {
+		wpa_printf(MSG_DEBUG, "%s: unexpected element type %c",
+			   __func__, type);
+		return FALSE;
+	}
+
+	while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) {
+		DBusMessageIter iter_array;
+
+		if (entry->array_len == buflen) {
+			struct wpabuf **newbuf;
+
+			buflen += BIN_ARRAY_CHUNK_SIZE;
+
+			newbuf = os_realloc_array(entry->binarray_value,
+						  buflen, BIN_ARRAY_ITEM_SIZE);
+			if (!newbuf)
+				goto cleanup;
+			entry->binarray_value = newbuf;
+		}
+
+		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;
+
+		entry->binarray_value[entry->array_len] =
+			wpabuf_alloc_ext_data((u8 *) tmpentry.bytearray_value,
+					      tmpentry.array_len);
+		if (entry->binarray_value[entry->array_len] == NULL) {
+			wpa_dbus_dict_entry_clear(&tmpentry);
+			goto cleanup;
+		}
+		entry->array_len++;
+		dbus_message_iter_next(iter);
+	}
+	wpa_printf(MSG_MSGDUMP, "%s: binarray length %u",
+		   __func__, entry->array_len);
+
+	return TRUE;
+
+ cleanup:
+	for (i = 0; i < (int) entry->array_len; i++)
+		wpabuf_free(entry->binarray_value[i]);
+	os_free(entry->binarray_value);
+	entry->array_len = 0;
+	entry->binarray_value = NULL;
+	return FALSE;
+}
+
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_array(
+	DBusMessageIter *iter_dict_val, struct wpa_dbus_dict_entry *entry)
+{
+	int array_type = dbus_message_iter_get_element_type(iter_dict_val);
+	dbus_bool_t success = FALSE;
+	DBusMessageIter iter_array;
+
+	wpa_printf(MSG_MSGDUMP, "%s: array_type %c", __func__, array_type);
+
+	dbus_message_iter_recurse(iter_dict_val, &iter_array);
+
+	switch (array_type) {
+	case DBUS_TYPE_BYTE:
+		success = _wpa_dbus_dict_entry_get_byte_array(&iter_array,
+							      entry);
+		break;
+	case DBUS_TYPE_STRING:
+		success = _wpa_dbus_dict_entry_get_string_array(&iter_array,
+								array_type,
+								entry);
+		break;
+	case DBUS_TYPE_ARRAY:
+		success = _wpa_dbus_dict_entry_get_binarray(&iter_array, entry);
+		break;
+	default:
+		wpa_printf(MSG_MSGDUMP, "%s: unsupported array type %c",
+			   __func__, array_type);
+		break;
+	}
+
+	return success;
+}
+
+
+static dbus_bool_t _wpa_dbus_dict_fill_value_from_variant(
+	struct wpa_dbus_dict_entry *entry, DBusMessageIter *iter)
+{
+	const char *v;
+
+	switch (entry->type) {
+	case DBUS_TYPE_OBJECT_PATH:
+		dbus_message_iter_get_basic(iter, &v);
+		wpa_printf(MSG_MSGDUMP, "%s: object path value: %s",
+			   __func__, v);
+		entry->str_value = os_strdup(v);
+		if (entry->str_value == NULL)
+			return FALSE;
+		break;
+	case DBUS_TYPE_STRING:
+		dbus_message_iter_get_basic(iter, &v);
+		wpa_printf(MSG_MSGDUMP, "%s: string value: %s",
+			   __func__, wpa_debug_show_keys ? v : "[omitted]");
+		entry->str_value = os_strdup(v);
+		if (entry->str_value == NULL)
+			return FALSE;
+		break;
+	case DBUS_TYPE_BOOLEAN:
+		dbus_message_iter_get_basic(iter, &entry->bool_value);
+		wpa_printf(MSG_MSGDUMP, "%s: boolean value: %d",
+			   __func__, entry->bool_value);
+		break;
+	case DBUS_TYPE_BYTE:
+		dbus_message_iter_get_basic(iter, &entry->byte_value);
+		wpa_printf(MSG_MSGDUMP, "%s: byte value: %d",
+			   __func__, entry->byte_value);
+		break;
+	case DBUS_TYPE_INT16:
+		dbus_message_iter_get_basic(iter, &entry->int16_value);
+		wpa_printf(MSG_MSGDUMP, "%s: int16 value: %d",
+			   __func__, entry->int16_value);
+		break;
+	case DBUS_TYPE_UINT16:
+		dbus_message_iter_get_basic(iter, &entry->uint16_value);
+		wpa_printf(MSG_MSGDUMP, "%s: uint16 value: %d",
+			   __func__, entry->uint16_value);
+		break;
+	case DBUS_TYPE_INT32:
+		dbus_message_iter_get_basic(iter, &entry->int32_value);
+		wpa_printf(MSG_MSGDUMP, "%s: int32 value: %d",
+			   __func__, entry->int32_value);
+		break;
+	case DBUS_TYPE_UINT32:
+		dbus_message_iter_get_basic(iter, &entry->uint32_value);
+		wpa_printf(MSG_MSGDUMP, "%s: uint32 value: %d",
+			   __func__, entry->uint32_value);
+		break;
+	case DBUS_TYPE_INT64:
+		dbus_message_iter_get_basic(iter, &entry->int64_value);
+		wpa_printf(MSG_MSGDUMP, "%s: int64 value: %lld",
+			   __func__, (long long int) entry->int64_value);
+		break;
+	case DBUS_TYPE_UINT64:
+		dbus_message_iter_get_basic(iter, &entry->uint64_value);
+		wpa_printf(MSG_MSGDUMP, "%s: uint64 value: %llu",
+			   __func__,
+			   (unsigned long long int) entry->uint64_value);
+		break;
+	case DBUS_TYPE_DOUBLE:
+		dbus_message_iter_get_basic(iter, &entry->double_value);
+		wpa_printf(MSG_MSGDUMP, "%s: double value: %f",
+			   __func__, entry->double_value);
+		break;
+	case DBUS_TYPE_ARRAY:
+		return _wpa_dbus_dict_entry_get_array(iter, entry);
+	default:
+		wpa_printf(MSG_MSGDUMP, "%s: unsupported type %c",
+			   __func__, entry->type);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+/**
+ * Read the current key/value entry from the dict.  Entries are dynamically
+ * allocated when needed and must be freed after use with the
+ * wpa_dbus_dict_entry_clear() function.
+ *
+ * The returned entry object will be filled with the type and value of the next
+ * entry in the dict, or the type will be DBUS_TYPE_INVALID if an error
+ * occurred.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_read()
+ * @param entry A valid dict entry object into which the dict key and value
+ *    will be placed
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict,
+				    struct wpa_dbus_dict_entry * entry)
+{
+	DBusMessageIter iter_dict_entry, iter_dict_val;
+	int type;
+	const char *key;
+
+	if (!iter_dict || !entry ||
+	    dbus_message_iter_get_arg_type(iter_dict) != DBUS_TYPE_DICT_ENTRY) {
+		wpa_printf(MSG_DEBUG, "%s: not a dict entry", __func__);
+		goto error;
+	}
+
+	dbus_message_iter_recurse(iter_dict, &iter_dict_entry);
+	dbus_message_iter_get_basic(&iter_dict_entry, &key);
+	wpa_printf(MSG_MSGDUMP, "%s: dict entry key: %s", __func__, key);
+	entry->key = key;
+
+	if (!dbus_message_iter_next(&iter_dict_entry)) {
+		wpa_printf(MSG_DEBUG, "%s: no variant in dict entry", __func__);
+		goto error;
+	}
+	type = dbus_message_iter_get_arg_type(&iter_dict_entry);
+	if (type != DBUS_TYPE_VARIANT) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: unexpected dict entry variant type: %c",
+			   __func__, type);
+		goto error;
+	}
+
+	dbus_message_iter_recurse(&iter_dict_entry, &iter_dict_val);
+	entry->type = dbus_message_iter_get_arg_type(&iter_dict_val);
+	wpa_printf(MSG_MSGDUMP, "%s: dict entry variant content type: %c",
+		   __func__, entry->type);
+	entry->array_type = DBUS_TYPE_INVALID;
+	if (!_wpa_dbus_dict_fill_value_from_variant(entry, &iter_dict_val)) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: failed to fetch dict values from variant",
+			   __func__);
+		goto error;
+	}
+
+	dbus_message_iter_next(iter_dict);
+	return TRUE;
+
+error:
+	if (entry) {
+		wpa_dbus_dict_entry_clear(entry);
+		entry->type = DBUS_TYPE_INVALID;
+		entry->array_type = DBUS_TYPE_INVALID;
+	}
+
+	return FALSE;
+}
+
+
+/**
+ * Return whether or not there are additional dictionary entries.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_read()
+ * @return TRUE if more dict entries exists, FALSE if no more dict entries
+ * exist
+ */
+dbus_bool_t wpa_dbus_dict_has_dict_entry(DBusMessageIter *iter_dict)
+{
+	if (!iter_dict)
+		return FALSE;
+	return dbus_message_iter_get_arg_type(iter_dict) ==
+		DBUS_TYPE_DICT_ENTRY;
+}
+
+
+/**
+ * Free any memory used by the entry object.
+ *
+ * @param entry The entry object
+ */
+void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry)
+{
+	unsigned int i;
+
+	if (!entry)
+		return;
+	switch (entry->type) {
+	case DBUS_TYPE_OBJECT_PATH:
+	case DBUS_TYPE_STRING:
+		os_free(entry->str_value);
+		break;
+	case DBUS_TYPE_ARRAY:
+		switch (entry->array_type) {
+		case DBUS_TYPE_BYTE:
+			os_free(entry->bytearray_value);
+			break;
+		case DBUS_TYPE_STRING:
+			if (!entry->strarray_value)
+				break;
+			for (i = 0; i < entry->array_len; i++)
+				os_free(entry->strarray_value[i]);
+			os_free(entry->strarray_value);
+			break;
+		case WPAS_DBUS_TYPE_BINARRAY:
+			for (i = 0; i < entry->array_len; i++)
+				wpabuf_free(entry->binarray_value[i]);
+			os_free(entry->binarray_value);
+			break;
+		}
+		break;
+	}
+
+	os_memset(entry, 0, sizeof(struct wpa_dbus_dict_entry));
+}
diff --git a/hostap/wpa_supplicant/dbus/dbus_dict_helpers.h b/hostap/wpa_supplicant/dbus/dbus_dict_helpers.h
new file mode 100644
index 0000000..b068431
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -0,0 +1,167 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DBUS_DICT_HELPERS_H
+#define DBUS_DICT_HELPERS_H
+
+#include "wpabuf.h"
+
+/*
+ * Adding a dict to a DBusMessage
+ */
+
+dbus_bool_t wpa_dbus_dict_open_write(DBusMessageIter *iter,
+				     DBusMessageIter *iter_dict);
+
+dbus_bool_t wpa_dbus_dict_close_write(DBusMessageIter *iter,
+				      DBusMessageIter *iter_dict);
+
+const char * wpa_dbus_type_as_string(const int type);
+
+dbus_bool_t wpa_dbus_dict_append_string(DBusMessageIter *iter_dict,
+					const char *key, const char *value);
+
+dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict,
+				      const char *key, const char value);
+
+dbus_bool_t wpa_dbus_dict_append_bool(DBusMessageIter *iter_dict,
+				      const char *key,
+				      const dbus_bool_t value);
+
+dbus_bool_t wpa_dbus_dict_append_int16(DBusMessageIter *iter_dict,
+				       const char *key,
+				       const dbus_int16_t value);
+
+dbus_bool_t wpa_dbus_dict_append_uint16(DBusMessageIter *iter_dict,
+					const char *key,
+					const dbus_uint16_t value);
+
+dbus_bool_t wpa_dbus_dict_append_int32(DBusMessageIter *iter_dict,
+				       const char *key,
+				       const dbus_int32_t value);
+
+dbus_bool_t wpa_dbus_dict_append_uint32(DBusMessageIter *iter_dict,
+					const char *key,
+					const dbus_uint32_t value);
+
+dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict,
+				       const char *key,
+				       const dbus_int64_t value);
+
+dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict,
+					const char *key,
+					const dbus_uint64_t value);
+
+dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict,
+					const char *key,
+					const double value);
+
+dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict,
+					     const char *key,
+					     const char *value);
+
+dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict,
+					    const char *key,
+					    const char *value,
+					    const dbus_uint32_t value_len);
+
+/* Manual construction and addition of array elements */
+dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict,
+				      const char *key, const char *type,
+				      DBusMessageIter *iter_dict_entry,
+				      DBusMessageIter *iter_dict_val,
+				      DBusMessageIter *iter_array);
+
+dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
+					     const char *key,
+					     DBusMessageIter *iter_dict_entry,
+					     DBusMessageIter *iter_dict_val,
+					     DBusMessageIter *iter_array);
+
+dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array,
+						   const char *elem);
+
+dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array,
+						const u8 *value,
+						size_t value_len);
+
+dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict,
+				    DBusMessageIter *iter_dict_entry,
+				    DBusMessageIter *iter_dict_val,
+				    DBusMessageIter *iter_array);
+
+static inline dbus_bool_t
+wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict,
+			       DBusMessageIter *iter_dict_entry,
+			       DBusMessageIter *iter_dict_val,
+			       DBusMessageIter *iter_array)
+{
+	return wpa_dbus_dict_end_array(iter_dict, iter_dict_entry,
+				       iter_dict_val, iter_array);
+}
+
+/* Convenience function to add a whole string list */
+dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
+					      const char *key,
+					      const char **items,
+					      const dbus_uint32_t num_items);
+
+dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict,
+					      const char *key,
+					      const struct wpabuf **items,
+					      const dbus_uint32_t num_items);
+
+/*
+ * Reading a dict from a DBusMessage
+ */
+
+/*
+ * Used only in struct wpa_dbus_dict_entry::array_type internally to identify
+ * special binary array case.
+ */
+#define WPAS_DBUS_TYPE_BINARRAY ((int) '@')
+
+struct wpa_dbus_dict_entry {
+	int type;         /** the dbus type of the dict entry's value */
+	int array_type;   /** the dbus type of the array elements if the dict
+			      entry value contains an array, or the special
+			      WPAS_DBUS_TYPE_BINARRAY */
+	const char *key;  /** key of the dict entry */
+
+	/** Possible values of the property */
+	union {
+		char *str_value;
+		char byte_value;
+		dbus_bool_t bool_value;
+		dbus_int16_t int16_value;
+		dbus_uint16_t uint16_value;
+		dbus_int32_t int32_value;
+		dbus_uint32_t uint32_value;
+		dbus_int64_t int64_value;
+		dbus_uint64_t uint64_value;
+		double double_value;
+		char *bytearray_value;
+		char **strarray_value;
+		struct wpabuf **binarray_value;
+	};
+	dbus_uint32_t array_len; /** length of the array if the dict entry's
+				     value contains an array */
+};
+
+dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter,
+				    DBusMessageIter *iter_dict,
+				    DBusError *error);
+
+dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict,
+				    struct wpa_dbus_dict_entry *entry);
+
+dbus_bool_t wpa_dbus_dict_has_dict_entry(DBusMessageIter *iter_dict);
+
+void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry);
+
+#endif  /* DBUS_DICT_HELPERS_H */
diff --git a/hostap/wpa_supplicant/dbus/dbus_new.c b/hostap/wpa_supplicant/dbus/dbus_new.c
new file mode 100644
index 0000000..aa5ca54
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_new.c
@@ -0,0 +1,4136 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
+ * 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 "common/ieee802_11_defs.h"
+#include "wps/wps.h"
+#include "../config.h"
+#include "../wpa_supplicant_i.h"
+#include "../bss.h"
+#include "../wpas_glue.h"
+#include "dbus_new_helpers.h"
+#include "dbus_dict_helpers.h"
+#include "dbus_new.h"
+#include "dbus_new_handlers.h"
+#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 */
+
+/*
+ * NameOwnerChanged handling
+ *
+ * Some services we provide allow an application to register for
+ * a signal that it needs. While it can also unregister, we must
+ * be prepared for the case where the application simply crashes
+ * and thus doesn't clean up properly. The way to handle this in
+ * DBus is to register for the NameOwnerChanged signal which will
+ * signal an owner change to NULL if the peer closes the socket
+ * for whatever reason.
+ *
+ * Handle this signal via a filter function whenever necessary.
+ * The code below also handles refcounting in case in the future
+ * there will be multiple instances of this subscription scheme.
+ */
+static const char wpas_dbus_noc_filter_str[] =
+	"interface=org.freedesktop.DBus,member=NameOwnerChanged";
+
+
+static DBusHandlerResult noc_filter(DBusConnection *conn,
+				    DBusMessage *message, void *data)
+{
+	struct wpas_dbus_priv *priv = data;
+
+	if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS,
+				   "NameOwnerChanged")) {
+		const char *name;
+		const char *prev_owner;
+		const char *new_owner;
+		DBusError derr;
+		struct wpa_supplicant *wpa_s;
+
+		dbus_error_init(&derr);
+
+		if (!dbus_message_get_args(message, &derr,
+					   DBUS_TYPE_STRING, &name,
+					   DBUS_TYPE_STRING, &prev_owner,
+					   DBUS_TYPE_STRING, &new_owner,
+					   DBUS_TYPE_INVALID)) {
+			/* Ignore this error */
+			dbus_error_free(&derr);
+			return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+		}
+
+		for (wpa_s = priv->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+			if (wpa_s->preq_notify_peer != NULL &&
+			    os_strcmp(name, wpa_s->preq_notify_peer) == 0 &&
+			    (new_owner == NULL || os_strlen(new_owner) == 0)) {
+				/* probe request owner disconnected */
+				os_free(wpa_s->preq_notify_peer);
+				wpa_s->preq_notify_peer = NULL;
+				wpas_dbus_unsubscribe_noc(priv);
+			}
+		}
+	}
+
+	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv)
+{
+	priv->dbus_noc_refcnt++;
+	if (priv->dbus_noc_refcnt > 1)
+		return;
+
+	if (!dbus_connection_add_filter(priv->con, noc_filter, priv, NULL)) {
+		wpa_printf(MSG_ERROR, "dbus: failed to add filter");
+		return;
+	}
+
+	dbus_bus_add_match(priv->con, wpas_dbus_noc_filter_str, NULL);
+}
+
+
+void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv)
+{
+	priv->dbus_noc_refcnt--;
+	if (priv->dbus_noc_refcnt > 0)
+		return;
+
+	dbus_bus_remove_match(priv->con, wpas_dbus_noc_filter_str, NULL);
+	dbus_connection_remove_filter(priv->con, noc_filter, priv);
+}
+
+#endif /* CONFIG_AP */
+
+
+/**
+ * wpas_dbus_signal_interface - Send a interface related event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sig_name: signal name - InterfaceAdded or InterfaceRemoved
+ * @properties: Whether to add second argument with object properties
+ *
+ * Notify listeners about event related with interface
+ */
+static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s,
+				       const char *sig_name, int properties)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(WPAS_DBUS_NEW_PATH,
+				      WPAS_DBUS_NEW_INTERFACE, sig_name);
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					    &wpa_s->dbus_new_path) ||
+	    (properties &&
+	     !wpa_dbus_get_object_properties(
+		     iface, wpa_s->dbus_new_path,
+		     WPAS_DBUS_NEW_IFACE_INTERFACE, &iter)))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_interface_added - Send a interface created signal
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Notify listeners about creating new interface
+ */
+static void wpas_dbus_signal_interface_added(struct wpa_supplicant *wpa_s)
+{
+	wpas_dbus_signal_interface(wpa_s, "InterfaceAdded", TRUE);
+}
+
+
+/**
+ * wpas_dbus_signal_interface_removed - Send a interface removed signal
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Notify listeners about removing interface
+ */
+static void wpas_dbus_signal_interface_removed(struct wpa_supplicant *wpa_s)
+{
+	wpas_dbus_signal_interface(wpa_s, "InterfaceRemoved", FALSE);
+
+}
+
+
+/**
+ * wpas_dbus_signal_scan_done - send scan done signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @success: indicates if scanning succeed or failed
+ *
+ * Notify listeners about finishing a scan
+ */
+void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	dbus_bool_t succ;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "ScanDone");
+	if (msg == NULL)
+		return;
+
+	succ = success ? TRUE : FALSE;
+	if (dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &succ,
+				     DBUS_TYPE_INVALID))
+		dbus_connection_send(iface->con, msg, NULL);
+	else
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_bss - Send a BSS related event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @bss_obj_path: BSS object path
+ * @sig_name: signal name - BSSAdded or BSSRemoved
+ * @properties: Whether to add second argument with object properties
+ *
+ * Notify listeners about event related with BSS
+ */
+static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s,
+				 const char *bss_obj_path,
+				 const char *sig_name, int properties)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      sig_name);
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					    &bss_obj_path) ||
+	    (properties &&
+	     !wpa_dbus_get_object_properties(iface, bss_obj_path,
+					     WPAS_DBUS_NEW_IFACE_BSS,
+					     &iter)))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_bss_added - Send a BSS added signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @bss_obj_path: new BSS object path
+ *
+ * Notify listeners about adding new BSS
+ */
+static void wpas_dbus_signal_bss_added(struct wpa_supplicant *wpa_s,
+				       const char *bss_obj_path)
+{
+	wpas_dbus_signal_bss(wpa_s, bss_obj_path, "BSSAdded", TRUE);
+}
+
+
+/**
+ * wpas_dbus_signal_bss_removed - Send a BSS removed signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @bss_obj_path: BSS object path
+ *
+ * Notify listeners about removing BSS
+ */
+static void wpas_dbus_signal_bss_removed(struct wpa_supplicant *wpa_s,
+					 const char *bss_obj_path)
+{
+	wpas_dbus_signal_bss(wpa_s, bss_obj_path, "BSSRemoved", FALSE);
+}
+
+
+/**
+ * wpas_dbus_signal_blob - Send a blob related event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @name: blob name
+ * @sig_name: signal name - BlobAdded or BlobRemoved
+ *
+ * Notify listeners about event related with blob
+ */
+static void wpas_dbus_signal_blob(struct wpa_supplicant *wpa_s,
+				  const char *name, const char *sig_name)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      sig_name);
+	if (msg == NULL)
+		return;
+
+	if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
+				     DBUS_TYPE_INVALID))
+		dbus_connection_send(iface->con, msg, NULL);
+	else
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_blob_added - Send a blob added signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @name: blob name
+ *
+ * Notify listeners about adding a new blob
+ */
+void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s,
+				 const char *name)
+{
+	wpas_dbus_signal_blob(wpa_s, name, "BlobAdded");
+}
+
+
+/**
+ * wpas_dbus_signal_blob_removed - Send a blob removed signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @name: blob name
+ *
+ * Notify listeners about removing blob
+ */
+void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s,
+				   const char *name)
+{
+	wpas_dbus_signal_blob(wpa_s, name, "BlobRemoved");
+}
+
+
+/**
+ * wpas_dbus_signal_network - Send a network related event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: new network id
+ * @sig_name: signal name - NetworkAdded, NetworkRemoved or NetworkSelected
+ * @properties: determines if add second argument with object properties
+ *
+ * Notify listeners about event related with configured network
+ */
+static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s,
+				     int id, const char *sig_name,
+				     int properties)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+		    wpa_s->dbus_new_path, id);
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      sig_name);
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	path = net_obj_path;
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					    &path) ||
+	    (properties &&
+	     !wpa_dbus_get_object_properties(
+		     iface, net_obj_path, WPAS_DBUS_NEW_IFACE_NETWORK,
+		     &iter)))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_network_added - Send a network added signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: new network id
+ *
+ * Notify listeners about adding new network
+ */
+static void wpas_dbus_signal_network_added(struct wpa_supplicant *wpa_s,
+					   int id)
+{
+	wpas_dbus_signal_network(wpa_s, id, "NetworkAdded", TRUE);
+}
+
+
+/**
+ * wpas_dbus_signal_network_removed - Send a network removed signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: network id
+ *
+ * Notify listeners about removing a network
+ */
+static void wpas_dbus_signal_network_removed(struct wpa_supplicant *wpa_s,
+					     int id)
+{
+	wpas_dbus_signal_network(wpa_s, id, "NetworkRemoved", FALSE);
+}
+
+
+/**
+ * wpas_dbus_signal_network_selected - Send a network selected signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: network id
+ *
+ * Notify listeners about selecting a network
+ */
+void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id)
+{
+	wpas_dbus_signal_network(wpa_s, id, "NetworkSelected", FALSE);
+}
+
+
+/**
+ * wpas_dbus_signal_network_request - Indicate that additional information
+ * (EAP password, etc.) is required to complete the association to this SSID
+ * @wpa_s: %wpa_supplicant network interface data
+ * @rtype: The specific additional information required
+ * @default_text: Optional description of required information
+ *
+ * Request additional information or passwords to complete an association
+ * request.
+ */
+void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s,
+				      struct wpa_ssid *ssid,
+				      enum wpa_ctrl_req_type rtype,
+				      const char *default_txt)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+	const char *field, *txt = NULL, *net_ptr;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	field = wpa_supplicant_ctrl_req_to_string(rtype, default_txt, &txt);
+	if (field == NULL)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "NetworkRequest");
+	if (msg == NULL)
+		return;
+
+	os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+		    wpa_s->dbus_new_path, ssid->id);
+	net_ptr = &net_obj_path[0];
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					    &net_ptr) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &field) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &txt))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_network_enabled_changed - Signals Enabled property changes
+ * @wpa_s: %wpa_supplicant network interface data
+ * @ssid: configured network which Enabled property has changed
+ *
+ * Sends PropertyChanged signals containing new value of Enabled property
+ * for specified network
+ */
+void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s,
+					      struct wpa_ssid *ssid)
+{
+
+	char path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+	if (!wpa_s->dbus_new_path)
+		return;
+	os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d",
+		    wpa_s->dbus_new_path, ssid->id);
+
+	wpa_dbus_queue_property_changed(wpa_s->global->dbus, path,
+				        WPAS_DBUS_NEW_IFACE_NETWORK, "Enabled");
+}
+
+
+#ifdef CONFIG_WPS
+
+/**
+ * wpas_dbus_signal_wps_event_pbc_overlap - Signals PBC overlap WPS event
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Sends Event dbus signal with name "pbc-overlap" and empty dict as arguments
+ */
+void wpas_dbus_signal_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *iface;
+	char *key = "pbc-overlap";
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_WPS, "Event");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
+	    !wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_wps_event_success - Signals Success WPS event
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Sends Event dbus signal with name "success" and empty dict as arguments
+ */
+void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s)
+{
+
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *iface;
+	char *key = "success";
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_WPS, "Event");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
+	    !wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_wps_event_fail - Signals Fail WPS event
+ * @wpa_s: %wpa_supplicant network interface data
+ * @fail: WPS failure information
+ *
+ * Sends Event dbus signal with name "fail" and dictionary containing
+ * "msg field with fail message number (int32) as arguments
+ */
+void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s,
+				     struct wps_event_fail *fail)
+{
+
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *iface;
+	char *key = "fail";
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_WPS, "Event");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
+	    !wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "msg", fail->msg) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "config_error",
+					fail->config_error) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "error_indication",
+					fail->error_indication) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_wps_event_m2d - Signals M2D WPS event
+ * @wpa_s: %wpa_supplicant network interface data
+ * @m2d: M2D event data information
+ *
+ * Sends Event dbus signal with name "m2d" and dictionary containing
+ * fields of wps_event_m2d structure.
+ */
+void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s,
+				    struct wps_event_m2d *m2d)
+{
+
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *iface;
+	char *key = "m2d";
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_WPS, "Event");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
+	    !wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_uint16(&dict_iter, "config_methods",
+					 m2d->config_methods) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "manufacturer",
+					     (const char *) m2d->manufacturer,
+					     m2d->manufacturer_len) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "model_name",
+					     (const char *) m2d->model_name,
+					     m2d->model_name_len) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "model_number",
+					     (const char *) m2d->model_number,
+					     m2d->model_number_len) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "serial_number",
+					     (const char *)
+					     m2d->serial_number,
+					     m2d->serial_number_len) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "dev_name",
+					     (const char *) m2d->dev_name,
+					     m2d->dev_name_len) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "primary_dev_type",
+					     (const char *)
+					     m2d->primary_dev_type, 8) ||
+	    !wpa_dbus_dict_append_uint16(&dict_iter, "config_error",
+					 m2d->config_error) ||
+	    !wpa_dbus_dict_append_uint16(&dict_iter, "dev_password_id",
+					 m2d->dev_password_id) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_wps_cred - Signals new credentials
+ * @wpa_s: %wpa_supplicant network interface data
+ * @cred: WPS Credential information
+ *
+ * Sends signal with credentials in directory argument
+ */
+void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
+			       const struct wps_credential *cred)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *iface;
+	char *auth_type[5]; /* we have five possible authentication types */
+	int at_num = 0;
+	char *encr_type[3]; /* we have three possible encryption types */
+	int et_num = 0;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_WPS,
+				      "Credentials");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+		goto nomem;
+
+	if (cred->auth_type & WPS_AUTH_OPEN)
+		auth_type[at_num++] = "open";
+	if (cred->auth_type & WPS_AUTH_WPAPSK)
+		auth_type[at_num++] = "wpa-psk";
+	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";
+
+	if (cred->encr_type & WPS_ENCR_NONE)
+		encr_type[et_num++] = "none";
+	if (cred->encr_type & WPS_ENCR_TKIP)
+		encr_type[et_num++] = "tkip";
+	if (cred->encr_type & WPS_ENCR_AES)
+		encr_type[et_num++] = "aes";
+
+	if ((wpa_s->current_ssid &&
+	     !wpa_dbus_dict_append_byte_array(
+		     &dict_iter, "BSSID",
+		     (const char *) wpa_s->current_ssid->bssid, ETH_ALEN)) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "SSID",
+					     (const char *) cred->ssid,
+					     cred->ssid_len) ||
+	    !wpa_dbus_dict_append_string_array(&dict_iter, "AuthType",
+					       (const char **) auth_type,
+					       at_num) ||
+	    !wpa_dbus_dict_append_string_array(&dict_iter, "EncrType",
+					       (const char **) encr_type,
+					       et_num) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "Key",
+					     (const char *) cred->key,
+					     cred->key_len) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "KeyIndex",
+					 cred->key_idx) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		goto nomem;
+
+	dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+	dbus_message_unref(msg);
+}
+
+#endif /* CONFIG_WPS */
+
+void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
+				    int depth, const char *subject,
+				    const char *altsubject[],
+				    int num_altsubject,
+				    const char *cert_hash,
+				    const struct wpabuf *cert)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "Certification");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "depth", depth) ||
+	    !wpa_dbus_dict_append_string(&dict_iter, "subject", subject) ||
+	    (altsubject && num_altsubject &&
+	     !wpa_dbus_dict_append_string_array(&dict_iter, "altsubject",
+						altsubject, num_altsubject)) ||
+	    (cert_hash &&
+	     !wpa_dbus_dict_append_string(&dict_iter, "cert_hash",
+					  cert_hash)) ||
+	    (cert &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter, "cert",
+					      wpabuf_head(cert),
+					      wpabuf_len(cert))) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
+				 const char *status, const char *parameter)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "EAP");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &status) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+					    &parameter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_sta - Send a station related event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sta: station mac address
+ * @sig_name: signal name - StaAuthorized or StaDeauthorized
+ *
+ * Notify listeners about event related with station
+ */
+static void wpas_dbus_signal_sta(struct wpa_supplicant *wpa_s,
+				 const u8 *sta, const char *sig_name)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	char sta_mac[WPAS_DBUS_OBJECT_PATH_MAX];
+	char *dev_mac;
+
+	os_snprintf(sta_mac, WPAS_DBUS_OBJECT_PATH_MAX, MACSTR, MAC2STR(sta));
+	dev_mac = sta_mac;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE, sig_name);
+	if (msg == NULL)
+		return;
+
+	if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &dev_mac,
+				     DBUS_TYPE_INVALID))
+		dbus_connection_send(iface->con, msg, NULL);
+	else
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	dbus_message_unref(msg);
+
+	wpa_printf(MSG_DEBUG, "dbus: Station MAC address '%s' '%s'",
+		   sta_mac, sig_name);
+}
+
+
+/**
+ * wpas_dbus_signal_sta_authorized - Send a STA authorized signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sta: station mac address
+ *
+ * Notify listeners a new station has been authorized
+ */
+void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s,
+				     const u8 *sta)
+{
+	wpas_dbus_signal_sta(wpa_s, sta, "StaAuthorized");
+}
+
+
+/**
+ * wpas_dbus_signal_sta_deauthorized - Send a STA deauthorized signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sta: station mac address
+ *
+ * Notify listeners a station has been deauthorized
+ */
+void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s,
+				       const u8 *sta)
+{
+	wpas_dbus_signal_sta(wpa_s, sta, "StaDeauthorized");
+}
+
+
+#ifdef CONFIG_P2P
+
+/**
+ * wpas_dbus_signal_p2p_group_removed - Signals P2P group was removed
+ * @wpa_s: %wpa_supplicant network interface data
+ * @role: role of this device (client or GO)
+ * Sends signal with i/f name and role as string arguments
+ */
+void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
+					const char *role)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *iface = wpa_s->global->dbus;
+	struct wpa_supplicant *parent;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	parent = wpa_s->parent;
+	if (parent->p2p_mgmt)
+		parent = parent->parent;
+
+	if (!wpa_s->dbus_groupobj_path || !wpa_s->dbus_new_path ||
+	    !parent->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(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) ||
+	    !wpa_dbus_dict_append_object_path(&dict_iter,
+					      "interface_object",
+					      wpa_s->dbus_new_path) ||
+	    !wpa_dbus_dict_append_string(&dict_iter, "role", role) ||
+	    !wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
+					      wpa_s->dbus_groupobj_path) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_provision_discovery - Signals various PD events
+ *
+ * @dev_addr - who sent the request or responded to our request.
+ * @request - Will be 1 if request, 0 for response.
+ * @status - valid only in case of response
+ * @config_methods - wps config methods
+ * @generated_pin - pin to be displayed in case of WPS_CONFIG_DISPLAY method
+ *
+ * Sends following provision discovery related events:
+ *	ProvisionDiscoveryRequestDisplayPin
+ *	ProvisionDiscoveryResponseDisplayPin
+ *	ProvisionDiscoveryRequestEnterPin
+ *	ProvisionDiscoveryResponseEnterPin
+ *	ProvisionDiscoveryPBCRequest
+ *	ProvisionDiscoveryPBCResponse
+ *
+ *	TODO::
+ *	ProvisionDiscoveryFailure (timeout case)
+ */
+void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+					      const u8 *dev_addr, int request,
+					      enum p2p_prov_disc_status status,
+					      u16 config_methods,
+					      unsigned int generated_pin)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	struct wpas_dbus_priv *iface;
+	char *_signal;
+	int add_pin = 0;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+	int error_ret = 1;
+	char pin[9], *p_pin = NULL;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
+	if (request || !status) {
+		if (config_methods & WPS_CONFIG_DISPLAY)
+			_signal = request ?
+				 "ProvisionDiscoveryRequestDisplayPin" :
+				 "ProvisionDiscoveryResponseEnterPin";
+		else if (config_methods & WPS_CONFIG_KEYPAD)
+			_signal = request ?
+				 "ProvisionDiscoveryRequestEnterPin" :
+				 "ProvisionDiscoveryResponseDisplayPin";
+		else if (config_methods & WPS_CONFIG_PUSHBUTTON)
+			_signal = request ? "ProvisionDiscoveryPBCRequest" :
+				   "ProvisionDiscoveryPBCResponse";
+		else
+			return; /* Unknown or un-supported method */
+	} else {
+		/* Explicit check for failure response */
+		_signal = "ProvisionDiscoveryFailure";
+	}
+
+	add_pin = ((request && (config_methods & WPS_CONFIG_DISPLAY)) ||
+		   (!request && !status &&
+			(config_methods & WPS_CONFIG_KEYPAD)));
+
+	if (add_pin) {
+		os_snprintf(pin, sizeof(pin), "%08d", generated_pin);
+		p_pin = pin;
+	}
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE, _signal);
+	if (msg == NULL)
+		return;
+
+	/* Check if this is a known peer */
+	if (!p2p_peer_known(wpa_s->global->p2p, dev_addr))
+		goto error;
+
+	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));
+
+	path = peer_obj_path;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter,
+					    DBUS_TYPE_OBJECT_PATH,
+					    &path))
+			goto error;
+
+	if (!request && status)
+		/* Attach status to ProvisionDiscoveryFailure */
+		error_ret = !dbus_message_iter_append_basic(&iter,
+						    DBUS_TYPE_INT32,
+						    &status);
+	else
+		error_ret = (add_pin &&
+				 !dbus_message_iter_append_basic(&iter,
+							DBUS_TYPE_STRING,
+							&p_pin));
+
+error:
+	if (!error_ret)
+		dbus_connection_send(iface->con, msg, NULL);
+	else
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_go_neg_req - Signal P2P GO Negotiation Request RX
+ * @wpa_s: %wpa_supplicant network interface data
+ * @src: Source address of the message triggering this notification
+ * @dev_passwd_id: WPS Device Password Id
+ * @go_intent: Peer's GO Intent value
+ *
+ * Sends signal to notify that a peer P2P Device is requesting group owner
+ * negotiation with us.
+ */
+void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+				     const u8 *src, u16 dev_passwd_id,
+				     u8 go_intent)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	struct wpas_dbus_priv *iface;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+		    wpa_s->dbus_new_path, MAC2STR(src));
+	path = peer_obj_path;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "GONegotiationRequest");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					    &path) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16,
+					    &dev_passwd_id) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BYTE,
+					    &go_intent))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
+static int wpas_dbus_get_group_obj_path(struct wpa_supplicant *wpa_s,
+					const struct wpa_ssid *ssid,
+					char *group_obj_path)
+{
+	char group_name[3];
+
+	if (!wpa_s->dbus_new_path ||
+	    os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN))
+		return -1;
+
+	os_memcpy(group_name, ssid->ssid + P2P_WILDCARD_SSID_LEN, 2);
+	group_name[2] = '\0';
+
+	os_snprintf(group_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_P2P_GROUPS_PART "/%s",
+		    wpa_s->dbus_new_path, group_name);
+
+	return 0;
+}
+
+
+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
+ * irrespective of the role (client/GO) of the current device
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @ssid: SSID object
+ * @client: this device is P2P client
+ * @network_id: network id of the group started, use instead of ssid->id
+ *	to account for persistent groups
+ */
+void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
+					const struct wpa_ssid *ssid,
+					int client, int network_id)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *iface;
+	struct wpa_supplicant *parent;
+
+	parent = wpa_s->parent;
+	if (parent->p2p_mgmt)
+		parent = parent->parent;
+
+	iface = parent->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !parent->dbus_new_path || !wpa_s->dbus_new_path)
+		return;
+
+	if (wpa_s->dbus_groupobj_path == NULL)
+		return;
+
+	/* New interface has been created for this group */
+	msg = dbus_message_new_signal(parent->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "GroupStarted");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	/*
+	 * In case the device supports creating a separate interface the
+	 * DBus client will need to know the object path for the interface
+	 * object this group was created on, so include it here.
+	 */
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_object_path(&dict_iter,
+					      "interface_object",
+					      wpa_s->dbus_new_path) ||
+	    !wpa_dbus_dict_append_string(&dict_iter, "role",
+					 client ? "client" : "GO") ||
+	    !wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
+					      wpa_s->dbus_groupobj_path) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter)) {
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	} else {
+		dbus_connection_send(iface->con, msg, NULL);
+		if (client)
+			peer_groups_changed(wpa_s);
+	}
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_go_neg_resp - Emit GONegotiation Success/Failure signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @res: Result of the GO Neg Request
+ */
+void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
+				      struct p2p_go_neg_results *res)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	DBusMessageIter iter_dict_entry, iter_dict_val, iter_dict_array;
+	struct wpas_dbus_priv *iface;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+	dbus_int32_t freqs[P2P_MAX_CHANNELS];
+	dbus_int32_t *f_array = freqs;
+
+
+	iface = wpa_s->global->dbus;
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+
+	os_memset(freqs, 0, sizeof(freqs));
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+		    wpa_s->dbus_new_path, MAC2STR(res->peer_device_addr));
+	path = peer_obj_path;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      res->status ? "GONegotiationFailure" :
+						    "GONegotiationSuccess");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+					      path) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "status", res->status))
+		goto err;
+
+	if (!res->status) {
+		int i = 0;
+		int freq_list_num = 0;
+
+		if ((res->role_go &&
+		     !wpa_dbus_dict_append_string(&dict_iter, "passphrase",
+						  res->passphrase)) ||
+		    !wpa_dbus_dict_append_string(&dict_iter, "role_go",
+						 res->role_go ? "GO" :
+						 "client") ||
+		    !wpa_dbus_dict_append_int32(&dict_iter, "frequency",
+						res->freq) ||
+		    !wpa_dbus_dict_append_byte_array(&dict_iter, "ssid",
+						     (const char *) res->ssid,
+						     res->ssid_len) ||
+		    !wpa_dbus_dict_append_byte_array(&dict_iter,
+						     "peer_device_addr",
+						     (const char *)
+						     res->peer_device_addr,
+						     ETH_ALEN) ||
+		    !wpa_dbus_dict_append_byte_array(&dict_iter,
+						     "peer_interface_addr",
+						     (const char *)
+						     res->peer_interface_addr,
+						     ETH_ALEN) ||
+		    !wpa_dbus_dict_append_string(&dict_iter, "wps_method",
+						 p2p_wps_method_text(
+							 res->wps_method)))
+			goto err;
+
+		for (i = 0; i < P2P_MAX_CHANNELS; i++) {
+			if (res->freq_list[i]) {
+				freqs[i] = res->freq_list[i];
+				freq_list_num++;
+			}
+		}
+
+		if (!wpa_dbus_dict_begin_array(&dict_iter,
+					       "frequency_list",
+					       DBUS_TYPE_INT32_AS_STRING,
+					       &iter_dict_entry,
+					       &iter_dict_val,
+					       &iter_dict_array) ||
+		    !dbus_message_iter_append_fixed_array(&iter_dict_array,
+							  DBUS_TYPE_INT32,
+							  &f_array,
+							  freq_list_num) ||
+		    !wpa_dbus_dict_end_array(&dict_iter,
+					     &iter_dict_entry,
+					     &iter_dict_val,
+					     &iter_dict_array) ||
+		    !wpa_dbus_dict_append_int32(&dict_iter, "persistent_group",
+						res->persistent_group) ||
+		    !wpa_dbus_dict_append_uint32(&dict_iter,
+						 "peer_config_timeout",
+						 res->peer_config_timeout))
+			goto err;
+	}
+
+	if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
+		goto err;
+
+	dbus_connection_send(iface->con, msg, NULL);
+err:
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_invitation_result - Emit InvitationResult signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @status: Status of invitation process
+ * @bssid: Basic Service Set Identifier
+ */
+void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+					    int status, const u8 *bssid)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *iface;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	iface = wpa_s->global->dbus;
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "InvitationResult");
+
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "status", status) ||
+	    (bssid &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter, "BSSID",
+					      (const char *) bssid,
+					      ETH_ALEN)) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit a signal for a peer joining the group.
+ * The signal will carry path to the group member object
+ * constructed using p2p i/f addr used for connecting.
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @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 *peer_addr)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+	struct wpa_supplicant *parent;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	if (!wpa_s->dbus_groupobj_path)
+		return;
+
+	parent = wpa_s->parent;
+	if (parent->p2p_mgmt)
+		parent = parent->parent;
+	if (!parent->dbus_new_path)
+		return;
+
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+			"%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+			COMPACT_MACSTR,
+			parent->dbus_new_path, MAC2STR(peer_addr));
+
+	msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
+				      WPAS_DBUS_NEW_IFACE_P2P_GROUP,
+				      "PeerJoined");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	path = peer_obj_path;
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					    &path)) {
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	} else {
+		dbus_connection_send(iface->con, msg, NULL);
+		wpas_dbus_signal_peer_groups_changed(parent, peer_addr);
+	}
+	dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit a signal for a peer disconnecting the group.
+ * The signal will carry path to the group member object
+ * constructed using the P2P Device Address of the peer.
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @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 *peer_addr)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+	struct wpa_supplicant *parent;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	if (!wpa_s->dbus_groupobj_path)
+		return;
+
+	parent = wpa_s->parent;
+	if (parent->p2p_mgmt)
+		parent = parent->parent;
+	if (!parent->dbus_new_path)
+		return;
+
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+			"%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+			COMPACT_MACSTR,
+			parent->dbus_new_path, MAC2STR(peer_addr));
+
+	msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
+				      WPAS_DBUS_NEW_IFACE_P2P_GROUP,
+				      "PeerDisconnected");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	path = peer_obj_path;
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					    &path)) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: Failed to construct PeerDisconnected signal");
+	} else {
+		dbus_connection_send(iface->con, msg, NULL);
+		wpas_dbus_signal_peer_groups_changed(parent, peer_addr);
+	}
+	dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit a signal for a service discovery request.
+ * The signal will carry station address, frequency, dialog token,
+ * update indicator and it tlvs
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sa: station addr (p2p i/f) of the peer
+ * @dialog_token: service discovery request dialog token
+ * @update_indic: service discovery request update indicator
+ * @tlvs: service discovery request genrated byte array of tlvs
+ * @tlvs_len: service discovery request tlvs length
+ */
+void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
+				     int freq, const u8 *sa, u8 dialog_token,
+				     u16 update_indic, const u8 *tlvs,
+				     size_t tlvs_len)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *iface;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
+	/* Check if this is a known peer */
+	if (!p2p_peer_known(wpa_s->global->p2p, sa))
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "ServiceDiscoveryRequest");
+	if (msg == NULL)
+		return;
+
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+		    COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa));
+
+	path = peer_obj_path;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+					      path) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "frequency", freq) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "dialog_token",
+					dialog_token) ||
+	    !wpa_dbus_dict_append_uint16(&dict_iter, "update_indicator",
+					 update_indic) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "tlvs",
+					     (const char *) tlvs,
+					     tlvs_len) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit a signal for a service discovery response.
+ * The signal will carry station address, update indicator and it
+ * tlvs
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sa: station addr (p2p i/f) of the peer
+ * @update_indic: service discovery request update indicator
+ * @tlvs: service discovery request genrated byte array of tlvs
+ * @tlvs_len: service discovery request tlvs length
+ */
+void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
+				      const u8 *sa, u16 update_indic,
+				      const u8 *tlvs, size_t tlvs_len)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *iface;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
+	/* Check if this is a known peer */
+	if (!p2p_peer_known(wpa_s->global->p2p, sa))
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "ServiceDiscoveryResponse");
+	if (msg == NULL)
+		return;
+
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+		    COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa));
+
+	path = peer_obj_path;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+					      path) ||
+	    !wpa_dbus_dict_append_uint16(&dict_iter, "update_indicator",
+					 update_indic) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "tlvs",
+					     (const char *) tlvs,
+					     tlvs_len) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_persistent_group - Send a persistent group related
+ *	event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: new persistent group id
+ * @sig_name: signal name - PersistentGroupAdded, PersistentGroupRemoved
+ * @properties: determines if add second argument with object properties
+ *
+ * Notify listeners about an event related to persistent groups.
+ */
+static void wpas_dbus_signal_persistent_group(struct wpa_supplicant *wpa_s,
+					      int id, const char *sig_name,
+					      int properties)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	char pgrp_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
+	os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
+		    wpa_s->dbus_new_path, id);
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      sig_name);
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	path = pgrp_obj_path;
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					    &path) ||
+	    (properties &&
+	     !wpa_dbus_get_object_properties(
+		     iface, pgrp_obj_path,
+		     WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, &iter)))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_persistent_group_added - Send a persistent_group
+ *	added signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: new persistent group id
+ *
+ * Notify listeners about addition of a new persistent group.
+ */
+static void wpas_dbus_signal_persistent_group_added(
+	struct wpa_supplicant *wpa_s, int id)
+{
+	wpas_dbus_signal_persistent_group(wpa_s, id, "PersistentGroupAdded",
+					  TRUE);
+}
+
+
+/**
+ * wpas_dbus_signal_persistent_group_removed - Send a persistent_group
+ *	removed signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: persistent group id
+ *
+ * Notify listeners about removal of a persistent group.
+ */
+static void wpas_dbus_signal_persistent_group_removed(
+	struct wpa_supplicant *wpa_s, int id)
+{
+	wpas_dbus_signal_persistent_group(wpa_s, id, "PersistentGroupRemoved",
+					  FALSE);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_wps_failed - Signals WpsFailed event
+ * @wpa_s: %wpa_supplicant network interface data
+ * @fail: WPS failure information
+ *
+ * Sends Event dbus signal with name "fail" and dictionary containing
+ * "msg" field with fail message number (int32) as arguments
+ */
+void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+				     struct wps_event_fail *fail)
+{
+
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *iface;
+	char *key = "fail";
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+
+	if (!wpa_s->dbus_new_path)
+		return;
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "WpsFailed");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
+	    !wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "msg", fail->msg) ||
+	    !wpa_dbus_dict_append_int16(&dict_iter, "config_error",
+					fail->config_error) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_group_formation_failure - Signals GroupFormationFailure event
+ * @wpa_s: %wpa_supplicant network interface data
+ * @reason: indicates the reason code for group formation failure
+ *
+ * Sends Event dbus signal and string reason code when available.
+ */
+void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+						  const char *reason)
+{
+	DBusMessage *msg;
+	struct wpas_dbus_priv *iface;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "GroupFormationFailure");
+	if (msg == NULL)
+		return;
+
+	if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &reason,
+				     DBUS_TYPE_INVALID))
+		dbus_connection_send(iface->con, msg, NULL);
+	else
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_invitation_received - Emit InvitationReceived signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sa: Source address of the Invitation Request
+ * @dev_add: GO Device Address
+ * @bssid: P2P Group BSSID or %NULL if not received
+ * @id: Persistent group id or %0 if not persistent group
+ * @op_freq: Operating frequency for the group
+ */
+
+void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+					      const u8 *sa, const u8 *dev_addr,
+					      const u8 *bssid, int id,
+					      int op_freq)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *iface;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "InvitationReceived");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    (sa &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter, "sa",
+					      (const char *) sa, ETH_ALEN)) ||
+	    (dev_addr &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter, "go_dev_addr",
+					      (const char *) dev_addr,
+					      ETH_ALEN)) ||
+	    (bssid &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid",
+					      (const char *) bssid,
+					      ETH_ALEN)) ||
+	    (id &&
+	     !wpa_dbus_dict_append_int32(&dict_iter, "persistent_id", id)) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "op_freq", op_freq) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter)) {
+		dbus_message_unref(msg);
+		return;
+	}
+
+	dbus_connection_send(iface->con, msg, NULL);
+}
+
+
+#endif /* CONFIG_P2P */
+
+
+/**
+ * wpas_dbus_signal_state_changed - Signals change of state property
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Sends ProertyChanged signals immediately with path, interface and
+ * arguments for the supplicant state property. This is sent
+ * immediately so that supplicant state changes are NOT coaslesced
+ * which can lead to problems in higher-level network managers which
+ * depend on seeing all, specific state changes for correct operation.
+ */
+void wpas_dbus_signal_state_changed(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_dbus_object_desc *obj_desc = NULL;
+
+	if (wpa_s->dbus_new_path == NULL)
+		return; /* Skip signal since D-Bus setup is not yet ready */
+
+	obj_desc = wpa_dbus_mark_property_changed(wpa_s->global->dbus,
+						  wpa_s->dbus_new_path,
+						  WPAS_DBUS_NEW_IFACE_INTERFACE,
+						  "State");
+	if (!obj_desc)
+		return;
+
+	wpa_dbus_flush_object_changed_properties(wpa_s->global->dbus->con,
+						 obj_desc->path);
+}
+
+
+/**
+ * wpas_dbus_signal_prop_changed - Signals change of property
+ * @wpa_s: %wpa_supplicant network interface data
+ * @property: indicates which property has changed
+ *
+ * Sends PropertyChanged signals with path, interface and arguments
+ * depending on which property has changed.
+ */
+void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
+				   enum wpas_dbus_prop property)
+{
+	const char *prop;
+
+	if (wpa_s->dbus_new_path == NULL)
+		return; /* Skip signal since D-Bus setup is not yet ready */
+
+	switch (property) {
+	case WPAS_DBUS_PROP_AP_SCAN:
+		prop = "ApScan";
+		break;
+	case WPAS_DBUS_PROP_SCANNING:
+		prop = "Scanning";
+		break;
+	case WPAS_DBUS_PROP_STATE:
+		prop = "State";
+		break;
+	case WPAS_DBUS_PROP_CURRENT_BSS:
+		prop = "CurrentBSS";
+		break;
+	case WPAS_DBUS_PROP_CURRENT_NETWORK:
+		prop = "CurrentNetwork";
+		break;
+	case WPAS_DBUS_PROP_BSSS:
+		prop = "BSSs";
+		break;
+	case WPAS_DBUS_PROP_CURRENT_AUTH_MODE:
+		prop = "CurrentAuthMode";
+		break;
+	case WPAS_DBUS_PROP_DISCONNECT_REASON:
+		prop = "DisconnectReason";
+		break;
+	case WPAS_DBUS_PROP_ASSOC_STATUS_CODE:
+		prop = "AssocStatusCode";
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
+			   __func__, property);
+		return;
+	}
+
+	wpa_dbus_queue_property_changed(wpa_s->global->dbus,
+				        wpa_s->dbus_new_path,
+				        WPAS_DBUS_NEW_IFACE_INTERFACE, prop);
+}
+
+
+/**
+ * wpas_dbus_bss_signal_prop_changed - Signals change of BSS property
+ * @wpa_s: %wpa_supplicant network interface data
+ * @property: indicates which property has changed
+ * @id: unique BSS identifier
+ *
+ * Sends PropertyChanged signals with path, interface, and arguments depending
+ * on which property has changed.
+ */
+void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
+				       enum wpas_dbus_bss_prop property,
+				       unsigned int id)
+{
+	char path[WPAS_DBUS_OBJECT_PATH_MAX];
+	char *prop;
+
+	if (!wpa_s->dbus_new_path)
+		return;
+
+	switch (property) {
+	case WPAS_DBUS_BSS_PROP_SIGNAL:
+		prop = "Signal";
+		break;
+	case WPAS_DBUS_BSS_PROP_FREQ:
+		prop = "Frequency";
+		break;
+	case WPAS_DBUS_BSS_PROP_MODE:
+		prop = "Mode";
+		break;
+	case WPAS_DBUS_BSS_PROP_PRIVACY:
+		prop = "Privacy";
+		break;
+	case WPAS_DBUS_BSS_PROP_RATES:
+		prop = "Rates";
+		break;
+	case WPAS_DBUS_BSS_PROP_WPA:
+		prop = "WPA";
+		break;
+	case WPAS_DBUS_BSS_PROP_RSN:
+		prop = "RSN";
+		break;
+	case WPAS_DBUS_BSS_PROP_WPS:
+		prop = "WPS";
+		break;
+	case WPAS_DBUS_BSS_PROP_IES:
+		prop = "IEs";
+		break;
+	case WPAS_DBUS_BSS_PROP_AGE:
+		prop = "Age";
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
+			   __func__, property);
+		return;
+	}
+
+	os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
+		    wpa_s->dbus_new_path, id);
+
+	wpa_dbus_queue_property_changed(wpa_s->global->dbus, path,
+				        WPAS_DBUS_NEW_IFACE_BSS, prop);
+}
+
+
+/**
+ * wpas_dbus_signal_debug_level_changed - Signals change of debug param
+ * @global: wpa_global structure
+ *
+ * Sends PropertyChanged signals informing that debug level has changed.
+ */
+void wpas_dbus_signal_debug_level_changed(struct wpa_global *global)
+{
+	wpa_dbus_queue_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
+				        WPAS_DBUS_NEW_INTERFACE,
+				        "DebugLevel");
+}
+
+
+/**
+ * wpas_dbus_signal_debug_timestamp_changed - Signals change of debug param
+ * @global: wpa_global structure
+ *
+ * Sends PropertyChanged signals informing that debug timestamp has changed.
+ */
+void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global)
+{
+	wpa_dbus_queue_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
+				        WPAS_DBUS_NEW_INTERFACE,
+				        "DebugTimestamp");
+}
+
+
+/**
+ * wpas_dbus_signal_debug_show_keys_changed - Signals change of debug param
+ * @global: wpa_global structure
+ *
+ * Sends PropertyChanged signals informing that debug show_keys has changed.
+ */
+void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global)
+{
+	wpa_dbus_queue_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
+				        WPAS_DBUS_NEW_INTERFACE,
+				        "DebugShowKeys");
+}
+
+
+static void wpas_dbus_register(struct wpa_dbus_object_desc *obj_desc,
+			       void *priv,
+			       WPADBusArgumentFreeFunction priv_free,
+			       const struct wpa_dbus_method_desc *methods,
+			       const struct wpa_dbus_property_desc *properties,
+			       const struct wpa_dbus_signal_desc *signals)
+{
+	int n;
+
+	obj_desc->user_data = priv;
+	obj_desc->user_data_free_func = priv_free;
+	obj_desc->methods = methods;
+	obj_desc->properties = properties;
+	obj_desc->signals = signals;
+
+	for (n = 0; properties && properties->dbus_property; properties++)
+		n++;
+
+	obj_desc->prop_changed_flags = os_zalloc(n);
+	if (!obj_desc->prop_changed_flags)
+		wpa_printf(MSG_DEBUG, "dbus: %s: can't register handlers",
+			   __func__);
+}
+
+
+static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = {
+	{ "CreateInterface", WPAS_DBUS_NEW_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_create_interface,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  { "path", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "RemoveInterface", WPAS_DBUS_NEW_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_remove_interface,
+	  {
+		  { "path", "o", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "GetInterface", WPAS_DBUS_NEW_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_get_interface,
+	  {
+		  { "ifname", "s", ARG_IN },
+		  { "path", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ NULL, NULL, NULL, { END_ARGS } }
+};
+
+static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = {
+	{ "DebugLevel", WPAS_DBUS_NEW_INTERFACE, "s",
+	  wpas_dbus_getter_debug_level,
+	  wpas_dbus_setter_debug_level
+	},
+	{ "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b",
+	  wpas_dbus_getter_debug_timestamp,
+	  wpas_dbus_setter_debug_timestamp
+	},
+	{ "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b",
+	  wpas_dbus_getter_debug_show_keys,
+	  wpas_dbus_setter_debug_show_keys
+	},
+	{ "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao",
+	  wpas_dbus_getter_interfaces,
+	  NULL
+	},
+	{ "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as",
+	  wpas_dbus_getter_eap_methods,
+	  NULL
+	},
+	{ "Capabilities", WPAS_DBUS_NEW_INTERFACE, "as",
+	  wpas_dbus_getter_global_capabilities,
+	  NULL
+	},
+#ifdef CONFIG_WIFI_DISPLAY
+	{ "WFDIEs", WPAS_DBUS_NEW_INTERFACE, "ay",
+	  wpas_dbus_getter_global_wfd_ies,
+	  wpas_dbus_setter_global_wfd_ies
+	},
+#endif /* CONFIG_WIFI_DISPLAY */
+	{ NULL, NULL, NULL, NULL, NULL }
+};
+
+static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = {
+	{ "InterfaceAdded", WPAS_DBUS_NEW_INTERFACE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "InterfaceRemoved", WPAS_DBUS_NEW_INTERFACE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	/* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+	{ "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE,
+	  {
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ NULL, NULL, { END_ARGS } }
+};
+
+
+/**
+ * wpas_dbus_ctrl_iface_init - Initialize dbus control interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: 0 on success or -1 on failure
+ *
+ * Initialize the dbus control interface for wpa_supplicantand and start
+ * receiving commands from external programs over the bus.
+ */
+int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv)
+{
+	struct wpa_dbus_object_desc *obj_desc;
+	int ret;
+
+	obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+	if (!obj_desc) {
+		wpa_printf(MSG_ERROR,
+			   "Not enough memory to create object description");
+		return -1;
+	}
+
+	wpas_dbus_register(obj_desc, priv->global, NULL,
+			   wpas_dbus_global_methods,
+			   wpas_dbus_global_properties,
+			   wpas_dbus_global_signals);
+
+	wpa_printf(MSG_DEBUG, "dbus: Register D-Bus object '%s'",
+		   WPAS_DBUS_NEW_PATH);
+	ret = wpa_dbus_ctrl_iface_init(priv, WPAS_DBUS_NEW_PATH,
+				       WPAS_DBUS_NEW_SERVICE,
+				       obj_desc);
+	if (ret < 0)
+		free_dbus_object_desc(obj_desc);
+	else
+		priv->dbus_new_initialized = 1;
+
+	return ret;
+}
+
+
+/**
+ * wpas_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface for
+ * wpa_supplicant
+ * @iface: Pointer to dbus private data from wpas_dbus_init()
+ *
+ * Deinitialize the dbus control interface that was initialized with
+ * wpas_dbus_ctrl_iface_init().
+ */
+void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface)
+{
+	if (!iface->dbus_new_initialized)
+		return;
+	wpa_printf(MSG_DEBUG, "dbus: Unregister D-Bus object '%s'",
+		   WPAS_DBUS_NEW_PATH);
+	dbus_connection_unregister_object_path(iface->con,
+					       WPAS_DBUS_NEW_PATH);
+}
+
+
+static void wpa_dbus_free(void *ptr)
+{
+	os_free(ptr);
+}
+
+
+static const struct wpa_dbus_property_desc wpas_dbus_network_properties[] = {
+	{ "Properties", WPAS_DBUS_NEW_IFACE_NETWORK, "a{sv}",
+	  wpas_dbus_getter_network_properties,
+	  wpas_dbus_setter_network_properties
+	},
+	{ "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b",
+	  wpas_dbus_getter_enabled,
+	  wpas_dbus_setter_enabled
+	},
+	{ NULL, NULL, NULL, NULL, NULL }
+};
+
+
+static const struct wpa_dbus_signal_desc wpas_dbus_network_signals[] = {
+	/* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+	{ "PropertiesChanged", WPAS_DBUS_NEW_IFACE_NETWORK,
+	  {
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ NULL, NULL, { END_ARGS } }
+};
+
+
+/**
+ * wpas_dbus_register_network - Register a configured network with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @ssid: network configuration data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers network representing object with dbus
+ */
+int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
+			       struct wpa_ssid *ssid)
+{
+	struct wpas_dbus_priv *ctrl_iface;
+	struct wpa_dbus_object_desc *obj_desc;
+	struct network_handler_args *arg;
+	char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+#ifdef CONFIG_P2P
+	/*
+	 * If it is a persistent group register it as such.
+	 * This is to handle cases where an interface is being initialized
+	 * with a list of networks read from config.
+	 */
+	if (network_is_persistent_group(ssid))
+		return wpas_dbus_register_persistent_group(wpa_s, ssid);
+#endif /* CONFIG_P2P */
+
+	/* Do nothing if the control interface is not turned on */
+	if (wpa_s == NULL || wpa_s->global == NULL || !wpa_s->dbus_new_path)
+		return 0;
+	ctrl_iface = wpa_s->global->dbus;
+	if (ctrl_iface == NULL)
+		return 0;
+
+	os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+		    wpa_s->dbus_new_path, ssid->id);
+
+	wpa_printf(MSG_DEBUG, "dbus: Register network object '%s'",
+		   net_obj_path);
+	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 network_handler_args));
+	if (!arg) {
+		wpa_printf(MSG_ERROR,
+			   "Not enough memory to create arguments for method");
+		goto err;
+	}
+
+	arg->wpa_s = wpa_s;
+	arg->ssid = ssid;
+
+	wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
+			   wpas_dbus_network_properties,
+			   wpas_dbus_network_signals);
+
+	if (wpa_dbus_register_object_per_iface(ctrl_iface, net_obj_path,
+					       wpa_s->ifname, obj_desc))
+		goto err;
+
+	wpas_dbus_signal_network_added(wpa_s, ssid->id);
+
+	return 0;
+
+err:
+	free_dbus_object_desc(obj_desc);
+	return -1;
+}
+
+
+/**
+ * wpas_dbus_unregister_network - Unregister a configured network from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @nid: network id
+ * Returns: 0 on success, -1 on failure
+ *
+ * Unregisters network representing object from dbus
+ */
+int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid)
+{
+	struct wpas_dbus_priv *ctrl_iface;
+	char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+	int ret;
+#ifdef CONFIG_P2P
+	struct wpa_ssid *ssid;
+
+	ssid = wpa_config_get_network(wpa_s->conf, nid);
+
+	/* If it is a persistent group unregister it as such */
+	if (ssid && network_is_persistent_group(ssid))
+		return wpas_dbus_unregister_persistent_group(wpa_s, nid);
+#endif /* CONFIG_P2P */
+
+	/* Do nothing if the control interface is not turned on */
+	if (wpa_s->global == NULL || wpa_s->dbus_new_path == NULL)
+		return 0;
+	ctrl_iface = wpa_s->global->dbus;
+	if (ctrl_iface == NULL)
+		return 0;
+
+	os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+		    wpa_s->dbus_new_path, nid);
+
+	wpa_printf(MSG_DEBUG, "dbus: Unregister network object '%s'",
+		   net_obj_path);
+	ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, net_obj_path);
+
+	if (!ret)
+		wpas_dbus_signal_network_removed(wpa_s, nid);
+
+	return ret;
+}
+
+
+static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = {
+	{ "SSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
+	  wpas_dbus_getter_bss_ssid,
+	  NULL
+	},
+	{ "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
+	  wpas_dbus_getter_bss_bssid,
+	  NULL
+	},
+	{ "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b",
+	  wpas_dbus_getter_bss_privacy,
+	  NULL
+	},
+	{ "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s",
+	  wpas_dbus_getter_bss_mode,
+	  NULL
+	},
+	{ "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n",
+	  wpas_dbus_getter_bss_signal,
+	  NULL
+	},
+	{ "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q",
+	  wpas_dbus_getter_bss_frequency,
+	  NULL
+	},
+	{ "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au",
+	  wpas_dbus_getter_bss_rates,
+	  NULL
+	},
+	{ "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
+	  wpas_dbus_getter_bss_wpa,
+	  NULL
+	},
+	{ "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
+	  wpas_dbus_getter_bss_rsn,
+	  NULL
+	},
+	{ "WPS", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
+	  wpas_dbus_getter_bss_wps,
+	  NULL
+	},
+	{ "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay",
+	  wpas_dbus_getter_bss_ies,
+	  NULL
+	},
+	{ "Age", WPAS_DBUS_NEW_IFACE_BSS, "u",
+	  wpas_dbus_getter_bss_age,
+	  NULL
+	},
+	{ NULL, NULL, NULL, NULL, NULL }
+};
+
+
+static const struct wpa_dbus_signal_desc wpas_dbus_bss_signals[] = {
+	/* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+	{ "PropertiesChanged", WPAS_DBUS_NEW_IFACE_BSS,
+	  {
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ NULL, NULL, { END_ARGS } }
+};
+
+
+/**
+ * wpas_dbus_unregister_bss - Unregister a scanned BSS from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @bssid: scanned network bssid
+ * @id: unique BSS identifier
+ * Returns: 0 on success, -1 on failure
+ *
+ * Unregisters BSS representing object from dbus
+ */
+int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
+			     u8 bssid[ETH_ALEN], unsigned int id)
+{
+	struct wpas_dbus_priv *ctrl_iface;
+	char bss_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 || !wpa_s->dbus_new_path)
+		return 0;
+	ctrl_iface = wpa_s->global->dbus;
+	if (ctrl_iface == NULL)
+		return 0;
+
+	os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
+		    wpa_s->dbus_new_path, id);
+
+	wpa_printf(MSG_DEBUG, "dbus: Unregister BSS object '%s'",
+		   bss_obj_path);
+	if (wpa_dbus_unregister_object_per_iface(ctrl_iface, bss_obj_path)) {
+		wpa_printf(MSG_ERROR, "dbus: Cannot unregister BSS object %s",
+			   bss_obj_path);
+		return -1;
+	}
+
+	wpas_dbus_signal_bss_removed(wpa_s, bss_obj_path);
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSSS);
+
+	return 0;
+}
+
+
+/**
+ * wpas_dbus_register_bss - Register a scanned BSS with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @bssid: scanned network bssid
+ * @id: unique BSS identifier
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers BSS representing object with dbus
+ */
+int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
+			   u8 bssid[ETH_ALEN], unsigned int id)
+{
+	struct wpas_dbus_priv *ctrl_iface;
+	struct wpa_dbus_object_desc *obj_desc;
+	char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+	struct bss_handler_args *arg;
+
+	/* Do nothing if the control interface is not turned on */
+	if (wpa_s == NULL || wpa_s->global == NULL || !wpa_s->dbus_new_path)
+		return 0;
+	ctrl_iface = wpa_s->global->dbus;
+	if (ctrl_iface == NULL)
+		return 0;
+
+	os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
+		    wpa_s->dbus_new_path, id);
+
+	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;
+	}
+
+	arg = os_zalloc(sizeof(struct bss_handler_args));
+	if (!arg) {
+		wpa_printf(MSG_ERROR,
+			   "Not enough memory to create arguments for handler");
+		goto err;
+	}
+	arg->wpa_s = wpa_s;
+	arg->id = id;
+
+	wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
+			   wpas_dbus_bss_properties,
+			   wpas_dbus_bss_signals);
+
+	wpa_printf(MSG_DEBUG, "dbus: Register BSS object '%s'",
+		   bss_obj_path);
+	if (wpa_dbus_register_object_per_iface(ctrl_iface, bss_obj_path,
+					       wpa_s->ifname, obj_desc)) {
+		wpa_printf(MSG_ERROR,
+			   "Cannot register BSSID dbus object %s.",
+			   bss_obj_path);
+		goto err;
+	}
+
+	wpas_dbus_signal_bss_added(wpa_s, bss_obj_path);
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSSS);
+
+	return 0;
+
+err:
+	free_dbus_object_desc(obj_desc);
+	return -1;
+}
+
+
+static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
+	{ "Scan", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_scan,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "SignalPoll", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_signal_poll,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "Disconnect", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_disconnect,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "AddNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_add_network,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  { "path", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "Reassociate", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_reassociate,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "Reattach", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_reattach,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "Reconnect", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_reconnect,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_remove_network,
+	  {
+		  { "path", "o", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "RemoveAllNetworks", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_remove_all_networks,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "SelectNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_select_network,
+	  {
+		  { "path", "o", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "NetworkReply", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_network_reply,
+	  {
+		  { "path", "o", ARG_IN },
+		  { "field", "s", ARG_IN },
+		  { "value", "s", ARG_IN },
+		  END_ARGS
+	  }
+	},
+#ifndef CONFIG_NO_CONFIG_BLOBS
+	{ "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_add_blob,
+	  {
+		  { "name", "s", ARG_IN },
+		  { "data", "ay", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "GetBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_get_blob,
+	  {
+		  { "name", "s", ARG_IN },
+		  { "data", "ay", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "RemoveBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_remove_blob,
+	  {
+		  { "name", "s", ARG_IN },
+		  END_ARGS
+	  }
+	},
+#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,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  { "output", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "Cancel", WPAS_DBUS_NEW_IFACE_WPS,
+	  (WPADBusMethodHandler) wpas_dbus_handler_wps_cancel,
+	  {
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+	{ "Find", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_find,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "StopFind", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_stop_find,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "Listen", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_listen,
+	  {
+		  { "timeout", "i", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "ExtendedListen", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_extendedlisten,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "PresenceRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_presence_request,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "ProvisionDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_prov_disc_req,
+	  {
+		  { "peer", "o", ARG_IN },
+		  { "config_method", "s", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "Connect", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_connect,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  { "generated_pin", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "GroupAdd", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_group_add,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "Cancel", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_cancel,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "Invite", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_invite,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "Disconnect", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_disconnect,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "RejectPeer", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_rejectpeer,
+	  {
+		  { "peer", "o", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "RemoveClient", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_remove_client,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "Flush", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_flush,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "AddService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_add_service,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "DeleteService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_delete_service,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "FlushService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_flush_service,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_sd_req,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  { "ref", "t", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "ServiceDiscoveryResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_sd_res,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "ServiceDiscoveryCancelRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_sd_cancel_req,
+	  {
+		  { "args", "t", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "ServiceUpdate", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_update,
+	  {
+		  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,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  { "path", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "RemovePersistentGroup", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_remove_persistent_group,
+	  {
+		  { "path", "o", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "RemoveAllPersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler)
+	  wpas_dbus_handler_remove_all_persistent_groups,
+	  {
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_P2P */
+	{ "FlushBSS", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_flush_bss,
+	  {
+		  { "age", "u", ARG_IN },
+		  END_ARGS
+	  }
+	},
+#ifdef CONFIG_AP
+	{ "SubscribeProbeReq", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_subscribe_preq,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "UnsubscribeProbeReq", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_unsubscribe_preq,
+	  {
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_AP */
+	{ "EAPLogoff", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_eap_logoff,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "EAPLogon", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_eap_logon,
+	  {
+		  END_ARGS
+	  }
+	},
+#ifdef CONFIG_AUTOSCAN
+	{ "AutoScan", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_autoscan,
+	  {
+		  { "arg", "s", ARG_IN },
+		  END_ARGS
+	  }
+	},
+#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 } }
+};
+
+static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = {
+	{ "Capabilities", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}",
+	  wpas_dbus_getter_capabilities,
+	  NULL
+	},
+	{ "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+	  wpas_dbus_getter_state,
+	  NULL
+	},
+	{ "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
+	  wpas_dbus_getter_scanning,
+	  NULL
+	},
+	{ "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
+	  wpas_dbus_getter_ap_scan,
+	  wpas_dbus_setter_ap_scan
+	},
+	{ "BSSExpireAge", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
+	  wpas_dbus_getter_bss_expire_age,
+	  wpas_dbus_setter_bss_expire_age
+	},
+	{ "BSSExpireCount", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
+	  wpas_dbus_getter_bss_expire_count,
+	  wpas_dbus_setter_bss_expire_count
+	},
+	{ "Country", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+	  wpas_dbus_getter_country,
+	  wpas_dbus_setter_country
+	},
+	{ "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+	  wpas_dbus_getter_ifname,
+	  NULL
+	},
+	{ "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+	  wpas_dbus_getter_driver,
+	  NULL
+	},
+	{ "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+	  wpas_dbus_getter_bridge_ifname,
+	  NULL
+	},
+	{ "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
+	  wpas_dbus_getter_current_bss,
+	  NULL
+	},
+	{ "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
+	  wpas_dbus_getter_current_network,
+	  NULL
+	},
+	{ "CurrentAuthMode", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+	  wpas_dbus_getter_current_auth_mode,
+	  NULL
+	},
+	{ "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}",
+	  wpas_dbus_getter_blobs,
+	  NULL
+	},
+	{ "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
+	  wpas_dbus_getter_bsss,
+	  NULL
+	},
+	{ "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
+	  wpas_dbus_getter_networks,
+	  NULL
+	},
+	{ "FastReauth", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
+	  wpas_dbus_getter_fast_reauth,
+	  wpas_dbus_setter_fast_reauth
+	},
+	{ "ScanInterval", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
+	  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,
+	  wpas_dbus_setter_process_credentials
+	},
+	{ "ConfigMethods", WPAS_DBUS_NEW_IFACE_WPS, "s",
+	  wpas_dbus_getter_config_methods,
+	  wpas_dbus_setter_config_methods
+	},
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+	{ "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}",
+	  wpas_dbus_getter_p2p_device_config,
+	  wpas_dbus_setter_p2p_device_config
+	},
+	{ "Peers", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
+	  wpas_dbus_getter_p2p_peers,
+	  NULL
+	},
+	{ "Role", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "s",
+	  wpas_dbus_getter_p2p_role,
+	  NULL
+	},
+	{ "Group", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o",
+	  wpas_dbus_getter_p2p_group,
+	  NULL
+	},
+	{ "PeerGO", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o",
+	  wpas_dbus_getter_p2p_peergo,
+	  NULL
+	},
+	{ "PersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
+	  wpas_dbus_getter_persistent_groups,
+	  NULL
+	},
+#endif /* CONFIG_P2P */
+	{ "DisconnectReason", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
+	  wpas_dbus_getter_disconnect_reason,
+	  NULL
+	},
+	{ "AssocStatusCode", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
+	  wpas_dbus_getter_assoc_status_code,
+	  NULL
+	},
+	{ NULL, NULL, NULL, NULL, NULL }
+};
+
+static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
+	{ "ScanDone", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "success", "b", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "BSSAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "BSSRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "BlobAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "name", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "BlobRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "name", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NetworkAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NetworkRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NetworkSelected", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	/* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+	{ "PropertiesChanged", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+#ifdef CONFIG_WPS
+	{ "Event", WPAS_DBUS_NEW_IFACE_WPS,
+	  {
+		  { "name", "s", ARG_OUT },
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "Credentials", WPAS_DBUS_NEW_IFACE_WPS,
+	  {
+		  { "credentials", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	/* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+	{ "PropertiesChanged", WPAS_DBUS_NEW_IFACE_WPS,
+	  {
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+	{ "DeviceFound", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "DeviceLost", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "FindStopped", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "ProvisionDiscoveryRequestDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "peer_object", "o", ARG_OUT },
+		  { "pin", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "ProvisionDiscoveryResponseDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "peer_object", "o", ARG_OUT },
+		  { "pin", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "ProvisionDiscoveryRequestEnterPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "peer_object", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "ProvisionDiscoveryResponseEnterPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "peer_object", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "ProvisionDiscoveryPBCRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "peer_object", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "ProvisionDiscoveryPBCResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "peer_object", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "ProvisionDiscoveryFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "peer_object", "o", ARG_OUT },
+		  { "status", "i", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "GroupStarted", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "GroupFormationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "reason", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "GONegotiationSuccess", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "GONegotiationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "GONegotiationRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  { "dev_passwd_id", "q", ARG_OUT },
+		  { "device_go_intent", "y", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "InvitationResult", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "invite_result", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "GroupFinished", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "sd_request", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "ServiceDiscoveryResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "sd_response", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "PersistentGroupAdded", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "PersistentGroupRemoved", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "WpsFailed", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "name", "s", ARG_OUT },
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "InvitationReceived", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_AP
+	{ "ProbeRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_AP */
+	{ "Certification", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "certification", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "EAP", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "status", "s", ARG_OUT },
+		  { "parameter", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "StaAuthorized", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "name", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "StaDeauthorized", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "name", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NetworkRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  { "field", "s", ARG_OUT },
+		  { "text", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ NULL, NULL, { END_ARGS } }
+};
+
+
+/**
+ * wpas_dbus_register_interface - Register an interface with D-Bus
+ * @wpa_s: wpa_supplicant interface structure
+ * Returns: 0 on success, -1 on failure
+ */
+int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
+{
+
+	struct wpa_dbus_object_desc *obj_desc = NULL;
+	struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus;
+	int next;
+
+	/* Do nothing if the control interface is not turned on */
+	if (ctrl_iface == NULL)
+		return 0;
+
+	/* Create and set the interface's object path */
+	wpa_s->dbus_new_path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+	if (wpa_s->dbus_new_path == NULL)
+		return -1;
+	next = ctrl_iface->next_objid++;
+	os_snprintf(wpa_s->dbus_new_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    WPAS_DBUS_NEW_PATH_INTERFACES "/%u",
+		    next);
+
+	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;
+	}
+
+	wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods,
+			   wpas_dbus_interface_properties,
+			   wpas_dbus_interface_signals);
+
+	wpa_printf(MSG_DEBUG, "dbus: Register interface object '%s'",
+		   wpa_s->dbus_new_path);
+	if (wpa_dbus_register_object_per_iface(ctrl_iface,
+					       wpa_s->dbus_new_path,
+					       wpa_s->ifname, obj_desc))
+		goto err;
+
+	wpas_dbus_signal_interface_added(wpa_s);
+
+	return 0;
+
+err:
+	os_free(wpa_s->dbus_new_path);
+	wpa_s->dbus_new_path = NULL;
+	free_dbus_object_desc(obj_desc);
+	return -1;
+}
+
+
+/**
+ * wpas_dbus_unregister_interface - Unregister the interface from D-Bus
+ * @wpa_s: wpa_supplicant interface structure
+ * Returns: 0 on success, -1 on failure
+ */
+int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dbus_priv *ctrl_iface;
+
+	/* Do nothing if the control interface is not turned on */
+	if (wpa_s == NULL || wpa_s->global == NULL)
+		return 0;
+	ctrl_iface = wpa_s->global->dbus;
+	if (ctrl_iface == NULL || wpa_s->dbus_new_path == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'",
+		   wpa_s->dbus_new_path);
+
+#ifdef CONFIG_AP
+	if (wpa_s->preq_notify_peer) {
+		wpas_dbus_unsubscribe_noc(ctrl_iface);
+		os_free(wpa_s->preq_notify_peer);
+		wpa_s->preq_notify_peer = NULL;
+	}
+#endif /* CONFIG_AP */
+
+	if (wpa_dbus_unregister_object_per_iface(ctrl_iface,
+						 wpa_s->dbus_new_path))
+		return -1;
+
+	wpas_dbus_signal_interface_removed(wpa_s);
+
+	os_free(wpa_s->dbus_new_path);
+	wpa_s->dbus_new_path = NULL;
+
+	return 0;
+}
+
+#ifdef CONFIG_P2P
+
+static const struct wpa_dbus_property_desc wpas_dbus_p2p_peer_properties[] = {
+	{ "DeviceName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
+	  wpas_dbus_getter_p2p_peer_device_name,
+	  NULL
+	},
+	{ "Manufacturer", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
+	  wpas_dbus_getter_p2p_peer_manufacturer,
+	  NULL
+	},
+	{ "ModelName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
+	  wpas_dbus_getter_p2p_peer_modelname,
+	  NULL
+	},
+	{ "ModelNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
+	  wpas_dbus_getter_p2p_peer_modelnumber,
+	  NULL
+	},
+	{ "SerialNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
+	  wpas_dbus_getter_p2p_peer_serialnumber,
+	  NULL
+	},
+	{ "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
+	  wpas_dbus_getter_p2p_peer_primary_device_type,
+	  NULL
+	},
+	{ "config_method", WPAS_DBUS_NEW_IFACE_P2P_PEER, "q",
+	  wpas_dbus_getter_p2p_peer_config_method,
+	  NULL
+	},
+	{ "level", WPAS_DBUS_NEW_IFACE_P2P_PEER, "i",
+	  wpas_dbus_getter_p2p_peer_level,
+	  NULL
+	},
+	{ "devicecapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
+	  wpas_dbus_getter_p2p_peer_device_capability,
+	  NULL
+	},
+	{ "groupcapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
+	  wpas_dbus_getter_p2p_peer_group_capability,
+	  NULL
+	},
+	{ "SecondaryDeviceTypes", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
+	  wpas_dbus_getter_p2p_peer_secondary_device_types,
+	  NULL
+	},
+	{ "VendorExtension", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
+	  wpas_dbus_getter_p2p_peer_vendor_extension,
+	  NULL
+	},
+	{ "IEs", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
+	  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 } }
+};
+
+/**
+ * wpas_dbus_signal_peer - Send a peer related event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @dev: peer device object
+ * @interface: name of the interface emitting this signal.
+ *	In case of peer objects, it would be emitted by either
+ *	the "interface object" or by "peer objects"
+ * @sig_name: signal name - DeviceFound
+ *
+ * Notify listeners about event related with newly found p2p peer device
+ */
+static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s,
+				  const u8 *dev_addr, const char *interface,
+				  const char *sig_name)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+		    wpa_s->dbus_new_path, MAC2STR(dev_addr));
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path, interface,
+				      sig_name);
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	path = peer_obj_path;
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					    &path))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_peer_found - Send a peer found signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @dev_addr: Peer P2P Device Address
+ *
+ * Notify listeners about find a p2p peer device found
+ */
+void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
+					const u8 *dev_addr)
+{
+	wpas_dbus_signal_peer(wpa_s, dev_addr,
+			      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+			      "DeviceFound");
+}
+
+/**
+ * wpas_dbus_signal_peer_lost - Send a peer lost signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @dev_addr: Peer P2P Device Address
+ *
+ * Notify listeners about lost a p2p peer device
+ */
+void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
+				       const u8 *dev_addr)
+{
+	wpas_dbus_signal_peer(wpa_s, dev_addr,
+			      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+			      "DeviceLost");
+}
+
+/**
+ * wpas_dbus_register_peer - Register a discovered peer object with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @dev_addr: P2P Device Address of the peer
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers network representing object with dbus
+ */
+int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr)
+{
+	struct wpas_dbus_priv *ctrl_iface;
+	struct wpa_dbus_object_desc *obj_desc;
+	struct peer_handler_args *arg;
+	char peer_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 0;
+
+	ctrl_iface = wpa_s->global->dbus;
+	if (ctrl_iface == NULL)
+		return 0;
+
+	wpa_s = wpa_s->parent->parent;
+	if (!wpa_s->dbus_new_path)
+		return 0;
+
+	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_printf(MSG_INFO, "dbus: Register peer object '%s'",
+		   peer_obj_path);
+	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 peer_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->p2p_device_addr, dev_addr, ETH_ALEN);
+
+	wpas_dbus_register(obj_desc, arg, wpa_dbus_free,
+			   NULL,
+			   wpas_dbus_p2p_peer_properties,
+			   wpas_dbus_p2p_peer_signals);
+
+	if (wpa_dbus_register_object_per_iface(ctrl_iface, peer_obj_path,
+					       wpa_s->ifname, obj_desc))
+		goto err;
+
+	return 0;
+
+err:
+	free_dbus_object_desc(obj_desc);
+	return -1;
+}
+
+/**
+ * wpas_dbus_unregister_peer - Unregister a peer object with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @dev_addr: p2p device addr
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers network representing object with dbus
+ */
+int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
+				  const u8 *dev_addr)
+{
+	struct wpas_dbus_priv *ctrl_iface;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+	int ret;
+
+	/* Do nothing if the control interface is not turned on */
+	if (wpa_s == NULL || wpa_s->global == NULL)
+		return 0;
+
+	wpa_s = wpa_s->parent->parent;
+	if (!wpa_s->dbus_new_path)
+		return 0;
+
+	ctrl_iface = wpa_s->global->dbus;
+	if (ctrl_iface == NULL)
+		return 0;
+
+	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_printf(MSG_INFO, "dbus: Unregister peer object '%s'",
+		   peer_obj_path);
+	ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, peer_obj_path);
+
+	return ret;
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_find_stopped - Send P2P Find stopped signal
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Notify listeners about P2P Find stopped
+ */
+void wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "FindStopped");
+	if (msg == NULL)
+		return;
+
+	dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_peer_groups_changed - Send peer group change property signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @dev_addr: P2P Device Address
+ *
+ * Notify listeners about peer Groups property changes.
+ */
+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];
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+
+	if (!wpa_s->dbus_new_path)
+		return;
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+		    wpa_s->dbus_new_path, MAC2STR(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,
+	  NULL
+	},
+	{ "Group", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "o",
+	  wpas_dbus_getter_p2p_group,
+	  NULL
+	},
+	{ "Role", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
+	  wpas_dbus_getter_p2p_role,
+	  NULL
+	},
+	{ "SSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+	  wpas_dbus_getter_p2p_group_ssid,
+	  NULL
+	},
+	{ "BSSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+	  wpas_dbus_getter_p2p_group_bssid,
+	  NULL
+	},
+	{ "Frequency", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "q",
+	  wpas_dbus_getter_p2p_group_frequency,
+	  NULL
+	},
+	{ "Passphrase", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
+	  wpas_dbus_getter_p2p_group_passphrase,
+	  NULL
+	},
+	{ "PSK", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+	  wpas_dbus_getter_p2p_group_psk,
+	  NULL
+	},
+	{ "WPSVendorExtensions", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "aay",
+	  wpas_dbus_getter_p2p_group_vendor_ext,
+	  wpas_dbus_setter_p2p_group_vendor_ext
+	},
+	{ NULL, NULL, NULL, NULL, NULL }
+};
+
+static const struct wpa_dbus_signal_desc wpas_dbus_p2p_group_signals[] = {
+	{ "PeerJoined", WPAS_DBUS_NEW_IFACE_P2P_GROUP,
+	  {
+		  { "peer", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "PeerDisconnected", WPAS_DBUS_NEW_IFACE_P2P_GROUP,
+	  {
+		  { "peer", "o", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ NULL, NULL, { END_ARGS } }
+};
+
+/**
+ * wpas_dbus_register_p2p_group - Register a p2p group object with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @ssid: SSID struct
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers p2p group representing object with dbus
+ */
+void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
+				  struct wpa_ssid *ssid)
+{
+	struct wpas_dbus_priv *ctrl_iface;
+	struct wpa_dbus_object_desc *obj_desc;
+	char group_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) {
+		wpa_printf(MSG_INFO, "%s: Group object '%s' already exists",
+			   __func__, wpa_s->dbus_groupobj_path);
+		return;
+	}
+
+	if (wpas_dbus_get_group_obj_path(wpa_s, ssid, group_obj_path) < 0)
+		return;
+
+	wpa_s->dbus_groupobj_path = os_strdup(group_obj_path);
+	if (wpa_s->dbus_groupobj_path == NULL)
+		return;
+
+	wpa_printf(MSG_INFO, "dbus: Register group object '%s'",
+		   group_obj_path);
+	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;
+	}
+
+	wpas_dbus_register(obj_desc, wpa_s, NULL, NULL,
+			   wpas_dbus_p2p_group_properties,
+			   wpas_dbus_p2p_group_signals);
+
+	if (wpa_dbus_register_object_per_iface(ctrl_iface, group_obj_path,
+					       wpa_s->ifname, obj_desc))
+		goto err;
+
+	return;
+
+err:
+	if (wpa_s->dbus_groupobj_path) {
+		os_free(wpa_s->dbus_groupobj_path);
+		wpa_s->dbus_groupobj_path = NULL;
+	}
+
+	free_dbus_object_desc(obj_desc);
+}
+
+/**
+ * wpas_dbus_unregister_p2p_group - Unregister a p2p group object from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @ssid: network name of the p2p group started
+ */
+void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
+				    const struct wpa_ssid *ssid)
+{
+	struct wpas_dbus_priv *ctrl_iface;
+
+	/* Do nothing if the control interface is not turned on */
+	if (wpa_s == NULL || wpa_s->global == NULL)
+		return;
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+
+	ctrl_iface = wpa_s->global->dbus;
+	if (ctrl_iface == NULL)
+		return;
+
+	if (!wpa_s->dbus_groupobj_path) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Group object '%s' already unregistered",
+			   __func__, wpa_s->dbus_groupobj_path);
+		return;
+	}
+
+	peer_groups_changed(wpa_s);
+
+	wpa_printf(MSG_DEBUG, "dbus: Unregister group object '%s'",
+		   wpa_s->dbus_groupobj_path);
+
+	wpa_dbus_unregister_object_per_iface(ctrl_iface,
+					     wpa_s->dbus_groupobj_path);
+
+	os_free(wpa_s->dbus_groupobj_path);
+	wpa_s->dbus_groupobj_path = NULL;
+}
+
+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,
+	  wpas_dbus_setter_persistent_group_properties
+	},
+	{ NULL, NULL, NULL, NULL, NULL }
+};
+
+/* No signals intended for persistent group objects */
+
+/**
+ * wpas_dbus_register_persistent_group - Register a configured(saved)
+ *	persistent group with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @ssid: persistent group (still represented as a network within wpa)
+ *	  configuration data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers a persistent group representing object with dbus.
+ */
+int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s,
+					struct wpa_ssid *ssid)
+{
+	struct wpas_dbus_priv *ctrl_iface;
+	struct wpa_dbus_object_desc *obj_desc;
+	struct network_handler_args *arg;
+	char pgrp_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 0;
+	wpa_s = wpa_s->parent->parent;
+	if (!wpa_s->dbus_new_path)
+		return 0;
+
+	/* Make sure ssid is a persistent group */
+	if (ssid->disabled != 2 && !ssid->p2p_persistent_group)
+		return -1; /* should we return w/o complaining? */
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+
+	ctrl_iface = wpa_s->global->dbus;
+	if (ctrl_iface == NULL)
+		return 0;
+
+	/*
+	 * Intentionally not coming up with different numbering scheme
+	 * for persistent groups.
+	 */
+	os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
+		    wpa_s->dbus_new_path, ssid->id);
+
+	wpa_printf(MSG_DEBUG, "dbus: Register persistent group object '%s'",
+		   pgrp_obj_path);
+	obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+	if (!obj_desc) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory to create object description");
+		goto err;
+	}
+
+	/*
+	 * Reusing the same context structure as that for networks
+	 * since these are represented using same data structure.
+	 */
+	/* allocate memory for handlers arguments */
+	arg = os_zalloc(sizeof(struct network_handler_args));
+	if (!arg) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory to create arguments for method");
+		goto err;
+	}
+
+	arg->wpa_s = wpa_s;
+	arg->ssid = ssid;
+
+	wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
+			   wpas_dbus_persistent_group_properties,
+			   NULL);
+
+	if (wpa_dbus_register_object_per_iface(ctrl_iface, pgrp_obj_path,
+					       wpa_s->ifname, obj_desc))
+		goto err;
+
+	wpas_dbus_signal_persistent_group_added(wpa_s, ssid->id);
+
+	return 0;
+
+err:
+	free_dbus_object_desc(obj_desc);
+	return -1;
+}
+
+
+/**
+ * wpas_dbus_unregister_persistent_group - Unregister a persistent_group
+ *	from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @nid: network id
+ * Returns: 0 on success, -1 on failure
+ *
+ * Unregisters persistent group representing object from dbus
+ *
+ * NOTE: There is a slight issue with the semantics here. While the
+ * implementation simply means the persistent group is unloaded from memory,
+ * it should not get interpreted as the group is actually being erased/removed
+ * from persistent storage as well.
+ */
+int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s,
+					  int nid)
+{
+	struct wpas_dbus_priv *ctrl_iface;
+	char pgrp_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+	int ret;
+
+	/* Do nothing if the control interface is not turned on */
+	if (wpa_s == NULL || wpa_s->global == NULL)
+		return 0;
+
+	wpa_s = wpa_s->parent->parent;
+
+	ctrl_iface = wpa_s->global->dbus;
+	if (ctrl_iface == NULL || !wpa_s->dbus_new_path)
+		return 0;
+
+	os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
+		    wpa_s->dbus_new_path, nid);
+
+	wpa_printf(MSG_DEBUG, "dbus: Unregister persistent group object '%s'",
+		   pgrp_obj_path);
+	ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, pgrp_obj_path);
+
+	if (!ret)
+		wpas_dbus_signal_persistent_group_removed(wpa_s, nid);
+
+	return ret;
+}
+
+#endif /* CONFIG_P2P */
diff --git a/hostap/wpa_supplicant/dbus/dbus_new.h b/hostap/wpa_supplicant/dbus/dbus_new.h
new file mode 100644
index 0000000..79e893a
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_new.h
@@ -0,0 +1,559 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CTRL_IFACE_DBUS_NEW_H
+#define CTRL_IFACE_DBUS_NEW_H
+
+#include "common/defs.h"
+#include "p2p/p2p.h"
+
+struct wpa_global;
+struct wpa_supplicant;
+struct wpa_ssid;
+struct wps_event_m2d;
+struct wps_event_fail;
+struct wps_credential;
+
+enum wpas_dbus_prop {
+	WPAS_DBUS_PROP_AP_SCAN,
+	WPAS_DBUS_PROP_SCANNING,
+	WPAS_DBUS_PROP_STATE,
+	WPAS_DBUS_PROP_CURRENT_BSS,
+	WPAS_DBUS_PROP_CURRENT_NETWORK,
+	WPAS_DBUS_PROP_CURRENT_AUTH_MODE,
+	WPAS_DBUS_PROP_BSSS,
+	WPAS_DBUS_PROP_DISCONNECT_REASON,
+	WPAS_DBUS_PROP_ASSOC_STATUS_CODE,
+};
+
+enum wpas_dbus_bss_prop {
+	WPAS_DBUS_BSS_PROP_SIGNAL,
+	WPAS_DBUS_BSS_PROP_FREQ,
+	WPAS_DBUS_BSS_PROP_MODE,
+	WPAS_DBUS_BSS_PROP_PRIVACY,
+	WPAS_DBUS_BSS_PROP_RATES,
+	WPAS_DBUS_BSS_PROP_WPA,
+	WPAS_DBUS_BSS_PROP_RSN,
+	WPAS_DBUS_BSS_PROP_WPS,
+	WPAS_DBUS_BSS_PROP_IES,
+	WPAS_DBUS_BSS_PROP_AGE,
+};
+
+#define WPAS_DBUS_OBJECT_PATH_MAX 150
+
+#define WPAS_DBUS_NEW_SERVICE		"fi.w1.wpa_supplicant1"
+#define WPAS_DBUS_NEW_PATH		"/fi/w1/wpa_supplicant1"
+#define WPAS_DBUS_NEW_INTERFACE		"fi.w1.wpa_supplicant1"
+
+#define WPAS_DBUS_NEW_PATH_INTERFACES	WPAS_DBUS_NEW_PATH "/Interfaces"
+#define WPAS_DBUS_NEW_IFACE_INTERFACE	WPAS_DBUS_NEW_INTERFACE ".Interface"
+#define WPAS_DBUS_NEW_IFACE_WPS WPAS_DBUS_NEW_IFACE_INTERFACE ".WPS"
+
+#define WPAS_DBUS_NEW_NETWORKS_PART "Networks"
+#define WPAS_DBUS_NEW_IFACE_NETWORK WPAS_DBUS_NEW_INTERFACE ".Network"
+
+#define WPAS_DBUS_NEW_BSSIDS_PART "BSSs"
+#define WPAS_DBUS_NEW_IFACE_BSS	WPAS_DBUS_NEW_INTERFACE ".BSS"
+
+#define WPAS_DBUS_NEW_IFACE_P2PDEVICE	\
+		WPAS_DBUS_NEW_IFACE_INTERFACE ".P2PDevice"
+
+/*
+ * Groups correspond to P2P groups where this device is a GO (owner)
+ */
+#define WPAS_DBUS_NEW_P2P_GROUPS_PART	"Groups"
+#define	WPAS_DBUS_NEW_IFACE_P2P_GROUP WPAS_DBUS_NEW_INTERFACE ".Group"
+
+/*
+ * Different dbus object for persistent groups so they do not get confused
+ * with regular (configured) network objects.
+ */
+#define WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "PersistentGroups"
+#define WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP \
+	WPAS_DBUS_NEW_INTERFACE ".PersistentGroup"
+
+#define WPAS_DBUS_NEW_P2P_PEERS_PART	"Peers"
+#define	WPAS_DBUS_NEW_IFACE_P2P_PEER WPAS_DBUS_NEW_INTERFACE ".Peer"
+
+/* Top-level Errors */
+#define WPAS_DBUS_ERROR_UNKNOWN_ERROR \
+	WPAS_DBUS_NEW_INTERFACE ".UnknownError"
+#define WPAS_DBUS_ERROR_INVALID_ARGS \
+	WPAS_DBUS_NEW_INTERFACE ".InvalidArgs"
+
+#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"
+
+#define WPAS_DBUS_ERROR_NOT_CONNECTED \
+	WPAS_DBUS_NEW_INTERFACE ".NotConnected"
+#define WPAS_DBUS_ERROR_NETWORK_UNKNOWN \
+	WPAS_DBUS_NEW_INTERFACE ".NetworkUnknown"
+
+#define WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE \
+	WPAS_DBUS_NEW_INTERFACE ".ConnectChannelUnavailable"
+#define WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNSUPPORTED \
+	WPAS_DBUS_NEW_INTERFACE ".ConnectChannelUnsupported"
+#define WPAS_DBUS_ERROR_CONNECT_UNSPECIFIED_ERROR \
+	WPAS_DBUS_NEW_INTERFACE ".ConnectUnspecifiedError"
+
+#define WPAS_DBUS_ERROR_BLOB_EXISTS \
+	WPAS_DBUS_NEW_INTERFACE ".BlobExists"
+#define WPAS_DBUS_ERROR_BLOB_UNKNOWN \
+	WPAS_DBUS_NEW_INTERFACE ".BlobUnknown"
+
+#define WPAS_DBUS_ERROR_SUBSCRIPTION_IN_USE \
+	WPAS_DBUS_NEW_INTERFACE ".SubscriptionInUse"
+#define WPAS_DBUS_ERROR_NO_SUBSCRIPTION \
+	WPAS_DBUS_NEW_INTERFACE ".NoSubscription"
+#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);
+
+
+#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+
+int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv);
+void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface);
+
+int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s);
+int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s);
+void wpas_dbus_signal_state_changed(struct wpa_supplicant *wpa_s);
+void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
+				   enum wpas_dbus_prop property);
+void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
+				       enum wpas_dbus_bss_prop property,
+				       unsigned int id);
+void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s,
+					      struct wpa_ssid *ssid);
+void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id);
+void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s,
+				      struct wpa_ssid *ssid,
+				      enum wpa_ctrl_req_type rtype,
+				      const char *default_text);
+void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success);
+void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
+			       const struct wps_credential *cred);
+void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s,
+				    struct wps_event_m2d *m2d);
+void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s,
+				     struct wps_event_fail *fail);
+void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s);
+void wpas_dbus_signal_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s);
+int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
+			       struct wpa_ssid *ssid);
+int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid);
+int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
+			     u8 bssid[ETH_ALEN], unsigned int id);
+int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
+			   u8 bssid[ETH_ALEN], unsigned int id);
+void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s,
+				 const char *name);
+void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s,
+				   const char *name);
+void wpas_dbus_signal_debug_level_changed(struct wpa_global *global);
+void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global);
+void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global);
+
+int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr);
+void wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s);
+void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
+					   const u8 *dev_addr);
+int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
+				  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,
+					      const u8 *dev_addr, int request,
+					      enum p2p_prov_disc_status status,
+					      u16 config_methods,
+					      unsigned int generated_pin);
+void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+				     const u8 *src, u16 dev_passwd_id,
+				     u8 go_intent);
+void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
+					const struct wpa_ssid *ssid,
+					int client, int network_id);
+void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+						  const char *reason);
+void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
+				  struct wpa_ssid *ssid);
+void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
+				      struct p2p_go_neg_results *res);
+void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
+				    const struct wpa_ssid *ssid);
+int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s,
+					struct wpa_ssid *ssid);
+int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s,
+					  int nid);
+void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+					    int status, const u8 *bssid);
+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,
+				     int freq, const u8 *sa, u8 dialog_token,
+				     u16 update_indic, const u8 *tlvs,
+				     size_t tlvs_len);
+void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
+				      const u8 *sa, u16 update_indic,
+				      const u8 *tlvs, size_t tlvs_len);
+void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
+				const u8 *member);
+void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+				     struct wps_event_fail *fail);
+void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
+				    int depth, const char *subject,
+				    const char *altsubject[],
+				    int num_altsubject,
+				    const char *cert_hash,
+				    const struct wpabuf *cert);
+void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s,
+			   const u8 *addr, const u8 *dst, const u8 *bssid,
+			   const u8 *ie, size_t ie_len, u32 ssi_signal);
+void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
+				 const char *status, const char *parameter);
+void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s,
+				     const u8 *sta);
+void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s,
+				       const u8 *sta);
+void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+					      const u8 *sa, const u8 *dev_addr,
+					      const u8 *bssid, int id,
+					      int op_freq);
+
+#else /* CONFIG_CTRL_IFACE_DBUS_NEW */
+
+static inline int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+
+static inline int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+
+#define wpas_dbus_signal_state_changed(w) do { } while (0)
+
+static inline void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
+						 enum wpas_dbus_prop property)
+{
+}
+
+static inline void wpas_dbus_bss_signal_prop_changed(
+	struct wpa_supplicant *wpa_s, enum wpas_dbus_bss_prop property,
+	unsigned int id)
+{
+}
+
+static inline void wpas_dbus_signal_network_enabled_changed(
+	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+}
+
+static inline void wpas_dbus_signal_network_selected(
+	struct wpa_supplicant *wpa_s, int id)
+{
+}
+
+static inline void wpas_dbus_signal_network_request(
+	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+	enum wpa_ctrl_req_type rtype, const char *default_txt)
+{
+}
+
+static inline void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s,
+					      int success)
+{
+}
+
+static inline void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
+					     const struct wps_credential *cred)
+{
+}
+
+static inline void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s,
+						  struct wps_event_m2d *m2d)
+{
+}
+
+static inline void wpas_dbus_signal_wps_event_fail(
+	struct wpa_supplicant *wpa_s, struct wps_event_fail *fail)
+{
+}
+
+static inline void wpas_dbus_signal_wps_event_success(
+	struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_dbus_signal_wps_event_pbc_overlap(
+	struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
+					     struct wpa_ssid *ssid)
+{
+	return 0;
+}
+
+static inline int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s,
+					       int nid)
+{
+	return 0;
+}
+
+static inline int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
+					   u8 bssid[ETH_ALEN], unsigned int id)
+{
+	return 0;
+}
+
+static inline int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
+					 u8 bssid[ETH_ALEN], unsigned int id)
+{
+	return 0;
+}
+
+static inline void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s,
+					       const char *name)
+{
+}
+
+static inline void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s,
+						 const char *name)
+{
+}
+
+static inline void wpas_dbus_signal_debug_level_changed(
+	struct wpa_global *global)
+{
+}
+
+static inline void wpas_dbus_signal_debug_timestamp_changed(
+	struct wpa_global *global)
+{
+}
+
+static inline void wpas_dbus_signal_debug_show_keys_changed(
+	struct wpa_global *global)
+{
+}
+
+static inline int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s,
+					  const u8 *dev_addr)
+{
+	return 0;
+}
+
+static inline int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
+					    const u8 *dev_addr)
+{
+	return 0;
+}
+
+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)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+					 const u8 *dev_addr, int request,
+					 enum p2p_prov_disc_status status,
+					 u16 config_methods,
+					 unsigned int generated_pin)
+{
+}
+
+static inline void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+						   const u8 *src,
+						   u16 dev_passwd_id,
+						   u8 go_intent)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
+				   const struct wpa_ssid *ssid,
+				   int client, int network_id)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+					     const char *reason)
+{
+}
+
+static inline void
+wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
+			     struct wpa_ssid *ssid)
+{
+}
+
+static inline int wpas_dbus_register_persistent_group(
+	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	return 0;
+}
+
+static inline int wpas_dbus_unregister_persistent_group(
+	struct wpa_supplicant *wpa_s, int nid)
+{
+	return 0;
+}
+
+static inline void
+wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
+				 struct p2p_go_neg_results *res)
+{
+}
+
+static inline void
+wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
+			       const struct wpa_ssid *ssid)
+{
+}
+
+static inline void wpas_dbus_signal_p2p_invitation_result(
+				struct wpa_supplicant *wpa_s, int status,
+				const u8 *bssid)
+{
+}
+
+static inline void
+wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
+				   const u8 *p2p_if_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, int freq,
+				const u8 *sa, u8 dialog_token, u16 update_indic,
+				const u8 *tlvs, size_t tlvs_len)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
+				 const u8 *sa, u16 update_indic,
+				 const u8 *tlvs, size_t tlvs_len)
+{
+}
+
+static inline void
+wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
+				     const u8 *p2p_if_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
+				 const u8 *member)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void
+wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
+				   const u8 *dev_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
+				  const u8 *dev_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
+				       const u8 *member)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+				struct wps_event_fail *fail)
+{
+}
+
+static inline void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
+						  int depth,
+						  const char *subject,
+						  const char *altsubject[],
+						  int num_altsubject,
+						  const char *cert_hash,
+						  const struct wpabuf *cert)
+{
+}
+
+static inline void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s,
+					 const u8 *addr, const u8 *dst,
+					 const u8 *bssid,
+					 const u8 *ie, size_t ie_len,
+					 u32 ssi_signal)
+{
+}
+
+static inline void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
+					       const char *status,
+					       const char *parameter)
+{
+}
+
+static inline
+void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s,
+				     const u8 *sta)
+{
+}
+
+static inline
+void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s,
+				       const u8 *sta)
+{
+}
+
+static inline
+void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+					      const u8 *sa, const u8 *dev_addr,
+					      const u8 *bssid, int id,
+					      int op_freq)
+{
+}
+
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+
+#endif /* CTRL_IFACE_DBUS_H_NEW */
diff --git a/hostap/wpa_supplicant/dbus/dbus_new_handlers.c b/hostap/wpa_supplicant/dbus/dbus_new_handlers.c
new file mode 100644
index 0000000..689b0e1
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -0,0 +1,4233 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
+ * Copyright (c) 2009-2015, 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 "common/ieee802_11_defs.h"
+#include "eap_peer/eap_methods.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "../config.h"
+#include "../wpa_supplicant_i.h"
+#include "../driver_i.h"
+#include "../notify.h"
+#include "../bss.h"
+#include "../scan.h"
+#include "../autoscan.h"
+#include "dbus_new_helpers.h"
+#include "dbus_new.h"
+#include "dbus_new_handlers.h"
+#include "dbus_dict_helpers.h"
+#include "dbus_common_i.h"
+#include "drivers/driver.h"
+
+static const char * const debug_strings[] = {
+	"excessive", "msgdump", "debug", "info", "warning", "error", NULL
+};
+
+
+/**
+ * wpas_dbus_error_unknown_error - Return a new UnknownError error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * @arg: Optional string appended to error message
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an UnknownError
+ */
+DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
+					    const char *arg)
+{
+	return dbus_message_new_error(message, WPAS_DBUS_ERROR_UNKNOWN_ERROR,
+				      arg);
+}
+
+
+/**
+ * wpas_dbus_error_iface_unknown - Return a new invalid interface error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: A dbus error message
+ *
+ * Convenience function to create and return an invalid interface error
+ */
+static DBusMessage * wpas_dbus_error_iface_unknown(DBusMessage *message)
+{
+	return dbus_message_new_error(
+		message, WPAS_DBUS_ERROR_IFACE_UNKNOWN,
+		"wpa_supplicant knows nothing about this interface.");
+}
+
+
+/**
+ * wpas_dbus_error_network_unknown - Return a new NetworkUnknown error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an invalid network error
+ */
+static DBusMessage * wpas_dbus_error_network_unknown(DBusMessage *message)
+{
+	return dbus_message_new_error(
+		message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN,
+		"There is no such a network in this interface.");
+}
+
+
+/**
+ * wpas_dbus_error_invalid_args - Return a new InvalidArgs error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an invalid options error
+ */
+DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
+					  const char *arg)
+{
+	DBusMessage *reply;
+
+	reply = dbus_message_new_error(
+		message, WPAS_DBUS_ERROR_INVALID_ARGS,
+		"Did not receive correct message arguments.");
+	if (arg != NULL)
+		dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg,
+					 DBUS_TYPE_INVALID);
+
+	return reply;
+}
+
+
+/**
+ * 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
+ */
+static DBusMessage * wpas_dbus_error_scan_error(DBusMessage *message,
+						const char *error)
+{
+	return dbus_message_new_error(message,
+				      WPAS_DBUS_ERROR_IFACE_SCAN_ERROR,
+				      error);
+}
+
+
+DBusMessage * wpas_dbus_error_no_memory(DBusMessage *message)
+{
+	wpa_printf(MSG_DEBUG, "dbus: Failed to allocate memory");
+	return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL);
+}
+
+
+static const char * const dont_quote[] = {
+	"key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
+	"opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path",
+	"bssid", "scan_freq", "freq_list", NULL
+};
+
+static dbus_bool_t should_quote_opt(const char *key)
+{
+	int i = 0;
+
+	while (dont_quote[i] != NULL) {
+		if (os_strcmp(key, dont_quote[i]) == 0)
+			return FALSE;
+		i++;
+	}
+	return TRUE;
+}
+
+/**
+ * get_iface_by_dbus_path - Get a new network interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @path: Pointer to a dbus object path representing an interface
+ * Returns: Pointer to the interface or %NULL if not found
+ */
+static struct wpa_supplicant * get_iface_by_dbus_path(
+	struct wpa_global *global, const char *path)
+{
+	struct wpa_supplicant *wpa_s;
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (wpa_s->dbus_new_path &&
+		    os_strcmp(wpa_s->dbus_new_path, path) == 0)
+			return wpa_s;
+	}
+	return NULL;
+}
+
+
+/**
+ * set_network_properties - Set properties of a configured network
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network
+ * @iter: DBus message iterator containing dictionary of network
+ * properties to set.
+ * @error: On failure, an error describing the failure
+ * Returns: TRUE if the request succeeds, FALSE if it failed
+ *
+ * Sets network configuration with parameters given id DBus dictionary
+ */
+dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
+				   struct wpa_ssid *ssid,
+				   DBusMessageIter *iter,
+				   DBusError *error)
+{
+	struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
+	DBusMessageIter	iter_dict;
+	char *value = NULL;
+
+	if (!wpa_dbus_dict_open_read(iter, &iter_dict, error))
+		return FALSE;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		size_t size = 50;
+		int ret;
+
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto error;
+
+		value = NULL;
+		if (entry.type == DBUS_TYPE_ARRAY &&
+		    entry.array_type == DBUS_TYPE_BYTE) {
+			if (entry.array_len <= 0)
+				goto error;
+
+			size = entry.array_len * 2 + 1;
+			value = os_zalloc(size);
+			if (value == NULL)
+				goto error;
+
+			ret = wpa_snprintf_hex(value, size,
+					       (u8 *) entry.bytearray_value,
+					       entry.array_len);
+			if (ret <= 0)
+				goto error;
+		} else if (entry.type == DBUS_TYPE_STRING) {
+			if (should_quote_opt(entry.key)) {
+				size = os_strlen(entry.str_value);
+				if (size == 0)
+					goto error;
+
+				size += 3;
+				value = os_zalloc(size);
+				if (value == NULL)
+					goto error;
+
+				ret = os_snprintf(value, size, "\"%s\"",
+						  entry.str_value);
+				if (os_snprintf_error(size, ret))
+					goto error;
+			} else {
+				value = os_strdup(entry.str_value);
+				if (value == NULL)
+					goto error;
+			}
+		} else if (entry.type == DBUS_TYPE_UINT32) {
+			value = os_zalloc(size);
+			if (value == NULL)
+				goto error;
+
+			ret = os_snprintf(value, size, "%u",
+					  entry.uint32_value);
+			if (os_snprintf_error(size, ret))
+				goto error;
+		} else if (entry.type == DBUS_TYPE_INT32) {
+			value = os_zalloc(size);
+			if (value == NULL)
+				goto error;
+
+			ret = os_snprintf(value, size, "%d",
+					  entry.int32_value);
+			if (os_snprintf_error(size, ret))
+				goto error;
+		} else
+			goto error;
+
+		if (wpa_config_set(ssid, entry.key, value, 0) < 0)
+			goto error;
+
+		if (os_strcmp(entry.key, "bssid") != 0 &&
+		    os_strcmp(entry.key, "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(entry.key, "psk") == 0 &&
+		     value[0] == '"' && ssid->ssid_len) ||
+		    (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase))
+			wpa_config_update_psk(ssid);
+		else if (os_strcmp(entry.key, "priority") == 0)
+			wpa_config_update_prio_list(wpa_s->conf);
+
+		os_free(value);
+		value = NULL;
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+
+	return TRUE;
+
+error:
+	os_free(value);
+	wpa_dbus_dict_entry_clear(&entry);
+	dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+			     "invalid message format");
+	return FALSE;
+}
+
+
+/**
+ * wpas_dbus_simple_property_getter - Get basic type property
+ * @iter: Message iter to use when appending arguments
+ * @type: DBus type of property (must be basic type)
+ * @val: pointer to place holding property value
+ * @error: On failure an error describing the failure
+ * Returns: TRUE if the request was successful, FALSE if it failed
+ *
+ * Generic getter for basic type properties. Type is required to be basic.
+ */
+dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter,
+					     const int type,
+					     const void *val,
+					     DBusError *error)
+{
+	DBusMessageIter variant_iter;
+
+	if (!dbus_type_is_basic(type)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: given type is not basic", __func__);
+		return FALSE;
+	}
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      wpa_dbus_type_as_string(type),
+					      &variant_iter) ||
+	    !dbus_message_iter_append_basic(&variant_iter, type, val) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: error constructing reply", __func__);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_simple_property_setter - Set basic type property
+ * @message: Pointer to incoming dbus message
+ * @type: DBus type of property (must be basic type)
+ * @val: pointer to place where value being set will be stored
+ * Returns: TRUE if the request was successful, FALSE if it failed
+ *
+ * Generic setter for basic type properties. Type is required to be basic.
+ */
+dbus_bool_t wpas_dbus_simple_property_setter(DBusMessageIter *iter,
+					     DBusError *error,
+					     const int type, void *val)
+{
+	DBusMessageIter variant_iter;
+
+	if (!dbus_type_is_basic(type)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: given type is not basic", __func__);
+		return FALSE;
+	}
+
+	/* Look at the new value */
+	dbus_message_iter_recurse(iter, &variant_iter);
+	if (dbus_message_iter_get_arg_type(&variant_iter) != type) {
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "wrong property type");
+		return FALSE;
+	}
+	dbus_message_iter_get_basic(&variant_iter, val);
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_simple_array_property_getter - Get array type property
+ * @iter: Pointer to incoming dbus message iterator
+ * @type: DBus type of property array elements (must be basic type)
+ * @array: pointer to array of elements to put into response message
+ * @array_len: length of above array
+ * @error: a pointer to an error to fill on failure
+ * Returns: TRUE if the request succeeded, FALSE if it failed
+ *
+ * Generic getter for array type properties. Array elements type is
+ * required to be basic.
+ */
+dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter,
+						   const int type,
+						   const void *array,
+						   size_t array_len,
+						   DBusError *error)
+{
+	DBusMessageIter variant_iter, array_iter;
+	char type_str[] = "a?"; /* ? will be replaced with subtype letter; */
+	const char *sub_type_str;
+	size_t element_size, i;
+
+	if (!dbus_type_is_basic(type)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: given type is not basic", __func__);
+		return FALSE;
+	}
+
+	sub_type_str = wpa_dbus_type_as_string(type);
+	type_str[1] = sub_type_str[0];
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      type_str, &variant_iter) ||
+	    !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+					      sub_type_str, &array_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: failed to construct message", __func__);
+		return FALSE;
+	}
+
+	switch (type) {
+	case DBUS_TYPE_BYTE:
+	case DBUS_TYPE_BOOLEAN:
+		element_size = 1;
+		break;
+	case DBUS_TYPE_INT16:
+	case DBUS_TYPE_UINT16:
+		element_size = sizeof(uint16_t);
+		break;
+	case DBUS_TYPE_INT32:
+	case DBUS_TYPE_UINT32:
+		element_size = sizeof(uint32_t);
+		break;
+	case DBUS_TYPE_INT64:
+	case DBUS_TYPE_UINT64:
+		element_size = sizeof(uint64_t);
+		break;
+	case DBUS_TYPE_DOUBLE:
+		element_size = sizeof(double);
+		break;
+	case DBUS_TYPE_STRING:
+	case DBUS_TYPE_OBJECT_PATH:
+		element_size = sizeof(char *);
+		break;
+	default:
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: unknown element type %d", __func__, type);
+		return FALSE;
+	}
+
+	for (i = 0; i < array_len; i++) {
+		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) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: failed to construct message 3", __func__);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_simple_array_array_property_getter - Get array array type property
+ * @iter: Pointer to incoming dbus message iterator
+ * @type: DBus type of property array elements (must be basic type)
+ * @array: pointer to array of elements to put into response message
+ * @array_len: length of above array
+ * @error: a pointer to an error to fill on failure
+ * Returns: TRUE if the request succeeded, FALSE if it failed
+ *
+ * Generic getter for array type properties. Array elements type is
+ * required to be basic.
+ */
+dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter,
+							 const int type,
+							 struct wpabuf **array,
+							 size_t array_len,
+							 DBusError *error)
+{
+	DBusMessageIter variant_iter, array_iter;
+	char type_str[] = "aa?";
+	char inner_type_str[] = "a?";
+	const char *sub_type_str;
+	size_t i;
+
+	if (!dbus_type_is_basic(type)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: given type is not basic", __func__);
+		return FALSE;
+	}
+
+	sub_type_str = wpa_dbus_type_as_string(type);
+	type_str[2] = sub_type_str[0];
+	inner_type_str[1] = sub_type_str[0];
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      type_str, &variant_iter) ||
+	    !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+					      inner_type_str, &array_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: failed to construct message", __func__);
+		return FALSE;
+	}
+
+	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]));
+
+	}
+
+	if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: failed to close message", __func__);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_handler_create_interface - Request registration of a network iface
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: The object path of the new interface object,
+ *          or a dbus error message with more information
+ *
+ * Handler function for "CreateInterface" method call. Handles requests
+ * by dbus clients to register a network interface that wpa_supplicant
+ * will manage.
+ */
+DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
+						 struct wpa_global *global)
+{
+	DBusMessageIter iter_dict;
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter;
+	struct wpa_dbus_dict_entry entry;
+	char *driver = NULL;
+	char *ifname = NULL;
+	char *confname = NULL;
+	char *bridge_ifname = NULL;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto error;
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto error;
+		if (os_strcmp(entry.key, "Driver") == 0 &&
+		    entry.type == DBUS_TYPE_STRING) {
+			os_free(driver);
+			driver = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (driver == NULL)
+				goto oom;
+		} else if (os_strcmp(entry.key, "Ifname") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			os_free(ifname);
+			ifname = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (ifname == NULL)
+				goto oom;
+		} else if (os_strcmp(entry.key, "ConfigFile") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			os_free(confname);
+			confname = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (confname == NULL)
+				goto oom;
+		} else if (os_strcmp(entry.key, "BridgeIfname") == 0 &&
+			   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)
+				goto oom;
+		} else {
+			wpa_dbus_dict_entry_clear(&entry);
+			goto error;
+		}
+	}
+
+	if (ifname == NULL)
+		goto error; /* Required Ifname argument missing */
+
+	/*
+	 * Try to get the wpa_supplicant record for this iface, return
+	 * an error if we already control it.
+	 */
+	if (wpa_supplicant_get_iface(global, ifname) != NULL) {
+		reply = dbus_message_new_error(
+			message, WPAS_DBUS_ERROR_IFACE_EXISTS,
+			"wpa_supplicant already controls this interface.");
+	} else {
+		struct wpa_supplicant *wpa_s;
+		struct wpa_interface iface;
+
+		os_memset(&iface, 0, sizeof(iface));
+		iface.driver = driver;
+		iface.ifname = ifname;
+		iface.confname = confname;
+		iface.bridge_ifname = bridge_ifname;
+		/* Otherwise, have wpa_supplicant attach to it. */
+		wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
+		if (wpa_s && wpa_s->dbus_new_path) {
+			const char *path = wpa_s->dbus_new_path;
+
+			reply = dbus_message_new_method_return(message);
+			dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH,
+						 &path, DBUS_TYPE_INVALID);
+		} else {
+			reply = wpas_dbus_error_unknown_error(
+				message,
+				"wpa_supplicant couldn't grab this interface.");
+		}
+	}
+
+out:
+	os_free(driver);
+	os_free(ifname);
+	os_free(confname);
+	os_free(bridge_ifname);
+	return reply;
+
+error:
+	reply = wpas_dbus_error_invalid_args(message, NULL);
+	goto out;
+oom:
+	reply = wpas_dbus_error_no_memory(message);
+	goto out;
+}
+
+
+/**
+ * wpas_dbus_handler_remove_interface - Request deregistration of an interface
+ * @message: Pointer to incoming dbus message
+ * @global: wpa_supplicant global data structure
+ * Returns: a dbus message containing a UINT32 indicating success (1) or
+ *          failure (0), or returns a dbus error message with more information
+ *
+ * Handler function for "removeInterface" method call.  Handles requests
+ * by dbus clients to deregister a network interface that wpa_supplicant
+ * currently manages.
+ */
+DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message,
+						 struct wpa_global *global)
+{
+	struct wpa_supplicant *wpa_s;
+	char *path;
+	DBusMessage *reply = NULL;
+
+	dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+			      DBUS_TYPE_INVALID);
+
+	wpa_s = get_iface_by_dbus_path(global, path);
+	if (wpa_s == NULL)
+		reply = wpas_dbus_error_iface_unknown(message);
+	else if (wpa_supplicant_remove_iface(global, wpa_s, 0)) {
+		reply = wpas_dbus_error_unknown_error(
+			message,
+			"wpa_supplicant couldn't remove this interface.");
+	}
+
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_get_interface - Get the object path for an interface name
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: The object path of the interface object,
+ *          or a dbus error message with more information
+ *
+ * Handler function for "getInterface" method call.
+ */
+DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message,
+					      struct wpa_global *global)
+{
+	DBusMessage *reply = NULL;
+	const char *ifname;
+	const char *path;
+	struct wpa_supplicant *wpa_s;
+
+	dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &ifname,
+			      DBUS_TYPE_INVALID);
+
+	wpa_s = wpa_supplicant_get_iface(global, ifname);
+	if (wpa_s == NULL || wpa_s->dbus_new_path == NULL)
+		return wpas_dbus_error_iface_unknown(message);
+
+	path = wpa_s->dbus_new_path;
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL)
+		return wpas_dbus_error_no_memory(message);
+	if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+				      DBUS_TYPE_INVALID)) {
+		dbus_message_unref(reply);
+		return wpas_dbus_error_no_memory(message);
+	}
+
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_getter_debug_level - Get debug level
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "DebugLevel" property.
+ */
+dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
+					 DBusError *error,
+					 void *user_data)
+{
+	const char *str;
+	int idx = wpa_debug_level;
+
+	if (idx < 0)
+		idx = 0;
+	if (idx > 5)
+		idx = 5;
+	str = debug_strings[idx];
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+						&str, error);
+}
+
+
+/**
+ * wpas_dbus_getter_debug_timestamp - Get debug timestamp
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "DebugTimestamp" property.
+ */
+dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data)
+{
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+						&wpa_debug_timestamp, error);
+
+}
+
+
+/**
+ * wpas_dbus_getter_debug_show_keys - Get debug show keys
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "DebugShowKeys" property.
+ */
+dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data)
+{
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+						&wpa_debug_show_keys, error);
+
+}
+
+/**
+ * wpas_dbus_setter_debug_level - Set debug level
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "DebugLevel" property.
+ */
+dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
+					 DBusError *error, void *user_data)
+{
+	struct wpa_global *global = user_data;
+	const char *str = NULL;
+	int i, val = -1;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+					      &str))
+		return FALSE;
+
+	for (i = 0; debug_strings[i]; i++)
+		if (os_strcmp(debug_strings[i], str) == 0) {
+			val = i;
+			break;
+		}
+
+	if (val < 0 ||
+	    wpa_supplicant_set_debug_params(global, val, wpa_debug_timestamp,
+					    wpa_debug_show_keys)) {
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "wrong debug level value");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_setter_debug_timestamp - Set debug timestamp
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "DebugTimestamp" property.
+ */
+dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data)
+{
+	struct wpa_global *global = user_data;
+	dbus_bool_t val;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+					      &val))
+		return FALSE;
+
+	wpa_supplicant_set_debug_params(global, wpa_debug_level, val ? 1 : 0,
+					wpa_debug_show_keys);
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_setter_debug_show_keys - Set debug show keys
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "DebugShowKeys" property.
+ */
+dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data)
+{
+	struct wpa_global *global = user_data;
+	dbus_bool_t val;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+					      &val))
+		return FALSE;
+
+	wpa_supplicant_set_debug_params(global, wpa_debug_level,
+					wpa_debug_timestamp,
+					val ? 1 : 0);
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_interfaces - Request registered interfaces list
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Interfaces" property. Handles requests
+ * by dbus clients to return list of registered interfaces objects
+ * paths
+ */
+dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter,
+					DBusError *error,
+					void *user_data)
+{
+	struct wpa_global *global = user_data;
+	struct wpa_supplicant *wpa_s;
+	const char **paths;
+	unsigned int i = 0, num = 0;
+	dbus_bool_t success;
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (wpa_s->dbus_new_path)
+			num++;
+	}
+
+	paths = os_calloc(num, sizeof(char *));
+	if (!paths) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (wpa_s->dbus_new_path)
+			paths[i++] = wpa_s->dbus_new_path;
+	}
+
+	success = wpas_dbus_simple_array_property_getter(iter,
+							 DBUS_TYPE_OBJECT_PATH,
+							 paths, num, error);
+
+	os_free(paths);
+	return success;
+}
+
+
+/**
+ * wpas_dbus_getter_eap_methods - Request supported EAP methods list
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "EapMethods" property. Handles requests
+ * by dbus clients to return list of strings with supported EAP methods
+ */
+dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
+					 DBusError *error, void *user_data)
+{
+	char **eap_methods;
+	size_t num_items = 0;
+	dbus_bool_t success;
+
+	eap_methods = eap_get_names_as_string_array(&num_items);
+	if (!eap_methods) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	success = wpas_dbus_simple_array_property_getter(iter,
+							 DBUS_TYPE_STRING,
+							 eap_methods,
+							 num_items, error);
+
+	while (num_items)
+		os_free(eap_methods[--num_items]);
+	os_free(eap_methods);
+	return success;
+}
+
+
+/**
+ * wpas_dbus_getter_global_capabilities - Request supported global capabilities
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Capabilities" property. Handles requests by dbus clients to
+ * return a list of strings with supported capabilities like AP, RSN IBSS,
+ * and P2P that are determined at compile time.
+ */
+dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter,
+						 DBusError *error,
+						 void *user_data)
+{
+	const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL };
+	size_t num_items = 0;
+
+#ifdef CONFIG_AP
+	capabilities[num_items++] = "ap";
+#endif /* CONFIG_AP */
+#ifdef CONFIG_IBSS_RSN
+	capabilities[num_items++] = "ibss-rsn";
+#endif /* CONFIG_IBSS_RSN */
+#ifdef CONFIG_P2P
+	capabilities[num_items++] = "p2p";
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+	capabilities[num_items++] = "interworking";
+#endif /* CONFIG_INTERWORKING */
+
+	return wpas_dbus_simple_array_property_getter(iter,
+						      DBUS_TYPE_STRING,
+						      capabilities,
+						      num_items, error);
+}
+
+
+static int wpas_dbus_get_scan_type(DBusMessage *message, DBusMessageIter *var,
+				   char **type, DBusMessage **reply)
+{
+	if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_STRING) {
+		wpa_printf(MSG_DEBUG, "%s[dbus]: Type must be a string",
+			   __func__);
+		*reply = wpas_dbus_error_invalid_args(
+			message, "Wrong Type value type. String required");
+		return -1;
+	}
+	dbus_message_iter_get_basic(var, type);
+	return 0;
+}
+
+
+static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var,
+				    struct wpa_driver_scan_params *params,
+				    DBusMessage **reply)
+{
+	struct wpa_driver_scan_ssid *ssids = params->ssids;
+	size_t ssids_num = 0;
+	u8 *ssid;
+	DBusMessageIter array_iter, sub_array_iter;
+	char *val;
+	int len;
+
+	if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: ssids must be an array of arrays of bytes",
+			   __func__);
+		*reply = wpas_dbus_error_invalid_args(
+			message,
+			"Wrong SSIDs value type. Array of arrays of bytes required");
+		return -1;
+	}
+
+	dbus_message_iter_recurse(var, &array_iter);
+
+	if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY ||
+	    dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) {
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: ssids must be an array of arrays of bytes",
+			   __func__);
+		*reply = wpas_dbus_error_invalid_args(
+			message,
+			"Wrong SSIDs value type. Array of arrays of bytes required");
+		return -1;
+	}
+
+	while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) {
+		if (ssids_num >= WPAS_MAX_SCAN_SSIDS) {
+			wpa_printf(MSG_DEBUG,
+				   "%s[dbus]: Too many ssids specified on scan dbus call",
+				   __func__);
+			*reply = wpas_dbus_error_invalid_args(
+				message,
+				"Too many ssids specified. Specify at most four");
+			return -1;
+		}
+
+		dbus_message_iter_recurse(&array_iter, &sub_array_iter);
+
+		dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len);
+
+		if (len > SSID_MAX_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "%s[dbus]: SSID too long (len=%d max_len=%d)",
+				   __func__, len, SSID_MAX_LEN);
+			*reply = wpas_dbus_error_invalid_args(
+				message, "Invalid SSID: too long");
+			return -1;
+		}
+
+		if (len != 0) {
+			ssid = os_malloc(len);
+			if (ssid == NULL) {
+				*reply = wpas_dbus_error_no_memory(message);
+				return -1;
+			}
+			os_memcpy(ssid, val, len);
+		} else {
+			/* Allow zero-length SSIDs */
+			ssid = NULL;
+		}
+
+		ssids[ssids_num].ssid = ssid;
+		ssids[ssids_num].ssid_len = len;
+
+		dbus_message_iter_next(&array_iter);
+		ssids_num++;
+	}
+
+	params->num_ssids = ssids_num;
+	return 0;
+}
+
+
+static int wpas_dbus_get_scan_ies(DBusMessage *message, DBusMessageIter *var,
+				  struct wpa_driver_scan_params *params,
+				  DBusMessage **reply)
+{
+	u8 *ies = NULL, *nies;
+	int ies_len = 0;
+	DBusMessageIter array_iter, sub_array_iter;
+	char *val;
+	int len;
+
+	if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: ies must be an array of arrays of bytes",
+			   __func__);
+		*reply = wpas_dbus_error_invalid_args(
+			message,
+			"Wrong IEs value type. Array of arrays of bytes required");
+		return -1;
+	}
+
+	dbus_message_iter_recurse(var, &array_iter);
+
+	if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY ||
+	    dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) {
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: ies must be an array of arrays of bytes",
+			   __func__);
+		*reply = wpas_dbus_error_invalid_args(
+			message, "Wrong IEs value type. Array required");
+		return -1;
+	}
+
+	while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) {
+		dbus_message_iter_recurse(&array_iter, &sub_array_iter);
+
+		dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len);
+		if (len == 0) {
+			dbus_message_iter_next(&array_iter);
+			continue;
+		}
+
+		nies = os_realloc(ies, ies_len + len);
+		if (nies == NULL) {
+			os_free(ies);
+			*reply = wpas_dbus_error_no_memory(message);
+			return -1;
+		}
+		ies = nies;
+		os_memcpy(ies + ies_len, val, len);
+		ies_len += len;
+
+		dbus_message_iter_next(&array_iter);
+	}
+
+	params->extra_ies = ies;
+	params->extra_ies_len = ies_len;
+	return 0;
+}
+
+
+static int wpas_dbus_get_scan_channels(DBusMessage *message,
+				       DBusMessageIter *var,
+				       struct wpa_driver_scan_params *params,
+				       DBusMessage **reply)
+{
+	DBusMessageIter array_iter, sub_array_iter;
+	int *freqs = NULL, *nfreqs;
+	int freqs_num = 0;
+
+	if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: Channels must be an array of structs",
+			   __func__);
+		*reply = wpas_dbus_error_invalid_args(
+			message,
+			"Wrong Channels value type. Array of structs required");
+		return -1;
+	}
+
+	dbus_message_iter_recurse(var, &array_iter);
+
+	if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_STRUCT) {
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: Channels must be an array of structs",
+			   __func__);
+		*reply = wpas_dbus_error_invalid_args(
+			message,
+			"Wrong Channels value type. Array of structs required");
+		return -1;
+	}
+
+	while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_STRUCT)
+	{
+		int freq, width;
+
+		dbus_message_iter_recurse(&array_iter, &sub_array_iter);
+
+		if (dbus_message_iter_get_arg_type(&sub_array_iter) !=
+		    DBUS_TYPE_UINT32) {
+			wpa_printf(MSG_DEBUG,
+				   "%s[dbus]: Channel must by specified by struct of two UINT32s %c",
+				   __func__,
+				   dbus_message_iter_get_arg_type(
+					   &sub_array_iter));
+			*reply = wpas_dbus_error_invalid_args(
+				message,
+				"Wrong Channel struct. Two UINT32s required");
+			os_free(freqs);
+			return -1;
+		}
+		dbus_message_iter_get_basic(&sub_array_iter, &freq);
+
+		if (!dbus_message_iter_next(&sub_array_iter) ||
+		    dbus_message_iter_get_arg_type(&sub_array_iter) !=
+		    DBUS_TYPE_UINT32) {
+			wpa_printf(MSG_DEBUG,
+				   "%s[dbus]: Channel must by specified by struct of two UINT32s",
+				   __func__);
+			*reply = wpas_dbus_error_invalid_args(
+				message,
+				"Wrong Channel struct. Two UINT32s required");
+			os_free(freqs);
+			return -1;
+		}
+
+		dbus_message_iter_get_basic(&sub_array_iter, &width);
+
+#define FREQS_ALLOC_CHUNK 32
+		if (freqs_num % FREQS_ALLOC_CHUNK == 0) {
+			nfreqs = os_realloc_array(
+				freqs, freqs_num + FREQS_ALLOC_CHUNK,
+				sizeof(int));
+			if (nfreqs == NULL)
+				os_free(freqs);
+			freqs = nfreqs;
+		}
+		if (freqs == NULL) {
+			*reply = wpas_dbus_error_no_memory(message);
+			return -1;
+		}
+
+		freqs[freqs_num] = freq;
+
+		freqs_num++;
+		dbus_message_iter_next(&array_iter);
+	}
+
+	nfreqs = os_realloc_array(freqs, freqs_num + 1, sizeof(int));
+	if (nfreqs == NULL)
+		os_free(freqs);
+	freqs = nfreqs;
+	if (freqs == NULL) {
+		*reply = wpas_dbus_error_no_memory(message);
+		return -1;
+	}
+	freqs[freqs_num] = 0;
+
+	params->freqs = freqs;
+	return 0;
+}
+
+
+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, "%s[dbus]: Type must be a boolean",
+			   __func__);
+		*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
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "Scan" method call of a network device. Requests
+ * that wpa_supplicant perform a wireless scan as soon as possible
+ * on a particular wireless interface.
+ */
+DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
+				     struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter, dict_iter, entry_iter, variant_iter;
+	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));
+
+	dbus_message_iter_init(message, &iter);
+
+	dbus_message_iter_recurse(&iter, &dict_iter);
+
+	while (dbus_message_iter_get_arg_type(&dict_iter) ==
+	       DBUS_TYPE_DICT_ENTRY) {
+		dbus_message_iter_recurse(&dict_iter, &entry_iter);
+		dbus_message_iter_get_basic(&entry_iter, &key);
+		dbus_message_iter_next(&entry_iter);
+		dbus_message_iter_recurse(&entry_iter, &variant_iter);
+
+		if (os_strcmp(key, "Type") == 0) {
+			if (wpas_dbus_get_scan_type(message, &variant_iter,
+						    &type, &reply) < 0)
+				goto out;
+		} else if (os_strcmp(key, "SSIDs") == 0) {
+			if (wpas_dbus_get_scan_ssids(message, &variant_iter,
+						     &params, &reply) < 0)
+				goto out;
+		} else if (os_strcmp(key, "IEs") == 0) {
+			if (wpas_dbus_get_scan_ies(message, &variant_iter,
+						   &params, &reply) < 0)
+				goto out;
+		} else if (os_strcmp(key, "Channels") == 0) {
+			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, "%s[dbus]: Unknown argument %s",
+				   __func__, key);
+			reply = wpas_dbus_error_invalid_args(message, key);
+			goto out;
+		}
+
+		dbus_message_iter_next(&dict_iter);
+	}
+
+	if (!type) {
+		wpa_printf(MSG_DEBUG, "%s[dbus]: Scan type not specified",
+			   __func__);
+		reply = wpas_dbus_error_invalid_args(message, key);
+		goto out;
+	}
+
+	if (os_strcmp(type, "passive") == 0) {
+		if (params.num_ssids || params.extra_ies_len) {
+			wpa_printf(MSG_DEBUG,
+				   "%s[dbus]: SSIDs or IEs specified for passive scan.",
+				   __func__);
+			reply = wpas_dbus_error_invalid_args(
+				message,
+				"You can specify only Channels in passive scan");
+			goto out;
+		} else {
+			if (wpa_s->sched_scanning) {
+				wpa_printf(MSG_DEBUG,
+					   "%s[dbus]: Stop ongoing sched_scan to allow requested scan to proceed",
+					   __func__);
+				wpa_supplicant_cancel_sched_scan(wpa_s);
+			}
+
+			if (params.freqs && params.freqs[0]) {
+				wpa_s->last_scan_req = MANUAL_SCAN_REQ;
+				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);
+			}
+		}
+	} else if (os_strcmp(type, "active") == 0) {
+		if (!params.num_ssids) {
+			/* Add wildcard ssid */
+			params.num_ssids++;
+		}
+#ifdef CONFIG_AUTOSCAN
+		autoscan_deinit(wpa_s);
+#endif /* CONFIG_AUTOSCAN */
+		if (wpa_s->sched_scanning) {
+			wpa_printf(MSG_DEBUG,
+				   "%s[dbus]: Stop ongoing sched_scan to allow requested scan to proceed",
+				   __func__);
+			wpa_supplicant_cancel_sched_scan(wpa_s);
+		}
+
+		wpa_s->last_scan_req = MANUAL_SCAN_REQ;
+		if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
+			reply = wpas_dbus_error_scan_error(
+				message, "Scan request rejected");
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "%s[dbus]: Unknown scan type: %s",
+			   __func__, type);
+		reply = wpas_dbus_error_invalid_args(message,
+						     "Wrong scan type");
+		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);
+	os_free((u8 *) params.extra_ies);
+	os_free(params.freqs);
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_signal_poll - Request immediate signal properties
+ * @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 "SignalPoll" method call of a network device. Requests
+ * that wpa_supplicant read signal properties like RSSI, noise, and link
+ * speed and return them.
+ */
+DBusMessage * wpas_dbus_handler_signal_poll(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s)
+{
+	struct wpa_signal_info si;
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter, iter_dict, variant_iter;
+	int ret;
+
+	ret = wpa_drv_signal_poll(wpa_s, &si);
+	if (ret) {
+		return dbus_message_new_error(message, DBUS_ERROR_FAILED,
+					      "Failed to read signal");
+	}
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL)
+		goto nomem;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+					      "a{sv}", &variant_iter) ||
+	    !wpa_dbus_dict_open_write(&variant_iter, &iter_dict) ||
+	    !wpa_dbus_dict_append_int32(&iter_dict, "rssi",
+					si.current_signal) ||
+	    !wpa_dbus_dict_append_int32(&iter_dict, "linkspeed",
+					si.current_txrate / 1000) ||
+	    !wpa_dbus_dict_append_int32(&iter_dict, "noise",
+					si.current_noise) ||
+	    !wpa_dbus_dict_append_uint32(&iter_dict, "frequency",
+					 si.frequency) ||
+	    (si.chanwidth != CHAN_WIDTH_UNKNOWN &&
+	     !wpa_dbus_dict_append_string(
+		     &iter_dict, "width",
+		     channel_width_to_string(si.chanwidth))) ||
+	    (si.center_frq1 > 0 && si.center_frq2 > 0 &&
+	     (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1",
+					  si.center_frq1) ||
+	      !wpa_dbus_dict_append_int32(&iter_dict, "center-frq2",
+					  si.center_frq2))) ||
+	    (si.avg_signal &&
+	     !wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi",
+					 si.avg_signal)) ||
+	    !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+	    !dbus_message_iter_close_container(&iter, &variant_iter))
+		goto nomem;
+
+	return reply;
+
+nomem:
+	if (reply)
+		dbus_message_unref(reply);
+	return wpas_dbus_error_no_memory(message);
+}
+
+
+/*
+ * wpas_dbus_handler_disconnect - Terminate the current connection
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NotConnected DBus error message if already not connected
+ * or NULL otherwise.
+ *
+ * Handler function for "Disconnect" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->current_ssid != NULL) {
+		wpa_s->disconnected = 1;
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+
+		return NULL;
+	}
+
+	return dbus_message_new_error(message, WPAS_DBUS_ERROR_NOT_CONNECTED,
+				      "This interface is not connected");
+}
+
+
+/**
+ * wpas_dbus_new_iface_add_network - Add a new configured network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing the object path of the new network
+ *
+ * Handler function for "AddNetwork" method call of a network interface.
+ */
+DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	DBusMessageIter	iter;
+	struct wpa_ssid *ssid = NULL;
+	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
+	DBusError error;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (wpa_s->dbus_new_path)
+		ssid = wpa_config_add_network(wpa_s->conf);
+	if (ssid == NULL) {
+		wpa_printf(MSG_ERROR, "%s[dbus]: can't add new interface.",
+			   __func__);
+		reply = wpas_dbus_error_unknown_error(
+			message,
+			"wpa_supplicant could not add a network on this interface.");
+		goto err;
+	}
+	wpas_notify_network_added(wpa_s, ssid);
+	ssid->disabled = 1;
+	wpa_config_set_network_defaults(ssid);
+
+	dbus_error_init(&error);
+	if (!set_network_properties(wpa_s, ssid, &iter, &error)) {
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: control interface couldn't set network properties",
+			   __func__);
+		reply = wpas_dbus_reply_new_from_error(message, &error,
+						       DBUS_ERROR_INVALID_ARGS,
+						       "Failed to add network");
+		dbus_error_free(&error);
+		goto err;
+	}
+
+	/* Construct the object path for this network. */
+	os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d",
+		    wpa_s->dbus_new_path, ssid->id);
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL) {
+		reply = wpas_dbus_error_no_memory(message);
+		goto err;
+	}
+	if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+				      DBUS_TYPE_INVALID)) {
+		dbus_message_unref(reply);
+		reply = wpas_dbus_error_no_memory(message);
+		goto err;
+	}
+
+	return reply;
+
+err:
+	if (ssid) {
+		wpas_notify_network_removed(wpa_s, ssid);
+		wpa_config_remove_network(wpa_s->conf, ssid->id);
+	}
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_reassociate - Reassociate
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: InterfaceDisabled DBus error message if disabled
+ * or NULL otherwise.
+ *
+ * Handler function for "Reassociate" method call of network interface.
+ */
+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;
+	}
+
+	return dbus_message_new_error(message, WPAS_DBUS_ERROR_NOT_CONNECTED,
+				      "This interface is not connected");
+}
+
+
+/**
+ * wpas_dbus_handler_reconnect - Reconnect if disconnected
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: InterfaceDisabled DBus error message if disabled
+ * or NULL otherwise.
+ *
+ * Handler function for "Reconnect" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_reconnect(DBusMessage *message,
+		struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+		return dbus_message_new_error(message,
+					      WPAS_DBUS_ERROR_IFACE_DISABLED,
+					      "This interface is disabled");
+	}
+
+	if (wpa_s->disconnected)
+		wpas_request_connection(wpa_s);
+	return NULL;
+}
+
+
+/**
+ * wpas_dbus_handler_remove_network - Remove a configured network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "RemoveNetwork" method call of a network interface.
+ */
+DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
+					       struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	const char *op;
+	char *iface, *net_id;
+	int id;
+	struct wpa_ssid *ssid;
+	int was_disabled;
+
+	dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op,
+			      DBUS_TYPE_INVALID);
+
+	/* Extract the network ID and ensure the network */
+	/* is actually a child of this interface */
+	iface = wpas_dbus_new_decompose_object_path(op,
+						    WPAS_DBUS_NEW_NETWORKS_PART,
+						    &net_id);
+	if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path ||
+	    os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+		reply = wpas_dbus_error_invalid_args(message, op);
+		goto out;
+	}
+
+	errno = 0;
+	id = strtoul(net_id, NULL, 10);
+	if (errno != 0) {
+		reply = wpas_dbus_error_invalid_args(message, op);
+		goto out;
+	}
+
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid == NULL) {
+		reply = wpas_dbus_error_network_unknown(message);
+		goto out;
+	}
+
+	was_disabled = ssid->disabled;
+
+	wpas_notify_network_removed(wpa_s, ssid);
+
+	if (ssid == wpa_s->current_ssid)
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+	else if (!was_disabled && wpa_s->sched_scanning) {
+		wpa_printf(MSG_DEBUG,
+			   "Stop ongoing sched_scan to remove network from filters");
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	}
+
+	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s[dbus]: error occurred when removing network %d",
+			   __func__, id);
+		reply = wpas_dbus_error_unknown_error(
+			message,
+			"error removing the specified network on is interface.");
+		goto out;
+	}
+
+out:
+	os_free(iface);
+	return reply;
+}
+
+
+static void remove_network(void *arg, struct wpa_ssid *ssid)
+{
+	struct wpa_supplicant *wpa_s = arg;
+
+	wpas_notify_network_removed(wpa_s, ssid);
+
+	if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s[dbus]: error occurred when removing network %d",
+			   __func__, ssid->id);
+		return;
+	}
+
+	if (ssid == wpa_s->current_ssid)
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+}
+
+
+/**
+ * wpas_dbus_handler_remove_all_networks - Remove all configured networks
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "RemoveAllNetworks" method call of a network interface.
+ */
+DBusMessage * wpas_dbus_handler_remove_all_networks(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->sched_scanning)
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+
+	/* NB: could check for failure and return an error */
+	wpa_config_foreach_network(wpa_s->conf, remove_network, wpa_s);
+	return NULL;
+}
+
+
+/**
+ * wpas_dbus_handler_select_network - Attempt association with a network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "SelectNetwork" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message,
+					       struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	const char *op;
+	char *iface, *net_id;
+	int id;
+	struct wpa_ssid *ssid;
+
+	dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op,
+			      DBUS_TYPE_INVALID);
+
+	/* Extract the network ID and ensure the network */
+	/* is actually a child of this interface */
+	iface = wpas_dbus_new_decompose_object_path(op,
+						    WPAS_DBUS_NEW_NETWORKS_PART,
+						    &net_id);
+	if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path ||
+	    os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+		reply = wpas_dbus_error_invalid_args(message, op);
+		goto out;
+	}
+
+	errno = 0;
+	id = strtoul(net_id, NULL, 10);
+	if (errno != 0) {
+		reply = wpas_dbus_error_invalid_args(message, op);
+		goto out;
+	}
+
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid == NULL) {
+		reply = wpas_dbus_error_network_unknown(message);
+		goto out;
+	}
+
+	/* Finally, associate with the network */
+	wpa_supplicant_select_network(wpa_s, ssid);
+
+out:
+	os_free(iface);
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_network_reply - Reply to a NetworkRequest signal
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "NetworkReply" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message,
+					      struct wpa_supplicant *wpa_s)
+{
+#ifdef IEEE8021X_EAPOL
+	DBusMessage *reply = NULL;
+	const char *op, *field, *value;
+	char *iface, *net_id;
+	int id;
+	struct wpa_ssid *ssid;
+
+	if (!dbus_message_get_args(message, NULL,
+				   DBUS_TYPE_OBJECT_PATH, &op,
+				   DBUS_TYPE_STRING, &field,
+				   DBUS_TYPE_STRING, &value,
+				   DBUS_TYPE_INVALID))
+		return wpas_dbus_error_invalid_args(message, NULL);
+
+	/* Extract the network ID and ensure the network */
+	/* is actually a child of this interface */
+	iface = wpas_dbus_new_decompose_object_path(op,
+						    WPAS_DBUS_NEW_NETWORKS_PART,
+						    &net_id);
+	if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path ||
+	    os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+		reply = wpas_dbus_error_invalid_args(message, op);
+		goto out;
+	}
+
+	errno = 0;
+	id = strtoul(net_id, NULL, 10);
+	if (errno != 0) {
+		reply = wpas_dbus_error_invalid_args(message, net_id);
+		goto out;
+	}
+
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid == NULL) {
+		reply = wpas_dbus_error_network_unknown(message);
+		goto out;
+	}
+
+	if (wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid,
+						      field, value) < 0)
+		reply = wpas_dbus_error_invalid_args(message, field);
+	else {
+		/* Tell EAP to retry immediately */
+		eapol_sm_notify_ctrl_response(wpa_s->eapol);
+	}
+
+out:
+	os_free(iface);
+	return reply;
+#else /* IEEE8021X_EAPOL */
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
+	return wpas_dbus_error_unknown_error(message, "802.1X not included");
+#endif /* IEEE8021X_EAPOL */
+}
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+
+/**
+ * wpas_dbus_handler_add_blob - Store named binary blob (ie, for certificates)
+ * @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
+ *
+ * Asks wpa_supplicant to internally store a binary blobs.
+ */
+DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	DBusMessageIter	iter, array_iter;
+
+	char *blob_name;
+	u8 *blob_data;
+	int blob_len;
+	struct wpa_config_blob *blob = NULL;
+
+	dbus_message_iter_init(message, &iter);
+	dbus_message_iter_get_basic(&iter, &blob_name);
+
+	if (wpa_config_get_blob(wpa_s->conf, blob_name)) {
+		return dbus_message_new_error(message,
+					      WPAS_DBUS_ERROR_BLOB_EXISTS,
+					      NULL);
+	}
+
+	dbus_message_iter_next(&iter);
+	dbus_message_iter_recurse(&iter, &array_iter);
+
+	dbus_message_iter_get_fixed_array(&array_iter, &blob_data, &blob_len);
+
+	blob = os_zalloc(sizeof(*blob));
+	if (!blob) {
+		reply = wpas_dbus_error_no_memory(message);
+		goto err;
+	}
+
+	blob->data = os_malloc(blob_len);
+	blob->name = os_strdup(blob_name);
+	if (!blob->data || !blob->name) {
+		reply = wpas_dbus_error_no_memory(message);
+		goto err;
+	}
+	os_memcpy(blob->data, blob_data, blob_len);
+	blob->len = blob_len;
+
+	wpa_config_set_blob(wpa_s->conf, blob);
+	wpas_notify_blob_added(wpa_s, blob->name);
+
+	return reply;
+
+err:
+	if (blob) {
+		os_free(blob->name);
+		os_free(blob->data);
+		os_free(blob);
+	}
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_get_blob - Get named binary blob (ie, for certificates)
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing array of bytes (blob)
+ *
+ * Gets one wpa_supplicant's binary blobs.
+ */
+DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	DBusMessageIter	iter, array_iter;
+
+	char *blob_name;
+	const struct wpa_config_blob *blob;
+
+	dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &blob_name,
+			      DBUS_TYPE_INVALID);
+
+	blob = wpa_config_get_blob(wpa_s->conf, blob_name);
+	if (!blob) {
+		return dbus_message_new_error(message,
+					      WPAS_DBUS_ERROR_BLOB_UNKNOWN,
+					      "Blob id not set");
+	}
+
+	reply = dbus_message_new_method_return(message);
+	if (!reply)
+		return wpas_dbus_error_no_memory(message);
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					      DBUS_TYPE_BYTE_AS_STRING,
+					      &array_iter) ||
+	    !dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE,
+						  &(blob->data), blob->len) ||
+	    !dbus_message_iter_close_container(&iter, &array_iter)) {
+		dbus_message_unref(reply);
+		reply = wpas_dbus_error_no_memory(message);
+	}
+
+	return reply;
+}
+
+
+/**
+ * wpas_remove_handler_remove_blob - Remove named binary blob
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: NULL on success or dbus error
+ *
+ * Asks wpa_supplicant to internally remove a binary blobs.
+ */
+DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	char *blob_name;
+
+	dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &blob_name,
+			      DBUS_TYPE_INVALID);
+
+	if (wpa_config_remove_blob(wpa_s->conf, blob_name)) {
+		return dbus_message_new_error(message,
+					      WPAS_DBUS_ERROR_BLOB_UNKNOWN,
+					      "Blob id not set");
+	}
+	wpas_notify_blob_removed(wpa_s, blob_name);
+
+	return reply;
+
+}
+
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+/*
+ * wpas_dbus_handler_flush_bss - Flush the BSS cache
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL
+ *
+ * Handler function for "FlushBSS" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message,
+					  struct wpa_supplicant *wpa_s)
+{
+	dbus_uint32_t age;
+
+	dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &age,
+			      DBUS_TYPE_INVALID);
+
+	if (age == 0)
+		wpa_bss_flush(wpa_s);
+	else
+		wpa_bss_flush_by_age(wpa_s, age);
+
+	return NULL;
+}
+
+
+#ifdef CONFIG_AUTOSCAN
+/**
+ * wpas_dbus_handler_autoscan - Set autoscan parameters for the interface
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL
+ *
+ * Handler function for "AutoScan" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	enum wpa_states state = wpa_s->wpa_state;
+	char *arg;
+
+	dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg,
+			      DBUS_TYPE_INVALID);
+
+	if (arg != NULL && os_strlen(arg) > 0) {
+		char *tmp;
+
+		tmp = os_strdup(arg);
+		if (tmp == NULL) {
+			reply = wpas_dbus_error_no_memory(message);
+		} else {
+			os_free(wpa_s->conf->autoscan);
+			wpa_s->conf->autoscan = tmp;
+			if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
+				autoscan_init(wpa_s, 1);
+			else if (state == WPA_SCANNING)
+				wpa_supplicant_reinit_autoscan(wpa_s);
+		}
+	} else if (arg != NULL && os_strlen(arg) == 0) {
+		os_free(wpa_s->conf->autoscan);
+		wpa_s->conf->autoscan = NULL;
+		autoscan_deinit(wpa_s);
+	} else
+		reply = dbus_message_new_error(message,
+					       DBUS_ERROR_INVALID_ARGS,
+					       NULL);
+
+	return reply;
+}
+#endif /* CONFIG_AUTOSCAN */
+
+
+/*
+ * wpas_dbus_handler_eap_logoff - IEEE 802.1X EAPOL state machine logoff
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL
+ *
+ * Handler function for "EAPLogoff" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_eap_logoff(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	eapol_sm_notify_logoff(wpa_s->eapol, TRUE);
+	return NULL;
+}
+
+
+/*
+ * wpas_dbus_handler_eap_logon - IEEE 802.1X EAPOL state machine logon
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL
+ *
+ * Handler function for "EAPLogin" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_eap_logon(DBusMessage *message,
+					  struct wpa_supplicant *wpa_s)
+{
+	eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
+	return NULL;
+}
+
+
+#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.");
+
+	if (wpa_s->dbus_new_path) {
+		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
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Capabilities" property of an interface.
+ */
+dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
+					  DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	struct wpa_driver_capa capa;
+	int res;
+	DBusMessageIter iter_dict, iter_dict_entry, iter_dict_val, iter_array,
+		variant_iter;
+	const char *scans[] = { "active", "passive", "ssid" };
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      "a{sv}", &variant_iter) ||
+	    !wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+		goto nomem;
+
+	res = wpa_drv_get_capa(wpa_s, &capa);
+
+	/***** pairwise cipher */
+	if (res < 0) {
+		const char *args[] = {"ccmp", "tkip", "none"};
+
+		if (!wpa_dbus_dict_append_string_array(
+			    &iter_dict, "Pairwise", args,
+			    ARRAY_SIZE(args)))
+			goto nomem;
+	} else {
+		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Pairwise",
+						      &iter_dict_entry,
+						      &iter_dict_val,
+						      &iter_array) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "ccmp-256")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "gcmp-256")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "ccmp")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "gcmp")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "tkip")) ||
+		    ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "none")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
+						    &iter_dict_entry,
+						    &iter_dict_val,
+						    &iter_array))
+			goto nomem;
+	}
+
+	/***** group cipher */
+	if (res < 0) {
+		const char *args[] = {
+			"ccmp", "tkip", "wep104", "wep40"
+		};
+
+		if (!wpa_dbus_dict_append_string_array(
+			    &iter_dict, "Group", args,
+			    ARRAY_SIZE(args)))
+			goto nomem;
+	} else {
+		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Group",
+						      &iter_dict_entry,
+						      &iter_dict_val,
+						      &iter_array) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "ccmp-256")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "gcmp-256")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "ccmp")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "gcmp")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "tkip")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "wep104")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "wep40")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
+						    &iter_dict_entry,
+						    &iter_dict_val,
+						    &iter_array))
+			goto nomem;
+	}
+
+	/***** key management */
+	if (res < 0) {
+		const char *args[] = {
+			"wpa-psk", "wpa-eap", "ieee8021x", "wpa-none",
+#ifdef CONFIG_WPS
+			"wps",
+#endif /* CONFIG_WPS */
+			"none"
+		};
+		if (!wpa_dbus_dict_append_string_array(
+			    &iter_dict, "KeyMgmt", args,
+			    ARRAY_SIZE(args)))
+			goto nomem;
+	} else {
+		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "KeyMgmt",
+						      &iter_dict_entry,
+						      &iter_dict_val,
+						      &iter_array) ||
+		    !wpa_dbus_dict_string_array_add_element(&iter_array,
+							    "none") ||
+		    !wpa_dbus_dict_string_array_add_element(&iter_array,
+							    "ieee8021x"))
+			goto nomem;
+
+		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
+			if (!wpa_dbus_dict_string_array_add_element(
+				    &iter_array, "wpa-eap") ||
+			    ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) &&
+			     !wpa_dbus_dict_string_array_add_element(
+				     &iter_array, "wpa-ft-eap")))
+				goto nomem;
+
+/* TODO: Ensure that driver actually supports sha256 encryption. */
+#ifdef CONFIG_IEEE80211W
+			if (!wpa_dbus_dict_string_array_add_element(
+				    &iter_array, "wpa-eap-sha256"))
+				goto nomem;
+#endif /* CONFIG_IEEE80211W */
+		}
+
+		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+			if (!wpa_dbus_dict_string_array_add_element(
+				    &iter_array, "wpa-psk") ||
+			    ((capa.key_mgmt &
+			      WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) &&
+			     !wpa_dbus_dict_string_array_add_element(
+				     &iter_array, "wpa-ft-psk")))
+				goto nomem;
+
+/* TODO: Ensure that driver actually supports sha256 encryption. */
+#ifdef CONFIG_IEEE80211W
+			if (!wpa_dbus_dict_string_array_add_element(
+				    &iter_array, "wpa-psk-sha256"))
+				goto nomem;
+#endif /* CONFIG_IEEE80211W */
+		}
+
+		if ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) &&
+		    !wpa_dbus_dict_string_array_add_element(&iter_array,
+							    "wpa-none"))
+			goto nomem;
+
+
+#ifdef CONFIG_WPS
+		if (!wpa_dbus_dict_string_array_add_element(&iter_array,
+							    "wps"))
+			goto nomem;
+#endif /* CONFIG_WPS */
+
+		if (!wpa_dbus_dict_end_string_array(&iter_dict,
+						    &iter_dict_entry,
+						    &iter_dict_val,
+						    &iter_array))
+			goto nomem;
+	}
+
+	/***** WPA protocol */
+	if (res < 0) {
+		const char *args[] = { "rsn", "wpa" };
+
+		if (!wpa_dbus_dict_append_string_array(
+			    &iter_dict, "Protocol", args,
+			    ARRAY_SIZE(args)))
+			goto nomem;
+	} else {
+		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Protocol",
+						      &iter_dict_entry,
+						      &iter_dict_val,
+						      &iter_array) ||
+		    ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+				       WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "rsn")) ||
+		    ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+				       WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "wpa")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
+						    &iter_dict_entry,
+						    &iter_dict_val,
+						    &iter_array))
+			goto nomem;
+	}
+
+	/***** auth alg */
+	if (res < 0) {
+		const char *args[] = { "open", "shared", "leap" };
+
+		if (!wpa_dbus_dict_append_string_array(
+			    &iter_dict, "AuthAlg", args,
+			    ARRAY_SIZE(args)))
+			goto nomem;
+	} else {
+		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "AuthAlg",
+						      &iter_dict_entry,
+						      &iter_dict_val,
+						      &iter_array))
+			goto nomem;
+
+		if (((capa.auth & WPA_DRIVER_AUTH_OPEN) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "open")) ||
+		    ((capa.auth & WPA_DRIVER_AUTH_SHARED) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "shared")) ||
+		    ((capa.auth & WPA_DRIVER_AUTH_LEAP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "leap")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
+						    &iter_dict_entry,
+						    &iter_dict_val,
+						    &iter_array))
+			goto nomem;
+	}
+
+	/***** Scan */
+	if (!wpa_dbus_dict_append_string_array(&iter_dict, "Scan", scans,
+					       ARRAY_SIZE(scans)))
+		goto nomem;
+
+	/***** Modes */
+	if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Modes",
+					      &iter_dict_entry,
+					      &iter_dict_val,
+					      &iter_array) ||
+	    !wpa_dbus_dict_string_array_add_element(
+		    &iter_array, "infrastructure") ||
+	    !wpa_dbus_dict_string_array_add_element(
+		    &iter_array, "ad-hoc") ||
+	    (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_AP) &&
+	     !wpa_dbus_dict_string_array_add_element(
+		     &iter_array, "ap")) ||
+	    (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) &&
+	     !wpa_dbus_dict_string_array_add_element(
+		     &iter_array, "p2p")) ||
+	    !wpa_dbus_dict_end_string_array(&iter_dict,
+					    &iter_dict_entry,
+					    &iter_dict_val,
+					    &iter_array))
+		goto nomem;
+	/***** Modes end */
+
+	if (res >= 0) {
+		dbus_int32_t max_scan_ssid = capa.max_scan_ssids;
+
+		if (!wpa_dbus_dict_append_int32(&iter_dict, "MaxScanSSID",
+						max_scan_ssid))
+			goto nomem;
+	}
+
+	if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter))
+		goto nomem;
+
+	return TRUE;
+
+nomem:
+	dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+	return FALSE;
+}
+
+
+/**
+ * wpas_dbus_getter_state - Get interface state
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "State" property.
+ */
+dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
+				   void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	const char *str_state;
+	char *state_ls, *tmp;
+	dbus_bool_t success = FALSE;
+
+	str_state = wpa_supplicant_state_txt(wpa_s->wpa_state);
+
+	/* make state string lowercase to fit new DBus API convention
+	 */
+	state_ls = tmp = os_strdup(str_state);
+	if (!tmp) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+	while (*tmp) {
+		*tmp = tolower(*tmp);
+		tmp++;
+	}
+
+	success = wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+						   &state_ls, error);
+
+	os_free(state_ls);
+
+	return success;
+}
+
+
+/**
+ * wpas_dbus_new_iface_get_scanning - Get interface scanning state
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "scanning" property.
+ */
+dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
+				      void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+						&scanning, error);
+}
+
+
+/**
+ * wpas_dbus_getter_ap_scan - Control roaming mode
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "ApScan" property.
+ */
+dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error,
+				     void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_uint32_t ap_scan = wpa_s->conf->ap_scan;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
+						&ap_scan, error);
+}
+
+
+/**
+ * wpas_dbus_setter_ap_scan - Control roaming mode
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "ApScan" property.
+ */
+dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
+				     void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_uint32_t ap_scan;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32,
+					      &ap_scan))
+		return FALSE;
+
+	if (wpa_supplicant_set_ap_scan(wpa_s, ap_scan)) {
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "ap_scan must be 0, 1, or 2");
+		return FALSE;
+	}
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_fast_reauth - Control fast
+ * reauthentication (TLS session resumption)
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "FastReauth" property.
+ */
+dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter,
+					 DBusError *error,
+					 void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_bool_t fast_reauth = wpa_s->conf->fast_reauth ? TRUE : FALSE;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+						&fast_reauth, error);
+}
+
+
+/**
+ * wpas_dbus_setter_fast_reauth - Control fast
+ * reauthentication (TLS session resumption)
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "FastReauth" property.
+ */
+dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter,
+				     DBusError *error,
+				     void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_bool_t fast_reauth;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+					      &fast_reauth))
+		return FALSE;
+
+	wpa_s->conf->fast_reauth = fast_reauth;
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_disconnect_reason - Get most recent reason for disconnect
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "DisconnectReason" property.  The reason is negative if it is
+ * locally generated.
+ */
+dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_int32_t reason = wpa_s->disconnect_reason;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
+						&reason, error);
+}
+
+
+/**
+* wpas_dbus_getter_assoc_status_code - Get most recent failed assoc status code
+* @iter: Pointer to incoming dbus message iter
+* @error: Location to store error on failure
+* @user_data: Function specific data
+* Returns: TRUE on success, FALSE on failure
+*
+* Getter for "AssocStatusCode" property.
+*/
+dbus_bool_t wpas_dbus_getter_assoc_status_code(DBusMessageIter *iter,
+						DBusError *error,
+						void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_int32_t status_code = wpa_s->assoc_status_code;
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
+						&status_code, error);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_expire_age - Get BSS entry expiration age
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "BSSExpireAge" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
+					    DBusError *error,
+					    void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_uint32_t expire_age = wpa_s->conf->bss_expiration_age;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
+						&expire_age, error);
+}
+
+
+/**
+ * wpas_dbus_setter_bss_expire_age - Control BSS entry expiration age
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "BSSExpireAge" property.
+ */
+dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter,
+					    DBusError *error,
+					    void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_uint32_t expire_age;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32,
+					      &expire_age))
+		return FALSE;
+
+	if (wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age)) {
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "BSSExpireAge must be >= 10");
+		return FALSE;
+	}
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_bss_expire_count - Get BSS entry expiration scan count
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "BSSExpireCount" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter,
+					      DBusError *error,
+					      void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_scan_count;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
+						&expire_count, error);
+}
+
+
+/**
+ * wpas_dbus_setter_bss_expire_count - Control BSS entry expiration scan count
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "BSSExpireCount" property.
+ */
+dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter,
+					      DBusError *error,
+					      void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_uint32_t expire_count;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32,
+					      &expire_count))
+		return FALSE;
+
+	if (wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count)) {
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "BSSExpireCount must be > 0");
+		return FALSE;
+	}
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_country - Control country code
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "Country" property.
+ */
+dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error,
+				     void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	char country[3];
+	char *str = country;
+
+	country[0] = wpa_s->conf->country[0];
+	country[1] = wpa_s->conf->country[1];
+	country[2] = '\0';
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+						&str, error);
+}
+
+
+/**
+ * wpas_dbus_setter_country - Control country code
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "Country" property.
+ */
+dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
+				     void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	const char *country;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+					      &country))
+		return FALSE;
+
+	if (!country[0] || !country[1]) {
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "invalid country code");
+		return FALSE;
+	}
+
+	if (wpa_s->drv_priv != NULL && wpa_drv_set_country(wpa_s, country)) {
+		wpa_printf(MSG_DEBUG, "Failed to set country");
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "failed to set country code");
+		return FALSE;
+	}
+
+	wpa_s->conf->country[0] = country[0];
+	wpa_s->conf->country[1] = country[1];
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_scan_interval - Get scan interval
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "ScanInterval" property.
+ */
+dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter,
+					   DBusError *error,
+					   void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_int32_t scan_interval = wpa_s->scan_interval;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
+						&scan_interval, error);
+}
+
+
+/**
+ * wpas_dbus_setter_scan_interval - Control scan interval
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "ScanInterval" property.
+ */
+dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
+					   DBusError *error,
+					   void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_int32_t scan_interval;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_INT32,
+					      &scan_interval))
+		return FALSE;
+
+	if (wpa_supplicant_set_scan_interval(wpa_s, scan_interval)) {
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "scan_interval must be >= 0");
+		return FALSE;
+	}
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_ifname - Get interface name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Ifname" property.
+ */
+dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
+				    void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	const char *ifname = wpa_s->ifname;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+						&ifname, error);
+}
+
+
+/**
+ * wpas_dbus_getter_driver - Get interface name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Driver" property.
+ */
+dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
+				    void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	const char *driver;
+
+	if (wpa_s->driver == NULL || wpa_s->driver->name == NULL) {
+		wpa_printf(MSG_DEBUG, "%s[dbus]: wpa_s has no driver set",
+			   __func__);
+		dbus_set_error(error, DBUS_ERROR_FAILED, "%s: no driver set",
+			       __func__);
+		return FALSE;
+	}
+
+	driver = wpa_s->driver->name;
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+						&driver, error);
+}
+
+
+/**
+ * wpas_dbus_getter_current_bss - Get current bss object path
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "CurrentBSS" property.
+ */
+dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
+					 DBusError *error,
+					 void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf;
+
+	if (wpa_s->current_bss && wpa_s->dbus_new_path)
+		os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+			    "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
+			    wpa_s->dbus_new_path, wpa_s->current_bss->id);
+	else
+		os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
+						&bss_obj_path, error);
+}
+
+
+/**
+ * wpas_dbus_getter_current_network - Get current network object path
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "CurrentNetwork" property.
+ */
+dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf;
+
+	if (wpa_s->current_ssid && wpa_s->dbus_new_path)
+		os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+			    "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+			    wpa_s->dbus_new_path, wpa_s->current_ssid->id);
+	else
+		os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
+						&net_obj_path, error);
+}
+
+
+/**
+ * wpas_dbus_getter_current_auth_mode - Get current authentication type
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "CurrentAuthMode" property.
+ */
+dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	const char *eap_mode;
+	const char *auth_mode;
+	char eap_mode_buf[WPAS_DBUS_AUTH_MODE_MAX];
+
+	if (wpa_s->wpa_state != WPA_COMPLETED) {
+		auth_mode = "INACTIVE";
+	} else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+		eap_mode = wpa_supplicant_get_eap_mode(wpa_s);
+		os_snprintf(eap_mode_buf, WPAS_DBUS_AUTH_MODE_MAX,
+			    "EAP-%s", eap_mode);
+		auth_mode = eap_mode_buf;
+
+	} else {
+		auth_mode = wpa_key_mgmt_txt(wpa_s->key_mgmt,
+					     wpa_s->current_ssid->proto);
+	}
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+						&auth_mode, error);
+}
+
+
+/**
+ * wpas_dbus_getter_bridge_ifname - Get interface name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "BridgeIfname" property.
+ */
+dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
+					   DBusError *error,
+					   void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	const char *bridge_ifname = wpa_s->bridge_ifname;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+						&bridge_ifname, error);
+}
+
+
+/**
+ * wpas_dbus_getter_bsss - Get array of BSSs objects
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "BSSs" property.
+ */
+dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
+				  void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	struct wpa_bss *bss;
+	char **paths;
+	unsigned int i = 0;
+	dbus_bool_t success = FALSE;
+
+	if (!wpa_s->dbus_new_path) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: no D-Bus interface", __func__);
+		return FALSE;
+	}
+
+	paths = os_calloc(wpa_s->num_bss, sizeof(char *));
+	if (!paths) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	/* 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) {
+		paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+		if (paths[i] == NULL) {
+			dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+					     "no memory");
+			goto out;
+		}
+		/* Construct the object path for this BSS. */
+		os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX,
+			    "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
+			    wpa_s->dbus_new_path, bss->id);
+	}
+
+	success = wpas_dbus_simple_array_property_getter(iter,
+							 DBUS_TYPE_OBJECT_PATH,
+							 paths, wpa_s->num_bss,
+							 error);
+
+out:
+	while (i)
+		os_free(paths[--i]);
+	os_free(paths);
+	return success;
+}
+
+
+/**
+ * wpas_dbus_getter_networks - Get array of networks objects
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Networks" property.
+ */
+dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
+				      void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	struct wpa_ssid *ssid;
+	char **paths;
+	unsigned int i = 0, num = 0;
+	dbus_bool_t success = FALSE;
+
+	if (!wpa_s->dbus_new_path) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: no D-Bus interface", __func__);
+		return FALSE;
+	}
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+		if (!network_is_persistent_group(ssid))
+			num++;
+
+	paths = os_calloc(num, sizeof(char *));
+	if (!paths) {
+		dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	/* Loop through configured networks and append object path of each */
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (network_is_persistent_group(ssid))
+			continue;
+		paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+		if (paths[i] == NULL) {
+			dbus_set_error(error, DBUS_ERROR_NO_MEMORY,
+				       "no memory");
+			goto out;
+		}
+
+		/* Construct the object path for this network. */
+		os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX,
+			    "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d",
+			    wpa_s->dbus_new_path, ssid->id);
+	}
+
+	success = wpas_dbus_simple_array_property_getter(iter,
+							 DBUS_TYPE_OBJECT_PATH,
+							 paths, num, error);
+
+out:
+	while (i)
+		os_free(paths[--i]);
+	os_free(paths);
+	return success;
+}
+
+
+/**
+ * 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->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->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
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Blobs" property.
+ */
+dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
+				   void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	DBusMessageIter variant_iter, dict_iter, entry_iter, array_iter;
+	struct wpa_config_blob *blob;
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      "a{say}", &variant_iter) ||
+	    !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+					      "{say}", &dict_iter)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	blob = wpa_s->conf->blobs;
+	while (blob) {
+		if (!dbus_message_iter_open_container(&dict_iter,
+						      DBUS_TYPE_DICT_ENTRY,
+						      NULL, &entry_iter) ||
+		    !dbus_message_iter_append_basic(&entry_iter,
+						    DBUS_TYPE_STRING,
+						    &(blob->name)) ||
+		    !dbus_message_iter_open_container(&entry_iter,
+						      DBUS_TYPE_ARRAY,
+						      DBUS_TYPE_BYTE_AS_STRING,
+						      &array_iter) ||
+		    !dbus_message_iter_append_fixed_array(&array_iter,
+							  DBUS_TYPE_BYTE,
+							  &(blob->data),
+							  blob->len) ||
+		    !dbus_message_iter_close_container(&entry_iter,
+						       &array_iter) ||
+		    !dbus_message_iter_close_container(&dict_iter,
+						       &entry_iter)) {
+			dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+					     "no memory");
+			return FALSE;
+		}
+
+		blob = blob->next;
+	}
+
+	if (!dbus_message_iter_close_container(&variant_iter, &dict_iter) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+static struct wpa_bss * get_bss_helper(struct bss_handler_args *args,
+				       DBusError *error, const char *func_name)
+{
+	struct wpa_bss *res = wpa_bss_get_id(args->wpa_s, args->id);
+
+	if (!res) {
+		wpa_printf(MSG_ERROR, "%s[dbus]: no bss with id %d found",
+			   func_name, args->id);
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: BSS %d not found",
+			       func_name, args->id);
+	}
+
+	return res;
+}
+
+
+/**
+ * wpas_dbus_getter_bss_bssid - Return the BSSID of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "BSSID" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
+				       void *user_data)
+{
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *res;
+
+	res = get_bss_helper(args, error, __func__);
+	if (!res)
+		return FALSE;
+
+	return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+						      res->bssid, ETH_ALEN,
+						      error);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_ssid - Return the SSID of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "SSID" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
+				      void *user_data)
+{
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *res;
+
+	res = get_bss_helper(args, error, __func__);
+	if (!res)
+		return FALSE;
+
+	return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+						      res->ssid, res->ssid_len,
+						      error);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_privacy - Return the privacy flag of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Privacy" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
+					 DBusError *error, void *user_data)
+{
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *res;
+	dbus_bool_t privacy;
+
+	res = get_bss_helper(args, error, __func__);
+	if (!res)
+		return FALSE;
+
+	privacy = (res->caps & IEEE80211_CAP_PRIVACY) ? TRUE : FALSE;
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+						&privacy, error);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_mode - Return the mode of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Mode" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
+				      void *user_data)
+{
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *res;
+	const char *mode;
+
+	res = get_bss_helper(args, error, __func__);
+	if (!res)
+		return FALSE;
+	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);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_level - Return the signal strength of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Level" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
+					DBusError *error, void *user_data)
+{
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *res;
+	s16 level;
+
+	res = get_bss_helper(args, error, __func__);
+	if (!res)
+		return FALSE;
+
+	level = (s16) res->level;
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT16,
+						&level, error);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_frequency - Return the frequency of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Frequency" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter,
+					   DBusError *error, void *user_data)
+{
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *res;
+	u16 freq;
+
+	res = get_bss_helper(args, error, __func__);
+	if (!res)
+		return FALSE;
+
+	freq = (u16) res->freq;
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
+						&freq, error);
+}
+
+
+static int cmp_u8s_desc(const void *a, const void *b)
+{
+	return (*(u8 *) b - *(u8 *) a);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_rates - Return available bit rates of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Rates" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter,
+				       DBusError *error, void *user_data)
+{
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *res;
+	u8 *ie_rates = NULL;
+	u32 *real_rates;
+	int rates_num, i;
+	dbus_bool_t success = FALSE;
+
+	res = get_bss_helper(args, error, __func__);
+	if (!res)
+		return FALSE;
+
+	rates_num = wpa_bss_get_bit_rates(res, &ie_rates);
+	if (rates_num < 0)
+		return FALSE;
+
+	qsort(ie_rates, rates_num, 1, cmp_u8s_desc);
+
+	real_rates = os_malloc(sizeof(u32) * rates_num);
+	if (!real_rates) {
+		os_free(ie_rates);
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	for (i = 0; i < rates_num; i++)
+		real_rates[i] = ie_rates[i] * 500000;
+
+	success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_UINT32,
+							 real_rates, rates_num,
+							 error);
+
+	os_free(ie_rates);
+	os_free(real_rates);
+	return success;
+}
+
+
+static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter,
+						   struct wpa_ie_data *ie_data,
+						   DBusError *error)
+{
+	DBusMessageIter iter_dict, variant_iter;
+	const char *group;
+	const char *pairwise[5]; /* max 5 pairwise ciphers is supported */
+	const char *key_mgmt[9]; /* max 9 key managements may be supported */
+	int n;
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      "a{sv}", &variant_iter))
+		goto nomem;
+
+	if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+		goto nomem;
+
+	/* KeyMgmt */
+	n = 0;
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_PSK)
+		key_mgmt[n++] = "wpa-psk";
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_PSK)
+		key_mgmt[n++] = "wpa-ft-psk";
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+		key_mgmt[n++] = "wpa-psk-sha256";
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+		key_mgmt[n++] = "wpa-eap";
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+		key_mgmt[n++] = "wpa-ft-eap";
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+		key_mgmt[n++] = "wpa-eap-sha256";
+#ifdef CONFIG_SUITEB
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+		key_mgmt[n++] = "wpa-eap-suite-b";
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		key_mgmt[n++] = "wpa-eap-suite-b-192";
+#endif /* CONFIG_SUITEB192 */
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_NONE)
+		key_mgmt[n++] = "wpa-none";
+
+	if (!wpa_dbus_dict_append_string_array(&iter_dict, "KeyMgmt",
+					       key_mgmt, n))
+		goto nomem;
+
+	/* Group */
+	switch (ie_data->group_cipher) {
+	case WPA_CIPHER_WEP40:
+		group = "wep40";
+		break;
+	case WPA_CIPHER_TKIP:
+		group = "tkip";
+		break;
+	case WPA_CIPHER_CCMP:
+		group = "ccmp";
+		break;
+	case WPA_CIPHER_GCMP:
+		group = "gcmp";
+		break;
+	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;
+	}
+
+	if (!wpa_dbus_dict_append_string(&iter_dict, "Group", group))
+		goto nomem;
+
+	/* Pairwise */
+	n = 0;
+	if (ie_data->pairwise_cipher & WPA_CIPHER_TKIP)
+		pairwise[n++] = "tkip";
+	if (ie_data->pairwise_cipher & WPA_CIPHER_CCMP)
+		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))
+		goto nomem;
+
+	/* Management group (RSN only) */
+	if (ie_data->proto == WPA_PROTO_RSN) {
+		switch (ie_data->mgmt_group_cipher) {
+#ifdef CONFIG_IEEE80211W
+		case WPA_CIPHER_AES_128_CMAC:
+			group = "aes128cmac";
+			break;
+#endif /* CONFIG_IEEE80211W */
+		default:
+			group = "";
+			break;
+		}
+
+		if (!wpa_dbus_dict_append_string(&iter_dict, "MgmtGroup",
+						 group))
+			goto nomem;
+	}
+
+	if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter))
+		goto nomem;
+
+	return TRUE;
+
+nomem:
+	dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+	return FALSE;
+}
+
+
+/**
+ * wpas_dbus_getter_bss_wpa - Return the WPA options of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "WPA" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
+				     void *user_data)
+{
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *res;
+	struct wpa_ie_data wpa_data;
+	const u8 *ie;
+
+	res = get_bss_helper(args, error, __func__);
+	if (!res)
+		return FALSE;
+
+	os_memset(&wpa_data, 0, sizeof(wpa_data));
+	ie = wpa_bss_get_vendor_ie(res, WPA_IE_VENDOR_TYPE);
+	if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "failed to parse WPA IE");
+		return FALSE;
+	}
+
+	return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_rsn - Return the RSN options of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "RSN" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
+				     void *user_data)
+{
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *res;
+	struct wpa_ie_data wpa_data;
+	const u8 *ie;
+
+	res = get_bss_helper(args, error, __func__);
+	if (!res)
+		return FALSE;
+
+	os_memset(&wpa_data, 0, sizeof(wpa_data));
+	ie = wpa_bss_get_ie(res, WLAN_EID_RSN);
+	if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "failed to parse RSN IE");
+		return FALSE;
+	}
+
+	return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_wps - Return the WPS options of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "WPS" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error,
+				     void *user_data)
+{
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *res;
+#ifdef CONFIG_WPS
+	struct wpabuf *wps_ie;
+#endif /* CONFIG_WPS */
+	DBusMessageIter iter_dict, variant_iter;
+	int wps_support = 0;
+	const char *type = "";
+
+	res = get_bss_helper(args, error, __func__);
+	if (!res)
+		return FALSE;
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      "a{sv}", &variant_iter) ||
+	    !wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+		goto nomem;
+
+#ifdef CONFIG_WPS
+	wps_ie = wpa_bss_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE);
+	if (wps_ie) {
+		wps_support = 1;
+		if (wps_is_selected_pbc_registrar(wps_ie))
+			type = "pbc";
+		else if (wps_is_selected_pin_registrar(wps_ie))
+			type = "pin";
+
+		wpabuf_free(wps_ie);
+	}
+#endif /* CONFIG_WPS */
+
+	if ((wps_support && !wpa_dbus_dict_append_string(&iter_dict, "Type", type)) ||
+	    !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter))
+		goto nomem;
+
+	return TRUE;
+
+nomem:
+	dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+	return FALSE;
+}
+
+
+/**
+ * wpas_dbus_getter_bss_ies - Return all IEs of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "IEs" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
+				     void *user_data)
+{
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *res;
+
+	res = get_bss_helper(args, error, __func__);
+	if (!res)
+		return FALSE;
+
+	return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+						      res + 1, res->ie_len,
+						      error);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_age - Return time in seconds since BSS was last seen
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for BSS age
+ */
+dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error,
+				     void *user_data)
+{
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *res;
+	struct os_reltime now, diff = { 0, 0 };
+	u32 age;
+
+	res = get_bss_helper(args, error, __func__);
+	if (!res)
+		return FALSE;
+
+	os_get_reltime(&now);
+	os_reltime_sub(&now, &res->last_update, &diff);
+	age = diff.sec > 0 ? diff.sec : 0;
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32, &age,
+						error);
+}
+
+
+/**
+ * wpas_dbus_getter_enabled - Check whether network is enabled or disabled
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "enabled" property of a configured network.
+ */
+dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error,
+				     void *user_data)
+{
+	struct network_handler_args *net = user_data;
+	dbus_bool_t enabled = net->ssid->disabled ? FALSE : TRUE;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+						&enabled, error);
+}
+
+
+/**
+ * wpas_dbus_setter_enabled - Mark a configured network as enabled or disabled
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "Enabled" property of a configured network.
+ */
+dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error,
+				     void *user_data)
+{
+	struct network_handler_args *net = user_data;
+	struct wpa_supplicant *wpa_s;
+	struct wpa_ssid *ssid;
+	dbus_bool_t enable;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+					      &enable))
+		return FALSE;
+
+	wpa_s = net->wpa_s;
+	ssid = net->ssid;
+
+	if (enable)
+		wpa_supplicant_enable_network(wpa_s, ssid);
+	else
+		wpa_supplicant_disable_network(wpa_s, ssid);
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_network_properties - Get options for a configured network
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Properties" property of a configured network.
+ */
+dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter,
+						DBusError *error,
+						void *user_data)
+{
+	struct network_handler_args *net = user_data;
+	DBusMessageIter	variant_iter, dict_iter;
+	char **iterator;
+	char **props = wpa_config_get_all(net->ssid, 1);
+	dbus_bool_t success = FALSE;
+
+	if (!props) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}",
+					      &variant_iter) ||
+	    !wpa_dbus_dict_open_write(&variant_iter, &dict_iter)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		goto out;
+	}
+
+	iterator = props;
+	while (*iterator) {
+		if (!wpa_dbus_dict_append_string(&dict_iter, *iterator,
+						 *(iterator + 1))) {
+			dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+					     "no memory");
+			goto out;
+		}
+		iterator += 2;
+	}
+
+
+	if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		goto out;
+	}
+
+	success = TRUE;
+
+out:
+	iterator = props;
+	while (*iterator) {
+		os_free(*iterator);
+		iterator++;
+	}
+	os_free(props);
+	return success;
+}
+
+
+/**
+ * wpas_dbus_setter_network_properties - Set options for a configured network
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "Properties" property of a configured network.
+ */
+dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
+						DBusError *error,
+						void *user_data)
+{
+	struct network_handler_args *net = user_data;
+	struct wpa_ssid *ssid = net->ssid;
+	DBusMessageIter	variant_iter;
+
+	dbus_message_iter_recurse(iter, &variant_iter);
+	return set_network_properties(net->wpa_s, ssid, &variant_iter, error);
+}
+
+
+#ifdef CONFIG_AP
+
+DBusMessage * wpas_dbus_handler_subscribe_preq(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dbus_priv *priv = wpa_s->global->dbus;
+	char *name;
+
+	if (wpa_s->preq_notify_peer != NULL) {
+		if (os_strcmp(dbus_message_get_sender(message),
+			      wpa_s->preq_notify_peer) == 0)
+			return NULL;
+
+		return dbus_message_new_error(message,
+			WPAS_DBUS_ERROR_SUBSCRIPTION_IN_USE,
+			"Another application is already subscribed");
+	}
+
+	name = os_strdup(dbus_message_get_sender(message));
+	if (!name)
+		return wpas_dbus_error_no_memory(message);
+
+	wpa_s->preq_notify_peer = name;
+
+	/* Subscribe to clean up if application closes socket */
+	wpas_dbus_subscribe_noc(priv);
+
+	/*
+	 * Double-check it's still alive to make sure that we didn't
+	 * miss the NameOwnerChanged signal, e.g. while strdup'ing.
+	 */
+	if (!dbus_bus_name_has_owner(priv->con, name, NULL)) {
+		/*
+		 * Application no longer exists, clean up.
+		 * The return value is irrelevant now.
+		 *
+		 * Need to check if the NameOwnerChanged handling
+		 * already cleaned up because we have processed
+		 * DBus messages while checking if the name still
+		 * has an owner.
+		 */
+		if (!wpa_s->preq_notify_peer)
+			return NULL;
+		os_free(wpa_s->preq_notify_peer);
+		wpa_s->preq_notify_peer = NULL;
+		wpas_dbus_unsubscribe_noc(priv);
+	}
+
+	return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_unsubscribe_preq(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dbus_priv *priv = wpa_s->global->dbus;
+
+	if (!wpa_s->preq_notify_peer)
+		return dbus_message_new_error(message,
+			WPAS_DBUS_ERROR_NO_SUBSCRIPTION,
+			"Not subscribed");
+
+	if (os_strcmp(wpa_s->preq_notify_peer,
+		      dbus_message_get_sender(message)))
+		return dbus_message_new_error(message,
+			WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM,
+			"Can't unsubscribe others");
+
+	os_free(wpa_s->preq_notify_peer);
+	wpa_s->preq_notify_peer = NULL;
+	wpas_dbus_unsubscribe_noc(priv);
+	return NULL;
+}
+
+
+void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s,
+			   const u8 *addr, const u8 *dst, const u8 *bssid,
+			   const u8 *ie, size_t ie_len, u32 ssi_signal)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *priv = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (priv == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	if (wpa_s->preq_notify_peer == NULL)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "ProbeRequest");
+	if (msg == NULL)
+		return;
+
+	dbus_message_set_destination(msg, wpa_s->preq_notify_peer);
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    (addr && !wpa_dbus_dict_append_byte_array(&dict_iter, "addr",
+						      (const char *) addr,
+						      ETH_ALEN)) ||
+	    (dst && !wpa_dbus_dict_append_byte_array(&dict_iter, "dst",
+						     (const char *) dst,
+						     ETH_ALEN)) ||
+	    (bssid && !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid",
+						       (const char *) bssid,
+						       ETH_ALEN)) ||
+	    (ie && ie_len && !wpa_dbus_dict_append_byte_array(&dict_iter, "ies",
+							      (const char *) ie,
+							      ie_len)) ||
+	    (ssi_signal && !wpa_dbus_dict_append_int32(&dict_iter, "signal",
+						       ssi_signal)) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		goto fail;
+
+	dbus_connection_send(priv->con, msg, NULL);
+	goto out;
+fail:
+	wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+out:
+	dbus_message_unref(msg);
+}
+
+#endif /* CONFIG_AP */
diff --git a/hostap/wpa_supplicant/dbus/dbus_new_handlers.h b/hostap/wpa_supplicant/dbus/dbus_new_handlers.h
new file mode 100644
index 0000000..e2ad1a4
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -0,0 +1,339 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CTRL_IFACE_DBUS_NEW_HANDLERS_H
+#define CTRL_IFACE_DBUS_NEW_HANDLERS_H
+
+struct network_handler_args {
+	struct wpa_supplicant *wpa_s;
+	struct wpa_ssid *ssid;
+};
+
+struct bss_handler_args {
+	struct wpa_supplicant *wpa_s;
+	unsigned int id;
+};
+
+dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter,
+					     const int type,
+					     const void *val,
+					     DBusError *error);
+
+dbus_bool_t wpas_dbus_simple_property_setter(DBusMessageIter *iter,
+					     DBusError *error,
+					     const int type, void *val);
+
+dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter,
+						   const int type,
+						   const void *array,
+						   size_t array_len,
+						   DBusError *error);
+
+dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter,
+							 const int type,
+							 struct wpabuf **array,
+							 size_t array_len,
+							 DBusError *error);
+
+DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
+						 struct wpa_global *global);
+
+DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message,
+						 struct wpa_global *global);
+
+DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message,
+					      struct wpa_global *global);
+
+dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
+					 DBusError *error,
+					 void *user_data);
+
+dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data);
+
+dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
+					 DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data);
+
+dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter,
+					DBusError *error,
+					void *user_data);
+
+dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
+					 DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter,
+						 DBusError *error,
+						 void *user_data);
+
+DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
+				     struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_signal_poll(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s);
+
+dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
+				   struct wpa_ssid *ssid,
+				   DBusMessageIter *iter,
+				   DBusError *error);
+
+DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s);
+
+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_reconnect(DBusMessage *message,
+					  struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
+					       struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_remove_all_networks(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message,
+					       struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message,
+					      struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s);
+
+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);
+
+DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_eap_logoff(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_eap_logon(DBusMessage *message,
+					  struct wpa_supplicant *wpa_s);
+
+dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
+					  DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
+				   void *user_data);
+
+dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
+				      void *user_data);
+
+dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error,
+				     void *user_data);
+
+dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
+				     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter,
+					 DBusError *error,
+					 void *user_data);
+
+dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter,
+					 DBusError *error,
+					 void *user_data);
+
+dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data);
+
+dbus_bool_t wpas_dbus_getter_assoc_status_code(DBusMessageIter *iter,
+					      DBusError *error,
+					      void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
+					    DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter,
+					    DBusError *error,
+					    void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter,
+					      DBusError *error,
+					      void *user_data);
+
+dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter,
+					      DBusError *error,
+					      void *user_data);
+
+dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error,
+				     void *user_data);
+
+dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
+				     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter,
+					   DBusError *error,
+					   void *user_data);
+
+dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
+					   DBusError *error,
+					   void *user_data);
+
+dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
+				    void *user_data);
+
+dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
+				    void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
+					   DBusError *error,
+					   void *user_data);
+
+dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
+					 DBusError *error,
+					 void *user_data);
+
+dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
+				  void *user_data);
+
+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);
+
+dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
+				       void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
+				      void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
+					 DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
+				      void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
+					DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter,
+					   DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter,
+				       DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
+				     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
+				     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error,
+				     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
+				     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error,
+				     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error,
+				     void *user_data);
+
+dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error,
+				     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter,
+						DBusError *error,
+						void *user_data);
+
+dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
+						DBusError *error,
+						void *user_data);
+
+DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
+					  struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s);
+
+dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
+	DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
+						 DBusError *error,
+						 void *user_data);
+
+dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter,
+					    DBusError *error,
+					    void *user_data);
+
+dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter,
+					    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,
+					    const char *arg);
+DBusMessage * wpas_dbus_error_no_memory(DBusMessage *message);
+
+DBusMessage * wpas_dbus_handler_subscribe_preq(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_unsubscribe_preq(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+#endif /* CTRL_IFACE_DBUS_HANDLERS_NEW_H */
diff --git a/hostap/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/hostap/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
new file mode 100644
index 0000000..67c079e
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -0,0 +1,2948 @@
+/*
+ * WPA Supplicant / dbus-based control interface (P2P)
+ * Copyright (c) 2011-2012, Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/includes.h"
+#include "common.h"
+#include "../config.h"
+#include "../wpa_supplicant_i.h"
+#include "../wps_supplicant.h"
+#include "../notify.h"
+#include "dbus_new_helpers.h"
+#include "dbus_new.h"
+#include "dbus_new_handlers.h"
+#include "dbus_new_handlers_p2p.h"
+#include "dbus_dict_helpers.h"
+#include "p2p/p2p.h"
+#include "common/ieee802_11_defs.h"
+#include "ap/hostapd.h"
+#include "ap/ap_config.h"
+#include "ap/wps_hostapd.h"
+
+#include "../p2p_supplicant.h"
+#include "../wifi_display.h"
+
+/**
+ * Parses out the mac address from the peer object path.
+ * @peer_path - object path of the form
+ *	/fi/w1/wpa_supplicant1/Interfaces/n/Peers/00112233445566 (no colons)
+ * @addr - out param must be of ETH_ALEN size
+ * Returns 0 if valid (including MAC), -1 otherwise
+ */
+static int parse_peer_object_path(const char *peer_path, u8 addr[ETH_ALEN])
+{
+	const char *p;
+
+	if (!peer_path)
+		return -1;
+	p = os_strrchr(peer_path, '/');
+	if (!p)
+		return -1;
+	p++;
+	return hwaddr_compact_aton(p, addr);
+}
+
+
+/**
+ * wpas_dbus_error_persistent_group_unknown - Return a new PersistentGroupUnknown
+ * error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an invalid persistent group error.
+ */
+static DBusMessage *
+wpas_dbus_error_persistent_group_unknown(DBusMessage *message)
+{
+	return dbus_message_new_error(
+		message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN,
+		"There is no such persistent group in this P2P device.");
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s)
+{
+	struct wpa_dbus_dict_entry entry;
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter;
+	DBusMessageIter iter_dict;
+	unsigned int timeout = 0;
+	enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
+	int num_req_dev_types = 0;
+	unsigned int i;
+	u8 *req_dev_types = NULL;
+
+	dbus_message_iter_init(message, &iter);
+	entry.key = NULL;
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto error;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto error;
+
+		if (os_strcmp(entry.key, "Timeout") == 0 &&
+		    entry.type == DBUS_TYPE_INT32) {
+			timeout = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "RequestedDeviceTypes") == 0) {
+			if (entry.type != DBUS_TYPE_ARRAY ||
+			    entry.array_type != WPAS_DBUS_TYPE_BINARRAY)
+				goto error_clear;
+
+			os_free(req_dev_types);
+			req_dev_types =
+				os_malloc(WPS_DEV_TYPE_LEN * entry.array_len);
+			if (!req_dev_types)
+				goto error_clear;
+
+			for (i = 0; i < entry.array_len; i++) {
+				if (wpabuf_len(entry.binarray_value[i]) !=
+				    WPS_DEV_TYPE_LEN)
+					goto error_clear;
+				os_memcpy(req_dev_types + i * WPS_DEV_TYPE_LEN,
+					  wpabuf_head(entry.binarray_value[i]),
+					  WPS_DEV_TYPE_LEN);
+			}
+			num_req_dev_types = entry.array_len;
+		} else if (os_strcmp(entry.key, "DiscoveryType") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			if (os_strcmp(entry.str_value, "start_with_full") == 0)
+				type = P2P_FIND_START_WITH_FULL;
+			else if (os_strcmp(entry.str_value, "social") == 0)
+				type = P2P_FIND_ONLY_SOCIAL;
+			else if (os_strcmp(entry.str_value, "progressive") == 0)
+				type = P2P_FIND_PROGRESSIVE;
+			else
+				goto error_clear;
+		} else
+			goto error_clear;
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types,
+		      NULL, 0, 0, NULL, 0);
+	os_free(req_dev_types);
+	return reply;
+
+error_clear:
+	wpa_dbus_dict_entry_clear(&entry);
+error:
+	os_free(req_dev_types);
+	reply = wpas_dbus_error_invalid_args(message, entry.key);
+	return reply;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_stop_find(DBusMessage *message,
+					      struct wpa_supplicant *wpa_s)
+{
+	wpas_p2p_stop_find(wpa_s->global->p2p_init_wpa_s);
+	return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_rejectpeer(DBusMessage *message,
+					       struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter;
+	char *peer_object_path = NULL;
+	u8 peer_addr[ETH_ALEN];
+
+	dbus_message_iter_init(message, &iter);
+	dbus_message_iter_get_basic(&iter, &peer_object_path);
+
+	if (parse_peer_object_path(peer_object_path, peer_addr) < 0)
+		return wpas_dbus_error_invalid_args(message, NULL);
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (wpas_p2p_reject(wpa_s, peer_addr) < 0)
+		return wpas_dbus_error_unknown_error(message,
+				"Failed to call wpas_p2p_reject method.");
+
+	return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_listen(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	dbus_int32_t timeout = 0;
+
+	if (!dbus_message_get_args(message, NULL, DBUS_TYPE_INT32, &timeout,
+				   DBUS_TYPE_INVALID))
+		return wpas_dbus_error_no_memory(message);
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (wpas_p2p_listen(wpa_s, (unsigned int) timeout)) {
+		return dbus_message_new_error(message,
+					      WPAS_DBUS_ERROR_UNKNOWN_ERROR,
+					      "Could not start P2P listen");
+	}
+
+	return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_extendedlisten(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	unsigned int period = 0, interval = 0;
+	struct wpa_dbus_dict_entry entry;
+	DBusMessageIter iter;
+	DBusMessageIter iter_dict;
+
+	dbus_message_iter_init(message, &iter);
+	entry.key = NULL;
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto error;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto error;
+
+		if (os_strcmp(entry.key, "period") == 0 &&
+		    entry.type == DBUS_TYPE_INT32)
+			period = entry.uint32_value;
+		else if (os_strcmp(entry.key, "interval") == 0 &&
+			 entry.type == DBUS_TYPE_INT32)
+			interval = entry.uint32_value;
+		else
+			goto error_clear;
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (wpas_p2p_ext_listen(wpa_s, period, interval))
+		return wpas_dbus_error_unknown_error(
+			message, "failed to initiate a p2p_ext_listen.");
+
+	return NULL;
+
+error_clear:
+	wpa_dbus_dict_entry_clear(&entry);
+error:
+	return wpas_dbus_error_invalid_args(message, entry.key);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_presence_request(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0;
+	struct wpa_dbus_dict_entry entry;
+	DBusMessageIter iter;
+	DBusMessageIter iter_dict;
+
+	dbus_message_iter_init(message, &iter);
+	entry.key = NULL;
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto error;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto error;
+
+		if (os_strcmp(entry.key, "duration1") == 0 &&
+		    entry.type == DBUS_TYPE_INT32)
+			dur1 = entry.uint32_value;
+		else if (os_strcmp(entry.key, "interval1") == 0 &&
+			 entry.type == DBUS_TYPE_INT32)
+			int1 = entry.uint32_value;
+		else if (os_strcmp(entry.key, "duration2") == 0 &&
+			 entry.type == DBUS_TYPE_INT32)
+			dur2 = entry.uint32_value;
+		else if (os_strcmp(entry.key, "interval2") == 0 &&
+			 entry.type == DBUS_TYPE_INT32)
+			int2 = entry.uint32_value;
+		else
+			goto error_clear;
+
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+
+	if (wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2) < 0)
+		return wpas_dbus_error_unknown_error(message,
+				"Failed to invoke presence request.");
+
+	return NULL;
+
+error_clear:
+	wpa_dbus_dict_entry_clear(&entry);
+error:
+	return wpas_dbus_error_invalid_args(message, entry.key);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message,
+					      struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter_dict;
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter;
+	struct wpa_dbus_dict_entry entry;
+	char *pg_object_path = NULL;
+	int persistent_group = 0;
+	int freq = 0;
+	char *iface = NULL;
+	unsigned int group_id = 0;
+	struct wpa_ssid *ssid;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto inv_args;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto inv_args;
+
+		if (os_strcmp(entry.key, "persistent") == 0 &&
+		    entry.type == DBUS_TYPE_BOOLEAN) {
+			persistent_group = entry.bool_value;
+		} else if (os_strcmp(entry.key, "frequency") == 0 &&
+			   entry.type == DBUS_TYPE_INT32) {
+			freq = entry.int32_value;
+			if (freq <= 0)
+				goto inv_args_clear;
+		} else if (os_strcmp(entry.key, "persistent_group_object") ==
+			   0 &&
+			   entry.type == DBUS_TYPE_OBJECT_PATH)
+			pg_object_path = os_strdup(entry.str_value);
+		else
+			goto inv_args_clear;
+
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (pg_object_path != NULL) {
+		char *net_id_str;
+
+		/*
+		 * A persistent group Object Path is defined meaning we want
+		 * to re-invoke a persistent group.
+		 */
+
+		iface = wpas_dbus_new_decompose_object_path(
+			pg_object_path, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART,
+			&net_id_str);
+		if (iface == NULL || net_id_str == NULL ||
+		    !wpa_s->parent->dbus_new_path ||
+		    os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) {
+			reply =
+			    wpas_dbus_error_invalid_args(message,
+							 pg_object_path);
+			goto out;
+		}
+
+		group_id = strtoul(net_id_str, NULL, 10);
+		if (errno == EINVAL) {
+			reply = wpas_dbus_error_invalid_args(
+						message, pg_object_path);
+			goto out;
+		}
+
+		/* Get the SSID structure from the persistent group id */
+		ssid = wpa_config_get_network(wpa_s->conf, group_id);
+		if (ssid == NULL || ssid->disabled != 2)
+			goto inv_args;
+
+		if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0,
+						  NULL, 0, 0)) {
+			reply = wpas_dbus_error_unknown_error(
+				message,
+				"Failed to reinvoke a persistent group");
+			goto out;
+		}
+	} else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0))
+		goto inv_args;
+
+out:
+	os_free(pg_object_path);
+	os_free(iface);
+	return reply;
+inv_args_clear:
+	wpa_dbus_dict_entry_clear(&entry);
+inv_args:
+	reply = wpas_dbus_error_invalid_args(message, NULL);
+	goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_disconnect(DBusMessage *message,
+					       struct wpa_supplicant *wpa_s)
+{
+	if (wpas_p2p_disconnect(wpa_s))
+		return wpas_dbus_error_unknown_error(message,
+						"failed to disconnect");
+
+	return NULL;
+}
+
+
+static dbus_bool_t wpa_dbus_p2p_check_enabled(struct wpa_supplicant *wpa_s,
+					      DBusMessage *message,
+					      DBusMessage **out_reply,
+					      DBusError *error)
+{
+	/* Return an error message or an error if P2P isn't available */
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) {
+		if (out_reply) {
+			*out_reply = dbus_message_new_error(
+				message, DBUS_ERROR_FAILED,
+				"P2P is not available for this interface");
+		}
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "P2P is not available for this interface");
+		return FALSE;
+	}
+	return TRUE;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_remove_client(DBusMessage *message,
+						  struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter_dict;
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter;
+	struct wpa_dbus_dict_entry entry;
+	char *peer_object_path = NULL;
+	char *interface_addr = NULL;
+	u8 peer_addr[ETH_ALEN];
+
+	if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
+		return reply;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto err;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto err;
+
+		if (os_strcmp(entry.key, "peer") == 0 &&
+		    entry.type == DBUS_TYPE_OBJECT_PATH) {
+			os_free(peer_object_path);
+			peer_object_path = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "iface") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			os_free(interface_addr);
+			interface_addr = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else {
+			wpa_dbus_dict_entry_clear(&entry);
+			goto err;
+		}
+	}
+
+	if ((!peer_object_path && !interface_addr) ||
+	    (peer_object_path &&
+	     (parse_peer_object_path(peer_object_path, peer_addr) < 0 ||
+	      !p2p_peer_known(wpa_s->global->p2p, peer_addr))) ||
+	    (interface_addr && hwaddr_aton(interface_addr, peer_addr) < 0))
+		goto err;
+
+	wpas_p2p_remove_client(wpa_s, peer_addr, interface_addr != NULL);
+	reply = NULL;
+out:
+	os_free(peer_object_path);
+	os_free(interface_addr);
+	return reply;
+err:
+	reply = wpas_dbus_error_invalid_args(message, "Invalid address format");
+	goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message,
+					  struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+
+	if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
+		return reply;
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+	wpa_s->force_long_sd = 0;
+	p2p_flush(wpa_s->global->p2p);
+
+	return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter_dict;
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter;
+	struct wpa_dbus_dict_entry entry;
+	char *peer_object_path = NULL;
+	int persistent_group = 0;
+	int join = 0;
+	int authorize_only = 0;
+	int go_intent = -1;
+	int freq = 0;
+	u8 addr[ETH_ALEN];
+	char *pin = NULL;
+	enum p2p_wps_method wps_method = WPS_NOT_READY;
+	int new_pin;
+	char *err_msg = NULL;
+	char *iface = NULL;
+
+	if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
+		return reply;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto inv_args;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto inv_args;
+
+		if (os_strcmp(entry.key, "peer") == 0 &&
+		    entry.type == DBUS_TYPE_OBJECT_PATH) {
+			peer_object_path = os_strdup(entry.str_value);
+		} else if (os_strcmp(entry.key, "persistent") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			persistent_group = entry.bool_value;
+		} else if (os_strcmp(entry.key, "join") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			join = entry.bool_value;
+		} else if (os_strcmp(entry.key, "authorize_only") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			authorize_only = entry.bool_value;
+		} else if (os_strcmp(entry.key, "frequency") == 0 &&
+			   entry.type == DBUS_TYPE_INT32) {
+			freq = entry.int32_value;
+			if (freq <= 0)
+				goto inv_args_clear;
+		} else if (os_strcmp(entry.key, "go_intent") == 0 &&
+			   entry.type == DBUS_TYPE_INT32) {
+			go_intent = entry.int32_value;
+			if ((go_intent < 0) || (go_intent > 15))
+				goto inv_args_clear;
+		} else if (os_strcmp(entry.key, "wps_method") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			if (os_strcmp(entry.str_value, "pbc") == 0)
+				wps_method = WPS_PBC;
+			else if (os_strcmp(entry.str_value, "pin") == 0)
+				wps_method = WPS_PIN_DISPLAY;
+			else if (os_strcmp(entry.str_value, "display") == 0)
+				wps_method = WPS_PIN_DISPLAY;
+			else if (os_strcmp(entry.str_value, "keypad") == 0)
+				wps_method = WPS_PIN_KEYPAD;
+			else
+				goto inv_args_clear;
+		} else if (os_strcmp(entry.key, "pin") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			pin = os_strdup(entry.str_value);
+		} else
+			goto inv_args_clear;
+
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+
+	if (wps_method == WPS_NOT_READY ||
+	    parse_peer_object_path(peer_object_path, addr) < 0 ||
+	    !p2p_peer_known(wpa_s->global->p2p, addr))
+		goto inv_args;
+
+	/*
+	 * Validate the wps_method specified and the pin value.
+	 */
+	if ((!pin || !pin[0]) && wps_method == WPS_PIN_KEYPAD)
+		goto inv_args;
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
+				   persistent_group, 0, join, authorize_only,
+				   go_intent, freq, -1, 0, 0, 0);
+
+	if (new_pin >= 0) {
+		char npin[9];
+		char *generated_pin;
+
+		os_snprintf(npin, sizeof(npin), "%08d", new_pin);
+		generated_pin = npin;
+		reply = dbus_message_new_method_return(message);
+		dbus_message_append_args(reply, DBUS_TYPE_STRING,
+					 &generated_pin, DBUS_TYPE_INVALID);
+	} else {
+		switch (new_pin) {
+		case -2:
+			err_msg =
+				"connect failed due to channel unavailability.";
+			iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE;
+			break;
+
+		case -3:
+			err_msg = "connect failed due to unsupported channel.";
+			iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNSUPPORTED;
+			break;
+
+		default:
+			err_msg = "connect failed due to unspecified error.";
+			iface = WPAS_DBUS_ERROR_CONNECT_UNSPECIFIED_ERROR;
+			break;
+		}
+
+		/*
+		 * TODO:
+		 * Do we need specialized errors corresponding to above
+		 * error conditions as against just returning a different
+		 * error message?
+		 */
+		reply = dbus_message_new_error(message, iface, err_msg);
+	}
+
+out:
+	os_free(peer_object_path);
+	os_free(pin);
+	return reply;
+inv_args_clear:
+	wpa_dbus_dict_entry_clear(&entry);
+inv_args:
+	reply = wpas_dbus_error_invalid_args(message, NULL);
+	goto out;
+}
+
+
+/**
+ * wpas_dbus_handler_p2p_cancel - Cancel P2P group formation
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: NULL on success or DBus error on failure
+ *
+ * Handler for "Cancel" method call. Returns NULL if P2P cancel succeeds or DBus
+ * error on P2P cancel failure
+ */
+DBusMessage * wpas_dbus_handler_p2p_cancel(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	if (wpas_p2p_cancel(wpa_s))
+		return wpas_dbus_error_unknown_error(message,
+						     "P2P cancel failed");
+
+	return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter_dict;
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter;
+	struct wpa_dbus_dict_entry entry;
+	char *peer_object_path = NULL;
+	char *pg_object_path = NULL;
+	char *iface = NULL;
+	u8 peer_addr[ETH_ALEN];
+	unsigned int group_id = 0;
+	int persistent = 0;
+	struct wpa_ssid *ssid;
+
+	if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
+		return reply;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto err;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto err;
+
+		if (os_strcmp(entry.key, "peer") == 0 &&
+		    entry.type == DBUS_TYPE_OBJECT_PATH) {
+			peer_object_path = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "persistent_group_object") ==
+			   0 &&
+			   entry.type == DBUS_TYPE_OBJECT_PATH) {
+			pg_object_path = os_strdup(entry.str_value);
+			persistent = 1;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else {
+			wpa_dbus_dict_entry_clear(&entry);
+			goto err;
+		}
+	}
+
+	if (parse_peer_object_path(peer_object_path, peer_addr) < 0 ||
+	    !p2p_peer_known(wpa_s->global->p2p, peer_addr))
+		goto err;
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (persistent) {
+		char *net_id_str;
+		/*
+		 * A group ID is defined meaning we want to re-invoke a
+		 * persistent group
+		 */
+
+		iface = wpas_dbus_new_decompose_object_path(
+			pg_object_path,
+			WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART,
+			&net_id_str);
+		if (iface == NULL || net_id_str == NULL ||
+		    !wpa_s->parent->dbus_new_path ||
+		    os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) {
+			reply = wpas_dbus_error_invalid_args(message,
+							     pg_object_path);
+			goto out;
+		}
+
+		group_id = strtoul(net_id_str, NULL, 10);
+		if (errno == EINVAL) {
+			reply = wpas_dbus_error_invalid_args(
+				message, pg_object_path);
+			goto out;
+		}
+
+		/* Get the SSID structure from the persistent group id */
+		ssid = wpa_config_get_network(wpa_s->conf, group_id);
+		if (ssid == NULL || ssid->disabled != 2)
+			goto err;
+
+		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");
+			goto out;
+		}
+	} else {
+		/*
+		 * No group ID means propose to a peer to join my active group
+		 */
+		if (wpas_p2p_invite_group(wpa_s, wpa_s->ifname,
+					  peer_addr, NULL)) {
+			reply = wpas_dbus_error_unknown_error(
+				message, "Failed to join to an active group");
+			goto out;
+		}
+	}
+
+out:
+	os_free(iface);
+	os_free(pg_object_path);
+	os_free(peer_object_path);
+	return reply;
+
+err:
+	reply = wpas_dbus_error_invalid_args(message, NULL);
+	goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message,
+						  struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter;
+	char *peer_object_path = NULL;
+	char *config_method = NULL;
+	u8 peer_addr[ETH_ALEN];
+
+	dbus_message_iter_init(message, &iter);
+	dbus_message_iter_get_basic(&iter, &peer_object_path);
+
+	if (parse_peer_object_path(peer_object_path, peer_addr) < 0)
+		return wpas_dbus_error_invalid_args(message, NULL);
+
+	dbus_message_iter_next(&iter);
+	dbus_message_iter_get_basic(&iter, &config_method);
+
+	/*
+	 * Validation checks on config_method are being duplicated here
+	 * to be able to return invalid args reply since the error code
+	 * from p2p module are not granular enough (yet).
+	 */
+	if (os_strcmp(config_method, "display") &&
+	    os_strcmp(config_method, "keypad") &&
+	    os_strcmp(config_method, "pbc") &&
+	    os_strcmp(config_method, "pushbutton"))
+		return wpas_dbus_error_invalid_args(message, NULL);
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method,
+			       WPAS_P2P_PD_FOR_GO_NEG, NULL) < 0)
+		return wpas_dbus_error_unknown_error(message,
+				"Failed to send provision discovery request");
+
+	return NULL;
+}
+
+
+/*
+ * P2P Device property accessor methods.
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	DBusMessageIter variant_iter, dict_iter;
+	DBusMessageIter iter_secdev_dict_entry, iter_secdev_dict_val,
+		iter_secdev_dict_array;
+	const char *dev_name;
+	int num_vendor_extensions = 0;
+	int i;
+	const struct wpabuf *vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+
+	if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
+		return FALSE;
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      "a{sv}", &variant_iter) ||
+	    !wpa_dbus_dict_open_write(&variant_iter, &dict_iter))
+		goto err_no_mem;
+
+	/* DeviceName */
+	dev_name = wpa_s->conf->device_name;
+	if (dev_name &&
+	    !wpa_dbus_dict_append_string(&dict_iter, "DeviceName", dev_name))
+		goto err_no_mem;
+
+	/* Primary device type */
+	if (!wpa_dbus_dict_append_byte_array(&dict_iter, "PrimaryDeviceType",
+					     (char *) wpa_s->conf->device_type,
+					     WPS_DEV_TYPE_LEN))
+		goto err_no_mem;
+
+	/* Secondary device types */
+	if (wpa_s->conf->num_sec_device_types) {
+		if (!wpa_dbus_dict_begin_array(&dict_iter,
+					       "SecondaryDeviceTypes",
+					       DBUS_TYPE_ARRAY_AS_STRING
+					       DBUS_TYPE_BYTE_AS_STRING,
+					       &iter_secdev_dict_entry,
+					       &iter_secdev_dict_val,
+					       &iter_secdev_dict_array))
+			goto err_no_mem;
+
+		for (i = 0; i < wpa_s->conf->num_sec_device_types; i++)
+			wpa_dbus_dict_bin_array_add_element(
+				&iter_secdev_dict_array,
+				wpa_s->conf->sec_device_type[i],
+				WPS_DEV_TYPE_LEN);
+
+		if (!wpa_dbus_dict_end_array(&dict_iter,
+					     &iter_secdev_dict_entry,
+					     &iter_secdev_dict_val,
+					     &iter_secdev_dict_array))
+			goto err_no_mem;
+	}
+
+	/* Vendor Extensions */
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		if (wpa_s->conf->wps_vendor_ext[i] == NULL)
+			continue;
+		vendor_ext[num_vendor_extensions++] =
+			wpa_s->conf->wps_vendor_ext[i];
+	}
+
+	if ((num_vendor_extensions &&
+	     !wpa_dbus_dict_append_wpabuf_array(&dict_iter,
+						"VendorExtension",
+						vendor_ext,
+						num_vendor_extensions)) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "GOIntent",
+					 wpa_s->conf->p2p_go_intent) ||
+	    !wpa_dbus_dict_append_bool(&dict_iter, "PersistentReconnect",
+				       wpa_s->conf->persistent_reconnect) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "ListenRegClass",
+					 wpa_s->conf->p2p_listen_reg_class) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "ListenChannel",
+					 wpa_s->conf->p2p_listen_channel) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "OperRegClass",
+					 wpa_s->conf->p2p_oper_reg_class) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "OperChannel",
+					 wpa_s->conf->p2p_oper_channel) ||
+	    (wpa_s->conf->p2p_ssid_postfix &&
+	     !wpa_dbus_dict_append_string(&dict_iter, "SsidPostfix",
+					  wpa_s->conf->p2p_ssid_postfix)) ||
+	    !wpa_dbus_dict_append_bool(&dict_iter, "IntraBss",
+				       wpa_s->conf->p2p_intra_bss) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "GroupIdle",
+					 wpa_s->conf->p2p_group_idle) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "disassoc_low_ack",
+					 wpa_s->conf->disassoc_low_ack) ||
+	    !wpa_dbus_dict_append_bool(&dict_iter, "NoGroupIface",
+				       wpa_s->conf->p2p_no_group_iface) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "p2p_search_delay",
+					 wpa_s->conf->p2p_search_delay) ||
+	    !wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter))
+		goto err_no_mem;
+
+	return TRUE;
+
+err_no_mem:
+	dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+	return FALSE;
+}
+
+
+dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	DBusMessageIter variant_iter, iter_dict;
+	struct wpa_dbus_dict_entry entry = {.type = DBUS_TYPE_STRING };
+	unsigned int i;
+
+	if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
+		return FALSE;
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	dbus_message_iter_recurse(iter, &variant_iter);
+	if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error))
+		return FALSE;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
+			dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+					     "invalid message format");
+			return FALSE;
+		}
+
+		if (os_strcmp(entry.key, "DeviceName") == 0) {
+			char *devname;
+
+			if (entry.type != DBUS_TYPE_STRING)
+				goto error;
+
+			devname = os_strdup(entry.str_value);
+			if (devname == NULL)
+				goto err_no_mem_clear;
+
+			os_free(wpa_s->conf->device_name);
+			wpa_s->conf->device_name = devname;
+
+			wpa_s->conf->changed_parameters |=
+				CFG_CHANGED_DEVICE_NAME;
+		} else if (os_strcmp(entry.key, "PrimaryDeviceType") == 0) {
+			if (entry.type != DBUS_TYPE_ARRAY ||
+			    entry.array_type != DBUS_TYPE_BYTE ||
+			    entry.array_len != WPS_DEV_TYPE_LEN)
+				goto error;
+
+			os_memcpy(wpa_s->conf->device_type,
+				  entry.bytearray_value,
+				  WPS_DEV_TYPE_LEN);
+			wpa_s->conf->changed_parameters |=
+				CFG_CHANGED_DEVICE_TYPE;
+		} else if (os_strcmp(entry.key, "SecondaryDeviceTypes") == 0) {
+			if (entry.type != DBUS_TYPE_ARRAY ||
+			    entry.array_type != WPAS_DBUS_TYPE_BINARRAY ||
+			    entry.array_len > MAX_SEC_DEVICE_TYPES)
+				goto error;
+
+			for (i = 0; i < entry.array_len; i++)
+				if (wpabuf_len(entry.binarray_value[i]) !=
+				    WPS_DEV_TYPE_LEN)
+					goto err_no_mem_clear;
+			for (i = 0; i < entry.array_len; i++)
+				os_memcpy(wpa_s->conf->sec_device_type[i],
+					  wpabuf_head(entry.binarray_value[i]),
+					  WPS_DEV_TYPE_LEN);
+			wpa_s->conf->num_sec_device_types = entry.array_len;
+			wpa_s->conf->changed_parameters |=
+					CFG_CHANGED_SEC_DEVICE_TYPE;
+		} else if (os_strcmp(entry.key, "VendorExtension") == 0) {
+			if (entry.type != DBUS_TYPE_ARRAY ||
+			    entry.array_type != WPAS_DBUS_TYPE_BINARRAY ||
+			    (entry.array_len > P2P_MAX_WPS_VENDOR_EXT))
+				goto error;
+
+			wpa_s->conf->changed_parameters |=
+				CFG_CHANGED_VENDOR_EXTENSION;
+
+			for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+				wpabuf_free(wpa_s->conf->wps_vendor_ext[i]);
+				if (i < entry.array_len) {
+					wpa_s->conf->wps_vendor_ext[i] =
+						entry.binarray_value[i];
+					entry.binarray_value[i] = NULL;
+				} else
+					wpa_s->conf->wps_vendor_ext[i] = NULL;
+			}
+		} else if (os_strcmp(entry.key, "GOIntent") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32 &&
+			   (entry.uint32_value <= 15))
+			wpa_s->conf->p2p_go_intent = entry.uint32_value;
+		else if (os_strcmp(entry.key, "PersistentReconnect") == 0 &&
+			 entry.type == DBUS_TYPE_BOOLEAN)
+			wpa_s->conf->persistent_reconnect = entry.bool_value;
+		else if (os_strcmp(entry.key, "ListenRegClass") == 0 &&
+			 entry.type == DBUS_TYPE_UINT32) {
+			wpa_s->conf->p2p_listen_reg_class = entry.uint32_value;
+			wpa_s->conf->changed_parameters |=
+				CFG_CHANGED_P2P_LISTEN_CHANNEL;
+		} else if (os_strcmp(entry.key, "ListenChannel") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			wpa_s->conf->p2p_listen_channel = entry.uint32_value;
+			wpa_s->conf->changed_parameters |=
+				CFG_CHANGED_P2P_LISTEN_CHANNEL;
+		} else if (os_strcmp(entry.key, "OperRegClass") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			wpa_s->conf->p2p_oper_reg_class = entry.uint32_value;
+			wpa_s->conf->changed_parameters |=
+				CFG_CHANGED_P2P_OPER_CHANNEL;
+		} else if (os_strcmp(entry.key, "OperChannel") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			wpa_s->conf->p2p_oper_channel = entry.uint32_value;
+			wpa_s->conf->changed_parameters |=
+				CFG_CHANGED_P2P_OPER_CHANNEL;
+		} else if (os_strcmp(entry.key, "SsidPostfix") == 0) {
+			char *postfix;
+
+			if (entry.type != DBUS_TYPE_STRING)
+				goto error;
+
+			postfix = os_strdup(entry.str_value);
+			if (!postfix)
+				goto err_no_mem_clear;
+
+			os_free(wpa_s->conf->p2p_ssid_postfix);
+			wpa_s->conf->p2p_ssid_postfix = postfix;
+
+			wpa_s->conf->changed_parameters |=
+					CFG_CHANGED_P2P_SSID_POSTFIX;
+		} else if (os_strcmp(entry.key, "IntraBss") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			wpa_s->conf->p2p_intra_bss = entry.bool_value;
+			wpa_s->conf->changed_parameters |=
+				CFG_CHANGED_P2P_INTRA_BSS;
+		} else if (os_strcmp(entry.key, "GroupIdle") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32)
+			wpa_s->conf->p2p_group_idle = entry.uint32_value;
+		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;
+
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+
+	if (wpa_s->conf->changed_parameters) {
+		/* Some changed parameters requires to update config*/
+		wpa_supplicant_update_config(wpa_s);
+	}
+
+	return TRUE;
+
+ error:
+	dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+			     "invalid message format");
+	wpa_dbus_dict_entry_clear(&entry);
+	return FALSE;
+
+ err_no_mem_clear:
+	dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+	wpa_dbus_dict_entry_clear(&entry);
+	return FALSE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error,
+				       void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	struct p2p_data *p2p = wpa_s->global->p2p;
+	int next = 0, i = 0;
+	int num = 0, out_of_mem = 0;
+	const u8 *addr;
+	const struct p2p_peer_info *peer_info = NULL;
+	dbus_bool_t success = FALSE;
+
+	struct dl_list peer_objpath_list;
+	struct peer_objpath_node {
+		struct dl_list list;
+		char path[WPAS_DBUS_OBJECT_PATH_MAX];
+	} *node, *tmp;
+
+	char **peer_obj_paths = NULL;
+
+	if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error) ||
+	    !wpa_s->parent->parent->dbus_new_path)
+		return FALSE;
+
+	dl_list_init(&peer_objpath_list);
+
+	/* Get the first peer info */
+	peer_info = p2p_get_peer_found(p2p, NULL, next);
+
+	/* Get next and accumulate them */
+	next = 1;
+	while (peer_info != NULL) {
+		node = os_zalloc(sizeof(struct peer_objpath_node));
+		if (!node) {
+			out_of_mem = 1;
+			goto error;
+		}
+
+		addr = peer_info->p2p_device_addr;
+		os_snprintf(node->path, WPAS_DBUS_OBJECT_PATH_MAX,
+			    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART
+			    "/" COMPACT_MACSTR,
+			    wpa_s->parent->parent->dbus_new_path,
+			    MAC2STR(addr));
+		dl_list_add_tail(&peer_objpath_list, &node->list);
+		num++;
+
+		peer_info = p2p_get_peer_found(p2p, addr, next);
+	}
+
+	/*
+	 * Now construct the peer object paths in a form suitable for
+	 * array_property_getter helper below.
+	 */
+	peer_obj_paths = os_calloc(num, sizeof(char *));
+
+	if (!peer_obj_paths) {
+		out_of_mem = 1;
+		goto error;
+	}
+
+	dl_list_for_each_safe(node, tmp, &peer_objpath_list,
+			      struct peer_objpath_node, list)
+		peer_obj_paths[i++] = node->path;
+
+	success = wpas_dbus_simple_array_property_getter(iter,
+							 DBUS_TYPE_OBJECT_PATH,
+							 peer_obj_paths, num,
+							 error);
+
+error:
+	if (peer_obj_paths)
+		os_free(peer_obj_paths);
+
+	dl_list_for_each_safe(node, tmp, &peer_objpath_list,
+			      struct peer_objpath_node, list) {
+		dl_list_del(&node->list);
+		os_free(node);
+	}
+	if (out_of_mem)
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+
+	return success;
+}
+
+
+enum wpas_p2p_role {
+	WPAS_P2P_ROLE_DEVICE,
+	WPAS_P2P_ROLE_GO,
+	WPAS_P2P_ROLE_CLIENT,
+};
+
+static enum wpas_p2p_role wpas_get_p2p_role(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (!ssid)
+		return WPAS_P2P_ROLE_DEVICE;
+	if (wpa_s->wpa_state != WPA_COMPLETED)
+		return WPAS_P2P_ROLE_DEVICE;
+
+	switch (ssid->mode) {
+	case WPAS_MODE_P2P_GO:
+	case WPAS_MODE_P2P_GROUP_FORMATION:
+		return WPAS_P2P_ROLE_GO;
+	case WPAS_MODE_INFRA:
+		if (ssid->p2p_group)
+			return WPAS_P2P_ROLE_CLIENT;
+		return WPAS_P2P_ROLE_DEVICE;
+	default:
+		return WPAS_P2P_ROLE_DEVICE;
+	}
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error,
+				      void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	char *str;
+
+	switch (wpas_get_p2p_role(wpa_s)) {
+	case WPAS_P2P_ROLE_GO:
+		str = "GO";
+		break;
+	case WPAS_P2P_ROLE_CLIENT:
+		str = "client";
+		break;
+	default:
+		str = "device";
+		break;
+	}
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &str,
+						error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error,
+				       void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX];
+	char *dbus_groupobj_path = path_buf;
+
+	if (wpa_s->dbus_groupobj_path == NULL)
+		os_snprintf(dbus_groupobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+			    "/");
+	else
+		os_snprintf(dbus_groupobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+			    "%s", wpa_s->dbus_groupobj_path);
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
+						&dbus_groupobj_path, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter,
+					DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	char go_peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+	if (!wpa_s->parent->parent->dbus_new_path)
+		return FALSE;
+
+	if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_CLIENT)
+		os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
+	else
+		os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+			    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+			    COMPACT_MACSTR,
+			    wpa_s->parent->parent->dbus_new_path,
+			    MAC2STR(wpa_s->go_dev_addr));
+
+	path = go_peer_obj_path;
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
+						&path, error);
+}
+
+
+/*
+ * Peer object properties accessor methods
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter,
+						  DBusError *error,
+						  void *user_data)
+{
+	struct peer_handler_args *peer_args = user_data;
+	const struct p2p_peer_info *info;
+	char *tmp;
+
+	if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
+		return FALSE;
+
+	/* get the peer 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;
+	}
+
+	tmp = os_strdup(info->device_name);
+	if (!tmp) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
+					      error)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		os_free(tmp);
+		return FALSE;
+	}
+
+	os_free(tmp);
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter,
+						   DBusError *error,
+						   void *user_data)
+{
+	struct peer_handler_args *peer_args = user_data;
+	const struct p2p_peer_info *info;
+	char *tmp;
+
+	if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
+		return FALSE;
+
+	/* get the peer 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;
+	}
+
+	tmp = os_strdup(info->manufacturer);
+	if (!tmp) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
+					      error)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		os_free(tmp);
+		return FALSE;
+	}
+
+	os_free(tmp);
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter,
+						DBusError *error,
+						void *user_data)
+{
+	struct peer_handler_args *peer_args = user_data;
+	const struct p2p_peer_info *info;
+	char *tmp;
+
+	if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
+		return FALSE;
+
+	/* get the peer 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;
+	}
+
+	tmp = os_strdup(info->model_name);
+	if (!tmp) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
+					      error)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		os_free(tmp);
+		return FALSE;
+	}
+
+	os_free(tmp);
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter,
+						  DBusError *error,
+						  void *user_data)
+{
+	struct peer_handler_args *peer_args = user_data;
+	const struct p2p_peer_info *info;
+	char *tmp;
+
+	if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
+		return FALSE;
+
+	/* get the peer 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;
+	}
+
+	tmp = os_strdup(info->model_number);
+	if (!tmp) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
+					      error)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		os_free(tmp);
+		return FALSE;
+	}
+
+	os_free(tmp);
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter,
+						   DBusError *error,
+						   void *user_data)
+{
+	struct peer_handler_args *peer_args = user_data;
+	const struct p2p_peer_info *info;
+	char *tmp;
+
+	if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
+		return FALSE;
+
+	/* get the peer 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;
+	}
+
+	tmp = os_strdup(info->serial_number);
+	if (!tmp) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
+					      error)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		os_free(tmp);
+		return FALSE;
+	}
+
+	os_free(tmp);
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
+	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;
+	}
+
+	if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+						    (char *)
+						    info->pri_dev_type,
+						    WPS_DEV_TYPE_LEN, error)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(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;
+	}
+
+	if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
+					      &info->config_methods, error)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_level(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;
+	}
+
+	if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
+					      &info->level, error)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(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;
+	}
+
+	if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BYTE,
+					      &info->dev_capab, error)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(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;
+	}
+
+	if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BYTE,
+					      &info->group_capab, error)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct peer_handler_args *peer_args = user_data;
+	const struct p2p_peer_info *info;
+	DBusMessageIter variant_iter, array_iter;
+
+	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 (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      DBUS_TYPE_ARRAY_AS_STRING
+					      DBUS_TYPE_ARRAY_AS_STRING
+					      DBUS_TYPE_BYTE_AS_STRING,
+					      &variant_iter) ||
+	    !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+					      DBUS_TYPE_ARRAY_AS_STRING
+					      DBUS_TYPE_BYTE_AS_STRING,
+					      &array_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: failed to construct message 1", __func__);
+		return FALSE;
+	}
+
+	if (info->wps_sec_dev_type_list_len) {
+		const u8 *sec_dev_type_list = info->wps_sec_dev_type_list;
+		int num_sec_device_types =
+			info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN;
+		int i;
+		DBusMessageIter inner_array_iter;
+
+		for (i = 0; i < num_sec_device_types; i++) {
+			if (!dbus_message_iter_open_container(
+				    &array_iter, DBUS_TYPE_ARRAY,
+				    DBUS_TYPE_BYTE_AS_STRING,
+				    &inner_array_iter) ||
+			    !dbus_message_iter_append_fixed_array(
+				    &inner_array_iter, DBUS_TYPE_BYTE,
+				    &sec_dev_type_list, WPS_DEV_TYPE_LEN) ||
+			    !dbus_message_iter_close_container(
+				    &array_iter, &inner_array_iter)) {
+				dbus_set_error(error, DBUS_ERROR_FAILED,
+					       "%s: failed to construct message 2 (%d)",
+					       __func__, i);
+				return FALSE;
+			}
+
+			sec_dev_type_list += WPS_DEV_TYPE_LEN;
+		}
+	}
+
+	if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: failed to construct message 3", __func__);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
+						       DBusError *error,
+						       void *user_data)
+{
+	struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT];
+	unsigned int i, num = 0;
+	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;
+	}
+
+	/* Add WPS vendor extensions attribute */
+	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];
+		num++;
+	}
+
+	if (!wpas_dbus_simple_array_array_property_getter(iter, DBUS_TYPE_BYTE,
+							  vendor_extension,
+							  num, error))
+		return FALSE;
+
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_ies(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;
+	}
+
+	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, *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 = peer_args->wpa_s;
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	wpa_s_go = wpas_get_p2p_client_iface(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;
+}
+
+
+/**
+ * wpas_dbus_getter_persistent_groups - Get array of persistent group objects
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "PersistentGroups" property.
+ */
+dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	struct wpa_ssid *ssid;
+	char **paths;
+	unsigned int i = 0, num = 0;
+	dbus_bool_t success = FALSE;
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (!wpa_s->parent->dbus_new_path)
+		return FALSE;
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+		if (network_is_persistent_group(ssid))
+			num++;
+
+	paths = os_calloc(num, sizeof(char *));
+	if (!paths) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	/* Loop through configured networks and append object path of each */
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (!network_is_persistent_group(ssid))
+			continue;
+		paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+		if (paths[i] == NULL) {
+			dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+					     "no memory");
+			goto out;
+		}
+		/* Construct the object path for this network. */
+		os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX,
+			    "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d",
+			    wpa_s->parent->dbus_new_path, ssid->id);
+	}
+
+	success = wpas_dbus_simple_array_property_getter(iter,
+							 DBUS_TYPE_OBJECT_PATH,
+							 paths, num, error);
+
+out:
+	while (i)
+		os_free(paths[--i]);
+	os_free(paths);
+	return success;
+}
+
+
+/**
+ * wpas_dbus_getter_persistent_group_properties - Get options for a persistent
+ *	group
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Properties" property of a persistent group.
+ */
+dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter,
+							 DBusError *error,
+							 void *user_data)
+{
+	struct network_handler_args *net = user_data;
+
+	/* Leveraging the fact that persistent group object is still
+	 * represented in same manner as network within.
+	 */
+	return wpas_dbus_getter_network_properties(iter, error, net);
+}
+
+
+/**
+ * wpas_dbus_setter_persistent_group_properties - Set options for a persistent
+ *	group
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "Properties" property of a persistent group.
+ */
+dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter,
+							 DBusError *error,
+							 void *user_data)
+{
+	struct network_handler_args *net = user_data;
+	struct wpa_ssid *ssid = net->ssid;
+	DBusMessageIter	variant_iter;
+
+	/*
+	 * Leveraging the fact that persistent group object is still
+	 * represented in same manner as network within.
+	 */
+	dbus_message_iter_recurse(iter, &variant_iter);
+	return set_network_properties(net->wpa_s, ssid, &variant_iter, error);
+}
+
+
+/**
+ * wpas_dbus_new_iface_add_persistent_group - Add a new configured
+ *	persistent_group
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing the object path of the new
+ * persistent group
+ *
+ * Handler function for "AddPersistentGroup" method call of a P2P Device
+ * interface.
+ */
+DBusMessage * wpas_dbus_handler_add_persistent_group(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	DBusMessageIter	iter;
+	struct wpa_ssid *ssid = NULL;
+	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
+	DBusError error;
+
+	dbus_message_iter_init(message, &iter);
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (wpa_s->parent->dbus_new_path)
+		ssid = wpa_config_add_network(wpa_s->conf);
+	if (ssid == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: Cannot add new persistent group",
+			   __func__);
+		reply = wpas_dbus_error_unknown_error(
+			message,
+			"wpa_supplicant could not add a persistent group on this interface.");
+		goto err;
+	}
+
+	/* Mark the ssid as being a persistent group before the notification */
+	ssid->disabled = 2;
+	ssid->p2p_persistent_group = 1;
+	wpas_notify_persistent_group_added(wpa_s, ssid);
+
+	wpa_config_set_network_defaults(ssid);
+
+	dbus_error_init(&error);
+	if (!set_network_properties(wpa_s, ssid, &iter, &error)) {
+		wpa_printf(MSG_DEBUG,
+			   "dbus: %s: Control interface could not set persistent group properties",
+			   __func__);
+		reply = wpas_dbus_reply_new_from_error(
+			message, &error, DBUS_ERROR_INVALID_ARGS,
+			"Failed to set network properties");
+		dbus_error_free(&error);
+		goto err;
+	}
+
+	/* Construct the object path for this network. */
+	os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d",
+		    wpa_s->parent->dbus_new_path, ssid->id);
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL) {
+		reply = wpas_dbus_error_no_memory(message);
+		goto err;
+	}
+	if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+				      DBUS_TYPE_INVALID)) {
+		dbus_message_unref(reply);
+		reply = wpas_dbus_error_no_memory(message);
+		goto err;
+	}
+
+	return reply;
+
+err:
+	if (ssid) {
+		wpas_notify_persistent_group_removed(wpa_s, ssid);
+		wpa_config_remove_network(wpa_s->conf, ssid->id);
+	}
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_remove_persistent_group - Remove a configured persistent
+ *	group
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "RemovePersistentGroup" method call of a P2P Device
+ * interface.
+ */
+DBusMessage * wpas_dbus_handler_remove_persistent_group(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	const char *op;
+	char *iface = NULL, *persistent_group_id;
+	int id;
+	struct wpa_ssid *ssid;
+
+	dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op,
+			      DBUS_TYPE_INVALID);
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	/*
+	 * Extract the network ID and ensure the network is actually a child of
+	 * this interface.
+	 */
+	iface = wpas_dbus_new_decompose_object_path(
+		op, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART,
+		&persistent_group_id);
+	if (iface == NULL || persistent_group_id == NULL ||
+	    !wpa_s->parent->dbus_new_path ||
+	    os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) {
+		reply = wpas_dbus_error_invalid_args(message, op);
+		goto out;
+	}
+
+	id = strtoul(persistent_group_id, NULL, 10);
+	if (errno == EINVAL) {
+		reply = wpas_dbus_error_invalid_args(message, op);
+		goto out;
+	}
+
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid == NULL) {
+		reply = wpas_dbus_error_persistent_group_unknown(message);
+		goto out;
+	}
+
+	wpas_notify_persistent_group_removed(wpa_s, ssid);
+
+	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: error occurred when removing persistent group %d",
+			   __func__, id);
+		reply = wpas_dbus_error_unknown_error(
+			message,
+			"error removing the specified persistent group on this interface.");
+		goto out;
+	}
+
+out:
+	os_free(iface);
+	return reply;
+}
+
+
+static void remove_persistent_group(struct wpa_supplicant *wpa_s,
+				    struct wpa_ssid *ssid)
+{
+	wpas_notify_persistent_group_removed(wpa_s, ssid);
+
+	if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: error occurred when removing persistent group %d",
+			   __func__, ssid->id);
+		return;
+	}
+}
+
+
+/**
+ * wpas_dbus_handler_remove_all_persistent_groups - Remove all configured
+ * persistent groups
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "RemoveAllPersistentGroups" method call of a
+ * P2P Device interface.
+ */
+DBusMessage * wpas_dbus_handler_remove_all_persistent_groups(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *ssid, *next;
+	struct wpa_config *config;
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	config = wpa_s->conf;
+	ssid = config->ssid;
+	while (ssid) {
+		next = ssid->next;
+		if (network_is_persistent_group(ssid))
+			remove_persistent_group(wpa_s, ssid);
+		ssid = next;
+	}
+	return NULL;
+}
+
+
+/*
+ * Group object properties accessor methods
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	struct wpa_ssid *ssid;
+	unsigned int num_members;
+	char **paths;
+	unsigned int i;
+	void *next = NULL;
+	const u8 *addr;
+	dbus_bool_t success = FALSE;
+
+	if (!wpa_s->parent->parent->dbus_new_path)
+		return FALSE;
+
+	/* Verify correct role for this property */
+	if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_GO) {
+		return wpas_dbus_simple_array_property_getter(
+			iter, DBUS_TYPE_OBJECT_PATH, NULL, 0, error);
+	}
+
+	ssid = wpa_s->conf->ssid;
+	/* At present WPAS P2P_GO mode only applicable for p2p_go */
+	if (ssid->mode != WPAS_MODE_P2P_GO &&
+	    ssid->mode != WPAS_MODE_AP &&
+	    ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)
+		return FALSE;
+
+	num_members = p2p_get_group_num_members(wpa_s->p2p_group);
+
+	paths = os_calloc(num_members, sizeof(char *));
+	if (!paths)
+		goto out_of_memory;
+
+	i = 0;
+	while ((addr = p2p_iterate_group_members(wpa_s->p2p_group, &next))) {
+		paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+		if (!paths[i])
+			goto out_of_memory;
+		os_snprintf(paths[i], WPAS_DBUS_OBJECT_PATH_MAX,
+			    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART
+			    "/" COMPACT_MACSTR,
+			    wpa_s->parent->parent->dbus_new_path,
+			    MAC2STR(addr));
+		i++;
+	}
+
+	success = wpas_dbus_simple_array_property_getter(iter,
+							 DBUS_TYPE_OBJECT_PATH,
+							 paths, num_members,
+							 error);
+
+	for (i = 0; i < num_members; i++)
+		os_free(paths[i]);
+	os_free(paths);
+	return success;
+
+out_of_memory:
+	dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+	if (paths) {
+		for (i = 0; i < num_members; i++)
+			os_free(paths[i]);
+		os_free(paths);
+	}
+	return FALSE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
+					    DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+
+	if (wpa_s->current_ssid == NULL)
+		return FALSE;
+	return wpas_dbus_simple_array_property_getter(
+		iter, DBUS_TYPE_BYTE, wpa_s->current_ssid->ssid,
+		wpa_s->current_ssid->ssid_len, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	u8 role = wpas_get_p2p_role(wpa_s);
+	u8 *p_bssid;
+
+	if (role == WPAS_P2P_ROLE_CLIENT) {
+		if (wpa_s->current_ssid == NULL)
+			return FALSE;
+		p_bssid = wpa_s->current_ssid->bssid;
+	} else {
+		if (wpa_s->ap_iface == NULL)
+			return FALSE;
+		p_bssid = wpa_s->ap_iface->bss[0]->own_addr;
+	}
+
+	return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+						      p_bssid, ETH_ALEN,
+						      error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
+						 DBusError *error,
+						 void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	u16 op_freq;
+	u8 role = wpas_get_p2p_role(wpa_s);
+
+	if (role == WPAS_P2P_ROLE_CLIENT) {
+		if (wpa_s->go_params == NULL)
+			return FALSE;
+		op_freq = wpa_s->go_params->freq;
+	} else {
+		if (wpa_s->ap_iface == NULL)
+			return FALSE;
+		op_freq = wpa_s->ap_iface->freq;
+	}
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
+						&op_freq, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
+						  DBusError *error,
+						  void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	char *p_pass;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (ssid == NULL)
+		return FALSE;
+
+	p_pass = ssid->passphrase;
+	if (!p_pass)
+		p_pass = "";
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+						&p_pass, error);
+
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
+					   DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	u8 *p_psk = NULL;
+	u8 psk_len = 0;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (ssid == NULL)
+		return FALSE;
+
+	if (ssid->psk_set) {
+		p_psk = ssid->psk;
+		psk_len = sizeof(ssid->psk);
+	}
+
+	return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+						      p_psk, psk_len, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
+						  DBusError *error,
+						  void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	struct hostapd_data *hapd;
+	struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+	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) {
+		if (wpa_s->ap_iface == NULL)
+			return FALSE;
+		hapd = wpa_s->ap_iface->bss[0];
+
+		/* 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)
+				continue;
+			vendor_ext[num_vendor_ext++] =
+				hapd->conf->wps_vendor_ext[i];
+		}
+	}
+
+	/* Return vendor extensions or no data */
+	return wpas_dbus_simple_array_array_property_getter(iter,
+							    DBUS_TYPE_BYTE,
+							    vendor_ext,
+							    num_vendor_ext,
+							    error);
+}
+
+
+dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
+						  DBusError *error,
+						  void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	DBusMessageIter variant_iter, iter_dict, array_iter, sub;
+	struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
+	unsigned int i;
+	struct hostapd_data *hapd = NULL;
+
+	if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO &&
+	    wpa_s->ap_iface != NULL)
+		hapd = wpa_s->ap_iface->bss[0];
+	else
+		return FALSE;
+
+	dbus_message_iter_recurse(iter, &variant_iter);
+	if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY)
+		return FALSE;
+
+	/*
+	 * This is supposed to be array of bytearrays (aay), but the earlier
+	 * implementation used a dict with "WPSVendorExtensions" as the key in
+	 * this setter function which does not match the format used by the
+	 * getter function. For backwards compatibility, allow both formats to
+	 * be used in the setter.
+	 */
+	if (dbus_message_iter_get_element_type(&variant_iter) ==
+	    DBUS_TYPE_ARRAY) {
+		/* This is the proper format matching the getter */
+		struct wpabuf *vals[MAX_WPS_VENDOR_EXTENSIONS];
+
+		dbus_message_iter_recurse(&variant_iter, &array_iter);
+
+		if (dbus_message_iter_get_arg_type(&array_iter) !=
+		    DBUS_TYPE_ARRAY ||
+		    dbus_message_iter_get_element_type(&array_iter) !=
+		    DBUS_TYPE_BYTE) {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: Not an array of array of bytes");
+			return FALSE;
+		}
+
+		i = 0;
+		os_memset(vals, 0, sizeof(vals));
+
+		while (dbus_message_iter_get_arg_type(&array_iter) ==
+		       DBUS_TYPE_ARRAY) {
+			char *val;
+			int len;
+
+			if (i == MAX_WPS_VENDOR_EXTENSIONS) {
+				wpa_printf(MSG_DEBUG,
+					   "dbus: Too many WPSVendorExtensions values");
+				i = MAX_WPS_VENDOR_EXTENSIONS + 1;
+				break;
+			}
+
+			dbus_message_iter_recurse(&array_iter, &sub);
+			dbus_message_iter_get_fixed_array(&sub, &val, &len);
+			wpa_hexdump(MSG_DEBUG, "dbus: WPSVendorExtentions[]",
+				    val, len);
+			vals[i] = wpabuf_alloc_copy(val, len);
+			if (vals[i] == NULL) {
+				i = MAX_WPS_VENDOR_EXTENSIONS + 1;
+				break;
+			}
+			i++;
+			dbus_message_iter_next(&array_iter);
+		}
+
+		if (i > MAX_WPS_VENDOR_EXTENSIONS) {
+			for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+				wpabuf_free(vals[i]);
+			return FALSE;
+		}
+
+		for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+			wpabuf_free(hapd->conf->wps_vendor_ext[i]);
+			hapd->conf->wps_vendor_ext[i] = vals[i];
+		}
+
+		hostapd_update_wps(hapd);
+
+		return TRUE;
+	}
+
+	if (dbus_message_iter_get_element_type(&variant_iter) !=
+	    DBUS_TYPE_DICT_ENTRY)
+		return FALSE;
+
+	wpa_printf(MSG_DEBUG,
+		   "dbus: Try to use backwards compatibility version of WPSVendorExtensions setter");
+	if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error))
+		return FALSE;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
+			dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+					     "invalid message format");
+			return FALSE;
+		}
+
+		if (os_strcmp(entry.key, "WPSVendorExtensions") == 0) {
+			if (entry.type != DBUS_TYPE_ARRAY ||
+			    entry.array_type != WPAS_DBUS_TYPE_BINARRAY ||
+			    entry.array_len > MAX_WPS_VENDOR_EXTENSIONS)
+				goto error;
+
+			for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+				wpabuf_free(hapd->conf->wps_vendor_ext[i]);
+				if (i < entry.array_len) {
+					hapd->conf->wps_vendor_ext[i] =
+						entry.binarray_value[i];
+					entry.binarray_value[i] = NULL;
+				} else
+					hapd->conf->wps_vendor_ext[i] = NULL;
+			}
+
+			hostapd_update_wps(hapd);
+		} else
+			goto error;
+
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+
+	return TRUE;
+
+error:
+	wpa_dbus_dict_entry_clear(&entry);
+	dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+			     "invalid message format");
+	return FALSE;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message,
+						struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter_dict;
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter;
+	struct wpa_dbus_dict_entry entry;
+	int upnp = 0;
+	int bonjour = 0;
+	char *service = NULL;
+	struct wpabuf *query = NULL;
+	struct wpabuf *resp = NULL;
+	u8 version = 0;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto error;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto error;
+
+		if (os_strcmp(entry.key, "service_type") == 0 &&
+		    entry.type == DBUS_TYPE_STRING) {
+			if (os_strcmp(entry.str_value, "upnp") == 0)
+				upnp = 1;
+			else if (os_strcmp(entry.str_value, "bonjour") == 0)
+				bonjour = 1;
+			else
+				goto error_clear;
+		} else if (os_strcmp(entry.key, "version") == 0 &&
+			   entry.type == DBUS_TYPE_INT32) {
+			version = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "service") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			os_free(service);
+			service = os_strdup(entry.str_value);
+		} else if (os_strcmp(entry.key, "query") == 0) {
+			if (entry.type != DBUS_TYPE_ARRAY ||
+			    entry.array_type != DBUS_TYPE_BYTE)
+				goto error_clear;
+			query = wpabuf_alloc_copy(
+				entry.bytearray_value,
+				entry.array_len);
+		} else if (os_strcmp(entry.key, "response") == 0) {
+			if (entry.type != DBUS_TYPE_ARRAY ||
+			    entry.array_type != DBUS_TYPE_BYTE)
+				goto error_clear;
+			resp = wpabuf_alloc_copy(entry.bytearray_value,
+						 entry.array_len);
+		}
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+
+	if (upnp == 1) {
+		if (version <= 0 || service == NULL)
+			goto error;
+
+		if (wpas_p2p_service_add_upnp(wpa_s, version, service) != 0)
+			goto error;
+
+	} else if (bonjour == 1) {
+		if (query == NULL || resp == NULL)
+			goto error;
+
+		if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0)
+			goto error;
+		query = NULL;
+		resp = NULL;
+	} else
+		goto error;
+
+	os_free(service);
+	return reply;
+error_clear:
+	wpa_dbus_dict_entry_clear(&entry);
+error:
+	os_free(service);
+	wpabuf_free(query);
+	wpabuf_free(resp);
+	return wpas_dbus_error_invalid_args(message, NULL);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_delete_service(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter_dict;
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter;
+	struct wpa_dbus_dict_entry entry;
+	int upnp = 0;
+	int bonjour = 0;
+	int ret = 0;
+	char *service = NULL;
+	struct wpabuf *query = NULL;
+	u8 version = 0;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto error;
+
+	if (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto error;
+
+		if (os_strcmp(entry.key, "service_type") == 0 &&
+		    entry.type == DBUS_TYPE_STRING) {
+			if (os_strcmp(entry.str_value, "upnp") == 0)
+				upnp = 1;
+			else if (os_strcmp(entry.str_value, "bonjour") == 0)
+				bonjour = 1;
+			else
+				goto error_clear;
+			wpa_dbus_dict_entry_clear(&entry);
+		}
+	}
+	if (upnp == 1) {
+		while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+			if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+				goto error;
+			if (os_strcmp(entry.key, "version") == 0 &&
+			    entry.type == DBUS_TYPE_INT32)
+				version = entry.uint32_value;
+			else if (os_strcmp(entry.key, "service") == 0 &&
+				 entry.type == DBUS_TYPE_STRING) {
+				os_free(service);
+				service = os_strdup(entry.str_value);
+			} else
+				goto error_clear;
+
+			wpa_dbus_dict_entry_clear(&entry);
+		}
+
+		if (version <= 0 || service == NULL)
+			goto error;
+
+		ret = wpas_p2p_service_del_upnp(wpa_s, version, service);
+		if (ret != 0)
+			goto error;
+	} else if (bonjour == 1) {
+		while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+			if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+				goto error;
+
+			if (os_strcmp(entry.key, "query") == 0) {
+				if (entry.type != DBUS_TYPE_ARRAY ||
+				    entry.array_type != DBUS_TYPE_BYTE)
+					goto error_clear;
+				wpabuf_free(query);
+				query = wpabuf_alloc_copy(
+					entry.bytearray_value,
+					entry.array_len);
+			} else
+				goto error_clear;
+
+			wpa_dbus_dict_entry_clear(&entry);
+		}
+
+		if (query == NULL)
+			goto error;
+
+		ret = wpas_p2p_service_del_bonjour(wpa_s, query);
+		if (ret != 0)
+			goto error;
+	} else
+		goto error;
+
+	wpabuf_free(query);
+	os_free(service);
+	return reply;
+error_clear:
+	wpa_dbus_dict_entry_clear(&entry);
+error:
+	wpabuf_free(query);
+	os_free(service);
+	return wpas_dbus_error_invalid_args(message, NULL);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_flush_service(DBusMessage *message,
+						  struct wpa_supplicant *wpa_s)
+{
+	wpas_p2p_service_flush(wpa_s);
+	return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_service_sd_req(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter_dict;
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter;
+	struct wpa_dbus_dict_entry entry;
+	int upnp = 0;
+	char *service = NULL;
+	char *peer_object_path = NULL;
+	struct wpabuf *tlv = NULL;
+	u8 version = 0;
+	u64 ref = 0;
+	u8 addr_buf[ETH_ALEN], *addr;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto error;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto error;
+		if (os_strcmp(entry.key, "peer_object") == 0 &&
+		    entry.type == DBUS_TYPE_OBJECT_PATH) {
+			peer_object_path = os_strdup(entry.str_value);
+		} else if (os_strcmp(entry.key, "service_type") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			if (os_strcmp(entry.str_value, "upnp") == 0)
+				upnp = 1;
+			else
+				goto error_clear;
+		} else if (os_strcmp(entry.key, "version") == 0 &&
+			   entry.type == DBUS_TYPE_INT32) {
+			version = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "service") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			service = os_strdup(entry.str_value);
+		} else if (os_strcmp(entry.key, "tlv") == 0) {
+			if (entry.type != DBUS_TYPE_ARRAY ||
+			    entry.array_type != DBUS_TYPE_BYTE)
+				goto error_clear;
+			tlv = wpabuf_alloc_copy(entry.bytearray_value,
+						entry.array_len);
+		} else
+			goto error_clear;
+
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+
+	if (!peer_object_path) {
+		addr = NULL;
+	} else {
+		if (parse_peer_object_path(peer_object_path, addr_buf) < 0 ||
+		    !p2p_peer_known(wpa_s->global->p2p, addr_buf))
+			goto error;
+
+		addr = addr_buf;
+	}
+
+	if (upnp == 1) {
+		if (version <= 0 || service == NULL)
+			goto error;
+
+		ref = wpas_p2p_sd_request_upnp(wpa_s, addr, version, service);
+	} else {
+		if (tlv == NULL)
+			goto error;
+		ref = wpas_p2p_sd_request(wpa_s, addr, tlv);
+		wpabuf_free(tlv);
+	}
+
+	if (ref != 0) {
+		reply = dbus_message_new_method_return(message);
+		dbus_message_append_args(reply, DBUS_TYPE_UINT64,
+					 &ref, DBUS_TYPE_INVALID);
+	} else {
+		reply = wpas_dbus_error_unknown_error(
+			message, "Unable to send SD request");
+	}
+out:
+	os_free(service);
+	os_free(peer_object_path);
+	return reply;
+error_clear:
+	wpa_dbus_dict_entry_clear(&entry);
+error:
+	if (tlv)
+		wpabuf_free(tlv);
+	reply = wpas_dbus_error_invalid_args(message, NULL);
+	goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_service_sd_res(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter_dict;
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter;
+	struct wpa_dbus_dict_entry entry;
+	char *peer_object_path = NULL;
+	struct wpabuf *tlv = NULL;
+	int freq = 0;
+	int dlg_tok = 0;
+	u8 addr[ETH_ALEN];
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto error;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto error;
+
+		if (os_strcmp(entry.key, "peer_object") == 0 &&
+		    entry.type == DBUS_TYPE_OBJECT_PATH) {
+			peer_object_path = os_strdup(entry.str_value);
+		} else if (os_strcmp(entry.key, "frequency") == 0 &&
+			   entry.type == DBUS_TYPE_INT32) {
+			freq = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "dialog_token") == 0 &&
+			   (entry.type == DBUS_TYPE_UINT32 ||
+			    entry.type == DBUS_TYPE_INT32)) {
+			dlg_tok = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "tlvs") == 0) {
+			if (entry.type != DBUS_TYPE_ARRAY ||
+			    entry.array_type != DBUS_TYPE_BYTE)
+				goto error_clear;
+			tlv = wpabuf_alloc_copy(entry.bytearray_value,
+						entry.array_len);
+		} else
+			goto error_clear;
+
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+	if (parse_peer_object_path(peer_object_path, addr) < 0 ||
+	    !p2p_peer_known(wpa_s->global->p2p, addr) ||
+	    tlv == NULL)
+		goto error;
+
+	wpas_p2p_sd_response(wpa_s, freq, addr, (u8) dlg_tok, tlv);
+	wpabuf_free(tlv);
+out:
+	os_free(peer_object_path);
+	return reply;
+error_clear:
+	wpa_dbus_dict_entry_clear(&entry);
+error:
+	reply = wpas_dbus_error_invalid_args(message, NULL);
+	goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_service_sd_cancel_req(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter;
+	u64 req = 0;
+
+	dbus_message_iter_init(message, &iter);
+	dbus_message_iter_get_basic(&iter, &req);
+
+	if (req == 0)
+		goto error;
+
+	if (wpas_p2p_sd_cancel_request(wpa_s, req) < 0)
+		goto error;
+
+	return NULL;
+error:
+	return wpas_dbus_error_invalid_args(message, NULL);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_service_update(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	wpas_p2p_sd_service_update(wpa_s);
+	return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_serv_disc_external(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter;
+	int ext = 0;
+
+	dbus_message_iter_init(message, &iter);
+	dbus_message_iter_get_basic(&iter, &ext);
+
+	wpa_s->p2p_sd_over_ctrl_iface = ext;
+
+	return NULL;
+
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter,
+					    DBusError *error, void *user_data)
+{
+	struct wpa_global *global = user_data;
+	struct wpabuf *ie;
+	dbus_bool_t ret;
+
+	ie = wifi_display_get_wfd_ie(global);
+	if (ie == NULL)
+		return wpas_dbus_simple_array_property_getter(iter,
+							      DBUS_TYPE_BYTE,
+							      NULL, 0, error);
+
+	ret = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+						     wpabuf_head(ie),
+						     wpabuf_len(ie), error);
+	wpabuf_free(ie);
+
+	return ret;
+}
+
+
+dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter,
+					    DBusError *error, void *user_data)
+{
+	struct wpa_global *global = user_data;
+	DBusMessageIter variant, array;
+	struct wpabuf *ie = NULL;
+	const u8 *data;
+	int len;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
+		goto err;
+
+	dbus_message_iter_recurse(iter, &variant);
+	if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY)
+		goto err;
+
+	dbus_message_iter_recurse(&variant, &array);
+	dbus_message_iter_get_fixed_array(&array, &data, &len);
+	if (len == 0) {
+		wifi_display_enable(global, 0);
+		wifi_display_deinit(global);
+
+		return TRUE;
+	}
+
+	ie = wpabuf_alloc(len);
+	if (ie == NULL)
+		goto err;
+
+	wpabuf_put_data(ie, data, len);
+	if (wifi_display_subelem_set_from_ies(global, ie) != 0)
+		goto err;
+
+	if (global->wifi_display == 0)
+		wifi_display_enable(global, 1);
+
+	wpabuf_free(ie);
+
+	return TRUE;
+err:
+	wpabuf_free(ie);
+
+	dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+			     "invalid message format");
+	return FALSE;
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
diff --git a/hostap/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/hostap/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
new file mode 100644
index 0000000..2aecbbe
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
@@ -0,0 +1,247 @@
+/*
+ * WPA Supplicant / dbus-based control interface for p2p
+ * Copyright (c) 2011-2012, Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DBUS_NEW_HANDLERS_P2P_H
+#define DBUS_NEW_HANDLERS_P2P_H
+
+struct peer_handler_args {
+	struct wpa_supplicant *wpa_s;
+	u8 p2p_device_addr[ETH_ALEN];
+};
+
+/*
+ * P2P Device methods
+ */
+
+DBusMessage *wpas_dbus_handler_p2p_find(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_stop_find(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_rejectpeer(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_listen(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_extendedlisten(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_presence_request(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_prov_disc_req(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_group_add(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_connect(
+		DBusMessage *message,
+		struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_p2p_cancel(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_invite(
+		DBusMessage *message,
+		struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_disconnect(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_p2p_remove_client(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_flush(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_add_service(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_delete_service(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_flush_service(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_service_sd_req(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_service_sd_res(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_service_sd_cancel_req(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_service_update(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_serv_disc_external(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+/*
+ * P2P Device property accessor methods.
+ */
+dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error,
+				       void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error,
+				      void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error,
+				       void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter,
+					DBusError *error,
+					void *user_data);
+
+/*
+ * P2P Peer properties.
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter,
+						  DBusError *error,
+						  void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter,
+						   DBusError *error,
+						   void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter,
+						DBusError *error,
+						void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter,
+						  DBusError *error,
+						  void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter,
+						   DBusError *error,
+						   void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
+	DBusMessageIter *iter, DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter,
+						    DBusError *error,
+						    void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
+					    DBusError *error,
+					    void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter,
+							DBusError *error,
+							void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter,
+						       DBusError *error,
+						       void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
+	DBusMessageIter *iter, DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
+						       DBusError *error,
+						       void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
+					  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
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
+					    DBusError *error,
+					    void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
+						 DBusError *error,
+						 void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
+						  DBusError *error,
+						  void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
+					   DBusError *error,
+					   void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
+						  DBusError *error,
+						  void *user_data);
+
+dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
+						  DBusError *error,
+						  void *user_data);
+
+/*
+ * P2P Persistent Groups and properties
+ */
+
+dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data);
+
+dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter,
+	DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter,
+							 DBusError *error,
+							 void *user_data);
+
+DBusMessage * wpas_dbus_handler_add_persistent_group(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_remove_persistent_group(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_remove_all_persistent_groups(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter,
+					    DBusError *error,
+					    void *user_data);
+
+dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter,
+					    DBusError *error,
+					    void *user_data);
+
+#endif /* CONFIG_WIFI_DISPLAY */
+
+#endif /* DBUS_NEW_HANDLERS_P2P_H */
diff --git a/hostap/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/hostap/wpa_supplicant/dbus/dbus_new_handlers_wps.c
new file mode 100644
index 0000000..78ebc2f
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_new_handlers_wps.c
@@ -0,0 +1,456 @@
+/*
+ * WPA Supplicant / dbus-based control interface (WPS)
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "../config.h"
+#include "../wpa_supplicant_i.h"
+#include "../wps_supplicant.h"
+#include "../driver_i.h"
+#include "../ap.h"
+#include "dbus_new_helpers.h"
+#include "dbus_new.h"
+#include "dbus_new_handlers.h"
+#include "dbus_dict_helpers.h"
+
+
+struct wps_start_params {
+	int role; /* 0 - not set, 1 - enrollee, 2 - registrar */
+	int type; /* 0 - not set, 1 - pin,      2 - pbc       */
+	u8 *bssid;
+	char *pin;
+	u8 *p2p_dev_addr;
+};
+
+
+static int wpas_dbus_handler_wps_role(DBusMessage *message,
+				      DBusMessageIter *entry_iter,
+				      struct wps_start_params *params,
+				      DBusMessage **reply)
+{
+	DBusMessageIter variant_iter;
+	char *val;
+
+	dbus_message_iter_recurse(entry_iter, &variant_iter);
+	if (dbus_message_iter_get_arg_type(&variant_iter) !=
+	    DBUS_TYPE_STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "dbus: WPS.Start - Wrong Role type, string required");
+		*reply = wpas_dbus_error_invalid_args(message,
+						      "Role must be a string");
+		return -1;
+	}
+	dbus_message_iter_get_basic(&variant_iter, &val);
+	if (os_strcmp(val, "enrollee") == 0)
+		params->role = 1;
+	else if (os_strcmp(val, "registrar") == 0)
+		params->role = 2;
+	else {
+		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Unknown role %s", val);
+		*reply = wpas_dbus_error_invalid_args(message, val);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wpas_dbus_handler_wps_type(DBusMessage *message,
+				      DBusMessageIter *entry_iter,
+				      struct wps_start_params *params,
+				      DBusMessage **reply)
+{
+	DBusMessageIter variant_iter;
+	char *val;
+
+	dbus_message_iter_recurse(entry_iter, &variant_iter);
+	if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "dbus: WPS.Start - Wrong Type type, string required");
+		*reply = wpas_dbus_error_invalid_args(message,
+						      "Type must be a string");
+		return -1;
+	}
+	dbus_message_iter_get_basic(&variant_iter, &val);
+	if (os_strcmp(val, "pin") == 0)
+		params->type = 1;
+	else if (os_strcmp(val, "pbc") == 0)
+		params->type = 2;
+	else {
+		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Unknown type %s",
+			   val);
+		*reply = wpas_dbus_error_invalid_args(message, val);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wpas_dbus_handler_wps_bssid(DBusMessage *message,
+				       DBusMessageIter *entry_iter,
+				       struct wps_start_params *params,
+				       DBusMessage **reply)
+{
+	DBusMessageIter variant_iter, array_iter;
+	int len;
+
+	dbus_message_iter_recurse(entry_iter, &variant_iter);
+	if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY ||
+	    dbus_message_iter_get_element_type(&variant_iter) !=
+	    DBUS_TYPE_BYTE) {
+		wpa_printf(MSG_DEBUG,
+			   "dbus: WPS.Start - Wrong Bssid type, byte array required");
+		*reply = wpas_dbus_error_invalid_args(
+			message, "Bssid must be a byte array");
+		return -1;
+	}
+	dbus_message_iter_recurse(&variant_iter, &array_iter);
+	dbus_message_iter_get_fixed_array(&array_iter, &params->bssid, &len);
+	if (len != ETH_ALEN) {
+		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Bssid length %d",
+			   len);
+		*reply = wpas_dbus_error_invalid_args(message,
+						      "Bssid is wrong length");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wpas_dbus_handler_wps_pin(DBusMessage *message,
+				     DBusMessageIter *entry_iter,
+				     struct wps_start_params *params,
+				     DBusMessage **reply)
+{
+	DBusMessageIter variant_iter;
+
+	dbus_message_iter_recurse(entry_iter, &variant_iter);
+	if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "dbus: WPS.Start - Wrong Pin type, string required");
+		*reply = wpas_dbus_error_invalid_args(message,
+						      "Pin must be a string");
+		return -1;
+	}
+	dbus_message_iter_get_basic(&variant_iter, &params->pin);
+	return 0;
+}
+
+
+#ifdef CONFIG_P2P
+static int wpas_dbus_handler_wps_p2p_dev_addr(DBusMessage *message,
+					      DBusMessageIter *entry_iter,
+					      struct wps_start_params *params,
+					      DBusMessage **reply)
+{
+	DBusMessageIter variant_iter, array_iter;
+	int len;
+
+	dbus_message_iter_recurse(entry_iter, &variant_iter);
+	if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY ||
+	    dbus_message_iter_get_element_type(&variant_iter) !=
+	    DBUS_TYPE_BYTE) {
+		wpa_printf(MSG_DEBUG,
+			   "dbus: WPS.Start - Wrong P2PDeviceAddress type, byte array required");
+		*reply = wpas_dbus_error_invalid_args(
+			message, "P2PDeviceAddress must be a byte array");
+		return -1;
+	}
+	dbus_message_iter_recurse(&variant_iter, &array_iter);
+	dbus_message_iter_get_fixed_array(&array_iter, &params->p2p_dev_addr,
+					  &len);
+	if (len != ETH_ALEN) {
+		wpa_printf(MSG_DEBUG,
+			   "dbus: WPS.Start - Wrong P2PDeviceAddress length %d",
+			   len);
+		*reply = wpas_dbus_error_invalid_args(
+			message, "P2PDeviceAddress has wrong length");
+		return -1;
+	}
+	return 0;
+}
+#endif /* CONFIG_P2P */
+
+
+static int wpas_dbus_handler_wps_start_entry(DBusMessage *message, char *key,
+					     DBusMessageIter *entry_iter,
+					     struct wps_start_params *params,
+					     DBusMessage **reply)
+{
+	if (os_strcmp(key, "Role") == 0)
+		return wpas_dbus_handler_wps_role(message, entry_iter,
+						  params, reply);
+	else if (os_strcmp(key, "Type") == 0)
+		return wpas_dbus_handler_wps_type(message, entry_iter,
+						  params, reply);
+	else if (os_strcmp(key, "Bssid") == 0)
+		return wpas_dbus_handler_wps_bssid(message, entry_iter,
+						   params, reply);
+	else if (os_strcmp(key, "Pin") == 0)
+		return wpas_dbus_handler_wps_pin(message, entry_iter,
+						 params, reply);
+#ifdef CONFIG_P2P
+	else if (os_strcmp(key, "P2PDeviceAddress") == 0)
+		return wpas_dbus_handler_wps_p2p_dev_addr(message, entry_iter,
+							  params, reply);
+#endif /* CONFIG_P2P */
+
+	wpa_printf(MSG_DEBUG, "dbus: WPS.Start - unknown key %s", key);
+	*reply = wpas_dbus_error_invalid_args(message, key);
+	return -1;
+}
+
+
+/**
+ * wpas_dbus_handler_wps_start - Start WPS configuration
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: DBus message dictionary on success or DBus error on failure
+ *
+ * Handler for "Start" method call. DBus dictionary argument contains
+ * information about role (enrollee or registrar), authorization method
+ * (pin or push button) and optionally pin and bssid. Returned message
+ * has a dictionary argument which may contain newly generated pin (optional).
+ */
+DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
+					  struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter, dict_iter, entry_iter;
+	struct wps_start_params params;
+	char *key;
+	char npin[9] = { '\0' };
+	int ret;
+
+	os_memset(&params, 0, sizeof(params));
+	dbus_message_iter_init(message, &iter);
+
+	dbus_message_iter_recurse(&iter, &dict_iter);
+	while (dbus_message_iter_get_arg_type(&dict_iter) ==
+	       DBUS_TYPE_DICT_ENTRY) {
+		dbus_message_iter_recurse(&dict_iter, &entry_iter);
+
+		dbus_message_iter_get_basic(&entry_iter, &key);
+		dbus_message_iter_next(&entry_iter);
+
+		if (wpas_dbus_handler_wps_start_entry(message, key,
+						      &entry_iter,
+						      &params, &reply))
+			return reply;
+
+		dbus_message_iter_next(&dict_iter);
+	}
+
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface && params.type == 1) {
+		if (params.pin == NULL) {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: WPS.Start - Pin required for registrar role");
+			return wpas_dbus_error_invalid_args(
+				message, "Pin required for registrar role.");
+		}
+		ret = wpa_supplicant_ap_wps_pin(wpa_s,
+						params.bssid,
+						params.pin,
+						npin, sizeof(npin), 0);
+	} else if (wpa_s->ap_iface) {
+		ret = wpa_supplicant_ap_wps_pbc(wpa_s,
+						params.bssid,
+						params.p2p_dev_addr);
+	} else
+#endif /* CONFIG_AP */
+	if (params.role == 0) {
+		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Role not specified");
+		return wpas_dbus_error_invalid_args(message,
+						    "Role not specified");
+	} else if (params.role == 2) {
+		if (params.pin == NULL) {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: WPS.Start - Pin required for registrar role");
+			return wpas_dbus_error_invalid_args(
+				message, "Pin required for registrar role.");
+		}
+		ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin,
+					 NULL);
+	} else if (params.type == 0) {
+		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Type not specified");
+		return wpas_dbus_error_invalid_args(message,
+						    "Type not specified");
+	} else if (params.type == 1) {
+		ret = wpas_wps_start_pin(wpa_s, params.bssid,
+					 params.pin, 0,
+					 DEV_PW_DEFAULT);
+		if (ret > 0)
+			os_snprintf(npin, sizeof(npin), "%08d", ret);
+	} else {
+		ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0);
+	}
+
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "dbus: WPS.Start wpas_wps_failed in role %s and key %s",
+			   (params.role == 1 ? "enrollee" : "registrar"),
+			   (params.type == 0 ? "" :
+			    (params.type == 1 ? "pin" : "pbc")));
+		return wpas_dbus_error_unknown_error(message,
+						     "WPS start failed");
+	}
+
+	reply = dbus_message_new_method_return(message);
+	if (!reply)
+		return wpas_dbus_error_no_memory(message);
+
+	dbus_message_iter_init_append(reply, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    (os_strlen(npin) > 0 &&
+	     !wpa_dbus_dict_append_string(&dict_iter, "Pin", npin)) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter)) {
+		dbus_message_unref(reply);
+		return wpas_dbus_error_no_memory(message);
+	}
+
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_wps_cancel - Cancel ongoing WPS configuration
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: NULL on success or DBus error on failure
+ *
+ * Handler for "Cancel" method call. Returns NULL if WPS cancel successfull
+ * or DBus error on WPS cancel failure
+ */
+DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	if (wpas_wps_cancel(wpa_s))
+		return wpas_dbus_error_unknown_error(message,
+						     "WPS cancel failed");
+
+	return NULL;
+}
+
+
+/**
+ * wpas_dbus_getter_process_credentials - Check if credentials are processed
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "ProcessCredentials" property. Returns returned boolean will be
+ * true if wps_cred_processing configuration field is not equal to 1 or false
+ * if otherwise.
+ */
+dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
+						 DBusError *error,
+						 void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_bool_t process = wpa_s->conf->wps_cred_processing != 1;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+						&process, error);
+}
+
+
+/**
+ * wpas_dbus_setter_process_credentials - Set credentials_processed conf param
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "ProcessCredentials" property. Sets credentials_processed on 2
+ * if boolean argument is true or on 1 if otherwise.
+ */
+dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
+						 DBusError *error,
+						 void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_bool_t process_credentials, old_pc;
+
+	if (!wpa_s->dbus_new_path)
+		return FALSE;
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+					      &process_credentials))
+		return FALSE;
+
+	old_pc = wpa_s->conf->wps_cred_processing != 1;
+	wpa_s->conf->wps_cred_processing = (process_credentials ? 2 : 1);
+
+	if ((wpa_s->conf->wps_cred_processing != 1) != old_pc)
+		wpa_dbus_queue_property_changed(wpa_s->global->dbus,
+					        wpa_s->dbus_new_path,
+					        WPAS_DBUS_NEW_IFACE_WPS,
+					        "ProcessCredentials");
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_config_methods - Get current WPS configuration methods
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "ConfigMethods" property. Returned boolean will be true if
+ * providing the relevant string worked, or false otherwise.
+ */
+dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter,
+					    DBusError *error,
+					    void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	char *methods = wpa_s->conf->config_methods;
+
+	if (methods == NULL)
+		methods = "";
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+						&methods, error);
+}
+
+
+/**
+ * wpas_dbus_setter_config_methods - Set WPS configuration methods
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "ConfigMethods" property. Sets the methods string, apply such
+ * change and returns true on success. Returns false otherwise.
+ */
+dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter,
+					    DBusError *error,
+					    void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	char *methods, *new_methods;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+					      &methods))
+		return FALSE;
+
+	new_methods = os_strdup(methods);
+	if (!new_methods)
+		return FALSE;
+
+	os_free(wpa_s->conf->config_methods);
+	wpa_s->conf->config_methods = new_methods;
+
+	wpa_s->conf->changed_parameters |= CFG_CHANGED_CONFIG_METHODS;
+	wpa_supplicant_update_config(wpa_s);
+
+	return TRUE;
+}
diff --git a/hostap/wpa_supplicant/dbus/dbus_new_helpers.c b/hostap/wpa_supplicant/dbus/dbus_new_helpers.c
new file mode 100644
index 0000000..d8d2234
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -0,0 +1,1053 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ *
+ * 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 "dbus_common.h"
+#include "dbus_common_i.h"
+#include "dbus_new.h"
+#include "dbus_new_helpers.h"
+#include "dbus_new_handlers.h"
+#include "dbus_dict_helpers.h"
+
+
+static dbus_bool_t fill_dict_with_properties(
+	DBusMessageIter *dict_iter,
+	const struct wpa_dbus_property_desc *props,
+	const char *interface, void *user_data, DBusError *error)
+{
+	DBusMessageIter entry_iter;
+	const struct wpa_dbus_property_desc *dsc;
+
+	for (dsc = props; dsc && dsc->dbus_property; dsc++) {
+		/* Only return properties for the requested D-Bus interface */
+		if (os_strncmp(dsc->dbus_interface, interface,
+			       WPAS_DBUS_INTERFACE_MAX) != 0)
+			continue;
+
+		/* Skip write-only properties */
+		if (dsc->getter == NULL)
+			continue;
+
+		if (!dbus_message_iter_open_container(dict_iter,
+						      DBUS_TYPE_DICT_ENTRY,
+						      NULL, &entry_iter) ||
+		    !dbus_message_iter_append_basic(&entry_iter,
+						    DBUS_TYPE_STRING,
+						    &dsc->dbus_property))
+			goto error;
+
+		/* An error getting a property fails the request entirely */
+		if (!dsc->getter(&entry_iter, error, user_data)) {
+			wpa_printf(MSG_INFO,
+				   "dbus: %s dbus_interface=%s dbus_property=%s getter failed",
+				   __func__, dsc->dbus_interface,
+				   dsc->dbus_property);
+			return FALSE;
+		}
+
+		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;
+}
+
+
+/**
+ * get_all_properties - Responds for GetAll properties calls on object
+ * @message: Message with GetAll call
+ * @interface: interface name which properties will be returned
+ * @property_dsc: list of object's properties
+ * Returns: Message with dict of variants as argument with properties values
+ *
+ * Iterates over all properties registered with object and execute getters
+ * of those, which are readable and which interface matches interface
+ * specified as argument. Returned message contains one dict argument
+ * with properties names as keys and theirs values as values.
+ */
+static DBusMessage * get_all_properties(DBusMessage *message, char *interface,
+					struct wpa_dbus_object_desc *obj_dsc)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter, dict_iter;
+	DBusError error;
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL)
+		return wpas_dbus_error_no_memory(message);
+
+	dbus_message_iter_init_append(reply, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) {
+		dbus_message_unref(reply);
+		return wpas_dbus_error_no_memory(message);
+	}
+
+	dbus_error_init(&error);
+	if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties,
+				       interface, obj_dsc->user_data, &error)) {
+		dbus_message_unref(reply);
+		reply = wpas_dbus_reply_new_from_error(
+			message, &error, DBUS_ERROR_INVALID_ARGS,
+			"No readable properties in this interface");
+		dbus_error_free(&error);
+		return reply;
+	}
+
+	if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) {
+		dbus_message_unref(reply);
+		return wpas_dbus_error_no_memory(message);
+	}
+
+	return reply;
+}
+
+
+static int is_signature_correct(DBusMessage *message,
+				const struct wpa_dbus_method_desc *method_dsc)
+{
+	/* According to DBus documentation max length of signature is 255 */
+#define MAX_SIG_LEN 256
+	char registered_sig[MAX_SIG_LEN], *pos;
+	const char *sig = dbus_message_get_signature(message);
+	int ret;
+	const struct wpa_dbus_argument *arg;
+
+	pos = registered_sig;
+	*pos = '\0';
+
+	for (arg = method_dsc->args; arg && arg->name; arg++) {
+		if (arg->dir == ARG_IN) {
+			size_t blen = registered_sig + MAX_SIG_LEN - pos;
+
+			ret = os_snprintf(pos, blen, "%s", arg->type);
+			if (os_snprintf_error(blen, ret))
+				return 0;
+			pos += ret;
+		}
+	}
+
+	return !os_strncmp(registered_sig, sig, MAX_SIG_LEN);
+}
+
+
+static DBusMessage * properties_get_all(DBusMessage *message, char *interface,
+					struct wpa_dbus_object_desc *obj_dsc)
+{
+	if (os_strcmp(dbus_message_get_signature(message), "s") != 0)
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      NULL);
+
+	return get_all_properties(message, interface, obj_dsc);
+}
+
+
+static DBusMessage * properties_get(DBusMessage *message,
+				    const struct wpa_dbus_property_desc *dsc,
+				    void *user_data)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusError error;
+
+	if (os_strcmp(dbus_message_get_signature(message), "ss")) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      NULL);
+	}
+
+	if (dsc->getter == NULL) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Property is write-only");
+	}
+
+	reply = dbus_message_new_method_return(message);
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_error_init(&error);
+	if (dsc->getter(&iter, &error, user_data) == FALSE) {
+		dbus_message_unref(reply);
+		reply = wpas_dbus_reply_new_from_error(
+			message, &error, DBUS_ERROR_FAILED,
+			"Failed to read property");
+		dbus_error_free(&error);
+	}
+
+	return reply;
+}
+
+
+static DBusMessage * properties_set(DBusMessage *message,
+				    const struct wpa_dbus_property_desc *dsc,
+				    void *user_data)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusError error;
+
+	if (os_strcmp(dbus_message_get_signature(message), "ssv")) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      NULL);
+	}
+
+	if (dsc->setter == NULL) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Property is read-only");
+	}
+
+	dbus_message_iter_init(message, &iter);
+	/* Skip the interface name and the property name */
+	dbus_message_iter_next(&iter);
+	dbus_message_iter_next(&iter);
+
+	/* Iter will now point to the property's new value */
+	dbus_error_init(&error);
+	if (dsc->setter(&iter, &error, user_data) == TRUE) {
+		/* Success */
+		reply = dbus_message_new_method_return(message);
+	} else {
+		reply = wpas_dbus_reply_new_from_error(
+			message, &error, DBUS_ERROR_FAILED,
+			"Failed to set property");
+		dbus_error_free(&error);
+	}
+
+	return reply;
+}
+
+
+static DBusMessage *
+properties_get_or_set(DBusMessage *message, DBusMessageIter *iter,
+		      char *interface,
+		      struct wpa_dbus_object_desc *obj_dsc)
+{
+	const struct wpa_dbus_property_desc *property_dsc;
+	char *property;
+	const char *method;
+
+	method = dbus_message_get_member(message);
+	property_dsc = obj_dsc->properties;
+
+	/* Second argument: property name (DBUS_TYPE_STRING) */
+	if (!dbus_message_iter_next(iter) ||
+	    dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      NULL);
+	}
+	dbus_message_iter_get_basic(iter, &property);
+
+	while (property_dsc && property_dsc->dbus_property) {
+		/* compare property names and
+		 * interfaces */
+		if (!os_strncmp(property_dsc->dbus_property, property,
+				WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
+		    !os_strncmp(property_dsc->dbus_interface, interface,
+				WPAS_DBUS_INTERFACE_MAX))
+			break;
+
+		property_dsc++;
+	}
+	if (property_dsc == NULL || property_dsc->dbus_property == NULL) {
+		wpa_printf(MSG_DEBUG, "no property handler for %s.%s on %s",
+			   interface, property,
+			   dbus_message_get_path(message));
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "No such property");
+	}
+
+	if (os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
+		       WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0) {
+		wpa_printf(MSG_MSGDUMP, "%s: Get(%s)", __func__, property);
+		return properties_get(message, property_dsc,
+				      obj_dsc->user_data);
+	}
+
+	wpa_printf(MSG_MSGDUMP, "%s: Set(%s)", __func__, property);
+	return properties_set(message, property_dsc, obj_dsc->user_data);
+}
+
+
+static DBusMessage * properties_handler(DBusMessage *message,
+					struct wpa_dbus_object_desc *obj_dsc)
+{
+	DBusMessageIter iter;
+	char *interface;
+	const char *method;
+
+	method = dbus_message_get_member(message);
+	dbus_message_iter_init(message, &iter);
+
+	if (!os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
+			WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) ||
+	    !os_strncmp(WPA_DBUS_PROPERTIES_SET, method,
+			WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) ||
+	    !os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
+			WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
+		/* First argument: interface name (DBUS_TYPE_STRING) */
+		if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+			return dbus_message_new_error(message,
+						      DBUS_ERROR_INVALID_ARGS,
+						      NULL);
+		}
+
+		dbus_message_iter_get_basic(&iter, &interface);
+
+		if (!os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
+				WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
+			/* GetAll */
+			return properties_get_all(message, interface, obj_dsc);
+		}
+		/* Get or Set */
+		return properties_get_or_set(message, &iter, interface,
+					     obj_dsc);
+	}
+	return dbus_message_new_error(message, DBUS_ERROR_UNKNOWN_METHOD,
+				      NULL);
+}
+
+
+static DBusMessage * msg_method_handler(DBusMessage *message,
+					struct wpa_dbus_object_desc *obj_dsc)
+{
+	const struct wpa_dbus_method_desc *method_dsc = obj_dsc->methods;
+	const char *method;
+	const char *msg_interface;
+
+	method = dbus_message_get_member(message);
+	msg_interface = dbus_message_get_interface(message);
+
+	/* try match call to any registered method */
+	while (method_dsc && method_dsc->dbus_method) {
+		/* compare method names and interfaces */
+		if (!os_strncmp(method_dsc->dbus_method, method,
+				WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
+		    !os_strncmp(method_dsc->dbus_interface, msg_interface,
+				WPAS_DBUS_INTERFACE_MAX))
+			break;
+
+		method_dsc++;
+	}
+	if (method_dsc == NULL || method_dsc->dbus_method == NULL) {
+		wpa_printf(MSG_DEBUG, "no method handler for %s.%s on %s",
+			   msg_interface, method,
+			   dbus_message_get_path(message));
+		return dbus_message_new_error(message,
+					      DBUS_ERROR_UNKNOWN_METHOD, NULL);
+	}
+
+	if (!is_signature_correct(message, method_dsc)) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      NULL);
+	}
+
+	return method_dsc->method_handler(message, obj_dsc->user_data);
+}
+
+
+/**
+ * message_handler - Handles incoming DBus messages
+ * @connection: DBus connection on which message was received
+ * @message: Received message
+ * @user_data: pointer to description of object to which message was sent
+ * Returns: Returns information whether message was handled or not
+ *
+ * Reads message interface and method name, then checks if they matches one
+ * of the special cases i.e. introspection call or properties get/getall/set
+ * methods and handles it. Else it iterates over registered methods list
+ * and tries to match method's name and interface to those read from message
+ * If appropriate method was found its handler function is called and
+ * response is sent. Otherwise, the DBUS_ERROR_UNKNOWN_METHOD error message
+ * will be sent.
+ */
+static DBusHandlerResult message_handler(DBusConnection *connection,
+					 DBusMessage *message, void *user_data)
+{
+	struct wpa_dbus_object_desc *obj_dsc = user_data;
+	const char *method;
+	const char *path;
+	const char *msg_interface;
+	DBusMessage *reply;
+
+	/* get method, interface and path the message is addressed to */
+	method = dbus_message_get_member(message);
+	path = dbus_message_get_path(message);
+	msg_interface = dbus_message_get_interface(message);
+	if (!method || !path || !msg_interface)
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s) [%s]",
+		   msg_interface, method, path,
+		   dbus_message_get_signature(message));
+
+	/* if message is introspection method call */
+	if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method,
+			WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
+	    !os_strncmp(WPA_DBUS_INTROSPECTION_INTERFACE, msg_interface,
+			WPAS_DBUS_INTERFACE_MAX)) {
+#ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
+		reply = wpa_dbus_introspect(message, obj_dsc);
+#else /* CONFIG_CTRL_IFACE_DBUS_INTRO */
+		reply = dbus_message_new_error(
+			message, DBUS_ERROR_UNKNOWN_METHOD,
+			"wpa_supplicant was compiled without introspection support.");
+#endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */
+	} else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface,
+			     WPAS_DBUS_INTERFACE_MAX)) {
+		/* if message is properties method call */
+		reply = properties_handler(message, obj_dsc);
+	} else {
+		reply = msg_method_handler(message, obj_dsc);
+	}
+
+	/* If handler succeed returning NULL, reply empty message */
+	if (!reply)
+		reply = dbus_message_new_method_return(message);
+	if (reply) {
+		if (!dbus_message_get_no_reply(message))
+			dbus_connection_send(connection, reply, NULL);
+		dbus_message_unref(reply);
+	}
+
+	wpa_dbus_flush_all_changed_properties(connection);
+
+	return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+
+/**
+ * free_dbus_object_desc - Frees object description data structure
+ * @connection: DBus connection
+ * @obj_dsc: Object description to free
+ *
+ * Frees each of properties, methods and signals description lists and
+ * the object description structure itself.
+ */
+void free_dbus_object_desc(struct wpa_dbus_object_desc *obj_dsc)
+{
+	if (!obj_dsc)
+		return;
+
+	/* free handler's argument */
+	if (obj_dsc->user_data_free_func)
+		obj_dsc->user_data_free_func(obj_dsc->user_data);
+
+	os_free(obj_dsc->path);
+	os_free(obj_dsc->prop_changed_flags);
+	os_free(obj_dsc);
+}
+
+
+static void free_dbus_object_desc_cb(DBusConnection *connection, void *obj_dsc)
+{
+	free_dbus_object_desc(obj_dsc);
+}
+
+
+/**
+ * wpa_dbus_ctrl_iface_init - Initialize dbus control interface
+ * @application_data: Pointer to application specific data structure
+ * @dbus_path: DBus path to interface object
+ * @dbus_service: DBus service name to register with
+ * @messageHandler: a pointer to function which will handle dbus messages
+ * coming on interface
+ * Returns: 0 on success, -1 on failure
+ *
+ * Initialize the dbus control interface and start receiving commands from
+ * external programs over the bus.
+ */
+int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface,
+			     char *dbus_path, char *dbus_service,
+			     struct wpa_dbus_object_desc *obj_desc)
+{
+	DBusError error;
+	int ret = -1;
+	DBusObjectPathVTable wpa_vtable = {
+		&free_dbus_object_desc_cb, &message_handler,
+		NULL, NULL, NULL, NULL
+	};
+
+	obj_desc->connection = iface->con;
+	obj_desc->path = os_strdup(dbus_path);
+
+	/* Register the message handler for the global dbus interface */
+	if (!dbus_connection_register_object_path(iface->con, dbus_path,
+						  &wpa_vtable, obj_desc)) {
+		wpa_printf(MSG_ERROR, "dbus: Could not set up message handler");
+		return -1;
+	}
+
+	/* Register our service with the message bus */
+	dbus_error_init(&error);
+	switch (dbus_bus_request_name(iface->con, dbus_service, 0, &error)) {
+	case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
+		ret = 0;
+		break;
+	case DBUS_REQUEST_NAME_REPLY_EXISTS:
+	case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
+	case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
+		wpa_printf(MSG_ERROR,
+			   "dbus: Could not request service name: already registered");
+		break;
+	default:
+		wpa_printf(MSG_ERROR,
+			   "dbus: Could not request service name: %s %s",
+			   error.name, error.message);
+		break;
+	}
+	dbus_error_free(&error);
+
+	if (ret != 0)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "Providing DBus service '%s'.", dbus_service);
+
+	return 0;
+}
+
+
+/**
+ * wpa_dbus_register_object_per_iface - Register a new object with dbus
+ * @ctrl_iface: pointer to dbus private data
+ * @path: DBus path to object
+ * @ifname: interface name
+ * @obj_desc: description of object's methods, signals and properties
+ * Returns: 0 on success, -1 on error
+ *
+ * Registers a new interface with dbus and assigns it a dbus object path.
+ */
+int wpa_dbus_register_object_per_iface(struct wpas_dbus_priv *ctrl_iface,
+				       const char *path, const char *ifname,
+				       struct wpa_dbus_object_desc *obj_desc)
+{
+	DBusConnection *con;
+	DBusError error;
+	DBusObjectPathVTable vtable = {
+		&free_dbus_object_desc_cb, &message_handler,
+		NULL, NULL, NULL, NULL
+	};
+
+	/* Do nothing if the control interface is not turned on */
+	if (ctrl_iface == NULL)
+		return 0;
+
+	con = ctrl_iface->con;
+	obj_desc->connection = con;
+	obj_desc->path = os_strdup(path);
+
+	dbus_error_init(&error);
+	/* Register the message handler for the interface functions */
+	if (!dbus_connection_try_register_object_path(con, path, &vtable,
+						      obj_desc, &error)) {
+		if (os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE) == 0) {
+			wpa_printf(MSG_DEBUG, "dbus: %s", error.message);
+		} else {
+			wpa_printf(MSG_ERROR,
+				   "dbus: Could not set up message handler for interface %s object %s (error: %s message: %s)",
+				   ifname, path, error.name, error.message);
+		}
+		dbus_error_free(&error);
+		return -1;
+	}
+
+	dbus_error_free(&error);
+	return 0;
+}
+
+
+static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx);
+
+
+/**
+ * wpa_dbus_unregister_object_per_iface - Unregisters DBus object
+ * @ctrl_iface: Pointer to dbus private data
+ * @path: DBus path to object which will be unregistered
+ * Returns: Zero on success and -1 on failure
+ *
+ * Unregisters DBus object given by its path
+ */
+int wpa_dbus_unregister_object_per_iface(
+	struct wpas_dbus_priv *ctrl_iface, const char *path)
+{
+	DBusConnection *con = ctrl_iface->con;
+	struct wpa_dbus_object_desc *obj_desc = NULL;
+
+	dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
+	if (!obj_desc) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: Could not obtain object's private data: %s",
+			   __func__, path);
+		return 0;
+	}
+
+	eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
+
+	if (!dbus_connection_unregister_object_path(con, path))
+		return -1;
+
+	return 0;
+}
+
+
+static dbus_bool_t put_changed_properties(
+	const struct wpa_dbus_object_desc *obj_dsc, const char *interface,
+	DBusMessageIter *dict_iter, int clear_changed)
+{
+	DBusMessageIter entry_iter;
+	const struct wpa_dbus_property_desc *dsc;
+	int i;
+	DBusError error;
+
+	for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property;
+	     dsc++, i++) {
+		if (obj_dsc->prop_changed_flags == NULL ||
+		    !obj_dsc->prop_changed_flags[i])
+			continue;
+		if (os_strcmp(dsc->dbus_interface, interface) != 0)
+			continue;
+		if (clear_changed)
+			obj_dsc->prop_changed_flags[i] = 0;
+
+		if (!dbus_message_iter_open_container(dict_iter,
+						      DBUS_TYPE_DICT_ENTRY,
+						      NULL, &entry_iter) ||
+		    !dbus_message_iter_append_basic(&entry_iter,
+						    DBUS_TYPE_STRING,
+						    &dsc->dbus_property))
+			return FALSE;
+
+		dbus_error_init(&error);
+		if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) {
+			if (dbus_error_is_set(&error)) {
+				wpa_printf(MSG_ERROR,
+					   "dbus: %s: Cannot get new value of property %s: (%s) %s",
+					   __func__, dsc->dbus_property,
+					   error.name, error.message);
+			} else {
+				wpa_printf(MSG_ERROR,
+					   "dbus: %s: Cannot get new value of property %s",
+					   __func__, dsc->dbus_property);
+			}
+			dbus_error_free(&error);
+			return FALSE;
+		}
+
+		if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+static void do_send_prop_changed_signal(
+	DBusConnection *con, const char *path, const char *interface,
+	const struct wpa_dbus_object_desc *obj_dsc)
+{
+	DBusMessage *msg;
+	DBusMessageIter signal_iter, dict_iter;
+
+	msg = dbus_message_new_signal(path, DBUS_INTERFACE_PROPERTIES,
+				      "PropertiesChanged");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &signal_iter);
+
+	if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING,
+					    &interface) ||
+	    /* Changed properties dict */
+	    !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
+					      "{sv}", &dict_iter) ||
+	    !put_changed_properties(obj_dsc, interface, &dict_iter, 0) ||
+	    !dbus_message_iter_close_container(&signal_iter, &dict_iter) ||
+	    /* Invalidated properties array (empty) */
+	    !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
+					      "s", &dict_iter) ||
+	    !dbus_message_iter_close_container(&signal_iter, &dict_iter)) {
+		wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
+			   __func__);
+	} else {
+		dbus_connection_send(con, msg, NULL);
+	}
+
+	dbus_message_unref(msg);
+}
+
+
+static void do_send_deprecated_prop_changed_signal(
+	DBusConnection *con, const char *path, const char *interface,
+	const struct wpa_dbus_object_desc *obj_dsc)
+{
+	DBusMessage *msg;
+	DBusMessageIter signal_iter, dict_iter;
+
+	msg = dbus_message_new_signal(path, interface, "PropertiesChanged");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &signal_iter);
+
+	if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
+					      "{sv}", &dict_iter) ||
+	    !put_changed_properties(obj_dsc, interface, &dict_iter, 1) ||
+	    !dbus_message_iter_close_container(&signal_iter, &dict_iter)) {
+		wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
+			   __func__);
+	} else {
+		dbus_connection_send(con, msg, NULL);
+	}
+
+	dbus_message_unref(msg);
+}
+
+
+static void send_prop_changed_signal(
+	DBusConnection *con, const char *path, const char *interface,
+	const struct wpa_dbus_object_desc *obj_dsc)
+{
+	/*
+	 * First, send property change notification on the standardized
+	 * org.freedesktop.DBus.Properties interface. This call will not
+	 * clear the property change bits, so that they are preserved for
+	 * the call that follows.
+	 */
+	do_send_prop_changed_signal(con, path, interface, obj_dsc);
+
+	/*
+	 * Now send PropertiesChanged on our own interface for backwards
+	 * compatibility. This is deprecated and will be removed in a future
+	 * release.
+	 */
+	do_send_deprecated_prop_changed_signal(con, path, interface, obj_dsc);
+
+	/* Property change bits have now been cleared. */
+}
+
+
+static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx)
+{
+	DBusConnection *con = eloop_ctx;
+	struct wpa_dbus_object_desc *obj_desc = timeout_ctx;
+
+	wpa_printf(MSG_DEBUG,
+		   "dbus: %s: Timeout - sending changed properties of object %s",
+		   __func__, obj_desc->path);
+	wpa_dbus_flush_object_changed_properties(con, obj_desc->path);
+}
+
+
+static void recursive_flush_changed_properties(DBusConnection *con,
+					       const char *path)
+{
+	char **objects = NULL;
+	char subobj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+	int i;
+
+	wpa_dbus_flush_object_changed_properties(con, path);
+
+	if (!dbus_connection_list_registered(con, path, &objects))
+		goto out;
+
+	for (i = 0; objects[i]; i++) {
+		os_snprintf(subobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+			    "%s/%s", path, objects[i]);
+		recursive_flush_changed_properties(con, subobj_path);
+	}
+
+out:
+	dbus_free_string_array(objects);
+}
+
+
+/**
+ * wpa_dbus_flush_all_changed_properties - Send all PropertiesChanged signals
+ * @con: DBus connection
+ *
+ * Traverses through all registered objects and sends PropertiesChanged for
+ * each properties.
+ */
+void wpa_dbus_flush_all_changed_properties(DBusConnection *con)
+{
+	recursive_flush_changed_properties(con, WPAS_DBUS_NEW_PATH);
+}
+
+
+/**
+ * wpa_dbus_flush_object_changed_properties - Send PropertiesChanged for object
+ * @con: DBus connection
+ * @path: path to a DBus object for which PropertiesChanged will be sent.
+ *
+ * Iterates over all properties registered with object and for each interface
+ * containing properties marked as changed, sends a PropertiesChanged signal
+ * containing names and new values of properties that have changed.
+ *
+ * You need to call this function after
+ * wpa_dbus_mark_property_changed() to send the PropertyChanged signal
+ * or after wpa_dbus_queue_property_changed() if you want to send
+ * PropertiesChanged signal immediately (i.e., without waiting timeout
+ * to expire).
+ *
+ * PropertiesChanged signal for an object is sent automatically short
+ * time after first marking property as changed. All PropertiesChanged
+ * signals are sent automatically after responding on DBus message, so
+ * if you marked a property changed as a result of DBus call (e.g.,
+ * param setter), you usually do not need to call this function.
+ */
+void wpa_dbus_flush_object_changed_properties(DBusConnection *con,
+					      const char *path)
+{
+	struct wpa_dbus_object_desc *obj_desc = NULL;
+	const struct wpa_dbus_property_desc *dsc;
+	int i;
+
+	dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
+	if (!obj_desc)
+		return;
+	eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
+
+	for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property;
+	     dsc++, i++) {
+		if (obj_desc->prop_changed_flags == NULL ||
+		    !obj_desc->prop_changed_flags[i])
+			continue;
+		send_prop_changed_signal(con, path, dsc->dbus_interface,
+					 obj_desc);
+	}
+}
+
+
+#define WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT 5000
+
+
+/**
+ * wpa_dbus_mark_property_changed - Mark a property as changed
+ * @iface: dbus priv struct
+ * @path: path to DBus object which property has changed
+ * @interface: interface containing changed property
+ * @property: property name which has changed
+ * Returns: A pointer to the private data for the object on success.
+ *
+ * Iterates over all properties registered with an object and marks the one
+ * given in parameters as changed. All parameters registered for an object
+ * within a single interface will be aggregated together and sent in one
+ * PropertiesChanged signal when function
+ * wpa_dbus_flush_object_changed_properties() is called.
+ */
+struct wpa_dbus_object_desc *
+wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
+                               const char *path, const char *interface,
+                               const char *property)
+{
+	struct wpa_dbus_object_desc *obj_desc = NULL;
+	const struct wpa_dbus_property_desc *dsc;
+	int i = 0;
+
+	if (iface == NULL)
+		return NULL;
+
+	dbus_connection_get_object_path_data(iface->con, path,
+					     (void **) &obj_desc);
+	if (!obj_desc) {
+		wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
+			   "could not obtain object's private data: %s", path);
+		return NULL;
+	}
+
+	for (dsc = obj_desc->properties; dsc && dsc->dbus_property; dsc++, i++)
+		if (os_strcmp(property, dsc->dbus_property) == 0 &&
+		    os_strcmp(interface, dsc->dbus_interface) == 0) {
+			if (obj_desc->prop_changed_flags)
+				obj_desc->prop_changed_flags[i] = 1;
+			break;
+		}
+
+	if (!dsc || !dsc->dbus_property) {
+		wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
+			   "no property %s in object %s", property, path);
+		return NULL;
+	}
+
+        return obj_desc;
+}
+
+/**
+ * wpa_dbus_queue_property_changed - Mark a property as changed and send it
+ * @iface: dbus priv struct
+ * @path: path to DBus object which property has changed
+ * @interface: interface containing changed property
+ * @property: property name which has changed
+ *
+ * Iterates over all properties registered with an object and marks the one
+ * given in parameters as changed. All parameters registered for an object
+ * within a single interface will be aggregated together and sent in one
+ * PropertiesChanged signal when function
+ * wpa_dbus_flush_object_changed_properties() is called.
+ */
+void wpa_dbus_queue_property_changed(struct wpas_dbus_priv *iface,
+				    const char *path, const char *interface,
+				    const char *property)
+{
+	struct wpa_dbus_object_desc *obj_desc;
+
+	obj_desc = wpa_dbus_mark_property_changed(iface, path, interface,
+						  property);
+	if (!obj_desc)
+		return;
+
+	if (!eloop_is_timeout_registered(flush_object_timeout_handler,
+					 iface->con, obj_desc)) {
+		eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT,
+				       flush_object_timeout_handler,
+				       iface->con, obj_desc);
+	}
+}
+
+
+/**
+ * wpa_dbus_get_object_properties - Put object's properties into dictionary
+ * @iface: dbus priv struct
+ * @path: path to DBus object which properties will be obtained
+ * @interface: interface name which properties will be obtained
+ * @iter: DBus message iter at which to append property dictionary.
+ *
+ * Iterates over all properties registered with object and execute getters
+ * of those, which are readable and which interface matches interface
+ * specified as argument. Obtained properties values are stored in
+ * dict_iter dictionary.
+ */
+dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
+					   const char *path,
+					   const char *interface,
+					   DBusMessageIter *iter)
+{
+	struct wpa_dbus_object_desc *obj_desc = NULL;
+	DBusMessageIter dict_iter;
+	DBusError error;
+
+	dbus_connection_get_object_path_data(iface->con, path,
+					     (void **) &obj_desc);
+	if (!obj_desc) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: could not obtain object's private data: %s",
+			   __func__, path);
+		return FALSE;
+	}
+
+	if (!wpa_dbus_dict_open_write(iter, &dict_iter)) {
+		wpa_printf(MSG_ERROR, "dbus: %s: failed to open message dict",
+			   __func__);
+		return FALSE;
+	}
+
+	dbus_error_init(&error);
+	if (!fill_dict_with_properties(&dict_iter, obj_desc->properties,
+				       interface, obj_desc->user_data,
+				       &error)) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: failed to get object properties: (%s) %s",
+			   __func__,
+			   dbus_error_is_set(&error) ? error.name : "none",
+			   dbus_error_is_set(&error) ? error.message : "none");
+		dbus_error_free(&error);
+		return FALSE;
+	}
+
+	return wpa_dbus_dict_close_write(iter, &dict_iter);
+}
+
+/**
+ * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts
+ * @path: The dbus object path
+ * @sep: Separating part (e.g., "Networks" or "PersistentGroups")
+ * @item: (out) The part following the specified separator, if any
+ * Returns: The object path of the interface this path refers to
+ *
+ * For a given object path, decomposes the object path into object id and
+ * requested part, if those parts exist. The caller is responsible for freeing
+ * the returned value. The *item pointer points to that allocated value and must
+ * not be freed separately.
+ *
+ * As an example, path = "/fi/w1/wpa_supplicant1/Interfaces/1/Networks/0" and
+ * sep = "Networks" would result in "/fi/w1/wpa_supplicant1/Interfaces/1"
+ * getting returned and *items set to point to "0".
+ */
+char * wpas_dbus_new_decompose_object_path(const char *path, const char *sep,
+					   char **item)
+{
+	const unsigned int dev_path_prefix_len =
+		os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
+	char *obj_path_only;
+	char *pos;
+	size_t sep_len;
+
+	*item = NULL;
+
+	/* Verify that this starts with our interface prefix */
+	if (os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
+		       dev_path_prefix_len) != 0)
+		return NULL; /* not our path */
+
+	/* Ensure there's something at the end of the path */
+	if ((path + dev_path_prefix_len)[0] == '\0')
+		return NULL;
+
+	obj_path_only = os_strdup(path);
+	if (obj_path_only == NULL)
+		return NULL;
+
+	pos = obj_path_only + dev_path_prefix_len;
+	pos = os_strchr(pos, '/');
+	if (pos == NULL)
+		return obj_path_only; /* no next item on the path */
+
+	 /* Separate network interface prefix from the path */
+	*pos++ = '\0';
+
+	sep_len = os_strlen(sep);
+	if (os_strncmp(pos, sep, sep_len) != 0 || pos[sep_len] != '/')
+		return obj_path_only; /* no match */
+
+	 /* return a pointer to the requested item */
+	*item = pos + sep_len + 1;
+	return obj_path_only;
+}
+
+
+/**
+ * wpas_dbus_reply_new_from_error - Create a new D-Bus error message from a
+ *   dbus error structure
+ * @message: The original request message for which the error is a reply
+ * @error: The error containing a name and a descriptive error cause
+ * @fallback_name: A generic error name if @error was not set
+ * @fallback_string: A generic error string if @error was not set
+ * Returns: A new D-Bus error message
+ *
+ * Given a DBusMessage structure, creates a new D-Bus error message using
+ * the error name and string contained in that structure.
+ */
+DBusMessage * wpas_dbus_reply_new_from_error(DBusMessage *message,
+					     DBusError *error,
+					     const char *fallback_name,
+					     const char *fallback_string)
+{
+	if (error && error->name && error->message) {
+		return dbus_message_new_error(message, error->name,
+					      error->message);
+	}
+	if (fallback_name && fallback_string) {
+		return dbus_message_new_error(message, fallback_name,
+					      fallback_string);
+	}
+	return NULL;
+}
diff --git a/hostap/wpa_supplicant/dbus/dbus_new_helpers.h b/hostap/wpa_supplicant/dbus/dbus_new_helpers.h
new file mode 100644
index 0000000..37b779c
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_new_helpers.h
@@ -0,0 +1,152 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_DBUS_CTRL_H
+#define WPA_DBUS_CTRL_H
+
+#include <dbus/dbus.h>
+
+typedef DBusMessage * (*WPADBusMethodHandler)(DBusMessage *message,
+					      void *user_data);
+typedef void (*WPADBusArgumentFreeFunction)(void *handler_arg);
+
+typedef dbus_bool_t (*WPADBusPropertyAccessor)(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data);
+
+struct wpa_dbus_object_desc {
+	DBusConnection *connection;
+	char *path;
+
+	/* list of methods, properties and signals registered with object */
+	const struct wpa_dbus_method_desc *methods;
+	const struct wpa_dbus_signal_desc *signals;
+	const struct wpa_dbus_property_desc *properties;
+
+	/* property changed flags */
+	u8 *prop_changed_flags;
+
+	/* argument for method handlers and properties
+	 * getter and setter functions */
+	void *user_data;
+	/* function used to free above argument */
+	WPADBusArgumentFreeFunction user_data_free_func;
+};
+
+enum dbus_arg_direction { ARG_IN, ARG_OUT };
+
+struct wpa_dbus_argument {
+	char *name;
+	char *type;
+	enum dbus_arg_direction dir;
+};
+
+#define END_ARGS { NULL, NULL, ARG_IN }
+
+/**
+ * struct wpa_dbus_method_desc - DBus method description
+ */
+struct wpa_dbus_method_desc {
+	/* method name */
+	const char *dbus_method;
+	/* method interface */
+	const char *dbus_interface;
+	/* method handling function */
+	WPADBusMethodHandler method_handler;
+	/* array of arguments */
+	struct wpa_dbus_argument args[4];
+};
+
+/**
+ * struct wpa_dbus_signal_desc - DBus signal description
+ */
+struct wpa_dbus_signal_desc {
+	/* signal name */
+	const char *dbus_signal;
+	/* signal interface */
+	const char *dbus_interface;
+	/* array of arguments */
+	struct wpa_dbus_argument args[4];
+};
+
+/**
+ * struct wpa_dbus_property_desc - DBus property description
+ */
+struct wpa_dbus_property_desc {
+	/* property name */
+	const char *dbus_property;
+	/* property interface */
+	const char *dbus_interface;
+	/* property type signature in DBus type notation */
+	const char *type;
+	/* property getter function */
+	WPADBusPropertyAccessor getter;
+	/* property setter function */
+	WPADBusPropertyAccessor setter;
+};
+
+
+#define WPAS_DBUS_OBJECT_PATH_MAX 150
+#define WPAS_DBUS_INTERFACE_MAX 150
+#define WPAS_DBUS_METHOD_SIGNAL_PROP_MAX 50
+#define WPAS_DBUS_AUTH_MODE_MAX 64
+
+#define WPA_DBUS_INTROSPECTION_INTERFACE "org.freedesktop.DBus.Introspectable"
+#define WPA_DBUS_INTROSPECTION_METHOD "Introspect"
+#define WPA_DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
+#define WPA_DBUS_PROPERTIES_GET "Get"
+#define WPA_DBUS_PROPERTIES_SET "Set"
+#define WPA_DBUS_PROPERTIES_GETALL "GetAll"
+
+void free_dbus_object_desc(struct wpa_dbus_object_desc *obj_dsc);
+
+int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface, char *dbus_path,
+			     char *dbus_service,
+			     struct wpa_dbus_object_desc *obj_desc);
+
+int wpa_dbus_register_object_per_iface(
+	struct wpas_dbus_priv *ctrl_iface,
+	const char *path, const char *ifname,
+	struct wpa_dbus_object_desc *obj_desc);
+
+int wpa_dbus_unregister_object_per_iface(
+	struct wpas_dbus_priv *ctrl_iface,
+	const char *path);
+
+dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
+					   const char *path,
+					   const char *interface,
+					   DBusMessageIter *iter);
+
+
+void wpa_dbus_flush_all_changed_properties(DBusConnection *con);
+
+void wpa_dbus_flush_object_changed_properties(DBusConnection *con,
+					      const char *path);
+
+struct wpa_dbus_object_desc * wpa_dbus_mark_property_changed(
+	struct wpas_dbus_priv *iface,
+	const char *path, const char *interface,
+	const char *property);
+void wpa_dbus_queue_property_changed(struct wpas_dbus_priv *iface,
+				     const char *path, const char *interface,
+				     const char *property);
+
+DBusMessage * wpa_dbus_introspect(DBusMessage *message,
+				  struct wpa_dbus_object_desc *obj_dsc);
+
+char * wpas_dbus_new_decompose_object_path(const char *path, const char *sep,
+					   char **item);
+
+DBusMessage *wpas_dbus_reply_new_from_error(DBusMessage *message,
+					    DBusError *error,
+					    const char *fallback_name,
+					    const char *fallback_string);
+
+#endif /* WPA_DBUS_CTRL_H */
diff --git a/hostap/wpa_supplicant/dbus/dbus_new_introspect.c b/hostap/wpa_supplicant/dbus/dbus_new_introspect.c
new file mode 100644
index 0000000..fba57e6
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_new_introspect.c
@@ -0,0 +1,286 @@
+/*
+ * wpa_supplicant - D-Bus introspection
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ * Copyright (c) 2010, 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/list.h"
+#include "utils/wpabuf.h"
+#include "dbus_common_i.h"
+#include "dbus_new_helpers.h"
+
+
+struct interfaces {
+	struct dl_list list;
+	char *dbus_interface;
+	struct wpabuf *xml;
+};
+
+
+static struct interfaces * add_interface(struct dl_list *list,
+					 const char *dbus_interface)
+{
+	struct interfaces *iface;
+
+	dl_list_for_each(iface, list, struct interfaces, list) {
+		if (os_strcmp(iface->dbus_interface, dbus_interface) == 0)
+			return iface; /* already in the list */
+	}
+
+	iface = os_zalloc(sizeof(struct interfaces));
+	if (!iface)
+		return NULL;
+	iface->dbus_interface = os_strdup(dbus_interface);
+	iface->xml = wpabuf_alloc(6000);
+	if (iface->dbus_interface == NULL || iface->xml == NULL) {
+		os_free(iface->dbus_interface);
+		wpabuf_free(iface->xml);
+		os_free(iface);
+		return NULL;
+	}
+	wpabuf_printf(iface->xml, "<interface name=\"%s\">", dbus_interface);
+	dl_list_add_tail(list, &iface->list);
+	return iface;
+}
+
+
+static void add_arg(struct wpabuf *xml, const char *name, const char *type,
+		    const char *direction)
+{
+	wpabuf_printf(xml, "<arg name=\"%s\"", name);
+	if (type)
+		wpabuf_printf(xml, " type=\"%s\"", type);
+	if (direction)
+		wpabuf_printf(xml, " direction=\"%s\"", direction);
+	wpabuf_put_str(xml, "/>");
+}
+
+
+static void add_entry(struct wpabuf *xml, const char *type, const char *name,
+		      const struct wpa_dbus_argument *args, int include_dir)
+{
+	const struct wpa_dbus_argument *arg;
+
+	if (args == NULL || args->name == NULL) {
+		wpabuf_printf(xml, "<%s name=\"%s\"/>", type, name);
+		return;
+	}
+	wpabuf_printf(xml, "<%s name=\"%s\">", type, name);
+	for (arg = args; arg && arg->name; arg++) {
+		add_arg(xml, arg->name, arg->type,
+			include_dir ? (arg->dir == ARG_IN ? "in" : "out") :
+			NULL);
+	}
+	wpabuf_printf(xml, "</%s>", type);
+}
+
+
+static void add_property(struct wpabuf *xml,
+			 const struct wpa_dbus_property_desc *dsc)
+{
+	wpabuf_printf(xml, "<property name=\"%s\" type=\"%s\" "
+		      "access=\"%s%s\"/>",
+		      dsc->dbus_property, dsc->type,
+		      dsc->getter ? "read" : "",
+		      dsc->setter ? "write" : "");
+}
+
+
+static void extract_interfaces_methods(
+	struct dl_list *list, const struct wpa_dbus_method_desc *methods)
+{
+	const struct wpa_dbus_method_desc *dsc;
+	struct interfaces *iface;
+
+	for (dsc = methods; dsc && dsc->dbus_method; dsc++) {
+		iface = add_interface(list, dsc->dbus_interface);
+		if (iface)
+			add_entry(iface->xml, "method", dsc->dbus_method,
+				  dsc->args, 1);
+	}
+}
+
+
+static void extract_interfaces_signals(
+	struct dl_list *list, const struct wpa_dbus_signal_desc *signals)
+{
+	const struct wpa_dbus_signal_desc *dsc;
+	struct interfaces *iface;
+
+	for (dsc = signals; dsc && dsc->dbus_signal; dsc++) {
+		iface = add_interface(list, dsc->dbus_interface);
+		if (iface)
+			add_entry(iface->xml, "signal", dsc->dbus_signal,
+				  dsc->args, 0);
+	}
+}
+
+
+static void extract_interfaces_properties(
+	struct dl_list *list, const struct wpa_dbus_property_desc *properties)
+{
+	const struct wpa_dbus_property_desc *dsc;
+	struct interfaces *iface;
+
+	for (dsc = properties; dsc && dsc->dbus_property; dsc++) {
+		iface = add_interface(list, dsc->dbus_interface);
+		if (iface)
+			add_property(iface->xml, dsc);
+	}
+}
+
+
+/**
+ * extract_interfaces - Extract interfaces from methods, signals and props
+ * @list: Interface list to be filled
+ * @obj_dsc: Description of object from which interfaces will be extracted
+ *
+ * Iterates over all methods, signals, and properties registered with an
+ * object and collects all declared DBus interfaces and create interfaces'
+ * node in XML root node for each. Returned list elements contain interface
+ * name and XML node of corresponding interface.
+ */
+static void extract_interfaces(struct dl_list *list,
+			       struct wpa_dbus_object_desc *obj_dsc)
+{
+	extract_interfaces_methods(list, obj_dsc->methods);
+	extract_interfaces_signals(list, obj_dsc->signals);
+	extract_interfaces_properties(list, obj_dsc->properties);
+}
+
+
+static void add_interfaces(struct dl_list *list, struct wpabuf *xml)
+{
+	struct interfaces *iface, *n;
+
+	dl_list_for_each_safe(iface, n, list, struct interfaces, list) {
+		if (wpabuf_len(iface->xml) + 20 < wpabuf_tailroom(xml)) {
+			wpabuf_put_buf(xml, iface->xml);
+			wpabuf_put_str(xml, "</interface>");
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: Not enough room for add_interfaces inspect data: tailroom %u, add %u",
+				   (unsigned int) wpabuf_tailroom(xml),
+				   (unsigned int) wpabuf_len(iface->xml));
+		}
+		dl_list_del(&iface->list);
+		wpabuf_free(iface->xml);
+		os_free(iface->dbus_interface);
+		os_free(iface);
+	}
+}
+
+
+static void add_child_nodes(struct wpabuf *xml, DBusConnection *con,
+			    const char *path)
+{
+	char **children;
+	int i;
+
+	/* add child nodes to introspection tree */
+	dbus_connection_list_registered(con, path, &children);
+	for (i = 0; children[i]; i++)
+		wpabuf_printf(xml, "<node name=\"%s\"/>", children[i]);
+	dbus_free_string_array(children);
+}
+
+
+static void add_introspectable_interface(struct wpabuf *xml)
+{
+	wpabuf_printf(xml, "<interface name=\"%s\">"
+		      "<method name=\"%s\">"
+		      "<arg name=\"data\" type=\"s\" direction=\"out\"/>"
+		      "</method>"
+		      "</interface>",
+		      WPA_DBUS_INTROSPECTION_INTERFACE,
+		      WPA_DBUS_INTROSPECTION_METHOD);
+}
+
+
+static void add_properties_interface(struct wpabuf *xml)
+{
+	wpabuf_printf(xml, "<interface name=\"%s\">",
+		      WPA_DBUS_PROPERTIES_INTERFACE);
+
+	wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_GET);
+	add_arg(xml, "interface", "s", "in");
+	add_arg(xml, "propname", "s", "in");
+	add_arg(xml, "value", "v", "out");
+	wpabuf_put_str(xml, "</method>");
+
+	wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_GETALL);
+	add_arg(xml, "interface", "s", "in");
+	add_arg(xml, "props", "a{sv}", "out");
+	wpabuf_put_str(xml, "</method>");
+
+	wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_SET);
+	add_arg(xml, "interface", "s", "in");
+	add_arg(xml, "propname", "s", "in");
+	add_arg(xml, "value", "v", "in");
+	wpabuf_put_str(xml, "</method>");
+
+	wpabuf_put_str(xml, "</interface>");
+}
+
+
+static void add_wpas_interfaces(struct wpabuf *xml,
+				struct wpa_dbus_object_desc *obj_dsc)
+{
+	struct dl_list ifaces;
+
+	dl_list_init(&ifaces);
+	extract_interfaces(&ifaces, obj_dsc);
+	add_interfaces(&ifaces, xml);
+}
+
+
+/**
+ * wpa_dbus_introspect - Responds for Introspect calls on object
+ * @message: Message with Introspect call
+ * @obj_dsc: Object description on which Introspect was called
+ * Returns: Message with introspection result XML string as only argument
+ *
+ * Iterates over all methods, signals and properties registered with
+ * object and generates introspection data for the object as XML string.
+ */
+DBusMessage * wpa_dbus_introspect(DBusMessage *message,
+				  struct wpa_dbus_object_desc *obj_dsc)
+{
+
+	DBusMessage *reply;
+	struct wpabuf *xml;
+
+	xml = wpabuf_alloc(15000);
+	if (xml == NULL)
+		return NULL;
+
+	wpabuf_put_str(xml, "<?xml version=\"1.0\"?>\n");
+	wpabuf_put_str(xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+	wpabuf_put_str(xml, "<node>");
+
+	add_introspectable_interface(xml);
+	add_properties_interface(xml);
+	add_wpas_interfaces(xml, obj_dsc);
+	add_child_nodes(xml, obj_dsc->connection,
+			dbus_message_get_path(message));
+
+	wpabuf_put_str(xml, "</node>\n");
+
+	reply = dbus_message_new_method_return(message);
+	if (reply) {
+		const char *intro_str = wpabuf_head(xml);
+
+		dbus_message_append_args(reply, DBUS_TYPE_STRING, &intro_str,
+					 DBUS_TYPE_INVALID);
+	}
+	wpabuf_free(xml);
+
+	return reply;
+}
diff --git a/hostap/wpa_supplicant/dbus/dbus_old.c b/hostap/wpa_supplicant/dbus/dbus_old.c
new file mode 100644
index 0000000..88227af
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_old.c
@@ -0,0 +1,745 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <dbus/dbus.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "wps/wps.h"
+#include "../config.h"
+#include "../wpa_supplicant_i.h"
+#include "../bss.h"
+#include "dbus_old.h"
+#include "dbus_old_handlers.h"
+#include "dbus_common_i.h"
+
+
+/**
+ * wpas_dbus_decompose_object_path - Decompose an interface object path into parts
+ * @path: The dbus object path
+ * @network: (out) the configured network this object path refers to, if any
+ * @bssid: (out) the scanned bssid this object path refers to, if any
+ * Returns: The object path of the network interface this path refers to
+ *
+ * For a given object path, decomposes the object path into object id, network,
+ * and BSSID parts, if those parts exist.
+ */
+char * wpas_dbus_decompose_object_path(const char *path, char **network,
+				       char **bssid)
+{
+	const unsigned int dev_path_prefix_len =
+		strlen(WPAS_DBUS_PATH_INTERFACES "/");
+	char *obj_path_only;
+	char *next_sep;
+
+	/* Be a bit paranoid about path */
+	if (!path || strncmp(path, WPAS_DBUS_PATH_INTERFACES "/",
+			     dev_path_prefix_len))
+		return NULL;
+
+	/* Ensure there's something at the end of the path */
+	if ((path + dev_path_prefix_len)[0] == '\0')
+		return NULL;
+
+	obj_path_only = os_strdup(path);
+	if (obj_path_only == NULL)
+		return NULL;
+
+	next_sep = strchr(obj_path_only + dev_path_prefix_len, '/');
+	if (next_sep != NULL) {
+		const char *net_part = strstr(next_sep,
+					      WPAS_DBUS_NETWORKS_PART "/");
+		const char *bssid_part = strstr(next_sep,
+						WPAS_DBUS_BSSIDS_PART "/");
+
+		if (network && net_part) {
+			/* Deal with a request for a configured network */
+			const char *net_name = net_part +
+				strlen(WPAS_DBUS_NETWORKS_PART "/");
+			*network = NULL;
+			if (strlen(net_name))
+				*network = os_strdup(net_name);
+		} else if (bssid && bssid_part) {
+			/* Deal with a request for a scanned BSSID */
+			const char *bssid_name = bssid_part +
+				strlen(WPAS_DBUS_BSSIDS_PART "/");
+			if (strlen(bssid_name))
+				*bssid = os_strdup(bssid_name);
+			else
+				*bssid = NULL;
+		}
+
+		/* Cut off interface object path before "/" */
+		*next_sep = '\0';
+	}
+
+	return obj_path_only;
+}
+
+
+/**
+ * wpas_dbus_new_invalid_iface_error - Return a new invalid interface error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: A dbus error message
+ *
+ * Convenience function to create and return an invalid interface error
+ */
+DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message)
+{
+	return dbus_message_new_error(
+		message, WPAS_ERROR_INVALID_IFACE,
+		"wpa_supplicant knows nothing about this interface.");
+}
+
+
+/**
+ * wpas_dbus_new_invalid_network_error - Return a new invalid network error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an invalid network error
+ */
+DBusMessage * wpas_dbus_new_invalid_network_error(DBusMessage *message)
+{
+	return dbus_message_new_error(message, WPAS_ERROR_INVALID_NETWORK,
+				      "The requested network does not exist.");
+}
+
+
+/**
+ * wpas_dbus_new_invalid_bssid_error - Return a new invalid bssid error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an invalid bssid error
+ */
+static DBusMessage * wpas_dbus_new_invalid_bssid_error(DBusMessage *message)
+{
+	return dbus_message_new_error(message, WPAS_ERROR_INVALID_BSSID,
+				      "The BSSID requested was invalid.");
+}
+
+
+/**
+ * wpas_dispatch_network_method - dispatch messages for configured networks
+ * @message: the incoming dbus message
+ * @wpa_s: a network interface's data
+ * @network_id: id of the configured network we're interested in
+ * Returns: a reply dbus message, or a dbus error message
+ *
+ * This function dispatches all incoming dbus messages for configured networks.
+ */
+static DBusMessage * wpas_dispatch_network_method(DBusMessage *message,
+						  struct wpa_supplicant *wpa_s,
+						  int network_id)
+{
+	DBusMessage *reply = NULL;
+	const char *method = dbus_message_get_member(message);
+	struct wpa_ssid *ssid;
+
+	ssid = wpa_config_get_network(wpa_s->conf, network_id);
+	if (ssid == NULL)
+		return wpas_dbus_new_invalid_network_error(message);
+
+	if (!strcmp(method, "set"))
+		reply = wpas_dbus_iface_set_network(message, wpa_s, ssid);
+	else if (!strcmp(method, "enable"))
+		reply = wpas_dbus_iface_enable_network(message, wpa_s, ssid);
+	else if (!strcmp(method, "disable"))
+		reply = wpas_dbus_iface_disable_network(message, wpa_s, ssid);
+
+	return reply;
+}
+
+
+/**
+ * wpas_dispatch_bssid_method - dispatch messages for scanned networks
+ * @message: the incoming dbus message
+ * @wpa_s: a network interface's data
+ * @bssid: bssid of the scanned network we're interested in
+ * Returns: a reply dbus message, or a dbus error message
+ *
+ * This function dispatches all incoming dbus messages for scanned networks.
+ */
+static DBusMessage * wpas_dispatch_bssid_method(DBusMessage *message,
+						struct wpa_supplicant *wpa_s,
+						const char *bssid_txt)
+{
+	u8 bssid[ETH_ALEN];
+	struct wpa_bss *bss;
+
+	if (hexstr2bin(bssid_txt, bssid, ETH_ALEN) < 0)
+		return wpas_dbus_new_invalid_bssid_error(message);
+
+	bss = wpa_bss_get_bssid(wpa_s, bssid);
+	if (bss == NULL)
+		return wpas_dbus_new_invalid_bssid_error(message);
+
+	/* Dispatch the method call against the scanned bssid */
+	if (os_strcmp(dbus_message_get_member(message), "properties") == 0)
+		return wpas_dbus_bssid_properties(message, wpa_s, bss);
+
+	return NULL;
+}
+
+
+/**
+ * wpas_iface_message_handler - Dispatch messages for interfaces or networks
+ * @connection: Connection to the system message bus
+ * @message: An incoming dbus message
+ * @user_data: A pointer to a dbus control interface data structure
+ * Returns: Whether or not the message was handled
+ *
+ * This function dispatches all incoming dbus messages for network interfaces,
+ * or objects owned by them, such as scanned BSSIDs and configured networks.
+ */
+static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection,
+						    DBusMessage *message,
+						    void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	const char *method = dbus_message_get_member(message);
+	const char *path = dbus_message_get_path(message);
+	const char *msg_interface = dbus_message_get_interface(message);
+	char *iface_obj_path = NULL;
+	char *network = NULL;
+	char *bssid = NULL;
+	DBusMessage *reply = NULL;
+
+	/* Caller must specify a message interface */
+	if (!msg_interface)
+		goto out;
+
+	wpa_printf(MSG_MSGDUMP, "dbus[old/iface]: %s.%s (%s) [%s]",
+		   msg_interface, method, path,
+		   dbus_message_get_signature(message));
+
+	iface_obj_path = wpas_dbus_decompose_object_path(path, &network,
+							 &bssid);
+	if (iface_obj_path == NULL) {
+		reply = wpas_dbus_new_invalid_iface_error(message);
+		goto out;
+	}
+
+	/* Make sure the message's object path actually refers to the
+	 * wpa_supplicant structure it's supposed to (which is wpa_s)
+	 */
+	if (wpa_supplicant_get_iface_by_dbus_path(wpa_s->global,
+						  iface_obj_path) != wpa_s) {
+		reply = wpas_dbus_new_invalid_iface_error(message);
+		goto out;
+	}
+
+	if (network && !strcmp(msg_interface, WPAS_DBUS_IFACE_NETWORK)) {
+		/* A method for one of this interface's configured networks */
+		int nid = strtoul(network, NULL, 10);
+
+		if (errno != EINVAL)
+			reply = wpas_dispatch_network_method(message, wpa_s,
+							     nid);
+		else
+			reply = wpas_dbus_new_invalid_network_error(message);
+	} else if (bssid && !strcmp(msg_interface, WPAS_DBUS_IFACE_BSSID)) {
+		/* A method for one of this interface's scanned BSSIDs */
+		reply = wpas_dispatch_bssid_method(message, wpa_s, bssid);
+	} else if (!strcmp(msg_interface, WPAS_DBUS_IFACE_INTERFACE)) {
+		/* A method for an interface only. */
+		if (!strcmp(method, "scan"))
+			reply = wpas_dbus_iface_scan(message, wpa_s);
+		else if (!strcmp(method, "scanResults"))
+			reply = wpas_dbus_iface_scan_results(message, wpa_s);
+		else if (!strcmp(method, "addNetwork"))
+			reply = wpas_dbus_iface_add_network(message, wpa_s);
+		else if (!strcmp(method, "removeNetwork"))
+			reply = wpas_dbus_iface_remove_network(message, wpa_s);
+		else if (!strcmp(method, "selectNetwork"))
+			reply = wpas_dbus_iface_select_network(message, wpa_s);
+		else if (!strcmp(method, "capabilities"))
+			reply = wpas_dbus_iface_capabilities(message, wpa_s);
+		else if (!strcmp(method, "disconnect"))
+			reply = wpas_dbus_iface_disconnect(message, wpa_s);
+		else if (!strcmp(method, "setAPScan"))
+			reply = wpas_dbus_iface_set_ap_scan(message, wpa_s);
+		else if (!strcmp(method, "setSmartcardModules"))
+			reply = wpas_dbus_iface_set_smartcard_modules(message,
+								      wpa_s);
+		else if (!strcmp(method, "state"))
+			reply = wpas_dbus_iface_get_state(message, wpa_s);
+		else if (!strcmp(method, "scanning"))
+			reply = wpas_dbus_iface_get_scanning(message, wpa_s);
+#ifndef CONFIG_NO_CONFIG_BLOBS
+		else if (!strcmp(method, "setBlobs"))
+			reply = wpas_dbus_iface_set_blobs(message, wpa_s);
+		else if (!strcmp(method, "removeBlobs"))
+			reply = wpas_dbus_iface_remove_blobs(message, wpa_s);
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+#ifdef CONFIG_WPS
+		else if (os_strcmp(method, "wpsPbc") == 0)
+			reply = wpas_dbus_iface_wps_pbc(message, wpa_s);
+		else if (os_strcmp(method, "wpsPin") == 0)
+			reply = wpas_dbus_iface_wps_pin(message, wpa_s);
+		else if (os_strcmp(method, "wpsReg") == 0)
+			reply = wpas_dbus_iface_wps_reg(message, wpa_s);
+#endif /* CONFIG_WPS */
+		else if (os_strcmp(method, "flush") == 0)
+			reply = wpas_dbus_iface_flush(message, wpa_s);
+	}
+
+	/* If the message was handled, send back the reply */
+out:
+	if (reply) {
+		if (!dbus_message_get_no_reply(message))
+			dbus_connection_send(connection, reply, NULL);
+		dbus_message_unref(reply);
+	}
+
+	os_free(iface_obj_path);
+	os_free(network);
+	os_free(bssid);
+	return reply ? DBUS_HANDLER_RESULT_HANDLED :
+		DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+/**
+ * wpas_message_handler - dispatch incoming dbus messages
+ * @connection: connection to the system message bus
+ * @message: an incoming dbus message
+ * @user_data: a pointer to a dbus control interface data structure
+ * Returns: whether or not the message was handled
+ *
+ * This function dispatches all incoming dbus messages to the correct
+ * handlers, depending on what the message's target object path is,
+ * and what the method call is.
+ */
+static DBusHandlerResult wpas_message_handler(DBusConnection *connection,
+	DBusMessage *message, void *user_data)
+{
+	struct wpas_dbus_priv *ctrl_iface = user_data;
+	const char *method;
+	const char *path;
+	const char *msg_interface;
+	DBusMessage *reply = NULL;
+
+	method = dbus_message_get_member(message);
+	path = dbus_message_get_path(message);
+	msg_interface = dbus_message_get_interface(message);
+	if (!method || !path || !ctrl_iface || !msg_interface)
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	wpa_printf(MSG_MSGDUMP, "dbus[old]: %s.%s (%s) [%s]",
+		   msg_interface, method, path,
+		   dbus_message_get_signature(message));
+
+	/* Validate the method interface */
+	if (strcmp(msg_interface, WPAS_DBUS_INTERFACE) != 0)
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	if (!strcmp(path, WPAS_DBUS_PATH)) {
+		/* dispatch methods against our global dbus interface here */
+		if (!strcmp(method, "addInterface")) {
+			reply = wpas_dbus_global_add_interface(
+				message, ctrl_iface->global);
+		} else if (!strcmp(method, "removeInterface")) {
+			reply = wpas_dbus_global_remove_interface(
+				message, ctrl_iface->global);
+		} else if (!strcmp(method, "getInterface")) {
+			reply = wpas_dbus_global_get_interface(
+				message, ctrl_iface->global);
+		} else if (!strcmp(method, "setDebugParams")) {
+			reply = wpas_dbus_global_set_debugparams(
+				message, ctrl_iface->global);
+		}
+	}
+
+	/* If the message was handled, send back the reply */
+	if (reply) {
+		if (!dbus_message_get_no_reply(message))
+			dbus_connection_send(connection, reply, NULL);
+		dbus_message_unref(reply);
+	}
+
+	return reply ? DBUS_HANDLER_RESULT_HANDLED :
+		DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+/**
+ * wpa_supplicant_dbus_notify_scan_results - Send a scan results signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Notify listeners that this interface has updated scan results.
+ */
+void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dbus_priv *iface = wpa_s->global->dbus;
+	DBusMessage *_signal;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_path)
+		return;
+
+	_signal = dbus_message_new_signal(wpa_s->dbus_path,
+					  WPAS_DBUS_IFACE_INTERFACE,
+					  "ScanResultsAvailable");
+	if (_signal == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory to send scan results signal");
+		return;
+	}
+	dbus_connection_send(iface->con, _signal, NULL);
+	dbus_message_unref(_signal);
+}
+
+
+/**
+ * wpa_supplicant_dbus_notify_state_change - Send a state change signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @new_state: new state wpa_supplicant is entering
+ * @old_state: old state wpa_supplicant is leaving
+ * Returns: 0 on success, -1 on failure
+ *
+ * Notify listeners that wpa_supplicant has changed state
+ */
+void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s,
+					     enum wpa_states new_state,
+					     enum wpa_states old_state)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *_signal = NULL;
+	const char *new_state_str, *old_state_str;
+
+	if (wpa_s->dbus_path == NULL)
+		return; /* Skip signal since D-Bus setup is not yet ready */
+
+	/* Do nothing if the control interface is not turned on */
+	if (wpa_s->global == NULL)
+		return;
+	iface = wpa_s->global->dbus;
+	if (iface == NULL)
+		return;
+
+	/* Only send signal if state really changed */
+	if (new_state == old_state)
+		return;
+
+	_signal = dbus_message_new_signal(wpa_s->dbus_path,
+					  WPAS_DBUS_IFACE_INTERFACE,
+					  "StateChange");
+	if (_signal == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: could not create dbus signal; likely out of memory",
+			   __func__);
+		return;
+	}
+
+	new_state_str = wpa_supplicant_state_txt(new_state);
+	old_state_str = wpa_supplicant_state_txt(old_state);
+
+	if (!dbus_message_append_args(_signal,
+				      DBUS_TYPE_STRING, &new_state_str,
+				      DBUS_TYPE_STRING, &old_state_str,
+				      DBUS_TYPE_INVALID)) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: Not enough memory to construct state change signal",
+			   __func__);
+		goto out;
+	}
+
+	dbus_connection_send(iface->con, _signal, NULL);
+
+out:
+	dbus_message_unref(_signal);
+}
+
+
+/**
+ * wpa_supplicant_dbus_notify_scanning - send scanning status
+ * @wpa_s: %wpa_supplicant network interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Notify listeners of interface scanning state changes
+ */
+void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dbus_priv *iface = wpa_s->global->dbus;
+	DBusMessage *_signal;
+	dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_path)
+		return;
+
+	_signal = dbus_message_new_signal(wpa_s->dbus_path,
+					  WPAS_DBUS_IFACE_INTERFACE,
+					  "Scanning");
+	if (_signal == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory to send scan results signal");
+		return;
+	}
+
+	if (dbus_message_append_args(_signal,
+				     DBUS_TYPE_BOOLEAN, &scanning,
+				     DBUS_TYPE_INVALID)) {
+		dbus_connection_send(iface->con, _signal, NULL);
+	} else {
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory to construct signal");
+	}
+	dbus_message_unref(_signal);
+}
+
+
+#ifdef CONFIG_WPS
+void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
+					 const struct wps_credential *cred)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *_signal = NULL;
+
+	/* Do nothing if the control interface is not turned on */
+	if (wpa_s->global == NULL)
+		return;
+	iface = wpa_s->global->dbus;
+	if (iface == NULL || !wpa_s->dbus_path)
+		return;
+
+	_signal = dbus_message_new_signal(wpa_s->dbus_path,
+					  WPAS_DBUS_IFACE_INTERFACE,
+					  "WpsCred");
+	if (_signal == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: Could not create dbus signal; likely out of memory",
+			   __func__);
+		return;
+	}
+
+	if (!dbus_message_append_args(_signal,
+				      DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+				      &cred->cred_attr, cred->cred_attr_len,
+				      DBUS_TYPE_INVALID)) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: Not enough memory to construct signal",
+			   __func__);
+		goto out;
+	}
+
+	dbus_connection_send(iface->con, _signal, NULL);
+
+out:
+	dbus_message_unref(_signal);
+}
+#else /* CONFIG_WPS */
+void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
+					 const struct wps_credential *cred)
+{
+}
+#endif /* CONFIG_WPS */
+
+void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
+					      int depth, const char *subject,
+					      const char *cert_hash,
+					      const struct wpabuf *cert)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *_signal = NULL;
+	const char *hash;
+	const char *cert_hex;
+	int cert_hex_len;
+
+	/* Do nothing if the control interface is not turned on */
+	if (wpa_s->global == NULL)
+		return;
+	iface = wpa_s->global->dbus;
+	if (iface == NULL || !wpa_s->dbus_path)
+		return;
+
+	_signal = dbus_message_new_signal(wpa_s->dbus_path,
+					  WPAS_DBUS_IFACE_INTERFACE,
+					  "Certification");
+	if (_signal == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: Could not create dbus signal; likely out of memory",
+			   __func__);
+		return;
+	}
+
+	hash = cert_hash ? cert_hash : "";
+	cert_hex = cert ? wpabuf_head(cert) : "";
+	cert_hex_len = cert ? wpabuf_len(cert) : 0;
+
+	if (!dbus_message_append_args(_signal,
+				      DBUS_TYPE_INT32, &depth,
+				      DBUS_TYPE_STRING, &subject,
+				      DBUS_TYPE_STRING, &hash,
+				      DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+				      &cert_hex, cert_hex_len,
+				      DBUS_TYPE_INVALID)) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: Not enough memory to construct signal",
+			   __func__);
+		goto out;
+	}
+
+	dbus_connection_send(iface->con, _signal, NULL);
+
+out:
+	dbus_message_unref(_signal);
+
+}
+
+
+/**
+ * wpa_supplicant_dbus_ctrl_iface_init - Initialize dbus control interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * Initialize the dbus control interface and start receiving commands from
+ * external programs over the bus.
+ */
+int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface)
+{
+	DBusError error;
+	int ret = -1;
+	DBusObjectPathVTable wpas_vtable = {
+		NULL, &wpas_message_handler, NULL, NULL, NULL, NULL
+	};
+
+	/* Register the message handler for the global dbus interface */
+	if (!dbus_connection_register_object_path(iface->con,
+						  WPAS_DBUS_PATH, &wpas_vtable,
+						  iface)) {
+		wpa_printf(MSG_ERROR, "dbus: Could not set up message handler");
+		return -1;
+	}
+
+	/* Register our service with the message bus */
+	dbus_error_init(&error);
+	switch (dbus_bus_request_name(iface->con, WPAS_DBUS_SERVICE,
+				      0, &error)) {
+	case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
+		ret = 0;
+		break;
+	case DBUS_REQUEST_NAME_REPLY_EXISTS:
+	case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
+	case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
+		wpa_printf(MSG_ERROR,
+			   "dbus: Could not request service name: already registered");
+		break;
+	default:
+		wpa_printf(MSG_ERROR,
+			   "dbus: Could not request service name: %s %s",
+			   error.name, error.message);
+		break;
+	}
+	dbus_error_free(&error);
+
+	if (ret != 0)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "Providing DBus service '" WPAS_DBUS_SERVICE
+		   "'.");
+
+	return 0;
+}
+
+
+/**
+ * wpas_dbus_register_new_iface - Register a new interface with dbus
+ * @wpa_s: %wpa_supplicant interface description structure to register
+ * Returns: 0 on success, -1 on error
+ *
+ * Registers a new interface with dbus and assigns it a dbus object path.
+ */
+int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus;
+	DBusConnection * con;
+	u32 next;
+	DBusObjectPathVTable vtable = {
+		NULL, &wpas_iface_message_handler, NULL, NULL, NULL, NULL
+	};
+
+	/* Do nothing if the control interface is not turned on */
+	if (ctrl_iface == NULL)
+		return 0;
+
+	con = ctrl_iface->con;
+	next = ctrl_iface->next_objid++;
+
+	/* Create and set the interface's object path */
+	wpa_s->dbus_path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+	if (wpa_s->dbus_path == NULL)
+		return -1;
+	os_snprintf(wpa_s->dbus_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    WPAS_DBUS_PATH_INTERFACES "/%u",
+		    next);
+
+	/* Register the message handler for the interface functions */
+	if (!dbus_connection_register_fallback(con, wpa_s->dbus_path, &vtable,
+					       wpa_s)) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: Could not set up message handler for interface %s",
+			   wpa_s->ifname);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpas_dbus_unregister_iface - Unregister an interface from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * Returns: 0 on success, -1 on failure
+ *
+ * Unregisters the interface with dbus
+ */
+int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dbus_priv *ctrl_iface;
+	DBusConnection *con;
+
+	/* Do nothing if the control interface is not turned on */
+	if (wpa_s == NULL || wpa_s->global == NULL)
+		return 0;
+	ctrl_iface = wpa_s->global->dbus;
+	if (ctrl_iface == NULL || wpa_s->dbus_path == NULL)
+		return 0;
+
+	con = ctrl_iface->con;
+	if (!dbus_connection_unregister_object_path(con, wpa_s->dbus_path))
+		return -1;
+
+	os_free(wpa_s->dbus_path);
+	wpa_s->dbus_path = NULL;
+
+	return 0;
+}
+
+
+/**
+ * wpa_supplicant_get_iface_by_dbus_path - Get a new network interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @path: Pointer to a dbus object path representing an interface
+ * Returns: Pointer to the interface or %NULL if not found
+ */
+struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path(
+	struct wpa_global *global, const char *path)
+{
+	struct wpa_supplicant *wpa_s;
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (wpa_s->dbus_path && strcmp(wpa_s->dbus_path, path) == 0)
+			return wpa_s;
+	}
+	return NULL;
+}
diff --git a/hostap/wpa_supplicant/dbus/dbus_old.h b/hostap/wpa_supplicant/dbus/dbus_old.h
new file mode 100644
index 0000000..451a9f8
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_old.h
@@ -0,0 +1,142 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CTRL_IFACE_DBUS_H
+#define CTRL_IFACE_DBUS_H
+
+struct wps_credential;
+
+#ifdef CONFIG_CTRL_IFACE_DBUS
+
+#define WPAS_DBUS_OBJECT_PATH_MAX 150
+
+#define WPAS_DBUS_SERVICE	"fi.epitest.hostap.WPASupplicant"
+#define WPAS_DBUS_PATH		"/fi/epitest/hostap/WPASupplicant"
+#define WPAS_DBUS_INTERFACE	"fi.epitest.hostap.WPASupplicant"
+
+#define WPAS_DBUS_PATH_INTERFACES	WPAS_DBUS_PATH "/Interfaces"
+#define WPAS_DBUS_IFACE_INTERFACE	WPAS_DBUS_INTERFACE ".Interface"
+
+#define WPAS_DBUS_NETWORKS_PART "Networks"
+#define WPAS_DBUS_IFACE_NETWORK	WPAS_DBUS_INTERFACE ".Network"
+
+#define WPAS_DBUS_BSSIDS_PART	"BSSIDs"
+#define WPAS_DBUS_IFACE_BSSID	WPAS_DBUS_INTERFACE ".BSSID"
+
+
+/* Errors */
+#define WPAS_ERROR_INVALID_NETWORK \
+	WPAS_DBUS_IFACE_INTERFACE ".InvalidNetwork"
+#define WPAS_ERROR_INVALID_BSSID \
+	WPAS_DBUS_IFACE_INTERFACE ".InvalidBSSID"
+
+#define WPAS_ERROR_INVALID_OPTS \
+	WPAS_DBUS_INTERFACE ".InvalidOptions"
+#define WPAS_ERROR_INVALID_IFACE \
+	WPAS_DBUS_INTERFACE ".InvalidInterface"
+
+#define WPAS_ERROR_ADD_ERROR \
+	WPAS_DBUS_INTERFACE ".AddError"
+#define WPAS_ERROR_EXISTS_ERROR \
+	WPAS_DBUS_INTERFACE ".ExistsError"
+#define WPAS_ERROR_REMOVE_ERROR \
+	WPAS_DBUS_INTERFACE ".RemoveError"
+
+#define WPAS_ERROR_SCAN_ERROR \
+	WPAS_DBUS_IFACE_INTERFACE ".ScanError"
+#define WPAS_ERROR_ADD_NETWORK_ERROR \
+	WPAS_DBUS_IFACE_INTERFACE ".AddNetworkError"
+#define WPAS_ERROR_INTERNAL_ERROR \
+	WPAS_DBUS_IFACE_INTERFACE ".InternalError"
+#define WPAS_ERROR_REMOVE_NETWORK_ERROR \
+	WPAS_DBUS_IFACE_INTERFACE ".RemoveNetworkError"
+
+#define WPAS_ERROR_WPS_PBC_ERROR \
+	WPAS_DBUS_IFACE_INTERFACE ".WpsPbcError"
+#define WPAS_ERROR_WPS_PIN_ERROR \
+	WPAS_DBUS_IFACE_INTERFACE ".WpsPinError"
+#define WPAS_ERROR_WPS_REG_ERROR \
+	WPAS_DBUS_IFACE_INTERFACE ".WpsRegError"
+
+#define WPAS_DBUS_BSSID_FORMAT "%02x%02x%02x%02x%02x%02x"
+
+struct wpa_global;
+struct wpa_supplicant;
+
+int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface);
+void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s,
+					     enum wpa_states new_state,
+					     enum wpa_states old_state);
+void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
+					 const struct wps_credential *cred);
+void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
+					      int depth, const char *subject,
+					      const char *cert_hash,
+					      const struct wpabuf *cert);
+
+char * wpas_dbus_decompose_object_path(const char *path, char **network,
+				       char **bssid);
+
+int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s);
+int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s);
+
+
+/* Methods internal to the dbus control interface */
+struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path(
+	struct wpa_global *global, const char *path);
+
+#else /* CONFIG_CTRL_IFACE_DBUS */
+
+static inline void
+wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void
+wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void
+wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s,
+					enum wpa_states new_state,
+					enum wpa_states old_state)
+{
+}
+
+static inline void
+wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
+				    const struct wps_credential *cred)
+{
+}
+
+static inline void
+wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
+					      int depth, const char *subject,
+					      const char *cert_hash,
+					      const struct wpabuf *cert)
+{
+}
+
+static inline int
+wpas_dbus_register_iface(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+
+static inline int
+wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+
+#endif /* CONFIG_CTRL_IFACE_DBUS */
+
+#endif /* CTRL_IFACE_DBUS_H */
diff --git a/hostap/wpa_supplicant/dbus/dbus_old_handlers.c b/hostap/wpa_supplicant/dbus/dbus_old_handlers.c
new file mode 100644
index 0000000..e8f62ef
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_old_handlers.c
@@ -0,0 +1,1404 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <dbus/dbus.h>
+
+#include "common.h"
+#include "eap_peer/eap_methods.h"
+#include "common/ieee802_11_defs.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "../config.h"
+#include "../wpa_supplicant_i.h"
+#include "../driver_i.h"
+#include "../notify.h"
+#include "../wpas_glue.h"
+#include "../bss.h"
+#include "../scan.h"
+#include "dbus_old.h"
+#include "dbus_old_handlers.h"
+#include "dbus_dict_helpers.h"
+
+/**
+ * wpas_dbus_new_invalid_opts_error - Return a new invalid options error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an invalid options error
+ */
+DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message,
+					       const char *arg)
+{
+	DBusMessage *reply;
+
+	reply = dbus_message_new_error(
+		message, WPAS_ERROR_INVALID_OPTS,
+		"Did not receive correct message arguments.");
+	if (arg != NULL)
+		dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg,
+					 DBUS_TYPE_INVALID);
+
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_new_success_reply - Return a new success reply message
+ * @message: Pointer to incoming dbus message this reply refers to
+ * Returns: a dbus message containing a single UINT32 that indicates
+ *          success (ie, a value of 1)
+ *
+ * Convenience function to create and return a success reply message
+ */
+DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message)
+{
+	DBusMessage *reply;
+	unsigned int success = 1;
+
+	reply = dbus_message_new_method_return(message);
+	dbus_message_append_args(reply, DBUS_TYPE_UINT32, &success,
+				 DBUS_TYPE_INVALID);
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_global_add_interface - Request registration of a network interface
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: The object path of the new interface object,
+ *          or a dbus error message with more information
+ *
+ * Handler function for "addInterface" method call. Handles requests
+ * by dbus clients to register a network interface that wpa_supplicant
+ * will manage.
+ */
+DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message,
+					     struct wpa_global *global)
+{
+	char *ifname = NULL;
+	char *driver = NULL;
+	char *driver_param = NULL;
+	char *confname = NULL;
+	char *bridge_ifname = NULL;
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter;
+
+	dbus_message_iter_init(message, &iter);
+
+	/* First argument: interface name (DBUS_TYPE_STRING)
+	 *    Required; must be non-zero length
+	 */
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		goto error;
+	dbus_message_iter_get_basic(&iter, &ifname);
+	if (!os_strlen(ifname))
+		goto error;
+
+	/* Second argument: dict of options */
+	if (dbus_message_iter_next(&iter)) {
+		DBusMessageIter iter_dict;
+		struct wpa_dbus_dict_entry entry;
+
+		if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+			goto error;
+		while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+			if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+				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)
+					goto error;
+			} else {
+				wpa_dbus_dict_entry_clear(&entry);
+				goto error;
+			}
+		}
+	}
+
+	/*
+	 * Try to get the wpa_supplicant record for this iface, return
+	 * an error if we already control it.
+	 */
+	if (wpa_supplicant_get_iface(global, ifname) != NULL) {
+		reply = dbus_message_new_error(
+			message, WPAS_ERROR_EXISTS_ERROR,
+			"wpa_supplicant already controls this interface.");
+	} else {
+		struct wpa_supplicant *wpa_s;
+		struct wpa_interface iface;
+
+		os_memset(&iface, 0, sizeof(iface));
+		iface.ifname = ifname;
+		iface.driver = driver;
+		iface.driver_param = driver_param;
+		iface.confname = confname;
+		iface.bridge_ifname = bridge_ifname;
+		/* Otherwise, have wpa_supplicant attach to it. */
+		wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
+		if (wpa_s && wpa_s->dbus_path) {
+			const char *path = wpa_s->dbus_path;
+
+			reply = dbus_message_new_method_return(message);
+			dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH,
+						 &path, DBUS_TYPE_INVALID);
+		} else {
+			reply = dbus_message_new_error(
+				message, WPAS_ERROR_ADD_ERROR,
+				"wpa_supplicant couldn't grab this interface.");
+		}
+	}
+
+out:
+	os_free(driver);
+	os_free(driver_param);
+	os_free(confname);
+	os_free(bridge_ifname);
+	return reply;
+
+error:
+	reply = wpas_dbus_new_invalid_opts_error(message, NULL);
+	goto out;
+}
+
+
+/**
+ * wpas_dbus_global_remove_interface - Request deregistration of an interface
+ * @message: Pointer to incoming dbus message
+ * @global: wpa_supplicant global data structure
+ * Returns: a dbus message containing a UINT32 indicating success (1) or
+ *          failure (0), or returns a dbus error message with more information
+ *
+ * Handler function for "removeInterface" method call.  Handles requests
+ * by dbus clients to deregister a network interface that wpa_supplicant
+ * currently manages.
+ */
+DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message,
+						struct wpa_global *global)
+{
+	struct wpa_supplicant *wpa_s;
+	char *path;
+	DBusMessage *reply = NULL;
+
+	if (!dbus_message_get_args(message, NULL,
+				   DBUS_TYPE_OBJECT_PATH, &path,
+				   DBUS_TYPE_INVALID)) {
+		reply = wpas_dbus_new_invalid_opts_error(message, NULL);
+		goto out;
+	}
+
+	wpa_s = wpa_supplicant_get_iface_by_dbus_path(global, path);
+	if (wpa_s == NULL) {
+		reply = wpas_dbus_new_invalid_iface_error(message);
+		goto out;
+	}
+
+	if (!wpa_supplicant_remove_iface(global, wpa_s, 0)) {
+		reply = wpas_dbus_new_success_reply(message);
+	} else {
+		reply = dbus_message_new_error(
+			message, WPAS_ERROR_REMOVE_ERROR,
+			"wpa_supplicant couldn't remove this interface.");
+	}
+
+out:
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_global_get_interface - Get the object path for an interface name
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: The object path of the interface object,
+ *          or a dbus error message with more information
+ *
+ * Handler function for "getInterface" method call. Handles requests
+ * by dbus clients for the object path of an specific network interface.
+ */
+DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message,
+					     struct wpa_global *global)
+{
+	DBusMessage *reply = NULL;
+	const char *ifname;
+	const char *path;
+	struct wpa_supplicant *wpa_s;
+
+	if (!dbus_message_get_args(message, NULL,
+				   DBUS_TYPE_STRING, &ifname,
+				   DBUS_TYPE_INVALID)) {
+		reply = wpas_dbus_new_invalid_opts_error(message, NULL);
+		goto out;
+	}
+
+	wpa_s = wpa_supplicant_get_iface(global, ifname);
+	if (wpa_s == NULL || !wpa_s->dbus_path) {
+		reply = wpas_dbus_new_invalid_iface_error(message);
+		goto out;
+	}
+
+	path = wpa_s->dbus_path;
+	reply = dbus_message_new_method_return(message);
+	dbus_message_append_args(reply,
+				 DBUS_TYPE_OBJECT_PATH, &path,
+				 DBUS_TYPE_INVALID);
+
+out:
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_global_set_debugparams- Set the debug params
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: a dbus message containing a UINT32 indicating success (1) or
+ *          failure (0), or returns a dbus error message with more information
+ *
+ * Handler function for "setDebugParams" method call. Handles requests
+ * by dbus clients for the object path of an specific network interface.
+ */
+DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message,
+					       struct wpa_global *global)
+{
+	DBusMessage *reply = NULL;
+	int debug_level;
+	dbus_bool_t debug_timestamp;
+	dbus_bool_t debug_show_keys;
+
+	if (!dbus_message_get_args(message, NULL,
+				   DBUS_TYPE_INT32, &debug_level,
+				   DBUS_TYPE_BOOLEAN, &debug_timestamp,
+				   DBUS_TYPE_BOOLEAN, &debug_show_keys,
+				   DBUS_TYPE_INVALID)) {
+		return wpas_dbus_new_invalid_opts_error(message, NULL);
+	}
+
+	if (wpa_supplicant_set_debug_params(global, debug_level,
+					    debug_timestamp ? 1 : 0,
+					    debug_show_keys ? 1 : 0)) {
+		return wpas_dbus_new_invalid_opts_error(message, NULL);
+	}
+
+	reply = wpas_dbus_new_success_reply(message);
+
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_scan - Request a wireless scan on an interface
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: a dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "scan" method call of a network device. Requests
+ * that wpa_supplicant perform a wireless scan as soon as possible
+ * on a particular wireless interface.
+ */
+DBusMessage * wpas_dbus_iface_scan(DBusMessage *message,
+				   struct wpa_supplicant *wpa_s)
+{
+	wpa_s->scan_req = MANUAL_SCAN_REQ;
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+	return wpas_dbus_new_success_reply(message);
+}
+
+
+/**
+ * wpas_dbus_iface_scan_results - Get the results of a recent scan request
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: a dbus message containing a dbus array of objects paths, or returns
+ *          a dbus error message if not scan results could be found
+ *
+ * Handler function for "scanResults" method call of a network device. Returns
+ * a dbus message containing the object paths of wireless networks found.
+ */
+DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter sub_iter;
+	struct wpa_bss *bss;
+
+	if (!wpa_s->dbus_path)
+		return dbus_message_new_error(message,
+					      WPAS_ERROR_INTERNAL_ERROR,
+					      "no D-Bus interface available");
+
+	/* Create and initialize the return message */
+	reply = dbus_message_new_method_return(message);
+	dbus_message_iter_init_append(reply, &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) {
+		char path_buf[WPAS_DBUS_OBJECT_PATH_MAX];
+		char *path = path_buf;
+
+		/* Construct the object path for this network.  Note that ':'
+		 * is not a valid character in dbus object paths.
+		 */
+		os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+			    "%s/" WPAS_DBUS_BSSIDS_PART "/"
+			    WPAS_DBUS_BSSID_FORMAT,
+			    wpa_s->dbus_path, MAC2STR(bss->bssid));
+		if (!dbus_message_iter_append_basic(&sub_iter,
+						    DBUS_TYPE_OBJECT_PATH,
+						    &path))
+			goto error;
+	}
+
+	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");
+}
+
+
+/**
+ * wpas_dbus_bssid_properties - Return the properties of a scanned network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @res: wpa_supplicant scan result for which to get properties
+ * Returns: a dbus message containing the properties for the requested network
+ *
+ * Handler function for "properties" method call of a scanned network.
+ * Returns a dbus message containing the the properties.
+ */
+DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s,
+					 struct wpa_bss *bss)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter, iter_dict;
+	const u8 *wpa_ie, *rsn_ie, *wps_ie;
+
+	/* Dump the properties into a dbus message */
+	reply = dbus_message_new_method_return(message);
+
+	wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+	rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	wps_ie = wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE);
+
+	dbus_message_iter_init_append(reply, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &iter_dict) ||
+	    !wpa_dbus_dict_append_byte_array(&iter_dict, "bssid",
+					     (const char *) bss->bssid,
+					     ETH_ALEN) ||
+	    !wpa_dbus_dict_append_byte_array(&iter_dict, "ssid",
+					     (const char *) bss->ssid,
+					     bss->ssid_len) ||
+	    (wpa_ie &&
+	     !wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie",
+					      (const char *) wpa_ie,
+					      wpa_ie[1] + 2)) ||
+	    (rsn_ie &&
+	     !wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie",
+					      (const char *) rsn_ie,
+					      rsn_ie[1] + 2)) ||
+	    (wps_ie &&
+	     !wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie",
+					     (const char *) wps_ie,
+					      wps_ie[1] + 2)) ||
+	    (bss->freq &&
+	     !wpa_dbus_dict_append_int32(&iter_dict, "frequency", bss->freq)) ||
+	    !wpa_dbus_dict_append_uint16(&iter_dict, "capabilities",
+					 bss->caps) ||
+	    (!(bss->flags & WPA_BSS_QUAL_INVALID) &&
+	     !wpa_dbus_dict_append_int32(&iter_dict, "quality", bss->qual)) ||
+	    (!(bss->flags & WPA_BSS_NOISE_INVALID) &&
+	     !wpa_dbus_dict_append_int32(&iter_dict, "noise", bss->noise)) ||
+	    (!(bss->flags & WPA_BSS_LEVEL_INVALID) &&
+	     !wpa_dbus_dict_append_int32(&iter_dict, "level", bss->level)) ||
+	    !wpa_dbus_dict_append_int32(&iter_dict, "maxrate",
+					wpa_bss_get_max_rate(bss) * 500000) ||
+	    !wpa_dbus_dict_close_write(&iter, &iter_dict)) {
+		if (reply)
+			dbus_message_unref(reply);
+		reply = dbus_message_new_error(
+			message, WPAS_ERROR_INTERNAL_ERROR,
+			"an internal error occurred returning BSSID properties.");
+	}
+
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_capabilities - Return interface capabilities
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a dict of strings
+ *
+ * Handler function for "capabilities" method call of an interface.
+ */
+DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	struct wpa_driver_capa capa;
+	int res;
+	DBusMessageIter iter, iter_dict;
+	char **eap_methods;
+	size_t num_items;
+	dbus_bool_t strict = FALSE;
+	DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
+
+	if (!dbus_message_get_args(message, NULL,
+				   DBUS_TYPE_BOOLEAN, &strict,
+				   DBUS_TYPE_INVALID))
+		strict = FALSE;
+
+	reply = dbus_message_new_method_return(message);
+
+	dbus_message_iter_init_append(reply, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
+		goto error;
+
+	/* EAP methods */
+	eap_methods = eap_get_names_as_string_array(&num_items);
+	if (eap_methods) {
+		dbus_bool_t success;
+		size_t i = 0;
+
+		success = wpa_dbus_dict_append_string_array(
+			&iter_dict, "eap", (const char **) eap_methods,
+			num_items);
+
+		/* free returned method array */
+		while (eap_methods[i])
+			os_free(eap_methods[i++]);
+		os_free(eap_methods);
+
+		if (!success)
+			goto error;
+	}
+
+	res = wpa_drv_get_capa(wpa_s, &capa);
+
+	/***** pairwise cipher */
+	if (res < 0) {
+		if (!strict) {
+			const char *args[] = {"CCMP", "TKIP", "NONE"};
+
+			if (!wpa_dbus_dict_append_string_array(
+				    &iter_dict, "pairwise", args,
+				    ARRAY_SIZE(args)))
+				goto error;
+		}
+	} else {
+		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "pairwise",
+						      &iter_dict_entry,
+						      &iter_dict_val,
+						      &iter_array) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "CCMP")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "TKIP")) ||
+		    ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "NONE")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
+						    &iter_dict_entry,
+						    &iter_dict_val,
+						    &iter_array))
+			goto error;
+	}
+
+	/***** group cipher */
+	if (res < 0) {
+		if (!strict) {
+			const char *args[] = {
+				"CCMP", "TKIP", "WEP104", "WEP40"
+			};
+
+			if (!wpa_dbus_dict_append_string_array(
+				    &iter_dict, "group", args,
+				    ARRAY_SIZE(args)))
+				goto error;
+		}
+	} else {
+		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "group",
+						      &iter_dict_entry,
+						      &iter_dict_val,
+						      &iter_array))
+			goto error;
+
+		if (((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "CCMP")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "TKIP")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "WEP104")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "WEP40")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
+						    &iter_dict_entry,
+						    &iter_dict_val,
+						    &iter_array))
+			goto error;
+	}
+
+	/***** key management */
+	if (res < 0) {
+		if (!strict) {
+			const char *args[] = {
+				"WPA-PSK", "WPA-EAP", "IEEE8021X", "WPA-NONE",
+				"NONE"
+			};
+			if (!wpa_dbus_dict_append_string_array(
+				    &iter_dict, "key_mgmt", args,
+				    ARRAY_SIZE(args)))
+				goto error;
+		}
+	} else {
+		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "key_mgmt",
+						      &iter_dict_entry,
+						      &iter_dict_val,
+						      &iter_array) ||
+		    !wpa_dbus_dict_string_array_add_element(&iter_array,
+							    "NONE") ||
+		    !wpa_dbus_dict_string_array_add_element(&iter_array,
+							    "IEEE8021X") ||
+		    ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+				       WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "WPA-EAP")) ||
+		    ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+				       WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "WPA-PSK")) ||
+		    ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "WPA-NONE")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
+						    &iter_dict_entry,
+						    &iter_dict_val,
+						    &iter_array))
+			goto error;
+	}
+
+	/***** WPA protocol */
+	if (res < 0) {
+		if (!strict) {
+			const char *args[] = { "RSN", "WPA" };
+
+			if (!wpa_dbus_dict_append_string_array(
+				    &iter_dict, "proto", args,
+				    ARRAY_SIZE(args)))
+				goto error;
+		}
+	} else {
+		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "proto",
+						      &iter_dict_entry,
+						      &iter_dict_val,
+						      &iter_array) ||
+		    ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+				       WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "RSN")) ||
+		    ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+				       WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "WPA")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
+						    &iter_dict_entry,
+						    &iter_dict_val,
+						    &iter_array))
+			goto error;
+	}
+
+	/***** auth alg */
+	if (res < 0) {
+		if (!strict) {
+			const char *args[] = { "OPEN", "SHARED", "LEAP" };
+
+			if (!wpa_dbus_dict_append_string_array(
+				    &iter_dict, "auth_alg", args,
+				    ARRAY_SIZE(args)))
+				goto error;
+		}
+	} else {
+		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "auth_alg",
+						      &iter_dict_entry,
+						      &iter_dict_val,
+						      &iter_array) ||
+		    ((capa.auth & WPA_DRIVER_AUTH_OPEN) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "OPEN")) ||
+		    ((capa.auth & WPA_DRIVER_AUTH_SHARED) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "SHARED")) ||
+		    ((capa.auth & WPA_DRIVER_AUTH_LEAP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "LEAP")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
+						    &iter_dict_entry,
+						    &iter_dict_val,
+						    &iter_array))
+			goto error;
+	}
+
+	if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+		goto error;
+
+	return reply;
+
+error:
+	if (reply)
+		dbus_message_unref(reply);
+	return dbus_message_new_error(
+		message, WPAS_ERROR_INTERNAL_ERROR,
+		"an internal error occurred returning interface capabilities.");
+}
+
+
+/**
+ * wpas_dbus_iface_add_network - Add a new configured network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing the object path of the new network
+ *
+ * Handler function for "addNetwork" method call of a network interface.
+ */
+DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message,
+					  struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	struct wpa_ssid *ssid = NULL;
+	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
+
+	if (wpa_s->dbus_path)
+		ssid = wpa_config_add_network(wpa_s->conf);
+	if (ssid == NULL) {
+		reply = dbus_message_new_error(
+			message, WPAS_ERROR_ADD_NETWORK_ERROR,
+			"wpa_supplicant could not add a network on this interface.");
+		goto out;
+	}
+	wpas_notify_network_added(wpa_s, ssid);
+	ssid->disabled = 1;
+	wpa_config_set_network_defaults(ssid);
+
+	/* Construct the object path for this network. */
+	os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NETWORKS_PART "/%d",
+		    wpa_s->dbus_path, ssid->id);
+
+	reply = dbus_message_new_method_return(message);
+	dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH,
+				 &path, DBUS_TYPE_INVALID);
+
+out:
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_remove_network - Remove a configured network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "removeNetwork" method call of a network interface.
+ */
+DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message,
+					     struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	const char *op;
+	char *iface = NULL, *net_id = NULL;
+	int id;
+	struct wpa_ssid *ssid;
+
+	if (!dbus_message_get_args(message, NULL,
+				   DBUS_TYPE_OBJECT_PATH, &op,
+				   DBUS_TYPE_INVALID)) {
+		reply = wpas_dbus_new_invalid_opts_error(message, NULL);
+		goto out;
+	}
+
+	/* Extract the network ID */
+	iface = wpas_dbus_decompose_object_path(op, &net_id, NULL);
+	if (iface == NULL || net_id == NULL) {
+		reply = wpas_dbus_new_invalid_network_error(message);
+		goto out;
+	}
+
+	/* Ensure the network is actually a child of this interface */
+	if (!wpa_s->dbus_path || os_strcmp(iface, wpa_s->dbus_path) != 0) {
+		reply = wpas_dbus_new_invalid_network_error(message);
+		goto out;
+	}
+
+	id = strtoul(net_id, NULL, 10);
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid == NULL) {
+		reply = wpas_dbus_new_invalid_network_error(message);
+		goto out;
+	}
+
+	wpas_notify_network_removed(wpa_s, ssid);
+
+	if (ssid == wpa_s->current_ssid)
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+
+	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+		reply = dbus_message_new_error(
+			message, WPAS_ERROR_REMOVE_NETWORK_ERROR,
+			"error removing the specified on this interface.");
+		goto out;
+	}
+
+	reply = wpas_dbus_new_success_reply(message);
+
+out:
+	os_free(iface);
+	os_free(net_id);
+	return reply;
+}
+
+
+static const char * const dont_quote[] = {
+	"key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
+	"opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path",
+	"bssid", "scan_freq", "freq_list", NULL
+};
+
+
+static dbus_bool_t should_quote_opt(const char *key)
+{
+	int i = 0;
+
+	while (dont_quote[i] != NULL) {
+		if (os_strcmp(key, dont_quote[i]) == 0)
+			return FALSE;
+		i++;
+	}
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_iface_set_network - Set options for a configured network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network
+ * Returns: a dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "set" method call of a configured network.
+ */
+DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message,
+					  struct wpa_supplicant *wpa_s,
+					  struct wpa_ssid *ssid)
+{
+	DBusMessage *reply = NULL;
+	struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
+	DBusMessageIter	iter, iter_dict;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) {
+		reply = wpas_dbus_new_invalid_opts_error(message, NULL);
+		goto out;
+	}
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		char *value = NULL;
+		size_t size = 50;
+		int ret;
+
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
+			reply = wpas_dbus_new_invalid_opts_error(message,
+								 NULL);
+			goto out;
+		}
+
+		/* Type conversions, since wpa_supplicant wants strings */
+		if (entry.type == DBUS_TYPE_ARRAY &&
+		    entry.array_type == DBUS_TYPE_BYTE) {
+			if (entry.array_len <= 0)
+				goto error;
+
+			size = entry.array_len * 2 + 1;
+			value = os_zalloc(size);
+			if (value == NULL)
+				goto error;
+			ret = wpa_snprintf_hex(value, size,
+					       (u8 *) entry.bytearray_value,
+					       entry.array_len);
+			if (ret <= 0)
+				goto error;
+		} else if (entry.type == DBUS_TYPE_STRING) {
+			if (should_quote_opt(entry.key)) {
+				size = os_strlen(entry.str_value);
+				/* Zero-length option check */
+				if (size == 0)
+					goto error;
+				size += 3;  /* For quotes and terminator */
+				value = os_zalloc(size);
+				if (value == NULL)
+					goto error;
+				ret = os_snprintf(value, size, "\"%s\"",
+						  entry.str_value);
+				if (os_snprintf_error(size, ret))
+					goto error;
+			} else {
+				value = os_strdup(entry.str_value);
+				if (value == NULL)
+					goto error;
+			}
+		} else if (entry.type == DBUS_TYPE_UINT32) {
+			value = os_zalloc(size);
+			if (value == NULL)
+				goto error;
+			ret = os_snprintf(value, size, "%u",
+					  entry.uint32_value);
+			if (os_snprintf_error(size, ret))
+				goto error;
+		} else if (entry.type == DBUS_TYPE_INT32) {
+			value = os_zalloc(size);
+			if (value == NULL)
+				goto error;
+			ret = os_snprintf(value, size, "%d",
+					  entry.int32_value);
+			if (os_snprintf_error(size, ret))
+				goto error;
+		} else
+			goto error;
+
+		if (wpa_config_set(ssid, entry.key, value, 0) < 0)
+			goto error;
+
+		if ((os_strcmp(entry.key, "psk") == 0 &&
+		     value[0] == '"' && ssid->ssid_len) ||
+		    (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase))
+			wpa_config_update_psk(ssid);
+		else if (os_strcmp(entry.key, "priority") == 0)
+			wpa_config_update_prio_list(wpa_s->conf);
+
+		os_free(value);
+		wpa_dbus_dict_entry_clear(&entry);
+		continue;
+
+	error:
+		os_free(value);
+		reply = wpas_dbus_new_invalid_opts_error(message, entry.key);
+		wpa_dbus_dict_entry_clear(&entry);
+		break;
+	}
+
+	if (!reply)
+		reply = wpas_dbus_new_success_reply(message);
+
+out:
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_enable_network - Mark a configured network as enabled
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "enable" method call of a configured network.
+ */
+DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message,
+					     struct wpa_supplicant *wpa_s,
+					     struct wpa_ssid *ssid)
+{
+	wpa_supplicant_enable_network(wpa_s, ssid);
+	return wpas_dbus_new_success_reply(message);
+}
+
+
+/**
+ * wpas_dbus_iface_disable_network - Mark a configured network as disabled
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "disable" method call of a configured network.
+ */
+DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message,
+					      struct wpa_supplicant *wpa_s,
+					      struct wpa_ssid *ssid)
+{
+	wpa_supplicant_disable_network(wpa_s, ssid);
+	return wpas_dbus_new_success_reply(message);
+}
+
+
+/**
+ * wpas_dbus_iface_select_network - Attempt association with a configured network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "selectNetwork" method call of network interface.
+ */
+DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message,
+					     struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	const char *op;
+	struct wpa_ssid *ssid;
+	char *iface_obj_path = NULL;
+	char *network = NULL;
+
+	if (os_strlen(dbus_message_get_signature(message)) == 0) {
+		/* Any network */
+		ssid = NULL;
+	} else {
+		int nid;
+
+		if (!dbus_message_get_args(message, NULL,
+					   DBUS_TYPE_OBJECT_PATH, &op,
+					   DBUS_TYPE_INVALID)) {
+			reply = wpas_dbus_new_invalid_opts_error(message,
+								 NULL);
+			goto out;
+		}
+
+		/* Extract the network number */
+		iface_obj_path = wpas_dbus_decompose_object_path(op,
+								 &network,
+								 NULL);
+		if (iface_obj_path == NULL) {
+			reply = wpas_dbus_new_invalid_iface_error(message);
+			goto out;
+		}
+		/* Ensure the object path really points to this interface */
+		if (network == NULL || !wpa_s->dbus_path ||
+		    os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) {
+			reply = wpas_dbus_new_invalid_network_error(message);
+			goto out;
+		}
+
+		nid = strtoul(network, NULL, 10);
+		if (errno == EINVAL) {
+			reply = wpas_dbus_new_invalid_network_error(message);
+			goto out;
+		}
+
+		ssid = wpa_config_get_network(wpa_s->conf, nid);
+		if (ssid == NULL) {
+			reply = wpas_dbus_new_invalid_network_error(message);
+			goto out;
+		}
+	}
+
+	/* Finally, associate with the network */
+	wpa_supplicant_select_network(wpa_s, ssid);
+
+	reply = wpas_dbus_new_success_reply(message);
+
+out:
+	os_free(iface_obj_path);
+	os_free(network);
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_disconnect - Terminate the current connection
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "disconnect" method call of network interface.
+ */
+DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s)
+{
+	wpa_s->disconnected = 1;
+	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+
+	return wpas_dbus_new_success_reply(message);
+}
+
+
+/**
+ * wpas_dbus_iface_set_ap_scan - Control roaming mode
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "setAPScan" method call.
+ */
+DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message,
+					  struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	dbus_uint32_t ap_scan = 1;
+
+	if (!dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &ap_scan,
+				   DBUS_TYPE_INVALID)) {
+		reply = wpas_dbus_new_invalid_opts_error(message, NULL);
+		goto out;
+	}
+
+	if (wpa_supplicant_set_ap_scan(wpa_s, ap_scan)) {
+		reply = wpas_dbus_new_invalid_opts_error(message, NULL);
+		goto out;
+	}
+
+	reply = wpas_dbus_new_success_reply(message);
+
+out:
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_set_smartcard_modules - Set smartcard related module paths
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "setSmartcardModules" method call.
+ */
+DBusMessage * wpas_dbus_iface_set_smartcard_modules(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter, iter_dict;
+	char *opensc_engine_path = NULL;
+	char *pkcs11_engine_path = NULL;
+	char *pkcs11_module_path = NULL;
+	struct wpa_dbus_dict_entry entry;
+
+	if (!dbus_message_iter_init(message, &iter))
+		goto error;
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto error;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			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);
+			wpa_dbus_dict_entry_clear(&entry);
+			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);
+			wpa_dbus_dict_entry_clear(&entry);
+			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);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (pkcs11_module_path == NULL)
+				goto error;
+		} else {
+			wpa_dbus_dict_entry_clear(&entry);
+			goto error;
+		}
+	}
+
+	os_free(wpa_s->conf->opensc_engine_path);
+	wpa_s->conf->opensc_engine_path = opensc_engine_path;
+	os_free(wpa_s->conf->pkcs11_engine_path);
+	wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path;
+	os_free(wpa_s->conf->pkcs11_module_path);
+	wpa_s->conf->pkcs11_module_path = pkcs11_module_path;
+
+	wpa_sm_set_eapol(wpa_s->wpa, NULL);
+	eapol_sm_deinit(wpa_s->eapol);
+	wpa_s->eapol = NULL;
+	wpa_supplicant_init_eapol(wpa_s);
+	wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
+
+	return wpas_dbus_new_success_reply(message);
+
+error:
+	os_free(opensc_engine_path);
+	os_free(pkcs11_engine_path);
+	os_free(pkcs11_module_path);
+	return wpas_dbus_new_invalid_opts_error(message, NULL);
+}
+
+
+/**
+ * wpas_dbus_iface_get_state - Get interface state
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a STRING representing the current
+ *          interface state
+ *
+ * Handler function for "state" method call.
+ */
+DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message,
+					struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	const char *str_state;
+
+	reply = dbus_message_new_method_return(message);
+	if (reply != NULL) {
+		str_state = wpa_supplicant_state_txt(wpa_s->wpa_state);
+		dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_state,
+					 DBUS_TYPE_INVALID);
+	}
+
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_get_scanning - Get interface scanning state
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing whether the interface is scanning
+ *
+ * Handler function for "scanning" method call.
+ */
+DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
+
+	reply = dbus_message_new_method_return(message);
+	if (reply != NULL) {
+		dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &scanning,
+					 DBUS_TYPE_INVALID);
+	} else {
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory to return scanning state");
+	}
+
+	return reply;
+}
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+
+/**
+ * wpas_dbus_iface_set_blobs - Store named binary blobs (ie, for certificates)
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Asks wpa_supplicant to internally store a one or more binary blobs.
+ */
+DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message,
+					struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
+	DBusMessageIter	iter, iter_dict;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		return wpas_dbus_new_invalid_opts_error(message, NULL);
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		struct wpa_config_blob *blob;
+
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
+			reply = wpas_dbus_new_invalid_opts_error(message,
+								 NULL);
+			break;
+		}
+
+		if (entry.type != DBUS_TYPE_ARRAY ||
+		    entry.array_type != DBUS_TYPE_BYTE) {
+			reply = wpas_dbus_new_invalid_opts_error(
+				message, "Byte array expected.");
+			break;
+		}
+
+		if ((entry.array_len <= 0) || (entry.array_len > 65536) ||
+		    !strlen(entry.key)) {
+			reply = wpas_dbus_new_invalid_opts_error(
+				message, "Invalid array size.");
+			break;
+		}
+
+		blob = os_zalloc(sizeof(*blob));
+		if (blob == NULL) {
+			reply = dbus_message_new_error(
+				message, WPAS_ERROR_ADD_ERROR,
+				"Not enough memory to add blob.");
+			break;
+		}
+		blob->data = os_zalloc(entry.array_len);
+		if (blob->data == NULL) {
+			reply = dbus_message_new_error(
+				message, WPAS_ERROR_ADD_ERROR,
+				"Not enough memory to add blob data.");
+			os_free(blob);
+			break;
+		}
+
+		blob->name = os_strdup(entry.key);
+		blob->len = entry.array_len;
+		os_memcpy(blob->data, (u8 *) entry.bytearray_value,
+				entry.array_len);
+		if (blob->name == NULL) {
+			wpa_config_free_blob(blob);
+			reply = dbus_message_new_error(
+				message, WPAS_ERROR_ADD_ERROR,
+				"Error adding blob.");
+			break;
+		}
+
+		/* Success */
+		if (!wpa_config_remove_blob(wpa_s->conf, blob->name))
+			wpas_notify_blob_removed(wpa_s, blob->name);
+		wpa_config_set_blob(wpa_s->conf, blob);
+		wpas_notify_blob_added(wpa_s, blob->name);
+
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+	wpa_dbus_dict_entry_clear(&entry);
+
+	return reply ? reply : wpas_dbus_new_success_reply(message);
+}
+
+
+/**
+ * wpas_dbus_iface_remove_blob - Remove named binary blobs
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Asks wpa_supplicant to remove one or more previously stored binary blobs.
+ */
+DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter, array;
+	char *err_msg = NULL;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+	    dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING)
+		return wpas_dbus_new_invalid_opts_error(message, NULL);
+
+	dbus_message_iter_recurse(&iter, &array);
+	while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
+		const char *name;
+
+		dbus_message_iter_get_basic(&array, &name);
+		if (!os_strlen(name))
+			err_msg = "Invalid blob name.";
+		else if (wpa_config_remove_blob(wpa_s->conf, name) != 0)
+			err_msg = "Error removing blob.";
+		else
+			wpas_notify_blob_removed(wpa_s, name);
+		dbus_message_iter_next(&array);
+	}
+
+	if (err_msg)
+		return dbus_message_new_error(message, WPAS_ERROR_REMOVE_ERROR,
+					      err_msg);
+
+	return wpas_dbus_new_success_reply(message);
+}
+
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+/**
+ * wpas_dbus_iface_flush - Clear BSS of old or all inactive entries
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: a dbus message containing a UINT32 indicating success (1) or
+ *          failure (0), or returns a dbus error message with more information
+ *
+ * Handler function for "flush" method call. Handles requests for an
+ * interface with an optional "age" parameter that specifies the minimum
+ * age of a BSS to be flushed.
+ */
+DBusMessage * wpas_dbus_iface_flush(DBusMessage *message,
+				    struct wpa_supplicant *wpa_s)
+{
+	int flush_age = 0;
+
+	if (os_strlen(dbus_message_get_signature(message)) != 0 &&
+	    !dbus_message_get_args(message, NULL,
+				   DBUS_TYPE_INT32, &flush_age,
+				   DBUS_TYPE_INVALID)) {
+		return wpas_dbus_new_invalid_opts_error(message, NULL);
+	}
+
+	if (flush_age == 0)
+		wpa_bss_flush(wpa_s);
+	else
+		wpa_bss_flush_by_age(wpa_s, flush_age);
+
+	return wpas_dbus_new_success_reply(message);
+}
diff --git a/hostap/wpa_supplicant/dbus/dbus_old_handlers.h b/hostap/wpa_supplicant/dbus/dbus_old_handlers.h
new file mode 100644
index 0000000..e60ad06
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_old_handlers.h
@@ -0,0 +1,101 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CTRL_IFACE_DBUS_HANDLERS_H
+#define CTRL_IFACE_DBUS_HANDLERS_H
+
+struct wpa_bss;
+
+DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message);
+DBusMessage * wpas_dbus_new_invalid_network_error(DBusMessage *message);
+
+DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message,
+					     struct wpa_global *global);
+
+DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message,
+						struct wpa_global *global);
+
+DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message,
+					     struct wpa_global *global);
+
+DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message,
+					       struct wpa_global *global);
+
+DBusMessage * wpas_dbus_iface_scan(DBusMessage *message,
+				   struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s,
+					 struct wpa_bss *bss);
+
+DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message,
+					  struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message,
+					     struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message,
+					  struct wpa_supplicant *wpa_s,
+					  struct wpa_ssid *ssid);
+
+DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message,
+					     struct wpa_supplicant *wpa_s,
+					     struct wpa_ssid *ssid);
+
+DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message,
+					      struct wpa_supplicant *wpa_s,
+					      struct wpa_ssid *ssid);
+
+DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message,
+					     struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message,
+					  struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_set_smartcard_modules(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message,
+					struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message,
+					struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message,
+				      struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message,
+				      struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message,
+				      struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_flush(DBusMessage *message,
+				    struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message);
+DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message,
+					       const char *arg);
+
+#endif /* CTRL_IFACE_DBUS_HANDLERS_H */
+
diff --git a/hostap/wpa_supplicant/dbus/dbus_old_handlers_wps.c b/hostap/wpa_supplicant/dbus/dbus_old_handlers_wps.c
new file mode 100644
index 0000000..5309a53
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/dbus_old_handlers_wps.c
@@ -0,0 +1,152 @@
+/*
+ * WPA Supplicant / dbus-based control interface (WPS)
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <dbus/dbus.h>
+
+#include "common.h"
+#include "../config.h"
+#include "../wpa_supplicant_i.h"
+#include "../wps_supplicant.h"
+#include "dbus_old.h"
+#include "dbus_old_handlers.h"
+
+/**
+ * wpas_dbus_iface_wps_pbc - Request credentials using WPS PBC method
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "wpsPbc" method call
+ */
+DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message,
+				      struct wpa_supplicant *wpa_s)
+{
+	char *arg_bssid = NULL;
+	u8 bssid[ETH_ALEN];
+	int ret = 0;
+
+	if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid,
+				   DBUS_TYPE_INVALID))
+		return wpas_dbus_new_invalid_opts_error(message, NULL);
+
+	if (os_strcmp(arg_bssid, "any") == 0)
+		ret = wpas_wps_start_pbc(wpa_s, NULL, 0);
+	else if (!hwaddr_aton(arg_bssid, bssid))
+		ret = wpas_wps_start_pbc(wpa_s, bssid, 0);
+	else {
+		return wpas_dbus_new_invalid_opts_error(message,
+							"Invalid BSSID");
+	}
+
+	if (ret < 0) {
+		return dbus_message_new_error(
+			message, WPAS_ERROR_WPS_PBC_ERROR,
+			"Could not start PBC negotiation");
+	}
+
+	return wpas_dbus_new_success_reply(message);
+}
+
+
+/**
+ * wpas_dbus_iface_wps_pin - Establish the PIN number of the enrollee
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "wpsPin" method call
+ */
+DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message,
+				      struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	char *arg_bssid;
+	char *pin = NULL;
+	u8 bssid[ETH_ALEN], *_bssid = NULL;
+	int ret = 0;
+	char npin[9];
+
+	if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid,
+				   DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID))
+		return wpas_dbus_new_invalid_opts_error(message, NULL);
+
+	if (os_strcmp(arg_bssid, "any") == 0)
+		_bssid = NULL;
+	else if (!hwaddr_aton(arg_bssid, bssid))
+		_bssid = bssid;
+	else {
+		return wpas_dbus_new_invalid_opts_error(message,
+							"Invalid BSSID");
+	}
+
+	if (os_strlen(pin) > 0)
+		ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0,
+					 DEV_PW_DEFAULT);
+	else
+		ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0,
+					 DEV_PW_DEFAULT);
+
+	if (ret < 0) {
+		return dbus_message_new_error(message,
+					      WPAS_ERROR_WPS_PIN_ERROR,
+					      "Could not init PIN");
+	}
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL)
+		return NULL;
+
+	if (ret > 0) {
+		os_snprintf(npin, sizeof(npin), "%08d", ret);
+		pin = npin;
+	}
+	dbus_message_append_args(reply, DBUS_TYPE_STRING, &pin,
+				 DBUS_TYPE_INVALID);
+	return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_wps_reg - Request credentials using the PIN of the AP
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "wpsReg" method call
+ */
+DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message,
+				      struct wpa_supplicant *wpa_s)
+{
+	char *arg_bssid;
+	char *pin = NULL;
+	u8 bssid[ETH_ALEN];
+	int ret = 0;
+
+	if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid,
+				   DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID))
+		return wpas_dbus_new_invalid_opts_error(message, NULL);
+
+	if (!hwaddr_aton(arg_bssid, bssid))
+		ret = wpas_wps_start_reg(wpa_s, bssid, pin, NULL);
+	else {
+		return wpas_dbus_new_invalid_opts_error(message,
+							"Invalid BSSID");
+	}
+
+	if (ret < 0) {
+		return dbus_message_new_error(message,
+					      WPAS_ERROR_WPS_REG_ERROR,
+					      "Could not request credentials");
+	}
+
+	return wpas_dbus_new_success_reply(message);
+}
diff --git a/hostap/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in b/hostap/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in
new file mode 100644
index 0000000..a75918f
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=fi.epitest.hostap.WPASupplicant
+Exec=@BINDIR@/wpa_supplicant -u
+User=root
+SystemdService=wpa_supplicant.service
diff --git a/hostap/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in b/hostap/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in
new file mode 100644
index 0000000..d97ff39
--- /dev/null
+++ b/hostap/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=fi.w1.wpa_supplicant1
+Exec=@BINDIR@/wpa_supplicant -u
+User=root
+SystemdService=wpa_supplicant.service
diff --git a/hostap/wpa_supplicant/defconfig b/hostap/wpa_supplicant/defconfig
new file mode 100644
index 0000000..44ddbc0
--- /dev/null
+++ b/hostap/wpa_supplicant/defconfig
@@ -0,0 +1,508 @@
+# Example wpa_supplicant build time configuration
+#
+# This file lists the configuration options that are used when building the
+# hostapd binary. All lines starting with # are ignored. Configuration option
+# lines must be commented out complete, if they are not to be included, i.e.,
+# just setting VARIABLE=n is not disabling that variable.
+#
+# This file is included in Makefile, so variables like CFLAGS and LIBS can also
+# be modified from here. In most cases, these lines should use += in order not
+# to override previous values of the variables.
+
+CFLAGS += $(OPENSSL_CFLAGS) $(NETLINK_CFLAGS) $(LINUX_CFLAGS)
+LDFLAGS += $(DBUS_LDFLAGS) $(OPENSSL_LDFLAGS) $(NETLINK_LDFLAGS)
+
+# Uncomment following two lines and fix the paths if you have installed OpenSSL
+# or GnuTLS in non-default location
+#CFLAGS += -I/usr/local/openssl/include
+#LIBS += -L/usr/local/openssl/lib
+
+# Some Red Hat versions seem to include kerberos header files from OpenSSL, but
+# the kerberos files are not in the default include path. Following line can be
+# used to fix build issues on such systems (krb5.h not found).
+#CFLAGS += -I/usr/include/kerberos
+
+# 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
+# replacement for WEXT and its use allows wpa_supplicant to properly control
+# the driver to improve existing functionality like roaming and to support new
+# functionality.
+CONFIG_DRIVER_WEXT=y
+
+# 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
+#LIBS += -L/usr/local/lib
+#LIBS_p += -L/usr/local/lib
+#LIBS_c += -L/usr/local/lib
+
+# Driver interface for Windows NDIS
+#CONFIG_DRIVER_NDIS=y
+#CFLAGS += -I/usr/include/w32api/ddk
+#LIBS += -L/usr/local/lib
+# For native build using mingw
+#CONFIG_NATIVE_WINDOWS=y
+# Additional directories for cross-compilation on Linux host for mingw target
+#CFLAGS += -I/opt/mingw/mingw32/include/ddk
+#LIBS += -L/opt/mingw/mingw32/lib
+#CC=mingw32-gcc
+# By default, driver_ndis uses WinPcap for low-level operations. This can be
+# replaced with the following option which replaces WinPcap calls with NDISUIO.
+# However, this requires that WZC is disabled (net stop wzcsvc) before starting
+# wpa_supplicant.
+# CONFIG_USE_NDISUIO=y
+
+# Driver interface for wired Ethernet drivers
+CONFIG_DRIVER_WIRED=y
+
+# Driver interface for the Broadcom RoboSwitch family
+#CONFIG_DRIVER_ROBOSWITCH=y
+
+# Driver interface for no driver (e.g., WPS ER only)
+#CONFIG_DRIVER_NONE=y
+
+# Solaris libraries
+#LIBS += -lsocket -ldlpi -lnsl
+#LIBS_c += -lsocket
+
+# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is
+# included)
+CONFIG_IEEE8021X_EAPOL=y
+
+# EAP-MD5
+CONFIG_EAP_MD5=y
+
+# EAP-MSCHAPv2
+CONFIG_EAP_MSCHAPV2=y
+
+# EAP-TLS
+CONFIG_EAP_TLS=y
+
+# EAL-PEAP
+CONFIG_EAP_PEAP=y
+
+# EAP-TTLS
+CONFIG_EAP_TTLS=y
+
+# EAP-FAST
+# 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
+CONFIG_EAP_GTC=y
+
+# EAP-OTP
+CONFIG_EAP_OTP=y
+
+# EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used)
+#CONFIG_EAP_SIM=y
+
+# EAP-PSK (experimental; this is _not_ needed for WPA-PSK)
+#CONFIG_EAP_PSK=y
+
+# EAP-pwd (secure authentication using only a password)
+#CONFIG_EAP_PWD=y
+
+# EAP-PAX
+#CONFIG_EAP_PAX=y
+
+# LEAP
+CONFIG_EAP_LEAP=y
+
+# EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used)
+#CONFIG_EAP_AKA=y
+
+# EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used).
+# This requires CONFIG_EAP_AKA to be enabled, too.
+#CONFIG_EAP_AKA_PRIME=y
+
+# Enable USIM simulator (Milenage) for EAP-AKA
+#CONFIG_USIM_SIMULATOR=y
+
+# EAP-SAKE
+#CONFIG_EAP_SAKE=y
+
+# EAP-GPSK
+#CONFIG_EAP_GPSK=y
+# Include support for optional SHA256 cipher suite in EAP-GPSK
+#CONFIG_EAP_GPSK_SHA256=y
+
+# EAP-TNC and related Trusted Network Connect support (experimental)
+#CONFIG_EAP_TNC=y
+
+# Wi-Fi Protected Setup (WPS)
+#CONFIG_WPS=y
+# Enable WPS external registrar functionality
+#CONFIG_WPS_ER=y
+# Disable credentials for an open network by default when acting as a WPS
+# registrar.
+#CONFIG_WPS_REG_DISABLE_OPEN=y
+# Enable WPS support with NFC config method
+#CONFIG_WPS_NFC=y
+
+# EAP-IKEv2
+#CONFIG_EAP_IKEV2=y
+
+# EAP-EKE
+#CONFIG_EAP_EKE=y
+
+# PKCS#12 (PFX) support (used to read private key and certificate file from
+# a file that usually has extension .p12 or .pfx)
+CONFIG_PKCS12=y
+
+# Smartcard support (i.e., private key on a smartcard), e.g., with openssl
+# engine.
+#CONFIG_SMARTCARD=y
+
+# PC/SC interface for smartcards (USIM, GSM SIM)
+# Enable this if EAP-SIM or EAP-AKA is included
+#CONFIG_PCSC=y
+
+# Support HT overrides (disable HT/HT40, mask MCS rates, etc.)
+#CONFIG_HT_OVERRIDES=y
+
+# Support VHT overrides (disable VHT, mask MCS rates, etc.)
+#CONFIG_VHT_OVERRIDES=y
+
+# Development testing
+#CONFIG_EAPOL_TEST=y
+
+# 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.
+CONFIG_CTRL_IFACE=y
+
+# Include support for GNU Readline and History Libraries in wpa_cli.
+# When building a wpa_cli binary for distribution, please note that these
+# libraries are licensed under GPL and as such, BSD license may not apply for
+# the resulting binary.
+#CONFIG_READLINE=y
+
+# Include internal line edit mode in wpa_cli. This can be used as a replacement
+# for GNU Readline to provide limited command line editing and history support.
+#CONFIG_WPA_CLI_EDIT=y
+
+# Remove debugging code that is printing out debug message to stdout.
+# This can be used to reduce the size of the wpa_supplicant considerably
+# if debugging code is not needed. The size reduction can be around 35%
+# (e.g., 90 kB).
+#CONFIG_NO_STDOUT_DEBUG=y
+
+# Remove WPA support, e.g., for wired-only IEEE 802.1X supplicant, to save
+# 35-50 kB in code size.
+#CONFIG_NO_WPA=y
+
+# Remove IEEE 802.11i/WPA-Personal ASCII passphrase support
+# This option can be used to reduce code size by removing support for
+# converting ASCII passphrases into PSK. If this functionality is removed, the
+# PSK can only be configured as the 64-octet hexstring (e.g., from
+# wpa_passphrase). This saves about 0.5 kB in code size.
+#CONFIG_NO_WPA_PASSPHRASE=y
+
+# Disable scan result processing (ap_mode=1) to save code size by about 1 kB.
+# This can be used if ap_scan=1 mode is never enabled.
+#CONFIG_NO_SCAN_PROCESSING=y
+
+# Select configuration backend:
+# file = text file (e.g., wpa_supplicant.conf; note: the configuration file
+#	path is given on command line, not here; this option is just used to
+#	select the backend that allows configuration files to be used)
+# winreg = Windows registry (see win_example.reg for an example)
+CONFIG_BACKEND=file
+
+# Remove configuration write functionality (i.e., to allow the configuration
+# file to be updated based on runtime configuration changes). The runtime
+# configuration can still be changed, the changes are just not going to be
+# persistent over restarts. This option can be used to reduce code size by
+# about 3.5 kB.
+#CONFIG_NO_CONFIG_WRITE=y
+
+# Remove support for configuration blobs to reduce code size by about 1.5 kB.
+#CONFIG_NO_CONFIG_BLOBS=y
+
+# Select program entry point implementation:
+# main = UNIX/POSIX like main() function (default)
+# main_winsvc = Windows service (read parameters from registry)
+# main_none = Very basic example (development use only)
+#CONFIG_MAIN=main
+
+# Select wrapper for operating system and C library specific functions
+# unix = UNIX/POSIX like systems (default)
+# win32 = Windows systems
+# none = Empty template
+#CONFIG_OS=unix
+
+# Select event loop implementation
+# eloop = select() loop (default)
+# eloop_win = Windows events and WaitForMultipleObject() loop
+#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
+# freebsd = FreeBSD libpcap
+# winpcap = WinPcap with receive thread
+# ndis = Windows NDISUIO (note: requires CONFIG_USE_NDISUIO=y)
+# none = Empty template
+#CONFIG_L2_PACKET=linux
+
+# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
+CONFIG_PEERKEY=y
+
+# IEEE 802.11w (management frame protection), also known as PMF
+# Driver support is also needed for IEEE 802.11w.
+#CONFIG_IEEE80211W=y
+
+# Select TLS implementation
+# openssl = OpenSSL (default)
+# gnutls = GnuTLS
+# internal = Internal TLSv1 implementation (experimental)
+# none = Empty template
+#CONFIG_TLS=openssl
+
+# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1)
+# can be enabled to get a stronger construction of messages when block ciphers
+# are used. It should be noted that some existing TLS v1.0 -based
+# implementation may not be compatible with TLS v1.1 message (ClientHello is
+# sent prior to negotiating which version will be used)
+#CONFIG_TLSV11=y
+
+# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2)
+# can be enabled to enable use of stronger crypto algorithms. It should be
+# noted that some existing TLS v1.0 -based implementation may not be compatible
+# with TLS v1.2 message (ClientHello is sent prior to negotiating which version
+# will be used)
+#CONFIG_TLSV12=y
+
+# If CONFIG_TLS=internal is used, additional library and include paths are
+# needed for LibTomMath. Alternatively, an integrated, minimal version of
+# LibTomMath can be used. See beginning of libtommath.c for details on benefits
+# and drawbacks of this option.
+#CONFIG_INTERNAL_LIBTOMMATH=y
+#ifndef CONFIG_INTERNAL_LIBTOMMATH
+#LTM_PATH=/usr/src/libtommath-0.39
+#CFLAGS += -I$(LTM_PATH)
+#LIBS += -L$(LTM_PATH)
+#LIBS_p += -L$(LTM_PATH)
+#endif
+# At the cost of about 4 kB of additional binary size, the internal LibTomMath
+# can be configured to include faster routines for exptmod, sqr, and div to
+# speed up DH and RSA calculation considerably
+#CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+
+# Include NDIS event processing through WMI into wpa_supplicant/wpasvc.
+# This is only for Windows builds and requires WMI-related header files and
+# WbemUuid.Lib from Platform SDK even when building with MinGW.
+#CONFIG_NDIS_EVENTS_INTEGRATED=y
+#PLATFORMSDKLIB="/opt/Program Files/Microsoft Platform SDK/Lib"
+
+# Add support for old DBus control interface
+# (fi.epitest.hostap.WPASupplicant)
+#CONFIG_CTRL_IFACE_DBUS=y
+
+# Add support for new DBus control interface
+# (fi.w1.hostap.wpa_supplicant1)
+CONFIG_CTRL_IFACE_DBUS_NEW=y
+
+# Add introspection support for new DBus control interface
+#CONFIG_CTRL_IFACE_DBUS_INTRO=y
+
+# Add support for loading EAP methods dynamically as shared libraries.
+# When this option is enabled, each EAP method can be either included
+# statically (CONFIG_EAP_<method>=y) or dynamically (CONFIG_EAP_<method>=dyn).
+# Dynamic EAP methods are build as shared objects (eap_*.so) and they need to
+# be loaded in the beginning of the wpa_supplicant configuration file
+# (see load_dynamic_eap parameter in the example file) before being used in
+# the network blocks.
+#
+# Note that some shared parts of EAP methods are included in the main program
+# and in order to be able to use dynamic EAP methods using these parts, the
+# main program must have been build with the EAP method enabled (=y or =dyn).
+# This means that EAP-TLS/PEAP/TTLS/FAST cannot be added as dynamic libraries
+# unless at least one of them was included in the main build to force inclusion
+# of the shared code. Similarly, at least one of EAP-SIM/AKA must be included
+# in the main build to be able to load these methods dynamically.
+#
+# Please also note that using dynamic libraries will increase the total binary
+# size. Thus, it may not be the best option for targets that have limited
+# amount of memory/flash.
+#CONFIG_DYNAMIC_EAP_METHODS=y
+
+# IEEE Std 802.11r-2008 (Fast BSS Transition)
+#CONFIG_IEEE80211R=y
+
+# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt)
+CONFIG_DEBUG_FILE=y
+
+# Send debug messages to syslog instead of stdout
+CONFIG_DEBUG_SYSLOG=y
+# Set syslog facility for debug messages
+#CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON
+
+# 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
+
+# Add support for writing debug log to Android logcat instead of standard
+# output
+#CONFIG_ANDROID_LOG=y
+
+# Enable privilege separation (see README 'Privilege separation' for details)
+#CONFIG_PRIVSEP=y
+
+# Enable mitigation against certain attacks against TKIP by delaying Michael
+# MIC error reports by a random amount of time between 0 and 60 seconds
+#CONFIG_DELAYED_MIC_ERROR_REPORT=y
+
+# Enable tracing code for developer debugging
+# This tracks use of memory allocations and other registrations and reports
+# incorrect use with a backtrace of call (or allocation) location.
+#CONFIG_WPA_TRACE=y
+# For BSD, uncomment these.
+#LIBS += -lexecinfo
+#LIBS_p += -lexecinfo
+#LIBS_c += -lexecinfo
+
+# Use libbfd to get more details for developer debugging
+# This enables use of libbfd to get more detailed symbols for the backtraces
+# generated by CONFIG_WPA_TRACE=y.
+#CONFIG_WPA_TRACE_BFD=y
+# For BSD, uncomment these.
+#LIBS += -lbfd -liberty -lz
+#LIBS_p += -lbfd -liberty -lz
+#LIBS_c += -lbfd -liberty -lz
+
+# wpa_supplicant depends on strong random number generation being available
+# from the operating system. os_get_random() function is used to fetch random
+# data when needed, e.g., for key generation. On Linux and BSD systems, this
+# works by reading /dev/urandom. It should be noted that the OS entropy pool
+# needs to be properly initialized before wpa_supplicant is started. This is
+# important especially on embedded devices that do not have a hardware random
+# number generator and may by default start up with minimal entropy available
+# for random number generation.
+#
+# As a safety net, wpa_supplicant is by default trying to internally collect
+# additional entropy for generating random data to mix in with the data fetched
+# from the OS. This by itself is not considered to be very strong, but it may
+# help in cases where the system pool is not initialized properly. However, it
+# is very strongly recommended that the system pool is initialized with enough
+# entropy either by using hardware assisted random number generator or by
+# storing state over device reboots.
+#
+# wpa_supplicant can be configured to maintain its own entropy store over
+# restarts to enhance random number generation. This is not perfect, but it is
+# much more secure than using the same sequence of random numbers after every
+# reboot. This can be enabled with -e<entropy file> command line option. The
+# specified file needs to be readable and writable by wpa_supplicant.
+#
+# If the os_get_random() is known to provide strong random data (e.g., on
+# Linux/BSD, the board in question is known to have reliable source of random
+# data from /dev/urandom), the internal wpa_supplicant random pool can be
+# disabled. This will save some in binary size and CPU use. However, this
+# should only be considered for builds that are known to be used on devices
+# that meet the requirements described above.
+#CONFIG_NO_RANDOM_POOL=y
+
+# 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
+
+# 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
+
+# Hotspot 2.0
+#CONFIG_HS20=y
+
+# Disable roaming in wpa_supplicant
+#CONFIG_NO_ROAMING=y
+
+# AP mode operations with wpa_supplicant
+# This can be used for controlling AP mode operations with wpa_supplicant. It
+# should be noted that this is mainly aimed at simple cases like
+# WPA2-Personal while more complex configurations like WPA2-Enterprise with an
+# external RADIUS server can be supported with hostapd.
+#CONFIG_AP=y
+
+# P2P (Wi-Fi Direct)
+# This can be used to enable P2P support in wpa_supplicant. See README-P2P for
+# more information on P2P operations.
+#CONFIG_P2P=y
+
+# 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.
+#
+# Enabling directly a module will enable autoscan support.
+# For exponential module:
+#CONFIG_AUTOSCAN_EXPONENTIAL=y
+# For periodic module:
+#CONFIG_AUTOSCAN_PERIODIC=y
+
+# Password (and passphrase, etc.) backend for external storage
+# These optional mechanisms can be used to add support for storing passwords
+# and other secrets in external (to wpa_supplicant) location. This allows, for
+# example, operating system specific key storage to be used
+#
+# External password backend for testing purposes (developer use)
+#CONFIG_EXT_PASSWORD_TEST=y
+
+# Enable Fast Session Transfer (FST)
+#CONFIG_FST=y
+
+# Enable CLI commands for FST testing
+#CONFIG_FST_TEST=y
+
+# OS X builds. This is only for building eapol_test.
+#CONFIG_OSX=y
diff --git a/hostap/wpa_supplicant/doc/docbook/Makefile b/hostap/wpa_supplicant/doc/docbook/Makefile
new file mode 100644
index 0000000..82f9de3
--- /dev/null
+++ b/hostap/wpa_supplicant/doc/docbook/Makefile
@@ -0,0 +1,28 @@
+all: man html pdf
+
+FILES += wpa_background
+FILES += wpa_cli
+FILES += wpa_gui
+FILES += wpa_passphrase
+FILES += wpa_priv
+FILES += wpa_supplicant.conf
+FILES += wpa_supplicant
+FILES += eapol_test
+
+man:
+	for i in $(FILES); do docbook2man $$i.sgml; done
+
+html:
+	for i in $(FILES); do docbook2html $$i.sgml && \
+		mv index.html $$i.html; done
+
+pdf:
+	for i in $(FILES); do docbook2pdf $$i.sgml; done
+
+
+clean:
+	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)
+	rm -f $(FILES:%=%.html)
diff --git a/hostap/wpa_supplicant/doc/docbook/eapol_test.sgml b/hostap/wpa_supplicant/doc/docbook/eapol_test.sgml
new file mode 100644
index 0000000..e9af6d9
--- /dev/null
+++ b/hostap/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-2015,
+    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/hostap/wpa_supplicant/doc/docbook/wpa_background.sgml b/hostap/wpa_supplicant/doc/docbook/wpa_background.sgml
new file mode 100644
index 0000000..afb8c3b
--- /dev/null
+++ b/hostap/wpa_supplicant/doc/docbook/wpa_background.sgml
@@ -0,0 +1,101 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+  <refmeta>
+    <refentrytitle>wpa_background</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>wpa_background</refname>
+    <refpurpose>Background information on Wi-Fi Protected Access and IEEE 802.11i</refpurpose>
+  </refnamediv>
+  <refsect1>
+    <title>WPA</title>
+
+    <para>The original security mechanism of IEEE 802.11 standard was
+    not designed to be strong and has proven to be insufficient for
+    most networks that require some kind of security. Task group I
+    (Security) of IEEE 802.11 working group
+    (http://www.ieee802.org/11/) has worked to address the flaws of
+    the base standard and has in practice completed its work in May
+    2004. The IEEE 802.11i amendment to the IEEE 802.11 standard was
+    approved in June 2004 and published in July 2004.</para>
+
+    <para>Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version
+    of the IEEE 802.11i work (draft 3.0) to define a subset of the
+    security enhancements that can be implemented with existing wlan
+    hardware. This is called Wi-Fi Protected Access&lt;TM&gt; (WPA). This
+    has now become a mandatory component of interoperability testing
+    and certification done by Wi-Fi Alliance. Wi-Fi provides
+    information about WPA at its web site
+    (http://www.wi-fi.org/OpenSection/protected_access.asp).</para>
+
+    <para>IEEE 802.11 standard defined wired equivalent privacy (WEP)
+    algorithm for protecting wireless networks. WEP uses RC4 with
+    40-bit keys, 24-bit initialization vector (IV), and CRC32 to
+    protect against packet forgery. All these choices have proven to
+    be insufficient: key space is too small against current attacks,
+    RC4 key scheduling is insufficient (beginning of the pseudorandom
+    stream should be skipped), IV space is too small and IV reuse
+    makes attacks easier, there is no replay protection, and non-keyed
+    authentication does not protect against bit flipping packet
+    data.</para>
+
+    <para>WPA is an intermediate solution for the security issues. It
+    uses Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP
+    is a compromise on strong security and possibility to use existing
+    hardware. It still uses RC4 for the encryption like WEP, but with
+    per-packet RC4 keys. In addition, it implements replay protection,
+    keyed packet authentication mechanism (Michael MIC).</para>
+
+    <para>Keys can be managed using two different mechanisms. WPA can
+    either use an external authentication server (e.g., RADIUS) and
+    EAP just like IEEE 802.1X is using or pre-shared keys without need
+    for additional servers. Wi-Fi calls these "WPA-Enterprise" and
+    "WPA-Personal", respectively. Both mechanisms will generate a
+    master session key for the Authenticator (AP) and Supplicant
+    (client station).</para>
+
+    <para>WPA implements a new key handshake (4-Way Handshake and
+    Group Key Handshake) for generating and exchanging data encryption
+    keys between the Authenticator and Supplicant. This handshake is
+    also used to verify that both Authenticator and Supplicant know
+    the master session key. These handshakes are identical regardless
+    of the selected key management mechanism (only the method for
+    generating master session key changes).</para>
+  </refsect1>
+
+  <refsect1>
+    <title>IEEE 802.11i / WPA2</title>
+
+    <para>The design for parts of IEEE 802.11i that were not included
+    in WPA has finished (May 2004) and this amendment to IEEE 802.11
+    was approved in June 2004. Wi-Fi Alliance is using the final IEEE
+    802.11i as a new version of WPA called WPA2. This includes, e.g.,
+    support for more robust encryption algorithm (CCMP: AES in Counter
+    mode with CBC-MAC) to replace TKIP and optimizations for handoff
+    (reduced number of messages in initial key handshake,
+    pre-authentication, and PMKSA caching).</para>
+  </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-2015,
+    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/hostap/wpa_supplicant/doc/docbook/wpa_cli.sgml b/hostap/wpa_supplicant/doc/docbook/wpa_cli.sgml
new file mode 100644
index 0000000..47947c1
--- /dev/null
+++ b/hostap/wpa_supplicant/doc/docbook/wpa_cli.sgml
@@ -0,0 +1,356 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+  <refmeta>
+    <refentrytitle>wpa_cli</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>wpa_cli</refname>
+
+    <refpurpose>WPA command line client</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <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>
+
+  <refsect1>
+    <title>Overview</title>
+
+    <para>wpa_cli is a text-based frontend program for interacting
+    with wpa_supplicant. It is used to query current status, change
+    configuration, trigger events, and request interactive user
+    input.</para>
+
+    <para>wpa_cli can show the current authentication status, selected
+    security mode, dot11 and dot1x MIBs, etc. In addition, it can
+    configure some variables like EAPOL state machine parameters and
+    trigger events like reassociation and IEEE 802.1X
+    logoff/logon. wpa_cli provides a user interface to request
+    authentication information, like username and password, if these
+    are not included in the configuration. This can be used to
+    implement, e.g., one-time-passwords or generic token card
+    authentication where the authentication is based on a
+    challenge-response that uses an external device for generating the
+    response.</para>
+
+    <para>The control interface of wpa_supplicant can be configured to
+    allow non-root user access (ctrl_interface GROUP= parameter in the
+    configuration file). This makes it possible to run wpa_cli with a
+    normal user account.</para>
+
+    <para>wpa_cli supports two modes: interactive and command
+    line. Both modes share the same command set and the main
+    difference is in interactive mode providing access to unsolicited
+    messages (event messages, username/password requests).</para>
+
+    <para>Interactive mode is started when wpa_cli is executed without
+    including the command as a command line parameter. Commands are
+    then entered on the wpa_cli prompt. In command line mode, the same
+    commands are entered as command line arguments for wpa_cli.</para>
+ </refsect1>
+ <refsect1>
+   <title>Interactive authentication parameters request</title>
+
+   <para>When wpa_supplicant need authentication parameters, like
+   username and password, which are not present in the configuration
+   file, it sends a request message to all attached frontend programs,
+   e.g., wpa_cli in interactive mode. wpa_cli shows these requests
+   with "CTRL-REQ-&lt;type&gt;-&lt;id&gt;:&lt;text&gt;"
+   prefix. &lt;type&gt; is IDENTITY, PASSWORD, or OTP
+   (one-time-password). &lt;id&gt; is a unique identifier for the
+   current network. &lt;text&gt; is description of the request. In
+   case of OTP request, it includes the challenge from the
+   authentication server.</para>
+
+    <para>The reply to these requests can be given with
+    <emphasis>identity</emphasis>, <emphasis>password</emphasis>, and
+    <emphasis>otp</emphasis> commands. &lt;id&gt; needs to be copied from
+    the matching request. <emphasis>password</emphasis> and
+    <emphasis>otp</emphasis> commands can be used regardless of whether
+    the request was for PASSWORD or OTP. The main difference between these
+    two commands is that values given with <emphasis>password</emphasis> are
+    remembered as long as wpa_supplicant is running whereas values given
+    with <emphasis>otp</emphasis> are used only once and then forgotten,
+    i.e., wpa_supplicant will ask frontend for a new value for every use.
+    This can be used to implement one-time-password lists and generic token
+    card -based authentication.</para>
+
+    <para>Example request for password and a matching reply:</para>
+
+<blockquote><programlisting>
+CTRL-REQ-PASSWORD-1:Password needed for SSID foobar
+> password 1 mysecretpassword
+</programlisting></blockquote>
+
+    <para>Example request for generic token card challenge-response:</para>
+
+<blockquote><programlisting>
+CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
+> otp 2 9876
+</programlisting></blockquote>
+
+  </refsect1>
+  <refsect1>
+    <title>Command Arguments</title>
+    <variablelist>
+      <varlistentry>
+	<term>-p path</term>
+
+	<listitem><para>Change the path where control sockets should
+	be found.</para></listitem>
+      </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
+	configured.  By default, choose the first interface found with
+	a control socket in the socket path.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-h</term>
+	<listitem><para>Help.  Show a usage message.</para></listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+	<term>-v</term>
+	<listitem><para>Show version information.</para></listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+	<term>-B</term>
+	<listitem><para>Run as a daemon in the background.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-a file</term>
+
+	<listitem><para>Run in daemon mode executing the action file
+        based on events from wpa_supplicant.  The specified file will
+	be executed with the first argument set to interface name and
+	second to "CONNECTED" or "DISCONNECTED" depending on the event.
+	This can be used to execute networking tools required to configure
+	the interface.</para>
+
+	<para>Additionally, three environmental variables are available to
+	the file: WPA_CTRL_DIR, WPA_ID, and WPA_ID_STR. WPA_CTRL_DIR
+	contains the absolute path to the ctrl_interface socket. WPA_ID
+	contains the unique network_id identifier assigned to the active
+	network, and WPA_ID_STR contains the content of the id_str option.
+	</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-P file</term>
+
+	<listitem><para>Set the location of the PID
+	file.</para></listitem>
+      </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
+	listed in the next section.</para></listitem>
+
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+  <refsect1>
+    <title>Commands</title>
+    <para>The following commands are available:</para>
+
+    <variablelist>
+      <varlistentry>
+	<term>status</term>
+	<listitem>
+	  <para>get current WPA/EAPOL/EAP status</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>mib</term>
+	<listitem>
+	  <para>get MIB variables (dot1x, dot11)</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>help</term>
+	<listitem>
+	  <para>show this usage help</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>interface [ifname]</term>
+	<listitem>
+	  <para>show interfaces/select interface</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>level &lt;debug level&gt;</term>
+	<listitem>
+	  <para>change debug level</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>license</term>
+	<listitem>
+	  <para>show full wpa_cli license</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>logoff</term>
+	<listitem>
+	  <para>IEEE 802.1X EAPOL state machine logoff</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>logon</term>
+	<listitem>
+	  <para>IEEE 802.1X EAPOL state machine logon</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>set</term>
+	<listitem>
+	  <para>set variables (shows list of variables when run without arguments)</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term>pmksa</term>
+	<listitem>
+	  <para>show PMKSA cache</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term>reassociate</term>
+	<listitem>
+	  <para>force reassociation</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term>reconfigure</term>
+	<listitem>
+	  <para>force wpa_supplicant to re-read its configuration file</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>preauthenticate &lt;BSSID&gt;</term>
+	<listitem>
+	  <para>force preauthentication</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>identity &lt;network id&gt; &lt;identity&gt;</term>
+	<listitem>
+	  <para>configure identity for an SSID</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>password &lt;network id&gt; &lt;password&gt;</term>
+	<listitem>
+	  <para>configure password for an SSID</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>pin &lt;network id&gt; &lt;pin&gt;</term>
+	<listitem>
+	  <para>configure pin for an SSID</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>otp &lt;network id&gt; &lt;password&gt;</term>
+	<listitem>
+	  <para>configure one-time-password for an SSID</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>bssid &lt;network id&gt; &lt;BSSID&gt;</term>
+	<listitem>
+	  <para>set preferred BSSID for an SSID</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>list_networks</term>
+	<listitem>
+	  <para>list configured networks</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>terminate</term>
+	<listitem>
+	  <para>terminate <command>wpa_supplicant</command></para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>quit</term>
+	<listitem><para>exit wpa_cli</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-2015,
+    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/hostap/wpa_supplicant/doc/docbook/wpa_gui.sgml b/hostap/wpa_supplicant/doc/docbook/wpa_gui.sgml
new file mode 100644
index 0000000..5f7b49d
--- /dev/null
+++ b/hostap/wpa_supplicant/doc/docbook/wpa_gui.sgml
@@ -0,0 +1,102 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+  <refmeta>
+    <refentrytitle>wpa_gui</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>wpa_gui</refname>
+
+    <refpurpose>WPA Graphical User Interface</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>wpa_gui</command>
+      <arg>-p <replaceable>path to ctrl sockets</replaceable></arg>
+      <arg>-i <replaceable>ifname</replaceable></arg>
+      <arg>-m <replaceable>seconds</replaceable></arg>
+      <arg>-t</arg>
+      <arg>-q</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Overview</title>
+
+    <para>wpa_gui is a QT graphical frontend program for interacting
+    with wpa_supplicant. It is used to query current status, change
+    configuration and request interactive user input.</para>
+
+    <para>wpa_gui supports (almost) all of the interactive status and
+    configuration features of the command line client, wpa_cli. Refer
+    to the wpa_cli manpage for a comprehensive list of the
+    interactive mode features.</para>
+  </refsect1>
+  <refsect1>
+    <title>Command Arguments</title>
+    <variablelist>
+      <varlistentry>
+	<term>-p path</term>
+
+	<listitem><para>Change the path where control sockets should
+	be found.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-i ifname</term>
+
+        <listitem><para>Specify the interface that is being
+	configured. By default, choose the first interface found with
+	a control socket in the socket path.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-m seconds</term>
+
+	<listitem><para>Set the update interval in seconds for the signal
+	strength meter. This value must be a positive integer, otherwise
+	meter is not enabled (default behavior).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-t</term>
+
+        <listitem><para>Start program in the system tray only (if the window
+	manager supports it). By default the main status window is
+	shown.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-q</term>
+
+        <listitem><para>Run program in the quiet mode - do not display tray
+	icon pop-up messages.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry>
+	<refentrytitle>wpa_cli</refentrytitle>
+	<manvolnum>8</manvolnum>
+      </citerefentry>
+      <citerefentry>
+	<refentrytitle>wpa_supplicant</refentrytitle>
+	<manvolnum>8</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+  <refsect1>
+    <title>Legal</title>
+    <para>wpa_supplicant is copyright (c) 2003-2015,
+    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/hostap/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/hostap/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
new file mode 100644
index 0000000..b381e40
--- /dev/null
+++ b/hostap/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
@@ -0,0 +1,73 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+  <refmeta>
+    <refentrytitle>wpa_passphrase</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>wpa_passphrase</refname>
+    <refpurpose>Generate a WPA PSK from an ASCII passphrase for a SSID</refpurpose>
+  </refnamediv>
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>wpa_passphrase</command>
+      <arg><replaceable>ssid</replaceable></arg>
+      <arg><replaceable>passphrase</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Overview</title> 
+
+    <para><command>wpa_passphrase</command> pre-computes PSK entries for
+    network configuration blocks of a
+    <filename>wpa_supplicant.conf</filename> file. An ASCII passphrase
+    and SSID are used to generate a 256-bit PSK.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+    <variablelist>
+      <varlistentry>
+	<term>ssid</term>
+	<listitem>
+	  <para>The SSID whose passphrase should be derived.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>passphrase</term>
+	<listitem>
+	  <para>The passphrase to use. If not included on the command line,
+	  passphrase will be read from standard input.</para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry>
+	<refentrytitle>wpa_supplicant.conf</refentrytitle>
+	<manvolnum>5</manvolnum>
+      </citerefentry>
+      <citerefentry>
+	<refentrytitle>wpa_supplicant</refentrytitle>
+	<manvolnum>8</manvolnum>
+      </citerefentry>
+    </para>
+
+  </refsect1>
+  <refsect1>
+    <title>Legal</title>
+    <para>wpa_supplicant is copyright (c) 2003-2015,
+    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/hostap/wpa_supplicant/doc/docbook/wpa_priv.sgml b/hostap/wpa_supplicant/doc/docbook/wpa_priv.sgml
new file mode 100644
index 0000000..d13a5db
--- /dev/null
+++ b/hostap/wpa_supplicant/doc/docbook/wpa_priv.sgml
@@ -0,0 +1,148 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+  <refmeta>
+    <refentrytitle>wpa_priv</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>wpa_priv</refname>
+
+    <refpurpose>wpa_supplicant privilege separation helper</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>wpa_priv</command>
+      <arg>-c <replaceable>ctrl path</replaceable></arg>
+      <arg>-Bdd</arg>
+      <arg>-P <replaceable>pid file</replaceable></arg>
+      <arg>driver:ifname <replaceable>[driver:ifname ...]</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Overview</title>
+
+    <para><command>wpa_priv</command> is a privilege separation helper that
+    minimizes the size of <command>wpa_supplicant</command> code that needs
+    to be run with root privileges.</para>
+
+    <para>If enabled, privileged operations are done in the wpa_priv process
+    while leaving rest of the code (e.g., EAP authentication and WPA
+    handshakes) to operate in an unprivileged process (wpa_supplicant) that
+    can be run as non-root user. Privilege separation restricts the effects
+    of potential software errors by containing the majority of the code in an
+    unprivileged process to avoid the possibility of a full system
+    compromise.</para>
+
+    <para><command>wpa_priv</command> needs to be run with network admin
+    privileges (usually, root user). It opens a UNIX domain socket for each
+    interface that is included on the command line; any other interface will
+    be off limits for <command>wpa_supplicant</command> in this kind of
+    configuration. After this, <command>wpa_supplicant</command> can be run as
+    a non-root user (e.g., all standard users on a laptop or as a special
+    non-privileged user account created just for this purpose to limit access
+    to user files even further).</para>
+  </refsect1>
+  <refsect1>
+    <title>Example configuration</title>
+
+    <para>The following steps are an example of how to configure
+    <command>wpa_priv</command> to allow users in the
+    <emphasis>wpapriv</emphasis> group to communicate with
+    <command>wpa_supplicant</command> with privilege separation:</para>
+
+    <para>Create user group (e.g., wpapriv) and assign users that
+    should be able to use wpa_supplicant into that group.</para>
+
+    <para>Create /var/run/wpa_priv directory for UNIX domain sockets and
+    control user access by setting it accessible only for the wpapriv
+    group:</para>
+
+<blockquote><programlisting>
+mkdir /var/run/wpa_priv
+chown root:wpapriv /var/run/wpa_priv
+chmod 0750 /var/run/wpa_priv
+</programlisting></blockquote>
+
+    <para>Start <command>wpa_priv</command> as root (e.g., from system
+    startup scripts) with the enabled interfaces configured on the
+    command line:</para>
+
+<blockquote><programlisting>
+wpa_priv -B -c /var/run/wpa_priv -P /var/run/wpa_priv.pid wext:wlan0
+</programlisting></blockquote>
+
+    <para>Run <command>wpa_supplicant</command> as non-root with a user
+    that is in the wpapriv group:</para>
+
+<blockquote><programlisting>
+wpa_supplicant -i ath0 -c wpa_supplicant.conf
+</programlisting></blockquote>
+
+  </refsect1>
+  <refsect1>
+    <title>Command Arguments</title>
+    <variablelist>
+      <varlistentry>
+	<term>-c ctrl path</term>
+
+	<listitem><para>Specify the path to wpa_priv control directory
+	(Default: /var/run/wpa_priv/).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-B</term>
+	<listitem><para>Run as a daemon in the background.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-P file</term>
+
+	<listitem><para>Set the location of the PID
+	file.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>driver:ifname [driver:ifname ...]</term>
+
+	<listitem><para>The &lt;driver&gt; string dictates which of the
+	supported <command>wpa_supplicant</command> driver backends is to be
+	used. To get a list of supported driver types see wpa_supplicant help
+	(e.g, wpa_supplicant -h). The driver backend supported by most good
+	drivers is <emphasis>wext</emphasis>.</para>
+
+	<para>The &lt;ifname&gt; string specifies which network
+	interface is to be managed by <command>wpa_supplicant</command>
+	(e.g., wlan0 or ath0).</para>
+
+	<para><command>wpa_priv</command> does not use the network interface
+	before <command>wpa_supplicant</command> is started, so it is fine to
+	include network interfaces that are not available at the time wpa_priv
+	is started. wpa_priv can control multiple interfaces with one process,
+	but it is also possible to run multiple <command>wpa_priv</command>
+	processes at the same time, if desired.</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-2015,
+    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/hostap/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml b/hostap/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml
new file mode 100644
index 0000000..462039d
--- /dev/null
+++ b/hostap/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml
@@ -0,0 +1,239 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+<refentry>
+  <refmeta>
+    <refentrytitle>wpa_supplicant.conf</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>wpa_supplicant.conf</refname>
+    <refpurpose>configuration file for wpa_supplicant</refpurpose>
+  </refnamediv>
+  <refsect1>
+    <title>Overview</title>
+
+    <para><command>wpa_supplicant</command> is configured using a text
+    file that lists all accepted networks and security policies,
+    including pre-shared keys. See the example configuration file,
+    probably in <command>/usr/share/doc/wpa_supplicant/</command>, for
+    detailed information about the configuration format and supported
+    fields.</para>
+
+    <para>All file paths in this configuration file should use full
+    (absolute, not relative to working directory) path in order to allow
+    working directory to be changed. This can happen if wpa_supplicant is
+    run in the background.</para>
+
+    <para>Changes to configuration file can be reloaded be sending
+    SIGHUP signal to <command>wpa_supplicant</command> ('killall -HUP
+    wpa_supplicant'). Similarly, reloading can be triggered with
+    the <emphasis>wpa_cli reconfigure</emphasis> command.</para>
+
+    <para>Configuration file can include one or more network blocks,
+    e.g., one for each used SSID. wpa_supplicant will automatically
+    select the best network based on the order of network blocks in
+    the configuration file, network security level (WPA/WPA2 is
+    preferred), and signal strength.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Quick Examples</title>
+
+    <orderedlist>
+      <listitem>
+
+      <para>WPA-Personal (PSK) as home network and WPA-Enterprise with
+      EAP-TLS as work network.</para>
+
+<blockquote><programlisting>
+# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
+#
+# home network; allow all valid ciphers
+network={
+	ssid="home"
+	scan_ssid=1
+	key_mgmt=WPA-PSK
+	psk="very secret passphrase"
+}
+#
+# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers
+network={
+	ssid="work"
+	scan_ssid=1
+	key_mgmt=WPA-EAP
+	pairwise=CCMP TKIP
+	group=CCMP TKIP
+	eap=TLS
+	identity="user@example.com"
+	ca_cert="/etc/cert/ca.pem"
+	client_cert="/etc/cert/user.pem"
+	private_key="/etc/cert/user.prv"
+	private_key_passwd="password"
+}
+</programlisting></blockquote>   
+      </listitem>
+
+      <listitem>
+	<para>WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that
+        use old peaplabel (e.g., Funk Odyssey and SBR, Meetinghouse
+        Aegis, Interlink RAD-Series)</para>
+
+<blockquote><programlisting>
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
+network={
+	ssid="example"
+	scan_ssid=1
+	key_mgmt=WPA-EAP
+	eap=PEAP
+	identity="user@example.com"
+	password="foobar"
+	ca_cert="/etc/cert/ca.pem"
+	phase1="peaplabel=0"
+	phase2="auth=MSCHAPV2"
+}
+</programlisting></blockquote>
+      </listitem>
+
+      <listitem>
+	<para>EAP-TTLS/EAP-MD5-Challenge configuration with anonymous
+        identity for the unencrypted use. Real identity is sent only
+        within an encrypted TLS tunnel.</para>
+
+
+<blockquote><programlisting>
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
+network={
+	ssid="example"
+	scan_ssid=1
+	key_mgmt=WPA-EAP
+	eap=TTLS
+	identity="user@example.com"
+	anonymous_identity="anonymous@example.com"
+	password="foobar"
+	ca_cert="/etc/cert/ca.pem"
+	phase2="auth=MD5"
+}
+</programlisting></blockquote>
+
+      </listitem>
+
+      <listitem>
+	<para>IEEE 802.1X (i.e., no WPA) with dynamic WEP keys
+        (require both unicast and broadcast); use EAP-TLS for
+        authentication</para>
+
+<blockquote><programlisting>
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
+network={
+	ssid="1x-test"
+	scan_ssid=1
+	key_mgmt=IEEE8021X
+	eap=TLS
+	identity="user@example.com"
+	ca_cert="/etc/cert/ca.pem"
+	client_cert="/etc/cert/user.pem"
+	private_key="/etc/cert/user.prv"
+	private_key_passwd="password"
+	eapol_flags=3
+}
+</programlisting></blockquote>
+      </listitem>
+
+
+      <listitem>
+	<para>Catch all example that allows more or less all
+        configuration modes. The configuration options are used based
+        on what security policy is used in the selected SSID. This is
+        mostly for testing and is not recommended for normal
+        use.</para>
+
+<blockquote><programlisting>
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
+network={
+	ssid="example"
+	scan_ssid=1
+	key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
+	pairwise=CCMP TKIP
+	group=CCMP TKIP WEP104 WEP40
+	psk="very secret passphrase"
+	eap=TTLS PEAP TLS
+	identity="user@example.com"
+	password="foobar"
+	ca_cert="/etc/cert/ca.pem"
+	client_cert="/etc/cert/user.pem"
+	private_key="/etc/cert/user.prv"
+	private_key_passwd="password"
+	phase1="peaplabel=0"
+	ca_cert2="/etc/cert/ca2.pem"
+	client_cert2="/etc/cer/user.pem"
+	private_key2="/etc/cer/user.prv"
+	private_key2_passwd="password"
+}
+</programlisting></blockquote>
+      </listitem>
+
+      <listitem>
+	<para>Authentication for wired Ethernet. This can be used with
+        <emphasis>wired</emphasis> or <emphasis>roboswitch</emphasis> interface
+        (-Dwired or -Droboswitch on command line).</para>
+
+<blockquote><programlisting>
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
+ap_scan=0
+network={
+	key_mgmt=IEEE8021X
+	eap=MD5
+	identity="user"
+	password="password"
+	eapol_flags=0
+}
+</programlisting></blockquote>
+      </listitem>
+    </orderedlist>
+
+
+
+
+
+  </refsect1>
+  <refsect1>
+    <title>Certificates</title>
+
+    <para>Some EAP authentication methods require use of
+    certificates. EAP-TLS uses both server side and client
+    certificates whereas EAP-PEAP and EAP-TTLS only require the server
+    side certificate. When client certificate is used, a matching
+    private key file has to also be included in configuration. If the
+    private key uses a passphrase, this has to be configured in
+    wpa_supplicant.conf ("private_key_passwd").</para>
+
+    <para>wpa_supplicant supports X.509 certificates in PEM and DER
+    formats. User certificate and private key can be included in the
+    same file.</para>
+
+    <para>If the user certificate and private key is received in
+    PKCS#12/PFX format, they need to be converted to suitable PEM/DER
+    format for wpa_supplicant. This can be done, e.g., with following
+    commands:</para>
+<blockquote><programlisting>
+# convert client certificate and private key to PEM format
+openssl pkcs12 -in example.pfx -out user.pem -clcerts
+# convert CA certificate (if included in PFX file) to PEM format
+openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys
+</programlisting></blockquote>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry>
+	<refentrytitle>wpa_supplicant</refentrytitle>
+	<manvolnum>8</manvolnum>
+      </citerefentry>
+      <citerefentry>
+	<refentrytitle>openssl</refentrytitle>
+	<manvolnum>1</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+</refentry>
diff --git a/hostap/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/hostap/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
new file mode 100644
index 0000000..46c21b5
--- /dev/null
+++ b/hostap/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
@@ -0,0 +1,747 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+  <refmeta>
+    <refentrytitle>wpa_supplicant</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>wpa_supplicant</refname>
+    <refpurpose>Wi-Fi Protected Access client and IEEE 802.1X supplicant</refpurpose>
+  </refnamediv>
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>wpa_supplicant</command>
+      <arg>-BddfhKLqqsTtuvW</arg>
+      <arg>-i<replaceable>ifname</replaceable></arg>
+      <arg>-c<replaceable>config file</replaceable></arg>
+      <arg>-D<replaceable>driver</replaceable></arg>
+      <arg>-P<replaceable>PID_file</replaceable></arg>
+      <arg>-f<replaceable>output file</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+  <refsect1>
+    <title>Overview</title>
+
+    <para>
+    Wireless networks do not require physical access to the network equipment
+    in the same way as wired networks. This makes it easier for unauthorized
+    users to passively monitor a network and capture all transmitted frames.
+    In addition, unauthorized use of the network is much easier. In many cases,
+    this can happen even without user's explicit knowledge since the wireless
+    LAN adapter may have been configured to automatically join any available
+    network.
+    </para>
+
+    <para>
+    Link-layer encryption can be used to provide a layer of security for
+    wireless networks. The original wireless LAN standard, IEEE 802.11,
+    included a simple encryption mechanism, WEP. However, that proved to
+    be flawed in many areas and network protected with WEP cannot be consider
+    secure. IEEE 802.1X authentication and frequently changed dynamic WEP keys
+    can be used to improve the network security, but even that has inherited
+    security issues due to the use of WEP for encryption. Wi-Fi Protected
+    Access and IEEE 802.11i amendment to the wireless LAN standard introduce
+    a much improvement mechanism for securing wireless networks. IEEE 802.11i
+    enabled networks that are using CCMP (encryption mechanism based on strong
+    cryptographic algorithm AES) can finally be called secure used for
+    applications which require efficient protection against unauthorized
+    access.
+    </para>
+
+    <para><command>wpa_supplicant</command> is an implementation of
+    the WPA Supplicant component, i.e., the part that runs in the
+    client stations. It implements WPA key negotiation with a WPA
+    Authenticator and EAP authentication with Authentication
+    Server. In addition, it controls the roaming and IEEE 802.11
+    authentication/association of the wireless LAN driver.</para>
+
+    <para><command>wpa_supplicant</command> is designed to be a
+    "daemon" program that runs in the background and acts as the
+    backend component controlling the wireless
+    connection. <command>wpa_supplicant</command> supports separate
+    frontend programs and an example text-based frontend,
+    <command>wpa_cli</command>, is included with
+    wpa_supplicant.</para>
+
+    <para>Before wpa_supplicant can do its work, the network interface
+    must be available.  That means that the physical device must be
+    present and enabled, and the driver for the device must be
+    loaded. The daemon will exit immediately if the device is not already
+    available.</para>
+
+    <para>After <command>wpa_supplicant</command> has configured the
+    network device, higher level configuration such as DHCP may
+    proceed.  There are a variety of ways to integrate wpa_supplicant
+    into a machine's networking scripts, a few of which are described
+    in sections below.</para>
+
+    <para>The following steps are used when associating with an AP
+    using WPA:</para>
+
+    <itemizedlist>
+      <listitem>
+	<para><command>wpa_supplicant</command> requests the kernel
+	driver to scan neighboring BSSes</para>
+      </listitem>
+
+      <listitem>
+	<para><command>wpa_supplicant</command> selects a BSS based on
+	its configuration</para>
+      </listitem>
+
+      <listitem>
+	<para><command>wpa_supplicant</command> requests the kernel
+        driver to associate with the chosen BSS</para>
+      </listitem>
+
+      <listitem>
+	<para>If WPA-EAP: integrated IEEE 802.1X Supplicant
+        completes EAP authentication with the
+        authentication server (proxied by the Authenticator in the
+        AP)</para>
+      </listitem>
+
+      <listitem>
+	<para>If WPA-EAP: master key is received from the IEEE 802.1X
+	Supplicant</para>
+      </listitem>
+
+      <listitem>
+	<para>If WPA-PSK: <command>wpa_supplicant</command> uses PSK
+	as the master session key</para>
+      </listitem>
+
+      <listitem>
+	<para><command>wpa_supplicant</command> completes WPA 4-Way
+        Handshake and Group Key Handshake with the Authenticator
+        (AP)</para>
+      </listitem>
+
+      <listitem>
+	<para><command>wpa_supplicant</command> configures encryption
+	keys for unicast and broadcast</para>
+      </listitem>
+
+      <listitem>
+	<para>normal data packets can be transmitted and received</para>
+      </listitem>
+    </itemizedlist>
+  </refsect1>
+
+  <refsect1>
+    <title>Supported Features</title>
+    <para>Supported WPA/IEEE 802.11i features:</para>
+    <itemizedlist>
+      <listitem>
+	<para>WPA-PSK ("WPA-Personal")</para>
+      </listitem>
+
+      <listitem>
+	<para>WPA with EAP (e.g., with RADIUS authentication server)
+       ("WPA-Enterprise") Following authentication methods are
+       supported with an integrate IEEE 802.1X Supplicant:</para>
+
+	<itemizedlist>
+	  <listitem>
+	    <para>EAP-TLS</para>
+	  </listitem>
+	</itemizedlist>
+
+	<itemizedlist>
+	  <listitem>
+	    <para>EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1)</para>
+	  </listitem>
+
+
+	  <listitem>
+	    <para>EAP-PEAP/TLS (both PEAPv0 and PEAPv1)</para>
+	  </listitem>
+
+	  <listitem>
+	    <para>EAP-PEAP/GTC (both PEAPv0 and PEAPv1)</para>
+	  </listitem>
+
+	  <listitem>
+	    <para>EAP-PEAP/OTP (both PEAPv0 and PEAPv1)</para>
+	  </listitem>
+
+	  <listitem>
+	    <para>EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1)</para>
+	  </listitem>
+
+	  <listitem>
+	    <para>EAP-TTLS/EAP-MD5-Challenge</para>
+	  </listitem>
+
+	  <listitem>
+	    <para>EAP-TTLS/EAP-GTC</para>
+	  </listitem>
+
+          <listitem><para>EAP-TTLS/EAP-OTP</para></listitem>
+
+          <listitem><para>EAP-TTLS/EAP-MSCHAPv2</para></listitem>
+
+          <listitem><para>EAP-TTLS/EAP-TLS</para></listitem>
+
+          <listitem><para>EAP-TTLS/MSCHAPv2</para></listitem>
+
+          <listitem><para>EAP-TTLS/MSCHAP</para></listitem>
+
+          <listitem><para>EAP-TTLS/PAP</para></listitem>
+
+          <listitem><para>EAP-TTLS/CHAP</para></listitem>
+
+          <listitem><para>EAP-SIM</para></listitem>
+
+          <listitem><para>EAP-AKA</para></listitem>
+
+          <listitem><para>EAP-PSK</para></listitem>
+
+          <listitem><para>EAP-PAX</para></listitem>
+
+          <listitem><para>LEAP (note: requires special support from
+          the driver for IEEE 802.11 authentication)</para></listitem>
+
+          <listitem><para>(following methods are supported, but since
+          they do not generate keying material, they cannot be used
+          with WPA or IEEE 802.1X WEP keying)</para></listitem>
+
+          <listitem><para>EAP-MD5-Challenge </para></listitem>
+
+          <listitem><para>EAP-MSCHAPv2</para></listitem>
+
+          <listitem><para>EAP-GTC</para></listitem>
+
+          <listitem><para>EAP-OTP</para></listitem>
+	</itemizedlist>
+      </listitem>
+
+      <listitem>
+	<para>key management for CCMP, TKIP, WEP104, WEP40</para>
+      </listitem>
+
+      <listitem>
+	<para>RSN/WPA2 (IEEE 802.11i)</para>
+	<itemizedlist>
+	  <listitem>
+	    <para>pre-authentication</para>
+	  </listitem>
+
+	  <listitem>
+	    <para>PMKSA caching</para>
+	  </listitem>
+	</itemizedlist>
+      </listitem>
+    </itemizedlist>
+  </refsect1>
+
+  <refsect1>
+    <title>Available Drivers</title>
+    <para>A summary of available driver backends is below. Support for each
+    of the driver backends is chosen at wpa_supplicant compile time. For a
+    list of supported driver backends that may be used with the -D option on
+    your system, refer to the help output of wpa_supplicant
+    (<emphasis>wpa_supplicant -h</emphasis>).</para>
+
+    <variablelist>
+      <varlistentry>
+	<term>wext</term>
+	<listitem>
+	  <para>Linux wireless extensions (generic).</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>wired</term>
+	<listitem>
+	  <para>wpa_supplicant wired Ethernet driver</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>roboswitch</term>
+	<listitem>
+	  <para>wpa_supplicant Broadcom switch driver</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>bsd</term>
+	<listitem>
+	  <para>BSD 802.11 support (Atheros, etc.).</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>ndis</term>
+	<listitem>
+	  <para>Windows NDIS driver.</para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Command Line Options</title>
+    <para>Most command line options have global scope. Some are given per
+    interface, and are only valid if at least one <option>-i</option> option
+    is specified, otherwise they're ignored. Option groups for different
+    interfaces must be separated by <option>-N</option> option.</para>
+    <variablelist>
+      <varlistentry>
+	<term>-b br_ifname</term>
+	<listitem>
+	  <para>Optional bridge interface name. (Per interface)</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-B</term>
+	<listitem>
+	  <para>Run daemon in the background.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-c filename</term>
+	<listitem>
+	  <para>Path to configuration file. (Per interface)</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-C ctrl_interface</term>
+	<listitem>
+	  <para>Path to ctrl_interface socket (Per interface. Only used if
+		  <option>-c</option> is not).</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-i ifname</term>
+	<listitem>
+	  <para>Interface to listen on. Multiple instances of this option can
+	  be present, one per interface, separated by <option>-N</option>
+	  option (see below).</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-d</term>
+	<listitem>
+	  <para>Increase debugging verbosity (<option>-dd</option> even
+		  more).</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-D driver</term>
+	<listitem>
+	  <para>Driver to use (can be multiple drivers: nl80211,wext).
+		  (Per interface, see the available options below.)</para>
+	</listitem>
+      </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. (This
+	  is only available if <command>wpa_supplicant</command> was
+	  built with the <literal>CONFIG_DEBUG_FILE</literal>
+	  option.)</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-g global ctrl_interface</term>
+	<listitem>
+	  <para>Path to global ctrl_interface socket. If specified, interface
+	  definitions may be omitted.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-K</term>
+	<listitem>
+	  <para>Include keys (passwords, etc.) in debug output.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-t</term>
+	<listitem>
+	  <para>Include timestamp in debug messages.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-h</term>
+	<listitem>
+	  <para>Help.  Show a usage message.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-L</term>
+	<listitem>
+	  <para>Show license (BSD).</para>
+	</listitem>
+      </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>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-P PID_file</term>
+	<listitem>
+	  <para>Path to PID file.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-q</term>
+	<listitem>
+	  <para>Decrease debugging verbosity (<option>-qq</option> even
+		  less).</para>
+	</listitem>
+      </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>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>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-v</term>
+	<listitem>
+	  <para>Show version.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-W</term>
+	<listitem>
+	  <para>Wait for a control interface monitor before starting.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-N</term>
+	<listitem>
+	  <para>Start describing new interface.</para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <para>In most common cases, <command>wpa_supplicant</command> is
+    started with:</para>
+
+<blockquote><programlisting>
+wpa_supplicant -B -c/etc/wpa_supplicant.conf -iwlan0
+</programlisting></blockquote>
+
+    <para>This makes the process fork into background.</para>
+
+    <para>The easiest way to debug problems, and to get debug log for
+    bug reports, is to start <command>wpa_supplicant</command> on
+    foreground with debugging enabled:</para>
+
+<blockquote><programlisting>
+wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d
+</programlisting></blockquote>
+
+    <para>If the specific driver wrapper is not known beforehand, it is
+    possible to specify multiple comma separated driver wrappers on the command
+    line. <command>wpa_supplicant</command> will use the first driver
+    wrapper that is able to initialize the interface.</para>
+
+<blockquote><programlisting>
+wpa_supplicant -Dnl80211,wext -c/etc/wpa_supplicant.conf -iwlan0
+</programlisting></blockquote>
+
+    <para><command>wpa_supplicant</command> can control multiple
+    interfaces (radios) either by running one process for each
+    interface separately or by running just one process and list of
+    options at command line. Each interface is separated with -N
+    argument. As an example, following command would start
+    wpa_supplicant for two interfaces:</para>
+
+<blockquote><programlisting>
+wpa_supplicant \
+	-c wpa1.conf -i wlan0 -D nl80211 -N \
+	-c wpa2.conf -i ath0 -D wext
+</programlisting></blockquote>
+  </refsect1>
+
+  <refsect1>
+    <title>OS Requirements</title>
+    <para>Current hardware/software requirements:</para>
+
+    <itemizedlist>
+      <listitem>
+	<para>Linux kernel 2.4.x or 2.6.x with Linux Wireless
+	Extensions v15 or newer</para>
+      </listitem>
+
+
+      <listitem>
+	<para>FreeBSD 6-CURRENT</para>
+      </listitem>
+
+      <listitem>
+	<para>Microsoft Windows with WinPcap (at least WinXP, may work
+	with other versions)</para>
+      </listitem>
+    </itemizedlist>
+  </refsect1>
+
+  <refsect1>
+    <title>Supported Drivers</title>
+    <variablelist>
+      <varlistentry>
+	<term>Linux wireless extensions</term>
+	<listitem>
+	  <para>In theory, any driver that supports Linux wireless
+	extensions can be used with IEEE 802.1X (i.e., not WPA) when
+	using ap_scan=0 option in configuration file.</para>
+	</listitem>
+      </varlistentry>
+      
+      <varlistentry>
+	<term>Wired Ethernet drivers</term>
+	<listitem>
+	  <para>Use ap_scan=0.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>BSD net80211 layer (e.g., Atheros driver)</term>
+	<listitem>
+	  <para>At the moment, this is for FreeBSD 6-CURRENT branch.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>Windows NDIS</term>
+	<listitem>
+	  <para>The current Windows port requires WinPcap
+	(http://winpcap.polito.it/).  See README-Windows.txt for more
+	information.</para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+
+	
+    <para>wpa_supplicant was designed to be portable for different
+    drivers and operating systems. Hopefully, support for more wlan
+    cards and OSes will be added in the future. See developer.txt for
+    more information about the design of wpa_supplicant and porting to
+    other drivers. One main goal is to add full WPA/WPA2 support to
+    Linux wireless extensions to allow new drivers to be supported
+    without having to implement new driver-specific interface code in
+    wpa_supplicant.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Architecture</title> <para>The
+    <command>wpa_supplicant</command> system consists of the following
+    components:</para>
+
+    <variablelist>
+      <varlistentry>
+	<term><filename>wpa_supplicant.conf</filename> </term>
+	<listitem>
+        <para>the configuration file describing all networks that the
+        user wants the computer to connect to.  </para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><command>wpa_supplicant</command></term>
+        <listitem><para>the program that directly interacts with the
+        network interface.  </para></listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><command>wpa_cli</command></term> <listitem><para> the
+	client program that provides a high-level interface to the
+	functionality of the daemon.  </para></listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><command>wpa_passphrase</command></term>
+        <listitem><para>a utility needed to construct
+        <filename>wpa_supplicant.conf</filename> files that include
+        encrypted passwords.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Quick Start</title>
+
+    <para>First, make a configuration file, e.g.
+    <filename>/etc/wpa_supplicant.conf</filename>, that describes the networks
+    you are interested in.  See <citerefentry>
+	<refentrytitle>wpa_supplicant.conf</refentrytitle>
+	<manvolnum>5</manvolnum>
+      </citerefentry>
+    for details.</para>
+
+    <para>Once the configuration is ready, you can test whether the
+    configuration works by running <command>wpa_supplicant</command>
+    with following command to start it on foreground with debugging
+    enabled:</para>
+
+    <blockquote><programlisting>
+wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d
+    </programlisting></blockquote>
+
+    <para>Assuming everything goes fine, you can start using following
+    command to start <command>wpa_supplicant</command> on background
+    without debugging:</para>
+
+    <blockquote><programlisting>
+wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B
+    </programlisting></blockquote>
+
+    <para>Please note that if you included more than one driver
+    interface in the build time configuration (.config), you may need
+    to specify which interface to use by including -D&lt;driver
+    name&gt; option on the command line.</para>
+
+    <!-- XXX at this point, the page could include a little script
+         based on wpa_cli to wait for a connection and then run
+         dhclient -->
+
+  </refsect1>
+
+  <refsect1>
+    <title>Interface to pcmcia-cs/cardmrg</title>
+
+    <para>For example, following small changes to pcmcia-cs scripts
+    can be used to enable WPA support:</para>
+
+    <para>Add MODE="Managed" and WPA="y" to the network scheme in
+    <filename>/etc/pcmcia/wireless.opts</filename>.</para>
+
+    <para>Add the following block to the end of <emphasis>start</emphasis>
+    action handler in <filename>/etc/pcmcia/wireless</filename>:</para>
+
+    <blockquote><programlisting>
+if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
+    /usr/local/bin/wpa_supplicant -B -c/etc/wpa_supplicant.conf -i$DEVICE
+fi
+    </programlisting></blockquote>
+
+
+    <para>Add the following block to the end of <emphasis>stop</emphasis>
+    action handler (may need to be separated from other actions) in
+    <filename>/etc/pcmcia/wireless</filename>:</para>
+
+    <blockquote><programlisting>
+if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
+    killall wpa_supplicant
+fi
+    </programlisting></blockquote>
+
+    <para>This will make <command>cardmgr</command> start
+    <command>wpa_supplicant</command> when the card is plugged
+    in.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry>
+	<refentrytitle>wpa_background</refentrytitle>
+	<manvolnum>8</manvolnum>
+      </citerefentry>
+      <citerefentry>
+	<refentrytitle>wpa_supplicant.conf</refentrytitle>
+	<manvolnum>5</manvolnum>
+      </citerefentry>
+      <citerefentry>
+	<refentrytitle>wpa_cli</refentrytitle>
+	<manvolnum>8</manvolnum>
+      </citerefentry>
+      <citerefentry>
+	<refentrytitle>wpa_passphrase</refentrytitle>
+	<manvolnum>8</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+  <refsect1>
+    <title>Legal</title>
+    <para>wpa_supplicant is copyright (c) 2003-2015,
+    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/hostap/wpa_supplicant/driver_i.h b/hostap/wpa_supplicant/driver_i.h
new file mode 100644
index 0000000..73768c7
--- /dev/null
+++ b/hostap/wpa_supplicant/driver_i.h
@@ -0,0 +1,915 @@
+/*
+ * wpa_supplicant - Internal driver interface wrappers
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_I_H
+#define DRIVER_I_H
+
+#include "drivers/driver.h"
+
+/* driver_ops */
+static inline void * wpa_drv_init(struct wpa_supplicant *wpa_s,
+				  const char *ifname)
+{
+	if (wpa_s->driver->init2)
+		return wpa_s->driver->init2(wpa_s, ifname,
+					    wpa_s->global_drv_priv);
+	if (wpa_s->driver->init) {
+		return wpa_s->driver->init(wpa_s, ifname);
+	}
+	return NULL;
+}
+
+static inline void wpa_drv_deinit(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->deinit)
+		wpa_s->driver->deinit(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_set_param(struct wpa_supplicant *wpa_s,
+				    const char *param)
+{
+	if (wpa_s->driver->set_param)
+		return wpa_s->driver->set_param(wpa_s->drv_priv, param);
+	return 0;
+}
+
+static inline int wpa_drv_set_countermeasures(struct wpa_supplicant *wpa_s,
+					      int enabled)
+{
+	if (wpa_s->driver->set_countermeasures) {
+		return wpa_s->driver->set_countermeasures(wpa_s->drv_priv,
+							  enabled);
+	}
+	return -1;
+}
+
+static inline int wpa_drv_authenticate(struct wpa_supplicant *wpa_s,
+				       struct wpa_driver_auth_params *params)
+{
+	if (wpa_s->driver->authenticate)
+		return wpa_s->driver->authenticate(wpa_s->drv_priv, params);
+	return -1;
+}
+
+static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s,
+				    struct wpa_driver_associate_params *params)
+{
+	if (wpa_s->driver->associate) {
+		return wpa_s->driver->associate(wpa_s->drv_priv, params);
+	}
+	return -1;
+}
+
+static inline int wpa_drv_init_mesh(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->init_mesh)
+		return wpa_s->driver->init_mesh(wpa_s->drv_priv);
+	return -1;
+}
+
+static inline int wpa_drv_join_mesh(struct wpa_supplicant *wpa_s,
+				    struct wpa_driver_mesh_join_params *params)
+{
+	if (wpa_s->driver->join_mesh)
+		return wpa_s->driver->join_mesh(wpa_s->drv_priv, params);
+	return -1;
+}
+
+static inline int wpa_drv_leave_mesh(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->leave_mesh)
+		return wpa_s->driver->leave_mesh(wpa_s->drv_priv);
+	return -1;
+}
+
+static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
+			       struct wpa_driver_scan_params *params)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->test_failure == WPAS_TEST_FAILURE_SCAN_TRIGGER)
+		return -EBUSY;
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (wpa_s->driver->scan2)
+		return wpa_s->driver->scan2(wpa_s->drv_priv, params);
+	return -1;
+}
+
+static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s,
+				     struct wpa_driver_scan_params *params,
+				     u32 interval)
+{
+	if (wpa_s->driver->sched_scan)
+		return wpa_s->driver->sched_scan(wpa_s->drv_priv,
+						 params, interval);
+	return -1;
+}
+
+static inline int wpa_drv_stop_sched_scan(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->stop_sched_scan)
+		return wpa_s->driver->stop_sched_scan(wpa_s->drv_priv);
+	return -1;
+}
+
+static inline struct wpa_scan_results * wpa_drv_get_scan_results2(
+	struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->get_scan_results2)
+		return wpa_s->driver->get_scan_results2(wpa_s->drv_priv);
+	return NULL;
+}
+
+static inline int wpa_drv_get_bssid(struct wpa_supplicant *wpa_s, u8 *bssid)
+{
+	if (wpa_s->driver->get_bssid) {
+		return wpa_s->driver->get_bssid(wpa_s->drv_priv, bssid);
+	}
+	return -1;
+}
+
+static inline int wpa_drv_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid)
+{
+	if (wpa_s->driver->get_ssid) {
+		return wpa_s->driver->get_ssid(wpa_s->drv_priv, ssid);
+	}
+	return -1;
+}
+
+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)
+{
+	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) {
+		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);
+	}
+	return -1;
+}
+
+static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s,
+				     const u8 *addr, int reason_code)
+{
+	if (wpa_s->driver->sta_deauth) {
+		return wpa_s->driver->sta_deauth(wpa_s->drv_priv,
+						 wpa_s->own_addr, addr,
+						 reason_code);
+	}
+	return -1;
+}
+
+static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s,
+					 const u8 *addr, int reason_code)
+{
+	if (wpa_s->driver->deauthenticate) {
+		return wpa_s->driver->deauthenticate(wpa_s->drv_priv, addr,
+						     reason_code);
+	}
+	return -1;
+}
+
+static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s,
+				    const u8 *bssid, const u8 *pmkid)
+{
+	if (wpa_s->driver->add_pmkid) {
+		return wpa_s->driver->add_pmkid(wpa_s->drv_priv, bssid, pmkid);
+	}
+	return -1;
+}
+
+static inline int wpa_drv_remove_pmkid(struct wpa_supplicant *wpa_s,
+				       const u8 *bssid, const u8 *pmkid)
+{
+	if (wpa_s->driver->remove_pmkid) {
+		return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, bssid,
+						   pmkid);
+	}
+	return -1;
+}
+
+static inline int wpa_drv_flush_pmkid(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->flush_pmkid) {
+		return wpa_s->driver->flush_pmkid(wpa_s->drv_priv);
+	}
+	return -1;
+}
+
+static inline int wpa_drv_get_capa(struct wpa_supplicant *wpa_s,
+				   struct wpa_driver_capa *capa)
+{
+	if (wpa_s->driver->get_capa) {
+		return wpa_s->driver->get_capa(wpa_s->drv_priv, capa);
+	}
+	return -1;
+}
+
+static inline void wpa_drv_poll(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->poll) {
+		wpa_s->driver->poll(wpa_s->drv_priv);
+	}
+}
+
+static inline const char * wpa_drv_get_ifname(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->get_ifname) {
+		return wpa_s->driver->get_ifname(wpa_s->drv_priv);
+	}
+	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) {
+		return wpa_s->driver->get_mac_addr(wpa_s->drv_priv);
+	}
+	return NULL;
+}
+
+static inline int wpa_drv_set_operstate(struct wpa_supplicant *wpa_s,
+					int state)
+{
+	if (wpa_s->driver->set_operstate)
+		return wpa_s->driver->set_operstate(wpa_s->drv_priv, state);
+	return 0;
+}
+
+static inline int wpa_drv_mlme_setprotection(struct wpa_supplicant *wpa_s,
+					     const u8 *addr, int protect_type,
+					     int key_type)
+{
+	if (wpa_s->driver->mlme_setprotection)
+		return wpa_s->driver->mlme_setprotection(wpa_s->drv_priv, addr,
+							 protect_type,
+							 key_type);
+	return 0;
+}
+
+static inline struct hostapd_hw_modes *
+wpa_drv_get_hw_feature_data(struct wpa_supplicant *wpa_s, u16 *num_modes,
+			    u16 *flags)
+{
+	if (wpa_s->driver->get_hw_feature_data)
+		return wpa_s->driver->get_hw_feature_data(wpa_s->drv_priv,
+							  num_modes, flags);
+	return NULL;
+}
+
+static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
+				      const char *alpha2)
+{
+	if (wpa_s->driver->set_country)
+		return wpa_s->driver->set_country(wpa_s->drv_priv, alpha2);
+	return 0;
+}
+
+static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
+				    const u8 *data, size_t data_len, int noack,
+				    unsigned int freq)
+{
+	if (wpa_s->driver->send_mlme)
+		return wpa_s->driver->send_mlme(wpa_s->drv_priv,
+						data, data_len, noack,
+						freq);
+	return -1;
+}
+
+static inline int wpa_drv_update_ft_ies(struct wpa_supplicant *wpa_s,
+					const u8 *md,
+					const u8 *ies, size_t ies_len)
+{
+	if (wpa_s->driver->update_ft_ies)
+		return wpa_s->driver->update_ft_ies(wpa_s->drv_priv, md,
+						    ies, ies_len);
+	return -1;
+}
+
+static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s,
+				 struct wpa_driver_ap_params *params)
+{
+	if (wpa_s->driver->set_ap)
+		return wpa_s->driver->set_ap(wpa_s->drv_priv, params);
+	return -1;
+}
+
+static inline int wpa_drv_sta_add(struct wpa_supplicant *wpa_s,
+				  struct hostapd_sta_add_params *params)
+{
+	if (wpa_s->driver->sta_add)
+		return wpa_s->driver->sta_add(wpa_s->drv_priv, params);
+	return -1;
+}
+
+static inline int wpa_drv_sta_remove(struct wpa_supplicant *wpa_s,
+				     const u8 *addr)
+{
+	if (wpa_s->driver->sta_remove)
+		return wpa_s->driver->sta_remove(wpa_s->drv_priv, addr);
+	return -1;
+}
+
+static inline int wpa_drv_hapd_send_eapol(struct wpa_supplicant *wpa_s,
+					  const u8 *addr, const u8 *data,
+					  size_t data_len, int encrypt,
+					  const u8 *own_addr, u32 flags)
+{
+	if (wpa_s->driver->hapd_send_eapol)
+		return wpa_s->driver->hapd_send_eapol(wpa_s->drv_priv, addr,
+						      data, data_len, encrypt,
+						      own_addr, flags);
+	return -1;
+}
+
+static inline int wpa_drv_sta_set_flags(struct wpa_supplicant *wpa_s,
+					const u8 *addr, int total_flags,
+					int flags_or, int flags_and)
+{
+	if (wpa_s->driver->sta_set_flags)
+		return wpa_s->driver->sta_set_flags(wpa_s->drv_priv, addr,
+						    total_flags, flags_or,
+						    flags_and);
+	return -1;
+}
+
+static inline int wpa_drv_set_supp_port(struct wpa_supplicant *wpa_s,
+					int authorized)
+{
+	if (wpa_s->driver->set_supp_port) {
+		return wpa_s->driver->set_supp_port(wpa_s->drv_priv,
+						    authorized);
+	}
+	return 0;
+}
+
+static inline int wpa_drv_send_action(struct wpa_supplicant *wpa_s,
+				      unsigned int freq,
+				      unsigned int wait,
+				      const u8 *dst, const u8 *src,
+				      const u8 *bssid,
+				      const u8 *data, size_t data_len,
+				      int no_cck)
+{
+	if (wpa_s->driver->send_action)
+		return wpa_s->driver->send_action(wpa_s->drv_priv, freq,
+						  wait, dst, src, bssid,
+						  data, data_len, no_cck);
+	return -1;
+}
+
+static inline void wpa_drv_send_action_cancel_wait(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->send_action_cancel_wait)
+		wpa_s->driver->send_action_cancel_wait(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_set_freq(struct wpa_supplicant *wpa_s,
+				   struct hostapd_freq_params *freq)
+{
+	if (wpa_s->driver->set_freq)
+		return wpa_s->driver->set_freq(wpa_s->drv_priv, freq);
+	return -1;
+}
+
+static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s,
+				 enum wpa_driver_if_type type,
+				 const char *ifname, const u8 *addr,
+				 void *bss_ctx, char *force_ifname,
+				 u8 *if_addr, const char *bridge)
+{
+	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, 0);
+	return -1;
+}
+
+static inline int wpa_drv_if_remove(struct wpa_supplicant *wpa_s,
+				    enum wpa_driver_if_type type,
+				    const char *ifname)
+{
+	if (wpa_s->driver->if_remove)
+		return wpa_s->driver->if_remove(wpa_s->drv_priv, type, ifname);
+	return -1;
+}
+
+static inline int wpa_drv_remain_on_channel(struct wpa_supplicant *wpa_s,
+					    unsigned int freq,
+					    unsigned int duration)
+{
+	if (wpa_s->driver->remain_on_channel)
+		return wpa_s->driver->remain_on_channel(wpa_s->drv_priv, freq,
+							duration);
+	return -1;
+}
+
+static inline int wpa_drv_cancel_remain_on_channel(
+	struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->cancel_remain_on_channel)
+		return wpa_s->driver->cancel_remain_on_channel(
+			wpa_s->drv_priv);
+	return -1;
+}
+
+static inline int wpa_drv_probe_req_report(struct wpa_supplicant *wpa_s,
+					   int report)
+{
+	if (wpa_s->driver->probe_req_report)
+		return wpa_s->driver->probe_req_report(wpa_s->drv_priv,
+						       report);
+	return -1;
+}
+
+static inline int wpa_drv_deinit_ap(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->deinit_ap)
+		return wpa_s->driver->deinit_ap(wpa_s->drv_priv);
+	return 0;
+}
+
+static inline int wpa_drv_deinit_p2p_cli(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->deinit_p2p_cli)
+		return wpa_s->driver->deinit_p2p_cli(wpa_s->drv_priv);
+	return 0;
+}
+
+static inline void wpa_drv_suspend(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->suspend)
+		wpa_s->driver->suspend(wpa_s->drv_priv);
+}
+
+static inline void wpa_drv_resume(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->resume)
+		wpa_s->driver->resume(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_signal_monitor(struct wpa_supplicant *wpa_s,
+					 int threshold, int hysteresis)
+{
+	if (wpa_s->driver->signal_monitor)
+		return wpa_s->driver->signal_monitor(wpa_s->drv_priv,
+						     threshold, hysteresis);
+	return -1;
+}
+
+static inline int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s,
+				      struct wpa_signal_info *si)
+{
+	if (wpa_s->driver->signal_poll)
+		return wpa_s->driver->signal_poll(wpa_s->drv_priv, si);
+	return -1;
+}
+
+static inline int wpa_drv_pktcnt_poll(struct wpa_supplicant *wpa_s,
+				      struct hostap_sta_driver_data *sta)
+{
+	if (wpa_s->driver->read_sta_data)
+		return wpa_s->driver->read_sta_data(wpa_s->drv_priv, sta,
+						    wpa_s->bssid);
+	return -1;
+}
+
+static inline int wpa_drv_set_ap_wps_ie(struct wpa_supplicant *wpa_s,
+					const struct wpabuf *beacon,
+					const struct wpabuf *proberesp,
+					const struct wpabuf *assocresp)
+{
+	if (!wpa_s->driver->set_ap_wps_ie)
+		return -1;
+	return wpa_s->driver->set_ap_wps_ie(wpa_s->drv_priv, beacon,
+					    proberesp, assocresp);
+}
+
+static inline int wpa_drv_get_noa(struct wpa_supplicant *wpa_s,
+				  u8 *buf, size_t buf_len)
+{
+	if (!wpa_s->driver->get_noa)
+		return -1;
+	return wpa_s->driver->get_noa(wpa_s->drv_priv, buf, buf_len);
+}
+
+static inline int wpa_drv_set_p2p_powersave(struct wpa_supplicant *wpa_s,
+					    int legacy_ps, int opp_ps,
+					    int ctwindow)
+{
+	if (!wpa_s->driver->set_p2p_powersave)
+		return -1;
+	return wpa_s->driver->set_p2p_powersave(wpa_s->drv_priv, legacy_ps,
+						opp_ps, ctwindow);
+}
+
+static inline int wpa_drv_ampdu(struct wpa_supplicant *wpa_s, int ampdu)
+{
+	if (!wpa_s->driver->ampdu)
+		return -1;
+	return wpa_s->driver->ampdu(wpa_s->drv_priv, ampdu);
+}
+
+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,
+					 u32 peer_capab, int initiator,
+					 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, peer_capab,
+						     initiator, buf, len);
+	}
+	return -1;
+}
+
+static inline int wpa_drv_tdls_oper(struct wpa_supplicant *wpa_s,
+				    enum tdls_oper oper, const u8 *peer)
+{
+	if (!wpa_s->driver->tdls_oper)
+		return -1;
+	return wpa_s->driver->tdls_oper(wpa_s->drv_priv, oper, peer);
+}
+
+#ifdef ANDROID
+static inline int wpa_drv_driver_cmd(struct wpa_supplicant *wpa_s,
+				     char *cmd, char *buf, size_t buf_len)
+{
+	if (!wpa_s->driver->driver_cmd)
+		return -1;
+	return wpa_s->driver->driver_cmd(wpa_s->drv_priv, cmd, buf, buf_len);
+}
+#endif /* ANDROID */
+
+static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s,
+					  const u8 *kek, size_t kek_len,
+					  const u8 *kck, size_t kck_len,
+					  const u8 *replay_ctr)
+{
+	if (!wpa_s->driver->set_rekey_info)
+		return;
+	wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kek_len,
+				      kck, kck_len, replay_ctr);
+}
+
+static inline int wpa_drv_radio_disable(struct wpa_supplicant *wpa_s,
+					int disabled)
+{
+	if (!wpa_s->driver->radio_disable)
+		return -1;
+	return wpa_s->driver->radio_disable(wpa_s->drv_priv, disabled);
+}
+
+static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s,
+					 struct csa_settings *settings)
+{
+	if (!wpa_s->driver->switch_channel)
+		return -1;
+	return wpa_s->driver->switch_channel(wpa_s->drv_priv, settings);
+}
+
+static inline int wpa_drv_add_ts(struct wpa_supplicant *wpa_s, u8 tsid,
+				 const u8 *address, u8 user_priority,
+				 u16 admitted_time)
+{
+	if (!wpa_s->driver->add_tx_ts)
+		return -1;
+	return wpa_s->driver->add_tx_ts(wpa_s->drv_priv, tsid, address,
+					user_priority, admitted_time);
+}
+
+static inline int wpa_drv_del_ts(struct wpa_supplicant *wpa_s, u8 tid,
+				 const u8 *address)
+{
+	if (!wpa_s->driver->del_tx_ts)
+		return -1;
+	return wpa_s->driver->del_tx_ts(wpa_s->drv_priv, tid, address);
+}
+
+static inline int wpa_drv_tdls_enable_channel_switch(
+	struct wpa_supplicant *wpa_s, const u8 *addr, u8 oper_class,
+	const struct hostapd_freq_params *freq_params)
+{
+	if (!wpa_s->driver->tdls_enable_channel_switch)
+		return -1;
+	return wpa_s->driver->tdls_enable_channel_switch(wpa_s->drv_priv, addr,
+							 oper_class,
+							 freq_params);
+}
+
+static inline int
+wpa_drv_tdls_disable_channel_switch(struct wpa_supplicant *wpa_s,
+				    const u8 *addr)
+{
+	if (!wpa_s->driver->tdls_disable_channel_switch)
+		return -1;
+	return wpa_s->driver->tdls_disable_channel_switch(wpa_s->drv_priv,
+							  addr);
+}
+
+static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s,
+				   enum wnm_oper oper, const u8 *peer,
+				   u8 *buf, u16 *buf_len)
+{
+	if (!wpa_s->driver->wnm_oper)
+		return -1;
+	return wpa_s->driver->wnm_oper(wpa_s->drv_priv, oper, peer, buf,
+				       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);
+}
+
+static inline int wpa_drv_roaming(struct wpa_supplicant *wpa_s, int allowed,
+				  const u8 *bssid)
+{
+	if (!wpa_s->driver->roaming)
+		return -1;
+	return wpa_s->driver->roaming(wpa_s->drv_priv, allowed, bssid);
+}
+
+static inline int wpa_drv_set_mac_addr(struct wpa_supplicant *wpa_s,
+				       const u8 *addr)
+{
+	if (!wpa_s->driver->set_mac_addr)
+		return -1;
+	return wpa_s->driver->set_mac_addr(wpa_s->drv_priv, addr);
+}
+
+
+#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 */
+
+static inline int wpa_drv_setband(struct wpa_supplicant *wpa_s,
+				  enum set_band band)
+{
+	if (!wpa_s->driver->set_band)
+		return -1;
+	return wpa_s->driver->set_band(wpa_s->drv_priv, band);
+}
+
+static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s,
+					     enum wpa_driver_if_type if_type,
+					     unsigned int *num,
+					     unsigned int *freq_list)
+{
+	if (!wpa_s->driver->get_pref_freq_list)
+		return -1;
+	return wpa_s->driver->get_pref_freq_list(wpa_s->drv_priv, if_type,
+						 num, freq_list);
+}
+
+static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s,
+					     unsigned int freq)
+{
+	if (!wpa_s->driver->set_prob_oper_freq)
+		return 0;
+	return wpa_s->driver->set_prob_oper_freq(wpa_s->drv_priv, freq);
+}
+
+#endif /* DRIVER_I_H */
diff --git a/hostap/wpa_supplicant/eap_proxy_dummy.mak b/hostap/wpa_supplicant/eap_proxy_dummy.mak
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hostap/wpa_supplicant/eap_proxy_dummy.mak
diff --git a/hostap/wpa_supplicant/eap_proxy_dummy.mk b/hostap/wpa_supplicant/eap_proxy_dummy.mk
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hostap/wpa_supplicant/eap_proxy_dummy.mk
diff --git a/hostap/wpa_supplicant/eap_register.c b/hostap/wpa_supplicant/eap_register.c
new file mode 100644
index 0000000..ece5716
--- /dev/null
+++ b/hostap/wpa_supplicant/eap_register.c
@@ -0,0 +1,261 @@
+/*
+ * EAP method registration
+ * 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 "common.h"
+#include "eap_peer/eap_methods.h"
+#include "eap_server/eap_methods.h"
+#include "wpa_supplicant_i.h"
+
+
+/**
+ * eap_register_methods - Register statically linked EAP methods
+ * Returns: 0 on success, -1 or -2 on failure
+ *
+ * This function is called at program initialization to register all EAP
+ * methods that were linked in statically.
+ */
+int eap_register_methods(void)
+{
+	int ret = 0;
+
+#ifdef EAP_MD5
+	if (ret == 0)
+		ret = eap_peer_md5_register();
+#endif /* EAP_MD5 */
+
+#ifdef EAP_TLS
+	if (ret == 0)
+		ret = eap_peer_tls_register();
+#endif /* EAP_TLS */
+
+#ifdef EAP_UNAUTH_TLS
+	if (ret == 0)
+		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();
+#endif /* EAP_MSCHAPv2 */
+
+#ifdef EAP_PEAP
+	if (ret == 0)
+		ret = eap_peer_peap_register();
+#endif /* EAP_PEAP */
+
+#ifdef EAP_TTLS
+	if (ret == 0)
+		ret = eap_peer_ttls_register();
+#endif /* EAP_TTLS */
+
+#ifdef EAP_GTC
+	if (ret == 0)
+		ret = eap_peer_gtc_register();
+#endif /* EAP_GTC */
+
+#ifdef EAP_OTP
+	if (ret == 0)
+		ret = eap_peer_otp_register();
+#endif /* EAP_OTP */
+
+#ifdef EAP_SIM
+	if (ret == 0)
+		ret = eap_peer_sim_register();
+#endif /* EAP_SIM */
+
+#ifdef EAP_LEAP
+	if (ret == 0)
+		ret = eap_peer_leap_register();
+#endif /* EAP_LEAP */
+
+#ifdef EAP_PSK
+	if (ret == 0)
+		ret = eap_peer_psk_register();
+#endif /* EAP_PSK */
+
+#ifdef EAP_AKA
+	if (ret == 0)
+		ret = eap_peer_aka_register();
+#endif /* EAP_AKA */
+
+#ifdef EAP_AKA_PRIME
+	if (ret == 0)
+		ret = eap_peer_aka_prime_register();
+#endif /* EAP_AKA_PRIME */
+
+#ifdef EAP_FAST
+	if (ret == 0)
+		ret = eap_peer_fast_register();
+#endif /* EAP_FAST */
+
+#ifdef EAP_PAX
+	if (ret == 0)
+		ret = eap_peer_pax_register();
+#endif /* EAP_PAX */
+
+#ifdef EAP_SAKE
+	if (ret == 0)
+		ret = eap_peer_sake_register();
+#endif /* EAP_SAKE */
+
+#ifdef EAP_GPSK
+	if (ret == 0)
+		ret = eap_peer_gpsk_register();
+#endif /* EAP_GPSK */
+
+#ifdef EAP_WSC
+	if (ret == 0)
+		ret = eap_peer_wsc_register();
+#endif /* EAP_WSC */
+
+#ifdef EAP_IKEV2
+	if (ret == 0)
+		ret = eap_peer_ikev2_register();
+#endif /* EAP_IKEV2 */
+
+#ifdef EAP_VENDOR_TEST
+	if (ret == 0)
+		ret = eap_peer_vendor_test_register();
+#endif /* EAP_VENDOR_TEST */
+
+#ifdef EAP_TNC
+	if (ret == 0)
+		ret = eap_peer_tnc_register();
+#endif /* EAP_TNC */
+
+#ifdef EAP_PWD
+	if (ret == 0)
+		ret = eap_peer_pwd_register();
+#endif /* EAP_PWD */
+
+#ifdef EAP_EKE
+	if (ret == 0)
+		ret = eap_peer_eke_register();
+#endif /* EAP_EKE */
+
+#ifdef EAP_SERVER_IDENTITY
+	if (ret == 0)
+		ret = eap_server_identity_register();
+#endif /* EAP_SERVER_IDENTITY */
+
+#ifdef EAP_SERVER_MD5
+	if (ret == 0)
+		ret = eap_server_md5_register();
+#endif /* EAP_SERVER_MD5 */
+
+#ifdef EAP_SERVER_TLS
+	if (ret == 0)
+		ret = eap_server_tls_register();
+#endif /* EAP_SERVER_TLS */
+
+#ifdef EAP_SERVER_UNAUTH_TLS
+	if (ret == 0)
+		ret = eap_server_unauth_tls_register();
+#endif /* EAP_SERVER_UNAUTH_TLS */
+
+#ifdef EAP_SERVER_MSCHAPV2
+	if (ret == 0)
+		ret = eap_server_mschapv2_register();
+#endif /* EAP_SERVER_MSCHAPV2 */
+
+#ifdef EAP_SERVER_PEAP
+	if (ret == 0)
+		ret = eap_server_peap_register();
+#endif /* EAP_SERVER_PEAP */
+
+#ifdef EAP_SERVER_TLV
+	if (ret == 0)
+		ret = eap_server_tlv_register();
+#endif /* EAP_SERVER_TLV */
+
+#ifdef EAP_SERVER_GTC
+	if (ret == 0)
+		ret = eap_server_gtc_register();
+#endif /* EAP_SERVER_GTC */
+
+#ifdef EAP_SERVER_TTLS
+	if (ret == 0)
+		ret = eap_server_ttls_register();
+#endif /* EAP_SERVER_TTLS */
+
+#ifdef EAP_SERVER_SIM
+	if (ret == 0)
+		ret = eap_server_sim_register();
+#endif /* EAP_SERVER_SIM */
+
+#ifdef EAP_SERVER_AKA
+	if (ret == 0)
+		ret = eap_server_aka_register();
+#endif /* EAP_SERVER_AKA */
+
+#ifdef EAP_SERVER_AKA_PRIME
+	if (ret == 0)
+		ret = eap_server_aka_prime_register();
+#endif /* EAP_SERVER_AKA_PRIME */
+
+#ifdef EAP_SERVER_PAX
+	if (ret == 0)
+		ret = eap_server_pax_register();
+#endif /* EAP_SERVER_PAX */
+
+#ifdef EAP_SERVER_PSK
+	if (ret == 0)
+		ret = eap_server_psk_register();
+#endif /* EAP_SERVER_PSK */
+
+#ifdef EAP_SERVER_SAKE
+	if (ret == 0)
+		ret = eap_server_sake_register();
+#endif /* EAP_SERVER_SAKE */
+
+#ifdef EAP_SERVER_GPSK
+	if (ret == 0)
+		ret = eap_server_gpsk_register();
+#endif /* EAP_SERVER_GPSK */
+
+#ifdef EAP_SERVER_VENDOR_TEST
+	if (ret == 0)
+		ret = eap_server_vendor_test_register();
+#endif /* EAP_SERVER_VENDOR_TEST */
+
+#ifdef EAP_SERVER_FAST
+	if (ret == 0)
+		ret = eap_server_fast_register();
+#endif /* EAP_SERVER_FAST */
+
+#ifdef EAP_SERVER_WSC
+	if (ret == 0)
+		ret = eap_server_wsc_register();
+#endif /* EAP_SERVER_WSC */
+
+#ifdef EAP_SERVER_IKEV2
+	if (ret == 0)
+		ret = eap_server_ikev2_register();
+#endif /* EAP_SERVER_IKEV2 */
+
+#ifdef EAP_SERVER_TNC
+	if (ret == 0)
+		ret = eap_server_tnc_register();
+#endif /* EAP_SERVER_TNC */
+
+#ifdef EAP_SERVER_PWD
+	if (ret == 0)
+		ret = eap_server_pwd_register();
+#endif /* EAP_SERVER_PWD */
+
+	return ret;
+}
diff --git a/hostap/wpa_supplicant/eap_testing.txt b/hostap/wpa_supplicant/eap_testing.txt
new file mode 100644
index 0000000..8d13222
--- /dev/null
+++ b/hostap/wpa_supplicant/eap_testing.txt
@@ -0,0 +1,392 @@
+Automatic regression and interoperability testing of wpa_supplicant's
+IEEE 802.1X/EAPOL authentication
+
+Test program:
+- Linked some parts of IEEE 802.1X Authenticator implementation from
+  hostapd (RADIUS client and RADIUS processing, EAP<->RADIUS
+  encapsulation/decapsulation) into wpa_supplicant.
+- Replaced wpa_supplicant.c and wpa.c with test code that trigger
+  IEEE 802.1X authentication automatically without need for wireless
+  client card or AP.
+- For EAP methods that generate keying material, the key derived by the
+  Supplicant is verified to match with the one received by the (now
+  integrated) Authenticator.
+
+The full automated test suite can now be run in couple of seconds, but
+I'm more than willing to add new RADIUS authentication servers to make
+this take a bit more time.. ;-) As an extra bonus, this can also be
+seen as automatic regression/interoperability testing for the RADIUS
+server, too.
+
+In order for me to be able to use a new authentication server, the
+server need to be available from Internet (at least from one static IP
+address) and I will need to get suitable user name/password pairs,
+certificates, and private keys for testing use. Other alternative
+would be to get an evaluation version of the server so that I can
+install it on my own test setup. If you are interested in providing
+either server access or evaluation version, please contact me
+(j@w1.fi).
+
+
+Test matrix
+
++) tested successfully
+F) failed
+-) server did not support
+?) not tested
+
+Cisco ACS ----------------------------------------------------------.
+hostapd --------------------------------------------------------.   |
+Cisco Aironet 1200 AP (local RADIUS server) ----------------.   |   |
+Periodik Labs Elektron ---------------------------------.   |   |   |
+Lucent NavisRadius ---------------------------------.   |   |   |   |
+Interlink RAD-Series ---------------------------.   |   |   |   |   |
+Radiator -----------------------------------.   |   |   |   |   |   |
+Meetinghouse Aegis ---------------------.   |   |   |   |   |   |   |
+Funk Steel-Belted ------------------.   |   |   |   |   |   |   |   |
+Funk Odyssey -------------------.   |   |   |   |   |   |   |   |   |
+Microsoft IAS --------------.   |   |   |   |   |   |   |   |   |   |
+FreeRADIUS -------------.   |   |   |   |   |   |   |   |   |   |   |
+			|   |   |   |   |   |   |   |   |   |   |   |
+
+EAP-MD5			+   -   -   +   +   +   +   +   -   -   +   +
+EAP-GTC			+   -   -   ?   +   +   +   +   -   -   +   -
+EAP-OTP			-   -   -   -   -   +   -   -   -   -   -   -
+EAP-MSCHAPv2		+   -   -   +   +   +   +   +   -   -   +   -
+EAP-TLS			+   +   +   +   +   +   +   +   -   -   +   +
+EAP-PEAPv0/MSCHAPv2	+   +   +   +   +   +   +   +   +   -   +   +
+EAP-PEAPv0/GTC		+   -   +   -   +   +   +   +   -   -   +   +
+EAP-PEAPv0/OTP		-   -   -   -   -   +   -   -   -   -   -   -
+EAP-PEAPv0/MD5		+   -   -   +   +   +   +   +   -   -   +   -
+EAP-PEAPv0/TLS		+   +   -   +   +   +   F   +   -   -   +   +
+EAP-PEAPv0/SIM		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv0/AKA		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv0/PSK		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv0/PAX		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv0/SAKE		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv0/GPSK		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv1/MSCHAPv2	-   -   +   +   +   +1  +   +5  +8  -   +   +
+EAP-PEAPv1/GTC		-   -   +   +   +   +1  +   +5  +8  -   +   +
+EAP-PEAPv1/OTP		-   -   -   -   -   +1  -   -   -   -   -   -
+EAP-PEAPv1/MD5		-   -   -   +   +   +1  +   +5  -   -   +   -
+EAP-PEAPv1/TLS		-   -   -   +   +   +1  F   +5  -   -   +   +
+EAP-PEAPv1/SIM		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv1/AKA		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv1/PSK		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv1/PAX		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv1/SAKE		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv1/GPSK		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-TTLS/CHAP		+   -   +2  +   +   +   +   +   +   -   +   -
+EAP-TTLS/MSCHAP		+   -   +   +   +   +   +   +   +   -   +   -
+EAP-TTLS/MSCHAPv2	+   -   +   +   +   +   +   +   +   -   +   -
+EAP-TTLS/PAP		+   -   +   +   +   +   +   +   +   -   +   -
+EAP-TTLS/EAP-MD5	+   -   +2  +   +   +   +   +   +   -   +   -
+EAP-TTLS/EAP-GTC	+   -   +2  ?   +   +   +   +   -   -   +   -
+EAP-TTLS/EAP-OTP	-   -   -   -   -   +   -   -   -   -   -   -
+EAP-TTLS/EAP-MSCHAPv2	+   -   +2  +   +   +   +   +   +   -   +   -
+EAP-TTLS/EAP-TLS	+   -   +2  +   F   +   +   +   -   -   +   -
+EAP-TTLS/EAP-SIM	-   -   -   -   -   -   -   -   -   -   +   -
+EAP-TTLS/EAP-AKA	-   -   -   -   -   -   -   -   -   -   +   -
+EAP-TTLS/EAP-PSK	-   -   -   -   -   -   -   -   -   -   +   -
+EAP-TTLS/EAP-PAX	-   -   -   -   -   -   -   -   -   -   +   -
+EAP-TTLS/EAP-SAKE	-   -   -   -   -   -   -   -   -   -   +   -
+EAP-TTLS/EAP-GPSK	-   -   -   -   -   -   -   -   -   -   +   -
+EAP-TTLS + TNC		-   -   -   -   -   +   -   -   -   -   +   -
+EAP-SIM			+   -   -   ?   -   +   -   ?   -   -   +   -
+EAP-AKA			-   -   -   -   -   +   -   -   -   -   +   -
+EAP-AKA'		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-PSK			+7  -   -   -   -   +   -   -   -   -   +   -
+EAP-PAX			-   -   -   -   -   +   -   -   -   -   +   -
+EAP-SAKE		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-GPSK		-   -   -   -   -   -   -   -   -   -   +   -
+EAP-FAST/MSCHAPv2(prov)	-   -   -   +   -   +   -   -   -   +   +   +
+EAP-FAST/GTC(auth)	-   -   -   +   -   +   -   -   -   +   +   +
+EAP-FAST/MSCHAPv2(aprov)-   -   -   -   -   +   -   -   -   -   +   +
+EAP-FAST/GTC(aprov)	-   -   -   -   -   +   -   -   -   -   +   +
+EAP-FAST/MD5(aprov)	-   -   -   -   -   +   -   -   -   -   +   -
+EAP-FAST/TLS(aprov)	-   -   -   -   -   -   -   -   -   -   +   +
+EAP-FAST/SIM(aprov)	-   -   -   -   -   -   -   -   -   -   +   -
+EAP-FAST/AKA(aprov)	-   -   -   -   -   -   -   -   -   -   +   -
+EAP-FAST/MSCHAPv2(auth)	-   -   -   -   -   +   -   -   -   -   +   +
+EAP-FAST/MD5(auth)	-   -   -   -   -   +   -   -   -   -   +   -
+EAP-FAST/TLS(auth)	-   -   -   -   -   -   -   -   -   -   +   +
+EAP-FAST/SIM(auth)	-   -   -   -   -   -   -   -   -   -   +   -
+EAP-FAST/AKA(auth)	-   -   -   -   -   -   -   -   -   -   +   -
+EAP-FAST + TNC		-   -   -   -   -   -   -   -   -   -   +   -
+LEAP			+   -   +   +   +   +   F   +6  -   +   -   +
+EAP-TNC			+9  -   -   -   -   +   -   -   -   -   +   -
+EAP-IKEv2		+10 -   -   -   -   -   -   -   -   -   +   -
+
+1) PEAPv1 required new label, "client PEAP encryption" instead of "client EAP
+   encryption", during key derivation (requires phase1="peaplabel=1" in the
+   network configuration in wpa_supplicant.conf)
+2) used FreeRADIUS as inner auth server
+5) PEAPv1 required termination of negotiation on tunneled EAP-Success and new
+   label in key deriviation
+   (phase1="peap_outer_success=0 peaplabel=1") (in "IETF Draft 5" mode)
+6) Authenticator simulator required patching for handling Access-Accept within
+   negotiation (for the first EAP-Success of LEAP)
+7) tested only with an older (incompatible) draft of EAP-PSK; FreeRADIUS does
+   not support the current EAP-PSK (RFC) specification
+8) PEAPv1 used non-standard version negotiation (client had to force v1 even
+   though server reported v0 as the highest supported version)
+9) only EAP-TTLS/EAP-TNC tested, i.e., test did not include proper sequence of
+   client authentication followed by TNC inside the tunnel
+10) worked only with special compatibility code to match the IKEv2 server
+    implementation
+
+
+Automated tests:
+
+FreeRADIUS (2.0-beta/CVS snapshot)
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv0 / TLS
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / EAP-TLS
+- EAP-TTLS / CHAP
+- EAP-TTLS / PAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / EAP-TNC (partial support; no authentication sequence)
+- EAP-SIM
+- LEAP
+
+Microsoft Windows Server 2003 / IAS
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / TLS
+- EAP-MD5
+* IAS does not seem to support other EAP methods
+
+Funk Odyssey 2.01.00.653
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+  Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption"
+- EAP-TTLS / CHAP (using FreeRADIUS as inner auth srv)
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge (using FreeRADIUS as inner auth srv)
+- EAP-TTLS / EAP-GTC (using FreeRADIUS as inner auth srv)
+- EAP-TTLS / EAP-MSCHAPv2 (using FreeRADIUS as inner auth srv)
+- EAP-TTLS / EAP-TLS (using FreeRADIUS as inner auth srv)
+* not supported in Odyssey:
+  - EAP-MD5-Challenge
+  - EAP-GTC
+  - EAP-MSCHAPv2
+  - EAP-PEAP / MD5-Challenge
+  - EAP-PEAP / TLS
+
+Funk Steel-Belted Radius Enterprise Edition v4.71.739
+- EAP-MD5-Challenge
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / MD5
+- EAP-PEAPv0 / TLS
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / MD5
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / TLS
+  Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption"
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / EAP-TLS
+
+Meetinghouse Aegis 1.1.4
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / TLS
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / TLS
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / MD5-Challenge
+  Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption"
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-MSCHAPv2
+* did not work
+  - EAP-TTLS / EAP-TLS
+    (Server rejects authentication without any reason in debug log. It
+     looks like the inner TLS negotiation starts properly and the last
+     packet from Supplicant looks like the one sent in the Phase 1. The
+     server generates a valid looking reply in the same way as in Phase
+     1, but then ends up sending Access-Reject. Maybe an issue with TTLS
+     fragmentation in the Aegis server(?) The packet seems to include
+     1328 bytes of EAP-Message and this may go beyond the fragmentation
+     limit with AVP encapsulation and TLS tunneling. Note: EAP-PEAP/TLS
+     did work, so this issue seems to be with something TTLS specific.)
+
+Radiator 3.17.1 (eval, with all patches up to and including 2007-05-25)
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-OTP
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / OTP
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv0 / TLS
+  Note: Needed to use unknown identity in outer auth and some times the server
+	seems to get confused and fails to send proper Phase 2 data.
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / OTP
+- EAP-PEAPv1 / MD5-Challenge
+- EAP-PEAPv1 / TLS
+  Note: This has some additional requirements for EAPTLS_MaxFragmentSize.
+        Using 1300 for outer auth and 500 for inner auth seemed to work.
+  Note: Needed to use unknown identity in outer auth and some times the server
+	seems to get confused and fails to send proper Phase 2 data.
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-OTP
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / EAP-TLS
+  Note: This has some additional requirements for EAPTLS_MaxFragmentSize.
+        Using 1300 for outer auth and 500 for inner auth seemed to work.
+- EAP-SIM
+- EAP-AKA
+- EAP-PSK
+- EAP-PAX
+- EAP-TNC
+
+Interlink Networks RAD-Series 6.1.2.7
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / MD5-Challenge
+  Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption"
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / EAP-TLS
+* did not work
+  - EAP-PEAPv0 / TLS
+  - EAP-PEAPv1 / TLS
+    (Failed to decrypt Phase 2 data)
+
+Lucent NavisRadius 4.4.0
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / TLS
+- EAP-PEAPv1 / MD5-Challenge
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / TLS
+  "IETF Draft 5" mode requires phase1="peap_outer_success=0 peaplabel=1"
+  'Cisco ACU 5.05' mode works without phase1 configuration
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-TLS
+
+Note: user certificate from NavisRadius had private key in a format
+that wpa_supplicant could not use. Converting this to PKCS#12 and then
+back to PEM allowed wpa_supplicant to use the key.
+
+
+hostapd v0.3.3
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / MD5-Challenge
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-SIM
+- EAP-PAX
+
+PEAPv1:
+
+Funk Odyssey 2.01.00.653:
+- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE
+  keys with outer EAP-Success message after this
+- uses label "client EAP encryption"
+- (peap_outer_success 1 and 2 work)
+
+Funk Steel-Belted Radius Enterprise Edition v4.71.739
+- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE
+  keys with outer EAP-Success message after this
+- uses label "client EAP encryption"
+- (peap_outer_success 1 and 2 work)
+
+Radiator 3.9:
+- uses TLV Success and Reply, sends MPPE keys with outer EAP-Success message
+  after this
+- uses label "client PEAP encryption"
+
+Lucent NavisRadius 4.4.0 (in "IETF Draft 5" mode):
+- sends tunneled EAP-Success with MPPE keys and expects the authentication to
+  terminate at this point (gets somewhat confused with reply to this)
+- uses label "client PEAP encryption"
+- phase1="peap_outer_success=0 peaplabel=1"
+
+Lucent NavisRadius 4.4.0 (in "Cisco ACU 5.05" mode):
+- sends tunneled EAP-Success with MPPE keys and expects to receive TLS ACK
+  as a reply
+- uses label "client EAP encryption"
+
+Meetinghouse Aegis 1.1.4
+- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE
+  keys with outer EAP-Success message after this
+- uses label "client EAP encryption"
+- peap_outer_success 1 and 2 work
diff --git a/hostap/wpa_supplicant/eapol_test.c b/hostap/wpa_supplicant/eapol_test.c
new file mode 100644
index 0000000..dce7d1f
--- /dev/null
+++ b/hostap/wpa_supplicant/eapol_test.c
@@ -0,0 +1,1547 @@
+/*
+ * WPA Supplicant - test code
+ * 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.
+ *
+ * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c.
+ * Not used in production version.
+ */
+
+#include "includes.h"
+#include <assert.h>
+
+#include "common.h"
+#include "utils/ext_password.h"
+#include "config.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "eap_peer/eap.h"
+#include "eap_server/eap_methods.h"
+#include "eloop.h"
+#include "utils/base64.h"
+#include "rsn_supp/wpa.h"
+#include "wpa_supplicant_i.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "common/wpa_ctrl.h"
+#include "ctrl_iface.h"
+#include "pcsc_funcs.h"
+#include "wpas_glue.h"
+
+
+const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
+
+
+struct extra_radius_attr {
+	u8 type;
+	char syntax;
+	char *data;
+	struct extra_radius_attr *next;
+};
+
+struct eapol_test_data {
+	struct wpa_supplicant *wpa_s;
+
+	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;
+	struct in_addr own_ip_addr;
+	struct radius_client_data *radius;
+	struct hostapd_radius_servers *radius_conf;
+
+	 /* last received EAP Response from Authentication Server */
+	struct wpabuf *last_eap_radius;
+
+	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;
+
+	u8 *eap_identity;
+	size_t eap_identity_len;
+
+	char *connect_info;
+	u8 own_addr[ETH_ALEN];
+	struct extra_radius_attr *extra_attrs;
+
+	FILE *server_cert_file;
+
+	const char *pcsc_reader;
+	const char *pcsc_pin;
+
+	unsigned int ctrl_iface:1;
+	unsigned int id_req_sent:1;
+};
+
+static struct eapol_test_data eapol_test;
+
+
+static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx);
+
+
+static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
+			      int level, const char *txt, size_t len)
+{
+	if (addr)
+		wpa_printf(MSG_DEBUG, "STA " MACSTR ": %s\n",
+			   MAC2STR(addr), txt);
+	else
+		wpa_printf(MSG_DEBUG, "%s", txt);
+}
+
+
+static int add_extra_attr(struct radius_msg *msg,
+			  struct extra_radius_attr *attr)
+{
+	size_t len;
+	char *pos;
+	u32 val;
+	char buf[RADIUS_MAX_ATTR_LEN + 1];
+
+	switch (attr->syntax) {
+	case 's':
+		os_snprintf(buf, sizeof(buf), "%s", attr->data);
+		len = os_strlen(buf);
+		break;
+	case 'n':
+		buf[0] = '\0';
+		len = 1;
+		break;
+	case 'x':
+		pos = attr->data;
+		if (pos[0] == '0' && pos[1] == 'x')
+			pos += 2;
+		len = os_strlen(pos);
+		if ((len & 1) || (len / 2) > RADIUS_MAX_ATTR_LEN) {
+			printf("Invalid extra attribute hexstring\n");
+			return -1;
+		}
+		len /= 2;
+		if (hexstr2bin(pos, (u8 *) buf, len) < 0) {
+			printf("Invalid extra attribute hexstring\n");
+			return -1;
+		}
+		break;
+	case 'd':
+		val = htonl(atoi(attr->data));
+		os_memcpy(buf, &val, 4);
+		len = 4;
+		break;
+	default:
+		printf("Incorrect extra attribute syntax specification\n");
+		return -1;
+	}
+
+	if (!radius_msg_add_attr(msg, attr->type, (u8 *) buf, len)) {
+		printf("Could not add attribute %d\n", attr->type);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int add_extra_attrs(struct radius_msg *msg,
+			   struct extra_radius_attr *attrs)
+{
+	struct extra_radius_attr *p;
+	for (p = attrs; p; p = p->next) {
+		if (add_extra_attr(msg, p) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+
+static struct extra_radius_attr *
+find_extra_attr(struct extra_radius_attr *attrs, u8 type)
+{
+	struct extra_radius_attr *p;
+	for (p = attrs; p; p = p->next) {
+		if (p->type == type)
+			return p;
+	}
+	return NULL;
+}
+
+
+static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e,
+					  const u8 *eap, size_t len)
+{
+	struct radius_msg *msg;
+	char buf[RADIUS_MAX_ATTR_LEN + 1];
+	const struct eap_hdr *hdr;
+	const u8 *pos;
+
+	wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS "
+		   "packet");
+
+	e->radius_identifier = radius_client_get_id(e->radius);
+	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
+			     e->radius_identifier);
+	if (msg == NULL) {
+		printf("Could not create net RADIUS packet\n");
+		return;
+	}
+
+	radius_msg_make_authenticator(msg, (u8 *) e, sizeof(*e));
+
+	hdr = (const struct eap_hdr *) eap;
+	pos = (const u8 *) (hdr + 1);
+	if (len > sizeof(*hdr) && hdr->code == EAP_CODE_RESPONSE &&
+	    pos[0] == EAP_TYPE_IDENTITY) {
+		pos++;
+		os_free(e->eap_identity);
+		e->eap_identity_len = len - sizeof(*hdr) - 1;
+		e->eap_identity = os_malloc(e->eap_identity_len);
+		if (e->eap_identity) {
+			os_memcpy(e->eap_identity, pos, e->eap_identity_len);
+			wpa_hexdump(MSG_DEBUG, "Learned identity from "
+				    "EAP-Response-Identity",
+				    e->eap_identity, e->eap_identity_len);
+		}
+	}
+
+	if (e->eap_identity &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
+				 e->eap_identity, e->eap_identity_len)) {
+		printf("Could not add User-Name\n");
+		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)) {
+		printf("Could not add NAS-IP-Address\n");
+		goto fail;
+	}
+
+	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+		    MAC2STR(e->wpa_s->own_addr));
+	if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CALLING_STATION_ID)
+	    &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+				 (u8 *) buf, os_strlen(buf))) {
+		printf("Could not add Calling-Station-Id\n");
+		goto fail;
+	}
+
+	/* TODO: should probably check MTU from driver config; 2304 is max for
+	 * IEEE 802.11, but use 1400 to avoid problems with too large packets
+	 */
+	if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_FRAMED_MTU) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
+		printf("Could not add Framed-MTU\n");
+		goto fail;
+	}
+
+	if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_PORT_TYPE) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
+				       RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
+		printf("Could not add NAS-Port-Type\n");
+		goto fail;
+	}
+
+	os_snprintf(buf, sizeof(buf), "%s", e->connect_info);
+	if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CONNECT_INFO) &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+				 (u8 *) buf, os_strlen(buf))) {
+		printf("Could not add Connect-Info\n");
+		goto fail;
+	}
+
+	if (add_extra_attrs(msg, e->extra_attrs) < 0)
+		goto fail;
+
+	if (eap && !radius_msg_add_eap(msg, eap, len)) {
+		printf("Could not add EAP-Message\n");
+		goto fail;
+	}
+
+	/* State attribute must be copied if and only if this packet is
+	 * Access-Request reply to the previous Access-Challenge */
+	if (e->last_recv_radius &&
+	    radius_msg_get_hdr(e->last_recv_radius)->code ==
+	    RADIUS_CODE_ACCESS_CHALLENGE) {
+		int res = radius_msg_copy_attr(msg, e->last_recv_radius,
+					       RADIUS_ATTR_STATE);
+		if (res < 0) {
+			printf("Could not copy State attribute from previous "
+			       "Access-Challenge\n");
+			goto fail;
+		}
+		if (res > 0) {
+			wpa_printf(MSG_DEBUG, "  Copied RADIUS State "
+				   "Attribute");
+		}
+	}
+
+	if (radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr)
+	    < 0)
+		goto fail;
+	return;
+
+ fail:
+	radius_msg_free(msg);
+}
+
+
+static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf,
+				 size_t len)
+{
+	printf("WPA: eapol_test_eapol_send(type=%d len=%lu)\n",
+	       type, (unsigned long) len);
+	if (type == IEEE802_1X_TYPE_EAP_PACKET) {
+		wpa_hexdump(MSG_DEBUG, "TX EAP -> RADIUS", buf, len);
+		ieee802_1x_encapsulate_radius(&eapol_test, buf, len);
+	}
+	return 0;
+}
+
+
+static void eapol_test_set_config_blob(void *ctx,
+				       struct wpa_config_blob *blob)
+{
+	struct eapol_test_data *e = ctx;
+	wpa_config_set_blob(e->wpa_s->conf, blob);
+}
+
+
+static const struct wpa_config_blob *
+eapol_test_get_config_blob(void *ctx, const char *name)
+{
+	struct eapol_test_data *e = ctx;
+	return wpa_config_get_blob(e->wpa_s->conf, name);
+}
+
+
+static void eapol_test_eapol_done_cb(void *ctx)
+{
+	struct eapol_test_data *e = ctx;
+
+	printf("WPA: EAPOL processing complete\n");
+	wpa_supplicant_cancel_auth_timeout(e->wpa_s);
+	wpa_supplicant_set_state(e->wpa_s, WPA_COMPLETED);
+}
+
+
+static void eapol_sm_reauth(void *eloop_ctx, void *timeout_ctx)
+{
+	struct eapol_test_data *e = eloop_ctx;
+	printf("\n\n\n\n\neapol_test: Triggering EAP reauthentication\n\n");
+	e->radius_access_accept_received = 0;
+	send_eap_request_identity(e->wpa_s, NULL);
+}
+
+
+static int eapol_test_compare_pmk(struct eapol_test_data *e)
+{
+	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);
+		if (os_memcmp(pmk, e->authenticator_pmk, PMK_LEN) != 0) {
+			printf("WARNING: PMK mismatch\n");
+			wpa_hexdump(MSG_DEBUG, "PMK from AS",
+				    e->authenticator_pmk, PMK_LEN);
+		} else if (e->radius_access_accept_received)
+			ret = 0;
+	} else if (e->authenticator_pmk_len == 16 &&
+		   eapol_sm_get_key(e->wpa_s->eapol, pmk, 16) == 0) {
+		wpa_hexdump(MSG_DEBUG, "LEAP PMK from EAPOL", pmk, 16);
+		if (os_memcmp(pmk, e->authenticator_pmk, 16) != 0) {
+			printf("WARNING: PMK mismatch\n");
+			wpa_hexdump(MSG_DEBUG, "PMK from AS",
+				    e->authenticator_pmk, 16);
+		} else if (e->radius_access_accept_received)
+			ret = 0;
+	} else if (e->radius_access_accept_received && e->no_mppe_keys) {
+		/* No keying material expected */
+		ret = 0;
+	}
+
+	if (ret && !e->no_mppe_keys)
+		e->num_mppe_mismatch++;
+	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, enum eapol_supp_result result,
+			void *ctx)
+{
+	struct eapol_test_data *e = ctx;
+	printf("eapol_sm_cb: result=%d\n", result);
+	e->id_req_sent = 0;
+	if (e->ctrl_iface)
+		return;
+	e->eapol_test_num_reauths--;
+	if (e->eapol_test_num_reauths < 0)
+		eloop_terminate();
+	else {
+		eapol_test_compare_pmk(e);
+		eloop_register_timeout(0, 100000, eapol_sm_reauth, e, NULL);
+	}
+}
+
+
+static void eapol_test_write_cert(FILE *f, const char *subject,
+				  const struct wpabuf *cert)
+{
+	unsigned char *encoded;
+
+	encoded = base64_encode(wpabuf_head(cert), wpabuf_len(cert), NULL);
+	if (encoded == NULL)
+		return;
+	fprintf(f, "%s\n-----BEGIN CERTIFICATE-----\n%s"
+		"-----END CERTIFICATE-----\n\n", subject, encoded);
+	os_free(encoded);
+}
+
+
+#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 (os_snprintf_error(buflen, len)) {
+		os_free(buf);
+		return;
+	}
+	if (ssid->ssid && buflen > len + ssid->ssid_len) {
+		os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
+		len += ssid->ssid_len;
+		buf[len] = '\0';
+	}
+	buf[buflen - 1] = '\0';
+	wpa_msg(wpa_s, MSG_INFO, "%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 *altsubject[], int num_altsubject,
+			       const char *cert_hash,
+			       const struct wpabuf *cert)
+{
+	struct eapol_test_data *e = ctx;
+
+	wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
+		"depth=%d subject='%s'%s%s",
+		depth, subject,
+		cert_hash ? " hash=" : "",
+		cert_hash ? cert_hash : "");
+
+	if (cert) {
+		char *cert_hex;
+		size_t len = wpabuf_len(cert) * 2 + 1;
+		cert_hex = os_malloc(len);
+		if (cert_hex) {
+			wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert),
+					 wpabuf_len(cert));
+			wpa_msg_ctrl(e->wpa_s, MSG_INFO,
+				     WPA_EVENT_EAP_PEER_CERT
+				     "depth=%d subject='%s' cert=%s",
+				     depth, subject, cert_hex);
+			os_free(cert_hex);
+		}
+
+		if (e->server_cert_file)
+			eapol_test_write_cert(e->server_cert_file,
+					      subject, cert);
+	}
+
+	if (altsubject) {
+		int i;
+
+		for (i = 0; i < num_altsubject; i++)
+			wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT
+				"depth=%d %s", depth, altsubject[i]);
+	}
+}
+
+
+static void eapol_test_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+	struct eapol_test_data *e = ctx;
+	struct wpa_supplicant *wpa_s = e->wpa_s;
+	char *str;
+	int res;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity",
+			  id, len);
+
+	if (wpa_s->current_ssid == NULL)
+		return;
+
+	if (id == NULL) {
+		if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+				   "NULL", 0) < 0)
+			return;
+	} else {
+		str = os_malloc(len * 2 + 1);
+		if (str == NULL)
+			return;
+		wpa_snprintf_hex(str, len * 2 + 1, id, len);
+		res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+				     str, 0);
+		os_free(str);
+		if (res < 0)
+			return;
+	}
+}
+
+
+static enum wpa_states eapol_test_get_state(void *ctx)
+{
+	struct eapol_test_data *e = ctx;
+	struct wpa_supplicant *wpa_s = e->wpa_s;
+
+	return wpa_s->wpa_state;
+}
+
+
+static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
+		      struct wpa_ssid *ssid)
+{
+	struct eapol_config eapol_conf;
+	struct eapol_ctx *ctx;
+	struct wpa_sm_ctx *wctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL) {
+		printf("Failed to allocate EAPOL context.\n");
+		return -1;
+	}
+	ctx->ctx = e;
+	ctx->msg_ctx = wpa_s;
+	ctx->scard_ctx = wpa_s->scard;
+	ctx->cb = eapol_sm_cb;
+	ctx->cb_ctx = e;
+	ctx->eapol_send_ctx = wpa_s;
+	ctx->preauth = 0;
+	ctx->eapol_done_cb = eapol_test_eapol_done_cb;
+	ctx->eapol_send = eapol_test_eapol_send;
+	ctx->set_config_blob = eapol_test_set_config_blob;
+	ctx->get_config_blob = eapol_test_get_config_blob;
+	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->openssl_ciphers = wpa_s->conf->openssl_ciphers;
+	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;
+
+	wpa_s->eapol = eapol_sm_init(ctx);
+	if (wpa_s->eapol == NULL) {
+		os_free(ctx);
+		printf("Failed to initialize EAPOL state machines.\n");
+		return -1;
+	}
+
+	wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+	wctx = os_zalloc(sizeof(*wctx));
+	if (wctx == NULL) {
+		os_free(ctx);
+		return -1;
+	}
+	wctx->ctx = e;
+	wctx->msg_ctx = wpa_s;
+	wctx->get_state = eapol_test_get_state;
+	wpa_s->wpa = wpa_sm_init(wctx);
+	if (!wpa_s->wpa) {
+		os_free(ctx);
+		os_free(wctx);
+		return -1;
+	}
+
+	if (!ssid)
+		return 0;
+
+	wpa_s->current_ssid = ssid;
+	os_memset(&eapol_conf, 0, sizeof(eapol_conf));
+	eapol_conf.accept_802_1x_keys = 1;
+	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);
+
+
+	eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+	/* 802.1X::portControl = Auto */
+	eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE);
+
+	return 0;
+}
+
+
+static void test_eapol_clean(struct eapol_test_data *e,
+			     struct wpa_supplicant *wpa_s)
+{
+	struct extra_radius_attr *p, *prev;
+
+	wpa_sm_deinit(wpa_s->wpa);
+	wpa_s->wpa = NULL;
+	radius_client_deinit(e->radius);
+	wpabuf_free(e->last_eap_radius);
+	radius_msg_free(e->last_recv_radius);
+	e->last_recv_radius = NULL;
+	os_free(e->eap_identity);
+	e->eap_identity = NULL;
+	eapol_sm_deinit(wpa_s->eapol);
+	wpa_s->eapol = NULL;
+	if (e->radius_conf && e->radius_conf->auth_server) {
+		os_free(e->radius_conf->auth_server->shared_secret);
+		os_free(e->radius_conf->auth_server);
+	}
+	os_free(e->radius_conf);
+	e->radius_conf = NULL;
+	scard_deinit(wpa_s->scard);
+	if (wpa_s->ctrl_iface) {
+		wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
+		wpa_s->ctrl_iface = NULL;
+	}
+
+	ext_password_deinit(wpa_s->ext_pw);
+	wpa_s->ext_pw = NULL;
+
+	wpa_config_free(wpa_s->conf);
+
+	p = e->extra_attrs;
+	while (p) {
+		prev = p;
+		p = p->next;
+		os_free(prev);
+	}
+}
+
+
+static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	u8 buf[100], *pos;
+	struct ieee802_1x_hdr *hdr;
+	struct eap_hdr *eap;
+
+	hdr = (struct ieee802_1x_hdr *) buf;
+	hdr->version = EAPOL_VERSION;
+	hdr->type = IEEE802_1X_TYPE_EAP_PACKET;
+	hdr->length = htons(5);
+
+	eap = (struct eap_hdr *) (hdr + 1);
+	eap->code = EAP_CODE_REQUEST;
+	eap->identifier = 0;
+	eap->length = htons(5);
+	pos = (u8 *) (eap + 1);
+	*pos = EAP_TYPE_IDENTITY;
+
+	printf("Sending fake EAP-Request-Identity\n");
+	eapol_sm_rx_eapol(wpa_s->eapol, wpa_s->bssid, buf,
+			  sizeof(*hdr) + 5);
+}
+
+
+static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct eapol_test_data *e = eloop_ctx;
+	printf("EAPOL test timed out\n");
+	e->auth_timed_out = 1;
+	eloop_terminate();
+}
+
+
+static char *eap_type_text(u8 type)
+{
+	switch (type) {
+	case EAP_TYPE_IDENTITY: return "Identity";
+	case EAP_TYPE_NOTIFICATION: return "Notification";
+	case EAP_TYPE_NAK: return "Nak";
+	case EAP_TYPE_TLS: return "TLS";
+	case EAP_TYPE_TTLS: return "TTLS";
+	case EAP_TYPE_PEAP: return "PEAP";
+	case EAP_TYPE_SIM: return "SIM";
+	case EAP_TYPE_GTC: return "GTC";
+	case EAP_TYPE_MD5: return "MD5";
+	case EAP_TYPE_OTP: return "OTP";
+	case EAP_TYPE_FAST: return "FAST";
+	case EAP_TYPE_SAKE: return "SAKE";
+	case EAP_TYPE_PSK: return "PSK";
+	default: return "Unknown";
+	}
+}
+
+
+static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e)
+{
+	struct wpabuf *eap;
+	const struct eap_hdr *hdr;
+	int eap_type = -1;
+	char buf[64];
+	struct radius_msg *msg;
+
+	if (e->last_recv_radius == NULL)
+		return;
+
+	msg = e->last_recv_radius;
+
+	eap = radius_msg_get_eap(msg);
+	if (eap == NULL) {
+		/* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3:
+		 * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
+		 * attribute */
+		wpa_printf(MSG_DEBUG, "could not extract "
+			       "EAP-Message from RADIUS message");
+		wpabuf_free(e->last_eap_radius);
+		e->last_eap_radius = NULL;
+		return;
+	}
+
+	if (wpabuf_len(eap) < sizeof(*hdr)) {
+		wpa_printf(MSG_DEBUG, "too short EAP packet "
+			       "received from authentication server");
+		wpabuf_free(eap);
+		return;
+	}
+
+	if (wpabuf_len(eap) > sizeof(*hdr))
+		eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)];
+
+	hdr = wpabuf_head(eap);
+	switch (hdr->code) {
+	case EAP_CODE_REQUEST:
+		os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
+			    eap_type >= 0 ? eap_type_text(eap_type) : "??",
+			    eap_type);
+		break;
+	case EAP_CODE_RESPONSE:
+		os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
+			    eap_type >= 0 ? eap_type_text(eap_type) : "??",
+			    eap_type);
+		break;
+	case EAP_CODE_SUCCESS:
+		os_strlcpy(buf, "EAP Success", sizeof(buf));
+		/* LEAP uses EAP Success within an authentication, so must not
+		 * stop here with eloop_terminate(); */
+		break;
+	case EAP_CODE_FAILURE:
+		os_strlcpy(buf, "EAP Failure", sizeof(buf));
+		if (e->ctrl_iface)
+			break;
+		eloop_terminate();
+		break;
+	default:
+		os_strlcpy(buf, "unknown EAP code", sizeof(buf));
+		wpa_hexdump_buf(MSG_DEBUG, "Decapsulated EAP packet", eap);
+		break;
+	}
+	wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d "
+		       "id=%d len=%d) from RADIUS server: %s",
+		      hdr->code, hdr->identifier, ntohs(hdr->length), buf);
+
+	/* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */
+
+	wpabuf_free(e->last_eap_radius);
+	e->last_eap_radius = eap;
+
+	{
+		struct ieee802_1x_hdr *dot1x;
+		dot1x = os_malloc(sizeof(*dot1x) + wpabuf_len(eap));
+		assert(dot1x != NULL);
+		dot1x->version = EAPOL_VERSION;
+		dot1x->type = IEEE802_1X_TYPE_EAP_PACKET;
+		dot1x->length = htons(wpabuf_len(eap));
+		os_memcpy((u8 *) (dot1x + 1), wpabuf_head(eap),
+			  wpabuf_len(eap));
+		eapol_sm_rx_eapol(e->wpa_s->eapol, e->wpa_s->bssid,
+				  (u8 *) dot1x,
+				  sizeof(*dot1x) + wpabuf_len(eap));
+		os_free(dot1x);
+	}
+}
+
+
+static void ieee802_1x_get_keys(struct eapol_test_data *e,
+				struct radius_msg *msg, struct radius_msg *req,
+				const u8 *shared_secret,
+				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);
+	if (keys && keys->send == NULL && keys->recv == NULL) {
+		os_free(keys);
+		keys = radius_msg_get_cisco_keys(msg, req, shared_secret,
+						 shared_secret_len);
+	}
+
+	if (keys) {
+		if (keys->send) {
+			wpa_hexdump(MSG_DEBUG, "MS-MPPE-Send-Key (sign)",
+				    keys->send, keys->send_len);
+		}
+		if (keys->recv) {
+			wpa_hexdump(MSG_DEBUG, "MS-MPPE-Recv-Key (crypt)",
+				    keys->recv, keys->recv_len);
+			e->authenticator_pmk_len =
+				keys->recv_len > PMK_LEN ? PMK_LEN :
+				keys->recv_len;
+			os_memcpy(e->authenticator_pmk, keys->recv,
+				  e->authenticator_pmk_len);
+			if (e->authenticator_pmk_len == 16 && keys->send &&
+			    keys->send_len == 16) {
+				/* MS-CHAP-v2 derives 16 octet keys */
+				wpa_printf(MSG_DEBUG, "Use MS-MPPE-Send-Key "
+					   "to extend PMK to 32 octets");
+				os_memcpy(e->authenticator_pmk +
+					  e->authenticator_pmk_len,
+					  keys->send, keys->send_len);
+				e->authenticator_pmk_len += keys->send_len;
+			}
+		}
+
+		os_free(keys->send);
+		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;
+	}
+}
+
+
+/* Process the RADIUS frames from Authentication Server */
+static RadiusRxResult
+ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
+			const u8 *shared_secret, size_t shared_secret_len,
+			void *data)
+{
+	struct eapol_test_data *e = data;
+	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+
+	/* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be
+	 * present when packet contains an EAP-Message attribute */
+	if (hdr->code == RADIUS_CODE_ACCESS_REJECT &&
+	    radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
+				0) < 0 &&
+	    radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "Allowing RADIUS "
+			      "Access-Reject without Message-Authenticator "
+			      "since it does not include EAP-Message\n");
+	} else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
+				     req, 1)) {
+		printf("Incoming RADIUS packet did not have correct "
+		       "Message-Authenticator - dropped\n");
+		return RADIUS_RX_UNKNOWN;
+	}
+
+	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");
+		return RADIUS_RX_UNKNOWN;
+	}
+
+	e->radius_identifier = -1;
+	wpa_printf(MSG_DEBUG, "RADIUS packet matching with station");
+
+	radius_msg_free(e->last_recv_radius);
+	e->last_recv_radius = msg;
+
+	switch (hdr->code) {
+	case RADIUS_CODE_ACCESS_ACCEPT:
+		e->radius_access_accept_received = 1;
+		ieee802_1x_get_keys(e, msg, req, shared_secret,
+				    shared_secret_len);
+		break;
+	case RADIUS_CODE_ACCESS_REJECT:
+		e->radius_access_reject_received = 1;
+		break;
+	}
+
+	ieee802_1x_decapsulate_radius(e);
+
+	if ((hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
+	     e->eapol_test_num_reauths < 0) ||
+	    hdr->code == RADIUS_CODE_ACCESS_REJECT) {
+		if (!e->ctrl_iface)
+			eloop_terminate();
+	}
+
+	return RADIUS_RX_QUEUED;
+}
+
+
+static int driver_get_ssid(void *priv, u8 *ssid)
+{
+	ssid[0] = 0;
+	return 0;
+}
+
+
+static int driver_get_bssid(void *priv, u8 *bssid)
+{
+	struct eapol_test_data *e = priv;
+
+	if (e->ctrl_iface && !e->id_req_sent) {
+		eloop_register_timeout(0, 0, send_eap_request_identity,
+				       e->wpa_s, NULL);
+		e->id_req_sent = 1;
+	}
+
+	os_memset(bssid, 0, ETH_ALEN);
+	bssid[5] = 1;
+	return 0;
+}
+
+
+static int driver_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+	os_memset(capa, 0, sizeof(*capa));
+	capa->flags = WPA_DRIVER_FLAGS_WIRED;
+	return 0;
+}
+
+
+struct wpa_driver_ops eapol_test_drv_ops = {
+	.name = "test",
+	.get_ssid = driver_get_ssid,
+	.get_bssid = driver_get_bssid,
+	.get_capa = driver_get_capa,
+};
+
+static void wpa_init_conf(struct eapol_test_data *e,
+			  struct wpa_supplicant *wpa_s, const char *authsrv,
+			  int port, const char *secret,
+			  const char *cli_addr, const char *ifname)
+{
+	struct hostapd_radius_server *as;
+	int res;
+
+	wpa_s->driver = &eapol_test_drv_ops;
+	wpa_s->drv_priv = e;
+	wpa_s->bssid[5] = 1;
+	os_memcpy(wpa_s->own_addr, e->own_addr, ETH_ALEN);
+	e->own_ip_addr.s_addr = htonl((127 << 24) | 1);
+	os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
+
+	e->radius_conf = os_zalloc(sizeof(struct hostapd_radius_servers));
+	assert(e->radius_conf != NULL);
+	e->radius_conf->num_auth_servers = 1;
+	as = os_zalloc(sizeof(struct hostapd_radius_server));
+	assert(as != NULL);
+#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA)
+	{
+		int a[4];
+		u8 *pos;
+		sscanf(authsrv, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]);
+		pos = (u8 *) &as->addr.u.v4;
+		*pos++ = a[0];
+		*pos++ = a[1];
+		*pos++ = a[2];
+		*pos++ = a[3];
+	}
+#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
+	if (hostapd_parse_ip_addr(authsrv, &as->addr) < 0) {
+		wpa_printf(MSG_ERROR, "Invalid IP address '%s'",
+			   authsrv);
+		assert(0);
+	}
+#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
+	as->port = port;
+	as->shared_secret = (u8 *) os_strdup(secret);
+	as->shared_secret_len = os_strlen(secret);
+	e->radius_conf->auth_server = as;
+	e->radius_conf->auth_servers = as;
+	e->radius_conf->msg_dumps = 1;
+	if (cli_addr) {
+		if (hostapd_parse_ip_addr(cli_addr,
+					  &e->radius_conf->client_addr) == 0)
+			e->radius_conf->force_client_addr = 1;
+		else {
+			wpa_printf(MSG_ERROR, "Invalid IP address '%s'",
+				   cli_addr);
+			assert(0);
+		}
+	}
+
+	e->radius = radius_client_init(wpa_s, e->radius_conf);
+	assert(e->radius != NULL);
+
+	res = radius_client_register(e->radius, RADIUS_AUTH,
+				     ieee802_1x_receive_auth, e);
+	assert(res == 0);
+}
+
+
+static int scard_test(struct eapol_test_data *e)
+{
+	struct scard_data *scard;
+	size_t len;
+	char imsi[20];
+	unsigned char _rand[16];
+#ifdef PCSC_FUNCS
+	unsigned char sres[4];
+	unsigned char kc[8];
+#endif /* PCSC_FUNCS */
+#define num_triplets 5
+	unsigned char rand_[num_triplets][16];
+	unsigned char sres_[num_triplets][4];
+	unsigned char kc_[num_triplets][8];
+	int i, res;
+	size_t j;
+
+#define AKA_RAND_LEN 16
+#define AKA_AUTN_LEN 16
+#define AKA_AUTS_LEN 14
+#define RES_MAX_LEN 16
+#define IK_LEN 16
+#define CK_LEN 16
+	unsigned char aka_rand[AKA_RAND_LEN];
+	unsigned char aka_autn[AKA_AUTN_LEN];
+	unsigned char aka_auts[AKA_AUTS_LEN];
+	unsigned char aka_res[RES_MAX_LEN];
+	size_t aka_res_len;
+	unsigned char aka_ik[IK_LEN];
+	unsigned char aka_ck[CK_LEN];
+
+	scard = scard_init(e->pcsc_reader);
+	if (scard == NULL)
+		return -1;
+	if (scard_set_pin(scard, e->pcsc_pin)) {
+		wpa_printf(MSG_WARNING, "PIN validation failed");
+		scard_deinit(scard);
+		return -1;
+	}
+
+	len = sizeof(imsi);
+	if (scard_get_imsi(scard, imsi, &len))
+		goto failed;
+	wpa_hexdump_ascii(MSG_DEBUG, "SCARD: IMSI", (u8 *) imsi, len);
+	/* NOTE: Permanent Username: 1 | IMSI */
+
+	wpa_printf(MSG_DEBUG, "SCARD: MNC length %d",
+		   scard_get_mnc_len(scard));
+
+	os_memset(_rand, 0, sizeof(_rand));
+	if (scard_gsm_auth(scard, _rand, sres, kc))
+		goto failed;
+
+	os_memset(_rand, 0xff, sizeof(_rand));
+	if (scard_gsm_auth(scard, _rand, sres, kc))
+		goto failed;
+
+	for (i = 0; i < num_triplets; i++) {
+		os_memset(rand_[i], i, sizeof(rand_[i]));
+		if (scard_gsm_auth(scard, rand_[i], sres_[i], kc_[i]))
+			goto failed;
+	}
+
+	for (i = 0; i < num_triplets; i++) {
+		printf("1");
+		for (j = 0; j < len; j++)
+			printf("%c", imsi[j]);
+		printf(",");
+		for (j = 0; j < 16; j++)
+			printf("%02X", rand_[i][j]);
+		printf(",");
+		for (j = 0; j < 4; j++)
+			printf("%02X", sres_[i][j]);
+		printf(",");
+		for (j = 0; j < 8; j++)
+			printf("%02X", kc_[i][j]);
+		printf("\n");
+	}
+
+	wpa_printf(MSG_DEBUG, "Trying to use UMTS authentication");
+
+	/* seq 39 (0x28) */
+	os_memset(aka_rand, 0xaa, 16);
+	os_memcpy(aka_autn, "\x86\x71\x31\xcb\xa2\xfc\x61\xdf"
+		  "\xa3\xb3\x97\x9d\x07\x32\xa2\x12", 16);
+
+	res = scard_umts_auth(scard, aka_rand, aka_autn, aka_res, &aka_res_len,
+			      aka_ik, aka_ck, aka_auts);
+	if (res == 0) {
+		wpa_printf(MSG_DEBUG, "UMTS auth completed successfully");
+		wpa_hexdump(MSG_DEBUG, "RES", aka_res, aka_res_len);
+		wpa_hexdump(MSG_DEBUG, "IK", aka_ik, IK_LEN);
+		wpa_hexdump(MSG_DEBUG, "CK", aka_ck, CK_LEN);
+	} else if (res == -2) {
+		wpa_printf(MSG_DEBUG, "UMTS auth resulted in synchronization "
+			   "failure");
+		wpa_hexdump(MSG_DEBUG, "AUTS", aka_auts, AKA_AUTS_LEN);
+	} else {
+		wpa_printf(MSG_DEBUG, "UMTS auth failed");
+	}
+
+failed:
+	scard_deinit(scard);
+
+	return 0;
+#undef num_triplets
+}
+
+
+static int scard_get_triplets(struct eapol_test_data *e, int argc, char *argv[])
+{
+	struct scard_data *scard;
+	size_t len;
+	char imsi[20];
+	unsigned char _rand[16];
+	unsigned char sres[4];
+	unsigned char kc[8];
+	int num_triplets;
+	int i;
+	size_t j;
+
+	if (argc < 2 || ((num_triplets = atoi(argv[1])) <= 0)) {
+		printf("invalid parameters for sim command\n");
+		return -1;
+	}
+
+	if (argc <= 2 || os_strcmp(argv[2], "debug") != 0) {
+		/* disable debug output */
+		wpa_debug_level = 99;
+	}
+
+	scard = scard_init(e->pcsc_reader);
+	if (scard == NULL) {
+		printf("Failed to open smartcard connection\n");
+		return -1;
+	}
+	if (scard_set_pin(scard, argv[0])) {
+		wpa_printf(MSG_WARNING, "PIN validation failed");
+		scard_deinit(scard);
+		return -1;
+	}
+
+	len = sizeof(imsi);
+	if (scard_get_imsi(scard, imsi, &len)) {
+		scard_deinit(scard);
+		return -1;
+	}
+
+	for (i = 0; i < num_triplets; i++) {
+		os_memset(_rand, i, sizeof(_rand));
+		if (scard_gsm_auth(scard, _rand, sres, kc))
+			break;
+
+		/* IMSI:Kc:SRES:RAND */
+		for (j = 0; j < len; j++)
+			printf("%c", imsi[j]);
+		printf(":");
+		for (j = 0; j < 8; j++)
+			printf("%02X", kc[j]);
+		printf(":");
+		for (j = 0; j < 4; j++)
+			printf("%02X", sres[j]);
+		printf(":");
+		for (j = 0; j < 16; j++)
+			printf("%02X", _rand[j]);
+		printf("\n");
+	}
+
+	scard_deinit(scard);
+
+	return 0;
+}
+
+
+static void eapol_test_terminate(int sig, void *signal_ctx)
+{
+	struct wpa_supplicant *wpa_s = signal_ctx;
+	wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig);
+	eloop_terminate();
+}
+
+
+static void usage(void)
+{
+	printf("usage:\n"
+	       "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>] [-R<PC/SC reader>] "
+	       "[-P<PC/SC PIN>] \\\n"
+	       "           [-A<client IP>] [-i<ifname>] [-T<ctrl_iface>]\n"
+	       "eapol_test scard\n"
+	       "eapol_test sim <PIN> <num triplets> [debug]\n"
+	       "\n");
+	printf("options:\n"
+	       "  -c<conf> = configuration file\n"
+	       "  -a<AS IP> = IP address of the authentication server, "
+	       "default 127.0.0.1\n"
+	       "  -p<AS port> = UDP port of the authentication server, "
+	       "default 1812\n"
+	       "  -s<AS secret> = shared secret with the authentication "
+	       "server, default 'radius'\n"
+	       "  -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"
+	       "  -t<timeout> = sets timeout in seconds (default: 30 s)\n"
+	       "  -C<Connect-Info> = RADIUS Connect-Info (default: "
+	       "CONNECT 11Mbps 802.11b)\n"
+	       "  -M<client MAC address> = Set own MAC address "
+	       "(Calling-Station-Id,\n"
+	       "                           default: 02:00:00:00:00:01)\n"
+	       "  -o<server cert file> = Write received server certificate\n"
+	       "                         chain to the specified file\n"
+	       "  -N<attr spec> = send arbitrary attribute specified by:\n"
+	       "                  attr_id:syntax:value or attr_id\n"
+	       "                  attr_id - number id of the attribute\n"
+	       "                  syntax - one of: s, d, x\n"
+	       "                     s = string\n"
+	       "                     d = integer\n"
+	       "                     x = octet string\n"
+	       "                  value - attribute value.\n"
+	       "       When only attr_id is specified, NULL will be used as "
+	       "value.\n"
+	       "       Multiple attributes can be specified by using the "
+	       "option several times.\n");
+}
+
+
+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";
+	int as_port = 1812;
+	char *as_secret = "radius";
+	char *cli_addr = NULL;
+	char *conf = NULL;
+	int timeout = 30;
+	char *pos;
+	struct extra_radius_attr *p = NULL, *p1;
+	const char *ifname = "test";
+	const char *ctrl_iface = NULL;
+
+	if (os_program_init())
+		return -1;
+
+	hostapd_logger_register_cb(hostapd_logger_cb);
+
+	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:ei:M:nN:o:p:P:r:R:s:St:T:W");
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'a':
+			as_addr = optarg;
+			break;
+		case 'A':
+			cli_addr = optarg;
+			break;
+		case 'c':
+			conf = optarg;
+			break;
+		case 'C':
+			eapol_test.connect_info = optarg;
+			break;
+		case 'e':
+			eapol_test.req_eap_key_name = 1;
+			break;
+		case 'i':
+			ifname = optarg;
+			break;
+		case 'M':
+			if (hwaddr_aton(optarg, eapol_test.own_addr)) {
+				usage();
+				return -1;
+			}
+			break;
+		case 'n':
+			eapol_test.no_mppe_keys++;
+			break;
+		case 'o':
+			if (eapol_test.server_cert_file)
+				fclose(eapol_test.server_cert_file);
+			eapol_test.server_cert_file = fopen(optarg, "w");
+			if (eapol_test.server_cert_file == NULL) {
+				printf("Could not open '%s' for writing\n",
+				       optarg);
+				return -1;
+			}
+			break;
+		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;
+		case 'S':
+			save_config++;
+			break;
+		case 't':
+			timeout = atoi(optarg);
+			break;
+		case 'T':
+			ctrl_iface = optarg;
+			eapol_test.ctrl_iface = 1;
+			break;
+		case 'W':
+			wait_for_monitor++;
+			break;
+		case 'N':
+			p1 = os_zalloc(sizeof(*p1));
+			if (p1 == NULL)
+				break;
+			if (!p)
+				eapol_test.extra_attrs = p1;
+			else
+				p->next = p1;
+			p = p1;
+
+			p->type = atoi(optarg);
+			pos = os_strchr(optarg, ':');
+			if (pos == NULL) {
+				p->syntax = 'n';
+				p->data = NULL;
+				break;
+			}
+
+			pos++;
+			if (pos[0] == '\0' || pos[1] != ':') {
+				printf("Incorrect format of attribute "
+				       "specification\n");
+				break;
+			}
+
+			p->syntax = pos[0];
+			p->data = pos + 2;
+			break;
+		default:
+			usage();
+			return -1;
+		}
+	}
+
+	if (argc > optind && os_strcmp(argv[optind], "scard") == 0) {
+		return scard_test(&eapol_test);
+	}
+
+	if (argc > optind && os_strcmp(argv[optind], "sim") == 0) {
+		return scard_get_triplets(&eapol_test, argc - optind - 1,
+					  &argv[optind + 1]);
+	}
+
+	if (conf == NULL && !ctrl_iface) {
+		usage();
+		printf("Configuration file is required.\n");
+		return -1;
+	}
+
+	if (eap_register_methods()) {
+		wpa_printf(MSG_ERROR, "Failed to register EAP methods");
+		return -1;
+	}
+
+	if (eloop_init()) {
+		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+		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);
+	if (conf)
+		wpa_s.conf = wpa_config_read(conf, NULL);
+	else
+		wpa_s.conf = wpa_config_alloc_empty(ctrl_iface, NULL);
+	if (wpa_s.conf == NULL) {
+		printf("Failed to parse configuration file '%s'.\n", conf);
+		return -1;
+	}
+	if (!ctrl_iface && wpa_s.conf->ssid == NULL) {
+		printf("No networks defined.\n");
+		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, ifname);
+	wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
+	if (wpa_s.ctrl_iface == NULL) {
+		printf("Failed to initialize control interface '%s'.\n"
+		       "You may have another eapol_test process already "
+		       "running or the file was\n"
+		       "left by an unclean termination of eapol_test in "
+		       "which case you will need\n"
+		       "to manually remove this file before starting "
+		       "eapol_test again.\n",
+		       wpa_s.conf->ctrl_interface);
+		return -1;
+	}
+	if (wpa_s.conf->ssid &&
+	    wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
+		return -1;
+
+	if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid))
+		return -1;
+
+	if (wpas_init_ext_pw(&wpa_s) < 0)
+		return -1;
+
+	if (wait_for_monitor)
+		wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface);
+
+	if (!ctrl_iface) {
+		eloop_register_timeout(timeout, 0, eapol_test_timeout,
+				       &eapol_test, NULL);
+		eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s,
+				       NULL);
+	}
+	eloop_register_signal_terminate(eapol_test_terminate, &wpa_s);
+	eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s);
+	eloop_run();
+
+	eloop_cancel_timeout(eapol_test_timeout, &eapol_test, NULL);
+	eloop_cancel_timeout(eapol_sm_reauth, &eapol_test, NULL);
+
+	if (eapol_test_compare_pmk(&eapol_test) == 0 ||
+	    eapol_test.no_mppe_keys)
+		ret = 0;
+	if (eapol_test.auth_timed_out)
+		ret = -2;
+	if (eapol_test.radius_access_reject_received)
+		ret = -3;
+
+	if (save_config)
+		wpa_config_write(conf, wpa_s.conf);
+
+	test_eapol_clean(&eapol_test, &wpa_s);
+
+	eap_peer_unregister_methods();
+#ifdef CONFIG_AP
+	eap_server_unregister_methods();
+#endif /* CONFIG_AP */
+
+	eloop_destroy();
+
+	if (eapol_test.server_cert_file)
+		fclose(eapol_test.server_cert_file);
+
+	printf("MPPE keys OK: %d  mismatch: %d\n",
+	       eapol_test.num_mppe_ok, eapol_test.num_mppe_mismatch);
+	if (eapol_test.num_mppe_mismatch)
+		ret = -4;
+	if (ret)
+		printf("FAILURE\n");
+	else
+		printf("SUCCESS\n");
+
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/hostap/wpa_supplicant/eapol_test.py b/hostap/wpa_supplicant/eapol_test.py
new file mode 100755
index 0000000..80e7dfc
--- /dev/null
+++ b/hostap/wpa_supplicant/eapol_test.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python2
+#
+# eapol_test controller
+# Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import argparse
+import logging
+import os
+import Queue
+import sys
+import threading
+
+logger = logging.getLogger()
+dir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
+sys.path.append(os.path.join(dir, '..', 'wpaspy'))
+import wpaspy
+wpas_ctrl = '/tmp/eapol_test'
+
+class eapol_test:
+    def __init__(self, ifname):
+        self.ifname = ifname
+        self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
+        if "PONG" not in self.ctrl.request("PING"):
+            raise Exception("Failed to connect to eapol_test (%s)" % ifname)
+        self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
+        self.mon.attach()
+
+    def add_network(self):
+        id = self.request("ADD_NETWORK")
+        if "FAIL" in id:
+            raise Exception("ADD_NETWORK failed")
+        return int(id)
+
+    def remove_network(self, id):
+        id = self.request("REMOVE_NETWORK " + str(id))
+        if "FAIL" in id:
+            raise Exception("REMOVE_NETWORK failed")
+        return None
+
+    def set_network(self, id, field, value):
+        res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value)
+        if "FAIL" in res:
+            raise Exception("SET_NETWORK failed")
+        return None
+
+    def set_network_quoted(self, id, field, value):
+        res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"')
+        if "FAIL" in res:
+            raise Exception("SET_NETWORK failed")
+        return None
+
+    def request(self, cmd, timeout=10):
+        return self.ctrl.request(cmd, timeout=timeout)
+
+    def wait_event(self, events, timeout=10):
+        start = os.times()[4]
+        while True:
+            while self.mon.pending():
+                ev = self.mon.recv()
+                logger.debug(self.ifname + ": " + ev)
+                for event in events:
+                    if event in ev:
+                        return ev
+            now = os.times()[4]
+            remaining = start + timeout - now
+            if remaining <= 0:
+                break
+            if not self.mon.pending(timeout=remaining):
+                break
+        return None
+
+def run(ifname, count, no_fast_reauth, res):
+    et = eapol_test(ifname)
+
+    et.request("AP_SCAN 0")
+    if no_fast_reauth:
+        et.request("SET fast_reauth 0")
+    else:
+        et.request("SET fast_reauth 1")
+    id = et.add_network()
+    et.set_network(id, "key_mgmt", "IEEE8021X")
+    et.set_network(id, "eapol_flags", "0")
+    et.set_network(id, "eap", "TLS")
+    et.set_network_quoted(id, "identity", "user")
+    et.set_network_quoted(id, "ca_cert", 'ca.pem')
+    et.set_network_quoted(id, "client_cert", 'client.pem')
+    et.set_network_quoted(id, "private_key", 'client.key')
+    et.set_network_quoted(id, "private_key_passwd", 'whatever')
+    et.set_network(id, "disabled", "0")
+
+    fail = False
+    for i in range(count):
+        et.request("REASSOCIATE")
+        ev = et.wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-EAP-FAILURE"])
+        if ev is None or "CTRL-EVENT-CONNECTED" not in ev:
+            fail = True
+            break
+
+    et.remove_network(id)
+
+    if fail:
+        res.put("FAIL (%d OK)" % i)
+    else:
+        res.put("PASS %d" % (i + 1))
+
+def main():
+    parser = argparse.ArgumentParser(description='eapol_test controller')
+    parser.add_argument('--ctrl', help='control interface directory')
+    parser.add_argument('--num', help='number of processes')
+    parser.add_argument('--iter', help='number of iterations')
+    parser.add_argument('--no-fast-reauth', action='store_true',
+                        dest='no_fast_reauth',
+                        help='disable TLS session resumption')
+    args = parser.parse_args()
+
+    num = int(args.num)
+    iter = int(args.iter)
+    if args.ctrl:
+        global wpas_ctrl
+        wpas_ctrl = args.ctrl
+
+    t = {}
+    res = {}
+    for i in range(num):
+        res[i] = Queue.Queue()
+        t[i] = threading.Thread(target=run, args=(str(i), iter,
+                                                  args.no_fast_reauth, res[i]))
+    for i in range(num):
+        t[i].start()
+    for i in range(num):
+        t[i].join()
+        try:
+            results = res[i].get(False)
+        except:
+            results = "N/A"
+        print "%d: %s" % (i, results)
+
+if __name__ == "__main__":
+    main()
diff --git a/hostap/wpa_supplicant/events.c b/hostap/wpa_supplicant/events.c
new file mode 100644
index 0000000..294fbd7
--- /dev/null
+++ b/hostap/wpa_supplicant/events.c
@@ -0,0 +1,3831 @@
+/*
+ * WPA Supplicant - Driver event processing
+ * Copyright (c) 2003-2015, 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 "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "eloop.h"
+#include "config.h"
+#include "l2_packet/l2_packet.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "pcsc_funcs.h"
+#include "rsn_supp/preauth.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "common/wpa_ctrl.h"
+#include "eap_peer/eap.h"
+#include "ap/hostapd.h"
+#include "p2p/p2p.h"
+#include "fst/fst.h"
+#include "wnm_sta.h"
+#include "notify.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/random.h"
+#include "blacklist.h"
+#include "wpas_glue.h"
+#include "wps_supplicant.h"
+#include "ibss_rsn.h"
+#include "sme.h"
+#include "gas_query.h"
+#include "p2p_supplicant.h"
+#include "bgscan.h"
+#include "autoscan.h"
+#include "ap.h"
+#include "bss.h"
+#include "scan.h"
+#include "offchannel.h"
+#include "interworking.h"
+#include "mesh.h"
+#include "mesh_mpm.h"
+#include "wmm_ac.h"
+
+
+#ifndef CONFIG_NO_SCAN_PROCESSING
+static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
+					      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_reltime now;
+
+	if (ssid == NULL || ssid->disabled_until.sec == 0)
+		return 0;
+
+	os_get_reltime(&now);
+	if (ssid->disabled_until.sec > now.sec)
+		return ssid->disabled_until.sec - now.sec;
+
+	wpas_clear_temp_disabled(wpa_s, ssid, 0);
+
+	return 0;
+}
+
+
+/**
+ * wpas_reenabled_network_time - Time until first network is re-enabled
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: If all enabled networks are temporarily disabled, returns the time
+ *	(in sec) until the first network is re-enabled. Otherwise returns 0.
+ *
+ * This function is used in case all enabled networks are temporarily disabled,
+ * in which case it returns the time (in sec) that the first network will be
+ * re-enabled. The function assumes that at least one network is enabled.
+ */
+static int wpas_reenabled_network_time(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *ssid;
+	int disabled_for, res = 0;
+
+#ifdef CONFIG_INTERWORKING
+	if (wpa_s->conf->auto_interworking && wpa_s->conf->interworking &&
+	    wpa_s->conf->cred)
+		return 0;
+#endif /* CONFIG_INTERWORKING */
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (ssid->disabled)
+			continue;
+
+		disabled_for = wpas_temp_disabled(wpa_s, ssid);
+		if (!disabled_for)
+			return 0;
+
+		if (!res || disabled_for < res)
+			res = disabled_for;
+	}
+
+	return res;
+}
+
+
+void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (wpa_s->disconnected || wpa_s->wpa_state != WPA_SCANNING)
+		return;
+
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"Try to associate due to network getting re-enabled");
+	if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	}
+}
+
+
+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;
+	u8 drv_ssid[SSID_MAX_LEN];
+	size_t drv_ssid_len;
+	int res;
+
+	if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) {
+		wpa_supplicant_update_current_bss(wpa_s);
+
+		if (wpa_s->current_ssid->ssid_len == 0)
+			return 0; /* current profile still in use */
+		res = wpa_drv_get_ssid(wpa_s, drv_ssid);
+		if (res < 0) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"Failed to read SSID from driver");
+			return 0; /* try to use current profile */
+		}
+		drv_ssid_len = res;
+
+		if (drv_ssid_len == wpa_s->current_ssid->ssid_len &&
+		    os_memcmp(drv_ssid, wpa_s->current_ssid->ssid,
+			      drv_ssid_len) == 0)
+			return 0; /* current profile still in use */
+
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Driver-initiated BSS selection changed the SSID to %s",
+			wpa_ssid_txt(drv_ssid, drv_ssid_len));
+		/* continue selecting a new network profile */
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association "
+		"information");
+	ssid = wpa_supplicant_get_ssid(wpa_s);
+	if (ssid == NULL) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"No network configuration found for the current AP");
+		return -1;
+	}
+
+	if (wpas_network_disabled(wpa_s, ssid)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is disabled");
+		return -1;
+	}
+
+	if (disallowed_bssid(wpa_s, wpa_s->bssid) ||
+	    disallowed_ssid(wpa_s, ssid->ssid, ssid->ssid_len)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS is disallowed");
+		return -1;
+	}
+
+	res = wpas_temp_disabled(wpa_s, ssid);
+	if (res > 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is temporarily "
+			"disabled for %d second(s)", res);
+		return -1;
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the "
+		"current AP");
+	if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
+		u8 wpa_ie[80];
+		size_t wpa_ie_len = sizeof(wpa_ie);
+		if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
+					      wpa_ie, &wpa_ie_len) < 0)
+			wpa_dbg(wpa_s, MSG_DEBUG, "Could not set WPA suites");
+	} else {
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+	}
+
+	if (wpa_s->current_ssid && wpa_s->current_ssid != ssid)
+		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)
+		wpas_notify_network_changed(wpa_s);
+
+	return 0;
+}
+
+
+void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (wpa_s->countermeasures) {
+		wpa_s->countermeasures = 0;
+		wpa_drv_set_countermeasures(wpa_s, 0);
+		wpa_msg(wpa_s, MSG_INFO, "WPA: TKIP countermeasures stopped");
+
+		/*
+		 * It is possible that the device is sched scanning, which means
+		 * that a connection attempt will be done only when we receive
+		 * scan results. However, in this case, it would be preferable
+		 * to scan and connect immediately, so cancel the sched_scan and
+		 * issue a regular scan flow.
+		 */
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	}
+}
+
+
+void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
+{
+	int bssid_changed;
+
+	wnm_bss_keep_alive_deinit(wpa_s);
+
+#ifdef CONFIG_IBSS_RSN
+	ibss_rsn_deinit(wpa_s->ibss_rsn);
+	wpa_s->ibss_rsn = NULL;
+#endif /* CONFIG_IBSS_RSN */
+
+#ifdef CONFIG_AP
+	wpa_supplicant_ap_deinit(wpa_s);
+#endif /* CONFIG_AP */
+
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+		return;
+
+	wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+	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);
+	sme_clear_on_disassoc(wpa_s);
+	wpa_s->current_bss = NULL;
+	wpa_s->assoc_freq = 0;
+
+	if (bssid_changed)
+		wpas_notify_bssid_changed(wpa_s);
+
+	eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
+	eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt))
+		eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+	wpa_s->ap_ies_from_associnfo = 0;
+	wpa_s->current_ssid = NULL;
+	eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+	wpa_s->key_mgmt = 0;
+
+	wpas_rrm_reset(wpa_s);
+}
+
+
+static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ie_data ie;
+	int pmksa_set = -1;
+	size_t i;
+
+	if (wpa_sm_parse_own_wpa_ie(wpa_s->wpa, &ie) < 0 ||
+	    ie.pmkid == NULL)
+		return;
+
+	for (i = 0; i < ie.num_pmkid; i++) {
+		pmksa_set = pmksa_cache_set_current(wpa_s->wpa,
+						    ie.pmkid + i * PMKID_LEN,
+						    NULL, NULL, 0);
+		if (pmksa_set == 0) {
+			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
+			break;
+		}
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from "
+		"PMKSA cache", pmksa_set == 0 ? "" : "not ");
+}
+
+
+static void wpa_supplicant_event_pmkid_candidate(struct wpa_supplicant *wpa_s,
+						 union wpa_event_data *data)
+{
+	if (data == NULL) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: No data in PMKID candidate "
+			"event");
+		return;
+	}
+	wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR
+		" index=%d preauth=%d",
+		MAC2STR(data->pmkid_candidate.bssid),
+		data->pmkid_candidate.index,
+		data->pmkid_candidate.preauth);
+
+	pmksa_candidate_add(wpa_s->wpa, data->pmkid_candidate.bssid,
+			    data->pmkid_candidate.index,
+			    data->pmkid_candidate.preauth);
+}
+
+
+static int wpa_supplicant_dynamic_keys(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE)
+		return 0;
+
+#ifdef IEEE8021X_EAPOL
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+	    wpa_s->current_ssid &&
+	    !(wpa_s->current_ssid->eapol_flags &
+	      (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
+	       EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) {
+		/* IEEE 802.1X, but not using dynamic WEP keys (i.e., either
+		 * plaintext or static WEP keys). */
+		return 0;
+	}
+#endif /* IEEE8021X_EAPOL */
+
+	return 1;
+}
+
+
+/**
+ * wpa_supplicant_scard_init - Initialize SIM/USIM access with PC/SC
+ * @wpa_s: pointer to wpa_supplicant data
+ * @ssid: Configuration data for the network
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when starting authentication with a network that is
+ * configured to use PC/SC for SIM/USIM access (EAP-SIM or EAP-AKA).
+ */
+int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid)
+{
+#ifdef IEEE8021X_EAPOL
+#ifdef PCSC_FUNCS
+	int aka = 0, sim = 0;
+
+	if ((ssid != NULL && ssid->eap.pcsc == NULL) ||
+	    wpa_s->scard != NULL || wpa_s->conf->external_sim)
+		return 0;
+
+	if (ssid == NULL || ssid->eap.eap_methods == NULL) {
+		sim = 1;
+		aka = 1;
+	} else {
+		struct eap_method_type *eap = ssid->eap.eap_methods;
+		while (eap->vendor != EAP_VENDOR_IETF ||
+		       eap->method != EAP_TYPE_NONE) {
+			if (eap->vendor == EAP_VENDOR_IETF) {
+				if (eap->method == EAP_TYPE_SIM)
+					sim = 1;
+				else if (eap->method == EAP_TYPE_AKA ||
+					 eap->method == EAP_TYPE_AKA_PRIME)
+					aka = 1;
+			}
+			eap++;
+		}
+	}
+
+	if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_SIM) == NULL)
+		sim = 0;
+	if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL &&
+	    eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME) ==
+	    NULL)
+		aka = 0;
+
+	if (!sim && !aka) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to "
+			"use SIM, but neither EAP-SIM nor EAP-AKA are "
+			"enabled");
+		return 0;
+	}
+
+	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(wpa_s->conf->pcsc_reader);
+	if (wpa_s->scard == NULL) {
+		wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM "
+			"(pcsc-lite)");
+		return -1;
+	}
+	wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard);
+	eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
+#endif /* PCSC_FUNCS */
+#endif /* IEEE8021X_EAPOL */
+
+	return 0;
+}
+
+
+#ifndef CONFIG_NO_SCAN_PROCESSING
+
+static int has_wep_key(struct wpa_ssid *ssid)
+{
+	int i;
+
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		if (ssid->wep_key_len[i])
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_supplicant_match_privacy(struct wpa_bss *bss,
+					struct wpa_ssid *ssid)
+{
+	int privacy = 0;
+
+	if (ssid->mixed_cell)
+		return 1;
+
+#ifdef CONFIG_WPS
+	if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
+		return 1;
+#endif /* CONFIG_WPS */
+
+	if (has_wep_key(ssid))
+		privacy = 1;
+
+#ifdef IEEE8021X_EAPOL
+	if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
+	    ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
+				 EAPOL_FLAG_REQUIRE_KEY_BROADCAST))
+		privacy = 1;
+#endif /* IEEE8021X_EAPOL */
+
+	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;
+}
+
+
+static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
+					 struct wpa_ssid *ssid,
+					 struct wpa_bss *bss)
+{
+	struct wpa_ie_data ie;
+	int proto_match = 0;
+	const u8 *rsn_ie, *wpa_ie;
+	int ret;
+	int wep_ok;
+
+	ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss);
+	if (ret >= 0)
+		return ret;
+
+	/* Allow TSN if local configuration accepts WEP use without WPA/WPA2 */
+	wep_ok = !wpa_key_mgmt_wpa(ssid->key_mgmt) &&
+		(((ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
+		  ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) ||
+		 (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA));
+
+	rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) {
+		proto_match++;
+
+		if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - parse "
+				"failed");
+			break;
+		}
+
+		if (wep_ok &&
+		    (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
+		{
+			wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on TSN "
+				"in RSN IE");
+			return 1;
+		}
+
+		if (!(ie.proto & ssid->proto)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - proto "
+				"mismatch");
+			break;
+		}
+
+		if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - PTK "
+				"cipher mismatch");
+			break;
+		}
+
+		if (!(ie.group_cipher & ssid->group_cipher)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - GTK "
+				"cipher mismatch");
+			break;
+		}
+
+		if (!(ie.key_mgmt & ssid->key_mgmt)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - key mgmt "
+				"mismatch");
+			break;
+		}
+
+#ifdef CONFIG_IEEE80211W
+		if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
+		    wpas_get_ssid_pmf(wpa_s, ssid) ==
+		    MGMT_FRAME_PROTECTION_REQUIRED) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - no mgmt "
+				"frame protection");
+			break;
+		}
+#endif /* CONFIG_IEEE80211W */
+
+		wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on RSN IE");
+		return 1;
+	}
+
+	wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+	while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) {
+		proto_match++;
+
+		if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - parse "
+				"failed");
+			break;
+		}
+
+		if (wep_ok &&
+		    (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
+		{
+			wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on TSN "
+				"in WPA IE");
+			return 1;
+		}
+
+		if (!(ie.proto & ssid->proto)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - proto "
+				"mismatch");
+			break;
+		}
+
+		if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - PTK "
+				"cipher mismatch");
+			break;
+		}
+
+		if (!(ie.group_cipher & ssid->group_cipher)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - GTK "
+				"cipher mismatch");
+			break;
+		}
+
+		if (!(ie.key_mgmt & ssid->key_mgmt)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - key mgmt "
+				"mismatch");
+			break;
+		}
+
+		wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on WPA IE");
+		return 1;
+	}
+
+	if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && !wpa_ie &&
+	    !rsn_ie) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "   allow for non-WPA IEEE 802.1X");
+		return 1;
+	}
+
+	if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) &&
+	    wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no WPA/RSN proto match");
+		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;
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "   reject due to mismatch with "
+		"WPA/WPA2");
+
+	return 0;
+}
+
+
+static int freq_allowed(int *freqs, int freq)
+{
+	int i;
+
+	if (freqs == NULL)
+		return 1;
+
+	for (i = 0; freqs[i]; i++)
+		if (freqs[i] == freq)
+			return 1;
+	return 0;
+}
+
+
+static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+	const struct hostapd_hw_modes *mode = NULL, *modes;
+	const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES };
+	const u8 *rate_ie;
+	int i, j, k;
+
+	if (bss->freq == 0)
+		return 1; /* Cannot do matching without knowing band */
+
+	modes = wpa_s->hw.modes;
+	if (modes == NULL) {
+		/*
+		 * The driver does not provide any additional information
+		 * about the utilized hardware, so allow the connection attempt
+		 * to continue.
+		 */
+		return 1;
+	}
+
+	for (i = 0; i < wpa_s->hw.num_modes; i++) {
+		for (j = 0; j < modes[i].num_channels; j++) {
+			int freq = modes[i].channels[j].freq;
+			if (freq == bss->freq) {
+				if (mode &&
+				    mode->mode == HOSTAPD_MODE_IEEE80211G)
+					break; /* do not allow 802.11b replace
+						* 802.11g */
+				mode = &modes[i];
+				break;
+			}
+		}
+	}
+
+	if (mode == NULL)
+		return 0;
+
+	for (i = 0; i < (int) sizeof(scan_ie); i++) {
+		rate_ie = wpa_bss_get_ie(bss, scan_ie[i]);
+		if (rate_ie == NULL)
+			continue;
+
+		for (j = 2; j < rate_ie[1] + 2; j++) {
+			int flagged = !!(rate_ie[j] & 0x80);
+			int r = (rate_ie[j] & 0x7f) * 5;
+
+			/*
+			 * IEEE Std 802.11n-2009 7.3.2.2:
+			 * The new BSS Membership selector value is encoded
+			 * like a legacy basic rate, but it is not a rate and
+			 * only indicates if the BSS members are required to
+			 * support the mandatory features of Clause 20 [HT PHY]
+			 * in order to join the BSS.
+			 */
+			if (flagged && ((rate_ie[j] & 0x7f) ==
+					BSS_MEMBERSHIP_SELECTOR_HT_PHY)) {
+				if (!ht_supported(mode)) {
+					wpa_dbg(wpa_s, MSG_DEBUG,
+						"   hardware does not support "
+						"HT PHY");
+					return 0;
+				}
+				continue;
+			}
+
+			/* There's also a VHT selector for 802.11ac */
+			if (flagged && ((rate_ie[j] & 0x7f) ==
+					BSS_MEMBERSHIP_SELECTOR_VHT_PHY)) {
+				if (!vht_supported(mode)) {
+					wpa_dbg(wpa_s, MSG_DEBUG,
+						"   hardware does not support "
+						"VHT PHY");
+					return 0;
+				}
+				continue;
+			}
+
+			if (!flagged)
+				continue;
+
+			/* check for legacy basic rates */
+			for (k = 0; k < mode->num_rates; k++) {
+				if (mode->rates[k] == r)
+					break;
+			}
+			if (k == mode->num_rates) {
+				/*
+				 * IEEE Std 802.11-2007 7.3.2.2 demands that in
+				 * order to join a BSS all required rates
+				 * have to be supported by the hardware.
+				 */
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   hardware does not support required rate %d.%d Mbps (freq=%d mode==%d num_rates=%d)",
+					r / 10, r % 10,
+					bss->freq, mode->mode, mode->num_rates);
+				return 0;
+			}
+		}
+	}
+
+	return 1;
+}
+
+
+/*
+ * Test whether BSS is in an ESS.
+ * This is done differently in DMG (60 GHz) and non-DMG bands
+ */
+static int bss_is_ess(struct wpa_bss *bss)
+{
+	if (bss_is_dmg(bss)) {
+		return (bss->caps & IEEE80211_CAP_DMG_MASK) ==
+			IEEE80211_CAP_DMG_AP;
+	}
+
+	return ((bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) ==
+		IEEE80211_CAP_ESS);
+}
+
+
+static int match_mac_mask(const u8 *addr_a, const u8 *addr_b, const u8 *mask)
+{
+	size_t i;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		if ((addr_a[i] & mask[i]) != (addr_b[i] & mask[i]))
+			return 0;
+	}
+	return 1;
+}
+
+
+static int addr_in_list(const u8 *addr, const u8 *list, size_t num)
+{
+	size_t i;
+
+	for (i = 0; i < num; i++) {
+		const u8 *a = list + i * ETH_ALEN * 2;
+		const u8 *m = a + ETH_ALEN;
+
+		if (match_mac_mask(a, addr, m))
+			return 1;
+	}
+	return 0;
+}
+
+
+static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
+					    int i, struct wpa_bss *bss,
+					    struct wpa_ssid *group,
+					    int only_first_ssid)
+{
+	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;
+
+	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 freq=%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, bss->freq,
+		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) {
+		int limit = 1;
+		if (wpa_supplicant_enabled_networks(wpa_s) == 1) {
+			/*
+			 * When only a single network is enabled, we can
+			 * trigger blacklisting on the first failure. This
+			 * should not be done with multiple enabled networks to
+			 * avoid getting forced to move into a worse ESS on
+			 * single error if there are no other BSSes of the
+			 * current ESS.
+			 */
+			limit = 0;
+		}
+		if (e->count > limit) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - blacklisted "
+				"(count=%d limit=%d)", e->count, limit);
+			return NULL;
+		}
+	}
+
+	if (bss->ssid_len == 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID not known");
+		return NULL;
+	}
+
+	if (disallowed_bssid(wpa_s, bss->bssid)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "   skip - BSSID disallowed");
+		return NULL;
+	}
+
+	if (disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID disallowed");
+		return NULL;
+	}
+
+	wpa = wpa_ie_len > 0 || rsn_ie_len > 0;
+
+	for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) {
+		int check_ssid = wpa ? 1 : (ssid->ssid_len != 0);
+		int res;
+
+		if (wpas_network_disabled(wpa_s, ssid)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - disabled");
+			continue;
+		}
+
+		res = wpas_temp_disabled(wpa_s, ssid);
+		if (res > 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - disabled "
+				"temporarily for %d second(s)", res);
+			continue;
+		}
+
+#ifdef CONFIG_WPS
+		if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - blacklisted "
+				"(WPS)");
+			continue;
+		}
+
+		if (wpa && ssid->ssid_len == 0 &&
+		    wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
+			check_ssid = 0;
+
+		if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+			/* Only allow wildcard SSID match if an AP
+			 * advertises active WPS operation that matches
+			 * with our mode. */
+			check_ssid = 1;
+			if (ssid->ssid_len == 0 &&
+			    wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
+				check_ssid = 0;
+		}
+#endif /* CONFIG_WPS */
+
+		if (ssid->bssid_set && ssid->ssid_len == 0 &&
+		    os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0)
+			check_ssid = 0;
+
+		if (check_ssid &&
+		    (bss->ssid_len != ssid->ssid_len ||
+		     os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID mismatch");
+			continue;
+		}
+
+		if (ssid->bssid_set &&
+		    os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - BSSID mismatch");
+			continue;
+		}
+
+		/* check blacklist */
+		if (ssid->num_bssid_blacklist &&
+		    addr_in_list(bss->bssid, ssid->bssid_blacklist,
+				 ssid->num_bssid_blacklist)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - BSSID blacklisted");
+			continue;
+		}
+
+		/* if there is a whitelist, only accept those APs */
+		if (ssid->num_bssid_whitelist &&
+		    !addr_in_list(bss->bssid, ssid->bssid_whitelist,
+				  ssid->num_bssid_whitelist)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - BSSID not in whitelist");
+			continue;
+		}
+
+		if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
+			continue;
+
+		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)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - non-WPA network "
+				"not allowed");
+			continue;
+		}
+
+		if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) &&
+		    has_wep_key(ssid)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - ignore WPA/WPA2 AP for WEP network block");
+			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");
+			continue;
+		}
+
+		if (!bss_is_ess(bss)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - not ESS network");
+			continue;
+		}
+
+		if (!freq_allowed(ssid->freq_list, bss->freq)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - frequency not "
+				"allowed");
+			continue;
+		}
+
+		if (!rate_match(wpa_s, bss)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - rate sets do "
+				"not match");
+			continue;
+		}
+
+#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
+		 * are not in Group Formation with that device.
+		 */
+#endif /* CONFIG_P2P */
+
+		if (os_reltime_before(&bss->last_update, &wpa_s->scan_min_time))
+		{
+			struct os_reltime diff;
+
+			os_reltime_sub(&wpa_s->scan_min_time,
+				       &bss->last_update, &diff);
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - scan result not recent enough (%u.%06u seconds too old)",
+				(unsigned int) diff.sec,
+				(unsigned int) diff.usec);
+			continue;
+		}
+
+		/* Matching configuration found */
+		return ssid;
+	}
+
+	/* No matching configuration found */
+	return NULL;
+}
+
+
+static struct wpa_bss *
+wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
+			  struct wpa_ssid *group,
+			  struct wpa_ssid **selected_ssid,
+			  int only_first_ssid)
+{
+	unsigned int i;
+
+	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,
+						    only_first_ssid);
+		if (!*selected_ssid)
+			continue;
+		wpa_dbg(wpa_s, MSG_DEBUG, "   selected BSS " MACSTR
+			" ssid='%s'",
+			MAC2STR(bss->bssid),
+			wpa_ssid_txt(bss->ssid, bss->ssid_len));
+		return bss;
+	}
+
+	return NULL;
+}
+
+
+struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
+					     struct wpa_ssid **selected_ssid)
+{
+	struct wpa_bss *selected = NULL;
+	int prio;
+	struct wpa_ssid *next_ssid = NULL;
+	struct wpa_ssid *ssid;
+
+	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) {
+		/* 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, 0);
+			if (selected)
+				break;
+		}
+
+		if (selected == NULL && wpa_s->blacklist &&
+		    !wpa_s->countermeasures) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "No APs found - clear "
+				"blacklist and try again");
+			wpa_blacklist_clear(wpa_s);
+			wpa_s->blacklist_cleared++;
+		} else if (selected == NULL)
+			break;
+	}
+
+	ssid = *selected_ssid;
+	if (selected && ssid && ssid->mem_only_psk && !ssid->psk_set &&
+	    !ssid->passphrase && !ssid->ext_psk) {
+		const char *field_name, *txt = NULL;
+
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"PSK/passphrase not yet available for the selected network");
+
+		wpas_notify_network_request(wpa_s, ssid,
+					    WPA_CTRL_REQ_PSK_PASSPHRASE, NULL);
+
+		field_name = wpa_supplicant_ctrl_req_to_string(
+			WPA_CTRL_REQ_PSK_PASSPHRASE, NULL, &txt);
+		if (field_name == NULL)
+			return NULL;
+
+		wpas_send_ctrl_req(wpa_s, ssid, field_name, txt);
+
+		selected = NULL;
+	}
+
+	return selected;
+}
+
+
+static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s,
+					int timeout_sec, int timeout_usec)
+{
+	if (!wpa_supplicant_enabled_networks(wpa_s)) {
+		/*
+		 * No networks are enabled; short-circuit request so
+		 * we don't wait timeout seconds before transitioning
+		 * to INACTIVE state.
+		 */
+		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);
+		return;
+	}
+
+	wpa_s->scan_for_connection = 1;
+	wpa_supplicant_req_scan(wpa_s, timeout_sec, timeout_usec);
+}
+
+
+int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
+			   struct wpa_bss *selected,
+			   struct wpa_ssid *ssid)
+{
+	if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
+		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
+			"PBC session overlap");
+		wpas_notify_wps_event_pbc_overlap(wpa_s);
+#ifdef CONFIG_P2P
+		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
+		wpas_wps_pbc_overlap(wpa_s);
+		wpas_wps_cancel(wpa_s);
+#endif /* CONFIG_WPS */
+		return -1;
+	}
+
+	wpa_msg(wpa_s, MSG_DEBUG,
+		"Considering connect request: reassociate: %d  selected: "
+		MACSTR "  bssid: " MACSTR "  pending: " MACSTR
+		"  wpa_state: %s  ssid=%p  current_ssid=%p",
+		wpa_s->reassociate, MAC2STR(selected->bssid),
+		MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
+		wpa_supplicant_state_txt(wpa_s->wpa_state),
+		ssid, wpa_s->current_ssid);
+
+	/*
+	 * Do not trigger new association unless the BSSID has changed or if
+	 * reassociation is requested. If we are in process of associating with
+	 * the selected BSSID, do not trigger new attempt.
+	 */
+	if (wpa_s->reassociate ||
+	    (os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
+	     ((wpa_s->wpa_state != WPA_ASSOCIATING &&
+	       wpa_s->wpa_state != WPA_AUTHENTICATING) ||
+	      (!is_zero_ether_addr(wpa_s->pending_bssid) &&
+	       os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) !=
+	       0) ||
+	      (is_zero_ether_addr(wpa_s->pending_bssid) &&
+	       ssid != wpa_s->current_ssid)))) {
+		if (wpa_supplicant_scard_init(wpa_s, ssid)) {
+			wpa_supplicant_req_new_scan(wpa_s, 10, 0);
+			return 0;
+		}
+		wpa_msg(wpa_s, MSG_DEBUG, "Request association with " MACSTR,
+			MAC2STR(selected->bssid));
+		wpa_supplicant_associate(wpa_s, selected, ssid);
+	} else {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Already associated or trying to "
+			"connect with the selected AP");
+	}
+
+	return 0;
+}
+
+
+static struct wpa_ssid *
+wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s)
+{
+	int prio;
+	struct wpa_ssid *ssid;
+
+	for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
+		for (ssid = wpa_s->conf->pssid[prio]; ssid; ssid = ssid->pnext)
+		{
+			if (wpas_network_disabled(wpa_s, ssid))
+				continue;
+			if (ssid->mode == IEEE80211_MODE_IBSS ||
+			    ssid->mode == IEEE80211_MODE_AP ||
+			    ssid->mode == IEEE80211_MODE_MESH)
+				return ssid;
+		}
+	}
+	return NULL;
+}
+
+
+/* TODO: move the rsn_preauth_scan_result*() to be called from notify.c based
+ * on BSS added and BSS changed events */
+static void wpa_supplicant_rsn_preauth_scan_results(
+	struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss;
+
+	if (rsn_preauth_scan_results(wpa_s->wpa) < 0)
+		return;
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		const u8 *ssid, *rsn;
+
+		ssid = wpa_bss_get_ie(bss, WLAN_EID_SSID);
+		if (ssid == NULL)
+			continue;
+
+		rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+		if (rsn == NULL)
+			continue;
+
+		rsn_preauth_scan_result(wpa_s->wpa, bss->bssid, ssid, rsn);
+	}
+
+}
+
+
+static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
+				       struct wpa_bss *selected,
+				       struct wpa_ssid *ssid)
+{
+	struct wpa_bss *current_bss = NULL;
+#ifndef CONFIG_NO_ROAMING
+	int min_diff;
+#endif /* CONFIG_NO_ROAMING */
+
+	if (wpa_s->reassociate)
+		return 1; /* explicit request to reassociate */
+	if (wpa_s->wpa_state < WPA_ASSOCIATED)
+		return 1; /* we are not associated; continue */
+	if (wpa_s->current_ssid == NULL)
+		return 1; /* unknown current SSID */
+	if (wpa_s->current_ssid != ssid)
+		return 1; /* different network block */
+
+	if (wpas_driver_bss_selection(wpa_s)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - driver suppressed");
+		return 0; /* Driver-based roaming */
+        }
+
+	if (wpa_s->current_ssid->ssid)
+		current_bss = wpa_bss_get(wpa_s, wpa_s->bssid,
+					  wpa_s->current_ssid->ssid,
+					  wpa_s->current_ssid->ssid_len);
+	if (!current_bss)
+		current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
+
+	if (!current_bss)
+		return 1; /* current BSS not seen in scan results */
+
+	if (current_bss == selected)
+		return 0;
+
+	if (selected->last_update_idx > current_bss->last_update_idx)
+		return 1; /* current BSS not seen in the last scan */
+
+#ifndef CONFIG_NO_ROAMING
+	wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
+	wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR
+		" level=%d snr=%d est_throughput=%u",
+		MAC2STR(current_bss->bssid), current_bss->level,
+		current_bss->snr, current_bss->est_throughput);
+	wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR
+		" level=%d snr=%d est_throughput=%u",
+		MAC2STR(selected->bssid), selected->level,
+		selected->snr, selected->est_throughput);
+
+	if (wpa_s->current_ssid->bssid_set &&
+	    os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) ==
+	    0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS "
+			"has preferred BSSID");
+		return 1;
+	}
+
+	if (selected->est_throughput > current_bss->est_throughput + 5000) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Allow reassociation - selected BSS has better estimated throughput");
+		return 1;
+	}
+
+	if (current_bss->level < 0 && current_bss->level > selected->level) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better "
+			"signal level");
+		return 0;
+	}
+
+	min_diff = 2;
+	if (current_bss->level < 0) {
+		if (current_bss->level < -85)
+			min_diff = 1;
+		else if (current_bss->level < -80)
+			min_diff = 2;
+		else if (current_bss->level < -75)
+			min_diff = 3;
+		else if (current_bss->level < -70)
+			min_diff = 4;
+		else
+			min_diff = 5;
+	}
+	if (abs(current_bss->level - selected->level) < min_diff) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference "
+			"in signal level");
+		return 0;
+	}
+
+	return 1;
+#else /* CONFIG_NO_ROAMING */
+	return 0;
+#endif /* CONFIG_NO_ROAMING */
+}
+
+
+/* Return != 0 if no scan results could be fetched or if scan results should not
+ * be shared with other virtual interfaces. */
+static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
+					      union wpa_event_data *data,
+					      int own_request)
+{
+	struct wpa_scan_results *scan_res = NULL;
+	int ret = 0;
+	int ap = 0;
+#ifndef CONFIG_NO_RANDOM_POOL
+	size_t i, num;
+#endif /* CONFIG_NO_RANDOM_POOL */
+
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface)
+		ap = 1;
+#endif /* CONFIG_AP */
+
+	wpa_supplicant_notify_scanning(wpa_s, 0);
+
+	scan_res = wpa_supplicant_get_scan_results(wpa_s,
+						   data ? &data->scan_info :
+						   NULL, 1);
+	if (scan_res == NULL) {
+		if (wpa_s->conf->ap_scan == 2 || ap ||
+		    wpa_s->scan_res_handler == scan_only_handler)
+			return -1;
+		if (!own_request)
+			return -1;
+		wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
+			"scanning again");
+		wpa_supplicant_req_new_scan(wpa_s, 1, 0);
+		ret = -1;
+		goto scan_work_done;
+	}
+
+#ifndef CONFIG_NO_RANDOM_POOL
+	num = scan_res->num;
+	if (num > 10)
+		num = 10;
+	for (i = 0; i < num; i++) {
+		u8 buf[5];
+		struct wpa_scan_res *res = scan_res->res[i];
+		buf[0] = res->bssid[5];
+		buf[1] = res->qual & 0xff;
+		buf[2] = res->noise & 0xff;
+		buf[3] = res->level & 0xff;
+		buf[4] = res->tsf & 0xff;
+		random_add_randomness(buf, sizeof(buf));
+	}
+#endif /* CONFIG_NO_RANDOM_POOL */
+
+	if (own_request && wpa_s->scan_res_handler &&
+	    (wpa_s->own_scan_running || !wpa_s->radio->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);
+		ret = -2;
+		goto scan_work_done;
+	}
+
+	if (ap) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Ignore scan results in AP mode");
+#ifdef CONFIG_AP
+		if (wpa_s->ap_iface->scan_cb)
+			wpa_s->ap_iface->scan_cb(wpa_s->ap_iface);
+#endif /* CONFIG_AP */
+		goto scan_work_done;
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)",
+		wpa_s->own_scan_running, wpa_s->radio->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 (!wpa_s->own_scan_running && wpa_s->radio->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 (wnm_scan_process(wpa_s, 1) > 0)
+		goto scan_work_done;
+
+	if (sme_proc_obss_scan(wpa_s) > 0)
+		goto scan_work_done;
+
+	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);
+		goto scan_work_done;
+	}
+
+	if (!wpas_driver_bss_selection(wpa_s) &&
+	    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);
+
+	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 own_request)
+{
+	struct wpa_bss *selected;
+	struct wpa_ssid *ssid = NULL;
+	int time_to_reenable = wpas_reenabled_network_time(wpa_s);
+
+	if (time_to_reenable > 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Postpone network selection by %d seconds since all networks are disabled",
+			time_to_reenable);
+		eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+		eloop_register_timeout(time_to_reenable, 0,
+				       wpas_network_reenabled, wpa_s, NULL);
+		return 0;
+	}
+
+	if (wpa_s->p2p_mgmt)
+		return 0; /* no normal connection on p2p_mgmt interface */
+
+	selected = wpa_supplicant_pick_network(wpa_s, &ssid);
+
+	if (selected) {
+		int skip;
+		skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
+		if (skip) {
+			if (new_scan)
+				wpa_supplicant_rsn_preauth_scan_results(wpa_s);
+			return 0;
+		}
+
+		if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
+			return -1;
+		}
+		if (new_scan)
+			wpa_supplicant_rsn_preauth_scan_results(wpa_s);
+		/*
+		 * Do not notify other virtual radios of scan results since we do not
+		 * want them to start other associations at the same time.
+		 */
+		return 1;
+	} else {
+#ifdef CONFIG_MESH
+		if (wpa_s->ifmsh) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"Avoiding join because we already joined a mesh group");
+			return 0;
+		}
+#endif /* CONFIG_MESH */
+		wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
+		ssid = wpa_supplicant_pick_new_network(wpa_s);
+		if (ssid) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "Setup a new network");
+			wpa_supplicant_associate(wpa_s, NULL, ssid);
+			if (new_scan)
+				wpa_supplicant_rsn_preauth_scan_results(wpa_s);
+		} 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
+			int res;
+
+			res = wpas_p2p_scan_no_go_seen(wpa_s);
+			if (res == 2)
+				return 2;
+			if (res == 1)
+				return 0;
+
+			if (wpa_s->p2p_in_provisioning ||
+			    wpa_s->show_group_started ||
+			    wpa_s->p2p_in_invitation) {
+				/*
+				 * Use shorter wait during P2P Provisioning
+				 * state and during P2P join-a-group operation
+				 * to speed up group formation.
+				 */
+				timeout_sec = 0;
+				timeout_usec = 250000;
+				wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+							    timeout_usec);
+				return 0;
+			}
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+			if (wpa_s->conf->auto_interworking &&
+			    wpa_s->conf->interworking &&
+			    wpa_s->conf->cred) {
+				wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: "
+					"start ANQP fetch since no matching "
+					"networks found");
+				wpa_s->network_select = 1;
+				wpa_s->auto_network_select = 1;
+				interworking_start_fetch_anqp(wpa_s);
+				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);
+
+			wpa_msg_ctrl(wpa_s, MSG_INFO,
+				     WPA_EVENT_NETWORK_NOT_FOUND);
+		}
+	}
+	return 0;
+}
+
+
+static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
+					     union wpa_event_data *data)
+{
+	struct wpa_supplicant *ifs;
+	int res;
+
+	res = _wpa_supplicant_event_scan_results(wpa_s, data, 1);
+	if (res == 2) {
+		/*
+		 * Interface may have been removed, so must not dereference
+		 * wpa_s after this.
+		 */
+		return 1;
+	}
+	if (res != 0) {
+		/*
+		 * If no scan results could be fetched, then no need to
+		 * notify those interfaces that did not actually request
+		 * this scan. Similarly, if scan results started a new operation on this
+		 * interface, do not notify other interfaces to avoid concurrent
+		 * operations during a connection attempt.
+		 */
+		return 0;
+	}
+
+	/*
+	 * Check other interfaces to see if they share the same radio. If
+	 * so, they get updated with this same scan 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 scan results from "
+				   "sibling", ifs->ifname);
+			_wpa_supplicant_event_scan_results(ifs, data, 0);
+		}
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+
+
+int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_NO_SCAN_PROCESSING
+	return -1;
+#else /* CONFIG_NO_SCAN_PROCESSING */
+	struct os_reltime now;
+
+	if (wpa_s->last_scan_res_used == 0)
+		return -1;
+
+	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, 1);
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+}
+
+#ifdef CONFIG_WNM
+
+static void wnm_bss_keep_alive(void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (wpa_s->wpa_state < WPA_ASSOCIATED)
+		return;
+
+	if (!wpa_s->no_keep_alive) {
+		wpa_printf(MSG_DEBUG, "WNM: Send keep-alive to AP " MACSTR,
+			   MAC2STR(wpa_s->bssid));
+		/* TODO: could skip this if normal data traffic has been sent */
+		/* TODO: Consider using some more appropriate data frame for
+		 * this */
+		if (wpa_s->l2)
+			l2_packet_send(wpa_s->l2, wpa_s->bssid, 0x0800,
+				       (u8 *) "", 0);
+	}
+
+#ifdef CONFIG_SME
+	if (wpa_s->sme.bss_max_idle_period) {
+		unsigned int msec;
+		msec = wpa_s->sme.bss_max_idle_period * 1024; /* times 1000 */
+		if (msec > 100)
+			msec -= 100;
+		eloop_register_timeout(msec / 1000, msec % 1000 * 1000,
+				       wnm_bss_keep_alive, wpa_s, NULL);
+	}
+#endif /* CONFIG_SME */
+}
+
+
+static void wnm_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;
+
+#ifdef CONFIG_SME
+	if (elems.bss_max_idle_period) {
+		unsigned int msec;
+		wpa_s->sme.bss_max_idle_period =
+			WPA_GET_LE16(elems.bss_max_idle_period);
+		wpa_printf(MSG_DEBUG, "WNM: BSS Max Idle Period: %u (* 1000 "
+			   "TU)%s", wpa_s->sme.bss_max_idle_period,
+			   (elems.bss_max_idle_period[2] & 0x01) ?
+			   " (protected keep-live required)" : "");
+		if (wpa_s->sme.bss_max_idle_period == 0)
+			wpa_s->sme.bss_max_idle_period = 1;
+		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) {
+			eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL);
+			 /* msec times 1000 */
+			msec = wpa_s->sme.bss_max_idle_period * 1024;
+			if (msec > 100)
+				msec -= 100;
+			eloop_register_timeout(msec / 1000, msec % 1000 * 1000,
+					       wnm_bss_keep_alive, wpa_s,
+					       NULL);
+		}
+	}
+#endif /* CONFIG_SME */
+}
+
+#endif /* CONFIG_WNM */
+
+
+void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WNM
+	eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL);
+#endif /* CONFIG_WNM */
+}
+
+
+#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)
+{
+	int l, len, found = 0, wpa_found, rsn_found;
+	const u8 *p;
+#ifdef CONFIG_IEEE80211R
+	u8 bssid[ETH_ALEN];
+#endif /* CONFIG_IEEE80211R */
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Association info event");
+	if (data->assoc_info.req_ies)
+		wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies,
+			    data->assoc_info.req_ies_len);
+	if (data->assoc_info.resp_ies) {
+		wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies,
+			    data->assoc_info.resp_ies_len);
+#ifdef CONFIG_TDLS
+		wpa_tdls_assoc_resp_ies(wpa_s->wpa, data->assoc_info.resp_ies,
+					data->assoc_info.resp_ies_len);
+#endif /* CONFIG_TDLS */
+#ifdef CONFIG_WNM
+		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",
+			    data->assoc_info.beacon_ies,
+			    data->assoc_info.beacon_ies_len);
+	if (data->assoc_info.freq)
+		wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz",
+			data->assoc_info.freq);
+
+	p = data->assoc_info.req_ies;
+	l = data->assoc_info.req_ies_len;
+
+	/* Go through the IEs and make a copy of the WPA/RSN IE, if present. */
+	while (p && l >= 2) {
+		len = p[1] + 2;
+		if (len > l) {
+			wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info",
+				    p, l);
+			break;
+		}
+		if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
+		     (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
+		    (p[0] == WLAN_EID_RSN && p[1] >= 2)) {
+			if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len))
+				break;
+			found = 1;
+			wpa_find_assoc_pmkid(wpa_s);
+			break;
+		}
+		l -= len;
+		p += len;
+	}
+	if (!found && data->assoc_info.req_ies)
+		wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+
+#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_SME
+	if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) {
+		if (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
+		    wpa_ft_validate_reassoc_resp(wpa_s->wpa,
+						 data->assoc_info.resp_ies,
+						 data->assoc_info.resp_ies_len,
+						 bssid) < 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of "
+				"Reassociation Response failed");
+			wpa_supplicant_deauthenticate(
+				wpa_s, WLAN_REASON_INVALID_IE);
+			return -1;
+		}
+	}
+
+	p = data->assoc_info.resp_ies;
+	l = data->assoc_info.resp_ies_len;
+
+#ifdef CONFIG_WPS_STRICT
+	if (p && wpa_s->current_ssid &&
+	    wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
+		struct wpabuf *wps;
+		wps = ieee802_11_vendor_ie_concat(p, l, WPS_IE_VENDOR_TYPE);
+		if (wps == NULL) {
+			wpa_msg(wpa_s, MSG_INFO, "WPS-STRICT: AP did not "
+				"include WPS IE in (Re)Association Response");
+			return -1;
+		}
+
+		if (wps_validate_assoc_resp(wps) < 0) {
+			wpabuf_free(wps);
+			wpa_supplicant_deauthenticate(
+				wpa_s, WLAN_REASON_INVALID_IE);
+			return -1;
+		}
+		wpabuf_free(wps);
+	}
+#endif /* CONFIG_WPS_STRICT */
+
+	/* Go through the IEs and make a copy of the MDIE, if present. */
+	while (p && l >= 2) {
+		len = p[1] + 2;
+		if (len > l) {
+			wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info",
+				    p, l);
+			break;
+		}
+		if (p[0] == WLAN_EID_MOBILITY_DOMAIN &&
+		    p[1] >= MOBILITY_DOMAIN_ID_LEN) {
+			wpa_s->sme.ft_used = 1;
+			os_memcpy(wpa_s->sme.mobility_domain, p + 2,
+				  MOBILITY_DOMAIN_ID_LEN);
+			break;
+		}
+		l -= len;
+		p += len;
+	}
+#endif /* CONFIG_SME */
+
+	/* Process FT when SME is in the driver */
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+	    wpa_ft_is_completed(wpa_s->wpa)) {
+		if (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
+		    wpa_ft_validate_reassoc_resp(wpa_s->wpa,
+						 data->assoc_info.resp_ies,
+						 data->assoc_info.resp_ies_len,
+						 bssid) < 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of "
+				"Reassociation Response failed");
+			wpa_supplicant_deauthenticate(
+				wpa_s, WLAN_REASON_INVALID_IE);
+			return -1;
+		}
+		wpa_dbg(wpa_s, MSG_DEBUG, "FT: Reassociation Response done");
+	}
+
+	wpa_sm_set_ft_params(wpa_s->wpa, data->assoc_info.resp_ies,
+			     data->assoc_info.resp_ies_len);
+#endif /* CONFIG_IEEE80211R */
+
+	/* WPA/RSN IE from Beacon/ProbeResp */
+	p = data->assoc_info.beacon_ies;
+	l = data->assoc_info.beacon_ies_len;
+
+	/* Go through the IEs and make a copy of the WPA/RSN IEs, if present.
+	 */
+	wpa_found = rsn_found = 0;
+	while (p && l >= 2) {
+		len = p[1] + 2;
+		if (len > l) {
+			wpa_hexdump(MSG_DEBUG, "Truncated IE in beacon_ies",
+				    p, l);
+			break;
+		}
+		if (!wpa_found &&
+		    p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
+		    os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0) {
+			wpa_found = 1;
+			wpa_sm_set_ap_wpa_ie(wpa_s->wpa, p, len);
+		}
+
+		if (!rsn_found &&
+		    p[0] == WLAN_EID_RSN && p[1] >= 2) {
+			rsn_found = 1;
+			wpa_sm_set_ap_rsn_ie(wpa_s->wpa, p, len);
+		}
+
+		l -= len;
+		p += len;
+	}
+
+	if (!wpa_found && data->assoc_info.beacon_ies)
+		wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0);
+	if (!rsn_found && data->assoc_info.beacon_ies)
+		wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
+	if (wpa_found || rsn_found)
+		wpa_s->ap_ies_from_associnfo = 1;
+
+#ifdef CONFIG_FST
+	wpabuf_free(wpa_s->received_mb_ies);
+	wpa_s->received_mb_ies = NULL;
+	if (wpa_s->fst) {
+		struct mb_ies_info mb_ies;
+
+		wpa_printf(MSG_DEBUG, "Looking for MB IE");
+		if (!mb_ies_info_by_ies(&mb_ies, data->assoc_info.resp_ies,
+					data->assoc_info.resp_ies_len))
+			wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies);
+	}
+#endif /* CONFIG_FST */
+
+	if (wpa_s->assoc_freq && data->assoc_info.freq &&
+	    wpa_s->assoc_freq != data->assoc_info.freq) {
+		wpa_printf(MSG_DEBUG, "Operating frequency changed from "
+			   "%u to %u MHz",
+			   wpa_s->assoc_freq, data->assoc_info.freq);
+		wpa_supplicant_update_scan_results(wpa_s);
+	}
+
+	wpa_s->assoc_freq = data->assoc_info.freq;
+
+	return 0;
+}
+
+
+static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
+{
+	const u8 *bss_wpa = NULL, *bss_rsn = NULL;
+
+	if (!wpa_s->current_bss || !wpa_s->current_ssid)
+		return -1;
+
+	if (!wpa_key_mgmt_wpa_any(wpa_s->current_ssid->key_mgmt))
+		return 0;
+
+	bss_wpa = wpa_bss_get_vendor_ie(wpa_s->current_bss,
+					WPA_IE_VENDOR_TYPE);
+	bss_rsn = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSN);
+
+	if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
+				 bss_wpa ? 2 + bss_wpa[1] : 0) ||
+	    wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
+				 bss_rsn ? 2 + bss_rsn[1] : 0))
+		return -1;
+
+	return 0;
+}
+
+
+static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
+				       union wpa_event_data *data)
+{
+	u8 bssid[ETH_ALEN];
+	int ft_completed;
+	int new_bss = 0;
+
+#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,
+				    data->assoc_info.req_ies_len,
+				    data->assoc_info.reassoc);
+		return;
+	}
+#endif /* CONFIG_AP */
+
+	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+
+	ft_completed = wpa_ft_is_completed(wpa_s->wpa);
+	if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0)
+		return;
+
+	if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+		wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID");
+		wpa_supplicant_deauthenticate(
+			wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+		return;
+	}
+
+	wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED);
+	if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID="
+			MACSTR, MAC2STR(bssid));
+		new_bss = 1;
+		random_add_randomness(bssid, ETH_ALEN);
+		os_memcpy(wpa_s->bssid, bssid, ETH_ALEN);
+		os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+		wpas_notify_bssid_changed(wpa_s);
+
+		if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) {
+			wpa_clear_keys(wpa_s, bssid);
+		}
+		if (wpa_supplicant_select_config(wpa_s) < 0) {
+			wpa_supplicant_deauthenticate(
+				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+			return;
+		}
+	}
+
+	if (wpa_s->conf->ap_scan == 1 &&
+	    wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) {
+		if (wpa_supplicant_assoc_update_ie(wpa_s) < 0 && new_bss)
+			wpa_msg(wpa_s, MSG_WARNING,
+				"WPA/RSN IEs not updated");
+	}
+
+#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));
+	if (wpa_s->current_ssid) {
+		/* When using scanning (ap_scan=1), SIM PC/SC interface can be
+		 * initialized before association, but for other modes,
+		 * initialize PC/SC here, if the current configuration needs
+		 * smartcard or SIM/USIM. */
+		wpa_supplicant_scard_init(wpa_s, wpa_s->current_ssid);
+	}
+	wpa_sm_notify_assoc(wpa_s->wpa, bssid);
+	if (wpa_s->l2)
+		l2_packet_notify_auth_start(wpa_s->l2);
+
+	/*
+	 * Set portEnabled first to FALSE in order to get EAP state machine out
+	 * of the SUCCESS state and eapSuccess cleared. Without this, EAPOL PAE
+	 * state machine may transit to AUTHENTICATING state based on obsolete
+	 * eapSuccess and then trigger BE_AUTH to SUCCESS and PAE to
+	 * AUTHENTICATED without ever giving chance to EAP state machine to
+	 * reset the state.
+	 */
+	if (!ft_completed) {
+		eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
+		eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+	}
+	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed)
+		eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+	/* 802.1X::portControl = Auto */
+	eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE);
+	wpa_s->eapol_received = 0;
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE ||
+	    (wpa_s->current_ssid &&
+	     wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) {
+		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)) {
+			/*
+			 * Set the key after having received joined-IBSS event
+			 * from the driver.
+			 */
+			wpa_supplicant_set_wpa_none_key(wpa_s,
+							wpa_s->current_ssid);
+		}
+		wpa_supplicant_cancel_auth_timeout(wpa_s);
+		wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+	} else if (!ft_completed) {
+		/* Timeout for receiving the first EAPOL packet */
+		wpa_supplicant_req_auth_timeout(wpa_s, 10, 0);
+	}
+	wpa_supplicant_cancel_scan(wpa_s);
+
+	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+	    wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
+		/*
+		 * We are done; the driver will take care of RSN 4-way
+		 * handshake.
+		 */
+		wpa_supplicant_cancel_auth_timeout(wpa_s);
+		wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+		eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+		eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+	} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+		   wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+		/*
+		 * The driver will take care of RSN 4-way handshake, so we need
+		 * to allow EAPOL supplicant to complete its work without
+		 * waiting for WPA supplicant.
+		 */
+		eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+	} else if (ft_completed) {
+		/*
+		 * FT protocol completed - make sure EAPOL state machine ends
+		 * up in authenticated.
+		 */
+		wpa_supplicant_cancel_auth_timeout(wpa_s);
+		wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+		eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+		eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+	}
+
+	wpa_s->last_eapol_matches_bssid = 0;
+
+	if (wpa_s->pending_eapol_rx) {
+		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) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL "
+				"frame that was received just before "
+				"association notification");
+			wpa_supplicant_rx_eapol(
+				wpa_s, wpa_s->pending_eapol_rx_src,
+				wpabuf_head(wpa_s->pending_eapol_rx),
+				wpabuf_len(wpa_s->pending_eapol_rx));
+		}
+		wpabuf_free(wpa_s->pending_eapol_rx);
+		wpa_s->pending_eapol_rx = NULL;
+	}
+
+	if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+	     wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
+	    wpa_s->current_ssid &&
+	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) {
+		/* Set static WEP keys again */
+		wpa_set_wep_keys(wpa_s, wpa_s->current_ssid);
+	}
+
+#ifdef CONFIG_IBSS_RSN
+	if (wpa_s->current_ssid &&
+	    wpa_s->current_ssid->mode == WPAS_MODE_IBSS &&
+	    wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
+	    wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE &&
+	    wpa_s->ibss_rsn == NULL) {
+		wpa_s->ibss_rsn = ibss_rsn_init(wpa_s);
+		if (!wpa_s->ibss_rsn) {
+			wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN");
+			wpa_supplicant_deauthenticate(
+				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+			return;
+		}
+
+		ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk);
+	}
+#endif /* CONFIG_IBSS_RSN */
+
+	wpas_wps_notify_assoc(wpa_s, bssid);
+
+	if (data) {
+		wmm_ac_notify_assoc(wpa_s, data->assoc_info.resp_ies,
+				    data->assoc_info.resp_ies_len,
+				    &data->assoc_info.wmm_params);
+
+		if (wpa_s->reassoc_same_bss)
+			wmm_ac_restore_tspecs(wpa_s);
+	}
+}
+
+
+static int disconnect_reason_recoverable(u16 reason_code)
+{
+	return reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY ||
+		reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA ||
+		reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
+}
+
+
+static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
+					  u16 reason_code,
+					  int locally_generated)
+{
+	const u8 *bssid;
+
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+		/*
+		 * At least Host AP driver and a Prism3 card seemed to be
+		 * generating streams of disconnected events when configuring
+		 * IBSS for WPA-None. Ignore them for now.
+		 */
+		return;
+	}
+
+	bssid = wpa_s->bssid;
+	if (is_zero_ether_addr(bssid))
+		bssid = wpa_s->pending_bssid;
+
+	if (!is_zero_ether_addr(bssid) ||
+	    wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
+			" reason=%d%s",
+			MAC2STR(bssid), reason_code,
+			locally_generated ? " locally_generated=1" : "");
+	}
+}
+
+
+static int could_be_psk_mismatch(struct wpa_supplicant *wpa_s, u16 reason_code,
+				 int locally_generated)
+{
+	if (wpa_s->wpa_state != WPA_4WAY_HANDSHAKE ||
+	    !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt))
+		return 0; /* Not in 4-way handshake with PSK */
+
+	/*
+	 * It looks like connection was lost while trying to go through PSK
+	 * 4-way handshake. Filter out known disconnection cases that are caused
+	 * by something else than PSK mismatch to avoid confusing reports.
+	 */
+
+	if (locally_generated) {
+		if (reason_code == WLAN_REASON_IE_IN_4WAY_DIFFERS)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
+						 u16 reason_code,
+						 int locally_generated)
+{
+	const u8 *bssid;
+	int authenticating;
+	u8 prev_pending_bssid[ETH_ALEN];
+	struct wpa_bss *fast_reconnect = NULL;
+	struct wpa_ssid *fast_reconnect_ssid = NULL;
+	struct wpa_ssid *last_ssid;
+
+	authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
+	os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN);
+
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+		/*
+		 * At least Host AP driver and a Prism3 card seemed to be
+		 * generating streams of disconnected events when configuring
+		 * IBSS for WPA-None. Ignore them for now.
+		 */
+		wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - ignore in "
+			"IBSS/WPA-None mode");
+		return;
+	}
+
+	if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) {
+		wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
+			"pre-shared key may be incorrect");
+		if (wpas_p2p_4way_hs_failed(wpa_s) > 0)
+			return; /* P2P group removed */
+		wpas_auth_failed(wpa_s, "WRONG_KEY");
+	}
+	if (!wpa_s->disconnected &&
+	    (!wpa_s->auto_reconnect_disabled ||
+	     wpa_s->key_mgmt == WPA_KEY_MGMT_WPS ||
+	     wpas_wps_searching(wpa_s))) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to "
+			"reconnect (wps=%d/%d wpa_state=%d)",
+			wpa_s->key_mgmt == WPA_KEY_MGMT_WPS,
+			wpas_wps_searching(wpa_s),
+			wpa_s->wpa_state);
+		if (wpa_s->wpa_state == WPA_COMPLETED &&
+		    wpa_s->current_ssid &&
+		    wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
+		    !locally_generated &&
+		    disconnect_reason_recoverable(reason_code)) {
+			/*
+			 * It looks like the AP has dropped association with
+			 * us, but could allow us to get back in. Try to
+			 * reconnect to the same BSS without full scan to save
+			 * time for some common cases.
+			 */
+			fast_reconnect = wpa_s->current_bss;
+			fast_reconnect_ssid = wpa_s->current_ssid;
+		} else if (wpa_s->wpa_state >= WPA_ASSOCIATING)
+			wpa_supplicant_req_scan(wpa_s, 0, 100000);
+		else
+			wpa_dbg(wpa_s, MSG_DEBUG, "Do not request new "
+				"immediate scan");
+	} else {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect disabled: do not "
+			"try to re-connect");
+		wpa_s->reassociate = 0;
+		wpa_s->disconnected = 1;
+		if (!wpa_s->pno)
+			wpa_supplicant_cancel_sched_scan(wpa_s);
+	}
+	bssid = wpa_s->bssid;
+	if (is_zero_ether_addr(bssid))
+		bssid = wpa_s->pending_bssid;
+	if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+		wpas_connection_failed(wpa_s, bssid);
+	wpa_sm_notify_disassoc(wpa_s->wpa);
+	if (locally_generated)
+		wpa_s->disconnect_reason = -reason_code;
+	else
+		wpa_s->disconnect_reason = reason_code;
+	wpas_notify_disconnect_reason(wpa_s);
+	if (wpa_supplicant_dynamic_keys(wpa_s)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
+		wpa_clear_keys(wpa_s, wpa_s->bssid);
+	}
+	last_ssid = wpa_s->current_ssid;
+	wpa_supplicant_mark_disassoc(wpa_s);
+
+	if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) {
+		sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid);
+		wpa_s->current_ssid = last_ssid;
+	}
+
+	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,
+					   fast_reconnect_ssid) < 0) {
+			/* Recover through full scan */
+			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);
+	}
+}
+
+
+#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (!wpa_s->pending_mic_error_report)
+		return;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Sending pending MIC error report");
+	wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise);
+	wpa_s->pending_mic_error_report = 0;
+}
+#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
+
+
+static void
+wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s,
+					 union wpa_event_data *data)
+{
+	int pairwise;
+	struct os_reltime t;
+
+	wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected");
+	pairwise = (data && data->michael_mic_failure.unicast);
+	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) {
+			/*
+			 * Send the pending MIC error report immediately since
+			 * we are going to start countermeasures and AP better
+			 * do the same.
+			 */
+			wpa_sm_key_request(wpa_s->wpa, 1,
+					   wpa_s->pending_mic_error_pairwise);
+		}
+
+		/* Send the new MIC error report immediately since we are going
+		 * to start countermeasures and AP better do the same.
+		 */
+		wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
+
+		/* initialize countermeasures */
+		wpa_s->countermeasures = 1;
+
+		wpa_blacklist_add(wpa_s, wpa_s->bssid);
+
+		wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures started");
+
+		/*
+		 * Need to wait for completion of request frame. We do not get
+		 * any callback for the message completion, so just wait a
+		 * short while and hope for the best. */
+		os_sleep(0, 10000);
+
+		wpa_drv_set_countermeasures(wpa_s, 1);
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_MICHAEL_MIC_FAILURE);
+		eloop_cancel_timeout(wpa_supplicant_stop_countermeasures,
+				     wpa_s, NULL);
+		eloop_register_timeout(60, 0,
+				       wpa_supplicant_stop_countermeasures,
+				       wpa_s, NULL);
+		/* TODO: mark the AP rejected for 60 second. STA is
+		 * allowed to associate with another AP.. */
+	} else {
+#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+		if (wpa_s->mic_errors_seen) {
+			/*
+			 * Reduce the effectiveness of Michael MIC error
+			 * reports as a means for attacking against TKIP if
+			 * more than one MIC failure is noticed with the same
+			 * PTK. We delay the transmission of the reports by a
+			 * random time between 0 and 60 seconds in order to
+			 * force the attacker wait 60 seconds before getting
+			 * the information on whether a frame resulted in a MIC
+			 * failure.
+			 */
+			u8 rval[4];
+			int sec;
+
+			if (os_get_random(rval, sizeof(rval)) < 0)
+				sec = os_random() % 60;
+			else
+				sec = WPA_GET_BE32(rval) % 60;
+			wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Delay MIC error "
+				"report %d seconds", sec);
+			wpa_s->pending_mic_error_report = 1;
+			wpa_s->pending_mic_error_pairwise = pairwise;
+			eloop_cancel_timeout(
+				wpa_supplicant_delayed_mic_error_report,
+				wpa_s, NULL);
+			eloop_register_timeout(
+				sec, os_random() % 1000000,
+				wpa_supplicant_delayed_mic_error_report,
+				wpa_s, NULL);
+		} else {
+			wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
+		}
+#else /* CONFIG_DELAYED_MIC_ERROR_REPORT */
+		wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
+#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
+	}
+	wpa_s->last_michael_mic_error = t;
+	wpa_s->mic_errors_seen++;
+}
+
+
+#ifdef CONFIG_TERMINATE_ONLASTIF
+static int any_interfaces(struct wpa_supplicant *head)
+{
+	struct wpa_supplicant *wpa_s;
+
+	for (wpa_s = head; wpa_s != NULL; wpa_s = wpa_s->next)
+		if (!wpa_s->interface_removed)
+			return 1;
+	return 0;
+}
+#endif /* CONFIG_TERMINATE_ONLASTIF */
+
+
+static void
+wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s,
+				      union wpa_event_data *data)
+{
+	if (os_strcmp(wpa_s->ifname, data->interface_status.ifname) != 0)
+		return;
+
+	switch (data->interface_status.ievent) {
+	case EVENT_INTERFACE_ADDED:
+		if (!wpa_s->interface_removed)
+			break;
+		wpa_s->interface_removed = 0;
+		wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was added");
+		if (wpa_supplicant_driver_init(wpa_s) < 0) {
+			wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the "
+				"driver after interface was added");
+		}
+
+#ifdef CONFIG_P2P
+		if (!wpa_s->global->p2p &&
+		    !wpa_s->global->p2p_disabled &&
+		    !wpa_s->conf->p2p_disabled &&
+		    (wpa_s->drv_flags &
+		     WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
+		    wpas_p2p_add_p2pdev_interface(
+			    wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) {
+			wpa_printf(MSG_INFO,
+				   "P2P: Failed to enable P2P Device interface");
+			/* Try to continue without. P2P will be disabled. */
+		}
+#endif /* CONFIG_P2P */
+
+		break;
+	case EVENT_INTERFACE_REMOVED:
+		wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed");
+		wpa_s->interface_removed = 1;
+		wpa_supplicant_mark_disassoc(wpa_s);
+		wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
+		l2_packet_deinit(wpa_s->l2);
+		wpa_s->l2 = NULL;
+
+#ifdef CONFIG_P2P
+		if (wpa_s->global->p2p &&
+		    wpa_s->global->p2p_init_wpa_s->parent == wpa_s &&
+		    (wpa_s->drv_flags &
+		     WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Removing P2P Device interface");
+			wpa_supplicant_remove_iface(
+				wpa_s->global, wpa_s->global->p2p_init_wpa_s,
+				0);
+			wpa_s->global->p2p_init_wpa_s = NULL;
+		}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_TERMINATE_ONLASTIF
+		/* check if last interface */
+		if (!any_interfaces(wpa_s->global->ifaces))
+			eloop_terminate();
+#endif /* CONFIG_TERMINATE_ONLASTIF */
+		break;
+	}
+}
+
+
+#ifdef CONFIG_PEERKEY
+static void
+wpa_supplicant_event_stkstart(struct wpa_supplicant *wpa_s,
+			      union wpa_event_data *data)
+{
+	if (data == NULL)
+		return;
+	wpa_sm_stkstart(wpa_s->wpa, data->stkstart.peer);
+}
+#endif /* CONFIG_PEERKEY */
+
+
+#ifdef CONFIG_TDLS
+static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s,
+				      union wpa_event_data *data)
+{
+	if (data == NULL)
+		return;
+	switch (data->tdls.oper) {
+	case TDLS_REQUEST_SETUP:
+		wpa_tdls_remove(wpa_s->wpa, data->tdls.peer);
+		if (wpa_tdls_is_external_setup(wpa_s->wpa))
+			wpa_tdls_start(wpa_s->wpa, data->tdls.peer);
+		else
+			wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, data->tdls.peer);
+		break;
+	case TDLS_REQUEST_TEARDOWN:
+		if (wpa_tdls_is_external_setup(wpa_s->wpa))
+			wpa_tdls_teardown_link(wpa_s->wpa, data->tdls.peer,
+					       data->tdls.reason_code);
+		else
+			wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN,
+					  data->tdls.peer);
+		break;
+	case TDLS_REQUEST_DISCOVER:
+			wpa_tdls_send_discovery_request(wpa_s->wpa,
+							data->tdls.peer);
+		break;
+	}
+}
+#endif /* CONFIG_TDLS */
+
+
+#ifdef CONFIG_WNM
+static void wpa_supplicant_event_wnm(struct wpa_supplicant *wpa_s,
+				     union wpa_event_data *data)
+{
+	if (data == NULL)
+		return;
+	switch (data->wnm.oper) {
+	case WNM_OPER_SLEEP:
+		wpa_printf(MSG_DEBUG, "Start sending WNM-Sleep Request "
+			   "(action=%d, intval=%d)",
+			   data->wnm.sleep_action, data->wnm.sleep_intval);
+		ieee802_11_send_wnmsleep_req(wpa_s, data->wnm.sleep_action,
+					     data->wnm.sleep_intval, NULL);
+		break;
+	}
+}
+#endif /* CONFIG_WNM */
+
+
+#ifdef CONFIG_IEEE80211R
+static void
+wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s,
+				 union wpa_event_data *data)
+{
+	if (data == NULL)
+		return;
+
+	if (wpa_ft_process_response(wpa_s->wpa, data->ft_ies.ies,
+				    data->ft_ies.ies_len,
+				    data->ft_ies.ft_action,
+				    data->ft_ies.target_ap,
+				    data->ft_ies.ric_ies,
+				    data->ft_ies.ric_ies_len) < 0) {
+		/* TODO: prevent MLME/driver from trying to associate? */
+	}
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+#ifdef CONFIG_IBSS_RSN
+static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s,
+						union wpa_event_data *data)
+{
+	struct wpa_ssid *ssid;
+	if (wpa_s->wpa_state < WPA_ASSOCIATED)
+		return;
+	if (data == NULL)
+		return;
+	ssid = wpa_s->current_ssid;
+	if (ssid == NULL)
+		return;
+	if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt))
+		return;
+
+	ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer);
+}
+
+
+static void wpa_supplicant_event_ibss_auth(struct wpa_supplicant *wpa_s,
+					   union wpa_event_data *data)
+{
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (ssid == NULL)
+		return;
+
+	/* check if the ssid is correctly configured as IBSS/RSN */
+	if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt))
+		return;
+
+	ibss_rsn_handle_auth(wpa_s->ibss_rsn, data->rx_mgmt.frame,
+			     data->rx_mgmt.frame_len);
+}
+#endif /* CONFIG_IBSS_RSN */
+
+
+#ifdef CONFIG_IEEE80211R
+static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data,
+			 size_t len)
+{
+	const u8 *sta_addr, *target_ap_addr;
+	u16 status;
+
+	wpa_hexdump(MSG_MSGDUMP, "FT: RX Action", data, len);
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+		return; /* only SME case supported for now */
+	if (len < 1 + 2 * ETH_ALEN + 2)
+		return;
+	if (data[0] != 2)
+		return; /* Only FT Action Response is supported for now */
+	sta_addr = data + 1;
+	target_ap_addr = data + 1 + ETH_ALEN;
+	status = WPA_GET_LE16(data + 1 + 2 * ETH_ALEN);
+	wpa_dbg(wpa_s, MSG_DEBUG, "FT: Received FT Action Response: STA "
+		MACSTR " TargetAP " MACSTR " status %u",
+		MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
+
+	if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "FT: Foreign STA Address " MACSTR
+			" in FT Action Response", MAC2STR(sta_addr));
+		return;
+	}
+
+	if (status) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "FT: FT Action Response indicates "
+			"failure (status code %d)", status);
+		/* TODO: report error to FT code(?) */
+		return;
+	}
+
+	if (wpa_ft_process_response(wpa_s->wpa, data + 1 + 2 * ETH_ALEN + 2,
+				    len - (1 + 2 * ETH_ALEN + 2), 1,
+				    target_ap_addr, NULL, 0) < 0)
+		return;
+
+#ifdef CONFIG_SME
+	{
+		struct wpa_bss *bss;
+		bss = wpa_bss_get_bssid(wpa_s, target_ap_addr);
+		if (bss)
+			wpa_s->sme.freq = bss->freq;
+		wpa_s->sme.auth_alg = WPA_AUTH_ALG_FT;
+		sme_associate(wpa_s, WPAS_MODE_INFRA, target_ap_addr,
+			      WLAN_AUTH_FT);
+	}
+#endif /* CONFIG_SME */
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+static void wpa_supplicant_event_unprot_deauth(struct wpa_supplicant *wpa_s,
+					       struct unprot_deauth *e)
+{
+#ifdef CONFIG_IEEE80211W
+	wpa_printf(MSG_DEBUG, "Unprotected Deauthentication frame "
+		   "dropped: " MACSTR " -> " MACSTR
+		   " (reason code %u)",
+		   MAC2STR(e->sa), MAC2STR(e->da), e->reason_code);
+	sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code);
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+static void wpa_supplicant_event_unprot_disassoc(struct wpa_supplicant *wpa_s,
+						 struct unprot_disassoc *e)
+{
+#ifdef CONFIG_IEEE80211W
+	wpa_printf(MSG_DEBUG, "Unprotected Disassociation frame "
+		   "dropped: " MACSTR " -> " MACSTR
+		   " (reason code %u)",
+		   MAC2STR(e->sa), MAC2STR(e->da), e->reason_code);
+	sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code);
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+static void wpas_event_disconnect(struct wpa_supplicant *wpa_s, const u8 *addr,
+				  u16 reason_code, int locally_generated,
+				  const u8 *ie, size_t ie_len, int deauth)
+{
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface && addr) {
+		hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], addr);
+		return;
+	}
+
+	if (wpa_s->ap_iface) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in AP mode");
+		return;
+	}
+#endif /* CONFIG_AP */
+
+	if (!locally_generated)
+		wpa_s->own_disconnect_req = 0;
+
+	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))) &&
+	     !wpa_s->eap_expected_failure))
+		wpas_auth_failed(wpa_s, "AUTH_FAILED");
+
+#ifdef CONFIG_P2P
+	if (deauth && reason_code > 0) {
+		if (wpas_p2p_deauth_notif(wpa_s, addr, reason_code, ie, ie_len,
+					  locally_generated) > 0) {
+			/*
+			 * The interface was removed, so cannot continue
+			 * processing any additional operations after this.
+			 */
+			return;
+		}
+	}
+#endif /* CONFIG_P2P */
+
+	wpa_supplicant_event_disassoc_finish(wpa_s, reason_code,
+					     locally_generated);
+}
+
+
+static void wpas_event_disassoc(struct wpa_supplicant *wpa_s,
+				struct disassoc_info *info)
+{
+	u16 reason_code = 0;
+	int locally_generated = 0;
+	const u8 *addr = NULL;
+	const u8 *ie = NULL;
+	size_t ie_len = 0;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification");
+
+	if (info) {
+		addr = info->addr;
+		ie = info->ie;
+		ie_len = info->ie_len;
+		reason_code = info->reason_code;
+		locally_generated = info->locally_generated;
+		wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", reason_code,
+			locally_generated ? " (locally generated)" : "");
+		if (addr)
+			wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
+				MAC2STR(addr));
+		wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)",
+			    ie, ie_len);
+	}
+
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface && info && info->addr) {
+		hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], info->addr);
+		return;
+	}
+
+	if (wpa_s->ap_iface) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in AP mode");
+		return;
+	}
+#endif /* CONFIG_AP */
+
+#ifdef CONFIG_P2P
+	if (info) {
+		wpas_p2p_disassoc_notif(
+			wpa_s, info->addr, reason_code, info->ie, info->ie_len,
+			locally_generated);
+	}
+#endif /* CONFIG_P2P */
+
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+		sme_event_disassoc(wpa_s, info);
+
+	wpas_event_disconnect(wpa_s, addr, reason_code, locally_generated,
+			      ie, ie_len, 0);
+}
+
+
+static void wpas_event_deauth(struct wpa_supplicant *wpa_s,
+			      struct deauth_info *info)
+{
+	u16 reason_code = 0;
+	int locally_generated = 0;
+	const u8 *addr = NULL;
+	const u8 *ie = NULL;
+	size_t ie_len = 0;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Deauthentication notification");
+
+	if (info) {
+		addr = info->addr;
+		ie = info->ie;
+		ie_len = info->ie_len;
+		reason_code = info->reason_code;
+		locally_generated = info->locally_generated;
+		wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s",
+			reason_code,
+			locally_generated ? " (locally generated)" : "");
+		if (addr) {
+			wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
+				MAC2STR(addr));
+		}
+		wpa_hexdump(MSG_DEBUG, "Deauthentication frame IE(s)",
+			    ie, ie_len);
+	}
+
+	wpa_reset_ft_completed(wpa_s->wpa);
+
+	wpas_event_disconnect(wpa_s, addr, reason_code,
+			      locally_generated, ie, ie_len, 1);
+}
+
+
+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 */
+
+	dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+			 radio_list) {
+		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);
+	}
+
+	/* Restart sched_scan with updated channel list */
+	if (wpa_s->sched_scanning) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Channel list changed restart sched scan.");
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	}
+
+	wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER);
+}
+
+
+static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
+				      const u8 *frame, size_t len, int freq,
+				      int rssi)
+{
+	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);
+
+	if (category == WLAN_ACTION_WMM) {
+		wmm_ac_rx_action(wpa_s, mgmt->da, mgmt->sa, payload, plen);
+		return;
+	}
+
+#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 */
+
+	if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
+	    payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) {
+		wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1);
+		return;
+	}
+
+	if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
+	    payload[0] == WLAN_RRM_LINK_MEASUREMENT_REQUEST) {
+		wpas_rrm_handle_link_measurement_request(wpa_s, mgmt->sa,
+							 payload + 1, plen - 1,
+							 rssi);
+		return;
+	}
+
+#ifdef CONFIG_FST
+	if (mgmt->u.action.category == WLAN_ACTION_FST && wpa_s->fst) {
+		fst_rx_action(wpa_s->fst, mgmt, len);
+		return;
+	}
+#endif /* CONFIG_FST */
+
+	wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
+			   category, payload, plen, freq);
+	if (wpa_s->ifmsh)
+		mesh_mpm_action_rx(wpa_s, mgmt, len);
+}
+
+
+static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s,
+					     union wpa_event_data *event)
+{
+	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");
+
+		/*
+		 * The update channel flow will also take care of moving a GO
+		 * from the unsafe frequency if needed.
+		 */
+		wpas_p2p_update_channel_list(wpa_s,
+					     WPAS_P2P_CHANNEL_UPDATE_AVOID);
+	}
+#endif /* CONFIG_P2P */
+
+	os_free(str);
+}
+
+
+static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s,
+					    union wpa_event_data *data)
+{
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"Connection authorized by device, previous state %d",
+		wpa_s->wpa_state);
+	if (wpa_s->wpa_state == WPA_ASSOCIATED) {
+		wpa_supplicant_cancel_auth_timeout(wpa_s);
+		wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+		eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+		eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+	}
+	wpa_sm_set_rx_replay_ctr(wpa_s->wpa, data->assoc_info.key_replay_ctr);
+	wpa_sm_set_ptk_kck_kek(wpa_s->wpa, data->assoc_info.ptk_kck,
+			       data->assoc_info.ptk_kck_len,
+			       data->assoc_info.ptk_kek,
+			       data->assoc_info.ptk_kek_len);
+}
+
+
+void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+			  union wpa_event_data *data)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	int resched;
+
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
+	    event != EVENT_INTERFACE_ENABLED &&
+	    event != EVENT_INTERFACE_STATUS &&
+	    event != EVENT_SCHED_SCAN_STOPPED) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Ignore event %s (%d) while interface is disabled",
+			event_to_string(event), event);
+		return;
+	}
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+{
+	int level = MSG_DEBUG;
+
+	if (event == EVENT_RX_MGMT && data->rx_mgmt.frame_len >= 24) {
+		const struct ieee80211_hdr *hdr;
+		u16 fc;
+		hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame;
+		fc = le_to_host16(hdr->frame_control);
+		if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+		    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
+			level = MSG_EXCESSIVE;
+	}
+
+	wpa_dbg(wpa_s, level, "Event %s (%d) received",
+		event_to_string(event), event);
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+	switch (event) {
+	case EVENT_AUTH:
+		sme_event_auth(wpa_s, data);
+		break;
+	case EVENT_ASSOC:
+		wpa_supplicant_event_assoc(wpa_s, data);
+		if (data && data->assoc_info.authorized)
+			wpa_supplicant_event_assoc_auth(wpa_s, data);
+		break;
+	case EVENT_DISASSOC:
+		wpas_event_disassoc(wpa_s,
+				    data ? &data->disassoc_info : NULL);
+		break;
+	case EVENT_DEAUTH:
+		wpas_event_deauth(wpa_s,
+				  data ? &data->deauth_info : NULL);
+		break;
+	case EVENT_MICHAEL_MIC_FAILURE:
+		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_ctrl(wpa_s, MSG_INFO,
+					     WPA_EVENT_SCAN_STARTED "id=%u",
+					     wpa_s->manual_scan_id);
+			} else {
+				wpa_msg_ctrl(wpa_s, MSG_INFO,
+					     WPA_EVENT_SCAN_STARTED);
+			}
+		} else {
+			wpa_dbg(wpa_s, MSG_DEBUG, "External program started a scan");
+			wpa_s->radio->external_scan_running = 1;
+			wpa_msg_ctrl(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);
+		}
+		if (wpa_supplicant_event_scan_results(wpa_s, data))
+			break; /* interface may have been removed */
+		wpa_s->own_scan_running = 0;
+		wpa_s->radio->external_scan_running = 0;
+		radio_work_check_next(wpa_s);
+		break;
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+	case EVENT_ASSOCINFO:
+		wpa_supplicant_event_associnfo(wpa_s, data);
+		break;
+	case EVENT_INTERFACE_STATUS:
+		wpa_supplicant_event_interface_status(wpa_s, data);
+		break;
+	case EVENT_PMKID_CANDIDATE:
+		wpa_supplicant_event_pmkid_candidate(wpa_s, data);
+		break;
+#ifdef CONFIG_PEERKEY
+	case EVENT_STKSTART:
+		wpa_supplicant_event_stkstart(wpa_s, data);
+		break;
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_TDLS
+	case EVENT_TDLS:
+		wpa_supplicant_event_tdls(wpa_s, data);
+		break;
+#endif /* CONFIG_TDLS */
+#ifdef CONFIG_WNM
+	case EVENT_WNM:
+		wpa_supplicant_event_wnm(wpa_s, data);
+		break;
+#endif /* CONFIG_WNM */
+#ifdef CONFIG_IEEE80211R
+	case EVENT_FT_RESPONSE:
+		wpa_supplicant_event_ft_response(wpa_s, data);
+		break;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IBSS_RSN
+	case EVENT_IBSS_RSN_START:
+		wpa_supplicant_event_ibss_rsn_start(wpa_s, data);
+		break;
+#endif /* CONFIG_IBSS_RSN */
+	case EVENT_ASSOC_REJECT:
+		if (data->assoc_reject.bssid)
+			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
+				"bssid=" MACSTR	" status_code=%u",
+				MAC2STR(data->assoc_reject.bssid),
+				data->assoc_reject.status_code);
+		else
+			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
+				"status_code=%u",
+				data->assoc_reject.status_code);
+		wpa_s->assoc_status_code = data->assoc_reject.status_code;
+		wpas_notify_assoc_status_code(wpa_s);
+		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+			sme_event_assoc_reject(wpa_s, data);
+		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);
+		}
+		break;
+	case EVENT_AUTH_TIMED_OUT:
+		/* It is possible to get this event from earlier connection */
+		if (wpa_s->current_ssid &&
+		    wpa_s->current_ssid->mode == WPAS_MODE_MESH) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Ignore AUTH_TIMED_OUT in mesh configuration");
+			break;
+		}
+		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+			sme_event_auth_timed_out(wpa_s, data);
+		break;
+	case EVENT_ASSOC_TIMED_OUT:
+		/* It is possible to get this event from earlier connection */
+		if (wpa_s->current_ssid &&
+		    wpa_s->current_ssid->mode == WPAS_MODE_MESH) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Ignore ASSOC_TIMED_OUT in mesh configuration");
+			break;
+		}
+		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+			sme_event_assoc_timed_out(wpa_s, data);
+		break;
+	case EVENT_TX_STATUS:
+		wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS dst=" MACSTR
+			" type=%d stype=%d",
+			MAC2STR(data->tx_status.dst),
+			data->tx_status.type, data->tx_status.stype);
+#ifdef CONFIG_AP
+		if (wpa_s->ap_iface == NULL) {
+#ifdef CONFIG_OFFCHANNEL
+			if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+			    data->tx_status.stype == WLAN_FC_STYPE_ACTION)
+				offchannel_send_action_tx_status(
+					wpa_s, data->tx_status.dst,
+					data->tx_status.data,
+					data->tx_status.data_len,
+					data->tx_status.ack ?
+					OFFCHANNEL_SEND_ACTION_SUCCESS :
+					OFFCHANNEL_SEND_ACTION_NO_ACK);
+#endif /* CONFIG_OFFCHANNEL */
+			break;
+		}
+#endif /* CONFIG_AP */
+#ifdef CONFIG_OFFCHANNEL
+		wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst="
+			MACSTR, MAC2STR(wpa_s->parent->pending_action_dst));
+		/*
+		 * Catch TX status events for Action frames we sent via group
+		 * interface in GO mode.
+		 */
+		if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+		    data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
+		    os_memcmp(wpa_s->parent->pending_action_dst,
+			      data->tx_status.dst, ETH_ALEN) == 0) {
+			offchannel_send_action_tx_status(
+				wpa_s->parent, data->tx_status.dst,
+				data->tx_status.data,
+				data->tx_status.data_len,
+				data->tx_status.ack ?
+				OFFCHANNEL_SEND_ACTION_SUCCESS :
+				OFFCHANNEL_SEND_ACTION_NO_ACK);
+			break;
+		}
+#endif /* CONFIG_OFFCHANNEL */
+#ifdef CONFIG_AP
+		switch (data->tx_status.type) {
+		case WLAN_FC_TYPE_MGMT:
+			ap_mgmt_tx_cb(wpa_s, data->tx_status.data,
+				      data->tx_status.data_len,
+				      data->tx_status.stype,
+				      data->tx_status.ack);
+			break;
+		case WLAN_FC_TYPE_DATA:
+			ap_tx_status(wpa_s, data->tx_status.dst,
+				     data->tx_status.data,
+				     data->tx_status.data_len,
+				     data->tx_status.ack);
+			break;
+		}
+#endif /* CONFIG_AP */
+		break;
+#ifdef CONFIG_AP
+	case EVENT_EAPOL_TX_STATUS:
+		ap_eapol_tx_status(wpa_s, data->eapol_tx_status.dst,
+				   data->eapol_tx_status.data,
+				   data->eapol_tx_status.data_len,
+				   data->eapol_tx_status.ack);
+		break;
+	case EVENT_DRIVER_CLIENT_POLL_OK:
+		ap_client_poll_ok(wpa_s, data->client_poll.addr);
+		break;
+	case EVENT_RX_FROM_UNKNOWN:
+		if (wpa_s->ap_iface == NULL)
+			break;
+		ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr,
+				       data->rx_from_unknown.wds);
+		break;
+	case EVENT_CH_SWITCH:
+		if (!data)
+			break;
+		if (!wpa_s->ap_iface) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch "
+				"event in non-AP mode");
+			break;
+		}
+
+		wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
+				  data->ch_switch.ht_enabled,
+				  data->ch_switch.ch_offset,
+				  data->ch_switch.ch_width,
+				  data->ch_switch.cf1,
+				  data->ch_switch.cf2);
+		break;
+#ifdef NEED_AP_MLME
+	case EVENT_DFS_RADAR_DETECTED:
+		if (data)
+			wpas_event_dfs_radar_detected(wpa_s, &data->dfs_event);
+		break;
+	case EVENT_DFS_CAC_STARTED:
+		if (data)
+			wpas_event_dfs_cac_started(wpa_s, &data->dfs_event);
+		break;
+	case EVENT_DFS_CAC_FINISHED:
+		if (data)
+			wpas_event_dfs_cac_finished(wpa_s, &data->dfs_event);
+		break;
+	case EVENT_DFS_CAC_ABORTED:
+		if (data)
+			wpas_event_dfs_cac_aborted(wpa_s, &data->dfs_event);
+		break;
+	case EVENT_DFS_NOP_FINISHED:
+		if (data)
+			wpas_event_dfs_cac_nop_finished(wpa_s,
+							&data->dfs_event);
+		break;
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_AP */
+	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);
+		stype = WLAN_FC_GET_STYPE(fc);
+
+#ifdef CONFIG_AP
+		if (wpa_s->ap_iface == NULL) {
+#endif /* CONFIG_AP */
+#ifdef CONFIG_P2P
+			if (stype == WLAN_FC_STYPE_PROBE_REQ &&
+			    data->rx_mgmt.frame_len > 24) {
+				const u8 *src = mgmt->sa;
+				const u8 *ie = mgmt->u.probe_req.variable;
+				size_t ie_len = data->rx_mgmt.frame_len -
+					(mgmt->u.probe_req.variable -
+					 data->rx_mgmt.frame);
+				wpas_p2p_probe_req_rx(
+					wpa_s, src, mgmt->da,
+					mgmt->bssid, ie, ie_len,
+					data->rx_mgmt.freq,
+					data->rx_mgmt.ssi_signal);
+				break;
+			}
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_IBSS_RSN
+			if (wpa_s->current_ssid &&
+			    wpa_s->current_ssid->mode == WPAS_MODE_IBSS &&
+			    stype == WLAN_FC_STYPE_AUTH &&
+			    data->rx_mgmt.frame_len >= 30) {
+				wpa_supplicant_event_ibss_auth(wpa_s, data);
+				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,
+					data->rx_mgmt.ssi_signal);
+				break;
+			}
+
+			if (wpa_s->ifmsh) {
+				mesh_mpm_mgmt_rx(wpa_s, &data->rx_mgmt);
+				break;
+			}
+
+			wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received "
+				"management frame in non-AP mode");
+			break;
+#ifdef CONFIG_AP
+		}
+
+		if (stype == WLAN_FC_STYPE_PROBE_REQ &&
+		    data->rx_mgmt.frame_len > 24) {
+			const u8 *ie = mgmt->u.probe_req.variable;
+			size_t ie_len = data->rx_mgmt.frame_len -
+				(mgmt->u.probe_req.variable -
+				 data->rx_mgmt.frame);
+
+			wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da,
+					 mgmt->bssid, ie, ie_len,
+					 data->rx_mgmt.ssi_signal);
+		}
+
+		ap_mgmt_rx(wpa_s, &data->rx_mgmt);
+#endif /* CONFIG_AP */
+		break;
+		}
+	case EVENT_RX_PROBE_REQ:
+		if (data->rx_probe_req.sa == NULL ||
+		    data->rx_probe_req.ie == NULL)
+			break;
+#ifdef CONFIG_AP
+		if (wpa_s->ap_iface) {
+			hostapd_probe_req_rx(wpa_s->ap_iface->bss[0],
+					     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);
+			break;
+		}
+#endif /* CONFIG_AP */
+		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,
+				      0,
+				      data->rx_probe_req.ssi_signal);
+		break;
+	case EVENT_REMAIN_ON_CHANNEL:
+#ifdef CONFIG_OFFCHANNEL
+		offchannel_remain_on_channel_cb(
+			wpa_s, data->remain_on_channel.freq,
+			data->remain_on_channel.duration);
+#endif /* CONFIG_OFFCHANNEL */
+		wpas_p2p_remain_on_channel_cb(
+			wpa_s, data->remain_on_channel.freq,
+			data->remain_on_channel.duration);
+		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 */
+		wpas_p2p_cancel_remain_on_channel_cb(
+			wpa_s, data->remain_on_channel.freq);
+		break;
+	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);
+		wpa_bss_update_level(wpa_s->current_bss,
+				     data->signal_change.current_signal);
+		bgscan_notify_signal_change(
+			wpa_s, data->signal_change.above_threshold,
+			data->signal_change.current_signal,
+			data->signal_change.current_noise,
+			data->signal_change.current_txrate);
+		break;
+	case EVENT_INTERFACE_ENABLED:
+		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,
+							 WPA_DISCONNECTED);
+				wpa_s->scan_req = NORMAL_SCAN_REQ;
+				wpa_supplicant_req_scan(wpa_s, 0, 0);
+			} else
+				wpa_supplicant_set_state(wpa_s,
+							 WPA_COMPLETED);
+#else /* CONFIG_AP */
+			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+			wpa_supplicant_req_scan(wpa_s, 0, 0);
+#endif /* CONFIG_AP */
+		}
+		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)) {
+			/*
+			 * Mark interface disabled if this happens to end up not
+			 * being removed as a separate P2P group interface.
+			 */
+			wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
+			/*
+			 * The interface was externally disabled. Remove
+			 * it assuming an external entity will start a
+			 * new session if needed.
+			 */
+			if (wpa_s->current_ssid &&
+			    wpa_s->current_ssid->p2p_group)
+				wpas_p2p_interface_unavailable(wpa_s);
+			else
+				wpas_p2p_disconnect(wpa_s);
+			/*
+			 * wpa_s instance may have been freed, so must not use
+			 * it here anymore.
+			 */
+			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:
+		wpa_supplicant_update_channel_list(
+			wpa_s, &data->channel_list_changed);
+		break;
+	case EVENT_INTERFACE_UNAVAILABLE:
+		wpas_p2p_interface_unavailable(wpa_s);
+		break;
+	case EVENT_BEST_CHANNEL:
+		wpa_dbg(wpa_s, MSG_DEBUG, "Best channel event received "
+			"(%d %d %d)",
+			data->best_chan.freq_24, data->best_chan.freq_5,
+			data->best_chan.freq_overall);
+		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;
+		wpas_p2p_update_best_channels(wpa_s, data->best_chan.freq_24,
+					      data->best_chan.freq_5,
+					      data->best_chan.freq_overall);
+		break;
+	case EVENT_UNPROT_DEAUTH:
+		wpa_supplicant_event_unprot_deauth(wpa_s,
+						   &data->unprot_deauth);
+		break;
+	case EVENT_UNPROT_DISASSOC:
+		wpa_supplicant_event_unprot_disassoc(wpa_s,
+						     &data->unprot_disassoc);
+		break;
+	case EVENT_STATION_LOW_ACK:
+#ifdef CONFIG_AP
+		if (wpa_s->ap_iface && data)
+			hostapd_event_sta_low_ack(wpa_s->ap_iface->bss[0],
+						  data->low_ack.addr);
+#endif /* CONFIG_AP */
+#ifdef CONFIG_TDLS
+		if (data)
+			wpa_tdls_disable_unreachable_link(wpa_s->wpa,
+							  data->low_ack.addr);
+#endif /* CONFIG_TDLS */
+		break;
+	case EVENT_IBSS_PEER_LOST:
+#ifdef CONFIG_IBSS_RSN
+		ibss_rsn_stop(wpa_s->ibss_rsn, data->ibss_peer_lost.peer);
+#endif /* CONFIG_IBSS_RSN */
+		break;
+	case EVENT_DRIVER_GTK_REKEY:
+		if (os_memcmp(data->driver_gtk_rekey.bssid,
+			      wpa_s->bssid, ETH_ALEN))
+			break;
+		if (!wpa_s->wpa)
+			break;
+		wpa_sm_update_replay_ctr(wpa_s->wpa,
+					 data->driver_gtk_rekey.replay_ctr);
+		break;
+	case EVENT_SCHED_SCAN_STOPPED:
+		wpa_s->pno = 0;
+		wpa_s->sched_scanning = 0;
+		resched = wpa_s->scanning;
+		wpa_supplicant_notify_scanning(wpa_s, 0);
+
+		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+			break;
+
+		/*
+		 * 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) {
+			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);
+		} else if (resched) {
+			wpa_supplicant_req_scan(wpa_s, 0, 0);
+		}
+
+		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)
+			break;
+		hostapd_event_connect_failed_reason(
+			wpa_s->ap_iface->bss[0],
+			data->connect_failed_reason.addr,
+			data->connect_failed_reason.code);
+#endif /* CONFIG_AP */
+		break;
+	case EVENT_NEW_PEER_CANDIDATE:
+#ifdef CONFIG_MESH
+		if (!wpa_s->ifmsh || !data)
+			break;
+		wpa_mesh_notify_peer(wpa_s, data->mesh_peer.peer,
+				     data->mesh_peer.ies,
+				     data->mesh_peer.ie_len);
+#endif /* CONFIG_MESH */
+		break;
+	default:
+		wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
+		break;
+	}
+}
diff --git a/hostap/wpa_supplicant/examples/60_wpa_supplicant b/hostap/wpa_supplicant/examples/60_wpa_supplicant
new file mode 100755
index 0000000..39bd8e0
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/60_wpa_supplicant
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# /etc/pm/sleep.d/60_wpa_supplicant
+# Action script to notify wpa_supplicant of pm-action events.
+
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+
+WPACLI=wpa_cli
+
+case "$1" in
+	suspend|hibernate)
+		$WPACLI suspend
+		;;
+	resume|thaw)
+		$WPACLI resume
+		;;
+esac
+
+exit 0
diff --git a/hostap/wpa_supplicant/examples/dbus-listen-preq.py b/hostap/wpa_supplicant/examples/dbus-listen-preq.py
new file mode 100755
index 0000000..5ac9859
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/dbus-listen-preq.py
@@ -0,0 +1,62 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+import time
+import gobject
+from dbus.mainloop.glib import DBusGMainLoop
+
+WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_INTERFACE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_OPATH = "/fi/w1/wpa_supplicant1"
+WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface"
+
+def usage():
+	print "Usage: %s <ifname>" % sys.argv[0]
+	print "Press Ctrl-C to stop"
+
+def ProbeRequest(args):
+	if 'addr' in args:
+		print '%.2x:%.2x:%.2x:%.2x:%.2x:%.2x' % tuple(args['addr']),
+	if 'dst' in args:
+		print '-> %.2x:%.2x:%.2x:%.2x:%.2x:%.2x' % tuple(args['dst']),
+	if 'bssid' in args:
+		print '(bssid %.2x:%.2x:%.2x:%.2x:%.2x:%.2x)' % tuple(args['dst']),
+	if 'signal' in args:
+		print 'signal:%d' % args['signal'],
+	if 'ies' in args:
+		print 'have IEs (%d bytes)' % len(args['ies']),
+        print ''
+
+if __name__ == "__main__":
+	global bus
+	global wpas_obj
+	global if_obj
+	global p2p_iface
+
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+	wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH)
+
+	# Print list of i/f if no one is specified
+	if (len(sys.argv) < 2)  :
+		usage()
+		sys.exit(0)
+
+	wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE)
+
+	ifname = sys.argv[1]
+
+	path = wpas.GetInterface(ifname)
+
+	if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+	iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE)
+
+	bus.add_signal_receiver(ProbeRequest,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="ProbeRequest")
+
+	iface.SubscribeProbeReq()
+
+	gobject.MainLoop().run()
diff --git a/hostap/wpa_supplicant/examples/ieee8021x.conf b/hostap/wpa_supplicant/examples/ieee8021x.conf
new file mode 100644
index 0000000..e8a5503
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/ieee8021x.conf
@@ -0,0 +1,13 @@
+# IEEE 802.1X with dynamic WEP keys using EAP-PEAP/MSCHAPv2
+
+ctrl_interface=/var/run/wpa_supplicant
+
+network={
+	ssid="example 802.1x network"
+	key_mgmt=IEEE8021X
+	eap=PEAP
+	phase2="auth=MSCHAPV2"
+	identity="user name"
+	password="password"
+	ca_cert="/etc/cert/ca.pem"
+}
diff --git a/hostap/wpa_supplicant/examples/openCryptoki.conf b/hostap/wpa_supplicant/examples/openCryptoki.conf
new file mode 100644
index 0000000..e2301a6
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/openCryptoki.conf
@@ -0,0 +1,41 @@
+# EAP-TLS using private key and certificates via OpenSSL PKCS#11 engine and
+# openCryptoki (e.g., with TPM token)
+
+# This example uses following PKCS#11 objects:
+# $ pkcs11-tool --module /usr/lib/opencryptoki/libopencryptoki.so  -O -l
+# Please enter User PIN:
+# Private Key Object; RSA
+#   label:      rsakey
+#   ID:         04
+#   Usage:      decrypt, sign, unwrap
+# Certificate Object, type = X.509 cert
+#   label:      ca
+#   ID:         01
+# Certificate Object, type = X.509 cert
+#   label:      cert
+#   ID:         04
+
+# Configure OpenSSL to load the PKCS#11 engine and openCryptoki module
+pkcs11_engine_path=/usr/lib/engines/engine_pkcs11.so
+pkcs11_module_path=/usr/lib/opencryptoki/libopencryptoki.so
+
+network={
+	ssid="test network"
+	key_mgmt=WPA-EAP
+	eap=TLS
+	identity="User"
+
+	# use OpenSSL PKCS#11 engine for this network
+	engine=1
+	engine_id="pkcs11"
+
+	# select the private key and certificates based on ID (see pkcs11-tool
+	# output above)
+	key_id="4"
+	cert_id="4"
+	ca_cert_id="1"
+
+	# set the PIN code; leave this out to configure the PIN to be requested
+	# interactively when needed (e.g., via wpa_gui or wpa_cli)
+	pin="123456"
+}
diff --git a/hostap/wpa_supplicant/examples/p2p-action-udhcp.sh b/hostap/wpa_supplicant/examples/p2p-action-udhcp.sh
new file mode 100755
index 0000000..d7d0e79
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/p2p-action-udhcp.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+IFNAME=$1
+CMD=$2
+
+kill_daemon() {
+    NAME=$1
+    PF=$2
+
+    if [ ! -r $PF ]; then
+	return
+    fi
+
+    PID=`cat $PF`
+    if [ $PID -gt 0 ]; then
+	if ps $PID | grep -q $NAME; then
+	    kill $PID
+	fi
+    fi
+    rm $PF
+}
+
+if [ "$CMD" = "P2P-GROUP-STARTED" ]; then
+    GIFNAME=$3
+    if [ "$4" = "GO" ]; then
+	kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid
+	ifconfig $GIFNAME 192.168.42.1 up
+	udhcpd /etc/udhcpd-p2p.conf
+    fi
+    if [ "$4" = "client" ]; then
+	kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid
+	kill_daemon udhcpd /var/run/udhcpd-$GIFNAME.pid
+	udhcpc -i $GIFNAME -p /var/run/udhcpc-$GIFNAME.pid \
+		-s /etc/udhcpc.script
+    fi
+fi
+
+if [ "$CMD" = "P2P-GROUP-REMOVED" ]; then
+    GIFNAME=$3
+    if [ "$4" = "GO" ]; then
+	kill_daemon udhcpd /var/run/udhcpd-$GIFNAME.pid
+	ifconfig $GIFNAME 0.0.0.0
+    fi
+    if [ "$4" = "client" ]; then
+	kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid
+	ifconfig $GIFNAME 0.0.0.0
+    fi
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-ENABLE" ]; then
+    GIFNAME=$3
+    UPLINK=$4
+    # enable NAT/masquarade $GIFNAME -> $UPLINK
+    iptables -P FORWARD DROP
+    iptables -t nat -A POSTROUTING -o $UPLINK -j MASQUERADE
+    iptables -A FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+    iptables -A FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+    sysctl net.ipv4.ip_forward=1
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-DISABLE" ]; then
+    GIFNAME=$3
+    UPLINK=$4
+    # disable NAT/masquarade $GIFNAME -> $UPLINK
+    sysctl net.ipv4.ip_forward=0
+    iptables -t nat -D POSTROUTING -o $UPLINK -j MASQUERADE
+    iptables -D FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+    iptables -D FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+fi
diff --git a/hostap/wpa_supplicant/examples/p2p-action.sh b/hostap/wpa_supplicant/examples/p2p-action.sh
new file mode 100755
index 0000000..797d43a
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/p2p-action.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+IFNAME=$1
+CMD=$2
+
+kill_daemon() {
+    NAME=$1
+    PF=$2
+
+    if [ ! -r $PF ]; then
+	return
+    fi
+
+    PID=`cat $PF`
+    if [ $PID -gt 0 ]; then
+	if ps $PID | grep -q $NAME; then
+	    kill $PID
+	fi
+    fi
+    rm $PF
+}
+
+if [ "$CMD" = "P2P-GROUP-STARTED" ]; then
+    GIFNAME=$3
+    if [ "$4" = "GO" ]; then
+	kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
+	rm /var/run/dhclient.leases-$GIFNAME
+	kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+	ifconfig $GIFNAME 192.168.42.1 up
+	if ! dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \
+	    -i $GIFNAME \
+	    -F192.168.42.11,192.168.42.99; then
+	    # another dnsmasq instance may be running and blocking us; try to
+	    # 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 -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 \
+	    $GIFNAME
+    fi
+fi
+
+if [ "$CMD" = "P2P-GROUP-REMOVED" ]; then
+    GIFNAME=$3
+    if [ "$4" = "GO" ]; then
+	kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+	ifconfig $GIFNAME 0.0.0.0
+    fi
+    if [ "$4" = "client" ]; then
+	kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
+	rm /var/run/dhclient.leases-$GIFNAME
+	ifconfig $GIFNAME 0.0.0.0
+    fi
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-ENABLE" ]; then
+    GIFNAME=$3
+    UPLINK=$4
+    # enable NAT/masquarade $GIFNAME -> $UPLINK
+    iptables -P FORWARD DROP
+    iptables -t nat -A POSTROUTING -o $UPLINK -j MASQUERADE
+    iptables -A FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+    iptables -A FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+    sysctl net.ipv4.ip_forward=1
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-DISABLE" ]; then
+    GIFNAME=$3
+    UPLINK=$4
+    # disable NAT/masquarade $GIFNAME -> $UPLINK
+    sysctl net.ipv4.ip_forward=0
+    iptables -t nat -D POSTROUTING -o $UPLINK -j MASQUERADE
+    iptables -D FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+    iptables -D FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+fi
diff --git a/hostap/wpa_supplicant/examples/p2p-nfc.py b/hostap/wpa_supplicant/examples/p2p-nfc.py
new file mode 100755
index 0000000..91eba28
--- /dev/null
+++ b/hostap/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/hostap/wpa_supplicant/examples/p2p/p2p_connect.py b/hostap/wpa_supplicant/examples/p2p/p2p_connect.py
new file mode 100644
index 0000000..59b0a9d
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/p2p/p2p_connect.py
@@ -0,0 +1,299 @@
+#!/usr/bin/python
+# Tests p2p_connect
+# Will try to connect to another peer
+# and form a group
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> -m <wps_method> \ " \
+		% sys.argv[0]
+	print "		-a <addr> [-p <pin>] [-g <go_intent>] \ "
+	print "  		[-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -m = wps method"
+	print "  -a = peer address"
+	print "  -p = pin number (8 digits)"
+	print "  -g = group owner intent"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i wlan0 -a 0015008352c0 -m display -p 12345670" % sys.argv[0]
+
+
+# Required Signals
+def GONegotiationSuccess(status):
+	print "Go Negotiation Success"
+
+def GONegotiationFailure(status):
+	print 'Go Negotiation Failed. Status:'
+	print format(status)
+	os._exit(0)
+
+def GroupStarted(properties):
+	if properties.has_key("group_object"):
+		print 'Group Formation Complete %s' \
+			% properties["group_object"]
+	os._exit(0)
+
+def WpsFailure(status, etc):
+	print "WPS Authentication Failure".format(status)
+	print etc
+	os._exit(0)
+
+class P2P_Connect():
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global ifname
+	global wpas
+	global wpas_dbus_interface
+	global timeout
+	global path
+	global wps_method
+	global go_intent
+	global addr
+	global pin
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Dictionary of Arguements
+	global p2p_connect_arguements
+
+	# Constructor
+	def __init__(self,ifname,wpas_dbus_interface,addr,
+					pin,wps_method,go_intent):
+		# Initializes variables and threads
+		self.ifname = ifname
+		self.wpas_dbus_interface = wpas_dbus_interface
+		self.wps_method = wps_method
+		self.go_intent = go_intent
+		self.addr = addr
+		self.pin = pin
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = \
+			"/" + self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = \
+			self.wpas_dbus_opath + "/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+			self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+			self.wpas_dbus_interfaces_interface + ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(
+				self.wpas_object, self.wpas_dbus_interface)
+
+		# See if wpa_supplicant already knows about this interface
+		self.path = None
+		try:
+			self.path = self.wpas.GetInterface(ifname)
+		except:
+			if not str(exc).startswith(
+				self.wpas_dbus_interface + \
+				".InterfaceUnknown:"):
+				raise exc
+			try:
+				path = self.wpas.CreateInterface(
+					{'Ifname': ifname, 'Driver': 'test'})
+				time.sleep(1)
+
+			except dbus.DBusException, exc:
+				if not str(exc).startswith(
+					self.wpas_dbus_interface + \
+					".InterfaceExists:"):
+					raise exc
+
+		# Get Interface and objects
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface,self.path)
+		self.p2p_interface = dbus.Interface(
+				self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		# Add signals
+		self.bus.add_signal_receiver(GONegotiationSuccess,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="GONegotiationSuccess")
+		self.bus.add_signal_receiver(GONegotiationFailure,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="GONegotiationFailure")
+		self.bus.add_signal_receiver(GroupStarted,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="GroupStarted")
+		self.bus.add_signal_receiver(WpsFailure,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="WpsFailed")
+
+
+	#Constructing all the arguements needed to connect
+	def constructArguements(self):
+		# Adding required arguements
+		self.p2p_connect_arguements = {'wps_method':self.wps_method,
+			'peer':dbus.ObjectPath(self.path+'/Peers/'+self.addr)}
+
+		# Display requires a pin, and a go intent of 15
+		if (self.wps_method == 'display'):
+			if (self.pin != None):
+				self.p2p_connect_arguements.update({'pin':self.pin})
+			else:
+				print "Error:\n  Pin required for wps_method=display"
+				usage()
+				quit()
+
+			if (self.go_intent != None and int(self.go_intent) != 15):
+				print "go_intent overwritten to 15"
+
+			self.go_intent = '15'
+
+		# Keypad requires a pin, and a go intent of less than 15
+		elif (self.wps_method == 'keypad'):
+			if (self.pin != None):
+				self.p2p_connect_arguements.update({'pin':self.pin})
+			else:
+				print "Error:\n  Pin required for wps_method=keypad"
+				usage()
+				quit()
+
+			if (self.go_intent != None and int(self.go_intent) == 15):
+				error = "Error :\n Group Owner intent cannot be" + \
+					" 15 for wps_method=keypad"
+				print error
+				usage()
+				quit()
+
+		# Doesn't require pin
+		# for ./wpa_cli, p2p_connect [mac] [pin#], wps_method=keypad
+		elif (self.wps_method == 'pin'):
+			if (self.pin != None):
+				print "pin ignored"
+
+		# No pin is required for pbc so it is ignored
+		elif (self.wps_method == 'pbc'):
+			if (self.pin != None):
+				print "pin ignored"
+
+		else:
+			print "Error:\n  wps_method not supported or does not exist"
+			usage()
+			quit()
+
+		# Go_intent is optional for all arguements
+		if (self.go_intent != None):
+			self.p2p_connect_arguements.update(
+				{'go_intent':dbus.Int32(self.go_intent)})
+
+	# Running p2p_connect
+	def run(self):
+		try:
+			result_pin = self.p2p_interface.Connect(
+				self.p2p_connect_arguements)
+
+		except dbus.DBusException, exc:
+				raise exc
+
+		if (self.wps_method == 'pin' and \
+		not self.p2p_connect_arguements.has_key('pin') ):
+			print "Connect return with pin value of %d " % int(result_pin)
+		gobject.MainLoop().run()
+
+if __name__ == "__main__":
+
+	# Required
+	interface_name = None
+	wps_method = None
+	addr = None
+
+	# Conditionally optional
+	pin = None
+
+	# Optional
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+	go_intent = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"hi:m:a:p:g:w:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		# WPS Method
+		elif (key == "-m"):
+			wps_method = value
+		# Address
+		elif (key == "-a"):
+			addr = value
+		# Pin
+		elif (key == "-p"):
+			pin = value
+		# Group Owner Intent
+		elif (key == "-g"):
+			go_intent = value
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Required Arguements check
+	if (interface_name == None or wps_method == None or addr == None):
+		print "Error:\n  Required arguements not specified"
+		usage()
+		quit()
+
+	# Group Owner Intent Check
+	if (go_intent != None and (int(go_intent) > 15 or int(go_intent) < 0) ):
+		print "Error:\n  Group Owner Intent must be between 0 and 15 inclusive"
+		usage()
+		quit()
+
+	# Pin Check
+	if (pin != None and len(pin) != 8):
+		print "Error:\n  Pin is not 8 digits"
+		usage()
+		quit()
+
+	try:
+		p2p_connect_test = P2P_Connect(interface_name,wpas_dbus_interface,
+			addr,pin,wps_method,go_intent)
+
+	except:
+		print "Error:\n  Invalid Arguements"
+		usage()
+		quit()
+
+	p2p_connect_test.constructArguements()
+	p2p_connect_test.run()
+
+	os._exit(0)
diff --git a/hostap/wpa_supplicant/examples/p2p/p2p_disconnect.py b/hostap/wpa_supplicant/examples/p2p/p2p_disconnect.py
new file mode 100644
index 0000000..c3e39b3
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/p2p/p2p_disconnect.py
@@ -0,0 +1,169 @@
+#!/usr/bin/python
+# Tests P2P_Disconnect
+# Will perform disconnect on interface_name
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> \ " \
+		% sys.argv[0]
+	print "  		[-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i p2p-wlan0-0" % sys.argv[0]
+
+# Required Signals
+def GroupFinished(status, etc):
+	print "Disconnected"	
+	os._exit(0)
+
+class P2P_Disconnect (threading.Thread):
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global interface_name
+	global wpas
+	global wpas_dbus_interface
+	global path
+	global timeout
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Constructor
+	def __init__(self,interface_name,wpas_dbus_interface,timeout):
+		# Initializes variables and threads
+		self.interface_name = interface_name
+		self.wpas_dbus_interface = wpas_dbus_interface
+		self.timeout = timeout
+
+		# Initializes thread and daemon allows for ctrl-c kill
+		threading.Thread.__init__(self)
+		self.daemon = True
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = "/" + \
+				self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+				"/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+				self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+				self.wpas_dbus_interfaces_interface \
+				+ ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(self.wpas_object,
+				self.wpas_dbus_interface)
+
+		# Try to see if supplicant knows about interface
+		# If not, throw an exception
+		try:
+			self.path = self.wpas.GetInterface(
+					self.interface_name)
+		except dbus.DBusException, exc:
+			error = 'Error:\n  Interface ' + self.interface_name \
+				+ ' was not found'
+			print error
+			usage()
+			os._exit(0)
+
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface, self.path)
+		self.p2p_interface = dbus.Interface(self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		# Signals
+		self.bus.add_signal_receiver(GroupFinished,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="GroupFinished")
+
+	# Runs p2p_disconnect
+	def run(self):
+		# Allows other threads to keep working while MainLoop runs
+		# Required for timeout implementation
+		gobject.MainLoop().get_context().iteration(True)
+		gobject.threads_init()
+		self.p2p_interface.Disconnect()
+		gobject.MainLoop().run()
+
+
+if __name__ == "__main__":
+
+	timeout = 5
+	# Defaults for optional inputs
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+	# interface_name is required
+	interface_name = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"hi:w:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Interface name is required and was not given
+	if (interface_name == None):
+		print "Error:\n  interface_name is required"
+		usage()
+		quit()
+
+	# Constructor
+	try:
+		p2p_disconnect_test = P2P_Disconnect(interface_name,
+						wpas_dbus_interface,timeout)
+
+	except:
+		print "Error:\n  Invalid wpas_dbus_interface"
+		usage()
+		quit()
+
+	# Start P2P_Disconnect
+	p2p_disconnect_test.start()
+
+	try:
+		time.sleep(int(p2p_disconnect_test.timeout))
+
+	except:
+		pass
+
+	print "Disconnect timed out"
+	quit()
diff --git a/hostap/wpa_supplicant/examples/p2p/p2p_find.py b/hostap/wpa_supplicant/examples/p2p/p2p_find.py
new file mode 100644
index 0000000..973d46a
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/p2p/p2p_find.py
@@ -0,0 +1,192 @@
+#!/usr/bin/python
+# Tests p2p_find
+# Will list all devices found/lost within a time frame (timeout)
+# Then Program will exit
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> [-t <timeout>] \ " \
+		% sys.argv[0]
+	print "  		[-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -t = timeout = 0s (infinite)"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i wlan0 -t 10" % sys.argv[0]
+
+# Required Signals
+def deviceFound(devicepath):
+	print "Device found: %s" % (devicepath)
+
+def deviceLost(devicepath):
+	print "Device lost: %s" % (devicepath)
+
+class P2P_Find (threading.Thread):
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global interface_name
+	global wpas
+	global wpas_dbus_interface
+	global timeout
+	global path
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Constructor
+	def __init__(self,interface_name,wpas_dbus_interface,timeout):
+		# Initializes variables and threads
+		self.timeout = int(timeout)
+		self.interface_name = interface_name
+		self.wpas_dbus_interface = wpas_dbus_interface
+
+		# Initializes thread and daemon allows for ctrl-c kill
+		threading.Thread.__init__(self)
+		self.daemon = True
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = "/" + \
+				self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+				"/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+				self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+				self.wpas_dbus_interfaces_interface \
+				+ ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(self.wpas_object,
+				self.wpas_dbus_interface)
+
+		# Try to see if supplicant knows about interface
+		# If not, throw an exception
+		try:
+			self.path = self.wpas.GetInterface(
+					self.interface_name)
+		except dbus.DBusException, exc:
+			error = 'Error:\n  Interface ' + self.interface_name \
+				+ ' was not found'
+			print error
+			usage()
+			os._exit(0)
+
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface, self.path)
+		self.p2p_interface = dbus.Interface(self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		#Adds listeners for find and lost
+		self.bus.add_signal_receiver(deviceFound,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="DeviceFound")
+		self.bus.add_signal_receiver(deviceLost,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="DeviceLost")
+
+
+		# Sets up p2p_find
+		P2PFindDict = dbus.Dictionary(
+				{'Timeout':int(self.timeout)})
+		self.p2p_interface.Find(P2PFindDict)
+
+	# Run p2p_find
+	def run(self):
+		# Allows other threads to keep working while MainLoop runs
+		# Required for timeout implementation
+		gobject.MainLoop().get_context().iteration(True)
+		gobject.threads_init()
+		gobject.MainLoop().run()
+
+if __name__ == "__main__":
+
+	# Defaults for optional inputs
+	timeout = 0
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+	# interface_name is required
+	interface_name = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"hi:t:w:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		# Timeout
+		elif (key == "-t"):
+			if ( int(value) >= 0):
+				timeout = value
+			else:
+				print "Error:\n  Timeout cannot be negative"
+				usage()
+				quit()
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Interface name is required and was not given
+	if (interface_name == None):
+		print "Error:\n  interface_name is required"
+		usage()
+		quit()
+
+	# Constructor
+	try:
+		p2p_find_test = P2P_Find(interface_name, wpas_dbus_interface, timeout)
+
+	except:
+		print "Error:\n  Invalid wpas_dbus_interface"
+		usage()
+		quit()
+
+	# Start P2P_Find
+	p2p_find_test.start()
+
+	try:
+		# If timeout is 0, then run forever
+		if (timeout == 0):
+			while(True):
+				pass
+		# Else sleep for (timeout)
+		else:
+			time.sleep(p2p_find_test.timeout)
+
+	except:
+		pass
+
+	quit()
diff --git a/hostap/wpa_supplicant/examples/p2p/p2p_flush.py b/hostap/wpa_supplicant/examples/p2p/p2p_flush.py
new file mode 100644
index 0000000..ff8509d
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/p2p/p2p_flush.py
@@ -0,0 +1,168 @@
+#!/usr/bin/python
+# Tests P2P_Flush
+# Will flush the p2p interface
+# Then Program will exit
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> \ " \
+		% sys.argv[0]
+	print "  		[-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i wlan0" % sys.argv[0]
+
+# Required Signals\
+def deviceLost(devicepath):
+	print "Device lost: %s" % (devicepath)
+
+class P2P_Flush (threading.Thread):
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global interface_name
+	global wpas
+	global wpas_dbus_interface
+	global path
+	global timeout
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Constructor
+	def __init__(self,interface_name,wpas_dbus_interface,timeout):
+		# Initializes variables and threads
+		self.interface_name = interface_name
+		self.wpas_dbus_interface = wpas_dbus_interface
+		self.timeout = timeout
+
+		# Initializes thread and daemon allows for ctrl-c kill
+		threading.Thread.__init__(self)
+		self.daemon = True
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = "/" + \
+				self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+				"/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+				self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+				self.wpas_dbus_interfaces_interface \
+				+ ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(self.wpas_object,
+				self.wpas_dbus_interface)
+
+		# Try to see if supplicant knows about interface
+		# If not, throw an exception
+		try:
+			self.path = self.wpas.GetInterface(
+					self.interface_name)
+		except dbus.DBusException, exc:
+			error = 'Error:\n  Interface ' + self.interface_name \
+				+ ' was not found'
+			print error
+			usage()
+			os._exit(0)
+
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface, self.path)
+		self.p2p_interface = dbus.Interface(self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		# Signals
+		self.bus.add_signal_receiver(deviceLost,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="DeviceLost")
+
+	# Runs p2p_flush
+	def run(self):
+		# Allows other threads to keep working while MainLoop runs
+		# Required for timeout implementation
+		gobject.MainLoop().get_context().iteration(True)
+		gobject.threads_init()
+		self.p2p_interface.Flush()
+		gobject.MainLoop().run()
+
+
+if __name__ == "__main__":
+	# Needed to show which devices were lost
+	timeout = 5
+	# Defaults for optional inputs
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+	# interface_name is required
+	interface_name = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"hi:w:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Interface name is required and was not given
+	if (interface_name == None):
+		print "Error:\n  interface_name is required"
+		usage()
+		quit()
+
+	# Constructor
+	try:
+		p2p_flush_test = P2P_Flush(interface_name, wpas_dbus_interface,timeout)
+
+	except:
+		print "Error:\n  Invalid wpas_dbus_interface"
+		usage()
+		quit()
+
+	# Start P2P_Find
+	p2p_flush_test.start()
+
+	try:
+		time.sleep(int(p2p_flush_test.timeout))
+
+	except:
+		pass
+
+	print "p2p_flush complete"
+	quit()
diff --git a/hostap/wpa_supplicant/examples/p2p/p2p_group_add.py b/hostap/wpa_supplicant/examples/p2p/p2p_group_add.py
new file mode 100644
index 0000000..5c8fdaf
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/p2p/p2p_group_add.py
@@ -0,0 +1,222 @@
+#!/usr/bin/python
+# Tests p2p_group_add
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import getopt
+import threading
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> [-p <persistent>] \ " \
+		% sys.argv[0]
+	print "		[-f <frequency>] [-o <group_object_path>] \ "
+	print "  		[-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -p = persistant group = 0 (0=false, 1=true)"
+	print "  -f = frequency"
+	print "  -o = persistent group object path"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i wlan0" % sys.argv[0]
+
+# Required Signals
+def GroupStarted(properties):
+	if properties.has_key("group_object"):
+		print 'Group Formation Complete %s' \
+			% properties["group_object"]
+	os._exit(0)
+
+def WpsFailure(status, etc):
+	print "WPS Authentication Failure".format(status)
+	print etc
+	os._exit(0)
+
+class P2P_Group_Add (threading.Thread):
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global interface_name
+	global wpas
+	global wpas_dbus_interface
+	global path
+	global persistent
+	global frequency
+	global persistent_group_object
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Arguements
+	global P2PDictionary
+
+	# Constructor
+	def __init__(self,interface_name,wpas_dbus_interface,persistent,frequency,
+						persistent_group_object):
+		# Initializes variables and threads
+		self.interface_name = interface_name
+		self.wpas_dbus_interface = wpas_dbus_interface
+		self.persistent = persistent
+		self.frequency = frequency
+		self.persistent_group_object = persistent_group_object
+
+		# Initializes thread and daemon allows for ctrl-c kill
+		threading.Thread.__init__(self)
+		self.daemon = True
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = "/" + \
+				self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+				"/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+				self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+				self.wpas_dbus_interfaces_interface \
+				+ ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(self.wpas_object,
+				self.wpas_dbus_interface)
+
+		# Try to see if supplicant knows about interface
+		# If not, throw an exception
+		try:
+			self.path = self.wpas.GetInterface(
+					self.interface_name)
+		except dbus.DBusException, exc:
+			error = 'Error:\n  Interface ' + self.interface_name \
+				+ ' was not found'
+			print error
+			usage()
+			os._exit(0)
+
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface, self.path)
+		self.p2p_interface = dbus.Interface(self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		#Adds listeners
+		self.bus.add_signal_receiver(GroupStarted,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="GroupStarted")
+		self.bus.add_signal_receiver(WpsFailure,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="WpsFailed")
+
+		# Sets up p2p_group_add dictionary
+	def constructArguements(self):
+		self.P2PDictionary = {'persistent':self.persistent}
+
+		if (self.frequency != None):
+			if (int(self.frequency) > 0):
+				self.P2PDictionary.update({'frequency':int(self.frequency)})
+			else:
+				print "Error:\n  Frequency must be greater than 0"
+				usage()
+				os._exit(0)
+
+		if (self.persistent_group_object != None):
+			self.P2PDictionary.update({'persistent_group_object':
+						self.persistent_group_object})
+
+	# Run p2p_group_remove
+	def run(self):
+		try:
+			self.p2p_interface.GroupAdd(self.P2PDictionary)
+
+		except:
+			print "Error:\n  Could not preform group add"
+			usage()
+			os._exit(0)
+
+		# Allows other threads to keep working while MainLoop runs
+		# Required for timeout implementation
+		gobject.MainLoop().get_context().iteration(True)
+		gobject.threads_init()
+		gobject.MainLoop().run()
+
+
+if __name__ == "__main__":
+
+	# Defaults for optional inputs
+	# 0 = false, 1 = true
+	persistent = False
+	frequency = None
+	persistent_group_object = None
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+	# interface_name is required
+	interface_name = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"hi:p:f:o:w:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		# Timeout
+		elif (key == "-p"):
+			if (value == '0'):
+				persistent = False
+			elif (value == '1'):
+				persistent = True
+			else:
+				print "Error:\n  Persistent can only be 1 or 0"
+				usage()
+				os._exit(0)
+		# Frequency
+		elif (key == "-f"):
+			frequency = value
+		# Persistent group object path
+		elif (key == "-o"):
+			persistent_group_object = value
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Interface name is required and was not given
+	if (interface_name == None):
+		print "Error:\n  interface_name is required"
+		usage()
+		quit()
+
+	try:
+		p2p_group_add_test = P2P_Group_Add(interface_name,wpas_dbus_interface,
+					persistent,frequency,persistent_group_object)
+	except:
+		print "Error:\n  Invalid Arguements"
+
+	p2p_group_add_test.constructArguements()
+	p2p_group_add_test.start()
+	time.sleep(5)
+	print "Error:\n  Group formation timed out"
+	os._exit(0)
diff --git a/hostap/wpa_supplicant/examples/p2p/p2p_invite.py b/hostap/wpa_supplicant/examples/p2p/p2p_invite.py
new file mode 100644
index 0000000..6deb397
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/p2p/p2p_invite.py
@@ -0,0 +1,201 @@
+#!/usr/bin/python
+# Tests p2p_invite
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import getopt
+import threading
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> -a <addr> \ " \
+		% sys.argv[0]
+	print "		[-o <persistent_group_object>] [-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -a = address of peer"
+	print "  -o = persistent group object path"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i p2p-wlan0-0 -a 00150083523c" % sys.argv[0]
+
+# Required Signals
+def InvitationResult(invite_result):
+	print "Inviation Result signal :"
+	status = invite_result['status']
+	print "status = ", status
+	if invite_result.has_key('BSSID'):
+		bssid = invite_result['BSSID']
+		print "BSSID = ", hex(bssid[0]) , ":" , \
+		 hex(bssid[1]) , ":" , hex(bssid[2]) , ":", \
+		 hex(bssid[3]) , ":" , hex(bssid[4]) , ":" , \
+		hex(bssid[5])
+	os._exit(0)
+
+class P2P_Invite (threading.Thread):
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global interface_name
+	global wpas
+	global wpas_dbus_interface
+	global path
+	global addr
+	global persistent_group_object
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Arguements
+	global P2PDictionary
+
+	# Constructor
+	def __init__(self,interface_name,wpas_dbus_interface,addr,
+						persistent_group_object):
+		# Initializes variables and threads
+		self.interface_name = interface_name
+		self.wpas_dbus_interface = wpas_dbus_interface
+		self.addr = addr
+		self.persistent_group_object = persistent_group_object
+
+		# Initializes thread and daemon allows for ctrl-c kill
+		threading.Thread.__init__(self)
+		self.daemon = True
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = "/" + \
+				self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+				"/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+				self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+				self.wpas_dbus_interfaces_interface \
+				+ ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(self.wpas_object,
+				self.wpas_dbus_interface)
+
+		# Try to see if supplicant knows about interface
+		# If not, throw an exception
+		try:
+			self.path = self.wpas.GetInterface(
+					self.interface_name)
+		except dbus.DBusException, exc:
+			error = 'Error:\n  Interface ' + self.interface_name \
+				+ ' was not found'
+			print error
+			usage()
+			os._exit(0)
+
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface, self.path)
+		self.p2p_interface = dbus.Interface(self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		#Adds listeners
+		self.bus.add_signal_receiver(InvitationResult,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="InvitationResult")
+
+	# Sets up p2p_invite dictionary
+	def constructArguements(self):
+		self.P2PDictionary = \
+			{'peer':dbus.ObjectPath(self.path+'/Peers/'+self.addr)}
+		if (self.persistent_group_object != None):
+			self.P2PDictionary.update({"persistent_group_object":
+					self.persistent_group_object})
+
+	# Run p2p_invite
+	def run(self):
+		try:
+			self.p2p_interface.Invite(self.P2PDictionary)
+
+		except:
+			print "Error:\n  Invalid Arguements"
+			usage()
+			os._exit(0)
+
+		# Allows other threads to keep working while MainLoop runs
+		# Required for timeout implementation
+		gobject.MainLoop().get_context().iteration(True)
+		gobject.threads_init()
+		gobject.MainLoop().run()
+
+if __name__ == "__main__":
+	# Defaults for optional inputs
+	addr = None
+	persistent_group_object = None
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+	# interface_name is required
+	interface_name = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"hi:o:w:a:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		elif (key == "-a"):
+			addr = value
+		# Persistent group object path
+		elif (key == "-o"):
+			persistent_group_object = value
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Interface name is required and was not given
+	if (interface_name == None):
+		print "Error:\n  interface_name is required"
+		usage()
+		quit()
+
+	if (addr == None):
+		print "Error:\n  peer address is required"
+		usage()
+		quit()
+
+	try:
+		p2p_invite_test = \
+			P2P_Invite(interface_name,wpas_dbus_interface,
+					addr,persistent_group_object)
+	except:
+		print "Error:\n  Invalid Arguements"
+		usage()
+		os._exit(1)
+
+	p2p_invite_test.constructArguements()
+	p2p_invite_test.start()
+	time.sleep(10)
+	print "Error:\n  p2p_invite timed out"
+	os._exit(0)
diff --git a/hostap/wpa_supplicant/examples/p2p/p2p_listen.py b/hostap/wpa_supplicant/examples/p2p/p2p_listen.py
new file mode 100644
index 0000000..bb3c1e4
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/p2p/p2p_listen.py
@@ -0,0 +1,182 @@
+#!/usr/bin/python
+# Tests P2P_Find
+# Will listen
+# Then Program will exit
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> [-t <timeout>] \ " \
+		% sys.argv[0]
+	print "  		[-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -t = timeout = 0s (infinite)"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i wlan0 -t 5" % sys.argv[0]
+
+# Required Signals
+def p2pStateChange(status):
+	print status
+
+class P2P_Listen(threading.Thread):
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global interface_name
+	global wpas
+	global wpas_dbus_interface
+	global path
+	global timeout
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Constructor
+	def __init__(self,interface_name,wpas_dbus_interface,timeout):
+		# Initializes variables and threads
+		self.timeout = int(timeout)
+		self.interface_name = interface_name
+		self.wpas_dbus_interface = wpas_dbus_interface
+
+		# Initializes thread and daemon allows for ctrl-c kill
+		threading.Thread.__init__(self)
+		self.daemon = True
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = "/" + \
+				self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+				"/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+				self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+				self.wpas_dbus_interfaces_interface \
+				+ ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(self.wpas_object,
+				self.wpas_dbus_interface)
+
+		# Try to see if supplicant knows about interface
+		# If not, throw an exception
+		try:
+			self.path = self.wpas.GetInterface(
+					self.interface_name)
+		except dbus.DBusException, exc:
+			error = 'Error:\n  Interface ' + self.interface_name \
+				+ ' was not found'
+			print error
+			usage()
+			os._exit(0)
+
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface, self.path)
+		self.p2p_interface = dbus.Interface(self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		self.bus.add_signal_receiver(p2pStateChange,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="P2PStateChanged")
+
+	# Run p2p_find
+	def run(self):
+		# Sets up p2p_listen
+		self.p2p_interface.Listen(int(self.timeout))
+
+		# Allows other threads to keep working while MainLoop runs
+		# Required for timeout implementation
+		gobject.MainLoop().get_context().iteration(True)
+		gobject.threads_init()
+		gobject.MainLoop().run()
+
+if __name__ == "__main__":
+
+	# Defaults for optional inputs
+	timeout = 0
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+	# interface_name is required
+	interface_name = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"hi:t:w:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		# Timeout
+		elif (key == "-t"):
+			if ( int(value) >= 0):
+				timeout = value
+			else:
+				print "Error:\n  Timeout cannot be negative"
+				usage()
+				quit()
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Interface name is required and was not given
+	if (interface_name == None):
+		print "Error:\n  interface_name is required"
+		usage()
+		quit()
+
+	# Constructor
+	try:
+		p2p_listen_test = P2P_Listen(interface_name, wpas_dbus_interface, timeout)
+
+	except:
+		print "Error:\n  Invalid wpas_dbus_interface"
+		usage()
+		quit()
+
+	# Start P2P_Find
+	p2p_listen_test.start()
+
+	try:
+		# If timeout is 0, then run forever
+		if (int(p2p_listen_test.timeout) == 0):
+			while(True):
+				pass
+		# Else sleep for (timeout)
+		else:
+			time.sleep(int(p2p_listen_test.timeout))
+
+	except:
+		pass
+
+	quit()
diff --git a/hostap/wpa_supplicant/examples/p2p/p2p_stop_find.py b/hostap/wpa_supplicant/examples/p2p/p2p_stop_find.py
new file mode 100644
index 0000000..f6c03b0
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/p2p/p2p_stop_find.py
@@ -0,0 +1,174 @@
+#!/usr/bin/python
+# Tests p2p_stop_find
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> \ " \
+		% sys.argv[0]
+	print "  		[-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i wlan0" % sys.argv[0]
+
+# Required Signals
+def deviceLost(devicepath):
+	print "Device lost: %s" % (devicepath)
+
+def p2pStateChange(status):
+	print status
+	os._exit(0)
+
+class P2P_Stop_Find (threading.Thread):
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global interface_name
+	global wpas
+	global wpas_dbus_interface
+	global path
+	global timeout
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Constructor
+	def __init__(self,interface_name,wpas_dbus_interface,timeout):
+		# Initializes variables and threads
+		self.interface_name = interface_name
+		self.wpas_dbus_interface = wpas_dbus_interface
+		self.timeout = timeout
+
+		# Initializes thread and daemon allows for ctrl-c kill
+		threading.Thread.__init__(self)
+		self.daemon = True
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = "/" + \
+				self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+				"/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+				self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+				self.wpas_dbus_interfaces_interface \
+				+ ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(self.wpas_object,
+				self.wpas_dbus_interface)
+
+		# Try to see if supplicant knows about interface
+		# If not, throw an exception
+		try:
+			self.path = self.wpas.GetInterface(
+					self.interface_name)
+		except dbus.DBusException, exc:
+			error = 'Error:\n  Interface ' + self.interface_name \
+				+ ' was not found'
+			print error
+			usage()
+			os._exit(0)
+
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface, self.path)
+		self.p2p_interface = dbus.Interface(self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		# Signals
+		self.bus.add_signal_receiver(deviceLost,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="DeviceLost")
+		self.bus.add_signal_receiver(p2pStateChange,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="P2PStateChanged")
+
+	# Runs p2p_stop_find
+	def run(self):
+		# Allows other threads to keep working while MainLoop runs
+		# Required for timeout implementation
+		gobject.MainLoop().get_context().iteration(True)
+		gobject.threads_init()
+		self.p2p_interface.StopFind()
+		gobject.MainLoop().run()
+
+
+if __name__ == "__main__":
+	# Needed because P2PStateChanged signal is not caught
+	timeout = 5
+	# Defaults for optional inputs
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+	# interface_name is required
+	interface_name = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"ht:i:w:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Interface name is required and was not given
+	if (interface_name == None):
+		print "Error:\n  interface_name is required"
+		usage()
+		quit()
+
+	# Constructor
+	try:
+		p2p_stop_find_test = P2P_Stop_Find(interface_name,
+						wpas_dbus_interface,timeout)
+
+	except:
+		print "Error:\n  Invalid wpas_dbus_interface"
+		usage()
+		quit()
+
+	# Start P2P_Find
+	p2p_stop_find_test.start()
+
+	try:
+		time.sleep(int(p2p_stop_find_test.timeout))
+
+	except:
+		pass
+
+	print "p2p find stopped"
+	quit()
diff --git a/hostap/wpa_supplicant/examples/plaintext.conf b/hostap/wpa_supplicant/examples/plaintext.conf
new file mode 100644
index 0000000..542ac1d
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/plaintext.conf
@@ -0,0 +1,8 @@
+# Plaintext (no encryption) network
+
+ctrl_interface=/var/run/wpa_supplicant
+
+network={
+	ssid="example open network"
+	key_mgmt=NONE
+}
diff --git a/hostap/wpa_supplicant/examples/udhcpd-p2p.conf b/hostap/wpa_supplicant/examples/udhcpd-p2p.conf
new file mode 100644
index 0000000..df59094
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/udhcpd-p2p.conf
@@ -0,0 +1,120 @@
+# Sample udhcpd configuration file (/etc/udhcpd.conf)
+
+# The start and end of the IP lease block
+
+start 		192.168.42.20	#default: 192.168.0.20
+end		192.168.42.254	#default: 192.168.0.254
+
+
+# The interface that udhcpd will use
+
+interface	wlan2		#default: eth0
+
+
+# The maximim number of leases (includes addressesd reserved
+# by OFFER's, DECLINE's, and ARP conficts
+
+#max_leases	254		#default: 254
+
+
+# If remaining is true (default), udhcpd will store the time
+# remaining for each lease in the udhcpd leases file. This is
+# for embedded systems that cannot keep time between reboots.
+# If you set remaining to no, the absolute time that the lease
+# expires at will be stored in the dhcpd.leases file.
+
+#remaining	yes		#default: yes
+
+
+# The time period at which udhcpd will write out a dhcpd.leases
+# file. If this is 0, udhcpd will never automatically write a
+# lease file. (specified in seconds)
+
+#auto_time	7200		#default: 7200 (2 hours)
+
+
+# The amount of time that an IP will be reserved (leased) for if a
+# DHCP decline message is received (seconds).
+
+#decline_time	3600		#default: 3600 (1 hour)
+
+
+# The amount of time that an IP will be reserved (leased) for if an
+# ARP conflct occurs. (seconds
+
+#conflict_time	3600		#default: 3600 (1 hour)
+
+
+# How long an offered address is reserved (leased) in seconds
+
+#offer_time	60		#default: 60 (1 minute)
+
+# If a lease to be given is below this value, the full lease time is
+# instead used (seconds).
+
+#min_lease	60		#defult: 60
+
+
+# The location of the leases file
+
+#lease_file	/var/lib/misc/udhcpd.leases	#defualt: /var/lib/misc/udhcpd.leases
+
+# The location of the pid file
+pidfile	/var/run/udhcpd-wlan2.pid	#default: /var/run/udhcpd.pid
+
+# Every time udhcpd writes a leases file, the below script will be called.
+# Useful for writing the lease file to flash every few hours.
+
+#notify_file				#default: (no script)
+
+#notify_file	dumpleases 	# <--- useful for debugging
+
+# The following are bootp specific options, setable by udhcpd.
+
+#siaddr		192.168.0.22		#default: 0.0.0.0
+
+#sname		zorak			#default: (none)
+
+#boot_file	/var/nfs_root		#default: (none)
+
+# The remainer of options are DHCP options and can be specifed with the
+# keyword 'opt' or 'option'. If an option can take multiple items, such
+# as the dns option, they can be listed on the same line, or multiple
+# lines. The only option with a default is 'lease'.
+
+#Examles
+opt	dns	192.168.2.1
+option	subnet	255.255.255.0
+option	domain	atherosowl.com
+option	lease	864000		# 10 days of seconds
+
+
+# Currently supported options, for more info, see options.c
+#opt subnet
+#opt timezone
+#opt router
+#opt timesvr
+#opt namesvr
+#opt dns
+#opt logsvr
+#opt cookiesvr
+#opt lprsvr
+#opt bootsize
+#opt domain
+#opt swapsvr
+#opt rootpath
+#opt ipttl
+#opt mtu
+#opt broadcast
+#opt wins
+#opt lease
+#opt ntpsrv
+#opt tftp
+#opt bootfile
+
+
+# Static leases map
+#static_lease 00:60:08:11:CE:4E 192.168.0.54
+#static_lease 00:60:08:11:CE:3E 192.168.0.44
+
+
diff --git a/hostap/wpa_supplicant/examples/wep.conf b/hostap/wpa_supplicant/examples/wep.conf
new file mode 100644
index 0000000..9c7b55f
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/wep.conf
@@ -0,0 +1,11 @@
+# Static WEP keys
+
+ctrl_interface=/var/run/wpa_supplicant
+
+network={
+	ssid="example wep network"
+	key_mgmt=NONE
+	wep_key0="abcde"
+	wep_key1=0102030405
+	wep_tx_keyidx=0
+}
diff --git a/hostap/wpa_supplicant/examples/wpa-psk-tkip.conf b/hostap/wpa_supplicant/examples/wpa-psk-tkip.conf
new file mode 100644
index 0000000..93d7fc2
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/wpa-psk-tkip.conf
@@ -0,0 +1,12 @@
+# WPA-PSK/TKIP
+
+ctrl_interface=/var/run/wpa_supplicant
+
+network={
+	ssid="example wpa-psk network"
+	key_mgmt=WPA-PSK
+	proto=WPA
+	pairwise=TKIP
+	group=TKIP
+	psk="secret passphrase"
+}
diff --git a/hostap/wpa_supplicant/examples/wpa2-eap-ccmp.conf b/hostap/wpa_supplicant/examples/wpa2-eap-ccmp.conf
new file mode 100644
index 0000000..d7a64d8
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/wpa2-eap-ccmp.conf
@@ -0,0 +1,15 @@
+# WPA2-EAP/CCMP using EAP-TLS
+
+ctrl_interface=/var/run/wpa_supplicant
+
+network={
+	ssid="example wpa2-eap network"
+	key_mgmt=WPA-EAP
+	proto=WPA2
+	pairwise=CCMP
+	group=CCMP
+	eap=TLS
+	ca_cert="/etc/cert/ca.pem"
+	private_key="/etc/cert/user.p12"
+	private_key_passwd="PKCS#12 passhrase"
+}
diff --git a/hostap/wpa_supplicant/examples/wpas-dbus-new-getall.py b/hostap/wpa_supplicant/examples/wpas-dbus-new-getall.py
new file mode 100755
index 0000000..03da187
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/wpas-dbus-new-getall.py
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+
+import dbus
+import sys, os
+import time
+import gobject
+
+def main():
+	bus = dbus.SystemBus()
+	wpas_obj = bus.get_object("fi.w1.wpa_supplicant1",
+				  "/fi/w1/wpa_supplicant1")
+	props = wpas_obj.GetAll("fi.w1.wpa_supplicant1",
+				dbus_interface=dbus.PROPERTIES_IFACE)
+	print "GetAll(fi.w1.wpa_supplicant1, /fi/w1/wpa_supplicant1):"
+	print props
+
+	if len(sys.argv) != 2:
+		os._exit(1)
+
+	ifname = sys.argv[1]
+
+	wpas = dbus.Interface(wpas_obj, "fi.w1.wpa_supplicant1")
+	path = wpas.GetInterface(ifname)
+	if_obj = bus.get_object("fi.w1.wpa_supplicant1", path)
+	props = if_obj.GetAll("fi.w1.wpa_supplicant1.Interface",
+			      dbus_interface=dbus.PROPERTIES_IFACE)
+	print
+	print "GetAll(fi.w1.wpa_supplicant1.Interface, %s):" % (path)
+	print props
+
+	props = if_obj.GetAll("fi.w1.wpa_supplicant1.Interface.WPS",
+			      dbus_interface=dbus.PROPERTIES_IFACE)
+	print
+	print "GetAll(fi.w1.wpa_supplicant1.Interface.WPS, %s):" % (path)
+	print props
+
+	res = if_obj.Get("fi.w1.wpa_supplicant1.Interface", 'BSSs',
+			 dbus_interface=dbus.PROPERTIES_IFACE)
+	if len(res) > 0:
+		bss_obj = bus.get_object("fi.w1.wpa_supplicant1", res[0])
+		props = bss_obj.GetAll("fi.w1.wpa_supplicant1.BSS",
+				       dbus_interface=dbus.PROPERTIES_IFACE)
+		print
+		print "GetAll(fi.w1.wpa_supplicant1.BSS, %s):" % (res[0])
+		print props
+
+	res = if_obj.Get("fi.w1.wpa_supplicant1.Interface", 'Networks',
+			 dbus_interface=dbus.PROPERTIES_IFACE)
+	if len(res) > 0:
+		net_obj = bus.get_object("fi.w1.wpa_supplicant1", res[0])
+		props = net_obj.GetAll("fi.w1.wpa_supplicant1.Network",
+				       dbus_interface=dbus.PROPERTIES_IFACE)
+		print
+		print "GetAll(fi.w1.wpa_supplicant1.Network, %s):" % (res[0])
+		print props
+
+if __name__ == "__main__":
+	main()
+
diff --git a/hostap/wpa_supplicant/examples/wpas-dbus-new-signals.py b/hostap/wpa_supplicant/examples/wpas-dbus-new-signals.py
new file mode 100755
index 0000000..d90ef18
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/wpas-dbus-new-signals.py
@@ -0,0 +1,203 @@
+#!/usr/bin/python
+
+import dbus
+import sys, os
+import time
+import gobject
+from dbus.mainloop.glib import DBusGMainLoop
+
+WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_INTERFACE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_OPATH = "/fi/w1/wpa_supplicant1"
+
+WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface"
+WPAS_DBUS_INTERFACES_OPATH = "/fi/w1/wpa_supplicant1/Interfaces"
+WPAS_DBUS_BSS_INTERFACE = "fi.w1.wpa_supplicant1.BSS"
+WPAS_DBUS_NETWORK_INTERFACE = "fi.w1.wpa_supplicant1.Network"
+
+def byte_array_to_string(s):
+	import urllib
+	r = ""    
+	for c in s:
+		if c >= 32 and c < 127:
+			r += "%c" % c
+		else:
+			r += urllib.quote(chr(c))
+	return r
+
+def list_interfaces(wpas_obj):
+	ifaces = wpas_obj.Get(WPAS_DBUS_INTERFACE, 'Interfaces',
+			      dbus_interface=dbus.PROPERTIES_IFACE)
+	for path in ifaces:
+		if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+		ifname = if_obj.Get(WPAS_DBUS_INTERFACES_INTERFACE, 'Ifname',
+			      dbus_interface=dbus.PROPERTIES_IFACE)
+		print ifname
+
+def interfaceAdded(interface, properties):
+	print "InterfaceAdded(%s): Ifname=%s" % (interface, properties['Ifname'])
+
+def interfaceRemoved(interface):
+	print "InterfaceRemoved(%s)" % (interface)
+
+def propertiesChanged(properties):
+	for i in properties:
+		print "PropertiesChanged: %s=%s" % (i, properties[i])
+
+def showBss(bss):
+	net_obj = bus.get_object(WPAS_DBUS_SERVICE, bss)
+	net = dbus.Interface(net_obj, WPAS_DBUS_BSS_INTERFACE)
+
+	# Convert the byte-array for SSID and BSSID to printable strings
+	val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'BSSID',
+			  dbus_interface=dbus.PROPERTIES_IFACE)
+	bssid = ""
+	for item in val:
+		bssid = bssid + ":%02x" % item
+	bssid = bssid[1:]
+	val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'SSID',
+			  dbus_interface=dbus.PROPERTIES_IFACE)
+	ssid = byte_array_to_string(val)
+
+	val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'WPA',
+			  dbus_interface=dbus.PROPERTIES_IFACE)
+	wpa = "no"
+	if val != None:
+		wpa = "yes"
+	val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'RSN',
+			  dbus_interface=dbus.PROPERTIES_IFACE)
+	wpa2 = "no"
+	if val != None:
+		wpa2 = "yes"
+	freq = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Frequency',
+			   dbus_interface=dbus.PROPERTIES_IFACE)
+	signal = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Signal',
+			     dbus_interface=dbus.PROPERTIES_IFACE)
+	val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Rates',
+			  dbus_interface=dbus.PROPERTIES_IFACE)
+	if len(val) > 0:
+		maxrate = val[0] / 1000000
+	else:
+		maxrate = 0
+
+	print "  %s  ::  ssid='%s'  wpa=%s  wpa2=%s  signal=%d  rate=%d  freq=%d" % (bssid, ssid, wpa, wpa2, signal, maxrate, freq)
+
+def scanDone(success):
+	gobject.MainLoop().quit()
+	print "Scan done: success=%s" % success
+
+def scanDone2(success, path=None):
+	print "Scan done: success=%s [path=%s]" % (success, path)
+
+def bssAdded(bss, properties):
+	print "BSS added: %s" % (bss)
+	showBss(bss)
+
+def bssRemoved(bss):
+	print "BSS removed: %s" % (bss)
+
+def blobAdded(blob):
+	print "BlobAdded(%s)" % (blob)
+
+def blobRemoved(blob):
+	print "BlobRemoved(%s)" % (blob)
+
+def networkAdded(network, properties):
+	print "NetworkAdded(%s)" % (network)
+
+def networkRemoved(network):
+	print "NetworkRemoved(%s)" % (network)
+
+def networkSelected(network):
+	print "NetworkSelected(%s)" % (network)
+
+def propertiesChangedInterface(properties):
+	for i in properties:
+		print "PropertiesChanged(interface): %s=%s" % (i, properties[i])
+
+def propertiesChangedBss(properties):
+	for i in properties:
+		print "PropertiesChanged(BSS): %s=%s" % (i, properties[i])
+
+def propertiesChangedNetwork(properties):
+	for i in properties:
+		print "PropertiesChanged(Network): %s=%s" % (i, properties[i])
+
+def main():
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+	global bus
+	bus = dbus.SystemBus()
+	wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH)
+
+	if len(sys.argv) != 2:
+		list_interfaces(wpas_obj)
+		os._exit(1)
+
+	wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE)
+	bus.add_signal_receiver(interfaceAdded,
+				dbus_interface=WPAS_DBUS_INTERFACE,
+				signal_name="InterfaceAdded")
+	bus.add_signal_receiver(interfaceRemoved,
+				dbus_interface=WPAS_DBUS_INTERFACE,
+				signal_name="InterfaceRemoved")
+	bus.add_signal_receiver(propertiesChanged,
+				dbus_interface=WPAS_DBUS_INTERFACE,
+				signal_name="PropertiesChanged")
+
+	ifname = sys.argv[1]
+	path = wpas.GetInterface(ifname)
+	if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+	iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE)
+	iface.connect_to_signal("ScanDone", scanDone2,
+				path_keyword='path')
+
+	bus.add_signal_receiver(scanDone,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="ScanDone",
+				path=path)
+	bus.add_signal_receiver(bssAdded,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="BSSAdded",
+				path=path)
+	bus.add_signal_receiver(bssRemoved,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="BSSRemoved",
+				path=path)
+	bus.add_signal_receiver(blobAdded,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="BlobAdded",
+				path=path)
+	bus.add_signal_receiver(blobRemoved,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="BlobRemoved",
+				path=path)
+	bus.add_signal_receiver(networkAdded,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="NetworkAdded",
+				path=path)
+	bus.add_signal_receiver(networkRemoved,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="NetworkRemoved",
+				path=path)
+	bus.add_signal_receiver(networkSelected,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="NetworkSelected",
+				path=path)
+	bus.add_signal_receiver(propertiesChangedInterface,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="PropertiesChanged",
+				path=path)
+
+	bus.add_signal_receiver(propertiesChangedBss,
+				dbus_interface=WPAS_DBUS_BSS_INTERFACE,
+				signal_name="PropertiesChanged")
+
+	bus.add_signal_receiver(propertiesChangedNetwork,
+				dbus_interface=WPAS_DBUS_NETWORK_INTERFACE,
+				signal_name="PropertiesChanged")
+
+	gobject.MainLoop().run()
+
+if __name__ == "__main__":
+	main()
+
diff --git a/hostap/wpa_supplicant/examples/wpas-dbus-new-wps.py b/hostap/wpa_supplicant/examples/wpas-dbus-new-wps.py
new file mode 100755
index 0000000..b886385
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/wpas-dbus-new-wps.py
@@ -0,0 +1,80 @@
+#!/usr/bin/python
+
+import dbus
+import sys, os
+import time
+import gobject
+from dbus.mainloop.glib import DBusGMainLoop
+
+WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_INTERFACE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_OPATH = "/fi/w1/wpa_supplicant1"
+
+WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface"
+WPAS_DBUS_WPS_INTERFACE = "fi.w1.wpa_supplicant1.Interface.WPS"
+
+def propertiesChanged(properties):
+	if properties.has_key("State"):
+		print "PropertiesChanged: State: %s" % (properties["State"])
+
+def scanDone(success):
+	print "Scan done: success=%s" % success
+
+def bssAdded(bss, properties):
+	print "BSS added: %s" % (bss)
+
+def bssRemoved(bss):
+	print "BSS removed: %s" % (bss)
+
+def wpsEvent(name, args):
+	print "WPS event: %s" % (name)
+	print args
+
+def credentials(cred):
+	print "WPS credentials: %s" % (cred)
+
+def main():
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+	global bus
+	bus = dbus.SystemBus()
+	wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH)
+
+	if len(sys.argv) != 2:
+		print "Missing ifname argument"
+		os._exit(1)
+
+	wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE)
+	bus.add_signal_receiver(scanDone,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="ScanDone")
+	bus.add_signal_receiver(bssAdded,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="BSSAdded")
+	bus.add_signal_receiver(bssRemoved,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="BSSRemoved")
+	bus.add_signal_receiver(propertiesChanged,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="PropertiesChanged")
+	bus.add_signal_receiver(wpsEvent,
+				dbus_interface=WPAS_DBUS_WPS_INTERFACE,
+				signal_name="Event")
+	bus.add_signal_receiver(credentials,
+				dbus_interface=WPAS_DBUS_WPS_INTERFACE,
+				signal_name="Credentials")
+
+	ifname = sys.argv[1]
+
+	path = wpas.GetInterface(ifname)
+	if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+	if_obj.Set(WPAS_DBUS_WPS_INTERFACE, 'ProcessCredentials',
+		   dbus.Boolean(1),
+		   dbus_interface=dbus.PROPERTIES_IFACE)
+	wps = dbus.Interface(if_obj, WPAS_DBUS_WPS_INTERFACE)
+	wps.Start({'Role': 'enrollee', 'Type': 'pbc'})
+
+	gobject.MainLoop().run()
+
+if __name__ == "__main__":
+	main()
+
diff --git a/hostap/wpa_supplicant/examples/wpas-dbus-new.py b/hostap/wpa_supplicant/examples/wpas-dbus-new.py
new file mode 100755
index 0000000..25072ce
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/wpas-dbus-new.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+
+import dbus
+import sys, os
+import time
+import gobject
+from dbus.mainloop.glib import DBusGMainLoop
+
+WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_INTERFACE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_OPATH = "/fi/w1/wpa_supplicant1"
+
+WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface"
+WPAS_DBUS_INTERFACES_OPATH = "/fi/w1/wpa_supplicant1/Interfaces"
+WPAS_DBUS_BSS_INTERFACE = "fi.w1.wpa_supplicant1.BSS"
+
+def byte_array_to_string(s):
+	import urllib
+	r = ""    
+	for c in s:
+		if c >= 32 and c < 127:
+			r += "%c" % c
+		else:
+			r += urllib.quote(chr(c))
+	return r
+
+def list_interfaces(wpas_obj):
+	ifaces = wpas_obj.Get(WPAS_DBUS_INTERFACE, 'Interfaces',
+			      dbus_interface=dbus.PROPERTIES_IFACE)
+	for path in ifaces:
+		if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+		ifname = if_obj.Get(WPAS_DBUS_INTERFACES_INTERFACE, 'Ifname',
+			      dbus_interface=dbus.PROPERTIES_IFACE)
+		print ifname
+
+def propertiesChanged(properties):
+	if properties.has_key("State"):
+		print "PropertiesChanged: State: %s" % (properties["State"])
+
+def showBss(bss):
+	net_obj = bus.get_object(WPAS_DBUS_SERVICE, bss)
+	net = dbus.Interface(net_obj, WPAS_DBUS_BSS_INTERFACE)
+
+	# Convert the byte-array for SSID and BSSID to printable strings
+	val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'BSSID',
+			  dbus_interface=dbus.PROPERTIES_IFACE)
+	bssid = ""
+	for item in val:
+		bssid = bssid + ":%02x" % item
+	bssid = bssid[1:]
+	val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'SSID',
+			  dbus_interface=dbus.PROPERTIES_IFACE)
+	ssid = byte_array_to_string(val)
+
+	val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'WPA',
+			  dbus_interface=dbus.PROPERTIES_IFACE)
+	wpa = "no"
+	if len(val["KeyMgmt"]) > 0:
+		wpa = "yes"
+	val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'RSN',
+			  dbus_interface=dbus.PROPERTIES_IFACE)
+	wpa2 = "no"
+	if len(val["KeyMgmt"]) > 0:
+		wpa2 = "yes"
+	freq = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Frequency',
+			   dbus_interface=dbus.PROPERTIES_IFACE)
+	signal = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Signal',
+			     dbus_interface=dbus.PROPERTIES_IFACE)
+	val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Rates',
+			  dbus_interface=dbus.PROPERTIES_IFACE)
+	if len(val) > 0:
+		maxrate = val[0] / 1000000
+	else:
+		maxrate = 0
+
+	print "  %s  ::  ssid='%s'  wpa=%s  wpa2=%s  signal=%d  rate=%d  freq=%d" % (bssid, ssid, wpa, wpa2, signal, maxrate, freq)
+
+def scanDone(success):
+	print "Scan done: success=%s" % success
+	
+	res = if_obj.Get(WPAS_DBUS_INTERFACES_INTERFACE, 'BSSs',
+			 dbus_interface=dbus.PROPERTIES_IFACE)
+
+	print "Scanned wireless networks:"
+	for opath in res:
+		print opath
+		showBss(opath)
+
+def bssAdded(bss, properties):
+	print "BSS added: %s" % (bss)
+	showBss(bss)
+
+def bssRemoved(bss):
+	print "BSS removed: %s" % (bss)
+
+def main():
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+	global bus
+	bus = dbus.SystemBus()
+	wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH)
+
+	if len(sys.argv) != 2:
+		list_interfaces(wpas_obj)
+		os._exit(1)
+
+	wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE)
+	bus.add_signal_receiver(scanDone,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="ScanDone")
+	bus.add_signal_receiver(bssAdded,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="BSSAdded")
+	bus.add_signal_receiver(bssRemoved,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="BSSRemoved")
+	bus.add_signal_receiver(propertiesChanged,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="PropertiesChanged")
+
+	ifname = sys.argv[1]
+
+	# See if wpa_supplicant already knows about this interface
+	path = None
+	try:
+		path = wpas.GetInterface(ifname)
+	except dbus.DBusException, exc:
+		if not str(exc).startswith("fi.w1.wpa_supplicant1.InterfaceUnknown:"):
+			raise exc
+		try:
+			path = wpas.CreateInterface({'Ifname': ifname, 'Driver': 'test'})
+			time.sleep(1)
+
+		except dbus.DBusException, exc:
+			if not str(exc).startswith("fi.w1.wpa_supplicant1.InterfaceExists:"):
+				raise exc
+
+	global if_obj
+	if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+	global iface
+	iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE)
+	iface.Scan({'Type': 'active'})
+
+	gobject.MainLoop().run()
+
+	wpas.RemoveInterface(dbus.ObjectPath(path))
+
+if __name__ == "__main__":
+	main()
+
diff --git a/hostap/wpa_supplicant/examples/wpas-test.py b/hostap/wpa_supplicant/examples/wpas-test.py
new file mode 100755
index 0000000..fd7f73d
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/wpas-test.py
@@ -0,0 +1,91 @@
+#!/usr/bin/python
+
+import dbus
+import sys, os
+import time
+
+WPAS_DBUS_SERVICE = "fi.epitest.hostap.WPASupplicant"
+WPAS_DBUS_INTERFACE = "fi.epitest.hostap.WPASupplicant"
+WPAS_DBUS_OPATH = "/fi/epitest/hostap/WPASupplicant"
+
+WPAS_DBUS_INTERFACES_INTERFACE = "fi.epitest.hostap.WPASupplicant.Interface"
+WPAS_DBUS_INTERFACES_OPATH = "/fi/epitest/hostap/WPASupplicant/Interfaces"
+WPAS_DBUS_BSSID_INTERFACE = "fi.epitest.hostap.WPASupplicant.BSSID"
+
+def byte_array_to_string(s):
+	import urllib
+	r = ""    
+	for c in s:
+		if c >= 32 and c < 127:
+			r += "%c" % c
+		else:
+			r += urllib.quote(chr(c))
+	return r
+
+def main():
+	if len(sys.argv) != 2:
+		print "Usage: wpas-test.py <interface>"
+		os._exit(1)
+
+	ifname = sys.argv[1]
+
+	bus = dbus.SystemBus()
+	wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH)
+	wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE)
+
+	# See if wpa_supplicant already knows about this interface
+	path = None
+	try:
+		path = wpas.getInterface(ifname)
+	except dbus.dbus_bindings.DBusException, exc:
+		if str(exc) != "wpa_supplicant knows nothing about this interface.":
+			raise exc
+		try:
+			path = wpas.addInterface(ifname, {'driver': dbus.Variant('wext')})
+		except dbus.dbus_bindings.DBusException, exc:
+			if str(exc) != "wpa_supplicant already controls this interface.":
+				raise exc
+
+	if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+	iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE)
+	iface.scan()
+	# Should really wait for the "scanResults" signal instead of sleeping
+	time.sleep(5)
+	res = iface.scanResults()
+
+	print "Scanned wireless networks:"
+	for opath in res:
+		net_obj = bus.get_object(WPAS_DBUS_SERVICE, opath)
+		net = dbus.Interface(net_obj, WPAS_DBUS_BSSID_INTERFACE)
+		props = net.properties()
+
+		# Convert the byte-array for SSID and BSSID to printable strings
+		bssid = ""
+		for item in props["bssid"]:
+			bssid = bssid + ":%02x" % item
+		bssid = bssid[1:]
+		ssid = byte_array_to_string(props["ssid"])
+		wpa = "no"
+		if props.has_key("wpaie"):
+			wpa = "yes"
+		wpa2 = "no"
+		if props.has_key("rsnie"):
+			wpa2 = "yes"
+		freq = 0
+		if props.has_key("frequency"):
+			freq = props["frequency"]
+		caps = props["capabilities"]
+		qual = props["quality"]
+		level = props["level"]
+		noise = props["noise"]
+		maxrate = props["maxrate"] / 1000000
+
+		print "  %s  ::  ssid='%s'  wpa=%s  wpa2=%s  quality=%d%%  rate=%d  freq=%d" % (bssid, ssid, wpa, wpa2, qual, maxrate, freq)
+
+	wpas.removeInterface(dbus.ObjectPath(path))
+	# Should fail here with unknown interface error
+	iface.scan()
+
+if __name__ == "__main__":
+	main()
+
diff --git a/hostap/wpa_supplicant/examples/wps-ap-cli b/hostap/wpa_supplicant/examples/wps-ap-cli
new file mode 100755
index 0000000..cc2cff2
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/wps-ap-cli
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+CLI=wpa_cli
+
+pbc()
+{
+	echo "Starting PBC mode"
+	echo "Push button on the station within two minutes"
+	if ! $CLI wps_pbc | grep -q OK; then
+		echo "Failed to enable PBC mode"
+	fi
+}
+
+enter_pin()
+{
+	echo "Enter a PIN from a station to be enrolled to the network."
+	echo -n "Enrollee PIN: "
+	read pin
+	cpin=`$CLI wps_check_pin "$pin" | tail -1`
+	if [ "$cpin" = "FAIL-CHECKSUM" ]; then
+		echo "Checksum digit is not valid"
+		echo -n "Do you want to use this PIN (y/n)? "
+		read resp
+		case "$resp" in
+			y*)
+				cpin=`echo "$pin" | sed "s/[^1234567890]//g"`
+				;;
+			*)
+				return 1
+				;;
+		esac
+	fi
+	if [ "$cpin" = "FAIL" ]; then
+		echo "Invalid PIN: $pin"
+		return 1
+	fi
+	echo "Enabling Enrollee PIN: $cpin"
+	$CLI wps_pin any "$cpin"
+}
+
+show_config()
+{
+	$CLI status wps
+}
+
+main_menu()
+{
+	echo "WPS AP"
+	echo "------"
+	echo "1: Push button (activate PBC)"
+	echo "2: Enter Enrollee PIN"
+	echo "3: Show current configuration"
+	echo "0: Exit wps-ap-cli"
+
+	echo -n "Command: "
+	read cmd
+
+	case "$cmd" in
+		1)
+			pbc
+			;;
+		2)
+			enter_pin
+			;;
+		3)
+			show_config
+			;;
+		0)
+			exit 0
+			;;
+		*)
+			echo "Unknown command: $cmd"
+			;;
+	esac
+
+	echo
+	main_menu
+}
+
+
+main_menu
diff --git a/hostap/wpa_supplicant/examples/wps-nfc.py b/hostap/wpa_supplicant/examples/wps-nfc.py
new file mode 100755
index 0000000..7459eb9
--- /dev/null
+++ b/hostap/wpa_supplicant/examples/wps-nfc.py
@@ -0,0 +1,525 @@
+#!/usr/bin/python
+#
+# Example nfcpy to wpa_supplicant wrapper for WPS 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'
+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:
+        try:
+            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
+    if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
+        return False
+    return True
+
+def wpas_get_config_token(id=None):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    if id:
+        ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF " + id)
+    else:
+        ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF")
+    if "FAIL" in ret:
+        return None
+    return ret.rstrip().decode("hex")
+
+
+def wpas_get_er_config_token(uuid):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    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
+    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
+    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):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    if uuid is None:
+        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):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    return wpas.request("NFC_REPORT_HANDOVER " + type + " WPS " +
+                        str(req).encode("hex") + " " +
+                        str(sel).encode("hex"))
+
+
+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
+        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":
+                summary("WPS carrier type match - add WPS carrier record")
+                data = wpas_get_handover_sel(self.uuid)
+                if data is None:
+                    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:"
+        try:
+            print sel.pretty()
+        except Exception, e:
+            print e
+        print str(sel).encode("hex")
+
+        summary("Sending handover select")
+        self.success = True
+        return sel
+
+
+def wps_handover_init(llc):
+    summary("Trying to initiate WPS handover")
+
+    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")
+
+    message = nfc.ndef.HandoverRequestMessage(version="1.2")
+    message.nonce = random.randint(0, 0xffff)
+    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.wsc":
+            print "WPS carrier type match - send to wpa_supplicant"
+            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"
+    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):
+        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
+    else:
+        summary("Empty tag")
+
+    if success:
+        success_report("Tag read succeeded")
+
+    if wait_remove:
+        print "Remove tag"
+        while tag.is_present:
+            time.sleep(0.1)
+
+    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"
+    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"
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
+
+
+def wps_write_er_config_tag(clf, uuid, wait_remove=True):
+    print "Write WPS ER config token"
+    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"
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
+
+
+def wps_write_password_tag(clf, wait_remove=True):
+    print "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:
+        print "Could not get WPS password token from wpa_supplicant"
+        return
+
+    print "Touch an NFC tag"
+    clf.connect(rdwr={'on-connect': rdwr_connected_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 = 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
+
+    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:
+        if not clf.open("usb"):
+            print "Could not open connection with an NFC device"
+            raise SystemExit
+
+        if args.command == "write-config":
+            wps_write_config_tag(clf, id=args.id, wait_remove=not args.no_wait)
+            raise SystemExit
+
+        if args.command == "write-er-config":
+            wps_write_er_config_tag(clf, args.uuid, wait_remove=not args.no_wait)
+            raise SystemExit
+
+        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},
+                                   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/hostap/wpa_supplicant/gas_query.c b/hostap/wpa_supplicant/gas_query.c
new file mode 100644
index 0000000..10ecce7
--- /dev/null
+++ b/hostap/wpa_supplicant/gas_query.c
@@ -0,0 +1,719 @@
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#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"
+#include "gas_query.h"
+
+
+/** GAS query timeout in seconds */
+#define GAS_QUERY_TIMEOUT_PERIOD 2
+
+
+/**
+ * struct gas_query_pending - Pending GAS query
+ */
+struct gas_query_pending {
+	struct dl_list list;
+	struct gas_query *gas;
+	u8 addr[ETH_ALEN];
+	u8 dialog_token;
+	u8 next_frag_id;
+	unsigned int wait_comeback:1;
+	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,
+		   const struct wpabuf *resp, u16 status_code);
+	void *ctx;
+};
+
+/**
+ * struct gas_query - Internal GAS query data
+ */
+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;
+};
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
+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
+ * Returns: Pointer to GAS query data or %NULL on failure
+ */
+struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
+{
+	struct gas_query *gas;
+
+	gas = os_zalloc(sizeof(*gas));
+	if (gas == NULL)
+		return NULL;
+
+	gas->wpa_s = wpa_s;
+	dl_list_init(&gas->pending);
+
+	return gas;
+}
+
+
+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);
+	eloop_cancel_timeout(gas_query_timeout, gas, query);
+	dl_list_del(&query->list);
+	query->cb(query->ctx, query->addr, query->dialog_token, result,
+		  query->adv_proto, query->resp, query->status_code);
+	gas_query_free(query, 0);
+}
+
+
+/**
+ * gas_query_deinit - Deinitialize GAS query component
+ * @gas: GAS query data from gas_query_init()
+ */
+void gas_query_deinit(struct gas_query *gas)
+{
+	struct gas_query_pending *query, *next;
+
+	if (gas == NULL)
+		return;
+
+	dl_list_for_each_safe(query, next, &gas->pending,
+			      struct gas_query_pending, list)
+		gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
+
+	os_free(gas);
+}
+
+
+static struct gas_query_pending *
+gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
+{
+	struct gas_query_pending *q;
+	dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+		if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
+		    q->dialog_token == dialog_token)
+			return q;
+	}
+	return NULL;
+}
+
+
+static int gas_query_append(struct gas_query_pending *query, const u8 *data,
+			    size_t len)
+{
+	if (wpabuf_resize(&query->resp, len) < 0) {
+		wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
+		return -1;
+	}
+	wpabuf_put_data(query->resp, data, len);
+	return 0;
+}
+
+
+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)
+{
+	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 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),
+				     wait_time, gas_query_tx_status, 0);
+	if (res == 0)
+		query->offchannel_tx_started = 1;
+	return res;
+}
+
+
+static void gas_query_tx_comeback_req(struct gas_query *gas,
+				      struct gas_query_pending *query)
+{
+	struct wpabuf *req;
+
+	req = gas_build_comeback_req(query->dialog_token);
+	if (req == NULL) {
+		gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+		return;
+	}
+
+	if (gas_query_tx(gas, query, req) < 0) {
+		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+			   MACSTR, MAC2STR(query->addr));
+		gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+	}
+
+	wpabuf_free(req);
+}
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+	struct gas_query *gas = eloop_data;
+	struct gas_query_pending *query = user_ctx;
+
+	wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
+		   MAC2STR(query->addr));
+	gas_query_tx_comeback_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
+					    struct gas_query_pending *query,
+					    u16 comeback_delay)
+{
+	unsigned int secs, usecs;
+
+	secs = (comeback_delay * 1024) / 1000000;
+	usecs = comeback_delay * 1024 - secs * 1000000;
+	wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
+		   " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
+	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+	eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
+			       gas, query);
+}
+
+
+static void gas_query_rx_initial(struct gas_query *gas,
+				 struct gas_query_pending *query,
+				 const u8 *adv_proto, const u8 *resp,
+				 size_t len, u16 comeback_delay)
+{
+	wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
+		   MACSTR " (dialog_token=%u comeback_delay=%u)",
+		   MAC2STR(query->addr), query->dialog_token, comeback_delay);
+
+	query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
+	if (query->adv_proto == NULL) {
+		gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+		return;
+	}
+
+	if (comeback_delay) {
+		query->wait_comeback = 1;
+		gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+		return;
+	}
+
+	/* Query was completed without comeback mechanism */
+	if (gas_query_append(query, resp, len) < 0) {
+		gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+		return;
+	}
+
+	gas_query_done(gas, query, GAS_QUERY_SUCCESS);
+}
+
+
+static void gas_query_rx_comeback(struct gas_query *gas,
+				  struct gas_query_pending *query,
+				  const u8 *adv_proto, const u8 *resp,
+				  size_t len, u8 frag_id, u8 more_frags,
+				  u16 comeback_delay)
+{
+	wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
+		   MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
+		   "comeback_delay=%u)",
+		   MAC2STR(query->addr), query->dialog_token, frag_id,
+		   more_frags, comeback_delay);
+
+	if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
+	    os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
+		      wpabuf_len(query->adv_proto)) != 0) {
+		wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
+			   "between initial and comeback response from "
+			   MACSTR, MAC2STR(query->addr));
+		gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+		return;
+	}
+
+	if (comeback_delay) {
+		if (frag_id) {
+			wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
+				   "with non-zero frag_id and comeback_delay "
+				   "from " MACSTR, MAC2STR(query->addr));
+			gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+			return;
+		}
+		gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+		return;
+	}
+
+	if (frag_id != query->next_frag_id) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
+			   "from " MACSTR, MAC2STR(query->addr));
+		if (frag_id + 1 == query->next_frag_id) {
+			wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
+				   "retry of previous fragment");
+			return;
+		}
+		gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+		return;
+	}
+	query->next_frag_id++;
+
+	if (gas_query_append(query, resp, len) < 0) {
+		gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+		return;
+	}
+
+	if (more_frags) {
+		gas_query_tx_comeback_req(gas, query);
+		return;
+	}
+
+	gas_query_done(gas, query, GAS_QUERY_SUCCESS);
+}
+
+
+/**
+ * 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, 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;
+	unsigned int left;
+
+	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++;
+
+	if (action != WLAN_PA_GAS_INITIAL_RESP &&
+	    action != WLAN_PA_GAS_COMEBACK_RESP)
+		return -1; /* Not a GAS response */
+
+	query = gas_query_get_pending(gas, sa, dialog_token);
+	if (query == NULL) {
+		wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
+			   " dialog token %u", MAC2STR(sa), dialog_token);
+		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 "
+			   "response", MAC2STR(sa), dialog_token);
+		return 0;
+	}
+
+	if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
+			   MACSTR " dialog token %u when waiting for initial "
+			   "response", MAC2STR(sa), dialog_token);
+		return 0;
+	}
+
+	query->status_code = WPA_GET_LE16(pos);
+	pos += 2;
+
+	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);
+		gas_query_done(gas, query, GAS_QUERY_FAILURE);
+		return 0;
+	}
+
+	if (action == WLAN_PA_GAS_COMEBACK_RESP) {
+		if (pos + 1 > data + len)
+			return 0;
+		frag_id = *pos & 0x7f;
+		more_frags = (*pos & 0x80) >> 7;
+		pos++;
+	}
+
+	/* Comeback Delay */
+	if (pos + 2 > data + len)
+		return 0;
+	comeback_delay = WPA_GET_LE16(pos);
+	pos += 2;
+
+	/* Advertisement Protocol element */
+	if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
+		wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
+			   "Protocol element in the response from " MACSTR,
+			   MAC2STR(sa));
+		return 0;
+	}
+
+	if (*pos != WLAN_EID_ADV_PROTO) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
+			   "Protocol element ID %u in response from " MACSTR,
+			   *pos, MAC2STR(sa));
+		return 0;
+	}
+
+	adv_proto = pos;
+	pos += 2 + pos[1];
+
+	/* Query Response Length */
+	if (pos + 2 > data + len) {
+		wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
+		return 0;
+	}
+	resp_len = WPA_GET_LE16(pos);
+	pos += 2;
+
+	left = data + len - pos;
+	if (resp_len > left) {
+		wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
+			   "response from " MACSTR, MAC2STR(sa));
+		return 0;
+	}
+
+	if (resp_len < left) {
+		wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
+			   "after Query Response from " MACSTR,
+			   left - resp_len, MAC2STR(sa));
+	}
+
+	if (action == WLAN_PA_GAS_COMEBACK_RESP)
+		gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
+				      frag_id, more_frags, comeback_delay);
+	else
+		gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
+				     comeback_delay);
+
+	return 0;
+}
+
+
+static void gas_query_timeout(void *eloop_data, void *user_ctx)
+{
+	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
+		   " dialog token %u",
+		   MAC2STR(query->addr), query->dialog_token);
+	gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
+}
+
+
+static int gas_query_dialog_token_available(struct gas_query *gas,
+					    const u8 *dst, u8 dialog_token)
+{
+	struct gas_query_pending *q;
+	dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+		if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
+		    dialog_token == q->dialog_token)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+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;
+	struct wpa_supplicant *wpa_s = gas->wpa_s;
+
+	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;
+	}
+
+	if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"Failed to assign random MAC address for GAS");
+		gas_query_free(query, 1);
+		radio_work_done(work);
+		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 (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
+ */
+int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
+		  struct wpabuf *req,
+		  void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+			     enum gas_query_result result,
+			     const struct wpabuf *adv_proto,
+			     const struct wpabuf *resp, u16 status_code),
+		  void *ctx)
+{
+	struct gas_query_pending *query;
+	int dialog_token;
+	static int next_start = 0;
+
+	if (wpabuf_len(req) < 3)
+		return -1;
+
+	for (dialog_token = 0; dialog_token < 256; dialog_token++) {
+		if (gas_query_dialog_token_available(
+			    gas, dst, (next_start + dialog_token) % 256))
+			break;
+	}
+	if (dialog_token == 256)
+		return -1; /* Too many pending queries */
+	dialog_token = (next_start + dialog_token) % 256;
+	next_start = (dialog_token + 1) % 256;
+
+	query = os_zalloc(sizeof(*query));
+	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_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;
+	}
+
+	return dialog_token;
+}
+
+
+/**
+ * gas_query_cancel - Cancel a pending GAS query
+ * @gas: GAS query data from gas_query_init()
+ * @dst: Destination MAC address for the query
+ * @dialog_token: Dialog token from gas_query_req()
+ */
+void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token)
+{
+	struct gas_query_pending *query;
+
+	query = gas_query_get_pending(gas, dst, dialog_token);
+	if (query)
+		gas_query_done(gas, query, GAS_QUERY_CANCELLED);
+
+}
diff --git a/hostap/wpa_supplicant/gas_query.h b/hostap/wpa_supplicant/gas_query.h
new file mode 100644
index 0000000..ad13490
--- /dev/null
+++ b/hostap/wpa_supplicant/gas_query.h
@@ -0,0 +1,59 @@
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_QUERY_H
+#define GAS_QUERY_H
+
+struct gas_query;
+
+#ifdef CONFIG_GAS
+
+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, u8 categ, const u8 *data, size_t len,
+		 int freq);
+
+/**
+ * enum gas_query_result - GAS query result
+ */
+enum gas_query_result {
+	GAS_QUERY_SUCCESS,
+	GAS_QUERY_FAILURE,
+	GAS_QUERY_TIMEOUT,
+	GAS_QUERY_PEER_ERROR,
+	GAS_QUERY_INTERNAL_ERROR,
+	GAS_QUERY_CANCELLED,
+	GAS_QUERY_DELETED_AT_DEINIT
+};
+
+int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
+		  struct wpabuf *req,
+		  void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+			     enum gas_query_result result,
+			     const struct wpabuf *adv_proto,
+			     const struct wpabuf *resp, u16 status_code),
+		  void *ctx);
+void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token);
+
+#else /* CONFIG_GAS */
+
+static inline struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
+{
+	return (void *) 1;
+}
+
+static inline void gas_query_deinit(struct gas_query *gas)
+{
+}
+
+#endif /* CONFIG_GAS */
+
+
+#endif /* GAS_QUERY_H */
diff --git a/hostap/wpa_supplicant/hs20_supplicant.c b/hostap/wpa_supplicant/hs20_supplicant.c
new file mode 100644
index 0000000..a1afc85
--- /dev/null
+++ b/hostap/wpa_supplicant/hs20_supplicant.c
@@ -0,0 +1,1009 @@
+/*
+ * Copyright (c) 2009, Atheros Communications, 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.
+ */
+
+#include "includes.h"
+#include <sys/stat.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_common.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 "config.h"
+#include "scan.h"
+#include "bss.h"
+#include "blacklist.h"
+#include "gas_query.h"
+#include "interworking.h"
+#include "hs20_supplicant.h"
+
+
+#define OSU_MAX_ITEMS 10
+
+struct osu_lang_string {
+	char lang[4];
+	char text[253];
+};
+
+struct osu_icon {
+	u16 width;
+	u16 height;
+	char lang[4];
+	char icon_type[256];
+	char filename[256];
+	unsigned int id;
+	unsigned int failed:1;
+};
+
+struct osu_provider {
+	u8 bssid[ETH_ALEN];
+	u8 osu_ssid[SSID_MAX_LEN];
+	u8 osu_ssid_len;
+	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, pps_mo_id >= 0 ? 7 : 5);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE);
+	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);
+}
+
+
+int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+		    struct wpa_bss *bss)
+{
+	if (!wpa_s->conf->hs20 || !ssid)
+		return 0;
+
+	if (ssid->parent_cred)
+		return 1;
+
+	if (bss && !wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE))
+		return 0;
+
+	/*
+	 * This may catch some non-Hotspot 2.0 cases, but it is safer to do that
+	 * than cause Hotspot 2.0 connections without indication element getting
+	 * added. Non-Hotspot 2.0 APs should ignore the unknown vendor element.
+	 */
+
+	if (!(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X))
+		return 0;
+	if (!(ssid->pairwise_cipher & WPA_CIPHER_CCMP))
+		return 0;
+	if (ssid->proto != WPA_PROTO_RSN)
+		return 0;
+
+	return 1;
+}
+
+
+int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	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;
+
+	if (buf == NULL)
+		return;
+
+	len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+	if (stypes == BIT(HS20_STYPE_NAI_HOME_REALM_QUERY)) {
+		wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
+		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);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		for (i = 0; i < 32; i++) {
+			if (stypes & BIT(i))
+				wpabuf_put_u8(buf, i);
+		}
+	}
+	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;
+}
+
+
+int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
+		       const u8 *payload, size_t payload_len)
+{
+	struct wpabuf *buf;
+	int ret = 0;
+	int freq;
+	struct wpa_bss *bss;
+	int res;
+
+	bss = wpa_bss_get_bssid(wpa_s, dst);
+	if (!bss) {
+		wpa_printf(MSG_WARNING,
+			   "ANQP: Cannot send query to unknown BSS "
+			   MACSTR, MAC2STR(dst));
+		return -1;
+	}
+
+	wpa_bss_anqp_unshare_alloc(bss);
+	freq = bss->freq;
+
+	wpa_printf(MSG_DEBUG, "HS20: ANQP Query Request to " MACSTR " for "
+		   "subtypes 0x%x", MAC2STR(dst), stypes);
+
+	buf = hs20_build_anqp_req(stypes, payload, payload_len);
+	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);
+
+	return ret;
+}
+
+
+static void hs20_set_osu_access_permission(const char *osu_dir,
+					   const char *fname)
+{
+	struct stat statbuf;
+
+	/* Get OSU directory information */
+	if (stat(osu_dir, &statbuf) < 0) {
+		wpa_printf(MSG_WARNING, "Cannot stat the OSU directory %s",
+			   osu_dir);
+		return;
+	}
+
+	if (chmod(fname, statbuf.st_mode) < 0) {
+		wpa_printf(MSG_WARNING,
+			   "Cannot change the permissions for %s", fname);
+		return;
+	}
+
+	if (chown(fname, statbuf.st_uid, statbuf.st_gid) < 0) {
+		wpa_printf(MSG_WARNING, "Cannot change the ownership for %s",
+			   fname);
+	}
+}
+
+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;
+
+	hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname);
+
+	if (fwrite(pos, slen, 1, f) != 1) {
+		fclose(f);
+		unlink(fname);
+		return -1;
+	}
+	fclose(f);
+
+	wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP-ICON %s", fname);
+	return 0;
+}
+
+
+static void hs20_continue_icon_fetch(void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	if (wpa_s->fetch_osu_icon_in_progress)
+		hs20_next_osu_icon(wpa_s);
+}
+
+
+static void hs20_osu_icon_fetch_result(struct wpa_supplicant *wpa_s, int res)
+{
+	size_t i, j;
+	struct os_reltime now, tmp;
+	int dur;
+
+	os_get_reltime(&now);
+	os_reltime_sub(&now, &wpa_s->osu_icon_fetch_start, &tmp);
+	dur = tmp.sec * 1000 + tmp.usec / 1000;
+	wpa_printf(MSG_DEBUG, "HS 2.0: Icon fetch dur=%d ms res=%d",
+		   dur, res);
+
+	for (i = 0; i < wpa_s->osu_prov_count; i++) {
+		struct osu_provider *osu = &wpa_s->osu_prov[i];
+		for (j = 0; j < osu->icon_count; j++) {
+			struct osu_icon *icon = &osu->icon[j];
+			if (icon->id || icon->failed)
+				continue;
+			if (res < 0)
+				icon->failed = 1;
+			else
+				icon->id = wpa_s->osu_icon_id;
+			return;
+		}
+	}
+}
+
+
+void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
+				  struct wpa_bss *bss, const u8 *sa,
+				  const u8 *data, size_t slen)
+{
+	const u8 *pos = data;
+	u8 subtype;
+	struct wpa_bss_anqp *anqp = NULL;
+	int ret;
+
+	if (slen < 2)
+		return;
+
+	if (bss)
+		anqp = bss->anqp;
+
+	subtype = *pos++;
+	slen--;
+
+	pos++; /* Reserved */
+	slen--;
+
+	switch (subtype) {
+	case HS20_STYPE_CAPABILITY_LIST:
+		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+			" HS Capability List", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->hs20_capability_list);
+			anqp->hs20_capability_list =
+				wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
+		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+			" Operator Friendly Name", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "oper friendly name", pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->hs20_operator_friendly_name);
+			anqp->hs20_operator_friendly_name =
+				wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case HS20_STYPE_WAN_METRICS:
+		wpa_hexdump(MSG_DEBUG, "WAN Metrics", pos, slen);
+		if (slen < 13) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short WAN "
+				"Metrics value from " MACSTR, MAC2STR(sa));
+			break;
+		}
+		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+			" WAN Metrics %02x:%u:%u:%u:%u:%u", MAC2STR(sa),
+			pos[0], WPA_GET_LE32(pos + 1), WPA_GET_LE32(pos + 5),
+			pos[9], pos[10], WPA_GET_LE16(pos + 11));
+		if (anqp) {
+			wpabuf_free(anqp->hs20_wan_metrics);
+			anqp->hs20_wan_metrics = wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case HS20_STYPE_CONNECTION_CAPABILITY:
+		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+			" Connection Capability", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "conn capability", pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->hs20_connection_capability);
+			anqp->hs20_connection_capability =
+				wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case HS20_STYPE_OPERATING_CLASS:
+		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+			" Operating Class", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "Operating Class", pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->hs20_operating_class);
+			anqp->hs20_operating_class =
+				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;
+	}
+
+	hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname);
+
+	for (i = 0; i < wpa_s->osu_prov_count; i++) {
+		struct osu_provider *osu = &wpa_s->osu_prov[i];
+		if (i > 0)
+			fprintf(f, "\n");
+		fprintf(f, "OSU-PROVIDER " MACSTR "\n"
+			"uri=%s\n"
+			"methods=%08x\n",
+			MAC2STR(osu->bssid), osu->server_uri, osu->osu_methods);
+		if (osu->osu_ssid_len) {
+			fprintf(f, "osu_ssid=%s\n",
+				wpa_ssid_txt(osu->osu_ssid,
+					     osu->osu_ssid_len));
+		}
+		if (osu->osu_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;
+	u8 uri_len, osu_method_len, osu_nai_len;
+
+	wpa_hexdump(MSG_DEBUG, "HS 2.0: Parsing OSU Provider", pos, len);
+	prov = os_realloc_array(wpa_s->osu_prov,
+				wpa_s->osu_prov_count + 1,
+				sizeof(*prov));
+	if (prov == NULL)
+		return;
+	wpa_s->osu_prov = prov;
+	prov = &prov[wpa_s->osu_prov_count];
+	os_memset(prov, 0, sizeof(*prov));
+
+	os_memcpy(prov->bssid, bss->bssid, ETH_ALEN);
+	os_memcpy(prov->osu_ssid, osu_ssid, osu_ssid_len);
+	prov->osu_ssid_len = osu_ssid_len;
+
+	/* 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 (len2 > end - pos) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+			   "Friendly Name Duples");
+		return;
+	}
+	pos2 = pos;
+	pos += len2;
+
+	/* OSU Friendly Name Duples */
+	while (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) {
+		wpa_printf(MSG_DEBUG,
+			   "HS 2.0: Not enough room for OSU Server URI length");
+		return;
+	}
+	uri_len = *pos++;
+	if (uri_len > end - pos) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server "
+			   "URI");
+		return;
+	}
+	os_memcpy(prov->server_uri, pos, uri_len);
+	pos += uri_len;
+
+	/* OSU Method list */
+	if (pos + 1 > end) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
+			   "list length");
+		return;
+	}
+	osu_method_len = pos[0];
+	if (osu_method_len > end - pos - 1) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
+			   "list");
+		return;
+	}
+	pos2 = pos + 1;
+	pos += 1 + osu_method_len;
+	while (pos2 < pos) {
+		if (*pos2 < 32)
+			prov->osu_methods |= BIT(*pos2);
+		pos2++;
+	}
+
+	/* Icons Available Length */
+	if (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 (len2 > end - pos) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
+			   "Available");
+		return;
+	}
+	pos2 = pos;
+	pos += len2;
+
+	/* Icons Available */
+	while (pos2 < pos) {
+		struct osu_icon *icon = &prov->icon[prov->icon_count];
+		u8 flen;
+
+		if (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;
+
+		flen = pos2[0];
+		if (flen > pos - pos2 - 1) {
+			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type");
+			break;
+		}
+		os_memcpy(icon->icon_type, pos2 + 1, flen);
+		pos2 += 1 + flen;
+
+		if (pos2 + 1 > pos) {
+			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
+				   "Filename length");
+			break;
+		}
+		flen = pos2[0];
+		if (flen > pos - pos2 - 1) {
+			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
+				   "Filename");
+			break;
+		}
+		os_memcpy(icon->filename, pos2 + 1, flen);
+		pos2 += 1 + flen;
+
+		prov->icon_count++;
+	}
+
+	/* OSU_NAI */
+	if (pos + 1 > end) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
+		return;
+	}
+	osu_nai_len = pos[0];
+	if (osu_nai_len > end - pos - 1) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
+		return;
+	}
+	os_memcpy(prov->osu_nai, pos + 1, osu_nai_len);
+	pos += 1 + osu_nai_len;
+
+	/* 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 (len2 > end - pos) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+			   "Service Description Duples");
+		return;
+	}
+	pos2 = pos;
+	pos += len2;
+
+	/* OSU Service Description Duples */
+	while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) {
+		struct osu_lang_string *f;
+		u8 descr_len;
+
+		descr_len = pos2[0];
+		if (descr_len > pos - pos2 - 1 || descr_len < 3) {
+			wpa_printf(MSG_DEBUG, "Invalid OSU Service "
+				   "Description");
+			break;
+		}
+		f = &prov->serv_desc[prov->serv_desc_count++];
+		os_memcpy(f->lang, pos2 + 1, 3);
+		os_memcpy(f->text, pos2 + 1 + 3, descr_len - 3);
+		pos2 += 1 + descr_len;
+	}
+
+	wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR,
+		   MAC2STR(bss->bssid));
+	wpa_s->osu_prov_count++;
+}
+
+
+void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss;
+	struct wpabuf *prov_anqp;
+	const u8 *pos, *end;
+	u16 len;
+	const u8 *osu_ssid;
+	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 > SSID_MAX_LEN) {
+			wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID "
+				   "Length %u", osu_ssid_len);
+			continue;
+		}
+		osu_ssid = pos;
+		pos += osu_ssid_len;
+
+		if (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 (len > (unsigned int) (end - pos))
+				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");
+	if (!wpa_s->fetch_osu_waiting_scan) {
+		wpa_printf(MSG_DEBUG, "OSU fetch have been canceled");
+		return;
+	}
+	wpa_s->network_select = 0;
+	wpa_s->fetch_all_anqp = 1;
+	wpa_s->fetch_osu_info = 1;
+	wpa_s->fetch_osu_icon_in_progress = 0;
+
+	interworking_start_fetch_anqp(wpa_s);
+}
+
+
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s)
+{
+	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->fetch_osu_waiting_scan = 1;
+	wpa_s->num_osu_scans++;
+	wpa_s->scan_req = MANUAL_SCAN_REQ;
+	wpa_s->scan_res_handler = hs20_osu_scan_res_handler;
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s)
+{
+	wpa_printf(MSG_DEBUG, "Cancel OSU fetch");
+	interworking_stop_fetch_anqp(wpa_s);
+	wpa_s->fetch_osu_waiting_scan = 0;
+	wpa_s->network_select = 0;
+	wpa_s->fetch_osu_info = 0;
+	wpa_s->fetch_osu_icon_in_progress = 0;
+}
+
+
+void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s)
+{
+	hs20_osu_icon_fetch_result(wpa_s, -1);
+	eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
+	eloop_register_timeout(0, 0, hs20_continue_icon_fetch, wpa_s, NULL);
+}
+
+
+void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
+				      const char *url, u8 osu_method)
+{
+	if (url)
+		wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION "%u %s",
+			osu_method, url);
+	else
+		wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION);
+}
+
+
+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/hostap/wpa_supplicant/hs20_supplicant.h b/hostap/wpa_supplicant/hs20_supplicant.h
new file mode 100644
index 0000000..85b5120
--- /dev/null
+++ b/hostap/wpa_supplicant/hs20_supplicant.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HS20_SUPPLICANT_H
+#define HS20_SUPPLICANT_H
+
+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,
+				  struct wpa_bss *bss, 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/hostap/wpa_supplicant/ibss_rsn.c b/hostap/wpa_supplicant/ibss_rsn.c
new file mode 100644
index 0000000..d9d0ae7
--- /dev/null
+++ b/hostap/wpa_supplicant/ibss_rsn.c
@@ -0,0 +1,920 @@
+/*
+ * wpa_supplicant - IBSS RSN
+ * Copyright (c) 2009-2013, 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 "common/wpa_ctrl.h"
+#include "utils/eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/wpa_ie.h"
+#include "ap/wpa_auth.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "common/ieee802_11_defs.h"
+#include "ibss_rsn.h"
+
+
+static void ibss_rsn_auth_timeout(void *eloop_ctx, void *timeout_ctx);
+
+
+static struct ibss_rsn_peer * ibss_rsn_get_peer(struct ibss_rsn *ibss_rsn,
+						const u8 *addr)
+{
+	struct ibss_rsn_peer *peer;
+
+	for (peer = ibss_rsn->peers; peer; peer = peer->next)
+		if (os_memcmp(addr, peer->addr, ETH_ALEN) == 0)
+			break;
+	return peer;
+}
+
+
+static void ibss_rsn_free(struct ibss_rsn_peer *peer)
+{
+	eloop_cancel_timeout(ibss_rsn_auth_timeout, peer, NULL);
+	wpa_auth_sta_deinit(peer->auth);
+	wpa_sm_deinit(peer->supp);
+	os_free(peer);
+}
+
+
+static void supp_set_state(void *ctx, enum wpa_states state)
+{
+	struct ibss_rsn_peer *peer = ctx;
+	peer->supp_state = state;
+}
+
+
+static enum wpa_states supp_get_state(void *ctx)
+{
+	struct ibss_rsn_peer *peer = ctx;
+	return peer->supp_state;
+}
+
+
+static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
+			   size_t len)
+{
+	struct ibss_rsn_peer *peer = ctx;
+	struct wpa_supplicant *wpa_s = peer->ibss_rsn->wpa_s;
+
+	wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x "
+		   "len=%lu)",
+		   __func__, MAC2STR(dest), proto, (unsigned long) len);
+
+	if (wpa_s->l2)
+		return l2_packet_send(wpa_s->l2, dest, proto, buf, len);
+
+	return -1;
+}
+
+
+static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data,
+			     u16 data_len, size_t *msg_len, void **data_pos)
+{
+	struct ieee802_1x_hdr *hdr;
+
+	wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)",
+		   __func__, type, data_len);
+
+	*msg_len = sizeof(*hdr) + data_len;
+	hdr = os_malloc(*msg_len);
+	if (hdr == NULL)
+		return NULL;
+
+	hdr->version = 2;
+	hdr->type = type;
+	hdr->length = host_to_be16(data_len);
+
+	if (data)
+		os_memcpy(hdr + 1, data, data_len);
+	else
+		os_memset(hdr + 1, 0, data_len);
+
+	if (data_pos)
+		*data_pos = hdr + 1;
+
+	return (u8 *) hdr;
+}
+
+
+static int supp_get_beacon_ie(void *ctx)
+{
+	struct ibss_rsn_peer *peer = ctx;
+
+	wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
+	/* TODO: get correct RSN IE */
+	return wpa_sm_set_ap_rsn_ie(peer->supp,
+				    (u8 *) "\x30\x14\x01\x00"
+				    "\x00\x0f\xac\x04"
+				    "\x01\x00\x00\x0f\xac\x04"
+				    "\x01\x00\x00\x0f\xac\x02"
+				    "\x00\x00", 22);
+}
+
+
+static void ibss_check_rsn_completed(struct ibss_rsn_peer *peer)
+{
+	struct wpa_supplicant *wpa_s = peer->ibss_rsn->wpa_s;
+
+	if ((peer->authentication_status &
+	     (IBSS_RSN_SET_PTK_SUPP | IBSS_RSN_SET_PTK_AUTH)) !=
+	    (IBSS_RSN_SET_PTK_SUPP | IBSS_RSN_SET_PTK_AUTH))
+		return;
+	if (peer->authentication_status & IBSS_RSN_REPORTED_PTK)
+		return;
+	peer->authentication_status |= IBSS_RSN_REPORTED_PTK;
+	wpa_msg(wpa_s, MSG_INFO, IBSS_RSN_COMPLETED MACSTR,
+		MAC2STR(peer->addr));
+}
+
+
+static int supp_set_key(void *ctx, enum wpa_alg alg,
+			const u8 *addr, int key_idx, int set_tx,
+			const u8 *seq, size_t seq_len,
+			const u8 *key, size_t key_len)
+{
+	struct ibss_rsn_peer *peer = ctx;
+
+	wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d "
+		   "set_tx=%d)",
+		   __func__, alg, MAC2STR(addr), key_idx, set_tx);
+	wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len);
+	wpa_hexdump_key(MSG_DEBUG, "SUPP: set_key - key", key, key_len);
+
+	if (key_idx == 0) {
+		peer->authentication_status |= IBSS_RSN_SET_PTK_SUPP;
+		ibss_check_rsn_completed(peer);
+		/*
+		 * In IBSS RSN, the pairwise key from the 4-way handshake
+		 * initiated by the peer with highest MAC address is used.
+		 */
+		if (os_memcmp(peer->ibss_rsn->wpa_s->own_addr, peer->addr,
+			      ETH_ALEN) > 0) {
+			wpa_printf(MSG_DEBUG, "SUPP: Do not use this PTK");
+			return 0;
+		}
+	}
+
+	if (is_broadcast_ether_addr(addr))
+		addr = peer->addr;
+	return wpa_drv_set_key(peer->ibss_rsn->wpa_s, alg, addr, key_idx,
+			       set_tx, seq, seq_len, key, key_len);
+}
+
+
+static void * supp_get_network_ctx(void *ctx)
+{
+	struct ibss_rsn_peer *peer = ctx;
+	return wpa_supplicant_get_ssid(peer->ibss_rsn->wpa_s);
+}
+
+
+static int supp_mlme_setprotection(void *ctx, const u8 *addr,
+				   int protection_type, int key_type)
+{
+	wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d "
+		   "key_type=%d)",
+		   __func__, MAC2STR(addr), protection_type, key_type);
+	return 0;
+}
+
+
+static void supp_cancel_auth_timeout(void *ctx)
+{
+	wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
+}
+
+
+static void supp_deauthenticate(void * ctx, int reason_code)
+{
+	wpa_printf(MSG_DEBUG, "SUPP: %s (TODO)", __func__);
+}
+
+
+static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
+			      const u8 *psk)
+{
+	struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return -1;
+
+	ctx->ctx = peer;
+	ctx->msg_ctx = peer->ibss_rsn->wpa_s;
+	ctx->set_state = supp_set_state;
+	ctx->get_state = supp_get_state;
+	ctx->ether_send = supp_ether_send;
+	ctx->get_beacon_ie = supp_get_beacon_ie;
+	ctx->alloc_eapol = supp_alloc_eapol;
+	ctx->set_key = supp_set_key;
+	ctx->get_network_ctx = supp_get_network_ctx;
+	ctx->mlme_setprotection = supp_mlme_setprotection;
+	ctx->cancel_auth_timeout = supp_cancel_auth_timeout;
+	ctx->deauthenticate = supp_deauthenticate;
+	peer->supp = wpa_sm_init(ctx);
+	if (peer->supp == NULL) {
+		wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed");
+		return -1;
+	}
+
+	wpa_sm_set_own_addr(peer->supp, own_addr);
+	wpa_sm_set_param(peer->supp, WPA_PARAM_RSN_ENABLED, 1);
+	wpa_sm_set_param(peer->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN);
+	wpa_sm_set_param(peer->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP);
+	wpa_sm_set_param(peer->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP);
+	wpa_sm_set_param(peer->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK);
+	wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL);
+
+	peer->supp_ie_len = sizeof(peer->supp_ie);
+	if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie,
+					    &peer->supp_ie_len) < 0) {
+		wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()"
+			   " failed");
+		return -1;
+	}
+
+	wpa_sm_notify_assoc(peer->supp, peer->addr);
+
+	return 0;
+}
+
+
+static void auth_logger(void *ctx, const u8 *addr, logger_level level,
+			const char *txt)
+{
+	if (addr)
+		wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s",
+			   MAC2STR(addr), txt);
+	else
+		wpa_printf(MSG_DEBUG, "AUTH: %s", txt);
+}
+
+
+static const u8 * auth_get_psk(void *ctx, const u8 *addr,
+			       const u8 *p2p_dev_addr, const u8 *prev_psk)
+{
+	struct ibss_rsn *ibss_rsn = ctx;
+	wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
+		   __func__, MAC2STR(addr), prev_psk);
+	if (prev_psk)
+		return NULL;
+	return ibss_rsn->psk;
+}
+
+
+static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data,
+			   size_t data_len, int encrypt)
+{
+	struct ibss_rsn *ibss_rsn = ctx;
+	struct wpa_supplicant *wpa_s = ibss_rsn->wpa_s;
+
+	wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu "
+		   "encrypt=%d)",
+		   __func__, MAC2STR(addr), (unsigned long) data_len, encrypt);
+
+	if (wpa_s->l2)
+		return l2_packet_send(wpa_s->l2, addr, ETH_P_EAPOL, data,
+				      data_len);
+
+	return -1;
+}
+
+
+static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
+			const u8 *addr, int idx, u8 *key, size_t key_len)
+{
+	struct ibss_rsn *ibss_rsn = ctx;
+	u8 seq[6];
+
+	os_memset(seq, 0, sizeof(seq));
+
+	if (addr) {
+		wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d addr=" MACSTR
+			   " key_idx=%d)",
+			   __func__, alg, MAC2STR(addr), idx);
+	} else {
+		wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d key_idx=%d)",
+			   __func__, alg, idx);
+	}
+	wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len);
+
+	if (idx == 0) {
+		if (addr) {
+			struct ibss_rsn_peer *peer;
+			peer = ibss_rsn_get_peer(ibss_rsn, addr);
+			if (peer) {
+				peer->authentication_status |=
+					IBSS_RSN_SET_PTK_AUTH;
+				ibss_check_rsn_completed(peer);
+			}
+		}
+		/*
+		 * In IBSS RSN, the pairwise key from the 4-way handshake
+		 * initiated by the peer with highest MAC address is used.
+		 */
+		if (addr == NULL ||
+		    os_memcmp(ibss_rsn->wpa_s->own_addr, addr, ETH_ALEN) < 0) {
+			wpa_printf(MSG_DEBUG, "AUTH: Do not use this PTK");
+			return 0;
+		}
+	}
+
+	return wpa_drv_set_key(ibss_rsn->wpa_s, alg, addr, idx,
+			       1, seq, 6, key, key_len);
+}
+
+
+static void ibss_rsn_disconnect(void *ctx, const u8 *addr, u16 reason)
+{
+	struct ibss_rsn *ibss_rsn = ctx;
+	wpa_drv_sta_deauth(ibss_rsn->wpa_s, addr, reason);
+}
+
+
+static int auth_for_each_sta(void *ctx, int (*cb)(struct wpa_state_machine *sm,
+						  void *ctx),
+			     void *cb_ctx)
+{
+	struct ibss_rsn *ibss_rsn = ctx;
+	struct ibss_rsn_peer *peer;
+
+	wpa_printf(MSG_DEBUG, "AUTH: for_each_sta");
+
+	for (peer = ibss_rsn->peers; peer; peer = peer->next) {
+		if (peer->auth && cb(peer->auth, cb_ctx))
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void ibss_set_sta_authorized(struct ibss_rsn *ibss_rsn,
+				    struct ibss_rsn_peer *peer, int authorized)
+{
+	int res;
+
+	if (authorized) {
+		res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr,
+					    WPA_STA_AUTHORIZED,
+					    WPA_STA_AUTHORIZED, ~0);
+		wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " authorizing port",
+			   MAC2STR(peer->addr));
+	} else {
+		res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr,
+					    0, 0, ~WPA_STA_AUTHORIZED);
+		wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " unauthorizing port",
+			   MAC2STR(peer->addr));
+	}
+
+	if (res && errno != ENOENT) {
+		wpa_printf(MSG_DEBUG, "Could not set station " MACSTR " flags "
+			   "for kernel driver (errno=%d)",
+			   MAC2STR(peer->addr), errno);
+	}
+}
+
+
+static void auth_set_eapol(void *ctx, const u8 *addr,
+				       wpa_eapol_variable var, int value)
+{
+	struct ibss_rsn *ibss_rsn = ctx;
+	struct ibss_rsn_peer *peer = ibss_rsn_get_peer(ibss_rsn, addr);
+
+	if (peer == NULL)
+		return;
+
+	switch (var) {
+	case WPA_EAPOL_authorized:
+		ibss_set_sta_authorized(ibss_rsn, peer, value);
+		break;
+	default:
+		/* do not handle any other event */
+		wpa_printf(MSG_DEBUG, "AUTH: eapol event not handled %d", var);
+		break;
+	}
+}
+
+
+static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
+				    const u8 *own_addr)
+{
+	struct wpa_auth_config conf;
+	struct wpa_auth_callbacks cb;
+
+	wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
+
+	os_memset(&conf, 0, sizeof(conf));
+	conf.wpa = 2;
+	conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+	conf.wpa_pairwise = WPA_CIPHER_CCMP;
+	conf.rsn_pairwise = WPA_CIPHER_CCMP;
+	conf.wpa_group = WPA_CIPHER_CCMP;
+	conf.eapol_version = 2;
+	conf.wpa_group_rekey = 600;
+
+	os_memset(&cb, 0, sizeof(cb));
+	cb.ctx = ibss_rsn;
+	cb.logger = auth_logger;
+	cb.set_eapol = auth_set_eapol;
+	cb.send_eapol = auth_send_eapol;
+	cb.get_psk = auth_get_psk;
+	cb.set_key = auth_set_key;
+	cb.for_each_sta = auth_for_each_sta;
+	cb.disconnect = ibss_rsn_disconnect;
+
+	ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb);
+	if (ibss_rsn->auth_group == NULL) {
+		wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
+		return -1;
+	}
+
+	wpa_init_keys(ibss_rsn->auth_group);
+
+	return 0;
+}
+
+
+static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
+			      struct ibss_rsn_peer *peer)
+{
+	peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr, NULL);
+	if (peer->auth == NULL) {
+		wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed");
+		return -1;
+	}
+
+	/* TODO: get peer RSN IE with Probe Request */
+	if (wpa_validate_wpa_ie(ibss_rsn->auth_group, peer->auth,
+				(u8 *) "\x30\x14\x01\x00"
+				"\x00\x0f\xac\x04"
+				"\x01\x00\x00\x0f\xac\x04"
+				"\x01\x00\x00\x0f\xac\x02"
+				"\x00\x00", 22, NULL, 0) !=
+	    WPA_IE_OK) {
+		wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
+		return -1;
+	}
+
+	if (wpa_auth_sm_event(peer->auth, WPA_ASSOC))
+		return -1;
+
+	if (wpa_auth_sta_associated(ibss_rsn->auth_group, peer->auth))
+		return -1;
+
+	return 0;
+}
+
+
+static int ibss_rsn_send_auth(struct ibss_rsn *ibss_rsn, const u8 *da, int seq)
+{
+	struct ieee80211_mgmt auth;
+	const size_t auth_length = IEEE80211_HDRLEN + sizeof(auth.u.auth);
+	struct wpa_supplicant *wpa_s = ibss_rsn->wpa_s;
+
+	if (wpa_s->driver->send_frame == NULL)
+		return -1;
+
+	os_memset(&auth, 0, sizeof(auth));
+
+	auth.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					  WLAN_FC_STYPE_AUTH);
+	os_memcpy(auth.da, da, ETH_ALEN);
+	os_memcpy(auth.sa, wpa_s->own_addr, ETH_ALEN);
+	os_memcpy(auth.bssid, wpa_s->bssid, ETH_ALEN);
+
+	auth.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN);
+	auth.u.auth.auth_transaction = host_to_le16(seq);
+	auth.u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+
+	wpa_printf(MSG_DEBUG, "RSN: IBSS TX Auth frame (SEQ %d) to " MACSTR,
+		   seq, MAC2STR(da));
+
+	return wpa_s->driver->send_frame(wpa_s->drv_priv, (u8 *) &auth,
+					 auth_length, 0);
+}
+
+
+static int ibss_rsn_is_auth_started(struct ibss_rsn_peer * peer)
+{
+	return peer->authentication_status &
+	       (IBSS_RSN_AUTH_BY_US | IBSS_RSN_AUTH_EAPOL_BY_US);
+}
+
+
+static struct ibss_rsn_peer *
+ibss_rsn_peer_init(struct ibss_rsn *ibss_rsn, const u8 *addr)
+{
+	struct ibss_rsn_peer *peer;
+	if (ibss_rsn == NULL)
+		return NULL;
+
+	peer = ibss_rsn_get_peer(ibss_rsn, addr);
+	if (peer) {
+		wpa_printf(MSG_DEBUG, "RSN: IBSS Supplicant for peer "MACSTR
+			   " already running", MAC2STR(addr));
+		return peer;
+	}
+
+	wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Supplicant for peer "MACSTR,
+		   MAC2STR(addr));
+
+	peer = os_zalloc(sizeof(*peer));
+	if (peer == NULL) {
+		wpa_printf(MSG_DEBUG, "RSN: Could not allocate memory.");
+		return NULL;
+	}
+
+	peer->ibss_rsn = ibss_rsn;
+	os_memcpy(peer->addr, addr, ETH_ALEN);
+	peer->authentication_status = IBSS_RSN_AUTH_NOT_AUTHENTICATED;
+
+	if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr,
+			       ibss_rsn->psk) < 0) {
+		ibss_rsn_free(peer);
+		return NULL;
+	}
+
+	peer->next = ibss_rsn->peers;
+	ibss_rsn->peers = peer;
+
+	return peer;
+}
+
+
+static void ibss_rsn_auth_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct ibss_rsn_peer *peer = eloop_ctx;
+
+	/*
+	 * Assume peer does not support Authentication exchange or the frame was
+	 * lost somewhere - start EAPOL Authenticator.
+	 */
+	wpa_printf(MSG_DEBUG,
+		   "RSN: Timeout on waiting Authentication frame response from "
+		   MACSTR " - start authenticator", MAC2STR(peer->addr));
+
+	peer->authentication_status |= IBSS_RSN_AUTH_BY_US;
+	ibss_rsn_auth_init(peer->ibss_rsn, peer);
+}
+
+
+int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr)
+{
+	struct ibss_rsn_peer *peer;
+	int res;
+
+	if (!ibss_rsn)
+		return -1;
+
+	/* if the peer already exists, exit immediately */
+	peer = ibss_rsn_get_peer(ibss_rsn, addr);
+	if (peer)
+		return 0;
+
+	peer = ibss_rsn_peer_init(ibss_rsn, addr);
+	if (peer == NULL)
+		return -1;
+
+	/* Open Authentication: send first Authentication frame */
+	res = ibss_rsn_send_auth(ibss_rsn, addr, 1);
+	if (res) {
+		/*
+		 * The driver may not support Authentication frame exchange in
+		 * IBSS. Ignore authentication and go through EAPOL exchange.
+		 */
+		peer->authentication_status |= IBSS_RSN_AUTH_BY_US;
+		return ibss_rsn_auth_init(ibss_rsn, peer);
+	} else {
+		os_get_reltime(&peer->own_auth_tx);
+		eloop_register_timeout(1, 0, ibss_rsn_auth_timeout, peer, NULL);
+	}
+
+	return 0;
+}
+
+
+static int ibss_rsn_peer_authenticated(struct ibss_rsn *ibss_rsn,
+				       struct ibss_rsn_peer *peer, int reason)
+{
+	int already_started;
+
+	if (ibss_rsn == NULL || peer == NULL)
+		return -1;
+
+	already_started = ibss_rsn_is_auth_started(peer);
+	peer->authentication_status |= reason;
+
+	if (already_started) {
+		wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator already "
+			   "started for peer " MACSTR, MAC2STR(peer->addr));
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator "
+		   "for now-authenticated peer " MACSTR, MAC2STR(peer->addr));
+
+	return ibss_rsn_auth_init(ibss_rsn, peer);
+}
+
+
+void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac)
+{
+	struct ibss_rsn_peer *peer, *prev;
+
+	if (ibss_rsn == NULL)
+		return;
+
+	if (peermac == NULL) {
+		/* remove all peers */
+		wpa_printf(MSG_DEBUG, "%s: Remove all peers", __func__);
+		peer = ibss_rsn->peers;
+		while (peer) {
+			prev = peer;
+			peer = peer->next;
+			ibss_rsn_free(prev);
+			ibss_rsn->peers = peer;
+		}
+	} else {
+		/* remove specific peer */
+		wpa_printf(MSG_DEBUG, "%s: Remove specific peer " MACSTR,
+			   __func__, MAC2STR(peermac));
+
+		for (prev = NULL, peer = ibss_rsn->peers; peer != NULL;
+		     prev = peer, peer = peer->next) {
+			if (os_memcmp(peermac, peer->addr, ETH_ALEN) == 0) {
+				if (prev == NULL)
+					ibss_rsn->peers = peer->next;
+				else
+					prev->next = peer->next;
+				ibss_rsn_free(peer);
+				wpa_printf(MSG_DEBUG, "%s: Successfully "
+					   "removed a specific peer",
+					   __func__);
+				break;
+			}
+		}
+	}
+}
+
+
+struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s)
+{
+	struct ibss_rsn *ibss_rsn;
+
+	ibss_rsn = os_zalloc(sizeof(*ibss_rsn));
+	if (ibss_rsn == NULL)
+		return NULL;
+	ibss_rsn->wpa_s = wpa_s;
+
+	if (ibss_rsn_auth_init_group(ibss_rsn, wpa_s->own_addr) < 0) {
+		ibss_rsn_deinit(ibss_rsn);
+		return NULL;
+	}
+
+	return ibss_rsn;
+}
+
+
+void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn)
+{
+	struct ibss_rsn_peer *peer, *prev;
+
+	if (ibss_rsn == NULL)
+		return;
+
+	peer = ibss_rsn->peers;
+	while (peer) {
+		prev = peer;
+		peer = peer->next;
+		ibss_rsn_free(prev);
+	}
+
+	if (ibss_rsn->auth_group)
+		wpa_deinit(ibss_rsn->auth_group);
+	os_free(ibss_rsn);
+
+}
+
+
+static int ibss_rsn_eapol_dst_supp(const u8 *buf, size_t len)
+{
+	const struct ieee802_1x_hdr *hdr;
+	const struct wpa_eapol_key *key;
+	u16 key_info;
+	size_t plen;
+
+	/* TODO: Support other EAPOL packets than just EAPOL-Key */
+
+	if (len < sizeof(*hdr) + sizeof(*key))
+		return -1;
+
+	hdr = (const struct ieee802_1x_hdr *) buf;
+	key = (const struct wpa_eapol_key *) (hdr + 1);
+	plen = be_to_host16(hdr->length);
+
+	if (hdr->version < EAPOL_VERSION) {
+		/* TODO: backwards compatibility */
+	}
+	if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
+		wpa_printf(MSG_DEBUG, "RSN: EAPOL frame (type %u) discarded, "
+			"not a Key frame", hdr->type);
+		return -1;
+	}
+	if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) {
+		wpa_printf(MSG_DEBUG, "RSN: EAPOL frame payload size %lu "
+			   "invalid (frame size %lu)",
+			   (unsigned long) plen, (unsigned long) len);
+		return -1;
+	}
+
+	if (key->type != EAPOL_KEY_TYPE_RSN) {
+		wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key type (%d) unknown, "
+			   "discarded", key->type);
+		return -1;
+	}
+
+	key_info = WPA_GET_BE16(key->key_info);
+
+	return !!(key_info & WPA_KEY_INFO_ACK);
+}
+
+
+static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn,
+				     struct ibss_rsn_peer *peer,
+				     const u8 *buf, size_t len)
+{
+	int supp;
+	u8 *tmp;
+
+	supp = ibss_rsn_eapol_dst_supp(buf, len);
+	if (supp < 0)
+		return -1;
+
+	tmp = os_malloc(len);
+	if (tmp == NULL)
+		return -1;
+	os_memcpy(tmp, buf, len);
+	if (supp) {
+		peer->authentication_status |= IBSS_RSN_AUTH_EAPOL_BY_PEER;
+		wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant from "
+			   MACSTR, MAC2STR(peer->addr));
+		wpa_sm_rx_eapol(peer->supp, peer->addr, tmp, len);
+	} else {
+		if (ibss_rsn_is_auth_started(peer) == 0) {
+			wpa_printf(MSG_DEBUG, "RSN: IBSS EAPOL for "
+				   "Authenticator dropped as " MACSTR " is not "
+				   "authenticated", MAC2STR(peer->addr));
+			os_free(tmp);
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator "
+			   "from "MACSTR, MAC2STR(peer->addr));
+		wpa_receive(ibss_rsn->auth_group, peer->auth, tmp, len);
+	}
+	os_free(tmp);
+
+	return 1;
+}
+
+
+int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
+		      const u8 *buf, size_t len)
+{
+	struct ibss_rsn_peer *peer;
+
+	if (ibss_rsn == NULL)
+		return -1;
+
+	peer = ibss_rsn_get_peer(ibss_rsn, src_addr);
+	if (peer)
+		return ibss_rsn_process_rx_eapol(ibss_rsn, peer, buf, len);
+
+	if (ibss_rsn_eapol_dst_supp(buf, len) > 0) {
+		/*
+		 * Create new IBSS peer based on an EAPOL message from the peer
+		 * Authenticator.
+		 */
+		peer = ibss_rsn_peer_init(ibss_rsn, src_addr);
+		if (peer == NULL)
+			return -1;
+
+		/* assume the peer is authenticated already */
+		wpa_printf(MSG_DEBUG, "RSN: IBSS Not using IBSS Auth for peer "
+			   MACSTR, MAC2STR(src_addr));
+		ibss_rsn_peer_authenticated(ibss_rsn, peer,
+					    IBSS_RSN_AUTH_EAPOL_BY_US);
+
+		return ibss_rsn_process_rx_eapol(ibss_rsn, ibss_rsn->peers,
+						 buf, len);
+	}
+
+	return 0;
+}
+
+void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk)
+{
+	if (ibss_rsn == NULL)
+		return;
+	os_memcpy(ibss_rsn->psk, psk, PMK_LEN);
+}
+
+
+static void ibss_rsn_handle_auth_1_of_2(struct ibss_rsn *ibss_rsn,
+					struct ibss_rsn_peer *peer,
+					const u8* addr)
+{
+	wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 1) from " MACSTR,
+		   MAC2STR(addr));
+
+	if (peer &&
+	    peer->authentication_status & IBSS_RSN_AUTH_EAPOL_BY_PEER) {
+		if (peer->own_auth_tx.sec) {
+			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);
+				goto skip_reinit;
+			}
+		}
+		/*
+		 * A peer sent us an Authentication frame even though it already
+		 * started an EAPOL session. We should reinit state machines
+		 * here, but it's much more complicated than just deleting and
+		 * recreating the state machine
+		 */
+		wpa_printf(MSG_DEBUG, "RSN: IBSS Reinitializing station "
+			   MACSTR, MAC2STR(addr));
+
+		ibss_rsn_stop(ibss_rsn, addr);
+		peer = NULL;
+	}
+
+	if (!peer) {
+		peer = ibss_rsn_peer_init(ibss_rsn, addr);
+		if (!peer)
+			return;
+
+		wpa_printf(MSG_DEBUG, "RSN: IBSS Auth started by peer " MACSTR,
+			   MAC2STR(addr));
+	}
+
+skip_reinit:
+	/* reply with an Authentication frame now, before sending an EAPOL */
+	ibss_rsn_send_auth(ibss_rsn, addr, 2);
+	/* no need to start another AUTH challenge in the other way.. */
+	ibss_rsn_peer_authenticated(ibss_rsn, peer, IBSS_RSN_AUTH_EAPOL_BY_US);
+}
+
+
+void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame,
+			  size_t len)
+{
+	const struct ieee80211_mgmt *header;
+	struct ibss_rsn_peer *peer;
+	size_t auth_length;
+
+	header = (const struct ieee80211_mgmt *) auth_frame;
+	auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth);
+
+	if (ibss_rsn == NULL || len < auth_length)
+		return;
+
+	if (le_to_host16(header->u.auth.auth_alg) != WLAN_AUTH_OPEN ||
+	    le_to_host16(header->u.auth.status_code) != WLAN_STATUS_SUCCESS)
+		return;
+
+	peer = ibss_rsn_get_peer(ibss_rsn, header->sa);
+
+	switch (le_to_host16(header->u.auth.auth_transaction)) {
+	case 1:
+		ibss_rsn_handle_auth_1_of_2(ibss_rsn, peer, header->sa);
+		break;
+	case 2:
+		wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 2) from "
+			   MACSTR, MAC2STR(header->sa));
+		if (!peer) {
+			wpa_printf(MSG_DEBUG, "RSN: Received Auth seq 2 from "
+				   "unknown STA " MACSTR, MAC2STR(header->sa));
+			break;
+		}
+
+		/* authentication has been completed */
+		eloop_cancel_timeout(ibss_rsn_auth_timeout, peer, NULL);
+		wpa_printf(MSG_DEBUG, "RSN: IBSS Auth completed with " MACSTR,
+			   MAC2STR(header->sa));
+		ibss_rsn_peer_authenticated(ibss_rsn, peer,
+					    IBSS_RSN_AUTH_BY_US);
+		break;
+	}
+}
diff --git a/hostap/wpa_supplicant/ibss_rsn.h b/hostap/wpa_supplicant/ibss_rsn.h
new file mode 100644
index 0000000..67fae2d
--- /dev/null
+++ b/hostap/wpa_supplicant/ibss_rsn.h
@@ -0,0 +1,64 @@
+/*
+ * wpa_supplicant - IBSS RSN
+ * 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.
+ */
+
+#ifndef IBSS_RSN_H
+#define IBSS_RSN_H
+
+struct ibss_rsn;
+
+/* not authenticated */
+#define IBSS_RSN_AUTH_NOT_AUTHENTICATED	0x00
+/* remote peer sent an EAPOL message */
+#define IBSS_RSN_AUTH_EAPOL_BY_PEER	0x01
+/* we sent an AUTH message with seq 1 */
+#define IBSS_RSN_AUTH_BY_US		0x02
+/* we sent an EAPOL message */
+#define IBSS_RSN_AUTH_EAPOL_BY_US	0x04
+/* PTK derived as supplicant */
+#define IBSS_RSN_SET_PTK_SUPP		0x08
+/* PTK derived as authenticator */
+#define IBSS_RSN_SET_PTK_AUTH		0x10
+/* PTK completion reported */
+#define IBSS_RSN_REPORTED_PTK		0x20
+
+struct ibss_rsn_peer {
+	struct ibss_rsn_peer *next;
+	struct ibss_rsn *ibss_rsn;
+
+	u8 addr[ETH_ALEN];
+
+	struct wpa_sm *supp;
+	enum wpa_states supp_state;
+	u8 supp_ie[80];
+	size_t supp_ie_len;
+
+	struct wpa_state_machine *auth;
+	int authentication_status;
+
+	struct os_reltime own_auth_tx;
+};
+
+struct ibss_rsn {
+	struct wpa_supplicant *wpa_s;
+	struct wpa_authenticator *auth_group;
+	struct ibss_rsn_peer *peers;
+	u8 psk[PMK_LEN];
+};
+
+
+struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s);
+void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn);
+int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr);
+void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac);
+int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
+		      const u8 *buf, size_t len);
+void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk);
+void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame,
+			  size_t len);
+
+#endif /* IBSS_RSN_H */
diff --git a/hostap/wpa_supplicant/interworking.c b/hostap/wpa_supplicant/interworking.c
new file mode 100644
index 0000000..fd47c17
--- /dev/null
+++ b/hostap/wpa_supplicant/interworking.c
@@ -0,0 +1,3057 @@
+/*
+ * Interworking (IEEE 802.11u)
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "utils/pcsc_funcs.h"
+#include "utils/eloop.h"
+#include "drivers/driver.h"
+#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"
+
+
+#if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC)
+#define INTERWORKING_3GPP
+#else
+#if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC)
+#define INTERWORKING_3GPP
+#else
+#if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC)
+#define INTERWORKING_3GPP
+#endif
+#endif
+#endif
+
+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, int ignore_bw,
+	int *excluded);
+static struct wpa_cred * interworking_credentials_available_3gpp(
+	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)
+{
+	unsigned int tried;
+
+	if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_s->own_disconnect_req = 1;
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+	}
+	wpa_s->disconnected = 0;
+	wpa_s->reassociate = 1;
+	tried = wpa_s->interworking_fast_assoc_tried;
+	wpa_s->interworking_fast_assoc_tried = 1;
+
+	if (!tried && wpa_supplicant_fast_associate(wpa_s) >= 0)
+		return;
+
+	wpa_s->interworking_fast_assoc_tried = 0;
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
+				      struct wpabuf *extra)
+{
+	struct wpabuf *buf;
+	size_t i;
+	u8 *len_pos;
+
+	buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
+					 (extra ? wpabuf_len(extra) : 0));
+	if (buf == NULL)
+		return NULL;
+
+	len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
+	for (i = 0; i < num_ids; i++)
+		wpabuf_put_le16(buf, info_ids[i]);
+	gas_anqp_set_element_len(buf, len_pos);
+	if (extra)
+		wpabuf_put_buf(buf, extra);
+
+	gas_anqp_set_len(buf);
+
+	return buf;
+}
+
+
+static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
+				      u8 dialog_token,
+				      enum gas_query_result result,
+				      const struct wpabuf *adv_proto,
+				      const struct wpabuf *resp,
+				      u16 status_code)
+{
+	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);
+}
+
+
+static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_cred *cred;
+
+	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;
+}
+
+
+static int cred_with_3gpp(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_cred *cred;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->pcsc || cred->imsi)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int cred_with_nai_realm(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_cred *cred;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->pcsc || cred->imsi)
+			continue;
+		if (!cred->eap_method)
+			return 1;
+		if (cred->realm && cred->roaming_consortium_len == 0)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int cred_with_domain(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_cred *cred;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		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;
+	ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+	if (ie == NULL || ie[1] == 0)
+		return 0;
+	return ie[2]; /* Number of ANQP OIs */
+}
+
+
+static void interworking_continue_anqp(void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	interworking_next_anqp_fetch(wpa_s);
+}
+
+
+static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
+				      struct wpa_bss *bss)
+{
+	struct wpabuf *buf;
+	int ret = 0;
+	int res;
+	u16 info_ids[8];
+	size_t num_info_ids = 0;
+	struct wpabuf *extra = NULL;
+	int all = wpa_s->fetch_all_anqp;
+
+	wpa_msg(wpa_s, MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
+		MAC2STR(bss->bssid));
+	wpa_s->interworking_gas_bss = bss;
+
+	info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST;
+	if (all) {
+		info_ids[num_info_ids++] = ANQP_VENUE_NAME;
+		info_ids[num_info_ids++] = ANQP_NETWORK_AUTH_TYPE;
+	}
+	if (all || (cred_with_roaming_consortium(wpa_s) &&
+		    additional_roaming_consortiums(bss)))
+		info_ids[num_info_ids++] = ANQP_ROAMING_CONSORTIUM;
+	if (all)
+		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)) {
+		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",
+		    (u8 *) info_ids, num_info_ids * 2);
+
+#ifdef CONFIG_HS20
+	if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
+		u8 *len_pos;
+
+		extra = wpabuf_alloc(100);
+		if (!extra)
+			return -1;
+
+		len_pos = gas_anqp_add_element(extra, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(extra, OUI_WFA);
+		wpabuf_put_u8(extra, HS20_ANQP_OUI_TYPE);
+		wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
+		wpabuf_put_u8(extra, 0); /* Reserved */
+		wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
+		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 */
+
+	buf = anqp_build_req(info_ids, num_info_ids, extra);
+	wpabuf_free(extra);
+	if (buf == NULL)
+		return -1;
+
+	res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
+			    interworking_anqp_resp_cb, wpa_s);
+	if (res < 0) {
+		wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
+		wpabuf_free(buf);
+		ret = -1;
+		eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s,
+				       NULL);
+	} else
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"ANQP: Query started with dialog token %u", res);
+
+	return ret;
+}
+
+
+struct nai_realm_eap {
+	u8 method;
+	u8 inner_method;
+	enum nai_realm_eap_auth_inner_non_eap inner_non_eap;
+	u8 cred_type;
+	u8 tunneled_cred_type;
+};
+
+struct nai_realm {
+	u8 encoding;
+	char *realm;
+	u8 eap_count;
+	struct nai_realm_eap *eap;
+};
+
+
+static void nai_realm_free(struct nai_realm *realms, u16 count)
+{
+	u16 i;
+
+	if (realms == NULL)
+		return;
+	for (i = 0; i < count; i++) {
+		os_free(realms[i].eap);
+		os_free(realms[i].realm);
+	}
+	os_free(realms);
+}
+
+
+static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos,
+				      const u8 *end)
+{
+	u8 elen, auth_count, a;
+	const u8 *e_end;
+
+	if (pos + 3 > end) {
+		wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
+		return NULL;
+	}
+
+	elen = *pos++;
+	if (pos + elen > end || elen < 2) {
+		wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
+		return NULL;
+	}
+	e_end = pos + elen;
+	e->method = *pos++;
+	auth_count = *pos++;
+	wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u",
+		   elen, e->method, auth_count);
+
+	for (a = 0; a < auth_count; a++) {
+		u8 id, len;
+
+		if (pos + 2 > end || pos + 2 + pos[1] > end) {
+			wpa_printf(MSG_DEBUG, "No room for Authentication "
+				   "Parameter subfield");
+			return NULL;
+		}
+
+		id = *pos++;
+		len = *pos++;
+
+		switch (id) {
+		case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
+			if (len < 1)
+				break;
+			e->inner_non_eap = *pos;
+			if (e->method != EAP_TYPE_TTLS)
+				break;
+			switch (*pos) {
+			case NAI_REALM_INNER_NON_EAP_PAP:
+				wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP");
+				break;
+			case NAI_REALM_INNER_NON_EAP_CHAP:
+				wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP");
+				break;
+			case NAI_REALM_INNER_NON_EAP_MSCHAP:
+				wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP");
+				break;
+			case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
+				wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
+				break;
+			}
+			break;
+		case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
+			if (len < 1)
+				break;
+			e->inner_method = *pos;
+			wpa_printf(MSG_DEBUG, "Inner EAP method: %u",
+				   e->inner_method);
+			break;
+		case NAI_REALM_EAP_AUTH_CRED_TYPE:
+			if (len < 1)
+				break;
+			e->cred_type = *pos;
+			wpa_printf(MSG_DEBUG, "Credential Type: %u",
+				   e->cred_type);
+			break;
+		case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE:
+			if (len < 1)
+				break;
+			e->tunneled_cred_type = *pos;
+			wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential "
+				   "Type: %u", e->tunneled_cred_type);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "Unsupported Authentication "
+				   "Parameter: id=%u len=%u", id, len);
+			wpa_hexdump(MSG_DEBUG, "Authentication Parameter "
+				    "Value", pos, len);
+			break;
+		}
+
+		pos += len;
+	}
+
+	return e_end;
+}
+
+
+static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos,
+					const u8 *end)
+{
+	u16 len;
+	const u8 *f_end;
+	u8 realm_len, e;
+
+	if (end - pos < 4) {
+		wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
+			   "fixed fields");
+		return NULL;
+	}
+
+	len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
+	pos += 2;
+	if (pos + len > end || len < 3) {
+		wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
+			   "(len=%u; left=%u)",
+			   len, (unsigned int) (end - pos));
+		return NULL;
+	}
+	f_end = pos + len;
+
+	r->encoding = *pos++;
+	realm_len = *pos++;
+	if (pos + realm_len > f_end) {
+		wpa_printf(MSG_DEBUG, "No room for NAI Realm "
+			   "(len=%u; left=%u)",
+			   realm_len, (unsigned int) (f_end - pos));
+		return NULL;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
+	r->realm = dup_binstr(pos, realm_len);
+	if (r->realm == NULL)
+		return NULL;
+	pos += realm_len;
+
+	if (pos + 1 > f_end) {
+		wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
+		return NULL;
+	}
+	r->eap_count = *pos++;
+	wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
+	if (pos + r->eap_count * 3 > f_end) {
+		wpa_printf(MSG_DEBUG, "No room for EAP Methods");
+		return NULL;
+	}
+	r->eap = os_calloc(r->eap_count, sizeof(struct nai_realm_eap));
+	if (r->eap == NULL)
+		return NULL;
+
+	for (e = 0; e < r->eap_count; e++) {
+		pos = nai_realm_parse_eap(&r->eap[e], pos, f_end);
+		if (pos == NULL)
+			return NULL;
+	}
+
+	return f_end;
+}
+
+
+static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count)
+{
+	struct nai_realm *realm;
+	const u8 *pos, *end;
+	u16 i, num;
+	size_t left;
+
+	if (anqp == NULL)
+		return NULL;
+	left = wpabuf_len(anqp);
+	if (left < 2)
+		return NULL;
+
+	pos = wpabuf_head_u8(anqp);
+	end = pos + left;
+	num = WPA_GET_LE16(pos);
+	wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
+	pos += 2;
+	left -= 2;
+
+	if (num > left / 5) {
+		wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
+			   "enough data (%u octets) for that many realms",
+			   num, (unsigned int) left);
+		return NULL;
+	}
+
+	realm = os_calloc(num, sizeof(struct nai_realm));
+	if (realm == NULL)
+		return NULL;
+
+	for (i = 0; i < num; i++) {
+		pos = nai_realm_parse_realm(&realm[i], pos, end);
+		if (pos == NULL) {
+			nai_realm_free(realm, num);
+			return NULL;
+		}
+	}
+
+	*count = num;
+	return realm;
+}
+
+
+static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
+{
+	char *tmp, *pos, *end;
+	int match = 0;
+
+	if (realm->realm == NULL || home_realm == NULL)
+		return 0;
+
+	if (os_strchr(realm->realm, ';') == NULL)
+		return os_strcasecmp(realm->realm, home_realm) == 0;
+
+	tmp = os_strdup(realm->realm);
+	if (tmp == NULL)
+		return 0;
+
+	pos = tmp;
+	while (*pos) {
+		end = os_strchr(pos, ';');
+		if (end)
+			*end = '\0';
+		if (os_strcasecmp(pos, home_realm) == 0) {
+			match = 1;
+			break;
+		}
+		if (end == NULL)
+			break;
+		pos = end + 1;
+	}
+
+	os_free(tmp);
+
+	return match;
+}
+
+
+static int nai_realm_cred_username(struct wpa_supplicant *wpa_s,
+				   struct nai_realm_eap *eap)
+{
+	if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"nai-realm-cred-username: EAP method not supported: %d",
+			eap->method);
+		return 0; /* method not supported */
+	}
+
+	if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP &&
+	    eap->method != EAP_TYPE_FAST) {
+		/* Only tunneled methods with username/password supported */
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"nai-realm-cred-username: Method: %d is not TTLS, PEAP, or FAST",
+			eap->method);
+		return 0;
+	}
+
+	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) {
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"nai-realm-cred-username: PEAP/FAST: Inner method not supported: %d",
+				eap->inner_method);
+			return 0;
+		}
+		if (!eap->inner_method &&
+		    eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL) {
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"nai-realm-cred-username: MSCHAPv2 not supported");
+			return 0;
+		}
+	}
+
+	if (eap->method == EAP_TYPE_TTLS) {
+		if (eap->inner_method == 0 && eap->inner_non_eap == 0)
+			return 1; /* Assume TTLS/MSCHAPv2 is used */
+		if (eap->inner_method &&
+		    eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) {
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"nai-realm-cred-username: TTLS, but inner not supported: %d",
+				eap->inner_method);
+			return 0;
+		}
+		if (eap->inner_non_eap &&
+		    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
+		    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
+		    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
+		    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2) {
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"nai-realm-cred-username: TTLS, inner-non-eap not supported: %d",
+				eap->inner_non_eap);
+			return 0;
+		}
+	}
+
+	if (eap->inner_method &&
+	    eap->inner_method != EAP_TYPE_GTC &&
+	    eap->inner_method != EAP_TYPE_MSCHAPV2) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"nai-realm-cred-username: inner-method not GTC or MSCHAPv2: %d",
+			eap->inner_method);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static int nai_realm_cred_cert(struct wpa_supplicant *wpa_s,
+			       struct nai_realm_eap *eap)
+{
+	if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"nai-realm-cred-cert: Method not supported: %d",
+			eap->method);
+		return 0; /* method not supported */
+	}
+
+	if (eap->method != EAP_TYPE_TLS) {
+		/* Only EAP-TLS supported for credential authentication */
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"nai-realm-cred-cert: Method not TLS: %d",
+			eap->method);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s,
+						 struct wpa_cred *cred,
+						 struct nai_realm *realm)
+{
+	u8 e;
+
+	if (cred->username == NULL ||
+	    cred->username[0] == '\0' ||
+	    ((cred->password == NULL ||
+	      cred->password[0] == '\0') &&
+	     (cred->private_key == NULL ||
+	      cred->private_key[0] == '\0'))) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"nai-realm-find-eap: incomplete cred info: username: %s  password: %s private_key: %s",
+			cred->username ? cred->username : "NULL",
+			cred->password ? cred->password : "NULL",
+			cred->private_key ? cred->private_key : "NULL");
+		return NULL;
+	}
+
+	for (e = 0; e < realm->eap_count; e++) {
+		struct nai_realm_eap *eap = &realm->eap[e];
+		if (cred->password && cred->password[0] &&
+		    nai_realm_cred_username(wpa_s, eap))
+			return eap;
+		if (cred->private_key && cred->private_key[0] &&
+		    nai_realm_cred_cert(wpa_s, eap))
+			return eap;
+	}
+
+	return NULL;
+}
+
+
+#ifdef INTERWORKING_3GPP
+
+static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
+{
+	u8 plmn[3], plmn2[3];
+	const u8 *pos, *end;
+	u8 udhl;
+
+	/*
+	 * See Annex A of 3GPP TS 24.234 v8.1.0 for description. The network
+	 * operator is allowed to include only two digits of the MNC, so allow
+	 * matches based on both two and three digit MNC assumptions. Since some
+	 * SIM/USIM cards may not expose MNC length conveniently, we may be
+	 * provided the default MNC length 3 here and as such, checking with MNC
+	 * length 2 is justifiable even though 3GPP TS 24.234 does not mention
+	 * that case. Anyway, MCC/MNC pair where both 2 and 3 digit MNC is used
+	 * with otherwise matching values would not be good idea in general, so
+	 * this should not result in selecting incorrect networks.
+	 */
+	/* Match with 3 digit MNC */
+	plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
+	plmn[1] = (imsi[2] - '0') | ((imsi[5] - '0') << 4);
+	plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
+	/* Match with 2 digit MNC */
+	plmn2[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
+	plmn2[1] = (imsi[2] - '0') | 0xf0;
+	plmn2[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
+
+	if (anqp == NULL)
+		return 0;
+	pos = wpabuf_head_u8(anqp);
+	end = pos + wpabuf_len(anqp);
+	if (pos + 2 > end)
+		return 0;
+	if (*pos != 0) {
+		wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
+		return 0;
+	}
+	pos++;
+	udhl = *pos++;
+	if (pos + udhl > end) {
+		wpa_printf(MSG_DEBUG, "Invalid UDHL");
+		return 0;
+	}
+	end = pos + udhl;
+
+	wpa_printf(MSG_DEBUG, "Interworking: Matching against MCC/MNC alternatives: %02x:%02x:%02x or %02x:%02x:%02x (IMSI %s, MNC length %d)",
+		   plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2],
+		   imsi, mnc_len);
+
+	while (pos + 2 <= end) {
+		u8 iei, len;
+		const u8 *l_end;
+		iei = *pos++;
+		len = *pos++ & 0x7f;
+		if (pos + len > end)
+			break;
+		l_end = pos + len;
+
+		if (iei == 0 && len > 0) {
+			/* PLMN List */
+			u8 num, i;
+			wpa_hexdump(MSG_DEBUG, "Interworking: PLMN List information element",
+				    pos, len);
+			num = *pos++;
+			for (i = 0; i < num; i++) {
+				if (pos + 3 > l_end)
+					break;
+				if (os_memcmp(pos, plmn, 3) == 0 ||
+				    os_memcmp(pos, plmn2, 3) == 0)
+					return 1; /* Found matching PLMN */
+				pos += 3;
+			}
+		} else {
+			wpa_hexdump(MSG_DEBUG, "Interworking: Unrecognized 3GPP information element",
+				    pos, len);
+		}
+
+		pos = l_end;
+	}
+
+	return 0;
+}
+
+
+static int build_root_nai(char *nai, size_t nai_len, const char *imsi,
+			  size_t mnc_len, char prefix)
+{
+	const char *sep, *msin;
+	char *end, *pos;
+	size_t msin_len, plmn_len;
+
+	/*
+	 * TS 23.003, Clause 14 (3GPP to WLAN Interworking)
+	 * Root NAI:
+	 * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org
+	 * <MNC> is zero-padded to three digits in case two-digit MNC is used
+	 */
+
+	if (imsi == NULL || os_strlen(imsi) > 16) {
+		wpa_printf(MSG_DEBUG, "No valid IMSI available");
+		return -1;
+	}
+	sep = os_strchr(imsi, '-');
+	if (sep) {
+		plmn_len = sep - imsi;
+		msin = sep + 1;
+	} else if (mnc_len && os_strlen(imsi) >= 3 + mnc_len) {
+		plmn_len = 3 + mnc_len;
+		msin = imsi + plmn_len;
+	} else
+		return -1;
+	if (plmn_len != 5 && plmn_len != 6)
+		return -1;
+	msin_len = os_strlen(msin);
+
+	pos = nai;
+	end = nai + nai_len;
+	if (prefix)
+		*pos++ = prefix;
+	os_memcpy(pos, imsi, plmn_len);
+	pos += plmn_len;
+	os_memcpy(pos, msin, msin_len);
+	pos += msin_len;
+	pos += os_snprintf(pos, end - pos, "@wlan.mnc");
+	if (plmn_len == 5) {
+		*pos++ = '0';
+		*pos++ = imsi[3];
+		*pos++ = imsi[4];
+	} else {
+		*pos++ = imsi[3];
+		*pos++ = imsi[4];
+		*pos++ = imsi[5];
+	}
+	os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
+		    imsi[0], imsi[1], imsi[2]);
+
+	return 0;
+}
+
+
+static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
+{
+	char nai[100];
+	if (build_root_nai(nai, sizeof(nai), imsi, 0, prefix) < 0)
+		return -1;
+	return wpa_config_set_quoted(ssid, "identity", nai);
+}
+
+#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_s->own_disconnect_req = 1;
+		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)
+{
+	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;
+	if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0)
+		return -1;
+	return 0;
+}
+
+
+static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
+				     struct wpa_cred *cred,
+				     struct wpa_bss *bss, int only_add)
+{
+#ifdef INTERWORKING_3GPP
+	struct wpa_ssid *ssid;
+	int eap_type;
+	int res;
+	char prefix;
+
+	if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
+		return -1;
+
+	wpa_msg(wpa_s, 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 wpa_s->current_ssid->id;
+	}
+
+	remove_duplicate_network(wpa_s, cred, bss);
+
+	ssid = wpa_config_add_network(wpa_s->conf);
+	if (ssid == NULL)
+		return -1;
+	ssid->parent_cred = cred;
+
+	wpas_notify_network_added(wpa_s, ssid);
+	wpa_config_set_network_defaults(ssid);
+	ssid->priority = cred->priority;
+	ssid->temporary = 1;
+	ssid->ssid = os_zalloc(bss->ssid_len + 1);
+	if (ssid->ssid == NULL)
+		goto fail;
+	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;
+
+	eap_type = EAP_TYPE_SIM;
+	if (cred->pcsc && wpa_s->scard && scard_supports_umts(wpa_s->scard))
+		eap_type = EAP_TYPE_AKA;
+	if (cred->eap_method && cred->eap_method[0].vendor == EAP_VENDOR_IETF) {
+		if (cred->eap_method[0].method == EAP_TYPE_SIM ||
+		    cred->eap_method[0].method == EAP_TYPE_AKA ||
+		    cred->eap_method[0].method == EAP_TYPE_AKA_PRIME)
+			eap_type = cred->eap_method[0].method;
+	}
+
+	switch (eap_type) {
+	case EAP_TYPE_SIM:
+		prefix = '1';
+		res = wpa_config_set(ssid, "eap", "SIM", 0);
+		break;
+	case EAP_TYPE_AKA:
+		prefix = '0';
+		res = wpa_config_set(ssid, "eap", "AKA", 0);
+		break;
+	case EAP_TYPE_AKA_PRIME:
+		prefix = '6';
+		res = wpa_config_set(ssid, "eap", "AKA'", 0);
+		break;
+	default:
+		res = -1;
+		break;
+	}
+	if (res < 0) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Selected EAP method (%d) not supported", eap_type);
+		goto fail;
+	}
+
+	if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) {
+		wpa_msg(wpa_s, MSG_DEBUG, "Failed to set Root NAI");
+		goto fail;
+	}
+
+	if (cred->milenage && cred->milenage[0]) {
+		if (wpa_config_set_quoted(ssid, "password",
+					  cred->milenage) < 0)
+			goto fail;
+	} else if (cred->pcsc) {
+		if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
+			goto fail;
+		if (wpa_s->conf->pcsc_pin &&
+		    wpa_config_set_quoted(ssid, "pin", wpa_s->conf->pcsc_pin)
+		    < 0)
+			goto fail;
+	}
+
+	wpa_s->next_ssid = ssid;
+	wpa_config_update_prio_list(wpa_s->conf);
+	if (!only_add)
+		interworking_reconnect(wpa_s);
+
+	return ssid->id;
+
+fail:
+	wpas_notify_network_removed(wpa_s, ssid);
+	wpa_config_remove_network(wpa_s->conf, ssid->id);
+#endif /* INTERWORKING_3GPP */
+	return -1;
+}
+
+
+static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id,
+					    size_t rc_len)
+{
+	const u8 *pos, *end;
+	u8 lens;
+
+	if (ie == NULL)
+		return 0;
+
+	pos = ie + 2;
+	end = ie + 2 + ie[1];
+
+	/* Roaming Consortium element:
+	 * Number of ANQP OIs
+	 * OI #1 and #2 lengths
+	 * OI #1, [OI #2], [OI #3]
+	 */
+
+	if (pos + 2 > end)
+		return 0;
+
+	pos++; /* skip Number of ANQP OIs */
+	lens = *pos++;
+	if (pos + (lens & 0x0f) + (lens >> 4) > end)
+		return 0;
+
+	if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+		return 1;
+	pos += lens & 0x0f;
+
+	if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+		return 1;
+	pos += lens >> 4;
+
+	if (pos < end && (size_t) (end - pos) == rc_len &&
+	    os_memcmp(pos, rc_id, rc_len) == 0)
+		return 1;
+
+	return 0;
+}
+
+
+static int roaming_consortium_anqp_match(const struct wpabuf *anqp,
+					 const u8 *rc_id, size_t rc_len)
+{
+	const u8 *pos, *end;
+	u8 len;
+
+	if (anqp == NULL)
+		return 0;
+
+	pos = wpabuf_head(anqp);
+	end = pos + wpabuf_len(anqp);
+
+	/* Set of <OI Length, OI> duples */
+	while (pos < end) {
+		len = *pos++;
+		if (pos + len > end)
+			break;
+		if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+			return 1;
+		pos += len;
+	}
+
+	return 0;
+}
+
+
+static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp,
+				    const u8 *rc_id, size_t rc_len)
+{
+	return roaming_consortium_element_match(ie, rc_id, rc_len) ||
+		roaming_consortium_anqp_match(anqp, rc_id, rc_len);
+}
+
+
+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;
+
+	if (!cred->excluded_ssid)
+		return 0;
+
+	for (i = 0; i < cred->num_excluded_ssid; i++) {
+		struct excluded_ssid *e = &cred->excluded_ssid[i];
+		if (bss->ssid_len == e->ssid_len &&
+		    os_memcmp(bss->ssid, e->ssid, e->ssid_len) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+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, 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);
+
+	if (ie == NULL &&
+	    (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
+		return NULL;
+
+	if (wpa_s->conf->cred == NULL)
+		return NULL;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->roaming_consortium_len == 0)
+			continue;
+
+		if (!roaming_consortium_match(ie,
+					      bss->anqp ?
+					      bss->anqp->roaming_consortium :
+					      NULL,
+					      cred->roaming_consortium,
+					      cred->roaming_consortium_len))
+			continue;
+
+		if (cred_no_required_oi_match(cred, bss))
+			continue;
+		if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss))
+			continue;
+		if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
+			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;
+}
+
+
+static int interworking_set_eap_params(struct wpa_ssid *ssid,
+				       struct wpa_cred *cred, int ttls)
+{
+	if (cred->eap_method) {
+		ttls = cred->eap_method->vendor == EAP_VENDOR_IETF &&
+			cred->eap_method->method == EAP_TYPE_TTLS;
+
+		os_free(ssid->eap.eap_methods);
+		ssid->eap.eap_methods =
+			os_malloc(sizeof(struct eap_method_type) * 2);
+		if (ssid->eap.eap_methods == NULL)
+			return -1;
+		os_memcpy(ssid->eap.eap_methods, cred->eap_method,
+			  sizeof(*cred->eap_method));
+		ssid->eap.eap_methods[1].vendor = EAP_VENDOR_IETF;
+		ssid->eap.eap_methods[1].method = EAP_TYPE_NONE;
+	}
+
+	if (ttls && cred->username && cred->username[0]) {
+		const char *pos;
+		char *anon;
+		/* Use anonymous NAI in Phase 1 */
+		pos = os_strchr(cred->username, '@');
+		if (pos) {
+			size_t buflen = 9 + os_strlen(pos) + 1;
+			anon = os_malloc(buflen);
+			if (anon == NULL)
+				return -1;
+			os_snprintf(anon, buflen, "anonymous%s", pos);
+		} else if (cred->realm) {
+			size_t buflen = 10 + os_strlen(cred->realm) + 1;
+			anon = os_malloc(buflen);
+			if (anon == NULL)
+				return -1;
+			os_snprintf(anon, buflen, "anonymous@%s", cred->realm);
+		} else {
+			anon = os_strdup("anonymous");
+			if (anon == NULL)
+				return -1;
+		}
+		if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) <
+		    0) {
+			os_free(anon);
+			return -1;
+		}
+		os_free(anon);
+	}
+
+	if (cred->username && cred->username[0] &&
+	    wpa_config_set_quoted(ssid, "identity", cred->username) < 0)
+		return -1;
+
+	if (cred->password && cred->password[0]) {
+		if (cred->ext_password &&
+		    wpa_config_set(ssid, "password", cred->password, 0) < 0)
+			return -1;
+		if (!cred->ext_password &&
+		    wpa_config_set_quoted(ssid, "password", cred->password) <
+		    0)
+			return -1;
+	}
+
+	if (cred->client_cert && cred->client_cert[0] &&
+	    wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0)
+		return -1;
+
+#ifdef ANDROID
+	if (cred->private_key &&
+	    os_strncmp(cred->private_key, "keystore://", 11) == 0) {
+		/* Use OpenSSL engine configuration for Android keystore */
+		if (wpa_config_set_quoted(ssid, "engine_id", "keystore") < 0 ||
+		    wpa_config_set_quoted(ssid, "key_id",
+					  cred->private_key + 11) < 0 ||
+		    wpa_config_set(ssid, "engine", "1", 0) < 0)
+			return -1;
+	} else
+#endif /* ANDROID */
+	if (cred->private_key && cred->private_key[0] &&
+	    wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0)
+		return -1;
+
+	if (cred->private_key_passwd && cred->private_key_passwd[0] &&
+	    wpa_config_set_quoted(ssid, "private_key_passwd",
+				  cred->private_key_passwd) < 0)
+		return -1;
+
+	if (cred->phase1) {
+		os_free(ssid->eap.phase1);
+		ssid->eap.phase1 = os_strdup(cred->phase1);
+	}
+	if (cred->phase2) {
+		os_free(ssid->eap.phase2);
+		ssid->eap.phase2 = os_strdup(cred->phase2);
+	}
+
+	if (cred->ca_cert && cred->ca_cert[0] &&
+	    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, int only_add)
+{
+	struct wpa_ssid *ssid;
+
+	wpa_msg(wpa_s, 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 wpa_s->current_ssid->id;
+	}
+
+	remove_duplicate_network(wpa_s, cred, bss);
+
+	ssid = wpa_config_add_network(wpa_s->conf);
+	if (ssid == NULL)
+		return -1;
+	ssid->parent_cred = cred;
+	wpas_notify_network_added(wpa_s, ssid);
+	wpa_config_set_network_defaults(ssid);
+	ssid->priority = cred->priority;
+	ssid->temporary = 1;
+	ssid->ssid = os_zalloc(bss->ssid_len + 1);
+	if (ssid->ssid == NULL)
+		goto fail;
+	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;
+
+	if (cred->eap_method == NULL) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: No EAP method set for credential using roaming consortium");
+		goto fail;
+	}
+
+	if (interworking_set_eap_params(
+		    ssid, cred,
+		    cred->eap_method->vendor == EAP_VENDOR_IETF &&
+		    cred->eap_method->method == EAP_TYPE_TTLS) < 0)
+		goto fail;
+
+	wpa_s->next_ssid = ssid;
+	wpa_config_update_prio_list(wpa_s->conf);
+	if (!only_add)
+		interworking_reconnect(wpa_s);
+
+	return ssid->id;
+
+fail:
+	wpas_notify_network_removed(wpa_s, ssid);
+	wpa_config_remove_network(wpa_s->conf, ssid->id);
+	return -1;
+}
+
+
+static int interworking_connect_helper(struct wpa_supplicant *wpa_s,
+				       struct wpa_bss *bss, int allow_excluded,
+				       int only_add)
+{
+	struct wpa_cred *cred, *cred_rc, *cred_3gpp;
+	struct wpa_ssid *ssid;
+	struct nai_realm *realm;
+	struct nai_realm_eap *eap = NULL;
+	u16 count, i;
+	char buf[100];
+	int excluded = 0, *excl = allow_excluded ? &excluded : NULL;
+	const char *name;
+
+	if (wpa_s->conf->cred == NULL || bss == NULL)
+		return -1;
+	if (disallowed_bssid(wpa_s, bss->bssid) ||
+	    disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
+		wpa_msg(wpa_s, 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
+		 * required to use WPA2-Enterprise.
+		 */
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Network does not use RSN");
+		return -1;
+	}
+
+	cred_rc = interworking_credentials_available_roaming_consortium(
+		wpa_s, bss, 0, excl);
+	if (cred_rc) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Highest roaming 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, 0, excl);
+	if (cred) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Highest NAI Realm list 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, 0,
+							    excl);
+	if (cred_3gpp) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Highest 3GPP matching 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_msg(wpa_s, 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_msg(wpa_s, 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_msg(wpa_s, 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_msg(wpa_s, 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_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, only_add);
+
+	if (cred_3gpp &&
+	    (cred == NULL || cred_prio_cmp(cred_3gpp, cred) >= 0)) {
+		return interworking_connect_3gpp(wpa_s, cred_3gpp, bss,
+						 only_add);
+	}
+
+	if (cred == NULL) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: No matching credentials found for "
+			MACSTR, MAC2STR(bss->bssid));
+		return -1;
+	}
+
+	realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL,
+				&count);
+	if (realm == NULL) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Could not parse NAI Realm list from "
+			MACSTR, MAC2STR(bss->bssid));
+		return -1;
+	}
+
+	for (i = 0; i < count; i++) {
+		if (!nai_realm_match(&realm[i], cred->realm))
+			continue;
+		eap = nai_realm_find_eap(wpa_s, cred, &realm[i]);
+		if (eap)
+			break;
+	}
+
+	if (!eap) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: No matching credentials and EAP method found for "
+			MACSTR, MAC2STR(bss->bssid));
+		nai_realm_free(realm, count);
+		return -1;
+	}
+
+	wpa_msg(wpa_s, 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);
+		return -1;
+	}
+	ssid->parent_cred = cred;
+	wpas_notify_network_added(wpa_s, ssid);
+	wpa_config_set_network_defaults(ssid);
+	ssid->priority = cred->priority;
+	ssid->temporary = 1;
+	ssid->ssid = os_zalloc(bss->ssid_len + 1);
+	if (ssid->ssid == NULL)
+		goto fail;
+	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;
+
+	if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
+						     eap->method), 0) < 0)
+		goto fail;
+
+	switch (eap->method) {
+	case EAP_TYPE_TTLS:
+		if (eap->inner_method) {
+			os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
+				    eap_get_name(EAP_VENDOR_IETF,
+						 eap->inner_method));
+			if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
+				goto fail;
+			break;
+		}
+		switch (eap->inner_non_eap) {
+		case NAI_REALM_INNER_NON_EAP_PAP:
+			if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) <
+			    0)
+				goto fail;
+			break;
+		case NAI_REALM_INNER_NON_EAP_CHAP:
+			if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0)
+			    < 0)
+				goto fail;
+			break;
+		case NAI_REALM_INNER_NON_EAP_MSCHAP:
+			if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"",
+					   0) < 0)
+				goto fail;
+			break;
+		case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
+			if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
+					   0) < 0)
+				goto fail;
+			break;
+		default:
+			/* EAP params were not set - assume TTLS/MSCHAPv2 */
+			if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
+					   0) < 0)
+				goto fail;
+			break;
+		}
+		break;
+	case EAP_TYPE_PEAP:
+	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;
+	case EAP_TYPE_TLS:
+		break;
+	}
+
+	if (interworking_set_eap_params(ssid, cred,
+					eap->method == EAP_TYPE_TTLS) < 0)
+		goto fail;
+
+	nai_realm_free(realm, count);
+
+	wpa_s->next_ssid = ssid;
+	wpa_config_update_prio_list(wpa_s->conf);
+	if (!only_add)
+		interworking_reconnect(wpa_s);
+
+	return ssid->id;
+
+fail:
+	wpas_notify_network_removed(wpa_s, ssid);
+	wpa_config_remove_network(wpa_s->conf, ssid->id);
+	nai_realm_free(realm, count);
+	return -1;
+}
+
+
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			 int only_add)
+{
+	return interworking_connect_helper(wpa_s, bss, 1, only_add);
+}
+
+
+#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, 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) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"interworking-avail-3gpp: not avail, anqp: %p  anqp_3gpp: %p",
+			bss->anqp, bss->anqp ? bss->anqp->anqp_3gpp : NULL);
+		return NULL;
+	}
+
+#ifdef CONFIG_EAP_PROXY
+	if (!wpa_s->imsi[0]) {
+		size_t len;
+		wpa_msg(wpa_s, 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_msg(wpa_s, MSG_DEBUG,
+				"eap_proxy: IMSI %s (MNC length %d)",
+				wpa_s->imsi, wpa_s->mnc_len);
+		} else {
+			wpa_msg(wpa_s, 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;
+		int mnc_len;
+		char imsi_buf[16];
+		size_t msin_len;
+
+#ifdef PCSC_FUNCS
+		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;
+		}
+#endif /* PCSC_FUNCS */
+#ifdef CONFIG_EAP_PROXY
+		if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) {
+			imsi = wpa_s->imsi;
+			mnc_len = wpa_s->mnc_len;
+			goto compare;
+		}
+#endif /* CONFIG_EAP_PROXY */
+
+		if (cred->imsi == NULL || !cred->imsi[0] ||
+		    (!wpa_s->conf->external_sim &&
+		     (cred->milenage == NULL || !cred->milenage[0])))
+			continue;
+
+		sep = os_strchr(cred->imsi, '-');
+		if (sep == NULL ||
+		    (sep - cred->imsi != 5 && sep - cred->imsi != 6))
+			continue;
+		mnc_len = sep - cred->imsi - 3;
+		os_memcpy(imsi_buf, cred->imsi, 3 + mnc_len);
+		sep++;
+		msin_len = os_strlen(cred->imsi);
+		if (3 + mnc_len + msin_len >= sizeof(imsi_buf) - 1)
+			msin_len = sizeof(imsi_buf) - 3 - mnc_len - 1;
+		os_memcpy(&imsi_buf[3 + mnc_len], sep, msin_len);
+		imsi_buf[3 + mnc_len + msin_len] = '\0';
+		imsi = imsi_buf;
+
+#if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY)
+	compare:
+#endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Parsing 3GPP info from " MACSTR,
+			MAC2STR(bss->bssid));
+		ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
+		wpa_msg(wpa_s, MSG_DEBUG, "PLMN match %sfound",
+			ret ? "" : "not ");
+		if (ret) {
+			if (cred_no_required_oi_match(cred, bss))
+				continue;
+			if (!ignore_bw &&
+			    cred_below_min_backhaul(wpa_s, cred, bss))
+				continue;
+			if (!ignore_bw &&
+			    cred_over_max_bss_load(wpa_s, cred, bss))
+				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, 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;
+
+	if (wpa_s->conf->cred == NULL)
+		return NULL;
+
+	wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
+		MACSTR, MAC2STR(bss->bssid));
+	realm = nai_realm_parse(bss->anqp->nai_realm, &count);
+	if (realm == NULL) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Could not parse NAI Realm list from "
+			MACSTR, MAC2STR(bss->bssid));
+		return NULL;
+	}
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->realm == NULL)
+			continue;
+
+		for (i = 0; i < count; i++) {
+			if (!nai_realm_match(&realm[i], cred->realm))
+				continue;
+			if (nai_realm_find_eap(wpa_s, cred, &realm[i])) {
+				if (cred_no_required_oi_match(cred, bss))
+					continue;
+				if (!ignore_bw &&
+				    cred_below_min_backhaul(wpa_s, cred, bss))
+					continue;
+				if (!ignore_bw &&
+				    cred_over_max_bss_load(wpa_s, cred, bss))
+					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;
+			} else {
+				wpa_msg(wpa_s, MSG_DEBUG,
+					"Interworking: realm-find-eap returned false");
+			}
+		}
+	}
+
+	nai_realm_free(realm, count);
+
+	if (excluded)
+		*excluded = is_excluded;
+
+	return selected;
+}
+
+
+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 = 0;
+
+	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;
+	}
+
+	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;
+		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 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;
+
+	len = os_strlen(domain);
+	pos = wpabuf_head(domain_names);
+	end = pos + wpabuf_len(domain_names);
+
+	while (pos + 1 < end) {
+		if (pos + 1 + pos[0] > end)
+			break;
+
+		wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name",
+				  pos + 1, pos[0]);
+		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];
+	}
+
+	return 0;
+}
+
+
+int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
+			      struct wpa_cred *cred,
+			      struct wpabuf *domain_names)
+{
+	size_t i;
+	int ret = -1;
+#ifdef INTERWORKING_3GPP
+	char nai[100], *realm;
+
+	char *imsi = NULL;
+	int mnc_len = 0;
+	if (cred->imsi)
+		imsi = cred->imsi;
+#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 /* 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, '@');
+		if (realm)
+			realm++;
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Search for match with SIM/USIM domain %s",
+			realm);
+		if (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 ret;
+
+	for (i = 0; i < cred->num_domain; i++) {
+		wpa_msg(wpa_s, 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;
+}
+
+
+static int interworking_home_sp(struct wpa_supplicant *wpa_s,
+				struct wpabuf *domain_names)
+{
+	struct wpa_cred *cred;
+
+	if (domain_names == NULL || wpa_s->conf->cred == NULL)
+		return -1;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		int res = interworking_home_sp_cred(wpa_s, cred, domain_names);
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+
+static int interworking_find_network_match(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss;
+	struct wpa_ssid *ssid;
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+			if (wpas_network_disabled(wpa_s, ssid) ||
+			    ssid->mode != WPAS_MODE_INFRA)
+				continue;
+			if (ssid->ssid_len != bss->ssid_len ||
+			    os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) !=
+			    0)
+				continue;
+			/*
+			 * TODO: Consider more accurate matching of security
+			 * configuration similarly to what is done in events.c
+			 */
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+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;
+	struct wpa_bss *selected2 = NULL, *selected2_home = NULL;
+	unsigned int count = 0;
+	const char *type;
+	int res;
+	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) {
+		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)) {
+			/*
+			 * We currently support only HS 2.0 networks and those
+			 * are required to use WPA2-Enterprise.
+			 */
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"Interworking: Credential match with " MACSTR
+				" but network does not use RSN",
+				MAC2STR(bss->bssid));
+			continue;
+		}
+		if (!excluded)
+			count++;
+		res = interworking_home_sp(wpa_s, bss->anqp ?
+					   bss->anqp->domain_name : NULL);
+		if (res > 0)
+			type = "home";
+		else if (res == 0)
+			type = "roaming";
+		else
+			type = "unknown";
+		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 (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_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) {
+		/*
+		 * No matching network was found based on configured
+		 * credentials. Check whether any of the enabled network blocks
+		 * have matching APs.
+		 */
+		if (interworking_find_network_match(wpa_s)) {
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"Interworking: Possible BSS match for enabled network configurations");
+			if (wpa_s->auto_select) {
+				interworking_reconnect(wpa_s);
+				return;
+			}
+		}
+
+		if (wpa_s->auto_network_select) {
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"Interworking: Continue scanning after ANQP fetch");
+			wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval,
+						0);
+			return;
+		}
+
+		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
+			"with matching credentials found");
+		if (wpa_s->wpa_state == WPA_SCANNING)
+			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+	}
+
+	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, 0);
+	}
+}
+
+
+static struct wpa_bss_anqp *
+interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+	struct wpa_bss *other;
+
+	if (is_zero_ether_addr(bss->hessid))
+		return NULL; /* Cannot be in the same homegenous ESS */
+
+	dl_list_for_each(other, &wpa_s->bss, struct wpa_bss, list) {
+		if (other == bss)
+			continue;
+		if (other->anqp == NULL)
+			continue;
+		if (other->anqp->roaming_consortium == NULL &&
+		    other->anqp->nai_realm == NULL &&
+		    other->anqp->anqp_3gpp == NULL &&
+		    other->anqp->domain_name == NULL)
+			continue;
+		if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED))
+			continue;
+		if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0)
+			continue;
+		if (bss->ssid_len != other->ssid_len ||
+		    os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0)
+			continue;
+
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Share ANQP data with already fetched BSSID "
+			MACSTR " and " MACSTR,
+			MAC2STR(other->bssid), MAC2STR(bss->bssid));
+		other->anqp->users++;
+		return other->anqp;
+	}
+
+	return NULL;
+}
+
+
+static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss;
+	int found = 0;
+	const u8 *ie;
+
+	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))
+			continue;
+		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) {
+				bss->anqp = interworking_match_anqp_info(wpa_s,
+									 bss);
+				if (bss->anqp) {
+					/* Shared data already fetched */
+					continue;
+				}
+				bss->anqp = wpa_bss_anqp_alloc();
+				if (bss->anqp == NULL)
+					break;
+			}
+			found++;
+			bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
+			wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
+				MACSTR, MAC2STR(bss->bssid));
+			interworking_anqp_send_req(wpa_s, bss);
+			break;
+		}
+	}
+
+	if (found == 0) {
+		if (wpa_s->fetch_osu_info) {
+			if (wpa_s->num_prov_found == 0 &&
+			    wpa_s->fetch_osu_waiting_scan &&
+			    wpa_s->num_osu_scans < 3) {
+				wpa_printf(MSG_DEBUG, "HS 2.0: No OSU providers seen - try to scan again");
+				hs20_start_osu_scan(wpa_s);
+				return;
+			}
+			wpa_printf(MSG_DEBUG, "Interworking: Next icon");
+			hs20_osu_icon_fetch(wpa_s);
+			return;
+		}
+		wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
+		wpa_s->fetch_anqp_in_progress = 0;
+		if (wpa_s->network_select)
+			interworking_select_network(wpa_s);
+	}
+}
+
+
+void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss;
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
+		bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
+
+	wpa_s->fetch_anqp_in_progress = 1;
+
+	/*
+	 * Start actual ANQP operation from eloop call to make sure the loop
+	 * does not end up using excessive recursion.
+	 */
+	eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s, NULL);
+}
+
+
+int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select)
+		return 0;
+
+	wpa_s->network_select = 0;
+	wpa_s->fetch_all_anqp = 1;
+	wpa_s->fetch_osu_info = 0;
+
+	interworking_start_fetch_anqp(wpa_s);
+
+	return 0;
+}
+
+
+void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->fetch_anqp_in_progress)
+		return;
+
+	wpa_s->fetch_anqp_in_progress = 0;
+}
+
+
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+		  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;
+	int res;
+
+	bss = wpa_bss_get_bssid(wpa_s, dst);
+	if (!bss) {
+		wpa_printf(MSG_WARNING,
+			   "ANQP: Cannot send query to unknown BSS "
+			   MACSTR, MAC2STR(dst));
+		return -1;
+	}
+
+	wpa_bss_anqp_unshare_alloc(bss);
+	freq = bss->freq;
+
+	wpa_msg(wpa_s, MSG_DEBUG,
+		"ANQP: Query Request to " MACSTR " for %u id(s)",
+		MAC2STR(dst), (unsigned int) num_ids);
+
+#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_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
+		wpabuf_free(buf);
+		ret = -1;
+	} else {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"ANQP: Query started with dialog token %u", res);
+	}
+
+	return ret;
+}
+
+
+static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
+					    struct wpa_bss *bss, const u8 *sa,
+					    u16 info_id,
+					    const u8 *data, size_t slen)
+{
+	const u8 *pos = data;
+	struct wpa_bss_anqp *anqp = NULL;
+#ifdef CONFIG_HS20
+	u8 type;
+#endif /* CONFIG_HS20 */
+
+	if (bss)
+		anqp = bss->anqp;
+
+	switch (info_id) {
+	case ANQP_CAPABILITY_LIST:
+		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+			" ANQP Capability list", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Capability list",
+				  pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->capability_list);
+			anqp->capability_list = wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case ANQP_VENUE_NAME:
+		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+			" Venue Name", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->venue_name);
+			anqp->venue_name = wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case ANQP_NETWORK_AUTH_TYPE:
+		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+			" Network Authentication Type information",
+			MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
+				  "Type", pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->network_auth_type);
+			anqp->network_auth_type = wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case ANQP_ROAMING_CONSORTIUM:
+		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+			" Roaming Consortium list", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
+				  pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->roaming_consortium);
+			anqp->roaming_consortium = wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case ANQP_IP_ADDR_TYPE_AVAILABILITY:
+		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+			" IP Address Type Availability information",
+			MAC2STR(sa));
+		wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
+			    pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->ip_addr_type_availability);
+			anqp->ip_addr_type_availability =
+				wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case ANQP_NAI_REALM:
+		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+			" NAI Realm list", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->nai_realm);
+			anqp->nai_realm = wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case ANQP_3GPP_CELLULAR_NETWORK:
+		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+			" 3GPP Cellular Network information", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
+				  pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->anqp_3gpp);
+			anqp->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case ANQP_DOMAIN_NAME:
+		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+			" Domain Name list", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->domain_name);
+			anqp->domain_name = wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case ANQP_VENDOR_SPECIFIC:
+		if (slen < 3)
+			return;
+
+		switch (WPA_GET_BE24(pos)) {
+#ifdef CONFIG_HS20
+		case OUI_WFA:
+			pos += 3;
+			slen -= 3;
+
+			if (slen < 1)
+				return;
+			type = *pos++;
+			slen--;
+
+			switch (type) {
+			case HS20_ANQP_OUI_TYPE:
+				hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa,
+							     pos, slen);
+				break;
+			default:
+				wpa_msg(wpa_s, MSG_DEBUG,
+					"HS20: Unsupported ANQP vendor type %u",
+					type);
+				break;
+			}
+			break;
+#endif /* CONFIG_HS20 */
+		default:
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"Interworking: Unsupported vendor-specific ANQP OUI %06x",
+				WPA_GET_BE24(pos));
+			return;
+		}
+		break;
+	default:
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Unsupported ANQP Info ID %u", info_id);
+		break;
+	}
+}
+
+
+void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
+		  enum gas_query_result result,
+		  const struct wpabuf *adv_proto,
+		  const struct wpabuf *resp, u16 status_code)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	const u8 *pos;
+	const u8 *end;
+	u16 info_id;
+	u16 slen;
+	struct wpa_bss *bss = NULL, *tmp;
+	const char *anqp_result = "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);
+		anqp_result = "FAILURE";
+		goto out;
+	}
+
+	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_msg(wpa_s, MSG_DEBUG,
+			"ANQP: Unexpected Advertisement Protocol in response");
+		if (wpa_s->fetch_osu_icon_in_progress)
+			hs20_icon_fetch_failed(wpa_s);
+		anqp_result = "INVALID_FRAME";
+		goto out;
+	}
+
+	/*
+	 * If possible, select the BSS entry based on which BSS entry was used
+	 * for the request. This can help in cases where multiple BSS entries
+	 * may exist for the same AP.
+	 */
+	dl_list_for_each_reverse(tmp, &wpa_s->bss, struct wpa_bss, list) {
+		if (tmp == wpa_s->interworking_gas_bss &&
+		    os_memcmp(tmp->bssid, dst, ETH_ALEN) == 0) {
+			bss = tmp;
+			break;
+		}
+	}
+	if (bss == NULL)
+		bss = wpa_bss_get_bssid(wpa_s, dst);
+
+	pos = wpabuf_head(resp);
+	end = pos + wpabuf_len(resp);
+
+	while (pos < end) {
+		unsigned int left = end - pos;
+
+		if (left < 4) {
+			wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Invalid element");
+			anqp_result = "INVALID_FRAME";
+			goto out_parse_done;
+		}
+		info_id = WPA_GET_LE16(pos);
+		pos += 2;
+		slen = WPA_GET_LE16(pos);
+		pos += 2;
+		left -= 4;
+		if (left < slen) {
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"ANQP: Invalid element length for Info ID %u",
+				info_id);
+			anqp_result = "INVALID_FRAME";
+			goto out_parse_done;
+		}
+		interworking_parse_rx_anqp_resp(wpa_s, bss, dst, info_id, pos,
+						slen);
+		pos += slen;
+	}
+
+out_parse_done:
+	hs20_notify_parse_done(wpa_s);
+out:
+	wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
+		MAC2STR(dst), anqp_result);
+}
+
+
+static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
+					  struct wpa_scan_results *scan_res)
+{
+	wpa_msg(wpa_s, MSG_DEBUG,
+		"Interworking: Scan results available - start ANQP fetch");
+	interworking_start_fetch_anqp(wpa_s);
+}
+
+
+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_msg(wpa_s, 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;
+}
+
+
+static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
+			enum gas_query_result result,
+			const struct wpabuf *adv_proto,
+			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",
+		MAC2STR(addr), dialog_token, status_code,
+		resp ? (int) wpabuf_len(resp) : -1);
+	if (!resp)
+		return;
+
+	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;
+}
+
+
+int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+		     const struct wpabuf *adv_proto,
+		     const struct wpabuf *query)
+{
+	struct wpabuf *buf;
+	int ret = 0;
+	int freq;
+	struct wpa_bss *bss;
+	int res;
+	size_t len;
+	u8 query_resp_len_limit = 0;
+
+	freq = wpa_s->assoc_freq;
+	bss = wpa_bss_get_bssid(wpa_s, dst);
+	if (bss)
+		freq = bss->freq;
+	if (freq <= 0)
+		return -1;
+
+	wpa_msg(wpa_s, MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)",
+		MAC2STR(dst), freq);
+	wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto);
+	wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query);
+
+	len = 3 + wpabuf_len(adv_proto) + 2;
+	if (query)
+		len += wpabuf_len(query);
+	buf = gas_build_initial_req(0, len);
+	if (buf == NULL)
+		return -1;
+
+	/* 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);
+	wpabuf_put_buf(buf, adv_proto);
+
+	/* GAS Query */
+	if (query) {
+		wpabuf_put_le16(buf, wpabuf_len(query));
+		wpabuf_put_buf(buf, query);
+	} else
+		wpabuf_put_le16(buf, 0);
+
+	res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
+	if (res < 0) {
+		wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request");
+		wpabuf_free(buf);
+		ret = -1;
+	} else
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"GAS: Query started with dialog token %u", res);
+
+	return ret;
+}
diff --git a/hostap/wpa_supplicant/interworking.h b/hostap/wpa_supplicant/interworking.h
new file mode 100644
index 0000000..3743dc0
--- /dev/null
+++ b/hostap/wpa_supplicant/interworking.h
@@ -0,0 +1,36 @@
+/*
+ * Interworking (IEEE 802.11u)
+ * Copyright (c) 2011-2012, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef INTERWORKING_H
+#define INTERWORKING_H
+
+enum gas_query_result;
+
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+		  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,
+		  const struct wpabuf *resp, u16 status_code);
+int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+		     const struct wpabuf *adv_proto,
+		     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 *freqs);
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			 int only_add);
+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/hostap/wpa_supplicant/main.c b/hostap/wpa_supplicant/main.c
new file mode 100644
index 0000000..d5d47e1
--- /dev/null
+++ b/hostap/wpa_supplicant/main.c
@@ -0,0 +1,359 @@
+/*
+ * WPA Supplicant / main() function for UNIX like OSes and MinGW
+ * 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.
+ */
+
+#include "includes.h"
+#ifdef __linux__
+#include <fcntl.h>
+#endif /* __linux__ */
+
+#include "common.h"
+#include "fst/fst.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "p2p_supplicant.h"
+
+
+static void usage(void)
+{
+	int i;
+	printf("%s\n\n%s\n"
+	       "usage:\n"
+	       "  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>] [-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"
+	       "drivers:\n",
+	       wpa_supplicant_version, wpa_supplicant_license);
+
+	for (i = 0; wpa_drivers[i]; i++) {
+		printf("  %s = %s\n",
+		       wpa_drivers[i]->name,
+		       wpa_drivers[i]->desc);
+	}
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+	printf("options:\n"
+	       "  -b = optional bridge interface name\n"
+	       "  -B = run daemon in the background\n"
+	       "  -c = Configuration file\n"
+	       "  -C = ctrl_interface parameter (only used if -c is not)\n"
+	       "  -i = interface name\n"
+	       "  -I = additional configuration file\n"
+	       "  -d = increase debugging verbosity (-dd even more)\n"
+	       "  -D = driver name (can be multiple drivers: nl80211,wext)\n"
+	       "  -e = entropy file\n");
+#ifdef CONFIG_DEBUG_FILE
+	printf("  -f = log output to debug file instead of stdout\n");
+#endif /* CONFIG_DEBUG_FILE */
+	printf("  -g = global ctrl_interface\n"
+	       "  -G = global ctrl_interface group\n"
+	       "  -K = include keys (passwords, etc.) in debug output\n");
+#ifdef CONFIG_DEBUG_SYSLOG
+	printf("  -s = log output to syslog instead of stdout\n");
+#endif /* CONFIG_DEBUG_SYSLOG */
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	printf("  -T = record to Linux tracing in addition to logging\n");
+	printf("       (records all messages regardless of debug verbosity)\n");
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+	printf("  -t = include timestamp in debug messages\n"
+	       "  -h = show this help text\n"
+	       "  -L = show license (BSD)\n"
+	       "  -o = override driver parameter for new interfaces\n"
+	       "  -O = override ctrl_interface parameter for new interfaces\n"
+	       "  -p = driver parameters\n"
+	       "  -P = PID file\n"
+	       "  -q = decrease debugging verbosity (-qq even less)\n");
+#ifdef CONFIG_DBUS
+	printf("  -u = enable DBus control interface\n");
+#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"
+	       "  wpa_supplicant -D%s -iwlan0 -c/etc/wpa_supplicant.conf\n",
+	       wpa_drivers[0] ? wpa_drivers[0]->name : "nl80211");
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+static void license(void)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+	printf("%s\n\n%s%s%s%s%s\n",
+	       wpa_supplicant_version,
+	       wpa_supplicant_full_license1,
+	       wpa_supplicant_full_license2,
+	       wpa_supplicant_full_license3,
+	       wpa_supplicant_full_license4,
+	       wpa_supplicant_full_license5);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+static void wpa_supplicant_fd_workaround(int start)
+{
+#ifdef __linux__
+	static int fd[3] = { -1, -1, -1 };
+	int i;
+	/* When started from pcmcia-cs scripts, wpa_supplicant might start with
+	 * fd 0, 1, and 2 closed. This will cause some issues because many
+	 * places in wpa_supplicant are still printing out to stdout. As a
+	 * workaround, make sure that fd's 0, 1, and 2 are not used for other
+	 * sockets. */
+	if (start) {
+		for (i = 0; i < 3; i++) {
+			fd[i] = open("/dev/null", O_RDWR);
+			if (fd[i] > 2) {
+				close(fd[i]);
+				fd[i] = -1;
+				break;
+			}
+		}
+	} else {
+		for (i = 0; i < 3; i++) {
+			if (fd[i] >= 0) {
+				close(fd[i]);
+				fd[i] = -1;
+			}
+		}
+	}
+#endif /* __linux__ */
+}
+
+
+int main(int argc, char *argv[])
+{
+	int c, i;
+	struct wpa_interface *ifaces, *iface;
+	int iface_count, exitcode = -1;
+	struct wpa_params params;
+	struct wpa_global *global;
+
+	if (os_program_init())
+		return -1;
+
+	os_memset(&params, 0, sizeof(params));
+	params.wpa_debug_level = MSG_INFO;
+
+	iface = ifaces = os_zalloc(sizeof(struct wpa_interface));
+	if (ifaces == NULL)
+		return -1;
+	iface_count = 1;
+
+	wpa_supplicant_fd_workaround(1);
+
+	for (;;) {
+		c = getopt(argc, argv,
+			   "b:Bc:C:D:de:f:g:G:hi:I:KLm:No:O:p:P:qsTtuvW");
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'b':
+			iface->bridge_ifname = optarg;
+			break;
+		case 'B':
+			params.daemonize++;
+			break;
+		case 'c':
+			iface->confname = optarg;
+			break;
+		case 'C':
+			iface->ctrl_interface = optarg;
+			break;
+		case 'D':
+			iface->driver = optarg;
+			break;
+		case 'd':
+#ifdef CONFIG_NO_STDOUT_DEBUG
+			printf("Debugging disabled with "
+			       "CONFIG_NO_STDOUT_DEBUG=y build time "
+			       "option.\n");
+			goto out;
+#else /* CONFIG_NO_STDOUT_DEBUG */
+			params.wpa_debug_level--;
+			break;
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+		case 'e':
+			params.entropy_file = optarg;
+			break;
+#ifdef CONFIG_DEBUG_FILE
+		case 'f':
+			params.wpa_debug_file_path = optarg;
+			break;
+#endif /* CONFIG_DEBUG_FILE */
+		case 'g':
+			params.ctrl_interface = optarg;
+			break;
+		case 'G':
+			params.ctrl_interface_group = optarg;
+			break;
+		case 'h':
+			usage();
+			exitcode = 0;
+			goto out;
+		case 'i':
+			iface->ifname = optarg;
+			break;
+		case 'I':
+			iface->confanother = optarg;
+			break;
+		case 'K':
+			params.wpa_debug_show_keys++;
+			break;
+		case 'L':
+			license();
+			exitcode = 0;
+			goto out;
+#ifdef CONFIG_P2P
+		case 'm':
+			params.conf_p2p_dev = optarg;
+			break;
+#endif /* CONFIG_P2P */
+		case 'o':
+			params.override_driver = optarg;
+			break;
+		case 'O':
+			params.override_ctrl_interface = optarg;
+			break;
+		case 'p':
+			iface->driver_param = optarg;
+			break;
+		case 'P':
+			os_free(params.pid_file);
+			params.pid_file = os_rel2abs_path(optarg);
+			break;
+		case 'q':
+			params.wpa_debug_level++;
+			break;
+#ifdef CONFIG_DEBUG_SYSLOG
+		case 's':
+			params.wpa_debug_syslog++;
+			break;
+#endif /* CONFIG_DEBUG_SYSLOG */
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+		case 'T':
+			params.wpa_debug_tracing++;
+			break;
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+		case 't':
+			params.wpa_debug_timestamp++;
+			break;
+#ifdef CONFIG_DBUS
+		case 'u':
+			params.dbus_ctrl_interface = 1;
+			break;
+#endif /* CONFIG_DBUS */
+		case 'v':
+			printf("%s\n", wpa_supplicant_version);
+			exitcode = 0;
+			goto out;
+		case 'W':
+			params.wait_for_monitor++;
+			break;
+		case 'N':
+			iface_count++;
+			iface = os_realloc_array(ifaces, iface_count,
+						 sizeof(struct wpa_interface));
+			if (iface == NULL)
+				goto out;
+			ifaces = iface;
+			iface = &ifaces[iface_count - 1];
+			os_memset(iface, 0, sizeof(*iface));
+			break;
+		default:
+			usage();
+			exitcode = 0;
+			goto out;
+		}
+	}
+
+	exitcode = 0;
+	global = wpa_supplicant_init(&params);
+	if (global == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant");
+		exitcode = -1;
+		goto out;
+	} else {
+		wpa_printf(MSG_INFO, "Successfully initialized "
+			   "wpa_supplicant");
+	}
+
+	if (fst_global_init()) {
+		wpa_printf(MSG_ERROR, "Failed to initialize FST");
+		exitcode = -1;
+		goto out;
+	}
+
+#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
+	if (!fst_global_add_ctrl(fst_ctrl_cli))
+		wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
+#endif
+
+	for (i = 0; exitcode == 0 && i < iface_count; i++) {
+		struct wpa_supplicant *wpa_s;
+
+		if ((ifaces[i].confname == NULL &&
+		     ifaces[i].ctrl_interface == NULL) ||
+		    ifaces[i].ifname == NULL) {
+			if (iface_count == 1 && (params.ctrl_interface ||
+						 params.dbus_ctrl_interface))
+				break;
+			usage();
+			exitcode = -1;
+			break;
+		}
+		wpa_s = wpa_supplicant_add_iface(global, &ifaces[i], NULL);
+		if (wpa_s == NULL) {
+			exitcode = -1;
+			break;
+		}
+	}
+
+	if (exitcode == 0)
+		exitcode = wpa_supplicant_run(global);
+
+	wpa_supplicant_deinit(global);
+
+	fst_global_deinit();
+
+out:
+	wpa_supplicant_fd_workaround(0);
+	os_free(ifaces);
+	os_free(params.pid_file);
+
+	os_program_deinit();
+
+	return exitcode;
+}
diff --git a/hostap/wpa_supplicant/main_none.c b/hostap/wpa_supplicant/main_none.c
new file mode 100644
index 0000000..4d3caf2
--- /dev/null
+++ b/hostap/wpa_supplicant/main_none.c
@@ -0,0 +1,40 @@
+/*
+ * WPA Supplicant / Example program entrypoint
+ * Copyright (c) 2003-2005, 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 "wpa_supplicant_i.h"
+
+int main(int argc, char *argv[])
+{
+	struct wpa_interface iface;
+	int exitcode = 0;
+	struct wpa_params params;
+	struct wpa_global *global;
+
+	memset(&params, 0, sizeof(params));
+	params.wpa_debug_level = MSG_INFO;
+
+	global = wpa_supplicant_init(&params);
+	if (global == NULL)
+		return -1;
+
+	memset(&iface, 0, sizeof(iface));
+	/* TODO: set interface parameters */
+
+	if (wpa_supplicant_add_iface(global, &iface, NULL) == NULL)
+		exitcode = -1;
+
+	if (exitcode == 0)
+		exitcode = wpa_supplicant_run(global);
+
+	wpa_supplicant_deinit(global);
+
+	return exitcode;
+}
diff --git a/hostap/wpa_supplicant/main_winmain.c b/hostap/wpa_supplicant/main_winmain.c
new file mode 100644
index 0000000..e1dded0
--- /dev/null
+++ b/hostap/wpa_supplicant/main_winmain.c
@@ -0,0 +1,78 @@
+/*
+ * WPA Supplicant / WinMain() function for Windows-based applications
+ * Copyright (c) 2006, 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 "wpa_supplicant_i.h"
+
+#ifdef _WIN32_WCE
+#define CMDLINE LPWSTR
+#else /* _WIN32_WCE */
+#define CMDLINE LPSTR
+#endif /* _WIN32_WCE */
+
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+		   CMDLINE lpCmdLine, int nShowCmd)
+{
+	int i;
+	struct wpa_interface *ifaces, *iface;
+	int iface_count, exitcode = -1;
+	struct wpa_params params;
+	struct wpa_global *global;
+
+	if (os_program_init())
+		return -1;
+
+	os_memset(&params, 0, sizeof(params));
+	params.wpa_debug_level = MSG_MSGDUMP;
+	params.wpa_debug_file_path = "\\Temp\\wpa_supplicant-log.txt";
+	params.wpa_debug_show_keys = 1;
+
+	iface = ifaces = os_zalloc(sizeof(struct wpa_interface));
+	if (ifaces == NULL)
+		return -1;
+	iface_count = 1;
+
+	iface->confname = "default";
+	iface->driver = "ndis";
+	iface->ifname = "";
+
+	exitcode = 0;
+	global = wpa_supplicant_init(&params);
+	if (global == NULL) {
+		printf("Failed to initialize wpa_supplicant\n");
+		exitcode = -1;
+	}
+
+	for (i = 0; exitcode == 0 && i < iface_count; i++) {
+		if ((ifaces[i].confname == NULL &&
+		     ifaces[i].ctrl_interface == NULL) ||
+		    ifaces[i].ifname == NULL) {
+			if (iface_count == 1 && (params.ctrl_interface ||
+						 params.dbus_ctrl_interface))
+				break;
+			exitcode = -1;
+			break;
+		}
+		if (wpa_supplicant_add_iface(global, &ifaces[i], NULL) == NULL)
+			exitcode = -1;
+	}
+
+	if (exitcode == 0)
+		exitcode = wpa_supplicant_run(global);
+
+	wpa_supplicant_deinit(global);
+
+	os_free(ifaces);
+
+	os_program_deinit();
+
+	return exitcode;
+}
diff --git a/hostap/wpa_supplicant/main_winsvc.c b/hostap/wpa_supplicant/main_winsvc.c
new file mode 100644
index 0000000..9950aa9
--- /dev/null
+++ b/hostap/wpa_supplicant/main_winsvc.c
@@ -0,0 +1,458 @@
+/*
+ * WPA Supplicant / main() function for Win32 service
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * The root of wpa_supplicant configuration in registry is
+ * HKEY_LOCAL_MACHINE\\SOFTWARE\\%wpa_supplicant. This level includes global
+ * parameters and a 'interfaces' subkey with all the interface configuration
+ * (adapter to confname mapping). Each such mapping is a subkey that has
+ * 'adapter' and 'config' values.
+ *
+ * This program can be run either as a normal command line application, e.g.,
+ * for debugging, with 'wpasvc.exe app' or as a Windows service. Service need
+ * to be registered with 'wpasvc.exe reg <full path to wpasvc.exe>'. After
+ * this, it can be started like any other Windows service (e.g., 'net start
+ * wpasvc') or it can be configured to start automatically through the Services
+ * tool in administrative tasks. The service can be unregistered with
+ * 'wpasvc.exe unreg'.
+ */
+
+#include "includes.h"
+#include <windows.h>
+
+#include "common.h"
+#include "wpa_supplicant_i.h"
+#include "eloop.h"
+
+#ifndef WPASVC_NAME
+#define WPASVC_NAME TEXT("wpasvc")
+#endif
+#ifndef WPASVC_DISPLAY_NAME
+#define WPASVC_DISPLAY_NAME TEXT("wpa_supplicant service")
+#endif
+#ifndef WPASVC_DESCRIPTION
+#define WPASVC_DESCRIPTION \
+TEXT("Provides IEEE 802.1X and WPA/WPA2 supplicant functionality")
+#endif
+
+static HANDLE kill_svc;
+
+static SERVICE_STATUS_HANDLE svc_status_handle;
+static SERVICE_STATUS svc_status;
+
+
+#ifndef WPA_KEY_ROOT
+#define WPA_KEY_ROOT HKEY_LOCAL_MACHINE
+#endif
+#ifndef WPA_KEY_PREFIX
+#define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant")
+#endif
+
+#ifdef UNICODE
+#define TSTR "%S"
+#else /* UNICODE */
+#define TSTR "%s"
+#endif /* UNICODE */
+
+
+static int read_interface(struct wpa_global *global, HKEY _hk,
+			  const TCHAR *name)
+{
+	HKEY hk;
+#define TBUFLEN 255
+	TCHAR adapter[TBUFLEN], config[TBUFLEN], ctrl_interface[TBUFLEN];
+	DWORD buflen, val;
+	LONG ret;
+	struct wpa_interface iface;
+	int skip_on_error = 0;
+
+	ret = RegOpenKeyEx(_hk, name, 0, KEY_QUERY_VALUE, &hk);
+	if (ret != ERROR_SUCCESS) {
+		printf("Could not open wpa_supplicant interface key\n");
+		return -1;
+	}
+
+	os_memset(&iface, 0, sizeof(iface));
+	iface.driver = "ndis";
+
+	buflen = sizeof(ctrl_interface);
+	ret = RegQueryValueEx(hk, TEXT("ctrl_interface"), NULL, NULL,
+			      (LPBYTE) ctrl_interface, &buflen);
+	if (ret == ERROR_SUCCESS) {
+		ctrl_interface[TBUFLEN - 1] = TEXT('\0');
+		wpa_unicode2ascii_inplace(ctrl_interface);
+		printf("ctrl_interface[len=%d] '%s'\n",
+		       (int) buflen, (char *) ctrl_interface);
+		iface.ctrl_interface = (char *) ctrl_interface;
+	}
+
+	buflen = sizeof(adapter);
+	ret = RegQueryValueEx(hk, TEXT("adapter"), NULL, NULL,
+			      (LPBYTE) adapter, &buflen);
+	if (ret == ERROR_SUCCESS) {
+		adapter[TBUFLEN - 1] = TEXT('\0');
+		wpa_unicode2ascii_inplace(adapter);
+		printf("adapter[len=%d] '%s'\n",
+		       (int) buflen, (char *) adapter);
+		iface.ifname = (char *) adapter;
+	}
+
+	buflen = sizeof(config);
+	ret = RegQueryValueEx(hk, TEXT("config"), NULL, NULL,
+			      (LPBYTE) config, &buflen);
+	if (ret == ERROR_SUCCESS) {
+		config[sizeof(config) - 1] = '\0';
+		wpa_unicode2ascii_inplace(config);
+		printf("config[len=%d] '%s'\n",
+		       (int) buflen, (char *) config);
+		iface.confname = (char *) config;
+	}
+
+	buflen = sizeof(val);
+	ret = RegQueryValueEx(hk, TEXT("skip_on_error"), NULL, NULL,
+			      (LPBYTE) &val, &buflen);
+	if (ret == ERROR_SUCCESS && buflen == sizeof(val))
+		skip_on_error = val;
+
+	RegCloseKey(hk);
+
+	if (wpa_supplicant_add_iface(global, &iface, NULL) == NULL) {
+		if (skip_on_error)
+			wpa_printf(MSG_DEBUG, "Skipped interface '%s' due to "
+				   "initialization failure", iface.ifname);
+		else
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_supplicant_thread(void)
+{
+	int exitcode;
+	struct wpa_params params;
+	struct wpa_global *global;
+	HKEY hk, ihk;
+	DWORD val, buflen, i;
+	LONG ret;
+
+	if (os_program_init())
+		return -1;
+
+	os_memset(&params, 0, sizeof(params));
+	params.wpa_debug_level = MSG_INFO;
+
+	ret = RegOpenKeyEx(WPA_KEY_ROOT, WPA_KEY_PREFIX,
+			   0, KEY_QUERY_VALUE, &hk);
+	if (ret != ERROR_SUCCESS) {
+		printf("Could not open wpa_supplicant registry key\n");
+		return -1;
+	}
+
+	buflen = sizeof(val);
+	ret = RegQueryValueEx(hk, TEXT("debug_level"), NULL, NULL,
+			      (LPBYTE) &val, &buflen);
+	if (ret == ERROR_SUCCESS && buflen == sizeof(val)) {
+		params.wpa_debug_level = val;
+	}
+
+	buflen = sizeof(val);
+	ret = RegQueryValueEx(hk, TEXT("debug_show_keys"), NULL, NULL,
+			      (LPBYTE) &val, &buflen);
+	if (ret == ERROR_SUCCESS && buflen == sizeof(val)) {
+		params.wpa_debug_show_keys = val;
+	}
+
+	buflen = sizeof(val);
+	ret = RegQueryValueEx(hk, TEXT("debug_timestamp"), NULL, NULL,
+			      (LPBYTE) &val, &buflen);
+	if (ret == ERROR_SUCCESS && buflen == sizeof(val)) {
+		params.wpa_debug_timestamp = val;
+	}
+
+	buflen = sizeof(val);
+	ret = RegQueryValueEx(hk, TEXT("debug_use_file"), NULL, NULL,
+			      (LPBYTE) &val, &buflen);
+	if (ret == ERROR_SUCCESS && buflen == sizeof(val) && val) {
+		params.wpa_debug_file_path = "\\Temp\\wpa_supplicant-log.txt";
+	}
+
+	exitcode = 0;
+	global = wpa_supplicant_init(&params);
+	if (global == NULL) {
+		printf("Failed to initialize wpa_supplicant\n");
+		exitcode = -1;
+	}
+
+	ret = RegOpenKeyEx(hk, TEXT("interfaces"), 0, KEY_ENUMERATE_SUB_KEYS,
+			   &ihk);
+	RegCloseKey(hk);
+	if (ret != ERROR_SUCCESS) {
+		printf("Could not open wpa_supplicant interfaces registry "
+		       "key\n");
+		return -1;
+	}
+
+	for (i = 0; ; i++) {
+		TCHAR name[255];
+		DWORD namelen;
+
+		namelen = 255;
+		ret = RegEnumKeyEx(ihk, i, name, &namelen, NULL, NULL, NULL,
+				   NULL);
+
+		if (ret == ERROR_NO_MORE_ITEMS)
+			break;
+
+		if (ret != ERROR_SUCCESS) {
+			printf("RegEnumKeyEx failed: 0x%x\n",
+			       (unsigned int) ret);
+			break;
+		}
+
+		if (namelen >= 255)
+			namelen = 255 - 1;
+		name[namelen] = '\0';
+
+		wpa_printf(MSG_DEBUG, "interface %d: %s\n", (int) i, name);
+		if (read_interface(global, ihk, name) < 0)
+			exitcode = -1;
+	}
+
+	RegCloseKey(ihk);
+
+	if (exitcode == 0)
+		exitcode = wpa_supplicant_run(global);
+
+	wpa_supplicant_deinit(global);
+
+	os_program_deinit();
+
+	return exitcode;
+}
+
+
+static DWORD svc_thread(LPDWORD param)
+{
+	int ret = wpa_supplicant_thread();
+
+	svc_status.dwCurrentState = SERVICE_STOPPED;
+	svc_status.dwWaitHint = 0;
+	if (!SetServiceStatus(svc_status_handle, &svc_status)) {
+		printf("SetServiceStatus() failed: %d\n",
+		       (int) GetLastError());
+	}
+
+	return ret;
+}
+
+
+static int register_service(const TCHAR *exe)
+{
+	SC_HANDLE svc, scm;
+	SERVICE_DESCRIPTION sd;
+
+	printf("Registering service: " TSTR "\n", WPASVC_NAME);
+
+	scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
+	if (!scm) {
+		printf("OpenSCManager failed: %d\n", (int) GetLastError());
+		return -1;
+	}
+
+	svc = CreateService(scm, WPASVC_NAME, WPASVC_DISPLAY_NAME,
+			    SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
+			    SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
+			    exe, NULL, NULL, NULL, NULL, NULL);
+
+	if (!svc) {
+		printf("CreateService failed: %d\n\n", (int) GetLastError());
+		CloseServiceHandle(scm);
+		return -1;
+	}
+
+	os_memset(&sd, 0, sizeof(sd));
+	sd.lpDescription = WPASVC_DESCRIPTION;
+	if (!ChangeServiceConfig2(svc, SERVICE_CONFIG_DESCRIPTION, &sd)) {
+		printf("ChangeServiceConfig2 failed: %d\n",
+		       (int) GetLastError());
+		/* This is not a fatal error, so continue anyway. */
+	}
+
+	CloseServiceHandle(svc);
+	CloseServiceHandle(scm);
+
+	printf("Service registered successfully.\n");
+
+	return 0;
+}
+
+
+static int unregister_service(void)
+{
+	SC_HANDLE svc, scm;
+	SERVICE_STATUS status;
+
+	printf("Unregistering service: " TSTR "\n", WPASVC_NAME);
+
+	scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
+	if (!scm) {
+		printf("OpenSCManager failed: %d\n", (int) GetLastError());
+		return -1;
+	}
+
+	svc = OpenService(scm, WPASVC_NAME, SERVICE_ALL_ACCESS | DELETE);
+	if (!svc) {
+		printf("OpenService failed: %d\n\n", (int) GetLastError());
+		CloseServiceHandle(scm);
+		return -1;
+	}
+
+	if (QueryServiceStatus(svc, &status)) {
+		if (status.dwCurrentState != SERVICE_STOPPED) {
+			printf("Service currently active - stopping "
+			       "service...\n");
+			if (!ControlService(svc, SERVICE_CONTROL_STOP,
+					    &status)) {
+				printf("ControlService failed: %d\n",
+				       (int) GetLastError());
+			}
+			Sleep(500);
+		}
+	}
+
+	if (DeleteService(svc)) {
+		printf("Service unregistered successfully.\n");
+	} else {
+		printf("DeleteService failed: %d\n", (int) GetLastError());
+	}
+
+	CloseServiceHandle(svc);
+	CloseServiceHandle(scm);
+
+	return 0;
+}
+
+
+static void WINAPI service_ctrl_handler(DWORD control_code)
+{
+	switch (control_code) {
+	case SERVICE_CONTROL_INTERROGATE:
+		break;
+	case SERVICE_CONTROL_SHUTDOWN:
+	case SERVICE_CONTROL_STOP:
+		svc_status.dwCurrentState = SERVICE_STOP_PENDING;
+		svc_status.dwWaitHint = 2000;
+		eloop_terminate();
+		SetEvent(kill_svc);
+		break;
+	}
+
+	if (!SetServiceStatus(svc_status_handle, &svc_status)) {
+		printf("SetServiceStatus() failed: %d\n",
+		       (int) GetLastError());
+	}
+}
+
+
+static void WINAPI service_start(DWORD argc, LPTSTR *argv)
+{
+	DWORD id;
+
+	svc_status_handle = RegisterServiceCtrlHandler(WPASVC_NAME,
+						       service_ctrl_handler);
+	if (svc_status_handle == (SERVICE_STATUS_HANDLE) 0) {
+		printf("RegisterServiceCtrlHandler failed: %d\n",
+		       (int) GetLastError());
+		return;
+	}
+
+	os_memset(&svc_status, 0, sizeof(svc_status));
+	svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+	svc_status.dwCurrentState = SERVICE_START_PENDING;
+	svc_status.dwWaitHint = 1000;
+
+	if (!SetServiceStatus(svc_status_handle, &svc_status)) {
+		printf("SetServiceStatus() failed: %d\n",
+		       (int) GetLastError());
+		return;
+	}
+
+	kill_svc = CreateEvent(0, TRUE, FALSE, 0);
+	if (!kill_svc) {
+		printf("CreateEvent failed: %d\n", (int) GetLastError());
+		return;
+	}
+
+	if (CreateThread(0, 0, (LPTHREAD_START_ROUTINE) svc_thread, 0, 0, &id)
+	    == 0) {
+		printf("CreateThread failed: %d\n", (int) GetLastError());
+		return;
+	}
+
+	if (svc_status.dwCurrentState == SERVICE_START_PENDING) {
+		svc_status.dwCurrentState = SERVICE_RUNNING;
+		svc_status.dwWaitHint = 0;
+		svc_status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+			SERVICE_ACCEPT_SHUTDOWN;
+	}
+
+	if (!SetServiceStatus(svc_status_handle, &svc_status)) {
+		printf("SetServiceStatus() failed: %d\n",
+		       (int) GetLastError());
+		return;
+	}
+
+	/* wait until service gets killed */
+	WaitForSingleObject(kill_svc, INFINITE);
+}
+
+
+int main(int argc, char *argv[])
+{
+	SERVICE_TABLE_ENTRY dt[] = {
+		{ WPASVC_NAME, service_start },
+		{ NULL, NULL }
+	};
+
+	if (argc > 1) {
+		if (os_strcmp(argv[1], "reg") == 0) {
+			TCHAR *path;
+			int ret;
+
+			if (argc < 3) {
+				path = os_malloc(MAX_PATH * sizeof(TCHAR));
+				if (path == NULL)
+					return -1;
+				if (!GetModuleFileName(NULL, path, MAX_PATH)) {
+					printf("GetModuleFileName failed: "
+					       "%d\n", (int) GetLastError());
+					os_free(path);
+					return -1;
+				}
+			} else {
+				path = wpa_strdup_tchar(argv[2]);
+				if (path == NULL)
+					return -1;
+			}
+			ret = register_service(path);
+			os_free(path);
+			return ret;
+		} else if (os_strcmp(argv[1], "unreg") == 0) {
+			return unregister_service();
+		} else if (os_strcmp(argv[1], "app") == 0) {
+			return wpa_supplicant_thread();
+		}
+	}
+
+	if (!StartServiceCtrlDispatcher(dt)) {
+		printf("StartServiceCtrlDispatcher failed: %d\n",
+		       (int) GetLastError());
+	}
+
+	return 0;
+}
diff --git a/hostap/wpa_supplicant/mesh.c b/hostap/wpa_supplicant/mesh.c
new file mode 100644
index 0000000..77f708b
--- /dev/null
+++ b/hostap/wpa_supplicant/mesh.c
@@ -0,0 +1,543 @@
+/*
+ * WPA Supplicant - Basic mesh mode routines
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * 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 "utils/uuid.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "ap/sta_info.h"
+#include "ap/hostapd.h"
+#include "ap/ieee802_11.h"
+#include "config_ssid.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "notify.h"
+#include "ap.h"
+#include "mesh_mpm.h"
+#include "mesh_rsn.h"
+#include "mesh.h"
+
+
+static void wpa_supplicant_mesh_deinit(struct wpa_supplicant *wpa_s)
+{
+	wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh);
+	wpa_s->ifmsh = NULL;
+	wpa_s->current_ssid = NULL;
+	os_free(wpa_s->mesh_rsn);
+	wpa_s->mesh_rsn = NULL;
+	/* TODO: leave mesh (stop beacon). This will happen on link down
+	 * anyway, so it's not urgent */
+}
+
+
+void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s,
+				      struct hostapd_iface *ifmsh)
+{
+	if (!ifmsh)
+		return;
+
+	if (ifmsh->mconf) {
+		mesh_mpm_deinit(wpa_s, ifmsh);
+		if (ifmsh->mconf->rsn_ie) {
+			ifmsh->mconf->rsn_ie = NULL;
+			/* We cannot free this struct
+			 * because wpa_authenticator on
+			 * hostapd side is also using it
+			 * for now just set to NULL and
+			 * let hostapd code free it.
+			 */
+		}
+		os_free(ifmsh->mconf);
+		ifmsh->mconf = NULL;
+	}
+
+	/* take care of shared data */
+	hostapd_interface_deinit(ifmsh);
+	hostapd_interface_free(ifmsh);
+}
+
+
+static struct mesh_conf * mesh_config_create(struct wpa_ssid *ssid)
+{
+	struct mesh_conf *conf;
+
+	conf = os_zalloc(sizeof(struct mesh_conf));
+	if (!conf)
+		return NULL;
+
+	os_memcpy(conf->meshid, ssid->ssid, ssid->ssid_len);
+	conf->meshid_len = ssid->ssid_len;
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE)
+		conf->security |= MESH_CONF_SEC_AUTH |
+			MESH_CONF_SEC_AMPE;
+	else
+		conf->security |= MESH_CONF_SEC_NONE;
+
+	/* defaults */
+	conf->mesh_pp_id = MESH_PATH_PROTOCOL_HWMP;
+	conf->mesh_pm_id = MESH_PATH_METRIC_AIRTIME;
+	conf->mesh_cc_id = 0;
+	conf->mesh_sp_id = MESH_SYNC_METHOD_NEIGHBOR_OFFSET;
+	conf->mesh_auth_id = (conf->security & MESH_CONF_SEC_AUTH) ? 1 : 0;
+	conf->dot11MeshMaxRetries = ssid->dot11MeshMaxRetries;
+	conf->dot11MeshRetryTimeout = ssid->dot11MeshRetryTimeout;
+	conf->dot11MeshConfirmTimeout = ssid->dot11MeshConfirmTimeout;
+	conf->dot11MeshHoldingTimeout = ssid->dot11MeshHoldingTimeout;
+
+	return conf;
+}
+
+
+static void wpas_mesh_copy_groups(struct hostapd_data *bss,
+				  struct wpa_supplicant *wpa_s)
+{
+	int num_groups;
+	size_t groups_size;
+
+	for (num_groups = 0; wpa_s->conf->sae_groups[num_groups] > 0;
+	     num_groups++)
+		;
+
+	groups_size = (num_groups + 1) * sizeof(wpa_s->conf->sae_groups[0]);
+	bss->conf->sae_groups = os_malloc(groups_size);
+	if (bss->conf->sae_groups)
+		os_memcpy(bss->conf->sae_groups, wpa_s->conf->sae_groups,
+			  groups_size);
+}
+
+
+static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
+				    struct wpa_ssid *ssid)
+{
+	struct hostapd_iface *ifmsh;
+	struct hostapd_data *bss;
+	struct hostapd_config *conf;
+	struct mesh_conf *mconf;
+	int basic_rates_erp[] = { 10, 20, 55, 60, 110, 120, 240, -1 };
+	static int default_groups[] = { 19, 20, 21, 25, 26, -1 };
+	size_t len;
+	int rate_len;
+
+	if (!wpa_s->conf->user_mpm) {
+		/* not much for us to do here */
+		wpa_msg(wpa_s, MSG_WARNING,
+			"user_mpm is not enabled in configuration");
+		return 0;
+	}
+
+	wpa_s->ifmsh = ifmsh = os_zalloc(sizeof(*wpa_s->ifmsh));
+	if (!ifmsh)
+		return -ENOMEM;
+
+	ifmsh->drv_flags = wpa_s->drv_flags;
+	ifmsh->num_bss = 1;
+	ifmsh->bss = os_calloc(wpa_s->ifmsh->num_bss,
+			       sizeof(struct hostapd_data *));
+	if (!ifmsh->bss)
+		goto out_free;
+
+	ifmsh->bss[0] = bss = os_zalloc(sizeof(struct hostapd_data));
+	if (!bss)
+		goto out_free;
+
+	os_memcpy(bss->own_addr, wpa_s->own_addr, ETH_ALEN);
+	bss->driver = wpa_s->driver;
+	bss->drv_priv = wpa_s->drv_priv;
+	bss->iface = ifmsh;
+	bss->mesh_sta_free_cb = mesh_mpm_free_sta;
+	wpa_s->assoc_freq = ssid->frequency;
+	wpa_s->current_ssid = ssid;
+
+	/* setup an AP config for auth processing */
+	conf = hostapd_config_defaults();
+	if (!conf)
+		goto out_free;
+
+	bss->conf = *conf->bss;
+	bss->conf->start_disabled = 1;
+	bss->conf->mesh = MESH_ENABLED;
+	bss->conf->ap_max_inactivity = wpa_s->conf->mesh_max_inactivity;
+	bss->iconf = conf;
+	ifmsh->conf = conf;
+
+	ifmsh->bss[0]->max_plinks = wpa_s->conf->max_peer_links;
+	ifmsh->bss[0]->dot11RSNASAERetransPeriod =
+		wpa_s->conf->dot11RSNASAERetransPeriod;
+	os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface));
+
+	mconf = mesh_config_create(ssid);
+	if (!mconf)
+		goto out_free;
+	ifmsh->mconf = mconf;
+
+	/* need conf->hw_mode for supported rates. */
+	if (ssid->frequency == 0) {
+		conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
+		conf->channel = 1;
+	} else {
+		conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
+						       &conf->channel);
+	}
+	if (conf->hw_mode == NUM_HOSTAPD_MODES) {
+		wpa_printf(MSG_ERROR, "Unsupported mesh mode frequency: %d MHz",
+			   ssid->frequency);
+		goto out_free;
+	}
+
+	if (ssid->mesh_basic_rates == NULL) {
+		/*
+		 * XXX: Hack! This is so an MPM which correctly sets the ERP
+		 * mandatory rates as BSSBasicRateSet doesn't reject us. We
+		 * could add a new hw_mode HOSTAPD_MODE_IEEE80211G_ERP, but
+		 * this is way easier. This also makes our BSSBasicRateSet
+		 * advertised in beacons match the one in peering frames, sigh.
+		 */
+		if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) {
+			conf->basic_rates = os_malloc(sizeof(basic_rates_erp));
+			if (!conf->basic_rates)
+				goto out_free;
+			os_memcpy(conf->basic_rates, basic_rates_erp,
+				  sizeof(basic_rates_erp));
+		}
+	} else {
+		rate_len = 0;
+		while (1) {
+			if (ssid->mesh_basic_rates[rate_len] < 1)
+				break;
+			rate_len++;
+		}
+		conf->basic_rates = os_calloc(rate_len + 1, sizeof(int));
+		if (conf->basic_rates == NULL)
+			goto out_free;
+		os_memcpy(conf->basic_rates, ssid->mesh_basic_rates,
+			  rate_len * sizeof(int));
+		conf->basic_rates[rate_len] = -1;
+	}
+
+	if (hostapd_setup_interface(ifmsh)) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to initialize hostapd interface for mesh");
+		return -1;
+	}
+
+	if (wpa_drv_init_mesh(wpa_s)) {
+		wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh in driver");
+		return -1;
+	}
+
+	if (mconf->security != MESH_CONF_SEC_NONE) {
+		if (ssid->passphrase == NULL) {
+			wpa_printf(MSG_ERROR,
+				   "mesh: Passphrase for SAE not configured");
+			goto out_free;
+		}
+
+		bss->conf->wpa = ssid->proto;
+		bss->conf->wpa_key_mgmt = ssid->key_mgmt;
+
+		if (wpa_s->conf->sae_groups &&
+		    wpa_s->conf->sae_groups[0] > 0) {
+			wpas_mesh_copy_groups(bss, wpa_s);
+		} else {
+			bss->conf->sae_groups =
+				os_malloc(sizeof(default_groups));
+			if (!bss->conf->sae_groups)
+				goto out_free;
+			os_memcpy(bss->conf->sae_groups, default_groups,
+				  sizeof(default_groups));
+		}
+
+		len = os_strlen(ssid->passphrase);
+		bss->conf->ssid.wpa_passphrase =
+			dup_binstr(ssid->passphrase, len);
+
+		wpa_s->mesh_rsn = mesh_rsn_auth_init(wpa_s, mconf);
+		if (!wpa_s->mesh_rsn)
+			goto out_free;
+	}
+
+	wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf);
+
+	return 0;
+out_free:
+	wpa_supplicant_mesh_deinit(wpa_s);
+	return -ENOMEM;
+}
+
+
+void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+			  const u8 *ies, size_t ie_len)
+{
+	struct ieee802_11_elems elems;
+
+	wpa_msg(wpa_s, MSG_INFO,
+		"new peer notification for " MACSTR, MAC2STR(addr));
+
+	if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) {
+		wpa_msg(wpa_s, MSG_INFO, "Could not parse beacon from " MACSTR,
+			MAC2STR(addr));
+		return;
+	}
+	wpa_mesh_new_mesh_peer(wpa_s, addr, &elems);
+}
+
+
+void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s,
+				     struct wpabuf **extra_ie)
+{
+	/* EID + 0-length (wildcard) mesh-id */
+	size_t ielen = 2;
+
+	if (wpabuf_resize(extra_ie, ielen) == 0) {
+		wpabuf_put_u8(*extra_ie, WLAN_EID_MESH_ID);
+		wpabuf_put_u8(*extra_ie, 0);
+	}
+}
+
+
+int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
+			     struct wpa_ssid *ssid)
+{
+	struct wpa_driver_mesh_join_params params;
+	int ret = 0;
+
+	if (!ssid || !ssid->ssid || !ssid->ssid_len || !ssid->frequency) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	wpa_supplicant_mesh_deinit(wpa_s);
+
+	os_memset(&params, 0, sizeof(params));
+	params.meshid = ssid->ssid;
+	params.meshid_len = ssid->ssid_len;
+	ibss_mesh_setup_freq(wpa_s, ssid, &params.freq);
+	wpa_s->mesh_ht_enabled = !!params.freq.ht_enabled;
+	if (ssid->beacon_int > 0)
+		params.beacon_int = ssid->beacon_int;
+	else if (wpa_s->conf->beacon_int > 0)
+		params.beacon_int = wpa_s->conf->beacon_int;
+	params.max_peer_links = wpa_s->conf->max_peer_links;
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+		params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH;
+		params.flags |= WPA_DRIVER_MESH_FLAG_AMPE;
+		wpa_s->conf->user_mpm = 1;
+	}
+
+	if (wpa_s->conf->user_mpm) {
+		params.flags |= WPA_DRIVER_MESH_FLAG_USER_MPM;
+		params.conf.flags &= ~WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS;
+	} else {
+		params.flags |= WPA_DRIVER_MESH_FLAG_DRIVER_MPM;
+		params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS;
+	}
+	params.conf.peer_link_timeout = wpa_s->conf->mesh_max_inactivity;
+
+	if (wpa_supplicant_mesh_init(wpa_s, ssid)) {
+		wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh");
+		wpa_drv_leave_mesh(wpa_s);
+		ret = -1;
+		goto out;
+	}
+
+	if (wpa_s->ifmsh) {
+		params.ies = wpa_s->ifmsh->mconf->rsn_ie;
+		params.ie_len = wpa_s->ifmsh->mconf->rsn_ie_len;
+		params.basic_rates = wpa_s->ifmsh->basic_rates;
+	}
+
+	wpa_msg(wpa_s, MSG_INFO, "joining mesh %s",
+		wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+	ret = wpa_drv_join_mesh(wpa_s, &params);
+	if (ret)
+		wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d\n", ret);
+
+	/* hostapd sets the interface down until we associate */
+	wpa_drv_set_operstate(wpa_s, 1);
+
+out:
+	return ret;
+}
+
+
+int wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s)
+{
+	int ret = 0;
+
+	wpa_msg(wpa_s, MSG_INFO, "leaving mesh");
+
+	/* Need to send peering close messages first */
+	wpa_supplicant_mesh_deinit(wpa_s);
+
+	ret = wpa_drv_leave_mesh(wpa_s);
+	if (ret)
+		wpa_msg(wpa_s, MSG_ERROR, "mesh leave error=%d", ret);
+
+	wpa_drv_set_operstate(wpa_s, 1);
+
+	return ret;
+}
+
+
+static int mesh_attr_text(const u8 *ies, size_t ies_len, char *buf, char *end)
+{
+	struct ieee802_11_elems elems;
+	char *mesh_id, *pos = buf;
+	u8 *bss_basic_rate_set;
+	int bss_basic_rate_set_len, ret, i;
+
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == ParseFailed)
+		return -1;
+
+	if (elems.mesh_id_len < 1)
+		return 0;
+
+	mesh_id = os_malloc(elems.mesh_id_len + 1);
+	if (mesh_id == NULL)
+		return -1;
+
+	os_memcpy(mesh_id, elems.mesh_id, elems.mesh_id_len);
+	mesh_id[elems.mesh_id_len] = '\0';
+	ret = os_snprintf(pos, end - pos, "mesh_id=%s\n", mesh_id);
+	os_free(mesh_id);
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	if (elems.mesh_config_len > 6) {
+		ret = os_snprintf(pos, end - pos,
+				  "active_path_selection_protocol_id=0x%02x\n"
+				  "active_path_selection_metric_id=0x%02x\n"
+				  "congestion_control_mode_id=0x%02x\n"
+				  "synchronization_method_id=0x%02x\n"
+				  "authentication_protocol_id=0x%02x\n"
+				  "mesh_formation_info=0x%02x\n"
+				  "mesh_capability=0x%02x\n",
+				  elems.mesh_config[0], elems.mesh_config[1],
+				  elems.mesh_config[2], elems.mesh_config[3],
+				  elems.mesh_config[4], elems.mesh_config[5],
+				  elems.mesh_config[6]);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	bss_basic_rate_set = os_malloc(elems.supp_rates_len +
+		elems.ext_supp_rates_len);
+	if (bss_basic_rate_set == NULL)
+		return -1;
+
+	bss_basic_rate_set_len = 0;
+	for (i = 0; i < elems.supp_rates_len; i++) {
+		if (elems.supp_rates[i] & 0x80) {
+			bss_basic_rate_set[bss_basic_rate_set_len++] =
+				(elems.supp_rates[i] & 0x7f) * 5;
+		}
+	}
+	for (i = 0; i < elems.ext_supp_rates_len; i++) {
+		if (elems.ext_supp_rates[i] & 0x80) {
+			bss_basic_rate_set[bss_basic_rate_set_len++] =
+				(elems.ext_supp_rates[i] & 0x7f) * 5;
+		}
+	}
+	if (bss_basic_rate_set_len > 0) {
+		ret = os_snprintf(pos, end - pos, "bss_basic_rate_set=%d",
+				  bss_basic_rate_set[0]);
+		if (os_snprintf_error(end - pos, ret))
+			goto fail;
+		pos += ret;
+
+		for (i = 1; i < bss_basic_rate_set_len; i++) {
+			ret = os_snprintf(pos, end - pos, " %d",
+					  bss_basic_rate_set[i]);
+			if (os_snprintf_error(end - pos, ret))
+				goto fail;
+			pos += ret;
+		}
+
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			goto fail;
+		pos += ret;
+	}
+fail:
+	os_free(bss_basic_rate_set);
+
+	return pos - buf;
+}
+
+
+int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+			       char *end)
+{
+	return mesh_attr_text(ies, ies_len, buf, end);
+}
+
+
+static int wpas_mesh_get_ifname(struct wpa_supplicant *wpa_s, char *ifname,
+				size_t len)
+{
+	char *ifname_ptr = wpa_s->ifname;
+	int res;
+
+	res = os_snprintf(ifname, len, "mesh-%s-%d", ifname_ptr,
+			  wpa_s->mesh_if_idx);
+	if (os_snprintf_error(len, res) ||
+	    (os_strlen(ifname) >= IFNAMSIZ &&
+	     os_strlen(wpa_s->ifname) < IFNAMSIZ)) {
+		/* Try to avoid going over the IFNAMSIZ length limit */
+		res = os_snprintf(ifname, len, "mesh-%d", wpa_s->mesh_if_idx);
+		if (os_snprintf_error(len, res))
+			return -1;
+	}
+	wpa_s->mesh_if_idx++;
+	return 0;
+}
+
+
+int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname,
+			    size_t len)
+{
+	struct wpa_interface iface;
+	struct wpa_supplicant *mesh_wpa_s;
+	u8 addr[ETH_ALEN];
+
+	if (ifname[0] == '\0' && wpas_mesh_get_ifname(wpa_s, ifname, len) < 0)
+		return -1;
+
+	if (wpa_drv_if_add(wpa_s, WPA_IF_MESH, ifname, NULL, NULL, NULL, addr,
+			   NULL) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "mesh: Failed to create new mesh interface");
+		return -1;
+	}
+	wpa_printf(MSG_INFO, "mesh: Created virtual interface %s addr "
+		   MACSTR, ifname, MAC2STR(addr));
+
+	os_memset(&iface, 0, sizeof(iface));
+	iface.ifname = ifname;
+	iface.driver = wpa_s->driver->name;
+	iface.driver_param = wpa_s->conf->driver_param;
+	iface.ctrl_interface = wpa_s->conf->ctrl_interface;
+
+	mesh_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
+	if (!mesh_wpa_s) {
+		wpa_printf(MSG_ERROR,
+			   "mesh: Failed to create new wpa_supplicant interface");
+		wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
+		return -1;
+	}
+	mesh_wpa_s->mesh_if_created = 1;
+	return 0;
+}
diff --git a/hostap/wpa_supplicant/mesh.h b/hostap/wpa_supplicant/mesh.h
new file mode 100644
index 0000000..3cb7f1b
--- /dev/null
+++ b/hostap/wpa_supplicant/mesh.h
@@ -0,0 +1,44 @@
+/*
+ * WPA Supplicant - Basic mesh mode routines
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MESH_H
+#define MESH_H
+
+int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
+			     struct wpa_ssid *ssid);
+int wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s,
+				      struct hostapd_iface *ifmsh);
+int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+			       char *end);
+int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname,
+			    size_t len);
+
+#ifdef CONFIG_MESH
+
+void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+			  const u8 *ies, size_t ie_len);
+void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s,
+				     struct wpabuf **extra_ie);
+
+#else /* CONFIG_MESH */
+
+static inline void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s,
+					const u8 *addr,
+					const u8 *ies, size_t ie_len)
+{
+}
+
+static inline void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s,
+						   struct wpabuf **extra_ie)
+{
+}
+
+#endif /* CONFIG_MESH */
+
+#endif /* MESH_H */
diff --git a/hostap/wpa_supplicant/mesh_mpm.c b/hostap/wpa_supplicant/mesh_mpm.c
new file mode 100644
index 0000000..f81b88c
--- /dev/null
+++ b/hostap/wpa_supplicant/mesh_mpm.c
@@ -0,0 +1,1064 @@
+/*
+ * WPA Supplicant - Basic mesh peer management
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * 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 "ap/hostapd.h"
+#include "ap/sta_info.h"
+#include "ap/ieee802_11.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "mesh_mpm.h"
+#include "mesh_rsn.h"
+
+struct mesh_peer_mgmt_ie {
+	const u8 *proto_id;
+	const u8 *llid;
+	const u8 *plid;
+	const u8 *reason;
+	const u8 *pmk;
+};
+
+static void plink_timer(void *eloop_ctx, void *user_data);
+
+
+enum plink_event {
+	PLINK_UNDEFINED,
+	OPN_ACPT,
+	OPN_RJCT,
+	OPN_IGNR,
+	CNF_ACPT,
+	CNF_RJCT,
+	CNF_IGNR,
+	CLS_ACPT,
+	CLS_IGNR
+};
+
+static const char * const mplstate[] = {
+	[PLINK_LISTEN] = "LISTEN",
+	[PLINK_OPEN_SENT] = "OPEN_SENT",
+	[PLINK_OPEN_RCVD] = "OPEN_RCVD",
+	[PLINK_CNF_RCVD] = "CNF_RCVD",
+	[PLINK_ESTAB] = "ESTAB",
+	[PLINK_HOLDING] = "HOLDING",
+	[PLINK_BLOCKED] = "BLOCKED"
+};
+
+static const char * const mplevent[] = {
+	[PLINK_UNDEFINED] = "UNDEFINED",
+	[OPN_ACPT] = "OPN_ACPT",
+	[OPN_RJCT] = "OPN_RJCT",
+	[OPN_IGNR] = "OPN_IGNR",
+	[CNF_ACPT] = "CNF_ACPT",
+	[CNF_RJCT] = "CNF_RJCT",
+	[CNF_IGNR] = "CNF_IGNR",
+	[CLS_ACPT] = "CLS_ACPT",
+	[CLS_IGNR] = "CLS_IGNR"
+};
+
+
+static int mesh_mpm_parse_peer_mgmt(struct wpa_supplicant *wpa_s,
+				    u8 action_field,
+				    const u8 *ie, size_t len,
+				    struct mesh_peer_mgmt_ie *mpm_ie)
+{
+	os_memset(mpm_ie, 0, sizeof(*mpm_ie));
+
+	/* remove optional PMK at end */
+	if (len >= 16) {
+		len -= 16;
+		mpm_ie->pmk = ie + len - 16;
+	}
+
+	if ((action_field == PLINK_OPEN && len != 4) ||
+	    (action_field == PLINK_CONFIRM && len != 6) ||
+	    (action_field == PLINK_CLOSE && len != 6 && len != 8)) {
+		wpa_msg(wpa_s, MSG_DEBUG, "MPM: Invalid peer mgmt ie");
+		return -1;
+	}
+
+	/* required fields */
+	if (len < 4)
+		return -1;
+	mpm_ie->proto_id = ie;
+	mpm_ie->llid = ie + 2;
+	ie += 4;
+	len -= 4;
+
+	/* close reason is always present at end for close */
+	if (action_field == PLINK_CLOSE) {
+		if (len < 2)
+			return -1;
+		mpm_ie->reason = ie + len - 2;
+		len -= 2;
+	}
+
+	/* plid, present for confirm, and possibly close */
+	if (len)
+		mpm_ie->plid = ie;
+
+	return 0;
+}
+
+
+static int plink_free_count(struct hostapd_data *hapd)
+{
+	if (hapd->max_plinks > hapd->num_plinks)
+		return hapd->max_plinks - hapd->num_plinks;
+	return 0;
+}
+
+
+static u16 copy_supp_rates(struct wpa_supplicant *wpa_s,
+			   struct sta_info *sta,
+			   struct ieee802_11_elems *elems)
+{
+	if (!elems->supp_rates) {
+		wpa_msg(wpa_s, MSG_ERROR, "no supported rates from " MACSTR,
+			MAC2STR(sta->addr));
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (elems->supp_rates_len + elems->ext_supp_rates_len >
+	    sizeof(sta->supported_rates)) {
+		wpa_msg(wpa_s, MSG_ERROR,
+			"Invalid supported rates element length " MACSTR
+			" %d+%d", MAC2STR(sta->addr), elems->supp_rates_len,
+			elems->ext_supp_rates_len);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->supported_rates_len = merge_byte_arrays(
+		sta->supported_rates, sizeof(sta->supported_rates),
+		elems->supp_rates, elems->supp_rates_len,
+		elems->ext_supp_rates, elems->ext_supp_rates_len);
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+/* return true if elems from a neighbor match this MBSS */
+static Boolean matches_local(struct wpa_supplicant *wpa_s,
+			     struct ieee802_11_elems *elems)
+{
+	struct mesh_conf *mconf = wpa_s->ifmsh->mconf;
+
+	if (elems->mesh_config_len < 5)
+		return FALSE;
+
+	return (mconf->meshid_len == elems->mesh_id_len &&
+		os_memcmp(mconf->meshid, elems->mesh_id,
+			  elems->mesh_id_len) == 0 &&
+		mconf->mesh_pp_id == elems->mesh_config[0] &&
+		mconf->mesh_pm_id == elems->mesh_config[1] &&
+		mconf->mesh_cc_id == elems->mesh_config[2] &&
+		mconf->mesh_sp_id == elems->mesh_config[3] &&
+		mconf->mesh_auth_id == elems->mesh_config[4]);
+}
+
+
+/* check if local link id is already used with another peer */
+static Boolean llid_in_use(struct wpa_supplicant *wpa_s, u16 llid)
+{
+	struct sta_info *sta;
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (sta->my_lid == llid)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+/* generate an llid for a link and set to initial state */
+static void mesh_mpm_init_link(struct wpa_supplicant *wpa_s,
+			       struct sta_info *sta)
+{
+	u16 llid;
+
+	do {
+		if (os_get_random((u8 *) &llid, sizeof(llid)) < 0)
+			continue;
+	} while (!llid || llid_in_use(wpa_s, llid));
+
+	sta->my_lid = llid;
+	sta->peer_lid = 0;
+
+	/*
+	 * We do not use wpa_mesh_set_plink_state() here because there is no
+	 * entry in kernel yet.
+	 */
+	sta->plink_state = PLINK_LISTEN;
+}
+
+
+static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s,
+				       struct sta_info *sta,
+				       enum plink_action_field type,
+				       u16 close_reason)
+{
+	struct wpabuf *buf;
+	struct hostapd_iface *ifmsh = wpa_s->ifmsh;
+	struct hostapd_data *bss = ifmsh->bss[0];
+	struct mesh_conf *conf = ifmsh->mconf;
+	u8 supp_rates[2 + 2 + 32];
+#ifdef CONFIG_IEEE80211N
+	u8 ht_capa_oper[2 + 26 + 2 + 22];
+#endif /* CONFIG_IEEE80211N */
+	u8 *pos, *cat;
+	u8 ie_len, add_plid = 0;
+	int ret;
+	int ampe = conf->security & MESH_CONF_SEC_AMPE;
+	size_t buf_len;
+
+	if (!sta)
+		return;
+
+	buf_len = 2 +      /* capability info */
+		  2 +      /* AID */
+		  2 + 8 +  /* supported rates */
+		  2 + (32 - 8) +
+		  2 + 32 + /* mesh ID */
+		  2 + 7 +  /* mesh config */
+		  2 + 23 + /* peering management */
+		  2 + 96 + /* AMPE */
+		  2 + 16;  /* MIC */
+#ifdef CONFIG_IEEE80211N
+	if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) {
+		buf_len += 2 + 26 + /* HT capabilities */
+			   2 + 22;  /* HT operation */
+	}
+#endif /* CONFIG_IEEE80211N */
+	if (type != PLINK_CLOSE)
+		buf_len += conf->rsn_ie_len; /* RSN IE */
+
+	buf = wpabuf_alloc(buf_len);
+	if (!buf)
+		return;
+
+	cat = wpabuf_mhead_u8(buf);
+	wpabuf_put_u8(buf, WLAN_ACTION_SELF_PROTECTED);
+	wpabuf_put_u8(buf, type);
+
+	if (type != PLINK_CLOSE) {
+		u8 info;
+
+		/* capability info */
+		wpabuf_put_le16(buf, ampe ? IEEE80211_CAP_PRIVACY : 0);
+
+		/* aid */
+		if (type == PLINK_CONFIRM)
+			wpabuf_put_le16(buf, sta->peer_lid);
+
+		/* IE: supp + ext. supp rates */
+		pos = hostapd_eid_supp_rates(bss, supp_rates);
+		pos = hostapd_eid_ext_supp_rates(bss, pos);
+		wpabuf_put_data(buf, supp_rates, pos - supp_rates);
+
+		/* IE: RSN IE */
+		wpabuf_put_data(buf, conf->rsn_ie, conf->rsn_ie_len);
+
+		/* IE: Mesh ID */
+		wpabuf_put_u8(buf, WLAN_EID_MESH_ID);
+		wpabuf_put_u8(buf, conf->meshid_len);
+		wpabuf_put_data(buf, conf->meshid, conf->meshid_len);
+
+		/* IE: mesh conf */
+		wpabuf_put_u8(buf, WLAN_EID_MESH_CONFIG);
+		wpabuf_put_u8(buf, 7);
+		wpabuf_put_u8(buf, conf->mesh_pp_id);
+		wpabuf_put_u8(buf, conf->mesh_pm_id);
+		wpabuf_put_u8(buf, conf->mesh_cc_id);
+		wpabuf_put_u8(buf, conf->mesh_sp_id);
+		wpabuf_put_u8(buf, conf->mesh_auth_id);
+		info = (bss->num_plinks > 63 ? 63 : bss->num_plinks) << 1;
+		/* TODO: Add Connected to Mesh Gate/AS subfields */
+		wpabuf_put_u8(buf, info);
+		/* always forwarding & accepting plinks for now */
+		wpabuf_put_u8(buf, 0x1 | 0x8);
+	} else {	/* Peer closing frame */
+		/* IE: Mesh ID */
+		wpabuf_put_u8(buf, WLAN_EID_MESH_ID);
+		wpabuf_put_u8(buf, conf->meshid_len);
+		wpabuf_put_data(buf, conf->meshid, conf->meshid_len);
+	}
+
+	/* IE: Mesh Peering Management element */
+	ie_len = 4;
+	if (ampe)
+		ie_len += PMKID_LEN;
+	switch (type) {
+	case PLINK_OPEN:
+		break;
+	case PLINK_CONFIRM:
+		ie_len += 2;
+		add_plid = 1;
+		break;
+	case PLINK_CLOSE:
+		ie_len += 2;
+		add_plid = 1;
+		ie_len += 2; /* reason code */
+		break;
+	}
+
+	wpabuf_put_u8(buf, WLAN_EID_PEER_MGMT);
+	wpabuf_put_u8(buf, ie_len);
+	/* peering protocol */
+	if (ampe)
+		wpabuf_put_le16(buf, 1);
+	else
+		wpabuf_put_le16(buf, 0);
+	wpabuf_put_le16(buf, sta->my_lid);
+	if (add_plid)
+		wpabuf_put_le16(buf, sta->peer_lid);
+	if (type == PLINK_CLOSE)
+		wpabuf_put_le16(buf, close_reason);
+	if (ampe) {
+		if (sta->sae == NULL) {
+			wpa_msg(wpa_s, MSG_INFO, "Mesh MPM: no SAE session");
+			goto fail;
+		}
+		mesh_rsn_get_pmkid(wpa_s->mesh_rsn, sta,
+				   wpabuf_put(buf, PMKID_LEN));
+	}
+
+#ifdef CONFIG_IEEE80211N
+	if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) {
+		pos = hostapd_eid_ht_capabilities(bss, ht_capa_oper);
+		pos = hostapd_eid_ht_operation(bss, pos);
+		wpabuf_put_data(buf, ht_capa_oper, pos - ht_capa_oper);
+	}
+#endif /* CONFIG_IEEE80211N */
+
+	if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"Mesh MPM: failed to add AMPE and MIC IE");
+		goto fail;
+	}
+
+	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0,
+				  sta->addr, wpa_s->own_addr, wpa_s->own_addr,
+				  wpabuf_head(buf), wpabuf_len(buf), 0);
+	if (ret < 0)
+		wpa_msg(wpa_s, MSG_INFO,
+			"Mesh MPM: failed to send peering frame");
+
+fail:
+	wpabuf_free(buf);
+}
+
+
+/* configure peering state in ours and driver's station entry */
+void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s,
+			      struct sta_info *sta,
+			      enum mesh_plink_state state)
+{
+	struct hostapd_sta_add_params params;
+	int ret;
+
+	sta->plink_state = state;
+
+	os_memset(&params, 0, sizeof(params));
+	params.addr = sta->addr;
+	params.plink_state = state;
+	params.set = 1;
+
+	wpa_msg(wpa_s, MSG_DEBUG, "MPM set " MACSTR " into %s",
+		MAC2STR(sta->addr), mplstate[state]);
+	ret = wpa_drv_sta_add(wpa_s, &params);
+	if (ret) {
+		wpa_msg(wpa_s, MSG_ERROR, "Driver failed to set " MACSTR
+			": %d", MAC2STR(sta->addr), ret);
+	}
+}
+
+
+static void mesh_mpm_fsm_restart(struct wpa_supplicant *wpa_s,
+				 struct sta_info *sta)
+{
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+
+	eloop_cancel_timeout(plink_timer, wpa_s, sta);
+
+	ap_free_sta(hapd, sta);
+}
+
+
+static void plink_timer(void *eloop_ctx, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct sta_info *sta = user_data;
+	u16 reason = 0;
+	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+
+	switch (sta->plink_state) {
+	case PLINK_OPEN_RCVD:
+	case PLINK_OPEN_SENT:
+		/* retry timer */
+		if (sta->mpm_retries < conf->dot11MeshMaxRetries) {
+			eloop_register_timeout(
+				conf->dot11MeshRetryTimeout / 1000,
+				(conf->dot11MeshRetryTimeout % 1000) * 1000,
+				plink_timer, wpa_s, sta);
+			mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0);
+			sta->mpm_retries++;
+			break;
+		}
+		reason = WLAN_REASON_MESH_MAX_RETRIES;
+		/* fall through on else */
+
+	case PLINK_CNF_RCVD:
+		/* confirm timer */
+		if (!reason)
+			reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT;
+		wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+		eloop_register_timeout(conf->dot11MeshHoldingTimeout / 1000,
+			(conf->dot11MeshHoldingTimeout % 1000) * 1000,
+			plink_timer, wpa_s, sta);
+		mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason);
+		break;
+	case PLINK_HOLDING:
+		/* holding timer */
+		mesh_mpm_fsm_restart(wpa_s, sta);
+		break;
+	default:
+		break;
+	}
+}
+
+
+/* initiate peering with station */
+static void
+mesh_mpm_plink_open(struct wpa_supplicant *wpa_s, struct sta_info *sta,
+		    enum mesh_plink_state next_state)
+{
+	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+
+	eloop_cancel_timeout(plink_timer, wpa_s, sta);
+	eloop_register_timeout(conf->dot11MeshRetryTimeout / 1000,
+			       (conf->dot11MeshRetryTimeout % 1000) * 1000,
+			       plink_timer, wpa_s, sta);
+	mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0);
+	wpa_mesh_set_plink_state(wpa_s, sta, next_state);
+}
+
+
+int mesh_mpm_plink_close(struct hostapd_data *hapd,
+			 struct sta_info *sta, void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	int reason = WLAN_REASON_MESH_PEERING_CANCELLED;
+
+	if (sta) {
+		wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+		mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason);
+		wpa_printf(MSG_DEBUG, "MPM closing plink sta=" MACSTR,
+			   MAC2STR(sta->addr));
+		eloop_cancel_timeout(plink_timer, wpa_s, sta);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh)
+{
+	struct hostapd_data *hapd = ifmsh->bss[0];
+
+	/* notify peers we're leaving */
+	ap_for_each_sta(hapd, mesh_mpm_plink_close, wpa_s);
+
+	hapd->num_plinks = 0;
+	hostapd_free_stas(hapd);
+}
+
+
+/* for mesh_rsn to indicate this peer has completed authentication, and we're
+ * ready to start AMPE */
+void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+	struct hostapd_data *data = wpa_s->ifmsh->bss[0];
+	struct hostapd_sta_add_params params;
+	struct sta_info *sta;
+	int ret;
+
+	sta = ap_get_sta(data, addr);
+	if (!sta) {
+		wpa_msg(wpa_s, MSG_DEBUG, "no such mesh peer");
+		return;
+	}
+
+	/* TODO: Should do nothing if this STA is already authenticated, but
+	 * the AP code already sets this flag. */
+	sta->flags |= WLAN_STA_AUTH;
+
+	mesh_rsn_init_ampe_sta(wpa_s, sta);
+
+	os_memset(&params, 0, sizeof(params));
+	params.addr = sta->addr;
+	params.flags = WPA_STA_AUTHENTICATED | WPA_STA_AUTHORIZED;
+	params.set = 1;
+
+	wpa_msg(wpa_s, MSG_DEBUG, "MPM authenticating " MACSTR,
+		MAC2STR(sta->addr));
+	ret = wpa_drv_sta_add(wpa_s, &params);
+	if (ret) {
+		wpa_msg(wpa_s, MSG_ERROR,
+			"Driver failed to set " MACSTR ": %d",
+			MAC2STR(sta->addr), ret);
+	}
+
+	if (!sta->my_lid)
+		mesh_mpm_init_link(wpa_s, sta);
+
+	mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
+}
+
+/*
+ * Initialize a sta_info structure for a peer and upload it into the driver
+ * in preparation for beginning authentication or peering. This is done when a
+ * Beacon (secure or open mesh) or a peering open frame (for open mesh) is
+ * received from the peer for the first time.
+ */
+static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s,
+					   const u8 *addr,
+					   struct ieee802_11_elems *elems)
+{
+	struct hostapd_sta_add_params params;
+	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+	struct hostapd_data *data = wpa_s->ifmsh->bss[0];
+	struct sta_info *sta;
+	int ret;
+
+	sta = ap_get_sta(data, addr);
+	if (!sta) {
+		sta = ap_sta_add(data, addr);
+		if (!sta)
+			return NULL;
+	}
+
+	/* initialize sta */
+	if (copy_supp_rates(wpa_s, sta, elems)) {
+		ap_free_sta(data, sta);
+		return NULL;
+	}
+
+	mesh_mpm_init_link(wpa_s, sta);
+
+#ifdef CONFIG_IEEE80211N
+	copy_sta_ht_capab(data, sta, elems->ht_capabilities);
+	update_ht_state(data, sta);
+#endif /* CONFIG_IEEE80211N */
+
+	/* insert into driver */
+	os_memset(&params, 0, sizeof(params));
+	params.supp_rates = sta->supported_rates;
+	params.supp_rates_len = sta->supported_rates_len;
+	params.addr = addr;
+	params.plink_state = sta->plink_state;
+	params.aid = sta->peer_lid;
+	params.listen_interval = 100;
+	params.ht_capabilities = sta->ht_capabilities;
+	params.flags |= WPA_STA_WMM;
+	params.flags_mask |= WPA_STA_AUTHENTICATED;
+	if (conf->security == MESH_CONF_SEC_NONE) {
+		params.flags |= WPA_STA_AUTHORIZED;
+		params.flags |= WPA_STA_AUTHENTICATED;
+	} else {
+		sta->flags |= WLAN_STA_MFP;
+		params.flags |= WPA_STA_MFP;
+	}
+
+	ret = wpa_drv_sta_add(wpa_s, &params);
+	if (ret) {
+		wpa_msg(wpa_s, MSG_ERROR,
+			"Driver failed to insert " MACSTR ": %d",
+			MAC2STR(addr), ret);
+		ap_free_sta(data, sta);
+		return NULL;
+	}
+
+	return sta;
+}
+
+
+void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+			    struct ieee802_11_elems *elems)
+{
+	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+	struct hostapd_data *data = wpa_s->ifmsh->bss[0];
+	struct sta_info *sta;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	sta = mesh_mpm_add_peer(wpa_s, addr, elems);
+	if (!sta)
+		return;
+
+	if (ssid && ssid->no_auto_peer) {
+		wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with "
+			MACSTR " because of no_auto_peer", MAC2STR(addr));
+		if (data->mesh_pending_auth) {
+			struct os_reltime age;
+			const struct ieee80211_mgmt *mgmt;
+			struct hostapd_frame_info fi;
+
+			mgmt = wpabuf_head(data->mesh_pending_auth);
+			os_reltime_age(&data->mesh_pending_auth_time, &age);
+			if (age.sec < 2 &&
+			    os_memcmp(mgmt->sa, addr, ETH_ALEN) == 0) {
+				wpa_printf(MSG_DEBUG,
+					   "mesh: Process pending Authentication frame from %u.%06u seconds ago",
+					   (unsigned int) age.sec,
+					   (unsigned int) age.usec);
+				os_memset(&fi, 0, sizeof(fi));
+				ieee802_11_mgmt(
+					data,
+					wpabuf_head(data->mesh_pending_auth),
+					wpabuf_len(data->mesh_pending_auth),
+					&fi);
+			}
+			wpabuf_free(data->mesh_pending_auth);
+			data->mesh_pending_auth = NULL;
+		}
+		return;
+	}
+
+	if (conf->security == MESH_CONF_SEC_NONE)
+		mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
+	else
+		mesh_rsn_auth_sae_sta(wpa_s, sta);
+}
+
+
+void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, struct rx_mgmt *rx_mgmt)
+{
+	struct hostapd_frame_info fi;
+
+	os_memset(&fi, 0, sizeof(fi));
+	fi.datarate = rx_mgmt->datarate;
+	fi.ssi_signal = rx_mgmt->ssi_signal;
+	ieee802_11_mgmt(wpa_s->ifmsh->bss[0], rx_mgmt->frame,
+			rx_mgmt->frame_len, &fi);
+}
+
+
+static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s,
+				 struct sta_info *sta)
+{
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+	u8 seq[6] = {};
+
+	wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR " established",
+		MAC2STR(sta->addr));
+
+	if (conf->security & MESH_CONF_SEC_AMPE) {
+		wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 0, 0,
+				seq, sizeof(seq), sta->mtk, sizeof(sta->mtk));
+		wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 1, 0,
+				seq, sizeof(seq),
+				sta->mgtk, sizeof(sta->mgtk));
+		wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr, 4, 0,
+				seq, sizeof(seq),
+				sta->mgtk, sizeof(sta->mgtk));
+
+		wpa_hexdump_key(MSG_DEBUG, "mtk:", sta->mtk, sizeof(sta->mtk));
+		wpa_hexdump_key(MSG_DEBUG, "mgtk:",
+				sta->mgtk, sizeof(sta->mgtk));
+	}
+
+	wpa_mesh_set_plink_state(wpa_s, sta, PLINK_ESTAB);
+	hapd->num_plinks++;
+
+	sta->flags |= WLAN_STA_ASSOC;
+
+	eloop_cancel_timeout(plink_timer, wpa_s, sta);
+
+	/* Send ctrl event */
+	wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR,
+		     MAC2STR(sta->addr));
+}
+
+
+static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta,
+			 enum plink_event event)
+{
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+	u16 reason = 0;
+
+	wpa_msg(wpa_s, MSG_DEBUG, "MPM " MACSTR " state %s event %s",
+		MAC2STR(sta->addr), mplstate[sta->plink_state],
+		mplevent[event]);
+
+	switch (sta->plink_state) {
+	case PLINK_LISTEN:
+		switch (event) {
+		case CLS_ACPT:
+			mesh_mpm_fsm_restart(wpa_s, sta);
+			break;
+		case OPN_ACPT:
+			mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_RCVD);
+			mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM,
+						   0);
+			break;
+		default:
+			break;
+		}
+		break;
+	case PLINK_OPEN_SENT:
+		switch (event) {
+		case OPN_RJCT:
+		case CNF_RJCT:
+			reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
+			/* fall-through */
+		case CLS_ACPT:
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+			if (!reason)
+				reason = WLAN_REASON_MESH_CLOSE_RCVD;
+			eloop_register_timeout(
+				conf->dot11MeshHoldingTimeout / 1000,
+				(conf->dot11MeshHoldingTimeout % 1000) * 1000,
+				plink_timer, wpa_s, sta);
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CLOSE, reason);
+			break;
+		case OPN_ACPT:
+			/* retry timer is left untouched */
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_OPEN_RCVD);
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CONFIRM, 0);
+			break;
+		case CNF_ACPT:
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_CNF_RCVD);
+			eloop_register_timeout(
+				conf->dot11MeshConfirmTimeout / 1000,
+				(conf->dot11MeshConfirmTimeout % 1000) * 1000,
+				plink_timer, wpa_s, sta);
+			break;
+		default:
+			break;
+		}
+		break;
+	case PLINK_OPEN_RCVD:
+		switch (event) {
+		case OPN_RJCT:
+		case CNF_RJCT:
+			reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
+			/* fall-through */
+		case CLS_ACPT:
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+			if (!reason)
+				reason = WLAN_REASON_MESH_CLOSE_RCVD;
+			eloop_register_timeout(
+				conf->dot11MeshHoldingTimeout / 1000,
+				(conf->dot11MeshHoldingTimeout % 1000) * 1000,
+				plink_timer, wpa_s, sta);
+			sta->mpm_close_reason = reason;
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CLOSE, reason);
+			break;
+		case OPN_ACPT:
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CONFIRM, 0);
+			break;
+		case CNF_ACPT:
+			if (conf->security & MESH_CONF_SEC_AMPE)
+				mesh_rsn_derive_mtk(wpa_s, sta);
+			mesh_mpm_plink_estab(wpa_s, sta);
+			break;
+		default:
+			break;
+		}
+		break;
+	case PLINK_CNF_RCVD:
+		switch (event) {
+		case OPN_RJCT:
+		case CNF_RJCT:
+			reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
+			/* fall-through */
+		case CLS_ACPT:
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+			if (!reason)
+				reason = WLAN_REASON_MESH_CLOSE_RCVD;
+			eloop_register_timeout(
+				conf->dot11MeshHoldingTimeout / 1000,
+				(conf->dot11MeshHoldingTimeout % 1000) * 1000,
+				plink_timer, wpa_s, sta);
+			sta->mpm_close_reason = reason;
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CLOSE, reason);
+			break;
+		case OPN_ACPT:
+			mesh_mpm_plink_estab(wpa_s, sta);
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CONFIRM, 0);
+			break;
+		default:
+			break;
+		}
+		break;
+	case PLINK_ESTAB:
+		switch (event) {
+		case CLS_ACPT:
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+			reason = WLAN_REASON_MESH_CLOSE_RCVD;
+
+			eloop_register_timeout(
+				conf->dot11MeshHoldingTimeout / 1000,
+				(conf->dot11MeshHoldingTimeout % 1000) * 1000,
+				plink_timer, wpa_s, sta);
+			sta->mpm_close_reason = reason;
+
+			wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR
+				" closed with reason %d",
+				MAC2STR(sta->addr), reason);
+
+			wpa_msg_ctrl(wpa_s, MSG_INFO,
+				     MESH_PEER_DISCONNECTED MACSTR,
+				     MAC2STR(sta->addr));
+
+			hapd->num_plinks--;
+
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CLOSE, reason);
+			break;
+		case OPN_ACPT:
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CONFIRM, 0);
+			break;
+		default:
+			break;
+		}
+		break;
+	case PLINK_HOLDING:
+		switch (event) {
+		case CLS_ACPT:
+			mesh_mpm_fsm_restart(wpa_s, sta);
+			break;
+		case OPN_ACPT:
+		case CNF_ACPT:
+		case OPN_RJCT:
+		case CNF_RJCT:
+			reason = sta->mpm_close_reason;
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CLOSE, reason);
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Unsupported MPM event %s for state %s",
+			mplevent[event], mplstate[sta->plink_state]);
+		break;
+	}
+}
+
+
+void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
+			const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	u8 action_field;
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+	struct mesh_conf *mconf = wpa_s->ifmsh->mconf;
+	struct sta_info *sta;
+	u16 plid = 0, llid = 0;
+	enum plink_event event;
+	struct ieee802_11_elems elems;
+	struct mesh_peer_mgmt_ie peer_mgmt_ie;
+	const u8 *ies;
+	size_t ie_len;
+	int ret;
+
+	if (mgmt->u.action.category != WLAN_ACTION_SELF_PROTECTED)
+		return;
+
+	action_field = mgmt->u.action.u.slf_prot_action.action;
+	if (action_field != PLINK_OPEN &&
+	    action_field != PLINK_CONFIRM &&
+	    action_field != PLINK_CLOSE)
+		return;
+
+	ies = mgmt->u.action.u.slf_prot_action.variable;
+	ie_len = (const u8 *) mgmt + len -
+		mgmt->u.action.u.slf_prot_action.variable;
+
+	/* at least expect mesh id and peering mgmt */
+	if (ie_len < 2 + 2) {
+		wpa_printf(MSG_DEBUG,
+			   "MPM: Ignore too short action frame %u ie_len %u",
+			   action_field, (unsigned int) ie_len);
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "MPM: Received PLINK action %u", action_field);
+
+	if (action_field == PLINK_OPEN || action_field == PLINK_CONFIRM) {
+		wpa_printf(MSG_DEBUG, "MPM: Capability 0x%x",
+			   WPA_GET_LE16(ies));
+		ies += 2;	/* capability */
+		ie_len -= 2;
+	}
+	if (action_field == PLINK_CONFIRM) {
+		wpa_printf(MSG_DEBUG, "MPM: AID 0x%x", WPA_GET_LE16(ies));
+		ies += 2;	/* aid */
+		ie_len -= 2;
+	}
+
+	/* check for mesh peering, mesh id and mesh config IEs */
+	if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "MPM: Failed to parse PLINK IEs");
+		return;
+	}
+	if (!elems.peer_mgmt) {
+		wpa_printf(MSG_DEBUG,
+			   "MPM: No Mesh Peering Management element");
+		return;
+	}
+	if (action_field != PLINK_CLOSE) {
+		if (!elems.mesh_id || !elems.mesh_config) {
+			wpa_printf(MSG_DEBUG,
+				   "MPM: No Mesh ID or Mesh Configuration element");
+			return;
+		}
+
+		if (!matches_local(wpa_s, &elems)) {
+			wpa_printf(MSG_DEBUG,
+				   "MPM: Mesh ID or Mesh Configuration element do not match local MBSS");
+			return;
+		}
+	}
+
+	ret = mesh_mpm_parse_peer_mgmt(wpa_s, action_field,
+				       elems.peer_mgmt,
+				       elems.peer_mgmt_len,
+				       &peer_mgmt_ie);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "MPM: Mesh parsing rejected frame");
+		return;
+	}
+
+	/* the sender's llid is our plid and vice-versa */
+	plid = WPA_GET_LE16(peer_mgmt_ie.llid);
+	if (peer_mgmt_ie.plid)
+		llid = WPA_GET_LE16(peer_mgmt_ie.plid);
+	wpa_printf(MSG_DEBUG, "MPM: plid=0x%x llid=0x%x", plid, llid);
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+
+	/*
+	 * If this is an open frame from an unknown STA, and this is an
+	 * open mesh, then go ahead and add the peer before proceeding.
+	 */
+	if (!sta && action_field == PLINK_OPEN &&
+	    !(mconf->security & MESH_CONF_SEC_AMPE))
+		sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems);
+
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "MPM: No STA entry for peer");
+		return;
+	}
+
+#ifdef CONFIG_SAE
+	/* peer is in sae_accepted? */
+	if (sta->sae && sta->sae->state != SAE_ACCEPTED) {
+		wpa_printf(MSG_DEBUG, "MPM: SAE not yet accepted for peer");
+		return;
+	}
+#endif /* CONFIG_SAE */
+
+	if (!sta->my_lid)
+		mesh_mpm_init_link(wpa_s, sta);
+
+	if ((mconf->security & MESH_CONF_SEC_AMPE) &&
+	    mesh_rsn_process_ampe(wpa_s, sta, &elems,
+				  &mgmt->u.action.category,
+				  ies, ie_len)) {
+		wpa_printf(MSG_DEBUG, "MPM: RSN process rejected frame");
+		return;
+	}
+
+	if (sta->plink_state == PLINK_BLOCKED) {
+		wpa_printf(MSG_DEBUG, "MPM: PLINK_BLOCKED");
+		return;
+	}
+
+	/* Now we will figure out the appropriate event... */
+	switch (action_field) {
+	case PLINK_OPEN:
+		if (plink_free_count(hapd) == 0) {
+			event = OPN_IGNR;
+			wpa_printf(MSG_INFO,
+				   "MPM: Peer link num over quota(%d)",
+				   hapd->max_plinks);
+		} else if (sta->peer_lid && sta->peer_lid != plid) {
+			event = OPN_IGNR;
+		} else {
+			sta->peer_lid = plid;
+			event = OPN_ACPT;
+		}
+		break;
+	case PLINK_CONFIRM:
+		if (plink_free_count(hapd) == 0) {
+			event = CNF_IGNR;
+			wpa_printf(MSG_INFO,
+				   "MPM: Peer link num over quota(%d)",
+				   hapd->max_plinks);
+		} else if (sta->my_lid != llid ||
+			   (sta->peer_lid && sta->peer_lid != plid)) {
+			event = CNF_IGNR;
+		} else {
+			if (!sta->peer_lid)
+				sta->peer_lid = plid;
+			event = CNF_ACPT;
+		}
+		break;
+	case PLINK_CLOSE:
+		if (sta->plink_state == PLINK_ESTAB)
+			/* Do not check for llid or plid. This does not
+			 * follow the standard but since multiple plinks
+			 * per cand are not supported, it is necessary in
+			 * order to avoid a livelock when MP A sees an
+			 * establish peer link to MP B but MP B does not
+			 * see it. This can be caused by a timeout in
+			 * B's peer link establishment or B being
+			 * restarted.
+			 */
+			event = CLS_ACPT;
+		else if (sta->peer_lid != plid)
+			event = CLS_IGNR;
+		else if (peer_mgmt_ie.plid && sta->my_lid != llid)
+			event = CLS_IGNR;
+		else
+			event = CLS_ACPT;
+		break;
+	default:
+		/*
+		 * This cannot be hit due to the action_field check above, but
+		 * compilers may not be able to figure that out and can warn
+		 * about uninitialized event below.
+		 */
+		return;
+	}
+	mesh_mpm_fsm(wpa_s, sta, event);
+}
+
+
+/* called by ap_free_sta */
+void mesh_mpm_free_sta(struct sta_info *sta)
+{
+	eloop_cancel_timeout(plink_timer, ELOOP_ALL_CTX, sta);
+	eloop_cancel_timeout(mesh_auth_timer, ELOOP_ALL_CTX, sta);
+}
diff --git a/hostap/wpa_supplicant/mesh_mpm.h b/hostap/wpa_supplicant/mesh_mpm.h
new file mode 100644
index 0000000..7ebaef0
--- /dev/null
+++ b/hostap/wpa_supplicant/mesh_mpm.h
@@ -0,0 +1,43 @@
+/*
+ * WPA Supplicant - Basic mesh peer management
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MESH_MPM_H
+#define MESH_MPM_H
+
+/* notify MPM of new mesh peer to be inserted in MPM and driver */
+void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+			    struct ieee802_11_elems *elems);
+void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh);
+void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr);
+void mesh_mpm_free_sta(struct sta_info *sta);
+void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s,
+			      struct sta_info *sta,
+			      enum mesh_plink_state state);
+
+#ifdef CONFIG_MESH
+
+void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
+			const struct ieee80211_mgmt *mgmt, size_t len);
+void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, struct rx_mgmt *rx_mgmt);
+
+#else /* CONFIG_MESH */
+
+static inline void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
+				      const struct ieee80211_mgmt *mgmt,
+				      size_t len)
+{
+}
+
+static inline void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s,
+				    struct rx_mgmt *rx_mgmt)
+{
+}
+
+#endif /* CONFIG_MESH */
+
+#endif /* MESH_MPM_H */
diff --git a/hostap/wpa_supplicant/mesh_rsn.c b/hostap/wpa_supplicant/mesh_rsn.c
new file mode 100644
index 0000000..747f1ae
--- /dev/null
+++ b/hostap/wpa_supplicant/mesh_rsn.c
@@ -0,0 +1,576 @@
+/*
+ * WPA Supplicant - Mesh RSN routines
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * 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 "crypto/sha256.h"
+#include "crypto/random.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "rsn_supp/wpa.h"
+#include "ap/hostapd.h"
+#include "ap/wpa_auth.h"
+#include "ap/sta_info.h"
+#include "ap/ieee802_11.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "wpas_glue.h"
+#include "mesh_mpm.h"
+#include "mesh_rsn.h"
+
+#define MESH_AUTH_TIMEOUT 10
+#define MESH_AUTH_RETRY 3
+#define MESH_AUTH_BLOCK_DURATION 3600
+
+void mesh_auth_timer(void *eloop_ctx, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct sta_info *sta = user_data;
+
+	if (sta->sae->state != SAE_ACCEPTED) {
+		wpa_printf(MSG_DEBUG, "AUTH: Re-authenticate with " MACSTR
+			   " (attempt %d) ",
+			   MAC2STR(sta->addr), sta->sae_auth_retry);
+		wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_FAILURE "addr=" MACSTR,
+			MAC2STR(sta->addr));
+		if (sta->sae_auth_retry < MESH_AUTH_RETRY) {
+			mesh_rsn_auth_sae_sta(wpa_s, sta);
+		} else {
+			if (sta->sae_auth_retry > MESH_AUTH_RETRY) {
+				ap_free_sta(wpa_s->ifmsh->bss[0], sta);
+				return;
+			}
+
+			/* block the STA if exceeded the number of attempts */
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_BLOCKED);
+			sta->sae->state = SAE_NOTHING;
+			if (wpa_s->mesh_auth_block_duration <
+			    MESH_AUTH_BLOCK_DURATION)
+				wpa_s->mesh_auth_block_duration += 60;
+			eloop_register_timeout(wpa_s->mesh_auth_block_duration,
+					       0, mesh_auth_timer, wpa_s, sta);
+			wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_BLOCKED "addr="
+				MACSTR " duration=%d",
+				MAC2STR(sta->addr),
+				wpa_s->mesh_auth_block_duration);
+		}
+		sta->sae_auth_retry++;
+	}
+}
+
+
+static void auth_logger(void *ctx, const u8 *addr, logger_level level,
+			const char *txt)
+{
+	if (addr)
+		wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s",
+			   MAC2STR(addr), txt);
+	else
+		wpa_printf(MSG_DEBUG, "AUTH: %s", txt);
+}
+
+
+static const u8 *auth_get_psk(void *ctx, const u8 *addr,
+			      const u8 *p2p_dev_addr, const u8 *prev_psk)
+{
+	struct mesh_rsn *mesh_rsn = ctx;
+	struct hostapd_data *hapd = mesh_rsn->wpa_s->ifmsh->bss[0];
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
+		   __func__, MAC2STR(addr), prev_psk);
+
+	if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
+		if (!sta->sae || prev_psk)
+			return NULL;
+		return sta->sae->pmk;
+	}
+
+	return NULL;
+}
+
+
+static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
+			const u8 *addr, int idx, u8 *key, size_t key_len)
+{
+	struct mesh_rsn *mesh_rsn = ctx;
+	u8 seq[6];
+
+	os_memset(seq, 0, sizeof(seq));
+
+	if (addr) {
+		wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d addr=" MACSTR
+			   " key_idx=%d)",
+			   __func__, alg, MAC2STR(addr), idx);
+	} else {
+		wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d key_idx=%d)",
+			   __func__, alg, idx);
+	}
+	wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len);
+
+	return wpa_drv_set_key(mesh_rsn->wpa_s, alg, addr, idx,
+			       1, seq, 6, key, key_len);
+}
+
+
+static int auth_start_ampe(void *ctx, const u8 *addr)
+{
+	struct mesh_rsn *mesh_rsn = ctx;
+	struct hostapd_data *hapd;
+	struct sta_info *sta;
+
+	if (mesh_rsn->wpa_s->current_ssid->mode != WPAS_MODE_MESH)
+		return -1;
+
+	hapd = mesh_rsn->wpa_s->ifmsh->bss[0];
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		eloop_cancel_timeout(mesh_auth_timer, mesh_rsn->wpa_s, sta);
+
+	mesh_mpm_auth_peer(mesh_rsn->wpa_s, addr);
+	return 0;
+}
+
+
+static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr)
+{
+	struct wpa_auth_config conf;
+	struct wpa_auth_callbacks cb;
+	u8 seq[6] = {};
+
+	wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
+
+	os_memset(&conf, 0, sizeof(conf));
+	conf.wpa = 2;
+	conf.wpa_key_mgmt = WPA_KEY_MGMT_SAE;
+	conf.wpa_pairwise = WPA_CIPHER_CCMP;
+	conf.rsn_pairwise = WPA_CIPHER_CCMP;
+	conf.wpa_group = WPA_CIPHER_CCMP;
+	conf.eapol_version = 0;
+	conf.wpa_group_rekey = -1;
+
+	os_memset(&cb, 0, sizeof(cb));
+	cb.ctx = rsn;
+	cb.logger = auth_logger;
+	cb.get_psk = auth_get_psk;
+	cb.set_key = auth_set_key;
+	cb.start_ampe = auth_start_ampe;
+
+	rsn->auth = wpa_init(addr, &conf, &cb);
+	if (rsn->auth == NULL) {
+		wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
+		return -1;
+	}
+
+	/* TODO: support rekeying */
+	if (random_get_bytes(rsn->mgtk, 16) < 0) {
+		wpa_deinit(rsn->auth);
+		return -1;
+	}
+
+	/* group mgmt */
+	wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL, 4, 1,
+			seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk));
+
+	/* group privacy / data frames */
+	wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, 1, 1,
+			seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk));
+
+	return 0;
+}
+
+
+static void mesh_rsn_deinit(struct mesh_rsn *rsn)
+{
+	os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk));
+	if (rsn->auth)
+		wpa_deinit(rsn->auth);
+}
+
+
+struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s,
+				    struct mesh_conf *conf)
+{
+	struct mesh_rsn *mesh_rsn;
+	struct hostapd_data *bss = wpa_s->ifmsh->bss[0];
+	const u8 *ie;
+	size_t ie_len;
+
+	mesh_rsn = os_zalloc(sizeof(*mesh_rsn));
+	if (mesh_rsn == NULL)
+		return NULL;
+	mesh_rsn->wpa_s = wpa_s;
+
+	if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) {
+		mesh_rsn_deinit(mesh_rsn);
+		os_free(mesh_rsn);
+		return NULL;
+	}
+
+	bss->wpa_auth = mesh_rsn->auth;
+
+	ie = wpa_auth_get_wpa_ie(mesh_rsn->auth, &ie_len);
+	conf->rsn_ie = (u8 *) ie;
+	conf->rsn_ie_len = ie_len;
+
+	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+
+	return mesh_rsn;
+}
+
+
+static int index_within_array(const int *array, int idx)
+{
+	int i;
+
+	for (i = 0; i < idx; i++) {
+		if (array[i] == -1)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+static int mesh_rsn_sae_group(struct wpa_supplicant *wpa_s,
+			      struct sae_data *sae)
+{
+	int *groups = wpa_s->ifmsh->bss[0]->conf->sae_groups;
+
+	/* Configuration may have changed, so validate current index */
+	if (!index_within_array(groups, wpa_s->mesh_rsn->sae_group_index))
+		return -1;
+
+	for (;;) {
+		int group = groups[wpa_s->mesh_rsn->sae_group_index];
+
+		if (group <= 0)
+			break;
+		if (sae_set_group(sae, group) == 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
+				sae->group);
+			return 0;
+		}
+		wpa_s->mesh_rsn->sae_group_index++;
+	}
+
+	return -1;
+}
+
+
+static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s,
+				     struct wpa_ssid *ssid,
+				     struct sta_info *sta)
+{
+	if (ssid->passphrase == NULL) {
+		wpa_msg(wpa_s, MSG_DEBUG, "SAE: No password available");
+		return -1;
+	}
+
+	if (mesh_rsn_sae_group(wpa_s, sta->sae) < 0) {
+		wpa_msg(wpa_s, MSG_DEBUG, "SAE: Failed to select group");
+		return -1;
+	}
+
+	return sae_prepare_commit(wpa_s->own_addr, sta->addr,
+				  (u8 *) ssid->passphrase,
+				  os_strlen(ssid->passphrase), sta->sae);
+}
+
+
+/* initiate new SAE authentication with sta */
+int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s,
+			  struct sta_info *sta)
+{
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	unsigned int rnd;
+	int ret;
+
+	if (!ssid) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"AUTH: No current_ssid known to initiate new SAE");
+		return -1;
+	}
+
+	if (!sta->sae) {
+		sta->sae = os_zalloc(sizeof(*sta->sae));
+		if (sta->sae == NULL)
+			return -1;
+	}
+
+	if (mesh_rsn_build_sae_commit(wpa_s, ssid, sta))
+		return -1;
+
+	wpa_msg(wpa_s, MSG_DEBUG,
+		"AUTH: started authentication with SAE peer: " MACSTR,
+		MAC2STR(sta->addr));
+
+	wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
+	ret = auth_sae_init_committed(hapd, sta);
+	if (ret)
+		return ret;
+
+	eloop_cancel_timeout(mesh_auth_timer, wpa_s, sta);
+	rnd = rand() % MESH_AUTH_TIMEOUT;
+	eloop_register_timeout(MESH_AUTH_TIMEOUT + rnd, 0, mesh_auth_timer,
+			       wpa_s, sta);
+	return 0;
+}
+
+
+void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid)
+{
+	/* don't expect wpa auth to cache the pmkid for now */
+	rsn_pmkid(sta->sae->pmk, PMK_LEN, rsn->wpa_s->own_addr,
+		  sta->addr, pmkid,
+		  wpa_key_mgmt_sha256(wpa_auth_sta_key_mgmt(sta->wpa_sm)));
+}
+
+
+static void
+mesh_rsn_derive_aek(struct mesh_rsn *rsn, struct sta_info *sta)
+{
+	u8 *myaddr = rsn->wpa_s->own_addr;
+	u8 *peer = sta->addr;
+	u8 *addr1 = peer, *addr2 = myaddr;
+	u8 context[AES_BLOCK_SIZE];
+
+	/* SAE */
+	RSN_SELECTOR_PUT(context, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP));
+
+	if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) {
+		addr1 = myaddr;
+		addr2 = peer;
+	}
+	os_memcpy(context + 4, addr1, ETH_ALEN);
+	os_memcpy(context + 10, addr2, ETH_ALEN);
+
+	sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), "AEK Derivation",
+		   context, sizeof(context), sta->aek, sizeof(sta->aek));
+}
+
+
+/* derive mesh temporal key from pmk */
+int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta)
+{
+	u8 *ptr;
+	u8 *min, *max;
+	u16 min_lid, max_lid;
+	size_t nonce_len = sizeof(sta->my_nonce);
+	size_t lid_len = sizeof(sta->my_lid);
+	u8 *myaddr = wpa_s->own_addr;
+	u8 *peer = sta->addr;
+	/* 2 nonces, 2 linkids, akm suite, 2 mac addrs */
+	u8 context[64 + 4 + 4 + 12];
+
+	ptr = context;
+	if (os_memcmp(sta->my_nonce, sta->peer_nonce, nonce_len) < 0) {
+		min = sta->my_nonce;
+		max = sta->peer_nonce;
+	} else {
+		min = sta->peer_nonce;
+		max = sta->my_nonce;
+	}
+	os_memcpy(ptr, min, nonce_len);
+	os_memcpy(ptr + nonce_len, max, nonce_len);
+	ptr += 2 * nonce_len;
+
+	if (sta->my_lid < sta->peer_lid) {
+		min_lid = host_to_le16(sta->my_lid);
+		max_lid = host_to_le16(sta->peer_lid);
+	} else {
+		min_lid = host_to_le16(sta->peer_lid);
+		max_lid = host_to_le16(sta->my_lid);
+	}
+	os_memcpy(ptr, &min_lid, lid_len);
+	os_memcpy(ptr + lid_len, &max_lid, lid_len);
+	ptr += 2 * lid_len;
+
+	/* SAE */
+	RSN_SELECTOR_PUT(ptr, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP));
+	ptr += 4;
+
+	if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) {
+		min = myaddr;
+		max = peer;
+	} else {
+		min = peer;
+		max = myaddr;
+	}
+	os_memcpy(ptr, min, ETH_ALEN);
+	os_memcpy(ptr + ETH_ALEN, max, ETH_ALEN);
+
+	sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk),
+		   "Temporal Key Derivation", context, sizeof(context),
+		   sta->mtk, sizeof(sta->mtk));
+	return 0;
+}
+
+
+void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta)
+{
+	if (random_get_bytes(sta->my_nonce, 32) < 0) {
+		wpa_printf(MSG_INFO, "mesh: Failed to derive random nonce");
+		/* TODO: How to handle this more cleanly? */
+	}
+	os_memset(sta->peer_nonce, 0, 32);
+	mesh_rsn_derive_aek(wpa_s->mesh_rsn, sta);
+}
+
+
+/* insert AMPE and encrypted MIC at @ie.
+ * @mesh_rsn: mesh RSN context
+ * @sta: STA we're sending to
+ * @cat: pointer to category code in frame header.
+ * @buf: wpabuf to add encrypted AMPE and MIC to.
+ * */
+int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta,
+			   const u8 *cat, struct wpabuf *buf)
+{
+	struct ieee80211_ampe_ie *ampe;
+	u8 const *ie = wpabuf_head_u8(buf) + wpabuf_len(buf);
+	u8 *ampe_ie = NULL, *mic_ie = NULL, *mic_payload;
+	const u8 *aad[] = { rsn->wpa_s->own_addr, sta->addr, cat };
+	const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, ie - cat };
+	int ret = 0;
+
+	if (AES_BLOCK_SIZE + 2 + sizeof(*ampe) + 2 > wpabuf_tailroom(buf)) {
+		wpa_printf(MSG_ERROR, "protect frame: buffer too small");
+		return -EINVAL;
+	}
+
+	ampe_ie = os_zalloc(2 + sizeof(*ampe));
+	if (!ampe_ie) {
+		wpa_printf(MSG_ERROR, "protect frame: out of memory");
+		return -ENOMEM;
+	}
+
+	mic_ie = os_zalloc(2 + AES_BLOCK_SIZE);
+	if (!mic_ie) {
+		wpa_printf(MSG_ERROR, "protect frame: out of memory");
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	/*  IE: AMPE */
+	ampe_ie[0] = WLAN_EID_AMPE;
+	ampe_ie[1] = sizeof(*ampe);
+	ampe = (struct ieee80211_ampe_ie *) (ampe_ie + 2);
+
+	RSN_SELECTOR_PUT(ampe->selected_pairwise_suite,
+		     wpa_cipher_to_suite(WPA_PROTO_RSN, WPA_CIPHER_CCMP));
+	os_memcpy(ampe->local_nonce, sta->my_nonce, 32);
+	os_memcpy(ampe->peer_nonce, sta->peer_nonce, 32);
+	/* incomplete: see 13.5.4 */
+	/* TODO: static mgtk for now since we don't support rekeying! */
+	os_memcpy(ampe->mgtk, rsn->mgtk, 16);
+	/*  TODO: Populate Key RSC */
+	/*  expire in 13 decades or so */
+	os_memset(ampe->key_expiration, 0xff, 4);
+
+	/* IE: MIC */
+	mic_ie[0] = WLAN_EID_MIC;
+	mic_ie[1] = AES_BLOCK_SIZE;
+	wpabuf_put_data(buf, mic_ie, 2);
+	/* MIC field is output ciphertext */
+
+	/* encrypt after MIC */
+	mic_payload = (u8 *) wpabuf_put(buf, 2 + sizeof(*ampe) +
+					AES_BLOCK_SIZE);
+
+	if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + sizeof(*ampe), 3,
+			    aad, aad_len, mic_payload)) {
+		wpa_printf(MSG_ERROR, "protect frame: failed to encrypt");
+		ret = -ENOMEM;
+		goto free;
+	}
+
+free:
+	os_free(ampe_ie);
+	os_free(mic_ie);
+
+	return ret;
+}
+
+
+int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
+			  struct ieee802_11_elems *elems, const u8 *cat,
+			  const u8 *start, size_t elems_len)
+{
+	int ret = 0;
+	struct ieee80211_ampe_ie *ampe;
+	u8 null_nonce[32] = {};
+	u8 ampe_eid;
+	u8 ampe_ie_len;
+	u8 *ampe_buf, *crypt = NULL;
+	size_t crypt_len;
+	const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat };
+	const size_t aad_len[] = { ETH_ALEN, ETH_ALEN,
+				   (elems->mic - 2) - cat };
+
+	if (!elems->mic || elems->mic_len < AES_BLOCK_SIZE) {
+		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing mic ie");
+		return -1;
+	}
+
+	ampe_buf = (u8 *) elems->mic + elems->mic_len;
+	if ((int) elems_len < ampe_buf - start)
+		return -1;
+
+	crypt_len = elems_len - (elems->mic - start);
+	if (crypt_len < 2) {
+		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie");
+		return -1;
+	}
+
+	/* crypt is modified by siv_decrypt */
+	crypt = os_zalloc(crypt_len);
+	if (!crypt) {
+		wpa_printf(MSG_ERROR, "Mesh RSN: out of memory");
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	os_memcpy(crypt, elems->mic, crypt_len);
+
+	if (aes_siv_decrypt(sta->aek, crypt, crypt_len, 3,
+			    aad, aad_len, ampe_buf)) {
+		wpa_printf(MSG_ERROR, "Mesh RSN: frame verification failed!");
+		ret = -1;
+		goto free;
+	}
+
+	ampe_eid = *ampe_buf++;
+	ampe_ie_len = *ampe_buf++;
+
+	if (ampe_eid != WLAN_EID_AMPE ||
+	    ampe_ie_len < sizeof(struct ieee80211_ampe_ie)) {
+		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid ampe ie");
+		ret = -1;
+		goto free;
+	}
+
+	ampe = (struct ieee80211_ampe_ie *) ampe_buf;
+	if (os_memcmp(ampe->peer_nonce, null_nonce, 32) != 0 &&
+	    os_memcmp(ampe->peer_nonce, sta->my_nonce, 32) != 0) {
+		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid peer nonce");
+		ret = -1;
+		goto free;
+	}
+	os_memcpy(sta->peer_nonce, ampe->local_nonce,
+		  sizeof(ampe->local_nonce));
+	os_memcpy(sta->mgtk, ampe->mgtk, sizeof(ampe->mgtk));
+
+	/* todo parse mgtk expiration */
+free:
+	os_free(crypt);
+	return ret;
+}
diff --git a/hostap/wpa_supplicant/mesh_rsn.h b/hostap/wpa_supplicant/mesh_rsn.h
new file mode 100644
index 0000000..b1471b2
--- /dev/null
+++ b/hostap/wpa_supplicant/mesh_rsn.h
@@ -0,0 +1,36 @@
+/*
+ * WPA Supplicant - Mesh RSN routines
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MESH_RSN_H
+#define MESH_RSN_H
+
+struct mesh_rsn {
+	struct wpa_supplicant *wpa_s;
+	struct wpa_authenticator *auth;
+	u8 mgtk[16];
+#ifdef CONFIG_SAE
+	struct wpabuf *sae_token;
+	int sae_group_index;
+#endif /* CONFIG_SAE */
+};
+
+struct mesh_rsn * mesh_rsn_auth_init(struct wpa_supplicant *wpa_s,
+				     struct mesh_conf *conf);
+int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta);
+int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta);
+void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid);
+void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s,
+			    struct sta_info *sta);
+int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta,
+			   const u8 *cat, struct wpabuf *buf);
+int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
+			  struct ieee802_11_elems *elems, const u8 *cat,
+			  const u8 *start, size_t elems_len);
+void mesh_auth_timer(void *eloop_ctx, void *user_data);
+
+#endif /* MESH_RSN_H */
diff --git a/hostap/wpa_supplicant/nfc_pw_token.c b/hostap/wpa_supplicant/nfc_pw_token.c
new file mode 100644
index 0000000..11afb5b
--- /dev/null
+++ b/hostap/wpa_supplicant/nfc_pw_token.c
@@ -0,0 +1,83 @@
+/*
+ * nfc_pw_token - Tool for building NFC password tokens for WPS
+ * Copyright (c) 2012, 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 "utils/common.h"
+#include "crypto/random.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "wps_supplicant.h"
+
+
+static void print_bin(const char *title, const struct wpabuf *buf)
+{
+	size_t i, len;
+	const u8 *pos;
+
+	if (buf == NULL)
+		return;
+
+	printf("%s=", title);
+
+	pos = wpabuf_head(buf);
+	len = wpabuf_len(buf);
+	for (i = 0; i < len; i++)
+		printf("%02X", *pos++);
+
+	printf("\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct wpa_supplicant wpa_s;
+	int ret = -1;
+	struct wpabuf *buf = NULL, *ndef = NULL;
+	char txt[1000];
+
+	if (os_program_init())
+		return -1;
+	random_init(NULL);
+
+	os_memset(&wpa_s, 0, sizeof(wpa_s));
+	wpa_s.conf = os_zalloc(sizeof(*wpa_s.conf));
+	if (wpa_s.conf == NULL)
+		goto fail;
+
+	buf = wpas_wps_nfc_token(&wpa_s, 0);
+	if (buf == NULL)
+		goto fail;
+
+	ndef = ndef_build_wifi(buf);
+	if (ndef == NULL)
+		goto fail;
+
+	wpa_snprintf_hex_uppercase(txt, sizeof(txt), wpabuf_head(buf),
+				   wpabuf_len(buf));
+	printf("#WPS=%s\n", txt);
+
+	wpa_snprintf_hex_uppercase(txt, sizeof(txt), wpabuf_head(ndef),
+				   wpabuf_len(ndef));
+	printf("#NDEF=%s\n", txt);
+
+	printf("wps_nfc_dev_pw_id=%d\n", wpa_s.conf->wps_nfc_dev_pw_id);
+	print_bin("wps_nfc_dh_pubkey", wpa_s.conf->wps_nfc_dh_pubkey);
+	print_bin("wps_nfc_dh_privkey", wpa_s.conf->wps_nfc_dh_privkey);
+	print_bin("wps_nfc_dev_pw", wpa_s.conf->wps_nfc_dev_pw);
+
+	ret = 0;
+fail:
+	wpabuf_free(ndef);
+	wpabuf_free(buf);
+	wpa_config_free(wpa_s.conf);
+	random_deinit();
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/hostap/wpa_supplicant/nmake.mak b/hostap/wpa_supplicant/nmake.mak
new file mode 100644
index 0000000..80e0ac8
--- /dev/null
+++ b/hostap/wpa_supplicant/nmake.mak
@@ -0,0 +1,240 @@
+# Makefile for Microsoft nmake to build wpa_supplicant
+
+# This can be run in Visual Studio 2005 Command Prompt
+
+# Note: Make sure that cl.exe is configured to include Platform SDK
+# include and lib directories (vsvars32.bat)
+
+all: wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe wpasvc.exe win_if_list.exe
+
+# Root directory for WinPcap developer's pack
+# (http://www.winpcap.org/install/bin/WpdPack_3_1.zip)
+WINPCAPDIR=C:\dev\WpdPack
+
+# Root directory for OpenSSL
+# (http://www.openssl.org/source/openssl-0.9.8a.tar.gz)
+# Build and installed following instructions in INSTALL.W32
+# Note: If EAP-FAST is included in the build, OpenSSL needs to be patched to
+# support it (openssl-tls-extensions.patch)
+# Alternatively, see README-Windows.txt for information about binary
+# installation package for OpenSSL.
+OPENSSLDIR=C:\dev\openssl
+
+CC = cl
+OBJDIR = objs
+
+CFLAGS = /DCONFIG_NATIVE_WINDOWS
+CFLAGS = $(CFLAGS) /DCONFIG_NDIS_EVENTS_INTEGRATED
+CFLAGS = $(CFLAGS) /DCONFIG_ANSI_C_EXTRA
+CFLAGS = $(CFLAGS) /DCONFIG_WINPCAP
+CFLAGS = $(CFLAGS) /DIEEE8021X_EAPOL
+CFLAGS = $(CFLAGS) /DPKCS12_FUNCS
+CFLAGS = $(CFLAGS) /DEAP_MD5
+CFLAGS = $(CFLAGS) /DEAP_TLS
+CFLAGS = $(CFLAGS) /DEAP_MSCHAPv2
+CFLAGS = $(CFLAGS) /DEAP_PEAP
+CFLAGS = $(CFLAGS) /DEAP_TTLS
+CFLAGS = $(CFLAGS) /DEAP_GTC
+CFLAGS = $(CFLAGS) /DEAP_OTP
+CFLAGS = $(CFLAGS) /DEAP_SIM
+CFLAGS = $(CFLAGS) /DEAP_LEAP
+CFLAGS = $(CFLAGS) /DEAP_PSK
+CFLAGS = $(CFLAGS) /DEAP_AKA
+#CFLAGS = $(CFLAGS) /DEAP_FAST
+CFLAGS = $(CFLAGS) /DEAP_PAX
+CFLAGS = $(CFLAGS) /DEAP_TNC
+CFLAGS = $(CFLAGS) /DPCSC_FUNCS
+CFLAGS = $(CFLAGS) /DCONFIG_CTRL_IFACE
+CFLAGS = $(CFLAGS) /DCONFIG_CTRL_IFACE_NAMED_PIPE
+CFLAGS = $(CFLAGS) /DCONFIG_DRIVER_NDIS
+CFLAGS = $(CFLAGS) /I..\src /I..\src\utils
+CFLAGS = $(CFLAGS) /I.
+CFLAGS = $(CFLAGS) /DWIN32
+CFLAGS = $(CFLAGS) /Fo$(OBJDIR)\\ /c
+CFLAGS = $(CFLAGS) /W3
+
+#CFLAGS = $(CFLAGS) /WX
+
+# VS 2005 complains about lot of deprecated string functions; let's ignore them
+# at least for now since snprintf and strncpy can be used in a safe way
+CFLAGS = $(CFLAGS) /D_CRT_SECURE_NO_DEPRECATE
+
+OBJS = \
+	$(OBJDIR)\os_win32.obj \
+	$(OBJDIR)\eloop_win.obj \
+	$(OBJDIR)\sha1.obj \
+	$(OBJDIR)\sha1-tlsprf.obj \
+	$(OBJDIR)\sha1-pbkdf2.obj \
+	$(OBJDIR)\md5.obj \
+	$(OBJDIR)\aes-cbc.obj \
+	$(OBJDIR)\aes-ctr.obj \
+	$(OBJDIR)\aes-eax.obj \
+	$(OBJDIR)\aes-encblock.obj \
+	$(OBJDIR)\aes-omac1.obj \
+	$(OBJDIR)\aes-unwrap.obj \
+	$(OBJDIR)\aes-wrap.obj \
+	$(OBJDIR)\common.obj \
+	$(OBJDIR)\wpa_debug.obj \
+	$(OBJDIR)\wpabuf.obj \
+	$(OBJDIR)\wpa_supplicant.obj \
+	$(OBJDIR)\wpa.obj \
+	$(OBJDIR)\wpa_common.obj \
+	$(OBJDIR)\wpa_ie.obj \
+	$(OBJDIR)\preauth.obj \
+	$(OBJDIR)\pmksa_cache.obj \
+	$(OBJDIR)\eapol_supp_sm.obj \
+	$(OBJDIR)\eap.obj \
+	$(OBJDIR)\eap_common.obj \
+	$(OBJDIR)\chap.obj \
+	$(OBJDIR)\eap_methods.obj \
+	$(OBJDIR)\eap_md5.obj \
+	$(OBJDIR)\eap_tls.obj \
+	$(OBJDIR)\eap_tls_common.obj \
+	$(OBJDIR)\eap_mschapv2.obj \
+	$(OBJDIR)\mschapv2.obj \
+	$(OBJDIR)\eap_peap.obj \
+	$(OBJDIR)\eap_peap_common.obj \
+	$(OBJDIR)\eap_ttls.obj \
+	$(OBJDIR)\eap_gtc.obj \
+	$(OBJDIR)\eap_otp.obj \
+	$(OBJDIR)\eap_leap.obj \
+	$(OBJDIR)\eap_sim.obj \
+	$(OBJDIR)\eap_sim_common.obj \
+	$(OBJDIR)\eap_aka.obj \
+	$(OBJDIR)\eap_pax.obj \
+	$(OBJDIR)\eap_pax_common.obj \
+	$(OBJDIR)\eap_psk.obj \
+	$(OBJDIR)\eap_psk_common.obj \
+	$(OBJDIR)\eap_tnc.obj \
+	$(OBJDIR)\tncc.obj \
+	$(OBJDIR)\base64.obj \
+	$(OBJDIR)\ctrl_iface.obj \
+	$(OBJDIR)\ctrl_iface_named_pipe.obj \
+	$(OBJDIR)\driver_ndis.obj \
+	$(OBJDIR)\driver_ndis_.obj \
+	$(OBJDIR)\scan_helpers.obj \
+	$(OBJDIR)\events.obj \
+	$(OBJDIR)\blacklist.obj \
+	$(OBJDIR)\scan.obj \
+	$(OBJDIR)\wpas_glue.obj \
+	$(OBJDIR)\eap_register.obj \
+	$(OBJDIR)\config.obj \
+	$(OBJDIR)\l2_packet_winpcap.obj \
+	$(OBJDIR)\tls_openssl.obj \
+	$(OBJDIR)\ms_funcs.obj \
+	$(OBJDIR)\crypto_openssl.obj \
+	$(OBJDIR)\fips_prf_openssl.obj \
+	$(OBJDIR)\pcsc_funcs.obj \
+	$(OBJDIR)\notify.obj \
+	$(OBJDIR)\ndis_events.obj
+
+# OBJS = $(OBJS) $(OBJDIR)\eap_fast.obj
+
+OBJS_t = $(OBJS) \
+	$(OBJDIR)\eapol_test.obj \
+	$(OBJDIR)\radius.obj \
+	$(OBJDIR)\radius_client.obj \
+	$(OBJDIR)\config_file.obj $(OBJDIR)\base64.obj
+
+OBJS_t2 = $(OBJS) \
+	$(OBJDIR)\preauth_test.obj \
+	$(OBJDIR)\config_file.obj $(OBJDIR)\base64.obj
+
+OBJS2 = $(OBJDIR)\drivers.obj \
+	$(OBJDIR)\config_file.obj \
+	$(OBJS2) $(OBJDIR)\main.obj
+
+OBJS3 = $(OBJDIR)\drivers.obj \
+	$(OBJDIR)\config_winreg.obj \
+	$(OBJS3) $(OBJDIR)\main_winsvc.obj
+
+OBJS_c = \
+	$(OBJDIR)\os_win32.obj \
+	$(OBJDIR)\wpa_cli.obj \
+	$(OBJDIR)\wpa_ctrl.obj \
+	$(OBJDIR)\common.obj
+
+OBJS_p = \
+	$(OBJDIR)\os_win32.obj \
+	$(OBJDIR)\common.obj \
+	$(OBJDIR)\wpa_debug.obj \
+	$(OBJDIR)\wpabuf.obj \
+	$(OBJDIR)\sha1.obj \
+	$(OBJDIR)\md5.obj \
+	$(OBJDIR)\crypto_openssl.obj \
+	$(OBJDIR)\sha1-pbkdf2.obj \
+	$(OBJDIR)\wpa_passphrase.obj
+
+LIBS = wbemuuid.lib libcmt.lib kernel32.lib uuid.lib ole32.lib oleaut32.lib \
+	ws2_32.lib Advapi32.lib Crypt32.lib Winscard.lib \
+	Packet.lib wpcap.lib \
+	libeay32.lib ssleay32.lib
+# If using Win32 OpenSSL binary installation from Shining Light Productions,
+# replace the last line with this for dynamic libraries
+#	libeay32MT.lib ssleay32MT.lib
+# and this for static libraries
+#	libeay32MT.lib ssleay32MT.lib Gdi32.lib User32.lib
+
+CFLAGS = $(CFLAGS) /I"$(WINPCAPDIR)/Include" /I"$(OPENSSLDIR)\include"
+LFLAGS = /libpath:"$(WINPCAPDIR)\Lib" /libpath:"$(OPENSSLDIR)\lib"
+
+wpa_supplicant.exe: $(OBJDIR) $(OBJS) $(OBJS2)
+	link.exe /out:wpa_supplicant.exe $(LFLAGS) $(OBJS) $(OBJS2) $(LIBS)
+
+wpasvc.exe: $(OBJDIR) $(OBJS) $(OBJS3)
+	link.exe /out:wpasvc.exe $(LFLAGS) $(OBJS) $(OBJS3) $(LIBS)
+
+wpa_cli.exe: $(OBJDIR) $(OBJS_c)
+	link.exe /out:wpa_cli.exe $(LFLAGS) $(OBJS_c) $(LIBS)
+
+wpa_passphrase.exe: $(OBJDIR) $(OBJS_p)
+	link.exe /out:wpa_passphrase.exe $(LFLAGS) $(OBJS_p) $(LIBS)
+
+eapol_test.exe: $(OBJDIR) $(OBJS_t)
+	link.exe /out:eapol_test.exe $(LFLAGS) $(OBJS_t) $(LIBS)
+
+preauth_test.exe: $(OBJDIR) $(OBJS_t2)
+	link.exe /out:preauth_test.exe $(LFLAGS) $(OBJS_t2) $(LIBS)
+
+win_if_list.exe: $(OBJDIR) $(OBJDIR)\win_if_list.obj
+	link.exe /out:win_if_list.exe $(LFLAGS) $(OBJDIR)\win_if_list.obj $(LIBS)
+
+
+{..\src\utils}.c{$(OBJDIR)}.obj::
+	$(CC) $(CFLAGS) $<
+
+{..\src\common}.c{$(OBJDIR)}.obj::
+	$(CC) $(CFLAGS) $<
+
+{..\src\rsn_supp}.c{$(OBJDIR)}.obj::
+	$(CC) $(CFLAGS) $<
+
+{..\src\eapol_supp}.c{$(OBJDIR)}.obj::
+	$(CC) $(CFLAGS) $<
+
+{..\src\crypto}.c{$(OBJDIR)}.obj::
+	$(CC) $(CFLAGS) $<
+
+{..\src\eap_peer}.c{$(OBJDIR)}.obj::
+	$(CC) $(CFLAGS) $<
+
+{..\src\eap_common}.c{$(OBJDIR)}.obj::
+	$(CC) $(CFLAGS) $<
+
+{..\src\drivers}.c{$(OBJDIR)}.obj::
+	$(CC) $(CFLAGS) $<
+
+{..\src\l2_packet}.c{$(OBJDIR)}.obj::
+	$(CC) $(CFLAGS) $<
+
+{.\}.c{$(OBJDIR)}.obj::
+	$(CC) $(CFLAGS) $<
+
+{.\}.cpp{$(OBJDIR)}.obj::
+	$(CC) $(CFLAGS) $<
+
+$(OBJDIR):
+	if not exist "$(OBJDIR)" mkdir "$(OBJDIR)"
+
+clean:
+	erase $(OBJDIR)\*.obj wpa_supplicant.exe
diff --git a/hostap/wpa_supplicant/notify.c b/hostap/wpa_supplicant/notify.c
new file mode 100644
index 0000000..fbff02f
--- /dev/null
+++ b/hostap/wpa_supplicant/notify.c
@@ -0,0 +1,837 @@
+/*
+ * wpa_supplicant - Event notifications
+ * Copyright (c) 2009-2010, 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 "common/wpa_ctrl.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "wps_supplicant.h"
+#include "dbus/dbus_common.h"
+#include "dbus/dbus_old.h"
+#include "dbus/dbus_new.h"
+#include "rsn_supp/wpa.h"
+#include "fst/fst.h"
+#include "driver_i.h"
+#include "scan.h"
+#include "p2p_supplicant.h"
+#include "sme.h"
+#include "notify.h"
+
+int wpas_notify_supplicant_initialized(struct wpa_global *global)
+{
+#ifdef CONFIG_DBUS
+	if (global->params.dbus_ctrl_interface) {
+		global->dbus = wpas_dbus_init(global);
+		if (global->dbus == NULL)
+			return -1;
+	}
+#endif /* CONFIG_DBUS */
+
+	return 0;
+}
+
+
+void wpas_notify_supplicant_deinitialized(struct wpa_global *global)
+{
+#ifdef CONFIG_DBUS
+	if (global->dbus)
+		wpas_dbus_deinit(global->dbus);
+#endif /* CONFIG_DBUS */
+}
+
+
+int wpas_notify_iface_added(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_mgmt)
+		return 0;
+
+	if (wpas_dbus_register_iface(wpa_s))
+		return -1;
+
+	if (wpas_dbus_register_interface(wpa_s))
+		return -1;
+
+	return 0;
+}
+
+
+void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	/* unregister interface in old DBus ctrl iface */
+	wpas_dbus_unregister_iface(wpa_s);
+
+	/* unregister interface in new DBus ctrl iface */
+	wpas_dbus_unregister_interface(wpa_s);
+}
+
+
+void wpas_notify_state_changed(struct wpa_supplicant *wpa_s,
+			       enum wpa_states new_state,
+			       enum wpa_states old_state)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	/* notify the old DBus API */
+	wpa_supplicant_dbus_notify_state_change(wpa_s, new_state,
+						old_state);
+
+	/* notify the new DBus API */
+	wpas_dbus_signal_state_changed(wpa_s);
+
+#ifdef CONFIG_FST
+	if (wpa_s->fst && !is_zero_ether_addr(wpa_s->bssid)) {
+		if (new_state == WPA_COMPLETED)
+			fst_notify_peer_connected(wpa_s->fst, wpa_s->bssid);
+		else if (old_state >= WPA_ASSOCIATED &&
+			 new_state < WPA_ASSOCIATED)
+			fst_notify_peer_disconnected(wpa_s->fst, wpa_s->bssid);
+	}
+#endif /* CONFIG_FST */
+
+	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);
+
+	sme_state_changed(wpa_s);
+
+#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,
+		     new_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) : "");
+#endif /* ANDROID */
+}
+
+
+void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_DISCONNECT_REASON);
+}
+
+
+void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s)
+{
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_ASSOC_STATUS_CODE);
+}
+
+
+void wpas_notify_network_changed(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_NETWORK);
+}
+
+
+void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_AP_SCAN);
+}
+
+
+void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_BSS);
+}
+
+
+void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_AUTH_MODE);
+}
+
+
+void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
+					 struct wpa_ssid *ssid)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_network_enabled_changed(wpa_s, ssid);
+}
+
+
+void wpas_notify_network_selected(struct wpa_supplicant *wpa_s,
+				  struct wpa_ssid *ssid)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_network_selected(wpa_s, ssid->id);
+}
+
+
+void wpas_notify_network_request(struct wpa_supplicant *wpa_s,
+				 struct wpa_ssid *ssid,
+				 enum wpa_ctrl_req_type rtype,
+				 const char *default_txt)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_network_request(wpa_s, ssid, rtype, default_txt);
+}
+
+
+void wpas_notify_scanning(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	/* notify the old DBus API */
+	wpa_supplicant_dbus_notify_scanning(wpa_s);
+
+	/* notify the new DBus API */
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SCANNING);
+}
+
+
+void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_scan_done(wpa_s, success);
+}
+
+
+void wpas_notify_scan_results(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	/* notify the old DBus API */
+	wpa_supplicant_dbus_notify_scan_results(wpa_s);
+
+	wpas_wps_notify_scan_results(wpa_s);
+}
+
+
+void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s,
+				const struct wps_credential *cred)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+#ifdef CONFIG_WPS
+	/* notify the old DBus API */
+	wpa_supplicant_dbus_notify_wps_cred(wpa_s, cred);
+	/* notify the new DBus API */
+	wpas_dbus_signal_wps_cred(wpa_s, cred);
+#endif /* CONFIG_WPS */
+}
+
+
+void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s,
+			       struct wps_event_m2d *m2d)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+#ifdef CONFIG_WPS
+	wpas_dbus_signal_wps_event_m2d(wpa_s, m2d);
+#endif /* CONFIG_WPS */
+}
+
+
+void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s,
+				struct wps_event_fail *fail)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+#ifdef CONFIG_WPS
+	wpas_dbus_signal_wps_event_fail(wpa_s, fail);
+#endif /* CONFIG_WPS */
+}
+
+
+void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+#ifdef CONFIG_WPS
+	wpas_dbus_signal_wps_event_success(wpa_s);
+#endif /* CONFIG_WPS */
+}
+
+void wpas_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+#ifdef CONFIG_WPS
+	wpas_dbus_signal_wps_event_pbc_overlap(wpa_s);
+#endif /* CONFIG_WPS */
+}
+
+
+void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
+			       struct wpa_ssid *ssid)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	/*
+	 * Networks objects created during any P2P activities should not be
+	 * exposed out. They might/will confuse certain non-P2P aware
+	 * applications since these network objects won't behave like
+	 * regular ones.
+	 */
+	if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
+		wpas_dbus_register_network(wpa_s, ssid);
+}
+
+
+void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
+					struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_P2P
+	wpas_dbus_register_persistent_group(wpa_s, ssid);
+#endif /* CONFIG_P2P */
+}
+
+
+void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s,
+					  struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_P2P
+	wpas_dbus_unregister_persistent_group(wpa_s, ssid->id);
+#endif /* CONFIG_P2P */
+}
+
+
+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 &&
+	    !wpa_s->p2p_mgmt)
+		wpas_dbus_unregister_network(wpa_s, ssid->id);
+	if (network_is_persistent_group(ssid))
+		wpas_notify_persistent_group_removed(wpa_s, ssid);
+
+	wpas_p2p_network_removed(wpa_s, ssid);
+}
+
+
+void wpas_notify_bss_added(struct wpa_supplicant *wpa_s,
+			   u8 bssid[], unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_register_bss(wpa_s, bssid, id);
+	wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_ADDED "%u " MACSTR,
+		     id, MAC2STR(bssid));
+}
+
+
+void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s,
+			     u8 bssid[], unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_unregister_bss(wpa_s, bssid, id);
+	wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_REMOVED "%u " MACSTR,
+		     id, MAC2STR(bssid));
+}
+
+
+void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s,
+				  unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_FREQ, id);
+}
+
+
+void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s,
+				    unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_SIGNAL,
+					  id);
+}
+
+
+void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s,
+				     unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_PRIVACY,
+					  id);
+}
+
+
+void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s,
+				  unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_MODE, id);
+}
+
+
+void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s,
+				   unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPA, id);
+}
+
+
+void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s,
+				   unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RSN, id);
+}
+
+
+void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s,
+				 unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+#ifdef CONFIG_WPS
+	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPS, id);
+#endif /* CONFIG_WPS */
+}
+
+
+void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s,
+				   unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_IES, id);
+}
+
+
+void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
+				   unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RATES, id);
+}
+
+
+void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_AGE, id);
+}
+
+
+void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_blob_added(wpa_s, name);
+}
+
+
+void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_blob_removed(wpa_s, name);
+}
+
+
+void wpas_notify_debug_level_changed(struct wpa_global *global)
+{
+	wpas_dbus_signal_debug_level_changed(global);
+}
+
+
+void wpas_notify_debug_timestamp_changed(struct wpa_global *global)
+{
+	wpas_dbus_signal_debug_timestamp_changed(global);
+}
+
+
+void wpas_notify_debug_show_keys_changed(struct wpa_global *global)
+{
+	wpas_dbus_signal_debug_show_keys_changed(global);
+}
+
+
+void wpas_notify_suspend(struct wpa_global *global)
+{
+	struct wpa_supplicant *wpa_s;
+
+	os_get_time(&global->suspend_time);
+	wpa_printf(MSG_DEBUG, "System suspend notification");
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
+		wpa_drv_suspend(wpa_s);
+}
+
+
+void wpas_notify_resume(struct wpa_global *global)
+{
+	struct os_time now;
+	int slept;
+	struct wpa_supplicant *wpa_s;
+
+	if (global->suspend_time.sec == 0)
+		slept = -1;
+	else {
+		os_get_time(&now);
+		slept = now.sec - global->suspend_time.sec;
+	}
+	wpa_printf(MSG_DEBUG, "System resume notification (slept %d seconds)",
+		   slept);
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		wpa_drv_resume(wpa_s);
+		if (wpa_s->wpa_state == WPA_DISCONNECTED)
+			wpa_supplicant_req_scan(wpa_s, 0, 100000);
+	}
+}
+
+
+#ifdef CONFIG_P2P
+
+void wpas_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s)
+{
+	/* Notify P2P find has stopped */
+	wpas_dbus_signal_p2p_find_stopped(wpa_s);
+}
+
+
+void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
+				  const u8 *dev_addr, int new_device)
+{
+	if (new_device) {
+		/* Create the new peer object */
+		wpas_dbus_register_peer(wpa_s, dev_addr);
+	}
+
+	/* Notify a new peer has been detected*/
+	wpas_dbus_signal_peer_device_found(wpa_s, dev_addr);
+}
+
+
+void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s,
+				 const u8 *dev_addr)
+{
+	wpas_dbus_unregister_peer(wpa_s, dev_addr);
+
+	/* Create signal on interface object*/
+	wpas_dbus_signal_peer_device_lost(wpa_s, dev_addr);
+}
+
+
+void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s,
+				   const struct wpa_ssid *ssid,
+				   const char *role)
+{
+	wpas_dbus_signal_p2p_group_removed(wpa_s, role);
+
+	wpas_dbus_unregister_p2p_group(wpa_s, ssid);
+}
+
+
+void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+				const u8 *src, u16 dev_passwd_id, u8 go_intent)
+{
+	wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent);
+}
+
+
+void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s,
+				      struct p2p_go_neg_results *res)
+{
+	wpas_dbus_signal_p2p_go_neg_resp(wpa_s, res);
+}
+
+
+void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+				       int status, const u8 *bssid)
+{
+	wpas_dbus_signal_p2p_invitation_result(wpa_s, status, bssid);
+}
+
+
+void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s,
+				int freq, const u8 *sa, u8 dialog_token,
+				u16 update_indic, const u8 *tlvs,
+				size_t tlvs_len)
+{
+	wpas_dbus_signal_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+					update_indic, tlvs, tlvs_len);
+}
+
+
+void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s,
+				 const u8 *sa, u16 update_indic,
+				 const u8 *tlvs, size_t tlvs_len)
+{
+	wpas_dbus_signal_p2p_sd_response(wpa_s, sa, update_indic,
+					 tlvs, tlvs_len);
+}
+
+
+/**
+ * wpas_notify_p2p_provision_discovery - Notification of provision discovery
+ * @dev_addr: Who sent the request or responded to our request.
+ * @request: Will be 1 if request, 0 for response.
+ * @status: Valid only in case of response (0 in case of success)
+ * @config_methods: WPS config methods
+ * @generated_pin: PIN to be displayed in case of WPS_CONFIG_DISPLAY method
+ *
+ * This can be used to notify:
+ * - Requests or responses
+ * - Various config methods
+ * - Failure condition in case of response
+ */
+void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+					 const u8 *dev_addr, int request,
+					 enum p2p_prov_disc_status status,
+					 u16 config_methods,
+					 unsigned int generated_pin)
+{
+	wpas_dbus_signal_p2p_provision_discovery(wpa_s, dev_addr, request,
+						 status, config_methods,
+						 generated_pin);
+}
+
+
+void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
+				   struct wpa_ssid *ssid, int network_id,
+				   int client)
+{
+	/* Notify a group has been started */
+	wpas_dbus_register_p2p_group(wpa_s, ssid);
+
+	wpas_dbus_signal_p2p_group_started(wpa_s, ssid, client, network_id);
+}
+
+
+void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+					     const char *reason)
+{
+	/* Notify a group formation failed */
+	wpas_dbus_signal_p2p_group_formation_failure(wpa_s, reason);
+}
+
+
+void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+				struct wps_event_fail *fail)
+{
+	wpas_dbus_signal_p2p_wps_failed(wpa_s, fail);
+}
+
+
+void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+					 const u8 *sa, const u8 *go_dev_addr,
+					 const u8 *bssid, int id, int op_freq)
+{
+	/* Notify a P2P Invitation Request */
+	wpas_dbus_signal_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid,
+						 id, op_freq);
+}
+
+#endif /* CONFIG_P2P */
+
+
+static void wpas_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
+					  const u8 *sta,
+					  const u8 *p2p_dev_addr)
+{
+#ifdef CONFIG_P2P
+	wpas_p2p_notify_ap_sta_authorized(wpa_s, p2p_dev_addr);
+
+	/*
+	 * Create 'peer-joined' signal on group object -- will also
+	 * check P2P itself.
+	 */
+	if (p2p_dev_addr)
+		wpas_dbus_signal_p2p_peer_joined(wpa_s, p2p_dev_addr);
+#endif /* CONFIG_P2P */
+
+	/* Notify listeners a new station has been authorized */
+	wpas_dbus_signal_sta_authorized(wpa_s, sta);
+}
+
+
+static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s,
+					    const u8 *sta,
+					    const u8 *p2p_dev_addr)
+{
+#ifdef CONFIG_P2P
+	/*
+	 * Create 'peer-disconnected' signal on group object if this
+	 * is a P2P group.
+	 */
+	if (p2p_dev_addr)
+		wpas_dbus_signal_p2p_peer_disconnected(wpa_s, p2p_dev_addr);
+#endif /* CONFIG_P2P */
+
+	/* Notify listeners a station has been deauthorized */
+	wpas_dbus_signal_sta_deauthorized(wpa_s, sta);
+}
+
+
+void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s,
+				const u8 *mac_addr, int authorized,
+				const u8 *p2p_dev_addr)
+{
+	if (authorized)
+		wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr);
+	else
+		wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr, p2p_dev_addr);
+}
+
+
+void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
+			       const char *subject, const char *altsubject[],
+			       int num_altsubject, const char *cert_hash,
+			       const struct wpabuf *cert)
+{
+	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
+		"depth=%d subject='%s'%s%s",
+		depth, subject, cert_hash ? " hash=" : "",
+		cert_hash ? cert_hash : "");
+
+	if (cert) {
+		char *cert_hex;
+		size_t len = wpabuf_len(cert) * 2 + 1;
+		cert_hex = os_malloc(len);
+		if (cert_hex) {
+			wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert),
+					 wpabuf_len(cert));
+			wpa_msg_ctrl(wpa_s, MSG_INFO,
+				     WPA_EVENT_EAP_PEER_CERT
+				     "depth=%d subject='%s' cert=%s",
+				     depth, subject, cert_hex);
+			os_free(cert_hex);
+		}
+	}
+
+	if (altsubject) {
+		int i;
+
+		for (i = 0; i < num_altsubject; i++)
+			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT
+				"depth=%d %s", depth, altsubject[i]);
+	}
+
+	/* notify the old DBus API */
+	wpa_supplicant_dbus_notify_certification(wpa_s, depth, subject,
+						 cert_hash, cert);
+	/* notify the new DBus API */
+	wpas_dbus_signal_certification(wpa_s, depth, subject, altsubject,
+				       num_altsubject, cert_hash, cert);
+}
+
+
+void wpas_notify_preq(struct wpa_supplicant *wpa_s,
+		      const u8 *addr, const u8 *dst, const u8 *bssid,
+		      const u8 *ie, size_t ie_len, u32 ssi_signal)
+{
+#ifdef CONFIG_AP
+	wpas_dbus_signal_preq(wpa_s, addr, dst, bssid, ie, ie_len, ssi_signal);
+#endif /* CONFIG_AP */
+}
+
+
+void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
+			    const char *parameter)
+{
+	wpas_dbus_signal_eap_status(wpa_s, status, parameter);
+	wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_EAP_STATUS
+		     "status='%s' parameter='%s'",
+		     status, parameter);
+}
+
+
+void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
+					   struct wpa_ssid *ssid)
+{
+	if (wpa_s->current_ssid != ssid)
+		return;
+
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"Network bssid config changed for the current network - within-ESS roaming %s",
+		ssid->bssid_set ? "disabled" : "enabled");
+
+	wpa_drv_roaming(wpa_s, !ssid->bssid_set,
+			ssid->bssid_set ? ssid->bssid : NULL);
+}
+
+
+void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
+				      struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_P2P
+	if (ssid->disabled == 2) {
+		/* Changed from normal network profile to persistent group */
+		ssid->disabled = 0;
+		wpas_dbus_unregister_network(wpa_s, ssid->id);
+		ssid->disabled = 2;
+		ssid->p2p_persistent_group = 1;
+		wpas_dbus_register_persistent_group(wpa_s, ssid);
+	} else {
+		/* Changed from persistent group to normal network profile */
+		wpas_dbus_unregister_persistent_group(wpa_s, ssid->id);
+		ssid->p2p_persistent_group = 0;
+		wpas_dbus_register_network(wpa_s, ssid);
+	}
+#endif /* CONFIG_P2P */
+}
diff --git a/hostap/wpa_supplicant/notify.h b/hostap/wpa_supplicant/notify.h
new file mode 100644
index 0000000..1b7f04d
--- /dev/null
+++ b/hostap/wpa_supplicant/notify.h
@@ -0,0 +1,145 @@
+/*
+ * wpa_supplicant - Event notifications
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NOTIFY_H
+#define NOTIFY_H
+
+#include "p2p/p2p.h"
+
+struct wps_credential;
+struct wps_event_m2d;
+struct wps_event_fail;
+
+int wpas_notify_supplicant_initialized(struct wpa_global *global);
+void wpas_notify_supplicant_deinitialized(struct wpa_global *global);
+int wpas_notify_iface_added(struct wpa_supplicant *wpa_s);
+void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s);
+void wpas_notify_state_changed(struct wpa_supplicant *wpa_s,
+			       enum wpa_states new_state,
+			       enum wpa_states old_state);
+void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s);
+void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s);
+void wpas_notify_network_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
+					 struct wpa_ssid *ssid);
+void wpas_notify_network_selected(struct wpa_supplicant *wpa_s,
+				  struct wpa_ssid *ssid);
+void wpas_notify_network_request(struct wpa_supplicant *wpa_s,
+				 struct wpa_ssid *ssid,
+				 enum wpa_ctrl_req_type rtype,
+				 const char *default_txt);
+void wpas_notify_scanning(struct wpa_supplicant *wpa_s);
+void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success);
+void wpas_notify_scan_results(struct wpa_supplicant *wpa_s);
+void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s,
+				const struct wps_credential *cred);
+void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s,
+			       struct wps_event_m2d *m2d);
+void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s,
+				struct wps_event_fail *fail);
+void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s);
+void wpas_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s);
+void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
+			       struct wpa_ssid *ssid);
+void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
+				 struct wpa_ssid *ssid);
+void wpas_notify_bss_added(struct wpa_supplicant *wpa_s, u8 bssid[],
+			   unsigned int id);
+void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s, u8 bssid[],
+			     unsigned int id);
+void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s,
+				  unsigned int id);
+void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s,
+				    unsigned int id);
+void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s,
+				     unsigned int id);
+void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s,
+				  unsigned int id);
+void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s,
+				   unsigned int id);
+void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s,
+				   unsigned int id);
+void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s,
+				 unsigned int id);
+void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s,
+				 unsigned int id);
+void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
+				   unsigned int id);
+void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id);
+void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name);
+void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name);
+
+void wpas_notify_debug_level_changed(struct wpa_global *global);
+void wpas_notify_debug_timestamp_changed(struct wpa_global *global);
+void wpas_notify_debug_show_keys_changed(struct wpa_global *global);
+void wpas_notify_suspend(struct wpa_global *global);
+void wpas_notify_resume(struct wpa_global *global);
+
+void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s,
+				const u8 *mac_addr, int authorized,
+				const u8 *p2p_dev_addr);
+void wpas_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s);
+void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
+				  const u8 *dev_addr, int new_device);
+void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s,
+				 const u8 *dev_addr);
+void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s,
+				   const struct wpa_ssid *ssid,
+				   const char *role);
+void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+				const u8 *src, u16 dev_passwd_id, u8 go_intent);
+void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s,
+				      struct p2p_go_neg_results *res);
+void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+				       int status, const u8 *bssid);
+void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s,
+				int freq, const u8 *sa, u8 dialog_token,
+				u16 update_indic, const u8 *tlvs,
+				size_t tlvs_len);
+void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s,
+				 const u8 *sa, u16 update_indic,
+				 const u8 *tlvs, size_t tlvs_len);
+void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+					 const u8 *dev_addr, int request,
+					 enum p2p_prov_disc_status status,
+					 u16 config_methods,
+					 unsigned int generated_pin);
+void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
+				   struct wpa_ssid *ssid, int network_id,
+				   int client);
+void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+					     const char *reason);
+void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
+					struct wpa_ssid *ssid);
+void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s,
+					  struct wpa_ssid *ssid);
+
+void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+				struct wps_event_fail *fail);
+
+void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
+			       const char *subject, const char *altsubject[],
+			       int num_altsubject, const char *cert_hash,
+			       const struct wpabuf *cert);
+void wpas_notify_preq(struct wpa_supplicant *wpa_s,
+		      const u8 *addr, const u8 *dst, const u8 *bssid,
+		      const u8 *ie, size_t ie_len, u32 ssi_signal);
+void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
+			    const char *parameter);
+void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
+					   struct wpa_ssid *ssid);
+void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
+				      struct wpa_ssid *ssid);
+void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+					 const u8 *sa, const u8 *go_dev_addr,
+					 const u8 *bssid, int id, int op_freq);
+
+#endif /* NOTIFY_H */
diff --git a/hostap/wpa_supplicant/offchannel.c b/hostap/wpa_supplicant/offchannel.c
new file mode 100644
index 0000000..63af83a
--- /dev/null
+++ b/hostap/wpa_supplicant/offchannel.c
@@ -0,0 +1,447 @@
+/*
+ * wpa_supplicant - Off-channel Action frame TX/RX
+ * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * 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 "wpa_supplicant_i.h"
+#include "p2p_supplicant.h"
+#include "driver_i.h"
+#include "offchannel.h"
+
+
+
+static struct wpa_supplicant *
+wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src)
+{
+	struct wpa_supplicant *iface;
+
+	if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0)
+		return wpa_s;
+
+	/*
+	 * Try to find a group interface that matches with the source address.
+	 */
+	iface = wpa_s->global->ifaces;
+	while (iface) {
+		if (os_memcmp(src, iface->own_addr, ETH_ALEN) == 0)
+			break;
+		iface = iface->next;
+	}
+	if (iface) {
+		wpa_printf(MSG_DEBUG, "P2P: Use group interface %s "
+			   "instead of interface %s for Action TX",
+			   iface->ifname, wpa_s->ifname);
+		return iface;
+	}
+
+	return wpa_s;
+}
+
+
+static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct wpa_supplicant *iface;
+	int res;
+	int without_roc;
+
+	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 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 || wpa_s->pending_action_tx_done)
+		return;
+
+	/*
+	 * This call is likely going to be on the P2P device instance if the
+	 * driver uses a separate interface for that purpose. However, some
+	 * Action frames are actually sent within a P2P Group and when that is
+	 * the case, we need to follow power saving (e.g., GO buffering the
+	 * frame for a client in PS mode or a client following the advertised
+	 * NoA from its GO). To make that easier for the driver, select the
+	 * correct group interface here.
+	 */
+	iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src);
+
+	if (wpa_s->off_channel_freq != wpa_s->pending_action_freq &&
+	    wpa_s->pending_action_freq != 0 &&
+	    wpa_s->pending_action_freq != iface->assoc_freq) {
+		wpa_printf(MSG_DEBUG, "Off-channel: Pending Action frame TX "
+			   "waiting for another freq=%u (off_channel_freq=%u "
+			   "assoc_freq=%u)",
+			   wpa_s->pending_action_freq,
+			   wpa_s->off_channel_freq,
+			   iface->assoc_freq);
+		if (without_roc && wpa_s->off_channel_freq == 0) {
+			unsigned int duration = 200;
+			/*
+			 * We may get here if wpas_send_action() found us to be
+			 * on the correct channel, but remain-on-channel cancel
+			 * event was received before getting here.
+			 */
+			wpa_printf(MSG_DEBUG, "Off-channel: Schedule "
+				   "remain-on-channel to send Action frame");
+#ifdef CONFIG_TESTING_OPTIONS
+			if (wpa_s->extra_roc_dur) {
+				wpa_printf(MSG_DEBUG,
+					   "TESTING: Increase ROC duration %u -> %u",
+					   duration,
+					   duration + wpa_s->extra_roc_dur);
+				duration += wpa_s->extra_roc_dur;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+			if (wpa_drv_remain_on_channel(
+				    wpa_s, wpa_s->pending_action_freq,
+				    duration) < 0) {
+				wpa_printf(MSG_DEBUG, "Off-channel: Failed to "
+					   "request driver to remain on "
+					   "channel (%u MHz) for Action Frame "
+					   "TX", wpa_s->pending_action_freq);
+			} else {
+				wpa_s->off_channel_freq = 0;
+				wpa_s->roc_waiting_drv_freq =
+					wpa_s->pending_action_freq;
+			}
+		}
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to "
+		   MACSTR " using interface %s",
+		   MAC2STR(wpa_s->pending_action_dst), iface->ifname);
+	res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0,
+				  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 (res) {
+		wpa_printf(MSG_DEBUG, "Off-channel: Failed to send the "
+			   "pending Action frame");
+		/*
+		 * Use fake TX status event to allow state machines to
+		 * continue.
+		 */
+		offchannel_send_action_tx_status(
+			wpa_s, wpa_s->pending_action_dst,
+			wpabuf_head(wpa_s->pending_action_tx),
+			wpabuf_len(wpa_s->pending_action_tx),
+			OFFCHANNEL_SEND_ACTION_FAILED);
+	}
+}
+
+
+/**
+ * offchannel_send_action_tx_status - TX status callback
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @dst: Destination MAC address of the transmitted Action frame
+ * @data: Transmitted frame payload
+ * @data_len: Length of @data in bytes
+ * @result: TX status
+ *
+ * This function is called whenever the driver indicates a TX status event for
+ * a frame sent by offchannel_send_action() using wpa_drv_send_action().
+ */
+void offchannel_send_action_tx_status(
+	struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
+	size_t data_len, enum offchannel_send_action_result result)
+{
+	if (wpa_s->pending_action_tx == NULL) {
+		wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+			   "no pending operation");
+		return;
+	}
+
+	if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+			   "unknown destination address");
+		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;
+
+	wpa_printf(MSG_DEBUG, "Off-channel: TX status result=%d cb=%p",
+		   result, wpa_s->pending_action_tx_status_cb);
+
+	if (wpa_s->pending_action_tx_status_cb) {
+		wpa_s->pending_action_tx_status_cb(
+			wpa_s, wpa_s->pending_action_freq,
+			wpa_s->pending_action_dst, wpa_s->pending_action_src,
+			wpa_s->pending_action_bssid,
+			data, data_len, result);
+	}
+
+#ifdef CONFIG_P2P
+	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);
+	}
+#endif /* CONFIG_P2P */
+}
+
+
+/**
+ * offchannel_send_action - Request off-channel Action frame TX
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: The frequency in MHz indicating the channel on which the frame is to
+ *	transmitted or 0 for the current channel (only if associated)
+ * @dst: Action frame destination MAC address
+ * @src: Action frame source MAC address
+ * @bssid: Action frame BSSID
+ * @buf: Frame to transmit starting from the Category field
+ * @len: Length of @buf in bytes
+ * @wait_time: Wait time for response in milliseconds
+ * @tx_cb: Callback function for indicating TX status or %NULL for now callback
+ * @no_cck: Whether CCK rates are to be disallowed for TX rate selection
+ * Returns: 0 on success or -1 on failure
+ *
+ * This function is used to request an Action frame to be transmitted on the
+ * current operating channel or on another channel (off-channel). The actual
+ * frame transmission will be delayed until the driver is ready on the specified
+ * channel. The @wait_time parameter can be used to request the driver to remain
+ * awake on the channel to wait for a response.
+ */
+int offchannel_send_action(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,
+			   void (*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),
+			   int no_cck)
+{
+	wpa_printf(MSG_DEBUG, "Off-channel: Send action frame: freq=%d dst="
+		   MACSTR " src=" MACSTR " bssid=" MACSTR " len=%d",
+		   freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+		   (int) len);
+
+	wpa_s->pending_action_tx_status_cb = tx_cb;
+
+	if (wpa_s->pending_action_tx) {
+		wpa_printf(MSG_DEBUG, "Off-channel: Dropped pending Action "
+			   "frame TX to " MACSTR,
+			   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 "
+			   "frame TX buffer (len=%llu)",
+			   (unsigned long long) len);
+		return -1;
+	}
+	wpabuf_put_data(wpa_s->pending_action_tx, buf, len);
+	os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN);
+	os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN);
+	os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
+	wpa_s->pending_action_freq = freq;
+	wpa_s->pending_action_no_cck = no_cck;
+
+	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, src);
+		wpa_s->action_tx_wait_time = wait_time;
+
+		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) {
+		struct wpa_supplicant *tx_iface;
+		tx_iface = wpas_get_tx_interface(wpa_s, src);
+		if (tx_iface->assoc_freq == freq) {
+			wpa_printf(MSG_DEBUG, "Off-channel: Already on "
+				   "requested channel (TX interface operating "
+				   "channel)");
+			freq = 0;
+		}
+	}
+
+	if (wpa_s->off_channel_freq == freq || freq == 0) {
+		wpa_printf(MSG_DEBUG, "Off-channel: Already on requested "
+			   "channel; send Action frame immediately");
+		/* TODO: Would there ever be need to extend the current
+		 * duration on the channel? */
+		wpa_s->pending_action_without_roc = 1;
+		eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
+		eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL);
+		return 0;
+	}
+	wpa_s->pending_action_without_roc = 0;
+
+	if (wpa_s->roc_waiting_drv_freq == freq) {
+		wpa_printf(MSG_DEBUG, "Off-channel: Already waiting for "
+			   "driver to get to frequency %u MHz; continue "
+			   "waiting to send the Action frame", freq);
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "Off-channel: Schedule Action frame to be "
+		   "transmitted once the driver gets to the requested "
+		   "channel");
+	if (wait_time > wpa_s->max_remain_on_chan)
+		wait_time = wpa_s->max_remain_on_chan;
+	else if (wait_time == 0)
+		wait_time = 20;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->extra_roc_dur) {
+		wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u",
+			   wait_time, wait_time + wpa_s->extra_roc_dur);
+		wait_time += wpa_s->extra_roc_dur;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) {
+		wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver "
+			   "to remain on channel (%u MHz) for Action "
+			   "Frame TX", freq);
+		return -1;
+	}
+	wpa_s->off_channel_freq = 0;
+	wpa_s->roc_waiting_drv_freq = freq;
+
+	return 0;
+}
+
+
+/**
+ * offchannel_send_send_action_done - Notify completion of Action frame sequence
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function can be used to cancel a wait for additional response frames on
+ * the channel that was used with offchannel_send_action().
+ */
+void offchannel_send_action_done(struct wpa_supplicant *wpa_s)
+{
+	wpa_printf(MSG_DEBUG,
+		   "Off-channel: Action frame sequence done notification: pending_action_tx=%p drv_offchan_tx=%d action_tx_wait_time=%d off_channel_freq=%d roc_waiting_drv_freq=%d",
+		   wpa_s->pending_action_tx,
+		   !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX),
+		   wpa_s->action_tx_wait_time, wpa_s->off_channel_freq,
+		   wpa_s->roc_waiting_drv_freq);
+	wpabuf_free(wpa_s->pending_action_tx);
+	wpa_s->pending_action_tx = NULL;
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
+	    wpa_s->action_tx_wait_time)
+		wpa_drv_send_action_cancel_wait(wpa_s);
+	else if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+		wpa_drv_cancel_remain_on_channel(wpa_s);
+		wpa_s->off_channel_freq = 0;
+		wpa_s->roc_waiting_drv_freq = 0;
+	}
+}
+
+
+/**
+ * offchannel_remain_on_channel_cb - Remain-on-channel callback function
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: Frequency (in MHz) of the selected channel
+ * @duration: Duration of the remain-on-channel operation in milliseconds
+ *
+ * This function is called whenever the driver notifies beginning of a
+ * remain-on-channel operation.
+ */
+void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+				     unsigned int freq, unsigned int duration)
+{
+	wpa_s->roc_waiting_drv_freq = 0;
+	wpa_s->off_channel_freq = freq;
+	wpas_send_action_cb(wpa_s, NULL);
+}
+
+
+/**
+ * offchannel_cancel_remain_on_channel_cb - Remain-on-channel stopped callback
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: Frequency (in MHz) of the selected channel
+ *
+ * This function is called whenever the driver notifies termination of a
+ * remain-on-channel operation.
+ */
+void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+					    unsigned int freq)
+{
+	wpa_s->off_channel_freq = 0;
+}
+
+
+/**
+ * offchannel_pending_action_tx - Check whether there is a pending Action TX
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: Pointer to pending frame or %NULL if no pending operation
+ *
+ * This function can be used to check whether there is a pending Action frame TX
+ * operation. The returned pointer should be used only for checking whether it
+ * is %NULL (no pending frame) or to print the pointer value in debug
+ * information (i.e., the pointer should not be dereferenced).
+ */
+const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s)
+{
+	return wpa_s->pending_action_tx;
+}
+
+
+/**
+ * offchannel_clear_pending_action_tx - Clear pending Action frame TX
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
+{
+	wpabuf_free(wpa_s->pending_action_tx);
+	wpa_s->pending_action_tx = NULL;
+}
+
+
+/**
+ * offchannel_deinit - Deinit off-channel operations
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to free up any allocated resources for off-channel
+ * operations.
+ */
+void offchannel_deinit(struct wpa_supplicant *wpa_s)
+{
+	offchannel_clear_pending_action_tx(wpa_s);
+	eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
+}
diff --git a/hostap/wpa_supplicant/offchannel.h b/hostap/wpa_supplicant/offchannel.h
new file mode 100644
index 0000000..0ad7e18
--- /dev/null
+++ b/hostap/wpa_supplicant/offchannel.h
@@ -0,0 +1,35 @@
+/*
+ * wpa_supplicant - Off-channel Action frame TX/RX
+ * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef OFFCHANNEL_H
+#define OFFCHANNEL_H
+
+int offchannel_send_action(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,
+			   void (*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),
+			   int no_cck);
+void offchannel_send_action_done(struct wpa_supplicant *wpa_s);
+void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+				     unsigned int freq, unsigned int duration);
+void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+					    unsigned int freq);
+void offchannel_deinit(struct wpa_supplicant *wpa_s);
+void offchannel_send_action_tx_status(
+	struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
+	size_t data_len, enum offchannel_send_action_result result);
+const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s);
+void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s);
+
+#endif /* OFFCHANNEL_H */
diff --git a/hostap/wpa_supplicant/p2p_supplicant.c b/hostap/wpa_supplicant/p2p_supplicant.c
new file mode 100644
index 0000000..78bdd08
--- /dev/null
+++ b/hostap/wpa_supplicant/p2p_supplicant.c
@@ -0,0 +1,8728 @@
+/*
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "wps/wps_i.h"
+#include "p2p/p2p.h"
+#include "ap/hostapd.h"
+#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 "ap/dfs.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "ap.h"
+#include "config_ssid.h"
+#include "config.h"
+#include "notify.h"
+#include "scan.h"
+#include "bss.h"
+#include "offchannel.h"
+#include "wps_supplicant.h"
+#include "p2p_supplicant.h"
+#include "wifi_display.h"
+
+
+/*
+ * How many times to try to scan to find the GO before giving up on join
+ * request.
+ */
+#define P2P_MAX_JOIN_SCAN_ATTEMPTS 10
+
+#define P2P_AUTO_PD_SCAN_ATTEMPTS 5
+
+/**
+ * Defines time interval in seconds when a GO needs to evacuate a frequency that
+ * it is currently using, but is no longer valid for P2P use cases.
+ */
+#define P2P_GO_FREQ_CHANGE_TIME 5
+
+#ifndef P2P_MAX_CLIENT_IDLE
+/*
+ * How many seconds to try to reconnect to the GO when connection in P2P client
+ * role has been lost.
+ */
+#define P2P_MAX_CLIENT_IDLE 10
+#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 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 */
+
+#ifndef P2P_MAX_INITIAL_CONN_WAIT_GO
+/*
+ * How many seconds to wait for initial 4-way handshake to get completed after
+ * WPS provisioning step on the GO. This controls the extra time the P2P
+ * operation is considered to be in progress (e.g., to delay other scans) after
+ * WPS provisioning has been completed on the GO during group formation.
+ */
+#define P2P_MAX_INITIAL_CONN_WAIT_GO 10
+#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO */
+
+#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-"
+
+/*
+ * How many seconds to wait to re-attempt to move GOs, in case previous attempt
+ * was not possible.
+ */
+#define P2P_RECONSIDER_GO_MOVE_DELAY 30
+
+enum p2p_group_removal_reason {
+	P2P_GROUP_REMOVAL_UNKNOWN,
+	P2P_GROUP_REMOVAL_SILENT,
+	P2P_GROUP_REMOVAL_FORMATION_FAILED,
+	P2P_GROUP_REMOVAL_REQUESTED,
+	P2P_GROUP_REMOVAL_IDLE_TIMEOUT,
+	P2P_GROUP_REMOVAL_UNAVAILABLE,
+	P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
+	P2P_GROUP_REMOVAL_PSK_FAILURE,
+	P2P_GROUP_REMOVAL_FREQ_CONFLICT,
+	P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL
+};
+
+
+static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx);
+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, 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 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);
+static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx);
+static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
+				       int group_added);
+static void 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);
+static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
+					enum wpa_driver_if_type type);
+static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
+					    int already_deleted);
+static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
+					     struct wpa_used_freq_data *freqs,
+					     unsigned int num);
+static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx);
+static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq);
+static void
+wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
+			     struct wpa_used_freq_data *freqs, unsigned int num,
+			     enum wpas_p2p_channel_update_trig trig);
+static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx);
+
+
+/*
+ * Get the number of concurrent channels that the HW can operate, but that are
+ * currently not in use by any of the wpa_supplicant interfaces.
+ */
+static int wpas_p2p_num_unused_channels(struct wpa_supplicant *wpa_s)
+{
+	int *freqs;
+	int num, unused;
+
+	freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int));
+	if (!freqs)
+		return -1;
+
+	num = get_shared_radio_freqs(wpa_s, freqs,
+				     wpa_s->num_multichan_concurrent);
+	os_free(freqs);
+
+	unused = wpa_s->num_multichan_concurrent - num;
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: num_unused_channels: %d", unused);
+	return unused;
+}
+
+
+/*
+ * 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 unsigned int
+wpas_p2p_valid_oper_freqs(struct wpa_supplicant *wpa_s,
+			  struct wpa_used_freq_data *p2p_freqs,
+			  unsigned int len)
+{
+	struct wpa_used_freq_data *freqs;
+	unsigned int num, i, j;
+
+	freqs = os_calloc(wpa_s->num_multichan_concurrent,
+			  sizeof(struct wpa_used_freq_data));
+	if (!freqs)
+		return 0;
+
+	num = get_shared_radio_freqs_data(wpa_s, freqs,
+					  wpa_s->num_multichan_concurrent);
+
+	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].freq))
+			p2p_freqs[j++] = freqs[i];
+	}
+
+	os_free(freqs);
+
+	dump_freq_data(wpa_s, "valid for P2P", p2p_freqs, j);
+
+	return j;
+}
+
+
+static void wpas_p2p_set_own_freq_preference(struct wpa_supplicant *wpa_s,
+					     int freq)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return;
+
+	/* Use the wpa_s used to control the P2P Device operation */
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (wpa_s->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);
+}
+
+
+static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
+				      struct wpa_scan_results *scan_res)
+{
+	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;
+
+	wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS)",
+		   (int) scan_res->num);
+
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		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_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,
+					 ies, ies_len) > 0)
+			break;
+	}
+
+	p2p_scan_res_handled(wpa_s->global->p2p);
+}
+
+
+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);
+		p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret);
+		return;
+	}
+
+	p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret);
+	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_search_social_channel(struct wpa_supplicant *wpa_s,
+					  int freq)
+{
+	if (wpa_s->global->p2p_24ghz_social_channels &&
+	    (freq == 2412 || freq == 2437 || freq == 2462)) {
+		/*
+		 * Search all social channels regardless of whether these have
+		 * been disabled for P2P operating channel use to avoid missing
+		 * peers.
+		 */
+		return 1;
+	}
+	return p2p_supported_freq(wpa_s->global->p2p, freq);
+}
+
+
+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_driver_scan_params *params = NULL;
+	struct wpabuf *wps_ie, *ies;
+	unsigned int num_channels = 0;
+	int social_channels_freq[] = { 2412, 2437, 2462, 60480 };
+	size_t ielen;
+	u8 *n, i;
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		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;
+	}
+
+	params = os_zalloc(sizeof(*params));
+	if (params == NULL)
+		return -1;
+
+	/* P2P Wildcard SSID */
+	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)
+		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);
+		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;
+	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 = os_calloc(ARRAY_SIZE(social_channels_freq) + 1,
+					  sizeof(int));
+		if (params->freqs == NULL)
+			goto fail;
+		for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) {
+			if (wpas_p2p_search_social_channel(
+				    wpa_s, social_channels_freq[i]))
+				params->freqs[num_channels++] =
+					social_channels_freq[i];
+		}
+		params->freqs[num_channels++] = 0;
+		break;
+	case P2P_SCAN_FULL:
+		break;
+	case P2P_SCAN_SPECIFIC:
+		params->freqs = os_calloc(2, sizeof(int));
+		if (params->freqs == NULL)
+			goto fail;
+		params->freqs[0] = freq;
+		params->freqs[1] = 0;
+		break;
+	case P2P_SCAN_SOCIAL_PLUS_ONE:
+		params->freqs = os_calloc(ARRAY_SIZE(social_channels_freq) + 2,
+					  sizeof(int));
+		if (params->freqs == NULL)
+			goto fail;
+		for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) {
+			if (wpas_p2p_search_social_channel(
+				    wpa_s, social_channels_freq[i]))
+				params->freqs[num_channels++] =
+					social_channels_freq[i];
+		}
+		if (p2p_supported_freq(wpa_s->global->p2p, freq))
+			params->freqs[num_channels++] = freq;
+		params->freqs[num_channels++] = 0;
+		break;
+	}
+
+	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;
+
+fail:
+	wpa_scan_free_params(params);
+	return -1;
+}
+
+
+static enum wpa_driver_if_type wpas_p2p_if_type(int p2p_group_interface)
+{
+	switch (p2p_group_interface) {
+	case P2P_GROUP_INTERFACE_PENDING:
+		return WPA_IF_P2P_GROUP;
+	case P2P_GROUP_INTERFACE_GO:
+		return WPA_IF_P2P_GO;
+	case P2P_GROUP_INTERFACE_CLIENT:
+		return WPA_IF_P2P_CLIENT;
+	}
+
+	return WPA_IF_P2P_GROUP;
+}
+
+
+static struct wpa_supplicant * wpas_get_p2p_group(struct wpa_supplicant *wpa_s,
+						  const u8 *ssid,
+						  size_t ssid_len, int *go)
+{
+	struct wpa_ssid *s;
+
+	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		for (s = wpa_s->conf->ssid; s; s = s->next) {
+			if (s->disabled != 0 || !s->p2p_group ||
+			    s->ssid_len != ssid_len ||
+			    os_memcmp(ssid, s->ssid, ssid_len) != 0)
+				continue;
+			if (s->mode == WPAS_MODE_P2P_GO &&
+			    s != wpa_s->current_ssid)
+				continue;
+			if (go)
+				*go = s->mode == WPAS_MODE_P2P_GO;
+			return wpa_s;
+		}
+	}
+
+	return NULL;
+}
+
+
+static void run_wpas_p2p_disconnect(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	wpa_printf(MSG_DEBUG,
+		   "P2P: Complete previously requested removal of %s",
+		   wpa_s->ifname);
+	wpas_p2p_disconnect(wpa_s);
+}
+
+
+static int wpas_p2p_disconnect_safely(struct wpa_supplicant *wpa_s,
+				      struct wpa_supplicant *calling_wpa_s)
+{
+	if (calling_wpa_s == wpa_s && wpa_s &&
+	    wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
+		/*
+		 * The calling wpa_s instance is going to be removed. Do that
+		 * from an eloop callback to keep the instance available until
+		 * the caller has returned. This my be needed, e.g., to provide
+		 * control interface responses on the per-interface socket.
+		 */
+		if (eloop_register_timeout(0, 0, run_wpas_p2p_disconnect,
+					   wpa_s, NULL) < 0)
+			return -1;
+		return 0;
+	}
+
+	return wpas_p2p_disconnect(wpa_s);
+}
+
+
+/* Determine total number of clients in active groups where we are the GO */
+static unsigned int p2p_group_go_member_count(struct wpa_supplicant *wpa_s)
+{
+	unsigned int count = 0;
+	struct wpa_ssid *s;
+
+	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		for (s = wpa_s->conf->ssid; s; s = s->next) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
+				   wpa_s, s, s->disabled, s->p2p_group,
+				   s->mode);
+			if (!s->disabled && s->p2p_group &&
+			    s->mode == WPAS_MODE_P2P_GO) {
+				count += p2p_get_group_num_members(
+					wpa_s->p2p_group);
+			}
+		}
+	}
+
+	return count;
+}
+
+
+/* Find an interface for a P2P group where we are the GO */
+static struct wpa_supplicant *
+wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_supplicant *save = NULL;
+	struct wpa_ssid *s;
+
+	if (!wpa_s)
+		return NULL;
+
+	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		for (s = wpa_s->conf->ssid; s; s = s->next) {
+			if (s->disabled || !s->p2p_group ||
+			    s->mode != WPAS_MODE_P2P_GO)
+				continue;
+
+			/* Prefer a group with connected clients */
+			if (p2p_get_group_num_members(wpa_s->p2p_group))
+				return wpa_s;
+			save = wpa_s;
+		}
+	}
+
+	/* No group with connected clients, so pick the one without (if any) */
+	return save;
+}
+
+
+/* Find an active P2P group where we are the GO */
+static struct wpa_ssid * wpas_p2p_group_go_ssid(struct wpa_supplicant *wpa_s,
+						u8 *bssid)
+{
+	struct wpa_ssid *s, *empty = NULL;
+
+	if (!wpa_s)
+		return 0;
+
+	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		for (s = wpa_s->conf->ssid; s; s = s->next) {
+			if (s->disabled || !s->p2p_group ||
+			    s->mode != WPAS_MODE_P2P_GO)
+				continue;
+
+			os_memcpy(bssid, wpa_s->own_addr, ETH_ALEN);
+			if (p2p_get_group_num_members(wpa_s->p2p_group))
+				return s;
+			empty = s;
+		}
+	}
+
+	return empty;
+}
+
+
+/* Find a persistent group where we are the GO */
+static struct wpa_ssid *
+wpas_p2p_get_persistent_go(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *s;
+
+	for (s = wpa_s->conf->ssid; s; s = s->next) {
+		if (s->disabled == 2 && s->mode == WPAS_MODE_P2P_GO)
+			return s;
+	}
+
+	return NULL;
+}
+
+
+static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
+{
+	struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s;
+	struct wpa_ssid *s;
+	u8 conncap = P2PS_SETUP_NONE;
+	unsigned int owned_members = 0;
+	unsigned int owner = 0;
+	unsigned int client = 0;
+	struct wpa_supplicant *go_wpa_s;
+	struct wpa_ssid *persistent_go;
+	int p2p_no_group_iface;
+
+	wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
+
+	/*
+	 * For non-concurrent capable devices:
+	 * If persistent_go, then no new.
+	 * If GO, then no client.
+	 * If client, then no GO.
+	 */
+	go_wpa_s = wpas_p2p_get_go_group(wpa_s);
+	persistent_go = wpas_p2p_get_persistent_go(wpa_s);
+	p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s);
+
+	wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p",
+		   go_wpa_s, persistent_go);
+
+	for (tmp_wpa_s = wpa_s->global->ifaces; tmp_wpa_s;
+	     tmp_wpa_s = tmp_wpa_s->next) {
+		for (s = tmp_wpa_s->conf->ssid; s; s = s->next) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
+				   tmp_wpa_s, s, s->disabled,
+				   s->p2p_group, s->mode);
+			if (!s->disabled && s->p2p_group) {
+				if (s->mode == WPAS_MODE_P2P_GO) {
+					owned_members +=
+						p2p_get_group_num_members(
+							tmp_wpa_s->p2p_group);
+					owner++;
+				} else
+					client++;
+			}
+		}
+	}
+
+	/* If not concurrent, restrict our choices */
+	if (p2p_no_group_iface) {
+		wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface");
+
+		if (client)
+			return P2PS_SETUP_NONE;
+
+		if (go_wpa_s) {
+			if (role == P2PS_SETUP_CLIENT ||
+			    incoming == P2PS_SETUP_GROUP_OWNER ||
+			    p2p_client_limit_reached(go_wpa_s->p2p_group))
+				return P2PS_SETUP_NONE;
+
+			return P2PS_SETUP_GROUP_OWNER;
+		}
+
+		if (persistent_go) {
+			if (role == P2PS_SETUP_NONE || role == P2PS_SETUP_NEW) {
+				if (!incoming)
+					return P2PS_SETUP_GROUP_OWNER |
+						P2PS_SETUP_CLIENT;
+				if (incoming == P2PS_SETUP_NEW) {
+					u8 r;
+
+					if (os_get_random(&r, sizeof(r)) < 0 ||
+					    (r & 1))
+						return P2PS_SETUP_CLIENT;
+					return P2PS_SETUP_GROUP_OWNER;
+				}
+			}
+		}
+	}
+
+	/* If a required role has been specified, handle it here */
+	if (role && role != P2PS_SETUP_NEW) {
+		switch (incoming) {
+		case P2PS_SETUP_NONE:
+		case P2PS_SETUP_NEW:
+		case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
+		case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
+			conncap = role;
+			goto grp_owner;
+
+		case P2PS_SETUP_GROUP_OWNER:
+			/*
+			 * Must be a complimentary role - cannot be a client to
+			 * more than one peer.
+			 */
+			if (incoming == role || client)
+				return P2PS_SETUP_NONE;
+
+			return P2PS_SETUP_CLIENT;
+
+		case P2PS_SETUP_CLIENT:
+			/* Must be a complimentary role */
+			if (incoming != role) {
+				conncap = P2PS_SETUP_GROUP_OWNER;
+				goto grp_owner;
+			}
+
+		default:
+			return P2PS_SETUP_NONE;
+		}
+	}
+
+	/*
+	 * For now, we only will support ownership of one group, and being a
+	 * client of one group. Therefore, if we have either an existing GO
+	 * group, or an existing client group, we will not do a new GO
+	 * negotiation, but rather try to re-use the existing groups.
+	 */
+	switch (incoming) {
+	case P2PS_SETUP_NONE:
+	case P2PS_SETUP_NEW:
+		if (client)
+			conncap = P2PS_SETUP_GROUP_OWNER;
+		else if (!owned_members)
+			conncap = P2PS_SETUP_NEW;
+		else if (incoming == P2PS_SETUP_NONE)
+			conncap = P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT;
+		else
+			conncap = P2PS_SETUP_CLIENT;
+		break;
+
+	case P2PS_SETUP_CLIENT:
+		conncap = P2PS_SETUP_GROUP_OWNER;
+		break;
+
+	case P2PS_SETUP_GROUP_OWNER:
+		if (!client)
+			conncap = P2PS_SETUP_CLIENT;
+		break;
+
+	case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
+	case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
+		if (client)
+			conncap = P2PS_SETUP_GROUP_OWNER;
+		else {
+			u8 r;
+
+			if (os_get_random(&r, sizeof(r)) < 0 ||
+			    (r & 1))
+				conncap = P2PS_SETUP_CLIENT;
+			else
+				conncap = P2PS_SETUP_GROUP_OWNER;
+		}
+		break;
+
+	default:
+		return P2PS_SETUP_NONE;
+	}
+
+grp_owner:
+	if ((conncap & P2PS_SETUP_GROUP_OWNER) ||
+	    (!incoming && (conncap & P2PS_SETUP_NEW))) {
+		if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group))
+			conncap &= ~P2PS_SETUP_GROUP_OWNER;
+		wpa_printf(MSG_DEBUG, "P2P: GOs:%d members:%d conncap:%d",
+			   owner, owned_members, conncap);
+
+		s = wpas_p2p_get_persistent_go(wpa_s);
+
+		if (!s && !owner && p2p_no_group_iface) {
+			p2p_set_intended_addr(wpa_s->global->p2p,
+					      wpa_s->own_addr);
+		} else if (!s && !owner) {
+			if (wpas_p2p_add_group_interface(wpa_s,
+							 WPA_IF_P2P_GO) < 0) {
+				wpa_printf(MSG_ERROR,
+					   "P2P: Failed to allocate a new interface for the group");
+				return P2PS_SETUP_NONE;
+			}
+			wpa_s->global->pending_group_iface_for_p2ps = 1;
+			p2p_set_intended_addr(wpa_s->global->p2p,
+					      wpa_s->pending_interface_addr);
+		}
+	}
+
+	return conncap;
+}
+
+
+static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
+				 enum p2p_group_removal_reason removal_reason)
+{
+	struct wpa_ssid *ssid;
+	char *gtype;
+	const char *reason;
+
+	ssid = wpa_s->current_ssid;
+	if (ssid == NULL) {
+		/*
+		 * The current SSID was not known, but there may still be a
+		 * pending P2P group interface waiting for provisioning or a
+		 * P2P group that is trying to reconnect.
+		 */
+		ssid = wpa_s->conf->ssid;
+		while (ssid) {
+			if (ssid->p2p_group && ssid->disabled != 2)
+				break;
+			ssid = ssid->next;
+		}
+		if (ssid == NULL &&
+			wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)
+		{
+			wpa_printf(MSG_ERROR, "P2P: P2P group interface "
+				   "not found");
+			return -1;
+		}
+	}
+	if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO)
+		gtype = "GO";
+	else if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
+		 (ssid && ssid->mode == WPAS_MODE_INFRA)) {
+		wpa_s->reassociate = 0;
+		wpa_s->disconnected = 1;
+		gtype = "client";
+	} else
+		gtype = "GO";
+
+	if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid)
+		wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
+
+	if (os_strcmp(gtype, "client") == 0) {
+		wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+		if (eloop_is_timeout_registered(wpas_p2p_psk_failure_removal,
+						wpa_s, NULL)) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: PSK failure removal was scheduled, so use PSK failure as reason for group removal");
+			removal_reason = P2P_GROUP_REMOVAL_PSK_FAILURE;
+			eloop_cancel_timeout(wpas_p2p_psk_failure_removal,
+					     wpa_s, NULL);
+		}
+	}
+
+	if (wpa_s->cross_connect_in_use) {
+		wpa_s->cross_connect_in_use = 0;
+		wpa_msg_global(wpa_s->parent, MSG_INFO,
+			       P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+			       wpa_s->ifname, wpa_s->cross_connect_uplink);
+	}
+	switch (removal_reason) {
+	case P2P_GROUP_REMOVAL_REQUESTED:
+		reason = " reason=REQUESTED";
+		break;
+	case P2P_GROUP_REMOVAL_FORMATION_FAILED:
+		reason = " reason=FORMATION_FAILED";
+		break;
+	case P2P_GROUP_REMOVAL_IDLE_TIMEOUT:
+		reason = " reason=IDLE";
+		break;
+	case P2P_GROUP_REMOVAL_UNAVAILABLE:
+		reason = " reason=UNAVAILABLE";
+		break;
+	case P2P_GROUP_REMOVAL_GO_ENDING_SESSION:
+		reason = " reason=GO_ENDING_SESSION";
+		break;
+	case P2P_GROUP_REMOVAL_PSK_FAILURE:
+		reason = " reason=PSK_FAILURE";
+		break;
+	case P2P_GROUP_REMOVAL_FREQ_CONFLICT:
+		reason = " reason=FREQ_CONFLICT";
+		break;
+	default:
+		reason = "";
+		break;
+	}
+	if (removal_reason != P2P_GROUP_REMOVAL_SILENT) {
+		wpa_msg_global(wpa_s->parent, MSG_INFO,
+			       P2P_EVENT_GROUP_REMOVED "%s %s%s",
+			       wpa_s->ifname, gtype, reason);
+	}
+
+	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,
+				 wpa_s->parent, NULL) > 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation "
+			   "timeout");
+		wpa_s->p2p_in_provisioning = 0;
+		wpas_p2p_group_formation_failed(wpa_s, 1);
+	}
+
+	wpa_s->p2p_in_invitation = 0;
+	eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL);
+
+	/*
+	 * 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 (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
+		struct wpa_global *global;
+		char *ifname;
+		enum wpa_driver_if_type type;
+		wpa_printf(MSG_DEBUG, "P2P: Remove group interface %s",
+			wpa_s->ifname);
+		global = wpa_s->global;
+		ifname = os_strdup(wpa_s->ifname);
+		type = wpas_p2p_if_type(wpa_s->p2p_group_interface);
+		eloop_cancel_timeout(run_wpas_p2p_disconnect, wpa_s, NULL);
+		wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
+		wpa_s = global->ifaces;
+		if (wpa_s && ifname)
+			wpa_drv_if_remove(wpa_s, type, ifname);
+		os_free(ifname);
+		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;
+
+	os_free(wpa_s->p2p_group_common_freqs);
+	wpa_s->p2p_group_common_freqs = NULL;
+	wpa_s->p2p_group_common_freqs_num = 0;
+
+	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 ||
+		     (ssid->key_mgmt & WPA_KEY_MGMT_WPS))) {
+		int id = ssid->id;
+		if (ssid == wpa_s->current_ssid) {
+			wpa_sm_set_config(wpa_s->wpa, NULL);
+			eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+			wpa_s->current_ssid = NULL;
+		}
+		/*
+		 * Networks objects created during any P2P activities are not
+		 * exposed out as they might/will confuse certain non-P2P aware
+		 * applications since these network objects won't behave like
+		 * regular ones.
+		 *
+		 * Likewise, we don't send out network removed signals for such
+		 * network objects.
+		 */
+		wpa_config_remove_network(wpa_s->conf, id);
+		wpa_supplicant_clear_status(wpa_s);
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+	} else {
+		wpa_printf(MSG_DEBUG, "P2P: Temporary group network not "
+			   "found");
+	}
+	if (wpa_s->ap_iface)
+		wpa_supplicant_ap_deinit(wpa_s);
+	else
+		wpa_drv_deinit_p2p_cli(wpa_s);
+
+	os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+
+	return 0;
+}
+
+
+static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s,
+				     u8 *go_dev_addr,
+				     const u8 *ssid, size_t ssid_len)
+{
+	struct wpa_bss *bss;
+	const u8 *bssid;
+	struct wpabuf *p2p;
+	u8 group_capab;
+	const u8 *addr;
+
+	if (wpa_s->go_params)
+		bssid = wpa_s->go_params->peer_interface_addr;
+	else
+		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,
+					   iface_addr) == 0)
+			bss = wpa_bss_get(wpa_s, iface_addr, ssid, ssid_len);
+	}
+	if (bss == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
+			   "group is persistent - BSS " MACSTR " not found",
+			   MAC2STR(bssid));
+		return 0;
+	}
+
+	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
+			   " did not include P2P IE", MAC2STR(bssid));
+		wpa_hexdump(MSG_DEBUG, "P2P: Probe Response IEs",
+			    (u8 *) (bss + 1), bss->ie_len);
+		wpa_hexdump(MSG_DEBUG, "P2P: Beacon IEs",
+			    ((u8 *) bss + 1) + bss->ie_len,
+			    bss->beacon_ie_len);
+		return 0;
+	}
+
+	group_capab = p2p_get_group_capab(p2p);
+	addr = p2p_get_go_dev_addr(p2p);
+	wpa_printf(MSG_DEBUG, "P2P: Checking whether group is persistent: "
+		   "group_capab=0x%x", group_capab);
+	if (addr) {
+		os_memcpy(go_dev_addr, addr, ETH_ALEN);
+		wpa_printf(MSG_DEBUG, "P2P: GO Device Address " MACSTR,
+			   MAC2STR(addr));
+	} else
+		os_memset(go_dev_addr, 0, ETH_ALEN);
+	wpabuf_free(p2p);
+
+	wpa_printf(MSG_DEBUG, "P2P: BSS " MACSTR " group_capab=0x%x "
+		   "go_dev_addr=" MACSTR,
+		   MAC2STR(bssid), group_capab, MAC2STR(go_dev_addr));
+
+	return group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+}
+
+
+static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s,
+					   struct wpa_ssid *ssid,
+					   const u8 *go_dev_addr)
+{
+	struct wpa_ssid *s;
+	int changed = 0;
+
+	wpa_printf(MSG_DEBUG, "P2P: Storing credentials for a persistent "
+		   "group (GO Dev Addr " MACSTR ")", MAC2STR(go_dev_addr));
+	for (s = wpa_s->conf->ssid; s; s = s->next) {
+		if (s->disabled == 2 &&
+		    os_memcmp(go_dev_addr, s->bssid, ETH_ALEN) == 0 &&
+		    s->ssid_len == ssid->ssid_len &&
+		    os_memcmp(ssid->ssid, s->ssid, ssid->ssid_len) == 0)
+			break;
+	}
+
+	if (s) {
+		wpa_printf(MSG_DEBUG, "P2P: Update existing persistent group "
+			   "entry");
+		if (ssid->passphrase && !s->passphrase)
+			changed = 1;
+		else if (ssid->passphrase && s->passphrase &&
+			 os_strcmp(ssid->passphrase, s->passphrase) != 0)
+			changed = 1;
+	} else {
+		wpa_printf(MSG_DEBUG, "P2P: Create a new persistent group "
+			   "entry");
+		changed = 1;
+		s = wpa_config_add_network(wpa_s->conf);
+		if (s == NULL)
+			return -1;
+
+		/*
+		 * Instead of network_added we emit persistent_group_added
+		 * notification. Also to keep the defense checks in
+		 * persistent_group obj registration method, we set the
+		 * relevant flags in s to designate it as a persistent group.
+		 */
+		s->p2p_group = 1;
+		s->p2p_persistent_group = 1;
+		wpas_notify_persistent_group_added(wpa_s, s);
+		wpa_config_set_network_defaults(s);
+	}
+
+	s->p2p_group = 1;
+	s->p2p_persistent_group = 1;
+	s->disabled = 2;
+	s->bssid_set = 1;
+	os_memcpy(s->bssid, go_dev_addr, ETH_ALEN);
+	s->mode = ssid->mode;
+	s->auth_alg = WPA_AUTH_ALG_OPEN;
+	s->key_mgmt = WPA_KEY_MGMT_PSK;
+	s->proto = WPA_PROTO_RSN;
+	s->pairwise_cipher = WPA_CIPHER_CCMP;
+	s->export_keys = 1;
+	if (ssid->passphrase) {
+		os_free(s->passphrase);
+		s->passphrase = os_strdup(ssid->passphrase);
+	}
+	if (ssid->psk_set) {
+		s->psk_set = 1;
+		os_memcpy(s->psk, ssid->psk, 32);
+	}
+	if (s->passphrase && !s->psk_set)
+		wpa_config_update_psk(s);
+	if (s->ssid == NULL || s->ssid_len < ssid->ssid_len) {
+		os_free(s->ssid);
+		s->ssid = os_malloc(ssid->ssid_len);
+	}
+	if (s->ssid) {
+		s->ssid_len = ssid->ssid_len;
+		os_memcpy(s->ssid, ssid->ssid, s->ssid_len);
+	}
+	if (ssid->mode == WPAS_MODE_P2P_GO && wpa_s->global->add_psk) {
+		dl_list_add(&s->psk_list, &wpa_s->global->add_psk->list);
+		wpa_s->global->add_psk = NULL;
+		changed = 1;
+	}
+
+	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");
+	}
+
+	return s->id;
+}
+
+
+static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s,
+						 const u8 *addr)
+{
+	struct wpa_ssid *ssid, *s;
+	u8 *n;
+	size_t i;
+	int found = 0;
+	struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	ssid = wpa_s->current_ssid;
+	if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
+	    !ssid->p2p_persistent_group)
+		return;
+
+	for (s = p2p_wpa_s->conf->ssid; s; s = s->next) {
+		if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
+			continue;
+
+		if (s->ssid_len == ssid->ssid_len &&
+		    os_memcmp(s->ssid, ssid->ssid, s->ssid_len) == 0)
+			break;
+	}
+
+	if (s == NULL)
+		return;
+
+	for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) {
+		if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, addr,
+			      ETH_ALEN) != 0)
+			continue;
+
+		if (i == s->num_p2p_clients - 1)
+			return; /* already the most recent entry */
+
+		/* move the entry to mark it most recent */
+		os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN,
+			   s->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
+			   (s->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
+		os_memcpy(s->p2p_client_list +
+			  (s->num_p2p_clients - 1) * 2 * ETH_ALEN, addr,
+			  ETH_ALEN);
+		os_memset(s->p2p_client_list +
+			  (s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN,
+			  0xff, ETH_ALEN);
+		found = 1;
+		break;
+	}
+
+	if (!found && s->num_p2p_clients < P2P_MAX_STORED_CLIENTS) {
+		n = os_realloc_array(s->p2p_client_list,
+				     s->num_p2p_clients + 1, 2 * ETH_ALEN);
+		if (n == NULL)
+			return;
+		os_memcpy(n + s->num_p2p_clients * 2 * ETH_ALEN, addr,
+			  ETH_ALEN);
+		os_memset(n + s->num_p2p_clients * 2 * ETH_ALEN + ETH_ALEN,
+			  0xff, ETH_ALEN);
+		s->p2p_client_list = n;
+		s->num_p2p_clients++;
+	} else if (!found && s->p2p_client_list) {
+		/* Not enough room for an additional entry - drop the oldest
+		 * entry */
+		os_memmove(s->p2p_client_list,
+			   s->p2p_client_list + 2 * ETH_ALEN,
+			   (s->num_p2p_clients - 1) * 2 * ETH_ALEN);
+		os_memcpy(s->p2p_client_list +
+			  (s->num_p2p_clients - 1) * 2 * ETH_ALEN,
+			  addr, ETH_ALEN);
+		os_memset(s->p2p_client_list +
+			  (s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN,
+			  0xff, ETH_ALEN);
+	}
+
+	if (p2p_wpa_s->conf->update_config &&
+	    wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf))
+		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+}
+
+
+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);
+}
+
+
+static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
+					   int success, int already_deleted)
+{
+	struct wpa_ssid *ssid;
+	int client;
+	int persistent;
+	u8 go_dev_addr[ETH_ALEN];
+	int network_id = -1;
+
+	/*
+	 * This callback is likely called for the main interface. Update wpa_s
+	 * to use the group interface if a new interface was created for the
+	 * group.
+	 */
+	if (wpa_s->global->p2p_group_formation)
+		wpa_s = wpa_s->global->p2p_group_formation;
+	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;
+	wpa_s->group_formation_reported = 1;
+
+	if (!success) {
+		wpa_msg_global(wpa_s->parent, MSG_INFO,
+			       P2P_EVENT_GROUP_FORMATION_FAILURE);
+		wpas_notify_p2p_group_formation_failure(wpa_s, "");
+		if (already_deleted)
+			return;
+		wpas_p2p_group_delete(wpa_s,
+				      P2P_GROUP_REMOVAL_FORMATION_FAILED);
+		return;
+	}
+
+	wpa_msg_global(wpa_s->parent, MSG_INFO,
+		       P2P_EVENT_GROUP_FORMATION_SUCCESS);
+
+	ssid = wpa_s->current_ssid;
+	if (ssid && ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
+		ssid->mode = WPAS_MODE_P2P_GO;
+		p2p_group_notif_formation_done(wpa_s->p2p_group);
+		wpa_supplicant_ap_mac_addr_filter(wpa_s, NULL);
+	}
+
+	persistent = 0;
+	if (ssid) {
+		client = ssid->mode == WPAS_MODE_INFRA;
+		if (ssid->mode == WPAS_MODE_P2P_GO) {
+			persistent = ssid->p2p_persistent_group;
+			os_memcpy(go_dev_addr, wpa_s->global->p2p_dev_addr,
+				  ETH_ALEN);
+		} else
+			persistent = wpas_p2p_persistent_group(wpa_s,
+							       go_dev_addr,
+							       ssid->ssid,
+							       ssid->ssid_len);
+	} else {
+		client = wpa_s->p2p_group_interface ==
+			P2P_GROUP_INTERFACE_CLIENT;
+		os_memset(go_dev_addr, 0, ETH_ALEN);
+	}
+
+	wpa_s->show_group_started = 0;
+	if (client) {
+		/*
+		 * Indicate event only after successfully completed 4-way
+		 * handshake, i.e., when the interface is ready for data
+		 * packets.
+		 */
+		wpa_s->show_group_started = 1;
+	} else {
+		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);
+	}
+
+	if (persistent)
+		network_id = wpas_p2p_store_persistent_group(wpa_s->parent,
+							     ssid, go_dev_addr);
+	else {
+		os_free(wpa_s->global->add_psk);
+		wpa_s->global->add_psk = NULL;
+	}
+	if (network_id < 0 && ssid)
+		network_id = ssid->id;
+	if (!client) {
+		wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
+		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);
+		}
+	}
+}
+
+
+static void wpas_p2p_send_action_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)
+{
+	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;
+
+	switch (result) {
+	case OFFCHANNEL_SEND_ACTION_SUCCESS:
+		res = P2P_SEND_ACTION_SUCCESS;
+		break;
+	case OFFCHANNEL_SEND_ACTION_NO_ACK:
+		res = P2P_SEND_ACTION_NO_ACK;
+		break;
+	case OFFCHANNEL_SEND_ACTION_FAILED:
+		res = P2P_SEND_ACTION_FAILED;
+		break;
+	}
+
+	p2p_send_action_cb(wpa_s->global->p2p, freq, dst, src, bssid, res);
+
+	if (result != OFFCHANNEL_SEND_ACTION_SUCCESS &&
+	    wpa_s->pending_pd_before_join &&
+	    (os_memcmp(dst, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
+	     os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0) &&
+	    wpa_s->p2p_fallback_to_go_neg) {
+		wpa_s->pending_pd_before_join = 0;
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req "
+			"during p2p_connect-auto");
+		wpa_msg_global(wpa_s->parent, MSG_INFO,
+			       P2P_EVENT_FALLBACK_TO_GO_NEG
+			       "reason=no-ACK-to-PD-Req");
+		wpas_p2p_fallback_to_go_neg(wpa_s, 0);
+		return;
+	}
+}
+
+
+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);
+}
+
+
+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);
+}
+
+
+static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s,
+				    struct p2p_go_neg_results *params)
+{
+	if (wpa_s->go_params == NULL) {
+		wpa_s->go_params = os_malloc(sizeof(*params));
+		if (wpa_s->go_params == NULL)
+			return -1;
+	}
+	os_memcpy(wpa_s->go_params, params, sizeof(*params));
+	return 0;
+}
+
+
+static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
+				    struct p2p_go_neg_results *res)
+{
+	wpa_s->group_formation_reported = 0;
+	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) {
+		wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1);
+#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_P2PS)
+			dev_pw_id = DEV_PW_P2PS_DEFAULT;
+		if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD)
+			dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED;
+		wpas_wps_start_pin(wpa_s, res->peer_interface_addr,
+				   wpa_s->p2p_pin, 1, dev_pw_id);
+	}
+}
+
+
+static void wpas_p2p_add_psk_list(struct wpa_supplicant *wpa_s,
+				  struct wpa_ssid *ssid)
+{
+	struct wpa_ssid *persistent;
+	struct psk_list_entry *psk;
+	struct hostapd_data *hapd;
+
+	if (!wpa_s->ap_iface)
+		return;
+
+	persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid,
+					     ssid->ssid_len);
+	if (persistent == NULL)
+		return;
+
+	hapd = wpa_s->ap_iface->bss[0];
+
+	dl_list_for_each(psk, &persistent->psk_list, struct psk_list_entry,
+			 list) {
+		struct hostapd_wpa_psk *hpsk;
+
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add persistent group PSK entry for "
+			MACSTR " psk=%d",
+			MAC2STR(psk->addr), psk->p2p);
+		hpsk = os_zalloc(sizeof(*hpsk));
+		if (hpsk == NULL)
+			break;
+		os_memcpy(hpsk->psk, psk->psk, PMK_LEN);
+		if (psk->p2p)
+			os_memcpy(hpsk->p2p_dev_addr, psk->addr, ETH_ALEN);
+		else
+			os_memcpy(hpsk->addr, psk->addr, ETH_ALEN);
+		hpsk->next = hapd->conf->ssid.wpa_psk;
+		hapd->conf->ssid.wpa_psk = hpsk;
+	}
+}
+
+
+static void p2p_go_dump_common_freqs(struct wpa_supplicant *wpa_s)
+{
+	unsigned int i;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Common group frequencies (len=%u):",
+		wpa_s->p2p_group_common_freqs_num);
+
+	for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++)
+		wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d",
+			i, wpa_s->p2p_group_common_freqs[i]);
+}
+
+
+static void p2p_go_save_group_common_freqs(struct wpa_supplicant *wpa_s,
+					   struct p2p_go_neg_results *params)
+{
+	unsigned int i, len = int_array_len(wpa_s->go_params->freq_list);
+
+	wpa_s->p2p_group_common_freqs_num = 0;
+	os_free(wpa_s->p2p_group_common_freqs);
+	wpa_s->p2p_group_common_freqs = os_calloc(len, sizeof(int));
+	if (!wpa_s->p2p_group_common_freqs)
+		return;
+
+	for (i = 0; i < len; i++) {
+		if (!wpa_s->go_params->freq_list[i])
+			break;
+		wpa_s->p2p_group_common_freqs[i] =
+			wpa_s->go_params->freq_list[i];
+	}
+	wpa_s->p2p_group_common_freqs_num = i;
+}
+
+
+static void p2p_config_write(struct wpa_supplicant *wpa_s)
+{
+#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 p2p_go_configured(void *ctx, void *data)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct p2p_go_neg_results *params = data;
+	struct wpa_ssid *ssid;
+	int network_id = -1;
+
+	p2p_go_save_group_common_freqs(wpa_s, params);
+	p2p_go_dump_common_freqs(wpa_s);
+
+	ssid = wpa_s->current_ssid;
+	if (ssid && ssid->mode == WPAS_MODE_P2P_GO) {
+		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;
+		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, "");
+		wpa_s->group_formation_reported = 1;
+
+		if (wpa_s->parent->p2ps_method_config_any) {
+			if (is_zero_ether_addr(wpa_s->parent->p2ps_join_addr)) {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"P2PS: Setting default PIN for ANY");
+				wpa_supplicant_ap_wps_pin(wpa_s, NULL,
+							  "12345670", NULL, 0,
+							  0);
+			} else {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"P2PS: Setting default PIN for " MACSTR,
+					MAC2STR(wpa_s->parent->p2ps_join_addr));
+				wpa_supplicant_ap_wps_pin(
+					wpa_s, wpa_s->parent->p2ps_join_addr,
+					"12345670", NULL, 0, 0);
+			}
+			wpa_s->parent->p2ps_method_config_any = 0;
+		}
+
+		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,
+				wpa_s->global->p2p_dev_addr);
+			wpas_p2p_add_psk_list(wpa_s, ssid);
+		}
+		if (network_id < 0)
+			network_id = ssid->id;
+		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;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning");
+	if (wpa_supplicant_ap_mac_addr_filter(wpa_s,
+					      params->peer_interface_addr)) {
+		wpa_printf(MSG_DEBUG, "P2P: Failed to setup MAC address "
+			   "filtering");
+		return;
+	}
+	if (params->wps_method == WPS_PBC) {
+		wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr,
+					  params->peer_device_addr);
+#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);
+	wpa_s->go_params = NULL;
+}
+
+
+static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
+			      struct p2p_go_neg_results *params,
+			      int group_formation)
+{
+	struct wpa_ssid *ssid;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Starting GO");
+	if (wpas_copy_go_neg_results(wpa_s, params) < 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not copy GO Negotiation "
+			"results");
+		return;
+	}
+
+	ssid = wpa_config_add_network(wpa_s->conf);
+	if (ssid == NULL) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not add network for GO");
+		return;
+	}
+
+	wpa_s->show_group_started = 0;
+	wpa_s->p2p_go_group_formation_completed = 0;
+	wpa_s->group_formation_reported = 0;
+	os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+
+	wpa_config_set_network_defaults(ssid);
+	ssid->temporary = 1;
+	ssid->p2p_group = 1;
+	ssid->p2p_persistent_group = params->persistent_group;
+	ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION :
+		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);
+		ssid->ssid_len = params->ssid_len;
+	}
+	ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+	ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+	ssid->proto = WPA_PROTO_RSN;
+	ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+	ssid->group_cipher = WPA_CIPHER_CCMP;
+	if (params->freq > 56160) {
+		/*
+		 * Enable GCMP instead of CCMP as pairwise_cipher and
+		 * group_cipher in 60 GHz.
+		 */
+		ssid->pairwise_cipher = WPA_CIPHER_GCMP;
+		ssid->group_cipher = WPA_CIPHER_GCMP;
+	}
+	if (os_strlen(params->passphrase) > 0) {
+		ssid->passphrase = os_strdup(params->passphrase);
+		if (ssid->passphrase == NULL) {
+			wpa_msg_global(wpa_s, MSG_ERROR,
+				       "P2P: Failed to copy passphrase for GO");
+			wpa_config_remove_network(wpa_s->conf, ssid->id);
+			return;
+		}
+	} else
+		ssid->passphrase = NULL;
+	ssid->psk_set = params->psk_set;
+	if (ssid->psk_set)
+		os_memcpy(ssid->psk, params->psk, sizeof(ssid->psk));
+	else if (ssid->passphrase)
+		wpa_config_update_psk(ssid);
+	ssid->ap_max_inactivity = wpa_s->parent->conf->p2p_go_max_inactivity;
+
+	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;
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Request scan (that will be skipped) to "
+		"start GO)");
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
+				  const struct wpa_supplicant *src)
+{
+	struct wpa_config *d;
+	const struct wpa_config *s;
+
+	d = dst->conf;
+	s = src->conf;
+
+#define C(n) if (s->n) d->n = os_strdup(s->n)
+	C(device_name);
+	C(manufacturer);
+	C(model_name);
+	C(model_number);
+	C(serial_number);
+	C(config_methods);
+#undef C
+
+	os_memcpy(d->device_type, s->device_type, WPS_DEV_TYPE_LEN);
+	os_memcpy(d->sec_device_type, s->sec_device_type,
+		  sizeof(d->sec_device_type));
+	d->num_sec_device_types = s->num_sec_device_types;
+
+	d->p2p_group_idle = s->p2p_group_idle;
+	d->p2p_go_freq_change_policy = s->p2p_go_freq_change_policy;
+	d->p2p_intra_bss = s->p2p_intra_bss;
+	d->persistent_reconnect = s->persistent_reconnect;
+	d->max_num_sta = s->max_num_sta;
+	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->p2p_go_ctwindow = s->p2p_go_ctwindow;
+	d->disassoc_low_ack = s->disassoc_low_ack;
+	d->disable_scan_offload = s->disable_scan_offload;
+	d->passive_scan = s->passive_scan;
+
+	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);
+	}
+	d->p2p_cli_probe = s->p2p_cli_probe;
+}
+
+
+static void wpas_p2p_get_group_ifname(struct wpa_supplicant *wpa_s,
+				      char *ifname, size_t len)
+{
+	char *ifname_ptr = wpa_s->ifname;
+
+	if (os_strncmp(wpa_s->ifname, P2P_MGMT_DEVICE_PREFIX,
+		       os_strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) {
+		ifname_ptr = os_strrchr(wpa_s->ifname, '-') + 1;
+	}
+
+	os_snprintf(ifname, len, "p2p-%s-%d", ifname_ptr, wpa_s->p2p_group_idx);
+	if (os_strlen(ifname) >= IFNAMSIZ &&
+	    os_strlen(wpa_s->ifname) < IFNAMSIZ) {
+		int res;
+
+		/* Try to avoid going over the IFNAMSIZ length limit */
+		res = os_snprintf(ifname, len, "p2p-%d", wpa_s->p2p_group_idx);
+		if (os_snprintf_error(len, res) && len)
+			ifname[len - 1] = '\0';
+	}
+}
+
+
+static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
+					enum wpa_driver_if_type type)
+{
+	char ifname[120], force_ifname[120];
+
+	if (wpa_s->pending_interface_name[0]) {
+		wpa_printf(MSG_DEBUG, "P2P: Pending virtual interface exists "
+			   "- skip creation of a new one");
+		if (is_zero_ether_addr(wpa_s->pending_interface_addr)) {
+			wpa_printf(MSG_DEBUG, "P2P: Pending virtual address "
+				   "unknown?! ifname='%s'",
+				   wpa_s->pending_interface_name);
+			return -1;
+		}
+		return 0;
+	}
+
+	wpas_p2p_get_group_ifname(wpa_s, ifname, sizeof(ifname));
+	force_ifname[0] = '\0';
+
+	wpa_printf(MSG_DEBUG, "P2P: Create a new interface %s for the group",
+		   ifname);
+	wpa_s->p2p_group_idx++;
+
+	wpa_s->pending_interface_type = type;
+	if (wpa_drv_if_add(wpa_s, type, ifname, NULL, NULL, force_ifname,
+			   wpa_s->pending_interface_addr, NULL) < 0) {
+		wpa_printf(MSG_ERROR, "P2P: Failed to create new group "
+			   "interface");
+		return -1;
+	}
+
+	if (force_ifname[0]) {
+		wpa_printf(MSG_DEBUG, "P2P: Driver forced interface name %s",
+			   force_ifname);
+		os_strlcpy(wpa_s->pending_interface_name, force_ifname,
+			   sizeof(wpa_s->pending_interface_name));
+	} else
+		os_strlcpy(wpa_s->pending_interface_name, ifname,
+			   sizeof(wpa_s->pending_interface_name));
+	wpa_printf(MSG_DEBUG, "P2P: Created pending virtual interface %s addr "
+		   MACSTR, wpa_s->pending_interface_name,
+		   MAC2STR(wpa_s->pending_interface_addr));
+
+	return 0;
+}
+
+
+static void wpas_p2p_remove_pending_group_interface(
+	struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->pending_interface_name[0] ||
+	    is_zero_ether_addr(wpa_s->pending_interface_addr))
+		return; /* No pending virtual interface */
+
+	wpa_printf(MSG_DEBUG, "P2P: Removing pending group interface %s",
+		   wpa_s->pending_interface_name);
+	wpa_drv_if_remove(wpa_s, wpa_s->pending_interface_type,
+			  wpa_s->pending_interface_name);
+	os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
+	wpa_s->pending_interface_name[0] = '\0';
+	wpa_s->global->pending_group_iface_for_p2ps = 0;
+}
+
+
+static struct wpa_supplicant *
+wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go)
+{
+	struct wpa_interface iface;
+	struct wpa_supplicant *group_wpa_s;
+
+	if (!wpa_s->pending_interface_name[0]) {
+		wpa_printf(MSG_ERROR, "P2P: No pending group interface");
+		if (!wpas_p2p_create_iface(wpa_s))
+			return NULL;
+		/*
+		 * Something has forced us to remove the pending interface; try
+		 * to create a new one and hope for the best that we will get
+		 * the same local address.
+		 */
+		if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
+						 WPA_IF_P2P_CLIENT) < 0)
+			return NULL;
+	}
+
+	os_memset(&iface, 0, sizeof(iface));
+	iface.ifname = wpa_s->pending_interface_name;
+	iface.driver = wpa_s->driver->name;
+	if (wpa_s->conf->ctrl_interface == NULL &&
+	    wpa_s->parent != wpa_s &&
+	    wpa_s->p2p_mgmt &&
+	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE))
+		iface.ctrl_interface = wpa_s->parent->conf->ctrl_interface;
+	else
+		iface.ctrl_interface = wpa_s->conf->ctrl_interface;
+	iface.driver_param = wpa_s->conf->driver_param;
+	group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
+	if (group_wpa_s == NULL) {
+		wpa_printf(MSG_ERROR, "P2P: Failed to create new "
+			   "wpa_supplicant interface");
+		return NULL;
+	}
+	wpa_s->pending_interface_name[0] = '\0';
+	group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO :
+		P2P_GROUP_INTERFACE_CLIENT;
+	wpa_s->global->p2p_group_formation = group_wpa_s;
+	wpa_s->global->pending_group_iface_for_p2ps = 0;
+
+	wpas_p2p_clone_config(group_wpa_s, wpa_s);
+
+	return group_wpa_s;
+}
+
+
+static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
+					     void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out");
+	wpas_p2p_group_formation_failed(wpa_s, 0);
+}
+
+
+static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
+					    int already_deleted)
+{
+	eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+			     wpa_s->parent, NULL);
+	if (wpa_s->global->p2p)
+		p2p_group_formation_failed(wpa_s->global->p2p);
+	wpas_group_formation_completed(wpa_s, 0, already_deleted);
+}
+
+
+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)
+		return;
+	/* Speed up group formation timeout since this cannot succeed */
+	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);
+}
+
+
+static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+		wpa_drv_cancel_remain_on_channel(wpa_s);
+		wpa_s->off_channel_freq = 0;
+		wpa_s->roc_waiting_drv_freq = 0;
+	}
+
+	if (res->status) {
+		wpa_msg_global(wpa_s, MSG_INFO,
+			       P2P_EVENT_GO_NEG_FAILURE "status=%d",
+			       res->status);
+		wpas_notify_p2p_go_neg_completed(wpa_s, res);
+		wpas_p2p_remove_pending_group_interface(wpa_s);
+		return;
+	}
+
+	if (!res->role_go) {
+		/* Inform driver of the operating channel of GO. */
+		wpa_drv_set_prob_oper_freq(wpa_s, res->freq);
+	}
+
+	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
+		       " wps_method=%s",
+		       res->role_go ? "GO" : "client", res->freq, res->ht40,
+		       MAC2STR(res->peer_device_addr),
+		       MAC2STR(res->peer_interface_addr),
+		       p2p_wps_method_text(res->wps_method));
+	wpas_notify_p2p_go_neg_completed(wpa_s, res);
+
+	if (res->role_go && wpa_s->p2p_persistent_id >= 0) {
+		struct wpa_ssid *ssid;
+		ssid = wpa_config_get_network(wpa_s->conf,
+					      wpa_s->p2p_persistent_id);
+		if (ssid && ssid->disabled == 2 &&
+		    ssid->mode == WPAS_MODE_P2P_GO && ssid->passphrase) {
+			size_t len = os_strlen(ssid->passphrase);
+			wpa_printf(MSG_DEBUG, "P2P: Override passphrase based "
+				   "on requested persistent group");
+			os_memcpy(res->passphrase, ssid->passphrase, len);
+			res->passphrase[len] = '\0';
+		}
+	}
+
+	if (wpa_s->create_p2p_iface) {
+		struct wpa_supplicant *group_wpa_s =
+			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, 1);
+			return;
+		}
+		if (group_wpa_s != wpa_s) {
+			os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin,
+				  sizeof(group_wpa_s->p2p_pin));
+			group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method;
+		}
+		os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
+		wpa_s->pending_interface_name[0] = '\0';
+		group_wpa_s->p2p_in_provisioning = 1;
+
+		if (res->role_go) {
+			wpas_start_wps_go(group_wpa_s, res, 1);
+		} else {
+			os_get_reltime(&group_wpa_s->scan_min_time);
+			wpas_start_wps_enrollee(group_wpa_s, res);
+		}
+	} else {
+		wpa_s->p2p_in_provisioning = 1;
+		wpa_s->global->p2p_group_formation = wpa_s;
+
+		if (res->role_go) {
+			wpas_start_wps_go(wpa_s, res, 1);
+		} else {
+			os_get_reltime(&wpa_s->scan_min_time);
+			wpas_start_wps_enrollee(ctx, res);
+		}
+	}
+
+	wpa_s->p2p_long_listen = 0;
+	eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+
+	eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
+	eloop_register_timeout(15 + res->peer_config_timeout / 100,
+			       (res->peer_config_timeout % 100) * 10000,
+			       wpas_p2p_group_formation_timeout, wpa_s, NULL);
+}
+
+
+static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id,
+			       u8 go_intent)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR
+		       " dev_passwd_id=%u go_intent=%u", MAC2STR(src),
+		       dev_passwd_id, go_intent);
+
+	wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent);
+}
+
+
+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];
+	char *wfd_dev_info_hex = NULL;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	wfd_dev_info_hex = wifi_display_subelem_hex(info->wfd_subelems,
+						    WFD_SUBELEM_DEVICE_INFO);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	if (info->p2ps_instance) {
+		char str[256];
+		const u8 *buf = wpabuf_head(info->p2ps_instance);
+		size_t len = wpabuf_len(info->p2ps_instance);
+
+		while (len) {
+			u32 id;
+			u16 methods;
+			u8 str_len;
+
+			if (len < 4 + 2 + 1)
+				break;
+			id = WPA_GET_LE32(buf);
+			buf += sizeof(u32);
+			methods = WPA_GET_BE16(buf);
+			buf += sizeof(u16);
+			str_len = *buf++;
+			if (str_len > len - 4 - 2 - 1)
+				break;
+			os_memcpy(str, buf, str_len);
+			str[str_len] = '\0';
+			buf += str_len;
+			len -= str_len + sizeof(u32) + sizeof(u16) + sizeof(u8);
+
+			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"
+				       " adv_id=%x asp_svc=%s%s",
+				       MAC2STR(addr),
+				       MAC2STR(info->p2p_device_addr),
+				       wps_dev_type_bin2str(
+					       info->pri_dev_type,
+					       devtype, sizeof(devtype)),
+				       info->device_name, methods,
+				       info->dev_capab, info->group_capab,
+				       id, str,
+				       info->vendor_elems ?
+				       " vendor_elems=1" : "");
+		}
+		goto done;
+	}
+
+	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
+		       " p2p_dev_addr=" MACSTR
+		       " pri_dev_type=%s name='%s' config_methods=0x%x "
+		       "dev_capab=0x%x group_capab=0x%x%s%s%s new=%d",
+		       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 ? " wfd_dev_info=0x" : "",
+		       wfd_dev_info_hex ? wfd_dev_info_hex : "",
+		       info->vendor_elems ? " vendor_elems=1" : "",
+		       new_device);
+
+done:
+	os_free(wfd_dev_info_hex);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+	wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device);
+}
+
+
+static void wpas_dev_lost(void *ctx, const u8 *dev_addr)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_LOST
+		       "p2p_dev_addr=" MACSTR, MAC2STR(dev_addr));
+
+	wpas_notify_p2p_device_lost(wpa_s, dev_addr);
+}
+
+
+static void wpas_find_stopped(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED);
+	wpas_notify_p2p_find_stopped(wpa_s);
+}
+
+
+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;
+	unsigned int duration;
+
+	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;
+
+	duration = lwork->duration;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->extra_roc_dur) {
+		wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u",
+			   duration, duration + wpa_s->extra_roc_dur);
+		duration += wpa_s->extra_roc_dur;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, 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;
+
+	if (wpa_s->p2p_listen_work) {
+		wpa_printf(MSG_DEBUG, "P2P: Reject start_listen since p2p_listen_work already exists");
+		return -1;
+	}
+
+	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 (radio_add_work(wpa_s, freq, "p2p-listen", 0, wpas_start_listen_cb,
+			   lwork) < 0) {
+		wpas_p2p_listen_work_free(lwork);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void wpas_stop_listen(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+		wpa_drv_cancel_remain_on_channel(wpa_s);
+		wpa_s->off_channel_freq = 0;
+		wpa_s->roc_waiting_drv_freq = 0;
+	}
+	wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL);
+
+	/*
+	 * Don't cancel Probe Request RX reporting for a connected P2P Client
+	 * handling Probe Request frames.
+	 */
+	if (!wpa_s->p2p_cli_probe)
+		wpa_drv_probe_req_report(wpa_s, 0);
+
+	wpas_p2p_listen_work_done(wpa_s);
+}
+
+
+static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf,
+				unsigned int freq)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1,
+				 freq);
+}
+
+
+static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s,
+					 const u8 *peer, const char *params,
+					 unsigned int generated_pin)
+{
+	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_SHOW_PIN MACSTR
+		       " %08d%s", MAC2STR(peer), generated_pin, params);
+}
+
+
+static void wpas_prov_disc_local_keypad(struct wpa_supplicant *wpa_s,
+					const u8 *peer, const char *params)
+{
+	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_ENTER_PIN MACSTR
+		       "%s", MAC2STR(peer), params);
+}
+
+
+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];
+	char params[300];
+	u8 empty_dev_type[8];
+	unsigned int generated_pin = 0;
+	struct wpa_supplicant *group = NULL;
+	int res;
+
+	if (group_id) {
+		for (group = wpa_s->global->ifaces; group; group = group->next)
+		{
+			struct wpa_ssid *s = group->current_ssid;
+			if (s != NULL &&
+			    s->mode == WPAS_MODE_P2P_GO &&
+			    group_id_len - ETH_ALEN == s->ssid_len &&
+			    os_memcmp(group_id + ETH_ALEN, s->ssid,
+				      s->ssid_len) == 0)
+				break;
+		}
+	}
+
+	if (pri_dev_type == NULL) {
+		os_memset(empty_dev_type, 0, sizeof(empty_dev_type));
+		pri_dev_type = empty_dev_type;
+	}
+	res = os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR
+			  " pri_dev_type=%s name='%s' config_methods=0x%x "
+			  "dev_capab=0x%x group_capab=0x%x%s%s",
+			  MAC2STR(dev_addr),
+			  wps_dev_type_bin2str(pri_dev_type, devtype,
+					       sizeof(devtype)),
+			  dev_name, supp_config_methods, dev_capab, group_capab,
+			  group ? " group=" : "",
+			  group ? group->ifname : "");
+	if (os_snprintf_error(sizeof(params), res))
+		wpa_printf(MSG_DEBUG, "P2P: PD Request event truncated");
+	params[sizeof(params) - 1] = '\0';
+
+	if (config_methods & WPS_CONFIG_DISPLAY) {
+		generated_pin = wps_generate_pin();
+		wpas_prov_disc_local_display(wpa_s, peer, params,
+					     generated_pin);
+	} else if (config_methods & WPS_CONFIG_KEYPAD)
+		wpas_prov_disc_local_keypad(wpa_s, peer, params);
+	else if (config_methods & WPS_CONFIG_PUSHBUTTON)
+		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_REQ
+			       MACSTR "%s", MAC2STR(peer), params);
+
+	wpas_notify_p2p_provision_discovery(wpa_s, peer, 1 /* request */,
+					    P2P_PROV_DISC_SUCCESS,
+					    config_methods, generated_pin);
+}
+
+
+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;
+	char params[20];
+
+	if (wpa_s->pending_pd_before_join &&
+	    (os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
+	     os_memcmp(peer, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
+		wpa_s->pending_pd_before_join = 0;
+		wpa_printf(MSG_DEBUG, "P2P: Starting pending "
+			   "join-existing-group operation");
+		wpas_p2p_join_start(wpa_s, 0, NULL, 0);
+		return;
+	}
+
+	if (wpa_s->pending_pd_use == AUTO_PD_JOIN ||
+	    wpa_s->pending_pd_use == AUTO_PD_GO_NEG) {
+		int res;
+
+		res = os_snprintf(params, sizeof(params), " peer_go=%d",
+				  wpa_s->pending_pd_use == AUTO_PD_JOIN);
+		if (os_snprintf_error(sizeof(params), res))
+			params[sizeof(params) - 1] = '\0';
+	} else
+		params[0] = '\0';
+
+	if (config_methods & WPS_CONFIG_DISPLAY)
+		wpas_prov_disc_local_keypad(wpa_s, peer, params);
+	else if (config_methods & WPS_CONFIG_KEYPAD) {
+		generated_pin = wps_generate_pin();
+		wpas_prov_disc_local_display(wpa_s, peer, params,
+					     generated_pin);
+	} else if (config_methods & WPS_CONFIG_PUSHBUTTON)
+		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP
+			       MACSTR "%s", MAC2STR(peer), params);
+
+	wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
+					    P2P_PROV_DISC_SUCCESS,
+					    config_methods, generated_pin);
+}
+
+
+static void wpas_prov_disc_fail(void *ctx, const u8 *peer,
+				enum p2p_prov_disc_status status,
+				u32 adv_id, const u8 *adv_mac,
+				const char *deferred_session_resp)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	if (wpa_s->p2p_fallback_to_go_neg) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: PD for p2p_connect-auto "
+			"failed - fall back to GO Negotiation");
+		wpa_msg_global(wpa_s->parent, MSG_INFO,
+			       P2P_EVENT_FALLBACK_TO_GO_NEG
+			       "reason=PD-failed");
+		wpas_p2p_fallback_to_go_neg(wpa_s, 0);
+		return;
+	}
+
+	if (status == P2P_PROV_DISC_TIMEOUT_JOIN) {
+		wpa_s->pending_pd_before_join = 0;
+		wpa_printf(MSG_DEBUG, "P2P: Starting pending "
+			   "join-existing-group operation (no ACK for PD "
+			   "Req attempts)");
+		wpas_p2p_join_start(wpa_s, 0, NULL, 0);
+		return;
+	}
+
+	if (adv_id && adv_mac && deferred_session_resp) {
+		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+			       " p2p_dev_addr=" MACSTR " status=%d adv_id=%x"
+			       " deferred_session_resp='%s'",
+			       MAC2STR(peer), status, adv_id,
+			       deferred_session_resp);
+	} else if (adv_id && adv_mac) {
+		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+			       " p2p_dev_addr=" MACSTR " status=%d adv_id=%x",
+			       MAC2STR(peer), status, adv_id);
+	} else {
+		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+			       " p2p_dev_addr=" MACSTR " status=%d",
+			       MAC2STR(peer), status);
+	}
+
+	wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
+					    status, 0, 0);
+}
+
+
+static int freq_included(struct wpa_supplicant *wpa_s,
+			 const struct p2p_channels *channels,
+			 unsigned int freq)
+{
+	if ((channels == NULL || p2p_channels_includes_freq(channels, freq)) &&
+	    wpas_p2p_go_is_peer_freq(wpa_s, freq))
+		return 1;
+	return 0;
+}
+
+
+static void wpas_p2p_go_update_common_freqs(struct wpa_supplicant *wpa_s)
+{
+	unsigned int num = P2P_MAX_CHANNELS;
+	int *common_freqs;
+	int ret;
+
+	p2p_go_dump_common_freqs(wpa_s);
+	common_freqs = os_calloc(num, sizeof(int));
+	if (!common_freqs)
+		return;
+
+	ret = p2p_group_get_common_freqs(wpa_s->p2p_group, common_freqs, &num);
+	if (ret < 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Failed to get group common freqs");
+		os_free(common_freqs);
+		return;
+	}
+
+	os_free(wpa_s->p2p_group_common_freqs);
+	wpa_s->p2p_group_common_freqs = common_freqs;
+	wpa_s->p2p_group_common_freqs_num = num;
+	p2p_go_dump_common_freqs(wpa_s);
+}
+
+
+/*
+ * Check if the given frequency is one of the possible operating frequencies
+ * set after the completion of the GO Negotiation.
+ */
+static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq)
+{
+	unsigned int i;
+
+	p2p_go_dump_common_freqs(wpa_s);
+
+	/* assume no restrictions */
+	if (!wpa_s->p2p_group_common_freqs_num)
+		return 1;
+
+	for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
+		if (wpa_s->p2p_group_common_freqs[i] == freq)
+			return 1;
+	}
+	return 0;
+}
+
+
+/**
+ * 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,
+				  int dev_pw_id)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_ssid *s;
+	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 (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 ||
+		     os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0)) {
+			wpa_printf(MSG_DEBUG, "P2P: Accept previously "
+				   "authorized invitation");
+			goto accept_inv;
+		}
+
+#ifdef CONFIG_WPS_NFC
+		if (dev_pw_id >= 0 && wpa_s->p2p_nfc_tag_enabled &&
+		    dev_pw_id == wpa_s->p2p_oob_dev_pw_id) {
+			wpa_printf(MSG_DEBUG, "P2P: Accept invitation based on local enabled NFC Tag");
+			wpa_s->p2p_wps_method = WPS_NFC;
+			wpa_s->pending_join_wps_method = WPS_NFC;
+			os_memcpy(wpa_s->pending_join_dev_addr,
+				  go_dev_addr, ETH_ALEN);
+			os_memcpy(wpa_s->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.
+		 */
+		return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+	}
+
+	grp = wpas_get_p2p_group(wpa_s, ssid, ssid_len, go);
+	if (grp) {
+		wpa_printf(MSG_DEBUG, "P2P: Accept invitation to already "
+			   "running persistent group");
+		if (*go)
+			os_memcpy(group_bssid, grp->own_addr, ETH_ALEN);
+		goto accept_inv;
+	}
+
+	if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
+	    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;
+
+	for (s = wpa_s->conf->ssid; s; s = s->next) {
+		if (s->disabled == 2 &&
+		    os_memcmp(s->bssid, go_dev_addr, ETH_ALEN) == 0 &&
+		    s->ssid_len == ssid_len &&
+		    os_memcmp(ssid, s->ssid, ssid_len) == 0)
+			break;
+	}
+
+	if (!s) {
+		wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
+			   " requested reinvocation of an unknown group",
+			   MAC2STR(sa));
+		return P2P_SC_FAIL_UNKNOWN_GROUP;
+	}
+
+	if (s->mode == WPAS_MODE_P2P_GO && !wpas_p2p_create_iface(wpa_s)) {
+		*go = 1;
+		if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+			wpa_printf(MSG_DEBUG, "P2P: The only available "
+				   "interface is already in use - reject "
+				   "invitation");
+			return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+		}
+		os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN);
+	} else if (s->mode == WPAS_MODE_P2P_GO) {
+		*go = 1;
+		if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0)
+		{
+			wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
+				   "interface address for the group");
+			return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+		}
+		os_memcpy(group_bssid, wpa_s->pending_interface_addr,
+			  ETH_ALEN);
+	}
+
+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 (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 &&
+	    wpas_p2p_num_unused_channels(wpa_s) > 0) {
+		if (*go == 0) {
+			/* We are the client */
+			wpa_printf(MSG_DEBUG, "P2P: Peer was found to be "
+				   "running a GO but we are capable of MCC, "
+				   "figure out the best channel to use");
+			*force_freq = 0;
+		} else if (!freq_included(wpa_s, channels, *force_freq)) {
+			/* We are the GO, and *force_freq is not in the
+			 * intersection */
+			wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
+				   "in intersection but we are capable of MCC, "
+				   "figure out the best channel to use",
+				   *force_freq);
+			*force_freq = 0;
+		}
+	}
+
+	return P2P_SC_SUCCESS;
+}
+
+
+static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
+				     const u8 *ssid, size_t ssid_len,
+				     const u8 *go_dev_addr, u8 status,
+				     int op_freq)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_ssid *s;
+
+	for (s = wpa_s->conf->ssid; s; s = s->next) {
+		if (s->disabled == 2 &&
+		    s->ssid_len == ssid_len &&
+		    os_memcmp(ssid, s->ssid, ssid_len) == 0)
+			break;
+	}
+
+	if (status == P2P_SC_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
+			   " 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, 0, op_freq, 0, 0, NULL,
+				go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0,
+				1);
+		} else if (bssid) {
+			wpa_s->user_initiated_pd = 0;
+			wpas_p2p_join(wpa_s, bssid, go_dev_addr,
+				      wpa_s->p2p_wps_method, 0, op_freq,
+				      ssid, ssid_len);
+		}
+		return;
+	}
+
+	if (status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+		wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
+			   " was rejected (status %u)", MAC2STR(sa), status);
+		return;
+	}
+
+	if (!s) {
+		if (bssid) {
+			wpa_msg_global(wpa_s, MSG_INFO,
+				       P2P_EVENT_INVITATION_RECEIVED
+				       "sa=" MACSTR " go_dev_addr=" MACSTR
+				       " bssid=" MACSTR " unknown-network",
+				       MAC2STR(sa), MAC2STR(go_dev_addr),
+				       MAC2STR(bssid));
+		} else {
+			wpa_msg_global(wpa_s, MSG_INFO,
+				       P2P_EVENT_INVITATION_RECEIVED
+				       "sa=" MACSTR " go_dev_addr=" MACSTR
+				       " unknown-network",
+				       MAC2STR(sa), MAC2STR(go_dev_addr));
+		}
+		wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr,
+						    bssid, 0, op_freq);
+		return;
+	}
+
+	if (s->mode == WPAS_MODE_P2P_GO && op_freq) {
+		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
+			       "sa=" MACSTR " persistent=%d freq=%d",
+			       MAC2STR(sa), s->id, op_freq);
+	} else {
+		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
+			       "sa=" MACSTR " persistent=%d",
+			       MAC2STR(sa), s->id);
+	}
+	wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid,
+					    s->id, op_freq);
+}
+
+
+static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s,
+					struct wpa_ssid *ssid,
+					const u8 *peer, int inv)
+{
+	size_t i;
+	struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (ssid == NULL)
+		return;
+
+	for (i = 0; ssid->p2p_client_list && i < ssid->num_p2p_clients; i++) {
+		if (os_memcmp(ssid->p2p_client_list + i * 2 * ETH_ALEN, peer,
+			      ETH_ALEN) == 0)
+			break;
+	}
+	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 "
+				   "due to invitation result", ssid->id);
+			wpas_notify_network_removed(wpa_s, ssid);
+			wpa_config_remove_network(wpa_s->conf, ssid->id);
+			return;
+		}
+		return; /* Peer not found in client list */
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Remove peer " MACSTR " from persistent "
+		   "group %d client list%s",
+		   MAC2STR(peer), ssid->id,
+		   inv ? " due to invitation result" : "");
+	os_memmove(ssid->p2p_client_list + i * 2 * ETH_ALEN,
+		   ssid->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
+		   (ssid->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
+	ssid->num_p2p_clients--;
+	if (p2p_wpa_s->conf->update_config &&
+	    wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf))
+		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+}
+
+
+static void wpas_remove_persistent_client(struct wpa_supplicant *wpa_s,
+					  const u8 *peer)
+{
+	struct wpa_ssid *ssid;
+
+	wpa_s = wpa_s->global->p2p_invite_group;
+	if (wpa_s == NULL)
+		return; /* No known invitation group */
+	ssid = wpa_s->current_ssid;
+	if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
+	    !ssid->p2p_persistent_group)
+		return; /* Not operating as a GO in persistent group */
+	ssid = wpas_p2p_get_persistent(wpa_s->parent, peer,
+				       ssid->ssid, ssid->ssid_len);
+	wpas_remove_persistent_peer(wpa_s, ssid, peer, 1);
+}
+
+
+static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
+				   const struct p2p_channels *channels,
+				   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
+			       "status=%d " MACSTR,
+			       status, MAC2STR(bssid));
+	} else {
+		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
+			       "status=%d ", status);
+	}
+	wpas_notify_p2p_invitation_result(wpa_s, status, bssid);
+
+	wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR,
+		   status, MAC2STR(peer));
+	if (wpa_s->pending_invite_ssid_id == -1) {
+		if (status == P2P_SC_FAIL_UNKNOWN_GROUP)
+			wpas_remove_persistent_client(wpa_s, peer);
+		return; /* Invitation to active group */
+	}
+
+	if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+		wpa_printf(MSG_DEBUG, "P2P: Waiting for peer to start another "
+			   "invitation exchange to indicate readiness for "
+			   "re-invocation");
+	}
+
+	if (status != P2P_SC_SUCCESS) {
+		if (status == P2P_SC_FAIL_UNKNOWN_GROUP) {
+			ssid = wpa_config_get_network(
+				wpa_s->conf, wpa_s->pending_invite_ssid_id);
+			wpas_remove_persistent_peer(wpa_s, ssid, peer, 1);
+		}
+		wpas_p2p_remove_pending_group_interface(wpa_s);
+		return;
+	}
+
+	ssid = wpa_config_get_network(wpa_s->conf,
+				      wpa_s->pending_invite_ssid_id);
+	if (ssid == NULL) {
+		wpa_printf(MSG_ERROR, "P2P: Could not find persistent group "
+			   "data matching with invitation");
+		return;
+	}
+
+	/*
+	 * The peer could have missed our ctrl::ack frame for Invitation
+	 * Response and continue retransmitting the frame. To reduce the
+	 * likelihood of the peer not getting successful TX status for the
+	 * Invitation Response frame, wait a short time here before starting
+	 * the persistent group so that we will remain on the current channel to
+	 * acknowledge any possible retransmission from the peer.
+	 */
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: 50 ms wait on current channel before "
+		"starting persistent group");
+	os_sleep(0, 50000);
+
+	if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO &&
+	    freq_included(wpa_s, channels, neg_freq))
+		freq = neg_freq;
+	else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO &&
+		 freq_included(wpa_s, 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,
+				      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, 1);
+}
+
+
+static int wpas_p2p_disallowed_freq(struct wpa_global *global,
+				    unsigned int freq)
+{
+	if (freq_range_list_includes(&global->p2p_go_avoid_freq, freq))
+		return 1;
+	return freq_range_list_includes(&global->p2p_disallow_freq, freq);
+}
+
+
+static void wpas_p2p_add_chan(struct p2p_reg_class *reg, u8 chan)
+{
+	reg->channel[reg->channels] = chan;
+	reg->channels++;
+}
+
+
+static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s,
+				     struct p2p_channels *chan,
+				     struct p2p_channels *cli_chan)
+{
+	int i, cla = 0;
+
+	wpa_s->global->p2p_24ghz_social_channels = 1;
+
+	os_memset(cli_chan, 0, sizeof(*cli_chan));
+
+	wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz "
+		   "band");
+
+	/* Operating class 81 - 2.4 GHz band channels 1..13 */
+	chan->reg_class[cla].reg_class = 81;
+	chan->reg_class[cla].channels = 0;
+	for (i = 0; i < 11; i++) {
+		if (!wpas_p2p_disallowed_freq(wpa_s->global, 2412 + i * 5))
+			wpas_p2p_add_chan(&chan->reg_class[cla], i + 1);
+	}
+	if (chan->reg_class[cla].channels)
+		cla++;
+
+	wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for lower 5 GHz "
+		   "band");
+
+	/* Operating class 115 - 5 GHz, channels 36-48 */
+	chan->reg_class[cla].reg_class = 115;
+	chan->reg_class[cla].channels = 0;
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 36 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 36);
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 40 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 40);
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 44 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 44);
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 48 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 48);
+	if (chan->reg_class[cla].channels)
+		cla++;
+
+	wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for higher 5 GHz "
+		   "band");
+
+	/* Operating class 124 - 5 GHz, channels 149,153,157,161 */
+	chan->reg_class[cla].reg_class = 124;
+	chan->reg_class[cla].channels = 0;
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 149 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 149);
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 153 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 153);
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 156 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 157);
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 161 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 161);
+	if (chan->reg_class[cla].channels)
+		cla++;
+
+	chan->reg_classes = cla;
+	return 0;
+}
+
+
+static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
+					  u16 num_modes,
+					  enum hostapd_hw_mode mode)
+{
+	u16 i;
+
+	for (i = 0; i < num_modes; i++) {
+		if (modes[i].mode == mode)
+			return &modes[i];
+	}
+
+	return NULL;
+}
+
+
+enum chan_allowed {
+	NOT_ALLOWED, NO_IR, ALLOWED
+};
+
+static int has_channel(struct wpa_global *global,
+		       struct hostapd_hw_modes *mode, u8 chan, int *flags)
+{
+	int i;
+	unsigned int freq;
+
+	freq = (mode->mode == HOSTAPD_MODE_IEEE80211A ? 5000 : 2407) +
+		chan * 5;
+	if (wpas_p2p_disallowed_freq(global, freq))
+		return NOT_ALLOWED;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		if (mode->channels[i].chan == chan) {
+			if (flags)
+				*flags = mode->channels[i].flag;
+			if (mode->channels[i].flag &
+			    (HOSTAPD_CHAN_DISABLED |
+			     HOSTAPD_CHAN_RADAR))
+				return NOT_ALLOWED;
+			if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR)
+				return NO_IR;
+			return ALLOWED;
+		}
+	}
+
+	return NOT_ALLOWED;
+}
+
+
+struct p2p_oper_class_map {
+	enum hostapd_hw_mode mode;
+	u8 op_class;
+	u8 min_chan;
+	u8 max_chan;
+	u8 inc;
+	enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160 } bw;
+};
+
+static const struct p2p_oper_class_map op_class[] = {
+	{ HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 },
+#if 0 /* Do not enable HT40 on 2 GHz for now */
+	{ HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS },
+	{ HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS },
+#endif
+	{ HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 },
+	{ HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 },
+	{ HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20 },
+	{ HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS },
+	{ 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 },
+	{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160 },
+	{ -1, 0, 0, 0, 0, BW20 }
+};
+
+
+static int wpas_p2p_get_center_80mhz(struct wpa_supplicant *wpa_s,
+				     struct hostapd_hw_modes *mode,
+				     u8 channel)
+{
+	u8 center_channels[] = { 42, 58, 106, 122, 138, 155 };
+	unsigned int i;
+
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return 0;
+
+	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 == NO_IR)
+			ret = NO_IR;
+
+		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 == NO_IR || res2 == NO_IR)
+		return NO_IR;
+	return res;
+}
+
+
+static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
+				   struct p2p_channels *chan,
+				   struct p2p_channels *cli_chan)
+{
+	struct hostapd_hw_modes *mode;
+	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, cli_chan);
+	}
+
+	cla = cli_cla = 0;
+
+	for (op = 0; op_class[op].op_class; op++) {
+		const struct p2p_oper_class_map *o = &op_class[op];
+		u8 ch;
+		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;
+		if (mode->mode == HOSTAPD_MODE_IEEE80211G)
+			wpa_s->global->p2p_24ghz_social_channels = 1;
+		for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
+			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 == NO_IR &&
+				   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++;
+			}
+		}
+		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;
+}
+
+
+int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
+			   struct hostapd_hw_modes *mode, u8 channel)
+{
+	int op;
+	enum chan_allowed ret;
+
+	for (op = 0; op_class[op].op_class; op++) {
+		const struct p2p_oper_class_map *o = &op_class[op];
+		u8 ch;
+
+		for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
+			if (o->mode != HOSTAPD_MODE_IEEE80211A ||
+			    (o->bw != BW40PLUS && o->bw != BW40MINUS) ||
+			    ch != channel)
+				continue;
+			ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
+			if (ret == ALLOWED)
+				return (o->bw == BW40MINUS) ? -1 : 1;
+		}
+	}
+	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)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (os_memcmp(wpa_s->own_addr, interface_addr, ETH_ALEN) == 0)
+			break;
+	}
+	if (wpa_s == NULL)
+		return -1;
+
+	return wpa_drv_get_noa(wpa_s, buf, buf_len);
+}
+
+
+struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s,
+					      const u8 *ssid, size_t ssid_len)
+{
+	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 && (ssid->mode != WPAS_MODE_INFRA || !ssid->p2p_group))
+			continue;
+		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;
+}
+
+
+static void wpas_p2p_debug_print(void *ctx, int level, const char *msg)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	wpa_msg_global(wpa_s, level, "P2P: %s", msg);
+}
+
+
+int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s,
+				  const char *conf_p2p_dev)
+{
+	struct wpa_interface iface;
+	struct wpa_supplicant *p2pdev_wpa_s;
+	char ifname[100];
+	char force_name[100];
+	int ret;
+
+	ret = os_snprintf(ifname, sizeof(ifname), P2P_MGMT_DEVICE_PREFIX "%s",
+			  wpa_s->ifname);
+	if (os_snprintf_error(sizeof(ifname), ret))
+		return -1;
+	force_name[0] = '\0';
+	wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE;
+	ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, ifname, NULL, NULL,
+			     force_name, wpa_s->pending_interface_addr, NULL);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Failed to create P2P Device interface");
+		return ret;
+	}
+	os_strlcpy(wpa_s->pending_interface_name, ifname,
+		   sizeof(wpa_s->pending_interface_name));
+
+	os_memset(&iface, 0, sizeof(iface));
+	iface.p2p_mgmt = 1;
+	iface.ifname = wpa_s->pending_interface_name;
+	iface.driver = wpa_s->driver->name;
+	iface.driver_param = wpa_s->conf->driver_param;
+
+	/*
+	 * If a P2P Device configuration file was given, use it as the interface
+	 * configuration file (instead of using parent's configuration file.
+	 */
+	if (conf_p2p_dev) {
+		iface.confname = conf_p2p_dev;
+		iface.ctrl_interface = NULL;
+	} else {
+		iface.confname = wpa_s->confname;
+		iface.ctrl_interface = wpa_s->conf->ctrl_interface;
+	}
+
+	p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
+	if (!p2pdev_wpa_s) {
+		wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface");
+		return -1;
+	}
+
+	wpa_s->pending_interface_name[0] = '\0';
+	return 0;
+}
+
+
+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_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid,
+				     size_t ssid_len, u8 *go_dev_addr,
+				     u8 *ret_ssid, size_t *ret_ssid_len,
+				     u8 *intended_iface_addr)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_ssid *s;
+
+	s = wpas_p2p_get_persistent(wpa_s, addr, ssid, ssid_len);
+	if (s) {
+		os_memcpy(ret_ssid, s->ssid, s->ssid_len);
+		*ret_ssid_len = s->ssid_len;
+		os_memcpy(go_dev_addr, s->bssid, ETH_ALEN);
+
+		if (s->mode != WPAS_MODE_P2P_GO) {
+			os_memset(intended_iface_addr, 0, ETH_ALEN);
+		} else if (wpas_p2p_create_iface(wpa_s)) {
+			if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO))
+				return 0;
+
+			os_memcpy(intended_iface_addr,
+				  wpa_s->pending_interface_addr, ETH_ALEN);
+		} else {
+			os_memcpy(intended_iface_addr, wpa_s->own_addr,
+				  ETH_ALEN);
+		}
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int wpas_get_go_info(void *ctx, u8 *intended_addr,
+			    u8 *ssid, size_t *ssid_len, int *group_iface)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_ssid *s;
+	u8 bssid[ETH_ALEN];
+
+	/*
+	 * group_iface will be set to 1 only if a dedicated interface for P2P
+	 * role is required. First, we try to reuse an active GO. However,
+	 * if it is not present, we will try to reactivate an existing
+	 * persistent group and set group_iface to 1, so the caller will know
+	 * that the pending interface should be used.
+	 */
+	*group_iface = 0;
+	s = wpas_p2p_group_go_ssid(wpa_s, bssid);
+	if (!s) {
+		s = wpas_p2p_get_persistent_go(wpa_s);
+		*group_iface = wpas_p2p_create_iface(wpa_s);
+		if (s)
+			os_memcpy(bssid, s->bssid, ETH_ALEN);
+		else
+			return 0;
+	}
+
+	os_memcpy(intended_addr, bssid, ETH_ALEN);
+	os_memcpy(ssid, s->ssid, s->ssid_len);
+	*ssid_len = s->ssid_len;
+
+	return 1;
+}
+
+
+static int wpas_remove_stale_groups(void *ctx, const u8 *peer, const u8 *go,
+				    const u8 *ssid, size_t ssid_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_ssid *s;
+	int save_config = 0;
+	size_t i;
+
+	/* Start with our first choice of Persistent Groups */
+	while ((s = wpas_p2p_get_persistent(wpa_s, peer, NULL, 0))) {
+		if (go && ssid && ssid_len &&
+		    s->ssid_len == ssid_len &&
+		    os_memcmp(go, s->bssid, ETH_ALEN) == 0 &&
+		    os_memcmp(ssid, s->ssid, ssid_len) == 0)
+			break;
+
+		/* Remove stale persistent group */
+		if (s->mode != WPAS_MODE_P2P_GO || s->num_p2p_clients <= 1) {
+			wpa_config_remove_network(wpa_s->conf, s->id);
+			save_config = 1;
+			continue;
+		}
+
+		for (i = 0; i < s->num_p2p_clients; i++) {
+			if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN,
+				      peer, ETH_ALEN) != 0)
+				continue;
+
+			os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN,
+				   s->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
+				   (s->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
+			break;
+		}
+		s->num_p2p_clients--;
+		save_config = 1;
+	}
+
+	if (save_config)
+		p2p_config_write(wpa_s);
+
+	/* Return TRUE if valid SSID remains */
+	return s != NULL;
+}
+
+
+static void wpas_p2ps_get_feat_cap_str(char *buf, size_t buf_len,
+				       const u8 *feat_cap, size_t feat_cap_len)
+{
+	static const char pref[] = " feature_cap=";
+	int ret;
+
+	buf[0] = '\0';
+
+	/*
+	 * We expect a feature capability to contain at least one byte to be
+	 * reported. The string buffer provided by the caller function is
+	 * expected to be big enough to contain all bytes of the attribute for
+	 * known specifications. This function truncates the reported bytes if
+	 * the feature capability data exceeds the string buffer size.
+	 */
+	if (!feat_cap || !feat_cap_len || buf_len < sizeof(pref) + 2)
+		return;
+
+	os_memcpy(buf, pref, sizeof(pref));
+	ret = wpa_snprintf_hex(&buf[sizeof(pref) - 1],
+			       buf_len - sizeof(pref) + 1,
+			       feat_cap, feat_cap_len);
+
+	if (ret != (2 * (int) feat_cap_len))
+		wpa_printf(MSG_WARNING, "P2PS feature_cap bytes truncated");
+}
+
+
+static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev,
+				    const u8 *adv_mac, const u8 *ses_mac,
+				    const u8 *grp_mac, u32 adv_id, u32 ses_id,
+				    u8 conncap, int passwd_id,
+				    const u8 *persist_ssid,
+				    size_t persist_ssid_size, int response_done,
+				    int prov_start, const char *session_info,
+				    const u8 *feat_cap, size_t feat_cap_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	u8 mac[ETH_ALEN];
+	struct wpa_ssid *persistent_go, *stale, *s;
+	int save_config = 0;
+	struct wpa_supplicant *go_wpa_s;
+	char feat_cap_str[256];
+
+	if (!dev)
+		return;
+
+	os_memset(mac, 0, ETH_ALEN);
+	if (!adv_mac)
+		adv_mac = mac;
+	if (!ses_mac)
+		ses_mac = mac;
+	if (!grp_mac)
+		grp_mac = mac;
+
+	wpas_p2ps_get_feat_cap_str(feat_cap_str, sizeof(feat_cap_str),
+				   feat_cap, feat_cap_len);
+
+	if (prov_start) {
+		if (session_info == NULL) {
+			wpa_msg_global(wpa_s, MSG_INFO,
+				       P2P_EVENT_P2PS_PROVISION_START MACSTR
+				       " adv_id=%x conncap=%x"
+				       " adv_mac=" MACSTR
+				       " session=%x mac=" MACSTR
+				       " dev_passwd_id=%d%s",
+				       MAC2STR(dev), adv_id, conncap,
+				       MAC2STR(adv_mac),
+				       ses_id, MAC2STR(ses_mac),
+				       passwd_id, feat_cap_str);
+		} else {
+			wpa_msg_global(wpa_s, MSG_INFO,
+				       P2P_EVENT_P2PS_PROVISION_START MACSTR
+				       " adv_id=%x conncap=%x"
+				       " adv_mac=" MACSTR
+				       " session=%x mac=" MACSTR
+				       " dev_passwd_id=%d info='%s'%s",
+				       MAC2STR(dev), adv_id, conncap,
+				       MAC2STR(adv_mac),
+				       ses_id, MAC2STR(ses_mac),
+				       passwd_id, session_info, feat_cap_str);
+		}
+		return;
+	}
+
+	go_wpa_s = wpas_p2p_get_go_group(wpa_s);
+	persistent_go = wpas_p2p_get_persistent_go(wpa_s);
+
+	if (status && status != P2P_SC_SUCCESS_DEFERRED) {
+		if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
+			wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
+
+		if (persistent_go && !persistent_go->num_p2p_clients) {
+			/* remove empty persistent GO */
+			wpa_config_remove_network(wpa_s->conf,
+						  persistent_go->id);
+		}
+
+		wpa_msg_global(wpa_s, MSG_INFO,
+			       P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+			       " status=%d"
+			       " adv_id=%x adv_mac=" MACSTR
+			       " session=%x mac=" MACSTR "%s",
+			       MAC2STR(dev), status,
+			       adv_id, MAC2STR(adv_mac),
+			       ses_id, MAC2STR(ses_mac), feat_cap_str);
+		return;
+	}
+
+	/* Clean up stale persistent groups with this device */
+	s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
+				    persist_ssid_size);
+
+	if (persist_ssid && s && s->mode != WPAS_MODE_P2P_GO &&
+	    is_zero_ether_addr(grp_mac)) {
+		wpa_dbg(wpa_s, MSG_ERROR,
+			"P2P: Peer device is a GO in a persistent group, but it did not provide the intended MAC address");
+		return;
+	}
+
+	for (;;) {
+		stale = wpas_p2p_get_persistent(wpa_s, dev, NULL, 0);
+		if (!stale)
+			break;
+
+		if (s && s->ssid_len == stale->ssid_len &&
+		    os_memcmp(stale->bssid, s->bssid, ETH_ALEN) == 0 &&
+		    os_memcmp(stale->ssid, s->ssid, s->ssid_len) == 0)
+			break;
+
+		/* Remove stale persistent group */
+		if (stale->mode != WPAS_MODE_P2P_GO ||
+		    stale->num_p2p_clients <= 1) {
+			wpa_config_remove_network(wpa_s->conf, stale->id);
+		} else {
+			size_t i;
+
+			for (i = 0; i < stale->num_p2p_clients; i++) {
+				if (os_memcmp(stale->p2p_client_list +
+					      i * ETH_ALEN,
+					      dev, ETH_ALEN) == 0) {
+					os_memmove(stale->p2p_client_list +
+						   i * ETH_ALEN,
+						   stale->p2p_client_list +
+						   (i + 1) * ETH_ALEN,
+						   (stale->num_p2p_clients -
+						    i - 1) * ETH_ALEN);
+					break;
+				}
+			}
+			stale->num_p2p_clients--;
+		}
+		save_config = 1;
+	}
+
+	if (save_config)
+		p2p_config_write(wpa_s);
+
+	if (s) {
+		if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
+			wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
+
+		if (persistent_go && s != persistent_go &&
+		    !persistent_go->num_p2p_clients) {
+			/* remove empty persistent GO */
+			wpa_config_remove_network(wpa_s->conf,
+						  persistent_go->id);
+			/* Save config */
+		}
+
+		wpa_msg_global(wpa_s, MSG_INFO,
+			       P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+			       " status=%d"
+			       " adv_id=%x adv_mac=" MACSTR
+			       " session=%x mac=" MACSTR
+			       " persist=%d%s",
+			       MAC2STR(dev), status,
+			       adv_id, MAC2STR(adv_mac),
+			       ses_id, MAC2STR(ses_mac), s->id, feat_cap_str);
+		return;
+	}
+
+	if (conncap == P2PS_SETUP_GROUP_OWNER) {
+		/*
+		 * We need to copy the interface name. Simply saving a
+		 * pointer isn't enough, since if we use pending_interface_name
+		 * it will be overwritten when the group is added.
+		 */
+		char go_ifname[100];
+
+		go_ifname[0] = '\0';
+		if (!go_wpa_s) {
+			wpa_s->global->pending_p2ps_group = 1;
+
+			if (!wpas_p2p_create_iface(wpa_s))
+				os_memcpy(go_ifname, wpa_s->ifname,
+					  sizeof(go_ifname));
+			else if (wpa_s->pending_interface_name[0])
+				os_memcpy(go_ifname,
+					  wpa_s->pending_interface_name,
+					  sizeof(go_ifname));
+
+			if (!go_ifname[0]) {
+				wpas_p2ps_prov_complete(
+					wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP,
+					dev, adv_mac, ses_mac,
+					grp_mac, adv_id, ses_id, 0, 0,
+					NULL, 0, 0, 0, NULL, NULL, 0);
+				return;
+			}
+
+			/* If PD Resp complete, start up the GO */
+			if (response_done && persistent_go) {
+				wpas_p2p_group_add_persistent(
+					wpa_s, persistent_go,
+					0, 0, 0, 0, 0, NULL,
+					persistent_go->mode ==
+					WPAS_MODE_P2P_GO ?
+					P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
+					0, 0);
+			} else if (response_done) {
+				wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+			}
+
+			if (passwd_id == DEV_PW_P2PS_DEFAULT) {
+				os_memcpy(wpa_s->p2ps_join_addr, grp_mac,
+					  ETH_ALEN);
+				wpa_s->p2ps_method_config_any = 1;
+			}
+		} else if (passwd_id == DEV_PW_P2PS_DEFAULT) {
+			os_memcpy(go_ifname, go_wpa_s->ifname,
+				  sizeof(go_ifname));
+
+			if (is_zero_ether_addr(grp_mac)) {
+				wpa_dbg(go_wpa_s, MSG_DEBUG,
+					"P2P: Setting PIN-1 for ANY");
+				wpa_supplicant_ap_wps_pin(go_wpa_s, NULL,
+							  "12345670", NULL, 0,
+							  0);
+			} else {
+				wpa_dbg(go_wpa_s, MSG_DEBUG,
+					"P2P: Setting PIN-1 for " MACSTR,
+					MAC2STR(grp_mac));
+				wpa_supplicant_ap_wps_pin(go_wpa_s, grp_mac,
+							  "12345670", NULL, 0,
+							  0);
+			}
+
+			os_memcpy(wpa_s->p2ps_join_addr, grp_mac, ETH_ALEN);
+			wpa_s->p2ps_method_config_any = 1;
+		}
+
+		wpa_msg_global(wpa_s, MSG_INFO,
+			       P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+			       " status=%d conncap=%x"
+			       " adv_id=%x adv_mac=" MACSTR
+			       " session=%x mac=" MACSTR
+			       " dev_passwd_id=%d go=%s%s",
+			       MAC2STR(dev), status, conncap,
+			       adv_id, MAC2STR(adv_mac),
+			       ses_id, MAC2STR(ses_mac),
+			       passwd_id, go_ifname, feat_cap_str);
+		return;
+	}
+
+	if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
+		wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
+
+	if (persistent_go && !persistent_go->num_p2p_clients) {
+		/* remove empty persistent GO */
+		wpa_config_remove_network(wpa_s->conf, persistent_go->id);
+	}
+
+	if (conncap == P2PS_SETUP_CLIENT) {
+		wpa_msg_global(wpa_s, MSG_INFO,
+			       P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+			       " status=%d conncap=%x"
+			       " adv_id=%x adv_mac=" MACSTR
+			       " session=%x mac=" MACSTR
+			       " dev_passwd_id=%d join=" MACSTR "%s",
+			       MAC2STR(dev), status, conncap,
+			       adv_id, MAC2STR(adv_mac),
+			       ses_id, MAC2STR(ses_mac),
+			       passwd_id, MAC2STR(grp_mac), feat_cap_str);
+	} else {
+		wpa_msg_global(wpa_s, MSG_INFO,
+			       P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+			       " status=%d conncap=%x"
+			       " adv_id=%x adv_mac=" MACSTR
+			       " session=%x mac=" MACSTR
+			       " dev_passwd_id=%d%s",
+			       MAC2STR(dev), status, conncap,
+			       adv_id, MAC2STR(adv_mac),
+			       ses_id, MAC2STR(ses_mac),
+			       passwd_id, feat_cap_str);
+	}
+}
+
+
+static int _wpas_p2p_in_progress(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	return wpas_p2p_in_progress(wpa_s);
+}
+
+
+static int wpas_prov_disc_resp_cb(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_ssid *persistent_go;
+
+	if (!wpa_s->global->pending_p2ps_group)
+		return 0;
+
+	wpa_s->global->pending_p2ps_group = 0;
+
+	if (wpas_p2p_get_go_group(wpa_s))
+		return 0;
+	persistent_go = wpas_p2p_get_persistent_go(wpa_s);
+
+	if (persistent_go) {
+		wpas_p2p_group_add_persistent(
+			wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL,
+			persistent_go->mode == WPAS_MODE_P2P_GO ?
+			P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0);
+	} else {
+		wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+	}
+
+	return 1;
+}
+
+
+static int wpas_p2p_get_pref_freq_list(void *ctx, int go,
+				       unsigned int *len,
+				       unsigned int *freq_list)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpa_drv_get_pref_freq_list(wpa_s, go ? WPA_IF_P2P_GO :
+					  WPA_IF_P2P_CLIENT, len, freq_list);
+}
+
+
+/**
+ * wpas_p2p_init - Initialize P2P module for %wpa_supplicant
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * Returns: 0 on success, -1 on failure
+ */
+int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
+{
+	struct p2p_config p2p;
+	int i;
+
+	if (wpa_s->conf->p2p_disabled)
+		return 0;
+
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
+		return 0;
+
+	if (global->p2p)
+		return 0;
+
+	os_memset(&p2p, 0, sizeof(p2p));
+	p2p.cb_ctx = wpa_s;
+	p2p.debug_print = wpas_p2p_debug_print;
+	p2p.p2p_scan = wpas_p2p_scan;
+	p2p.send_action = wpas_send_action;
+	p2p.send_action_done = wpas_send_action_done;
+	p2p.go_neg_completed = wpas_go_neg_completed;
+	p2p.go_neg_req_rx = wpas_go_neg_req_rx;
+	p2p.dev_found = wpas_dev_found;
+	p2p.dev_lost = wpas_dev_lost;
+	p2p.find_stopped = wpas_find_stopped;
+	p2p.start_listen = wpas_start_listen;
+	p2p.stop_listen = wpas_stop_listen;
+	p2p.send_probe_resp = wpas_send_probe_resp;
+	p2p.sd_request = wpas_sd_request;
+	p2p.sd_response = wpas_sd_response;
+	p2p.prov_disc_req = wpas_prov_disc_req;
+	p2p.prov_disc_resp = wpas_prov_disc_resp;
+	p2p.prov_disc_fail = wpas_prov_disc_fail;
+	p2p.invitation_process = wpas_invitation_process;
+	p2p.invitation_received = wpas_invitation_received;
+	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;
+	p2p.get_persistent_group = wpas_get_persistent_group;
+	p2p.get_go_info = wpas_get_go_info;
+	p2p.remove_stale_groups = wpas_remove_stale_groups;
+	p2p.p2ps_prov_complete = wpas_p2ps_prov_complete;
+	p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb;
+	p2p.p2ps_group_capability = p2ps_group_capability;
+	p2p.get_pref_freq_list = wpas_p2p_get_pref_freq_list;
+
+	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);
+	p2p.dev_name = wpa_s->conf->device_name;
+	p2p.manufacturer = wpa_s->conf->manufacturer;
+	p2p.model_name = wpa_s->conf->model_name;
+	p2p.model_number = wpa_s->conf->model_number;
+	p2p.serial_number = wpa_s->conf->serial_number;
+	if (wpa_s->wps) {
+		os_memcpy(p2p.uuid, wpa_s->wps->uuid, 16);
+		p2p.config_methods = wpa_s->wps->config_methods;
+	}
+
+	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;
+	}
+
+	if (wpa_s->conf->p2p_listen_reg_class &&
+	    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 {
+		/*
+		 * Pick one of the social channels randomly as the listen
+		 * channel.
+		 */
+		if (p2p_config_get_random_social(&p2p, &p2p.reg_class,
+						 &p2p.channel) != 0) {
+			wpa_printf(MSG_INFO,
+				   "P2P: No social channels supported by the driver - do not enable P2P");
+			return 0;
+		}
+		p2p.channel_forced = 0;
+	}
+	wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d:%d",
+		   p2p.reg_class, p2p.channel);
+
+	if (wpa_s->conf->p2p_oper_reg_class &&
+	    wpa_s->conf->p2p_oper_channel) {
+		p2p.op_reg_class = wpa_s->conf->p2p_oper_reg_class;
+		p2p.op_channel = wpa_s->conf->p2p_oper_channel;
+		p2p.cfg_op_channel = 1;
+		wpa_printf(MSG_DEBUG, "P2P: Configured operating channel: "
+			   "%d:%d", p2p.op_reg_class, p2p.op_channel);
+
+	} else {
+		/*
+		 * Use random operation channel from 2.4 GHz band social
+		 * channels (1, 6, 11) or band 60 GHz social channel (2) if no
+		 * other preference is indicated.
+		 */
+		if (p2p_config_get_random_social(&p2p, &p2p.op_reg_class,
+						 &p2p.op_channel) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "P2P: Failed to select random social channel as operation channel");
+			return -1;
+		}
+		p2p.cfg_op_channel = 0;
+		wpa_printf(MSG_DEBUG, "P2P: Random operating channel: "
+			   "%d:%d", p2p.op_reg_class, p2p.op_channel);
+	}
+
+	if (wpa_s->conf->p2p_pref_chan && wpa_s->conf->num_p2p_pref_chan) {
+		p2p.pref_chan = wpa_s->conf->p2p_pref_chan;
+		p2p.num_pref_chan = wpa_s->conf->num_p2p_pref_chan;
+	}
+
+	if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
+		os_memcpy(p2p.country, wpa_s->conf->country, 2);
+		p2p.country[2] = 0x04;
+	} else
+		os_memcpy(p2p.country, "XX\x04", 3);
+
+	os_memcpy(p2p.pri_dev_type, wpa_s->conf->device_type,
+		  WPS_DEV_TYPE_LEN);
+
+	p2p.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+	os_memcpy(p2p.sec_dev_type, wpa_s->conf->sec_device_type,
+		  p2p.num_sec_dev_types * WPS_DEV_TYPE_LEN);
+
+	p2p.concurrent_operations = !!(wpa_s->drv_flags &
+				       WPA_DRIVER_FLAGS_P2P_CONCURRENT);
+
+	p2p.max_peers = 100;
+
+	if (wpa_s->conf->p2p_ssid_postfix) {
+		p2p.ssid_postfix_len =
+			os_strlen(wpa_s->conf->p2p_ssid_postfix);
+		if (p2p.ssid_postfix_len > sizeof(p2p.ssid_postfix))
+			p2p.ssid_postfix_len = sizeof(p2p.ssid_postfix);
+		os_memcpy(p2p.ssid_postfix, wpa_s->conf->p2p_ssid_postfix,
+			  p2p.ssid_postfix_len);
+	}
+
+	p2p.p2p_intra_bss = wpa_s->conf->p2p_intra_bss;
+
+	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;
+	global->p2p_init_wpa_s = wpa_s;
+
+	for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) {
+		if (wpa_s->conf->wps_vendor_ext[i] == NULL)
+			continue;
+		p2p_add_wps_vendor_extension(
+			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;
+}
+
+
+/**
+ * wpas_p2p_deinit - Deinitialize per-interface P2P data
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ *
+ * This function deinitialize per-interface P2P data.
+ */
+void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver && wpa_s->drv_priv)
+		wpa_drv_probe_req_report(wpa_s, 0);
+
+	if (wpa_s->go_params) {
+		/* Clear any stored provisioning info */
+		p2p_clear_provisioning_info(
+			wpa_s->global->p2p,
+			wpa_s->go_params->peer_device_addr);
+	}
+
+	os_free(wpa_s->go_params);
+	wpa_s->go_params = NULL;
+	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;
+
+	os_free(wpa_s->p2p_group_common_freqs);
+	wpa_s->p2p_group_common_freqs = NULL;
+	wpa_s->p2p_group_common_freqs_num = 0;
+
+	/* TODO: remove group interface from the driver if this wpa_s instance
+	 * is on top of a P2P group interface */
+}
+
+
+/**
+ * wpas_p2p_deinit_global - Deinitialize global P2P module
+ * @global: Pointer to global data from wpa_supplicant_init()
+ *
+ * This function deinitializes the global (per device) P2P module.
+ */
+static void wpas_p2p_deinit_global(struct wpa_global *global)
+{
+	struct wpa_supplicant *wpa_s, *tmp;
+
+	wpa_s = global->ifaces;
+
+	wpas_p2p_service_flush(global->p2p_init_wpa_s);
+
+	/* Remove remaining P2P group interfaces */
+	while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
+		wpa_s = wpa_s->next;
+	while (wpa_s) {
+		tmp = global->ifaces;
+		while (tmp &&
+		       (tmp == wpa_s ||
+			tmp->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)) {
+			tmp = tmp->next;
+		}
+		if (tmp == NULL)
+			break;
+		/* Disconnect from the P2P group and deinit the interface */
+		wpas_p2p_disconnect(tmp);
+	}
+
+	/*
+	 * Deinit GO data on any possibly remaining interface (if main
+	 * interface is used as GO).
+	 */
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (wpa_s->ap_iface)
+			wpas_p2p_group_deinit(wpa_s);
+	}
+
+	p2p_deinit(global->p2p);
+	global->p2p = NULL;
+	global->p2p_init_wpa_s = NULL;
+}
+
+
+static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s)
+{
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
+	    wpa_s->conf->p2p_no_group_iface)
+		return 0; /* separate interface disabled per configuration */
+	if (wpa_s->drv_flags &
+	    (WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE |
+	     WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P))
+		return 1; /* P2P group requires a new interface in every case
+			   */
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CONCURRENT))
+		return 0; /* driver does not support concurrent operations */
+	if (wpa_s->global->ifaces->next)
+		return 1; /* more that one interface already in use */
+	if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+		return 1; /* this interface is already in use */
+	return 0;
+}
+
+
+static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
+				 const u8 *peer_addr,
+				 enum p2p_wps_method wps_method,
+				 int go_intent, const u8 *own_interface_addr,
+				 unsigned int force_freq, int persistent_group,
+				 struct wpa_ssid *ssid, unsigned int pref_freq)
+{
+	if (persistent_group && wpa_s->conf->persistent_reconnect)
+		persistent_group = 2;
+
+	/*
+	 * 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.
+	 */
+	p2p_set_config_timeout(wpa_s->global->p2p,
+			       wpa_s->p2p_go_ht40 ? 255 : 100, 20);
+
+	return p2p_connect(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,
+			   wpa_s->p2p_pd_before_go_neg, pref_freq,
+			   wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
+			   0);
+}
+
+
+static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s,
+				const u8 *peer_addr,
+				enum p2p_wps_method wps_method,
+				int go_intent, const u8 *own_interface_addr,
+				unsigned int force_freq, int persistent_group,
+				struct wpa_ssid *ssid, unsigned int pref_freq)
+{
+	if (persistent_group && wpa_s->conf->persistent_reconnect)
+		persistent_group = 2;
+
+	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,
+			     wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
+			     0);
+}
+
+
+static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->p2p_join_scan_count++;
+	wpa_printf(MSG_DEBUG, "P2P: Join scan attempt %d",
+		   wpa_s->p2p_join_scan_count);
+	if (wpa_s->p2p_join_scan_count > P2P_MAX_JOIN_SCAN_ATTEMPTS) {
+		wpa_printf(MSG_DEBUG, "P2P: Failed to find GO " MACSTR
+			   " for join operationg - stop join attempt",
+			   MAC2STR(wpa_s->pending_join_iface_addr));
+		eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+		if (wpa_s->p2p_auto_pd) {
+			wpa_s->p2p_auto_pd = 0;
+			wpa_msg_global(wpa_s, MSG_INFO,
+				       P2P_EVENT_PROV_DISC_FAILURE
+				       " p2p_dev_addr=" MACSTR " status=N/A",
+				       MAC2STR(wpa_s->pending_join_dev_addr));
+			return;
+		}
+		wpa_msg_global(wpa_s->parent, MSG_INFO,
+			       P2P_EVENT_GROUP_FORMATION_FAILURE);
+		wpas_notify_p2p_group_formation_failure(wpa_s, "");
+	}
+}
+
+
+static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq)
+{
+	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(struct wpa_used_freq_data));
+	if (!freqs)
+		return 1;
+
+	num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
+					wpa_s->num_multichan_concurrent);
+
+	for (i = 0; i < num; i++) {
+		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;
+			goto exit_free;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: No valid operating frequencies");
+	res = 1;
+
+exit_free:
+	os_free(freqs);
+	return res;
+}
+
+
+static int wpas_p2p_peer_go(struct wpa_supplicant *wpa_s,
+			    const u8 *peer_dev_addr)
+{
+	struct wpa_bss *bss;
+	int updated;
+
+	bss = wpa_bss_get_p2p_dev_addr(wpa_s, peer_dev_addr);
+	if (bss == NULL)
+		return -1;
+	if (bss->last_update_idx < wpa_s->bss_update_idx) {
+		wpa_printf(MSG_DEBUG, "P2P: Peer BSS entry not updated in the "
+			   "last scan");
+		return 0;
+	}
+
+	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,
+		   updated ? "": "not ");
+
+	return updated;
+}
+
+
+static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
+				   struct wpa_scan_results *scan_res)
+{
+	struct wpa_bss *bss = NULL;
+	int freq;
+	u8 iface_addr[ETH_ALEN];
+
+	eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+
+	if (wpa_s->global->p2p_disabled)
+		return;
+
+	wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for %sjoin",
+		   scan_res ? (int) scan_res->num : -1,
+		   wpa_s->p2p_auto_join ? "auto_" : "");
+
+	if (scan_res)
+		wpas_p2p_scan_res_handler(wpa_s, scan_res);
+
+	if (wpa_s->p2p_auto_pd) {
+		int join = wpas_p2p_peer_go(wpa_s,
+					    wpa_s->pending_join_dev_addr);
+		if (join == 0 &&
+		    wpa_s->auto_pd_scan_retry < P2P_AUTO_PD_SCAN_ATTEMPTS) {
+			wpa_s->auto_pd_scan_retry++;
+			bss = wpa_bss_get_bssid_latest(
+				wpa_s, wpa_s->pending_join_dev_addr);
+			if (bss) {
+				freq = bss->freq;
+				wpa_printf(MSG_DEBUG, "P2P: Scan retry %d for "
+					   "the peer " MACSTR " at %d MHz",
+					   wpa_s->auto_pd_scan_retry,
+					   MAC2STR(wpa_s->
+						   pending_join_dev_addr),
+					   freq);
+				wpas_p2p_join_scan_req(wpa_s, freq, NULL, 0);
+				return;
+			}
+		}
+
+		if (join < 0)
+			join = 0;
+
+		wpa_s->p2p_auto_pd = 0;
+		wpa_s->pending_pd_use = join ? AUTO_PD_JOIN : AUTO_PD_GO_NEG;
+		wpa_printf(MSG_DEBUG, "P2P: Auto PD with " MACSTR " join=%d",
+			   MAC2STR(wpa_s->pending_join_dev_addr), join);
+		if (p2p_prov_disc_req(wpa_s->global->p2p,
+				      wpa_s->pending_join_dev_addr, NULL,
+				      wpa_s->pending_pd_config_methods, join,
+				      0, wpa_s->user_initiated_pd) < 0) {
+			wpa_s->p2p_auto_pd = 0;
+			wpa_msg_global(wpa_s, MSG_INFO,
+				       P2P_EVENT_PROV_DISC_FAILURE
+				       " p2p_dev_addr=" MACSTR " status=N/A",
+				       MAC2STR(wpa_s->pending_join_dev_addr));
+		}
+		return;
+	}
+
+	if (wpa_s->p2p_auto_join) {
+		int join = wpas_p2p_peer_go(wpa_s,
+					    wpa_s->pending_join_dev_addr);
+		if (join < 0) {
+			wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be "
+				   "running a GO -> use GO Negotiation");
+			wpa_msg_global(wpa_s->parent, MSG_INFO,
+				       P2P_EVENT_FALLBACK_TO_GO_NEG
+				       "reason=peer-not-running-GO");
+			wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr,
+					 wpa_s->p2p_pin, wpa_s->p2p_wps_method,
+					 wpa_s->p2p_persistent_group, 0, 0, 0,
+					 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_vht);
+			return;
+		}
+
+		wpa_printf(MSG_DEBUG, "P2P: Peer was found running GO%s -> "
+			   "try to join the group", join ? "" :
+			   " in older scan");
+		if (!join) {
+			wpa_msg_global(wpa_s->parent, MSG_INFO,
+				       P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED);
+			wpa_s->p2p_fallback_to_go_neg = 1;
+		}
+	}
+
+	freq = p2p_get_oper_freq(wpa_s->global->p2p,
+				 wpa_s->pending_join_iface_addr);
+	if (freq < 0 &&
+	    p2p_get_interface_addr(wpa_s->global->p2p,
+				   wpa_s->pending_join_dev_addr,
+				   iface_addr) == 0 &&
+	    os_memcmp(iface_addr, wpa_s->pending_join_dev_addr, ETH_ALEN) != 0
+	    && !wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr)) {
+		wpa_printf(MSG_DEBUG, "P2P: Overwrite pending interface "
+			   "address for join from " MACSTR " to " MACSTR
+			   " based on newly discovered P2P peer entry",
+			   MAC2STR(wpa_s->pending_join_iface_addr),
+			   MAC2STR(iface_addr));
+		os_memcpy(wpa_s->pending_join_iface_addr, iface_addr,
+			  ETH_ALEN);
+
+		freq = p2p_get_oper_freq(wpa_s->global->p2p,
+					 wpa_s->pending_join_iface_addr);
+	}
+	if (freq >= 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
+			   "from P2P peer table: %d MHz", freq);
+	}
+	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) {
+		u8 dev_addr[ETH_ALEN];
+
+		freq = bss->freq;
+		wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
+			   "from BSS table: %d MHz (SSID %s)", freq,
+			   wpa_ssid_txt(bss->ssid, bss->ssid_len));
+		if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len,
+				       dev_addr) == 0 &&
+		    os_memcmp(wpa_s->pending_join_dev_addr,
+			      wpa_s->pending_join_iface_addr, ETH_ALEN) == 0 &&
+		    os_memcmp(dev_addr, wpa_s->pending_join_dev_addr,
+			      ETH_ALEN) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Update target GO device address based on BSS entry: " MACSTR " (was " MACSTR ")",
+				   MAC2STR(dev_addr),
+				   MAC2STR(wpa_s->pending_join_dev_addr));
+			os_memcpy(wpa_s->pending_join_dev_addr, dev_addr,
+				  ETH_ALEN);
+		}
+	}
+	if (freq > 0) {
+		u16 method;
+
+		if (wpas_check_freq_conflict(wpa_s, freq) > 0) {
+			wpa_msg_global(wpa_s->parent, MSG_INFO,
+				       P2P_EVENT_GROUP_FORMATION_FAILURE
+				       "reason=FREQ_CONFLICT");
+			wpas_notify_p2p_group_formation_failure(
+				wpa_s, "FREQ_CONFLICT");
+			return;
+		}
+
+		wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request "
+			   "prior to joining an existing group (GO " MACSTR
+			   " freq=%u MHz)",
+			   MAC2STR(wpa_s->pending_join_dev_addr), freq);
+		wpa_s->pending_pd_before_join = 1;
+
+		switch (wpa_s->pending_join_wps_method) {
+		case WPS_PIN_DISPLAY:
+			method = WPS_CONFIG_KEYPAD;
+			break;
+		case WPS_PIN_KEYPAD:
+			method = WPS_CONFIG_DISPLAY;
+			break;
+		case WPS_PBC:
+			method = WPS_CONFIG_PUSHBUTTON;
+			break;
+		case WPS_P2PS:
+			method = WPS_CONFIG_P2PS;
+			break;
+		default:
+			method = 0;
+			break;
+		}
+
+		if ((p2p_get_provisioning_info(wpa_s->global->p2p,
+					       wpa_s->pending_join_dev_addr) ==
+		     method)) {
+			/*
+			 * We have already performed provision discovery for
+			 * joining the group. Proceed directly to join
+			 * operation without duplicated provision discovery. */
+			wpa_printf(MSG_DEBUG, "P2P: Provision discovery "
+				   "with " MACSTR " already done - proceed to "
+				   "join",
+				   MAC2STR(wpa_s->pending_join_dev_addr));
+			wpa_s->pending_pd_before_join = 0;
+			goto start;
+		}
+
+		if (p2p_prov_disc_req(wpa_s->global->p2p,
+				      wpa_s->pending_join_dev_addr,
+				      NULL, method, 1,
+				      freq, wpa_s->user_initiated_pd) < 0) {
+			wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision "
+				   "Discovery Request before joining an "
+				   "existing group");
+			wpa_s->pending_pd_before_join = 0;
+			goto start;
+		}
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Failed to find BSS/GO - try again later");
+	eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+	eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
+	wpas_p2p_check_join_scan_limit(wpa_s);
+	return;
+
+start:
+	/* Start join operation immediately */
+	wpas_p2p_join_start(wpa_s, 0, NULL, 0);
+}
+
+
+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 };
+
+	os_memset(&params, 0, sizeof(params));
+
+	/* P2P Wildcard SSID */
+	params.num_ssids = 1;
+	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,
+					wpa_s->wps->uuid, WPS_REQ_ENROLLEE, 0,
+					NULL);
+	if (wps_ie == NULL) {
+		wpas_p2p_scan_res_join(wpa_s, NULL);
+		return;
+	}
+
+	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);
+		wpas_p2p_scan_res_join(wpa_s, NULL);
+		return;
+	}
+	wpabuf_put_buf(ies, wps_ie);
+	wpabuf_free(wps_ie);
+
+	p2p_scan_ie(wpa_s->global->p2p, ies, NULL);
+
+	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;
+	}
+
+	/*
+	 * Run a scan to update BSS table and start Provision Discovery once
+	 * the new scan results become available.
+	 */
+	ret = wpa_drv_scan(wpa_s, &params);
+	if (!ret) {
+		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);
+
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "P2P: Failed to start scan for join - "
+			   "try again later");
+		eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+		eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
+		wpas_p2p_check_join_scan_limit(wpa_s);
+	}
+}
+
+
+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, 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 op_freq,
+			 const u8 *ssid, size_t ssid_len)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
+		   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;
+	os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN);
+	os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN);
+	wpa_s->pending_join_wps_method = wps_method;
+
+	/* Make sure we are not running find during connection establishment */
+	wpas_p2p_stop_find(wpa_s);
+
+	wpa_s->p2p_join_scan_count = 0;
+	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, int freq,
+			       const u8 *ssid, size_t ssid_len)
+{
+	struct wpa_supplicant *group;
+	struct p2p_go_neg_results res;
+	struct wpa_bss *bss;
+
+	group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
+	if (group == NULL)
+		return -1;
+	if (group != wpa_s) {
+		os_memcpy(group->p2p_pin, wpa_s->p2p_pin,
+			  sizeof(group->p2p_pin));
+		group->p2p_wps_method = wpa_s->p2p_wps_method;
+	} else {
+		/*
+		 * Need to mark the current interface for p2p_group_formation
+		 * when a separate group interface is not used. This is needed
+		 * to allow p2p_cancel stop a pending p2p_connect-join.
+		 * wpas_p2p_init_group_interface() addresses this for the case
+		 * where a separate group interface is used.
+		 */
+		wpa_s->global->p2p_group_formation = wpa_s;
+	}
+
+	group->p2p_in_provisioning = 1;
+	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;
+	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) {
+		wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel prior to "
+			   "starting client");
+		wpa_drv_cancel_remain_on_channel(wpa_s);
+		wpa_s->off_channel_freq = 0;
+		wpa_s->roc_waiting_drv_freq = 0;
+	}
+	wpas_start_wps_enrollee(group, &res);
+
+	/*
+	 * Allow a longer timeout for join-a-running-group than normal 15
+	 * second group formation timeout since the GO may not have authorized
+	 * our connection yet.
+	 */
+	eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
+	eloop_register_timeout(60, 0, wpas_p2p_group_formation_timeout,
+			       wpa_s, NULL);
+
+	return 0;
+}
+
+
+static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
+				int *force_freq, int *pref_freq, int go,
+				unsigned int *pref_freq_list,
+				unsigned int *num_pref_freq)
+{
+	struct wpa_used_freq_data *freqs;
+	int res, best_freq, num_unused;
+	unsigned int freq_in_use = 0, num, i, max_pref_freq;
+
+	max_pref_freq = *num_pref_freq;
+	*num_pref_freq = 0;
+
+	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);
+
+	/*
+	 * 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 num_unused=%d",
+		   freq, wpa_s->num_multichan_concurrent, num, num_unused);
+
+	if (freq > 0) {
+		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) {
+			if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+			    ieee80211_is_dfs(freq)) {
+				/*
+				 * If freq is a DFS channel and DFS is offloaded
+				 * to the driver, allow P2P GO to use it.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "P2P: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to the driver",
+					   freq);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: The forced channel (%u MHz) is not supported for P2P uses",
+					   freq);
+				res = -3;
+				goto exit_free;
+			}
+		}
+
+		for (i = 0; i < num; i++) {
+			if (freqs[i].freq == freq)
+				freq_in_use = 1;
+		}
+
+		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;
+			goto exit_free;
+		}
+		wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
+			   "requested channel (%u MHz)", freq);
+		*force_freq = freq;
+		goto exit_ok;
+	}
+
+	best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+
+	if (!wpa_s->conf->num_p2p_pref_chan && *pref_freq == 0) {
+		enum wpa_driver_if_type iface_type;
+
+		if (go)
+			iface_type = WPA_IF_P2P_GO;
+		else
+			iface_type = WPA_IF_P2P_CLIENT;
+
+		wpa_printf(MSG_DEBUG, "P2P: best_freq=%d, go=%d",
+			   best_freq, go);
+
+		res = wpa_drv_get_pref_freq_list(wpa_s, iface_type,
+						 &max_pref_freq,
+						 pref_freq_list);
+		if (!res && max_pref_freq > 0) {
+			*num_pref_freq = max_pref_freq;
+			i = 0;
+			while (wpas_p2p_disallowed_freq(wpa_s->global,
+							pref_freq_list[i]) &&
+			       i < *num_pref_freq) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: preferred_freq_list[%d]=%d is disallowed",
+					   i, pref_freq_list[i]);
+				i++;
+			}
+			if (i != *num_pref_freq) {
+				best_freq = pref_freq_list[i];
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Using preferred_freq_list[%d]=%d",
+					   i, best_freq);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: All driver preferred frequencies are disallowed for P2P use");
+				*num_pref_freq = 0;
+			}
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: No preferred frequency list available");
+		}
+	}
+
+	/* 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",
+				   best_freq);
+			*force_freq = best_freq;
+		}
+	} 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:
+	res = 0;
+exit_free:
+	os_free(freqs);
+	return res;
+}
+
+
+/**
+ * wpas_p2p_connect - Request P2P Group Formation to be started
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @peer_addr: Address of the peer P2P Device
+ * @pin: PIN to use during provisioning or %NULL to indicate PBC mode
+ * @persistent_group: Whether to create a persistent group
+ * @auto_join: Whether to select join vs. GO Negotiation automatically
+ * @join: Whether to join an existing group (as a client) instead of starting
+ *	Group Owner negotiation; @peer_addr is BSSID in that case
+ * @auth: Whether to only authorize the connection instead of doing that and
+ *	initiating Group Owner negotiation
+ * @go_intent: GO Intent or -1 to use default
+ * @freq: Frequency for the group or 0 for auto-selection
+ * @persistent_id: Persistent group credentials to use for forcing GO
+ *	parameters or -1 to generate new values (SSID/passphrase)
+ * @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
+ */
+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, int vht)
+{
+	int force_freq = 0, pref_freq = 0;
+	int ret = 0, res;
+	enum wpa_driver_if_type iftype;
+	const u8 *if_addr;
+	struct wpa_ssid *ssid = NULL;
+	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+
+	if (persistent_id >= 0) {
+		ssid = wpa_config_get_network(wpa_s->conf, persistent_id);
+		if (ssid == NULL || ssid->disabled != 2 ||
+		    ssid->mode != WPAS_MODE_P2P_GO)
+			return -1;
+	}
+
+	os_free(wpa_s->global->add_psk);
+	wpa_s->global->add_psk = NULL;
+
+	wpa_s->global->p2p_fail_on_wps_complete = 0;
+	wpa_s->global->pending_p2ps_group = 0;
+	wpa_s->p2ps_method_config_any = 0;
+
+	if (go_intent < 0)
+		go_intent = wpa_s->conf->p2p_go_intent;
+
+	if (!auth)
+		wpa_s->p2p_long_listen = 0;
+
+	wpa_s->p2p_wps_method = wps_method;
+	wpa_s->p2p_persistent_group = !!persistent_group;
+	wpa_s->p2p_persistent_id = persistent_id;
+	wpa_s->p2p_go_intent = go_intent;
+	wpa_s->p2p_connect_freq = freq;
+	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));
+	else if (wps_method == WPS_PIN_DISPLAY) {
+		ret = wps_generate_pin();
+		res = os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin),
+				  "%08d", ret);
+		if (os_snprintf_error(sizeof(wpa_s->p2p_pin), res))
+			wpa_s->p2p_pin[sizeof(wpa_s->p2p_pin) - 1] = '\0';
+		wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s",
+			   wpa_s->p2p_pin);
+	} else
+		wpa_s->p2p_pin[0] = '\0';
+
+	if (join || auto_join) {
+		u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN];
+		if (auth) {
+			wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to "
+				   "connect a running group from " MACSTR,
+				   MAC2STR(peer_addr));
+			os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN);
+			return ret;
+		}
+		os_memcpy(dev_addr, peer_addr, ETH_ALEN);
+		if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr,
+					   iface_addr) < 0) {
+			os_memcpy(iface_addr, peer_addr, ETH_ALEN);
+			p2p_get_dev_addr(wpa_s->global->p2p, peer_addr,
+					 dev_addr);
+		}
+		if (auto_join) {
+			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,
+				   wpa_s->p2p_auto_started.usec);
+		}
+		wpa_s->user_initiated_pd = 1;
+		if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
+				  auto_join, freq, NULL, 0) < 0)
+			return -1;
+		return ret;
+	}
+
+	size = P2P_MAX_PREF_CHANNELS;
+	res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
+				   go_intent == 15, pref_freq_list, &size);
+	if (res)
+		return res;
+	wpas_p2p_set_own_freq_preference(wpa_s,
+					 force_freq ? force_freq : pref_freq);
+
+	p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
+
+	wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
+
+	if (wpa_s->create_p2p_iface) {
+		/* 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;
+		os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+	}
+
+	if (auth) {
+		if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
+					 go_intent, if_addr,
+					 force_freq, persistent_group, ssid,
+					 pref_freq) < 0)
+			return -1;
+		return ret;
+	}
+
+	if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method,
+				  go_intent, if_addr, force_freq,
+				  persistent_group, ssid, pref_freq) < 0) {
+		if (wpa_s->create_p2p_iface)
+			wpas_p2p_remove_pending_group_interface(wpa_s);
+		return -1;
+	}
+	return ret;
+}
+
+
+/**
+ * wpas_p2p_remain_on_channel_cb - Indication of remain-on-channel start
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @freq: Frequency of the channel in MHz
+ * @duration: Duration of the stay on the channel in milliseconds
+ *
+ * This callback is called when the driver indicates that it has started the
+ * requested remain-on-channel duration.
+ */
+void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+				   unsigned int freq, unsigned int duration)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "P2P: remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d roc_waiting_drv_freq=%d freq=%u duration=%u)",
+		   wpa_s->off_channel_freq, wpa_s->pending_listen_freq,
+		   wpa_s->roc_waiting_drv_freq, freq, duration);
+	if (wpa_s->off_channel_freq &&
+	    wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
+		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);
+	}
+}
+
+
+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;
+
+	return p2p_listen(wpa_s->global->p2p, timeout);
+}
+
+
+/**
+ * wpas_p2p_cancel_remain_on_channel_cb - Remain-on-channel timeout
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @freq: Frequency of the channel in MHz
+ *
+ * This callback is called when the driver indicates that a remain-on-channel
+ * operation has been completed, i.e., the duration on the requested channel
+ * has timed out.
+ */
+void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+					  unsigned int freq)
+{
+	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_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
+		wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
+	} else {
+		/*
+		 * When listen duration is over, stop listen & update p2p_state
+		 * to IDLE.
+		 */
+		p2p_stop_listen(wpa_s->global->p2p);
+	}
+}
+
+
+/**
+ * wpas_p2p_group_remove - Remove a P2P group
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @ifname: Network interface name of the group interface or "*" to remove all
+ *	groups
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to remove a P2P group. This can be used to disconnect
+ * from a group in which the local end is a P2P Client or to end a P2P Group in
+ * case the local end is the Group Owner. If a virtual network interface was
+ * created for this group, that interface will be removed. Otherwise, only the
+ * configured P2P group network will be removed from the interface.
+ */
+int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
+{
+	struct wpa_global *global = wpa_s->global;
+	struct wpa_supplicant *calling_wpa_s = wpa_s;
+
+	if (os_strcmp(ifname, "*") == 0) {
+		struct wpa_supplicant *prev;
+		wpa_s = global->ifaces;
+		while (wpa_s) {
+			prev = wpa_s;
+			wpa_s = wpa_s->next;
+			if (prev->p2p_group_interface !=
+			    NOT_P2P_GROUP_INTERFACE ||
+			    (prev->current_ssid &&
+			     prev->current_ssid->p2p_group))
+				wpas_p2p_disconnect_safely(prev, calling_wpa_s);
+		}
+		return 0;
+	}
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (os_strcmp(wpa_s->ifname, ifname) == 0)
+			break;
+	}
+
+	return wpas_p2p_disconnect_safely(wpa_s, calling_wpa_s);
+}
+
+
+static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
+{
+	unsigned int r;
+
+	if (!wpa_s->conf->num_p2p_pref_chan && !freq) {
+		unsigned int i, size = P2P_MAX_PREF_CHANNELS;
+		unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
+		int res;
+
+		res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO,
+						 &size, pref_freq_list);
+		if (!res && size > 0) {
+			i = 0;
+			while (wpas_p2p_disallowed_freq(wpa_s->global,
+							pref_freq_list[i]) &&
+			       i < size) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: preferred_freq_list[%d]=%d is disallowed",
+					   i, pref_freq_list[i]);
+				i++;
+			}
+			if (i != size) {
+				freq = pref_freq_list[i];
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Using preferred_freq_list[%d]=%d",
+					   i, freq);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: All driver preferred frequencies are disallowed for P2P use");
+			}
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: No preferred frequency list available");
+		}
+	}
+
+	if (freq == 2) {
+		wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
+			   "band");
+		if (wpa_s->best_24_freq > 0 &&
+		    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);
+		} else {
+			if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+				return -1;
+			freq = 2412 + (r % 3) * 25;
+			wpa_printf(MSG_DEBUG, "P2P: Use random 2.4 GHz band "
+				   "channel: %d MHz", freq);
+		}
+	}
+
+	if (freq == 5) {
+		wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz "
+			   "band");
+		if (wpa_s->best_5_freq > 0 &&
+		    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 "
+				   "channel: %d MHz", freq);
+		} else {
+			if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+				return -1;
+			freq = 5180 + (r % 4) * 20;
+			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;
+			}
+			wpa_printf(MSG_DEBUG, "P2P: Use random 5 GHz band "
+				   "channel: %d MHz", freq);
+		}
+	}
+
+	if (freq > 0 && !p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+		if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+		    ieee80211_is_dfs(freq)) {
+			/*
+			 * If freq is a DFS channel and DFS is offloaded to the
+			 * driver, allow P2P GO to use it.
+			 */
+			wpa_printf(MSG_DEBUG, "P2P: "
+				   "%s: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded",
+				   __func__, freq);
+			return freq;
+		}
+		wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO "
+			   "(%u MHz) is not supported for P2P uses",
+			   freq);
+		return -1;
+	}
+
+	return freq;
+}
+
+
+static int wpas_p2p_supported_freq_go(struct wpa_supplicant *wpa_s,
+				      const struct p2p_channels *channels,
+				      int freq)
+{
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, freq) &&
+	    p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
+	    freq_included(wpa_s, channels, freq))
+		return 1;
+	return 0;
+}
+
+
+static void wpas_p2p_select_go_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 */
+	if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+		return;
+
+	for (i = 0; i < 3; i++) {
+		params->freq = 2412 + ((r + i) % 3) * 25;
+		if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
+			goto out;
+	}
+
+	/* try all other channels in operating class 81 */
+	for (i = 0; i < 11; i++) {
+		params->freq = 2412 + i * 5;
+
+		/* skip social channels; covered in the previous loop */
+		if (params->freq == 2412 ||
+		    params->freq == 2437 ||
+		    params->freq == 2462)
+			continue;
+
+		if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
+			goto out;
+	}
+
+	/* try all channels in operating class 115 */
+	for (i = 0; i < 4; i++) {
+		params->freq = 5180 + i * 20;
+		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+		    freq_included(wpa_s, channels, params->freq) &&
+		    p2p_supported_freq(wpa_s->global->p2p, params->freq))
+			goto out;
+	}
+
+	/* try all channels in operating class 124 */
+	for (i = 0; i < 4; i++) {
+		params->freq = 5745 + i * 20;
+		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+		    freq_included(wpa_s, channels, params->freq) &&
+		    p2p_supported_freq(wpa_s->global->p2p, params->freq))
+			goto out;
+	}
+
+	/* try social channel class 180 channel 2 */
+	params->freq = 58320 + 1 * 2160;
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+	    freq_included(wpa_s, channels, params->freq) &&
+	    p2p_supported_freq(wpa_s->global->p2p, params->freq))
+		goto out;
+
+	/* try all channels in reg. class 180 */
+	for (i = 0; i < 4; i++) {
+		params->freq = 58320 + i * 2160;
+		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+		    freq_included(wpa_s, channels, params->freq) &&
+		    p2p_supported_freq(wpa_s->global->p2p, params->freq))
+			goto out;
+	}
+
+	params->freq = 0;
+	wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed");
+	return;
+out:
+	wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)",
+		   params->freq);
+}
+
+
+static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
+				   struct p2p_go_neg_results *params,
+				   int freq, int ht40, int vht,
+				   const struct p2p_channels *channels)
+{
+	struct wpa_used_freq_data *freqs;
+	unsigned int cand;
+	unsigned int num, i;
+
+	os_memset(params, 0, sizeof(*params));
+	params->role_go = 1;
+	params->ht40 = ht40;
+	params->vht = vht;
+
+	if (wpa_s->p2p_group_common_freqs_num)
+		wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO",
+			   __func__);
+
+	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);
+
+	/* try using the forced freq */
+	if (freq) {
+		if (!wpas_p2p_supported_freq_go(wpa_s, channels, freq)) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Forced GO freq %d MHz not accepted",
+				   freq);
+			goto fail;
+		}
+
+		for (i = 0; i < num; i++) {
+			if (freqs[i].freq == freq) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: forced freq (%d MHz) is also shared",
+					   freq);
+				params->freq = freq;
+				goto success;
+			}
+		}
+
+		if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Cannot force GO on freq (%d MHz) as all the channels are in use",
+				   freq);
+			goto fail;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "P2P: force GO freq (%d MHz) on a free channel",
+			   freq);
+		params->freq = freq;
+		goto success;
+	}
+
+	/* consider using one of the shared frequencies */
+	if (num) {
+		cand = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+		if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Use shared freq (%d MHz) for GO",
+				   freq);
+			params->freq = cand;
+			goto success;
+		}
+
+		/* try using one of the shared freqs */
+		for (i = 0; i < num; i++) {
+			if (wpas_p2p_supported_freq_go(wpa_s, channels,
+						       freqs[i].freq)) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Use shared freq (%d MHz) for GO",
+					   freq);
+				params->freq = freqs[i].freq;
+				goto success;
+			}
+		}
+	}
+
+	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");
+		goto fail;
+	}
+
+	/* try using the setting from the configuration file */
+	if (wpa_s->conf->p2p_oper_reg_class == 81 &&
+	    wpa_s->conf->p2p_oper_channel >= 1 &&
+	    wpa_s->conf->p2p_oper_channel <= 11 &&
+	    wpas_p2p_supported_freq_go(
+		    wpa_s, channels,
+		    2407 + 5 * wpa_s->conf->p2p_oper_channel)) {
+		params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
+		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
+			   "frequency %d MHz", params->freq);
+		goto success;
+	}
+
+	if ((wpa_s->conf->p2p_oper_reg_class == 115 ||
+	     wpa_s->conf->p2p_oper_reg_class == 116 ||
+	     wpa_s->conf->p2p_oper_reg_class == 117 ||
+	     wpa_s->conf->p2p_oper_reg_class == 124 ||
+	     wpa_s->conf->p2p_oper_reg_class == 125 ||
+	     wpa_s->conf->p2p_oper_reg_class == 126 ||
+	     wpa_s->conf->p2p_oper_reg_class == 127) &&
+	    wpas_p2p_supported_freq_go(wpa_s, channels,
+				       5000 +
+				       5 * wpa_s->conf->p2p_oper_channel)) {
+		params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
+		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
+			   "frequency %d MHz", params->freq);
+		goto success;
+	}
+
+	/* Try using best channels */
+	if (wpa_s->conf->p2p_oper_channel == 0 &&
+	    wpa_s->best_overall_freq > 0 &&
+	    wpas_p2p_supported_freq_go(wpa_s, 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);
+		goto success;
+	}
+
+	if (wpa_s->conf->p2p_oper_channel == 0 &&
+	    wpa_s->best_24_freq > 0 &&
+	    wpas_p2p_supported_freq_go(wpa_s, 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);
+		goto success;
+	}
+
+	if (wpa_s->conf->p2p_oper_channel == 0 &&
+	    wpa_s->best_5_freq > 0 &&
+	    wpas_p2p_supported_freq_go(wpa_s, 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 "
+			   "channel %d MHz", params->freq);
+		goto success;
+	}
+
+	/* try using preferred channels */
+	cand = p2p_get_pref_freq(wpa_s->global->p2p, channels);
+	if (cand && wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+		params->freq = cand;
+		wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
+			   "channels", params->freq);
+		goto success;
+	}
+
+	/* Try using one of the group common freqs */
+	if (wpa_s->p2p_group_common_freqs) {
+		for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
+			cand = wpa_s->p2p_group_common_freqs[i];
+			if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+				params->freq = cand;
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Use freq %d MHz common with the peer",
+					   params->freq);
+				goto success;
+			}
+		}
+	}
+
+	/* no preference, select some channel */
+	wpas_p2p_select_go_freq_no_pref(wpa_s, params, channels);
+
+	if (params->freq == 0) {
+		wpa_printf(MSG_DEBUG, "P2P: did not find a freq for GO use");
+		goto fail;
+	}
+
+success:
+	os_free(freqs);
+	return 0;
+fail:
+	os_free(freqs);
+	return -1;
+}
+
+
+static struct wpa_supplicant *
+wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
+			 int go)
+{
+	struct wpa_supplicant *group_wpa_s;
+
+	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;
+	}
+
+	if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
+					 WPA_IF_P2P_CLIENT) < 0) {
+		wpa_msg_global(wpa_s, MSG_ERROR,
+			       "P2P: Failed to add group interface");
+		return NULL;
+	}
+	group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go);
+	if (group_wpa_s == NULL) {
+		wpa_msg_global(wpa_s, MSG_ERROR,
+			       "P2P: Failed to initialize group interface");
+		wpas_p2p_remove_pending_group_interface(wpa_s);
+		return NULL;
+	}
+
+	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;
+}
+
+
+/**
+ * wpas_p2p_group_add - Add a new P2P group with local end as Group Owner
+ * @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 vht)
+{
+	struct p2p_go_neg_results params;
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+
+	os_free(wpa_s->global->add_psk);
+	wpa_s->global->add_psk = NULL;
+
+	/* Make sure we are not running find during connection establishment */
+	wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND");
+	wpas_p2p_stop_find_oper(wpa_s);
+
+	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, vht, NULL))
+		return -1;
+	if (params.freq &&
+	    !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
+		if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+		    ieee80211_is_dfs(params.freq)) {
+			/*
+			 * If freq is a DFS channel and DFS is offloaded to the
+			 * driver, allow P2P GO to use it.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "P2P: %s: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to driver",
+				__func__, params.freq);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: The selected channel for GO (%u MHz) is not supported for P2P uses",
+				   params.freq);
+			return -1;
+		}
+	}
+	p2p_go_params(wpa_s->global->p2p, &params);
+	params.persistent_group = persistent_group;
+
+	wpa_s = wpas_p2p_get_group_iface(wpa_s, 0, 1);
+	if (wpa_s == NULL)
+		return -1;
+	wpas_start_wps_go(wpa_s, &params, 0);
+
+	return 0;
+}
+
+
+static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
+				 struct wpa_ssid *params, int addr_allocated,
+				 int freq, int force_scan)
+{
+	struct wpa_ssid *ssid;
+
+	wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
+	if (wpa_s == NULL)
+		return -1;
+	if (force_scan)
+		os_get_reltime(&wpa_s->scan_min_time);
+	wpa_s->p2p_last_4way_hs_fail = NULL;
+
+	wpa_supplicant_ap_deinit(wpa_s);
+
+	ssid = wpa_config_add_network(wpa_s->conf);
+	if (ssid == NULL)
+		return -1;
+	os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+	wpa_config_set_network_defaults(ssid);
+	ssid->temporary = 1;
+	ssid->proto = WPA_PROTO_RSN;
+	ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+	ssid->group_cipher = WPA_CIPHER_CCMP;
+	ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+	ssid->ssid = os_malloc(params->ssid_len);
+	if (ssid->ssid == NULL) {
+		wpa_config_remove_network(wpa_s->conf, ssid->id);
+		return -1;
+	}
+	os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
+	ssid->ssid_len = params->ssid_len;
+	ssid->p2p_group = 1;
+	ssid->export_keys = 1;
+	if (params->psk_set) {
+		os_memcpy(ssid->psk, params->psk, 32);
+		ssid->psk_set = 1;
+	}
+	if (params->passphrase)
+		ssid->passphrase = os_strdup(params->passphrase);
+
+	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;
+}
+
+
+int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
+				  struct wpa_ssid *ssid, int addr_allocated,
+				  int force_freq, int neg_freq, int ht40,
+				  int vht, const struct p2p_channels *channels,
+				  int connection_timeout, int force_scan)
+{
+	struct p2p_go_neg_results params;
+	int go = 0, freq;
+
+	if (ssid->disabled != 2 || ssid->ssid == NULL)
+		return -1;
+
+	if (wpas_get_p2p_group(wpa_s, ssid->ssid, ssid->ssid_len, &go) &&
+	    go == (ssid->mode == WPAS_MODE_P2P_GO)) {
+		wpa_printf(MSG_DEBUG, "P2P: Requested persistent group is "
+			   "already running");
+		if (go == 0 &&
+		    eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+					 wpa_s->parent, NULL)) {
+			/*
+			 * This can happen if Invitation Response frame was lost
+			 * and the peer (GO of a persistent group) tries to
+			 * invite us again. Reschedule the timeout to avoid
+			 * terminating the wait for the connection too early
+			 * since we now know that the peer is still trying to
+			 * invite us instead of having already started the GO.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Reschedule group formation timeout since peer is still trying to invite us");
+			eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
+					       wpas_p2p_group_formation_timeout,
+					       wpa_s->parent, NULL);
+		}
+		return 0;
+	}
+
+	os_free(wpa_s->global->add_psk);
+	wpa_s->global->add_psk = NULL;
+
+	/* Make sure we are not running find during connection establishment */
+	wpas_p2p_stop_find_oper(wpa_s);
+
+	wpa_s->p2p_fallback_to_go_neg = 0;
+
+	if (ssid->mode == WPAS_MODE_P2P_GO) {
+		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(wpa_s, channels, freq)))
+				freq = 0;
+		}
+	} else if (ssid->mode == WPAS_MODE_INFRA) {
+		freq = neg_freq;
+		if (freq <= 0 || !freq_included(wpa_s, channels, freq)) {
+			struct os_reltime now;
+			struct wpa_bss *bss =
+				wpa_bss_get_p2p_dev_addr(wpa_s, ssid->bssid);
+
+			os_get_reltime(&now);
+			if (bss &&
+			    !os_reltime_expired(&now, &bss->last_update, 5) &&
+			    freq_included(wpa_s, channels, bss->freq))
+				freq = bss->freq;
+			else
+				freq = 0;
+		}
+
+		return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq,
+					     force_scan);
+	} else {
+		return -1;
+	}
+
+	if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, vht, channels))
+		return -1;
+
+	params.role_go = 1;
+	params.psk_set = ssid->psk_set;
+	if (params.psk_set)
+		os_memcpy(params.psk, ssid->psk, sizeof(params.psk));
+	if (ssid->passphrase) {
+		if (os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) {
+			wpa_printf(MSG_ERROR, "P2P: Invalid passphrase in "
+				   "persistent group");
+			return -1;
+		}
+		os_strlcpy(params.passphrase, ssid->passphrase,
+			   sizeof(params.passphrase));
+	}
+	os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len);
+	params.ssid_len = ssid->ssid_len;
+	params.persistent_group = 1;
+
+	wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1);
+	if (wpa_s == NULL)
+		return -1;
+
+	p2p_channels_to_freqs(channels, params.freq_list, P2P_MAX_CHANNELS);
+
+	wpa_s->p2p_first_connection_timeout = connection_timeout;
+	wpas_start_wps_go(wpa_s, &params, 0);
+
+	return 0;
+}
+
+
+static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies,
+			       struct wpabuf *proberesp_ies)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	if (wpa_s->ap_iface) {
+		struct hostapd_data *hapd = wpa_s->ap_iface->bss[0];
+		if (!(hapd->conf->p2p & P2P_GROUP_OWNER)) {
+			wpabuf_free(beacon_ies);
+			wpabuf_free(proberesp_ies);
+			return;
+		}
+		if (beacon_ies) {
+			wpabuf_free(hapd->p2p_beacon_ie);
+			hapd->p2p_beacon_ie = beacon_ies;
+		}
+		wpabuf_free(hapd->p2p_probe_resp_ie);
+		hapd->p2p_probe_resp_ie = proberesp_ies;
+	} else {
+		wpabuf_free(beacon_ies);
+		wpabuf_free(proberesp_ies);
+	}
+	wpa_supplicant_ap_update_beacon(wpa_s);
+}
+
+
+static void wpas_p2p_idle_update(void *ctx, int idle)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	if (!wpa_s->ap_iface)
+		return;
+	wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not ");
+	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
+		eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+}
+
+
+struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
+				       struct wpa_ssid *ssid)
+{
+	struct p2p_group *group;
+	struct p2p_group_config *cfg;
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return NULL;
+
+	cfg = os_zalloc(sizeof(*cfg));
+	if (cfg == NULL)
+		return NULL;
+
+	if (ssid->p2p_persistent_group && wpa_s->conf->persistent_reconnect)
+		cfg->persistent_group = 2;
+	else if (ssid->p2p_persistent_group)
+		cfg->persistent_group = 1;
+	os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN);
+	if (wpa_s->max_stations &&
+	    wpa_s->max_stations < wpa_s->conf->max_num_sta)
+		cfg->max_clients = wpa_s->max_stations;
+	else
+		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;
+
+	group = p2p_group_init(wpa_s->global->p2p, cfg);
+	if (group == NULL)
+		os_free(cfg);
+	if (ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)
+		p2p_group_notif_formation_done(group);
+	wpa_s->p2p_group = group;
+	return group;
+}
+
+
+void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+			  int registrar)
+{
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (!wpa_s->p2p_in_provisioning) {
+		wpa_printf(MSG_DEBUG, "P2P: Ignore WPS success event - P2P "
+			   "provisioning not in progress");
+		return;
+	}
+
+	if (ssid && ssid->mode == WPAS_MODE_INFRA) {
+		u8 go_dev_addr[ETH_ALEN];
+		os_memcpy(go_dev_addr, wpa_s->bssid, ETH_ALEN);
+		wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
+					  ssid->ssid_len);
+		/* Clear any stored provisioning info */
+		p2p_clear_provisioning_info(wpa_s->global->p2p, go_dev_addr);
+	}
+
+	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
+		 * complete to allow the group to be removed automatically if
+		 * 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);
+	wpas_group_formation_completed(wpa_s, 1, 0);
+}
+
+
+void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+			 struct wps_event_fail *fail)
+{
+	if (!wpa_s->p2p_in_provisioning) {
+		wpa_printf(MSG_DEBUG, "P2P: Ignore WPS fail event - P2P "
+			   "provisioning not in progress");
+		return;
+	}
+
+	if (wpa_s->go_params) {
+		p2p_clear_provisioning_info(
+			wpa_s->global->p2p,
+			wpa_s->go_params->peer_device_addr);
+	}
+
+	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;
+}
+
+
+int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+		       const char *config_method,
+		       enum wpas_p2p_prov_disc_use use,
+		       struct p2ps_provision *p2ps_prov)
+{
+	u16 config_methods;
+
+	wpa_s->global->pending_p2ps_group = 0;
+	wpa_s->p2p_fallback_to_go_neg = 0;
+	wpa_s->pending_pd_use = NORMAL_PD;
+	if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) {
+		p2ps_prov->conncap = p2ps_group_capability(
+			wpa_s, P2PS_SETUP_NONE, p2ps_prov->role);
+		wpa_printf(MSG_DEBUG,
+			   "P2P: %s conncap: %d - ASP parsed: %x %x %d %s",
+			   __func__, p2ps_prov->conncap,
+			   p2ps_prov->adv_id, p2ps_prov->conncap,
+			   p2ps_prov->status, p2ps_prov->info);
+
+		config_methods = 0;
+	} else if (os_strncmp(config_method, "display", 7) == 0)
+		config_methods = WPS_CONFIG_DISPLAY;
+	else if (os_strncmp(config_method, "keypad", 6) == 0)
+		config_methods = WPS_CONFIG_KEYPAD;
+	else if (os_strncmp(config_method, "pbc", 3) == 0 ||
+		 os_strncmp(config_method, "pushbutton", 10) == 0)
+		config_methods = WPS_CONFIG_PUSHBUTTON;
+	else {
+		wpa_printf(MSG_DEBUG, "P2P: Unknown config method");
+		os_free(p2ps_prov);
+		return -1;
+	}
+
+	if (use == WPAS_P2P_PD_AUTO) {
+		os_memcpy(wpa_s->pending_join_dev_addr, peer_addr, ETH_ALEN);
+		wpa_s->pending_pd_config_methods = config_methods;
+		wpa_s->p2p_auto_pd = 1;
+		wpa_s->p2p_auto_join = 0;
+		wpa_s->pending_pd_before_join = 0;
+		wpa_s->auto_pd_scan_retry = 0;
+		wpas_p2p_stop_find(wpa_s);
+		wpa_s->p2p_join_scan_count = 0;
+		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);
+		wpas_p2p_join_scan(wpa_s, NULL);
+		return 0;
+	}
+
+	if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) {
+		os_free(p2ps_prov);
+		return -1;
+	}
+
+	return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, p2ps_prov,
+				 config_methods, use == WPAS_P2P_PD_FOR_JOIN,
+				 0, 1);
+}
+
+
+int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+			      char *end)
+{
+	return p2p_scan_result_text(ies, ies_len, buf, end);
+}
+
+
+static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
+{
+	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);
+}
+
+
+int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
+		  enum p2p_discovery_type type,
+		  unsigned int num_req_dev_types, const u8 *req_dev_types,
+		  const u8 *dev_id, unsigned int search_delay,
+		  u8 seek_cnt, const char **seek_string, int freq)
+{
+	wpas_p2p_clear_pending_action_tx(wpa_s);
+	wpa_s->p2p_long_listen = 0;
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
+	    wpa_s->p2p_in_provisioning)
+		return -1;
+
+	wpa_supplicant_cancel_sched_scan(wpa_s);
+
+	return p2p_find(wpa_s->global->p2p, timeout, type,
+			num_req_dev_types, req_dev_types, dev_id,
+			search_delay, seek_cnt, seek_string, freq);
+}
+
+
+static void wpas_p2p_scan_res_ignore_search(struct wpa_supplicant *wpa_s,
+					    struct wpa_scan_results *scan_res)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Ignore scan results");
+
+	if (wpa_s->p2p_scan_work) {
+		struct wpa_radio_work *work = wpa_s->p2p_scan_work;
+		wpa_s->p2p_scan_work = NULL;
+		radio_work_done(work);
+	}
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return;
+
+	/*
+	 * Indicate that results have been processed so that the P2P module can
+	 * continue pending tasks.
+	 */
+	p2p_scan_res_handled(wpa_s->global->p2p);
+}
+
+
+static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s)
+{
+	wpas_p2p_clear_pending_action_tx(wpa_s);
+	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);
+
+	if (wpa_s->global->p2p)
+		p2p_stop_find(wpa_s->global->p2p);
+
+	if (wpa_s->scan_res_handler == wpas_p2p_scan_res_handler) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Do not consider the scan results after stop_find");
+		wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore_search;
+	}
+}
+
+
+void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
+{
+	wpas_p2p_stop_find_oper(wpa_s);
+	if (!wpa_s->global->pending_group_iface_for_p2ps)
+		wpas_p2p_remove_pending_group_interface(wpa_s);
+}
+
+
+static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	wpa_s->p2p_long_listen = 0;
+}
+
+
+int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout)
+{
+	int res;
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+
+	wpa_supplicant_cancel_sched_scan(wpa_s);
+	wpas_p2p_clear_pending_action_tx(wpa_s);
+
+	if (timeout == 0) {
+		/*
+		 * This is a request for unlimited Listen state. However, at
+		 * least for now, this is mapped to a Listen state for one
+		 * hour.
+		 */
+		timeout = 3600;
+	}
+	eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+	wpa_s->p2p_long_listen = 0;
+
+	/*
+	 * Stop previous find/listen operation to avoid trying to request a new
+	 * remain-on-channel operation while the driver is still running the
+	 * previous one.
+	 */
+	if (wpa_s->global->p2p)
+		p2p_stop_find(wpa_s->global->p2p);
+
+	res = wpas_p2p_listen_start(wpa_s, timeout * 1000);
+	if (res == 0 && timeout * 1000 > wpa_s->max_remain_on_chan) {
+		wpa_s->p2p_long_listen = timeout * 1000;
+		eloop_register_timeout(timeout, 0,
+				       wpas_p2p_long_listen_timeout,
+				       wpa_s, NULL);
+	}
+
+	return res;
+}
+
+
+int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			  u8 *buf, size_t len, int p2p_group)
+{
+	struct wpabuf *p2p_ie;
+	int ret;
+
+	if (wpa_s->global->p2p_disabled)
+		return -1;
+	/*
+	 * Advertize mandatory cross connection capability even on
+	 * p2p_disabled=1 interface when associating with a P2P Manager WLAN AP.
+	 */
+	if (wpa_s->conf->p2p_disabled && p2p_group)
+		return -1;
+	if (wpa_s->global->p2p == NULL)
+		return -1;
+	if (bss == NULL)
+		return -1;
+
+	p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+	ret = p2p_assoc_req_ie(wpa_s->global->p2p, bss->bssid, buf, len,
+			       p2p_group, p2p_ie);
+	wpabuf_free(p2p_ie);
+
+	return ret;
+}
+
+
+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,
+			  unsigned int rx_freq, int ssi_signal)
+{
+	if (wpa_s->global->p2p_disabled)
+		return 0;
+	if (wpa_s->global->p2p == NULL)
+		return 0;
+
+	switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
+				 ie, ie_len, rx_freq)) {
+	case P2P_PREQ_NOT_P2P:
+		wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len,
+				 ssi_signal);
+		/* fall through */
+	case P2P_PREQ_MALFORMED:
+	case P2P_PREQ_NOT_LISTEN:
+	case P2P_PREQ_NOT_PROCESSED:
+	default: /* make gcc happy */
+		return 0;
+	case P2P_PREQ_PROCESSED:
+		return 1;
+	}
+}
+
+
+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)
+{
+	if (wpa_s->global->p2p_disabled)
+		return;
+	if (wpa_s->global->p2p == NULL)
+		return;
+
+	p2p_rx_action(wpa_s->global->p2p, da, sa, bssid, category, data, len,
+		      freq);
+}
+
+
+void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies)
+{
+	if (wpa_s->global->p2p_disabled)
+		return;
+	if (wpa_s->global->p2p == NULL)
+		return;
+
+	p2p_scan_ie(wpa_s->global->p2p, ies, NULL);
+}
+
+
+static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s)
+{
+	p2p_group_deinit(wpa_s->p2p_group);
+	wpa_s->p2p_group = NULL;
+
+	wpa_s->ap_configured_cb = NULL;
+	wpa_s->ap_configured_cb_ctx = NULL;
+	wpa_s->ap_configured_cb_data = NULL;
+	wpa_s->connect_without_scan = NULL;
+}
+
+
+int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+	wpa_s->p2p_long_listen = 0;
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+
+	return p2p_reject(wpa_s->global->p2p, addr);
+}
+
+
+/* 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 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;
+	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
+
+	wpa_s->global->p2p_invite_group = NULL;
+	if (peer_addr)
+		os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN);
+	else
+		os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+
+	wpa_s->p2p_persistent_go_freq = freq;
+	wpa_s->p2p_go_ht40 = !!ht40;
+	if (ssid->mode == WPAS_MODE_P2P_GO) {
+		role = P2P_INVITE_ROLE_GO;
+		if (peer_addr == NULL) {
+			wpa_printf(MSG_DEBUG, "P2P: Missing peer "
+				   "address in invitation command");
+			return -1;
+		}
+		if (wpas_p2p_create_iface(wpa_s)) {
+			if (wpas_p2p_add_group_interface(wpa_s,
+							 WPA_IF_P2P_GO) < 0) {
+				wpa_printf(MSG_ERROR, "P2P: Failed to "
+					   "allocate a new interface for the "
+					   "group");
+				return -1;
+			}
+			bssid = wpa_s->pending_interface_addr;
+		} else
+			bssid = wpa_s->own_addr;
+	} else {
+		role = P2P_INVITE_ROLE_CLIENT;
+		peer_addr = ssid->bssid;
+	}
+	wpa_s->pending_invite_ssid_id = ssid->id;
+
+	size = P2P_MAX_PREF_CHANNELS;
+	res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
+				   role == P2P_INVITE_ROLE_GO,
+				   pref_freq_list, &size);
+	if (res)
+		return res;
+	p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
+
+	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;
+	}
+
+	/*
+	 * Stop any find/listen operations before invitation and possibly
+	 * connection establishment.
+	 */
+	wpas_p2p_stop_find_oper(wpa_s);
+
+	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);
+}
+
+
+/* Invite to join an active group */
+int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
+			  const u8 *peer_addr, const u8 *go_dev_addr)
+{
+	struct wpa_global *global = wpa_s->global;
+	enum p2p_invite_role role;
+	u8 *bssid = NULL;
+	struct wpa_ssid *ssid;
+	int persistent;
+	int freq = 0, force_freq = 0, pref_freq = 0;
+	int res;
+	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
+
+	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)
+			break;
+	}
+	if (wpa_s == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: Interface '%s' not found", ifname);
+		return -1;
+	}
+
+	ssid = wpa_s->current_ssid;
+	if (ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: No current SSID to use for "
+			   "invitation");
+		return -1;
+	}
+
+	wpa_s->global->p2p_invite_group = wpa_s;
+	persistent = ssid->p2p_persistent_group &&
+		wpas_p2p_get_persistent(wpa_s->parent, peer_addr,
+					ssid->ssid, ssid->ssid_len);
+
+	if (ssid->mode == WPAS_MODE_P2P_GO) {
+		role = P2P_INVITE_ROLE_ACTIVE_GO;
+		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) {
+			wpa_printf(MSG_DEBUG, "P2P: Not associated - cannot "
+				   "invite to current group");
+			return -1;
+		}
+		bssid = wpa_s->bssid;
+		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->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+
+	size = P2P_MAX_PREF_CHANNELS;
+	res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
+				   role == P2P_INVITE_ROLE_ACTIVE_GO,
+				   pref_freq_list, &size);
+	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, -1);
+}
+
+
+void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	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,
+				     wpa_s->parent, NULL);
+	}
+
+	if (!wpa_s->show_group_started || !ssid)
+		return;
+
+	wpa_s->show_group_started = 0;
+
+	os_memset(go_dev_addr, 0, ETH_ALEN);
+	if (ssid->bssid_set)
+		os_memcpy(go_dev_addr, ssid->bssid, ETH_ALEN);
+	persistent = wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
+					       ssid->ssid_len);
+	os_memcpy(wpa_s->go_dev_addr, go_dev_addr, ETH_ALEN);
+
+	if (wpa_s->global->p2p_group_formation == wpa_s)
+		wpa_s->global->p2p_group_formation = NULL;
+
+	freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
+		(int) wpa_s->assoc_freq;
+
+	ip_addr[0] = '\0';
+	if (wpa_sm_get_p2p_ip_addr(wpa_s->wpa, ip) == 0) {
+		int res;
+
+		res = 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]);
+		if (os_snprintf_error(sizeof(ip_addr), res))
+			ip_addr[0] = '\0';
+	}
+
+	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);
+}
+
+
+int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
+			  u32 interval1, u32 duration2, u32 interval2)
+{
+	int ret;
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+
+	if (wpa_s->wpa_state < WPA_ASSOCIATED ||
+	    wpa_s->current_ssid == NULL ||
+	    wpa_s->current_ssid->mode != WPAS_MODE_INFRA)
+		return -1;
+
+	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->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+
+	return p2p_ext_listen(wpa_s->global->p2p, period, interval);
+}
+
+
+static int wpas_p2p_is_client(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->current_ssid == NULL) {
+		/*
+		 * current_ssid can be cleared when P2P client interface gets
+		 * disconnected, so assume this interface was used as P2P
+		 * client.
+		 */
+		return 1;
+	}
+	return wpa_s->current_ssid->p2p_group &&
+		wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
+}
+
+
+static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (wpa_s->conf->p2p_group_idle == 0 && !wpas_p2p_is_client(wpa_s)) {
+		wpa_printf(MSG_DEBUG, "P2P: Ignore group idle timeout - "
+			   "disabled");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate "
+		   "group");
+	wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_IDLE_TIMEOUT);
+}
+
+
+static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s)
+{
+	int 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 (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
+		return;
+
+	timeout = wpa_s->conf->p2p_group_idle;
+	if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
+	    (timeout == 0 || timeout > P2P_MAX_CLIENT_IDLE))
+	    timeout = P2P_MAX_CLIENT_IDLE;
+
+	if (timeout == 0)
+		return;
+
+	if (timeout < 0) {
+		if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA)
+			timeout = 0; /* special client mode no-timeout */
+		else
+			return;
+	}
+
+	if (wpa_s->p2p_in_provisioning) {
+		/*
+		 * Use the normal group formation timeout during the
+		 * provisioning phase to avoid terminating this process too
+		 * early due to group idle timeout.
+		 */
+		wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
+			   "during provisioning");
+		return;
+	}
+
+	if (wpa_s->show_group_started) {
+		/*
+		 * Use the normal group formation timeout between the end of
+		 * the provisioning phase and completion of 4-way handshake to
+		 * avoid terminating this process too early due to group idle
+		 * timeout.
+		 */
+		wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
+			   "while waiting for initial 4-way handshake to "
+			   "complete");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds",
+		   timeout);
+	eloop_register_timeout(timeout, 0, wpas_p2p_group_idle_timeout,
+			       wpa_s, NULL);
+}
+
+
+/* Returns 1 if the interface was removed */
+int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			  u16 reason_code, const u8 *ie, size_t ie_len,
+			  int locally_generated)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return 0;
+
+	if (!locally_generated)
+		p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie,
+				 ie_len);
+
+	if (reason_code == WLAN_REASON_DEAUTH_LEAVING && !locally_generated &&
+	    wpa_s->current_ssid &&
+	    wpa_s->current_ssid->p2p_group &&
+	    wpa_s->current_ssid->mode == WPAS_MODE_INFRA) {
+		wpa_printf(MSG_DEBUG, "P2P: GO indicated that the P2P Group "
+			   "session is ending");
+		if (wpas_p2p_group_delete(wpa_s,
+					  P2P_GROUP_REMOVAL_GO_ENDING_SESSION)
+		    > 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+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)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return;
+
+	if (!locally_generated)
+		p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie,
+				   ie_len);
+}
+
+
+void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
+{
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	if (p2p == NULL)
+		return;
+
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
+		return;
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_NAME)
+		p2p_set_dev_name(p2p, wpa_s->conf->device_name);
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
+		p2p_set_pri_dev_type(p2p, wpa_s->conf->device_type);
+
+	if (wpa_s->wps &&
+	    (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS))
+		p2p_set_config_methods(p2p, wpa_s->wps->config_methods);
+
+	if (wpa_s->wps && (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID))
+		p2p_set_uuid(p2p, wpa_s->wps->uuid);
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_WPS_STRING) {
+		p2p_set_manufacturer(p2p, wpa_s->conf->manufacturer);
+		p2p_set_model_name(p2p, wpa_s->conf->model_name);
+		p2p_set_model_number(p2p, wpa_s->conf->model_number);
+		p2p_set_serial_number(p2p, wpa_s->conf->serial_number);
+	}
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE)
+		p2p_set_sec_dev_types(p2p,
+				      (void *) wpa_s->conf->sec_device_type,
+				      wpa_s->conf->num_sec_device_types);
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION) {
+		int i;
+		p2p_remove_wps_vendor_extensions(p2p);
+		for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) {
+			if (wpa_s->conf->wps_vendor_ext[i] == NULL)
+				continue;
+			p2p_add_wps_vendor_extension(
+				p2p, wpa_s->conf->wps_vendor_ext[i]);
+		}
+	}
+
+	if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) &&
+	    wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
+		char country[3];
+		country[0] = wpa_s->conf->country[0];
+		country[1] = wpa_s->conf->country[1];
+		country[2] = 0x04;
+		p2p_set_country(p2p, country);
+	}
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_SSID_POSTFIX) {
+		p2p_set_ssid_postfix(p2p, (u8 *) wpa_s->conf->p2p_ssid_postfix,
+				     wpa_s->conf->p2p_ssid_postfix ?
+				     os_strlen(wpa_s->conf->p2p_ssid_postfix) :
+				     0);
+	}
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_INTRA_BSS)
+		p2p_set_intra_bss_dist(p2p, wpa_s->conf->p2p_intra_bss);
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_LISTEN_CHANNEL) {
+		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;
+			/*
+			 * Pick one of the social channels randomly as the
+			 * listen channel.
+			 */
+			if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+				channel = 1;
+			else
+				channel = 1 + (r % 3) * 5;
+			channel_forced = 0;
+		}
+		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);
+	}
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_OPER_CHANNEL) {
+		u8 op_reg_class, op_channel, cfg_op_channel;
+		int ret = 0;
+		unsigned int r;
+		if (wpa_s->conf->p2p_oper_reg_class &&
+		    wpa_s->conf->p2p_oper_channel) {
+			op_reg_class = wpa_s->conf->p2p_oper_reg_class;
+			op_channel = wpa_s->conf->p2p_oper_channel;
+			cfg_op_channel = 1;
+		} else {
+			op_reg_class = 81;
+			/*
+			 * Use random operation channel from (1, 6, 11)
+			 *if no other preference is indicated.
+			 */
+			if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+				op_channel = 1;
+			else
+				op_channel = 1 + (r % 3) * 5;
+			cfg_op_channel = 0;
+		}
+		ret = p2p_set_oper_channel(p2p, op_reg_class, op_channel,
+					   cfg_op_channel);
+		if (ret)
+			wpa_printf(MSG_ERROR, "P2P: Own oper channel update "
+				   "failed: %d", ret);
+	}
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PREF_CHAN) {
+		if (p2p_set_pref_chan(p2p, wpa_s->conf->num_p2p_pref_chan,
+				      wpa_s->conf->p2p_pref_chan) < 0) {
+			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);
+}
+
+
+int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
+		     int duration)
+{
+	if (!wpa_s->ap_iface)
+		return -1;
+	return hostapd_p2p_set_noa(wpa_s->ap_iface->bss[0], count, start,
+				   duration);
+}
+
+
+int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+
+	wpa_s->global->cross_connection = enabled;
+	p2p_set_cross_connect(wpa_s->global->p2p, enabled);
+
+	if (!enabled) {
+		struct wpa_supplicant *iface;
+
+		for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
+		{
+			if (iface->cross_connect_enabled == 0)
+				continue;
+
+			iface->cross_connect_enabled = 0;
+			iface->cross_connect_in_use = 0;
+			wpa_msg_global(iface->parent, MSG_INFO,
+				       P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+				       iface->ifname,
+				       iface->cross_connect_uplink);
+		}
+	}
+
+	return 0;
+}
+
+
+static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink)
+{
+	struct wpa_supplicant *iface;
+
+	if (!uplink->global->cross_connection)
+		return;
+
+	for (iface = uplink->global->ifaces; iface; iface = iface->next) {
+		if (!iface->cross_connect_enabled)
+			continue;
+		if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
+		    0)
+			continue;
+		if (iface->ap_iface == NULL)
+			continue;
+		if (iface->cross_connect_in_use)
+			continue;
+
+		iface->cross_connect_in_use = 1;
+		wpa_msg_global(iface->parent, MSG_INFO,
+			       P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
+			       iface->ifname, iface->cross_connect_uplink);
+	}
+}
+
+
+static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink)
+{
+	struct wpa_supplicant *iface;
+
+	for (iface = uplink->global->ifaces; iface; iface = iface->next) {
+		if (!iface->cross_connect_enabled)
+			continue;
+		if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
+		    0)
+			continue;
+		if (!iface->cross_connect_in_use)
+			continue;
+
+		wpa_msg_global(iface->parent, MSG_INFO,
+			       P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+			       iface->ifname, iface->cross_connect_uplink);
+		iface->cross_connect_in_use = 0;
+	}
+}
+
+
+void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->ap_iface || wpa_s->current_ssid == NULL ||
+	    wpa_s->current_ssid->mode != WPAS_MODE_INFRA ||
+	    wpa_s->cross_connect_disallowed)
+		wpas_p2p_disable_cross_connect(wpa_s);
+	else
+		wpas_p2p_enable_cross_connect(wpa_s);
+	if (!wpa_s->ap_iface &&
+	    eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
+		wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
+}
+
+
+void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
+{
+	wpas_p2p_disable_cross_connect(wpa_s);
+	if (!wpa_s->ap_iface &&
+	    !eloop_is_timeout_registered(wpas_p2p_group_idle_timeout,
+					 wpa_s, NULL))
+		wpas_p2p_set_group_idle_timeout(wpa_s);
+}
+
+
+static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_supplicant *iface;
+
+	if (!wpa_s->global->cross_connection)
+		return;
+
+	for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+		if (iface == wpa_s)
+			continue;
+		if (iface->drv_flags &
+		    WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)
+			continue;
+		if ((iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) &&
+		    iface != wpa_s->parent)
+			continue;
+
+		wpa_s->cross_connect_enabled = 1;
+		os_strlcpy(wpa_s->cross_connect_uplink, iface->ifname,
+			   sizeof(wpa_s->cross_connect_uplink));
+		wpa_printf(MSG_DEBUG, "P2P: Enable cross connection from "
+			   "%s to %s whenever uplink is available",
+			   wpa_s->ifname, wpa_s->cross_connect_uplink);
+
+		if (iface->ap_iface || iface->current_ssid == NULL ||
+		    iface->current_ssid->mode != WPAS_MODE_INFRA ||
+		    iface->cross_connect_disallowed ||
+		    iface->wpa_state != WPA_COMPLETED)
+			break;
+
+		wpa_s->cross_connect_in_use = 1;
+		wpa_msg_global(wpa_s->parent, MSG_INFO,
+			       P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
+			       wpa_s->ifname, wpa_s->cross_connect_uplink);
+		break;
+	}
+}
+
+
+int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_group_interface != P2P_GROUP_INTERFACE_CLIENT &&
+	    !wpa_s->p2p_in_provisioning)
+		return 0; /* not P2P client operation */
+
+	wpa_printf(MSG_DEBUG, "P2P: Terminate connection due to WPS PBC "
+		   "session overlap");
+	if (wpa_s != wpa_s->parent)
+		wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP);
+	wpas_p2p_group_formation_failed(wpa_s, 0);
+	return 1;
+}
+
+
+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,
+				  enum wpas_p2p_channel_update_trig trig)
+{
+	struct p2p_channels chan, cli_chan;
+	struct wpa_used_freq_data *freqs = NULL;
+	unsigned int num = wpa_s->num_multichan_concurrent;
+
+	if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
+		return;
+
+	freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
+	if (!freqs)
+		return;
+
+	num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+
+	os_memset(&chan, 0, sizeof(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, &cli_chan);
+
+	wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
+
+	/*
+	 * The used frequencies map changed, so it is possible that a GO is
+	 * using a channel that is no longer valid for P2P use. It is also
+	 * possible that due to policy consideration, it would be preferable to
+	 * move it to a frequency already used by other station interfaces.
+	 */
+	wpas_p2p_consider_moving_gos(wpa_s, freqs, num, trig);
+
+	os_free(freqs);
+}
+
+
+static void wpas_p2p_scan_res_ignore(struct wpa_supplicant *wpa_s,
+				     struct wpa_scan_results *scan_res)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Ignore scan results");
+}
+
+
+int wpas_p2p_cancel(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_global *global = wpa_s->global;
+	int found = 0;
+	const u8 *peer;
+
+	if (global->p2p == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "P2P: Request to cancel group formation");
+
+	if (wpa_s->pending_interface_name[0] &&
+	    !is_zero_ether_addr(wpa_s->pending_interface_addr))
+		found = 1;
+
+	peer = p2p_get_go_neg_peer(global->p2p);
+	if (peer) {
+		wpa_printf(MSG_DEBUG, "P2P: Unauthorize pending GO Neg peer "
+			   MACSTR, MAC2STR(peer));
+		p2p_unauthorize(global->p2p, peer);
+		found = 1;
+	}
+
+	if (wpa_s->scan_res_handler == wpas_p2p_scan_res_join) {
+		wpa_printf(MSG_DEBUG, "P2P: Stop pending scan for join");
+		wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore;
+		found = 1;
+	}
+
+	if (wpa_s->pending_pd_before_join) {
+		wpa_printf(MSG_DEBUG, "P2P: Stop pending PD before join");
+		wpa_s->pending_pd_before_join = 0;
+		found = 1;
+	}
+
+	wpas_p2p_stop_find(wpa_s);
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (wpa_s == global->p2p_group_formation &&
+		    (wpa_s->p2p_in_provisioning ||
+		     wpa_s->parent->pending_interface_type ==
+		     WPA_IF_P2P_CLIENT)) {
+			wpa_printf(MSG_DEBUG, "P2P: Interface %s in group "
+				   "formation found - cancelling",
+				   wpa_s->ifname);
+			found = 1;
+			eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+					     wpa_s->parent, NULL);
+			if (wpa_s->p2p_in_provisioning) {
+				wpas_group_formation_completed(wpa_s, 0, 0);
+				break;
+			}
+			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, 0);
+		}
+	}
+
+	if (!found) {
+		wpa_printf(MSG_DEBUG, "P2P: No ongoing group formation found");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
+		return;
+
+	wpa_printf(MSG_DEBUG, "P2P: Remove group due to driver resource not "
+		   "being available anymore");
+	wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_UNAVAILABLE);
+}
+
+
+void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+				   int freq_24, int freq_5, int freq_overall)
+{
+	struct p2p_data *p2p = wpa_s->global->p2p;
+	if (p2p == NULL)
+		return;
+	p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall);
+}
+
+
+int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr)
+{
+	u8 peer[ETH_ALEN];
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	if (p2p == NULL)
+		return -1;
+
+	if (hwaddr_aton(addr, peer))
+		return -1;
+
+	return p2p_unauthorize(p2p, peer);
+}
+
+
+/**
+ * wpas_p2p_disconnect - Disconnect from a P2P Group
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This can be used to disconnect from a group in which the local end is a P2P
+ * Client or to end a P2P Group in case the local end is the Group Owner. If a
+ * virtual network interface was created for this group, that interface will be
+ * removed. Otherwise, only the configured P2P group network will be removed
+ * from the interface.
+ */
+int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s)
+{
+
+	if (wpa_s == NULL)
+		return -1;
+
+	return wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_REQUESTED) < 0 ?
+		-1 : 0;
+}
+
+
+int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
+{
+	int ret;
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return 0;
+
+	ret = p2p_in_progress(wpa_s->global->p2p);
+	if (ret == 0) {
+		/*
+		 * Check whether there is an ongoing WPS provisioning step (or
+		 * other parts of group formation) on another interface since
+		 * p2p_in_progress() does not report this to avoid issues for
+		 * scans during such provisioning step.
+		 */
+		if (wpa_s->global->p2p_group_formation &&
+		    wpa_s->global->p2p_group_formation != wpa_s) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Another interface (%s) "
+				"in group formation",
+				wpa_s->global->p2p_group_formation->ifname);
+			ret = 1;
+		}
+	}
+
+	if (!ret && wpa_s->global->p2p_go_wait_client.sec) {
+		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 {
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Waiting for initial client connection during group formation");
+			ret = 1;
+		}
+	}
+
+	return ret;
+}
+
+
+void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid)
+{
+	if (wpa_s->p2p_in_provisioning && ssid->p2p_group &&
+	    eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+				 wpa_s->parent, NULL) > 0) {
+		/**
+		 * Remove the network by scheduling the group formation
+		 * timeout to happen immediately. The teardown code
+		 * needs to be scheduled to run asynch later so that we
+		 * don't delete data from under ourselves unexpectedly.
+		 * Calling wpas_p2p_group_formation_timeout directly
+		 * causes a series of crashes in WPS failure scenarios.
+		 */
+		wpa_printf(MSG_DEBUG, "P2P: Canceled group formation due to "
+			   "P2P group network getting removed");
+		eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
+				       wpa_s->parent, NULL);
+	}
+}
+
+
+struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
+					  const u8 *addr, const u8 *ssid,
+					  size_t ssid_len)
+{
+	struct wpa_ssid *s;
+	size_t i;
+
+	for (s = wpa_s->conf->ssid; s; s = s->next) {
+		if (s->disabled != 2)
+			continue;
+		if (ssid &&
+		    (ssid_len != s->ssid_len ||
+		     os_memcmp(ssid, s->ssid, ssid_len) != 0))
+			continue;
+		if (addr == NULL) {
+			if (s->mode == WPAS_MODE_P2P_GO)
+				return s;
+			continue;
+		}
+		if (os_memcmp(s->bssid, addr, ETH_ALEN) == 0)
+			return s; /* peer is GO in the persistent group */
+		if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL)
+			continue;
+		for (i = 0; i < s->num_p2p_clients; i++) {
+			if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN,
+				      addr, ETH_ALEN) == 0)
+				return s; /* peer is P2P client in persistent
+					   * group */
+		}
+	}
+
+	return NULL;
+}
+
+
+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_s->group_formation_reported) {
+			/*
+			 * GO has not yet notified group formation success since
+			 * the WPS step was not completed cleanly. Do that
+			 * notification now since the P2P Client was able to
+			 * connect and as such, must have received the
+			 * credential from the WPS step.
+			 */
+			if (wpa_s->global->p2p)
+				p2p_wps_success_cb(wpa_s->global->p2p, addr);
+			wpas_group_formation_completed(wpa_s, 1, 0);
+		}
+	}
+	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;
+	wpas_p2p_add_persistent_group_client(wpa_s, addr);
+}
+
+
+static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
+				       int group_added)
+{
+	struct wpa_supplicant *group = wpa_s;
+	int ret = 0;
+
+	if (wpa_s->global->p2p_group_formation)
+		group = wpa_s->global->p2p_group_formation;
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	offchannel_send_action_done(wpa_s);
+	if (group_added)
+		ret = wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT);
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Fall back to GO Negotiation");
+	wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin,
+			 wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0,
+			 0, 0, 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_vht);
+	return ret;
+}
+
+
+int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s)
+{
+	int res;
+
+	if (!wpa_s->p2p_fallback_to_go_neg ||
+	    wpa_s->p2p_in_provisioning <= 5)
+		return 0;
+
+	if (wpas_p2p_peer_go(wpa_s, wpa_s->pending_join_dev_addr) > 0)
+		return 0; /* peer operating as a GO */
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - "
+		"fallback to GO Negotiation");
+	wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG
+		       "reason=GO-not-found");
+	res = wpas_p2p_fallback_to_go_neg(wpa_s, 1);
+
+	return res == 1 ? 2 : 1;
+}
+
+
+unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s)
+{
+	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",
+			wpa_s->conf->p2p_search_delay);
+		return wpa_s->conf->p2p_search_delay;
+	}
+
+	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",
+				wpa_s->conf->p2p_search_delay,
+				ifs->ifname);
+			return wpa_s->conf->p2p_search_delay;
+		}
+	}
+
+	return 0;
+}
+
+
+static int wpas_p2p_remove_psk_entry(struct wpa_supplicant *wpa_s,
+				     struct wpa_ssid *s, const u8 *addr,
+				     int iface_addr)
+{
+	struct psk_list_entry *psk, *tmp;
+	int changed = 0;
+
+	dl_list_for_each_safe(psk, tmp, &s->psk_list, struct psk_list_entry,
+			      list) {
+		if ((iface_addr && !psk->p2p &&
+		     os_memcmp(addr, psk->addr, ETH_ALEN) == 0) ||
+		    (!iface_addr && psk->p2p &&
+		     os_memcmp(addr, psk->addr, ETH_ALEN) == 0)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"P2P: Remove persistent group PSK list entry for "
+				MACSTR " p2p=%u",
+				MAC2STR(psk->addr), psk->p2p);
+			dl_list_del(&psk->list);
+			os_free(psk);
+			changed++;
+		}
+	}
+
+	return changed;
+}
+
+
+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)
+{
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	struct wpa_ssid *persistent;
+	struct psk_list_entry *p, *last;
+
+	if (psk_len != sizeof(p->psk))
+		return;
+
+	if (p2p_dev_addr) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR
+			" p2p_dev_addr=" MACSTR,
+			MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
+		if (is_zero_ether_addr(p2p_dev_addr))
+			p2p_dev_addr = NULL;
+	} else {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR,
+			MAC2STR(mac_addr));
+	}
+
+	if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: new_psk_cb during group formation");
+		/* To be added to persistent group once created */
+		if (wpa_s->global->add_psk == NULL) {
+			wpa_s->global->add_psk = os_zalloc(sizeof(*p));
+			if (wpa_s->global->add_psk == NULL)
+				return;
+		}
+		p = wpa_s->global->add_psk;
+		if (p2p_dev_addr) {
+			p->p2p = 1;
+			os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN);
+		} else {
+			p->p2p = 0;
+			os_memcpy(p->addr, mac_addr, ETH_ALEN);
+		}
+		os_memcpy(p->psk, psk, psk_len);
+		return;
+	}
+
+	if (ssid->mode != WPAS_MODE_P2P_GO || !ssid->p2p_persistent_group) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Ignore new_psk_cb on not-persistent GO");
+		return;
+	}
+
+	persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid,
+					     ssid->ssid_len);
+	if (!persistent) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not find persistent group information to store the new PSK");
+		return;
+	}
+
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL)
+		return;
+	if (p2p_dev_addr) {
+		p->p2p = 1;
+		os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN);
+	} else {
+		p->p2p = 0;
+		os_memcpy(p->addr, mac_addr, ETH_ALEN);
+	}
+	os_memcpy(p->psk, psk, psk_len);
+
+	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);
+		dl_list_del(&last->list);
+		os_free(last);
+	}
+
+	wpas_p2p_remove_psk_entry(wpa_s->parent, persistent,
+				  p2p_dev_addr ? p2p_dev_addr : mac_addr,
+				  p2p_dev_addr == NULL);
+	if (p2p_dev_addr) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add new PSK for p2p_dev_addr="
+			MACSTR, MAC2STR(p2p_dev_addr));
+	} else {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add new PSK for addr=" MACSTR,
+			MAC2STR(mac_addr));
+	}
+	dl_list_add(&persistent->psk_list, &p->list);
+
+	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");
+}
+
+
+static void wpas_p2p_remove_psk(struct wpa_supplicant *wpa_s,
+				struct wpa_ssid *s, const u8 *addr,
+				int iface_addr)
+{
+	int res;
+
+	res = wpas_p2p_remove_psk_entry(wpa_s, s, addr, iface_addr);
+	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");
+}
+
+
+static void wpas_p2p_remove_client_go(struct wpa_supplicant *wpa_s,
+				      const u8 *peer, int iface_addr)
+{
+	struct hostapd_data *hapd;
+	struct hostapd_wpa_psk *psk, *prev, *rem;
+	struct sta_info *sta;
+
+	if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL ||
+	    wpa_s->current_ssid->mode != WPAS_MODE_P2P_GO)
+		return;
+
+	/* Remove per-station PSK entry */
+	hapd = wpa_s->ap_iface->bss[0];
+	prev = NULL;
+	psk = hapd->conf->ssid.wpa_psk;
+	while (psk) {
+		if ((iface_addr && os_memcmp(peer, psk->addr, ETH_ALEN) == 0) ||
+		    (!iface_addr &&
+		     os_memcmp(peer, psk->p2p_dev_addr, ETH_ALEN) == 0)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove operating group PSK entry for "
+				MACSTR " iface_addr=%d",
+				MAC2STR(peer), iface_addr);
+			if (prev)
+				prev->next = psk->next;
+			else
+				hapd->conf->ssid.wpa_psk = psk->next;
+			rem = psk;
+			psk = psk->next;
+			os_free(rem);
+		} else {
+			prev = psk;
+			psk = psk->next;
+		}
+	}
+
+	/* Disconnect from group */
+	if (iface_addr)
+		sta = ap_get_sta(hapd, peer);
+	else
+		sta = ap_get_sta_p2p(hapd, peer);
+	if (sta) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disconnect peer " MACSTR
+			" (iface_addr=%d) from group",
+			MAC2STR(peer), iface_addr);
+		hostapd_drv_sta_deauth(hapd, sta->addr,
+				       WLAN_REASON_DEAUTH_LEAVING);
+		ap_sta_deauthenticate(hapd, sta, WLAN_REASON_DEAUTH_LEAVING);
+	}
+}
+
+
+void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer,
+			    int iface_addr)
+{
+	struct wpa_ssid *s;
+	struct wpa_supplicant *w;
+	struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove client " MACSTR, MAC2STR(peer));
+
+	/* Remove from any persistent group */
+	for (s = p2p_wpa_s->conf->ssid; s; s = s->next) {
+		if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
+			continue;
+		if (!iface_addr)
+			wpas_remove_persistent_peer(p2p_wpa_s, s, peer, 0);
+		wpas_p2p_remove_psk(p2p_wpa_s, s, peer, iface_addr);
+	}
+
+	/* Remove from any operating group */
+	for (w = wpa_s->global->ifaces; w; w = w->next)
+		wpas_p2p_remove_client_go(w, peer, iface_addr);
+}
+
+
+static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_PSK_FAILURE);
+}
+
+
+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;
+
+	if (ssid == NULL || !ssid->p2p_group)
+		return 0;
+
+	if (wpa_s->p2p_last_4way_hs_fail &&
+	    wpa_s->p2p_last_4way_hs_fail == ssid) {
+		u8 go_dev_addr[ETH_ALEN];
+		struct wpa_ssid *persistent;
+
+		if (wpas_p2p_persistent_group(wpa_s, go_dev_addr,
+					      ssid->ssid,
+					      ssid->ssid_len) <= 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not determine whether 4-way handshake failures were for a persistent group");
+			goto disconnect;
+		}
+
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Two 4-way handshake failures for a P2P group - go_dev_addr="
+			MACSTR, MAC2STR(go_dev_addr));
+		persistent = wpas_p2p_get_persistent(wpa_s->parent, go_dev_addr,
+						     ssid->ssid,
+						     ssid->ssid_len);
+		if (persistent == NULL || persistent->mode != WPAS_MODE_INFRA) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No matching persistent group stored");
+			goto disconnect;
+		}
+		wpa_msg_global(wpa_s->parent, MSG_INFO,
+			       P2P_EVENT_PERSISTENT_PSK_FAIL "%d",
+			       persistent->id);
+	disconnect:
+		wpa_s->p2p_last_4way_hs_fail = NULL;
+		/*
+		 * Remove the group from a timeout to avoid issues with caller
+		 * continuing to use the interface if this is on a P2P group
+		 * interface.
+		 */
+		eloop_register_timeout(0, 0, wpas_p2p_psk_failure_removal,
+				       wpa_s, NULL);
+		return 1;
+	}
+
+	wpa_s->p2p_last_4way_hs_fail = ssid;
+	return 0;
+}
+
+
+#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;
+}
+
+
+static int wpas_p2p_cli_freq(struct wpa_supplicant *wpa_s,
+			     struct wpa_ssid **ssid, u8 *go_dev_addr)
+{
+	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->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;
+}
+
+
+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 (len > end - pos) {
+		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 (len > end - pos) {
+		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;
+
+	/*
+	 * 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;
+
+	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 based on operating channel",
+			cand);
+		p2p_set_listen_channel(wpa_s->global->p2p, 81, cand, 0);
+	}
+}
+
+
+static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s)
+{
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled");
+		return -1;
+	}
+
+	/* TODO: Add CSA support */
+	wpa_dbg(wpa_s, MSG_DEBUG, "Moving GO with CSA is not implemented");
+	return -1;
+}
+
+
+static void wpas_p2p_move_go_no_csa(struct wpa_supplicant *wpa_s)
+{
+	struct p2p_go_neg_results params;
+	struct wpa_ssid *current_ssid = wpa_s->current_ssid;
+
+	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz",
+		current_ssid->frequency);
+
+	/* Stop the AP functionality */
+	/* TODO: Should do this in a way that does not indicated to possible
+	 * P2P Clients in the group that the group is terminated. */
+	wpa_supplicant_ap_deinit(wpa_s);
+
+	/* Reselect the GO frequency */
+	if (wpas_p2p_init_go_params(wpa_s, &params, 0, 0, 0, NULL)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to reselect freq");
+		wpas_p2p_group_delete(wpa_s,
+				      P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
+		return;
+	}
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New freq selected for the GO (%u MHz)",
+		params.freq);
+
+	if (params.freq &&
+	    !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Selected freq (%u MHz) is not valid for P2P",
+			   params.freq);
+		wpas_p2p_group_delete(wpa_s,
+				      P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
+		return;
+	}
+
+	/* Update the frequency */
+	current_ssid->frequency = params.freq;
+	wpa_s->connect_without_scan = current_ssid;
+	wpa_s->reassociate = 1;
+	wpa_s->disconnected = 0;
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (!wpa_s->ap_iface || !wpa_s->current_ssid)
+		return;
+
+	wpas_p2p_go_update_common_freqs(wpa_s);
+
+	/*
+	 * First, try a channel switch flow. If it is not supported or fails,
+	 * take down the GO and bring it up again.
+	 */
+	if (wpas_p2p_move_go_csa(wpa_s) < 0)
+		wpas_p2p_move_go_no_csa(wpa_s);
+}
+
+
+static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct wpa_used_freq_data *freqs = NULL;
+	unsigned int num = wpa_s->num_multichan_concurrent;
+
+	freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
+	if (!freqs)
+		return;
+
+	num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+
+	/* Previous attempt to move a GO was not possible -- try again. */
+	wpas_p2p_consider_moving_gos(wpa_s, freqs, num,
+				     WPAS_P2P_CHANNEL_UPDATE_ANY);
+
+	os_free(freqs);
+}
+
+
+/*
+ * Consider moving a GO from its currently used frequency:
+ * 1. It is possible that due to regulatory consideration the frequency
+ *    can no longer be used and there is a need to evacuate the GO.
+ * 2. It is possible that due to MCC considerations, it would be preferable
+ *    to move the GO to a channel that is currently used by some other
+ *    station interface.
+ *
+ * In case a frequency that became invalid is once again valid, cancel a
+ * previously initiated GO frequency change.
+ */
+static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s,
+					    struct wpa_used_freq_data *freqs,
+					    unsigned int num)
+{
+	unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0;
+	unsigned int timeout;
+	int freq;
+
+	wpas_p2p_go_update_common_freqs(wpa_s);
+
+	freq = wpa_s->current_ssid->frequency;
+	for (i = 0, invalid_freq = 0; i < num; i++) {
+		if (freqs[i].freq == freq) {
+			flags = freqs[i].flags;
+
+			/* The channel is invalid, must change it */
+			if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"P2P: Freq=%d MHz no longer valid for GO",
+					freq);
+				invalid_freq = 1;
+			}
+		} else if (freqs[i].flags == 0) {
+			/* Freq is not used by any other station interface */
+			continue;
+		} else if (!p2p_supported_freq(wpa_s->global->p2p,
+					       freqs[i].freq)) {
+			/* Freq is not valid for P2P use cases */
+			continue;
+		} else if (wpa_s->conf->p2p_go_freq_change_policy ==
+			   P2P_GO_FREQ_MOVE_SCM) {
+			policy_move = 1;
+		} else if (wpa_s->conf->p2p_go_freq_change_policy ==
+			   P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS &&
+			   wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
+			policy_move = 1;
+		}
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"P2P: GO move: invalid_freq=%u, policy_move=%u, flags=0x%X",
+		invalid_freq, policy_move, flags);
+
+	/*
+	 * The channel is valid, or we are going to have a policy move, so
+	 * cancel timeout.
+	 */
+	if (!invalid_freq || policy_move) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Cancel a GO move from freq=%d MHz", freq);
+		eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
+
+		if (wpas_p2p_in_progress(wpa_s)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"P2P: GO move: policy CS is not allowed - setting timeout to re-consider GO move");
+			eloop_cancel_timeout(wpas_p2p_reconsider_moving_go,
+					     wpa_s, NULL);
+			eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
+					       wpas_p2p_reconsider_moving_go,
+					       wpa_s, NULL);
+			return;
+		}
+	}
+
+	if (!invalid_freq && (!policy_move || flags != 0)) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Not initiating a GO frequency change");
+		return;
+	}
+
+	if (invalid_freq && !wpas_p2p_disallowed_freq(wpa_s->global, freq))
+		timeout = P2P_GO_FREQ_CHANGE_TIME;
+	else
+		timeout = 0;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz in %d secs",
+		freq, timeout);
+	eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
+	eloop_register_timeout(timeout, 0, wpas_p2p_move_go, wpa_s, NULL);
+}
+
+
+static void wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
+					 struct wpa_used_freq_data *freqs,
+					 unsigned int num,
+					 enum wpas_p2p_channel_update_trig trig)
+{
+	struct wpa_supplicant *ifs;
+
+	eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, ELOOP_ALL_CTX,
+			     NULL);
+
+	/*
+	 * Travers all the radio interfaces, and for each GO interface, check
+	 * if there is a need to move the GO from the frequency it is using,
+	 * or in case the frequency is valid again, cancel the evacuation flow.
+	 */
+	dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+			 radio_list) {
+		if (ifs->current_ssid == NULL ||
+		    ifs->current_ssid->mode != WPAS_MODE_P2P_GO)
+			continue;
+
+		/*
+		 * The GO was just started or completed channel switch, no need
+		 * to move it.
+		 */
+		if (wpa_s == ifs &&
+		    (trig == WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE ||
+		     trig == WPAS_P2P_CHANNEL_UPDATE_CS)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"P2P: GO move - schedule re-consideration");
+			eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
+					       wpas_p2p_reconsider_moving_go,
+					       wpa_s, NULL);
+			continue;
+		}
+
+		wpas_p2p_consider_moving_one_go(ifs, freqs, num);
+	}
+}
+
+
+void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return;
+
+	wpas_p2p_update_channel_list(wpa_s,
+				     WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE);
+}
+
+
+void wpas_p2p_deinit_iface(struct wpa_supplicant *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/hostap/wpa_supplicant/p2p_supplicant.h b/hostap/wpa_supplicant/p2p_supplicant.h
new file mode 100644
index 0000000..56e6834
--- /dev/null
+++ b/hostap/wpa_supplicant/p2p_supplicant.h
@@ -0,0 +1,335 @@
+/*
+ * wpa_supplicant - P2P
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef P2P_SUPPLICANT_H
+#define P2P_SUPPLICANT_H
+
+enum p2p_wps_method;
+struct p2p_go_neg_results;
+enum p2p_send_action_result;
+struct p2p_peer_info;
+struct p2p_channels;
+struct wps_event_fail;
+struct p2ps_provision;
+
+enum wpas_p2p_channel_update_trig {
+	WPAS_P2P_CHANNEL_UPDATE_ANY,
+	WPAS_P2P_CHANNEL_UPDATE_DRIVER,
+	WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE,
+	WPAS_P2P_CHANNEL_UPDATE_AVOID,
+	WPAS_P2P_CHANNEL_UPDATE_DISALLOW,
+	WPAS_P2P_CHANNEL_UPDATE_CS,
+};
+
+int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s,
+				  const char *conf_p2p_dev);
+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, int vht);
+int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s,
+                                          int freq, struct wpa_ssid *ssid);
+int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
+		       int freq, int ht40, int vht);
+int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
+				  struct wpa_ssid *ssid, int addr_allocated,
+				  int force_freq, int neg_freq, int ht40,
+				  int vht, const struct p2p_channels *channels,
+				  int connection_timeout, int force_scan);
+struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
+				       struct wpa_ssid *ssid);
+enum wpas_p2p_prov_disc_use {
+	WPAS_P2P_PD_FOR_GO_NEG,
+	WPAS_P2P_PD_FOR_JOIN,
+	WPAS_P2P_PD_AUTO,
+	WPAS_P2P_PD_FOR_ASP
+};
+int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+		       const char *config_method,
+		       enum wpas_p2p_prov_disc_use use,
+		       struct p2ps_provision *p2ps_prov);
+void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
+				const u8 *data, size_t data_len,
+				enum p2p_send_action_result result);
+int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+			      char *end);
+enum p2p_discovery_type;
+int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
+		  enum p2p_discovery_type type,
+		  unsigned int num_req_dev_types, const u8 *req_dev_types,
+		  const u8 *dev_id, unsigned int search_delay,
+		  u8 seek_cnt, const char **seek_string, int freq);
+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);
+void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies);
+u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+			const struct wpabuf *tlvs);
+u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
+			    const char *svc_str, const char *info_substr);
+u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+			     u8 version, const char *query);
+u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
+				     const u8 *dst, const char *role);
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req);
+void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
+			  const u8 *dst, u8 dialog_token,
+			  const struct wpabuf *resp_tlvs);
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s);
+void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s);
+int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
+				 struct wpabuf *query, struct wpabuf *resp);
+int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
+				 const struct wpabuf *query);
+int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
+			      const char *service);
+int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
+			      const char *service);
+int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, int auto_accept,
+			     u32 adv_id, const char *adv_str, u8 svc_state,
+			     u16 config_methods, const char *svc_info,
+			     const u8 *cpt_priority);
+int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id);
+void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s);
+int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id);
+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);
+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 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);
+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,
+			unsigned int interval);
+int wpas_p2p_deauth_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_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			     u16 reason_code, const u8 *ie, size_t ie_len,
+			     int locally_generated);
+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);
+int wpas_p2p_cancel(struct wpa_supplicant *wpa_s);
+int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr);
+int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s);
+struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
+					  const u8 *addr, const u8 *ssid,
+					  size_t ssid_len);
+void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
+				       const u8 *addr);
+int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s);
+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
+
+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,
+			  unsigned int rx_freq, 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,
+				  enum wpas_p2p_channel_update_trig trig);
+
+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);
+int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
+
+#else /* CONFIG_P2P */
+
+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,
+					unsigned int rx_freq, 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,
+			     enum wpas_p2p_channel_update_trig trig)
+{
+}
+
+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)
+{
+	return 0;
+}
+
+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)
+{
+}
+
+static inline int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s,
+					const char *ifname)
+{
+	return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+#endif /* P2P_SUPPLICANT_H */
diff --git a/hostap/wpa_supplicant/p2p_supplicant_sd.c b/hostap/wpa_supplicant/p2p_supplicant_sd.c
new file mode 100644
index 0000000..fc07b07
--- /dev/null
+++ b/hostap/wpa_supplicant/p2p_supplicant_sd.c
@@ -0,0 +1,1273 @@
+/*
+ * wpa_supplicant - P2P service discovery
+ * 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.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "p2p/p2p.h"
+#include "wpa_supplicant_i.h"
+#include "notify.h"
+#include "p2p_supplicant.h"
+
+
+/*
+ * DNS Header section is used only to calculate compression pointers, so the
+ * contents of this data does not matter, but the length needs to be reserved
+ * in the virtual packet.
+ */
+#define DNS_HEADER_LEN 12
+
+/*
+ * 27-octet in-memory packet from P2P specification containing two implied
+ * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN
+ */
+#define P2P_SD_IN_MEMORY_LEN 27
+
+static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start,
+				       u8 **spos, const u8 *end)
+{
+	while (*spos < end) {
+		u8 val = ((*spos)[0] & 0xc0) >> 6;
+		int len;
+
+		if (val == 1 || val == 2) {
+			/* These are reserved values in RFC 1035 */
+			wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
+				   "sequence starting with 0x%x", val);
+			return -1;
+		}
+
+		if (val == 3) {
+			u16 offset;
+			u8 *spos_tmp;
+
+			/* Offset */
+			if (*spos + 2 > end) {
+				wpa_printf(MSG_DEBUG, "P2P: No room for full "
+					   "DNS offset field");
+				return -1;
+			}
+
+			offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1];
+			if (offset >= *spos - start) {
+				wpa_printf(MSG_DEBUG, "P2P: Invalid DNS "
+					   "pointer offset %u", offset);
+				return -1;
+			}
+
+			(*spos) += 2;
+			spos_tmp = start + offset;
+			return p2p_sd_dns_uncompress_label(upos, uend, start,
+							   &spos_tmp,
+							   *spos - 2);
+		}
+
+		/* Label */
+		len = (*spos)[0] & 0x3f;
+		if (len == 0)
+			return 0;
+
+		(*spos)++;
+		if (*spos + len > end) {
+			wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
+				   "sequence - no room for label with length "
+				   "%u", len);
+			return -1;
+		}
+
+		if (*upos + len + 2 > uend)
+			return -2;
+
+		os_memcpy(*upos, *spos, len);
+		*spos += len;
+		*upos += len;
+		(*upos)[0] = '.';
+		(*upos)++;
+		(*upos)[0] = '\0';
+	}
+
+	return 0;
+}
+
+
+/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet.
+ * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is
+ * not large enough */
+static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg,
+				 size_t msg_len, size_t offset)
+{
+	/* 27-octet in-memory packet from P2P specification */
+	const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01"
+		"\x04_udp\xC0\x11\x00\x0C\x00\x01";
+	u8 *tmp, *end, *spos;
+	char *upos, *uend;
+	int ret = 0;
+
+	if (buf_len < 2)
+		return -1;
+	if (offset > msg_len)
+		return -1;
+
+	tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len);
+	if (tmp == NULL)
+		return -1;
+	spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN;
+	end = spos + msg_len;
+	spos += offset;
+
+	os_memset(tmp, 0, DNS_HEADER_LEN);
+	os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN);
+	os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len);
+
+	upos = buf;
+	uend = buf + buf_len;
+
+	ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end);
+	if (ret) {
+		os_free(tmp);
+		return ret;
+	}
+
+	if (upos == buf) {
+		upos[0] = '.';
+		upos[1] = '\0';
+	} else if (upos[-1] == '.')
+		upos[-1] = '\0';
+
+	os_free(tmp);
+	return 0;
+}
+
+
+static struct p2p_srv_bonjour *
+wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
+			     const struct wpabuf *query)
+{
+	struct p2p_srv_bonjour *bsrv;
+	size_t len;
+
+	len = wpabuf_len(query);
+	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+			 struct p2p_srv_bonjour, list) {
+		if (len == wpabuf_len(bsrv->query) &&
+		    os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
+			      len) == 0)
+			return bsrv;
+	}
+	return NULL;
+}
+
+
+static struct p2p_srv_upnp *
+wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
+			  const char *service)
+{
+	struct p2p_srv_upnp *usrv;
+
+	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+			 struct p2p_srv_upnp, list) {
+		if (version == usrv->version &&
+		    os_strcmp(service, usrv->service) == 0)
+			return usrv;
+	}
+	return NULL;
+}
+
+
+static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto,
+			      u8 srv_trans_id, u8 status)
+{
+	u8 *len_pos;
+
+	if (wpabuf_tailroom(resp) < 5)
+		return;
+
+	/* Length (to be filled) */
+	len_pos = wpabuf_put(resp, 2);
+	wpabuf_put_u8(resp, srv_proto);
+	wpabuf_put_u8(resp, srv_trans_id);
+	/* Status Code */
+	wpabuf_put_u8(resp, status);
+	/* Response Data: empty */
+	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
+					u8 srv_trans_id)
+{
+	wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
+			  P2P_SD_PROTO_NOT_AVAILABLE);
+}
+
+
+static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto,
+				    u8 srv_trans_id)
+{
+	wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST);
+}
+
+
+static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto,
+				  u8 srv_trans_id)
+{
+	wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
+			  P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+}
+
+
+static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s,
+				struct wpabuf *resp, u8 srv_trans_id)
+{
+	struct p2p_srv_bonjour *bsrv;
+	u8 *len_pos;
+
+	wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
+
+	if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+		wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+		return;
+	}
+
+	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+			 struct p2p_srv_bonjour, list) {
+		if (wpabuf_tailroom(resp) <
+		    5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
+			return;
+		/* Length (to be filled) */
+		len_pos = wpabuf_put(resp, 2);
+		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+		wpabuf_put_u8(resp, srv_trans_id);
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+				  wpabuf_head(bsrv->resp),
+				  wpabuf_len(bsrv->resp));
+		/* Response Data */
+		wpabuf_put_buf(resp, bsrv->query); /* Key */
+		wpabuf_put_buf(resp, bsrv->resp); /* Value */
+		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+			     2);
+	}
+}
+
+
+static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query,
+			       size_t query_len)
+{
+	char str_rx[256], str_srv[256];
+
+	if (query_len < 3 || wpabuf_len(bsrv->query) < 3)
+		return 0; /* Too short to include DNS Type and Version */
+	if (os_memcmp(query + query_len - 3,
+		      wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3,
+		      3) != 0)
+		return 0; /* Mismatch in DNS Type or Version */
+	if (query_len == wpabuf_len(bsrv->query) &&
+	    os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0)
+		return 1; /* Binary match */
+
+	if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3,
+				  0))
+		return 0; /* Failed to uncompress query */
+	if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv),
+				  wpabuf_head(bsrv->query),
+				  wpabuf_len(bsrv->query) - 3, 0))
+		return 0; /* Failed to uncompress service */
+
+	return os_strcmp(str_rx, str_srv) == 0;
+}
+
+
+static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
+				struct wpabuf *resp, u8 srv_trans_id,
+				const u8 *query, size_t query_len)
+{
+	struct p2p_srv_bonjour *bsrv;
+	u8 *len_pos;
+	int matches = 0;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
+			  query, query_len);
+	if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+		wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+		wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
+					    srv_trans_id);
+		return;
+	}
+
+	if (query_len == 0) {
+		wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+		return;
+	}
+
+	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+			 struct p2p_srv_bonjour, list) {
+		if (!match_bonjour_query(bsrv, query, query_len))
+			continue;
+
+		if (wpabuf_tailroom(resp) <
+		    5 + query_len + wpabuf_len(bsrv->resp))
+			return;
+
+		matches++;
+
+		/* Length (to be filled) */
+		len_pos = wpabuf_put(resp, 2);
+		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+		wpabuf_put_u8(resp, srv_trans_id);
+
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+				  wpabuf_head(bsrv->resp),
+				  wpabuf_len(bsrv->resp));
+
+		/* Response Data */
+		wpabuf_put_data(resp, query, query_len); /* Key */
+		wpabuf_put_buf(resp, bsrv->resp); /* Value */
+
+		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+	}
+
+	if (matches == 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
+			   "available");
+		if (wpabuf_tailroom(resp) < 5)
+			return;
+
+		/* Length (to be filled) */
+		len_pos = wpabuf_put(resp, 2);
+		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+		wpabuf_put_u8(resp, srv_trans_id);
+
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+		/* Response Data: empty */
+		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+			     2);
+	}
+}
+
+
+static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
+			     struct wpabuf *resp, u8 srv_trans_id)
+{
+	struct p2p_srv_upnp *usrv;
+	u8 *len_pos;
+
+	wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
+
+	if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+		wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+		return;
+	}
+
+	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+			 struct p2p_srv_upnp, list) {
+		if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
+			return;
+
+		/* Length (to be filled) */
+		len_pos = wpabuf_put(resp, 2);
+		wpabuf_put_u8(resp, P2P_SERV_UPNP);
+		wpabuf_put_u8(resp, srv_trans_id);
+
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+		/* Response Data */
+		wpabuf_put_u8(resp, usrv->version);
+		wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+			   usrv->service);
+		wpabuf_put_str(resp, usrv->service);
+		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+			     2);
+	}
+}
+
+
+static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
+			     struct wpabuf *resp, u8 srv_trans_id,
+			     const u8 *query, size_t query_len)
+{
+	struct p2p_srv_upnp *usrv;
+	u8 *len_pos;
+	u8 version;
+	char *str;
+	int count = 0;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
+			  query, query_len);
+
+	if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+		wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+		wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
+					    srv_trans_id);
+		return;
+	}
+
+	if (query_len == 0) {
+		wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+		return;
+	}
+
+	if (wpabuf_tailroom(resp) < 5)
+		return;
+
+	/* Length (to be filled) */
+	len_pos = wpabuf_put(resp, 2);
+	wpabuf_put_u8(resp, P2P_SERV_UPNP);
+	wpabuf_put_u8(resp, srv_trans_id);
+
+	version = query[0];
+	str = os_malloc(query_len);
+	if (str == NULL)
+		return;
+	os_memcpy(str, query + 1, query_len - 1);
+	str[query_len - 1] = '\0';
+
+	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+			 struct p2p_srv_upnp, list) {
+		if (version != usrv->version)
+			continue;
+
+		if (os_strcmp(str, "ssdp:all") != 0 &&
+		    os_strstr(usrv->service, str) == NULL)
+			continue;
+
+		if (wpabuf_tailroom(resp) < 2)
+			break;
+		if (count == 0) {
+			/* Status Code */
+			wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+			/* Response Data */
+			wpabuf_put_u8(resp, version);
+		} else
+			wpabuf_put_u8(resp, ',');
+
+		count++;
+
+		wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+			   usrv->service);
+		if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
+			break;
+		wpabuf_put_str(resp, usrv->service);
+	}
+	os_free(str);
+
+	if (count == 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
+			   "available");
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+		/* Response Data: empty */
+	}
+
+	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
+			    struct wpabuf *resp, u8 srv_trans_id,
+			    const u8 *query, size_t query_len)
+{
+	const u8 *pos;
+	u8 role;
+	u8 *len_pos;
+
+	wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len);
+
+	if (!wpa_s->global->wifi_display) {
+		wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available");
+		wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY,
+					    srv_trans_id);
+		return;
+	}
+
+	if (query_len < 1) {
+		wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device "
+			   "Role");
+		return;
+	}
+
+	if (wpabuf_tailroom(resp) < 5)
+		return;
+
+	pos = query;
+	role = *pos++;
+	wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role);
+
+	/* TODO: role specific handling */
+
+	/* Length (to be filled) */
+	len_pos = wpabuf_put(resp, 2);
+	wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY);
+	wpabuf_put_u8(resp, srv_trans_id);
+	wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */
+
+	while (pos < query + query_len) {
+		if (*pos < MAX_WFD_SUBELEMS &&
+		    wpa_s->global->wfd_subelem[*pos] &&
+		    wpabuf_tailroom(resp) >=
+		    wpabuf_len(wpa_s->global->wfd_subelem[*pos])) {
+			wpa_printf(MSG_DEBUG, "P2P: Add WSD response "
+				   "subelement %u", *pos);
+			wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]);
+		}
+		pos++;
+	}
+
+	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+static int find_p2ps_substr(struct p2ps_advertisement *adv_data,
+			    const u8 *needle, size_t needle_len)
+{
+	const u8 *haystack = (const u8 *) adv_data->svc_info;
+	size_t haystack_len, i;
+
+	/* Allow search term to be empty */
+	if (!needle || !needle_len)
+		return 1;
+
+	if (!haystack)
+		return 0;
+
+	haystack_len = os_strlen(adv_data->svc_info);
+	for (i = 0; i < haystack_len; i++) {
+		if (haystack_len - i < needle_len)
+			break;
+		if (os_memcmp(haystack + i, needle, needle_len) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s,
+			    struct wpabuf *resp, u8 srv_trans_id,
+			    const u8 *query, size_t query_len)
+{
+	struct p2ps_advertisement *adv_data;
+	const u8 *svc = &query[1];
+	const u8 *info = NULL;
+	size_t svc_len = query[0];
+	size_t info_len = 0;
+	int prefix = 0;
+	u8 *count_pos = NULL;
+	u8 *len_pos = NULL;
+
+	wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len);
+
+	if (!wpa_s->global->p2p) {
+		wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available");
+		wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id);
+		return;
+	}
+
+	/* Info block is optional */
+	if (svc_len + 1 < query_len) {
+		info = &svc[svc_len];
+		info_len = *info++;
+	}
+
+	/* Range check length of svc string and info block */
+	if (svc_len + (info_len ? info_len + 2 : 1) > query_len) {
+		wpa_printf(MSG_DEBUG, "P2P: ASP bad request");
+		wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id);
+		return;
+	}
+
+	/* Detect and correct for prefix search */
+	if (svc_len && svc[svc_len - 1] == '*') {
+		prefix = 1;
+		svc_len--;
+	}
+
+	for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p);
+	     adv_data; adv_data = adv_data->next) {
+		/* If not a prefix match, reject length mismatches */
+		if (!prefix && svc_len != os_strlen(adv_data->svc_name))
+			continue;
+
+		/* Search each service for request */
+		if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 &&
+		    find_p2ps_substr(adv_data, info, info_len)) {
+			size_t len = os_strlen(adv_data->svc_name);
+			size_t svc_info_len = 0;
+
+			if (adv_data->svc_info)
+				svc_info_len = os_strlen(adv_data->svc_info);
+
+			if (len > 0xff || svc_info_len > 0xffff)
+				return;
+
+			/* Length & Count to be filled as we go */
+			if (!len_pos && !count_pos) {
+				if (wpabuf_tailroom(resp) <
+				    len + svc_info_len + 16)
+					return;
+
+				len_pos = wpabuf_put(resp, 2);
+				wpabuf_put_u8(resp, P2P_SERV_P2PS);
+				wpabuf_put_u8(resp, srv_trans_id);
+				/* Status Code */
+				wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+				count_pos = wpabuf_put(resp, 1);
+				*count_pos = 0;
+			} else if (wpabuf_tailroom(resp) <
+				   len + svc_info_len + 10)
+				return;
+
+			if (svc_info_len) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Add Svc: %s info: %s",
+					   adv_data->svc_name,
+					   adv_data->svc_info);
+			} else {
+				wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s",
+					   adv_data->svc_name);
+			}
+
+			/* Advertisement ID */
+			wpabuf_put_le32(resp, adv_data->id);
+
+			/* Config Methods */
+			wpabuf_put_be16(resp, adv_data->config_methods);
+
+			/* Service Name */
+			wpabuf_put_u8(resp, (u8) len);
+			wpabuf_put_data(resp, adv_data->svc_name, len);
+
+			/* Service State */
+			wpabuf_put_u8(resp, adv_data->state);
+
+			/* Service Information */
+			wpabuf_put_le16(resp, (u16) svc_info_len);
+			wpabuf_put_data(resp, adv_data->svc_info, svc_info_len);
+
+			/* Update length and count */
+			(*count_pos)++;
+			WPA_PUT_LE16(len_pos,
+				     (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+		}
+	}
+
+	/* Return error if no matching svc found */
+	if (count_pos == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: ASP service not found");
+		wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id);
+	}
+}
+
+
+static void wpas_sd_all_asp(struct wpa_supplicant *wpa_s,
+			    struct wpabuf *resp, u8 srv_trans_id)
+{
+	/* Query data to add all P2PS advertisements:
+	 *  - Service name length: 1
+	 *  - Service name: '*'
+	 *  - Service Information Request Length: 0
+	 */
+	const u8 q[] = { 1, (const u8) '*', 0 };
+
+	if (p2p_get_p2ps_adv_list(wpa_s->global->p2p))
+		wpas_sd_req_asp(wpa_s, resp, srv_trans_id, q, sizeof(q));
+}
+
+
+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;
+	const u8 *end = tlvs + tlvs_len;
+	const u8 *tlv_end;
+	u16 slen;
+	struct wpabuf *resp;
+	u8 srv_proto, srv_trans_id;
+	size_t buf_len;
+	char *buf;
+
+	wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
+		    tlvs, tlvs_len);
+	buf_len = 2 * tlvs_len + 1;
+	buf = os_malloc(buf_len);
+	if (buf) {
+		wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+		wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
+			     MACSTR " %u %u %s",
+			     freq, MAC2STR(sa), dialog_token, update_indic,
+			     buf);
+		os_free(buf);
+	}
+
+	if (wpa_s->p2p_sd_over_ctrl_iface) {
+		wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+					   update_indic, tlvs, tlvs_len);
+		return; /* to be processed by an external program */
+	}
+
+	resp = wpabuf_alloc(10000);
+	if (resp == NULL)
+		return;
+
+	while (pos + 1 < end) {
+		wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
+		slen = WPA_GET_LE16(pos);
+		pos += 2;
+		if (pos + slen > end || slen < 2) {
+			wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
+				   "length");
+			wpabuf_free(resp);
+			return;
+		}
+		tlv_end = pos + slen;
+
+		srv_proto = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+			   srv_proto);
+		srv_trans_id = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+			   srv_trans_id);
+
+		wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
+			    pos, tlv_end - pos);
+
+
+		if (wpa_s->force_long_sd) {
+			wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
+				   "response");
+			wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+			wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+			wpas_sd_all_asp(wpa_s, resp, srv_trans_id);
+			goto done;
+		}
+
+		switch (srv_proto) {
+		case P2P_SERV_ALL_SERVICES:
+			wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
+				   "for all services");
+			if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
+			    dl_list_empty(&wpa_s->global->p2p_srv_bonjour) &&
+			    !p2p_get_p2ps_adv_list(wpa_s->global->p2p)) {
+				wpa_printf(MSG_DEBUG, "P2P: No service "
+					   "discovery protocols available");
+				wpas_sd_add_proto_not_avail(
+					resp, P2P_SERV_ALL_SERVICES,
+					srv_trans_id);
+				break;
+			}
+			wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+			wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+			wpas_sd_all_asp(wpa_s, resp, srv_trans_id);
+			break;
+		case P2P_SERV_BONJOUR:
+			wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
+					    pos, tlv_end - pos);
+			break;
+		case P2P_SERV_UPNP:
+			wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
+					 pos, tlv_end - pos);
+			break;
+#ifdef CONFIG_WIFI_DISPLAY
+		case P2P_SERV_WIFI_DISPLAY:
+			wpas_sd_req_wfd(wpa_s, resp, srv_trans_id,
+					pos, tlv_end - pos);
+			break;
+#endif /* CONFIG_WIFI_DISPLAY */
+		case P2P_SERV_P2PS:
+			wpas_sd_req_asp(wpa_s, resp, srv_trans_id,
+					pos, tlv_end - pos);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
+				   "protocol %u", srv_proto);
+			wpas_sd_add_proto_not_avail(resp, srv_proto,
+						    srv_trans_id);
+			break;
+		}
+
+		pos = tlv_end;
+	}
+
+done:
+	wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+				   update_indic, tlvs, tlvs_len);
+
+	wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
+
+	wpabuf_free(resp);
+}
+
+
+static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s,
+				       const u8 *sa, u8 srv_trans_id,
+				       const u8 *pos, const u8 *tlv_end)
+{
+	u8 left = *pos++;
+	u32 adv_id;
+	u8 svc_status;
+	u16 config_methods;
+	char svc_str[256];
+
+	while (left-- && pos < tlv_end) {
+		char *buf = NULL;
+		size_t buf_len;
+		u8 svc_len;
+
+		/* Sanity check fixed length+svc_str */
+		if (pos + 6 >= tlv_end)
+			break;
+		svc_len = pos[6];
+		if (pos + svc_len + 10 > tlv_end)
+			break;
+
+		/* Advertisement ID */
+		adv_id = WPA_GET_LE32(pos);
+		pos += sizeof(u32);
+
+		/* Config Methods */
+		config_methods = WPA_GET_BE16(pos);
+		pos += sizeof(u16);
+
+		/* Service Name */
+		pos++; /* svc_len */
+		os_memcpy(svc_str, pos, svc_len);
+		svc_str[svc_len] = '\0';
+		pos += svc_len;
+
+		/* Service Status */
+		svc_status = *pos++;
+
+		/* Service Information Length */
+		buf_len = WPA_GET_LE16(pos);
+		pos += sizeof(u16);
+
+		/* Sanity check buffer length */
+		if (buf_len > (unsigned int) (tlv_end - pos))
+			break;
+
+		if (buf_len) {
+			buf = os_zalloc(2 * buf_len + 1);
+			if (buf) {
+				utf8_escape((const char *) pos, buf_len, buf,
+					    2 * buf_len + 1);
+			}
+		}
+
+		pos += buf_len;
+
+		if (buf) {
+			wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
+				       MACSTR " %x %x %x %x %s '%s'",
+				       MAC2STR(sa), srv_trans_id, adv_id,
+				       svc_status, config_methods, svc_str,
+				       buf);
+			os_free(buf);
+		} else {
+			wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
+				       MACSTR " %x %x %x %x %s",
+				       MAC2STR(sa), srv_trans_id, adv_id,
+				       svc_status, config_methods, svc_str);
+		}
+	}
+}
+
+
+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;
+	const u8 *end = tlvs + tlvs_len;
+	const u8 *tlv_end;
+	u16 slen;
+	size_t buf_len;
+	char *buf;
+
+	wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
+		    tlvs, tlvs_len);
+	if (tlvs_len > 1500) {
+		/* TODO: better way for handling this */
+		wpa_msg_ctrl(wpa_s, MSG_INFO,
+			     P2P_EVENT_SERV_DISC_RESP MACSTR
+			     " %u <long response: %u bytes>",
+			     MAC2STR(sa), update_indic,
+			     (unsigned int) tlvs_len);
+	} else {
+		buf_len = 2 * tlvs_len + 1;
+		buf = os_malloc(buf_len);
+		if (buf) {
+			wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+			wpa_msg_ctrl(wpa_s, MSG_INFO,
+				     P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
+				     MAC2STR(sa), update_indic, buf);
+			os_free(buf);
+		}
+	}
+
+	while (pos < end) {
+		u8 srv_proto, srv_trans_id, status;
+
+		wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
+		slen = WPA_GET_LE16(pos);
+		pos += 2;
+		if (pos + slen > end || slen < 3) {
+			wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
+				   "length");
+			return;
+		}
+		tlv_end = pos + slen;
+
+		srv_proto = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+			   srv_proto);
+		srv_trans_id = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+			   srv_trans_id);
+		status = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
+			   status);
+
+		wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data",
+			    pos, tlv_end - pos);
+
+		if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) {
+			wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id,
+						   pos, tlv_end);
+		}
+
+		pos = tlv_end;
+	}
+
+	wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
+}
+
+
+u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+			const struct wpabuf *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);
+}
+
+
+u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+			     u8 version, const char *query)
+{
+	struct wpabuf *tlvs;
+	u64 ret;
+
+	tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
+	if (tlvs == NULL)
+		return 0;
+	wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
+	wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
+	wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
+	wpabuf_put_u8(tlvs, version);
+	wpabuf_put_str(tlvs, query);
+	ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+	wpabuf_free(tlvs);
+	return ret;
+}
+
+
+u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
+			    const char *svc_str, const char *info_substr)
+{
+	struct wpabuf *tlvs;
+	size_t plen, svc_len, substr_len = 0;
+	u64 ret;
+
+	svc_len = os_strlen(svc_str);
+	if (info_substr)
+		substr_len = os_strlen(info_substr);
+
+	if (svc_len > 0xff || substr_len > 0xff)
+		return 0;
+
+	plen = 1 + 1 + 1 + svc_len + 1 + substr_len;
+	tlvs = wpabuf_alloc(2 + plen);
+	if (tlvs == NULL)
+		return 0;
+
+	wpabuf_put_le16(tlvs, plen);
+	wpabuf_put_u8(tlvs, P2P_SERV_P2PS);
+	wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
+	wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */
+	wpabuf_put_data(tlvs, svc_str, svc_len);
+	wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */
+	wpabuf_put_data(tlvs, info_substr, substr_len);
+	ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+	wpabuf_free(tlvs);
+
+	return ret;
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst,
+				   const struct wpabuf *tlvs)
+{
+	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);
+}
+
+
+#define MAX_WFD_SD_SUBELEMS 20
+
+static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role,
+				const char *subelems)
+{
+	u8 *len;
+	const char *pos;
+	int val;
+	int count = 0;
+
+	len = wpabuf_put(tlvs, 2);
+	wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */
+	wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
+
+	wpabuf_put_u8(tlvs, role);
+
+	pos = subelems;
+	while (*pos) {
+		val = atoi(pos);
+		if (val >= 0 && val < 256) {
+			wpabuf_put_u8(tlvs, val);
+			count++;
+			if (count == MAX_WFD_SD_SUBELEMS)
+				break;
+		}
+		pos = os_strchr(pos + 1, ',');
+		if (pos == NULL)
+			break;
+		pos++;
+	}
+
+	WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2);
+}
+
+
+u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
+				     const u8 *dst, const char *role)
+{
+	struct wpabuf *tlvs;
+	u64 ret;
+	const char *subelems;
+	u8 id = 1;
+
+	subelems = os_strchr(role, ' ');
+	if (subelems == NULL)
+		return 0;
+	subelems++;
+
+	tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS));
+	if (tlvs == NULL)
+		return 0;
+
+	if (os_strstr(role, "[source]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x00, subelems);
+	if (os_strstr(role, "[pri-sink]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x01, subelems);
+	if (os_strstr(role, "[sec-sink]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x02, subelems);
+	if (os_strstr(role, "[source+sink]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x03, subelems);
+
+	ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs);
+	wpabuf_free(tlvs);
+	return ret;
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+	return p2p_sd_cancel_request(wpa_s->global->p2p,
+				     (void *) (uintptr_t) req);
+}
+
+
+void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
+			  const u8 *dst, u8 dialog_token,
+			  const struct wpabuf *resp_tlvs)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return;
+	p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
+			resp_tlvs);
+}
+
+
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->global->p2p)
+		p2p_sd_service_update(wpa_s->global->p2p);
+}
+
+
+static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
+{
+	dl_list_del(&bsrv->list);
+	wpabuf_free(bsrv->query);
+	wpabuf_free(bsrv->resp);
+	os_free(bsrv);
+}
+
+
+static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
+{
+	dl_list_del(&usrv->list);
+	os_free(usrv->service);
+	os_free(usrv);
+}
+
+
+void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
+{
+	struct p2p_srv_bonjour *bsrv, *bn;
+	struct p2p_srv_upnp *usrv, *un;
+
+	dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
+			      struct p2p_srv_bonjour, list)
+		wpas_p2p_srv_bonjour_free(bsrv);
+
+	dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
+			      struct p2p_srv_upnp, list)
+		wpas_p2p_srv_upnp_free(usrv);
+
+	wpas_p2p_service_flush_asp(wpa_s);
+	wpas_p2p_sd_service_update(wpa_s);
+}
+
+
+int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id)
+{
+	if (adv_id == 0)
+		return 1;
+
+	if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id))
+		return 1;
+
+	return 0;
+}
+
+
+int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id)
+{
+	int ret;
+
+	ret = p2p_service_del_asp(wpa_s->global->p2p, adv_id);
+	if (ret == 0)
+		wpas_p2p_sd_service_update(wpa_s);
+	return ret;
+}
+
+
+int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s,
+			     int auto_accept, u32 adv_id,
+			     const char *adv_str, u8 svc_state,
+			     u16 config_methods, const char *svc_info,
+			     const u8 *cpt_priority)
+{
+	int ret;
+
+	ret = p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id,
+				  adv_str, svc_state, config_methods,
+				  svc_info, cpt_priority);
+	if (ret == 0)
+		wpas_p2p_sd_service_update(wpa_s);
+	return ret;
+}
+
+
+void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s)
+{
+	p2p_service_flush_asp(wpa_s->global->p2p);
+}
+
+
+int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
+				 struct wpabuf *query, struct wpabuf *resp)
+{
+	struct p2p_srv_bonjour *bsrv;
+
+	bsrv = os_zalloc(sizeof(*bsrv));
+	if (bsrv == NULL)
+		return -1;
+	bsrv->query = query;
+	bsrv->resp = resp;
+	dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
+
+	wpas_p2p_sd_service_update(wpa_s);
+	return 0;
+}
+
+
+int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
+				 const struct wpabuf *query)
+{
+	struct p2p_srv_bonjour *bsrv;
+
+	bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
+	if (bsrv == NULL)
+		return -1;
+	wpas_p2p_srv_bonjour_free(bsrv);
+	wpas_p2p_sd_service_update(wpa_s);
+	return 0;
+}
+
+
+int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
+			      const char *service)
+{
+	struct p2p_srv_upnp *usrv;
+
+	if (wpas_p2p_service_get_upnp(wpa_s, version, service))
+		return 0; /* Already listed */
+	usrv = os_zalloc(sizeof(*usrv));
+	if (usrv == NULL)
+		return -1;
+	usrv->version = version;
+	usrv->service = os_strdup(service);
+	if (usrv->service == NULL) {
+		os_free(usrv);
+		return -1;
+	}
+	dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
+
+	wpas_p2p_sd_service_update(wpa_s);
+	return 0;
+}
+
+
+int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
+			      const char *service)
+{
+	struct p2p_srv_upnp *usrv;
+
+	usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
+	if (usrv == NULL)
+		return -1;
+	wpas_p2p_srv_upnp_free(usrv);
+	wpas_p2p_sd_service_update(wpa_s);
+	return 0;
+}
diff --git a/hostap/wpa_supplicant/preauth_test.c b/hostap/wpa_supplicant/preauth_test.c
new file mode 100644
index 0000000..f4bba98
--- /dev/null
+++ b/hostap/wpa_supplicant/preauth_test.c
@@ -0,0 +1,360 @@
+/*
+ * WPA Supplicant - test code for pre-authentication
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c.
+ * Not used in production version.
+ */
+
+#include "includes.h"
+#include <assert.h>
+
+#include "common.h"
+#include "config.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "eloop.h"
+#include "rsn_supp/wpa.h"
+#include "eap_peer/eap.h"
+#include "wpa_supplicant_i.h"
+#include "l2_packet/l2_packet.h"
+#include "ctrl_iface.h"
+#include "pcsc_funcs.h"
+#include "rsn_supp/preauth.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "drivers/driver.h"
+
+
+const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
+
+
+struct preauth_test_data {
+	int auth_timed_out;
+};
+
+
+static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code)
+{
+	wpa_supplicant_deauthenticate(wpa_s, reason_code);
+}
+
+
+static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type,
+			    const void *data, u16 data_len,
+			    size_t *msg_len, void **data_pos)
+{
+	struct ieee802_1x_hdr *hdr;
+
+	*msg_len = sizeof(*hdr) + data_len;
+	hdr = os_malloc(*msg_len);
+	if (hdr == NULL)
+		return NULL;
+
+	hdr->version = wpa_s->conf->eapol_version;
+	hdr->type = type;
+	hdr->length = htons(data_len);
+
+	if (data)
+		os_memcpy(hdr + 1, data, data_len);
+	else
+		os_memset(hdr + 1, 0, data_len);
+
+	if (data_pos)
+		*data_pos = hdr + 1;
+
+	return (u8 *) hdr;
+}
+
+
+static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type,
+			     const void *data, u16 data_len,
+			     size_t *msg_len, void **data_pos)
+{
+	return wpa_alloc_eapol(wpa_s, type, data, data_len, msg_len, data_pos);
+}
+
+
+static void _wpa_supplicant_set_state(void *ctx, enum wpa_states state)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	wpa_s->wpa_state = state;
+}
+
+
+static enum wpa_states _wpa_supplicant_get_state(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	return wpa_s->wpa_state;
+}
+
+
+static int wpa_ether_send(void *wpa_s, const u8 *dest, u16 proto,
+			  const u8 *buf, size_t len)
+{
+	printf("%s - not implemented\n", __func__);
+	return -1;
+}
+
+
+static void * wpa_supplicant_get_network_ctx(void *wpa_s)
+{
+	return wpa_supplicant_get_ssid(wpa_s);
+}
+
+
+static void _wpa_supplicant_cancel_auth_timeout(void *wpa_s)
+{
+	wpa_supplicant_cancel_auth_timeout(wpa_s);
+}
+
+
+static int wpa_supplicant_get_beacon_ie(void *wpa_s)
+{
+	printf("%s - not implemented\n", __func__);
+	return -1;
+}
+
+
+static int wpa_supplicant_get_bssid(void *wpa_s, u8 *bssid)
+{
+	printf("%s - not implemented\n", __func__);
+	return -1;
+}
+
+
+static int wpa_supplicant_set_key(void *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)
+{
+	printf("%s - not implemented\n", __func__);
+	return -1;
+}
+
+
+static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr,
+					     int protection_type,
+					     int key_type)
+{
+	printf("%s - not implemented\n", __func__);
+	return -1;
+}
+
+
+static int wpa_supplicant_add_pmkid(void *wpa_s,
+				    const u8 *bssid, const u8 *pmkid)
+{
+	printf("%s - not implemented\n", __func__);
+	return -1;
+}
+
+
+static int wpa_supplicant_remove_pmkid(void *wpa_s,
+				       const u8 *bssid, const u8 *pmkid)
+{
+	printf("%s - not implemented\n", __func__);
+	return -1;
+}
+
+
+static void wpa_supplicant_set_config_blob(void *ctx,
+					   struct wpa_config_blob *blob)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	wpa_config_set_blob(wpa_s->conf, blob);
+}
+
+
+static const struct wpa_config_blob *
+wpa_supplicant_get_config_blob(void *ctx, const char *name)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	return wpa_config_get_blob(wpa_s->conf, name);
+}
+
+
+static void test_eapol_clean(struct wpa_supplicant *wpa_s)
+{
+	rsn_preauth_deinit(wpa_s->wpa);
+	pmksa_candidate_free(wpa_s->wpa);
+	wpa_sm_deinit(wpa_s->wpa);
+	scard_deinit(wpa_s->scard);
+	if (wpa_s->ctrl_iface) {
+		wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
+		wpa_s->ctrl_iface = NULL;
+	}
+	wpa_config_free(wpa_s->conf);
+}
+
+
+static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct preauth_test_data *p = eloop_ctx;
+	printf("EAPOL test timed out\n");
+	p->auth_timed_out = 1;
+	eloop_terminate();
+}
+
+
+static void eapol_test_poll(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	if (!rsn_preauth_in_progress(wpa_s->wpa))
+		eloop_terminate();
+	else {
+		eloop_register_timeout(0, 100000, eapol_test_poll, eloop_ctx,
+				       timeout_ctx);
+	}
+}
+
+
+static struct wpa_driver_ops dummy_driver;
+
+
+static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *ifname)
+{
+	struct l2_packet_data *l2;
+	struct wpa_sm_ctx *ctx;
+
+	os_memset(&dummy_driver, 0, sizeof(dummy_driver));
+	wpa_s->driver = &dummy_driver;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	assert(ctx != NULL);
+
+	ctx->ctx = wpa_s;
+	ctx->msg_ctx = wpa_s;
+	ctx->set_state = _wpa_supplicant_set_state;
+	ctx->get_state = _wpa_supplicant_get_state;
+	ctx->deauthenticate = _wpa_supplicant_deauthenticate;
+	ctx->set_key = wpa_supplicant_set_key;
+	ctx->get_network_ctx = wpa_supplicant_get_network_ctx;
+	ctx->get_bssid = wpa_supplicant_get_bssid;
+	ctx->ether_send = wpa_ether_send;
+	ctx->get_beacon_ie = wpa_supplicant_get_beacon_ie;
+	ctx->alloc_eapol = _wpa_alloc_eapol;
+	ctx->cancel_auth_timeout = _wpa_supplicant_cancel_auth_timeout;
+	ctx->add_pmkid = wpa_supplicant_add_pmkid;
+	ctx->remove_pmkid = wpa_supplicant_remove_pmkid;
+	ctx->set_config_blob = wpa_supplicant_set_config_blob;
+	ctx->get_config_blob = wpa_supplicant_get_config_blob;
+	ctx->mlme_setprotection = wpa_supplicant_mlme_setprotection;
+
+	wpa_s->wpa = wpa_sm_init(ctx);
+	assert(wpa_s->wpa != NULL);
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, WPA_PROTO_RSN);
+
+	os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
+	wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname, NULL);
+
+	l2 = l2_packet_init(wpa_s->ifname, NULL, ETH_P_RSN_PREAUTH, NULL,
+			    NULL, 0);
+	assert(l2 != NULL);
+	if (l2_packet_get_own_addr(l2, wpa_s->own_addr)) {
+		wpa_printf(MSG_WARNING, "Failed to get own L2 address\n");
+		exit(-1);
+	}
+	l2_packet_deinit(l2);
+	wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
+}
+
+
+static void eapol_test_terminate(int sig, void *signal_ctx)
+{
+	struct wpa_supplicant *wpa_s = signal_ctx;
+	wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig);
+	eloop_terminate();
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct wpa_supplicant wpa_s;
+	int ret = 1;
+	u8 bssid[ETH_ALEN];
+	struct preauth_test_data preauth_test;
+
+	if (os_program_init())
+		return -1;
+
+	os_memset(&preauth_test, 0, sizeof(preauth_test));
+
+	wpa_debug_level = 0;
+	wpa_debug_show_keys = 1;
+
+	if (argc != 4) {
+		printf("usage: preauth_test <conf> <target MAC address> "
+		       "<ifname>\n");
+		return -1;
+	}
+
+	if (hwaddr_aton(argv[2], bssid)) {
+		printf("Failed to parse target address '%s'.\n", argv[2]);
+		return -1;
+	}
+
+	if (eap_register_methods()) {
+		wpa_printf(MSG_ERROR, "Failed to register EAP methods");
+		return -1;
+	}
+
+	if (eloop_init()) {
+		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+		return -1;
+	}
+
+	os_memset(&wpa_s, 0, sizeof(wpa_s));
+	wpa_s.conf = wpa_config_read(argv[1], NULL);
+	if (wpa_s.conf == NULL) {
+		printf("Failed to parse configuration file '%s'.\n", argv[1]);
+		return -1;
+	}
+	if (wpa_s.conf->ssid == NULL) {
+		printf("No networks defined.\n");
+		return -1;
+	}
+
+	wpa_init_conf(&wpa_s, argv[3]);
+	wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
+	if (wpa_s.ctrl_iface == NULL) {
+		printf("Failed to initialize control interface '%s'.\n"
+		       "You may have another preauth_test process already "
+		       "running or the file was\n"
+		       "left by an unclean termination of preauth_test in "
+		       "which case you will need\n"
+		       "to manually remove this file before starting "
+		       "preauth_test again.\n",
+		       wpa_s.conf->ctrl_interface);
+		return -1;
+	}
+	if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
+		return -1;
+
+	if (rsn_preauth_init(wpa_s.wpa, bssid, &wpa_s.conf->ssid->eap))
+		return -1;
+
+	eloop_register_timeout(30, 0, eapol_test_timeout, &preauth_test, NULL);
+	eloop_register_timeout(0, 100000, eapol_test_poll, &wpa_s, NULL);
+	eloop_register_signal_terminate(eapol_test_terminate, &wpa_s);
+	eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s);
+	eloop_run();
+
+	if (preauth_test.auth_timed_out)
+		ret = -2;
+	else {
+		ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0)
+			? 0 : -3;
+	}
+
+	test_eapol_clean(&wpa_s);
+
+	eap_peer_unregister_methods();
+
+	eloop_destroy();
+
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/hostap/wpa_supplicant/scan.c b/hostap/wpa_supplicant/scan.c
new file mode 100644
index 0000000..6c07ade
--- /dev/null
+++ b/hostap/wpa_supplicant/scan.c
@@ -0,0 +1,2488 @@
+/*
+ * WPA Supplicant - Scanning
+ * 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.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "wps_supplicant.h"
+#include "p2p_supplicant.h"
+#include "p2p/p2p.h"
+#include "hs20_supplicant.h"
+#include "notify.h"
+#include "bss.h"
+#include "scan.h"
+#include "mesh.h"
+
+
+static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *ssid;
+	union wpa_event_data data;
+
+	ssid = wpa_supplicant_get_ssid(wpa_s);
+	if (ssid == NULL)
+		return;
+
+	if (wpa_s->current_ssid == NULL) {
+		wpa_s->current_ssid = ssid;
+		if (wpa_s->current_ssid != NULL)
+			wpas_notify_network_changed(wpa_s);
+	}
+	wpa_supplicant_initiate_eapol(wpa_s);
+	wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured "
+		"network - generating associated event");
+	os_memset(&data, 0, sizeof(data));
+	wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
+}
+
+
+#ifdef CONFIG_WPS
+static int wpas_wps_in_use(struct wpa_supplicant *wpa_s,
+			   enum wps_request_type *req_type)
+{
+	struct wpa_ssid *ssid;
+	int wps = 0;
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
+			continue;
+
+		wps = 1;
+		*req_type = wpas_wps_get_req_type(ssid);
+		if (!ssid->eap.phase1)
+			continue;
+
+		if (os_strstr(ssid->eap.phase1, "pbc=1"))
+			return 2;
+	}
+
+#ifdef CONFIG_P2P
+	if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p &&
+	    !wpa_s->conf->p2p_disabled) {
+		wpa_s->wps->dev.p2p = 1;
+		if (!wps) {
+			wps = 1;
+			*req_type = WPS_REQ_ENROLLEE_INFO;
+		}
+	}
+#endif /* CONFIG_P2P */
+
+	return wps;
+}
+#endif /* CONFIG_WPS */
+
+
+/**
+ * wpa_supplicant_enabled_networks - Check whether there are enabled networks
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 if no networks are enabled, >0 if networks are enabled
+ *
+ * This function is used to figure out whether any networks (or Interworking
+ * with enabled credentials and auto_interworking) are present in the current
+ * configuration.
+ */
+int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *ssid = wpa_s->conf->ssid;
+	int count = 0, disabled = 0;
+
+	if (wpa_s->p2p_mgmt)
+		return 0; /* no normal network profiles on p2p_mgmt interface */
+
+	while (ssid) {
+		if (!wpas_network_disabled(wpa_s, ssid))
+			count++;
+		else
+			disabled++;
+		ssid = ssid->next;
+	}
+	if (wpa_s->conf->cred && wpa_s->conf->interworking &&
+	    wpa_s->conf->auto_interworking)
+		count++;
+	if (count == 0 && disabled > 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks (%d disabled "
+			"networks)", disabled);
+	}
+	return count;
+}
+
+
+static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
+				     struct wpa_ssid *ssid)
+{
+	while (ssid) {
+		if (!wpas_network_disabled(wpa_s, ssid))
+			break;
+		ssid = ssid->next;
+	}
+
+	/* ap_scan=2 mode - try to associate with each SSID. */
+	if (ssid == NULL) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached "
+			"end of scan list - go back to beginning");
+		wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+		return;
+	}
+	if (ssid->next) {
+		/* Continue from the next SSID on the next attempt. */
+		wpa_s->prev_scan_ssid = ssid;
+	} else {
+		/* Start from the beginning of the SSID list. */
+		wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
+	}
+	wpa_supplicant_associate(wpa_s, NULL, ssid);
+}
+
+
+static void wpas_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_supplicant_notify_scanning(wpa_s, 0);
+		wpas_notify_scan_done(wpa_s, 0);
+		wpa_s->scan_work = NULL;
+		return;
+	}
+
+	if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"Failed to assign random MAC address for a scan");
+		radio_work_done(work);
+		return;
+	}
+
+	wpa_supplicant_notify_scanning(wpa_s, 1);
+
+	if (wpa_s->clear_driver_scan_cache) {
+		wpa_printf(MSG_DEBUG,
+			   "Request driver to clear scan cache due to local BSS flush");
+		params->only_new_results = 1;
+	}
+	ret = wpa_drv_scan(wpa_s, params);
+	wpa_scan_free_params(params);
+	work->ctx = NULL;
+	if (ret) {
+		int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ;
+
+		if (wpa_s->disconnected)
+			retry = 0;
+
+		wpa_supplicant_notify_scanning(wpa_s, 0);
+		wpas_notify_scan_done(wpa_s, 0);
+		if (wpa_s->wpa_state == WPA_SCANNING)
+			wpa_supplicant_set_state(wpa_s,
+						 wpa_s->scan_prev_wpa_state);
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=%d%s",
+			ret, retry ? " retry=1" : "");
+		radio_work_done(work);
+
+		if (retry) {
+			/* Restore scan_req since we will try to scan again */
+			wpa_s->scan_req = wpa_s->last_scan_req;
+			wpa_supplicant_req_scan(wpa_s, 1, 0);
+		}
+		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;
+}
+
+
+/**
+ * wpa_supplicant_trigger_scan - Request driver to start a scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
+				struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_scan_params *ctx;
+
+	if (wpa_s->scan_work) {
+		wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending");
+		return -1;
+	}
+
+	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;
+}
+
+
+static void
+wpa_supplicant_delayed_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Starting delayed sched scan");
+
+	if (wpa_supplicant_req_sched_scan(wpa_s))
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void
+wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Sched scan timeout - stopping it");
+
+	wpa_s->sched_scan_timed_out = 1;
+	wpa_supplicant_cancel_sched_scan(wpa_s);
+}
+
+
+int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
+				    struct wpa_driver_scan_params *params,
+				    int interval)
+{
+	int ret;
+
+	wpa_supplicant_notify_scanning(wpa_s, 1);
+	ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000);
+	if (ret)
+		wpa_supplicant_notify_scanning(wpa_s, 0);
+	else
+		wpa_s->sched_scanning = 1;
+
+	return ret;
+}
+
+
+int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
+{
+	int ret;
+
+	ret = wpa_drv_stop_sched_scan(wpa_s);
+	if (ret) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "stopping sched_scan failed!");
+		/* TODO: what to do if stopping fails? */
+		return -1;
+	}
+
+	return ret;
+}
+
+
+static struct wpa_driver_scan_filter *
+wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
+{
+	struct wpa_driver_scan_filter *ssids;
+	struct wpa_ssid *ssid;
+	size_t count;
+
+	*num_ssids = 0;
+	if (!conf->filter_ssids)
+		return NULL;
+
+	for (count = 0, ssid = conf->ssid; ssid; ssid = ssid->next) {
+		if (ssid->ssid && ssid->ssid_len)
+			count++;
+	}
+	if (count == 0)
+		return NULL;
+	ssids = os_calloc(count, sizeof(struct wpa_driver_scan_filter));
+	if (ssids == NULL)
+		return NULL;
+
+	for (ssid = conf->ssid; ssid; ssid = ssid->next) {
+		if (!ssid->ssid || !ssid->ssid_len)
+			continue;
+		os_memcpy(ssids[*num_ssids].ssid, ssid->ssid, ssid->ssid_len);
+		ssids[*num_ssids].ssid_len = ssid->ssid_len;
+		(*num_ssids)++;
+	}
+
+	return ssids;
+}
+
+
+static void wpa_supplicant_optimize_freqs(
+	struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params)
+{
+#ifdef CONFIG_P2P
+	if (params->freqs == NULL && wpa_s->p2p_in_provisioning &&
+	    wpa_s->go_params) {
+		/* Optimize provisioning state scan based on GO information */
+		if (wpa_s->p2p_in_provisioning < 5 &&
+		    wpa_s->go_params->freq > 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO "
+				"preferred frequency %d MHz",
+				wpa_s->go_params->freq);
+			params->freqs = os_calloc(2, sizeof(int));
+			if (params->freqs)
+				params->freqs[0] = wpa_s->go_params->freq;
+		} else if (wpa_s->p2p_in_provisioning < 8 &&
+			   wpa_s->go_params->freq_list[0]) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only common "
+				"channels");
+			int_array_concat(&params->freqs,
+					 wpa_s->go_params->freq_list);
+			if (params->freqs)
+				int_array_sort_unique(params->freqs);
+		}
+		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_calloc(2, sizeof(int));
+			if (params->freqs)
+				params->freqs[0] = wpa_s->p2p_invite_go_freq;
+		}
+		wpa_s->p2p_in_invitation++;
+		if (wpa_s->p2p_in_invitation > 20) {
+			/*
+			 * 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
+	if (params->freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) {
+		/*
+		 * Optimize post-provisioning scan based on channel used
+		 * during provisioning.
+		 */
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz "
+			"that was used during provisioning", wpa_s->wps_freq);
+		params->freqs = os_calloc(2, sizeof(int));
+		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)
+	{
+		/* Optimize provisioning scan based on already known channel */
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz",
+			wpa_s->wps_freq);
+		params->freqs = os_calloc(2, sizeof(int));
+		if (params->freqs)
+			params->freqs[0] = wpa_s->wps_freq;
+		wpa_s->known_wps_freq = 0; /* only do this once */
+	}
+#endif /* CONFIG_WPS */
+}
+
+
+#ifdef CONFIG_INTERWORKING
+static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s,
+					   struct wpabuf *buf)
+{
+	wpabuf_put_u8(buf, WLAN_EID_INTERWORKING);
+	wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 :
+		      1 + ETH_ALEN);
+	wpabuf_put_u8(buf, wpa_s->conf->access_network_type);
+	/* No Venue Info */
+	if (!is_zero_ether_addr(wpa_s->conf->hessid))
+		wpabuf_put_data(buf, wpa_s->conf->hessid, ETH_ALEN);
+}
+#endif /* CONFIG_INTERWORKING */
+
+
+static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
+{
+	struct wpabuf *extra_ie = NULL;
+	u8 ext_capab[18];
+	int ext_capab_len;
+#ifdef CONFIG_WPS
+	int wps = 0;
+	enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
+#endif /* CONFIG_WPS */
+
+	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+					     sizeof(ext_capab));
+	if (ext_capab_len > 0 &&
+	    wpabuf_resize(&extra_ie, ext_capab_len) == 0)
+		wpabuf_put_data(extra_ie, ext_capab, ext_capab_len);
+
+#ifdef CONFIG_INTERWORKING
+	if (wpa_s->conf->interworking &&
+	    wpabuf_resize(&extra_ie, 100) == 0)
+		wpas_add_interworking_elements(wpa_s, extra_ie);
+#endif /* CONFIG_INTERWORKING */
+
+#ifdef CONFIG_WPS
+	wps = wpas_wps_in_use(wpa_s, &req_type);
+
+	if (wps) {
+		struct wpabuf *wps_ie;
+		wps_ie = wps_build_probe_req_ie(wps == 2 ? DEV_PW_PUSHBUTTON :
+						DEV_PW_DEFAULT,
+						&wpa_s->wps->dev,
+						wpa_s->wps->uuid, req_type,
+						0, NULL);
+		if (wps_ie) {
+			if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0)
+				wpabuf_put_buf(extra_ie, wps_ie);
+			wpabuf_free(wps_ie);
+		}
+	}
+
+#ifdef CONFIG_P2P
+	if (wps) {
+		size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
+		if (wpabuf_resize(&extra_ie, ielen) == 0)
+			wpas_p2p_scan_ie(wpa_s, extra_ie);
+	}
+#endif /* CONFIG_P2P */
+
+	wpa_supplicant_mesh_add_scan_ie(wpa_s, &extra_ie);
+
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_HS20
+	if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 7) == 0)
+		wpas_hs20_add_indication(extra_ie, -1);
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_FST
+	if (wpa_s->fst_ies &&
+	    wpabuf_resize(&extra_ie, wpabuf_len(wpa_s->fst_ies)) == 0)
+		wpabuf_put_buf(extra_ie, wpa_s->fst_ies);
+#endif /* CONFIG_FST */
+
+	return extra_ie;
+}
+
+
+#ifdef CONFIG_P2P
+
+/*
+ * Check whether there are any enabled networks or credentials that could be
+ * used for a non-P2P connection.
+ */
+static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *ssid;
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (wpas_network_disabled(wpa_s, ssid))
+			continue;
+		if (!ssid->p2p_group)
+			return 1;
+	}
+
+	if (wpa_s->conf->cred && wpa_s->conf->interworking &&
+	    wpa_s->conf->auto_interworking)
+		return 1;
+
+	return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+
+static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
+					  u16 num_modes,
+					  enum hostapd_hw_mode mode)
+{
+	u16 i;
+
+	for (i = 0; i < num_modes; i++) {
+		if (modes[i].mode == mode)
+			return &modes[i];
+	}
+
+	return NULL;
+}
+
+
+static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s,
+					enum hostapd_hw_mode band,
+					struct wpa_driver_scan_params *params)
+{
+	/* Include only supported channels for the specified band */
+	struct hostapd_hw_modes *mode;
+	int count, i;
+
+	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band);
+	if (mode == NULL) {
+		/* No channels supported in this band - use empty list */
+		params->freqs = os_zalloc(sizeof(int));
+		return;
+	}
+
+	params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
+	if (params->freqs == NULL)
+		return;
+	for (count = 0, i = 0; i < mode->num_channels; i++) {
+		if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+		params->freqs[count++] = mode->channels[i].freq;
+	}
+}
+
+
+static void wpa_setband_scan_freqs(struct wpa_supplicant *wpa_s,
+				   struct wpa_driver_scan_params *params)
+{
+	if (wpa_s->hw.modes == NULL)
+		return; /* unknown what channels the driver supports */
+	if (params->freqs)
+		return; /* already using a limited channel set */
+	if (wpa_s->setband == WPA_SETBAND_5G)
+		wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A,
+					    params);
+	else if (wpa_s->setband == WPA_SETBAND_2G)
+		wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G,
+					    params);
+}
+
+
+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 int wpa_set_ssids_from_scan_req(struct wpa_supplicant *wpa_s,
+				       struct wpa_driver_scan_params *params,
+				       size_t max_ssids)
+{
+	unsigned int i;
+
+	if (wpa_s->ssids_from_scan_req == NULL ||
+	    wpa_s->num_ssids_from_scan_req == 0)
+		return 0;
+
+	if (wpa_s->num_ssids_from_scan_req > max_ssids) {
+		wpa_s->num_ssids_from_scan_req = max_ssids;
+		wpa_printf(MSG_DEBUG, "Over max scan SSIDs from scan req: %u",
+			   (unsigned int) max_ssids);
+	}
+
+	for (i = 0; i < wpa_s->num_ssids_from_scan_req; i++) {
+		params->ssids[i].ssid = wpa_s->ssids_from_scan_req[i].ssid;
+		params->ssids[i].ssid_len =
+			wpa_s->ssids_from_scan_req[i].ssid_len;
+		wpa_hexdump_ascii(MSG_DEBUG, "specific SSID",
+				  params->ssids[i].ssid,
+				  params->ssids[i].ssid_len);
+	}
+
+	params->num_ssids = wpa_s->num_ssids_from_scan_req;
+	wpa_s->num_ssids_from_scan_req = 0;
+	return 1;
+}
+
+
+static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct wpa_ssid *ssid;
+	int ret, p2p_in_prog;
+	struct wpabuf *extra_ie = NULL;
+	struct wpa_driver_scan_params params;
+	struct wpa_driver_scan_params *scan_params;
+	size_t max_ssids;
+	int connect_without_scan = 0;
+
+	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");
+		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);
+		return;
+	}
+
+	if (wpa_s->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;
+	}
+
+	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);
+		return;
+	}
+
+	if (wpa_s->conf->ap_scan != 0 &&
+	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Using wired authentication - "
+			"overriding ap_scan configuration");
+		wpa_s->conf->ap_scan = 0;
+		wpas_notify_ap_scan_changed(wpa_s);
+	}
+
+	if (wpa_s->conf->ap_scan == 0) {
+		wpa_supplicant_gen_assoc_event(wpa_s);
+		return;
+	}
+
+	ssid = NULL;
+	if (wpa_s->scan_req != MANUAL_SCAN_REQ &&
+	    wpa_s->connect_without_scan) {
+		connect_without_scan = 1;
+		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+			if (ssid == wpa_s->connect_without_scan)
+				break;
+		}
+	}
+
+	p2p_in_prog = wpas_p2p_in_progress(wpa_s);
+	if (p2p_in_prog && p2p_in_prog != 2 &&
+	    (!ssid ||
+	     (ssid->mode != WPAS_MODE_AP && ssid->mode != WPAS_MODE_P2P_GO))) {
+		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;
+	}
+
+	if (wpa_s->conf->ap_scan == 2)
+		max_ssids = 1;
+	else {
+		max_ssids = wpa_s->max_scan_ssids;
+		if (max_ssids > WPAS_MAX_SCAN_SSIDS)
+			max_ssids = WPAS_MAX_SCAN_SSIDS;
+	}
+
+	wpa_s->last_scan_req = wpa_s->scan_req;
+	wpa_s->scan_req = NORMAL_SCAN_REQ;
+
+	if (connect_without_scan) {
+		wpa_s->connect_without_scan = NULL;
+		if (ssid) {
+			wpa_printf(MSG_DEBUG, "Start a pre-selected network "
+				   "without scan step");
+			wpa_supplicant_associate(wpa_s, NULL, ssid);
+			return;
+		}
+	}
+
+	os_memset(&params, 0, sizeof(params));
+
+	wpa_s->scan_prev_wpa_state = wpa_s->wpa_state;
+	if (wpa_s->wpa_state == WPA_DISCONNECTED ||
+	    wpa_s->wpa_state == WPA_INACTIVE)
+		wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
+
+	/*
+	 * If autoscan has set its own scanning parameters
+	 */
+	if (wpa_s->autoscan_params != NULL) {
+		scan_params = wpa_s->autoscan_params;
+		goto scan;
+	}
+
+	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+	    wpa_set_ssids_from_scan_req(wpa_s, &params, max_ssids)) {
+		wpa_printf(MSG_DEBUG, "Use specific SSIDs from SCAN command");
+		goto ssid_list_set;
+	}
+
+#ifdef CONFIG_P2P
+	if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) &&
+	    wpa_s->go_params && !wpa_s->conf->passive_scan) {
+		wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during P2P group formation (p2p_in_provisioning=%d show_group_started=%d)",
+			   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 */
+	ssid = wpa_s->conf->ssid;
+	if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) {
+		while (ssid) {
+			if (ssid == wpa_s->prev_scan_ssid) {
+				ssid = ssid->next;
+				break;
+			}
+			ssid = ssid->next;
+		}
+	}
+
+	if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
+#ifdef CONFIG_AP
+	    !wpa_s->ap_iface &&
+#endif /* CONFIG_AP */
+	    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;
+	} else if (wpa_s->conf->ap_scan == 2) {
+		/*
+		 * User-initiated scan request in ap_scan == 2; scan with
+		 * wildcard SSID.
+		 */
+		ssid = NULL;
+	} 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;
+		if (ssid == NULL && max_ssids > 1)
+			ssid = wpa_s->conf->ssid;
+		while (ssid) {
+			if (!wpas_network_disabled(wpa_s, ssid) &&
+			    ssid->scan_ssid) {
+				wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
+						  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++;
+				if (params.num_ssids + 1 >= max_ssids)
+					break;
+			}
+			ssid = ssid->next;
+			if (ssid == start)
+				break;
+			if (ssid == NULL && max_ssids > 1 &&
+			    start != wpa_s->conf->ssid)
+				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;
+		     wpa_s->last_scan_req != MANUAL_SCAN_REQ && tssid;
+		     tssid = tssid->next) {
+			if (wpas_network_disabled(wpa_s, tssid))
+				continue;
+			if ((params.freqs || !freqs_set) && tssid->scan_freq) {
+				int_array_concat(&params.freqs,
+						 tssid->scan_freq);
+			} else {
+				os_free(params.freqs);
+				params.freqs = NULL;
+			}
+			freqs_set = 1;
+		}
+		int_array_sort_unique(params.freqs);
+	}
+
+	if (ssid && max_ssids == 1) {
+		/*
+		 * If the driver is limited to 1 SSID at a time interleave
+		 * wildcard SSID scans with specific SSID scans to avoid
+		 * waiting a long time for a wildcard scan.
+		 */
+		if (!wpa_s->prev_scan_wildcard) {
+			params.ssids[0].ssid = NULL;
+			params.ssids[0].ssid_len = 0;
+			wpa_s->prev_scan_wildcard = 1;
+			wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for "
+				"wildcard SSID (Interleave with specific)");
+		} else {
+			wpa_s->prev_scan_ssid = ssid;
+			wpa_s->prev_scan_wildcard = 0;
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Starting AP scan for specific SSID: %s",
+				wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+		}
+	} else if (ssid) {
+		/* max_ssids > 1 */
+
+		wpa_s->prev_scan_ssid = ssid;
+		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 if (wpa_s->conf->passive_scan) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Use passive scan based on configuration");
+	} else {
+		wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
+		params.num_ssids++;
+		wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard "
+			"SSID");
+	}
+
+ssid_list_set:
+	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) {
+		wpa_printf(MSG_DEBUG,
+			   "Request driver to clear scan cache due to manual only_new=1 scan");
+		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");
+		params.freqs = wpa_s->next_scan_freqs;
+	} else
+		os_free(wpa_s->next_scan_freqs);
+	wpa_s->next_scan_freqs = NULL;
+	wpa_setband_scan_freqs(wpa_s, &params);
+
+	/* See if user specified frequencies. If so, scan only those. */
+	if (wpa_s->conf->freq_list && !params.freqs) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Optimize scan based on conf->freq_list");
+		int_array_concat(&params.freqs, wpa_s->conf->freq_list);
+	}
+
+	/* Use current associated channel? */
+	if (wpa_s->conf->scan_cur_freq && !params.freqs) {
+		unsigned int num = wpa_s->num_multichan_concurrent;
+
+		params.freqs = os_calloc(num + 1, sizeof(int));
+		if (params.freqs) {
+			num = get_shared_radio_freqs(wpa_s, params.freqs, num);
+			if (num > 0) {
+				wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the "
+					"current operating channels since "
+					"scan_cur_freq is enabled");
+			} else {
+				os_free(params.freqs);
+				params.freqs = NULL;
+			}
+		}
+	}
+
+	params.filter_ssids = wpa_supplicant_build_filter_ssids(
+		wpa_s->conf, &params.num_filter_ssids);
+	if (extra_ie) {
+		params.extra_ies = wpabuf_head(extra_ie);
+		params.extra_ies_len = wpabuf_len(extra_ie);
+	}
+
+#ifdef CONFIG_P2P
+	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
+		 * explicitly request P2P probe to disable CCK rates.
+		 */
+		params.p2p_probe = 1;
+	}
+#endif /* CONFIG_P2P */
+
+	if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) {
+		params.mac_addr_rand = 1;
+		if (wpa_s->mac_addr_scan) {
+			params.mac_addr = wpa_s->mac_addr_scan;
+			params.mac_addr_mask = wpa_s->mac_addr_scan + ETH_ALEN;
+		}
+	}
+
+	scan_params = &params;
+
+scan:
+#ifdef CONFIG_P2P
+	/*
+	 * If the driver does not support multi-channel concurrency and a
+	 * virtual interface that shares the same radio with the wpa_s interface
+	 * is operating there may not be need to scan other channels apart from
+	 * the current operating channel on the other virtual interface. Filter
+	 * out other channels in case we are trying to find a connection for a
+	 * 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 &&
+	    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 &&
+	    non_p2p_network_enabled(wpa_s)) {
+		unsigned int num = wpa_s->num_multichan_concurrent;
+
+		params.freqs = os_calloc(num + 1, sizeof(int));
+		if (params.freqs) {
+			num = get_shared_radio_freqs(wpa_s, params.freqs, num);
+			if (num > 0 && num == wpa_s->num_multichan_concurrent) {
+				wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the current operating channels since all channels are already used");
+			} else {
+				os_free(params.freqs);
+				params.freqs = NULL;
+			}
+		}
+	}
+#endif /* CONFIG_P2P */
+
+	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);
+
+	if (ret) {
+		wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan");
+		if (wpa_s->scan_prev_wpa_state != wpa_s->wpa_state)
+			wpa_supplicant_set_state(wpa_s,
+						 wpa_s->scan_prev_wpa_state);
+		/* Restore scan_req since we will try to scan again */
+		wpa_s->scan_req = wpa_s->last_scan_req;
+		wpa_supplicant_req_scan(wpa_s, 1, 0);
+	} else {
+		wpa_s->scan_for_connection = 0;
+#ifdef CONFIG_INTERWORKING
+		wpa_s->interworking_fast_assoc_tried = 0;
+#endif /* CONFIG_INTERWORKING */
+	}
+}
+
+
+void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec)
+{
+	struct os_reltime remaining, new_int;
+	int cancelled;
+
+	cancelled = eloop_cancel_timeout_one(wpa_supplicant_scan, wpa_s, NULL,
+					     &remaining);
+
+	new_int.sec = sec;
+	new_int.usec = 0;
+	if (cancelled && os_reltime_before(&remaining, &new_int)) {
+		new_int.sec = remaining.sec;
+		new_int.usec = remaining.usec;
+	}
+
+	if (cancelled) {
+		eloop_register_timeout(new_int.sec, new_int.usec,
+				       wpa_supplicant_scan, wpa_s, NULL);
+	}
+	wpa_s->scan_interval = sec;
+}
+
+
+/**
+ * wpa_supplicant_req_scan - Schedule a scan for neighboring access points
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @sec: Number of seconds after which to scan
+ * @usec: Number of microseconds after which to scan
+ *
+ * This function is used to schedule a scan for neighboring access points after
+ * the specified time.
+ */
+void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
+{
+	int res;
+
+	if (wpa_s->p2p_mgmt) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Ignore scan request (%d.%06d sec) on p2p_mgmt interface",
+			sec, usec);
+		return;
+	}
+
+	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);
+	}
+}
+
+
+/**
+ * wpa_supplicant_delayed_sched_scan - Request a delayed scheduled scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @sec: Number of seconds after which to scan
+ * @usec: Number of microseconds after which to scan
+ * Returns: 0 on success or -1 otherwise
+ *
+ * This function is used to schedule periodic scans for neighboring
+ * access points after the specified time.
+ */
+int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
+				      int sec, int usec)
+{
+	if (!wpa_s->sched_scan_supported)
+		return -1;
+
+	eloop_register_timeout(sec, usec,
+			       wpa_supplicant_delayed_sched_scan_timeout,
+			       wpa_s, NULL);
+
+	return 0;
+}
+
+
+/**
+ * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 is sched_scan was started or -1 otherwise
+ *
+ * This function is used to schedule periodic scans for neighboring
+ * access points repeating the scan continuously.
+ */
+int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_driver_scan_params params;
+	struct wpa_driver_scan_params *scan_params;
+	enum wpa_states prev_state;
+	struct wpa_ssid *ssid = NULL;
+	struct wpabuf *extra_ie = NULL;
+	int ret;
+	unsigned int max_sched_scan_ssids;
+	int wildcard = 0;
+	int need_ssids;
+
+	if (!wpa_s->sched_scan_supported)
+		return -1;
+
+	if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
+		max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
+	else
+		max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
+	if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload)
+		return -1;
+
+	if (wpa_s->sched_scanning) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning");
+		return 0;
+	}
+
+	need_ssids = 0;
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (!wpas_network_disabled(wpa_s, ssid) && !ssid->scan_ssid) {
+			/* Use wildcard SSID to find this network */
+			wildcard = 1;
+		} else if (!wpas_network_disabled(wpa_s, ssid) &&
+			   ssid->ssid_len)
+			need_ssids++;
+
+#ifdef CONFIG_WPS
+		if (!wpas_network_disabled(wpa_s, ssid) &&
+		    ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
+			/*
+			 * Normal scan is more reliable and faster for WPS
+			 * operations and since these are for short periods of
+			 * time, the benefit of trying to use sched_scan would
+			 * be limited.
+			 */
+			wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
+				"sched_scan for WPS");
+			return -1;
+		}
+#endif /* CONFIG_WPS */
+	}
+	if (wildcard)
+		need_ssids++;
+
+	if (wpa_s->normal_scans < 3 &&
+	    (need_ssids <= wpa_s->max_scan_ssids ||
+	     wpa_s->max_scan_ssids >= (int) max_sched_scan_ssids)) {
+		/*
+		 * When normal scan can speed up operations, use that for the
+		 * first operations before starting the sched_scan to allow
+		 * user space sleep more. We do this only if the normal scan
+		 * has functionality that is suitable for this or if the
+		 * sched_scan does not have better support for multiple SSIDs.
+		 */
+		wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
+			"sched_scan for initial scans (normal_scans=%d)",
+			wpa_s->normal_scans);
+		return -1;
+	}
+
+	os_memset(&params, 0, sizeof(params));
+
+	/* If we can't allocate space for the filters, we just don't filter */
+	params.filter_ssids = os_calloc(wpa_s->max_match_sets,
+					sizeof(struct wpa_driver_scan_filter));
+
+	prev_state = wpa_s->wpa_state;
+	if (wpa_s->wpa_state == WPA_DISCONNECTED ||
+	    wpa_s->wpa_state == WPA_INACTIVE)
+		wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
+
+	if (wpa_s->autoscan_params != NULL) {
+		scan_params = wpa_s->autoscan_params;
+		goto scan;
+	}
+
+	/* Find the starting point from which to continue scanning */
+	ssid = wpa_s->conf->ssid;
+	if (wpa_s->prev_sched_ssid) {
+		while (ssid) {
+			if (ssid == wpa_s->prev_sched_ssid) {
+				ssid = ssid->next;
+				break;
+			}
+			ssid = ssid->next;
+		}
+	}
+
+	if (!ssid || !wpa_s->prev_sched_ssid) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
+		if (wpa_s->conf->sched_scan_interval)
+			wpa_s->sched_scan_interval =
+				wpa_s->conf->sched_scan_interval;
+		if (wpa_s->sched_scan_interval == 0)
+			wpa_s->sched_scan_interval = 10;
+		wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
+		wpa_s->first_sched_scan = 1;
+		ssid = wpa_s->conf->ssid;
+		wpa_s->prev_sched_ssid = ssid;
+	}
+
+	if (wildcard) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Add wildcard SSID to sched_scan");
+		params.num_ssids++;
+	}
+
+	while (ssid) {
+		if (wpas_network_disabled(wpa_s, ssid))
+			goto next;
+
+		if (params.num_filter_ssids < wpa_s->max_match_sets &&
+		    params.filter_ssids && ssid->ssid && ssid->ssid_len) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "add to filter ssid: %s",
+				wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+			os_memcpy(params.filter_ssids[params.num_filter_ssids].ssid,
+				  ssid->ssid, ssid->ssid_len);
+			params.filter_ssids[params.num_filter_ssids].ssid_len =
+				ssid->ssid_len;
+			params.num_filter_ssids++;
+		} else if (params.filter_ssids && ssid->ssid && ssid->ssid_len)
+		{
+			wpa_dbg(wpa_s, MSG_DEBUG, "Not enough room for SSID "
+				"filter for sched_scan - drop filter");
+			os_free(params.filter_ssids);
+			params.filter_ssids = NULL;
+			params.num_filter_ssids = 0;
+		}
+
+		if (ssid->scan_ssid && ssid->ssid && ssid->ssid_len) {
+			if (params.num_ssids == max_sched_scan_ssids)
+				break; /* only room for broadcast SSID */
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"add to active scan ssid: %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++;
+			if (params.num_ssids >= max_sched_scan_ssids) {
+				wpa_s->prev_sched_ssid = ssid;
+				do {
+					ssid = ssid->next;
+				} while (ssid &&
+					 (wpas_network_disabled(wpa_s, ssid) ||
+					  !ssid->scan_ssid));
+				break;
+			}
+		}
+
+	next:
+		wpa_s->prev_sched_ssid = ssid;
+		ssid = ssid->next;
+	}
+
+	if (params.num_filter_ssids == 0) {
+		os_free(params.filter_ssids);
+		params.filter_ssids = NULL;
+	}
+
+	extra_ie = wpa_supplicant_extra_ies(wpa_s);
+	if (extra_ie) {
+		params.extra_ies = wpabuf_head(extra_ie);
+		params.extra_ies_len = wpabuf_len(extra_ie);
+	}
+
+	if (wpa_s->conf->filter_rssi)
+		params.filter_rssi = wpa_s->conf->filter_rssi;
+
+	/* See if user specified frequencies. If so, scan only those. */
+	if (wpa_s->conf->freq_list && !params.freqs) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Optimize scan based on conf->freq_list");
+		int_array_concat(&params.freqs, wpa_s->conf->freq_list);
+	}
+
+	scan_params = &params;
+
+scan:
+	if (ssid || !wpa_s->first_sched_scan) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Starting sched scan: interval %d timeout %d",
+			wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
+	} else {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Starting sched scan: interval %d (no timeout)",
+			wpa_s->sched_scan_interval);
+	}
+
+	wpa_setband_scan_freqs(wpa_s, scan_params);
+
+	if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) {
+		params.mac_addr_rand = 1;
+		if (wpa_s->mac_addr_sched_scan) {
+			params.mac_addr = wpa_s->mac_addr_sched_scan;
+			params.mac_addr_mask = wpa_s->mac_addr_sched_scan +
+				ETH_ALEN;
+		}
+	}
+
+	ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params,
+					      wpa_s->sched_scan_interval);
+	wpabuf_free(extra_ie);
+	os_free(params.filter_ssids);
+	if (ret) {
+		wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan");
+		if (prev_state != wpa_s->wpa_state)
+			wpa_supplicant_set_state(wpa_s, prev_state);
+		return ret;
+	}
+
+	/* If we have more SSIDs to scan, add a timeout so we scan them too */
+	if (ssid || !wpa_s->first_sched_scan) {
+		wpa_s->sched_scan_timed_out = 0;
+		eloop_register_timeout(wpa_s->sched_scan_timeout, 0,
+				       wpa_supplicant_sched_scan_timeout,
+				       wpa_s, NULL);
+		wpa_s->first_sched_scan = 0;
+		wpa_s->sched_scan_timeout /= 2;
+		wpa_s->sched_scan_interval *= 2;
+		if (wpa_s->sched_scan_timeout < wpa_s->sched_scan_interval) {
+			wpa_s->sched_scan_interval = 10;
+			wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
+		}
+	}
+
+	/* If there is no more ssids, start next time from the beginning */
+	if (!ssid)
+		wpa_s->prev_sched_ssid = NULL;
+
+	return 0;
+}
+
+
+/**
+ * wpa_supplicant_cancel_scan - Cancel a scheduled scan request
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to cancel a scan request scheduled with
+ * wpa_supplicant_req_scan().
+ */
+void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
+{
+	wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request");
+	eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+	wpa_supplicant_notify_scanning(wpa_s, 0);
+}
+
+
+/**
+ * wpa_supplicant_cancel_delayed_sched_scan - Stop a delayed scheduled scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to stop a delayed scheduled scan.
+ */
+void wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->sched_scan_supported)
+		return;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling delayed sched scan");
+	eloop_cancel_timeout(wpa_supplicant_delayed_sched_scan_timeout,
+			     wpa_s, NULL);
+}
+
+
+/**
+ * wpa_supplicant_cancel_sched_scan - Stop running scheduled scans
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to stop a periodic scheduled scan.
+ */
+void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->sched_scanning)
+		return;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan");
+	eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL);
+	wpa_supplicant_stop_sched_scan(wpa_s);
+}
+
+
+/**
+ * wpa_supplicant_notify_scanning - Indicate possible scan state change
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @scanning: Whether scanning is currently in progress
+ *
+ * This function is to generate scanning notifycations. It is called whenever
+ * there may have been a change in scanning (scan started, completed, stopped).
+ * wpas_notify_scanning() is called whenever the scanning state changed from the
+ * previously notified state.
+ */
+void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
+				    int scanning)
+{
+	if (wpa_s->scanning != scanning) {
+		wpa_s->scanning = scanning;
+		wpas_notify_scanning(wpa_s);
+	}
+}
+
+
+static int wpa_scan_get_max_rate(const struct wpa_scan_res *res)
+{
+	int rate = 0;
+	const u8 *ie;
+	int i;
+
+	ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES);
+	for (i = 0; ie && i < ie[1]; i++) {
+		if ((ie[i + 2] & 0x7f) > rate)
+			rate = ie[i + 2] & 0x7f;
+	}
+
+	ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES);
+	for (i = 0; ie && i < ie[1]; i++) {
+		if ((ie[i + 2] & 0x7f) > rate)
+			rate = ie[i + 2] & 0x7f;
+	}
+
+	return rate;
+}
+
+
+/**
+ * wpa_scan_get_ie - Fetch a specified information element from a scan result
+ * @res: Scan result entry
+ * @ie: Information element identitifier (WLAN_EID_*)
+ * 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.
+ */
+const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
+{
+	const u8 *end, *pos;
+
+	pos = (const u8 *) (res + 1);
+	end = pos + res->ie_len;
+
+	while (pos + 1 < end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		if (pos[0] == ie)
+			return pos;
+		pos += 2 + pos[1];
+	}
+
+	return NULL;
+}
+
+
+/**
+ * wpa_scan_get_vendor_ie - Fetch vendor information element 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.
+ */
+const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
+				  u32 vendor_type)
+{
+	const u8 *end, *pos;
+
+	pos = (const u8 *) (res + 1);
+	end = pos + res->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_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)
+ * Returns: Pointer to the information element payload or %NULL if not found
+ *
+ * This function returns concatenated payload of possibly fragmented vendor
+ * specific information elements in the scan result. The caller is responsible
+ * for freeing the returned buffer.
+ */
+struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
+					     u32 vendor_type)
+{
+	struct wpabuf *buf;
+	const u8 *end, *pos;
+
+	buf = wpabuf_alloc(res->ie_len);
+	if (buf == NULL)
+		return NULL;
+
+	pos = (const u8 *) (res + 1);
+	end = pos + res->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]))
+			wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
+		pos += 2 + pos[1];
+	}
+
+	if (wpabuf_len(buf) == 0) {
+		wpabuf_free(buf);
+		buf = NULL;
+	}
+
+	return buf;
+}
+
+
+/*
+ * Channels with a great SNR can operate at full rate. What is a great SNR?
+ * This doc https://supportforums.cisco.com/docs/DOC-12954 says, "the general
+ * rule of thumb is that any SNR above 20 is good." This one
+ * http://www.cisco.com/en/US/tech/tk722/tk809/technologies_q_and_a_item09186a00805e9a96.shtml#qa23
+ * recommends 25 as a minimum SNR for 54 Mbps data rate. 30 is chosen here as a
+ * conservative value.
+ */
+#define GREAT_SNR 30
+
+#define IS_5GHZ(n) (n > 4000)
+
+static int is_good_5ghz_network(struct wpa_scan_res *w)
+{
+	if (w->freq < 4000)
+	    return 0; /* not 5Ghz */
+
+	if (w->level < -70)
+	    return 0; /* too weak */
+
+	return 1;
+}
+
+/* Compare function for sorting scan results. Return >0 if @b is considered
+ * better. */
+static int wpa_scan_result_compar(const void *a, const void *b)
+{
+#define MIN(a,b) a < b ? a : b
+	struct wpa_scan_res **_wa = (void *) a;
+	struct wpa_scan_res **_wb = (void *) b;
+	struct wpa_scan_res *wa = *_wa;
+	struct wpa_scan_res *wb = *_wb;
+	int wpa_a, wpa_b;
+	int snr_a, snr_b, snr_a_full, snr_b_full;
+
+	/* WPA/WPA2 support preferred */
+	wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
+		wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL;
+	wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL ||
+		wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL;
+
+	if (wpa_b && !wpa_a)
+		return 1;
+	if (!wpa_b && wpa_a)
+		return -1;
+
+	/* privacy support preferred */
+	if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 &&
+	    (wb->caps & IEEE80211_CAP_PRIVACY))
+		return 1;
+	if ((wa->caps & IEEE80211_CAP_PRIVACY) &&
+	    (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
+		return -1;
+
+	/* (Dropcam addition) heavily favor 5Ghz networks over 2.4Ghz */
+	if (is_good_5ghz_network(wa) && !is_good_5ghz_network(wb))
+		return -1;
+
+	if (!is_good_5ghz_network(wa) && is_good_5ghz_network(wb))
+		return 1;
+
+	if (wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) {
+		snr_a_full = wa->snr;
+		snr_a = MIN(wa->snr, GREAT_SNR);
+		snr_b_full = wb->snr;
+		snr_b = MIN(wb->snr, GREAT_SNR);
+	} else {
+		/* Level is not in dBm, so we can't calculate
+		 * SNR. Just use raw level (units unknown). */
+		snr_a = snr_a_full = wa->level;
+		snr_b = snr_b_full = wb->level;
+	}
+
+	/*
+	wpa_printf(MSG_DEBUG, "Channel a: freq:%d qual:%d level:%d noise:%d snr:%d", wa->freq, wa->qual, wa->level, wa->noise, snr_a);
+	wpa_printf(MSG_DEBUG, "Channel b: freq:%d qual:%d level:%d noise:%d snr:%d", wb->freq, wa->qual, wb->level, wb->noise, snr_b);
+	*/
+
+	/* if SNR is close, decide by max rate or frequency band */
+	if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
+	    (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
+		if (wa->est_throughput != wb->est_throughput)
+			return wb->est_throughput - wa->est_throughput;
+		if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq))
+			return IS_5GHZ(wa->freq) ? -1 : 1;
+	}
+
+	/* all things being equal, use SNR; if SNRs are
+	 * identical, use quality values since some drivers may only report
+	 * that value and leave the signal level zero */
+	if (snr_b_full == snr_a_full)
+		return wb->qual - wa->qual;
+	return snr_b_full - snr_a_full;
+#undef MIN
+}
+
+
+#ifdef CONFIG_WPS
+/* Compare function for sorting scan results when searching a WPS AP for
+ * provisioning. Return >0 if @b is considered better. */
+static int wpa_scan_result_wps_compar(const void *a, const void *b)
+{
+	struct wpa_scan_res **_wa = (void *) a;
+	struct wpa_scan_res **_wb = (void *) b;
+	struct wpa_scan_res *wa = *_wa;
+	struct wpa_scan_res *wb = *_wb;
+	int uses_wps_a, uses_wps_b;
+	struct wpabuf *wps_a, *wps_b;
+	int res;
+
+	/* Optimization - check WPS IE existence before allocated memory and
+	 * doing full reassembly. */
+	uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL;
+	uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL;
+	if (uses_wps_a && !uses_wps_b)
+		return -1;
+	if (!uses_wps_a && uses_wps_b)
+		return 1;
+
+	if (uses_wps_a && uses_wps_b) {
+		wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE);
+		wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE);
+		res = wps_ap_priority_compar(wps_a, wps_b);
+		wpabuf_free(wps_a);
+		wpabuf_free(wps_b);
+		if (res)
+			return res;
+	}
+
+	/*
+	 * Do not use current AP security policy as a sorting criteria during
+	 * WPS provisioning step since the AP may get reconfigured at the
+	 * completion of provisioning.
+	 */
+
+	/* all things being equal, use signal level; if signal levels are
+	 * identical, use quality values since some drivers may only report
+	 * that value and leave the signal level zero */
+	if (wb->level == wa->level)
+		return wb->qual - wa->qual;
+	return wb->level - wa->level;
+}
+#endif /* CONFIG_WPS */
+
+
+static void dump_scan_res(struct wpa_scan_results *scan_res)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+	size_t i;
+
+	if (scan_res->res == NULL || scan_res->num == 0)
+		return;
+
+	wpa_printf(MSG_EXCESSIVE, "Sorted scan results");
+
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *r = scan_res->res[i];
+		u8 *pos;
+		if (r->flags & WPA_SCAN_LEVEL_DBM) {
+			int noise_valid = !(r->flags & WPA_SCAN_NOISE_INVALID);
+
+			wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
+				   "noise=%d%s level=%d snr=%d%s flags=0x%x age=%u est=%u",
+				   MAC2STR(r->bssid), r->freq, r->qual,
+				   r->noise, noise_valid ? "" : "~", r->level,
+				   r->snr, r->snr >= GREAT_SNR ? "*" : "",
+				   r->flags,
+				   r->age, r->est_throughput);
+		} else {
+			wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
+				   "noise=%d level=%d flags=0x%x age=%u est=%u",
+				   MAC2STR(r->bssid), r->freq, r->qual,
+				   r->noise, r->level, r->flags, r->age,
+				   r->est_throughput);
+		}
+		pos = (u8 *) (r + 1);
+		if (r->ie_len)
+			wpa_hexdump(MSG_EXCESSIVE, "IEs", pos, r->ie_len);
+		pos += r->ie_len;
+		if (r->beacon_ie_len)
+			wpa_hexdump(MSG_EXCESSIVE, "Beacon IEs",
+				    pos, r->beacon_ie_len);
+	}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+/**
+ * wpa_supplicant_filter_bssid_match - Is the specified BSSID allowed
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to check
+ * Returns: 0 if the BSSID is filtered or 1 if not
+ *
+ * This function is used to filter out specific BSSIDs from scan reslts mainly
+ * for testing purposes (SET bssid_filter ctrl_iface command).
+ */
+int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
+				      const u8 *bssid)
+{
+	size_t i;
+
+	if (wpa_s->bssid_filter == NULL)
+		return 1;
+
+	for (i = 0; i < wpa_s->bssid_filter_count; i++) {
+		if (os_memcmp(wpa_s->bssid_filter + i * ETH_ALEN, bssid,
+			      ETH_ALEN) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void filter_scan_res(struct wpa_supplicant *wpa_s,
+			    struct wpa_scan_results *res)
+{
+	size_t i, j;
+
+	if (wpa_s->bssid_filter == NULL)
+		return;
+
+	for (i = 0, j = 0; i < res->num; i++) {
+		if (wpa_supplicant_filter_bssid_match(wpa_s,
+						      res->res[i]->bssid)) {
+			res->res[j++] = res->res[i];
+		} else {
+			os_free(res->res[i]);
+			res->res[i] = NULL;
+		}
+	}
+
+	if (res->num != j) {
+		wpa_printf(MSG_DEBUG, "Filtered out %d scan results",
+			   (int) (res->num - j));
+		res->num = j;
+	}
+}
+
+
+/*
+ * Noise floor values to use when we have signal strength
+ * measurements, but no noise floor measurments. These values were
+ * measured in an office environment with many APs.
+ */
+#define DEFAULT_NOISE_FLOOR_2GHZ (-89)
+#define DEFAULT_NOISE_FLOOR_5GHZ (-92)
+
+static void scan_snr(struct wpa_scan_res *res)
+{
+	if (res->flags & WPA_SCAN_NOISE_INVALID) {
+		res->noise = IS_5GHZ(res->freq) ?
+			DEFAULT_NOISE_FLOOR_5GHZ :
+			DEFAULT_NOISE_FLOOR_2GHZ;
+	}
+
+	if (res->flags & WPA_SCAN_LEVEL_DBM) {
+		res->snr = res->level - res->noise;
+	} else {
+		/* Level is not in dBm, so we can't calculate
+		 * SNR. Just use raw level (units unknown). */
+		res->snr = res->level;
+	}
+}
+
+
+static unsigned int max_ht20_rate(int snr)
+{
+	if (snr < 6)
+		return 6500; /* HT20 MCS0 */
+	if (snr < 8)
+		return 13000; /* HT20 MCS1 */
+	if (snr < 13)
+		return 19500; /* HT20 MCS2 */
+	if (snr < 17)
+		return 26000; /* HT20 MCS3 */
+	if (snr < 20)
+		return 39000; /* HT20 MCS4 */
+	if (snr < 23)
+		return 52000; /* HT20 MCS5 */
+	if (snr < 24)
+		return 58500; /* HT20 MCS6 */
+	return 65000; /* HT20 MCS7 */
+}
+
+
+static unsigned int max_ht40_rate(int snr)
+{
+	if (snr < 3)
+		return 13500; /* HT40 MCS0 */
+	if (snr < 6)
+		return 27000; /* HT40 MCS1 */
+	if (snr < 10)
+		return 40500; /* HT40 MCS2 */
+	if (snr < 15)
+		return 54000; /* HT40 MCS3 */
+	if (snr < 17)
+		return 81000; /* HT40 MCS4 */
+	if (snr < 22)
+		return 108000; /* HT40 MCS5 */
+	if (snr < 24)
+		return 121500; /* HT40 MCS6 */
+	return 135000; /* HT40 MCS7 */
+}
+
+
+static unsigned int max_vht80_rate(int snr)
+{
+	if (snr < 1)
+		return 0;
+	if (snr < 2)
+		return 29300; /* VHT80 MCS0 */
+	if (snr < 5)
+		return 58500; /* VHT80 MCS1 */
+	if (snr < 9)
+		return 87800; /* VHT80 MCS2 */
+	if (snr < 11)
+		return 117000; /* VHT80 MCS3 */
+	if (snr < 15)
+		return 175500; /* VHT80 MCS4 */
+	if (snr < 16)
+		return 234000; /* VHT80 MCS5 */
+	if (snr < 18)
+		return 263300; /* VHT80 MCS6 */
+	if (snr < 20)
+		return 292500; /* VHT80 MCS7 */
+	if (snr < 22)
+		return 351000; /* VHT80 MCS8 */
+	return 390000; /* VHT80 MCS9 */
+}
+
+
+static void scan_est_throughput(struct wpa_supplicant *wpa_s,
+				struct wpa_scan_res *res)
+{
+	enum local_hw_capab capab = wpa_s->hw_capab;
+	int rate; /* max legacy rate in 500 kb/s units */
+	const u8 *ie;
+	unsigned int est, tmp;
+	int snr = res->snr;
+
+	if (res->est_throughput)
+		return;
+
+	/* Get maximum legacy rate */
+	rate = wpa_scan_get_max_rate(res);
+
+	/* Limit based on estimated SNR */
+	if (rate > 1 * 2 && snr < 1)
+		rate = 1 * 2;
+	else if (rate > 2 * 2 && snr < 4)
+		rate = 2 * 2;
+	else if (rate > 6 * 2 && snr < 5)
+		rate = 6 * 2;
+	else if (rate > 9 * 2 && snr < 6)
+		rate = 9 * 2;
+	else if (rate > 12 * 2 && snr < 7)
+		rate = 12 * 2;
+	else if (rate > 18 * 2 && snr < 10)
+		rate = 18 * 2;
+	else if (rate > 24 * 2 && snr < 11)
+		rate = 24 * 2;
+	else if (rate > 36 * 2 && snr < 15)
+		rate = 36 * 2;
+	else if (rate > 48 * 2 && snr < 19)
+		rate = 48 * 2;
+	else if (rate > 54 * 2 && snr < 21)
+		rate = 54 * 2;
+	est = rate * 500;
+
+	if (capab == CAPAB_HT || capab == CAPAB_HT40 || capab == CAPAB_VHT) {
+		ie = wpa_scan_get_ie(res, WLAN_EID_HT_CAP);
+		if (ie) {
+			tmp = max_ht20_rate(snr);
+			if (tmp > est)
+				est = tmp;
+		}
+	}
+
+	if (capab == CAPAB_HT40 || capab == CAPAB_VHT) {
+		ie = wpa_scan_get_ie(res, WLAN_EID_HT_OPERATION);
+		if (ie && ie[1] >= 2 &&
+		    (ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
+			tmp = max_ht40_rate(snr);
+			if (tmp > est)
+				est = tmp;
+		}
+	}
+
+	if (capab == CAPAB_VHT) {
+		/* Use +1 to assume VHT is always faster than HT */
+		ie = wpa_scan_get_ie(res, WLAN_EID_VHT_CAP);
+		if (ie) {
+			tmp = max_ht20_rate(snr) + 1;
+			if (tmp > est)
+				est = tmp;
+
+			ie = wpa_scan_get_ie(res, WLAN_EID_HT_OPERATION);
+			if (ie && ie[1] >= 2 &&
+			    (ie[3] &
+			     HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
+				tmp = max_ht40_rate(snr) + 1;
+				if (tmp > est)
+					est = tmp;
+			}
+
+			ie = wpa_scan_get_ie(res, WLAN_EID_VHT_OPERATION);
+			if (ie && ie[1] >= 1 &&
+			    (ie[2] & VHT_OPMODE_CHANNEL_WIDTH_MASK)) {
+				tmp = max_vht80_rate(snr) + 1;
+				if (tmp > est)
+					est = tmp;
+			}
+		}
+	}
+
+	/* TODO: channel utilization and AP load (e.g., from AP Beacon) */
+
+	res->est_throughput = est;
+}
+
+
+/**
+ * wpa_supplicant_get_scan_results - Get scan results
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @info: Information about what was scanned or %NULL if not available
+ * @new_scan: Whether a new scan was performed
+ * Returns: Scan results, %NULL on failure
+ *
+ * This function request the current scan results from the driver and updates
+ * the local BSS list wpa_s->bss. The caller is responsible for freeing the
+ * results with wpa_scan_results_free().
+ */
+struct wpa_scan_results *
+wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
+				struct scan_info *info, int new_scan)
+{
+	struct wpa_scan_results *scan_res;
+	size_t i;
+	int (*compar)(const void *, const void *) = wpa_scan_result_compar;
+
+	scan_res = wpa_drv_get_scan_results2(wpa_s);
+	if (scan_res == NULL) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
+		return NULL;
+	}
+	if (scan_res->fetch_time.sec == 0) {
+		/*
+		 * Make sure we have a valid timestamp if the driver wrapper
+		 * does not set this.
+		 */
+		os_get_reltime(&scan_res->fetch_time);
+	}
+	filter_scan_res(wpa_s, scan_res);
+
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *scan_res_item = scan_res->res[i];
+
+		scan_snr(scan_res_item);
+		scan_est_throughput(wpa_s, scan_res_item);
+	}
+
+#ifdef CONFIG_WPS
+	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;
+	}
+#endif /* CONFIG_WPS */
+
+	qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *),
+	      compar);
+	dump_scan_res(scan_res);
+
+	wpa_bss_update_start(wpa_s);
+	for (i = 0; i < scan_res->num; i++)
+		wpa_bss_update_scan_res(wpa_s, scan_res->res[i],
+					&scan_res->fetch_time);
+	wpa_bss_update_end(wpa_s, info, new_scan);
+
+	return scan_res;
+}
+
+
+/**
+ * wpa_supplicant_update_scan_results - Update scan results from the driver
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function updates the BSS table within wpa_supplicant based on the
+ * currently available scan results from the driver without requesting a new
+ * scan. This is used in cases where the driver indicates an association
+ * (including roaming within ESS) and wpa_supplicant does not yet have the
+ * needed information to complete the connection (e.g., to perform validation
+ * steps in 4-way handshake).
+ */
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_scan_results *scan_res;
+	scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+	if (scan_res == NULL)
+		return -1;
+	wpa_scan_results_free(scan_res);
+
+	return 0;
+}
+
+
+/**
+ * scan_only_handler - Reports scan results
+ */
+void scan_only_handler(struct wpa_supplicant *wpa_s,
+		       struct wpa_scan_results *scan_res)
+{
+	wpa_dbg(wpa_s, MSG_DEBUG, "Scan-only results received");
+	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);
+	}
+}
+
+
+int wpas_scan_scheduled(struct wpa_supplicant *wpa_s)
+{
+	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;
+
+	if (src->mac_addr_rand) {
+		params->mac_addr_rand = src->mac_addr_rand;
+
+		if (src->mac_addr && src->mac_addr_mask) {
+			u8 *mac_addr;
+
+			mac_addr = os_malloc(2 * ETH_ALEN);
+			if (!mac_addr)
+				goto failed;
+
+			os_memcpy(mac_addr, src->mac_addr, ETH_ALEN);
+			os_memcpy(mac_addr + ETH_ALEN, src->mac_addr_mask,
+				  ETH_ALEN);
+			params->mac_addr = mac_addr;
+			params->mac_addr_mask = mac_addr + ETH_ALEN;
+		}
+	}
+	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);
+
+	/*
+	 * Note: params->mac_addr_mask points to same memory allocation and
+	 * must not be freed separately.
+	 */
+	os_free((u8 *) params->mac_addr);
+
+	os_free(params);
+}
+
+
+int wpas_start_pno(struct wpa_supplicant *wpa_s)
+{
+	int ret, interval, prio;
+	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;
+	prio = 0;
+	ssid = wpa_s->conf->pssid[prio];
+	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;
+		}
+		if (ssid->pnext)
+			ssid = ssid->pnext;
+		else if (prio + 1 == wpa_s->conf->num_prio)
+			break;
+		else
+			ssid = wpa_s->conf->pssid[++prio];
+	}
+
+	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;
+	}
+
+	if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) {
+		params.mac_addr_rand = 1;
+		if (wpa_s->mac_addr_pno) {
+			params.mac_addr = wpa_s->mac_addr_pno;
+			params.mac_addr_mask = wpa_s->mac_addr_pno + ETH_ALEN;
+		}
+	}
+
+	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;
+}
+
+
+void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s,
+				    unsigned int type)
+{
+	type &= MAC_ADDR_RAND_ALL;
+	wpa_s->mac_addr_rand_enable &= ~type;
+
+	if (type & MAC_ADDR_RAND_SCAN) {
+		os_free(wpa_s->mac_addr_scan);
+		wpa_s->mac_addr_scan = NULL;
+	}
+
+	if (type & MAC_ADDR_RAND_SCHED_SCAN) {
+		os_free(wpa_s->mac_addr_sched_scan);
+		wpa_s->mac_addr_sched_scan = NULL;
+	}
+
+	if (type & MAC_ADDR_RAND_PNO) {
+		os_free(wpa_s->mac_addr_pno);
+		wpa_s->mac_addr_pno = NULL;
+	}
+}
+
+
+int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
+				unsigned int type, const u8 *addr,
+				const u8 *mask)
+{
+	u8 *tmp = NULL;
+
+	wpas_mac_addr_rand_scan_clear(wpa_s, type);
+
+	if (addr) {
+		tmp = os_malloc(2 * ETH_ALEN);
+		if (!tmp)
+			return -1;
+		os_memcpy(tmp, addr, ETH_ALEN);
+		os_memcpy(tmp + ETH_ALEN, mask, ETH_ALEN);
+	}
+
+	if (type == MAC_ADDR_RAND_SCAN) {
+		wpa_s->mac_addr_scan = tmp;
+	} else if (type == MAC_ADDR_RAND_SCHED_SCAN) {
+		wpa_s->mac_addr_sched_scan = tmp;
+	} else if (type == MAC_ADDR_RAND_PNO) {
+		wpa_s->mac_addr_pno = tmp;
+	} else {
+		wpa_printf(MSG_INFO,
+			   "scan: Invalid MAC randomization type=0x%x",
+			   type);
+		os_free(tmp);
+		return -1;
+	}
+
+	wpa_s->mac_addr_rand_enable |= type;
+	return 0;
+}
diff --git a/hostap/wpa_supplicant/scan.h b/hostap/wpa_supplicant/scan.h
new file mode 100644
index 0000000..7650f5a
--- /dev/null
+++ b/hostap/wpa_supplicant/scan.h
@@ -0,0 +1,58 @@
+/*
+ * WPA Supplicant - Scanning
+ * 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.
+ */
+
+#ifndef SCAN_H
+#define SCAN_H
+
+int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec);
+int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
+				      int sec, int usec);
+int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
+				    int scanning);
+struct wpa_driver_scan_params;
+int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
+				struct wpa_driver_scan_params *params);
+struct wpa_scan_results *
+wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
+				struct scan_info *info, int new_scan);
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s);
+const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie);
+const u8 * wpa_scan_get_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,
+				      const u8 *bssid);
+void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec);
+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);
+
+void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s,
+				   unsigned int type);
+int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
+				unsigned int type, const u8 *addr,
+				const u8 *mask);
+
+#endif /* SCAN_H */
diff --git a/hostap/wpa_supplicant/sme.c b/hostap/wpa_supplicant/sme.c
new file mode 100644
index 0000000..f2e5a43
--- /dev/null
+++ b/hostap/wpa_supplicant/sme.c
@@ -0,0 +1,1662 @@
+/*
+ * wpa_supplicant - SME
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "common/wpa_common.h"
+#include "common/sae.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "wpas_glue.h"
+#include "wps_supplicant.h"
+#include "p2p_supplicant.h"
+#include "notify.h"
+#include "bss.h"
+#include "scan.h"
+#include "sme.h"
+#include "hs20_supplicant.h"
+
+#define SME_AUTH_TIMEOUT 5
+#define SME_ASSOC_TIMEOUT 5
+
+static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx);
+static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx);
+static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+#ifdef CONFIG_IEEE80211W
+static void sme_stop_sa_query(struct wpa_supplicant *wpa_s);
+#endif /* CONFIG_IEEE80211W */
+
+
+#ifdef CONFIG_SAE
+
+static int index_within_array(const int *array, int idx)
+{
+	int i;
+	for (i = 0; i < idx; i++) {
+		if (array[i] <= 0)
+			return 0;
+	}
+	return 1;
+}
+
+
+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, 0 };
+
+	if (!groups || groups[0] <= 0)
+		groups = default_groups;
+
+	/* Configuration may have changed, so validate current index */
+	if (!index_within_array(groups, wpa_s->sme.sae_group_index))
+		return -1;
+
+	for (;;) {
+		int group = groups[wpa_s->sme.sae_group_index];
+		if (group <= 0)
+			break;
+		if (sae_set_group(&wpa_s->sme.sae, group) == 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
+				wpa_s->sme.sae.group);
+		       return 0;
+		}
+		wpa_s->sme.sae_group_index++;
+	}
+
+	return -1;
+}
+
+
+static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
+						 struct wpa_ssid *ssid,
+						 const u8 *bssid)
+{
+	struct wpabuf *buf;
+	size_t len;
+
+	if (ssid->passphrase == NULL) {
+		wpa_printf(MSG_DEBUG, "SAE: No password available");
+		return NULL;
+	}
+
+	if (sme_set_sae_group(wpa_s) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to select group");
+		return NULL;
+	}
+
+	if (sae_prepare_commit(wpa_s->own_addr, bssid,
+			       (u8 *) ssid->passphrase,
+			       os_strlen(ssid->passphrase),
+			       &wpa_s->sme.sae) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
+		return NULL;
+	}
+
+	len = wpa_s->sme.sae_token ? wpabuf_len(wpa_s->sme.sae_token) : 0;
+	buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len);
+	if (buf == NULL)
+		return NULL;
+
+	wpabuf_put_le16(buf, 1); /* Transaction seq# */
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+	sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token);
+
+	return buf;
+}
+
+
+static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN);
+	if (buf == NULL)
+		return NULL;
+
+	wpabuf_put_le16(buf, 2); /* Transaction seq# */
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+	sae_write_confirm(&wpa_s->sme.sae, buf);
+
+	return buf;
+}
+
+#endif /* CONFIG_SAE */
+
+
+/**
+ * sme_auth_handle_rrm - Handle RRM aspects of current authentication attempt
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: Pointer to the bss which is the target of authentication attempt
+ */
+static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s,
+				struct wpa_bss *bss)
+{
+	const u8 rrm_ie_len = 5;
+	u8 *pos;
+	const u8 *rrm_ie;
+
+	wpa_s->rrm.rrm_used = 0;
+
+	wpa_printf(MSG_DEBUG,
+		   "RRM: Determining whether RRM can be used - device support: 0x%x",
+		   wpa_s->drv_rrm_flags);
+
+	rrm_ie = wpa_bss_get_ie(bss, WLAN_EID_RRM_ENABLED_CAPABILITIES);
+	if (!rrm_ie || !(bss->caps & IEEE80211_CAP_RRM)) {
+		wpa_printf(MSG_DEBUG, "RRM: No RRM in network");
+		return;
+	}
+
+	if (!(wpa_s->drv_rrm_flags &
+	      WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) ||
+	    !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Insufficient RRM support in driver - do not use RRM");
+		return;
+	}
+
+	if (sizeof(wpa_s->sme.assoc_req_ie) <
+	    wpa_s->sme.assoc_req_ie_len + rrm_ie_len + 2) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Unable to use RRM, no room for RRM IE");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "RRM: Adding RRM IE to Association Request");
+	pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
+	os_memset(pos, 0, 2 + rrm_ie_len);
+	*pos++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
+	*pos++ = rrm_ie_len;
+
+	/* Set supported capabilites flags */
+	if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)
+		*pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT;
+
+	wpa_s->sme.assoc_req_ie_len += rrm_ie_len + 2;
+	wpa_s->rrm.rrm_used = 1;
+}
+
+
+static void sme_send_authentication(struct wpa_supplicant *wpa_s,
+				    struct wpa_bss *bss, struct wpa_ssid *ssid,
+				    int start)
+{
+	struct wpa_driver_auth_params params;
+	struct wpa_ssid *old_ssid;
+#ifdef CONFIG_IEEE80211R
+	const u8 *ie;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211R
+	const u8 *md = NULL;
+#endif /* CONFIG_IEEE80211R */
+	int i, bssid_changed;
+	struct wpabuf *resp = NULL;
+	u8 ext_capab[18];
+	int ext_capab_len;
+	int skip_auth;
+
+	if (bss == NULL) {
+		wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for "
+			"the network");
+		wpas_connect_work_done(wpa_s);
+		return;
+	}
+
+	skip_auth = wpa_s->conf->reassoc_same_bss_optim &&
+		wpa_s->reassoc_same_bss;
+	wpa_s->current_bss = bss;
+
+	os_memset(&params, 0, sizeof(params));
+	wpa_s->reassociate = 0;
+
+	params.freq = bss->freq;
+	params.bssid = bss->bssid;
+	params.ssid = bss->ssid;
+	params.ssid_len = bss->ssid_len;
+	params.p2p = ssid->p2p_group;
+
+	if (wpa_s->sme.ssid_len != params.ssid_len ||
+	    os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0)
+		wpa_s->sme.prev_bssid_set = 0;
+
+	wpa_s->sme.freq = params.freq;
+	os_memcpy(wpa_s->sme.ssid, params.ssid, params.ssid_len);
+	wpa_s->sme.ssid_len = params.ssid_len;
+
+	params.auth_alg = WPA_AUTH_ALG_OPEN;
+#ifdef IEEE8021X_EAPOL
+	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+		if (ssid->leap) {
+			if (ssid->non_leap == 0)
+				params.auth_alg = WPA_AUTH_ALG_LEAP;
+			else
+				params.auth_alg |= WPA_AUTH_ALG_LEAP;
+		}
+	}
+#endif /* IEEE8021X_EAPOL */
+	wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x",
+		params.auth_alg);
+	if (ssid->auth_alg) {
+		params.auth_alg = ssid->auth_alg;
+		wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
+			"0x%x", params.auth_alg);
+	}
+#ifdef CONFIG_SAE
+	wpa_s->sme.sae_pmksa_caching = 0;
+	if (wpa_key_mgmt_sae(ssid->key_mgmt)) {
+		const u8 *rsn;
+		struct wpa_ie_data ied;
+
+		rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+		if (!rsn) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"SAE enabled, but target BSS does not advertise RSN");
+		} else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 &&
+			   wpa_key_mgmt_sae(ied.key_mgmt)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
+			params.auth_alg = WPA_AUTH_ALG_SAE;
+		} else {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"SAE enabled, but target BSS does not advertise SAE AKM for RSN");
+		}
+	}
+#endif /* CONFIG_SAE */
+
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		if (ssid->wep_key_len[i])
+			params.wep_key[i] = ssid->wep_key[i];
+		params.wep_key_len[i] = ssid->wep_key_len[i];
+	}
+	params.wep_tx_keyidx = ssid->wep_tx_keyidx;
+
+	bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
+	os_memset(wpa_s->bssid, 0, ETH_ALEN);
+	os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
+	if (bssid_changed)
+		wpas_notify_bssid_changed(wpa_s);
+
+	if ((wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
+	     wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
+	    wpa_key_mgmt_wpa(ssid->key_mgmt)) {
+		int try_opportunistic;
+		try_opportunistic = (ssid->proactive_key_caching < 0 ?
+				     wpa_s->conf->okc :
+				     ssid->proactive_key_caching) &&
+			(ssid->proto & WPA_PROTO_RSN);
+		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+					    wpa_s->current_ssid,
+					    try_opportunistic) == 0)
+			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
+		wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
+		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
+					      wpa_s->sme.assoc_req_ie,
+					      &wpa_s->sme.assoc_req_ie_len)) {
+			wpa_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) &&
+		   wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
+		/*
+		 * Both WPA and non-WPA IEEE 802.1X enabled in configuration -
+		 * use non-WPA since the scan results did not indicate that the
+		 * AP is using WPA or WPA2.
+		 */
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+		wpa_s->sme.assoc_req_ie_len = 0;
+	} else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
+		wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
+		if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
+					      wpa_s->sme.assoc_req_ie,
+					      &wpa_s->sme.assoc_req_ie_len)) {
+			wpa_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
+	} else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+		struct wpabuf *wps_ie;
+		wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
+		if (wps_ie && wpabuf_len(wps_ie) <=
+		    sizeof(wpa_s->sme.assoc_req_ie)) {
+			wpa_s->sme.assoc_req_ie_len = wpabuf_len(wps_ie);
+			os_memcpy(wpa_s->sme.assoc_req_ie, wpabuf_head(wps_ie),
+				  wpa_s->sme.assoc_req_ie_len);
+		} else
+			wpa_s->sme.assoc_req_ie_len = 0;
+		wpabuf_free(wps_ie);
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+#endif /* CONFIG_WPS */
+	} else {
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+		wpa_s->sme.assoc_req_ie_len = 0;
+	}
+
+#ifdef CONFIG_IEEE80211R
+	ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
+	if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
+		md = ie + 2;
+	wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
+	if (md) {
+		/* Prepare for the next transition */
+		wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
+	}
+
+	if (md && wpa_key_mgmt_ft(ssid->key_mgmt)) {
+		if (wpa_s->sme.assoc_req_ie_len + 5 <
+		    sizeof(wpa_s->sme.assoc_req_ie)) {
+			struct rsn_mdie *mdie;
+			u8 *pos = wpa_s->sme.assoc_req_ie +
+				wpa_s->sme.assoc_req_ie_len;
+			*pos++ = WLAN_EID_MOBILITY_DOMAIN;
+			*pos++ = sizeof(*mdie);
+			mdie = (struct rsn_mdie *) pos;
+			os_memcpy(mdie->mobility_domain, md,
+				  MOBILITY_DOMAIN_ID_LEN);
+			mdie->ft_capab = md[MOBILITY_DOMAIN_ID_LEN];
+			wpa_s->sme.assoc_req_ie_len += 5;
+		}
+
+		if (wpa_s->sme.ft_used &&
+		    os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 &&
+		    wpa_sm_has_ptk(wpa_s->wpa)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT "
+				"over-the-air");
+			params.auth_alg = WPA_AUTH_ALG_FT;
+			params.ie = wpa_s->sme.ft_ies;
+			params.ie_len = wpa_s->sme.ft_ies_len;
+		}
+	}
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211W
+	wpa_s->sme.mfp = wpas_get_ssid_pmf(wpa_s, ssid);
+	if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION) {
+		const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+		struct wpa_ie_data _ie;
+		if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 &&
+		    _ie.capabilities &
+		    (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected AP supports "
+				"MFP: require MFP");
+			wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED;
+		}
+	}
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_P2P
+	if (wpa_s->global->p2p) {
+		u8 *pos;
+		size_t len;
+		int res;
+		pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
+		len = sizeof(wpa_s->sme.assoc_req_ie) -
+			wpa_s->sme.assoc_req_ie_len;
+		res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
+					    ssid->p2p_group);
+		if (res >= 0)
+			wpa_s->sme.assoc_req_ie_len += res;
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_HS20
+	if (is_hs20_network(wpa_s, ssid, bss)) {
+		struct wpabuf *hs20;
+		hs20 = wpabuf_alloc(20);
+		if (hs20) {
+			int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+			size_t len;
+
+			wpas_hs20_add_indication(hs20, pps_mo_id);
+			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 */
+
+#ifdef CONFIG_FST
+	if (wpa_s->fst_ies) {
+		int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
+
+		if (wpa_s->sme.assoc_req_ie_len + fst_ies_len <=
+		    sizeof(wpa_s->sme.assoc_req_ie)) {
+			os_memcpy(wpa_s->sme.assoc_req_ie +
+				  wpa_s->sme.assoc_req_ie_len,
+				  wpabuf_head(wpa_s->fst_ies),
+				  fst_ies_len);
+			wpa_s->sme.assoc_req_ie_len += fst_ies_len;
+		}
+	}
+#endif /* CONFIG_FST */
+
+	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)
+			pos += 2 + pos[1];
+		os_memmove(pos + ext_capab_len, pos,
+			   wpa_s->sme.assoc_req_ie_len -
+			   (pos - wpa_s->sme.assoc_req_ie));
+		wpa_s->sme.assoc_req_ie_len += ext_capab_len;
+		os_memcpy(pos, ext_capab, ext_capab_len);
+	}
+
+	if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
+		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
+		size_t len;
+
+		len = sizeof(wpa_s->sme.assoc_req_ie) -
+			wpa_s->sme.assoc_req_ie_len;
+		if (wpabuf_len(buf) <= len) {
+			os_memcpy(wpa_s->sme.assoc_req_ie +
+				  wpa_s->sme.assoc_req_ie_len,
+				  wpabuf_head(buf), wpabuf_len(buf));
+			wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf);
+		}
+	}
+
+	sme_auth_handle_rrm(wpa_s, bss);
+
+#ifdef CONFIG_SAE
+	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
+	    pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0) == 0)
+	{
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication");
+		params.auth_alg = WPA_AUTH_ALG_OPEN;
+		wpa_s->sme.sae_pmksa_caching = 1;
+	}
+
+	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) {
+		if (start)
+			resp = sme_auth_build_sae_commit(wpa_s, ssid,
+							 bss->bssid);
+		else
+			resp = sme_auth_build_sae_confirm(wpa_s);
+		if (resp == NULL) {
+			wpas_connection_failed(wpa_s, bss->bssid);
+			return;
+		}
+		params.sae_data = wpabuf_head(resp);
+		params.sae_data_len = wpabuf_len(resp);
+		wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED;
+	}
+#endif /* CONFIG_SAE */
+
+	wpa_supplicant_cancel_sched_scan(wpa_s);
+	wpa_supplicant_cancel_scan(wpa_s);
+
+	wpa_msg(wpa_s, MSG_INFO, "SME: Trying to authenticate with " MACSTR
+		" (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
+		wpa_ssid_txt(params.ssid, params.ssid_len), params.freq);
+
+	wpa_clear_keys(wpa_s, bss->bssid);
+	wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
+	old_ssid = wpa_s->current_ssid;
+	wpa_s->current_ssid = ssid;
+	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)
+		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 */
+
+	if (skip_auth) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"SME: Skip authentication step on reassoc-to-same-BSS");
+		wpabuf_free(resp);
+		sme_associate(wpa_s, ssid->mode, bss->bssid, WLAN_AUTH_OPEN);
+		return;
+	}
+
+
+	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 "
+			"driver failed");
+		wpas_connection_failed(wpa_s, bss->bssid);
+		wpa_supplicant_mark_disassoc(wpa_s);
+		wpabuf_free(resp);
+		wpas_connect_work_done(wpa_s);
+		return;
+	}
+
+	eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s,
+			       NULL);
+
+	/*
+	 * Association will be started based on the authentication event from
+	 * the driver.
+	 */
+
+	wpabuf_free(resp);
+}
+
+
+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 (cwork->bss_removed ||
+	    !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid) ||
+	    wpas_network_disabled(wpa_s, 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 */
+
+	if (radio_add_work(wpa_s, bss->freq, "sme-connect", 1,
+			   sme_auth_start_cb, cwork) < 0)
+		wpas_connect_work_free(cwork);
+}
+
+
+#ifdef CONFIG_SAE
+
+static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
+			u16 status_code, const u8 *data, size_t len)
+{
+	int *groups;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u "
+		"status code %u", auth_transaction, status_code);
+
+	if (auth_transaction == 1 &&
+	    status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
+	    wpa_s->sme.sae.state == SAE_COMMITTED &&
+	    wpa_s->current_bss && wpa_s->current_ssid) {
+		int default_groups[] = { 19, 20, 21, 25, 26, 0 };
+		u16 group;
+
+		groups = wpa_s->conf->sae_groups;
+		if (!groups || groups[0] <= 0)
+			groups = default_groups;
+
+		if (len < sizeof(le16)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"SME: Too short SAE anti-clogging token request");
+			return -1;
+		}
+		group = WPA_GET_LE16(data);
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"SME: SAE anti-clogging token requested (group %u)",
+			group);
+		if (sae_group_allowed(&wpa_s->sme.sae, groups, group) !=
+		    WLAN_STATUS_SUCCESS) {
+			wpa_dbg(wpa_s, MSG_ERROR,
+				"SME: SAE group %u of anti-clogging request is invalid",
+				group);
+			return -1;
+		}
+		wpabuf_free(wpa_s->sme.sae_token);
+		wpa_s->sme.sae_token = wpabuf_alloc_copy(data + sizeof(le16),
+							 len - sizeof(le16));
+		sme_send_authentication(wpa_s, wpa_s->current_bss,
+					wpa_s->current_ssid, 1);
+		return 0;
+	}
+
+	if (auth_transaction == 1 &&
+	    status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
+	    wpa_s->sme.sae.state == SAE_COMMITTED &&
+	    wpa_s->current_bss && wpa_s->current_ssid) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported");
+		wpa_s->sme.sae_group_index++;
+		if (sme_set_sae_group(wpa_s) < 0)
+			return -1; /* no other groups enabled */
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group");
+		sme_send_authentication(wpa_s, wpa_s->current_bss,
+					wpa_s->current_ssid, 1);
+		return 0;
+	}
+
+	if (status_code != WLAN_STATUS_SUCCESS)
+		return -1;
+
+	if (auth_transaction == 1) {
+		u16 res;
+
+		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;
+		res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
+				       groups);
+		if (res == SAE_SILENTLY_DISCARD) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Drop commit message due to reflection attack");
+			return 0;
+		}
+		if (res != WLAN_STATUS_SUCCESS)
+			return -1;
+
+		if (sae_process_commit(&wpa_s->sme.sae) < 0) {
+			wpa_printf(MSG_DEBUG, "SAE: Failed to process peer "
+				   "commit");
+			return -1;
+		}
+
+		wpabuf_free(wpa_s->sme.sae_token);
+		wpa_s->sme.sae_token = NULL;
+		sme_send_authentication(wpa_s, wpa_s->current_bss,
+					wpa_s->current_ssid, 0);
+		return 0;
+	} else if (auth_transaction == 2) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
+		if (wpa_s->sme.sae.state != SAE_CONFIRMED)
+			return -1;
+		if (sae_check_confirm(&wpa_s->sme.sae, data, len) < 0)
+			return -1;
+		wpa_s->sme.sae.state = SAE_ACCEPTED;
+		sae_clear_temp_data(&wpa_s->sme.sae);
+		return 1;
+	}
+
+	return -1;
+}
+#endif /* CONFIG_SAE */
+
+
+void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
+{
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (ssid == NULL) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event "
+			"when network is not selected");
+		return;
+	}
+
+	if (wpa_s->wpa_state != WPA_AUTHENTICATING) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event "
+			"when not in authenticating state");
+		return;
+	}
+
+	if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication with "
+			"unexpected peer " MACSTR,
+			MAC2STR(data->auth.peer));
+		return;
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication response: peer=" MACSTR
+		" auth_type=%d auth_transaction=%d status_code=%d",
+		MAC2STR(data->auth.peer), data->auth.auth_type,
+		data->auth.auth_transaction, data->auth.status_code);
+	wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs",
+		    data->auth.ies, data->auth.ies_len);
+
+	eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
+
+#ifdef CONFIG_SAE
+	if (data->auth.auth_type == WLAN_AUTH_SAE) {
+		int res;
+		res = sme_sae_auth(wpa_s, data->auth.auth_transaction,
+				   data->auth.status_code, data->auth.ies,
+				   data->auth.ies_len);
+		if (res < 0) {
+			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+
+		}
+		if (res != 1)
+			return;
+
+		wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for "
+			   "4-way handshake");
+		wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
+			       wpa_s->pending_bssid);
+	}
+#endif /* CONFIG_SAE */
+
+	if (data->auth.status_code != WLAN_STATUS_SUCCESS) {
+		char *ie_txt = NULL;
+
+		if (data->auth.ies && data->auth.ies_len) {
+			size_t buflen = 2 * data->auth.ies_len + 1;
+			ie_txt = os_malloc(buflen);
+			if (ie_txt) {
+				wpa_snprintf_hex(ie_txt, buflen, data->auth.ies,
+						 data->auth.ies_len);
+			}
+		}
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AUTH_REJECT MACSTR
+			" auth_type=%u auth_transaction=%u status_code=%u ie=%s",
+			MAC2STR(data->auth.peer), data->auth.auth_type,
+			data->auth.auth_transaction, data->auth.status_code,
+			ie_txt);
+		os_free(ie_txt);
+
+		if (data->auth.status_code !=
+		    WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG ||
+		    wpa_s->sme.auth_alg == data->auth.auth_type ||
+		    wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) {
+			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+			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;
+
+			wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying SHARED auth");
+			wpa_supplicant_associate(wpa_s, wpa_s->current_bss,
+						 wpa_s->current_ssid);
+			return;
+
+		case WLAN_AUTH_SHARED_KEY:
+			wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_LEAP;
+
+			wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying LEAP auth");
+			wpa_supplicant_associate(wpa_s, wpa_s->current_bss,
+						 wpa_s->current_ssid);
+			return;
+
+		default:
+			return;
+		}
+	}
+
+#ifdef CONFIG_IEEE80211R
+	if (data->auth.auth_type == WLAN_AUTH_FT) {
+		if (wpa_ft_process_response(wpa_s->wpa, data->auth.ies,
+					    data->auth.ies_len, 0,
+					    data->auth.peer, NULL, 0) < 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"SME: FT Authentication response processing failed");
+			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
+				MACSTR
+				" reason=%d locally_generated=1",
+				MAC2STR(wpa_s->pending_bssid),
+				WLAN_REASON_DEAUTH_LEAVING);
+			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+			wpa_supplicant_mark_disassoc(wpa_s);
+			return;
+		}
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	sme_associate(wpa_s, ssid->mode, data->auth.peer,
+		      data->auth.auth_type);
+}
+
+
+void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
+		   const u8 *bssid, u16 auth_type)
+{
+	struct wpa_driver_associate_params params;
+	struct ieee802_11_elems elems;
+#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 */
+
+	os_memset(&params, 0, sizeof(params));
+	params.bssid = bssid;
+	params.ssid = wpa_s->sme.ssid;
+	params.ssid_len = wpa_s->sme.ssid_len;
+	params.freq.freq = wpa_s->sme.freq;
+	params.bg_scan_period = wpa_s->current_ssid ?
+		wpa_s->current_ssid->bg_scan_period : -1;
+	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_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));
+	params.htcaps = (u8 *) &htcaps;
+	params.htcaps_mask = (u8 *) &htcaps_mask;
+	wpa_supplicant_apply_ht_overrides(wpa_s, wpa_s->current_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 CONFIG_IEEE80211R
+	if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
+		params.wpa_ie = wpa_s->sme.ft_ies;
+		params.wpa_ie_len = wpa_s->sme.ft_ies_len;
+	}
+#endif /* CONFIG_IEEE80211R */
+	params.mode = mode;
+	params.mgmt_frame_protection = wpa_s->sme.mfp;
+	params.rrm_used = wpa_s->rrm.rrm_used;
+	if (wpa_s->sme.prev_bssid_set)
+		params.prev_bssid = wpa_s->sme.prev_bssid;
+
+	wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
+		" (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
+		params.ssid ? wpa_ssid_txt(params.ssid, params.ssid_len) : "",
+		params.freq.freq);
+
+	wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
+
+	if (params.wpa_ie == NULL ||
+	    ieee802_11_parse_elems(params.wpa_ie, params.wpa_ie_len, &elems, 0)
+	    < 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Could not parse own IEs?!");
+		os_memset(&elems, 0, sizeof(elems));
+	}
+	if (elems.rsn_ie) {
+		params.wpa_proto = WPA_PROTO_RSN;
+		wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.rsn_ie - 2,
+					elems.rsn_ie_len + 2);
+	} else if (elems.wpa_ie) {
+		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)
+		params.p2p = 1;
+
+	if (wpa_s->parent->set_sta_uapsd)
+		params.uapsd = wpa_s->parent->sta_uapsd;
+	else
+		params.uapsd = -1;
+
+	if (wpa_drv_associate(wpa_s, &params) < 0) {
+		wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the "
+			"driver failed");
+		wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+		wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+		os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+		return;
+	}
+
+	eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s,
+			       NULL);
+}
+
+
+int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
+		      const u8 *ies, size_t ies_len)
+{
+	if (md == NULL || ies == NULL) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Remove mobility domain");
+		os_free(wpa_s->sme.ft_ies);
+		wpa_s->sme.ft_ies = NULL;
+		wpa_s->sme.ft_ies_len = 0;
+		wpa_s->sme.ft_used = 0;
+		return 0;
+	}
+
+	os_memcpy(wpa_s->sme.mobility_domain, md, MOBILITY_DOMAIN_ID_LEN);
+	wpa_hexdump(MSG_DEBUG, "SME: FT IEs", ies, ies_len);
+	os_free(wpa_s->sme.ft_ies);
+	wpa_s->sme.ft_ies = os_malloc(ies_len);
+	if (wpa_s->sme.ft_ies == NULL)
+		return -1;
+	os_memcpy(wpa_s->sme.ft_ies, ies, ies_len);
+	wpa_s->sme.ft_ies_len = ies_len;
+	return 0;
+}
+
+
+static void sme_deauth(struct wpa_supplicant *wpa_s)
+{
+	int bssid_changed;
+
+	bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
+
+	if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
+				   WLAN_REASON_DEAUTH_LEAVING) < 0) {
+		wpa_msg(wpa_s, MSG_INFO, "SME: Deauth request to the driver "
+			"failed");
+	}
+	wpa_s->sme.prev_bssid_set = 0;
+
+	wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+	wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+	os_memset(wpa_s->bssid, 0, ETH_ALEN);
+	os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+	if (bssid_changed)
+		wpas_notify_bssid_changed(wpa_s);
+}
+
+
+void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
+			    union wpa_event_data *data)
+{
+	wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association with " MACSTR " failed: "
+		"status code %d", MAC2STR(wpa_s->pending_bssid),
+		data->assoc_reject.status_code);
+
+	eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
+
+#ifdef CONFIG_SAE
+	if (wpa_s->sme.sae_pmksa_caching && wpa_s->current_ssid &&
+	    wpa_key_mgmt_sae(wpa_s->current_ssid->key_mgmt)) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"PMKSA caching attempt rejected - drop PMKSA cache entry and fall back to SAE authentication");
+		wpa_sm_aborted_cached(wpa_s->wpa);
+		wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid);
+		if (wpa_s->current_bss) {
+			struct wpa_bss *bss = wpa_s->current_bss;
+			struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+			wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
+					       WLAN_REASON_DEAUTH_LEAVING);
+			wpas_connect_work_done(wpa_s);
+			wpa_supplicant_mark_disassoc(wpa_s);
+			wpa_supplicant_connect(wpa_s, bss, ssid);
+			return;
+		}
+	}
+#endif /* CONFIG_SAE */
+
+	/*
+	 * For now, unconditionally terminate the previous authentication. In
+	 * theory, this should not be needed, but mac80211 gets quite confused
+	 * if the authentication is left pending.. Some roaming cases might
+	 * benefit from using the previous authentication, so this could be
+	 * optimized in the future.
+	 */
+	sme_deauth(wpa_s);
+}
+
+
+void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
+			      union wpa_event_data *data)
+{
+	wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication timed out");
+	wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+	wpa_supplicant_mark_disassoc(wpa_s);
+}
+
+
+void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
+			       union wpa_event_data *data)
+{
+	wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association timed out");
+	wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+	wpa_supplicant_mark_disassoc(wpa_s);
+}
+
+
+void sme_event_disassoc(struct wpa_supplicant *wpa_s,
+			struct disassoc_info *info)
+{
+	wpa_dbg(wpa_s, MSG_DEBUG, "SME: Disassociation event received");
+	if (wpa_s->sme.prev_bssid_set) {
+		/*
+		 * cfg80211/mac80211 can get into somewhat confused state if
+		 * the AP only disassociates us and leaves us in authenticated
+		 * state. For now, force the state to be cleared to avoid
+		 * confusing errors if we try to associate with the AP again.
+		 */
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Deauthenticate to clear "
+			"driver state");
+		wpa_drv_deauthenticate(wpa_s, wpa_s->sme.prev_bssid,
+				       WLAN_REASON_DEAUTH_LEAVING);
+	}
+}
+
+
+static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	if (wpa_s->wpa_state == WPA_AUTHENTICATING) {
+		wpa_msg(wpa_s, MSG_DEBUG, "SME: Authentication timeout");
+		sme_deauth(wpa_s);
+	}
+}
+
+
+static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	if (wpa_s->wpa_state == WPA_ASSOCIATING) {
+		wpa_msg(wpa_s, MSG_DEBUG, "SME: Association timeout");
+		sme_deauth(wpa_s);
+	}
+}
+
+
+void sme_state_changed(struct wpa_supplicant *wpa_s)
+{
+	/* Make sure timers are cleaned up appropriately. */
+	if (wpa_s->wpa_state != WPA_ASSOCIATING)
+		eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
+	if (wpa_s->wpa_state != WPA_AUTHENTICATING)
+		eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
+}
+
+
+void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
+				       const u8 *prev_pending_bssid)
+{
+	/*
+	 * mac80211-workaround to force deauth on failed auth cmd,
+	 * requires us to remain in authenticating state to allow the
+	 * second authentication attempt to be continued properly.
+	 */
+	wpa_dbg(wpa_s, MSG_DEBUG, "SME: Allow pending authentication "
+		"to proceed after disconnection event");
+	wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
+	os_memcpy(wpa_s->pending_bssid, prev_pending_bssid, ETH_ALEN);
+
+	/*
+	 * Re-arm authentication timer in case auth fails for whatever reason.
+	 */
+	eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
+	eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s,
+			       NULL);
+}
+
+
+void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->sme.prev_bssid_set = 0;
+#ifdef CONFIG_SAE
+	wpabuf_free(wpa_s->sme.sae_token);
+	wpa_s->sme.sae_token = NULL;
+	sae_clear_data(&wpa_s->sme.sae);
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_IEEE80211R
+	if (wpa_s->sme.ft_ies)
+		sme_update_ft_ies(wpa_s, NULL, NULL, 0);
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+void sme_deinit(struct wpa_supplicant *wpa_s)
+{
+	os_free(wpa_s->sme.ft_ies);
+	wpa_s->sme.ft_ies = NULL;
+	wpa_s->sme.ft_ies_len = 0;
+#ifdef CONFIG_IEEE80211W
+	sme_stop_sa_query(wpa_s);
+#endif /* CONFIG_IEEE80211W */
+	sme_clear_on_disassoc(wpa_s);
+
+	eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
+	eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
+	eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL);
+}
+
+
+static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s,
+				   const u8 *chan_list, u8 num_channels,
+				   u8 num_intol)
+{
+	struct ieee80211_2040_bss_coex_ie *bc_ie;
+	struct ieee80211_2040_intol_chan_report *ic_report;
+	struct wpabuf *buf;
+
+	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) +
+			   sizeof(struct ieee80211_2040_intol_chan_report) +
+			   num_channels);
+	if (buf == NULL)
+		return;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(buf, WLAN_PA_20_40_BSS_COEX);
+
+	bc_ie = wpabuf_put(buf, sizeof(*bc_ie));
+	bc_ie->element_id = WLAN_EID_20_40_BSS_COEXISTENCE;
+	bc_ie->length = 1;
+	if (num_intol)
+		bc_ie->coex_param |= WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ;
+
+	if (num_channels > 0) {
+		ic_report = wpabuf_put(buf, sizeof(*ic_report));
+		ic_report->element_id = WLAN_EID_20_40_BSS_INTOLERANT;
+		ic_report->length = num_channels + 1;
+		ic_report->op_class = 0;
+		os_memcpy(wpabuf_put(buf, num_channels), chan_list,
+			  num_channels);
+	}
+
+	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				wpa_s->own_addr, wpa_s->bssid,
+				wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"SME: Failed to send 20/40 BSS Coexistence frame");
+	}
+
+	wpabuf_free(buf);
+}
+
+
+int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss;
+	const u8 *ie;
+	u16 ht_cap;
+	u8 chan_list[P2P_MAX_CHANNELS], channel;
+	u8 num_channels = 0, num_intol = 0, i;
+
+	if (!wpa_s->sme.sched_obss_scan)
+		return 0;
+
+	wpa_s->sme.sched_obss_scan = 0;
+	if (!wpa_s->current_bss || wpa_s->wpa_state != WPA_COMPLETED)
+		return 1;
+
+	/*
+	 * Check whether AP uses regulatory triplet or channel triplet in
+	 * country info. Right now the operating class of the BSS channel
+	 * width trigger event is "unknown" (IEEE Std 802.11-2012 10.15.12),
+	 * based on the assumption that operating class triplet is not used in
+	 * beacon frame. If the First Channel Number/Operating Extension
+	 * Identifier octet has a positive integer value of 201 or greater,
+	 * then its operating class triplet.
+	 *
+	 * TODO: If Supported Operating Classes element is present in beacon
+	 * frame, have to lookup operating class in Annex E and fill them in
+	 * 2040 coex frame.
+	 */
+	ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY);
+	if (ie && (ie[1] >= 6) && (ie[5] >= 201))
+		return 1;
+
+	os_memset(chan_list, 0, sizeof(chan_list));
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		/* Skip other band bss */
+		enum hostapd_hw_mode mode;
+		mode = ieee80211_freq_to_chan(bss->freq, &channel);
+		if (mode != HOSTAPD_MODE_IEEE80211G &&
+		    mode != HOSTAPD_MODE_IEEE80211B)
+			continue;
+
+		ie = wpa_bss_get_ie(bss, WLAN_EID_HT_CAP);
+		ht_cap = (ie && (ie[1] == 26)) ? WPA_GET_LE16(ie + 2) : 0;
+		wpa_printf(MSG_DEBUG, "SME OBSS scan BSS " MACSTR
+			   " freq=%u chan=%u ht_cap=0x%x",
+			   MAC2STR(bss->bssid), bss->freq, channel, ht_cap);
+
+		if (!ht_cap || (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)) {
+			if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)
+				num_intol++;
+
+			/* Check whether the channel is already considered */
+			for (i = 0; i < num_channels; i++) {
+				if (channel == chan_list[i])
+					break;
+			}
+			if (i != num_channels)
+				continue;
+
+			chan_list[num_channels++] = channel;
+		}
+	}
+
+	sme_send_2040_bss_coex(wpa_s, chan_list, num_channels, num_intol);
+	return 1;
+}
+
+
+static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
+					  u16 num_modes,
+					  enum hostapd_hw_mode mode)
+{
+	u16 i;
+
+	for (i = 0; i < num_modes; i++) {
+		if (modes[i].mode == mode)
+			return &modes[i];
+	}
+
+	return NULL;
+}
+
+
+static void wpa_obss_scan_freqs_list(struct wpa_supplicant *wpa_s,
+				     struct wpa_driver_scan_params *params)
+{
+	/* Include only affected channels */
+	struct hostapd_hw_modes *mode;
+	int count, i;
+	int start, end;
+
+	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
+			HOSTAPD_MODE_IEEE80211G);
+	if (mode == NULL) {
+		/* No channels supported in this band - use empty list */
+		params->freqs = os_zalloc(sizeof(int));
+		return;
+	}
+
+	if (wpa_s->sme.ht_sec_chan == HT_SEC_CHAN_UNKNOWN &&
+	    wpa_s->current_bss) {
+		const u8 *ie;
+
+		ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_OPERATION);
+		if (ie && ie[1] >= 2) {
+			u8 o;
+
+			o = ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+			if (o == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+				wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_ABOVE;
+			else if (o == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+				wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_BELOW;
+		}
+	}
+
+	start = wpa_s->assoc_freq - 10;
+	end = wpa_s->assoc_freq + 10;
+	switch (wpa_s->sme.ht_sec_chan) {
+	case HT_SEC_CHAN_UNKNOWN:
+		/* HT40+ possible on channels 1..9 */
+		if (wpa_s->assoc_freq <= 2452)
+			start -= 20;
+		/* HT40- possible on channels 5-13 */
+		if (wpa_s->assoc_freq >= 2432)
+			end += 20;
+		break;
+	case HT_SEC_CHAN_ABOVE:
+		end += 20;
+		break;
+	case HT_SEC_CHAN_BELOW:
+		start -= 20;
+		break;
+	}
+	wpa_printf(MSG_DEBUG,
+		   "OBSS: assoc_freq %d possible affected range %d-%d",
+		   wpa_s->assoc_freq, start, end);
+
+	params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
+	if (params->freqs == NULL)
+		return;
+	for (count = 0, i = 0; i < mode->num_channels; i++) {
+		int freq;
+
+		if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+		freq = mode->channels[i].freq;
+		if (freq - 10 >= end || freq + 10 <= start)
+			continue; /* not affected */
+		params->freqs[count++] = freq;
+	}
+}
+
+
+static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct wpa_driver_scan_params params;
+
+	if (!wpa_s->current_bss) {
+		wpa_printf(MSG_DEBUG, "SME OBSS: Ignore scan request");
+		return;
+	}
+
+	os_memset(&params, 0, sizeof(params));
+	wpa_obss_scan_freqs_list(wpa_s, &params);
+	params.low_priority = 1;
+	wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan");
+
+	if (wpa_supplicant_trigger_scan(wpa_s, &params))
+		wpa_printf(MSG_DEBUG, "SME OBSS: Failed to trigger scan");
+	else
+		wpa_s->sme.sched_obss_scan = 1;
+	os_free(params.freqs);
+
+	eloop_register_timeout(wpa_s->sme.obss_scan_int, 0,
+			       sme_obss_scan_timeout, wpa_s, NULL);
+}
+
+
+void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable)
+{
+	const u8 *ie;
+	struct wpa_bss *bss = wpa_s->current_bss;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	struct hostapd_hw_modes *hw_mode = NULL;
+	int i;
+
+	eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL);
+	wpa_s->sme.sched_obss_scan = 0;
+	wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_UNKNOWN;
+	if (!enable)
+		return;
+
+	/*
+	 * Schedule OBSS scan if driver is using station SME in wpa_supplicant
+	 * or it expects OBSS scan to be performed by wpa_supplicant.
+	 */
+	if (!((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) ||
+	      (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OBSS_SCAN)) ||
+	    ssid == NULL || ssid->mode != IEEE80211_MODE_INFRA)
+		return;
+
+	if (!wpa_s->hw.modes)
+		return;
+
+	/* only HT caps in 11g mode are relevant */
+	for (i = 0; i < wpa_s->hw.num_modes; i++) {
+		hw_mode = &wpa_s->hw.modes[i];
+		if (hw_mode->mode == HOSTAPD_MODE_IEEE80211G)
+			break;
+	}
+
+	/* Driver does not support HT40 for 11g or doesn't have 11g. */
+	if (i == wpa_s->hw.num_modes || !hw_mode ||
+	    !(hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+		return;
+
+	if (bss == NULL || bss->freq < 2400 || bss->freq > 2500)
+		return; /* Not associated on 2.4 GHz band */
+
+	/* Check whether AP supports HT40 */
+	ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_CAP);
+	if (!ie || ie[1] < 2 ||
+	    !(WPA_GET_LE16(ie + 2) & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+		return; /* AP does not support HT40 */
+
+	ie = wpa_bss_get_ie(wpa_s->current_bss,
+			    WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS);
+	if (!ie || ie[1] < 14)
+		return; /* AP does not request OBSS scans */
+
+	wpa_s->sme.obss_scan_int = WPA_GET_LE16(ie + 6);
+	if (wpa_s->sme.obss_scan_int < 10) {
+		wpa_printf(MSG_DEBUG, "SME: Invalid OBSS Scan Interval %u "
+			   "replaced with the minimum 10 sec",
+			   wpa_s->sme.obss_scan_int);
+		wpa_s->sme.obss_scan_int = 10;
+	}
+	wpa_printf(MSG_DEBUG, "SME: OBSS Scan Interval %u sec",
+		   wpa_s->sme.obss_scan_int);
+	eloop_register_timeout(wpa_s->sme.obss_scan_int, 0,
+			       sme_obss_scan_timeout, wpa_s, NULL);
+}
+
+
+#ifdef CONFIG_IEEE80211W
+
+static const unsigned int sa_query_max_timeout = 1000;
+static const unsigned int sa_query_retry_timeout = 201;
+
+static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s)
+{
+	u32 tu;
+	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");
+		sme_stop_sa_query(wpa_s);
+		wpa_supplicant_deauthenticate(
+			wpa_s, WLAN_REASON_PREV_AUTH_NOT_VALID);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static void sme_send_sa_query_req(struct wpa_supplicant *wpa_s,
+				  const u8 *trans_id)
+{
+	u8 req[2 + WLAN_SA_QUERY_TR_ID_LEN];
+	wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Request to "
+		MACSTR, MAC2STR(wpa_s->bssid));
+	wpa_hexdump(MSG_DEBUG, "SME: SA Query Transaction ID",
+		    trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+	req[0] = WLAN_ACTION_SA_QUERY;
+	req[1] = WLAN_SA_QUERY_REQUEST;
+	os_memcpy(req + 2, trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				wpa_s->own_addr, wpa_s->bssid,
+				req, sizeof(req), 0) < 0)
+		wpa_msg(wpa_s, MSG_INFO, "SME: Failed to send SA Query "
+			"Request");
+}
+
+
+static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	unsigned int timeout, sec, usec;
+	u8 *trans_id, *nbuf;
+
+	if (wpa_s->sme.sa_query_count > 0 &&
+	    sme_check_sa_query_timeout(wpa_s))
+		return;
+
+	nbuf = os_realloc_array(wpa_s->sme.sa_query_trans_id,
+				wpa_s->sme.sa_query_count + 1,
+				WLAN_SA_QUERY_TR_ID_LEN);
+	if (nbuf == NULL)
+		return;
+	if (wpa_s->sme.sa_query_count == 0) {
+		/* Starting a new SA Query procedure */
+		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;
+	wpa_s->sme.sa_query_count++;
+
+	if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) {
+		wpa_printf(MSG_DEBUG, "Could not generate SA Query ID");
+		return;
+	}
+
+	timeout = sa_query_retry_timeout;
+	sec = ((timeout / 1000) * 1024) / 1000;
+	usec = (timeout % 1000) * 1024;
+	eloop_register_timeout(sec, usec, sme_sa_query_timer, wpa_s, NULL);
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association SA Query attempt %d",
+		wpa_s->sme.sa_query_count);
+
+	sme_send_sa_query_req(wpa_s, trans_id);
+}
+
+
+static void sme_start_sa_query(struct wpa_supplicant *wpa_s)
+{
+	sme_sa_query_timer(wpa_s, NULL);
+}
+
+
+static void sme_stop_sa_query(struct wpa_supplicant *wpa_s)
+{
+	eloop_cancel_timeout(sme_sa_query_timer, wpa_s, NULL);
+	os_free(wpa_s->sme.sa_query_trans_id);
+	wpa_s->sme.sa_query_trans_id = NULL;
+	wpa_s->sme.sa_query_count = 0;
+}
+
+
+void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
+				 const u8 *da, u16 reason_code)
+{
+	struct wpa_ssid *ssid;
+	struct os_reltime now;
+
+	if (wpa_s->wpa_state != WPA_COMPLETED)
+		return;
+	ssid = wpa_s->current_ssid;
+	if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION)
+		return;
+	if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
+		return;
+	if (reason_code != WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA &&
+	    reason_code != WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA)
+		return;
+	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);
+}
+
+
+void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
+		     const u8 *data, size_t len)
+{
+	int i;
+
+	if (wpa_s->sme.sa_query_trans_id == NULL ||
+	    len < 1 + WLAN_SA_QUERY_TR_ID_LEN ||
+	    data[0] != WLAN_SA_QUERY_RESPONSE)
+		return;
+	wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query response from "
+		MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]);
+
+	if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
+		return;
+
+	for (i = 0; i < wpa_s->sme.sa_query_count; i++) {
+		if (os_memcmp(wpa_s->sme.sa_query_trans_id +
+			      i * WLAN_SA_QUERY_TR_ID_LEN,
+			      data + 1, WLAN_SA_QUERY_TR_ID_LEN) == 0)
+			break;
+	}
+
+	if (i >= wpa_s->sme.sa_query_count) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: No matching SA Query "
+			"transaction identifier found");
+		return;
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reply to pending SA Query received "
+		"from " MACSTR, MAC2STR(sa));
+	sme_stop_sa_query(wpa_s);
+}
+
+#endif /* CONFIG_IEEE80211W */
diff --git a/hostap/wpa_supplicant/sme.h b/hostap/wpa_supplicant/sme.h
new file mode 100644
index 0000000..fd5c3b4
--- /dev/null
+++ b/hostap/wpa_supplicant/sme.h
@@ -0,0 +1,118 @@
+/*
+ * wpa_supplicant - SME
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SME_H
+#define SME_H
+
+#ifdef CONFIG_SME
+
+void sme_authenticate(struct wpa_supplicant *wpa_s,
+		      struct wpa_bss *bss, struct wpa_ssid *ssid);
+void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
+		   const u8 *bssid, u16 auth_type);
+void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data);
+int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
+		      const u8 *ies, size_t ies_len);
+void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
+			    union wpa_event_data *data);
+void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
+			      union wpa_event_data *data);
+void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
+			       union wpa_event_data *data);
+void sme_event_disassoc(struct wpa_supplicant *wpa_s,
+			struct disassoc_info *info);
+void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
+				 const u8 *da, u16 reason_code);
+void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
+		     const u8 *data, size_t len);
+void sme_state_changed(struct wpa_supplicant *wpa_s);
+void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
+				       const u8 *prev_pending_bssid);
+void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s);
+void sme_deinit(struct wpa_supplicant *wpa_s);
+
+int sme_proc_obss_scan(struct wpa_supplicant *wpa_s);
+void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable);
+
+#else /* CONFIG_SME */
+
+static inline void sme_authenticate(struct wpa_supplicant *wpa_s,
+				    struct wpa_bss *bss,
+				    struct wpa_ssid *ssid)
+{
+}
+
+static inline void sme_event_auth(struct wpa_supplicant *wpa_s,
+				  union wpa_event_data *data)
+{
+}
+
+static inline int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
+				    const u8 *ies, size_t ies_len)
+{
+	return -1;
+}
+
+
+static inline void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
+					  union wpa_event_data *data)
+{
+}
+
+static inline void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
+					    union wpa_event_data *data)
+{
+}
+
+static inline void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
+					     union wpa_event_data *data)
+{
+}
+
+static inline void sme_event_disassoc(struct wpa_supplicant *wpa_s,
+				      struct disassoc_info *info)
+{
+}
+
+static inline void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s,
+					       const u8 *sa, const u8 *da,
+					       u16 reason_code)
+{
+}
+
+static inline void sme_state_changed(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void
+sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
+				  const u8 *prev_pending_bssid)
+{
+}
+
+static inline void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void sme_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+
+static inline void sme_sched_obss_scan(struct wpa_supplicant *wpa_s,
+				       int enable)
+{
+}
+
+#endif /* CONFIG_SME */
+
+#endif /* SME_H */
diff --git a/hostap/wpa_supplicant/src b/hostap/wpa_supplicant/src
new file mode 120000
index 0000000..e057607
--- /dev/null
+++ b/hostap/wpa_supplicant/src
@@ -0,0 +1 @@
+../src/
\ No newline at end of file
diff --git a/hostap/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in b/hostap/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
new file mode 100644
index 0000000..03ac507
--- /dev/null
+++ b/hostap/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
@@ -0,0 +1,15 @@
+[Unit]
+Description=WPA supplicant daemon (interface- and nl80211 driver-specific version)
+Requires=sys-subsystem-net-devices-%i.device
+After=sys-subsystem-net-devices-%i.device
+Before=network.target
+Wants=network.target
+
+# NetworkManager users will probably want the dbus version instead.
+
+[Service]
+Type=simple
+ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-nl80211-%I.conf -Dnl80211 -i%I
+
+[Install]
+Alias=multi-user.target.wants/wpa_supplicant-nl80211@%i.service
diff --git a/hostap/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in b/hostap/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in
new file mode 100644
index 0000000..c8a744d
--- /dev/null
+++ b/hostap/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in
@@ -0,0 +1,15 @@
+[Unit]
+Description=WPA supplicant daemon (interface- and wired driver-specific version)
+Requires=sys-subsystem-net-devices-%i.device
+After=sys-subsystem-net-devices-%i.device
+Before=network.target
+Wants=network.target
+
+# NetworkManager users will probably want the dbus version instead.
+
+[Service]
+Type=simple
+ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-wired-%I.conf -Dwired -i%I
+
+[Install]
+Alias=multi-user.target.wants/wpa_supplicant-wired@%i.service
diff --git a/hostap/wpa_supplicant/systemd/wpa_supplicant.service.arg.in b/hostap/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
new file mode 100644
index 0000000..7788b38
--- /dev/null
+++ b/hostap/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
@@ -0,0 +1,15 @@
+[Unit]
+Description=WPA supplicant daemon (interface-specific version)
+Requires=sys-subsystem-net-devices-%i.device
+After=sys-subsystem-net-devices-%i.device
+Before=network.target
+Wants=network.target
+
+# NetworkManager users will probably want the dbus version instead.
+
+[Service]
+Type=simple
+ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-%I.conf -i%I
+
+[Install]
+Alias=multi-user.target.wants/wpa_supplicant@%i.service
diff --git a/hostap/wpa_supplicant/systemd/wpa_supplicant.service.in b/hostap/wpa_supplicant/systemd/wpa_supplicant.service.in
new file mode 100644
index 0000000..ea964ce
--- /dev/null
+++ b/hostap/wpa_supplicant/systemd/wpa_supplicant.service.in
@@ -0,0 +1,13 @@
+[Unit]
+Description=WPA supplicant
+Before=network.target
+Wants=network.target
+
+[Service]
+Type=dbus
+BusName=fi.epitest.hostap.WPASupplicant
+ExecStart=@BINDIR@/wpa_supplicant -u
+
+[Install]
+WantedBy=multi-user.target
+Alias=dbus-fi.epitest.hostap.WPASupplicant.service
diff --git a/hostap/wpa_supplicant/tests/link_test.c b/hostap/wpa_supplicant/tests/link_test.c
new file mode 100644
index 0000000..3bfbed5
--- /dev/null
+++ b/hostap/wpa_supplicant/tests/link_test.c
@@ -0,0 +1,83 @@
+/*
+ * Dummy functions to allow link_test to be linked. The need for these
+ * functions should be removed to allow IEEE 802.1X/EAPOL authenticator to
+ * be built outside hostapd.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+
+
+struct hostapd_data;
+struct sta_info;
+struct rsn_pmksa_cache_entry;
+struct eapol_state_machine;
+struct hostapd_eap_user;
+struct hostapd_bss_config;
+struct hostapd_vlan;
+
+
+struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+{
+	return NULL;
+}
+
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+		    int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+			      void *ctx),
+		    void *ctx)
+{
+	return 0;
+}
+
+
+void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+			    u32 session_timeout)
+{
+}
+
+
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+		     int old_vlanid)
+{
+	return 0;
+}
+
+
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+			  int success)
+{
+}
+
+
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+		      u8 *buf, size_t len)
+{
+}
+
+
+void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
+{
+}
+
+
+void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
+			       struct eapol_state_machine *eapol)
+{
+}
+
+
+const struct hostapd_eap_user *
+hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity,
+		     size_t identity_len, int phase2)
+{
+	return NULL;
+}
+
+
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
+{
+	return NULL;
+}
diff --git a/hostap/wpa_supplicant/tests/test_eap_sim_common.c b/hostap/wpa_supplicant/tests/test_eap_sim_common.c
new file mode 100644
index 0000000..f60b182
--- /dev/null
+++ b/hostap/wpa_supplicant/tests/test_eap_sim_common.c
@@ -0,0 +1,47 @@
+/*
+ * Test program for EAP-SIM PRF
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "eap_common/eap_sim_common.c"
+
+
+static int test_eap_sim_prf(void)
+{
+	/* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */
+	u8 xkey[] = {
+		0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b,
+		0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f,
+		0xeb, 0x5a, 0x38, 0xb6
+	};
+	u8 w[] = {
+		0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f,
+		0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49,
+		0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba,
+		0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78,
+		0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16
+	};
+	u8 buf[40];
+
+	printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n");
+	eap_sim_prf(xkey, buf, sizeof(buf));
+	if (memcmp(w, buf, sizeof(w)) != 0) {
+		printf("eap_sim_prf failed\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+	int errors = 0;
+
+	errors += test_eap_sim_prf();
+
+	return errors;
+}
diff --git a/hostap/wpa_supplicant/tests/test_wpa.c b/hostap/wpa_supplicant/tests/test_wpa.c
new file mode 100644
index 0000000..39971f2
--- /dev/null
+++ b/hostap/wpa_supplicant/tests/test_wpa.c
@@ -0,0 +1,369 @@
+/*
+ * Test program for combined WPA authenticator/supplicant
+ * Copyright (c) 2006-2007, 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 "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "../config.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/wpa_ie.h"
+#include "ap/wpa_auth.h"
+
+
+struct wpa {
+	u8 auth_addr[ETH_ALEN];
+	u8 supp_addr[ETH_ALEN];
+	u8 psk[PMK_LEN];
+
+	/* from authenticator */
+	u8 auth_eapol_dst[ETH_ALEN];
+	u8 *auth_eapol;
+	size_t auth_eapol_len;
+
+	/* from supplicant */
+	u8 *supp_eapol;
+	size_t supp_eapol_len;
+
+	struct wpa_sm *supp;
+	struct wpa_authenticator *auth_group;
+	struct wpa_state_machine *auth;
+
+	struct wpa_ssid ssid;
+	u8 supp_ie[80];
+	size_t supp_ie_len;
+};
+
+
+static int supp_get_bssid(void *ctx, u8 *bssid)
+{
+	struct wpa *wpa = ctx;
+	wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
+	os_memcpy(bssid, wpa->auth_addr, ETH_ALEN);
+	return 0;
+}
+
+
+static void supp_set_state(void *ctx, enum wpa_states state)
+{
+	wpa_printf(MSG_DEBUG, "SUPP: %s(state=%d)", __func__, state);
+}
+
+
+static void auth_eapol_rx(void *eloop_data, void *user_ctx)
+{
+	struct wpa *wpa = eloop_data;
+
+	wpa_printf(MSG_DEBUG, "AUTH: RX EAPOL frame");
+	wpa_receive(wpa->auth_group, wpa->auth, wpa->supp_eapol,
+		    wpa->supp_eapol_len);
+}
+
+
+static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
+			   size_t len)
+{
+	struct wpa *wpa = ctx;
+
+	wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x "
+		   "len=%lu)",
+		   __func__, MAC2STR(dest), proto, (unsigned long) len);
+
+	os_free(wpa->supp_eapol);
+	wpa->supp_eapol = os_malloc(len);
+	if (wpa->supp_eapol == NULL)
+		return -1;
+	os_memcpy(wpa->supp_eapol, buf, len);
+	wpa->supp_eapol_len = len;
+	eloop_register_timeout(0, 0, auth_eapol_rx, wpa, NULL);
+
+	return 0;
+}
+
+
+static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data,
+			     u16 data_len, size_t *msg_len, void **data_pos)
+{
+	struct ieee802_1x_hdr *hdr;
+
+	wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)",
+		   __func__, type, data_len);
+
+	*msg_len = sizeof(*hdr) + data_len;
+	hdr = os_malloc(*msg_len);
+	if (hdr == NULL)
+		return NULL;
+
+	hdr->version = 2;
+	hdr->type = type;
+	hdr->length = host_to_be16(data_len);
+
+	if (data)
+		os_memcpy(hdr + 1, data, data_len);
+	else
+		os_memset(hdr + 1, 0, data_len);
+
+	if (data_pos)
+		*data_pos = hdr + 1;
+
+	return (u8 *) hdr;
+}
+
+
+static int supp_get_beacon_ie(void *ctx)
+{
+	struct wpa *wpa = ctx;
+	const u8 *ie;
+	size_t ielen;
+
+	wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
+
+	ie = wpa_auth_get_wpa_ie(wpa->auth_group, &ielen);
+	if (ie == NULL || ielen < 1)
+		return -1;
+	if (ie[0] == WLAN_EID_RSN)
+		return wpa_sm_set_ap_rsn_ie(wpa->supp, ie, 2 + ie[1]);
+	return wpa_sm_set_ap_wpa_ie(wpa->supp, ie, 2 + ie[1]);
+}
+
+
+static int supp_set_key(void *ctx, enum wpa_alg alg,
+			const u8 *addr, int key_idx, int set_tx,
+			const u8 *seq, size_t seq_len,
+			const u8 *key, size_t key_len)
+{
+	wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d "
+		   "set_tx=%d)",
+		   __func__, alg, MAC2STR(addr), key_idx, set_tx);
+	wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len);
+	wpa_hexdump(MSG_DEBUG, "SUPP: set_key - key", key, key_len);
+	return 0;
+}
+
+
+static int supp_mlme_setprotection(void *ctx, const u8 *addr,
+				   int protection_type, int key_type)
+{
+	wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d "
+		   "key_type=%d)",
+		   __func__, MAC2STR(addr), protection_type, key_type);
+	return 0;
+}
+
+
+static void supp_cancel_auth_timeout(void *ctx)
+{
+	wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
+}
+
+
+static int supp_init(struct wpa *wpa)
+{
+	struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return -1;
+
+	ctx->ctx = wpa;
+	ctx->msg_ctx = wpa;
+	ctx->set_state = supp_set_state;
+	ctx->get_bssid = supp_get_bssid;
+	ctx->ether_send = supp_ether_send;
+	ctx->get_beacon_ie = supp_get_beacon_ie;
+	ctx->alloc_eapol = supp_alloc_eapol;
+	ctx->set_key = supp_set_key;
+	ctx->mlme_setprotection = supp_mlme_setprotection;
+	ctx->cancel_auth_timeout = supp_cancel_auth_timeout;
+	wpa->supp = wpa_sm_init(ctx);
+	if (wpa->supp == NULL) {
+		wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed");
+		return -1;
+	}
+
+	wpa_sm_set_own_addr(wpa->supp, wpa->supp_addr);
+	wpa_sm_set_param(wpa->supp, WPA_PARAM_RSN_ENABLED, 1);
+	wpa_sm_set_param(wpa->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN);
+	wpa_sm_set_param(wpa->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP);
+	wpa_sm_set_param(wpa->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP);
+	wpa_sm_set_param(wpa->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK);
+	wpa_sm_set_pmk(wpa->supp, wpa->psk, PMK_LEN);
+
+	wpa->supp_ie_len = sizeof(wpa->supp_ie);
+	if (wpa_sm_set_assoc_wpa_ie_default(wpa->supp, wpa->supp_ie,
+					    &wpa->supp_ie_len) < 0) {
+		wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()"
+			   " failed");
+		return -1;
+	}
+
+	wpa_sm_notify_assoc(wpa->supp, wpa->auth_addr);
+
+	return 0;
+}
+
+
+static void auth_logger(void *ctx, const u8 *addr, logger_level level,
+			const char *txt)
+{
+	if (addr)
+		wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s",
+			   MAC2STR(addr), txt);
+	else
+		wpa_printf(MSG_DEBUG, "AUTH: %s", txt);
+}
+
+
+static void supp_eapol_rx(void *eloop_data, void *user_ctx)
+{
+	struct wpa *wpa = eloop_data;
+
+	wpa_printf(MSG_DEBUG, "SUPP: RX EAPOL frame");
+	wpa_sm_rx_eapol(wpa->supp, wpa->auth_addr, wpa->auth_eapol,
+			wpa->auth_eapol_len);
+}
+
+
+static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data,
+			   size_t data_len, int encrypt)
+{
+	struct wpa *wpa = ctx;
+
+	wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu "
+		   "encrypt=%d)",
+		   __func__, MAC2STR(addr), (unsigned long) data_len, encrypt);
+
+	os_free(wpa->auth_eapol);
+	wpa->auth_eapol = os_malloc(data_len);
+	if (wpa->auth_eapol == NULL)
+		return -1;
+	os_memcpy(wpa->auth_eapol_dst, addr, ETH_ALEN);
+	os_memcpy(wpa->auth_eapol, data, data_len);
+	wpa->auth_eapol_len = data_len;
+	eloop_register_timeout(0, 0, supp_eapol_rx, wpa, NULL);
+
+	return 0;
+}
+
+
+static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk)
+{
+	struct wpa *wpa = ctx;
+	wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
+		   __func__, MAC2STR(addr), prev_psk);
+	if (prev_psk)
+		return NULL;
+	return wpa->psk;
+}
+
+
+static int auth_init_group(struct wpa *wpa)
+{
+	struct wpa_auth_config conf;
+	struct wpa_auth_callbacks cb;
+
+	wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
+
+	os_memset(&conf, 0, sizeof(conf));
+	conf.wpa = 2;
+	conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+	conf.wpa_pairwise = WPA_CIPHER_CCMP;
+	conf.rsn_pairwise = WPA_CIPHER_CCMP;
+	conf.wpa_group = WPA_CIPHER_CCMP;
+	conf.eapol_version = 2;
+
+	os_memset(&cb, 0, sizeof(cb));
+	cb.ctx = wpa;
+	cb.logger = auth_logger;
+	cb.send_eapol = auth_send_eapol;
+	cb.get_psk = auth_get_psk;
+
+	wpa->auth_group = wpa_init(wpa->auth_addr, &conf, &cb);
+	if (wpa->auth_group == NULL) {
+		wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int auth_init(struct wpa *wpa)
+{
+	wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr, NULL);
+	if (wpa->auth == NULL) {
+		wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed");
+		return -1;
+	}
+
+	if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, wpa->supp_ie,
+				wpa->supp_ie_len, NULL, 0) != WPA_IE_OK) {
+		wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
+		return -1;
+	}
+
+	wpa_auth_sm_event(wpa->auth, WPA_ASSOC);
+
+	wpa_auth_sta_associated(wpa->auth_group, wpa->auth);
+
+	return 0;
+}
+
+
+static void deinit(struct wpa *wpa)
+{
+	wpa_auth_sta_deinit(wpa->auth);
+	wpa_sm_deinit(wpa->supp);
+	wpa_deinit(wpa->auth_group);
+	os_free(wpa->auth_eapol);
+	wpa->auth_eapol = NULL;
+	os_free(wpa->supp_eapol);
+	wpa->supp_eapol = NULL;
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct wpa wpa;
+
+	if (os_program_init())
+		return -1;
+
+	os_memset(&wpa, 0, sizeof(wpa));
+	os_memset(wpa.auth_addr, 0x12, ETH_ALEN);
+	os_memset(wpa.supp_addr, 0x32, ETH_ALEN);
+	os_memset(wpa.psk, 0x44, PMK_LEN);
+
+	wpa_debug_level = 0;
+	wpa_debug_show_keys = 1;
+
+	if (eloop_init()) {
+		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+		return -1;
+	}
+
+	if (auth_init_group(&wpa) < 0)
+		return -1;
+
+	if (supp_init(&wpa) < 0)
+		return -1;
+
+	if (auth_init(&wpa) < 0)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "Starting eloop");
+	eloop_run();
+	wpa_printf(MSG_DEBUG, "eloop done");
+
+	deinit(&wpa);
+
+	eloop_destroy();
+
+	os_program_deinit();
+
+	return 0;
+}
diff --git a/hostap/wpa_supplicant/todo.txt b/hostap/wpa_supplicant/todo.txt
new file mode 100644
index 0000000..4c9f98e
--- /dev/null
+++ b/hostap/wpa_supplicant/todo.txt
@@ -0,0 +1,78 @@
+To do:
+- add support for WPA with ap_scan=0 (update selected cipher etc. based on
+  AssocInfo; make sure these match with configuration)
+- consider closing smart card / PCSC connection when EAP-SIM/EAP-AKA
+  authentication has been completed (cache scard data based on serial#(?)
+  and try to optimize next connection if the same card is present for next
+  auth)
+- if driver/hw is not WPA2 capable, must remove WPA_PROTO_RSN flag from
+  ssid->proto fields to avoid detecting downgrade attacks when the driver
+  is not reporting RSN IE, but msg 3/4 has one
+- Cisco AP and non-zero keyidx for unicast -> map to broadcast
+  (actually, this already works with driver_ndis; so maybe just change
+  driver_*.c to do the mapping for drivers that cannot handle non-zero keyidx
+  for unicast); worked also with Host AP driver and madwifi
+- IEEE 802.1X and key update with driver_ndis?? wpa_supplicant did not seem
+  to see unencrypted EAPOL-Key frames at all..
+- EAP-PAX with PAX_SEC
+- EAP (RFC 3748)
+  * OTP Extended Responses (Sect. 5.5)
+- test what happens if authenticator sends EAP-Success before real EAP
+  authentication ("canned" Success); this should be ignored based on
+  RFC 3748 Sect. 4.2
+- test compilation with gcc -W options (more warnings?)
+  (Done once; number of unused function arguments still present)
+- ctrl_iface: get/remove blob
+- use doc/docbook/*.sgml and docbook2{txt,html,pdf} to replace README and
+  web pages including the same information.. i.e., have this information only
+  in one page; how to build a PDF file with all the SGML included?
+- EAP-POTP/RSA SecurID profile (RFC 4793)
+- document wpa_gui build and consider adding it to 'make install'
+- consider merging hostapd and wpa_supplicant PMKSA cache implementations
+- consider redesigning pending EAP requests (identity/password/otp from
+  ctrl_iface) by moving the retrying of the previous request into EAP
+  state machine so that EAPOL state machine is not needed for this
+- rfc4284.txt (network selection for eap)
+- www pages about configuring wpa_supplicant:
+  * global options (ap_scan, ctrl_interfaces) based on OS/driver
+  * network block
+  * key_mgmt selection
+  * WPA parameters
+  * EAP options (one page for each method)
+  * "configuration wizard" (step 1: select OS, step 2: select driver, ...) to
+    generate example configuration
+- error path in rsn_preauth_init: should probably deinit l2_packet handlers
+  if something fails; does something else need deinit?
+- consider moving SIM card functionality (IMSI fetching) away from eap.c;
+  this should likely happen before EAP is initialized for authentication;
+  now IMSI is read only after receiving EAP-Identity/Request, but since it is
+  really needed for all cases, reading IMSI and generating Identity string
+  could very well be done before EAP has been started
+- try to work around race in receiving association event and first EAPOL
+  message
+- try to work around race in configuring PTK and sending msg 4/4 (some NDIS
+  drivers with ndiswrapper end up not being able to complete 4-way handshake
+  in some cases; extra delay before setting the key seems to help)
+- make sure that TLS session cache is not shared between EAP types or if it
+  is, that the cache entries are bound to only one EAP type; e.g., cache entry
+  created with EAP-TLS must not be allowed to do fast re-auth with EAP-TTLS
+- consider moving eap_peer_tls_build_ack() call into
+  eap_peer_tls_process_helper()
+  (it seems to be called always if helper returns 1)
+  * could need to modify eap_{ttls,peap,fast}_decrypt to do same
+- add support for fetching full user cert chain from Windows certificate
+  stores even when there are intermediate CA certs that are not in the
+  configured ca_cert store (e.g., ROOT) (they could be, e.g., in CA store)
+- clean up common.[ch]
+- change TLS/crypto library interface to use a structure of function
+  pointers and helper inline functions (like driver_ops) instead of
+  requiring every TLS wrapper to implement all functions
+- add support for encrypted configuration fields (e.g., password, psk,
+  passphrase, pin)
+- wpa_gui: add support for setting and showing priority
+- cleanup TLS/PEAP/TTLS/FAST fragmentation: both the handshake and Appl. Data
+  phases should be able to use the same functions for this;
+  the last step in processing sent should be this code and rest of the code
+  should not need to care about fragmentation at all
+- test EAP-FAST peer with OpenSSL and verify that fallback to full handshake
+  (ServerHello followed by something else than ChangeCipherSpec)
diff --git a/hostap/wpa_supplicant/utils/log2pcap.py b/hostap/wpa_supplicant/utils/log2pcap.py
new file mode 100755
index 0000000..65e2fa1
--- /dev/null
+++ b/hostap/wpa_supplicant/utils/log2pcap.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2012, Intel Corporation
+#
+# Author: Johannes Berg <johannes@sipsolutions.net>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import sys, struct, re
+
+def write_pcap_header(pcap_file):
+    pcap_file.write(
+        struct.pack('<IHHIIII',
+                    0xa1b2c3d4, 2, 4, 0, 0, 65535,
+                    105 # raw 802.11 format
+                    ))
+
+def pcap_addpacket(pcap_file, ts, data):
+    # ts in seconds, float
+    pcap_file.write(struct.pack('<IIII',
+        int(ts), int(1000000 * ts) % 1000000,
+        len(data), len(data)))
+    pcap_file.write(data)
+
+if __name__ == "__main__":
+    try:
+        input = sys.argv[1]
+        pcap = sys.argv[2]
+    except IndexError:
+        print "Usage: %s <log file> <pcap file>" % sys.argv[0]
+        sys.exit(2)
+
+    input_file = open(input, 'r')
+    pcap_file = open(pcap, 'w')
+    frame_re = re.compile(r'(([0-9]+.[0-9]{6}):\s*)?nl80211: MLME event frame - hexdump\(len=[0-9]*\):((\s*[0-9a-fA-F]{2})*)')
+
+    write_pcap_header(pcap_file)
+
+    for line in input_file:
+        m = frame_re.match(line)
+        if m is None:
+            continue
+        if m.group(2):
+            ts = float(m.group(2))
+        else:
+            ts = 0
+        hexdata = m.group(3)
+        hexdata = hexdata.split()
+        data = ''.join([chr(int(x, 16)) for x in hexdata])
+        pcap_addpacket(pcap_file, ts, data)
+
+    input_file.close()
+    pcap_file.close()
diff --git a/hostap/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj b/hostap/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj
new file mode 100755
index 0000000..af7b3fe
--- /dev/null
+++ b/hostap/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj
@@ -0,0 +1,473 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="eapol_test"

+	ProjectGUID="{0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}"

+	RootNamespace="eapol_test"

+	Keyword="Win32Proj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"

+			IntermediateDirectory="$(ConfigurationName)"

+			ConfigurationType="1"

+			CharacterSet="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"

+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"

+				MinimalRebuild="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="3"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="4"

+				DisableSpecificWarnings="4244;4267;4311"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"

+				LinkIncremental="2"

+				AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"

+				GenerateDebugInformation="true"

+				SubSystem="1"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"

+			IntermediateDirectory="$(ConfigurationName)"

+			ConfigurationType="1"

+			CharacterSet="1"

+			WholeProgramOptimization="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"

+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"

+				RuntimeLibrary="2"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				DisableSpecificWarnings="4244;4267;4311"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"

+				LinkIncremental="1"

+				AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"

+				GenerateDebugInformation="true"

+				SubSystem="1"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"

+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"

+			>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-cbc.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-ctr.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-eax.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-encblock.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-omac1.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-unwrap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-wrap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\base64.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\blacklist.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\bss.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_common\chap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\config.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\config_file.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\crypto_openssl.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\ctrl_iface.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\ctrl_iface_named_pipe.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\drivers\driver_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_aka.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_common\eap_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_gtc.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_leap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_md5.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_methods.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_otp.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_peap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_common\eap_peap_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\eap_register.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_sim.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_common\eap_sim_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_tls.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_tls_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_tnc.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_ttls.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\eapol_test.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\eloop_win.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\events.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\fips_prf_openssl.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\ip_addr.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\md5.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\ms_funcs.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\mschapv2.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\notify.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\os_win32.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\pcsc_funcs.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\peerkey.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\preauth.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\radius\radius.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\radius\radius_client.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\scan.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1-prf.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1-tlsprf.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\tls_openssl.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\tncc.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\wpa.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\common\wpa_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\wpa_debug.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\wpa_ie.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\wpa_supplicant.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\wpabuf.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\wpas_glue.c"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc;xsd"

+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"

+			>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"

+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"

+			>

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/hostap/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj b/hostap/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj
new file mode 100755
index 0000000..e79fc0f
--- /dev/null
+++ b/hostap/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="win_if_list"

+	ProjectGUID="{9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}"

+	RootNamespace="win_if_list"

+	Keyword="Win32Proj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"

+			IntermediateDirectory="$(ConfigurationName)"

+			ConfigurationType="1"

+			CharacterSet="0"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="..\..\..\src\utils;C:\dev\WpdPack\include"

+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"

+				MinimalRebuild="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="3"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="4"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="wpcap.lib"

+				LinkIncremental="2"

+				AdditionalLibraryDirectories="C:\dev\WpdPack\lib"

+				GenerateDebugInformation="true"

+				SubSystem="1"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"

+			IntermediateDirectory="$(ConfigurationName)"

+			ConfigurationType="1"

+			CharacterSet="0"

+			WholeProgramOptimization="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="..\..\..\src\utils;C:\dev\WpdPack\include"

+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"

+				RuntimeLibrary="2"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="wpcap.lib"

+				LinkIncremental="1"

+				AdditionalLibraryDirectories="C:\dev\WpdPack\lib"

+				GenerateDebugInformation="true"

+				SubSystem="1"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"

+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"

+			>

+			<File

+				RelativePath="..\..\win_if_list.c"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc;xsd"

+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"

+			>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"

+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"

+			>

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/hostap/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj b/hostap/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj
new file mode 100755
index 0000000..d2de768
--- /dev/null
+++ b/hostap/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="wpa_cli"

+	ProjectGUID="{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}"

+	RootNamespace="wpa_cli"

+	Keyword="Win32Proj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"

+			IntermediateDirectory="$(ConfigurationName)"

+			ConfigurationType="1"

+			CharacterSet="0"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils"

+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"

+				MinimalRebuild="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="3"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="4"

+				DisableSpecificWarnings="4244;4267"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="ws2_32.lib"

+				LinkIncremental="2"

+				GenerateDebugInformation="true"

+				SubSystem="1"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"

+			IntermediateDirectory="$(ConfigurationName)"

+			ConfigurationType="1"

+			CharacterSet="0"

+			WholeProgramOptimization="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils"

+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"

+				RuntimeLibrary="2"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				DisableSpecificWarnings="4244;4267"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="ws2_32.lib"

+				LinkIncremental="1"

+				GenerateDebugInformation="true"

+				SubSystem="1"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"

+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"

+			>

+			<File

+				RelativePath="..\..\..\src\utils\common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\os_win32.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\wpa_cli.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\common\wpa_ctrl.c"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc;xsd"

+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"

+			>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"

+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"

+			>

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/hostap/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj b/hostap/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj
new file mode 100755
index 0000000..97aa2c5
--- /dev/null
+++ b/hostap/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj
@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="wpa_passphrase"

+	ProjectGUID="{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}"

+	RootNamespace="wpa_passphrase"

+	Keyword="Win32Proj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"

+			IntermediateDirectory="$(ConfigurationName)"

+			ConfigurationType="1"

+			CharacterSet="0"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils;C:\dev\openssl\include"

+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"

+				MinimalRebuild="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="3"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="4"

+				DisableSpecificWarnings="4244;4267"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="ws2_32.lib"

+				LinkIncremental="2"

+				AdditionalLibraryDirectories=""

+				GenerateDebugInformation="true"

+				SubSystem="1"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"

+			IntermediateDirectory="$(ConfigurationName)"

+			ConfigurationType="1"

+			CharacterSet="0"

+			WholeProgramOptimization="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils;C:\dev\openssl\include"

+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"

+				RuntimeLibrary="2"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				DisableSpecificWarnings="4244;4267"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="ws2_32.lib"

+				LinkIncremental="1"

+				GenerateDebugInformation="true"

+				SubSystem="1"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"

+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"

+			>

+			<File

+				RelativePath="..\..\..\src\utils\common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\md5.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\md5-internal.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\os_win32.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1-internal.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1-prf.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\wpa_passphrase.c"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc;xsd"

+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"

+			>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"

+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"

+			>

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/hostap/wpa_supplicant/vs2005/wpa_supplicant.sln b/hostap/wpa_supplicant/vs2005/wpa_supplicant.sln
new file mode 100755
index 0000000..df89e31
--- /dev/null
+++ b/hostap/wpa_supplicant/vs2005/wpa_supplicant.sln
@@ -0,0 +1,52 @@
+

+Microsoft Visual Studio Solution File, Format Version 9.00

+# Visual Studio 2005

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_supplicant", "wpa_supplicant\wpa_supplicant.vcproj", "{8BCFDA77-AEDC-4168-8897-5B73105BBB87}"

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_cli", "wpa_cli\wpa_cli.vcproj", "{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}"

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpasvc", "wpasvc\wpasvc.vcproj", "{E2A4A85F-CA77-406D-8ABF-63EF94545ACC}"

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_passphrase", "wpa_passphrase\wpa_passphrase.vcproj", "{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}"

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "win_if_list", "win_if_list\win_if_list.vcproj", "{9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}"

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "eapol_test", "eapol_test\eapol_test.vcproj", "{0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}"

+EndProject

+Global

+	GlobalSection(DPCodeReviewSolutionGUID) = preSolution

+		DPCodeReviewSolutionGUID = {00000000-0000-0000-0000-000000000000}

+	EndGlobalSection

+	GlobalSection(SolutionConfigurationPlatforms) = preSolution

+		Debug|Win32 = Debug|Win32

+		Release|Win32 = Release|Win32

+	EndGlobalSection

+	GlobalSection(ProjectConfigurationPlatforms) = postSolution

+		{8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Debug|Win32.ActiveCfg = Debug|Win32

+		{8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Debug|Win32.Build.0 = Debug|Win32

+		{8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Release|Win32.ActiveCfg = Release|Win32

+		{8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Release|Win32.Build.0 = Release|Win32

+		{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Debug|Win32.ActiveCfg = Debug|Win32

+		{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Debug|Win32.Build.0 = Debug|Win32

+		{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Release|Win32.ActiveCfg = Release|Win32

+		{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Release|Win32.Build.0 = Release|Win32

+		{E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Debug|Win32.ActiveCfg = Debug|Win32

+		{E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Debug|Win32.Build.0 = Debug|Win32

+		{E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Release|Win32.ActiveCfg = Release|Win32

+		{E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Release|Win32.Build.0 = Release|Win32

+		{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Debug|Win32.ActiveCfg = Debug|Win32

+		{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Debug|Win32.Build.0 = Debug|Win32

+		{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Release|Win32.ActiveCfg = Release|Win32

+		{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Release|Win32.Build.0 = Release|Win32

+		{9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Debug|Win32.ActiveCfg = Debug|Win32

+		{9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Debug|Win32.Build.0 = Debug|Win32

+		{9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Release|Win32.ActiveCfg = Release|Win32

+		{9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Release|Win32.Build.0 = Release|Win32

+		{0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Debug|Win32.ActiveCfg = Debug|Win32

+		{0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Debug|Win32.Build.0 = Debug|Win32

+		{0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Release|Win32.ActiveCfg = Release|Win32

+	EndGlobalSection

+	GlobalSection(SolutionProperties) = preSolution

+		HideSolutionNode = FALSE

+	EndGlobalSection

+EndGlobal

diff --git a/hostap/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj b/hostap/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj
new file mode 100755
index 0000000..51acab9
--- /dev/null
+++ b/hostap/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj
@@ -0,0 +1,461 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="wpa_supplicant"

+	ProjectGUID="{8BCFDA77-AEDC-4168-8897-5B73105BBB87}"

+	RootNamespace="wpa_supplicant"

+	Keyword="Win32Proj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"

+			IntermediateDirectory="$(ConfigurationName)"

+			ConfigurationType="1"

+			CharacterSet="0"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"

+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"

+				MinimalRebuild="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="3"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="4"

+				DisableSpecificWarnings="4244;4267;4311"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"

+				LinkIncremental="2"

+				AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"

+				GenerateDebugInformation="true"

+				SubSystem="1"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"

+			IntermediateDirectory="$(ConfigurationName)"

+			ConfigurationType="1"

+			CharacterSet="0"

+			WholeProgramOptimization="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"

+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"

+				RuntimeLibrary="2"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				DisableSpecificWarnings="4244;4267;4311"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"

+				LinkIncremental="1"

+				AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"

+				GenerateDebugInformation="true"

+				SubSystem="1"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"

+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"

+			>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-cbc.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-ctr.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-eax.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-encblock.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-omac1.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-unwrap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-wrap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\base64.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\blacklist.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\bss.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_common\chap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\config.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\config_file.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\crypto_openssl.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\ctrl_iface.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\ctrl_iface_named_pipe.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\drivers\driver_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\drivers\driver_ndis.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\drivers\driver_ndis_.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\drivers\drivers.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_common\eap_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_gtc.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_leap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_md5.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_methods.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_otp.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_peap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_common\eap_peap_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\eap_register.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_tls.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_tls_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_tnc.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_ttls.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\eloop_win.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\events.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\main.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\md5.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\ms_funcs.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\mschapv2.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\drivers\ndis_events.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\notify.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\os_win32.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\pcsc_funcs.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\peerkey.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\preauth.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\scan.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1-prf.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1-tlsprf.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\tls_openssl.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\tncc.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\wpa.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\common\wpa_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\wpa_debug.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\wpa_ie.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\wpa_supplicant.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\wpabuf.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\wpas_glue.c"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc;xsd"

+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"

+			>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"

+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"

+			>

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/hostap/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj b/hostap/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj
new file mode 100755
index 0000000..6fd8af8
--- /dev/null
+++ b/hostap/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj
@@ -0,0 +1,461 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="wpasvc"

+	ProjectGUID="{E2A4A85F-CA77-406D-8ABF-63EF94545ACC}"

+	RootNamespace="wpasvc"

+	Keyword="Win32Proj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"

+			IntermediateDirectory="$(ConfigurationName)"

+			ConfigurationType="1"

+			CharacterSet="0"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"

+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"

+				MinimalRebuild="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="3"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="4"

+				DisableSpecificWarnings="4244;4267;4311"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"

+				LinkIncremental="2"

+				AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"

+				GenerateDebugInformation="true"

+				SubSystem="1"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"

+			IntermediateDirectory="$(ConfigurationName)"

+			ConfigurationType="1"

+			CharacterSet="0"

+			WholeProgramOptimization="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"

+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"

+				RuntimeLibrary="2"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				DisableSpecificWarnings="4244;4267;4311"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"

+				LinkIncremental="1"

+				AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"

+				GenerateDebugInformation="true"

+				SubSystem="1"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"

+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"

+			>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-cbc.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-ctr.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-eax.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-encblock.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-omac1.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-unwrap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\aes-wrap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\base64.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\blacklist.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\bss.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_common\chap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\config.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\config_winreg.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\crypto_openssl.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\ctrl_iface.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\ctrl_iface_named_pipe.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\drivers\driver_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\drivers\driver_ndis.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\drivers\driver_ndis_.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\drivers\drivers.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_common\eap_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_gtc.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_leap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_md5.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_methods.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_otp.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_peap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_common\eap_peap_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\eap_register.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_tls.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_tls_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_tnc.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\eap_ttls.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\eloop_win.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\events.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\main_winsvc.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\md5.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\ms_funcs.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\mschapv2.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\drivers\ndis_events.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\notify.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\os_win32.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\pcsc_funcs.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\peerkey.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\preauth.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\scan.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1-prf.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\sha1-tlsprf.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\crypto\tls_openssl.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\eap_peer\tncc.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\wpa.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\common\wpa_common.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\wpa_debug.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\rsn_supp\wpa_ie.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\wpa_supplicant.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\src\utils\wpabuf.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\wpas_glue.c"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc;xsd"

+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"

+			>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"

+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"

+			>

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/hostap/wpa_supplicant/wifi_display.c b/hostap/wpa_supplicant/wifi_display.c
new file mode 100644
index 0000000..c363b21
--- /dev/null
+++ b/hostap/wpa_supplicant/wifi_display.c
@@ -0,0 +1,418 @@
+/*
+ * wpa_supplicant - Wi-Fi Display
+ * Copyright (c) 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 "common.h"
+#include "p2p/p2p.h"
+#include "common/ieee802_11_defs.h"
+#include "wpa_supplicant_i.h"
+#include "wifi_display.h"
+
+
+#define WIFI_DISPLAY_SUBELEM_HEADER_LEN 3
+
+
+int wifi_display_init(struct wpa_global *global)
+{
+	global->wifi_display = 1;
+	return 0;
+}
+
+
+void wifi_display_deinit(struct wpa_global *global)
+{
+	int i;
+	for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
+		wpabuf_free(global->wfd_subelem[i]);
+		global->wfd_subelem[i] = NULL;
+	}
+}
+
+
+struct wpabuf * wifi_display_get_wfd_ie(struct wpa_global *global)
+{
+	struct wpabuf *ie;
+	size_t len;
+	int i;
+
+	if (global->p2p == NULL)
+		return NULL;
+
+	len = 0;
+	for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
+		if (global->wfd_subelem[i])
+			len += wpabuf_len(global->wfd_subelem[i]);
+	}
+
+	ie = wpabuf_alloc(len);
+	if (ie == NULL)
+		return NULL;
+
+	for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
+		if (global->wfd_subelem[i])
+			wpabuf_put_buf(ie, global->wfd_subelem[i]);
+	}
+
+	return ie;
+}
+
+
+static int wifi_display_update_wfd_ie(struct wpa_global *global)
+{
+	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) {
+		wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display disabled - do not "
+			   "include WFD IE");
+		p2p_set_wfd_ie_beacon(global->p2p, NULL);
+		p2p_set_wfd_ie_probe_req(global->p2p, NULL);
+		p2p_set_wfd_ie_probe_resp(global->p2p, NULL);
+		p2p_set_wfd_ie_assoc_req(global->p2p, NULL);
+		p2p_set_wfd_ie_invitation(global->p2p, NULL);
+		p2p_set_wfd_ie_prov_disc_req(global->p2p, NULL);
+		p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL);
+		p2p_set_wfd_ie_go_neg(global->p2p, NULL);
+		p2p_set_wfd_dev_info(global->p2p, NULL);
+		p2p_set_wfd_assoc_bssid(global->p2p, NULL);
+		p2p_set_wfd_coupled_sink_info(global->p2p, NULL);
+		return 0;
+	}
+
+	p2p_set_wfd_dev_info(global->p2p,
+			     global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
+	p2p_set_wfd_assoc_bssid(
+		global->p2p,
+		global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]);
+	p2p_set_wfd_coupled_sink_info(
+		global->p2p, global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
+
+	/*
+	 * WFD IE is included in number of management frames. Two different
+	 * sets of subelements are included depending on the frame:
+	 *
+	 * Beacon, (Re)Association Request, GO Negotiation Req/Resp/Conf,
+	 * Provision Discovery Req:
+	 * WFD Device Info
+	 * [Associated BSSID]
+	 * [Coupled Sink Info]
+	 *
+	 * Probe Request:
+	 * WFD Device Info
+	 * [Associated BSSID]
+	 * [Coupled Sink Info]
+	 * [WFD Extended Capability]
+	 *
+	 * Probe Response:
+	 * WFD Device Info
+	 * [Associated BSSID]
+	 * [Coupled Sink Info]
+	 * [WFD Extended Capability]
+	 * [WFD Session Info]
+	 *
+	 * (Re)Association Response, P2P Invitation Req/Resp,
+	 * Provision Discovery Resp:
+	 * WFD Device Info
+	 * [Associated BSSID]
+	 * [Coupled Sink Info]
+	 * [WFD Session Info]
+	 */
+	len = 0;
+	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
+		len += wpabuf_len(global->wfd_subelem[
+					  WFD_SUBELEM_DEVICE_INFO]);
+	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
+		len += wpabuf_len(global->wfd_subelem[
+					  WFD_SUBELEM_ASSOCIATED_BSSID]);
+	if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
+		len += wpabuf_len(global->wfd_subelem[
+					  WFD_SUBELEM_COUPLED_SINK]);
+	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
+		len += wpabuf_len(global->wfd_subelem[
+					  WFD_SUBELEM_SESSION_INFO]);
+	if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
+		len += wpabuf_len(global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
+	buf = wpabuf_alloc(len);
+	if (buf == NULL)
+		return -1;
+
+	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
+		wpabuf_put_buf(buf,
+			       global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
+	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
+		wpabuf_put_buf(buf, global->wfd_subelem[
+				       WFD_SUBELEM_ASSOCIATED_BSSID]);
+	if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
+		wpabuf_put_buf(buf,
+			       global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
+
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Beacon", ie);
+	p2p_set_wfd_ie_beacon(global->p2p, ie);
+
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for (Re)Association Request",
+			ie);
+	p2p_set_wfd_ie_assoc_req(global->p2p, ie);
+
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for GO Negotiation", ie);
+	p2p_set_wfd_ie_go_neg(global->p2p, ie);
+
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
+			"Request", ie);
+	p2p_set_wfd_ie_prov_disc_req(global->p2p, ie);
+
+	plen = buf->used;
+	if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
+		wpabuf_put_buf(buf,
+			       global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
+
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Request", ie);
+	p2p_set_wfd_ie_probe_req(global->p2p, ie);
+
+	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
+		wpabuf_put_buf(buf,
+			       global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Response", ie);
+	p2p_set_wfd_ie_probe_resp(global->p2p, ie);
+
+	/* Remove WFD Extended Capability from buffer */
+	buf->used = plen;
+	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
+		wpabuf_put_buf(buf,
+			       global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
+
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for P2P Invitation", ie);
+	p2p_set_wfd_ie_invitation(global->p2p, ie);
+
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
+			"Response", ie);
+	p2p_set_wfd_ie_prov_disc_resp(global->p2p, ie);
+
+	wpabuf_free(buf);
+
+	return 0;
+}
+
+
+void wifi_display_enable(struct wpa_global *global, int enabled)
+{
+	wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display %s",
+		   enabled ? "enabled" : "disabled");
+	global->wifi_display = enabled;
+	wifi_display_update_wfd_ie(global);
+}
+
+
+int wifi_display_subelem_set(struct wpa_global *global, char *cmd)
+{
+	char *pos;
+	int subelem;
+	size_t len;
+	struct wpabuf *e;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	len = os_strlen(pos);
+	if (len & 1)
+		return -1;
+	len /= 2;
+
+	if (os_strcmp(cmd, "all") == 0) {
+		int res;
+
+		e = wpabuf_alloc(len);
+		if (e == NULL)
+			return -1;
+		if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
+			wpabuf_free(e);
+			return -1;
+		}
+		res = wifi_display_subelem_set_from_ies(global, e);
+		wpabuf_free(e);
+		return res;
+	}
+
+	subelem = atoi(cmd);
+	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
+		return -1;
+
+	if (len == 0) {
+		/* Clear subelement */
+		e = NULL;
+		wpa_printf(MSG_DEBUG, "WFD: Clear subelement %d", subelem);
+	} else {
+		e = wpabuf_alloc(1 + len);
+		if (e == NULL)
+			return -1;
+		wpabuf_put_u8(e, subelem);
+		if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
+			wpabuf_free(e);
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "WFD: Set subelement %d", subelem);
+	}
+
+	wpabuf_free(global->wfd_subelem[subelem]);
+	global->wfd_subelem[subelem] = e;
+	wifi_display_update_wfd_ie(global);
+
+	return 0;
+}
+
+
+int wifi_display_subelem_set_from_ies(struct wpa_global *global,
+				      struct wpabuf *ie)
+{
+	int subelements[MAX_WFD_SUBELEMS] = {};
+	const u8 *pos, *end;
+	unsigned int len, subelem;
+	struct wpabuf *e;
+
+	wpa_printf(MSG_DEBUG, "WFD IEs set: %p - %lu",
+		   ie, ie ? (unsigned long) wpabuf_len(ie) : 0);
+
+	if (ie == NULL || wpabuf_len(ie) < 6)
+		return -1;
+
+	pos = wpabuf_head(ie);
+	end = pos + wpabuf_len(ie);
+
+	while (end > pos) {
+		if (pos + 3 > end)
+			break;
+
+		len = WPA_GET_BE16(pos + 1) + 3;
+
+		wpa_printf(MSG_DEBUG, "WFD Sub-Element ID %d - len %d",
+			   *pos, len - 3);
+
+		if (len > (unsigned int) (end - pos))
+			break;
+
+		subelem = *pos;
+		if (subelem < MAX_WFD_SUBELEMS && subelements[subelem] == 0) {
+			e = wpabuf_alloc_copy(pos, len);
+			if (e == NULL)
+				return -1;
+
+			wpabuf_free(global->wfd_subelem[subelem]);
+			global->wfd_subelem[subelem] = e;
+			subelements[subelem] = 1;
+		}
+
+		pos += len;
+	}
+
+	for (subelem = 0; subelem < MAX_WFD_SUBELEMS; subelem++) {
+		if (subelements[subelem] == 0) {
+			wpabuf_free(global->wfd_subelem[subelem]);
+			global->wfd_subelem[subelem] = NULL;
+		}
+	}
+
+	return wifi_display_update_wfd_ie(global);
+}
+
+
+int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
+			     char *buf, size_t buflen)
+{
+	int subelem;
+
+	if (os_strcmp(cmd, "all") == 0) {
+		struct wpabuf *ie;
+		int res;
+
+		ie = wifi_display_get_wfd_ie(global);
+		if (ie == NULL)
+			return 0;
+		res = wpa_snprintf_hex(buf, buflen, wpabuf_head(ie),
+				       wpabuf_len(ie));
+		wpabuf_free(ie);
+		return res;
+	}
+
+	subelem = atoi(cmd);
+	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
+		return -1;
+
+	if (global->wfd_subelem[subelem] == NULL)
+		return 0;
+
+	return wpa_snprintf_hex(buf, buflen,
+				wpabuf_head_u8(global->wfd_subelem[subelem]) +
+				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/hostap/wpa_supplicant/wifi_display.h b/hostap/wpa_supplicant/wifi_display.h
new file mode 100644
index 0000000..0966bdb
--- /dev/null
+++ b/hostap/wpa_supplicant/wifi_display.h
@@ -0,0 +1,24 @@
+/*
+ * wpa_supplicant - Wi-Fi Display
+ * Copyright (c) 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 WIFI_DISPLAY_H
+#define WIFI_DISPLAY_H
+
+int wifi_display_init(struct wpa_global *global);
+void wifi_display_deinit(struct wpa_global *global);
+void wifi_display_enable(struct wpa_global *global, int enabled);
+struct wpabuf *wifi_display_get_wfd_ie(struct wpa_global *global);
+int wifi_display_subelem_set(struct wpa_global *global, char *cmd);
+int wifi_display_subelem_set_from_ies(struct wpa_global *global,
+				      struct wpabuf *ie);
+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/hostap/wpa_supplicant/win_example.reg b/hostap/wpa_supplicant/win_example.reg
new file mode 100755
index 0000000..875d4ef
--- /dev/null
+++ b/hostap/wpa_supplicant/win_example.reg
@@ -0,0 +1,42 @@
+REGEDIT4

+

+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant]

+"debug_level"=dword:00000000

+"debug_show_keys"=dword:00000001

+"debug_timestamp"=dword:00000000

+"debug_use_file"=dword:00000000

+

+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs]

+

+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test]

+"ap_scan"=dword:00000002

+"update_config"=dword:00000001

+"uuid"="12345678-9abc-def0-1234-56789abcdef0"

+"device_name"="Wireless Client"

+"manufacturer"="Company"

+"model_name"="cmodel"

+"serial_number"="12345"

+"device_type"="1-0050F204-1"

+"os_version"="01020300"

+

+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\blobs]

+"testblob"=hex:01,02,03,04,05

+

+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks]

+

+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000]

+"ssid"="\"example network\""

+"key_mgmt"="WPA-PSK"

+"psk"="\"secret password\""

+"pairwise"="CCMP"

+"group"="CCMP"

+"proto"="WPA"

+

+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\interfaces]

+

+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\interfaces\0000]

+"adapter"="{A7627643-C310-49E5-BD89-7E77709C04AB}"

+"config"="test"

+"ctrl_interface"=""

+"skip_on_error"=dword:00000000

+

diff --git a/hostap/wpa_supplicant/win_if_list.c b/hostap/wpa_supplicant/win_if_list.c
new file mode 100644
index 0000000..39634d9
--- /dev/null
+++ b/hostap/wpa_supplicant/win_if_list.c
@@ -0,0 +1,173 @@
+/*
+ * win_if_list - Display network interfaces with description (for Windows)
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This small tool is for the Windows build to provide an easy way of fetching
+ * a list of available network interfaces.
+ */
+
+#include "includes.h"
+#include <stdio.h>
+#ifdef CONFIG_USE_NDISUIO
+#include <winsock2.h>
+#include <ntddndis.h>
+#else /* CONFIG_USE_NDISUIO */
+#include "pcap.h"
+#include <winsock.h>
+#endif /* CONFIG_USE_NDISUIO */
+
+#ifdef CONFIG_USE_NDISUIO
+
+/* from nuiouser.h */
+#define FSCTL_NDISUIO_BASE      FILE_DEVICE_NETWORK
+
+#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
+	CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
+
+#define IOCTL_NDISUIO_QUERY_BINDING \
+	_NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_BIND_WAIT \
+	_NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+typedef struct _NDISUIO_QUERY_BINDING
+{
+	ULONG BindingIndex;
+	ULONG DeviceNameOffset;
+	ULONG DeviceNameLength;
+	ULONG DeviceDescrOffset;
+	ULONG DeviceDescrLength;
+} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING;
+
+
+static HANDLE ndisuio_open(void)
+{
+	DWORD written;
+	HANDLE h;
+
+	h = CreateFile(TEXT("\\\\.\\\\Ndisuio"),
+		       GENERIC_READ | GENERIC_WRITE, 0, NULL,
+		       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+		       INVALID_HANDLE_VALUE);
+	if (h == INVALID_HANDLE_VALUE)
+		return h;
+
+#ifndef _WIN32_WCE
+	if (!DeviceIoControl(h, IOCTL_NDISUIO_BIND_WAIT, NULL, 0, NULL, 0,
+			     &written, NULL)) {
+		printf("IOCTL_NDISUIO_BIND_WAIT failed: %d",
+		       (int) GetLastError());
+		CloseHandle(h);
+		return INVALID_HANDLE_VALUE;
+	}
+#endif /* _WIN32_WCE */
+
+	return h;
+}
+
+
+static void ndisuio_query_bindings(HANDLE ndisuio)
+{
+	NDISUIO_QUERY_BINDING *b;
+	size_t blen = sizeof(*b) + 1024;
+	int i, error;
+	DWORD written;
+	char name[256], desc[256];
+	WCHAR *pos;
+	size_t j, len;
+
+	b = malloc(blen);
+	if (b == NULL)
+		return;
+
+	for (i = 0; ; i++) {
+		memset(b, 0, blen);
+		b->BindingIndex = i;
+		if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
+				     b, sizeof(NDISUIO_QUERY_BINDING), b,
+				     (DWORD) blen, &written, NULL)) {
+			error = (int) GetLastError();
+			if (error == ERROR_NO_MORE_ITEMS)
+				break;
+			printf("IOCTL_NDISUIO_QUERY_BINDING failed: %d",
+			       error);
+			break;
+		}
+
+		pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
+		len = b->DeviceNameLength;
+		if (len >= sizeof(name))
+			len = sizeof(name) - 1;
+		for (j = 0; j < len; j++)
+			name[j] = (char) pos[j];
+		name[len] = '\0';
+
+		pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
+		len = b->DeviceDescrLength;
+		if (len >= sizeof(desc))
+			len = sizeof(desc) - 1;
+		for (j = 0; j < len; j++)
+			desc[j] = (char) pos[j];
+		desc[len] = '\0';
+
+		printf("ifname: %s\ndescription: %s\n\n", name, desc);
+	}
+
+	free(b);
+}
+
+
+static void ndisuio_enum_bindings(void)
+{
+	HANDLE ndisuio = ndisuio_open();
+	if (ndisuio == INVALID_HANDLE_VALUE)
+		return;
+
+	ndisuio_query_bindings(ndisuio);
+	CloseHandle(ndisuio);
+}
+
+#else /* CONFIG_USE_NDISUIO */
+
+static void show_dev(pcap_if_t *dev)
+{
+	printf("ifname: %s\ndescription: %s\n\n",
+	       dev->name, dev->description);
+}
+
+
+static void pcap_enum_devs(void)
+{
+	pcap_if_t *devs, *dev;
+	char err[PCAP_ERRBUF_SIZE + 1];
+
+	if (pcap_findalldevs(&devs, err) < 0) {
+		fprintf(stderr, "Error - pcap_findalldevs: %s\n", err);
+		return;
+	}
+
+	for (dev = devs; dev; dev = dev->next) {
+		show_dev(dev);
+	}
+
+	pcap_freealldevs(devs);
+}
+
+#endif /* CONFIG_USE_NDISUIO */
+
+
+int main(int argc, char *argv[])
+{
+#ifdef CONFIG_USE_NDISUIO
+	ndisuio_enum_bindings();
+#else /* CONFIG_USE_NDISUIO */
+	pcap_enum_devs();
+#endif /* CONFIG_USE_NDISUIO */
+
+	return 0;
+}
diff --git a/hostap/wpa_supplicant/wmm_ac.c b/hostap/wpa_supplicant/wmm_ac.c
new file mode 100644
index 0000000..5625d36
--- /dev/null
+++ b/hostap/wpa_supplicant/wmm_ac.c
@@ -0,0 +1,995 @@
+/*
+ * Wi-Fi Multimedia Admission Control (WMM-AC)
+ * Copyright(c) 2014, Intel Mobile Communication GmbH.
+ * Copyright(c) 2014, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/common.h"
+#include "utils/list.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_common.h"
+#include "wpa_supplicant_i.h"
+#include "bss.h"
+#include "driver_i.h"
+#include "wmm_ac.h"
+
+static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx);
+
+static const enum wmm_ac up_to_ac[8] = {
+	WMM_AC_BK,
+	WMM_AC_BE,
+	WMM_AC_BE,
+	WMM_AC_BK,
+	WMM_AC_VI,
+	WMM_AC_VI,
+	WMM_AC_VO,
+	WMM_AC_VO
+};
+
+
+static inline u8 wmm_ac_get_tsid(const struct wmm_tspec_element *tspec)
+{
+	return (tspec->ts_info[0] >> 1) & 0x0f;
+}
+
+
+static u8 wmm_ac_get_direction(const struct wmm_tspec_element *tspec)
+{
+	return (tspec->ts_info[0] >> 5) & 0x03;
+}
+
+
+static u8 wmm_ac_get_user_priority(const struct wmm_tspec_element *tspec)
+{
+	return (tspec->ts_info[1] >> 3) & 0x07;
+}
+
+
+static u8 wmm_ac_direction_to_idx(u8 direction)
+{
+	switch (direction) {
+	case WMM_AC_DIR_UPLINK:
+		return TS_DIR_IDX_UPLINK;
+	case WMM_AC_DIR_DOWNLINK:
+		return TS_DIR_IDX_DOWNLINK;
+	case WMM_AC_DIR_BIDIRECTIONAL:
+		return TS_DIR_IDX_BIDI;
+	default:
+		wpa_printf(MSG_ERROR, "Invalid direction: %d", direction);
+		return WMM_AC_DIR_UPLINK;
+	}
+}
+
+
+static int wmm_ac_add_ts(struct wpa_supplicant *wpa_s, const u8 *addr,
+			 const struct wmm_tspec_element *tspec)
+{
+	struct wmm_tspec_element *_tspec;
+	int ret;
+	u16 admitted_time = le_to_host16(tspec->medium_time);
+	u8 up = wmm_ac_get_user_priority(tspec);
+	u8 ac = up_to_ac[up];
+	u8 dir = wmm_ac_get_direction(tspec);
+	u8 tsid = wmm_ac_get_tsid(tspec);
+	enum ts_dir_idx idx = wmm_ac_direction_to_idx(dir);
+
+	/* should have been verified before, but double-check here */
+	if (wpa_s->tspecs[ac][idx]) {
+		wpa_printf(MSG_ERROR,
+			   "WMM AC: tspec (ac=%d, dir=%d) already exists!",
+			   ac, dir);
+		return -1;
+	}
+
+	/* copy tspec */
+	_tspec = os_malloc(sizeof(*_tspec));
+	if (!_tspec)
+		return -1;
+
+	/* store the admitted TSPEC */
+	os_memcpy(_tspec, tspec, sizeof(*_tspec));
+
+	if (dir != WMM_AC_DIR_DOWNLINK) {
+		ret = wpa_drv_add_ts(wpa_s, tsid, addr, up, admitted_time);
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: Add TS: addr=" MACSTR
+			   " TSID=%u admitted time=%u, ret=%d",
+			   MAC2STR(addr), tsid, admitted_time, ret);
+		if (ret < 0) {
+			os_free(_tspec);
+			return -1;
+		}
+	}
+
+	wpa_s->tspecs[ac][idx] = _tspec;
+
+	wpa_printf(MSG_DEBUG, "Traffic stream was created successfully");
+
+	wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_ADDED
+		"tsid=%d addr=" MACSTR " admitted_time=%d",
+		tsid, MAC2STR(addr), admitted_time);
+
+	return 0;
+}
+
+
+static void wmm_ac_del_ts_idx(struct wpa_supplicant *wpa_s, u8 ac,
+			      enum ts_dir_idx dir)
+{
+	struct wmm_tspec_element *tspec = wpa_s->tspecs[ac][dir];
+	u8 tsid;
+
+	if (!tspec)
+		return;
+
+	tsid = wmm_ac_get_tsid(tspec);
+	wpa_printf(MSG_DEBUG, "WMM AC: Del TS ac=%d tsid=%d", ac, tsid);
+
+	/* update the driver in case of uplink/bidi */
+	if (wmm_ac_get_direction(tspec) != WMM_AC_DIR_DOWNLINK)
+		wpa_drv_del_ts(wpa_s, tsid, wpa_s->bssid);
+
+	wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REMOVED
+		"tsid=%d addr=" MACSTR, tsid, MAC2STR(wpa_s->bssid));
+
+	os_free(wpa_s->tspecs[ac][dir]);
+	wpa_s->tspecs[ac][dir] = NULL;
+}
+
+
+static void wmm_ac_del_req(struct wpa_supplicant *wpa_s, int failed)
+{
+	struct wmm_ac_addts_request *req = wpa_s->addts_request;
+
+	if (!req)
+		return;
+
+	if (failed)
+		wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED
+			"tsid=%u", wmm_ac_get_tsid(&req->tspec));
+
+	eloop_cancel_timeout(wmm_ac_addts_req_timeout, wpa_s, req);
+	wpa_s->addts_request = NULL;
+	os_free(req);
+}
+
+
+static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct wmm_ac_addts_request *addts_req = timeout_ctx;
+
+	wpa_printf(MSG_DEBUG,
+		   "Timeout getting ADDTS response (tsid=%d up=%d)",
+		   wmm_ac_get_tsid(&addts_req->tspec),
+		   wmm_ac_get_user_priority(&addts_req->tspec));
+
+	wmm_ac_del_req(wpa_s, 1);
+}
+
+
+static int wmm_ac_send_addts_request(struct wpa_supplicant *wpa_s,
+				     const struct wmm_ac_addts_request *req)
+{
+	struct wpabuf *buf;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "Sending ADDTS Request to " MACSTR,
+		   MAC2STR(req->address));
+
+	/* category + action code + dialog token + status + sizeof(tspec) */
+	buf = wpabuf_alloc(4 + sizeof(req->tspec));
+	if (!buf) {
+		wpa_printf(MSG_ERROR, "WMM AC: Allocation error");
+		return -1;
+	}
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WMM);
+	wpabuf_put_u8(buf, WMM_ACTION_CODE_ADDTS_REQ);
+	wpabuf_put_u8(buf, req->dialog_token);
+	wpabuf_put_u8(buf, 0); /* status code */
+	wpabuf_put_data(buf, &req->tspec, sizeof(req->tspec));
+
+	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, req->address,
+				wpa_s->own_addr, wpa_s->bssid,
+				wpabuf_head(buf), wpabuf_len(buf), 0);
+	if (ret) {
+		wpa_printf(MSG_WARNING,
+			   "WMM AC: Failed to send ADDTS Request");
+	}
+
+	wpabuf_free(buf);
+	return ret;
+}
+
+
+static int wmm_ac_send_delts(struct wpa_supplicant *wpa_s,
+			     const struct wmm_tspec_element *tspec,
+			     const u8 *address)
+{
+	struct wpabuf *buf;
+	int ret;
+
+	/* category + action code + dialog token + status + sizeof(tspec) */
+	buf = wpabuf_alloc(4 + sizeof(*tspec));
+	if (!buf)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "Sending DELTS to " MACSTR, MAC2STR(address));
+
+	/* category + action code + dialog token + status + sizeof(tspec) */
+	wpabuf_put_u8(buf, WLAN_ACTION_WMM);
+	wpabuf_put_u8(buf, WMM_ACTION_CODE_DELTS);
+	wpabuf_put_u8(buf, 0); /* Dialog Token (not used) */
+	wpabuf_put_u8(buf, 0); /* Status Code (not used) */
+	wpabuf_put_data(buf, tspec, sizeof(*tspec));
+
+	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, address,
+				  wpa_s->own_addr, wpa_s->bssid,
+				  wpabuf_head(buf), wpabuf_len(buf), 0);
+	if (ret)
+		wpa_printf(MSG_WARNING, "Failed to send DELTS frame");
+
+	wpabuf_free(buf);
+	return ret;
+}
+
+
+/* return the AC using the given TSPEC tid */
+static int wmm_ac_find_tsid(struct wpa_supplicant *wpa_s, u8 tsid,
+			    enum ts_dir_idx *dir)
+{
+	int ac;
+	enum ts_dir_idx idx;
+
+	for (ac = 0; ac < WMM_AC_NUM; ac++) {
+		for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+			if (wpa_s->tspecs[ac][idx] &&
+			    wmm_ac_get_tsid(wpa_s->tspecs[ac][idx]) == tsid) {
+				if (dir)
+					*dir = idx;
+				return ac;
+			}
+		}
+	}
+
+	return -1;
+}
+
+
+static struct wmm_ac_addts_request *
+wmm_ac_build_addts_req(struct wpa_supplicant *wpa_s,
+		       const struct wmm_ac_ts_setup_params *params,
+		       const u8 *address)
+{
+	struct wmm_ac_addts_request *addts_req;
+	struct wmm_tspec_element *tspec;
+	u8 ac = up_to_ac[params->user_priority];
+	u8 uapsd = wpa_s->wmm_ac_assoc_info->ac_params[ac].uapsd;
+
+	addts_req = os_zalloc(sizeof(*addts_req));
+	if (!addts_req)
+		return NULL;
+
+	tspec = &addts_req->tspec;
+	os_memcpy(addts_req->address, address, ETH_ALEN);
+
+	/* The dialog token cannot be zero */
+	if (++wpa_s->wmm_ac_last_dialog_token == 0)
+		wpa_s->wmm_ac_last_dialog_token++;
+
+	addts_req->dialog_token = wpa_s->wmm_ac_last_dialog_token;
+	tspec->eid = WLAN_EID_VENDOR_SPECIFIC;
+	tspec->length = sizeof(*tspec) - 2; /* reduce eid and length */
+	tspec->oui[0] = 0x00;
+	tspec->oui[1] = 0x50;
+	tspec->oui[2] = 0xf2;
+	tspec->oui_type = WMM_OUI_TYPE;
+	tspec->oui_subtype = WMM_OUI_SUBTYPE_TSPEC_ELEMENT;
+	tspec->version = WMM_VERSION;
+
+	tspec->ts_info[0] = params->tsid << 1;
+	tspec->ts_info[0] |= params->direction << 5;
+	tspec->ts_info[0] |= WMM_AC_ACCESS_POLICY_EDCA << 7;
+	tspec->ts_info[1] = uapsd << 2;
+	tspec->ts_info[1] |= params->user_priority << 3;
+	tspec->ts_info[2] = 0;
+
+	tspec->nominal_msdu_size = host_to_le16(params->nominal_msdu_size);
+	if (params->fixed_nominal_msdu)
+		tspec->nominal_msdu_size |=
+			host_to_le16(WMM_AC_FIXED_MSDU_SIZE);
+
+	tspec->mean_data_rate = host_to_le32(params->mean_data_rate);
+	tspec->minimum_phy_rate = host_to_le32(params->minimum_phy_rate);
+	tspec->surplus_bandwidth_allowance =
+		host_to_le16(params->surplus_bandwidth_allowance);
+
+	return addts_req;
+}
+
+
+static int param_in_range(const char *name, long value,
+			  long min_val, long max_val)
+{
+	if (value < min_val || (max_val >= 0 && value > max_val)) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: param %s (%ld) is out of range (%ld-%ld)",
+			   name, value, min_val, max_val);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static int wmm_ac_should_replace_ts(struct wpa_supplicant *wpa_s,
+				    u8 tsid, u8 ac, u8 dir)
+{
+	enum ts_dir_idx idx;
+	int cur_ac, existing_ts = 0, replace_ts = 0;
+
+	cur_ac = wmm_ac_find_tsid(wpa_s, tsid, &idx);
+	if (cur_ac >= 0) {
+		if (cur_ac != ac) {
+			wpa_printf(MSG_DEBUG,
+				   "WMM AC: TSID %i already exists on different ac (%d)",
+				   tsid, cur_ac);
+			return -1;
+		}
+
+		/* same tsid - this tspec will replace the current one */
+		replace_ts |= BIT(idx);
+	}
+
+	for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+		if (wpa_s->tspecs[ac][idx])
+			existing_ts |= BIT(idx);
+	}
+
+	switch (dir) {
+	case WMM_AC_DIR_UPLINK:
+		/* replace existing uplink/bidi tspecs */
+		replace_ts |= existing_ts & (BIT(TS_DIR_IDX_UPLINK) |
+					     BIT(TS_DIR_IDX_BIDI));
+		break;
+	case WMM_AC_DIR_DOWNLINK:
+		/* replace existing downlink/bidi tspecs */
+		replace_ts |= existing_ts & (BIT(TS_DIR_IDX_DOWNLINK) |
+					     BIT(TS_DIR_IDX_BIDI));
+		break;
+	case WMM_AC_DIR_BIDIRECTIONAL:
+		/* replace all existing tspecs */
+		replace_ts |= existing_ts;
+		break;
+	default:
+		return -1;
+	}
+
+	return replace_ts;
+}
+
+
+static int wmm_ac_ts_req_is_valid(struct wpa_supplicant *wpa_s,
+				  const struct wmm_ac_ts_setup_params *params)
+{
+	enum wmm_ac req_ac;
+
+#define PARAM_IN_RANGE(field, min_value, max_value) \
+	param_in_range(#field, params->field, min_value, max_value)
+
+	if (!PARAM_IN_RANGE(tsid, 0, WMM_AC_MAX_TID) ||
+	    !PARAM_IN_RANGE(user_priority, 0, WMM_AC_MAX_USER_PRIORITY) ||
+	    !PARAM_IN_RANGE(nominal_msdu_size, 1, WMM_AC_MAX_NOMINAL_MSDU) ||
+	    !PARAM_IN_RANGE(mean_data_rate, 1, -1) ||
+	    !PARAM_IN_RANGE(minimum_phy_rate, 1, -1) ||
+	    !PARAM_IN_RANGE(surplus_bandwidth_allowance, WMM_AC_MIN_SBA_UNITY,
+			    -1))
+		return 0;
+#undef PARAM_IN_RANGE
+
+	if (!(params->direction == WMM_TSPEC_DIRECTION_UPLINK ||
+	      params->direction == WMM_TSPEC_DIRECTION_DOWNLINK ||
+	      params->direction == WMM_TSPEC_DIRECTION_BI_DIRECTIONAL)) {
+		wpa_printf(MSG_DEBUG, "WMM AC: invalid TS direction: %d",
+			   params->direction);
+		return 0;
+	}
+
+	req_ac = up_to_ac[params->user_priority];
+
+	/* Requested accesss category must have acm */
+	if (!wpa_s->wmm_ac_assoc_info->ac_params[req_ac].acm) {
+		wpa_printf(MSG_DEBUG, "WMM AC: AC %d is not ACM", req_ac);
+		return 0;
+	}
+
+	if (wmm_ac_should_replace_ts(wpa_s, params->tsid, req_ac,
+				     params->direction) < 0)
+		return 0;
+
+	return 1;
+}
+
+
+static struct wmm_ac_assoc_data *
+wmm_ac_process_param_elem(struct wpa_supplicant *wpa_s, const u8 *ies,
+			  size_t ies_len)
+{
+	struct ieee802_11_elems elems;
+	struct wmm_parameter_element *wmm_params;
+	struct wmm_ac_assoc_data *assoc_data;
+	int i;
+
+	/* Parsing WMM Parameter Element */
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "WMM AC: could not parse assoc ies");
+		return NULL;
+	}
+
+	if (!elems.wmm) {
+		wpa_printf(MSG_DEBUG, "WMM AC: No WMM IE");
+		return NULL;
+	}
+
+	if (elems.wmm_len != sizeof(*wmm_params)) {
+		wpa_printf(MSG_DEBUG, "WMM AC: Invalid WMM ie length");
+		return NULL;
+	}
+
+	wmm_params = (struct wmm_parameter_element *)(elems.wmm);
+
+	assoc_data = os_zalloc(sizeof(*assoc_data));
+	if (!assoc_data)
+		return NULL;
+
+	for (i = 0; i < WMM_AC_NUM; i++)
+		assoc_data->ac_params[i].acm =
+			!!(wmm_params->ac[i].aci_aifsn & WMM_AC_ACM);
+
+	wpa_printf(MSG_DEBUG,
+		   "WMM AC: AC mandatory: AC_BE=%u AC_BK=%u AC_VI=%u AC_VO=%u",
+		   assoc_data->ac_params[WMM_AC_BE].acm,
+		   assoc_data->ac_params[WMM_AC_BK].acm,
+		   assoc_data->ac_params[WMM_AC_VI].acm,
+		   assoc_data->ac_params[WMM_AC_VO].acm);
+
+	return assoc_data;
+}
+
+
+static int wmm_ac_init(struct wpa_supplicant *wpa_s, const u8 *ies,
+		       size_t ies_len, const struct wmm_params *wmm_params)
+{
+	struct wmm_ac_assoc_data *assoc_data;
+	u8 ac;
+
+	if (wpa_s->wmm_ac_assoc_info) {
+		wpa_printf(MSG_ERROR, "WMM AC: Already initialized");
+		return -1;
+	}
+
+	if (!ies) {
+		wpa_printf(MSG_ERROR, "WMM AC: Missing IEs");
+		return -1;
+	}
+
+	if (!(wmm_params->info_bitmap & WMM_PARAMS_UAPSD_QUEUES_INFO)) {
+		wpa_printf(MSG_DEBUG, "WMM AC: Missing U-APSD configuration");
+		return -1;
+	}
+
+	os_memset(wpa_s->tspecs, 0, sizeof(wpa_s->tspecs));
+	wpa_s->wmm_ac_last_dialog_token = 0;
+	wpa_s->addts_request = NULL;
+
+	assoc_data = wmm_ac_process_param_elem(wpa_s, ies, ies_len);
+	if (!assoc_data)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "WMM AC: U-APSD queues=0x%x",
+		   wmm_params->uapsd_queues);
+
+	for (ac = 0; ac < WMM_AC_NUM; ac++) {
+		assoc_data->ac_params[ac].uapsd =
+			!!(wmm_params->uapsd_queues & BIT(ac));
+	}
+
+	wpa_s->wmm_ac_assoc_info = assoc_data;
+	return 0;
+}
+
+
+static void wmm_ac_del_ts(struct wpa_supplicant *wpa_s, u8 ac, int dir_bitmap)
+{
+	enum ts_dir_idx idx;
+
+	for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+		if (!(dir_bitmap & BIT(idx)))
+			continue;
+
+		wmm_ac_del_ts_idx(wpa_s, ac, idx);
+	}
+}
+
+
+static void wmm_ac_deinit(struct wpa_supplicant *wpa_s)
+{
+	int i;
+
+	for (i = 0; i < WMM_AC_NUM; i++)
+		wmm_ac_del_ts(wpa_s, i, TS_DIR_IDX_ALL);
+
+	/* delete pending add_ts requset */
+	wmm_ac_del_req(wpa_s, 1);
+
+	os_free(wpa_s->wmm_ac_assoc_info);
+	wpa_s->wmm_ac_assoc_info = NULL;
+}
+
+
+void wmm_ac_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *ies,
+			 size_t ies_len, const struct wmm_params *wmm_params)
+{
+	if (wmm_ac_init(wpa_s, ies, ies_len, wmm_params))
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "WMM AC: Valid WMM association, WMM AC is enabled");
+}
+
+
+void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->wmm_ac_assoc_info)
+		return;
+
+	wmm_ac_deinit(wpa_s);
+	wpa_printf(MSG_DEBUG, "WMM AC: WMM AC is disabled");
+}
+
+
+int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid)
+{
+	struct wmm_tspec_element tspec;
+	int ac;
+	enum ts_dir_idx dir;
+
+	if (!wpa_s->wmm_ac_assoc_info) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: Failed to delete TS, WMM AC is disabled");
+		return -1;
+	}
+
+	ac = wmm_ac_find_tsid(wpa_s, tsid, &dir);
+	if (ac < 0) {
+		wpa_printf(MSG_DEBUG, "WMM AC: TS does not exist");
+		return -1;
+	}
+
+	tspec = *wpa_s->tspecs[ac][dir];
+
+	wmm_ac_del_ts_idx(wpa_s, ac, dir);
+
+	wmm_ac_send_delts(wpa_s, &tspec, wpa_s->bssid);
+
+	return 0;
+}
+
+
+int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s,
+		      struct wmm_ac_ts_setup_params *params)
+{
+	struct wmm_ac_addts_request *addts_req;
+
+	if (!wpa_s->wmm_ac_assoc_info) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: Cannot add TS - missing assoc data");
+		return -1;
+	}
+
+	if (wpa_s->addts_request) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: can't add TS - ADDTS request is already pending");
+		return -1;
+	}
+
+	/*
+	 * we can setup downlink TS even without driver support.
+	 * however, we need driver support for the other directions.
+	 */
+	if (params->direction != WMM_AC_DIR_DOWNLINK &&
+	    !wpa_s->wmm_ac_supported) {
+		wpa_printf(MSG_DEBUG,
+			   "Cannot set uplink/bidi TS without driver support");
+		return -1;
+	}
+
+	if (!wmm_ac_ts_req_is_valid(wpa_s, params))
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "WMM AC: TS setup request (addr=" MACSTR
+		   " tsid=%u user priority=%u direction=%d)",
+		   MAC2STR(wpa_s->bssid), params->tsid,
+		   params->user_priority, params->direction);
+
+	addts_req = wmm_ac_build_addts_req(wpa_s, params, wpa_s->bssid);
+	if (!addts_req)
+		return -1;
+
+	if (wmm_ac_send_addts_request(wpa_s, addts_req))
+		goto err;
+
+	/* save as pending and set ADDTS resp timeout to 1 second */
+	wpa_s->addts_request = addts_req;
+	eloop_register_timeout(1, 0, wmm_ac_addts_req_timeout,
+			       wpa_s, addts_req);
+	return 0;
+err:
+	os_free(addts_req);
+	return -1;
+}
+
+
+static void wmm_ac_handle_delts(struct wpa_supplicant *wpa_s, const u8 *sa,
+				const struct wmm_tspec_element *tspec)
+{
+	int ac;
+	u8 tsid;
+	enum ts_dir_idx idx;
+
+	tsid = wmm_ac_get_tsid(tspec);
+
+	wpa_printf(MSG_DEBUG,
+		   "WMM AC: DELTS frame has been received TSID=%u addr="
+		   MACSTR, tsid, MAC2STR(sa));
+
+	ac = wmm_ac_find_tsid(wpa_s, tsid, &idx);
+	if (ac < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: Ignoring DELTS frame - TSID does not exist");
+		return;
+	}
+
+	wmm_ac_del_ts_idx(wpa_s, ac, idx);
+
+	wpa_printf(MSG_DEBUG,
+		   "TS was deleted successfully (tsid=%u address=" MACSTR ")",
+		   tsid, MAC2STR(sa));
+}
+
+
+static void wmm_ac_handle_addts_resp(struct wpa_supplicant *wpa_s, const u8 *sa,
+		const u8 resp_dialog_token, const u8 status_code,
+		const struct wmm_tspec_element *tspec)
+{
+	struct wmm_ac_addts_request *req = wpa_s->addts_request;
+	u8 ac, tsid, up, dir;
+	int replace_tspecs;
+
+	tsid = wmm_ac_get_tsid(tspec);
+	dir = wmm_ac_get_direction(tspec);
+	up = wmm_ac_get_user_priority(tspec);
+	ac = up_to_ac[up];
+
+	/* make sure we have a matching addts request */
+	if (!req || req->dialog_token != resp_dialog_token) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: no req with dialog=%u, ignoring frame",
+			   resp_dialog_token);
+		return;
+	}
+
+	/* make sure the params are the same */
+	if (os_memcmp(req->address, sa, ETH_ALEN) != 0 ||
+	    tsid != wmm_ac_get_tsid(&req->tspec) ||
+	    up != wmm_ac_get_user_priority(&req->tspec) ||
+	    dir != wmm_ac_get_direction(&req->tspec)) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: ADDTS params do not match, ignoring frame");
+		return;
+	}
+
+	/* delete pending request */
+	wmm_ac_del_req(wpa_s, 0);
+
+	wpa_printf(MSG_DEBUG,
+		   "ADDTS response status=%d tsid=%u up=%u direction=%u",
+		   status_code, tsid, up, dir);
+
+	if (status_code != WMM_ADDTS_STATUS_ADMISSION_ACCEPTED) {
+		wpa_printf(MSG_INFO, "WMM AC: ADDTS request was rejected");
+		goto err_msg;
+	}
+
+	replace_tspecs = wmm_ac_should_replace_ts(wpa_s, tsid, ac, dir);
+	if (replace_tspecs < 0)
+		goto err_delts;
+
+	wpa_printf(MSG_DEBUG, "ts idx replace bitmap: 0x%x", replace_tspecs);
+
+	/* when replacing tspecs - delete first */
+	wmm_ac_del_ts(wpa_s, ac, replace_tspecs);
+
+	/* Creating a new traffic stream */
+	wpa_printf(MSG_DEBUG,
+		   "WMM AC: adding a new TS with TSID=%u address="MACSTR
+		   " medium time=%u access category=%d dir=%d ",
+		   tsid, MAC2STR(sa),
+		   le_to_host16(tspec->medium_time), ac, dir);
+
+	if (wmm_ac_add_ts(wpa_s, sa, tspec))
+		goto err_delts;
+
+	return;
+
+err_delts:
+	/* ask the ap to delete the tspec */
+	wmm_ac_send_delts(wpa_s, tspec, sa);
+err_msg:
+	wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED "tsid=%u",
+		tsid);
+}
+
+
+void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+			const u8 *sa, const u8 *data, size_t len)
+{
+	u8 action;
+	u8 dialog_token;
+	u8 status_code;
+	struct ieee802_11_elems elems;
+	struct wmm_tspec_element *tspec;
+
+	if (wpa_s->wmm_ac_assoc_info == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: WMM AC is disabled, ignoring action frame");
+		return;
+	}
+
+	action = data[0];
+
+	if (action != WMM_ACTION_CODE_ADDTS_RESP &&
+	    action != WMM_ACTION_CODE_DELTS) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: Unknown action (%d), ignoring action frame",
+			   action);
+		return;
+	}
+
+	/* WMM AC action frame */
+	if (os_memcmp(da, wpa_s->own_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WMM AC: frame destination addr="MACSTR
+			   " is other than ours, ignoring frame", MAC2STR(da));
+		return;
+	}
+
+	if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WMM AC: ignore frame with sa " MACSTR
+			   " different other than our bssid", MAC2STR(da));
+		return;
+	}
+
+	if (len < 2 + sizeof(struct wmm_tspec_element)) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: Short ADDTS response ignored (len=%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+	data++;
+	len--;
+	dialog_token = data[0];
+	status_code = data[1];
+
+	if (ieee802_11_parse_elems(data + 2, len - 2, &elems, 1) != ParseOK) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: Could not parse WMM AC action from " MACSTR,
+			   MAC2STR(sa));
+		return;
+	}
+
+	/* the struct also contains the type and value, so decrease it */
+	if (elems.wmm_tspec_len != sizeof(struct wmm_tspec_element) - 2) {
+		wpa_printf(MSG_DEBUG, "WMM AC: missing or wrong length TSPEC");
+		return;
+	}
+
+	tspec = (struct wmm_tspec_element *)(elems.wmm_tspec - 2);
+
+	wpa_printf(MSG_DEBUG, "WMM AC: RX WMM AC Action from " MACSTR,
+		   MAC2STR(sa));
+	wpa_hexdump(MSG_MSGDUMP, "WMM AC: WMM AC Action content", data, len);
+
+	switch (action) {
+	case WMM_ACTION_CODE_ADDTS_RESP:
+		wmm_ac_handle_addts_resp(wpa_s, sa, dialog_token, status_code,
+					 tspec);
+		break;
+	case WMM_ACTION_CODE_DELTS:
+		wmm_ac_handle_delts(wpa_s, sa, tspec);
+		break;
+	default:
+		break;
+	}
+}
+
+
+static const char * get_ac_str(u8 ac)
+{
+	switch (ac) {
+	case WMM_AC_BE:
+		return "BE";
+	case WMM_AC_BK:
+		return "BK";
+	case WMM_AC_VI:
+		return "VI";
+	case WMM_AC_VO:
+		return "VO";
+	default:
+		return "N/A";
+	}
+}
+
+
+static const char * get_direction_str(u8 direction)
+{
+	switch (direction) {
+	case WMM_AC_DIR_DOWNLINK:
+		return "Downlink";
+	case WMM_AC_DIR_UPLINK:
+		return "Uplink";
+	case WMM_AC_DIR_BIDIRECTIONAL:
+		return "Bi-directional";
+	default:
+		return "N/A";
+	}
+}
+
+
+int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+	struct wmm_ac_assoc_data *assoc_info = wpa_s->wmm_ac_assoc_info;
+	enum ts_dir_idx idx;
+	int pos = 0;
+	u8 ac, up;
+
+	if (!assoc_info) {
+		return wpa_scnprintf(buf, buflen - pos,
+				     "Not associated to a WMM AP, WMM AC is Disabled\n");
+	}
+
+	pos += wpa_scnprintf(buf + pos, buflen - pos, "WMM AC is Enabled\n");
+
+	for (ac = 0; ac < WMM_AC_NUM; ac++) {
+		int ts_count = 0;
+
+		pos += wpa_scnprintf(buf + pos, buflen - pos,
+				     "%s: acm=%d uapsd=%d\n",
+				     get_ac_str(ac),
+				     assoc_info->ac_params[ac].acm,
+				     assoc_info->ac_params[ac].uapsd);
+
+		for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+			struct wmm_tspec_element *tspec;
+			u8 dir, tsid;
+			const char *dir_str;
+
+			tspec = wpa_s->tspecs[ac][idx];
+			if (!tspec)
+				continue;
+
+			ts_count++;
+
+			dir = wmm_ac_get_direction(tspec);
+			dir_str = get_direction_str(dir);
+			tsid = wmm_ac_get_tsid(tspec);
+			up = wmm_ac_get_user_priority(tspec);
+
+			pos += wpa_scnprintf(buf + pos, buflen - pos,
+					     "\tTSID=%u UP=%u\n"
+					     "\tAddress = "MACSTR"\n"
+					     "\tWMM AC dir = %s\n"
+					     "\tTotal admitted time = %u\n\n",
+					     tsid, up,
+					     MAC2STR(wpa_s->bssid),
+					     dir_str,
+					     le_to_host16(tspec->medium_time));
+		}
+
+		if (!ts_count) {
+			pos += wpa_scnprintf(buf + pos, buflen - pos,
+					     "\t(No Traffic Stream)\n\n");
+		}
+	}
+
+	return pos;
+}
+
+
+static u8 wmm_ac_get_tspecs_count(struct wpa_supplicant *wpa_s)
+{
+	int ac, dir, tspecs_count = 0;
+
+	for (ac = 0; ac < WMM_AC_NUM; ac++) {
+		for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) {
+			if (wpa_s->tspecs[ac][dir])
+				tspecs_count++;
+		}
+	}
+
+	return tspecs_count;
+}
+
+
+void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s)
+{
+	int ac, dir, tspecs_count;
+
+	wpa_printf(MSG_DEBUG, "WMM AC: Save last configured tspecs");
+
+	if (!wpa_s->wmm_ac_assoc_info)
+		return;
+
+	tspecs_count = wmm_ac_get_tspecs_count(wpa_s);
+	if (!tspecs_count) {
+		wpa_printf(MSG_DEBUG, "WMM AC: No configured TSPECs");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "WMM AC: Saving tspecs");
+
+	wmm_ac_clear_saved_tspecs(wpa_s);
+	wpa_s->last_tspecs = os_calloc(tspecs_count,
+				       sizeof(*wpa_s->last_tspecs));
+	if (!wpa_s->last_tspecs) {
+		wpa_printf(MSG_ERROR, "WMM AC: Failed to save tspecs!");
+		return;
+	}
+
+	for (ac = 0; ac < WMM_AC_NUM; ac++) {
+		for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) {
+			if (!wpa_s->tspecs[ac][dir])
+				continue;
+
+			wpa_s->last_tspecs[wpa_s->last_tspecs_count++] =
+				*wpa_s->tspecs[ac][dir];
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "WMM AC: Successfully saved %d TSPECs",
+		   wpa_s->last_tspecs_count);
+}
+
+
+void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->last_tspecs) {
+		wpa_printf(MSG_DEBUG, "WMM AC: Clear saved tspecs");
+		os_free(wpa_s->last_tspecs);
+		wpa_s->last_tspecs = NULL;
+		wpa_s->last_tspecs_count = 0;
+	}
+}
+
+
+int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s)
+{
+	unsigned int i;
+
+	if (!wpa_s->wmm_ac_assoc_info || !wpa_s->last_tspecs_count)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "WMM AC: Restore %u saved tspecs",
+		   wpa_s->last_tspecs_count);
+
+	for (i = 0; i < wpa_s->last_tspecs_count; i++)
+		wmm_ac_add_ts(wpa_s, wpa_s->bssid, &wpa_s->last_tspecs[i]);
+
+	return 0;
+}
diff --git a/hostap/wpa_supplicant/wmm_ac.h b/hostap/wpa_supplicant/wmm_ac.h
new file mode 100644
index 0000000..5171b16
--- /dev/null
+++ b/hostap/wpa_supplicant/wmm_ac.h
@@ -0,0 +1,176 @@
+/*
+ * Wi-Fi Multimedia Admission Control (WMM-AC)
+ * Copyright(c) 2014, Intel Mobile Communication GmbH.
+ * Copyright(c) 2014, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WMM_AC_H
+#define WMM_AC_H
+
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+
+struct wpa_supplicant;
+
+#define WMM_AC_ACCESS_POLICY_EDCA 1
+#define WMM_AC_FIXED_MSDU_SIZE BIT(15)
+
+#define WMM_AC_MAX_TID 7
+#define WMM_AC_MAX_USER_PRIORITY 7
+#define WMM_AC_MIN_SBA_UNITY 0x2000
+#define WMM_AC_MAX_NOMINAL_MSDU 32767
+
+/**
+ * struct wmm_ac_assoc_data - WMM Admission Control Association Data
+ *
+ * This struct will store any relevant WMM association data needed by WMM AC.
+ * In case there is a valid WMM association, an instance of this struct will be
+ * created. In case there is no instance of this struct, the station is not
+ * associated to a valid WMM BSS and hence, WMM AC will not be used.
+ */
+struct wmm_ac_assoc_data {
+	struct {
+		/*
+		 * acm - Admission Control Mandatory
+		 * In case an access category is ACM, the traffic will have
+		 * to be admitted by WMM-AC's admission mechanism before use.
+		 */
+		unsigned int acm:1;
+
+		/*
+		 * uapsd_queues - Unscheduled Automatic Power Save Delivery
+		 *		  queues.
+		 * Indicates whether ACs are configured for U-APSD (or legacy
+		 * PS). Storing this value is necessary in order to set the
+		 * Power Save Bit (PSB) in ADDTS request Action frames (if not
+		 * given).
+		 */
+		unsigned int uapsd:1;
+	} ac_params[WMM_AC_NUM];
+};
+
+/**
+ * wmm_ac_dir - WMM Admission Control Direction
+ */
+enum wmm_ac_dir {
+	WMM_AC_DIR_UPLINK = 0,
+	WMM_AC_DIR_DOWNLINK = 1,
+	WMM_AC_DIR_BIDIRECTIONAL = 3
+};
+
+/**
+ * ts_dir_idx - indices of internally saved tspecs
+ *
+ * we can have multiple tspecs (downlink + uplink) per ac.
+ * save them in array, and use the enum to directly access
+ * the respective tspec slot (according to the direction).
+ */
+enum ts_dir_idx {
+	TS_DIR_IDX_UPLINK,
+	TS_DIR_IDX_DOWNLINK,
+	TS_DIR_IDX_BIDI,
+
+	TS_DIR_IDX_COUNT
+};
+#define TS_DIR_IDX_ALL (BIT(TS_DIR_IDX_COUNT) - 1)
+
+/**
+ * struct wmm_ac_addts_request - ADDTS Request Information
+ *
+ * The last sent ADDTS request(s) will be saved as element(s) of this struct in
+ * order to be compared with the received ADDTS response in ADDTS response
+ * action frame handling and should be stored until that point.
+ * In case a new traffic stream will be created/replaced/updated, only its
+ * relevant traffic stream information will be stored as a wmm_ac_ts struct.
+ */
+struct wmm_ac_addts_request {
+	/*
+	 * dialog token - Used to link the recived ADDTS response with this
+	 * saved ADDTS request when ADDTS response is being handled
+	 */
+	u8 dialog_token;
+
+	/*
+	 * address - The alleged traffic stream's receiver/transmitter address
+	 * Address and TID are used to identify the TS (TID is contained in
+	 * TSPEC)
+	 */
+	u8 address[ETH_ALEN];
+
+	/*
+	 * tspec - Traffic Stream Specification, will be used to compare the
+	 * sent TSPEC in ADDTS request to the received TSPEC in ADDTS response
+	 * and act accordingly in ADDTS response handling
+	 */
+	struct wmm_tspec_element tspec;
+};
+
+
+/**
+ * struct wmm_ac_ts_setup_params - TS setup parameters
+ *
+ * This struct holds parameters which should be provided
+ * to wmm_ac_ts_setup in order to setup a traffic stream
+ */
+struct wmm_ac_ts_setup_params {
+	/*
+	 * tsid - Traffic ID
+	 * TID and address are used to identify the TS
+	 */
+	int tsid;
+
+	/*
+	 * direction - Traffic Stream's direction
+	 */
+	enum wmm_ac_dir direction;
+
+	/*
+	 * user_priority - Traffic Stream's user priority
+	 */
+	int user_priority;
+
+	/*
+	 * nominal_msdu_size - Nominal MAC service data unit size
+	 */
+	int nominal_msdu_size;
+
+	/*
+	 * fixed_nominal_msdu - Whether the size is fixed
+	 * 0 = Nominal MSDU size is not fixed
+	 * 1 = Nominal MSDU size is fixed
+	 */
+	int fixed_nominal_msdu;
+
+	/*
+	 * surplus_bandwidth_allowance - Specifies excess time allocation
+	 */
+	int mean_data_rate;
+
+	/*
+	 * minimum_phy_rate - Specifies the minimum supported PHY rate in bps
+	 */
+	int minimum_phy_rate;
+
+	/*
+	 * surplus_bandwidth_allowance - Specifies excess time allocation
+	 */
+	int surplus_bandwidth_allowance;
+};
+
+void wmm_ac_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *ies,
+			 size_t ies_len, const struct wmm_params *wmm_params);
+void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s);
+int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s,
+		      struct wmm_ac_ts_setup_params *params);
+int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid);
+void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+			const u8 *sa, const u8 *data, size_t len);
+int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen);
+void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s);
+void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s);
+int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s);
+
+#endif /* WMM_AC_H */
diff --git a/hostap/wpa_supplicant/wnm_sta.c b/hostap/wpa_supplicant/wnm_sta.c
new file mode 100644
index 0000000..954de67
--- /dev/null
+++ b/hostap/wpa_supplicant/wnm_sta.c
@@ -0,0 +1,1148 @@
+/*
+ * wpa_supplicant - WNM
+ * Copyright (c) 2011-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/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "rsn_supp/wpa.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "scan.h"
+#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
+
+
+/* get the TFS IE from driver */
+static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
+				   u16 *buf_len, enum wnm_oper oper)
+{
+	wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
+
+	return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len);
+}
+
+
+/* set the TFS IE to driver */
+static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
+				   const u8 *addr, u8 *buf, u16 *buf_len,
+				   enum wnm_oper oper)
+{
+	wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
+
+	return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len);
+}
+
+
+/* MLME-SLEEPMODE.request */
+int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
+				 u8 action, u16 intval, struct wpabuf *tfs_req)
+{
+	struct ieee80211_mgmt *mgmt;
+	int res;
+	size_t len;
+	struct wnm_sleep_element *wnmsleep_ie;
+	u8 *wnmtfs_ie;
+	u8 wnmsleep_ie_len;
+	u16 wnmtfs_ie_len;  /* possibly multiple IE(s) */
+	enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
+		WNM_SLEEP_TFS_REQ_IE_NONE;
+
+	wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request "
+		   "action=%s to " MACSTR,
+		   action == 0 ? "enter" : "exit",
+		   MAC2STR(wpa_s->bssid));
+
+	/* WNM-Sleep Mode IE */
+	wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
+	wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element));
+	if (wnmsleep_ie == NULL)
+		return -1;
+	wnmsleep_ie->eid = WLAN_EID_WNMSLEEP;
+	wnmsleep_ie->len = wnmsleep_ie_len - 2;
+	wnmsleep_ie->action_type = action;
+	wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT;
+	wnmsleep_ie->intval = host_to_le16(intval);
+	wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element",
+		    (u8 *) wnmsleep_ie, wnmsleep_ie_len);
+
+	/* TFS IE(s) */
+	if (tfs_req) {
+		wnmtfs_ie_len = wpabuf_len(tfs_req);
+		wnmtfs_ie = os_malloc(wnmtfs_ie_len);
+		if (wnmtfs_ie == NULL) {
+			os_free(wnmsleep_ie);
+			return -1;
+		}
+		os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len);
+	} else {
+		wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
+		if (wnmtfs_ie == NULL) {
+			os_free(wnmsleep_ie);
+			return -1;
+		}
+		if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
+					    tfs_oper)) {
+			wnmtfs_ie_len = 0;
+			os_free(wnmtfs_ie);
+			wnmtfs_ie = NULL;
+		}
+	}
+	wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
+		    (u8 *) wnmtfs_ie, wnmtfs_ie_len);
+
+	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len);
+	if (mgmt == NULL) {
+		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
+			   "WNM-Sleep Request action frame");
+		os_free(wnmsleep_ie);
+		os_free(wnmtfs_ie);
+		return -1;
+	}
+
+	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, wpa_s->bssid, 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.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ;
+	mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1;
+	os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie,
+		  wnmsleep_ie_len);
+	/* copy TFS IE here */
+	if (wnmtfs_ie_len > 0) {
+		os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
+			  wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
+	}
+
+	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
+		wnmtfs_ie_len;
+
+	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				  wpa_s->own_addr, wpa_s->bssid,
+				  &mgmt->u.action.category, len, 0);
+	if (res < 0)
+		wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
+			   "(action=%d, intval=%d)", action, intval);
+
+	os_free(wnmsleep_ie);
+	os_free(wnmtfs_ie);
+	os_free(mgmt);
+
+	return res;
+}
+
+
+static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
+					 u8 *tfsresp_ie_start,
+					 u8 *tfsresp_ie_end)
+{
+	wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
+			 wpa_s->bssid, NULL, NULL);
+	/* remove GTK/IGTK ?? */
+
+	/* set the TFS Resp IE(s) */
+	if (tfsresp_ie_start && tfsresp_ie_end &&
+	    tfsresp_ie_end - tfsresp_ie_start >= 0) {
+		u16 tfsresp_ie_len;
+		tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) -
+			tfsresp_ie_start;
+		wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
+		/* pass the TFS Resp IE(s) to driver for processing */
+		if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
+					    tfsresp_ie_start,
+					    &tfsresp_ie_len,
+					    WNM_SLEEP_TFS_RESP_IE_SET))
+			wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
+	}
+}
+
+
+static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
+					const u8 *frm, u16 key_len_total)
+{
+	u8 *ptr, *end;
+	u8 gtk_len;
+
+	wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM,  wpa_s->bssid,
+			 NULL, NULL);
+
+	/* Install GTK/IGTK */
+
+	/* point to key data field */
+	ptr = (u8 *) frm + 1 + 2;
+	end = ptr + key_len_total;
+	wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
+
+	while (ptr + 1 < end) {
+		if (ptr + 2 + ptr[1] > end) {
+			wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
+				   "length");
+			if (end > ptr) {
+				wpa_hexdump(MSG_DEBUG, "WNM: Remaining data",
+					    ptr, end - ptr);
+			}
+			break;
+		}
+		if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
+			if (ptr[1] < 11 + 5) {
+				wpa_printf(MSG_DEBUG, "WNM: Too short GTK "
+					   "subelem");
+				break;
+			}
+			gtk_len = *(ptr + 4);
+			if (ptr[1] < 11 + gtk_len ||
+			    gtk_len < 5 || gtk_len > 32) {
+				wpa_printf(MSG_DEBUG, "WNM: Invalid GTK "
+					   "subelem");
+				break;
+			}
+			wpa_wnmsleep_install_key(
+				wpa_s->wpa,
+				WNM_SLEEP_SUBELEM_GTK,
+				ptr);
+			ptr += 13 + gtk_len;
+#ifdef CONFIG_IEEE80211W
+		} else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) {
+			if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) {
+				wpa_printf(MSG_DEBUG, "WNM: Too short IGTK "
+					   "subelem");
+				break;
+			}
+			wpa_wnmsleep_install_key(wpa_s->wpa,
+						 WNM_SLEEP_SUBELEM_IGTK, ptr);
+			ptr += 10 + WPA_IGTK_LEN;
+#endif /* CONFIG_IEEE80211W */
+		} else
+			break; /* skip the loop */
+	}
+}
+
+
+static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
+					const u8 *frm, int len)
+{
+	/*
+	 * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
+	 * WNM-Sleep Mode IE | TFS Response IE
+	 */
+	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;
+	size_t left;
+
+	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);
+	left = len - 3;
+	if (key_len_total > left) {
+		wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
+		return;
+	}
+	pos += 3 + key_len_total;
+	while (pos - frm < len) {
+		u8 ie_len = *(pos + 1);
+		if (pos + 2 + ie_len > frm + len) {
+			wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
+			break;
+		}
+		wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
+		if (*pos == WLAN_EID_WNMSLEEP)
+			wnmsleep_ie = (struct wnm_sleep_element *) pos;
+		else if (*pos == WLAN_EID_TFS_RESP) {
+			if (!tfsresp_ie_start)
+				tfsresp_ie_start = pos;
+			tfsresp_ie_end = pos;
+		} else
+			wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
+		pos += ie_len + 2;
+	}
+
+	if (!wnmsleep_ie) {
+		wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
+		return;
+	}
+
+	if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
+	    wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) {
+		wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
+			   "frame (action=%d, intval=%d)",
+			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
+		if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) {
+			wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start,
+						     tfsresp_ie_end);
+		} else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
+			wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total);
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame "
+			   "(action=%d, intval=%d)",
+			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
+		if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER)
+			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
+					 wpa_s->bssid, NULL, NULL);
+		else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT)
+			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
+					 wpa_s->bssid, NULL, NULL);
+	}
+}
+
+
+void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
+{
+	int i;
+
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
+		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;
+}
+
+
+static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
+					   u8 id, u8 elen, const u8 *pos)
+{
+	switch (id) {
+	case WNM_NEIGHBOR_TSF:
+		if (elen < 2 + 2) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
+			break;
+		}
+		rep->tsf_offset = WPA_GET_LE16(pos);
+		rep->beacon_int = WPA_GET_LE16(pos + 2);
+		rep->tsf_present = 1;
+		break;
+	case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
+		if (elen < 2) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
+				   "country string");
+			break;
+		}
+		os_memcpy(rep->country, pos, 2);
+		rep->country_present = 1;
+		break;
+	case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
+		if (elen < 1) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
+				   "candidate");
+			break;
+		}
+		rep->preference = pos[0];
+		rep->preference_present = 1;
+		break;
+	case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
+		rep->bss_term_tsf = WPA_GET_LE64(pos);
+		rep->bss_term_dur = WPA_GET_LE16(pos + 8);
+		rep->bss_term_present = 1;
+		break;
+	case WNM_NEIGHBOR_BEARING:
+		if (elen < 8) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
+				   "bearing");
+			break;
+		}
+		rep->bearing = WPA_GET_LE16(pos);
+		rep->distance = WPA_GET_LE32(pos + 2);
+		rep->rel_height = WPA_GET_LE16(pos + 2 + 4);
+		rep->bearing_present = 1;
+		break;
+	case WNM_NEIGHBOR_MEASUREMENT_PILOT:
+		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->measurement_pilot = pos[0];
+		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 < 5) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
+				   "capabilities");
+			break;
+		}
+		os_memcpy(rep->rm_capab, pos, 5);
+		rep->rm_capab_present = 1;
+		break;
+	case WNM_NEIGHBOR_MULTIPLE_BSSID:
+		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->max_bssid_indicator = pos[0];
+		rep->mul_bssid->subelem_len = elen - 1;
+		os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
+		break;
+	}
+}
+
+
+static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan)
+{
+	struct wpa_bss *bss = wpa_s->current_bss;
+	const char *country = NULL;
+
+	if (bss) {
+		const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY);
+
+		if (elem && elem[1] >= 2)
+			country = (const char *) (elem + 2);
+	}
+
+	return ieee80211_chan_to_freq(country, op_class, chan);
+}
+
+
+static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
+				      const u8 *pos, u8 len,
+				      struct neighbor_report *rep)
+{
+	u8 left = len;
+
+	if (left < 13) {
+		wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
+		return;
+	}
+
+	os_memcpy(rep->bssid, pos, ETH_ALEN);
+	rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN);
+	rep->regulatory_class = *(pos + 10);
+	rep->channel_number = *(pos + 11);
+	rep->phy_type = *(pos + 12);
+
+	pos += 13;
+	left -= 13;
+
+	while (left >= 2) {
+		u8 id, elen;
+
+		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 -= elen;
+		pos += elen;
+	}
+
+	rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class,
+				     rep->channel_number);
+}
+
+
+static struct wpa_bss *
+compare_scan_neighbor_results(struct wpa_supplicant *wpa_s)
+{
+
+	u8 i;
+	struct wpa_bss *bss = wpa_s->current_bss;
+	struct wpa_bss *target;
+
+	if (!bss)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
+		   MAC2STR(wpa_s->bssid), bss->level);
+
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		struct neighbor_report *nei;
+
+		nei = &wpa_s->wnm_neighbor_report_elements[i];
+		if (nei->preference_present && nei->preference == 0) {
+			wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR,
+				   MAC2STR(nei->bssid));
+			continue;
+		}
+
+		target = wpa_bss_get_bssid(wpa_s, nei->bssid);
+		if (!target) {
+			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
+				   " (pref %d) not found in scan results",
+				   MAC2STR(nei->bssid),
+				   nei->preference_present ? nei->preference :
+				   -1);
+			continue;
+		}
+
+		if (bss->ssid_len != target->ssid_len ||
+		    os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
+			/*
+			 * TODO: Could consider allowing transition to another
+			 * ESS if PMF was enabled for the association.
+			 */
+			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
+				   " (pref %d) in different ESS",
+				   MAC2STR(nei->bssid),
+				   nei->preference_present ? nei->preference :
+				   -1);
+			continue;
+		}
+
+		if (target->level < bss->level && target->level < -80) {
+			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
+				   " (pref %d) does not have sufficient signal level (%d)",
+				   MAC2STR(nei->bssid),
+				   nei->preference_present ? nei->preference :
+				   -1,
+				   target->level);
+			continue;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Found an acceptable preferred transition candidate BSS "
+			   MACSTR " (RSSI %d)",
+			   MAC2STR(nei->bssid), target->level);
+		return target;
+	}
+
+	return NULL;
+}
+
+
+static void wnm_send_bss_transition_mgmt_resp(
+	struct wpa_supplicant *wpa_s, u8 dialog_token,
+	enum bss_trans_mgmt_status_code status, u8 delay,
+	const u8 *target_bssid)
+{
+	u8 buf[1000], *pos;
+	struct ieee80211_mgmt *mgmt;
+	size_t len;
+	int res;
+
+	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
+		   "to " MACSTR " dialog_token=%u status=%u delay=%d",
+		   MAC2STR(wpa_s->bssid), dialog_token, status, delay);
+	if (!wpa_s->current_bss) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Current BSS not known - drop response");
+		return;
+	}
+
+	mgmt = (struct ieee80211_mgmt *) buf;
+	os_memset(&buf, 0, sizeof(buf));
+	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, wpa_s->bssid, 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_resp.action = WNM_BSS_TRANS_MGMT_RESP;
+	mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token;
+	mgmt->u.action.u.bss_tm_resp.status_code = status;
+	mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay;
+	pos = mgmt->u.action.u.bss_tm_resp.variable;
+	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;
+
+	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				  wpa_s->own_addr, wpa_s->bssid,
+				  &mgmt->u.action.category, len, 0);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Failed to send BSS Transition Management Response");
+	}
+}
+
+
+int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
+{
+	struct wpa_bss *bss;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
+
+	if (!wpa_s->wnm_neighbor_report_elements)
+		return 0;
+
+	if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
+			      &wpa_s->scan_trigger_time)) {
+		wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
+		wnm_deallocate_memory(wpa_s);
+		return 0;
+	}
+
+	if (!wpa_s->current_bss ||
+	    os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid,
+		      ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it");
+		return 0;
+	}
+
+	/* Compare the Neighbor Report and scan results */
+	bss = compare_scan_neighbor_results(wpa_s);
+	if (!bss) {
+		wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
+		status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
+		goto send_bss_resp_fail;
+	}
+
+	/* Associate to the network */
+	/* Send the BSS Management Response - Accept */
+	if (wpa_s->wnm_reply) {
+		wpa_s->wnm_reply = 0;
+		wnm_send_bss_transition_mgmt_resp(wpa_s,
+						  wpa_s->wnm_dialog_token,
+						  WNM_BSS_TM_ACCEPT,
+						  0, bss->bssid);
+	}
+
+	if (bss == wpa_s->current_bss) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Already associated with the preferred candidate");
+		return 1;
+	}
+
+	wpa_s->reassociate = 1;
+	wpa_supplicant_connect(wpa_s, bss, ssid);
+	wnm_deallocate_memory(wpa_s);
+	return 1;
+
+send_bss_resp_fail:
+	if (!reply_on_fail)
+		return 0;
+
+	/* Send reject response for all the failures */
+
+	if (wpa_s->wnm_reply) {
+		wpa_s->wnm_reply = 0;
+		wnm_send_bss_transition_mgmt_resp(wpa_s,
+						  wpa_s->wnm_dialog_token,
+						  status, 0, NULL);
+	}
+	wnm_deallocate_memory(wpa_s);
+
+	return 0;
+}
+
+
+static int cand_pref_compar(const void *a, const void *b)
+{
+	const struct neighbor_report *aa = a;
+	const struct neighbor_report *bb = b;
+
+	if (!aa->preference_present && !bb->preference_present)
+		return 0;
+	if (!aa->preference_present)
+		return 1;
+	if (!bb->preference_present)
+		return -1;
+	if (bb->preference > aa->preference)
+		return 1;
+	if (bb->preference < aa->preference)
+		return -1;
+	return 0;
+}
+
+
+static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->wnm_neighbor_report_elements)
+		return;
+	qsort(wpa_s->wnm_neighbor_report_elements,
+	      wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report),
+	      cand_pref_compar);
+}
+
+
+static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
+{
+	unsigned int i;
+
+	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List");
+	if (!wpa_s->wnm_neighbor_report_elements)
+		return;
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		struct neighbor_report *nei;
+
+		nei = &wpa_s->wnm_neighbor_report_elements[i];
+		wpa_printf(MSG_DEBUG, "%u: " MACSTR
+			   " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d",
+			   i, MAC2STR(nei->bssid), nei->bssid_info,
+			   nei->regulatory_class,
+			   nei->channel_number, nei->phy_type,
+			   nei->preference_present ? nei->preference : -1,
+			   nei->freq);
+	}
+}
+
+
+static int chan_supported(struct wpa_supplicant *wpa_s, int freq)
+{
+	unsigned int i;
+
+	for (i = 0; i < wpa_s->hw.num_modes; i++) {
+		struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
+		int j;
+
+		for (j = 0; j < mode->num_channels; j++) {
+			struct hostapd_channel_data *chan;
+
+			chan = &mode->channels[j];
+			if (chan->freq == freq &&
+			    !(chan->flag & HOSTAPD_CHAN_DISABLED))
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
+{
+	int *freqs;
+	int num_freqs = 0;
+	unsigned int i;
+
+	if (!wpa_s->wnm_neighbor_report_elements)
+		return;
+
+	if (wpa_s->hw.modes == NULL)
+		return;
+
+	os_free(wpa_s->next_scan_freqs);
+	wpa_s->next_scan_freqs = NULL;
+
+	freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int));
+	if (freqs == NULL)
+		return;
+
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		struct neighbor_report *nei;
+
+		nei = &wpa_s->wnm_neighbor_report_elements[i];
+		if (nei->freq <= 0) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Unknown neighbor operating frequency for "
+				   MACSTR " - scan all channels",
+				   MAC2STR(nei->bssid));
+			os_free(freqs);
+			return;
+		}
+		if (chan_supported(wpa_s, nei->freq))
+			add_freq(freqs, &num_freqs, nei->freq);
+	}
+
+	if (num_freqs == 0) {
+		os_free(freqs);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "WNM: Scan %d frequencies based on transition candidate list",
+		   num_freqs);
+	wpa_s->next_scan_freqs = freqs;
+}
+
+
+static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
+					     const u8 *pos, const u8 *end,
+					     int reply)
+{
+	unsigned int beacon_int;
+	u8 valid_int;
+
+	if (pos + 5 > end)
+		return;
+
+	if (wpa_s->current_bss)
+		beacon_int = wpa_s->current_bss->beacon_int;
+	else
+		beacon_int = 100; /* best guess */
+
+	wpa_s->wnm_dialog_token = pos[0];
+	wpa_s->wnm_mode = pos[1];
+	wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
+	valid_int = pos[4];
+	wpa_s->wnm_reply = reply;
+
+	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
+		   "dialog_token=%u request_mode=0x%x "
+		   "disassoc_timer=%u validity_interval=%u",
+		   wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
+		   wpa_s->wnm_dissoc_timer, valid_int);
+
+	pos += 5;
+
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
+		if (pos + 12 > end) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
+			return;
+		}
+		os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
+		pos += 12; /* BSS Termination Duration */
+	}
+
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
+		char url[256];
+
+		if (pos + 1 > end || pos + 1 + pos[0] > end) {
+			wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
+				   "Management Request (URL)");
+			return;
+		}
+		os_memcpy(url, pos + 1, pos[0]);
+		url[pos[0]] = '\0';
+		pos += 1 + pos[0];
+
+		wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
+			wpa_sm_pmf_enabled(wpa_s->wpa),
+			wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
+	}
+
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+		wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
+			"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
+		if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) {
+			/* TODO: mark current BSS less preferred for
+			 * selection */
+			wpa_printf(MSG_DEBUG, "Trying to find another BSS");
+			wpa_supplicant_req_scan(wpa_s, 0, 0);
+		}
+	}
+
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
+		unsigned int valid_ms;
+
+		wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
+		wnm_deallocate_memory(wpa_s);
+		wpa_s->wnm_neighbor_report_elements = os_calloc(
+			WNM_MAX_NEIGHBOR_REPORT,
+			sizeof(struct neighbor_report));
+		if (wpa_s->wnm_neighbor_report_elements == NULL)
+			return;
+
+		while (pos + 2 <= end &&
+		       wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
+		{
+			u8 tag = *pos++;
+			u8 len = *pos++;
+
+			wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
+				   tag);
+			if (pos + len > end) {
+				wpa_printf(MSG_DEBUG, "WNM: Truncated request");
+				return;
+			}
+			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++;
+		}
+		wnm_sort_cand_list(wpa_s);
+		wnm_dump_cand_list(wpa_s);
+		valid_ms = valid_int * beacon_int * 128 / 125;
+		wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms",
+			   valid_ms);
+		os_get_reltime(&wpa_s->wnm_cand_valid_until);
+		wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000;
+		wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000;
+		wpa_s->wnm_cand_valid_until.sec +=
+			wpa_s->wnm_cand_valid_until.usec / 1000000;
+		wpa_s->wnm_cand_valid_until.usec %= 1000000;
+		os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
+
+		if (wpa_s->last_scan_res_used > 0) {
+			struct os_reltime now;
+
+			os_get_reltime(&now);
+			if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) {
+				wpa_printf(MSG_DEBUG,
+					   "WNM: Try to use recent scan results");
+				if (wnm_scan_process(wpa_s, 0) > 0)
+					return;
+				wpa_printf(MSG_DEBUG,
+					   "WNM: No match in previous scan results - try a new scan");
+			}
+		}
+
+		wnm_set_scan_freqs(wpa_s);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	} else if (reply) {
+		enum bss_trans_mgmt_status_code status;
+		if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
+			status = WNM_BSS_TM_ACCEPT;
+		else {
+			wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
+			status = WNM_BSS_TM_REJECT_UNSPECIFIED;
+		}
+		wnm_send_bss_transition_mgmt_resp(wpa_s,
+						  wpa_s->wnm_dialog_token,
+						  status, 0, NULL);
+	}
+}
+
+
+int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
+				       u8 query_reason)
+{
+	u8 buf[1000], *pos;
+	struct ieee80211_mgmt *mgmt;
+	size_t len;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
+		   MACSTR " query_reason=%u",
+		   MAC2STR(wpa_s->bssid), query_reason);
+
+	mgmt = (struct ieee80211_mgmt *) buf;
+	os_memset(&buf, 0, sizeof(buf));
+	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, wpa_s->bssid, 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_query.action = WNM_BSS_TRANS_MGMT_QUERY;
+	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;
+
+	len = pos - (u8 *) &mgmt->u.action.category;
+
+	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				  wpa_s->own_addr, wpa_s->bssid,
+				  &mgmt->u.action.category, len, 0);
+
+	return ret;
+}
+
+
+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,
+			      const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	const u8 *pos, *end;
+	u8 act;
+
+	if (len < IEEE80211_HDRLEN + 2)
+		return;
+
+	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(mgmt->sa));
+	if (wpa_s->wpa_state < WPA_ASSOCIATED ||
+	    os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
+			   "frame");
+		return;
+	}
+
+	switch (act) {
+	case WNM_BSS_TRANS_MGMT_REQ:
+		ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
+						 !(mgmt->da[0] & 0x01));
+		break;
+	case WNM_SLEEP_MODE_RESP:
+		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");
+		break;
+	}
+}
diff --git a/hostap/wpa_supplicant/wnm_sta.h b/hostap/wpa_supplicant/wnm_sta.h
new file mode 100644
index 0000000..8de4348
--- /dev/null
+++ b/hostap/wpa_supplicant/wnm_sta.h
@@ -0,0 +1,77 @@
+/*
+ * IEEE 802.11v WNM related functions and structures
+ * 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 WNM_STA_H
+#define WNM_STA_H
+
+struct measurement_pilot {
+	u8 measurement_pilot;
+	u8 subelem_len;
+	u8 subelems[255];
+};
+
+struct multiple_bssid {
+	u8 max_bssid_indicator;
+	u8 subelem_len;
+	u8 subelems[255];
+};
+
+struct neighbor_report {
+	u8 bssid[ETH_ALEN];
+	u32 bssid_info;
+	u8 regulatory_class;
+	u8 channel_number;
+	u8 phy_type;
+	u8 preference; /* valid if preference_present=1 */
+	u16 tsf_offset; /* valid if tsf_present=1 */
+	u16 beacon_int; /* valid if tsf_present=1 */
+	char country[2]; /* valid if country_present=1 */
+	u8 rm_capab[5]; /* valid if rm_capab_present=1 */
+	u16 bearing; /* valid if bearing_present=1 */
+	u16 rel_height; /* valid if bearing_present=1 */
+	u32 distance; /* valid if bearing_present=1 */
+	u64 bss_term_tsf; /* valid if bss_term_present=1 */
+	u16 bss_term_dur; /* valid if bss_term_present=1 */
+	unsigned int preference_present:1;
+	unsigned int tsf_present:1;
+	unsigned int country_present:1;
+	unsigned int rm_capab_present:1;
+	unsigned int bearing_present:1;
+	unsigned int bss_term_present:1;
+	struct measurement_pilot *meas_pilot;
+	struct multiple_bssid *mul_bssid;
+	int freq;
+};
+
+
+int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
+				 u8 action, u16 intval, struct wpabuf *tfs_req);
+
+void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
+			      const struct ieee80211_mgmt *mgmt, size_t len);
+
+int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
+				       u8 query_reason);
+void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
+
+
+#ifdef CONFIG_WNM
+
+int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail);
+
+#else /* CONFIG_WNM */
+
+static inline int wnm_scan_process(struct wpa_supplicant *wpa_s,
+				   int reply_on_fail)
+{
+	return 0;
+}
+
+#endif /* CONFIG_WNM */
+
+#endif /* WNM_STA_H */
diff --git a/hostap/wpa_supplicant/wpa_cli.c b/hostap/wpa_supplicant/wpa_cli.c
new file mode 100644
index 0000000..7ddae3d
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_cli.c
@@ -0,0 +1,4399 @@
+/*
+ * WPA Supplicant - command line interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2015, 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"
+
+#ifdef CONFIG_CTRL_IFACE
+
+#ifdef CONFIG_CTRL_IFACE_UNIX
+#include <dirent.h>
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+
+#include "common/wpa_ctrl.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/edit.h"
+#include "utils/list.h"
+#include "common/version.h"
+#include "common/ieee802_11_defs.h"
+#ifdef ANDROID
+#include <cutils/properties.h>
+#endif /* ANDROID */
+
+
+static const char *const wpa_cli_version =
+"wpa_cli v" VERSION_STR "\n"
+"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors";
+
+
+static const char *const wpa_cli_license =
+"This software may be distributed under the terms of the BSD license.\n"
+"See README for more details.\n";
+
+static const char *const wpa_cli_full_license =
+"This software may be distributed under the terms of the BSD license.\n"
+"\n"
+"Redistribution and use in source and binary forms, with or without\n"
+"modification, are permitted provided that the following conditions are\n"
+"met:\n"
+"\n"
+"1. Redistributions of source code must retain the above copyright\n"
+"   notice, this list of conditions and the following disclaimer.\n"
+"\n"
+"2. Redistributions in binary form must reproduce the above copyright\n"
+"   notice, this list of conditions and the following disclaimer in the\n"
+"   documentation and/or other materials provided with the distribution.\n"
+"\n"
+"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
+"   names of its contributors may be used to endorse or promote products\n"
+"   derived from this software without specific prior written permission.\n"
+"\n"
+"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
+"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+"\n";
+
+static struct wpa_ctrl *ctrl_conn;
+static struct wpa_ctrl *mon_conn;
+static int wpa_cli_quit = 0;
+static int wpa_cli_attached = 0;
+static int wpa_cli_connected = -1;
+static int wpa_cli_last_id = 0;
+#ifndef CONFIG_CTRL_IFACE_DIR
+#define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant"
+#endif /* CONFIG_CTRL_IFACE_DIR */
+static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
+static const char *client_socket_dir = NULL;
+static char *ctrl_ifname = NULL;
+static const char *pid_file = NULL;
+static const char *action_file = NULL;
+static int ping_interval = 5;
+static int interactive = 0;
+static char *ifname_prefix = NULL;
+
+struct cli_txt_entry {
+	struct dl_list list;
+	char *txt;
+};
+
+static DEFINE_DL_LIST(bsses); /* struct cli_txt_entry */
+static DEFINE_DL_LIST(p2p_peers); /* struct cli_txt_entry */
+static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */
+static DEFINE_DL_LIST(ifnames); /* struct cli_txt_entry */
+static DEFINE_DL_LIST(networks); /* struct cli_txt_entry */
+
+
+static void print_help(const char *cmd);
+static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx);
+static void wpa_cli_close_connection(void);
+static char * wpa_cli_get_default_ifname(void);
+static char ** wpa_list_cmd_list(void);
+static void update_networks(struct wpa_ctrl *ctrl);
+
+
+static void usage(void)
+{
+	printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] "
+	       "[-a<action file>] \\\n"
+	       "        [-P<pid file>] [-g<global ctrl>] [-G<ping interval>] "
+	       "\\\n"
+	       "        [-s<wpa_client_socket_file_path>] "
+	       "[command..]\n"
+	       "  -h = help (show this usage text)\n"
+	       "  -v = shown version information\n"
+	       "  -a = run in daemon mode executing the action file based on "
+	       "events from\n"
+	       "       wpa_supplicant\n"
+	       "  -B = run a daemon in the background\n"
+	       "  default path: " CONFIG_CTRL_IFACE_DIR "\n"
+	       "  default interface: first interface found in socket path\n");
+	print_help(NULL);
+}
+
+
+static void cli_txt_list_free(struct cli_txt_entry *e)
+{
+	dl_list_del(&e->list);
+	os_free(e->txt);
+	os_free(e);
+}
+
+
+static void cli_txt_list_flush(struct dl_list *list)
+{
+	struct cli_txt_entry *e;
+	while ((e = dl_list_first(list, struct cli_txt_entry, list)))
+		cli_txt_list_free(e);
+}
+
+
+static struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list,
+					       const char *txt)
+{
+	struct cli_txt_entry *e;
+	dl_list_for_each(e, txt_list, struct cli_txt_entry, list) {
+		if (os_strcmp(e->txt, txt) == 0)
+			return e;
+	}
+	return NULL;
+}
+
+
+static void cli_txt_list_del(struct dl_list *txt_list, const char *txt)
+{
+	struct cli_txt_entry *e;
+	e = cli_txt_list_get(txt_list, txt);
+	if (e)
+		cli_txt_list_free(e);
+}
+
+
+static void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt)
+{
+	u8 addr[ETH_ALEN];
+	char buf[18];
+	if (hwaddr_aton(txt, addr) < 0)
+		return;
+	os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+	cli_txt_list_del(txt_list, buf);
+}
+
+
+#ifdef CONFIG_P2P
+static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt,
+				  int separator)
+{
+	const char *end;
+	char *buf;
+	end = os_strchr(txt, separator);
+	if (end == NULL)
+		end = txt + os_strlen(txt);
+	buf = dup_binstr(txt, end - txt);
+	if (buf == NULL)
+		return;
+	cli_txt_list_del(txt_list, buf);
+	os_free(buf);
+}
+#endif /* CONFIG_P2P */
+
+
+static int cli_txt_list_add(struct dl_list *txt_list, const char *txt)
+{
+	struct cli_txt_entry *e;
+	e = cli_txt_list_get(txt_list, txt);
+	if (e)
+		return 0;
+	e = os_zalloc(sizeof(*e));
+	if (e == NULL)
+		return -1;
+	e->txt = os_strdup(txt);
+	if (e->txt == NULL) {
+		os_free(e);
+		return -1;
+	}
+	dl_list_add(txt_list, &e->list);
+	return 0;
+}
+
+
+#ifdef CONFIG_P2P
+static int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt)
+{
+	u8 addr[ETH_ALEN];
+	char buf[18];
+	if (hwaddr_aton(txt, addr) < 0)
+		return -1;
+	os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+	return cli_txt_list_add(txt_list, buf);
+}
+#endif /* CONFIG_P2P */
+
+
+static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt,
+				 int separator)
+{
+	const char *end;
+	char *buf;
+	int ret;
+	end = os_strchr(txt, separator);
+	if (end == NULL)
+		end = txt + os_strlen(txt);
+	buf = dup_binstr(txt, end - txt);
+	if (buf == NULL)
+		return -1;
+	ret = cli_txt_list_add(txt_list, buf);
+	os_free(buf);
+	return ret;
+}
+
+
+static char ** cli_txt_list_array(struct dl_list *txt_list)
+{
+	unsigned int i, count = dl_list_len(txt_list);
+	char **res;
+	struct cli_txt_entry *e;
+
+	res = os_calloc(count + 1, sizeof(char *));
+	if (res == NULL)
+		return NULL;
+
+	i = 0;
+	dl_list_for_each(e, txt_list, struct cli_txt_entry, list) {
+		res[i] = os_strdup(e->txt);
+		if (res[i] == NULL)
+			break;
+		i++;
+	}
+
+	return res;
+}
+
+
+static int get_cmd_arg_num(const char *str, int pos)
+{
+	int arg = 0, i;
+
+	for (i = 0; i <= pos; i++) {
+		if (str[i] != ' ') {
+			arg++;
+			while (i <= pos && str[i] != ' ')
+				i++;
+		}
+	}
+
+	if (arg > 0)
+		arg--;
+	return arg;
+}
+
+
+static int str_starts(const char *src, const char *match)
+{
+	return os_strncmp(src, match, os_strlen(match)) == 0;
+}
+
+
+static int wpa_cli_show_event(const char *event)
+{
+	const char *start;
+
+	start = os_strchr(event, '>');
+	if (start == NULL)
+		return 1;
+
+	start++;
+	/*
+	 * Skip BSS added/removed events since they can be relatively frequent
+	 * and are likely of not much use for an interactive user.
+	 */
+	if (str_starts(start, WPA_EVENT_BSS_ADDED) ||
+	    str_starts(start, WPA_EVENT_BSS_REMOVED))
+		return 0;
+
+	return 1;
+}
+
+
+static int wpa_cli_open_connection(const char *ifname, int attach)
+{
+#if defined(CONFIG_CTRL_IFACE_UDP) || defined(CONFIG_CTRL_IFACE_NAMED_PIPE)
+	ctrl_conn = wpa_ctrl_open(ifname);
+	if (ctrl_conn == NULL)
+		return -1;
+
+	if (attach && interactive)
+		mon_conn = wpa_ctrl_open(ifname);
+	else
+		mon_conn = NULL;
+#else /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */
+	char *cfile = NULL;
+	int flen, res;
+
+	if (ifname == NULL)
+		return -1;
+
+#ifdef ANDROID
+	if (access(ctrl_iface_dir, F_OK) < 0) {
+		cfile = os_strdup(ifname);
+		if (cfile == NULL)
+			return -1;
+	}
+#endif /* ANDROID */
+
+	if (client_socket_dir && client_socket_dir[0] &&
+	    access(client_socket_dir, F_OK) < 0) {
+		perror(client_socket_dir);
+		os_free(cfile);
+		return -1;
+	}
+
+	if (cfile == NULL) {
+		flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2;
+		cfile = os_malloc(flen);
+		if (cfile == NULL)
+			return -1;
+		res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir,
+				  ifname);
+		if (os_snprintf_error(flen, res)) {
+			os_free(cfile);
+			return -1;
+		}
+	}
+
+	ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);
+	if (ctrl_conn == NULL) {
+		os_free(cfile);
+		return -1;
+	}
+
+	if (attach && interactive)
+		mon_conn = wpa_ctrl_open2(cfile, client_socket_dir);
+	else
+		mon_conn = NULL;
+	os_free(cfile);
+#endif /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */
+
+	if (mon_conn) {
+		if (wpa_ctrl_attach(mon_conn) == 0) {
+			wpa_cli_attached = 1;
+			if (interactive)
+				eloop_register_read_sock(
+					wpa_ctrl_get_fd(mon_conn),
+					wpa_cli_mon_receive, NULL, NULL);
+		} else {
+			printf("Warning: Failed to attach to "
+			       "wpa_supplicant.\n");
+			wpa_cli_close_connection();
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static void wpa_cli_close_connection(void)
+{
+	if (ctrl_conn == NULL)
+		return;
+
+	if (wpa_cli_attached) {
+		wpa_ctrl_detach(interactive ? mon_conn : ctrl_conn);
+		wpa_cli_attached = 0;
+	}
+	wpa_ctrl_close(ctrl_conn);
+	ctrl_conn = NULL;
+	if (mon_conn) {
+		eloop_unregister_read_sock(wpa_ctrl_get_fd(mon_conn));
+		wpa_ctrl_close(mon_conn);
+		mon_conn = NULL;
+	}
+}
+
+
+static void wpa_cli_msg_cb(char *msg, size_t len)
+{
+	printf("%s\n", msg);
+}
+
+
+static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
+{
+	char buf[4096];
+	size_t len;
+	int ret;
+
+	if (ctrl_conn == NULL) {
+		printf("Not connected to wpa_supplicant - command dropped.\n");
+		return -1;
+	}
+	if (ifname_prefix) {
+		os_snprintf(buf, sizeof(buf), "IFNAME=%s %s",
+			    ifname_prefix, cmd);
+		buf[sizeof(buf) - 1] = '\0';
+		cmd = buf;
+	}
+	len = sizeof(buf) - 1;
+	ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
+			       wpa_cli_msg_cb);
+	if (ret == -2) {
+		printf("'%s' command timed out.\n", cmd);
+		return -2;
+	} else if (ret < 0) {
+		printf("'%s' command failed.\n", cmd);
+		return -1;
+	}
+	if (print) {
+		buf[len] = '\0';
+		printf("%s", buf);
+		if (interactive && len > 0 && buf[len - 1] != '\n')
+			printf("\n");
+	}
+	return 0;
+}
+
+
+static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
+{
+	return _wpa_ctrl_command(ctrl, cmd, 1);
+}
+
+
+static int write_cmd(char *buf, size_t buflen, const char *cmd, int argc,
+		     char *argv[])
+{
+	int i, res;
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + buflen;
+
+	res = os_snprintf(pos, end - pos, "%s", cmd);
+	if (os_snprintf_error(end - pos, res))
+		goto fail;
+	pos += res;
+
+	for (i = 0; i < argc; i++) {
+		res = os_snprintf(pos, end - pos, " %s", argv[i]);
+		if (os_snprintf_error(end - pos, res))
+			goto fail;
+		pos += res;
+	}
+
+	buf[buflen - 1] = '\0';
+	return 0;
+
+fail:
+	printf("Too long command\n");
+	return -1;
+}
+
+
+static int wpa_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, int min_args,
+		       int argc, char *argv[])
+{
+	char buf[4096];
+	if (argc < min_args) {
+		printf("Invalid %s command - at least %d argument%s "
+		       "required.\n", cmd, min_args,
+		       min_args > 1 ? "s are" : " is");
+		return -1;
+	}
+	if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0)
+		return -1;
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int wpa_cli_cmd_ifname(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "IFNAME");
+}
+
+
+static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	if (argc > 0 && os_strcmp(argv[0], "verbose") == 0)
+		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");
+#ifdef ANDROID
+	if (argc > 0 && os_strcmp(argv[0], "no_events") == 0)
+		return wpa_ctrl_command(ctrl, "STATUS-NO_EVENTS");
+#endif /* ANDROID */
+	return wpa_ctrl_command(ctrl, "STATUS");
+}
+
+
+static int wpa_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "PING");
+}
+
+
+static int wpa_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "RELOG");
+}
+
+
+static int wpa_cli_cmd_note(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NOTE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "MIB");
+}
+
+
+static int wpa_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "PMKSA");
+}
+
+
+static int wpa_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "PMKSA_FLUSH");
+}
+
+
+static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	print_help(argc > 0 ? argv[0] : NULL);
+	return 0;
+}
+
+
+static char ** wpa_cli_complete_help(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = wpa_list_cmd_list();
+		break;
+	}
+
+	return res;
+}
+
+
+static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	printf("%s\n\n%s\n", wpa_cli_version, wpa_cli_full_license);
+	return 0;
+}
+
+
+static int wpa_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	wpa_cli_quit = 1;
+	if (interactive)
+		eloop_terminate();
+	return 0;
+}
+
+
+static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256];
+	int res;
+
+	if (argc == 1) {
+		res = os_snprintf(cmd, sizeof(cmd), "SET %s ", argv[0]);
+		if (os_snprintf_error(sizeof(cmd), res)) {
+			printf("Too long SET command.\n");
+			return -1;
+		}
+		return wpa_ctrl_command(ctrl, cmd);
+	}
+
+	return wpa_cli_cmd(ctrl, "SET", 2, argc, argv);
+}
+
+
+static char ** wpa_cli_complete_set(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	const char *fields[] = {
+		/* runtime values */
+		"EAPOL::heldPeriod", "EAPOL::authPeriod", "EAPOL::startPeriod",
+		"EAPOL::maxStart", "dot11RSNAConfigPMKLifetime",
+		"dot11RSNAConfigPMKReauthThreshold", "dot11RSNAConfigSATimeout",
+		"wps_fragment_size", "wps_version_number", "ampdu",
+		"tdls_testing", "tdls_disabled", "pno", "radio_disabled",
+		"uapsd", "ps", "wifi_display", "bssid_filter", "disallow_aps",
+		"no_keep_alive",
+		/* global configuration parameters */
+#ifdef CONFIG_CTRL_IFACE
+		"ctrl_interface", "no_ctrl_interface", "ctrl_interface_group",
+#endif /* CONFIG_CTRL_IFACE */
+		"eapol_version", "ap_scan", "bgscan",
+#ifdef CONFIG_MESH
+		"user_mpm", "max_peer_links", "mesh_max_inactivity",
+		"dot11RSNASAERetransPeriod",
+#endif /* CONFIG_MESH */
+		"disable_scan_offload", "fast_reauth", "opensc_engine_path",
+		"pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers",
+		"pcsc_reader", "pcsc_pin", "external_sim", "driver_param",
+		"dot11RSNAConfigPMKLifetime",
+		"dot11RSNAConfigPMKReauthThreshold",
+		"dot11RSNAConfigSATimeout",
+#ifndef CONFIG_NO_CONFIG_WRITE
+		"update_config",
+#endif /* CONFIG_NO_CONFIG_WRITE */
+		"load_dynamic_eap",
+#ifdef CONFIG_WPS
+		"uuid", "device_name", "manufacturer", "model_name",
+		"model_number", "serial_number", "device_type", "os_version",
+		"config_methods", "wps_cred_processing", "wps_vendor_ext_m1",
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+		"sec_device_type",
+		"p2p_listen_reg_class", "p2p_listen_channel",
+		"p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent",
+		"p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss",
+		"p2p_group_idle", "p2p_passphrase_len", "p2p_pref_chan",
+		"p2p_no_go_freq", "p2p_add_cli_chan",
+		"p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht",
+		"p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface",
+		"p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask",
+		"ip_addr_start", "ip_addr_end",
+#endif /* CONFIG_P2P */
+		"country", "bss_max_count", "bss_expiration_age",
+		"bss_expiration_scan_count", "filter_ssids", "filter_rssi",
+		"max_num_sta", "disassoc_low_ack",
+#ifdef CONFIG_HS20
+		"hs20",
+#endif /* CONFIG_HS20 */
+		"interworking", "hessid", "access_network_type", "pbc_in_m1",
+		"autoscan", "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey",
+		"wps_nfc_dh_privkey", "wps_nfc_dev_pw", "ext_password_backend",
+		"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
+		"sae_groups", "dtim_period", "beacon_int",
+		"ap_vendor_elements", "ignore_old_scan_res", "freq_list",
+		"scan_cur_freq", "sched_scan_interval",
+		"tdls_external_control", "osu_dir", "wowlan_triggers",
+		"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
+		"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
+		"reassoc_same_bss_optim", "wps_priority"
+	};
+	int i, num_fields = ARRAY_SIZE(fields);
+
+	if (arg == 1) {
+		char **res = os_calloc(num_fields + 1, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; i < num_fields; i++) {
+			res[i] = os_strdup(fields[i]);
+			if (res[i] == NULL)
+				return res;
+		}
+		return res;
+	}
+
+	if (arg > 1 && os_strncasecmp(str, "set bssid_filter ", 17) == 0)
+		return cli_txt_list_array(&bsses);
+
+	return NULL;
+}
+
+static int wpa_cli_cmd_dump(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "DUMP");
+}
+
+
+static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "GET", 1, argc, argv);
+}
+
+
+static char ** wpa_cli_complete_get(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	const char *fields[] = {
+#ifdef CONFIG_CTRL_IFACE
+		"ctrl_interface", "ctrl_interface_group",
+#endif /* CONFIG_CTRL_IFACE */
+		"eapol_version", "ap_scan",
+#ifdef CONFIG_MESH
+		"user_mpm", "max_peer_links", "mesh_max_inactivity",
+#endif /* CONFIG_MESH */
+		"disable_scan_offload", "fast_reauth", "opensc_engine_path",
+		"pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers",
+		"pcsc_reader", "pcsc_pin", "external_sim", "driver_param",
+		"dot11RSNAConfigPMKLifetime",
+		"dot11RSNAConfigPMKReauthThreshold",
+		"dot11RSNAConfigSATimeout",
+#ifndef CONFIG_NO_CONFIG_WRITE
+		"update_config",
+#endif /* CONFIG_NO_CONFIG_WRITE */
+#ifdef CONFIG_WPS
+		"device_name", "manufacturer", "model_name", "model_number",
+		"serial_number", "config_methods", "wps_cred_processing",
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+		"p2p_listen_reg_class", "p2p_listen_channel",
+		"p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent",
+		"p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss",
+		"p2p_group_idle", "p2p_passphrase_len", "p2p_add_cli_chan",
+		"p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht",
+		"p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface",
+		"p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask",
+		"ip_addr_start", "ip_addr_end",
+#endif /* CONFIG_P2P */
+		"bss_max_count", "bss_expiration_age",
+		"bss_expiration_scan_count", "filter_ssids", "filter_rssi",
+		"max_num_sta", "disassoc_low_ack",
+#ifdef CONFIG_HS20
+		"hs20",
+#endif /* CONFIG_HS20 */
+		"interworking", "access_network_type", "pbc_in_m1", "autoscan",
+		"wps_nfc_dev_pw_id", "ext_password_backend",
+		"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
+		"dtim_period", "beacon_int", "ignore_old_scan_res",
+		"scan_cur_freq", "sched_scan_interval",
+		"tdls_external_control", "osu_dir", "wowlan_triggers",
+		"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
+		"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
+		"reassoc_same_bss_optim"
+	};
+	int i, num_fields = ARRAY_SIZE(fields);
+
+	if (arg == 1) {
+		char **res = os_calloc(num_fields + 1, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; i < num_fields; i++) {
+			res[i] = os_strdup(fields[i]);
+			if (res[i] == NULL)
+				return res;
+		}
+		return res;
+	}
+
+	return NULL;
+}
+
+
+static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "LOGOFF");
+}
+
+
+static int wpa_cli_cmd_logon(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "LOGON");
+}
+
+
+static int wpa_cli_cmd_reassociate(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "REASSOCIATE");
+}
+
+
+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[])
+{
+	return wpa_cli_cmd(ctrl, "PREAUTH", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "AP_SCAN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_scan_interval(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "SCAN_INTERVAL", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_bss_expire_age(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "BSS_EXPIRE_AGE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_bss_expire_count(struct wpa_ctrl *ctrl, int argc,
+				        char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "BSS_EXPIRE_COUNT", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_bss_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256];
+	int res;
+
+	if (argc < 1)
+		res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH 0");
+	else
+		res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH %s", argv[0]);
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long BSS_FLUSH command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc,
+				char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "STKSTART", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "FT_DS", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WPS_PBC", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	if (argc == 0) {
+		printf("Invalid WPS_PIN command: need one or two arguments:\n"
+		       "- BSSID: use 'any' to select any\n"
+		       "- PIN: optional, used only with devices that have no "
+		       "display\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "WPS_PIN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WPS_CHECK_PIN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "WPS_CANCEL");
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+static int wpa_cli_cmd_wps_nfc(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WPS_NFC", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl, int argc,
+					    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WPS_NFC_CONFIG_TOKEN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WPS_NFC_TOKEN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	int ret;
+	char *buf;
+	size_t buflen;
+
+	if (argc != 1) {
+		printf("Invalid 'wps_nfc_tag_read' command - one argument "
+		       "is required.\n");
+		return -1;
+	}
+
+	buflen = 18 + os_strlen(argv[0]);
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return -1;
+	os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
+
+	ret = wpa_ctrl_command(ctrl, buf);
+	os_free(buf);
+
+	return ret;
+}
+
+
+static int wpa_cli_cmd_nfc_get_handover_req(struct wpa_ctrl *ctrl, int argc,
+					    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_REQ", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, int argc,
+					    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_SEL", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nfc_report_handover(struct wpa_ctrl *ctrl, int argc,
+					   char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NFC_REPORT_HANDOVER", 4, argc, argv);
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256];
+	int res;
+
+	if (argc == 2)
+		res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s",
+				  argv[0], argv[1]);
+	else if (argc == 5 || argc == 6) {
+		char ssid_hex[2 * SSID_MAX_LEN + 1];
+		char key_hex[2 * 64 + 1];
+		int i;
+
+		ssid_hex[0] = '\0';
+		for (i = 0; i < SSID_MAX_LEN; i++) {
+			if (argv[2][i] == '\0')
+				break;
+			os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]);
+		}
+
+		key_hex[0] = '\0';
+		if (argc == 6) {
+			for (i = 0; i < 64; i++) {
+				if (argv[5][i] == '\0')
+					break;
+				os_snprintf(&key_hex[i * 2], 3, "%02x",
+					    argv[5][i]);
+			}
+		}
+
+		res = os_snprintf(cmd, sizeof(cmd),
+				  "WPS_REG %s %s %s %s %s %s",
+				  argv[0], argv[1], ssid_hex, argv[3], argv[4],
+				  key_hex);
+	} else {
+		printf("Invalid WPS_REG command: need two arguments:\n"
+		       "- BSSID of the target AP\n"
+		       "- AP PIN\n");
+		printf("Alternatively, six arguments can be used to "
+		       "reconfigure the AP:\n"
+		       "- BSSID of the target AP\n"
+		       "- AP PIN\n"
+		       "- new SSID\n"
+		       "- new auth (OPEN, WPAPSK, WPA2PSK)\n"
+		       "- new encr (NONE, WEP, TKIP, CCMP)\n"
+		       "- new key\n");
+		return -1;
+	}
+
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long WPS_REG command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WPS_AP_PIN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WPS_ER_START", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_er_stop(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "WPS_ER_STOP");
+
+}
+
+
+static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	if (argc < 2) {
+		printf("Invalid WPS_ER_PIN command: need at least two "
+		       "arguments:\n"
+		       "- UUID: use 'any' to select any\n"
+		       "- PIN: Enrollee PIN\n"
+		       "optional: - Enrollee MAC address\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "WPS_ER_PIN", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_er_pbc(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WPS_ER_PBC", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	if (argc != 2) {
+		printf("Invalid WPS_ER_LEARN command: need two arguments:\n"
+		       "- UUID: specify which AP to use\n"
+		       "- PIN: AP PIN\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "WPS_ER_LEARN", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_er_set_config(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	if (argc != 2) {
+		printf("Invalid WPS_ER_SET_CONFIG command: need two "
+		       "arguments:\n"
+		       "- UUID: specify which AP to use\n"
+		       "- Network configuration id\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "WPS_ER_SET_CONFIG", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_er_config(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	char cmd[256];
+	int res;
+
+	if (argc == 5 || argc == 6) {
+		char ssid_hex[2 * SSID_MAX_LEN + 1];
+		char key_hex[2 * 64 + 1];
+		int i;
+
+		ssid_hex[0] = '\0';
+		for (i = 0; i < SSID_MAX_LEN; i++) {
+			if (argv[2][i] == '\0')
+				break;
+			os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]);
+		}
+
+		key_hex[0] = '\0';
+		if (argc == 6) {
+			for (i = 0; i < 64; i++) {
+				if (argv[5][i] == '\0')
+					break;
+				os_snprintf(&key_hex[i * 2], 3, "%02x",
+					    argv[5][i]);
+			}
+		}
+
+		res = os_snprintf(cmd, sizeof(cmd),
+				  "WPS_ER_CONFIG %s %s %s %s %s %s",
+				  argv[0], argv[1], ssid_hex, argv[3], argv[4],
+				  key_hex);
+	} else {
+		printf("Invalid WPS_ER_CONFIG command: need six arguments:\n"
+		       "- AP UUID\n"
+		       "- AP PIN\n"
+		       "- new SSID\n"
+		       "- new auth (OPEN, WPAPSK, WPA2PSK)\n"
+		       "- new encr (NONE, WEP, TKIP, CCMP)\n"
+		       "- new key\n");
+		return -1;
+	}
+
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long WPS_ER_CONFIG command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+#ifdef CONFIG_WPS_NFC
+static int wpa_cli_cmd_wps_er_nfc_config_token(struct wpa_ctrl *ctrl, int argc,
+					       char *argv[])
+{
+	if (argc != 2) {
+		printf("Invalid WPS_ER_NFC_CONFIG_TOKEN command: need two "
+		       "arguments:\n"
+		       "- WPS/NDEF: token format\n"
+		       "- UUID: specify which AP to use\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "WPS_ER_NFC_CONFIG_TOKEN", 2, argc, argv);
+}
+#endif /* CONFIG_WPS_NFC */
+
+
+static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "IBSS_RSN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "LEVEL", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256], *pos, *end;
+	int i, ret;
+
+	if (argc < 2) {
+		printf("Invalid IDENTITY command: needs two arguments "
+		       "(network id and identity)\n");
+		return -1;
+	}
+
+	end = cmd + sizeof(cmd);
+	pos = cmd;
+	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "IDENTITY-%s:%s",
+			  argv[0], argv[1]);
+	if (os_snprintf_error(end - pos, ret)) {
+		printf("Too long IDENTITY command.\n");
+		return -1;
+	}
+	pos += ret;
+	for (i = 2; i < argc; i++) {
+		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+		if (os_snprintf_error(end - pos, ret)) {
+			printf("Too long IDENTITY command.\n");
+			return -1;
+		}
+		pos += ret;
+	}
+
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_password(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256], *pos, *end;
+	int i, ret;
+
+	if (argc < 2) {
+		printf("Invalid PASSWORD command: needs two arguments "
+		       "(network id and password)\n");
+		return -1;
+	}
+
+	end = cmd + sizeof(cmd);
+	pos = cmd;
+	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSWORD-%s:%s",
+			  argv[0], argv[1]);
+	if (os_snprintf_error(end - pos, ret)) {
+		printf("Too long PASSWORD command.\n");
+		return -1;
+	}
+	pos += ret;
+	for (i = 2; i < argc; i++) {
+		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+		if (os_snprintf_error(end - pos, ret)) {
+			printf("Too long PASSWORD command.\n");
+			return -1;
+		}
+		pos += ret;
+	}
+
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_new_password(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	char cmd[256], *pos, *end;
+	int i, ret;
+
+	if (argc < 2) {
+		printf("Invalid NEW_PASSWORD command: needs two arguments "
+		       "(network id and password)\n");
+		return -1;
+	}
+
+	end = cmd + sizeof(cmd);
+	pos = cmd;
+	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "NEW_PASSWORD-%s:%s",
+			  argv[0], argv[1]);
+	if (os_snprintf_error(end - pos, ret)) {
+		printf("Too long NEW_PASSWORD command.\n");
+		return -1;
+	}
+	pos += ret;
+	for (i = 2; i < argc; i++) {
+		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+		if (os_snprintf_error(end - pos, ret)) {
+			printf("Too long NEW_PASSWORD command.\n");
+			return -1;
+		}
+		pos += ret;
+	}
+
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256], *pos, *end;
+	int i, ret;
+
+	if (argc < 2) {
+		printf("Invalid PIN command: needs two arguments "
+		       "(network id and pin)\n");
+		return -1;
+	}
+
+	end = cmd + sizeof(cmd);
+	pos = cmd;
+	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PIN-%s:%s",
+			  argv[0], argv[1]);
+	if (os_snprintf_error(end - pos, ret)) {
+		printf("Too long PIN command.\n");
+		return -1;
+	}
+	pos += ret;
+	for (i = 2; i < argc; i++) {
+		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+		if (os_snprintf_error(end - pos, ret)) {
+			printf("Too long PIN command.\n");
+			return -1;
+		}
+		pos += ret;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256], *pos, *end;
+	int i, ret;
+
+	if (argc < 2) {
+		printf("Invalid OTP command: needs two arguments (network "
+		       "id and password)\n");
+		return -1;
+	}
+
+	end = cmd + sizeof(cmd);
+	pos = cmd;
+	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "OTP-%s:%s",
+			  argv[0], argv[1]);
+	if (os_snprintf_error(end - pos, ret)) {
+		printf("Too long OTP command.\n");
+		return -1;
+	}
+	pos += ret;
+	for (i = 2; i < argc; i++) {
+		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+		if (os_snprintf_error(end - pos, ret)) {
+			printf("Too long OTP command.\n");
+			return -1;
+		}
+		pos += ret;
+	}
+
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+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 (os_snprintf_error(end - pos, ret)) {
+		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 (os_snprintf_error(end - pos, ret)) {
+			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[])
+{
+	char cmd[256], *pos, *end;
+	int i, ret;
+
+	if (argc < 2) {
+		printf("Invalid PASSPHRASE command: needs two arguments "
+		       "(network id and passphrase)\n");
+		return -1;
+	}
+
+	end = cmd + sizeof(cmd);
+	pos = cmd;
+	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSPHRASE-%s:%s",
+			  argv[0], argv[1]);
+	if (os_snprintf_error(end - pos, ret)) {
+		printf("Too long PASSPHRASE command.\n");
+		return -1;
+	}
+	pos += ret;
+	for (i = 2; i < argc; i++) {
+		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+		if (os_snprintf_error(end - pos, ret)) {
+			printf("Too long PASSPHRASE command.\n");
+			return -1;
+		}
+		pos += ret;
+	}
+
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	if (argc < 2) {
+		printf("Invalid BSSID command: needs two arguments (network "
+		       "id and BSSID)\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "BSSID", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_blacklist(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "BLACKLIST", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "LOG_LEVEL", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_list_networks(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "LIST_NETWORKS");
+}
+
+
+static int wpa_cli_cmd_select_network(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "SELECT_NETWORK", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_enable_network(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "ENABLE_NETWORK", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_disable_network(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DISABLE_NETWORK", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_add_network(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	int res = wpa_ctrl_command(ctrl, "ADD_NETWORK");
+	if (interactive)
+		update_networks(ctrl);
+	return res;
+}
+
+
+static int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	int res = wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv);
+	if (interactive)
+		update_networks(ctrl);
+	return res;
+}
+
+
+static void wpa_cli_show_network_variables(void)
+{
+	printf("set_network variables:\n"
+	       "  ssid (network name, SSID)\n"
+	       "  psk (WPA passphrase or pre-shared key)\n"
+	       "  key_mgmt (key management protocol)\n"
+	       "  identity (EAP identity)\n"
+	       "  password (EAP password)\n"
+	       "  ...\n"
+	       "\n"
+	       "Note: Values are entered in the same format as the "
+	       "configuration file is using,\n"
+	       "i.e., strings values need to be inside double quotation "
+	       "marks.\n"
+	       "For example: set_network 1 ssid \"network name\"\n"
+	       "\n"
+	       "Please see wpa_supplicant.conf documentation for full list "
+	       "of\navailable variables.\n");
+}
+
+
+static int wpa_cli_cmd_set_network(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	if (argc == 0) {
+		wpa_cli_show_network_variables();
+		return 0;
+	}
+
+	if (argc < 3) {
+		printf("Invalid SET_NETWORK command: needs three arguments\n"
+		       "(network id, variable name, and value)\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "SET_NETWORK", 3, argc, argv);
+}
+
+
+static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	if (argc == 0) {
+		wpa_cli_show_network_variables();
+		return 0;
+	}
+
+	if (argc != 2) {
+		printf("Invalid GET_NETWORK command: needs two arguments\n"
+		       "(network id and variable name)\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "GET_NETWORK", 2, argc, argv);
+}
+
+
+static const char *network_fields[] = {
+	"ssid", "scan_ssid", "bssid", "bssid_blacklist",
+	"bssid_whitelist", "psk", "proto", "key_mgmt",
+	"bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq",
+	"freq_list",
+#ifdef IEEE8021X_EAPOL
+	"eap", "identity", "anonymous_identity", "password", "ca_cert",
+	"ca_path", "client_cert", "private_key", "private_key_passwd",
+	"dh_file", "subject_match", "altsubject_match",
+	"domain_suffix_match", "domain_match", "ca_cert2", "ca_path2",
+	"client_cert2", "private_key2", "private_key2_passwd",
+	"dh_file2", "subject_match2", "altsubject_match2",
+	"domain_suffix_match2", "domain_match2", "phase1", "phase2",
+	"pcsc", "pin", "engine_id", "key_id", "cert_id", "ca_cert_id",
+	"pin2", "engine2_id", "key2_id", "cert2_id", "ca_cert2_id",
+	"engine", "engine2", "eapol_flags", "sim_num",
+	"openssl_ciphers", "erp",
+#endif /* IEEE8021X_EAPOL */
+	"wep_key0", "wep_key1", "wep_key2", "wep_key3",
+	"wep_tx_keyidx", "priority",
+#ifdef IEEE8021X_EAPOL
+	"eap_workaround", "pac_file", "fragment_size", "ocsp",
+#endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_MESH
+	"mode", "no_auto_peer",
+#else /* CONFIG_MESH */
+	"mode",
+#endif /* CONFIG_MESH */
+	"proactive_key_caching", "disabled", "id_str",
+#ifdef CONFIG_IEEE80211W
+	"ieee80211w",
+#endif /* CONFIG_IEEE80211W */
+	"peerkey", "mixed_cell", "frequency", "fixed_freq",
+#ifdef CONFIG_MESH
+	"mesh_basic_rates", "dot11MeshMaxRetries",
+	"dot11MeshRetryTimeout", "dot11MeshConfirmTimeout",
+	"dot11MeshHoldingTimeout",
+#endif /* CONFIG_MESH */
+	"wpa_ptk_rekey", "bgscan", "ignore_broadcast_ssid",
+#ifdef CONFIG_P2P
+	"go_p2p_dev_addr", "p2p_client_list", "psk_list",
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_HT_OVERRIDES
+	"disable_ht", "disable_ht40", "disable_sgi", "disable_ldpc",
+	"ht40_intolerant", "disable_max_amsdu", "ampdu_factor",
+	"ampdu_density", "ht_mcs",
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+	"disable_vht", "vht_capa", "vht_capa_mask", "vht_rx_mcs_nss_1",
+	"vht_rx_mcs_nss_2", "vht_rx_mcs_nss_3", "vht_rx_mcs_nss_4",
+	"vht_rx_mcs_nss_5", "vht_rx_mcs_nss_6", "vht_rx_mcs_nss_7",
+	"vht_rx_mcs_nss_8", "vht_tx_mcs_nss_1", "vht_tx_mcs_nss_2",
+	"vht_tx_mcs_nss_3", "vht_tx_mcs_nss_4", "vht_tx_mcs_nss_5",
+	"vht_tx_mcs_nss_6", "vht_tx_mcs_nss_7", "vht_tx_mcs_nss_8",
+#endif /* CONFIG_VHT_OVERRIDES */
+	"ap_max_inactivity", "dtim_period", "beacon_int",
+#ifdef CONFIG_MACSEC
+	"macsec_policy",
+#endif /* CONFIG_MACSEC */
+#ifdef CONFIG_HS20
+	"update_identifier",
+#endif /* CONFIG_HS20 */
+	"mac_addr"
+};
+
+
+static char ** wpa_cli_complete_network(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	int i, num_fields = ARRAY_SIZE(network_fields);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = cli_txt_list_array(&networks);
+		break;
+	case 2:
+		res = os_calloc(num_fields + 1, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; i < num_fields; i++) {
+			res[i] = os_strdup(network_fields[i]);
+			if (res[i] == NULL)
+				break;
+		}
+	}
+	return res;
+}
+
+
+static char ** wpa_cli_complete_network_id(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	if (arg == 1)
+		return cli_txt_list_array(&networks);
+	return NULL;
+}
+
+
+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 char ** wpa_cli_complete_dup_network(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	int i, num_fields = ARRAY_SIZE(network_fields);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+	case 2:
+		res = cli_txt_list_array(&networks);
+		break;
+	case 3:
+		res = os_calloc(num_fields + 1, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; i < num_fields; i++) {
+			res[i] = os_strdup(network_fields[i]);
+			if (res[i] == NULL)
+				break;
+		}
+	}
+	return res;
+}
+
+
+static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "LIST_CREDS");
+}
+
+
+static int wpa_cli_cmd_add_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "ADD_CRED");
+}
+
+
+static int wpa_cli_cmd_remove_cred(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "REMOVE_CRED", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_set_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	if (argc != 3) {
+		printf("Invalid SET_CRED command: needs three arguments\n"
+		       "(cred id, variable name, and value)\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "SET_CRED", 3, argc, argv);
+}
+
+
+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[])
+{
+	return wpa_ctrl_command(ctrl, "DISCONNECT");
+}
+
+
+static int wpa_cli_cmd_reconnect(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "RECONNECT");
+}
+
+
+static int wpa_cli_cmd_save_config(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "SAVE_CONFIG");
+}
+
+
+static int wpa_cli_cmd_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "SCAN", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_scan_results(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "SCAN_RESULTS");
+}
+
+
+static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "BSS", 1, argc, argv);
+}
+
+
+static char ** wpa_cli_complete_bss(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = cli_txt_list_array(&bsses);
+		break;
+	}
+
+	return res;
+}
+
+
+static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	if (argc < 1 || argc > 2) {
+		printf("Invalid GET_CAPABILITY command: need either one or "
+		       "two arguments\n");
+		return -1;
+	}
+
+	if ((argc == 2) && os_strcmp(argv[1], "strict") != 0) {
+		printf("Invalid GET_CAPABILITY command: second argument, "
+		       "if any, must be 'strict'\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "GET_CAPABILITY", 1, argc, argv);
+}
+
+
+static int wpa_cli_list_interfaces(struct wpa_ctrl *ctrl)
+{
+	printf("Available interfaces:\n");
+	return wpa_ctrl_command(ctrl, "INTERFACES");
+}
+
+
+static int wpa_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	if (argc < 1) {
+		wpa_cli_list_interfaces(ctrl);
+		return 0;
+	}
+
+	wpa_cli_close_connection();
+	os_free(ctrl_ifname);
+	ctrl_ifname = os_strdup(argv[0]);
+	if (!ctrl_ifname) {
+		printf("Failed to allocate memory\n");
+		return 0;
+	}
+
+	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",
+		       ctrl_ifname);
+	}
+	return 0;
+}
+
+
+static int wpa_cli_cmd_reconfigure(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "RECONFIGURE");
+}
+
+
+static int wpa_cli_cmd_terminate(struct wpa_ctrl *ctrl, int argc,
+				 char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "TERMINATE");
+}
+
+
+static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	char cmd[256];
+	int res;
+
+	if (argc < 1) {
+		printf("Invalid INTERFACE_ADD command: needs at least one "
+		       "argument (interface name)\n"
+		       "All arguments: ifname confname driver ctrl_interface "
+		       "driver_param bridge_name [create]\n");
+		return -1;
+	}
+
+	/*
+	 * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB
+	 * <driver_param>TAB<bridge_name>[TAB<create>]
+	 */
+	res = os_snprintf(cmd, sizeof(cmd),
+			  "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s",
+			  argv[0],
+			  argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "",
+			  argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "",
+			  argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : "");
+	if (os_snprintf_error(sizeof(cmd), res))
+		return -1;
+	cmd[sizeof(cmd) - 1] = '\0';
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_interface_remove(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "INTERFACE_REMOVE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_interface_list(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "INTERFACE_LIST");
+}
+
+
+#ifdef CONFIG_AP
+static int wpa_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "STA", 1, argc, argv);
+}
+
+
+static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
+				char *addr, size_t addr_len)
+{
+	char buf[4096], *pos;
+	size_t len;
+	int ret;
+
+	if (ctrl_conn == NULL) {
+		printf("Not connected to hostapd - command dropped.\n");
+		return -1;
+	}
+	len = sizeof(buf) - 1;
+	ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
+			       wpa_cli_msg_cb);
+	if (ret == -2) {
+		printf("'%s' command timed out.\n", cmd);
+		return -2;
+	} else if (ret < 0) {
+		printf("'%s' command failed.\n", cmd);
+		return -1;
+	}
+
+	buf[len] = '\0';
+	if (os_memcmp(buf, "FAIL", 4) == 0)
+		return -1;
+	printf("%s", buf);
+
+	pos = buf;
+	while (*pos != '\0' && *pos != '\n')
+		pos++;
+	*pos = '\0';
+	os_strlcpy(addr, buf, addr_len);
+	return 0;
+}
+
+
+static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char addr[32], cmd[64];
+
+	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
+		return 0;
+	do {
+		os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
+
+	return -1;
+}
+
+
+static int wpa_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DEAUTHENTICATE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	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 */
+
+
+static int wpa_cli_cmd_suspend(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "SUSPEND");
+}
+
+
+static int wpa_cli_cmd_resume(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "RESUME");
+}
+
+
+#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[])
+{
+	return wpa_cli_cmd(ctrl, "ROAM", 1, argc, argv);
+}
+
+
+#ifdef CONFIG_MESH
+
+static int wpa_cli_cmd_mesh_interface_add(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MESH_INTERFACE_ADD", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_mesh_group_add(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MESH_GROUP_ADD", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_mesh_group_remove(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MESH_GROUP_REMOVE", 1, argc, argv);
+}
+
+#endif /* CONFIG_MESH */
+
+
+#ifdef CONFIG_P2P
+
+static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_FIND", 0, argc, argv);
+}
+
+
+static char ** wpa_cli_complete_p2p_find(const char *str, int pos)
+{
+	char **res = NULL;
+	int arg = get_cmd_arg_num(str, pos);
+
+	res = os_calloc(6, sizeof(char *));
+	if (res == NULL)
+		return NULL;
+	res[0] = os_strdup("type=social");
+	if (res[0] == NULL) {
+		os_free(res);
+		return NULL;
+	}
+	res[1] = os_strdup("type=progressive");
+	if (res[1] == NULL)
+		return res;
+	res[2] = os_strdup("delay=");
+	if (res[2] == NULL)
+		return res;
+	res[3] = os_strdup("dev_id=");
+	if (res[3] == NULL)
+		return res;
+	if (arg == 1)
+		res[4] = os_strdup("[timeout]");
+
+	return res;
+}
+
+
+static int wpa_cli_cmd_p2p_stop_find(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "P2P_STOP_FIND");
+}
+
+
+static int wpa_cli_cmd_p2p_asp_provision(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_ASP_PROVISION", 3, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_asp_provision_resp(struct wpa_ctrl *ctrl, int argc,
+					      char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_ASP_PROVISION_RESP", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_CONNECT", 2, argc, argv);
+}
+
+
+static char ** wpa_cli_complete_p2p_connect(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = cli_txt_list_array(&p2p_peers);
+		break;
+	}
+
+	return res;
+}
+
+
+static int wpa_cli_cmd_p2p_listen(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_LISTEN", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_group_remove(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_GROUP_REMOVE", 1, argc, argv);
+}
+
+
+static char ** wpa_cli_complete_p2p_group_remove(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = cli_txt_list_array(&p2p_groups);
+		break;
+	}
+
+	return res;
+}
+
+
+static int wpa_cli_cmd_p2p_group_add(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_GROUP_ADD", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	if (argc != 2 && argc != 3) {
+		printf("Invalid P2P_PROV_DISC command: needs at least "
+		       "two arguments, address and config method\n"
+		       "(display, keypad, or pbc) and an optional join\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "P2P_PROV_DISC", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_get_passphrase(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "P2P_GET_PASSPHRASE");
+}
+
+
+static int wpa_cli_cmd_p2p_serv_disc_req(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	char cmd[4096];
+
+	if (argc < 2) {
+		printf("Invalid P2P_SERV_DISC_REQ command: needs two "
+		       "or more arguments (address and TLVs)\n");
+		return -1;
+	}
+
+	if (write_cmd(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ", argc, argv) < 0)
+		return -1;
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_serv_disc_cancel_req(struct wpa_ctrl *ctrl,
+						int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_CANCEL_REQ", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_serv_disc_resp(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	char cmd[4096];
+	int res;
+
+	if (argc != 4) {
+		printf("Invalid P2P_SERV_DISC_RESP command: needs four "
+		       "arguments (freq, address, dialog token, and TLVs)\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_RESP %s %s %s %s",
+			  argv[0], argv[1], argv[2], argv[3]);
+	if (os_snprintf_error(sizeof(cmd), res))
+		return -1;
+	cmd[sizeof(cmd) - 1] = '\0';
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_service_update(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "P2P_SERVICE_UPDATE");
+}
+
+
+static int wpa_cli_cmd_p2p_serv_disc_external(struct wpa_ctrl *ctrl,
+					      int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_EXTERNAL", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_service_flush(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "P2P_SERVICE_FLUSH");
+}
+
+
+static int wpa_cli_cmd_p2p_service_add(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	if (argc < 3) {
+		printf("Invalid P2P_SERVICE_ADD command: needs 3-6 arguments\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "P2P_SERVICE_ADD", 3, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_service_rep(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	if (argc < 5 || argc > 6) {
+		printf("Invalid P2P_SERVICE_REP command: needs 5-6 "
+		       "arguments\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "P2P_SERVICE_REP", 5, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_service_del(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	char cmd[4096];
+	int res;
+
+	if (argc != 2 && argc != 3) {
+		printf("Invalid P2P_SERVICE_DEL command: needs two or three "
+		       "arguments\n");
+		return -1;
+	}
+
+	if (argc == 3)
+		res = os_snprintf(cmd, sizeof(cmd),
+				  "P2P_SERVICE_DEL %s %s %s",
+				  argv[0], argv[1], argv[2]);
+	else
+		res = os_snprintf(cmd, sizeof(cmd),
+				  "P2P_SERVICE_DEL %s %s",
+				  argv[0], argv[1]);
+	if (os_snprintf_error(sizeof(cmd), res))
+		return -1;
+	cmd[sizeof(cmd) - 1] = '\0';
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_reject(struct wpa_ctrl *ctrl,
+				  int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_REJECT", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_invite(struct wpa_ctrl *ctrl,
+				  int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_INVITE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_peer(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_PEER", 1, argc, argv);
+}
+
+
+static char ** wpa_cli_complete_p2p_peer(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = cli_txt_list_array(&p2p_peers);
+		break;
+	}
+
+	return res;
+}
+
+
+static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, char *cmd,
+				     char *addr, size_t addr_len,
+				     int discovered)
+{
+	char buf[4096], *pos;
+	size_t len;
+	int ret;
+
+	if (ctrl_conn == NULL)
+		return -1;
+	len = sizeof(buf) - 1;
+	ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
+			       wpa_cli_msg_cb);
+	if (ret == -2) {
+		printf("'%s' command timed out.\n", cmd);
+		return -2;
+	} else if (ret < 0) {
+		printf("'%s' command failed.\n", cmd);
+		return -1;
+	}
+
+	buf[len] = '\0';
+	if (os_memcmp(buf, "FAIL", 4) == 0)
+		return -1;
+
+	pos = buf;
+	while (*pos != '\0' && *pos != '\n')
+		pos++;
+	*pos++ = '\0';
+	os_strlcpy(addr, buf, addr_len);
+	if (!discovered || os_strstr(pos, "[PROBE_REQ_ONLY]") == NULL)
+		printf("%s\n", addr);
+	return 0;
+}
+
+
+static int wpa_cli_cmd_p2p_peers(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char addr[32], cmd[64];
+	int discovered;
+
+	discovered = argc > 0 && os_strcmp(argv[0], "discovered") == 0;
+
+	if (wpa_ctrl_command_p2p_peer(ctrl, "P2P_PEER FIRST",
+				      addr, sizeof(addr), discovered))
+		return -1;
+	do {
+		os_snprintf(cmd, sizeof(cmd), "P2P_PEER NEXT-%s", addr);
+	} while (wpa_ctrl_command_p2p_peer(ctrl, cmd, addr, sizeof(addr),
+			 discovered) == 0);
+
+	return 0;
+}
+
+
+static int wpa_cli_cmd_p2p_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_SET", 2, argc, argv);
+}
+
+
+static char ** wpa_cli_complete_p2p_set(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	const char *fields[] = {
+		"discoverability",
+		"managed",
+		"listen_channel",
+		"ssid_postfix",
+		"noa",
+		"ps",
+		"oppps",
+		"ctwindow",
+		"disabled",
+		"conc_pref",
+		"force_long_sd",
+		"peer_filter",
+		"cross_connect",
+		"go_apsd",
+		"client_apsd",
+		"disallow_freq",
+		"disc_int",
+		"per_sta_psk",
+	};
+	int i, num_fields = ARRAY_SIZE(fields);
+
+	if (arg == 1) {
+		char **res = os_calloc(num_fields + 1, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; i < num_fields; i++) {
+			res[i] = os_strdup(fields[i]);
+			if (res[i] == NULL)
+				return res;
+		}
+		return res;
+	}
+
+	if (arg == 2 && os_strncasecmp(str, "p2p_set peer_filter ", 20) == 0)
+		return cli_txt_list_array(&p2p_peers);
+
+	return NULL;
+}
+
+
+static int wpa_cli_cmd_p2p_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "P2P_FLUSH");
+}
+
+
+static int wpa_cli_cmd_p2p_cancel(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "P2P_CANCEL");
+}
+
+
+static int wpa_cli_cmd_p2p_unauthorize(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_UNAUTHORIZE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	if (argc != 0 && argc != 2 && argc != 4) {
+		printf("Invalid P2P_PRESENCE_REQ command: needs two arguments "
+		       "(preferred duration, interval; in microsecods).\n"
+		       "Optional second pair can be used to provide "
+		       "acceptable values.\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "P2P_PRESENCE_REQ", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	if (argc != 0 && argc != 2) {
+		printf("Invalid P2P_EXT_LISTEN command: needs two arguments "
+		       "(availability period, availability interval; in "
+		       "millisecods).\n"
+		       "Extended Listen Timing can be cancelled with this "
+		       "command when used without parameters.\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "P2P_EXT_LISTEN", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_remove_client(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_REMOVE_CLIENT", 1, argc, argv);
+}
+
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+static int wpa_cli_cmd_wfd_subelem_set(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	char cmd[100];
+	int res;
+
+	if (argc != 1 && argc != 2) {
+		printf("Invalid WFD_SUBELEM_SET command: needs one or two "
+		       "arguments (subelem, hexdump)\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_SET %s %s",
+			  argv[0], argc > 1 ? argv[1] : "");
+	if (os_snprintf_error(sizeof(cmd), res))
+		return -1;
+	cmd[sizeof(cmd) - 1] = '\0';
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_wfd_subelem_get(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	char cmd[100];
+	int res;
+
+	if (argc != 1) {
+		printf("Invalid WFD_SUBELEM_GET command: needs one "
+		       "argument (subelem)\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_GET %s",
+			  argv[0]);
+	if (os_snprintf_error(sizeof(cmd), res))
+		return -1;
+	cmd[sizeof(cmd) - 1] = '\0';
+	return wpa_ctrl_command(ctrl, cmd);
+}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+#ifdef CONFIG_INTERWORKING
+static int wpa_cli_cmd_fetch_anqp(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "FETCH_ANQP");
+}
+
+
+static int wpa_cli_cmd_stop_fetch_anqp(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "STOP_FETCH_ANQP");
+}
+
+
+static int wpa_cli_cmd_interworking_select(struct wpa_ctrl *ctrl, int argc,
+					   char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "INTERWORKING_SELECT", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_interworking_connect(struct wpa_ctrl *ctrl, int argc,
+					    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "INTERWORKING_CONNECT", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_interworking_add_network(struct wpa_ctrl *ctrl, int argc,
+						char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "INTERWORKING_ADD_NETWORK", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "ANQP_GET", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_gas_request(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "GAS_REQUEST", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_gas_response_get(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "GAS_RESPONSE_GET", 2, argc, argv);
+}
+#endif /* CONFIG_INTERWORKING */
+
+
+#ifdef CONFIG_HS20
+
+static int wpa_cli_cmd_hs20_anqp_get(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "HS20_ANQP_GET", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_get_nai_home_realm_list(struct wpa_ctrl *ctrl, int argc,
+					       char *argv[])
+{
+	char cmd[512];
+
+	if (argc == 0) {
+		printf("Command needs one or two arguments (dst mac addr and "
+		       "optional home realm)\n");
+		return -1;
+	}
+
+	if (write_cmd(cmd, sizeof(cmd), "HS20_GET_NAI_HOME_REALM_LIST",
+		      argc, argv) < 0)
+		return -1;
+
+	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 */
+
+
+static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "STA_AUTOCONNECT", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_tdls_discover(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "TDLS_DISCOVER", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_tdls_setup(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "TDLS_SETUP", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "TDLS_TEARDOWN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_tdls_link_status(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "TDLS_LINK_STATUS", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wmm_ac_addts(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WMM_AC_ADDTS", 3, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wmm_ac_delts(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WMM_AC_DELTS", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wmm_ac_status(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "WMM_AC_STATUS");
+}
+
+
+static int wpa_cli_cmd_tdls_chan_switch(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "TDLS_CHAN_SWITCH", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_tdls_cancel_chan_switch(struct wpa_ctrl *ctrl, int argc,
+					       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "TDLS_CANCEL_CHAN_SWITCH", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "SIGNAL_POLL");
+}
+
+
+static int wpa_cli_cmd_pktcnt_poll(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "PKTCNT_POLL");
+}
+
+
+static int wpa_cli_cmd_reauthenticate(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "REAUTHENTICATE");
+}
+
+
+#ifdef CONFIG_AUTOSCAN
+
+static int wpa_cli_cmd_autoscan(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	if (argc == 0)
+		return wpa_ctrl_command(ctrl, "AUTOSCAN ");
+
+	return wpa_cli_cmd(ctrl, "AUTOSCAN", 0, argc, argv);
+}
+
+#endif /* CONFIG_AUTOSCAN */
+
+
+#ifdef CONFIG_WNM
+
+static int wpa_cli_cmd_wnm_sleep(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WNM_SLEEP", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wnm_bss_query(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WNM_BSS_QUERY", 1, argc, argv);
+}
+
+#endif /* CONFIG_WNM */
+
+
+static int wpa_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	if (argc == 0)
+		return -1;
+	return wpa_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]);
+}
+
+
+#ifdef ANDROID
+static int wpa_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DRIVER", 1, argc, argv);
+}
+#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[])
+{
+	return wpa_ctrl_command(ctrl, "FLUSH");
+}
+
+
+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);
+}
+
+
+static int wpa_cli_cmd_neighbor_rep_request(struct wpa_ctrl *ctrl, int argc,
+					    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NEIGHBOR_REP_REQUEST", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "ERP_FLUSH");
+}
+
+
+static int wpa_cli_cmd_mac_rand_scan(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MAC_RAND_SCAN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_get_pref_freq_list(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "GET_PREF_FREQ_LIST", 1, argc, argv);
+}
+
+
+enum wpa_cli_cmd_flags {
+	cli_cmd_flag_none		= 0x00,
+	cli_cmd_flag_sensitive		= 0x01
+};
+
+struct wpa_cli_cmd {
+	const char *cmd;
+	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+	char ** (*completion)(const char *str, int pos);
+	enum wpa_cli_cmd_flags flags;
+	const char *usage;
+};
+
+static const struct wpa_cli_cmd wpa_cli_commands[] = {
+	{ "status", wpa_cli_cmd_status, NULL,
+	  cli_cmd_flag_none,
+	  "[verbose] = get current WPA/EAPOL/EAP status" },
+	{ "ifname", wpa_cli_cmd_ifname, NULL,
+	  cli_cmd_flag_none,
+	  "= get current interface name" },
+	{ "ping", wpa_cli_cmd_ping, NULL,
+	  cli_cmd_flag_none,
+	  "= pings wpa_supplicant" },
+	{ "relog", wpa_cli_cmd_relog, NULL,
+	  cli_cmd_flag_none,
+	  "= re-open log-file (allow rolling logs)" },
+	{ "note", wpa_cli_cmd_note, NULL,
+	  cli_cmd_flag_none,
+	  "<text> = add a note to wpa_supplicant debug log" },
+	{ "mib", wpa_cli_cmd_mib, NULL,
+	  cli_cmd_flag_none,
+	  "= get MIB variables (dot1x, dot11)" },
+	{ "help", wpa_cli_cmd_help, wpa_cli_complete_help,
+	  cli_cmd_flag_none,
+	  "[command] = show usage help" },
+	{ "interface", wpa_cli_cmd_interface, NULL,
+	  cli_cmd_flag_none,
+	  "[ifname] = show interfaces/select interface" },
+	{ "level", wpa_cli_cmd_level, NULL,
+	  cli_cmd_flag_none,
+	  "<debug level> = change debug level" },
+	{ "license", wpa_cli_cmd_license, NULL,
+	  cli_cmd_flag_none,
+	  "= show full wpa_cli license" },
+	{ "quit", wpa_cli_cmd_quit, NULL,
+	  cli_cmd_flag_none,
+	  "= exit wpa_cli" },
+	{ "set", wpa_cli_cmd_set, wpa_cli_complete_set,
+	  cli_cmd_flag_none,
+	  "= set variables (shows list of variables when run without "
+	  "arguments)" },
+	{ "dump", wpa_cli_cmd_dump, NULL,
+	  cli_cmd_flag_none,
+	  "= dump config variables" },
+	{ "get", wpa_cli_cmd_get, wpa_cli_complete_get,
+	  cli_cmd_flag_none,
+	  "<name> = get information" },
+	{ "logon", wpa_cli_cmd_logon, NULL,
+	  cli_cmd_flag_none,
+	  "= IEEE 802.1X EAPOL state machine logon" },
+	{ "logoff", wpa_cli_cmd_logoff, NULL,
+	  cli_cmd_flag_none,
+	  "= IEEE 802.1X EAPOL state machine logoff" },
+	{ "pmksa", wpa_cli_cmd_pmksa, NULL,
+	  cli_cmd_flag_none,
+	  "= show PMKSA cache" },
+	{ "pmksa_flush", wpa_cli_cmd_pmksa_flush, NULL,
+	  cli_cmd_flag_none,
+	  "= flush PMKSA cache entries" },
+	{ "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" },
+	{ "identity", wpa_cli_cmd_identity, NULL,
+	  cli_cmd_flag_none,
+	  "<network id> <identity> = configure identity for an SSID" },
+	{ "password", wpa_cli_cmd_password, NULL,
+	  cli_cmd_flag_sensitive,
+	  "<network id> <password> = configure password for an SSID" },
+	{ "new_password", wpa_cli_cmd_new_password, NULL,
+	  cli_cmd_flag_sensitive,
+	  "<network id> <password> = change password for an SSID" },
+	{ "pin", wpa_cli_cmd_pin, NULL,
+	  cli_cmd_flag_sensitive,
+	  "<network id> <pin> = configure pin for an SSID" },
+	{ "otp", wpa_cli_cmd_otp, NULL,
+	  cli_cmd_flag_sensitive,
+	  "<network id> <password> = configure one-time-password for an SSID"
+	},
+	{ "passphrase", wpa_cli_cmd_passphrase, NULL,
+	  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" },
+	{ "blacklist", wpa_cli_cmd_blacklist, wpa_cli_complete_bss,
+	  cli_cmd_flag_none,
+	  "<BSSID> = add a BSSID to the blacklist\n"
+	  "blacklist clear = clear the blacklist\n"
+	  "blacklist = display the blacklist" },
+	{ "log_level", wpa_cli_cmd_log_level, NULL,
+	  cli_cmd_flag_none,
+	  "<level> [<timestamp>] = update the log level/timestamp\n"
+	  "log_level = display the current log level and log options" },
+	{ "list_networks", wpa_cli_cmd_list_networks, NULL,
+	  cli_cmd_flag_none,
+	  "= list configured networks" },
+	{ "select_network", wpa_cli_cmd_select_network,
+	  wpa_cli_complete_network_id,
+	  cli_cmd_flag_none,
+	  "<network id> = select a network (disable others)" },
+	{ "enable_network", wpa_cli_cmd_enable_network,
+	  wpa_cli_complete_network_id,
+	  cli_cmd_flag_none,
+	  "<network id> = enable a network" },
+	{ "disable_network", wpa_cli_cmd_disable_network,
+	  wpa_cli_complete_network_id,
+	  cli_cmd_flag_none,
+	  "<network id> = disable a network" },
+	{ "add_network", wpa_cli_cmd_add_network, NULL,
+	  cli_cmd_flag_none,
+	  "= add a network" },
+	{ "remove_network", wpa_cli_cmd_remove_network,
+	  wpa_cli_complete_network_id,
+	  cli_cmd_flag_none,
+	  "<network id> = remove a network" },
+	{ "set_network", wpa_cli_cmd_set_network, wpa_cli_complete_network,
+	  cli_cmd_flag_sensitive,
+	  "<network id> <variable> <value> = set network variables (shows\n"
+	  "  list of variables when run without arguments)" },
+	{ "get_network", wpa_cli_cmd_get_network, wpa_cli_complete_network,
+	  cli_cmd_flag_none,
+	  "<network id> <variable> = get network variables" },
+	{ "dup_network", wpa_cli_cmd_dup_network, wpa_cli_complete_dup_network,
+	  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" },
+	{ "add_cred", wpa_cli_cmd_add_cred, NULL,
+	  cli_cmd_flag_none,
+	  "= add a credential" },
+	{ "remove_cred", wpa_cli_cmd_remove_cred, NULL,
+	  cli_cmd_flag_none,
+	  "<cred id> = remove a credential" },
+	{ "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" },
+	{ "disconnect", wpa_cli_cmd_disconnect, NULL,
+	  cli_cmd_flag_none,
+	  "= disconnect and wait for reassociate/reconnect command before\n"
+	  "  connecting" },
+	{ "reconnect", wpa_cli_cmd_reconnect, NULL,
+	  cli_cmd_flag_none,
+	  "= like reassociate, but only takes effect if already disconnected"
+	},
+	{ "scan", wpa_cli_cmd_scan, NULL,
+	  cli_cmd_flag_none,
+	  "= request new BSS scan" },
+	{ "scan_results", wpa_cli_cmd_scan_results, NULL,
+	  cli_cmd_flag_none,
+	  "= get latest scan results" },
+	{ "bss", wpa_cli_cmd_bss, wpa_cli_complete_bss,
+	  cli_cmd_flag_none,
+	  "<<idx> | <bssid>> = get detailed scan result info" },
+	{ "get_capability", wpa_cli_cmd_get_capability, NULL,
+	  cli_cmd_flag_none,
+	  "<eap/pairwise/group/key_mgmt/proto/auth_alg/channels/freq/modes> "
+	  "= get capabilities" },
+	{ "reconfigure", wpa_cli_cmd_reconfigure, NULL,
+	  cli_cmd_flag_none,
+	  "= force wpa_supplicant to re-read its configuration file" },
+	{ "terminate", wpa_cli_cmd_terminate, NULL,
+	  cli_cmd_flag_none,
+	  "= terminate wpa_supplicant" },
+	{ "interface_add", wpa_cli_cmd_interface_add, NULL,
+	  cli_cmd_flag_none,
+	  "<ifname> <confname> <driver> <ctrl_interface> <driver_param>\n"
+	  "  <bridge_name> = adds new interface, all parameters but <ifname>\n"
+	  "  are optional" },
+	{ "interface_remove", wpa_cli_cmd_interface_remove, NULL,
+	  cli_cmd_flag_none,
+	  "<ifname> = removes the interface" },
+	{ "interface_list", wpa_cli_cmd_interface_list, NULL,
+	  cli_cmd_flag_none,
+	  "= list available interfaces" },
+	{ "ap_scan", wpa_cli_cmd_ap_scan, NULL,
+	  cli_cmd_flag_none,
+	  "<value> = set ap_scan parameter" },
+	{ "scan_interval", wpa_cli_cmd_scan_interval, NULL,
+	  cli_cmd_flag_none,
+	  "<value> = set scan_interval parameter (in seconds)" },
+	{ "bss_expire_age", wpa_cli_cmd_bss_expire_age, NULL,
+	  cli_cmd_flag_none,
+	  "<value> = set BSS expiration age parameter" },
+	{ "bss_expire_count", wpa_cli_cmd_bss_expire_count, NULL,
+	  cli_cmd_flag_none,
+	  "<value> = set BSS expiration scan count parameter" },
+	{ "bss_flush", wpa_cli_cmd_bss_flush, NULL,
+	  cli_cmd_flag_none,
+	  "<value> = set BSS flush age (0 by default)" },
+	{ "stkstart", wpa_cli_cmd_stkstart, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = request STK negotiation with <addr>" },
+	{ "ft_ds", wpa_cli_cmd_ft_ds, wpa_cli_complete_bss,
+	  cli_cmd_flag_none,
+	  "<addr> = request over-the-DS FT with <addr>" },
+	{ "wps_pbc", wpa_cli_cmd_wps_pbc, wpa_cli_complete_bss,
+	  cli_cmd_flag_none,
+	  "[BSSID] = start Wi-Fi Protected Setup: Push Button Configuration" },
+	{ "wps_pin", wpa_cli_cmd_wps_pin, wpa_cli_complete_bss,
+	  cli_cmd_flag_sensitive,
+	  "<BSSID> [PIN] = start WPS PIN method (returns PIN, if not "
+	  "hardcoded)" },
+	{ "wps_check_pin", wpa_cli_cmd_wps_check_pin, NULL,
+	  cli_cmd_flag_sensitive,
+	  "<PIN> = verify PIN checksum" },
+	{ "wps_cancel", wpa_cli_cmd_wps_cancel, NULL, cli_cmd_flag_none,
+	  "Cancels the pending WPS operation" },
+#ifdef CONFIG_WPS_NFC
+	{ "wps_nfc", wpa_cli_cmd_wps_nfc, wpa_cli_complete_bss,
+	  cli_cmd_flag_none,
+	  "[BSSID] = start Wi-Fi Protected Setup: NFC" },
+	{ "wps_nfc_config_token", wpa_cli_cmd_wps_nfc_config_token, NULL,
+	  cli_cmd_flag_none,
+	  "<WPS|NDEF> = build configuration token" },
+	{ "wps_nfc_token", wpa_cli_cmd_wps_nfc_token, NULL,
+	  cli_cmd_flag_none,
+	  "<WPS|NDEF> = create password token" },
+	{ "wps_nfc_tag_read", wpa_cli_cmd_wps_nfc_tag_read, NULL,
+	  cli_cmd_flag_sensitive,
+	  "<hexdump of payload> = report read NFC tag with WPS data" },
+	{ "nfc_get_handover_req", wpa_cli_cmd_nfc_get_handover_req, NULL,
+	  cli_cmd_flag_none,
+	  "<NDEF> <WPS> = create NFC handover request" },
+	{ "nfc_get_handover_sel", wpa_cli_cmd_nfc_get_handover_sel, NULL,
+	  cli_cmd_flag_none,
+	  "<NDEF> <WPS> = create 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 "
+	  "NFC handover" },
+#endif /* CONFIG_WPS_NFC */
+	{ "wps_reg", wpa_cli_cmd_wps_reg, wpa_cli_complete_bss,
+	  cli_cmd_flag_sensitive,
+	  "<BSSID> <AP PIN> = start WPS Registrar to configure an AP" },
+	{ "wps_ap_pin", wpa_cli_cmd_wps_ap_pin, NULL,
+	  cli_cmd_flag_sensitive,
+	  "[params..] = enable/disable AP PIN" },
+	{ "wps_er_start", wpa_cli_cmd_wps_er_start, NULL,
+	  cli_cmd_flag_none,
+	  "[IP address] = start Wi-Fi Protected Setup External Registrar" },
+	{ "wps_er_stop", wpa_cli_cmd_wps_er_stop, NULL,
+	  cli_cmd_flag_none,
+	  "= stop Wi-Fi Protected Setup External Registrar" },
+	{ "wps_er_pin", wpa_cli_cmd_wps_er_pin, NULL,
+	  cli_cmd_flag_sensitive,
+	  "<UUID> <PIN> = add an Enrollee PIN to External Registrar" },
+	{ "wps_er_pbc", wpa_cli_cmd_wps_er_pbc, NULL,
+	  cli_cmd_flag_none,
+	  "<UUID> = accept an Enrollee PBC using External Registrar" },
+	{ "wps_er_learn", wpa_cli_cmd_wps_er_learn, NULL,
+	  cli_cmd_flag_sensitive,
+	  "<UUID> <PIN> = learn AP configuration" },
+	{ "wps_er_set_config", wpa_cli_cmd_wps_er_set_config, NULL,
+	  cli_cmd_flag_none,
+	  "<UUID> <network id> = set AP configuration for enrolling" },
+	{ "wps_er_config", wpa_cli_cmd_wps_er_config, NULL,
+	  cli_cmd_flag_sensitive,
+	  "<UUID> <PIN> <SSID> <auth> <encr> <key> = configure AP" },
+#ifdef CONFIG_WPS_NFC
+	{ "wps_er_nfc_config_token", wpa_cli_cmd_wps_er_nfc_config_token, NULL,
+	  cli_cmd_flag_none,
+	  "<WPS/NDEF> <UUID> = build NFC configuration token" },
+#endif /* CONFIG_WPS_NFC */
+	{ "ibss_rsn", wpa_cli_cmd_ibss_rsn, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = request RSN authentication with <addr> in IBSS" },
+#ifdef CONFIG_AP
+	{ "sta", wpa_cli_cmd_sta, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = get information about an associated station (AP)" },
+	{ "all_sta", wpa_cli_cmd_all_sta, NULL,
+	  cli_cmd_flag_none,
+	  "= get information about all associated stations (AP)" },
+	{ "deauthenticate", wpa_cli_cmd_deauthenticate, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = deauthenticate a station" },
+	{ "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" },
+#ifdef CONFIG_MESH
+	{ "mesh_interface_add", wpa_cli_cmd_mesh_interface_add, NULL,
+	  cli_cmd_flag_none,
+	  "[ifname] = Create a new mesh interface" },
+	{ "mesh_group_add", wpa_cli_cmd_mesh_group_add, NULL,
+	  cli_cmd_flag_none,
+	  "<network id> = join a mesh network (disable others)" },
+	{ "mesh_group_remove", wpa_cli_cmd_mesh_group_remove, NULL,
+	  cli_cmd_flag_none,
+	  "<ifname> = Remove mesh group interface" },
+#endif /* CONFIG_MESH */
+#ifdef CONFIG_P2P
+	{ "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find,
+	  cli_cmd_flag_none,
+	  "[timeout] [type=*] = find P2P Devices for up-to timeout seconds" },
+	{ "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, NULL, cli_cmd_flag_none,
+	  "= stop P2P Devices search" },
+	{ "p2p_asp_provision", wpa_cli_cmd_p2p_asp_provision, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> adv_id=<adv_id> conncap=<conncap> [info=<infodata>] = provision with a P2P ASP Device" },
+	{ "p2p_asp_provision_resp", wpa_cli_cmd_p2p_asp_provision_resp, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> adv_id=<adv_id> [role<conncap>] [info=<infodata>] = provision with a P2P ASP Device" },
+	{ "p2p_connect", wpa_cli_cmd_p2p_connect, wpa_cli_complete_p2p_connect,
+	  cli_cmd_flag_none,
+	  "<addr> <\"pbc\"|PIN> [ht40] = connect to a P2P Device" },
+	{ "p2p_listen", wpa_cli_cmd_p2p_listen, NULL, cli_cmd_flag_none,
+	  "[timeout] = listen for P2P Devices for up-to timeout seconds" },
+	{ "p2p_group_remove", wpa_cli_cmd_p2p_group_remove,
+	  wpa_cli_complete_p2p_group_remove, cli_cmd_flag_none,
+	  "<ifname> = remove P2P group interface (terminate group if GO)" },
+	{ "p2p_group_add", wpa_cli_cmd_p2p_group_add, NULL, cli_cmd_flag_none,
+	  "[ht40] = add a new P2P group (local end as GO)" },
+	{ "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc,
+	  wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
+	  "<addr> <method> = request provisioning discovery" },
+	{ "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase, NULL,
+	  cli_cmd_flag_none,
+	  "= get the passphrase for a group (GO only)" },
+	{ "p2p_serv_disc_req", wpa_cli_cmd_p2p_serv_disc_req,
+	  wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
+	  "<addr> <TLVs> = schedule service discovery request" },
+	{ "p2p_serv_disc_cancel_req", wpa_cli_cmd_p2p_serv_disc_cancel_req,
+	  NULL, cli_cmd_flag_none,
+	  "<id> = cancel pending service discovery request" },
+	{ "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp, NULL,
+	  cli_cmd_flag_none,
+	  "<freq> <addr> <dialog token> <TLVs> = service discovery response" },
+	{ "p2p_service_update", wpa_cli_cmd_p2p_service_update, NULL,
+	  cli_cmd_flag_none,
+	  "= indicate change in local services" },
+	{ "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external, NULL,
+	  cli_cmd_flag_none,
+	  "<external> = set external processing of service discovery" },
+	{ "p2p_service_flush", wpa_cli_cmd_p2p_service_flush, NULL,
+	  cli_cmd_flag_none,
+	  "= remove all stored service entries" },
+	{ "p2p_service_add", wpa_cli_cmd_p2p_service_add, NULL,
+	  cli_cmd_flag_none,
+	  "<bonjour|upnp|asp> <query|version> <response|service> = add a local "
+	  "service" },
+	{ "p2p_service_rep", wpa_cli_cmd_p2p_service_rep, NULL,
+	  cli_cmd_flag_none,
+	  "asp <auto> <adv_id> <svc_state> <svc_string> [<svc_info>] = replace "
+	  "local ASP service" },
+	{ "p2p_service_del", wpa_cli_cmd_p2p_service_del, NULL,
+	  cli_cmd_flag_none,
+	  "<bonjour|upnp> <query|version> [|service] = remove a local "
+	  "service" },
+	{ "p2p_reject", wpa_cli_cmd_p2p_reject, wpa_cli_complete_p2p_peer,
+	  cli_cmd_flag_none,
+	  "<addr> = reject connection attempts from a specific peer" },
+	{ "p2p_invite", wpa_cli_cmd_p2p_invite, NULL,
+	  cli_cmd_flag_none,
+	  "<cmd> [peer=addr] = invite peer" },
+	{ "p2p_peers", wpa_cli_cmd_p2p_peers, NULL, cli_cmd_flag_none,
+	  "[discovered] = list known (optionally, only fully discovered) P2P "
+	  "peers" },
+	{ "p2p_peer", wpa_cli_cmd_p2p_peer, wpa_cli_complete_p2p_peer,
+	  cli_cmd_flag_none,
+	  "<address> = show information about known P2P peer" },
+	{ "p2p_set", wpa_cli_cmd_p2p_set, wpa_cli_complete_p2p_set,
+	  cli_cmd_flag_none,
+	  "<field> <value> = set a P2P parameter" },
+	{ "p2p_flush", wpa_cli_cmd_p2p_flush, NULL, cli_cmd_flag_none,
+	  "= flush P2P state" },
+	{ "p2p_cancel", wpa_cli_cmd_p2p_cancel, NULL, cli_cmd_flag_none,
+	  "= cancel P2P group formation" },
+	{ "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize,
+	  wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
+	  "<address> = unauthorize a peer" },
+	{ "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, NULL,
+	  cli_cmd_flag_none,
+	  "[<duration> <interval>] [<duration> <interval>] = request GO "
+	  "presence" },
+	{ "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, NULL,
+	  cli_cmd_flag_none,
+	  "[<period> <interval>] = set extended listen timing" },
+	{ "p2p_remove_client", wpa_cli_cmd_p2p_remove_client,
+	  wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
+	  "<address|iface=address> = remove a peer from all groups" },
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_WIFI_DISPLAY
+	{ "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL,
+	  cli_cmd_flag_none,
+	  "<subelem> [contents] = set Wi-Fi Display subelement" },
+	{ "wfd_subelem_get", wpa_cli_cmd_wfd_subelem_get, NULL,
+	  cli_cmd_flag_none,
+	  "<subelem> = get Wi-Fi Display subelement" },
+#endif /* CONFIG_WIFI_DISPLAY */
+#ifdef CONFIG_INTERWORKING
+	{ "fetch_anqp", wpa_cli_cmd_fetch_anqp, NULL, cli_cmd_flag_none,
+	  "= fetch ANQP information for all APs" },
+	{ "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, NULL,
+	  cli_cmd_flag_none,
+	  "= stop fetch_anqp operation" },
+	{ "interworking_select", wpa_cli_cmd_interworking_select, NULL,
+	  cli_cmd_flag_none,
+	  "[auto] = perform Interworking network selection" },
+	{ "interworking_connect", wpa_cli_cmd_interworking_connect,
+	  wpa_cli_complete_bss, cli_cmd_flag_none,
+	  "<BSSID> = connect using Interworking credentials" },
+	{ "interworking_add_network", wpa_cli_cmd_interworking_add_network,
+	  wpa_cli_complete_bss, cli_cmd_flag_none,
+	  "<BSSID> = connect using Interworking credentials" },
+	{ "anqp_get", wpa_cli_cmd_anqp_get, wpa_cli_complete_bss,
+	  cli_cmd_flag_none,
+	  "<addr> <info id>[,<info id>]... = request ANQP information" },
+	{ "gas_request", wpa_cli_cmd_gas_request, wpa_cli_complete_bss,
+	  cli_cmd_flag_none,
+	  "<addr> <AdvProtoID> [QueryReq] = GAS request" },
+	{ "gas_response_get", wpa_cli_cmd_gas_response_get,
+	  wpa_cli_complete_bss, cli_cmd_flag_none,
+	  "<addr> <dialog token> [start,len] = Fetch last GAS response" },
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+	{ "hs20_anqp_get", wpa_cli_cmd_hs20_anqp_get, wpa_cli_complete_bss,
+	  cli_cmd_flag_none,
+	  "<addr> <subtype>[,<subtype>]... = request HS 2.0 ANQP information"
+	},
+	{ "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,
+	  "<0/1> = disable/enable automatic reconnection" },
+	{ "tdls_discover", wpa_cli_cmd_tdls_discover, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = request TDLS discovery with <addr>" },
+	{ "tdls_setup", wpa_cli_cmd_tdls_setup, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = request TDLS setup with <addr>" },
+	{ "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = tear down TDLS with <addr>" },
+	{ "tdls_link_status", wpa_cli_cmd_tdls_link_status, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = TDLS link status with <addr>" },
+	{ "wmm_ac_addts", wpa_cli_cmd_wmm_ac_addts, NULL,
+	  cli_cmd_flag_none,
+	  "<uplink/downlink/bidi> <tsid=0..7> <up=0..7> [nominal_msdu_size=#] "
+	  "[mean_data_rate=#] [min_phy_rate=#] [sba=#] [fixed_nominal_msdu] "
+	  "= add WMM-AC traffic stream" },
+	{ "wmm_ac_delts", wpa_cli_cmd_wmm_ac_delts, NULL,
+	  cli_cmd_flag_none,
+	  "<tsid> = delete WMM-AC traffic stream" },
+	{ "wmm_ac_status", wpa_cli_cmd_wmm_ac_status, NULL,
+	  cli_cmd_flag_none,
+	  "= show status for Wireless Multi-Media Admission-Control" },
+	{ "tdls_chan_switch", wpa_cli_cmd_tdls_chan_switch, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> <oper class> <freq> [sec_channel_offset=] [center_freq1=] "
+	  "[center_freq2=] [bandwidth=] [ht|vht] = enable channel switching "
+	  "with TDLS peer" },
+	{ "tdls_cancel_chan_switch", wpa_cli_cmd_tdls_cancel_chan_switch, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = disable channel switching with TDLS peer <addr>" },
+	{ "signal_poll", wpa_cli_cmd_signal_poll, NULL,
+	  cli_cmd_flag_none,
+	  "= get signal parameters" },
+	{ "pktcnt_poll", wpa_cli_cmd_pktcnt_poll, NULL,
+	  cli_cmd_flag_none,
+	  "= get TX/RX packet counters" },
+	{ "reauthenticate", wpa_cli_cmd_reauthenticate, NULL,
+	  cli_cmd_flag_none,
+	  "= trigger IEEE 802.1X/EAPOL reauthentication" },
+#ifdef CONFIG_AUTOSCAN
+	{ "autoscan", wpa_cli_cmd_autoscan, NULL, cli_cmd_flag_none,
+	  "[params] = Set or unset (if none) autoscan parameters" },
+#endif /* CONFIG_AUTOSCAN */
+#ifdef CONFIG_WNM
+	{ "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none,
+	  "<enter/exit> [interval=#] = enter/exit WNM-Sleep mode" },
+	{ "wnm_bss_query", wpa_cli_cmd_wnm_bss_query, NULL, cli_cmd_flag_none,
+	  "<query reason> = Send BSS Transition Management Query" },
+#endif /* CONFIG_WNM */
+	{ "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
+	  "<params..> = Sent unprocessed command" },
+	{ "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,
+	  "<command> = driver private commands" },
+#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"
+	},
+	{ "neighbor_rep_request",
+	  wpa_cli_cmd_neighbor_rep_request, NULL, cli_cmd_flag_none,
+	  "[ssid=<SSID>] = Trigger request to AP for neighboring AP report "
+	  "(with optional given SSID, default: current SSID)"
+	},
+	{ "erp_flush", wpa_cli_cmd_erp_flush, NULL, cli_cmd_flag_none,
+	  "= flush ERP keys" },
+	{ "mac_rand_scan",
+	  wpa_cli_cmd_mac_rand_scan, NULL, cli_cmd_flag_none,
+	  "<scan|sched|pno|all> enable=<0/1> [addr=mac-address "
+	  "mask=mac-address-mask] = scan MAC randomization"
+	},
+	{ "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL,
+	  cli_cmd_flag_none,
+	  "<interface type> = retrieve preferred freq list for the specified interface type" },
+	{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
+};
+
+
+/*
+ * Prints command usage, lines are padded with the specified string.
+ */
+static void print_cmd_help(const struct wpa_cli_cmd *cmd, const char *pad)
+{
+	char c;
+	size_t n;
+
+	printf("%s%s ", pad, cmd->cmd);
+	for (n = 0; (c = cmd->usage[n]); n++) {
+		printf("%c", c);
+		if (c == '\n')
+			printf("%s", pad);
+	}
+	printf("\n");
+}
+
+
+static void print_help(const char *cmd)
+{
+	int n;
+	printf("commands:\n");
+	for (n = 0; wpa_cli_commands[n].cmd; n++) {
+		if (cmd == NULL || str_starts(wpa_cli_commands[n].cmd, cmd))
+			print_cmd_help(&wpa_cli_commands[n], "  ");
+	}
+}
+
+
+static int wpa_cli_edit_filter_history_cb(void *ctx, const char *cmd)
+{
+	const char *c, *delim;
+	int n;
+	size_t len;
+
+	delim = os_strchr(cmd, ' ');
+	if (delim)
+		len = delim - cmd;
+	else
+		len = os_strlen(cmd);
+
+	for (n = 0; (c = wpa_cli_commands[n].cmd); n++) {
+		if (os_strncasecmp(cmd, c, len) == 0 && len == os_strlen(c))
+			return (wpa_cli_commands[n].flags &
+				cli_cmd_flag_sensitive);
+	}
+	return 0;
+}
+
+
+static char ** wpa_list_cmd_list(void)
+{
+	char **res;
+	int i, count;
+	struct cli_txt_entry *e;
+
+	count = ARRAY_SIZE(wpa_cli_commands);
+	count += dl_list_len(&p2p_groups);
+	count += dl_list_len(&ifnames);
+	res = os_calloc(count + 1, sizeof(char *));
+	if (res == NULL)
+		return NULL;
+
+	for (i = 0; wpa_cli_commands[i].cmd; i++) {
+		res[i] = os_strdup(wpa_cli_commands[i].cmd);
+		if (res[i] == NULL)
+			break;
+	}
+
+	dl_list_for_each(e, &p2p_groups, struct cli_txt_entry, list) {
+		size_t len = 8 + os_strlen(e->txt);
+		res[i] = os_malloc(len);
+		if (res[i] == NULL)
+			break;
+		os_snprintf(res[i], len, "ifname=%s", e->txt);
+		i++;
+	}
+
+	dl_list_for_each(e, &ifnames, struct cli_txt_entry, list) {
+		res[i] = os_strdup(e->txt);
+		if (res[i] == NULL)
+			break;
+		i++;
+	}
+
+	return res;
+}
+
+
+static char ** wpa_cli_cmd_completion(const char *cmd, const char *str,
+				      int pos)
+{
+	int i;
+
+	for (i = 0; wpa_cli_commands[i].cmd; i++) {
+		if (os_strcasecmp(wpa_cli_commands[i].cmd, cmd) == 0) {
+			if (wpa_cli_commands[i].completion)
+				return wpa_cli_commands[i].completion(str,
+								      pos);
+			edit_clear_line();
+			printf("\r%s\n", wpa_cli_commands[i].usage);
+			edit_redraw();
+			break;
+		}
+	}
+
+	return NULL;
+}
+
+
+static char ** wpa_cli_edit_completion_cb(void *ctx, const char *str, int pos)
+{
+	char **res;
+	const char *end;
+	char *cmd;
+
+	if (pos > 7 && os_strncasecmp(str, "IFNAME=", 7) == 0) {
+		end = os_strchr(str, ' ');
+		if (end && pos > end - str) {
+			pos -= end - str + 1;
+			str = end + 1;
+		}
+	}
+
+	end = os_strchr(str, ' ');
+	if (end == NULL || str + pos < end)
+		return wpa_list_cmd_list();
+
+	cmd = os_malloc(pos + 1);
+	if (cmd == NULL)
+		return NULL;
+	os_memcpy(cmd, str, pos);
+	cmd[end - str] = '\0';
+	res = wpa_cli_cmd_completion(cmd, str, pos);
+	os_free(cmd);
+	return res;
+}
+
+
+static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	const struct wpa_cli_cmd *cmd, *match = NULL;
+	int count;
+	int ret = 0;
+
+	if (argc > 1 && os_strncasecmp(argv[0], "IFNAME=", 7) == 0) {
+		ifname_prefix = argv[0] + 7;
+		argv = &argv[1];
+		argc--;
+	} else
+		ifname_prefix = NULL;
+
+	if (argc == 0)
+		return -1;
+
+	count = 0;
+	cmd = wpa_cli_commands;
+	while (cmd->cmd) {
+		if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0)
+		{
+			match = cmd;
+			if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
+				/* we have an exact match */
+				count = 1;
+				break;
+			}
+			count++;
+		}
+		cmd++;
+	}
+
+	if (count > 1) {
+		printf("Ambiguous command '%s'; possible commands:", argv[0]);
+		cmd = wpa_cli_commands;
+		while (cmd->cmd) {
+			if (os_strncasecmp(cmd->cmd, argv[0],
+					   os_strlen(argv[0])) == 0) {
+				printf(" %s", cmd->cmd);
+			}
+			cmd++;
+		}
+		printf("\n");
+		ret = 1;
+	} else if (count == 0) {
+		printf("Unknown command '%s'\n", argv[0]);
+		ret = 1;
+	} else {
+		ret = match->handler(ctrl, argc - 1, &argv[1]);
+	}
+
+	return ret;
+}
+
+
+static int str_match(const char *a, const char *b)
+{
+	return os_strncmp(a, b, os_strlen(b)) == 0;
+}
+
+
+static int wpa_cli_exec(const char *program, const char *arg1,
+			const char *arg2)
+{
+	char *arg;
+	size_t len;
+	int res;
+
+	len = os_strlen(arg1) + os_strlen(arg2) + 2;
+	arg = os_malloc(len);
+	if (arg == NULL)
+		return -1;
+	os_snprintf(arg, len, "%s %s", arg1, arg2);
+	res = os_exec(program, arg, 1);
+	os_free(arg);
+
+	return res;
+}
+
+
+static void wpa_cli_action_process(const char *msg)
+{
+	const char *pos;
+	char *copy = NULL, *id, *pos2;
+	const char *ifname = ctrl_ifname;
+	char ifname_buf[100];
+
+	if (eloop_terminated())
+		return;
+
+	pos = msg;
+	if (os_strncmp(pos, "IFNAME=", 7) == 0) {
+		const char *end;
+		end = os_strchr(pos + 7, ' ');
+		if (end && (unsigned int) (end - pos) < sizeof(ifname_buf)) {
+			pos += 7;
+			os_memcpy(ifname_buf, pos, end - pos);
+			ifname_buf[end - pos] = '\0';
+			ifname = ifname_buf;
+			pos = end + 1;
+		}
+	}
+	if (*pos == '<') {
+		const char *prev = pos;
+		/* skip priority */
+		pos = os_strchr(pos, '>');
+		if (pos)
+			pos++;
+		else
+			pos = prev;
+	}
+
+	if (str_match(pos, WPA_EVENT_CONNECTED)) {
+		int new_id = -1;
+		os_unsetenv("WPA_ID");
+		os_unsetenv("WPA_ID_STR");
+		os_unsetenv("WPA_CTRL_DIR");
+
+		pos = os_strstr(pos, "[id=");
+		if (pos)
+			copy = os_strdup(pos + 4);
+
+		if (copy) {
+			pos2 = id = copy;
+			while (*pos2 && *pos2 != ' ')
+				pos2++;
+			*pos2++ = '\0';
+			new_id = atoi(id);
+			os_setenv("WPA_ID", id, 1);
+			while (*pos2 && *pos2 != '=')
+				pos2++;
+			if (*pos2 == '=')
+				pos2++;
+			id = pos2;
+			while (*pos2 && *pos2 != ']')
+				pos2++;
+			*pos2 = '\0';
+			os_setenv("WPA_ID_STR", id, 1);
+			os_free(copy);
+		}
+
+		os_setenv("WPA_CTRL_DIR", ctrl_iface_dir, 1);
+
+		if (wpa_cli_connected <= 0 || new_id != wpa_cli_last_id) {
+			wpa_cli_connected = 1;
+			wpa_cli_last_id = new_id;
+			wpa_cli_exec(action_file, ifname, "CONNECTED");
+		}
+	} else if (str_match(pos, WPA_EVENT_DISCONNECTED)) {
+		if (wpa_cli_connected) {
+			wpa_cli_connected = 0;
+			wpa_cli_exec(action_file, ifname, "DISCONNECTED");
+		}
+	} else if (str_match(pos, MESH_GROUP_STARTED)) {
+		wpa_cli_exec(action_file, ctrl_ifname, pos);
+	} else if (str_match(pos, MESH_GROUP_REMOVED)) {
+		wpa_cli_exec(action_file, ctrl_ifname, pos);
+	} else if (str_match(pos, MESH_PEER_CONNECTED)) {
+		wpa_cli_exec(action_file, ctrl_ifname, pos);
+	} else if (str_match(pos, MESH_PEER_DISCONNECTED)) {
+		wpa_cli_exec(action_file, ctrl_ifname, pos);
+	} else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_match(pos, P2P_EVENT_GO_NEG_FAILURE)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_match(pos, WPS_EVENT_SUCCESS)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_match(pos, WPS_EVENT_FAIL)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_match(pos, AP_STA_CONNECTED)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_match(pos, AP_STA_DISCONNECTED)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_match(pos, ESS_DISASSOC_IMMINENT)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_match(pos, HS20_SUBSCRIPTION_REMEDIATION)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_match(pos, HS20_DEAUTH_IMMINENT_NOTICE)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_match(pos, WPA_EVENT_TERMINATING)) {
+		printf("wpa_supplicant is terminating - stop monitoring\n");
+		wpa_cli_quit = 1;
+	}
+}
+
+
+#ifndef CONFIG_ANSI_C_EXTRA
+static void wpa_cli_action_cb(char *msg, size_t len)
+{
+	wpa_cli_action_process(msg);
+}
+#endif /* CONFIG_ANSI_C_EXTRA */
+
+
+static void wpa_cli_reconnect(void)
+{
+	wpa_cli_close_connection();
+	if (wpa_cli_open_connection(ctrl_ifname, 1) < 0)
+		return;
+
+	if (interactive) {
+		edit_clear_line();
+		printf("\rConnection to wpa_supplicant re-established\n");
+		edit_redraw();
+	}
+}
+
+
+static void cli_event(const char *str)
+{
+	const char *start, *s;
+
+	start = os_strchr(str, '>');
+	if (start == NULL)
+		return;
+
+	start++;
+
+	if (str_starts(start, WPA_EVENT_BSS_ADDED)) {
+		s = os_strchr(start, ' ');
+		if (s == NULL)
+			return;
+		s = os_strchr(s + 1, ' ');
+		if (s == NULL)
+			return;
+		cli_txt_list_add(&bsses, s + 1);
+		return;
+	}
+
+	if (str_starts(start, WPA_EVENT_BSS_REMOVED)) {
+		s = os_strchr(start, ' ');
+		if (s == NULL)
+			return;
+		s = os_strchr(s + 1, ' ');
+		if (s == NULL)
+			return;
+		cli_txt_list_del_addr(&bsses, s + 1);
+		return;
+	}
+
+#ifdef CONFIG_P2P
+	if (str_starts(start, P2P_EVENT_DEVICE_FOUND)) {
+		s = os_strstr(start, " p2p_dev_addr=");
+		if (s == NULL)
+			return;
+		cli_txt_list_add_addr(&p2p_peers, s + 14);
+		return;
+	}
+
+	if (str_starts(start, P2P_EVENT_DEVICE_LOST)) {
+		s = os_strstr(start, " p2p_dev_addr=");
+		if (s == NULL)
+			return;
+		cli_txt_list_del_addr(&p2p_peers, s + 14);
+		return;
+	}
+
+	if (str_starts(start, P2P_EVENT_GROUP_STARTED)) {
+		s = os_strchr(start, ' ');
+		if (s == NULL)
+			return;
+		cli_txt_list_add_word(&p2p_groups, s + 1, ' ');
+		return;
+	}
+
+	if (str_starts(start, P2P_EVENT_GROUP_REMOVED)) {
+		s = os_strchr(start, ' ');
+		if (s == NULL)
+			return;
+		cli_txt_list_del_word(&p2p_groups, s + 1, ' ');
+		return;
+	}
+#endif /* CONFIG_P2P */
+}
+
+
+static int check_terminating(const char *msg)
+{
+	const char *pos = msg;
+
+	if (*pos == '<') {
+		/* skip priority */
+		pos = os_strchr(pos, '>');
+		if (pos)
+			pos++;
+		else
+			pos = msg;
+	}
+
+	if (str_match(pos, WPA_EVENT_TERMINATING) && ctrl_conn) {
+		edit_clear_line();
+		printf("\rConnection to wpa_supplicant lost - trying to "
+		       "reconnect\n");
+		edit_redraw();
+		wpa_cli_attached = 0;
+		wpa_cli_close_connection();
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor)
+{
+	if (ctrl_conn == NULL) {
+		wpa_cli_reconnect();
+		return;
+	}
+	while (wpa_ctrl_pending(ctrl) > 0) {
+		char buf[4096];
+		size_t len = sizeof(buf) - 1;
+		if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
+			buf[len] = '\0';
+			if (action_monitor)
+				wpa_cli_action_process(buf);
+			else {
+				cli_event(buf);
+				if (wpa_cli_show_event(buf)) {
+					edit_clear_line();
+					printf("\r%s\n", buf);
+					edit_redraw();
+				}
+
+				if (interactive && check_terminating(buf) > 0)
+					return;
+			}
+		} else {
+			printf("Could not read pending message.\n");
+			break;
+		}
+	}
+
+	if (wpa_ctrl_pending(ctrl) < 0) {
+		printf("Connection to wpa_supplicant lost - trying to "
+		       "reconnect\n");
+		wpa_cli_reconnect();
+	}
+}
+
+#define max_args 10
+
+static int tokenize_cmd(char *cmd, char *argv[])
+{
+	char *pos;
+	int argc = 0;
+
+	pos = cmd;
+	for (;;) {
+		while (*pos == ' ')
+			pos++;
+		if (*pos == '\0')
+			break;
+		argv[argc] = pos;
+		argc++;
+		if (argc == max_args)
+			break;
+		if (*pos == '"') {
+			char *pos2 = os_strrchr(pos, '"');
+			if (pos2)
+				pos = pos2 + 1;
+		}
+		while (*pos != '\0' && *pos != ' ')
+			pos++;
+		if (*pos == ' ')
+			*pos++ = '\0';
+	}
+
+	return argc;
+}
+
+
+static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx)
+{
+	if (ctrl_conn) {
+		int res;
+		char *prefix = ifname_prefix;
+
+		ifname_prefix = NULL;
+		res = _wpa_ctrl_command(ctrl_conn, "PING", 0);
+		ifname_prefix = prefix;
+		if (res) {
+			printf("Connection to wpa_supplicant lost - trying to "
+			       "reconnect\n");
+			wpa_cli_close_connection();
+		}
+	}
+	if (!ctrl_conn)
+		wpa_cli_reconnect();
+	eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL);
+}
+
+
+static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	wpa_cli_recv_pending(mon_conn, 0);
+}
+
+
+static void wpa_cli_edit_cmd_cb(void *ctx, char *cmd)
+{
+	char *argv[max_args];
+	int argc;
+	argc = tokenize_cmd(cmd, argv);
+	if (argc)
+		wpa_request(ctrl_conn, argc, argv);
+}
+
+
+static void wpa_cli_edit_eof_cb(void *ctx)
+{
+	eloop_terminate();
+}
+
+
+static int warning_displayed = 0;
+static char *hfile = NULL;
+static int edit_started = 0;
+
+static void start_edit(void)
+{
+	char *home;
+	char *ps = NULL;
+
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	ps = wpa_ctrl_get_remote_ifname(ctrl_conn);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
+#ifdef CONFIG_WPA_CLI_HISTORY_DIR
+	home = CONFIG_WPA_CLI_HISTORY_DIR;
+#else /* CONFIG_WPA_CLI_HISTORY_DIR */
+	home = getenv("HOME");
+#endif /* CONFIG_WPA_CLI_HISTORY_DIR */
+	if (home) {
+		const char *fname = ".wpa_cli_history";
+		int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
+		hfile = os_malloc(hfile_len);
+		if (hfile)
+			os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
+	}
+
+	if (edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb,
+		      wpa_cli_edit_completion_cb, NULL, hfile, ps) < 0) {
+		eloop_terminate();
+		return;
+	}
+
+	edit_started = 1;
+	eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL);
+}
+
+
+static void update_bssid_list(struct wpa_ctrl *ctrl)
+{
+	char buf[4096];
+	size_t len = sizeof(buf);
+	int ret;
+	char *cmd = "BSS RANGE=ALL MASK=0x2";
+	char *pos, *end;
+
+	if (ctrl == NULL)
+		return;
+	ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL);
+	if (ret < 0)
+		return;
+	buf[len] = '\0';
+
+	pos = buf;
+	while (pos) {
+		pos = os_strstr(pos, "bssid=");
+		if (pos == NULL)
+			break;
+		pos += 6;
+		end = os_strchr(pos, '\n');
+		if (end == NULL)
+			break;
+		*end = '\0';
+		cli_txt_list_add(&bsses, pos);
+		pos = end + 1;
+	}
+}
+
+
+static void update_ifnames(struct wpa_ctrl *ctrl)
+{
+	char buf[4096];
+	size_t len = sizeof(buf);
+	int ret;
+	char *cmd = "INTERFACES";
+	char *pos, *end;
+	char txt[200];
+
+	cli_txt_list_flush(&ifnames);
+
+	if (ctrl == NULL)
+		return;
+	ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL);
+	if (ret < 0)
+		return;
+	buf[len] = '\0';
+
+	pos = buf;
+	while (pos) {
+		end = os_strchr(pos, '\n');
+		if (end == NULL)
+			break;
+		*end = '\0';
+		ret = os_snprintf(txt, sizeof(txt), "ifname=%s", pos);
+		if (!os_snprintf_error(sizeof(txt), ret))
+			cli_txt_list_add(&ifnames, txt);
+		pos = end + 1;
+	}
+}
+
+
+static void update_networks(struct wpa_ctrl *ctrl)
+{
+	char buf[4096];
+	size_t len = sizeof(buf);
+	int ret;
+	char *cmd = "LIST_NETWORKS";
+	char *pos, *end;
+	int header = 1;
+
+	cli_txt_list_flush(&networks);
+
+	if (ctrl == NULL)
+		return;
+	ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL);
+	if (ret < 0)
+		return;
+	buf[len] = '\0';
+
+	pos = buf;
+	while (pos) {
+		end = os_strchr(pos, '\n');
+		if (end == NULL)
+			break;
+		*end = '\0';
+		if (!header)
+			cli_txt_list_add_word(&networks, pos, '\t');
+		header = 0;
+		pos = end + 1;
+	}
+}
+
+
+static void try_connection(void *eloop_ctx, void *timeout_ctx)
+{
+	if (ctrl_conn)
+		goto done;
+
+	if (ctrl_ifname == NULL)
+		ctrl_ifname = wpa_cli_get_default_ifname();
+
+	if (!wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
+		if (!warning_displayed) {
+			printf("Could not connect to wpa_supplicant: "
+			       "%s - re-trying\n",
+			       ctrl_ifname ? ctrl_ifname : "(nil)");
+			warning_displayed = 1;
+		}
+		eloop_register_timeout(1, 0, try_connection, NULL, NULL);
+		return;
+	}
+
+	update_bssid_list(ctrl_conn);
+	update_networks(ctrl_conn);
+
+	if (warning_displayed)
+		printf("Connection established.\n");
+
+done:
+	start_edit();
+}
+
+
+static void wpa_cli_interactive(void)
+{
+	printf("\nInteractive mode\n\n");
+
+	eloop_register_timeout(0, 0, try_connection, NULL, NULL);
+	eloop_run();
+	eloop_cancel_timeout(try_connection, NULL, NULL);
+
+	cli_txt_list_flush(&p2p_peers);
+	cli_txt_list_flush(&p2p_groups);
+	cli_txt_list_flush(&bsses);
+	cli_txt_list_flush(&ifnames);
+	cli_txt_list_flush(&networks);
+	if (edit_started)
+		edit_deinit(hfile, wpa_cli_edit_filter_history_cb);
+	os_free(hfile);
+	eloop_cancel_timeout(wpa_cli_ping, NULL, NULL);
+	wpa_cli_close_connection();
+}
+
+
+static void wpa_cli_action_ping(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_ctrl *ctrl = eloop_ctx;
+	char buf[256];
+	size_t len;
+
+	/* verify that connection is still working */
+	len = sizeof(buf) - 1;
+	if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
+			     wpa_cli_action_cb) < 0 ||
+	    len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
+		printf("wpa_supplicant did not reply to PING command - exiting\n");
+		eloop_terminate();
+		return;
+	}
+	eloop_register_timeout(ping_interval, 0, wpa_cli_action_ping,
+			       ctrl, NULL);
+}
+
+
+static void wpa_cli_action_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_ctrl *ctrl = eloop_ctx;
+
+	wpa_cli_recv_pending(ctrl, 1);
+}
+
+
+static void wpa_cli_action(struct wpa_ctrl *ctrl)
+{
+#ifdef CONFIG_ANSI_C_EXTRA
+	/* TODO: ANSI C version(?) */
+	printf("Action processing not supported in ANSI C build.\n");
+#else /* CONFIG_ANSI_C_EXTRA */
+	int fd;
+
+	fd = wpa_ctrl_get_fd(ctrl);
+	eloop_register_timeout(ping_interval, 0, wpa_cli_action_ping,
+			       ctrl, NULL);
+	eloop_register_read_sock(fd, wpa_cli_action_receive, ctrl, NULL);
+	eloop_run();
+	eloop_cancel_timeout(wpa_cli_action_ping, ctrl, NULL);
+	eloop_unregister_read_sock(fd);
+#endif /* CONFIG_ANSI_C_EXTRA */
+}
+
+
+static void wpa_cli_cleanup(void)
+{
+	wpa_cli_close_connection();
+	if (pid_file)
+		os_daemonize_terminate(pid_file);
+
+	os_program_deinit();
+}
+
+
+static void wpa_cli_terminate(int sig, void *ctx)
+{
+	eloop_terminate();
+}
+
+
+static char * wpa_cli_get_default_ifname(void)
+{
+	char *ifname = NULL;
+
+#ifdef ANDROID
+	char ifprop[PROPERTY_VALUE_MAX];
+	if (property_get("wifi.interface", ifprop, NULL) != 0) {
+		ifname = os_strdup(ifprop);
+		printf("Using interface '%s'\n", ifname ? ifname : "N/A");
+	}
+#else /* ANDROID */
+#ifdef CONFIG_CTRL_IFACE_UNIX
+	struct dirent *dent;
+	DIR *dir = opendir(ctrl_iface_dir);
+	if (!dir) {
+		return NULL;
+	}
+	while ((dent = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+		/*
+		 * Skip the file if it is not a socket. Also accept
+		 * DT_UNKNOWN (0) in case the C library or underlying
+		 * file system does not support d_type.
+		 */
+		if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
+			continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+		if (os_strcmp(dent->d_name, ".") == 0 ||
+		    os_strcmp(dent->d_name, "..") == 0)
+			continue;
+		printf("Selected interface '%s'\n", dent->d_name);
+		ifname = os_strdup(dent->d_name);
+		break;
+	}
+	closedir(dir);
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+	char buf[4096], *pos;
+	size_t len;
+	struct wpa_ctrl *ctrl;
+	int ret;
+
+	ctrl = wpa_ctrl_open(NULL);
+	if (ctrl == NULL)
+		return NULL;
+
+	len = sizeof(buf) - 1;
+	ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf, &len, NULL);
+	if (ret >= 0) {
+		buf[len] = '\0';
+		pos = os_strchr(buf, '\n');
+		if (pos)
+			*pos = '\0';
+		ifname = os_strdup(buf);
+	}
+	wpa_ctrl_close(ctrl);
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+#endif /* ANDROID */
+
+	return ifname;
+}
+
+
+int main(int argc, char *argv[])
+{
+	int c;
+	int daemonize = 0;
+	int ret = 0;
+	const char *global = NULL;
+
+	if (os_program_init())
+		return -1;
+
+	for (;;) {
+		c = getopt(argc, argv, "a:Bg:G:hi:p:P:s:v");
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'a':
+			action_file = optarg;
+			break;
+		case 'B':
+			daemonize = 1;
+			break;
+		case 'g':
+			global = optarg;
+			break;
+		case 'G':
+			ping_interval = atoi(optarg);
+			break;
+		case 'h':
+			usage();
+			return 0;
+		case 'v':
+			printf("%s\n", wpa_cli_version);
+			return 0;
+		case 'i':
+			os_free(ctrl_ifname);
+			ctrl_ifname = os_strdup(optarg);
+			break;
+		case 'p':
+			ctrl_iface_dir = optarg;
+			break;
+		case 'P':
+			pid_file = optarg;
+			break;
+		case 's':
+			client_socket_dir = optarg;
+			break;
+		default:
+			usage();
+			return -1;
+		}
+	}
+
+	interactive = (argc == optind) && (action_file == NULL);
+
+	if (interactive)
+		printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license);
+
+	if (eloop_init())
+		return -1;
+
+	if (global) {
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+		ctrl_conn = wpa_ctrl_open(NULL);
+#else /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+		ctrl_conn = wpa_ctrl_open(global);
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+		if (ctrl_conn == NULL) {
+			fprintf(stderr, "Failed to connect to wpa_supplicant "
+				"global interface: %s  error: %s\n",
+				global, strerror(errno));
+			return -1;
+		}
+
+		if (interactive) {
+			update_ifnames(ctrl_conn);
+			mon_conn = wpa_ctrl_open(global);
+			if (mon_conn) {
+				if (wpa_ctrl_attach(mon_conn) == 0) {
+					wpa_cli_attached = 1;
+					eloop_register_read_sock(
+						wpa_ctrl_get_fd(mon_conn),
+						wpa_cli_mon_receive,
+						NULL, NULL);
+				} else {
+					printf("Failed to open monitor "
+					       "connection through global "
+					       "control interface\n");
+				}
+			}
+		}
+	}
+
+	eloop_register_signal_terminate(wpa_cli_terminate, NULL);
+
+	if (ctrl_ifname == NULL)
+		ctrl_ifname = wpa_cli_get_default_ifname();
+
+	if (interactive) {
+		wpa_cli_interactive();
+	} else {
+		if (!global &&
+		    wpa_cli_open_connection(ctrl_ifname, 0) < 0) {
+			fprintf(stderr, "Failed to connect to non-global "
+				"ctrl_ifname: %s  error: %s\n",
+				ctrl_ifname ? ctrl_ifname : "(nil)",
+				strerror(errno));
+			return -1;
+		}
+
+		if (action_file) {
+			if (wpa_ctrl_attach(ctrl_conn) == 0) {
+				wpa_cli_attached = 1;
+			} else {
+				printf("Warning: Failed to attach to "
+				       "wpa_supplicant.\n");
+				return -1;
+			}
+		}
+
+		if (daemonize && os_daemonize(pid_file))
+			return -1;
+
+		if (action_file)
+			wpa_cli_action(ctrl_conn);
+		else
+			ret = wpa_request(ctrl_conn, argc - optind,
+					  &argv[optind]);
+	}
+
+	os_free(ctrl_ifname);
+	eloop_destroy();
+	wpa_cli_cleanup();
+
+	return ret;
+}
+
+#else /* CONFIG_CTRL_IFACE */
+int main(int argc, char *argv[])
+{
+	printf("CONFIG_CTRL_IFACE not defined - wpa_cli disabled\n");
+	return -1;
+}
+#endif /* CONFIG_CTRL_IFACE */
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/addinterface.cpp b/hostap/wpa_supplicant/wpa_gui-qt4/addinterface.cpp
new file mode 100644
index 0000000..7d92f63
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/addinterface.cpp
@@ -0,0 +1,239 @@
+/*
+ * wpa_gui - AddInterface class
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <cstdio>
+#include "common/wpa_ctrl.h"
+
+#include <QMessageBox>
+
+#include "wpagui.h"
+#include "addinterface.h"
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <windows.h>
+
+#ifndef WPA_KEY_ROOT
+#define WPA_KEY_ROOT HKEY_LOCAL_MACHINE
+#endif
+#ifndef WPA_KEY_PREFIX
+#define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant")
+#endif
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+AddInterface::AddInterface(WpaGui *_wpagui, QWidget *parent)
+	: QDialog(parent), wpagui(_wpagui)
+{
+	setWindowTitle(tr("Select network interface to add"));
+	resize(400, 200);
+	vboxLayout = new QVBoxLayout(this);
+
+	interfaceWidget = new QTreeWidget(this);
+	interfaceWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
+	interfaceWidget->setUniformRowHeights(true);
+	interfaceWidget->setSortingEnabled(true);
+	interfaceWidget->setColumnCount(3);
+	interfaceWidget->headerItem()->setText(0, tr("driver"));
+	interfaceWidget->headerItem()->setText(1, tr("interface"));
+	interfaceWidget->headerItem()->setText(2, tr("description"));
+	interfaceWidget->setItemsExpandable(false);
+	interfaceWidget->setRootIsDecorated(false);
+	vboxLayout->addWidget(interfaceWidget);
+
+	connect(interfaceWidget,
+		SIGNAL(itemActivated(QTreeWidgetItem *, int)), this,
+		SLOT(interfaceSelected(QTreeWidgetItem *)));
+
+	addInterfaces();
+}
+
+
+void AddInterface::addInterfaces()
+{
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+	struct wpa_ctrl *ctrl;
+	int ret;
+	char buf[2048];
+	size_t len;
+
+	ctrl = wpa_ctrl_open(NULL);
+	if (ctrl == NULL)
+		return;
+
+	len = sizeof(buf) - 1;
+	ret = wpa_ctrl_request(ctrl, "INTERFACE_LIST", 14, buf, &len, NULL);
+	if (ret < 0) {
+		wpa_ctrl_close(ctrl);
+		return;
+	}
+	buf[len] = '\0';
+
+	wpa_ctrl_close(ctrl);
+
+	QString ifaces(buf);
+	QStringList lines = ifaces.split(QRegExp("\\n"));
+	for (QStringList::Iterator it = lines.begin();
+	     it != lines.end(); it++) {
+		QStringList arg = (*it).split(QChar('\t'));
+		if (arg.size() < 3)
+			continue;
+		QTreeWidgetItem *item = new QTreeWidgetItem(interfaceWidget);
+		if (!item)
+			break;
+
+		item->setText(0, arg[0]);
+		item->setText(1, arg[1]);
+		item->setText(2, arg[2]);
+	}
+
+	interfaceWidget->resizeColumnToContents(0);
+	interfaceWidget->resizeColumnToContents(1);
+	interfaceWidget->resizeColumnToContents(2);
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+}
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+bool AddInterface::addRegistryInterface(const QString &ifname)
+{
+	HKEY hk, ihk;
+	LONG ret;
+	int id, tmp;
+	TCHAR name[10];
+	DWORD val, i;
+
+	ret = RegOpenKeyEx(WPA_KEY_ROOT, WPA_KEY_PREFIX TEXT("\\interfaces"),
+			   0, KEY_ENUMERATE_SUB_KEYS | KEY_CREATE_SUB_KEY,
+			   &hk);
+	if (ret != ERROR_SUCCESS)
+		return false;
+
+	id = -1;
+
+	for (i = 0; ; i++) {
+		TCHAR name[255];
+		DWORD namelen;
+
+		namelen = 255;
+		ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL,
+				   NULL);
+
+		if (ret == ERROR_NO_MORE_ITEMS)
+			break;
+
+		if (ret != ERROR_SUCCESS)
+			break;
+
+		if (namelen >= 255)
+			namelen = 255 - 1;
+		name[namelen] = '\0';
+
+#ifdef UNICODE
+		QString s((QChar *) name, namelen);
+#else /* UNICODE */
+		QString s(name);
+#endif /* UNICODE */
+		tmp = s.toInt();
+		if (tmp > id)
+			id = tmp;
+	}
+
+	id += 1;
+
+#ifdef UNICODE
+	wsprintf(name, L"%04d", id);
+#else /* UNICODE */
+	os_snprintf(name, sizeof(name), "%04d", id);
+#endif /* UNICODE */
+	ret = RegCreateKeyEx(hk, name, 0, NULL, 0, KEY_WRITE, NULL, &ihk,
+			     NULL);
+	RegCloseKey(hk);
+	if (ret != ERROR_SUCCESS)
+		return false;
+
+#ifdef UNICODE
+	RegSetValueEx(ihk, TEXT("adapter"), 0, REG_SZ,
+		      (LPBYTE) ifname.unicode(),
+		      (ifname.length() + 1) * sizeof(TCHAR));
+
+#else /* UNICODE */
+	RegSetValueEx(ihk, TEXT("adapter"), 0, REG_SZ,
+		      (LPBYTE) ifname.toLocal8Bit(), ifname.length() + 1);
+#endif /* UNICODE */
+	RegSetValueEx(ihk, TEXT("config"), 0, REG_SZ,
+		      (LPBYTE) TEXT("default"), 8 * sizeof(TCHAR));
+	RegSetValueEx(ihk, TEXT("ctrl_interface"), 0, REG_SZ,
+		      (LPBYTE) TEXT(""), 1 * sizeof(TCHAR));
+	val = 1;
+	RegSetValueEx(ihk, TEXT("skip_on_error"), 0, REG_DWORD, (LPBYTE) &val,
+		      sizeof(val));
+
+	RegCloseKey(ihk);
+	return true;
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+void AddInterface::interfaceSelected(QTreeWidgetItem *sel)
+{
+	if (!sel)
+		return;
+
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+	struct wpa_ctrl *ctrl;
+	int ret;
+	char buf[20], cmd[256];
+	size_t len;
+
+	/*
+	 * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB
+	 * <driver_param>TAB<bridge_name>
+	 */
+	snprintf(cmd, sizeof(cmd),
+		 "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s",
+		 sel->text(1).toLocal8Bit().constData(),
+		 "default",
+		 sel->text(0).toLocal8Bit().constData(),
+		 "yes", "", "");
+	cmd[sizeof(cmd) - 1] = '\0';
+
+	ctrl = wpa_ctrl_open(NULL);
+	if (ctrl == NULL)
+		return;
+
+	len = sizeof(buf) - 1;
+	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, NULL);
+	wpa_ctrl_close(ctrl);
+
+	if (ret < 0) {
+		QMessageBox::warning(this, "wpa_gui",
+				     tr("Add interface command could not be "
+					"completed."));
+		return;
+	}
+
+	buf[len] = '\0';
+	if (buf[0] != 'O' || buf[1] != 'K') {
+		QMessageBox::warning(this, "wpa_gui",
+				     tr("Failed to add the interface."));
+		return;
+	}
+
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+
+#ifdef CONFIG_NATIVE_WINDOWS
+	if (!addRegistryInterface(sel->text(1))) {
+		QMessageBox::information(this, "wpa_gui",
+					 tr("Failed to add the interface into "
+					    "registry."));
+	}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	wpagui->selectAdapter(sel->text(1));
+	close();
+}
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/addinterface.h b/hostap/wpa_supplicant/wpa_gui-qt4/addinterface.h
new file mode 100644
index 0000000..332fc71
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/addinterface.h
@@ -0,0 +1,39 @@
+/*
+ * wpa_gui - AddInterface class
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ADDINTERFACE_H
+#define ADDINTERFACE_H
+
+#include <QObject>
+
+#include <QDialog>
+#include <QTreeWidget>
+#include <QVBoxLayout>
+
+class WpaGui;
+
+class AddInterface : public QDialog
+{
+	Q_OBJECT
+
+public:
+	AddInterface(WpaGui *_wpagui, QWidget *parent = 0);
+
+public slots:
+	virtual void interfaceSelected(QTreeWidgetItem *sel);
+
+private:
+	void addInterfaces();
+	bool addRegistryInterface(const QString &ifname);
+
+	QVBoxLayout *vboxLayout;
+	QTreeWidget *interfaceWidget;
+	WpaGui *wpagui;
+};
+
+#endif /* ADDINTERFACE_H */
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp b/hostap/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp
new file mode 100644
index 0000000..09145cd
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp
@@ -0,0 +1,124 @@
+/*
+ * wpa_gui - EventHistory class
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <QHeaderView>
+#include <QScrollBar>
+
+#include "eventhistory.h"
+
+
+int EventListModel::rowCount(const QModelIndex &) const
+{
+	return msgList.count();
+}
+
+
+int EventListModel::columnCount(const QModelIndex &) const
+{
+	return 2;
+}
+
+
+QVariant EventListModel::data(const QModelIndex &index, int role) const
+{
+	if (!index.isValid())
+		return QVariant();
+
+        if (role == Qt::DisplayRole)
+		if (index.column() == 0) {
+			if (index.row() >= timeList.size())
+				return QVariant();
+			return timeList.at(index.row());
+		} else {
+			if (index.row() >= msgList.size())
+				return QVariant();
+			return msgList.at(index.row());
+		}
+        else
+		return QVariant();
+}
+
+
+QVariant EventListModel::headerData(int section, Qt::Orientation orientation,
+				    int role) const
+{
+	if (role != Qt::DisplayRole)
+		return QVariant();
+
+	if (orientation == Qt::Horizontal) {
+		switch (section) {
+		case 0:
+			return QString(tr("Timestamp"));
+		case 1:
+			return QString(tr("Message"));
+		default:
+			return QVariant();
+		}
+	} else
+		return QString("%1").arg(section);
+}
+
+
+void EventListModel::addEvent(QString time, QString msg)
+{
+	beginInsertRows(QModelIndex(), msgList.size(), msgList.size() + 1);
+	timeList << time;
+	msgList << msg;
+	endInsertRows();
+}
+
+
+EventHistory::EventHistory(QWidget *parent, const char *, bool, Qt::WindowFlags)
+	: QDialog(parent)
+{
+	setupUi(this);
+
+	connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
+
+	eventListView->setItemsExpandable(false);
+	eventListView->setRootIsDecorated(false);
+	elm = new EventListModel(parent);
+	eventListView->setModel(elm);
+}
+
+
+EventHistory::~EventHistory()
+{
+	destroy();
+	delete elm;
+}
+
+
+void EventHistory::languageChange()
+{
+	retranslateUi(this);
+}
+
+
+void EventHistory::addEvents(WpaMsgList msgs)
+{
+	WpaMsgList::iterator it;
+	for (it = msgs.begin(); it != msgs.end(); it++)
+		addEvent(*it);
+}
+
+
+void EventHistory::addEvent(WpaMsg msg)
+{
+	bool scroll = true;
+
+	if (eventListView->verticalScrollBar()->value() <
+	    eventListView->verticalScrollBar()->maximum())
+	    	scroll = false;
+
+	elm->addEvent(msg.getTimestamp().toString("yyyy-MM-dd hh:mm:ss.zzz"),
+		      msg.getMsg());
+
+	if (scroll)
+		eventListView->scrollToBottom();
+}
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/eventhistory.h b/hostap/wpa_supplicant/wpa_gui-qt4/eventhistory.h
new file mode 100644
index 0000000..afd7b63
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/eventhistory.h
@@ -0,0 +1,57 @@
+/*
+ * wpa_gui - EventHistory class
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EVENTHISTORY_H
+#define EVENTHISTORY_H
+
+#include <QObject>
+#include "ui_eventhistory.h"
+
+
+class EventListModel : public QAbstractTableModel
+{
+	Q_OBJECT
+
+public:
+	EventListModel(QObject *parent = 0)
+		: QAbstractTableModel(parent) {}
+
+        int rowCount(const QModelIndex &parent = QModelIndex()) const;
+        int columnCount(const QModelIndex &parent = QModelIndex()) const;
+        QVariant data(const QModelIndex &index, int role) const;
+        QVariant headerData(int section, Qt::Orientation orientation,
+                            int role = Qt::DisplayRole) const;
+	void addEvent(QString time, QString msg);
+
+private:
+	QStringList timeList;
+	QStringList msgList;
+};
+
+
+class EventHistory : public QDialog, public Ui::EventHistory
+{
+	Q_OBJECT
+
+public:
+	EventHistory(QWidget *parent = 0, const char *name = 0,
+		     bool modal = false, Qt::WindowFlags fl = 0);
+	~EventHistory();
+
+public slots:
+	virtual void addEvents(WpaMsgList msgs);
+	virtual void addEvent(WpaMsg msg);
+
+protected slots:
+	virtual void languageChange();
+
+private:
+	EventListModel *elm;
+};
+
+#endif /* EVENTHISTORY_H */
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/eventhistory.ui b/hostap/wpa_supplicant/wpa_gui-qt4/eventhistory.ui
new file mode 100644
index 0000000..afe9149
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/eventhistory.ui
@@ -0,0 +1,61 @@
+<ui version="4.0" >
+ <class>EventHistory</class>
+ <widget class="QDialog" name="EventHistory" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>533</width>
+    <height>285</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Event history</string>
+  </property>
+  <layout class="QGridLayout" >
+   <item row="0" column="0" colspan="2" >
+    <widget class="QTreeView" name="eventListView" >
+     <property name="sizePolicy" >
+      <sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="verticalScrollBarPolicy" >
+      <enum>Qt::ScrollBarAlwaysOn</enum>
+     </property>
+     <property name="selectionMode" >
+      <enum>QAbstractItemView::NoSelection</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0" >
+    <spacer>
+     <property name="orientation" >
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="sizeHint" >
+      <size>
+       <width>40</width>
+       <height>20</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="1" column="1" >
+    <widget class="QPushButton" name="closeButton" >
+     <property name="text" >
+      <string>Close</string>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11" />
+ <pixmapfunction></pixmapfunction>
+ <includes>
+  <include location="local" >wpamsg.h</include>
+ </includes>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/icons.qrc b/hostap/wpa_supplicant/wpa_gui-qt4/icons.qrc
new file mode 100644
index 0000000..dd72c7e
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/icons.qrc
@@ -0,0 +1,9 @@
+<RCC>
+ <qresource prefix="/icons" >
+  <file alias="wpa_gui.svg">icons/wpa_gui.svg</file>
+  <file alias="ap.svg">icons/ap.svg</file>
+  <file alias="laptop.svg">icons/laptop.svg</file>
+  <file alias="group.svg">icons/group.svg</file>
+  <file alias="invitation.svg">icons/invitation.svg</file>
+ </qresource>
+</RCC>
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/icons/Makefile b/hostap/wpa_supplicant/wpa_gui-qt4/icons/Makefile
new file mode 100644
index 0000000..709514c
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/icons/Makefile
@@ -0,0 +1,23 @@
+#!/usr/bin/make -f
+
+NAMES := wpa_gui ap laptop group invitation
+SIZES := 16x16 22x22 32x32 48x48 64x64 128x128
+ICONS := $(addsuffix .png, $(foreach name, $(NAMES), $(foreach size, $(SIZES), $(size)/$(name))))
+ICONS += $(addsuffix .xpm, $(NAMES))
+
+all: $(ICONS)
+
+%.png:
+	mkdir -p hicolor/$(word 1, $(subst /, ,$(@)))/apps/
+	inkscape $(subst .png,.svg, $(word 2, $(subst /, , $(@)))) --without-gui \
+		--export-width=$(word 1, $(subst x, , $(@)))  \
+	        --export-height=$(word 2, $(subst x, , $(subst /, , $(@)))) \
+		--export-png=hicolor/$(word 1, $(subst /, ,$(@)))/apps/$(word 2, $(subst /, , $@))
+
+%.xpm:
+	mkdir -p pixmaps/
+	convert hicolor/16x16/apps/$(@:.xpm=.png) pixmaps/$(@:.xpm=-16.xpm)
+	convert hicolor/32x32/apps/$(@:.xpm=.png) pixmaps/$@
+
+clean:
+	$(RM) -r pixmaps hicolor
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/icons/README b/hostap/wpa_supplicant/wpa_gui-qt4/icons/README
new file mode 100644
index 0000000..3953238
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/icons/README
@@ -0,0 +1,74 @@
+wpa_gui icon files
+
+To convert the svg icons to other formats, make sure inkscape and imagemagick
+are installed and use `make' to create various sized png and xpm icons.
+
+
+wpa_gui.svg
+-----------
+
+Copyright (c) 2008 Bernard Gray <bernard.gray@gmail.com>
+
+The wpa_gui icon is licensed under the GPL version 2. Alternatively, the icon
+may be distributed under the terms of BSD license.
+
+
+ap.svg
+------
+
+mystica_Wireless_Router.svg
+
+http://openclipart.org/media/files/mystica/8390
+Wireless Router
+by:     mystica
+last change:    April 20, 2008 10:32 pm (File added)
+date:   April 20, 2008 10:31 pm
+license: PD
+
+
+laptop.svg
+----------
+
+metalmarious_Laptop.svg
+
+http://openclipart.org/media/files/metalmarious/4056
+Laptop
+by:      metalmarious
+last change:    May 18, 2008 07:04 pm (File added)
+date:   August 27, 2007 04:44 am
+license: PD
+
+
+group.svg
+---------
+
+http://www.openclipart.org/detail/25428
+http://www.openclipart.org/people/Anonymous/Anonymous_Network.svg
+Uploader:
+    Anonymous
+Drawn by:
+    Andrew Fitzsimon / Anonymous
+Created:
+    2009-04-29 04:07:37
+Description:
+    A network icon by Andrew Fitzsimon. Etiquette Icon set.
+    From 0.18 OCAL database.
+
+Public Domain
+
+
+
+invitation.svg
+--------------
+
+http://www.openclipart.org/detail/974
+http://www.openclipart.org/people/jean_victor_balin/jean_victor_balin_unknown_green.svg
+Uploader:
+    jean_victor_balin
+Drawn by:
+    jean_victor_balin
+Created:
+    2006-10-27 02:12:13
+Description:
+
+Public Domain
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/icons/ap.svg b/hostap/wpa_supplicant/wpa_gui-qt4/icons/ap.svg
new file mode 100644
index 0000000..51cc8ce
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/icons/ap.svg
@@ -0,0 +1,832 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="546"
+   height="482.67157"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1+0.46pre1+devel"
+   sodipodi:docname="Wireless Router.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0"
+   inkscape:export-filename="C:\Documents and Settings\Dan\Skrivbord\Clipart egna (InkScape)\Original\Kanske Upload\Wireless Router.png"
+   inkscape:export-xdpi="310"
+   inkscape:export-ydpi="310">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="-50 : 600 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="700 : 600 : 1"
+       inkscape:persp3d-origin="300 : 400 : 1"
+       id="perspective148" />
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="-50 : 600 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="700 : 600 : 1"
+       inkscape:persp3d-origin="300 : 400 : 1"
+       id="perspective138" />
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="-50 : 600 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="700 : 600 : 1"
+       inkscape:persp3d-origin="300 : 400 : 1"
+       id="perspective10" />
+    <inkscape:perspective
+       id="perspective2395"
+       inkscape:persp3d-origin="300 : 400 : 1"
+       inkscape:vp_z="700 : 600 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="-50 : 600 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <filter
+       inkscape:collect="always"
+       id="filter3304">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.27999283"
+         id="feGaussianBlur3306" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3336">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.5315371"
+         id="feGaussianBlur3338" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3368">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.26473573"
+         id="feGaussianBlur3370" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3564"
+       x="-0.37202433"
+       width="1.7440487"
+       y="-0.43252525"
+       height="1.8650506">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.1017616"
+         id="feGaussianBlur3566" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3748"
+       x="-0.41952851"
+       width="1.839057"
+       y="-0.39121628"
+       height="1.7824326">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.1235829"
+         id="feGaussianBlur3750" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3862"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3864" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3866"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-0.20756502"
+       height="1.4151301">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3868" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3870"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3872" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3874"
+       x="-0.3380883"
+       width="1.6761765"
+       y="-0.21154897"
+       height="1.4230978">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3876" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3878"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3880" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3882"
+       x="-0.36018598"
+       width="1.720372"
+       y="-0.20953795"
+       height="1.4190758">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3884" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3886"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3888" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3890"
+       x="-0.35439494"
+       width="1.7087899"
+       y="-0.20953795"
+       height="1.4190758">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3892" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3894"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3896" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3898"
+       x="-0.38537359"
+       width="1.7707472"
+       y="-0.20562869"
+       height="1.4112574">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3900" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3902"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3904" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3906"
+       x="-0.38537359"
+       width="1.7707472"
+       y="-0.21359873"
+       height="1.4271975">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3908" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3910"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3912" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3914"
+       x="-0.36018598"
+       width="1.720372"
+       y="-0.20562869"
+       height="1.4112574">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3916" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3918"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3920" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3922"
+       x="-0.36616942"
+       width="1.7323389"
+       y="-0.20953795"
+       height="1.4190758">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3924" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3926"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3928" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3930"
+       x="-0.34878778"
+       width="1.6975756"
+       y="-0.20186263"
+       height="1.4037253">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3932" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3934"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3936" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3938"
+       x="-0.32802677"
+       width="1.6560535"
+       y="-0.21568884"
+       height="1.4313776">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3940" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3942"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3944" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3946"
+       x="-0.32321677"
+       width="1.6464336"
+       y="-0.21568884"
+       height="1.4313776">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3948" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3950"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3952" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3954"
+       x="-0.3185463"
+       width="1.6370926"
+       y="-0.21359873"
+       height="1.4271975">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3956" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3958"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3960" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3962"
+       x="-0.28553614"
+       width="1.5710723"
+       y="-0.21568884"
+       height="1.4313776">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3964" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3982"
+       x="-0.0048889387"
+       width="1.0097779"
+       y="-0.26385465"
+       height="1.5277092">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.0547249"
+         id="feGaussianBlur3984" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3996">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.8032234"
+         id="feGaussianBlur3998" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3517"
+       x="-0.25713229"
+       width="1.5142646"
+       y="-0.087099633"
+       height="1.1741993">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.33984317"
+         id="feGaussianBlur3519" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3329"
+       x="-0.18025071"
+       width="1.3605014"
+       y="-1.1780664"
+       height="3.3561328">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.49086099"
+         id="feGaussianBlur3331" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3333"
+       x="-0.15131117"
+       width="1.3026223"
+       y="-0.1853139"
+       height="1.3706278">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.49086099"
+         id="feGaussianBlur3335" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3337"
+       x="-0.14412392"
+       width="1.2882478"
+       y="-0.18013415"
+       height="1.3602683">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.49086099"
+         id="feGaussianBlur3339" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3341"
+       x="-1.1780664"
+       width="3.3561328"
+       y="-0.23067047"
+       height="1.4613409">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.49086099"
+         id="feGaussianBlur3343" />
+    </filter>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.98994949"
+     inkscape:cx="233.05018"
+     inkscape:cy="176.49031"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer2"
+     showgrid="false"
+     inkscape:window-width="1152"
+     inkscape:window-height="838"
+     inkscape:window-x="0"
+     inkscape:window-y="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     transform="translate(-45.788597,-496.6196)">
+    <path
+       style="fill:#606060;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 75.574315,977.86259 L 535.56828,979.20564 L 564.86002,979.29116 C 564.86002,979.29116 573.43146,977.86259 573.43146,973.57687 C 573.43146,969.29116 574.14573,959.29117 574.14573,959.29117 L 566.03832,959.7296 L 72.464255,956.66717 L 58.640665,955.63307 C 58.640665,955.63307 59.860025,973.57688 65.574315,975.71973 C 71.288595,977.86259 75.574315,977.14831 75.574315,977.86259 z"
+       id="path2402"
+       sodipodi:nodetypes="cccsccccsc" />
+    <path
+       style="fill:#dddddd;fill-opacity:1;fill-rule:evenodd;stroke:#b2b2b2;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 67.002885,957.14831 L 566.28859,960.00545 C 566.28859,960.00545 577.71717,959.29116 583.43146,955.71973 C 589.14574,952.14831 588.43146,942.86259 588.43146,942.86259 C 588.43146,942.86259 591.28859,907.14831 591.28859,902.14831 C 591.28859,897.14831 574.86002,839.29116 572.00288,827.86259 C 569.14574,816.43402 557.00288,757.1483 557.00288,757.1483 C 557.00288,757.1483 555.57431,749.29116 552.71717,748.57688 C 549.86002,747.86259 548.43146,748.57688 548.43146,748.57688 L 558.43146,797.86259 L 577.71717,880.00545 L 578.07431,881.34473 L 579.05004,882.28742 L 584.86002,897.14831 C 584.86002,897.14831 584.93925,904.12528 581.70701,906.43402 C 576.70701,910.00545 557.71717,910.71973 557.71717,910.71973 L 68.431455,907.86259 C 68.431455,907.86259 57.002885,906.43402 54.145745,903.57688 C 51.288595,900.71973 52.298745,895.51053 52.298745,895.51053 L 94.860025,745.00545 C 94.860025,745.00545 86.288605,747.86259 84.145745,752.1483 C 82.002885,756.43402 71.288595,811.43402 68.431455,820.00545 C 65.574315,828.57688 47.002885,893.57688 47.002885,893.57688 C 47.002885,893.57688 46.288597,900.00545 46.288597,903.57688 C 46.288597,907.14831 48.431455,946.43402 48.431455,946.43402 C 48.431455,946.43402 52.002885,953.57688 55.574315,955.00545 C 59.145745,956.43402 68.431455,957.14831 67.002885,957.14831 z"
+       id="path2404"
+       sodipodi:nodetypes="ccscsscsccccccsccsccsscscsc" />
+    <path
+       style="fill:#ececec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3336)"
+       d="M 562.71717,958.57688 C 562.71717,958.57688 572.00288,957.86259 572.00288,951.43402 C 572.00288,945.00545 575.57431,922.14831 572.00288,918.57688 C 568.43146,915.00545 564.86002,912.86259 564.86002,912.86259 C 564.86002,912.86259 586.28859,903.57688 585.57431,907.86259 C 584.86002,912.14831 581.28859,914.29116 581.28859,920.00545 C 581.28859,925.71973 580.57431,948.57688 580.57431,948.57688 C 580.57431,948.57688 578.43146,952.86259 581.28859,953.57688 C 584.14574,954.29116 582.71717,955.71973 582.71717,955.71973 L 576.28859,957.86259 L 570.57431,958.57688 L 562.71717,958.57688 z"
+       id="path2406" />
+    <path
+       style="fill:#ededed;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 75.574315,913.57688 L 560.57431,915.71973 L 557.71717,955.71973 L 77.002885,954.29116 L 75.574315,913.57688 z"
+       id="path2408" />
+    <path
+       style="fill:#020202;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 100.57432,925.00545 L 541.28859,927.86259 C 541.28859,927.86259 548.43146,932.14831 548.43146,936.43402 C 548.43146,940.71973 540.57431,945.71973 540.57431,945.71973 L 98.431455,942.86259 C 98.431455,942.86259 89.145745,938.57688 89.860025,932.86259 C 90.574315,927.14831 101.2886,924.29116 100.57432,925.00545 z"
+       id="path2410" />
+    <path
+       style="fill:#121212;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 71.497795,907.90591 L 557.85419,910.32545 L 573.61002,909.11259 C 573.61002,909.11259 581.28967,908.48718 583.43146,904.46973 C 584.17421,903.07651 585.22722,901.79095 585.2275,899.8245 C 585.22862,892.20247 577.89573,880.63045 577.89573,880.63045 C 577.89573,880.63045 578.20783,882.29016 577.30114,882.74322 C 575.50038,883.64304 573.664,884.53037 571.28859,885.00545 C 567.71717,885.71973 66.036055,879.91879 66.036055,879.91879 L 59.860025,880.00545 L 56.288605,877.68402 L 52.002885,896.43402 C 52.002885,896.43402 51.828665,903.29437 57.673845,905.51053 C 62.003235,907.15199 72.212085,908.6202 71.497795,907.90591 z"
+       id="path2412"
+       sodipodi:nodetypes="cccsscssccccsc" />
+    <path
+       style="fill:#2c2c2c;fill-opacity:1;fill-rule:evenodd;stroke:#252525;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 93.431455,743.57688 L 59.145745,868.57688 C 59.145745,868.57688 57.538605,871.96973 60.574315,873.57688 C 65.381975,876.12212 74.681455,875.18402 74.681455,875.18402 C 74.681455,875.18402 567.00288,879.29116 569.86002,878.57688 C 572.71717,877.86259 575.03859,876.96974 575.03859,876.96974 L 575.21717,868.75545 C 575.21717,868.75545 579.61685,882.27622 576.28859,883.75545 C 573.07431,885.18402 566.28859,885.00545 566.28859,885.00545 L 64.860025,880.71973 L 58.431455,879.29116 L 56.645745,876.79116 L 57.861935,870.85997 L 93.431455,743.57688 z"
+       id="path2414"
+       sodipodi:nodetypes="ccscsccscccccc" />
+    <path
+       style="fill:#d5d5d5;fill-opacity:1;fill-rule:evenodd;stroke:#d1d1d1;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3748)"
+       d="M 62.002885,879.82688 L 62.181455,874.29116 L 60.217165,873.93402 L 57.360025,874.82688 L 56.824315,876.25545 C 56.824315,876.25545 56.467165,877.86259 57.360025,878.21973 C 58.252885,878.57688 59.860025,879.11259 59.860025,879.11259 L 62.002885,879.82688 z"
+       id="path2416" />
+    <path
+       style="fill:#d5d5d5;fill-opacity:1;fill-rule:evenodd;stroke:#dadada;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3564)"
+       d="M 571.82431,883.04116 C 571.82431,882.32688 570.57431,879.82688 572.18146,878.93402 C 573.78859,878.04116 575.03859,878.57688 575.03859,878.57688 C 575.03859,878.57688 578.07431,881.25545 577.36002,881.43402 C 576.64574,881.61259 576.46717,882.50545 574.68146,883.21973 C 572.89574,883.93402 572.36002,883.21973 571.82431,883.04116 z"
+       id="path2418" />
+    <path
+       style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#2aea00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3341)"
+       d="M 194.42891,930.61513 L 194.68145,934.46973"
+       id="path2420"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#2aea00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3337)"
+       d="M 192.18145,932.32688 C 192.18145,932.32688 189.18906,937.68402 194.50288,937.86259 C 200.57352,938.06659 197.36003,932.50545 197.36003,932.50545"
+       id="path2422"
+       sodipodi:nodetypes="csc" />
+    <path
+       sodipodi:type="arc"
+       style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#444444;stroke-width:0.92299998;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path2424"
+       sodipodi:cx="289.82144"
+       sodipodi:cy="742.89789"
+       sodipodi:rx="4.2857141"
+       sodipodi:ry="4.2857141"
+       d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z"
+       transform="translate(-57.282828,191.92898)" />
+    <path
+       sodipodi:type="arc"
+       style="fill:#101010;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.92626119;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path2439"
+       sodipodi:cx="289.82144"
+       sodipodi:cy="742.89789"
+       sodipodi:rx="4.2857141"
+       sodipodi:ry="4.2857141"
+       d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z"
+       transform="matrix(0.4791666,0,0,0.4791666,93.755115,578.94427)" />
+    <path
+       sodipodi:type="arc"
+       style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#7b7b7b;stroke-width:1.70648665;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3211"
+       sodipodi:cx="289.82144"
+       sodipodi:cy="742.89789"
+       sodipodi:rx="4.2857141"
+       sodipodi:ry="4.2857141"
+       d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z"
+       transform="matrix(0.5109914,0,0,0.5109914,116.22806,556.99816)" />
+    <path
+       sodipodi:type="arc"
+       style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#7b7b7b;stroke-width:1.70648665;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3213"
+       sodipodi:cx="289.82144"
+       sodipodi:cy="742.89789"
+       sodipodi:rx="4.2857141"
+       sodipodi:ry="4.2857141"
+       d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z"
+       transform="matrix(0.5109914,0,0,0.5109914,122.47805,556.99816)" />
+    <path
+       sodipodi:type="arc"
+       style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#7b7b7b;stroke-width:1.70648665;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3215"
+       sodipodi:cx="289.82144"
+       sodipodi:cy="742.89789"
+       sodipodi:rx="4.2857141"
+       sodipodi:ry="4.2857141"
+       d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z"
+       transform="matrix(0.5109914,0,0,0.5109914,119.44234,552.71244)" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#606060;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 308.07431,934.29116 C 308.07431,934.29116 307.00288,935.71973 306.28859,936.61259 C 305.57431,937.50544 305.57431,939.11259 304.32431,938.57688 C 303.07431,938.04116 301.11002,936.07688 301.28859,934.64831 C 301.46717,933.21974 304.32431,931.07688 304.32431,931.07688 C 304.32431,931.07688 305.03859,932.1483 306.11002,932.86259 C 307.18145,933.57688 307.89574,934.11259 308.07431,934.29116 z"
+       id="path3217"
+       sodipodi:nodetypes="cssscsc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#28cc03;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3333)"
+       d="M 336.28859,931.43402 L 340.57431,931.43402 L 341.82431,932.68402 L 341.82431,935.00545 L 340.93145,936.79116 L 336.46717,936.79116 L 335.03859,935.71973 L 335.03859,932.50545 L 336.28859,931.43402 z"
+       id="path3221"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#28cc03;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3329)"
+       d="M 335.75288,938.57688 L 341.28859,938.57688"
+       id="path3223" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 369.86002,931.43402 L 374.14574,931.43402 L 375.39574,932.68402 L 375.39574,935.00545 L 374.50288,936.79116 L 370.0386,936.79116 L 368.61002,935.71973 L 368.61002,932.50545 L 369.86002,931.43402 z"
+       id="path3225"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 369.32431,938.57688 L 374.86002,938.57688"
+       id="path3227" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 407.00288,931.79116 L 411.2886,931.79116 L 412.5386,933.04116 L 412.5386,935.36259 L 411.64574,937.1483 L 407.18147,937.1483 L 405.75288,936.07687 L 405.75288,932.86259 L 407.00288,931.79116 z"
+       id="path3229"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 406.46717,938.93402 L 412.00288,938.93402"
+       id="path3231" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 444.14574,931.96973 L 448.43145,931.96973 L 449.68145,933.21973 L 449.68145,935.54116 L 448.78859,937.32687 L 444.32431,937.32687 L 442.89574,936.25544 L 442.89574,933.04116 L 444.14574,931.96973 z"
+       id="path3233"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 443.61002,939.11259 L 449.14574,939.11259"
+       id="path3235" />
+    <path
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#28cc03;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican;filter:url(#filter3517)"
+       d="M 329.05831,937.86898 L 329.10629,932.30075 L 328.72213,932.54098 C 328.65816,932.59689 328.60226,932.62485 328.55441,932.62484 C 328.52242,932.62485 328.47243,932.59085 328.40444,932.52285 C 328.33646,932.45487 328.30245,932.38486 328.30245,932.31283 C 328.30245,932.22495 328.34841,932.12497 328.44034,932.0129 C 328.53225,931.90085 328.62618,931.78879 328.72213,931.67672 L 329.68234,930.33273 L 330.47445,930.78903 L 330.46236,938.5527 C 330.46236,938.64889 330.38631,938.69699 330.23421,938.69699 C 330.1783,938.69699 330.13437,938.69296 330.10238,938.6849 C 329.86239,938.62094 329.6224,938.56088 329.38241,938.50472 C 329.16634,938.40072 329.05831,938.18881 329.05831,937.86898 L 329.05831,937.86898 z"
+       id="text3237" />
+    <path
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#939393;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican"
+       d="M 364.94248,932.84854 C 364.94246,933.5126 364.7065,934.32059 364.23458,935.27249 C 363.84249,936.05667 363.33443,936.86868 362.71041,937.70852 L 362.50643,937.97256 L 363.8984,937.94876 C 364.14644,937.94876 364.37044,937.97476 364.5704,938.02676 C 364.77034,938.07876 364.91036,938.16067 364.99044,938.27249 C 365.16646,938.51248 365.25447,938.70449 365.25448,938.84854 C 365.25447,938.98452 365.19448,939.06851 365.07449,939.10049 C 364.95449,939.13247 364.85042,939.14846 364.76229,939.14846 L 360.97054,939.34072 L 360.5743,937.93667 C 360.83846,937.62466 361.10653,937.31265 361.3785,937.00064 C 361.96248,936.20865 362.44246,935.46463 362.81844,934.76858 C 363.3304,933.83255 363.58638,933.12857 363.58639,932.65664 C 363.58638,932.55264 363.5784,932.46866 363.5624,932.40469 C 363.54641,932.34073 363.51046,932.30875 363.45455,932.30874 C 363.30245,932.30875 362.99044,932.50064 362.51852,932.88442 C 362.21456,933.13248 361.81051,933.50052 361.30636,933.98855 C 360.90646,934.38065 360.70248,934.58462 360.69442,934.60049 L 360.02242,933.65273 C 360.02242,933.58853 360.19845,933.3605 360.5505,932.96865 C 360.95846,932.51261 361.36642,932.1326 361.77438,931.82864 C 362.32638,931.42069 362.79439,931.21671 363.17843,931.2167 C 363.40255,931.21671 363.59053,931.27664 363.74239,931.39651 C 364.29439,931.80448 364.60237,932.04849 364.66634,932.12856 C 364.85042,932.35269 364.94246,932.59268 364.94248,932.84854 L 364.94248,932.84854 z"
+       id="text3241" />
+    <path
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#939393;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican"
+       d="M 398.55154,939.8793 L 397.90371,938.83523 L 398.62368,938.71511 C 399.16762,938.61917 399.70754,938.33914 400.24343,937.87502 C 400.73952,937.44314 400.98757,937.09915 400.98757,936.84304 C 400.98757,936.69119 400.84755,936.55526 400.56753,936.43527 C 400.2875,936.31527 399.97951,936.25528 399.64358,936.25527 C 399.42751,936.25528 399.23348,936.28128 399.06149,936.33328 C 398.88949,936.38528 398.74355,936.47525 398.62368,936.60317 L 398.17947,936.27908 L 398.17947,935.23501 L 398.50356,935.079 C 399.04751,934.83902 399.59548,934.46304 400.14748,933.95107 C 400.69948,933.43912 400.97548,933.05521 400.97549,932.79934 C 400.97548,932.7432 400.95547,932.6951 400.91544,932.65505 C 400.81948,932.55911 400.65957,932.51114 400.43569,932.51113 C 400.10756,932.51114 399.69155,932.61112 399.18765,932.81106 C 398.7797,932.97122 398.5436,933.08328 398.47939,933.14724 L 397.97549,932.00723 C 398.06363,931.91129 398.32363,931.77127 398.75552,931.58718 C 399.29165,931.36307 399.75564,931.25101 400.14748,931.251 C 400.25955,931.25101 400.3716,931.26309 400.48367,931.28726 C 400.85965,931.37516 401.16762,931.52713 401.40762,931.74319 C 401.59169,931.90311 401.78371,932.15506 401.98367,932.49905 C 402.03151,932.57913 402.06149,932.6572 402.07357,932.73324 C 402.08565,932.8093 402.09169,932.88333 402.0917,932.95535 C 402.09169,933.25125 401.98366,933.5712 401.76761,933.91519 C 401.55154,934.25919 401.28347,934.56717 400.9634,934.83914 L 400.50747,935.21121 C 400.8915,935.21121 401.28951,935.43722 401.7015,935.88925 C 402.11348,936.34128 402.31948,936.8273 402.31948,937.34731 C 402.31948,937.65127 402.24947,937.91726 402.10947,938.14529 C 401.96944,938.37332 401.76748,938.6033 401.50356,938.83523 C 401.16762,939.13113 400.79567,939.37112 400.38772,939.5552 C 399.88357,939.78713 399.3915,939.9031 398.91152,939.9031 C 398.84756,939.9031 398.78158,939.89913 398.71359,939.8912 C 398.64559,939.88326 398.59157,939.8793 398.55154,939.8793 L 398.55154,939.8793 z"
+       id="text3245" />
+    <path
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#939393;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican"
+       d="M 433.97515,936.58822 L 436.87884,931.5964 L 438.17486,932.11239 L 438.01885,936.03634 L 438.42718,936.13229 C 438.62712,936.1882 438.73503,936.22018 438.75091,936.22824 C 438.799,936.26022 438.83904,936.31222 438.87103,936.38424 C 438.93499,936.52829 438.98094,936.63431 439.0089,936.7023 C 439.03686,936.77029 439.05083,936.84823 439.05083,936.93612 C 439.05083,937.00033 439.03887,937.06442 439.01495,937.12838 L 437.92291,937.20016 L 437.93499,939.73214 C 437.93499,939.94015 437.86296,940.03219 437.71893,940.00826 C 437.31902,939.95236 437.08709,939.91232 437.02312,939.88815 C 436.78313,939.78414 436.66314,939.57223 436.66314,939.25241 L 436.66314,937.28439 L 434.71893,937.28439 L 433.97515,936.58822 z M 435.48687,936.08431 L 436.80706,936.10812 L 437.01104,933.13229 L 435.48687,936.08431 z"
+       id="text3249" />
+    <path
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#979797;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican"
+       d="M 109.34409,932.3674 C 109.08016,933.58322 108.9001,934.43124 108.80392,934.91146 C 108.65206,935.67123 108.49617,936.52719 108.33628,937.47933 L 107.04026,936.98715 L 106.27194,932.81124 L 105.39597,937.73129 L 104.01609,937.28744 L 103.3082,929.55929 C 103.3082,929.47141 103.38021,929.42746 103.52426,929.42745 C 103.63608,929.43527 103.71604,929.43918 103.76413,929.43917 C 103.91622,929.43918 104.04623,929.46518 104.15414,929.51718 C 104.26206,929.56919 104.32004,929.65927 104.3281,929.78744 L 104.77194,935.69113 L 105.64792,929.97933 L 107.04026,930.03939 L 107.65219,935.5234 C 107.76425,934.39523 107.95627,933.16318 108.22824,931.82723 C 108.35617,931.17124 108.46811,930.65927 108.56405,930.29135 C 108.63607,930.00327 108.78011,929.49521 108.99618,928.76718 C 109.02011,928.7352 109.04806,928.69919 109.08005,928.65914 C 109.15206,928.58713 109.23213,928.55918 109.32028,928.57528 C 109.55221,928.61533 109.73819,928.69132 109.87821,928.80325 C 110.0182,928.91519 110.08821,929.06723 110.08822,929.25936 C 109.91219,929.97129 109.78413,930.48728 109.70407,930.80734 C 109.57613,931.32736 109.45614,931.84738 109.34409,932.3674 L 109.34409,932.3674 z M 111.18136,931.43136 L 111.18136,930.65133 C 111.18136,930.49924 111.22733,930.36521 111.31924,930.24923 C 111.41116,930.13327 111.52914,930.07529 111.67319,930.07528 C 111.80917,930.07529 111.9332,930.12729 112.04525,930.23129 C 112.15732,930.3353 112.21335,930.45932 112.21335,930.60336 L 112.21335,931.68331 C 112.21335,931.79538 112.16135,931.87741 112.05734,931.92941 C 111.95334,931.98141 111.82932,932.00742 111.68527,932.00741 C 111.54122,932.00742 111.42123,931.95139 111.32529,931.83932 C 111.22933,931.72726 111.18136,931.59128 111.18136,931.43136 L 111.18136,931.43136 z M 111.13339,937.22738 L 111.13339,932.83541 C 111.13339,932.64328 111.24533,932.54721 111.46921,932.54721 C 111.74117,932.54721 111.93515,932.58121 112.05112,932.64919 C 112.16708,932.71719 112.22507,932.81125 112.22507,932.93136 L 112.1174,937.67123 C 112.1174,937.76718 112.04538,937.81515 111.90134,937.81515 C 111.82126,937.81515 111.75118,937.80715 111.69112,937.79116 C 111.63107,937.77517 111.58505,937.76327 111.55307,937.75546 C 111.27329,937.66732 111.13339,937.4913 111.13339,937.22738 L 111.13339,937.22738 z M 113.68845,932.8713 L 113.79648,932.73947 L 114.0726,932.73947 C 114.13657,932.73947 114.21053,932.77743 114.29452,932.85336 C 114.37851,932.92929 114.44455,932.97531 114.49265,932.99142 C 114.60446,933.02341 114.69638,932.93741 114.76839,932.73342 C 114.84042,932.52945 114.92641,932.41342 115.0264,932.38534 C 115.12636,932.35727 115.19638,932.34323 115.23641,932.34323 C 115.38852,932.34323 115.52255,932.39725 115.63852,932.50527 C 115.75449,932.61331 115.81247,932.74326 115.81247,932.89511 L 115.80039,933.36312 C 115.80038,933.5953 115.77237,933.80539 115.71634,933.99337 C 115.6603,934.18136 115.57236,934.27536 115.45248,934.27535 C 115.26839,934.27536 115.14437,934.0953 115.08042,933.73519 C 115.04843,933.55917 114.9924,933.47116 114.91233,933.47116 C 114.84054,933.47116 114.78261,933.53116 114.73855,933.65115 C 114.69449,933.77115 114.67246,934.0112 114.67246,934.3713 C 114.67246,934.48337 114.67246,934.58139 114.67246,934.66537 C 114.67246,934.74936 114.67246,934.81527 114.67246,934.86312 L 114.67246,937.79135 C 114.58456,937.83138 114.48459,937.8514 114.37252,937.8514 C 114.30051,937.8514 114.2125,937.83938 114.10849,937.81533 C 114.00449,937.79128 113.90451,937.73324 113.80857,937.6412 C 113.71261,937.54916 113.66464,937.43124 113.66464,937.28744 L 113.68845,932.8713 z M 119.45774,933.78317 L 119.52951,935.22311 L 117.82552,935.49923 C 117.68173,935.53122 117.58579,935.56723 117.53769,935.60726 C 117.43368,935.68734 117.38168,935.7994 117.38169,935.94345 L 117.40548,936.49533 C 117.40548,936.6152 117.44351,936.72518 117.51955,936.82528 C 117.59561,936.92538 117.68563,936.9714 117.78964,936.96334 C 118.01376,936.93136 118.29379,936.75534 118.62973,936.43527 C 118.96567,936.1152 119.11764,935.91916 119.08567,935.84713 C 119.11764,935.83126 119.16965,935.82333 119.24167,935.82333 C 119.37765,935.82333 119.47763,935.86734 119.5416,935.95535 C 119.60556,936.04336 119.63754,936.13534 119.63755,936.23129 C 119.63754,936.27133 119.62557,936.32333 119.60165,936.38729 C 119.55356,936.57138 119.48556,936.73141 119.39767,936.8674 C 119.35763,936.93136 119.24556,937.07138 119.0615,937.28744 C 118.90964,937.46322 118.75571,937.59115 118.59969,937.67123 C 118.44369,937.75131 118.25765,937.79135 118.0416,937.79135 C 117.89755,937.79135 117.77956,937.77731 117.68766,937.74923 C 117.59573,937.72116 117.50168,937.6672 117.40548,937.58737 L 116.78182,937.0234 C 116.75765,937.00729 116.73361,936.97122 116.70969,936.91519 C 116.68576,936.85916 116.66574,936.81515 116.64963,936.78317 C 116.59372,936.63937 116.56576,936.48739 116.56576,936.32723 L 116.56576,935.3912 C 116.56576,934.78329 116.69974,934.1553 116.96768,933.50723 C 117.23562,932.85916 117.52158,932.53513 117.82552,932.53512 C 117.87363,932.53513 117.90964,932.53915 117.93357,932.54721 C 118.08566,932.59531 118.27969,932.63132 118.51566,932.65524 C 118.75162,932.67917 118.93563,932.76315 119.06771,932.90719 C 119.19979,933.05124 119.29782,933.18924 119.36178,933.32119 C 119.42575,933.45315 119.45773,933.60715 119.45774,933.78317 L 119.45774,933.78317 z M 118.58176,933.77145 C 118.58176,933.63547 118.55576,933.51547 118.50376,933.41146 C 118.45175,933.30746 118.36972,933.23935 118.25765,933.20712 C 118.13754,933.1832 118.0256,933.31125 117.92185,933.59127 C 117.84982,933.79123 117.80173,933.93124 117.77756,934.01132 C 117.71358,934.17929 117.66562,934.32327 117.63363,934.44326 C 117.60165,934.56326 117.58566,934.69125 117.58567,934.82723 L 118.58176,934.59945 L 118.58176,933.77145 z M 120.69589,937.04721 L 120.80356,929.73947 C 120.80356,929.64328 120.85361,929.5712 120.95371,929.52322 C 121.0538,929.47525 121.16379,929.46323 121.28366,929.48715 C 121.45163,929.51914 121.56564,929.57517 121.6257,929.65524 C 121.68576,929.73532 121.71579,929.85129 121.71579,930.00314 L 121.53562,937.61117 C 121.53561,937.6993 121.45969,937.74337 121.30783,937.74337 C 121.25168,937.74337 121.19162,937.73532 121.12766,937.7192 C 120.91965,937.62326 120.79568,937.5433 120.75576,937.47933 C 120.71585,937.41537 120.69589,937.27133 120.69589,937.04721 L 120.69589,937.04721 z M 125.71554,933.78317 L 125.78733,935.22311 L 124.08335,935.49923 C 123.93955,935.53122 123.84359,935.56723 123.7955,935.60726 C 123.6915,935.68734 123.6395,935.7994 123.6395,935.94345 L 123.6633,936.49533 C 123.6633,936.6152 123.70132,936.72518 123.77738,936.82528 C 123.85343,936.92538 123.94344,936.9714 124.04746,936.96334 C 124.27158,936.93136 124.5516,936.75534 124.88755,936.43527 C 125.22348,936.1152 125.37546,935.91916 125.34348,935.84713 C 125.37546,935.83126 125.42746,935.82333 125.49948,935.82333 C 125.63547,935.82333 125.73543,935.86734 125.79941,935.95535 C 125.86337,936.04336 125.89534,936.13534 125.89535,936.23129 C 125.89534,936.27133 125.88339,936.32333 125.85947,936.38729 C 125.81136,936.57138 125.74338,936.73141 125.65548,936.8674 C 125.61544,936.93136 125.50339,937.07138 125.31931,937.28744 C 125.16745,937.46322 125.01352,937.59115 124.85752,937.67123 C 124.7015,937.75131 124.51547,937.79135 124.29941,937.79135 C 124.15537,937.79135 124.03737,937.77731 123.94547,937.74923 C 123.85354,937.72116 123.75949,937.6672 123.6633,937.58737 L 123.03964,937.0234 C 123.01547,937.00729 122.99142,936.97122 122.9675,936.91519 C 122.94357,936.85916 122.92355,936.81515 122.90743,936.78317 C 122.85153,936.63937 122.82358,936.48739 122.82358,936.32723 L 122.82358,935.3912 C 122.82358,934.78329 122.95755,934.1553 123.22549,933.50723 C 123.49344,932.85916 123.77938,932.53513 124.08335,932.53512 C 124.13144,932.53513 124.16745,932.53915 124.19137,932.54721 C 124.34348,932.59531 124.53751,932.63132 124.77347,932.65524 C 125.00942,932.67917 125.19344,932.76315 125.32552,932.90719 C 125.45761,933.05124 125.55562,933.18924 125.6196,933.32119 C 125.68356,933.45315 125.71554,933.60715 125.71554,933.78317 L 125.71554,933.78317 z M 124.83956,933.77145 C 124.83956,933.63547 124.81357,933.51547 124.76157,933.41146 C 124.70955,933.30746 124.62752,933.23935 124.51547,933.20712 C 124.39535,933.1832 124.28341,933.31125 124.17965,933.59127 C 124.10764,933.79123 124.05954,933.93124 124.03537,934.01132 C 123.97141,934.17929 123.92343,934.32327 123.89145,934.44326 C 123.85947,934.56326 123.84348,934.69125 123.84348,934.82723 L 124.83956,934.59945 L 124.83956,933.77145 z M 126.83358,937.26327 C 126.73764,937.19931 126.68966,937.09933 126.68966,936.96334 C 126.68966,936.88327 126.71366,936.80722 126.76163,936.73519 C 126.8096,936.66317 126.86758,936.60916 126.93558,936.57315 C 127.00357,936.53714 127.06955,936.53524 127.13352,936.56747 C 127.34152,936.70346 127.49752,936.80343 127.60153,936.8674 C 127.80148,936.99533 127.94942,937.05929 128.04538,937.05929 C 128.14937,937.05929 128.2134,937.02127 128.23746,936.94522 C 128.2615,936.86917 128.27353,936.81918 128.27353,936.79525 C 128.27353,936.75521 128.27353,936.72726 128.27353,936.71139 C 128.27353,936.54342 128.18149,936.37936 127.99741,936.2192 C 127.82138,936.08322 127.64737,935.94522 127.47537,935.8052 C 127.30338,935.66519 127.17141,935.52719 127.0795,935.3912 C 126.98757,935.25522 126.92362,935.09921 126.88761,934.92318 C 126.85159,934.74716 126.83358,934.59518 126.83358,934.46725 C 126.83358,934.05929 126.94956,933.6893 127.18149,933.35726 C 127.41342,933.02524 127.70944,932.85922 128.06955,932.85922 C 128.17355,932.85922 128.23752,932.85922 128.26145,932.85922 C 128.3176,932.85922 128.35764,932.86728 128.38155,932.88339 L 128.95761,933.23129 C 129.08553,933.31137 129.17355,933.42343 129.22164,933.56747 C 129.24556,933.63144 129.2695,933.78732 129.29343,934.03512 C 129.29343,934.21921 129.28341,934.37527 129.2634,934.50332 C 129.24338,934.63138 129.20139,934.76742 129.13742,934.91146 L 128.5134,934.91146 C 128.48946,934.84726 128.4515,934.79318 128.39951,934.74923 C 128.3475,934.70529 128.32149,934.48337 128.32151,934.08346 C 128.32149,933.9714 128.32149,933.85531 128.32151,933.73519 C 128.29757,933.67123 128.26157,933.63925 128.21347,933.63925 C 128.13363,933.63925 128.03366,933.73526 127.91354,933.92727 C 127.79343,934.11929 127.72141,934.28732 127.69747,934.43136 C 127.67355,934.6152 127.71761,934.79123 127.82968,934.95944 C 127.90951,935.07931 128.05344,935.22323 128.26145,935.3912 C 128.46945,935.55917 128.68552,935.72714 128.90964,935.89511 C 129.16549,936.12728 129.29343,936.3674 129.29343,936.61544 C 129.29343,936.92745 129.15347,937.21145 128.87357,937.46743 C 128.59366,937.72341 128.32968,937.8514 128.08164,937.8514 C 127.89755,937.8514 127.72549,937.81539 127.56546,937.74337 C 127.40543,937.67135 127.16146,937.51132 126.83358,937.26327 L 126.83358,937.26327 z M 130.2789,937.26327 C 130.18295,937.19931 130.13497,937.09933 130.13497,936.96334 C 130.13497,936.88327 130.15896,936.80722 130.20694,936.73519 C 130.25492,936.66317 130.31289,936.60916 130.38089,936.57315 C 130.44888,936.53714 130.51486,936.53524 130.57882,936.56747 C 130.78683,936.70346 130.94284,936.80343 131.04685,936.8674 C 131.24679,936.99533 131.39475,937.05929 131.49069,937.05929 C 131.5947,937.05929 131.65872,937.02127 131.68277,936.94522 C 131.70682,936.86917 131.71884,936.81918 131.71884,936.79525 C 131.71884,936.75521 131.71884,936.72726 131.71884,936.71139 C 131.71884,936.54342 131.6268,936.37936 131.44271,936.2192 C 131.26668,936.08322 131.09268,935.94522 130.92068,935.8052 C 130.74868,935.66519 130.61673,935.52719 130.52481,935.3912 C 130.43288,935.25522 130.36893,935.09921 130.33292,934.92318 C 130.29691,934.74716 130.2789,934.59518 130.2789,934.46725 C 130.2789,934.05929 130.39487,933.6893 130.6268,933.35726 C 130.85873,933.02524 131.15475,932.85922 131.51486,932.85922 C 131.61886,932.85922 131.68283,932.85922 131.70676,932.85922 C 131.76291,932.85922 131.80294,932.86728 131.82688,932.88339 L 132.40293,933.23129 C 132.53084,933.31137 132.61886,933.42343 132.66696,933.56747 C 132.69089,933.63144 132.71481,933.78732 132.73873,934.03512 C 132.73873,934.21921 132.72872,934.37527 132.70871,934.50332 C 132.68868,934.63138 132.64669,934.76742 132.58274,934.91146 L 131.95871,934.91146 C 131.93478,934.84726 131.89682,934.79318 131.84482,934.74923 C 131.79282,934.70529 131.76682,934.48337 131.76682,934.08346 C 131.76682,933.9714 131.76682,933.85531 131.76682,933.73519 C 131.74289,933.67123 131.70687,933.63925 131.65878,933.63925 C 131.57895,933.63925 131.47897,933.73526 131.35886,933.92727 C 131.23873,934.11929 131.16672,934.28732 131.14279,934.43136 C 131.11886,934.6152 131.16293,934.79123 131.275,934.95944 C 131.35483,935.07931 131.49875,935.22323 131.70676,935.3912 C 131.91476,935.55917 132.13083,935.72714 132.35495,935.89511 C 132.6108,936.12728 132.73873,936.3674 132.73873,936.61544 C 132.73873,936.92745 132.59878,937.21145 132.31887,937.46743 C 132.03897,937.72341 131.77499,937.8514 131.52695,937.8514 C 131.34287,937.8514 131.17081,937.81539 131.01077,937.74337 C 130.85074,937.67135 130.60677,937.51132 130.2789,937.26327 L 130.2789,937.26327 z M 145.64951,931.63534 C 145.6014,932.07529 145.41745,932.48727 145.09762,932.8713 C 144.84152,933.17526 144.45345,933.50326 143.93344,933.85531 L 143.41745,934.20321 L 145.36167,937.13143 C 145.37752,937.21127 145.38546,937.2712 145.38547,937.31124 C 145.38546,937.51119 145.29745,937.6672 145.12143,937.77926 C 145.03354,937.82736 144.94955,937.8514 144.86947,937.8514 C 144.74959,937.8514 144.63961,937.82137 144.53951,937.76132 C 144.43941,937.70126 144.34945,937.6152 144.26962,937.50314 L 141.84567,934.22738 L 141.78562,937.58737 C 141.78561,937.70724 141.68955,937.76718 141.49741,937.76718 C 141.38558,937.76718 141.29757,937.75118 141.23336,937.7192 C 141.0815,937.67135 140.94956,937.62539 140.8375,937.58132 C 140.72543,937.53726 140.6694,937.41525 140.66941,937.2153 L 140.69358,930.0632 C 140.69357,929.91135 140.71554,929.7814 140.75949,929.67336 C 140.80344,929.56534 140.8895,929.48337 141.01766,929.42745 C 141.08164,929.40329 141.14963,929.39121 141.22164,929.3912 C 141.39767,929.39121 141.53769,929.45529 141.64169,929.58346 C 141.84969,929.47946 142.03769,929.41342 142.20566,929.38534 C 142.37363,929.35727 142.60556,929.34323 142.90145,929.34323 L 143.52548,929.34323 C 144.03744,929.34323 144.52144,929.56723 144.97751,930.01522 C 145.43356,930.46323 145.66159,930.95126 145.66159,931.47933 C 145.65353,931.55136 145.6495,931.60336 145.64951,931.63534 L 145.64951,931.63534 z M 141.86947,933.48324 C 142.29354,933.37924 142.61758,933.24124 142.84159,933.06924 C 143.06557,932.89725 143.24959,932.75522 143.39364,932.64315 C 143.70566,932.39535 143.95163,932.11942 144.13155,931.81533 C 144.31149,931.51126 144.40145,931.19919 144.40145,930.87911 C 144.40145,930.68723 144.30544,930.54129 144.11344,930.44131 C 143.92141,930.34134 143.68538,930.29135 143.40537,930.29135 C 143.07747,930.29135 142.76552,930.34336 142.46952,930.44735 C 142.17348,930.55136 141.99752,930.67929 141.94162,930.83114 L 141.86947,933.48324 z M 147.50216,937.62325 C 147.1423,937.47921 146.87833,937.25119 146.71024,936.93917 C 146.54215,936.62716 146.4581,936.25521 146.4581,935.82333 C 146.4581,935.71933 146.4581,935.63131 146.4581,935.55929 C 146.4581,935.48727 146.47409,935.36325 146.50608,935.18722 C 146.52219,934.89132 146.53817,934.65133 146.55405,934.46725 C 146.61826,933.83517 146.87436,933.17917 147.32236,932.49923 C 147.3702,932.41916 147.4421,932.37912 147.53806,932.37911 C 147.59421,932.37912 147.7103,932.41513 147.88631,932.48715 C 148.06234,932.55917 148.19833,932.59518 148.29427,932.59518 C 148.75034,932.59518 149.09432,932.85922 149.32627,933.38729 C 149.52621,933.84335 149.62618,934.42331 149.62618,935.12716 C 149.62618,935.84713 149.53414,936.43112 149.35007,936.87911 C 149.12618,937.4233 148.7822,937.75533 148.31808,937.87521 C 148.23825,937.89132 148.18625,937.89938 148.16208,937.89938 C 148.09811,937.89938 148.03817,937.88339 147.98227,937.8514 C 147.92636,937.81942 147.87033,937.78744 147.81418,937.75546 L 147.50216,937.62325 z M 148.03025,933.43527 C 147.91012,933.69919 147.79208,933.96518 147.67612,934.23324 C 147.56015,934.50131 147.50216,934.83127 147.50216,935.22311 C 147.50216,935.34323 147.49019,935.51931 147.46627,935.75137 C 147.44235,935.98343 147.43039,936.15145 147.43039,936.25546 C 147.43039,936.45541 147.46035,936.62539 147.5203,936.76541 C 147.58023,936.90542 147.69016,937.01937 147.85007,937.10726 C 147.97824,937.17929 148.11826,937.01132 148.27011,936.60336 C 148.38217,936.29135 148.47421,935.91134 148.54623,935.46334 C 148.57015,935.3193 148.58212,935.16732 148.58212,935.00741 C 148.58212,934.62338 148.53011,934.25137 148.42612,933.89138 C 148.3221,933.5314 148.22214,933.35141 148.12618,933.3514 C 148.08615,933.35141 148.05417,933.37936 148.03025,933.43527 L 148.03025,933.43527 z M 151.76157,937.77926 C 151.64145,937.77926 151.54147,937.75521 151.46164,937.70712 L 151.05331,937.45516 C 150.98154,937.41513 150.92362,937.36715 150.87955,937.31124 C 150.83548,937.25534 150.78146,937.16732 150.7175,937.04721 C 150.6135,936.84725 150.56149,936.64328 150.5615,936.43527 L 150.72959,933.02731 C 150.72959,932.94723 150.78159,932.9072 150.88558,932.90719 C 150.93344,932.9072 150.99741,932.91721 151.07748,932.93722 C 151.15756,932.95724 151.23959,932.97726 151.32358,932.99728 C 151.40756,933.0173 151.50949,933.07529 151.62937,933.17123 L 151.46164,935.79916 C 151.46164,936.23935 151.48557,936.56344 151.53341,936.77145 C 151.58956,936.84323 151.6456,936.91122 151.7015,936.97543 C 151.97348,936.78329 152.14945,936.61526 152.2294,936.47134 C 152.30936,936.32742 152.34932,936.17538 152.34933,936.01522 L 152.4094,932.67941 L 152.72141,932.64315 C 153.01755,932.64316 153.27353,932.74716 153.48935,932.95516 L 153.47763,937.83932 C 153.40561,937.8713 153.32149,937.88729 153.2253,937.88729 C 153.20138,937.88729 153.14742,937.87728 153.06345,937.85726 C 152.97946,937.83724 152.90347,937.80721 152.83548,937.76718 C 152.76748,937.72714 152.72946,937.6672 152.72141,937.58737 L 152.64963,937.08346 L 152.13363,937.63534 C 152.03744,937.73129 151.91342,937.77926 151.76157,937.77926 L 151.76157,937.77926 z M 155.04757,933.75936 L 154.85567,933.66342 L 154.66342,933.66342 L 154.48361,933.43527 L 154.45944,932.85922 L 155.07174,932.72738 L 155.13144,930.44735 C 155.2596,930.41538 155.38363,930.39939 155.50351,930.39938 C 155.57552,930.39939 155.66153,930.40738 155.7615,930.42336 C 155.86148,930.43936 155.99545,930.4874 156.16342,930.56747 L 156.13961,932.60726 L 156.6677,932.60726 L 156.83542,932.91928 L 156.8237,933.49533 L 156.0195,933.57919 L 156.0195,937.75546 C 155.93161,937.77938 155.85959,937.79135 155.80344,937.79135 C 155.74752,937.79135 155.68154,937.78134 155.60549,937.76132 C 155.52945,937.7413 155.45553,937.72323 155.38376,937.70712 C 155.10372,937.62728 154.96372,937.4714 154.96372,937.23947 L 155.04757,933.75936 z M 160.42649,933.78317 L 160.49826,935.22311 L 158.79427,935.49923 C 158.65048,935.53122 158.55454,935.56723 158.50644,935.60726 C 158.40243,935.68734 158.35043,935.7994 158.35044,935.94345 L 158.37423,936.49533 C 158.37423,936.6152 158.41226,936.72518 158.4883,936.82528 C 158.56436,936.92538 158.65438,936.9714 158.75839,936.96334 C 158.98251,936.93136 159.26254,936.75534 159.59848,936.43527 C 159.93442,936.1152 160.08639,935.91916 160.05442,935.84713 C 160.08639,935.83126 160.1384,935.82333 160.21042,935.82333 C 160.3464,935.82333 160.44638,935.86734 160.51035,935.95535 C 160.57431,936.04336 160.60629,936.13534 160.6063,936.23129 C 160.60629,936.27133 160.59432,936.32333 160.5704,936.38729 C 160.52231,936.57138 160.45431,936.73141 160.36642,936.8674 C 160.32638,936.93136 160.21431,937.07138 160.03025,937.28744 C 159.87839,937.46322 159.72446,937.59115 159.56844,937.67123 C 159.41244,937.75131 159.2264,937.79135 159.01035,937.79135 C 158.8663,937.79135 158.74831,937.77731 158.65641,937.74923 C 158.56448,937.72116 158.47043,937.6672 158.37423,937.58737 L 157.75057,937.0234 C 157.7264,937.00729 157.70236,936.97122 157.67844,936.91519 C 157.65451,936.85916 157.63449,936.81515 157.61838,936.78317 C 157.56247,936.63937 157.53451,936.48739 157.53451,936.32723 L 157.53451,935.3912 C 157.53451,934.78329 157.66849,934.1553 157.93643,933.50723 C 158.20437,932.85916 158.49033,932.53513 158.79427,932.53512 C 158.84238,932.53513 158.87839,932.53915 158.90232,932.54721 C 159.05441,932.59531 159.24844,932.63132 159.48441,932.65524 C 159.72037,932.67917 159.90438,932.76315 160.03646,932.90719 C 160.16854,933.05124 160.26657,933.18924 160.33053,933.32119 C 160.3945,933.45315 160.42648,933.60715 160.42649,933.78317 L 160.42649,933.78317 z M 159.55051,933.77145 C 159.55051,933.63547 159.52451,933.51547 159.47251,933.41146 C 159.4205,933.30746 159.33847,933.23935 159.2264,933.20712 C 159.10629,933.1832 158.99435,933.31125 158.8906,933.59127 C 158.81857,933.79123 158.77048,933.93124 158.74631,934.01132 C 158.68233,934.17929 158.63437,934.32327 158.60238,934.44326 C 158.5704,934.56326 158.55441,934.69125 158.55442,934.82723 L 159.55051,934.59945 L 159.55051,933.77145 z M 161.68845,932.8713 L 161.79648,932.73947 L 162.0726,932.73947 C 162.13657,932.73947 162.21053,932.77743 162.29452,932.85336 C 162.37851,932.92929 162.44455,932.97531 162.49265,932.99142 C 162.60446,933.02341 162.69638,932.93741 162.76839,932.73342 C 162.84042,932.52945 162.92641,932.41342 163.0264,932.38534 C 163.12636,932.35727 163.19638,932.34323 163.23641,932.34323 C 163.38852,932.34323 163.52255,932.39725 163.63852,932.50527 C 163.75449,932.61331 163.81247,932.74326 163.81247,932.89511 L 163.80039,933.36312 C 163.80038,933.5953 163.77237,933.80539 163.71634,933.99337 C 163.6603,934.18136 163.57236,934.27536 163.45248,934.27535 C 163.26839,934.27536 163.14437,934.0953 163.08042,933.73519 C 163.04843,933.55917 162.9924,933.47116 162.91233,933.47116 C 162.84054,933.47116 162.78261,933.53116 162.73855,933.65115 C 162.69449,933.77115 162.67246,934.0112 162.67246,934.3713 C 162.67246,934.48337 162.67246,934.58139 162.67246,934.66537 C 162.67246,934.74936 162.67246,934.81527 162.67246,934.86312 L 162.67246,937.79135 C 162.58456,937.83138 162.48459,937.8514 162.37252,937.8514 C 162.30051,937.8514 162.2125,937.83938 162.10849,937.81533 C 162.00449,937.79128 161.90451,937.73324 161.80857,937.6412 C 161.71261,937.54916 161.66464,937.43124 161.66464,937.28744 L 161.68845,932.8713 z"
+       id="text3253" />
+    <path
+       style="fill:#ececec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3304)"
+       d="M 62.895745,955.18402 C 62.360025,954.46973 57.538595,951.43402 57.181455,948.04116 C 56.824315,944.6483 57.895745,913.75545 57.895745,913.75545 C 57.895745,913.75545 60.217165,909.11259 62.181455,908.57688 C 64.145745,908.04116 65.217165,907.50545 65.217165,907.50545 L 53.610025,904.82688 L 50.931455,901.43402 L 51.467165,913.93402 L 52.360025,944.6483 C 52.360025,944.6483 51.645745,947.68402 50.395745,948.04116 C 49.145745,948.3983 53.074315,953.57688 53.967165,953.93402 C 54.860025,954.29116 62.895745,955.54116 62.895745,955.18402 z"
+       id="path3282" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#fdfdfd;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3368)"
+       d="M 47.895745,901.79116 C 47.895745,901.79116 51.288595,907.68402 54.502885,908.04116 C 57.717165,908.3983 64.681455,909.6483 68.967165,909.6483 C 73.252885,909.6483 73.252885,909.6483 73.252885,909.6483"
+       id="path3284" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#fdfdfd;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 76.467165,910.00545 L 264.32431,909.29116"
+       id="path3286" />
+    <path
+       style="fill:#2c2c2c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 59.598995,868.26188 L 91.671345,744.01311 C 91.671345,744.01311 93.439105,737.19458 100.76272,733.65905 C 108.08633,730.12352 119.45054,729.61844 119.45054,729.61844 C 119.45054,729.61844 522.5014,731.13367 523.51155,731.13367 C 524.5217,731.13367 538.91638,733.91159 542.19938,737.19458 C 545.48237,740.47758 548.51283,747.54865 548.51283,747.54865 L 575.02933,869.52457 C 575.02933,869.52457 576.03949,876.3431 573.76664,877.35325 C 571.4938,878.3634 564.92782,878.3634 564.92782,878.3634 L 65.407375,874.82786 C 65.407375,874.82786 60.609145,873.56518 59.851535,872.30249 C 59.093915,871.0398 59.851535,868.51442 59.598995,868.26188 z"
+       id="path3752"
+       sodipodi:nodetypes="ccscssccsccsc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#868686;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3982)"
+       d="M 59.346455,869.77711 C 59.346455,869.77711 59.598995,872.30249 61.366765,873.0601 C 63.134535,873.81772 67.175145,873.81772 67.175145,873.81772 L 570.23111,877.85833 C 570.23111,877.85833 573.00902,878.11086 574.52425,876.3431 C 576.03949,874.57533 575.53441,870.02964 575.53441,870.02964"
+       id="path3754" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3962)"
+       d="M 163.96716,740.00545 L 162.18145,748.13045 L 166.02075,748.13045 L 168.07432,740.18402 L 163.96716,740.00545 z"
+       id="path3810" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3958)"
+       d="M 168.69932,739.20188 C 168.25288,739.20188 163.7886,739.0233 163.7886,739.0233"
+       id="path3812" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3954)"
+       d="M 189.36894,739.91617 L 188.29753,748.13046 L 192.13682,748.13046 L 193.4761,740.09474 L 189.36894,739.91617 z"
+       id="path3814"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3950)"
+       d="M 193.92253,739.1126 C 193.4761,739.1126 189.01181,738.93402 189.01181,738.93402"
+       id="path3816" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3946)"
+       d="M 215.44037,739.46974 L 214.45824,747.59474 L 218.29752,747.59474 L 219.54752,739.64831 L 215.44037,739.46974 z"
+       id="path3818"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3942)"
+       d="M 220.17252,738.66617 C 219.7261,738.66617 215.26181,738.48759 215.26181,738.48759"
+       id="path3820" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3938)"
+       d="M 241.06539,740.00545 L 240.17254,748.13045 L 244.01183,748.13045 L 245.17254,740.18402 L 241.06539,740.00545 z"
+       id="path3822"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3934)"
+       d="M 245.79754,739.20188 C 245.3511,739.20188 240.88681,739.0233 240.88681,739.0233"
+       id="path3824" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3930)"
+       d="M 266.33324,739.64831 L 265.79752,748.39831 L 269.63681,748.39831 L 270.44038,739.82688 L 266.33324,739.64831 z"
+       id="path3826"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3926)"
+       d="M 271.06538,738.84474 C 270.61895,738.84474 266.15466,738.66616 266.15466,738.66616"
+       id="path3828" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3922)"
+       d="M 291.95824,740.36259 L 291.69039,748.75545 L 295.52968,748.75545 L 296.06539,740.54116 L 291.95824,740.36259 z"
+       id="path3830"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3918)"
+       d="M 296.69039,739.55902 C 296.24397,739.55902 291.77967,739.38044 291.77967,739.38044"
+       id="path3832" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3914)"
+       d="M 318.11895,740.63045 L 317.76181,749.20188 L 321.6011,749.20188 L 322.2261,740.80902 L 318.11895,740.63045 z"
+       id="path3834"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3910)"
+       d="M 322.8511,739.82688 C 322.40467,739.82688 317.94038,739.6483 317.94038,739.6483"
+       id="path3836" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3906)"
+       d="M 343.56537,740.63045 L 343.65466,748.84474 L 347.49395,748.84474 L 347.67252,740.80902 L 343.56537,740.63045 z"
+       id="path3838"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3902)"
+       d="M 348.29752,739.82688 C 347.85109,739.82688 343.3868,739.6483 343.3868,739.6483"
+       id="path3840" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3898)"
+       d="M 369.81537,740.36259 L 369.81537,748.93402 L 373.65466,748.93402 L 373.92252,740.54116 L 369.81537,740.36259 z"
+       id="path3842"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3894)"
+       d="M 374.54752,739.55902 C 374.10109,739.55902 369.6368,739.38044 369.6368,739.38044"
+       id="path3844" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3890)"
+       d="M 394.27966,740.98759 L 394.99395,749.38045 L 398.83324,749.38045 L 398.38681,741.16616 L 394.27966,740.98759 z"
+       id="path3846"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3886)"
+       d="M 399.01181,740.18402 C 398.56538,740.18402 394.10109,740.00544 394.10109,740.00544"
+       id="path3848" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3882)"
+       d="M 420.61895,740.63045 L 421.24395,749.02331 L 425.08324,749.02331 L 424.7261,740.80902 L 420.61895,740.63045 z"
+       id="path3850"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3878)"
+       d="M 425.3511,739.82688 C 424.90467,739.82688 420.44038,739.6483 420.44038,739.6483"
+       id="path3852" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3874)"
+       d="M 446.15466,741.34473 L 447.13681,749.6483 L 450.9761,749.6483 L 450.26181,741.5233 L 446.15466,741.34473 z"
+       id="path3854"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3870)"
+       d="M 450.88681,740.54116 C 450.44038,740.54116 445.97609,740.36258 445.97609,740.36258"
+       id="path3856" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3866)"
+       d="M 472.1368,741.07688 L 473.20823,749.55902 L 477.04752,749.55902 L 476.24396,741.25545 L 472.1368,741.07688 z"
+       id="path3858"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3862)"
+       d="M 476.86896,740.27331 C 476.42252,740.27331 471.95823,740.09473 471.95823,740.09473"
+       id="path3860" />
+    <path
+       style="fill:#010101;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3996)"
+       d="M 457.00288,731.43402 L 458.43145,635.00545 L 457.71716,507.86259 C 457.71716,507.86259 460.10513,497.86259 471.28859,497.1483 C 478.45249,496.69074 482.71717,509.29116 482.71717,509.29116 L 482.71717,651.43402 L 484.14574,731.43402 L 457.00288,731.43402 z"
+       id="path3986"
+       sodipodi:nodetypes="cccscccc" />
+  </g>
+</svg>
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/icons/group.svg b/hostap/wpa_supplicant/wpa_gui-qt4/icons/group.svg
new file mode 100644
index 0000000..4ea959b
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/icons/group.svg
@@ -0,0 +1,616 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   height="160.00000"
+   id="Andysvg"
+   inkscape:version="0.46"
+   sodipodi:docbase="/home/andy/Desktop/etiquette-icons-0.4/scalable/filesystems"
+   sodipodi:docname="gnome-fs-network.svg"
+   sodipodi:version="0.32"
+   version="1.0"
+   width="160.00000"
+   x="0.00000000"
+   y="0.00000000"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="C:\Documents and Settings\All Users\Documents\Ubuntu Brig\Andy Fitzsimon\gnome-fs-network.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata3">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:title>Etiquette Icons</dc:title>
+        <dc:description />
+        <dc:subject>
+          <rdf:Bag>
+            <rdf:li>hash</rdf:li>
+            <rdf:li />
+            <rdf:li>filesystem</rdf:li>
+            <rdf:li>computer</rdf:li>
+            <rdf:li>icons</rdf:li>
+          </rdf:Bag>
+        </dc:subject>
+        <dc:publisher>
+          <cc:Agent
+             rdf:about="http://www.openclipart.org">
+            <dc:title>Andy Fitzsimon</dc:title>
+          </cc:Agent>
+        </dc:publisher>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Andy Fitzsimon</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:rights>
+          <cc:Agent>
+            <dc:title>Andy Fitzsimon</dc:title>
+          </cc:Agent>
+        </dc:rights>
+        <dc:date />
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <cc:license
+           rdf:resource="http://web.resource.org/cc/PublicDomain" />
+        <dc:language>en</dc:language>
+      </cc:Work>
+      <cc:License
+         rdf:about="http://web.resource.org/cc/PublicDomain">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs3">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 80 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="160 : 80 : 1"
+       inkscape:persp3d-origin="80 : 53.333333 : 1"
+       id="perspective97" />
+    <linearGradient
+       id="linearGradient4894">
+      <stop
+         id="stop4895"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop4896"
+         offset="0.47000000"
+         style="stop-color:#ffffff;stop-opacity:0.85567009;" />
+      <stop
+         id="stop4897"
+         offset="1.0000000"
+         style="stop-color:#ffffff;stop-opacity:0.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1853">
+      <stop
+         id="stop1854"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop1855"
+         offset="0.47000000"
+         style="stop-color:#ffffff;stop-opacity:0.85567009;" />
+      <stop
+         id="stop1856"
+         offset="1.0000000"
+         style="stop-color:#ffffff;stop-opacity:0.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1806">
+      <stop
+         id="stop1807"
+         offset="0.0000000"
+         style="stop-color:#000000;stop-opacity:0.35051546;" />
+      <stop
+         id="stop3276"
+         offset="0.64999998"
+         style="stop-color:#000000;stop-opacity:0.13402061;" />
+      <stop
+         id="stop1808"
+         offset="1.0000000"
+         style="stop-color:#000000;stop-opacity:0.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient893">
+      <stop
+         id="stop895"
+         offset="0"
+         style="stop-color:#000;stop-opacity:1;" />
+      <stop
+         id="stop896"
+         offset="1"
+         style="stop-color:#fff;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1317">
+      <stop
+         id="stop1318"
+         offset="0.00000000"
+         style="stop-color:#000000;stop-opacity:0.52892560;" />
+      <stop
+         id="stop1320"
+         offset="0.50000000"
+         style="stop-color:#000000;stop-opacity:0.17355372;" />
+      <stop
+         id="stop1319"
+         offset="1.0000000"
+         style="stop-color:#000000;stop-opacity:0.00000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1133">
+      <stop
+         id="stop1134"
+         offset="0.00000000"
+         style="stop-color:#8bb7df;stop-opacity:1.0000000;" />
+      <stop
+         id="stop1136"
+         offset="0.76209301"
+         style="stop-color:#2a6092;stop-opacity:1.0000000;" />
+      <stop
+         id="stop1135"
+         offset="1.0000000"
+         style="stop-color:#375e82;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1098">
+      <stop
+         id="stop1099"
+         offset="0.00000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop1101"
+         offset="0.50000000"
+         style="stop-color:#ffffff;stop-opacity:0.22314049;" />
+      <stop
+         id="stop1102"
+         offset="0.59930235"
+         style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+      <stop
+         id="stop1100"
+         offset="1.0000000"
+         style="stop-color:#ffffff;stop-opacity:0.60330576;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient902">
+      <stop
+         id="stop903"
+         offset="0.00000000"
+         style="stop-color:#000000;stop-opacity:0.00000000;" />
+      <stop
+         id="stop904"
+         offset="1.0000000"
+         style="stop-color:#000000;stop-opacity:0.22000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient892">
+      <stop
+         id="stop893"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:0.0000000;" />
+      <stop
+         id="stop894"
+         offset="1"
+         style="stop-color:#fff;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient888">
+      <stop
+         id="stop889"
+         offset="0.0000000"
+         style="stop-color:#626262;stop-opacity:1.0000000;" />
+      <stop
+         id="stop890"
+         offset="1"
+         style="stop-color:#fff;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient891"
+       x1="1.3485916"
+       x2="0.024647888"
+       xlink:href="#linearGradient888"
+       y1="-0.85185188"
+       y2="1.0899471" />
+    <linearGradient
+       id="linearGradient901"
+       spreadMethod="pad"
+       x1="1.5803921"
+       x2="0.14117648"
+       xlink:href="#linearGradient888"
+       y1="2.4285715"
+       y2="-0.38571429" />
+    <linearGradient
+       id="linearGradient905"
+       x1="-1.5389611"
+       x2="1.0909091"
+       xlink:href="#linearGradient888"
+       y1="2.7890625"
+       y2="-0.19531250" />
+    <radialGradient
+       cx="0.10362694"
+       cy="0.093750000"
+       fx="0.10362694"
+       fy="0.093750000"
+       id="radialGradient1132"
+       r="1.2958785"
+       xlink:href="#linearGradient1133" />
+    <linearGradient
+       id="linearGradient1138"
+       xlink:href="#linearGradient4894" />
+    <linearGradient
+       id="linearGradient1140"
+       x1="0.54117650"
+       x2="0.57647061"
+       xlink:href="#linearGradient888"
+       y1="-2.4210527"
+       y2="4.6315789" />
+    <linearGradient
+       id="linearGradient1141"
+       x1="1.8281938"
+       x2="-0.0088105723"
+       xlink:href="#linearGradient888"
+       y1="3.0546875"
+       y2="-0.44531250" />
+    <linearGradient
+       id="linearGradient1144"
+       x1="0.21960784"
+       x2="0.59607846"
+       xlink:href="#linearGradient1853"
+       y1="-11.111111"
+       y2="5.2777777" />
+    <linearGradient
+       id="linearGradient1146"
+       x1="0.51351351"
+       x2="-0.076576576"
+       xlink:href="#linearGradient892"
+       y1="0.55468750"
+       y2="1.1875000" />
+    <linearGradient
+       id="linearGradient1148"
+       x1="0.23245615"
+       x2="1.0789474"
+       xlink:href="#linearGradient892"
+       y1="0.15625000"
+       y2="-0.64843750" />
+    <linearGradient
+       id="linearGradient1150"
+       x1="0.25221238"
+       x2="-0.57522124"
+       xlink:href="#linearGradient892"
+       y1="0.57812500"
+       y2="1.4765625" />
+    <linearGradient
+       id="linearGradient1156"
+       x1="0.48260871"
+       x2="0.48260871"
+       xlink:href="#linearGradient888"
+       y1="-0.40000001"
+       y2="1.8750000" />
+    <linearGradient
+       id="linearGradient1157"
+       x1="1.5528169"
+       x2="-1.2077465"
+       xlink:href="#linearGradient888"
+       y1="3.3265307"
+       y2="-0.48979592" />
+    <linearGradient
+       id="linearGradient1166"
+       x1="0.52941179"
+       x2="0.57647061"
+       xlink:href="#linearGradient1317"
+       y1="-3.5714285"
+       y2="4.6315789" />
+    <linearGradient
+       id="linearGradient1167"
+       x1="1.6111112"
+       x2="-0.083333336"
+       xlink:href="#linearGradient888"
+       y1="3.0703125"
+       y2="0.046875000" />
+    <linearGradient
+       id="linearGradient1169"
+       x1="1.4780220"
+       x2="-0.13028169"
+       xlink:href="#linearGradient893"
+       y1="2.9218750"
+       y2="-0.26732674" />
+    <linearGradient
+       gradientTransform="scale(0.998371,1.001632)"
+       id="linearGradient1170"
+       x1="0.47284532"
+       x2="0.48655096"
+       xlink:href="#linearGradient902"
+       y1="-0.016295359"
+       y2="1.8378206" />
+    <linearGradient
+       id="linearGradient1171"
+       x1="0.83050847"
+       x2="0.56355929"
+       xlink:href="#linearGradient902"
+       y1="0.57812500"
+       y2="0.36718750" />
+    <radialGradient
+       cx="0.088082902"
+       cy="0.093750000"
+       fx="0.090673581"
+       fy="0.10937500"
+       id="radialGradient1315"
+       r="1.1765809"
+       xlink:href="#linearGradient1133" />
+    <radialGradient
+       cx="0.50000000"
+       cy="0.50000006"
+       fx="0.50352114"
+       fy="0.18269235"
+       id="radialGradient1316"
+       r="0.34964636"
+       xlink:href="#linearGradient1317" />
+    <linearGradient
+       id="linearGradient1404"
+       x1="0.53169012"
+       x2="0.54577464"
+       xlink:href="#linearGradient892"
+       y1="0.28888890"
+       y2="1.1000000" />
+    <linearGradient
+       gradientTransform="scale(0.997825,1.002180)"
+       id="linearGradient1505"
+       x1="0.47157744"
+       x2="0.48548824"
+       xlink:href="#linearGradient902"
+       y1="-0.024853170"
+       y2="1.8570156" />
+    <linearGradient
+       gradientTransform="scale(0.995847,1.004170)"
+       id="linearGradient1506"
+       x1="0.47042510"
+       x2="0.48481107"
+       xlink:href="#linearGradient902"
+       y1="-0.043652620"
+       y2="1.9025002" />
+    <linearGradient
+       gradientTransform="scale(0.997153,1.002855)"
+       id="linearGradient2740"
+       x1="0.47041038"
+       x2="0.48453596"
+       xlink:href="#linearGradient902"
+       y1="-0.033741195"
+       y2="1.8771822" />
+    <linearGradient
+       id="linearGradient4283"
+       x1="-0.77314812"
+       x2="0.99074072"
+       xlink:href="#linearGradient893"
+       y1="2.0837989"
+       y2="-0.033519555" />
+    <linearGradient
+       id="linearGradient4284"
+       x1="-2.3960868e-17"
+       x2="0.92957747"
+       xlink:href="#linearGradient893"
+       y1="3.3012049"
+       y2="-0.45783132" />
+    <radialGradient
+       cx="0.50000000"
+       cy="0.50000000"
+       fx="0.50000000"
+       fy="0.50000000"
+       id="radialGradient1977"
+       r="0.50000000"
+       xlink:href="#linearGradient1853" />
+  </defs>
+  <sodipodi:namedview
+     bordercolor="#666666"
+     borderopacity="1.0"
+     id="base"
+     inkscape:cx="62.122256"
+     inkscape:cy="81.091465"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:window-height="667"
+     inkscape:window-width="573"
+     inkscape:window-x="380"
+     inkscape:window-y="151"
+     inkscape:zoom="2"
+     pagecolor="#ffffff"
+     showborder="true"
+     showgrid="false"
+     inkscape:current-layer="Andysvg" />
+  <path
+     d="M 26.564473,83.749649 L 26.564473,121.41271 L 57.756286,121.41271"
+     id="path3723"
+     sodipodi:nodetypes="ccc"
+     style="fill:none;fill-rule:evenodd;stroke:#9c9c9c;stroke-width:5.7184987;stroke-linecap:round;stroke-linejoin:round;" />
+  <g
+     id="g2843"
+     transform="matrix(0.999379,0.000000,0.000000,0.999379,1.227893e-3,3.986513)">
+    <rect
+       height="8.3153667"
+       id="rect1906"
+       style="fill:url(#linearGradient1156);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.4473482pt;"
+       transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)"
+       width="57.567924"
+       x="33.326111"
+       y="78.658051" />
+    <rect
+       height="60.126495"
+       id="rect1907"
+       rx="5.4369707"
+       ry="5.4369707"
+       style="fill:url(#linearGradient905);fill-opacity:1;fill-rule:evenodd;stroke-width:1.6282668;"
+       transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)"
+       width="72.279724"
+       x="26.015469"
+       y="22.413721" />
+    <rect
+       height="38.044163"
+       id="rect1908"
+       style="fill:url(#radialGradient1315);fill-rule:evenodd;stroke:url(#linearGradient891);stroke-width:1.4649456pt;"
+       transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)"
+       width="58.178177"
+       x="33.386066"
+       y="31.695871" />
+    <path
+       d="M 27.690431,52.841444 L 27.370609,74.749236 C 27.319624,78.241665 29.310209,80.477938 32.807578,80.506029 L 72.625393,80.825852 L 76.463254,71.870840 L 32.008024,71.551020 L 31.688202,52.681533 L 27.690431,52.841444 z "
+       id="path1909"
+       sodipodi:nodetypes="czzccccc"
+       style="fill:url(#linearGradient1146);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;"
+       transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)" />
+    <rect
+       height="26.147448"
+       id="rect1913"
+       rx="7.4449978"
+       ry="7.4449978"
+       style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:2.3625000;"
+       transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)"
+       width="104.09673"
+       x="140.62315"
+       y="-34.316952" />
+    <rect
+       height="15.829688"
+       id="rect1914"
+       rx="3.7576280"
+       ry="3.7576280"
+       style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.3591428;"
+       transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)"
+       width="56.908955"
+       x="184.04552"
+       y="-28.539845" />
+    <rect
+       height="15.829688"
+       id="rect1915"
+       rx="2.9970589"
+       ry="2.9970589"
+       style="fill:url(#linearGradient1141);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:0.96249998;"
+       transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)"
+       width="28.796961"
+       x="145.28902"
+       y="-28.227346" />
+    <rect
+       height="3.3627598"
+       id="rect1916"
+       rx="1.6813799"
+       ry="1.6813799"
+       style="fill-opacity:0.13836475;fill-rule:evenodd;stroke-width:0.46326005;"
+       transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)"
+       width="49.231453"
+       x="187.88426"
+       y="-21.681381" />
+  </g>
+  <path
+     style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:helvetica"
+     d="M 7.0612345,-14.660837 L 7.0612345,-23.250681 L 8.2272501,-23.250681 L 12.738969,-16.50654 L 12.738969,-23.250681 L 13.828813,-23.250681 L 13.828813,-14.660837 L 12.662797,-14.660837 L 8.1510782,-21.410837 L 8.1510782,-14.660837 L 7.0612345,-14.660837 z M 19.869828,-16.664743 L 20.959672,-16.529978 C 20.787791,-15.893258 20.469432,-15.399118 20.004594,-15.047556 C 19.539745,-14.695993 18.945996,-14.520212 18.223344,-14.520212 C 17.313185,-14.520212 16.591506,-14.800485 16.058305,-15.361032 C 15.525101,-15.921578 15.2585,-16.70771 15.2585,-17.719431 C 15.2585,-18.766302 15.528031,-19.578801 16.067094,-20.156931 C 16.606155,-20.73505 17.305373,-21.024112 18.16475,-21.024118 C 18.996777,-21.024112 19.676464,-20.740909 20.203813,-20.174509 C 20.73115,-19.608098 20.994822,-18.811224 20.994828,-17.783884 C 20.994822,-17.721381 20.992869,-17.627631 20.988969,-17.502634 L 16.348344,-17.502634 C 16.387405,-16.819038 16.580764,-16.295601 16.928422,-15.932322 C 17.276076,-15.569039 17.709669,-15.387399 18.229203,-15.3874 C 18.615918,-15.387399 18.945996,-15.488961 19.219438,-15.692087 C 19.49287,-15.895211 19.709667,-16.219429 19.869828,-16.664743 L 19.869828,-16.664743 z M 16.406938,-18.369822 L 19.881547,-18.369822 C 19.834667,-18.893255 19.701855,-19.285833 19.483109,-19.547556 C 19.147168,-19.953801 18.711621,-20.156925 18.176469,-20.156931 C 17.692091,-20.156925 17.284865,-19.994816 16.954789,-19.670603 C 16.624709,-19.346379 16.442092,-18.912786 16.406938,-18.369822 L 16.406938,-18.369822 z M 24.592484,-15.604197 L 24.744828,-14.672556 C 24.44795,-14.610056 24.182326,-14.578806 23.947953,-14.578806 C 23.565139,-14.578806 23.268264,-14.639353 23.057328,-14.760447 C 22.846389,-14.88154 22.697952,-15.04072 22.612016,-15.237986 C 22.526077,-15.43525 22.483108,-15.850289 22.483109,-16.483103 L 22.483109,-20.063181 L 21.709672,-20.063181 L 21.709672,-20.883493 L 22.483109,-20.883493 L 22.483109,-22.424509 L 23.531938,-23.057322 L 23.531938,-20.883493 L 24.592484,-20.883493 L 24.592484,-20.063181 L 23.531938,-20.063181 L 23.531938,-16.424509 C 23.531936,-16.123726 23.55049,-15.930367 23.587602,-15.844431 C 23.624709,-15.758492 23.685256,-15.690133 23.769242,-15.639353 C 23.853224,-15.588571 23.973341,-15.56318 24.129594,-15.563181 C 24.246779,-15.56318 24.401075,-15.576852 24.592484,-15.604197 L 24.592484,-15.604197 z M 26.766313,-14.660837 L 24.862016,-20.883493 L 25.951859,-20.883493 L 26.942094,-17.291697 L 27.311234,-15.955759 C 27.326857,-16.022164 27.434279,-16.449898 27.6335,-17.238962 L 28.623734,-20.883493 L 29.707719,-20.883493 L 30.639359,-17.274118 L 30.949906,-16.084665 L 31.307328,-17.285837 L 32.373734,-20.883493 L 33.399125,-20.883493 L 31.453813,-14.660837 L 30.358109,-14.660837 L 29.367875,-18.3874 L 29.127641,-19.447947 L 27.867875,-14.660837 L 26.766313,-14.660837 z M 33.897172,-17.772165 C 33.897172,-18.924505 34.217484,-19.77802 34.858109,-20.332712 C 35.393264,-20.793644 36.045607,-21.024112 36.815141,-21.024118 C 37.670605,-21.024112 38.369823,-20.743839 38.912797,-20.183298 C 39.45576,-19.622746 39.727244,-18.848333 39.72725,-17.860056 C 39.727244,-17.059272 39.607127,-16.42939 39.366899,-15.970407 C 39.126659,-15.511422 38.77705,-15.154977 38.31807,-14.901072 C 37.859082,-14.647165 37.358106,-14.520212 36.815141,-14.520212 C 35.944045,-14.520212 35.239944,-14.799509 34.702836,-15.358103 C 34.165726,-15.916695 33.897172,-16.721382 33.897172,-17.772165 L 33.897172,-17.772165 z M 34.981156,-17.772165 C 34.981155,-16.975288 35.154983,-16.378609 35.502641,-15.982126 C 35.850295,-15.585641 36.287794,-15.387399 36.815141,-15.3874 C 37.338574,-15.387399 37.774121,-15.586617 38.121781,-15.985056 C 38.469433,-16.383492 38.643261,-16.990913 38.643266,-17.807322 C 38.643261,-18.576849 38.468456,-19.159856 38.118852,-19.556345 C 37.769238,-19.952824 37.334668,-20.151066 36.815141,-20.151072 C 36.287794,-20.151066 35.850295,-19.953801 35.502641,-19.559275 C 35.154983,-19.164739 34.981155,-18.569036 34.981156,-17.772165 L 34.981156,-17.772165 z M 40.957719,-14.660837 L 40.957719,-20.883493 L 41.906938,-20.883493 L 41.906938,-19.940134 C 42.149123,-20.381535 42.372756,-20.67255 42.577836,-20.813181 C 42.782912,-20.9538 43.008497,-21.024112 43.254594,-21.024118 C 43.610059,-21.024112 43.971387,-20.910831 44.338578,-20.684275 L 43.975297,-19.705759 C 43.717481,-19.858098 43.459669,-19.934269 43.201859,-19.934275 C 42.971388,-19.934269 42.764357,-19.864934 42.580766,-19.726267 C 42.39717,-19.58759 42.266311,-19.395207 42.188188,-19.149118 C 42.070998,-18.774114 42.012405,-18.363958 42.012406,-17.91865 L 42.012406,-14.660837 L 40.957719,-14.660837 z M 44.983109,-14.660837 L 44.983109,-23.250681 L 46.037797,-23.250681 L 46.037797,-18.352243 L 48.533891,-20.883493 L 49.899125,-20.883493 L 47.520219,-18.5749 L 50.139359,-14.660837 L 48.838578,-14.660837 L 46.781938,-17.842478 L 46.037797,-17.127634 L 46.037797,-14.660837 L 44.983109,-14.660837 z"
+     id="text1232" />
+  <path
+     transform="scale(0.246729,0.246729)"
+     style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:helvetica"
+     d="M 91.619637,-37.962852 L 92.756355,-37.675743 C 92.518066,-36.742148 92.089356,-36.030234 91.470222,-35.540001 C 90.851076,-35.049766 90.09424,-34.804649 89.199715,-34.804649 C 88.27393,-34.804649 87.521001,-34.993126 86.940926,-35.370079 C 86.360846,-35.747031 85.91944,-36.292929 85.616707,-37.007774 C 85.313972,-37.722615 85.162605,-38.490193 85.162605,-39.310509 C 85.162605,-40.205035 85.333503,-40.985307 85.675301,-41.651329 C 86.017096,-42.317337 86.503424,-42.823196 87.134285,-43.168907 C 87.765141,-43.514602 88.459476,-43.687453 89.217293,-43.687462 C 90.076662,-43.687453 90.799318,-43.468703 91.385262,-43.031212 C 91.971192,-42.593704 92.379394,-41.97847 92.609871,-41.185509 L 91.49073,-40.921837 C 91.291505,-41.54683 91.002443,-42.001908 90.623543,-42.287071 C 90.244631,-42.57222 89.768069,-42.714798 89.193855,-42.714806 C 88.533695,-42.714798 87.981938,-42.556595 87.538582,-42.240196 C 87.09522,-41.923783 86.783697,-41.498979 86.604012,-40.965782 C 86.424322,-40.432574 86.334479,-39.882769 86.33448,-39.316368 C 86.334479,-38.585896 86.440924,-37.948201 86.653816,-37.403282 C 86.866705,-36.858358 87.197759,-36.451132 87.64698,-36.181602 C 88.096196,-35.91207 88.582523,-35.777305 89.105965,-35.777306 C 89.742678,-35.777305 90.28174,-35.960898 90.723152,-36.328087 C 91.164552,-36.695273 91.46338,-37.240194 91.619637,-37.962852 L 91.619637,-37.962852 z M 94.016121,-34.951134 L 94.016121,-41.17379 L 94.96534,-41.17379 L 94.96534,-40.230431 C 95.207525,-40.671831 95.431158,-40.962846 95.636238,-41.103477 C 95.841314,-41.244096 96.066899,-41.314409 96.312996,-41.314415 C 96.668461,-41.314409 97.029789,-41.201127 97.39698,-40.974571 L 97.033699,-39.996056 C 96.775883,-40.148394 96.518071,-40.224566 96.260262,-40.224571 C 96.02979,-40.224566 95.822759,-40.15523 95.639168,-40.016563 C 95.455572,-39.877887 95.324713,-39.685504 95.24659,-39.439415 C 95.1294,-39.064411 95.070807,-38.654255 95.070808,-38.208946 L 95.070808,-34.951134 L 94.016121,-34.951134 z M 102.29542,-36.95504 L 103.38526,-36.820274 C 103.21338,-36.183554 102.89502,-35.689414 102.43018,-35.337852 C 101.96533,-34.98629 101.37159,-34.810509 100.64893,-34.810509 C 99.738775,-34.810509 99.017096,-35.090782 98.483894,-35.651329 C 97.950691,-36.211875 97.684089,-36.998007 97.68409,-38.009727 C 97.684089,-39.056598 97.95362,-39.869098 98.492683,-40.447227 C 99.031744,-41.025346 99.730962,-41.314409 100.59034,-41.314415 C 101.42237,-41.314409 102.10205,-41.031206 102.6294,-40.464806 C 103.15674,-39.898394 103.42041,-39.10152 103.42042,-38.074181 C 103.42041,-38.011678 103.41846,-37.917928 103.41456,-37.792931 L 98.773933,-37.792931 C 98.812994,-37.109335 99.006354,-36.585898 99.354012,-36.222618 C 99.701665,-35.859336 100.13526,-35.677696 100.65479,-35.677696 C 101.04151,-35.677696 101.37159,-35.779258 101.64503,-35.982384 C 101.91846,-36.185507 102.13526,-36.509726 102.29542,-36.95504 L 102.29542,-36.95504 z M 98.832527,-38.660118 L 102.30714,-38.660118 C 102.26026,-39.183551 102.12744,-39.576129 101.9087,-39.837852 C 101.57276,-40.244097 101.13721,-40.447222 100.60206,-40.447227 C 100.11768,-40.447222 99.710454,-40.285113 99.380379,-39.960899 C 99.050299,-39.636676 98.867682,-39.203083 98.832527,-38.660118 L 98.832527,-38.660118 z M 108.77589,-35.718712 C 108.38526,-35.38668 108.00928,-35.152305 107.64796,-35.015587 C 107.28663,-34.878868 106.89893,-34.810509 106.48487,-34.810509 C 105.80128,-34.810509 105.27589,-34.977501 104.9087,-35.311485 C 104.54151,-35.645469 104.35792,-36.072226 104.35792,-36.591759 C 104.35792,-36.896444 104.42725,-37.174764 104.56593,-37.42672 C 104.7046,-37.67867 104.88624,-37.880818 105.11085,-38.033165 C 105.33546,-38.185505 105.58838,-38.30074 105.86964,-38.378868 C 106.07667,-38.433552 106.38917,-38.486286 106.80714,-38.537071 C 107.6587,-38.63863 108.28565,-38.759724 108.688,-38.900352 C 108.6919,-39.04488 108.69385,-39.136676 108.69386,-39.175743 C 108.69385,-39.605426 108.59424,-39.90816 108.39503,-40.083946 C 108.12549,-40.322222 107.7251,-40.441363 107.19386,-40.441368 C 106.69776,-40.441363 106.33155,-40.354449 106.09522,-40.180626 C 105.85889,-40.006793 105.68409,-39.699176 105.57081,-39.257774 L 104.53956,-39.398399 C 104.63331,-39.839801 104.7876,-40.196246 105.00245,-40.467735 C 105.21729,-40.739214 105.52784,-40.948198 105.93409,-41.094688 C 106.34034,-41.241167 106.81104,-41.314409 107.3462,-41.314415 C 107.87745,-41.314409 108.30909,-41.251909 108.64112,-41.126915 C 108.97315,-41.001909 109.21729,-40.844683 109.37354,-40.655235 C 109.52979,-40.465777 109.63916,-40.226519 109.70167,-39.937462 C 109.73682,-39.75777 109.7544,-39.433551 109.7544,-38.964806 L 109.7544,-37.558556 C 109.7544,-36.578085 109.77686,-35.957969 109.82178,-35.698204 C 109.8667,-35.438438 109.95557,-35.189415 110.08839,-34.951134 L 108.98682,-34.951134 C 108.87744,-35.169884 108.80713,-35.425743 108.77589,-35.718712 L 108.77589,-35.718712 z M 108.688,-38.074181 C 108.30518,-37.917928 107.73096,-37.785115 106.96534,-37.675743 C 106.53174,-37.61324 106.2251,-37.542928 106.04542,-37.464806 C 105.86573,-37.386678 105.72706,-37.27242 105.6294,-37.122032 C 105.53174,-36.97164 105.48292,-36.804647 105.48292,-36.621056 C 105.48292,-36.339804 105.58936,-36.105429 105.80225,-35.917931 C 106.01514,-35.73043 106.32667,-35.63668 106.73682,-35.636681 C 107.14307,-35.63668 107.5044,-35.725547 107.82081,-35.903282 C 108.13721,-36.081015 108.36963,-36.324179 108.51807,-36.632774 C 108.63135,-36.871054 108.68799,-37.222616 108.688,-37.687462 L 108.688,-38.074181 z M 113.69776,-35.894493 L 113.85011,-34.962852 C 113.55323,-34.900353 113.2876,-34.869103 113.05323,-34.869102 C 112.67042,-34.869103 112.37354,-34.929649 112.16261,-35.050743 C 111.95167,-35.171837 111.80323,-35.331016 111.71729,-35.528282 C 111.63135,-35.725547 111.58839,-36.140586 111.58839,-36.773399 L 111.58839,-40.353477 L 110.81495,-40.353477 L 110.81495,-41.17379 L 111.58839,-41.17379 L 111.58839,-42.714806 L 112.63721,-43.347618 L 112.63721,-41.17379 L 113.69776,-41.17379 L 113.69776,-40.353477 L 112.63721,-40.353477 L 112.63721,-36.714806 C 112.63721,-36.414023 112.65577,-36.220664 112.69288,-36.134727 C 112.72999,-36.048789 112.79053,-35.98043 112.87452,-35.929649 C 112.9585,-35.878867 113.07862,-35.853477 113.23487,-35.853477 C 113.35206,-35.853477 113.50635,-35.867148 113.69776,-35.894493 L 113.69776,-35.894493 z M 118.98292,-36.95504 L 120.07276,-36.820274 C 119.90088,-36.183554 119.58252,-35.689414 119.11768,-35.337852 C 118.65283,-34.98629 118.05909,-34.810509 117.33643,-34.810509 C 116.42627,-34.810509 115.7046,-35.090782 115.17139,-35.651329 C 114.63819,-36.211875 114.37159,-36.998007 114.37159,-38.009727 C 114.37159,-39.056598 114.64112,-39.869098 115.18018,-40.447227 C 115.71924,-41.025346 116.41846,-41.314409 117.27784,-41.314415 C 118.10987,-41.314409 118.78955,-41.031206 119.3169,-40.464806 C 119.84424,-39.898394 120.10791,-39.10152 120.10792,-38.074181 C 120.10791,-38.011678 120.10596,-37.917928 120.10206,-37.792931 L 115.46143,-37.792931 C 115.50049,-37.109335 115.69385,-36.585898 116.04151,-36.222618 C 116.38917,-35.859336 116.82276,-35.677696 117.34229,-35.677696 C 117.72901,-35.677696 118.05909,-35.779258 118.33253,-35.982384 C 118.60596,-36.185507 118.82276,-36.509726 118.98292,-36.95504 L 118.98292,-36.95504 z M 115.52003,-38.660118 L 118.99464,-38.660118 C 118.94776,-39.183551 118.81494,-39.576129 118.5962,-39.837852 C 118.26026,-40.244097 117.82471,-40.447222 117.28956,-40.447227 C 116.80518,-40.447222 116.39795,-40.285113 116.06788,-39.960899 C 115.7378,-39.636676 115.55518,-39.203083 115.52003,-38.660118 L 115.52003,-38.660118 z M 125.43995,-34.951134 L 125.43995,-35.73629 C 125.04541,-35.119102 124.46534,-34.810509 123.69971,-34.810509 C 123.20362,-34.810509 122.74756,-34.947227 122.33155,-35.220665 C 121.91553,-35.494102 121.59327,-35.875937 121.36475,-36.366173 C 121.13624,-36.856405 121.02198,-37.419881 121.02198,-38.056602 C 121.02198,-38.677693 121.1255,-39.241169 121.33253,-39.747032 C 121.53956,-40.252886 121.8501,-40.640581 122.26417,-40.910118 C 122.67823,-41.179643 123.14112,-41.314409 123.65284,-41.314415 C 124.02784,-41.314409 124.36182,-41.235307 124.65479,-41.07711 C 124.94776,-40.918901 125.18604,-40.712847 125.36964,-40.458946 L 125.36964,-43.540977 L 126.41846,-43.540977 L 126.41846,-34.951134 L 125.43995,-34.951134 z M 122.10596,-38.056602 C 122.10596,-37.259725 122.27393,-36.664023 122.60987,-36.269493 C 122.94581,-35.874961 123.34229,-35.677696 123.79932,-35.677696 C 124.26026,-35.677696 124.65186,-35.866172 124.97413,-36.243126 C 125.29639,-36.620077 125.45752,-37.195272 125.45753,-37.968712 C 125.45752,-38.82027 125.29346,-39.44527 124.96534,-39.843712 C 124.63721,-40.242144 124.23291,-40.441363 123.75245,-40.441368 C 123.2837,-40.441363 122.8921,-40.249957 122.57764,-39.867149 C 122.26319,-39.484332 122.10596,-38.880817 122.10596,-38.056602 L 122.10596,-38.056602 z M 132.38331,-34.951134 L 131.40479,-34.951134 L 131.40479,-43.540977 L 132.45948,-43.540977 L 132.45948,-40.476524 C 132.90479,-41.035112 133.47315,-41.314409 134.16456,-41.314415 C 134.54737,-41.314409 134.90967,-41.23726 135.25147,-41.08297 C 135.59326,-40.928667 135.87451,-40.71187 136.09522,-40.432579 C 136.31592,-40.153277 136.48877,-39.816363 136.61378,-39.421837 C 136.73877,-39.027302 136.80127,-38.605427 136.80128,-38.156212 C 136.80127,-37.089803 136.5376,-36.265586 136.01026,-35.683556 C 135.48291,-35.101524 134.8501,-34.810509 134.11182,-34.810509 C 133.37745,-34.810509 132.80127,-35.117149 132.38331,-35.730431 L 132.38331,-34.951134 z M 132.37159,-38.109337 C 132.37159,-37.363241 132.47315,-36.824179 132.67628,-36.492149 C 133.00831,-35.94918 133.45752,-35.677696 134.02393,-35.677696 C 134.48487,-35.677696 134.8833,-35.877891 135.21925,-36.278282 C 135.55518,-36.678671 135.72315,-37.27535 135.72315,-38.068321 C 135.72315,-38.880817 135.56201,-39.480426 135.23975,-39.867149 C 134.91748,-40.253863 134.52784,-40.447222 134.07081,-40.447227 C 133.60987,-40.447222 133.21143,-40.247027 132.8755,-39.846642 C 132.53956,-39.446246 132.37159,-38.867145 132.37159,-38.109337 L 132.37159,-38.109337 z M 138.04346,-32.554649 L 137.92628,-33.544884 C 138.15675,-33.482385 138.35792,-33.451135 138.52979,-33.451134 C 138.76417,-33.451135 138.95167,-33.490198 139.09229,-33.568321 C 139.23292,-33.646448 139.34815,-33.755822 139.438,-33.896446 C 139.5044,-34.001916 139.61182,-34.263634 139.76026,-34.681602 C 139.77979,-34.740196 139.81104,-34.826134 139.85401,-34.939415 L 137.49268,-41.17379 L 138.6294,-41.17379 L 139.92432,-37.570274 C 140.09229,-37.113241 140.24268,-36.632773 140.3755,-36.128868 C 140.49659,-36.613241 140.64112,-37.085897 140.80909,-37.546837 L 142.13917,-41.17379 L 143.19386,-41.17379 L 140.82667,-34.845665 C 140.57276,-34.162072 140.37549,-33.691369 140.23487,-33.433556 C 140.04737,-33.085901 139.83252,-32.831019 139.59034,-32.668907 C 139.34815,-32.5068 139.05909,-32.425746 138.72315,-32.425743 C 138.52003,-32.425746 138.29346,-32.468714 138.04346,-32.554649 L 138.04346,-32.554649 z M 146.60987,-34.951134 L 149.9087,-43.540977 L 151.13331,-43.540977 L 154.64893,-34.951134 L 153.35401,-34.951134 L 152.35206,-37.552696 L 148.76026,-37.552696 L 147.8169,-34.951134 L 146.60987,-34.951134 z M 149.08839,-38.478477 L 152.0005,-38.478477 L 151.10401,-40.857384 C 150.83057,-41.580033 150.62745,-42.173783 150.49464,-42.638634 C 150.38526,-42.087845 150.23096,-41.540971 150.03175,-40.998009 L 149.08839,-38.478477 z M 155.43409,-34.951134 L 155.43409,-41.17379 L 156.38331,-41.17379 L 156.38331,-40.289024 C 156.84034,-40.972612 157.50049,-41.314409 158.36378,-41.314415 C 158.73877,-41.314409 159.0835,-41.247026 159.39796,-41.112267 C 159.7124,-40.977495 159.94776,-40.800737 160.10401,-40.581993 C 160.26026,-40.363238 160.36963,-40.103472 160.43214,-39.802696 C 160.47119,-39.607379 160.49072,-39.265583 160.49073,-38.777306 L 160.49073,-34.951134 L 159.43604,-34.951134 L 159.43604,-38.73629 C 159.43604,-39.165973 159.39502,-39.487262 159.313,-39.700157 C 159.23096,-39.913043 159.08545,-40.082965 158.87647,-40.209923 C 158.66748,-40.336871 158.42237,-40.400347 158.14112,-40.400352 C 157.6919,-40.400347 157.3042,-40.257769 156.97803,-39.972618 C 156.65186,-39.687457 156.48878,-39.146442 156.48878,-38.349571 L 156.48878,-34.951134 L 155.43409,-34.951134 z M 166.15089,-34.951134 L 166.15089,-35.73629 C 165.75635,-35.119102 165.17627,-34.810509 164.41065,-34.810509 C 163.91456,-34.810509 163.4585,-34.947227 163.04249,-35.220665 C 162.62647,-35.494102 162.30421,-35.875937 162.07569,-36.366173 C 161.84718,-36.856405 161.73292,-37.419881 161.73292,-38.056602 C 161.73292,-38.677693 161.83643,-39.241169 162.04346,-39.747032 C 162.25049,-40.252886 162.56104,-40.640581 162.97511,-40.910118 C 163.38917,-41.179643 163.85206,-41.314409 164.36378,-41.314415 C 164.73877,-41.314409 165.07276,-41.235307 165.36573,-41.07711 C 165.65869,-40.918901 165.89698,-40.712847 166.08057,-40.458946 L 166.08057,-43.540977 L 167.1294,-43.540977 L 167.1294,-34.951134 L 166.15089,-34.951134 z M 162.8169,-38.056602 C 162.8169,-37.259725 162.98487,-36.664023 163.32081,-36.269493 C 163.65674,-35.874961 164.05323,-35.677696 164.51026,-35.677696 C 164.9712,-35.677696 165.3628,-35.866172 165.68507,-36.243126 C 166.00733,-36.620077 166.16846,-37.195272 166.16846,-37.968712 C 166.16846,-38.82027 166.0044,-39.44527 165.67628,-39.843712 C 165.34815,-40.242144 164.94385,-40.441363 164.46339,-40.441368 C 163.99463,-40.441363 163.60303,-40.249957 163.28858,-39.867149 C 162.97413,-39.484332 162.8169,-38.880817 162.8169,-38.056602 L 162.8169,-38.056602 z M 168.78175,-34.951134 L 168.78175,-41.17379 L 169.73096,-41.17379 L 169.73096,-40.230431 C 169.97315,-40.671831 170.19678,-40.962846 170.40186,-41.103477 C 170.60694,-41.244096 170.83252,-41.314409 171.07862,-41.314415 C 171.43409,-41.314409 171.79541,-41.201127 172.16261,-40.974571 L 171.79932,-39.996056 C 171.54151,-40.148394 171.2837,-40.224566 171.02589,-40.224571 C 170.79541,-40.224566 170.58838,-40.15523 170.40479,-40.016563 C 170.2212,-39.877887 170.09034,-39.685504 170.01221,-39.439415 C 169.89503,-39.064411 169.83643,-38.654255 169.83643,-38.208946 L 169.83643,-34.951134 L 168.78175,-34.951134 z M 177.06104,-36.95504 L 178.15089,-36.820274 C 177.97901,-36.183554 177.66065,-35.689414 177.19581,-35.337852 C 176.73096,-34.98629 176.13721,-34.810509 175.41456,-34.810509 C 174.5044,-34.810509 173.78272,-35.090782 173.24952,-35.651329 C 172.71632,-36.211875 172.44971,-36.998007 172.44971,-38.009727 C 172.44971,-39.056598 172.71925,-39.869098 173.25831,-40.447227 C 173.79737,-41.025346 174.49659,-41.314409 175.35596,-41.314415 C 176.18799,-41.314409 176.86768,-41.031206 177.39503,-40.464806 C 177.92236,-39.898394 178.18604,-39.10152 178.18604,-38.074181 C 178.18604,-38.011678 178.18408,-37.917928 178.18018,-37.792931 L 173.53956,-37.792931 C 173.57862,-37.109335 173.77198,-36.585898 174.11964,-36.222618 C 174.46729,-35.859336 174.90088,-35.677696 175.42042,-35.677696 C 175.80713,-35.677696 176.13721,-35.779258 176.41065,-35.982384 C 176.68408,-36.185507 176.90088,-36.509726 177.06104,-36.95504 L 177.06104,-36.95504 z M 173.59815,-38.660118 L 177.07276,-38.660118 C 177.02588,-39.183551 176.89307,-39.576129 176.67432,-39.837852 C 176.33838,-40.244097 175.90284,-40.447222 175.36768,-40.447227 C 174.88331,-40.447222 174.47608,-40.285113 174.146,-39.960899 C 173.81592,-39.636676 173.63331,-39.203083 173.59815,-38.660118 L 173.59815,-38.660118 z M 180.6294,-34.951134 L 178.72511,-41.17379 L 179.81495,-41.17379 L 180.80518,-37.581993 L 181.17432,-36.246056 C 181.18995,-36.31246 181.29737,-36.740194 181.49659,-37.529259 L 182.48682,-41.17379 L 183.57081,-41.17379 L 184.50245,-37.564415 L 184.813,-36.374962 L 185.17042,-37.576134 L 186.23682,-41.17379 L 187.26221,-41.17379 L 185.3169,-34.951134 L 184.2212,-34.951134 L 183.23096,-38.677696 L 182.99073,-39.738243 L 181.73096,-34.951134 L 180.6294,-34.951134 z M 191.67432,-34.951134 L 191.67432,-43.540977 L 197.46925,-43.540977 L 197.46925,-42.527306 L 192.81104,-42.527306 L 192.81104,-39.867149 L 196.84229,-39.867149 L 196.84229,-38.853477 L 192.81104,-38.853477 L 192.81104,-34.951134 L 191.67432,-34.951134 z M 198.82276,-42.328087 L 198.82276,-43.540977 L 199.87745,-43.540977 L 199.87745,-42.328087 L 198.82276,-42.328087 z M 198.82276,-34.951134 L 198.82276,-41.17379 L 199.87745,-41.17379 L 199.87745,-34.951134 L 198.82276,-34.951134 z M 203.79151,-35.894493 L 203.94386,-34.962852 C 203.64698,-34.900353 203.38135,-34.869103 203.14698,-34.869102 C 202.76417,-34.869103 202.46729,-34.929649 202.25636,-35.050743 C 202.04542,-35.171837 201.89698,-35.331016 201.81104,-35.528282 C 201.7251,-35.725547 201.68214,-36.140586 201.68214,-36.773399 L 201.68214,-40.353477 L 200.9087,-40.353477 L 200.9087,-41.17379 L 201.68214,-41.17379 L 201.68214,-42.714806 L 202.73096,-43.347618 L 202.73096,-41.17379 L 203.79151,-41.17379 L 203.79151,-40.353477 L 202.73096,-40.353477 L 202.73096,-36.714806 C 202.73096,-36.414023 202.74952,-36.220664 202.78663,-36.134727 C 202.82374,-36.048789 202.88428,-35.98043 202.96827,-35.929649 C 203.05225,-35.878867 203.17237,-35.853477 203.32862,-35.853477 C 203.44581,-35.853477 203.6001,-35.867148 203.79151,-35.894493 L 203.79151,-35.894493 z M 204.26026,-34.951134 L 204.26026,-35.806602 L 208.2212,-40.353477 C 207.77198,-40.330035 207.37549,-40.318316 207.03175,-40.318321 L 204.49464,-40.318321 L 204.49464,-41.17379 L 209.58057,-41.17379 L 209.58057,-40.476524 L 206.21143,-36.527306 L 205.56104,-35.806602 C 206.0337,-35.841758 206.47706,-35.859336 206.89112,-35.859337 L 209.76807,-35.859337 L 209.76807,-34.951134 L 204.26026,-34.951134 z M 210.39503,-36.808556 L 211.438,-36.972618 C 211.49659,-36.554648 211.65967,-36.234336 211.92725,-36.011681 C 212.19483,-35.789024 212.56885,-35.677696 213.04932,-35.677696 C 213.5337,-35.677696 213.89307,-35.776328 214.12745,-35.973595 C 214.36182,-36.170859 214.47901,-36.402304 214.47901,-36.667931 C 214.47901,-36.90621 214.37549,-37.09371 214.16846,-37.230431 C 214.02393,-37.324178 213.66455,-37.443319 213.09034,-37.587852 C 212.3169,-37.783162 211.78077,-37.952107 211.48194,-38.094688 C 211.18311,-38.237263 210.95655,-38.434529 210.80225,-38.686485 C 210.64796,-38.938434 210.57081,-39.216754 210.57081,-39.521446 C 210.57081,-39.798785 210.63428,-40.055621 210.76124,-40.291954 C 210.88819,-40.528277 211.06104,-40.724565 211.27979,-40.880821 C 211.44385,-41.001909 211.66749,-41.104448 211.95069,-41.188438 C 212.23389,-41.272416 212.5376,-41.314409 212.86182,-41.314415 C 213.3501,-41.314409 213.77881,-41.244096 214.14796,-41.103477 C 214.51709,-40.962846 214.78955,-40.772417 214.96534,-40.532188 C 215.14112,-40.291949 215.26221,-39.97066 215.32862,-39.568321 L 214.29737,-39.427696 C 214.25049,-39.748004 214.11475,-39.998004 213.89014,-40.177696 C 213.66553,-40.357378 213.34815,-40.447222 212.938,-40.447227 C 212.45362,-40.447222 212.10792,-40.367144 211.90089,-40.206993 C 211.69385,-40.046832 211.59034,-39.859332 211.59034,-39.644493 C 211.59034,-39.50777 211.63331,-39.384723 211.71925,-39.275352 C 211.80518,-39.162067 211.93995,-39.068317 212.12354,-38.994102 C 212.22901,-38.955036 212.53956,-38.865192 213.05518,-38.724571 C 213.80127,-38.525349 214.32178,-38.362263 214.61671,-38.235313 C 214.91162,-38.108357 215.14307,-37.923787 215.31104,-37.681602 C 215.47901,-37.439412 215.56299,-37.138632 215.563,-36.779259 C 215.56299,-36.427695 215.46045,-36.09664 215.25538,-35.786095 C 215.0503,-35.475547 214.7544,-35.235313 214.36768,-35.065392 C 213.98096,-34.89547 213.54346,-34.810509 213.05518,-34.810509 C 212.24659,-34.810509 211.63038,-34.978477 211.20655,-35.314415 C 210.78272,-35.650352 210.51221,-36.148398 210.39503,-36.808556 L 210.39503,-36.808556 z M 216.82276,-42.328087 L 216.82276,-43.540977 L 217.87745,-43.540977 L 217.87745,-42.328087 L 216.82276,-42.328087 z M 216.82276,-34.951134 L 216.82276,-41.17379 L 217.87745,-41.17379 L 217.87745,-34.951134 L 216.82276,-34.951134 z M 219.48878,-34.951134 L 219.48878,-41.17379 L 220.43214,-41.17379 L 220.43214,-40.300743 C 220.62745,-40.605425 220.88721,-40.850542 221.21143,-41.036095 C 221.53565,-41.221635 221.90479,-41.314409 222.31886,-41.314415 C 222.77979,-41.314409 223.15772,-41.218706 223.45264,-41.027306 C 223.74756,-40.835893 223.95557,-40.568316 224.07667,-40.224571 C 224.56885,-40.951128 225.20947,-41.314409 225.99854,-41.314415 C 226.61572,-41.314409 227.09033,-41.14351 227.42237,-40.80172 C 227.75439,-40.459917 227.92041,-39.933551 227.92042,-39.222618 L 227.92042,-34.951134 L 226.87159,-34.951134 L 226.87159,-38.871056 C 226.87158,-39.292926 226.8374,-39.596637 226.76905,-39.782188 C 226.70068,-39.96773 226.57666,-40.117144 226.39698,-40.230431 C 226.21729,-40.343706 226.00635,-40.400347 225.76417,-40.400352 C 225.32666,-40.400347 224.96338,-40.254839 224.67432,-39.963829 C 224.38526,-39.672809 224.24072,-39.206989 224.24073,-38.566368 L 224.24073,-34.951134 L 223.18604,-34.951134 L 223.18604,-38.994102 C 223.18604,-39.462848 223.1001,-39.81441 222.92823,-40.04879 C 222.75635,-40.28316 222.4751,-40.400347 222.08448,-40.400352 C 221.7876,-40.400347 221.51319,-40.322222 221.26124,-40.165977 C 221.00928,-40.009722 220.82667,-39.781207 220.71339,-39.480431 C 220.6001,-39.179645 220.54346,-38.746052 220.54346,-38.179649 L 220.54346,-34.951134 L 219.48878,-34.951134 z M 229.10401,-38.062462 C 229.10401,-39.214801 229.42432,-40.068316 230.06495,-40.623009 C 230.6001,-41.08394 231.25245,-41.314409 232.02198,-41.314415 C 232.87744,-41.314409 233.57666,-41.034135 234.11964,-40.473595 C 234.6626,-39.913043 234.93408,-39.13863 234.93409,-38.150352 C 234.93408,-37.349569 234.81397,-36.719687 234.57374,-36.260704 C 234.3335,-35.801719 233.98389,-35.445274 233.52491,-35.191368 C 233.06592,-34.937462 232.56495,-34.810509 232.02198,-34.810509 C 231.15088,-34.810509 230.44678,-35.089805 229.90968,-35.648399 C 229.37257,-36.206992 229.10401,-37.011679 229.10401,-38.062462 L 229.10401,-38.062462 z M 230.188,-38.062462 C 230.18799,-37.265585 230.36182,-36.668905 230.70948,-36.272423 C 231.05713,-35.875937 231.49463,-35.677696 232.02198,-35.677696 C 232.54541,-35.677696 232.98096,-35.876914 233.32862,-36.275352 C 233.67627,-36.673788 233.8501,-37.28121 233.85011,-38.097618 C 233.8501,-38.867145 233.6753,-39.450153 233.32569,-39.846642 C 232.97608,-40.243121 232.54151,-40.441363 232.02198,-40.441368 C 231.49463,-40.441363 231.05713,-40.244097 230.70948,-39.849571 C 230.36182,-39.455035 230.18799,-38.859333 230.188,-38.062462 L 230.188,-38.062462 z M 236.17628,-34.951134 L 236.17628,-41.17379 L 237.1255,-41.17379 L 237.1255,-40.289024 C 237.58252,-40.972612 238.24268,-41.314409 239.10596,-41.314415 C 239.48096,-41.314409 239.82569,-41.247026 240.14014,-41.112267 C 240.45459,-40.977495 240.68994,-40.800737 240.8462,-40.581993 C 241.00244,-40.363238 241.11182,-40.103472 241.17432,-39.802696 C 241.21338,-39.607379 241.23291,-39.265583 241.23292,-38.777306 L 241.23292,-34.951134 L 240.17823,-34.951134 L 240.17823,-38.73629 C 240.17823,-39.165973 240.13721,-39.487262 240.05518,-39.700157 C 239.97315,-39.913043 239.82764,-40.082965 239.61866,-40.209923 C 239.40967,-40.336871 239.16455,-40.400347 238.88331,-40.400352 C 238.43409,-40.400347 238.04639,-40.257769 237.72022,-39.972618 C 237.39405,-39.687457 237.23096,-39.146442 237.23096,-38.349571 L 237.23096,-34.951134 L 236.17628,-34.951134 z"
+     id="text1235" />
+  <g
+     id="g2852"
+     transform="matrix(1.018857,0.000000,0.000000,1.018857,-4.481650,2.131177)">
+    <rect
+       height="8.3153667"
+       id="rect1866"
+       style="fill:url(#linearGradient1156);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.4473482pt;"
+       transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)"
+       width="57.567924"
+       x="33.326111"
+       y="78.658051" />
+    <rect
+       height="60.126495"
+       id="rect1867"
+       rx="5.4369707"
+       ry="5.4369707"
+       style="fill:url(#linearGradient905);fill-opacity:1;fill-rule:evenodd;stroke-width:1.6282668;"
+       transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)"
+       width="72.279724"
+       x="26.015469"
+       y="22.413721" />
+    <rect
+       height="38.044163"
+       id="rect1868"
+       style="fill:url(#radialGradient1132);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient891);stroke-width:1.4649456pt;"
+       transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)"
+       width="58.178177"
+       x="33.386066"
+       y="31.695871" />
+    <path
+       d="M 27.690431,52.841444 L 27.370609,74.749236 C 27.319624,78.241665 29.310209,80.477938 32.807578,80.506029 L 72.625393,80.825852 L 76.463254,71.870840 L 32.008024,71.551020 L 31.688202,52.681533 L 27.690431,52.841444 z "
+       id="path1869"
+       sodipodi:nodetypes="czzccccc"
+       style="fill:url(#linearGradient1146);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;"
+       transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)" />
+    <g
+       id="g1870"
+       transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)">
+      <path
+         d="M 42.062098,33.460351 L 77.341205,33.008055 C 82.787126,32.938235 89.553204,38.416797 89.553204,43.863165 L 89.553204,60.145830 L 41.609801,59.693534 L 42.062098,33.460351 z "
+         id="path1871"
+         sodipodi:nodetypes="czzccc"
+         style="fill:url(#linearGradient1148);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;" />
+      <path
+         d="M 78.337784,67.629235 L 46.723745,67.724544 C 41.843589,67.739257 35.829319,62.771024 35.877168,57.891081 L 36.020221,43.301821 L 78.973514,44.128288 L 78.337784,67.629235 z "
+         id="path1872"
+         sodipodi:nodetypes="czzccc"
+         style="fill:url(#linearGradient1150);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;" />
+    </g>
+    <rect
+       height="26.147448"
+       id="rect1888"
+       rx="7.4449978"
+       ry="7.4449978"
+       style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:2.3625000;"
+       transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)"
+       width="104.09673"
+       x="140.62315"
+       y="-34.316952" />
+    <rect
+       height="15.829688"
+       id="rect1889"
+       rx="3.7576280"
+       ry="3.7576280"
+       style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.3591428;"
+       transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)"
+       width="56.908955"
+       x="184.04552"
+       y="-28.539845" />
+    <rect
+       height="15.829688"
+       id="rect1890"
+       rx="2.9970589"
+       ry="2.9970589"
+       style="fill:url(#linearGradient1141);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:0.96249998;"
+       transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)"
+       width="28.796961"
+       x="145.28902"
+       y="-28.227346" />
+    <rect
+       height="3.3627598"
+       id="rect1891"
+       rx="1.6813799"
+       ry="1.6813799"
+       style="fill-opacity:0.16981132;fill-rule:evenodd;stroke-width:0.46326005;"
+       transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)"
+       width="49.231453"
+       x="187.88426"
+       y="-21.681381" />
+  </g>
+</svg>
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg b/hostap/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg
new file mode 100644
index 0000000..1a02d13
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg
@@ -0,0 +1,374 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="64.000000px"
+   height="64.000000px"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.42"
+   sodipodi:docbase="G:\Projs\Cliparts Stocker\released"
+   sodipodi:docname="unknown_green.svg"
+   inkscape:export-filename="/datas/wiki/unknown_green.png"
+   inkscape:export-xdpi="90.000000"
+   inkscape:export-ydpi="90.000000">
+  <defs
+     id="defs4">
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2842"
+       id="linearGradient1363"
+       x1="25.403513"
+       y1="19.175573"
+       x2="35.541985"
+       y2="49.068703"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-2.402975,4.759656e-3)" />
+    <linearGradient
+       id="linearGradient2900">
+      <stop
+         id="stop2902"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2904"
+         offset="1.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2842">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2844" />
+      <stop
+         style="stop-color:#c8c8c8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2846" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2814">
+      <stop
+         id="stop2816"
+         offset="0.0000000"
+         style="stop-color:#e6e6e6;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2818"
+         offset="1.0000000"
+         style="stop-color:#11661d;stop-opacity:0.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2171">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2173" />
+      <stop
+         style="stop-color:#a3a5ee;stop-opacity:0.0000000;"
+         offset="1.0000000"
+         id="stop2175" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2160">
+      <stop
+         id="stop2162"
+         offset="0.0000000"
+         style="stop-color:#d3cece;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2164"
+         offset="1.0000000"
+         style="stop-color:#474240;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1367">
+      <stop
+         id="stop1369"
+         offset="0.0000000"
+         style="stop-color:#f67e36;stop-opacity:1.0000000;" />
+      <stop
+         id="stop1371"
+         offset="1.0000000"
+         style="stop-color:#602604;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1347">
+      <stop
+         style="stop-color:#f0da27;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop1349" />
+      <stop
+         style="stop-color:#bf4d09;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop1351" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1315">
+      <stop
+         style="stop-color:#97ff82;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop1317" />
+      <stop
+         style="stop-color:#ceff24;stop-opacity:0.0000000;"
+         offset="1.0000000"
+         id="stop1319" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2122">
+      <stop
+         style="stop-color:#2edc32;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2124" />
+      <stop
+         style="stop-color:#11661d;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2126" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1364">
+      <stop
+         style="stop-color:#236b0d;stop-opacity:1.0000000;"
+         offset="0.00000000"
+         id="stop1366" />
+      <stop
+         style="stop-color:#0a2205;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop1368" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1367"
+       id="radialGradient1402"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.211118e-16,1.330643,-1.347411,2.027373e-5,44.09678,-13.39507)"
+       cx="21.959658"
+       cy="14.921703"
+       fx="21.959658"
+       fy="14.921703"
+       r="27.500000" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2122"
+       id="radialGradient1404"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.211118e-16,1.330643,-1.347411,2.027373e-5,44.09678,-13.39507)"
+       cx="21.959658"
+       cy="14.921703"
+       fx="21.959658"
+       fy="14.921703"
+       r="27.500000" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1364"
+       id="linearGradient1419"
+       gradientUnits="userSpaceOnUse"
+       x1="74.910713"
+       y1="32.362179"
+       x2="84.910713"
+       y2="47.451466" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2122"
+       id="linearGradient1421"
+       gradientUnits="userSpaceOnUse"
+       x1="73.839287"
+       y1="34.428566"
+       x2="76.875000"
+       y2="43.714283" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1315"
+       id="linearGradient1423"
+       gradientUnits="userSpaceOnUse"
+       x1="72.946426"
+       y1="35.589287"
+       x2="85.000000"
+       y2="47.375000" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2171"
+       id="linearGradient2177"
+       x1="24.916031"
+       y1="28.824427"
+       x2="39.816792"
+       y2="49.099239"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2122"
+       id="radialGradient2184"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(9.909149e-17,1.088708,-1.102427,1.658760e-5,41.48828,-4.732338)"
+       cx="21.959658"
+       cy="14.921703"
+       fx="21.959658"
+       fy="14.921703"
+       r="27.500000" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1364"
+       id="linearGradient2189"
+       x1="10.018247"
+       y1="8.6306763"
+       x2="63.487556"
+       y2="63.660282"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2171"
+       id="linearGradient1339"
+       gradientUnits="userSpaceOnUse"
+       x1="24.916031"
+       y1="28.824427"
+       x2="39.816792"
+       y2="49.099239" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2122"
+       id="radialGradient1343"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.521415e-2,1.026125,-0.978137,2.404729e-2,38.83024,-3.575704)"
+       cx="24.764277"
+       cy="16.361967"
+       fx="24.764277"
+       fy="16.361967"
+       r="27.500000" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1364"
+       id="linearGradient1346"
+       gradientUnits="userSpaceOnUse"
+       x1="10.018247"
+       y1="8.6306763"
+       x2="63.487556"
+       y2="63.660282" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2814"
+       id="radialGradient2812"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.142398e-2,1.098850,-1.843995,1.878760e-2,52.15051,-5.667446)"
+       cx="18.387238"
+       cy="14.046815"
+       fx="18.387238"
+       fy="14.046815"
+       r="27.500000" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1364"
+       id="linearGradient2832"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-2.841000e-3,-2.841000e-3)"
+       x1="10.018247"
+       y1="8.6306763"
+       x2="63.487556"
+       y2="63.660282" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2842"
+       id="linearGradient2848"
+       x1="-0.56685609"
+       y1="22.651009"
+       x2="-0.33713850"
+       y2="23.858734"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2842"
+       id="linearGradient2864"
+       gradientUnits="userSpaceOnUse"
+       x1="-0.82287467"
+       y1="22.444542"
+       x2="-0.33713850"
+       y2="23.858734" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="8.2031250"
+     inkscape:cx="32.000000"
+     inkscape:cy="32.000000"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:grid-bbox="true"
+     inkscape:grid-points="true"
+     inkscape:window-width="1156"
+     inkscape:window-height="693"
+     inkscape:window-x="0"
+     inkscape:window-y="25"
+     showguides="false" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Green Unknown</dc:title>
+        <dc:date>2005-11-01</dc:date>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Jean-Victor Balin</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:description>jean.victor.balin@gmail.com</dc:description>
+        <cc:license
+           rdf:resource="http://web.resource.org/cc/PublicDomain" />
+        <dc:subject>
+          <rdf:Bag>
+            <rdf:li>icon</rdf:li>
+          </rdf:Bag>
+        </dc:subject>
+      </cc:Work>
+      <cc:License
+         rdf:about="http://web.resource.org/cc/PublicDomain">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Calque 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <g
+       id="g1354">
+      <path
+         id="path1373"
+         d="M 32.000000,8.6306766 C 19.113097,8.6306766 8.6306766,19.113097 8.6306766,32.000000 C 8.6306766,44.886903 19.113097,55.369323 32.000000,55.369323 C 44.886903,55.369323 55.369323,44.886903 55.369323,32.000000 C 55.369323,19.113097 44.886903,8.6306766 32.000000,8.6306766 z "
+         style="fill:url(#linearGradient1346);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+      <path
+         id="path1339"
+         d="M 54.500005,32.000000 C 54.500005,44.420003 44.420003,54.500005 32.000000,54.500005 C 19.579997,54.500005 9.4999950,44.420003 9.4999950,32.000000 C 9.4999950,19.579997 19.579997,9.4999950 32.000000,9.4999950 C 44.420003,9.4999950 54.500005,19.579997 54.500005,32.000000 z "
+         style="fill:url(#radialGradient1343);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+      <path
+         id="path1341"
+         d="M 32.016991,9.1562500 C 22.574792,9.1562500 14.505423,14.865048 11.062500,22.968750 C 16.006322,25.801817 21.393258,27.855853 27.181339,27.593750 C 32.755311,27.279922 37.553510,23.530916 43.236968,23.812500 C 47.451058,23.716455 52.244330,25.294372 54.488550,29.000000 C 53.142630,17.846718 43.657640,9.1562500 32.016991,9.1562500 z "
+         style="fill:url(#radialGradient2812);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+      <path
+         id="path2827"
+         d="M 32.000000,8.6250000 C 19.113098,8.6250000 8.6250000,19.113097 8.6250000,32.000000 C 8.6250000,44.886904 19.113097,55.375000 32.000000,55.375000 C 44.886904,55.375000 55.375000,44.886903 55.375000,32.000000 C 55.375000,19.113098 44.886903,8.6250000 32.000000,8.6250000 z M 32.000000,9.5000000 C 44.420004,9.4999998 54.500000,19.579997 54.500000,32.000000 C 54.499998,44.420004 44.420003,54.500000 32.000000,54.500000 C 19.579998,54.499998 9.5000000,44.420003 9.5000000,32.000000 C 9.5000000,19.579998 19.579997,9.5000000 32.000000,9.5000000 z "
+         style="fill:url(#linearGradient2832);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+      <path
+         id="text1353"
+         d="M 32.556888,39.006317 C 32.692760,35.835967 33.100380,35.066018 35.908404,32.892064 C 39.395790,30.219911 39.803410,29.902873 40.120445,29.631129 C 41.705621,28.272407 42.611437,26.189029 42.611437,24.015074 C 42.611437,19.078386 38.625844,15.953318 32.285143,15.953318 C 26.306768,15.953318 22.094721,18.851931 22.094721,23.018677 C 22.094721,25.464376 23.906354,27.230718 26.397344,27.230718 C 28.707171,27.230718 30.292350,25.736121 30.292350,23.607457 C 30.292350,22.384608 29.794150,21.388209 28.843045,20.663558 C 28.027812,20.029488 27.982521,19.984196 27.982521,19.667161 C 27.982521,19.033091 28.978919,18.534892 30.382931,18.534892 C 33.100374,18.534892 34.640263,20.346525 34.640263,23.516876 C 34.640263,25.373795 33.960900,27.683628 32.828632,29.721710 C 30.337643,34.160201 29.975314,35.066023 29.975314,37.104105 C 29.975314,37.557012 30.020605,38.281665 30.111187,39.006317 L 32.556888,39.006317 M 31.424619,41.497309 C 29.069501,41.497309 27.167287,43.399523 27.167287,45.754641 C 27.167287,48.064467 29.069501,50.011973 31.379328,50.011973 C 33.779736,50.011973 35.681951,48.109758 35.681951,45.754641 C 35.681951,43.399523 33.779736,41.497309 31.424619,41.497309"
+         style="font-size:45.290764px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;fill:url(#linearGradient1363);fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Century Schoolbook L" />
+    </g>
+  </g>
+</svg>
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/icons/laptop.svg b/hostap/wpa_supplicant/wpa_gui-qt4/icons/laptop.svg
new file mode 100644
index 0000000..06235f0
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/icons/laptop.svg
@@ -0,0 +1,1568 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="400"
+   height="400"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45"
+   version="1.0"
+   sodipodi:docbase="C:\Documents and Settings\Mete  Ä°slam\Desktop"
+   sodipodi:docname="MyLaptop.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="C:\Documents and Settings\Mete  Ä°slam\Desktop\MyLaptop.png"
+   inkscape:export-xdpi="98"
+   inkscape:export-ydpi="98"
+   sodipodi:modified="true">
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="319.93339"
+     inkscape:cy="202.90098"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     width="400px"
+     height="400px"
+     inkscape:window-width="1277"
+     inkscape:window-height="751"
+     inkscape:window-x="0"
+     inkscape:window-y="22"
+     showguides="true"
+     inkscape:guide-bbox="true" />
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3757">
+      <stop
+         style="stop-color:#70ffea;stop-opacity:1;"
+         offset="0"
+         id="stop3759" />
+      <stop
+         style="stop-color:#0055f6;stop-opacity:1;"
+         offset="1"
+         id="stop3761" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3460">
+      <stop
+         style="stop-color:#f1ff00;stop-opacity:1;"
+         offset="0"
+         id="stop3462" />
+      <stop
+         style="stop-color:#8bff00;stop-opacity:0;"
+         offset="1"
+         id="stop3464" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient26774">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0.1122449;"
+         offset="0"
+         id="stop26776" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0.89795917;"
+         offset="1"
+         id="stop26778" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient17245">
+      <stop
+         offset="0"
+         style="stop-color:#ffefef;stop-opacity:0.58163267;"
+         id="stop17247" />
+      <stop
+         offset="1"
+         style="stop-color:#ffefef;stop-opacity:0.14285715;"
+         id="stop17249" />
+    </linearGradient>
+    <pattern
+       patternTransform="matrix(0.9848362,0,0,0.9848362,-402.92422,36.839002)"
+       id="pattern13296"
+       xlink:href="#pattern13289"
+       inkscape:collect="always" />
+    <pattern
+       patternTransform="matrix(0.6565232,0,0,0.6651903,-8.1640579,-22.602821)"
+       id="pattern13287"
+       xlink:href="#pattern12311"
+       inkscape:collect="always" />
+    <pattern
+       patternTransform="translate(-88.774232,-72.100299)"
+       id="pattern12309"
+       xlink:href="#pattern11335"
+       inkscape:collect="always" />
+    <pattern
+       patternTransform="translate(-5.8654428,10.456268)"
+       id="pattern9368"
+       xlink:href="#pattern8394"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4451">
+      <stop
+         offset="0"
+         style="stop-color:#fffbfb;stop-opacity:0.82653064;"
+         id="stop4453" />
+      <stop
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;"
+         id="stop4455" />
+    </linearGradient>
+    <pattern
+       height="253"
+       id="pattern8394"
+       patternUnits="userSpaceOnUse"
+       patternTransform="translate(404.25649,166.01976)"
+       width="337">
+      <image
+         id="image4466"
+         width="337"
+         y="0"
+         xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QCURXhpZgAASUkqAAgAAAADADEBAgAcAAAAMgAAADIBAgAU AAAATgAAAGmHBAABAAAAYgAAAAAAAABBZG9iZSBQaG90b3Nob3AgQ1MyIFdpbmRvd3MAMjAwNjow NjoxMyAxMzozNDoyNAADAAGgAwABAAAAAQAAAAKgBAABAAAAAAQAAAOgBAABAAAAAAMAAAAAAAD/ 4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAABtbnRyUkdCIFhZWiAHzgACAAkABgAxAABhY3Nw TVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLUhQICAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAA AGx3dHB0AAAB8AAAABRia3B0AAACBAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAAC QAAAABRkbW5kAAACVAAAAHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1p AAAD+AAAABRtZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxi VFJDAAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2thcmQgQ29t cGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAABJzUkdCIElFQzYx OTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA WFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAABvogAAOPUA AAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVogAAAAAAAAJKAAAA+EAAC2z2Rlc2MAAAAAAAAAFklF QyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAAC5JRUMg NjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAC5JRUMg NjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAAAAAAAA AAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9uIGluIElFQzYxOTY2 LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBpbiBJRUM2MTk2Ni0y LjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAAABOk/gAUXy4AEM8UAAPtzAAEEwsA A1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVhcwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA Ao8AAAACc2lnIAAAAABDUlQgY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA3ADsA QABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8AMEAxgDL ANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUB fAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YD ogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUc BSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG 9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQ CSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4AL mAuwC8gL4Qv5DBIMKgxDDFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5k Dn8Omw62DtIO7g8JDyUPQQ9eD3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwR qhHJEegSBxImEkUSZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0 FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZ RRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2Z HcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUi giKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZclxyX3JicmVyaHJrcm6CcYJ0kneier J9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2K2krnSvRLAUsOSxuLKIs1y0MLUEt di2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/ M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6 Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50Ep QWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI 10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/dUCdQcVC7 UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjLWRpZ aVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr /2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXh dj52m3b4d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeA qIEKgWuBzYIwgpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuW i/yMY4zKjTGNmI3/jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqX dZfgmEyYuJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2 o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACw dbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2P vgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbL tsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx 2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6Lzp RunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio +Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t////2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB AQEBAQEBAQEBAQICAQECAQEBAgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEB AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wAARCAD9 AVEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIE AwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJico KSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZ mqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6 /8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAEC AxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNE RUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmq srO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEA PwD+lfSrfVX862m8NeKtZEEiS215pNhfvD4eWORy8dvNMFbxJpwkbf8AZzsRSzGGU53H2vwz4x+I WonTtGs9Nlhg1SLUZZdVn02TTLzSrywmS21Pfp98piiSSS6t76FSSSXlhw2045PwD8L9X1+4XXJv ih4oeLT7vypWjS5WSSRkJKW89zq0oVlDDJaM7Tj5TXu40K38D282p6d4m1ODTVunudTtPEF5Pqtn cNctHGiW7yuHsn3sVhSNtm+cZU1/gH4PeH3HOCy6jxLWqY/hLhydnWrUcdlHt8TgYVYTdSLoV5p0 qdNYmlOWJrurhqcozwTlChRw0Px3LMHjIwVdynhqD3anTvKF09HFvRLmTcpXimnDZRXiHj+XUrC0 j0D4rxR+Jnuplk0jxbpVpPpGn2YuN6PY3l60Hk296wjRkIUp+8AmIUb15LXPBdnaeHbm/wBA8VQW 99qdzpSwNqd3HpV450CymsbjS7iZpVSC/b7bbSPl9rqilQyMGrubvx+/jW71HQ7m+kl0XU9RSTQm vfBtrqml2giaQJZandWuqlLqCUlQJWEbwvt3YXfVLQYYvHFhN8N38O20+pwTrHbaDbwav4ZuLgc2 zXpe/S9FvdWUn2cRF5PLSKTY22IBa+Yz/KOGuMc9zOnllf8A1kjnWHr4LJMVjJzebPH0o1KKwtTG 4LE4zG4+VXCYic8DiMVRr4p061KlSoVasamHOetSoYmtNU39Y9rFxpSnd1edaKLnGUpzvFvklJSl ZpJN3ivKNP8AH/xN8FwWNhqUt3b6dZvusJr+0TU9MZdwkSGSZNyXFnkDY0Ugkh3nYcZWt24+OAuL 25h8SeFxDp89uWGk214L7RrlvLMkBm0zVYJY/IkkPM1s0TgMGG8jns/FH7PPxB+Gojm0fX7+00y/ kjt1i1OOG90eSaZfltdQaxeeBGJLLma2VG2kq2K841Pwpq9jpU11410b/hF9NimFvDr/AIdNrrOn 30wcs1vb6MjTfZwEEzNLBJbRxlTvjJIQ/nOa5J9IHw7jieG8zq59w9/Y0I1J4XOaFHMcFRw9OlyU 6dTMJqVHDYapSdlUxNPK6EqdoTnXtSjT4atPOcC5UKjrUPZWbjVUZwUUtE5vSMWuslTVtLvS0vhq 5+EnjG+uxfeB9V8O3KwtKsuj6re3vhu3LNtWbUYtiS6bbbyMsHMYGeBXVaRo+h+HZYtd0TRrLxLC qTW0d/4RmbWdNtJJPNSVNbm1W6luIYnh3KypbRgq6/vSzYHmupWXim80HUbHwra6Vq3g55IL6/m8 NSpPqM6WCu8cuuROI7pZlB3OHgWNCg8tQBz5jpGt6noF3LqGjajfaRfQqojls7l4GLCSM+VPGw/0 iIgMWRxj+8CowfgZ8d4LhTF5A8/4KwdXMF7TELPMFk2X5XWnVVVtVcFCnh/qGY0KUYR5K0fqzq1K skpUuRVJ8n1uGGlR9thYSlrL2saUKbbvo4pR5JxSSs1y3b3Vrv6P0bV/D17418R+RrGkQWF/ZXOg SJrdvJqeo6lfajiKxt9MFw0wuNNi1QW52uoRUgIKBSDXG3T62dMuofE1lDBbxaouiS2yQ29ldWt0 qxWyzaYkSqtlbx3caIWwI9l6ysG3k1iab4h8K+NtQtLXxhZ/8Izr9zdRpb+NfDUKQRPdySKIX1zQ 1IjbMpXM9sY3BO5kIya9f8eajcfD2S18TWOn6T4kh1lRBca3fFNStD4ksTDa3c9rarI8Vi08ETze UcN5yEycoFr6/CPDcU8K51xFUzmjT4WyTE4mtia+BVeco0c0m3OnmGS1HOtT+p1Vh4UqdKSy+s8V iZLMKslO3RHlxGHq1nUSw9KUnJwu9Kj2nSbuuV8qSXuPmk+d6m1+zr4oFt4k8T+C7i8muRdRrqlj JdbN73umKmn6gkciSMsyvapburKeVtjwMGvr+vzA0fxrqPh3xf4V8QzXEsljYazcXKQ4gRIrK4uP suqwLHbooiZrZnYoCU/eqy4yRX6cpOkqLLEQ8cqLJE4IKvHIodHBB5BUgj2Nf2d9DXjeln/AWd8I TxE62K4GxzVL2q5an1DMlLGYZtOrVbjHEPGUoS53ejCk7Q5uSP1HDOJVfCVcNzXlhJaX0fJU96PV 315ktdktrpE1YniHw/pvibS7jSdTjkaCdWMcsEjQ3VpOY5I0urSdeYLhVkcBhnAcjBBrY3j0NHmD 0Nf1vj8Bgs0wWKy7McLDG4HGwlTq0qkVOFSnNWlGUXdNNP8AXc+jlTVSMqc0pxkrNPVNeZ8b6v4O uPhdrnhyx8PXc+oajrN+lp4fvr8Wq2/h2yQLHrGqyRGNY7rXjDczKkm1xDbRM+AdoGZ8UfiDFqNh FqdlGJZ4dSu7W21BzzYWd9Dp9xBNNZuBHdaobSGzmt2iOYAwm4cgp9BfEO5snuIdM1W106OS/s7q 28IXk98y3tzr99YX1pqNulrHHugsI9MZ/Om3HcbtEUBvmHxPrOoHT4dFtNSMzxa/qN5r99Cthcos FrcRwaTpM1nY3IwL9U0+SeMYZSJ4gMoStf5neNeCh4Y4bizhHhPMllXCmdVY8mFSny5bUofUaVO8 cQ1NV54rEqvHFXq1P7MeDlh6tOjhcK4/C5rH6gsRhsNU9nh6jVo9INciWkurlLm5tX7PkcWlGJ6H 8MbctomsQalqF082uWRtrSV79WNvazqbKG6NrcuzQsItVlcb1ZPKcPwGBrbt9Ol8O/EptPm0y/vd KawTRY7t4Z5YdPjsbDNvvklPlvePqVvHOtw+VViGVG5I8k1HV5/DWu6Zo80qTWqXFpLLJbu7Ld6L eG0torOUnBu7hNPt4HU4DQXBeMD5ePT/AIivJokmra9BLc3/APausJb2OlK8kTzTaTHbxu2q3cn7 25hF7HEY7RAHuUdWEgTIPwXDmPy3D8J06UcJOOP8JswwU8TUcoSqKk446tiI1aUoVHVpV8TJYaXI qtZSnTcIzqUqduOhUgsNpF8+XTg5N2vb327qzunL3dLvVWvZHs3hyznv7s3erQ6lKdXliFppc15b x3VlYRzGS/OrrFCIprM3TLNbK6eYHuZYI5FXbn0PUBe6n/amlXNvYTaZLohn01VLL9ovhI88MLWj zZDQCC1cMP73DLjFfNeh6t4o0211K+axm1jxVq01vqCaNPNtezs9YsBaxz3beZvgnSKDISJ1EUVv KiBWDCP2DxVr2p2GkeFhoS2F1rfkM0Viv2dtNE2nQiHVSt/Od08KiVwIgyGUIHZwocN/XXA3F2U/ 6o5vVxWEx9GGEhGtjKcqXPicZSxOIqYVUKlqUYV8ROpWqVXgaU5TwjlhsKqUKcrVPpMJiaf1aq5R mlFJyTXvSUny2eiu223yJ3jdRtbfx+S01zWNZGj6n4chj0LSxfyT6/Pp8sP9m2txGbWdhNazBbqF JzMPs7EhwvzhFUEe46/4ftPiL8M7LTdN1h7rNtp13petugnkuJ9Lfy/OuEVMSTyLFOskY2/vHKkj k1y3jQ3ev2sukW9na6laNcaZeT6ANR/se6nhawhluVEkaNm0hS4jljZCxyGVg2xlPVfDK8sLTw1a 6fpC38mk2FrqsVv9r08Wl59r0vVLpLuNbL5XKuZo/JXbkiPLNvc1zcDcMZVh+JuMeB82lLPOH+MM txdDF4vEc9HE1XGrQwtPC4dUY06csLhKEnFV/bVsbQrVKTqrC0qlBVlhKFNV8RhKt6tHE05KUpXU nrGPKrJLlinvdyi2r8qav4+99fa29pamDVtM/sxby01/TvEMcclpqJSzlktvEWrJagHS9QlW3mC3 ERMY2pA52ygnrLrR5r3TrpLTxFcCa90eHTV028gtNUhE7JG1xHPJKxjns7mVSGWX7s8bAEq7GvIf iBBq/gPxVfXllq0Njp2saha3+n6ZfR3M13q+j62q/a9Fu7W5iZZrKC5adWTexgMygxqGQjvdY1fT dY0vUdZ0IyQeXps1pqEJuYoowI7f7VFFp13Am+2m85jLbyvH8r2rrlcbW/MMkzbC08VxrknEdGrU 4kyKpKliKU8VKjUr4eEK2HhXwksJOD5I0aPtqkJUaanKu8dDD1J1FJefTqRUsVSrpuvRbTTlZtJO KlDla0Sjdqy351FtnlelWetL4vjW5tdJ0rxTZA/abe+GnnSdVsUt2xB9uRDJo92bYxi2klZSFHlC c4RR6X4YiOs+DviZ4V1q3vPDHicaRHqWrXeopdzvcW9gjJp2uLGsjfbHFraGKaRZDJKIomcMWY18 9+NPFl3Nc6N4shMx1K70mPT7jUJDCx1K1+yW4S31iyfdHPIUkk3uOJQAxBJG33T4K/EjxV4suRp1 14fs9Vvf3EOuamq2dgg8MsBbRvKikNJdK0jCEbWjdIZEAVitfCeFOfcI1OPK/BM8wx9eeaVMxp0l UwssZSx+CzbL3h1KrHCxp18NmUaVSPtcTguWhOhHFe+3UlXqceXVsNLGPCuc26ntErx5lOFSHLd8 tpRna13DRpS73fz9o+pfDLSb3R1SPxV4g1C31W2vFuraS30iCOa3uYglha2VqZWuo5QqvvyrswRQ AN1es+KtH8Z67JceHNN0DUbafUZZ7ltOgtLbTtOs7DT7+J5NRijJQ6ncXET2o891aUNZCNSxY45/ xv8ACDx94Vv9d1u30O38V2Imujpeo6e0cN9pVgWk+zXMlnp6xSvfojoG2pIqLEW6sCv2b4L1K/13 wv4d1fVLE2WpXGl2v2mFmjlZZjFGJ2SVDlQzxgshwysNrDK8/U+D/hRm+d4zjDw74wwmJ4Dq4aFG rTpYTK5YRYnBKpPCYmU8bV9vDFVKkOT2FR4rGxjGrOUX7SnBUN8sy6rWlicFiYywcopO0afLzRvy ybm+ZSbWz5pbtrVacd8FvG154t8N3tnqkCW97oWq6lo9seVa607Tp0htpvJZB5bwLJHA2erQZ4OR Xf2usWt/faxpQfbf6LNax3cbgpK0V3As0F55LKN1rIVkVHGVYwtzxivnDxx4n8Q6J441y38G2ip/ YNtaeIHgtrGWFNbuY7S8ur+xvblcG9tpZbmBPLhwfOky/wA6Zr17QvEMXxH8Mwa5ohHhjxh/Z0sI i1SzWW5065jcrLaX1s+06hpAuslTkY3h1CSZFf1Z4f8AG05Uo8B4nNKmc8U8GPEUK1SrQVJ5zQwU quFqfU6kqsaKxNGt7FONWopVIxXPJRqVMRT+gwOLdlg5VHVxGF5k21b2qg3F8rvbmTtu7vru5Lnp /iDq9xq3ifw9plk1l4r0O3fVdD0/WUMFp4g0W3vGi1CxBif92zR2jTQ3Csz7LgOF2xyLWjca/quu 6eupWenf2t4f1G5t7a3uNEmaLWvDi3No7Xt7qMMsmy6ht7lYCstvktDcLKqEA5+ZfGniTxn4X8d6 ZrNzpF/eG1Aub7R4knuYNO1S3uJo7c6Vqaqz21tMXYuN6RyW16BIpB49f1jRb+wh0DxLpmq+K/CG i3k0msXfhO0gtJ7+3uruOzE/hTSrdZ1WKMvFfXJkG6OFFd0bbhD8ZkniBnec1+LcHVnj8XPhvEun iqDp0sLUjhK1ZvCYtrFQWEcadOvVw+MpOdLEzWGwrhh6VehVqvko42rVliYyc5uhK0lZRfK2nGVp Ll0UnGSupe7FqKabMDxL4z0HwPceBYL+xvTofinTpJb/AMWaTZWkNpObjagg1i2FkoujLcvcXM6o YmDpHIRJ84PIeMfH2g6d4ps/FXgy0S+1fwrFLH9nhkSKLVNLezjebSooooUFm8unMXRZCxC2rvGk hRCMjxB8S/C3xJ0TUdN8VQ+HD4n8LahcJb2WuXuo+GbbXdMDliLC+KbbDUxGIVnS5iaIhAY2G5iv jniDwZZ31sl1beHPib4at7VhdLNoejWnjTQrOG6iWU28Gr6TrCm5jEUiNGWIKBpFIUMAPyXi3j3O q0cWuC8fgOIcjrTwWMwUqdOVHHZfWwlONOpCVWdKrg8LUo4zD1MTCOJxM8Q5K8nXw1RVY+Xi8bWa n9UnCtRfJOFladNxSTV2nCLUk5LmlzX3vF3PtuH4wab4n8BzeKPA8tsdaubGzW2sNbjmSHSr6/vH sM6oLfmSGH7NeynYQjx2e5nRWyPlzSPFupah8YoNM8Xyz6v4d+Jvhu58JXlxrkyabCwme8Rbexby V+yP5+yGKOIsWe8DRu421rW1xoGieErPwQvim+1zxSNKvdVn0zU/DWp28dzZ6+0GIpbTSvNuXv7X TJwk8CmVYkuZpcgxvtlt10eC60PWH8Y+DL3UINR0zSF125ttS+xaVJbzrZxf8I1Lb6WLa48UG+Fz vjBKIly4IU7p6+tz3ifiTi6pwbXzLOsNRx2QrL8RmFCjicG8HUrxm1j6FfDLGVaXtalKrVwlenz1 Yxi1Pnw9G6qbVsTXxf1SVStGM6Hs5TipQ5G18cZR52rtNxkrtLe8UtfmeDRbDwVf61c3sj6Xe6Vq E6XZhuhqQ8HFXmS10bSbiQBNY8eywBlSUjy9Ni33En78ARdp8PfHdiFHjH4hWdlb/D/wtcLpXw60 t9Nt9U1LT/E0hjljl0a7vV825itYit7qUkjsjzPEQokZFG58VfhL4+8c/GDWdG0bQprTwPBqt5qU OpaVaxT6VY2lyTc63qlxHZyu13r894l2BG/+kTSqkIVUAC+XeK10HUpreO5XVtM8L+G7R/Dvh7wx 5Fs9zY3TRtdLeapcPOsNrc3N8IZdRuXdpGe4eFECRqR+CVcv4g4CzjM5YLLpZblOQ43E4XBQxdJ4 fDZpiqFVOpiaykqUa2X4S1LE2go/W8SsHyUVRo08PgPCdOvgqtTkh7OlRnKMFNcsas4tXlLa8IaS stJy5bK0VGnV1u6jhM4GvWWi+IYNYub42M2lx6ZqmttNvYX82taRDKqX811ueLPk74liZwHaIt1v xJ1rxD8P/BXh34cLql1b+K/ESf8ACXfEXU/NZrlZ9QG3RfDst+gIjgjs3MkwBwzSgtw5z23w6+Hn h/XvFN98VPE8uj2PhDwqLC6tIIruO5TxFr1pYLJExuEijivLRLm1l2LboGuJoVTZgfN4Xq8958QW 8R+M76+tINT1G+vbuDSp5r1JtXl+1W0Mmj291dyRLbW0FjOjLGrSoGsyqKGLVeY5fnGScOZhjaM3 hs14vWJw+X+zxFX2ksowc4PG4+UKtS9OtjpeywdD2apyrUPrsaVKo8RF1CpTrUcPOafJVxXNGCUn d0oNOdRpvSU9IK1rrnsm5a4J+NXxQXTINFk8Z65LZ2moR321tRmked7aNYYIJrkPvls0jQhYw4jO 8kqTgjY07xJpnj7R08AP4c0jQ9ZvL281jQ9ZsJZbS2n8TeSVh057Bn8m0t7+zhhtXZMbrmG2lYcN nx/UZYfNkihgtYNjHiLzZGViMPEZXbkpkqSAVJjypIOTSgupLSSC5tnaO6t5RNFNkExyoQ8MsYPK yI4DAjowBHSvxWlxhn8MV7HOM3rZ9lsoLDV6WJm8RzYVyiqlKhPERnLDz5V7lWl7OcJJSTvv5Cxd fmtVqutBrlkpPmvHS6Tldxdlo1Zp6ln7NqH/AD4Xf/gO9Feuf8Lv1b/oAaB/4Ar/APFUVt/YnAf/ AEWNf/w21P8A5cV7HA/9Bkv/AAW/8/6ufqJoXxC0Vtd8Q+Jbm81+zt9IsLp9T8KT6jPfPYXklzFE l5a6akzQtBIqyJIN6izui4chXVl57wv8QNW+M3jXUfDFxYR2Xge2NlrstjdQqdUEejTWzWtrLcxs FMdzqRhklXa+EQoj85Pzr4ktfEPw98e6na2bvaX+navqcuk3bJmDUdPu3M/2OSGVTHdW01u4zGwK OZGQ5BXH0R8DvGfgHU9YuZotJPhjxbcabcRapbQyyf8ACPPbw3MMxuNOiuZWbTwSAZIgypEFPBXB H73wT4kcQcZ8bZH4c8T5/R4Qo4DOsW85y6tCVBZ5JudKphPrEYOk1JyVPEZbKOGw1aH+0UJTjKnh 8J9PhMdWxeLpYLEVVhYwqy9rBpr23Rxvt5Sh7sWvei2mox+qI7WCJPKitrSKLnEcdvFHGMnJ+RVA 6k/XvXyx421/Qr34mx3Pg25vtJ8a6TZXmn65ei3l0xWkj2nSrqFJVVrmW21OOyaaQAiS2nV18xYm x7LoXxa8CeIdd1bQbHxBpyXOmyCOKS6nFpDqG0bbhrGe5VY7lUl4yjsXHzKNvNfPnii30zxH+0f4 ZeXVvDkmlQ614Tsp0XVbQT3CQvBLMrqmRNIZJCoBYkqFUelfvXjfxRlme8JcGx4KzXLc7oZhxRl2 XuVCrCc6FWnWqx9thsXRq82CqYarStOrCnUlUw9SdODjCtzy9jNsRTq4bCrB1KdZVMRCGju002rx kn7ji1q0ndNpaPX9M7nw/F8SvhtaaJ44sGhfxHoGlvrdmoEc1pfvDb3MphJBMEyXK5GOV6e1fAH7 WCeIfhp4s8JjwdHfeHfCtj4cg07TpNPdjpTzI7iWyvLdlaKacrE0jecrtN5pYk4OP1EZTnjucDkd uPWvgv8Aab+IXguePxP4Z1rTNX1TT7O3stLvdY0o6ZLBa61MXnt4LX7Rdqy6haM0BkG3b85VmBOD /W/01uC+HJeB+ZYzG8TQ4X42p0svw+Gz+UfZ47FPKfrGPp4WviaFN4inhqtX2+IrSpOnTo1Z+2lz csaU/puK8LQeUzlPELD4tKEY1npOXs7zUXJK6i3du1km79En8Kjx7b61p9vpeoqnhO7hvptRTxD4 YtTbx3V7coscsms6fbur+UcFibR0VSxb7O9N16PxHYWFpqviSw0nxZoF/I1tYeJbWaKQ3LRgM0cW r2PlzwXCr1ju4y6k4KZryJ3QOwj3NGGbYzgK7Jk7C6qSFfbjIBIB6E1oWGt6jppP2K7liifcJbZi JrO4VwodLizmDRTowVQQynO0egr/AJ/IccYrH0q9DiKtXxGJdOFOniaEqfK/ZcsYLG4GrGeCzCnG mpJPlw+JdSSqVMVUS5JfjP1uU01WcnKySkmulrc8H7k0l/hlfVyex2Wm6foOp3tq+lauNMuhPDIm m+ImWGN3WRCqWms248p33Y2idYASBluTXp+m+Jv+EU8d+JfDvi2wWXwj4uvZrfUbDVo2a0iaSZv7 O1xQhwjBnRpJbdsiOYlW3RgDxNP7E1wFVaHw/q7AeVE7FtAv5DgCNZZGL6NKxzjeZLfPGYV5rqfE WsarpGtvpOpaWL6xvLLS7oaBre+UW9xfaZah5tOukZZLOU3LOVkgcJIANwda+s4Zzh8PYOnn2BjR wVbC5jhHHF4WFSvgcSquHx1GthcxwE06lKliKUqlDEUoqCqUKr9jgq0bTN6Fb2EFVgowlGcfeinK DvGacZw3SaumkleL0g9333iX4K3TnU9Q8MX1tFpejPnXNM1u9WC68PCaFLhZ4r9k8rWtHa3KPHPH iTZxIm9TX2j8KNRa+8CaFBPqOn6pfaPbLouoXWmXLXdqbjT1VIgJ2jUyObJrRmO3BL8E18PQ+N/+ EZvdP8Ia/Pq0F74aligi8R6dew3F1pGoXe06nZ3dpOrQ67okUEkdu1vKTxBIY2AkIr6g+Cup+E4t W8XaHoN/dLeTyWut6ho11pEuj2sV7tNrf6joUMszj+zZs2jNEGIiLKY2aJlx/aH0bcw4IyfxOrVO FMPTyGvxJTqZbmuArY6MJYfGU4VMVRpYfB1VRcq1KrhquGqRw6rUGpSr4R0adWrgMB9Tkc8LTxz+ rpUpV06dSEp25ZJcyUYu12nFxdrrVuNk3CH0NRTdx/unH05/EUbv9lvyr/RXlb/4dH2yi32+88n+ K3hzTNVh8Oa5qUk1rH4a1O5me+gIDWUOo2MtqtxN8p3WiXosmkGQMDJIGa+HPFtv9l8Q63b6xfX7 2Wi2Hh/TrOK5jkN/dRjTrSOGSC7c+XBMsZuCzkuCrsFV8Db+hfj6/u9O8H69qViubnT7RbtYi+xb iOGeJri1dyjbI5YPMRm2sVD7gCQBXzf4w1LwNezWFlrNxaaYm2AaBHomsu1/BJPapp73OoxXdqkE ttBO7CHypEaRRvC7WzX8MfSf8P8AJM7zPEVqeZYTJM2r/U8dVeKjUhRxc50MTllONWtGpGlJU4YS hOEKiVqtKlSl7WGLtQ+Sz7B0qlSTVSNGq+Wb5r2k3GVNJtO2iinr1STupaeUeEl1CTxjbxWmnaZF c3gt/wCxdWvY2u5mg+xiK70mzNzIIbnVZBEVWVgwhdJCpQPuX2i41PT9SivfC9xrOoHxFZzapBb6 pZWotb3TTb2tpepp8EMAP9qx/wBkywJ5kTiQ/Y8xsgZmbzHUrrxN4cc6Qrtq1v8AaDfafdQb11BN IsZYI9RSykkPmwTyXKWtvdwRujR+XKghWMFn6fXPHWk3ltbajoT2SXnhy1F1cwedb2d1f3Dxw6Qk r6jIytDNHKVSa1dw0sYhkUOvA/G+Ea2W8L5Rn+VY3MJ4fGQxHtcXhcXRnD2+HrtSxlPB0cPXhRq1 8PTprEUswjKvXWG+r06EY1o0WvMw0oYenWpSqNT5ryjJWunZyUVFpNxSupq75bJWdjat7bVLCz1X StHv9EuZIbbTV1TWZZrPTbDVNJh+3/aLKNSHlis4bGcyPISzyzRlDtVjmLx4tzq/hzwYdGNtqUmj Xc3h672odKuvsz2sXk3qW10AiF7iwu3W4yqRi2JG7kV5rrOoQaXo9tqHhvTb3UL/AMR60NsFxqMd 1bWeoxtPJc6JcaQ0YM3mNPJMEwIpo5FlaRh8q9Nc6roWsaP4v0TRNeW7uNX0m7gu4GaRNviGzjj1 XTtK8OyROEurU/YtTEoGA7OsakxjL+i86wGNynPuF/aew+sYGEadGOMk+a9armmX0Mtni6UFXqVJ 0cOsZN0sZUhTrrETftvbKroq0Jwq4e/LzQVlzb6+0gqbktW2kpaSaTu3e9+qg8Q23izwZr9q95ca td+HtXthc6lFDZxTvArC7si6tHC13p1u8d3F80kUkpjM++PzUFetfD+DXNTvdH8TtqD3Gj3ej61B NavdXEv2aW61CxudOTy7obzIqJc7slzGJBGXYDcfifwHdeJW1nStO0S7uXaS5N7rurXM1tPZWNpL ZTT6l9qGyRJ7aHSILyR0cvtlZdoD7c/Xfw48R2F3NoNr4ZgvbLwrd+HPENxDYJZhYr+5tNetrQax IyRg2moyLMztbhgsUUo2hlUEfceBnGdHi3N8lzTPXXw+Mw0MPhGozlTeKr4TMMNUw1SnOc5yxFDB wx0cNmTrVKk3OrTlCderWlGh1ZRiliatKpWvGUbR3a5nGcXFptvmUVPlndt6pptu0fCPEHjF/EHx B8X+E/FUrwabN4kll8I6jrUcUaeG9Z0+Q2tjEWuQ32fQr024huNmdpaO4GCrGvN5fEer+HptZ8Ja 8osra4t4NN8SwRQOt1HLCYRb3KOz7XljSNXVEYxSLBwWSRjXffGTSPD+va+moWsFzofiTxPpvnp9 unK6Brd7Z3EmlXWnx3bRD+y9aS9sVUiZRDcNMhMsTPluBs9d0bxLHZ+B/iGNX0TXrKWHSdI8VXVp AdR0bO61h0zxMZSkmp+H43dQhZPOt1clZDGMV/OvG39tx4t4hy3G8SU3nEswxv8AZuaVKs4YXMML j51K0MozGpU9l9VqToYhfVZYlqH1bETwtSp/Z0sLiqHiYp1ViasJ106jnL2dRtqM4zbapzbtytxl 7vNpyycW+TlkuN1uKUW9wq3FjeWAtI7uy1DT2kS2jSCVY4YHtZhm0mM1zLmMHg3IXO1MD6D/AGcf A+qX+qTfES01aTQ9IgnbR4dPhhW5l1lYobb+0RcmVwkNqZlUg4ZhISU27cn58gT/AIRW88X+HdbK yjTpJtNkKWhmc3sd0qZt47ggfYpkt3dmZdwVI5Y8OFNdX4c+Mfi34Y2Ftpeimyn0yW3tL+PTr511 CzS4vx9qvWjlhEb28hDMsiBv3b7Rzht3znhdmnBvCXiDk/FniBQxdDLskdWpPDYV1HiMFmeGr1KW HU506tKs8NTkq0uT2vNOcacKnt4xnFc2X1MLhsbSxOMUlTpXbjG/NGpGTUdU07LV2vq7J82x+nDO JAXU4bOCR2KkFTjrnGR7/lXyT8YPF8Xwx1a2j0KW80geJ72zk1qS2huZ7OxtZrtZdb1Ozt7m4+zD UpohEmFjTbuLZLOaj+FX7SFvr9z4pHjdk017e0Gq6XZ6VZSTW8Wm2Mbf2hFCQzTXN2qN57mRuURv LUbCD4j8ftV8bWPjiw1m1vpv7G1uyjvPDGoac7yaLqtjcSSNBF5FwGhnuFs5LRJ0kQ5zuxsYGv7b 8YPG3hriLwhw/GPAeLq4nFvGQpfWKNKMsdllOderhalerQjXpVsMsXGHs6TlVouarUrzp1ZUkfVZ pm1CvlccVg5OU3JK6Xv005OLbXMnHmtZap6rVNo+nPi7pPh2PwOddt7lLQ6NJYa+VNw5/tfTrua3 trmO9t0lX7QJYbpWUqBiYADG41oeHviH8OvCXhTwyJ9Z8PabHcW9tbxWuhrLPHFNLZ/aYorhIxJK tw8UYy0xLvJIMkls18Aa1451Xx9BZWXiYmTUNHtlstNudPSK0tl0y0Cy3NlLpdqUS+PmRJLhNrHy j5RyFQ5s+sa5p2g6dNZfZ0SHU9ctLJ9Pijk+zwXtjaie1Yyfv4gqPcGN5D5kUgKoVMZr8hq/SZwu D4uzji7g7hDD0MHiMswuHjOrSnOt7ajiYfWKlXDUp0FRbpVIU/Z08TOFT2NCcqjnUao+Y8/jDE1c VhcLGMJU4xu027prmbimraNKylZ2Tvd6fd/jv4aeHfjRD4b+JHg/xDd6RdFAl3faYslte67pNs8g OnRq8sQt9bjnSSGF5TsBkKy5VVI4Dxn8QtU8I6H4T0r4geFNQh8K6kr6BrEc9xdXOuaDfRkXmk6r ZaxcEjV9QTTJkF18wWV7aeIEAlRzvwF8Vaf420K8+DfjSUKlzC+p+ELmxuIrfUbefTJGmmLzWUoZ NVhuI47mAucyrG+4Mo+b2g6D4qSC50H4j3fw/wDGvg1tRubLw1ZeJ7zy9au5ILWOPQY/7SEBSPV5 r4TJKJA8sYJaN3yQP2LLJ0OPuH/9fuB8K+G8746w1Glm+LoUo4jL1nGHlReLo5xldVzq4XCVoQgo VsLUxMsXRx7qYihKuqNdelBrG0HjcHD2FbGRiqs0lKHtY8vMqtN3cYtJWcebmjNtq9mfF3xn8L+O NEuYbnWZYfGfh37LBdWPi/Tbe1e6Gk3Qb7BFq1zbxvJbqYFOxrver8hZXwQMb4Upq+m6rb+JtJ1b Xm0SyWc2dlBqsmgQ6hqce3daarIT5FxpVsJ45r2TJDR+XCgMkscZ+y7jwB4e1sJZXlr41+D8fgS1 vVsNaivdLGjXOhzTC7ls01i7eYa5o8bHz0W4VYoFumhCkbkHG6v4h+AMsum6NoXja00u6kuhcW+r aR4T0270y51nT4AJ5gJ7KKyGs3EzRMhMbRxytH5AjLhq/NMb4M08o4wfGdbiehklF1aVTBUcdmlK ni62Lg4QqUsNUzOWBxc6WHxP7mFPMsNaqo4eVSu1UqU4+bUypU8V9beJjRjdOCnUSk5JpNRdRwk1 GWiVSOvuty1aXmP/AAkFrpOoJr/iC00zXNWvQ4OqWMUmieKrizN2q/ab64sI1TRPD5jSVBFPC+pa x53liGOKUR1q6p4D8Q+IL/w1Lpnhfw9HpqeIrO78P+HNcE1nP4e0m7mSS4ay8NxTQ3FlKba0guJI LiKXy5Y5XEktu5ZLviHTP7c0uHWf2eNXg8R+NNOu7jS/EkPitoLPx7p9xJD5KXuh6LrS29tpV6Ns ytNDbicAgRTYVhWTpHwg+Lnhi2jm0+8sJvHutNa61408W6/4it724gsbW7iuE8FaDbG6kuNQv5TG TqEwVEkBWzjkCMxb36OS5tVxFXLq/DuN4tyFqninjsqhRxGGrObhJRp5l7PFf2njKmIio4nD1JQp YOeHWKq4qtVpOpPRUqrlKm8PPF0NJOdNKUXe2iqWl7SbkrSi7KDjzOTau6XxZ8f6hp+u3uh+F/E1 3JpNj4h1G+1hdLK3eoeMNXSW5lvNBlS0uEi8OaDZ2MstuGkKPO6y3TxyMgevLtE8A3HxQ102l5Lr Uuj6Rb2Wo2/jRr+xlTSvBU1quoRzeK49SLf2vdW0dy1ujxETM1vLHgxxIE1fit8MX0XxH40upfBf xIv0vr/UPEEGq6ZpkGl+GpBcXTan9na/s0u5LhkhvLqENKkTB12hR38z1n4neJbLwD4Wh8O+Z4X8 JanceJfD2q+FtLvbtbe/i0q70+9K3epTubwzPDrLRuyyIqhSqRqmUPwXFmZShxRxBU8SMJiI5blk 511l1F4iftKcMTHCU8OpYiCoUcN7SrQq4jEUKk8Vy8tamq2KqV8RPgxVW2JrvMIydKneXs05XaU1 FRXMlFRu4uUoty6rmk5Sfs3jWLw1F4b0vRPBepax4c8K+ALhr+0J0C5XxB4gvbq8hjvtU0nVLu5a 0nnluJLF0uBte3jcqwRAQ3zDqqXV/Ot1Z6lrdzBHbTX8sF7Bb7LVbBC264/s25CxsLmaYM/kq4Mh kzJuJrIh8Y3OnXVxaaN9qOk3l8JrWK6na61i3s5x5Mllb3TuRHIY22BtmS8Eco2sFIztc1K3jlur KWSLXL1rh5ZtcLSxzpI+d8SSIFN4+BH5sko5dCiqAC7fl/F/F2W8SpYunhIZesNGnRlRoVK1PDRd CKp4aGHj7SSVFYalGjThTw8YKEFKo4SaZ5mKxVOvaah7NxsmouSjppFR125VZWikkru1zEvYxFcS iJNsAlbyPmaRGiLsITFO0SGdNi8OVXd1IB6UwzE4HXOMAE565PWo/M3tl2baCATkuQueqhm5HJ4y PrT4FaeeGCGN5pZpo4YYl+V5pJZAkaAjO1mZlHtmvyGUFVqP2cLOpLSKSvdvZJL5JJeS7Hl6N6K1 xdzejfp/8TRXb/8ACEN/0HdA/wDA5v8A43RXq/6sZz/0B/8AlSn5f3/U1+r1v5PxX+Z+qkVn4h8X eC9J0Tx14YsYda0eE22pnxJq+maTe3elW6SEaxp80cjXFheW0aRGUyRhPnyySITjx7UPhnqOkapL rXhPx54Sv7bRlSa41RtWB1CziuFISHVbK1t5xcqbZ3R5kVoZkY7hjq/UdMtrPxDpT3Ny2h+L4Z7u 00LXYp5bTQvFsds721tZ63e6isyaDq5hxDLA7TDawhufKJVqy9E1u5F06+FPDOnaZrNpPP8Aa9Bu ra5vdVhltpXkuf7A1C7nVJ7MFZC2nt5bp96Hzc5r9k4lxOQZ7XyvB8TZPiMRnWVulh6OPWNlVzef 1PD0fq6TweAwUamNpynCWIw2Il7aqowxGW16kMXiMW/o686NWVOOIpSlWp2ip816r5FHl+CELzTa bT1ekqbalKZVXwH4e8Qo39n+O/A9nrXzTPpNvPqr6fLEgZp202aXTw28tylsN7L8yxsylQuH/wAI xomh38Ev/CytAttQ065hn2R6P4nM1rdW8iyqCDpgw6uq/wA67W3EXiR59N1r4c6XDqGs3djYaf4z 0rQNQtreDUtTuDHa2muaLDcRC3uHnVkkkgCSR/6zy3Qgn6N8Ufs66LqHw503w9olvZ23izR3/tCD UYzIE1PUbnyv7TsJrm4ZpDYy7Qtv5jMYTBEeBvzy5D4JY7jzL87zvgrg7LMxxfD+G+tc0q2eYWti cTGq1SwlPB/WaEaeNqUqdWtGvQr1sBUlCFHDy5nOcZoZVPGwq1sJhadSdCPM3etFyd9IqKkrTaTa abg7KMerXvHxK+OOpaZ4Q8KeGtG17T9K8eeM/A8HiCLV207VLpIrcWolurzT7RLYtHcyW0F68YkI dG27UZhkfmJ/wj3hrVL557/4saGZLy4ae5vLvSfE7zSTTuHmnk8yxw7licksMkcn09f/AGsr/UdE +KmgWdi91Zy+EPA/hDS7W5h3ILe4g04SSLHKBt3bpcMOQQSCCCRXyOZcsc4JzzgjqeegFeh9Lvxm zbi/xTzLhni/KKfElPw4xNXKqFLE4jM6WHjDDUcLSrVY0sNmNCP1rEYyli5YrESg6lSmsNT57UTT iXNKuJzGph8VTVdYGTppSlUUUoqKbtGaXNKSlzNq7XKr6Ho954R06OeaHTvH3g3UhHK8cTvc6hp3 nKGIR995p4jQkYP+sKj+8RzVF/AnizYZrPTBq8AyfP0K8s9YQgE87NOnkdRx3UdK4MyEck/pSxXU kDiSCWSGQHIlhdopByOjxsCvT261/JE8dwxias51+GamXwk24xwWOqRUU3omsZRx0pqK6e0g31kt z5tzoSbboOH+CbSX/gam396NO5tb6zkaK7sbu1lQEtHc208DrjqWWWMED8K2te8QQ6vZ+GI0F6Lr Q9CTSLme4nWRJXh1C9urdrML80MKW1zEgDEndGcAKBUdl8QvGenRyQWvibVvJljMUkNzcG+haNhh kMd+sgCkcEAdKxJtWW6wbuwspHBJMtvAthO+cZ3G0AjbGDjMZxuzzTlVynDYPF4XJ80xMVmsIwxF PFYSlGEY069OtT9nXo4ivOUlKmm5/V6LSvHWM2g5qcYyjTqSXtFaSlFJaNNWalJvVb8q7dSOW4aR 3kkO5pCWYuWZmJ6lmJyzEnJJ5JJJr6I+Avi68X4h+FklvZTKfM0O5hknYxahpVzavb2zhGbaby1l Ft0ALwRg8mL5vnO5l0owxPZvfLcl2We3ukt2gjTgo0N1E4aUnkENEgGM5PSk0rVptJ1LTtUt3aO4 02+tb6J1JV1ktZ0mQgr0OUA+hr1eBeJcXwJxpw9xDTrOvTyzG4PFVVSqaVIUa9Ov8SvaaSaakr2l OnOLhOcJaYTESwmKo107qnOMnZ7pSUvv+XdNWbR+3FMLgHA59f8A63r3rPsLyPUbCy1CE5hv7O1v YiCCDHdwRzpgj/ZkFXM46YPTqB+Pb1r/AKB6VSjVpU61Oaq0q0VKLWzjJJp/NNNH7OnCyknzKWxk eJRcy+HddSxm+zXraTf/AGS4IQiC5+zSeRMQ4K4WXY3Ix8vPFfnNFf6R4g8M3cd1f2Vx4n0k29xp ccjyva6ellcWGmapcahcxsftFtcC6WQMx2o9p5gOJMJ9+/Ea6a08AeNblTsMPhbXWDL8pU/2dcAE MOhyRivyH0u7+zWerSNE432kdslwjMJIrl5QVKOpG5Sy/OpJG0A8kCv8/wD6ZfE8Mm4m4Jy+WDjj aGPyrNPbQrTm6UVKVNUqtOnFOMcRSnTlOnVmpRjUVKpyqdGnOHxfFGJVLEYSChzRnTqXTei2s0u6 aum9nZ2ukz03VvFeq6rr8t3ql4dNt9Mv9llDbSKXL2aGaNlc7TfRGCIAyvkslygYyEqKxNP8T6nP fzx/2dHqN5qd+W1C0ijf7XqjTS73tY4IVKTXAumeVWaMlXQH5kUKOW0nW9TEtlYWKW0l6LhIrBf7 MtLqe4knlK/YZZJIWcwM0zgAZ/1hU/Ljb02oyjwtM2k6MyXHiXUUMUt/bGEpottdBopdG0q6j+W4 1LJlhurtTiPy2ggbmSQ/wxDM8wzRzz6pm2KjQhiHUxdScY1Jzq1opU8PRg5yp1q7SnRpxVGjCNJ8 1VewnPk+RVWdS9X2krKV5NpNtvaKV2pPRpaKy30vbtNalvdBmvtM0SefWtT1yP7VrepWZ82HS7W7 Ut/ZtpaWRYWt4qtKl3eJvUbWt4mMfm73eAdG1HTfEGma7frHHDZ6hZQaTBd3VtaRStPBdtBqCTtm KS2tbWGWecHaGHynG5iPKLC+lt1tooP3N1ZmRYdWC7fs6zTOhtpUkYCez8xtwbDMhduGU7R7J8MN C1fxTrEOqa1rljF4ZA1OLXb6FvtBciyYtaXVhLDsiMml6fcAEIiJbRSDcuQrfXcIuPFHGHD31HLs TUxdHFUp4PC061P6tg17WMpVa1aulObo15+3rV6vNSm4urVqWToR6cO/rGKoKNOTlzJwimuWOqbb bs3aTu5NWe7fReiaqH8IaHrOseELU3MPxBl1K10NdSiVIfC+g2iOniRZo8/ZLiN7yLybcKrK1tuk YkGuy+AWra7ea/b2N1f201np/hzWNUW10iMR6YRqr6Qtvb36gqg1CKeO5JEaYjM7oCFwK+b9T8bw eK7nx7b/AL/TvDV1baPNoVqqR48O6fpUy6Xpr21lhjJZfY7pBc+UUlkWUyfMylW734Sz33gPwxZ6 xFFGk3i/4laH4el1mzeO687w1YWcmqXi2LEbfLluAytu2t8jBlV1wP2vgnjHBLxR4bzDKalenwbk tPEVa8cNUlToQ9hjqjlKnhoxT9jmmYxo1qUsTU5KGHr0cC5OGB5D1cHi6f8AaFCVO/1WkpN8raiu WbvaKW1SpZxcnZRkoX9w07zVdE8f6P4j8Esr6nrPh64n1/wjpXnww6kvlSXH/CS+GdM1ZoGXWgkC Ry24KrK625QEPGrN51p+vS+I9DHhjWBpGqXFsby602+vtXg0rX7nSlijd9A/ted3lhurdlnaK2u1 lgkBdFZWSMt5Vr8txofiaW90zVJ5l+1x61oOtRNJBcT2txJ9qsL5TuzFcqQFlGSUlt5EJJU16Hd2 8nxStG8XeGLa2tfiLpw3+K/Dto0SXHidChz4r8Nac3+tvNqSC+tYgSsmJ4R8zAflC4qzLirHZjh3 hFU4syunVwWIwdFU5vO8ppVpVIxpNwr08RjsvppLDUZKpRq4KjQWDjCOGnh8X5rxNTETnHlviIJw lBWftaad7LSSlOmvhjrGUUuW3K4z627stK+IdtpdvpOoxR/EvR4INOh0XXIbXTrnxfoVtp+21juN QhuXtLvXbazVxFMHja4hIVkWVVz4bqmgeK9NtfKvdPvXsY1WRZ7YDU7BcgFUF7ZNLFGy+YwK7lKs zAjcTWPf+bo0ulNbw3Wm6nbwrdS3LSTwXkd8t1NtIjZVNpLEIUA2/NnLE5OB1+vyT+ItEufHnh4X ljPFLbWPxB0/T5ZIbaDVLhM2niJYrYqq6bqEsMzSIV2wXiOo+WSOvks2xeXcYUMwxGKyurg+Lcso +0xCwdeSjjcJGjB1MTVoV6M51cdhYwg8xlSrUadehGeMVGMqGKrV+WrKniY1JSpuGJpq75ZaSjZX k01dzjZc9mk1edvdk5cx4efXbTXtIudI0+4u9SjvYja2TWsrreYG6S1kRkxJBLbCVXBP+rZicDmv uDXtL0XWPhlf+BDa3Omp4at7DXNFsJbe6vNW0X7JqUp1rULZrlWOp6JE+oW8aLGxkkhEkeMqhr42 8OeNvHd1f6dBBrer39vpMbyraNeFALNZoZ5bUXUkiNEZpooIg/mBx5pVWCsyn7u+EHhHxpqvhnVb z4h+I5NQtPFNnciz0WFppdU0K2vZkaVE1+dvNQmBEj8lA6ARKwkLjNfvX0Z8lw+dQ4l4ZybA47OM PxJg8VQxFTFYTD0sFh6E8NGNZTqrFyqRVfFLDJSozq1J1MLhqv1SLoc9P2Mhpxre3w9OE6scRGUZ OUYqMU4pO75rrmly7XbcYvl0uvhHWotC0HVrjUJNasfE1zFNnS7eF7mwtzDFFHPBfakRaAzB28yL 7KPLk3KPOaP7lYPiDxDe69Hd6pJbWf2rVdZ1K5JsYFQQgWtlLNDDGBlUUupO1QOSQQCQf1Et/hJ8 OYFVZvDNjqhj2lZtbe61ifKII0DPfyvgBQABgDjPXNeQfHjwf4A8HfDbUvEGleDfC1pqWm3+nnTR 9heCJrm/uo7SdHjtJ4jODbMxKFtjeQu8Mq4r6biz6LnGGQcNcS53iOKMqy7JcBQr5hXwuHjjJy/c xdWcnWrQqVa1WNF1oUuebSclGFlKRvi+HcVQoYitLE06dGnGU3GPPf3dX7zTbdrpX8rdT4Z+G8Hi dfFuha34Zilt59M1aymGryeXb6bY7pljlN1eXLLEymFpVMRYtIGKBTnFfSvjdrnV9C1H48/Dia61 jUJ5jYeL9Os77Um0Xw9qumRQxz67p2jyRxvqtnuFvIPP/dwiQTGJ1ZtvyZfeOfEN7eaffG4gt5NA lF5p1jDHDDo1nIghjD2mikeRHJuCkhVO7JJHBr1/4AfEzVNCW58GaZrFtpep6prtjrGgRatg+HNc uvJfT9W8La25RjZJfWZh+z3GNkNzaJ5mFcmvyzwo4g4aoSqeH+Ix2Np5Xn9StXo47kpRrYTNqeHh TwGMwGGnVVKFaXNisHVpVK05YmM6EYVaNVUpUfKy2vh4v6jKclTrNtTsrxqqKVOUIt2Un70Gm3zX jZp2a7bwR8aJ7jXj4Y8X/b9d8JfE/UW0jUoNVuzKnhvWNVgj07UH0/z1JOmzNdxSrbMBEsc8bxHd Gc+OfE/4YTQvqOsfD3yvEXgvStQl06TTtOS8PiPwjOXKS6f4p0K5T7Rbzvd28xFwBJHIFB3KoFfU niPwd8PviF41huYL278G/Eu80yDUofAvinH9jzSaNLd6TYJYwWCL5eoW76V5pihd2kif5VUNvWDx LDaeDPEkln/bi6T8UviHZvrnh/U7/TGl0jw1rlsl7YWtjdavZzOt/fS/2hq9rZTSlvsiSx/afM2x Y/dc58P82zzh3G5XxnmuHz/I8rx1SGAzujVowx1KOIjzQwlq1WLhVr46VCX9iY5RqQniko4jB4fD RxC9etgqlehOni6sa1GnNqFaLippS2hZvRyna9GdmnL4oRipHzHB4k0/4a21lb+KfO1f4i3Nq1tN q2lPZnxF8M9Cu4RF9kTUJ1ePU/FjW7tiK4ydOjfy1mjmciPyjXNG08xjUtI8R6hc6JM0zDxBeW1x NKt00kzQWuupZXc0uiahtKKA0ZSbJlR5Ewa+htJ0Xxn8SR4hivNL8Hy6zbXLWniaPWPDWkImo6tD dxI14l9ZxWeoaZeCVV+028hO4Xbz27scxx+UhPDPh7WLmGS11PTNTtnube/tPAaaxKL22KMbiz1P SvHS+S9gtqszMyySBSc4bAavyjPcixOIweV+2jGjwnUcoZbUxlOphsVzwkqdZylCvNY7E1FShLFy rVKFXmhChhp0cFSo0X5FehOUafMlHCy0puScZ3Vk9m1OTt793FvSMXGCUX1eqeLPFHhvW/DPi/wt 4p1az1jxN4L8Nz2FtaaldXOi6ld22nz+ENTil05irXEx1PTY7gM8RRfNbcu4BzV1nx9canoPhy3+ J3w90Lx7cvqfiW2m1GKS58J69psltFpImU61pXlw3OpmNnaR7uGRi1sodmKNXftoHhbxL8NNB1Tw bbaBqM3h/X9WlsdV1YMq+GdM16FJPtGseHDqyJaRnxBZzouGntopplliiCSlU5zWvgZ8WfE2haPp F35c1tYaE2opeapDqdpaXF/d32p6ns0qa3gdL25/s6W1j8gK7BUyGGxQfvcXkfHUaOOWSfWeIMFn GEoY2nh8PGGYZVVxGJlg62KlUoYyFXCTlUrUsTGlVhQrUVClG0vbRkod06OOcZqjzV4VoRmlFKpS cpODleM04ttqVmoyVkvtJ2wrj4ZfCHXtItfEfw68U+Iobq4uW02bw9q154dufEOk66IRcrYWsF5N Yw6xLJEkphlhvD5gRhFG7qQvh2t+F9J0K/8A7O1g+OtLv5SfJi1fwla2Uk53kGWOGTXN1whA/gZj u4ye/Uad8OfHXh3UIrGBfDd+Naa7sbvw9qepW8UWqWdkGuFe403UPIllgZUaS1uLcGWKRCUeORWW vTbfRfF/gGxs5Y/FuiWei69b3L2PgfxjrMWr2V55iqkkPh3xTYCRNHvGjd/s955mmTebABmRlJPw uIyHD8RUFisXwI+Fq+CUY4yph6FT6pCclTSqRw88Rh6adZ1KfPGjivbRqVFToYOpFQiuCVFYhKUs C8M4fG4p8i+HVRcorW6vaXMm7Rg1ZHz7deGtNtBC51LXHgusLazv4QvrOOWfJEltuvL5AZkIGQhc HPBwM17p8Dvgxa/EXVln0a416XS7SK6tdc8R3+l2+l2GhzTxxKv9jFL2c6nrZt5LkQoxVYWdbiUB UCseGfhf4h8e+I10PwNruo28vmQ3Hi/w/wCNbr+2JfDVpdTlJtVtZ3gez8T2LQsoSVAl2ZSYmT/l qe7+M/xm0f4faLB8Hvg1eRWcOlW11o/inxHp8aQzSTtLL/aVhZOqgRXclzJcGeVN3leZ5EDLtYj0 eGODOGOH4YvjrjjL44XhHKZKGHowlOVfNsapJxweAlUxM3FU7SePqVaUKuDj+5l7LEqU6WmFwmHo RljcdTUcLSdlFX5qs+kIXm9tXUcknBaaSu17x/wpL9lr+/pn/hVt/wDHaK/KTf8A9NJP+/kv/wAV RXo/8TAcFf8ASP8Aw5/4Jo//ADGa/wBu4P8A6EWH+5f/ACB+tfijxHovia6sjqMUnhm78RwYm0bx SjHwtfaxbottq2k6gbiAt4W8W29yAIrwIFmimgkk3pIVrjvE3he4t9JvrvVtOvVvLCSwsLLVre5f SPNhlDRQ6P4vNrdiKW5MexdO1OHfbXShY5WjxhNnwX478OfFzw7beBfirc2ra5qU5j0HWFlW2uNR vrFXt4Lya7WELY6ud4jXdvjvEBVkD43ZWmat4m+Gt5rPgzVbOfx54dtI5ILjwhqoSDxLZ6dcCaL7 RojSho9a0mSI5AtjImN26C2kU42zZ5JxRh8PxLjcbHNeHeKqcqazKjh4OvgMxnh3UeHzfJqf1j2c 4yk8ZQq5e6k/ZOrVjQrRniMbW66vscQo4iclVw+IVvaKKvCo435atJc2qfvxdO7tdqLTlN+5/s4T a9rdrq17q97e6hpOlTWljYx61B5l/b6tbxDeiXksQkkW1tn8rDu7I0u4EZFfWlgnmX1mn9+6t1/7 6lUfyNeeeAdC07w54T0fTtKgvLS0e2W/W3v5DLfQtqAF0Le6lLEySQxyRwjczFVt1XJCg16NoeX1 jS1JPN9bD1/5arX+hfg3whi+FeDuDsizLHyzHMoxozxNWU6lRyq16iqOnCVVubp0VNUad7e5BPlj flX3GV4SeHwuFpVJ887Jyd29W7tK+tleyv0XQ+Kf2zfh34hk+Id94v0GW4vNP1yC1stT0uKaXzRf 6VaJt8iD7k6PaxBwg+ffC5QNnA+IvEmvR6xd2/k6Lp3h+LTbOHTVsLCBonJty5km1B3Ae5vnmeQu 7AHkKAAor9lPjB4dtfFkfirSLixs9RlaWS6023v/ADvsp1exAuNMeY28iOIxdIgbawJR2XOCRX46 zeIF1ia50vxRb2UGqQXdxFb6lcQNEbaRJ3R9K1SaE+cLNJAUimLPJbhQHEkedv8Anx9Njw0wfDPi hn2c5fjpYDB+JmMxeMdOpBVKEsbh8Q54il7eUFPCKrVxP1mMIVJ0qtSq1Up0Y0ac5fE8WYCGGzGt WhPkjmEpTs9VzqV5LmavG7lzWTabeqSSZynnDv8A174ppk56+/H+evJrsvEeheF9L8PWFzBquqWH jFLl4dZ8KaraedE9tLmWz1bQtXtYRFc6a8BQguxL5yjHHPnhmzxz/wDq/H3/AEr+Gs3yDFZHi4YP GTo1K06VOr+5rU6qgqkVJU6qhJyoV6bvCth60adejNONSEdL/JVKUqUlGTTbSejT3V7PqpLZxdmn ui+ZfQnNN8388+vc8/4VQM2ep9MA+mTz9aj87Geh7jv+HFeYqPkZmg0nPXH0z69/XpTPO7ZPOfTk c9M1Q873/QUxpRnrn6+nt6d6tUull+YH7F/BDWRrXwn8DXpcySJokOnzMWyfO0uSXTnBPri2X869 ULkjgY/Gvlz9krVftvwmW0yC2k+I9YtCOuEn+zX6Ac9N109fTZckEcc/X/Gv98vB3M1n3hV4d5tP 362JybL/AGju3erTw1OlVfr7SEr+Z+y5VVjWy3AVWm5SpQv6qKT/ABPMvjdfx6d8JvHdzOSUfQbi zADlCXvpIrNAGwcHfMOMc9K/JCAXup3Vvp9hBNcz3Mois7KFd7mSTA2xxpgKThSzYwAm5iACR+mv 7Tj3k/wuudGsI99zrmtaVaF3lSC2tbW0kk1O7vL65lIS2so4rPMjuQoyBnJAP5rXmt2OiWs2j+Gp /PluI2t9Y8SbHin1CNv9bp+kI4D6fopb7zHE911l2RYir+AfpsypZh4m5JSxlf6tlGS5PQjPlt7W vXrYrFVpYehFr3peydGc6kr0sPGcZzvUnSpVfieLJqpmFJTfJSpUle27k5SfLH5Wb6Rvd6tJ7c+o WnhSCew0i7hvPEU6Nb6rrdqd9vpkbhkn0vw/OvDylcpc3qkbwTFbHy98knM2d4jCK0u4jNbudtu5 BM1ozOSz22W2vGZGy8bAq3ONrHdXNCb3A9t2MYzx+dWIbqRSojkmMyOjWggblZ96jIUgndjgbcHd jOa/iuvmFXFV6PLSWEwOGThSw8VzU4wbi5pqTftKlSylUqzvOpUUXeKhTUPlXUba05YR2j06Xv3b tq+rtskren6b4Z1u/u4PJ1bTIIoAs0er3t5D9gt9NRJJHunRVklto4hG7NG8QZMjdtYqG9CtfGKW 3w78X23hm3uRbQxaf4ZOu6hOZtR1O41i4uLzVLmwtSBFoNo2n2V4rRpvmddSVXlONteYW8F0mkwQ yX91ZLe+Y+sfZYYLp9Ru7y/RLHSm2MDdX4htLmTypDthZSXdAWx9aTfsq/E/xV4S8Mz/AA1tdBn8 F3Wmrq9wl9rmmrqGta7dmWK6v5GhXyWVbWO2jt1xEI1Rhsy7lv6D8N+BeNc+o59/xDrhXMM4zOll tX61SwcHjcc45hSeGpfV6cFKeFo4WUvaYjEKMa8p0oYdTnG6qe3gcJjK6rLA4apVqKm+aMffnaou VWS1io3vKXxXSjdnxFY300F3FJbxCR3H2ZrbaxS7jlURSW0katl1kU4IBHJyMEA19xeG7Xwj4Z8B +H7KO6j1GXQpNS+KfimwRY4pItH1DTtY0awuLW4upW869s76C2tlETNvkkDtmNg1emaf/wAE9hf6 RHey/EXU9J1++tV+3Wd54dtWjsbqVg9zbxva6jh4wQIw8bFSmSuAcBvib9l/4o+EJb27tYNM8b+G I9D0XwbJYaHGkevN4HXyLXW4ktLyLdJqioiXMRikY74So7Bv3Lw7+i948+FlDNOIOIPC2tjsPmGH hGhWpTwuYTwcG/rk6s8LgsTWxEF7TCYaOLhPD1HKnKWHUP3lSS9fBcPZ1l0alatlzkppWacZuKvz NuMJSkvgjzJxenu21Z8HXGivq2gTzaJPNqumeGZJ7yG/NjNHMNG1B4ZLu2voow5truzu90jx5KyJ dzTQsyIxrg7DVbvRNUstV0m/MGoabdw3thdwM8c0Fxbzb4pFJUFTuQEg9VODnJFdDqtprXwt8d6t orXd5puoaDqr26TNFLbyXVvFcLJaXTRSp/q3tGSQhlZWV2jZSrMpxfEOHt7TVLS4RtM1ZZbuKzSP edKvY5Vh1DTpJzGNipI0JjJJLQzwk5bk/wAX5xgXSrV60cJVyTibhqo6OMoKbvQnhKyo0alCbqqp Sjh2qeGcJe0qwdOhNVKjnUnH5OqrNtRdLEUHaaT+Fxdk1rdKOkbatWi7u7a+pPG/hvSfibY6d4q+ Hkfh6x1bUdFk8R+MfCV7dSWN9rFwszvqup6LeXcn2a6WC5huRJ9n8mSNmJkJDba8i8Iyz+EL/wAQ jf5Ftf8Ahu8nutH1Zgt4lvZXFpdo0tg8ax6sfsiX/kPH5tvcRzHeqozV5po+u6uttbWmn301tf8A h+8m13QJoJSl5BO6wLqFpZuDk+YkEU3lDIdrVxtJkOfc7TxJ4S8U2nhjUZZxpcuqS33h3xNo2qPG vhaLVri1uYRc6Df+RJN4Qv7mzvvPiKg6eGjlgKRiIZ/YsFm2SccZzQ4owlGlwzxbCFGpWkpQo4TF TqRpYTFVcNSUYrDVlWrVKmMoRqTjXpVfrVDDKtOrBenGrRxdZYiKWHxNk3qlCTaUJOK0UXzNuUU2 nF80Y3bOfk0a2i1jQ9U+Ger29rZ+IdU01rfTdftrSWO31dLphp4hkuopIJoxJLJLBazuJUe1eMtc NEHP6WJqFj4T0a0j8S+J7czQRQxz6x4gv7CwmvriNQ085DNEgBfJCIu1FKqOma/KSy+2+ENe0+HT 7LVLbXdNvYp77wf4lt4kGsR6deQahZLpd7bAJqV2s4neBlWOT7pgMm4o3QeLtd1Hxt4cvtT8aXs/ iGGHUpdT0bxLbskV94ft9UnksLjRdT0jys+Vbaktpm1Yq4hnaSzlaNWFfqHhN4oYXw0wPGeIw3Dc nxHjownHBrEVqOUYX6tTlUxcnT/eSwrqTqNyisPD6tW5aFZ4SlUhUq9+WZlHL4YuUcPfESSahzNU o8qvN21cW76+77rtGXImnL718U/Hz4X+GNOXU5PEI1q3lle3jPhuB9XxcRkB4J7mHEEEg6hZJFZl +ZQy818E/Gj45eIPibcpYQ2kGl+D7aZ5NMtOJmvZwrxi/vL50XbeGCSRVhVV8oSFcFiWrzHSRqnh yddO1SQW/hrxUsVt/aihbrRZQsjLp+swSsjRuLa7bMisokETzwuiliBzWpPrPhm9vNPuIYbSeKRr WcW8iXWnXckLypLLGySvBcoUYr8oI2nI2MK+U8UPHzxB8Qsk/s3HpcMZDXkqeNwmDozjN1ElUpwr 16lWdSth8RDlq0o/7PTm4zVq6o88uXMc8x2Oo+zqf7PRdlOME730a5pN3cZLVL3Vo/isYk08RLtE jICz7UZvMVFz8o3MoLMBkZI5x0Fbfhov5mtujQxyx6HdQR3UrMsFkdQurLTXvGuI1YQKkF5NlyDh S235ttc5qd+l7M939nitnlYtNFABHbeYCAXghA/cIR/DkgHkYBwJ9Fee5kvrO0kZrq8064trOzG4 i+nuJIUa1jCuALgwiRowQS7wqigsVFfztlUI0s1oumvrFpT9m4x5XKThJQcYuzU+ZxcYq757Rhd2 Z4MNKqt729ul9NPn2XfRH2l4Z1/w8+l+DNV0DWNL8W/GDR5r7w7peseI0urKw1yy0m5huptJ8OXl 9cbLbxJ9j1aBbK5uVje6hjZEMcrAH2Px98PvGHji48QaE9nceEp4LfUNf8B+I9BvJrTSNTl1CO1h 1bQ/Glk1yY7bXftTRSRSrtG6CR1ychvzv8PW7654U8R+Foyses6Lejxrp9vteO+uodJspbTxBp9t MoI+2rYmK5SPAJGmSYJOAPrP4MfGHWviF4dXwr4i1G11LxJ4XttUews9at45LfxXpcWlXMtjaSXc ixrBrEFxHEN077JoLTzGw8ZkP9ueF/G+Q8TYWjwjxPgamCp8SYag8LTwdSNPD4zEYOEsHi6GKnWV bFxzOUVRxUcTDEPE1nhsDaDq2jjfrctxtDERWFxEHD28Y8qg7RlKC5JKTd5Ko1aXMpOT5YaX+M1P wl4n1TTNJNy3i7UfGWgCz0PxvpEN3fX+m63BpMbWlrruk3FlEYku3mtEEpLNLjT/AJ2ikuWjbzfU rjWPDt7Fd62NatPFlhpGmz32m6lqz3mgW+qSQLBLqOvT6Tqc9yIbi3LSSQLbrEGlUPKFcJX0lqOp SeKbSTVfDXiXWtF8b+GlMPibwZrd5Bpt7rtkz3FnDfWnkNDDHczrcWL217vS3lxA04Dsc+batdfF PwF4b8aX8/iVvGOraf8A2DdbvEOpWemXXhwyO0qbbC3uZLmEeTJaCRJLhLa6aIqkUiMoH2Wf8MZf QSzDCTzCeGpUZ13mFKnQxtBrDYerOFWvQjXp4r+0eahUp16cWo08TDFVa1Cn7enVOnEYemv3kHUc Yx5udKM1aMW05Lm5vae61JdJczkldMg+CeleN9R1fWo7rT4bvwfeaTeW2q6u8i/2fpupq9pqemaT ZXtltt5447W3kiF7aCWVbmYPMY5uDh+K0mTxKut+K9Sv9QS81OCLRfDNlrGq+I9EhXTooLPTZblL O5hm0HVd8UKvOkjiQznz7dVkG7y/TvH/AI2v73T/ABJonjCfRdXl1O1i1jStO8c6FceHrmxvLmKe dovD2p3ka2e6RLlRDGm1OA0gzmsX4n6xqFsbK+0LxBqciLdW9/NpusWmnOkV6lzcPJcWy3Clol+2 QONkvneckSMkzJHx8Ri+L8mw3BFKhh8Nj8yWTV3iFUxLounXhUknSdOjJ0alSFBOv7ahOUlGtXlJ uvTqKEOGWLpRwaSjOoqMua8uW0r2tZe62opyunf3m3qmkdV498Q3ngXUk1Dw3pEPhnPkXek3EtpB qK+H31F5fNvtNtBGlvo8s09sSwczy/uln8yOR2Q8nok3iz9oWSz8HPeSXXjO1u5Z4rwWSWnhweGC qG8+1Jp0Kw6bNDdt5od4P3rMEjdZWIfkPBl98QPHXiXT/D3hVf7Z1vxBLKbvz49lrbRs0p1CTVY1 BibREjk8x/tKypgDYobah+wNZisfgnoc3w88GXXh7w34n8SwGTxt8T9Xu7Tw5Y2kzQuDb6DZyF5t odnW0ghiaODmZyJGBHz+QYavxvLNeIMbjsZl/hjQm6eNwijyQxdeoozoYPAqlOnhXjG1TcsVKnTo 5fQUamIqypqFGWFGMsa61ec508ui7ThbSTdnGELNQ59ryslTjrJ2tE4f4hfEvw/8BfDdv8GfhfO+ rapGhHjvxWbqaG7a4mH+lafp13aybrC8AZwgiYLZK2ArTvIR8/NoGvfFg6fc6E2kXTWUN0viHW9S jttGn0m3soEmOreNNe+zpA0DQMEW4d5JZ57dyBvk8scvcHwB4cmmd7m7+JOs+a7tIftmi+ExMxLN LPLI/wDaGunzMsSPsaPnlnBrE1rx14l161GnXF8tjoibUh8OaPEukeH4UR1kRV0mxCxzuHVG8yYS yllDM5PNfn/FfG8M5xVSjxDUhDIMHThh8BkOV1ozo4HDUJRdKi8cufDqUlG9bERjjq1Sc6spRoyk mvPxONVaTjXaVCCUYUKTXLTjG1o8693prL95Jtu/Kzs/+FbeFv8Aosvw9/79+Jf/AJS0V49kf7H5 H/Civz/+3sg/6IjB/wDhVmf/AM2nD7aj/wBAcP8AwKr5f9PPL8T6Emu4p7xWgSW3tkkVLWCN/Oni iVsxjzAq+bcljuZgF3OxIVeAPrzwd4ks/iXe+EPAfxNuLuLxHpOsWM/g7xRbl/tF2bcRTS+Gtbub UgPf/ZljImUjErAOdwZn+XfGOkzeD9Zhn02Dy9JvoGutA1iG7GqWGpWVyhVZ7G+VNi3UQd45FDNJ DNET8p2mu0/ZwY3fxp8BxO7OsOoX1yodmKq8WlX0u5VPAYyIv1wM10+GOIzThzxEy3hHGUViaXFO a5fl2Y4OtB/UcTh6uMoRjKdG1NzlB1Pb4KtTVJ0JRhUoynCo0u/L51KGOp4WS5liakKdSLXuSi5x tdaXtfmg1bl0a0Z+wpZugJAzkAdB/kVr+H2P9uaSSSf9Pts5Of8Alqo71jc9v54/pUllqthp2ueH 4bu7gtp9R1aC00+GWVUlvbpFe6eC2TOZZBbQTO2PurGSeK/3EwVSlhsbga1eUaVOFeiryairyqwU Vd6XlJxjFbuTUVdtI/XoOCnTbaWq773Vt++xveK2YeI9XGSMXshABP8AskH3Nfjr+0l4dj8LfFvx FHBGIrTXPs/iK1UAhB/acZa8CgdP+JjHdn23V+x3jJAnibVV6Fplk/77jRsj8/1r4D/as+F2qeMd S8P6/oF5pR1Wz0m4099Eu763stQ1WGO8NxG+m+fIouZka4dfL7mVQDkgH+Z/pr8BY3jLgHOpZXl8 sxzjhvOFi6MKaUq0oSq1cNXhTjfmm3CtGfs4pym6UVGMpJI+f4twcsVg6vsqbqVsPV5klu024y/B 3stXZaXPhKPWxdWS6drDz3MNtA6aVdDM11pjKGeO1i81xu015MhosgIX8yLB3K+H5+AOe3vxnrnB q1qHhrxRpMbTan4d1yxhVnVprnTb1IUZGZXV5vJ2oQyOMEg/Ke3Nc/8AaM87s8nOCevfp0r/AB0z HC5lTnRo5phalDEUYqKdanKFVwVlBS50pSjBLlg3dxjaF3CMYx/LZ86aVRNSS6qzt0vfXTpfZabW NYzD2/U/57U0ze5HfHHvge/esoz+h/8AQs0eeCBnr+NecsPbpcg0TNjr3znk9/8AI/Kmmbqcg5z0 6/h6VoaN4Z8ReIg8mjaTc3cEW7zbw+XaafGQDkS6heyRwIwA+6ZM57Vat/C9xcXUViusaJJfTv5c dhp9zc61elwcECHR7KdTg9TvwAMk4Ga9nD8N5ziaWHr08tqqhinalUnF06dV3S5adSpywm7tK0JN 6q+6NI0aslGSg+WWz2T9G7J/I++f2Kb4zeEvGtiSSbbxFYXIBxgC60zyzjnPW1/SvtSviz9kPRbb w8nj3Tx4i0zWrzzNAnv7XS0uni0qTytTRbe5u5YxHNeHDB0iLiPZhm3EqPtDzE/vD/P1r/aL6NOH xWH8EeBMLjoqGKwdLF0pxjOnUUXTzDFxUXKlKcOaMUoyjzc0JJwmoyjKK/V+HpTjk+DjOPvQUk9U 9qkrK6bV7aNX0ej1TR8H/treKJ4YfBnhG2unjiuV1LXdSt45GRZ1ieGy09bhVI3oJBeMobI3LuAy AR+f/nZ789evtxXvP7U/ikeIPjH4ghikD2/h23sfDsO1soHtIDcXuP8Aa+23dwp94/avm8ze5655 wK/y7+kTxA+K/GbjvMI1XWw2DxjwNH3m4qngIQwj5OijOpRqVdNG6jet7v8AOs9xP1rNsbUveMZc i9IJQ08m03879TX8/nr19wf0rpNIeOxCalNc2kF2ZfJ02OdpWaCTpJqjQ26lysJwIhxukOeVQg8L 5/Xk8AnoP6Vt6JbW93r2laZqdw9jbXmo2Vpd3XlNO1rBNPGJJI4o8mRgr52rk/NgDPFfkuVUZrG4 d0qUalec4QpucuSnGrOSjCU5NpR5W7puUVFpTbtFp+bTvzxsrybSWtkm3pr+Turb9D60+D/wJ8bf GrxJb6Z4bcWHhbwzpEcWoeL76Hbawanqtm9xOUMMSNqGpLPeSCOEFjEtuA8gGWP6zfA/9nvSfgdp zWGieMfF2sLcMsl9aaldwf2NNcbAjy22lCFhZkgDBSTd8oJJ5z8++HP2vv2ZPg1pVt8NdAj8VfYv Co/s+8urXw6FW51CN1ivr24M92klxdPcBmclc/wj5VFfWHwx+Nvwy+MFk134C8UWerSxKGudLl3W WsWo7tNptziQxjoXQOgPG6v90Por+GX0b+DMfgY5bx7k/G/jXTVSVeeHzVSnhJuMadXA5Xho1oKr h8NTpxw863JWq1lSlUm6dOSpQ/YeHcvyHCVKfssbSxmb2bbjUu46JOFOKeqiko3tJu13ZOy9WzjG fUf/AKqU8EfRe+OwzzSUrdeueAPy4/pX+gx9xd2bTvqeNfF34CfDf40aY9t4u0WEaokTRWHiWwRL bXLB9uI2S6Uf6VAMLmKXchGQNp5r8evjL+zl44+C9xqeg6laJrngzxDdJdeFvF1okht7XWrQSmys NQ3P/wAS27ubV57Z0f5ZXlhdXYRDH72dgOxAJHHHYn/Pv0rJ1vRNJ8RaXe6Jrun2mq6TqMLQXthe RLNbzxt/eU/dcMAyMpDIwDIQwBH8seP30TvD3xtoYjOKeDpcL+IKpzjTzbD0oReKjOm4Sw+Z04r/ AGqjUhJwVdr63h3y1KNRqn7Kfzud8NYHN4yqqCw+OtZVYr4r6ONRL4k11+KO6eln/LSbl433oWhk STKlWZHikVjgIc7kZT75GPau38Oa3Y6ilz4e1d7eyg117aO51GSMeUbm2leSx1FsOv2PU4TLOomU hJ4riSGdSzLKv2d+0r+yvYeB/HE+sad8S/BHhfwv4pE8+m6d40vr231CxZ5FN5ZRyWthN9osUnYm KR8MRIUO4hs/MEnwP0XqPjz8F2JYcDXNaXjuSToY49q/xFzzwM8TvDXi/N+H8wyrDV8Tk9epQr06 mPy6lTxNCUeVVIRnjY1VSxeHneMuWE/Y1OWUUpSgfj9fJ8wwGKqUJ0oylRbUk500pR7q807Ti9Ho 7PzaOk1XS/GOi6deQ6jp9xqNvZm01fX/AA9czpJI2liP7LN4v8I3DALbadMz2tzFLa7JrO5JEoaL g7mi+IItHntJPGGp3c/hnX9FXTLrxFaeU97qokM+oaY/ibRbm3ZIdYtBeW2WYSS7beSR1vIJEdOh 8DfDtm0mPQoviv8AB7X9b0aa41DwVdr4rnmniLiFL7wvKt7bwNBoVxErybUZ0WdiDC6yurdbN8Df iFf6dqupeFZ/AOtS6zDpmsQaBZeKPD832uVyLK8sG23UUHiPQmtRdiBriKKUeV8rNIS5/Z8l4B4w lDC5zkGS4/HTVLnWGjOnmjh7KM5VqFWOElD6wqjaoOhRcJyjiI18NLD0sVi8KvUpYLFNQq0aM5tK /KmqtrXck+W3NfaytfmUo8qnKJ806gNS8MeJVMdlqmj6PdQlbaaC4h17SLqyaJDbLeQyvJpmo6fO 9w0yokkOVuWACOQDm/ZtK17QJtb0SGL+2bSGb/hKPD+pTz2OnasovlsrbU9Gsb+KXfMy3FrFIsNy s1vOEZRtckew6v8ABn4o3WlyaTa/DXxB4etryKJmsND1v+3fDUd9aNEGVY7CW5kjtXm82URTvJGv mA20sJDxy+Q+MdI8RaDrGmWfifwh4v8AB88FvHa60ms6JfzLNHNaf2XcbdStoo11C2lt0WVS0Alj acqsjjp8DnfCed5CsZPM8gxmGyfENqH1vBYnDRoVsRKN5YeriMHhaVB0VCM7VMLRlisPVq0nTUac nDhrYerRUnUoSVJ7c0JRUXK1+VyhFRta+sIuUW1aybPMdX0mwuMz+HLO9jijt0fVbTU72FrrR7kT NDcFpCkaz6WJFCpKQrL5ypKA+C/GTK1nK8M/mRTwsDvikSRMcNG0UsJOMnDK6MeBx1zXRTeKdV0+ W60t7xbW3t57mxurSILNFIjGe2nk2zw7pAVIxtZdrJ5gjDHIwVvrUTXAv4FuTDJCYtRtpC0UcSzA LHLaPlLqzKycqAsikABuor8VxuGy6vWhUw0vq2Jbaqp04U6MZRTdqcKblKHM01d8kIyUtKdNqMPF mqTfuvlez0SV12Sva/yV10W3pHhzxNdWnxA8GX15M1gbu60+01O5gumksru01s/Ybq/VgT9nL6ff TGRQcxzq77RllHS2V7f+BtQ8Q6RqWj2et6vo03izRrXVru2tL62F4unajHMLuaGLfM50yHyliMpj C3Uz/eXI8X8L20t34u0bTYIbW+87W7EKN5FmYBcCZ33eYAkYt/MY7jlQrKSCGr1STx9ba78VRO12 llFZ+MNW+wids2GuJe3VzpenC+ihi8uC5itrhAZX3RSxl92HOX/TeE8ffAUa2JzCeBxrzaNPDyko zi/bU6MMY5KcY0nVpKjha0JrlnzpqMlVqQUu/DVfcTlUcJuqlHqteVT30vHlhK+/ndo+hvhx8UV1 Txl4cthLFdzyaDb+GtN+y2q2t5De6DpOnGXQhffZGLWk5ikBt5A1tJGsNzG8M4lRtiaDwD8StNOo 6fr0/hbx7I1rp2o3cFnqHhnU9Au1vZpLCDXrW0vH/s+wluLXMk8Ylso7mJQY4zKqH5gg8XweHdQ8 UyWNkdM1rw7q0OpvBbCGyii1Oynt7Vr+0sREBK8Krf2t1AVaCSOUTpsJIr6Wi1XwJ4kup/EWmJq1 jNqHiKSF7XSY4dQ+16DrNk32XUdA/tC5WaSze4mkkeG2CmzvBLbz20q/Mf6H4S4mlnuXV8mzXG4L OJUa9adXD4lVqcpU637n2uFqurFwxOFqYSr7STcnChiFCjZ03Uj7uFxLrU3RqzhVak24y5ldSsrx d1aUXB3eukly7XPEfEXgW303Wdninxf4LmnmmsdQsrzWdLkuLDVzPPdwulx4t8OWOy0lZ0Lh5/I8 0lWkihcc89pPw81PXvGEnw1tNIn1XUJrgXcN3aLdPbLpeoRRTyX9pLcytb6XocaMo+2GSWQyoVAY SGOvWfEHwZvfGVzBd/CrWI9Vt9QtdOeZ9VnW1ttOtW1Gey8Q2uraadPSKGGPULaa5mgfaIWWWKGI bo0P0hdeMvhh8NvhxqGl+Arzw/aa75F1okb6dIFMuvR2a3V28cUssktrZMxubi3hcJG/lN5SMR83 Bl3hjl+a4/NMZxFTo8McNZW/rKxUsS8ZVzfDv2jqYXBVa05UsRUny0mq9NQWHU6F6FadeZFLLadS pVniEsNhqfvczlzyqxd7xhKTtJvT3lblvH3W5M+fPFOueG/2Z/CWoeA/hrNba98SdTs1k8Z+NFe3 M+l25dFSKC2Zi0MCPMBFbrnBIuJ8kgV8JXl7eald3F/qN1c319dSPNc3l3K9xc3EshLPJLNIxZ2L Zzk/0o1hr86pftqsk8upPdTS3k11J5txLPIxd5ZZc/vCwbO4cENkcYrPBx9f5da/mjj3jTEcWYrD YLC4FcP8NZFGVDL8qpSfsMHSUndtNKVTE1HeeKxFVzrVqrlKUrcsY/O47GSxUoQjT9hhqGlOkvhg r+msnvKTu3K7v0JwxPc9Mde3oOelFQ7uMe47DH+c4/Knbh23fkOPw/D9K/PnB/18jhJKKZvHof0/ xoqeWXYD6K8P/EfU9C8N6r4TkstN1nRNVuorw2mrQNcLZXA2pdy2BGDayz26qrPGyujxrKjBwc+w fs/XXg2X4veBbnRk8V2Oq/b7gPpk8Wn6ppYElheR3BGpRSQzQWSQuzb5IXYbQrE/er5s8R6n4XuJ rJPC+l3unWttZRxXlxf3z3dzqV+T5lxdeUQFsrcOzJFGu4+XGpcly1dX8NvEt54UbxF4ssGVLrRb TQtsu3dKkd14o0g3McL9YjLaW88THvHKy9GNfbcF59icq404U/tXF0c9yvhCvRxFOpGm6ro0Mvm8 xnDCVZwoV04uFSMVJujzWtGpSjC/o4Su6WLw3tJqrSwslJO1+WMH7RqL92WlnbXl8mkj9zxM+ex7 Yxxn169a8n0Q2Xjbx/pfjaKWG+0rweNUsfDksdzErw6nLcSaXq18sVtcSLqGm3MELCCVhHJC9lIm PmIrkfjl8WdL8I/CaTX7LVksb7xrp0Vj4Vu4oZLtw+sWQuXvoooWDYh06R2D9EkePPWue/ZI8Inw 58KbXV5Lh7q48XX0+txu8c8Rh09SbWxgRLjlVJiuJiR8rNdbgTnJ/wBZMz4tw2eeJ/Dfh9gcLTzX CYHB/wBvY+sq1Nxw0qFbDyym9K0nUc67jXipKHK3hsTTlzU01+mTxUa2Y4fAwiqsIQ9tUd17vLKL p6a3u7S8vdkndH278QMp4gadcYvLKzuAcdd0IU89+Vr8t/2xPHb2Pjfwv4fSKz1C0sfD732qaXeR 74Wl1G+k+zSRzRMs1hfLBabo54JI5E8wclcg/pf8T/EOj6D4U0bxrr99DpukWWhsNSv7htkUX2Jx Eqk9WkaR1RFGSzOABk1+Bfxa8Xa14v8AiD4k8R65A1pc6pe+daWpdZYoNIRBDpMdrOjFLi2FjHDt lQlJCWdTzX539O7jaGQ8NVOHcvrtZrxfjcNimo2fs8EksY51VZxUa9ZU6UIVFy14xxMUpKnUS4eN MYqFB0IStUxU4y06Qsp6+UnZJPSSUt7M9x0z4oax4mijitPE/iOO+ht4obZLS7WLxbpUdumFjjhj Mdr8QtIwMvHKi6kq5ZRJhi3M6z4kurpLOHxt4N0HxpYXc7W1h4u8JW3/AAj/AIgnuHKhoHutLtVj fVUwN1pf2RmDfKRjDV83LeMrK6uUdGV0dSUdHQ5V0YMCrA8gjkYzX6HfskXuma7PqGqX895rni2C BUvr5dD2Wel2Cu8dhDrWtTybdX1iZot8DxxG5iij2vOV3Afwx4c4rOvFjiDL+Dsdncsvx2OvzVq/ s8ZhasIWlL2mX4uUqeJrJK8IRjKrFXnCthsJhvZL43ATrZnXp4Odf2c5/alacWlq7056SemitddH GEbHmEP7LnjjxFY2et+EYbq10+/dwmmePI4/DevWChVYNKkZljvrY7vlkjEbt3hXnHX6T+yH4402 GW81C88G6hqqlVsdLur7UzpEOVJa71F4bANeurYEduu2NjlpXZR5bfohP1VdxLKoBz9wtyWAbP3s kZz61zPinxjoHgnRrvXvFeo2um6VZRGR5LqULPO4UsltYxZ33V05G1I0DEkjgDJr+yIfRQ8Gckp1 s2zVYmCwdLnrVquLjRwdJxh+8xCpTjKNKKd6ijUqVKdPaMVFRS+s/wBWcpop1arlaC95ufLBaayt 076tpH5QeMdKl0PWb7SfiR4rN9f6LcyWY8MeFyt0IRHnYkcjwxWOg2zIUZVWOWYKw3QBq4e+8Yzi 2l0zQLODwzpEyGO4t9Okkk1DUIzjI1bWpT598pPWJTFbjPEIrv8AxtYWfxf8Ua/4z+H2spqeqa3e y3914F1dYNI8V2o2qgj0hTcG28R26xRpgW8ouecNATyfBbyO7sLmayv7W5sby2cxXFreQSW11byL nMcsE6ho2HPBA6V/njxnRzDKc0zKpk9KUOHsbXxEMJmUKjxVTG4ZTap+0zFTqP2kqDh9YwtKdBQb 5K+HjNWPg8VzUqtV0v4E5SUKl+ZzitFepd6uNuaKattKKZ+i/wCwzua1+I0mPkM/h2MHtuCaoxHH sQfxr7o1rVrXQNH1XXL5xHZ6Pp17qdy56CGxtpLl/wASI8D3NfFf7C9qV8GeN9RK/wDH34nsrVTn gpZaWsjY45w15XfftgeNR4V+EV5pUMwjv/Gd/b6DCobDmwjIvtWcDPKfZ4I4m/6+wK/0p8Hs7h4f /RXyjibE2j/Y2WZljIRlop1Z43GVMNT161as6VOPdzR+gZVXjguG6WIl/wAuqdSWvVuc3FfNtL5n 5Ya1rlzrusatrd25e71fUb7U7hiQSZb25kuH5PQAyED6VjGbBPcfh+eazjP/AJ+nXvUDzEE89c+n AFf5MVfbYqvXxNebq18RKU5yfxSnJuUpN9W222+7Z+ZNuUnKTvJ3bfds2kuDGUlRhvR1ZQVDYK/M GIIwRuA4Oc/Trci1S7S7N2sri7d5ZVnVtkkck5YyTxYwI5iWJUjoeQOlcz5/+HA/lk+lOF03zHqW GCSAx6g5BJ4Pv71VONSm4uMpQ5WpKzfxLaX+JdH01sNNrZtHbavqVjP5jwee93eG2NxLIwMcKWsK wvEOD9qkkljWUy5BG4oVLAsYfD/irXvCmrWmu+GtX1DQ9YsJBNaahptzLa3MLoQflkicZXIGVOVI 4YEVyktyjRWwU/OkbpINpGD5sjLyWwcow6elQCYngAknpwM/hg12TxOMhj6WYYarLB4yhKFSnUoO VOdOomqiqQnBqUaim3LmTUoy+G1klTnNTVSMuWas01o097prZ3/E/Yb4H/8ABQueHSrSw+Nlkt8s bGBvFug2+y8ghiiJSXXNMO2OeaQiMIbYh33FihNfpH4G+Kvw7+JVil/4H8YaH4ghYEtBZ3sa38BA Tetzp0xWeB1LIDuQDLDBORn+W1dVUaJcaezlW+3W08EaoPmQwzi7kkkBG750s9oYNjadu3nPcfDb WrrTdZt1068u4b+6tr+SL7PM8YjItp4r+OZYcSSJJpRkZAjhlntImXtX+hngz9PbxP4WnkvDXGeG o+IuUOFKmsRiasqGa05c/s+R4uKqxxL0Tj9ZoVK9RyTlWhHb7rKON8xwzpYfGRWPpNJc0m41U72+ JJ83/b0W33sf1NbW9CPwNcD8Qvid4L+GOjT6z4w1uz09IoZJLXTzNE2q6nIiki306w3+ZcSMwAyB tUsNzAV+AKfGr4w+DfEV74auvih42m07TNT0wLfWmvaolrqFle39reQyWdncyOz21xpbh4F3qwiG GIy9eVfFXxjqviXxhc67dahfTarazXVhqMd3eXGox2lzYXVzp8SwPfD545bKKF2Ug5Z3BA28f0Bx f+0Uy/CcOZhPhvw8r4bijD1fq0qWYYylKjh5qVSFWoo4em3inRnDSkqlBVVzSVTkp1EvdxXHtOGH m6GBaxMXytTmrJ7N2ivfs1tdX1d7JnvvxX/aEu/i94z8S33jiK5tdJttVS20XwzKkUcOk6BGs8Cr JP5O+/Vp0s5LqKRG4kkuLcxyIK+W9W06wZxLobTs4gnuNQ0mfa82mGKVg/2O7Riuq2PlGNldP3gV sOvyljY1HWbO90C5kuE+0XmoT2PlyCaeW50oWNs6SfaJBCovYbq5kmWMSSM9uiADduyeJttTuba7 trmKYw3NtdR3MU+4I0U6sp8wDGF4RQcgg45GMiv8t+OOLMz4xzavmfFGOXEGY5rUqYmrjZJ/W+av WlOzndR92moKlRX+z06MvYqnSqU4qh+bY3F1MXUlUxM/b1arcnN/F70r+S0VrL4UtEote7q6Vqx0 u9tb+OSeOezvLe4h+zOsM26B/NU+aVO1fMWM9DnB5HFfVGj+NLXx5oGoatBZWui+MvDM+m3M+k21 +1jp2paXaQXCb/DlnsaNr5ZoJp5rKXctxI26ArgrXxrdXW+ZphIWeUtLLkbNsruxkUDoRk5yMD58 ADFbPhvxNeeH7pru0TzJornTLuMmWSJEOnXv20oxRgMSKHQ5PPmFRndiubgfiivw3i6uDqz9pkmO U/bQ5IykpqlNUcRSfLOUK9KTUoOL5XrCopRbtGCxUsPOUG70Z3urdUnyyXVNbr7metah458UQ3Gv eHLi7vtDlltzqeh3Fqb3SL2ym05ri/srdTDdAyWstlJexruaTY0yhXcIK9S+G37W/jzShbeF9e8T +INQ8PTWVopm1HVDqF1pVzpkbyyvZTX1rN/od1DAguIXV13tmJo8sa+ePGN7qesanL42tIZbvSLk JuSFlkk8NOJfOm02eKF99vbK73P2eRwsbRzMq4MZVfN2mgghvHhumS4+0wpapF8xms545pJ3klIz G0e22XAwWMzA/dxX1uF4+404Oz943h/iPF4SnRlUUZe1nTji8C5TqU4ThzRhVpzjKqov4kpU4xca lKmqfRHH4zCV+ehiJRSur3aUoatJ62aevnqlo0kvrrxJ+0qLi6uLLxR8LfhX48jLhpb7V/DR0vWl V8F1e80qK1LhkdXSVWcMsoYMcjHL3fi/9l/xDN/p3w38deCnEMMkmo+C/EkOoWtvLIqmYLoHiQSm WBJOMpdruHQAGvC7rXX8SaTYaFcNZG/0KK/n0/VJspfatFcst3Lpd1dMP3zxlJTbK3cmJTnYD580 5ODk5I56+uMe3FaZr4m8S4vEOrmP9n8V4PE8klLMcqwOJrQajerR+tVKMsZF05z5JVI4pTnGMakJ QjJIVXMsRKTlU9nioStZ1KUJNaXceZx59H15ruyadmj7S8GfDP4S614p07WPAPxj8O37W97b3aeF /Hmm6n4D1SMGdI0tYNRL3VhK7PMsYaScDLhgCRtPnXxG+BHxd8Ja/qXiK98C6lc+HpdZuNTttd8N yL4l0M2U9697byjVdGeZVT7OyEM+xiMNjNfPtrdOsM8EaxLLK8c4nY/vUS1iuS8SOTgK/mAkEElo k2kEc+1+GvE/xI0+z0LXfhn4l8X2Ouuw0m60/wAPXN7LPdXv2hhBFHZWHElt5EseUmjZFDxgMQ+B 34TOOC+JMr/szGcF1skxeGqvFqpkeJlKLnJwpTf1HM/rdXESS9nJUaWY4aDV1DlSsrhWweJpeynh HRnF896Mm1dtJ+5U5nJpWdlUiu2xHrEGr+JtR8WeMJENpqM0Da/c22oeWLi+s5zbxX0jL5QjurT+ 0oJIljURspYK27ccev8AwV8L+LPHltb2OkQ6RN4e8PX9jeTatqbXcGn+EbVb2bV7iLasEZvfEFtd RMFXzGWWG5InQJEjL7jda/YaR4Yj1P8Aax0PwL4lu7nR47Wx0/RbBPDvxHk+0nzr611zX/D7xwbR cF1MBhubhmQuTDnIwNU+JngT4l22i6N8GPiVpvwah0eVTZfDnx3oy6Ro2pugKpZnxXprzWtzbTK7 rMl3HA03mHz5m4x+w5bwLkGR51TzfM+K/reOxtKVWpkdWpQy7P8AGzr1ueSxFHF1/qOFUpOM3QpY 7FYzEKEPY0KdSUZw9angqFGsqtTFc05rmdFuNOvNykn70Zv2ce/KpynKytFOzOp8UftH+BvDN4fB fg+Sxuf7dW603U/HjiKWCPUJ2uUe48hGZ5LFb2U7SWMcPnfIWCMw/PnVidLudU0PVJ7m31N9Zzq0 Kw297YwvAiyW2qW90ZPMluDLNJuT51MMx+Zsitn4jfCn4i/D0eZ4n8H3Wm6Vc3Ml5put6Z5eq+Gb hbjHmJpuu6c8tvcWRKI0SiXcgyDk5I8qa5nu5Q0ha5mkWOJWYs8jMqpFFkjmRgiIo65AAr8g8UeP uL+Isxhl3F2USyfGZROccLhJUKuEjhcPiIRVWj7CcYVZ+0cIyjWm3UqRnU9rKpen7Pycyx+Lr1FT xdJ0p0X7sOVxUYySurNJ6taN6u7vfS16/v7y8ljN7cG6e2hSzjlKrua3txsgG8IGlUIRtLZbHB9B ViWSWRY4o5JpHwEiiRpJXIzkKiKSx/WtfTtNspJ7m11G6ityhEbX8dwjWtgYir3LzRqC10xUiOFV wJZTsVhy6sutVisUNloRngjV5RcaozGHUdSDYVATE3+h2OxQVgVicuTK7nAX8mngpSX1vG4jljNt Nc3PWlKLty8snfbX2knyJac0p+4/McHbnqS3+cm1pa367fPQ6Xw5oN3FJqV5qukwxwW+iai9omty RWUU2oSxrDYrFbXUqPeSmWQBFQFgzBhgqDWbrOgf2ZJZXOZE0zUESeMho7q4hhVnjvPLlhbyb0Rv FNsZZBvXYXVCxA5mG6kMd3mVROyxSieaYLMBbyq4jgkdtxlLMpwDuxHnoDXb+GNC8XyJC2jwpE+r K5C393ptrDJaxo8sjzWOrXSrc2rxI8jyNE6rHDuBwTXrYShh8xo0MvwmU18RON589Ne2qqUqij8F OnTlNTSjBQc1ytqXPpZXBRqKNONKUut17zvddEle+i1elzqNn7Pn/QV+Lv8A4KvCH/yfRUP2SD/o NfBj/wAB5v8A5W0V9hyR/wChDk//AITvy/6j/wCvy6/+4VH/AMBfl/f/AK+Rwf2jpg59QcD+ldto mr3Nr4L8c2SCA22py+FoblnhieYGDULu7hEUzDdEuYXyFOGxznAry4XIHRuB7n/D3rrdPmf/AIQv xNJkeWut+Fo2+8CGaHxA6Y454Rs59K/MMkpVqWMrzpOVOTwmPi3HflngcRCa9JQlJS7xbOCi2pu1 0+Sf3OEkzu08SeLvinP8NPh5PctdR6K0PhPw3CoYtHFq2qBmnnyx8ySOKSJA2OIbJF7HP7e2cWj+ CvDFtatNDp2heFdDige4kYR29ppmj2So88jfwoIYGZvU+pNfkX+xh4eTxF8arHUJo/MtvCejanrr FiSqXbImmWBzj7wmv2Ye8We1fWn7b/xFbw18PtL8G2Fw0N744v5BfbHw40DSDFPdRnHISa+kskPZ kikXnOK/u76PGYPgPwk8R/GniGU8yzGt7LB4d15ylOrRy2hSwuCw6qO7VOria9PDO1+WGHho+RI+ 2yGs8DlWPzivepN2hG+rapxUYRT7OTUfLlXY6fxb8fIfiz+zn4u8W6Zokd7oHw0+JlrpWueF75i6 +I/h54jtTaCe/HJsr9rzM0EiYNtKkeCSrZ/Pf4naDdaLovh6+8P358Q/CzWZrzUfBWsTQRSajozz lRqXhPWLpU32V7bz43W7N5cjL58Kje+fWv2KNUtfEutfE/4IalKosvjF8Ota0nTUkYCNfE+iwSap okig9Zi8UoXuSR3rl/h745X4V6XP8KPFN3c6UnxCuLi61bWVZWufh5dZk0vw5qtrayoyid7i3Nxf hgHWzeHYQ2TXw3Gec1vF3hjgbirjDNfqb4hy7GZVicyjCMVhM6ynHx9jhZ01KlTlgcwy/GZOpUJ1 IU8PWp/2hGdOFDF+24cZXeaYbBYrFVORV6c6Uqn8lalNWi0rLkqQnSvFtKLXtLpRnf5lM5X72V+o A+nUD3r7f/Zw+Meg+AvBVn4ZsLL+2/HHjD4irZw6dG7RxWmmPb6RbDWdTnjRmWyhV7ooijLtG+Si K7D5Z8UeLfij4O1/WPDOu+JNWTUdNu2tbpJZYbmC5QDfb3du88LLNZzQMksLjh45gRwa7H4W/ETx RDL4z8S3F9p5Twn4F1y+huJdB8Phn1PVBB4f0q1luYtOWV1kutV+4H+fyiGym4V+O+GWPXAnHVPE Zdj8XgM3oxxGFryr5ZQ58JSjrjZxjPMJRjWpYelWV5wlGDcm4SseTl1f6jjVKnOdOquaMnKnG8Er Obt7TRpKS1Tt2PYfG37Y/wASV8Ua3aeFrvw8nh2y1q+g06caMJLjU9Nt7qSKCS5mnunMfmxJu3Rh GAYMCDXmer+KJPirayS61rmr61Lame8hbVJ2v/Efg9pSGuWihhVV8TeDy4zIYIheWajeY9gbzPG7 nx7Z6k8bat4K8HTGOKKEvpNheeG5nWJWAdjo2oRxNKd2WYxHJAzX1F8Mv2cD8RPhvqPxL0dvEngv V4Z5LnwfZW13HrJ1SHS95vLq0EkNrOjSSrJFbfvyXeA/MVYZ+ly/NPEvxbznNMpo55X49wtanicZ PLatTFUo0cPTtObpwrL6rBRbp06VOWIbnUdJUpQxKo16fTSrZjmtarRjWljotSm6bclaKs3a/uK2 iSctXazUrSXy7rOk6r4bvYIr5DE0kUd9puoWkwlsr+1ZswajpWoQnbc25I4dGyrDawVwQO+j+KFv 4ltrXSfijpsvim3tIFs7HxTZyx2njrSIUyIgmpyDy9ftIweLe9ViQMLPGea6po4fF9hN4XXVbPxL BYGbUvM0iBtJMN+0Je+vW0PUIkm8K66kzSrdR4OlX7RNve1uSJW+dtd0+70LVrrSLh45bi2kCK0B P7wSANHuiYh7efDAPFIFkjYFHUEV+b5nleacHyni8kxEq/D2aSVKrSqqliMPVqQ1lQrwTqYXEqnJ TdKtG7VlVpSg3TqT8+rCrhffotvD1bJp2lFta8slrGVns9e6ezf7Q/sseHdL8N/CWwfR9W/tvTtf 1jVtbtNTNjPps09tLLHZQx3VlcEmC6jWyKSBWdNyko7qQa+JP21fHv8AwkHxNt/C1tPvsfBGmJaT KrZU61qnl3t/ntvjt/sMR9DEw9a/Qnwu+n/Cb4J6LJqZWO18FeAba91Bj8pa4tdL+3XSAY4kkvXd R6vIK/JPV/EHgf4vf2hqV5HH4K+K+q6pLctO928fgXxObh2ld7ua6MjeHtadiEBLCzkchmaHJA/t Hx8n/Yng14f+E+XYnC5Rm+YYPCV62DlOpTVWjgqVOVTC0J1HVSnUx04zoRxFZSrfVp041alZqM/r s8k6OU5flcJRo1qkIylC7V1BJuCbvq5tcqlK8uVpNvR+NCc8Z6fhzkUx58kd/wAvy61Hq1hqeg6j daTrNlcabqVlJ5VzZ3UflyxtjKsM8SRspDI6ko6sHRipBrKefkZI79wa/wA76mCrUK1ShXoyo1qM nGcJxcZRlF2lGUZJOMotNNOzT0aufBtOLaas1o09GvJrv5GqZ+Mjt+HBHU/570hnwOTj1Jx/n1rW 8FeDPFXxE1+18M+D9IuNX1a6OfLiAS3tIMhZLzULpzssrJNwLSOQOcAMxCn9Xfgt+yR4K+HKWmt+ LktfG3jJAkvmXcIk8PaPOADs0zT5lIvJlb/l4nUkkZSNK/X/AAs8C+NPFfFt5NhY5fkWHny4jMsT Fxw1N6OVOkl72IrpO/sqSajeLqzpRkpHq5Zk2MzWb9hHkox+KpK/KvJfzS8l5XaTufCPwv8A2a/i n8UVgv7PSR4d8OzFT/wkPiNZrK2ljP3pNPs9hn1HjoUQRn/noBzXj/jHRJfB/ivxH4WmuRdy+HtZ 1HR3u0iMIuTp9zJbm4WJmJiVwgbbk4DYzX9Bqv8AKAcYG0KoOAqrgKoA+6AMDAwBivwQ+PEo/wCF zfE8oflPjXXcAY6C8kz9eRX6v4/+AnCnhFwVwtiMpxWIzPPMwx06OKxVeSjGpGOHc+Wlh4fu6NNT jdJurU1tKtJaHp57kmGyrB4WVKcqtepNqcpaX929lFaJXV92+7OFN4TbGLK484SZOPMz5ZQheOU4 Geeqj0q5pWoJaX1jcNtdYZlllGWhbZgpKvnrkhBFuYYHBBwDXKfaOgOABn8c45P51YiukmuY3u5H aIFfNYBS3lxqcIMsOoUL171/KNCE4VqNSLXtKThytpWXLJNXbutHvdNPW66P5iLakn1TX4H0rp11 c+ItMhRtXjeXwxpmkvr6XVhNPM2lWYhlS9tZgC63NqQsauQoMVzGGOMIviut3hudQv7yJ5LpG1G7 33sskkstzunke3muUm+ZJTFwSxO4oSctuznaf4u1jRdcTX9NupLbU455ZU3F5IzBOphls7iOVj9o tXtdsbK2dyAAnIyMi7ukld54lWEXEjO1sJS4jY4kwGY5dMsducsMFTyMn6fO81o5tgMJTUJLG4ec 41XJzl7SnFJYepBznJQlZyjVpqMXzJ1FOXtqkafTWrxq04LXni2nre605Xrez3ut7631aVpryTMm HcB/vAHAI4OMLgFcgcY7D0qGS4BLYLfMql953Hd/ERjsT+PPOayWn+ZseuMjrz1yK3NS0xk0jT/E VhFL/Y91ImlXE0txBI1vr8Ft595ZlEIcRNCY5omZMbJtm9mU187QwFfEQr1acXUWHipzSTbUG0nP RWUYyklJ3VuZdL25lFyUmteVX+Xf5FEz9Md/pn8ff/Gt3UNDutP8M6D4ma+sJLLxDeazYQ2UFyza hbTaG9iLhr62ZAEhf7dbtEwZgec7SOeL88YPQEDkc/h9K9Z1K3lm+BXhjU47y2+zWHxH8TadcWk9 q0d817qOiaPdQPYXuwi509LWwczruXy5rqPKHIavZyTKaWNw+fudJzrYHAvEUrSS5ZQxOFjOTTlF TiqE6ycfeavzpNxNKNNTjXbV3ThzLXqpRT9dG9PmcNp2syWF0JN032adVttQgglw11bMSsi4f5TL tOY9wZVcA7SCQY/EAtYdSmNisa2zYdY4RIBCWADRTQyktaXCvuWSIlgjgqjum01zKzlWD7gpRtwY nIVl+ZSBjk5H+NdJ4V8JeMvHt+2m+EfDuteJb2WQGVdMs5blIpHbIkvLwKIrVdxJJlkUdTnijA4X G5jCllWEwdTMMVWqL2NOlTlVq8zTThThBSk+dtNxitWr73vMOeolRhB1JSfupJuXmkldu/ZGGLjZ IsikgowYFCVdWU5UqwPysCBjvkVoaVpOt+JtSXTtB0rVNd1O7lwlnptnPe3Ukkr5BaO2RvLyx5Y4 UZOSBX0lb/AHwV8PYrfUfj78SdP0S5YpInw88EvFr/i+6Jb5ba7nh3R6eWBCkqr4LcSAivoq5/aF 8H/B/wCGr23hb4ZzfD+/1q1ntPAehXXkReKb6wEM1s3jrxU89sz21sLzP2aOczyXckDn5Y13D9e4 f8HqUHiq/iJxPR4IwOXUfrFbDKP1zMVFW5Y1cNRk4YGdV2pUY46pSr1KsoQpYeo27erQypLmlmGK jgoU1zSjbnqW03jF2g3e0edqTeiizwfQ/wBmRPDWnReJvjt4z0r4baKQrDQYrmDUPFt7ESC0CQRu 0VnMwBTb/pEi5yYwRWhN+0Z4H+G9nd+HfgT4Ej0a3nt5rabxtrjvceI7yUo0cd9+9BkkQNtLRM0U TBdvkgdPj7W/EeueJNQl1bxDrGoa1qM7M0t7qd3LdzkscsEaVv3Ue7OEXaq9ABWSJAeMjrxggkD0 A/OvIfiHhOHYvD+G2QQ4XSTjLMsQ443OayatJ/WpwjRwSmt4YChQlqlKrO1zF5hCh7uXYdYa2ntJ WnWff3muWF+qpxX+JnY+IvEeoeJ9SbVdSu76+vJos3D39yblhcEGS5ktgiILa0acyOkSqFjDbeQM 1z4kHc56HGOh/OqAkA6Zzkg9sZ9x9aeJTjqB14Pb8SK/L8TVr4yvVxOJquviK8nKc5NuUpPeTbu2 3uzzZSlKTlJuUpO7b3bPXvAXxq+JPw3Mlv4X8S3cWk3Py33hzVBFrHhnUYyfmivtB1NJbadGXgny wwzwQea9Zt/GHwA+JsyN4w8NXXwT8Ys26Pxf8P1nv/BE99gmK61TwlLKZ9HAn2uz2EzKuOLcDivk oSH6j8qswDd5jsp8tIpCzFHkTcUYIjFFPlszDCk8A88DmvrMn444gy7DUMpxbpcR5BRfu4DMofWs NTW8nhpSlHEYCT3lVwFfC1XbWbWh10cdXpxjSlbEUI7U6i5orvy6qUPNwlF+Z9E/ET4LfEPwho9r 4h02Sx8eeBoriS+j+IHgm5j1rS3vrthcF9Ue3T7Vo06qExFexxMrlyOWNeDwLJfSSyzvKYoIxPeX GRJJHArpHkb2+aQvJGiA9WcA4GSOq8KfFHxl8PNefXPAPiHVvD8kixxzW6zxzWl7biJI3sdUsGQ2 +qWhClSssRV15Kgnj6FXxt8IPjJo7aL4w021+B3jbUjHd3PjbwppCSfD3xFeNNstF8VaFbo9xoMD TwK3mWTGBZWdzaYGa+gWTcHcXV60sizWfDOaxTcMtzKtGeDxE4rlpwwebTdGFFSaVqWZwoqnSVv7 QxFWSibqlg8W37Cq8NVSdqdSV4Sa0ShWbSSfary2X/LyTPDtP8SNoGnHXNLstE0+VJY9O0iwlsLD Vr+RjbvJPruqTalBKzsvyeUqiNDLMCsYiiw2HLrt5d3Gs39+93d6hJZNHLffallkjivBDFMkU6KR FHLLO5kwNpjZoFCByR0fxR+FXjv4bixk8S2kN9o+q3F1daL4w0KeHVvCniC1eK1SGfS9dscxSjZG B5LbJIQu140ORXn2hxadm/1LV/31pploZ4bAM0f9qahLIlvZWTyoQ0Vt5shlmZfmMVs6KQzhh89m tDiDLsxjw9mdCtk9bBLmlQxClRjTlKk5yxDo8rjFODUoOlHllSjBUudyUnhVVenU+r1IypOGrjLR J2vzctrbaqy2StfQqfbp/wDntP8A9/pf/jlFXP8AhKbj/oFeGv8AwQWlFfPewwn/AENJ/wDgqf8A 8n/XzOb3f+fj+70/vepTu7u3luriSzgNpayTSPbWrTm4a2gZiY4DcMoM2xSF3EAttyea6Ky8VQWv g7XfCzabDLcaxrWhavHqxkYTWkejW+qwvaKm7a6SHUQc442Nk8rjzj7WexHccnH5Un2ojqRz7k/1 4rPD1MTha1avQcadWvTrU5WpwtyV6cqVWKjy8sbwnJJxScLpwcWkKM5RblHRyTT0W0lZrstG9tuh +pH/AAT40tGX4meInXLg+H9DhkIOQp+36hOoPbJW3JHsK8Y/bg8Utq/xrk0dJN1v4V8O6RpiIGyq 3N8j6xdNg/xH7bCp7/uh1r6a/wCCf1qE+E/ie+CjfqHjm5Qtk5ZbLSNMjQHjqDM2PrX52/tF642r fHT4o3hfeF8XalZJuIIEemGPTogOvRLUAfSv6+49Usj+if4YZJQ/dyz/ABrr1bfbp+0x+Ls11tOe Hf8A26ux9bjpOhwrllGOn1ifM/NNznr83EsfBDxHfeGvi98Odd0/Uo9KuNJ8W6RfPqEwlkgtbS2u Vmv5bhYFLNALFLjeAMbc5IXJHvP7dPhS20T43XXjPRJvtvhH4t6JpPxB8L30YAtprHVLOKO4t4do wixXELKEGNqsoIr5R8JMbbRfG3iV5PKbStEi0fTyFB8zU/FNyNKZFfOUddFGtSBuxjHIOM/ZaTf8 L5/YkbYftfjz9mDXdxX5Xu7r4a+JH5x/FJDaXq+4VIe2a/M+CcBHiLwv4w8O50+bNq0JcWZT70nz 1MojUw2Ow0IJuPNXyz+0cS0o885YChG7Til5uCSxOWYzL2v3zTxVLzdJctSKV93T9pLa79mvI8in v7b4vfC20hgsjN8UfhPpp+13CyGS88W/Da3Z0Vo4+Wu9R0dpLfcpy/2Ml1JCsFoWME3hn9nnVtfT VLayuPiR40tvD0dksf2q71jQPC8LX17HvC40mOLVri2dyctOPLVcDOfD/A/jnVfAPizQ/GGjCKS/ 0O9W6jtriSVbW9iKNFcWN55TBpLSa3lljkXOGWQg5r6t/aY0u21n4dfB/wCJPgTQxpXw2vdJ1VZb CA7R4f8AE2v6xcajfWU9uANts1xFPFDN8wItVTIXygfPyaFLiHhriji6P77i3hrKHg69KnGcZ1sP WnhcvhmrlSiuaVDA1sRhsbeSc5Rw1es6qr4m+dGSr4fE4te9isNR5JJJ3cZOFNVbrrGEpRnrraMm 3zSPDfhJ4Evvin8QvDfgqzZ401S9D6pdRqD9g0a0H2jVL0kD5StojhM9ZJEXvX786Tpmm6Dpem6L pFullpmk2Ntp2n2sS4jtrSziWG3jUDHSNBk9Sck5JNfnZ/wT98BCHRPFnxNvYAbjVLseFtCkkByl hYeVd6xPESOkl7JaRkj/AJ82HHNfo3kjrj8z+gxX9r/RN8O6XC/h+uKcTQUc540l7bmatKngaUpQ wtNdlUftMS2nacatK93CJ9lwrglhcD9alH99jNfSC+FbddZabprsflD+2n8Iz4K8VwfE3w1C9roH jSaa21xLTdDHp3iWSJ2uSfLIEdrqFsskhXhTNHOCMOorxT4NW9p8U/iD4F8H+IY55dTTWdPex16C MT3FzpOkONQvNG19SQbyzFhZzLb3RJltziJ/MgKiL9wPGnwQj+LvgDxB4Z8TywaB4d1qyMcet6gF V7K+iIn0/UtPt3Ia4nhuo43UDAcAoSAxr5j/AGXfCv7OXgTxl4yg+G/hfxR468YfDwnw9rnxG8er /Zmn/wBuXct1a32neHvDEJ/cRCGymLySkvsdQCQ+T8zxl9GjEf8AEasizajjsDkHA3G+Jhi8RhcZ OXPiK1CTxOYUMFgaMKleo6lKnLE08Qo0cPh51JReIpqMIz58Zw5L+2aFaM4YfA42SnKM3rJx96pG EEnJ3SclKyjFt+8rI0/2pZtd1PwVp/gDw/4U8VeJJfHl9LZXx8LaVqGo3WkaZp0S3cWoPFZxETQD VTpgeFiBNCkyKd2K/N26/ZC/adtjJn4L+NrmJFDrPaaZ58M0LjcksQDbiGTB2sqyLna6q2Vr6l/a J/bR+KmqfE/xZ8L/AAR49m+H1joM1rpWg6j4cFrpVtfeIILdTquj6veCMtDazXkhgt7lWQW89uFl zDK7x/H9t+0/8fEefw9r3xa8f6VqFnfXZtdXm1/VLfUdJ1VnjjntdYKy7rrSmkh2ujqzWzv50Xyi SOT4LxzzjwU4v48zGtnmO4kzaWVzlluHrYGllWDwtKeElyVsM5YieLqVFUruriKNWpGhG1Wyfs3K dLgzvE5Ni8dUlWniKvsm6cZU1ShBOGjjeTm3eV5JvlWvbVUPFOl+NvDujDw38XfBfjHRLrSLWRfC mt6rot1aahpzJyuh3lxfRoNQ8Pu27YDIZLR/mgzGzxnlPhl8OfE3xb8W2HhLwvArXE/7+/v5VY2G jaajKLnU7+VR8sKAgKo+aV2WNMs3H0T4U/bf/aS0HVo/BfjabS/jBp97eWunXPg3x/pNh4hXVmvW jS2gtdRij8xzOk8Xkyo7q6zK6kqQa/X74b/Bj4c6Voeo6x8NfCOn+BfFuvx2ms+MPCNrdSX8SXot Y/PsdFvZwGbToLhpvLhUBNzswUFhny/DjwA4V8aM/oV+GOLMfisp4acIZrgc1y5YHN/ZRU3Sw+Hx eGr4vBY2bVKVFuVTDYuhRj7mGqRp04xyy7IsLnNdPDYupKlhmlVhVp8lbl1tGMoOUJvTlveM4x2i 0kjzb4TfCbwl8HfDUPh/wzaCS5lWOTW9duET+1NdvVX5p7uVRmO3DFvKgU+XEvABYsx9TEg5yAOv OT+FQskiMyOCjqxVlZSGRgcMpBPBBBphY9lJ/P8Awr/Q7Kcly7IMtwWTZNgKeW5Zl8FTo0KUVCFO EeiXdu7lJ3lOTlKbcm2/0CjThh6cKFGEadOmrKKVkkunT57tu7ZcRkLoMnJYcY9+e/pX88/xT1P+ 0fiX8Qb8MGS78Z+JJVYNkFDq92qHP+6BX76+IdZg0Dw/ruu3LiKDRtG1TVJWc4CpYWM90ST2/wBU APc1/OJd373tzdXsxzLeXNxdSNnJMtzK8zk8f3nNfxF9NrHw+p8AZOpXq1KuPxMl2jCOGpQf/bzq VEn15XbY+M40qpxwFLq3Ul8kopffdlkyg459s5Hr79etR+Z1+Y9D7f5PNZ5lPUEduBTPOIB/xz9c enGa/gFUX93yPgzSM5PLMc+pwSfxPsP8Kct2VjkTCkSbMkgErtYt8p7E/wAuKyTNnPP5nH+Hqaja Yr3B98jrz19atUuys3/wwami04yeeD0OB1Pb/PrXYaF4ssLLw/4i8K6vptrdWHiCbTbu21fc8d94 b1fTHmS21S3KBvtNm1rd3cVzblcyRyhkYSRrnP8Ah94g8IeH/FVjq3jjws/jTw7bw3YufD6Xz2H2 q4kt3S0kknjdS0UcxVim5d2O+MH1a4+PfgzTLlp/A37P/wALtDZHL2914hj1Txhdxn5gjeXqN2sC sAegjI4A7Zr7bhvKcrhhv7WxvGGEyaq5VsPLCTwmMxWJqUalJQqSUIYZ4Vwqwqzpx58VSqKUXKPJ JRmdeHhSS9rPFwou7i4uE5ScWrN2UHCzTa1mmrN6aMw4/gN8Y7y/Wy0jwD4g1uC4cjT9Z0uxlk0L VLUgPBqOn6tNsiksJYWR45GK5VxwG4r6B0H9nPxLp/w38UaN8XfFfhT4b6Va6poninSX1LXINTv9 AvfMbSNVuL3RtNlO2G7064ghVTKC1xBD/d58t1/9rX40eM/Csnhtde0/w2LBXuWm8L2UWgXl/pUY WP8AsuFoJCII7eNtyJB5bPFGwYnYM0P2ebHRtd1P4k+Kfii15qfw20bwPdN46urjULz+0ry4utQs Z/DunadcifzJtXm1ewi8pd+CI3zjNfq3DuA8K4cSYTLeGcDmfEss6w+Jpyq5piKeX5bQhPC1XWeK pYSlXxc8PhVF1a1ZYqjKjCn7eClKEJy9PDwyxYmFLDQq4n28ZK9WSp0opxbfMoRlNxha8nzRcUuZ XsmdG+p/sqfDgJ/ZuneLPjn4jgYFZ9YZvDHgt5wSBiwhAnvLfdjCsJA/r2rqYfF37S3xQ0G1bwXp +lfCj4YXd3cabayaNLpXgLwzaQ28SC4N5rV5LFcXkCI5BkTdvcuqAsCo53RfHXguw0/xP4z+B/wR 0DQ18BadYz3Pjf4o+JJPEV7Zz3s4trMaRo12VsLjxLM3mmOJfNkKxNIiAJXzb4++KPjf4n3tnqPj fXJdZuNPtDZWQ+zWlja21qZ5bnyorKwhihXEk8nzbN20BSxCitsz4iwHDuXwowzJYbCZlTbo4Phb C1MpwmJowrexlLEZ5mFGWa4yClSrU5UpUMTSnKHu4mDUkipiadCCSq8kKi0hhYOjCaUrPmr1I+1m laSa5ZK60mj1KDX/AIf/AAuubvUdDvm+JvxNtLz/AEHxNqFiy+A/D15FJIJtU0y01CQ3Pi3VUkAa 3nukjtVcCVYpcKT5H4l8XeJfGWqS634r1/VPEOrTgiS+1a8lu5wjO8ghjaRsQwh5JCsaBUXd8oFc cJR1GSOmRnA9Dx+H50eZzjJ+uSB9DX43muf47MsPDLqVOOWZPTl7SODw7nGi6j/5fVXOc6mIxDXu +3xFSpVULQjKNOMYLyKmInUiqaSpUVqoRvy3/md23KX96TbtorKyNLzOeTjrn0HHvTxJx0z9DxWY JAO459Pb1z04/GnCT1wfUDqPXjua+ddP1RgaYkOOSR9D/L0p4k4znjpznrgnv9f0rNWTnrg+/I+m aeJMnk547H+lQ6b7JgaYkx1z+HH9fYV9Q/sop4e8S/EO9+FnioW6aN8W/DupeCoLydUJ0rxJOovf CuqRyP8A6p49ctbVSwIJSZl5DYr5RWU+wA7E1r6JrN7oGr6VrmnSPBf6TqFnqdlOrMrJc2NxHcQu jcdJI16cV9Bwhm9HhvijI87xODjj8Fl+JpyxOHmrwxGFk+TFYaf93EYedWjLspu3Q6cHXWGxVCvK CqQpyTlF7ShtOL8pRbXzNjxX4d1Xwb4l1/wprkElrq3h3Vr/AEfUbd1KvHc2FxJbyAhuQpaPI9Qw PesDzSAVyyq4G4AkB8MSNwB+bnkZr7x/bs8N2mtap8Mv2idBtlTQ/jp4K0vV9UaBcQW/jPTLSC11 2BivCyuRG57llfjrXwB5mMDdjjnOPzHrzXp+I/By4G42z/hqlWeKwODqqpgq/wD0E5fiqcMTl+JX RrEYOtRq6aJzcd0aZjhPqONr4ZPmhB3hL+anJKVOX/b0HF/M9t+Gvxy8Y/DeG50SI2XinwJqrY17 4e+KoDqvhXVo2AEjizlbOm6htH7u6tminjIBV+MV63d/CvwN8YNC1bxD+z5Le22t20Eeo+IfgxrN 0bzxNpcdvJLcXl54LusD/hMNHjgaUrAgF9EsWZI5ADJXyVptmlys97ePNDpViI2vJ4UV5meVtlva WyyEK11I/AycIqtIwIXB6LSfHmreHbyxuPCN7eeDpLS6F0L7Srhv7QE8RcWtzJqMSrPJMsbyBgrp H852RqOK9Th7imEMBh8l40oPPuF4wnDD05NLMMEpXi6mV4qf+7U1Nt1MPWlPA1nzt4d1kq1LShik qcaOMj7fC2aiv+XkL6XpTfwq+8W+R6+7zWa6z/hX7f8AQv8Ajv8A8Ed7/wDIFFdv/wANpftPf9Fe 8Sflp/8A8h0V7P8Axpj/AKGWef8Ahiy7/wCiE3vkv/Pyv/4Ip+X/AFEep8o+f7jAz379+3NJ5/Oc 8dxk55649Pyrr/D3w81LxF8OvHvxGt9RtIdO8A3nh6zv9Okjna9vX8Q3DwQS27qdkcUews+45PAU HOR5v559f/Qvevz/ABeS43A0cvr4vDSo0c1o/WMPJtWqUVWq4dzVm2kq1CrCzs7wvazTflSpzhGn KUeWNVc0X3XM43/8Ci1rZ6H7gfsDMF+BZdRky+N/EJY9yVi0xAD68KPyr8kvihqDXfxK+INyxIM3 jbxTIck551u9/oK/V7/gn9crJ8CXQHJh8d+IVbHJBeHSpefbDCvyA+I0zL8QfHanIK+MvFAKncCM a3fDGM9a/qTxmp83gZ4CU4fwvq1VtL+aOHwy/BuXzufUZy/+ELIEusHf1UI/8ExxcHBBY4OMjLAH GeSO/t/9evrX9jH4s6Z8OPjNYaZ4pdX8AfEuwu/hz44tpz/osmj+JF+xQ3cyNwBb3rwSbuSoDV8Y mc+v6n+lKty6MGVyrowZHUkFWUhlZSOhBH5iv5x4Rz3H8HcT5FxRlaTxuR4mliIQnrCooSTnRqL7 VKvTcqNWOqnTnOLVmfO4PE1MHiqGKpfHQkpJPZ2esX3UldNdU2e7fHr4Zan8Ffiz41+HWpq+3QdX nGl3JDBb/Q7pjdaNfwsR+8jl0+SFsjjOR2rtPjH4y8Y6DovgbwAPEVxBol38KfAN7rfhm0MH9mx3 r2N1PamTYrB7iSwntJpGRl3NKu/LoCPe/izbP+1L+zJ8PfjfpKxTfEz4Qzab8KPiyMkXF5ok7xW/ hDxXfFVJ8gF1illYEhpH/hSvPbH9nbxv8eP2i9a+H/hGJYtC8DReGNC8W+LdR3QeH/DWleGdB0nS tQubu7cBSWe0uRBEDumYgqNuSP2jOfD3M8Jm2dYLw5wmLzDK/Earlc8iWGnNVK2XZpDH4l4Wag1z ywcsNWwGN52qVOeGxDqrltKPs1sBVhVrQy2M6lLMnReH5W7yp1VOXK7buDi6c7uycZX7n6vfs2eB L/QvhL8NfCOmWLy348Naff3kaJg/btaQ6xeyztwEAmvWBJ6CMZ6V9RvH4c8CgG78jxJ4oUZFsDv0 nS5ev78/8vEwOPl6A9h1Obe+ItJ8JaXH4T8DfJDb20Flfa+Ri7vRbRLB5duw/wBTb7VwMenHcnzd pS7FjuZmOSWOWYk8kknk5r/VjJsuy3gvJspyPLYU8bjMowtDDRq2UqGHWHpQpRjQi7xq1IqCvWkn BP8AhxbtM/VKUKWCpUaNJKdSjCMb6OMeVJWS6tW3ei6Lqb2seI9W165NzqVy05B/dQ/ct4E7Rwwr hY1A44GfWvItXbw18KvD3xJ8eW1rFZfaV1Txx4hkyFW91Wz0e3tY2AA+XzBYWyhe8k7sOXNd9vHo f0/xr40/bv8AE1/oPwEvLOxDhPFHiTRdB1CZWK+TYZuNUkRgOqyy6dFGR6Oa+F8RM7/sLhfiLjDF w+uZhkGExWKoVKi9pUjiPYTp02pO8lzymqc2mv3cpJ+6cOY4j2GFxGMmlOph4SnFvV83K0u+97el z8Z9V1q71vVdT1jUZTNfatqF5qV5IzMTJc39zJdTsSeoMkrV2klz/wAJxo0kxO7xl4bsN9wxb974 o8M2MKqblsnM+u6dbqPMPL3NjHvOZLV2k8k+0H1HQdj2r1f4E6zp+lfGP4bXurRW8+nL4s0u2uo7 pBJbBL+U2CyzLJwUSS5R+ePk5BHFf475DR/tDOMPl2OrpYXP69KjiKlRtqDrVVFYlvdVKEpuqpLV rnpybp1Jxl+P0P3laNOcvcryUZN9Lu3N6xve/qno2n9wfsP+G5fiD4gk8U+LdAsdRtfhfZ6Xa+Dv EU9vJa38F7Ot6tppT+Uqw61Y29pJPNFJMjz20iwCOXYdg/WfT9TvdOvbe9spWiubeQSROnBBXkg4 6oRkMOhBOa5OwFnpdhFFHDb2sYBKW9rDFAgUHaixQxIqqoVQOgAAr5Y+Mnhz44fFXxLc/Dzw7rk3 gf4d6jpNnqcXjHQ4pRIZYLkW2t+G/FU4vUmLTQSrLZrZgBzGFn/dmVh/qjwjkUvBngXA5bl2GxPG XEDq3csLSp0cRi8VUv7CVacpyVClSpwpYZYivVqezjCF3y2jH9SwlF5NgKdOnTljq7d7wSUpzfw3 d7RSSjHmk3ZJdND718Q6/wCG/Ftnb+LPClzbaw/9qtoHjC30Ca21GDw7rsVq908mrvbzH7DG8aKD nJMk8akbmOOXWVWJ/hA7sQKzP2Zvht4J+D2hy/DHw/bsukeJIWTWNUvXEuo6z4gaMCLWNQlzjzy6 hURQEiXaqjgk6mo2UumX15YXGElsp5YJSTtUeUxBck9E2jOemOa/a4vNMwyfLM/zjC0MDm2YprG4 fDTdWjh8XFJyjCq4U3UVSEo1JT5IRlVdVQXIont+/Uo0sRXiqdaqvfjF3jGatdJtK90072SbvZWs fKf7ZnjuHwb8ENds4p1TUvGlzbeFbFFI81oblvtWrSKc52rptvMhI6G5Ud6/EDzeOMAY/Hp0+vSv p/8AbD+NcHxS+JR0nQrwXXg/wKt1o2lTxNm21LVJJF/tvVoyDiSJp4Y4YW/iitQw4evk5ZeM54HA 5yOfp9K/yQ+kjxvQ468SsfVy+v8AWMm4epxy/DTi7wqexlOWIqwaupRniKlRQnFtTpQpy6n5PxFj o47Mqjpy5qOHSpxfR2+KXzk2l3SRfMgz1Jx6fn+dMaT3PHGScn1/PFUzL75xggY61E0vvg9Tk564 9vr+dfgipX+yeEaMbxPNEs0pgheWNJZljaVoYmcCSURBgZSqFm2ggtjAIzx1c3hmG9uLW28Ka9Ze J5brcgsfKm0XVlmjXc4+w6m6pLBjJR0mYsFO5EbivPzLkk/l259vQcVE8mVAYAjHBx6dM16OF+rU 4yp4nBLERm4++pzp1YJPWNN3lS95bupRqW0asVFxWkocyfm016Pb74s6/wAUeF/E3grUX0vxRo95 o94oQqtzse3mWRA6PbXkDvDdIVYYMcjDtwQQOWaYLyDx17YyT2wOnNdp4W+KvjLwbpmoaJpF9Y3G havPDPqGi67o2leIdKuJIFdI3+y6xaTC3bY7BjEULcZPygiPW/Gvh3xRIs2seCdF0K7FssJ1DwEj aDFJMh+W4utBuJJrOQ+WNrCEWzN98tmvbq5bw9iKXtsuzKrhK8rv6tjKXMo3k0oQxlDmjWajytzq YXBptyXKlFOWso0GuanUcZfyzXnspxvzO3Vwgt/nyljFfahf2VjpltLe6jeXdva2FpbxNcXF1eTy LHb28UKqTMzyso2gHOcHjNfpFq3h3wDonh/WvgRP4P1ix8NeHtFsPiX8ffHmkaxJLL4M8XrokdzY aLo3mweRrSpJIIYrCXJY3h8v5o3lGf8AA/wv8LvgF8O9L+N/xB1qzXxV48jZPhlFreiXcOoaCIo7 tJLiGCL7T5Ek6tGzaj5TxW8M0TqreYVb5T+JafFzxBBpt7daW1h4G8XeKJrbw5aeGtcg1vw/rviL UpjM1xeX9pfSza9r8xkDNcXwExxsjjhQCJf3HJeH34bcLvMcfg6ee8RcTUIVKuX0aVHFzwWVVYWp Qxk+Sr9ThmrrUXVnFwrPCKNCi1XxMq2D9qjR/s3C+1qQVfE4mKcqcUpuFFrRTdpcircyu1Z8lox9 6TlT9m0XwQP2htL8NfCL9nHUzGNMuLvV9R8B+J7Sbw/rGtXaQwRt4u1zxFbz3Gn65exKbhEBNqLa FwkUbHJr9LPgN/wSp8AeGray1v446tP4618rHM/hjRp5tP8ACtjIdreRcXSBbnVyCCrEGGM7eAwO T9Y/scfsw+H/ANmz4X6Zp5sreb4heIrK11Hxzr7Rq15JfTRrMuiW8xG6LTLTf5aopAeRHlbJIx9d 5PrX+ivg19E7hLD5fkvGfivw7h8642r4eg/7Pl7SWV5dTjCCo0Fgqk6lKpiKUElXUr4SFVyjRw8e X2tT9IyXhLCRpUMbm2HjXx0ox/d6+yppJcseRtpyS+K/uJ35Yq13+On/AAU5+G3w2+G/7PHgmw8D eCvDPhNV+IVnBB/Yuk2dlcvCNI1FpY5buOITXCnbGW8x2yVBPPNfguJsdDj6Hp9OeOa/cz/gsT4u hg8N/BzwQkw+0Xmr6/4luIQw3i3s7a3062kK9gZri4APqp9DX4Q+cB6fnnn3/Ov4O+mfDK4+POfZ flGEo4LCZPgssw3ssPTp0qUJLB0qrjGFOMYR5Y1YxskrW8j4HjX2Sz/EU6MVCFGFKNopJJ8idklZ aXNUS/7XXnp35GOOlP8ANH+zn6/0PespZSTycc+vU+w9KlWUcY6j1PP8ulfye6Xl+h8oaglyck44 Pf8ApjFSrL6fQn2z9PasoS99x+hyalWT16Hnjv3GQetYyortYDVEo9cnoD2+hHr1/Kvrv4l+CtPk /Zb/AGffidptmkV0+seOvAviG4iRVM9xYan/AGtpclwyj5n+z3dwgJydseOg4+NhIeOh6dOvsfav 1GsvDx1//glxc6ls3y+EPjRc6xE2MmO2nltNNuf90f8AEwXP0zX6x4VcOU+IsF4r5dOgq1XD8J47 H0m4qUqdTLcbluOcoPeMnSo1aba3hOUdmz1sqw6xMM1puN3HCVKi8nSqUp6fKLXo2dh8KdEP7Qf/ AAT18feCkX7Z4t+BXiW68T+HVBD3S6Y0D6tLaxZJIieyfWgAMZaBB2r8oomRpMTM4jQM0m1SWwqn CY/hLOFXJ6b89a/a7/gk5pulS6T8Q7u3u2uW1UjQPFehXLK8KhY/teg6jDFj/UTWb63bygkgtCmM biK/ML9p/wCGV38Ffjn8RvASLNb6dbaxc3Wjt8yLdeHdXddS0zgAb4vIljUjkboPUV+n+MPB+NzL wS8BPFqrRU6k8BPh7H1INTjJYCrXllFSUl9t4KNTC1btOEsIqe8Vf1M3wk6mS5Dm0ldum8PUa1T9 m26TfnyXi/8ADY8da7ibTmjLwpLLJJKY1aUhVge3S2gWJYyI22vdNvLHcB8x3AZxi+Mk5A+vfn2q qXHJz07A9MdAKYZD0/LnI9jX8rVOaryOSs4RS0Xb8Lttt2W7ufKt3t5Fvf8A7X/j3/16KtfYYf8A n+h/75P/AMVRWn1Kr/Kv/A4//JD5Z9vxPQPDlrrmu/CXwt8PfD9vENV+Jvxjufs++RbQagmg6Hpe nWKX1y33tPhv9ZvHy2UjZJGALV4Tfxvp99e6e8sEz2N3c2Uk1rJ51rNJbTyQPLbSgDzbdmQlWwNy kHvX1j8MxrGmr+yj4uuzY3WhQar8RdI0qxluLaS7fU7bVdavbp20+RSTYj7RYgyNwSdg5xXxzqer S6pqWoanPFawTaheXN5NDZWsNlZxSXErSvFaWlsix2tuGYhERQqgYAAr9b4syqjQyfh7EVHVWNlS oUYQnHlhHCrLcuxilFJ/bxGPryTu+eNpPlbd+7F01GjhpNvnajFJqy5fZUp3XrKpK3da+v7U/wDB ObURc/CHxTZhgzaf4/uWZQTlReaNpMqk59TG/wCVfld8aoTp/wAYPijZOApg8f8AitdvOADrN24A A9mFfoR/wTO1YS6F8WNHL/Nb6x4Z1RY+flW6stRtHYDtlrVAT7CvhX9q2zbSf2ivi1bFdgl8VT6i gxgmPVbS11FGXAxg/aT7+tfs/iNS/tD6Ong5mHxLBYjE4V+TvioJeWmF/A9vMv3nDeTVEtKcpQb/ APA1/wC2nifnjPIPufzBpfP+v4Z+prH8/IHPp3J/ECt/wn4d17xv4l0Pwh4Y0+41XxB4j1O00jSN OtUZ5rq9vZlhhjVQeF3Plj0VQWPANfzBQwFbFVqOGw1GVfE4icYU6cIylOc5yUYwhFXcpSk0oxSu 20kj5aMZTlGEU5Sk0klq23okl3Z+hf8AwTU1TxhL8cNQ8LafoY8Q/DfxV4Y1Gx+LVpfukOhaZ4Xg ie4h1/ULi4PlW89repGYSxDMZGRCMkj7G/a01HW/h9rafCvwrqXhn4IfBqTxDZeOvFvirVNcguvG /wAcdYvL+21WZtK0rTZGvNX0VZpI7aNCY4QYjvYRxBB8b/tC+OdD/Zc+GP8AwyJ8JdShn8Y6nDbX /wC0d8QNMkUXOq6+8QkHgLTbyM7k0ayDBJwG+dwVOC0oPv8A40sNL/bG+C3hLwBFbQWn7Q3ww+Dv g74ifC+4aQGf4geDZNFjTxL4Wiml+e51C2v9NmlhTLNuxgYLtX93cI1Vkfh7xD4NZbj55r4h5BFY tqFWFOlPEYlzqZhwnhMVSpSxPLGFHnxEaGKoLHZnLEZdRn7KtKWJ++wc/YZdiclpVHVzLDrndmkn KTbqYSE0uayUby5ZxVSq5Uou0nzfocHEgEinKuA6n1DDcCMdRgivmT4+fFHUdF8RfDD4QeD72S28 a/FDxTpkV3dWjD7ZoPgmxvUn17UoyAfJuJ7e2uYInPRI7hwQUBr1n4deJV8Q/Dfwf4onWaN77wpp d5qEHlyPdQXtvYJFqlo0AG9rqO+t7qMpjcXj24zxXyj8IPhl4v8AFX7Rfjb48fEm80ux1PSkuNE8 IeBLbU7LV9V8NaNeWws9JuNd+w3Dpot4NKN1i0cec019PK4QKN39CcY5nmuY5fwzlPDdOq6vGWIw 0auJp3hHCZY1HEY2v7X3VCtUw3NRw8U1VlOpKdJN0W19Fja1WrTwlHC03fHSgnNaKFLSU5X6ScPd it222tmfeRwSSOBnge1fB/8AwUN1GGz+B2l2bn99qXjvRVgAGT/olhq1xK3soXaPq4r7nr8nf20P jFYa38XNB+F1lKNS0Xwd4d8VP4us4WSSC81zWvD11PFpsu6NwJbO3tLGQttYxTTcYeM48fx3zPCZ b4aZ/hMViI4etxD7LLqF1fmq4qpGMrR3ap0VVqytqo03a7snjn9aFLLMRCVoSxFqUdnrNpPTyV5P yT6n5l+eSOvX6nP5HpT472W3kjuIZGjmgkjlhkVirxyxMJI5VJ6FZEU57ECsMT4HJ42jJ5549a/S T9jj9kf/AITcaf8AFf4o6cw8HxyLc+E/C92jIfFEsT5TV9UibBGgLIv7qPj7Wy5P7gfvP80OBuAc /wCPuIMLw/w/h+fE1ffqVZcypYaimuevWmruEIXSVk5Tm4wgpTkov8wwGBxGY4iGGw0bzlq29ox6 yk+iX3t6K7aP0s+C/ia4+JXw08FeNL22u7GbWtCspr2C6t5baZr6BDa3skKToC1nJcwySQyAbXjl VkJBBr2VFWNQiAKg6KOg/wDr186fHH9pP4b/ALP+mQRa9MdS8RXFsn9ieCdDa3XUZbdFEcM10MiP RtJUKFEkgG4JthjcjA+O9b/4KN21tqVhd6N4Vi1zQNU0OzubrSTeXOia/wCFfEETSRalptzevaTW +t6e+IZreeII2yRldQRtH+meZeJ/AXh/DDZFxPxdRxGfZfRoQxcqdOc6rm4Ri61SlS9q6bqTSnKj B1KtNVIzlH2V6i/UKma5dl6jh8Xi1LEU1FTaTb2S5mo3tzPVxV2k02ran6mXOr2nh6Fta1HULXSL PTgt7NqF/cxWdraxwurefNPM6rHGH2jcTjLAZyRXxj/wUb/aA17R9I8E2fw6P2Pw98Z/CD6xf+Mo Gliup7a3mbTdR0XTEeNWsXkKA3ExxIY5gkYUMzV8SfHX4vw+Jvg9F4rXwzN4O8S/HfXGW509/Emr a8t78PfBN0Gh1FIL9lh0f7b4ndVZbaGNJ00osc853/ijKfF//BPb9nnxNMzS3vgH4m+NfAjTyMXl FjqEX9qW8BdiSIxsXA6D2wK/KPEDxpxfFPC3iTwXwxU/s36vkGHzqGLo1KvtalP+0MJTlRSrUKFS iq+T476xUtCNSDm6SnKMfaT8jH53LFYXM8FhX7Plw8a6mm+Zr2kNNYxa5qM+Z6XV7Xsrv4LSXGAC MDn1/A/l/SrCSgYJ6fU8noM/jmsVJffOO/ORx/8AWq2spz17DA55/wDrc/rX+b9Si9bo/OjTaQ8Z yBz0zz/kH9KiaQ8479x6fT/PTioPMO3qc989D+R7cV6D4B8CR+Mf7e1LV/EVh4P8L+GdLl1DVvEO pxvLFJdssg0vQNLt0ZTf65eXCMkMIYYWOSVyEQ56MuyvF5pi6WBwNJVcTW5rJyjCKUYuc5zqVJRh TpwhGU51JyjCEYuUmkm1dOnOrONOCvKW2qW2rbbskktW20ktWefmT0JPsSR+pqN5MZ7cdCT364/z 2qu0nGcHGc5zzjJxkHoenrzXXeBPAniP4ka+PDvhuK0a6WyvdUvLzUbuPT9M0vS9Nha4v9R1LUJj 5dpbRQKxLMQGIwuScVWBy3F5ji8PgMDhp4vGYucYUqcIuU5zk0oxjFatt7f5ChCdScYQi5zm7JLV tvojlJg8Wzeu0SRJKnIO+NyQrggnqVORnIxzX1J+zj8FfD3jKHWPir8VdUt/D/wc8BXKDWrm5maG TxDqqJHPDoNqVG94j5kHm+WDJIZ0gjG9yyLN+zpb+NfiX4X8GfCPxIfFXg+58K2Wq658RbiBoNE0 pLG/1Gw8UahJJLFGqWcV/Z3CWiE7pgEO4qWYYP7QvxI8JXFroPwX+EpeP4V/Dqa5zqKufM8ceL5Q IdV8WXrA4uIy6uluTxhmdAqGMD9ZyThTD8HVMdxXxhgKGYZfk1R0MBhJVo1KObZkownFQnRbjiMu wkZqvi61KXsqq9lho1G8Rp6tHCxwbqYvGU41KdF8tODknGtV0ejjfmpQT5ptPlfuxu+Y6n9o740/ Cz4x+PIZrTS/Eln4Z0DSNP0Lwxr2jz+RLHpsFuJpobjwbq6LBCEvZZIw0FxA7R26Fi+Fx6F+wd4W g1X9pf4a2fhvx5Yar4eOtNqviDwtqkM2jX1/b6ZYXN6hk8P6l5tpqzxXEUREltNJPEQJVAVSR8F6 /rNpq9xZS2ehaZoEdppOnabJbaW140d9PY26wT6vdte3EjNqNzIvmTbCse4/Iijr9LfsMeJj4W/a q+EGsGKaW2h164h1EwoXMGm3em3kF/eyDPyww2zySSMSAqREngV7vBfFn9u+NXCWecQ0cPipY/P8 vnWxGGhVwtqc8ZQTjCMHDmoxhaHLiKVSpKinCU3fmW2Cxv1jPMHiMSozdTEUnKUU4ac8dkrXSWnv JtpWbP68TjJ3Ag89PX6GobiaC1gmurieKC2t4pbi4uJ3WKG3ghQySzTSyECOJY1ZmYkABSScCvz2 8U/8FQ/2RvDcV39m8Xa/4mvLZpo1stB8Mai7TTRMyFVub8QRBCynDbsYOeRX5SftY/8ABTTxz8dN G1HwD8ONKufhz8P9RVrfVpnuxP4q8RWh4Nre3duFTTtPcffghJLg4kkdeK/1l8QvpT+DvAuUYzF0 OKsLxZnEYS+r4DLK0cVUrVUvdjUr0efD4enzW9pUqzUlG7hTqSSg/wBdzHi3JcBRnOGMhjKyT5ad KSm2+icleMVfdt3tsnsePft6fH20+Pv7QPiDWdDuvtXg/wAKwx+EfCkysTFdWOmSy/a9TjH925v5 LiVT/c2CvjES84JB7dOPqOOlZYkI6ng9cZGP/r5/lXaeHPh58QPFyQz+FvBXirxFb3NwbSG50jQt Sv7SW6Gf9HS5t7dkaU4xt3ZzxjPFf4s8S5xn/iJxbnvEuNozzDO+I8VWxVWFGnOo1KrO6hThFSkq dNONOmteWEYo/EcViMRmOLr4mcHUr4iUptJN7u9klrZbLsrIxoPPuZY4YY5JpZXCRRRI0kssjHCp FGuSzlsAADJJwKlk82CR4JopIJo22SRTRvFLG46o8bgFGHIwQMVHb3eueFNcguY/t+ha/oOoQ3EJ liktNQ03UbKdZYpPLlUNBcRzopGQMEA4r7B8X6lpH7Tfw41n4jQabZab8fPh7CNT+JEWmWws7T4i +ClW0sYvF1pp9svlx+JLKdlOpbFQTxXAucZSU1hk/DFLO8Hm9KhjXQ4hy6Eq9LBVKdo4uhRjKWKj Sq891jKEI+1jh50lGtShW5Kqrwp0a00MPGvCso1OXE0lzKDWk4xTcknf44pX5WveSdndJP5EWTBH cnr+OT/Mf561KsgOPXk56dKyo3LlVQMxY4VQCxY8DAAGScj61YLFWKurIykhlYFWVgeVYYBByO/P HtXyEqLSulp3OU1Vl5H8+cY+uOea/af9m+2TxZ/wTJ+PuhIpmm0nU/FF6qKN7q9tDoWsI2O2Ft3P 0U1+JKynjJwOwGee9fuN/wAEvZovGHwE/aW+Gsh8ya8iklgtyQTs17wzqWnBgp7faLWH8cV/SP0U MNDH+KGY8OTt/wAZhw7xDliT1vKvltapFa7tujoj6fhKKqZpUwz/AOYzD4ikvWVNv9D54/4Jk/Ee ++H/AO0la+DNQS4ttP8AiRo11oNzaTxyRsmqWsJ1bRbloZACrFoJEDY+7dEg4NfS/wDwV0+E4Q/D 740adbEZE3gnxJNGvGV8zUNCuJyB1Km/iBPaNR6V5lB4PXwZ+3D+yNrsdoLNPHPg/wCHWqXSKuxH 1W10Wbw/qTY/56GaxUt3y/vX6+fta/C2P4w/s+fEnwYsPnai+g3GtaFhQzpregqdTsPKBH33a3kh 45IuSO+K/rDwy8Msz4p+jH42+D2Pn/aGO4MzbE1cpbjaSqU8Jg82wqhFt8ixU5TTSbssXUV2mfW5 ZltXFcMZ3k9R+0qYKrJ0dNbqEKsLLpzNtf8AbzP5E2fjg5x69eeuPyp4KYySwweCCB0PB78Z7d6q SBo5JIpU2yxO8ciN95JEba6EH7pDK2R6jFQGQjjJHoP69frX+WUabTs1Zp7H5Ze3Q6L/AISDVv8A oJP/AN+rf/4zRXM7z6D9f8aK6/rOL/6Can/gUvLz/q4+ef8AM/vZ9KaN9on8R/sVeH9AdYZx4ag1 xs3VtZxnVNa8deIZ9Xm869xH9oaOwCbfmaQxpGgLsqn431N5YdT1GKXKzRahexTDGCJIrqWOQEdv mU8deK9p+K3jXUovh3+zR4etLgW48L/D2XxLbTxWsdvdx6rqvjHXp7eYXap5kqRQ2Nt5ZD7Msz7Q zGsn9o2zt4fiO3iTS9Hg0nw9498OeF/GuhvZweTp+pLrWhWEmtX1miEqjHxCuqLOikiOdZF46V+5 8YYWjjcvrVMNNzlkssr9pG1oqGMyfB0rQgnJRjhauBVGdSU7VHWopQg1aXpYtKdJuLu6Hsb+lShT Wi1soOnyt315louv2L/wTR13yfiX8QtAaUAav4Ktb+OMnG+XRtZgUkA9SItSf8DXl/8AwUA0ptJ/ aN1q7KbY/EHhnwvq8ZwcOUsG0qZgc8nzdNbP1rlv2DvEo0P9pbwdA8uyPxFp/iLw42M4eS80qa7t 0PPObmwhx719A/8ABTvQmtvF3wu8VquF1Pw7rWgzPjGZdG1GG+hBYdW8nV3/AAXpxX6fSo/239Fr EU0uafCOeKTW7jGrOCT9G8xl93qesv3/AAlNb/UsR+bWv/lU/NHz/fnnsf8APr+dfpV+zbb2P7Mv wE8WftfeJLW3fx54oe/+HX7O+mXsas6avcW7w+IfHUcUnJhsrd2jhkAK+YrAHLA18FfBr4ca38Zf ij4H+GPh+NpNS8YeIdP0lXVWK2lrLKpv76X+7DBZJPI7HgLGSa+mf29PipoviX4p6f8ACXwFKq/C v9n7Rbf4Z+Dba3INte3ekqsXiPXiI/llurvV0mJccusS8nNfnvh9h48J5JnnifWgnj8qnHLsiUle +cYmnKcsbFO6f9k4NSxMH9jG1sBJ6Np+bl1sHQxGay/iUX7LD/8AX+Su5/8AcGF5rtOVM+P9S1m/ 1jUL7VtUu577UtTu7i+v7y5d5bi6u7qV5p55pWJLyNK7EnPO6vuvwTB41m8N/s7/ABu8C+MrLwTZ /CPSdb0Lxd4+8Q3yR2fhjUNF8V3mqWWjvY27m41v7ZpOsxR2lhDG7zpI0bbVDMPiLxz4E8ZfDbVr bRPG+gXugajfaVp2t2UN2vy3emanbx3NrdW8yEpKAsmyRQd0UqNHIFdSK9jt5Lu6/ZE1hI4okttH +PmkT3kiSy+dP/avgm7jtTcQmTb5aSW2EIAyZCTkivM4NjjMnzXiSGZYXE08wwmDqYlw56uGxMMX gcRh8fSlKoo+2puM6F6jXs6vs3N06tOo4zWOCc6NbEqrCUalODna8oTU6coVFd7rWPvbO17NPU/X b47/ABh/4XL+yjr/AMUf2Tk1OPUoPEAtvi5puiQtpfiPwZbTyXN/rfiXTNFgnkm0/SdRu4xLvjY+ RFeTksHD7Lv7EXge68J/AvSNd1Z7ifxF8SL+78c6xeXkss99dRahtt9HN3cT5kmc6Zbwy5dmO68Y 55r8h/2QfjH8QvhR8b/B7+A5Ir5PGesaZ4O8TeFr9Wn0LxV4f1y8isr/AE3V7InbNELeaV1bG6Nk 3Keor+njXfBmkNo0HiD4fwRp4btoYre40G1jRZPDawxrGltHbp93T1RQEwMKqgDjp/cfgxjf+IwZ nV8UMTKdLiLhjALK8Vl0Y8uCjUqSVR5nl0U3yutQUoYyg1KdGpOU4zdCpCFL77I6rzqr/a0m44rC U/Yzp2/dpuz9rSXTmimpx1cW278rVvmf45eO5vhn8IfiD44tJlg1DQvDd9JpMrKrhNZu1Ww0h9jg h9uo3Ns2DwduCCOK/nX8DeIbu4+Iuk6tq1zcapd6zqt5FrE11LPJc6s3iGG7sdSjuJ4z5hluRfzI XU7gZtwOQK/XT/go94wOh/A/SfDcc2yfxn4y062kjVsM+n6Hbz6vc7gPvR/bE03Pua/Jb9n/AME+ JfiN8YPAnh3wtAJNQj1/TNauruRWNrpelaJe2+o6hqd620hYI4YQAD9+SRIxy4r8S+kdmWYZ34q8 I8L5dz4mWUQwnJQhed8Zi6/PpTvZzdFYda620btqeFxPWqV83wWEptz9ioWiv55yvttdxUd/yPo7 9jb9mib40+KpPFPi6xmi+G3hC9RNRhmSSL/hJtbhKyR+HIWJB+yR/K96w6IVg+9Kdv6U/tSftOaH +zz4Yt9C8PRWF58Q9XsBF4Y8PoqfYfD+mohtotc1S1iwIrCIJstbcbfPeLAxEjmur+J3xB8Afsof B651CysLW3trFru28KeGo3SK58R+J9UmuL5xI6jLl7qae5vZ8HZErYwfLWv58PiD468U/EDxnrnj HxtNI3iXX7r7dfLLBNaJChRVtrazs5zutbCK3WNYUHyhFGOtenxLmOA+jtwTDg3hatDFeInEMI1c fjoRu8NCakoyTa91xXNTwVOVrfvMVOMZSip6YqvT4awKwODmp5niknUqW1gn1+66px6azer10tQv /GPxE8QajruoSav4q8Ra1fLLqF+6S3l5eX12zeTAuP4yEcQwR4ASIrGgRDt7zwD8HPFHjPxn4P8A Cs6w6PbeKbd9bk1SeaKW3sfCNhFNd654gke2LqIra1tb1GQkSLc2/kSIrnFGpfCHUtA8AeANdg1m 7u/iD8Q/7Q8T6N4C0YeZdaf4C0vTryYeKNVmimDWN3J9nupYVIwbRJHDbgyj3v4BeEdZ0D4QT+Ld M1efwx4m+NfjXTvhR4a8TwJLNqeieEUvYG8Sz6BbQsHbUdQ1eS1s1cGOOOOznkeVADu/nfh/gzEZ jxDQo8RYHE4iTpLMcXP28G50mqFdUqkmm418V9YoUW6tanKjUxdKrWcY3PnMNg5VMTCOJpTnde1m +Zax92Vn2lPnjF80k4uacrGL8bPC3jn4hyaP468NeFNZj8AWsA8EfDDw3Bo1+2oW3w98KqljpviW 7EVsI4IdR1OS9c+YUmkuJJCqtGA1e+eNdH1Pwb/wTZ8E6R4htnsNW1r9obVdQtrOWWGRxawaCQ0g aCV1+6ybgGJVjtcK4ZR8hfG/4i6h428aReBPCOp69eeCfB9xZeDPCGn3N9dXN1rl3pAGjv4l1IZz d61f3iyyZbPlpMscYUAg/U37clxb/DH4e/s1/su2k0Z1L4Z+Bj4s8eQxNk2/jHxuU1CazuAD/wAf ENuxznnEwr7rD1cpo4Xxu4ooOtiFSymGUSxM6kPq+JzLM8bhKU6eFpRppxpexwuNr4aKq2p4XDJe zSty+lCVGMM9xcOaSVFUedtcs6lWcItRVtFywqSir6Qjt2/PVJQeM4Axzzkn/OatLL3z7ZGD/ntW MknT8hx+HOTVpJcflyTjB6Yr+Yp079P8z5k2VkyCO3X8jnI9RW1capqg8M2Gkm7kGjnWNR1SKwzi FtRW1sbOS9kUf61/s4WNSc7AHC43tnr/AIe/BX4pfFCK0n8C+DtX1uyuNSl0x9Vji+z6PbTxxwyu 11qVyVihhVZQWYMcbcYLcH730z9hHwH4Y0ODXfjF8VYLCz8MaZJqPjCx0FrSzgtJbi4eZEfVbtnm SDyFihjxbLLPIp8kcqK/Q+DvCjj3iyhi8Zk2T1MNlapJzxuJqRwWDdNyjOT9vXlThVhGEZVKkaft OWMHJrRJ+ng8qzDGRnOhRcaNtZyahC2jfvSsmktXa9kr9r/Dngr4O+I/ib8QNB8GeE7doB4m0u01 2LUZ4Z7jTNF0y5s5Zbi81KW2VjDZxX9vc24JO4uqoAXOK/R/Xv2c/Cfwr+G+geHNZ+LWi/Cvwdf2 Tp8Y9cggNt4t+J93A6m30zTbi6meS00ICW7j+zwRsZEaN5I3YkV4nqP7b/g/4a2ln4O/Z9+F+n2X hzRok0uLxD4okmOqanpsVzcXJMcEJ84bri6upka6nkIe4LmFcla+AfGvjjxT4+1678Q+L/EGqeJN TuJZGW71S5edoYmclILaE4SzgCYASJEUAfdFfo1DOfCrw2yvH0crpR8RuMs05qVavTnisHl2DpNU pVMPQrR9lXxVGpOM6VSpRVD61hpzoSqQozlCr6Kr5TltKoqUVmWNq3UpJzhSgvdvGMlaUk2mm48v PFuLai7P7B+P37R6WOhJ8F/gXFpHhj4PQ6YlpFrPh+5FzqvjHTpgxukv7mSNZ9KikumuRcwSqtzK 5dpXMbgN8HswxgDjp8ufzIx0prP1+u4dj7fjgfpUJfPXtzxnPt+tfkHFXFWb8Y5o8zzar7tKKpYb DU7Qw2Dw8f4eGwlGKjCjQprSEIq9tZynUcpy8XF4yvjavta0r20jFaRhFbQhFaRitkl823dt7P17 /mD+JP4V9MfB6QeBPhd8Xvi9JiLU306D4WeCZSxSVNb8ZRTHXr62PXfb+F7a+QsPutqKHqRXy67j B7jHXnt7Yr6V+M8g8IfCv4H/AAwhdVuH0C++J/iWELtk/tfxtcBNKiugPvNH4c07TmTPQXZxwTXo 8Hx+o/25xHL3ZZBg5ug9v9sxco4PDOL6VKPtquMp21UsLfpcvBfu/b4rrhoNx/xzahH5xu5r/AfN bSH1z3JPf1phk7Ejrn/6x56VWLFjhQSxwAgDEsTjCqoGSxOMAc5omSaCR4Z4pbeZCA8c0bxSRkjc BJHIoKHac8gHpXycaTtzKPup7+fr8jj6Mn34zjpjsR7ZHJ+n51+13/BIH46Xlj4z8V/AfXtZd9F1 7S38TeDdMu5UNvba/psqPq0FiJPuSz6eRKVXq1iGAyK/EUufpz/+r+Y/Kuj8I+MfEngPxLovjDwj q95ofiTw/fwalpGq2MhiubO8t33xyIwHzLwQytlWVirAg4r9L8IvEDGeFviFw5xrhozrUcrrWxVC nPleIwdWLp4mjvytypycqan7qqwpyfwpr0smzKeU5lhcdC8lSl78U7c0HpKPzTbV9LpM/Zz/AILD fBzRfDnif4efGPQ7C20+fxguoeGvFTWsSQpfarpMUN3p2ozrGoBunsZpI3bGXFqpYk5NfmN+zd8S 7T4W/GPwb4n1aM3Phya+l0DxZYmSRVvfCviOCTRddgdYzmT/AIl95KwU5BaMZGK+ubj9rvxD+174 ebwB8ffD1h4x1bwjHN4q8C6R4YuT4Km8WatZ2Mlvqem3l5bwz7tRbTy89vFCkYmeB4VG5464r4s6 p8K/hBcfD7wx4L/Zt8MT+PPEPgbQ/EPiK28Xa/4i8b6jpep688t9pOn2+lW1xbrb6iNPaxlZSrsV uEBUfNn9l8TJcN8WeJGZeN3AOd4fIOH54nAY2FPFYTGfWf7Sg6NOtSdDDYbEYZ1q+IpzxEqMsSpV KU51pLkmm/czSWGxmZ1c9y6vHD4ZypzSnCfN7VcqkuWEJRvKS5mnPVNyejV/VdE/ZC1H4Z/tBz+M pr3RdW+EHw48U6x4v1WW01a0udY8OeGdJsZ/E/hKfxTpV3Fvt7PUbQWCwyiKRJfNKDEnyV8i/DSD wt8QPih4u8X/ABDsp7nwVYW3izxlr9hYT3FpPdy3P2k6JpNlPaxlknuNdvNOiTAGQ5zwDX3r8Jv2 6PCUur6rpv7UvgDwrpviLxPoFh4DuPGPhXw5HNqEPhW3ureIeHviF4WkuxDdaRHbwxICoW/jjTCs pUY3fHnwEi/ZzsY/Fnwz0ZfGnwz1u91r4zyfEvSIv7W0FNE8N2wufh/4Gb5XEBi8R3nmzE4+0R+U smDC6r6mZcA8H57gMs4g8O8RSxnCeQ5jjMyzfAV6Uq2MwFTESoUcBTxmXyjGf1CMaCddOdTDQhLE /wC1yU4U6e08Bg68KOIy2UZ4OhVnVrU5K86blyxpqdNpNU/d97eKTl77ukvx4kkXz5ykbQL50myB 92+FA5KxvuGdyjg554r9f/8Agj34rsLD4ufEjwndXMi3XibwVb3enWojDW9y+i6kkt0XctlJEtbl yowQwLelfj/q91qVzql7f6tBNBf6pcSapOs9s9q0jai7XYnWF1XET+duQgbSrArkYr6//wCCf/js eBf2sPhPfSTeRaa1q8/hS9Zmwhh8RWc2moGOeQLmS3PPdRX4j4CZ7DhLxv8ADrOZvlw9HN6GHqSm uS1HGSeCqylFS91xp15StdqLWt0mn4nD+IWDz3LazdoqtGLb092b5G7ekrn68fta+HoPCf7Qn7Cn iGCJYIrDx5deETjgLbx6zZzW0YPZR9qlx2+fiv1mZVZWVgGVgVZSAQykYIIPUEGvkX9rXw58LtRs PhDrvxOk1fS7fw58XPDUnhzxRpVxDCnhzxFqMjrp02srOhWTQZ7q1ghucEOgdXQ8GvrpSGAIIIIB BU5BBGcg9xX+1PAPDq4e4+8XPZ1qEsJneIyXGU6VOf72lbKKOBn7alZOCnLBc1Ka5oVI8yUuenUj D9uy/DLD5hm/LKPJXlQmknqv3KpvmXS7hdPVPWzuml/IJ+2D8N/+FS/tG/FLwhFA1vpqeIrjWtFR lKqdH14DVbEx8f6tY7opn/pmR2r5mLsTgdfzOPp+FftV/wAFY/hPaXXxb+EXjWTU9O8NWHjbSLzw lrHiPVhcjS7G/wBCm+02dxqD2cMkix/Yr5EyqMQIs4wDX5CeO/h54n+Hl3Y2+v29u9jq9qNQ0HXd OuotQ0HxBp5Yp9s0jVLcmO6jDfLIoIeJwUkRXBA/xW8d/DjG8EeKXiPl2EwEo5JlmZ1J05QScaOH xyhi8JGajrTh7LE06UJyUYTnGUIScoyS/E8/y2eBzXMaUabVClVbTWyjO04X7K0kk3ZNppO6OL3r 6/of8KKrbz6D/voUV+K8i7s8M6z9o+y1LRvEngDSrqzvLPS7D4NfDG28PG5jdIbrTv8AhGre6u7m ydkXz4DrF1qIdwBmRHB5BrT+IOk6x4p/Z++D3xN+02FxZeDV1f4UaxbQ6hZSXth5Ws3+ueFpp7GK XzYHuLO91FMOmdunLKTtkFdT+2Rpeu+L/wBo7x7YeELTVfFGn+CfC+hJcafo+m3Utl4J0LQdCsBq FgoG5ItMspJlMsqlUEl0wPzZz51+zNJo3ivxPr3wc8US3v8AwjnxZ0G80y1a0eLfpnjbQrefWvBu uxRTHElxHe209tgFTJFqjxHKtiv6WzHLIrjvi7hycZywuf1sRluGqymoU/rVDE0vqslJRlCVL6zQ p0JK6dKjVfM1KLT9mrTSzHG4Sz5cTKVKDvZc8Zrk6NOPPFRa+zF6u6OO+DHi0+Dfi78NfE+/Ymje NvDl1O2dqi1bU7eC8LnuPsk0wPsTmv2F/wCClHhk6t8END8TQx+ZJ4Q8b6fLJKASU07X7S60yUgj ohvBp344Pavwbkklt5HQs0c9tKyjjY8c0LkZK5yriRM47EYr+jjx5Anx3/Yxv7i3Q3d54o+EGneI rJEw0ra7o+lWusxooHPnf2npskfrliK/QPBPDPPvD/xf4JcfaVsTgo4zDw6utSp1VorX/jU8Ku+q PTyBfWctzzAWvKdNVIr+8k//AG6MD4J/Yijg+Dvwi/aL/a71OJEvvBXhdvhn8LpZBgyePvHERs5b u0LfemtdNcscchZj718vfsteCT8W/wBoTwLomqhr6ybW5vFfiNpSZPtNhoYk1q7WcsDuWa7igibP X7SQetfSf7Vsx+Df7If7KH7PVsTa6v4t0q/+PXj+3B2SS3/iVvs3hqK6QHOY9ND4VhxtBxVH/gmF p8N58Y/G+qSKrS6T8PZY7dm+YxnUtd0yGUr6ExQsCfQ+9clLIqFbjnwf8LqsL4Ph2nhMVmFN7VMw zFU81x6qK2soYZYTL5J6pYSxCoRlmOR5PJLkwqhOqukqlW1eon5qHJS/7cP0+/ab/Z/0b9oH4fXW iPFbWnjLRUuNQ8Ea66hGsdT2AtplzKq5Oj3YRYpk6IxSZRuj5/FT4UQ6tP4d+PnwA123m03Wr7QJ /FOmaZdgRTWvjj4UXM+p3enkMuWln0JNZiGDtPkIRkHNf0bFl9R+HNfk1+1l4e0v4OftT/Bv45pb pD4a8dakuheO1VE8iS4SBNB1u4mXGM3PhfVVZz/E2nu3UnP7v448BYJ4rKvEHDwjhalGpDLs4klZ Vctx6eAnWnbeph41+RSevs5RbdqEEfRcQZfTc6OZxSg01SrtfapVV7NyfnFStfs1/Kj5K/YO0BPE 37THgiSRN8Ph2z1/xSwxkCXTdKmitHPbi9vLcj3Ar+j3w54n1Xwxfi+02bAYbLm1l+e2vIf4obiL o6kZwcZGcg1+Jn7Avgg+FP2nfjjo0qof+EH0fWtBtpFJePyLjxbawWksUn8SvYWiMpPJV8+tfseW AHUe3fn8K9b6MuUYjh/gGrVV8Pj62aYycpJ2lGVB08La/wDdlQlp5tdTbhOjPDZa38NSVao36x5Y fnFnyr+2/wDA/wAOftW6r4U0j4V+L9H0/wCKvgXTLvxPqvwKv9StdI1XxZomuXMMd5qHgzUb79w+ rbNImSKF1ZQDudVBAbmf2RP2dB+zx4M8QeJ/HWmXPhvxr4me7vtWj8RCKHUfCXg3TpZp9N0jUJgq pDciCM3V8yYQyFF6RCvm2TwPqXx0/wCChninVRc39l4V+Cx8MS6tqFhcXNpI9zomnWj6ZokN5bur RPc63NdNIFYZt7Scdwa/Rv8AaO/ah+Ffhj4ea34U/aD0S88XeE9fsoPDGq3HhyWODxjGviJZlj0+ yaCVZGvG0y21C73PsjENpl3JkUHuyfCcIZ3xTxv4vZ7l9HhrOMkxeOwGDxlatP8As7HSwcPqixk6 SpVKuCqKNJ4aVTDurh5L2tSOGpVIOU9KEcDXxeYZ3iKUcLWw86lOE5SfsqjguT2jVm6bSjyNxvF+ 81CLTv8AhJ+1T+1Br/xz8a6zY6VqEsHwv0u7Nj4Z0f7PAov4LGUY12+kaIym6uriMzKm8LFE0ceM 789j4E0Tx/4z8a/B/wAO3eoSfEPwz4z8N+HPH/jW38V6boeuXGgeGotel0bxO0OsahbyXFtpUVvp YO23kD20VztZEdXNemSfsg/srfGxo7r9mL9qPR/C+pT2H21vht+0BE/hbW495MyCz14Qx29zC0Ms QTasu7buEjBhj6H+Gv7C/wC058OfgZ8X9N0vwx4e8UfEDWdIh0L4deJPBPxH0W4kk8H6/fQ3XiO1 02ZZM28Ruo2uQGEZnW6miWVCcn+dss8PvEviLirNeIM6w1bizAY6NTFzxuS4qOZYarHCc2KWCprB SxChGvBTwlDDYilB03UjD2ak0fMUctzbE4yticRGWMp1E5uph5qrB8nvqC9m5WUleEYTirc1uVM+ WvDkf/C0/wBpPxV8PNY8NfDuG81DSvF2j2PjXS38TQ6LYeB7fQojoc1iNP1OKKHSYfB0KR21wIY2 f7WPNfDMT7/qPxa0z4fWF54ni8N5+DXgN/C0Pw80BLsQ39mbKa8t/hlqltpD582bUZl8SeIL6S9k Q3Vr/ZjIVZkz9E/A/wDYb+JHh34V6vqXjPw/ZeGPjD41+Glz8PtSvvEfiHRF0zQdC00XFhpjaith qImlt7u1Glx3jxO9yY7NU3RnC1U8f3f7IHwB8MHwN4n8b+HPij438C6fFrOsfBvT/Ebaf4N8ReNN D0K0TSbfxR4gu7SSbXLOxTSoYbDTDKdpZI7j95tWv0fL/D/irhzIK2fZ5icNwXiM1liMc8ZmUaeG qwddOWCwFWhOkq+Mq0Iqpi8Xh6NKrWrScIeznVoR5fWo5djMNh3iMROOAnXcqnPVSg1za06bi4qU 5R1nOMU5SelnKKPkz9nfwNY+ArPXf28f2krG1tdFt9X1HWvhL4HuLSHTbj4o/Em8nmu7O70/RwB5 Hhm0vHEzSBfLygxuC/N8CfEb4i+I/ip478U/EPxbete6/wCK9Xu9Y1GZidkb3MhaK2hB/wBXbQwi OKNRwqQqBgCvU/2o/jl8Qfjh4j8GeJvHGo6YIJvBdlf+H/DHhzNt4Y8JaRfalqYsdF0zTEPl2ctv ZW9tDLhQ5aH52Y816B+x58B9M8fatqXxW+JBt9P+EPw236nq1zqZ8mw13U7GI3a6e7PgSadbqqS3 eM7iY7cAmUgfzXndKrxlm2TeFvA1Or/YWErzxdfF4u1Otj8bVpRljM6zKzlGhRoULxw9DnqLC4dS XPUxGIrSqfM1k8bWoZTl6f1eEnOU56OpOUU516u/LGMdIxu+SN9XKUm/T/gB+wnqPxC8LWvjv4oe JL3wJoGpwx3mh6VZW9o2tX2myAPHqd/LqDeXpNtInMSMjyujLIQqkA/Vvh/4BfspfDr7Uy+DdV8e y2DC4ufE/jW5MHhSwmhhaWG1bXtZksNKDSNj5I1uSWZd3Ar4w+NX7eXxI8Wa9qGl/CnUz4G8AWjG y0eS102yTxBqlrCoiF9eXN1HJ/ZyttzDDAEMMZUMxfOPjzxH478aeNZRP4w8WeI/FEiv5qf25rF9 qUUchBG6GC4maO34JHyKoxxgCvoq/HPgzwFCllvCHBceMM1y29OrmmYUaU4YqrG6nWw6ryxEKdOU r8lsHH93ZJ3/AHj63j8jy9KlgsCsdWp6OtUSam1vKPM5JJva0Fp95+q3j/8AaysNGCeGtL8ceF/A HhmzjEUfh34I6TF4x8VC2UErYr4wvoLXQtCkZQA72cV08RbhmYc/AfxQ+MeqePBPoekQT+HPAiXv 2630B7x9R1TWb5SwGv8AjXXpQJfE/iJ2Z3Mkp8m3Mhjtoo0GT4TG2OBgDJ9uemB+Nd94v8L2fhW2 8JqutRapquu+GLHxLq1jb27JBoS6u0lxpNh9rZz9qun0c2k8w2J5L3Ij+frX5RxZ4lcacb4bHTxO J+r5Zh1FVIQqyVqdSSp06EVOajGEtHPD4KlQo1VT9rVoNUVKHmYvNMdj4VHOfLSja6Tez0UVd7Pr GCjF2u46XT9D0Cwv/B/j3xDfC+WXw7F4ah0iS2eJbZtV1vVzbG3v1dSXhbS7bUXTYVKvbjqCRXBM eOfXPJxnGOmPoK9u8F6e0fwa+NGv6lpcl1pLy+BNC0i+PnRRW3iyTXJr2GaKRMJPJDokeqiSNs4G oRnHII8LJ/IfXA/PtXxOaYKGEwPDdVUlTqY7BTqyspKU39fxtKM5XSTvCnBRabXLGOvRcFWHLDDO 1nUg297v95NXenZK3kj6A/Zb+G3g/wCMXx5+Hvw08dahqemeHPF2qS6Xc3WlTQW+oC4azuJLGGCe 4idIjJeRxISVY4c4Ga/Yz4kf8Ebvh7eaRcy/Cr4leJtF16KFms7LxfFZ6vo95MqkrFcXVjBDNZKx AG9UlxnO0ivwP8IeKtX8D+LPDvjHQbhrbWPDGs6frmmzbmAW7025juYQxU52F4wD6gmv7Ef2Y/2g vD37S/wk0P4maDaz6dNNJLpHiLSZ9pbS/EdhFAdStYnDHzrQvOjwucFo5VyAQa/tr6H3CPg54k5T xZwNx3w3hsz4rjU+uYStOdalip4GVKnSqxw9ajUpuLwtaKqON7tYhS5ZRjPl+54MweS5pRxmAzDD Rq4xPnhJuSm6dkmoyTXwSV/+3tmkz+S/xR8CviF8PfjNY/Bbxzokuk+K5fEek6OIP9ZbX8Gp3sMN rqOnXKjbeafLFJvjkXgjrggin/tNeI4Ne+N3j17V0Ol6HqaeEtHEZAjj0nwlaweH7BYsHGzyLAHj jLGv6c/2p/2arT4t/ED4DfEbSrS3j8UeAvGL295elAHudCm0+/v7KO6YDMkVtrVrA6DqBdPjrVf4 Q/8ABPP9m74ZWgvdd8E6b8SPGl5PJqOseKfGsA1U3Gp3Er3FwdO0mcm3sLPznOxTG74ALuSa97Hf Qu4vqZrxLwhw1mVHC8LTzOlioZjj5S5nhKOEvhKKpUKcpVq8KmOxVKrJRp0nLDKo5Q9pCBvU4Hxz rYrBYWrGOEdWM1VqN35FD3I2im3JOpNPZPlvdXSP5tvA2meGvgemi/Ez4kOmpeMNR0Ua98LfAGnN ZX1/ZXl3DL/Yfjbxzb3i+Tp2lQy+VcWdlIHnvGRJDGsSkt88eIvEes+LNd1bxN4ivX1PXddvrjU9 W1CRY45Lu9unMk8zRwIiICxOFRVVRgAADFf2K/GH4D/snz+G9f8AF3xf+GvwztND0+we51nxHqWl WWkz21tbW6xR7NTsxFN56wxRxwRxuXJVI41PC1/IF8TLrwTc/EHxlP8ADiyu9P8AAcniPVT4Qsr+ Z7i8t9A+1yDTUuJpPmkk+zCMktzzzkivx/6QvgzmXg3g+G8nlxJl+PyfHVK1SjhMPKtDHTqRhCNT H42lUjySU7KlSnGfJSV6NKH8WpPw+JMjq5HDC0JYqnUoVHJxhHmVRtJKVSomrO+yd7R+FLdvjS31 BzzjJJ9j9MfrQG9cnjuTjr3qDdyck9++fX/P+eE3ZweR68/TJHP/ANav5gUH1PlD0L4Y6fqus/EX wNpGh3FzZatqPivQbPTruzZlu7W5n1K3jjngcHiRC24Hp8vPFfoZ+0f8Q/CniDxJ8R/iH8CNFuoP iX4Y1lPD/jnxZPqEo8T6TpXh+AaB/wAJT4Q0e2/cWulXwgjW8uE8ya0lAKGGKZSPmr9iqDSbb4xX njTWohcWvwv+H/jr4i29s0rQtcar4e0C5fR1SYA+W41Oa2cHqDHwc4r550Tx54i8PeLT400a+ltt Ze/u72WR8TxXi38kj3tnfwy5W+s50lkSaOQFJUkYMCCa/XMhzl8LcB0KdVp0uMsyruc4QhPFYKGW 4enSoY3Byqe7Sr+1zDER5oOFScKMoRq0XKFWHs4ev9Uy+Kl8ONqyu0k5U1SjFRnBvaV6ktmm0rXj oypp8Oo+Jtds7NXuL/VNd1SC3EsjNPdXd9qN2se93c5llaabJJOSWyTmv0I0j9qTxh+yf8XvEfw1 0S4k8e/B3RLKDwD4r+HGuXZk0TxDBb6ctp4guLIK8i6bqT6hcagVlhypD7XVlJFeNeBdP8La14hX 40fDuaHw/rHgux1HxT4g+HKW/wDaFxp2u2FnNNYX/he1lDnUfC02oNGZFIaXTwxEm6ILKPB/BXiT QD4gv4vHVsbjSPE8pi1TWoUL6z4eupLr7RHrulSHJM0VwcyxEHzoS8fysVdebJcbmvAjy/MMjzv+ zc9zbHKrhs0pVL0Hg8PSnHkmnzc9HGV8TyYuhiqSjTWG5MRSlepGM0KlXLvZVKGI9liK1S8aqfu8 kVs11jOUrTjNactpR3R9ofHH9n3RfiD8PZf2nP2eb/xTr/w+tltLHxt4A8Ui5uvGfwsnRUt7Oyiu GTGu+Eo4xHFbXEX+qjRUcAKSPiHwrr134V8UeHvEVsXhvPD+uaZq8HJSSObTr6G6UEYyrb4sEfga /XX9iP8Aazj+Gvibw98A/jdq0Unga6ku7T4e/EJGB0S+0fXpiG0bXvPKw33hq5kbzIriUGfT7pSj ELvQfGP7TX7Lvir4Z/tOX3wr8OQf8JJH411ZfEXgMWciyS6j4f128mubRXeQhGniVbiN9rEHyNwP zCvsvETgfLcz4e4a8U+Am5Zji8XRwGe5fQounLLs8ko1KNSjhlWr1KVDH1FU+rxpyqUHUp82GlTh WjhMN6GY4GlVw2FzbLnepOcaeIpxjZ0sRo04xvJqNR35Um43V4tKXJH+h79qu3i+Lv7GfinxHpKf aJbjwV4d+JWkbB8yzaS+meJsrx8rrbR3IP0Ir6l+H2tx+JfAfgvxDE4dNc8KeH9WV1OQ32/SbS6J Bz/elNeJfA2O58afAq+8C+ItHm0gaZZa/wDDSSyuo4Y2m0mzsW0W3ulgjc+VC9u7BAwU4i3YwRW5 +y5pnijQPgZ4E8L+MtLvdI8Q+ErG78LXdrfpsnktdD1G7sdKvFGTugm0mKykjYHBWQGv9d+GZ1sV xhlXEvsZuHF/DWHhiKns5whHFZVi3NQnde5Oos2xHs4yd5RoTtfkZ+xYVyljaOJ5XbGYWKk7NJSp TvZ9m/bSte11F9tPlf8A4Kn+BrTxT+zOfEFxFM//AAgfjLQNbnktlRrmLSr+Z9G1Uwb+PM8u8gIz 8pMYzxX5afBT4QW/xI8M33gKbX7bxX+z3r07P4e+Jdzpwh8Q/B34jagtounaVqNtO4NpdX0qR2s1 rDJJa3ImW5R1MbMv9CP7QXw9X4q/BX4k/D7MSyeKPC2oafbSTYEUN4FW4s53LcKqXUMTE9gpNfz3 aH8aPF2nfGbwZ8AP2eT4T8M+C/AHiCz0S58RvDZSDxedIuYo/E3i/X7jUpWtp5rlkvRGEQO8LxQo WOyv5I+k1wvw/lHjDkfGHEOEnicp4twGEy36vhoSqYvMcbHEV6NTBul7WnSng5YOdF4uVRwqQlDD fVa9Cu4VI/IcUYXD0c5w+MxMOejjKcKXLFNzqTUmnC11FwcGue9mrR5ZRlZnm/8Awxtcf8+3jn/v 1oP/AMsKK/Tbz9U/6Cenf+C8f40V+VrwF8O9P+E2t/4FDy/6eeh5X9gZb/z6l+H+f9fn+VX7Vf7X y23i/wAR/C/RPAGi33gm80pj4pOq79N1fxTN4n0vTNe06e11rQVt7vStPt5JrMvCZJPthtcTMVIA +UfDnxY+A2mah4C1mL4S+LvA3iHwJrum63H4g8H+OI9abXmsdYh1JodasPEmnL0jWRIpYZUdYwsT B/vDjvj1qV54qtvhJ8TtSvpr/V/H/wAMtMXX7qaHymm8QeCdQv8AwTqF00igLM88WjWczFcfNMxI Ga+e/O75HfPPJr8V4w424hxXFWY47EVqGPpznGphfb4PCTdLC1fZ4rCxoydOdSgowdKbVKqn7Rzl KUpVKjl8jjsxxVTGVKk5RqJtShzU4Nxg7TgotpuGln7slrfW7d/tLxt8CtQ8W/F/4palpeteGvA3 wyi1tfFNp498c3raF4WbRfGT2uv6Ra6XcCBzrGqnT9WLpZ2okfbasGKcV+yf7Aur+EvGvwi0LwT4 Q1TWPEOj+DfFuq+A21PXrWzs7vVLd7xdSS7jsLWRxaaVLbaq/wBmjdmlEKASnduA/Cj40+JdW8Tf DX9nG+n1q51LRdM+HeqeFYbB5ZDaaVrvhnxNqFvqEYjbAF4+k3WgMzkFjGIwGKgAfpR/wRS8XLJ8 WfH3gC6kJt5NIsviBYxlsotxoPn6ZqLKOzmLUdOPv5HPQV+ueCmZ5Vl/jPlWUYLL1QpcXKUa1ec2 21jqVPMKNClTjL2dOjTlyUot+0q1GlKUqak6Ufc4fr0Kef0KEKdo47SUm9f3kVVjFJaRinaPVvdt bL5c/wCCnHi6TX/2xPiTo8cJtNL+H8Hh7wBodkoxFaaX4e0SyjhSBOiws8zuuOMSVq/8EzfFdvpP x71bQLlwh8XeBNWsrQsQoe90m7sdZSIA/ec2tveEd/kNbP8AwVT8EvpXx1034m28LLZfEvSp1vpg uEOv+G7g2k5dxx5j6TcaWeeT5TEdDX58fDL4hat8LviB4S+IOiEHUvCmuWWrRwlyiXkET7L2wmYf 8sriye4hb2mJxXwnFGZYrg/6QOc5/madRYPPauLm0rOWDxNZ1Y8i/wCwOslFLRaJW6edjK8sDxNX xNbVU8S5v/r3KV9P+4ctD+nXUfiJcyfHnwx8KdLkTyLb4feIvH3i8hVZ0hk1HTtB8K2Zc8w77qTV Z2xgsLeMcrkH4w/4Kg6tpdj8LPh3ZXVta3mo3njm9nsIJ5Jo3S3tvDmoQXd1E1vIrjy572wPXaW2 hwVJB2v2PvHtt8Yvip+0v+0RLHcWPh++n8LeEfDov1xPpnhrw9plxqc8MoVmCOIvss0oU4LyE8k1 +ev7cHxbt/izrvgvxFdyapYTyR61LoHhWa4tAnh74ey3Vsvh3VdU0tR59v4o1tYp9UPmlVXT57GJ VO3ef3zxH41o4zwn4ixvtliXxficRHARl8KwOGxtDCRqpSTXLOFGNaMPilUxDny8sarj9JmuYRnk uJqKXM8bOapJ7ezjUjBS7aqKkl3lfVJtfoj+xXqnh/xh4y8X/EPSrC7gv/FHwl+FK6tfu9tFbanq ukLqPh3Xp5rGKMtHq39u+Hr5ppTMVmimgcRI5dm/RUEAgnoCCfp3/SvzY/4J1WN74b0D4q+CbprX U7Hwr4l0K78N+JI7ZreXVvD/AIz0KLxFabVfJWzaMwXCJuZUlvpgOeT+kJc+gx781+p+EsasuA8m r14KGKxUsVUrpRUUsRLF1vrCXLdNKsp2nd86tK7vc9nJFzZbh5zX7ybm5WX2+eXNto/eT1677ni3 hHTNB+D3gXxp498WJbaTe6nf+J/iJ8QdWPmT3Ege9v72zgnkUM8/2XR2tLaGJAQrApEvzAV+Y/xP 8K+LPjj8Q/CngyfwbqPi3xcvh3xD8ZvFENr4in8IeDdXOvvE+iaLFqN5pTXL3Vr4UsfCmkow+zeT NPIkiwu8kh+tP2r/AImadrfivwF+zTYWy6rqfjbXvCev+NLNIpbgR+DdP106pNpZSBgVvbtNGkYK T80ShSMTZHwza/EbUfCnivxXqknii4+KOoeL5/H1l4yuLnXjH4V+HOm+ItC1QWfgqHTNLv3uNI8T yXFvZ2VzqcDS6ZAsUVtaSTyIXH5P4l5nk1arg+E3K/DeUVaOHxUqapS5sQpwxGKhOE6dWM6yTwrl Vhh6rp+3xjrqMHK/j5tVoOdPBX/2WjKMZtKOsrqU0007te421F25qjlZHz54Q8P6VBoXxOn+KnhG 6h0/4c6honiLTNNtkdme/m8QPoVz4Ai11Lp3j0e7a6tQ8gnlMMWkSSxN5zAt+hv7Efw31TX30H9o rxN4q1S01DVI/EVv4P8ABmka/faRpV3d6RdT6Sq2+hpdJCmg6fodl5UMBLxt9qMsx/coX+Lfhj8C W8Y6L8VtS+HniCK7+H2pfDOZpIPE7XGna/oviIyab4r8OaVNbLbeV4juS+j33kXVh5nmxRSNLFDJ mOv1d034L3Vxp3w6j8ReKGs/hN4I+H+jtZeDNBVNBn13xTdL5zT6lqpkj3wy2U9raxWiuvmXLtIW VmXP5/4UcJY6WYYDOquSfW8JlVJToJVaahXxX1+r7PGVcVFRVShgsLCUabjSqclS8FRb5qT8zJMF UdSniJ0OeFGKcfejaU/au03NWvGnBNK0XZ6KO8Tovil8fPBXw18Ia58U9Y046lpWt6xoGneFvEL6 xHrc3jTVbkv5kkNpHP5lv4d0iSymaSEyBZlsZTBGcxu/4ueOfBWmfEfxJ4y8V/D2407Ttdm1LVNa 1z4U6hqclx4h+1TMNRu774fXRgI8a6DcfaTc20Y8u+iikKNDKiCU/anizwh8Y/2jfh14x8LeKvh9 pfgvwzo2qN4j/Z71C317wpDpfhpNFtodHtvhz4kaw1R40OpaNOhtLhiT9sLbj5RUj5OsPhv4M0gP Y/EfX9d1j4g/Ce2lk8SeBfhtbw2mupo9vPFHY6ZJ4r1mWCOfVtM1G5X7RLpsF7JDYzgoXS0Lji8W cbnPF+Ky2ljcqS4XlSdWhVxNL+zav1pzq/W5QnUpxq83J+/w9JQxHt8HCNSrTq4rmnTnOqmIx06S nQ/2RxbjKa9lLnu+dq6Ur296MbT5qau1Keq9P0X9kTxz8V9K+Glz4bvdKtNDg8GeC7ObVb23lsr7 VZ9f1TVdb8TzWdow3TRaFDqP2a5kfAM0EcAG58L9KftIaroPhf4Xaz+zL4L8MeJdG8B+B9C0yTWP iWAtrog8VWLx6vHoesx3EaSa1BfTPAb64tTK0N1fwjy2ijcD2/xV4q8G/srfAPwrruupqEXjK48M w+DdBvkey1vxktzrktxrt1uuNQkhh1R7Ca8kurlyI45poFB2+agH5B+KNC8R/ErULzVtD+K3/C07 a2e6n0628Y+Ijo/j1Vug1zLax+FdevSLjUZJIioi0ya6jldU2YLKo5uL8Hk3hxk1fI8gy/65xlxZ gKEMzhCvSnVo4KpQaWDhTqReIpwqvWv7CnKq6WHo89eFSrGsGNhQyuhLD4enz47G04qqlJNxpuPw JO80pfa5U3yxjeSlLmPG4C0jIqK7yOyqsaKWd2dsIiKoJZycAAcknA5rSeK4tbia0uopbe5tpZLe 4t5ozFPBNC5jlglicAxyq6sGUjIKkEAivavB3wf8VaBZaB8TfEtpFpui2q+Ltfs9HuWnj8RzTfD+ xmv5JbzSJIFex0460mmwNJIQT9rUqpDA14U11NdTTXE8jS3FxNLcTzOxZ5J5XMssjk/ednYsfdq/ kvM8jxeV4XC1cww9TCYjGvmp06keV+xdOlUhVafvctVVouGiTim+qPk6lGdKEHUi4SqapPT3bRad t7Pm09DYs42uri3tUdUa6uIbZXk3BI2nlWJXfYrEIC4JIBOBwCeK9K+KmqQ6l4816K1gt7ew0KS0 8KadDbg+Wun+FLODw/bSGRlDzySDT2ld3+djMc4GAMr4Q21hqPxQ+H+n6tZRajpmoeLdDsb+yldk SS0vb+G3nYyR/NGyRyNIGH3TEG7Vl+Lr4X/i7xVfrerqK3niTW7lb5YBbJerPqV1ItylsHYQxyBt yoCdoYDJrB0pUuGqtZTjy47HQg4/b/2ahN31+y/raty3TavPlap8ztbCt3/iVErdfci/w9/z87aX +qPAmgWM/wCx18YtX1y+1S1sk+I/hS50e3s7GO7Fzqmm2EtnENzyqLWzkuNYjjuJyMItptXfIQtf GbN6dR+XfI59/wCVfdK2N4vwV+O3wx037Rrmj/CzQvh3rF1Ppt2LO1bxTda9fX/inW5Y51Ml1p8C X/2Y2+QHGjxyjaw5+EiQ3KkEcjcpyDjGenvmvp+PcNDDYTgTDQocjwuUOjUn7z58RRzPMaeJXO26 c4U60Z04Ok7OKTfxI6swioQwEVGzjR5W+rlGrUU+rTSldK2liNmPv9c479vX/wCvXtnwZ/aS+NH7 P2oyaj8LPHGpeHormZJtQ0Vil9oGqMmADqGjXYaGdsYG/aHAGAwrxBj1zkevJI9cgdun619rfDj9 hP4x+Kf+EM8W+K9Ki8N/CbxJolp4vk8bpewahaz+HjLE02nWi6e0jQeIpLYzeXBKqbdjOx+XB5eA sq46zDPKOJ4ApY+Oc5a4T+s4B1qc8JGc1TVWriKTj9Xo80lGdWpONNJ2k7OxGApY+pXjLLlUVelZ 81PmTgm7c0pL4Y3dm20u5+nnwJ/4KAfEz42fCD4z618T9L0nwBo3w78HvexfFLwXHdDWX8RNJCiW mi6DeySRT6strKXyjiONrmPzFCsCPmm5/wCCyXxg0/VriHQfAHhTVPC9vBFaaUfFkt/L4luFt1Mf 9oate6TcQxNdzAK7xpGVQkgO5ya8a8Jap4u8f+Bf2jPDfw+8DeINI+FPhD4a3mk+APDOn6feXtu0 j+MNCn1bXNR1cwAa54nu7C1muLiYuxEKrHEoiQA/nl4b8K+JvGer2+geEfD2seJdbvDtttL0TTrn Ur6Y9CVt7WNmAHckADHJFf0txd49eMmXZZwHhMh4zzDFY/GYavCrmCoL2mZ16OPxFCMaUJUuWpQp K0aMpUViq8HTq4q0nTpUfpcbxDndOll8MPj6s6k4yTqKOtWUako2ScdYrZe6pyTTnrZR+lf2nP20 /jP+1Pc6dF45v7LRfDGkZfT/AAZ4ZF3Z6CtyxLNf30c87vqV9ghVklZgirhAozn5k13wv4j8MjST 4h0XUtGGvaVba7ox1G1ktv7T0a8Li01Sz8wDz7KQxuEkHyttOOld546+BHxl+GNvYX3xG+GfjLwX p2o3UVnZ6j4h0S9sbGW4lOVgW5kj2ecUyQmdxCkgcGvq79trwRJpugfCfxhf+KBqF3b6Np/wt0zw 6kJt7fStC8C+FfDsn9oWkcoEhjudW1a/YkhV4BXJY4/GM4yvjLi6jxzxfxvicwxPE2RrAVK/15ez quniqkqSlUhiOSpGnCMIRo06MLe/G0VCLa8OtSxuNjmGNx8qk8Vh/ZuXtNJWk2veUrNJJJRUV12s fnruA6k5AyeP58cdRTQ3GSfwxz+fSm9++PTPoOnPUemfSkyc9TxyOf51+Wcr26/1/W55B9ffs9Wp 8PeEP2ifE8rJqFmnwGvbEz6ZIsiadf8AirxFo2lWdnqUskQNtd8TFo1JLKMbiCcea/Av9nb4t/tF +J08LfC3wvd6zNCYjqusz/6HoGhQOyr9p1fVZV8u2Xk7U5kcjCIxr7G/4Jw/COw/aF/4Xp8Dta1T U9C0LxV4a8Ja1qusaPbxTXyQeHfE8Ex0+KS4/dw+eJz8zZx5OVViMH+lv4PfBr4e/AnwRpngD4ba DbaHoWmxr5jKqPqGq3m0LNqesX2wPf6hIwJaR+mdqBUAUf3F4K/Rrr+M+RcE55mmZPKeBMro4yOI 9i4vGYjGf2livaYehzKSpU/YxoTniKsZWc+SlTm+aUPvci4XlnmHwFerU9jl1KM+bla55T9rK8Y3 vZcqi3J97JPdfmD+z5/wSJ8F/D270bxZ8TviN4j1/wAX6fJHdpY+Cp28N6JZXAAJh+3yRvdajFyy uCIUdSVKlSRX2Xc/sAfsgXt5d6he/A/wtdXl9M1xdTSS6snmTycyyrDb6ikcJZssQiKuWJCjpX2L TXdI0aSR1REBZ3dgqKoGSzMxwoA7mv8ARLh3wK8IeFsqp5RlvAOW1sHTfO3jMPDHVJVOXldWdTGK vLnlFWk04q2iSikl+l4bh/JsJRVGll1KUFr78VUbdrXbnzO9v6sfHet/sHfs16n4YtfC+meBk8L2 +lXc+oeH7/RLqR9R8PX1yQ1xPpU2ri6EMcjqjPEytEWjDBFf5q534nfscaP4x8O/C7UtS17WfFfx I+CL3Fz4W8aXyWcOueINLs0uLuw8O6+kKrFekXItkilUJsaIMVAd8fVd/wDFX4YaVK0Gp/EfwJp8 ysVaG98XaBazKw4KtFNqCsDnsRXS6R4h0DxBaR3+g65o+t2MzFIbzSdSstRtZXXO5I7izmdHcbTk A5GDnpXVW8OvCvNI47LsNkWW0p4ulRpVaWEVKkuXDVadbDTlh6DjT9phqtKnKhVlTdSjy8lOcYSl F3LLcpqqpThQpRc1GLULL4WpRfLFpXhJJxdrx2TSbR+Ln/BLLxbqfhHx98Y/hH8SNd1vSviPr+r3 HiH/AIV/4o0zU7bUreTSDIL7V7bUbxilwJoLtg8QCkC0WRS6k7f06+FyeJdA+KPxr8L+IfEVnqum ajruleOPA2nS6zDd6zpOg61psVtq1lLpjN51jpUWt2kgt2I8s+cwU8Yrr/Fnwg8FeKvGPhT4jT6T bWfxB8FXTTaD4rtI1t9T+xzQyW19ompTxAHUdGntZpUaGTdsLCSIqwO7yjx94Z1jRP2nvg18TtIt idH1zwv4u+GXji4WOcxxW7iHX/Cs108MbBD/AGtFcRRvJhQZNpZdwr4zg3grO/CrhXhrIsVinxBg uE86jTwmJoyrU6lbLM3qSw1SWPozlWUquExOOnXrRhJYf2dGlXp+y5ZUo8WCwNfKcJhcPKf1iGEr pQlFyTdKs+VupFuWsJVHJpPltFSVrcq+oL21ivrO7spl3Q3ltPayqSQGiuInikUkdMq5r8O/Cn/B L3Qpfii2v6trt7pulad43S7HhzQdRXVo20+Cf7dFHqMlwlvfaak8hQgmN1iRGzIysklfubXyB+0R 8KviJqOreFvir8F/EUGh/EXwC2pajq+ltpMV5b/ELw28ERudBlgLqj60YbCK3tpHYNi4Kb0U17Hj P4ecMcY4PIs74g4RlxjU4Qr+2p4WlOMK8qVWpQWIdLmlD20qcKarfVnUpxxCpOnzc7gntneXYXGQ w9fEYN414OXMoppSs3HmtdrmaS5uW65uW172Pgj/AIRHwL/0M9n/AODBv/jtFfMn/DTXxp/6Nx1X /wAJyX/5Eor+Iv8AXvgH/oEr/wDhmzPy8v6ufEf2jl/8kv8AwRW8v6/4Y/HSyuNT8dfA3UtMMs9/ efBbXE13Trc/ObLwH43mjsddWMdRZ2viyDS5iOQh16RuhOPDDN9PXjv7da9o/Z1kub3x7J4SZZ10 f4m+H/EHwy1K5Fs0lrFc+KNPf+wHlkxsSRPEtrosiBmUnZx1rxW6hjslNvO9xFqtre3NnqNlNEFS I28hjJikHIYOjo6thgwyvAr+Wsyw7xeW5Pmck/ack8LUlJ6zlhfZqm1fdRw1ahSiu1GVvhZ+dVVK dHD1nvZwk315OXlt6QlGKX90+g9ElTxF+zV4404wrPf/AA5+JfhfxXbTFAZrPQvGmmX3hvWBG/UW zatpehFxggM6twSM/pD/AMEbPBOt2fxa+IHxS1O0fTfDVj8H/Fdpo+pXkkdsusXU2p6bY3Z02F23 3trbzKqTTKvlRyssZcyHaPgT9j2/0DWPibrvwx8SaTYar4Z+LnhTVvDS6FqmpzadbXviPSpI/FHg ixk1eErLAJNf0m2tmZCrut6wHJFftD8IfDei+El/aXvrrxL4N0PXPBv7GNl4W8RWngTUbLUF0nVt DvZ7+6u9G8MWLGPwtp8V60VjFBczLc3c8RuZIxl5H/f/AAV4ep4ziDgzjiVRVo8Ne2ozp39m3icH SxWJpSnUlHl5I4V0FTV4uThLmlBQhCr9Rw7hvaYnL8xc1L6nzRa2vOmpzjdtWsoONle7s7tJJSr/ APBQf4YP8Rv2dvEGqWVsbjXfhzeQ+OdPEab5nsbNJLXxHAmBnadHuJ5iB1NgvHFfzfed7g8enX3+ tf1rfD/xNpPxX+F3hTxS0Md5pPjzwZp17e2kyq8ckWs6Wseq2E6EYysst3C6kcFGBr+Xn4+/C+++ C3xe8cfDm7WUW+hazM+izyAj7b4b1D/TtBvFY8Pu02aBXI482N16rgdP0lOGqVfE8Pcd5fH2uCzi jDD1pxWjmo+1w1Rvq6tCUorsqEV1K4uwqlPC5lQV6deKjJ20btzQk/8AFFtf9uo/T39lXWp/BP7A nxd8V6dK8Go3fjTUbaOeGJZZUlvbjwj4ejZYWUiYiK7fCkENnGOcV+XXxj1HVL74sfEm61u6N3qk vjbxKt3OZo7kFodVuoIYY5YSUMUVvHFGip8iJCEUBVAH6ofsQanpV9+xt8QrHWoIb3SNC+LOnT6x a3DTLG+mS6l4I1Gcg2o82OVVSV42QqweEfMBk1+aPjj4b+NPE3xI+K2o6D4X1C28P6f428cXUmsa vImkaFb29vrOsTpbjXtceC3uryRLWZYYUkeaaQBERmNfI8fYHF43gTwwp4HnxVGWW0EsPTU5ONSn PGe1qyhFOPvOXKpaNckk7p3XBmcKs8ryVUrzTox9xJvVOpzSsl+O+jvofsb/AMEwdZ1LXfgv4qn1 WZbubSvGFv4csbx1/wBLOj6VodlNp1hcTdZ4bYX88cG75kiYRZ2IgX9HdR1Cy0jT77VdTuI7TTtM s7m/v7qU7Y7aztIXnuZ3bsqwxuT9K/NL/glgoPwD8VzBkJm+JmqEgMpddmg6Cq+Yg5QkZIz1HI4r 3P8Abx1+88O/srfFG6sLyWwub+20LRBcQO0cph1fxFpdpdQI6kEeZaNPG2OqyMvev6k4CzWWR+DG U53VTxEsqyqvinGT1n7JVqqi3v73Ko387n2uWYn6tw9h8RL33RoSm135VKVr/h1Pl3xl4R8Q+Pfi H8X/AIzaadN8P+Ibr4SajYaBHeTS2qabe6bqXjX4dPq91dCORoZjo8ENzCkLPI08kWFA24+M/hj8 FZvC3jK++JHhnx74UvvDfwtZNav7bUdQ0b+2dfsdOhig1y01HTLu9j0/RLW7u3vLe2tNRvPtNxH8 0dvP5cjD6ltLjTpf2XdN1P4n+L/Cngnw58RPC/h3RbrRvE1lc2lx4gsfD+s6nq0s+kaLBci7Pilt cuXju9Qjtii2rLcJHNI0deVeGPCvwt+LOuaVbfBDQ5te8DeHNama/tvHfm2Phb4X2EUhfT54vBFn cwP8Qtc1GKG9uI9U1W+Yxqrpc28EVvtP4Jn+V4LM8wyDGRw0KubYpf2hGk8W6eMqYvGVJ1fbww9G M5KnGKoV6tRwSajTTVPDU61dfNYmlSrVsLNRUq8/3qi6nLUlUqScuZQim+X4ZSdv5doKUj7E/Zn8 XeBrvwxFe+Eray1nSbn4hapb6VHrulafolj8G5NN8Pz3d3Z6xrEE8MWu25XWLxrE2cIhhjvLmECJ CzL1X7V/xOsvAfwh8K6j4oEbeLvFaWPhyw8XL4auNY8LaN4i0PU7XW7zVP8AhENQnjDaebvShPay bZLgRwQPGk6ptP5t6x8YNDs/Avis/EBJbnXviV8TviLeXa+B7LQZjf8AhEW+h6S+n6R4iWcWGjaW dW062E0tpbXMl8uj+QXFu8vmeo/tOaxJ44/Yp/Zp8ZWR1GawtPEur6XfS6nqLarqMM4ttWsLVNVv /KiW+vj/AGa4klESLu4REU4r1Fx7KPBHFWU5c6U8flWUrExUVKPuVMThaM5Sw6c5UZKjWVSLeInO hzyhQdKjSpHRHM/+E/G0KVnVoUOe2q0c6cXeP2Xyyuvfbje0bRSPhLxj8SfHHji+WTxR4tv9bjsJ Zk0+O326XotspuZJzPpeiafb29vYeZMxkytvHISwL/MOPt74IavoP7QXiXwX4k+IfheD/hJvg9f2 niX4nfFJ5PsWgax8KvDWkz/Y/wDhNreOVP7Q8W/2lZWFnbTxEPcxI/2hZQjA/EXwj+GfiX4xeP8A w98PvCkDS6prl4EmuTGz22k6ZERJqWs3xUfu7O3tt7sT95tqL8zqK/RH9tjxd4O+Dngvwx+yx8K7 SysPJ0jRLz4latZQQw6nqttYCS50TS9ZvIR5lzcXF9Nc6jcJIxCCaFAAkhFfhfBtHMaeWcQ8e8QY x1+HcsqUIzo4le3/ALVzCEva4TC05VudxlSaU6+IjerRws6sYO1WR4GXqoqWKzLFT58LScbxn73t qqd4QTlezi9ZS3jByS3Z4D+1b8cYvj94k0vxpp/iCVNAtZ9X0LRPh7dI8d74Xs9Plt/J8QS7F8m4 XWIZElLA+dA9o9s4McUcj/LUbcqw+8CGVlGGVwQQVb+HBHGPzrJjbPTjPIH49fbitSyj8+4t4AQG uJ4YFZ87QZZUiBbH8OWyfbpX5PxJnWP4nzvG53mUva5jmc1KrJOVpVOWMbxUpPkT5bxhFqnTVoU4 wpxjFeViMRUxdeeIqvmq1XeT7uyWl27LyWi2SSSS/RHX/Gtn4e/Zs8D2/jE3nia+1rwvbaTp9pPd S22u3Fxq+tS+Jbyx1HxCytcJ4Ij8N6Z4NDW9qyyzO/kvJGGLj4z8U+LdQ8Y6rHqt/ZaNp32fTtO0 ix07QNLg0nSrHTdKtUtLO2gtoSWdhEoLyyvJNK7FpJGJr6Q/a5Wyg8T/AA98A+FluLzT/C3g68a3 srKJrlIQuq3+lubVIYzI9rFpvhm2BZwzJHBukc8tXyFGwGMHOBz9D2Fe/wCJ+Y4/+13kEq3tMJkl DCYRzWvtqmGoQvKdV3lUcJ1ZxinLlStJRjKTv25pVqe3+rOV4YeNOF19pwir3lu7NtLWy3sm2e6/ Aa20+b4gR6jq1teXWm+GvDHjLxXcLYzfZ3ifw/4Y1O+tZZJfJcpD9tS2QlQG3SqVOQAcb4V+Hp/F nxA8JaNBErQy61Y3WovLg2tno9hcJfapeX00uEhsYrCCYySSFUVRlyBVX4ayPE3jq7SYRJbfDTxa sincDOt+lnpQhUL94+Zfox3cARk5yBX2T8GfgD4v1H4TWviv4c6Nc6p41+L+j3ngezvtQUwaL4W0 CXV9Vj8ca5qLXlsUhs5dDs9GsrWVBI0z6lcNbgspxycJ8N43iZZDgMFl88bHLJ4zMcRCjDnr1qSq YKgqNOMYuUp1atOnQpKV4qpWcpOMLtGCw08V7CnCm6ipOdWSirylG9OPKkk3dtKMel5anWeCdT8Z Xngz43/GfwVoPiGw8UfFvWvB/gvwhf3K6Bf6brviXU/FrLrel6Ho9vYLANNhjthAGuVkUwsfN3Mz tXzDP8WLK41C40zxx8Cvhz4u8aJqFxot9qVpbar4c1K6VVk02ewTSfB93DZPrSXCjyLyC33o0YPl ysQ1fb/w6m/Zz/Z28L+N/hX8SvjtceMdV1oiy1fRPDena5JYeCb5La6tL4+HJraCX+z9f33tyJLp HSVfKUbFYNn481n43+Cvh8b7S/2bfCFz4WeeSVLn4o+M2tfEHxFvoMgIuimeA2/hC3PLBoEN2xOW lRsiv0biuCynKOFqmacZ4PAYvD0MQsyy2NTA5zXhjZ4zEYmrOOX0qdTBxxNWeIb9viMRh3RoxjRn VnWhL2np4z91Qwjq46nCcYyVWknTryVRzlOVqSTp87cn70pRcYpRbct/S9c+En7PPgXTLXxd4+i8 Q+DfEMGh6tr8PwA8ReL4dT8TeIZJYtPPha3vdV0bR0l8MWE1xLemS3utt5JbIkgKYcV+1v8AwTg+ Mtj8ef2d73QNV0Dw/o//AAgWu3PhUeGNCt5rbSdO8OTwpe6BDbJNcPKWWN7lDM0nmO8JckMTX8tW qapqWt6hd6trOo3urapfzNcX2pajcz3t7dzt96W5url2eV8ADLE4AAHAAr7a/YV/bIuv2UPHWoR6 xph1n4c+OJdNtvGFtAManphs3kW01vSxuCyXEK3M3mQtxMhIBVgpH0XgJ435BwZ4r5ZiMVl2G4W4 HzWjXwOPnChCVSr7WEVQxWNdKnGKjCvSoudLDUqWFownWnCg5yqTn1cPZ9hsDm9Kc6UcJgKsZU6j UVd8ySjOpZJaSSbjBKCTk1G92fur+2H+zzoCfs+fG/Xfh7ba7pnii48CzPPZWOv6w+najbaRf2Gq MRpUt40NvdRWljMEeBI2ZGZH37uLv/BPr9mHw/8As/8AwQ8Navc6Tbf8LK8f6TZeJPF2tzQRtqNv FqcK3enaBb3DLut7KCzlhLopUPM7s4O1cfX3hrxT4M+Lfge38QeE9Z03xR4Q8WaVMlvqFlIlza3F re25imt7iPOYZ1WUrLDIFdGyrqDVjxb4z8E/DDwxP4i8aeItG8I+GNGtUSbUtXvIbG0iht4gkcMA c5nm8tAEiiV5GxhVJ4r/AE/peHvBFLjbDeLNOOEp08DlDw+GqR9jHB0YVa9TFVswpzVqMJ1aVTke Ii0nTlUk5Pnk3+qxyzL44+GcRUIxp0HGLVlCKcnN1E9k3F25uzbvqfkD/wAFi/iPbQaF8FPhDHOG uPEHi4eMNYtgw3rpulyJpWnlh/AHub2+wT/zxJr4l/4KCaZfazo82r299aalaeAPirf+FNShtnil OgWmu+CfC114UsTcBt1zvsdJvA4AxDLbMjHJBPmX7UXxlsf2t/2zdF1HwjLc6l4QuPEfg/wN4MSd GtJLvTLfUbaGW6SGYg24ub6a7lG7B2yLkBs16Hr1kPH3xF/bl+BEmq2c3iPWNYuPGPgSxs7abUBr PiL4Z311cXWkaS5XdDfSeGnv13YDObbYMg4P+eniJxlT8TuJvGCeBmsblPE2YYTKMrr06i5Ks8my vMq+EjSbajVWOxlGl7KN3zfWrRXPOm1+bZnjY5ris7dN89HF1IUKUk9JOhSqygo9Je0nFWS359NW mflhz/kdcelHYknHQgY5x3PTpxSyKYmaOVSjozI8bAh0dG2sjKRwwYEEex9KidxtOPTt+Hr3r+KU ntbU+CP6qP8Agk98E7H4cfs22Xj+5s418T/Fy9n1+6u2QfaE8PWE81hoVirsMrD+6u5yBwxugTna K/USvnH9kGSwm/Zf+BEmmFDZt8M/C4QxgBfNXT41uun8X2pZt3+1mvdNRSz1mPU/D/26eCV7JEvz p1yba/tbXUVuIY2iuIzvs5nWKcpIuHXy9yEHBH+//hRkmX8K+F3AmTZXTgqGHyrBuKi1FVq1bDxx Fapf+atWnUqyer95vWx/RmT0KeEynL6NFLljRp26c0pRUm/WUm2/U+M/2i/2ydG+HkereCvg9d/D /wCIHxis7j+z7jw3rXjrQ/D1l4cuZIt6y6ob68iN/cJnm2hkDKy4leMjafyQ/aT8E/8ABRb45arZ 615PibWvBuqeE9G1iPQ/AHiqxHheyuJdLhfVrdbHR9T/AH0xukuSA3mllOEZhXY/tO/8EvLDRfjN 4Ik+HV/4muPAvxc8SL4fu7u/uZ9YvvA/iu7mN9Jd6vfNC82o6HdWMN/5ckhWSO4AEspUAv8Aqp4k 8H/Bj4c/By9+HHhfW9O0rVfhh4W0PQrK++0yz+INOlvbq20rSrnUEgmSS7F1qUsaPtbAaXaCm1cf zBxBkXiR4wY/xAyfxOrYrgTh/hiUFhsPlmcUaOFxko06uJhCn7TAOeLVTBv21atWq+zU1CMaOHcK sKXymJw+aZ1VzKjm0p5dhsJbljSrRUJ2TlZXp3neHvNydr292Nml/KZ4f+FOvarq89p4zm1fwfbw eJbPwpdavq2g63f2w8Q3kskaaWZ44tovcRuwRnBdVLDKgkftFr/hpf2HvgJ8L9IPxX0zw7qdh4xl 8SeJdIvtB1nV5vGviWW3iuLyCxn0v95pUGk2I0yGCaKQRm6u7gSswHln9PfBXwt8Ua1pnhDSPizo HgTxBpuh6Nomu3d7NZ3dzrafEu0uFmlvbVb5XWe0htEtYhcTvJOxjeP5kJY5n7RX7K/gj422R1XV r7xRp/iHS9Ev9M0m60HU/KtYLm+uvtcd7PoFzFJaanKLx23IyrujkZVO8IR4nB30Xsx4E4b4nzvh nELMuMMfhqVLA1sbHFYGvh4urGviozhQxnJOf7ulCk6dSjCqoVYV5So1Z0zHA8J1MBhcVXwkvaY2 pGKpuop05RXMpTuoz1eiSs4p2kpNxbRwH7LH7dfww/aOsLXT7XXLfQ/HNpFs1Lwtq7QW11e7AV+0 2J3KJULDIkiymGxLFEcY+7Li3juo4t6KwSRJgrBX6A5UEZ5IJGQefXFfmR+zp+wdoPw+1G58S/EY /D/xXqGh6lb6j4Q8XaB4Sm8MeJZY7WFvt0msrFeIkN1DeBgDHG29VDFuStff/g7x/wCFvFV3f6V4 a1aHXDozmDUbqzmF7BbXJJMcE95Eoj+0FEkLRqWZCuHwa/qHwnzXjurwxluG8VI4TDcQ4tyhRVOc Y1sTGno5VcPGdWlGraPPJ4atVo1IWqrlUrH1eT1cweEpRzdQjiZ/DZpSkl1cU2r6XfLJxa106+hV +fP7UX7f3wf/AGafGcvgvxBoni3xJ46sNJ07VrbTtKtIYNKjt9VJZd+pXNyqm4a1ViT5ThcBQQc1 9/QrMktx5s6ypJIJLdNqq8EXlojREr/rF81XYMef3mCcAV/PR/wV/wDAnhlvjN8KfEVlqkcHifxh pUfh/X7KeVVjtbCxvYYNI1dkxuS3IvLuN2+6fsfHzA14P0leNeMPD/wrzDingqrhsPm+BxeEpzli qcKtqNeo6HNRpzlySrqtOhOHOppQU5ODcdMOJ8djcuympi8DKMK1OcE3NJ+7J8uiejldxa30u7aH mv8Aw8m0/wD6EDU//A61/wAKK+Zf+GXLH/ofPDH/AIN//tNFf5nLjr6RWn/ChT/8F4Dy/u+p+X/2 jxJ/z9j/AOA0/wDLzPyi0rW9R0S+sdQ02/urGfT9RsdUtpYJpYxFe6dcx3VrdbI3GZY5YkZTjIKj GK+jP2vfB6eEfjdr19ZW8MGgePtN0D4i+HZrS3NtY3dh4u0i01S6ltY8kADVpNQDDqGHIFeMWvh7 wPr8NnHo3jX+wNZu7i7SXR/G1mbLR7SFZJnsyvjHT2lhk3WqxK5ntLUecxAITBr7K/aI8HeMPH3w 8/Yk07Q3tPHvizXPhzrXgy1uPC9x/atndSaN4ggt7C1l1BF2L9ls50juZX2xxfZnZ2CLk8OV5LXx vC/EuHVL61VwssvxWHdGcKzcnX+pzoqMJSlGVT6/Tm48qlzUoxkk7I8KjQlUweMilzyg6U4crUrv m9m42TbTftU7WveKTseE/s4Xnh7wn4l174yeKNKvddsPgxpmmeL9I0SFmgstb8Y3Ou2GleFdM1W+ QF7Oz+13E11uRSWbSwjYVjn9Y/2dfDun2nif/goBrXhVJj4Q+MP7KWpfF7wNfZMkV1Y67J/a+o2s dwq4e4stefUbWZM7onttj4yM/kH8RfFOgeD/AAg3wM8Etb6jFa6/BrXxN8cxlifGHjHSYbmxt9H0 Tp5fgrSXuLxLVmAe8uJJbxgqGJR+k/8AwSK+INz4s134v/s6axGNRTxN8FPid/wr6aV83GlXusaf AniLQIGkYY0q8MVld+XnbHc2DOuDM9fpPhNicDR4t4c4NqSg61SddqtCKf8AwpYnCYzCTw7qpOU6 c6FenQvC9P61Qg6d6c51ZexkVSnHH4TL205ScveS/wCX06dSm4827ThKMdLrnguX3W5P60/4J8/F HQviB8BNP0rS1kg1fwVqF5p/iayMbJZWep61eXutqmkKUVYtKZbh3ihTcluHMIZgleB/8FRPgc/i HwjoXxz0GyMmp+CxH4f8Z+QmZJ/Ct9cltL1OUAZcWWqztG5/hi1TcSFj48D/AOCbfxGuvhX8b/HX wE8Z2E+hX/jC5uLS2s9Qs7mHU7Pxn4TN2f7JvRK4W1ik0kajt+Tc80cY3Mrrj9x9f0LSPFGh6x4a 1+xh1LQ9f0280jV9PnUNDeafqED211A4PTMUjYI5VgGGCAa/c+FcFhvFDwchkGYSjTzDDUpYGp7v K8NjMFJfVpSj8UWoKhKolbmjOcUkpWX0eCUc64fjhatlVjF0m7WcKlN+42t1Zcjl3Ta0Pxj/AOCX 19aeKvC/x8+FGoeXLbalD4X8Rx2s7uqTKzXWm3eQhyEEltpu4ryNw718YeOPG/jb42fFDxd8NfH/ AIw8UalJf/EjW4/A9ndSJc6N4b18a1c6d5Z0q5mhFlpT6RF5BWDY0bxRTFWCSB/rX9nPwLrf7I37 e0Xwv1uSaTw14+0fX9G8I6zOGSDXtDv1Os+G7nePle/ivdI+x3CZylxuH3XUn5c+N2meOfhz+118 XvBnw505NX8T+K/Gd5baBay+HtN13VJG8YS23iGyOiR6lbTCw1FJdQUJdxBJIliL70G41+E5xSx+ G8O+E8rzGnXo1OHc3xuT5jh6d5Tq3csVh6TpxnD2nu1aqoSu0nL2kHdq/wA1iPawynA0aylF4TEV MPWgr3lf34Rtdc2kmodm7rWx+qP/AATG8L+K/CfwU8Y2vinw/q3h+W7+JOoz2EWr2c1lJeQW+j6T Y3NxbRzqDNai8tpoxKBsdom2EgE19SftN6v430b4P63qXw68JeHPGvjGDUvD66Ro/iewTVdOt5p9 Xtrca5FpU3yX97YNKlzGrfKggaZsrEQeo+CPgjUPhv8ACP4eeCNYvZtR1rw94W0uy1u+nnNzLcay 0AuNTZrhuZ1S8mljRySWSBTk15N+1r8Qbv4W+FPhr45tIby5Gh/GbwfNqNrZxNN9q0GbTPElv4lj uFVgRAnh+XU5s8gPaoWG0Ej+osLldPhPwtw+W4jE1sHDAZcoVaq5Pb4d1VepOPuuEqmHdWXKrWk4 JJ3dz7SnTjgsljh5zlT9nSSctOaPMtXtbmjd201sj8yf22fC3xH1bx38G/gzDZTeNfGmneG7XxD4 h8dnTEga/wDF3jXU2GqqdTKLBofhK2vLMypCWit7dLjeQqIu2hb/ABG8B/D22WC51zRPEGmwX+s/ C3RP7AS4n8K20msaDqF14+8fa7cRpbt4+1mfxLc6DJMyMltp9lqgsbW4njEqj6N/bS8cXdldaf8A BDwwl14f1v40eEde1aXxHHf3F3camlj4i1fU/Dvhf+1FjM13BrKm/tjbIEW3Or21sC8Jevyn+Llx ceHj4c+EnkWsdt8MdPli1CX+zIrPU7jxj4ngsdZ8YC+ueZJ4ra++zWECM2xI9HDqoMrV/LHiDiaf C/EvEeY5dVljMTKeHpOdVSnClVjCn7HAe9KNSq4YRfWK2IqpqdaFJKCrw9qfGZpUWCxmMrUX7Sbc Itu7SklHlpatOVqfvynLeSjpzLmK/wARYhaN4ORoWtbi98G2Wv3ttC3laXb3fiHVNX1NhomnRAQ6 TpJtJbMxQwKEx8/LOa/Vz4v+BZdC/wCCcvwm8KWti97rt7f/AA81O105I2e/n1rxhqF/fC3s7Xbv lunOrNGigAnaeeMV+cXiXwTLq/xO0Hww1xBc3NjovwctjapcxzyX2haj4e8KwXgtdrFWubf7cHkt wPMETzOVxE2P6UPFOjeAtEg07xh4xn0zS9F+Hy2uq219rFxFa6Rocml2N7pOn37+cRFA0EGq3ixE glZblGX5kSu/wm4Medw8UvbVIYGFfBwyp1allGjTqymsRXf2b0lhIzcW4qTd3OMXzHRkeB+sLOOZ qmpU1Q5n9mMm1KV9rx5E7Oyb6pan55eH4PB//BPL4AW3iHXNO0/Vf2g/iNZMiWEkouJFvNomj0wy Lhrbw1pkc1s16UI+1XeUVjuQr+f/AIi+PPhf4ytf3nx38ERzeL7gxzQfFD4Y22n6B4rna3UQW2m+ ItEv5Tp2t6atqQiyKtvdRi3jHmuM4439pj4veJvjN8YPFHiXxE0UMGnX1z4f8OaVZ3kF/p+j6Bpd 1NHZwWl3au0V40pLTyzoSs0lyWUlAgHhsR4xzyAfX69T9K/J+OvEKdXFw4Z4ZpQwvAvD8XhcNg61 GE4YlwvGpjcVCcbyxVefPUVVclWkpe5KE5VJS8nMMzcprCYSKp5dhVyQpyimpW3qTTXxyd3zaSjf Szbv614i8K+CoLCXWfBPxEs9dsIkt/N0PxJpd14Z8aQzS7Vkij02M3NnqVvGzD99b3pyoYtGmMGX 4Y+HrnxF8QfAejeU8cWseJ9IjWaWCRoGtINRik1CdeP38cdvFOX25C7CDivMIznv6enTJyPpmvsn 9liwuNeutfEesaSb/wADaP4o8UaFpetNPCunT6j4T1jRm8QafeQl2lgTWJ9DhubCK3me4kvLecDM DGvz3I8HhuIOJ8qwsMFDBQrVablSpSm4SVOaqVbe2qynG9GM7JTm+ZJJJP3eDDQjicXRgoKmpSjd K7TSacvibesU+r12NX9oX4z399q1n4d8E2MPg7w9e+F9K1O7uLAt/wAJPrtr4ttJfEM2ma94gIEt 3pSHWrgRW8Qhh23DLIsnGPnTwd4R8TePPEGneFfB2iX3iDX9Ucx2WmafF5k0m0ZklkZmCW9uiZLy yMqIOWYV9uaz8LdN+Pfxa8X+CPAvgzxfqXiHS7Hwv4eXxbrWp2+ieAfhZpGjadpltJnTLS1efWJ/ JjuY44Jp42mmndkgRV3D2nxd8R/hh+wnpY8DfCLwjJ4t+KOtW4fXvHPi60u4rGWCAGKWS2vI44xf W63ySL9hsnWCIxt9omeUYP6BmnBGIz7N824q4y4khlnA2W4idGeLp06jlJwqyprA5bhVTVOdaSpp t0ebD0VNTrTdWM6S9epgJYmtWxmNxSpZfSm4uaTu2m17OlC1nLTdXjG95PmvEu/D79l74cfs+6Rf /EL4268mr6/N4B1e5m+DcF3pt1PeReVF/a1ikltL5mvoZUtlQokcMbTlZGk2ZPj37Vf7S3xEHxBH gjwH4j1X4e+FPCWj6HZt4e8KXsWkpDrU9jFqF9bXz6UQUuLI3cVk9vv2RS6fL8gZjR8GLjxt4x+M vjr9oD4xWcU/h3RPhxqHijxJqcFhNe6DZLq2i2UvhPQ7KwtHcC6WN7G5+wK+8Im+42+YzH41+IXi fTPF/i3Vda0bSn0nTpnSK3huWWTUr1ogwudY1udc+frl7dtPc3RBKrLcmNCURTV8acVYfJ+AsPl3 BeEnwVlua46pClTU6kc0xuFwbnGeMxddJSVLE15xXsIShQo1sPVpUoVP3koXjcZGjl8aeBg8BSrV HZXaq1IQunUnLR2lJr3U1FOLUU9Weu23xm0vx9AuhfHjSn8QW5UNa/EjwxpekWfxP0m4gheO3N7e FIofGOmtnE1vfYmOBJHcqy85d98GV160bVPg34oT4q2sMLXGpeHbPSbvRviDoVuoy9xqPhO4eQ39 ihKq9zYTXMasw3hFINfsn8FP+CWvwA+Jnwf+FXj6/wDEXji31XxL4Ii1fxGlpqduLK41jVNOLW0l rDNY7rGKzvWy0ZLiYRbWZc7q/HrRND8Z/Bn4lWOr/Yda0vw3eeKfEPgHT/GNzaXul6dq1hJdS+H9 WutI1gPEFuoba4S4EkMymJ40beAK5eMvC/jbhLAcJZl4nZXQzPKuL4RnQzPCYiVbMKNBwwdT2mIq R/dzjD63Tp3xtKtJvnpU61NeykzG5TjsHTwVXNaUatHGpONWEnKrGNoO8ns0udL94pPdKSXKzwW1 sb3Ur610yytLi81G+uobG0sbeIvdXN5cTLBBbRRAZadp3VQvXJwa6Pxn4a0/w5q1j4e07UG1rWra wto/Exs3t7vTrbxJO7vcaNpE1oCbsWsbwW87ksGvIZxEWiCE/S2qWmhWXha2+IGiW2qyftCWU3ih NQsrmCwgstV0nStV1HTbv4u6Tonllp9cSOOaN4o9yLPbzX6xOLZ3Pl3ws8DX2h6p4d+KnjtJfC3g LQ2fxbp95qGo2mh6p42u9ELXum6F4NivFeXUb261G3hj85YXgjQSM7hgoP5o+D54erhcsgvrlfMv ZV54uKbo4LAy9nJzrWfLSrxc/wDaoym40VCEIzn7a68l4NxcKS991bSc18NOm7O8uikr++m7Rskm +Y9d/ZE+MnxQ+AHiqXxxFr3irRPhxoN9YQ+IfDM93cWOg+JtX1m9h0qz0qXTr0hLuWGO5uL6cQIZ o4tNy5VWBri/2wfiN8XvG3xs8Z6H8R/Guv8Ai1NG8RXlt4bsbiVo9Kh0e8lFzoj6To9sFghSWxuL VlKJubzOpzX9Vdv4G+D37Q/wv8H634u+HfhDxNofi3w3pfiS3t9R0ixuHtZde021ubmS2vYYUkt7 vLBWliZHJiHPAA+MvEX7BXhSX9sz4QfEu3003vw60LwVcT6jpmoP9tT/AISfwOlpY+Ere7kuCTdQ /YriwYeZkuNHO/OST/c3Ev0XfEPDcBZBwhw5x5PiThLHZll1SNpYmjGnSx0pUsRiauD9pUpSwtGN TB1acI1Z+z9lXqqMZV5s+9xXCeZwy7DYPDZg8VgqlWk18cbKp7spOF3HkScGld2tJ6OTPkb9g3/g mn40ttV8KfG/4y3MHhi0t3s9b8N+A5dOhvtfuAksN5p+parLcHboDkxxvGqBrkL94R7q9M/bUm/Z d/Zs/aO+DPjPUvBHizR/GuoT3njB/EngHUdOt1u7648TMmqXPjCw1KNpNbFxFLdxllmjZIpCFBCh a/bYYJ5/z2A4r+ev/gr/APAn4veJ/G2k/GjRtNXXPAPhfwrBolzaaSs9xrXhy0srp7q/8Q61aiPF to8uoXyxRygnJhywAwa/V/ErwuyTwT8C8TS8POGFnub5VjsBjKmKxWHjjsR7SjVp1K+PkpprDqEK SivqqpqipKS2lUPYzbKaGQ8PSjlmE+s16M6c3OcVUleMk5VGn8KSj9i3Lf1Z4JrH7C+meIPGfxo0 zwZqE/xV+I1lZ694z0L4b+Eb2DTLLwj4b1yc3vhbVvFviTUB5V/qctpe2zwaRZ5mkY7ZpYwGU/mJ 458FeLvhx4n1XwX450HUPDXijRJlg1bRNTiEV7YyyRJNGsqKxAzFNGykEghgQSK/oj/Yd8WfE4+I /C3jnwp4A8L6l4J+Knwg8GX/AMSfiJ4l1D+w7nQdf8Bz3fgu6srDVEt3GoSzRadbXBsH2ZYmYyoM k/C3/BRzwr8FviX8d/Clr+z/AOLLr4l/G74j+JtQ0z4gabZ3s17bW+ryPZaZ4f0q03W8dvpvk+VN EYo3k2pEHkfHX+XPEzwj4XxHhrl/H/C8P7IzirmEqay+pelHHYXGVubCzy9V4SxWZYyVPEYSdVYe rLDU4/WIU6VF0lTfyGa5NhJ5VSzHCL2Fd1WvZv3faQnK8HT5lzVZtSg3ytwS5klG1j9Hv+CQv7Qm m/ED4GT/AAb1S/jHi74TXM4srSWTE174N1W5e6sbmBW5kW2vprqCTGdokh/vV6x4Z+LHiDwl+1Tq +t618Ovi5YeA/jbo1jo0mvX+jS3HhTwxr/gK81PSbW+vCB5mh6fcafEZC75jeO/jmA618bf8E+PA Nj+xTL8TfE37SPhq8+GerfadH8Ian458QLDc+G9Pl1K5gu9GsNK1exWRBY3lvL5k825ow9ogJjAO f1P+LGt/EH4k/D2w139l74yfDHQru2v4tR1HxL4gtbTxX4T1DQI4pPtVq93aPItg+/YTIOihgxXg j+y/CutxHjPCLgGhneY1cLxp4fr239l0KNGeZVMNhalXBUKWIwmNrYSUW8D9YwtR+0pRd5SVR1aT g/uMoliqmTZdHEVXDHZb73sYxi6rjBuEVKFSUGn7Pmg9Und63VjR8H+PPGF74+8UfD/xpocjWOnG HxF4Q+IFu9nb6NrkN/qF3caVotraxZJmttNS0V7hmIneRkIViFr5u+BfwVi+GHxL+L3iDxzf3XxH 8T+O/HGgafpEU1zL4ivWtTF/b8l7r1pc2yxaNollq0weGRYxHF9hWNJZXCg/Uvw8uPEOo6F4d1LV fFfhb4hIbLThe3PhHS7Y6JJqUP206rqWhX6HElk13HaiOPPH2RiCXbaPSbG80fUdUN5BALW5lj+y w3jQLbz6vBayTExCcoGntY5VnKxk9dzbcFSf2Knw3hs9nw1muYYupicZk2IxGIwbxSpzlGli6M6T hNUqk6NeVGlVUKVacqk9udzlOSn7kcLDEfVa1WTnUoSlOHPZtKaas7Nxk4ppKTbfe97PoZJ3t7dp 5Y5pmVFzDawmWR3AIbyo+pJPYngLzjmua8aaJp3iLw5e2mratqehaW0KXN3eabqD6PeQRwyJO5e9 DAwDam09NucgggEOt77w5Hqeo3kNz5+pxmG01AKZZrm15C29pNZRfNag4BTfGN+7cjNkmptT8UeG 7Z7OxvdQsXudRvv7OtNLlkQ39/dCIzSW9rpzDzLyRIss6qhChWzgjFfeYirhMRhMRSxVWl7CopU+ WU04y5m4Lmd4t8zaTgtneKlfU9GUqcoSU5RcXpq7rsr7b9vxPKLTx6s3jK5gsNUtvEPh6zGkaMdC 0nTprfxB4RuL3T57m41bV9TvL9Ydc0CSyijcG3jlkR23qzKrmux8K6HpFnqt1qPhtdPsdPuGzLpm j2ul2WjX0k80s8mui3sYkkXXGLeXcNLkssa4xk1keKr3RIZ7y+vYLSxd7vT9Os57q4stMv5rpoLu 2tmsJdRCILhoJbuKGNGMrLjajM22tDwH4UuNEaW/h1fxBf2Wr3K3i2fiJbfz9LgjtRHb2dtGkCva x5b5lLvlwcgc183goYj+06dGry5h7KtVq+0g5RlQjOTjDlVSc6nJLlinCM5QXKuSMKSVNctPm9qo ytVtJu605b7btu2ysnbTRKOh6c9vG9xDckuJYUljXa5CMk3ll1kQcPzGhGehHHU18X/ti/sWeDf2 stD0t7zU5PDHjnw4kkXh7xNBBHMotbieJ7mw1OEJvurMIJ3iVXXZLJu6FgftevCv2h/C/hbxv8J/ EWm+K9a8caFoUBjv7vUPh41/H4tVtNd5Ps2nQ2FrLNdCRlKvEIysiHDEJk1v4hZBkfEfB3EOUZ/k lDiDLcTh51J4TEVvq1OrKilODeJtJ4aUJQjKOIim6TSmth5jh6GKwWJo4ihHEUpxbcJS5U+XVe9r y2aVpLZ6n89n/Duub/oqcf8A4DQf/JlFeW/bvAv/ADx+Pn/hP2v/AMh0V/kV9W8Jf+jfUP8Aw+4r y/6df1c/HFDJ9P8AhOX/AIUT8vLy/E/EsOeMY79sYwcY6dP8a+7Ph78TfFemfsO/FvQvD2qtYXnh 74q+F7WW9tQqa1pvg34gafcR63Y6ZqEf7/TNPvNZ0K0S5MbIsolMTH58H4K3H1PQD8q+kv2er2Of Sfj34RvrYXeleJ/gn4iu5VMgRrPVvCGo6V4i0HU4cxsHkhvLZ12/KSlwwDL38fhDFVcJmtelQryw 8syweNwvNFtPmrYaoqV3HWyrKm32SurWPncBUlTryjGbh7anUhdX+1B22/vWPnfzD+PfPOc9yc1+ gn/BLjxkng/9uP4IyTy+RaeJNV1XwbdlmKo8fibRr7TY42PGAbiWH8cV+eIY4HOMgf5/z6V6R8Hf FWpeCfiz8NfF+ktjU/Dnjnwvq9nligM1nrNnKqFwCVVgCCQDgN0PSseD8z/sHizhnO1d/wBk4/CY hpbtUa9Ocl81Fr5k5fX+rY/B4jf2FWnP/wABkm/wR+ifx5S/uPjx8TvijoFnBoPxt/ZS+IlvL448 N2K3kzfEPwJ4W8SwabpfxAsW3s66xbaabW21xGLJJbyRXSkAyKP3d8OeIdN8V+H9C8U6LMlzpHiP R9N1zTJ0OVksdWs4b61bIPXyZ0B9CuO1fhv/AMFDPFF98Df+Ckfizx94PVRLrNr4M8Ta9otztbTd csfF/hjTv+Ep8N6pGYyt1pd9aSTxS70PMokC71XH6b/Cfx3oHhH4eeFdB8OeE72y0C30xbzRtMuf ExvjounatLJq1voVtdSaGrzafZrfG3tt43LBbxoSdua/sXw8zDD5Vxr4kZTiMQqU8Fj69PEe5O1W rRxVWnh8XFQi4qeIw79nil7r9ph6dW05V6jh+hZRWhh8xzihOVpQqyU9H70ozahUVla8oaT2d4KW rk7dp8dPgppvxc07wxqlrJBpPxC+G/iTTvGXw68TNHltP1jTLy3u5tIv3Qb5NAv4rcQXSDJTck6A vGA3mnjf4K6Y37R3gP4q6FpcsXi7x0INJ8ba08qPB4Z8I+B9Hl1O9h0NY4cw6vrN4dE0u5ui29bG CWODZ58hr1lfjFExx/wjUg4z/wAhtf8A5TUf8Ldty4Y+GGLqpCudZjLKrkbwrHRsqCUTODztGelf omY4PhTMa9TFVJxhiqtfCV5zVKpd1MJJqFS3s7Kq6E6mHdVfvFSlFKSdOm4+xVp4GvJzdlUlKnNu z+Knez2+Lkbhzb8rVtVG3tPnRCRIS6iWRXkjjLKHeOMqJHRM5KKZIwcDjeoPUV5h8aPBOk+P/h9q +ha3Fby6ZHJa6lfrcD5X0yxmD63bI/nRiKS40F9VtRIzBY/txc524r8zP20/2svif8KvH3w78RfD V9N0KWPwp4h0m8stZtYvElnejW9UsLiW6MMsVusM8a6DbIh2swEr/MAdtfV/wU+P2s/F34C+F9V8 Y6Yy6/4y8ManYa5q/h/UYtFInup9S0iXUNKtP7KmXTpxCqvGN0ipIueRxXl/6/cMZ7m3EfBdSNSW KwVCTnz0m6FalOFJaSV5qXPW5WpU4pKLlzbHL/aeDxWKxeXcsnOnFt3Xuyi1HZ7p3lbVLa9zwX4R zeDfiX8KPhz43+KS6hqmufsu+Ndf0LUdJtNZ06Sz8SeItIigh+HcUmqSEN4g1d1Ok2lkYJxbyXkU 8kpZAxb8ePG/jaf4lfFbXvGmpWs08PiXxnc6imk6jcl5IdNv9aee20W4u7cKSqWkywPIvPBZT0r9 R/D+peCtfms/grovhHVvDHgK11rxT8IYrKDxjPqN8NS+Hn2f4k+H/iV9tuNFT/isBrNxqETts8s2 180S7VVRXyH4y8BfDbw58a/h58L/AA54UubSxuhpmp+J9e1TXZtY13XLnT9W1yWeKxc2cNv4etLg aTCsiwW7ybWwZWAIb+WfEHDYvNsm4dpYfF4apQwVWhQxdblqqpjMwjSw2HpX56EZThSwzpR9pV5I ylKvU9nGUuR/H5pCdfD4RRqQcYOMJytK9SraEI7wTajFxV5WT952TdjvNC+Et/44/a3h8PaZPJaf EIfFGz8V6jpGjKB4W8BfDvQpY9Smg1PVcF7nXF0yLS7eO3hUwxmdYpZpJZGSP9Nv247zSvEHw41v wnrVxDpnh3T4LbWbrUdQ8ZWnhPSdX8UefGug+E70Jpt1eXax2txNqr+VGkBbT7eGRy8qhfnz/gnv baH4Q8F+Kfijc6TJrfjD4j6/fpd6hLeJatpmlWN4839l2rPZzvKst/PJNNIXUuY4l2AR5b4s/as/ aT+MXjb4k/E/wNfeM9Vs/h/a+Jr7SbLwbZPbwaVFpumyGC3t7l4LZJL53ILzNI2JZCCUAVAvtPN8 l4K8Ls1zDFQqTx/iRWqzVClGNSMKMoVZYeOInUdOEvaRl7TETpJ39p7KNGMIWXXGth8vyWtWqJut m8pPliuZKLTcFNyaTve83Fa35eVJWXzyNB8B6Sw/tLxxNr7LsBtPBeg3mxvkVpEOreJls44wHJTe lvNyu4KV6781z8GLi2sEs9I+Jul3Q1KU6lcSa34Y1qI6P5cQgSztv7Is9uoiTzixd/LwFAPORxug wWbeFPGlzPaRT3kCeG4bG5kAMlh9o1hzdSQZHEkkNusROR8jsO9UtGtoLu8ht7gTeXL5uTBIkbjZ FKy4aSFx/rApPHIBHBIYfyxWxXsY0I0cBhYQx0b8rpyqNWqygk51pTnF/u7t05JOMrPW9vkea3Ko 04JVFfZv7TVryba26NbkhMIkk8nzPI8x/JM21ZRCHPlmXZ8vmlNu7acZzjiv0v8A2Fv2cvFvii61 T4l69AuifD3UfDWtaFa6xM5j1KW5i1LQb6a4s7CdF36a9lbXKC7JeH5ZAPmUV3Xwg/Zo+D/wz8MR fFHxloV/8V9WtdDXXbHRNcv7XSfDFtPHbJdBX0uDTLg30gYgK1xJJGuM+STXqfwb/aV8Q/HDTDc6 7o0Ph3TtM8Qa2NH0LwlqMmk6Vb6NpOkPDZ6Nfw/Y3OpRbtRLO+YVY26BYUAGP2rw/wDDfK8gz/I8 w42x6eZ4yM6+CyygqklJRcE6mLxSg6UKaVSyo0XUqTvdzpOOv0eV5VSw2Kw9TMKn76onKnSjd3tb Wc0uVLX4Ytt33jYwviR+2N4X8LaP8RvCP7OEFjp9r4a0s6ne/EqdbWdNY8S6pr2lafFHo8F9Azaw 62t1qKrc3AJKWKiBRFEHbw34L/Abxl8c9N1D4lftCa3rGm/DiydfEcvjfxTqFzba7c6TC0kurPpO oXySJH4dlWLZIjrEqtIk9huYOr2fhN8Nfh74N8Y3Ol3/AIcfxjBr3xk1bwtokHiK9jm03QbLwJb3 99bXd/pkNkqeJtQlN24H2gx20TRpIbaZlArP/bH+Pvjb4g+IPGvw4sJk8JfD3wCdEim8M6ayzDxP f3D25jvtWvUhgKRQCVBBbJH5CLEMqzYYdOdZjUx2Xvi/xFxf9o0crlUwuC4bwSnTy+WIarYmCrtq FKGGpYWNKpNwdXE1Vy3q+1i6S1rVnOl9ezSftY0rwp4Wmmqbk+aS5toqChyt2vN6a82h6b4w/bx8 GeErqD4efB34Y6Dq3wg061/sjUxrZvdLvPE0EcENilxpT2p8zTlW2t4xHdXKzXM7KsjrHtUV5Brv jv8AZA+JFmg1Pw78V/h74jmngul1XRNP8N+Jrhp4rOOxTSJrgXVvJrsUjxQOLi5iW5aQMZJGd2Y/ BiuSQD7jPt1xWhY6hd6dcLdWcnlTok0aS7EdkWeF4JCm9SEfy5G2sBuQ4ZSGAI/Gsy8WOJ87q1aW fUMBmuV1HanhKuBoOhhaWiVLCez9lXo04RVoRjXT5vfcvaOU34tXOcXiHJYiNOtRe0HTjywXaFuW UUlt7/ne+p/UZ+xv+0rdfEKWT4Ix+O72+k+GHhzTzruseO9B8M+CdeTSLSEWv9gR6Pa3kxurizt4 oo7y4AX7PwHctzXzN/wUB/as/Zo8YQ2vwQ174deM9Ti8Aa/a6tb6h4buNE8OW7u8Lu9loMs/mH+w ryFj593HCj/Iixruckfn3+zLb6N4LvPhn8Vm0+51zX5fF13FrdpqGpytpPiDQpJ0srnQNTsZoJUn tZkmZ5XcOzOinGBg+i/8FOvEOh+LvivoGv6R4SsPCU8GjNoN7Dp9wk0epLYwWN/a3kwisbdY5lTV HiwqHKwqSxPT+os88YuK8x+jxmMMTiMH/aGHxGDw9Whi6VXMli8oxkVPC0oSxUJ06M8PyU/bOpUr VK0qPOpxbgl9dXzvGVeGqsZzh7WMqcZRmnV56NRJwSc01Fxsr3cm2r32P1h/Zx/Z2/Zj/aL+E3hv 432HgySPxlrF5c3emeINZ1aXWNf8HXmi3L6fp+kx29vdi0jtrWGANFBJE+4T+dL5juxPxb/wU/8A 2QrCDxv4N8W/B+K61LxX4p0yW11T4dpqtk9zLZaNGsA1zwrp2oX0cjoXaFLiysozGjt5yRoGKj3D 9gv9tFl+BNl4Qb4UaBZD4d2sWkpe+H9ZOixa8sECv/aGoWJ0OfZqkpJM8wlfzXJbavSv0N+EqeC/ jrNofx+1/wADaXB4z0Yan4d8LzXlw2tS+HNIkNrLcRWM89tFGLqS5EjmdbeOUCUoGC9f3rLuEvDH xr8J+HuGMqo5fS4k4iwuX4rGYzD4CrgKsK+FlTjj8ZD/AGZU54m9SvSUZuUa/tFGU/ZRjOH0NPA5 Vn2T4bCUI01i8VGnOc403TalBxVSovds56tK9+a9m7JNfOn/AATa+Luqar8F/D/wY+Iuia34R+JH w2tpdKttO8QaddWQ17wwk80umXmnXcimK4ubdGkgngDiaMQI5Ta2R+g/inVbjQNB1PXbXSLjXLnR 7C9v4dJtHSK81BobaSRbO0llG2O4ldURS3y5kAbAr+T3/godbar8E/2vviXp/wAO/FvjHw/Hr/8A ZfinU30/xDeadv1TxLax6tqC28WleQkFkLmT93HtYqF5djXk3hf/AIKBftieEYbG20747eMbyy05 UjgtNclstcjaCN9/kzSapaSyTKckZdmYKcA4AA+LyP6VuW+GMMX4XcZ5Hj8ZjuBJ1cohmOEnha86 kMFN4ajUqUazw8HUhShBykpNVHG0oXcpy8/D8ZUspjUyjHYepUqZc3RVWDhJtU3yxcoy5E2kk21v tbqf1oWt94m+Nvwi0bVNDvvFvwS1Xxhp9tc3i3ulWUnjHw7aTswubO3S+jaG11FogvlXPlyBVkDq u7GPw/1+f9q/4h/B7432vhHXvEXxNv8A4UfGPxX4H8eNqF2z2PxN+GS2b3uoXN/LNJEjwWd1oeTH ZyKYhfuiDDYP6FfsgfGT4ufGbxx4ys/FnjKFvBlp4B+Gvi/T/DsGhWUWraff+O9DS+vrK38U28kc jaZDdpK0Ub2zuEkEfmhV+b9ALXwl4b0rw5d+FtM0XTrDQrmzv4LjTbS1itrW5XUI5Fv3uI7cJ50s /mSGZyd8hkJZsnNf0FjeGKXjTkWT5zhOIMxyXBrC5hgMRUU/YVcdUjbDwrQw9KvVwmHgsVSqV3J0 faVKbeHnSVKpJL6WphFnuHoV44mrQgoVacmnyuo/gUlGMnCK505X5bte60otn4c/szfHbxT8KP2V PD8Ph7wf4T17xbN4n/4WLd/CK8tNSh+wfBnxJ4gl8O3mtabLqF5LJdGHWrSO7kch0S3YSt8u5x8b +A/iF8EfhD/wUHvPjF8TTqL/AA51rXNY8WeCPFXhu1lj0bTNc1CWSD+0o7a2R/7a0C11AanaM0BY FoxMoZV2nxjW/jF43+Gn7RXxg+ITXtt4l13wP4kk+HOk2eoWq23h1PBc2q3eg3XhuPQ4XaO30VvD 9rJZpArERJcM6kyANX2XoVt8N/DPxa1f4L+I/h3B408F+B9H8PfHb4Qi91n7Fq/w61HxHpVp4q1D wg1/LpV0vijwobu6K/Z7qJFJjEhUlnV/4zwvEuK4lo8GZZQzGjgqvhtmeCo4NZhhpYjDLE4R4qnh qtWlh1KShmEMHjFXo0qlSGFr4bAewlTp1q9Wj8HDFzxccBSjWjCWVVYRh7WDlFShzKMnGGtqqhPm im1CUKfK0pSlH9xLiD4F/ti/DUWbG1+I3wuvtU0zUp2RdR0/T9T1HRrlbu2s7iOeGCeaJJFiaVcK rKyruYEgesH4feBIPBsvgNfCugWngX+zm02fwxb6fa2Ogf2WI9slvJZW6pH5Hlr8+R83JYk5NeJD W/Fd7ZabrfhPV9L8F6fd+ELO7t9BtPDllf2VvM97pnzzMZ4FuGS2a6ii2RQqguclW2KK/JvT/wBs v4t/tQXvx3+FOt3aeCdK0zxDbaf4c1XwhcXFlquj6ZDJfWElrNMmw6pLLc20NxJJIyjcpjWNYzgf 3HxD4icM8JVMtee5RHMuLeKsPUw1GrRwlKlSzFYahUxPsHUqVcRWw+F99qFLFTqKMq92pfvZR/Qc TmeEwLpfWKCq43FxcIuMElV5YuXLdylKMNbJTbtzerX7naPp1noph8I+EdC03QPCukeHozp66ZbW 9jpSXF080Vpp9lbWcYEMUUEbTOVXn7RHjuTzvi74bX2vH4ftonirU/C83gfxjp3iSZdNYi18RaVH 58WqeG9UhZi0lpPDIjBixIkhViOTXnvhbx5qHgj4LQ+IdQW48TzeEPAkZnW6vRZXGtTeHmgsRcz3 a2swtp7iGePzT5cgDQAgckV5h+yv+0N4x+O3jXX5Nft7HTNDi+HfhLxhpWhWa+cNNv8AxFrXiO2k jOovGkl2senWVtFllVXZWk8tC20fWVOI+G62J4c4bxsakcw4mgp4fD04yhGhSpKNZL2tJwhThSdG MYqnJylKC93kbb7ZYrCuWFwtRP2uMV4xStyxVpLVWSSsrWbba7HqH7RXif4ffAnQtS/aa8Q+F9X1 nXvBWjr4cQeHbn7NqGpaf4i1axsobW6t5ZkgvvKupVaFp1cxea4TG/j03w1qdl8QfBWk+L7TwZf6 FqeoaN9p0zT/ABnpcGka/pZ1CFZ3SWSIyvZFmcMWjcBjhuO35gftd/G7U/i/8afEf7D2q6Dp+l+E dY8FX/iP/hNbK7vJPEFp4g0TTz4n0S8isWKwSWkNzpwjkgZv3y3DN5kZVQPjT9mf9rj9oaH446Z4 c8ffEjVfHvg/4daOuhW3hfybTw1YazBe61p3ha2udTksIZXlurZJ0njdzIxMAj3AEtX43nHjrw/k HiNismeFq4rhjM8R/Zbnh8LQh7HPcNVvmmIxUqnJiakKODqYF0p0aeIjVdKcYpzSv4lbiDD4fNJ0 ORzwlaXsrxhFcuIi/wB9KblaTSg6dnFS5mnbXf8ASXxHpKWXwu+H/gz43a2/hf4iXfxG8QDwBp/i PxNe63apr8z30eh6oviPR9Ld7x7W3vRd6atyCsMjRpI7+Wor76+HmiNoXhnSbBruW/FppWm2o1Ca e4nk1KSG0jF1qUjTkZlnuNzs21dzEsPlIqvpuh6R4ltrC51XTLC6t7GeHUtKtZrcTNp07SC4CxXM rFmiEqxNtwq7olOAoCj0EAKAqgKqgBVAAAAGAAB0GK/b+EuE4ZRjK2YutHE05YfC4ehUlFrEzp0K Sg5YqopKnVm2rwlTo0FGChH2ceV83u4PBqhOVXmU04wjFte+1FJe+9m7rS0Y2VlZWKF5qEVlPZQy qcXkxhEhZVSIkBYy5YgEtK8aAZyS/AOMVz2ok2Fve2EMWqX06xz61Yj7TJG1xJDcCZ9PhvI33jaz rtQhf3eFDHBI4v4m/D/SPiQV0DXLnUbSF20+TT7/AEa9n0/UtKvLG+ttVtr62lEjRvcLe2sDAtEf lTZnBJPo1xbTLHpMj3IluYXisbmd4QPtkM+2G5LRo48p3MYcbSQrHoQMV9FKtjcRicxpzwyp4Sgo KjWU4ylJyvGvCVJwShyJU6kJOVRT52ny8nLLqvOc6icLQjZRd0276SVmtLaNPW9+lrP8Z/8AhNfE X/QBH/gf/wDdFFe2/wDCh/g1/wBATxp/4Xb/APyhor+Mv9UuNf8AoY4T/wAKJ+X/AFLPU+J+qY3/ AJ+x/wDAn/8AKj//2Q== "
+         x="0"
+         height="253" />
+    </pattern>
+    <filter
+       id="filter16257"
+       inkscape:collect="always">
+      <feGaussianBlur
+         stdDeviation="0.41431294"
+         id="feGaussianBlur16259"
+         inkscape:collect="always" />
+    </filter>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient17245"
+       id="linearGradient27074"
+       gradientUnits="userSpaceOnUse"
+       x1="136.5"
+       y1="161.5"
+       x2="313.74622"
+       y2="285.25275" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4451"
+       id="linearGradient27076"
+       gradientUnits="userSpaceOnUse"
+       x1="155.34465"
+       y1="112.46042"
+       x2="136.51547"
+       y2="2.1517708" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient26774"
+       id="linearGradient27078"
+       gradientUnits="userSpaceOnUse"
+       x1="280.27875"
+       y1="261.40704"
+       x2="322.26389"
+       y2="275.19568" />
+    <filter
+       inkscape:collect="always"
+       x="-0.086395349"
+       width="1.1727907"
+       y="-0.11145"
+       height="1.2229"
+       id="filter3497">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="11.145"
+         id="feGaussianBlur3499" />
+    </filter>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3460"
+       id="radialGradient3466"
+       cx="167.48819"
+       cy="192.38739"
+       fx="167.48819"
+       fy="192.38739"
+       r="105.62836"
+       gradientTransform="matrix(0.8393229,-0.4383343,0.2966517,0.5680291,-30.16055,165.27307)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient26774"
+       id="linearGradient2490"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+       x1="280.27875"
+       y1="261.40704"
+       x2="322.26389"
+       y2="275.19568" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4451"
+       id="linearGradient2500"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+       x1="159.38791"
+       y1="126.94874"
+       x2="138.87404"
+       y2="12.596838" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient17245"
+       id="linearGradient2777"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+       x1="136.5"
+       y1="161.5"
+       x2="313.74622"
+       y2="285.25275" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3757"
+       id="radialGradient3763"
+       cx="170.31175"
+       cy="209.16652"
+       fx="170.31175"
+       fy="209.16652"
+       r="104.54334"
+       gradientTransform="matrix(1.0743517,-0.8811517,0.8948667,1.0910737,-193.89727,134.77199)"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     id="layer1"
+     inkscape:groupmode="layer">
+    <g
+       id="g2491">
+      <path
+         transform="matrix(0.9747328,0,0,0.9747328,8.1232897,4.8258519)"
+         inkscape:export-ydpi="98"
+         inkscape:export-xdpi="98"
+         sodipodi:nodetypes="cccccc"
+         id="path2486"
+         d="M 183,323 L 53,296 L 0.99999999,150 L 196,96 L 255,237 L 183,323 z "
+         style="fill:#1a1a1a;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3497)" />
+      <path
+         id="path5140"
+         sodipodi:nodetypes="ccccc"
+         d="M 103.94986,273.26347 L 112.82679,278.13224 L 130.15293,266.67465 L 121.4575,262.08933 L 103.94986,273.26347 z "
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path5128"
+         sodipodi:nodetypes="ccccc"
+         d="M 156.01098,239.64164 L 164.60219,243.86025 L 180.14702,233.7166 L 171.54021,229.64474 L 156.01098,239.64164 z "
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path5132"
+         sodipodi:nodetypes="ccccc"
+         d="M 171.60139,229.6086 L 180.08668,233.62387 L 195.07741,223.67266 L 186.68482,219.84353 L 171.60139,229.6086 z "
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path5130"
+         sodipodi:nodetypes="ccccc"
+         d="M 186.53932,219.8472 L 195.14618,223.77328 L 207.96039,215.29699 L 199.53214,211.5709 L 186.53932,219.8472 z "
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path5134"
+         sodipodi:nodetypes="ccccc"
+         d="M 199.53942,211.55443 L 208.00833,215.29699 L 219.61158,207.48873 L 211.43624,203.97858 L 199.53942,211.55443 z "
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path5136"
+         sodipodi:nodetypes="ccccc"
+         d="M 211.48298,203.99346 L 219.48498,207.43045 L 231.12889,200.02493 L 223.45622,196.26063 L 211.48298,203.99346 z "
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2172"
+         sodipodi:nodetypes="ccccc"
+         d="M 68.307573,278.59889 L 233.3461,375.01483 L 393.09703,219.55258 L 250.10479,166.11978 L 68.307573,278.59889 z "
+         style="fill:#000000;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cccccc"
+         id="path16273"
+         d="M 277.69986,176.50516 C 177.30035,235.42715 239.60255,221.94517 101.71421,297.5792 L 189.47945,349.38523 C 273.26264,323.79193 366.41421,274.97725 355.07543,205.22753 L 295.56691,183.1214 L 277.69986,176.50516 z "
+         style="fill:url(#linearGradient2777);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path2174"
+         sodipodi:nodetypes="ccccc"
+         d="M 68.171626,278.57262 L 68.303363,291.78988 L 232.57786,391.18452 C 235.83055,389.43142 237.23238,379.0975 233.63174,375.00319 L 68.171626,278.57262 z "
+         style="fill:#000000;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path2176"
+         sodipodi:nodetypes="ccccc"
+         d="M 393.07972,219.62716 C 394.063,222.66073 395.10956,229.26471 392.24335,231.97585 L 232.84133,390.92104 C 237.0608,386.09241 235.76133,380.47536 233.63174,374.73973 L 393.07972,219.62716 z "
+         style="fill:#000000;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         d="M 34.974214,77.099241 C 34.974214,77.099241 63.429139,279.82741 65.53691,277.66987 C 67.644683,275.51232 249.44002,164.63325 249.44002,164.63325 L 249.96698,8.789858 L 34.974214,77.099241 z "
+         sodipodi:nodetypes="csccc"
+         id="path2178"
+         style="fill:#1a1a1a;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         inkscape:export-ydpi="98"
+         inkscape:export-xdpi="98"
+         d="M 243.62988,7.3609944 C 248.64901,5.9522606 248.53121,7.8674281 249.93941,8.795189 L 34.974214,76.572297 C 33.041161,74.848362 32.232239,75.552395 27.898382,75.685479 L 243.62988,7.3609944 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2182"
+         style="fill:#1a1a1a;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path3155"
+         sodipodi:nodetypes="ccccc"
+         d="M 41.127572,89.515883 L 247.04644,21.857141 L 245.56959,155.03645 L 66.892268,262.94003 L 41.127572,89.515883 z "
+         style="fill:url(#radialGradient3763);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         id="path4130"
+         sodipodi:nodetypes="ccccc"
+         d="M 270.30146,173.89938 L 92.059548,291.88393 L 83.064049,286.84829 L 262.53913,170.82327 L 270.30146,173.89938 z "
+         style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path5126"
+         sodipodi:nodetypes="ccc"
+         d="M 113.88343,277.34844 L 133.41939,264.57042 L 113.88343,277.34844 z "
+         style="opacity:0.3;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 260.55944,282.16245 C 260.55944,282.16245 299.34305,249.72696 299.34305,249.72696 C 301.56567,247.86815 306.24993,247.63883 309.88553,249.20565 C 309.88553,249.20565 329.40032,257.90584 329.40032,257.90584 C 333.27561,259.57597 334.75677,262.55505 332.67995,264.59388 C 332.67995,264.59388 296.81833,299.27831 296.81833,299.27831 C 294.33747,301.71377 288.97351,302.06916 284.843,300.06406 C 284.843,300.06406 262.71041,289.52115 262.71041,289.52115 C 258.86144,287.6527 257.92393,284.36659 260.55944,282.16245 C 260.55944,282.16245 260.55944,282.16245 260.55944,282.16245"
+         sodipodi:nodetypes="cccccccccc"
+         id="rect2179"
+         style="opacity:0.88999999;fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.97445869;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 156.31199,239.61556 C 158.84932,237.6711 165.25317,241.39833 164.88374,243.74344 L 156.31199,239.61556 z "
+         sodipodi:nodetypes="ccc"
+         id="path5322"
+         style="opacity:0.99720004;fill:#666666;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         d="M 112.5495,278.27384 L 130.07045,266.61299 C 128.6036,263.60624 125.86497,261.45071 121.46704,261.8451 L 104.04747,273.28768 C 107.08494,271.21271 112.98604,275.81472 112.5495,278.27384 z "
+         sodipodi:nodetypes="ccccc"
+         id="path6299"
+         style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         d="M 104.03121,273.29774 C 107.49791,271.05672 113.43311,276.6129 112.52259,278.36122 L 104.03121,273.29774 z "
+         sodipodi:nodetypes="ccc"
+         id="path4349"
+         style="opacity:0.68999999;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path8239"
+         sodipodi:nodetypes="ccccc"
+         d="M 164.84019,243.7543 L 179.63884,233.97378 C 178.89388,231.15559 176.668,229.33065 171.42447,229.78669 L 156.27423,239.63114 C 158.95009,237.51097 165.57347,241.76179 164.84019,243.7543 z "
+         style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         d="M 179.54294,233.98976 L 194.50141,224.24118 C 193.46879,221.32711 190.97123,219.43825 185.96742,220.29382 L 170.84915,230.10631 C 173.88125,228.78365 179.05461,230.45474 179.54294,233.98976 z "
+         sodipodi:nodetypes="ccccc"
+         id="path8241"
+         style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path8243"
+         sodipodi:nodetypes="ccccc"
+         d="M 194.43749,224.22521 L 207.70194,215.40355 C 205.96614,212.71322 203.80418,211.59145 199.45559,211.72786 L 185.90349,220.3098 C 189.09541,219.56246 193.62952,220.65822 194.43749,224.22521 z "
+         style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         d="M 207.74987,215.38757 L 219.33629,207.66862 C 217.34479,205.17006 215.05499,204.11222 211.28174,204.12078 L 199.40765,211.74385 C 202.7434,211.44397 205.80725,212.3959 207.74987,215.38757 z "
+         sodipodi:nodetypes="ccccc"
+         id="path8245"
+         style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path8247"
+         sodipodi:nodetypes="ccccc"
+         d="M 219.30433,207.70059 L 231.03457,199.96565 C 229.55448,196.50822 226.62543,195.89785 223.29965,196.19408 L 211.04202,204.2646 C 214.01019,203.93277 216.67451,204.42126 219.30433,207.70059 z "
+         style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="rect2202"
+         sodipodi:nodetypes="cccccccccc"
+         d="M 221.68659,286.68216 C 221.68659,286.68216 271.7827,246.38199 271.7827,246.38199 C 272.38533,245.90267 273.62888,245.89275 274.57389,246.35926 C 274.57389,246.35926 282.22513,250.1218 282.22513,250.1218 C 283.18438,250.59535 283.48058,251.37156 282.88624,251.86264 C 282.88624,251.86264 233.55382,293.3224 233.55382,293.3224 C 232.81433,293.93337 231.39356,293.96431 230.37209,293.3911 C 230.37209,293.3911 222.1464,288.7742 222.1464,288.7742 C 221.14187,288.21048 220.9385,287.27719 221.68659,286.68216 C 221.68659,286.68216 221.68659,286.68216 221.68659,286.68216"
+         style="fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path2449"
+         d="M 219.03426,288.29491 L 230.39646,294.74997 L 222.03124,301.79784 L 210.47874,295.2849 L 219.03426,288.29491 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2451"
+         d="M 209.4152,296.07932 L 221.07617,302.55531 L 212.38161,309.86664 L 201.01939,302.85172 L 209.4152,296.07932 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2453"
+         d="M 200.12349,303.5516 L 211.48907,310.55261 L 202.5404,317.99996 L 190.60096,311.18361 L 200.12349,303.5516 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2455"
+         d="M 189.70952,311.95745 L 201.6931,318.78476 L 190.71106,328.04619 L 178.53214,321.0788 L 189.70952,311.95745 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2457"
+         sodipodi:nodetypes="ccccc"
+         d="M 273.68393,244.42789 L 284.19406,249.63047 L 292.16408,243.02723 L 281.60172,238.10854 L 273.68393,244.42789 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2459"
+         sodipodi:nodetypes="ccccc"
+         d="M 282.58552,237.35878 L 290.13787,231.26634 L 300.94177,235.89096 L 293.0886,242.26102 L 282.58552,237.35878 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2461"
+         sodipodi:nodetypes="ccccc"
+         d="M 291.16226,230.48888 L 301.87328,235.11565 L 309.36795,228.87453 L 299.02555,224.08989 L 291.16226,230.48888 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2463"
+         sodipodi:nodetypes="ccccc"
+         d="M 289.93538,230.01549 L 280.93975,225.55672 L 289.03737,219.25932 L 297.95995,223.58772 L 289.93538,230.01549 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2465"
+         d="M 177.42321,320.41889 L 166.48786,314.26225 L 188.72682,297.2375 L 199.03429,302.90648 L 177.42321,320.41889 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2467"
+         sodipodi:nodetypes="ccccc"
+         d="M 290.08046,218.43641 L 298.85735,222.73969 L 319.87746,205.75759 L 310.78391,202.19572 L 290.08046,218.43641 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2469"
+         sodipodi:nodetypes="ccccccc"
+         d="M 298.08535,210.7646 L 309.62549,201.69751 L 288.03973,193.31937 L 273.6384,203.95987 L 283.46625,207.8962 L 286.18927,205.81393 L 298.08535,210.7646 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2471"
+         sodipodi:nodetypes="ccccc"
+         d="M 286.62375,192.75442 L 266.91147,207.38925 L 257.59069,203.41102 L 277.74109,189.32694 L 286.62375,192.75442 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2473"
+         d="M 165.36066,313.63832 L 184.46965,298.98505 L 172.33203,292.59392 L 153.19109,306.83736 L 165.36066,313.63832 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2475"
+         d="M 151.95557,306.23713 L 167.0892,294.93513 L 156.09726,289.01503 L 140.88977,299.91382 L 151.95557,306.23713 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2477"
+         d="M 139.74507,299.3323 L 150.85746,291.335 L 140.35153,285.53863 L 129.36288,293.43149 L 139.74507,299.3323 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2497"
+         d="M 141.44916,284.84419 L 150.48582,278.42628 L 160.93153,283.91261 L 151.79607,290.59399 L 141.44916,284.84419 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2499"
+         d="M 200.06639,302.0818 L 208.31119,295.49502 L 197.64102,290.05561 L 189.67659,296.43646 L 200.06639,302.0818 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2501"
+         d="M 185.44374,298.19559 L 193.86354,291.59284 L 181.96116,285.25056 L 173.47309,291.62812 L 185.44374,298.19559 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2503"
+         d="M 168.25222,294.17765 L 176.70261,287.76919 L 165.64522,281.91859 L 157.20372,288.20299 L 168.25222,294.17765 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 151.55607,277.71812 L 160.35985,271.62622 L 170.55671,277.01143 L 161.94955,283.18847 L 151.55607,277.71812 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2505"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2507"
+         d="M 177.75365,287.00502 L 186.23698,280.4648 L 175.54186,274.93789 L 166.63928,281.22229 L 177.75365,287.00502 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2509"
+         d="M 161.45339,270.89479 L 170.04758,264.82618 L 180.19785,270.07166 L 171.68385,276.24869 L 161.45339,270.89479 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2511"
+         d="M 194.72496,290.89785 L 203.34236,284.16335 L 191.50585,278.08454 L 183.01779,284.4621 L 194.72496,290.89785 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2513"
+         d="M 209.44739,294.73981 L 218.00506,287.74135 L 207.50073,282.4405 L 198.6953,289.16034 L 209.44739,294.73981 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2515"
+         d="M 219.03117,287.00034 L 227.95111,279.8372 L 217.57851,274.66809 L 208.54255,281.61848 L 219.03117,287.00034 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2517"
+         d="M 229.01016,279.06327 L 236.63632,273.08946 L 226.18986,268.0248 L 218.58741,273.879 L 229.01016,279.06327 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2519"
+         d="M 237.61157,272.25159 L 246.12267,265.48601 L 235.81592,260.50649 L 227.24338,267.18776 L 237.61157,272.25159 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 246.92622,264.70405 L 255.31922,258.03494 L 245.2101,253.19516 L 236.79987,259.71739 L 246.92622,264.70405 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2521"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2523"
+         sodipodi:nodetypes="ccccc"
+         d="M 256.24944,257.30562 L 264.00138,251.14415 L 253.91623,246.49536 L 246.24255,252.47136 L 256.24944,257.30562 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 264.94343,250.38346 L 271.79281,244.57197 L 262.14241,240.07284 L 254.8806,245.75341 L 264.94343,250.38346 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2525"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2527"
+         sodipodi:nodetypes="ccccc"
+         d="M 272.6829,243.79667 L 280.45445,237.62292 L 270.90284,233.32138 L 263.17995,239.29836 L 272.6829,243.79667 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 281.41039,236.84761 L 288.91847,230.83852 L 279.86087,226.40525 L 272.03919,232.4481 L 281.41039,236.84761 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2529"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2531"
+         d="M 171.18769,264.04817 L 180.52708,257.51381 L 190.25819,262.57298 L 181.32501,269.3555 L 171.18769,264.04817 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2533"
+         sodipodi:nodetypes="ccccc"
+         d="M 181.56334,256.75316 L 190.15752,250.68454 L 199.60916,255.83687 L 191.23489,261.73446 L 181.56334,256.75316 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 191.13838,249.97224 L 200.12848,243.67074 L 209.58148,248.76519 L 200.57843,255.12855 L 191.13838,249.97224 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2535"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2537"
+         sodipodi:nodetypes="ccccc"
+         d="M 201.14148,242.94942 L 208.96784,237.50889 L 218.76812,242.01382 L 210.62107,248.07043 L 201.14148,242.94942 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 209.93861,236.70829 L 218.48624,230.77939 L 228.17074,235.18651 L 219.79647,241.31698 L 209.93861,236.70829 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2539"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2541"
+         sodipodi:nodetypes="ccccc"
+         d="M 219.48896,230.08656 L 227.94108,224.18895 L 237.62559,228.38647 L 229.19014,234.46609 L 219.48896,230.08656 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 229.04981,223.34939 L 237.64399,217.28078 L 247.18879,221.5016 L 238.67479,227.67863 L 229.04981,223.34939 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2543"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2545"
+         sodipodi:nodetypes="ccccc"
+         d="M 238.75966,216.51376 L 246.46891,211.02733 L 256.03698,215.155 L 248.29149,220.74984 L 238.75966,216.51376 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 247.52515,210.2999 L 256.52687,204.13813 L 265.95523,208.14936 L 257.24328,214.48941 L 247.52515,210.2999 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2547"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2242"
+         d="M 192.42707,277.38276 L 201.04243,270.93376 L 212.81727,276.9437 L 204.25518,283.46405 L 192.42707,277.38276 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2244"
+         d="M 202.01582,270.12726 L 210.21341,264.10219 L 221.92044,269.93134 L 213.74806,276.20857 L 202.01582,270.12726 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2246"
+         d="M 211.22102,263.35122 L 219.52192,257.1363 L 231.06172,262.8037 L 222.85738,269.20877 L 211.22102,263.35122 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2248"
+         sodipodi:nodetypes="ccccc"
+         d="M 220.48694,256.41147 L 231.99345,262.05286 L 239.51447,256.25393 L 228.07804,250.72214 C 228.07804,250.72214 220.51891,256.44343 220.48694,256.41147 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 228.98488,249.90241 L 240.4914,255.5438 L 248.01241,249.74486 L 236.57599,244.21308 C 236.57599,244.21308 229.01684,249.93436 228.98488,249.90241 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2250"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2252"
+         sodipodi:nodetypes="ccccc"
+         d="M 237.56868,243.44725 L 248.97931,248.99274 L 257.07404,242.57877 L 246.02277,237.37437 L 237.56868,243.44725 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 247.06561,236.56786 L 258.0749,241.84765 L 265.97947,235.69713 L 254.71266,230.82259 L 247.06561,236.56786 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2254"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2256"
+         sodipodi:nodetypes="ccccc"
+         d="M 255.89713,229.9869 L 266.95163,234.90506 L 274.38224,229.19653 L 263.30742,224.43317 L 255.89713,229.9869 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 264.32207,223.68092 L 275.3101,228.44997 L 282.89504,222.58711 L 271.91318,217.99159 L 264.32207,223.68092 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2258"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2260"
+         sodipodi:nodetypes="ccccc"
+         d="M 272.9276,217.22814 L 283.86907,221.82988 L 290.30525,216.88979 L 279.45645,212.28463 L 272.9276,217.22814 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 280.51495,211.48086 L 291.20783,216.17301 L 297.14678,211.54933 L 286.38838,207.05719 L 280.51495,211.48086 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2262"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2264"
+         d="M 187.21456,279.65363 L 195.69788,273.11343 L 185.00277,267.58651 L 176.10019,273.87091 L 187.21456,279.65363 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2266"
+         d="M 196.93117,272.23833 L 205.68571,265.78852 L 194.71938,260.1712 L 185.8168,266.4556 L 196.93117,272.23833 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2268"
+         sodipodi:nodetypes="ccccc"
+         d="M 206.76011,265.04128 L 215.60505,258.63667 L 204.90993,253.42617 L 196.23337,259.52977 L 206.76011,265.04128 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 216.55615,257.79127 L 225.72667,251.12321 L 215.30323,245.97983 L 205.95318,252.6478 L 216.55615,257.79127 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2270"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2272"
+         sodipodi:nodetypes="ccccc"
+         d="M 226.62217,250.28446 L 234.42747,244.46748 L 224.14692,239.49074 L 216.41185,245.13456 L 226.62217,250.28446 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 235.3202,243.66108 L 242.92208,238.20571 L 232.8824,233.22122 L 225.20027,238.78239 L 235.3202,243.66108 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2274"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2276"
+         sodipodi:nodetypes="ccccc"
+         d="M 244.04269,237.40717 L 251.47219,231.70252 L 241.73406,226.77098 L 234.01317,232.34768 L 244.04269,237.40717 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 252.6522,231.11226 L 260.3045,225.27523 L 250.58643,220.64138 L 242.89843,226.09476 L 252.6522,231.11226 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2278"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2280"
+         sodipodi:nodetypes="ccccc"
+         d="M 261.23976,224.42239 L 268.45876,219.21044 L 258.80517,214.83417 L 251.72352,219.79033 L 261.23976,224.42239 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 269.52519,218.43638 L 275.83883,213.61385 L 266.36416,209.30737 L 259.8121,214.10012 L 269.52519,218.43638 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2282"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2284"
+         sodipodi:nodetypes="ccccc"
+         d="M 276.80269,212.87655 L 282.2575,208.64164 L 272.64721,204.83239 L 267.406,208.63069 L 276.80269,212.87655 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="rect3291"
+         d="M 302.87759,234.27933 C 302.87759,234.27933 308.38212,229.69548 308.38212,229.69548 C 308.92862,229.24038 308.77424,228.59985 308.03355,228.25718 C 308.03355,228.25718 300.43832,224.74347 300.43832,224.74347 C 299.65719,224.38211 298.56475,224.46487 297.99153,224.93134 C 297.99153,224.93134 292.2163,229.63113 292.2163,229.63113 C 291.63275,230.10601 291.81664,230.77156 292.62585,231.12111 C 292.62585,231.12111 300.49174,234.51889 300.49174,234.51889 C 301.25859,234.85015 302.32155,234.74235 302.87759,234.27933 C 302.87759,234.27933 302.87759,234.27933 302.87759,234.27933"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3293"
+         d="M 235.3731,272.47703 C 235.3731,272.47703 227.42329,268.6228 227.42329,268.6228 C 226.73948,268.29128 225.78761,268.33455 225.2875,268.71966 C 225.2875,268.71966 219.50192,273.17479 219.50192,273.17479 C 218.99549,273.56477 219.13572,274.15174 219.81793,274.49107 C 219.81793,274.49107 227.7497,278.43631 227.7497,278.43631 C 228.44749,278.78339 229.41954,278.74258 229.9276,278.34461 C 229.9276,278.34461 235.73123,273.79845 235.73123,273.79845 C 236.23285,273.4055 236.07241,272.81607 235.3731,272.47703 C 235.3731,272.47703 235.3731,272.47703 235.3731,272.47703"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3299"
+         d="M 247.43674,211.44485 C 247.43674,211.44485 255.04475,214.72695 255.04475,214.72695 C 255.59412,214.96395 255.68572,215.40873 255.24904,215.72416 C 255.24904,215.72416 249.09015,220.17295 249.09015,220.17295 C 248.64784,220.49244 247.85024,220.55375 247.30291,220.31051 C 247.30291,220.31051 239.72373,216.94221 239.72373,216.94221 C 239.18929,216.7047 239.11431,216.26136 239.55452,215.94809 C 239.55452,215.94809 245.68459,211.58552 245.68459,211.58552 C 246.11924,211.27618 246.90023,211.21341 247.43674,211.44485 C 247.43674,211.44485 247.43674,211.44485 247.43674,211.44485"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3301"
+         d="M 211.38791,264.687 C 211.38791,264.687 220.69642,269.32187 220.69642,269.32187 C 221.37384,269.65917 221.55369,270.21304 221.09728,270.56361 C 221.09728,270.56361 214.59913,275.55485 214.59913,275.55485 C 214.12804,275.9167 213.20005,275.92451 212.52093,275.57249 C 212.52093,275.57249 203.19239,270.73714 203.19239,270.73714 C 202.53988,270.39891 202.39682,269.84723 202.86919,269.50005 C 202.86919,269.50005 209.38739,264.7093 209.38739,264.7093 C 209.84537,264.37269 210.73656,264.36268 211.38791,264.687 C 211.38791,264.687 211.38791,264.687 211.38791,264.687"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3305"
+         d="M 190.80124,251.03544 C 190.80124,251.03544 198.99762,255.5035 198.99762,255.5035 C 199.33669,255.68835 199.3609,256.01171 199.05255,256.22886 C 199.05255,256.22886 191.79012,261.34344 191.79012,261.34344 C 191.48252,261.56007 190.95608,261.59085 190.60912,261.41215 C 190.60912,261.41215 182.222,257.0924 182.222,257.0924 C 181.85741,256.90462 181.81745,256.57372 182.13313,256.35082 C 182.13313,256.35082 189.58629,251.08792 189.58629,251.08792 C 189.90275,250.86444 190.44492,250.8412 190.80124,251.03544 C 190.80124,251.03544 190.80124,251.03544 190.80124,251.03544"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3309"
+         d="M 200.86922,319.47957 C 200.86922,319.47957 191.5626,327.32806 191.5626,327.32806 C 191.09111,327.72566 190.29604,327.80875 189.78059,327.51386 C 189.78059,327.51386 179.45944,321.6093 179.45944,321.6093 C 178.94568,321.31539 178.91894,320.76315 179.3988,320.37157 C 179.3988,320.37157 188.87097,312.64175 188.87097,312.64175 C 189.33581,312.26242 190.11645,312.18928 190.62199,312.47729 C 190.62199,312.47729 200.77757,318.26317 200.77757,318.26317 C 201.28475,318.55212 201.32593,319.09442 200.86922,319.47957 C 200.86922,319.47957 200.86922,319.47957 200.86922,319.47957"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3311"
+         d="M 227.03328,279.37981 C 227.03328,279.37981 218.4746,275.11465 218.4746,275.11465 C 217.9779,274.86712 217.23368,274.93335 216.80449,275.26346 C 216.80449,275.26346 209.3489,280.99824 209.3489,280.99824 C 208.90258,281.34154 208.94628,281.82562 209.44841,282.08328 C 209.44841,282.08328 218.10282,286.52398 218.10282,286.52398 C 218.61686,286.78775 219.38667,286.71486 219.82736,286.36095 C 219.82736,286.36095 227.18725,280.45063 227.18725,280.45063 C 227.61081,280.1105 227.5415,279.63307 227.03328,279.37981 C 227.03328,279.37981 227.03328,279.37981 227.03328,279.37981"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3313"
+         d="M 200.87727,244.07429 C 200.87727,244.07429 208.8242,248.35707 208.8242,248.35707 C 209.24365,248.58313 209.26299,248.99029 208.86725,249.27001 C 208.86725,249.27001 201.29858,254.61953 201.29858,254.61953 C 200.89969,254.90148 200.24107,254.94426 199.82216,254.71545 C 199.82216,254.71545 191.88613,250.38067 191.88613,250.38067 C 191.47181,250.15435 191.45917,249.7474 191.8575,249.46819 C 191.8575,249.46819 199.41526,244.17067 199.41526,244.17067 C 199.81044,243.89367 200.46234,243.85067 200.87727,244.07429 C 200.87727,244.07429 200.87727,244.07429 200.87727,244.07429"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3317"
+         d="M 168.98052,312.35404 C 168.98052,312.35404 186.52228,298.92516 186.52228,298.92516 C 187.74742,297.98727 189.20942,297.50291 189.80916,297.83277 C 189.80916,297.83277 197.94313,302.30636 197.94313,302.30636 C 198.54751,302.63875 198.08283,303.67748 196.89261,304.64197 C 196.89261,304.64197 179.8462,318.45547 179.8462,318.45547 C 178.50836,319.53957 176.90663,320.12806 176.26526,319.76696 C 176.26526,319.76696 167.6358,314.90855 167.6358,314.90855 C 166.99971,314.55042 167.60418,313.40767 168.98052,312.35404 C 168.98052,312.35404 168.98052,312.35404 168.98052,312.35404"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3319"
+         d="M 192.5101,290.87165 C 192.5101,290.87165 183.27464,285.95046 183.27464,285.95046 C 182.54633,285.56237 181.54412,285.5639 181.0251,285.95388 C 181.0251,285.95388 174.43893,290.90244 174.43893,290.90244 C 173.90432,291.3041 174.06146,291.95091 174.79375,292.35267 C 174.79375,292.35267 184.08217,297.44861 184.08217,297.44861 C 184.83581,297.86207 185.87162,297.86005 186.40206,297.44408 C 186.40206,297.44408 192.93525,292.32079 192.93525,292.32079 C 193.44997,291.91714 193.25925,291.27084 192.5101,290.87165 C 192.5101,290.87165 192.5101,290.87165 192.5101,290.87165"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3321"
+         d="M 185.1382,279.89699 C 185.1382,279.89699 176.63387,275.50221 176.63387,275.50221 C 176.02877,275.18952 175.15088,275.21388 174.66322,275.55813 C 174.66322,275.55813 167.58535,280.55447 167.58535,280.55447 C 167.06223,280.92372 167.14522,281.48553 167.77396,281.81266 C 167.77396,281.81266 176.61165,286.41084 176.61165,286.41084 C 177.2442,286.73996 178.15674,286.69425 178.65527,286.3099 C 178.65527,286.3099 185.39983,281.11021 185.39983,281.11021 C 185.86446,280.752 185.74682,280.2115 185.1382,279.89699 C 185.1382,279.89699 185.1382,279.89699 185.1382,279.89699"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3323"
+         d="M 161.12258,272.02905 C 161.12258,272.02905 169.7964,276.60989 169.7964,276.60989 C 170.21763,276.83236 170.27487,277.2137 169.92432,277.46528 C 169.92432,277.46528 162.60291,282.7196 162.60291,282.7196 C 162.24117,282.9792 161.60395,283.00656 161.17461,282.7806 C 161.17461,282.7806 152.33353,278.12732 152.33353,278.12732 C 151.90283,277.90063 151.85435,277.51172 152.22438,277.25569 C 152.22438,277.25569 159.71303,272.0738 159.71303,272.0738 C 160.07158,271.82571 160.70003,271.80589 161.12258,272.02905 C 161.12258,272.02905 161.12258,272.02905 161.12258,272.02905"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3325"
+         d="M 220.2904,303.21609 C 220.2904,303.21609 213.19436,309.1832 213.19436,309.1832 C 212.74444,309.56153 211.88835,309.56212 211.27912,309.18598 C 211.27912,309.18598 202.00839,303.46232 202.00839,303.46232 C 201.45919,303.12325 201.36941,302.5694 201.80347,302.21926 C 201.80347,302.21926 208.65568,296.69198 208.65568,296.69198 C 209.07675,296.35232 209.86703,296.33025 210.43121,296.64355 C 210.43121,296.64355 219.94579,301.92755 219.94579,301.92755 C 220.57046,302.27448 220.72603,302.84974 220.2904,303.21609 C 220.2904,303.21609 220.2904,303.21609 220.2904,303.21609"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3327"
+         d="M 250.18155,231.04893 C 250.18155,231.04893 243.02922,227.42687 243.02922,227.42687 C 242.31181,227.06355 241.28875,227.09263 240.73358,227.49361 C 240.73358,227.49361 235.06335,231.58914 235.06335,231.58914 C 234.48252,232.00866 234.60822,232.64785 235.34714,233.02061 C 235.34714,233.02061 242.71349,236.73664 242.71349,236.73664 C 243.44994,237.10815 244.49431,237.0604 245.05318,236.63127 C 245.05318,236.63127 250.50942,232.44177 250.50942,232.44177 C 251.04367,232.03155 250.89663,231.41106 250.18155,231.04893 C 250.18155,231.04893 250.18155,231.04893 250.18155,231.04893"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3329"
+         d="M 241.73896,237.6183 C 241.73896,237.6183 234.09875,233.82511 234.09875,233.82511 C 233.42547,233.49084 232.47146,233.51871 231.96125,233.88804 C 231.96125,233.88804 226.11501,238.1202 226.11501,238.1202 C 225.60814,238.48713 225.74764,239.04628 226.42628,239.37343 C 226.42628,239.37343 234.12755,243.08611 234.12755,243.08611 C 234.78882,243.40491 235.72382,243.37142 236.22543,243.01146 C 236.22543,243.01146 242.01062,238.85982 242.01062,238.85982 C 242.51547,238.49751 242.39495,237.94399 241.73896,237.6183 C 241.73896,237.6183 241.73896,237.6183 241.73896,237.6183"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3331"
+         d="M 291.06923,231.66501 C 291.06923,231.66501 299.93537,235.46017 299.93537,235.46017 C 300.49195,235.69842 300.62867,236.14494 300.23967,236.46046 C 300.23967,236.46046 293.79405,241.68879 293.79405,241.68879 C 293.40327,242.00578 292.65126,242.0569 292.11015,241.80432 C 292.11015,241.80432 283.49087,237.78134 283.49087,237.78134 C 282.98847,237.54685 282.88811,237.11468 283.26389,236.81155 C 283.26389,236.81155 289.4626,231.81108 289.4626,231.81108 C 289.83674,231.50927 290.5524,231.44378 291.06923,231.66501 C 291.06923,231.66501 291.06923,231.66501 291.06923,231.66501"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3333"
+         d="M 214.6618,258.17714 C 214.6618,258.17714 205.79323,253.8565 205.79323,253.8565 C 205.30323,253.61778 204.58399,253.65545 204.17864,253.94061 C 204.17864,253.94061 196.98327,259.00223 196.98327,259.00223 C 196.56804,259.29434 196.62028,259.73235 197.10235,259.98475 C 197.10235,259.98475 205.83129,264.55497 205.83129,264.55497 C 206.34512,264.82401 207.10144,264.79412 207.5249,264.48748 C 207.5249,264.48748 214.85991,259.17623 214.85991,259.17623 C 215.27294,258.87715 215.18363,258.43136 214.6618,258.17714 C 214.6618,258.17714 214.6618,258.17714 214.6618,258.17714"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3335"
+         d="M 238.61522,217.71027 C 238.61522,217.71027 246.20482,221.06648 246.20482,221.06648 C 246.74975,221.30745 246.80537,221.77975 246.32841,222.12581 C 246.32841,222.12581 239.55856,227.03746 239.55856,227.03746 C 239.06928,227.39242 238.23201,227.47949 237.68245,227.2323 C 237.68245,227.2323 230.0291,223.78987 230.0291,223.78987 C 229.48641,223.54578 229.44798,223.06823 229.94181,222.71952 C 229.94181,222.71952 236.77542,217.89409 236.77542,217.89409 C 237.25693,217.55409 238.07699,217.47225 238.61522,217.71027 C 238.61522,217.71027 238.61522,217.71027 238.61522,217.71027"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3337"
+         d="M 228.91723,224.61205 C 228.91723,224.61205 236.61775,227.94965 236.61775,227.94965 C 237.17563,228.19144 237.24665,228.65958 236.77513,228.99941 C 236.77513,228.99941 230.06777,233.83356 230.06777,233.83356 C 229.58195,234.18369 228.73926,234.26255 228.18027,234.01019 C 228.18027,234.01019 220.46652,230.52787 220.46652,230.52787 C 219.92451,230.28318 219.88144,229.8127 220.36809,229.47313 C 220.36809,229.47313 227.0887,224.78372 227.0887,224.78372 C 227.56129,224.45396 228.37602,224.37747 228.91723,224.61205 C 228.91723,224.61205 228.91723,224.61205 228.91723,224.61205"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3339"
+         d="M 257.14967,204.40311 C 257.14967,204.40311 265.32623,207.88176 265.32623,207.88176 C 265.67464,208.02998 265.706,208.33073 265.3957,208.55655 C 265.3957,208.55655 257.84099,214.05443 257.84099,214.05443 C 257.51021,214.29515 256.95402,214.3647 256.59485,214.20987 C 256.59485,214.20987 248.16698,210.57659 248.16698,210.57659 C 247.81135,210.42328 247.8009,210.11114 248.14264,209.87723 C 248.14264,209.87723 255.94863,204.53395 255.94863,204.53395 C 256.2693,204.31446 256.80459,204.25629 257.14967,204.40311 C 257.14967,204.40311 257.14967,204.40311 257.14967,204.40311"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3341"
+         d="M 201.8511,271.34651 C 201.8511,271.34651 212.00249,276.52784 212.00249,276.52784 C 212.45381,276.7582 212.55592,277.14272 212.23112,277.39008 C 212.23112,277.39008 204.84954,283.01143 204.84954,283.01143 C 204.52035,283.26212 203.89008,283.27634 203.43671,283.04324 C 203.43671,283.04324 193.23937,277.8004 193.23937,277.8004 C 192.78929,277.56899 192.69387,277.18305 193.0251,276.9351 C 193.0251,276.9351 200.45262,271.37525 200.45262,271.37525 C 200.77944,271.1306 201.40305,271.11781 201.8511,271.34651 C 201.8511,271.34651 201.8511,271.34651 201.8511,271.34651"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3343"
+         d="M 219.21667,231.1118 C 219.21667,231.1118 227.42383,234.84661 227.42383,234.84661 C 227.83744,235.03484 227.89363,235.38938 227.54865,235.64192 C 227.54865,235.64192 220.45221,240.83693 220.45221,240.83693 C 220.08929,241.10262 219.45708,241.15831 219.03594,240.96141 C 219.03594,240.96141 210.6819,237.05577 210.6819,237.05577 C 210.26995,236.86319 210.23739,236.50104 210.60771,236.24418 C 210.60771,236.24418 217.85107,231.21996 217.85107,231.21996 C 218.20329,230.97566 218.81185,230.92758 219.21667,231.1118 C 219.21667,231.1118 219.21667,231.1118 219.21667,231.1118"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3345"
+         d="M 166.11203,294.40885 C 166.11203,294.40885 157.04241,289.52408 157.04241,289.52408 C 156.51843,289.24186 155.52217,289.42717 154.80588,289.94053 C 154.80588,289.94053 142.25863,298.93279 142.25863,298.93279 C 141.50124,299.47559 141.31352,300.15596 141.84075,300.45723 C 141.84075,300.45723 150.97129,305.67469 150.97129,305.67469 C 151.51617,305.98604 152.56442,305.78244 153.31853,305.21926 C 153.31853,305.21926 165.80481,295.89435 165.80481,295.89435 C 166.51725,295.36228 166.65299,294.70021 166.11203,294.40885 C 166.11203,294.40885 166.11203,294.40885 166.11203,294.40885"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3349"
+         d="M 151.01455,278.70397 C 151.01455,278.70397 160.37455,283.62006 160.37455,283.62006 C 160.68292,283.78202 160.7229,284.06518 160.46357,284.25485 C 160.46357,284.25485 152.27741,290.24195 152.27741,290.24195 C 152.01084,290.43692 151.5497,290.45709 151.24414,290.28729 C 151.24414,290.28729 141.97268,285.13512 141.97268,285.13512 C 141.68247,284.97384 141.66152,284.69337 141.92511,284.50616 C 141.92511,284.50616 150.02275,278.75515 150.02275,278.75515 C 150.27937,278.57289 150.72146,278.55003 151.01455,278.70397 C 151.01455,278.70397 151.01455,278.70397 151.01455,278.70397"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3351"
+         d="M 175.52443,287.1458 C 175.52443,287.1458 166.79876,282.52893 166.79876,282.52893 C 166.15936,282.19062 165.25137,282.21179 164.76171,282.57631 C 164.76171,282.57631 158.10028,287.53553 158.10028,287.53553 C 157.60379,287.90516 157.7174,288.48077 158.35625,288.82623 C 158.35625,288.82623 167.07489,293.541 167.07489,293.541 C 167.72676,293.8935 168.65274,293.87392 169.1498,293.49696 C 169.1498,293.49696 175.81827,288.43985 175.81827,288.43985 C 176.30838,288.06815 176.17677,287.49096 175.52443,287.1458 C 175.52443,287.1458 175.52443,287.1458 175.52443,287.1458"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3353"
+         d="M 201.97097,283.45905 C 201.97097,283.45905 192.78768,278.74285 192.78768,278.74285 C 192.07622,278.37747 191.08839,278.3982 190.5689,278.78852 C 190.5689,278.78852 183.98269,283.7371 183.98269,283.7371 C 183.44857,284.1384 183.58158,284.77203 184.28486,285.15865 C 184.28486,285.15865 193.36778,290.15177 193.36778,290.15177 C 194.11817,290.56428 195.16258,290.55584 195.70514,290.13183 C 195.70514,290.13183 202.39168,284.90631 202.39168,284.90631 C 202.9188,284.49436 202.72924,283.84847 201.97097,283.45905 C 201.97097,283.45905 201.97097,283.45905 201.97097,283.45905"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3355"
+         d="M 183.209,298.32124 C 183.209,298.32124 173.55788,293.23939 173.55788,293.23939 C 172.87828,292.88156 171.48018,293.22782 170.41912,294.01738 C 170.41912,294.01738 155.20003,305.34244 155.20003,305.34244 C 154.08849,306.16958 153.73855,307.14332 154.41967,307.52396 C 154.41967,307.52396 164.0962,312.93167 164.0962,312.93167 C 164.79619,313.32286 166.25692,312.95103 167.36704,312.09976 C 167.36704,312.09976 182.56068,300.44889 182.56068,300.44889 C 183.61957,299.63691 183.90689,298.68872 183.209,298.32124 C 183.209,298.32124 183.209,298.32124 183.209,298.32124"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3357"
+         d="M 181.25173,257.89055 C 181.25173,257.89055 189.52937,262.19408 189.52937,262.19408 C 189.93309,262.40396 189.973,262.78951 189.61763,263.05932 C 189.61763,263.05932 182.01974,268.82803 182.01974,268.82803 C 181.63543,269.11981 180.98628,269.17817 180.56566,268.95795 C 180.56566,268.95795 171.94248,264.44335 171.94248,264.44335 C 171.5243,264.2244 171.5122,263.82114 171.91393,263.54006 C 171.91393,263.54006 179.85732,257.98241 179.85732,257.98241 C 180.2289,257.72244 180.85025,257.68182 181.25173,257.89055 C 181.25173,257.89055 181.25173,257.89055 181.25173,257.89055"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3359"
+         d="M 207.30074,294.9799 C 207.30074,294.9799 198.59287,290.54083 198.59287,290.54083 C 198.06485,290.27167 197.31412,290.31751 196.90812,290.6428 C 196.90812,290.6428 190.40778,295.85065 190.40778,295.85065 C 190.00268,296.17521 190.08929,296.66071 190.60345,296.94008 C 190.60345,296.94008 199.08253,301.54721 199.08253,301.54721 C 199.62684,301.84297 200.40395,301.81213 200.82331,301.47711 C 200.82331,301.47711 207.55245,296.10117 207.55245,296.10117 C 207.97277,295.76537 207.85976,295.26488 207.30074,294.9799 C 207.30074,294.9799 207.30074,294.9799 207.30074,294.9799"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3361"
+         d="M 210.31834,311.52693 C 210.31834,311.52693 203.74605,316.99659 203.74605,316.99659 C 203.0788,317.55189 201.86,317.61152 201.01035,317.12644 C 201.01035,317.12644 192.24289,312.121 192.24289,312.121 C 191.3355,311.60296 191.17426,310.72413 191.88471,310.15474 C 191.88471,310.15474 198.87844,304.54948 198.87844,304.54948 C 199.56891,303.99608 200.82226,303.98204 201.68556,304.51382 C 201.68556,304.51382 210.03168,309.65489 210.03168,309.65489 C 210.84096,310.15338 210.96757,310.98662 210.31834,311.52693 C 210.31834,311.52693 210.31834,311.52693 210.31834,311.52693"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3363"
+         d="M 229.40869,295.58217 C 229.40869,295.58217 223.04271,300.94565 223.04271,300.94565 C 222.48275,301.41742 221.41805,301.45214 220.65599,301.0225 C 220.65599,301.0225 211.86431,296.06605 211.86431,296.06605 C 211.09687,295.63339 210.94055,294.9076 211.51326,294.43967 C 211.51326,294.43967 218.02408,289.12024 218.02408,289.12024 C 218.58411,288.66268 219.64217,288.64028 220.39694,289.06907 C 220.39694,289.06907 229.04379,293.98149 229.04379,293.98149 C 229.79334,294.40733 229.95631,295.12081 229.40869,295.58217 C 229.40869,295.58217 229.40869,295.58217 229.40869,295.58217"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3365"
+         d="M 217.05231,287.26056 C 217.05231,287.26056 208.43084,282.90987 208.43084,282.90987 C 207.9153,282.6497 207.16011,282.70044 206.73573,283.02431 C 206.73573,283.02431 199.50923,288.5392 199.50923,288.5392 C 199.05894,288.88283 199.11946,289.38044 199.64696,289.65417 C 199.64696,289.65417 208.47177,294.23353 208.47177,294.23353 C 209.01197,294.51386 209.80095,294.45066 210.23875,294.09263 C 210.23875,294.09263 217.26191,288.3491 217.26191,288.3491 C 217.67418,288.01194 217.57986,287.52677 217.05231,287.26056 C 217.05231,287.26056 217.05231,287.26056 217.05231,287.26056"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3367"
+         d="M 170.90895,265.27133 C 170.90895,265.27133 179.32655,269.62137 179.32655,269.62137 C 179.80913,269.87078 179.87784,270.30382 179.47992,270.59253 C 179.47992,270.59253 172.41934,275.71508 172.41934,275.71508 C 172.01209,276.01056 171.29204,276.04365 170.8056,275.78907 C 170.8056,275.78907 162.3215,271.34911 162.3215,271.34911 C 161.84045,271.09734 161.7847,270.66085 162.19576,270.37059 C 162.19576,270.37059 169.32283,265.33795 169.32283,265.33795 C 169.72453,265.05428 170.43163,265.02465 170.90895,265.27133 C 170.90895,265.27133 170.90895,265.27133 170.90895,265.27133"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3369"
+         d="M 204.55438,265.20903 C 204.55438,265.20903 195.83444,260.74237 195.83444,260.74237 C 195.21646,260.42583 194.32033,260.45289 193.82368,260.80347 C 193.82368,260.80347 186.74496,265.80041 186.74496,265.80041 C 186.23123,266.16305 186.32053,266.71769 186.94675,267.04351 C 186.94675,267.04351 195.78441,271.64167 195.78441,271.64167 C 196.41947,271.97209 197.33877,271.93803 197.84403,271.56578 C 197.84403,271.56578 204.80505,266.43735 204.80505,266.43735 C 205.29337,266.07757 205.18091,265.52994 204.55438,265.20903 C 204.55438,265.20903 204.55438,265.20903 204.55438,265.20903"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3371"
+         d="M 194.5991,272.5456 C 194.5991,272.5456 186.09478,268.15083 186.09478,268.15083 C 185.48971,267.83814 184.61179,267.8625 184.12412,268.20675 C 184.12412,268.20675 177.04625,273.20308 177.04625,273.20308 C 176.52314,273.57235 176.60616,274.13416 177.23487,274.46127 C 177.23487,274.46127 186.07255,279.05946 186.07255,279.05946 C 186.70512,279.38857 187.61765,279.34288 188.11618,278.95853 C 188.11618,278.95853 194.86074,273.75884 194.86074,273.75884 C 195.32537,273.40062 195.20773,272.86013 194.5991,272.5456 C 194.5991,272.5456 194.5991,272.5456 194.5991,272.5456"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3373"
+         d="M 284.73208,249.18471 C 284.73208,249.18471 291.64382,243.45826 291.64382,243.45826 C 291.9322,243.21935 291.84218,242.87732 291.44316,242.6915 C 291.44316,242.6915 282.28362,238.42609 282.28362,238.42609 C 281.90552,238.25002 281.37123,238.2925 281.08461,238.52125 C 281.08461,238.52125 274.21817,244.0015 274.21817,244.0015 C 273.92236,244.23759 273.98607,244.57745 274.36212,244.76359 C 274.36212,244.76359 283.47634,249.2752 283.47634,249.2752 C 283.87359,249.47184 284.43418,249.43153 284.73208,249.18471 C 284.73208,249.18471 284.73208,249.18471 284.73208,249.18471"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3375"
+         d="M 266.25849,207.11055 C 266.25849,207.11055 258.22296,203.68087 258.22296,203.68087 C 257.8725,203.5313 258.26258,202.94141 259.09217,202.36158 C 259.09217,202.36158 276.45653,190.22478 276.45653,190.22478 C 277.16993,189.72614 278.01001,189.4307 278.34442,189.55973 C 278.34442,189.55973 286.00228,192.51461 286.00228,192.51461 C 286.34641,192.64741 286.06579,193.16868 285.36877,193.68615 C 285.36877,193.68615 268.38219,206.29734 268.38219,206.29734 C 267.56963,206.9006 266.62006,207.26486 266.25849,207.11055 C 266.25849,207.11055 266.25849,207.11055 266.25849,207.11055"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3377"
+         d="M 150.04703,290.88787 C 150.04703,290.88787 141.14376,285.97573 141.14376,285.97573 C 140.70469,285.73348 139.97814,285.80683 139.51431,286.13998 C 139.51431,286.13998 130.20189,292.82886 130.20189,292.82886 C 129.7371,293.1627 129.71188,293.62984 130.14579,293.87645 C 130.14579,293.87645 138.94417,298.87711 138.94417,298.87711 C 139.38768,299.12918 140.1235,299.05996 140.59351,298.72169 C 140.59351,298.72169 150.01083,291.94429 150.01083,291.94429 C 150.47987,291.60674 150.49581,291.13546 150.04703,290.88787 C 150.04703,290.88787 150.04703,290.88787 150.04703,290.88787"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3379"
+         sodipodi:nodetypes="cccccssssccccc"
+         d="M 308.02082,201.07469 C 308.02082,201.07469 290.25388,194.17877 290.25388,194.17877 C 289.03643,193.70622 287.49217,193.72395 286.81047,194.22762 C 286.81047,194.22762 274.92805,203.00701 274.92805,203.00701 C 274.21432,203.53434 274.06866,203.98769 275.45256,204.3729 L 282.20799,207.13372 C 283.30081,207.58033 283.29864,207.60345 284.24763,206.91193 L 285.51228,205.99038 C 285.78882,205.78886 286.61757,205.71073 287.50613,206.09359 L 296.65875,210.03701 C 297.66812,210.31796 298.54496,210.40349 299.11475,209.9558 C 299.11475,209.9558 308.63657,202.4745 308.63657,202.4745 C 309.1849,202.04368 308.91548,201.42193 308.02082,201.07469 C 308.02082,201.07469 308.02082,201.07469 308.02082,201.07469"
+         style="fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3381"
+         d="M 263.00018,250.94679 C 263.00018,250.94679 254.5221,247.0388 254.5221,247.0388 C 254.08593,246.83775 253.46532,246.88652 253.12956,247.148 C 253.12956,247.148 246.6785,252.17186 246.6785,252.17186 C 246.33694,252.43788 246.40946,252.82015 246.84217,253.0292 C 246.84217,253.0292 255.25442,257.09309 255.25442,257.09309 C 255.70512,257.31082 256.34668,257.26542 256.6918,256.99111 C 256.6918,256.99111 263.20867,251.81133 263.20867,251.81133 C 263.54778,251.54179 263.45431,251.15613 263.00018,250.94679 C 263.00018,250.94679 263.00018,250.94679 263.00018,250.94679"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3385"
+         d="M 224.63535,250.5847 C 224.63535,250.5847 216.40989,246.52591 216.40989,246.52591 C 215.79702,246.2235 214.86783,246.29035 214.3264,246.67645 C 214.3264,246.67645 206.94803,251.93833 206.94803,251.93833 C 206.39716,252.33118 206.45553,252.89148 207.07898,253.19391 C 207.07898,253.19391 215.44611,257.2528 215.44611,257.2528 C 216.0613,257.55121 216.99156,257.47468 217.53182,257.08184 C 217.53182,257.08184 224.76854,251.81989 224.76854,251.81989 C 225.29959,251.43374 225.24017,250.88315 224.63535,250.5847 C 224.63535,250.5847 224.63535,250.5847 224.63535,250.5847"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3387"
+         d="M 220.79919,257.76358 C 220.79919,257.76358 229.7534,262.16116 229.7534,262.16116 C 230.47771,262.51688 230.65884,263.11821 230.15739,263.5097 C 230.15739,263.5097 223.79138,268.4796 223.79138,268.4796 C 223.27442,268.8832 222.26833,268.91225 221.53782,268.54453 C 221.53782,268.54453 212.50868,263.9994 212.50868,263.9994 C 211.79484,263.64005 211.64287,263.03537 212.16581,262.64384 C 212.16581,262.64384 218.60675,257.8215 218.60675,257.8215 C 219.11421,257.44156 220.09111,257.41585 220.79919,257.76358 C 220.79919,257.76358 220.79919,257.76358 220.79919,257.76358"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3389"
+         d="M 209.90305,237.93879 C 209.90305,237.93879 217.69144,241.5189 217.69144,241.5189 C 218.2857,241.79206 218.40792,242.28161 217.95894,242.61539 C 217.95894,242.61539 211.48141,247.43084 211.48141,247.43084 C 211.00555,247.78462 210.15328,247.81772 209.57718,247.50651 C 209.57718,247.50651 202.04394,243.43694 202.04394,243.43694 C 201.54218,243.16588 201.50989,242.69331 201.96601,242.37623 C 201.96601,242.37623 208.18868,238.05054 208.18868,238.05054 C 208.62095,237.75004 209.38314,237.69979 209.90305,237.93879 C 209.90305,237.93879 209.90305,237.93879 209.90305,237.93879"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3391"
+         d="M 300.49388,221.41753 C 300.49388,221.41753 318.37188,206.97394 318.37188,206.97394 C 319.2072,206.29909 319.56463,205.63506 319.17704,205.48324 C 319.17704,205.48324 311.44233,202.45361 311.44233,202.45361 C 311.07717,202.31058 310.12278,202.71434 309.29892,203.3606 C 309.29892,203.3606 291.6901,217.17374 291.6901,217.17374 C 290.79969,217.87223 290.3631,218.57498 290.71507,218.74754 C 290.71507,218.74754 298.1804,222.40777 298.1804,222.40777 C 298.55498,222.59143 299.58864,222.1489 300.49388,221.41753 C 300.49388,221.41753 300.49388,221.41753 300.49388,221.41753"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 258.97104,224.63939 C 258.97104,224.63939 251.83463,221.23656 251.83463,221.23656 C 251.14159,220.90611 250.14534,220.95426 249.59505,221.34461 C 249.59505,221.34461 243.94919,225.34942 243.94919,225.34942 C 243.36824,225.76151 243.45494,226.38105 244.14978,226.73848 C 244.14978,226.73848 251.31231,230.42301 251.31231,230.42301 C 252.05284,230.80395 253.12041,230.75512 253.69927,230.31357 C 253.69927,230.31357 259.31886,226.02706 259.31886,226.02706 C 259.86601,225.60971 259.70806,224.99085 258.97104,224.63939 C 258.97104,224.63939 258.97104,224.63939 258.97104,224.63939"
+         id="path13125"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 245.03115,264.95865 C 245.03115,264.95865 236.89775,261.02915 236.89775,261.02915 C 236.29828,260.73954 235.41666,260.81767 234.92021,261.2046 C 234.92021,261.2046 228.15537,266.47697 228.15537,266.47697 C 227.65036,266.87057 227.72859,267.42473 228.33161,267.71924 C 228.33161,267.71924 236.5135,271.71529 236.5135,271.71529 C 237.12168,272.01232 238.01565,271.93037 238.51705,271.53179 C 238.51705,271.53179 245.23339,266.19289 245.23339,266.19289 C 245.7263,265.80109 245.63571,265.25074 245.03115,264.95865 C 245.03115,264.95865 245.03115,264.95865 245.03115,264.95865"
+         id="path13127"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 241.3425,254.88757 C 241.3425,254.88757 247.17841,250.38791 247.17841,250.38791 C 247.6407,250.03146 247.43669,249.46639 246.72219,249.12079 C 246.72219,249.12079 237.84809,244.82838 237.84809,244.82838 C 237.14306,244.48737 236.20078,244.49428 235.73414,244.84402 C 235.73414,244.84402 229.84385,249.25863 229.84385,249.25863 C 229.36826,249.61507 229.55538,250.18212 230.26467,250.52985 C 230.26467,250.52985 239.19316,254.90731 239.19316,254.90731 C 239.9121,255.25978 240.87126,255.2509 241.3425,254.88757 C 241.3425,254.88757 241.3425,254.88757 241.3425,254.88757"
+         id="path13129"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path13131"
+         d="M 250.09625,248.10772 C 250.09625,248.10772 256.04021,243.39795 256.04021,243.39795 C 256.61443,242.94295 256.40961,242.26587 255.58606,241.87803 C 255.58606,241.87803 247.4695,238.05567 247.4695,238.05567 C 246.66725,237.67787 245.54243,237.71942 244.94236,238.15046 C 244.94236,238.15046 238.73449,242.60981 238.73449,242.60981 C 238.09034,243.07251 238.23366,243.77042 239.06151,244.17275 C 239.06151,244.17275 247.44196,248.2456 247.44196,248.2456 C 248.29278,248.65909 249.47914,248.5967 250.09625,248.10772 C 250.09625,248.10772 250.09625,248.10772 250.09625,248.10772"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path13133"
+         d="M 270.78129,244.1004 C 270.78129,244.1004 263.16585,240.54998 263.16585,240.54998 C 262.59905,240.28573 261.81284,240.33065 261.40189,240.65211 C 261.40189,240.65211 255.67209,245.13428 255.67209,245.13428 C 255.23437,245.47666 255.35686,245.97255 255.94801,246.24454 C 255.94801,246.24454 263.88891,249.89826 263.88891,249.89826 C 264.4733,250.16714 265.27704,250.1004 265.6898,249.75019 C 265.6898,249.75019 271.09419,245.16472 271.09419,245.16472 C 271.48189,244.83578 271.34185,244.36173 270.78129,244.1004 C 270.78129,244.1004 270.78129,244.1004 270.78129,244.1004"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path13135"
+         d="M 279.36283,237.13131 C 279.36283,237.13131 271.95158,233.79368 271.95158,233.79368 C 271.36993,233.53174 270.52181,233.61628 270.04782,233.98311 C 270.04782,233.98311 264.05529,238.62092 264.05529,238.62092 C 263.57067,238.99597 263.64455,239.51827 264.22306,239.79213 C 264.22306,239.79213 271.59655,243.28243 271.59655,243.28243 C 272.19767,243.56699 273.0762,243.48424 273.56398,243.09674 C 273.56398,243.09674 279.59427,238.30625 279.59427,238.30625 C 280.07112,237.92743 279.96688,237.40335 279.36283,237.13131 C 279.36283,237.13131 279.36283,237.13131 279.36283,237.13131"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 259.13878,241.01984 C 259.13878,241.01984 264.94427,236.50262 264.94427,236.50262 C 265.5183,236.05597 265.27718,235.39329 264.41129,235.01866 C 264.41129,235.01866 256.13903,231.43971 256.13903,231.43971 C 255.34632,231.09675 254.26614,231.15807 253.71044,231.57558 C 253.71044,231.57558 248.09408,235.79517 248.09408,235.79517 C 247.52482,236.22286 247.6843,236.86458 248.45835,237.23578 C 248.45835,237.23578 256.54144,241.11225 256.54144,241.11225 C 257.38813,241.51828 258.54995,241.47801 259.13878,241.01984 C 259.13878,241.01984 259.13878,241.01984 259.13878,241.01984"
+         id="path14377"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 232.79455,261.43519 C 232.79455,261.43519 238.72958,256.85909 238.72958,256.85909 C 239.16465,256.52365 238.9726,255.99183 238.3001,255.66653 C 238.3001,255.66653 229.27525,251.30123 229.27525,251.30123 C 228.61176,250.98029 227.72493,250.98679 227.28579,251.31591 C 227.28579,251.31591 221.29544,255.80552 221.29544,255.80552 C 220.84778,256.14104 221.02387,256.67471 221.69138,257.00198 C 221.69138,257.00198 230.77154,261.45377 230.77154,261.45377 C 231.44822,261.78553 232.35098,261.77719 232.79455,261.43519 C 232.79455,261.43519 232.79455,261.43519 232.79455,261.43519"
+         id="path14379"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 254.17375,257.48652 C 254.17375,257.48652 246.32963,253.73115 246.32963,253.73115 C 245.70901,253.43402 244.79483,253.51721 244.27828,253.91779 C 244.27828,253.91779 237.75243,258.97869 237.75243,258.97869 C 237.22504,259.38768 237.29958,259.96347 237.92117,260.26957 C 237.92117,260.26957 245.77862,264.13893 245.77862,264.13893 C 246.41397,264.45181 247.35058,264.36687 247.87695,263.94859 C 247.87695,263.94859 254.38946,258.77374 254.38946,258.77374 C 254.90487,258.36419 254.80792,257.79015 254.17375,257.48652 C 254.17375,257.48652 254.17375,257.48652 254.17375,257.48652"
+         id="path14381"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path14383"
+         d="M 233.03678,243.79425 C 233.03678,243.79425 225.48646,240.13918 225.48646,240.13918 C 224.74342,239.77949 223.69299,239.82194 223.12841,240.23388 C 223.12841,240.23388 217.44731,244.37905 217.44731,244.37905 C 216.874,244.79736 217.00415,245.4333 217.742,245.80545 C 217.742,245.80545 225.24071,249.58767 225.24071,249.58767 C 226.00503,249.97318 227.0886,249.93684 227.66722,249.50562 C 227.66722,249.50562 233.39989,245.23329 233.39989,245.23329 C 233.96951,244.80878 233.80621,244.16674 233.03678,243.79425 C 233.03678,243.79425 233.03678,243.79425 233.03678,243.79425"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 282.03015,224.70872 C 282.03015,224.70872 287.9774,220.08364 287.9774,220.08364 C 288.56519,219.62653 289.56121,219.51344 290.21268,219.82947 C 290.21268,219.82947 296.76597,223.00851 296.76597,223.00851 C 297.42707,223.32921 297.49209,223.96249 296.90969,224.429 C 296.90969,224.429 291.01609,229.14982 291.01609,229.14982 C 290.41797,229.62893 289.39806,229.74916 288.73146,229.41875 C 288.73146,229.41875 282.12452,226.14398 282.12452,226.14398 C 281.4678,225.81845 281.42665,225.17805 282.03015,224.70872 C 282.03015,224.70872 282.03015,224.70872 282.03015,224.70872"
+         id="path14385"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path20314"
+         d="M 274.59176,213.04701 C 274.59176,213.04701 267.63301,209.8841 267.63301,209.8841 C 266.9305,209.56479 265.98277,209.58635 265.5079,209.93372 C 265.5079,209.93372 260.69581,213.4537 260.69581,213.4537 C 260.20677,213.81142 260.39277,214.35936 261.11308,214.68093 C 261.11308,214.68093 268.24694,217.86572 268.24694,217.86572 C 268.95551,218.18206 269.90543,218.14595 270.37662,217.78604 C 270.37662,217.78604 275.0136,214.24417 275.0136,214.24417 C 275.47126,213.89461 275.28303,213.36122 274.59176,213.04701 C 274.59176,213.04701 274.59176,213.04701 274.59176,213.04701"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 292.00448,215.55279 C 292.00448,215.55279 296.3664,212.1569 296.3664,212.1569 C 296.79902,211.82008 296.4902,211.27517 295.67813,210.9361 C 295.67813,210.9361 287.77738,207.63715 287.77738,207.63715 C 287.00644,207.31524 286.04433,207.31631 285.61635,207.63866 C 285.61635,207.63866 281.30257,210.88766 281.30257,210.88766 C 280.86653,211.21606 281.12901,211.75032 281.89503,212.08646 C 281.89503,212.08646 289.74764,215.53227 289.74764,215.53227 C 290.55499,215.88655 291.56345,215.89614 292.00448,215.55279 C 292.00448,215.55279 292.00448,215.55279 292.00448,215.55279"
+         id="path20316"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path20318"
+         d="M 284.72744,221.17105 C 284.72744,221.17105 289.45459,217.54272 289.45459,217.54272 C 289.926,217.18089 289.66619,216.61851 288.87179,216.28131 C 288.87179,216.28131 280.90373,212.899 280.90373,212.899 C 280.10221,212.55875 279.07175,212.57593 278.59356,212.93801 C 278.59356,212.93801 273.79834,216.56885 273.79834,216.56885 C 273.31611,216.93397 273.57888,217.50206 274.38726,217.84205 C 274.38726,217.84205 282.42339,221.22188 282.42339,221.22188 C 283.22457,221.55883 284.25207,221.53592 284.72744,221.17105 C 284.72744,221.17105 284.72744,221.17105 284.72744,221.17105"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 280.70619,208.18882 C 280.70619,208.18882 273.65334,205.39325 273.65334,205.39325 C 272.99267,205.13139 272.16292,205.14273 271.78475,205.41678 C 271.78475,205.41678 267.9355,208.20633 267.9355,208.20633 C 267.54254,208.4911 267.73876,208.95315 268.38364,209.24454 C 268.38364,209.24454 275.27945,212.36039 275.27945,212.36039 C 276.01926,212.69466 276.95281,212.71002 277.36247,212.39198 C 277.36247,212.39198 281.36853,209.28183 281.36853,209.28183 C 281.76145,208.97678 281.46158,208.48822 280.70619,208.18882 C 280.70619,208.18882 280.70619,208.18882 280.70619,208.18882"
+         id="path20320"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path20322"
+         d="M 267.13462,218.61017 C 267.13462,218.61017 260.04553,215.39646 260.04553,215.39646 C 259.35688,215.08427 258.39121,215.12387 257.87608,215.4844 C 257.87608,215.4844 252.67496,219.12445 252.67496,219.12445 C 252.14829,219.49305 252.26711,220.05491 252.94569,220.38521 C 252.94569,220.38521 259.93387,223.78674 259.93387,223.78674 C 260.65566,224.13807 261.67299,224.1096 262.21011,223.72182 C 262.21011,223.72182 267.5121,219.8939 267.5121,219.8939 C 268.03699,219.51494 267.86652,218.94196 267.13462,218.61017 C 267.13462,218.61017 267.13462,218.61017 267.13462,218.61017"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 287.7354,230.25946 C 287.7354,230.25946 281.08316,227.00351 281.08316,227.00351 C 280.40678,226.67245 279.40568,226.75692 278.83891,227.19481 C 278.83891,227.19481 273.09436,231.63291 273.09436,231.63291 C 272.51043,232.08403 272.60418,232.71336 273.30418,233.04198 C 273.30418,233.04198 280.18673,236.27314 280.18673,236.27314 C 280.86539,236.59174 281.86259,236.48569 282.42295,236.03722 C 282.42295,236.03722 287.93718,231.62391 287.93718,231.62391 C 288.48138,231.18834 288.39153,230.58062 287.7354,230.25946 C 287.7354,230.25946 287.7354,230.25946 287.7354,230.25946"
+         id="path20324"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path20326"
+         d="M 267.94729,234.14015 C 267.94729,234.14015 273.40476,229.94747 273.40476,229.94747 C 273.94662,229.5312 273.71505,228.90956 272.88832,228.55398 C 272.88832,228.55398 264.75456,225.0556 264.75456,225.0556 C 263.95198,224.71041 262.87291,224.75882 262.33246,225.16387 C 262.33246,225.16387 256.88991,229.24286 256.88991,229.24286 C 256.34026,229.6548 256.54039,230.27309 257.34135,230.62945 C 257.34135,230.62945 265.46021,234.24152 265.46021,234.24152 C 266.28553,234.60871 267.39604,234.56364 267.94729,234.14015 C 267.94729,234.14015 267.94729,234.14015 267.94729,234.14015"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path21282"
+         d="M 276.32923,227.66222 C 276.32923,227.66222 281.90001,223.35623 281.90001,223.35623 C 282.4517,222.9298 282.23314,222.31013 281.41303,221.96694 C 281.41303,221.96694 273.34754,218.59182 273.34754,218.59182 C 272.55203,218.25892 271.46937,218.32422 270.91711,218.73813 C 270.91711,218.73813 265.34181,222.91665 265.34181,222.91665 C 264.77733,223.33971 264.96112,223.95828 265.75691,224.30368 C 265.75691,224.30368 273.82693,227.80624 273.82693,227.80624 C 274.64767,228.16246 275.7651,228.09826 276.32923,227.66222 C 276.32923,227.66222 276.32923,227.66222 276.32923,227.66222"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <g
+         transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+         id="g5349">
+        <path
+           id="path3281"
+           d="M 200.53013,313.0503 C 200.53013,313.0503 193.03458,319.37147 193.03458,319.37147 C 192.65485,319.6917 192.0145,319.75862 191.59935,319.52112 C 191.59935,319.52112 183.2867,314.76558 183.2867,314.76558 C 182.87292,314.52886 182.85139,314.08409 183.23786,313.76871 C 183.23786,313.76871 190.86675,307.54312 190.86675,307.54312 C 191.24112,307.23761 191.86986,307.1787 192.27701,307.41066 C 192.27701,307.41066 200.45632,312.07061 200.45632,312.07061 C 200.86479,312.30333 200.89796,312.7401 200.53013,313.0503 C 200.53013,313.0503 200.53013,313.0503 200.53013,313.0503"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3283"
+           d="M 172.41739,306.03622 C 172.41739,306.03622 188.0277,294.08595 188.0277,294.08595 C 189.11795,293.25133 190.41897,292.82031 190.95268,293.11384 C 190.95268,293.11384 198.19106,297.09485 198.19106,297.09485 C 198.72888,297.39065 198.31537,298.31501 197.2562,299.17331 C 197.2562,299.17331 182.08669,311.46584 182.08669,311.46584 C 180.89616,312.43058 179.47079,312.95427 178.90005,312.63295 C 178.90005,312.63295 171.22074,308.30947 171.22074,308.30947 C 170.65468,307.99077 171.1926,306.97384 172.41739,306.03622 C 172.41739,306.03622 172.41739,306.03622 172.41739,306.03622"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3285"
+           d="M 183.71685,293.09242 C 183.71685,293.09242 175.21974,288.61823 175.21974,288.61823 C 174.62141,288.30317 173.39049,288.60803 172.45631,289.30319 C 172.45631,289.30319 159.05702,299.27405 159.05702,299.27405 C 158.07838,300.0023 157.77029,300.8596 158.36997,301.19473 C 158.36997,301.19473 166.88944,305.95582 166.88944,305.95582 C 167.50572,306.30023 168.79179,305.97286 169.76917,305.22339 C 169.76917,305.22339 183.14605,294.96565 183.14605,294.96565 C 184.07833,294.25076 184.33129,293.41596 183.71685,293.09242 C 183.71685,293.09242 183.71685,293.09242 183.71685,293.09242"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3287"
+           d="M 166.83475,289.12754 C 166.83475,289.12754 159.13844,284.98242 159.13844,284.98242 C 158.69381,284.74294 157.84841,284.90019 157.24056,285.33581 C 157.24056,285.33581 146.5932,292.96648 146.5932,292.96648 C 145.95049,293.4271 145.7912,294.00445 146.23859,294.2601 C 146.23859,294.2601 153.9866,298.68753 153.9866,298.68753 C 154.44898,298.95174 155.33851,298.77896 155.97843,298.30106 C 155.97843,298.30106 166.57405,290.38811 166.57405,290.38811 C 167.17862,289.93661 167.2938,289.37478 166.83475,289.12754 C 166.83475,289.12754 166.83475,289.12754 166.83475,289.12754"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3289"
+           d="M 151.36095,285.65566 C 151.36095,285.65566 143.84889,281.51108 143.84889,281.51108 C 143.47842,281.30668 142.86541,281.36856 142.47405,281.64966 C 142.47405,281.64966 134.61675,287.29335 134.61675,287.29335 C 134.22459,287.57503 134.20331,287.96918 134.56941,288.17726 C 134.56941,288.17726 141.993,292.39653 141.993,292.39653 C 142.36721,292.60921 142.98804,292.55081 143.38462,292.2654 C 143.38462,292.2654 151.33041,286.54701 151.33041,286.54701 C 151.72616,286.2622 151.73961,285.86457 151.36095,285.65566 C 151.36095,285.65566 151.36095,285.65566 151.36095,285.65566"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3291"
+           d="M 210.70247,305.77557 C 210.70247,305.77557 204.93036,310.57929 204.93036,310.57929 C 204.34435,311.06699 203.27394,311.11934 202.52773,310.69333 C 202.52773,310.69333 194.82772,306.29731 194.82772,306.29731 C 194.03081,305.84234 193.88921,305.07052 194.51315,304.57044 C 194.51315,304.57044 200.65538,299.64763 200.65538,299.64763 C 201.26179,299.16161 202.36255,299.14928 203.12074,299.61631 C 203.12074,299.61631 210.45071,304.13144 210.45071,304.13144 C 211.16146,304.56926 211.27265,305.30104 210.70247,305.77557 C 210.70247,305.77557 210.70247,305.77557 210.70247,305.77557"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3293"
+           d="M 220.36679,297.64972 C 220.36679,297.64972 214.19821,302.83693 214.19821,302.83693 C 213.8071,303.16581 213.0629,303.16632 212.53329,302.83934 C 212.53329,302.83934 204.47426,297.86377 204.47426,297.86377 C 203.99682,297.56901 203.91878,297.08754 204.2961,296.78318 C 204.2961,296.78318 210.25274,291.97831 210.25274,291.97831 C 210.61877,291.68306 211.30575,291.66386 211.7962,291.93623 C 211.7962,291.93623 220.06723,296.5296 220.06723,296.5296 C 220.61026,296.83118 220.74549,297.33126 220.36679,297.64972 C 220.36679,297.64972 220.36679,297.64972 220.36679,297.64972"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3295"
+           d="M 229.27683,290.15464 C 229.27683,290.15464 223.76946,294.79471 223.76946,294.79471 C 223.28503,295.20286 222.36392,295.23289 221.70465,294.86121 C 221.70465,294.86121 214.09874,290.57325 214.09874,290.57325 C 213.43481,290.19895 213.29956,289.57104 213.79504,289.16623 C 213.79504,289.16623 219.4277,284.56425 219.4277,284.56425 C 219.91221,284.1684 220.82757,284.14902 221.48054,284.51999 C 221.48054,284.51999 228.96116,288.76985 228.96116,288.76985 C 229.60961,289.13825 229.75059,289.75549 229.27683,290.15464 C 229.27683,290.15464 229.27683,290.15464 229.27683,290.15464"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3297"
+           d="M 207.6366,289.76045 C 207.6366,289.76045 200.20453,285.97175 200.20453,285.97175 C 199.75388,285.74202 199.11315,285.78115 198.76662,286.05878 C 198.76662,286.05878 193.21867,290.50362 193.21867,290.50362 C 192.87292,290.78062 192.94685,291.19499 193.38567,291.43343 C 193.38567,291.43343 200.62246,295.36556 200.62246,295.36556 C 201.08703,295.61798 201.75028,295.59166 202.10819,295.30573 C 202.10819,295.30573 207.85144,290.71744 207.85144,290.71744 C 208.21018,290.43084 208.11372,290.00367 207.6366,289.76045 C 207.6366,289.76045 207.6366,289.76045 207.6366,289.76045"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3299"
+           d="M 193.30538,285.7839 C 193.30538,285.7839 185.22052,281.47582 185.22052,281.47582 C 184.58295,281.13609 183.70561,281.13742 183.25124,281.47882 C 183.25124,281.47882 177.48561,285.81085 177.48561,285.81085 C 177.01761,286.16248 177.15516,286.7287 177.79623,287.08041 C 177.79623,287.08041 185.92745,291.54147 185.92745,291.54147 C 186.5872,291.90343 187.49396,291.90165 187.95831,291.53752 C 187.95831,291.53752 193.67756,287.05251 193.67756,287.05251 C 194.12816,286.69915 193.9612,286.13336 193.30538,285.7839 C 193.30538,285.7839 193.30538,285.7839 193.30538,285.7839"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3301"
+           d="M 153.65825,274.40909 C 153.65825,274.40909 161.75797,278.66326 161.75797,278.66326 C 162.02482,278.80341 162.05943,279.04844 161.83501,279.21257 C 161.83501,279.21257 154.75107,284.39353 154.75107,284.39353 C 154.52039,284.56224 154.12135,284.5797 153.85694,284.43276 C 153.85694,284.43276 145.83383,279.97431 145.83383,279.97431 C 145.58269,279.83475 145.56457,279.59204 145.79266,279.43005 C 145.79266,279.43005 152.79999,274.45338 152.79999,274.45338 C 153.02206,274.29566 153.40462,274.27588 153.65825,274.40909 C 153.65825,274.40909 153.65825,274.40909 153.65825,274.40909"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3303"
+           d="M 176.68036,282.15173 C 176.68036,282.15173 169.08774,278.13438 169.08774,278.13438 C 168.53136,277.83999 167.74128,277.85841 167.31522,278.1756 C 167.31522,278.1756 161.51878,282.49085 161.51878,282.49085 C 161.08675,282.81248 161.18562,283.31335 161.7415,283.61396 C 161.7415,283.61396 169.32801,287.71649 169.32801,287.71649 C 169.89523,288.02323 170.70097,288.00619 171.1335,287.67818 C 171.1335,287.67818 176.93604,283.27774 176.93604,283.27774 C 177.36252,282.95432 177.24799,282.45207 176.68036,282.15173 C 176.68036,282.15173 176.68036,282.15173 176.68036,282.15173"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3305"
+           d="M 186.05575,275.08382 C 186.05575,275.08382 178.66837,271.26624 178.66837,271.26624 C 178.14275,270.99462 177.38016,271.01579 176.95654,271.31482 C 176.95654,271.31482 170.80827,275.65494 170.80827,275.65494 C 170.35386,275.97571 170.42595,276.46372 170.9721,276.74789 C 170.9721,276.74789 178.64907,280.74215 178.64907,280.74215 C 179.19855,281.02804 179.99123,280.98835 180.42429,280.65448 C 180.42429,280.65448 186.28302,276.1377 186.28302,276.1377 C 186.68663,275.82653 186.58444,275.35703 186.05575,275.08382 C 186.05575,275.08382 186.05575,275.08382 186.05575,275.08382"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3307"
+           d="M 163.51597,267.87954 C 163.51597,267.87954 171.1646,271.91896 171.1646,271.91896 C 171.53604,272.11513 171.58651,272.4514 171.2774,272.67324 C 171.2774,272.67324 164.82134,277.30652 164.82134,277.30652 C 164.50235,277.53544 163.94045,277.55958 163.56185,277.36032 C 163.56185,277.36032 155.76574,273.25703 155.76574,273.25703 C 155.38595,273.05714 155.34321,272.7142 155.66949,272.48842 C 155.66949,272.48842 162.27303,267.91901 162.27303,267.91901 C 162.5892,267.70024 163.14337,267.68276 163.51597,267.87954 C 163.51597,267.87954 163.51597,267.87954 163.51597,267.87954"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3309"
+           d="M 173.08132,261.26965 C 173.08132,261.26965 180.42033,265.06231 180.42033,265.06231 C 180.84108,265.27975 180.901,265.65731 180.55405,265.90902 C 180.55405,265.90902 174.39819,270.3752 174.39819,270.3752 C 174.04312,270.63281 173.41533,270.66166 172.99122,270.43971 C 172.99122,270.43971 165.59423,266.56865 165.59423,266.56865 C 165.17482,266.34916 165.12621,265.96859 165.4846,265.71552 C 165.4846,265.71552 171.69844,261.32774 171.69844,261.32774 C 172.04868,261.08043 172.66517,261.05459 173.08132,261.26965 C 173.08132,261.26965 173.08132,261.26965 173.08132,261.26965"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3311"
+           d="M 183.15808,254.05968 C 183.15808,254.05968 190.5411,257.89809 190.5411,257.89809 C 190.90119,258.0853 190.9368,258.42917 190.61983,258.66983 C 190.61983,258.66983 183.84308,263.81508 183.84308,263.81508 C 183.50031,264.07534 182.92132,264.12739 182.54615,263.93097 C 182.54615,263.93097 174.85492,259.90428 174.85492,259.90428 C 174.48192,259.709 174.47113,259.34932 174.82945,259.09862 C 174.82945,259.09862 181.91436,254.14161 181.91436,254.14161 C 182.24579,253.90973 182.79998,253.8735 183.15808,254.05968 C 183.15808,254.05968 183.15808,254.05968 183.15808,254.05968"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3313"
+           d="M 195.50846,268.00414 C 195.50846,268.00414 187.92237,264.08387 187.92237,264.08387 C 187.38263,263.80495 186.5995,263.82667 186.16449,264.13375 C 186.16449,264.13375 179.85083,268.59062 179.85083,268.59062 C 179.38419,268.92002 179.45825,269.42118 180.01907,269.71297 C 180.01907,269.71297 187.90254,273.81468 187.90254,273.81468 C 188.4668,274.10826 189.28081,274.0675 189.72552,273.72465 C 189.72552,273.72465 195.74185,269.08637 195.74185,269.08637 C 196.15632,268.76683 196.05137,268.2847 195.50846,268.00414 C 195.50846,268.00414 195.50846,268.00414 195.50846,268.00414"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3315"
+           d="M 202.55006,278.55525 C 202.55006,278.55525 194.52053,274.43157 194.52053,274.43157 C 193.89847,274.1121 193.03474,274.13023 192.58051,274.47152 C 192.58051,274.47152 186.82178,278.79836 186.82178,278.79836 C 186.35477,279.14926 186.47106,279.70327 187.086,280.04131 C 187.086,280.04131 195.02775,284.40711 195.02775,284.40711 C 195.68386,284.76779 196.59706,284.76041 197.07144,284.38968 C 197.07144,284.38968 202.9179,279.82067 202.9179,279.82067 C 203.3788,279.46048 203.21306,278.89574 202.55006,278.55525 C 202.55006,278.55525 202.55006,278.55525 202.55006,278.55525"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3317"
+           d="M 217.28268,282.30723 C 217.28268,282.30723 209.76798,278.51506 209.76798,278.51506 C 209.31861,278.28829 208.66038,278.33252 208.29049,278.6148 C 208.29049,278.6148 201.99168,283.42172 201.99168,283.42172 C 201.5992,283.72125 201.65194,284.15497 202.11173,284.39356 C 202.11173,284.39356 209.80365,288.38505 209.80365,288.38505 C 210.27451,288.62939 210.96221,288.57431 211.3438,288.26224 C 211.3438,288.26224 217.46536,283.25603 217.46536,283.25603 C 217.82471,282.96216 217.7425,282.53927 217.28268,282.30723 C 217.28268,282.30723 217.28268,282.30723 217.28268,282.30723"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5259"
+           sodipodi:nodetypes="cccccccccc"
+           d="M 223.6315,280.9685 C 223.6315,280.9685 271.20382,242.80623 271.20382,242.80623 C 271.77183,242.35446 272.94396,242.34509 273.8347,242.78481 C 273.8347,242.78481 280.16259,245.8893 280.16259,245.8893 C 281.06675,246.33566 281.34594,247.06729 280.78574,247.53016 C 280.78574,247.53016 233.93323,286.78539 233.93323,286.78539 C 233.23622,287.36129 231.89705,287.39044 230.93425,286.85016 C 230.93425,286.85016 224.06489,282.94036 224.06489,282.94036 C 223.11807,282.40904 222.92637,281.52934 223.6315,280.9685 C 223.6315,280.9685 223.6315,280.9685 223.6315,280.9685"
+           style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5261"
+           d="M 227.03565,274.61509 C 227.03565,274.61509 219.57159,270.89543 219.57159,270.89543 C 219.13842,270.67957 218.48938,270.73732 218.1151,271.02521 C 218.1151,271.02521 211.61304,276.02653 211.61304,276.02653 C 211.22381,276.32593 211.26193,276.7481 211.69984,276.97279 C 211.69984,276.97279 219.24737,280.84554 219.24737,280.84554 C 219.69565,281.07556 220.36702,281.012 220.75135,280.70336 C 220.75135,280.70336 227.16991,275.54895 227.16991,275.54895 C 227.53929,275.25232 227.47886,274.83596 227.03565,274.61509 C 227.03565,274.61509 227.03565,274.61509 227.03565,274.61509"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5263"
+           d="M 192.51964,247.35677 C 192.51964,247.35677 199.78325,251.31635 199.78325,251.31635 C 200.08373,251.48015 200.10518,251.76671 199.83193,251.95915 C 199.83193,251.95915 193.39598,256.49168 193.39598,256.49168 C 193.12338,256.68366 192.65687,256.71094 192.34938,256.55257 C 192.34938,256.55257 184.91676,252.72442 184.91676,252.72442 C 184.59365,252.55801 184.55824,252.26477 184.83799,252.06723 C 184.83799,252.06723 191.44296,247.40326 191.44296,247.40326 C 191.7234,247.20524 192.20387,247.18464 192.51964,247.35677 C 192.51964,247.35677 192.51964,247.35677 192.51964,247.35677"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5265"
+           d="M 204.95758,260.67876 C 204.95758,260.67876 197.45088,256.83357 197.45088,256.83357 C 196.91888,256.56107 196.14744,256.58436 195.71989,256.88617 C 195.71989,256.88617 189.62606,261.18786 189.62606,261.18786 C 189.18381,261.50005 189.26067,261.97752 189.79977,262.25801 C 189.79977,262.25801 197.4078,266.21641 197.4078,266.21641 C 197.95451,266.50085 198.7459,266.47153 199.18087,266.15108 C 199.18087,266.15108 205.17336,261.73618 205.17336,261.73618 C 205.59375,261.42646 205.49693,260.95503 204.95758,260.67876 C 204.95758,260.67876 204.95758,260.67876 204.95758,260.67876"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5267"
+           d="M 203.42735,267.20908 C 203.42735,267.20908 212.2333,271.70369 212.2333,271.70369 C 212.6248,271.90351 212.71338,272.23708 212.43163,272.45164 C 212.43163,272.45164 206.02839,277.32796 206.02839,277.32796 C 205.74282,277.54543 205.19609,277.55776 204.8028,277.35556 C 204.8028,277.35556 195.957,272.80759 195.957,272.80759 C 195.56658,272.60686 195.48379,272.27205 195.77113,272.05697 C 195.77113,272.05697 202.21422,267.23402 202.21422,267.23402 C 202.49773,267.02179 203.03868,267.0107 203.42735,267.20908 C 203.42735,267.20908 203.42735,267.20908 203.42735,267.20908"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5269"
+           d="M 212.72962,260.6903 C 212.72962,260.6903 220.81856,264.71792 220.81856,264.71792 C 221.40722,265.01103 221.56351,265.49233 221.1669,265.79697 C 221.1669,265.79697 215.52012,270.13427 215.52012,270.13427 C 215.11075,270.44871 214.30434,270.45549 213.7142,270.1496 C 213.7142,270.1496 205.60787,265.94776 205.60787,265.94776 C 205.04085,265.65385 204.91653,265.17445 205.32701,264.87276 C 205.32701,264.87276 210.99121,260.70967 210.99121,260.70967 C 211.38919,260.41716 212.16362,260.40847 212.72962,260.6903 C 212.72962,260.6903 212.72962,260.6903 212.72962,260.6903"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5271"
+           d="M 235.38208,267.94652 C 235.38208,267.94652 228.36195,264.54302 228.36195,264.54302 C 227.7581,264.25027 226.91755,264.28848 226.47592,264.62855 C 226.47592,264.62855 221.36693,268.56268 221.36693,268.56268 C 220.91972,268.90705 221.04356,269.42537 221.64599,269.72502 C 221.64599,269.72502 228.65018,273.2089 228.65018,273.2089 C 229.26637,273.51538 230.12476,273.47935 230.5734,273.12791 C 230.5734,273.12791 235.69833,269.1134 235.69833,269.1134 C 236.14129,268.76642 235.99961,268.24591 235.38208,267.94652 C 235.38208,267.94652 235.38208,267.94652 235.38208,267.94652"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5273"
+           d="M 202.3439,240.55011 C 202.3439,240.55011 209.40685,244.3565 209.40685,244.3565 C 209.77966,244.55741 209.79685,244.91929 209.44513,245.16789 C 209.44513,245.16789 202.71836,249.92236 202.71836,249.92236 C 202.36384,250.17294 201.77847,250.21096 201.40617,250.00761 C 201.40617,250.00761 194.3529,246.15501 194.3529,246.15501 C 193.98466,245.95386 193.97344,245.59217 194.32745,245.34403 C 194.32745,245.34403 201.04453,240.63577 201.04453,240.63577 C 201.39575,240.38958 201.97514,240.35138 202.3439,240.55011 C 202.3439,240.55011 202.3439,240.55011 202.3439,240.55011"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5275"
+           d="M 214.95641,253.88523 C 214.95641,253.88523 207.20865,250.11064 207.20865,250.11064 C 206.78058,249.90209 206.15223,249.93501 205.79811,250.18412 C 205.79811,250.18412 199.5121,254.60606 199.5121,254.60606 C 199.14935,254.86124 199.19499,255.24389 199.61614,255.46439 C 199.61614,255.46439 207.24189,259.45703 207.24189,259.45703 C 207.69079,259.69206 208.35153,259.66596 208.72148,259.39808 C 208.72148,259.39808 215.12948,254.75806 215.12948,254.75806 C 215.49031,254.49678 215.41229,254.10733 214.95641,253.88523 C 214.95641,253.88523 214.95641,253.88523 214.95641,253.88523"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5277"
+           d="M 221.9033,253.92205 C 221.9033,253.92205 229.74607,257.77376 229.74607,257.77376 C 230.38047,258.08533 230.53912,258.61203 230.09991,258.95492 C 230.09991,258.95492 224.52409,263.30793 224.52409,263.30793 C 224.07129,263.66142 223.1901,263.68688 222.55025,263.36479 C 222.55025,263.36479 214.64187,259.38384 214.64187,259.38384 C 214.01663,259.0691 213.88352,258.53947 214.34155,258.19654 C 214.34155,258.19654 219.983,253.97277 219.983,253.97277 C 220.42747,253.64 221.28312,253.61747 221.9033,253.92205 C 221.9033,253.92205 221.9033,253.92205 221.9033,253.92205"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 244.75785,260.58731 C 244.75785,260.58731 237.5872,257.12295 237.5872,257.12295 C 237.05869,256.86761 236.28144,256.9365 235.84374,257.27763 C 235.84374,257.27763 229.87966,261.92591 229.87966,261.92591 C 229.43444,262.2729 229.5034,262.76148 230.03504,263.02113 C 230.03504,263.02113 237.24843,266.54415 237.24843,266.54415 C 237.78463,266.80603 238.57278,266.73378 239.01483,266.38238 C 239.01483,266.38238 244.93616,261.67544 244.93616,261.67544 C 245.3707,261.33002 245.29084,260.84482 244.75785,260.58731 C 244.75785,260.58731 244.75785,260.58731 244.75785,260.58731"
+           id="path5279"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 233.2943,256.416 C 233.2943,256.416 238.45564,252.43646 238.45564,252.43646 C 238.83398,252.14474 238.66697,251.68224 238.08213,251.39936 C 238.08213,251.39936 230.23378,247.60312 230.23378,247.60312 C 229.65678,247.32402 228.88557,247.32967 228.50367,247.6159 C 228.50367,247.6159 223.29424,251.52023 223.29424,251.52023 C 222.90493,251.812 223.05807,252.27611 223.63856,252.56071 C 223.63856,252.56071 231.535,256.43217 231.535,256.43217 C 232.12348,256.72068 232.90856,256.71342 233.2943,256.416 C 233.2943,256.416 233.2943,256.416 233.2943,256.416"
+           id="path5281"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5283"
+           d="M 224.84883,246.55597 C 224.84883,246.55597 217.52734,242.94323 217.52734,242.94323 C 216.98183,242.67405 216.15475,242.73355 215.67283,243.07723 C 215.67283,243.07723 209.10533,247.76083 209.10533,247.76083 C 208.61499,248.11051 208.66694,248.60923 209.22188,248.87843 C 209.22188,248.87843 216.66948,252.49124 216.66948,252.49124 C 217.21707,252.75687 218.04508,252.68874 218.52598,252.33907 C 218.52598,252.33907 224.96738,247.65541 224.96738,247.65541 C 225.44008,247.3117 225.38717,246.82161 224.84883,246.55597 C 224.84883,246.55597 224.84883,246.55597 224.84883,246.55597"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5285"
+           d="M 211.2176,234.55068 C 211.2176,234.55068 218.13924,237.73238 218.13924,237.73238 C 218.66737,237.97515 218.77598,238.41021 218.37697,238.70684 C 218.37697,238.70684 212.62031,242.9864 212.62031,242.9864 C 212.1974,243.3008 211.43997,243.33022 210.928,243.05365 C 210.928,243.05365 204.23311,239.43697 204.23311,239.43697 C 203.78717,239.19607 203.75848,238.77609 204.16385,238.4943 C 204.16385,238.4943 209.694,234.65 209.694,234.65 C 210.07817,234.38294 210.75554,234.33829 211.2176,234.55068 C 211.2176,234.55068 211.2176,234.55068 211.2176,234.55068"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5287"
+           d="M 233.15615,239.94299 C 233.15615,239.94299 226.44421,236.69379 226.44421,236.69379 C 225.78369,236.37403 224.84989,236.41177 224.348,236.77797 C 224.348,236.77797 219.29773,240.46286 219.29773,240.46286 C 218.78807,240.83472 218.90378,241.40005 219.55968,241.73088 C 219.55968,241.73088 226.22576,245.09312 226.22576,245.09312 C 226.90521,245.43582 227.86846,245.40352 228.38283,245.02018 C 228.38283,245.02018 233.47896,241.22224 233.47896,241.22224 C 233.98531,240.84488 233.84015,240.27412 233.15615,239.94299 C 233.15615,239.94299 233.15615,239.94299 233.15615,239.94299"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5289"
+           d="M 220.30432,227.87754 C 220.30432,227.87754 227.46486,231.13608 227.46486,231.13608 C 227.82573,231.3003 227.87475,231.60963 227.57378,231.82996 C 227.57378,231.82996 221.38229,236.3625 221.38229,236.3625 C 221.06565,236.59429 220.51407,236.64288 220.14663,236.4711 C 220.14663,236.4711 212.85793,233.06353 212.85793,233.06353 C 212.49851,232.8955 212.47011,232.57953 212.7932,232.35543 C 212.7932,232.35543 219.11286,227.97192 219.11286,227.97192 C 219.42016,227.75877 219.95112,227.71681 220.30432,227.87754 C 220.30432,227.87754 220.30432,227.87754 220.30432,227.87754"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 253.8328,253.35343 C 253.8328,253.35343 246.80705,249.98982 246.80705,249.98982 C 246.25118,249.7237 245.43236,249.7982 244.96971,250.15699 C 244.96971,250.15699 239.12467,254.6899 239.12467,254.6899 C 238.65231,255.05622 238.71907,255.57195 239.27581,255.84611 C 239.27581,255.84611 246.31353,259.3118 246.31353,259.3118 C 246.88258,259.59203 247.72148,259.51595 248.19294,259.14132 C 248.19294,259.14132 254.02601,254.50633 254.02601,254.50633 C 254.48765,254.13951 254.40082,253.62536 253.8328,253.35343 C 253.8328,253.35343 253.8328,253.35343 253.8328,253.35343"
+           id="path5291"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 241.69245,250.18836 C 241.69245,250.18836 246.86241,246.20217 246.86241,246.20217 C 247.27195,245.8864 247.09122,245.38581 246.45826,245.07965 C 246.45826,245.07965 238.59679,241.27706 238.59679,241.27706 C 237.97222,240.97496 237.13747,240.98108 236.72408,241.29091 C 236.72408,241.29091 231.50595,245.20176 231.50595,245.20176 C 231.08462,245.51753 231.25039,246.01986 231.87874,246.32792 C 231.87874,246.32792 239.78839,250.20585 239.78839,250.20585 C 240.42527,250.5181 241.27499,250.51024 241.69245,250.18836 C 241.69245,250.18836 241.69245,250.18836 241.69245,250.18836"
+           id="path5293"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5295"
+           d="M 262.29906,246.8676 C 262.29906,246.8676 254.88168,243.44853 254.88168,243.44853 C 254.50008,243.27263 253.95712,243.3153 253.66336,243.54406 C 253.66336,243.54406 248.0194,247.9394 248.0194,247.9394 C 247.72057,248.17212 247.78403,248.50657 248.1626,248.68946 C 248.1626,248.68946 255.52239,252.24492 255.52239,252.24492 C 255.9167,252.43541 256.47799,252.39569 256.77993,252.1557 C 256.77993,252.1557 262.48146,247.62397 262.48146,247.62397 C 262.77816,247.38815 262.69638,247.05074 262.29906,246.8676 C 262.29906,246.8676 262.29906,246.8676 262.29906,246.8676"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5297"
+           d="M 269.92313,240.18072 C 269.92313,240.18072 263.31143,237.09826 263.31143,237.09826 C 262.81933,236.86884 262.13676,236.90784 261.77997,237.18694 C 261.77997,237.18694 256.80537,241.07833 256.80537,241.07833 C 256.42536,241.3756 256.53169,241.80611 257.04493,242.04226 C 257.04493,242.04226 263.93919,245.21441 263.93919,245.21441 C 264.44655,245.44785 265.14436,245.3899 265.50271,245.08585 C 265.50271,245.08585 270.19479,241.10477 270.19479,241.10477 C 270.53139,240.81918 270.4098,240.40761 269.92313,240.18072 C 269.92313,240.18072 269.92313,240.18072 269.92313,240.18072"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5299"
+           d="M 284.15897,244.25926 C 284.15897,244.25926 290.08975,239.34554 290.08975,239.34554 C 290.3372,239.14053 290.25996,238.84705 289.91756,238.6876 C 289.91756,238.6876 282.05801,235.02758 282.05801,235.02758 C 281.73357,234.87649 281.27511,234.91294 281.02917,235.10924 C 281.02917,235.10924 275.13725,239.81168 275.13725,239.81168 C 274.88342,240.01427 274.9381,240.30589 275.26078,240.46561 C 275.26078,240.46561 283.08145,244.3369 283.08145,244.3369 C 283.42232,244.50564 283.90334,244.47105 284.15897,244.25926 C 284.15897,244.25926 284.15897,244.25926 284.15897,244.25926"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5301"
+           d="M 290.6347,228.41799 C 290.6347,228.41799 298.47677,231.7748 298.47677,231.7748 C 298.96904,231.98552 299.08998,232.38046 298.7459,232.65955 C 298.7459,232.65955 293.04479,237.28397 293.04479,237.28397 C 292.69915,237.56434 292.03399,237.60956 291.55538,237.38617 C 291.55538,237.38617 283.93166,233.82786 283.93166,233.82786 C 283.4873,233.62045 283.39853,233.2382 283.73091,232.97007 C 283.73091,232.97007 289.21363,228.54719 289.21363,228.54719 C 289.54456,228.28023 290.17756,228.22231 290.6347,228.41799 C 290.6347,228.41799 290.6347,228.41799 290.6347,228.41799"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5303"
+           d="M 301.86982,230.05516 C 301.86982,230.05516 306.72655,226.01075 306.72655,226.01075 C 307.20873,225.60921 307.07252,225.04406 306.41901,224.74173 C 306.41901,224.74173 299.71761,221.64153 299.71761,221.64153 C 299.02841,221.32268 298.06453,221.39571 297.55878,221.80728 C 297.55878,221.80728 292.46319,225.95398 292.46319,225.95398 C 291.94831,226.37298 292.11057,226.9602 292.82454,227.2686 C 292.82454,227.2686 299.76474,230.26652 299.76474,230.26652 C 300.44135,230.5588 301.37922,230.4637 301.86982,230.05516 C 301.86982,230.05516 301.86982,230.05516 301.86982,230.05516"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5305"
+           d="M 229.75901,221.51708 C 229.75901,221.51708 236.30365,224.3537 236.30365,224.3537 C 236.77779,224.55921 236.83816,224.95708 236.4374,225.2459 C 236.4374,225.2459 230.73685,229.35443 230.73685,229.35443 C 230.32395,229.65201 229.60775,229.71902 229.13267,229.50455 C 229.13267,229.50455 222.57677,226.54493 222.57677,226.54493 C 222.11612,226.33697 222.07952,225.93711 222.49312,225.64852 C 222.49312,225.64852 228.20494,221.66299 228.20494,221.66299 C 228.60659,221.38273 229.29903,221.31772 229.75901,221.51708 C 229.75901,221.51708 229.75901,221.51708 229.75901,221.51708"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5307"
+           d="M 241.4331,233.77444 C 241.4331,233.77444 234.86702,230.51453 234.86702,230.51453 C 234.2884,230.22725 233.46852,230.2512 233.03004,230.56861 C 233.03004,230.56861 228.00571,234.20578 228.00571,234.20578 C 227.5701,234.52112 227.69,235.00165 228.27321,235.28282 C 228.27321,235.28282 234.89177,238.47354 234.89177,238.47354 C 235.46008,238.74751 236.26364,238.71874 236.69472,238.40938 C 236.69472,238.40938 241.66657,234.84141 241.66657,234.84141 C 242.10045,234.53004 241.99687,234.05434 241.4331,233.77444 C 241.4331,233.77444 241.4331,233.77444 241.4331,233.77444"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5309"
+           d="M 250.21733,243.40142 C 250.21733,243.40142 255.39176,239.30139 255.39176,239.30139 C 255.89164,238.9053 255.71333,238.31587 254.9964,237.97824 C 254.9964,237.97824 247.93065,234.65074 247.93065,234.65074 C 247.23226,234.32185 246.25306,234.35802 245.73067,234.73327 C 245.73067,234.73327 240.32649,238.61528 240.32649,238.61528 C 239.76575,239.01809 239.8905,239.62564 240.61117,239.97588 C 240.61117,239.97588 247.90666,243.52145 247.90666,243.52145 C 248.64734,243.88142 249.68011,243.82709 250.21733,243.40142 C 250.21733,243.40142 250.21733,243.40142 250.21733,243.40142"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 259.03235,236.38432 C 259.03235,236.38432 264.00233,232.5172 264.00233,232.5172 C 264.49375,232.13483 264.28734,231.56751 263.54605,231.2468 C 263.54605,231.2468 256.4643,228.18292 256.4643,228.18292 C 255.78568,227.88932 254.86097,227.94181 254.38524,228.29923 C 254.38524,228.29923 249.57716,231.91156 249.57716,231.91156 C 249.08982,232.2777 249.22635,232.82706 249.889,233.14485 C 249.889,233.14485 256.80881,236.46342 256.80881,236.46342 C 257.53364,236.81102 258.52826,236.77654 259.03235,236.38432 C 259.03235,236.38432 259.03235,236.38432 259.03235,236.38432"
+           id="path5311"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5313"
+           d="M 249.92718,227.47874 C 249.92718,227.47874 243.57843,224.26363 243.57843,224.26363 C 242.94161,223.94113 242.03351,223.96693 241.54071,224.32288 C 241.54071,224.32288 236.50753,227.95826 236.50753,227.95826 C 235.99196,228.33065 236.10354,228.89802 236.75945,229.2289 C 236.75945,229.2289 243.29817,232.52743 243.29817,232.52743 C 243.95188,232.8572 244.8789,232.81481 245.37499,232.4339 C 245.37499,232.4339 250.21821,228.7151 250.21821,228.7151 C 250.69243,228.35098 250.56193,227.80019 249.92718,227.47874 C 249.92718,227.47874 249.92718,227.47874 249.92718,227.47874"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5315"
+           d="M 278.37779,233.42877 C 278.37779,233.42877 271.86628,230.49633 271.86628,230.49633 C 271.35525,230.26619 270.61009,230.34047 270.19363,230.66278 C 270.19363,230.66278 264.92861,234.73754 264.92861,234.73754 C 264.50283,235.06706 264.56772,235.52596 265.07602,235.76657 C 265.07602,235.76657 271.55435,238.83315 271.55435,238.83315 C 272.08251,239.08315 272.85436,239.01044 273.28293,238.66998 C 273.28293,238.66998 278.58112,234.46107 278.58112,234.46107 C 279.00009,234.12824 278.90852,233.66778 278.37779,233.42877 C 278.37779,233.42877 278.37779,233.42877 278.37779,233.42877"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 286.64967,226.73945 C 286.64967,226.73945 280.75469,223.85413 280.75469,223.85413 C 280.1553,223.56076 279.26816,223.63562 278.7659,224.02366 C 278.7659,224.02366 273.67528,227.95655 273.67528,227.95655 C 273.15783,228.35631 273.24091,228.914 273.86123,229.20522 C 273.86123,229.20522 279.9603,232.06855 279.9603,232.06855 C 280.56171,232.3509 281.4454,232.25691 281.94196,231.85949 C 281.94196,231.85949 286.82847,227.94856 286.82847,227.94856 C 287.31074,227.56258 287.23111,227.02404 286.64967,226.73945 C 286.64967,226.73945 286.64967,226.73945 286.64967,226.73945"
+           id="path5317"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5319"
+           d="M 267.6823,229.91307 C 267.6823,229.91307 272.46908,226.23566 272.46908,226.23566 C 272.94434,225.87054 272.74124,225.3253 272.01611,225.01342 C 272.01611,225.01342 264.88193,221.94496 264.88193,221.94496 C 264.17799,221.64219 263.23154,221.68466 262.7575,222.03994 C 262.7575,222.03994 257.98381,225.61763 257.98381,225.61763 C 257.50171,225.97894 257.67724,226.52125 258.37978,226.83381 C 258.37978,226.83381 265.50086,230.00199 265.50086,230.00199 C 266.22477,230.32406 267.1988,230.28451 267.6823,229.91307 C 267.6823,229.91307 267.6823,229.91307 267.6823,229.91307"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 258.51488,221.22986 C 258.51488,221.22986 252.19037,218.21416 252.19037,218.21416 C 251.57618,217.9213 250.69327,217.96397 250.20557,218.30991 C 250.20557,218.30991 245.20204,221.8591 245.20204,221.8591 C 244.68719,222.22431 244.76403,222.77336 245.37981,223.09013 C 245.37981,223.09013 251.72747,226.35547 251.72747,226.35547 C 252.38375,226.69307 253.32987,226.6498 253.84288,226.25849 C 253.84288,226.25849 258.82312,222.45965 258.82312,222.45965 C 259.30803,222.08977 259.16806,221.54132 258.51488,221.22986 C 258.51488,221.22986 258.51488,221.22986 258.51488,221.22986"
+           id="path5321"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5323"
+           d="M 239.22906,214.77058 C 239.22906,214.77058 245.75948,217.65839 245.75948,217.65839 C 246.22836,217.86574 246.27624,218.27214 245.86583,218.5699 C 245.86583,218.5699 240.04074,222.79608 240.04074,222.79608 C 239.61975,223.10153 238.89933,223.17644 238.42646,222.96374 C 238.42646,222.96374 231.84118,220.00174 231.84118,220.00174 C 231.37421,219.79169 231.34116,219.38081 231.76608,219.08076 C 231.76608,219.08076 237.64602,214.92874 237.64602,214.92874 C 238.06034,214.63619 238.76594,214.56577 239.22906,214.77058 C 239.22906,214.77058 239.22906,214.77058 239.22906,214.77058"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5325"
+           d="M 247.90724,208.64597 C 247.90724,208.64597 254.53759,211.50631 254.53759,211.50631 C 255.01636,211.71285 255.09618,212.10048 254.71562,212.37537 C 254.71562,212.37537 249.34818,216.25246 249.34818,216.25246 C 248.96271,216.5309 248.26761,216.58434 247.79061,216.37235 C 247.79061,216.37235 241.18538,213.43689 241.18538,213.43689 C 240.71963,213.22991 240.65428,212.84354 241.03791,212.57052 C 241.03791,212.57052 246.38024,208.76857 246.38024,208.76857 C 246.75905,208.49898 247.43968,208.44427 247.90724,208.64597 C 247.90724,208.64597 247.90724,208.64597 247.90724,208.64597"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5327"
+           d="M 266.53609,215.35847 C 266.53609,215.35847 260.2425,212.50539 260.2425,212.50539 C 259.63111,212.22823 258.77381,212.26339 258.31648,212.58345 C 258.31648,212.58345 253.69899,215.81504 253.69899,215.81504 C 253.23142,216.14228 253.3369,216.6411 253.93933,216.93434 C 253.93933,216.93434 260.14335,219.95417 260.14335,219.95417 C 260.78415,220.26608 261.68732,220.24081 262.16416,219.89654 C 262.16416,219.89654 266.87121,216.49816 266.87121,216.49816 C 267.3372,216.16172 267.18586,215.65303 266.53609,215.35847 C 266.53609,215.35847 266.53609,215.35847 266.53609,215.35847"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5329"
+           d="M 275.86123,223.49708 C 275.86123,223.49708 280.69597,219.76003 280.69597,219.76003 C 281.17477,219.38993 280.98509,218.85214 280.27334,218.55429 C 280.27334,218.55429 273.2735,215.62511 273.2735,215.62511 C 272.58309,215.3362 271.64347,215.39287 271.16418,215.75208 C 271.16418,215.75208 266.32552,219.37853 266.32552,219.37853 C 265.83563,219.74569 265.99513,220.28253 266.68578,220.58229 C 266.68578,220.58229 273.68954,223.62208 273.68954,223.62208 C 274.40184,223.93123 275.37164,223.87552 275.86123,223.49708 C 275.86123,223.49708 275.86123,223.49708 275.86123,223.49708"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 282.59083,221.04513 C 282.59083,221.04513 287.71903,217.05699 287.71903,217.05699 C 288.22587,216.66285 289.08472,216.56532 289.64647,216.83783 C 289.64647,216.83783 295.29725,219.57907 295.29725,219.57907 C 295.86731,219.85561 295.92338,220.40166 295.42118,220.80393 C 295.42118,220.80393 290.33924,224.87461 290.33924,224.87461 C 289.82349,225.28773 288.94404,225.3914 288.36924,225.1065 C 288.36924,225.1065 282.67221,222.28271 282.67221,222.28271 C 282.10592,222.00203 282.07045,221.44982 282.59083,221.04513 C 282.59083,221.04513 282.59083,221.04513 282.59083,221.04513"
+           id="path5331"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5333"
+           d="M 273.82385,209.9072 C 273.82385,209.9072 267.67742,207.1135 267.67742,207.1135 C 267.05691,206.83146 266.21983,206.85051 265.80038,207.15733 C 265.80038,207.15733 261.55003,210.26641 261.55003,210.26641 C 261.11807,210.58238 261.28238,211.06634 261.91859,211.35037 C 261.91859,211.35037 268.21969,214.1634 268.21969,214.1634 C 268.84554,214.4428 269.68458,214.41091 270.10075,214.09302 C 270.10075,214.09302 274.19645,210.96462 274.19645,210.96462 C 274.60069,210.65585 274.43443,210.18473 273.82385,209.9072 C 273.82385,209.9072 273.82385,209.9072 273.82385,209.9072"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5335"
+           d="M 257.3726,201.76885 C 257.3726,201.76885 264.64202,204.86157 264.64202,204.86157 C 264.95177,204.99335 264.97965,205.26073 264.70378,205.46149 C 264.70378,205.46149 257.98723,210.34941 257.98723,210.34941 C 257.69315,210.56343 257.19866,210.62527 256.87934,210.48761 C 256.87934,210.48761 249.38649,207.25742 249.38649,207.25742 C 249.07032,207.12112 249.06104,206.84361 249.36486,206.63564 C 249.36486,206.63564 256.30482,201.88518 256.30482,201.88518 C 256.58991,201.69003 257.06581,201.63832 257.3726,201.76885 C 257.3726,201.76885 257.3726,201.76885 257.3726,201.76885"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5337"
+           d="M 284.0905,217.40082 C 284.0905,217.40082 288.28521,214.18116 288.28521,214.18116 C 288.70353,213.86008 288.47298,213.36105 287.76806,213.06182 C 287.76806,213.06182 280.69745,210.06045 280.69745,210.06045 C 279.9862,209.75854 279.07181,209.77379 278.64748,210.09508 C 278.64748,210.09508 274.39236,213.31696 274.39236,213.31696 C 273.96443,213.64097 274.19761,214.14507 274.91495,214.44677 C 274.91495,214.44677 282.04596,217.44592 282.04596,217.44592 C 282.75689,217.74492 283.66866,217.7246 284.0905,217.40082 C 284.0905,217.40082 284.0905,217.40082 284.0905,217.40082"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5339"
+           d="M 300.10154,217.06983 C 300.10154,217.06983 316.31837,203.96829 316.31837,203.96829 C 317.07607,203.35614 317.4003,202.7538 317.04873,202.61609 C 317.04873,202.61609 310.03269,199.86796 310.03269,199.86796 C 309.70146,199.73822 308.83575,200.10447 308.08844,200.69068 C 308.08844,200.69068 292.11577,213.22035 292.11577,213.22035 C 291.30809,213.85393 290.91207,214.49139 291.23134,214.64793 C 291.23134,214.64793 298.003,217.96805 298.003,217.96805 C 298.34279,218.13465 299.2804,217.73322 300.10154,217.06983 C 300.10154,217.06983 300.10154,217.06983 300.10154,217.06983"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 291.15172,211.81971 C 291.15172,211.81971 294.95784,208.85652 294.95784,208.85652 C 295.33535,208.56261 295.06588,208.08714 294.35728,207.79127 C 294.35728,207.79127 287.46322,204.91266 287.46322,204.91266 C 286.7905,204.63177 285.951,204.6327 285.57753,204.91398 C 285.57753,204.91398 281.81341,207.749 281.81341,207.749 C 281.43293,208.03556 281.66197,208.50175 282.33038,208.79505 C 282.33038,208.79505 289.18243,211.80181 289.18243,211.80181 C 289.88692,212.11095 290.76688,212.11932 291.15172,211.81971 C 291.15172,211.81971 291.15172,211.81971 291.15172,211.81971"
+           id="path5341"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 279.78845,205.15721 C 279.78845,205.15721 273.64923,202.7238 273.64923,202.7238 C 273.07415,202.49585 272.35188,202.50572 272.02271,202.74427 C 272.02271,202.74427 268.6721,205.17246 268.6721,205.17246 C 268.33004,205.42034 268.50084,205.82253 269.06217,206.07617 C 269.06217,206.07617 275.0647,208.78839 275.0647,208.78839 C 275.70867,209.07937 276.52129,209.09274 276.87788,208.8159 C 276.87788,208.8159 280.365,206.10863 280.365,206.10863 C 280.70701,205.84311 280.44598,205.41784 279.78845,205.15721 C 279.78845,205.15721 279.78845,205.15721 279.78845,205.15721"
+           id="path5343"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5345"
+           d="M 266.63729,203.4528 C 266.63729,203.4528 259.22437,200.28887 259.22437,200.28887 C 258.90107,200.15088 259.26093,199.6067 260.02623,199.07179 C 260.02623,199.07179 276.04515,187.8754 276.04515,187.8754 C 276.70328,187.4154 277.47826,187.14286 277.78675,187.26189 C 277.78675,187.26189 284.85126,189.98782 284.85126,189.98782 C 285.16872,190.11032 284.90984,190.59119 284.26684,191.06857 C 284.26684,191.06857 268.59644,202.70261 268.59644,202.70261 C 267.84684,203.25912 266.97083,203.59516 266.63729,203.4528 C 266.63729,203.4528 266.63729,203.4528 266.63729,203.4528"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5347"
+           sodipodi:nodetypes="cccccssssccccc"
+           d="M 306.08894,198.05464 C 306.08894,198.05464 289.86483,191.75754 289.86483,191.75754 C 288.75309,191.32603 287.34294,191.34222 286.72043,191.80216 C 286.72043,191.80216 275.86985,199.81916 275.86985,199.81916 C 275.21811,200.30071 275.08509,200.71469 276.34881,201.06644 L 282.51762,203.58752 C 283.51554,203.99535 283.51356,204.01646 284.38014,203.38499 L 285.53498,202.54346 C 285.7875,202.35945 286.54428,202.2881 287.35569,202.6377 L 295.71352,206.2387 C 296.63524,206.49525 297.43593,206.57335 297.95625,206.16455 C 297.95625,206.16455 306.65122,199.3329 306.65122,199.3329 C 307.15193,198.93949 306.90591,198.37173 306.08894,198.05464 C 306.08894,198.05464 306.08894,198.05464 306.08894,198.05464"
+           style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      </g>
+      <path
+         d="M 195.84384,316.18138 C 198.01729,317.52381 197.72963,318.48268 195.26851,320.68809 C 192.78733,322.9115 196.00365,319.69726 196.67486,318.67446 C 197.37566,317.60657 193.70779,314.86206 195.84384,316.18138 z "
+         sodipodi:nodetypes="czzz"
+         id="path2379"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3350"
+         sodipodi:nodetypes="czzz"
+         d="M 216.172,300.16816 C 218.34546,301.51059 218.79294,302.1818 216.33181,304.38722 C 213.85064,306.61061 216.81125,303.42834 217.61032,302.34161 C 218.38754,301.28458 214.03595,298.84884 216.172,300.16816 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 206.4554,308.31861 C 208.62885,309.66103 209.20418,310.14046 206.74306,312.34587 C 204.26188,314.56927 207.35035,311.1313 207.82978,310.49205 C 208.39381,309.74001 204.31935,306.99928 206.4554,308.31861 z "
+         sodipodi:nodetypes="czzz"
+         id="path3352"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3354"
+         sodipodi:nodetypes="czzz"
+         d="M 225.50506,292.46519 C 227.67851,293.80762 228.25383,294.28706 225.79272,296.49247 C 223.31154,298.71586 227.0073,295.62948 226.78356,294.4149 C 226.56079,293.20564 223.36901,291.14588 225.50506,292.46519 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3356"
+         sodipodi:nodetypes="czzz"
+         d="M 203.49886,293.36015 C 205.63491,294.67947 206.0239,295.13407 203.48288,297.25957 C 200.9308,299.39433 204.5367,295.76662 204.79335,295.2779 C 205.04344,294.80165 201.36281,292.04083 203.49886,293.36015 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 189.00386,289.3009 C 191.59281,290.73922 191.94897,291.213 189.29153,293.32818 C 186.61856,295.45568 190.37824,292.20949 190.37824,291.21865 C 190.37824,290.19585 186.42834,287.87006 189.00386,289.3009 z "
+         sodipodi:nodetypes="czzz"
+         id="path3358"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3360"
+         sodipodi:nodetypes="czzz"
+         d="M 171.16877,285.43344 C 173.47007,286.64801 174.17325,287.38316 171.45643,289.46072 C 168.74413,291.53484 172.83082,288.34203 172.60709,287.38316 C 172.36917,286.36356 168.8898,284.23064 171.16877,285.43344 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 155.9546,281.50205 C 158.12806,282.78055 158.83124,283.54764 156.24227,285.52933 C 153.64201,287.51964 157.42488,284.66634 157.20114,283.38784 C 156.97829,282.11439 153.75272,280.20681 155.9546,281.50205 z "
+         sodipodi:nodetypes="czzz"
+         id="path3362"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 161.10057,292.56108 C 163.27403,293.9035 163.96914,294.4897 161.38823,296.58835 C 158.81117,298.6839 157.15517,299.80076 160.6531,296.71621 C 164.16898,293.61583 158.96453,291.24176 161.10057,292.56108 z "
+         sodipodi:nodetypes="czzz"
+         id="path3364"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 178.74389,296.62031 C 182.35566,298.53807 181.81229,298.31433 176.12296,302.85301 C 170.44917,307.37928 178.76168,300.08938 179.60688,299.14536 C 180.42197,298.23497 175.15702,294.71579 178.74389,296.62031 z "
+         sodipodi:nodetypes="czzz"
+         id="path3366"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 193.22292,300.42386 C 195.39636,301.76628 195.49224,303.36442 189.3874,308.28664 C 183.26317,313.22451 195.05807,303.89438 193.51058,301.89414 C 192.00596,299.94933 191.08687,299.10454 193.22292,300.42386 z "
+         sodipodi:nodetypes="czzz"
+         id="path3368"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3370"
+         sodipodi:nodetypes="czzz"
+         d="M 145.18324,288.69362 C 147.80416,290.22781 147.8681,290.89903 145.18324,292.91266 C 142.52875,294.90353 146.62155,291.63417 146.39782,290.67529 C 146.18115,289.74671 142.58609,287.17332 145.18324,288.69362 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3372"
+         sodipodi:nodetypes="czzz"
+         d="M 165.73514,274.56618 C 167.9086,275.9086 168.61177,276.57982 166.0228,278.59347 C 163.40256,280.63143 166.7899,277.25103 167.20542,276.73963 C 167.59048,276.2657 163.5991,273.24685 165.73514,274.56618 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 181.26892,278.14598 C 183.44237,279.48841 184.0177,279.96784 181.55659,282.17326 C 179.07541,284.39665 182.5794,281.34223 182.35566,280.06373 C 182.13279,278.79029 179.13287,276.82666 181.26892,278.14598 z "
+         sodipodi:nodetypes="czzz"
+         id="path3374"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3376"
+         sodipodi:nodetypes="czzz"
+         d="M 198.2659,281.884 C 200.43936,283.22641 201.01468,283.70585 198.55356,285.91126 C 196.07238,288.13466 199.57637,285.08024 199.35263,283.80174 C 199.12976,282.52829 196.12986,280.56467 198.2659,281.884 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 213.08942,285.57681 C 215.8957,286.96443 216.29022,287.35347 213.37708,289.60408 C 210.50713,291.82133 214.72017,288.27028 214.62817,287.40415 C 214.54091,286.58272 210.2463,284.17095 213.08942,285.57681 z "
+         sodipodi:nodetypes="czzz"
+         id="path3378"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3380"
+         sodipodi:nodetypes="czzz"
+         d="M 175.13212,267.66227 C 177.30557,269.00471 177.99635,269.63282 175.41979,271.68956 C 172.87468,273.72117 176.44259,270.7946 176.41062,269.64395 C 176.37866,268.4933 172.99607,266.34296 175.13212,267.66227 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3382"
+         sodipodi:nodetypes="czzz"
+         d="M 190.72984,270.85853 C 192.96721,272.0731 193.60646,272.74431 191.01751,274.88581 C 188.47105,276.99212 192.23208,274.11871 192.00834,272.84021 C 191.78548,271.56676 188.48472,269.63975 190.72984,270.85853 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 207.55763,274.67852 C 209.73109,276.02094 210.30641,276.50038 207.8453,278.70579 C 205.36412,280.92919 208.8681,277.87477 208.64437,276.59627 C 208.4215,275.32282 205.42158,273.35919 207.55763,274.67852 z "
+         sodipodi:nodetypes="czzz"
+         id="path3384"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3386"
+         sodipodi:nodetypes="czzz"
+         d="M 223.20376,277.63457 C 226.23604,279.29342 225.95254,279.45644 223.49142,281.66186 C 221.01023,283.88525 224.74999,280.23696 224.6521,279.41673 C 224.56485,278.6857 220.20621,275.99474 223.20376,277.63457 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 277.38117,248.70302 C 281.95031,251.26589 281.48626,250.13465 270.88855,258.92295 C 260.38024,267.63712 274.33964,254.68517 277.12282,252.1444 C 279.88337,249.62427 272.7431,246.10148 277.38117,248.70302 z "
+         sodipodi:nodetypes="czzz"
+         id="path3388"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3390"
+         sodipodi:nodetypes="czzz"
+         d="M 231.57794,270.34712 C 233.75139,271.68956 234.32672,272.16899 231.86561,274.37441 C 229.38442,276.5978 232.88839,273.54338 232.66467,272.26488 C 232.4418,270.99143 229.44189,269.02781 231.57794,270.34712 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 241.29454,263.69892 C 243.46799,265.04135 244.04332,265.52079 241.58221,267.72621 C 239.10103,269.9496 242.60501,266.89517 242.38128,265.61667 C 242.15841,264.34322 239.15849,262.3796 241.29454,263.69892 z "
+         sodipodi:nodetypes="czzz"
+         id="path3392"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3394"
+         sodipodi:nodetypes="czzz"
+         d="M 250.24405,256.53932 C 252.4175,257.88175 252.99283,258.36119 250.53172,260.5666 C 248.05053,262.78999 251.55451,259.73557 251.33077,258.45707 C 251.10792,257.18362 248.10801,255.21999 250.24405,256.53932 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 258.68216,249.37971 C 260.85561,250.72214 261.43093,251.20158 258.96982,253.40699 C 256.48864,255.63038 259.99262,252.57596 259.76888,251.29747 C 259.54603,250.02402 256.54611,248.06039 258.68216,249.37971 z "
+         sodipodi:nodetypes="czzz"
+         id="path3396"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3398"
+         sodipodi:nodetypes="czzz"
+         d="M 215.08528,267.40658 C 217.25872,268.749 217.83405,269.22845 215.37294,271.43386 C 212.89175,273.65725 216.39574,270.60283 216.172,269.32432 C 215.94914,268.05088 212.94923,266.08725 215.08528,267.40658 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 200.54781,263.45098 C 204.31939,265.43265 203.68014,265.40068 200.83547,267.47825 C 198.05046,269.51225 201.27066,266.64722 202.04136,265.54954 C 202.78342,264.49262 196.80222,261.48295 200.54781,263.45098 z "
+         sodipodi:nodetypes="czzz"
+         id="path3400"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3402"
+         sodipodi:nodetypes="czzz"
+         d="M 185.48798,260.50267 C 188.30069,262.16472 188.68423,262.2606 185.77564,264.52995 C 182.90663,266.76843 187.05414,263.76284 186.83041,262.48434 C 186.60755,261.21089 182.72253,258.86853 185.48798,260.50267 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 194.50141,253.47091 C 196.67486,254.81334 197.25018,255.29277 194.78907,257.49819 C 192.30789,259.72159 195.81187,256.66717 195.58814,255.38867 C 195.36528,254.11522 192.36537,252.15159 194.50141,253.47091 z "
+         sodipodi:nodetypes="czzz"
+         id="path3404"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3406"
+         sodipodi:nodetypes="czzz"
+         d="M 204.79335,246.88664 C 206.96679,248.22906 207.54211,248.7085 205.081,250.91391 C 202.59982,253.13731 206.10381,250.08289 205.88007,248.80439 C 205.65721,247.53094 202.6573,245.56731 204.79335,246.88664 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 209.97127,256.34754 C 212.14473,257.68997 212.72004,258.16941 210.25893,260.37482 C 207.77775,262.59821 211.28174,259.54379 211.058,258.26529 C 210.83513,256.99184 207.83522,255.02823 209.97127,256.34754 z "
+         sodipodi:nodetypes="czzz"
+         id="path3408"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3410"
+         sodipodi:nodetypes="czzz"
+         d="M 219.94358,248.99617 C 222.11703,250.33858 222.69236,250.81803 220.23124,253.02345 C 217.75006,255.24684 221.25405,252.19241 221.03031,250.91391 C 220.80745,249.64046 217.80754,247.67683 219.94358,248.99617 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 226.08039,260.5666 C 228.25383,261.90901 228.82916,262.38845 226.36805,264.59388 C 223.88687,266.81727 227.39085,263.76284 227.16711,262.48434 C 226.94426,261.21089 223.94434,259.24727 226.08039,260.5666 z "
+         sodipodi:nodetypes="czzz"
+         id="path3412"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3414"
+         sodipodi:nodetypes="czzz"
+         d="M 234.39063,254.11016 C 236.56408,255.4526 237.13941,255.93204 234.6783,258.13745 C 232.19712,260.36084 235.70111,257.30642 235.47737,256.02792 C 235.25451,254.75447 232.25458,252.79084 234.39063,254.11016 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 229.27663,242.2201 C 231.45009,243.56254 232.02541,244.04197 229.5643,246.24738 C 227.08312,248.47078 230.58711,245.41636 230.36337,244.13786 C 230.1405,242.86442 227.14058,240.90079 229.27663,242.2201 z "
+         sodipodi:nodetypes="czzz"
+         id="path3416"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3418"
+         sodipodi:nodetypes="czzz"
+         d="M 243.0141,247.59302 C 245.39457,248.78588 245.85328,249.41488 243.39217,251.6203 C 240.91099,253.84369 244.68618,250.11125 244.59804,249.42037 C 244.51044,248.73372 240.56163,246.3641 243.0141,247.59302 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 213.8707,239.98274 C 216.04415,241.32515 216.61948,241.8046 214.15837,244.01001 C 211.67717,246.23341 215.18116,243.17898 214.95742,241.90048 C 214.73456,240.62703 211.73465,238.66341 213.8707,239.98274 z "
+         sodipodi:nodetypes="czzz"
+         id="path3420"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3422"
+         sodipodi:nodetypes="czzz"
+         d="M 222.91837,233.04137 C 225.31782,234.20299 225.93836,234.99885 223.20602,237.06866 C 220.48021,239.13354 224.63565,235.424 224.54751,234.77832 C 224.44726,234.04379 220.53635,231.8882 222.91837,233.04137 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 246.64686,229.49127 C 249.00111,230.74329 249.89286,231.08713 246.93452,233.51855 C 243.99824,235.93184 248.36413,232.59712 248.14039,231.31861 C 247.91754,230.04517 244.31107,228.24907 246.64686,229.49127 z "
+         sodipodi:nodetypes="czzz"
+         id="path3424"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3426"
+         sodipodi:nodetypes="czzz"
+         d="M 237.71153,235.84633 C 240.02059,237.03055 240.64112,237.84899 237.9992,239.87362 C 235.36048,241.89576 239.38361,238.50015 239.25027,237.67368 C 239.10149,236.75149 235.41158,234.66678 237.71153,235.84633 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 284.38244,228.84004 C 286.66889,230.04686 287.0481,230.84805 284.67009,232.86732 C 282.27897,234.89772 285.51201,231.73741 285.85337,231.07421 C 286.18578,230.42841 282.11363,227.64254 284.38244,228.84004 z "
+         sodipodi:nodetypes="czzz"
+         id="path3428"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3430"
+         sodipodi:nodetypes="czzz"
+         d="M 232.52396,226.41189 C 234.78783,227.52831 235.40834,228.14335 232.81163,230.16796 C 230.19284,232.20977 234.24125,228.7945 234.06271,228.10364 C 233.88666,227.4224 230.2964,225.31338 232.52396,226.41189 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 242.49949,219.69334 C 245.873,221.52582 244.75103,221.244 242.19951,223.3138 C 239.67516,225.36158 243.76147,221.8425 243.54101,221.11387 C 243.32053,220.38523 239.12596,217.86086 242.49949,219.69334 z "
+         sodipodi:nodetypes="czzz"
+         id="path3432"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3434"
+         sodipodi:nodetypes="czzz"
+         d="M 255.29092,223.29442 C 257.75818,224.59164 258.28831,225.25187 255.57858,227.32169 C 252.87211,229.38902 256.89519,225.88045 256.80705,225.21217 C 256.71558,224.51847 252.84135,222.00649 255.29092,223.29442 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 269.10194,227.3895 C 271.27539,228.73192 271.85071,229.21136 269.3896,231.41677 C 266.90842,233.64017 270.41241,230.58576 270.18867,229.30725 C 269.96581,228.0338 266.96589,226.07017 269.10194,227.3895 z "
+         sodipodi:nodetypes="czzz"
+         id="path3436"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3438"
+         sodipodi:nodetypes="czzz"
+         d="M 260.47206,233.782 C 262.64551,235.12443 263.22084,235.60386 260.75972,237.80927 C 258.27854,240.03267 261.78252,236.97826 261.55878,235.69975 C 261.33593,234.42631 258.33601,232.46268 260.47206,233.782 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 252.00426,240.41924 C 254.76534,241.85208 254.88865,242.2863 252.29193,244.44653 C 249.69924,246.60338 253.58595,242.98267 253.49781,242.15619 C 253.40345,241.27142 249.33359,239.0316 252.00426,240.41924 z "
+         sodipodi:nodetypes="czzz"
+         id="path3440"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3442"
+         sodipodi:nodetypes="czzz"
+         d="M 266.73671,242.2201 C 268.91017,243.56254 269.4855,244.04197 267.02438,246.24738 C 264.54319,248.47078 268.04717,245.41636 267.82343,244.13786 C 267.60058,242.86442 264.60067,240.90079 266.73671,242.2201 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 275.77863,235.80888 C 278.29108,237.0157 278.6856,237.56294 276.08889,239.70055 C 273.50776,241.82532 277.52012,238.29917 277.43037,237.50061 C 277.34312,236.7244 273.32091,234.62837 275.77863,235.80888 z "
+         sodipodi:nodetypes="czzz"
+         id="path3444"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3446"
+         sodipodi:nodetypes="czzz"
+         d="M 286.95302,240.94161 C 289.63786,242.37993 289.92552,242.74749 287.24067,244.96888 C 284.57146,247.17734 287.44366,244.39791 288.24749,243.37076 C 289.03146,242.369 284.28786,239.51385 286.95302,240.94161 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 295.77487,234.08611 C 298.28732,235.22513 298.70444,235.86277 296.06253,238.11339 C 293.44279,240.34511 296.0466,237.62948 297.1102,236.38809 C 298.1513,235.17297 293.28962,232.95944 295.77487,234.08611 z "
+         sodipodi:nodetypes="czzz"
+         id="path3448"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3450"
+         sodipodi:nodetypes="czzz"
+         d="M 304.36663,227.14477 C 306.83388,228.44201 307.1154,228.96663 304.65428,231.17204 C 302.17311,233.39544 304.72941,230.49547 305.36295,229.31112 C 306.04412,228.03769 301.92637,225.86174 304.36663,227.14477 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 293.12178,221.54036 C 295.47102,222.691 295.98242,223.42615 293.40945,225.56763 C 290.83798,227.70786 294.60852,224.14154 294.54411,223.33025 C 294.48107,222.53624 290.77257,220.38972 293.12178,221.54036 z "
+         sodipodi:nodetypes="czzz"
+         id="path3452"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3454"
+         sodipodi:nodetypes="czzz"
+         d="M 277.5149,220.70385 C 280.36638,222.31747 280.39928,222.70652 277.80256,224.73113 C 275.18377,226.77294 276.77322,224.86768 278.55642,223.48044 C 280.36765,222.07138 274.77049,219.15079 277.5149,220.70385 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 263.22084,217.41719 C 265.39428,218.75961 265.96961,219.23905 263.5085,221.44447 C 261.02732,223.66786 264.5313,220.61343 264.30756,219.33494 C 264.08471,218.06149 261.08479,216.09787 263.22084,217.41719 z "
+         sodipodi:nodetypes="czzz"
+         id="path3456"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3458"
+         sodipodi:nodetypes="czzz"
+         d="M 251.15585,213.31501 C 254.39256,214.77625 254.35665,214.86567 251.3531,216.98068 C 248.32814,219.11078 252.86671,215.85366 252.60418,214.91636 C 252.33613,213.95933 247.95664,211.87072 251.15585,213.31501 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 260.85561,206.10245 C 263.02906,207.44489 263.60438,207.92432 261.14327,210.12974 C 258.66209,212.35313 262.16607,209.29871 261.94234,208.02022 C 261.71947,206.74677 258.71956,204.78313 260.85561,206.10245 z "
+         sodipodi:nodetypes="czzz"
+         id="path3460"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3462"
+         sodipodi:nodetypes="czzz"
+         d="M 271.01969,211.72786 C 273.19314,213.07028 273.76847,213.54972 271.30736,215.75513 C 268.82618,217.97853 272.33015,214.92411 272.10641,213.64561 C 271.88356,212.37216 268.88364,210.40854 271.01969,211.72786 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 284.99091,214.99901 C 287.70679,216.25102 288.10131,216.82086 285.27858,219.02628 C 282.51164,221.18811 285.03572,219.0993 286.34886,217.27838 C 287.55847,215.60098 282.34938,213.78125 284.99091,214.99901 z "
+         sodipodi:nodetypes="czzz"
+         id="path3464"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3466"
+         sodipodi:nodetypes="czzz"
+         d="M 276.90079,206.80563 C 279.07425,208.14806 279.64957,208.6275 277.18845,210.83291 C 274.70727,213.05631 278.21126,210.00188 277.98752,208.72339 C 277.76466,207.44994 274.76474,205.48631 276.90079,206.80563 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 292.17888,209.68226 C 294.35233,211.02468 294.92766,211.50412 292.46654,213.70954 C 289.98536,215.93294 293.48934,212.87852 293.2656,211.60001 C 293.04275,210.32657 290.04283,208.36293 292.17888,209.68226 z "
+         sodipodi:nodetypes="czzz"
+         id="path3468"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3470"
+         sodipodi:nodetypes="czzz"
+         d="M 314.68049,204.24863 C 317.7489,205.39928 317.87645,205.56214 311.38835,210.80095 C 304.93164,216.01443 312.98405,209.0753 315.15994,207.02937 C 317.35285,204.96744 311.62248,203.10188 314.68049,204.24863 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 303.62147,199.96565 C 305.79492,201.30808 306.37025,201.78752 303.90913,203.99293 C 301.42794,206.21632 304.93192,203.16191 304.70818,201.8834 C 304.48533,200.60995 301.48542,198.64633 303.62147,199.96565 z "
+         sodipodi:nodetypes="czzz"
+         id="path3472"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3474"
+         sodipodi:nodetypes="czzz"
+         d="M 281.07787,191.30607 C 284.96899,192.8745 284.82107,192.72112 278.69861,197.41265 C 272.5826,202.09922 281.39387,194.86394 282.66181,193.63065 C 283.93119,192.39593 277.15462,189.7247 281.07787,191.30607 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3477"
+         d="M 247.0478,21.870476 L 41.113266,89.503174 L 48.496608,139.14097 C 116.54867,104.13919 192.87556,126.61352 246.15284,103.7265 L 247.0478,21.870476 z "
+         style="fill:url(#linearGradient2500);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         inkscape:transform-center-x="21.926591"
+         inkscape:transform-center-y="-98.60823"
+         d="M 27.882376,75.746856 C 32.065415,74.206702 33.389047,74.781014 34.949531,76.633878 L 65.088465,277.78657 L 56.641394,273.5449 L 27.882376,75.746856 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2180"
+         style="fill:#1a1a1a;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path13298"
+         sodipodi:nodetypes="ccccc"
+         d="M 92.828627,259.35202 L 230.90672,174.71526 C 232.64824,175.78414 234.17161,176.98389 234.23083,179.06216 L 96.66413,263.82678 C 96.420949,261.86067 95.117349,260.38059 92.828627,259.35202 z "
+         style="fill:#808080;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+         id="path14269"
+         sodipodi:nodetypes="ccccc"
+         d="M 166.43526,236.83511 L 228.92582,195.64614 L 229.45615,196.53002 L 168.02625,238.24933 L 166.43526,236.83511 z "
+         style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" />
+      <path
+         transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+         d="M 114.5513,270.06913 L 130.10765,259.99286 L 130.63798,260.87674 L 116.14229,271.48335 L 114.5513,270.06913 z "
+         sodipodi:nodetypes="ccccc"
+         id="path16261"
+         style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" />
+      <path
+         transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+         d="M 114.5513,270.06913 L 130.10765,259.99286 L 130.63798,260.87674 L 116.14229,271.48335 L 114.5513,270.06913 z "
+         sodipodi:nodetypes="ccccc"
+         id="path16263"
+         style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" />
+      <path
+         transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+         d="M 166.43526,236.83511 L 228.92582,195.64614 L 229.45615,196.53002 L 168.02625,238.24933 L 166.43526,236.83511 z "
+         sodipodi:nodetypes="ccccc"
+         id="path16267"
+         style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" />
+      <path
+         d="M 104.05869,273.38134 C 106.59601,271.03005 113.13546,275.43531 112.49482,278.36804 L 104.05869,273.38134 z "
+         sodipodi:nodetypes="ccc"
+         id="path16269"
+         style="opacity:0.99720004;fill:#666666;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="ccsccccc"
+         id="path25800"
+         d="M 304.06894,248.1971 C 302.1656,248.29093 300.4498,248.80189 299.33849,249.7313 C 299.33849,249.7313 260.56796,282.17326 260.56795,282.17326 C 257.93245,284.37739 258.86046,287.65621 262.70943,289.52464 C 262.70943,289.52464 267.73895,291.93253 272.68174,294.28706 C 306.66914,288.15442 283.9982,253.02759 313.94536,251.0098 C 312.61326,250.41591 309.88612,249.2199 309.88612,249.2199 C 308.06831,248.43649 305.97228,248.10326 304.06894,248.1971 z "
+         style="fill:url(#linearGradient2490);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.9527356;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+    </g>
+  </g>
+</svg>
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg b/hostap/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg
new file mode 100644
index 0000000..b3abf0a
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg
@@ -0,0 +1,256 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="128"
+   height="128"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   sodipodi:docname="wpa_gui.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <metadata
+     id="metadata47">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     inkscape:window-height="771"
+     inkscape:window-width="640"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     guidetolerance="10.0"
+     gridtolerance="10.0"
+     objecttolerance="10.0"
+     borderopacity="1.0"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     showgrid="false"
+     inkscape:zoom="4.2421875"
+     inkscape:cx="64"
+     inkscape:cy="64"
+     inkscape:window-x="634"
+     inkscape:window-y="0"
+     inkscape:current-layer="svg2" />
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 64 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="128 : 64 : 1"
+       inkscape:persp3d-origin="64 : 42.666667 : 1"
+       id="perspective49" />
+    <linearGradient
+       id="linearGradient39133">
+      <stop
+         id="stop39135"
+         style="stop-color:#252525;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop39137"
+         style="stop-color:#515151;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop39139"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="0.28677997" />
+      <stop
+         id="stop39141"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0.92151743" />
+      <stop
+         id="stop39143"
+         style="stop-color:#ffffff;stop-opacity:0.73786408"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient39119">
+      <stop
+         id="stop39121"
+         style="stop-color:#ffffff;stop-opacity:0.82905984"
+         offset="0" />
+      <stop
+         id="stop39123"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient39106">
+      <stop
+         id="stop39108"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop39110"
+         style="stop-color:#a8a8a8;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient39094">
+      <stop
+         id="stop39096"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop39098"
+         style="stop-color:#333333;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient39062">
+      <stop
+         id="stop39064"
+         style="stop-color:#252525;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop39086"
+         style="stop-color:#515151;stop-opacity:1"
+         offset="0.21101321" />
+      <stop
+         id="stop39088"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="0.75" />
+      <stop
+         id="stop39090"
+         style="stop-color:#6c6c6c;stop-opacity:1"
+         offset="0.875" />
+      <stop
+         id="stop39066"
+         style="stop-color:#1e1e1e;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="4"
+       y1="40"
+       x2="124"
+       y2="60"
+       id="linearGradient39068"
+       xlink:href="#linearGradient39062"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       cx="100.70589"
+       cy="96"
+       r="60"
+       fx="158.07428"
+       fy="95.718063"
+       id="radialGradient39100"
+       xlink:href="#linearGradient39094"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.7837903e-8,-1,0.99999999,-2.1864248e-6,-32.000004,164.7061)" />
+    <radialGradient
+       cx="100.44444"
+       cy="34.363636"
+       r="32"
+       fx="83.18"
+       fy="34.228985"
+       id="radialGradient39104"
+       xlink:href="#linearGradient39106"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(3.1472435e-6,1.0227273,-0.87499999,-9.5061964e-8,94.067865,-4.7272712)" />
+    <radialGradient
+       cx="75.999977"
+       cy="-2.7730541"
+       r="48"
+       fx="55.266491"
+       fy="-2.5338216"
+       id="radialGradient39125"
+       xlink:href="#linearGradient39119"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,0.83333324,-1.6666667,2.518705e-6,59.378243,-35.333302)" />
+    <radialGradient
+       cx="64.066589"
+       cy="63.713329"
+       r="60"
+       fx="64.066589"
+       fy="63.713329"
+       id="radialGradient39131"
+       xlink:href="#linearGradient39133"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.1333333,5.1768857e-8,5.2556881e-6,1.1666667,-8.6091298,-10.332226)" />
+    <filter
+       id="filter39153">
+      <feGaussianBlur
+         id="feGaussianBlur39155"
+         stdDeviation="2.28"
+         inkscape:collect="always" />
+    </filter>
+    <filter
+       id="filter39159">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.68"
+         id="feGaussianBlur39161" />
+    </filter>
+  </defs>
+  <g
+     id="layer1"
+     style="display:inline">
+    <path
+       d="M 29,4 C 15.147058,4 4,15.14706 4,29 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,15.14706 112.85294,4 99,4 L 29,4 z"
+       id="path39151"
+       style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter39153)" />
+    <path
+       d="M 29,4 C 15.147058,4 4,15.14706 4,29 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,15.14706 112.85294,4 99,4 L 29,4 z"
+       id="path39157"
+       style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter39159)" />
+    <rect
+       width="120"
+       height="120"
+       ry="25.00531"
+       x="4"
+       y="0"
+       id="rect2573"
+       style="opacity:1;fill:url(#radialGradient39100);fill-opacity:1;stroke:none" />
+    <path
+       d="M 29,0 C 15.147058,0 4,11.14706 4,25 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,11.14706 112.85294,0 99,0 L 29,0 z"
+       id="path39127"
+       style="opacity:0.20512821;fill:url(#radialGradient39131);fill-opacity:1;stroke:none" />
+    <path
+       d="m 44,68 40,0 12,40 c -20,7.27273 -44,7.27273 -64,0 L 44,68 z"
+       id="path39102"
+       style="opacity:0.53418801;fill:url(#radialGradient39104);fill-opacity:1;stroke:none" />
+    <path
+       d="M 25.339207,12 C 52,8 76,8 102.66079,12 107.83471,12 112,16.165286 112,21.339207 L 116,52 C 100,73.339207 28,73.339207 12,52 L 16,21.339207 C 16,16.165286 20.165286,12 25.339207,12 z"
+       id="rect39116"
+       style="opacity:0.92307691;fill:url(#radialGradient39125);fill-opacity:1;stroke:none" />
+    <path
+       d="M 29,8 C 15.147058,8 4,19.14706 4,33 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,19.14706 112.85294,8 99,8 L 29,8 z"
+       id="path39147"
+       style="opacity:0.20512821;fill:#000000;fill-opacity:1;stroke:none" />
+    <path
+       d="M 29,0 C 15.147058,0 4,11.147058 4,25 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,11.147058 112.85294,0 99,0 L 29,0 z m 0,4 70,0 c 11.70613,0 21,9.293869 21,21 l 0,70 c 0,11.70613 -9.29387,21 -21,21 l -70,0 C 17.293869,116 8,106.70613 8,95 L 8,25 C 8,13.293869 17.293869,4 29,4 z"
+       id="rect39029"
+       style="opacity:1;fill:url(#linearGradient39068);fill-opacity:1;stroke:none" />
+    <path
+       d="M 66.35081,74.771345 A 36,36 0 1 1 54.34964,35.777782"
+       transform="matrix(-0.16680323,0.53082142,-0.53082142,-0.16680323,103.31027,53.117897)"
+       id="path3351"
+       style="opacity:1;fill:none;stroke:#ffffff;stroke-width:21.56673813;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+    <path
+       d="m 36,56 a 4,4 0 1 1 -8,0 4,4 0 1 1 8,0 z"
+       transform="matrix(1.4851301,0,0,1.4851301,16.475837,-23.948973)"
+       id="path3353"
+       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none" />
+    <path
+       d="M 66.35081,74.771345 A 36,36 0 1 1 54.34964,35.777782"
+       transform="matrix(-0.35033273,1.1148712,-1.1148712,-0.35033273,146.5624,46.88078)"
+       id="path2622"
+       style="opacity:1;fill:none;stroke:#ffffff;stroke-width:10.26852894;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+  </g>
+</svg>
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/icons_png.qrc b/hostap/wpa_supplicant/wpa_gui-qt4/icons_png.qrc
new file mode 100644
index 0000000..9a30b7f
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/icons_png.qrc
@@ -0,0 +1,9 @@
+<RCC>
+ <qresource prefix="/icons" >
+  <file alias="wpa_gui.png">icons/hicolor/16x16/apps/wpa_gui.png</file>
+  <file alias="ap.png">icons/hicolor/32x32/apps/ap.png</file>
+  <file alias="laptop.png">icons/hicolor/32x32/apps/laptop.png</file>
+  <file alias="group.png">icons/hicolor/32x32/apps/group.png</file>
+  <file alias="invitation.png">icons/hicolor/32x32/apps/invitation.png</file>
+ </qresource>
+</RCC>
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/lang/wpa_gui_de.ts b/hostap/wpa_supplicant/wpa_gui-qt4/lang/wpa_gui_de.ts
new file mode 100644
index 0000000..d7a9c89
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/lang/wpa_gui_de.ts
@@ -0,0 +1,1262 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="de_DE" sourcelanguage="en_US">
+<context>
+    <name>AddInterface</name>
+    <message>
+        <location filename="../addinterface.cpp" line="38"/>
+        <source>Select network interface to add</source>
+        <translation>Wähle die Netzwerkschnittstelle zum hinzufügen aus</translation>
+    </message>
+    <message>
+        <location filename="../addinterface.cpp" line="47"/>
+        <source>driver</source>
+        <translation>Treiber</translation>
+    </message>
+    <message>
+        <location filename="../addinterface.cpp" line="48"/>
+        <source>interface</source>
+        <translation>Schnittstelle</translation>
+    </message>
+    <message>
+        <location filename="../addinterface.cpp" line="49"/>
+        <source>description</source>
+        <translation>Beschreibung</translation>
+    </message>
+    <message>
+        <location filename="../addinterface.cpp" line="221"/>
+        <source>Add interface command could not be completed.</source>
+        <translation>Das Schnittstellen hinzufügen Kommando konnte nicht abgeschlossen werden.</translation>
+    </message>
+    <message>
+        <location filename="../addinterface.cpp" line="229"/>
+        <source>Failed to add the interface.</source>
+        <translation>Fehler beim hinzufügen der Schnittstelle.</translation>
+    </message>
+    <message>
+        <location filename="../addinterface.cpp" line="238"/>
+        <source>Failed to add the interface into registry.</source>
+        <translation>Fehler beim hinzufügen der Schnittstelle in die Registry.</translation>
+    </message>
+</context>
+<context>
+    <name>ErrorMsg</name>
+    <message>
+        <location filename="../wpagui.cpp" line="1621"/>
+        <source>wpa_gui error</source>
+        <translation>wpa_gui Fehler</translation>
+    </message>
+</context>
+<context>
+    <name>EventHistory</name>
+    <message>
+        <location filename="../eventhistory.ui" line="13"/>
+        <source>Event history</source>
+        <translation>Ereignis Historie</translation>
+    </message>
+    <message>
+        <location filename="../eventhistory.ui" line="48"/>
+        <source>Close</source>
+        <translation>Schließen</translation>
+    </message>
+</context>
+<context>
+    <name>EventListModel</name>
+    <message>
+        <location filename="../eventhistory.cpp" line="62"/>
+        <source>Timestamp</source>
+        <translation>Zeit</translation>
+    </message>
+    <message>
+        <location filename="../eventhistory.cpp" line="64"/>
+        <source>Message</source>
+        <translation>Meldung</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkConfig</name>
+    <message>
+        <location filename="../networkconfig.ui" line="13"/>
+        <source>NetworkConfig</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="19"/>
+        <source>Cancel</source>
+        <translation>Abbrechen</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="35"/>
+        <source>SSID</source>
+        <translation>SSID</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="42"/>
+        <source>Network name (Service Set IDentifier)</source>
+        <translation>Netzwerkname (Service Set IDentifier)</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="52"/>
+        <source>Authentication</source>
+        <translation>Authentifizierung</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="60"/>
+        <source>Plaintext (open / no authentication)</source>
+        <translation>Plaintext (offen / keine Authentifizierung)</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="65"/>
+        <source>Static WEP (no authentication)</source>
+        <translation>Static WEP (keine Authentifizierung)</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="70"/>
+        <source>Static WEP (Shared Key authentication)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="75"/>
+        <source>IEEE 802.1X</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="80"/>
+        <source>WPA-Personal (PSK)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="85"/>
+        <source>WPA-Enterprise (EAP)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="90"/>
+        <source>WPA2-Personal (PSK)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="95"/>
+        <source>WPA2-Enterprise (EAP)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="103"/>
+        <source>Encryption</source>
+        <translation>Verschlüsselung</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="111"/>
+        <source>None</source>
+        <translation>Keine</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="116"/>
+        <source>WEP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="121"/>
+        <source>TKIP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="126"/>
+        <source>CCMP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="134"/>
+        <source>PSK</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="144"/>
+        <source>WPA/WPA2 pre-shared key or passphrase</source>
+        <translation>WPA/WPA2 Pre-Shared Key oder Passphrase</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="157"/>
+        <source>EAP method</source>
+        <translation>EAP Verfahren</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="171"/>
+        <source>Identity</source>
+        <translation>Identität</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="181"/>
+        <source>Username/Identity for EAP methods</source>
+        <translation>Nutzername/Identitär für die EAP Verfahren</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="188"/>
+        <source>Password</source>
+        <translation>Passwort</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="198"/>
+        <source>Password for EAP methods</source>
+        <translation>Passwort für die EAP Verfahren</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="208"/>
+        <source>CA certificate</source>
+        <translation>CA Zertifikat</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="225"/>
+        <source>WEP keys</source>
+        <translation>WEP Schlüssel</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="234"/>
+        <source>key 0</source>
+        <translation>Schlüssel 0</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="244"/>
+        <source>key 1</source>
+        <translation>Schlüssel 1</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="254"/>
+        <source>key 3</source>
+        <translation>Schlüssel 3</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="264"/>
+        <source>key 2</source>
+        <translation>Schlüssel 2</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="305"/>
+        <source>Optional Settings</source>
+        <translation>Optionale Einstellungen</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="311"/>
+        <source>Network Identification String</source>
+        <translation>Netzwerk Indentifikations Zeichenfolge</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="318"/>
+        <source>Network Priority</source>
+        <translation>Netzwerk Priorität</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="331"/>
+        <source>IDString</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="338"/>
+        <source>Priority</source>
+        <translation>Priorität</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="345"/>
+        <source>Inner auth</source>
+        <translation>Geheime Auth</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="365"/>
+        <source>Add</source>
+        <translation>Hinzufügen</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="375"/>
+        <source>Remove</source>
+        <translation>Entfernen</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="398"/>
+        <source>WPS</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="202"/>
+        <source>WPA Pre-Shared Key Error</source>
+        <translation>WPA Pre Shared Key Fehler</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="203"/>
+        <source>WPA-PSK requires a passphrase of 8 to 63 characters
+or 64 hex digit PSK</source>
+        <translation>WPA PSK benötigt ein Passphrase mit 8 bis 63 Zeichen
+oder 64 hexadezimal stelligen PSK</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="215"/>
+        <source>Network ID Error</source>
+        <translation>Netzwerk ID Fehler</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="216"/>
+        <source>Network ID String contains non-word characters.
+It must be a simple string, without spaces, containing
+only characters in this range: [A-Za-z0-9_-]
+</source>
+        <translation>Netzwerk ID Zeichnfolge beinhaltet ungültige Zeichen.
+Es muss eine einfache Zeichnfolge aus [A-Za-z0-9_] ohne
+Leerzeichen sein
+</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="237"/>
+        <source>Failed to add network to wpa_supplicant
+configuration.</source>
+        <translation>Hinzufügen des Netzwerks in die wpa_supplicant
+Konfiguration fehlgeschlagen.</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="414"/>
+        <source>Failed to enable network in wpa_supplicant
+configuration.</source>
+        <translation>Aktivieren des Netzwerks in der wpa_supplicant
+Konfiguration fehlgeschlagen.</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="802"/>
+        <source>This will permanently remove the network
+from the configuration. Do you really want
+to remove this network?</source>
+        <translation>Dies wird das Netzwerk permanent aus
+der Konfiguration entfernen. Möchtest du
+das Netzwerk wirklich entfernen?</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="805"/>
+        <source>Yes</source>
+        <translation>Ja</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="805"/>
+        <source>No</source>
+        <translation>Nein</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="813"/>
+        <source>Failed to remove network from wpa_supplicant
+configuration.</source>
+        <translation>Entfernen des Netzwerks aus der wpa_supplicant
+Konfiguration fehlgeschlagen.</translation>
+    </message>
+</context>
+<context>
+    <name>Peers</name>
+    <message>
+        <location filename="../peers.ui" line="14"/>
+        <source>Peers</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="107"/>
+        <source>Associated station</source>
+        <translation>Verbundene Stationen</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="110"/>
+        <source>AP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="113"/>
+        <source>WPS AP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="116"/>
+        <source>WPS PIN needed</source>
+        <translation>WPS PIN wird benötigt</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="119"/>
+        <source>ER: WPS AP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="122"/>
+        <source>ER: WPS AP (Unconfigured)</source>
+        <translation>ER: WPS AP (nicht konfiguriert)</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="125"/>
+        <source>ER: WPS Enrollee</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="128"/>
+        <source>WPS Enrollee</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="159"/>
+        <source>Enter WPS PIN</source>
+        <translation>WPS PIN Eingabe</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="164"/>
+        <source>Connect (PBC)</source>
+        <translation>Verbinden (PBC)</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="172"/>
+        <source>Enroll (PBC)</source>
+        <translation>Anmelden (PBC)</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="177"/>
+        <source>Learn Configuration</source>
+        <translation>Konfiguration lernen</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="181"/>
+        <source>Properties</source>
+        <translation>Eigenschaften</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="184"/>
+        <source>Refresh</source>
+        <translation>Aktualisieren</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="205"/>
+        <source>PIN:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="206"/>
+        <source>PIN for </source>
+        <translation>Pin für </translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="227"/>
+        <source>Failed to set the WPS PIN.</source>
+        <translation>Setzten des WPS PIN fehlgeschlagen.</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="815"/>
+        <source>Peer Properties</source>
+        <translation>Peer Eigenschaften</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="820"/>
+        <source>Name: </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="827"/>
+        <source>Address: </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="831"/>
+        <source>UUID: </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="835"/>
+        <source>Primary Device Type: </source>
+        <translation>Primärer Geräte Typ: </translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="840"/>
+        <source>SSID: </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="845"/>
+        <source>Configuration Methods: </source>
+        <translation>Konfigurationsverfahren: </translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="847"/>
+        <source>[USBA]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="849"/>
+        <source>[Ethernet]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="851"/>
+        <source>[Label]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="853"/>
+        <source>[Display]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="855"/>
+        <source>[Ext. NFC Token]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="857"/>
+        <source>[Int. NFC Token]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="859"/>
+        <source>[NFC Interface]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="861"/>
+        <source>[Push Button]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="863"/>
+        <source>[Keypad]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="869"/>
+        <source>Device Password ID: </source>
+        <translation>Geräte Passwort ID: </translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="872"/>
+        <source> (Default PIN)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="875"/>
+        <source> (User-specified PIN)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="878"/>
+        <source> (Machine-specified PIN)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="881"/>
+        <source> (Rekey)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="884"/>
+        <source> (Push Button)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="887"/>
+        <source> (Registrar-specified)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="924"/>
+        <source>Failed to start WPS PBC.</source>
+        <translation>Starten von WPS PBC fehlgeschlagen.</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="937"/>
+        <source>AP PIN:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="938"/>
+        <source>AP PIN for </source>
+        <translation>AP PIN für </translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="953"/>
+        <source>Failed to start learning AP configuration.</source>
+        <translation>Fehler beim erkennen der AP Konfiguration.</translation>
+    </message>
+</context>
+<context>
+    <name>ScanResults</name>
+    <message>
+        <location filename="../scanresults.ui" line="13"/>
+        <source>Scan results</source>
+        <translation>Scan Ergebnisse</translation>
+    </message>
+    <message>
+        <location filename="../scanresults.ui" line="32"/>
+        <source>SSID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../scanresults.ui" line="37"/>
+        <source>BSSID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../scanresults.ui" line="42"/>
+        <source>frequency</source>
+        <translation>Frequenz</translation>
+    </message>
+    <message>
+        <location filename="../scanresults.ui" line="47"/>
+        <source>signal</source>
+        <translation>Signal</translation>
+    </message>
+    <message>
+        <location filename="../scanresults.ui" line="52"/>
+        <source>flags</source>
+        <translation>Flags</translation>
+    </message>
+    <message>
+        <location filename="../scanresults.ui" line="75"/>
+        <source>Scan</source>
+        <translation>Scannen</translation>
+    </message>
+    <message>
+        <location filename="../scanresults.ui" line="82"/>
+        <source>Close</source>
+        <translation>Schließen</translation>
+    </message>
+</context>
+<context>
+    <name>UserDataRequest</name>
+    <message>
+        <location filename="../userdatarequest.ui" line="16"/>
+        <source>Authentication credentials required</source>
+        <translation>Authentifzierungs Beglaubigung nötig</translation>
+    </message>
+    <message>
+        <location filename="../userdatarequest.ui" line="77"/>
+        <source>&amp;OK</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../userdatarequest.ui" line="93"/>
+        <source>&amp;Cancel</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../userdatarequest.cpp" line="67"/>
+        <source>Password: </source>
+        <translation>Passwort: </translation>
+    </message>
+    <message>
+        <location filename="../userdatarequest.cpp" line="70"/>
+        <source>New password: </source>
+        <translation>Neues Passwort: </translation>
+    </message>
+    <message>
+        <location filename="../userdatarequest.cpp" line="73"/>
+        <source>Identity: </source>
+        <translation>Identität: </translation>
+    </message>
+    <message>
+        <location filename="../userdatarequest.cpp" line="75"/>
+        <source>Private key passphrase: </source>
+        <translation>Privater Key Passphrase: </translation>
+    </message>
+</context>
+<context>
+    <name>WpaGui</name>
+    <message>
+        <location filename="../wpagui.ui" line="13"/>
+        <source>wpa_gui</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="24"/>
+        <source>Adapter:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="34"/>
+        <source>Network:</source>
+        <translation>Netzwerk:</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="48"/>
+        <source>Current Status</source>
+        <translation>Aktueller Status</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="63"/>
+        <location filename="../wpagui.ui" line="300"/>
+        <source>Status:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="70"/>
+        <source>Last message:</source>
+        <translation>Letzte Meldung:</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="77"/>
+        <source>Authentication:</source>
+        <translation>Authentifizierung:</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="84"/>
+        <source>Encryption:</source>
+        <translation>Verschlüsselung:</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="91"/>
+        <source>SSID:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="98"/>
+        <source>BSSID:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="105"/>
+        <source>IP address:</source>
+        <translation>IP Adresse:</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="177"/>
+        <source>Connect</source>
+        <translation>Verbinden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="184"/>
+        <source>Disconnect</source>
+        <translation>Trennen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="191"/>
+        <location filename="../wpagui.ui" line="286"/>
+        <source>Scan</source>
+        <translation>Scannen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="212"/>
+        <source>Manage Networks</source>
+        <translation>Netzwerke verwalten</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="238"/>
+        <source>Enabled</source>
+        <translation>Aktiviert</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="245"/>
+        <source>Edit</source>
+        <translation>Bearbeiten</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="252"/>
+        <source>Remove</source>
+        <translation>Entfernen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="272"/>
+        <source>Disabled</source>
+        <translation>Deaktiviert</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="279"/>
+        <source>Add</source>
+        <translation>Hinzufügen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="294"/>
+        <source>WPS</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="314"/>
+        <source>PBC - push button</source>
+        <translation>PBC - Taste</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="321"/>
+        <source>Generate PIN</source>
+        <translation>PIN erzeugen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="328"/>
+        <source>PIN:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="348"/>
+        <source>Use AP PIN</source>
+        <translation>AP PIN verwenden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="355"/>
+        <source>AP PIN:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="390"/>
+        <source>&amp;File</source>
+        <translation>&amp;Datei</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="401"/>
+        <source>&amp;Network</source>
+        <translation>&amp;Netzwerk</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="413"/>
+        <source>&amp;Help</source>
+        <translation>&amp;Hilfe</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="426"/>
+        <source>Event &amp;History</source>
+        <translation>Ereignis &amp;Historie</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="431"/>
+        <source>&amp;Save Configuration</source>
+        <translation>Konfiguration &amp;Speichern</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="434"/>
+        <source>Ctrl+S</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="439"/>
+        <source>E&amp;xit</source>
+        <translation>&amp;Beenden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="442"/>
+        <source>Ctrl+Q</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="447"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Hinzufügen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="452"/>
+        <source>&amp;Edit</source>
+        <translation>&amp;Bearbeiten</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="457"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Entfernen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="462"/>
+        <source>E&amp;nable All</source>
+        <translation>Alle &amp;aktivieren</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="467"/>
+        <source>&amp;Disable All</source>
+        <translation>Alle &amp;deaktivieren</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="472"/>
+        <source>Re&amp;move All</source>
+        <translation>Alle &amp;entfernen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="480"/>
+        <source>&amp;Contents...</source>
+        <translation>&amp;Inhalt...</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="488"/>
+        <source>&amp;Index...</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="493"/>
+        <source>&amp;About</source>
+        <translation>&amp;Über</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="501"/>
+        <source>&amp;Wi-Fi Protected Setup</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="506"/>
+        <source>&amp;Peers</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="53"/>
+        <source>Stop Service</source>
+        <translation>Dienst stoppen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="58"/>
+        <source>Start Service</source>
+        <translation>Dienst starten</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="67"/>
+        <source>Add Interface</source>
+        <translation>Schnittstelle hinzufügen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="167"/>
+        <source>connecting to wpa_supplicant</source>
+        <translation>Verbindungsaufbau zu wpa_supplicant</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="343"/>
+        <source>wpa_supplicant service is not running.
+Do you want to start it?</source>
+        <translation>wpa_supplicant ist nicht gestartet.
+Möchtest du ihn starten?</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="466"/>
+        <source>Disconnected</source>
+        <translation>Getrennt</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="468"/>
+        <source>Inactive</source>
+        <translation>Inaktiv</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="470"/>
+        <source>Scanning</source>
+        <translation>Scannen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="472"/>
+        <source>Authenticating</source>
+        <translation>Authentifizieren</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="474"/>
+        <source>Associating</source>
+        <translation>Assoziieren</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="476"/>
+        <source>Associated</source>
+        <translation>Assoziiert</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="478"/>
+        <source>4-Way Handshake</source>
+        <translation>4-Wege Handshake</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="480"/>
+        <source>Group Handshake</source>
+        <translation>Gruppen Handshake</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="482"/>
+        <source>Completed</source>
+        <translation>Abgeschlossen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="484"/>
+        <source>Unknown</source>
+        <translation>Unbekannt</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="497"/>
+        <source>Could not get status from wpa_supplicant</source>
+        <translation>Status konnte nicht von wpa_supplicant abgerufen werden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="512"/>
+        <source>No network interfaces in use.
+Would you like to add one?</source>
+        <translation>Es ist keine Netzwerkschnittstelle in verwendung.
+Möchtest du eine hinzufügen?</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="682"/>
+        <location filename="../wpagui.cpp" line="974"/>
+        <location filename="../wpagui.cpp" line="1039"/>
+        <location filename="../wpagui.cpp" line="1117"/>
+        <source>Select any network</source>
+        <translation>Wähle beliebiges Netzwerk</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="883"/>
+        <source>Disconnected from network.</source>
+        <translation>Getrennt vom Netzwerk.</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="886"/>
+        <source>Connection to network established.</source>
+        <translation>Verbindung zum Netzwerk wurde aufgebaut.</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="890"/>
+        <location filename="../wpagui.cpp" line="1523"/>
+        <source>WPS AP in active PBC mode found</source>
+        <translation>WPS AP im aktiven PBC Modus gefunden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="894"/>
+        <source>Press the PBC button on the screen to start registration</source>
+        <translation>Drücke den PBC Knopf auf dem Bildschirm um die Registrierung zu starten</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="897"/>
+        <source>WPS AP with recently selected registrar</source>
+        <translation>WPS AP mit kürzlich ausgewähltem Registrator</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="903"/>
+        <source>WPS AP detected</source>
+        <translation>WPS AP erkannt</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="905"/>
+        <source>PBC mode overlap detected</source>
+        <translation>PBC Modus Overlap erkannt</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="906"/>
+        <source>More than one AP is currently in active WPS PBC mode. Wait couple of minutes and try again</source>
+        <translation>Mehr als ein AP ist momentan im aktiven WPS PBC Modus. Versuch es in ein paar Minuten nochmal</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="911"/>
+        <source>Network configuration received</source>
+        <translation>Netzwerk Konfiguration empfangen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="915"/>
+        <source>Registration started</source>
+        <translation>Registrierung gestartet</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="917"/>
+        <source>Registrar does not yet know PIN</source>
+        <translation>Registrator kennt den PIN noch nicht</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="919"/>
+        <source>Registration failed</source>
+        <translation>Registrierung fehlgeschlagen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="921"/>
+        <source>Registration succeeded</source>
+        <translation>Registrierung erfolgreich</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1069"/>
+        <location filename="../wpagui.cpp" line="1138"/>
+        <source>No Networks</source>
+        <translation>Keine Netzwerke</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1070"/>
+        <source>There are no networks to edit.
+</source>
+        <translation>Keine Netzwerke zum bearbeiten.
+</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1081"/>
+        <location filename="../wpagui.cpp" line="1151"/>
+        <source>Select A Network</source>
+        <translation>Wähle ein Netzwerk</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1082"/>
+        <source>Select a network from the list to edit it.
+</source>
+        <translation>Wähle ein Netzwerk aus der Liste zum bearbeiten.
+</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1139"/>
+        <source>There are no networks to remove.
+</source>
+        <translation>Es sind keine Netzwerke zum entfernen vorhanden.
+</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1152"/>
+        <source>Select a network from the list to remove it.
+</source>
+        <translation>Wähle ein Netzwerk aus der Liste zum entfernen.
+</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1264"/>
+        <source>Failed to save configuration</source>
+        <translation>Speichern der Konfiguration fehlgeschlagen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1265"/>
+        <source>The configuration could not be saved.
+
+The update_config=1 configuration option
+must be used for configuration saving to
+be permitted.
+</source>
+        <translation>Die Konfiguration konnte nicht gespeichert werden.
+
+Die Einstellung update_config=1 muss gesetzt sein,
+damit Konfigurationen gespeichert werden können.
+</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1272"/>
+        <source>Saved configuration</source>
+        <translation>Konfiguration gespeichert</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1273"/>
+        <source>The current configuration was saved.
+</source>
+        <translation>Die aktuelle Konfiguration wurde gespeichert.
+</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1293"/>
+        <source> - wpa_supplicant user interface</source>
+        <translation> - wpa_supplicant Benutzerschnittstelle</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1307"/>
+        <source>&amp;Disconnect</source>
+        <translation>&amp;Trennen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1308"/>
+        <source>Re&amp;connect</source>
+        <translation>&amp;Wiederverbinden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1317"/>
+        <source>&amp;Event History</source>
+        <translation>&amp;Ereignis Historie</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1318"/>
+        <source>Scan &amp;Results</source>
+        <translation>Scan E&amp;rgebnisse</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1319"/>
+        <source>S&amp;tatus</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1328"/>
+        <source>&amp;Show Window</source>
+        <translation>&amp;Fenster anzeigen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1329"/>
+        <source>&amp;Hide Window</source>
+        <translation>&amp;Fenster ausblenden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1330"/>
+        <source>&amp;Quit</source>
+        <translation>&amp;Beenden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1462"/>
+        <source> will keep running in the system tray.</source>
+        <translation> wird weiterhin in der System Ablage laufen.</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1466"/>
+        <source> systray</source>
+        <translation>System Ablage</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1467"/>
+        <source>The program will keep running in the system tray.</source>
+        <translation>Das Programm wird weiterhin in der System Ablage laufen.</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1524"/>
+        <source>Press the push button on the AP to start the PBC mode.</source>
+        <translation>Drücke die Taste am AP um den PBC Modus zu starten.</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1527"/>
+        <source>If you have not yet done so, press the push button on the AP to start the PBC mode.</source>
+        <translation>Wenn Sie es noch nicht getan haben, so drücken Sie die Taste am AP um den PBC Modus zu starten.</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1531"/>
+        <location filename="../wpagui.cpp" line="1551"/>
+        <source>Waiting for Registrar</source>
+        <translation>Warte auf Registrator</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1548"/>
+        <source>Enter the generated PIN into the Registrar (either the internal one in the AP or an external one).</source>
+        <translation>Geben Sie den generierten PIN in der Registrierungsstelle ein (entweder der interne oder der externe im AP).</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1561"/>
+        <source>WPS AP selected from scan results</source>
+        <translation>WPS AP ausgewählt aus Scan Ergebnissen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1562"/>
+        <source>If you want to use an AP device PIN, e.g., from a label in the device, enter the eight digit AP PIN and click Use AP PIN button.</source>
+        <translation>Wenn Sie einen AP Geräte PIN verwenden möchten, z.B.: von einem Aufkleber am Gerät, geben Sie denn acht stelligen AP PIN ein und klicken Sie auf den AP PIN Knopf.</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1583"/>
+        <source>Waiting for AP/Enrollee</source>
+        <translation>Warte auf AP/Bewerber</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1591"/>
+        <source>Connected to the network</source>
+        <translation>Verbunden zum Netzwerk</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1592"/>
+        <source>Stopped</source>
+        <translation>Gestoppt</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1651"/>
+        <location filename="../wpagui.cpp" line="1679"/>
+        <source>OpenSCManager failed</source>
+        <translation>OpenSCManager fehlgeschlagen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1657"/>
+        <location filename="../wpagui.cpp" line="1685"/>
+        <source>OpenService failed</source>
+        <translation>OpenService fehlgeschlagen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1663"/>
+        <source>Failed to start wpa_supplicant service</source>
+        <translation>Starten des wpa_supplicant Dienstes fehlgeschlagen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1691"/>
+        <source>Failed to stop wpa_supplicant service</source>
+        <translation>Stoppen des wpa_supplicant Dienstes fehlgeschlagen</translation>
+    </message>
+    <message>
+        <source>OpenSCManager failed: %d
+</source>
+        <translation type="obsolete">OpenSCManager fehlgeschlagen: %d
+</translation>
+    </message>
+    <message>
+        <source>OpenService failed: %d
+
+</source>
+        <translation type="obsolete">OpenService fehlgeschlagen: %d
+
+</translation>
+    </message>
+</context>
+</TS>
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/main.cpp b/hostap/wpa_supplicant/wpa_gui-qt4/main.cpp
new file mode 100644
index 0000000..bbd45c6
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/main.cpp
@@ -0,0 +1,67 @@
+/*
+ * wpa_gui - Application startup
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <winsock.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+#include <QApplication>
+#include <QtCore/QLibraryInfo>
+#include <QtCore/QTranslator>
+#include "wpagui.h"
+
+WpaGuiApp::WpaGuiApp(int &argc, char **argv) :
+	QApplication(argc, argv),
+	argc(argc),
+	argv(argv)
+{
+	w = NULL;
+}
+
+#if !defined(QT_NO_SESSIONMANAGER) && QT_VERSION < 0x050000
+void WpaGuiApp::saveState(QSessionManager &manager)
+{
+	QApplication::saveState(manager);
+	w->saveState();
+}
+#endif
+
+
+int main(int argc, char *argv[])
+{
+	WpaGuiApp app(argc, argv);
+	QTranslator translator;
+	QString locale;
+	QString resourceDir;
+	int ret;
+
+	locale = QLocale::system().name();
+	resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
+	if (!translator.load("wpa_gui_" + locale, resourceDir))
+		translator.load("wpa_gui_" + locale, "lang");
+	app.installTranslator(&translator);
+
+	WpaGui w(&app);
+
+#ifdef CONFIG_NATIVE_WINDOWS
+	WSADATA wsaData;
+	if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
+		/* printf("Could not find a usable WinSock.dll\n"); */
+		return -1;
+	}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	app.w = &w;
+
+	ret = app.exec();
+
+#ifdef CONFIG_NATIVE_WINDOWS
+	WSACleanup();
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	return ret;
+}
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp b/hostap/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp
new file mode 100644
index 0000000..2727318
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp
@@ -0,0 +1,853 @@
+/*
+ * wpa_gui - NetworkConfig class
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <cstdio>
+#include <QMessageBox>
+
+#include "networkconfig.h"
+#include "wpagui.h"
+
+enum {
+	AUTH_NONE_OPEN,
+	AUTH_NONE_WEP,
+	AUTH_NONE_WEP_SHARED,
+	AUTH_IEEE8021X,
+	AUTH_WPA_PSK,
+	AUTH_WPA_EAP,
+	AUTH_WPA2_PSK,
+	AUTH_WPA2_EAP
+};
+
+#define WPA_GUI_KEY_DATA "[key is configured]"
+
+
+NetworkConfig::NetworkConfig(QWidget *parent, const char *, bool,
+			     Qt::WindowFlags)
+	: QDialog(parent)
+{
+	setupUi(this);
+
+	encrSelect->setEnabled(false);
+	connect(authSelect, SIGNAL(activated(int)), this,
+		SLOT(authChanged(int)));
+	connect(cancelButton, SIGNAL(clicked()), this, SLOT(close()));
+	connect(addButton, SIGNAL(clicked()), this, SLOT(addNetwork()));
+	connect(encrSelect, SIGNAL(activated(const QString &)), this,
+		SLOT(encrChanged(const QString &)));
+	connect(removeButton, SIGNAL(clicked()), this, SLOT(removeNetwork()));
+	connect(eapSelect, SIGNAL(activated(int)), this,
+		SLOT(eapChanged(int)));
+	connect(useWpsButton, SIGNAL(clicked()), this, SLOT(useWps()));
+
+	wpagui = NULL;
+	new_network = false;
+}
+
+
+NetworkConfig::~NetworkConfig()
+{
+}
+
+
+void NetworkConfig::languageChange()
+{
+	retranslateUi(this);
+}
+
+
+void NetworkConfig::paramsFromScanResults(QTreeWidgetItem *sel)
+{
+	new_network = true;
+
+	/* SSID BSSID frequency signal flags */
+	setWindowTitle(sel->text(0));
+	ssidEdit->setText(sel->text(0));
+
+	QString flags = sel->text(4);
+	int auth, encr = 0;
+	if (flags.indexOf("[WPA2-EAP") >= 0)
+		auth = AUTH_WPA2_EAP;
+	else if (flags.indexOf("[WPA-EAP") >= 0)
+		auth = AUTH_WPA_EAP;
+	else if (flags.indexOf("[WPA2-PSK") >= 0)
+		auth = AUTH_WPA2_PSK;
+	else if (flags.indexOf("[WPA-PSK") >= 0)
+		auth = AUTH_WPA_PSK;
+	else
+		auth = AUTH_NONE_OPEN;
+
+	if (flags.indexOf("-CCMP") >= 0)
+		encr = 1;
+	else if (flags.indexOf("-TKIP") >= 0)
+		encr = 0;
+	else if (flags.indexOf("WEP") >= 0) {
+		encr = 1;
+		if (auth == AUTH_NONE_OPEN)
+			auth = AUTH_NONE_WEP;
+	} else
+		encr = 0;
+
+	authSelect->setCurrentIndex(auth);
+	authChanged(auth);
+	encrSelect->setCurrentIndex(encr);
+
+	wepEnabled(auth == AUTH_NONE_WEP);
+
+	getEapCapa();
+
+	if (flags.indexOf("[WPS") >= 0)
+		useWpsButton->setEnabled(true);
+	bssid = sel->text(1);
+}
+
+
+void NetworkConfig::authChanged(int sel)
+{
+	encrSelect->setEnabled(sel != AUTH_NONE_OPEN && sel != AUTH_NONE_WEP &&
+			       sel != AUTH_NONE_WEP_SHARED);
+	pskEdit->setEnabled(sel == AUTH_WPA_PSK || sel == AUTH_WPA2_PSK);
+	bool eap = sel == AUTH_IEEE8021X || sel == AUTH_WPA_EAP ||
+		sel == AUTH_WPA2_EAP;
+	eapSelect->setEnabled(eap);
+	identityEdit->setEnabled(eap);
+	passwordEdit->setEnabled(eap);
+	cacertEdit->setEnabled(eap);
+	phase2Select->setEnabled(eap);
+	if (eap)
+		eapChanged(eapSelect->currentIndex());
+
+	while (encrSelect->count())
+		encrSelect->removeItem(0);
+
+	if (sel == AUTH_NONE_OPEN || sel == AUTH_NONE_WEP ||
+	    sel == AUTH_NONE_WEP_SHARED || sel == AUTH_IEEE8021X) {
+		encrSelect->addItem("None");
+		encrSelect->addItem("WEP");
+		encrSelect->setCurrentIndex(sel == AUTH_NONE_OPEN ? 0 : 1);
+	} else {
+		encrSelect->addItem("TKIP");
+		encrSelect->addItem("CCMP");
+		encrSelect->setCurrentIndex((sel == AUTH_WPA2_PSK ||
+					     sel == AUTH_WPA2_EAP) ? 1 : 0);
+	}
+
+	wepEnabled(sel == AUTH_NONE_WEP || sel == AUTH_NONE_WEP_SHARED);
+}
+
+
+void NetworkConfig::eapChanged(int sel)
+{
+	QString prev_val = phase2Select->currentText();
+	while (phase2Select->count())
+		phase2Select->removeItem(0);
+
+	QStringList inner;
+	inner << "PEAP" << "TTLS" << "FAST";
+	if (!inner.contains(eapSelect->itemText(sel)))
+		return;
+
+	phase2Select->addItem("[ any ]");
+
+	/* Add special cases based on outer method */
+	if (eapSelect->currentText().compare("TTLS") == 0) {
+		phase2Select->addItem("PAP");
+		phase2Select->addItem("CHAP");
+		phase2Select->addItem("MSCHAP");
+		phase2Select->addItem("MSCHAPv2");
+	} else if (eapSelect->currentText().compare("FAST") == 0)
+		phase2Select->addItem("GTC(auth) + MSCHAPv2(prov)");
+
+	/* Add all enabled EAP methods that can be used in the tunnel */
+	int i;
+	QStringList allowed;
+	allowed << "MSCHAPV2" << "MD5" << "GTC" << "TLS" << "OTP" << "SIM"
+		<< "AKA";
+	for (i = 0; i < eapSelect->count(); i++) {
+		if (allowed.contains(eapSelect->itemText(i))) {
+			phase2Select->addItem("EAP-" + eapSelect->itemText(i));
+		}
+	}
+
+	for (i = 0; i < phase2Select->count(); i++) {
+		if (phase2Select->itemText(i).compare(prev_val) == 0) {
+			phase2Select->setCurrentIndex(i);
+			break;
+		}
+	}
+}
+
+
+void NetworkConfig::addNetwork()
+{
+	char reply[10], cmd[256];
+	size_t reply_len;
+	int id;
+	int psklen = pskEdit->text().length();
+	int auth = authSelect->currentIndex();
+
+	if (auth == AUTH_WPA_PSK || auth == AUTH_WPA2_PSK) {
+		if (psklen < 8 || psklen > 64) {
+			QMessageBox::warning(
+				this,
+				tr("WPA Pre-Shared Key Error"),
+				tr("WPA-PSK requires a passphrase of 8 to 63 "
+				   "characters\n"
+				   "or 64 hex digit PSK"));
+			pskEdit->setFocus();
+			return;
+		}
+	}
+
+	if (idstrEdit->isEnabled() && !idstrEdit->text().isEmpty()) {
+		QRegExp rx("^(\\w|-)+$");
+		if (rx.indexIn(idstrEdit->text()) < 0) {
+			QMessageBox::warning(
+				this, tr("Network ID Error"),
+				tr("Network ID String contains non-word "
+				   "characters.\n"
+				   "It must be a simple string, "
+				   "without spaces, containing\n"
+				   "only characters in this range: "
+				   "[A-Za-z0-9_-]\n"));
+			idstrEdit->setFocus();
+			return;
+		}
+	}
+
+	if (wpagui == NULL)
+		return;
+
+	memset(reply, 0, sizeof(reply));
+	reply_len = sizeof(reply) - 1;
+
+	if (new_network) {
+		wpagui->ctrlRequest("ADD_NETWORK", reply, &reply_len);
+		if (reply[0] == 'F') {
+			QMessageBox::warning(this, "wpa_gui",
+					     tr("Failed to add "
+						"network to wpa_supplicant\n"
+						"configuration."));
+			return;
+		}
+		id = atoi(reply);
+	} else
+		id = edit_network_id;
+
+	setNetworkParam(id, "ssid", ssidEdit->text().toLocal8Bit().constData(),
+			true);
+
+	const char *key_mgmt = NULL, *proto = NULL, *pairwise = NULL;
+	switch (auth) {
+	case AUTH_NONE_OPEN:
+	case AUTH_NONE_WEP:
+	case AUTH_NONE_WEP_SHARED:
+		key_mgmt = "NONE";
+		break;
+	case AUTH_IEEE8021X:
+		key_mgmt = "IEEE8021X";
+		break;
+	case AUTH_WPA_PSK:
+		key_mgmt = "WPA-PSK";
+		proto = "WPA";
+		break;
+	case AUTH_WPA_EAP:
+		key_mgmt = "WPA-EAP";
+		proto = "WPA";
+		break;
+	case AUTH_WPA2_PSK:
+		key_mgmt = "WPA-PSK";
+		proto = "WPA2";
+		break;
+	case AUTH_WPA2_EAP:
+		key_mgmt = "WPA-EAP";
+		proto = "WPA2";
+		break;
+	}
+
+	if (auth == AUTH_NONE_WEP_SHARED)
+		setNetworkParam(id, "auth_alg", "SHARED", false);
+	else
+		setNetworkParam(id, "auth_alg", "OPEN", false);
+
+	if (auth == AUTH_WPA_PSK || auth == AUTH_WPA_EAP ||
+	    auth == AUTH_WPA2_PSK || auth == AUTH_WPA2_EAP) {
+		int encr = encrSelect->currentIndex();
+		if (encr == 0)
+			pairwise = "TKIP";
+		else
+			pairwise = "CCMP";
+	}
+
+	if (proto)
+		setNetworkParam(id, "proto", proto, false);
+	if (key_mgmt)
+		setNetworkParam(id, "key_mgmt", key_mgmt, false);
+	if (pairwise) {
+		setNetworkParam(id, "pairwise", pairwise, false);
+		setNetworkParam(id, "group", "TKIP CCMP WEP104 WEP40", false);
+	}
+	if (pskEdit->isEnabled() &&
+	    strcmp(pskEdit->text().toLocal8Bit().constData(),
+		   WPA_GUI_KEY_DATA) != 0)
+		setNetworkParam(id, "psk",
+				pskEdit->text().toLocal8Bit().constData(),
+				psklen != 64);
+	if (eapSelect->isEnabled()) {
+		const char *eap =
+			eapSelect->currentText().toLocal8Bit().constData();
+		setNetworkParam(id, "eap", eap, false);
+		if (strcmp(eap, "SIM") == 0 || strcmp(eap, "AKA") == 0)
+			setNetworkParam(id, "pcsc", "", true);
+		else
+			setNetworkParam(id, "pcsc", "NULL", false);
+	}
+	if (phase2Select->isEnabled()) {
+		QString eap = eapSelect->currentText();
+		QString inner = phase2Select->currentText();
+		char phase2[32];
+		phase2[0] = '\0';
+		if (eap.compare("PEAP") == 0) {
+			if (inner.startsWith("EAP-"))
+				snprintf(phase2, sizeof(phase2), "auth=%s",
+					 inner.right(inner.size() - 4).
+					 toLocal8Bit().constData());
+		} else if (eap.compare("TTLS") == 0) {
+			if (inner.startsWith("EAP-"))
+				snprintf(phase2, sizeof(phase2), "autheap=%s",
+					 inner.right(inner.size() - 4).
+					 toLocal8Bit().constData());
+			else
+				snprintf(phase2, sizeof(phase2), "auth=%s",
+					 inner.toLocal8Bit().constData());
+		} else if (eap.compare("FAST") == 0) {
+			const char *provisioning = NULL;
+			if (inner.startsWith("EAP-")) {
+				snprintf(phase2, sizeof(phase2), "auth=%s",
+					 inner.right(inner.size() - 4).
+					 toLocal8Bit().constData());
+				provisioning = "fast_provisioning=2";
+			} else if (inner.compare("GTC(auth) + MSCHAPv2(prov)")
+				   == 0) {
+				snprintf(phase2, sizeof(phase2),
+					 "auth=GTC auth=MSCHAPV2");
+				provisioning = "fast_provisioning=1";
+			} else
+				provisioning = "fast_provisioning=3";
+			if (provisioning) {
+				char blob[32];
+				setNetworkParam(id, "phase1", provisioning,
+						true);
+				snprintf(blob, sizeof(blob),
+					 "blob://fast-pac-%d", id);
+				setNetworkParam(id, "pac_file", blob, true);
+			}
+		}
+		if (phase2[0])
+			setNetworkParam(id, "phase2", phase2, true);
+		else
+			setNetworkParam(id, "phase2", "NULL", false);
+	} else
+		setNetworkParam(id, "phase2", "NULL", false);
+	if (identityEdit->isEnabled() && identityEdit->text().length() > 0)
+		setNetworkParam(id, "identity",
+				identityEdit->text().toLocal8Bit().constData(),
+				true);
+	else
+		setNetworkParam(id, "identity", "NULL", false);
+	if (passwordEdit->isEnabled() && passwordEdit->text().length() > 0 &&
+	    strcmp(passwordEdit->text().toLocal8Bit().constData(),
+		   WPA_GUI_KEY_DATA) != 0)
+		setNetworkParam(id, "password",
+				passwordEdit->text().toLocal8Bit().constData(),
+				true);
+	else if (passwordEdit->text().length() == 0)
+		setNetworkParam(id, "password", "NULL", false);
+	if (cacertEdit->isEnabled() && cacertEdit->text().length() > 0)
+		setNetworkParam(id, "ca_cert",
+				cacertEdit->text().toLocal8Bit().constData(),
+				true);
+	else
+		setNetworkParam(id, "ca_cert", "NULL", false);
+	writeWepKey(id, wep0Edit, 0);
+	writeWepKey(id, wep1Edit, 1);
+	writeWepKey(id, wep2Edit, 2);
+	writeWepKey(id, wep3Edit, 3);
+
+	if (wep0Radio->isEnabled() && wep0Radio->isChecked())
+		setNetworkParam(id, "wep_tx_keyidx", "0", false);
+	else if (wep1Radio->isEnabled() && wep1Radio->isChecked())
+		setNetworkParam(id, "wep_tx_keyidx", "1", false);
+	else if (wep2Radio->isEnabled() && wep2Radio->isChecked())
+		setNetworkParam(id, "wep_tx_keyidx", "2", false);
+	else if (wep3Radio->isEnabled() && wep3Radio->isChecked())
+		setNetworkParam(id, "wep_tx_keyidx", "3", false);
+
+	if (idstrEdit->isEnabled() && idstrEdit->text().length() > 0)
+		setNetworkParam(id, "id_str",
+				idstrEdit->text().toLocal8Bit().constData(),
+				true);
+	else
+		setNetworkParam(id, "id_str", "NULL", false);
+
+	if (prioritySpinBox->isEnabled()) {
+		QString prio;
+		prio = prio.setNum(prioritySpinBox->value());
+		setNetworkParam(id, "priority", prio.toLocal8Bit().constData(),
+				false);
+	}
+
+	snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %d", id);
+	reply_len = sizeof(reply);
+	wpagui->ctrlRequest(cmd, reply, &reply_len);
+	if (strncmp(reply, "OK", 2) != 0) {
+		QMessageBox::warning(this, "wpa_gui",
+				     tr("Failed to enable "
+					"network in wpa_supplicant\n"
+					"configuration."));
+		/* Network was added, so continue anyway */
+	}
+	wpagui->triggerUpdate();
+	wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len);
+
+	close();
+}
+
+
+void NetworkConfig::setWpaGui(WpaGui *_wpagui)
+{
+	wpagui = _wpagui;
+}
+
+
+int NetworkConfig::setNetworkParam(int id, const char *field,
+				   const char *value, bool quote)
+{
+	char reply[10], cmd[256];
+	size_t reply_len;
+	snprintf(cmd, sizeof(cmd), "SET_NETWORK %d %s %s%s%s",
+		 id, field, quote ? "\"" : "", value, quote ? "\"" : "");
+	reply_len = sizeof(reply);
+	wpagui->ctrlRequest(cmd, reply, &reply_len);
+	return strncmp(reply, "OK", 2) == 0 ? 0 : -1;
+}
+
+
+void NetworkConfig::encrChanged(const QString &)
+{
+}
+
+
+void NetworkConfig::wepEnabled(bool enabled)
+{
+	wep0Edit->setEnabled(enabled);
+	wep1Edit->setEnabled(enabled);
+	wep2Edit->setEnabled(enabled);
+	wep3Edit->setEnabled(enabled);
+	wep0Radio->setEnabled(enabled);
+	wep1Radio->setEnabled(enabled);
+	wep2Radio->setEnabled(enabled);
+	wep3Radio->setEnabled(enabled);
+}
+
+
+void NetworkConfig::writeWepKey(int network_id, QLineEdit *edit, int id)
+{
+	char buf[10];
+	bool hex;
+	const char *txt, *pos;
+	size_t len;
+
+	if (!edit->isEnabled() || edit->text().isEmpty())
+		return;
+
+	/*
+	 * Assume hex key if only hex characters are present and length matches
+	 * with 40, 104, or 128-bit key
+	 */
+	txt = edit->text().toLocal8Bit().constData();
+	if (strcmp(txt, WPA_GUI_KEY_DATA) == 0)
+		return;
+	len = strlen(txt);
+	if (len == 0)
+		return;
+	pos = txt;
+	hex = true;
+	while (*pos) {
+		if (!((*pos >= '0' && *pos <= '9') ||
+		      (*pos >= 'a' && *pos <= 'f') ||
+		      (*pos >= 'A' && *pos <= 'F'))) {
+			hex = false;
+			break;
+		}
+		pos++;
+	}
+	if (hex && len != 10 && len != 26 && len != 32)
+		hex = false;
+	snprintf(buf, sizeof(buf), "wep_key%d", id);
+	setNetworkParam(network_id, buf, txt, !hex);
+}
+
+
+static int key_value_isset(const char *reply, size_t reply_len)
+{
+    return reply_len > 0 && (reply_len < 4 || memcmp(reply, "FAIL", 4) != 0);
+}
+
+
+void NetworkConfig::paramsFromConfig(int network_id)
+{
+	int i, res;
+
+	edit_network_id = network_id;
+	getEapCapa();
+
+	char reply[1024], cmd[256], *pos;
+	size_t reply_len;
+
+	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", network_id);
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 &&
+	    reply_len >= 2 && reply[0] == '"') {
+		reply[reply_len] = '\0';
+		pos = strchr(reply + 1, '"');
+		if (pos)
+			*pos = '\0';
+		ssidEdit->setText(reply + 1);
+	}
+
+	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d proto", network_id);
+	reply_len = sizeof(reply) - 1;
+	int wpa = 0;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) {
+		reply[reply_len] = '\0';
+		if (strstr(reply, "RSN") || strstr(reply, "WPA2"))
+			wpa = 2;
+		else if (strstr(reply, "WPA"))
+			wpa = 1;
+	}
+
+	int auth = AUTH_NONE_OPEN, encr = 0;
+	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d key_mgmt", network_id);
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) {
+		reply[reply_len] = '\0';
+		if (strstr(reply, "WPA-EAP"))
+			auth = wpa & 2 ? AUTH_WPA2_EAP : AUTH_WPA_EAP;
+		else if (strstr(reply, "WPA-PSK"))
+			auth = wpa & 2 ? AUTH_WPA2_PSK : AUTH_WPA_PSK;
+		else if (strstr(reply, "IEEE8021X")) {
+			auth = AUTH_IEEE8021X;
+			encr = 1;
+		}
+	}
+
+	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d pairwise", network_id);
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) {
+		reply[reply_len] = '\0';
+		if (strstr(reply, "CCMP") && auth != AUTH_NONE_OPEN &&
+		    auth != AUTH_NONE_WEP && auth != AUTH_NONE_WEP_SHARED)
+			encr = 1;
+		else if (strstr(reply, "TKIP"))
+			encr = 0;
+		else if (strstr(reply, "WEP"))
+			encr = 1;
+		else
+			encr = 0;
+	}
+
+	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d psk", network_id);
+	reply_len = sizeof(reply) - 1;
+	res = wpagui->ctrlRequest(cmd, reply, &reply_len);
+	if (res >= 0 && reply_len >= 2 && reply[0] == '"') {
+		reply[reply_len] = '\0';
+		pos = strchr(reply + 1, '"');
+		if (pos)
+			*pos = '\0';
+		pskEdit->setText(reply + 1);
+	} else if (res >= 0 && key_value_isset(reply, reply_len)) {
+		pskEdit->setText(WPA_GUI_KEY_DATA);
+	}
+
+	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d identity", network_id);
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 &&
+	    reply_len >= 2 && reply[0] == '"') {
+		reply[reply_len] = '\0';
+		pos = strchr(reply + 1, '"');
+		if (pos)
+			*pos = '\0';
+		identityEdit->setText(reply + 1);
+	}
+
+	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d password", network_id);
+	reply_len = sizeof(reply) - 1;
+	res = wpagui->ctrlRequest(cmd, reply, &reply_len);
+	if (res >= 0 && reply_len >= 2 && reply[0] == '"') {
+		reply[reply_len] = '\0';
+		pos = strchr(reply + 1, '"');
+		if (pos)
+			*pos = '\0';
+		passwordEdit->setText(reply + 1);
+	} else if (res >= 0 && key_value_isset(reply, reply_len)) {
+		passwordEdit->setText(WPA_GUI_KEY_DATA);
+	}
+
+	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ca_cert", network_id);
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 &&
+	    reply_len >= 2 && reply[0] == '"') {
+		reply[reply_len] = '\0';
+		pos = strchr(reply + 1, '"');
+		if (pos)
+			*pos = '\0';
+		cacertEdit->setText(reply + 1);
+	}
+
+	enum { NO_INNER, PEAP_INNER, TTLS_INNER, FAST_INNER } eap = NO_INNER;
+	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d eap", network_id);
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 &&
+	    reply_len >= 1) {
+		reply[reply_len] = '\0';
+		for (i = 0; i < eapSelect->count(); i++) {
+			if (eapSelect->itemText(i).compare(reply) == 0) {
+				eapSelect->setCurrentIndex(i);
+				if (strcmp(reply, "PEAP") == 0)
+					eap = PEAP_INNER;
+				else if (strcmp(reply, "TTLS") == 0)
+					eap = TTLS_INNER;
+				else if (strcmp(reply, "FAST") == 0)
+					eap = FAST_INNER;
+				break;
+			}
+		}
+	}
+
+	if (eap != NO_INNER) {
+		snprintf(cmd, sizeof(cmd), "GET_NETWORK %d phase2",
+			 network_id);
+		reply_len = sizeof(reply) - 1;
+		if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 &&
+		    reply_len >= 1) {
+			reply[reply_len] = '\0';
+			eapChanged(eapSelect->currentIndex());
+		} else
+			eap = NO_INNER;
+	}
+
+	char *val;
+	val = reply + 1;
+	while (*(val + 1))
+		val++;
+	if (*val == '"')
+		*val = '\0';
+
+	switch (eap) {
+	case PEAP_INNER:
+		if (strncmp(reply, "\"auth=", 6))
+			break;
+		val = reply + 2;
+		memcpy(val, "EAP-", 4);
+		break;
+	case TTLS_INNER:
+		if (strncmp(reply, "\"autheap=", 9) == 0) {
+			val = reply + 5;
+			memcpy(val, "EAP-", 4);
+		} else if (strncmp(reply, "\"auth=", 6) == 0)
+			val = reply + 6;
+		break;
+	case FAST_INNER:
+		if (strncmp(reply, "\"auth=", 6))
+			break;
+		if (strcmp(reply + 6, "GTC auth=MSCHAPV2") == 0) {
+			val = (char *) "GTC(auth) + MSCHAPv2(prov)";
+			break;
+		}
+		val = reply + 2;
+		memcpy(val, "EAP-", 4);
+		break;
+	case NO_INNER:
+		break;
+	}
+
+	for (i = 0; i < phase2Select->count(); i++) {
+		if (phase2Select->itemText(i).compare(val) == 0) {
+			phase2Select->setCurrentIndex(i);
+			break;
+		}
+	}
+
+	for (i = 0; i < 4; i++) {
+		QLineEdit *wepEdit;
+		switch (i) {
+		default:
+		case 0:
+			wepEdit = wep0Edit;
+			break;
+		case 1:
+			wepEdit = wep1Edit;
+			break;
+		case 2:
+			wepEdit = wep2Edit;
+			break;
+		case 3:
+			wepEdit = wep3Edit;
+			break;
+		}
+		snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_key%d",
+			 network_id, i);
+		reply_len = sizeof(reply) - 1;
+		res = wpagui->ctrlRequest(cmd, reply, &reply_len);
+		if (res >= 0 && reply_len >= 2 && reply[0] == '"') {
+			reply[reply_len] = '\0';
+			pos = strchr(reply + 1, '"');
+			if (pos)
+				*pos = '\0';
+			if (auth == AUTH_NONE_OPEN || auth == AUTH_IEEE8021X) {
+				if (auth == AUTH_NONE_OPEN)
+					auth = AUTH_NONE_WEP;
+				encr = 1;
+			}
+
+			wepEdit->setText(reply + 1);
+		} else if (res >= 0 && key_value_isset(reply, reply_len)) {
+			if (auth == AUTH_NONE_OPEN || auth == AUTH_IEEE8021X) {
+				if (auth == AUTH_NONE_OPEN)
+					auth = AUTH_NONE_WEP;
+				encr = 1;
+			}
+			wepEdit->setText(WPA_GUI_KEY_DATA);
+		}
+	}
+
+	if (auth == AUTH_NONE_WEP) {
+		snprintf(cmd, sizeof(cmd), "GET_NETWORK %d auth_alg",
+			 network_id);
+		reply_len = sizeof(reply) - 1;
+		if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) {
+			reply[reply_len] = '\0';
+			if (strcmp(reply, "SHARED") == 0)
+				auth = AUTH_NONE_WEP_SHARED;
+		}
+	}
+
+	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_tx_keyidx", network_id);
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1)
+	{
+		reply[reply_len] = '\0';
+		switch (atoi(reply)) {
+		case 0:
+			wep0Radio->setChecked(true);
+			break;
+		case 1:
+			wep1Radio->setChecked(true);
+			break;
+		case 2:
+			wep2Radio->setChecked(true);
+			break;
+		case 3:
+			wep3Radio->setChecked(true);
+			break;
+		}
+	}
+
+	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d id_str", network_id);
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 &&
+	    reply_len >= 2 && reply[0] == '"') {
+		reply[reply_len] = '\0';
+		pos = strchr(reply + 1, '"');
+		if (pos)
+			*pos = '\0';
+		idstrEdit->setText(reply + 1);
+	}
+
+	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d priority", network_id);
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1)
+	{
+		reply[reply_len] = '\0';
+		prioritySpinBox->setValue(atoi(reply));
+	}
+
+	authSelect->setCurrentIndex(auth);
+	authChanged(auth);
+	encrSelect->setCurrentIndex(encr);
+	wepEnabled(auth == AUTH_NONE_WEP || auth == AUTH_NONE_WEP_SHARED);
+
+	removeButton->setEnabled(true);
+	addButton->setText("Save");
+}
+
+
+void NetworkConfig::removeNetwork()
+{
+	char reply[10], cmd[256];
+	size_t reply_len;
+
+	if (QMessageBox::information(
+		    this, "wpa_gui",
+		    tr("This will permanently remove the network\n"
+		       "from the configuration. Do you really want\n"
+		       "to remove this network?"),
+		    tr("Yes"), tr("No")) != 0)
+		return;
+
+	snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %d", edit_network_id);
+	reply_len = sizeof(reply);
+	wpagui->ctrlRequest(cmd, reply, &reply_len);
+	if (strncmp(reply, "OK", 2) != 0) {
+		QMessageBox::warning(this, "wpa_gui",
+				     tr("Failed to remove network from "
+					"wpa_supplicant\n"
+					"configuration."));
+	} else {
+		wpagui->triggerUpdate();
+		wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len);
+	}
+
+	close();
+}
+
+
+void NetworkConfig::newNetwork()
+{
+	new_network = true;
+	getEapCapa();
+}
+
+
+void NetworkConfig::getEapCapa()
+{
+	char reply[256];
+	size_t reply_len;
+
+	if (wpagui == NULL)
+		return;
+
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest("GET_CAPABILITY eap", reply, &reply_len) < 0)
+		return;
+	reply[reply_len] = '\0';
+
+	QString res(reply);
+	QStringList types = res.split(QChar(' '));
+	eapSelect->insertItems(-1, types);
+}
+
+
+void NetworkConfig::useWps()
+{
+	if (wpagui == NULL)
+		return;
+	wpagui->setBssFromScan(bssid);
+	wpagui->wpsDialog();
+	close();
+}
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/networkconfig.h b/hostap/wpa_supplicant/wpa_gui-qt4/networkconfig.h
new file mode 100644
index 0000000..fd09dec
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/networkconfig.h
@@ -0,0 +1,55 @@
+/*
+ * wpa_gui - NetworkConfig class
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NETWORKCONFIG_H
+#define NETWORKCONFIG_H
+
+#include <QObject>
+#include "ui_networkconfig.h"
+
+class WpaGui;
+
+class NetworkConfig : public QDialog, public Ui::NetworkConfig
+{
+	Q_OBJECT
+
+public:
+	NetworkConfig(QWidget *parent = 0, const char *name = 0,
+		      bool modal = false, Qt::WindowFlags fl = 0);
+	~NetworkConfig();
+
+	virtual void paramsFromScanResults(QTreeWidgetItem *sel);
+	virtual void setWpaGui(WpaGui *_wpagui);
+	virtual int setNetworkParam(int id, const char *field,
+				    const char *value, bool quote);
+	virtual void paramsFromConfig(int network_id);
+	virtual void newNetwork();
+
+public slots:
+	virtual void authChanged(int sel);
+	virtual void addNetwork();
+	virtual void encrChanged(const QString &sel);
+	virtual void writeWepKey(int network_id, QLineEdit *edit, int id);
+	virtual void removeNetwork();
+	virtual void eapChanged(int sel);
+	virtual void useWps();
+
+protected slots:
+	virtual void languageChange();
+
+private:
+	WpaGui *wpagui;
+	int edit_network_id;
+	bool new_network;
+	QString bssid;
+
+	virtual void wepEnabled(bool enabled);
+	virtual void getEapCapa();
+};
+
+#endif /* NETWORKCONFIG_H */
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/networkconfig.ui b/hostap/wpa_supplicant/wpa_gui-qt4/networkconfig.ui
new file mode 100644
index 0000000..217a8ff
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/networkconfig.ui
@@ -0,0 +1,435 @@
+<ui version="4.0" >
+ <class>NetworkConfig</class>
+ <widget class="QDialog" name="NetworkConfig" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>410</width>
+    <height>534</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>NetworkConfig</string>
+  </property>
+  <layout class="QGridLayout" >
+   <item row="1" column="3" >
+    <widget class="QPushButton" name="cancelButton" >
+     <property name="text" >
+      <string>Cancel</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="0" colspan="4" >
+    <widget class="QFrame" name="frame9" >
+     <property name="frameShape" >
+      <enum>QFrame::NoFrame</enum>
+     </property>
+     <property name="frameShadow" >
+      <enum>QFrame::Plain</enum>
+     </property>
+     <layout class="QGridLayout" >
+      <item row="0" column="0" >
+       <widget class="QLabel" name="ssidLabel" >
+        <property name="text" >
+         <string>SSID</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1" >
+       <widget class="QLineEdit" name="ssidEdit" >
+        <property name="toolTip" >
+         <string>Network name (Service Set IDentifier)</string>
+        </property>
+        <property name="text" >
+         <string/>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0" >
+       <widget class="QLabel" name="authLabel" >
+        <property name="text" >
+         <string>Authentication</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1" >
+       <widget class="QComboBox" name="authSelect" >
+        <item>
+         <property name="text" >
+          <string>Plaintext (open / no authentication)</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>Static WEP (no authentication)</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>Static WEP (Shared Key authentication)</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>IEEE 802.1X</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>WPA-Personal (PSK)</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>WPA-Enterprise (EAP)</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>WPA2-Personal (PSK)</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>WPA2-Enterprise (EAP)</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+      <item row="2" column="0" >
+       <widget class="QLabel" name="encrLabel" >
+        <property name="text" >
+         <string>Encryption</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1" >
+       <widget class="QComboBox" name="encrSelect" >
+        <item>
+         <property name="text" >
+          <string>None</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>WEP</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>TKIP</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>CCMP</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+      <item row="3" column="0" >
+       <widget class="QLabel" name="pskLabel" >
+        <property name="text" >
+         <string>PSK</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1" >
+       <widget class="QLineEdit" name="pskEdit" >
+        <property name="enabled" >
+         <bool>false</bool>
+        </property>
+        <property name="toolTip" >
+         <string>WPA/WPA2 pre-shared key or passphrase</string>
+        </property>
+        <property name="whatsThis" >
+         <string/>
+        </property>
+        <property name="echoMode" >
+         <enum>QLineEdit::Password</enum>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="0" >
+       <widget class="QLabel" name="eapLabel" >
+        <property name="text" >
+         <string>EAP method</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="1" >
+       <widget class="QComboBox" name="eapSelect" >
+        <property name="enabled" >
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="0" >
+       <widget class="QLabel" name="identityLabel" >
+        <property name="text" >
+         <string>Identity</string>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="1" >
+       <widget class="QLineEdit" name="identityEdit" >
+        <property name="enabled" >
+         <bool>false</bool>
+        </property>
+        <property name="toolTip" >
+         <string>Username/Identity for EAP methods</string>
+        </property>
+       </widget>
+      </item>
+      <item row="6" column="0" >
+       <widget class="QLabel" name="passwordLabel" >
+        <property name="text" >
+         <string>Password</string>
+        </property>
+       </widget>
+      </item>
+      <item row="6" column="1" >
+       <widget class="QLineEdit" name="passwordEdit" >
+        <property name="enabled" >
+         <bool>false</bool>
+        </property>
+        <property name="toolTip" >
+         <string>Password for EAP methods</string>
+        </property>
+        <property name="echoMode" >
+         <enum>QLineEdit::Password</enum>
+        </property>
+       </widget>
+      </item>
+      <item row="7" column="0" >
+       <widget class="QLabel" name="cacertLabel" >
+        <property name="text" >
+         <string>CA certificate</string>
+        </property>
+       </widget>
+      </item>
+      <item row="7" column="1" >
+       <widget class="QLineEdit" name="cacertEdit" >
+        <property name="enabled" >
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="8" column="0" colspan="2" >
+       <widget class="QGroupBox" name="wepBox" >
+        <property name="enabled" >
+         <bool>true</bool>
+        </property>
+        <property name="title" >
+         <string>WEP keys</string>
+        </property>
+        <layout class="QGridLayout" >
+         <item row="0" column="0" >
+          <widget class="QRadioButton" name="wep0Radio" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+           <property name="text" >
+            <string>key 0</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0" >
+          <widget class="QRadioButton" name="wep1Radio" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+           <property name="text" >
+            <string>key 1</string>
+           </property>
+          </widget>
+         </item>
+         <item row="3" column="0" >
+          <widget class="QRadioButton" name="wep3Radio" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+           <property name="text" >
+            <string>key 3</string>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="0" >
+          <widget class="QRadioButton" name="wep2Radio" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+           <property name="text" >
+            <string>key 2</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1" >
+          <widget class="QLineEdit" name="wep0Edit" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="1" >
+          <widget class="QLineEdit" name="wep1Edit" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="1" >
+          <widget class="QLineEdit" name="wep2Edit" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="3" column="1" >
+          <widget class="QLineEdit" name="wep3Edit" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </item>
+      <item row="9" column="0" colspan="2" >
+       <widget class="QGroupBox" name="optionalSettingsBox" >
+        <property name="enabled" >
+         <bool>true</bool>
+        </property>
+        <property name="title" >
+         <string>Optional Settings</string>
+        </property>
+        <layout class="QGridLayout" >
+         <item row="0" column="1" >
+          <widget class="QLineEdit" name="idstrEdit" >
+           <property name="toolTip" >
+            <string>Network Identification String</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="3" >
+          <widget class="QSpinBox" name="prioritySpinBox" >
+           <property name="toolTip" >
+            <string>Network Priority</string>
+           </property>
+           <property name="maximum" >
+            <number>10000</number>
+           </property>
+           <property name="singleStep" >
+            <number>10</number>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="0" >
+          <widget class="QLabel" name="idstrLabel" >
+           <property name="text" >
+            <string>IDString</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="2" >
+          <widget class="QLabel" name="priorityLabel" >
+           <property name="text" >
+            <string>Priority</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0" >
+          <widget class="QLabel" name="phase2Label" >
+           <property name="text" >
+            <string>Inner auth</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="1" >
+          <widget class="QComboBox" name="phase2Select" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="1" column="2" >
+    <widget class="QPushButton" name="addButton" >
+     <property name="text" >
+      <string>Add</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="3" >
+    <widget class="QPushButton" name="removeButton" >
+     <property name="enabled" >
+      <bool>false</bool>
+     </property>
+     <property name="text" >
+      <string>Remove</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0" >
+    <spacer>
+     <property name="orientation" >
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" >
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="1" column="1" >
+    <widget class="QPushButton" name="useWpsButton" >
+     <property name="enabled" >
+      <bool>false</bool>
+     </property>
+     <property name="text" >
+      <string>WPS</string>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11" />
+ <pixmapfunction></pixmapfunction>
+ <tabstops>
+  <tabstop>ssidEdit</tabstop>
+  <tabstop>authSelect</tabstop>
+  <tabstop>encrSelect</tabstop>
+  <tabstop>pskEdit</tabstop>
+  <tabstop>eapSelect</tabstop>
+  <tabstop>identityEdit</tabstop>
+  <tabstop>passwordEdit</tabstop>
+  <tabstop>cacertEdit</tabstop>
+  <tabstop>wep0Radio</tabstop>
+  <tabstop>wep0Edit</tabstop>
+  <tabstop>wep1Radio</tabstop>
+  <tabstop>wep1Edit</tabstop>
+  <tabstop>wep2Radio</tabstop>
+  <tabstop>wep2Edit</tabstop>
+  <tabstop>wep3Radio</tabstop>
+  <tabstop>wep3Edit</tabstop>
+  <tabstop>idstrEdit</tabstop>
+  <tabstop>prioritySpinBox</tabstop>
+  <tabstop>phase2Select</tabstop>
+  <tabstop>addButton</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>cancelButton</tabstop>
+ </tabstops>
+ <includes>
+  <include location="global" >qtreewidget.h</include>
+ </includes>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/peers.cpp b/hostap/wpa_supplicant/wpa_gui-qt4/peers.cpp
new file mode 100644
index 0000000..3bcf2f5
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/peers.cpp
@@ -0,0 +1,1883 @@
+/*
+ * wpa_gui - Peers class
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <cstdio>
+#include <QImageReader>
+#include <QMessageBox>
+
+#include "common/wpa_ctrl.h"
+#include "wpagui.h"
+#include "stringquery.h"
+#include "peers.h"
+
+
+enum {
+	peer_role_address = Qt::UserRole + 1,
+	peer_role_type,
+	peer_role_uuid,
+	peer_role_details,
+	peer_role_ifname,
+	peer_role_pri_dev_type,
+	peer_role_ssid,
+	peer_role_config_methods,
+	peer_role_dev_passwd_id,
+	peer_role_bss_id,
+	peer_role_selected_method,
+	peer_role_selected_pin,
+	peer_role_requested_method,
+	peer_role_network_id
+};
+
+enum selected_method {
+	SEL_METHOD_NONE,
+	SEL_METHOD_PIN_PEER_DISPLAY,
+	SEL_METHOD_PIN_LOCAL_DISPLAY
+};
+
+/*
+ * TODO:
+ * - add current AP info (e.g., from WPS) in station mode
+ */
+
+enum peer_type {
+	PEER_TYPE_ASSOCIATED_STATION,
+	PEER_TYPE_AP,
+	PEER_TYPE_AP_WPS,
+	PEER_TYPE_WPS_PIN_NEEDED,
+	PEER_TYPE_P2P,
+	PEER_TYPE_P2P_CLIENT,
+	PEER_TYPE_P2P_GROUP,
+	PEER_TYPE_P2P_PERSISTENT_GROUP_GO,
+	PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT,
+	PEER_TYPE_P2P_INVITATION,
+	PEER_TYPE_WPS_ER_AP,
+	PEER_TYPE_WPS_ER_AP_UNCONFIGURED,
+	PEER_TYPE_WPS_ER_ENROLLEE,
+	PEER_TYPE_WPS_ENROLLEE
+};
+
+
+Peers::Peers(QWidget *parent, const char *, bool, Qt::WindowFlags)
+	: QDialog(parent)
+{
+	setupUi(this);
+
+	if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
+	{
+		default_icon = new QIcon(":/icons/wpa_gui.svg");
+		ap_icon = new QIcon(":/icons/ap.svg");
+		laptop_icon = new QIcon(":/icons/laptop.svg");
+		group_icon = new QIcon(":/icons/group.svg");
+		invitation_icon = new QIcon(":/icons/invitation.svg");
+	} else {
+		default_icon = new QIcon(":/icons/wpa_gui.png");
+		ap_icon = new QIcon(":/icons/ap.png");
+		laptop_icon = new QIcon(":/icons/laptop.png");
+		group_icon = new QIcon(":/icons/group.png");
+		invitation_icon = new QIcon(":/icons/invitation.png");
+	}
+
+	peers->setModel(&model);
+	peers->setResizeMode(QListView::Adjust);
+	peers->setDragEnabled(false);
+	peers->setSelectionMode(QAbstractItemView::NoSelection);
+
+	peers->setContextMenuPolicy(Qt::CustomContextMenu);
+	connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
+		this, SLOT(context_menu(const QPoint &)));
+
+	wpagui = NULL;
+	hide_ap = false;
+}
+
+
+void Peers::setWpaGui(WpaGui *_wpagui)
+{
+	wpagui = _wpagui;
+	update_peers();
+}
+
+
+Peers::~Peers()
+{
+	delete default_icon;
+	delete ap_icon;
+	delete laptop_icon;
+	delete group_icon;
+	delete invitation_icon;
+}
+
+
+void Peers::languageChange()
+{
+	retranslateUi(this);
+}
+
+
+QString Peers::ItemType(int type)
+{
+	QString title;
+	switch (type) {
+	case PEER_TYPE_ASSOCIATED_STATION:
+		title = tr("Associated station");
+		break;
+	case PEER_TYPE_AP:
+		title = tr("AP");
+		break;
+	case PEER_TYPE_AP_WPS:
+		title = tr("WPS AP");
+		break;
+	case PEER_TYPE_WPS_PIN_NEEDED:
+		title = tr("WPS PIN needed");
+		break;
+	case PEER_TYPE_P2P:
+		title = tr("P2P Device");
+		break;
+	case PEER_TYPE_P2P_CLIENT:
+		title = tr("P2P Device (group client)");
+		break;
+	case PEER_TYPE_P2P_GROUP:
+		title = tr("P2P Group");
+		break;
+	case PEER_TYPE_P2P_PERSISTENT_GROUP_GO:
+		title = tr("P2P Persistent Group (GO)");
+		break;
+	case PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT:
+		title = tr("P2P Persistent Group (client)");
+		break;
+	case PEER_TYPE_P2P_INVITATION:
+		title = tr("P2P Invitation");
+		break;
+	case PEER_TYPE_WPS_ER_AP:
+		title = tr("ER: WPS AP");
+		break;
+	case PEER_TYPE_WPS_ER_AP_UNCONFIGURED:
+		title = tr("ER: WPS AP (Unconfigured)");
+		break;
+	case PEER_TYPE_WPS_ER_ENROLLEE:
+		title = tr("ER: WPS Enrollee");
+		break;
+	case PEER_TYPE_WPS_ENROLLEE:
+		title = tr("WPS Enrollee");
+		break;
+	}
+	return title;
+}
+
+
+void Peers::context_menu(const QPoint &pos)
+{
+	QMenu *menu = new QMenu;
+	if (menu == NULL)
+		return;
+
+	QModelIndex idx = peers->indexAt(pos);
+	if (idx.isValid()) {
+		ctx_item = model.itemFromIndex(idx);
+		int type = ctx_item->data(peer_role_type).toInt();
+		menu->addAction(Peers::ItemType(type))->setEnabled(false);
+		menu->addSeparator();
+
+		int config_methods = -1;
+		QVariant var = ctx_item->data(peer_role_config_methods);
+		if (var.isValid())
+			config_methods = var.toInt();
+
+		enum selected_method method = SEL_METHOD_NONE;
+		var = ctx_item->data(peer_role_selected_method);
+		if (var.isValid())
+			method = (enum selected_method) var.toInt();
+
+		if ((type == PEER_TYPE_ASSOCIATED_STATION ||
+		     type == PEER_TYPE_AP_WPS ||
+		     type == PEER_TYPE_WPS_PIN_NEEDED ||
+		     type == PEER_TYPE_WPS_ER_ENROLLEE ||
+		     type == PEER_TYPE_WPS_ENROLLEE) &&
+		    (config_methods == -1 || (config_methods & 0x010c))) {
+			menu->addAction(tr("Enter WPS PIN"), this,
+					SLOT(enter_pin()));
+		}
+
+		if (type == PEER_TYPE_P2P || type == PEER_TYPE_P2P_CLIENT) {
+			menu->addAction(tr("P2P Connect"), this,
+					SLOT(ctx_p2p_connect()));
+			if (method == SEL_METHOD_NONE &&
+			    config_methods > -1 &&
+			    config_methods & 0x0080 /* PBC */ &&
+			    config_methods != 0x0080)
+				menu->addAction(tr("P2P Connect (PBC)"), this,
+						SLOT(connect_pbc()));
+			if (method == SEL_METHOD_NONE) {
+				menu->addAction(tr("P2P Request PIN"), this,
+						SLOT(ctx_p2p_req_pin()));
+				menu->addAction(tr("P2P Show PIN"), this,
+						SLOT(ctx_p2p_show_pin()));
+			}
+
+			if (config_methods > -1 && (config_methods & 0x0100)) {
+				/* Peer has Keypad */
+				menu->addAction(tr("P2P Display PIN"), this,
+						SLOT(ctx_p2p_display_pin()));
+			}
+
+			if (config_methods > -1 && (config_methods & 0x000c)) {
+				/* Peer has Label or Display */
+				menu->addAction(tr("P2P Enter PIN"), this,
+						SLOT(ctx_p2p_enter_pin()));
+			}
+		}
+
+		if (type == PEER_TYPE_P2P_GROUP) {
+			menu->addAction(tr("Show passphrase"), this,
+					SLOT(ctx_p2p_show_passphrase()));
+			menu->addAction(tr("Remove P2P Group"), this,
+					SLOT(ctx_p2p_remove_group()));
+		}
+
+		if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
+		    type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT ||
+		    type == PEER_TYPE_P2P_INVITATION) {
+			menu->addAction(tr("Start group"), this,
+					SLOT(ctx_p2p_start_persistent()));
+		}
+
+		if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
+		    type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT) {
+			menu->addAction(tr("Invite"), this,
+					SLOT(ctx_p2p_invite()));
+		}
+
+		if (type == PEER_TYPE_P2P_INVITATION) {
+			menu->addAction(tr("Ignore"), this,
+					SLOT(ctx_p2p_delete()));
+		}
+
+		if (type == PEER_TYPE_AP_WPS) {
+			menu->addAction(tr("Connect (PBC)"), this,
+					SLOT(connect_pbc()));
+		}
+
+		if ((type == PEER_TYPE_ASSOCIATED_STATION ||
+		     type == PEER_TYPE_WPS_ER_ENROLLEE ||
+		     type == PEER_TYPE_WPS_ENROLLEE) &&
+		    config_methods >= 0 && (config_methods & 0x0080)) {
+			menu->addAction(tr("Enroll (PBC)"), this,
+					SLOT(connect_pbc()));
+		}
+
+		if (type == PEER_TYPE_WPS_ER_AP) {
+			menu->addAction(tr("Learn Configuration"), this,
+					SLOT(learn_ap_config()));
+		}
+
+		menu->addAction(tr("Properties"), this, SLOT(properties()));
+	} else {
+		ctx_item = NULL;
+		menu->addAction(QString(tr("Refresh")), this,
+				SLOT(ctx_refresh()));
+		menu->addAction(tr("Start P2P discovery"), this,
+				SLOT(ctx_p2p_start()));
+		menu->addAction(tr("Stop P2P discovery"), this,
+				SLOT(ctx_p2p_stop()));
+		menu->addAction(tr("P2P listen only"), this,
+				SLOT(ctx_p2p_listen()));
+		menu->addAction(tr("Start P2P group"), this,
+				SLOT(ctx_p2p_start_group()));
+		if (hide_ap)
+			menu->addAction(tr("Show AP entries"), this,
+					SLOT(ctx_show_ap()));
+		else
+			menu->addAction(tr("Hide AP entries"), this,
+					SLOT(ctx_hide_ap()));
+	}
+
+	menu->exec(peers->mapToGlobal(pos));
+}
+
+
+void Peers::enter_pin()
+{
+	if (ctx_item == NULL)
+		return;
+
+	int peer_type = ctx_item->data(peer_role_type).toInt();
+	QString uuid;
+	QString addr;
+	addr = ctx_item->data(peer_role_address).toString();
+	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
+		uuid = ctx_item->data(peer_role_uuid).toString();
+
+	StringQuery input(tr("PIN:"));
+	input.setWindowTitle(tr("PIN for ") + ctx_item->text());
+	if (input.exec() != QDialog::Accepted)
+		return;
+
+	char cmd[100];
+	char reply[100];
+	size_t reply_len;
+
+	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
+		snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s",
+			 uuid.toLocal8Bit().constData(),
+			 input.get_string().toLocal8Bit().constData(),
+			 addr.toLocal8Bit().constData());
+	} else {
+		snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
+			 addr.toLocal8Bit().constData(),
+			 input.get_string().toLocal8Bit().constData());
+	}
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText(tr("Failed to set the WPS PIN."));
+		msg.exec();
+	}
+}
+
+
+void Peers::ctx_refresh()
+{
+	update_peers();
+}
+
+
+void Peers::ctx_p2p_start()
+{
+	char reply[20];
+	size_t reply_len;
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest("P2P_FIND", reply, &reply_len) < 0 ||
+	    memcmp(reply, "FAIL", 4) == 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText("Failed to start P2P discovery.");
+		msg.exec();
+	}
+}
+
+
+void Peers::ctx_p2p_stop()
+{
+	char reply[20];
+	size_t reply_len;
+	reply_len = sizeof(reply) - 1;
+	wpagui->ctrlRequest("P2P_STOP_FIND", reply, &reply_len);
+}
+
+
+void Peers::ctx_p2p_listen()
+{
+	char reply[20];
+	size_t reply_len;
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest("P2P_LISTEN 3600", reply, &reply_len) < 0 ||
+	    memcmp(reply, "FAIL", 4) == 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText("Failed to start P2P listen.");
+		msg.exec();
+	}
+}
+
+
+void Peers::ctx_p2p_start_group()
+{
+	char reply[20];
+	size_t reply_len;
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest("P2P_GROUP_ADD", reply, &reply_len) < 0 ||
+	    memcmp(reply, "FAIL", 4) == 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText("Failed to start P2P group.");
+		msg.exec();
+	}
+}
+
+
+void Peers::add_station(QString info)
+{
+	QStringList lines = info.split(QRegExp("\\n"));
+	QString name;
+
+	for (QStringList::Iterator it = lines.begin();
+	     it != lines.end(); it++) {
+		int pos = (*it).indexOf('=') + 1;
+		if (pos < 1)
+			continue;
+
+		if ((*it).startsWith("wpsDeviceName="))
+			name = (*it).mid(pos);
+		else if ((*it).startsWith("p2p_device_name="))
+			name = (*it).mid(pos);
+	}
+
+	if (name.isEmpty())
+		name = lines[0];
+
+	QStandardItem *item = new QStandardItem(*laptop_icon, name);
+	if (item) {
+		/* Remove WPS enrollee entry if one is still pending */
+		if (model.rowCount() > 0) {
+			QModelIndexList lst = model.match(model.index(0, 0),
+							  peer_role_address,
+							  lines[0]);
+			for (int i = 0; i < lst.size(); i++) {
+				QStandardItem *item;
+				item = model.itemFromIndex(lst[i]);
+				if (item == NULL)
+					continue;
+				int type = item->data(peer_role_type).toInt();
+				if (type == PEER_TYPE_WPS_ENROLLEE) {
+					model.removeRow(lst[i].row());
+					break;
+				}
+			}
+		}
+
+		item->setData(lines[0], peer_role_address);
+		item->setData(PEER_TYPE_ASSOCIATED_STATION,
+			      peer_role_type);
+		item->setData(info, peer_role_details);
+		item->setToolTip(ItemType(PEER_TYPE_ASSOCIATED_STATION));
+		model.appendRow(item);
+	}
+}
+
+
+void Peers::add_stations()
+{
+	char reply[2048];
+	size_t reply_len;
+	char cmd[30];
+	int res;
+
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
+		return;
+
+	do {
+		reply[reply_len] = '\0';
+		QString info(reply);
+		char *txt = reply;
+		while (*txt != '\0' && *txt != '\n')
+			txt++;
+		*txt++ = '\0';
+		if (strncmp(reply, "FAIL", 4) == 0 ||
+		    strncmp(reply, "UNKNOWN", 7) == 0)
+			break;
+
+		add_station(info);
+
+		reply_len = sizeof(reply) - 1;
+		snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
+		res = wpagui->ctrlRequest(cmd, reply, &reply_len);
+	} while (res >= 0);
+}
+
+
+void Peers::add_single_station(const char *addr)
+{
+	char reply[2048];
+	size_t reply_len;
+	char cmd[30];
+
+	reply_len = sizeof(reply) - 1;
+	snprintf(cmd, sizeof(cmd), "STA %s", addr);
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
+		return;
+
+	reply[reply_len] = '\0';
+	QString info(reply);
+	char *txt = reply;
+	while (*txt != '\0' && *txt != '\n')
+		txt++;
+	*txt++ = '\0';
+	if (strncmp(reply, "FAIL", 4) == 0 ||
+	    strncmp(reply, "UNKNOWN", 7) == 0)
+		return;
+
+	add_station(info);
+}
+
+
+void Peers::add_p2p_group_client(QStandardItem * /*parent*/, QString params)
+{
+	/*
+	 * dev=02:b5:64:63:30:63 iface=02:b5:64:63:30:63 dev_capab=0x0
+	 * dev_type=1-0050f204-1 dev_name='Wireless Client'
+	 * config_methods=0x8c
+	 */
+
+	QStringList items =
+		params.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)"));
+	QString addr = "";
+	QString name = "";
+	int config_methods = 0;
+	QString dev_type;
+
+	for (int i = 0; i < items.size(); i++) {
+		QString str = items.at(i);
+		int pos = str.indexOf('=') + 1;
+		if (str.startsWith("dev_name='"))
+			name = str.section('\'', 1, -2);
+		else if (str.startsWith("config_methods="))
+			config_methods =
+				str.section('=', 1).toInt(0, 0);
+		else if (str.startsWith("dev="))
+			addr = str.mid(pos);
+		else if (str.startsWith("dev_type=") && dev_type.isEmpty())
+			dev_type = str.mid(pos);
+	}
+
+	QStandardItem *item = find_addr(addr);
+	if (item)
+		return;
+
+	item = new QStandardItem(*default_icon, name);
+	if (item) {
+		/* TODO: indicate somehow the relationship to the group owner
+		 * (parent) */
+		item->setData(addr, peer_role_address);
+		item->setData(config_methods, peer_role_config_methods);
+		item->setData(PEER_TYPE_P2P_CLIENT, peer_role_type);
+		if (!dev_type.isEmpty())
+			item->setData(dev_type, peer_role_pri_dev_type);
+		item->setData(items.join(QString("\n")), peer_role_details);
+		item->setToolTip(ItemType(PEER_TYPE_P2P_CLIENT));
+		model.appendRow(item);
+	}
+}
+
+
+void Peers::remove_bss(int id)
+{
+	if (model.rowCount() == 0)
+		return;
+
+	QModelIndexList lst = model.match(model.index(0, 0), peer_role_bss_id,
+					  id);
+	if (lst.size() == 0)
+		return;
+	model.removeRow(lst[0].row());
+}
+
+
+bool Peers::add_bss(const char *cmd)
+{
+	char reply[2048];
+	size_t reply_len;
+
+	if (hide_ap)
+		return false;
+
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
+		return false;
+	reply[reply_len] = '\0';
+
+	QString bss(reply);
+	if (bss.isEmpty() || bss.startsWith("FAIL"))
+		return false;
+
+	QString ssid, bssid, flags, wps_name, pri_dev_type;
+	int id = -1;
+
+	QStringList lines = bss.split(QRegExp("\\n"));
+	for (QStringList::Iterator it = lines.begin();
+	     it != lines.end(); it++) {
+		int pos = (*it).indexOf('=') + 1;
+		if (pos < 1)
+			continue;
+
+		if ((*it).startsWith("bssid="))
+			bssid = (*it).mid(pos);
+		else if ((*it).startsWith("id="))
+			id = (*it).mid(pos).toInt();
+		else if ((*it).startsWith("flags="))
+			flags = (*it).mid(pos);
+		else if ((*it).startsWith("ssid="))
+			ssid = (*it).mid(pos);
+		else if ((*it).startsWith("wps_device_name="))
+			wps_name = (*it).mid(pos);
+		else if ((*it).startsWith("wps_primary_device_type="))
+			pri_dev_type = (*it).mid(pos);
+	}
+
+	QString name = wps_name;
+	if (name.isEmpty())
+		name = ssid + "\n" + bssid;
+
+	QStandardItem *item = new QStandardItem(*ap_icon, name);
+	if (item) {
+		item->setData(bssid, peer_role_address);
+		if (id >= 0)
+			item->setData(id, peer_role_bss_id);
+		int type;
+		if (flags.contains("[WPS"))
+			type = PEER_TYPE_AP_WPS;
+		else
+			type = PEER_TYPE_AP;
+		item->setData(type, peer_role_type);
+
+		for (int i = 0; i < lines.size(); i++) {
+			if (lines[i].length() > 60) {
+				lines[i].remove(60, lines[i].length());
+				lines[i] += "..";
+			}
+		}
+		item->setToolTip(ItemType(type));
+		item->setData(lines.join("\n"), peer_role_details);
+		if (!pri_dev_type.isEmpty())
+			item->setData(pri_dev_type,
+				      peer_role_pri_dev_type);
+		if (!ssid.isEmpty())
+			item->setData(ssid, peer_role_ssid);
+		model.appendRow(item);
+
+		lines = bss.split(QRegExp("\\n"));
+		for (QStringList::Iterator it = lines.begin();
+		     it != lines.end(); it++) {
+			if ((*it).startsWith("p2p_group_client:"))
+				add_p2p_group_client(item,
+						     (*it).mid(18));
+		}
+	}
+
+	return true;
+}
+
+
+void Peers::add_scan_results()
+{
+	int index;
+	char cmd[20];
+
+	index = 0;
+	while (wpagui) {
+		snprintf(cmd, sizeof(cmd), "BSS %d", index++);
+		if (index > 1000)
+			break;
+
+		if (!add_bss(cmd))
+			break;
+	}
+}
+
+
+void Peers::add_persistent(int id, const char *ssid, const char *bssid)
+{
+	char cmd[100];
+	char reply[100];
+	size_t reply_len;
+	int mode;
+
+	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d mode", id);
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
+		return;
+	reply[reply_len] = '\0';
+	mode = atoi(reply);
+
+	QString name = ssid;
+	name = '[' + name + ']';
+
+	QStandardItem *item = new QStandardItem(*group_icon, name);
+	if (!item)
+		return;
+
+	int type;
+	if (mode == 3)
+		type = PEER_TYPE_P2P_PERSISTENT_GROUP_GO;
+	else
+		type = PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT;
+	item->setData(type, peer_role_type);
+	item->setToolTip(ItemType(type));
+	item->setData(ssid, peer_role_ssid);
+	if (bssid && strcmp(bssid, "any") == 0)
+		bssid = NULL;
+	if (bssid)
+		item->setData(bssid, peer_role_address);
+	item->setData(id, peer_role_network_id);
+	item->setBackground(Qt::BDiagPattern);
+
+	model.appendRow(item);
+}
+
+
+void Peers::add_persistent_groups()
+{
+	char buf[2048], *start, *end, *id, *ssid, *bssid, *flags;
+	size_t len;
+
+	len = sizeof(buf) - 1;
+	if (wpagui->ctrlRequest("LIST_NETWORKS", buf, &len) < 0)
+		return;
+
+	buf[len] = '\0';
+	start = strchr(buf, '\n');
+	if (start == NULL)
+		return;
+	start++;
+
+	while (*start) {
+		bool last = false;
+		end = strchr(start, '\n');
+		if (end == NULL) {
+			last = true;
+			end = start;
+			while (end[0] && end[1])
+				end++;
+		}
+		*end = '\0';
+
+		id = start;
+		ssid = strchr(id, '\t');
+		if (ssid == NULL)
+			break;
+		*ssid++ = '\0';
+		bssid = strchr(ssid, '\t');
+		if (bssid == NULL)
+			break;
+		*bssid++ = '\0';
+		flags = strchr(bssid, '\t');
+		if (flags == NULL)
+			break;
+		*flags++ = '\0';
+
+		if (strstr(flags, "[DISABLED][P2P-PERSISTENT]"))
+			add_persistent(atoi(id), ssid, bssid);
+
+		if (last)
+			break;
+		start = end + 1;
+	}
+}
+
+
+void Peers::update_peers()
+{
+	model.clear();
+	if (wpagui == NULL)
+		return;
+
+	char reply[20];
+	size_t replylen = sizeof(reply) - 1;
+	wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);
+
+	add_stations();
+	add_scan_results();
+	add_persistent_groups();
+}
+
+
+QStandardItem * Peers::find_addr(QString addr)
+{
+	if (model.rowCount() == 0)
+		return NULL;
+
+	QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
+					  addr);
+	if (lst.size() == 0)
+		return NULL;
+	return model.itemFromIndex(lst[0]);
+}
+
+
+QStandardItem * Peers::find_addr_type(QString addr, int type)
+{
+	if (model.rowCount() == 0)
+		return NULL;
+
+	QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
+					  addr);
+	for (int i = 0; i < lst.size(); i++) {
+		QStandardItem *item = model.itemFromIndex(lst[i]);
+		if (item->data(peer_role_type).toInt() == type)
+			return item;
+	}
+	return NULL;
+}
+
+
+QStandardItem * Peers::find_uuid(QString uuid)
+{
+	if (model.rowCount() == 0)
+		return NULL;
+
+	QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
+					  uuid);
+	if (lst.size() == 0)
+		return NULL;
+	return model.itemFromIndex(lst[0]);
+}
+
+
+void Peers::event_notify(WpaMsg msg)
+{
+	QString text = msg.getMsg();
+
+	if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
+		/*
+		 * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
+		 * 02:2a:c4:18:5b:f3
+		 * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
+		 */
+		QStringList items = text.split(' ');
+		QString uuid = items[1];
+		QString addr = items[2];
+		QString name = "";
+
+		QStandardItem *item = find_addr(addr);
+		if (item)
+			return;
+
+		int pos = text.indexOf('[');
+		if (pos >= 0) {
+			int pos2 = text.lastIndexOf(']');
+			if (pos2 >= pos) {
+				items = text.mid(pos + 1, pos2 - pos - 1).
+					split('|');
+				name = items[0];
+				items.append(addr);
+			}
+		}
+
+		item = new QStandardItem(*laptop_icon, name);
+		if (item) {
+			item->setData(addr, peer_role_address);
+			item->setData(PEER_TYPE_WPS_PIN_NEEDED,
+				      peer_role_type);
+			item->setToolTip(ItemType(PEER_TYPE_WPS_PIN_NEEDED));
+			item->setData(items.join("\n"), peer_role_details);
+			item->setData(items[5], peer_role_pri_dev_type);
+			model.appendRow(item);
+		}
+		return;
+	}
+
+	if (text.startsWith(AP_STA_CONNECTED)) {
+		/* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
+		QStringList items = text.split(' ');
+		QString addr = items[1];
+		QStandardItem *item = find_addr(addr);
+		if (item == NULL || item->data(peer_role_type).toInt() !=
+		    PEER_TYPE_ASSOCIATED_STATION)
+			add_single_station(addr.toLocal8Bit().constData());
+		return;
+	}
+
+	if (text.startsWith(AP_STA_DISCONNECTED)) {
+		/* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
+		QStringList items = text.split(' ');
+		QString addr = items[1];
+
+		if (model.rowCount() == 0)
+			return;
+
+		QModelIndexList lst = model.match(model.index(0, 0),
+						  peer_role_address, addr, -1);
+		for (int i = 0; i < lst.size(); i++) {
+			QStandardItem *item = model.itemFromIndex(lst[i]);
+			if (item && item->data(peer_role_type).toInt() ==
+			    PEER_TYPE_ASSOCIATED_STATION) {
+				model.removeRow(lst[i].row());
+				break;
+			}
+		}
+		return;
+	}
+
+	if (text.startsWith(P2P_EVENT_DEVICE_FOUND)) {
+		/*
+		 * P2P-DEVICE-FOUND 02:b5:64:63:30:63
+		 * p2p_dev_addr=02:b5:64:63:30:63 pri_dev_type=1-0050f204-1
+		 * name='Wireless Client' config_methods=0x84 dev_capab=0x21
+		 * group_capab=0x0
+		 */
+		QStringList items =
+			text.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)"));
+		QString addr = items[1];
+		QString name = "";
+		QString pri_dev_type;
+		int config_methods = 0;
+		for (int i = 0; i < items.size(); i++) {
+			QString str = items.at(i);
+			if (str.startsWith("name='"))
+				name = str.section('\'', 1, -2);
+			else if (str.startsWith("config_methods="))
+				config_methods =
+					str.section('=', 1).toInt(0, 0);
+			else if (str.startsWith("pri_dev_type="))
+				pri_dev_type = str.section('=', 1);
+		}
+
+		QStandardItem *item = find_addr(addr);
+		if (item) {
+			int type = item->data(peer_role_type).toInt();
+			if (type == PEER_TYPE_P2P)
+				return;
+		}
+
+		item = new QStandardItem(*default_icon, name);
+		if (item) {
+			item->setData(addr, peer_role_address);
+			item->setData(config_methods,
+				      peer_role_config_methods);
+			item->setData(PEER_TYPE_P2P, peer_role_type);
+			if (!pri_dev_type.isEmpty())
+				item->setData(pri_dev_type,
+					      peer_role_pri_dev_type);
+			item->setData(items.join(QString("\n")),
+				      peer_role_details);
+			item->setToolTip(ItemType(PEER_TYPE_P2P));
+			model.appendRow(item);
+		}
+
+		item = find_addr_type(addr,
+				      PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT);
+		if (item)
+			item->setBackground(Qt::NoBrush);
+	}
+
+	if (text.startsWith(P2P_EVENT_GROUP_STARTED)) {
+		/* P2P-GROUP-STARTED wlan0-p2p-0 GO ssid="DIRECT-3F"
+		 * passphrase="YOyTkxID" go_dev_addr=02:40:61:c2:f3:b7
+		 * [PERSISTENT] */
+		QStringList items = text.split(' ');
+		if (items.size() < 4)
+			return;
+
+		int pos = text.indexOf(" ssid=\"");
+		if (pos < 0)
+			return;
+		QString ssid = text.mid(pos + 7);
+		pos = ssid.indexOf(" passphrase=\"");
+		if (pos < 0)
+			pos = ssid.indexOf(" psk=");
+		if (pos >= 0)
+			ssid.truncate(pos);
+		pos = ssid.lastIndexOf('"');
+		if (pos >= 0)
+			ssid.truncate(pos);
+
+		QStandardItem *item = new QStandardItem(*group_icon, ssid);
+		if (item) {
+			item->setData(PEER_TYPE_P2P_GROUP, peer_role_type);
+			item->setData(items[1], peer_role_ifname);
+			QString details;
+			if (items[2] == "GO") {
+				details = tr("P2P GO for interface ") +
+					items[1];
+			} else {
+				details = tr("P2P client for interface ") +
+					items[1];
+			}
+			if (text.contains(" [PERSISTENT]"))
+				details += "\nPersistent group";
+			item->setData(details, peer_role_details);
+			item->setToolTip(ItemType(PEER_TYPE_P2P_GROUP));
+			model.appendRow(item);
+		}
+	}
+
+	if (text.startsWith(P2P_EVENT_GROUP_REMOVED)) {
+		/* P2P-GROUP-REMOVED wlan0-p2p-0 GO */
+		QStringList items = text.split(' ');
+		if (items.size() < 2)
+			return;
+
+		if (model.rowCount() == 0)
+			return;
+
+		QModelIndexList lst = model.match(model.index(0, 0),
+						  peer_role_ifname, items[1]);
+		for (int i = 0; i < lst.size(); i++)
+			model.removeRow(lst[i].row());
+		return;
+	}
+
+	if (text.startsWith(P2P_EVENT_PROV_DISC_SHOW_PIN)) {
+		/* P2P-PROV-DISC-SHOW-PIN 02:40:61:c2:f3:b7 12345670 */
+		QStringList items = text.split(' ');
+		if (items.size() < 3)
+			return;
+		QString addr = items[1];
+		QString pin = items[2];
+
+		QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P);
+		if (item == NULL)
+			return;
+		item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY,
+			      peer_role_selected_method);
+		item->setData(pin, peer_role_selected_pin);
+		QVariant var = item->data(peer_role_requested_method);
+		if (var.isValid() &&
+		    var.toInt() == SEL_METHOD_PIN_LOCAL_DISPLAY) {
+			ctx_item = item;
+			ctx_p2p_display_pin_pd();
+		}
+		return;
+	}
+
+	if (text.startsWith(P2P_EVENT_PROV_DISC_ENTER_PIN)) {
+		/* P2P-PROV-DISC-ENTER-PIN 02:40:61:c2:f3:b7 */
+		QStringList items = text.split(' ');
+		if (items.size() < 2)
+			return;
+		QString addr = items[1];
+
+		QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P);
+		if (item == NULL)
+			return;
+		item->setData(SEL_METHOD_PIN_PEER_DISPLAY,
+			      peer_role_selected_method);
+		QVariant var = item->data(peer_role_requested_method);
+		if (var.isValid() &&
+		    var.toInt() == SEL_METHOD_PIN_PEER_DISPLAY) {
+			ctx_item = item;
+			ctx_p2p_connect();
+		}
+		return;
+	}
+
+	if (text.startsWith(P2P_EVENT_INVITATION_RECEIVED)) {
+		/* P2P-INVITATION-RECEIVED sa=02:f0:bc:44:87:62 persistent=4 */
+		QStringList items = text.split(' ');
+		if (items.size() < 3)
+			return;
+		if (!items[1].startsWith("sa=") ||
+		    !items[2].startsWith("persistent="))
+			return;
+		QString addr = items[1].mid(3);
+		int id = items[2].mid(11).toInt();
+
+		char cmd[100];
+		char reply[100];
+		size_t reply_len;
+
+		snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", id);
+		reply_len = sizeof(reply) - 1;
+		if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
+			return;
+		reply[reply_len] = '\0';
+		QString name;
+		char *pos = strrchr(reply, '"');
+		if (pos && reply[0] == '"') {
+			*pos = '\0';
+			name = reply + 1;
+		} else
+			name = reply;
+
+		QStandardItem *item;
+		item = find_addr_type(addr, PEER_TYPE_P2P_INVITATION);
+		if (item)
+			model.removeRow(item->row());
+
+		item = new QStandardItem(*invitation_icon, name);
+		if (!item)
+			return;
+		item->setData(PEER_TYPE_P2P_INVITATION, peer_role_type);
+		item->setToolTip(ItemType(PEER_TYPE_P2P_INVITATION));
+		item->setData(addr, peer_role_address);
+		item->setData(id, peer_role_network_id);
+
+		model.appendRow(item);
+
+		enable_persistent(id);
+
+		return;
+	}
+
+	if (text.startsWith(P2P_EVENT_INVITATION_RESULT)) {
+		/* P2P-INVITATION-RESULT status=1 */
+		/* TODO */
+		return;
+	}
+
+	if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
+		/*
+		 * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
+		 * 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1
+		 * |Very friendly name|Company|Long description of the model|
+		 * WAP|http://w1.fi/|http://w1.fi/hostapd/
+		 */
+		QStringList items = text.split(' ');
+		if (items.size() < 5)
+			return;
+		QString uuid = items[1];
+		QString addr = items[2];
+		QString pri_dev_type = items[3].mid(13);
+		int wps_state = items[4].mid(10).toInt();
+
+		int pos = text.indexOf('|');
+		if (pos < 0)
+			return;
+		items = text.mid(pos + 1).split('|');
+		if (items.size() < 1)
+			return;
+
+		QStandardItem *item = find_uuid(uuid);
+		if (item)
+			return;
+
+		item = new QStandardItem(*ap_icon, items[0]);
+		if (item) {
+			item->setData(uuid, peer_role_uuid);
+			item->setData(addr, peer_role_address);
+			int type = wps_state == 2 ? PEER_TYPE_WPS_ER_AP:
+				PEER_TYPE_WPS_ER_AP_UNCONFIGURED;
+			item->setData(type, peer_role_type);
+			item->setToolTip(ItemType(type));
+			item->setData(pri_dev_type, peer_role_pri_dev_type);
+			item->setData(items.join(QString("\n")),
+				      peer_role_details);
+			model.appendRow(item);
+		}
+
+		return;
+	}
+
+	if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
+		/* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
+		QStringList items = text.split(' ');
+		if (items.size() < 2)
+			return;
+		if (model.rowCount() == 0)
+			return;
+
+		QModelIndexList lst = model.match(model.index(0, 0),
+						  peer_role_uuid, items[1]);
+		for (int i = 0; i < lst.size(); i++) {
+			QStandardItem *item = model.itemFromIndex(lst[i]);
+			if (item &&
+			    (item->data(peer_role_type).toInt() ==
+			     PEER_TYPE_WPS_ER_AP ||
+			     item->data(peer_role_type).toInt() ==
+			     PEER_TYPE_WPS_ER_AP_UNCONFIGURED))
+				model.removeRow(lst[i].row());
+		}
+		return;
+	}
+
+	if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
+		/*
+		 * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
+		 * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
+		 * pri_dev_type=1-0050F204-1
+		 * |Wireless Client|Company|cmodel|123|12345|
+		 */
+		QStringList items = text.split(' ');
+		if (items.size() < 3)
+			return;
+		QString uuid = items[1];
+		QString addr = items[2];
+		QString pri_dev_type = items[6].mid(13);
+		int config_methods = -1;
+		int dev_passwd_id = -1;
+
+		for (int i = 3; i < items.size(); i++) {
+			int pos = items[i].indexOf('=') + 1;
+			if (pos < 1)
+				continue;
+			QString val = items[i].mid(pos);
+			if (items[i].startsWith("config_methods=")) {
+				config_methods = val.toInt(0, 0);
+			} else if (items[i].startsWith("dev_passwd_id=")) {
+				dev_passwd_id = val.toInt();
+			}
+		}
+
+		int pos = text.indexOf('|');
+		if (pos < 0)
+			return;
+		items = text.mid(pos + 1).split('|');
+		if (items.size() < 1)
+			return;
+		QString name = items[0];
+		if (name.length() == 0)
+			name = addr;
+
+		remove_enrollee_uuid(uuid);
+
+		QStandardItem *item;
+		item = new QStandardItem(*laptop_icon, name);
+		if (item) {
+			item->setData(uuid, peer_role_uuid);
+			item->setData(addr, peer_role_address);
+			item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
+				      peer_role_type);
+			item->setToolTip(ItemType(PEER_TYPE_WPS_ER_ENROLLEE));
+			item->setData(items.join(QString("\n")),
+				      peer_role_details);
+			item->setData(pri_dev_type, peer_role_pri_dev_type);
+			if (config_methods >= 0)
+				item->setData(config_methods,
+					      peer_role_config_methods);
+			if (dev_passwd_id >= 0)
+				item->setData(dev_passwd_id,
+					      peer_role_dev_passwd_id);
+			model.appendRow(item);
+		}
+
+		return;
+	}
+
+	if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
+		/*
+		 * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
+		 * 02:66:a0:ee:17:27
+		 */
+		QStringList items = text.split(' ');
+		if (items.size() < 2)
+			return;
+		remove_enrollee_uuid(items[1]);
+		return;
+	}
+
+	if (text.startsWith(WPS_EVENT_ENROLLEE_SEEN)) {
+		/* TODO: need to time out this somehow or remove on successful
+		 * WPS run, etc. */
+		/*
+		 * WPS-ENROLLEE-SEEN 02:00:00:00:01:00
+		 * 572cf82f-c957-5653-9b16-b5cfb298abf1 1-0050F204-1 0x80 4 1
+		 * [Wireless Client]
+		 * (MAC addr, UUID-E, pri dev type, config methods,
+		 * dev passwd id, request type, [dev name])
+		 */
+		QStringList items = text.split(' ');
+		if (items.size() < 7)
+			return;
+		QString addr = items[1];
+		QString uuid = items[2];
+		QString pri_dev_type = items[3];
+		int config_methods = items[4].toInt(0, 0);
+		int dev_passwd_id = items[5].toInt();
+		QString name;
+
+		QStandardItem *item = find_addr(addr);
+		if (item) {
+			int type = item->data(peer_role_type).toInt();
+			if (type == PEER_TYPE_ASSOCIATED_STATION)
+				return; /* already associated */
+		}
+
+		int pos = text.indexOf('[');
+		if (pos >= 0) {
+			int pos2 = text.lastIndexOf(']');
+			if (pos2 >= pos) {
+				QStringList items2 =
+					text.mid(pos + 1, pos2 - pos - 1).
+					split('|');
+				name = items2[0];
+			}
+		}
+		if (name.isEmpty())
+			name = addr;
+
+		item = find_uuid(uuid);
+		if (item) {
+			QVariant var = item->data(peer_role_config_methods);
+			QVariant var2 = item->data(peer_role_dev_passwd_id);
+			if ((var.isValid() && config_methods != var.toInt()) ||
+			    (var2.isValid() && dev_passwd_id != var2.toInt()))
+				remove_enrollee_uuid(uuid);
+			else
+				return;
+		}
+
+		item = new QStandardItem(*laptop_icon, name);
+		if (item) {
+			item->setData(uuid, peer_role_uuid);
+			item->setData(addr, peer_role_address);
+			item->setData(PEER_TYPE_WPS_ENROLLEE,
+				      peer_role_type);
+			item->setToolTip(ItemType(PEER_TYPE_WPS_ENROLLEE));
+			item->setData(items.join(QString("\n")),
+				      peer_role_details);
+			item->setData(pri_dev_type, peer_role_pri_dev_type);
+			item->setData(config_methods,
+				      peer_role_config_methods);
+			item->setData(dev_passwd_id, peer_role_dev_passwd_id);
+			model.appendRow(item);
+		}
+
+		return;
+	}
+
+	if (text.startsWith(WPA_EVENT_BSS_ADDED)) {
+		/* CTRL-EVENT-BSS-ADDED 34 00:11:22:33:44:55 */
+		QStringList items = text.split(' ');
+		if (items.size() < 2)
+			return;
+		char cmd[20];
+		snprintf(cmd, sizeof(cmd), "BSS ID-%d", items[1].toInt());
+		add_bss(cmd);
+		return;
+	}
+
+	if (text.startsWith(WPA_EVENT_BSS_REMOVED)) {
+		/* CTRL-EVENT-BSS-REMOVED 34 00:11:22:33:44:55 */
+		QStringList items = text.split(' ');
+		if (items.size() < 2)
+			return;
+		remove_bss(items[1].toInt());
+		return;
+	}
+}
+
+
+void Peers::ctx_p2p_connect()
+{
+	if (ctx_item == NULL)
+		return;
+	QString addr = ctx_item->data(peer_role_address).toString();
+	QString arg;
+	int config_methods =
+		ctx_item->data(peer_role_config_methods).toInt();
+	enum selected_method method = SEL_METHOD_NONE;
+	QVariant var = ctx_item->data(peer_role_selected_method);
+	if (var.isValid())
+		method = (enum selected_method) var.toInt();
+	if (method == SEL_METHOD_PIN_LOCAL_DISPLAY) {
+		arg = ctx_item->data(peer_role_selected_pin).toString();
+		char cmd[100];
+		char reply[100];
+		size_t reply_len;
+		snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display",
+			 addr.toLocal8Bit().constData(),
+			 arg.toLocal8Bit().constData());
+		reply_len = sizeof(reply) - 1;
+		if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+			QMessageBox msg;
+			msg.setIcon(QMessageBox::Warning);
+			msg.setText("Failed to initiate P2P connect.");
+			msg.exec();
+			return;
+		}
+		QMessageBox::information(this,
+					 tr("PIN for ") + ctx_item->text(),
+					 tr("Enter the following PIN on the\n"
+					    "peer device: ") + arg);
+	} else if (method == SEL_METHOD_PIN_PEER_DISPLAY) {
+		StringQuery input(tr("PIN from peer display:"));
+		input.setWindowTitle(tr("PIN for ") + ctx_item->text());
+		if (input.exec() != QDialog::Accepted)
+			return;
+		arg = input.get_string();
+	} else if (config_methods == 0x0080 /* PBC */) {
+		arg = "pbc";
+	} else {
+		StringQuery input(tr("PIN:"));
+		input.setWindowTitle(tr("PIN for ") + ctx_item->text());
+		if (input.exec() != QDialog::Accepted)
+			return;
+		arg = input.get_string();
+	}
+
+	char cmd[100];
+	char reply[100];
+	size_t reply_len;
+	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s",
+		 addr.toLocal8Bit().constData(),
+		 arg.toLocal8Bit().constData());
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText("Failed to initiate P2P connect.");
+		msg.exec();
+	}
+}
+
+
+void Peers::ctx_p2p_req_pin()
+{
+	if (ctx_item == NULL)
+		return;
+	QString addr = ctx_item->data(peer_role_address).toString();
+	ctx_item->setData(SEL_METHOD_PIN_PEER_DISPLAY,
+			  peer_role_requested_method);
+
+	char cmd[100];
+	char reply[100];
+	size_t reply_len;
+	snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s display",
+		 addr.toLocal8Bit().constData());
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText(tr("Failed to request PIN from peer."));
+		msg.exec();
+	}
+}
+
+
+void Peers::ctx_p2p_show_pin()
+{
+	if (ctx_item == NULL)
+		return;
+	QString addr = ctx_item->data(peer_role_address).toString();
+	ctx_item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY,
+			  peer_role_requested_method);
+
+	char cmd[100];
+	char reply[100];
+	size_t reply_len;
+	snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s keypad",
+		 addr.toLocal8Bit().constData());
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText(tr("Failed to request peer to enter PIN."));
+		msg.exec();
+	}
+}
+
+
+void Peers::ctx_p2p_display_pin()
+{
+	if (ctx_item == NULL)
+		return;
+	QString addr = ctx_item->data(peer_role_address).toString();
+
+	char cmd[100];
+	char reply[100];
+	size_t reply_len;
+	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pin",
+		 addr.toLocal8Bit().constData());
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText("Failed to initiate P2P connect.");
+		msg.exec();
+		return;
+	}
+	reply[reply_len] = '\0';
+	QMessageBox::information(this,
+				 tr("PIN for ") + ctx_item->text(),
+				 tr("Enter the following PIN on the\n"
+				    "peer device: ") + reply);
+}
+
+
+void Peers::ctx_p2p_display_pin_pd()
+{
+	if (ctx_item == NULL)
+		return;
+	QString addr = ctx_item->data(peer_role_address).toString();
+	QString arg = ctx_item->data(peer_role_selected_pin).toString();
+
+	char cmd[100];
+	char reply[100];
+	size_t reply_len;
+	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display",
+		 addr.toLocal8Bit().constData(),
+		 arg.toLocal8Bit().constData());
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText("Failed to initiate P2P connect.");
+		msg.exec();
+		return;
+	}
+	reply[reply_len] = '\0';
+	QMessageBox::information(this,
+				 tr("PIN for ") + ctx_item->text(),
+				 tr("Enter the following PIN on the\n"
+				    "peer device: ") + arg);
+}
+
+
+void Peers::ctx_p2p_enter_pin()
+{
+	if (ctx_item == NULL)
+		return;
+	QString addr = ctx_item->data(peer_role_address).toString();
+	QString arg;
+
+	StringQuery input(tr("PIN from peer:"));
+	input.setWindowTitle(tr("PIN for ") + ctx_item->text());
+	if (input.exec() != QDialog::Accepted)
+		return;
+	arg = input.get_string();
+
+	char cmd[100];
+	char reply[100];
+	size_t reply_len;
+	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s keypad",
+		 addr.toLocal8Bit().constData(),
+		 arg.toLocal8Bit().constData());
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText("Failed to initiate P2P connect.");
+		msg.exec();
+	}
+}
+
+
+void Peers::ctx_p2p_remove_group()
+{
+	if (ctx_item == NULL)
+		return;
+	char cmd[100];
+	char reply[100];
+	size_t reply_len;
+	snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s",
+		 ctx_item->data(peer_role_ifname).toString().toLocal8Bit().
+		 constData());
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText("Failed to remove P2P Group.");
+		msg.exec();
+	}
+}
+
+
+void Peers::closeEvent(QCloseEvent *)
+{
+	if (wpagui) {
+		char reply[20];
+		size_t replylen = sizeof(reply) - 1;
+		wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
+	}
+}
+
+
+void Peers::done(int r)
+{
+	QDialog::done(r);
+	close();
+}
+
+
+void Peers::remove_enrollee_uuid(QString uuid)
+{
+	if (model.rowCount() == 0)
+		return;
+
+	QModelIndexList lst = model.match(model.index(0, 0),
+					  peer_role_uuid, uuid);
+	for (int i = 0; i < lst.size(); i++) {
+		QStandardItem *item = model.itemFromIndex(lst[i]);
+		if (item == NULL)
+			continue;
+		int type = item->data(peer_role_type).toInt();
+		if (type == PEER_TYPE_WPS_ER_ENROLLEE ||
+		    type == PEER_TYPE_WPS_ENROLLEE)
+			model.removeRow(lst[i].row());
+	}
+}
+
+
+void Peers::properties()
+{
+	if (ctx_item == NULL)
+		return;
+
+	QMessageBox msg(this);
+	msg.setStandardButtons(QMessageBox::Ok);
+	msg.setDefaultButton(QMessageBox::Ok);
+	msg.setEscapeButton(QMessageBox::Ok);
+	msg.setWindowTitle(tr("Peer Properties"));
+
+	int type = ctx_item->data(peer_role_type).toInt();
+	QString title = Peers::ItemType(type);
+
+	msg.setText(title + QString("\n") + tr("Name: ") + ctx_item->text());
+
+	QVariant var;
+	QString info;
+
+	var = ctx_item->data(peer_role_address);
+	if (var.isValid())
+		info += tr("Address: ") + var.toString() + QString("\n");
+
+	var = ctx_item->data(peer_role_uuid);
+	if (var.isValid())
+		info += tr("UUID: ") + var.toString() + QString("\n");
+
+	var = ctx_item->data(peer_role_pri_dev_type);
+	if (var.isValid())
+		info += tr("Primary Device Type: ") + var.toString() +
+			QString("\n");
+
+	var = ctx_item->data(peer_role_ssid);
+	if (var.isValid())
+		info += tr("SSID: ") + var.toString() + QString("\n");
+
+	var = ctx_item->data(peer_role_config_methods);
+	if (var.isValid()) {
+		int methods = var.toInt();
+		info += tr("Configuration Methods: ");
+		if (methods & 0x0001)
+			info += tr("[USBA]");
+		if (methods & 0x0002)
+			info += tr("[Ethernet]");
+		if (methods & 0x0004)
+			info += tr("[Label]");
+		if (methods & 0x0008)
+			info += tr("[Display]");
+		if (methods & 0x0010)
+			info += tr("[Ext. NFC Token]");
+		if (methods & 0x0020)
+			info += tr("[Int. NFC Token]");
+		if (methods & 0x0040)
+			info += tr("[NFC Interface]");
+		if (methods & 0x0080)
+			info += tr("[Push Button]");
+		if (methods & 0x0100)
+			info += tr("[Keypad]");
+		info += "\n";
+	}
+
+	var = ctx_item->data(peer_role_selected_method);
+	if (var.isValid()) {
+		enum selected_method method =
+			(enum selected_method) var.toInt();
+		switch (method) {
+		case SEL_METHOD_NONE:
+			break;
+		case SEL_METHOD_PIN_PEER_DISPLAY:
+			info += tr("Selected Method: PIN on peer display\n");
+			break;
+		case SEL_METHOD_PIN_LOCAL_DISPLAY:
+			info += tr("Selected Method: PIN on local display\n");
+			break;
+		}
+	}
+
+	var = ctx_item->data(peer_role_selected_pin);
+	if (var.isValid()) {
+		info += tr("PIN to enter on peer: ") + var.toString() + "\n";
+	}
+
+	var = ctx_item->data(peer_role_dev_passwd_id);
+	if (var.isValid()) {
+		info += tr("Device Password ID: ") + var.toString();
+		switch (var.toInt()) {
+		case 0:
+			info += tr(" (Default PIN)");
+			break;
+		case 1:
+			info += tr(" (User-specified PIN)");
+			break;
+		case 2:
+			info += tr(" (Machine-specified PIN)");
+			break;
+		case 3:
+			info += tr(" (Rekey)");
+			break;
+		case 4:
+			info += tr(" (Push Button)");
+			break;
+		case 5:
+			info += tr(" (Registrar-specified)");
+			break;
+		}
+		info += "\n";
+	}
+
+	msg.setInformativeText(info);
+
+	var = ctx_item->data(peer_role_details);
+	if (var.isValid())
+		msg.setDetailedText(var.toString());
+
+	msg.exec();
+}
+
+
+void Peers::connect_pbc()
+{
+	if (ctx_item == NULL)
+		return;
+
+	char cmd[100];
+	char reply[100];
+	size_t reply_len;
+
+	int peer_type = ctx_item->data(peer_role_type).toInt();
+	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
+		snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
+			 ctx_item->data(peer_role_uuid).toString().toLocal8Bit().
+			 constData());
+	} else if (peer_type == PEER_TYPE_P2P ||
+		   peer_type == PEER_TYPE_P2P_CLIENT) {
+		snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pbc",
+			 ctx_item->data(peer_role_address).toString().
+			 toLocal8Bit().constData());
+	} else {
+		snprintf(cmd, sizeof(cmd), "WPS_PBC");
+	}
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText(tr("Failed to start WPS PBC."));
+		msg.exec();
+	}
+}
+
+
+void Peers::learn_ap_config()
+{
+	if (ctx_item == NULL)
+		return;
+
+	QString uuid = ctx_item->data(peer_role_uuid).toString();
+
+	StringQuery input(tr("AP PIN:"));
+	input.setWindowTitle(tr("AP PIN for ") + ctx_item->text());
+	if (input.exec() != QDialog::Accepted)
+		return;
+
+	char cmd[100];
+	char reply[100];
+	size_t reply_len;
+
+	snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s",
+		 uuid.toLocal8Bit().constData(),
+		 input.get_string().toLocal8Bit().constData());
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText(tr("Failed to start learning AP configuration."));
+		msg.exec();
+	}
+}
+
+
+void Peers::ctx_hide_ap()
+{
+	hide_ap = true;
+
+	if (model.rowCount() == 0)
+		return;
+
+	do {
+		QModelIndexList lst;
+		lst = model.match(model.index(0, 0),
+				  peer_role_type, PEER_TYPE_AP);
+		if (lst.size() == 0) {
+			lst = model.match(model.index(0, 0),
+					  peer_role_type, PEER_TYPE_AP_WPS);
+			if (lst.size() == 0)
+				break;
+		}
+
+		model.removeRow(lst[0].row());
+	} while (1);
+}
+
+
+void Peers::ctx_show_ap()
+{
+	hide_ap = false;
+	add_scan_results();
+}
+
+
+void Peers::ctx_p2p_show_passphrase()
+{
+	char reply[64];
+	size_t reply_len;
+
+	reply_len = sizeof(reply) - 1;
+	if (wpagui->ctrlRequest("P2P_GET_PASSPHRASE", reply, &reply_len) < 0 ||
+	    memcmp(reply, "FAIL", 4) == 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText("Failed to get P2P group passphrase.");
+		msg.exec();
+	} else {
+		reply[reply_len] = '\0';
+		QMessageBox::information(this, tr("Passphrase"),
+					 tr("P2P group passphrase:\n") +
+					 reply);
+	}
+}
+
+
+void Peers::ctx_p2p_start_persistent()
+{
+	if (ctx_item == NULL)
+		return;
+
+	char cmd[100];
+	char reply[100];
+	size_t reply_len;
+
+	snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD persistent=%d",
+		 ctx_item->data(peer_role_network_id).toInt());
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 ||
+	    memcmp(reply, "FAIL", 4) == 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText(tr("Failed to start persistent P2P Group."));
+		msg.exec();
+	} else if (ctx_item->data(peer_role_type).toInt() ==
+		   PEER_TYPE_P2P_INVITATION)
+		model.removeRow(ctx_item->row());
+}
+
+
+void Peers::ctx_p2p_invite()
+{
+	if (ctx_item == NULL)
+		return;
+
+	char cmd[100];
+	char reply[100];
+	size_t reply_len;
+
+	snprintf(cmd, sizeof(cmd), "P2P_INVITE persistent=%d",
+		 ctx_item->data(peer_role_network_id).toInt());
+	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 ||
+	    memcmp(reply, "FAIL", 4) == 0) {
+		QMessageBox msg;
+		msg.setIcon(QMessageBox::Warning);
+		msg.setText(tr("Failed to invite peer to start persistent "
+			       "P2P Group."));
+		msg.exec();
+	}
+}
+
+
+void Peers::ctx_p2p_delete()
+{
+	if (ctx_item == NULL)
+		return;
+	model.removeRow(ctx_item->row());
+}
+
+
+void Peers::enable_persistent(int id)
+{
+	if (model.rowCount() == 0)
+		return;
+
+	QModelIndexList lst = model.match(model.index(0, 0),
+					  peer_role_network_id, id);
+	for (int i = 0; i < lst.size(); i++) {
+		QStandardItem *item = model.itemFromIndex(lst[i]);
+		int type = item->data(peer_role_type).toInt();
+		if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
+		    type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT)
+			item->setBackground(Qt::NoBrush);
+	}
+}
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/peers.h b/hostap/wpa_supplicant/wpa_gui-qt4/peers.h
new file mode 100644
index 0000000..bb73737
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/peers.h
@@ -0,0 +1,90 @@
+/*
+ * wpa_gui - Peers class
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PEERS_H
+#define PEERS_H
+
+#include <QObject>
+#include <QStandardItemModel>
+#include "wpamsg.h"
+#include "ui_peers.h"
+
+class WpaGui;
+
+class Peers : public QDialog, public Ui::Peers
+{
+	Q_OBJECT
+
+public:
+	Peers(QWidget *parent = 0, const char *name = 0,
+		    bool modal = false, Qt::WindowFlags fl = 0);
+	~Peers();
+	void setWpaGui(WpaGui *_wpagui);
+	void event_notify(WpaMsg msg);
+
+public slots:
+	virtual void context_menu(const QPoint &pos);
+	virtual void enter_pin();
+	virtual void connect_pbc();
+	virtual void learn_ap_config();
+	virtual void ctx_refresh();
+	virtual void ctx_p2p_start();
+	virtual void ctx_p2p_stop();
+	virtual void ctx_p2p_listen();
+	virtual void ctx_p2p_start_group();
+	virtual void ctx_p2p_remove_group();
+	virtual void ctx_p2p_connect();
+	virtual void ctx_p2p_req_pin();
+	virtual void ctx_p2p_show_pin();
+	virtual void ctx_p2p_display_pin();
+	virtual void ctx_p2p_display_pin_pd();
+	virtual void ctx_p2p_enter_pin();
+	virtual void properties();
+	virtual void ctx_hide_ap();
+	virtual void ctx_show_ap();
+	virtual void ctx_p2p_show_passphrase();
+	virtual void ctx_p2p_start_persistent();
+	virtual void ctx_p2p_invite();
+	virtual void ctx_p2p_delete();
+
+protected slots:
+	virtual void languageChange();
+	virtual void closeEvent(QCloseEvent *event);
+
+private:
+	void add_station(QString info);
+	void add_stations();
+	void add_single_station(const char *addr);
+	bool add_bss(const char *cmd);
+	void remove_bss(int id);
+	void add_scan_results();
+	void add_persistent(int id, const char *ssid, const char *bssid);
+	void add_persistent_groups();
+	void update_peers();
+	QStandardItem * find_addr(QString addr);
+	QStandardItem * find_addr_type(QString addr, int type);
+	void add_p2p_group_client(QStandardItem *parent, QString params);
+	QStandardItem * find_uuid(QString uuid);
+	void done(int r);
+	void remove_enrollee_uuid(QString uuid);
+	QString ItemType(int type);
+	void enable_persistent(int id);
+
+	WpaGui *wpagui;
+	QStandardItemModel model;
+	QIcon *default_icon;
+	QIcon *ap_icon;
+	QIcon *laptop_icon;
+	QIcon *group_icon;
+	QIcon *invitation_icon;
+	QStandardItem *ctx_item;
+
+	bool hide_ap;
+};
+
+#endif /* PEERS_H */
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/peers.ui b/hostap/wpa_supplicant/wpa_gui-qt4/peers.ui
new file mode 100644
index 0000000..9508c25
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/peers.ui
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Peers</class>
+ <widget class="QDialog" name="Peers">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Peers</string>
+  </property>
+  <layout class="QGridLayout">
+   <item row="0" column="0">
+    <widget class="QListView" name="peers">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="mouseTracking">
+      <bool>true</bool>
+     </property>
+     <property name="editTriggers">
+      <set>QAbstractItemView::NoEditTriggers</set>
+     </property>
+     <property name="viewMode">
+      <enum>QListView::IconMode</enum>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/scanresults.cpp b/hostap/wpa_supplicant/wpa_gui-qt4/scanresults.cpp
new file mode 100644
index 0000000..a2e3072
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/scanresults.cpp
@@ -0,0 +1,141 @@
+/*
+ * wpa_gui - ScanResults class
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <cstdio>
+
+#include "scanresults.h"
+#include "signalbar.h"
+#include "wpagui.h"
+#include "networkconfig.h"
+#include "scanresultsitem.h"
+
+
+ScanResults::ScanResults(QWidget *parent, const char *, bool, Qt::WindowFlags)
+	: QDialog(parent)
+{
+	setupUi(this);
+
+	connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
+	connect(scanButton, SIGNAL(clicked()), this, SLOT(scanRequest()));
+	connect(scanResultsWidget,
+		SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this,
+		SLOT(bssSelected(QTreeWidgetItem *)));
+
+	wpagui = NULL;
+	scanResultsWidget->setItemsExpandable(false);
+	scanResultsWidget->setRootIsDecorated(false);
+	scanResultsWidget->setItemDelegate(new SignalBar(scanResultsWidget));
+}
+
+
+ScanResults::~ScanResults()
+{
+}
+
+
+void ScanResults::languageChange()
+{
+	retranslateUi(this);
+}
+
+
+void ScanResults::setWpaGui(WpaGui *_wpagui)
+{
+	wpagui = _wpagui;
+	updateResults();
+}
+
+
+void ScanResults::updateResults()
+{
+	char reply[2048];
+	size_t reply_len;
+	int index;
+	char cmd[20];
+
+	scanResultsWidget->clear();
+
+	index = 0;
+	while (wpagui) {
+		snprintf(cmd, sizeof(cmd), "BSS %d", index++);
+		if (index > 1000)
+			break;
+
+		reply_len = sizeof(reply) - 1;
+		if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
+			break;
+		reply[reply_len] = '\0';
+
+		QString bss(reply);
+		if (bss.isEmpty() || bss.startsWith("FAIL"))
+			break;
+
+		QString ssid, bssid, freq, signal, flags;
+
+		QStringList lines = bss.split(QRegExp("\\n"));
+		for (QStringList::Iterator it = lines.begin();
+		     it != lines.end(); it++) {
+			int pos = (*it).indexOf('=') + 1;
+			if (pos < 1)
+				continue;
+
+			if ((*it).startsWith("bssid="))
+				bssid = (*it).mid(pos);
+			else if ((*it).startsWith("freq="))
+				freq = (*it).mid(pos);
+			else if ((*it).startsWith("level="))
+				signal = (*it).mid(pos);
+			else if ((*it).startsWith("flags="))
+				flags = (*it).mid(pos);
+			else if ((*it).startsWith("ssid="))
+				ssid = (*it).mid(pos);
+		}
+
+		ScanResultsItem *item = new ScanResultsItem(scanResultsWidget);
+		if (item) {
+			item->setText(0, ssid);
+			item->setText(1, bssid);
+			item->setText(2, freq);
+			item->setText(3, signal);
+			item->setText(4, flags);
+		}
+
+		if (bssid.isEmpty())
+			break;
+	}
+}
+
+
+void ScanResults::scanRequest()
+{
+	char reply[10];
+	size_t reply_len = sizeof(reply);
+    
+	if (wpagui == NULL)
+		return;
+    
+	wpagui->ctrlRequest("SCAN", reply, &reply_len);
+}
+
+
+void ScanResults::getResults()
+{
+	updateResults();
+}
+
+
+void ScanResults::bssSelected(QTreeWidgetItem *sel)
+{
+	NetworkConfig *nc = new NetworkConfig();
+	if (nc == NULL)
+		return;
+	nc->setWpaGui(wpagui);
+	nc->paramsFromScanResults(sel);
+	nc->show();
+	nc->exec();
+}
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/scanresults.h b/hostap/wpa_supplicant/wpa_gui-qt4/scanresults.h
new file mode 100644
index 0000000..2cddd13
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/scanresults.h
@@ -0,0 +1,40 @@
+/*
+ * wpa_gui - ScanResults class
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SCANRESULTS_H
+#define SCANRESULTS_H
+
+#include <QObject>
+#include "ui_scanresults.h"
+
+class WpaGui;
+
+class ScanResults : public QDialog, public Ui::ScanResults
+{
+	Q_OBJECT
+
+public:
+	ScanResults(QWidget *parent = 0, const char *name = 0,
+		    bool modal = false, Qt::WindowFlags fl = 0);
+	~ScanResults();
+
+public slots:
+	virtual void setWpaGui(WpaGui *_wpagui);
+	virtual void updateResults();
+	virtual void scanRequest();
+	virtual void getResults();
+	virtual void bssSelected(QTreeWidgetItem *sel);
+
+protected slots:
+	virtual void languageChange();
+
+private:
+	WpaGui *wpagui;
+};
+
+#endif /* SCANRESULTS_H */
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/scanresults.ui b/hostap/wpa_supplicant/wpa_gui-qt4/scanresults.ui
new file mode 100644
index 0000000..81e405e
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/scanresults.ui
@@ -0,0 +1,94 @@
+<ui version="4.0" >
+ <class>ScanResults</class>
+ <widget class="QDialog" name="ScanResults" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>452</width>
+    <height>244</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Scan results</string>
+  </property>
+  <layout class="QVBoxLayout" >
+   <item>
+    <widget class="QTreeWidget" name="scanResultsWidget" >
+     <property name="editTriggers" >
+      <set>QAbstractItemView::NoEditTriggers</set>
+     </property>
+     <property name="uniformRowHeights" >
+      <bool>true</bool>
+     </property>
+     <property name="sortingEnabled" >
+      <bool>true</bool>
+     </property>
+     <property name="columnCount" >
+      <number>5</number>
+     </property>
+     <column>
+      <property name="text" >
+       <string>SSID</string>
+      </property>
+     </column>
+     <column>
+      <property name="text" >
+       <string>BSSID</string>
+      </property>
+     </column>
+     <column>
+      <property name="text" >
+       <string>frequency</string>
+      </property>
+     </column>
+     <column>
+      <property name="text" >
+       <string>signal</string>
+      </property>
+     </column>
+     <column>
+      <property name="text" >
+       <string>flags</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" >
+     <item>
+      <spacer>
+       <property name="orientation" >
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" >
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="scanButton" >
+       <property name="text" >
+        <string>Scan</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="closeButton" >
+       <property name="text" >
+        <string>Close</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11" />
+ <pixmapfunction></pixmapfunction>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/scanresultsitem.cpp b/hostap/wpa_supplicant/wpa_gui-qt4/scanresultsitem.cpp
new file mode 100644
index 0000000..9cd937c
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/scanresultsitem.cpp
@@ -0,0 +1,18 @@
+/*
+ * wpa_gui - ScanResultsItem class
+ * Copyright (c) 2015, Adrian Nowicki <adinowicki@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "scanresultsitem.h"
+
+bool ScanResultsItem::operator< (const QTreeWidgetItem &other) const
+{
+	int sortCol = treeWidget()->sortColumn();
+	if (sortCol == 2 || sortCol == 3) {
+		return text(sortCol).toInt() < other.text(sortCol).toInt();
+	}
+	return text(sortCol) < other.text(sortCol);
+}
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/scanresultsitem.h b/hostap/wpa_supplicant/wpa_gui-qt4/scanresultsitem.h
new file mode 100644
index 0000000..74887ee
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/scanresultsitem.h
@@ -0,0 +1,21 @@
+/*
+ * wpa_gui - ScanResultsItem class
+ * Copyright (c) 2015, Adrian Nowicki <adinowicki@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SCANRESULTSITEM_H
+#define SCANRESULTSITEM_H
+
+#include <QTreeWidgetItem>
+
+class ScanResultsItem : public QTreeWidgetItem
+{
+public:
+	ScanResultsItem(QTreeWidget *tree) : QTreeWidgetItem(tree) {}
+	bool operator< (const QTreeWidgetItem &other) const;
+};
+
+#endif /* SCANRESULTSITEM_H */
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/signalbar.cpp b/hostap/wpa_supplicant/wpa_gui-qt4/signalbar.cpp
new file mode 100644
index 0000000..2bba582
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/signalbar.cpp
@@ -0,0 +1,58 @@
+/*
+ * wpa_gui - SignalBar class
+ * Copyright (c) 2011, Kel Modderman <kel@otaku42.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <cstdio>
+#include <qapplication.h>
+
+#include "signalbar.h"
+
+
+SignalBar::SignalBar(QObject *parent)
+	: QStyledItemDelegate(parent)
+{
+}
+
+
+SignalBar::~SignalBar()
+{
+}
+
+
+void SignalBar::paint(QPainter *painter,
+		      const QStyleOptionViewItem &option,
+		      const QModelIndex &index) const
+{
+	QStyleOptionProgressBar opts;
+	int signal;
+
+	if (index.column() != 3) {
+		QStyledItemDelegate::paint(painter, option, index);
+		return;
+	}
+
+	if (index.data().toInt() > 0)
+		signal = 0 - (256 - index.data().toInt());
+	else
+		signal = index.data().toInt();
+
+	opts.minimum = -95;
+	opts.maximum = -35;
+	if (signal < opts.minimum)
+		opts.progress = opts.minimum;
+	else if (signal > opts.maximum)
+		opts.progress = opts.maximum;
+	else
+		opts.progress = signal;
+
+	opts.text = QString::number(signal) + " dBm";
+	opts.textVisible = true;
+	opts.rect = option.rect;
+
+	QApplication::style()->drawControl(QStyle::CE_ProgressBar,
+					   &opts, painter);
+}
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/signalbar.h b/hostap/wpa_supplicant/wpa_gui-qt4/signalbar.h
new file mode 100644
index 0000000..37da5dd
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/signalbar.h
@@ -0,0 +1,28 @@
+/*
+ * wpa_gui - SignalBar class
+ * Copyright (c) 2011, Kel Modderman <kel@otaku42.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SIGNALBAR_H
+#define SIGNALBAR_H
+
+#include <QObject>
+#include <QStyledItemDelegate>
+
+class SignalBar : public QStyledItemDelegate
+{
+	Q_OBJECT
+
+public:
+	SignalBar(QObject *parent = 0);
+	~SignalBar();
+
+	virtual void paint(QPainter *painter,
+			   const QStyleOptionViewItem &option,
+			   const QModelIndex &index) const ;
+};
+
+#endif /* SIGNALBAR_H */
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/stringquery.cpp b/hostap/wpa_supplicant/wpa_gui-qt4/stringquery.cpp
new file mode 100644
index 0000000..420e0be
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/stringquery.cpp
@@ -0,0 +1,31 @@
+/*
+ * wpa_gui - StringQuery class
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <cstdio>
+#include <QLabel>
+
+#include "stringquery.h"
+
+
+StringQuery::StringQuery(QString label)
+{
+	edit = new QLineEdit;
+	edit->setFocus();
+	QGridLayout *layout = new QGridLayout;
+	layout->addWidget(new QLabel(label), 0, 0);
+	layout->addWidget(edit, 0, 1);
+	setLayout(layout);
+
+	connect(edit, SIGNAL(returnPressed()), this, SLOT(accept()));
+}
+
+
+QString StringQuery::get_string()
+{
+	return edit->text();
+}
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/stringquery.h b/hostap/wpa_supplicant/wpa_gui-qt4/stringquery.h
new file mode 100644
index 0000000..9d6bffd
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/stringquery.h
@@ -0,0 +1,28 @@
+/*
+ * wpa_gui - StringQuery class
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef STRINGQUERY_H
+#define STRINGQUERY_H
+
+#include <QDialog>
+#include <QLineEdit>
+#include <QGridLayout>
+
+class StringQuery : public QDialog
+{
+	Q_OBJECT
+
+public:
+	StringQuery(QString label);
+	QString get_string();
+
+private:
+	QLineEdit *edit;
+};
+
+#endif /* STRINGQUERY_H */
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp b/hostap/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp
new file mode 100644
index 0000000..9d933b0
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp
@@ -0,0 +1,94 @@
+/*
+ * wpa_gui - UserDataRequest class
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "userdatarequest.h"
+#include "wpagui.h"
+#include "common/wpa_ctrl.h"
+
+
+UserDataRequest::UserDataRequest(QWidget *parent, const char *, bool,
+				 Qt::WindowFlags)
+	: QDialog(parent)
+{
+	setupUi(this);
+
+	connect(buttonOk, SIGNAL(clicked()), this, SLOT(sendReply()));
+	connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject()));
+	connect(queryEdit, SIGNAL(returnPressed()), this, SLOT(sendReply()));
+}
+
+
+UserDataRequest::~UserDataRequest()
+{
+}
+
+
+void UserDataRequest::languageChange()
+{
+	retranslateUi(this);
+}
+
+
+int UserDataRequest::setParams(WpaGui *_wpagui, const char *reqMsg)
+{
+	char *tmp, *pos, *pos2;
+	wpagui = _wpagui;
+	tmp = strdup(reqMsg);
+	if (tmp == NULL)
+		return -1;
+	pos = strchr(tmp, '-');
+	if (pos == NULL) {
+		free(tmp);
+		return -1;
+	}
+	*pos++ = '\0';
+	field = tmp;
+	pos2 = strchr(pos, ':');
+	if (pos2 == NULL) {
+		free(tmp);
+		return -1;
+	}
+	*pos2++ = '\0';
+
+	networkid = atoi(pos);
+	queryInfo->setText(pos2);
+	if (strcmp(tmp, "PASSWORD") == 0) {
+		queryField->setText(tr("Password: "));
+		queryEdit->setEchoMode(QLineEdit::Password);
+	} else if (strcmp(tmp, "NEW_PASSWORD") == 0) {
+		queryField->setText(tr("New password: "));
+		queryEdit->setEchoMode(QLineEdit::Password);
+	} else if (strcmp(tmp, "IDENTITY") == 0)
+		queryField->setText(tr("Identity: "));
+	else if (strcmp(tmp, "PASSPHRASE") == 0) {
+		queryField->setText(tr("Private key passphrase: "));
+		queryEdit->setEchoMode(QLineEdit::Password);
+	} else
+		queryField->setText(field + ":");
+	free(tmp);
+
+	return 0;
+}
+
+
+void UserDataRequest::sendReply()
+{
+	char reply[10];
+	size_t reply_len = sizeof(reply);
+
+	if (wpagui == NULL) {
+		reject();
+		return;
+	}
+
+	QString cmd = QString(WPA_CTRL_RSP) + field + '-' +
+		QString::number(networkid) + ':' +
+		queryEdit->text();
+	wpagui->ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len);
+	accept();
+}
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/userdatarequest.h b/hostap/wpa_supplicant/wpa_gui-qt4/userdatarequest.h
new file mode 100644
index 0000000..b6d1ad2
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/userdatarequest.h
@@ -0,0 +1,40 @@
+/*
+ * wpa_gui - UserDataRequest class
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef USERDATAREQUEST_H
+#define USERDATAREQUEST_H
+
+#include <QObject>
+#include "ui_userdatarequest.h"
+
+class WpaGui;
+
+class UserDataRequest : public QDialog, public Ui::UserDataRequest
+{
+	Q_OBJECT
+
+public:
+	UserDataRequest(QWidget *parent = 0, const char *name = 0,
+			bool modal = false, Qt::WindowFlags fl = 0);
+	~UserDataRequest();
+
+	int setParams(WpaGui *_wpagui, const char *reqMsg);
+
+public slots:
+	virtual void sendReply();
+
+protected slots:
+	virtual void languageChange();
+
+private:
+	WpaGui *wpagui;
+	int networkid;
+	QString field;
+};
+
+#endif /* USERDATAREQUEST_H */
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui b/hostap/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui
new file mode 100644
index 0000000..1de2a26
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui
@@ -0,0 +1,109 @@
+<ui version="4.0" stdsetdef="1" >
+  <author></author>
+  <comment></comment>
+  <exportmacro></exportmacro>
+  <class>UserDataRequest</class>
+  <widget class="QDialog" name="UserDataRequest" >
+    <property name="geometry" >
+      <rect>
+        <x>0</x>
+        <y>0</y>
+        <width>216</width>
+        <height>103</height>
+      </rect>
+    </property>
+    <property name="windowTitle" >
+      <string>Authentication credentials required</string>
+    </property>
+    <property name="sizeGripEnabled" >
+      <bool>true</bool>
+    </property>
+    <layout class="QVBoxLayout" >
+      <item>
+        <widget class="QLabel" name="queryInfo" >
+          <property name="text" >
+            <string/>
+          </property>
+        </widget>
+      </item>
+      <item>
+        <layout class="QHBoxLayout" >
+          <property name="margin" >
+            <number>0</number>
+          </property>
+          <item>
+            <widget class="QLabel" name="queryField" >
+              <property name="text" >
+                <string/>
+              </property>
+            </widget>
+          </item>
+          <item>
+            <widget class="QLineEdit" name="queryEdit" >
+              <property name="enabled" >
+                <bool>true</bool>
+              </property>
+              <property name="echoMode" >
+                <enum>QLineEdit::Password</enum>
+              </property>
+            </widget>
+          </item>
+        </layout>
+      </item>
+      <item>
+        <layout class="QHBoxLayout" >
+          <property name="margin" >
+            <number>0</number>
+          </property>
+          <item>
+            <spacer name="spacer4" >
+              <property name="sizeHint" >
+                <size>
+                  <width>20</width>
+                  <height>20</height>
+                </size>
+              </property>
+              <property name="sizeType" >
+                <enum>Expanding</enum>
+              </property>
+              <property name="orientation" >
+                <enum>Horizontal</enum>
+              </property>
+            </spacer>
+          </item>
+          <item>
+            <widget class="QPushButton" name="buttonOk" >
+              <property name="text" >
+                <string>&amp;OK</string>
+              </property>
+              <property name="shortcut" >
+                <string/>
+              </property>
+              <property name="autoDefault" >
+                <bool>true</bool>
+              </property>
+              <property name="default" >
+                <bool>true</bool>
+              </property>
+            </widget>
+          </item>
+          <item>
+            <widget class="QPushButton" name="buttonCancel" >
+              <property name="text" >
+                <string>&amp;Cancel</string>
+              </property>
+              <property name="shortcut" >
+                <string/>
+              </property>
+              <property name="autoDefault" >
+                <bool>true</bool>
+              </property>
+            </widget>
+          </item>
+        </layout>
+      </item>
+    </layout>
+  </widget>
+  <layoutdefault spacing="6" margin="11" />
+  <pixmapfunction></pixmapfunction>
+</ui>
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop b/hostap/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop
new file mode 100644
index 0000000..ccc7d87
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Version=1.0
+Name=wpa_gui
+Comment=Graphical user interface for wpa_supplicant
+Exec=wpa_gui
+Icon=wpa_gui
+GenericName=wpa_supplicant user interface
+Terminal=false
+Type=Application
+Categories=Qt;Network;
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro b/hostap/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro
new file mode 100644
index 0000000..3fa734b
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro
@@ -0,0 +1,73 @@
+TEMPLATE	= app
+LANGUAGE	= C++
+TRANSLATIONS	= lang/wpa_gui_de.ts
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+CONFIG	+= qt warn_on release
+
+DEFINES += CONFIG_CTRL_IFACE
+
+win32 {
+  LIBS += -lws2_32 -static
+  DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE
+  SOURCES += ../../src/utils/os_win32.c
+} else:win32-g++ {
+  # cross compilation to win32
+  LIBS += -lws2_32 -static -mwindows
+  DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE
+  SOURCES += ../../src/utils/os_win32.c
+  RESOURCES += icons_png.qrc
+} else:win32-x-g++ {
+  # cross compilation to win32
+  LIBS += -lws2_32 -static -mwindows
+  DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE
+  DEFINES += _X86_
+  SOURCES += ../../src/utils/os_win32.c
+  RESOURCES += icons_png.qrc
+} else {
+  DEFINES += CONFIG_CTRL_IFACE_UNIX
+  SOURCES += ../../src/utils/os_unix.c
+}
+
+INCLUDEPATH	+= . .. ../../src ../../src/utils
+
+HEADERS	+= wpamsg.h \
+	wpagui.h \
+	eventhistory.h \
+	scanresults.h \
+	scanresultsitem.h \
+	signalbar.h \
+	userdatarequest.h \
+	networkconfig.h \
+	addinterface.h \
+	peers.h \
+	stringquery.h
+
+SOURCES	+= main.cpp \
+	wpagui.cpp \
+	eventhistory.cpp \
+	scanresults.cpp \
+	scanresultsitem.cpp \
+	signalbar.cpp \
+	userdatarequest.cpp \
+	networkconfig.cpp \
+	addinterface.cpp \
+	peers.cpp \
+	stringquery.cpp \
+	../../src/common/wpa_ctrl.c
+
+RESOURCES += icons.qrc
+
+FORMS	= wpagui.ui \
+	eventhistory.ui \
+	scanresults.ui \
+	userdatarequest.ui \
+	networkconfig.ui \
+	peers.ui
+
+
+unix {
+  UI_DIR = .ui
+  MOC_DIR = .moc
+  OBJECTS_DIR = .obj
+}
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/hostap/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
new file mode 100644
index 0000000..a0aa05e
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
@@ -0,0 +1,1898 @@
+/*
+ * wpa_gui - WpaGui class
+ * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <windows.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#include <cstdio>
+#include <unistd.h>
+#include <QMessageBox>
+#include <QCloseEvent>
+#include <QImageReader>
+#include <QSettings>
+
+#include "wpagui.h"
+#include "dirent.h"
+#include "common/wpa_ctrl.h"
+#include "userdatarequest.h"
+#include "networkconfig.h"
+
+
+#ifndef QT_NO_DEBUG
+#define debug(M, ...) qDebug("DEBUG %d: " M, __LINE__, ##__VA_ARGS__)
+#else
+#define debug(M, ...) do {} while (0)
+#endif
+
+
+WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *,
+	       Qt::WindowFlags)
+	: QMainWindow(parent), app(_app)
+{
+	setupUi(this);
+	this->setWindowFlags(Qt::Dialog);
+
+#ifdef CONFIG_NATIVE_WINDOWS
+	fileStopServiceAction = new QAction(this);
+	fileStopServiceAction->setObjectName("Stop Service");
+	fileStopServiceAction->setIconText(tr("Stop Service"));
+	fileMenu->insertAction(actionWPS, fileStopServiceAction);
+
+	fileStartServiceAction = new QAction(this);
+	fileStartServiceAction->setObjectName("Start Service");
+	fileStartServiceAction->setIconText(tr("Start Service"));
+	fileMenu->insertAction(fileStopServiceAction, fileStartServiceAction);
+
+	connect(fileStartServiceAction, SIGNAL(triggered()), this,
+		SLOT(startService()));
+	connect(fileStopServiceAction, SIGNAL(triggered()), this,
+		SLOT(stopService()));
+
+	addInterfaceAction = new QAction(this);
+	addInterfaceAction->setIconText(tr("Add Interface"));
+	fileMenu->insertAction(fileStartServiceAction, addInterfaceAction);
+
+	connect(addInterfaceAction, SIGNAL(triggered()), this,
+		SLOT(addInterface()));
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	(void) statusBar();
+
+	/*
+	 * Disable WPS tab by default; it will be enabled if wpa_supplicant is
+	 * built with WPS support.
+	 */
+	wpsTab->setEnabled(false);
+	wpaguiTab->setTabEnabled(wpaguiTab->indexOf(wpsTab), false);
+
+	connect(fileEventHistoryAction, SIGNAL(triggered()), this,
+		SLOT(eventHistory()));
+	connect(fileSaveConfigAction, SIGNAL(triggered()), this,
+		SLOT(saveConfig()));
+	connect(actionWPS, SIGNAL(triggered()), this, SLOT(wpsDialog()));
+	connect(actionPeers, SIGNAL(triggered()), this, SLOT(peersDialog()));
+	connect(fileExitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
+	connect(networkAddAction, SIGNAL(triggered()), this,
+		SLOT(addNetwork()));
+	connect(networkEditAction, SIGNAL(triggered()), this,
+		SLOT(editSelectedNetwork()));
+	connect(networkRemoveAction, SIGNAL(triggered()), this,
+		SLOT(removeSelectedNetwork()));
+	connect(networkEnableAllAction, SIGNAL(triggered()), this,
+		SLOT(enableAllNetworks()));
+	connect(networkDisableAllAction, SIGNAL(triggered()), this,
+		SLOT(disableAllNetworks()));
+	connect(networkRemoveAllAction, SIGNAL(triggered()), this,
+		SLOT(removeAllNetworks()));
+	connect(helpIndexAction, SIGNAL(triggered()), this, SLOT(helpIndex()));
+	connect(helpContentsAction, SIGNAL(triggered()), this,
+		SLOT(helpContents()));
+	connect(helpAboutAction, SIGNAL(triggered()), this, SLOT(helpAbout()));
+	connect(disconnectButton, SIGNAL(clicked()), this, SLOT(disconnect()));
+	connect(scanButton, SIGNAL(clicked()), this, SLOT(scan()));
+	connect(connectButton, SIGNAL(clicked()), this, SLOT(connectB()));
+	connect(adapterSelect, SIGNAL(activated(const QString&)), this,
+		SLOT(selectAdapter(const QString&)));
+	connect(networkSelect, SIGNAL(activated(const QString&)), this,
+		SLOT(selectNetwork(const QString&)));
+	connect(addNetworkButton, SIGNAL(clicked()), this, SLOT(addNetwork()));
+	connect(editNetworkButton, SIGNAL(clicked()), this,
+		SLOT(editListedNetwork()));
+	connect(removeNetworkButton, SIGNAL(clicked()), this,
+		SLOT(removeListedNetwork()));
+	connect(networkList, SIGNAL(itemSelectionChanged()), this,
+		SLOT(updateNetworkDisabledStatus()));
+	connect(enableRadioButton, SIGNAL(toggled(bool)), this,
+		SLOT(enableListedNetwork(bool)));
+	connect(disableRadioButton, SIGNAL(toggled(bool)), this,
+		SLOT(disableListedNetwork(bool)));
+	connect(scanNetworkButton, SIGNAL(clicked()), this, SLOT(scan()));
+	connect(networkList, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
+		this, SLOT(editListedNetwork()));
+	connect(wpaguiTab, SIGNAL(currentChanged(int)), this,
+		SLOT(tabChanged(int)));
+	connect(wpsPbcButton, SIGNAL(clicked()), this, SLOT(wpsPbc()));
+	connect(wpsPinButton, SIGNAL(clicked()), this, SLOT(wpsGeneratePin()));
+	connect(wpsApPinEdit, SIGNAL(textChanged(const QString &)), this,
+		SLOT(wpsApPinChanged(const QString &)));
+	connect(wpsApPinButton, SIGNAL(clicked()), this, SLOT(wpsApPin()));
+
+	eh = NULL;
+	scanres = NULL;
+	peers = NULL;
+	add_iface = NULL;
+	udr = NULL;
+	tray_icon = NULL;
+	startInTray = false;
+	quietMode = false;
+	ctrl_iface = NULL;
+	ctrl_conn = NULL;
+	monitor_conn = NULL;
+	msgNotifier = NULL;
+	ctrl_iface_dir = strdup("/var/run/wpa_supplicant");
+	signalMeterInterval = 0;
+
+	parse_argv();
+
+#ifndef QT_NO_SESSIONMANAGER
+	if (app->isSessionRestored()) {
+		QSettings settings("wpa_supplicant", "wpa_gui");
+		settings.beginGroup("state");
+		if (app->sessionId().compare(settings.value("session_id").
+					     toString()) == 0)
+			startInTray = settings.value("in_tray").toBool();
+		settings.endGroup();
+	}
+#endif
+
+	if (QSystemTrayIcon::isSystemTrayAvailable())
+		createTrayIcon(startInTray);
+	else
+		show();
+
+	connectedToService = false;
+	textStatus->setText(tr("connecting to wpa_supplicant"));
+	timer = new QTimer(this);
+	connect(timer, SIGNAL(timeout()), SLOT(ping()));
+	timer->setSingleShot(false);
+	timer->start(1000);
+
+	signalMeterTimer = new QTimer(this);
+	signalMeterTimer->setInterval(signalMeterInterval);
+	connect(signalMeterTimer, SIGNAL(timeout()), SLOT(signalMeterUpdate()));
+
+	if (openCtrlConnection(ctrl_iface) < 0) {
+		debug("Failed to open control connection to "
+		      "wpa_supplicant.");
+	}
+
+	updateStatus();
+	networkMayHaveChanged = true;
+	updateNetworks();
+}
+
+
+WpaGui::~WpaGui()
+{
+	delete msgNotifier;
+
+	if (monitor_conn) {
+		wpa_ctrl_detach(monitor_conn);
+		wpa_ctrl_close(monitor_conn);
+		monitor_conn = NULL;
+	}
+	if (ctrl_conn) {
+		wpa_ctrl_close(ctrl_conn);
+		ctrl_conn = NULL;
+	}
+
+	if (eh) {
+		eh->close();
+		delete eh;
+		eh = NULL;
+	}
+
+	if (scanres) {
+		scanres->close();
+		delete scanres;
+		scanres = NULL;
+	}
+
+	if (peers) {
+		peers->close();
+		delete peers;
+		peers = NULL;
+	}
+
+	if (add_iface) {
+		add_iface->close();
+		delete add_iface;
+		add_iface = NULL;
+	}
+
+	if (udr) {
+		udr->close();
+		delete udr;
+		udr = NULL;
+	}
+
+	free(ctrl_iface);
+	ctrl_iface = NULL;
+
+	free(ctrl_iface_dir);
+	ctrl_iface_dir = NULL;
+}
+
+
+void WpaGui::languageChange()
+{
+	retranslateUi(this);
+}
+
+
+void WpaGui::parse_argv()
+{
+	int c;
+	WpaGuiApp *app = qobject_cast<WpaGuiApp*>(qApp);
+	for (;;) {
+		c = getopt(app->argc, app->argv, "i:m:p:tq");
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'i':
+			free(ctrl_iface);
+			ctrl_iface = strdup(optarg);
+			break;
+		case 'm':
+			signalMeterInterval = atoi(optarg) * 1000;
+			break;
+		case 'p':
+			free(ctrl_iface_dir);
+			ctrl_iface_dir = strdup(optarg);
+			break;
+		case 't':
+			startInTray = true;
+			break;
+		case 'q':
+			quietMode = true;
+			break;
+		}
+	}
+}
+
+
+int WpaGui::openCtrlConnection(const char *ifname)
+{
+	char *cfile;
+	int flen;
+	char buf[2048], *pos, *pos2;
+	size_t len;
+
+	if (ifname) {
+		if (ifname != ctrl_iface) {
+			free(ctrl_iface);
+			ctrl_iface = strdup(ifname);
+		}
+	} else {
+#ifdef CONFIG_CTRL_IFACE_UDP
+		free(ctrl_iface);
+		ctrl_iface = strdup("udp");
+#endif /* CONFIG_CTRL_IFACE_UDP */
+#ifdef CONFIG_CTRL_IFACE_UNIX
+		struct dirent *dent;
+		DIR *dir = opendir(ctrl_iface_dir);
+		free(ctrl_iface);
+		ctrl_iface = NULL;
+		if (dir) {
+			while ((dent = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+				/* Skip the file if it is not a socket.
+				 * Also accept DT_UNKNOWN (0) in case
+				 * the C library or underlying file
+				 * system does not support d_type. */
+				if (dent->d_type != DT_SOCK &&
+				    dent->d_type != DT_UNKNOWN)
+					continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+
+				if (strcmp(dent->d_name, ".") == 0 ||
+				    strcmp(dent->d_name, "..") == 0)
+					continue;
+				debug("Selected interface '%s'",
+				      dent->d_name);
+				ctrl_iface = strdup(dent->d_name);
+				break;
+			}
+			closedir(dir);
+		}
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+		struct wpa_ctrl *ctrl;
+		int ret;
+
+		free(ctrl_iface);
+		ctrl_iface = NULL;
+
+		ctrl = wpa_ctrl_open(NULL);
+		if (ctrl) {
+			len = sizeof(buf) - 1;
+			ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf,
+					       &len, NULL);
+			if (ret >= 0) {
+				connectedToService = true;
+				buf[len] = '\0';
+				pos = strchr(buf, '\n');
+				if (pos)
+					*pos = '\0';
+				ctrl_iface = strdup(buf);
+			}
+			wpa_ctrl_close(ctrl);
+		}
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+	}
+
+	if (ctrl_iface == NULL) {
+#ifdef CONFIG_NATIVE_WINDOWS
+		static bool first = true;
+		if (first && !serviceRunning()) {
+			first = false;
+			if (QMessageBox::warning(
+				    this, qAppName(),
+				    tr("wpa_supplicant service is not "
+				       "running.\n"
+				       "Do you want to start it?"),
+				    QMessageBox::Yes | QMessageBox::No) ==
+			    QMessageBox::Yes)
+				startService();
+		}
+#endif /* CONFIG_NATIVE_WINDOWS */
+		return -1;
+	}
+
+#ifdef CONFIG_CTRL_IFACE_UNIX
+	flen = strlen(ctrl_iface_dir) + strlen(ctrl_iface) + 2;
+	cfile = (char *) malloc(flen);
+	if (cfile == NULL)
+		return -1;
+	snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ctrl_iface);
+#else /* CONFIG_CTRL_IFACE_UNIX */
+	flen = strlen(ctrl_iface) + 1;
+	cfile = (char *) malloc(flen);
+	if (cfile == NULL)
+		return -1;
+	snprintf(cfile, flen, "%s", ctrl_iface);
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+
+	if (ctrl_conn) {
+		wpa_ctrl_close(ctrl_conn);
+		ctrl_conn = NULL;
+	}
+
+	if (monitor_conn) {
+		delete msgNotifier;
+		msgNotifier = NULL;
+		wpa_ctrl_detach(monitor_conn);
+		wpa_ctrl_close(monitor_conn);
+		monitor_conn = NULL;
+	}
+
+	debug("Trying to connect to '%s'", cfile);
+	ctrl_conn = wpa_ctrl_open(cfile);
+	if (ctrl_conn == NULL) {
+		free(cfile);
+		return -1;
+	}
+	monitor_conn = wpa_ctrl_open(cfile);
+	free(cfile);
+	if (monitor_conn == NULL) {
+		wpa_ctrl_close(ctrl_conn);
+		return -1;
+	}
+	if (wpa_ctrl_attach(monitor_conn)) {
+		debug("Failed to attach to wpa_supplicant");
+		wpa_ctrl_close(monitor_conn);
+		monitor_conn = NULL;
+		wpa_ctrl_close(ctrl_conn);
+		ctrl_conn = NULL;
+		return -1;
+	}
+
+#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP)
+	msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn),
+					  QSocketNotifier::Read, this);
+	connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs()));
+#endif
+
+	adapterSelect->clear();
+	adapterSelect->addItem(ctrl_iface);
+	adapterSelect->setCurrentIndex(0);
+
+	len = sizeof(buf) - 1;
+	if (wpa_ctrl_request(ctrl_conn, "INTERFACES", 10, buf, &len, NULL) >=
+	    0) {
+		buf[len] = '\0';
+		pos = buf;
+		while (*pos) {
+			pos2 = strchr(pos, '\n');
+			if (pos2)
+				*pos2 = '\0';
+			if (strcmp(pos, ctrl_iface) != 0)
+				adapterSelect->addItem(pos);
+			if (pos2)
+				pos = pos2 + 1;
+			else
+				break;
+		}
+	}
+
+	len = sizeof(buf) - 1;
+	if (wpa_ctrl_request(ctrl_conn, "GET_CAPABILITY eap", 18, buf, &len,
+			     NULL) >= 0) {
+		buf[len] = '\0';
+
+		QString res(buf);
+		QStringList types = res.split(QChar(' '));
+		bool wps = types.contains("WSC");
+		actionWPS->setEnabled(wps);
+		wpsTab->setEnabled(wps);
+		wpaguiTab->setTabEnabled(wpaguiTab->indexOf(wpsTab), wps);
+	}
+
+	return 0;
+}
+
+
+int WpaGui::ctrlRequest(const char *cmd, char *buf, size_t *buflen)
+{
+	int ret;
+
+	if (ctrl_conn == NULL)
+		return -3;
+	ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, buflen, NULL);
+	if (ret == -2)
+		debug("'%s' command timed out.", cmd);
+	else if (ret < 0)
+		debug("'%s' command failed.", cmd);
+
+	return ret;
+}
+
+
+QString WpaGui::wpaStateTranslate(char *state)
+{
+	if (!strcmp(state, "DISCONNECTED"))
+		return tr("Disconnected");
+	else if (!strcmp(state, "INACTIVE"))
+		return tr("Inactive");
+	else if (!strcmp(state, "SCANNING"))
+		return tr("Scanning");
+	else if (!strcmp(state, "AUTHENTICATING"))
+		return tr("Authenticating");
+	else if (!strcmp(state, "ASSOCIATING"))
+		return tr("Associating");
+	else if (!strcmp(state, "ASSOCIATED"))
+		return tr("Associated");
+	else if (!strcmp(state, "4WAY_HANDSHAKE"))
+		return tr("4-Way Handshake");
+	else if (!strcmp(state, "GROUP_HANDSHAKE"))
+		return tr("Group Handshake");
+	else if (!strcmp(state, "COMPLETED"))
+		return tr("Completed");
+	else
+		return tr("Unknown");
+}
+
+
+void WpaGui::updateStatus()
+{
+	char buf[2048], *start, *end, *pos;
+	size_t len;
+
+	pingsToStatusUpdate = 10;
+
+	len = sizeof(buf) - 1;
+	if (ctrl_conn == NULL || ctrlRequest("STATUS", buf, &len) < 0) {
+		textStatus->setText(tr("Could not get status from "
+				       "wpa_supplicant"));
+		textAuthentication->clear();
+		textEncryption->clear();
+		textSsid->clear();
+		textBssid->clear();
+		textIpAddress->clear();
+		updateTrayToolTip(tr("no status information"));
+		updateTrayIcon(TrayIconOffline);
+		signalMeterTimer->stop();
+
+#ifdef CONFIG_NATIVE_WINDOWS
+		static bool first = true;
+		if (first && connectedToService &&
+		    (ctrl_iface == NULL || *ctrl_iface == '\0')) {
+			first = false;
+			if (QMessageBox::information(
+				    this, qAppName(),
+				    tr("No network interfaces in use.\n"
+				       "Would you like to add one?"),
+				    QMessageBox::Yes | QMessageBox::No) ==
+			    QMessageBox::Yes)
+				addInterface();
+		}
+#endif /* CONFIG_NATIVE_WINDOWS */
+		return;
+	}
+
+	buf[len] = '\0';
+
+	bool auth_updated = false, ssid_updated = false;
+	bool bssid_updated = false, ipaddr_updated = false;
+	bool status_updated = false;
+	char *pairwise_cipher = NULL, *group_cipher = NULL;
+	char *mode = NULL;
+
+	start = buf;
+	while (*start) {
+		bool last = false;
+		end = strchr(start, '\n');
+		if (end == NULL) {
+			last = true;
+			end = start;
+			while (end[0] && end[1])
+				end++;
+		}
+		*end = '\0';
+
+		pos = strchr(start, '=');
+		if (pos) {
+			*pos++ = '\0';
+			if (strcmp(start, "bssid") == 0) {
+				bssid_updated = true;
+				textBssid->setText(pos);
+			} else if (strcmp(start, "ssid") == 0) {
+				ssid_updated = true;
+				textSsid->setText(pos);
+				updateTrayToolTip(pos + tr(" (associated)"));
+				if (!signalMeterInterval) {
+					/* if signal meter is not enabled show
+					 * full signal strength */
+					updateTrayIcon(TrayIconSignalExcellent);
+				}
+			} else if (strcmp(start, "ip_address") == 0) {
+				ipaddr_updated = true;
+				textIpAddress->setText(pos);
+			} else if (strcmp(start, "wpa_state") == 0) {
+				status_updated = true;
+				textStatus->setText(wpaStateTranslate(pos));
+			} else if (strcmp(start, "key_mgmt") == 0) {
+				auth_updated = true;
+				textAuthentication->setText(pos);
+				/* TODO: could add EAP status to this */
+			} else if (strcmp(start, "pairwise_cipher") == 0) {
+				pairwise_cipher = pos;
+			} else if (strcmp(start, "group_cipher") == 0) {
+				group_cipher = pos;
+			} else if (strcmp(start, "mode") == 0) {
+				mode = pos;
+			}
+		}
+
+		if (last)
+			break;
+		start = end + 1;
+	}
+	if (status_updated && mode)
+		textStatus->setText(textStatus->text() + " (" + mode + ")");
+
+	if (pairwise_cipher || group_cipher) {
+		QString encr;
+		if (pairwise_cipher && group_cipher &&
+		    strcmp(pairwise_cipher, group_cipher) != 0) {
+			encr.append(pairwise_cipher);
+			encr.append(" + ");
+			encr.append(group_cipher);
+		} else if (pairwise_cipher) {
+			encr.append(pairwise_cipher);
+		} else {
+			encr.append(group_cipher);
+			encr.append(" [group key only]");
+		}
+		textEncryption->setText(encr);
+	} else
+		textEncryption->clear();
+
+	if (signalMeterInterval) {
+		/*
+		 * Handle signal meter service. When network is not associated,
+		 * deactivate timer, otherwise keep it going. Tray icon has to
+		 * be initialized here, because of the initial delay of the
+		 * timer.
+		 */
+		if (ssid_updated) {
+			if (!signalMeterTimer->isActive()) {
+				updateTrayIcon(TrayIconConnected);
+				signalMeterTimer->start();
+			}
+		} else {
+			signalMeterTimer->stop();
+		}
+	}
+
+	if (!status_updated)
+		textStatus->clear();
+	if (!auth_updated)
+		textAuthentication->clear();
+	if (!ssid_updated) {
+		textSsid->clear();
+		updateTrayToolTip(tr("(not-associated)"));
+		updateTrayIcon(TrayIconOffline);
+	}
+	if (!bssid_updated)
+		textBssid->clear();
+	if (!ipaddr_updated)
+		textIpAddress->clear();
+}
+
+
+void WpaGui::updateNetworks()
+{
+	char buf[4096], *start, *end, *id, *ssid, *bssid, *flags;
+	size_t len;
+	int first_active = -1;
+	int was_selected = -1;
+	bool current = false;
+
+	if (!networkMayHaveChanged)
+		return;
+
+	if (networkList->currentRow() >= 0)
+		was_selected = networkList->currentRow();
+
+	networkSelect->clear();
+	networkList->clear();
+
+	if (ctrl_conn == NULL)
+		return;
+
+	len = sizeof(buf) - 1;
+	if (ctrlRequest("LIST_NETWORKS", buf, &len) < 0)
+		return;
+
+	buf[len] = '\0';
+	start = strchr(buf, '\n');
+	if (start == NULL)
+		return;
+	start++;
+
+	while (*start) {
+		bool last = false;
+		end = strchr(start, '\n');
+		if (end == NULL) {
+			last = true;
+			end = start;
+			while (end[0] && end[1])
+				end++;
+		}
+		*end = '\0';
+
+		id = start;
+		ssid = strchr(id, '\t');
+		if (ssid == NULL)
+			break;
+		*ssid++ = '\0';
+		bssid = strchr(ssid, '\t');
+		if (bssid == NULL)
+			break;
+		*bssid++ = '\0';
+		flags = strchr(bssid, '\t');
+		if (flags == NULL)
+			break;
+		*flags++ = '\0';
+
+		if (strstr(flags, "[DISABLED][P2P-PERSISTENT]")) {
+			if (last)
+				break;
+			start = end + 1;
+			continue;
+		}
+
+		QString network(id);
+		network.append(": ");
+		network.append(ssid);
+		networkSelect->addItem(network);
+		networkList->addItem(network);
+
+		if (strstr(flags, "[CURRENT]")) {
+			networkSelect->setCurrentIndex(networkSelect->count() -
+						      1);
+			current = true;
+		} else if (first_active < 0 &&
+			   strstr(flags, "[DISABLED]") == NULL)
+			first_active = networkSelect->count() - 1;
+
+		if (last)
+			break;
+		start = end + 1;
+	}
+
+	if (networkSelect->count() > 1)
+		networkSelect->addItem(tr("Select any network"));
+
+	if (!current && first_active >= 0)
+		networkSelect->setCurrentIndex(first_active);
+
+	if (was_selected >= 0 && networkList->count() > 0) {
+		if (was_selected < networkList->count())
+			networkList->setCurrentRow(was_selected);
+		else
+			networkList->setCurrentRow(networkList->count() - 1);
+	}
+	else
+		networkList->setCurrentRow(networkSelect->currentIndex());
+
+	networkMayHaveChanged = false;
+}
+
+
+void WpaGui::helpIndex()
+{
+	debug("helpIndex");
+}
+
+
+void WpaGui::helpContents()
+{
+	debug("helpContents");
+}
+
+
+void WpaGui::helpAbout()
+{
+	QMessageBox::about(this, "wpa_gui for wpa_supplicant",
+			   "Copyright (c) 2003-2015,\n"
+			   "Jouni Malinen <j@w1.fi>\n"
+			   "and contributors.\n"
+			   "\n"
+			   "This software may be distributed under\n"
+			   "the terms of the BSD license.\n"
+			   "See README for more details.\n"
+			   "\n"
+			   "This product includes software developed\n"
+			   "by the OpenSSL Project for use in the\n"
+			   "OpenSSL Toolkit (http://www.openssl.org/)\n");
+}
+
+
+void WpaGui::disconnect()
+{
+	char reply[10];
+	size_t reply_len = sizeof(reply);
+	ctrlRequest("DISCONNECT", reply, &reply_len);
+	stopWpsRun(false);
+}
+
+
+void WpaGui::scan()
+{
+	if (scanres) {
+		scanres->close();
+		delete scanres;
+	}
+
+	scanres = new ScanResults();
+	if (scanres == NULL)
+		return;
+	scanres->setWpaGui(this);
+	scanres->show();
+	scanres->exec();
+}
+
+
+void WpaGui::eventHistory()
+{
+	if (eh) {
+		eh->close();
+		delete eh;
+	}
+
+	eh = new EventHistory();
+	if (eh == NULL)
+		return;
+	eh->addEvents(msgs);
+	eh->show();
+	eh->exec();
+}
+
+
+void WpaGui::ping()
+{
+	char buf[10];
+	size_t len;
+
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+	/*
+	 * QSocketNotifier cannot be used with Windows named pipes, so use a
+	 * timer to check for received messages for now. This could be
+	 * optimized be doing something specific to named pipes or Windows
+	 * events, but it is not clear what would be the best way of doing that
+	 * in Qt.
+	 */
+	receiveMsgs();
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+
+	if (scanres && !scanres->isVisible()) {
+		delete scanres;
+		scanres = NULL;
+	}
+
+	if (eh && !eh->isVisible()) {
+		delete eh;
+		eh = NULL;
+	}
+
+	if (udr && !udr->isVisible()) {
+		delete udr;
+		udr = NULL;
+	}
+
+	len = sizeof(buf) - 1;
+	if (ctrlRequest("PING", buf, &len) < 0) {
+		debug("PING failed - trying to reconnect");
+		if (openCtrlConnection(ctrl_iface) >= 0) {
+			debug("Reconnected successfully");
+			pingsToStatusUpdate = 0;
+		}
+	}
+
+	pingsToStatusUpdate--;
+	if (pingsToStatusUpdate <= 0) {
+		updateStatus();
+		updateNetworks();
+	}
+
+#ifndef CONFIG_CTRL_IFACE_NAMED_PIPE
+	/* Use less frequent pings and status updates when the main window is
+	 * hidden (running in taskbar). */
+	int interval = isHidden() ? 5000 : 1000;
+	if (timer->interval() != interval)
+		timer->setInterval(interval);
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+}
+
+
+void WpaGui::signalMeterUpdate()
+{
+	char reply[128];
+	size_t reply_len = sizeof(reply);
+	char *rssi;
+	int rssi_value;
+
+	ctrlRequest("SIGNAL_POLL", reply, &reply_len);
+
+	/* In order to eliminate signal strength fluctuations, try
+	 * to obtain averaged RSSI value in the first place. */
+	if ((rssi = strstr(reply, "AVG_RSSI=")) != NULL)
+		rssi_value = atoi(&rssi[sizeof("AVG_RSSI")]);
+	else if ((rssi = strstr(reply, "RSSI=")) != NULL)
+		rssi_value = atoi(&rssi[sizeof("RSSI")]);
+	else {
+		debug("Failed to get RSSI value");
+		updateTrayIcon(TrayIconSignalNone);
+		return;
+	}
+
+	debug("RSSI value: %d", rssi_value);
+
+	/*
+	 * NOTE: The code below assumes, that the unit of the value returned
+	 * by the SIGNAL POLL request is dBm. It might not be true for all
+	 * wpa_supplicant drivers.
+	 */
+
+	/*
+	 * Calibration is based on "various Internet sources". Nonetheless,
+	 * it seems to be compatible with the Windows 8.1 strength meter -
+	 * tested on Intel Centrino Advanced-N 6235.
+	 */
+	if (rssi_value >= -60)
+		updateTrayIcon(TrayIconSignalExcellent);
+	else if (rssi_value >= -68)
+		updateTrayIcon(TrayIconSignalGood);
+	else if (rssi_value >= -76)
+		updateTrayIcon(TrayIconSignalOk);
+	else if (rssi_value >= -84)
+		updateTrayIcon(TrayIconSignalWeak);
+	else
+		updateTrayIcon(TrayIconSignalNone);
+}
+
+
+static int str_match(const char *a, const char *b)
+{
+	return strncmp(a, b, strlen(b)) == 0;
+}
+
+
+void WpaGui::processMsg(char *msg)
+{
+	char *pos = msg, *pos2;
+	int priority = 2;
+
+	if (*pos == '<') {
+		/* skip priority */
+		pos++;
+		priority = atoi(pos);
+		pos = strchr(pos, '>');
+		if (pos)
+			pos++;
+		else
+			pos = msg;
+	}
+
+	WpaMsg wm(pos, priority);
+	if (eh)
+		eh->addEvent(wm);
+	if (peers)
+		peers->event_notify(wm);
+	msgs.append(wm);
+	while (msgs.count() > 100)
+		msgs.pop_front();
+
+	/* Update last message with truncated version of the event */
+	if (strncmp(pos, "CTRL-", 5) == 0) {
+		pos2 = strchr(pos, str_match(pos, WPA_CTRL_REQ) ? ':' : ' ');
+		if (pos2)
+			pos2++;
+		else
+			pos2 = pos;
+	} else
+		pos2 = pos;
+	QString lastmsg = pos2;
+	lastmsg.truncate(40);
+	textLastMessage->setText(lastmsg);
+
+	pingsToStatusUpdate = 0;
+	networkMayHaveChanged = true;
+
+	if (str_match(pos, WPA_CTRL_REQ))
+		processCtrlReq(pos + strlen(WPA_CTRL_REQ));
+	else if (str_match(pos, WPA_EVENT_SCAN_RESULTS) && scanres)
+		scanres->updateResults();
+	else if (str_match(pos, WPA_EVENT_DISCONNECTED))
+		showTrayMessage(QSystemTrayIcon::Information, 3,
+				tr("Disconnected from network."));
+	else if (str_match(pos, WPA_EVENT_CONNECTED)) {
+		showTrayMessage(QSystemTrayIcon::Information, 3,
+				tr("Connection to network established."));
+		QTimer::singleShot(5 * 1000, this, SLOT(showTrayStatus()));
+		stopWpsRun(true);
+	} else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PBC)) {
+		wpsStatusText->setText(tr("WPS AP in active PBC mode found"));
+		if (textStatus->text() == "INACTIVE" ||
+		    textStatus->text() == "DISCONNECTED")
+			wpaguiTab->setCurrentWidget(wpsTab);
+		wpsInstructions->setText(tr("Press the PBC button on the "
+					    "screen to start registration"));
+	} else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PIN)) {
+		wpsStatusText->setText(tr("WPS AP with recently selected "
+					  "registrar"));
+		if (textStatus->text() == "INACTIVE" ||
+		    textStatus->text() == "DISCONNECTED")
+			wpaguiTab->setCurrentWidget(wpsTab);
+	} else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_AUTH)) {
+		showTrayMessage(QSystemTrayIcon::Information, 3,
+				"Wi-Fi Protected Setup (WPS) AP\n"
+				"indicating this client is authorized.");
+		wpsStatusText->setText("WPS AP indicating this client is "
+				       "authorized");
+		if (textStatus->text() == "INACTIVE" ||
+		    textStatus->text() == "DISCONNECTED")
+			wpaguiTab->setCurrentWidget(wpsTab);
+	} else if (str_match(pos, WPS_EVENT_AP_AVAILABLE)) {
+		wpsStatusText->setText(tr("WPS AP detected"));
+	} else if (str_match(pos, WPS_EVENT_OVERLAP)) {
+		wpsStatusText->setText(tr("PBC mode overlap detected"));
+		wpsInstructions->setText(tr("More than one AP is currently in "
+					    "active WPS PBC mode. Wait couple "
+					    "of minutes and try again"));
+		wpaguiTab->setCurrentWidget(wpsTab);
+	} else if (str_match(pos, WPS_EVENT_CRED_RECEIVED)) {
+		wpsStatusText->setText(tr("Network configuration received"));
+		wpaguiTab->setCurrentWidget(wpsTab);
+	} else if (str_match(pos, WPA_EVENT_EAP_METHOD)) {
+		if (strstr(pos, "(WSC)"))
+			wpsStatusText->setText(tr("Registration started"));
+	} else if (str_match(pos, WPS_EVENT_M2D)) {
+		wpsStatusText->setText(tr("Registrar does not yet know PIN"));
+	} else if (str_match(pos, WPS_EVENT_FAIL)) {
+		wpsStatusText->setText(tr("Registration failed"));
+	} else if (str_match(pos, WPS_EVENT_SUCCESS)) {
+		wpsStatusText->setText(tr("Registration succeeded"));
+	}
+}
+
+
+void WpaGui::processCtrlReq(const char *req)
+{
+	if (udr) {
+		udr->close();
+		delete udr;
+	}
+	udr = new UserDataRequest();
+	if (udr == NULL)
+		return;
+	if (udr->setParams(this, req) < 0) {
+		delete udr;
+		udr = NULL;
+		return;
+	}
+	udr->show();
+	udr->exec();
+}
+
+
+void WpaGui::receiveMsgs()
+{
+	char buf[256];
+	size_t len;
+
+	while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) {
+		len = sizeof(buf) - 1;
+		if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) {
+			buf[len] = '\0';
+			processMsg(buf);
+		}
+	}
+}
+
+
+void WpaGui::connectB()
+{
+	char reply[10];
+	size_t reply_len = sizeof(reply);
+	ctrlRequest("REASSOCIATE", reply, &reply_len);
+}
+
+
+void WpaGui::selectNetwork( const QString &sel )
+{
+	QString cmd(sel);
+	char reply[10];
+	size_t reply_len = sizeof(reply);
+
+	if (cmd.contains(QRegExp("^\\d+:")))
+		cmd.truncate(cmd.indexOf(':'));
+	else
+		cmd = "any";
+	cmd.prepend("SELECT_NETWORK ");
+	ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len);
+	triggerUpdate();
+	stopWpsRun(false);
+}
+
+
+void WpaGui::enableNetwork(const QString &sel)
+{
+	QString cmd(sel);
+	char reply[10];
+	size_t reply_len = sizeof(reply);
+
+	if (cmd.contains(QRegExp("^\\d+:")))
+		cmd.truncate(cmd.indexOf(':'));
+	else if (!cmd.startsWith("all")) {
+		debug("Invalid editNetwork '%s'",
+		      cmd.toLocal8Bit().constData());
+		return;
+	}
+	cmd.prepend("ENABLE_NETWORK ");
+	ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len);
+	triggerUpdate();
+}
+
+
+void WpaGui::disableNetwork(const QString &sel)
+{
+	QString cmd(sel);
+	char reply[10];
+	size_t reply_len = sizeof(reply);
+
+	if (cmd.contains(QRegExp("^\\d+:")))
+		cmd.truncate(cmd.indexOf(':'));
+	else if (!cmd.startsWith("all")) {
+		debug("Invalid editNetwork '%s'",
+		      cmd.toLocal8Bit().constData());
+		return;
+	}
+	cmd.prepend("DISABLE_NETWORK ");
+	ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len);
+	triggerUpdate();
+}
+
+
+void WpaGui::editNetwork(const QString &sel)
+{
+	QString cmd(sel);
+	int id = -1;
+
+	if (cmd.contains(QRegExp("^\\d+:"))) {
+		cmd.truncate(cmd.indexOf(':'));
+		id = cmd.toInt();
+	}
+
+	NetworkConfig *nc = new NetworkConfig();
+	if (nc == NULL)
+		return;
+	nc->setWpaGui(this);
+
+	if (id >= 0)
+		nc->paramsFromConfig(id);
+	else
+		nc->newNetwork();
+
+	nc->show();
+	nc->exec();
+}
+
+
+void WpaGui::editSelectedNetwork()
+{
+	if (networkSelect->count() < 1) {
+		QMessageBox::information(
+			this, tr("No Networks"),
+			tr("There are no networks to edit.\n"));
+		return;
+	}
+	QString sel(networkSelect->currentText());
+	editNetwork(sel);
+}
+
+
+void WpaGui::editListedNetwork()
+{
+	if (networkList->currentRow() < 0) {
+		QMessageBox::information(this, tr("Select A Network"),
+					 tr("Select a network from the list to"
+					    " edit it.\n"));
+		return;
+	}
+	QString sel(networkList->currentItem()->text());
+	editNetwork(sel);
+}
+
+
+void WpaGui::triggerUpdate()
+{
+	updateStatus();
+	networkMayHaveChanged = true;
+	updateNetworks();
+}
+
+
+void WpaGui::addNetwork()
+{
+	NetworkConfig *nc = new NetworkConfig();
+	if (nc == NULL)
+		return;
+	nc->setWpaGui(this);
+	nc->newNetwork();
+	nc->show();
+	nc->exec();
+}
+
+
+void WpaGui::removeNetwork(const QString &sel)
+{
+	QString cmd(sel);
+	char reply[10];
+	size_t reply_len = sizeof(reply);
+
+	if (cmd.contains(QRegExp("^\\d+:")))
+		cmd.truncate(cmd.indexOf(':'));
+	else if (!cmd.startsWith("all")) {
+		debug("Invalid editNetwork '%s'",
+		      cmd.toLocal8Bit().constData());
+		return;
+	}
+	cmd.prepend("REMOVE_NETWORK ");
+	ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len);
+	triggerUpdate();
+}
+
+
+void WpaGui::removeSelectedNetwork()
+{
+	if (networkSelect->count() < 1) {
+		QMessageBox::information(this, tr("No Networks"),
+			                 tr("There are no networks to remove."
+					    "\n"));
+		return;
+	}
+	QString sel(networkSelect->currentText());
+	removeNetwork(sel);
+}
+
+
+void WpaGui::removeListedNetwork()
+{
+	if (networkList->currentRow() < 0) {
+		QMessageBox::information(this, tr("Select A Network"),
+					 tr("Select a network from the list "
+					    "to remove it.\n"));
+		return;
+	}
+	QString sel(networkList->currentItem()->text());
+	removeNetwork(sel);
+}
+
+
+void WpaGui::enableAllNetworks()
+{
+	QString sel("all");
+	enableNetwork(sel);
+}
+
+
+void WpaGui::disableAllNetworks()
+{
+	QString sel("all");
+	disableNetwork(sel);
+}
+
+
+void WpaGui::removeAllNetworks()
+{
+	QString sel("all");
+	removeNetwork(sel);
+}
+
+
+int WpaGui::getNetworkDisabled(const QString &sel)
+{
+	QString cmd(sel);
+	char reply[10];
+	size_t reply_len = sizeof(reply) - 1;
+	int pos = cmd.indexOf(':');
+	if (pos < 0) {
+		debug("Invalid getNetworkDisabled '%s'",
+		      cmd.toLocal8Bit().constData());
+		return -1;
+	}
+	cmd.truncate(pos);
+	cmd.prepend("GET_NETWORK ");
+	cmd.append(" disabled");
+
+	if (ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len) >= 0
+	    && reply_len >= 1) {
+		reply[reply_len] = '\0';
+		if (!str_match(reply, "FAIL"))
+			return atoi(reply);
+	}
+
+	return -1;
+}
+
+
+void WpaGui::updateNetworkDisabledStatus()
+{
+	if (networkList->currentRow() < 0)
+		return;
+
+	QString sel(networkList->currentItem()->text());
+
+	switch (getNetworkDisabled(sel)) {
+	case 0:
+		if (!enableRadioButton->isChecked())
+			enableRadioButton->setChecked(true);
+		return;
+	case 1:
+		if (!disableRadioButton->isChecked())
+			disableRadioButton->setChecked(true);
+		return;
+	}
+}
+
+
+void WpaGui::enableListedNetwork(bool enabled)
+{
+	if (networkList->currentRow() < 0 || !enabled)
+		return;
+
+	QString sel(networkList->currentItem()->text());
+
+	if (getNetworkDisabled(sel) == 1)
+		enableNetwork(sel);
+}
+
+
+void WpaGui::disableListedNetwork(bool disabled)
+{
+	if (networkList->currentRow() < 0 || !disabled)
+		return;
+
+	QString sel(networkList->currentItem()->text());
+
+	if (getNetworkDisabled(sel) == 0)
+		disableNetwork(sel);
+}
+
+
+void WpaGui::saveConfig()
+{
+	char buf[10];
+	size_t len;
+
+	len = sizeof(buf) - 1;
+	ctrlRequest("SAVE_CONFIG", buf, &len);
+
+	buf[len] = '\0';
+
+	if (str_match(buf, "FAIL"))
+		QMessageBox::warning(
+			this, tr("Failed to save configuration"),
+			tr("The configuration could not be saved.\n"
+			   "\n"
+			   "The update_config=1 configuration option\n"
+			   "must be used for configuration saving to\n"
+			   "be permitted.\n"));
+	else
+		QMessageBox::information(
+			this, tr("Saved configuration"),
+			tr("The current configuration was saved."
+			   "\n"));
+}
+
+
+void WpaGui::selectAdapter( const QString & sel )
+{
+	if (openCtrlConnection(sel.toLocal8Bit().constData()) < 0)
+		debug("Failed to open control connection to "
+		      "wpa_supplicant.");
+	updateStatus();
+	updateNetworks();
+}
+
+
+void WpaGui::createTrayIcon(bool trayOnly)
+{
+	QApplication::setQuitOnLastWindowClosed(false);
+
+	tray_icon = new QSystemTrayIcon(this);
+	updateTrayIcon(TrayIconOffline);
+
+	connect(tray_icon,
+		SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
+		this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
+
+	ackTrayIcon = false;
+
+	tray_menu = new QMenu(this);
+
+	disconnectAction = new QAction(tr("&Disconnect"), this);
+	reconnectAction = new QAction(tr("Re&connect"), this);
+	connect(disconnectAction, SIGNAL(triggered()), this,
+		SLOT(disconnect()));
+	connect(reconnectAction, SIGNAL(triggered()), this,
+		SLOT(connectB()));
+	tray_menu->addAction(disconnectAction);
+	tray_menu->addAction(reconnectAction);
+	tray_menu->addSeparator();
+
+	eventAction = new QAction(tr("&Event History"), this);
+	scanAction = new QAction(tr("Scan &Results"), this);
+	statAction = new QAction(tr("S&tatus"), this);
+	connect(eventAction, SIGNAL(triggered()), this, SLOT(eventHistory()));
+	connect(scanAction, SIGNAL(triggered()), this, SLOT(scan()));
+	connect(statAction, SIGNAL(triggered()), this, SLOT(showTrayStatus()));
+	tray_menu->addAction(eventAction);
+	tray_menu->addAction(scanAction);
+	tray_menu->addAction(statAction);
+	tray_menu->addSeparator();
+
+	showAction = new QAction(tr("&Show Window"), this);
+	hideAction = new QAction(tr("&Hide Window"), this);
+	quitAction = new QAction(tr("&Quit"), this);
+	connect(showAction, SIGNAL(triggered()), this, SLOT(show()));
+	connect(hideAction, SIGNAL(triggered()), this, SLOT(hide()));
+	connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
+	tray_menu->addAction(showAction);
+	tray_menu->addAction(hideAction);
+	tray_menu->addSeparator();
+	tray_menu->addAction(quitAction);
+
+	tray_icon->setContextMenu(tray_menu);
+
+	tray_icon->show();
+
+	if (!trayOnly)
+		show();
+	inTray = trayOnly;
+}
+
+
+void WpaGui::showTrayMessage(QSystemTrayIcon::MessageIcon type, int sec,
+			     const QString & msg)
+{
+	if (!QSystemTrayIcon::supportsMessages())
+		return;
+
+	if (isVisible() || !tray_icon || !tray_icon->isVisible() || quietMode)
+		return;
+
+	tray_icon->showMessage(qAppName(), msg, type, sec * 1000);
+}
+
+
+void WpaGui::trayActivated(QSystemTrayIcon::ActivationReason how)
+ {
+	switch (how) {
+	/* use close() here instead of hide() and allow the
+	 * custom closeEvent handler take care of children */
+	case QSystemTrayIcon::Trigger:
+		ackTrayIcon = true;
+		if (isVisible()) {
+			close();
+			inTray = true;
+		} else {
+			show();
+			inTray = false;
+		}
+		break;
+	case QSystemTrayIcon::MiddleClick:
+		showTrayStatus();
+		break;
+	default:
+		break;
+	}
+}
+
+
+void WpaGui::showTrayStatus()
+{
+	char buf[2048];
+	size_t len;
+
+	len = sizeof(buf) - 1;
+	if (ctrlRequest("STATUS", buf, &len) < 0)
+		return;
+	buf[len] = '\0';
+
+	QString msg, status(buf);
+
+	QStringList lines = status.split(QRegExp("\\n"));
+	for (QStringList::Iterator it = lines.begin();
+	     it != lines.end(); it++) {
+		int pos = (*it).indexOf('=') + 1;
+		if (pos < 1)
+			continue;
+
+		if ((*it).startsWith("bssid="))
+			msg.append("BSSID:\t" + (*it).mid(pos) + "\n");
+		else if ((*it).startsWith("ssid="))
+			msg.append("SSID: \t" + (*it).mid(pos) + "\n");
+		else if ((*it).startsWith("pairwise_cipher="))
+			msg.append("PAIR: \t" + (*it).mid(pos) + "\n");
+		else if ((*it).startsWith("group_cipher="))
+			msg.append("GROUP:\t" + (*it).mid(pos) + "\n");
+		else if ((*it).startsWith("key_mgmt="))
+			msg.append("AUTH: \t" + (*it).mid(pos) + "\n");
+		else if ((*it).startsWith("wpa_state="))
+			msg.append("STATE:\t" + (*it).mid(pos) + "\n");
+		else if ((*it).startsWith("ip_address="))
+			msg.append("IP:   \t" + (*it).mid(pos) + "\n");
+		else if ((*it).startsWith("Supplicant PAE state="))
+			msg.append("PAE:  \t" + (*it).mid(pos) + "\n");
+		else if ((*it).startsWith("EAP state="))
+			msg.append("EAP:  \t" + (*it).mid(pos) + "\n");
+	}
+
+	if (!msg.isEmpty())
+		showTrayMessage(QSystemTrayIcon::Information, 10, msg);
+}
+
+
+void WpaGui::updateTrayToolTip(const QString &msg)
+{
+	if (tray_icon)
+		tray_icon->setToolTip(msg);
+}
+
+
+void WpaGui::updateTrayIcon(TrayIconType type)
+{
+	if (!tray_icon || currentIconType == type)
+		return;
+
+	QIcon fallback_icon;
+	QStringList names;
+
+	if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
+		fallback_icon = QIcon(":/icons/wpa_gui.svg");
+	else
+		fallback_icon = QIcon(":/icons/wpa_gui.png");
+
+	switch (type) {
+	case TrayIconOffline:
+		names << "network-wireless-offline-symbolic"
+		      << "network-wireless-offline"
+		      << "network-wireless-signal-none-symbolic"
+		      << "network-wireless-signal-none";
+		break;
+	case TrayIconAcquiring:
+		names << "network-wireless-acquiring-symbolic"
+		      << "network-wireless-acquiring";
+		break;
+	case TrayIconConnected:
+		names << "network-wireless-connected-symbolic"
+		      << "network-wireless-connected";
+		break;
+	case TrayIconSignalNone:
+		names << "network-wireless-signal-none-symbolic"
+		      << "network-wireless-signal-none";
+		break;
+	case TrayIconSignalWeak:
+		names << "network-wireless-signal-weak-symbolic"
+		      << "network-wireless-signal-weak";
+		break;
+	case TrayIconSignalOk:
+		names << "network-wireless-signal-ok-symbolic"
+		      << "network-wireless-signal-ok";
+		break;
+	case TrayIconSignalGood:
+		names << "network-wireless-signal-good-symbolic"
+		      << "network-wireless-signal-good";
+		break;
+	case TrayIconSignalExcellent:
+		names << "network-wireless-signal-excellent-symbolic"
+		      << "network-wireless-signal-excellent";
+		break;
+	}
+
+	currentIconType = type;
+	tray_icon->setIcon(loadThemedIcon(names, fallback_icon));
+}
+
+
+QIcon WpaGui::loadThemedIcon(const QStringList &names,
+			     const QIcon &fallback)
+{
+	QIcon icon;
+
+	for (QStringList::ConstIterator it = names.begin();
+	     it != names.end(); it++) {
+		icon = QIcon::fromTheme(*it);
+		if (!icon.isNull())
+			return icon;
+	}
+
+	return fallback;
+}
+
+
+void WpaGui::closeEvent(QCloseEvent *event)
+{
+	if (eh) {
+		eh->close();
+		delete eh;
+		eh = NULL;
+	}
+
+	if (scanres) {
+		scanres->close();
+		delete scanres;
+		scanres = NULL;
+	}
+
+	if (peers) {
+		peers->close();
+		delete peers;
+		peers = NULL;
+	}
+
+	if (udr) {
+		udr->close();
+		delete udr;
+		udr = NULL;
+	}
+
+	if (tray_icon && !ackTrayIcon) {
+		/* give user a visual hint that the tray icon exists */
+		if (QSystemTrayIcon::supportsMessages()) {
+			hide();
+			showTrayMessage(QSystemTrayIcon::Information, 3,
+					qAppName() +
+					tr(" will keep running in "
+					   "the system tray."));
+		} else {
+			QMessageBox::information(this, qAppName() +
+						 tr(" systray"),
+						 tr("The program will keep "
+						    "running in the system "
+						    "tray."));
+		}
+		ackTrayIcon = true;
+	}
+
+	event->accept();
+}
+
+
+void WpaGui::wpsDialog()
+{
+	wpaguiTab->setCurrentWidget(wpsTab);
+}
+
+
+void WpaGui::peersDialog()
+{
+	if (peers) {
+		peers->close();
+		delete peers;
+	}
+
+	peers = new Peers();
+	if (peers == NULL)
+		return;
+	peers->setWpaGui(this);
+	peers->show();
+	peers->exec();
+}
+
+
+void WpaGui::tabChanged(int index)
+{
+	if (index != 2)
+		return;
+
+	if (wpsRunning)
+		return;
+
+	wpsApPinEdit->setEnabled(!bssFromScan.isEmpty());
+	if (bssFromScan.isEmpty())
+		wpsApPinButton->setEnabled(false);
+}
+
+
+void WpaGui::wpsPbc()
+{
+	char reply[20];
+	size_t reply_len = sizeof(reply);
+
+	if (ctrlRequest("WPS_PBC", reply, &reply_len) < 0)
+		return;
+
+	wpsPinEdit->setEnabled(false);
+	if (wpsStatusText->text().compare(tr("WPS AP in active PBC mode found"))) {
+		wpsInstructions->setText(tr("Press the push button on the AP to "
+					 "start the PBC mode."));
+	} else {
+		wpsInstructions->setText(tr("If you have not yet done so, press "
+					 "the push button on the AP to start "
+					 "the PBC mode."));
+	}
+	wpsStatusText->setText(tr("Waiting for Registrar"));
+	wpsRunning = true;
+}
+
+
+void WpaGui::wpsGeneratePin()
+{
+	char reply[20];
+	size_t reply_len = sizeof(reply) - 1;
+
+	if (ctrlRequest("WPS_PIN any", reply, &reply_len) < 0)
+		return;
+
+	reply[reply_len] = '\0';
+
+	wpsPinEdit->setText(reply);
+	wpsPinEdit->setEnabled(true);
+	wpsInstructions->setText(tr("Enter the generated PIN into the Registrar "
+				 "(either the internal one in the AP or an "
+				 "external one)."));
+	wpsStatusText->setText(tr("Waiting for Registrar"));
+	wpsRunning = true;
+}
+
+
+void WpaGui::setBssFromScan(const QString &bssid)
+{
+	bssFromScan = bssid;
+	wpsApPinEdit->setEnabled(!bssFromScan.isEmpty());
+	wpsApPinButton->setEnabled(wpsApPinEdit->text().length() == 8);
+	wpsStatusText->setText(tr("WPS AP selected from scan results"));
+	wpsInstructions->setText(tr("If you want to use an AP device PIN, e.g., "
+				 "from a label in the device, enter the eight "
+				 "digit AP PIN and click Use AP PIN button."));
+}
+
+
+void WpaGui::wpsApPinChanged(const QString &text)
+{
+	wpsApPinButton->setEnabled(text.length() == 8);
+}
+
+
+void WpaGui::wpsApPin()
+{
+	char reply[20];
+	size_t reply_len = sizeof(reply);
+
+	QString cmd("WPS_REG " + bssFromScan + " " + wpsApPinEdit->text());
+	if (ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len) < 0)
+		return;
+
+	wpsStatusText->setText(tr("Waiting for AP/Enrollee"));
+	wpsRunning = true;
+}
+
+
+void WpaGui::stopWpsRun(bool success)
+{
+	if (wpsRunning)
+		wpsStatusText->setText(success ? tr("Connected to the network") :
+				       tr("Stopped"));
+	else
+		wpsStatusText->setText("");
+	wpsPinEdit->setEnabled(false);
+	wpsInstructions->setText("");
+	wpsRunning = false;
+	bssFromScan = "";
+	wpsApPinEdit->setEnabled(false);
+	wpsApPinButton->setEnabled(false);
+}
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+
+#ifndef WPASVC_NAME
+#define WPASVC_NAME TEXT("wpasvc")
+#endif
+
+class ErrorMsg : public QMessageBox {
+public:
+	ErrorMsg(QWidget *parent, DWORD last_err = GetLastError());
+	void showMsg(QString msg);
+private:
+	DWORD err;
+};
+
+ErrorMsg::ErrorMsg(QWidget *parent, DWORD last_err) :
+	QMessageBox(parent), err(last_err)
+{
+	setWindowTitle(tr("wpa_gui error"));
+	setIcon(QMessageBox::Warning);
+}
+
+void ErrorMsg::showMsg(QString msg)
+{
+	LPTSTR buf;
+
+	setText(msg);
+	if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+			  FORMAT_MESSAGE_FROM_SYSTEM,
+			  NULL, err, 0, (LPTSTR) (void *) &buf,
+			  0, NULL) > 0) {
+		QString msg = QString::fromWCharArray(buf);
+		setInformativeText(QString("[%1] %2").arg(err).arg(msg));
+		LocalFree(buf);
+	} else {
+		setInformativeText(QString("[%1]").arg(err));
+	}
+
+	exec();
+}
+
+
+void WpaGui::startService()
+{
+	SC_HANDLE svc, scm;
+
+	scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
+	if (!scm) {
+		ErrorMsg(this).showMsg(tr("OpenSCManager failed"));
+		return;
+	}
+
+	svc = OpenService(scm, WPASVC_NAME, SERVICE_START);
+	if (!svc) {
+		ErrorMsg(this).showMsg(tr("OpenService failed"));
+		CloseServiceHandle(scm);
+		return;
+	}
+
+	if (!StartService(svc, 0, NULL)) {
+		ErrorMsg(this).showMsg(tr("Failed to start wpa_supplicant "
+				       "service"));
+	}
+
+	CloseServiceHandle(svc);
+	CloseServiceHandle(scm);
+}
+
+
+void WpaGui::stopService()
+{
+	SC_HANDLE svc, scm;
+	SERVICE_STATUS status;
+
+	scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
+	if (!scm) {
+		ErrorMsg(this).showMsg(tr("OpenSCManager failed"));
+		return;
+	}
+
+	svc = OpenService(scm, WPASVC_NAME, SERVICE_STOP);
+	if (!svc) {
+		ErrorMsg(this).showMsg(tr("OpenService failed"));
+		CloseServiceHandle(scm);
+		return;
+	}
+
+	if (!ControlService(svc, SERVICE_CONTROL_STOP, &status)) {
+		ErrorMsg(this).showMsg(tr("Failed to stop wpa_supplicant "
+				       "service"));
+	}
+
+	CloseServiceHandle(svc);
+	CloseServiceHandle(scm);
+}
+
+
+bool WpaGui::serviceRunning()
+{
+	SC_HANDLE svc, scm;
+	SERVICE_STATUS status;
+	bool running = false;
+
+	scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
+	if (!scm) {
+		debug("OpenSCManager failed: %d", (int) GetLastError());
+		return false;
+	}
+
+	svc = OpenService(scm, WPASVC_NAME, SERVICE_QUERY_STATUS);
+	if (!svc) {
+		debug("OpenService failed: %d", (int) GetLastError());
+		CloseServiceHandle(scm);
+		return false;
+	}
+
+	if (QueryServiceStatus(svc, &status)) {
+		if (status.dwCurrentState != SERVICE_STOPPED)
+			running = true;
+	}
+
+	CloseServiceHandle(svc);
+	CloseServiceHandle(scm);
+
+	return running;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+void WpaGui::addInterface()
+{
+	if (add_iface) {
+		add_iface->close();
+		delete add_iface;
+	}
+	add_iface = new AddInterface(this, this);
+	add_iface->show();
+	add_iface->exec();
+}
+
+
+#ifndef QT_NO_SESSIONMANAGER
+void WpaGui::saveState()
+{
+	QSettings settings("wpa_supplicant", "wpa_gui");
+	settings.beginGroup("state");
+	settings.setValue("session_id", app->sessionId());
+	settings.setValue("in_tray", inTray);
+	settings.endGroup();
+}
+#endif
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/wpagui.h b/hostap/wpa_supplicant/wpa_gui-qt4/wpagui.h
new file mode 100644
index 0000000..f0a34c9
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/wpagui.h
@@ -0,0 +1,180 @@
+/*
+ * wpa_gui - WpaGui class
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPAGUI_H
+#define WPAGUI_H
+
+#include <QSystemTrayIcon>
+#include <QObject>
+#include "ui_wpagui.h"
+#include "addinterface.h"
+
+class UserDataRequest;
+
+class WpaGuiApp : public QApplication
+{
+	Q_OBJECT
+public:
+	WpaGuiApp(int &argc, char **argv);
+
+#if !defined(QT_NO_SESSIONMANAGER) && QT_VERSION < 0x050000
+	virtual void saveState(QSessionManager &manager);
+#endif
+
+	WpaGui *w;
+	int argc;
+	char **argv;
+};
+
+class WpaGui : public QMainWindow, public Ui::WpaGui
+{
+	Q_OBJECT
+
+public:
+
+	enum TrayIconType {
+		TrayIconOffline = 0,
+		TrayIconAcquiring,
+		TrayIconConnected,
+		TrayIconSignalNone,
+		TrayIconSignalWeak,
+		TrayIconSignalOk,
+		TrayIconSignalGood,
+		TrayIconSignalExcellent,
+	};
+
+	WpaGui(QApplication *app, QWidget *parent = 0, const char *name = 0,
+	       Qt::WindowFlags fl = 0);
+	~WpaGui();
+
+	virtual int ctrlRequest(const char *cmd, char *buf, size_t *buflen);
+	virtual void triggerUpdate();
+	virtual void editNetwork(const QString &sel);
+	virtual void removeNetwork(const QString &sel);
+	virtual void enableNetwork(const QString &sel);
+	virtual void disableNetwork(const QString &sel);
+	virtual int getNetworkDisabled(const QString &sel);
+	void setBssFromScan(const QString &bssid);
+#ifndef QT_NO_SESSIONMANAGER
+	void saveState();
+#endif
+
+public slots:
+	virtual void parse_argv();
+	virtual void updateStatus();
+	virtual void updateNetworks();
+	virtual void helpIndex();
+	virtual void helpContents();
+	virtual void helpAbout();
+	virtual void disconnect();
+	virtual void scan();
+	virtual void eventHistory();
+	virtual void ping();
+	virtual void signalMeterUpdate();
+	virtual void processMsg(char *msg);
+	virtual void processCtrlReq(const char *req);
+	virtual void receiveMsgs();
+	virtual void connectB();
+	virtual void selectNetwork(const QString &sel);
+	virtual void editSelectedNetwork();
+	virtual void editListedNetwork();
+	virtual void removeSelectedNetwork();
+	virtual void removeListedNetwork();
+	virtual void addNetwork();
+	virtual void enableAllNetworks();
+	virtual void disableAllNetworks();
+	virtual void removeAllNetworks();
+	virtual void saveConfig();
+	virtual void selectAdapter(const QString &sel);
+	virtual void updateNetworkDisabledStatus();
+	virtual void enableListedNetwork(bool);
+	virtual void disableListedNetwork(bool);
+	virtual void showTrayMessage(QSystemTrayIcon::MessageIcon type,
+				     int sec, const QString &msg);
+	virtual void showTrayStatus();
+	virtual void updateTrayIcon(TrayIconType type);
+	virtual void updateTrayToolTip(const QString &msg);
+	virtual QIcon loadThemedIcon(const QStringList &names,
+				     const QIcon &fallback);
+	virtual void wpsDialog();
+	virtual void peersDialog();
+	virtual void tabChanged(int index);
+	virtual void wpsPbc();
+	virtual void wpsGeneratePin();
+	virtual void wpsApPinChanged(const QString &text);
+	virtual void wpsApPin();
+#ifdef CONFIG_NATIVE_WINDOWS
+	virtual void startService();
+	virtual void stopService();
+#endif /* CONFIG_NATIVE_WINDOWS */
+	virtual void addInterface();
+
+protected slots:
+	virtual void languageChange();
+	virtual void trayActivated(QSystemTrayIcon::ActivationReason how);
+	virtual void closeEvent(QCloseEvent *event);
+
+private:
+	ScanResults *scanres;
+	Peers *peers;
+	bool networkMayHaveChanged;
+	char *ctrl_iface;
+	EventHistory *eh;
+	struct wpa_ctrl *ctrl_conn;
+	QSocketNotifier *msgNotifier;
+	QTimer *timer;
+	int pingsToStatusUpdate;
+	WpaMsgList msgs;
+	char *ctrl_iface_dir;
+	struct wpa_ctrl *monitor_conn;
+	UserDataRequest *udr;
+	QAction *disconnectAction;
+	QAction *reconnectAction;
+	QAction *eventAction;
+	QAction *scanAction;
+	QAction *statAction;
+	QAction *showAction;
+	QAction *hideAction;
+	QAction *quitAction;
+	QMenu *tray_menu;
+	QSystemTrayIcon *tray_icon;
+	TrayIconType currentIconType;
+	QString wpaStateTranslate(char *state);
+	void createTrayIcon(bool);
+	bool ackTrayIcon;
+	bool startInTray;
+	bool quietMode;
+
+	int openCtrlConnection(const char *ifname);
+
+	bool wpsRunning;
+
+	QString bssFromScan;
+
+	void stopWpsRun(bool success);
+
+	QTimer *signalMeterTimer;
+	int signalMeterInterval;
+
+#ifdef CONFIG_NATIVE_WINDOWS
+	QAction *fileStartServiceAction;
+	QAction *fileStopServiceAction;
+
+	bool serviceRunning();
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	QAction *addInterfaceAction;
+	AddInterface *add_iface;
+
+	bool connectedToService;
+
+	QApplication *app;
+	bool inTray;
+};
+
+#endif /* WPAGUI_H */
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/wpagui.ui b/hostap/wpa_supplicant/wpa_gui-qt4/wpagui.ui
new file mode 100644
index 0000000..9f9039f
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/wpagui.ui
@@ -0,0 +1,524 @@
+<ui version="4.0" >
+ <class>WpaGui</class>
+ <widget class="QMainWindow" name="WpaGui" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>345</width>
+    <height>330</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>wpa_gui</string>
+  </property>
+  <property name="windowIcon" >
+   <iconset resource="icons.qrc" >
+    <normaloff>:/icons/wpa_gui.svg</normaloff>:/icons/wpa_gui.svg</iconset>
+  </property>
+  <widget class="QWidget" name="widget" >
+   <layout class="QGridLayout" >
+    <item row="0" column="0" >
+     <widget class="QLabel" name="adapterLabel" >
+      <property name="text" >
+       <string>Adapter:</string>
+      </property>
+     </widget>
+    </item>
+    <item row="0" column="1" >
+     <widget class="QComboBox" name="adapterSelect" />
+    </item>
+    <item row="1" column="0" >
+     <widget class="QLabel" name="networkLabel" >
+      <property name="text" >
+       <string>Network:</string>
+      </property>
+     </widget>
+    </item>
+    <item row="1" column="1" >
+     <widget class="QComboBox" name="networkSelect" />
+    </item>
+    <item row="2" column="0" colspan="2" >
+     <widget class="QTabWidget" name="wpaguiTab" >
+      <property name="currentIndex" >
+       <number>0</number>
+      </property>
+      <widget class="QWidget" name="statusTab" >
+       <attribute name="title" >
+        <string>Current Status</string>
+       </attribute>
+       <layout class="QGridLayout" >
+        <item row="0" column="0" colspan="5" >
+         <widget class="QFrame" name="frame3" >
+          <property name="frameShape" >
+           <enum>QFrame::NoFrame</enum>
+          </property>
+          <property name="frameShadow" >
+           <enum>QFrame::Plain</enum>
+          </property>
+          <layout class="QGridLayout" >
+           <item row="0" column="0" >
+            <widget class="QLabel" name="statusLabel" >
+             <property name="text" >
+              <string>Status:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0" >
+            <widget class="QLabel" name="lastMessageLabel" >
+             <property name="text" >
+              <string>Last message:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="0" >
+            <widget class="QLabel" name="authenticationLabel" >
+             <property name="text" >
+              <string>Authentication:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="0" >
+            <widget class="QLabel" name="encryptionLabel" >
+             <property name="text" >
+              <string>Encryption:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="4" column="0" >
+            <widget class="QLabel" name="ssidLabel" >
+             <property name="text" >
+              <string>SSID:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="5" column="0" >
+            <widget class="QLabel" name="bssidLabel" >
+             <property name="text" >
+              <string>BSSID:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="6" column="0" >
+            <widget class="QLabel" name="ipAddressLabel" >
+             <property name="text" >
+              <string>IP address:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1" >
+            <widget class="QLabel" name="textStatus" >
+             <property name="text" >
+              <string/>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1" colspan="3" >
+            <widget class="QLabel" name="textLastMessage" >
+             <property name="text" >
+              <string/>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="1" >
+            <widget class="QLabel" name="textAuthentication" >
+             <property name="text" >
+              <string/>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="1" >
+            <widget class="QLabel" name="textEncryption" >
+             <property name="text" >
+              <string/>
+             </property>
+            </widget>
+           </item>
+           <item row="4" column="1" >
+            <widget class="QLabel" name="textSsid" >
+             <property name="text" >
+              <string/>
+             </property>
+            </widget>
+           </item>
+           <item row="5" column="1" >
+            <widget class="QLabel" name="textBssid" >
+             <property name="text" >
+              <string/>
+             </property>
+            </widget>
+           </item>
+           <item row="6" column="1" >
+            <widget class="QLabel" name="textIpAddress" >
+             <property name="text" >
+              <string/>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item row="1" column="0" >
+         <spacer>
+          <property name="orientation" >
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" >
+           <size>
+            <width>20</width>
+            <height>40</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="1" column="1" >
+         <widget class="QPushButton" name="connectButton" >
+          <property name="text" >
+           <string>Connect</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="2" >
+         <widget class="QPushButton" name="disconnectButton" >
+          <property name="text" >
+           <string>Disconnect</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="3" >
+         <widget class="QPushButton" name="scanButton" >
+          <property name="text" >
+           <string>Scan</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="4" >
+         <spacer>
+          <property name="orientation" >
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" >
+           <size>
+            <width>20</width>
+            <height>40</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="networkconfigTab" >
+       <attribute name="title" >
+        <string>Manage Networks</string>
+       </attribute>
+       <layout class="QGridLayout" >
+        <item row="0" column="0" colspan="5" >
+         <widget class="QListWidget" name="networkList" >
+          <property name="selectionRectVisible" >
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item rowspan="2" row="1" column="0" >
+         <spacer>
+          <property name="orientation" >
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" >
+           <size>
+            <width>20</width>
+            <height>61</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="1" column="1" >
+         <widget class="QRadioButton" name="enableRadioButton" >
+          <property name="text" >
+           <string>Enabled</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="2" >
+         <widget class="QPushButton" name="editNetworkButton" >
+          <property name="text" >
+           <string>Edit</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="3" >
+         <widget class="QPushButton" name="removeNetworkButton" >
+          <property name="text" >
+           <string>Remove</string>
+          </property>
+         </widget>
+        </item>
+        <item rowspan="2" row="1" column="4" >
+         <spacer>
+          <property name="orientation" >
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" >
+           <size>
+            <width>20</width>
+            <height>61</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="2" column="1" >
+         <widget class="QRadioButton" name="disableRadioButton" >
+          <property name="text" >
+           <string>Disabled</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="2" >
+         <widget class="QPushButton" name="addNetworkButton" >
+          <property name="text" >
+           <string>Add</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="3" >
+         <widget class="QPushButton" name="scanNetworkButton" >
+          <property name="text" >
+           <string>Scan</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="wpsTab" >
+       <attribute name="title" >
+        <string>WPS</string>
+       </attribute>
+       <layout class="QGridLayout" name="wpsGridLayout" >
+        <item row="0" column="0" >
+         <widget class="QLabel" name="label_2" >
+          <property name="text" >
+           <string>Status:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="1" colspan="3" >
+         <widget class="QLabel" name="wpsStatusText" >
+          <property name="text" >
+           <string/>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="0" colspan="2" >
+         <widget class="QPushButton" name="wpsPbcButton" >
+          <property name="text" >
+           <string>PBC - push button</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="0" colspan="2" >
+         <widget class="QPushButton" name="wpsPinButton" >
+          <property name="text" >
+           <string>Generate PIN</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="2" >
+         <widget class="QLabel" name="label" >
+          <property name="text" >
+           <string>PIN:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="3" >
+         <widget class="QLineEdit" name="wpsPinEdit" >
+          <property name="enabled" >
+           <bool>false</bool>
+          </property>
+          <property name="readOnly" >
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="3" column="0" colspan="2" >
+         <widget class="QPushButton" name="wpsApPinButton" >
+          <property name="enabled" >
+           <bool>false</bool>
+          </property>
+          <property name="text" >
+           <string>Use AP PIN</string>
+          </property>
+         </widget>
+        </item>
+        <item row="3" column="2" >
+         <widget class="QLabel" name="label_3" >
+          <property name="text" >
+           <string>AP PIN:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="3" column="3" >
+         <widget class="QLineEdit" name="wpsApPinEdit" >
+          <property name="enabled" >
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="4" column="0" colspan="4" >
+         <widget class="QTextEdit" name="wpsInstructions" >
+          <property name="readOnly" >
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="MenuBar" >
+   <property name="geometry" >
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>345</width>
+     <height>24</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="fileMenu" >
+    <property name="title" >
+     <string>&amp;File</string>
+    </property>
+    <addaction name="fileEventHistoryAction" />
+    <addaction name="fileSaveConfigAction" />
+    <addaction name="actionWPS" />
+    <addaction name="actionPeers" />
+    <addaction name="separator" />
+    <addaction name="fileExitAction" />
+   </widget>
+   <widget class="QMenu" name="networkMenu" >
+    <property name="title" >
+     <string>&amp;Network</string>
+    </property>
+    <addaction name="networkAddAction" />
+    <addaction name="networkEditAction" />
+    <addaction name="networkRemoveAction" />
+    <addaction name="separator" />
+    <addaction name="networkEnableAllAction" />
+    <addaction name="networkDisableAllAction" />
+    <addaction name="networkRemoveAllAction" />
+   </widget>
+   <widget class="QMenu" name="helpMenu" >
+    <property name="title" >
+     <string>&amp;Help</string>
+    </property>
+    <addaction name="helpContentsAction" />
+    <addaction name="helpIndexAction" />
+    <addaction name="separator" />
+    <addaction name="helpAboutAction" />
+   </widget>
+   <addaction name="fileMenu" />
+   <addaction name="networkMenu" />
+   <addaction name="helpMenu" />
+  </widget>
+  <action name="fileEventHistoryAction" >
+   <property name="text" >
+    <string>Event &amp;History</string>
+   </property>
+  </action>
+  <action name="fileSaveConfigAction" >
+   <property name="text" >
+    <string>&amp;Save Configuration</string>
+   </property>
+   <property name="shortcut" >
+    <string>Ctrl+S</string>
+   </property>
+  </action>
+  <action name="fileExitAction" >
+   <property name="text" >
+    <string>E&amp;xit</string>
+   </property>
+   <property name="shortcut" >
+    <string>Ctrl+Q</string>
+   </property>
+  </action>
+  <action name="networkAddAction" >
+   <property name="text" >
+    <string>&amp;Add</string>
+   </property>
+  </action>
+  <action name="networkEditAction" >
+   <property name="text" >
+    <string>&amp;Edit</string>
+   </property>
+  </action>
+  <action name="networkRemoveAction" >
+   <property name="text" >
+    <string>&amp;Remove</string>
+   </property>
+  </action>
+  <action name="networkEnableAllAction" >
+   <property name="text" >
+    <string>E&amp;nable All</string>
+   </property>
+  </action>
+  <action name="networkDisableAllAction" >
+   <property name="text" >
+    <string>&amp;Disable All</string>
+   </property>
+  </action>
+  <action name="networkRemoveAllAction" >
+   <property name="text" >
+    <string>Re&amp;move All</string>
+   </property>
+  </action>
+  <action name="helpContentsAction" >
+   <property name="enabled" >
+    <bool>false</bool>
+   </property>
+   <property name="text" >
+    <string>&amp;Contents...</string>
+   </property>
+  </action>
+  <action name="helpIndexAction" >
+   <property name="enabled" >
+    <bool>false</bool>
+   </property>
+   <property name="text" >
+    <string>&amp;Index...</string>
+   </property>
+  </action>
+  <action name="helpAboutAction" >
+   <property name="text" >
+    <string>&amp;About</string>
+   </property>
+  </action>
+  <action name="actionWPS" >
+   <property name="enabled" >
+    <bool>false</bool>
+   </property>
+   <property name="text" >
+    <string>&amp;Wi-Fi Protected Setup</string>
+   </property>
+  </action>
+  <action name="actionPeers" >
+   <property name="text" >
+    <string>&amp;Peers</string>
+   </property>
+  </action>
+ </widget>
+ <layoutdefault spacing="6" margin="11" />
+ <pixmapfunction></pixmapfunction>
+ <includes>
+  <include location="global" >qtimer.h</include>
+  <include location="global" >qsocketnotifier.h</include>
+  <include location="local" >wpamsg.h</include>
+  <include location="local" >eventhistory.h</include>
+  <include location="local" >scanresults.h</include>
+  <include location="local" >peers.h</include>
+ </includes>
+ <resources>
+  <include location="icons.qrc" />
+ </resources>
+ <connections/>
+</ui>
diff --git a/hostap/wpa_supplicant/wpa_gui-qt4/wpamsg.h b/hostap/wpa_supplicant/wpa_gui-qt4/wpamsg.h
new file mode 100644
index 0000000..8f2fcdc
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_gui-qt4/wpamsg.h
@@ -0,0 +1,35 @@
+/*
+ * wpa_gui - WpaMsg class for storing event messages
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPAMSG_H
+#define WPAMSG_H
+
+#include <QDateTime>
+#include <QLinkedList>
+
+class WpaMsg {
+public:
+	WpaMsg(const QString &_msg, int _priority = 2)
+		: msg(_msg), priority(_priority)
+	{
+		timestamp = QDateTime::currentDateTime();
+	}
+
+	QString getMsg() const { return msg; }
+	int getPriority() const { return priority; }
+	QDateTime getTimestamp() const { return timestamp; }
+
+private:
+	QString msg;
+	int priority;
+	QDateTime timestamp;
+};
+
+typedef QLinkedList<WpaMsg> WpaMsgList;
+
+#endif /* WPAMSG_H */
diff --git a/hostap/wpa_supplicant/wpa_passphrase.c b/hostap/wpa_supplicant/wpa_passphrase.c
new file mode 100644
index 0000000..9b568f0
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_passphrase.c
@@ -0,0 +1,67 @@
+/*
+ * WPA Supplicant - ASCII passphrase to WPA PSK tool
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+
+
+int main(int argc, char *argv[])
+{
+	unsigned char psk[32];
+	int i;
+	char *ssid, *passphrase, buf[64], *pos;
+
+	if (argc < 2) {
+		printf("usage: wpa_passphrase <ssid> [passphrase]\n"
+			"\nIf passphrase is left out, it will be read from "
+			"stdin\n");
+		return 1;
+	}
+
+	ssid = argv[1];
+
+	if (argc > 2) {
+		passphrase = argv[2];
+	} else {
+		printf("# reading passphrase from stdin\n");
+		if (fgets(buf, sizeof(buf), stdin) == NULL) {
+			printf("Failed to read passphrase\n");
+			return 1;
+		}
+		buf[sizeof(buf) - 1] = '\0';
+		pos = buf;
+		while (*pos != '\0') {
+			if (*pos == '\r' || *pos == '\n') {
+				*pos = '\0';
+				break;
+			}
+			pos++;
+		}
+		passphrase = buf;
+	}
+
+	if (os_strlen(passphrase) < 8 || os_strlen(passphrase) > 63) {
+		printf("Passphrase must be 8..63 characters\n");
+		return 1;
+	}
+
+	pbkdf2_sha1(passphrase, (u8 *) ssid, os_strlen(ssid), 4096, psk, 32);
+
+	printf("network={\n");
+	printf("\tssid=\"%s\"\n", ssid);
+	printf("\t#psk=\"%s\"\n", passphrase);
+	printf("\tpsk=");
+	for (i = 0; i < 32; i++)
+		printf("%02x", psk[i]);
+	printf("\n");
+	printf("}\n");
+
+	return 0;
+}
diff --git a/hostap/wpa_supplicant/wpa_priv.c b/hostap/wpa_supplicant/wpa_priv.c
new file mode 100644
index 0000000..850ec40
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_priv.c
@@ -0,0 +1,1156 @@
+/*
+ * WPA Supplicant / privileged helper program
+ * Copyright (c) 2007-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"
+#ifdef __linux__
+#include <fcntl.h>
+#endif /* __linux__ */
+#include <sys/un.h>
+#include <sys/stat.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "common/version.h"
+#include "drivers/driver.h"
+#include "l2_packet/l2_packet.h"
+#include "common/privsep_commands.h"
+#include "common/ieee802_11_defs.h"
+
+
+struct wpa_priv_interface {
+	struct wpa_priv_interface *next;
+	char *driver_name;
+	char *ifname;
+	char *sock_name;
+	int fd;
+
+	const struct wpa_driver_ops *driver;
+	void *drv_priv;
+	void *drv_global_priv;
+	struct sockaddr_un drv_addr;
+	int wpas_registered;
+
+	/* TODO: add support for multiple l2 connections */
+	struct l2_packet_data *l2;
+	struct sockaddr_un l2_addr;
+};
+
+
+static void wpa_priv_cmd_register(struct wpa_priv_interface *iface,
+				  struct sockaddr_un *from)
+{
+	if (iface->drv_priv) {
+		wpa_printf(MSG_DEBUG, "Cleaning up forgotten driver instance");
+		if (iface->driver->deinit)
+			iface->driver->deinit(iface->drv_priv);
+		iface->drv_priv = NULL;
+		if (iface->drv_global_priv) {
+			iface->driver->global_deinit(iface->drv_global_priv);
+			iface->drv_global_priv = NULL;
+		}
+		iface->wpas_registered = 0;
+	}
+
+	if (iface->l2) {
+		wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet "
+			   "instance");
+		l2_packet_deinit(iface->l2);
+		iface->l2 = NULL;
+	}
+
+	if (iface->driver->init2) {
+		if (iface->driver->global_init) {
+			iface->drv_global_priv = iface->driver->global_init();
+			if (!iface->drv_global_priv) {
+				wpa_printf(MSG_INFO,
+					   "Failed to initialize driver global context");
+				return;
+			}
+		} else {
+			iface->drv_global_priv = NULL;
+		}
+		iface->drv_priv = iface->driver->init2(iface, iface->ifname,
+						       iface->drv_global_priv);
+	} else if (iface->driver->init) {
+		iface->drv_priv = iface->driver->init(iface, iface->ifname);
+	} else {
+		return;
+	}
+	if (iface->drv_priv == NULL) {
+		wpa_printf(MSG_DEBUG, "Failed to initialize driver wrapper");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "Driver wrapper '%s' initialized for interface "
+		   "'%s'", iface->driver_name, iface->ifname);
+
+	os_memcpy(&iface->drv_addr, from, sizeof(iface->drv_addr));
+	iface->wpas_registered = 1;
+
+	if (iface->driver->set_param &&
+	    iface->driver->set_param(iface->drv_priv, NULL) < 0) {
+		wpa_printf(MSG_ERROR, "Driver interface rejected param");
+	}
+}
+
+
+static void wpa_priv_cmd_unregister(struct wpa_priv_interface *iface,
+				    struct sockaddr_un *from)
+{
+	if (iface->drv_priv) {
+		if (iface->driver->deinit)
+			iface->driver->deinit(iface->drv_priv);
+		iface->drv_priv = NULL;
+		if (iface->drv_global_priv) {
+			iface->driver->global_deinit(iface->drv_global_priv);
+			iface->drv_global_priv = NULL;
+		}
+		iface->wpas_registered = 0;
+	}
+}
+
+
+static void wpa_priv_cmd_scan(struct wpa_priv_interface *iface,
+			      char *buf, size_t len)
+{
+	struct wpa_driver_scan_params params;
+
+	if (iface->drv_priv == NULL)
+		return;
+
+	os_memset(&params, 0, sizeof(params));
+	if (len) {
+		params.ssids[0].ssid = (u8 *) buf;
+		params.ssids[0].ssid_len = len;
+		params.num_ssids = 1;
+	}
+
+	if (iface->driver->scan2)
+		iface->driver->scan2(iface->drv_priv, &params);
+}
+
+
+static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface,
+				       struct sockaddr_un *from)
+{
+	struct wpa_scan_results *res;
+	u8 *buf = NULL, *pos, *end;
+	int val;
+	size_t i;
+
+	res = iface->driver->get_scan_results2(iface->drv_priv);
+	if (res == NULL)
+		goto fail;
+
+	buf = os_malloc(60000);
+	if (buf == NULL)
+		goto fail;
+	pos = buf;
+	end = buf + 60000;
+	val = res->num;
+	os_memcpy(pos, &val, sizeof(int));
+	pos += sizeof(int);
+
+	for (i = 0; i < res->num; i++) {
+		struct wpa_scan_res *r = res->res[i];
+		val = sizeof(*r) + r->ie_len;
+		if (end - pos < (int) sizeof(int) + val)
+			break;
+		os_memcpy(pos, &val, sizeof(int));
+		pos += sizeof(int);
+		os_memcpy(pos, r, val);
+		pos += val;
+	}
+
+	sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from,
+	       sizeof(*from));
+
+	os_free(buf);
+	wpa_scan_results_free(res);
+	return;
+
+fail:
+	os_free(buf);
+	wpa_scan_results_free(res);
+	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+}
+
+
+static void wpa_priv_cmd_get_scan_results(struct wpa_priv_interface *iface,
+					  struct sockaddr_un *from)
+{
+	if (iface->drv_priv == NULL)
+		return;
+
+	if (iface->driver->get_scan_results2)
+		wpa_priv_get_scan_results2(iface, from);
+	else
+		sendto(iface->fd, "", 0, 0, (struct sockaddr *) from,
+		       sizeof(*from));
+}
+
+
+static void wpa_priv_cmd_authenticate(struct wpa_priv_interface *iface,
+				      void *buf, size_t len)
+{
+	struct wpa_driver_auth_params params;
+	struct privsep_cmd_authenticate *auth;
+	int res, i;
+
+	if (iface->drv_priv == NULL || iface->driver->authenticate == NULL)
+		return;
+
+	if (len < sizeof(*auth)) {
+		wpa_printf(MSG_DEBUG, "Invalid authentication request");
+		return;
+	}
+
+	auth = buf;
+	if (sizeof(*auth) + auth->ie_len + auth->sae_data_len > len) {
+		wpa_printf(MSG_DEBUG, "Authentication request overflow");
+		return;
+	}
+
+	os_memset(&params, 0, sizeof(params));
+	params.freq = auth->freq;
+	params.bssid = auth->bssid;
+	params.ssid = auth->ssid;
+	if (auth->ssid_len > SSID_MAX_LEN)
+		return;
+	params.ssid_len = auth->ssid_len;
+	params.auth_alg = auth->auth_alg;
+	for (i = 0; i < 4; i++) {
+		if (auth->wep_key_len[i]) {
+			params.wep_key[i] = auth->wep_key[i];
+			params.wep_key_len[i] = auth->wep_key_len[i];
+		}
+	}
+	params.wep_tx_keyidx = auth->wep_tx_keyidx;
+	params.local_state_change = auth->local_state_change;
+	params.p2p = auth->p2p;
+	if (auth->ie_len) {
+		params.ie = (u8 *) (auth + 1);
+		params.ie_len = auth->ie_len;
+	}
+	if (auth->sae_data_len) {
+		params.sae_data = ((u8 *) (auth + 1)) + auth->ie_len;
+		params.sae_data_len = auth->sae_data_len;
+	}
+
+	res = iface->driver->authenticate(iface->drv_priv, &params);
+	wpa_printf(MSG_DEBUG, "drv->authenticate: res=%d", res);
+}
+
+
+static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface,
+				   void *buf, size_t len)
+{
+	struct wpa_driver_associate_params params;
+	struct privsep_cmd_associate *assoc;
+	u8 *bssid;
+	int res;
+
+	if (iface->drv_priv == NULL || iface->driver->associate == NULL)
+		return;
+
+	if (len < sizeof(*assoc)) {
+		wpa_printf(MSG_DEBUG, "Invalid association request");
+		return;
+	}
+
+	assoc = buf;
+	if (sizeof(*assoc) + assoc->wpa_ie_len > len) {
+		wpa_printf(MSG_DEBUG, "Association request overflow");
+		return;
+	}
+
+	os_memset(&params, 0, sizeof(params));
+	bssid = assoc->bssid;
+	if (bssid[0] | bssid[1] | bssid[2] | bssid[3] | bssid[4] | bssid[5])
+		params.bssid = bssid;
+	params.ssid = assoc->ssid;
+	if (assoc->ssid_len > SSID_MAX_LEN)
+		return;
+	params.ssid_len = assoc->ssid_len;
+	params.freq.mode = assoc->hwmode;
+	params.freq.freq = assoc->freq;
+	params.freq.channel = assoc->channel;
+	if (assoc->wpa_ie_len) {
+		params.wpa_ie = (u8 *) (assoc + 1);
+		params.wpa_ie_len = assoc->wpa_ie_len;
+	}
+	params.pairwise_suite = assoc->pairwise_suite;
+	params.group_suite = assoc->group_suite;
+	params.key_mgmt_suite = assoc->key_mgmt_suite;
+	params.auth_alg = assoc->auth_alg;
+	params.mode = assoc->mode;
+
+	res = iface->driver->associate(iface->drv_priv, &params);
+	wpa_printf(MSG_DEBUG, "drv->associate: res=%d", res);
+}
+
+
+static void wpa_priv_cmd_get_bssid(struct wpa_priv_interface *iface,
+				   struct sockaddr_un *from)
+{
+	u8 bssid[ETH_ALEN];
+
+	if (iface->drv_priv == NULL)
+		goto fail;
+
+	if (iface->driver->get_bssid == NULL ||
+	    iface->driver->get_bssid(iface->drv_priv, bssid) < 0)
+		goto fail;
+
+	sendto(iface->fd, bssid, ETH_ALEN, 0, (struct sockaddr *) from,
+	       sizeof(*from));
+	return;
+
+fail:
+	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+}
+
+
+static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface,
+				  struct sockaddr_un *from)
+{
+	u8 ssid[sizeof(int) + SSID_MAX_LEN];
+	int res;
+
+	if (iface->drv_priv == NULL)
+		goto fail;
+
+	if (iface->driver->get_ssid == NULL)
+		goto fail;
+
+	res = iface->driver->get_ssid(iface->drv_priv, &ssid[sizeof(int)]);
+	if (res < 0 || res > SSID_MAX_LEN)
+		goto fail;
+	os_memcpy(ssid, &res, sizeof(int));
+
+	sendto(iface->fd, ssid, sizeof(ssid), 0, (struct sockaddr *) from,
+	       sizeof(*from));
+	return;
+
+fail:
+	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+}
+
+
+static void wpa_priv_cmd_set_key(struct wpa_priv_interface *iface,
+				 void *buf, size_t len)
+{
+	struct privsep_cmd_set_key *params;
+	int res;
+
+	if (iface->drv_priv == NULL || iface->driver->set_key == NULL)
+		return;
+
+	if (len != sizeof(*params)) {
+		wpa_printf(MSG_DEBUG, "Invalid set_key request");
+		return;
+	}
+
+	params = buf;
+
+	res = iface->driver->set_key(iface->ifname, iface->drv_priv,
+				     params->alg,
+				     params->addr, params->key_idx,
+				     params->set_tx,
+				     params->seq_len ? params->seq : NULL,
+				     params->seq_len,
+				     params->key_len ? params->key : NULL,
+				     params->key_len);
+	wpa_printf(MSG_DEBUG, "drv->set_key: res=%d", res);
+}
+
+
+static void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface,
+				  struct sockaddr_un *from)
+{
+	struct wpa_driver_capa capa;
+
+	if (iface->drv_priv == NULL)
+		goto fail;
+
+	if (iface->driver->get_capa == NULL ||
+	    iface->driver->get_capa(iface->drv_priv, &capa) < 0)
+		goto fail;
+
+	/* For now, no support for passing extended_capa pointers */
+	capa.extended_capa = NULL;
+	capa.extended_capa_mask = NULL;
+	capa.extended_capa_len = 0;
+	sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from,
+	       sizeof(*from));
+	return;
+
+fail:
+	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+}
+
+
+static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf,
+			   size_t len)
+{
+	struct wpa_priv_interface *iface = ctx;
+	struct msghdr msg;
+	struct iovec io[2];
+
+	io[0].iov_base = (u8 *) src_addr;
+	io[0].iov_len = ETH_ALEN;
+	io[1].iov_base = (u8 *) buf;
+	io[1].iov_len = len;
+
+	os_memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 2;
+	msg.msg_name = &iface->l2_addr;
+	msg.msg_namelen = sizeof(iface->l2_addr);
+
+	if (sendmsg(iface->fd, &msg, 0) < 0) {
+		wpa_printf(MSG_ERROR, "sendmsg(l2 rx): %s", strerror(errno));
+	}
+}
+
+
+static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface,
+				     struct sockaddr_un *from,
+				     void *buf, size_t len)
+{
+	int *reg_cmd = buf;
+	u8 own_addr[ETH_ALEN];
+	int res;
+	u16 proto;
+
+	if (len != 2 * sizeof(int)) {
+		wpa_printf(MSG_DEBUG, "Invalid l2_register length %lu",
+			   (unsigned long) len);
+		return;
+	}
+
+	proto = reg_cmd[0];
+	if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH &&
+	    proto != ETH_P_80211_ENCAP) {
+		wpa_printf(MSG_DEBUG, "Refused l2_packet connection for "
+			   "ethertype 0x%x", proto);
+		return;
+	}
+
+	if (iface->l2) {
+		wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet "
+			   "instance");
+		l2_packet_deinit(iface->l2);
+		iface->l2 = NULL;
+	}
+
+	os_memcpy(&iface->l2_addr, from, sizeof(iface->l2_addr));
+
+	iface->l2 = l2_packet_init(iface->ifname, NULL, proto,
+				   wpa_priv_l2_rx, iface, reg_cmd[1]);
+	if (iface->l2 == NULL) {
+		wpa_printf(MSG_DEBUG, "Failed to initialize l2_packet "
+			   "instance for protocol %d", proto);
+		return;
+	}
+
+	if (l2_packet_get_own_addr(iface->l2, own_addr) < 0) {
+		wpa_printf(MSG_DEBUG, "Failed to get own address from "
+			   "l2_packet");
+		l2_packet_deinit(iface->l2);
+		iface->l2 = NULL;
+		return;
+	}
+
+	res = sendto(iface->fd, own_addr, ETH_ALEN, 0,
+		     (struct sockaddr *) from, sizeof(*from));
+	wpa_printf(MSG_DEBUG, "L2 registration: res=%d", res);
+}
+
+
+static void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface,
+				       struct sockaddr_un *from)
+{
+	if (iface->l2) {
+		l2_packet_deinit(iface->l2);
+		iface->l2 = NULL;
+	}
+}
+
+
+static void wpa_priv_cmd_l2_notify_auth_start(struct wpa_priv_interface *iface,
+					      struct sockaddr_un *from)
+{
+	if (iface->l2)
+		l2_packet_notify_auth_start(iface->l2);
+}
+
+
+static void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface,
+				 struct sockaddr_un *from,
+				 void *buf, size_t len)
+{
+	u8 *dst_addr;
+	u16 proto;
+	int res;
+
+	if (iface->l2 == NULL)
+		return;
+
+	if (len < ETH_ALEN + 2) {
+		wpa_printf(MSG_DEBUG, "Too short L2 send packet (len=%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+	dst_addr = buf;
+	os_memcpy(&proto, buf + ETH_ALEN, 2);
+
+	if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) {
+		wpa_printf(MSG_DEBUG, "Refused l2_packet send for ethertype "
+			   "0x%x", proto);
+		return;
+	}
+
+	res = l2_packet_send(iface->l2, dst_addr, proto, buf + ETH_ALEN + 2,
+			     len - ETH_ALEN - 2);
+	wpa_printf(MSG_DEBUG, "L2 send: res=%d", res);
+}
+
+
+static void wpa_priv_cmd_set_country(struct wpa_priv_interface *iface,
+				     char *buf)
+{
+	if (iface->drv_priv == NULL || iface->driver->set_country == NULL ||
+	    *buf == '\0')
+		return;
+
+	iface->driver->set_country(iface->drv_priv, buf);
+}
+
+
+static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_priv_interface *iface = eloop_ctx;
+	char buf[2000], *pos;
+	void *cmd_buf;
+	size_t cmd_len;
+	int res, cmd;
+	struct sockaddr_un from;
+	socklen_t fromlen = sizeof(from);
+
+	res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
+		       &fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "recvfrom: %s", strerror(errno));
+		return;
+	}
+
+	if (res < (int) sizeof(int)) {
+		wpa_printf(MSG_DEBUG, "Too short command (len=%d)", res);
+		return;
+	}
+
+	os_memcpy(&cmd, buf, sizeof(int));
+	wpa_printf(MSG_DEBUG, "Command %d for interface %s",
+		   cmd, iface->ifname);
+	cmd_buf = &buf[sizeof(int)];
+	cmd_len = res - sizeof(int);
+
+	switch (cmd) {
+	case PRIVSEP_CMD_REGISTER:
+		wpa_priv_cmd_register(iface, &from);
+		break;
+	case PRIVSEP_CMD_UNREGISTER:
+		wpa_priv_cmd_unregister(iface, &from);
+		break;
+	case PRIVSEP_CMD_SCAN:
+		wpa_priv_cmd_scan(iface, cmd_buf, cmd_len);
+		break;
+	case PRIVSEP_CMD_GET_SCAN_RESULTS:
+		wpa_priv_cmd_get_scan_results(iface, &from);
+		break;
+	case PRIVSEP_CMD_ASSOCIATE:
+		wpa_priv_cmd_associate(iface, cmd_buf, cmd_len);
+		break;
+	case PRIVSEP_CMD_GET_BSSID:
+		wpa_priv_cmd_get_bssid(iface, &from);
+		break;
+	case PRIVSEP_CMD_GET_SSID:
+		wpa_priv_cmd_get_ssid(iface, &from);
+		break;
+	case PRIVSEP_CMD_SET_KEY:
+		wpa_priv_cmd_set_key(iface, cmd_buf, cmd_len);
+		break;
+	case PRIVSEP_CMD_GET_CAPA:
+		wpa_priv_cmd_get_capa(iface, &from);
+		break;
+	case PRIVSEP_CMD_L2_REGISTER:
+		wpa_priv_cmd_l2_register(iface, &from, cmd_buf, cmd_len);
+		break;
+	case PRIVSEP_CMD_L2_UNREGISTER:
+		wpa_priv_cmd_l2_unregister(iface, &from);
+		break;
+	case PRIVSEP_CMD_L2_NOTIFY_AUTH_START:
+		wpa_priv_cmd_l2_notify_auth_start(iface, &from);
+		break;
+	case PRIVSEP_CMD_L2_SEND:
+		wpa_priv_cmd_l2_send(iface, &from, cmd_buf, cmd_len);
+		break;
+	case PRIVSEP_CMD_SET_COUNTRY:
+		pos = cmd_buf;
+		if (pos + cmd_len >= buf + sizeof(buf))
+			break;
+		pos[cmd_len] = '\0';
+		wpa_priv_cmd_set_country(iface, pos);
+		break;
+	case PRIVSEP_CMD_AUTHENTICATE:
+		wpa_priv_cmd_authenticate(iface, cmd_buf, cmd_len);
+		break;
+	}
+}
+
+
+static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface)
+{
+	if (iface->drv_priv && iface->driver->deinit)
+		iface->driver->deinit(iface->drv_priv);
+
+	if (iface->fd >= 0) {
+		eloop_unregister_read_sock(iface->fd);
+		close(iface->fd);
+		unlink(iface->sock_name);
+	}
+
+	if (iface->l2)
+		l2_packet_deinit(iface->l2);
+
+	os_free(iface->ifname);
+	os_free(iface->driver_name);
+	os_free(iface->sock_name);
+	os_free(iface);
+}
+
+
+static struct wpa_priv_interface *
+wpa_priv_interface_init(const char *dir, const char *params)
+{
+	struct wpa_priv_interface *iface;
+	char *pos;
+	size_t len;
+	struct sockaddr_un addr;
+	int i;
+
+	pos = os_strchr(params, ':');
+	if (pos == NULL)
+		return NULL;
+
+	iface = os_zalloc(sizeof(*iface));
+	if (iface == NULL)
+		return NULL;
+	iface->fd = -1;
+
+	len = pos - params;
+	iface->driver_name = dup_binstr(params, len);
+	if (iface->driver_name == NULL) {
+		wpa_priv_interface_deinit(iface);
+		return NULL;
+	}
+
+	for (i = 0; wpa_drivers[i]; i++) {
+		if (os_strcmp(iface->driver_name,
+			      wpa_drivers[i]->name) == 0) {
+			iface->driver = wpa_drivers[i];
+			break;
+		}
+	}
+	if (iface->driver == NULL) {
+		wpa_printf(MSG_ERROR, "Unsupported driver '%s'",
+			   iface->driver_name);
+		wpa_priv_interface_deinit(iface);
+		return NULL;
+	}
+
+	pos++;
+	iface->ifname = os_strdup(pos);
+	if (iface->ifname == NULL) {
+		wpa_priv_interface_deinit(iface);
+		return NULL;
+	}
+
+	len = os_strlen(dir) + 1 + os_strlen(iface->ifname);
+	iface->sock_name = os_malloc(len + 1);
+	if (iface->sock_name == NULL) {
+		wpa_priv_interface_deinit(iface);
+		return NULL;
+	}
+
+	os_snprintf(iface->sock_name, len + 1, "%s/%s", dir, iface->ifname);
+	if (os_strlen(iface->sock_name) >= sizeof(addr.sun_path)) {
+		wpa_priv_interface_deinit(iface);
+		return NULL;
+	}
+
+	iface->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (iface->fd < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
+		wpa_priv_interface_deinit(iface);
+		return NULL;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	os_strlcpy(addr.sun_path, iface->sock_name, sizeof(addr.sun_path));
+
+	if (bind(iface->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_DEBUG, "bind(PF_UNIX) failed: %s",
+			   strerror(errno));
+		if (connect(iface->fd, (struct sockaddr *) &addr,
+			    sizeof(addr)) < 0) {
+			wpa_printf(MSG_DEBUG, "Socket exists, but does not "
+				   "allow connections - assuming it was "
+				   "leftover from forced program termination");
+			if (unlink(iface->sock_name) < 0) {
+				wpa_printf(MSG_ERROR,
+					   "Could not unlink existing ctrl_iface socket '%s': %s",
+					   iface->sock_name, strerror(errno));
+				goto fail;
+			}
+			if (bind(iface->fd, (struct sockaddr *) &addr,
+				 sizeof(addr)) < 0) {
+				wpa_printf(MSG_ERROR,
+					   "wpa-priv-iface-init: bind(PF_UNIX): %s",
+					   strerror(errno));
+				goto fail;
+			}
+			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
+				   "socket '%s'", iface->sock_name);
+		} else {
+			wpa_printf(MSG_INFO, "Socket exists and seems to be "
+				   "in use - cannot override it");
+			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
+				   "not used anymore", iface->sock_name);
+			goto fail;
+		}
+	}
+
+	if (chmod(iface->sock_name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
+		wpa_printf(MSG_ERROR, "chmod: %s", strerror(errno));
+		goto fail;
+	}
+
+	eloop_register_read_sock(iface->fd, wpa_priv_receive, iface, NULL);
+
+	return iface;
+
+fail:
+	wpa_priv_interface_deinit(iface);
+	return NULL;
+}
+
+
+static int wpa_priv_send_event(struct wpa_priv_interface *iface, int event,
+			       const void *data, size_t data_len)
+{
+	struct msghdr msg;
+	struct iovec io[2];
+
+	io[0].iov_base = &event;
+	io[0].iov_len = sizeof(event);
+	io[1].iov_base = (u8 *) data;
+	io[1].iov_len = data_len;
+
+	os_memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = io;
+	msg.msg_iovlen = data ? 2 : 1;
+	msg.msg_name = &iface->drv_addr;
+	msg.msg_namelen = sizeof(iface->drv_addr);
+
+	if (sendmsg(iface->fd, &msg, 0) < 0) {
+		wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void wpa_priv_send_auth(struct wpa_priv_interface *iface,
+			       union wpa_event_data *data)
+{
+	size_t buflen = sizeof(struct privsep_event_auth) + data->auth.ies_len;
+	struct privsep_event_auth *auth;
+	u8 *buf, *pos;
+
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return;
+
+	auth = (struct privsep_event_auth *) buf;
+	pos = (u8 *) (auth + 1);
+
+	os_memcpy(auth->peer, data->auth.peer, ETH_ALEN);
+	os_memcpy(auth->bssid, data->auth.bssid, ETH_ALEN);
+	auth->auth_type = data->auth.auth_type;
+	auth->auth_transaction = data->auth.auth_transaction;
+	auth->status_code = data->auth.status_code;
+	if (data->auth.ies) {
+		os_memcpy(pos, data->auth.ies, data->auth.ies_len);
+		auth->ies_len = data->auth.ies_len;
+	}
+
+	wpa_priv_send_event(iface, PRIVSEP_EVENT_AUTH, buf, buflen);
+
+	os_free(buf);
+}
+
+
+static void wpa_priv_send_assoc(struct wpa_priv_interface *iface, int event,
+				union wpa_event_data *data)
+{
+	size_t buflen = 3 * sizeof(int);
+	u8 *buf, *pos;
+	int len;
+
+	if (data) {
+		buflen += data->assoc_info.req_ies_len +
+			data->assoc_info.resp_ies_len +
+			data->assoc_info.beacon_ies_len;
+	}
+
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return;
+
+	pos = buf;
+
+	if (data && data->assoc_info.req_ies) {
+		len = data->assoc_info.req_ies_len;
+		os_memcpy(pos, &len, sizeof(int));
+		pos += sizeof(int);
+		os_memcpy(pos, data->assoc_info.req_ies, len);
+		pos += len;
+	} else {
+		len = 0;
+		os_memcpy(pos, &len, sizeof(int));
+		pos += sizeof(int);
+	}
+
+	if (data && data->assoc_info.resp_ies) {
+		len = data->assoc_info.resp_ies_len;
+		os_memcpy(pos, &len, sizeof(int));
+		pos += sizeof(int);
+		os_memcpy(pos, data->assoc_info.resp_ies, len);
+		pos += len;
+	} else {
+		len = 0;
+		os_memcpy(pos, &len, sizeof(int));
+		pos += sizeof(int);
+	}
+
+	if (data && data->assoc_info.beacon_ies) {
+		len = data->assoc_info.beacon_ies_len;
+		os_memcpy(pos, &len, sizeof(int));
+		pos += sizeof(int);
+		os_memcpy(pos, data->assoc_info.beacon_ies, len);
+		pos += len;
+	} else {
+		len = 0;
+		os_memcpy(pos, &len, sizeof(int));
+		pos += sizeof(int);
+	}
+
+	wpa_priv_send_event(iface, event, buf, buflen);
+
+	os_free(buf);
+}
+
+
+static void wpa_priv_send_interface_status(struct wpa_priv_interface *iface,
+					   union wpa_event_data *data)
+{
+	int ievent;
+	size_t len, maxlen;
+	u8 *buf;
+	char *ifname;
+
+	if (data == NULL)
+		return;
+
+	ievent = data->interface_status.ievent;
+	maxlen = sizeof(data->interface_status.ifname);
+	ifname = data->interface_status.ifname;
+	for (len = 0; len < maxlen && ifname[len]; len++)
+		;
+
+	buf = os_malloc(sizeof(int) + len);
+	if (buf == NULL)
+		return;
+
+	os_memcpy(buf, &ievent, sizeof(int));
+	os_memcpy(buf + sizeof(int), ifname, len);
+
+	wpa_priv_send_event(iface, PRIVSEP_EVENT_INTERFACE_STATUS,
+			    buf, sizeof(int) + len);
+
+	os_free(buf);
+
+}
+
+
+static void wpa_priv_send_ft_response(struct wpa_priv_interface *iface,
+				      union wpa_event_data *data)
+{
+	size_t len;
+	u8 *buf, *pos;
+
+	if (data == NULL || data->ft_ies.ies == NULL)
+		return;
+
+	len = sizeof(int) + ETH_ALEN + data->ft_ies.ies_len;
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return;
+
+	pos = buf;
+	os_memcpy(pos, &data->ft_ies.ft_action, sizeof(int));
+	pos += sizeof(int);
+	os_memcpy(pos, data->ft_ies.target_ap, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, data->ft_ies.ies, data->ft_ies.ies_len);
+
+	wpa_priv_send_event(iface, PRIVSEP_EVENT_FT_RESPONSE, buf, len);
+
+	os_free(buf);
+
+}
+
+
+void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+			  union wpa_event_data *data)
+{
+	struct wpa_priv_interface *iface = ctx;
+
+	wpa_printf(MSG_DEBUG, "%s - event=%d", __func__, event);
+
+	if (!iface->wpas_registered) {
+		wpa_printf(MSG_DEBUG, "Driver event received, but "
+			   "wpa_supplicant not registered");
+		return;
+	}
+
+	switch (event) {
+	case EVENT_ASSOC:
+		wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOC, data);
+		break;
+	case EVENT_DISASSOC:
+		wpa_priv_send_event(iface, PRIVSEP_EVENT_DISASSOC, NULL, 0);
+		break;
+	case EVENT_ASSOCINFO:
+		if (data == NULL)
+			return;
+		wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOCINFO, data);
+		break;
+	case EVENT_MICHAEL_MIC_FAILURE:
+		if (data == NULL)
+			return;
+		wpa_priv_send_event(iface, PRIVSEP_EVENT_MICHAEL_MIC_FAILURE,
+				    &data->michael_mic_failure.unicast,
+				    sizeof(int));
+		break;
+	case EVENT_SCAN_STARTED:
+		wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_STARTED, NULL,
+				    0);
+		break;
+	case EVENT_SCAN_RESULTS:
+		wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_RESULTS, NULL,
+				    0);
+		break;
+	case EVENT_INTERFACE_STATUS:
+		wpa_priv_send_interface_status(iface, data);
+		break;
+	case EVENT_PMKID_CANDIDATE:
+		if (data == NULL)
+			return;
+		wpa_priv_send_event(iface, PRIVSEP_EVENT_PMKID_CANDIDATE,
+				    &data->pmkid_candidate,
+				    sizeof(struct pmkid_candidate));
+		break;
+	case EVENT_STKSTART:
+		if (data == NULL)
+			return;
+		wpa_priv_send_event(iface, PRIVSEP_EVENT_STKSTART,
+				    &data->stkstart.peer, ETH_ALEN);
+		break;
+	case EVENT_FT_RESPONSE:
+		wpa_priv_send_ft_response(iface, data);
+		break;
+	case EVENT_AUTH:
+		wpa_priv_send_auth(iface, data);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "Unsupported driver event %d (%s) - TODO",
+			   event, event_to_string(event));
+		break;
+	}
+}
+
+
+void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
+			     const u8 *buf, size_t len)
+{
+	struct wpa_priv_interface *iface = ctx;
+	struct msghdr msg;
+	struct iovec io[3];
+	int event = PRIVSEP_EVENT_RX_EAPOL;
+
+	wpa_printf(MSG_DEBUG, "RX EAPOL from driver");
+	io[0].iov_base = &event;
+	io[0].iov_len = sizeof(event);
+	io[1].iov_base = (u8 *) src_addr;
+	io[1].iov_len = ETH_ALEN;
+	io[2].iov_base = (u8 *) buf;
+	io[2].iov_len = len;
+
+	os_memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 3;
+	msg.msg_name = &iface->drv_addr;
+	msg.msg_namelen = sizeof(iface->drv_addr);
+
+	if (sendmsg(iface->fd, &msg, 0) < 0)
+		wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s",
+			   strerror(errno));
+}
+
+
+static void wpa_priv_terminate(int sig, void *signal_ctx)
+{
+	wpa_printf(MSG_DEBUG, "wpa_priv termination requested");
+	eloop_terminate();
+}
+
+
+static void wpa_priv_fd_workaround(void)
+{
+#ifdef __linux__
+	int s, i;
+	/* When started from pcmcia-cs scripts, wpa_supplicant might start with
+	 * fd 0, 1, and 2 closed. This will cause some issues because many
+	 * places in wpa_supplicant are still printing out to stdout. As a
+	 * workaround, make sure that fd's 0, 1, and 2 are not used for other
+	 * sockets. */
+	for (i = 0; i < 3; i++) {
+		s = open("/dev/null", O_RDWR);
+		if (s > 2) {
+			close(s);
+			break;
+		}
+	}
+#endif /* __linux__ */
+}
+
+
+static void usage(void)
+{
+	printf("wpa_priv v" VERSION_STR "\n"
+	       "Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> and "
+	       "contributors\n"
+	       "\n"
+	       "usage:\n"
+	       "  wpa_priv [-Bdd] [-c<ctrl dir>] [-P<pid file>] "
+	       "<driver:ifname> \\\n"
+	       "           [driver:ifname ...]\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+	int c, i;
+	int ret = -1;
+	char *pid_file = NULL;
+	int daemonize = 0;
+	char *ctrl_dir = "/var/run/wpa_priv";
+	struct wpa_priv_interface *interfaces = NULL, *iface;
+
+	if (os_program_init())
+		return -1;
+
+	wpa_priv_fd_workaround();
+
+	for (;;) {
+		c = getopt(argc, argv, "Bc:dP:");
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'B':
+			daemonize++;
+			break;
+		case 'c':
+			ctrl_dir = optarg;
+			break;
+		case 'd':
+			wpa_debug_level--;
+			break;
+		case 'P':
+			pid_file = os_rel2abs_path(optarg);
+			break;
+		default:
+			usage();
+			goto out2;
+		}
+	}
+
+	if (optind >= argc) {
+		usage();
+		goto out2;
+	}
+
+	wpa_printf(MSG_DEBUG, "wpa_priv control directory: '%s'", ctrl_dir);
+
+	if (eloop_init()) {
+		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+		goto out2;
+	}
+
+	for (i = optind; i < argc; i++) {
+		wpa_printf(MSG_DEBUG, "Adding driver:interface %s", argv[i]);
+		iface = wpa_priv_interface_init(ctrl_dir, argv[i]);
+		if (iface == NULL)
+			goto out;
+		iface->next = interfaces;
+		interfaces = iface;
+	}
+
+	if (daemonize && os_daemonize(pid_file))
+		goto out;
+
+	eloop_register_signal_terminate(wpa_priv_terminate, NULL);
+	eloop_run();
+
+	ret = 0;
+
+out:
+	iface = interfaces;
+	while (iface) {
+		struct wpa_priv_interface *prev = iface;
+		iface = iface->next;
+		wpa_priv_interface_deinit(prev);
+	}
+
+	eloop_destroy();
+
+out2:
+	if (daemonize)
+		os_daemonize_terminate(pid_file);
+	os_free(pid_file);
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/hostap/wpa_supplicant/wpa_supplicant.c b/hostap/wpa_supplicant/wpa_supplicant.c
new file mode 100644
index 0000000..d116e03
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_supplicant.c
@@ -0,0 +1,5870 @@
+/*
+ * WPA Supplicant
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements functions for registering and unregistering
+ * %wpa_supplicant interfaces. In addition, this file contains number of
+ * functions for managing network connections.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/random.h"
+#include "crypto/sha1.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "eap_peer/eap.h"
+#include "eap_peer/eap_proxy.h"
+#include "eap_server/eap_methods.h"
+#include "rsn_supp/wpa.h"
+#include "eloop.h"
+#include "config.h"
+#include "utils/ext_password.h"
+#include "l2_packet/l2_packet.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "ctrl_iface.h"
+#include "pcsc_funcs.h"
+#include "common/version.h"
+#include "rsn_supp/preauth.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "common/wpa_ctrl.h"
+#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
+#include "p2p/p2p.h"
+#include "fst/fst.h"
+#include "blacklist.h"
+#include "wpas_glue.h"
+#include "wps_supplicant.h"
+#include "ibss_rsn.h"
+#include "sme.h"
+#include "gas_query.h"
+#include "ap.h"
+#include "p2p_supplicant.h"
+#include "wifi_display.h"
+#include "notify.h"
+#include "bgscan.h"
+#include "autoscan.h"
+#include "bss.h"
+#include "scan.h"
+#include "offchannel.h"
+#include "hs20_supplicant.h"
+#include "wnm_sta.h"
+#include "wpas_kay.h"
+#include "mesh.h"
+
+const char *const wpa_supplicant_version =
+"wpa_supplicant v" VERSION_STR "\n"
+"Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors";
+
+const char *const wpa_supplicant_license =
+"This software may be distributed under the terms of the BSD license.\n"
+"See README for more details.\n"
+#ifdef EAP_TLS_OPENSSL
+"\nThis product includes software developed by the OpenSSL Project\n"
+"for use in the OpenSSL Toolkit (http://www.openssl.org/)\n"
+#endif /* EAP_TLS_OPENSSL */
+;
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+/* Long text divided into parts in order to fit in C89 strings size limits. */
+const char *const wpa_supplicant_full_license1 =
+"";
+const char *const wpa_supplicant_full_license2 =
+"This software may be distributed under the terms of the BSD license.\n"
+"\n"
+"Redistribution and use in source and binary forms, with or without\n"
+"modification, are permitted provided that the following conditions are\n"
+"met:\n"
+"\n";
+const char *const wpa_supplicant_full_license3 =
+"1. Redistributions of source code must retain the above copyright\n"
+"   notice, this list of conditions and the following disclaimer.\n"
+"\n"
+"2. Redistributions in binary form must reproduce the above copyright\n"
+"   notice, this list of conditions and the following disclaimer in the\n"
+"   documentation and/or other materials provided with the distribution.\n"
+"\n";
+const char *const wpa_supplicant_full_license4 =
+"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
+"   names of its contributors may be used to endorse or promote products\n"
+"   derived from this software without specific prior written permission.\n"
+"\n"
+"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n";
+const char *const wpa_supplicant_full_license5 =
+"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+"\n";
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+/* Configure default/group WEP keys for static WEP */
+int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	int i, set = 0;
+
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		if (ssid->wep_key_len[i] == 0)
+			continue;
+
+		set = 1;
+		wpa_drv_set_key(wpa_s, WPA_ALG_WEP, NULL,
+				i, i == ssid->wep_tx_keyidx, NULL, 0,
+				ssid->wep_key[i], ssid->wep_key_len[i]);
+	}
+
+	return set;
+}
+
+
+int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
+				    struct wpa_ssid *ssid)
+{
+	u8 key[32];
+	size_t keylen;
+	enum wpa_alg alg;
+	u8 seq[6] = { 0 };
+	int ret;
+
+	/* IBSS/WPA-None uses only one key (Group) for both receiving and
+	 * sending unicast and multicast packets. */
+
+	if (ssid->mode != WPAS_MODE_IBSS) {
+		wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid mode %d (not "
+			"IBSS/ad-hoc) for WPA-None", ssid->mode);
+		return -1;
+	}
+
+	if (!ssid->psk_set) {
+		wpa_msg(wpa_s, MSG_INFO, "WPA: No PSK configured for "
+			"WPA-None");
+		return -1;
+	}
+
+	switch (wpa_s->group_cipher) {
+	case WPA_CIPHER_CCMP:
+		os_memcpy(key, ssid->psk, 16);
+		keylen = 16;
+		alg = WPA_ALG_CCMP;
+		break;
+	case WPA_CIPHER_GCMP:
+		os_memcpy(key, ssid->psk, 16);
+		keylen = 16;
+		alg = WPA_ALG_GCMP;
+		break;
+	case WPA_CIPHER_TKIP:
+		/* WPA-None uses the same Michael MIC key for both TX and RX */
+		os_memcpy(key, ssid->psk, 16 + 8);
+		os_memcpy(key + 16 + 8, ssid->psk + 16, 8);
+		keylen = 32;
+		alg = WPA_ALG_TKIP;
+		break;
+	default:
+		wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid group cipher %d for "
+			"WPA-None", wpa_s->group_cipher);
+		return -1;
+	}
+
+	/* TODO: should actually remember the previously used seq#, both for TX
+	 * and RX from each STA.. */
+
+	ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen);
+	os_memset(key, 0, sizeof(key));
+	return ret;
+}
+
+
+static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	const u8 *bssid = wpa_s->bssid;
+	if (is_zero_ether_addr(bssid))
+		bssid = wpa_s->pending_bssid;
+	wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.",
+		MAC2STR(bssid));
+	wpa_blacklist_add(wpa_s, bssid);
+	wpa_sm_notify_disassoc(wpa_s->wpa);
+	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+	wpa_s->reassociate = 1;
+
+	/*
+	 * If we timed out, the AP or the local radio may be busy.
+	 * So, wait a second until scanning again.
+	 */
+	wpa_supplicant_req_scan(wpa_s, 1, 0);
+}
+
+
+/**
+ * wpa_supplicant_req_auth_timeout - Schedule a timeout for authentication
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @sec: Number of seconds after which to time out authentication
+ * @usec: Number of microseconds after which to time out authentication
+ *
+ * This function is used to schedule a timeout for the current authentication
+ * attempt.
+ */
+void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
+				     int sec, int usec)
+{
+	if (wpa_s->conf->ap_scan == 0 &&
+	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
+		return;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec "
+		"%d usec", sec, usec);
+	eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+	eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL);
+}
+
+
+/**
+ * wpa_supplicant_cancel_auth_timeout - Cancel authentication timeout
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to cancel authentication timeout scheduled with
+ * wpa_supplicant_req_auth_timeout() and it is called when authentication has
+ * been completed.
+ */
+void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
+{
+	wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout");
+	eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+	wpa_blacklist_del(wpa_s, wpa_s->bssid);
+}
+
+
+/**
+ * wpa_supplicant_initiate_eapol - Configure EAPOL state machine
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to configure EAPOL state machine based on the selected
+ * authentication mode.
+ */
+void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
+{
+#ifdef IEEE8021X_EAPOL
+	struct eapol_config eapol_conf;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+#ifdef CONFIG_IBSS_RSN
+	if (ssid->mode == WPAS_MODE_IBSS &&
+	    wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
+	    wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) {
+		/*
+		 * RSN IBSS authentication is per-STA and we can disable the
+		 * per-BSSID EAPOL authentication.
+		 */
+		eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized);
+		eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+		eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE);
+		return;
+	}
+#endif /* CONFIG_IBSS_RSN */
+
+	eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+	eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE);
+
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE)
+		eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized);
+	else
+		eapol_sm_notify_portControl(wpa_s->eapol, Auto);
+
+	os_memset(&eapol_conf, 0, sizeof(eapol_conf));
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+		eapol_conf.accept_802_1x_keys = 1;
+		eapol_conf.required_keys = 0;
+		if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_UNICAST) {
+			eapol_conf.required_keys |= EAPOL_REQUIRE_KEY_UNICAST;
+		}
+		if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_BROADCAST) {
+			eapol_conf.required_keys |=
+				EAPOL_REQUIRE_KEY_BROADCAST;
+		}
+
+		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)
+			eapol_conf.required_keys = 0;
+	}
+	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;
+
+#ifdef CONFIG_WPS
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
+		eapol_conf.wps |= EAPOL_LOCAL_WPS_IN_USE;
+		if (wpa_s->current_bss) {
+			struct wpabuf *ie;
+			ie = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss,
+							 WPS_IE_VENDOR_TYPE);
+			if (ie) {
+				if (wps_is_20(ie))
+					eapol_conf.wps |=
+						EAPOL_PEER_IS_WPS20_AP;
+				wpabuf_free(ie);
+			}
+		}
+	}
+#endif /* CONFIG_WPS */
+
+	eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
+
+	ieee802_1x_alloc_kay_sm(wpa_s, ssid);
+#endif /* IEEE8021X_EAPOL */
+}
+
+
+/**
+ * wpa_supplicant_set_non_wpa_policy - Set WPA parameters to non-WPA mode
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @ssid: Configuration data for the network
+ *
+ * This function is used to configure WPA state machine and related parameters
+ * to a mode where WPA is not enabled. This is called as part of the
+ * authentication configuration when the selected network does not use WPA.
+ */
+void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
+				       struct wpa_ssid *ssid)
+{
+	int i;
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
+		wpa_s->key_mgmt = WPA_KEY_MGMT_WPS;
+	else if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)
+		wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+	else
+		wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
+	wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0);
+	wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
+	wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+	wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+	wpa_s->group_cipher = WPA_CIPHER_NONE;
+	wpa_s->mgmt_group_cipher = 0;
+
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		if (ssid->wep_key_len[i] > 5) {
+			wpa_s->pairwise_cipher = WPA_CIPHER_WEP104;
+			wpa_s->group_cipher = WPA_CIPHER_WEP104;
+			break;
+		} else if (ssid->wep_key_len[i] > 0) {
+			wpa_s->pairwise_cipher = WPA_CIPHER_WEP40;
+			wpa_s->group_cipher = WPA_CIPHER_WEP40;
+			break;
+		}
+	}
+
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, 0);
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt);
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE,
+			 wpa_s->pairwise_cipher);
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
+#ifdef CONFIG_IEEE80211W
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
+			 wpa_s->mgmt_group_cipher);
+#endif /* CONFIG_IEEE80211W */
+
+	pmksa_cache_clear_current(wpa_s->wpa);
+}
+
+
+void free_hw_features(struct wpa_supplicant *wpa_s)
+{
+	int i;
+	if (wpa_s->hw.modes == NULL)
+		return;
+
+	for (i = 0; i < wpa_s->hw.num_modes; i++) {
+		os_free(wpa_s->hw.modes[i].channels);
+		os_free(wpa_s->hw.modes[i].rates);
+	}
+
+	os_free(wpa_s->hw.modes);
+	wpa_s->hw.modes = NULL;
+}
+
+
+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);
+	wpa_s->scard = NULL;
+	wpa_sm_set_scard_ctx(wpa_s->wpa, NULL);
+	eapol_sm_register_scard_ctx(wpa_s->eapol, NULL);
+	l2_packet_deinit(wpa_s->l2);
+	wpa_s->l2 = NULL;
+	if (wpa_s->l2_br) {
+		l2_packet_deinit(wpa_s->l2_br);
+		wpa_s->l2_br = NULL;
+	}
+#ifdef CONFIG_TESTING_OPTIONS
+	l2_packet_deinit(wpa_s->l2_test);
+	wpa_s->l2_test = NULL;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (wpa_s->conf != NULL) {
+		struct wpa_ssid *ssid;
+		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+			wpas_notify_network_removed(wpa_s, ssid);
+	}
+
+	os_free(wpa_s->confname);
+	wpa_s->confname = NULL;
+
+	os_free(wpa_s->confanother);
+	wpa_s->confanother = NULL;
+
+	wpa_sm_set_eapol(wpa_s->wpa, NULL);
+	eapol_sm_deinit(wpa_s->eapol);
+	wpa_s->eapol = NULL;
+
+	rsn_preauth_deinit(wpa_s->wpa);
+
+#ifdef CONFIG_TDLS
+	wpa_tdls_deinit(wpa_s->wpa);
+#endif /* CONFIG_TDLS */
+
+	wmm_ac_clear_saved_tspecs(wpa_s);
+	pmksa_candidate_free(wpa_s->wpa);
+	wpa_sm_deinit(wpa_s->wpa);
+	wpa_s->wpa = NULL;
+	wpa_blacklist_clear(wpa_s);
+
+	wpa_bss_deinit(wpa_s);
+
+	wpa_supplicant_cancel_delayed_sched_scan(wpa_s);
+	wpa_supplicant_cancel_scan(wpa_s);
+	wpa_supplicant_cancel_auth_timeout(wpa_s);
+	eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL);
+#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+	eloop_cancel_timeout(wpa_supplicant_delayed_mic_error_report,
+			     wpa_s, NULL);
+#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
+
+	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+
+	wpas_wps_deinit(wpa_s);
+
+	wpabuf_free(wpa_s->pending_eapol_rx);
+	wpa_s->pending_eapol_rx = NULL;
+
+#ifdef CONFIG_IBSS_RSN
+	ibss_rsn_deinit(wpa_s->ibss_rsn);
+	wpa_s->ibss_rsn = NULL;
+#endif /* CONFIG_IBSS_RSN */
+
+	sme_deinit(wpa_s);
+
+#ifdef CONFIG_AP
+	wpa_supplicant_ap_deinit(wpa_s);
+#endif /* CONFIG_AP */
+
+	wpas_p2p_deinit(wpa_s);
+
+#ifdef CONFIG_OFFCHANNEL
+	offchannel_deinit(wpa_s);
+#endif /* CONFIG_OFFCHANNEL */
+
+	wpa_supplicant_cancel_sched_scan(wpa_s);
+
+	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;
+
+	wpas_mac_addr_rand_scan_clear(wpa_s, MAC_ADDR_RAND_ALL);
+
+	/*
+	 * Need to remove any pending gas-query radio work before the
+	 * gas_query_deinit() call because gas_query::work has not yet been set
+	 * for works that have not been started. gas_query_free() will be unable
+	 * to cancel such pending radio works and once the pending gas-query
+	 * radio work eventually gets removed, the deinit notification call to
+	 * gas_query_start_cb() would result in dereferencing freed memory.
+	 */
+	if (wpa_s->radio)
+		radio_remove_works(wpa_s, "gas-query", 0);
+	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;
+
+	os_free(wpa_s->disallow_aps_bssid);
+	wpa_s->disallow_aps_bssid = NULL;
+	os_free(wpa_s->disallow_aps_ssid);
+	wpa_s->disallow_aps_ssid = NULL;
+
+	wnm_bss_keep_alive_deinit(wpa_s);
+#ifdef CONFIG_WNM
+	wnm_deallocate_memory(wpa_s);
+#endif /* CONFIG_WNM */
+
+	ext_password_deinit(wpa_s->ext_pw);
+	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;
+	}
+
+	wmm_ac_notify_disassoc(wpa_s);
+}
+
+
+/**
+ * wpa_clear_keys - Clear keys configured for the driver
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @addr: Previously used BSSID or %NULL if not available
+ *
+ * This function clears the encryption keys that has been previously configured
+ * for the driver.
+ */
+void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+	int i, max;
+
+#ifdef CONFIG_IEEE80211W
+	max = 6;
+#else /* CONFIG_IEEE80211W */
+	max = 4;
+#endif /* CONFIG_IEEE80211W */
+
+	/* MLME-DELETEKEYS.request */
+	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) */
+		wpa_drv_mlme_setprotection(
+			wpa_s, addr,
+			MLME_SETPROTECTION_PROTECT_TYPE_NONE,
+			MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+	}
+	wpa_s->keys_cleared = (u32) -1;
+}
+
+
+/**
+ * wpa_supplicant_state_txt - Get the connection state name as a text string
+ * @state: State (wpa_state; WPA_*)
+ * Returns: The state name as a printable text string
+ */
+const char * wpa_supplicant_state_txt(enum wpa_states state)
+{
+	switch (state) {
+	case WPA_DISCONNECTED:
+		return "DISCONNECTED";
+	case WPA_INACTIVE:
+		return "INACTIVE";
+	case WPA_INTERFACE_DISABLED:
+		return "INTERFACE_DISABLED";
+	case WPA_SCANNING:
+		return "SCANNING";
+	case WPA_AUTHENTICATING:
+		return "AUTHENTICATING";
+	case WPA_ASSOCIATING:
+		return "ASSOCIATING";
+	case WPA_ASSOCIATED:
+		return "ASSOCIATED";
+	case WPA_4WAY_HANDSHAKE:
+		return "4WAY_HANDSHAKE";
+	case WPA_GROUP_HANDSHAKE:
+		return "GROUP_HANDSHAKE";
+	case WPA_COMPLETED:
+		return "COMPLETED";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+
+#ifdef CONFIG_BGSCAN
+
+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) {
+		if (bgscan_init(wpa_s, wpa_s->current_ssid, name)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
+				"bgscan");
+			/*
+			 * Live without bgscan; it is only used as a roaming
+			 * optimization, so the initial connection is not
+			 * affected.
+			 */
+		} else {
+			struct wpa_scan_results *scan_res;
+			wpa_s->bgscan_ssid = wpa_s->current_ssid;
+			scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL,
+								   0);
+			if (scan_res) {
+				bgscan_notify_scan(wpa_s, scan_res);
+				wpa_scan_results_free(scan_res);
+			}
+		}
+	} else
+		wpa_s->bgscan_ssid = NULL;
+}
+
+
+static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->bgscan_ssid != NULL) {
+		bgscan_deinit(wpa_s);
+		wpa_s->bgscan_ssid = NULL;
+	}
+}
+
+#endif /* CONFIG_BGSCAN */
+
+
+static void wpa_supplicant_start_autoscan(struct wpa_supplicant *wpa_s)
+{
+	if (autoscan_init(wpa_s, 0))
+		wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize autoscan");
+}
+
+
+static void wpa_supplicant_stop_autoscan(struct wpa_supplicant *wpa_s)
+{
+	autoscan_deinit(wpa_s);
+}
+
+
+void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->wpa_state == WPA_DISCONNECTED ||
+	    wpa_s->wpa_state == WPA_SCANNING) {
+		autoscan_deinit(wpa_s);
+		wpa_supplicant_start_autoscan(wpa_s);
+	}
+}
+
+
+/**
+ * wpa_supplicant_set_state - Set current connection state
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @state: The new connection state
+ *
+ * This function is called whenever the connection state changes, e.g.,
+ * association is completed for WPA/WPA2 4-Way Handshake is started.
+ */
+void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
+			      enum wpa_states state)
+{
+	enum wpa_states old_state = wpa_s->wpa_state;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "State: %s -> %s",
+		wpa_supplicant_state_txt(wpa_s->wpa_state),
+		wpa_supplicant_state_txt(state));
+
+	if (state == WPA_INTERFACE_DISABLED) {
+		/* Assure normal scan when interface is restored */
+		wpa_s->normal_scans = 0;
+	}
+
+	if (state == WPA_COMPLETED) {
+		wpas_connect_work_done(wpa_s);
+		/* Reinitialize normal_scan counter */
+		wpa_s->normal_scans = 0;
+	}
+
+#ifdef CONFIG_P2P
+	/*
+	 * P2PS client has to reply to Probe Request frames received on the
+	 * group operating channel. Enable Probe Request frame reporting for
+	 * P2P connected client in case p2p_cli_probe configuration property is
+	 * set to 1.
+	 */
+	if (wpa_s->conf->p2p_cli_probe && wpa_s->current_ssid &&
+	    wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
+	    wpa_s->current_ssid->p2p_group) {
+		if (state == WPA_COMPLETED && !wpa_s->p2p_cli_probe) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"P2P: Enable CLI Probe Request RX reporting");
+			wpa_s->p2p_cli_probe =
+				wpa_drv_probe_req_report(wpa_s, 1) >= 0;
+		} else if (state != WPA_COMPLETED && wpa_s->p2p_cli_probe) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"P2P: Disable CLI Probe Request RX reporting");
+			wpa_s->p2p_cli_probe = 0;
+			wpa_drv_probe_req_report(wpa_s, 0);
+		}
+	}
+#endif /* CONFIG_P2P */
+
+	if (state != WPA_SCANNING)
+		wpa_supplicant_notify_scanning(wpa_s, 0);
+
+	if (state == WPA_COMPLETED && wpa_s->new_connection) {
+		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 [id=%d id_str=%s]",
+			MAC2STR(wpa_s->bssid),
+			ssid ? ssid->id : -1,
+			ssid && ssid->id_str ? ssid->id_str : "");
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+		wpas_clear_temp_disabled(wpa_s, ssid, 1);
+		wpa_blacklist_clear(wpa_s);
+		wpa_s->extra_blacklist_count = 0;
+		wpa_s->new_connection = 0;
+		wpa_drv_set_operstate(wpa_s, 1);
+#ifndef IEEE8021X_EAPOL
+		wpa_drv_set_supp_port(wpa_s, 1);
+#endif /* IEEE8021X_EAPOL */
+		wpa_s->after_wps = 0;
+		wpa_s->known_wps_freq = 0;
+		wpas_p2p_completed(wpa_s);
+
+		sme_sched_obss_scan(wpa_s, 1);
+	} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
+		   state == WPA_ASSOCIATED) {
+		wpa_s->new_connection = 1;
+		wpa_drv_set_operstate(wpa_s, 0);
+#ifndef IEEE8021X_EAPOL
+		wpa_drv_set_supp_port(wpa_s, 0);
+#endif /* IEEE8021X_EAPOL */
+		sme_sched_obss_scan(wpa_s, 0);
+	}
+	wpa_s->wpa_state = state;
+
+#ifdef CONFIG_BGSCAN
+	if (state == WPA_COMPLETED)
+		wpa_supplicant_start_bgscan(wpa_s);
+	else if (state < WPA_ASSOCIATED)
+		wpa_supplicant_stop_bgscan(wpa_s);
+#endif /* CONFIG_BGSCAN */
+
+	if (state == WPA_AUTHENTICATING)
+		wpa_supplicant_stop_autoscan(wpa_s);
+
+	if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
+		wpa_supplicant_start_autoscan(wpa_s);
+
+	if (old_state >= WPA_ASSOCIATED && wpa_s->wpa_state < WPA_ASSOCIATED)
+		wmm_ac_notify_disassoc(wpa_s);
+
+	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);
+	}
+}
+
+
+void wpa_supplicant_terminate_proc(struct wpa_global *global)
+{
+	int pending = 0;
+#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;
+#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)
+		return;
+	eloop_terminate();
+}
+
+
+static void wpa_supplicant_terminate(int sig, void *signal_ctx)
+{
+	struct wpa_global *global = signal_ctx;
+	wpa_supplicant_terminate_proc(global);
+}
+
+
+void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
+{
+	enum wpa_states old_state = wpa_s->wpa_state;
+
+	wpa_s->pairwise_cipher = 0;
+	wpa_s->group_cipher = 0;
+	wpa_s->mgmt_group_cipher = 0;
+	wpa_s->key_mgmt = 0;
+	if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED)
+		wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+
+	if (wpa_s->wpa_state != old_state)
+		wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
+}
+
+
+/**
+ * wpa_supplicant_reload_configuration - Reload configuration data
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success or -1 if configuration parsing failed
+ *
+ * This function can be used to request that the configuration data is reloaded
+ * (e.g., after configuration file change). This function is reloading
+ * configuration only for one interface, so this may need to be called multiple
+ * times if %wpa_supplicant is controlling multiple interfaces and all
+ * interfaces need reconfiguration.
+ */
+int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_config *conf;
+	int reconf_ctrl;
+	int old_ap_scan;
+
+	if (wpa_s->confname == NULL)
+		return -1;
+	conf = wpa_config_read(wpa_s->confname, NULL);
+	if (conf == NULL) {
+		wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration "
+			"file '%s' - exiting", wpa_s->confname);
+		return -1;
+	}
+	wpa_config_read(wpa_s->confanother, conf);
+
+	conf->changed_parameters = (unsigned int) -1;
+
+	reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface
+		|| (conf->ctrl_interface && wpa_s->conf->ctrl_interface &&
+		    os_strcmp(conf->ctrl_interface,
+			      wpa_s->conf->ctrl_interface) != 0);
+
+	if (reconf_ctrl && wpa_s->ctrl_iface) {
+		wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
+		wpa_s->ctrl_iface = NULL;
+	}
+
+	eapol_sm_invalidate_cached_session(wpa_s->eapol);
+	if (wpa_s->current_ssid) {
+		if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+			wpa_s->own_disconnect_req = 1;
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+	}
+
+	/*
+	 * TODO: should notify EAPOL SM about changes in opensc_engine_path,
+	 * pkcs11_engine_path, pkcs11_module_path, openssl_ciphers.
+	 */
+	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
+		/*
+		 * Clear forced success to clear EAP state for next
+		 * authentication.
+		 */
+		eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+	}
+	eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+	wpa_sm_set_config(wpa_s->wpa, NULL);
+	wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
+	wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth);
+	rsn_preauth_deinit(wpa_s->wpa);
+
+	old_ap_scan = wpa_s->conf->ap_scan;
+	wpa_config_free(wpa_s->conf);
+	wpa_s->conf = conf;
+	if (old_ap_scan != wpa_s->conf->ap_scan)
+		wpas_notify_ap_scan_changed(wpa_s);
+
+	if (reconf_ctrl)
+		wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
+
+	wpa_supplicant_update_config(wpa_s);
+
+	wpa_supplicant_clear_status(wpa_s);
+	if (wpa_supplicant_enabled_networks(wpa_s)) {
+		wpa_s->reassociate = 1;
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	}
+	wpa_dbg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
+	return 0;
+}
+
+
+static void wpa_supplicant_reconfig(int sig, void *signal_ctx)
+{
+	struct wpa_global *global = signal_ctx;
+	struct wpa_supplicant *wpa_s;
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Signal %d received - reconfiguring",
+			sig);
+		if (wpa_supplicant_reload_configuration(wpa_s) < 0) {
+			wpa_supplicant_terminate_proc(global);
+		}
+	}
+}
+
+
+static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
+					 struct wpa_ssid *ssid,
+					 struct wpa_ie_data *ie)
+{
+	int ret = wpa_sm_parse_own_wpa_ie(wpa_s->wpa, ie);
+	if (ret) {
+		if (ret == -2) {
+			wpa_msg(wpa_s, MSG_INFO, "WPA: Failed to parse WPA IE "
+				"from association info");
+		}
+		return -1;
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set "
+		"cipher suites");
+	if (!(ie->group_cipher & ssid->group_cipher)) {
+		wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group "
+			"cipher 0x%x (mask 0x%x) - reject",
+			ie->group_cipher, ssid->group_cipher);
+		return -1;
+	}
+	if (!(ie->pairwise_cipher & ssid->pairwise_cipher)) {
+		wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled pairwise "
+			"cipher 0x%x (mask 0x%x) - reject",
+			ie->pairwise_cipher, ssid->pairwise_cipher);
+		return -1;
+	}
+	if (!(ie->key_mgmt & ssid->key_mgmt)) {
+		wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled key "
+			"management 0x%x (mask 0x%x) - reject",
+			ie->key_mgmt, ssid->key_mgmt);
+		return -1;
+	}
+
+#ifdef CONFIG_IEEE80211W
+	if (!(ie->capabilities & WPA_CAPABILITY_MFPC) &&
+	    wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
+		wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP "
+			"that does not support management frame protection - "
+			"reject");
+		return -1;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_suites - Set authentication and encryption parameters
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: Scan results for the selected BSS, or %NULL if not available
+ * @ssid: Configuration data for the selected network
+ * @wpa_ie: Buffer for the WPA/RSN IE
+ * @wpa_ie_len: Maximum wpa_ie buffer size on input. This is changed to be the
+ * used buffer length in case the functions returns success.
+ * Returns: 0 on success or -1 on failure
+ *
+ * This function is used to configure authentication and encryption parameters
+ * based on the network configuration and scan result for the selected BSS (if
+ * available).
+ */
+int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
+			      struct wpa_bss *bss, struct wpa_ssid *ssid,
+			      u8 *wpa_ie, size_t *wpa_ie_len)
+{
+	struct wpa_ie_data ie;
+	int sel, proto;
+	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 = bss_osen = NULL;
+
+	if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) &&
+	    wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 &&
+	    (ie.group_cipher & ssid->group_cipher) &&
+	    (ie.pairwise_cipher & ssid->pairwise_cipher) &&
+	    (ie.key_mgmt & ssid->key_mgmt)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
+		proto = WPA_PROTO_RSN;
+	} else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) &&
+		   wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie) == 0 &&
+		   (ie.group_cipher & ssid->group_cipher) &&
+		   (ie.pairwise_cipher & ssid->pairwise_cipher) &&
+		   (ie.key_mgmt & ssid->key_mgmt)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "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");
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"WPA: ssid proto=0x%x pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+			ssid->proto, ssid->pairwise_cipher, ssid->group_cipher,
+			ssid->key_mgmt);
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: BSS " MACSTR " ssid='%s'%s%s%s",
+			MAC2STR(bss->bssid),
+			wpa_ssid_txt(bss->ssid, bss->ssid_len),
+			bss_wpa ? " WPA" : "",
+			bss_rsn ? " RSN" : "",
+			bss_osen ? " OSEN" : "");
+		if (bss_rsn) {
+			wpa_hexdump(MSG_DEBUG, "RSN", bss_rsn, 2 + bss_rsn[1]);
+			if (wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie)) {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"Could not parse RSN element");
+			} else {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"RSN: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+					ie.pairwise_cipher, ie.group_cipher,
+					ie.key_mgmt);
+			}
+		}
+		if (bss_wpa) {
+			wpa_hexdump(MSG_DEBUG, "WPA", bss_wpa, 2 + bss_wpa[1]);
+			if (wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie)) {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"Could not parse WPA element");
+			} else {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"WPA: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+					ie.pairwise_cipher, ie.group_cipher,
+					ie.key_mgmt);
+			}
+		}
+		return -1;
+	} else {
+		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;
+		if (wpa_supplicant_suites_from_ai(wpa_s, ssid, &ie) < 0) {
+			os_memset(&ie, 0, sizeof(ie));
+			ie.group_cipher = ssid->group_cipher;
+			ie.pairwise_cipher = ssid->pairwise_cipher;
+			ie.key_mgmt = ssid->key_mgmt;
+#ifdef CONFIG_IEEE80211W
+			ie.mgmt_group_cipher =
+				ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ?
+				WPA_CIPHER_AES_128_CMAC : 0;
+#endif /* CONFIG_IEEE80211W */
+			wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites "
+				"based on configuration");
+		} else
+			proto = ie.proto;
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected cipher suites: group %d "
+		"pairwise %d key_mgmt %d proto %d",
+		ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto);
+#ifdef CONFIG_IEEE80211W
+	if (ssid->ieee80211w) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected mgmt group cipher %d",
+			ie.mgmt_group_cipher);
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	wpa_s->wpa_proto = proto;
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto);
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED,
+			 !!(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,
+					 bss_wpa ? 2 + bss_wpa[1] : 0) ||
+		    wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
+					 bss_rsn ? 2 + bss_rsn[1] : 0))
+			return -1;
+	}
+
+	sel = ie.group_cipher & ssid->group_cipher;
+	wpa_s->group_cipher = wpa_pick_group_cipher(sel);
+	if (wpa_s->group_cipher < 0) {
+		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group "
+			"cipher");
+		return -1;
+	}
+	wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK %s",
+		wpa_cipher_txt(wpa_s->group_cipher));
+
+	sel = ie.pairwise_cipher & ssid->pairwise_cipher;
+	wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(sel, 1);
+	if (wpa_s->pairwise_cipher < 0) {
+		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise "
+			"cipher");
+		return -1;
+	}
+	wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s",
+		wpa_cipher_txt(wpa_s->pairwise_cipher));
+
+	sel = ie.key_mgmt & ssid->key_mgmt;
+#ifdef CONFIG_SAE
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE))
+		sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE);
+#endif /* CONFIG_SAE */
+	if (0) {
+#ifdef CONFIG_SUITEB192
+	} else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"WPA: using KEY_MGMT 802.1X with Suite B (192-bit)");
+#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_SUITEB
+	} else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"WPA: using KEY_MGMT 802.1X with Suite B");
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_IEEE80211R
+	} else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X");
+	} else if (sel & WPA_KEY_MGMT_FT_PSK) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK");
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SAE
+	} else if (sel & WPA_KEY_MGMT_SAE) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_SAE;
+		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT SAE");
+	} else if (sel & WPA_KEY_MGMT_FT_SAE) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE;
+		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT FT/SAE");
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_IEEE80211W
+	} else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"WPA: using KEY_MGMT 802.1X with SHA256");
+	} else if (sel & WPA_KEY_MGMT_PSK_SHA256) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"WPA: using KEY_MGMT PSK with SHA256");
+#endif /* CONFIG_IEEE80211W */
+	} else if (sel & WPA_KEY_MGMT_IEEE8021X) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X");
+	} else if (sel & WPA_KEY_MGMT_PSK) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK");
+	} 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");
+		return -1;
+	}
+
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt);
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE,
+			 wpa_s->pairwise_cipher);
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
+
+#ifdef CONFIG_IEEE80211W
+	sel = ie.mgmt_group_cipher;
+	if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION ||
+	    !(ie.capabilities & WPA_CAPABILITY_MFPC))
+		sel = 0;
+	if (sel & WPA_CIPHER_AES_128_CMAC) {
+		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");
+	}
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
+			 wpa_s->mgmt_group_cipher);
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP,
+			 wpas_get_ssid_pmf(wpa_s, ssid));
+#endif /* CONFIG_IEEE80211W */
+
+	if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
+		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE");
+		return -1;
+	}
+
+	if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
+		int psk_set = 0;
+
+		if (ssid->psk_set) {
+			wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL);
+			psk_set = 1;
+		}
+#ifndef CONFIG_NO_PBKDF2
+		if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
+		    ssid->passphrase) {
+			u8 psk[PMK_LEN];
+		        pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len,
+				    4096, psk, PMK_LEN);
+		        wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
+					psk, PMK_LEN);
+			wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+			psk_set = 1;
+			os_memset(psk, 0, sizeof(psk));
+		}
+#endif /* CONFIG_NO_PBKDF2 */
+#ifdef CONFIG_EXT_PASSWORD
+		if (ssid->ext_psk) {
+			struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
+							     ssid->ext_psk);
+			char pw_str[64 + 1];
+			u8 psk[PMK_LEN];
+
+			if (pw == NULL) {
+				wpa_msg(wpa_s, MSG_INFO, "EXT PW: No PSK "
+					"found from external storage");
+				return -1;
+			}
+
+			if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) {
+				wpa_msg(wpa_s, MSG_INFO, "EXT PW: Unexpected "
+					"PSK length %d in external storage",
+					(int) wpabuf_len(pw));
+				ext_password_free(pw);
+				return -1;
+			}
+
+			os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw));
+			pw_str[wpabuf_len(pw)] = '\0';
+
+#ifndef CONFIG_NO_PBKDF2
+			if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss)
+			{
+				pbkdf2_sha1(pw_str, bss->ssid, bss->ssid_len,
+					    4096, psk, PMK_LEN);
+				os_memset(pw_str, 0, sizeof(pw_str));
+				wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
+						"external passphrase)",
+						psk, PMK_LEN);
+				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+				psk_set = 1;
+				os_memset(psk, 0, sizeof(psk));
+			} else
+#endif /* CONFIG_NO_PBKDF2 */
+			if (wpabuf_len(pw) == 2 * PMK_LEN) {
+				if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) {
+					wpa_msg(wpa_s, MSG_INFO, "EXT PW: "
+						"Invalid PSK hex string");
+					os_memset(pw_str, 0, sizeof(pw_str));
+					ext_password_free(pw);
+					return -1;
+				}
+				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+				psk_set = 1;
+				os_memset(psk, 0, sizeof(psk));
+			} else {
+				wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable "
+					"PSK available");
+				os_memset(pw_str, 0, sizeof(pw_str));
+				ext_password_free(pw);
+				return -1;
+			}
+
+			os_memset(pw_str, 0, sizeof(pw_str));
+			ext_password_free(pw);
+		}
+#endif /* CONFIG_EXT_PASSWORD */
+
+		if (!psk_set) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"No PSK available for association");
+			return -1;
+		}
+	} else
+		wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
+
+	return 0;
+}
+
+
+static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
+{
+	*pos = 0x00;
+
+	switch (idx) {
+	case 0: /* Bits 0-7 */
+		break;
+	case 1: /* Bits 8-15 */
+		break;
+	case 2: /* Bits 16-23 */
+#ifdef CONFIG_WNM
+		*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+		*pos |= 0x08; /* Bit 19 - BSS Transition */
+#endif /* CONFIG_WNM */
+		break;
+	case 3: /* Bits 24-31 */
+#ifdef CONFIG_WNM
+		*pos |= 0x02; /* Bit 25 - SSID List */
+#endif /* CONFIG_WNM */
+#ifdef CONFIG_INTERWORKING
+		if (wpa_s->conf->interworking)
+			*pos |= 0x80; /* Bit 31 - Interworking */
+#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;
+	}
+}
+
+
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
+{
+	u8 *pos = buf;
+	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;
+	for (i = 0; i < len; i++, pos++) {
+		wpas_ext_capab_byte(wpa_s, pos, i);
+
+		if (i < wpa_s->extended_capa_len) {
+			*pos &= ~wpa_s->extended_capa_mask[i];
+			*pos |= wpa_s->extended_capa[i];
+		}
+	}
+
+	while (len > 0 && buf[1 + len] == 0) {
+		len--;
+		buf[1] = len;
+	}
+	if (len == 0)
+		return 0;
+
+	return 2 + len;
+}
+
+
+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);
+}
+
+
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style)
+{
+	struct os_reltime now;
+	u8 addr[ETH_ALEN];
+
+	os_get_reltime(&now);
+	if (wpa_s->last_mac_addr_style == style &&
+	    wpa_s->last_mac_addr_change.sec != 0 &&
+	    !os_reltime_expired(&now, &wpa_s->last_mac_addr_change,
+				wpa_s->conf->rand_addr_lifetime)) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Previously selected random MAC address has not yet expired");
+		return 0;
+	}
+
+	switch (style) {
+	case 1:
+		if (random_mac_addr(addr) < 0)
+			return -1;
+		break;
+	case 2:
+		os_memcpy(addr, wpa_s->perm_addr, ETH_ALEN);
+		if (random_mac_addr_keep_oui(addr) < 0)
+			return -1;
+		break;
+	default:
+		return -1;
+	}
+
+	if (wpa_drv_set_mac_addr(wpa_s, addr) < 0) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"Failed to set random MAC address");
+		return -1;
+	}
+
+	os_get_reltime(&wpa_s->last_mac_addr_change);
+	wpa_s->mac_addr_changed = 1;
+	wpa_s->last_mac_addr_style = style;
+
+	if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"Could not update MAC address information");
+		return -1;
+	}
+
+	wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
+		MAC2STR(addr));
+
+	return 0;
+}
+
+
+int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->wpa_state >= WPA_AUTHENTICATING ||
+	    !wpa_s->conf->preassoc_mac_addr)
+		return 0;
+
+	return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr);
+}
+
+
+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
+ * @bss: Scan results for the selected BSS, or %NULL if not available
+ * @ssid: Configuration data for the selected network
+ *
+ * This function is used to request %wpa_supplicant to associate with a BSS.
+ */
+void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
+			      struct wpa_bss *bss, struct wpa_ssid *ssid)
+{
+	struct wpa_connect_work *cwork;
+	int rand_style;
+
+	if (ssid->mac_addr == -1)
+		rand_style = wpa_s->conf->mac_addr;
+	else
+		rand_style = ssid->mac_addr;
+
+	wmm_ac_clear_saved_tspecs(wpa_s);
+	wpa_s->reassoc_same_bss = 0;
+
+	if (wpa_s->last_ssid == ssid) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
+		if (wpa_s->current_bss && wpa_s->current_bss == bss) {
+			wmm_ac_save_tspecs(wpa_s);
+			wpa_s->reassoc_same_bss = 1;
+		}
+	} else if (rand_style > 0) {
+		if (wpas_update_random_addr(wpa_s, rand_style) < 0)
+			return;
+		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+	} else if (wpa_s->mac_addr_changed) {
+		if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"Could not restore permanent MAC address");
+			return;
+		}
+		wpa_s->mac_addr_changed = 0;
+		if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"Could not update MAC address information");
+			return;
+		}
+		wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address");
+	}
+	wpa_s->last_ssid = ssid;
+
+#ifdef CONFIG_IBSS_RSN
+	ibss_rsn_deinit(wpa_s->ibss_rsn);
+	wpa_s->ibss_rsn = NULL;
+#endif /* CONFIG_IBSS_RSN */
+
+	if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||
+	    ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
+#ifdef CONFIG_AP
+		if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP)) {
+			wpa_msg(wpa_s, MSG_INFO, "Driver does not support AP "
+				"mode");
+			return;
+		}
+		if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) {
+			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+			if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
+				wpas_p2p_ap_setup_failed(wpa_s);
+			return;
+		}
+		wpa_s->current_bss = bss;
+#else /* CONFIG_AP */
+		wpa_msg(wpa_s, MSG_ERROR, "AP mode support not included in "
+			"the build");
+#endif /* CONFIG_AP */
+		return;
+	}
+
+	if (ssid->mode == WPAS_MODE_MESH) {
+#ifdef CONFIG_MESH
+		if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MESH)) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"Driver does not support mesh mode");
+			return;
+		}
+		if (bss)
+			ssid->frequency = bss->freq;
+		if (wpa_supplicant_join_mesh(wpa_s, ssid) < 0) {
+			wpa_msg(wpa_s, MSG_ERROR, "Could not join mesh");
+			return;
+		}
+		wpa_s->current_bss = bss;
+		wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_STARTED
+			     "ssid=\"%s\" id=%d",
+			     wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+			     ssid->id);
+#else /* CONFIG_MESH */
+		wpa_msg(wpa_s, MSG_ERROR,
+			"mesh mode support not included in the build");
+#endif /* CONFIG_MESH */
+		return;
+	}
+
+#ifdef CONFIG_TDLS
+	if (bss)
+		wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1),
+				bss->ie_len);
+#endif /* CONFIG_TDLS */
+
+	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+	    ssid->mode == IEEE80211_MODE_INFRA) {
+		sme_authenticate(wpa_s, bss, ssid);
+		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 int bss_is_ibss(struct wpa_bss *bss)
+{
+	return (bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) ==
+		IEEE80211_CAP_IBSS;
+}
+
+
+void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+			  const struct wpa_ssid *ssid,
+			  struct hostapd_freq_params *freq)
+{
+	enum hostapd_hw_mode hw_mode;
+	struct hostapd_hw_modes *mode = NULL;
+	int ht40plus[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+			   184, 192 };
+	int vht80[] = { 36, 52, 100, 116, 132, 149 };
+	struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
+	u8 channel;
+	int i, chan_idx, ht40 = -1, res, obss_scan = 1;
+	unsigned int j;
+	struct hostapd_freq_params vht_freq;
+
+	freq->freq = ssid->frequency;
+
+	for (j = 0; j < wpa_s->last_scan_res_used; j++) {
+		struct wpa_bss *bss = wpa_s->last_scan_res[j];
+
+		if (ssid->mode != WPAS_MODE_IBSS)
+			break;
+
+		/* Don't adjust control freq in case of fixed_freq */
+		if (ssid->fixed_freq)
+			break;
+
+		if (!bss_is_ibss(bss))
+			continue;
+
+		if (ssid->ssid_len == bss->ssid_len &&
+		    os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "IBSS already found in scan results, adjust control freq: %d",
+				   bss->freq);
+			freq->freq = bss->freq;
+			obss_scan = 0;
+			break;
+		}
+	}
+
+	/* For IBSS check HT_IBSS flag */
+	if (ssid->mode == WPAS_MODE_IBSS &&
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_HT_IBSS))
+		return;
+
+	if (wpa_s->group_cipher == WPA_CIPHER_WEP40 ||
+	    wpa_s->group_cipher == WPA_CIPHER_WEP104 ||
+	    wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
+		wpa_printf(MSG_DEBUG,
+			   "IBSS: WEP/TKIP detected, do not try to enable HT");
+		return;
+	}
+
+	hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
+	for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
+		if (wpa_s->hw.modes[i].mode == hw_mode) {
+			mode = &wpa_s->hw.modes[i];
+			break;
+		}
+	}
+
+	if (!mode)
+		return;
+
+	freq->ht_enabled = ht_supported(mode);
+	if (!freq->ht_enabled)
+		return;
+
+	/* Setup higher BW only for 5 GHz */
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return;
+
+	for (chan_idx = 0; chan_idx < mode->num_channels; chan_idx++) {
+		pri_chan = &mode->channels[chan_idx];
+		if (pri_chan->chan == channel)
+			break;
+		pri_chan = NULL;
+	}
+	if (!pri_chan)
+		return;
+
+	/* Check primary channel flags */
+	if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+		return;
+
+	/* Check/setup HT40+/HT40- */
+	for (j = 0; j < ARRAY_SIZE(ht40plus); j++) {
+		if (ht40plus[j] == channel) {
+			ht40 = 1;
+			break;
+		}
+	}
+
+	/* Find secondary channel */
+	for (i = 0; i < mode->num_channels; i++) {
+		sec_chan = &mode->channels[i];
+		if (sec_chan->chan == channel + ht40 * 4)
+			break;
+		sec_chan = NULL;
+	}
+	if (!sec_chan)
+		return;
+
+	/* Check secondary channel flags */
+	if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+		return;
+
+	freq->channel = pri_chan->chan;
+
+	switch (ht40) {
+	case -1:
+		if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
+			return;
+		freq->sec_channel_offset = -1;
+		break;
+	case 1:
+		if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS))
+			return;
+		freq->sec_channel_offset = 1;
+		break;
+	default:
+		break;
+	}
+
+	if (freq->sec_channel_offset && obss_scan) {
+		struct wpa_scan_results *scan_res;
+
+		scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+		if (scan_res == NULL) {
+			/* Back to HT20 */
+			freq->sec_channel_offset = 0;
+			return;
+		}
+
+		res = check_40mhz_5g(mode, scan_res, pri_chan->chan,
+				     sec_chan->chan);
+		switch (res) {
+		case 0:
+			/* Back to HT20 */
+			freq->sec_channel_offset = 0;
+			break;
+		case 1:
+			/* Configuration allowed */
+			break;
+		case 2:
+			/* Switch pri/sec channels */
+			freq->freq = hw_get_freq(mode, sec_chan->chan);
+			freq->sec_channel_offset = -freq->sec_channel_offset;
+			freq->channel = sec_chan->chan;
+			break;
+		default:
+			freq->sec_channel_offset = 0;
+			break;
+		}
+
+		wpa_scan_results_free(scan_res);
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "IBSS/mesh: setup freq channel %d, sec_channel_offset %d",
+		   freq->channel, freq->sec_channel_offset);
+
+	/* Not sure if mesh is ready for VHT */
+	if (ssid->mode != WPAS_MODE_IBSS)
+		return;
+
+	/* For IBSS check VHT_IBSS flag */
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
+		return;
+
+	vht_freq = *freq;
+
+	vht_freq.vht_enabled = vht_supported(mode);
+	if (!vht_freq.vht_enabled)
+		return;
+
+	/* setup center_freq1, bandwidth */
+	for (j = 0; j < ARRAY_SIZE(vht80); j++) {
+		if (freq->channel >= vht80[j] &&
+		    freq->channel < vht80[j] + 16)
+			break;
+	}
+
+	if (j == ARRAY_SIZE(vht80))
+		return;
+
+	for (i = vht80[j]; i < vht80[j] + 16; i += 4) {
+		struct hostapd_channel_data *chan;
+
+		chan = hw_get_channel_chan(mode, i, NULL);
+		if (!chan)
+			return;
+
+		/* Back to HT configuration if channel not usable */
+		if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+			return;
+	}
+
+	if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
+				    freq->channel, freq->ht_enabled,
+				    vht_freq.vht_enabled,
+				    freq->sec_channel_offset,
+				    VHT_CHANWIDTH_80MHZ,
+				    vht80[j] + 6, 0, 0) != 0)
+		return;
+
+	*freq = vht_freq;
+
+	wpa_printf(MSG_DEBUG, "IBSS: VHT setup freq cf1 %d, cf2 %d, bw %d",
+		   freq->center_freq1, freq->center_freq2, freq->bandwidth);
+}
+
+
+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 (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) ||
+	    wpas_network_disabled(wpa_s, 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) || wpas_wps_searching(wpa_s))) {
+#ifdef CONFIG_IEEE80211R
+		const u8 *ie, *md = NULL;
+#endif /* CONFIG_IEEE80211R */
+		wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
+			" (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid),
+			wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq);
+		bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
+		os_memset(wpa_s->bssid, 0, ETH_ALEN);
+		os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
+		if (bssid_changed)
+			wpas_notify_bssid_changed(wpa_s);
+#ifdef CONFIG_IEEE80211R
+		ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
+		if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
+			md = ie + 2;
+		wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
+		if (md) {
+			/* Prepare for the next transition */
+			wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
+		}
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_WPS
+	} else if ((ssid->ssid == NULL || ssid->ssid_len == 0) &&
+		   wpa_s->conf->ap_scan == 2 &&
+		   (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+		/* Use ap_scan==1 style network selection to find the network
+		 */
+		wpas_connect_work_done(wpa_s);
+		wpa_s->scan_req = MANUAL_SCAN_REQ;
+		wpa_s->reassociate = 1;
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+		return;
+#endif /* CONFIG_WPS */
+	} else {
+		wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
+			wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+		os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+	}
+	if (!wpa_s->pno)
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+
+	wpa_supplicant_cancel_scan(wpa_s);
+
+	/* Starting new association, so clear the possibly used WPA IE from the
+	 * previous association. */
+	wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+
+#ifdef IEEE8021X_EAPOL
+	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+		if (ssid->leap) {
+			if (ssid->non_leap == 0)
+				algs = WPA_AUTH_ALG_LEAP;
+			else
+				algs |= WPA_AUTH_ALG_LEAP;
+		}
+	}
+#endif /* IEEE8021X_EAPOL */
+	wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
+	if (ssid->auth_alg) {
+		algs = ssid->auth_alg;
+		wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
+			"0x%x", algs);
+	}
+
+	if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
+		    wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
+	    wpa_key_mgmt_wpa(ssid->key_mgmt)) {
+		int try_opportunistic;
+		try_opportunistic = (ssid->proactive_key_caching < 0 ?
+				     wpa_s->conf->okc :
+				     ssid->proactive_key_caching) &&
+			(ssid->proto & WPA_PROTO_RSN);
+		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+					    ssid, try_opportunistic) == 0)
+			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
+		wpa_ie_len = sizeof(wpa_ie);
+		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
+					      wpa_ie, &wpa_ie_len)) {
+			wpa_msg(wpa_s, MSG_WARNING, "WPA: 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) && bss &&
+		   wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
+		/*
+		 * Both WPA and non-WPA IEEE 802.1X enabled in configuration -
+		 * use non-WPA since the scan results did not indicate that the
+		 * AP is using WPA or WPA2.
+		 */
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+		wpa_ie_len = 0;
+		wpa_s->wpa_proto = 0;
+	} else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
+		wpa_ie_len = sizeof(wpa_ie);
+		if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
+					      wpa_ie, &wpa_ie_len)) {
+			wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
+				"key management and encryption suites (no "
+				"scan results)");
+			wpas_connect_work_done(wpa_s);
+			return;
+		}
+#ifdef CONFIG_WPS
+	} else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+		struct wpabuf *wps_ie;
+		wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
+		if (wps_ie && wpabuf_len(wps_ie) <= sizeof(wpa_ie)) {
+			wpa_ie_len = wpabuf_len(wps_ie);
+			os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len);
+		} else
+			wpa_ie_len = 0;
+		wpabuf_free(wps_ie);
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+		if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY))
+			params.wps = WPS_MODE_PRIVACY;
+		else
+			params.wps = WPS_MODE_OPEN;
+		wpa_s->wpa_proto = 0;
+#endif /* CONFIG_WPS */
+	} else {
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+		wpa_ie_len = 0;
+		wpa_s->wpa_proto = 0;
+	}
+
+#ifdef CONFIG_P2P
+	if (wpa_s->global->p2p) {
+		u8 *pos;
+		size_t len;
+		int res;
+		pos = wpa_ie + wpa_ie_len;
+		len = sizeof(wpa_ie) - wpa_ie_len;
+		res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
+					    ssid->p2p_group);
+		if (res >= 0)
+			wpa_ie_len += res;
+	}
+
+	wpa_s->cross_connect_disallowed = 0;
+	if (bss) {
+		struct wpabuf *p2p;
+		p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+		if (p2p) {
+			wpa_s->cross_connect_disallowed =
+				p2p_get_cross_connect_disallowed(p2p);
+			wpabuf_free(p2p);
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: WLAN AP %s cross "
+				"connection",
+				wpa_s->cross_connect_disallowed ?
+				"disallows" : "allows");
+		}
+	}
+
+	os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_HS20
+	if (is_hs20_network(wpa_s, ssid, bss)) {
+		struct wpabuf *hs20;
+		hs20 = wpabuf_alloc(20);
+		if (hs20) {
+			int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+			size_t len;
+
+			wpas_hs20_add_indication(hs20, pps_mo_id);
+			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 */
+
+	/*
+	 * 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);
+		}
+	}
+
+	if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
+		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
+		size_t len;
+
+		len = sizeof(wpa_ie) - wpa_ie_len;
+		if (wpabuf_len(buf) <= len) {
+			os_memcpy(wpa_ie + wpa_ie_len,
+				  wpabuf_head(buf), wpabuf_len(buf));
+			wpa_ie_len += wpabuf_len(buf);
+		}
+	}
+
+#ifdef CONFIG_FST
+	if (wpa_s->fst_ies) {
+		int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
+
+		if (wpa_ie_len + fst_ies_len <= sizeof(wpa_ie)) {
+			os_memcpy(wpa_ie + wpa_ie_len,
+				  wpabuf_head(wpa_s->fst_ies), fst_ies_len);
+			wpa_ie_len += fst_ies_len;
+		}
+	}
+#endif /* CONFIG_FST */
+
+	wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
+	use_crypt = 1;
+	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)
+			use_crypt = 0;
+		if (wpa_set_wep_keys(wpa_s, ssid)) {
+			use_crypt = 1;
+			wep_keys_set = 1;
+		}
+	}
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS)
+		use_crypt = 0;
+
+#ifdef IEEE8021X_EAPOL
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+		if ((ssid->eapol_flags &
+		     (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
+		      EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) == 0 &&
+		    !wep_keys_set) {
+			use_crypt = 0;
+		} else {
+			/* Assume that dynamic WEP-104 keys will be used and
+			 * set cipher suites in order for drivers to expect
+			 * encryption. */
+			cipher_pairwise = cipher_group = WPA_CIPHER_WEP104;
+		}
+	}
+#endif /* IEEE8021X_EAPOL */
+
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+		/* Set the key before (and later after) association */
+		wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
+	}
+
+	wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
+	if (bss) {
+		params.ssid = bss->ssid;
+		params.ssid_len = bss->ssid_len;
+		if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
+			wpa_printf(MSG_DEBUG, "Limit connection to BSSID "
+				   MACSTR " freq=%u MHz based on scan results "
+				   "(bssid_set=%d)",
+				   MAC2STR(bss->bssid), bss->freq,
+				   ssid->bssid_set);
+			params.bssid = bss->bssid;
+			params.freq.freq = bss->freq;
+		}
+		params.bssid_hint = bss->bssid;
+		params.freq_hint = bss->freq;
+	} else {
+		params.ssid = ssid->ssid;
+		params.ssid_len = ssid->ssid_len;
+	}
+
+	if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set &&
+	    wpa_s->conf->ap_scan == 2) {
+		params.bssid = ssid->bssid;
+		params.fixed_bssid = 1;
+	}
+
+	/* Initial frequency for IBSS/mesh */
+	if ((ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) &&
+	    ssid->frequency > 0 && params.freq.freq == 0)
+		ibss_mesh_setup_freq(wpa_s, ssid, &params.freq);
+
+	if (ssid->mode == WPAS_MODE_IBSS) {
+		params.fixed_freq = ssid->fixed_freq;
+		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 = wpa_s->key_mgmt;
+	params.wpa_proto = wpa_s->wpa_proto;
+	params.auth_alg = algs;
+	params.mode = ssid->mode;
+	params.bg_scan_period = ssid->bg_scan_period;
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		if (ssid->wep_key_len[i])
+			params.wep_key[i] = ssid->wep_key[i];
+		params.wep_key_len[i] = ssid->wep_key_len[i];
+	}
+	params.wep_tx_keyidx = ssid->wep_tx_keyidx;
+
+	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+	    (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;
+	}
+
+	if (wpa_s->conf->key_mgmt_offload) {
+		if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+		    params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
+		    params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
+		    params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+			params.req_key_mgmt_offload =
+				ssid->proactive_key_caching < 0 ?
+				wpa_s->conf->okc : ssid->proactive_key_caching;
+		else
+			params.req_key_mgmt_offload = 1;
+
+		if ((params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+		     params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+		     params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) &&
+		    ssid->psk_set)
+			params.psk = ssid->psk;
+	}
+
+	params.drop_unencrypted = use_crypt;
+
+#ifdef CONFIG_IEEE80211W
+	params.mgmt_frame_protection = wpas_get_ssid_pmf(wpa_s, ssid);
+	if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) {
+		const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+		struct wpa_ie_data ie;
+		if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 &&
+		    ie.capabilities &
+		    (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected AP supports "
+				"MFP: require MFP");
+			params.mgmt_frame_protection =
+				MGMT_FRAME_PROTECTION_REQUIRED;
+		}
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	params.p2p = ssid->p2p_group;
+
+	if (wpa_s->parent->set_sta_uapsd)
+		params.uapsd = wpa_s->parent->sta_uapsd;
+	else
+		params.uapsd = -1;
+
+#ifdef CONFIG_HT_OVERRIDES
+	os_memset(&htcaps, 0, sizeof(htcaps));
+	os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
+	params.htcaps = (u8 *) &htcaps;
+	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, ssid, &params);
+#endif /* CONFIG_VHT_OVERRIDES */
+
+#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.freq) {
+			wpa_printf(MSG_DEBUG,
+				   "Assoc conflicting freq found (%d != %d)",
+				   freq, params.freq.freq);
+			if (wpas_p2p_handle_frequency_conflicts(
+				    wpa_s, params.freq.freq, ssid) < 0) {
+				wpas_connect_work_done(wpa_s);
+				return;
+			}
+		}
+	}
+#endif /* CONFIG_P2P */
+
+	ret = wpa_drv_associate(wpa_s, &params);
+	if (ret < 0) {
+		wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
+			"failed");
+		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) {
+			/*
+			 * The driver is known to mean what is saying, so we
+			 * can stop right here; the association will not
+			 * succeed.
+			 */
+			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+			os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+			return;
+		}
+		/* try to continue anyway; new association will be tried again
+		 * after timeout */
+		assoc_failed = 1;
+	}
+
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+		/* Set the key after the association just in case association
+		 * cleared the previously configured key. */
+		wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
+		/* No need to timeout authentication since there is no key
+		 * management. */
+		wpa_supplicant_cancel_auth_timeout(wpa_s);
+		wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+#ifdef CONFIG_IBSS_RSN
+	} else if (ssid->mode == WPAS_MODE_IBSS &&
+		   wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
+		   wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) {
+		/*
+		 * RSN IBSS authentication is per-STA and we can disable the
+		 * per-BSSID authentication.
+		 */
+		wpa_supplicant_cancel_auth_timeout(wpa_s);
+#endif /* CONFIG_IBSS_RSN */
+	} else {
+		/* Timeout for IEEE 802.11 authentication and association */
+		int timeout = 60;
+
+		if (assoc_failed) {
+			/* give IBSS a bit more time */
+			timeout = ssid->mode == WPAS_MODE_IBSS ? 10 : 5;
+		} else if (wpa_s->conf->ap_scan == 1) {
+			/* give IBSS a bit more time */
+			timeout = ssid->mode == WPAS_MODE_IBSS ? 20 : 10;
+		}
+		wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
+	}
+
+	if (wep_keys_set &&
+	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC)) {
+		/* Set static WEP keys again */
+		wpa_set_wep_keys(wpa_s, ssid);
+	}
+
+	if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) {
+		/*
+		 * Do not allow EAP session resumption between different
+		 * network configurations.
+		 */
+		eapol_sm_invalidate_cached_session(wpa_s->eapol);
+	}
+	old_ssid = wpa_s->current_ssid;
+	wpa_s->current_ssid = ssid;
+	if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set)
+		wpa_s->current_bss = bss;
+	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)
+		wpas_notify_network_changed(wpa_s);
+}
+
+
+static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s,
+					    const u8 *addr)
+{
+	struct wpa_ssid *old_ssid;
+
+	wpas_connect_work_done(wpa_s);
+	wpa_clear_keys(wpa_s, addr);
+	old_ssid = wpa_s->current_ssid;
+	wpa_supplicant_mark_disassoc(wpa_s);
+	wpa_sm_set_config(wpa_s->wpa, NULL);
+	eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+	if (old_ssid != wpa_s->current_ssid)
+		wpas_notify_network_changed(wpa_s);
+	eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+}
+
+
+/**
+ * wpa_supplicant_deauthenticate - Deauthenticate the current connection
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @reason_code: IEEE 802.11 reason code for the deauthenticate frame
+ *
+ * This function is used to request %wpa_supplicant to deauthenticate from the
+ * current AP.
+ */
+void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
+				   int reason_code)
+{
+	u8 *addr = NULL;
+	union wpa_event_data event;
+	int zero_addr = 0;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Request to deauthenticate - bssid=" MACSTR
+		" pending_bssid=" MACSTR " reason=%d state=%s",
+		MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
+		reason_code, wpa_supplicant_state_txt(wpa_s->wpa_state));
+
+	if (!is_zero_ether_addr(wpa_s->bssid))
+		addr = wpa_s->bssid;
+	else if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
+		 (wpa_s->wpa_state == WPA_AUTHENTICATING ||
+		  wpa_s->wpa_state == WPA_ASSOCIATING))
+		addr = wpa_s->pending_bssid;
+	else if (wpa_s->wpa_state == WPA_ASSOCIATING) {
+		/*
+		 * When using driver-based BSS selection, we may not know the
+		 * BSSID with which we are currently trying to associate. We
+		 * need to notify the driver of this disconnection even in such
+		 * a case, so use the all zeros address here.
+		 */
+		addr = wpa_s->bssid;
+		zero_addr = 1;
+	}
+
+#ifdef CONFIG_TDLS
+	wpa_tdls_teardown_peers(wpa_s->wpa);
+#endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_MESH
+	if (wpa_s->ifmsh) {
+		wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
+			     wpa_s->ifname);
+		wpa_supplicant_leave_mesh(wpa_s);
+	}
+#endif /* CONFIG_MESH */
+
+	if (addr) {
+		wpa_drv_deauthenticate(wpa_s, addr, reason_code);
+		os_memset(&event, 0, sizeof(event));
+		event.deauth_info.reason_code = (u16) reason_code;
+		event.deauth_info.locally_generated = 1;
+		wpa_supplicant_event(wpa_s, EVENT_DEAUTH, &event);
+		if (zero_addr)
+			addr = NULL;
+	}
+
+	wpa_supplicant_clear_connection(wpa_s, addr);
+}
+
+static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s,
+					      struct wpa_ssid *ssid)
+{
+	if (!ssid || !ssid->disabled || ssid->disabled == 2)
+		return;
+
+	ssid->disabled = 0;
+	wpas_clear_temp_disabled(wpa_s, ssid, 1);
+	wpas_notify_network_enabled_changed(wpa_s, ssid);
+
+	/*
+	 * Try to reassociate since there is no current configuration and a new
+	 * network was made available.
+	 */
+	if (!wpa_s->current_ssid && !wpa_s->disconnected)
+		wpa_s->reassociate = 1;
+}
+
+
+/**
+ * wpa_supplicant_enable_network - Mark a configured network as enabled
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network or %NULL
+ *
+ * Enables the specified network or all networks if no network specified.
+ */
+void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
+				   struct wpa_ssid *ssid)
+{
+	if (ssid == NULL) {
+		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+			wpa_supplicant_enable_one_network(wpa_s, ssid);
+	} else
+		wpa_supplicant_enable_one_network(wpa_s, ssid);
+
+	if (wpa_s->reassociate && !wpa_s->disconnected &&
+	    (!wpa_s->current_ssid ||
+	     wpa_s->wpa_state == WPA_DISCONNECTED ||
+	     wpa_s->wpa_state == WPA_SCANNING)) {
+		if (wpa_s->sched_scanning) {
+			wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add "
+				   "new network to scan filters");
+			wpa_supplicant_cancel_sched_scan(wpa_s);
+		}
+
+		if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+			wpa_s->scan_req = NORMAL_SCAN_REQ;
+			wpa_supplicant_req_scan(wpa_s, 0, 0);
+		}
+	}
+}
+
+
+/**
+ * wpa_supplicant_disable_network - Mark a configured network as disabled
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network or %NULL
+ *
+ * Disables the specified network or all networks if no network specified.
+ */
+void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
+				    struct wpa_ssid *ssid)
+{
+	struct wpa_ssid *other_ssid;
+	int was_disabled;
+
+	if (ssid == NULL) {
+		if (wpa_s->sched_scanning)
+			wpa_supplicant_cancel_sched_scan(wpa_s);
+
+		for (other_ssid = wpa_s->conf->ssid; other_ssid;
+		     other_ssid = other_ssid->next) {
+			was_disabled = other_ssid->disabled;
+			if (was_disabled == 2)
+				continue; /* do not change persistent P2P group
+					   * data */
+
+			other_ssid->disabled = 1;
+
+			if (was_disabled != other_ssid->disabled)
+				wpas_notify_network_enabled_changed(
+					wpa_s, other_ssid);
+		}
+		if (wpa_s->current_ssid)
+			wpa_supplicant_deauthenticate(
+				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+	} else if (ssid->disabled != 2) {
+		if (ssid == wpa_s->current_ssid)
+			wpa_supplicant_deauthenticate(
+				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+
+		was_disabled = ssid->disabled;
+
+		ssid->disabled = 1;
+
+		if (was_disabled != ssid->disabled) {
+			wpas_notify_network_enabled_changed(wpa_s, ssid);
+			if (wpa_s->sched_scanning) {
+				wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan "
+					   "to remove network from filters");
+				wpa_supplicant_cancel_sched_scan(wpa_s);
+				wpa_supplicant_req_scan(wpa_s, 0, 0);
+			}
+		}
+	}
+}
+
+
+/**
+ * wpa_supplicant_select_network - Attempt association with a network
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network or %NULL for any network
+ */
+void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
+				   struct wpa_ssid *ssid)
+{
+
+	struct wpa_ssid *other_ssid;
+	int disconnected = 0;
+
+	if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) {
+		if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+			wpa_s->own_disconnect_req = 1;
+		wpa_supplicant_deauthenticate(
+			wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+		disconnected = 1;
+	}
+
+	if (ssid)
+		wpas_clear_temp_disabled(wpa_s, ssid, 1);
+
+	/*
+	 * Mark all other networks disabled or mark all networks enabled if no
+	 * network specified.
+	 */
+	for (other_ssid = wpa_s->conf->ssid; other_ssid;
+	     other_ssid = other_ssid->next) {
+		int was_disabled = other_ssid->disabled;
+		if (was_disabled == 2)
+			continue; /* do not change persistent P2P group data */
+
+		other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0;
+		if (was_disabled && !other_ssid->disabled)
+			wpas_clear_temp_disabled(wpa_s, other_ssid, 0);
+
+		if (was_disabled != other_ssid->disabled)
+			wpas_notify_network_enabled_changed(wpa_s, other_ssid);
+	}
+
+	if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid) {
+		/* We are already associated with the selected network */
+		wpa_printf(MSG_DEBUG, "Already associated with the "
+			   "selected network - do nothing");
+		return;
+	}
+
+	if (ssid) {
+		wpa_s->current_ssid = ssid;
+		eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+		wpa_s->connect_without_scan =
+			(ssid->mode == WPAS_MODE_MESH) ? ssid : NULL;
+
+		/*
+		 * Don't optimize next scan freqs since a new ESS has been
+		 * selected.
+		 */
+		os_free(wpa_s->next_scan_freqs);
+		wpa_s->next_scan_freqs = NULL;
+	} else {
+		wpa_s->connect_without_scan = NULL;
+	}
+
+	wpa_s->disconnected = 0;
+	wpa_s->reassociate = 1;
+
+	if (wpa_s->connect_without_scan ||
+	    wpa_supplicant_fast_associate(wpa_s) != 1) {
+		wpa_s->scan_req = NORMAL_SCAN_REQ;
+		wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
+	}
+
+	if (ssid)
+		wpas_notify_network_selected(wpa_s, ssid);
+}
+
+
+/**
+ * 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
+ * Returns: 0 if succeed or -1 if ap_scan has an invalid value
+ *
+ */
+int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan)
+{
+
+	int old_ap_scan;
+
+	if (ap_scan < 0 || ap_scan > 2)
+		return -1;
+
+	if (ap_scan == 2 && os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
+		wpa_printf(MSG_INFO,
+			   "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
+	}
+
+#ifdef ANDROID
+	if (ap_scan == 2 && ap_scan != wpa_s->conf->ap_scan &&
+	    wpa_s->wpa_state >= WPA_ASSOCIATING &&
+	    wpa_s->wpa_state < WPA_COMPLETED) {
+		wpa_printf(MSG_ERROR, "ap_scan = %d (%d) rejected while "
+			   "associating", wpa_s->conf->ap_scan, ap_scan);
+		return 0;
+	}
+#endif /* ANDROID */
+
+	old_ap_scan = wpa_s->conf->ap_scan;
+	wpa_s->conf->ap_scan = ap_scan;
+
+	if (old_ap_scan != wpa_s->conf->ap_scan)
+		wpas_notify_ap_scan_changed(wpa_s);
+
+	return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_bss_expiration_age - Set BSS entry expiration age
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @expire_age: Expiration age in seconds
+ * Returns: 0 if succeed or -1 if expire_age has an invalid value
+ *
+ */
+int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s,
+					  unsigned int bss_expire_age)
+{
+	if (bss_expire_age < 10) {
+		wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration age %u",
+			bss_expire_age);
+		return -1;
+	}
+	wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration age: %d sec",
+		bss_expire_age);
+	wpa_s->conf->bss_expiration_age = bss_expire_age;
+
+	return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_bss_expiration_count - Set BSS entry expiration scan count
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @expire_count: number of scans after which an unseen BSS is reclaimed
+ * Returns: 0 if succeed or -1 if expire_count has an invalid value
+ *
+ */
+int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s,
+					    unsigned int bss_expire_count)
+{
+	if (bss_expire_count < 1) {
+		wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration count %u",
+			bss_expire_count);
+		return -1;
+	}
+	wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration scan count: %u",
+		bss_expire_count);
+	wpa_s->conf->bss_expiration_scan_count = bss_expire_count;
+
+	return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_scan_interval - Set scan interval
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @scan_interval: scan interval in seconds
+ * Returns: 0 if succeed or -1 if scan_interval has an invalid value
+ *
+ */
+int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s,
+				     int scan_interval)
+{
+	if (scan_interval < 0) {
+		wpa_msg(wpa_s, MSG_ERROR, "Invalid scan interval %d",
+			scan_interval);
+		return -1;
+	}
+	wpa_msg(wpa_s, MSG_DEBUG, "Setting scan interval: %d sec",
+		scan_interval);
+	wpa_supplicant_update_scan_int(wpa_s, scan_interval);
+
+	return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_debug_params - Set global debug params
+ * @global: wpa_global structure
+ * @debug_level: debug level
+ * @debug_timestamp: determines if show timestamp in debug data
+ * @debug_show_keys: determines if show keys in debug data
+ * Returns: 0 if succeed or -1 if debug_level has wrong value
+ */
+int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level,
+				    int debug_timestamp, int debug_show_keys)
+{
+
+	int old_level, old_timestamp, old_show_keys;
+
+	/* check for allowed debuglevels */
+	if (debug_level != MSG_EXCESSIVE &&
+	    debug_level != MSG_MSGDUMP &&
+	    debug_level != MSG_DEBUG &&
+	    debug_level != MSG_INFO &&
+	    debug_level != MSG_WARNING &&
+	    debug_level != MSG_ERROR)
+		return -1;
+
+	old_level = wpa_debug_level;
+	old_timestamp = wpa_debug_timestamp;
+	old_show_keys = wpa_debug_show_keys;
+
+	wpa_debug_level = debug_level;
+	wpa_debug_timestamp = debug_timestamp ? 1 : 0;
+	wpa_debug_show_keys = debug_show_keys ? 1 : 0;
+
+	if (wpa_debug_level != old_level)
+		wpas_notify_debug_level_changed(global);
+	if (wpa_debug_timestamp != old_timestamp)
+		wpas_notify_debug_timestamp_changed(global);
+	if (wpa_debug_show_keys != old_show_keys)
+		wpas_notify_debug_show_keys_changed(global);
+
+	return 0;
+}
+
+
+/**
+ * wpa_supplicant_get_ssid - Get a pointer to the current network structure
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: A pointer to the current network structure or %NULL on failure
+ */
+struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *entry;
+	u8 ssid[SSID_MAX_LEN];
+	int res;
+	size_t ssid_len;
+	u8 bssid[ETH_ALEN];
+	int wired;
+
+	res = wpa_drv_get_ssid(wpa_s, ssid);
+	if (res < 0) {
+		wpa_msg(wpa_s, MSG_WARNING, "Could not read SSID from "
+			"driver");
+		return NULL;
+	}
+	ssid_len = res;
+
+	if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+		wpa_msg(wpa_s, MSG_WARNING, "Could not read BSSID from "
+			"driver");
+		return NULL;
+	}
+
+	wired = wpa_s->conf->ap_scan == 0 &&
+		(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED);
+
+	entry = wpa_s->conf->ssid;
+	while (entry) {
+		if (!wpas_network_disabled(wpa_s, entry) &&
+		    ((ssid_len == entry->ssid_len &&
+		      os_memcmp(ssid, entry->ssid, ssid_len) == 0) || wired) &&
+		    (!entry->bssid_set ||
+		     os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
+			return entry;
+#ifdef CONFIG_WPS
+		if (!wpas_network_disabled(wpa_s, entry) &&
+		    (entry->key_mgmt & WPA_KEY_MGMT_WPS) &&
+		    (entry->ssid == NULL || entry->ssid_len == 0) &&
+		    (!entry->bssid_set ||
+		     os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
+			return entry;
+#endif /* CONFIG_WPS */
+
+		if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set &&
+		    entry->ssid_len == 0 &&
+		    os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)
+			return entry;
+
+		entry = entry->next;
+	}
+
+	return NULL;
+}
+
+
+static int select_driver(struct wpa_supplicant *wpa_s, int i)
+{
+	struct wpa_global *global = wpa_s->global;
+
+	if (wpa_drivers[i]->global_init && global->drv_priv[i] == NULL) {
+		global->drv_priv[i] = wpa_drivers[i]->global_init();
+		if (global->drv_priv[i] == NULL) {
+			wpa_printf(MSG_ERROR, "Failed to initialize driver "
+				   "'%s'", wpa_drivers[i]->name);
+			return -1;
+		}
+	}
+
+	wpa_s->driver = wpa_drivers[i];
+	wpa_s->global_drv_priv = global->drv_priv[i];
+
+	return 0;
+}
+
+
+static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s,
+				     const char *name)
+{
+	int i;
+	size_t len;
+	const char *pos, *driver = name;
+
+	if (wpa_s == NULL)
+		return -1;
+
+	if (wpa_drivers[0] == NULL) {
+		wpa_msg(wpa_s, MSG_ERROR, "No driver interfaces build into "
+			"wpa_supplicant");
+		return -1;
+	}
+
+	if (name == NULL) {
+		/* default to first driver in the list */
+		return select_driver(wpa_s, 0);
+	}
+
+	do {
+		pos = os_strchr(driver, ',');
+		if (pos)
+			len = pos - driver;
+		else
+			len = os_strlen(driver);
+
+		for (i = 0; wpa_drivers[i]; i++) {
+			if (os_strlen(wpa_drivers[i]->name) == len &&
+			    os_strncmp(driver, wpa_drivers[i]->name, len) ==
+			    0) {
+				/* First driver that succeeds wins */
+				if (select_driver(wpa_s, i) == 0)
+					return 0;
+			}
+		}
+
+		driver = pos + 1;
+	} while (pos);
+
+	wpa_msg(wpa_s, MSG_ERROR, "Unsupported driver '%s'", name);
+	return -1;
+}
+
+
+/**
+ * wpa_supplicant_rx_eapol - Deliver a received EAPOL frame to wpa_supplicant
+ * @ctx: Context pointer (wpa_s); this is the ctx variable registered
+ *	with struct wpa_driver_ops::init()
+ * @src_addr: Source address of the EAPOL frame
+ * @buf: EAPOL data starting from the EAPOL header (i.e., no Ethernet header)
+ * @len: Length of the EAPOL data
+ *
+ * This function is called for each received EAPOL frame. Most driver
+ * interfaces rely on more generic OS mechanism for receiving frames through
+ * l2_packet, but if such a mechanism is not available, the driver wrapper may
+ * take care of received EAPOL frames and deliver them to the core supplicant
+ * code by calling this function.
+ */
+void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
+			     const u8 *buf, size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	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
+	     !wpa_s->ap_iface &&
+#endif /* CONFIG_AP */
+	     os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) != 0)) {
+		/*
+		 * There is possible race condition between receiving the
+		 * association event and the EAPOL frame since they are coming
+		 * through different paths from the driver. In order to avoid
+		 * issues in trying to process the EAPOL frame before receiving
+		 * association information, lets queue it for processing until
+		 * the association event is received. This may also be needed in
+		 * driver-based roaming case, so also use src_addr != BSSID as a
+		 * trigger if we have previously confirmed that the
+		 * Authenticator uses BSSID as the src_addr (which is not the
+		 * case with wired IEEE 802.1X).
+		 */
+		wpa_dbg(wpa_s, MSG_DEBUG, "Not associated - Delay processing "
+			"of received EAPOL frame (state=%s bssid=" MACSTR ")",
+			wpa_supplicant_state_txt(wpa_s->wpa_state),
+			MAC2STR(wpa_s->bssid));
+		wpabuf_free(wpa_s->pending_eapol_rx);
+		wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
+		if (wpa_s->pending_eapol_rx) {
+			os_get_reltime(&wpa_s->pending_eapol_rx_time);
+			os_memcpy(wpa_s->pending_eapol_rx_src, src_addr,
+				  ETH_ALEN);
+		}
+		return;
+	}
+
+	wpa_s->last_eapol_matches_bssid =
+		os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) == 0;
+
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface) {
+		wpa_supplicant_ap_rx_eapol(wpa_s, src_addr, buf, len);
+		return;
+	}
+#endif /* CONFIG_AP */
+
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Ignored received EAPOL frame since "
+			"no key management is configured");
+		return;
+	}
+
+	if (wpa_s->eapol_received == 0 &&
+	    (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) ||
+	     !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+	     wpa_s->wpa_state != WPA_COMPLETED) &&
+	    (wpa_s->current_ssid == NULL ||
+	     wpa_s->current_ssid->mode != IEEE80211_MODE_IBSS)) {
+		/* Timeout for completing IEEE 802.1X and WPA authentication */
+		int timeout = 10;
+
+		if (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) {
+			/* Use longer timeout for IEEE 802.1X/EAP */
+			timeout = 70;
+		}
+
+#ifdef CONFIG_WPS
+		if (wpa_s->current_ssid && wpa_s->current_bss &&
+		    (wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
+		    eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
+			/*
+			 * Use shorter timeout if going through WPS AP iteration
+			 * for PIN config method with an AP that does not
+			 * advertise Selected Registrar.
+			 */
+			struct wpabuf *wps_ie;
+
+			wps_ie = wpa_bss_get_vendor_ie_multi(
+				wpa_s->current_bss, WPS_IE_VENDOR_TYPE);
+			if (wps_ie &&
+			    !wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1))
+				timeout = 10;
+			wpabuf_free(wps_ie);
+		}
+#endif /* CONFIG_WPS */
+
+		wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
+	}
+	wpa_s->eapol_received++;
+
+	if (wpa_s->countermeasures) {
+		wpa_msg(wpa_s, MSG_INFO, "WPA: Countermeasures - dropped "
+			"EAPOL packet");
+		return;
+	}
+
+#ifdef CONFIG_IBSS_RSN
+	if (wpa_s->current_ssid &&
+	    wpa_s->current_ssid->mode == WPAS_MODE_IBSS) {
+		ibss_rsn_rx_eapol(wpa_s->ibss_rsn, src_addr, buf, len);
+		return;
+	}
+#endif /* CONFIG_IBSS_RSN */
+
+	/* Source address of the incoming EAPOL frame could be compared to the
+	 * current BSSID. However, it is possible that a centralized
+	 * Authenticator could be using another MAC address than the BSSID of
+	 * an AP, so just allow any address to be used for now. The replies are
+	 * still sent to the current BSSID (if available), though. */
+
+	os_memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN);
+	if (!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) &&
+	    eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0)
+		return;
+	wpa_drv_poll(wpa_s);
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+		wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len);
+	else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+		/*
+		 * Set portValid = TRUE here since we are going to skip 4-way
+		 * handshake processing which would normally set portValid. We
+		 * need this to allow the EAPOL state machines to be completed
+		 * without going through EAPOL-Key handshake.
+		 */
+		eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+	}
+}
+
+
+int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
+{
+	if ((!wpa_s->p2p_mgmt ||
+	     !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) {
+		l2_packet_deinit(wpa_s->l2);
+		wpa_s->l2 = l2_packet_init(wpa_s->ifname,
+					   wpa_drv_get_mac_addr(wpa_s),
+					   ETH_P_EAPOL,
+					   wpa_supplicant_rx_eapol, wpa_s, 0);
+		if (wpa_s->l2 == NULL)
+			return -1;
+	} else {
+		const u8 *addr = wpa_drv_get_mac_addr(wpa_s);
+		if (addr)
+			os_memcpy(wpa_s->own_addr, addr, ETH_ALEN);
+	}
+
+	if (wpa_s->l2 && l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) {
+		wpa_msg(wpa_s, MSG_ERROR, "Failed to get own L2 address");
+		return -1;
+	}
+
+	wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
+
+	return 0;
+}
+
+
+static void wpa_supplicant_rx_eapol_bridge(void *ctx, const u8 *src_addr,
+					   const u8 *buf, size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	const struct l2_ethhdr *eth;
+
+	if (len < sizeof(*eth))
+		return;
+	eth = (const struct l2_ethhdr *) buf;
+
+	if (os_memcmp(eth->h_dest, wpa_s->own_addr, ETH_ALEN) != 0 &&
+	    !(eth->h_dest[0] & 0x01)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR
+			" (bridge - not for this interface - ignore)",
+			MAC2STR(src_addr), MAC2STR(eth->h_dest));
+		return;
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR
+		" (bridge)", MAC2STR(src_addr), MAC2STR(eth->h_dest));
+	wpa_supplicant_rx_eapol(wpa_s, src_addr, buf + sizeof(*eth),
+				len - sizeof(*eth));
+}
+
+
+/**
+ * wpa_supplicant_driver_init - Initialize driver interface parameters
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called to initialize driver interface parameters.
+ * wpa_drv_init() must have been called before this function to initialize the
+ * driver interface.
+ */
+int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
+{
+	static int interface_count = 0;
+
+	if (wpa_supplicant_update_mac_addr(wpa_s) < 0)
+		return -1;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR,
+		MAC2STR(wpa_s->own_addr));
+	os_memcpy(wpa_s->perm_addr, wpa_s->own_addr, ETH_ALEN);
+	wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
+
+	if (wpa_s->bridge_ifname[0]) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge "
+			"interface '%s'", wpa_s->bridge_ifname);
+		wpa_s->l2_br = l2_packet_init_bridge(
+			wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr,
+			ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1);
+		if (wpa_s->l2_br == NULL) {
+			wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet "
+				"connection for the bridge interface '%s'",
+				wpa_s->bridge_ifname);
+			return -1;
+		}
+	}
+
+	if (wpa_s->conf->ap_scan == 2 &&
+	    os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
+		wpa_printf(MSG_INFO,
+			   "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
+	}
+
+	wpa_clear_keys(wpa_s, NULL);
+
+	/* Make sure that TKIP countermeasures are not left enabled (could
+	 * happen if wpa_supplicant is killed during countermeasures. */
+	wpa_drv_set_countermeasures(wpa_s, 0);
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "RSN: flushing PMKID list in the driver");
+	wpa_drv_flush_pmkid(wpa_s);
+
+	wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
+	wpa_s->prev_scan_wildcard = 0;
+
+	if (wpa_supplicant_enabled_networks(wpa_s)) {
+		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+			interface_count = 0;
+		}
+#ifndef ANDROID
+		if (!wpa_s->p2p_mgmt &&
+		    wpa_supplicant_delayed_sched_scan(wpa_s,
+						      interface_count % 3,
+						      100000))
+			wpa_supplicant_req_scan(wpa_s, interface_count % 3,
+						100000);
+#endif /* ANDROID */
+		interface_count++;
+	} else
+		wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+
+	return 0;
+}
+
+
+static int wpa_supplicant_daemon(const char *pid_file)
+{
+	wpa_printf(MSG_DEBUG, "Daemonize..");
+	return os_daemonize(pid_file);
+}
+
+
+static struct wpa_supplicant *
+wpa_supplicant_alloc(struct wpa_supplicant *parent)
+{
+	struct wpa_supplicant *wpa_s;
+
+	wpa_s = os_zalloc(sizeof(*wpa_s));
+	if (wpa_s == NULL)
+		return NULL;
+	wpa_s->scan_req = INITIAL_SCAN_REQ;
+	wpa_s->scan_interval = 5;
+	wpa_s->new_connection = 1;
+	wpa_s->parent = parent ? parent : wpa_s;
+	wpa_s->sched_scanning = 0;
+
+	return wpa_s;
+}
+
+
+#ifdef CONFIG_HT_OVERRIDES
+
+static int wpa_set_htcap_mcs(struct wpa_supplicant *wpa_s,
+			     struct ieee80211_ht_capabilities *htcaps,
+			     struct ieee80211_ht_capabilities *htcaps_mask,
+			     const char *ht_mcs)
+{
+	/* parse ht_mcs into hex array */
+	int i;
+	const char *tmp = ht_mcs;
+	char *end = NULL;
+
+	/* If ht_mcs is null, do not set anything */
+	if (!ht_mcs)
+		return 0;
+
+	/* This is what we are setting in the kernel */
+	os_memset(&htcaps->supported_mcs_set, 0, IEEE80211_HT_MCS_MASK_LEN);
+
+	wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs);
+
+	for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+		errno = 0;
+		long v = strtol(tmp, &end, 16);
+		if (errno == 0) {
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"htcap value[%i]: %ld end: %p  tmp: %p",
+				i, v, end, tmp);
+			if (end == tmp)
+				break;
+
+			htcaps->supported_mcs_set[i] = v;
+			tmp = end;
+		} else {
+			wpa_msg(wpa_s, MSG_ERROR,
+				"Failed to parse ht-mcs: %s, error: %s\n",
+				ht_mcs, strerror(errno));
+			return -1;
+		}
+	}
+
+	/*
+	 * If we were able to parse any values, then set mask for the MCS set.
+	 */
+	if (i) {
+		os_memset(&htcaps_mask->supported_mcs_set, 0xff,
+			  IEEE80211_HT_MCS_MASK_LEN - 1);
+		/* skip the 3 reserved bits */
+		htcaps_mask->supported_mcs_set[IEEE80211_HT_MCS_MASK_LEN - 1] =
+			0x1f;
+	}
+
+	return 0;
+}
+
+
+static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s,
+				 struct ieee80211_ht_capabilities *htcaps,
+				 struct ieee80211_ht_capabilities *htcaps_mask,
+				 int disabled)
+{
+	le16 msk;
+
+	wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled);
+
+	if (disabled == -1)
+		return 0;
+
+	msk = host_to_le16(HT_CAP_INFO_MAX_AMSDU_SIZE);
+	htcaps_mask->ht_capabilities_info |= msk;
+	if (disabled)
+		htcaps->ht_capabilities_info &= msk;
+	else
+		htcaps->ht_capabilities_info |= msk;
+
+	return 0;
+}
+
+
+static int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s,
+				struct ieee80211_ht_capabilities *htcaps,
+				struct ieee80211_ht_capabilities *htcaps_mask,
+				int factor)
+{
+	wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_factor: %d", factor);
+
+	if (factor == -1)
+		return 0;
+
+	if (factor < 0 || factor > 3) {
+		wpa_msg(wpa_s, MSG_ERROR, "ampdu_factor: %d out of range. "
+			"Must be 0-3 or -1", factor);
+		return -EINVAL;
+	}
+
+	htcaps_mask->a_mpdu_params |= 0x3; /* 2 bits for factor */
+	htcaps->a_mpdu_params &= ~0x3;
+	htcaps->a_mpdu_params |= factor & 0x3;
+
+	return 0;
+}
+
+
+static int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s,
+				 struct ieee80211_ht_capabilities *htcaps,
+				 struct ieee80211_ht_capabilities *htcaps_mask,
+				 int density)
+{
+	wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_density: %d", density);
+
+	if (density == -1)
+		return 0;
+
+	if (density < 0 || density > 7) {
+		wpa_msg(wpa_s, MSG_ERROR,
+			"ampdu_density: %d out of range. Must be 0-7 or -1.",
+			density);
+		return -EINVAL;
+	}
+
+	htcaps_mask->a_mpdu_params |= 0x1C;
+	htcaps->a_mpdu_params &= ~(0x1C);
+	htcaps->a_mpdu_params |= (density << 2) & 0x1C;
+
+	return 0;
+}
+
+
+static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s,
+				struct ieee80211_ht_capabilities *htcaps,
+				struct ieee80211_ht_capabilities *htcaps_mask,
+				int disabled)
+{
+	/* Masking these out disables HT40 */
+	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);
+
+	if (disabled)
+		htcaps->ht_capabilities_info &= ~msk;
+	else
+		htcaps->ht_capabilities_info |= msk;
+
+	htcaps_mask->ht_capabilities_info |= msk;
+
+	return 0;
+}
+
+
+static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s,
+			       struct ieee80211_ht_capabilities *htcaps,
+			       struct ieee80211_ht_capabilities *htcaps_mask,
+			       int disabled)
+{
+	/* Masking these out disables SGI */
+	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);
+
+	if (disabled)
+		htcaps->ht_capabilities_info &= ~msk;
+	else
+		htcaps->ht_capabilities_info |= msk;
+
+	htcaps_mask->ht_capabilities_info |= msk;
+
+	return 0;
+}
+
+
+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)
+{
+	struct ieee80211_ht_capabilities *htcaps;
+	struct ieee80211_ht_capabilities *htcaps_mask;
+
+	if (!ssid)
+		return;
+
+	params->disable_ht = ssid->disable_ht;
+	if (!params->htcaps || !params->htcaps_mask)
+		return;
+
+	htcaps = (struct ieee80211_ht_capabilities *) params->htcaps;
+	htcaps_mask = (struct ieee80211_ht_capabilities *) params->htcaps_mask;
+	wpa_set_htcap_mcs(wpa_s, htcaps, htcaps_mask, ssid->ht_mcs);
+	wpa_disable_max_amsdu(wpa_s, htcaps, htcaps_mask,
+			      ssid->disable_max_amsdu);
+	wpa_set_ampdu_factor(wpa_s, htcaps, htcaps_mask, ssid->ampdu_factor);
+	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 */
+
+
+#ifdef CONFIG_VHT_OVERRIDES
+void wpa_supplicant_apply_vht_overrides(
+	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+	struct wpa_driver_associate_params *params)
+{
+	struct ieee80211_vht_capabilities *vhtcaps;
+	struct ieee80211_vht_capabilities *vhtcaps_mask;
+
+	if (!ssid)
+		return;
+
+	params->disable_vht = ssid->disable_vht;
+
+	vhtcaps = (void *) params->vhtcaps;
+	vhtcaps_mask = (void *) params->vhtcaps_mask;
+
+	if (!vhtcaps || !vhtcaps_mask)
+		return;
+
+	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 & VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) {
+		int max_ampdu;
+
+		max_ampdu = (ssid->vht_capa &
+			     VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) >>
+			VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT;
+
+		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 |=		\
+			3 << 2 * (i - 1);				\
+		vhtcaps->vht_supported_mcs_set.tx_map |=		\
+			ssid->vht_tx_mcs_nss_ ##i << 2 * (i - 1);	\
+	}								\
+	if (ssid->vht_rx_mcs_nss_ ##i >= 0) {				\
+		vhtcaps_mask->vht_supported_mcs_set.rx_map |=		\
+			3 << 2 * (i - 1);				\
+		vhtcaps->vht_supported_mcs_set.rx_map |=		\
+			ssid->vht_rx_mcs_nss_ ##i << 2 * (i - 1);	\
+	}
+
+	OVERRIDE_MCS(1);
+	OVERRIDE_MCS(2);
+	OVERRIDE_MCS(3);
+	OVERRIDE_MCS(4);
+	OVERRIDE_MCS(5);
+	OVERRIDE_MCS(6);
+	OVERRIDE_MCS(7);
+	OVERRIDE_MCS(8);
+}
+#endif /* CONFIG_VHT_OVERRIDES */
+
+
+static int pcsc_reader_init(struct wpa_supplicant *wpa_s)
+{
+#ifdef PCSC_FUNCS
+	size_t len;
+
+	if (!wpa_s->conf->pcsc_reader)
+		return 0;
+
+	wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader);
+	if (!wpa_s->scard)
+		return 1;
+
+	if (wpa_s->conf->pcsc_pin &&
+	    scard_set_pin(wpa_s->scard, wpa_s->conf->pcsc_pin) < 0) {
+		scard_deinit(wpa_s->scard);
+		wpa_s->scard = NULL;
+		wpa_msg(wpa_s, MSG_ERROR, "PC/SC PIN validation failed");
+		return -1;
+	}
+
+	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);
+
+	wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard);
+	eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
+#endif /* PCSC_FUNCS */
+
+	return 0;
+}
+
+
+int wpas_init_ext_pw(struct wpa_supplicant *wpa_s)
+{
+	char *val, *pos;
+
+	ext_password_deinit(wpa_s->ext_pw);
+	wpa_s->ext_pw = NULL;
+	eapol_sm_set_ext_pw_ctx(wpa_s->eapol, NULL);
+
+	if (!wpa_s->conf->ext_password_backend)
+		return 0;
+
+	val = os_strdup(wpa_s->conf->ext_password_backend);
+	if (val == NULL)
+		return -1;
+	pos = os_strchr(val, ':');
+	if (pos)
+		*pos++ = '\0';
+
+	wpa_printf(MSG_DEBUG, "EXT PW: Initialize backend '%s'", val);
+
+	wpa_s->ext_pw = ext_password_init(val, pos);
+	os_free(val);
+	if (wpa_s->ext_pw == NULL) {
+		wpa_printf(MSG_DEBUG, "EXT PW: Failed to initialize backend");
+		return -1;
+	}
+	eapol_sm_set_ext_pw_ctx(wpa_s->eapol, wpa_s->ext_pw);
+
+	return 0;
+}
+
+
+#ifdef CONFIG_FST
+
+static const u8 * wpas_fst_get_bssid_cb(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return (is_zero_ether_addr(wpa_s->bssid) ||
+		wpa_s->wpa_state != WPA_COMPLETED) ? NULL : wpa_s->bssid;
+}
+
+
+static void wpas_fst_get_channel_info_cb(void *ctx,
+					 enum hostapd_hw_mode *hw_mode,
+					 u8 *channel)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	if (wpa_s->current_bss) {
+		*hw_mode = ieee80211_freq_to_chan(wpa_s->current_bss->freq,
+						  channel);
+	} else if (wpa_s->hw.num_modes) {
+		*hw_mode = wpa_s->hw.modes[0].mode;
+	} else {
+		WPA_ASSERT(0);
+		*hw_mode = 0;
+	}
+}
+
+
+static int wpas_fst_get_hw_modes(void *ctx, struct hostapd_hw_modes **modes)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	*modes = wpa_s->hw.modes;
+	return wpa_s->hw.num_modes;
+}
+
+
+static void wpas_fst_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	wpa_hexdump_buf(MSG_DEBUG, "FST: Set IEs", fst_ies);
+	wpa_s->fst_ies = fst_ies;
+}
+
+
+static int wpas_fst_send_action_cb(void *ctx, const u8 *da, struct wpabuf *data)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	WPA_ASSERT(os_memcmp(wpa_s->bssid, da, ETH_ALEN) == 0);
+	return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+					  wpa_s->own_addr, wpa_s->bssid,
+					  wpabuf_head(data), wpabuf_len(data),
+				   0);
+}
+
+
+static const struct wpabuf * wpas_fst_get_mb_ie_cb(void *ctx, const u8 *addr)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
+	return wpa_s->received_mb_ies;
+}
+
+
+static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr,
+				     const u8 *buf, size_t size)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct mb_ies_info info;
+
+	WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
+
+	if (!mb_ies_info_by_ies(&info, buf, size)) {
+		wpabuf_free(wpa_s->received_mb_ies);
+		wpa_s->received_mb_ies = mb_ies_by_info(&info);
+	}
+}
+
+
+const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx,
+				   Boolean mb_only)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	*get_ctx = NULL;
+	if (!is_zero_ether_addr(wpa_s->bssid))
+		return (wpa_s->received_mb_ies || !mb_only) ?
+			wpa_s->bssid : NULL;
+	return NULL;
+}
+
+
+const u8 * wpas_fst_get_peer_next(void *ctx, struct fst_get_peer_ctx **get_ctx,
+				  Boolean mb_only)
+{
+	return NULL;
+}
+
+void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
+				       struct fst_wpa_obj *iface_obj)
+{
+	iface_obj->ctx              = wpa_s;
+	iface_obj->get_bssid        = wpas_fst_get_bssid_cb;
+	iface_obj->get_channel_info = wpas_fst_get_channel_info_cb;
+	iface_obj->get_hw_modes     = wpas_fst_get_hw_modes;
+	iface_obj->set_ies          = wpas_fst_set_ies_cb;
+	iface_obj->send_action      = wpas_fst_send_action_cb;
+	iface_obj->get_mb_ie        = wpas_fst_get_mb_ie_cb;
+	iface_obj->update_mb_ie     = wpas_fst_update_mb_ie_cb;
+	iface_obj->get_peer_first   = wpas_fst_get_peer_first;
+	iface_obj->get_peer_next    = wpas_fst_get_peer_next;
+}
+#endif /* CONFIG_FST */
+
+static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
+				    const 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->radio->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;
+	if (wpa_s->ext_work_in_progress) {
+		wpa_printf(MSG_DEBUG,
+			   "External radio work in progress - delay start of pending item");
+		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);
+}
+
+
+struct wpa_radio_work *
+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 work;
+	}
+
+	return NULL;
+}
+
+
+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)
+{
+	struct wpa_driver_capa capa;
+	int capa_res;
+
+	wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
+		   "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname,
+		   iface->confname ? iface->confname : "N/A",
+		   iface->driver ? iface->driver : "default",
+		   iface->ctrl_interface ? iface->ctrl_interface : "N/A",
+		   iface->bridge_ifname ? iface->bridge_ifname : "N/A");
+
+	if (iface->confname) {
+#ifdef CONFIG_BACKEND_FILE
+		wpa_s->confname = os_rel2abs_path(iface->confname);
+		if (wpa_s->confname == NULL) {
+			wpa_printf(MSG_ERROR, "Failed to get absolute path "
+				   "for configuration file '%s'.",
+				   iface->confname);
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "Configuration file '%s' -> '%s'",
+			   iface->confname, wpa_s->confname);
+#else /* CONFIG_BACKEND_FILE */
+		wpa_s->confname = os_strdup(iface->confname);
+#endif /* CONFIG_BACKEND_FILE */
+		wpa_s->conf = wpa_config_read(wpa_s->confname, NULL);
+		if (wpa_s->conf == NULL) {
+			wpa_printf(MSG_ERROR, "Failed to read or parse "
+				   "configuration '%s'.", wpa_s->confname);
+			return -1;
+		}
+		wpa_s->confanother = os_rel2abs_path(iface->confanother);
+		wpa_config_read(wpa_s->confanother, wpa_s->conf);
+
+		/*
+		 * Override ctrl_interface and driver_param if set on command
+		 * line.
+		 */
+		if (iface->ctrl_interface) {
+			os_free(wpa_s->conf->ctrl_interface);
+			wpa_s->conf->ctrl_interface =
+				os_strdup(iface->ctrl_interface);
+		}
+
+		if (iface->driver_param) {
+			os_free(wpa_s->conf->driver_param);
+			wpa_s->conf->driver_param =
+				os_strdup(iface->driver_param);
+		}
+
+		if (iface->p2p_mgmt && !iface->ctrl_interface) {
+			os_free(wpa_s->conf->ctrl_interface);
+			wpa_s->conf->ctrl_interface = NULL;
+		}
+	} else
+		wpa_s->conf = wpa_config_alloc_empty(iface->ctrl_interface,
+						     iface->driver_param);
+
+	if (wpa_s->conf == NULL) {
+		wpa_printf(MSG_ERROR, "\nNo configuration found.");
+		return -1;
+	}
+
+	if (iface->ifname == NULL) {
+		wpa_printf(MSG_ERROR, "\nInterface name is required.");
+		return -1;
+	}
+	if (os_strlen(iface->ifname) >= sizeof(wpa_s->ifname)) {
+		wpa_printf(MSG_ERROR, "\nToo long interface name '%s'.",
+			   iface->ifname);
+		return -1;
+	}
+	os_strlcpy(wpa_s->ifname, iface->ifname, sizeof(wpa_s->ifname));
+
+	if (iface->bridge_ifname) {
+		if (os_strlen(iface->bridge_ifname) >=
+		    sizeof(wpa_s->bridge_ifname)) {
+			wpa_printf(MSG_ERROR, "\nToo long bridge interface "
+				   "name '%s'.", iface->bridge_ifname);
+			return -1;
+		}
+		os_strlcpy(wpa_s->bridge_ifname, iface->bridge_ifname,
+			   sizeof(wpa_s->bridge_ifname));
+	}
+
+	/* RSNA Supplicant Key Management - INITIALIZE */
+	eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
+	eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+
+	/* Initialize driver interface and register driver event handler before
+	 * L2 receive handler so that association events are processed before
+	 * EAPOL-Key packets if both become available for the same select()
+	 * call. */
+	if (wpas_init_driver(wpa_s, iface) < 0)
+		return -1;
+
+	if (wpa_supplicant_init_wpa(wpa_s) < 0)
+		return -1;
+
+	wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname,
+			  wpa_s->bridge_ifname[0] ? wpa_s->bridge_ifname :
+			  NULL);
+	wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth);
+
+	if (wpa_s->conf->dot11RSNAConfigPMKLifetime &&
+	    wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
+			     wpa_s->conf->dot11RSNAConfigPMKLifetime)) {
+		wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
+			"dot11RSNAConfigPMKLifetime");
+		return -1;
+	}
+
+	if (wpa_s->conf->dot11RSNAConfigPMKReauthThreshold &&
+	    wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
+			     wpa_s->conf->dot11RSNAConfigPMKReauthThreshold)) {
+		wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
+			"dot11RSNAConfigPMKReauthThreshold");
+		return -1;
+	}
+
+	if (wpa_s->conf->dot11RSNAConfigSATimeout &&
+	    wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT,
+			     wpa_s->conf->dot11RSNAConfigSATimeout)) {
+		wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
+			"dot11RSNAConfigSATimeout");
+		return -1;
+	}
+
+	wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s,
+						      &wpa_s->hw.num_modes,
+						      &wpa_s->hw.flags);
+	if (wpa_s->hw.modes) {
+		u16 i;
+
+		for (i = 0; i < wpa_s->hw.num_modes; i++) {
+			if (wpa_s->hw.modes[i].vht_capab) {
+				wpa_s->hw_capab = CAPAB_VHT;
+				break;
+			}
+
+			if (wpa_s->hw.modes[i].ht_capab &
+			    HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
+				wpa_s->hw_capab = CAPAB_HT40;
+			else if (wpa_s->hw.modes[i].ht_capab &&
+				 wpa_s->hw_capab == CAPAB_NO_HT_VHT)
+				wpa_s->hw_capab = CAPAB_HT;
+		}
+	}
+
+	capa_res = wpa_drv_get_capa(wpa_s, &capa);
+	if (capa_res == 0) {
+		wpa_s->drv_capa_known = 1;
+		wpa_s->drv_flags = capa.flags;
+		wpa_s->drv_enc = capa.enc;
+		wpa_s->drv_smps_modes = capa.smps_modes;
+		wpa_s->drv_rrm_flags = capa.rrm_flags;
+		wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
+		wpa_s->max_scan_ssids = capa.max_scan_ssids;
+		wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
+		wpa_s->sched_scan_supported = capa.sched_scan_supported;
+		wpa_s->max_match_sets = capa.max_match_sets;
+		wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
+		wpa_s->max_stations = capa.max_stations;
+		wpa_s->extended_capa = capa.extended_capa;
+		wpa_s->extended_capa_mask = capa.extended_capa_mask;
+		wpa_s->extended_capa_len = capa.extended_capa_len;
+		wpa_s->num_multichan_concurrent =
+			capa.num_multichan_concurrent;
+		wpa_s->wmm_ac_supported = capa.wmm_ac_supported;
+
+		if (capa.mac_addr_rand_scan_supported)
+			wpa_s->mac_addr_rand_supported |= MAC_ADDR_RAND_SCAN;
+		if (wpa_s->sched_scan_supported &&
+		    capa.mac_addr_rand_sched_scan_supported)
+			wpa_s->mac_addr_rand_supported |=
+				(MAC_ADDR_RAND_SCHED_SCAN | MAC_ADDR_RAND_PNO);
+	}
+	if (wpa_s->max_remain_on_chan == 0)
+		wpa_s->max_remain_on_chan = 1000;
+
+	/*
+	 * Only take p2p_mgmt parameters when P2P Device is supported.
+	 * Doing it here as it determines whether l2_packet_init() will be done
+	 * during wpa_supplicant_driver_init().
+	 */
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)
+		wpa_s->p2p_mgmt = iface->p2p_mgmt;
+	else
+		iface->p2p_mgmt = 1;
+
+	if (wpa_s->num_multichan_concurrent == 0)
+		wpa_s->num_multichan_concurrent = 1;
+
+	if (wpa_supplicant_driver_init(wpa_s) < 0)
+		return -1;
+
+#ifdef CONFIG_TDLS
+	if ((!iface->p2p_mgmt ||
+	     !(wpa_s->drv_flags &
+	       WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
+	    wpa_tdls_init(wpa_s->wpa))
+		return -1;
+#endif /* CONFIG_TDLS */
+
+	if (wpa_s->conf->country[0] && wpa_s->conf->country[1] &&
+	    wpa_drv_set_country(wpa_s, wpa_s->conf->country)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Failed to set country");
+		return -1;
+	}
+
+#ifdef CONFIG_FST
+	if (wpa_s->conf->fst_group_id) {
+		struct fst_iface_cfg cfg;
+		struct fst_wpa_obj iface_obj;
+
+		fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
+		os_strlcpy(cfg.group_id, wpa_s->conf->fst_group_id,
+			   sizeof(cfg.group_id));
+		cfg.priority = wpa_s->conf->fst_priority;
+		cfg.llt = wpa_s->conf->fst_llt;
+
+		wpa_s->fst = fst_attach(wpa_s->ifname, wpa_s->own_addr,
+					&iface_obj, &cfg);
+		if (!wpa_s->fst) {
+			wpa_msg(wpa_s, MSG_ERROR,
+				"FST: Cannot attach iface %s to group %s",
+				wpa_s->ifname, cfg.group_id);
+			return -1;
+		}
+	}
+#endif /* CONFIG_FST */
+
+	if (wpas_wps_init(wpa_s))
+		return -1;
+
+	if (wpa_supplicant_init_eapol(wpa_s) < 0)
+		return -1;
+	wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
+
+	wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
+	if (wpa_s->ctrl_iface == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to initialize control interface '%s'.\n"
+			   "You may have another wpa_supplicant process "
+			   "already running or the file was\n"
+			   "left by an unclean termination of wpa_supplicant "
+			   "in which case you will need\n"
+			   "to manually remove this file before starting "
+			   "wpa_supplicant again.\n",
+			   wpa_s->conf->ctrl_interface);
+		return -1;
+	}
+
+	wpa_s->gas = gas_query_init(wpa_s);
+	if (wpa_s->gas == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to initialize GAS query");
+		return -1;
+	}
+
+	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;
+	}
+
+	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 (capa_res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
+		return -1;
+
+#ifdef CONFIG_EAP_PROXY
+{
+	size_t 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)",
+			   wpa_s->imsi, wpa_s->mnc_len);
+	} else {
+		wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available");
+	}
+}
+#endif /* CONFIG_EAP_PROXY */
+
+	if (pcsc_reader_init(wpa_s) < 0)
+		return -1;
+
+	if (wpas_init_ext_pw(wpa_s) < 0)
+		return -1;
+
+	wpas_rrm_reset(wpa_s);
+
+	return 0;
+}
+
+
+static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
+					int notify, int terminate)
+{
+	struct wpa_global *global = wpa_s->global;
+	struct wpa_supplicant *iface, *prev;
+
+	if (wpa_s == wpa_s->parent)
+		wpas_p2p_group_remove(wpa_s, "*");
+
+	iface = global->ifaces;
+	while (iface) {
+		if (iface == wpa_s || iface->parent != wpa_s) {
+			iface = iface->next;
+			continue;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "Remove remaining child interface %s from parent %s",
+			   iface->ifname, wpa_s->ifname);
+		prev = iface;
+		iface = iface->next;
+		wpa_supplicant_remove_iface(global, prev, terminate);
+	}
+
+	wpa_s->disconnected = 1;
+	if (wpa_s->drv_priv) {
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+
+		wpa_drv_set_countermeasures(wpa_s, 0);
+		wpa_clear_keys(wpa_s, NULL);
+	}
+
+	wpa_supplicant_cleanup(wpa_s);
+	wpas_p2p_deinit_iface(wpa_s);
+
+	wpas_ctrl_radio_work_flush(wpa_s);
+	radio_remove_interface(wpa_s);
+
+#ifdef CONFIG_FST
+	if (wpa_s->fst) {
+		fst_detach(wpa_s->fst);
+		wpa_s->fst = NULL;
+	}
+	if (wpa_s->received_mb_ies) {
+		wpabuf_free(wpa_s->received_mb_ies);
+		wpa_s->received_mb_ies = NULL;
+	}
+#endif /* CONFIG_FST */
+
+	if (wpa_s->drv_priv)
+		wpa_drv_deinit(wpa_s);
+
+	if (notify)
+		wpas_notify_iface_removed(wpa_s);
+
+	if (terminate)
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING);
+
+	if (wpa_s->ctrl_iface) {
+		wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
+		wpa_s->ctrl_iface = NULL;
+	}
+
+#ifdef CONFIG_MESH
+	if (wpa_s->ifmsh) {
+		wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh);
+		wpa_s->ifmsh = NULL;
+	}
+#endif /* CONFIG_MESH */
+
+	if (wpa_s->conf != NULL) {
+		wpa_config_free(wpa_s->conf);
+		wpa_s->conf = NULL;
+	}
+
+	os_free(wpa_s->ssids_from_scan_req);
+
+	os_free(wpa_s);
+}
+
+
+/**
+ * wpa_supplicant_add_iface - Add a new network interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @iface: Interface configuration options
+ * @parent: Parent interface or %NULL to assign new interface as parent
+ * Returns: Pointer to the created interface or %NULL on failure
+ *
+ * This function is used to add new network interfaces for %wpa_supplicant.
+ * This can be called before wpa_supplicant_run() to add interfaces before the
+ * main event loop has been started. In addition, new interfaces can be added
+ * dynamically while %wpa_supplicant is already running. This could happen,
+ * e.g., when a hotplug network adapter is inserted.
+ */
+struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
+						 struct wpa_interface *iface,
+						 struct wpa_supplicant *parent)
+{
+	struct wpa_supplicant *wpa_s;
+	struct wpa_interface t_iface;
+	struct wpa_ssid *ssid;
+
+	if (global == NULL || iface == NULL)
+		return NULL;
+
+	wpa_s = wpa_supplicant_alloc(parent);
+	if (wpa_s == NULL)
+		return NULL;
+
+	wpa_s->global = global;
+
+	t_iface = *iface;
+	if (global->params.override_driver) {
+		wpa_printf(MSG_DEBUG, "Override interface parameter: driver "
+			   "('%s' -> '%s')",
+			   iface->driver, global->params.override_driver);
+		t_iface.driver = global->params.override_driver;
+	}
+	if (global->params.override_ctrl_interface) {
+		wpa_printf(MSG_DEBUG, "Override interface parameter: "
+			   "ctrl_interface ('%s' -> '%s')",
+			   iface->ctrl_interface,
+			   global->params.override_ctrl_interface);
+		t_iface.ctrl_interface =
+			global->params.override_ctrl_interface;
+	}
+	if (wpa_supplicant_init_iface(wpa_s, &t_iface)) {
+		wpa_printf(MSG_DEBUG, "Failed to add interface %s",
+			   iface->ifname);
+		wpa_supplicant_deinit_iface(wpa_s, 0, 0);
+		return NULL;
+	}
+
+	if (iface->p2p_mgmt == 0) {
+		/* Notify the control interfaces about new iface */
+		if (wpas_notify_iface_added(wpa_s)) {
+			wpa_supplicant_deinit_iface(wpa_s, 1, 0);
+			return NULL;
+		}
+
+		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+			wpas_notify_network_added(wpa_s, ssid);
+	}
+
+	wpa_s->next = global->ifaces;
+	global->ifaces = wpa_s;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname);
+	wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+
+#ifdef CONFIG_P2P
+	if (wpa_s->global->p2p == NULL &&
+	    !wpa_s->global->p2p_disabled && !wpa_s->conf->p2p_disabled &&
+	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
+	    wpas_p2p_add_p2pdev_interface(
+		    wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) {
+		wpa_printf(MSG_INFO,
+			   "P2P: Failed to enable P2P Device interface");
+		/* Try to continue without. P2P will be disabled. */
+	}
+#endif /* CONFIG_P2P */
+
+	return wpa_s;
+}
+
+
+/**
+ * wpa_supplicant_remove_iface - Remove a network interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @wpa_s: Pointer to the network interface to be removed
+ * Returns: 0 if interface was removed, -1 if interface was not found
+ *
+ * This function can be used to dynamically remove network interfaces from
+ * %wpa_supplicant, e.g., when a hotplug network adapter is ejected. In
+ * addition, this function is used to remove all remaining interfaces when
+ * %wpa_supplicant is terminated.
+ */
+int wpa_supplicant_remove_iface(struct wpa_global *global,
+				struct wpa_supplicant *wpa_s,
+				int terminate)
+{
+	struct wpa_supplicant *prev;
+#ifdef CONFIG_MESH
+	unsigned int mesh_if_created = wpa_s->mesh_if_created;
+	char *ifname = NULL;
+#endif /* CONFIG_MESH */
+
+	/* Remove interface from the global list of interfaces */
+	prev = global->ifaces;
+	if (prev == wpa_s) {
+		global->ifaces = wpa_s->next;
+	} else {
+		while (prev && prev->next != wpa_s)
+			prev = prev->next;
+		if (prev == NULL)
+			return -1;
+		prev->next = wpa_s->next;
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
+
+#ifdef CONFIG_MESH
+	if (mesh_if_created) {
+		ifname = os_strdup(wpa_s->ifname);
+		if (ifname == NULL) {
+			wpa_dbg(wpa_s, MSG_ERROR,
+				"mesh: Failed to malloc ifname");
+			return -1;
+		}
+	}
+#endif /* CONFIG_MESH */
+
+	if (global->p2p_group_formation == wpa_s)
+		global->p2p_group_formation = NULL;
+	if (global->p2p_invite_group == wpa_s)
+		global->p2p_invite_group = NULL;
+	wpa_supplicant_deinit_iface(wpa_s, 1, terminate);
+
+#ifdef CONFIG_MESH
+	if (mesh_if_created) {
+		wpa_drv_if_remove(global->ifaces, WPA_IF_MESH, ifname);
+		os_free(ifname);
+	}
+#endif /* CONFIG_MESH */
+
+	return 0;
+}
+
+
+/**
+ * wpa_supplicant_get_eap_mode - Get the current EAP mode
+ * @wpa_s: Pointer to the network interface
+ * Returns: Pointer to the eap mode or the string "UNKNOWN" if not found
+ */
+const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s)
+{
+	const char *eapol_method;
+
+        if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) == 0 &&
+            wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+		return "NO-EAP";
+	}
+
+	eapol_method = eapol_sm_get_method_name(wpa_s->eapol);
+	if (eapol_method == NULL)
+		return "UNKNOWN-EAP";
+
+	return eapol_method;
+}
+
+
+/**
+ * wpa_supplicant_get_iface - Get a new network interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @ifname: Interface name
+ * Returns: Pointer to the interface or %NULL if not found
+ */
+struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global,
+						 const char *ifname)
+{
+	struct wpa_supplicant *wpa_s;
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (os_strcmp(wpa_s->ifname, ifname) == 0)
+			return wpa_s;
+	}
+	return NULL;
+}
+
+
+#ifndef CONFIG_NO_WPA_MSG
+static const char * wpa_supplicant_msg_ifname_cb(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	if (wpa_s == NULL)
+		return NULL;
+	return wpa_s->ifname;
+}
+#endif /* CONFIG_NO_WPA_MSG */
+
+
+#ifndef WPA_SUPPLICANT_CLEANUP_INTERVAL
+#define WPA_SUPPLICANT_CLEANUP_INTERVAL 10
+#endif /* WPA_SUPPLICANT_CLEANUP_INTERVAL */
+
+/* Periodic cleanup tasks */
+static void wpas_periodic(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_global *global = eloop_ctx;
+	struct wpa_supplicant *wpa_s;
+
+	eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+			       wpas_periodic, global, NULL);
+
+#ifdef CONFIG_P2P
+	if (global->p2p)
+		p2p_expire_peers(global->p2p);
+#endif /* CONFIG_P2P */
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
+#ifdef CONFIG_AP
+		ap_periodic(wpa_s);
+#endif /* CONFIG_AP */
+	}
+}
+
+
+/**
+ * wpa_supplicant_init - Initialize %wpa_supplicant
+ * @params: Parameters for %wpa_supplicant
+ * Returns: Pointer to global %wpa_supplicant data, or %NULL on failure
+ *
+ * This function is used to initialize %wpa_supplicant. After successful
+ * initialization, the returned data pointer can be used to add and remove
+ * network interfaces, and eventually, to deinitialize %wpa_supplicant.
+ */
+struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
+{
+	struct wpa_global *global;
+	int ret, i;
+
+	if (params == NULL)
+		return NULL;
+
+#ifdef CONFIG_DRIVER_NDIS
+	{
+		void driver_ndis_init_ops(void);
+		driver_ndis_init_ops();
+	}
+#endif /* CONFIG_DRIVER_NDIS */
+
+#ifndef CONFIG_NO_WPA_MSG
+	wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
+#endif /* CONFIG_NO_WPA_MSG */
+
+	if (params->wpa_debug_file_path)
+		wpa_debug_open_file(params->wpa_debug_file_path);
+	else
+		wpa_debug_setup_stdout();
+	if (params->wpa_debug_syslog)
+		wpa_debug_open_syslog();
+	if (params->wpa_debug_tracing) {
+		ret = wpa_debug_open_linux_tracing();
+		if (ret) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to enable trace logging");
+			return NULL;
+		}
+	}
+
+	ret = eap_register_methods();
+	if (ret) {
+		wpa_printf(MSG_ERROR, "Failed to register EAP methods");
+		if (ret == -2)
+			wpa_printf(MSG_ERROR, "Two or more EAP methods used "
+				   "the same EAP type.");
+		return NULL;
+	}
+
+	global = os_zalloc(sizeof(*global));
+	if (global == NULL)
+		return NULL;
+	dl_list_init(&global->p2p_srv_bonjour);
+	dl_list_init(&global->p2p_srv_upnp);
+	global->params.daemonize = params->daemonize;
+	global->params.wait_for_monitor = params->wait_for_monitor;
+	global->params.dbus_ctrl_interface = params->dbus_ctrl_interface;
+	if (params->pid_file)
+		global->params.pid_file = os_strdup(params->pid_file);
+	if (params->ctrl_interface)
+		global->params.ctrl_interface =
+			os_strdup(params->ctrl_interface);
+	if (params->ctrl_interface_group)
+		global->params.ctrl_interface_group =
+			os_strdup(params->ctrl_interface_group);
+	if (params->override_driver)
+		global->params.override_driver =
+			os_strdup(params->override_driver);
+	if (params->override_ctrl_interface)
+		global->params.override_ctrl_interface =
+			os_strdup(params->override_ctrl_interface);
+#ifdef CONFIG_P2P
+	if (params->conf_p2p_dev)
+		global->params.conf_p2p_dev =
+			os_strdup(params->conf_p2p_dev);
+#endif /* CONFIG_P2P */
+	wpa_debug_level = global->params.wpa_debug_level =
+		params->wpa_debug_level;
+	wpa_debug_show_keys = global->params.wpa_debug_show_keys =
+		params->wpa_debug_show_keys;
+	wpa_debug_timestamp = global->params.wpa_debug_timestamp =
+		params->wpa_debug_timestamp;
+
+	wpa_printf(MSG_INFO, "wpa_supplicant v" VERSION_STR);
+
+	if (eloop_init()) {
+		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+		wpa_supplicant_deinit(global);
+		return NULL;
+	}
+
+	random_init(params->entropy_file);
+
+	global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global);
+	if (global->ctrl_iface == NULL) {
+		wpa_supplicant_deinit(global);
+		return NULL;
+	}
+
+	if (wpas_notify_supplicant_initialized(global)) {
+		wpa_supplicant_deinit(global);
+		return NULL;
+	}
+
+	for (i = 0; wpa_drivers[i]; i++)
+		global->drv_count++;
+	if (global->drv_count == 0) {
+		wpa_printf(MSG_ERROR, "No drivers enabled");
+		wpa_supplicant_deinit(global);
+		return NULL;
+	}
+	global->drv_priv = os_calloc(global->drv_count, sizeof(void *));
+	if (global->drv_priv == NULL) {
+		wpa_supplicant_deinit(global);
+		return NULL;
+	}
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (wifi_display_init(global) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to initialize Wi-Fi Display");
+		wpa_supplicant_deinit(global);
+		return NULL;
+	}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+			       wpas_periodic, global, NULL);
+
+	return global;
+}
+
+
+/**
+ * wpa_supplicant_run - Run the %wpa_supplicant main event loop
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: 0 after successful event loop run, -1 on failure
+ *
+ * This function starts the main event loop and continues running as long as
+ * there are any remaining events. In most cases, this function is running as
+ * long as the %wpa_supplicant process in still in use.
+ */
+int wpa_supplicant_run(struct wpa_global *global)
+{
+	struct wpa_supplicant *wpa_s;
+
+	if (global->params.daemonize &&
+	    wpa_supplicant_daemon(global->params.pid_file))
+		return -1;
+
+	if (global->params.wait_for_monitor) {
+		for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
+			if (wpa_s->ctrl_iface)
+				wpa_supplicant_ctrl_iface_wait(
+					wpa_s->ctrl_iface);
+	}
+
+	eloop_register_signal_terminate(wpa_supplicant_terminate, global);
+	eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
+
+	eloop_run();
+
+	return 0;
+}
+
+
+/**
+ * wpa_supplicant_deinit - Deinitialize %wpa_supplicant
+ * @global: Pointer to global data from wpa_supplicant_init()
+ *
+ * This function is called to deinitialize %wpa_supplicant and to free all
+ * allocated resources. Remaining network interfaces will also be removed.
+ */
+void wpa_supplicant_deinit(struct wpa_global *global)
+{
+	int i;
+
+	if (global == NULL)
+		return;
+
+	eloop_cancel_timeout(wpas_periodic, global, NULL);
+
+#ifdef CONFIG_WIFI_DISPLAY
+	wifi_display_deinit(global);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	while (global->ifaces)
+		wpa_supplicant_remove_iface(global, global->ifaces, 1);
+
+	if (global->ctrl_iface)
+		wpa_supplicant_global_ctrl_iface_deinit(global->ctrl_iface);
+
+	wpas_notify_supplicant_deinitialized(global);
+
+	eap_peer_unregister_methods();
+#ifdef CONFIG_AP
+	eap_server_unregister_methods();
+#endif /* CONFIG_AP */
+
+	for (i = 0; wpa_drivers[i] && global->drv_priv; i++) {
+		if (!global->drv_priv[i])
+			continue;
+		wpa_drivers[i]->global_deinit(global->drv_priv[i]);
+	}
+	os_free(global->drv_priv);
+
+	random_deinit();
+
+	eloop_destroy();
+
+	if (global->params.pid_file) {
+		os_daemonize_terminate(global->params.pid_file);
+		os_free(global->params.pid_file);
+	}
+	os_free(global->params.ctrl_interface);
+	os_free(global->params.ctrl_interface_group);
+	os_free(global->params.override_driver);
+	os_free(global->params.override_ctrl_interface);
+#ifdef CONFIG_P2P
+	os_free(global->params.conf_p2p_dev);
+#endif /* CONFIG_P2P */
+
+	os_free(global->p2p_disallow_freq.range);
+	os_free(global->p2p_go_avoid_freq.range);
+	os_free(global->add_psk);
+
+	os_free(global);
+	wpa_debug_close_syslog();
+	wpa_debug_close_file();
+	wpa_debug_close_linux_tracing();
+}
+
+
+void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s)
+{
+	if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) &&
+	    wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
+		char country[3];
+		country[0] = wpa_s->conf->country[0];
+		country[1] = wpa_s->conf->country[1];
+		country[2] = '\0';
+		if (wpa_drv_set_country(wpa_s, country) < 0) {
+			wpa_printf(MSG_ERROR, "Failed to set country code "
+				   "'%s'", country);
+		}
+	}
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND)
+		wpas_init_ext_pw(wpa_s);
+
+#ifdef CONFIG_WPS
+	wpas_wps_update_config(wpa_s);
+#endif /* CONFIG_WPS */
+	wpas_p2p_update_config(wpa_s);
+	wpa_s->conf->changed_parameters = 0;
+}
+
+
+void add_freq(int *freqs, int *num_freqs, int freq)
+{
+	int i;
+
+	for (i = 0; i < *num_freqs; i++) {
+		if (freqs[i] == freq)
+			return;
+	}
+
+	freqs[*num_freqs] = freq;
+	(*num_freqs)++;
+}
+
+
+static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss, *cbss;
+	const int max_freqs = 10;
+	int *freqs;
+	int num_freqs = 0;
+
+	freqs = os_calloc(max_freqs + 1, sizeof(int));
+	if (freqs == NULL)
+		return NULL;
+
+	cbss = wpa_s->current_bss;
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		if (bss == cbss)
+			continue;
+		if (bss->ssid_len == cbss->ssid_len &&
+		    os_memcmp(bss->ssid, cbss->ssid, bss->ssid_len) == 0 &&
+		    wpa_blacklist_get(wpa_s, bss->bssid) == NULL) {
+			add_freq(freqs, &num_freqs, bss->freq);
+			if (num_freqs == max_freqs)
+				break;
+		}
+	}
+
+	if (num_freqs == 0) {
+		os_free(freqs);
+		freqs = NULL;
+	}
+
+	return freqs;
+}
+
+
+void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+	int timeout;
+	int count;
+	int *freqs = NULL;
+
+	wpas_connect_work_done(wpa_s);
+
+	/*
+	 * Remove possible authentication timeout since the connection failed.
+	 */
+	eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+
+	/*
+	 * There is no point in blacklisting the AP if this event is
+	 * generated based on local request to disconnect.
+	 */
+	if (wpa_s->own_disconnect_req) {
+		wpa_s->own_disconnect_req = 0;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Ignore connection failure due to local request to disconnect");
+		return;
+	}
+	if (wpa_s->disconnected) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Ignore connection failure "
+			"indication since interface has been put into "
+			"disconnected state");
+		return;
+	}
+
+	/*
+	 * Add the failed BSSID into the blacklist and speed up next scan
+	 * attempt if there could be other APs that could accept association.
+	 * The current blacklist count indicates how many times we have tried
+	 * connecting to this AP and multiple attempts mean that other APs are
+	 * either not available or has already been tried, so that we can start
+	 * increasing the delay here to avoid constant scanning.
+	 */
+	count = wpa_blacklist_add(wpa_s, bssid);
+	if (count == 1 && wpa_s->current_bss) {
+		/*
+		 * This BSS was not in the blacklist before. If there is
+		 * another BSS available for the same ESS, we should try that
+		 * next. Otherwise, we may as well try this one once more
+		 * before allowing other, likely worse, ESSes to be considered.
+		 */
+		freqs = get_bss_freqs_in_ess(wpa_s);
+		if (freqs) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "Another BSS in this ESS "
+				"has been seen; try it next");
+			wpa_blacklist_add(wpa_s, bssid);
+			/*
+			 * On the next scan, go through only the known channels
+			 * used in this ESS based on previous scans to speed up
+			 * common load balancing use case.
+			 */
+			os_free(wpa_s->next_scan_freqs);
+			wpa_s->next_scan_freqs = freqs;
+		}
+	}
+
+	/*
+	 * Add previous failure count in case the temporary blacklist was
+	 * cleared due to no other BSSes being available.
+	 */
+	count += wpa_s->extra_blacklist_count;
+
+	if (count > 3 && wpa_s->current_ssid) {
+		wpa_printf(MSG_DEBUG, "Continuous association failures - "
+			   "consider temporary network disabling");
+		wpas_auth_failed(wpa_s, "CONN_FAILED");
+	}
+
+	switch (count) {
+	case 1:
+		timeout = 100;
+		break;
+	case 2:
+		timeout = 500;
+		break;
+	case 3:
+		timeout = 1000;
+		break;
+	case 4:
+		timeout = 5000;
+		break;
+	default:
+		timeout = 10000;
+		break;
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Blacklist count %d --> request scan in %d "
+		"ms", count, timeout);
+
+	/*
+	 * TODO: if more than one possible AP is available in scan results,
+	 * could try the other ones before requesting a new scan.
+	 */
+	wpa_supplicant_req_scan(wpa_s, timeout / 1000,
+				1000 * (timeout % 1000));
+}
+
+
+int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s)
+{
+	return wpa_s->conf->ap_scan == 2 ||
+		(wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION);
+}
+
+
+#if defined(CONFIG_CTRL_IFACE) || defined(CONFIG_CTRL_IFACE_DBUS_NEW)
+int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
+					      struct wpa_ssid *ssid,
+					      const char *field,
+					      const char *value)
+{
+#ifdef IEEE8021X_EAPOL
+	struct eap_peer_config *eap = &ssid->eap;
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: response handle field=%s", field);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: response value",
+			      (const u8 *) value, os_strlen(value));
+
+	switch (wpa_supplicant_ctrl_req_from_string(field)) {
+	case WPA_CTRL_REQ_EAP_IDENTITY:
+		os_free(eap->identity);
+		eap->identity = (u8 *) os_strdup(value);
+		eap->identity_len = os_strlen(value);
+		eap->pending_req_identity = 0;
+		if (ssid == wpa_s->current_ssid)
+			wpa_s->reassociate = 1;
+		break;
+	case WPA_CTRL_REQ_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;
+		if (ssid == wpa_s->current_ssid)
+			wpa_s->reassociate = 1;
+		break;
+	case WPA_CTRL_REQ_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;
+		if (ssid == wpa_s->current_ssid)
+			wpa_s->reassociate = 1;
+		break;
+	case WPA_CTRL_REQ_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:
+		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);
+		eap->pending_req_otp = NULL;
+		eap->pending_req_otp_len = 0;
+		break;
+	case WPA_CTRL_REQ_EAP_PASSPHRASE:
+		str_clear_free(eap->private_key_passwd);
+		eap->private_key_passwd = os_strdup(value);
+		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;
+	case WPA_CTRL_REQ_PSK_PASSPHRASE:
+		if (wpa_config_set(ssid, "psk", value, 0) < 0)
+			return -1;
+		ssid->mem_only_psk = 1;
+		if (ssid->passphrase)
+			wpa_config_update_psk(ssid);
+		if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning)
+			wpa_supplicant_req_scan(wpa_s, 0, 0);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
+		return -1;
+	}
+
+	return 0;
+#else /* IEEE8021X_EAPOL */
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: IEEE 802.1X not included");
+	return -1;
+#endif /* IEEE8021X_EAPOL */
+}
+#endif /* CONFIG_CTRL_IFACE || CONFIG_CTRL_IFACE_DBUS_NEW */
+
+
+int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	int i;
+	unsigned int drv_enc;
+
+	if (wpa_s->p2p_mgmt)
+		return 1; /* no normal network profiles on p2p_mgmt interface */
+
+	if (ssid == NULL)
+		return 1;
+
+	if (ssid->disabled)
+		return 1;
+
+	if (wpa_s->drv_capa_known)
+		drv_enc = wpa_s->drv_enc;
+	else
+		drv_enc = (unsigned int) -1;
+
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		size_t len = ssid->wep_key_len[i];
+		if (len == 0)
+			continue;
+		if (len == 5 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP40))
+			continue;
+		if (len == 13 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP104))
+			continue;
+		if (len == 16 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP128))
+			continue;
+		return 1; /* invalid WEP key */
+	}
+
+	if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
+	    (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk &&
+	    !ssid->mem_only_psk)
+		return 1;
+
+	return 0;
+}
+
+
+int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_IEEE80211W
+	if (ssid == NULL || ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
+		if (wpa_s->conf->pmf == MGMT_FRAME_PROTECTION_OPTIONAL &&
+		    !(wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)) {
+			/*
+			 * Driver does not support BIP -- ignore pmf=1 default
+			 * since the connection with PMF would fail and the
+			 * configuration does not require PMF to be enabled.
+			 */
+			return NO_MGMT_FRAME_PROTECTION;
+		}
+
+		return wpa_s->conf->pmf;
+	}
+
+	return ssid->ieee80211w;
+#else /* CONFIG_IEEE80211W */
+	return NO_MGMT_FRAME_PROTECTION;
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->global->conc_pref == WPA_CONC_PREF_P2P)
+		return 1;
+	if (wpa_s->global->conc_pref == WPA_CONC_PREF_STA)
+		return 0;
+	return -1;
+}
+
+
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason)
+{
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	int dur;
+	struct os_reltime now;
+
+	if (ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "Authentication failure but no known "
+			   "SSID block");
+		return;
+	}
+
+	if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
+		return;
+
+	ssid->auth_failures++;
+
+#ifdef CONFIG_P2P
+	if (ssid->p2p_group &&
+	    (wpa_s->p2p_in_provisioning || wpa_s->show_group_started)) {
+		/*
+		 * Skip the wait time since there is a short timeout on the
+		 * connection to a P2P group.
+		 */
+		return;
+	}
+#endif /* CONFIG_P2P */
+
+	if (ssid->auth_failures > 50)
+		dur = 300;
+	else if (ssid->auth_failures > 10)
+		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;
+
+	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 reason=%s",
+		ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+		ssid->auth_failures, dur, reason);
+}
+
+
+void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid, int clear_failures)
+{
+	if (ssid == NULL)
+		return;
+
+	if (ssid->disabled_until.sec) {
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REENABLED
+			"id=%d ssid=\"%s\"",
+			ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+	}
+	ssid->disabled_until.sec = 0;
+	ssid->disabled_until.usec = 0;
+	if (clear_failures)
+		ssid->auth_failures = 0;
+}
+
+
+int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+	size_t i;
+
+	if (wpa_s->disallow_aps_bssid == NULL)
+		return 0;
+
+	for (i = 0; i < wpa_s->disallow_aps_bssid_count; i++) {
+		if (os_memcmp(wpa_s->disallow_aps_bssid + i * ETH_ALEN,
+			      bssid, ETH_ALEN) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
+		    size_t ssid_len)
+{
+	size_t i;
+
+	if (wpa_s->disallow_aps_ssid == NULL || ssid == NULL)
+		return 0;
+
+	for (i = 0; i < wpa_s->disallow_aps_ssid_count; i++) {
+		struct wpa_ssid_value *s = &wpa_s->disallow_aps_ssid[i];
+		if (ssid_len == s->ssid_len &&
+		    os_memcmp(ssid, s->ssid, ssid_len) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpas_request_connection - Request a new connection
+ * @wpa_s: Pointer to the network interface
+ *
+ * This function is used to request a new connection to be found. It will mark
+ * the interface to allow reassociation and request a new scan to find a
+ * suitable network to connect to.
+ */
+void wpas_request_connection(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->normal_scans = 0;
+	wpa_s->scan_req = NORMAL_SCAN_REQ;
+	wpa_supplicant_reinit_autoscan(wpa_s);
+	wpa_s->extra_blacklist_count = 0;
+	wpa_s->disconnected = 0;
+	wpa_s->reassociate = 1;
+
+	if (wpa_supplicant_fast_associate(wpa_s) != 1)
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	else
+		wpa_s->reattach = 0;
+}
+
+
+void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
+		    struct wpa_used_freq_data *freqs_data,
+		    unsigned int len)
+{
+	unsigned int i;
+
+	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);
+	}
+}
+
+
+/*
+ * Find the operating frequencies of any of the virtual interfaces that
+ * 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_data(struct wpa_supplicant *wpa_s,
+				struct wpa_used_freq_data *freqs_data,
+				unsigned int len)
+{
+	struct wpa_supplicant *ifs;
+	u8 bssid[ETH_ALEN];
+	int freq;
+	unsigned int idx = 0, i;
+
+	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);
+
+	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;
+
+		if (ifs->current_ssid->mode == WPAS_MODE_AP ||
+		    ifs->current_ssid->mode == WPAS_MODE_P2P_GO ||
+		    ifs->current_ssid->mode == WPAS_MODE_MESH)
+			freq = ifs->current_ssid->frequency;
+		else if (wpa_drv_get_bssid(ifs, bssid) == 0)
+			freq = ifs->assoc_freq;
+		else
+			continue;
+
+		/* Hold only distinct freqs */
+		for (i = 0; i < idx; i++)
+			if (freqs_data[i].freq == freq)
+				break;
+
+		if (i == idx)
+			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;
+}
+
+
+static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx)
+{
+	struct rrm_data *rrm = data;
+
+	if (!rrm->notify_neighbor_rep) {
+		wpa_printf(MSG_ERROR,
+			   "RRM: Unexpected neighbor report timeout");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE");
+	rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL);
+
+	rrm->notify_neighbor_rep = NULL;
+	rrm->neighbor_rep_cb_ctx = NULL;
+}
+
+
+/*
+ * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant
+ * @wpa_s: Pointer to wpa_supplicant
+ */
+void wpas_rrm_reset(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->rrm.rrm_used = 0;
+
+	eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+			     NULL);
+	if (wpa_s->rrm.notify_neighbor_rep)
+		wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
+	wpa_s->rrm.next_neighbor_rep_token = 1;
+}
+
+
+/*
+ * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report
+ * @wpa_s: Pointer to wpa_supplicant
+ * @report: Neighbor report buffer, prefixed by a 1-byte dialog token
+ * @report_len: Length of neighbor report buffer
+ */
+void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
+				   const u8 *report, size_t report_len)
+{
+	struct wpabuf *neighbor_rep;
+
+	wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len);
+	if (report_len < 1)
+		return;
+
+	if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Discarding neighbor report with token %d (expected %d)",
+			   report[0], wpa_s->rrm.next_neighbor_rep_token - 1);
+		return;
+	}
+
+	eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+			     NULL);
+
+	if (!wpa_s->rrm.notify_neighbor_rep) {
+		wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report");
+		return;
+	}
+
+	/* skipping the first byte, which is only an id (dialog token) */
+	neighbor_rep = wpabuf_alloc(report_len - 1);
+	if (neighbor_rep == NULL)
+		return;
+	wpabuf_put_data(neighbor_rep, report + 1, report_len - 1);
+	wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
+		   report[0]);
+	wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx,
+				       neighbor_rep);
+	wpa_s->rrm.notify_neighbor_rep = NULL;
+	wpa_s->rrm.neighbor_rep_cb_ctx = NULL;
+}
+
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+/* Workaround different, undefined for Windows, error codes used here */
+#define ENOTCONN -1
+#define EOPNOTSUPP -1
+#define ECANCELED -1
+#endif
+
+/**
+ * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP
+ * @wpa_s: Pointer to wpa_supplicant
+ * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE
+ *	  is sent in the request.
+ * @cb: Callback function to be called once the requested report arrives, or
+ *	timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds.
+ *	In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's
+ *	the requester's responsibility to free it.
+ *	In the latter case NULL will be sent in 'neighbor_rep'.
+ * @cb_ctx: Context value to send the callback function
+ * Returns: 0 in case of success, negative error code otherwise
+ *
+ * In case there is a previous request which has not been answered yet, the
+ * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT.
+ * Request must contain a callback function.
+ */
+int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
+				       const struct wpa_ssid *ssid,
+				       void (*cb)(void *ctx,
+						  struct wpabuf *neighbor_rep),
+				       void *cb_ctx)
+{
+	struct wpabuf *buf;
+	const u8 *rrm_ie;
+
+	if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM.");
+		return -ENOTCONN;
+	}
+
+	if (!wpa_s->rrm.rrm_used) {
+		wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection.");
+		return -EOPNOTSUPP;
+	}
+
+	rrm_ie = wpa_bss_get_ie(wpa_s->current_bss,
+				WLAN_EID_RRM_ENABLED_CAPABILITIES);
+	if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) ||
+	    !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: No network support for Neighbor Report.");
+		return -EOPNOTSUPP;
+	}
+
+	if (!cb) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Neighbor Report request must provide a callback.");
+		return -EINVAL;
+	}
+
+	/* Refuse if there's a live request */
+	if (wpa_s->rrm.notify_neighbor_rep) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Currently handling previous Neighbor Report.");
+		return -EBUSY;
+	}
+
+	/* 3 = action category + action code + dialog token */
+	buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0));
+	if (buf == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Failed to allocate Neighbor Report Request");
+		return -ENOMEM;
+	}
+
+	wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d",
+		   (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""),
+		   wpa_s->rrm.next_neighbor_rep_token);
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST);
+	wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token);
+	if (ssid) {
+		wpabuf_put_u8(buf, WLAN_EID_SSID);
+		wpabuf_put_u8(buf, ssid->ssid_len);
+		wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len);
+	}
+
+	wpa_s->rrm.next_neighbor_rep_token++;
+
+	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				wpa_s->own_addr, wpa_s->bssid,
+				wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Failed to send Neighbor Report Request");
+		wpabuf_free(buf);
+		return -ECANCELED;
+	}
+
+	wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx;
+	wpa_s->rrm.notify_neighbor_rep = cb;
+	eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0,
+			       wpas_rrm_neighbor_rep_timeout_handler,
+			       &wpa_s->rrm, NULL);
+
+	wpabuf_free(buf);
+	return 0;
+}
+
+
+void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
+					      const u8 *src,
+					      const u8 *frame, size_t len,
+					      int rssi)
+{
+	struct wpabuf *buf;
+	const struct rrm_link_measurement_request *req;
+	struct rrm_link_measurement_report report;
+
+	if (wpa_s->wpa_state != WPA_COMPLETED) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Ignoring link measurement request. Not associated");
+		return;
+	}
+
+	if (!wpa_s->rrm.rrm_used) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Ignoring link measurement request. Not RRM network");
+		return;
+	}
+
+	if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Measurement report failed. TX power insertion not supported");
+		return;
+	}
+
+	req = (const struct rrm_link_measurement_request *) frame;
+	if (len < sizeof(*req)) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Link measurement report failed. Request too short");
+		return;
+	}
+
+	os_memset(&report, 0, sizeof(report));
+	report.tpc.eid = WLAN_EID_TPC_REPORT;
+	report.tpc.len = 2;
+	report.rsni = 255; /* 255 indicates that RSNI is not available */
+	report.dialog_token = req->dialog_token;
+
+	/*
+	 * It's possible to estimate RCPI based on RSSI in dBm. This
+	 * calculation will not reflect the correct value for high rates,
+	 * but it's good enough for Action frames which are transmitted
+	 * with up to 24 Mbps rates.
+	 */
+	if (!rssi)
+		report.rcpi = 255; /* not available */
+	else if (rssi < -110)
+		report.rcpi = 0;
+	else if (rssi > 0)
+		report.rcpi = 220;
+	else
+		report.rcpi = (rssi + 110) * 2;
+
+	/* action_category + action_code */
+	buf = wpabuf_alloc(2 + sizeof(report));
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "RRM: Link measurement report failed. Buffer allocation failed");
+		return;
+	}
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT);
+	wpabuf_put_data(buf, &report, sizeof(report));
+	wpa_hexdump(MSG_DEBUG, "RRM: Link measurement report:",
+		    wpabuf_head(buf), wpabuf_len(buf));
+
+	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src,
+				wpa_s->own_addr, wpa_s->bssid,
+				wpabuf_head(buf), wpabuf_len(buf), 0)) {
+		wpa_printf(MSG_ERROR,
+			   "RRM: Link measurement report failed. Send action failed");
+	}
+	wpabuf_free(buf);
+}
diff --git a/hostap/wpa_supplicant/wpa_supplicant.conf b/hostap/wpa_supplicant/wpa_supplicant.conf
new file mode 100644
index 0000000..6fe67e4
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_supplicant.conf
@@ -0,0 +1,1566 @@
+##### Example wpa_supplicant configuration file ###############################
+#
+# This file describes configuration file format and lists all available option.
+# Please also take a look at simpler configuration examples in 'examples'
+# subdirectory.
+#
+# Empty lines and lines starting with # are ignored
+
+# NOTE! This file may contain password information and should probably be made
+# readable only by root user on multiuser systems.
+
+# Note: All file paths in this configuration file should use full (absolute,
+# not relative to working directory) path in order to allow working directory
+# to be changed. This can happen if wpa_supplicant is run in the background.
+
+# Whether to allow wpa_supplicant to update (overwrite) configuration
+#
+# This option can be used to allow wpa_supplicant to overwrite configuration
+# file whenever configuration is changed (e.g., new network block is added with
+# wpa_cli or wpa_gui, or a password is changed). This is required for
+# wpa_cli/wpa_gui to be able to store the configuration changes permanently.
+# Please note that overwriting configuration file will remove the comments from
+# it.
+#update_config=1
+
+# global configuration (shared by all network blocks)
+#
+# Parameters for the control interface. If this is specified, wpa_supplicant
+# will open a control interface that is available for external programs to
+# manage wpa_supplicant. The meaning of this string depends on which control
+# interface mechanism is used. For all cases, the existence of this parameter
+# in configuration is used to determine whether the control interface is
+# enabled.
+#
+# For UNIX domain sockets (default on Linux and BSD): This is a directory that
+# will be created for UNIX domain sockets for listening to requests from
+# external programs (CLI/GUI, etc.) for status information and configuration.
+# The socket file will be named based on the interface name, so multiple
+# wpa_supplicant processes can be run at the same time if more than one
+# interface is used.
+# /var/run/wpa_supplicant is the recommended directory for sockets and by
+# default, wpa_cli will use it when trying to connect with wpa_supplicant.
+#
+# Access control for the control interface can be configured by setting the
+# directory to allow only members of a group to use sockets. This way, it is
+# possible to run wpa_supplicant as root (since it needs to change network
+# configuration and open raw sockets) and still allow GUI/CLI components to be
+# run as non-root users. However, since the control interface can be used to
+# change the network configuration, this access needs to be protected in many
+# cases. By default, wpa_supplicant is configured to use gid 0 (root). If you
+# want to allow non-root users to use the control interface, add a new group
+# and change this value to match with that group. Add users that should have
+# control interface access to this group. If this variable is commented out or
+# not included in the configuration file, group will not be changed from the
+# value it got by default when the directory or socket was created.
+#
+# When configuring both the directory and group, use following format:
+# DIR=/var/run/wpa_supplicant GROUP=wheel
+# DIR=/var/run/wpa_supplicant GROUP=0
+# (group can be either group name or gid)
+#
+# For UDP connections (default on Windows): The value will be ignored. This
+# variable is just used to select that the control interface is to be created.
+# The value can be set to, e.g., udp (ctrl_interface=udp)
+#
+# For Windows Named Pipe: This value can be used to set the security descriptor
+# for controlling access to the control interface. Security descriptor can be
+# set using Security Descriptor String Format (see http://msdn.microsoft.com/
+# library/default.asp?url=/library/en-us/secauthz/security/
+# security_descriptor_string_format.asp). The descriptor string needs to be
+# prefixed with SDDL=. For example, ctrl_interface=SDDL=D: would set an empty
+# DACL (which will reject all connections). See README-Windows.txt for more
+# information about SDDL string format.
+#
+ctrl_interface=/var/run/wpa_supplicant
+
+# IEEE 802.1X/EAPOL version
+# wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which defines
+# EAPOL version 2. However, there are many APs that do not handle the new
+# version number correctly (they seem to drop the frames completely). In order
+# 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
+# By default, wpa_supplicant requests driver to perform AP scanning and then
+# uses the scan results to select a suitable AP. Another alternative is to
+# allow the driver to take care of AP scanning and selection and use
+# wpa_supplicant just to process EAPOL frames based on IEEE 802.11 association
+# information from the driver.
+# 1: wpa_supplicant initiates scanning and AP selection; if no APs matching to
+#    the currently enabled networks are found, a new network (IBSS or AP mode
+#    operation) may be initialized (if configured) (default)
+# 0: driver takes care of scanning, AP selection, and IEEE 802.11 association
+#    parameters (e.g., WPA IE generation); this mode can also be used with
+#    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,
+#    the network blocks in the configuration file are tried one by one until
+#    the driver reports successful association; each network block should have
+#    explicit security policy (i.e., only one option in the lists) for
+#    key_mgmt, pairwise, group, proto variables
+# Note: ap_scan=2 should not be used with the nl80211 driver interface (the
+# current Linux interface). ap_scan=1 is optimized work working with nl80211.
+# For finding networks using hidden SSID, scan_ssid=1 in the network block can
+# be used with nl80211.
+# When using IBSS or AP mode, ap_scan=2 mode can force the new network to be
+# created immediately regardless of scan results. ap_scan=1 mode will first try
+# to scan for existing networks and only if no matches with the enabled
+# networks are found, a new IBSS or AP mode network is created.
+ap_scan=1
+
+# MPM residency
+# By default, wpa_supplicant implements the mesh peering manager (MPM) for an
+# open mesh. However, if the driver can implement the MPM, you may set this to
+# 0 to use the driver version. When AMPE is enabled, the wpa_supplicant MPM is
+# always used.
+# 0: MPM lives in the driver
+# 1: wpa_supplicant provides an MPM which handles peering (default)
+#user_mpm=1
+
+# Maximum number of peer links (0-255; default: 99)
+# Maximum number of mesh peering currently maintained by the STA.
+#max_peer_links=99
+
+# Timeout in seconds to detect STA inactivity (default: 300 seconds)
+#
+# This timeout value is used in mesh STA to clean up inactive stations.
+#mesh_max_inactivity=300
+
+# cert_in_cb - Whether to include a peer certificate dump in events
+# This controls whether peer certificates for authentication server and
+# its certificate chain are included in EAP peer certificate events. This is
+# enabled by default.
+#cert_in_cb=1
+
+# EAP fast re-authentication
+# By default, fast re-authentication is enabled for all EAP methods that
+# support it. This variable can be used to disable fast re-authentication.
+# Normally, there is no need to disable this.
+fast_reauth=1
+
+# OpenSSL Engine support
+# These options can be used to load OpenSSL engines.
+# The two engines that are supported currently are shown below:
+# They are both from the opensc project (http://www.opensc.org/)
+# By default no engines are loaded.
+# make the opensc engine available
+#opensc_engine_path=/usr/lib/opensc/engine_opensc.so
+# make the pkcs11 engine available
+#pkcs11_engine_path=/usr/lib/opensc/engine_pkcs11.so
+# configure the path to the pkcs11 module required by the pkcs11 engine
+#pkcs11_module_path=/usr/lib/pkcs11/opensc-pkcs11.so
+
+# OpenSSL cipher string
+#
+# This is an OpenSSL specific configuration option for configuring the default
+# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default.
+# See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation
+# on cipher suite configuration. This is applicable only if wpa_supplicant is
+# built to use OpenSSL.
+#openssl_ciphers=DEFAULT:!EXP:!LOW
+
+
+# Dynamic EAP methods
+# If EAP methods were built dynamically as shared object files, they need to be
+# loaded here before being used in the network blocks. By default, EAP methods
+# are included statically in the build, so these lines are not needed
+#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_tls.so
+#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_md5.so
+
+# Driver interface parameters
+# This field can be used to configure arbitrary driver interace parameters. The
+# format is specific to the selected driver interface. This field is not used
+# in most cases.
+#driver_param="field=value"
+
+# Country code
+# The ISO/IEC alpha2 country code for the country in which this device is
+# currently operating.
+#country=US
+
+# Maximum lifetime for PMKSA in seconds; default 43200
+#dot11RSNAConfigPMKLifetime=43200
+# Threshold for reauthentication (percentage of PMK lifetime); default 70
+#dot11RSNAConfigPMKReauthThreshold=70
+# Timeout for security association negotiation in seconds; default 60
+#dot11RSNAConfigSATimeout=60
+
+# Wi-Fi Protected Setup (WPS) parameters
+
+# Universally Unique IDentifier (UUID; see RFC 4122) of the device
+# If not configured, UUID will be generated based on the local MAC address.
+#uuid=12345678-9abc-def0-1234-56789abcdef0
+
+# Device Name
+# User-friendly description of device; up to 32 octets encoded in UTF-8
+#device_name=Wireless Client
+
+# Manufacturer
+# The manufacturer of the device (up to 64 ASCII characters)
+#manufacturer=Company
+
+# Model Name
+# Model of the device (up to 32 ASCII characters)
+#model_name=cmodel
+
+# Model Number
+# Additional device description (up to 32 ASCII characters)
+#model_number=123
+
+# Serial Number
+# Serial number of the device (up to 32 characters)
+#serial_number=12345
+
+# Primary Device Type
+# Used format: <categ>-<OUI>-<subcateg>
+# categ = Category as an integer value
+# OUI = OUI and type octet as a 4-octet hex-encoded value; 0050F204 for
+#       default WPS OUI
+# subcateg = OUI-specific Sub Category as an integer value
+# Examples:
+#   1-0050F204-1 (Computer / PC)
+#   1-0050F204-2 (Computer / Server)
+#   5-0050F204-1 (Storage / NAS)
+#   6-0050F204-1 (Network Infrastructure / AP)
+#device_type=1-0050F204-1
+
+# OS Version
+# 4-octet operating system version number (hex string)
+#os_version=01020300
+
+# Config Methods
+# List of the supported configuration methods
+# Available methods: usba ethernet label display ext_nfc_token int_nfc_token
+#	nfc_interface push_button keypad virtual_display physical_display
+#	virtual_push_button physical_push_button
+# For WSC 1.0:
+#config_methods=label display push_button keypad
+# For WSC 2.0:
+#config_methods=label virtual_display virtual_push_button keypad
+
+# Credential processing
+#   0 = process received credentials internally (default)
+#   1 = do not process received credentials; just pass them over ctrl_iface to
+#	external program(s)
+#   2 = process received credentials internally and pass them over ctrl_iface
+#	to external program(s)
+#wps_cred_processing=0
+
+# Vendor attribute in WPS M1, e.g., Windows 7 Vertical Pairing
+# The vendor attribute contents to be added in M1 (hex string)
+#wps_vendor_ext_m1=000137100100020001
+
+# NFC password token for WPS
+# These parameters can be used to configure a fixed NFC password token for the
+# station. This can be generated, e.g., with nfc_pw_token. When these
+# parameters are used, the station is assumed to be deployed with a NFC tag
+# that includes the matching NFC password token (e.g., written based on the
+# NDEF record from nfc_pw_token).
+#
+#wps_nfc_dev_pw_id: Device Password ID (16..65535)
+#wps_nfc_dh_pubkey: Hexdump of DH Public Key
+#wps_nfc_dh_privkey: Hexdump of DH Private Key
+#wps_nfc_dev_pw: Hexdump of Device Password
+
+# Priority for the networks added through WPS
+# This priority value will be set to each network profile that is added
+# by executing the WPS protocol.
+#wps_priority=0
+
+# Maximum number of BSS entries to keep in memory
+# Default: 200
+# This can be used to limit memory use on the BSS entries (cached scan
+# results). A larger value may be needed in environments that have huge number
+# of APs when using ap_scan=1 mode.
+#bss_max_count=200
+
+# Automatic scan
+# 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=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>
+#autoscan=periodic:30
+# 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)
+# 1 = only include configured SSIDs in scan results/BSS table
+#filter_ssids=0
+
+# Password (and passphrase, etc.) backend for external storage
+# format: <backend name>[:<optional backend parameters>]
+#ext_password_backend=test:pw1=password|pw2=testing
+
+
+# Disable P2P functionality
+# p2p_disabled=1
+
+# Timeout in seconds to detect STA inactivity (default: 300 seconds)
+#
+# This timeout value is used in P2P GO mode to clean up
+# 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
+# with the global okc=1 parameter or with the per-network
+# proactive_key_caching=1 parameter. With okc=1, OKC is enabled by default, but
+# can be disabled with per-network proactive_key_caching=0 parameter.
+#okc=0
+
+# Protected Management Frames default
+# This parameter can be used to set the default behavior for the ieee80211w
+# parameter. By default, PMF is disabled unless enabled with the global pmf=1/2
+# parameter or with the per-network ieee80211w=1/2 parameter. With pmf=1/2, PMF
+# is enabled/required by default, but can be disabled with the per-network
+# ieee80211w parameter.
+#pmf=0
+
+# Enabled SAE finite cyclic groups in preference order
+# By default (if this parameter is not set), the mandatory group 19 (ECC group
+# defined over a 256-bit prime order field) is preferred, but other groups are
+# also enabled. If this parameter is set, the groups will be tried in the
+# indicated order. The group values are listed in the IANA registry:
+# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9
+#sae_groups=21 20 19 26 25
+
+# Default value for DTIM period (if not overridden in network block)
+#dtim_period=2
+
+# Default value for Beacon interval (if not overridden in network block)
+#beacon_int=100
+
+# Additional vendor specific elements for Beacon and Probe Response frames
+# This parameter can be used to add additional vendor specific element(s) into
+# the end of the Beacon and Probe Response frames. The format for these
+# element(s) is a hexdump of the raw information elements (id+len+payload for
+# one or more elements). This is used in AP and P2P GO modes.
+#ap_vendor_elements=dd0411223301
+
+# Ignore scan results older than request
+#
+# The driver may have a cache of scan results that makes it return
+# information that is older than our scan trigger. This parameter can
+# be used to configure such old information to be ignored instead of
+# allowing it to update the internal BSS table.
+#ignore_old_scan_res=0
+
+# scan_cur_freq: Whether to scan only the current frequency
+# 0:  Scan all available frequencies. (Default)
+# 1:  Scan current operating frequency if another VIF on the same radio
+#     is already associated.
+
+# MAC address policy default
+# 0 = use permanent MAC address
+# 1 = use random MAC address for each ESS connection
+# 2 = like 1, but maintain OUI (with local admin bit set)
+#
+# By default, permanent MAC address is used unless policy is changed by
+# the per-network mac_addr parameter. Global mac_addr=1 can be used to
+# change this default behavior.
+#mac_addr=0
+
+# Lifetime of random MAC address in seconds (default: 60)
+#rand_addr_lifetime=60
+
+# MAC address policy for pre-association operations (scanning, ANQP)
+# 0 = use permanent MAC address
+# 1 = use random MAC address
+# 2 = like 1, but maintain OUI (with local admin bit set)
+#preassoc_mac_addr=0
+
+# Interworking (IEEE 802.11u)
+
+# Enable Interworking
+# interworking=1
+
+# Homogenous ESS identifier
+# If this is set, scans will be used to request response only from BSSes
+# belonging to the specified Homogeneous ESS. This is used only if interworking
+# is enabled.
+# hessid=00:11:22:33:44:55
+
+# Automatic network selection behavior
+# 0 = do not automatically go through Interworking network selection
+#     (i.e., require explicit interworking_select command for this; default)
+# 1 = perform Interworking network selection if one or more
+#     credentials have been configured and scan did not find a
+#     matching network block
+#auto_interworking=0
+
+# credential block
+#
+# Each credential used for automatic network selection is configured as a set
+# of parameters that are compared to the information advertised by the APs when
+# interworking_select and interworking_connect commands are used.
+#
+# 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
+#	(and similarly in struct wpa_ssid for network blocks) to change the
+#	Interworking automatic networking selection behavior. The matching
+#	network (based on either an enabled network block or a credential)
+#	with the highest priority value will be selected.
+#
+# pcsc: Use PC/SC and SIM/USIM card
+#
+# realm: Home Realm for Interworking
+#
+# username: Username for Interworking network selection
+#
+# password: Password for Interworking network selection
+#
+# ca_cert: CA certificate for Interworking network selection
+#
+# client_cert: File path to client certificate file (PEM/DER)
+#	This field is used with Interworking networking selection for a case
+#	where client certificate/private key is used for authentication
+#	(EAP-TLS). Full path to the file should be used since working
+#	directory may change when wpa_supplicant is run in the background.
+#
+#	Alternatively, a named configuration blob can be used by setting
+#	this to blob://blob_name.
+#
+# private_key: File path to client private key file (PEM/DER/PFX)
+#	When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+#	commented out. Both the private key and certificate will be read
+#	from the PKCS#12 file in this case. Full path to the file should be
+#	used since working directory may change when wpa_supplicant is run
+#	in the background.
+#
+#	Windows certificate store can be used by leaving client_cert out and
+#	configuring private_key in one of the following formats:
+#
+#	cert://substring_to_match
+#
+#	hash://certificate_thumbprint_in_hex
+#
+#	For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+#
+#	Note that when running wpa_supplicant as an application, the user
+#	certificate store (My user account) is used, whereas computer store
+#	(Computer account) is used when running wpasvc as a service.
+#
+#	Alternatively, a named configuration blob can be used by setting
+#	this to blob://blob_name.
+#
+# private_key_passwd: Password for private key file
+#
+# imsi: IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+#
+# milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
+#	format
+#
+# 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. 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
+#	Roaming Consortium OI that can be used to determine which access
+#	points support authentication with this credential. This is an
+#	alternative to the use of the realm parameter. When using Roaming
+#	Consortium to match the network, the EAP parameters need to be
+#	pre-configured with the credential since the NAI Realm information
+#	may not be available or fetched.
+#
+# eap: Pre-configured EAP method
+#	This optional field can be used to specify which EAP method will be
+#	used with this credential. If not set, the EAP method is selected
+#	automatically based on ANQP information (e.g., NAI Realm).
+#
+# phase1: Pre-configure Phase 1 (outer authentication) parameters
+#	This optional field is used with like the 'eap' parameter.
+#
+# phase2: Pre-configure Phase 2 (inner authentication) parameters
+#	This optional field is used with like the 'eap' parameter.
+#
+# excluded_ssid: Excluded SSID
+#	This optional field can be used to excluded specific SSID(s) from
+#	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={
+#	realm="example.com"
+#	username="user@example.com"
+#	password="password"
+#	ca_cert="/etc/wpa_supplicant/ca.pem"
+#	domain="example.com"
+#}
+#
+#cred={
+#	imsi="310026-000000000"
+#	milenage="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82"
+#}
+#
+#cred={
+#	realm="example.com"
+#	username="user"
+#	password="password"
+#	ca_cert="/etc/wpa_supplicant/ca.pem"
+#	domain="example.com"
+#	roaming_consortium=223344
+#	eap=TTLS
+#	phase2="auth=MSCHAPV2"
+#}
+
+# Hotspot 2.0
+# hs20=1
+
+# network block
+#
+# Each network (usually AP's sharing the same SSID) is configured as a separate
+# block in this configuration file. The network blocks are in preference order
+# (the first match is used).
+#
+# network block fields:
+#
+# disabled:
+#	0 = this network can be used (default)
+#	1 = this network block is disabled (can be enabled through ctrl_iface,
+#	    e.g., with wpa_cli or wpa_gui)
+#
+# id_str: Network identifier string for external scripts. This value is passed
+#	to external action script through wpa_cli as WPA_ID_STR environment
+#	variable to make it easier to do network specific configuration.
+#
+# ssid: SSID (mandatory); network name in one of the optional formats:
+#	- an ASCII string with double quotation
+#	- a hex string (two characters per octet of SSID)
+#	- a printf-escaped ASCII string P"<escaped string>"
+#
+# scan_ssid:
+#	0 = do not scan this SSID with specific Probe Request frames (default)
+#	1 = scan with SSID-specific Probe Request frames (this can be used to
+#	    find APs that do not accept broadcast SSID or use multiple SSIDs;
+#	    this will add latency to scanning, so enable this only when needed)
+#
+# bssid: BSSID (optional); if set, this network block is used only when
+#	associating with the AP using the configured BSSID
+#
+# priority: priority group (integer)
+# By default, all networks will get same priority group (0). If some of the
+# networks are more desirable, this field can be used to change the order in
+# which wpa_supplicant goes through the networks when selecting a BSS. The
+# priority groups will be iterated in decreasing priority (i.e., the larger the
+# priority value, the sooner the network is matched against the scan results).
+# Within each priority group, networks will be selected based on security
+# policy, signal strength, etc.
+# Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are not
+# using this priority to select the order for scanning. Instead, they try the
+# networks in the order that used in the configuration file.
+#
+# mode: IEEE 802.11 operation mode
+# 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
+# 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.
+#
+# frequency: Channel frequency in megahertz (MHz) for IBSS, e.g.,
+# 2412 = IEEE 802.11b/g channel 1. This value is used to configure the initial
+# channel for IBSS (adhoc) networks. It is ignored in the infrastructure mode.
+# In addition, this value is only used by the station that creates the IBSS. If
+# an IBSS network with the configured SSID is already present, the frequency of
+# the network will be used instead of this configured value.
+#
+# scan_freq: List of frequencies to scan
+# Space-separated list of frequencies in MHz to scan when searching for this
+# BSS. If the subset of channels used by the network is known, this option can
+# be used to optimize scanning to not occur on channels that the network does
+# not use. Example: scan_freq=2412 2437 2462
+#
+# freq_list: Array of allowed frequencies
+# Space-separated list of frequencies in MHz to allow for selecting the BSS. If
+# set, scan results that do not match any of the specified frequencies are not
+# considered when selecting a BSS.
+#
+# This can also be set on the outside of the network block. In this case,
+# it limits the frequencies that will be scanned.
+#
+# bgscan: Background scanning
+# wpa_supplicant behavior for background scanning can be specified by
+# configuring a bgscan module. These modules are responsible for requesting
+# background scans for the purpose of roaming within an ESS (i.e., within a
+# single network block with all the APs using the same SSID). The bgscan
+# parameter uses following format: "<bgscan module name>:<module parameters>"
+# Following bgscan modules are available:
+# simple - Periodic background scans based on signal strength
+# bgscan="simple:<short bgscan interval in seconds>:<signal strength threshold>:
+# <long interval>"
+# bgscan="simple:30:-45:300"
+# learn - Learn channels used by the network and try to avoid bgscans on other
+# channels (experimental)
+# 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
+# RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN)
+# If not set, this defaults to: WPA RSN
+#
+# key_mgmt: list of accepted authenticated key management protocols
+# WPA-PSK = WPA pre-shared key (this requires 'psk' field)
+# WPA-EAP = WPA using EAP authentication
+# IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically
+#	generated WEP keys
+# NONE = WPA is not used; plaintext or static WEP could be used
+# WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms
+# WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms
+# If not set, this defaults to: WPA-PSK WPA-EAP
+#
+# ieee80211w: whether management frame protection is enabled
+# 0 = disabled (default unless changed with the global pmf parameter)
+# 1 = optional
+# 2 = required
+# The most common configuration options for this based on the PMF (protected
+# management frames) certification program are:
+# PMF enabled: ieee80211w=1 and key_mgmt=WPA-EAP WPA-EAP-SHA256
+# PMF required: ieee80211w=2 and key_mgmt=WPA-EAP-SHA256
+# (and similarly for WPA-PSK and WPA-WPSK-SHA256 if WPA2-Personal is used)
+#
+# auth_alg: list of allowed IEEE 802.11 authentication algorithms
+# OPEN = Open System authentication (required for WPA/WPA2)
+# SHARED = Shared Key authentication (requires static WEP keys)
+# LEAP = LEAP/Network EAP (only used with LEAP)
+# If not set, automatic selection is used (Open System with LEAP enabled if
+# LEAP is allowed as one of the EAP methods).
+#
+# pairwise: list of accepted pairwise (unicast) ciphers for WPA
+# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
+# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
+# NONE = Use only Group Keys (deprecated, should not be included if APs support
+#	pairwise keys)
+# If not set, this defaults to: CCMP TKIP
+#
+# group: list of accepted group (broadcast/multicast) ciphers for WPA
+# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
+# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
+# WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key
+# WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11]
+# If not set, this defaults to: CCMP TKIP WEP104 WEP40
+#
+# psk: WPA preshared key; 256-bit pre-shared key
+# The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e.,
+# 32 bytes or as an ASCII passphrase (in which case, the real PSK will be
+# generated using the passphrase and SSID). ASCII passphrase must be between
+# 8 and 63 characters (inclusive). ext:<name of external PSK field> format can
+# be used to indicate that the PSK/passphrase is stored in external storage.
+# This field is not needed, if WPA-EAP is used.
+# Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys
+# from ASCII passphrase. This process uses lot of CPU and wpa_supplicant
+# startup and reconfiguration time can be optimized by generating the PSK only
+# only when the passphrase or SSID has actually changed.
+#
+# mem_only_psk: Whether to keep PSK/passphrase only in memory
+# 0 = allow psk/passphrase to be stored to the configuration file
+# 1 = do not store psk/passphrase to the configuration file
+#mem_only_psk=0
+#
+# eapol_flags: IEEE 802.1X/EAPOL options (bit field)
+# Dynamic WEP key required for non-WPA mode
+# 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 (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
+# SSID, are allowed when selecting a BSS from scan results.
+# 0 = disabled (default)
+# 1 = enabled
+#
+# proactive_key_caching:
+# Enable/disable opportunistic PMKSA caching for WPA2.
+# 0 = disabled (default unless changed with the global okc parameter)
+# 1 = enabled
+#
+# wep_key0..3: Static WEP key (ASCII in double quotation, e.g. "abcde" or
+# hex without quotation, e.g., 0102030405)
+# wep_tx_keyidx: Default WEP key index (TX) (0..3)
+#
+# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e DLS) is
+# allowed. This is only used with RSN/WPA2.
+# 0 = disabled (default)
+# 1 = enabled
+#peerkey=1
+#
+# wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to
+# enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies.
+#
+# Following fields are only used with internal EAP implementation.
+# eap: space-separated list of accepted EAP methods
+#	MD5 = EAP-MD5 (unsecure and does not generate keying material ->
+#			cannot be used with WPA; to be used as a Phase 2 method
+#			with EAP-PEAP or EAP-TTLS)
+#       MSCHAPV2 = EAP-MSCHAPv2 (cannot be used separately with WPA; to be used
+#		as a Phase 2 method with EAP-PEAP or EAP-TTLS)
+#       OTP = EAP-OTP (cannot be used separately with WPA; to be used
+#		as a Phase 2 method with EAP-PEAP or EAP-TTLS)
+#       GTC = EAP-GTC (cannot be used separately with WPA; to be used
+#		as a Phase 2 method with EAP-PEAP or EAP-TTLS)
+#	TLS = EAP-TLS (client and server certificate)
+#	PEAP = EAP-PEAP (with tunnelled EAP authentication)
+#	TTLS = EAP-TTLS (with tunnelled EAP or PAP/CHAP/MSCHAP/MSCHAPV2
+#			 authentication)
+#	If not set, all compiled in methods are allowed.
+#
+# identity: Identity string for EAP
+#	This field is also used to configure user NAI for
+#	EAP-PSK/PAX/SAKE/GPSK.
+# anonymous_identity: Anonymous identity string for EAP (to be used as the
+#	unencrypted identity with EAP types that support different tunnelled
+#	identity, e.g., EAP-TTLS). This field can also be used with
+#	EAP-SIM/AKA/AKA' to store the pseudonym identity.
+# password: Password string for EAP. This field can include either the
+#	plaintext password (using ASCII or hex string) or a NtPasswordHash
+#	(16-byte MD4 hash of password) in hash:<32 hex digits> format.
+#	NtPasswordHash can only be used when the password is for MSCHAPv2 or
+#	MSCHAP (EAP-MSCHAPv2, EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
+#	EAP-PSK (128-bit PSK), EAP-PAX (128-bit PSK), and EAP-SAKE (256-bit
+#	PSK) is also configured using this field. For EAP-GPSK, this is a
+#	variable length PSK. ext:<name of external password field> format can
+#	be used to indicate that the password is stored in external storage.
+# ca_cert: File path to CA certificate file (PEM/DER). This file can have one
+#	or more trusted CA certificates. If ca_cert and ca_path are not
+#	included, server certificate will not be verified. This is insecure and
+#	a trusted CA certificate should always be configured when using
+#	EAP-TLS/TTLS/PEAP. Full path should be used since working directory may
+#	change when wpa_supplicant is run in the background.
+#
+#	Alternatively, this can be used to only perform matching of the server
+#	certificate (SHA-256 hash of the DER encoded X.509 certificate). In
+#	this case, the possible CA certificates in the server certificate chain
+#	are ignored and only the server certificate is verified. This is
+#	configured with the following format:
+#	hash:://server/sha256/cert_hash_in_hex
+#	For example: "hash://server/sha256/
+#	5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a"
+#
+#	On Windows, trusted CA certificates can be loaded from the system
+#	certificate store by setting this to cert_store://<name>, e.g.,
+#	ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT".
+#	Note that when running wpa_supplicant as an application, the user
+#	certificate store (My user account) is used, whereas computer store
+#	(Computer account) is used when running wpasvc as a service.
+# ca_path: Directory path for CA certificate files (PEM). This path may
+#	contain multiple CA certificates in OpenSSL format. Common use for this
+#	is to point to system trusted CA list which is often installed into
+#	directory like /etc/ssl/certs. If configured, these certificates are
+#	added to the list of trusted CAs. ca_cert may also be included in that
+#	case, but it is not required.
+# client_cert: File path to client certificate file (PEM/DER)
+#	Full path should be used since working directory may change when
+#	wpa_supplicant is run in the background.
+#	Alternatively, a named configuration blob can be used by setting this
+#	to blob://<blob name>.
+# private_key: File path to client private key file (PEM/DER/PFX)
+#	When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+#	commented out. Both the private key and certificate will be read from
+#	the PKCS#12 file in this case. Full path should be used since working
+#	directory may change when wpa_supplicant is run in the background.
+#	Windows certificate store can be used by leaving client_cert out and
+#	configuring private_key in one of the following formats:
+#	cert://substring_to_match
+#	hash://certificate_thumbprint_in_hex
+#	for example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+#	Note that when running wpa_supplicant as an application, the user
+#	certificate store (My user account) is used, whereas computer store
+#	(Computer account) is used when running wpasvc as a service.
+#	Alternatively, a named configuration blob can be used by setting this
+#	to blob://<blob name>.
+# private_key_passwd: Password for private key file (if left out, this will be
+#	asked through control interface)
+# dh_file: File path to DH/DSA parameters file (in PEM format)
+#	This is an optional configuration file for setting parameters for an
+#	ephemeral DH key exchange. In most cases, the default RSA
+#	authentication does not use this configuration. However, it is possible
+#	setup RSA to use ephemeral DH key exchange. In addition, ciphers with
+#	DSA keys always use ephemeral DH keys. This can be used to achieve
+#	forward secrecy. If the file is in DSA parameters format, it will be
+#	automatically converted into DH params.
+# subject_match: Substring to be matched against the subject of the
+#	authentication server certificate. If this string is set, the server
+#	sertificate is only accepted if it contains this string in the subject.
+#	The subject string is in following format:
+#	/C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com
+#	Note: Since this is a substring match, this cannot be used securily to
+#	do a suffix match against a possible domain name in the CN entry. For
+#	such a use case, domain_suffix_match or domain_match should be used
+#	instead.
+# altsubject_match: Semicolon separated string of entries to be matched against
+#	the alternative subject name of the authentication server certificate.
+#	If this string is set, the server sertificate is only accepted if it
+#	contains one of the entries in an alternative subject name extension.
+#	altSubjectName string is in following format: TYPE:VALUE
+#	Example: EMAIL:server@example.com
+#	Example: DNS:server.example.com;DNS:server2.example.com
+#	Following types are supported: EMAIL, DNS, URI
+# domain_suffix_match: Constraint for server domain name. If set, this FQDN is
+#	used as a suffix match requirement for the AAAserver 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 SubjectName 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_match: Constraint for server domain name
+#	If set, this FQDN is used as a full 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 SubjectName CN
+#	using same full match comparison. This behavior is similar to
+#	domain_suffix_match, but has the requirement of a full match, i.e.,
+#	no subdomains or wildcard matches are allowed. Case-insensitive
+#	comparison is used, so "Example.com" matches "example.com", but would
+#	not match "test.Example.com".
+# phase1: Phase1 (outer authentication, i.e., TLS tunnel) parameters
+#	(string with field-value pairs, e.g., "peapver=0" or
+#	"peapver=1 peaplabel=1")
+#	'peapver' can be used to force which PEAP version (0 or 1) is used.
+#	'peaplabel=1' can be used to force new label, "client PEAP encryption",
+#	to be used during key derivation when PEAPv1 or newer. Most existing
+#	PEAPv1 implementation seem to be using the old label, "client EAP
+#	encryption", and wpa_supplicant is now using that as the default value.
+#	Some servers, e.g., Radiator, may require peaplabel=1 configuration to
+#	interoperate with PEAPv1; see eap_testing.txt for more details.
+#	'peap_outer_success=0' can be used to terminate PEAP authentication on
+#	tunneled EAP-Success. This is required with some RADIUS servers that
+#	implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g.,
+#	Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode)
+#	include_tls_length=1 can be used to force wpa_supplicant to include
+#	TLS Message Length field in all TLS messages even if they are not
+#	fragmented.
+#	sim_min_num_chal=3 can be used to configure EAP-SIM to require three
+#	challenges (by default, it accepts 2 or 3)
+#	result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use
+#	protected result indication.
+#	'crypto_binding' option can be used to control PEAPv0 cryptobinding
+#	behavior:
+#	 * 0 = do not use cryptobinding (default)
+#	 * 1 = use cryptobinding if server supports it
+#	 * 2 = require cryptobinding
+#	EAP-WSC (WPS) uses following options: pin=<Device Password> or
+#	pbc=1.
+#
+#	For wired IEEE 802.1X authentication, "allow_canned_success=1" can be
+#	used to configure a mode that allows EAP-Success (and EAP-Failure)
+#	without going through authentication step. Some switches use such
+#	sequence when forcing the port to be authorized/unauthorized or as a
+#	fallback option if the authentication server is unreachable. By default,
+#	wpa_supplicant discards such frames to protect against potential attacks
+#	by rogue devices, but this option can be used to disable that protection
+#	for cases where the server/authenticator does not need to be
+#	authenticated.
+# phase2: Phase2 (inner authentication with TLS tunnel) parameters
+#	(string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
+#	"autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS). "mschapv2_retry=0" can be
+#	used to disable MSCHAPv2 password retry in authentication failure cases.
+#
+# TLS-based methods can use the following parameters to control TLS behavior
+# (these are normally in the phase1 parameter, but can be used also in the
+# phase2 parameter when EAP-TLS is used within the inner tunnel):
+# tls_allow_md5=1 - allow MD5-based certificate signatures (depending on the
+#	TLS library, these may be disabled by default to enforce stronger
+#	security)
+# tls_disable_time_checks=1 - ignore certificate validity time (this requests
+#	the TLS library to accept certificates even if they are not currently
+#	valid, i.e., have expired or have not yet become valid; this should be
+#	used only for testing purposes)
+# tls_disable_session_ticket=1 - disable TLS Session Ticket extension
+# tls_disable_session_ticket=0 - allow TLS Session Ticket extension to be used
+#	Note: If not set, this is automatically set to 1 for EAP-TLS/PEAP/TTLS
+#	as a workaround for broken authentication server implementations unless
+#	EAP workarounds are disabled with eap_workaround=0.
+#	For EAP-FAST, this must be set to 0 (or left unconfigured for the
+#	default value to be used automatically).
+# tls_disable_tlsv1_0=1 - disable use of TLSv1.0
+# 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.
+# ca_cert2: File path to CA certificate file. This file can have one or more
+#	trusted CA certificates. If ca_cert2 and ca_path2 are not included,
+#	server certificate will not be verified. This is insecure and a trusted
+#	CA certificate should always be configured.
+# ca_path2: Directory path for CA certificate files (PEM)
+# client_cert2: File path to client certificate file
+# private_key2: File path to client private key file
+# private_key2_passwd: Password for private key file
+# dh_file2: File path to DH/DSA parameters file (in PEM format)
+# subject_match2: Substring to be matched against the subject of the
+#	authentication server certificate. See subject_match for more details.
+# altsubject_match2: Semicolon separated string of entries to be matched
+#	against the alternative subject name of the authentication server
+#	certificate. See altsubject_match documentation for more details.
+# domain_suffix_match2: Constraint for server domain name. See
+#	domain_suffix_match for more details.
+#
+# fragment_size: Maximum EAP fragment size in bytes (default 1398).
+#	This value limits the fragment size for EAP methods that support
+#	fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set
+#	small enough to make the EAP messages fit in MTU of the network
+#	interface used for EAPOL. The default value is suitable for most
+#	cases.
+#
+# 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
+#
+# openssl_ciphers: OpenSSL specific cipher configuration
+#	This can be used to override the global openssl_ciphers configuration
+#	parameter (see above).
+#
+# erp: Whether EAP Re-authentication Protocol (ERP) is enabled
+#
+# EAP-FAST variables:
+# pac_file: File path for the PAC entries. wpa_supplicant will need to be able
+#	to create this file and write updates to it when PAC is being
+#	provisioned or refreshed. Full path to the file should be used since
+#	working directory may change when wpa_supplicant is run in the
+#	background. Alternatively, a named configuration blob can be used by
+#	setting this to blob://<blob name>
+# phase1: fast_provisioning option can be used to enable in-line provisioning
+#         of EAP-FAST credentials (PAC):
+#         0 = disabled,
+#         1 = allow unauthenticated provisioning,
+#         2 = allow authenticated provisioning,
+#         3 = allow both unauthenticated and authenticated provisioning
+#	fast_max_pac_list_len=<num> option can be used to set the maximum
+#		number of PAC entries to store in a PAC list (default: 10)
+#	fast_pac_format=binary option can be used to select binary format for
+#		storing PAC entries in order to save some space (the default
+#		text format uses about 2.5 times the size of minimal binary
+#		format)
+#
+# wpa_supplicant supports number of "EAP workarounds" to work around
+# interoperability issues with incorrectly behaving authentication servers.
+# These are enabled by default because some of the issues are present in large
+# number of authentication servers. Strict EAP conformance mode can be
+# configured by disabling workarounds with eap_workaround=0.
+
+# Station inactivity limit
+#
+# If a station does not send anything in ap_max_inactivity seconds, an
+# empty data frame is sent to it in order to verify whether it is
+# still in range. If this frame is not ACKed, the station will be
+# disassociated and then deauthenticated. This feature is used to
+# clear station table of old entries when the STAs move out of the
+# range.
+#
+# The station can associate again with the AP if it is still in range;
+# this inactivity poll is just used as a nicer way of verifying
+# inactivity; i.e., client will not report broken connection because
+# disassociation frame is not sent immediately without first polling
+# the STA with a data frame.
+# default: 300 (i.e., 5 minutes)
+#ap_max_inactivity=300
+
+# DTIM period in Beacon intervals for AP mode (default: 2)
+#dtim_period=2
+
+# Beacon interval (default: 100 TU)
+#beacon_int=100
+
+# MAC address policy
+# 0 = use permanent MAC address
+# 1 = use random MAC address for each ESS connection
+# 2 = like 1, but maintain OUI (with local admin bit set)
+#mac_addr=0
+
+# disable_ht: Whether HT (802.11n) should be disabled.
+# 0 = HT enabled (if AP supports it)
+# 1 = HT disabled
+#
+# disable_ht40: Whether HT-40 (802.11n) should be disabled.
+# 0 = HT-40 enabled (if AP supports it)
+# 1 = HT-40 disabled
+#
+# disable_sgi: Whether SGI (short guard interval) should be disabled.
+# 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)
+# ht_mcs="0xff 00 00 00 00 00 00 00 00 00 "   // Use MCS 0-7 only
+# ht_mcs="0xff ff 00 00 00 00 00 00 00 00 "   // Use MCS 0-15 only
+#
+# disable_max_amsdu:  Whether MAX_AMSDU should be disabled.
+# -1 = Do not make any changes.
+# 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.
+# 0-3 = Set AMPDU density (aka factor) to specified value.
+
+# disable_vht: Whether VHT should be disabled.
+# 0 = VHT enabled (if AP supports it)
+# 1 = VHT disabled
+#
+# vht_capa: VHT capabilities to set in the override
+# vht_capa_mask: mask of VHT capabilities
+#
+# vht_rx_mcs_nss_1/2/3/4/5/6/7/8: override the MCS set for RX NSS 1-8
+# vht_tx_mcs_nss_1/2/3/4/5/6/7/8: override the MCS set for TX NSS 1-8
+#  0: MCS 0-7
+#  1: MCS 0-8
+#  2: MCS 0-9
+#  3: not supported
+
+##### Fast Session Transfer (FST) support #####################################
+#
+# The options in this section are only available when the build configuration
+# option CONFIG_FST is set while compiling hostapd. They allow this interface
+# to be a part of FST setup.
+#
+# FST is the transfer of a session from a channel to another channel, in the
+# same or different frequency bands.
+#
+# For detals, see IEEE Std 802.11ad-2012.
+
+# Identifier of an FST Group  the interface belongs to.
+#fst_group_id=bond0
+
+# Interface priority within the FST Group.
+# Announcing a higher priority for an interface means declaring it more
+# preferable for FST switch.
+# fst_priority is in 1..255 range with 1 being the lowest priority.
+#fst_priority=100
+
+# Default LLT value for this interface in milliseconds. The value used in case
+# no value provided during session setup. Default is 50 msec.
+# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2
+# Transitioning between states).
+#fst_llt=100
+
+# Example blocks:
+
+# Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers
+network={
+	ssid="simple"
+	psk="very secret passphrase"
+	priority=5
+}
+
+# Same as previous, but request SSID-specific scanning (for APs that reject
+# broadcast SSID)
+network={
+	ssid="second ssid"
+	scan_ssid=1
+	psk="very secret passphrase"
+	priority=2
+}
+
+# Only WPA-PSK is used. Any valid cipher combination is accepted.
+network={
+	ssid="example"
+	proto=WPA
+	key_mgmt=WPA-PSK
+	pairwise=CCMP TKIP
+	group=CCMP TKIP WEP104 WEP40
+	psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb
+	priority=2
+}
+
+# WPA-Personal(PSK) with TKIP and enforcement for frequent PTK rekeying
+network={
+	ssid="example"
+	proto=WPA
+	key_mgmt=WPA-PSK
+	pairwise=TKIP
+	group=TKIP
+	psk="not so secure passphrase"
+	wpa_ptk_rekey=600
+}
+
+# Only WPA-EAP is used. Both CCMP and TKIP is accepted. An AP that used WEP104
+# or WEP40 as the group cipher will not be accepted.
+network={
+	ssid="example"
+	proto=RSN
+	key_mgmt=WPA-EAP
+	pairwise=CCMP TKIP
+	group=CCMP TKIP
+	eap=TLS
+	identity="user@example.com"
+	ca_cert="/etc/cert/ca.pem"
+	client_cert="/etc/cert/user.pem"
+	private_key="/etc/cert/user.prv"
+	private_key_passwd="password"
+	priority=1
+}
+
+# EAP-PEAP/MSCHAPv2 configuration for RADIUS servers that use the new peaplabel
+# (e.g., Radiator)
+network={
+	ssid="example"
+	key_mgmt=WPA-EAP
+	eap=PEAP
+	identity="user@example.com"
+	password="foobar"
+	ca_cert="/etc/cert/ca.pem"
+	phase1="peaplabel=1"
+	phase2="auth=MSCHAPV2"
+	priority=10
+}
+
+# EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the
+# unencrypted use. Real identity is sent only within an encrypted TLS tunnel.
+network={
+	ssid="example"
+	key_mgmt=WPA-EAP
+	eap=TTLS
+	identity="user@example.com"
+	anonymous_identity="anonymous@example.com"
+	password="foobar"
+	ca_cert="/etc/cert/ca.pem"
+	priority=2
+}
+
+# EAP-TTLS/MSCHAPv2 configuration with anonymous identity for the unencrypted
+# use. Real identity is sent only within an encrypted TLS tunnel.
+network={
+	ssid="example"
+	key_mgmt=WPA-EAP
+	eap=TTLS
+	identity="user@example.com"
+	anonymous_identity="anonymous@example.com"
+	password="foobar"
+	ca_cert="/etc/cert/ca.pem"
+	phase2="auth=MSCHAPV2"
+}
+
+# WPA-EAP, EAP-TTLS with different CA certificate used for outer and inner
+# authentication.
+network={
+	ssid="example"
+	key_mgmt=WPA-EAP
+	eap=TTLS
+	# Phase1 / outer authentication
+	anonymous_identity="anonymous@example.com"
+	ca_cert="/etc/cert/ca.pem"
+	# Phase 2 / inner authentication
+	phase2="autheap=TLS"
+	ca_cert2="/etc/cert/ca2.pem"
+	client_cert2="/etc/cer/user.pem"
+	private_key2="/etc/cer/user.prv"
+	private_key2_passwd="password"
+	priority=2
+}
+
+# Both WPA-PSK and WPA-EAP is accepted. Only CCMP is accepted as pairwise and
+# group cipher.
+network={
+	ssid="example"
+	bssid=00:11:22:33:44:55
+	proto=WPA RSN
+	key_mgmt=WPA-PSK WPA-EAP
+	pairwise=CCMP
+	group=CCMP
+	psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb
+}
+
+# Special characters in SSID, so use hex string. Default to WPA-PSK, WPA-EAP
+# and all valid ciphers.
+network={
+	ssid=00010203
+	psk=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
+}
+
+
+# EAP-SIM with a GSM SIM or USIM
+network={
+	ssid="eap-sim-test"
+	key_mgmt=WPA-EAP
+	eap=SIM
+	pin="1234"
+	pcsc=""
+}
+
+
+# EAP-PSK
+network={
+	ssid="eap-psk-test"
+	key_mgmt=WPA-EAP
+	eap=PSK
+	anonymous_identity="eap_psk_user"
+	password=06b4be19da289f475aa46a33cb793029
+	identity="eap_psk_user@example.com"
+}
+
+
+# IEEE 802.1X/EAPOL with dynamically generated WEP keys (i.e., no WPA) using
+# EAP-TLS for authentication and key generation; require both unicast and
+# broadcast WEP keys.
+network={
+	ssid="1x-test"
+	key_mgmt=IEEE8021X
+	eap=TLS
+	identity="user@example.com"
+	ca_cert="/etc/cert/ca.pem"
+	client_cert="/etc/cert/user.pem"
+	private_key="/etc/cert/user.prv"
+	private_key_passwd="password"
+	eapol_flags=3
+}
+
+
+# LEAP with dynamic WEP keys
+network={
+	ssid="leap-example"
+	key_mgmt=IEEE8021X
+	eap=LEAP
+	identity="user"
+	password="foobar"
+}
+
+# EAP-IKEv2 using shared secrets for both server and peer authentication
+network={
+	ssid="ikev2-example"
+	key_mgmt=WPA-EAP
+	eap=IKEV2
+	identity="user"
+	password="foobar"
+}
+
+# EAP-FAST with WPA (WPA or WPA2)
+network={
+	ssid="eap-fast-test"
+	key_mgmt=WPA-EAP
+	eap=FAST
+	anonymous_identity="FAST-000102030405"
+	identity="username"
+	password="password"
+	phase1="fast_provisioning=1"
+	pac_file="/etc/wpa_supplicant.eap-fast-pac"
+}
+
+network={
+	ssid="eap-fast-test"
+	key_mgmt=WPA-EAP
+	eap=FAST
+	anonymous_identity="FAST-000102030405"
+	identity="username"
+	password="password"
+	phase1="fast_provisioning=1"
+	pac_file="blob://eap-fast-pac"
+}
+
+# Plaintext connection (no WPA, no IEEE 802.1X)
+network={
+	ssid="plaintext-test"
+	key_mgmt=NONE
+}
+
+
+# Shared WEP key connection (no WPA, no IEEE 802.1X)
+network={
+	ssid="static-wep-test"
+	key_mgmt=NONE
+	wep_key0="abcde"
+	wep_key1=0102030405
+	wep_key2="1234567890123"
+	wep_tx_keyidx=0
+	priority=5
+}
+
+
+# Shared WEP key connection (no WPA, no IEEE 802.1X) using Shared Key
+# IEEE 802.11 authentication
+network={
+	ssid="static-wep-test2"
+	key_mgmt=NONE
+	wep_key0="abcde"
+	wep_key1=0102030405
+	wep_key2="1234567890123"
+	wep_tx_keyidx=0
+	priority=5
+	auth_alg=SHARED
+}
+
+
+# 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
+	frequency=2412
+	proto=WPA
+	key_mgmt=WPA-NONE
+	pairwise=NONE
+	group=TKIP
+	psk="secret passphrase"
+}
+
+# open mesh network
+network={
+	ssid="test mesh"
+	mode=5
+	frequency=2437
+	key_mgmt=NONE
+}
+
+# secure (SAE + AMPE) network
+network={
+	ssid="secure mesh"
+	mode=5
+	frequency=2437
+	key_mgmt=SAE
+	psk="very secret passphrase"
+}
+
+
+# Catch all example that allows more or less all configuration modes
+network={
+	ssid="example"
+	scan_ssid=1
+	key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
+	pairwise=CCMP TKIP
+	group=CCMP TKIP WEP104 WEP40
+	psk="very secret passphrase"
+	eap=TTLS PEAP TLS
+	identity="user@example.com"
+	password="foobar"
+	ca_cert="/etc/cert/ca.pem"
+	client_cert="/etc/cert/user.pem"
+	private_key="/etc/cert/user.prv"
+	private_key_passwd="password"
+	phase1="peaplabel=0"
+}
+
+# Example of EAP-TLS with smartcard (openssl engine)
+network={
+	ssid="example"
+	key_mgmt=WPA-EAP
+	eap=TLS
+	proto=RSN
+	pairwise=CCMP TKIP
+	group=CCMP TKIP
+	identity="user@example.com"
+	ca_cert="/etc/cert/ca.pem"
+	client_cert="/etc/cert/user.pem"
+
+	engine=1
+
+	# The engine configured here must be available. Look at
+	# OpenSSL engine support in the global section.
+	# The key available through the engine must be the private key
+	# matching the client certificate configured above.
+
+	# use the opensc engine
+	#engine_id="opensc"
+	#key_id="45"
+
+	# use the pkcs11 engine
+	engine_id="pkcs11"
+	key_id="id_45"
+
+	# Optional PIN configuration; this can be left out and PIN will be
+	# asked through the control interface
+	pin="1234"
+}
+
+# Example configuration showing how to use an inlined blob as a CA certificate
+# data instead of using external file
+network={
+	ssid="example"
+	key_mgmt=WPA-EAP
+	eap=TTLS
+	identity="user@example.com"
+	anonymous_identity="anonymous@example.com"
+	password="foobar"
+	ca_cert="blob://exampleblob"
+	priority=20
+}
+
+blob-base64-exampleblob={
+SGVsbG8gV29ybGQhCg==
+}
+
+
+# Wildcard match for SSID (plaintext APs only). This example select any
+# open AP regardless of its SSID.
+network={
+	key_mgmt=NONE
+}
+
+# Example configuration blacklisting two APs - these will be ignored
+# for this network.
+network={
+	ssid="example"
+	psk="very secret passphrase"
+	bssid_blacklist=02:11:22:33:44:55 02:22:aa:44:55:66
+}
+
+# Example configuration limiting AP selection to a specific set of APs;
+# any other AP not matching the masked address will be ignored.
+network={
+	ssid="example"
+	psk="very secret passphrase"
+	bssid_whitelist=02:55:ae:bc:00:00/ff:ff:ff:ff:00:00 00:00:77:66:55:44/00:00:ff:ff:ff:ff
+}
+
+# Example config file that will only scan on channel 36.
+freq_list=5180
+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/hostap/wpa_supplicant/wpa_supplicant_conf.mk b/hostap/wpa_supplicant/wpa_supplicant_conf.mk
new file mode 100644
index 0000000..74986ea
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_supplicant_conf.mk
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+#
+
+# Include this makefile to generate your hardware specific wpa_supplicant.conf
+# Requires: WIFI_DRIVER_SOCKET_IFACE
+
+LOCAL_PATH := $(call my-dir)
+
+########################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := wpa_supplicant.conf
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/wifi
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+WPA_SUPPLICANT_CONF_TEMPLATE := $(LOCAL_PATH)/wpa_supplicant_template.conf
+WPA_SUPPLICANT_CONF_SCRIPT := $(LOCAL_PATH)/wpa_supplicant_conf.sh
+$(LOCAL_BUILT_MODULE): PRIVATE_WIFI_DRIVER_SOCKET_IFACE := $(WIFI_DRIVER_SOCKET_IFACE)
+$(LOCAL_BUILT_MODULE): PRIVATE_WPA_SUPPLICANT_CONF_TEMPLATE := $(WPA_SUPPLICANT_CONF_TEMPLATE)
+$(LOCAL_BUILT_MODULE): PRIVATE_WPA_SUPPLICANT_CONF_SCRIPT := $(WPA_SUPPLICANT_CONF_SCRIPT)
+$(LOCAL_BUILT_MODULE) : $(WPA_SUPPLICANT_CONF_TEMPLATE) $(WPA_SUPPLICANT_CONF_SCRIPT)
+	@echo Target wpa_supplicant.conf: $@
+	@mkdir -p $(dir $@)
+	$(hide) WIFI_DRIVER_SOCKET_IFACE="$(PRIVATE_WIFI_DRIVER_SOCKET_IFACE)" \
+		bash $(PRIVATE_WPA_SUPPLICANT_CONF_SCRIPT) $(PRIVATE_WPA_SUPPLICANT_CONF_TEMPLATE) > $@
+
+########################
diff --git a/hostap/wpa_supplicant/wpa_supplicant_conf.sh b/hostap/wpa_supplicant/wpa_supplicant_conf.sh
new file mode 100755
index 0000000..f36eef1
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_supplicant_conf.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+#
+
+# Generate a wpa_supplicant.conf from the template.
+# $1: the template file name
+if [ -n "$WIFI_DRIVER_SOCKET_IFACE" ]
+then
+  sed -e 's/#.*$//' -e 's/[ \t]*$//' -e '/^$/d' < $1 | sed -e "s/wlan0/$WIFI_DRIVER_SOCKET_IFACE/"
+else
+  sed -e 's/#.*$//' -e 's/[ \t]*$//' -e '/^$/d' < $1
+fi
diff --git a/hostap/wpa_supplicant/wpa_supplicant_i.h b/hostap/wpa_supplicant/wpa_supplicant_i.h
new file mode 100644
index 0000000..653b91e
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_supplicant_i.h
@@ -0,0 +1,1173 @@
+/*
+ * wpa_supplicant - Internal definitions
+ * 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.
+ */
+
+#ifndef WPA_SUPPLICANT_I_H
+#define WPA_SUPPLICANT_I_H
+
+#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"
+#include "wmm_ac.h"
+
+extern const char *const wpa_supplicant_version;
+extern const char *const wpa_supplicant_license;
+#ifndef CONFIG_NO_STDOUT_DEBUG
+extern const char *const wpa_supplicant_full_license1;
+extern const char *const wpa_supplicant_full_license2;
+extern const char *const wpa_supplicant_full_license3;
+extern const char *const wpa_supplicant_full_license4;
+extern const char *const wpa_supplicant_full_license5;
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+struct wpa_sm;
+struct wpa_supplicant;
+struct ibss_rsn;
+struct scan_info;
+struct wpa_bss;
+struct wpa_scan_results;
+struct hostapd_hw_modes;
+struct wpa_driver_associate_params;
+
+/*
+ * Forward declarations of private structures used within the ctrl_iface
+ * backends. Other parts of wpa_supplicant do not have access to data stored in
+ * these structures.
+ */
+struct ctrl_iface_priv;
+struct ctrl_iface_global_priv;
+struct wpas_dbus_priv;
+
+/**
+ * struct wpa_interface - Parameters for wpa_supplicant_add_iface()
+ */
+struct wpa_interface {
+	/**
+	 * confname - Configuration name (file or profile) name
+	 *
+	 * This can also be %NULL when a configuration file is not used. In
+	 * that case, ctrl_interface must be set to allow the interface to be
+	 * configured.
+	 */
+	const char *confname;
+
+	/**
+	 * confanother - Additional configuration name (file or profile) name
+	 *
+	 * This can also be %NULL when the additional configuration file is not
+	 * used.
+	 */
+	const char *confanother;
+
+	/**
+	 * ctrl_interface - Control interface parameter
+	 *
+	 * If a configuration file is not used, this variable can be used to
+	 * set the ctrl_interface parameter that would have otherwise been read
+	 * from the configuration file. If both confname and ctrl_interface are
+	 * set, ctrl_interface is used to override the value from configuration
+	 * file.
+	 */
+	const char *ctrl_interface;
+
+	/**
+	 * driver - Driver interface name, or %NULL to use the default driver
+	 */
+	const char *driver;
+
+	/**
+	 * driver_param - Driver interface parameters
+	 *
+	 * If a configuration file is not used, this variable can be used to
+	 * set the driver_param parameters that would have otherwise been read
+	 * from the configuration file. If both confname and driver_param are
+	 * set, driver_param is used to override the value from configuration
+	 * file.
+	 */
+	const char *driver_param;
+
+	/**
+	 * ifname - Interface name
+	 */
+	const char *ifname;
+
+	/**
+	 * bridge_ifname - Optional bridge interface name
+	 *
+	 * If the driver interface (ifname) is included in a Linux bridge
+	 * device, the bridge interface may need to be used for receiving EAPOL
+	 * frames. This can be enabled by setting this variable to enable
+	 * receiving of EAPOL frames from an additional interface.
+	 */
+	const char *bridge_ifname;
+
+	/**
+	 * p2p_mgmt - Interface used for P2P management (P2P Device operations)
+	 *
+	 * Indicates whether wpas_p2p_init() must be called for this interface.
+	 * This is used only when the driver supports a dedicated P2P Device
+	 * interface that is not a network interface.
+	 */
+	int p2p_mgmt;
+};
+
+/**
+ * struct wpa_params - Parameters for wpa_supplicant_init()
+ */
+struct wpa_params {
+	/**
+	 * daemonize - Run %wpa_supplicant in the background
+	 */
+	int daemonize;
+
+	/**
+	 * wait_for_monitor - Wait for a monitor program before starting
+	 */
+	int wait_for_monitor;
+
+	/**
+	 * pid_file - Path to a PID (process ID) file
+	 *
+	 * If this and daemonize are set, process ID of the background process
+	 * will be written to the specified file.
+	 */
+	char *pid_file;
+
+	/**
+	 * wpa_debug_level - Debugging verbosity level (e.g., MSG_INFO)
+	 */
+	int wpa_debug_level;
+
+	/**
+	 * wpa_debug_show_keys - Whether keying material is included in debug
+	 *
+	 * This parameter can be used to allow keying material to be included
+	 * in debug messages. This is a security risk and this option should
+	 * not be enabled in normal configuration. If needed during
+	 * development or while troubleshooting, this option can provide more
+	 * details for figuring out what is happening.
+	 */
+	int wpa_debug_show_keys;
+
+	/**
+	 * wpa_debug_timestamp - Whether to include timestamp in debug messages
+	 */
+	int wpa_debug_timestamp;
+
+	/**
+	 * ctrl_interface - Global ctrl_iface path/parameter
+	 */
+	char *ctrl_interface;
+
+	/**
+	 * ctrl_interface_group - Global ctrl_iface group
+	 */
+	char *ctrl_interface_group;
+
+	/**
+	 * dbus_ctrl_interface - Enable the DBus control interface
+	 */
+	int dbus_ctrl_interface;
+
+	/**
+	 * wpa_debug_file_path - Path of debug file or %NULL to use stdout
+	 */
+	const char *wpa_debug_file_path;
+
+	/**
+	 * wpa_debug_syslog - Enable log output through syslog
+	 */
+	int wpa_debug_syslog;
+
+	/**
+	 * wpa_debug_tracing - Enable log output through Linux tracing
+	 */
+	int wpa_debug_tracing;
+
+	/**
+	 * override_driver - Optional driver parameter override
+	 *
+	 * This parameter can be used to override the driver parameter in
+	 * dynamic interface addition to force a specific driver wrapper to be
+	 * used instead.
+	 */
+	char *override_driver;
+
+	/**
+	 * override_ctrl_interface - Optional ctrl_interface override
+	 *
+	 * This parameter can be used to override the ctrl_interface parameter
+	 * in dynamic interface addition to force a control interface to be
+	 * created.
+	 */
+	char *override_ctrl_interface;
+
+	/**
+	 * entropy_file - Optional entropy file
+	 *
+	 * This parameter can be used to configure wpa_supplicant to maintain
+	 * its internal entropy store over restarts.
+	 */
+	char *entropy_file;
+
+#ifdef CONFIG_P2P
+	/**
+	 * conf_p2p_dev - 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.
+	 */
+	char *conf_p2p_dev;
+#endif /* CONFIG_P2P */
+
+};
+
+struct p2p_srv_bonjour {
+	struct dl_list list;
+	struct wpabuf *query;
+	struct wpabuf *resp;
+};
+
+struct p2p_srv_upnp {
+	struct dl_list list;
+	u8 version;
+	char *service;
+};
+
+/**
+ * struct wpa_global - Internal, global data for all %wpa_supplicant interfaces
+ *
+ * This structure is initialized by calling wpa_supplicant_init() when starting
+ * %wpa_supplicant.
+ */
+struct wpa_global {
+	struct wpa_supplicant *ifaces;
+	struct wpa_params params;
+	struct ctrl_iface_global_priv *ctrl_iface;
+	struct wpas_dbus_priv *dbus;
+	void **drv_priv;
+	size_t drv_count;
+	struct os_time suspend_time;
+	struct p2p_data *p2p;
+	struct wpa_supplicant *p2p_init_wpa_s;
+	struct wpa_supplicant *p2p_group_formation;
+	struct wpa_supplicant *p2p_invite_group;
+	u8 p2p_dev_addr[ETH_ALEN];
+	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_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_per_sta_psk:1;
+	unsigned int p2p_fail_on_wps_complete:1;
+	unsigned int p2p_24ghz_social_channels:1;
+	unsigned int pending_p2ps_group:1;
+	unsigned int pending_group_iface_for_p2ps:1;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	int wifi_display;
+#define MAX_WFD_SUBELEMS 10
+	struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS];
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	struct psk_list_entry *add_psk; /* From group formation */
+};
+
+
+/**
+ * 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 */
+	unsigned int external_scan_running:1;
+	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);
+struct wpa_radio_work *
+radio_work_pending(struct wpa_supplicant *wpa_s, const char *type);
+
+struct wpa_connect_work {
+	unsigned int sme:1;
+	unsigned int bss_removed: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 {
+	OFFCHANNEL_SEND_ACTION_SUCCESS /**< Frame was send and acknowledged */,
+	OFFCHANNEL_SEND_ACTION_NO_ACK /**< Frame was sent, but not acknowledged
+				       */,
+	OFFCHANNEL_SEND_ACTION_FAILED /**< Frame was not sent due to a failure
+				       */
+};
+
+struct wps_ap_info {
+	u8 bssid[ETH_ALEN];
+	enum wps_ap_info_type {
+		WPS_AP_NOT_SEL_REG,
+		WPS_AP_SEL_REG,
+		WPS_AP_SEL_REG_OUR
+	} type;
+	unsigned int tries;
+	struct os_reltime last_attempt;
+	unsigned int pbc_active;
+	u8 uuid[WPS_UUID_LEN];
+};
+
+struct wpa_ssid_value {
+	u8 ssid[SSID_MAX_LEN];
+	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;
+};
+
+#define RRM_NEIGHBOR_REPORT_TIMEOUT 1 /* 1 second for AP to send a report */
+
+/*
+ * struct rrm_data - Data used for managing RRM features
+ */
+struct rrm_data {
+	/* rrm_used - indication regarding the current connection */
+	unsigned int rrm_used:1;
+
+	/*
+	 * notify_neighbor_rep - Callback for notifying report requester
+	 */
+	void (*notify_neighbor_rep)(void *ctx, struct wpabuf *neighbor_rep);
+
+	/*
+	 * neighbor_rep_cb_ctx - Callback context
+	 * Received in the callback registration, and sent to the callback
+	 * function as a parameter.
+	 */
+	void *neighbor_rep_cb_ctx;
+
+	/* next_neighbor_rep_token - Next request's dialog token */
+	u8 next_neighbor_rep_token;
+};
+
+enum wpa_supplicant_test_failure {
+	WPAS_TEST_FAILURE_NONE,
+	WPAS_TEST_FAILURE_SCAN_TRIGGER,
+};
+
+/**
+ * struct wpa_supplicant - Internal data for wpa_supplicant interface
+ *
+ * This structure contains the internal data for core wpa_supplicant code. This
+ * should be only used directly from the core code. However, a pointer to this
+ * data is used from other files as an arbitrary context pointer in calls to
+ * core functions.
+ */
+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;
+	struct l2_packet_data *l2_br;
+	unsigned char own_addr[ETH_ALEN];
+	unsigned char perm_addr[ETH_ALEN];
+	char ifname[100];
+#ifdef CONFIG_CTRL_IFACE_DBUS
+	char *dbus_path;
+#endif /* CONFIG_CTRL_IFACE_DBUS */
+#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+	char *dbus_new_path;
+	char *dbus_groupobj_path;
+#ifdef CONFIG_AP
+	char *preq_notify_peer;
+#endif /* CONFIG_AP */
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+	char bridge_ifname[16];
+
+	char *confname;
+	char *confanother;
+
+	struct wpa_config *conf;
+	int countermeasures;
+	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. */
+	int reassociate; /* reassociation requested */
+	int reassoc_same_bss; /* reassociating to the same bss */
+	int disconnected; /* all connections disabled; i.e., do no reassociate
+			   * before this has been cleared */
+	struct wpa_ssid *current_ssid;
+	struct wpa_ssid *last_ssid;
+	struct wpa_bss *current_bss;
+	int ap_ies_from_associnfo;
+	unsigned int assoc_freq;
+
+	/* Selected configuration (based on Beacon/ProbeResp WPA IE) */
+	int pairwise_cipher;
+	int group_cipher;
+	int key_mgmt;
+	int wpa_proto;
+	int mgmt_group_cipher;
+
+	void *drv_priv; /* private data used by driver_ops */
+	void *global_drv_priv;
+
+	u8 *bssid_filter;
+	size_t bssid_filter_count;
+
+	u8 *disallow_aps_bssid;
+	size_t disallow_aps_bssid_count;
+	struct wpa_ssid_value *disallow_aps_ssid;
+	size_t disallow_aps_ssid_count;
+
+	enum set_band 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;
+	struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID;
+					  * NULL = not yet initialized (start
+					  * with wildcard SSID)
+					  * WILDCARD_SSID_SCAN = wildcard
+					  * SSID was used in the previous scan
+					  */
+#define WILDCARD_SSID_SCAN ((struct wpa_ssid *) 1)
+
+	struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */
+	int sched_scan_timeout;
+	int sched_scan_interval;
+	int first_sched_scan;
+	int sched_scan_timed_out;
+
+	void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
+				 struct wpa_scan_results *scan_res);
+	struct dl_list bss; /* struct wpa_bss::list */
+	struct dl_list bss_id; /* struct wpa_bss::list_id */
+	size_t num_bss;
+	unsigned int bss_update_idx;
+	unsigned int bss_next_id;
+
+	 /*
+	  * Pointers to BSS entries in the order they were in the last scan
+	  * results.
+	  */
+	struct wpa_bss **last_scan_res;
+	unsigned int last_scan_res_used;
+	unsigned int last_scan_res_size;
+	struct os_reltime last_scan;
+
+	const struct wpa_driver_ops *driver;
+	int interface_removed; /* whether the network interface has been
+				* removed */
+	struct wpa_sm *wpa;
+	struct eapol_sm *eapol;
+
+	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;
+
+	int eapol_received; /* number of EAPOL packets received after the
+			     * previous association event */
+
+	struct scard_data *scard;
+	char imsi[20];
+	int mnc_len;
+
+	unsigned char last_eapol_src[ETH_ALEN];
+
+	unsigned int keys_cleared; /* bitfield of key indexes that the driver is
+				    * known not to be configured with a key */
+
+	struct wpa_blacklist *blacklist;
+
+	/**
+	 * extra_blacklist_count - Sum of blacklist counts after last connection
+	 *
+	 * This variable is used to maintain a count of temporary blacklisting
+	 * failures (maximum number for any BSS) over blacklist clear
+	 * operations. This is needed for figuring out whether there has been
+	 * failures prior to the last blacklist clear operation which happens
+	 * whenever no other not-blacklisted BSS candidates are available. This
+	 * gets cleared whenever a connection has been established successfully.
+	 */
+	int extra_blacklist_count;
+
+	/**
+	 * scan_req - Type of the scan request
+	 */
+	enum scan_req_type {
+		/**
+		 * NORMAL_SCAN_REQ - Normal scan request
+		 *
+		 * This is used for scans initiated by wpa_supplicant to find an
+		 * AP for a connection.
+		 */
+		NORMAL_SCAN_REQ,
+
+		/**
+		 * INITIAL_SCAN_REQ - Initial scan request
+		 *
+		 * This is used for the first scan on an interface to force at
+		 * least one scan to be run even if the configuration does not
+		 * include any enabled networks.
+		 */
+		INITIAL_SCAN_REQ,
+
+		/**
+		 * MANUAL_SCAN_REQ - Manual scan request
+		 *
+		 * This is used for scans where the user request a scan or
+		 * a specific wpa_supplicant operation (e.g., WPS) requires scan
+		 * to be run.
+		 */
+		MANUAL_SCAN_REQ
+	} scan_req, last_scan_req;
+	enum wpa_states scan_prev_wpa_state;
+	struct os_reltime scan_trigger_time, scan_start_time;
+	/* Minimum freshness requirement for connection purposes */
+	struct os_reltime scan_min_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 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;
+
+	struct wpa_ssid_value *ssids_from_scan_req;
+	unsigned int num_ssids_from_scan_req;
+
+	u64 drv_flags;
+	unsigned int drv_enc;
+	unsigned int drv_smps_modes;
+	unsigned int drv_rrm_flags;
+
+	/*
+	 * A bitmap of supported protocols for probe response offload. See
+	 * struct wpa_driver_capa in driver.h
+	 */
+	unsigned int probe_resp_offloads;
+
+	/* extended capabilities supported by the driver */
+	const u8 *extended_capa, *extended_capa_mask;
+	unsigned int extended_capa_len;
+
+	int max_scan_ssids;
+	int max_sched_scan_ssids;
+	int sched_scan_supported;
+	unsigned int max_match_sets;
+	unsigned int max_remain_on_chan;
+	unsigned int max_stations;
+
+	int pending_mic_error_report;
+	int pending_mic_error_pairwise;
+	int mic_errors_seen; /* Michael MIC errors with the current PTK */
+
+	struct wps_context *wps;
+	int wps_success; /* WPS success event received */
+	struct wps_er *wps_er;
+	unsigned int wps_run;
+	struct os_reltime wps_pin_start_time;
+	int blacklist_cleared;
+
+	struct wpabuf *pending_eapol_rx;
+	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 */
+	unsigned int mac_addr_changed:1;
+	unsigned int added_vif:1;
+
+	struct os_reltime last_mac_addr_change;
+	int last_mac_addr_style;
+
+	struct ibss_rsn *ibss_rsn;
+
+	int set_sta_uapsd;
+	int sta_uapsd;
+	int set_ap_uapsd;
+	int ap_uapsd;
+
+#ifdef CONFIG_SME
+	struct {
+		u8 ssid[SSID_MAX_LEN];
+		size_t ssid_len;
+		int freq;
+		u8 assoc_req_ie[200];
+		size_t assoc_req_ie_len;
+		int mfp;
+		int ft_used;
+		u8 mobility_domain[2];
+		u8 *ft_ies;
+		size_t ft_ies_len;
+		u8 prev_bssid[ETH_ALEN];
+		int prev_bssid_set;
+		int auth_alg;
+		int proto;
+
+		int sa_query_count; /* number of pending SA Query requests;
+				     * 0 = no SA Query in progress */
+		int sa_query_timed_out;
+		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_reltime sa_query_start;
+		struct os_reltime last_unprot_disconnect;
+		enum { HT_SEC_CHAN_UNKNOWN,
+		       HT_SEC_CHAN_ABOVE,
+		       HT_SEC_CHAN_BELOW } ht_sec_chan;
+		u8 sched_obss_scan;
+		u16 obss_scan_int;
+		u16 bss_max_idle_period;
+#ifdef CONFIG_SAE
+		struct sae_data sae;
+		struct wpabuf *sae_token;
+		int sae_group_index;
+		unsigned int sae_pmksa_caching:1;
+#endif /* CONFIG_SAE */
+	} sme;
+#endif /* CONFIG_SME */
+
+#ifdef CONFIG_AP
+	struct hostapd_iface *ap_iface;
+	void (*ap_configured_cb)(void *ctx, void *data);
+	void *ap_configured_cb_ctx;
+	void *ap_configured_cb_data;
+#endif /* CONFIG_AP */
+
+	struct hostapd_iface *ifmsh;
+#ifdef CONFIG_MESH
+	struct mesh_rsn *mesh_rsn;
+	int mesh_if_idx;
+	unsigned int mesh_if_created:1;
+	unsigned int mesh_ht_enabled:1;
+	int mesh_auth_block_duration; /* sec */
+#endif /* CONFIG_MESH */
+
+	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;
+	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,
+					    const u8 *data, size_t data_len,
+					    enum offchannel_send_action_result
+					    result);
+	unsigned int roc_waiting_drv_freq;
+	int action_tx_wait_time;
+
+	int p2p_mgmt;
+
+#ifdef CONFIG_P2P
+	struct p2p_go_neg_results *go_params;
+	int create_p2p_iface;
+	u8 pending_interface_addr[ETH_ALEN];
+	char pending_interface_name[100];
+	int pending_interface_type;
+	int p2p_group_idx;
+	unsigned int pending_listen_freq;
+	unsigned int pending_listen_duration;
+	enum {
+		NOT_P2P_GROUP_INTERFACE,
+		P2P_GROUP_INTERFACE_PENDING,
+		P2P_GROUP_INTERFACE_GO,
+		P2P_GROUP_INTERFACE_CLIENT
+	} p2p_group_interface;
+	struct p2p_group *p2p_group;
+	int p2p_long_listen; /* remaining time in long Listen state in ms */
+	char p2p_pin[10];
+	int p2p_wps_method;
+	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];
+	int pending_pd_before_join;
+	u8 pending_join_iface_addr[ETH_ALEN];
+	u8 pending_join_dev_addr[ETH_ALEN];
+	int pending_join_wps_method;
+	u8 p2p_join_ssid[SSID_MAX_LEN];
+	size_t p2p_join_ssid_len;
+	int p2p_join_scan_count;
+	int auto_pd_scan_retry;
+	int force_long_sd;
+	u16 pending_pd_config_methods;
+	enum {
+		NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN, AUTO_PD_ASP
+	} pending_pd_use;
+
+	/*
+	 * Whether cross connection is disallowed by the AP to which this
+	 * interface is associated (only valid if there is an association).
+	 */
+	int cross_connect_disallowed;
+
+	/*
+	 * Whether this P2P group is configured to use cross connection (only
+	 * valid if this is P2P GO interface). The actual cross connect packet
+	 * forwarding may not be configured depending on the uplink status.
+	 */
+	int cross_connect_enabled;
+
+	/* Whether cross connection forwarding is in use at the moment. */
+	int cross_connect_in_use;
+
+	/*
+	 * Uplink interface name for cross connection
+	 */
+	char cross_connect_uplink[100];
+
+	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 group_formation_reported: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;
+	unsigned int p2ps_method_config_any:1;
+	unsigned int p2p_cli_probe:1;
+	int p2p_persistent_go_freq;
+	int p2p_persistent_id;
+	int p2p_go_intent;
+	int p2p_connect_freq;
+	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];
+
+	/* group common frequencies */
+	int *p2p_group_common_freqs;
+	unsigned int p2p_group_common_freqs_num;
+	u8 p2ps_join_addr[ETH_ALEN];
+#endif /* CONFIG_P2P */
+
+	struct wpa_ssid *bgscan_ssid;
+	const struct bgscan_ops *bgscan;
+	void *bgscan_priv;
+
+	const struct autoscan_ops *autoscan;
+	struct wpa_driver_scan_params *autoscan_params;
+	void *autoscan_priv;
+
+	struct wpa_ssid *connect_without_scan;
+
+	struct wps_ap_info *wps_ap;
+	size_t num_wps_ap;
+	int wps_ap_iter;
+
+	int after_wps;
+	int known_wps_freq;
+	unsigned int wps_freq;
+	int wps_fragment_size;
+	int auto_reconnect_disabled;
+
+	 /* Channel preferences for AP/P2P GO use */
+	int best_24_freq;
+	int best_5_freq;
+	int best_overall_freq;
+
+	struct gas_query *gas;
+
+#ifdef CONFIG_INTERWORKING
+	unsigned int fetch_anqp_in_progress:1;
+	unsigned int network_select:1;
+	unsigned int auto_select:1;
+	unsigned int auto_network_select:1;
+	unsigned int interworking_fast_assoc_tried:1;
+	unsigned int fetch_all_anqp:1;
+	unsigned int fetch_osu_info:1;
+	unsigned int fetch_osu_waiting_scan:1;
+	unsigned int fetch_osu_icon_in_progress:1;
+	struct wpa_bss *interworking_gas_bss;
+	unsigned int osu_icon_id;
+	struct 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;
+
+	struct {
+		struct hostapd_hw_modes *modes;
+		u16 num_modes;
+		u16 flags;
+	} hw;
+	enum local_hw_capab {
+		CAPAB_NO_HT_VHT,
+		CAPAB_HT,
+		CAPAB_HT40,
+		CAPAB_VHT,
+	} hw_capab;
+#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;
+
+	/* WLAN_STATUS_* status codes from (Re)Association Response frame. */
+	int assoc_status_code;
+
+	struct ext_password_data *ext_pw;
+
+	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;
+	unsigned int ext_eapol_frame_io:1;
+	unsigned int wmm_ac_supported:1;
+	unsigned int ext_work_in_progress:1;
+	unsigned int own_disconnect_req:1;
+
+#define MAC_ADDR_RAND_SCAN       BIT(0)
+#define MAC_ADDR_RAND_SCHED_SCAN BIT(1)
+#define MAC_ADDR_RAND_PNO        BIT(2)
+#define MAC_ADDR_RAND_ALL        (MAC_ADDR_RAND_SCAN | \
+				  MAC_ADDR_RAND_SCHED_SCAN | \
+				  MAC_ADDR_RAND_PNO)
+	unsigned int mac_addr_rand_supported;
+	unsigned int mac_addr_rand_enable;
+
+	/* MAC Address followed by mask (2 * ETH_ALEN) */
+	u8 *mac_addr_scan;
+	u8 *mac_addr_sched_scan;
+	u8 *mac_addr_pno;
+
+#ifdef CONFIG_WNM
+	u8 wnm_dialog_token;
+	u8 wnm_reply;
+	u8 wnm_num_neighbor_report;
+	u8 wnm_mode;
+	u16 wnm_dissoc_timer;
+	u8 wnm_bss_termination_duration[12];
+	struct neighbor_report *wnm_neighbor_report_elements;
+	struct os_reltime wnm_cand_valid_until;
+	u8 wnm_cand_from_bss[ETH_ALEN];
+#endif /* CONFIG_WNM */
+
+#ifdef CONFIG_TESTING_GET_GTK
+	u8 last_gtk[32];
+	size_t last_gtk_len;
+#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];
+
+#ifdef CONFIG_TESTING_OPTIONS
+	struct l2_packet_data *l2_test;
+	unsigned int extra_roc_dur;
+	enum wpa_supplicant_test_failure test_failure;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	struct wmm_ac_assoc_data *wmm_ac_assoc_info;
+	struct wmm_tspec_element *tspecs[WMM_AC_NUM][TS_DIR_IDX_COUNT];
+	struct wmm_ac_addts_request *addts_request;
+	u8 wmm_ac_last_dialog_token;
+	struct wmm_tspec_element *last_tspecs;
+	u8 last_tspecs_count;
+
+	struct rrm_data rrm;
+
+#ifdef CONFIG_FST
+	struct fst_iface *fst;
+	const struct wpabuf *fst_ies;
+	struct wpabuf *received_mb_ies;
+#endif /* CONFIG_FST */
+};
+
+
+/* wpa_supplicant.c */
+void wpa_supplicant_apply_ht_overrides(
+	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+	struct wpa_driver_associate_params *params);
+void wpa_supplicant_apply_vht_overrides(
+	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+	struct wpa_driver_associate_params *params);
+
+int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
+				    struct wpa_ssid *ssid);
+
+int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s);
+
+const char * wpa_supplicant_state_txt(enum wpa_states state);
+int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
+			      struct wpa_bss *bss, struct wpa_ssid *ssid,
+			      u8 *wpa_ie, size_t *wpa_ie_len);
+void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
+			      struct wpa_bss *bss,
+			      struct wpa_ssid *ssid);
+void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
+				       struct wpa_ssid *ssid);
+void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s);
+void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr);
+void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
+				     int sec, int usec);
+void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
+			      enum wpa_states state);
+struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s);
+const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
+				   int reason_code);
+
+void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
+				   struct wpa_ssid *ssid);
+void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
+				    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,
+					  unsigned int expire_age);
+int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s,
+					    unsigned int expire_count);
+int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s,
+				     int scan_interval);
+int wpa_supplicant_set_debug_params(struct wpa_global *global,
+				    int debug_level, int debug_timestamp,
+				    int debug_show_keys);
+void free_hw_features(struct wpa_supplicant *wpa_s);
+
+void wpa_show_license(void);
+
+struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
+						 struct wpa_interface *iface,
+						 struct wpa_supplicant *parent);
+int wpa_supplicant_remove_iface(struct wpa_global *global,
+				struct wpa_supplicant *wpa_s,
+				int terminate);
+struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global,
+						 const char *ifname);
+struct wpa_global * wpa_supplicant_init(struct wpa_params *params);
+int wpa_supplicant_run(struct wpa_global *global);
+void wpa_supplicant_deinit(struct wpa_global *global);
+
+int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid);
+void wpa_supplicant_terminate_proc(struct wpa_global *global);
+void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
+			     const u8 *buf, size_t len);
+void wpa_supplicant_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, 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, size_t buflen);
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style);
+int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s);
+void add_freq(int *freqs, int *num_freqs, int freq);
+
+void wpas_rrm_reset(struct wpa_supplicant *wpa_s);
+void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
+				   const u8 *report, size_t report_len);
+int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
+				       const struct wpa_ssid *ssid,
+				       void (*cb)(void *ctx,
+						  struct wpabuf *neighbor_rep),
+				       void *cb_ctx);
+void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
+					      const u8 *src,
+					      const u8 *frame, size_t len,
+					      int rssi);
+
+/**
+ * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @ssid: Pointer to the network block the reply is for
+ * @field: field the response is a reply for
+ * @value: value (ie, password, etc) for @field
+ * Returns: 0 on success, non-zero on error
+ *
+ * Helper function to handle replies to control interface requests.
+ */
+int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
+					      struct wpa_ssid *ssid,
+					      const char *field,
+					      const char *value);
+
+void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+			  const struct wpa_ssid *ssid,
+			  struct hostapd_freq_params *freq);
+
+/* events.c */
+void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
+			   struct wpa_bss *selected,
+			   struct wpa_ssid *ssid);
+void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx);
+void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx);
+void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s);
+struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
+					     struct wpa_ssid **selected_ssid);
+
+/* eap_register.c */
+int eap_register_methods(void);
+
+/**
+ * Utility method to tell if a given network is for persistent group storage
+ * @ssid: Network object
+ * Returns: 1 if network is a persistent group, 0 otherwise
+ */
+static inline int network_is_persistent_group(struct wpa_ssid *ssid)
+{
+	return ssid->disabled == 2 && ssid->p2p_persistent_group;
+}
+
+int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+
+int 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);
+
+void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx);
+
+#ifdef CONFIG_FST
+
+struct fst_wpa_obj;
+
+void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
+				       struct fst_wpa_obj *iface_obj);
+
+#endif /* CONFIG_FST */
+
+#endif /* WPA_SUPPLICANT_I_H */
diff --git a/hostap/wpa_supplicant/wpa_supplicant_template.conf b/hostap/wpa_supplicant/wpa_supplicant_template.conf
new file mode 100644
index 0000000..f3f2a64
--- /dev/null
+++ b/hostap/wpa_supplicant/wpa_supplicant_template.conf
@@ -0,0 +1,6 @@
+##### wpa_supplicant configuration file template #####
+update_config=1
+eapol_version=1
+ap_scan=1
+fast_reauth=1
+pmf=1
diff --git a/hostap/wpa_supplicant/wpas_glue.c b/hostap/wpa_supplicant/wpas_glue.c
new file mode 100644
index 0000000..29c22ba
--- /dev/null
+++ b/hostap/wpa_supplicant/wpas_glue.c
@@ -0,0 +1,1129 @@
+/*
+ * WPA Supplicant - Glue code to setup EAPOL and RSN modules
+ * Copyright (c) 2003-2015, 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 "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "eloop.h"
+#include "config.h"
+#include "l2_packet/l2_packet.h"
+#include "common/wpa_common.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "sme.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "wpas_glue.h"
+#include "wps_supplicant.h"
+#include "bss.h"
+#include "scan.h"
+#include "notify.h"
+#include "wpas_kay.h"
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+#if defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA)
+static void wpa_supplicant_set_config_blob(void *ctx,
+					   struct wpa_config_blob *blob)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	wpa_config_set_blob(wpa_s->conf, blob);
+	if (wpa_s->conf->update_config) {
+		int ret = wpa_config_write(wpa_s->confname, wpa_s->conf);
+		if (ret) {
+			wpa_printf(MSG_DEBUG, "Failed to update config after "
+				   "blob set");
+		}
+	}
+}
+
+
+static const struct wpa_config_blob *
+wpa_supplicant_get_config_blob(void *ctx, const char *name)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	return wpa_config_get_blob(wpa_s->conf, name);
+}
+#endif /* defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA) */
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+#if defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA)
+static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type,
+			    const void *data, u16 data_len,
+			    size_t *msg_len, void **data_pos)
+{
+	struct ieee802_1x_hdr *hdr;
+
+	*msg_len = sizeof(*hdr) + data_len;
+	hdr = os_malloc(*msg_len);
+	if (hdr == NULL)
+		return NULL;
+
+	hdr->version = wpa_s->conf->eapol_version;
+	hdr->type = type;
+	hdr->length = host_to_be16(data_len);
+
+	if (data)
+		os_memcpy(hdr + 1, data, data_len);
+	else
+		os_memset(hdr + 1, 0, data_len);
+
+	if (data_pos)
+		*data_pos = hdr + 1;
+
+	return (u8 *) hdr;
+}
+
+
+/**
+ * wpa_ether_send - Send Ethernet frame
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @dest: Destination MAC address
+ * @proto: Ethertype in host byte order
+ * @buf: Frame payload starting from IEEE 802.1X header
+ * @len: Frame payload length
+ * Returns: >=0 on success, <0 on failure
+ */
+static int wpa_ether_send(struct wpa_supplicant *wpa_s, const u8 *dest,
+			  u16 proto, const u8 *buf, size_t len)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->ext_eapol_frame_io && proto == ETH_P_EAPOL) {
+		size_t hex_len = 2 * len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex == NULL)
+			return -1;
+		wpa_snprintf_hex(hex, hex_len, buf, len);
+		wpa_msg(wpa_s, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+			MAC2STR(dest), hex);
+		os_free(hex);
+		return 0;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (wpa_s->l2) {
+		return l2_packet_send(wpa_s->l2, dest, proto, buf, len);
+	}
+
+	return -1;
+}
+#endif /* IEEE8021X_EAPOL || !CONFIG_NO_WPA */
+
+
+#ifdef IEEE8021X_EAPOL
+
+/**
+ * wpa_supplicant_eapol_send - Send IEEE 802.1X EAPOL packet to Authenticator
+ * @ctx: Pointer to wpa_supplicant data (wpa_s)
+ * @type: IEEE 802.1X packet type (IEEE802_1X_TYPE_*)
+ * @buf: EAPOL payload (after IEEE 802.1X header)
+ * @len: EAPOL payload length
+ * Returns: >=0 on success, <0 on failure
+ *
+ * This function adds Ethernet and IEEE 802.1X header and sends the EAPOL frame
+ * to the current Authenticator.
+ */
+static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf,
+				     size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	u8 *msg, *dst, bssid[ETH_ALEN];
+	size_t msglen;
+	int res;
+
+	/* TODO: could add l2_packet_sendmsg that allows fragments to avoid
+	 * extra copy here */
+
+	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
+		/* Current SSID is not using IEEE 802.1X/EAP, so drop possible
+		 * EAPOL frames (mainly, EAPOL-Start) from EAPOL state
+		 * machines. */
+		wpa_printf(MSG_DEBUG, "WPA: drop TX EAPOL in non-IEEE 802.1X "
+			   "mode (type=%d len=%lu)", type,
+			   (unsigned long) len);
+		return -1;
+	}
+
+	if (pmksa_cache_get_current(wpa_s->wpa) &&
+	    type == IEEE802_1X_TYPE_EAPOL_START) {
+		/*
+		 * We were trying to use PMKSA caching and sending EAPOL-Start
+		 * would abort that and trigger full EAPOL authentication.
+		 * However, we've already waited for the AP/Authenticator to
+		 * start 4-way handshake or EAP authentication, and apparently
+		 * it has not done so since the startWhen timer has reached zero
+		 * to get the state machine sending EAPOL-Start. This is not
+		 * really supposed to happen, but an interoperability issue with
+		 * a deployed AP has been identified where the connection fails
+		 * due to that AP failing to operate correctly if PMKID is
+		 * included in the Association Request frame. To work around
+		 * this, assume PMKSA caching failed and try to initiate full
+		 * EAP authentication.
+		 */
+		if (!wpa_s->current_ssid ||
+		    wpa_s->current_ssid->eap_workaround) {
+			wpa_printf(MSG_DEBUG,
+				   "RSN: Timeout on waiting for the AP to initiate 4-way handshake for PMKSA caching or EAP authentication - try to force it to start EAP authentication");
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "RSN: PMKSA caching - do not send EAPOL-Start");
+			return -1;
+		}
+	}
+
+	if (is_zero_ether_addr(wpa_s->bssid)) {
+		wpa_printf(MSG_DEBUG, "BSSID not set when trying to send an "
+			   "EAPOL frame");
+		if (wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+		    !is_zero_ether_addr(bssid)) {
+			dst = bssid;
+			wpa_printf(MSG_DEBUG, "Using current BSSID " MACSTR
+				   " from the driver as the EAPOL destination",
+				   MAC2STR(dst));
+		} else {
+			dst = wpa_s->last_eapol_src;
+			wpa_printf(MSG_DEBUG, "Using the source address of the"
+				   " last received EAPOL frame " MACSTR " as "
+				   "the EAPOL destination",
+				   MAC2STR(dst));
+		}
+	} else {
+		/* BSSID was already set (from (Re)Assoc event, so use it as
+		 * the EAPOL destination. */
+		dst = wpa_s->bssid;
+	}
+
+	msg = wpa_alloc_eapol(wpa_s, type, buf, len, &msglen, NULL);
+	if (msg == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "TX EAPOL: dst=" MACSTR, MAC2STR(dst));
+	wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", msg, msglen);
+	res = wpa_ether_send(wpa_s, dst, ETH_P_EAPOL, msg, msglen);
+	os_free(msg);
+	return res;
+}
+
+
+/**
+ * wpa_eapol_set_wep_key - set WEP key for the driver
+ * @ctx: Pointer to wpa_supplicant data (wpa_s)
+ * @unicast: 1 = individual unicast key, 0 = broadcast key
+ * @keyidx: WEP key index (0..3)
+ * @key: Pointer to key data
+ * @keylen: Key length in bytes
+ * Returns: 0 on success or < 0 on error.
+ */
+static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx,
+				 const u8 *key, size_t keylen)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+		int cipher = (keylen == 5) ? WPA_CIPHER_WEP40 :
+			WPA_CIPHER_WEP104;
+		if (unicast)
+			wpa_s->pairwise_cipher = cipher;
+		else
+			wpa_s->group_cipher = cipher;
+	}
+	return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
+			       unicast ? wpa_s->bssid : NULL,
+			       keyidx, unicast, NULL, 0, key, keylen);
+}
+
+
+static void wpa_supplicant_aborted_cached(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	wpa_sm_aborted_cached(wpa_s->wpa);
+}
+
+
+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 - result=%s",
+		   result_str(result));
+
+	if (wpas_wps_eapol_cb(wpa_s) > 0)
+		return;
+
+	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 (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))
+		return;
+
+	wpa_printf(MSG_DEBUG, "Configure PMK for driver-based RSN 4-way "
+		   "handshake");
+
+	pmk_len = PMK_LEN;
+	if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
+#ifdef CONFIG_IEEE80211R
+		u8 buf[2 * PMK_LEN];
+		wpa_printf(MSG_DEBUG, "RSN: Use FT XXKey as PMK for "
+			   "driver-based 4-way hs and FT");
+		res = eapol_sm_get_key(eapol, buf, 2 * PMK_LEN);
+		if (res == 0) {
+			os_memcpy(pmk, buf + PMK_LEN, PMK_LEN);
+			os_memset(buf, 0, sizeof(buf));
+		}
+#else /* CONFIG_IEEE80211R */
+		res = -1;
+#endif /* CONFIG_IEEE80211R */
+	} else {
+		res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
+		if (res) {
+			/*
+			 * EAP-LEAP is an exception from other EAP methods: it
+			 * uses only 16-byte PMK.
+			 */
+			res = eapol_sm_get_key(eapol, pmk, 16);
+			pmk_len = 16;
+		}
+	}
+
+	if (res) {
+		wpa_printf(MSG_DEBUG, "Failed to get PMK from EAPOL state "
+			   "machines");
+		return;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Configure PMK for driver-based 4-way "
+			"handshake", pmk, pmk_len);
+
+	if (wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0, NULL, 0, pmk,
+			    pmk_len)) {
+		wpa_printf(MSG_DEBUG, "Failed to set PMK to the driver");
+	}
+
+	wpa_supplicant_cancel_scan(wpa_s);
+	wpa_supplicant_cancel_auth_timeout(wpa_s);
+	wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+
+}
+
+
+static void wpa_supplicant_notify_eapol_done(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	wpa_msg(wpa_s, MSG_DEBUG, "WPA: EAPOL processing complete");
+	if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+		wpa_supplicant_set_state(wpa_s, WPA_4WAY_HANDSHAKE);
+	} else {
+		wpa_supplicant_cancel_auth_timeout(wpa_s);
+		wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+	}
+}
+
+#endif /* IEEE8021X_EAPOL */
+
+
+#ifndef CONFIG_NO_WPA
+
+static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s)
+{
+	int ret = 0;
+	struct wpa_bss *curr = NULL, *bss;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	const u8 *ie;
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		if (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) != 0)
+			continue;
+		if (ssid == NULL ||
+		    ((bss->ssid_len == ssid->ssid_len &&
+		      os_memcmp(bss->ssid, ssid->ssid, ssid->ssid_len) == 0) ||
+		     ssid->ssid_len == 0)) {
+			curr = bss;
+			break;
+		}
+	}
+
+	if (curr) {
+		ie = wpa_bss_get_vendor_ie(curr, WPA_IE_VENDOR_TYPE);
+		if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
+			ret = -1;
+
+		ie = wpa_bss_get_ie(curr, WLAN_EID_RSN);
+		if (wpa_sm_set_ap_rsn_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
+			ret = -1;
+	} else {
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+static int wpa_supplicant_get_beacon_ie(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	if (wpa_get_beacon_ie(wpa_s) == 0) {
+		return 0;
+	}
+
+	/* No WPA/RSN IE found in the cached scan results. Try to get updated
+	 * scan results from the driver. */
+	if (wpa_supplicant_update_scan_results(wpa_s) < 0)
+		return -1;
+
+	return wpa_get_beacon_ie(wpa_s);
+}
+
+
+static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type,
+			     const void *data, u16 data_len,
+			     size_t *msg_len, void **data_pos)
+{
+	return wpa_alloc_eapol(wpa_s, type, data, data_len, msg_len, data_pos);
+}
+
+
+static int _wpa_ether_send(void *wpa_s, const u8 *dest, u16 proto,
+			   const u8 *buf, size_t len)
+{
+	return wpa_ether_send(wpa_s, dest, proto, buf, len);
+}
+
+
+static void _wpa_supplicant_cancel_auth_timeout(void *wpa_s)
+{
+	wpa_supplicant_cancel_auth_timeout(wpa_s);
+}
+
+
+static void _wpa_supplicant_set_state(void *wpa_s, enum wpa_states state)
+{
+	wpa_supplicant_set_state(wpa_s, state);
+}
+
+
+/**
+ * wpa_supplicant_get_state - Get the connection state
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: The current connection state (WPA_*)
+ */
+static enum wpa_states wpa_supplicant_get_state(struct wpa_supplicant *wpa_s)
+{
+	return wpa_s->wpa_state;
+}
+
+
+static enum wpa_states _wpa_supplicant_get_state(void *wpa_s)
+{
+	return wpa_supplicant_get_state(wpa_s);
+}
+
+
+static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code)
+{
+	wpa_supplicant_deauthenticate(wpa_s, reason_code);
+	/* Schedule a scan to make sure we continue looking for networks */
+	wpa_supplicant_req_scan(wpa_s, 5, 0);
+}
+
+
+static void * wpa_supplicant_get_network_ctx(void *wpa_s)
+{
+	return wpa_supplicant_get_ssid(wpa_s);
+}
+
+
+static int wpa_supplicant_get_bssid(void *ctx, u8 *bssid)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	return wpa_drv_get_bssid(wpa_s, bssid);
+}
+
+
+static int wpa_supplicant_set_key(void *_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)
+{
+	struct wpa_supplicant *wpa_s = _wpa_s;
+	if (alg == WPA_ALG_TKIP && key_idx == 0 && key_len == 32) {
+		/* Clear the MIC error counter when setting a new PTK. */
+		wpa_s->mic_errors_seen = 0;
+	}
+#ifdef CONFIG_TESTING_GET_GTK
+	if (key_idx > 0 && addr && is_broadcast_ether_addr(addr) &&
+	    alg != WPA_ALG_NONE && key_len <= sizeof(wpa_s->last_gtk)) {
+		os_memcpy(wpa_s->last_gtk, key, key_len);
+		wpa_s->last_gtk_len = key_len;
+	}
+#endif /* CONFIG_TESTING_GET_GTK */
+	return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len,
+			       key, key_len);
+}
+
+
+static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr,
+					     int protection_type,
+					     int key_type)
+{
+	return wpa_drv_mlme_setprotection(wpa_s, addr, protection_type,
+					  key_type);
+}
+
+
+static int wpa_supplicant_add_pmkid(void *wpa_s,
+				    const u8 *bssid, const u8 *pmkid)
+{
+	return wpa_drv_add_pmkid(wpa_s, bssid, pmkid);
+}
+
+
+static int wpa_supplicant_remove_pmkid(void *wpa_s,
+				       const u8 *bssid, const u8 *pmkid)
+{
+	return wpa_drv_remove_pmkid(wpa_s, bssid, pmkid);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static int wpa_supplicant_update_ft_ies(void *ctx, const u8 *md,
+					const u8 *ies, size_t ies_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+		return sme_update_ft_ies(wpa_s, md, ies, ies_len);
+	return wpa_drv_update_ft_ies(wpa_s, md, ies, ies_len);
+}
+
+
+static int wpa_supplicant_send_ft_action(void *ctx, u8 action,
+					 const u8 *target_ap,
+					 const u8 *ies, size_t ies_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	int ret;
+	u8 *data, *pos;
+	size_t data_len;
+
+	if (action != 1) {
+		wpa_printf(MSG_ERROR, "Unsupported send_ft_action action %d",
+			   action);
+		return -1;
+	}
+
+	/*
+	 * Action frame payload:
+	 * Category[1] = 6 (Fast BSS Transition)
+	 * Action[1] = 1 (Fast BSS Transition Request)
+	 * STA Address
+	 * Target AP Address
+	 * FT IEs
+	 */
+
+	data_len = 2 + 2 * ETH_ALEN + ies_len;
+	data = os_malloc(data_len);
+	if (data == NULL)
+		return -1;
+	pos = data;
+	*pos++ = 0x06; /* FT Action category */
+	*pos++ = action;
+	os_memcpy(pos, wpa_s->own_addr, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, target_ap, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, ies, ies_len);
+
+	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0,
+				  wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid,
+				  data, data_len, 0);
+	os_free(data);
+
+	return ret;
+}
+
+
+static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_driver_auth_params params;
+	struct wpa_bss *bss;
+
+	bss = wpa_bss_get_bssid(wpa_s, target_ap);
+	if (bss == NULL)
+		return -1;
+
+	os_memset(&params, 0, sizeof(params));
+	params.bssid = target_ap;
+	params.freq = bss->freq;
+	params.ssid = bss->ssid;
+	params.ssid_len = bss->ssid_len;
+	params.auth_alg = WPA_AUTH_ALG_FT;
+	params.local_state_change = 1;
+	return wpa_drv_authenticate(wpa_s, &params);
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+#ifdef CONFIG_TDLS
+
+static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported,
+					int *tdls_ext_setup,
+					int *tdls_chan_switch)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	*tdls_supported = 0;
+	*tdls_ext_setup = 0;
+	*tdls_chan_switch = 0;
+
+	if (!wpa_s->drv_capa_known)
+		return -1;
+
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)
+		*tdls_supported = 1;
+
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP)
+		*tdls_ext_setup = 1;
+
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH)
+		*tdls_chan_switch = 1;
+
+	return 0;
+}
+
+
+static int wpa_supplicant_send_tdls_mgmt(void *ctx, const u8 *dst,
+					 u8 action_code, u8 dialog_token,
+					 u16 status_code, u32 peer_capab,
+					 int initiator, 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, peer_capab, initiator, buf,
+				      len);
+}
+
+
+static int wpa_supplicant_tdls_oper(void *ctx, int oper, const u8 *peer)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	return wpa_drv_tdls_oper(wpa_s, oper, peer);
+}
+
+
+static int wpa_supplicant_tdls_peer_addset(
+	void *ctx, const u8 *peer, int add, u16 aid, u16 capability,
+	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, int wmm, 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;
+
+	os_memset(&params, 0, sizeof(params));
+
+	params.addr = peer;
+	params.aid = aid;
+	params.capability = capability;
+	params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED;
+
+	/*
+	 * Don't rely only on qosinfo for WMM capability. It may be 0 even when
+	 * present. Allow the WMM IE to also indicate QoS support.
+	 */
+	if (wmm || qosinfo)
+		params.flags |= WPA_STA_WMM;
+
+	params.ht_capabilities = ht_capab;
+	params.vht_capabilities = vht_capab;
+	params.qosinfo = qosinfo;
+	params.listen_interval = 0;
+	params.supp_rates = supp_rates;
+	params.supp_rates_len = supp_rates_len;
+	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);
+}
+
+
+static int wpa_supplicant_tdls_enable_channel_switch(
+	void *ctx, const u8 *addr, u8 oper_class,
+	const struct hostapd_freq_params *params)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpa_drv_tdls_enable_channel_switch(wpa_s, addr, oper_class,
+						  params);
+}
+
+
+static int wpa_supplicant_tdls_disable_channel_switch(void *ctx, const u8 *addr)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpa_drv_tdls_disable_channel_switch(wpa_s, addr);
+}
+
+#endif /* CONFIG_TDLS */
+
+#endif /* CONFIG_NO_WPA */
+
+
+enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field)
+{
+	if (os_strcmp(field, "IDENTITY") == 0)
+		return WPA_CTRL_REQ_EAP_IDENTITY;
+	else if (os_strcmp(field, "PASSWORD") == 0)
+		return WPA_CTRL_REQ_EAP_PASSWORD;
+	else if (os_strcmp(field, "NEW_PASSWORD") == 0)
+		return WPA_CTRL_REQ_EAP_NEW_PASSWORD;
+	else if (os_strcmp(field, "PIN") == 0)
+		return WPA_CTRL_REQ_EAP_PIN;
+	else if (os_strcmp(field, "OTP") == 0)
+		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;
+	else if (os_strcmp(field, "PSK_PASSPHRASE") == 0)
+		return WPA_CTRL_REQ_PSK_PASSPHRASE;
+	return WPA_CTRL_REQ_UNKNOWN;
+}
+
+
+const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
+					       const char *default_txt,
+					       const char **txt)
+{
+	const char *ret = NULL;
+
+	*txt = default_txt;
+
+	switch (field) {
+	case WPA_CTRL_REQ_EAP_IDENTITY:
+		*txt = "Identity";
+		ret = "IDENTITY";
+		break;
+	case WPA_CTRL_REQ_EAP_PASSWORD:
+		*txt = "Password";
+		ret = "PASSWORD";
+		break;
+	case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
+		*txt = "New Password";
+		ret = "NEW_PASSWORD";
+		break;
+	case WPA_CTRL_REQ_EAP_PIN:
+		*txt = "PIN";
+		ret = "PIN";
+		break;
+	case WPA_CTRL_REQ_EAP_OTP:
+		ret = "OTP";
+		break;
+	case WPA_CTRL_REQ_EAP_PASSPHRASE:
+		*txt = "Private key passphrase";
+		ret = "PASSPHRASE";
+		break;
+	case WPA_CTRL_REQ_SIM:
+		ret = "SIM";
+		break;
+	case WPA_CTRL_REQ_PSK_PASSPHRASE:
+		*txt = "PSK or passphrase";
+		ret = "PSK_PASSPHRASE";
+		break;
+	default:
+		break;
+	}
+
+	/* txt needs to be something */
+	if (*txt == NULL) {
+		wpa_printf(MSG_WARNING, "No message for request %d", field);
+		ret = NULL;
+	}
+
+	return ret;
+}
+
+
+void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+			const char *field_name, const char *txt)
+{
+	char *buf;
+	size_t buflen;
+	int len;
+
+	buflen = 100 + os_strlen(txt) + ssid->ssid_len;
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return;
+	len = os_snprintf(buf, buflen, "%s-%d:%s needed for SSID ",
+			  field_name, ssid->id, txt);
+	if (os_snprintf_error(buflen, len)) {
+		os_free(buf);
+		return;
+	}
+	if (ssid->ssid && buflen > len + ssid->ssid_len) {
+		os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
+		len += ssid->ssid_len;
+		buf[len] = '\0';
+	}
+	buf[buflen - 1] = '\0';
+	wpa_msg(wpa_s, MSG_INFO, WPA_CTRL_REQ "%s", buf);
+	os_free(buf);
+}
+
+
+#ifdef IEEE8021X_EAPOL
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static void wpa_supplicant_eap_param_needed(void *ctx,
+					    enum wpa_ctrl_req_type field,
+					    const char *default_txt)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	const char *field_name, *txt = NULL;
+
+	if (ssid == NULL)
+		return;
+
+	wpas_notify_network_request(wpa_s, ssid, field, default_txt);
+
+	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;
+	}
+
+	wpas_notify_eap_status(wpa_s, "eap parameter needed", field_name);
+
+	wpas_send_ctrl_req(wpa_s, ssid, field_name, txt);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define wpa_supplicant_eap_param_needed NULL
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_EAP_PROXY
+static void wpa_supplicant_eap_proxy_cb(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	size_t 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)",
+			   wpa_s->imsi, wpa_s->mnc_len);
+	} else {
+		wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available");
+	}
+}
+#endif /* CONFIG_EAP_PROXY */
+
+
+static void wpa_supplicant_port_cb(void *ctx, int authorized)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface) {
+		wpa_printf(MSG_DEBUG, "AP mode active - skip EAPOL Supplicant "
+			   "port status: %s",
+			   authorized ? "Authorized" : "Unauthorized");
+		return;
+	}
+#endif /* CONFIG_AP */
+	wpa_printf(MSG_DEBUG, "EAPOL: Supplicant port status: %s",
+		   authorized ? "Authorized" : "Unauthorized");
+	wpa_drv_set_supp_port(wpa_s, authorized);
+}
+
+
+static void wpa_supplicant_cert_cb(void *ctx, int depth, const char *subject,
+				   const char *altsubject[], int num_altsubject,
+				   const char *cert_hash,
+				   const struct wpabuf *cert)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	wpas_notify_certification(wpa_s, depth, subject, altsubject,
+				  num_altsubject, cert_hash, cert);
+}
+
+
+static void wpa_supplicant_status_cb(void *ctx, const char *status,
+				     const char *parameter)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	wpas_notify_eap_status(wpa_s, status, parameter);
+}
+
+
+static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	char *str;
+	int res;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity",
+			  id, len);
+
+	if (wpa_s->current_ssid == NULL)
+		return;
+
+	if (id == NULL) {
+		if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+				   "NULL", 0) < 0)
+			return;
+	} else {
+		str = os_malloc(len * 2 + 1);
+		if (str == NULL)
+			return;
+		wpa_snprintf_hex(str, len * 2 + 1, id, len);
+		res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+				     str, 0);
+		os_free(str);
+		if (res < 0)
+			return;
+	}
+
+	if (wpa_s->conf->update_config) {
+		res = wpa_config_write(wpa_s->confname, wpa_s->conf);
+		if (res) {
+			wpa_printf(MSG_DEBUG, "Failed to update config after "
+				   "anonymous_id update");
+		}
+	}
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
+{
+#ifdef IEEE8021X_EAPOL
+	struct eapol_ctx *ctx;
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to allocate EAPOL context.");
+		return -1;
+	}
+
+	ctx->ctx = wpa_s;
+	ctx->msg_ctx = wpa_s;
+	ctx->eapol_send_ctx = wpa_s;
+	ctx->preauth = 0;
+	ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done;
+	ctx->eapol_send = wpa_supplicant_eapol_send;
+	ctx->set_wep_key = wpa_eapol_set_wep_key;
+#ifndef CONFIG_NO_CONFIG_BLOBS
+	ctx->set_config_blob = wpa_supplicant_set_config_blob;
+	ctx->get_config_blob = wpa_supplicant_get_config_blob;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+	ctx->aborted_cached = wpa_supplicant_aborted_cached;
+	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->openssl_ciphers = wpa_s->conf->openssl_ciphers;
+	ctx->wps = wpa_s->wps;
+	ctx->eap_param_needed = wpa_supplicant_eap_param_needed;
+#ifdef CONFIG_EAP_PROXY
+	ctx->eap_proxy_cb = wpa_supplicant_eap_proxy_cb;
+#endif /* CONFIG_EAP_PROXY */
+	ctx->port_cb = wpa_supplicant_port_cb;
+	ctx->cb = wpa_supplicant_eapol_cb;
+	ctx->cert_cb = wpa_supplicant_cert_cb;
+	ctx->cert_in_cb = wpa_s->conf->cert_in_cb;
+	ctx->status_cb = wpa_supplicant_status_cb;
+	ctx->set_anon_id = wpa_supplicant_set_anon_id;
+	ctx->cb_ctx = wpa_s;
+	wpa_s->eapol = eapol_sm_init(ctx);
+	if (wpa_s->eapol == NULL) {
+		os_free(ctx);
+		wpa_printf(MSG_ERROR, "Failed to initialize EAPOL state "
+			   "machines.");
+		return -1;
+	}
+#endif /* IEEE8021X_EAPOL */
+
+	return 0;
+}
+
+
+#ifndef CONFIG_NO_WPA
+static void wpa_supplicant_set_rekey_offload(void *ctx,
+					     const u8 *kek, size_t kek_len,
+					     const u8 *kck, size_t kck_len,
+					     const u8 *replay_ctr)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	wpa_drv_set_rekey_info(wpa_s, kek, kek_len, kck, kck_len, replay_ctr);
+}
+#endif /* CONFIG_NO_WPA */
+
+
+static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk,
+					   size_t pmk_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	if (wpa_s->conf->key_mgmt_offload &&
+	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
+		return wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0,
+				       NULL, 0, pmk, pmk_len);
+	else
+		return 0;
+}
+
+
+int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
+{
+#ifndef CONFIG_NO_WPA
+	struct wpa_sm_ctx *ctx;
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to allocate WPA context.");
+		return -1;
+	}
+
+	ctx->ctx = wpa_s;
+	ctx->msg_ctx = wpa_s;
+	ctx->set_state = _wpa_supplicant_set_state;
+	ctx->get_state = _wpa_supplicant_get_state;
+	ctx->deauthenticate = _wpa_supplicant_deauthenticate;
+	ctx->set_key = wpa_supplicant_set_key;
+	ctx->get_network_ctx = wpa_supplicant_get_network_ctx;
+	ctx->get_bssid = wpa_supplicant_get_bssid;
+	ctx->ether_send = _wpa_ether_send;
+	ctx->get_beacon_ie = wpa_supplicant_get_beacon_ie;
+	ctx->alloc_eapol = _wpa_alloc_eapol;
+	ctx->cancel_auth_timeout = _wpa_supplicant_cancel_auth_timeout;
+	ctx->add_pmkid = wpa_supplicant_add_pmkid;
+	ctx->remove_pmkid = wpa_supplicant_remove_pmkid;
+#ifndef CONFIG_NO_CONFIG_BLOBS
+	ctx->set_config_blob = wpa_supplicant_set_config_blob;
+	ctx->get_config_blob = wpa_supplicant_get_config_blob;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+	ctx->mlme_setprotection = wpa_supplicant_mlme_setprotection;
+#ifdef CONFIG_IEEE80211R
+	ctx->update_ft_ies = wpa_supplicant_update_ft_ies;
+	ctx->send_ft_action = wpa_supplicant_send_ft_action;
+	ctx->mark_authenticated = wpa_supplicant_mark_authenticated;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_TDLS
+	ctx->tdls_get_capa = wpa_supplicant_tdls_get_capa;
+	ctx->send_tdls_mgmt = wpa_supplicant_send_tdls_mgmt;
+	ctx->tdls_oper = wpa_supplicant_tdls_oper;
+	ctx->tdls_peer_addset = wpa_supplicant_tdls_peer_addset;
+	ctx->tdls_enable_channel_switch =
+		wpa_supplicant_tdls_enable_channel_switch;
+	ctx->tdls_disable_channel_switch =
+		wpa_supplicant_tdls_disable_channel_switch;
+#endif /* CONFIG_TDLS */
+	ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
+	ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk;
+
+	wpa_s->wpa = wpa_sm_init(ctx);
+	if (wpa_s->wpa == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
+			   "machine");
+		os_free(ctx);
+		return -1;
+	}
+#endif /* CONFIG_NO_WPA */
+
+	return 0;
+}
+
+
+void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
+					struct wpa_ssid *ssid)
+{
+	struct rsn_supp_config conf;
+	if (ssid) {
+		os_memset(&conf, 0, sizeof(conf));
+		conf.network_ctx = ssid;
+		conf.peerkey_enabled = ssid->peerkey;
+		conf.allowed_pairwise_cipher = ssid->pairwise_cipher;
+#ifdef IEEE8021X_EAPOL
+		conf.proactive_key_caching = ssid->proactive_key_caching < 0 ?
+			wpa_s->conf->okc : ssid->proactive_key_caching;
+		conf.eap_workaround = ssid->eap_workaround;
+		conf.eap_conf_ctx = &ssid->eap;
+#endif /* IEEE8021X_EAPOL */
+		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/hostap/wpa_supplicant/wpas_glue.h b/hostap/wpa_supplicant/wpas_glue.h
new file mode 100644
index 0000000..5585e56
--- /dev/null
+++ b/hostap/wpa_supplicant/wpas_glue.h
@@ -0,0 +1,28 @@
+/*
+ * WPA Supplicant - Glue code to setup EAPOL and RSN modules
+ * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPAS_GLUE_H
+#define WPAS_GLUE_H
+
+enum wpa_ctrl_req_type;
+
+int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
+					struct wpa_ssid *ssid);
+
+const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
+					       const char *default_txt,
+					       const char **txt);
+
+enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field);
+
+void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+			const char *field_name, const char *txt);
+
+#endif /* WPAS_GLUE_H */
diff --git a/hostap/wpa_supplicant/wpas_kay.c b/hostap/wpa_supplicant/wpas_kay.c
new file mode 100644
index 0000000..354decf
--- /dev/null
+++ b/hostap/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/hostap/wpa_supplicant/wpas_kay.h b/hostap/wpa_supplicant/wpas_kay.h
new file mode 100644
index 0000000..b7236d0
--- /dev/null
+++ b/hostap/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/hostap/wpa_supplicant/wpas_module_tests.c b/hostap/wpa_supplicant/wpas_module_tests.c
new file mode 100644
index 0000000..6af1678
--- /dev/null
+++ b/hostap/wpa_supplicant/wpas_module_tests.c
@@ -0,0 +1,108 @@
+/*
+ * 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;
+	}
+
+	{
+		int crypto_module_tests(void);
+		if (crypto_module_tests() < 0)
+			ret = -1;
+	}
+
+	return ret;
+}
diff --git a/hostap/wpa_supplicant/wps_supplicant.c b/hostap/wpa_supplicant/wps_supplicant.c
new file mode 100644
index 0000000..60f761c
--- /dev/null
+++ b/hostap/wpa_supplicant/wps_supplicant.c
@@ -0,0 +1,2902 @@
+/*
+ * wpa_supplicant / WPS integration
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "uuid.h"
+#include "crypto/random.h"
+#include "crypto/dh_group5.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_common.h"
+#include "common/wpa_ctrl.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_peer/eap.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "wps/wps_attr_parse.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "notify.h"
+#include "blacklist.h"
+#include "bss.h"
+#include "scan.h"
+#include "ap.h"
+#include "p2p/p2p.h"
+#include "p2p_supplicant.h"
+#include "wps_supplicant.h"
+
+
+#ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
+#define WPS_PIN_SCAN_IGNORE_SEL_REG 3
+#endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */
+
+/*
+ * The minimum time in seconds before trying to associate to a WPS PIN AP that
+ * does not have Selected Registrar TRUE.
+ */
+#ifndef WPS_PIN_TIME_IGNORE_SEL_REG
+#define WPS_PIN_TIME_IGNORE_SEL_REG 5
+#endif /* WPS_PIN_TIME_IGNORE_SEL_REG */
+
+static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpas_clear_wps(struct wpa_supplicant *wpa_s);
+
+
+static void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s)
+{
+	os_free(wpa_s->wps_ap);
+	wpa_s->wps_ap = NULL;
+	wpa_s->num_wps_ap = 0;
+	wpa_s->wps_ap_iter = 0;
+}
+
+
+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)) {
+		const u8 *bssid = wpa_s->bssid;
+		if (is_zero_ether_addr(bssid))
+			bssid = wpa_s->pending_bssid;
+
+		wpa_printf(MSG_DEBUG, "WPS: PIN registration with " MACSTR
+			   " did not succeed - continue trying to find "
+			   "suitable AP", MAC2STR(bssid));
+		wpa_blacklist_add(wpa_s, bssid);
+
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+		wpa_s->reassociate = 1;
+		wpa_supplicant_req_scan(wpa_s,
+					wpa_s->blacklist_cleared ? 5 : 0, 0);
+		wpa_s->blacklist_cleared = 0;
+		return 1;
+	}
+
+	wpas_wps_clear_ap_info(wpa_s);
+	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && !wpa_s->wps_success)
+		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL);
+
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid &&
+	    !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+		int disabled = wpa_s->current_ssid->disabled;
+		unsigned int freq = wpa_s->assoc_freq;
+		struct wpa_bss *bss;
+		struct wpa_ssid *ssid = NULL;
+		int use_fast_assoc = 0;
+
+		wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
+			   "try to associate with the received credential "
+			   "(freq=%u)", freq);
+		wpa_s->own_disconnect_req = 1;
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+		if (disabled) {
+			wpa_printf(MSG_DEBUG, "WPS: Current network is "
+				   "disabled - wait for user to enable");
+			return 1;
+		}
+		wpa_s->after_wps = 5;
+		wpa_s->wps_freq = freq;
+		wpa_s->normal_scans = 0;
+		wpa_s->reassociate = 1;
+
+		wpa_printf(MSG_DEBUG, "WPS: Checking whether fast association "
+			   "without a new scan can be used");
+		bss = wpa_supplicant_pick_network(wpa_s, &ssid);
+		if (bss) {
+			struct wpabuf *wps;
+			struct wps_parse_attr attr;
+
+			wps = wpa_bss_get_vendor_ie_multi(bss,
+							  WPS_IE_VENDOR_TYPE);
+			if (wps && wps_parse_msg(wps, &attr) == 0 &&
+			    attr.wps_state &&
+			    *attr.wps_state == WPS_STATE_CONFIGURED)
+				use_fast_assoc = 1;
+			wpabuf_free(wps);
+		}
+
+		/*
+		 * 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;
+	}
+
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid) {
+		wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting "
+			   "for external credential processing");
+		wpas_clear_wps(wpa_s);
+		wpa_s->own_disconnect_req = 1;
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s,
+					 struct wpa_ssid *ssid,
+					 const struct wps_credential *cred)
+{
+	struct wpa_driver_capa capa;
+	struct wpa_bss *bss;
+	const u8 *ie;
+	struct wpa_ie_data adv;
+	int wpa2 = 0, ccmp = 0;
+
+	/*
+	 * Many existing WPS APs do not know how to negotiate WPA2 or CCMP in
+	 * case they are configured for mixed mode operation (WPA+WPA2 and
+	 * TKIP+CCMP). Try to use scan results to figure out whether the AP
+	 * actually supports stronger security and select that if the client
+	 * has support for it, too.
+	 */
+
+	if (wpa_drv_get_capa(wpa_s, &capa))
+		return; /* Unknown what driver supports */
+
+	if (ssid->ssid == NULL)
+		return;
+	bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len);
+	if (bss == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS "
+			   "table - use credential as-is");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: AP found from BSS table");
+
+	ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0) {
+		wpa2 = 1;
+		if (adv.pairwise_cipher & WPA_CIPHER_CCMP)
+			ccmp = 1;
+	} else {
+		ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+		if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0 &&
+		    adv.pairwise_cipher & WPA_CIPHER_CCMP)
+			ccmp = 1;
+	}
+
+	if (ie == NULL && (ssid->proto & WPA_PROTO_WPA) &&
+	    (ssid->pairwise_cipher & WPA_CIPHER_TKIP)) {
+		/*
+		 * TODO: This could be the initial AP configuration and the
+		 * Beacon contents could change shortly. Should request a new
+		 * scan and delay addition of the network until the updated
+		 * scan results are available.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS: The AP did not yet advertise WPA "
+			   "support - use credential as-is");
+		return;
+	}
+
+	if (ccmp && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
+	    (ssid->pairwise_cipher & WPA_CIPHER_TKIP) &&
+	    (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+		wpa_printf(MSG_DEBUG, "WPS: Add CCMP into the credential "
+			   "based on scan results");
+		if (wpa_s->conf->ap_scan == 1)
+			ssid->pairwise_cipher |= WPA_CIPHER_CCMP;
+		else
+			ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+	}
+
+	if (wpa2 && !(ssid->proto & WPA_PROTO_RSN) &&
+	    (ssid->proto & WPA_PROTO_WPA) &&
+	    (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP)) {
+		wpa_printf(MSG_DEBUG, "WPS: Add WPA2 into the credential "
+			   "based on scan results");
+		if (wpa_s->conf->ap_scan == 1)
+			ssid->proto |= WPA_PROTO_RSN;
+		else
+			ssid->proto = WPA_PROTO_RSN;
+	}
+}
+
+
+static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s,
+					struct wpa_ssid *new_ssid)
+{
+	struct wpa_ssid *ssid, *next;
+
+	for (ssid = wpa_s->conf->ssid, next = ssid ? ssid->next : NULL; ssid;
+	     ssid = next, next = ssid ? ssid->next : NULL) {
+		/*
+		 * new_ssid has already been added to the list in
+		 * wpas_wps_add_network(), so skip it.
+		 */
+		if (ssid == new_ssid)
+			continue;
+
+		if (ssid->bssid_set || new_ssid->bssid_set) {
+			if (ssid->bssid_set != new_ssid->bssid_set)
+				continue;
+			if (os_memcmp(ssid->bssid, new_ssid->bssid, ETH_ALEN) !=
+			    0)
+				continue;
+		}
+
+		/* compare SSID */
+		if (ssid->ssid_len == 0 || ssid->ssid_len != new_ssid->ssid_len)
+			continue;
+
+		if (ssid->ssid && new_ssid->ssid) {
+			if (os_memcmp(ssid->ssid, new_ssid->ssid,
+				      ssid->ssid_len) != 0)
+				continue;
+		} else if (ssid->ssid || new_ssid->ssid)
+			continue;
+
+		/* compare security parameters */
+		if (ssid->auth_alg != new_ssid->auth_alg ||
+		    ssid->key_mgmt != new_ssid->key_mgmt ||
+		    (ssid->group_cipher != new_ssid->group_cipher &&
+		     !(ssid->group_cipher & new_ssid->group_cipher &
+		       WPA_CIPHER_CCMP)))
+			continue;
+
+		/*
+		 * Some existing WPS APs will send two creds in case they are
+		 * configured for mixed mode operation (WPA+WPA2 and TKIP+CCMP).
+		 * Try to merge these two creds if they are received in the same
+		 * M8 message.
+		 */
+		if (ssid->wps_run && ssid->wps_run == new_ssid->wps_run &&
+		    wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
+			if (new_ssid->passphrase && ssid->passphrase &&
+			    os_strcmp(new_ssid->passphrase, ssid->passphrase) !=
+			    0) {
+				wpa_printf(MSG_DEBUG,
+					   "WPS: M8 Creds with different passphrase - do not merge");
+				continue;
+			}
+
+			if (new_ssid->psk_set &&
+			    (!ssid->psk_set ||
+			     os_memcmp(new_ssid->psk, ssid->psk, 32) != 0)) {
+				wpa_printf(MSG_DEBUG,
+					   "WPS: M8 Creds with different PSK - do not merge");
+				continue;
+			}
+
+			if ((new_ssid->passphrase && !ssid->passphrase) ||
+			    (!new_ssid->passphrase && ssid->passphrase)) {
+				wpa_printf(MSG_DEBUG,
+					   "WPS: M8 Creds with different passphrase/PSK type - do not merge");
+				continue;
+			}
+
+			wpa_printf(MSG_DEBUG,
+				   "WPS: Workaround - merge likely WPA/WPA2-mixed mode creds in same M8 message");
+			new_ssid->proto |= ssid->proto;
+			new_ssid->pairwise_cipher |= ssid->pairwise_cipher;
+		} else {
+			/*
+			 * proto and pairwise_cipher difference matter for
+			 * non-mixed-mode creds.
+			 */
+			if (ssid->proto != new_ssid->proto ||
+			    ssid->pairwise_cipher != new_ssid->pairwise_cipher)
+				continue;
+		}
+
+		/* Remove the duplicated older network entry. */
+		wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id);
+		wpas_notify_network_removed(wpa_s, ssid);
+		if (wpa_s->current_ssid == ssid)
+			wpa_s->current_ssid = NULL;
+		wpa_config_remove_network(wpa_s->conf, ssid->id);
+	}
+}
+
+
+static int wpa_supplicant_wps_cred(void *ctx,
+				   const struct wps_credential *cred)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	u16 auth_type;
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
+	int registrar = 0;
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
+
+	if ((wpa_s->conf->wps_cred_processing == 1 ||
+	     wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) {
+		size_t blen = cred->cred_attr_len * 2 + 1;
+		char *buf = os_malloc(blen);
+		if (buf) {
+			wpa_snprintf_hex(buf, blen,
+					 cred->cred_attr, cred->cred_attr_len);
+			wpa_msg(wpa_s, MSG_INFO, "%s%s",
+				WPS_EVENT_CRED_RECEIVED, buf);
+			os_free(buf);
+		}
+
+		wpas_notify_wps_credential(wpa_s, cred);
+	} else
+		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CRED_RECEIVED);
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
+			cred->cred_attr, cred->cred_attr_len);
+
+	if (wpa_s->conf->wps_cred_processing == 1)
+		return 0;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len);
+	wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x",
+		   cred->auth_type);
+	wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type);
+	wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
+			cred->key, cred->key_len);
+	wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR,
+		   MAC2STR(cred->mac_addr));
+
+	auth_type = cred->auth_type;
+	if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
+		wpa_printf(MSG_DEBUG, "WPS: Workaround - convert mixed-mode "
+			   "auth_type into WPA2PSK");
+		auth_type = WPS_AUTH_WPA2PSK;
+	}
+
+	if (auth_type != WPS_AUTH_OPEN &&
+	    auth_type != WPS_AUTH_WPAPSK &&
+	    auth_type != WPS_AUTH_WPA2PSK) {
+		wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
+			   "unsupported authentication type 0x%x",
+			   auth_type);
+		return 0;
+	}
+
+	if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) {
+		if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) {
+			wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with "
+				   "invalid Network Key length %lu",
+				   (unsigned long) cred->key_len);
+			return -1;
+		}
+	}
+
+	if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+		wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based "
+			   "on the received credential");
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
+		if (ssid->eap.identity &&
+		    ssid->eap.identity_len == WSC_ID_REGISTRAR_LEN &&
+		    os_memcmp(ssid->eap.identity, WSC_ID_REGISTRAR,
+			      WSC_ID_REGISTRAR_LEN) == 0)
+			registrar = 1;
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
+		os_free(ssid->eap.identity);
+		ssid->eap.identity = NULL;
+		ssid->eap.identity_len = 0;
+		os_free(ssid->eap.phase1);
+		ssid->eap.phase1 = NULL;
+		os_free(ssid->eap.eap_methods);
+		ssid->eap.eap_methods = NULL;
+		if (!ssid->p2p_group) {
+			ssid->temporary = 0;
+			ssid->bssid_set = 0;
+		}
+		ssid->disabled_until.sec = 0;
+		ssid->disabled_until.usec = 0;
+		ssid->auth_failures = 0;
+	} else {
+		wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the "
+			   "received credential");
+		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);
+	}
+
+	wpa_config_set_network_defaults(ssid);
+	ssid->wps_run = wpa_s->wps_run;
+
+	os_free(ssid->ssid);
+	ssid->ssid = os_malloc(cred->ssid_len);
+	if (ssid->ssid) {
+		os_memcpy(ssid->ssid, cred->ssid, cred->ssid_len);
+		ssid->ssid_len = cred->ssid_len;
+	}
+
+	switch (cred->encr_type) {
+	case WPS_ENCR_NONE:
+		break;
+	case WPS_ENCR_TKIP:
+		ssid->pairwise_cipher = WPA_CIPHER_TKIP;
+		break;
+	case WPS_ENCR_AES:
+		ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+		if (wpa_s->drv_capa_known &&
+		    (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP)) {
+			ssid->pairwise_cipher |= WPA_CIPHER_GCMP;
+			ssid->group_cipher |= WPA_CIPHER_GCMP;
+		}
+		break;
+	}
+
+	switch (auth_type) {
+	case WPS_AUTH_OPEN:
+		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+		ssid->key_mgmt = WPA_KEY_MGMT_NONE;
+		ssid->proto = 0;
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
+		if (registrar) {
+			wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OPEN_NETWORK
+				"id=%d - Credentials for an open "
+				"network disabled by default - use "
+				"'select_network %d' to enable",
+				ssid->id, ssid->id);
+			ssid->disabled = 1;
+		}
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
+		break;
+	case WPS_AUTH_WPAPSK:
+		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+		ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+		ssid->proto = WPA_PROTO_WPA;
+		break;
+	case WPS_AUTH_WPA2PSK:
+		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+		ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+		ssid->proto = WPA_PROTO_RSN;
+		break;
+	}
+
+	if (ssid->key_mgmt == WPA_KEY_MGMT_PSK) {
+		if (cred->key_len == 2 * PMK_LEN) {
+			if (hexstr2bin((const char *) cred->key, ssid->psk,
+				       PMK_LEN)) {
+				wpa_printf(MSG_ERROR, "WPS: Invalid Network "
+					   "Key");
+				return -1;
+			}
+			ssid->psk_set = 1;
+			ssid->export_keys = 1;
+		} else if (cred->key_len >= 8 && cred->key_len < 2 * PMK_LEN) {
+			os_free(ssid->passphrase);
+			ssid->passphrase = os_malloc(cred->key_len + 1);
+			if (ssid->passphrase == NULL)
+				return -1;
+			os_memcpy(ssid->passphrase, cred->key, cred->key_len);
+			ssid->passphrase[cred->key_len] = '\0';
+			wpa_config_update_psk(ssid);
+			ssid->export_keys = 1;
+		} else {
+			wpa_printf(MSG_ERROR, "WPS: Invalid Network Key "
+				   "length %lu",
+				   (unsigned long) cred->key_len);
+			return -1;
+		}
+	}
+	ssid->priority = wpa_s->conf->wps_priority;
+
+	wpas_wps_security_workaround(wpa_s, ssid, cred);
+
+	wpas_wps_remove_dup_network(wpa_s, ssid);
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+	if (wpa_s->conf->update_config &&
+	    wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to update configuration");
+		return -1;
+	}
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+	if (ssid->priority)
+		wpa_config_update_prio_list(wpa_s->conf);
+
+	/*
+	 * Optimize the post-WPS scan based on the channel used during
+	 * the provisioning in case EAP-Failure is not received.
+	 */
+	wpa_s->after_wps = 5;
+	wpa_s->wps_freq = wpa_s->assoc_freq;
+
+	return 0;
+}
+
+
+static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
+					 struct wps_event_m2d *m2d)
+{
+	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_M2D
+		"dev_password_id=%d config_error=%d",
+		m2d->dev_password_id, m2d->config_error);
+	wpas_notify_wps_event_m2d(wpa_s, m2d);
+#ifdef CONFIG_P2P
+	if (wpa_s->parent && wpa_s->parent != wpa_s) {
+		wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_M2D
+			"dev_password_id=%d config_error=%d",
+			m2d->dev_password_id, m2d->config_error);
+	}
+	if (m2d->config_error == WPS_CFG_MULTIPLE_PBC_DETECTED) {
+		/*
+		 * Notify P2P from eloop timeout to avoid issues with the
+		 * interface getting removed while processing a message.
+		 */
+		eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb, wpa_s,
+				       NULL);
+	}
+#endif /* CONFIG_P2P */
+}
+
+
+static void wpas_wps_clear_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	wpa_printf(MSG_DEBUG, "WPS: Clear WPS network from timeout");
+	wpas_clear_wps(wpa_s);
+}
+
+
+static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
+					  struct wps_event_fail *fail)
+{
+	if (fail->error_indication > 0 &&
+	    fail->error_indication < NUM_WPS_EI_VALUES) {
+		wpa_msg(wpa_s, MSG_INFO,
+			WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
+			fail->msg, fail->config_error, fail->error_indication,
+			wps_ei_str(fail->error_indication));
+		if (wpa_s->parent && wpa_s->parent != wpa_s)
+			wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
+				"msg=%d config_error=%d reason=%d (%s)",
+				fail->msg, fail->config_error,
+				fail->error_indication,
+				wps_ei_str(fail->error_indication));
+	} else {
+		wpa_msg(wpa_s, MSG_INFO,
+			WPS_EVENT_FAIL "msg=%d config_error=%d",
+			fail->msg, fail->config_error);
+		if (wpa_s->parent && wpa_s->parent != wpa_s)
+			wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
+				"msg=%d config_error=%d",
+				fail->msg, fail->config_error);
+	}
+
+	/*
+	 * Need to allow WPS processing to complete, e.g., by sending WSC_NACK.
+	 */
+	wpa_printf(MSG_DEBUG, "WPS: Register timeout to clear WPS network");
+	eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
+	eloop_register_timeout(0, 100000, wpas_wps_clear_timeout, wpa_s, NULL);
+
+	wpas_notify_wps_event_fail(wpa_s, fail);
+	wpas_p2p_wps_failed(wpa_s, fail);
+}
+
+
+static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx);
+
+static void wpas_wps_reenable_networks(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *ssid;
+	int changed = 0;
+
+	eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (ssid->disabled_for_connect && ssid->disabled) {
+			ssid->disabled_for_connect = 0;
+			ssid->disabled = 0;
+			wpas_notify_network_enabled_changed(wpa_s, ssid);
+			changed++;
+		}
+	}
+
+	if (changed) {
+#ifndef CONFIG_NO_CONFIG_WRITE
+		if (wpa_s->conf->update_config &&
+		    wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+			wpa_printf(MSG_DEBUG, "WPS: Failed to update "
+				   "configuration");
+		}
+#endif /* CONFIG_NO_CONFIG_WRITE */
+	}
+}
+
+
+static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	/* Enable the networks disabled during wpas_wps_reassoc */
+	wpas_wps_reenable_networks(wpa_s);
+}
+
+
+static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
+{
+	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
+	wpa_s->wps_success = 1;
+	wpas_notify_wps_event_success(wpa_s);
+	if (wpa_s->current_ssid)
+		wpas_clear_temp_disabled(wpa_s, wpa_s->current_ssid, 1);
+	wpa_s->extra_blacklist_count = 0;
+
+	/*
+	 * Enable the networks disabled during wpas_wps_reassoc after 10
+	 * seconds. The 10 seconds timer is to allow the data connection to be
+	 * formed before allowing other networks to be selected.
+	 */
+	eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
+			       NULL);
+
+	wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0);
+}
+
+
+static void wpa_supplicant_wps_event_er_ap_add(struct wpa_supplicant *wpa_s,
+					       struct wps_event_er_ap *ap)
+{
+	char uuid_str[100];
+	char dev_type[WPS_DEV_TYPE_BUFSIZE];
+
+	uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str));
+	if (ap->pri_dev_type)
+		wps_dev_type_bin2str(ap->pri_dev_type, dev_type,
+				     sizeof(dev_type));
+	else
+		dev_type[0] = '\0';
+
+	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_ADD "%s " MACSTR
+		" pri_dev_type=%s wps_state=%d |%s|%s|%s|%s|%s|%s|",
+		uuid_str, MAC2STR(ap->mac_addr), dev_type, ap->wps_state,
+		ap->friendly_name ? ap->friendly_name : "",
+		ap->manufacturer ? ap->manufacturer : "",
+		ap->model_description ? ap->model_description : "",
+		ap->model_name ? ap->model_name : "",
+		ap->manufacturer_url ? ap->manufacturer_url : "",
+		ap->model_url ? ap->model_url : "");
+}
+
+
+static void wpa_supplicant_wps_event_er_ap_remove(struct wpa_supplicant *wpa_s,
+						  struct wps_event_er_ap *ap)
+{
+	char uuid_str[100];
+	uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str));
+	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_REMOVE "%s", uuid_str);
+}
+
+
+static void wpa_supplicant_wps_event_er_enrollee_add(
+	struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee)
+{
+	char uuid_str[100];
+	char dev_type[WPS_DEV_TYPE_BUFSIZE];
+
+	uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str));
+	if (enrollee->pri_dev_type)
+		wps_dev_type_bin2str(enrollee->pri_dev_type, dev_type,
+				     sizeof(dev_type));
+	else
+		dev_type[0] = '\0';
+
+	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_ADD "%s " MACSTR
+		" M1=%d config_methods=0x%x dev_passwd_id=%d pri_dev_type=%s "
+		"|%s|%s|%s|%s|%s|",
+		uuid_str, MAC2STR(enrollee->mac_addr), enrollee->m1_received,
+		enrollee->config_methods, enrollee->dev_passwd_id, dev_type,
+		enrollee->dev_name ? enrollee->dev_name : "",
+		enrollee->manufacturer ? enrollee->manufacturer : "",
+		enrollee->model_name ? enrollee->model_name : "",
+		enrollee->model_number ? enrollee->model_number : "",
+		enrollee->serial_number ? enrollee->serial_number : "");
+}
+
+
+static void wpa_supplicant_wps_event_er_enrollee_remove(
+	struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee)
+{
+	char uuid_str[100];
+	uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str));
+	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_REMOVE "%s " MACSTR,
+		uuid_str, MAC2STR(enrollee->mac_addr));
+}
+
+
+static void wpa_supplicant_wps_event_er_ap_settings(
+	struct wpa_supplicant *wpa_s,
+	struct wps_event_er_ap_settings *ap_settings)
+{
+	char uuid_str[100];
+	char key_str[65];
+	const struct wps_credential *cred = ap_settings->cred;
+
+	key_str[0] = '\0';
+	if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
+		if (cred->key_len >= 8 && cred->key_len <= 64) {
+			os_memcpy(key_str, cred->key, cred->key_len);
+			key_str[cred->key_len] = '\0';
+		}
+	}
+
+	uuid_bin2str(ap_settings->uuid, uuid_str, sizeof(uuid_str));
+	/* Use wpa_msg_ctrl to avoid showing the key in debug log */
+	wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_SETTINGS
+		     "uuid=%s ssid=%s auth_type=0x%04x encr_type=0x%04x "
+		     "key=%s",
+		     uuid_str, wpa_ssid_txt(cred->ssid, cred->ssid_len),
+		     cred->auth_type, cred->encr_type, key_str);
+}
+
+
+static void wpa_supplicant_wps_event_er_set_sel_reg(
+	struct wpa_supplicant *wpa_s,
+	struct wps_event_er_set_selected_registrar *ev)
+{
+	char uuid_str[100];
+
+	uuid_bin2str(ev->uuid, uuid_str, sizeof(uuid_str));
+	switch (ev->state) {
+	case WPS_ER_SET_SEL_REG_START:
+		wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG
+			"uuid=%s state=START sel_reg=%d dev_passwd_id=%u "
+			"sel_reg_config_methods=0x%x",
+			uuid_str, ev->sel_reg, ev->dev_passwd_id,
+			ev->sel_reg_config_methods);
+		break;
+	case WPS_ER_SET_SEL_REG_DONE:
+		wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG
+			"uuid=%s state=DONE", uuid_str);
+		break;
+	case WPS_ER_SET_SEL_REG_FAILED:
+		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_SET_SEL_REG
+			"uuid=%s state=FAILED", uuid_str);
+		break;
+	}
+}
+
+
+static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
+				     union wps_event_data *data)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	switch (event) {
+	case WPS_EV_M2D:
+		wpa_supplicant_wps_event_m2d(wpa_s, &data->m2d);
+		break;
+	case WPS_EV_FAIL:
+		wpa_supplicant_wps_event_fail(wpa_s, &data->fail);
+		break;
+	case WPS_EV_SUCCESS:
+		wpa_supplicant_wps_event_success(wpa_s);
+		break;
+	case WPS_EV_PWD_AUTH_FAIL:
+#ifdef CONFIG_AP
+		if (wpa_s->ap_iface && data->pwd_auth_fail.enrollee)
+			wpa_supplicant_ap_pwd_auth_fail(wpa_s);
+#endif /* CONFIG_AP */
+		break;
+	case WPS_EV_PBC_OVERLAP:
+		break;
+	case WPS_EV_PBC_TIMEOUT:
+		break;
+	case WPS_EV_PBC_ACTIVE:
+		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ACTIVE);
+		break;
+	case WPS_EV_PBC_DISABLE:
+		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_DISABLE);
+		break;
+	case WPS_EV_ER_AP_ADD:
+		wpa_supplicant_wps_event_er_ap_add(wpa_s, &data->ap);
+		break;
+	case WPS_EV_ER_AP_REMOVE:
+		wpa_supplicant_wps_event_er_ap_remove(wpa_s, &data->ap);
+		break;
+	case WPS_EV_ER_ENROLLEE_ADD:
+		wpa_supplicant_wps_event_er_enrollee_add(wpa_s,
+							 &data->enrollee);
+		break;
+	case WPS_EV_ER_ENROLLEE_REMOVE:
+		wpa_supplicant_wps_event_er_enrollee_remove(wpa_s,
+							    &data->enrollee);
+		break;
+	case WPS_EV_ER_AP_SETTINGS:
+		wpa_supplicant_wps_event_er_ap_settings(wpa_s,
+							&data->ap_settings);
+		break;
+	case WPS_EV_ER_SET_SELECTED_REGISTRAR:
+		wpa_supplicant_wps_event_er_set_sel_reg(wpa_s,
+							&data->set_sel_reg);
+		break;
+	case WPS_EV_AP_PIN_SUCCESS:
+		break;
+	}
+}
+
+
+static int wpa_supplicant_wps_rf_band(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	if (!wpa_s->current_ssid || !wpa_s->assoc_freq)
+		return 0;
+
+	return (wpa_s->assoc_freq > 50000) ? WPS_RF_60GHZ :
+		(wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ;
+}
+
+
+enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid)
+{
+	if (eap_is_wps_pbc_enrollee(&ssid->eap) ||
+	    eap_is_wps_pin_enrollee(&ssid->eap))
+		return WPS_REQ_ENROLLEE;
+	else
+		return WPS_REQ_REGISTRAR;
+}
+
+
+static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
+{
+	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;
+	while (ssid) {
+		if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+			if (ssid == wpa_s->current_ssid) {
+				wpa_s->own_disconnect_req = 1;
+				wpa_supplicant_deauthenticate(
+					wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+			}
+			id = ssid->id;
+			remove_ssid = ssid;
+		} else
+			id = -1;
+		ssid = ssid->next;
+		if (id >= 0) {
+			if (prev_current == remove_ssid) {
+				wpa_sm_set_config(wpa_s->wpa, NULL);
+				eapol_sm_notify_config(wpa_s->eapol, NULL,
+						       NULL);
+			}
+			wpas_notify_network_removed(wpa_s, remove_ssid);
+			wpa_config_remove_network(wpa_s->conf, id);
+		}
+	}
+
+	wpas_wps_clear_ap_info(wpa_s);
+}
+
+
+static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	union wps_event_data data;
+
+	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
+		"out");
+	os_memset(&data, 0, sizeof(data));
+	data.fail.config_error = WPS_CFG_MSG_TIMEOUT;
+	data.fail.error_indication = WPS_EI_NO_ERROR;
+	/*
+	 * Call wpas_notify_wps_event_fail() directly instead of through
+	 * wpa_supplicant_wps_event() which would end up registering unnecessary
+	 * timeouts (those are only for the case where the failure happens
+	 * during an EAP-WSC exchange).
+	 */
+	wpas_notify_wps_event_fail(wpa_s, &data.fail);
+	wpas_clear_wps(wpa_s);
+}
+
+
+static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
+					      int registrar, const u8 *dev_addr,
+					      const u8 *bssid)
+{
+	struct wpa_ssid *ssid;
+
+	ssid = wpa_config_add_network(wpa_s->conf);
+	if (ssid == NULL)
+		return NULL;
+	wpas_notify_network_added(wpa_s, ssid);
+	wpa_config_set_network_defaults(ssid);
+	ssid->temporary = 1;
+	if (wpa_config_set(ssid, "key_mgmt", "WPS", 0) < 0 ||
+	    wpa_config_set(ssid, "eap", "WSC", 0) < 0 ||
+	    wpa_config_set(ssid, "identity", registrar ?
+			   "\"" WSC_ID_REGISTRAR "\"" :
+			   "\"" WSC_ID_ENROLLEE "\"", 0) < 0) {
+		wpas_notify_network_removed(wpa_s, ssid);
+		wpa_config_remove_network(wpa_s->conf, ssid->id);
+		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;
+		int count = 0;
+#endif /* CONFIG_P2P */
+
+		os_memcpy(ssid->bssid, bssid, ETH_ALEN);
+		ssid->bssid_set = 1;
+
+		/*
+		 * Note: With P2P, the SSID may change at the time the WPS
+		 * provisioning is started, so better not filter the AP based
+		 * on the current SSID in the scan results.
+		 */
+#ifndef CONFIG_P2P
+		dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+			if (os_memcmp(bssid, bss->bssid, ETH_ALEN) != 0)
+				continue;
+
+			os_free(ssid->ssid);
+			ssid->ssid = os_malloc(bss->ssid_len);
+			if (ssid->ssid == NULL)
+				break;
+			os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+			ssid->ssid_len = bss->ssid_len;
+			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Picked SSID from "
+					  "scan results",
+					  ssid->ssid, ssid->ssid_len);
+			count++;
+		}
+
+		if (count > 1) {
+			wpa_printf(MSG_DEBUG, "WPS: More than one SSID found "
+				   "for the AP; use wildcard");
+			os_free(ssid->ssid);
+			ssid->ssid = NULL;
+			ssid->ssid_len = 0;
+		}
+#endif /* CONFIG_P2P */
+	}
+
+	return ssid;
+}
+
+
+static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s,
+				  struct wpa_ssid *selected)
+{
+	struct wpa_ssid *ssid;
+
+	if (wpa_s->current_ssid) {
+		wpa_s->own_disconnect_req = 1;
+		wpa_supplicant_deauthenticate(
+			wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+	}
+
+	/* Mark all other networks disabled and trigger reassociation */
+	ssid = wpa_s->conf->ssid;
+	while (ssid) {
+		int was_disabled = ssid->disabled;
+		ssid->disabled_for_connect = 0;
+		/*
+		 * In case the network object corresponds to a persistent group
+		 * then do not send out network disabled signal. In addition,
+		 * do not change disabled status of persistent network objects
+		 * from 2 to 1 should we connect to another network.
+		 */
+		if (was_disabled != 2) {
+			ssid->disabled = ssid != selected;
+			if (was_disabled != ssid->disabled) {
+				if (ssid->disabled)
+					ssid->disabled_for_connect = 1;
+				wpas_notify_network_enabled_changed(wpa_s,
+								    ssid);
+			}
+		}
+		ssid = ssid->next;
+	}
+}
+
+
+static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
+			     struct wpa_ssid *selected, const u8 *bssid,
+			     int freq)
+{
+	struct wpa_bss *bss;
+
+	wpa_s->wps_run++;
+	if (wpa_s->wps_run == 0)
+		wpa_s->wps_run++;
+	wpa_s->after_wps = 0;
+	wpa_s->known_wps_freq = 0;
+	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;
+			wpa_s->wps_freq = bss->freq;
+		}
+	}
+
+	wpas_wps_temp_disable(wpa_s, selected);
+
+	wpa_s->disconnected = 0;
+	wpa_s->reassociate = 1;
+	wpa_s->scan_runs = 0;
+	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);
+}
+
+
+int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+		       int p2p_group)
+{
+	struct wpa_ssid *ssid;
+
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface) {
+		wpa_printf(MSG_DEBUG,
+			   "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+		return -1;
+	}
+#endif /* CONFIG_AP */
+	wpas_clear_wps(wpa_s);
+	ssid = wpas_wps_add_network(wpa_s, 0, NULL, bssid);
+	if (ssid == NULL)
+		return -1;
+	ssid->temporary = 1;
+	ssid->p2p_group = p2p_group;
+#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);
+		if (ssid->ssid) {
+			ssid->ssid_len = wpa_s->go_params->ssid_len;
+			os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
+				  ssid->ssid_len);
+			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
+					  "SSID", ssid->ssid, ssid->ssid_len);
+		}
+	}
+#endif /* CONFIG_P2P */
+	if (wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0) < 0)
+		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);
+	wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
+	return 0;
+}
+
+
+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 + 2 * WPS_OOB_PUBKEY_HASH_LEN];
+	unsigned int rpin = 0;
+	char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10];
+
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface) {
+		wpa_printf(MSG_DEBUG,
+			   "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+		return -1;
+	}
+#endif /* CONFIG_AP */
+	wpas_clear_wps(wpa_s);
+	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) {
+		os_free(ssid->ssid);
+		ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
+		if (ssid->ssid) {
+			ssid->ssid_len = wpa_s->go_params->ssid_len;
+			os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
+				  ssid->ssid_len);
+			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
+					  "SSID", ssid->ssid, ssid->ssid_len);
+		}
+	}
+#endif /* CONFIG_P2P */
+	if (pin)
+		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%s\"",
+			    rpin, dev_pw_id, hash);
+	}
+	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, 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)
+{
+	os_get_reltime(&wpa_s->wps_pin_start_time);
+	return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group,
+				     dev_pw_id, NULL, NULL, 0, 0);
+}
+
+
+void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+	union wps_event_data data;
+
+	os_memset(&data, 0, sizeof(data));
+	data.fail.config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+	data.fail.error_indication = WPS_EI_NO_ERROR;
+	/*
+	 * Call wpas_notify_wps_event_fail() directly instead of through
+	 * wpa_supplicant_wps_event() which would end up registering unnecessary
+	 * timeouts (those are only for the case where the failure happens
+	 * during an EAP-WSC exchange).
+	 */
+	wpas_notify_wps_event_fail(wpa_s, &data.fail);
+}
+
+/* Cancel the wps pbc/pin requests */
+int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface) {
+		wpa_printf(MSG_DEBUG, "WPS: Cancelling in AP mode");
+		return wpa_supplicant_ap_wps_cancel(wpa_s);
+	}
+#endif /* CONFIG_AP */
+
+	if (wpa_s->wpa_state == WPA_SCANNING ||
+	    wpa_s->wpa_state == WPA_DISCONNECTED) {
+		wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan");
+		wpa_supplicant_cancel_scan(wpa_s);
+		wpas_clear_wps(wpa_s);
+	} else if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
+		wpa_printf(MSG_DEBUG, "WPS: Cancel operation - "
+			   "deauthenticate");
+		wpa_s->own_disconnect_req = 1;
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+		wpas_clear_wps(wpa_s);
+	} 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;
+}
+
+
+int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
+		       const char *pin, struct wps_new_ap_settings *settings)
+{
+	struct wpa_ssid *ssid;
+	char val[200];
+	char *pos, *end;
+	int res;
+
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface) {
+		wpa_printf(MSG_DEBUG,
+			   "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+		return -1;
+	}
+#endif /* CONFIG_AP */
+	if (!pin)
+		return -1;
+	wpas_clear_wps(wpa_s);
+	ssid = wpas_wps_add_network(wpa_s, 1, NULL, bssid);
+	if (ssid == NULL)
+		return -1;
+	ssid->temporary = 1;
+	pos = val;
+	end = pos + sizeof(val);
+	res = os_snprintf(pos, end - pos, "\"pin=%s", pin);
+	if (os_snprintf_error(end - pos, res))
+		return -1;
+	pos += res;
+	if (settings) {
+		res = os_snprintf(pos, end - pos, " new_ssid=%s new_auth=%s "
+				  "new_encr=%s new_key=%s",
+				  settings->ssid_hex, settings->auth,
+				  settings->encr, settings->key_hex);
+		if (os_snprintf_error(end - pos, res))
+			return -1;
+		pos += res;
+	}
+	res = os_snprintf(pos, end - pos, "\"");
+	if (os_snprintf_error(end - pos, res))
+		return -1;
+	if (wpa_config_set(ssid, "phase1", val, 0) < 0)
+		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);
+	wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
+	return 0;
+}
+
+
+static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr,
+			       const u8 *p2p_dev_addr, const u8 *psk,
+			       size_t psk_len)
+{
+	if (is_zero_ether_addr(p2p_dev_addr)) {
+		wpa_printf(MSG_DEBUG,
+			   "Received new WPA/WPA2-PSK from WPS for STA " MACSTR,
+			   MAC2STR(mac_addr));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "Received new WPA/WPA2-PSK from WPS for STA " MACSTR
+			   " P2P Device Addr " MACSTR,
+			   MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
+	}
+	wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
+
+	/* TODO */
+
+	return 0;
+}
+
+
+static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
+				   const struct wps_device_data *dev)
+{
+	char uuid[40], txt[400];
+	int len;
+	char devtype[WPS_DEV_TYPE_BUFSIZE];
+	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+		return;
+	wpa_printf(MSG_DEBUG, "WPS: PIN needed for UUID-E %s", uuid);
+	len = os_snprintf(txt, sizeof(txt), "WPS-EVENT-PIN-NEEDED %s " MACSTR
+			  " [%s|%s|%s|%s|%s|%s]",
+			  uuid, MAC2STR(dev->mac_addr), dev->device_name,
+			  dev->manufacturer, dev->model_name,
+			  dev->model_number, dev->serial_number,
+			  wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+					       sizeof(devtype)));
+	if (!os_snprintf_error(sizeof(txt), len))
+		wpa_printf(MSG_INFO, "%s", txt);
+}
+
+
+static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id,
+				    u16 sel_reg_config_methods)
+{
+#ifdef CONFIG_WPS_ER
+	struct wpa_supplicant *wpa_s = ctx;
+
+	if (wpa_s->wps_er == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar - sel_reg=%d "
+		   "dev_password_id=%u sel_reg_config_methods=0x%x",
+		   sel_reg, dev_passwd_id, sel_reg_config_methods);
+	wps_er_set_sel_reg(wpa_s->wps_er, sel_reg, dev_passwd_id,
+			   sel_reg_config_methods);
+#endif /* CONFIG_WPS_ER */
+}
+
+
+static u16 wps_fix_config_methods(u16 config_methods)
+{
+	if ((config_methods &
+	     (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
+	      WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
+		wpa_printf(MSG_INFO, "WPS: Converting display to "
+			   "virtual_display for WPS 2.0 compliance");
+		config_methods |= WPS_CONFIG_VIRT_DISPLAY;
+	}
+	if ((config_methods &
+	     (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
+	      WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
+		wpa_printf(MSG_INFO, "WPS: Converting push_button to "
+			   "virtual_push_button for WPS 2.0 compliance");
+		config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+	}
+
+	return config_methods;
+}
+
+
+static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
+			      struct wps_context *wps)
+{
+	char buf[50];
+	const char *src;
+
+	if (is_nil_uuid(wpa_s->conf->uuid)) {
+		struct wpa_supplicant *first;
+		first = wpa_s->global->ifaces;
+		while (first && first->next)
+			first = first->next;
+		if (first && first != wpa_s) {
+			if (wps != wpa_s->global->ifaces->wps)
+				os_memcpy(wps->uuid,
+					  wpa_s->global->ifaces->wps->uuid,
+					  WPS_UUID_LEN);
+			src = "from the first interface";
+		} else {
+			uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
+			src = "based on MAC address";
+		}
+	} else {
+		os_memcpy(wps->uuid, wpa_s->conf->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);
+}
+
+
+static void wpas_wps_set_vendor_ext_m1(struct wpa_supplicant *wpa_s,
+				       struct wps_context *wps)
+{
+	wpabuf_free(wps->dev.vendor_ext_m1);
+	wps->dev.vendor_ext_m1 = NULL;
+
+	if (wpa_s->conf->wps_vendor_ext_m1) {
+		wps->dev.vendor_ext_m1 =
+			wpabuf_dup(wpa_s->conf->wps_vendor_ext_m1);
+		if (!wps->dev.vendor_ext_m1) {
+			wpa_printf(MSG_ERROR, "WPS: Cannot "
+				   "allocate memory for vendor_ext_m1");
+		}
+	}
+}
+
+
+int wpas_wps_init(struct wpa_supplicant *wpa_s)
+{
+	struct wps_context *wps;
+	struct wps_registrar_config rcfg;
+	struct hostapd_hw_modes *modes;
+	u16 m;
+
+	wps = os_zalloc(sizeof(*wps));
+	if (wps == NULL)
+		return -1;
+
+	wps->cred_cb = wpa_supplicant_wps_cred;
+	wps->event_cb = wpa_supplicant_wps_event;
+	wps->rf_band_cb = wpa_supplicant_wps_rf_band;
+	wps->cb_ctx = wpa_s;
+
+	wps->dev.device_name = wpa_s->conf->device_name;
+	wps->dev.manufacturer = wpa_s->conf->manufacturer;
+	wps->dev.model_name = wpa_s->conf->model_name;
+	wps->dev.model_number = wpa_s->conf->model_number;
+	wps->dev.serial_number = wpa_s->conf->serial_number;
+	wps->config_methods =
+		wps_config_methods_str2bin(wpa_s->conf->config_methods);
+	if ((wps->config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) ==
+	    (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) {
+		wpa_printf(MSG_ERROR, "WPS: Both Label and Display config "
+			   "methods are not allowed at the same time");
+		os_free(wps);
+		return -1;
+	}
+	wps->config_methods = wps_fix_config_methods(wps->config_methods);
+	wps->dev.config_methods = wps->config_methods;
+	os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
+		  WPS_DEV_TYPE_LEN);
+
+	wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+	os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
+		  WPS_DEV_TYPE_LEN * wps->dev.num_sec_dev_types);
+
+	wpas_wps_set_vendor_ext_m1(wpa_s, wps);
+
+	wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
+	modes = wpa_s->hw.modes;
+	if (modes) {
+		for (m = 0; m < wpa_s->hw.num_modes; m++) {
+			if (modes[m].mode == HOSTAPD_MODE_IEEE80211B ||
+			    modes[m].mode == HOSTAPD_MODE_IEEE80211G)
+				wps->dev.rf_bands |= WPS_RF_24GHZ;
+			else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A)
+				wps->dev.rf_bands |= WPS_RF_50GHZ;
+			else if (modes[m].mode == HOSTAPD_MODE_IEEE80211AD)
+				wps->dev.rf_bands |= WPS_RF_60GHZ;
+		}
+	}
+	if (wps->dev.rf_bands == 0) {
+		/*
+		 * Default to claiming support for both bands if the driver
+		 * does not provide support for fetching supported bands.
+		 */
+		wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ;
+	}
+	os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN);
+	wpas_wps_set_uuid(wpa_s, wps);
+
+	wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
+	wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
+
+	os_memset(&rcfg, 0, sizeof(rcfg));
+	rcfg.new_psk_cb = wpas_wps_new_psk_cb;
+	rcfg.pin_needed_cb = wpas_wps_pin_needed_cb;
+	rcfg.set_sel_reg_cb = wpas_wps_set_sel_reg_cb;
+	rcfg.cb_ctx = wpa_s;
+
+	wps->registrar = wps_registrar_init(wps, &rcfg);
+	if (wps->registrar == NULL) {
+		wpa_printf(MSG_DEBUG, "Failed to initialize WPS Registrar");
+		os_free(wps);
+		return -1;
+	}
+
+	wpa_s->wps = wps;
+
+	return 0;
+}
+
+
+#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);
+	wpabuf_free(wpa_s->wps->dh_pubkey);
+	wpabuf_free(wpa_s->wps->dh_privkey);
+	wpabuf_free(wpa_s->wps->dev.vendor_ext_m1);
+	os_free(wpa_s->wps->network_key);
+	os_free(wpa_s->wps);
+	wpa_s->wps = NULL;
+}
+
+
+int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
+			    struct wpa_ssid *ssid, struct wpa_bss *bss)
+{
+	struct wpabuf *wps_ie;
+
+	if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
+		return -1;
+
+	wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+	if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
+		if (!wps_ie) {
+			wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
+			return 0;
+		}
+
+		if (!wps_is_selected_pbc_registrar(wps_ie)) {
+			wpa_printf(MSG_DEBUG, "   skip - WPS AP "
+				   "without active PBC Registrar");
+			wpabuf_free(wps_ie);
+			return 0;
+		}
+
+		/* TODO: overlap detection */
+		wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
+			   "(Active PBC)");
+		wpabuf_free(wps_ie);
+		return 1;
+	}
+
+	if (eap_is_wps_pin_enrollee(&ssid->eap)) {
+		if (!wps_ie) {
+			wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
+			return 0;
+		}
+
+		/*
+		 * Start with WPS APs that advertise our address as an
+		 * authorized MAC (v2.0) or active PIN Registrar (v1.0) and
+		 * allow any WPS AP after couple of scans since some APs do not
+		 * set Selected Registrar attribute properly when using
+		 * external Registrar.
+		 */
+		if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
+			struct os_reltime age;
+
+			os_reltime_age(&wpa_s->wps_pin_start_time, &age);
+
+			if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG ||
+			    age.sec < WPS_PIN_TIME_IGNORE_SEL_REG) {
+				wpa_printf(MSG_DEBUG,
+					   "   skip - WPS AP without active PIN Registrar (scan_runs=%d age=%d)",
+					   wpa_s->scan_runs, (int) age.sec);
+				wpabuf_free(wps_ie);
+				return 0;
+			}
+			wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
+		} else {
+			wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
+				   "(Authorized MAC or Active PIN)");
+		}
+		wpabuf_free(wps_ie);
+		return 1;
+	}
+
+	if (wps_ie) {
+		wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
+		wpabuf_free(wps_ie);
+		return 1;
+	}
+
+	return -1;
+}
+
+
+int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid,
+			      struct wpa_bss *bss)
+{
+	struct wpabuf *wps_ie = NULL;
+	int ret = 0;
+
+	if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
+		wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+		if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) {
+			/* allow wildcard SSID for WPS PBC */
+			ret = 1;
+		}
+	} else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
+		wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+		if (wps_ie &&
+		    (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) ||
+		     wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) {
+			/* allow wildcard SSID for WPS PIN */
+			ret = 1;
+		}
+	}
+
+	if (!ret && ssid->bssid_set &&
+	    os_memcmp(ssid->bssid, bss->bssid, ETH_ALEN) == 0) {
+		/* allow wildcard SSID due to hardcoded BSSID match */
+		ret = 1;
+	}
+
+#ifdef CONFIG_WPS_STRICT
+	if (wps_ie) {
+		if (wps_validate_beacon_probe_resp(wps_ie, bss->beacon_ie_len >
+						   0, bss->bssid) < 0)
+			ret = 0;
+		if (bss->beacon_ie_len) {
+			struct wpabuf *bcn_wps;
+			bcn_wps = wpa_bss_get_vendor_ie_multi_beacon(
+				bss, WPS_IE_VENDOR_TYPE);
+			if (bcn_wps == NULL) {
+				wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE "
+					   "missing from AP Beacon");
+				ret = 0;
+			} else {
+				if (wps_validate_beacon(wps_ie) < 0)
+					ret = 0;
+				wpabuf_free(bcn_wps);
+			}
+		}
+	}
+#endif /* CONFIG_WPS_STRICT */
+
+	wpabuf_free(wps_ie);
+
+	return ret;
+}
+
+
+int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
+			      struct wpa_bss *selected, struct wpa_ssid *ssid)
+{
+	const u8 *sel_uuid;
+	struct wpabuf *wps_ie;
+	int ret = 0;
+	size_t i;
+
+	if (!eap_is_wps_pbc_enrollee(&ssid->eap))
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "WPS: Check whether PBC session overlap is "
+		   "present in scan results; selected BSSID " MACSTR,
+		   MAC2STR(selected->bssid));
+
+	/* Make sure that only one AP is in active PBC mode */
+	wps_ie = wpa_bss_get_vendor_ie_multi(selected, WPS_IE_VENDOR_TYPE);
+	if (wps_ie) {
+		sel_uuid = wps_get_uuid_e(wps_ie);
+		wpa_hexdump(MSG_DEBUG, "WPS: UUID of the selected BSS",
+			    sel_uuid, UUID_LEN);
+	} else {
+		wpa_printf(MSG_DEBUG, "WPS: Selected BSS does not include "
+			   "WPS IE?!");
+		sel_uuid = NULL;
+	}
+
+	for (i = 0; i < wpa_s->num_wps_ap; i++) {
+		struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+
+		if (!ap->pbc_active ||
+		    os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: "
+			   MACSTR, MAC2STR(ap->bssid));
+		wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
+			    ap->uuid, UUID_LEN);
+		if (sel_uuid == NULL ||
+		    os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0) {
+			ret = 1; /* PBC overlap */
+			wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: "
+				MACSTR " and " MACSTR,
+				MAC2STR(selected->bssid),
+				MAC2STR(ap->bssid));
+			break;
+		}
+
+		/* TODO: verify that this is reasonable dual-band situation */
+	}
+
+	wpabuf_free(wps_ie);
+
+	return ret;
+}
+
+
+void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss;
+	unsigned int pbc = 0, auth = 0, pin = 0, wps = 0;
+
+	if (wpa_s->disconnected || wpa_s->wpa_state >= WPA_ASSOCIATED)
+		return;
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		struct wpabuf *ie;
+		ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+		if (!ie)
+			continue;
+		if (wps_is_selected_pbc_registrar(ie))
+			pbc++;
+		else if (wps_is_addr_authorized(ie, wpa_s->own_addr, 0))
+			auth++;
+		else if (wps_is_selected_pin_registrar(ie))
+			pin++;
+		else
+			wps++;
+		wpabuf_free(ie);
+	}
+
+	if (pbc)
+		wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PBC);
+	else if (auth)
+		wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_AUTH);
+	else if (pin)
+		wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PIN);
+	else if (wps)
+		wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE);
+}
+
+
+int wpas_wps_searching(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *ssid;
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !ssid->disabled)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+			      char *end)
+{
+	struct wpabuf *wps_ie;
+	int ret;
+
+	wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_DEV_OUI_WFA);
+	if (wps_ie == NULL)
+		return 0;
+
+	ret = wps_attr_text(wps_ie, buf, end);
+	wpabuf_free(wps_ie);
+	return ret;
+}
+
+
+int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter)
+{
+#ifdef CONFIG_WPS_ER
+	if (wpa_s->wps_er) {
+		wps_er_refresh(wpa_s->wps_er);
+		return 0;
+	}
+	wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname, filter);
+	if (wpa_s->wps_er == NULL)
+		return -1;
+	return 0;
+#else /* CONFIG_WPS_ER */
+	return 0;
+#endif /* CONFIG_WPS_ER */
+}
+
+
+void wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WPS_ER
+	wps_er_deinit(wpa_s->wps_er, NULL, NULL);
+	wpa_s->wps_er = NULL;
+#endif /* CONFIG_WPS_ER */
+}
+
+
+#ifdef CONFIG_WPS_ER
+int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr,
+			const char *uuid, const char *pin)
+{
+	u8 u[UUID_LEN];
+	const u8 *use_uuid = NULL;
+	u8 addr_buf[ETH_ALEN];
+
+	if (os_strcmp(uuid, "any") == 0) {
+	} else if (uuid_str2bin(uuid, u) == 0) {
+		use_uuid = u;
+	} else if (hwaddr_aton(uuid, addr_buf) == 0) {
+		use_uuid = wps_er_get_sta_uuid(wpa_s->wps_er, addr_buf);
+		if (use_uuid == NULL)
+			return -1;
+	} else
+		return -1;
+	return wps_registrar_add_pin(wpa_s->wps->registrar, addr,
+				     use_uuid,
+				     (const u8 *) pin, os_strlen(pin), 300);
+}
+
+
+int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid)
+{
+	u8 u[UUID_LEN], *use_uuid = NULL;
+	u8 addr[ETH_ALEN], *use_addr = NULL;
+
+	if (uuid_str2bin(uuid, u) == 0)
+		use_uuid = u;
+	else if (hwaddr_aton(uuid, addr) == 0)
+		use_addr = addr;
+	else
+		return -1;
+	return wps_er_pbc(wpa_s->wps_er, use_uuid, use_addr);
+}
+
+
+int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid,
+		      const char *pin)
+{
+	u8 u[UUID_LEN], *use_uuid = NULL;
+	u8 addr[ETH_ALEN], *use_addr = NULL;
+
+	if (uuid_str2bin(uuid, u) == 0)
+		use_uuid = u;
+	else if (hwaddr_aton(uuid, addr) == 0)
+		use_addr = addr;
+	else
+		return -1;
+
+	return wps_er_learn(wpa_s->wps_er, use_uuid, use_addr, (const u8 *) pin,
+			    os_strlen(pin));
+}
+
+
+static int wpas_wps_network_to_cred(struct wpa_ssid *ssid,
+				    struct wps_credential *cred)
+{
+	os_memset(cred, 0, sizeof(*cred));
+	if (ssid->ssid_len > SSID_MAX_LEN)
+		return -1;
+	os_memcpy(cred->ssid, ssid->ssid, ssid->ssid_len);
+	cred->ssid_len = ssid->ssid_len;
+	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
+		cred->auth_type = (ssid->proto & WPA_PROTO_RSN) ?
+			WPS_AUTH_WPA2PSK : WPS_AUTH_WPAPSK;
+		if (ssid->pairwise_cipher & WPA_CIPHER_CCMP)
+			cred->encr_type = WPS_ENCR_AES;
+		else
+			cred->encr_type = WPS_ENCR_TKIP;
+		if (ssid->passphrase) {
+			cred->key_len = os_strlen(ssid->passphrase);
+			if (cred->key_len >= 64)
+				return -1;
+			os_memcpy(cred->key, ssid->passphrase, cred->key_len);
+		} else if (ssid->psk_set) {
+			cred->key_len = 32;
+			os_memcpy(cred->key, ssid->psk, 32);
+		} else
+			return -1;
+	} else {
+		cred->auth_type = WPS_AUTH_OPEN;
+		cred->encr_type = WPS_ENCR_NONE;
+	}
+
+	return 0;
+}
+
+
+int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
+			   int id)
+{
+	u8 u[UUID_LEN], *use_uuid = NULL;
+	u8 addr[ETH_ALEN], *use_addr = NULL;
+	struct wpa_ssid *ssid;
+	struct wps_credential cred;
+	int ret;
+
+	if (uuid_str2bin(uuid, u) == 0)
+		use_uuid = u;
+	else if (hwaddr_aton(uuid, addr) == 0)
+		use_addr = addr;
+	else
+		return -1;
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid == NULL || ssid->ssid == NULL)
+		return -1;
+
+	if (wpas_wps_network_to_cred(ssid, &cred) < 0)
+		return -1;
+	ret = wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred);
+	os_memset(&cred, 0, sizeof(cred));
+	return ret;
+}
+
+
+int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
+		       const char *pin, struct wps_new_ap_settings *settings)
+{
+	u8 u[UUID_LEN], *use_uuid = NULL;
+	u8 addr[ETH_ALEN], *use_addr = NULL;
+	struct wps_credential cred;
+	size_t len;
+
+	if (uuid_str2bin(uuid, u) == 0)
+		use_uuid = u;
+	else if (hwaddr_aton(uuid, addr) == 0)
+		use_addr = addr;
+	else
+		return -1;
+	if (settings->ssid_hex == NULL || settings->auth == NULL ||
+	    settings->encr == NULL || settings->key_hex == NULL)
+		return -1;
+
+	os_memset(&cred, 0, sizeof(cred));
+	len = os_strlen(settings->ssid_hex);
+	if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
+	    hexstr2bin(settings->ssid_hex, cred.ssid, len / 2))
+		return -1;
+	cred.ssid_len = len / 2;
+
+	len = os_strlen(settings->key_hex);
+	if ((len & 1) || len > 2 * sizeof(cred.key) ||
+	    hexstr2bin(settings->key_hex, cred.key, len / 2))
+		return -1;
+	cred.key_len = len / 2;
+
+	if (os_strcmp(settings->auth, "OPEN") == 0)
+		cred.auth_type = WPS_AUTH_OPEN;
+	else if (os_strcmp(settings->auth, "WPAPSK") == 0)
+		cred.auth_type = WPS_AUTH_WPAPSK;
+	else if (os_strcmp(settings->auth, "WPA2PSK") == 0)
+		cred.auth_type = WPS_AUTH_WPA2PSK;
+	else
+		return -1;
+
+	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)
+		cred.encr_type = WPS_ENCR_AES;
+	else
+		return -1;
+
+	return wps_er_config(wpa_s->wps_er, use_uuid, use_addr,
+			     (const u8 *) pin, os_strlen(pin), &cred);
+}
+
+
+#ifdef CONFIG_WPS_NFC
+struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
+					     int ndef, const char *uuid)
+{
+	struct wpabuf *ret;
+	u8 u[UUID_LEN], *use_uuid = NULL;
+	u8 addr[ETH_ALEN], *use_addr = NULL;
+
+	if (!wpa_s->wps_er)
+		return NULL;
+
+	if (uuid_str2bin(uuid, u) == 0)
+		use_uuid = u;
+	else if (hwaddr_aton(uuid, addr) == 0)
+		use_addr = addr;
+	else
+		return NULL;
+
+	ret = wps_er_nfc_config_token(wpa_s->wps_er, use_uuid, use_addr);
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+}
+#endif /* CONFIG_WPS_NFC */
+
+
+static int callbacks_pending = 0;
+
+static void wpas_wps_terminate_cb(void *ctx)
+{
+	wpa_printf(MSG_DEBUG, "WPS ER: Terminated");
+	if (--callbacks_pending <= 0)
+		eloop_terminate();
+}
+#endif /* CONFIG_WPS_ER */
+
+
+int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WPS_ER
+	if (wpa_s->wps_er) {
+		callbacks_pending++;
+		wps_er_deinit(wpa_s->wps_er, wpas_wps_terminate_cb, wpa_s);
+		wpa_s->wps_er = NULL;
+		return 1;
+	}
+#endif /* CONFIG_WPS_ER */
+	return 0;
+}
+
+
+void wpas_wps_update_config(struct wpa_supplicant *wpa_s)
+{
+	struct wps_context *wps = wpa_s->wps;
+
+	if (wps == NULL)
+		return;
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS) {
+		wps->config_methods = wps_config_methods_str2bin(
+			wpa_s->conf->config_methods);
+		if ((wps->config_methods &
+		     (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) ==
+		    (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) {
+			wpa_printf(MSG_ERROR, "WPS: Both Label and Display "
+				   "config methods are not allowed at the "
+				   "same time");
+			wps->config_methods &= ~WPS_CONFIG_LABEL;
+		}
+	}
+	wps->config_methods = wps_fix_config_methods(wps->config_methods);
+	wps->dev.config_methods = wps->config_methods;
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
+		os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
+			  WPS_DEV_TYPE_LEN);
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) {
+		wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+		os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
+			  wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN);
+	}
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION)
+		wpas_wps_set_vendor_ext_m1(wpa_s, wps);
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION)
+		wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID)
+		wpas_wps_set_uuid(wpa_s, wps);
+
+	if (wpa_s->conf->changed_parameters &
+	    (CFG_CHANGED_DEVICE_NAME | CFG_CHANGED_WPS_STRING)) {
+		/* Update pointers to make sure they refer current values */
+		wps->dev.device_name = wpa_s->conf->device_name;
+		wps->dev.manufacturer = wpa_s->conf->manufacturer;
+		wps->dev.model_name = wpa_s->conf->model_name;
+		wps->dev.model_number = wpa_s->conf->model_number;
+		wps->dev.serial_number = wpa_s->conf->serial_number;
+	}
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+#ifdef CONFIG_WPS_ER
+static struct wpabuf *
+wpas_wps_network_config_token(struct wpa_supplicant *wpa_s, int ndef,
+			      struct wpa_ssid *ssid)
+{
+	struct wpabuf *ret;
+	struct wps_credential cred;
+
+	if (wpas_wps_network_to_cred(ssid, &cred) < 0)
+		return NULL;
+
+	ret = wps_er_config_token_from_cred(wpa_s->wps, &cred);
+
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+}
+#endif /* CONFIG_WPS_ER */
+
+
+struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
+					  int ndef, const char *id_str)
+{
+#ifdef CONFIG_WPS_ER
+	if (id_str) {
+		int id;
+		char *end = NULL;
+		struct wpa_ssid *ssid;
+
+		id = strtol(id_str, &end, 10);
+		if (end && *end)
+			return NULL;
+
+		ssid = wpa_config_get_network(wpa_s->conf, id);
+		if (ssid == NULL)
+			return NULL;
+		return wpas_wps_network_config_token(wpa_s, ndef, ssid);
+	}
+#endif /* CONFIG_WPS_ER */
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface)
+		return wpas_ap_wps_nfc_config_token(wpa_s, ndef);
+#endif /* CONFIG_AP */
+	return NULL;
+}
+
+
+struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef)
+{
+	if (wpa_s->conf->wps_nfc_pw_from_config) {
+		return wps_nfc_token_build(ndef,
+					   wpa_s->conf->wps_nfc_dev_pw_id,
+					   wpa_s->conf->wps_nfc_dh_pubkey,
+					   wpa_s->conf->wps_nfc_dev_pw);
+	}
+
+	return wps_nfc_token_gen(ndef, &wpa_s->conf->wps_nfc_dev_pw_id,
+				 &wpa_s->conf->wps_nfc_dh_pubkey,
+				 &wpa_s->conf->wps_nfc_dh_privkey,
+				 &wpa_s->conf->wps_nfc_dev_pw);
+}
+
+
+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_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);
+	wpabuf_free(wps->dh_privkey);
+	wps->dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+	wps->dh_pubkey = wpabuf_dup(wpa_s->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;
+		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);
+	if (wps->dh_ctx == NULL) {
+		wpabuf_free(wps->dh_pubkey);
+		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;
+	}
+
+	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)
+{
+	/*
+	 * Disable existing networks temporarily to allow the newly learned
+	 * credential to be preferred. Enable the temporarily disabled networks
+	 * after 10 seconds.
+	 */
+	wpas_wps_temp_disable(wpa_s, NULL);
+	eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
+			       NULL);
+
+	if (wps_oob_use_cred(wpa_s->wps, attr) < 0)
+		return -1;
+
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+		return 0;
+
+	if (attr->ap_channel) {
+		u16 chan = WPA_GET_BE16(attr->ap_channel);
+		int freq = 0;
+
+		if (chan >= 1 && chan <= 13)
+			freq = 2407 + 5 * chan;
+		else if (chan == 14)
+			freq = 2484;
+		else if (chan >= 30)
+			freq = 5000 + 5 * chan;
+
+		if (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;
+}
+
+
+#ifdef CONFIG_WPS_ER
+static int wpas_wps_add_nfc_password_token(struct wpa_supplicant *wpa_s,
+					   struct wps_parse_attr *attr)
+{
+	return wps_registrar_add_nfc_password_token(
+		wpa_s->wps->registrar, attr->oob_dev_password,
+		attr->oob_dev_password_len);
+}
+#endif /* CONFIG_WPS_ER */
+
+
+static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s,
+				    const struct wpabuf *wps)
+{
+	struct wps_parse_attr attr;
+
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
+
+	if (wps_parse_msg(wps, &attr)) {
+		wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
+		return -1;
+	}
+
+	if (attr.num_cred)
+		return wpas_wps_use_cred(wpa_s, &attr);
+
+#ifdef CONFIG_WPS_ER
+	if (attr.oob_dev_password)
+		return wpas_wps_add_nfc_password_token(wpa_s, &attr);
+#endif /* CONFIG_WPS_ER */
+
+	wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
+	return -1;
+}
+
+
+int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
+			  const struct wpabuf *data, int forced_freq)
+{
+	const struct wpabuf *wps = data;
+	struct wpabuf *tmp = NULL;
+	int ret;
+
+	if (wpabuf_len(data) < 4)
+		return -1;
+
+	if (*wpabuf_head_u8(data) != 0x10) {
+		/* 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;
+		}
+		wps = tmp;
+	}
+
+	ret = wpas_wps_nfc_tag_process(wpa_s, wps);
+	wpabuf_free(tmp);
+	return ret;
+}
+
+
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s,
+					  int ndef)
+{
+	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
+
+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 (wps == NULL)
+		return NULL;
+
+	if (uuid == NULL)
+		return NULL;
+	if (uuid_str2bin(uuid, u) == 0)
+		use_uuid = u;
+	else if (hwaddr_aton(uuid, addr) == 0)
+		use_addr = addr;
+	else
+		return NULL;
+
+	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);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+#else /* CONFIG_WPS_ER */
+	return NULL;
+#endif /* CONFIG_WPS_ER */
+}
+#endif /* CONFIG_WPS_NFC */
+
+
+struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+					  int ndef, int cr, const char *uuid)
+{
+	struct wpabuf *ret;
+	if (!cr)
+		return NULL;
+	ret = wpas_ap_wps_nfc_handover_sel(wpa_s, ndef);
+	if (ret)
+		return ret;
+	return wpas_wps_er_nfc_handover_sel(wpa_s, ndef, uuid);
+}
+
+
+static int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
+					const struct wpabuf *data)
+{
+	struct wpabuf *wps;
+	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(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;
+		else if (chan >= 1 && chan <= 4 &&
+			 (attr.rf_bands == NULL ||
+			  *attr.rf_bands & WPS_RF_60GHZ))
+			freq = 56160 + 2160 * 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;
+}
+
+
+int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+				 const struct wpabuf *req,
+				 const struct wpabuf *sel)
+{
+	wpa_printf(MSG_DEBUG, "NFC: WPS connection handover reported");
+	wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in request", req);
+	wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in select", sel);
+	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 */
+
+
+static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s)
+{
+	size_t i;
+	struct os_reltime now;
+
+	if (wpa_debug_level > MSG_DEBUG)
+		return;
+
+	if (wpa_s->wps_ap == NULL)
+		return;
+
+	os_get_reltime(&now);
+
+	for (i = 0; i < wpa_s->num_wps_ap; i++) {
+		struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+		struct wpa_blacklist *e = wpa_blacklist_get(wpa_s, ap->bssid);
+
+		wpa_printf(MSG_DEBUG, "WPS: AP[%d] " MACSTR " type=%d "
+			   "tries=%d last_attempt=%d sec ago blacklist=%d",
+			   (int) i, MAC2STR(ap->bssid), ap->type, ap->tries,
+			   ap->last_attempt.sec > 0 ?
+			   (int) now.sec - (int) ap->last_attempt.sec : -1,
+			   e ? e->count : 0);
+	}
+}
+
+
+static struct wps_ap_info * wpas_wps_get_ap_info(struct wpa_supplicant *wpa_s,
+						 const u8 *bssid)
+{
+	size_t i;
+
+	if (wpa_s->wps_ap == NULL)
+		return NULL;
+
+	for (i = 0; i < wpa_s->num_wps_ap; i++) {
+		struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+		if (os_memcmp(ap->bssid, bssid, ETH_ALEN) == 0)
+			return ap;
+	}
+
+	return NULL;
+}
+
+
+static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s,
+					struct wpa_scan_res *res)
+{
+	struct wpabuf *wps;
+	enum wps_ap_info_type type;
+	struct wps_ap_info *ap;
+	int r, pbc_active;
+	const u8 *uuid;
+
+	if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL)
+		return;
+
+	wps = wpa_scan_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE);
+	if (wps == NULL)
+		return;
+
+	r = wps_is_addr_authorized(wps, wpa_s->own_addr, 1);
+	if (r == 2)
+		type = WPS_AP_SEL_REG_OUR;
+	else if (r == 1)
+		type = WPS_AP_SEL_REG;
+	else
+		type = WPS_AP_NOT_SEL_REG;
+
+	uuid = wps_get_uuid_e(wps);
+	pbc_active = wps_is_selected_pbc_registrar(wps);
+
+	ap = wpas_wps_get_ap_info(wpa_s, res->bssid);
+	if (ap) {
+		if (ap->type != type) {
+			wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR
+				   " changed type %d -> %d",
+				   MAC2STR(res->bssid), ap->type, type);
+			ap->type = type;
+			if (type != WPS_AP_NOT_SEL_REG)
+				wpa_blacklist_del(wpa_s, ap->bssid);
+		}
+		ap->pbc_active = pbc_active;
+		if (uuid)
+			os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
+		goto out;
+	}
+
+	ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1,
+			      sizeof(struct wps_ap_info));
+	if (ap == NULL)
+		goto out;
+
+	wpa_s->wps_ap = ap;
+	ap = &wpa_s->wps_ap[wpa_s->num_wps_ap];
+	wpa_s->num_wps_ap++;
+
+	os_memset(ap, 0, sizeof(*ap));
+	os_memcpy(ap->bssid, res->bssid, ETH_ALEN);
+	ap->type = type;
+	ap->pbc_active = pbc_active;
+	if (uuid)
+		os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
+	wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added",
+		   MAC2STR(ap->bssid), ap->type);
+
+out:
+	wpabuf_free(wps);
+}
+
+
+void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
+			     struct wpa_scan_results *scan_res)
+{
+	size_t i;
+
+	for (i = 0; i < scan_res->num; i++)
+		wpas_wps_update_ap_info_bss(wpa_s, scan_res->res[i]);
+
+	wpas_wps_dump_ap_info(wpa_s);
+}
+
+
+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_reltime(&ap->last_attempt);
+}
diff --git a/hostap/wpa_supplicant/wps_supplicant.h b/hostap/wpa_supplicant/wps_supplicant.h
new file mode 100644
index 0000000..3c25ca8
--- /dev/null
+++ b/hostap/wpa_supplicant/wps_supplicant.h
@@ -0,0 +1,152 @@
+/*
+ * wpa_supplicant / WPS integration
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_SUPPLICANT_H
+#define WPS_SUPPLICANT_H
+
+struct wpa_scan_results;
+
+#ifdef CONFIG_WPS
+
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
+
+struct wpa_bss;
+
+struct wps_new_ap_settings {
+	const char *ssid_hex;
+	const char *auth;
+	const char *encr;
+	const char *key_hex;
+};
+
+int wpas_wps_init(struct wpa_supplicant *wpa_s);
+void wpas_wps_deinit(struct wpa_supplicant *wpa_s);
+int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s);
+enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid);
+int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+		       int p2p_group);
+int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+		       const char *pin, int p2p_group, u16 dev_pw_id);
+void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s);
+int wpas_wps_cancel(struct wpa_supplicant *wpa_s);
+int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
+		       const char *pin, struct wps_new_ap_settings *settings);
+int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
+			    struct wpa_ssid *ssid, struct wpa_bss *bss);
+int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid, struct wpa_bss *bss);
+int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
+			      struct wpa_bss *selected, struct wpa_ssid *ssid);
+void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s);
+int wpas_wps_searching(struct wpa_supplicant *wpa_s);
+int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos,
+			      char *end);
+int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter);
+void wpas_wps_er_stop(struct wpa_supplicant *wpa_s);
+int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr,
+			const char *uuid, const char *pin);
+int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid);
+int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid,
+		      const char *pin);
+int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
+			   int id);
+int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
+		       const char *pin, struct wps_new_ap_settings *settings);
+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);
+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 *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, 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_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);
+
+#else /* CONFIG_WPS */
+
+static inline int wpas_wps_init(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+
+static inline void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+
+static inline u8 wpas_wps_get_req_type(struct wpa_ssid *ssid)
+{
+	return 0;
+}
+
+static inline int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
+					  struct wpa_ssid *ssid,
+					  struct wpa_bss *bss)
+{
+	return -1;
+}
+
+static inline int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
+					    struct wpa_ssid *ssid,
+					    struct wpa_bss *bss)
+{
+	return 0;
+}
+
+static inline int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
+					    struct wpa_bss *selected,
+					    struct wpa_ssid *ssid)
+{
+	return 0;
+}
+
+static inline void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_wps_searching(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+
+static inline void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
+					   struct wpa_scan_results *scan_res)
+{
+}
+
+static inline void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s,
+					 const u8 *bssid)
+{
+}
+
+#endif /* CONFIG_WPS */
+
+#endif /* WPS_SUPPLICANT_H */
diff --git a/hostap/wpadebug/AndroidManifest.xml b/hostap/wpadebug/AndroidManifest.xml
new file mode 100644
index 0000000..9f3ca68
--- /dev/null
+++ b/hostap/wpadebug/AndroidManifest.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="w1.fi.wpadebug"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="17" />
+    <uses-permission android:name="android.permission.NFC" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application android:label="wpadebug">
+        <activity android:name="w1.fi.wpadebug.MainActivity"
+                  android:label="wpadebug">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+	<activity android:name="w1.fi.wpadebug.DisplayMessageActivity"
+		  android:label="Operation result"
+		  android:parentActivityName="w1.fi.wpadebug.MainActivity">
+	</activity>
+	<activity android:name="w1.fi.wpadebug.WpaNfcActivity"
+		  android:label="wpa_supplicant NFC operation"
+		  android:parentActivityName="w1.fi.wpadebug.MainActivity">
+            <intent-filter>
+		<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
+		<category android:name="android.intent.category.DEFAULT"/>
+		<data android:mimeType="application/vnd.wfa.wsc" />
+            </intent-filter>
+	</activity>
+	<activity android:name="w1.fi.wpadebug.CommandListActivity"
+		  android:label="Command list"
+		  android:parentActivityName="w1.fi.wpadebug.MainActivity">
+	</activity>
+	<activity android:name="w1.fi.wpadebug.WpaCommandListActivity"
+		  android:label="WPA command list"
+		  android:parentActivityName="w1.fi.wpadebug.MainActivity">
+	</activity>
+	<activity android:name="w1.fi.wpadebug.WpaCredActivity"
+		  android:label="Credentials"
+		  android:parentActivityName="w1.fi.wpadebug.MainActivity">
+	</activity>
+	<activity android:name="w1.fi.wpadebug.WpaCredEditActivity"
+		  android:label="Credential"
+		  android:parentActivityName="w1.fi.wpadebug.WpaCredActivity">
+	</activity>
+	<activity android:name="w1.fi.wpadebug.WpaWebViewActivity"
+		  android:label="WebView"
+		  android:launchMode="singleTop"
+		  android:noHistory="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+	</activity>
+	<receiver android:name="w1.fi.wpadebug.WifiReceiver">
+		<intent-filter>
+			<action android:name="android.net.wifi.STATE_CHANGE" />
+			<action android:name="android.net.wifi.RSSI_CHANGED" />
+			<action android:name="android.net.wifi.SCAN_RESULTS" />
+			<action android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
+			<action android:name="android.net.wifi.supplicant.STATE_CHANGE" />
+			<action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
+		</intent-filter>
+	</receiver>
+    </application>
+</manifest>
diff --git a/hostap/wpadebug/README b/hostap/wpadebug/README
new file mode 100644
index 0000000..843b99b
--- /dev/null
+++ b/hostap/wpadebug/README
@@ -0,0 +1,65 @@
+wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+Copyright (c) 2013, Jouni Malinen <j@w1.fi> and contributors
+All Rights Reserved.
+
+This program is licensed under the BSD license (the one with
+advertisement clause removed). See the top level README for detailed
+license text.
+
+If you are submitting changes to the project, please see CONTRIBUTIONS
+file for more instructions.
+
+
+NOTE! This Android app is for debugging and testing purposes only. It is
+not supposed to be installed on a production use device and doing so may
+result in complete loss of security protections on the device.
+
+
+
+Build
+-----
+
+- Install Android SDK and build tools
+- update project target if desired; for example:
+  android list targets
+  android update project --target 1 --path $PWD
+- run: ant debug
+
+
+Installation (with adb over USB)
+------------
+
+adb install bin/wpadebug-debug.apk
+
+NOTE: Following steps enable any app on the system to get root access!
+This is not suitable for any production use. This is needed for direct
+wpa_supplicant access and some networking operating in general. You can
+still use rest of the wpadebug app without doing this, but those
+functions will not work unless this step part of installation is
+done. It should be obvious that these steps require a rooted device. In
+addition, if you do not understand what the following commands do,
+please do not run them.
+
+adb root
+adb remount
+adb shell cp /system/bin/mksh /system/bin/mksh-su
+adb shell chmod 6755 /system/bin/mksh-su
+
+Optionally, a text file with a set of command can be installed to allow
+arbitrary shell commands to be executed. This text file need to be in
+/data/local/wpadebug.cmds and use title@command format per line. For
+example:
+version@cat /proc/version
+
+Similarly, /data/local/wpadebug.wpacmds can be used to define additional
+wpa_supplicant control interface commands.
+
+
+Uninstallation
+--------------
+
+adb root
+adb remount
+adb shell rm /system/bin/mksh-su
+
+adb uninstall w1.fi.wpadebug
diff --git a/hostap/wpadebug/build.xml b/hostap/wpadebug/build.xml
new file mode 100644
index 0000000..5301e69
--- /dev/null
+++ b/hostap/wpadebug/build.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="wpadebug" default="help">
+    <property file="local.properties" />
+    <property file="ant.properties" />
+    <property environment="env" />
+    <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+        <isset property="env.ANDROID_HOME" />
+    </condition>
+    <loadproperties srcFile="project.properties" />
+    <fail
+            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+            unless="sdk.dir"
+    />
+    <import file="custom_rules.xml" optional="true" />
+    <!-- version-tag: 1 -->
+    <import file="${sdk.dir}/tools/ant/build.xml" />
+</project>
diff --git a/hostap/wpadebug/project.properties b/hostap/wpadebug/project.properties
new file mode 100644
index 0000000..7c6ac05
--- /dev/null
+++ b/hostap/wpadebug/project.properties
@@ -0,0 +1,2 @@
+# Project target.
+target=android-17
diff --git a/hostap/wpadebug/res/layout/cred_edit.xml b/hostap/wpadebug/res/layout/cred_edit.xml
new file mode 100644
index 0000000..292b30a
--- /dev/null
+++ b/hostap/wpadebug/res/layout/cred_edit.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    >
+	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+		      android:layout_width="match_parent"
+		      android:layout_height="wrap_content"
+		      android:orientation="horizontal"
+		      >
+		<TextView
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="Username"
+		    />
+		<EditText android:id="@+id/cred_edit_username"
+			  android:layout_weight="1"
+			  android:layout_width="0dp"
+			  android:layout_height="wrap_content"
+			  android:singleLine="true"
+			  android:lines="1"
+			  />
+	</LinearLayout>
+	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+		      android:layout_width="match_parent"
+		      android:layout_height="wrap_content"
+		      android:orientation="horizontal"
+		      >
+		<TextView
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="Realm"
+		    />
+		<EditText android:id="@+id/cred_edit_realm"
+			  android:layout_weight="1"
+			  android:layout_width="0dp"
+			  android:layout_height="wrap_content"
+			  android:singleLine="true"
+			  android:lines="1"
+			  />
+	</LinearLayout>
+	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+		      android:layout_width="match_parent"
+		      android:layout_height="wrap_content"
+		      android:orientation="horizontal"
+		      >
+		<TextView
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="Password"
+		    />
+		<EditText android:id="@+id/cred_edit_password"
+			  android:layout_weight="1"
+			  android:layout_width="0dp"
+			  android:layout_height="wrap_content"
+			  android:singleLine="true"
+			  android:lines="1"
+			  android:inputType="textPassword"
+			  />
+	</LinearLayout>
+	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+		      android:layout_width="match_parent"
+		      android:layout_height="wrap_content"
+		      android:orientation="horizontal"
+		      >
+		<TextView
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="Domain"
+		    />
+		<EditText android:id="@+id/cred_edit_domain"
+			  android:layout_weight="1"
+			  android:layout_width="0dp"
+			  android:layout_height="wrap_content"
+			  android:singleLine="true"
+			  android:lines="1"
+			  />
+	</LinearLayout>
+	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+		      android:layout_width="match_parent"
+		      android:layout_height="wrap_content"
+		      android:orientation="horizontal"
+		      >
+		<TextView
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="IMSI"
+		    />
+		<EditText android:id="@+id/cred_edit_imsi"
+			  android:layout_weight="1"
+			  android:layout_width="0dp"
+			  android:layout_height="wrap_content"
+			  android:singleLine="true"
+			  android:lines="1"
+			  android:hint="Used only with SIM/USIM testing"
+			  />
+	</LinearLayout>
+	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+		      android:layout_width="match_parent"
+		      android:layout_height="wrap_content"
+		      android:orientation="horizontal"
+		      >
+		<Button
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="Save"
+		    android:onClick="credSave"
+		    />
+		<Button
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="Cancel"
+		    android:onClick="credCancel"
+		    />
+	</LinearLayout>
+</LinearLayout>
diff --git a/hostap/wpadebug/res/layout/main.xml b/hostap/wpadebug/res/layout/main.xml
new file mode 100644
index 0000000..6fdd565
--- /dev/null
+++ b/hostap/wpadebug/res/layout/main.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    >
+	<TextView
+	    android:layout_width="wrap_content"
+	    android:layout_height="wrap_content"
+	    android:text="Framework commands"
+	    />
+	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+		      android:layout_width="match_parent"
+		      android:layout_height="wrap_content"
+		      android:orientation="horizontal"
+		      >
+		<Button
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="WifiManager"
+		    android:onClick="wifiManagerInfo"
+		    />
+		<Button
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="WifiInfo"
+		    android:onClick="wifiInfo"
+		    />
+		<Button
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="Networks"
+		    android:onClick="wifiConfiguredNetworks"
+		    />
+	</LinearLayout>
+	<TextView
+	    android:layout_width="wrap_content"
+	    android:layout_height="wrap_content"
+	    android:text="wpa_supplicant commands"
+	    />
+	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+		      android:layout_width="match_parent"
+		      android:layout_height="wrap_content"
+		      android:orientation="horizontal"
+		      >
+		<Button
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="wpa_supplicant commands"
+		    android:onClick="runWpaCommands"
+		    />
+	</LinearLayout>
+	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+		      android:layout_width="match_parent"
+		      android:layout_height="wrap_content"
+		      android:orientation="horizontal"
+		      >
+		<Button
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="Credentials"
+		    android:onClick="runWpaCredentials"
+		    />
+	</LinearLayout>
+	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+		      android:layout_width="match_parent"
+		      android:layout_height="wrap_content"
+		      android:orientation="horizontal"
+		      >
+		<Button
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="log:info"
+		    android:onClick="wpaLogLevelInfo"
+		    />
+		<Button
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="log:debug"
+		    android:onClick="wpaLogLevelDebug"
+		    />
+		<Button
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="log:excessive"
+		    android:onClick="wpaLogLevelExcessive"
+		    />
+	</LinearLayout>
+	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+		      android:layout_width="match_parent"
+		      android:layout_height="wrap_content"
+		      android:orientation="horizontal"
+		      >
+		<EditText android:id="@+id/edit_cmd"
+			  android:layout_weight="1"
+			  android:layout_width="0dp"
+			  android:layout_height="wrap_content"
+			  android:hint="wpa_cli command"
+			  />
+		<Button
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="Run"
+		    android:onClick="runWpaCliCmd"
+		    />
+	</LinearLayout>
+	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+		      android:layout_width="match_parent"
+		      android:layout_height="wrap_content"
+		      android:orientation="horizontal"
+		      >
+		<Button
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="Shell commands"
+		    android:onClick="runCommands"
+		    />
+	</LinearLayout>
+	<TextView
+	    android:layout_width="wrap_content"
+	    android:layout_height="wrap_content"
+	    android:text="NFC commands"
+	    />
+	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+		      android:layout_width="match_parent"
+		      android:layout_height="wrap_content"
+		      android:orientation="horizontal"
+		      >
+		<Button
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:text="WPS handover request"
+		    android:onClick="nfcWpsHandoverRequest"
+		    />
+	</LinearLayout>
+</LinearLayout>
diff --git a/hostap/wpadebug/res/raw/shell_commands.txt b/hostap/wpadebug/res/raw/shell_commands.txt
new file mode 100644
index 0000000..9b45d65
--- /dev/null
+++ b/hostap/wpadebug/res/raw/shell_commands.txt
@@ -0,0 +1,2 @@
+id@id
+version@cat /proc/version
diff --git a/hostap/wpadebug/res/raw/wpa_commands.txt b/hostap/wpadebug/res/raw/wpa_commands.txt
new file mode 100644
index 0000000..3baa01c
--- /dev/null
+++ b/hostap/wpadebug/res/raw/wpa_commands.txt
@@ -0,0 +1,9 @@
+Status@STATUS
+PMKSA cache@PMKSA
+Networks@LIST_NETWORKS
+Interworking connect@INTERWORKING_SELECT auto
+Creds@LIST_CREDS
+Scan results@SCAN_RESULTS
+Flush@FLUSH
+Disconnect@DISCONNECT
+Reassociate@REASSOCIATE
diff --git a/hostap/wpadebug/src/w1/fi/wpadebug/CommandListActivity.java b/hostap/wpadebug/src/w1/fi/wpadebug/CommandListActivity.java
new file mode 100644
index 0000000..6d7ad4d
--- /dev/null
+++ b/hostap/wpadebug/src/w1/fi/wpadebug/CommandListActivity.java
@@ -0,0 +1,130 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.InputStream;
+import java.io.IOException;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.View;
+import android.widget.ListView;
+import android.widget.ArrayAdapter;
+import android.widget.Toast;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+
+class CmdList
+{
+    String title;
+    String command;
+
+    public CmdList(String _title, String _command)
+    {
+	title = _title;
+	command = _command;
+    }
+
+    @Override
+    public String toString()
+    {
+	return title;
+    }
+}
+
+public class CommandListActivity extends ListActivity
+{
+    private static final String TAG = "wpadebug";
+    private static final String cmdfile = "/data/local/wpadebug.cmds";
+
+    private void read_commands(ArrayList<CmdList> list, Scanner in)
+    {
+	in.useDelimiter("@");
+	while (in.hasNext()) {
+	    String title = in.next();
+	    String cmd = in.nextLine().substring(1);
+	    list.add(new CmdList(title, cmd));
+	}
+	in.close();
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+
+	ArrayList<CmdList> list = new ArrayList<CmdList>();
+
+	FileReader in;
+	try {
+	    in = new FileReader(cmdfile);
+	    read_commands(list, new Scanner(in));
+	} catch (IOException e) {
+	    Toast.makeText(this, "Could not read " + cmdfile,
+			   Toast.LENGTH_SHORT).show();
+	}
+
+	InputStream inres;
+	try {
+	    inres = getResources().openRawResource(R.raw.shell_commands);
+	    read_commands(list, new Scanner(inres));
+	} catch (android.content.res.Resources.NotFoundException e) {
+	    Toast.makeText(this, "Could not read internal resource",
+			   Toast.LENGTH_SHORT).show();
+	}
+
+	ArrayAdapter<CmdList> listAdapter;
+	listAdapter = new ArrayAdapter<CmdList>(this, android.R.layout.simple_list_item_1, list);
+
+	setListAdapter(listAdapter);
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id)
+    {
+	CmdList item = (CmdList) getListAdapter().getItem(position);
+	Toast.makeText(this, "Running: " + item.command,
+		       Toast.LENGTH_SHORT).show();
+	String message = run(item.command);
+	if (message == null)
+	    return;
+	Intent intent = new Intent(this, DisplayMessageActivity.class);
+	intent.putExtra(MainActivity.EXTRA_MESSAGE, message);
+	startActivity(intent);
+    }
+
+    private String run(String cmd)
+    {
+	try {
+	    Process proc = Runtime.getRuntime().exec(new String[]{"/system/bin/mksh-su", "-c", cmd});
+	    BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+	    StringBuffer output = new StringBuffer();
+	    int read;
+	    char[] buffer = new char[1024];
+	    while ((read = reader.read(buffer)) > 0)
+		output.append(buffer, 0, read);
+	    reader.close();
+	    proc.waitFor();
+	    return output.toString();
+	} catch (IOException e) {
+	    Toast.makeText(this, "Could not run command",
+			   Toast.LENGTH_LONG).show();
+	    return null;
+	} catch (InterruptedException e) {
+	    throw new RuntimeException(e);
+	}
+    }
+}
diff --git a/hostap/wpadebug/src/w1/fi/wpadebug/DisplayMessageActivity.java b/hostap/wpadebug/src/w1/fi/wpadebug/DisplayMessageActivity.java
new file mode 100644
index 0000000..28ef85f
--- /dev/null
+++ b/hostap/wpadebug/src/w1/fi/wpadebug/DisplayMessageActivity.java
@@ -0,0 +1,49 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.MenuItem;
+import android.content.Intent;
+import android.widget.TextView;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+
+public class DisplayMessageActivity extends Activity
+{
+    private static final String TAG = "wpadebug";
+
+    String byteArrayHex(byte[] a) {
+	StringBuilder sb = new StringBuilder();
+	for (byte b: a)
+	    sb.append(String.format("%02x", b));
+	return sb.toString();
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+	Log.d(TAG, "onCreate");
+        super.onCreate(savedInstanceState);
+
+	// Get the message from the intent
+	Intent intent = getIntent();
+	String action = intent.getAction();
+	Log.d(TAG, "onCreate: action=" + action);
+
+	String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
+
+	TextView textView = new TextView(this);
+	textView.setText(message);
+	textView.setMovementMethod(new ScrollingMovementMethod());
+        setContentView(textView);
+    }
+}
diff --git a/hostap/wpadebug/src/w1/fi/wpadebug/MainActivity.java b/hostap/wpadebug/src/w1/fi/wpadebug/MainActivity.java
new file mode 100644
index 0000000..c5d123e
--- /dev/null
+++ b/hostap/wpadebug/src/w1/fi/wpadebug/MainActivity.java
@@ -0,0 +1,191 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.view.View;
+import android.content.Intent;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.widget.EditText;
+import android.widget.Toast;
+import android.util.Log;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiConfiguration;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+
+public class MainActivity extends Activity
+{
+    public final static String EXTRA_MESSAGE = "w1.fi.wpadebug.MESSAGE";
+    private static final String TAG = "wpadebug";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+
+    public void runCommands(View view)
+    {
+	Intent intent = new Intent(this, CommandListActivity.class);
+	startActivity(intent);
+    }
+
+    public void runWpaCommands(View view)
+    {
+	Intent intent = new Intent(this, WpaCommandListActivity.class);
+	startActivity(intent);
+    }
+
+    public void runWpaCredentials(View view)
+    {
+	Intent intent = new Intent(this, WpaCredActivity.class);
+	startActivity(intent);
+    }
+
+    public void runWpaCliCmd(View view)
+    {
+	Intent intent = new Intent(this, DisplayMessageActivity.class);
+	EditText editText = (EditText) findViewById(R.id.edit_cmd);
+	String cmd = editText.getText().toString();
+	if (cmd.trim().length() == 0) {
+	    show_alert("wpa_cli command", "Invalid command");
+	    return;
+	}
+	wpaCmd(view, cmd);
+    }
+
+    public void wpaLogLevelInfo(View view)
+    {
+	wpaCmd(view, "LOG_LEVEL INFO 1");
+    }
+
+    public void wpaLogLevelDebug(View view)
+    {
+	wpaCmd(view, "LOG_LEVEL DEBUG 1");
+    }
+
+    public void wpaLogLevelExcessive(View view)
+    {
+	wpaCmd(view, "LOG_LEVEL EXCESSIVE 1");
+    }
+
+    private void wpaCmd(View view, String cmd)
+    {
+	Intent intent = new Intent(this, DisplayMessageActivity.class);
+	String message = run("wpa_cli " + cmd);
+	if (message == null)
+	    return;
+	intent.putExtra(EXTRA_MESSAGE, message);
+	startActivity(intent);
+    }
+
+    private String run(String cmd)
+    {
+	try {
+	    Log.d(TAG, "Running external process: " + cmd);
+	    Process proc = Runtime.getRuntime().exec(new String[]{"/system/bin/mksh-su", "-c", cmd});
+	    BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+	    StringBuffer output = new StringBuffer();
+	    int read;
+	    char[] buffer = new char[1024];
+	    while ((read = reader.read(buffer)) > 0)
+		output.append(buffer, 0, read);
+	    reader.close();
+	    proc.waitFor();
+	    Log.d(TAG, "External process completed - exitValue " +
+		  proc.exitValue());
+	    return output.toString();
+	} catch (IOException e) {
+	    show_alert("Could not run external program",
+		       "Execution of an external program failed. " +
+		       "Maybe mksh-su was not installed.");
+	    return null;
+	} catch (InterruptedException e) {
+	    throw new RuntimeException(e);
+	}
+    }
+
+    private void show_alert(String title, String message)
+    {
+	AlertDialog.Builder alert = new AlertDialog.Builder(this);
+	alert.setTitle(title);
+	alert.setMessage(message);
+	alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
+		public void onClick(DialogInterface dialog, int id)
+		{
+		}
+	    });
+	alert.create().show();
+    }
+
+    public void wifiManagerInfo(View view)
+    {
+	Intent intent = new Intent(this, DisplayMessageActivity.class);
+	WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+	String message = "WifiState: " + manager.getWifiState() + "\n" +
+	    "WifiEnabled: " + manager.isWifiEnabled() + "\n" +
+	    "pingSupplicant: " + manager.pingSupplicant() + "\n" +
+	    "DhcpInfo: " + manager.getDhcpInfo().toString() + "\n";
+	intent.putExtra(EXTRA_MESSAGE, message);
+	startActivity(intent);
+    }
+
+    public void wifiInfo(View view)
+    {
+	Intent intent = new Intent(this, DisplayMessageActivity.class);
+	WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+	WifiInfo wifi = manager.getConnectionInfo();
+	String message = wifi.toString() + "\n" + wifi.getSupplicantState();
+	intent.putExtra(EXTRA_MESSAGE, message);
+	startActivity(intent);
+    }
+
+    public void wifiConfiguredNetworks(View view)
+    {
+	Intent intent = new Intent(this, DisplayMessageActivity.class);
+	WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+	StringBuilder sb = new StringBuilder();
+	for (WifiConfiguration n: manager.getConfiguredNetworks())
+	    sb.append(n.toString() + "\n");
+	intent.putExtra(EXTRA_MESSAGE, sb.toString());
+	startActivity(intent);
+    }
+
+    public void nfcWpsHandoverRequest(View view)
+    {
+	NfcAdapter nfc;
+	nfc = NfcAdapter.getDefaultAdapter(this);
+	if (nfc == null) {
+	    Toast.makeText(this, "NFC is not available",
+			   Toast.LENGTH_LONG).show();
+	    return;
+	}
+
+	NdefMessage msg;
+	msg = new NdefMessage(new NdefRecord[] {
+		NdefRecord.createMime("application/vnd.wfa.wsc",
+				      new byte[0])
+	    });
+
+	nfc.setNdefPushMessage(msg, this);
+	Toast.makeText(this, "NFC push message (WSC) configured",
+		       Toast.LENGTH_LONG).show();
+    }
+}
diff --git a/hostap/wpadebug/src/w1/fi/wpadebug/WifiReceiver.java b/hostap/wpadebug/src/w1/fi/wpadebug/WifiReceiver.java
new file mode 100644
index 0000000..d69e05d
--- /dev/null
+++ b/hostap/wpadebug/src/w1/fi/wpadebug/WifiReceiver.java
@@ -0,0 +1,95 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkInfo;
+import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiInfo;
+import android.os.Bundle;
+import android.util.Log;
+
+public class WifiReceiver extends BroadcastReceiver
+{
+    private static final String TAG = "wpadebug";
+
+    @Override
+    public void onReceive(Context c, Intent intent)
+    {
+	String act = intent.getAction();
+	Log.d(TAG, "Received broadcast intent: action=" + act);
+
+	Bundle bundles = intent.getExtras();
+	if (bundles == null)
+	    return;
+
+	if (bundles.containsKey("bssid")) {
+	    String val;
+	    val = intent.getStringExtra("bssid");
+	    if (val != null)
+		Log.d(TAG, "  bssid: " + val);
+	}
+
+	if (bundles.containsKey("networkInfo")) {
+	    NetworkInfo info;
+	    info = (NetworkInfo) intent.getParcelableExtra("networkInfo");
+	    if (info != null)
+		Log.d(TAG, "  networkInfo: " + info);
+	}
+
+	if (bundles.containsKey("newRssi")) {
+	    int val;
+	    val = intent.getIntExtra("newRssi", -1);
+	    Log.d(TAG, "  newRssi: " + val);
+	}
+
+	if (bundles.containsKey("newState")) {
+	    SupplicantState state;
+	    state = (SupplicantState) intent.getParcelableExtra("newState");
+	    if (state != null)
+		Log.d(TAG, "  newState: " + state);
+	}
+
+	if (bundles.containsKey("previous_wifi_state")) {
+	    int wifi_state;
+	    wifi_state = intent.getIntExtra("previous_wifi_state", -1);
+	    if (wifi_state != -1)
+		Log.d(TAG, "  previous_wifi_state: " + wifi_state);
+	}
+
+	if (bundles.containsKey("connected")) {
+	    boolean connected;
+	    connected = intent.getBooleanExtra("connected", false);
+	    Log.d(TAG, "  connected: " + connected);
+	}
+
+	if (bundles.containsKey("supplicantError")) {
+	    int error;
+	    error = intent.getIntExtra("supplicantError", -1);
+	    if (error != -1)
+		Log.d(TAG, "  supplicantError: " + error);
+	}
+
+	if (bundles.containsKey("wifiInfo")) {
+	    WifiInfo info;
+	    info = (WifiInfo) intent.getParcelableExtra("wifiInfo");
+	    if (info != null)
+		Log.d(TAG, "  wifiInfo: " + info);
+	}
+
+	if (bundles.containsKey("wifi_state")) {
+	    int wifi_state;
+	    wifi_state = intent.getIntExtra("wifi_state", -1);
+	    if (wifi_state != -1)
+		Log.d(TAG, "  wifi_state: " + wifi_state);
+	}
+    }
+}
diff --git a/hostap/wpadebug/src/w1/fi/wpadebug/WpaCommandListActivity.java b/hostap/wpadebug/src/w1/fi/wpadebug/WpaCommandListActivity.java
new file mode 100644
index 0000000..e089179
--- /dev/null
+++ b/hostap/wpadebug/src/w1/fi/wpadebug/WpaCommandListActivity.java
@@ -0,0 +1,112 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.InputStream;
+import java.io.IOException;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.View;
+import android.widget.ListView;
+import android.widget.ArrayAdapter;
+import android.widget.Toast;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+
+public class WpaCommandListActivity extends ListActivity
+{
+    private static final String TAG = "wpadebug";
+    private static final String cmdfile = "/data/local/wpadebug.wpacmds";
+
+    private void read_commands(ArrayList<CmdList> list, Scanner in)
+    {
+	in.useDelimiter("@");
+	while (in.hasNext()) {
+	    String title = in.next();
+	    String cmd = in.nextLine().substring(1);
+	    list.add(new CmdList(title, cmd));
+	}
+	in.close();
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+
+	ArrayList<CmdList> list = new ArrayList<CmdList>();
+
+	FileReader in;
+	try {
+	    in = new FileReader(cmdfile);
+	    read_commands(list, new Scanner(in));
+	} catch (IOException e) {
+	    Toast.makeText(this, "Could not read " + cmdfile,
+			   Toast.LENGTH_SHORT).show();
+	}
+
+	InputStream inres;
+	try {
+	    inres = getResources().openRawResource(R.raw.wpa_commands);
+	    read_commands(list, new Scanner(inres));
+	} catch (android.content.res.Resources.NotFoundException e) {
+	    Toast.makeText(this, "Could not read internal resource",
+			   Toast.LENGTH_SHORT).show();
+	}
+
+	ArrayAdapter<CmdList> listAdapter;
+	listAdapter = new ArrayAdapter<CmdList>(this, android.R.layout.simple_list_item_1, list);
+
+	setListAdapter(listAdapter);
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id)
+    {
+	CmdList item = (CmdList) getListAdapter().getItem(position);
+	Toast.makeText(this, "Running: " + item.command,
+		       Toast.LENGTH_SHORT).show();
+	String message = run(item.command);
+	if (message == null)
+	    return;
+	Intent intent = new Intent(this, DisplayMessageActivity.class);
+	intent.putExtra(MainActivity.EXTRA_MESSAGE, message);
+	startActivity(intent);
+    }
+
+    private String run(String cmd)
+    {
+	try {
+	    Process proc = Runtime.getRuntime().exec(new String[]{"/system/bin/mksh-su", "-c", "wpa_cli " + cmd});
+	    BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+	    StringBuffer output = new StringBuffer();
+	    int read;
+	    char[] buffer = new char[1024];
+	    while ((read = reader.read(buffer)) > 0)
+		output.append(buffer, 0, read);
+	    reader.close();
+	    proc.waitFor();
+	    return output.toString();
+	} catch (IOException e) {
+	    Toast.makeText(this, "Could not run command",
+			   Toast.LENGTH_LONG).show();
+	    return null;
+	} catch (InterruptedException e) {
+	    throw new RuntimeException(e);
+	}
+    }
+}
diff --git a/hostap/wpadebug/src/w1/fi/wpadebug/WpaCredActivity.java b/hostap/wpadebug/src/w1/fi/wpadebug/WpaCredActivity.java
new file mode 100644
index 0000000..3902f09
--- /dev/null
+++ b/hostap/wpadebug/src/w1/fi/wpadebug/WpaCredActivity.java
@@ -0,0 +1,263 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import java.util.ArrayList;
+import java.util.ListIterator;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.InputStream;
+import java.io.IOException;
+
+import android.app.ListActivity;
+import android.app.ActionBar;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ListView;
+import android.widget.ArrayAdapter;
+import android.widget.Toast;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+class Credential
+{
+    int id;
+    String realm;
+    String username;
+    String domain;
+    String imsi;
+
+    public Credential(String entry)
+    {
+	String fields[] = entry.split("\t");
+	id = Integer.parseInt(fields[0]);
+	if (fields.length > 1)
+	    realm = fields[1];
+	else
+	    realm = "";
+	if (fields.length > 2)
+	    username = fields[2];
+	else
+	    username = "";
+	if (fields.length > 3 && fields[3].length() > 0)
+	    domain = fields[3];
+	else
+	    domain = null;
+	if (fields.length > 4 && fields[4].length() > 0)
+	    imsi = fields[4];
+	else
+	    imsi = null;
+    }
+
+    public Credential(int _id, String _username, String _realm, String _domain,
+		      String _imsi)
+    {
+	id = _id;
+	username = _username;
+	realm = _realm;
+	domain = _domain;
+	imsi = _imsi;
+    }
+
+
+    @Override
+    public String toString()
+    {
+	String res = id + " - " + username + "@" + realm;
+	if (domain != null)
+	    res += " (domain=" + domain + ")";
+	if (imsi != null)
+	    res += " (imsi=" + imsi + ")";
+	return res;
+    }
+}
+
+public class WpaCredActivity extends ListActivity
+{
+    private static final String TAG = "wpadebug";
+    static final int CRED_EDIT_REQ = 0;
+    private ArrayList<Credential> mList;
+    private ArrayAdapter<Credential> mListAdapter;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+
+	mList = new ArrayList<Credential>();
+
+	String res = run("LIST_CREDS");
+	if (res == null) {
+	    Toast.makeText(this, "Could not get credential list",
+			   Toast.LENGTH_LONG).show();
+	    finish();
+	    return;
+	}
+
+	String creds[] = res.split("\n");
+	for (String cred: creds) {
+	    if (Character.isDigit(cred.charAt(0)))
+		mList.add(new Credential(cred));
+	}
+
+	mListAdapter = new ArrayAdapter<Credential>(this, android.R.layout.simple_list_item_1, mList);
+
+	setListAdapter(mListAdapter);
+	registerForContextMenu(getListView());
+
+	ActionBar abar = getActionBar();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu)
+    {
+	menu.add(0, 0, 0, "Add credential");
+	return true;
+    }
+
+    protected void onActivityResult(int requestCode, int resultCode,
+				    Intent data)
+    {
+	if (requestCode == CRED_EDIT_REQ) {
+	    if (resultCode != RESULT_OK)
+		return;
+
+	    String username = data.getStringExtra("username");
+
+	    String realm = data.getStringExtra("realm");
+
+	    String domain = data.getStringExtra("domain");
+	    if (domain != null && domain.length() == 0)
+		domain = null;
+
+	    String imsi = data.getStringExtra("imsi");
+	    if (imsi != null && imsi.length() == 0)
+		imsi = null;
+
+	    String password = data.getStringExtra("password");
+	    if (password != null && password.length() == 0)
+		password = null;
+
+	    String res = run("ADD_CRED");
+	    if (res == null || res.contains("FAIL")) {
+		Toast.makeText(this, "Failed to add credential",
+			       Toast.LENGTH_LONG).show();
+		return;
+	    }
+
+	    int id = -1;
+	    String lines[] = res.split("\n");
+	    for (String line: lines) {
+		if (Character.isDigit(line.charAt(0))) {
+		    id = Integer.parseInt(line);
+		    break;
+		}
+	    }
+
+	    if (id < 0) {
+		Toast.makeText(this, "Failed to add credential (invalid id)",
+			       Toast.LENGTH_LONG).show();
+		return;
+	    }
+
+	    if (!set_cred_quoted(id, "username", username) ||
+		!set_cred_quoted(id, "realm", realm) ||
+		(password != null &&
+		 !set_cred_quoted(id, "password", password)) ||
+		(domain != null && !set_cred_quoted(id, "domain", domain)) ||
+		(imsi != null && !set_cred_quoted(id, "imsi", imsi))) {
+		run("REMOVE_CRED " + id);
+		Toast.makeText(this, "Failed to set credential field",
+			       Toast.LENGTH_LONG).show();
+		return;
+	    }
+
+	    mListAdapter.add(new Credential(id, username, realm, domain, imsi));
+	}
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item)
+    {
+	if (item.getTitle().equals("Add credential")) {
+	    startActivityForResult(new Intent(this, WpaCredEditActivity.class),
+				   CRED_EDIT_REQ);
+	    return true;
+	}
+	return false;
+    }
+
+    public void onCreateContextMenu(android.view.ContextMenu menu, View v,
+				    android.view.ContextMenu.ContextMenuInfo menuInfo)
+    {
+	menu.add(0, v.getId(), 0, "Delete");
+    }
+
+    @Override
+    public boolean onContextItemSelected(MenuItem item)
+    {
+	if (item.getTitle().equals("Delete")) {
+	    AdapterContextMenuInfo info =
+		(AdapterContextMenuInfo) item.getMenuInfo();
+	    Credential cred = (Credential) getListAdapter().getItem(info.position);
+	    String res = run("REMOVE_CRED " + cred.id);
+	    if (res == null || !res.contains("OK")) {
+		Toast.makeText(this, "Failed to delete credential",
+			       Toast.LENGTH_LONG).show();
+	    } else
+		mListAdapter.remove(cred);
+	    return true;
+	}
+	return super.onContextItemSelected(item);
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id)
+    {
+	Credential item = (Credential) getListAdapter().getItem(position);
+	Toast.makeText(this, "Credential selected: " + item,
+		       Toast.LENGTH_SHORT).show();
+    }
+
+    private String run(String cmd)
+    {
+	try {
+	    Process proc = Runtime.getRuntime().exec(new String[]{"/system/bin/mksh-su", "-c", "wpa_cli " + cmd});
+	    BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+	    StringBuffer output = new StringBuffer();
+	    int read;
+	    char[] buffer = new char[1024];
+	    while ((read = reader.read(buffer)) > 0)
+		output.append(buffer, 0, read);
+	    reader.close();
+	    proc.waitFor();
+	    return output.toString();
+	} catch (IOException e) {
+	    Toast.makeText(this, "Could not run command",
+			   Toast.LENGTH_LONG).show();
+	    return null;
+	} catch (InterruptedException e) {
+	    throw new RuntimeException(e);
+	}
+    }
+
+    private boolean set_cred(int id, String field, String value)
+    {
+	String res = run("SET_CRED " + id + " " + field + " " + value);
+	return res != null && res.contains("OK");
+    }
+
+    private boolean set_cred_quoted(int id, String field, String value)
+    {
+	String value2 = "'\"" + value + "\"'";
+	return set_cred(id, field, value2);
+    }
+}
diff --git a/hostap/wpadebug/src/w1/fi/wpadebug/WpaCredEditActivity.java b/hostap/wpadebug/src/w1/fi/wpadebug/WpaCredEditActivity.java
new file mode 100644
index 0000000..3f846c7
--- /dev/null
+++ b/hostap/wpadebug/src/w1/fi/wpadebug/WpaCredEditActivity.java
@@ -0,0 +1,55 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+
+public class WpaCredEditActivity extends Activity
+{
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.cred_edit);
+    }
+
+    public void credSave(View view)
+    {
+	Intent data = new Intent();
+	EditText edit;
+
+	edit = (EditText) findViewById(R.id.cred_edit_username);
+	data.putExtra("username", edit.getText().toString());
+
+	edit = (EditText) findViewById(R.id.cred_edit_realm);
+	data.putExtra("realm", edit.getText().toString());
+
+	edit = (EditText) findViewById(R.id.cred_edit_password);
+	data.putExtra("password", edit.getText().toString());
+
+	edit = (EditText) findViewById(R.id.cred_edit_domain);
+	data.putExtra("domain", edit.getText().toString());
+
+	edit = (EditText) findViewById(R.id.cred_edit_imsi);
+	data.putExtra("imsi", edit.getText().toString());
+
+	setResult(Activity.RESULT_OK, data);
+	finish();
+    }
+
+    public void credCancel(View view)
+    {
+	setResult(Activity.RESULT_CANCELED);
+	finish();
+    }
+}
diff --git a/hostap/wpadebug/src/w1/fi/wpadebug/WpaNfcActivity.java b/hostap/wpadebug/src/w1/fi/wpadebug/WpaNfcActivity.java
new file mode 100644
index 0000000..6a16017
--- /dev/null
+++ b/hostap/wpadebug/src/w1/fi/wpadebug/WpaNfcActivity.java
@@ -0,0 +1,131 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.MenuItem;
+import android.content.Intent;
+import android.content.DialogInterface;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+
+public class WpaNfcActivity extends Activity
+{
+    private static final String TAG = "wpadebug";
+
+    String byteArrayHex(byte[] a) {
+	StringBuilder sb = new StringBuilder();
+	for (byte b: a)
+	    sb.append(String.format("%02x", b));
+	return sb.toString();
+    }
+
+    private void show_alert(String title, String message)
+    {
+	AlertDialog.Builder alert = new AlertDialog.Builder(this);
+	alert.setTitle(title);
+	alert.setMessage(message);
+	alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
+		public void onClick(DialogInterface dialog, int id)
+		{
+		    finish();
+		}
+	    });
+	alert.create().show();
+    }
+
+    private String wpaCmd(String cmd)
+    {
+	try {
+	    Log.d(TAG, "Executing wpaCmd: " + cmd);
+	    Process proc = Runtime.getRuntime().exec(new String[]{"/system/bin/mksh-su", "-c", "wpa_cli " + cmd});
+	    BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+	    StringBuffer output = new StringBuffer();
+	    int read;
+	    char[] buffer = new char[1024];
+	    while ((read = reader.read(buffer)) > 0)
+		output.append(buffer, 0, read);
+	    reader.close();
+	    proc.waitFor();
+	    Log.d(TAG, "External process completed - exitValue " +
+		  proc.exitValue());
+	    return output.toString();
+	} catch (IOException e) {
+	    show_alert("Could not run external program",
+		       "Execution of an external program failed. " +
+		       "Maybe mksh-su was not installed.");
+	    return null;
+	} catch (InterruptedException e) {
+	    throw new RuntimeException(e);
+	}
+    }
+
+    public boolean report_tag_read(byte[] payload)
+    {
+	String res = wpaCmd("WPS_NFC_TAG_READ " + byteArrayHex(payload));
+	if (res == null)
+	    return false;
+	if (!res.contains("OK")) {
+	    Toast.makeText(this, "Failed to report WSC tag read to " +
+			   "wpa_supplicant", Toast.LENGTH_LONG).show();
+	} else {
+	    Toast.makeText(this, "Reported WSC tag read to wpa_supplicant",
+			   Toast.LENGTH_LONG).show();
+	}
+	finish();
+	return true;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+	super.onCreate(savedInstanceState);
+
+	Intent intent = getIntent();
+	String action = intent.getAction();
+	Log.d(TAG, "onCreate: action=" + action);
+
+	if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
+	    Log.d(TAG, "NDEF discovered");
+	    Parcelable[] raw = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
+	    if (raw != null) {
+		Log.d(TAG, "NDEF message count: " + raw.length);
+		NdefMessage[] msgs = new NdefMessage[raw.length];
+		for (int i = 0; i < raw.length; i++) {
+		    msgs[i] = (NdefMessage) raw[i];
+		    NdefRecord rec = msgs[i].getRecords()[0];
+		    Log.d(TAG, "MIME type: " + rec.toMimeType());
+		    byte[] a = rec.getPayload();
+		    Log.d(TAG, "NDEF record: " + byteArrayHex(a));
+		    if (rec.getTnf() == NdefRecord.TNF_MIME_MEDIA &&
+			rec.toMimeType().equals("application/vnd/wfa.wsc")) {
+			Log.d(TAG, "WSC tag read");
+		    }
+
+		    if (!report_tag_read(a))
+			return;
+		}
+	    }
+	}
+
+	finish();
+    }
+}
diff --git a/hostap/wpadebug/src/w1/fi/wpadebug/WpaWebViewActivity.java b/hostap/wpadebug/src/w1/fi/wpadebug/WpaWebViewActivity.java
new file mode 100644
index 0000000..a7c54fc
--- /dev/null
+++ b/hostap/wpadebug/src/w1/fi/wpadebug/WpaWebViewActivity.java
@@ -0,0 +1,146 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.net.http.SslError;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Window;
+import android.webkit.SslErrorHandler;
+import android.webkit.WebChromeClient;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.Toast;
+
+public class WpaWebViewActivity extends Activity
+{
+    private static final String TAG = "wpadebug";
+    private static final String EXTRA_MESSAGE = "w1.fi.wpadebug.URL";
+    private WebView mWebView;
+    final Activity activity = this;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+	Log.d(TAG, "WpaWebViewActivity::onCreate");
+        super.onCreate(savedInstanceState);
+
+	Intent intent = getIntent();
+	String url = intent.getStringExtra(EXTRA_MESSAGE);
+	Log.d(TAG, "url=" + url);
+	if (url.equals("FINISH")) {
+	    finish();
+	    return;
+	}
+
+	mWebView = new WebView(this);
+	mWebView.getSettings().setJavaScriptEnabled(true);
+	mWebView.setWebViewClient(new WpaWebViewClient());
+
+	getWindow().requestFeature(Window.FEATURE_PROGRESS);
+
+	mWebView.setWebChromeClient(new WebChromeClient()
+	    {
+		public void onProgressChanged(WebView view, int progress)
+		{
+		    Log.d(TAG, "progress=" + progress);
+		    activity.setProgress(progress * 1000);
+		}
+	    });
+
+        setContentView(mWebView);
+
+	mWebView.loadUrl(url);
+    }
+
+    @Override
+    public void onResume()
+    {
+	Log.d(TAG, "WpaWebViewActivity::onResume");
+        super.onResume();
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent)
+    {
+	Log.d(TAG, "WpaWebViewActivity::onNewIntent");
+	super.onNewIntent(intent);
+	String url = intent.getStringExtra(EXTRA_MESSAGE);
+	Log.d(TAG, "url=" + url);
+	setIntent(intent);
+	if (url.equals("FINISH")) {
+	    finish();
+	    return;
+	}
+	mWebView.loadUrl(url);
+    }
+
+    private class WpaWebViewClient extends WebViewClient {
+	@Override
+	public boolean shouldOverrideUrlLoading(WebView view, String url)
+	{
+	    Log.d(TAG, "shouldOverrideUrlLoading: url=" + url);
+	    Intent intent = getIntent();
+	    intent.putExtra(EXTRA_MESSAGE, url);
+
+	    view.loadUrl(url);
+	    return true;
+	}
+
+	@Override
+	public void onPageFinished(WebView view, String url)
+	{
+	    Log.d(TAG, "onPageFinished: url=" + url);
+	}
+
+	public void onReceivedError(WebView view, int errorCode,
+				    String description, String failingUrl)
+	{
+	    Log.d(TAG, "Failed to load page: errorCode=" +
+		  errorCode + " description=" + description +
+		  " URL=" + failingUrl);
+	    Toast.makeText(activity, "Failed to load page: " +
+			   description + " (URL=" + failingUrl + ")",
+			   Toast.LENGTH_LONG).show();
+	}
+
+	@Override
+	public void onReceivedSslError(WebView view, SslErrorHandler handler,
+				       SslError error)
+	{
+	    Log.d(TAG, "SSL error: " + error);
+
+	    final SslErrorHandler h = handler;
+	    AlertDialog.Builder alert = new AlertDialog.Builder(activity);
+	    alert.setTitle("SSL error - Continue?");
+	    alert.setMessage(error.toString())
+		.setCancelable(false)
+		.setPositiveButton("Yes", new DialogInterface.OnClickListener()
+		    {
+			public void onClick(DialogInterface dialog, int id)
+			{
+			    h.proceed();
+			}
+		    })
+		.setNegativeButton("No", new DialogInterface.OnClickListener()
+		    {
+			public void onClick(DialogInterface dialog, int id)
+			{
+			    h.cancel();
+			}
+		    });
+	    alert.show();
+	}
+    }
+}
diff --git a/hostap/wpaspy/Makefile b/hostap/wpaspy/Makefile
new file mode 100644
index 0000000..bc920e0
--- /dev/null
+++ b/hostap/wpaspy/Makefile
@@ -0,0 +1,14 @@
+all: build
+
+SRC=wpaspy.c
+
+build: $(SRC) setup.py
+	python setup.py build
+
+install:
+	python setup.py install
+
+clean:
+	python setup.py clean
+	rm -f *~
+	rm -rf build
diff --git a/hostap/wpaspy/setup.py b/hostap/wpaspy/setup.py
new file mode 100644
index 0000000..4dbf765
--- /dev/null
+++ b/hostap/wpaspy/setup.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+#
+# Python bindings for wpa_ctrl (wpa_supplicant/hostapd control interface)
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from distutils.core import setup, Extension
+
+ext = Extension(name = 'wpaspy',
+                sources = ['../src/common/wpa_ctrl.c',
+                           '../src/utils/os_unix.c',
+                           'wpaspy.c'],
+                extra_compile_args = ["-I../src/common",
+                                      "-I../src/utils",
+                                      "-DCONFIG_CTRL_IFACE",
+                                      "-DCONFIG_CTRL_IFACE_UNIX"])
+
+setup(name = 'wpaspy',
+      ext_modules = [ext],
+      description = 'Python bindings for wpa_ctrl (wpa_supplicant/hostapd)')
diff --git a/hostap/wpaspy/test.py b/hostap/wpaspy/test.py
new file mode 100755
index 0000000..493af7a
--- /dev/null
+++ b/hostap/wpaspy/test.py
@@ -0,0 +1,69 @@
+#!/usr/bin/python
+#
+# Test script for wpaspy
+# Copyright (c) 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 time
+import wpaspy
+
+wpas_ctrl = '/var/run/wpa_supplicant'
+
+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:
+        try:
+            wpas = wpaspy.Ctrl(ctrl)
+            return wpas
+        except Exception, e:
+            pass
+    return None
+
+
+def main():
+    print "Testing wpa_supplicant control interface connection"
+    wpas = wpas_connect()
+    if wpas is None:
+        return
+    print "Connected to wpa_supplicant"
+    print wpas.request('PING')
+
+    mon = wpas_connect()
+    if mon is None:
+        print "Could not open event monitor connection"
+        return
+
+    mon.attach()
+    print "Scan"
+    print wpas.request('SCAN')
+
+    count = 0
+    while count < 10:
+        count += 1
+        time.sleep(1)
+        while mon.pending():
+            ev = mon.recv()
+            print ev
+            if 'CTRL-EVENT-SCAN-RESULTS' in ev:
+                print 'Scan completed'
+                print wpas.request('SCAN_RESULTS')
+                count = 10
+                pass
+
+
+if __name__ == "__main__":
+    main()
diff --git a/hostap/wpaspy/wpaspy.c b/hostap/wpaspy/wpaspy.c
new file mode 100644
index 0000000..278089b
--- /dev/null
+++ b/hostap/wpaspy/wpaspy.c
@@ -0,0 +1,214 @@
+/*
+ * Python bindings for wpa_ctrl (wpa_supplicant/hostapd control interface)
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "wpa_ctrl.h"
+
+
+struct wpaspy_obj {
+	PyObject_HEAD
+	struct wpa_ctrl *ctrl;
+	int attached;
+};
+
+static PyObject *wpaspy_error;
+
+
+static int wpaspy_open(struct wpaspy_obj *self, PyObject *args)
+{
+	const char *path;
+
+	if (!PyArg_ParseTuple(args, "s", &path))
+		return -1;
+	self->ctrl = wpa_ctrl_open(path);
+	if (self->ctrl == NULL)
+		return -1;
+	self->attached = 0;
+	return 0;
+}
+
+
+static void wpaspy_close(struct wpaspy_obj *self)
+{
+	if (self->ctrl) {
+		if (self->attached)
+			wpa_ctrl_detach(self->ctrl);
+		wpa_ctrl_close(self->ctrl);
+		self->ctrl = NULL;
+	}
+
+	if (self->ob_type)
+		self->ob_type->tp_free((PyObject *) self);
+}
+
+
+static PyObject * wpaspy_request(struct wpaspy_obj *self, PyObject *args)
+{
+	const char *cmd;
+	char buf[4096];
+	size_t buflen;
+	int ret;
+
+	if (!PyArg_ParseTuple(args, "s", &cmd))
+		return NULL;
+
+	buflen = sizeof(buf) - 1;
+	ret = wpa_ctrl_request(self->ctrl, cmd, strlen(cmd), buf, &buflen,
+			       NULL);
+	if (ret == -2) {
+		PyErr_SetString(wpaspy_error, "Request timed out");
+		return NULL;
+	}
+	if (ret) {
+		PyErr_SetString(wpaspy_error, "Request failed");
+		return NULL;
+	}
+
+	buf[buflen] = '\0';
+	return Py_BuildValue("s", buf);
+}
+
+
+static PyObject * wpaspy_attach(struct wpaspy_obj *self)
+{
+	int ret;
+
+	if (self->attached)
+		Py_RETURN_NONE;
+
+	ret = wpa_ctrl_attach(self->ctrl);
+	if (ret) {
+		PyErr_SetString(wpaspy_error, "Attach failed");
+		return NULL;
+	}
+	Py_RETURN_NONE;
+}
+
+
+static PyObject * wpaspy_detach(struct wpaspy_obj *self)
+{
+	int ret;
+
+	if (!self->attached)
+		Py_RETURN_NONE;
+
+	ret = wpa_ctrl_detach(self->ctrl);
+	if (ret) {
+		PyErr_SetString(wpaspy_error, "Detach failed");
+		return NULL;
+	}
+	Py_RETURN_NONE;
+}
+
+
+static PyObject * wpaspy_pending(struct wpaspy_obj *self)
+{
+	switch (wpa_ctrl_pending(self->ctrl)) {
+	case 1:
+		Py_RETURN_TRUE;
+	case 0:
+		Py_RETURN_FALSE;
+	default:
+		PyErr_SetString(wpaspy_error, "wpa_ctrl_pending failed");
+		break;
+	}
+
+	return NULL;
+}
+
+
+static PyObject * wpaspy_recv(struct wpaspy_obj *self)
+{
+	int ret;
+	char buf[4096];
+	size_t buflen;
+
+	buflen = sizeof(buf) - 1;
+	Py_BEGIN_ALLOW_THREADS
+	ret = wpa_ctrl_recv(self->ctrl, buf, &buflen);
+	Py_END_ALLOW_THREADS
+
+	if (ret) {
+		PyErr_SetString(wpaspy_error, "wpa_ctrl_recv failed");
+		return NULL;
+	}
+
+	buf[buflen] = '\0';
+	return Py_BuildValue("s", buf);
+}
+
+
+static PyMethodDef wpaspy_methods[] = {
+	{
+		"request", (PyCFunction) wpaspy_request, METH_VARARGS,
+		"Send a control interface command and return response"
+	},
+	{
+		"attach", (PyCFunction) wpaspy_attach, METH_NOARGS,
+		"Attach as an event monitor"
+	},
+	{
+		"detach", (PyCFunction) wpaspy_detach, METH_NOARGS,
+		"Detach an event monitor"
+	},
+	{
+		"pending", (PyCFunction) wpaspy_pending, METH_NOARGS,
+		"Check whether any events are pending"
+	},
+	{
+		"recv", (PyCFunction) wpaspy_recv, METH_NOARGS,
+		"Received pending event"
+	},
+	{ NULL, NULL, 0, NULL }
+};
+
+static PyMemberDef wpaspy_members[] = {
+	{
+		"attached", T_INT, offsetof(struct wpaspy_obj, attached),
+		READONLY,
+		"Whether instance is attached as event monitor"
+	},
+	{ NULL }
+};
+
+static PyTypeObject wpaspy_ctrl = {
+	PyObject_HEAD_INIT(NULL)
+	.tp_name = "wpaspy.Ctrl",
+	.tp_basicsize = sizeof(struct wpaspy_obj),
+	.tp_getattro = PyObject_GenericGetAttr,
+	.tp_setattro = PyObject_GenericSetAttr,
+	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+	.tp_methods = wpaspy_methods,
+	.tp_members = wpaspy_members,
+	.tp_init = (initproc) wpaspy_open,
+	.tp_dealloc = (destructor) wpaspy_close,
+	.tp_new = PyType_GenericNew,
+};
+
+
+static PyMethodDef module_methods[] = {
+	{ NULL, NULL, 0, NULL }
+};
+
+
+PyMODINIT_FUNC initwpaspy(void)
+{
+	PyObject *mod;
+
+	PyType_Ready(&wpaspy_ctrl);
+	mod = Py_InitModule("wpaspy", module_methods);
+	wpaspy_error = PyErr_NewException("wpaspy.error", NULL, NULL);
+
+	Py_INCREF(&wpaspy_ctrl);
+	Py_INCREF(wpaspy_error);
+
+	PyModule_AddObject(mod, "Ctrl", (PyObject *) &wpaspy_ctrl);
+	PyModule_AddObject(mod, "error", wpaspy_error);
+}
diff --git a/hostap/wpaspy/wpaspy.py b/hostap/wpaspy/wpaspy.py
new file mode 100644
index 0000000..2f57d74
--- /dev/null
+++ b/hostap/wpaspy/wpaspy.py
@@ -0,0 +1,84 @@
+#!/usr/bin/python
+#
+# wpa_supplicant/hostapd control interface using Python
+# Copyright (c) 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 socket
+import select
+
+counter = 0
+
+class Ctrl:
+    def __init__(self, path):
+        global counter
+        self.started = False
+        self.attached = False
+        self.s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+        self.dest = path
+        self.local = "/tmp/wpa_ctrl_" + str(os.getpid()) + '-' + str(counter)
+        counter += 1
+        self.s.bind(self.local)
+        try:
+            self.s.connect(self.dest)
+        except Exception, e:
+            self.s.close()
+            os.unlink(self.local)
+            raise
+        self.started = True
+
+    def __del__(self):
+        self.close()
+
+    def close(self):
+        if self.attached:
+            try:
+                self.detach()
+            except Exception, e:
+                # Need to ignore this allow the socket to be closed
+                self.attached = False
+                pass
+        if self.started:
+            self.s.close()
+            os.unlink(self.local)
+            self.started = False
+
+    def request(self, cmd, timeout=10):
+        self.s.send(cmd)
+        [r, w, e] = select.select([self.s], [], [], timeout)
+        if r:
+            return self.s.recv(4096)
+        raise Exception("Timeout on waiting response")
+
+    def attach(self):
+        if self.attached:
+            return None
+        res = self.request("ATTACH")
+        if "OK" in res:
+            self.attached = True
+            return None
+        raise Exception("ATTACH failed")
+
+    def detach(self):
+        if not self.attached:
+            return None
+        while self.pending():
+            ev = self.recv()
+        res = self.request("DETACH")
+        if "FAIL" not in res:
+            self.attached = False
+            return None
+        raise Exception("DETACH failed")
+
+    def pending(self, timeout=0):
+        [r, w, e] = select.select([self.s], [], [], timeout)
+        if r:
+            return True
+        return False
+
+    def recv(self):
+        res = self.s.recv(4096)
+        return res